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_LC (1U << 6) /* Late Collision */ 1598d43a868SMichal Meloun #define RDESC0_FT (1U << 5) /* Frame Type */ 1608d43a868SMichal Meloun #define RDESC0_RWT (1U << 4) /* Receive Watchdog Timeout */ 1618d43a868SMichal Meloun #define RDESC0_RE (1U << 3) /* Receive Error */ 1628d43a868SMichal Meloun #define RDESC0_DBE (1U << 2) /* Dribble Bit Error */ 1638d43a868SMichal Meloun #define RDESC0_CE (1U << 1) /* CRC Error */ 1648d43a868SMichal Meloun #define RDESC0_PCE (1U << 0) /* Payload Checksum Error */ 1658d43a868SMichal Meloun #define RDESC0_RXMA (1U << 0) /* Rx MAC Address */ 1665df53927SLuiz Otavio O Souza 1678d43a868SMichal Meloun /* RX descriptors - RDESC1 normal format */ 1688d43a868SMichal Meloun #define NRDESC1_DIC (1U << 31) /* Disable Intr on Completion */ 1698d43a868SMichal Meloun #define NRDESC1_RER (1U << 25) /* Receive End of Ring */ 1708d43a868SMichal Meloun #define NRDESC1_RCH (1U << 24) /* Second Address Chained */ 1718d43a868SMichal Meloun #define NRDESC1_RBS2_MASK 0x7ff 1728d43a868SMichal Meloun #define NRDESC1_RBS2_SHIFT 11 /* Receive Buffer 2 Size */ 1738d43a868SMichal Meloun #define NRDESC1_RBS1_MASK 0x7ff 1748d43a868SMichal Meloun #define NRDESC1_RBS1_SHIFT 0 /* Receive Buffer 1 Size */ 1758d43a868SMichal Meloun 1768d43a868SMichal Meloun /* RX descriptors - RDESC1 enhanced format */ 1778d43a868SMichal Meloun #define ERDESC1_DIC (1U << 31) /* Disable Intr on Completion */ 1788d43a868SMichal Meloun #define ERDESC1_RBS2_MASK 0x7ffff 1798d43a868SMichal Meloun #define ERDESC1_RBS2_SHIFT 16 /* Receive Buffer 2 Size */ 1808d43a868SMichal Meloun #define ERDESC1_RER (1U << 15) /* Receive End of Ring */ 1818d43a868SMichal Meloun #define ERDESC1_RCH (1U << 14) /* Second Address Chained */ 1828d43a868SMichal Meloun #define ERDESC1_RBS1_MASK 0x7ffff 1838d43a868SMichal Meloun #define ERDESC1_RBS1_SHIFT 0 /* Receive Buffer 1 Size */ 1845d43fd68SRuslan Bukin 1855d43fd68SRuslan Bukin /* 1865d43fd68SRuslan Bukin * A hardware buffer descriptor. Rx and Tx buffers have the same descriptor 1875df53927SLuiz Otavio O Souza * layout, but the bits in the fields have different meanings. 1885d43fd68SRuslan Bukin */ 1895d43fd68SRuslan Bukin struct dwc_hwdesc 1905d43fd68SRuslan Bukin { 1918d43a868SMichal Meloun uint32_t desc0; 1928d43a868SMichal Meloun uint32_t desc1; 1938d43a868SMichal Meloun uint32_t addr1; /* ptr to first buffer data */ 1948d43a868SMichal Meloun uint32_t addr2; /* ptr to next descriptor / second buffer data*/ 1955d43fd68SRuslan Bukin }; 1965d43fd68SRuslan Bukin 1972a35d391SEmmanuel Vadot 1982a35d391SEmmanuel Vadot struct dwc_hash_maddr_ctx { 1992a35d391SEmmanuel Vadot struct dwc_softc *sc; 2002a35d391SEmmanuel Vadot uint32_t hash[8]; 2012a35d391SEmmanuel Vadot }; 2022a35d391SEmmanuel Vadot 2035d43fd68SRuslan Bukin /* 2045d43fd68SRuslan Bukin * The hardware imposes alignment restrictions on various objects involved in 2055d43fd68SRuslan Bukin * DMA transfers. These values are expressed in bytes (not bits). 2065d43fd68SRuslan Bukin */ 2075d43fd68SRuslan Bukin #define DWC_DESC_RING_ALIGN 2048 2085d43fd68SRuslan Bukin 2095d43fd68SRuslan Bukin static struct resource_spec dwc_spec[] = { 2105d43fd68SRuslan Bukin { SYS_RES_MEMORY, 0, RF_ACTIVE }, 2115d43fd68SRuslan Bukin { SYS_RES_IRQ, 0, RF_ACTIVE }, 2125d43fd68SRuslan Bukin { -1, 0 } 2135d43fd68SRuslan Bukin }; 2145d43fd68SRuslan Bukin 2155d43fd68SRuslan Bukin static void dwc_txfinish_locked(struct dwc_softc *sc); 2165d43fd68SRuslan Bukin static void dwc_rxfinish_locked(struct dwc_softc *sc); 2175d43fd68SRuslan Bukin static void dwc_stop_locked(struct dwc_softc *sc); 2185d43fd68SRuslan Bukin static void dwc_setup_rxfilter(struct dwc_softc *sc); 219158ce7baSEmmanuel Vadot static void dwc_setup_core(struct dwc_softc *sc); 220f368f4b1SEmmanuel Vadot static void dwc_enable_mac(struct dwc_softc *sc, bool enable); 221158ce7baSEmmanuel Vadot static void dwc_init_dma(struct dwc_softc *sc); 222afd0c3c2SEmmanuel Vadot static void dwc_stop_dma(struct dwc_softc *sc); 2235d43fd68SRuslan Bukin 2242a35d391SEmmanuel Vadot static void dwc_tick(void *arg); 2252a35d391SEmmanuel Vadot 2262b4a66edSEmmanuel Vadot /* Pause time field in the transmitted control frame */ 2272b4a66edSEmmanuel Vadot static int dwc_pause_time = 0xffff; 2282b4a66edSEmmanuel Vadot TUNABLE_INT("hw.dwc.pause_time", &dwc_pause_time); 2292b4a66edSEmmanuel Vadot 2302a35d391SEmmanuel Vadot /* 2312a35d391SEmmanuel Vadot * MIIBUS functions 2322a35d391SEmmanuel Vadot */ 2332a35d391SEmmanuel Vadot 2342a35d391SEmmanuel Vadot static int 2352a35d391SEmmanuel Vadot dwc_miibus_read_reg(device_t dev, int phy, int reg) 2362a35d391SEmmanuel Vadot { 2372a35d391SEmmanuel Vadot struct dwc_softc *sc; 2382a35d391SEmmanuel Vadot uint16_t mii; 2392a35d391SEmmanuel Vadot size_t cnt; 2402a35d391SEmmanuel Vadot int rv = 0; 2412a35d391SEmmanuel Vadot 2422a35d391SEmmanuel Vadot sc = device_get_softc(dev); 2432a35d391SEmmanuel Vadot 2442a35d391SEmmanuel Vadot mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) 2452a35d391SEmmanuel Vadot | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) 2462a35d391SEmmanuel Vadot | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) 2472a35d391SEmmanuel Vadot | GMII_ADDRESS_GB; /* Busy flag */ 2482a35d391SEmmanuel Vadot 2492a35d391SEmmanuel Vadot WRITE4(sc, GMII_ADDRESS, mii); 2502a35d391SEmmanuel Vadot 2512a35d391SEmmanuel Vadot for (cnt = 0; cnt < 1000; cnt++) { 2522a35d391SEmmanuel Vadot if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { 2532a35d391SEmmanuel Vadot rv = READ4(sc, GMII_DATA); 2542a35d391SEmmanuel Vadot break; 2552a35d391SEmmanuel Vadot } 2562a35d391SEmmanuel Vadot DELAY(10); 2572a35d391SEmmanuel Vadot } 2582a35d391SEmmanuel Vadot 2592a35d391SEmmanuel Vadot return rv; 2602a35d391SEmmanuel Vadot } 2612a35d391SEmmanuel Vadot 2622a35d391SEmmanuel Vadot static int 2632a35d391SEmmanuel Vadot dwc_miibus_write_reg(device_t dev, int phy, int reg, int val) 2642a35d391SEmmanuel Vadot { 2652a35d391SEmmanuel Vadot struct dwc_softc *sc; 2662a35d391SEmmanuel Vadot uint16_t mii; 2672a35d391SEmmanuel Vadot size_t cnt; 2682a35d391SEmmanuel Vadot 2692a35d391SEmmanuel Vadot sc = device_get_softc(dev); 2702a35d391SEmmanuel Vadot 2712a35d391SEmmanuel Vadot mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) 2722a35d391SEmmanuel Vadot | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) 2732a35d391SEmmanuel Vadot | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) 2742a35d391SEmmanuel Vadot | GMII_ADDRESS_GB | GMII_ADDRESS_GW; 2752a35d391SEmmanuel Vadot 2762a35d391SEmmanuel Vadot WRITE4(sc, GMII_DATA, val); 2772a35d391SEmmanuel Vadot WRITE4(sc, GMII_ADDRESS, mii); 2782a35d391SEmmanuel Vadot 2792a35d391SEmmanuel Vadot for (cnt = 0; cnt < 1000; cnt++) { 2802a35d391SEmmanuel Vadot if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { 2812a35d391SEmmanuel Vadot break; 2822a35d391SEmmanuel Vadot } 2832a35d391SEmmanuel Vadot DELAY(10); 2842a35d391SEmmanuel Vadot } 2852a35d391SEmmanuel Vadot 2862a35d391SEmmanuel Vadot return (0); 2872a35d391SEmmanuel Vadot } 2882a35d391SEmmanuel Vadot 2892a35d391SEmmanuel Vadot static void 2902a35d391SEmmanuel Vadot dwc_miibus_statchg(device_t dev) 2912a35d391SEmmanuel Vadot { 2922a35d391SEmmanuel Vadot struct dwc_softc *sc; 2932a35d391SEmmanuel Vadot struct mii_data *mii; 2942a35d391SEmmanuel Vadot uint32_t reg; 2952a35d391SEmmanuel Vadot 2962a35d391SEmmanuel Vadot /* 2972a35d391SEmmanuel Vadot * Called by the MII bus driver when the PHY establishes 2982a35d391SEmmanuel Vadot * link to set the MAC interface registers. 2992a35d391SEmmanuel Vadot */ 3002a35d391SEmmanuel Vadot 3012a35d391SEmmanuel Vadot sc = device_get_softc(dev); 3022a35d391SEmmanuel Vadot 3032a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 3042a35d391SEmmanuel Vadot 3052a35d391SEmmanuel Vadot mii = sc->mii_softc; 3062a35d391SEmmanuel Vadot 3072a35d391SEmmanuel Vadot if (mii->mii_media_status & IFM_ACTIVE) 3082a35d391SEmmanuel Vadot sc->link_is_up = true; 3092a35d391SEmmanuel Vadot else 3102a35d391SEmmanuel Vadot sc->link_is_up = false; 3112a35d391SEmmanuel Vadot 3122a35d391SEmmanuel Vadot reg = READ4(sc, MAC_CONFIGURATION); 3132a35d391SEmmanuel Vadot switch (IFM_SUBTYPE(mii->mii_media_active)) { 3142a35d391SEmmanuel Vadot case IFM_1000_T: 3152a35d391SEmmanuel Vadot case IFM_1000_SX: 3162a35d391SEmmanuel Vadot reg &= ~(CONF_FES | CONF_PS); 3172a35d391SEmmanuel Vadot break; 3182a35d391SEmmanuel Vadot case IFM_100_TX: 3192a35d391SEmmanuel Vadot reg |= (CONF_FES | CONF_PS); 3202a35d391SEmmanuel Vadot break; 3212a35d391SEmmanuel Vadot case IFM_10_T: 3222a35d391SEmmanuel Vadot reg &= ~(CONF_FES); 3232a35d391SEmmanuel Vadot reg |= (CONF_PS); 3242a35d391SEmmanuel Vadot break; 3252a35d391SEmmanuel Vadot case IFM_NONE: 3262a35d391SEmmanuel Vadot sc->link_is_up = false; 3272a35d391SEmmanuel Vadot return; 3282a35d391SEmmanuel Vadot default: 3292a35d391SEmmanuel Vadot sc->link_is_up = false; 3302a35d391SEmmanuel Vadot device_printf(dev, "Unsupported media %u\n", 3312a35d391SEmmanuel Vadot IFM_SUBTYPE(mii->mii_media_active)); 3322a35d391SEmmanuel Vadot return; 3332a35d391SEmmanuel Vadot } 3342a35d391SEmmanuel Vadot if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 3352a35d391SEmmanuel Vadot reg |= (CONF_DM); 3362a35d391SEmmanuel Vadot else 3372a35d391SEmmanuel Vadot reg &= ~(CONF_DM); 3382a35d391SEmmanuel Vadot WRITE4(sc, MAC_CONFIGURATION, reg); 3392a35d391SEmmanuel Vadot 3402b4a66edSEmmanuel Vadot reg = FLOW_CONTROL_UP; 3412b4a66edSEmmanuel Vadot if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) 3422b4a66edSEmmanuel Vadot reg |= FLOW_CONTROL_TX; 3432b4a66edSEmmanuel Vadot if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) 3442b4a66edSEmmanuel Vadot reg |= FLOW_CONTROL_RX; 3452b4a66edSEmmanuel Vadot if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 3462b4a66edSEmmanuel Vadot reg |= dwc_pause_time << FLOW_CONTROL_PT_SHIFT; 3472b4a66edSEmmanuel Vadot WRITE4(sc, FLOW_CONTROL, reg); 3482b4a66edSEmmanuel Vadot 3492a35d391SEmmanuel Vadot IF_DWC_SET_SPEED(dev, IFM_SUBTYPE(mii->mii_media_active)); 3502a35d391SEmmanuel Vadot 3512a35d391SEmmanuel Vadot } 3522a35d391SEmmanuel Vadot 3532a35d391SEmmanuel Vadot /* 3542a35d391SEmmanuel Vadot * Media functions 3552a35d391SEmmanuel Vadot */ 3562a35d391SEmmanuel Vadot 3572a35d391SEmmanuel Vadot static void 3582a35d391SEmmanuel Vadot dwc_media_status(struct ifnet * ifp, struct ifmediareq *ifmr) 3592a35d391SEmmanuel Vadot { 3602a35d391SEmmanuel Vadot struct dwc_softc *sc; 3612a35d391SEmmanuel Vadot struct mii_data *mii; 3622a35d391SEmmanuel Vadot 3632a35d391SEmmanuel Vadot sc = ifp->if_softc; 3642a35d391SEmmanuel Vadot mii = sc->mii_softc; 3652a35d391SEmmanuel Vadot DWC_LOCK(sc); 3662a35d391SEmmanuel Vadot mii_pollstat(mii); 3672a35d391SEmmanuel Vadot ifmr->ifm_active = mii->mii_media_active; 3682a35d391SEmmanuel Vadot ifmr->ifm_status = mii->mii_media_status; 3692a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 3702a35d391SEmmanuel Vadot } 3712a35d391SEmmanuel Vadot 3722a35d391SEmmanuel Vadot static int 3732a35d391SEmmanuel Vadot dwc_media_change_locked(struct dwc_softc *sc) 3742a35d391SEmmanuel Vadot { 3752a35d391SEmmanuel Vadot 3762a35d391SEmmanuel Vadot return (mii_mediachg(sc->mii_softc)); 3772a35d391SEmmanuel Vadot } 3782a35d391SEmmanuel Vadot 3792a35d391SEmmanuel Vadot static int 3802a35d391SEmmanuel Vadot dwc_media_change(struct ifnet * ifp) 3812a35d391SEmmanuel Vadot { 3822a35d391SEmmanuel Vadot struct dwc_softc *sc; 3832a35d391SEmmanuel Vadot int error; 3842a35d391SEmmanuel Vadot 3852a35d391SEmmanuel Vadot sc = ifp->if_softc; 3862a35d391SEmmanuel Vadot 3872a35d391SEmmanuel Vadot DWC_LOCK(sc); 3882a35d391SEmmanuel Vadot error = dwc_media_change_locked(sc); 3892a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 3902a35d391SEmmanuel Vadot return (error); 3912a35d391SEmmanuel Vadot } 3922a35d391SEmmanuel Vadot 3932a35d391SEmmanuel Vadot /* 3942a35d391SEmmanuel Vadot * Core functions 3952a35d391SEmmanuel Vadot */ 3962a35d391SEmmanuel Vadot 3972a35d391SEmmanuel Vadot static const uint8_t nibbletab[] = { 3982a35d391SEmmanuel Vadot /* 0x0 0000 -> 0000 */ 0x0, 3992a35d391SEmmanuel Vadot /* 0x1 0001 -> 1000 */ 0x8, 4002a35d391SEmmanuel Vadot /* 0x2 0010 -> 0100 */ 0x4, 4012a35d391SEmmanuel Vadot /* 0x3 0011 -> 1100 */ 0xc, 4022a35d391SEmmanuel Vadot /* 0x4 0100 -> 0010 */ 0x2, 4032a35d391SEmmanuel Vadot /* 0x5 0101 -> 1010 */ 0xa, 4042a35d391SEmmanuel Vadot /* 0x6 0110 -> 0110 */ 0x6, 4052a35d391SEmmanuel Vadot /* 0x7 0111 -> 1110 */ 0xe, 4062a35d391SEmmanuel Vadot /* 0x8 1000 -> 0001 */ 0x1, 4072a35d391SEmmanuel Vadot /* 0x9 1001 -> 1001 */ 0x9, 4082a35d391SEmmanuel Vadot /* 0xa 1010 -> 0101 */ 0x5, 4092a35d391SEmmanuel Vadot /* 0xb 1011 -> 1101 */ 0xd, 4102a35d391SEmmanuel Vadot /* 0xc 1100 -> 0011 */ 0x3, 4112a35d391SEmmanuel Vadot /* 0xd 1101 -> 1011 */ 0xb, 4122a35d391SEmmanuel Vadot /* 0xe 1110 -> 0111 */ 0x7, 4132a35d391SEmmanuel Vadot /* 0xf 1111 -> 1111 */ 0xf, }; 4142a35d391SEmmanuel Vadot 4152a35d391SEmmanuel Vadot static uint8_t 4162a35d391SEmmanuel Vadot bitreverse(uint8_t x) 4172a35d391SEmmanuel Vadot { 4182a35d391SEmmanuel Vadot 4192a35d391SEmmanuel Vadot return (nibbletab[x & 0xf] << 4) | nibbletab[x >> 4]; 4202a35d391SEmmanuel Vadot } 4212a35d391SEmmanuel Vadot 4222a35d391SEmmanuel Vadot static u_int 4232a35d391SEmmanuel Vadot dwc_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) 4242a35d391SEmmanuel Vadot { 4252a35d391SEmmanuel Vadot struct dwc_hash_maddr_ctx *ctx = arg; 4262a35d391SEmmanuel Vadot uint32_t crc, hashbit, hashreg; 4272a35d391SEmmanuel Vadot uint8_t val; 4282a35d391SEmmanuel Vadot 4292a35d391SEmmanuel Vadot crc = ether_crc32_le(LLADDR(sdl), ETHER_ADDR_LEN); 4302a35d391SEmmanuel Vadot /* Take lower 8 bits and reverse it */ 4312a35d391SEmmanuel Vadot val = bitreverse(~crc & 0xff); 4322a35d391SEmmanuel Vadot if (ctx->sc->mactype != DWC_GMAC_EXT_DESC) 4332a35d391SEmmanuel Vadot val >>= 2; /* Only need lower 6 bits */ 4342a35d391SEmmanuel Vadot hashreg = (val >> 5); 4352a35d391SEmmanuel Vadot hashbit = (val & 31); 4362a35d391SEmmanuel Vadot ctx->hash[hashreg] |= (1 << hashbit); 4372a35d391SEmmanuel Vadot 4382a35d391SEmmanuel Vadot return (1); 4392a35d391SEmmanuel Vadot } 4402a35d391SEmmanuel Vadot 4412a35d391SEmmanuel Vadot static void 4422a35d391SEmmanuel Vadot dwc_setup_rxfilter(struct dwc_softc *sc) 4432a35d391SEmmanuel Vadot { 4442a35d391SEmmanuel Vadot struct dwc_hash_maddr_ctx ctx; 4452a35d391SEmmanuel Vadot struct ifnet *ifp; 4462a35d391SEmmanuel Vadot uint8_t *eaddr; 4472a35d391SEmmanuel Vadot uint32_t ffval, hi, lo; 4482a35d391SEmmanuel Vadot int nhash, i; 4492a35d391SEmmanuel Vadot 4502a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 4512a35d391SEmmanuel Vadot 4522a35d391SEmmanuel Vadot ifp = sc->ifp; 4532a35d391SEmmanuel Vadot nhash = sc->mactype != DWC_GMAC_EXT_DESC ? 2 : 8; 4542a35d391SEmmanuel Vadot 4552a35d391SEmmanuel Vadot /* 4562a35d391SEmmanuel Vadot * Set the multicast (group) filter hash. 4572a35d391SEmmanuel Vadot */ 4582a35d391SEmmanuel Vadot if ((ifp->if_flags & IFF_ALLMULTI) != 0) { 4592a35d391SEmmanuel Vadot ffval = (FRAME_FILTER_PM); 4602a35d391SEmmanuel Vadot for (i = 0; i < nhash; i++) 4612a35d391SEmmanuel Vadot ctx.hash[i] = ~0; 4622a35d391SEmmanuel Vadot } else { 4632a35d391SEmmanuel Vadot ffval = (FRAME_FILTER_HMC); 4642a35d391SEmmanuel Vadot for (i = 0; i < nhash; i++) 4652a35d391SEmmanuel Vadot ctx.hash[i] = 0; 4662a35d391SEmmanuel Vadot ctx.sc = sc; 4672a35d391SEmmanuel Vadot if_foreach_llmaddr(ifp, dwc_hash_maddr, &ctx); 4682a35d391SEmmanuel Vadot } 4692a35d391SEmmanuel Vadot 4702a35d391SEmmanuel Vadot /* 4712a35d391SEmmanuel Vadot * Set the individual address filter hash. 4722a35d391SEmmanuel Vadot */ 4732a35d391SEmmanuel Vadot if (ifp->if_flags & IFF_PROMISC) 4742a35d391SEmmanuel Vadot ffval |= (FRAME_FILTER_PR); 4752a35d391SEmmanuel Vadot 4762a35d391SEmmanuel Vadot /* 4772a35d391SEmmanuel Vadot * Set the primary address. 4782a35d391SEmmanuel Vadot */ 4792a35d391SEmmanuel Vadot eaddr = IF_LLADDR(ifp); 4802a35d391SEmmanuel Vadot lo = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) | 4812a35d391SEmmanuel Vadot (eaddr[3] << 24); 4822a35d391SEmmanuel Vadot hi = eaddr[4] | (eaddr[5] << 8); 4832a35d391SEmmanuel Vadot WRITE4(sc, MAC_ADDRESS_LOW(0), lo); 4842a35d391SEmmanuel Vadot WRITE4(sc, MAC_ADDRESS_HIGH(0), hi); 4852a35d391SEmmanuel Vadot WRITE4(sc, MAC_FRAME_FILTER, ffval); 4862a35d391SEmmanuel Vadot if (sc->mactype != DWC_GMAC_EXT_DESC) { 4872a35d391SEmmanuel Vadot WRITE4(sc, GMAC_MAC_HTLOW, ctx.hash[0]); 4882a35d391SEmmanuel Vadot WRITE4(sc, GMAC_MAC_HTHIGH, ctx.hash[1]); 4892a35d391SEmmanuel Vadot } else { 4902a35d391SEmmanuel Vadot for (i = 0; i < nhash; i++) 4912a35d391SEmmanuel Vadot WRITE4(sc, HASH_TABLE_REG(i), ctx.hash[i]); 4922a35d391SEmmanuel Vadot } 4932a35d391SEmmanuel Vadot } 4942a35d391SEmmanuel Vadot 4952a35d391SEmmanuel Vadot static void 4962a35d391SEmmanuel Vadot dwc_setup_core(struct dwc_softc *sc) 4972a35d391SEmmanuel Vadot { 4982a35d391SEmmanuel Vadot uint32_t reg; 4992a35d391SEmmanuel Vadot 5002a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 5012a35d391SEmmanuel Vadot 5022a35d391SEmmanuel Vadot /* Enable core */ 5032a35d391SEmmanuel Vadot reg = READ4(sc, MAC_CONFIGURATION); 5042a35d391SEmmanuel Vadot reg |= (CONF_JD | CONF_ACS | CONF_BE); 5052a35d391SEmmanuel Vadot WRITE4(sc, MAC_CONFIGURATION, reg); 5062a35d391SEmmanuel Vadot } 5072a35d391SEmmanuel Vadot 5082a35d391SEmmanuel Vadot static void 5092a35d391SEmmanuel Vadot dwc_enable_mac(struct dwc_softc *sc, bool enable) 5102a35d391SEmmanuel Vadot { 5112a35d391SEmmanuel Vadot uint32_t reg; 5122a35d391SEmmanuel Vadot 5132a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 5142a35d391SEmmanuel Vadot reg = READ4(sc, MAC_CONFIGURATION); 5152a35d391SEmmanuel Vadot if (enable) 5162a35d391SEmmanuel Vadot reg |= CONF_TE | CONF_RE; 5172a35d391SEmmanuel Vadot else 5182a35d391SEmmanuel Vadot reg &= ~(CONF_TE | CONF_RE); 5192a35d391SEmmanuel Vadot WRITE4(sc, MAC_CONFIGURATION, reg); 5202a35d391SEmmanuel Vadot } 5212a35d391SEmmanuel Vadot 5222a35d391SEmmanuel Vadot static void 5232a35d391SEmmanuel Vadot dwc_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr) 5242a35d391SEmmanuel Vadot { 5252a35d391SEmmanuel Vadot uint32_t hi, lo, rnd; 5262a35d391SEmmanuel Vadot 5272a35d391SEmmanuel Vadot /* 5282a35d391SEmmanuel Vadot * Try to recover a MAC address from the running hardware. If there's 5292a35d391SEmmanuel Vadot * something non-zero there, assume the bootloader did the right thing 5302a35d391SEmmanuel Vadot * and just use it. 5312a35d391SEmmanuel Vadot * 5322a35d391SEmmanuel Vadot * Otherwise, set the address to a convenient locally assigned address, 5332a35d391SEmmanuel Vadot * 'bsd' + random 24 low-order bits. 'b' is 0x62, which has the locally 5342a35d391SEmmanuel Vadot * assigned bit set, and the broadcast/multicast bit clear. 5352a35d391SEmmanuel Vadot */ 5362a35d391SEmmanuel Vadot lo = READ4(sc, MAC_ADDRESS_LOW(0)); 5372a35d391SEmmanuel Vadot hi = READ4(sc, MAC_ADDRESS_HIGH(0)) & 0xffff; 5382a35d391SEmmanuel Vadot if ((lo != 0xffffffff) || (hi != 0xffff)) { 5392a35d391SEmmanuel Vadot hwaddr[0] = (lo >> 0) & 0xff; 5402a35d391SEmmanuel Vadot hwaddr[1] = (lo >> 8) & 0xff; 5412a35d391SEmmanuel Vadot hwaddr[2] = (lo >> 16) & 0xff; 5422a35d391SEmmanuel Vadot hwaddr[3] = (lo >> 24) & 0xff; 5432a35d391SEmmanuel Vadot hwaddr[4] = (hi >> 0) & 0xff; 5442a35d391SEmmanuel Vadot hwaddr[5] = (hi >> 8) & 0xff; 5452a35d391SEmmanuel Vadot } else { 5462a35d391SEmmanuel Vadot rnd = arc4random() & 0x00ffffff; 5472a35d391SEmmanuel Vadot hwaddr[0] = 'b'; 5482a35d391SEmmanuel Vadot hwaddr[1] = 's'; 5492a35d391SEmmanuel Vadot hwaddr[2] = 'd'; 5502a35d391SEmmanuel Vadot hwaddr[3] = rnd >> 16; 5512a35d391SEmmanuel Vadot hwaddr[4] = rnd >> 8; 5522a35d391SEmmanuel Vadot hwaddr[5] = rnd >> 0; 5532a35d391SEmmanuel Vadot } 5542a35d391SEmmanuel Vadot } 5552a35d391SEmmanuel Vadot 5562a35d391SEmmanuel Vadot /* 5572a35d391SEmmanuel Vadot * DMA functions 5582a35d391SEmmanuel Vadot */ 5592a35d391SEmmanuel Vadot 5602a35d391SEmmanuel Vadot static void 5612a35d391SEmmanuel Vadot dwc_init_dma(struct dwc_softc *sc) 5622a35d391SEmmanuel Vadot { 5632a35d391SEmmanuel Vadot uint32_t reg; 5642a35d391SEmmanuel Vadot 5652a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 5662a35d391SEmmanuel Vadot 5672a35d391SEmmanuel Vadot /* Initializa DMA and enable transmitters */ 5682a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 5692a35d391SEmmanuel Vadot reg |= (MODE_TSF | MODE_OSF | MODE_FUF); 5702a35d391SEmmanuel Vadot reg &= ~(MODE_RSF); 5712a35d391SEmmanuel Vadot reg |= (MODE_RTC_LEV32 << MODE_RTC_SHIFT); 5722a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 5732a35d391SEmmanuel Vadot 5742a35d391SEmmanuel Vadot WRITE4(sc, INTERRUPT_ENABLE, INT_EN_DEFAULT); 5752a35d391SEmmanuel Vadot 5762a35d391SEmmanuel Vadot /* Start DMA */ 5772a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 5782a35d391SEmmanuel Vadot reg |= (MODE_ST | MODE_SR); 5792a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 5802a35d391SEmmanuel Vadot } 5812a35d391SEmmanuel Vadot 5822a35d391SEmmanuel Vadot static void 5832a35d391SEmmanuel Vadot dwc_stop_dma(struct dwc_softc *sc) 5842a35d391SEmmanuel Vadot { 5852a35d391SEmmanuel Vadot uint32_t reg; 5862a35d391SEmmanuel Vadot 5872a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 5882a35d391SEmmanuel Vadot 5892a35d391SEmmanuel Vadot /* Stop DMA TX */ 5902a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 5912a35d391SEmmanuel Vadot reg &= ~(MODE_ST); 5922a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 5932a35d391SEmmanuel Vadot 5942a35d391SEmmanuel Vadot /* Flush TX */ 5952a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 5962a35d391SEmmanuel Vadot reg |= (MODE_FTF); 5972a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 5982a35d391SEmmanuel Vadot 5992a35d391SEmmanuel Vadot /* Stop DMA RX */ 6002a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 6012a35d391SEmmanuel Vadot reg &= ~(MODE_SR); 6022a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 6032a35d391SEmmanuel Vadot } 6042a35d391SEmmanuel Vadot 6055d43fd68SRuslan Bukin static inline uint32_t 6065d43fd68SRuslan Bukin next_rxidx(struct dwc_softc *sc, uint32_t curidx) 6075d43fd68SRuslan Bukin { 6085d43fd68SRuslan Bukin 6095d43fd68SRuslan Bukin return ((curidx + 1) % RX_DESC_COUNT); 6105d43fd68SRuslan Bukin } 6115d43fd68SRuslan Bukin 6125d43fd68SRuslan Bukin static inline uint32_t 6135d43fd68SRuslan Bukin next_txidx(struct dwc_softc *sc, uint32_t curidx) 6145d43fd68SRuslan Bukin { 6155d43fd68SRuslan Bukin 6165d43fd68SRuslan Bukin return ((curidx + 1) % TX_DESC_COUNT); 6175d43fd68SRuslan Bukin } 6185d43fd68SRuslan Bukin 6195d43fd68SRuslan Bukin static void 6205d43fd68SRuslan Bukin dwc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 6215d43fd68SRuslan Bukin { 6225d43fd68SRuslan Bukin 6235d43fd68SRuslan Bukin if (error != 0) 6245d43fd68SRuslan Bukin return; 6255d43fd68SRuslan Bukin *(bus_addr_t *)arg = segs[0].ds_addr; 6265d43fd68SRuslan Bukin } 6275d43fd68SRuslan Bukin 628b72e2878SMichal Meloun inline static void 6295d43fd68SRuslan Bukin dwc_setup_txdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr, 630*98ea5a7bSEmmanuel Vadot uint32_t len, uint32_t flags) 6315d43fd68SRuslan Bukin { 632b72e2878SMichal Meloun uint32_t desc0, desc1; 6335d43fd68SRuslan Bukin 6345d43fd68SRuslan Bukin /* Addr/len 0 means we're clearing the descriptor after xmit done. */ 6355d43fd68SRuslan Bukin if (paddr == 0 || len == 0) { 636b72e2878SMichal Meloun desc0 = 0; 637b72e2878SMichal Meloun desc1 = 0; 6385d43fd68SRuslan Bukin --sc->txcount; 6395d43fd68SRuslan Bukin } else { 640b72e2878SMichal Meloun if (sc->mactype != DWC_GMAC_EXT_DESC) { 641b72e2878SMichal Meloun desc0 = 0; 642b72e2878SMichal Meloun desc1 = NTDESC1_TCH | NTDESC1_FS | NTDESC1_LS | 643*98ea5a7bSEmmanuel Vadot NTDESC1_IC | len | flags; 644b72e2878SMichal Meloun } else { 645b72e2878SMichal Meloun desc0 = ETDESC0_TCH | ETDESC0_FS | ETDESC0_LS | 646*98ea5a7bSEmmanuel Vadot ETDESC0_IC | flags; 647b72e2878SMichal Meloun desc1 = len; 648b72e2878SMichal Meloun } 6495d43fd68SRuslan Bukin ++sc->txcount; 6505d43fd68SRuslan Bukin } 6515d43fd68SRuslan Bukin 6528d43a868SMichal Meloun sc->txdesc_ring[idx].addr1 = (uint32_t)(paddr); 653b72e2878SMichal Meloun sc->txdesc_ring[idx].desc0 = desc0; 654b72e2878SMichal Meloun sc->txdesc_ring[idx].desc1 = desc1; 6555d43fd68SRuslan Bukin 6565d43fd68SRuslan Bukin if (paddr && len) { 6575d43fd68SRuslan Bukin wmb(); 6588d43a868SMichal Meloun sc->txdesc_ring[idx].desc0 |= TDESC0_OWN; 6595d43fd68SRuslan Bukin wmb(); 6605d43fd68SRuslan Bukin } 6615d43fd68SRuslan Bukin } 6625d43fd68SRuslan Bukin 6635d43fd68SRuslan Bukin static int 6645d43fd68SRuslan Bukin dwc_setup_txbuf(struct dwc_softc *sc, int idx, struct mbuf **mp) 6655d43fd68SRuslan Bukin { 6665d43fd68SRuslan Bukin struct bus_dma_segment seg; 6675d43fd68SRuslan Bukin int error, nsegs; 6685d43fd68SRuslan Bukin struct mbuf * m; 669*98ea5a7bSEmmanuel Vadot uint32_t flags = 0; 6705d43fd68SRuslan Bukin 6715d43fd68SRuslan Bukin if ((m = m_defrag(*mp, M_NOWAIT)) == NULL) 6725d43fd68SRuslan Bukin return (ENOMEM); 6735d43fd68SRuslan Bukin *mp = m; 6745d43fd68SRuslan Bukin 6755d43fd68SRuslan Bukin error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, 6765d43fd68SRuslan Bukin m, &seg, &nsegs, 0); 6775d43fd68SRuslan Bukin if (error != 0) { 6785d43fd68SRuslan Bukin return (ENOMEM); 6795d43fd68SRuslan Bukin } 6805d43fd68SRuslan Bukin 6815d43fd68SRuslan Bukin KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 6825d43fd68SRuslan Bukin 6835d43fd68SRuslan Bukin bus_dmamap_sync(sc->txbuf_tag, sc->txbuf_map[idx].map, 6845d43fd68SRuslan Bukin BUS_DMASYNC_PREWRITE); 6855d43fd68SRuslan Bukin 6865d43fd68SRuslan Bukin sc->txbuf_map[idx].mbuf = m; 6875d43fd68SRuslan Bukin 688*98ea5a7bSEmmanuel Vadot if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) { 689*98ea5a7bSEmmanuel Vadot if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0) { 690*98ea5a7bSEmmanuel Vadot if (sc->mactype != DWC_GMAC_EXT_DESC) 691*98ea5a7bSEmmanuel Vadot flags = NTDESC1_CIC_FULL; 692*98ea5a7bSEmmanuel Vadot else 693*98ea5a7bSEmmanuel Vadot flags = ETDESC0_CIC_FULL; 694*98ea5a7bSEmmanuel Vadot } else { 695*98ea5a7bSEmmanuel Vadot if (sc->mactype != DWC_GMAC_EXT_DESC) 696*98ea5a7bSEmmanuel Vadot flags = NTDESC1_CIC_HDR; 697*98ea5a7bSEmmanuel Vadot else 698*98ea5a7bSEmmanuel Vadot flags = ETDESC0_CIC_HDR; 699*98ea5a7bSEmmanuel Vadot } 700*98ea5a7bSEmmanuel Vadot } 701*98ea5a7bSEmmanuel Vadot 702*98ea5a7bSEmmanuel Vadot dwc_setup_txdesc(sc, idx, seg.ds_addr, seg.ds_len, flags); 7035d43fd68SRuslan Bukin 7045d43fd68SRuslan Bukin return (0); 7055d43fd68SRuslan Bukin } 7065d43fd68SRuslan Bukin 7075d43fd68SRuslan Bukin inline static uint32_t 7085d43fd68SRuslan Bukin dwc_setup_rxdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr) 7095d43fd68SRuslan Bukin { 7105d43fd68SRuslan Bukin uint32_t nidx; 7115d43fd68SRuslan Bukin 7128d43a868SMichal Meloun sc->rxdesc_ring[idx].addr1 = (uint32_t)paddr; 7135d43fd68SRuslan Bukin nidx = next_rxidx(sc, idx); 7148d43a868SMichal Meloun sc->rxdesc_ring[idx].addr2 = sc->rxdesc_ring_paddr + 7155d43fd68SRuslan Bukin (nidx * sizeof(struct dwc_hwdesc)); 716188aee74SMichal Meloun if (sc->mactype != DWC_GMAC_EXT_DESC) 717b72e2878SMichal Meloun sc->rxdesc_ring[idx].desc1 = NRDESC1_RCH | 718b72e2878SMichal Meloun MIN(MCLBYTES, NRDESC1_RBS1_MASK); 7195df53927SLuiz Otavio O Souza else 720b72e2878SMichal Meloun sc->rxdesc_ring[idx].desc1 = ERDESC1_RCH | 721b72e2878SMichal Meloun MIN(MCLBYTES, ERDESC1_RBS1_MASK); 7225d43fd68SRuslan Bukin 7235d43fd68SRuslan Bukin wmb(); 7248d43a868SMichal Meloun sc->rxdesc_ring[idx].desc0 = RDESC0_OWN; 7255d43fd68SRuslan Bukin wmb(); 7265d43fd68SRuslan Bukin return (nidx); 7275d43fd68SRuslan Bukin } 7285d43fd68SRuslan Bukin 7295d43fd68SRuslan Bukin static int 7305d43fd68SRuslan Bukin dwc_setup_rxbuf(struct dwc_softc *sc, int idx, struct mbuf *m) 7315d43fd68SRuslan Bukin { 7325d43fd68SRuslan Bukin struct bus_dma_segment seg; 7335d43fd68SRuslan Bukin int error, nsegs; 7345d43fd68SRuslan Bukin 7355d43fd68SRuslan Bukin m_adj(m, ETHER_ALIGN); 7365d43fd68SRuslan Bukin 7375d43fd68SRuslan Bukin error = bus_dmamap_load_mbuf_sg(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 7385d43fd68SRuslan Bukin m, &seg, &nsegs, 0); 7397ed7d979SMichal Meloun if (error != 0) 7405d43fd68SRuslan Bukin return (error); 7415d43fd68SRuslan Bukin 7425d43fd68SRuslan Bukin KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 7435d43fd68SRuslan Bukin 7445d43fd68SRuslan Bukin bus_dmamap_sync(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 7455d43fd68SRuslan Bukin BUS_DMASYNC_PREREAD); 7465d43fd68SRuslan Bukin 7475d43fd68SRuslan Bukin sc->rxbuf_map[idx].mbuf = m; 7485d43fd68SRuslan Bukin dwc_setup_rxdesc(sc, idx, seg.ds_addr); 7495d43fd68SRuslan Bukin 7505d43fd68SRuslan Bukin return (0); 7515d43fd68SRuslan Bukin } 7525d43fd68SRuslan Bukin 7535d43fd68SRuslan Bukin static struct mbuf * 7545d43fd68SRuslan Bukin dwc_alloc_mbufcl(struct dwc_softc *sc) 7555d43fd68SRuslan Bukin { 7565d43fd68SRuslan Bukin struct mbuf *m; 7575d43fd68SRuslan Bukin 7585d43fd68SRuslan Bukin m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 759db8a143aSRuslan Bukin if (m != NULL) 7605d43fd68SRuslan Bukin m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; 7615d43fd68SRuslan Bukin 7625d43fd68SRuslan Bukin return (m); 7635d43fd68SRuslan Bukin } 7645d43fd68SRuslan Bukin 765b72e2878SMichal Meloun static struct mbuf * 766b72e2878SMichal Meloun dwc_rxfinish_one(struct dwc_softc *sc, struct dwc_hwdesc *desc, 767b72e2878SMichal Meloun struct dwc_bufmap *map) 768b72e2878SMichal Meloun { 769b72e2878SMichal Meloun struct ifnet *ifp; 770b72e2878SMichal Meloun struct mbuf *m, *m0; 771b72e2878SMichal Meloun int len; 772b72e2878SMichal Meloun uint32_t rdesc0; 773b72e2878SMichal Meloun 774b72e2878SMichal Meloun m = map->mbuf; 775b72e2878SMichal Meloun ifp = sc->ifp; 776b72e2878SMichal Meloun rdesc0 = desc ->desc0; 777b72e2878SMichal Meloun /* Validate descriptor. */ 778b72e2878SMichal Meloun if (rdesc0 & RDESC0_ES) { 779b72e2878SMichal Meloun /* 780b72e2878SMichal Meloun * Errored packet. Statistic counters are updated 781b72e2878SMichal Meloun * globally, so do nothing 782b72e2878SMichal Meloun */ 783b72e2878SMichal Meloun return (NULL); 784b72e2878SMichal Meloun } 785b72e2878SMichal Meloun 786b72e2878SMichal Meloun if ((rdesc0 & (RDESC0_FS | RDESC0_LS)) != 787b72e2878SMichal Meloun (RDESC0_FS | RDESC0_LS)) { 788b72e2878SMichal Meloun /* 789b72e2878SMichal Meloun * Something very wrong happens. The whole packet should be 790b72e2878SMichal Meloun * recevied in one descriptr. Report problem. 791b72e2878SMichal Meloun */ 792b72e2878SMichal Meloun device_printf(sc->dev, 793b72e2878SMichal Meloun "%s: RX descriptor without FIRST and LAST bit set: 0x%08X", 794b72e2878SMichal Meloun __func__, rdesc0); 795b72e2878SMichal Meloun return (NULL); 796b72e2878SMichal Meloun } 797b72e2878SMichal Meloun 798b72e2878SMichal Meloun len = (rdesc0 >> RDESC0_FL_SHIFT) & RDESC0_FL_MASK; 799b72e2878SMichal Meloun if (len < 64) { 800b72e2878SMichal Meloun /* 801b72e2878SMichal Meloun * Lenght is invalid, recycle old mbuf 802b72e2878SMichal Meloun * Probably impossible case 803b72e2878SMichal Meloun */ 804b72e2878SMichal Meloun return (NULL); 805b72e2878SMichal Meloun } 806b72e2878SMichal Meloun 807b72e2878SMichal Meloun /* Allocate new buffer */ 808b72e2878SMichal Meloun m0 = dwc_alloc_mbufcl(sc); 809b72e2878SMichal Meloun if (m0 == NULL) { 810b72e2878SMichal Meloun /* no new mbuf available, recycle old */ 811b72e2878SMichal Meloun if_inc_counter(sc->ifp, IFCOUNTER_IQDROPS, 1); 812b72e2878SMichal Meloun return (NULL); 813b72e2878SMichal Meloun } 814b72e2878SMichal Meloun /* Do dmasync for newly received packet */ 815b72e2878SMichal Meloun bus_dmamap_sync(sc->rxbuf_tag, map->map, BUS_DMASYNC_POSTREAD); 816b72e2878SMichal Meloun bus_dmamap_unload(sc->rxbuf_tag, map->map); 817b72e2878SMichal Meloun 818b72e2878SMichal Meloun /* Received packet is valid, process it */ 819b72e2878SMichal Meloun m->m_pkthdr.rcvif = ifp; 820b72e2878SMichal Meloun m->m_pkthdr.len = len; 821b72e2878SMichal Meloun m->m_len = len; 822b72e2878SMichal Meloun if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 823b72e2878SMichal Meloun 824*98ea5a7bSEmmanuel Vadot if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0 && 825*98ea5a7bSEmmanuel Vadot (rdesc0 & RDESC0_FT) != 0) { 826*98ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_flags = CSUM_IP_CHECKED; 827*98ea5a7bSEmmanuel Vadot if ((rdesc0 & RDESC0_ICE) == 0) 828*98ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_flags |= CSUM_IP_VALID; 829*98ea5a7bSEmmanuel Vadot if ((rdesc0 & RDESC0_PCE) == 0) { 830*98ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_flags |= 831*98ea5a7bSEmmanuel Vadot CSUM_DATA_VALID | CSUM_PSEUDO_HDR; 832*98ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_data = 0xffff; 833*98ea5a7bSEmmanuel Vadot } 834*98ea5a7bSEmmanuel Vadot } 835*98ea5a7bSEmmanuel Vadot 836b72e2878SMichal Meloun /* Remove trailing FCS */ 837b72e2878SMichal Meloun m_adj(m, -ETHER_CRC_LEN); 838b72e2878SMichal Meloun 839b72e2878SMichal Meloun DWC_UNLOCK(sc); 840b72e2878SMichal Meloun (*ifp->if_input)(ifp, m); 841b72e2878SMichal Meloun DWC_LOCK(sc); 842b72e2878SMichal Meloun return (m0); 843b72e2878SMichal Meloun } 844b72e2878SMichal Meloun 8455d43fd68SRuslan Bukin static int 8465d43fd68SRuslan Bukin setup_dma(struct dwc_softc *sc) 8475d43fd68SRuslan Bukin { 8485d43fd68SRuslan Bukin struct mbuf *m; 8495d43fd68SRuslan Bukin int error; 8505d43fd68SRuslan Bukin int nidx; 8515d43fd68SRuslan Bukin int idx; 8525d43fd68SRuslan Bukin 8535d43fd68SRuslan Bukin /* 8545d43fd68SRuslan Bukin * Set up TX descriptor ring, descriptors, and dma maps. 8555d43fd68SRuslan Bukin */ 8565d43fd68SRuslan Bukin error = bus_dma_tag_create( 8575d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 8585d43fd68SRuslan Bukin DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ 8595d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 8605d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 8615d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 8625d43fd68SRuslan Bukin TX_DESC_SIZE, 1, /* maxsize, nsegments */ 8635d43fd68SRuslan Bukin TX_DESC_SIZE, /* maxsegsize */ 8645d43fd68SRuslan Bukin 0, /* flags */ 8655d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 8665d43fd68SRuslan Bukin &sc->txdesc_tag); 8675d43fd68SRuslan Bukin if (error != 0) { 8685d43fd68SRuslan Bukin device_printf(sc->dev, 8695d43fd68SRuslan Bukin "could not create TX ring DMA tag.\n"); 8705d43fd68SRuslan Bukin goto out; 8715d43fd68SRuslan Bukin } 8725d43fd68SRuslan Bukin 8735d43fd68SRuslan Bukin error = bus_dmamem_alloc(sc->txdesc_tag, (void**)&sc->txdesc_ring, 8745d43fd68SRuslan Bukin BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, 8755d43fd68SRuslan Bukin &sc->txdesc_map); 8765d43fd68SRuslan Bukin if (error != 0) { 8775d43fd68SRuslan Bukin device_printf(sc->dev, 8785d43fd68SRuslan Bukin "could not allocate TX descriptor ring.\n"); 8795d43fd68SRuslan Bukin goto out; 8805d43fd68SRuslan Bukin } 8815d43fd68SRuslan Bukin 8825d43fd68SRuslan Bukin error = bus_dmamap_load(sc->txdesc_tag, sc->txdesc_map, 8835d43fd68SRuslan Bukin sc->txdesc_ring, TX_DESC_SIZE, dwc_get1paddr, 8845d43fd68SRuslan Bukin &sc->txdesc_ring_paddr, 0); 8855d43fd68SRuslan Bukin if (error != 0) { 8865d43fd68SRuslan Bukin device_printf(sc->dev, 8875d43fd68SRuslan Bukin "could not load TX descriptor ring map.\n"); 8885d43fd68SRuslan Bukin goto out; 8895d43fd68SRuslan Bukin } 8905d43fd68SRuslan Bukin 8915d43fd68SRuslan Bukin for (idx = 0; idx < TX_DESC_COUNT; idx++) { 8925d43fd68SRuslan Bukin nidx = next_txidx(sc, idx); 8938d43a868SMichal Meloun sc->txdesc_ring[idx].addr2 = sc->txdesc_ring_paddr + 8945d43fd68SRuslan Bukin (nidx * sizeof(struct dwc_hwdesc)); 8955d43fd68SRuslan Bukin } 8965d43fd68SRuslan Bukin 8975d43fd68SRuslan Bukin error = bus_dma_tag_create( 8985d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 8995d43fd68SRuslan Bukin 1, 0, /* alignment, boundary */ 9005d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9015d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9025d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 9035d43fd68SRuslan Bukin MCLBYTES, 1, /* maxsize, nsegments */ 9045d43fd68SRuslan Bukin MCLBYTES, /* maxsegsize */ 9055d43fd68SRuslan Bukin 0, /* flags */ 9065d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9075d43fd68SRuslan Bukin &sc->txbuf_tag); 9085d43fd68SRuslan Bukin if (error != 0) { 9095d43fd68SRuslan Bukin device_printf(sc->dev, 9105d43fd68SRuslan Bukin "could not create TX ring DMA tag.\n"); 9115d43fd68SRuslan Bukin goto out; 9125d43fd68SRuslan Bukin } 9135d43fd68SRuslan Bukin 9145d43fd68SRuslan Bukin for (idx = 0; idx < TX_DESC_COUNT; idx++) { 9155d43fd68SRuslan Bukin error = bus_dmamap_create(sc->txbuf_tag, BUS_DMA_COHERENT, 9165d43fd68SRuslan Bukin &sc->txbuf_map[idx].map); 9175d43fd68SRuslan Bukin if (error != 0) { 9185d43fd68SRuslan Bukin device_printf(sc->dev, 9195d43fd68SRuslan Bukin "could not create TX buffer DMA map.\n"); 9205d43fd68SRuslan Bukin goto out; 9215d43fd68SRuslan Bukin } 922*98ea5a7bSEmmanuel Vadot dwc_setup_txdesc(sc, idx, 0, 0, 0); 9235d43fd68SRuslan Bukin } 9245d43fd68SRuslan Bukin 9255d43fd68SRuslan Bukin /* 9265d43fd68SRuslan Bukin * Set up RX descriptor ring, descriptors, dma maps, and mbufs. 9275d43fd68SRuslan Bukin */ 9285d43fd68SRuslan Bukin error = bus_dma_tag_create( 9295d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 9305d43fd68SRuslan Bukin DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ 9315d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9325d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9335d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 9345d43fd68SRuslan Bukin RX_DESC_SIZE, 1, /* maxsize, nsegments */ 9355d43fd68SRuslan Bukin RX_DESC_SIZE, /* maxsegsize */ 9365d43fd68SRuslan Bukin 0, /* flags */ 9375d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9385d43fd68SRuslan Bukin &sc->rxdesc_tag); 9395d43fd68SRuslan Bukin if (error != 0) { 9405d43fd68SRuslan Bukin device_printf(sc->dev, 9415d43fd68SRuslan Bukin "could not create RX ring DMA tag.\n"); 9425d43fd68SRuslan Bukin goto out; 9435d43fd68SRuslan Bukin } 9445d43fd68SRuslan Bukin 9455d43fd68SRuslan Bukin error = bus_dmamem_alloc(sc->rxdesc_tag, (void **)&sc->rxdesc_ring, 9465d43fd68SRuslan Bukin BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, 9475d43fd68SRuslan Bukin &sc->rxdesc_map); 9485d43fd68SRuslan Bukin if (error != 0) { 9495d43fd68SRuslan Bukin device_printf(sc->dev, 9505d43fd68SRuslan Bukin "could not allocate RX descriptor ring.\n"); 9515d43fd68SRuslan Bukin goto out; 9525d43fd68SRuslan Bukin } 9535d43fd68SRuslan Bukin 9545d43fd68SRuslan Bukin error = bus_dmamap_load(sc->rxdesc_tag, sc->rxdesc_map, 9555d43fd68SRuslan Bukin sc->rxdesc_ring, RX_DESC_SIZE, dwc_get1paddr, 9565d43fd68SRuslan Bukin &sc->rxdesc_ring_paddr, 0); 9575d43fd68SRuslan Bukin if (error != 0) { 9585d43fd68SRuslan Bukin device_printf(sc->dev, 9595d43fd68SRuslan Bukin "could not load RX descriptor ring map.\n"); 9605d43fd68SRuslan Bukin goto out; 9615d43fd68SRuslan Bukin } 9625d43fd68SRuslan Bukin 9635d43fd68SRuslan Bukin error = bus_dma_tag_create( 9645d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 9655d43fd68SRuslan Bukin 1, 0, /* alignment, boundary */ 9665d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9675d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9685d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 9695d43fd68SRuslan Bukin MCLBYTES, 1, /* maxsize, nsegments */ 9705d43fd68SRuslan Bukin MCLBYTES, /* maxsegsize */ 9715d43fd68SRuslan Bukin 0, /* flags */ 9725d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9735d43fd68SRuslan Bukin &sc->rxbuf_tag); 9745d43fd68SRuslan Bukin if (error != 0) { 9755d43fd68SRuslan Bukin device_printf(sc->dev, 9765d43fd68SRuslan Bukin "could not create RX buf DMA tag.\n"); 9775d43fd68SRuslan Bukin goto out; 9785d43fd68SRuslan Bukin } 9795d43fd68SRuslan Bukin 9805d43fd68SRuslan Bukin for (idx = 0; idx < RX_DESC_COUNT; idx++) { 9815d43fd68SRuslan Bukin error = bus_dmamap_create(sc->rxbuf_tag, BUS_DMA_COHERENT, 9825d43fd68SRuslan Bukin &sc->rxbuf_map[idx].map); 9835d43fd68SRuslan Bukin if (error != 0) { 9845d43fd68SRuslan Bukin device_printf(sc->dev, 9855d43fd68SRuslan Bukin "could not create RX buffer DMA map.\n"); 9865d43fd68SRuslan Bukin goto out; 9875d43fd68SRuslan Bukin } 9885d43fd68SRuslan Bukin if ((m = dwc_alloc_mbufcl(sc)) == NULL) { 9895d43fd68SRuslan Bukin device_printf(sc->dev, "Could not alloc mbuf\n"); 9905d43fd68SRuslan Bukin error = ENOMEM; 9915d43fd68SRuslan Bukin goto out; 9925d43fd68SRuslan Bukin } 9935d43fd68SRuslan Bukin if ((error = dwc_setup_rxbuf(sc, idx, m)) != 0) { 9945d43fd68SRuslan Bukin device_printf(sc->dev, 9955d43fd68SRuslan Bukin "could not create new RX buffer.\n"); 9965d43fd68SRuslan Bukin goto out; 9975d43fd68SRuslan Bukin } 9985d43fd68SRuslan Bukin } 9995d43fd68SRuslan Bukin 10005d43fd68SRuslan Bukin out: 10015d43fd68SRuslan Bukin if (error != 0) 10025d43fd68SRuslan Bukin return (ENXIO); 10035d43fd68SRuslan Bukin 10045d43fd68SRuslan Bukin return (0); 10055d43fd68SRuslan Bukin } 10065d43fd68SRuslan Bukin 10072a35d391SEmmanuel Vadot /* 10082a35d391SEmmanuel Vadot * if_ functions 10092a35d391SEmmanuel Vadot */ 10102a35d391SEmmanuel Vadot 1011f88e0af6SEmmanuel Vadot static void 10122a35d391SEmmanuel Vadot dwc_txstart_locked(struct dwc_softc *sc) 10135d43fd68SRuslan Bukin { 10142a35d391SEmmanuel Vadot struct ifnet *ifp; 10152a35d391SEmmanuel Vadot struct mbuf *m; 10162a35d391SEmmanuel Vadot int enqueued; 10172a35d391SEmmanuel Vadot 10182a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 10192a35d391SEmmanuel Vadot 10202a35d391SEmmanuel Vadot if (!sc->link_is_up) 10212a35d391SEmmanuel Vadot return; 10222a35d391SEmmanuel Vadot 10232a35d391SEmmanuel Vadot ifp = sc->ifp; 10242a35d391SEmmanuel Vadot 10253bbd11eeSEmmanuel Vadot if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != 10263bbd11eeSEmmanuel Vadot IFF_DRV_RUNNING) 10272a35d391SEmmanuel Vadot return; 10282a35d391SEmmanuel Vadot 10292a35d391SEmmanuel Vadot enqueued = 0; 10302a35d391SEmmanuel Vadot 10312a35d391SEmmanuel Vadot for (;;) { 10322a35d391SEmmanuel Vadot if (sc->txcount == (TX_DESC_COUNT - 1)) { 10333bbd11eeSEmmanuel Vadot if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 10342a35d391SEmmanuel Vadot break; 10352a35d391SEmmanuel Vadot } 10362a35d391SEmmanuel Vadot 10373bbd11eeSEmmanuel Vadot m = if_dequeue(ifp); 10382a35d391SEmmanuel Vadot if (m == NULL) 10392a35d391SEmmanuel Vadot break; 10402a35d391SEmmanuel Vadot if (dwc_setup_txbuf(sc, sc->tx_idx_head, &m) != 0) { 10413bbd11eeSEmmanuel Vadot if_sendq_prepend(ifp, m); 10422a35d391SEmmanuel Vadot break; 10432a35d391SEmmanuel Vadot } 10443bbd11eeSEmmanuel Vadot if_bpfmtap(ifp, m); 10452a35d391SEmmanuel Vadot sc->tx_idx_head = next_txidx(sc, sc->tx_idx_head); 10462a35d391SEmmanuel Vadot ++enqueued; 10472a35d391SEmmanuel Vadot } 10482a35d391SEmmanuel Vadot 10492a35d391SEmmanuel Vadot if (enqueued != 0) { 10502a35d391SEmmanuel Vadot WRITE4(sc, TRANSMIT_POLL_DEMAND, 0x1); 10512a35d391SEmmanuel Vadot sc->tx_watchdog_count = WATCHDOG_TIMEOUT_SECS; 10522a35d391SEmmanuel Vadot } 10532a35d391SEmmanuel Vadot } 10542a35d391SEmmanuel Vadot 10552a35d391SEmmanuel Vadot static void 10562a35d391SEmmanuel Vadot dwc_txstart(struct ifnet *ifp) 10572a35d391SEmmanuel Vadot { 10582a35d391SEmmanuel Vadot struct dwc_softc *sc = ifp->if_softc; 10592a35d391SEmmanuel Vadot 10602a35d391SEmmanuel Vadot DWC_LOCK(sc); 10612a35d391SEmmanuel Vadot dwc_txstart_locked(sc); 10622a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 10632a35d391SEmmanuel Vadot } 10642a35d391SEmmanuel Vadot 10652a35d391SEmmanuel Vadot static void 10662a35d391SEmmanuel Vadot dwc_init_locked(struct dwc_softc *sc) 10672a35d391SEmmanuel Vadot { 10682a35d391SEmmanuel Vadot struct ifnet *ifp = sc->ifp; 10692a35d391SEmmanuel Vadot 10702a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 10712a35d391SEmmanuel Vadot 10723bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) 10732a35d391SEmmanuel Vadot return; 10742a35d391SEmmanuel Vadot 10752a35d391SEmmanuel Vadot dwc_setup_rxfilter(sc); 10762a35d391SEmmanuel Vadot dwc_setup_core(sc); 10772a35d391SEmmanuel Vadot dwc_enable_mac(sc, true); 10782a35d391SEmmanuel Vadot dwc_init_dma(sc); 10792a35d391SEmmanuel Vadot 10802a35d391SEmmanuel Vadot if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); 10815d43fd68SRuslan Bukin 10825d43fd68SRuslan Bukin /* 10832a35d391SEmmanuel Vadot * Call mii_mediachg() which will call back into dwc_miibus_statchg() 10842a35d391SEmmanuel Vadot * to set up the remaining config registers based on current media. 10855d43fd68SRuslan Bukin */ 10862a35d391SEmmanuel Vadot mii_mediachg(sc->mii_softc); 10872a35d391SEmmanuel Vadot callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); 10882a35d391SEmmanuel Vadot } 10892a35d391SEmmanuel Vadot 10902a35d391SEmmanuel Vadot static void 10912a35d391SEmmanuel Vadot dwc_init(void *if_softc) 10922a35d391SEmmanuel Vadot { 10932a35d391SEmmanuel Vadot struct dwc_softc *sc = if_softc; 10942a35d391SEmmanuel Vadot 10952a35d391SEmmanuel Vadot DWC_LOCK(sc); 10962a35d391SEmmanuel Vadot dwc_init_locked(sc); 10972a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 10982a35d391SEmmanuel Vadot } 10992a35d391SEmmanuel Vadot 11002a35d391SEmmanuel Vadot static void 11012a35d391SEmmanuel Vadot dwc_stop_locked(struct dwc_softc *sc) 11022a35d391SEmmanuel Vadot { 11032a35d391SEmmanuel Vadot struct ifnet *ifp; 11042a35d391SEmmanuel Vadot 11052a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 11062a35d391SEmmanuel Vadot 11072a35d391SEmmanuel Vadot ifp = sc->ifp; 11083bbd11eeSEmmanuel Vadot if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 11092a35d391SEmmanuel Vadot sc->tx_watchdog_count = 0; 11102a35d391SEmmanuel Vadot sc->stats_harvest_count = 0; 11112a35d391SEmmanuel Vadot 11122a35d391SEmmanuel Vadot callout_stop(&sc->dwc_callout); 11132a35d391SEmmanuel Vadot 11142a35d391SEmmanuel Vadot dwc_stop_dma(sc); 11152a35d391SEmmanuel Vadot dwc_enable_mac(sc, false); 11162a35d391SEmmanuel Vadot } 11172a35d391SEmmanuel Vadot 11182a35d391SEmmanuel Vadot static int 11192a35d391SEmmanuel Vadot dwc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 11202a35d391SEmmanuel Vadot { 11212a35d391SEmmanuel Vadot struct dwc_softc *sc; 11222a35d391SEmmanuel Vadot struct mii_data *mii; 11232a35d391SEmmanuel Vadot struct ifreq *ifr; 11243bbd11eeSEmmanuel Vadot int flags, mask, error; 11252a35d391SEmmanuel Vadot 11262a35d391SEmmanuel Vadot sc = ifp->if_softc; 11272a35d391SEmmanuel Vadot ifr = (struct ifreq *)data; 11282a35d391SEmmanuel Vadot 11292a35d391SEmmanuel Vadot error = 0; 11302a35d391SEmmanuel Vadot switch (cmd) { 11312a35d391SEmmanuel Vadot case SIOCSIFFLAGS: 11322a35d391SEmmanuel Vadot DWC_LOCK(sc); 11333bbd11eeSEmmanuel Vadot if (if_getflags(ifp) & IFF_UP) { 11343bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 11353bbd11eeSEmmanuel Vadot flags = if_getflags(ifp) ^ sc->if_flags; 11363bbd11eeSEmmanuel Vadot if ((flags & (IFF_PROMISC|IFF_ALLMULTI)) != 0) 11372a35d391SEmmanuel Vadot dwc_setup_rxfilter(sc); 11385d43fd68SRuslan Bukin } else { 11392a35d391SEmmanuel Vadot if (!sc->is_detaching) 11402a35d391SEmmanuel Vadot dwc_init_locked(sc); 11412a35d391SEmmanuel Vadot } 11422a35d391SEmmanuel Vadot } else { 11433bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) 11442a35d391SEmmanuel Vadot dwc_stop_locked(sc); 11452a35d391SEmmanuel Vadot } 11463bbd11eeSEmmanuel Vadot sc->if_flags = if_getflags(ifp); 11472a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 11482a35d391SEmmanuel Vadot break; 11492a35d391SEmmanuel Vadot case SIOCADDMULTI: 11502a35d391SEmmanuel Vadot case SIOCDELMULTI: 11513bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 11522a35d391SEmmanuel Vadot DWC_LOCK(sc); 11532a35d391SEmmanuel Vadot dwc_setup_rxfilter(sc); 11542a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 11552a35d391SEmmanuel Vadot } 11562a35d391SEmmanuel Vadot break; 11572a35d391SEmmanuel Vadot case SIOCSIFMEDIA: 11582a35d391SEmmanuel Vadot case SIOCGIFMEDIA: 11592a35d391SEmmanuel Vadot mii = sc->mii_softc; 11602a35d391SEmmanuel Vadot error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); 11612a35d391SEmmanuel Vadot break; 11622a35d391SEmmanuel Vadot case SIOCSIFCAP: 11633bbd11eeSEmmanuel Vadot mask = ifr->ifr_reqcap ^ if_getcapenable(ifp); 11642a35d391SEmmanuel Vadot if (mask & IFCAP_VLAN_MTU) { 11652a35d391SEmmanuel Vadot /* No work to do except acknowledge the change took */ 11663bbd11eeSEmmanuel Vadot if_togglecapenable(ifp, IFCAP_VLAN_MTU); 11672a35d391SEmmanuel Vadot } 1168*98ea5a7bSEmmanuel Vadot if (mask & IFCAP_RXCSUM) 1169*98ea5a7bSEmmanuel Vadot if_togglecapenable(ifp, IFCAP_RXCSUM); 1170*98ea5a7bSEmmanuel Vadot if (mask & IFCAP_TXCSUM) 1171*98ea5a7bSEmmanuel Vadot if_togglecapenable(ifp, IFCAP_TXCSUM); 1172*98ea5a7bSEmmanuel Vadot if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0) 1173*98ea5a7bSEmmanuel Vadot if_sethwassistbits(ifp, CSUM_IP | CSUM_UDP | CSUM_TCP, 0); 1174*98ea5a7bSEmmanuel Vadot else 1175*98ea5a7bSEmmanuel Vadot if_sethwassistbits(ifp, 0, CSUM_IP | CSUM_UDP | CSUM_TCP); 11762a35d391SEmmanuel Vadot break; 11772a35d391SEmmanuel Vadot 11782a35d391SEmmanuel Vadot default: 11792a35d391SEmmanuel Vadot error = ether_ioctl(ifp, cmd, data); 11802a35d391SEmmanuel Vadot break; 11812a35d391SEmmanuel Vadot } 11822a35d391SEmmanuel Vadot 11832a35d391SEmmanuel Vadot return (error); 11842a35d391SEmmanuel Vadot } 11852a35d391SEmmanuel Vadot 11862a35d391SEmmanuel Vadot /* 11872a35d391SEmmanuel Vadot * Interrupts functions 11882a35d391SEmmanuel Vadot */ 11892a35d391SEmmanuel Vadot 11902a35d391SEmmanuel Vadot static void 11912a35d391SEmmanuel Vadot dwc_txfinish_locked(struct dwc_softc *sc) 11922a35d391SEmmanuel Vadot { 11932a35d391SEmmanuel Vadot struct dwc_bufmap *bmap; 11942a35d391SEmmanuel Vadot struct dwc_hwdesc *desc; 11952a35d391SEmmanuel Vadot struct ifnet *ifp; 11962a35d391SEmmanuel Vadot 11972a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 11982a35d391SEmmanuel Vadot 11992a35d391SEmmanuel Vadot ifp = sc->ifp; 12002a35d391SEmmanuel Vadot while (sc->tx_idx_tail != sc->tx_idx_head) { 12012a35d391SEmmanuel Vadot desc = &sc->txdesc_ring[sc->tx_idx_tail]; 12022a35d391SEmmanuel Vadot if ((desc->desc0 & TDESC0_OWN) != 0) 12032a35d391SEmmanuel Vadot break; 12042a35d391SEmmanuel Vadot bmap = &sc->txbuf_map[sc->tx_idx_tail]; 12052a35d391SEmmanuel Vadot bus_dmamap_sync(sc->txbuf_tag, bmap->map, 12062a35d391SEmmanuel Vadot BUS_DMASYNC_POSTWRITE); 12072a35d391SEmmanuel Vadot bus_dmamap_unload(sc->txbuf_tag, bmap->map); 12082a35d391SEmmanuel Vadot m_freem(bmap->mbuf); 12092a35d391SEmmanuel Vadot bmap->mbuf = NULL; 1210*98ea5a7bSEmmanuel Vadot dwc_setup_txdesc(sc, sc->tx_idx_tail, 0, 0, 0); 12112a35d391SEmmanuel Vadot sc->tx_idx_tail = next_txidx(sc, sc->tx_idx_tail); 12123bbd11eeSEmmanuel Vadot if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); 12132a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 12142a35d391SEmmanuel Vadot } 12152a35d391SEmmanuel Vadot 12162a35d391SEmmanuel Vadot /* If there are no buffers outstanding, muzzle the watchdog. */ 12172a35d391SEmmanuel Vadot if (sc->tx_idx_tail == sc->tx_idx_head) { 12182a35d391SEmmanuel Vadot sc->tx_watchdog_count = 0; 12195d43fd68SRuslan Bukin } 12205d43fd68SRuslan Bukin } 12215d43fd68SRuslan Bukin 12222a35d391SEmmanuel Vadot static void 12232a35d391SEmmanuel Vadot dwc_rxfinish_locked(struct dwc_softc *sc) 12242a35d391SEmmanuel Vadot { 12252a35d391SEmmanuel Vadot struct ifnet *ifp; 12262a35d391SEmmanuel Vadot struct mbuf *m; 12272a35d391SEmmanuel Vadot int error, idx; 12282a35d391SEmmanuel Vadot struct dwc_hwdesc *desc; 12292a35d391SEmmanuel Vadot 12302a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 12312a35d391SEmmanuel Vadot ifp = sc->ifp; 12322a35d391SEmmanuel Vadot for (;;) { 12332a35d391SEmmanuel Vadot idx = sc->rx_idx; 12342a35d391SEmmanuel Vadot desc = sc->rxdesc_ring + idx; 12352a35d391SEmmanuel Vadot if ((desc->desc0 & RDESC0_OWN) != 0) 12362a35d391SEmmanuel Vadot break; 12372a35d391SEmmanuel Vadot 12382a35d391SEmmanuel Vadot m = dwc_rxfinish_one(sc, desc, sc->rxbuf_map + idx); 12392a35d391SEmmanuel Vadot if (m == NULL) { 12402a35d391SEmmanuel Vadot wmb(); 12412a35d391SEmmanuel Vadot desc->desc0 = RDESC0_OWN; 12422a35d391SEmmanuel Vadot wmb(); 12432a35d391SEmmanuel Vadot } else { 12442a35d391SEmmanuel Vadot /* We cannot create hole in RX ring */ 12452a35d391SEmmanuel Vadot error = dwc_setup_rxbuf(sc, idx, m); 12462a35d391SEmmanuel Vadot if (error != 0) 12472a35d391SEmmanuel Vadot panic("dwc_setup_rxbuf failed: error %d\n", 12482a35d391SEmmanuel Vadot error); 1249*98ea5a7bSEmmanuel Vadot 12502a35d391SEmmanuel Vadot } 12512a35d391SEmmanuel Vadot sc->rx_idx = next_rxidx(sc, sc->rx_idx); 12522a35d391SEmmanuel Vadot } 12532a35d391SEmmanuel Vadot } 12542a35d391SEmmanuel Vadot 12552a35d391SEmmanuel Vadot static void 12562a35d391SEmmanuel Vadot dwc_intr(void *arg) 12572a35d391SEmmanuel Vadot { 12582a35d391SEmmanuel Vadot struct dwc_softc *sc; 12592a35d391SEmmanuel Vadot uint32_t reg; 12602a35d391SEmmanuel Vadot 12612a35d391SEmmanuel Vadot sc = arg; 12622a35d391SEmmanuel Vadot 12632a35d391SEmmanuel Vadot DWC_LOCK(sc); 12642a35d391SEmmanuel Vadot 12652a35d391SEmmanuel Vadot reg = READ4(sc, INTERRUPT_STATUS); 12662a35d391SEmmanuel Vadot if (reg) 12672a35d391SEmmanuel Vadot READ4(sc, SGMII_RGMII_SMII_CTRL_STATUS); 12682a35d391SEmmanuel Vadot 12692a35d391SEmmanuel Vadot reg = READ4(sc, DMA_STATUS); 12702a35d391SEmmanuel Vadot if (reg & DMA_STATUS_NIS) { 12712a35d391SEmmanuel Vadot if (reg & DMA_STATUS_RI) 12722a35d391SEmmanuel Vadot dwc_rxfinish_locked(sc); 12732a35d391SEmmanuel Vadot 12742a35d391SEmmanuel Vadot if (reg & DMA_STATUS_TI) { 12752a35d391SEmmanuel Vadot dwc_txfinish_locked(sc); 12762a35d391SEmmanuel Vadot dwc_txstart_locked(sc); 12772a35d391SEmmanuel Vadot } 12782a35d391SEmmanuel Vadot } 12792a35d391SEmmanuel Vadot 12802a35d391SEmmanuel Vadot if (reg & DMA_STATUS_AIS) { 12812a35d391SEmmanuel Vadot if (reg & DMA_STATUS_FBI) { 12822a35d391SEmmanuel Vadot /* Fatal bus error */ 12832a35d391SEmmanuel Vadot device_printf(sc->dev, 12842a35d391SEmmanuel Vadot "Ethernet DMA error, restarting controller.\n"); 12852a35d391SEmmanuel Vadot dwc_stop_locked(sc); 12862a35d391SEmmanuel Vadot dwc_init_locked(sc); 12872a35d391SEmmanuel Vadot } 12882a35d391SEmmanuel Vadot } 12892a35d391SEmmanuel Vadot 12902a35d391SEmmanuel Vadot WRITE4(sc, DMA_STATUS, reg & DMA_STATUS_INTR_MASK); 12912a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 12922a35d391SEmmanuel Vadot } 12932a35d391SEmmanuel Vadot 12942a35d391SEmmanuel Vadot /* 12952a35d391SEmmanuel Vadot * Stats 12962a35d391SEmmanuel Vadot */ 12972a35d391SEmmanuel Vadot 12982a35d391SEmmanuel Vadot static void dwc_clear_stats(struct dwc_softc *sc) 12992a35d391SEmmanuel Vadot { 13002a35d391SEmmanuel Vadot uint32_t reg; 13012a35d391SEmmanuel Vadot 13022a35d391SEmmanuel Vadot reg = READ4(sc, MMC_CONTROL); 13032a35d391SEmmanuel Vadot reg |= (MMC_CONTROL_CNTRST); 13042a35d391SEmmanuel Vadot WRITE4(sc, MMC_CONTROL, reg); 13052a35d391SEmmanuel Vadot } 13062a35d391SEmmanuel Vadot 13072a35d391SEmmanuel Vadot static void 13082a35d391SEmmanuel Vadot dwc_harvest_stats(struct dwc_softc *sc) 13092a35d391SEmmanuel Vadot { 13102a35d391SEmmanuel Vadot struct ifnet *ifp; 13112a35d391SEmmanuel Vadot 13122a35d391SEmmanuel Vadot /* We don't need to harvest too often. */ 13132a35d391SEmmanuel Vadot if (++sc->stats_harvest_count < STATS_HARVEST_INTERVAL) 13142a35d391SEmmanuel Vadot return; 13152a35d391SEmmanuel Vadot 13162a35d391SEmmanuel Vadot sc->stats_harvest_count = 0; 13172a35d391SEmmanuel Vadot ifp = sc->ifp; 13182a35d391SEmmanuel Vadot 13192a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_IPACKETS, READ4(sc, RXFRAMECOUNT_GB)); 13202a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_IMCASTS, READ4(sc, RXMULTICASTFRAMES_G)); 13212a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_IERRORS, 13222a35d391SEmmanuel Vadot READ4(sc, RXOVERSIZE_G) + READ4(sc, RXUNDERSIZE_G) + 13232a35d391SEmmanuel Vadot READ4(sc, RXCRCERROR) + READ4(sc, RXALIGNMENTERROR) + 13242a35d391SEmmanuel Vadot READ4(sc, RXRUNTERROR) + READ4(sc, RXJABBERERROR) + 13252a35d391SEmmanuel Vadot READ4(sc, RXLENGTHERROR)); 13262a35d391SEmmanuel Vadot 13272a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OPACKETS, READ4(sc, TXFRAMECOUNT_G)); 13282a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OMCASTS, READ4(sc, TXMULTICASTFRAMES_G)); 13292a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OERRORS, 13302a35d391SEmmanuel Vadot READ4(sc, TXOVERSIZE_G) + READ4(sc, TXEXCESSDEF) + 13312a35d391SEmmanuel Vadot READ4(sc, TXCARRIERERR) + READ4(sc, TXUNDERFLOWERROR)); 13322a35d391SEmmanuel Vadot 13332a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 13342a35d391SEmmanuel Vadot READ4(sc, TXEXESSCOL) + READ4(sc, TXLATECOL)); 13352a35d391SEmmanuel Vadot 13362a35d391SEmmanuel Vadot dwc_clear_stats(sc); 13372a35d391SEmmanuel Vadot } 13382a35d391SEmmanuel Vadot 13392a35d391SEmmanuel Vadot static void 13402a35d391SEmmanuel Vadot dwc_tick(void *arg) 13412a35d391SEmmanuel Vadot { 13422a35d391SEmmanuel Vadot struct dwc_softc *sc; 13432a35d391SEmmanuel Vadot struct ifnet *ifp; 13442a35d391SEmmanuel Vadot int link_was_up; 13452a35d391SEmmanuel Vadot 13462a35d391SEmmanuel Vadot sc = arg; 13472a35d391SEmmanuel Vadot 13482a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 13492a35d391SEmmanuel Vadot 13502a35d391SEmmanuel Vadot ifp = sc->ifp; 13512a35d391SEmmanuel Vadot 13523bbd11eeSEmmanuel Vadot if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) 13532a35d391SEmmanuel Vadot return; 13542a35d391SEmmanuel Vadot 13552a35d391SEmmanuel Vadot /* 13562a35d391SEmmanuel Vadot * Typical tx watchdog. If this fires it indicates that we enqueued 13572a35d391SEmmanuel Vadot * packets for output and never got a txdone interrupt for them. Maybe 13582a35d391SEmmanuel Vadot * it's a missed interrupt somehow, just pretend we got one. 13592a35d391SEmmanuel Vadot */ 13602a35d391SEmmanuel Vadot if (sc->tx_watchdog_count > 0) { 13612a35d391SEmmanuel Vadot if (--sc->tx_watchdog_count == 0) { 13622a35d391SEmmanuel Vadot dwc_txfinish_locked(sc); 13632a35d391SEmmanuel Vadot } 13642a35d391SEmmanuel Vadot } 13652a35d391SEmmanuel Vadot 13662a35d391SEmmanuel Vadot /* Gather stats from hardware counters. */ 13672a35d391SEmmanuel Vadot dwc_harvest_stats(sc); 13682a35d391SEmmanuel Vadot 13692a35d391SEmmanuel Vadot /* Check the media status. */ 13702a35d391SEmmanuel Vadot link_was_up = sc->link_is_up; 13712a35d391SEmmanuel Vadot mii_tick(sc->mii_softc); 13722a35d391SEmmanuel Vadot if (sc->link_is_up && !link_was_up) 13732a35d391SEmmanuel Vadot dwc_txstart_locked(sc); 13742a35d391SEmmanuel Vadot 13752a35d391SEmmanuel Vadot /* Schedule another check one second from now. */ 13762a35d391SEmmanuel Vadot callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); 13772a35d391SEmmanuel Vadot } 13782a35d391SEmmanuel Vadot 13792a35d391SEmmanuel Vadot /* 13802a35d391SEmmanuel Vadot * Probe/Attach functions 13812a35d391SEmmanuel Vadot */ 13822a35d391SEmmanuel Vadot 1383d7acb49aSJared McNeill #define GPIO_ACTIVE_LOW 1 1384d7acb49aSJared McNeill 1385d7acb49aSJared McNeill static int 1386d7acb49aSJared McNeill dwc_reset(device_t dev) 1387d7acb49aSJared McNeill { 1388d7acb49aSJared McNeill pcell_t gpio_prop[4]; 1389d7acb49aSJared McNeill pcell_t delay_prop[3]; 1390d7acb49aSJared McNeill phandle_t node, gpio_node; 1391d7acb49aSJared McNeill device_t gpio; 1392d7acb49aSJared McNeill uint32_t pin, flags; 1393d7acb49aSJared McNeill uint32_t pin_value; 1394d7acb49aSJared McNeill 1395d7acb49aSJared McNeill node = ofw_bus_get_node(dev); 1396d7acb49aSJared McNeill if (OF_getencprop(node, "snps,reset-gpio", 1397d7acb49aSJared McNeill gpio_prop, sizeof(gpio_prop)) <= 0) 1398d7acb49aSJared McNeill return (0); 1399d7acb49aSJared McNeill 1400d7acb49aSJared McNeill if (OF_getencprop(node, "snps,reset-delays-us", 1401d7acb49aSJared McNeill delay_prop, sizeof(delay_prop)) <= 0) { 1402d7acb49aSJared McNeill device_printf(dev, 1403d7acb49aSJared McNeill "Wrong property for snps,reset-delays-us"); 1404d7acb49aSJared McNeill return (ENXIO); 1405d7acb49aSJared McNeill } 1406d7acb49aSJared McNeill 1407d7acb49aSJared McNeill gpio_node = OF_node_from_xref(gpio_prop[0]); 1408d7acb49aSJared McNeill if ((gpio = OF_device_from_xref(gpio_prop[0])) == NULL) { 1409d7acb49aSJared McNeill device_printf(dev, 1410d7acb49aSJared McNeill "Can't find gpio controller for phy reset\n"); 1411d7acb49aSJared McNeill return (ENXIO); 1412d7acb49aSJared McNeill } 1413d7acb49aSJared McNeill 1414d7acb49aSJared McNeill if (GPIO_MAP_GPIOS(gpio, node, gpio_node, 141573a1170aSPedro F. Giffuni nitems(gpio_prop) - 1, 1416d7acb49aSJared McNeill gpio_prop + 1, &pin, &flags) != 0) { 1417d7acb49aSJared McNeill device_printf(dev, "Can't map gpio for phy reset\n"); 1418d7acb49aSJared McNeill return (ENXIO); 1419d7acb49aSJared McNeill } 1420d7acb49aSJared McNeill 1421d7acb49aSJared McNeill pin_value = GPIO_PIN_LOW; 1422d7acb49aSJared McNeill if (OF_hasprop(node, "snps,reset-active-low")) 1423d7acb49aSJared McNeill pin_value = GPIO_PIN_HIGH; 1424d7acb49aSJared McNeill 1425d7acb49aSJared McNeill GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT); 1426d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 1427c069412eSEmmanuel Vadot DELAY(delay_prop[0] * 5); 1428d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, !pin_value); 1429c069412eSEmmanuel Vadot DELAY(delay_prop[1] * 5); 1430d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 1431c069412eSEmmanuel Vadot DELAY(delay_prop[2] * 5); 1432d7acb49aSJared McNeill 1433d7acb49aSJared McNeill return (0); 1434d7acb49aSJared McNeill } 1435d7acb49aSJared McNeill 14366a05f063SJared McNeill #ifdef EXT_RESOURCES 14376a05f063SJared McNeill static int 14386a05f063SJared McNeill dwc_clock_init(device_t dev) 14396a05f063SJared McNeill { 14406a05f063SJared McNeill hwreset_t rst; 14416a05f063SJared McNeill clk_t clk; 14426a05f063SJared McNeill int error; 1443824cfb47SOleksandr Tymoshenko int64_t freq; 14446a05f063SJared McNeill 1445824cfb47SOleksandr Tymoshenko /* Enable clocks */ 1446dac93553SMichal Meloun if (clk_get_by_ofw_name(dev, 0, "stmmaceth", &clk) == 0) { 14476a05f063SJared McNeill error = clk_enable(clk); 14486a05f063SJared McNeill if (error != 0) { 14496a05f063SJared McNeill device_printf(dev, "could not enable main clock\n"); 14506a05f063SJared McNeill return (error); 14516a05f063SJared McNeill } 1452824cfb47SOleksandr Tymoshenko if (bootverbose) { 1453824cfb47SOleksandr Tymoshenko clk_get_freq(clk, &freq); 1454fc254a2eSLi-Wen Hsu device_printf(dev, "MAC clock(%s) freq: %jd\n", 1455fc254a2eSLi-Wen Hsu clk_get_name(clk), (intmax_t)freq); 1456824cfb47SOleksandr Tymoshenko } 1457824cfb47SOleksandr Tymoshenko } 1458824cfb47SOleksandr Tymoshenko else { 1459824cfb47SOleksandr Tymoshenko device_printf(dev, "could not find clock stmmaceth\n"); 14606a05f063SJared McNeill } 14616a05f063SJared McNeill 14626a05f063SJared McNeill /* De-assert reset */ 1463dac93553SMichal Meloun if (hwreset_get_by_ofw_name(dev, 0, "stmmaceth", &rst) == 0) { 14646a05f063SJared McNeill error = hwreset_deassert(rst); 14656a05f063SJared McNeill if (error != 0) { 14666a05f063SJared McNeill device_printf(dev, "could not de-assert reset\n"); 14676a05f063SJared McNeill return (error); 14686a05f063SJared McNeill } 14696a05f063SJared McNeill } 14706a05f063SJared McNeill 14716a05f063SJared McNeill return (0); 14726a05f063SJared McNeill } 14736a05f063SJared McNeill #endif 14746a05f063SJared McNeill 14755d43fd68SRuslan Bukin static int 14765d43fd68SRuslan Bukin dwc_probe(device_t dev) 14775d43fd68SRuslan Bukin { 14785d43fd68SRuslan Bukin 14795d43fd68SRuslan Bukin if (!ofw_bus_status_okay(dev)) 14805d43fd68SRuslan Bukin return (ENXIO); 14815d43fd68SRuslan Bukin 14825d43fd68SRuslan Bukin if (!ofw_bus_is_compatible(dev, "snps,dwmac")) 14835d43fd68SRuslan Bukin return (ENXIO); 14845d43fd68SRuslan Bukin 14855d43fd68SRuslan Bukin device_set_desc(dev, "Gigabit Ethernet Controller"); 14865d43fd68SRuslan Bukin return (BUS_PROBE_DEFAULT); 14875d43fd68SRuslan Bukin } 14885d43fd68SRuslan Bukin 14895d43fd68SRuslan Bukin static int 14905d43fd68SRuslan Bukin dwc_attach(device_t dev) 14915d43fd68SRuslan Bukin { 14925d43fd68SRuslan Bukin uint8_t macaddr[ETHER_ADDR_LEN]; 14935d43fd68SRuslan Bukin struct dwc_softc *sc; 14945d43fd68SRuslan Bukin struct ifnet *ifp; 1495ff0752c8SLuiz Otavio O Souza int error, i; 1496ff0752c8SLuiz Otavio O Souza uint32_t reg; 1497824cfb47SOleksandr Tymoshenko char *phy_mode; 1498824cfb47SOleksandr Tymoshenko phandle_t node; 14995d43fd68SRuslan Bukin 15005d43fd68SRuslan Bukin sc = device_get_softc(dev); 15015d43fd68SRuslan Bukin sc->dev = dev; 15025d43fd68SRuslan Bukin sc->rx_idx = 0; 15035d43fd68SRuslan Bukin sc->txcount = TX_DESC_COUNT; 15045df53927SLuiz Otavio O Souza sc->mii_clk = IF_DWC_MII_CLK(dev); 15055df53927SLuiz Otavio O Souza sc->mactype = IF_DWC_MAC_TYPE(dev); 15065df53927SLuiz Otavio O Souza 1507824cfb47SOleksandr Tymoshenko node = ofw_bus_get_node(dev); 1508824cfb47SOleksandr Tymoshenko if (OF_getprop_alloc(node, "phy-mode", (void **)&phy_mode)) { 1509824cfb47SOleksandr Tymoshenko if (strcmp(phy_mode, "rgmii") == 0) 1510824cfb47SOleksandr Tymoshenko sc->phy_mode = PHY_MODE_RGMII; 1511824cfb47SOleksandr Tymoshenko if (strcmp(phy_mode, "rmii") == 0) 1512824cfb47SOleksandr Tymoshenko sc->phy_mode = PHY_MODE_RMII; 1513824cfb47SOleksandr Tymoshenko OF_prop_free(phy_mode); 1514824cfb47SOleksandr Tymoshenko } 1515824cfb47SOleksandr Tymoshenko 15165df53927SLuiz Otavio O Souza if (IF_DWC_INIT(dev) != 0) 15175df53927SLuiz Otavio O Souza return (ENXIO); 15185d43fd68SRuslan Bukin 15196a05f063SJared McNeill #ifdef EXT_RESOURCES 15206a05f063SJared McNeill if (dwc_clock_init(dev) != 0) 15216a05f063SJared McNeill return (ENXIO); 15226a05f063SJared McNeill #endif 15236a05f063SJared McNeill 15245d43fd68SRuslan Bukin if (bus_alloc_resources(dev, dwc_spec, sc->res)) { 15255d43fd68SRuslan Bukin device_printf(dev, "could not allocate resources\n"); 15265d43fd68SRuslan Bukin return (ENXIO); 15275d43fd68SRuslan Bukin } 15285d43fd68SRuslan Bukin 15295d43fd68SRuslan Bukin /* Read MAC before reset */ 1530f88e0af6SEmmanuel Vadot dwc_get_hwaddr(sc, macaddr); 15315d43fd68SRuslan Bukin 1532d7acb49aSJared McNeill /* Reset the PHY if needed */ 1533d7acb49aSJared McNeill if (dwc_reset(dev) != 0) { 1534d7acb49aSJared McNeill device_printf(dev, "Can't reset the PHY\n"); 1535d7acb49aSJared McNeill return (ENXIO); 1536d7acb49aSJared McNeill } 1537d7acb49aSJared McNeill 15385d43fd68SRuslan Bukin /* Reset */ 15395d43fd68SRuslan Bukin reg = READ4(sc, BUS_MODE); 15405d43fd68SRuslan Bukin reg |= (BUS_MODE_SWR); 15415d43fd68SRuslan Bukin WRITE4(sc, BUS_MODE, reg); 15425d43fd68SRuslan Bukin 1543d8e5258dSRuslan Bukin for (i = 0; i < MAC_RESET_TIMEOUT; i++) { 15445d43fd68SRuslan Bukin if ((READ4(sc, BUS_MODE) & BUS_MODE_SWR) == 0) 15455d43fd68SRuslan Bukin break; 15465d43fd68SRuslan Bukin DELAY(10); 15475d43fd68SRuslan Bukin } 1548d8e5258dSRuslan Bukin if (i >= MAC_RESET_TIMEOUT) { 15495d43fd68SRuslan Bukin device_printf(sc->dev, "Can't reset DWC.\n"); 15505d43fd68SRuslan Bukin return (ENXIO); 15515d43fd68SRuslan Bukin } 15525d43fd68SRuslan Bukin 1553188aee74SMichal Meloun if (sc->mactype != DWC_GMAC_EXT_DESC) { 15545df53927SLuiz Otavio O Souza reg = BUS_MODE_FIXEDBURST; 15555df53927SLuiz Otavio O Souza reg |= (BUS_MODE_PRIORXTX_41 << BUS_MODE_PRIORXTX_SHIFT); 15565df53927SLuiz Otavio O Souza } else 15575df53927SLuiz Otavio O Souza reg = (BUS_MODE_EIGHTXPBL); 15585d43fd68SRuslan Bukin reg |= (BUS_MODE_PBL_BEATS_8 << BUS_MODE_PBL_SHIFT); 15595d43fd68SRuslan Bukin WRITE4(sc, BUS_MODE, reg); 15605d43fd68SRuslan Bukin 15615d43fd68SRuslan Bukin /* 15625d43fd68SRuslan Bukin * DMA must be stop while changing descriptor list addresses. 15635d43fd68SRuslan Bukin */ 15645d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 15655d43fd68SRuslan Bukin reg &= ~(MODE_ST | MODE_SR); 15665d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 15675d43fd68SRuslan Bukin 15685d43fd68SRuslan Bukin if (setup_dma(sc)) 15695d43fd68SRuslan Bukin return (ENXIO); 15705d43fd68SRuslan Bukin 15715d43fd68SRuslan Bukin /* Setup addresses */ 15725d43fd68SRuslan Bukin WRITE4(sc, RX_DESCR_LIST_ADDR, sc->rxdesc_ring_paddr); 15735d43fd68SRuslan Bukin WRITE4(sc, TX_DESCR_LIST_ADDR, sc->txdesc_ring_paddr); 15745d43fd68SRuslan Bukin 1575d8e5258dSRuslan Bukin mtx_init(&sc->mtx, device_get_nameunit(sc->dev), 1576d8e5258dSRuslan Bukin MTX_NETWORK_LOCK, MTX_DEF); 1577d8e5258dSRuslan Bukin 1578d8e5258dSRuslan Bukin callout_init_mtx(&sc->dwc_callout, &sc->mtx, 0); 1579d8e5258dSRuslan Bukin 1580d8e5258dSRuslan Bukin /* Setup interrupt handler. */ 1581d8e5258dSRuslan Bukin error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE, 1582d8e5258dSRuslan Bukin NULL, dwc_intr, sc, &sc->intr_cookie); 1583d8e5258dSRuslan Bukin if (error != 0) { 1584d8e5258dSRuslan Bukin device_printf(dev, "could not setup interrupt handler.\n"); 1585d8e5258dSRuslan Bukin return (ENXIO); 1586d8e5258dSRuslan Bukin } 1587d8e5258dSRuslan Bukin 15885d43fd68SRuslan Bukin /* Set up the ethernet interface. */ 15895d43fd68SRuslan Bukin sc->ifp = ifp = if_alloc(IFT_ETHER); 15905d43fd68SRuslan Bukin 15915d43fd68SRuslan Bukin ifp->if_softc = sc; 15925d43fd68SRuslan Bukin if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 15933bbd11eeSEmmanuel Vadot if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); 15943bbd11eeSEmmanuel Vadot if_setstartfn(ifp, dwc_txstart); 15953bbd11eeSEmmanuel Vadot if_setioctlfn(ifp, dwc_ioctl); 15963bbd11eeSEmmanuel Vadot if_setinitfn(ifp, dwc_init); 15973bbd11eeSEmmanuel Vadot if_setsendqlen(ifp, TX_DESC_COUNT - 1); 15983bbd11eeSEmmanuel Vadot if_setsendqready(sc->ifp); 1599*98ea5a7bSEmmanuel Vadot if_sethwassist(sc->ifp, CSUM_IP | CSUM_UDP | CSUM_TCP); 1600*98ea5a7bSEmmanuel Vadot if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM); 16013bbd11eeSEmmanuel Vadot if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp)); 16025d43fd68SRuslan Bukin 16035d43fd68SRuslan Bukin /* Attach the mii driver. */ 16045d43fd68SRuslan Bukin error = mii_attach(dev, &sc->miibus, ifp, dwc_media_change, 16055d43fd68SRuslan Bukin dwc_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, 16065d43fd68SRuslan Bukin MII_OFFSET_ANY, 0); 16075d43fd68SRuslan Bukin 16085d43fd68SRuslan Bukin if (error != 0) { 16095d43fd68SRuslan Bukin device_printf(dev, "PHY attach failed\n"); 16105d43fd68SRuslan Bukin return (ENXIO); 16115d43fd68SRuslan Bukin } 16125d43fd68SRuslan Bukin sc->mii_softc = device_get_softc(sc->miibus); 16135d43fd68SRuslan Bukin 16145d43fd68SRuslan Bukin /* All ready to run, attach the ethernet interface. */ 16155d43fd68SRuslan Bukin ether_ifattach(ifp, macaddr); 16165d43fd68SRuslan Bukin sc->is_attached = true; 16175d43fd68SRuslan Bukin 16185d43fd68SRuslan Bukin return (0); 16195d43fd68SRuslan Bukin } 16205d43fd68SRuslan Bukin 16215d43fd68SRuslan Bukin static device_method_t dwc_methods[] = { 16225d43fd68SRuslan Bukin DEVMETHOD(device_probe, dwc_probe), 16235d43fd68SRuslan Bukin DEVMETHOD(device_attach, dwc_attach), 16245d43fd68SRuslan Bukin 16255d43fd68SRuslan Bukin /* MII Interface */ 16265d43fd68SRuslan Bukin DEVMETHOD(miibus_readreg, dwc_miibus_read_reg), 16275d43fd68SRuslan Bukin DEVMETHOD(miibus_writereg, dwc_miibus_write_reg), 16285d43fd68SRuslan Bukin DEVMETHOD(miibus_statchg, dwc_miibus_statchg), 16295d43fd68SRuslan Bukin 16305d43fd68SRuslan Bukin { 0, 0 } 16315d43fd68SRuslan Bukin }; 16325d43fd68SRuslan Bukin 16335df53927SLuiz Otavio O Souza driver_t dwc_driver = { 16345d43fd68SRuslan Bukin "dwc", 16355d43fd68SRuslan Bukin dwc_methods, 16365d43fd68SRuslan Bukin sizeof(struct dwc_softc), 16375d43fd68SRuslan Bukin }; 16385d43fd68SRuslan Bukin 16395d43fd68SRuslan Bukin static devclass_t dwc_devclass; 16405d43fd68SRuslan Bukin 16415d43fd68SRuslan Bukin DRIVER_MODULE(dwc, simplebus, dwc_driver, dwc_devclass, 0, 0); 16425d43fd68SRuslan Bukin DRIVER_MODULE(miibus, dwc, miibus_driver, miibus_devclass, 0, 0); 16435d43fd68SRuslan Bukin 16445d43fd68SRuslan Bukin MODULE_DEPEND(dwc, ether, 1, 1, 1); 16455d43fd68SRuslan Bukin MODULE_DEPEND(dwc, miibus, 1, 1, 1); 1646