15d43fd68SRuslan Bukin /*- 25d43fd68SRuslan Bukin * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com> 35d43fd68SRuslan Bukin * All rights reserved. 45d43fd68SRuslan Bukin * 55d43fd68SRuslan Bukin * This software was developed by SRI International and the University of 65d43fd68SRuslan Bukin * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 75d43fd68SRuslan Bukin * ("CTSRD"), as part of the DARPA CRASH research programme. 85d43fd68SRuslan Bukin * 95d43fd68SRuslan Bukin * Redistribution and use in source and binary forms, with or without 105d43fd68SRuslan Bukin * modification, are permitted provided that the following conditions 115d43fd68SRuslan Bukin * are met: 125d43fd68SRuslan Bukin * 1. Redistributions of source code must retain the above copyright 135d43fd68SRuslan Bukin * notice, this list of conditions and the following disclaimer. 145d43fd68SRuslan Bukin * 2. Redistributions in binary form must reproduce the above copyright 155d43fd68SRuslan Bukin * notice, this list of conditions and the following disclaimer in the 165d43fd68SRuslan Bukin * documentation and/or other materials provided with the distribution. 175d43fd68SRuslan Bukin * 185d43fd68SRuslan Bukin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 195d43fd68SRuslan Bukin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 205d43fd68SRuslan Bukin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 215d43fd68SRuslan Bukin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 225d43fd68SRuslan Bukin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 235d43fd68SRuslan Bukin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 245d43fd68SRuslan Bukin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 255d43fd68SRuslan Bukin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 265d43fd68SRuslan Bukin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 275d43fd68SRuslan Bukin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 285d43fd68SRuslan Bukin * SUCH DAMAGE. 295d43fd68SRuslan Bukin */ 305d43fd68SRuslan Bukin 315d43fd68SRuslan Bukin /* 325d43fd68SRuslan Bukin * Ethernet media access controller (EMAC) 335d43fd68SRuslan Bukin * Chapter 17, Altera Cyclone V Device Handbook (CV-5V2 2014.07.22) 345d43fd68SRuslan Bukin * 355d43fd68SRuslan Bukin * EMAC is an instance of the Synopsys DesignWare 3504-0 365d43fd68SRuslan Bukin * Universal 10/100/1000 Ethernet MAC (DWC_gmac). 375d43fd68SRuslan Bukin */ 385d43fd68SRuslan Bukin 395d43fd68SRuslan Bukin #include <sys/cdefs.h> 405d43fd68SRuslan Bukin __FBSDID("$FreeBSD$"); 415d43fd68SRuslan Bukin 425d43fd68SRuslan Bukin #include <sys/param.h> 435d43fd68SRuslan Bukin #include <sys/systm.h> 445d43fd68SRuslan Bukin #include <sys/bus.h> 45d7acb49aSJared McNeill #include <sys/gpio.h> 465d43fd68SRuslan Bukin #include <sys/kernel.h> 475d43fd68SRuslan Bukin #include <sys/lock.h> 48da9a326bSLuiz Otavio O Souza #include <sys/malloc.h> 495d43fd68SRuslan Bukin #include <sys/mbuf.h> 50da9a326bSLuiz Otavio O Souza #include <sys/module.h> 515d43fd68SRuslan Bukin #include <sys/mutex.h> 52da9a326bSLuiz Otavio O Souza #include <sys/rman.h> 535d43fd68SRuslan Bukin #include <sys/socket.h> 545d43fd68SRuslan Bukin #include <sys/sockio.h> 555d43fd68SRuslan Bukin 565d43fd68SRuslan Bukin #include <net/bpf.h> 575d43fd68SRuslan Bukin #include <net/if.h> 585d43fd68SRuslan Bukin #include <net/ethernet.h> 595d43fd68SRuslan Bukin #include <net/if_dl.h> 605d43fd68SRuslan Bukin #include <net/if_media.h> 615d43fd68SRuslan Bukin #include <net/if_types.h> 625d43fd68SRuslan Bukin #include <net/if_var.h> 635d43fd68SRuslan Bukin 645d43fd68SRuslan Bukin #include <machine/bus.h> 655d43fd68SRuslan Bukin 66da9a326bSLuiz Otavio O Souza #include <dev/dwc/if_dwc.h> 675df53927SLuiz Otavio O Souza #include <dev/dwc/if_dwcvar.h> 685d43fd68SRuslan Bukin #include <dev/mii/mii.h> 695d43fd68SRuslan Bukin #include <dev/mii/miivar.h> 70da9a326bSLuiz Otavio O Souza #include <dev/ofw/ofw_bus.h> 71da9a326bSLuiz Otavio O Souza #include <dev/ofw/ofw_bus_subr.h> 72da9a326bSLuiz Otavio O Souza 736a05f063SJared McNeill #ifdef EXT_RESOURCES 746a05f063SJared McNeill #include <dev/extres/clk/clk.h> 756a05f063SJared McNeill #include <dev/extres/hwreset/hwreset.h> 766a05f063SJared McNeill #endif 776a05f063SJared McNeill 785df53927SLuiz Otavio O Souza #include "if_dwc_if.h" 79d7acb49aSJared McNeill #include "gpio_if.h" 805d43fd68SRuslan Bukin #include "miibus_if.h" 815d43fd68SRuslan Bukin 825d43fd68SRuslan Bukin #define READ4(_sc, _reg) \ 835d43fd68SRuslan Bukin bus_read_4((_sc)->res[0], _reg) 845d43fd68SRuslan Bukin #define WRITE4(_sc, _reg, _val) \ 855d43fd68SRuslan Bukin bus_write_4((_sc)->res[0], _reg, _val) 865d43fd68SRuslan Bukin 87d8e5258dSRuslan Bukin #define MAC_RESET_TIMEOUT 100 885d43fd68SRuslan Bukin #define WATCHDOG_TIMEOUT_SECS 5 895d43fd68SRuslan Bukin #define STATS_HARVEST_INTERVAL 2 905d43fd68SRuslan Bukin 915d43fd68SRuslan Bukin #define DWC_LOCK(sc) mtx_lock(&(sc)->mtx) 925d43fd68SRuslan Bukin #define DWC_UNLOCK(sc) mtx_unlock(&(sc)->mtx) 93ff0752c8SLuiz Otavio O Souza #define DWC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED) 94ff0752c8SLuiz Otavio O Souza #define DWC_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED) 955d43fd68SRuslan Bukin 968d43a868SMichal Meloun /* TX descriptors - TDESC0 is almost unified */ 978d43a868SMichal Meloun #define TDESC0_OWN (1U << 31) 988d43a868SMichal Meloun #define TDESC0_IHE (1U << 16) /* IP Header Error */ 998d43a868SMichal Meloun #define TDESC0_ES (1U << 15) /* Error Summary */ 1008d43a868SMichal Meloun #define TDESC0_JT (1U << 14) /* Jabber Timeout */ 1018d43a868SMichal Meloun #define TDESC0_FF (1U << 13) /* Frame Flushed */ 1028d43a868SMichal Meloun #define TDESC0_PCE (1U << 12) /* Payload Checksum Error */ 1038d43a868SMichal Meloun #define TDESC0_LOC (1U << 11) /* Loss of Carrier */ 1048d43a868SMichal Meloun #define TDESC0_NC (1U << 10) /* No Carrier */ 1058d43a868SMichal Meloun #define TDESC0_LC (1U << 9) /* Late Collision */ 1068d43a868SMichal Meloun #define TDESC0_EC (1U << 8) /* Excessive Collision */ 1078d43a868SMichal Meloun #define TDESC0_VF (1U << 7) /* VLAN Frame */ 1088d43a868SMichal Meloun #define TDESC0_CC_MASK 0xf 1098d43a868SMichal Meloun #define TDESC0_CC_SHIFT 3 /* Collision Count */ 1108d43a868SMichal Meloun #define TDESC0_ED (1U << 2) /* Excessive Deferral */ 1118d43a868SMichal Meloun #define TDESC0_UF (1U << 1) /* Underflow Error */ 1128d43a868SMichal Meloun #define TDESC0_DB (1U << 0) /* Deferred Bit */ 1138d43a868SMichal Meloun /* TX descriptors - TDESC0 extended format only */ 1148d43a868SMichal Meloun #define ETDESC0_IC (1U << 30) /* Interrupt on Completion */ 1158d43a868SMichal Meloun #define ETDESC0_LS (1U << 29) /* Last Segment */ 1168d43a868SMichal Meloun #define ETDESC0_FS (1U << 28) /* First Segment */ 1178d43a868SMichal Meloun #define ETDESC0_DC (1U << 27) /* Disable CRC */ 1188d43a868SMichal Meloun #define ETDESC0_DP (1U << 26) /* Disable Padding */ 1198d43a868SMichal Meloun #define ETDESC0_CIC_NONE (0U << 22) /* Checksum Insertion Control */ 1208d43a868SMichal Meloun #define ETDESC0_CIC_HDR (1U << 22) 1218d43a868SMichal Meloun #define ETDESC0_CIC_SEG (2U << 22) 1228d43a868SMichal Meloun #define ETDESC0_CIC_FULL (3U << 22) 1238d43a868SMichal Meloun #define ETDESC0_TER (1U << 21) /* Transmit End of Ring */ 1248d43a868SMichal Meloun #define ETDESC0_TCH (1U << 20) /* Second Address Chained */ 1255d43fd68SRuslan Bukin 1268d43a868SMichal Meloun /* TX descriptors - TDESC1 normal format */ 1278d43a868SMichal Meloun #define NTDESC1_IC (1U << 31) /* Interrupt on Completion */ 1288d43a868SMichal Meloun #define NTDESC1_LS (1U << 30) /* Last Segment */ 1298d43a868SMichal Meloun #define NTDESC1_FS (1U << 29) /* First Segment */ 1308d43a868SMichal Meloun #define NTDESC1_CIC_NONE (0U << 27) /* Checksum Insertion Control */ 1318d43a868SMichal Meloun #define NTDESC1_CIC_HDR (1U << 27) 1328d43a868SMichal Meloun #define NTDESC1_CIC_SEG (2U << 27) 1338d43a868SMichal Meloun #define NTDESC1_CIC_FULL (3U << 27) 1348d43a868SMichal Meloun #define NTDESC1_DC (1U << 26) /* Disable CRC */ 1358d43a868SMichal Meloun #define NTDESC1_TER (1U << 25) /* Transmit End of Ring */ 1368d43a868SMichal Meloun #define NTDESC1_TCH (1U << 24) /* Second Address Chained */ 1378d43a868SMichal Meloun /* TX descriptors - TDESC1 extended format */ 1388d43a868SMichal Meloun #define ETDESC1_DP (1U << 23) /* Disable Padding */ 1398d43a868SMichal Meloun #define ETDESC1_TBS2_MASK 0x7ff 1408d43a868SMichal Meloun #define ETDESC1_TBS2_SHIFT 11 /* Receive Buffer 2 Size */ 1418d43a868SMichal Meloun #define ETDESC1_TBS1_MASK 0x7ff 1428d43a868SMichal Meloun #define ETDESC1_TBS1_SHIFT 0 /* Receive Buffer 1 Size */ 1435d43fd68SRuslan Bukin 1448d43a868SMichal Meloun /* RX descriptor - RDESC0 is unified */ 1458d43a868SMichal Meloun #define RDESC0_OWN (1U << 31) 1468d43a868SMichal Meloun #define RDESC0_AFM (1U << 30) /* Dest. Address Filter Fail */ 1478d43a868SMichal Meloun #define RDESC0_FL_MASK 0x3fff 1488d43a868SMichal Meloun #define RDESC0_FL_SHIFT 16 /* Frame Length */ 1498d43a868SMichal Meloun #define RDESC0_ES (1U << 15) /* Error Summary */ 1508d43a868SMichal Meloun #define RDESC0_DE (1U << 14) /* Descriptor Error */ 1518d43a868SMichal Meloun #define RDESC0_SAF (1U << 13) /* Source Address Filter Fail */ 1528d43a868SMichal Meloun #define RDESC0_LE (1U << 12) /* Length Error */ 1538d43a868SMichal Meloun #define RDESC0_OE (1U << 11) /* Overflow Error */ 1548d43a868SMichal Meloun #define RDESC0_VLAN (1U << 10) /* VLAN Tag */ 1558d43a868SMichal Meloun #define RDESC0_FS (1U << 9) /* First Descriptor */ 1568d43a868SMichal Meloun #define RDESC0_LS (1U << 8) /* Last Descriptor */ 1578d43a868SMichal Meloun #define RDESC0_ICE (1U << 7) /* IPC Checksum Error */ 1588d43a868SMichal Meloun #define RDESC0_GF (1U << 7) /* Giant Frame */ 1598d43a868SMichal Meloun #define RDESC0_LC (1U << 6) /* Late Collision */ 1608d43a868SMichal Meloun #define RDESC0_FT (1U << 5) /* Frame Type */ 1618d43a868SMichal Meloun #define RDESC0_RWT (1U << 4) /* Receive Watchdog Timeout */ 1628d43a868SMichal Meloun #define RDESC0_RE (1U << 3) /* Receive Error */ 1638d43a868SMichal Meloun #define RDESC0_DBE (1U << 2) /* Dribble Bit Error */ 1648d43a868SMichal Meloun #define RDESC0_CE (1U << 1) /* CRC Error */ 1658d43a868SMichal Meloun #define RDESC0_PCE (1U << 0) /* Payload Checksum Error */ 1668d43a868SMichal Meloun #define RDESC0_RXMA (1U << 0) /* Rx MAC Address */ 1675df53927SLuiz Otavio O Souza 1688d43a868SMichal Meloun /* RX descriptors - RDESC1 normal format */ 1698d43a868SMichal Meloun #define NRDESC1_DIC (1U << 31) /* Disable Intr on Completion */ 1708d43a868SMichal Meloun #define NRDESC1_RER (1U << 25) /* Receive End of Ring */ 1718d43a868SMichal Meloun #define NRDESC1_RCH (1U << 24) /* Second Address Chained */ 1728d43a868SMichal Meloun #define NRDESC1_RBS2_MASK 0x7ff 1738d43a868SMichal Meloun #define NRDESC1_RBS2_SHIFT 11 /* Receive Buffer 2 Size */ 1748d43a868SMichal Meloun #define NRDESC1_RBS1_MASK 0x7ff 1758d43a868SMichal Meloun #define NRDESC1_RBS1_SHIFT 0 /* Receive Buffer 1 Size */ 1768d43a868SMichal Meloun 1778d43a868SMichal Meloun /* RX descriptors - RDESC1 enhanced format */ 1788d43a868SMichal Meloun #define ERDESC1_DIC (1U << 31) /* Disable Intr on Completion */ 1798d43a868SMichal Meloun #define ERDESC1_RBS2_MASK 0x7ffff 1808d43a868SMichal Meloun #define ERDESC1_RBS2_SHIFT 16 /* Receive Buffer 2 Size */ 1818d43a868SMichal Meloun #define ERDESC1_RER (1U << 15) /* Receive End of Ring */ 1828d43a868SMichal Meloun #define ERDESC1_RCH (1U << 14) /* Second Address Chained */ 1838d43a868SMichal Meloun #define ERDESC1_RBS1_MASK 0x7ffff 1848d43a868SMichal Meloun #define ERDESC1_RBS1_SHIFT 0 /* Receive Buffer 1 Size */ 1855d43fd68SRuslan Bukin 1865d43fd68SRuslan Bukin /* 1875d43fd68SRuslan Bukin * A hardware buffer descriptor. Rx and Tx buffers have the same descriptor 1885df53927SLuiz Otavio O Souza * layout, but the bits in the fields have different meanings. 1895d43fd68SRuslan Bukin */ 1905d43fd68SRuslan Bukin struct dwc_hwdesc 1915d43fd68SRuslan Bukin { 1928d43a868SMichal Meloun uint32_t desc0; 1938d43a868SMichal Meloun uint32_t desc1; 1948d43a868SMichal Meloun uint32_t addr1; /* ptr to first buffer data */ 1958d43a868SMichal Meloun uint32_t addr2; /* ptr to next descriptor / second buffer data*/ 1965d43fd68SRuslan Bukin }; 1975d43fd68SRuslan Bukin 1985d43fd68SRuslan Bukin /* 1995d43fd68SRuslan Bukin * The hardware imposes alignment restrictions on various objects involved in 2005d43fd68SRuslan Bukin * DMA transfers. These values are expressed in bytes (not bits). 2015d43fd68SRuslan Bukin */ 2025d43fd68SRuslan Bukin #define DWC_DESC_RING_ALIGN 2048 2035d43fd68SRuslan Bukin 2045d43fd68SRuslan Bukin static struct resource_spec dwc_spec[] = { 2055d43fd68SRuslan Bukin { SYS_RES_MEMORY, 0, RF_ACTIVE }, 2065d43fd68SRuslan Bukin { SYS_RES_IRQ, 0, RF_ACTIVE }, 2075d43fd68SRuslan Bukin { -1, 0 } 2085d43fd68SRuslan Bukin }; 2095d43fd68SRuslan Bukin 2105d43fd68SRuslan Bukin static void dwc_txfinish_locked(struct dwc_softc *sc); 2115d43fd68SRuslan Bukin static void dwc_rxfinish_locked(struct dwc_softc *sc); 2125d43fd68SRuslan Bukin static void dwc_stop_locked(struct dwc_softc *sc); 2135d43fd68SRuslan Bukin static void dwc_setup_rxfilter(struct dwc_softc *sc); 2145d43fd68SRuslan Bukin 2155d43fd68SRuslan Bukin static inline uint32_t 2165d43fd68SRuslan Bukin next_rxidx(struct dwc_softc *sc, uint32_t curidx) 2175d43fd68SRuslan Bukin { 2185d43fd68SRuslan Bukin 2195d43fd68SRuslan Bukin return ((curidx + 1) % RX_DESC_COUNT); 2205d43fd68SRuslan Bukin } 2215d43fd68SRuslan Bukin 2225d43fd68SRuslan Bukin static inline uint32_t 2235d43fd68SRuslan Bukin next_txidx(struct dwc_softc *sc, uint32_t curidx) 2245d43fd68SRuslan Bukin { 2255d43fd68SRuslan Bukin 2265d43fd68SRuslan Bukin return ((curidx + 1) % TX_DESC_COUNT); 2275d43fd68SRuslan Bukin } 2285d43fd68SRuslan Bukin 2295d43fd68SRuslan Bukin static void 2305d43fd68SRuslan Bukin dwc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 2315d43fd68SRuslan Bukin { 2325d43fd68SRuslan Bukin 2335d43fd68SRuslan Bukin if (error != 0) 2345d43fd68SRuslan Bukin return; 2355d43fd68SRuslan Bukin *(bus_addr_t *)arg = segs[0].ds_addr; 2365d43fd68SRuslan Bukin } 2375d43fd68SRuslan Bukin 238b72e2878SMichal Meloun inline static void 2395d43fd68SRuslan Bukin dwc_setup_txdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr, 2405d43fd68SRuslan Bukin uint32_t len) 2415d43fd68SRuslan Bukin { 242b72e2878SMichal Meloun uint32_t desc0, desc1; 2435d43fd68SRuslan Bukin 2445d43fd68SRuslan Bukin /* Addr/len 0 means we're clearing the descriptor after xmit done. */ 2455d43fd68SRuslan Bukin if (paddr == 0 || len == 0) { 246b72e2878SMichal Meloun desc0 = 0; 247b72e2878SMichal Meloun desc1 = 0; 2485d43fd68SRuslan Bukin --sc->txcount; 2495d43fd68SRuslan Bukin } else { 250b72e2878SMichal Meloun if (sc->mactype != DWC_GMAC_EXT_DESC) { 251b72e2878SMichal Meloun desc0 = 0; 252b72e2878SMichal Meloun desc1 = NTDESC1_TCH | NTDESC1_FS | NTDESC1_LS | 253b72e2878SMichal Meloun NTDESC1_IC | len; 254b72e2878SMichal Meloun } else { 255b72e2878SMichal Meloun desc0 = ETDESC0_TCH | ETDESC0_FS | ETDESC0_LS | 2568d43a868SMichal Meloun ETDESC0_IC; 257b72e2878SMichal Meloun desc1 = len; 258b72e2878SMichal Meloun } 2595d43fd68SRuslan Bukin ++sc->txcount; 2605d43fd68SRuslan Bukin } 2615d43fd68SRuslan Bukin 2628d43a868SMichal Meloun sc->txdesc_ring[idx].addr1 = (uint32_t)(paddr); 263b72e2878SMichal Meloun sc->txdesc_ring[idx].desc0 = desc0; 264b72e2878SMichal Meloun sc->txdesc_ring[idx].desc1 = desc1; 2655d43fd68SRuslan Bukin 2665d43fd68SRuslan Bukin if (paddr && len) { 2675d43fd68SRuslan Bukin wmb(); 2688d43a868SMichal Meloun sc->txdesc_ring[idx].desc0 |= TDESC0_OWN; 2695d43fd68SRuslan Bukin wmb(); 2705d43fd68SRuslan Bukin } 2715d43fd68SRuslan Bukin } 2725d43fd68SRuslan Bukin 2735d43fd68SRuslan Bukin static int 2745d43fd68SRuslan Bukin dwc_setup_txbuf(struct dwc_softc *sc, int idx, struct mbuf **mp) 2755d43fd68SRuslan Bukin { 2765d43fd68SRuslan Bukin struct bus_dma_segment seg; 2775d43fd68SRuslan Bukin int error, nsegs; 2785d43fd68SRuslan Bukin struct mbuf * m; 2795d43fd68SRuslan Bukin 2805d43fd68SRuslan Bukin if ((m = m_defrag(*mp, M_NOWAIT)) == NULL) 2815d43fd68SRuslan Bukin return (ENOMEM); 2825d43fd68SRuslan Bukin *mp = m; 2835d43fd68SRuslan Bukin 2845d43fd68SRuslan Bukin error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, 2855d43fd68SRuslan Bukin m, &seg, &nsegs, 0); 2865d43fd68SRuslan Bukin if (error != 0) { 2875d43fd68SRuslan Bukin return (ENOMEM); 2885d43fd68SRuslan Bukin } 2895d43fd68SRuslan Bukin 2905d43fd68SRuslan Bukin KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 2915d43fd68SRuslan Bukin 2925d43fd68SRuslan Bukin bus_dmamap_sync(sc->txbuf_tag, sc->txbuf_map[idx].map, 2935d43fd68SRuslan Bukin BUS_DMASYNC_PREWRITE); 2945d43fd68SRuslan Bukin 2955d43fd68SRuslan Bukin sc->txbuf_map[idx].mbuf = m; 2965d43fd68SRuslan Bukin 2975d43fd68SRuslan Bukin dwc_setup_txdesc(sc, idx, seg.ds_addr, seg.ds_len); 2985d43fd68SRuslan Bukin 2995d43fd68SRuslan Bukin return (0); 3005d43fd68SRuslan Bukin } 3015d43fd68SRuslan Bukin 3025d43fd68SRuslan Bukin static void 3035d43fd68SRuslan Bukin dwc_txstart_locked(struct dwc_softc *sc) 3045d43fd68SRuslan Bukin { 3055d43fd68SRuslan Bukin struct ifnet *ifp; 3065d43fd68SRuslan Bukin struct mbuf *m; 3075d43fd68SRuslan Bukin int enqueued; 3085d43fd68SRuslan Bukin 3095d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 3105d43fd68SRuslan Bukin 3115d43fd68SRuslan Bukin if (!sc->link_is_up) 3125d43fd68SRuslan Bukin return; 3135d43fd68SRuslan Bukin 3145d43fd68SRuslan Bukin ifp = sc->ifp; 3155d43fd68SRuslan Bukin 3167ed7d979SMichal Meloun if (ifp->if_drv_flags & IFF_DRV_OACTIVE) 3175d43fd68SRuslan Bukin return; 3185d43fd68SRuslan Bukin 3195d43fd68SRuslan Bukin enqueued = 0; 3205d43fd68SRuslan Bukin 3215d43fd68SRuslan Bukin for (;;) { 3225d43fd68SRuslan Bukin if (sc->txcount == (TX_DESC_COUNT - 1)) { 3235d43fd68SRuslan Bukin ifp->if_drv_flags |= IFF_DRV_OACTIVE; 3245d43fd68SRuslan Bukin break; 3255d43fd68SRuslan Bukin } 3265d43fd68SRuslan Bukin 3275d43fd68SRuslan Bukin IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 3285d43fd68SRuslan Bukin if (m == NULL) 3295d43fd68SRuslan Bukin break; 3305d43fd68SRuslan Bukin if (dwc_setup_txbuf(sc, sc->tx_idx_head, &m) != 0) { 3315d43fd68SRuslan Bukin IFQ_DRV_PREPEND(&ifp->if_snd, m); 3325d43fd68SRuslan Bukin break; 3335d43fd68SRuslan Bukin } 3345d43fd68SRuslan Bukin BPF_MTAP(ifp, m); 3355d43fd68SRuslan Bukin sc->tx_idx_head = next_txidx(sc, sc->tx_idx_head); 3365d43fd68SRuslan Bukin ++enqueued; 3375d43fd68SRuslan Bukin } 3385d43fd68SRuslan Bukin 3395d43fd68SRuslan Bukin if (enqueued != 0) { 3405d43fd68SRuslan Bukin WRITE4(sc, TRANSMIT_POLL_DEMAND, 0x1); 3415d43fd68SRuslan Bukin sc->tx_watchdog_count = WATCHDOG_TIMEOUT_SECS; 3425d43fd68SRuslan Bukin } 3435d43fd68SRuslan Bukin } 3445d43fd68SRuslan Bukin 3455d43fd68SRuslan Bukin static void 3465d43fd68SRuslan Bukin dwc_txstart(struct ifnet *ifp) 3475d43fd68SRuslan Bukin { 3485d43fd68SRuslan Bukin struct dwc_softc *sc = ifp->if_softc; 3495d43fd68SRuslan Bukin 3505d43fd68SRuslan Bukin DWC_LOCK(sc); 3515d43fd68SRuslan Bukin dwc_txstart_locked(sc); 3525d43fd68SRuslan Bukin DWC_UNLOCK(sc); 3535d43fd68SRuslan Bukin } 3545d43fd68SRuslan Bukin 3555d43fd68SRuslan Bukin static void 3565d43fd68SRuslan Bukin dwc_stop_locked(struct dwc_softc *sc) 3575d43fd68SRuslan Bukin { 3585d43fd68SRuslan Bukin struct ifnet *ifp; 359ff0752c8SLuiz Otavio O Souza uint32_t reg; 3605d43fd68SRuslan Bukin 3615d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 3625d43fd68SRuslan Bukin 3635d43fd68SRuslan Bukin ifp = sc->ifp; 3645d43fd68SRuslan Bukin ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 3655d43fd68SRuslan Bukin sc->tx_watchdog_count = 0; 3665d43fd68SRuslan Bukin sc->stats_harvest_count = 0; 3675d43fd68SRuslan Bukin 3685d43fd68SRuslan Bukin callout_stop(&sc->dwc_callout); 3695d43fd68SRuslan Bukin 3705d43fd68SRuslan Bukin /* Stop DMA TX */ 3715d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 3725d43fd68SRuslan Bukin reg &= ~(MODE_ST); 3735d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 3745d43fd68SRuslan Bukin 3755d43fd68SRuslan Bukin /* Flush TX */ 3765d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 3775d43fd68SRuslan Bukin reg |= (MODE_FTF); 3785d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 3795d43fd68SRuslan Bukin 3805d43fd68SRuslan Bukin /* Stop transmitters */ 3815d43fd68SRuslan Bukin reg = READ4(sc, MAC_CONFIGURATION); 3825d43fd68SRuslan Bukin reg &= ~(CONF_TE | CONF_RE); 3835d43fd68SRuslan Bukin WRITE4(sc, MAC_CONFIGURATION, reg); 3845d43fd68SRuslan Bukin 3855d43fd68SRuslan Bukin /* Stop DMA RX */ 3865d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 3875d43fd68SRuslan Bukin reg &= ~(MODE_SR); 3885d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 3895d43fd68SRuslan Bukin } 3905d43fd68SRuslan Bukin 3915d43fd68SRuslan Bukin static void dwc_clear_stats(struct dwc_softc *sc) 3925d43fd68SRuslan Bukin { 393ff0752c8SLuiz Otavio O Souza uint32_t reg; 3945d43fd68SRuslan Bukin 3955d43fd68SRuslan Bukin reg = READ4(sc, MMC_CONTROL); 3965d43fd68SRuslan Bukin reg |= (MMC_CONTROL_CNTRST); 3975d43fd68SRuslan Bukin WRITE4(sc, MMC_CONTROL, reg); 3985d43fd68SRuslan Bukin } 3995d43fd68SRuslan Bukin 4005d43fd68SRuslan Bukin static void 4015d43fd68SRuslan Bukin dwc_harvest_stats(struct dwc_softc *sc) 4025d43fd68SRuslan Bukin { 4035d43fd68SRuslan Bukin struct ifnet *ifp; 4045d43fd68SRuslan Bukin 4055d43fd68SRuslan Bukin /* We don't need to harvest too often. */ 4065d43fd68SRuslan Bukin if (++sc->stats_harvest_count < STATS_HARVEST_INTERVAL) 4075d43fd68SRuslan Bukin return; 4085d43fd68SRuslan Bukin 4095d43fd68SRuslan Bukin sc->stats_harvest_count = 0; 4105d43fd68SRuslan Bukin ifp = sc->ifp; 4115d43fd68SRuslan Bukin 4125d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_IPACKETS, READ4(sc, RXFRAMECOUNT_GB)); 4135d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_IMCASTS, READ4(sc, RXMULTICASTFRAMES_G)); 4145d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_IERRORS, 4155d43fd68SRuslan Bukin READ4(sc, RXOVERSIZE_G) + READ4(sc, RXUNDERSIZE_G) + 4165d43fd68SRuslan Bukin READ4(sc, RXCRCERROR) + READ4(sc, RXALIGNMENTERROR) + 4175d43fd68SRuslan Bukin READ4(sc, RXRUNTERROR) + READ4(sc, RXJABBERERROR) + 4185d43fd68SRuslan Bukin READ4(sc, RXLENGTHERROR)); 4195d43fd68SRuslan Bukin 4205d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_OPACKETS, READ4(sc, TXFRAMECOUNT_G)); 4215d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_OMCASTS, READ4(sc, TXMULTICASTFRAMES_G)); 4225d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_OERRORS, 4235d43fd68SRuslan Bukin READ4(sc, TXOVERSIZE_G) + READ4(sc, TXEXCESSDEF) + 4245d43fd68SRuslan Bukin READ4(sc, TXCARRIERERR) + READ4(sc, TXUNDERFLOWERROR)); 4255d43fd68SRuslan Bukin 4265d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 4275d43fd68SRuslan Bukin READ4(sc, TXEXESSCOL) + READ4(sc, TXLATECOL)); 4285d43fd68SRuslan Bukin 4295d43fd68SRuslan Bukin dwc_clear_stats(sc); 4305d43fd68SRuslan Bukin } 4315d43fd68SRuslan Bukin 4325d43fd68SRuslan Bukin static void 4335d43fd68SRuslan Bukin dwc_tick(void *arg) 4345d43fd68SRuslan Bukin { 4355d43fd68SRuslan Bukin struct dwc_softc *sc; 4365d43fd68SRuslan Bukin struct ifnet *ifp; 4375d43fd68SRuslan Bukin int link_was_up; 4385d43fd68SRuslan Bukin 4395d43fd68SRuslan Bukin sc = arg; 4405d43fd68SRuslan Bukin 4415d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 4425d43fd68SRuslan Bukin 4435d43fd68SRuslan Bukin ifp = sc->ifp; 4445d43fd68SRuslan Bukin 4455d43fd68SRuslan Bukin if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) 4465d43fd68SRuslan Bukin return; 4475d43fd68SRuslan Bukin 4485d43fd68SRuslan Bukin /* 4495d43fd68SRuslan Bukin * Typical tx watchdog. If this fires it indicates that we enqueued 4505d43fd68SRuslan Bukin * packets for output and never got a txdone interrupt for them. Maybe 4515d43fd68SRuslan Bukin * it's a missed interrupt somehow, just pretend we got one. 4525d43fd68SRuslan Bukin */ 4535d43fd68SRuslan Bukin if (sc->tx_watchdog_count > 0) { 4545d43fd68SRuslan Bukin if (--sc->tx_watchdog_count == 0) { 4555d43fd68SRuslan Bukin dwc_txfinish_locked(sc); 4565d43fd68SRuslan Bukin } 4575d43fd68SRuslan Bukin } 4585d43fd68SRuslan Bukin 4595d43fd68SRuslan Bukin /* Gather stats from hardware counters. */ 4605d43fd68SRuslan Bukin dwc_harvest_stats(sc); 4615d43fd68SRuslan Bukin 4625d43fd68SRuslan Bukin /* Check the media status. */ 4635d43fd68SRuslan Bukin link_was_up = sc->link_is_up; 4645d43fd68SRuslan Bukin mii_tick(sc->mii_softc); 4655d43fd68SRuslan Bukin if (sc->link_is_up && !link_was_up) 4665d43fd68SRuslan Bukin dwc_txstart_locked(sc); 4675d43fd68SRuslan Bukin 4685d43fd68SRuslan Bukin /* Schedule another check one second from now. */ 4695d43fd68SRuslan Bukin callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); 4705d43fd68SRuslan Bukin } 4715d43fd68SRuslan Bukin 4725d43fd68SRuslan Bukin static void 4735d43fd68SRuslan Bukin dwc_init_locked(struct dwc_softc *sc) 4745d43fd68SRuslan Bukin { 4755d43fd68SRuslan Bukin struct ifnet *ifp = sc->ifp; 476ff0752c8SLuiz Otavio O Souza uint32_t reg; 4775d43fd68SRuslan Bukin 4785d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 4795d43fd68SRuslan Bukin 4805d43fd68SRuslan Bukin if (ifp->if_drv_flags & IFF_DRV_RUNNING) 4815d43fd68SRuslan Bukin return; 4825d43fd68SRuslan Bukin 4835d43fd68SRuslan Bukin ifp->if_drv_flags |= IFF_DRV_RUNNING; 4845d43fd68SRuslan Bukin 4855d43fd68SRuslan Bukin dwc_setup_rxfilter(sc); 4865d43fd68SRuslan Bukin 4875d43fd68SRuslan Bukin /* Initializa DMA and enable transmitters */ 4885d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 4895d43fd68SRuslan Bukin reg |= (MODE_TSF | MODE_OSF | MODE_FUF); 4905d43fd68SRuslan Bukin reg &= ~(MODE_RSF); 4915d43fd68SRuslan Bukin reg |= (MODE_RTC_LEV32 << MODE_RTC_SHIFT); 4925d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 4935d43fd68SRuslan Bukin 4945d43fd68SRuslan Bukin WRITE4(sc, INTERRUPT_ENABLE, INT_EN_DEFAULT); 4955d43fd68SRuslan Bukin 4965d43fd68SRuslan Bukin /* Start DMA */ 4975d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 4985d43fd68SRuslan Bukin reg |= (MODE_ST | MODE_SR); 4995d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 5005d43fd68SRuslan Bukin 5015d43fd68SRuslan Bukin /* Enable transmitters */ 5025d43fd68SRuslan Bukin reg = READ4(sc, MAC_CONFIGURATION); 5035d43fd68SRuslan Bukin reg |= (CONF_JD | CONF_ACS | CONF_BE); 5045d43fd68SRuslan Bukin reg |= (CONF_TE | CONF_RE); 5055d43fd68SRuslan Bukin WRITE4(sc, MAC_CONFIGURATION, reg); 5065d43fd68SRuslan Bukin 5075d43fd68SRuslan Bukin /* 5085d43fd68SRuslan Bukin * Call mii_mediachg() which will call back into dwc_miibus_statchg() 5095d43fd68SRuslan Bukin * to set up the remaining config registers based on current media. 5105d43fd68SRuslan Bukin */ 5115d43fd68SRuslan Bukin mii_mediachg(sc->mii_softc); 5125d43fd68SRuslan Bukin callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); 5135d43fd68SRuslan Bukin } 5145d43fd68SRuslan Bukin 5155d43fd68SRuslan Bukin static void 5165d43fd68SRuslan Bukin dwc_init(void *if_softc) 5175d43fd68SRuslan Bukin { 5185d43fd68SRuslan Bukin struct dwc_softc *sc = if_softc; 5195d43fd68SRuslan Bukin 5205d43fd68SRuslan Bukin DWC_LOCK(sc); 5215d43fd68SRuslan Bukin dwc_init_locked(sc); 5225d43fd68SRuslan Bukin DWC_UNLOCK(sc); 5235d43fd68SRuslan Bukin } 5245d43fd68SRuslan Bukin 525b72e2878SMichal Meloun 5265d43fd68SRuslan Bukin inline static uint32_t 5275d43fd68SRuslan Bukin dwc_setup_rxdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr) 5285d43fd68SRuslan Bukin { 5295d43fd68SRuslan Bukin uint32_t nidx; 5305d43fd68SRuslan Bukin 5318d43a868SMichal Meloun sc->rxdesc_ring[idx].addr1 = (uint32_t)paddr; 5325d43fd68SRuslan Bukin nidx = next_rxidx(sc, idx); 5338d43a868SMichal Meloun sc->rxdesc_ring[idx].addr2 = sc->rxdesc_ring_paddr + 5345d43fd68SRuslan Bukin (nidx * sizeof(struct dwc_hwdesc)); 535188aee74SMichal Meloun if (sc->mactype != DWC_GMAC_EXT_DESC) 536b72e2878SMichal Meloun sc->rxdesc_ring[idx].desc1 = NRDESC1_RCH | 537b72e2878SMichal Meloun MIN(MCLBYTES, NRDESC1_RBS1_MASK); 5385df53927SLuiz Otavio O Souza else 539b72e2878SMichal Meloun sc->rxdesc_ring[idx].desc1 = ERDESC1_RCH | 540b72e2878SMichal Meloun MIN(MCLBYTES, ERDESC1_RBS1_MASK); 5415d43fd68SRuslan Bukin 5425d43fd68SRuslan Bukin wmb(); 5438d43a868SMichal Meloun sc->rxdesc_ring[idx].desc0 = RDESC0_OWN; 5445d43fd68SRuslan Bukin wmb(); 5455d43fd68SRuslan Bukin return (nidx); 5465d43fd68SRuslan Bukin } 5475d43fd68SRuslan Bukin 5485d43fd68SRuslan Bukin static int 5495d43fd68SRuslan Bukin dwc_setup_rxbuf(struct dwc_softc *sc, int idx, struct mbuf *m) 5505d43fd68SRuslan Bukin { 5515d43fd68SRuslan Bukin struct bus_dma_segment seg; 5525d43fd68SRuslan Bukin int error, nsegs; 5535d43fd68SRuslan Bukin 5545d43fd68SRuslan Bukin m_adj(m, ETHER_ALIGN); 5555d43fd68SRuslan Bukin 5565d43fd68SRuslan Bukin error = bus_dmamap_load_mbuf_sg(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 5575d43fd68SRuslan Bukin m, &seg, &nsegs, 0); 5587ed7d979SMichal Meloun if (error != 0) 5595d43fd68SRuslan Bukin return (error); 5605d43fd68SRuslan Bukin 5615d43fd68SRuslan Bukin KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 5625d43fd68SRuslan Bukin 5635d43fd68SRuslan Bukin bus_dmamap_sync(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 5645d43fd68SRuslan Bukin BUS_DMASYNC_PREREAD); 5655d43fd68SRuslan Bukin 5665d43fd68SRuslan Bukin sc->rxbuf_map[idx].mbuf = m; 5675d43fd68SRuslan Bukin dwc_setup_rxdesc(sc, idx, seg.ds_addr); 5685d43fd68SRuslan Bukin 5695d43fd68SRuslan Bukin return (0); 5705d43fd68SRuslan Bukin } 5715d43fd68SRuslan Bukin 5725d43fd68SRuslan Bukin static struct mbuf * 5735d43fd68SRuslan Bukin dwc_alloc_mbufcl(struct dwc_softc *sc) 5745d43fd68SRuslan Bukin { 5755d43fd68SRuslan Bukin struct mbuf *m; 5765d43fd68SRuslan Bukin 5775d43fd68SRuslan Bukin m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 578db8a143aSRuslan Bukin if (m != NULL) 5795d43fd68SRuslan Bukin m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; 5805d43fd68SRuslan Bukin 5815d43fd68SRuslan Bukin return (m); 5825d43fd68SRuslan Bukin } 5835d43fd68SRuslan Bukin 584b72e2878SMichal Meloun static struct mbuf * 585b72e2878SMichal Meloun dwc_rxfinish_one(struct dwc_softc *sc, struct dwc_hwdesc *desc, 586b72e2878SMichal Meloun struct dwc_bufmap *map) 587b72e2878SMichal Meloun { 588b72e2878SMichal Meloun struct ifnet *ifp; 589b72e2878SMichal Meloun struct mbuf *m, *m0; 590b72e2878SMichal Meloun int len; 591b72e2878SMichal Meloun uint32_t rdesc0; 592b72e2878SMichal Meloun 593b72e2878SMichal Meloun m = map->mbuf; 594b72e2878SMichal Meloun ifp = sc->ifp; 595b72e2878SMichal Meloun rdesc0 = desc ->desc0; 596b72e2878SMichal Meloun /* Validate descriptor. */ 597b72e2878SMichal Meloun if (rdesc0 & RDESC0_ES) { 598b72e2878SMichal Meloun /* 599b72e2878SMichal Meloun * Errored packet. Statistic counters are updated 600b72e2878SMichal Meloun * globally, so do nothing 601b72e2878SMichal Meloun */ 602b72e2878SMichal Meloun return (NULL); 603b72e2878SMichal Meloun } 604b72e2878SMichal Meloun 605b72e2878SMichal Meloun if ((rdesc0 & (RDESC0_FS | RDESC0_LS)) != 606b72e2878SMichal Meloun (RDESC0_FS | RDESC0_LS)) { 607b72e2878SMichal Meloun /* 608b72e2878SMichal Meloun * Something very wrong happens. The whole packet should be 609b72e2878SMichal Meloun * recevied in one descriptr. Report problem. 610b72e2878SMichal Meloun */ 611b72e2878SMichal Meloun device_printf(sc->dev, 612b72e2878SMichal Meloun "%s: RX descriptor without FIRST and LAST bit set: 0x%08X", 613b72e2878SMichal Meloun __func__, rdesc0); 614b72e2878SMichal Meloun return (NULL); 615b72e2878SMichal Meloun } 616b72e2878SMichal Meloun 617b72e2878SMichal Meloun len = (rdesc0 >> RDESC0_FL_SHIFT) & RDESC0_FL_MASK; 618b72e2878SMichal Meloun if (len < 64) { 619b72e2878SMichal Meloun /* 620b72e2878SMichal Meloun * Lenght is invalid, recycle old mbuf 621b72e2878SMichal Meloun * Probably impossible case 622b72e2878SMichal Meloun */ 623b72e2878SMichal Meloun return (NULL); 624b72e2878SMichal Meloun } 625b72e2878SMichal Meloun 626b72e2878SMichal Meloun /* Allocate new buffer */ 627b72e2878SMichal Meloun m0 = dwc_alloc_mbufcl(sc); 628b72e2878SMichal Meloun if (m0 == NULL) { 629b72e2878SMichal Meloun /* no new mbuf available, recycle old */ 630b72e2878SMichal Meloun if_inc_counter(sc->ifp, IFCOUNTER_IQDROPS, 1); 631b72e2878SMichal Meloun return (NULL); 632b72e2878SMichal Meloun } 633b72e2878SMichal Meloun /* Do dmasync for newly received packet */ 634b72e2878SMichal Meloun bus_dmamap_sync(sc->rxbuf_tag, map->map, BUS_DMASYNC_POSTREAD); 635b72e2878SMichal Meloun bus_dmamap_unload(sc->rxbuf_tag, map->map); 636b72e2878SMichal Meloun 637b72e2878SMichal Meloun /* Received packet is valid, process it */ 638b72e2878SMichal Meloun m->m_pkthdr.rcvif = ifp; 639b72e2878SMichal Meloun m->m_pkthdr.len = len; 640b72e2878SMichal Meloun m->m_len = len; 641b72e2878SMichal Meloun if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 642b72e2878SMichal Meloun 643b72e2878SMichal Meloun /* Remove trailing FCS */ 644b72e2878SMichal Meloun m_adj(m, -ETHER_CRC_LEN); 645b72e2878SMichal Meloun 646b72e2878SMichal Meloun DWC_UNLOCK(sc); 647b72e2878SMichal Meloun (*ifp->if_input)(ifp, m); 648b72e2878SMichal Meloun DWC_LOCK(sc); 649b72e2878SMichal Meloun return (m0); 650b72e2878SMichal Meloun } 651b72e2878SMichal Meloun 6525d43fd68SRuslan Bukin static void 6535d43fd68SRuslan Bukin dwc_media_status(struct ifnet * ifp, struct ifmediareq *ifmr) 6545d43fd68SRuslan Bukin { 6555d43fd68SRuslan Bukin struct dwc_softc *sc; 6565d43fd68SRuslan Bukin struct mii_data *mii; 6575d43fd68SRuslan Bukin 6585d43fd68SRuslan Bukin sc = ifp->if_softc; 6595d43fd68SRuslan Bukin mii = sc->mii_softc; 6605d43fd68SRuslan Bukin DWC_LOCK(sc); 6615d43fd68SRuslan Bukin mii_pollstat(mii); 6625d43fd68SRuslan Bukin ifmr->ifm_active = mii->mii_media_active; 6635d43fd68SRuslan Bukin ifmr->ifm_status = mii->mii_media_status; 6645d43fd68SRuslan Bukin DWC_UNLOCK(sc); 6655d43fd68SRuslan Bukin } 6665d43fd68SRuslan Bukin 6675d43fd68SRuslan Bukin static int 6685d43fd68SRuslan Bukin dwc_media_change_locked(struct dwc_softc *sc) 6695d43fd68SRuslan Bukin { 6705d43fd68SRuslan Bukin 6715d43fd68SRuslan Bukin return (mii_mediachg(sc->mii_softc)); 6725d43fd68SRuslan Bukin } 6735d43fd68SRuslan Bukin 6745d43fd68SRuslan Bukin static int 6755d43fd68SRuslan Bukin dwc_media_change(struct ifnet * ifp) 6765d43fd68SRuslan Bukin { 6775d43fd68SRuslan Bukin struct dwc_softc *sc; 6785d43fd68SRuslan Bukin int error; 6795d43fd68SRuslan Bukin 6805d43fd68SRuslan Bukin sc = ifp->if_softc; 6815d43fd68SRuslan Bukin 6825d43fd68SRuslan Bukin DWC_LOCK(sc); 6835d43fd68SRuslan Bukin error = dwc_media_change_locked(sc); 6845d43fd68SRuslan Bukin DWC_UNLOCK(sc); 6855d43fd68SRuslan Bukin return (error); 6865d43fd68SRuslan Bukin } 6875d43fd68SRuslan Bukin 6885d43fd68SRuslan Bukin static const uint8_t nibbletab[] = { 6895d43fd68SRuslan Bukin /* 0x0 0000 -> 0000 */ 0x0, 6905d43fd68SRuslan Bukin /* 0x1 0001 -> 1000 */ 0x8, 6915d43fd68SRuslan Bukin /* 0x2 0010 -> 0100 */ 0x4, 6925d43fd68SRuslan Bukin /* 0x3 0011 -> 1100 */ 0xc, 6935d43fd68SRuslan Bukin /* 0x4 0100 -> 0010 */ 0x2, 6945d43fd68SRuslan Bukin /* 0x5 0101 -> 1010 */ 0xa, 6955d43fd68SRuslan Bukin /* 0x6 0110 -> 0110 */ 0x6, 6965d43fd68SRuslan Bukin /* 0x7 0111 -> 1110 */ 0xe, 6975d43fd68SRuslan Bukin /* 0x8 1000 -> 0001 */ 0x1, 6985d43fd68SRuslan Bukin /* 0x9 1001 -> 1001 */ 0x9, 6995d43fd68SRuslan Bukin /* 0xa 1010 -> 0101 */ 0x5, 7005d43fd68SRuslan Bukin /* 0xb 1011 -> 1101 */ 0xd, 7015d43fd68SRuslan Bukin /* 0xc 1100 -> 0011 */ 0x3, 7025d43fd68SRuslan Bukin /* 0xd 1101 -> 1011 */ 0xb, 7035d43fd68SRuslan Bukin /* 0xe 1110 -> 0111 */ 0x7, 7045d43fd68SRuslan Bukin /* 0xf 1111 -> 1111 */ 0xf, }; 7055d43fd68SRuslan Bukin 7065d43fd68SRuslan Bukin static uint8_t 7075d43fd68SRuslan Bukin bitreverse(uint8_t x) 7085d43fd68SRuslan Bukin { 7095d43fd68SRuslan Bukin 7105d43fd68SRuslan Bukin return (nibbletab[x & 0xf] << 4) | nibbletab[x >> 4]; 7115d43fd68SRuslan Bukin } 7125d43fd68SRuslan Bukin 7133e346c0aSGleb Smirnoff struct dwc_hash_maddr_ctx { 7143e346c0aSGleb Smirnoff struct dwc_softc *sc; 7153e346c0aSGleb Smirnoff uint32_t hash[8]; 7163e346c0aSGleb Smirnoff }; 7173e346c0aSGleb Smirnoff 7183e346c0aSGleb Smirnoff static u_int 7193e346c0aSGleb Smirnoff dwc_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) 7203e346c0aSGleb Smirnoff { 7213e346c0aSGleb Smirnoff struct dwc_hash_maddr_ctx *ctx = arg; 7223e346c0aSGleb Smirnoff uint32_t crc, hashbit, hashreg; 7233e346c0aSGleb Smirnoff uint8_t val; 7243e346c0aSGleb Smirnoff 7253e346c0aSGleb Smirnoff crc = ether_crc32_le(LLADDR(sdl), ETHER_ADDR_LEN); 7263e346c0aSGleb Smirnoff /* Take lower 8 bits and reverse it */ 7273e346c0aSGleb Smirnoff val = bitreverse(~crc & 0xff); 728188aee74SMichal Meloun if (ctx->sc->mactype != DWC_GMAC_EXT_DESC) 7293e346c0aSGleb Smirnoff val >>= 2; /* Only need lower 6 bits */ 7303e346c0aSGleb Smirnoff hashreg = (val >> 5); 7313e346c0aSGleb Smirnoff hashbit = (val & 31); 7323e346c0aSGleb Smirnoff ctx->hash[hashreg] |= (1 << hashbit); 7333e346c0aSGleb Smirnoff 7343e346c0aSGleb Smirnoff return (1); 7353e346c0aSGleb Smirnoff } 7363e346c0aSGleb Smirnoff 7375d43fd68SRuslan Bukin static void 7385d43fd68SRuslan Bukin dwc_setup_rxfilter(struct dwc_softc *sc) 7395d43fd68SRuslan Bukin { 7403e346c0aSGleb Smirnoff struct dwc_hash_maddr_ctx ctx; 7415d43fd68SRuslan Bukin struct ifnet *ifp; 7423e346c0aSGleb Smirnoff uint8_t *eaddr; 7433e346c0aSGleb Smirnoff uint32_t ffval, hi, lo; 744483ebfe0SJared McNeill int nhash, i; 7455d43fd68SRuslan Bukin 7465d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 7475d43fd68SRuslan Bukin 7485d43fd68SRuslan Bukin ifp = sc->ifp; 749188aee74SMichal Meloun nhash = sc->mactype != DWC_GMAC_EXT_DESC ? 2 : 8; 7505d43fd68SRuslan Bukin 7515d43fd68SRuslan Bukin /* 7525d43fd68SRuslan Bukin * Set the multicast (group) filter hash. 7535d43fd68SRuslan Bukin */ 754483ebfe0SJared McNeill if ((ifp->if_flags & IFF_ALLMULTI) != 0) { 7555d43fd68SRuslan Bukin ffval = (FRAME_FILTER_PM); 756483ebfe0SJared McNeill for (i = 0; i < nhash; i++) 7573e346c0aSGleb Smirnoff ctx.hash[i] = ~0; 758483ebfe0SJared McNeill } else { 7595d43fd68SRuslan Bukin ffval = (FRAME_FILTER_HMC); 760483ebfe0SJared McNeill for (i = 0; i < nhash; i++) 7613e346c0aSGleb Smirnoff ctx.hash[i] = 0; 7623e346c0aSGleb Smirnoff ctx.sc = sc; 7633e346c0aSGleb Smirnoff if_foreach_llmaddr(ifp, dwc_hash_maddr, &ctx); 7645d43fd68SRuslan Bukin } 7655d43fd68SRuslan Bukin 7665d43fd68SRuslan Bukin /* 7675d43fd68SRuslan Bukin * Set the individual address filter hash. 7685d43fd68SRuslan Bukin */ 7695d43fd68SRuslan Bukin if (ifp->if_flags & IFF_PROMISC) 7705d43fd68SRuslan Bukin ffval |= (FRAME_FILTER_PR); 7715d43fd68SRuslan Bukin 7725d43fd68SRuslan Bukin /* 7735d43fd68SRuslan Bukin * Set the primary address. 7745d43fd68SRuslan Bukin */ 7755d43fd68SRuslan Bukin eaddr = IF_LLADDR(ifp); 7765d43fd68SRuslan Bukin lo = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) | 7775d43fd68SRuslan Bukin (eaddr[3] << 24); 7785d43fd68SRuslan Bukin hi = eaddr[4] | (eaddr[5] << 8); 7795d43fd68SRuslan Bukin WRITE4(sc, MAC_ADDRESS_LOW(0), lo); 7805d43fd68SRuslan Bukin WRITE4(sc, MAC_ADDRESS_HIGH(0), hi); 7815d43fd68SRuslan Bukin WRITE4(sc, MAC_FRAME_FILTER, ffval); 782188aee74SMichal Meloun if (sc->mactype != DWC_GMAC_EXT_DESC) { 7833e346c0aSGleb Smirnoff WRITE4(sc, GMAC_MAC_HTLOW, ctx.hash[0]); 7843e346c0aSGleb Smirnoff WRITE4(sc, GMAC_MAC_HTHIGH, ctx.hash[1]); 785eeec1f9fSJared McNeill } else { 786483ebfe0SJared McNeill for (i = 0; i < nhash; i++) 7873e346c0aSGleb Smirnoff WRITE4(sc, HASH_TABLE_REG(i), ctx.hash[i]); 7885d43fd68SRuslan Bukin } 789eeec1f9fSJared McNeill } 7905d43fd68SRuslan Bukin 7915d43fd68SRuslan Bukin static int 7925d43fd68SRuslan Bukin dwc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 7935d43fd68SRuslan Bukin { 7945d43fd68SRuslan Bukin struct dwc_softc *sc; 7955d43fd68SRuslan Bukin struct mii_data *mii; 7965d43fd68SRuslan Bukin struct ifreq *ifr; 7975d43fd68SRuslan Bukin int mask, error; 7985d43fd68SRuslan Bukin 7995d43fd68SRuslan Bukin sc = ifp->if_softc; 8005d43fd68SRuslan Bukin ifr = (struct ifreq *)data; 8015d43fd68SRuslan Bukin 8025d43fd68SRuslan Bukin error = 0; 8035d43fd68SRuslan Bukin switch (cmd) { 8045d43fd68SRuslan Bukin case SIOCSIFFLAGS: 8055d43fd68SRuslan Bukin DWC_LOCK(sc); 8065d43fd68SRuslan Bukin if (ifp->if_flags & IFF_UP) { 8075d43fd68SRuslan Bukin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 8085d43fd68SRuslan Bukin if ((ifp->if_flags ^ sc->if_flags) & 8095d43fd68SRuslan Bukin (IFF_PROMISC | IFF_ALLMULTI)) 8105d43fd68SRuslan Bukin dwc_setup_rxfilter(sc); 8115d43fd68SRuslan Bukin } else { 8125d43fd68SRuslan Bukin if (!sc->is_detaching) 8135d43fd68SRuslan Bukin dwc_init_locked(sc); 8145d43fd68SRuslan Bukin } 8155d43fd68SRuslan Bukin } else { 8165d43fd68SRuslan Bukin if (ifp->if_drv_flags & IFF_DRV_RUNNING) 8175d43fd68SRuslan Bukin dwc_stop_locked(sc); 8185d43fd68SRuslan Bukin } 8195d43fd68SRuslan Bukin sc->if_flags = ifp->if_flags; 8205d43fd68SRuslan Bukin DWC_UNLOCK(sc); 8215d43fd68SRuslan Bukin break; 8225d43fd68SRuslan Bukin case SIOCADDMULTI: 8235d43fd68SRuslan Bukin case SIOCDELMULTI: 8245d43fd68SRuslan Bukin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 8255d43fd68SRuslan Bukin DWC_LOCK(sc); 8265d43fd68SRuslan Bukin dwc_setup_rxfilter(sc); 8275d43fd68SRuslan Bukin DWC_UNLOCK(sc); 8285d43fd68SRuslan Bukin } 8295d43fd68SRuslan Bukin break; 8305d43fd68SRuslan Bukin case SIOCSIFMEDIA: 8315d43fd68SRuslan Bukin case SIOCGIFMEDIA: 8325d43fd68SRuslan Bukin mii = sc->mii_softc; 8335d43fd68SRuslan Bukin error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); 8345d43fd68SRuslan Bukin break; 8355d43fd68SRuslan Bukin case SIOCSIFCAP: 8365d43fd68SRuslan Bukin mask = ifp->if_capenable ^ ifr->ifr_reqcap; 8375d43fd68SRuslan Bukin if (mask & IFCAP_VLAN_MTU) { 8385d43fd68SRuslan Bukin /* No work to do except acknowledge the change took */ 8395d43fd68SRuslan Bukin ifp->if_capenable ^= IFCAP_VLAN_MTU; 8405d43fd68SRuslan Bukin } 8415d43fd68SRuslan Bukin break; 8425d43fd68SRuslan Bukin 8435d43fd68SRuslan Bukin default: 8445d43fd68SRuslan Bukin error = ether_ioctl(ifp, cmd, data); 8455d43fd68SRuslan Bukin break; 8465d43fd68SRuslan Bukin } 8475d43fd68SRuslan Bukin 8485d43fd68SRuslan Bukin return (error); 8495d43fd68SRuslan Bukin } 8505d43fd68SRuslan Bukin 8515d43fd68SRuslan Bukin static void 8525d43fd68SRuslan Bukin dwc_txfinish_locked(struct dwc_softc *sc) 8535d43fd68SRuslan Bukin { 8545d43fd68SRuslan Bukin struct dwc_bufmap *bmap; 8555d43fd68SRuslan Bukin struct dwc_hwdesc *desc; 8569500101cSLuiz Otavio O Souza struct ifnet *ifp; 8575d43fd68SRuslan Bukin 8585d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 8595d43fd68SRuslan Bukin 860ecb3497fSLuiz Otavio O Souza ifp = sc->ifp; 8615d43fd68SRuslan Bukin while (sc->tx_idx_tail != sc->tx_idx_head) { 8625d43fd68SRuslan Bukin desc = &sc->txdesc_ring[sc->tx_idx_tail]; 8638d43a868SMichal Meloun if ((desc->desc0 & TDESC0_OWN) != 0) 8645d43fd68SRuslan Bukin break; 8655d43fd68SRuslan Bukin bmap = &sc->txbuf_map[sc->tx_idx_tail]; 8665d43fd68SRuslan Bukin bus_dmamap_sync(sc->txbuf_tag, bmap->map, 8675d43fd68SRuslan Bukin BUS_DMASYNC_POSTWRITE); 8685d43fd68SRuslan Bukin bus_dmamap_unload(sc->txbuf_tag, bmap->map); 8695d43fd68SRuslan Bukin m_freem(bmap->mbuf); 8705d43fd68SRuslan Bukin bmap->mbuf = NULL; 8715d43fd68SRuslan Bukin dwc_setup_txdesc(sc, sc->tx_idx_tail, 0, 0); 8725d43fd68SRuslan Bukin sc->tx_idx_tail = next_txidx(sc, sc->tx_idx_tail); 8739500101cSLuiz Otavio O Souza ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 874a5221d68SLuiz Otavio O Souza if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 8755d43fd68SRuslan Bukin } 8765d43fd68SRuslan Bukin 8775d43fd68SRuslan Bukin /* If there are no buffers outstanding, muzzle the watchdog. */ 8785d43fd68SRuslan Bukin if (sc->tx_idx_tail == sc->tx_idx_head) { 8795d43fd68SRuslan Bukin sc->tx_watchdog_count = 0; 8805d43fd68SRuslan Bukin } 8815d43fd68SRuslan Bukin } 8825d43fd68SRuslan Bukin 8835d43fd68SRuslan Bukin static void 8845d43fd68SRuslan Bukin dwc_rxfinish_locked(struct dwc_softc *sc) 8855d43fd68SRuslan Bukin { 8865d43fd68SRuslan Bukin struct ifnet *ifp; 8875d43fd68SRuslan Bukin struct mbuf *m; 888b72e2878SMichal Meloun int error, idx; 889b72e2878SMichal Meloun struct dwc_hwdesc *desc; 8905d43fd68SRuslan Bukin 891b72e2878SMichal Meloun DWC_ASSERT_LOCKED(sc); 8925d43fd68SRuslan Bukin ifp = sc->ifp; 8935d43fd68SRuslan Bukin for (;;) { 8945d43fd68SRuslan Bukin idx = sc->rx_idx; 895b72e2878SMichal Meloun desc = sc->rxdesc_ring + idx; 896b72e2878SMichal Meloun if ((desc->desc0 & RDESC0_OWN) != 0) 8975d43fd68SRuslan Bukin break; 8985d43fd68SRuslan Bukin 899b72e2878SMichal Meloun m = dwc_rxfinish_one(sc, desc, sc->rxbuf_map + idx); 900b72e2878SMichal Meloun if (m == NULL) { 901b72e2878SMichal Meloun wmb(); 902b72e2878SMichal Meloun desc->desc0 = RDESC0_OWN; 903b72e2878SMichal Meloun wmb(); 9045d43fd68SRuslan Bukin } else { 905b72e2878SMichal Meloun /* We cannot create hole in RX ring */ 906b72e2878SMichal Meloun error = dwc_setup_rxbuf(sc, idx, m); 907b72e2878SMichal Meloun if (error != 0) 908b72e2878SMichal Meloun panic("dwc_setup_rxbuf failed: error %d\n", 909b72e2878SMichal Meloun error); 9105d43fd68SRuslan Bukin } 9115d43fd68SRuslan Bukin sc->rx_idx = next_rxidx(sc, sc->rx_idx); 9125d43fd68SRuslan Bukin } 9135d43fd68SRuslan Bukin } 9145d43fd68SRuslan Bukin 9155d43fd68SRuslan Bukin static void 9165d43fd68SRuslan Bukin dwc_intr(void *arg) 9175d43fd68SRuslan Bukin { 9185d43fd68SRuslan Bukin struct dwc_softc *sc; 9195d43fd68SRuslan Bukin uint32_t reg; 9205d43fd68SRuslan Bukin 9215d43fd68SRuslan Bukin sc = arg; 9225d43fd68SRuslan Bukin 9235d43fd68SRuslan Bukin DWC_LOCK(sc); 9245d43fd68SRuslan Bukin 9255d43fd68SRuslan Bukin reg = READ4(sc, INTERRUPT_STATUS); 9268fbc5d18SLuiz Otavio O Souza if (reg) 9275d43fd68SRuslan Bukin READ4(sc, SGMII_RGMII_SMII_CTRL_STATUS); 9285d43fd68SRuslan Bukin 9295d43fd68SRuslan Bukin reg = READ4(sc, DMA_STATUS); 9305d43fd68SRuslan Bukin if (reg & DMA_STATUS_NIS) { 9315d43fd68SRuslan Bukin if (reg & DMA_STATUS_RI) 9325d43fd68SRuslan Bukin dwc_rxfinish_locked(sc); 9335d43fd68SRuslan Bukin 9349500101cSLuiz Otavio O Souza if (reg & DMA_STATUS_TI) { 9355d43fd68SRuslan Bukin dwc_txfinish_locked(sc); 9369500101cSLuiz Otavio O Souza dwc_txstart_locked(sc); 9379500101cSLuiz Otavio O Souza } 9385d43fd68SRuslan Bukin } 9395d43fd68SRuslan Bukin 9405d43fd68SRuslan Bukin if (reg & DMA_STATUS_AIS) { 9415d43fd68SRuslan Bukin if (reg & DMA_STATUS_FBI) { 9425d43fd68SRuslan Bukin /* Fatal bus error */ 9435d43fd68SRuslan Bukin device_printf(sc->dev, 9445d43fd68SRuslan Bukin "Ethernet DMA error, restarting controller.\n"); 9455d43fd68SRuslan Bukin dwc_stop_locked(sc); 9465d43fd68SRuslan Bukin dwc_init_locked(sc); 9475d43fd68SRuslan Bukin } 9485d43fd68SRuslan Bukin } 9495d43fd68SRuslan Bukin 9505d43fd68SRuslan Bukin WRITE4(sc, DMA_STATUS, reg & DMA_STATUS_INTR_MASK); 9515d43fd68SRuslan Bukin DWC_UNLOCK(sc); 9525d43fd68SRuslan Bukin } 9535d43fd68SRuslan Bukin 9545d43fd68SRuslan Bukin static int 9555d43fd68SRuslan Bukin setup_dma(struct dwc_softc *sc) 9565d43fd68SRuslan Bukin { 9575d43fd68SRuslan Bukin struct mbuf *m; 9585d43fd68SRuslan Bukin int error; 9595d43fd68SRuslan Bukin int nidx; 9605d43fd68SRuslan Bukin int idx; 9615d43fd68SRuslan Bukin 9625d43fd68SRuslan Bukin /* 9635d43fd68SRuslan Bukin * Set up TX descriptor ring, descriptors, and dma maps. 9645d43fd68SRuslan Bukin */ 9655d43fd68SRuslan Bukin error = bus_dma_tag_create( 9665d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 9675d43fd68SRuslan Bukin DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ 9685d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9695d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9705d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 9715d43fd68SRuslan Bukin TX_DESC_SIZE, 1, /* maxsize, nsegments */ 9725d43fd68SRuslan Bukin TX_DESC_SIZE, /* maxsegsize */ 9735d43fd68SRuslan Bukin 0, /* flags */ 9745d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9755d43fd68SRuslan Bukin &sc->txdesc_tag); 9765d43fd68SRuslan Bukin if (error != 0) { 9775d43fd68SRuslan Bukin device_printf(sc->dev, 9785d43fd68SRuslan Bukin "could not create TX ring DMA tag.\n"); 9795d43fd68SRuslan Bukin goto out; 9805d43fd68SRuslan Bukin } 9815d43fd68SRuslan Bukin 9825d43fd68SRuslan Bukin error = bus_dmamem_alloc(sc->txdesc_tag, (void**)&sc->txdesc_ring, 9835d43fd68SRuslan Bukin BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, 9845d43fd68SRuslan Bukin &sc->txdesc_map); 9855d43fd68SRuslan Bukin if (error != 0) { 9865d43fd68SRuslan Bukin device_printf(sc->dev, 9875d43fd68SRuslan Bukin "could not allocate TX descriptor ring.\n"); 9885d43fd68SRuslan Bukin goto out; 9895d43fd68SRuslan Bukin } 9905d43fd68SRuslan Bukin 9915d43fd68SRuslan Bukin error = bus_dmamap_load(sc->txdesc_tag, sc->txdesc_map, 9925d43fd68SRuslan Bukin sc->txdesc_ring, TX_DESC_SIZE, dwc_get1paddr, 9935d43fd68SRuslan Bukin &sc->txdesc_ring_paddr, 0); 9945d43fd68SRuslan Bukin if (error != 0) { 9955d43fd68SRuslan Bukin device_printf(sc->dev, 9965d43fd68SRuslan Bukin "could not load TX descriptor ring map.\n"); 9975d43fd68SRuslan Bukin goto out; 9985d43fd68SRuslan Bukin } 9995d43fd68SRuslan Bukin 10005d43fd68SRuslan Bukin for (idx = 0; idx < TX_DESC_COUNT; idx++) { 10015d43fd68SRuslan Bukin nidx = next_txidx(sc, idx); 10028d43a868SMichal Meloun sc->txdesc_ring[idx].addr2 = sc->txdesc_ring_paddr + 10035d43fd68SRuslan Bukin (nidx * sizeof(struct dwc_hwdesc)); 10045d43fd68SRuslan Bukin } 10055d43fd68SRuslan Bukin 10065d43fd68SRuslan Bukin error = bus_dma_tag_create( 10075d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 10085d43fd68SRuslan Bukin 1, 0, /* alignment, boundary */ 10095d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 10105d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 10115d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 10125d43fd68SRuslan Bukin MCLBYTES, 1, /* maxsize, nsegments */ 10135d43fd68SRuslan Bukin MCLBYTES, /* maxsegsize */ 10145d43fd68SRuslan Bukin 0, /* flags */ 10155d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 10165d43fd68SRuslan Bukin &sc->txbuf_tag); 10175d43fd68SRuslan Bukin if (error != 0) { 10185d43fd68SRuslan Bukin device_printf(sc->dev, 10195d43fd68SRuslan Bukin "could not create TX ring DMA tag.\n"); 10205d43fd68SRuslan Bukin goto out; 10215d43fd68SRuslan Bukin } 10225d43fd68SRuslan Bukin 10235d43fd68SRuslan Bukin for (idx = 0; idx < TX_DESC_COUNT; idx++) { 10245d43fd68SRuslan Bukin error = bus_dmamap_create(sc->txbuf_tag, BUS_DMA_COHERENT, 10255d43fd68SRuslan Bukin &sc->txbuf_map[idx].map); 10265d43fd68SRuslan Bukin if (error != 0) { 10275d43fd68SRuslan Bukin device_printf(sc->dev, 10285d43fd68SRuslan Bukin "could not create TX buffer DMA map.\n"); 10295d43fd68SRuslan Bukin goto out; 10305d43fd68SRuslan Bukin } 10315d43fd68SRuslan Bukin dwc_setup_txdesc(sc, idx, 0, 0); 10325d43fd68SRuslan Bukin } 10335d43fd68SRuslan Bukin 10345d43fd68SRuslan Bukin /* 10355d43fd68SRuslan Bukin * Set up RX descriptor ring, descriptors, dma maps, and mbufs. 10365d43fd68SRuslan Bukin */ 10375d43fd68SRuslan Bukin error = bus_dma_tag_create( 10385d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 10395d43fd68SRuslan Bukin DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ 10405d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 10415d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 10425d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 10435d43fd68SRuslan Bukin RX_DESC_SIZE, 1, /* maxsize, nsegments */ 10445d43fd68SRuslan Bukin RX_DESC_SIZE, /* maxsegsize */ 10455d43fd68SRuslan Bukin 0, /* flags */ 10465d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 10475d43fd68SRuslan Bukin &sc->rxdesc_tag); 10485d43fd68SRuslan Bukin if (error != 0) { 10495d43fd68SRuslan Bukin device_printf(sc->dev, 10505d43fd68SRuslan Bukin "could not create RX ring DMA tag.\n"); 10515d43fd68SRuslan Bukin goto out; 10525d43fd68SRuslan Bukin } 10535d43fd68SRuslan Bukin 10545d43fd68SRuslan Bukin error = bus_dmamem_alloc(sc->rxdesc_tag, (void **)&sc->rxdesc_ring, 10555d43fd68SRuslan Bukin BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, 10565d43fd68SRuslan Bukin &sc->rxdesc_map); 10575d43fd68SRuslan Bukin if (error != 0) { 10585d43fd68SRuslan Bukin device_printf(sc->dev, 10595d43fd68SRuslan Bukin "could not allocate RX descriptor ring.\n"); 10605d43fd68SRuslan Bukin goto out; 10615d43fd68SRuslan Bukin } 10625d43fd68SRuslan Bukin 10635d43fd68SRuslan Bukin error = bus_dmamap_load(sc->rxdesc_tag, sc->rxdesc_map, 10645d43fd68SRuslan Bukin sc->rxdesc_ring, RX_DESC_SIZE, dwc_get1paddr, 10655d43fd68SRuslan Bukin &sc->rxdesc_ring_paddr, 0); 10665d43fd68SRuslan Bukin if (error != 0) { 10675d43fd68SRuslan Bukin device_printf(sc->dev, 10685d43fd68SRuslan Bukin "could not load RX descriptor ring map.\n"); 10695d43fd68SRuslan Bukin goto out; 10705d43fd68SRuslan Bukin } 10715d43fd68SRuslan Bukin 10725d43fd68SRuslan Bukin error = bus_dma_tag_create( 10735d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 10745d43fd68SRuslan Bukin 1, 0, /* alignment, boundary */ 10755d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 10765d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 10775d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 10785d43fd68SRuslan Bukin MCLBYTES, 1, /* maxsize, nsegments */ 10795d43fd68SRuslan Bukin MCLBYTES, /* maxsegsize */ 10805d43fd68SRuslan Bukin 0, /* flags */ 10815d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 10825d43fd68SRuslan Bukin &sc->rxbuf_tag); 10835d43fd68SRuslan Bukin if (error != 0) { 10845d43fd68SRuslan Bukin device_printf(sc->dev, 10855d43fd68SRuslan Bukin "could not create RX buf DMA tag.\n"); 10865d43fd68SRuslan Bukin goto out; 10875d43fd68SRuslan Bukin } 10885d43fd68SRuslan Bukin 10895d43fd68SRuslan Bukin for (idx = 0; idx < RX_DESC_COUNT; idx++) { 10905d43fd68SRuslan Bukin error = bus_dmamap_create(sc->rxbuf_tag, BUS_DMA_COHERENT, 10915d43fd68SRuslan Bukin &sc->rxbuf_map[idx].map); 10925d43fd68SRuslan Bukin if (error != 0) { 10935d43fd68SRuslan Bukin device_printf(sc->dev, 10945d43fd68SRuslan Bukin "could not create RX buffer DMA map.\n"); 10955d43fd68SRuslan Bukin goto out; 10965d43fd68SRuslan Bukin } 10975d43fd68SRuslan Bukin if ((m = dwc_alloc_mbufcl(sc)) == NULL) { 10985d43fd68SRuslan Bukin device_printf(sc->dev, "Could not alloc mbuf\n"); 10995d43fd68SRuslan Bukin error = ENOMEM; 11005d43fd68SRuslan Bukin goto out; 11015d43fd68SRuslan Bukin } 11025d43fd68SRuslan Bukin if ((error = dwc_setup_rxbuf(sc, idx, m)) != 0) { 11035d43fd68SRuslan Bukin device_printf(sc->dev, 11045d43fd68SRuslan Bukin "could not create new RX buffer.\n"); 11055d43fd68SRuslan Bukin goto out; 11065d43fd68SRuslan Bukin } 11075d43fd68SRuslan Bukin } 11085d43fd68SRuslan Bukin 11095d43fd68SRuslan Bukin out: 11105d43fd68SRuslan Bukin if (error != 0) 11115d43fd68SRuslan Bukin return (ENXIO); 11125d43fd68SRuslan Bukin 11135d43fd68SRuslan Bukin return (0); 11145d43fd68SRuslan Bukin } 11155d43fd68SRuslan Bukin 11165d43fd68SRuslan Bukin static int 11175d43fd68SRuslan Bukin dwc_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr) 11185d43fd68SRuslan Bukin { 1119ff0752c8SLuiz Otavio O Souza uint32_t hi, lo, rnd; 11205d43fd68SRuslan Bukin 11215d43fd68SRuslan Bukin /* 11225d43fd68SRuslan Bukin * Try to recover a MAC address from the running hardware. If there's 11235d43fd68SRuslan Bukin * something non-zero there, assume the bootloader did the right thing 11245d43fd68SRuslan Bukin * and just use it. 11255d43fd68SRuslan Bukin * 11265d43fd68SRuslan Bukin * Otherwise, set the address to a convenient locally assigned address, 11275d43fd68SRuslan Bukin * 'bsd' + random 24 low-order bits. 'b' is 0x62, which has the locally 11285d43fd68SRuslan Bukin * assigned bit set, and the broadcast/multicast bit clear. 11295d43fd68SRuslan Bukin */ 11305d43fd68SRuslan Bukin lo = READ4(sc, MAC_ADDRESS_LOW(0)); 11315d43fd68SRuslan Bukin hi = READ4(sc, MAC_ADDRESS_HIGH(0)) & 0xffff; 11325d43fd68SRuslan Bukin if ((lo != 0xffffffff) || (hi != 0xffff)) { 11335d43fd68SRuslan Bukin hwaddr[0] = (lo >> 0) & 0xff; 11345d43fd68SRuslan Bukin hwaddr[1] = (lo >> 8) & 0xff; 11355d43fd68SRuslan Bukin hwaddr[2] = (lo >> 16) & 0xff; 11365d43fd68SRuslan Bukin hwaddr[3] = (lo >> 24) & 0xff; 11375d43fd68SRuslan Bukin hwaddr[4] = (hi >> 0) & 0xff; 11385d43fd68SRuslan Bukin hwaddr[5] = (hi >> 8) & 0xff; 11395d43fd68SRuslan Bukin } else { 11405d43fd68SRuslan Bukin rnd = arc4random() & 0x00ffffff; 11415d43fd68SRuslan Bukin hwaddr[0] = 'b'; 11425d43fd68SRuslan Bukin hwaddr[1] = 's'; 11435d43fd68SRuslan Bukin hwaddr[2] = 'd'; 11445d43fd68SRuslan Bukin hwaddr[3] = rnd >> 16; 11455d43fd68SRuslan Bukin hwaddr[4] = rnd >> 8; 11465d43fd68SRuslan Bukin hwaddr[5] = rnd >> 0; 11475d43fd68SRuslan Bukin } 11485d43fd68SRuslan Bukin 11495d43fd68SRuslan Bukin return (0); 11505d43fd68SRuslan Bukin } 11515d43fd68SRuslan Bukin 1152d7acb49aSJared McNeill #define GPIO_ACTIVE_LOW 1 1153d7acb49aSJared McNeill 1154d7acb49aSJared McNeill static int 1155d7acb49aSJared McNeill dwc_reset(device_t dev) 1156d7acb49aSJared McNeill { 1157d7acb49aSJared McNeill pcell_t gpio_prop[4]; 1158d7acb49aSJared McNeill pcell_t delay_prop[3]; 1159d7acb49aSJared McNeill phandle_t node, gpio_node; 1160d7acb49aSJared McNeill device_t gpio; 1161d7acb49aSJared McNeill uint32_t pin, flags; 1162d7acb49aSJared McNeill uint32_t pin_value; 1163d7acb49aSJared McNeill 1164d7acb49aSJared McNeill node = ofw_bus_get_node(dev); 1165d7acb49aSJared McNeill if (OF_getencprop(node, "snps,reset-gpio", 1166d7acb49aSJared McNeill gpio_prop, sizeof(gpio_prop)) <= 0) 1167d7acb49aSJared McNeill return (0); 1168d7acb49aSJared McNeill 1169d7acb49aSJared McNeill if (OF_getencprop(node, "snps,reset-delays-us", 1170d7acb49aSJared McNeill delay_prop, sizeof(delay_prop)) <= 0) { 1171d7acb49aSJared McNeill device_printf(dev, 1172d7acb49aSJared McNeill "Wrong property for snps,reset-delays-us"); 1173d7acb49aSJared McNeill return (ENXIO); 1174d7acb49aSJared McNeill } 1175d7acb49aSJared McNeill 1176d7acb49aSJared McNeill gpio_node = OF_node_from_xref(gpio_prop[0]); 1177d7acb49aSJared McNeill if ((gpio = OF_device_from_xref(gpio_prop[0])) == NULL) { 1178d7acb49aSJared McNeill device_printf(dev, 1179d7acb49aSJared McNeill "Can't find gpio controller for phy reset\n"); 1180d7acb49aSJared McNeill return (ENXIO); 1181d7acb49aSJared McNeill } 1182d7acb49aSJared McNeill 1183d7acb49aSJared McNeill if (GPIO_MAP_GPIOS(gpio, node, gpio_node, 118473a1170aSPedro F. Giffuni nitems(gpio_prop) - 1, 1185d7acb49aSJared McNeill gpio_prop + 1, &pin, &flags) != 0) { 1186d7acb49aSJared McNeill device_printf(dev, "Can't map gpio for phy reset\n"); 1187d7acb49aSJared McNeill return (ENXIO); 1188d7acb49aSJared McNeill } 1189d7acb49aSJared McNeill 1190d7acb49aSJared McNeill pin_value = GPIO_PIN_LOW; 1191d7acb49aSJared McNeill if (OF_hasprop(node, "snps,reset-active-low")) 1192d7acb49aSJared McNeill pin_value = GPIO_PIN_HIGH; 1193d7acb49aSJared McNeill 1194d7acb49aSJared McNeill GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT); 1195d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 1196c069412eSEmmanuel Vadot DELAY(delay_prop[0] * 5); 1197d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, !pin_value); 1198c069412eSEmmanuel Vadot DELAY(delay_prop[1] * 5); 1199d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 1200c069412eSEmmanuel Vadot DELAY(delay_prop[2] * 5); 1201d7acb49aSJared McNeill 1202d7acb49aSJared McNeill return (0); 1203d7acb49aSJared McNeill } 1204d7acb49aSJared McNeill 12056a05f063SJared McNeill #ifdef EXT_RESOURCES 12066a05f063SJared McNeill static int 12076a05f063SJared McNeill dwc_clock_init(device_t dev) 12086a05f063SJared McNeill { 12096a05f063SJared McNeill hwreset_t rst; 12106a05f063SJared McNeill clk_t clk; 12116a05f063SJared McNeill int error; 1212*824cfb47SOleksandr Tymoshenko int64_t freq; 12136a05f063SJared McNeill 1214*824cfb47SOleksandr Tymoshenko /* Enable clocks */ 1215dac93553SMichal Meloun if (clk_get_by_ofw_name(dev, 0, "stmmaceth", &clk) == 0) { 12166a05f063SJared McNeill error = clk_enable(clk); 12176a05f063SJared McNeill if (error != 0) { 12186a05f063SJared McNeill device_printf(dev, "could not enable main clock\n"); 12196a05f063SJared McNeill return (error); 12206a05f063SJared McNeill } 1221*824cfb47SOleksandr Tymoshenko if (bootverbose) { 1222*824cfb47SOleksandr Tymoshenko clk_get_freq(clk, &freq); 1223*824cfb47SOleksandr Tymoshenko device_printf(dev, "MAC clock(%s) freq: %ld\n", clk_get_name(clk), freq); 1224*824cfb47SOleksandr Tymoshenko } 1225*824cfb47SOleksandr Tymoshenko } 1226*824cfb47SOleksandr Tymoshenko else { 1227*824cfb47SOleksandr Tymoshenko device_printf(dev, "could not find clock stmmaceth\n"); 12286a05f063SJared McNeill } 12296a05f063SJared McNeill 12306a05f063SJared McNeill /* De-assert reset */ 1231dac93553SMichal Meloun if (hwreset_get_by_ofw_name(dev, 0, "stmmaceth", &rst) == 0) { 12326a05f063SJared McNeill error = hwreset_deassert(rst); 12336a05f063SJared McNeill if (error != 0) { 12346a05f063SJared McNeill device_printf(dev, "could not de-assert reset\n"); 12356a05f063SJared McNeill return (error); 12366a05f063SJared McNeill } 12376a05f063SJared McNeill } 12386a05f063SJared McNeill 12396a05f063SJared McNeill return (0); 12406a05f063SJared McNeill } 12416a05f063SJared McNeill #endif 12426a05f063SJared McNeill 12435d43fd68SRuslan Bukin static int 12445d43fd68SRuslan Bukin dwc_probe(device_t dev) 12455d43fd68SRuslan Bukin { 12465d43fd68SRuslan Bukin 12475d43fd68SRuslan Bukin if (!ofw_bus_status_okay(dev)) 12485d43fd68SRuslan Bukin return (ENXIO); 12495d43fd68SRuslan Bukin 12505d43fd68SRuslan Bukin if (!ofw_bus_is_compatible(dev, "snps,dwmac")) 12515d43fd68SRuslan Bukin return (ENXIO); 12525d43fd68SRuslan Bukin 12535d43fd68SRuslan Bukin device_set_desc(dev, "Gigabit Ethernet Controller"); 12545d43fd68SRuslan Bukin return (BUS_PROBE_DEFAULT); 12555d43fd68SRuslan Bukin } 12565d43fd68SRuslan Bukin 12575d43fd68SRuslan Bukin static int 12585d43fd68SRuslan Bukin dwc_attach(device_t dev) 12595d43fd68SRuslan Bukin { 12605d43fd68SRuslan Bukin uint8_t macaddr[ETHER_ADDR_LEN]; 12615d43fd68SRuslan Bukin struct dwc_softc *sc; 12625d43fd68SRuslan Bukin struct ifnet *ifp; 1263ff0752c8SLuiz Otavio O Souza int error, i; 1264ff0752c8SLuiz Otavio O Souza uint32_t reg; 1265*824cfb47SOleksandr Tymoshenko char *phy_mode; 1266*824cfb47SOleksandr Tymoshenko phandle_t node; 12675d43fd68SRuslan Bukin 12685d43fd68SRuslan Bukin sc = device_get_softc(dev); 12695d43fd68SRuslan Bukin sc->dev = dev; 12705d43fd68SRuslan Bukin sc->rx_idx = 0; 12715d43fd68SRuslan Bukin sc->txcount = TX_DESC_COUNT; 12725df53927SLuiz Otavio O Souza sc->mii_clk = IF_DWC_MII_CLK(dev); 12735df53927SLuiz Otavio O Souza sc->mactype = IF_DWC_MAC_TYPE(dev); 12745df53927SLuiz Otavio O Souza 1275*824cfb47SOleksandr Tymoshenko node = ofw_bus_get_node(dev); 1276*824cfb47SOleksandr Tymoshenko if (OF_getprop_alloc(node, "phy-mode", (void **)&phy_mode)) { 1277*824cfb47SOleksandr Tymoshenko if (strcmp(phy_mode, "rgmii") == 0) 1278*824cfb47SOleksandr Tymoshenko sc->phy_mode = PHY_MODE_RGMII; 1279*824cfb47SOleksandr Tymoshenko if (strcmp(phy_mode, "rmii") == 0) 1280*824cfb47SOleksandr Tymoshenko sc->phy_mode = PHY_MODE_RMII; 1281*824cfb47SOleksandr Tymoshenko OF_prop_free(phy_mode); 1282*824cfb47SOleksandr Tymoshenko } 1283*824cfb47SOleksandr Tymoshenko 12845df53927SLuiz Otavio O Souza if (IF_DWC_INIT(dev) != 0) 12855df53927SLuiz Otavio O Souza return (ENXIO); 12865d43fd68SRuslan Bukin 12876a05f063SJared McNeill #ifdef EXT_RESOURCES 12886a05f063SJared McNeill if (dwc_clock_init(dev) != 0) 12896a05f063SJared McNeill return (ENXIO); 12906a05f063SJared McNeill #endif 12916a05f063SJared McNeill 12925d43fd68SRuslan Bukin if (bus_alloc_resources(dev, dwc_spec, sc->res)) { 12935d43fd68SRuslan Bukin device_printf(dev, "could not allocate resources\n"); 12945d43fd68SRuslan Bukin return (ENXIO); 12955d43fd68SRuslan Bukin } 12965d43fd68SRuslan Bukin 12975d43fd68SRuslan Bukin /* Read MAC before reset */ 12985d43fd68SRuslan Bukin if (dwc_get_hwaddr(sc, macaddr)) { 12995d43fd68SRuslan Bukin device_printf(sc->dev, "can't get mac\n"); 13005d43fd68SRuslan Bukin return (ENXIO); 13015d43fd68SRuslan Bukin } 13025d43fd68SRuslan Bukin 1303d7acb49aSJared McNeill /* Reset the PHY if needed */ 1304d7acb49aSJared McNeill if (dwc_reset(dev) != 0) { 1305d7acb49aSJared McNeill device_printf(dev, "Can't reset the PHY\n"); 1306d7acb49aSJared McNeill return (ENXIO); 1307d7acb49aSJared McNeill } 1308d7acb49aSJared McNeill 13095d43fd68SRuslan Bukin /* Reset */ 13105d43fd68SRuslan Bukin reg = READ4(sc, BUS_MODE); 13115d43fd68SRuslan Bukin reg |= (BUS_MODE_SWR); 13125d43fd68SRuslan Bukin WRITE4(sc, BUS_MODE, reg); 13135d43fd68SRuslan Bukin 1314d8e5258dSRuslan Bukin for (i = 0; i < MAC_RESET_TIMEOUT; i++) { 13155d43fd68SRuslan Bukin if ((READ4(sc, BUS_MODE) & BUS_MODE_SWR) == 0) 13165d43fd68SRuslan Bukin break; 13175d43fd68SRuslan Bukin DELAY(10); 13185d43fd68SRuslan Bukin } 1319d8e5258dSRuslan Bukin if (i >= MAC_RESET_TIMEOUT) { 13205d43fd68SRuslan Bukin device_printf(sc->dev, "Can't reset DWC.\n"); 13215d43fd68SRuslan Bukin return (ENXIO); 13225d43fd68SRuslan Bukin } 13235d43fd68SRuslan Bukin 1324188aee74SMichal Meloun if (sc->mactype != DWC_GMAC_EXT_DESC) { 13255df53927SLuiz Otavio O Souza reg = BUS_MODE_FIXEDBURST; 13265df53927SLuiz Otavio O Souza reg |= (BUS_MODE_PRIORXTX_41 << BUS_MODE_PRIORXTX_SHIFT); 13275df53927SLuiz Otavio O Souza } else 13285df53927SLuiz Otavio O Souza reg = (BUS_MODE_EIGHTXPBL); 13295d43fd68SRuslan Bukin reg |= (BUS_MODE_PBL_BEATS_8 << BUS_MODE_PBL_SHIFT); 13305d43fd68SRuslan Bukin WRITE4(sc, BUS_MODE, reg); 13315d43fd68SRuslan Bukin 13325d43fd68SRuslan Bukin /* 13335d43fd68SRuslan Bukin * DMA must be stop while changing descriptor list addresses. 13345d43fd68SRuslan Bukin */ 13355d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 13365d43fd68SRuslan Bukin reg &= ~(MODE_ST | MODE_SR); 13375d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 13385d43fd68SRuslan Bukin 13395d43fd68SRuslan Bukin if (setup_dma(sc)) 13405d43fd68SRuslan Bukin return (ENXIO); 13415d43fd68SRuslan Bukin 13425d43fd68SRuslan Bukin /* Setup addresses */ 13435d43fd68SRuslan Bukin WRITE4(sc, RX_DESCR_LIST_ADDR, sc->rxdesc_ring_paddr); 13445d43fd68SRuslan Bukin WRITE4(sc, TX_DESCR_LIST_ADDR, sc->txdesc_ring_paddr); 13455d43fd68SRuslan Bukin 1346d8e5258dSRuslan Bukin mtx_init(&sc->mtx, device_get_nameunit(sc->dev), 1347d8e5258dSRuslan Bukin MTX_NETWORK_LOCK, MTX_DEF); 1348d8e5258dSRuslan Bukin 1349d8e5258dSRuslan Bukin callout_init_mtx(&sc->dwc_callout, &sc->mtx, 0); 1350d8e5258dSRuslan Bukin 1351d8e5258dSRuslan Bukin /* Setup interrupt handler. */ 1352d8e5258dSRuslan Bukin error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE, 1353d8e5258dSRuslan Bukin NULL, dwc_intr, sc, &sc->intr_cookie); 1354d8e5258dSRuslan Bukin if (error != 0) { 1355d8e5258dSRuslan Bukin device_printf(dev, "could not setup interrupt handler.\n"); 1356d8e5258dSRuslan Bukin return (ENXIO); 1357d8e5258dSRuslan Bukin } 1358d8e5258dSRuslan Bukin 13595d43fd68SRuslan Bukin /* Set up the ethernet interface. */ 13605d43fd68SRuslan Bukin sc->ifp = ifp = if_alloc(IFT_ETHER); 13615d43fd68SRuslan Bukin 13625d43fd68SRuslan Bukin ifp->if_softc = sc; 13635d43fd68SRuslan Bukin if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 13645d43fd68SRuslan Bukin ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 13655d43fd68SRuslan Bukin ifp->if_capabilities = IFCAP_VLAN_MTU; 13665d43fd68SRuslan Bukin ifp->if_capenable = ifp->if_capabilities; 13675d43fd68SRuslan Bukin ifp->if_start = dwc_txstart; 13685d43fd68SRuslan Bukin ifp->if_ioctl = dwc_ioctl; 13695d43fd68SRuslan Bukin ifp->if_init = dwc_init; 13705d43fd68SRuslan Bukin IFQ_SET_MAXLEN(&ifp->if_snd, TX_DESC_COUNT - 1); 13715d43fd68SRuslan Bukin ifp->if_snd.ifq_drv_maxlen = TX_DESC_COUNT - 1; 13725d43fd68SRuslan Bukin IFQ_SET_READY(&ifp->if_snd); 13735d43fd68SRuslan Bukin 13745d43fd68SRuslan Bukin /* Attach the mii driver. */ 13755d43fd68SRuslan Bukin error = mii_attach(dev, &sc->miibus, ifp, dwc_media_change, 13765d43fd68SRuslan Bukin dwc_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, 13775d43fd68SRuslan Bukin MII_OFFSET_ANY, 0); 13785d43fd68SRuslan Bukin 13795d43fd68SRuslan Bukin if (error != 0) { 13805d43fd68SRuslan Bukin device_printf(dev, "PHY attach failed\n"); 13815d43fd68SRuslan Bukin return (ENXIO); 13825d43fd68SRuslan Bukin } 13835d43fd68SRuslan Bukin sc->mii_softc = device_get_softc(sc->miibus); 13845d43fd68SRuslan Bukin 13855d43fd68SRuslan Bukin /* All ready to run, attach the ethernet interface. */ 13865d43fd68SRuslan Bukin ether_ifattach(ifp, macaddr); 13875d43fd68SRuslan Bukin sc->is_attached = true; 13885d43fd68SRuslan Bukin 13895d43fd68SRuslan Bukin return (0); 13905d43fd68SRuslan Bukin } 13915d43fd68SRuslan Bukin 13925d43fd68SRuslan Bukin static int 13935d43fd68SRuslan Bukin dwc_miibus_read_reg(device_t dev, int phy, int reg) 13945d43fd68SRuslan Bukin { 13955d43fd68SRuslan Bukin struct dwc_softc *sc; 13965d43fd68SRuslan Bukin uint16_t mii; 13975d43fd68SRuslan Bukin size_t cnt; 13985d43fd68SRuslan Bukin int rv = 0; 13995d43fd68SRuslan Bukin 14005d43fd68SRuslan Bukin sc = device_get_softc(dev); 14015d43fd68SRuslan Bukin 14025d43fd68SRuslan Bukin mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) 14035d43fd68SRuslan Bukin | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) 14045d43fd68SRuslan Bukin | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) 14055d43fd68SRuslan Bukin | GMII_ADDRESS_GB; /* Busy flag */ 14065d43fd68SRuslan Bukin 14075d43fd68SRuslan Bukin WRITE4(sc, GMII_ADDRESS, mii); 14085d43fd68SRuslan Bukin 14095d43fd68SRuslan Bukin for (cnt = 0; cnt < 1000; cnt++) { 14105d43fd68SRuslan Bukin if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { 14115d43fd68SRuslan Bukin rv = READ4(sc, GMII_DATA); 14125d43fd68SRuslan Bukin break; 14135d43fd68SRuslan Bukin } 14145d43fd68SRuslan Bukin DELAY(10); 14155d43fd68SRuslan Bukin } 14165d43fd68SRuslan Bukin 14175d43fd68SRuslan Bukin return rv; 14185d43fd68SRuslan Bukin } 14195d43fd68SRuslan Bukin 14205d43fd68SRuslan Bukin static int 14215d43fd68SRuslan Bukin dwc_miibus_write_reg(device_t dev, int phy, int reg, int val) 14225d43fd68SRuslan Bukin { 14235d43fd68SRuslan Bukin struct dwc_softc *sc; 14245d43fd68SRuslan Bukin uint16_t mii; 14255d43fd68SRuslan Bukin size_t cnt; 14265d43fd68SRuslan Bukin 14275d43fd68SRuslan Bukin sc = device_get_softc(dev); 14285d43fd68SRuslan Bukin 14295d43fd68SRuslan Bukin mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) 14305d43fd68SRuslan Bukin | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) 14315d43fd68SRuslan Bukin | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) 14325d43fd68SRuslan Bukin | GMII_ADDRESS_GB | GMII_ADDRESS_GW; 14335d43fd68SRuslan Bukin 14345d43fd68SRuslan Bukin WRITE4(sc, GMII_DATA, val); 14355d43fd68SRuslan Bukin WRITE4(sc, GMII_ADDRESS, mii); 14365d43fd68SRuslan Bukin 14375d43fd68SRuslan Bukin for (cnt = 0; cnt < 1000; cnt++) { 14385d43fd68SRuslan Bukin if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { 14395d43fd68SRuslan Bukin break; 14405d43fd68SRuslan Bukin } 14415d43fd68SRuslan Bukin DELAY(10); 14425d43fd68SRuslan Bukin } 14435d43fd68SRuslan Bukin 14445d43fd68SRuslan Bukin return (0); 14455d43fd68SRuslan Bukin } 14465d43fd68SRuslan Bukin 14475d43fd68SRuslan Bukin static void 14485d43fd68SRuslan Bukin dwc_miibus_statchg(device_t dev) 14495d43fd68SRuslan Bukin { 14505d43fd68SRuslan Bukin struct dwc_softc *sc; 14515d43fd68SRuslan Bukin struct mii_data *mii; 1452ff0752c8SLuiz Otavio O Souza uint32_t reg; 14535d43fd68SRuslan Bukin 14545d43fd68SRuslan Bukin /* 14555d43fd68SRuslan Bukin * Called by the MII bus driver when the PHY establishes 14565d43fd68SRuslan Bukin * link to set the MAC interface registers. 14575d43fd68SRuslan Bukin */ 14585d43fd68SRuslan Bukin 14595d43fd68SRuslan Bukin sc = device_get_softc(dev); 14605d43fd68SRuslan Bukin 14615d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 14625d43fd68SRuslan Bukin 14635d43fd68SRuslan Bukin mii = sc->mii_softc; 14645d43fd68SRuslan Bukin 14655d43fd68SRuslan Bukin if (mii->mii_media_status & IFM_ACTIVE) 14665d43fd68SRuslan Bukin sc->link_is_up = true; 14675d43fd68SRuslan Bukin else 14685d43fd68SRuslan Bukin sc->link_is_up = false; 14695d43fd68SRuslan Bukin 14705d43fd68SRuslan Bukin reg = READ4(sc, MAC_CONFIGURATION); 14715d43fd68SRuslan Bukin switch (IFM_SUBTYPE(mii->mii_media_active)) { 14725d43fd68SRuslan Bukin case IFM_1000_T: 14735d43fd68SRuslan Bukin case IFM_1000_SX: 14745d43fd68SRuslan Bukin reg &= ~(CONF_FES | CONF_PS); 14755d43fd68SRuslan Bukin break; 14765d43fd68SRuslan Bukin case IFM_100_TX: 14775d43fd68SRuslan Bukin reg |= (CONF_FES | CONF_PS); 14785d43fd68SRuslan Bukin break; 14795d43fd68SRuslan Bukin case IFM_10_T: 14805d43fd68SRuslan Bukin reg &= ~(CONF_FES); 14815d43fd68SRuslan Bukin reg |= (CONF_PS); 14825d43fd68SRuslan Bukin break; 14835d43fd68SRuslan Bukin case IFM_NONE: 14845d43fd68SRuslan Bukin sc->link_is_up = false; 14855d43fd68SRuslan Bukin return; 14865d43fd68SRuslan Bukin default: 14875d43fd68SRuslan Bukin sc->link_is_up = false; 14885d43fd68SRuslan Bukin device_printf(dev, "Unsupported media %u\n", 14895d43fd68SRuslan Bukin IFM_SUBTYPE(mii->mii_media_active)); 14905d43fd68SRuslan Bukin return; 14915d43fd68SRuslan Bukin } 14925d43fd68SRuslan Bukin if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 14935d43fd68SRuslan Bukin reg |= (CONF_DM); 14945d43fd68SRuslan Bukin else 14955d43fd68SRuslan Bukin reg &= ~(CONF_DM); 14965d43fd68SRuslan Bukin WRITE4(sc, MAC_CONFIGURATION, reg); 1497*824cfb47SOleksandr Tymoshenko 1498*824cfb47SOleksandr Tymoshenko IF_DWC_SET_SPEED(dev, IFM_SUBTYPE(mii->mii_media_active)); 1499*824cfb47SOleksandr Tymoshenko 15005d43fd68SRuslan Bukin } 15015d43fd68SRuslan Bukin 15025d43fd68SRuslan Bukin static device_method_t dwc_methods[] = { 15035d43fd68SRuslan Bukin DEVMETHOD(device_probe, dwc_probe), 15045d43fd68SRuslan Bukin DEVMETHOD(device_attach, dwc_attach), 15055d43fd68SRuslan Bukin 15065d43fd68SRuslan Bukin /* MII Interface */ 15075d43fd68SRuslan Bukin DEVMETHOD(miibus_readreg, dwc_miibus_read_reg), 15085d43fd68SRuslan Bukin DEVMETHOD(miibus_writereg, dwc_miibus_write_reg), 15095d43fd68SRuslan Bukin DEVMETHOD(miibus_statchg, dwc_miibus_statchg), 15105d43fd68SRuslan Bukin 15115d43fd68SRuslan Bukin { 0, 0 } 15125d43fd68SRuslan Bukin }; 15135d43fd68SRuslan Bukin 15145df53927SLuiz Otavio O Souza driver_t dwc_driver = { 15155d43fd68SRuslan Bukin "dwc", 15165d43fd68SRuslan Bukin dwc_methods, 15175d43fd68SRuslan Bukin sizeof(struct dwc_softc), 15185d43fd68SRuslan Bukin }; 15195d43fd68SRuslan Bukin 15205d43fd68SRuslan Bukin static devclass_t dwc_devclass; 15215d43fd68SRuslan Bukin 15225d43fd68SRuslan Bukin DRIVER_MODULE(dwc, simplebus, dwc_driver, dwc_devclass, 0, 0); 15235d43fd68SRuslan Bukin DRIVER_MODULE(miibus, dwc, miibus_driver, miibus_devclass, 0, 0); 15245d43fd68SRuslan Bukin 15255d43fd68SRuslan Bukin MODULE_DEPEND(dwc, ether, 1, 1, 1); 15265d43fd68SRuslan Bukin MODULE_DEPEND(dwc, miibus, 1, 1, 1); 1527