1262710Sganbold/*- 2266337Sian * Copyright (c) 2013 Ganbold Tsagaankhuu <ganbold@freebsd.org> 3262710Sganbold * All rights reserved. 4262710Sganbold * 5262710Sganbold * Redistribution and use in source and binary forms, with or without 6262710Sganbold * modification, are permitted provided that the following conditions 7262710Sganbold * are met: 8262710Sganbold * 1. Redistributions of source code must retain the above copyright 9262710Sganbold * notice, this list of conditions and the following disclaimer. 10262710Sganbold * 2. Redistributions in binary form must reproduce the above copyright 11262710Sganbold * notice, this list of conditions and the following disclaimer in the 12262710Sganbold * documentation and/or other materials provided with the distribution. 13262710Sganbold * 14262710Sganbold * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15262710Sganbold * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16262710Sganbold * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17262710Sganbold * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18262710Sganbold * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19262710Sganbold * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20262710Sganbold * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION 21262710Sganbold * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22262710Sganbold * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY 23262710Sganbold * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24262710Sganbold * SUCH DAMAGE. 25262710Sganbold * 26262710Sganbold * $FreeBSD$ 27262710Sganbold */ 28262710Sganbold 29262710Sganbold/* A10/A20 EMAC driver */ 30262710Sganbold 31262710Sganbold#include <sys/cdefs.h> 32262710Sganbold__FBSDID("$FreeBSD$"); 33262710Sganbold 34262710Sganbold#include <sys/param.h> 35262710Sganbold#include <sys/systm.h> 36262710Sganbold#include <sys/kernel.h> 37262710Sganbold#include <sys/module.h> 38262710Sganbold#include <sys/bus.h> 39262710Sganbold#include <sys/lock.h> 40262710Sganbold#include <sys/mbuf.h> 41262710Sganbold#include <sys/mutex.h> 42262710Sganbold#include <sys/rman.h> 43262710Sganbold#include <sys/socket.h> 44262710Sganbold#include <sys/sockio.h> 45262710Sganbold#include <sys/sysctl.h> 46262710Sganbold#include <sys/gpio.h> 47262710Sganbold 48262710Sganbold#include <machine/bus.h> 49262710Sganbold#include <machine/resource.h> 50262710Sganbold#include <machine/intr.h> 51262710Sganbold 52262710Sganbold#include <net/if.h> 53262710Sganbold#include <net/if_var.h> 54262710Sganbold#include <net/if_arp.h> 55262710Sganbold#include <net/if_dl.h> 56262710Sganbold#include <net/if_media.h> 57262710Sganbold#include <net/if_types.h> 58262710Sganbold#include <net/if_mib.h> 59262710Sganbold#include <net/ethernet.h> 60262710Sganbold#include <net/if_vlan_var.h> 61262710Sganbold 62262710Sganbold#ifdef INET 63262710Sganbold#include <netinet/in.h> 64262710Sganbold#include <netinet/in_systm.h> 65262710Sganbold#include <netinet/in_var.h> 66262710Sganbold#include <netinet/ip.h> 67262710Sganbold#endif 68262710Sganbold 69262710Sganbold#include <net/bpf.h> 70262710Sganbold#include <net/bpfdesc.h> 71262710Sganbold 72262710Sganbold#include <dev/fdt/fdt_common.h> 73262710Sganbold#include <dev/ofw/ofw_bus.h> 74262710Sganbold#include <dev/ofw/ofw_bus_subr.h> 75262710Sganbold 76262710Sganbold#include <dev/mii/mii.h> 77262710Sganbold#include <dev/mii/miivar.h> 78262710Sganbold 79262710Sganbold#include <arm/allwinner/if_emacreg.h> 80262710Sganbold 81262710Sganbold#include "miibus_if.h" 82262710Sganbold 83262710Sganbold#include "gpio_if.h" 84262710Sganbold 85262710Sganbold#include "a10_clk.h" 86262710Sganbold#include "a10_sramc.h" 87262710Sganbold#include "a10_gpio.h" 88262710Sganbold 89262710Sganboldstruct emac_softc { 90262710Sganbold struct ifnet *emac_ifp; 91262710Sganbold device_t emac_dev; 92262710Sganbold device_t emac_miibus; 93262710Sganbold bus_space_handle_t emac_handle; 94262710Sganbold bus_space_tag_t emac_tag; 95262710Sganbold struct resource *emac_res; 96262710Sganbold struct resource *emac_irq; 97262710Sganbold void *emac_intrhand; 98262710Sganbold int emac_if_flags; 99262710Sganbold struct mtx emac_mtx; 100262710Sganbold struct callout emac_tick_ch; 101262710Sganbold int emac_watchdog_timer; 102262710Sganbold int emac_rx_process_limit; 103262710Sganbold int emac_link; 104262710Sganbold}; 105262710Sganbold 106262710Sganboldstatic int emac_probe(device_t); 107262710Sganboldstatic int emac_attach(device_t); 108262710Sganboldstatic int emac_detach(device_t); 109262710Sganboldstatic int emac_shutdown(device_t); 110262710Sganboldstatic int emac_suspend(device_t); 111262710Sganboldstatic int emac_resume(device_t); 112262710Sganbold 113262710Sganboldstatic void emac_sys_setup(void); 114262710Sganboldstatic void emac_reset(struct emac_softc *); 115262710Sganbold 116262710Sganboldstatic void emac_init_locked(struct emac_softc *); 117262710Sganboldstatic void emac_start_locked(struct ifnet *); 118262710Sganboldstatic void emac_init(void *); 119262710Sganboldstatic void emac_stop_locked(struct emac_softc *); 120262710Sganboldstatic void emac_intr(void *); 121262710Sganboldstatic int emac_ioctl(struct ifnet *, u_long, caddr_t); 122262710Sganbold 123262710Sganboldstatic void emac_rxeof(struct emac_softc *, int); 124262710Sganboldstatic void emac_txeof(struct emac_softc *); 125262710Sganbold 126262710Sganboldstatic int emac_miibus_readreg(device_t, int, int); 127262710Sganboldstatic int emac_miibus_writereg(device_t, int, int, int); 128262710Sganboldstatic void emac_miibus_statchg(device_t); 129262710Sganbold 130262710Sganboldstatic int emac_ifmedia_upd(struct ifnet *); 131262710Sganboldstatic void emac_ifmedia_sts(struct ifnet *, struct ifmediareq *); 132262710Sganbold 133262710Sganboldstatic int sysctl_int_range(SYSCTL_HANDLER_ARGS, int, int); 134262710Sganboldstatic int sysctl_hw_emac_proc_limit(SYSCTL_HANDLER_ARGS); 135262710Sganbold 136262710Sganbold#define EMAC_READ_REG(sc, reg) \ 137262710Sganbold bus_space_read_4(sc->emac_tag, sc->emac_handle, reg) 138262710Sganbold#define EMAC_WRITE_REG(sc, reg, val) \ 139262710Sganbold bus_space_write_4(sc->emac_tag, sc->emac_handle, reg, val) 140262710Sganbold 141262710Sganboldstatic void 142262710Sganboldemac_sys_setup(void) 143262710Sganbold{ 144262710Sganbold int i; 145262710Sganbold 146262710Sganbold a10_clk_emac_activate(); 147262710Sganbold 148262710Sganbold /* 149262710Sganbold * Configure pin mux settings for MII. 150262710Sganbold * Pins PA0 from PA17. 151262710Sganbold */ 152262710Sganbold for (i = 0; i <= 17; i++) 153262710Sganbold a10_emac_gpio_config(i); 154262710Sganbold /* Map sram */ 155262710Sganbold a10_map_to_emac(); 156262710Sganbold} 157262710Sganbold 158262710Sganboldstatic void 159262710Sganboldemac_get_hwaddr(struct emac_softc *sc, uint8_t *hwaddr) 160262710Sganbold{ 161262710Sganbold uint32_t val0, val1, rnd; 162262710Sganbold 163262710Sganbold /* 164262710Sganbold * Try to get MAC address from running hardware. 165262710Sganbold * If there is something non-zero there just use it. 166262710Sganbold * 167262710Sganbold * Otherwise set the address to a convenient locally assigned address, 168262710Sganbold * 'bsd' + random 24 low-order bits. 'b' is 0x62, which has the locally 169262710Sganbold * assigned bit set, and the broadcast/multicast bit clear. 170262710Sganbold */ 171262710Sganbold val0 = EMAC_READ_REG(sc, EMAC_MAC_A0); 172262710Sganbold val1 = EMAC_READ_REG(sc, EMAC_MAC_A1); 173262710Sganbold if ((val0 | val1) != 0 && (val0 | val1) != 0xffffff) { 174262710Sganbold hwaddr[0] = (val1 >> 16) & 0xff; 175262710Sganbold hwaddr[1] = (val1 >> 8) & 0xff; 176262710Sganbold hwaddr[2] = (val1 >> 0) & 0xff; 177262710Sganbold hwaddr[3] = (val0 >> 16) & 0xff; 178262710Sganbold hwaddr[4] = (val0 >> 8) & 0xff; 179262710Sganbold hwaddr[5] = (val0 >> 0) & 0xff; 180262710Sganbold } else { 181262710Sganbold rnd = arc4random() & 0x00ffffff; 182262710Sganbold hwaddr[0] = 'b'; 183262710Sganbold hwaddr[1] = 's'; 184262710Sganbold hwaddr[2] = 'd'; 185262710Sganbold hwaddr[3] = (rnd >> 16) & 0xff; 186262710Sganbold hwaddr[4] = (rnd >> 8) & 0xff; 187262710Sganbold hwaddr[5] = (rnd >> 0) & 0xff; 188262710Sganbold } 189262710Sganbold if (bootverbose) 190262710Sganbold printf("MAC address: %s\n", ether_sprintf(hwaddr)); 191262710Sganbold} 192262710Sganbold 193262710Sganboldstatic void 194262710Sganboldemac_set_rx_mode(struct emac_softc *sc) 195262710Sganbold{ 196262710Sganbold struct ifnet *ifp; 197262710Sganbold struct ifmultiaddr *ifma; 198262710Sganbold uint32_t h, hashes[2]; 199262710Sganbold uint32_t rcr = 0; 200262710Sganbold 201262710Sganbold EMAC_ASSERT_LOCKED(sc); 202262710Sganbold 203262710Sganbold ifp = sc->emac_ifp; 204262710Sganbold 205262710Sganbold rcr = EMAC_READ_REG(sc, EMAC_RX_CTL); 206262710Sganbold 207262710Sganbold /* Unicast packet and DA filtering */ 208262710Sganbold rcr |= EMAC_RX_UCAD; 209262710Sganbold rcr |= EMAC_RX_DAF; 210262710Sganbold 211262710Sganbold hashes[0] = 0; 212262710Sganbold hashes[1] = 0; 213262710Sganbold if (ifp->if_flags & IFF_ALLMULTI) { 214262710Sganbold hashes[0] = 0xffffffff; 215262710Sganbold hashes[1] = 0xffffffff; 216262710Sganbold } else { 217262710Sganbold if_maddr_rlock(ifp); 218262710Sganbold TAILQ_FOREACH(ifma, &sc->emac_ifp->if_multiaddrs, ifma_link) { 219262710Sganbold if (ifma->ifma_addr->sa_family != AF_LINK) 220262710Sganbold continue; 221262710Sganbold h = ether_crc32_be(LLADDR((struct sockaddr_dl *) 222262710Sganbold ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; 223262710Sganbold hashes[h >> 5] |= 1 << (h & 0x1f); 224262710Sganbold } 225262710Sganbold if_maddr_runlock(ifp); 226262710Sganbold } 227262710Sganbold rcr |= EMAC_RX_MCO; 228262710Sganbold rcr |= EMAC_RX_MHF; 229262710Sganbold EMAC_WRITE_REG(sc, EMAC_RX_HASH0, hashes[0]); 230262710Sganbold EMAC_WRITE_REG(sc, EMAC_RX_HASH1, hashes[1]); 231262710Sganbold 232262710Sganbold if (ifp->if_flags & IFF_BROADCAST) { 233262710Sganbold rcr |= EMAC_RX_BCO; 234262710Sganbold rcr |= EMAC_RX_MCO; 235262710Sganbold } 236262710Sganbold 237262710Sganbold if (ifp->if_flags & IFF_PROMISC) 238262710Sganbold rcr |= EMAC_RX_PA; 239262710Sganbold else 240262710Sganbold rcr |= EMAC_RX_UCAD; 241262710Sganbold 242262710Sganbold EMAC_WRITE_REG(sc, EMAC_RX_CTL, rcr); 243262710Sganbold} 244262710Sganbold 245262710Sganboldstatic void 246262710Sganboldemac_reset(struct emac_softc *sc) 247262710Sganbold{ 248262710Sganbold 249262710Sganbold EMAC_WRITE_REG(sc, EMAC_CTL, 0); 250262710Sganbold DELAY(200); 251262710Sganbold EMAC_WRITE_REG(sc, EMAC_CTL, 1); 252262710Sganbold DELAY(200); 253262710Sganbold} 254262710Sganbold 255262710Sganboldstatic void 256262710Sganboldemac_txeof(struct emac_softc *sc) 257262710Sganbold{ 258262710Sganbold struct ifnet *ifp; 259262710Sganbold 260262710Sganbold EMAC_ASSERT_LOCKED(sc); 261262710Sganbold 262262710Sganbold ifp = sc->emac_ifp; 263262710Sganbold ifp->if_opackets++; 264262710Sganbold ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 265262710Sganbold 266262710Sganbold /* Unarm watchdog timer if no TX */ 267262710Sganbold sc->emac_watchdog_timer = 0; 268262710Sganbold} 269262710Sganbold 270262710Sganboldstatic void 271262710Sganboldemac_rxeof(struct emac_softc *sc, int count) 272262710Sganbold{ 273262710Sganbold struct ifnet *ifp; 274262710Sganbold struct mbuf *m, *m0; 275262710Sganbold uint32_t reg_val, rxcount; 276262710Sganbold int16_t len; 277262710Sganbold uint16_t status; 278262710Sganbold int good_packet, i; 279262710Sganbold 280262710Sganbold ifp = sc->emac_ifp; 281262710Sganbold for (; count > 0 && 282262710Sganbold (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0; count--) { 283262710Sganbold /* 284262710Sganbold * Race warning: The first packet might arrive with 285262710Sganbold * the interrupts disabled, but the second will fix 286262710Sganbold */ 287262710Sganbold rxcount = EMAC_READ_REG(sc, EMAC_RX_FBC); 288262710Sganbold if (!rxcount) { 289262710Sganbold /* Had one stuck? */ 290262710Sganbold rxcount = EMAC_READ_REG(sc, EMAC_RX_FBC); 291262710Sganbold if (!rxcount) 292262710Sganbold return; 293262710Sganbold } 294262710Sganbold /* Check packet header */ 295262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_RX_IO_DATA); 296262710Sganbold if (reg_val != EMAC_PACKET_HEADER) { 297262710Sganbold /* Packet header is wrong */ 298262710Sganbold if (bootverbose) 299262710Sganbold if_printf(ifp, "wrong packet header\n"); 300262710Sganbold /* Disable RX */ 301262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_CTL); 302262710Sganbold reg_val &= ~EMAC_CTL_RX_EN; 303262710Sganbold EMAC_WRITE_REG(sc, EMAC_CTL, reg_val); 304262710Sganbold 305262710Sganbold /* Flush RX FIFO */ 306262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_RX_CTL); 307262710Sganbold reg_val |= EMAC_RX_FLUSH_FIFO; 308262710Sganbold EMAC_WRITE_REG(sc, EMAC_RX_CTL, reg_val); 309262710Sganbold for (i = 100; i > 0; i--) { 310262710Sganbold DELAY(100); 311262710Sganbold if ((EMAC_READ_REG(sc, EMAC_RX_CTL) & 312262710Sganbold EMAC_RX_FLUSH_FIFO) == 0) 313262710Sganbold break; 314262710Sganbold } 315262710Sganbold if (i == 0) { 316262710Sganbold device_printf(sc->emac_dev, 317262710Sganbold "flush FIFO timeout\n"); 318262710Sganbold /* Reinitialize controller */ 319262710Sganbold emac_init_locked(sc); 320262710Sganbold return; 321262710Sganbold } 322262710Sganbold /* Enable RX */ 323262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_CTL); 324262710Sganbold reg_val |= EMAC_CTL_RX_EN; 325262710Sganbold EMAC_WRITE_REG(sc, EMAC_CTL, reg_val); 326262710Sganbold 327262710Sganbold return; 328262710Sganbold } 329262710Sganbold 330262710Sganbold good_packet = 1; 331262710Sganbold 332262710Sganbold /* Get packet size and status */ 333262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_RX_IO_DATA); 334262710Sganbold len = reg_val & 0xffff; 335262710Sganbold status = (reg_val >> 16) & 0xffff; 336262710Sganbold 337262710Sganbold if (len < 64) { 338262710Sganbold good_packet = 0; 339262710Sganbold if (bootverbose) 340262710Sganbold if_printf(ifp, 341262710Sganbold "bad packet: len = %i status = %i\n", 342262710Sganbold len, status); 343262710Sganbold ifp->if_ierrors++; 344262710Sganbold } 345262710Sganbold#if 0 346262710Sganbold if (status & (EMAC_CRCERR | EMAC_LENERR)) { 347262710Sganbold good_packet = 0; 348262710Sganbold ifp->if_ierrors++; 349262710Sganbold if (status & EMAC_CRCERR) 350262710Sganbold if_printf(ifp, "crc error\n"); 351262710Sganbold if (status & EMAC_LENERR) 352262710Sganbold if_printf(ifp, "length error\n"); 353262710Sganbold } 354262710Sganbold#endif 355262710Sganbold if (good_packet) { 356262710Sganbold m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 357262710Sganbold if (m == NULL) 358262710Sganbold return; 359262710Sganbold m->m_len = m->m_pkthdr.len = MCLBYTES; 360262710Sganbold 361262710Sganbold len -= ETHER_CRC_LEN; 362262710Sganbold 363262710Sganbold /* Copy entire frame to mbuf first. */ 364262710Sganbold bus_space_read_multi_4(sc->emac_tag, sc->emac_handle, 365262710Sganbold EMAC_RX_IO_DATA, mtod(m, uint32_t *), 366262710Sganbold roundup2(len, 4) / 4); 367262710Sganbold 368262710Sganbold m->m_pkthdr.rcvif = ifp; 369262710Sganbold m->m_len = m->m_pkthdr.len = len; 370262710Sganbold 371262710Sganbold /* 372262710Sganbold * Emac controller needs strict aligment, so to avoid 373262710Sganbold * copying over an entire frame to align, we allocate 374262710Sganbold * a new mbuf and copy ethernet header + IP header to 375262710Sganbold * the new mbuf. The new mbuf is prepended into the 376262710Sganbold * existing mbuf chain. 377262710Sganbold */ 378262710Sganbold if (m->m_len <= (MHLEN - ETHER_HDR_LEN)) { 379262710Sganbold bcopy(m->m_data, m->m_data + ETHER_HDR_LEN, 380262710Sganbold m->m_len); 381262710Sganbold m->m_data += ETHER_HDR_LEN; 382262710Sganbold } else if (m->m_len <= (MCLBYTES - ETHER_HDR_LEN) && 383262710Sganbold m->m_len > (MHLEN - ETHER_HDR_LEN)) { 384262710Sganbold MGETHDR(m0, M_NOWAIT, MT_DATA); 385262710Sganbold if (m0 != NULL) { 386262710Sganbold len = ETHER_HDR_LEN + 387262710Sganbold m->m_pkthdr.l2hlen; 388262710Sganbold bcopy(m->m_data, m0->m_data, len); 389262710Sganbold m->m_data += len; 390262710Sganbold m->m_len -= len; 391262710Sganbold m0->m_len = len; 392262710Sganbold M_MOVE_PKTHDR(m0, m); 393262710Sganbold m0->m_next = m; 394262710Sganbold m = m0; 395262710Sganbold } else { 396262710Sganbold ifp->if_ierrors++; 397262710Sganbold m_freem(m); 398262710Sganbold m = NULL; 399262710Sganbold continue; 400262710Sganbold } 401262710Sganbold } else if (m->m_len > EMAC_MAC_MAXF) { 402262710Sganbold ifp->if_ierrors++; 403262710Sganbold m_freem(m); 404262710Sganbold m = NULL; 405262710Sganbold continue; 406262710Sganbold } 407262710Sganbold ifp->if_ipackets++; 408262710Sganbold EMAC_UNLOCK(sc); 409262710Sganbold (*ifp->if_input)(ifp, m); 410262710Sganbold EMAC_LOCK(sc); 411262710Sganbold } 412262710Sganbold } 413262710Sganbold} 414262710Sganbold 415262710Sganboldstatic void 416262710Sganboldemac_watchdog(struct emac_softc *sc) 417262710Sganbold{ 418262710Sganbold struct ifnet *ifp; 419262710Sganbold 420262710Sganbold EMAC_ASSERT_LOCKED(sc); 421262710Sganbold 422262710Sganbold if (sc->emac_watchdog_timer == 0 || --sc->emac_watchdog_timer) 423262710Sganbold return; 424262710Sganbold 425262710Sganbold ifp = sc->emac_ifp; 426262710Sganbold 427262710Sganbold if (sc->emac_link == 0) { 428262710Sganbold if (bootverbose) 429262710Sganbold if_printf(sc->emac_ifp, "watchdog timeout " 430262710Sganbold "(missed link)\n"); 431262710Sganbold } else 432262710Sganbold if_printf(sc->emac_ifp, "watchdog timeout -- resetting\n"); 433262710Sganbold 434262710Sganbold ifp->if_oerrors++; 435262710Sganbold ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 436262710Sganbold emac_init_locked(sc); 437262710Sganbold if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 438262710Sganbold emac_start_locked(ifp); 439262710Sganbold} 440262710Sganbold 441262710Sganboldstatic void 442262710Sganboldemac_tick(void *arg) 443262710Sganbold{ 444262710Sganbold struct emac_softc *sc; 445262710Sganbold struct mii_data *mii; 446262710Sganbold 447262710Sganbold sc = (struct emac_softc *)arg; 448262710Sganbold mii = device_get_softc(sc->emac_miibus); 449262710Sganbold mii_tick(mii); 450262710Sganbold 451262710Sganbold emac_watchdog(sc); 452262710Sganbold callout_reset(&sc->emac_tick_ch, hz, emac_tick, sc); 453262710Sganbold} 454262710Sganbold 455262710Sganboldstatic void 456262710Sganboldemac_init(void *xcs) 457262710Sganbold{ 458262710Sganbold struct emac_softc *sc; 459262710Sganbold 460262710Sganbold sc = (struct emac_softc *)xcs; 461262710Sganbold EMAC_LOCK(sc); 462262710Sganbold emac_init_locked(sc); 463262710Sganbold EMAC_UNLOCK(sc); 464262710Sganbold} 465262710Sganbold 466262710Sganboldstatic void 467262710Sganboldemac_init_locked(struct emac_softc *sc) 468262710Sganbold{ 469262710Sganbold struct ifnet *ifp; 470262710Sganbold struct mii_data *mii; 471262710Sganbold uint32_t reg_val; 472262710Sganbold uint8_t *eaddr; 473262710Sganbold 474262710Sganbold EMAC_ASSERT_LOCKED(sc); 475262710Sganbold 476262710Sganbold ifp = sc->emac_ifp; 477262710Sganbold if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 478262710Sganbold return; 479262710Sganbold 480262710Sganbold /* Flush RX FIFO */ 481262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_RX_CTL); 482262710Sganbold reg_val |= EMAC_RX_FLUSH_FIFO; 483262710Sganbold EMAC_WRITE_REG(sc, EMAC_RX_CTL, reg_val); 484262710Sganbold DELAY(1); 485262710Sganbold 486262710Sganbold /* Soft reset MAC */ 487262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_MAC_CTL0); 488262710Sganbold reg_val &= (~EMAC_MAC_CTL0_SOFT_RST); 489262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_CTL0, reg_val); 490262710Sganbold 491262710Sganbold /* Set MII clock */ 492262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_MAC_MCFG); 493262710Sganbold reg_val &= (~(0xf << 2)); 494262710Sganbold reg_val |= (0xd << 2); 495262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_MCFG, reg_val); 496262710Sganbold 497262710Sganbold /* Clear RX counter */ 498262710Sganbold EMAC_WRITE_REG(sc, EMAC_RX_FBC, 0); 499262710Sganbold 500262710Sganbold /* Disable all interrupt and clear interrupt status */ 501262710Sganbold EMAC_WRITE_REG(sc, EMAC_INT_CTL, 0); 502262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_INT_STA); 503262710Sganbold EMAC_WRITE_REG(sc, EMAC_INT_STA, reg_val); 504262710Sganbold DELAY(1); 505262710Sganbold 506262710Sganbold /* Set up TX */ 507262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_TX_MODE); 508262710Sganbold reg_val |= EMAC_TX_AB_M; 509262710Sganbold reg_val &= EMAC_TX_TM; 510262710Sganbold EMAC_WRITE_REG(sc, EMAC_TX_MODE, reg_val); 511262710Sganbold 512262710Sganbold /* Set up RX */ 513262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_RX_CTL); 514262710Sganbold reg_val |= EMAC_RX_SETUP; 515262710Sganbold reg_val &= EMAC_RX_TM; 516262710Sganbold EMAC_WRITE_REG(sc, EMAC_RX_CTL, reg_val); 517262710Sganbold 518262710Sganbold /* Set up MAC CTL0. */ 519262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_MAC_CTL0); 520262710Sganbold reg_val |= EMAC_MAC_CTL0_SETUP; 521262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_CTL0, reg_val); 522262710Sganbold 523262710Sganbold /* Set up MAC CTL1. */ 524262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_MAC_CTL1); 525262710Sganbold reg_val |= EMAC_MAC_CTL1_SETUP; 526262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_CTL1, reg_val); 527262710Sganbold 528262710Sganbold /* Set up IPGT */ 529262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_IPGT, EMAC_MAC_IPGT_FD); 530262710Sganbold 531262710Sganbold /* Set up IPGR */ 532262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_IPGR, EMAC_MAC_NBTB_IPG2 | 533262710Sganbold (EMAC_MAC_NBTB_IPG1 << 8)); 534262710Sganbold 535262710Sganbold /* Set up Collison window */ 536262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_CLRT, EMAC_MAC_RM | (EMAC_MAC_CW << 8)); 537262710Sganbold 538262710Sganbold /* Set up Max Frame Length */ 539262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_MAXF, EMAC_MAC_MFL); 540262710Sganbold 541262710Sganbold /* Setup ethernet address */ 542262710Sganbold eaddr = IF_LLADDR(ifp); 543262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_A1, eaddr[0] << 16 | 544262710Sganbold eaddr[1] << 8 | eaddr[2]); 545262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_A0, eaddr[3] << 16 | 546262710Sganbold eaddr[4] << 8 | eaddr[5]); 547262710Sganbold 548262710Sganbold /* Setup rx filter */ 549262710Sganbold emac_set_rx_mode(sc); 550262710Sganbold 551262710Sganbold /* Enable RX/TX0/RX Hlevel interrupt */ 552262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_INT_CTL); 553262710Sganbold reg_val |= EMAC_INT_EN; 554262710Sganbold EMAC_WRITE_REG(sc, EMAC_INT_CTL, reg_val); 555262710Sganbold 556262710Sganbold ifp->if_drv_flags |= IFF_DRV_RUNNING; 557262710Sganbold ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 558262710Sganbold 559262710Sganbold sc->emac_link = 0; 560262710Sganbold 561262710Sganbold /* Switch to the current media. */ 562262710Sganbold mii = device_get_softc(sc->emac_miibus); 563262710Sganbold mii_mediachg(mii); 564262710Sganbold 565262710Sganbold callout_reset(&sc->emac_tick_ch, hz, emac_tick, sc); 566262710Sganbold} 567262710Sganbold 568262710Sganbold 569262710Sganboldstatic void 570262710Sganboldemac_start(struct ifnet *ifp) 571262710Sganbold{ 572262710Sganbold struct emac_softc *sc; 573262710Sganbold 574262710Sganbold sc = ifp->if_softc; 575262710Sganbold EMAC_LOCK(sc); 576262710Sganbold emac_start_locked(ifp); 577262710Sganbold EMAC_UNLOCK(sc); 578262710Sganbold} 579262710Sganbold 580262710Sganboldstatic void 581262710Sganboldemac_start_locked(struct ifnet *ifp) 582262710Sganbold{ 583262710Sganbold struct emac_softc *sc; 584262710Sganbold struct mbuf *m, *m0; 585262710Sganbold uint32_t reg_val; 586262710Sganbold 587262710Sganbold sc = ifp->if_softc; 588262710Sganbold if (ifp->if_drv_flags & IFF_DRV_OACTIVE) 589262710Sganbold return; 590262710Sganbold if (sc->emac_link == 0) 591262710Sganbold return; 592262710Sganbold IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 593262710Sganbold if (m == NULL) 594262710Sganbold return; 595262710Sganbold 596262710Sganbold /* Select channel */ 597262710Sganbold EMAC_WRITE_REG(sc, EMAC_TX_INS, 0); 598262710Sganbold 599262710Sganbold /* 600262710Sganbold * Emac controller wants 4 byte aligned TX buffers. 601262710Sganbold * We have to copy pretty much all the time. 602262710Sganbold */ 603262710Sganbold if (m->m_next != NULL || (mtod(m, uintptr_t) & 3) != 0) { 604262710Sganbold m0 = m_defrag(m, M_NOWAIT); 605262710Sganbold if (m0 == NULL) { 606262710Sganbold m_freem(m); 607262710Sganbold m = NULL; 608262710Sganbold return; 609262710Sganbold } 610262710Sganbold m = m0; 611262710Sganbold } 612262710Sganbold /* Write data */ 613262710Sganbold bus_space_write_multi_4(sc->emac_tag, sc->emac_handle, 614262710Sganbold EMAC_TX_IO_DATA, mtod(m, uint32_t *), 615262710Sganbold roundup2(m->m_len, 4) / 4); 616262710Sganbold 617262710Sganbold /* Send the data lengh. */ 618262710Sganbold EMAC_WRITE_REG(sc, EMAC_TX_PL0, m->m_len); 619262710Sganbold 620262710Sganbold /* Start translate from fifo to phy. */ 621262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_TX_CTL0); 622262710Sganbold reg_val |= 1; 623262710Sganbold EMAC_WRITE_REG(sc, EMAC_TX_CTL0, reg_val); 624262710Sganbold 625262710Sganbold /* Set timeout */ 626262710Sganbold sc->emac_watchdog_timer = 5; 627262710Sganbold 628262710Sganbold ifp->if_drv_flags |= IFF_DRV_OACTIVE; 629262710Sganbold BPF_MTAP(ifp, m); 630262710Sganbold m_freem(m); 631262710Sganbold} 632262710Sganbold 633262710Sganboldstatic void 634262710Sganboldemac_stop_locked(struct emac_softc *sc) 635262710Sganbold{ 636262710Sganbold struct ifnet *ifp; 637262710Sganbold uint32_t reg_val; 638262710Sganbold 639262710Sganbold EMAC_ASSERT_LOCKED(sc); 640262710Sganbold 641262710Sganbold ifp = sc->emac_ifp; 642262710Sganbold ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 643262710Sganbold sc->emac_link = 0; 644262710Sganbold 645262710Sganbold /* Disable all interrupt and clear interrupt status */ 646262710Sganbold EMAC_WRITE_REG(sc, EMAC_INT_CTL, 0); 647262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_INT_STA); 648262710Sganbold EMAC_WRITE_REG(sc, EMAC_INT_STA, reg_val); 649262710Sganbold 650262710Sganbold /* Disable RX/TX */ 651262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_CTL); 652262710Sganbold reg_val &= ~(EMAC_CTL_RST | EMAC_CTL_TX_EN | EMAC_CTL_RX_EN); 653262710Sganbold EMAC_WRITE_REG(sc, EMAC_CTL, reg_val); 654262710Sganbold 655262710Sganbold callout_stop(&sc->emac_tick_ch); 656262710Sganbold} 657262710Sganbold 658262710Sganboldstatic void 659262710Sganboldemac_intr(void *arg) 660262710Sganbold{ 661262710Sganbold struct emac_softc *sc; 662262710Sganbold struct ifnet *ifp; 663262710Sganbold uint32_t reg_val; 664262710Sganbold 665262710Sganbold sc = (struct emac_softc *)arg; 666262710Sganbold EMAC_LOCK(sc); 667262710Sganbold ifp = sc->emac_ifp; 668262710Sganbold if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 669262710Sganbold return; 670262710Sganbold 671262710Sganbold /* Disable all interrupts */ 672262710Sganbold EMAC_WRITE_REG(sc, EMAC_INT_CTL, 0); 673262710Sganbold /* Get EMAC interrupt status */ 674262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_INT_STA); 675262710Sganbold /* Clear ISR status */ 676262710Sganbold EMAC_WRITE_REG(sc, EMAC_INT_STA, reg_val); 677262710Sganbold 678262710Sganbold /* Received incoming packet */ 679262710Sganbold if (reg_val & EMAC_INT_STA_RX) 680262710Sganbold emac_rxeof(sc, sc->emac_rx_process_limit); 681262710Sganbold 682262710Sganbold /* Transmit Interrupt check */ 683262710Sganbold if (reg_val & EMAC_INT_STA_TX){ 684262710Sganbold emac_txeof(sc); 685262710Sganbold if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 686262710Sganbold emac_start_locked(ifp); 687262710Sganbold } 688262710Sganbold 689262710Sganbold if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { 690262710Sganbold /* Re-enable interrupt mask */ 691262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_INT_CTL); 692262710Sganbold reg_val |= EMAC_INT_EN; 693262710Sganbold EMAC_WRITE_REG(sc, EMAC_INT_CTL, reg_val); 694262710Sganbold } 695262710Sganbold EMAC_UNLOCK(sc); 696262710Sganbold} 697262710Sganbold 698262710Sganboldstatic int 699262710Sganboldemac_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 700262710Sganbold{ 701262710Sganbold struct emac_softc *sc; 702262710Sganbold struct mii_data *mii; 703262710Sganbold struct ifreq *ifr; 704262710Sganbold int error = 0; 705262710Sganbold 706262710Sganbold sc = ifp->if_softc; 707262710Sganbold ifr = (struct ifreq *)data; 708262710Sganbold 709262710Sganbold switch (command) { 710262710Sganbold case SIOCSIFFLAGS: 711262710Sganbold EMAC_LOCK(sc); 712262710Sganbold if (ifp->if_flags & IFF_UP) { 713262710Sganbold if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { 714262710Sganbold if ((ifp->if_flags ^ sc->emac_if_flags) & 715262710Sganbold (IFF_PROMISC | IFF_ALLMULTI)) 716262710Sganbold emac_set_rx_mode(sc); 717262710Sganbold } else 718262710Sganbold emac_init_locked(sc); 719262710Sganbold } else { 720262710Sganbold if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 721262710Sganbold emac_stop_locked(sc); 722262710Sganbold } 723262710Sganbold sc->emac_if_flags = ifp->if_flags; 724262710Sganbold EMAC_UNLOCK(sc); 725262710Sganbold break; 726262710Sganbold case SIOCADDMULTI: 727262710Sganbold case SIOCDELMULTI: 728262710Sganbold EMAC_LOCK(sc); 729262710Sganbold if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 730262710Sganbold emac_set_rx_mode(sc); 731262710Sganbold } 732262710Sganbold EMAC_UNLOCK(sc); 733262710Sganbold break; 734262710Sganbold case SIOCGIFMEDIA: 735262710Sganbold case SIOCSIFMEDIA: 736262710Sganbold mii = device_get_softc(sc->emac_miibus); 737262710Sganbold error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 738262710Sganbold break; 739262710Sganbold default: 740262710Sganbold error = ether_ioctl(ifp, command, data); 741262710Sganbold break; 742262710Sganbold } 743262710Sganbold return (error); 744262710Sganbold} 745262710Sganbold 746262710Sganboldstatic int 747262710Sganboldemac_probe(device_t dev) 748262710Sganbold{ 749262710Sganbold 750262710Sganbold if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-emac")) 751262710Sganbold return (ENXIO); 752262710Sganbold 753262710Sganbold device_set_desc(dev, "A10/A20 EMAC ethernet controller"); 754262710Sganbold return (BUS_PROBE_DEFAULT); 755262710Sganbold} 756262710Sganbold 757262710Sganboldstatic int 758262710Sganboldemac_detach(device_t dev) 759262710Sganbold{ 760262710Sganbold struct emac_softc *sc; 761262710Sganbold 762262710Sganbold sc = device_get_softc(dev); 763262710Sganbold sc->emac_ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 764262710Sganbold if (device_is_attached(dev)) { 765262710Sganbold ether_ifdetach(sc->emac_ifp); 766262710Sganbold EMAC_LOCK(sc); 767262710Sganbold emac_stop_locked(sc); 768262710Sganbold EMAC_UNLOCK(sc); 769262710Sganbold callout_drain(&sc->emac_tick_ch); 770262710Sganbold } 771262710Sganbold 772262710Sganbold if (sc->emac_intrhand != NULL) 773262710Sganbold bus_teardown_intr(sc->emac_dev, sc->emac_irq, 774262710Sganbold sc->emac_intrhand); 775262710Sganbold 776262710Sganbold if (sc->emac_miibus != NULL) { 777262710Sganbold device_delete_child(sc->emac_dev, sc->emac_miibus); 778262710Sganbold bus_generic_detach(sc->emac_dev); 779262710Sganbold } 780262710Sganbold 781262710Sganbold if (sc->emac_res != NULL) 782262710Sganbold bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->emac_res); 783262710Sganbold 784262710Sganbold if (sc->emac_irq != NULL) 785262710Sganbold bus_release_resource(dev, SYS_RES_IRQ, 0, sc->emac_irq); 786262710Sganbold 787262710Sganbold if (sc->emac_ifp != NULL) 788262710Sganbold if_free(sc->emac_ifp); 789262710Sganbold 790262710Sganbold if (mtx_initialized(&sc->emac_mtx)) 791262710Sganbold mtx_destroy(&sc->emac_mtx); 792262710Sganbold 793262710Sganbold return (0); 794262710Sganbold} 795262710Sganbold 796262710Sganboldstatic int 797262710Sganboldemac_shutdown(device_t dev) 798262710Sganbold{ 799262710Sganbold 800262710Sganbold return (emac_suspend(dev)); 801262710Sganbold} 802262710Sganbold 803262710Sganboldstatic int 804262710Sganboldemac_suspend(device_t dev) 805262710Sganbold{ 806262710Sganbold struct emac_softc *sc; 807262710Sganbold struct ifnet *ifp; 808262710Sganbold 809262710Sganbold sc = device_get_softc(dev); 810262710Sganbold 811262710Sganbold EMAC_LOCK(sc); 812262710Sganbold ifp = sc->emac_ifp; 813262710Sganbold if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 814262710Sganbold emac_stop_locked(sc); 815262710Sganbold EMAC_UNLOCK(sc); 816262710Sganbold 817262710Sganbold return (0); 818262710Sganbold} 819262710Sganbold 820262710Sganboldstatic int 821262710Sganboldemac_resume(device_t dev) 822262710Sganbold{ 823262710Sganbold struct emac_softc *sc; 824262710Sganbold struct ifnet *ifp; 825262710Sganbold 826262710Sganbold sc = device_get_softc(dev); 827262710Sganbold 828262710Sganbold EMAC_LOCK(sc); 829262710Sganbold ifp = sc->emac_ifp; 830262710Sganbold if ((ifp->if_flags & IFF_UP) != 0) { 831262710Sganbold ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 832262710Sganbold emac_init_locked(sc); 833262710Sganbold } 834262710Sganbold EMAC_UNLOCK(sc); 835262710Sganbold 836262710Sganbold return (0); 837262710Sganbold} 838262710Sganbold 839262710Sganboldstatic int 840262710Sganboldemac_attach(device_t dev) 841262710Sganbold{ 842262710Sganbold struct emac_softc *sc; 843262710Sganbold struct ifnet *ifp; 844262710Sganbold int error, rid; 845262710Sganbold uint8_t eaddr[ETHER_ADDR_LEN]; 846262710Sganbold 847262710Sganbold sc = device_get_softc(dev); 848262710Sganbold sc->emac_dev = dev; 849262710Sganbold 850262710Sganbold error = 0; 851262710Sganbold mtx_init(&sc->emac_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 852262710Sganbold MTX_DEF); 853262710Sganbold callout_init_mtx(&sc->emac_tick_ch, &sc->emac_mtx, 0); 854262710Sganbold 855262710Sganbold rid = 0; 856262710Sganbold sc->emac_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 857262710Sganbold RF_ACTIVE); 858262710Sganbold if (sc->emac_res == NULL) { 859262710Sganbold device_printf(dev, "unable to map memory\n"); 860262710Sganbold error = ENXIO; 861262710Sganbold goto fail; 862262710Sganbold } 863262710Sganbold 864262710Sganbold sc->emac_tag = rman_get_bustag(sc->emac_res); 865262710Sganbold sc->emac_handle = rman_get_bushandle(sc->emac_res); 866262710Sganbold 867262710Sganbold rid = 0; 868262710Sganbold sc->emac_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 869262710Sganbold RF_SHAREABLE | RF_ACTIVE); 870262710Sganbold if (sc->emac_irq == NULL) { 871262710Sganbold device_printf(dev, "cannot allocate IRQ resources.\n"); 872262710Sganbold error = ENXIO; 873262710Sganbold goto fail; 874262710Sganbold } 875262710Sganbold /* Create device sysctl node. */ 876262710Sganbold SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 877262710Sganbold SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 878262710Sganbold OID_AUTO, "process_limit", CTLTYPE_INT | CTLFLAG_RW, 879262710Sganbold &sc->emac_rx_process_limit, 0, sysctl_hw_emac_proc_limit, "I", 880262710Sganbold "max number of Rx events to process"); 881262710Sganbold 882262710Sganbold sc->emac_rx_process_limit = EMAC_PROC_DEFAULT; 883262710Sganbold error = resource_int_value(device_get_name(dev), device_get_unit(dev), 884262710Sganbold "process_limit", &sc->emac_rx_process_limit); 885262710Sganbold if (error == 0) { 886262710Sganbold if (sc->emac_rx_process_limit < EMAC_PROC_MIN || 887262710Sganbold sc->emac_rx_process_limit > EMAC_PROC_MAX) { 888262710Sganbold device_printf(dev, "process_limit value out of range; " 889262710Sganbold "using default: %d\n", EMAC_PROC_DEFAULT); 890262710Sganbold sc->emac_rx_process_limit = EMAC_PROC_DEFAULT; 891262710Sganbold } 892262710Sganbold } 893262710Sganbold /* Setup EMAC */ 894262710Sganbold emac_sys_setup(); 895262710Sganbold emac_reset(sc); 896262710Sganbold 897262710Sganbold ifp = sc->emac_ifp = if_alloc(IFT_ETHER); 898262710Sganbold if (ifp == NULL) { 899262710Sganbold device_printf(dev, "unable to allocate ifp\n"); 900262710Sganbold error = ENOSPC; 901262710Sganbold goto fail; 902262710Sganbold } 903262710Sganbold ifp->if_softc = sc; 904262710Sganbold 905262710Sganbold /* Setup MII */ 906262710Sganbold error = mii_attach(dev, &sc->emac_miibus, ifp, emac_ifmedia_upd, 907262710Sganbold emac_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0); 908262710Sganbold if (error != 0) { 909262710Sganbold device_printf(dev, "PHY probe failed\n"); 910262710Sganbold goto fail; 911262710Sganbold } 912262710Sganbold 913262710Sganbold if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 914262710Sganbold ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 915262710Sganbold ifp->if_start = emac_start; 916262710Sganbold ifp->if_ioctl = emac_ioctl; 917262710Sganbold ifp->if_init = emac_init; 918262710Sganbold IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); 919262710Sganbold 920262710Sganbold /* Get MAC address */ 921262710Sganbold emac_get_hwaddr(sc, eaddr); 922262710Sganbold ether_ifattach(ifp, eaddr); 923262710Sganbold 924262710Sganbold /* VLAN capability setup. */ 925262710Sganbold ifp->if_capabilities |= IFCAP_VLAN_MTU; 926262710Sganbold ifp->if_capenable = ifp->if_capabilities; 927262710Sganbold /* Tell the upper layer we support VLAN over-sized frames. */ 928262710Sganbold ifp->if_hdrlen = sizeof(struct ether_vlan_header); 929262710Sganbold 930262710Sganbold error = bus_setup_intr(dev, sc->emac_irq, INTR_TYPE_NET | INTR_MPSAFE, 931262710Sganbold NULL, emac_intr, sc, &sc->emac_intrhand); 932262710Sganbold if (error != 0) { 933262710Sganbold device_printf(dev, "could not set up interrupt handler.\n"); 934262710Sganbold ether_ifdetach(ifp); 935262710Sganbold goto fail; 936262710Sganbold } 937262710Sganbold 938262710Sganboldfail: 939262710Sganbold if (error != 0) 940262710Sganbold emac_detach(dev); 941262710Sganbold return (error); 942262710Sganbold} 943262710Sganbold 944262710Sganboldstatic boolean_t 945262710Sganboldemac_miibus_iowait(struct emac_softc *sc) 946262710Sganbold{ 947262710Sganbold uint32_t timeout; 948262710Sganbold 949262710Sganbold for (timeout = 100; timeout != 0; --timeout) { 950262710Sganbold DELAY(100); 951262710Sganbold if ((EMAC_READ_REG(sc, EMAC_MAC_MIND) & 0x1) == 0) 952262710Sganbold return (true); 953262710Sganbold } 954262710Sganbold 955262710Sganbold return (false); 956262710Sganbold} 957262710Sganbold 958262710Sganbold/* 959262710Sganbold * The MII bus interface 960262710Sganbold */ 961262710Sganboldstatic int 962262710Sganboldemac_miibus_readreg(device_t dev, int phy, int reg) 963262710Sganbold{ 964262710Sganbold struct emac_softc *sc; 965262710Sganbold int rval; 966262710Sganbold 967262710Sganbold sc = device_get_softc(dev); 968262710Sganbold 969262710Sganbold /* Issue phy address and reg */ 970262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_MADR, (phy << 8) | reg); 971262710Sganbold /* Pull up the phy io line */ 972262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_MCMD, 0x1); 973262710Sganbold if (!emac_miibus_iowait(sc)) { 974262710Sganbold device_printf(dev, "timeout waiting for mii read\n"); 975262710Sganbold return (0); 976262710Sganbold } 977262710Sganbold /* Push down the phy io line */ 978262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_MCMD, 0x0); 979262710Sganbold /* Read data */ 980262710Sganbold rval = EMAC_READ_REG(sc, EMAC_MAC_MRDD); 981262710Sganbold 982262710Sganbold return (rval); 983262710Sganbold} 984262710Sganbold 985262710Sganboldstatic int 986262710Sganboldemac_miibus_writereg(device_t dev, int phy, int reg, int data) 987262710Sganbold{ 988262710Sganbold struct emac_softc *sc; 989262710Sganbold 990262710Sganbold sc = device_get_softc(dev); 991262710Sganbold 992262710Sganbold /* Issue phy address and reg */ 993262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_MADR, (phy << 8) | reg); 994262710Sganbold /* Write data */ 995262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_MWTD, data); 996262710Sganbold /* Pull up the phy io line */ 997262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_MCMD, 0x1); 998262710Sganbold if (!emac_miibus_iowait(sc)) { 999262710Sganbold device_printf(dev, "timeout waiting for mii write\n"); 1000262710Sganbold return (0); 1001262710Sganbold } 1002262710Sganbold /* Push down the phy io line */ 1003262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_MCMD, 0x0); 1004262710Sganbold 1005262710Sganbold return (0); 1006262710Sganbold} 1007262710Sganbold 1008262710Sganboldstatic void 1009262710Sganboldemac_miibus_statchg(device_t dev) 1010262710Sganbold{ 1011262710Sganbold struct emac_softc *sc; 1012262710Sganbold struct mii_data *mii; 1013262710Sganbold struct ifnet *ifp; 1014262710Sganbold uint32_t reg_val; 1015262710Sganbold 1016262710Sganbold sc = device_get_softc(dev); 1017262710Sganbold 1018262710Sganbold mii = device_get_softc(sc->emac_miibus); 1019262710Sganbold ifp = sc->emac_ifp; 1020262710Sganbold if (mii == NULL || ifp == NULL || 1021262710Sganbold (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 1022262710Sganbold return; 1023262710Sganbold 1024262710Sganbold sc->emac_link = 0; 1025262710Sganbold if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == 1026262710Sganbold (IFM_ACTIVE | IFM_AVALID)) { 1027262710Sganbold switch (IFM_SUBTYPE(mii->mii_media_active)) { 1028262710Sganbold case IFM_10_T: 1029262710Sganbold case IFM_100_TX: 1030262710Sganbold sc->emac_link = 1; 1031262710Sganbold break; 1032262710Sganbold default: 1033262710Sganbold break; 1034262710Sganbold } 1035262710Sganbold } 1036262710Sganbold /* Program MACs with resolved speed/duplex. */ 1037262710Sganbold if (sc->emac_link != 0) { 1038262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_MAC_IPGT); 1039262710Sganbold if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { 1040262710Sganbold reg_val &= ~EMAC_MAC_IPGT_HD; 1041262710Sganbold reg_val |= EMAC_MAC_IPGT_FD; 1042262710Sganbold } else { 1043262710Sganbold reg_val &= ~EMAC_MAC_IPGT_FD; 1044262710Sganbold reg_val |= EMAC_MAC_IPGT_HD; 1045262710Sganbold } 1046262710Sganbold EMAC_WRITE_REG(sc, EMAC_MAC_IPGT, reg_val); 1047262710Sganbold /* Enable RX/TX */ 1048262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_CTL); 1049262710Sganbold reg_val |= EMAC_CTL_RST | EMAC_CTL_TX_EN | EMAC_CTL_RX_EN; 1050262710Sganbold EMAC_WRITE_REG(sc, EMAC_CTL, reg_val); 1051262710Sganbold } else { 1052262710Sganbold /* Disable RX/TX */ 1053262710Sganbold reg_val = EMAC_READ_REG(sc, EMAC_CTL); 1054262710Sganbold reg_val &= ~(EMAC_CTL_RST | EMAC_CTL_TX_EN | EMAC_CTL_RX_EN); 1055262710Sganbold EMAC_WRITE_REG(sc, EMAC_CTL, reg_val); 1056262710Sganbold } 1057262710Sganbold} 1058262710Sganbold 1059262710Sganboldstatic int 1060262710Sganboldemac_ifmedia_upd(struct ifnet *ifp) 1061262710Sganbold{ 1062262710Sganbold struct emac_softc *sc; 1063262710Sganbold struct mii_data *mii; 1064262710Sganbold struct mii_softc *miisc; 1065262710Sganbold int error; 1066262710Sganbold 1067262710Sganbold sc = ifp->if_softc; 1068262710Sganbold mii = device_get_softc(sc->emac_miibus); 1069262710Sganbold EMAC_LOCK(sc); 1070262710Sganbold LIST_FOREACH(miisc, &mii->mii_phys, mii_list) 1071262710Sganbold PHY_RESET(miisc); 1072262710Sganbold error = mii_mediachg(mii); 1073262710Sganbold EMAC_UNLOCK(sc); 1074262710Sganbold 1075262710Sganbold return (error); 1076262710Sganbold} 1077262710Sganbold 1078262710Sganboldstatic void 1079262710Sganboldemac_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 1080262710Sganbold{ 1081262710Sganbold struct emac_softc *sc; 1082262710Sganbold struct mii_data *mii; 1083262710Sganbold 1084262710Sganbold sc = ifp->if_softc; 1085262710Sganbold mii = device_get_softc(sc->emac_miibus); 1086262710Sganbold 1087262710Sganbold EMAC_LOCK(sc); 1088262710Sganbold mii_pollstat(mii); 1089262710Sganbold ifmr->ifm_active = mii->mii_media_active; 1090262710Sganbold ifmr->ifm_status = mii->mii_media_status; 1091262710Sganbold EMAC_UNLOCK(sc); 1092262710Sganbold} 1093262710Sganbold 1094262710Sganboldstatic device_method_t emac_methods[] = { 1095262710Sganbold /* Device interface */ 1096262710Sganbold DEVMETHOD(device_probe, emac_probe), 1097262710Sganbold DEVMETHOD(device_attach, emac_attach), 1098262710Sganbold DEVMETHOD(device_detach, emac_detach), 1099262710Sganbold DEVMETHOD(device_shutdown, emac_shutdown), 1100262710Sganbold DEVMETHOD(device_suspend, emac_suspend), 1101262710Sganbold DEVMETHOD(device_resume, emac_resume), 1102262710Sganbold 1103262710Sganbold /* bus interface, for miibus */ 1104262710Sganbold DEVMETHOD(bus_print_child, bus_generic_print_child), 1105262710Sganbold DEVMETHOD(bus_driver_added, bus_generic_driver_added), 1106262710Sganbold 1107262710Sganbold /* MII interface */ 1108262710Sganbold DEVMETHOD(miibus_readreg, emac_miibus_readreg), 1109262710Sganbold DEVMETHOD(miibus_writereg, emac_miibus_writereg), 1110262710Sganbold DEVMETHOD(miibus_statchg, emac_miibus_statchg), 1111262710Sganbold 1112262710Sganbold DEVMETHOD_END 1113262710Sganbold}; 1114262710Sganbold 1115262710Sganboldstatic driver_t emac_driver = { 1116262710Sganbold "emac", 1117262710Sganbold emac_methods, 1118262710Sganbold sizeof(struct emac_softc) 1119262710Sganbold}; 1120262710Sganbold 1121262710Sganboldstatic devclass_t emac_devclass; 1122262710Sganbold 1123262710SganboldDRIVER_MODULE(emac, simplebus, emac_driver, emac_devclass, 0, 0); 1124262710SganboldDRIVER_MODULE(miibus, emac, miibus_driver, miibus_devclass, 0, 0); 1125262710SganboldMODULE_DEPEND(emac, miibus, 1, 1, 1); 1126262710SganboldMODULE_DEPEND(emac, ether, 1, 1, 1); 1127262710Sganbold 1128262710Sganboldstatic int 1129262710Sganboldsysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high) 1130262710Sganbold{ 1131262710Sganbold int error, value; 1132262710Sganbold 1133262710Sganbold if (arg1 == NULL) 1134262710Sganbold return (EINVAL); 1135262710Sganbold value = *(int *)arg1; 1136262710Sganbold error = sysctl_handle_int(oidp, &value, 0, req); 1137262710Sganbold if (error || req->newptr == NULL) 1138262710Sganbold return (error); 1139262710Sganbold if (value < low || value > high) 1140262710Sganbold return (EINVAL); 1141262710Sganbold *(int *)arg1 = value; 1142262710Sganbold 1143262710Sganbold return (0); 1144262710Sganbold} 1145262710Sganbold 1146262710Sganboldstatic int 1147262710Sganboldsysctl_hw_emac_proc_limit(SYSCTL_HANDLER_ARGS) 1148262710Sganbold{ 1149262710Sganbold 1150262710Sganbold return (sysctl_int_range(oidp, arg1, arg2, req, 1151262710Sganbold EMAC_PROC_MIN, EMAC_PROC_MAX)); 1152262710Sganbold} 1153