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> 72f77d8d10SEmmanuel Vadot #include <dev/mii/mii_fdt.h> 73da9a326bSLuiz Otavio O Souza 746a05f063SJared McNeill #include <dev/extres/clk/clk.h> 756a05f063SJared McNeill #include <dev/extres/hwreset/hwreset.h> 766a05f063SJared McNeill 775df53927SLuiz Otavio O Souza #include "if_dwc_if.h" 78d7acb49aSJared McNeill #include "gpio_if.h" 795d43fd68SRuslan Bukin #include "miibus_if.h" 805d43fd68SRuslan Bukin 815d43fd68SRuslan Bukin #define READ4(_sc, _reg) \ 825d43fd68SRuslan Bukin bus_read_4((_sc)->res[0], _reg) 835d43fd68SRuslan Bukin #define WRITE4(_sc, _reg, _val) \ 845d43fd68SRuslan Bukin bus_write_4((_sc)->res[0], _reg, _val) 855d43fd68SRuslan Bukin 86d8e5258dSRuslan Bukin #define MAC_RESET_TIMEOUT 100 875d43fd68SRuslan Bukin #define WATCHDOG_TIMEOUT_SECS 5 885d43fd68SRuslan Bukin #define STATS_HARVEST_INTERVAL 2 895d43fd68SRuslan Bukin 905d43fd68SRuslan Bukin #define DWC_LOCK(sc) mtx_lock(&(sc)->mtx) 915d43fd68SRuslan Bukin #define DWC_UNLOCK(sc) mtx_unlock(&(sc)->mtx) 92ff0752c8SLuiz Otavio O Souza #define DWC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED) 93ff0752c8SLuiz Otavio O Souza #define DWC_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED) 945d43fd68SRuslan Bukin 958d43a868SMichal Meloun /* TX descriptors - TDESC0 is almost unified */ 968d43a868SMichal Meloun #define TDESC0_OWN (1U << 31) 978d43a868SMichal Meloun #define TDESC0_IHE (1U << 16) /* IP Header Error */ 988d43a868SMichal Meloun #define TDESC0_ES (1U << 15) /* Error Summary */ 998d43a868SMichal Meloun #define TDESC0_JT (1U << 14) /* Jabber Timeout */ 1008d43a868SMichal Meloun #define TDESC0_FF (1U << 13) /* Frame Flushed */ 1018d43a868SMichal Meloun #define TDESC0_PCE (1U << 12) /* Payload Checksum Error */ 1028d43a868SMichal Meloun #define TDESC0_LOC (1U << 11) /* Loss of Carrier */ 1038d43a868SMichal Meloun #define TDESC0_NC (1U << 10) /* No Carrier */ 1048d43a868SMichal Meloun #define TDESC0_LC (1U << 9) /* Late Collision */ 1058d43a868SMichal Meloun #define TDESC0_EC (1U << 8) /* Excessive Collision */ 1068d43a868SMichal Meloun #define TDESC0_VF (1U << 7) /* VLAN Frame */ 1078d43a868SMichal Meloun #define TDESC0_CC_MASK 0xf 1088d43a868SMichal Meloun #define TDESC0_CC_SHIFT 3 /* Collision Count */ 1098d43a868SMichal Meloun #define TDESC0_ED (1U << 2) /* Excessive Deferral */ 1108d43a868SMichal Meloun #define TDESC0_UF (1U << 1) /* Underflow Error */ 1118d43a868SMichal Meloun #define TDESC0_DB (1U << 0) /* Deferred Bit */ 1128d43a868SMichal Meloun /* TX descriptors - TDESC0 extended format only */ 1138d43a868SMichal Meloun #define ETDESC0_IC (1U << 30) /* Interrupt on Completion */ 1148d43a868SMichal Meloun #define ETDESC0_LS (1U << 29) /* Last Segment */ 1158d43a868SMichal Meloun #define ETDESC0_FS (1U << 28) /* First Segment */ 1168d43a868SMichal Meloun #define ETDESC0_DC (1U << 27) /* Disable CRC */ 1178d43a868SMichal Meloun #define ETDESC0_DP (1U << 26) /* Disable Padding */ 1188d43a868SMichal Meloun #define ETDESC0_CIC_NONE (0U << 22) /* Checksum Insertion Control */ 1198d43a868SMichal Meloun #define ETDESC0_CIC_HDR (1U << 22) 1208d43a868SMichal Meloun #define ETDESC0_CIC_SEG (2U << 22) 1218d43a868SMichal Meloun #define ETDESC0_CIC_FULL (3U << 22) 1228d43a868SMichal Meloun #define ETDESC0_TER (1U << 21) /* Transmit End of Ring */ 1238d43a868SMichal Meloun #define ETDESC0_TCH (1U << 20) /* Second Address Chained */ 1245d43fd68SRuslan Bukin 1258d43a868SMichal Meloun /* TX descriptors - TDESC1 normal format */ 1268d43a868SMichal Meloun #define NTDESC1_IC (1U << 31) /* Interrupt on Completion */ 1278d43a868SMichal Meloun #define NTDESC1_LS (1U << 30) /* Last Segment */ 1288d43a868SMichal Meloun #define NTDESC1_FS (1U << 29) /* First Segment */ 1298d43a868SMichal Meloun #define NTDESC1_CIC_NONE (0U << 27) /* Checksum Insertion Control */ 1308d43a868SMichal Meloun #define NTDESC1_CIC_HDR (1U << 27) 1318d43a868SMichal Meloun #define NTDESC1_CIC_SEG (2U << 27) 1328d43a868SMichal Meloun #define NTDESC1_CIC_FULL (3U << 27) 1338d43a868SMichal Meloun #define NTDESC1_DC (1U << 26) /* Disable CRC */ 1348d43a868SMichal Meloun #define NTDESC1_TER (1U << 25) /* Transmit End of Ring */ 1358d43a868SMichal Meloun #define NTDESC1_TCH (1U << 24) /* Second Address Chained */ 1368d43a868SMichal Meloun /* TX descriptors - TDESC1 extended format */ 1378d43a868SMichal Meloun #define ETDESC1_DP (1U << 23) /* Disable Padding */ 1388d43a868SMichal Meloun #define ETDESC1_TBS2_MASK 0x7ff 1398d43a868SMichal Meloun #define ETDESC1_TBS2_SHIFT 11 /* Receive Buffer 2 Size */ 1408d43a868SMichal Meloun #define ETDESC1_TBS1_MASK 0x7ff 1418d43a868SMichal Meloun #define ETDESC1_TBS1_SHIFT 0 /* Receive Buffer 1 Size */ 1425d43fd68SRuslan Bukin 1438d43a868SMichal Meloun /* RX descriptor - RDESC0 is unified */ 1448d43a868SMichal Meloun #define RDESC0_OWN (1U << 31) 1458d43a868SMichal Meloun #define RDESC0_AFM (1U << 30) /* Dest. Address Filter Fail */ 1468d43a868SMichal Meloun #define RDESC0_FL_MASK 0x3fff 1478d43a868SMichal Meloun #define RDESC0_FL_SHIFT 16 /* Frame Length */ 1488d43a868SMichal Meloun #define RDESC0_ES (1U << 15) /* Error Summary */ 1498d43a868SMichal Meloun #define RDESC0_DE (1U << 14) /* Descriptor Error */ 1508d43a868SMichal Meloun #define RDESC0_SAF (1U << 13) /* Source Address Filter Fail */ 1518d43a868SMichal Meloun #define RDESC0_LE (1U << 12) /* Length Error */ 1528d43a868SMichal Meloun #define RDESC0_OE (1U << 11) /* Overflow Error */ 1538d43a868SMichal Meloun #define RDESC0_VLAN (1U << 10) /* VLAN Tag */ 1548d43a868SMichal Meloun #define RDESC0_FS (1U << 9) /* First Descriptor */ 1558d43a868SMichal Meloun #define RDESC0_LS (1U << 8) /* Last Descriptor */ 1568d43a868SMichal Meloun #define RDESC0_ICE (1U << 7) /* IPC Checksum Error */ 1578d43a868SMichal Meloun #define RDESC0_LC (1U << 6) /* Late Collision */ 1588d43a868SMichal Meloun #define RDESC0_FT (1U << 5) /* Frame Type */ 1598d43a868SMichal Meloun #define RDESC0_RWT (1U << 4) /* Receive Watchdog Timeout */ 1608d43a868SMichal Meloun #define RDESC0_RE (1U << 3) /* Receive Error */ 1618d43a868SMichal Meloun #define RDESC0_DBE (1U << 2) /* Dribble Bit Error */ 1628d43a868SMichal Meloun #define RDESC0_CE (1U << 1) /* CRC Error */ 1638d43a868SMichal Meloun #define RDESC0_PCE (1U << 0) /* Payload Checksum Error */ 1648d43a868SMichal Meloun #define RDESC0_RXMA (1U << 0) /* Rx MAC Address */ 1655df53927SLuiz Otavio O Souza 1668d43a868SMichal Meloun /* RX descriptors - RDESC1 normal format */ 1678d43a868SMichal Meloun #define NRDESC1_DIC (1U << 31) /* Disable Intr on Completion */ 1688d43a868SMichal Meloun #define NRDESC1_RER (1U << 25) /* Receive End of Ring */ 1698d43a868SMichal Meloun #define NRDESC1_RCH (1U << 24) /* Second Address Chained */ 1708d43a868SMichal Meloun #define NRDESC1_RBS2_MASK 0x7ff 1718d43a868SMichal Meloun #define NRDESC1_RBS2_SHIFT 11 /* Receive Buffer 2 Size */ 1728d43a868SMichal Meloun #define NRDESC1_RBS1_MASK 0x7ff 1738d43a868SMichal Meloun #define NRDESC1_RBS1_SHIFT 0 /* Receive Buffer 1 Size */ 1748d43a868SMichal Meloun 1758d43a868SMichal Meloun /* RX descriptors - RDESC1 enhanced format */ 1768d43a868SMichal Meloun #define ERDESC1_DIC (1U << 31) /* Disable Intr on Completion */ 1778d43a868SMichal Meloun #define ERDESC1_RBS2_MASK 0x7ffff 1788d43a868SMichal Meloun #define ERDESC1_RBS2_SHIFT 16 /* Receive Buffer 2 Size */ 1798d43a868SMichal Meloun #define ERDESC1_RER (1U << 15) /* Receive End of Ring */ 1808d43a868SMichal Meloun #define ERDESC1_RCH (1U << 14) /* Second Address Chained */ 1818d43a868SMichal Meloun #define ERDESC1_RBS1_MASK 0x7ffff 1828d43a868SMichal Meloun #define ERDESC1_RBS1_SHIFT 0 /* Receive Buffer 1 Size */ 1835d43fd68SRuslan Bukin 1845d43fd68SRuslan Bukin /* 1855d43fd68SRuslan Bukin * A hardware buffer descriptor. Rx and Tx buffers have the same descriptor 1865df53927SLuiz Otavio O Souza * layout, but the bits in the fields have different meanings. 1875d43fd68SRuslan Bukin */ 1885d43fd68SRuslan Bukin struct dwc_hwdesc 1895d43fd68SRuslan Bukin { 1908d43a868SMichal Meloun uint32_t desc0; 1918d43a868SMichal Meloun uint32_t desc1; 1928d43a868SMichal Meloun uint32_t addr1; /* ptr to first buffer data */ 1938d43a868SMichal Meloun uint32_t addr2; /* ptr to next descriptor / second buffer data*/ 1945d43fd68SRuslan Bukin }; 1955d43fd68SRuslan Bukin 1962a35d391SEmmanuel Vadot 1972a35d391SEmmanuel Vadot struct dwc_hash_maddr_ctx { 1982a35d391SEmmanuel Vadot struct dwc_softc *sc; 1992a35d391SEmmanuel Vadot uint32_t hash[8]; 2002a35d391SEmmanuel Vadot }; 2012a35d391SEmmanuel Vadot 2025d43fd68SRuslan Bukin /* 2035d43fd68SRuslan Bukin * The hardware imposes alignment restrictions on various objects involved in 2045d43fd68SRuslan Bukin * DMA transfers. These values are expressed in bytes (not bits). 2055d43fd68SRuslan Bukin */ 2065d43fd68SRuslan Bukin #define DWC_DESC_RING_ALIGN 2048 2075d43fd68SRuslan Bukin 2085d43fd68SRuslan Bukin static struct resource_spec dwc_spec[] = { 2095d43fd68SRuslan Bukin { SYS_RES_MEMORY, 0, RF_ACTIVE }, 2105d43fd68SRuslan Bukin { SYS_RES_IRQ, 0, RF_ACTIVE }, 2115d43fd68SRuslan Bukin { -1, 0 } 2125d43fd68SRuslan Bukin }; 2135d43fd68SRuslan Bukin 2145d43fd68SRuslan Bukin static void dwc_txfinish_locked(struct dwc_softc *sc); 2155d43fd68SRuslan Bukin static void dwc_rxfinish_locked(struct dwc_softc *sc); 2165d43fd68SRuslan Bukin static void dwc_stop_locked(struct dwc_softc *sc); 2175d43fd68SRuslan Bukin static void dwc_setup_rxfilter(struct dwc_softc *sc); 218158ce7baSEmmanuel Vadot static void dwc_setup_core(struct dwc_softc *sc); 219f368f4b1SEmmanuel Vadot static void dwc_enable_mac(struct dwc_softc *sc, bool enable); 220158ce7baSEmmanuel Vadot static void dwc_init_dma(struct dwc_softc *sc); 221afd0c3c2SEmmanuel Vadot static void dwc_stop_dma(struct dwc_softc *sc); 2225d43fd68SRuslan Bukin 2232a35d391SEmmanuel Vadot static void dwc_tick(void *arg); 2242a35d391SEmmanuel Vadot 2252b4a66edSEmmanuel Vadot /* Pause time field in the transmitted control frame */ 2262b4a66edSEmmanuel Vadot static int dwc_pause_time = 0xffff; 2272b4a66edSEmmanuel Vadot TUNABLE_INT("hw.dwc.pause_time", &dwc_pause_time); 2282b4a66edSEmmanuel Vadot 2292a35d391SEmmanuel Vadot /* 2302a35d391SEmmanuel Vadot * MIIBUS functions 2312a35d391SEmmanuel Vadot */ 2322a35d391SEmmanuel Vadot 2332a35d391SEmmanuel Vadot static int 2342a35d391SEmmanuel Vadot dwc_miibus_read_reg(device_t dev, int phy, int reg) 2352a35d391SEmmanuel Vadot { 2362a35d391SEmmanuel Vadot struct dwc_softc *sc; 2372a35d391SEmmanuel Vadot uint16_t mii; 2382a35d391SEmmanuel Vadot size_t cnt; 2392a35d391SEmmanuel Vadot int rv = 0; 2402a35d391SEmmanuel Vadot 2412a35d391SEmmanuel Vadot sc = device_get_softc(dev); 2422a35d391SEmmanuel Vadot 2432a35d391SEmmanuel Vadot mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) 2442a35d391SEmmanuel Vadot | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) 2452a35d391SEmmanuel Vadot | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) 2462a35d391SEmmanuel Vadot | GMII_ADDRESS_GB; /* Busy flag */ 2472a35d391SEmmanuel Vadot 2482a35d391SEmmanuel Vadot WRITE4(sc, GMII_ADDRESS, mii); 2492a35d391SEmmanuel Vadot 2502a35d391SEmmanuel Vadot for (cnt = 0; cnt < 1000; cnt++) { 2512a35d391SEmmanuel Vadot if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { 2522a35d391SEmmanuel Vadot rv = READ4(sc, GMII_DATA); 2532a35d391SEmmanuel Vadot break; 2542a35d391SEmmanuel Vadot } 2552a35d391SEmmanuel Vadot DELAY(10); 2562a35d391SEmmanuel Vadot } 2572a35d391SEmmanuel Vadot 2582a35d391SEmmanuel Vadot return rv; 2592a35d391SEmmanuel Vadot } 2602a35d391SEmmanuel Vadot 2612a35d391SEmmanuel Vadot static int 2622a35d391SEmmanuel Vadot dwc_miibus_write_reg(device_t dev, int phy, int reg, int val) 2632a35d391SEmmanuel Vadot { 2642a35d391SEmmanuel Vadot struct dwc_softc *sc; 2652a35d391SEmmanuel Vadot uint16_t mii; 2662a35d391SEmmanuel Vadot size_t cnt; 2672a35d391SEmmanuel Vadot 2682a35d391SEmmanuel Vadot sc = device_get_softc(dev); 2692a35d391SEmmanuel Vadot 2702a35d391SEmmanuel Vadot mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) 2712a35d391SEmmanuel Vadot | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) 2722a35d391SEmmanuel Vadot | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) 2732a35d391SEmmanuel Vadot | GMII_ADDRESS_GB | GMII_ADDRESS_GW; 2742a35d391SEmmanuel Vadot 2752a35d391SEmmanuel Vadot WRITE4(sc, GMII_DATA, val); 2762a35d391SEmmanuel Vadot WRITE4(sc, GMII_ADDRESS, mii); 2772a35d391SEmmanuel Vadot 2782a35d391SEmmanuel Vadot for (cnt = 0; cnt < 1000; cnt++) { 2792a35d391SEmmanuel Vadot if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { 2802a35d391SEmmanuel Vadot break; 2812a35d391SEmmanuel Vadot } 2822a35d391SEmmanuel Vadot DELAY(10); 2832a35d391SEmmanuel Vadot } 2842a35d391SEmmanuel Vadot 2852a35d391SEmmanuel Vadot return (0); 2862a35d391SEmmanuel Vadot } 2872a35d391SEmmanuel Vadot 2882a35d391SEmmanuel Vadot static void 2892a35d391SEmmanuel Vadot dwc_miibus_statchg(device_t dev) 2902a35d391SEmmanuel Vadot { 2912a35d391SEmmanuel Vadot struct dwc_softc *sc; 2922a35d391SEmmanuel Vadot struct mii_data *mii; 2932a35d391SEmmanuel Vadot uint32_t reg; 2942a35d391SEmmanuel Vadot 2952a35d391SEmmanuel Vadot /* 2962a35d391SEmmanuel Vadot * Called by the MII bus driver when the PHY establishes 2972a35d391SEmmanuel Vadot * link to set the MAC interface registers. 2982a35d391SEmmanuel Vadot */ 2992a35d391SEmmanuel Vadot 3002a35d391SEmmanuel Vadot sc = device_get_softc(dev); 3012a35d391SEmmanuel Vadot 3022a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 3032a35d391SEmmanuel Vadot 3042a35d391SEmmanuel Vadot mii = sc->mii_softc; 3052a35d391SEmmanuel Vadot 3062a35d391SEmmanuel Vadot if (mii->mii_media_status & IFM_ACTIVE) 3072a35d391SEmmanuel Vadot sc->link_is_up = true; 3082a35d391SEmmanuel Vadot else 3092a35d391SEmmanuel Vadot sc->link_is_up = false; 3102a35d391SEmmanuel Vadot 3112a35d391SEmmanuel Vadot reg = READ4(sc, MAC_CONFIGURATION); 3122a35d391SEmmanuel Vadot switch (IFM_SUBTYPE(mii->mii_media_active)) { 3132a35d391SEmmanuel Vadot case IFM_1000_T: 3142a35d391SEmmanuel Vadot case IFM_1000_SX: 3152a35d391SEmmanuel Vadot reg &= ~(CONF_FES | CONF_PS); 3162a35d391SEmmanuel Vadot break; 3172a35d391SEmmanuel Vadot case IFM_100_TX: 3182a35d391SEmmanuel Vadot reg |= (CONF_FES | CONF_PS); 3192a35d391SEmmanuel Vadot break; 3202a35d391SEmmanuel Vadot case IFM_10_T: 3212a35d391SEmmanuel Vadot reg &= ~(CONF_FES); 3222a35d391SEmmanuel Vadot reg |= (CONF_PS); 3232a35d391SEmmanuel Vadot break; 3242a35d391SEmmanuel Vadot case IFM_NONE: 3252a35d391SEmmanuel Vadot sc->link_is_up = false; 3262a35d391SEmmanuel Vadot return; 3272a35d391SEmmanuel Vadot default: 3282a35d391SEmmanuel Vadot sc->link_is_up = false; 3292a35d391SEmmanuel Vadot device_printf(dev, "Unsupported media %u\n", 3302a35d391SEmmanuel Vadot IFM_SUBTYPE(mii->mii_media_active)); 3312a35d391SEmmanuel Vadot return; 3322a35d391SEmmanuel Vadot } 3332a35d391SEmmanuel Vadot if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 3342a35d391SEmmanuel Vadot reg |= (CONF_DM); 3352a35d391SEmmanuel Vadot else 3362a35d391SEmmanuel Vadot reg &= ~(CONF_DM); 3372a35d391SEmmanuel Vadot WRITE4(sc, MAC_CONFIGURATION, reg); 3382a35d391SEmmanuel Vadot 3392b4a66edSEmmanuel Vadot reg = FLOW_CONTROL_UP; 3402b4a66edSEmmanuel Vadot if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) 3412b4a66edSEmmanuel Vadot reg |= FLOW_CONTROL_TX; 3422b4a66edSEmmanuel Vadot if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) 3432b4a66edSEmmanuel Vadot reg |= FLOW_CONTROL_RX; 3442b4a66edSEmmanuel Vadot if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 3452b4a66edSEmmanuel Vadot reg |= dwc_pause_time << FLOW_CONTROL_PT_SHIFT; 3462b4a66edSEmmanuel Vadot WRITE4(sc, FLOW_CONTROL, reg); 3472b4a66edSEmmanuel Vadot 3482a35d391SEmmanuel Vadot IF_DWC_SET_SPEED(dev, IFM_SUBTYPE(mii->mii_media_active)); 3492a35d391SEmmanuel Vadot 3502a35d391SEmmanuel Vadot } 3512a35d391SEmmanuel Vadot 3522a35d391SEmmanuel Vadot /* 3532a35d391SEmmanuel Vadot * Media functions 3542a35d391SEmmanuel Vadot */ 3552a35d391SEmmanuel Vadot 3562a35d391SEmmanuel Vadot static void 357ca018790SMitchell Horne dwc_media_status(if_t ifp, struct ifmediareq *ifmr) 3582a35d391SEmmanuel Vadot { 3592a35d391SEmmanuel Vadot struct dwc_softc *sc; 3602a35d391SEmmanuel Vadot struct mii_data *mii; 3612a35d391SEmmanuel Vadot 362ca018790SMitchell Horne sc = if_getsoftc(ifp); 3632a35d391SEmmanuel Vadot mii = sc->mii_softc; 3642a35d391SEmmanuel Vadot DWC_LOCK(sc); 3652a35d391SEmmanuel Vadot mii_pollstat(mii); 3662a35d391SEmmanuel Vadot ifmr->ifm_active = mii->mii_media_active; 3672a35d391SEmmanuel Vadot ifmr->ifm_status = mii->mii_media_status; 3682a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 3692a35d391SEmmanuel Vadot } 3702a35d391SEmmanuel Vadot 3712a35d391SEmmanuel Vadot static int 3722a35d391SEmmanuel Vadot dwc_media_change_locked(struct dwc_softc *sc) 3732a35d391SEmmanuel Vadot { 3742a35d391SEmmanuel Vadot 3752a35d391SEmmanuel Vadot return (mii_mediachg(sc->mii_softc)); 3762a35d391SEmmanuel Vadot } 3772a35d391SEmmanuel Vadot 3782a35d391SEmmanuel Vadot static int 379ca018790SMitchell Horne dwc_media_change(if_t ifp) 3802a35d391SEmmanuel Vadot { 3812a35d391SEmmanuel Vadot struct dwc_softc *sc; 3822a35d391SEmmanuel Vadot int error; 3832a35d391SEmmanuel Vadot 384ca018790SMitchell Horne sc = if_getsoftc(ifp); 3852a35d391SEmmanuel Vadot 3862a35d391SEmmanuel Vadot DWC_LOCK(sc); 3872a35d391SEmmanuel Vadot error = dwc_media_change_locked(sc); 3882a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 3892a35d391SEmmanuel Vadot return (error); 3902a35d391SEmmanuel Vadot } 3912a35d391SEmmanuel Vadot 3922a35d391SEmmanuel Vadot /* 3932a35d391SEmmanuel Vadot * Core functions 3942a35d391SEmmanuel Vadot */ 3952a35d391SEmmanuel Vadot 3962a35d391SEmmanuel Vadot static const uint8_t nibbletab[] = { 3972a35d391SEmmanuel Vadot /* 0x0 0000 -> 0000 */ 0x0, 3982a35d391SEmmanuel Vadot /* 0x1 0001 -> 1000 */ 0x8, 3992a35d391SEmmanuel Vadot /* 0x2 0010 -> 0100 */ 0x4, 4002a35d391SEmmanuel Vadot /* 0x3 0011 -> 1100 */ 0xc, 4012a35d391SEmmanuel Vadot /* 0x4 0100 -> 0010 */ 0x2, 4022a35d391SEmmanuel Vadot /* 0x5 0101 -> 1010 */ 0xa, 4032a35d391SEmmanuel Vadot /* 0x6 0110 -> 0110 */ 0x6, 4042a35d391SEmmanuel Vadot /* 0x7 0111 -> 1110 */ 0xe, 4052a35d391SEmmanuel Vadot /* 0x8 1000 -> 0001 */ 0x1, 4062a35d391SEmmanuel Vadot /* 0x9 1001 -> 1001 */ 0x9, 4072a35d391SEmmanuel Vadot /* 0xa 1010 -> 0101 */ 0x5, 4082a35d391SEmmanuel Vadot /* 0xb 1011 -> 1101 */ 0xd, 4092a35d391SEmmanuel Vadot /* 0xc 1100 -> 0011 */ 0x3, 4102a35d391SEmmanuel Vadot /* 0xd 1101 -> 1011 */ 0xb, 4112a35d391SEmmanuel Vadot /* 0xe 1110 -> 0111 */ 0x7, 4122a35d391SEmmanuel Vadot /* 0xf 1111 -> 1111 */ 0xf, }; 4132a35d391SEmmanuel Vadot 4142a35d391SEmmanuel Vadot static uint8_t 4152a35d391SEmmanuel Vadot bitreverse(uint8_t x) 4162a35d391SEmmanuel Vadot { 4172a35d391SEmmanuel Vadot 4182a35d391SEmmanuel Vadot return (nibbletab[x & 0xf] << 4) | nibbletab[x >> 4]; 4192a35d391SEmmanuel Vadot } 4202a35d391SEmmanuel Vadot 4212a35d391SEmmanuel Vadot static u_int 4222a35d391SEmmanuel Vadot dwc_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) 4232a35d391SEmmanuel Vadot { 4242a35d391SEmmanuel Vadot struct dwc_hash_maddr_ctx *ctx = arg; 4252a35d391SEmmanuel Vadot uint32_t crc, hashbit, hashreg; 4262a35d391SEmmanuel Vadot uint8_t val; 4272a35d391SEmmanuel Vadot 4282a35d391SEmmanuel Vadot crc = ether_crc32_le(LLADDR(sdl), ETHER_ADDR_LEN); 4292a35d391SEmmanuel Vadot /* Take lower 8 bits and reverse it */ 4302a35d391SEmmanuel Vadot val = bitreverse(~crc & 0xff); 4312a35d391SEmmanuel Vadot if (ctx->sc->mactype != DWC_GMAC_EXT_DESC) 4322a35d391SEmmanuel Vadot val >>= 2; /* Only need lower 6 bits */ 4332a35d391SEmmanuel Vadot hashreg = (val >> 5); 4342a35d391SEmmanuel Vadot hashbit = (val & 31); 4352a35d391SEmmanuel Vadot ctx->hash[hashreg] |= (1 << hashbit); 4362a35d391SEmmanuel Vadot 4372a35d391SEmmanuel Vadot return (1); 4382a35d391SEmmanuel Vadot } 4392a35d391SEmmanuel Vadot 4402a35d391SEmmanuel Vadot static void 4412a35d391SEmmanuel Vadot dwc_setup_rxfilter(struct dwc_softc *sc) 4422a35d391SEmmanuel Vadot { 4432a35d391SEmmanuel Vadot struct dwc_hash_maddr_ctx ctx; 444ca018790SMitchell Horne if_t ifp; 4452a35d391SEmmanuel Vadot uint8_t *eaddr; 4462a35d391SEmmanuel Vadot uint32_t ffval, hi, lo; 4472a35d391SEmmanuel Vadot int nhash, i; 4482a35d391SEmmanuel Vadot 4492a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 4502a35d391SEmmanuel Vadot 4512a35d391SEmmanuel Vadot ifp = sc->ifp; 4522a35d391SEmmanuel Vadot nhash = sc->mactype != DWC_GMAC_EXT_DESC ? 2 : 8; 4532a35d391SEmmanuel Vadot 4542a35d391SEmmanuel Vadot /* 4552a35d391SEmmanuel Vadot * Set the multicast (group) filter hash. 4562a35d391SEmmanuel Vadot */ 457ca018790SMitchell Horne if ((if_getflags(ifp) & IFF_ALLMULTI) != 0) { 4582a35d391SEmmanuel Vadot ffval = (FRAME_FILTER_PM); 4592a35d391SEmmanuel Vadot for (i = 0; i < nhash; i++) 4602a35d391SEmmanuel Vadot ctx.hash[i] = ~0; 4612a35d391SEmmanuel Vadot } else { 4622a35d391SEmmanuel Vadot ffval = (FRAME_FILTER_HMC); 4632a35d391SEmmanuel Vadot for (i = 0; i < nhash; i++) 4642a35d391SEmmanuel Vadot ctx.hash[i] = 0; 4652a35d391SEmmanuel Vadot ctx.sc = sc; 4662a35d391SEmmanuel Vadot if_foreach_llmaddr(ifp, dwc_hash_maddr, &ctx); 4672a35d391SEmmanuel Vadot } 4682a35d391SEmmanuel Vadot 4692a35d391SEmmanuel Vadot /* 4702a35d391SEmmanuel Vadot * Set the individual address filter hash. 4712a35d391SEmmanuel Vadot */ 472ca018790SMitchell Horne if ((if_getflags(ifp) & IFF_PROMISC) != 0) 4732a35d391SEmmanuel Vadot ffval |= (FRAME_FILTER_PR); 4742a35d391SEmmanuel Vadot 4752a35d391SEmmanuel Vadot /* 4762a35d391SEmmanuel Vadot * Set the primary address. 4772a35d391SEmmanuel Vadot */ 4782a35d391SEmmanuel Vadot eaddr = IF_LLADDR(ifp); 4792a35d391SEmmanuel Vadot lo = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) | 4802a35d391SEmmanuel Vadot (eaddr[3] << 24); 4812a35d391SEmmanuel Vadot hi = eaddr[4] | (eaddr[5] << 8); 4822a35d391SEmmanuel Vadot WRITE4(sc, MAC_ADDRESS_LOW(0), lo); 4832a35d391SEmmanuel Vadot WRITE4(sc, MAC_ADDRESS_HIGH(0), hi); 4842a35d391SEmmanuel Vadot WRITE4(sc, MAC_FRAME_FILTER, ffval); 4852a35d391SEmmanuel Vadot if (sc->mactype != DWC_GMAC_EXT_DESC) { 4862a35d391SEmmanuel Vadot WRITE4(sc, GMAC_MAC_HTLOW, ctx.hash[0]); 4872a35d391SEmmanuel Vadot WRITE4(sc, GMAC_MAC_HTHIGH, ctx.hash[1]); 4882a35d391SEmmanuel Vadot } else { 4892a35d391SEmmanuel Vadot for (i = 0; i < nhash; i++) 4902a35d391SEmmanuel Vadot WRITE4(sc, HASH_TABLE_REG(i), ctx.hash[i]); 4912a35d391SEmmanuel Vadot } 4922a35d391SEmmanuel Vadot } 4932a35d391SEmmanuel Vadot 4942a35d391SEmmanuel Vadot static void 4952a35d391SEmmanuel Vadot dwc_setup_core(struct dwc_softc *sc) 4962a35d391SEmmanuel Vadot { 4972a35d391SEmmanuel Vadot uint32_t reg; 4982a35d391SEmmanuel Vadot 4992a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 5002a35d391SEmmanuel Vadot 5012a35d391SEmmanuel Vadot /* Enable core */ 5022a35d391SEmmanuel Vadot reg = READ4(sc, MAC_CONFIGURATION); 5032a35d391SEmmanuel Vadot reg |= (CONF_JD | CONF_ACS | CONF_BE); 5042a35d391SEmmanuel Vadot WRITE4(sc, MAC_CONFIGURATION, reg); 5052a35d391SEmmanuel Vadot } 5062a35d391SEmmanuel Vadot 5072a35d391SEmmanuel Vadot static void 5082a35d391SEmmanuel Vadot dwc_enable_mac(struct dwc_softc *sc, bool enable) 5092a35d391SEmmanuel Vadot { 5102a35d391SEmmanuel Vadot uint32_t reg; 5112a35d391SEmmanuel Vadot 5122a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 5132a35d391SEmmanuel Vadot reg = READ4(sc, MAC_CONFIGURATION); 5142a35d391SEmmanuel Vadot if (enable) 5152a35d391SEmmanuel Vadot reg |= CONF_TE | CONF_RE; 5162a35d391SEmmanuel Vadot else 5172a35d391SEmmanuel Vadot reg &= ~(CONF_TE | CONF_RE); 5182a35d391SEmmanuel Vadot WRITE4(sc, MAC_CONFIGURATION, reg); 5192a35d391SEmmanuel Vadot } 5202a35d391SEmmanuel Vadot 5212a35d391SEmmanuel Vadot static void 522*35c9edabSJiahao Li dwc_enable_csum_offload(struct dwc_softc *sc) 523*35c9edabSJiahao Li { 524*35c9edabSJiahao Li uint32_t reg; 525*35c9edabSJiahao Li 526*35c9edabSJiahao Li DWC_ASSERT_LOCKED(sc); 527*35c9edabSJiahao Li reg = READ4(sc, MAC_CONFIGURATION); 528*35c9edabSJiahao Li if ((if_getcapenable(sc->ifp) & IFCAP_RXCSUM) != 0) 529*35c9edabSJiahao Li reg |= CONF_IPC; 530*35c9edabSJiahao Li else 531*35c9edabSJiahao Li reg &= ~CONF_IPC; 532*35c9edabSJiahao Li WRITE4(sc, MAC_CONFIGURATION, reg); 533*35c9edabSJiahao Li } 534*35c9edabSJiahao Li 535*35c9edabSJiahao Li static void 5362a35d391SEmmanuel Vadot dwc_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr) 5372a35d391SEmmanuel Vadot { 5382a35d391SEmmanuel Vadot uint32_t hi, lo, rnd; 5392a35d391SEmmanuel Vadot 5402a35d391SEmmanuel Vadot /* 5412a35d391SEmmanuel Vadot * Try to recover a MAC address from the running hardware. If there's 5422a35d391SEmmanuel Vadot * something non-zero there, assume the bootloader did the right thing 5432a35d391SEmmanuel Vadot * and just use it. 5442a35d391SEmmanuel Vadot * 5452a35d391SEmmanuel Vadot * Otherwise, set the address to a convenient locally assigned address, 5462a35d391SEmmanuel Vadot * 'bsd' + random 24 low-order bits. 'b' is 0x62, which has the locally 5472a35d391SEmmanuel Vadot * assigned bit set, and the broadcast/multicast bit clear. 5482a35d391SEmmanuel Vadot */ 5492a35d391SEmmanuel Vadot lo = READ4(sc, MAC_ADDRESS_LOW(0)); 5502a35d391SEmmanuel Vadot hi = READ4(sc, MAC_ADDRESS_HIGH(0)) & 0xffff; 5512a35d391SEmmanuel Vadot if ((lo != 0xffffffff) || (hi != 0xffff)) { 5522a35d391SEmmanuel Vadot hwaddr[0] = (lo >> 0) & 0xff; 5532a35d391SEmmanuel Vadot hwaddr[1] = (lo >> 8) & 0xff; 5542a35d391SEmmanuel Vadot hwaddr[2] = (lo >> 16) & 0xff; 5552a35d391SEmmanuel Vadot hwaddr[3] = (lo >> 24) & 0xff; 5562a35d391SEmmanuel Vadot hwaddr[4] = (hi >> 0) & 0xff; 5572a35d391SEmmanuel Vadot hwaddr[5] = (hi >> 8) & 0xff; 5582a35d391SEmmanuel Vadot } else { 5592a35d391SEmmanuel Vadot rnd = arc4random() & 0x00ffffff; 5602a35d391SEmmanuel Vadot hwaddr[0] = 'b'; 5612a35d391SEmmanuel Vadot hwaddr[1] = 's'; 5622a35d391SEmmanuel Vadot hwaddr[2] = 'd'; 5632a35d391SEmmanuel Vadot hwaddr[3] = rnd >> 16; 5642a35d391SEmmanuel Vadot hwaddr[4] = rnd >> 8; 5652a35d391SEmmanuel Vadot hwaddr[5] = rnd >> 0; 5662a35d391SEmmanuel Vadot } 5672a35d391SEmmanuel Vadot } 5682a35d391SEmmanuel Vadot 5692a35d391SEmmanuel Vadot /* 5702a35d391SEmmanuel Vadot * DMA functions 5712a35d391SEmmanuel Vadot */ 5722a35d391SEmmanuel Vadot 5732a35d391SEmmanuel Vadot static void 5742a35d391SEmmanuel Vadot dwc_init_dma(struct dwc_softc *sc) 5752a35d391SEmmanuel Vadot { 5762a35d391SEmmanuel Vadot uint32_t reg; 5772a35d391SEmmanuel Vadot 5782a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 5792a35d391SEmmanuel Vadot 5802a35d391SEmmanuel Vadot /* Initializa DMA and enable transmitters */ 5812a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 5822a35d391SEmmanuel Vadot reg |= (MODE_TSF | MODE_OSF | MODE_FUF); 5832a35d391SEmmanuel Vadot reg &= ~(MODE_RSF); 5842a35d391SEmmanuel Vadot reg |= (MODE_RTC_LEV32 << MODE_RTC_SHIFT); 5852a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 5862a35d391SEmmanuel Vadot 5872a35d391SEmmanuel Vadot WRITE4(sc, INTERRUPT_ENABLE, INT_EN_DEFAULT); 5882a35d391SEmmanuel Vadot 5892a35d391SEmmanuel Vadot /* Start DMA */ 5902a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 5912a35d391SEmmanuel Vadot reg |= (MODE_ST | MODE_SR); 5922a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 5932a35d391SEmmanuel Vadot } 5942a35d391SEmmanuel Vadot 5952a35d391SEmmanuel Vadot static void 5962a35d391SEmmanuel Vadot dwc_stop_dma(struct dwc_softc *sc) 5972a35d391SEmmanuel Vadot { 5982a35d391SEmmanuel Vadot uint32_t reg; 5992a35d391SEmmanuel Vadot 6002a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 6012a35d391SEmmanuel Vadot 6022a35d391SEmmanuel Vadot /* Stop DMA TX */ 6032a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 6042a35d391SEmmanuel Vadot reg &= ~(MODE_ST); 6052a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 6062a35d391SEmmanuel Vadot 6072a35d391SEmmanuel Vadot /* Flush TX */ 6082a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 6092a35d391SEmmanuel Vadot reg |= (MODE_FTF); 6102a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 6112a35d391SEmmanuel Vadot 6122a35d391SEmmanuel Vadot /* Stop DMA RX */ 6132a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 6142a35d391SEmmanuel Vadot reg &= ~(MODE_SR); 6152a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 6162a35d391SEmmanuel Vadot } 6172a35d391SEmmanuel Vadot 6185d43fd68SRuslan Bukin static inline uint32_t 6195d43fd68SRuslan Bukin next_rxidx(struct dwc_softc *sc, uint32_t curidx) 6205d43fd68SRuslan Bukin { 6215d43fd68SRuslan Bukin 6225d43fd68SRuslan Bukin return ((curidx + 1) % RX_DESC_COUNT); 6235d43fd68SRuslan Bukin } 6245d43fd68SRuslan Bukin 6255d43fd68SRuslan Bukin static inline uint32_t 6265d43fd68SRuslan Bukin next_txidx(struct dwc_softc *sc, uint32_t curidx) 6275d43fd68SRuslan Bukin { 6285d43fd68SRuslan Bukin 6295d43fd68SRuslan Bukin return ((curidx + 1) % TX_DESC_COUNT); 6305d43fd68SRuslan Bukin } 6315d43fd68SRuslan Bukin 6325d43fd68SRuslan Bukin static void 6335d43fd68SRuslan Bukin dwc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 6345d43fd68SRuslan Bukin { 6355d43fd68SRuslan Bukin 6365d43fd68SRuslan Bukin if (error != 0) 6375d43fd68SRuslan Bukin return; 6385d43fd68SRuslan Bukin *(bus_addr_t *)arg = segs[0].ds_addr; 6395d43fd68SRuslan Bukin } 6405d43fd68SRuslan Bukin 641b72e2878SMichal Meloun inline static void 6425d43fd68SRuslan Bukin dwc_setup_txdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr, 643e5232621SOleksandr Tymoshenko uint32_t len, uint32_t flags, bool first, bool last) 6445d43fd68SRuslan Bukin { 645b72e2878SMichal Meloun uint32_t desc0, desc1; 6465d43fd68SRuslan Bukin 6475d43fd68SRuslan Bukin /* Addr/len 0 means we're clearing the descriptor after xmit done. */ 6485d43fd68SRuslan Bukin if (paddr == 0 || len == 0) { 649b72e2878SMichal Meloun desc0 = 0; 650b72e2878SMichal Meloun desc1 = 0; 651e5232621SOleksandr Tymoshenko --sc->tx_desccount; 6525d43fd68SRuslan Bukin } else { 653b72e2878SMichal Meloun if (sc->mactype != DWC_GMAC_EXT_DESC) { 654b72e2878SMichal Meloun desc0 = 0; 655e5232621SOleksandr Tymoshenko desc1 = NTDESC1_TCH | len | flags; 656e5232621SOleksandr Tymoshenko if (first) 657e5232621SOleksandr Tymoshenko desc1 |= NTDESC1_FS; 658e5232621SOleksandr Tymoshenko if (last) 659e5232621SOleksandr Tymoshenko desc1 |= NTDESC1_LS | NTDESC1_IC; 660b72e2878SMichal Meloun } else { 661e5232621SOleksandr Tymoshenko desc0 = ETDESC0_TCH | flags; 662e5232621SOleksandr Tymoshenko if (first) 663e5232621SOleksandr Tymoshenko desc0 |= ETDESC0_FS; 664e5232621SOleksandr Tymoshenko if (last) 665e5232621SOleksandr Tymoshenko desc0 |= ETDESC0_LS | ETDESC0_IC; 666b72e2878SMichal Meloun desc1 = len; 667b72e2878SMichal Meloun } 668e5232621SOleksandr Tymoshenko ++sc->tx_desccount; 6695d43fd68SRuslan Bukin } 6705d43fd68SRuslan Bukin 6718d43a868SMichal Meloun sc->txdesc_ring[idx].addr1 = (uint32_t)(paddr); 672b72e2878SMichal Meloun sc->txdesc_ring[idx].desc0 = desc0; 673b72e2878SMichal Meloun sc->txdesc_ring[idx].desc1 = desc1; 674e5232621SOleksandr Tymoshenko } 6755d43fd68SRuslan Bukin 676e5232621SOleksandr Tymoshenko inline static void 677e5232621SOleksandr Tymoshenko dwc_set_owner(struct dwc_softc *sc, int idx) 678e5232621SOleksandr Tymoshenko { 6795d43fd68SRuslan Bukin wmb(); 6808d43a868SMichal Meloun sc->txdesc_ring[idx].desc0 |= TDESC0_OWN; 6815d43fd68SRuslan Bukin wmb(); 6825d43fd68SRuslan Bukin } 6835d43fd68SRuslan Bukin 6845d43fd68SRuslan Bukin static int 6855d43fd68SRuslan Bukin dwc_setup_txbuf(struct dwc_softc *sc, int idx, struct mbuf **mp) 6865d43fd68SRuslan Bukin { 687e5232621SOleksandr Tymoshenko struct bus_dma_segment segs[TX_MAP_MAX_SEGS]; 6885d43fd68SRuslan Bukin int error, nsegs; 6895d43fd68SRuslan Bukin struct mbuf * m; 69098ea5a7bSEmmanuel Vadot uint32_t flags = 0; 691e5232621SOleksandr Tymoshenko int i; 692e5232621SOleksandr Tymoshenko int first, last; 6935d43fd68SRuslan Bukin 694e5232621SOleksandr Tymoshenko error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, 695e5232621SOleksandr Tymoshenko *mp, segs, &nsegs, 0); 696e5232621SOleksandr Tymoshenko if (error == EFBIG) { 697e5232621SOleksandr Tymoshenko /* 698e5232621SOleksandr Tymoshenko * The map may be partially mapped from the first call. 699e5232621SOleksandr Tymoshenko * Make sure to reset it. 700e5232621SOleksandr Tymoshenko */ 701e5232621SOleksandr Tymoshenko bus_dmamap_unload(sc->txbuf_tag, sc->txbuf_map[idx].map); 7025d43fd68SRuslan Bukin if ((m = m_defrag(*mp, M_NOWAIT)) == NULL) 7035d43fd68SRuslan Bukin return (ENOMEM); 7045d43fd68SRuslan Bukin *mp = m; 7055d43fd68SRuslan Bukin error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, 706e5232621SOleksandr Tymoshenko *mp, segs, &nsegs, 0); 707e5232621SOleksandr Tymoshenko } 708e5232621SOleksandr Tymoshenko if (error != 0) 709e5232621SOleksandr Tymoshenko return (ENOMEM); 710e5232621SOleksandr Tymoshenko 711e5232621SOleksandr Tymoshenko if (sc->tx_desccount + nsegs > TX_DESC_COUNT) { 712e5232621SOleksandr Tymoshenko bus_dmamap_unload(sc->txbuf_tag, sc->txbuf_map[idx].map); 7135d43fd68SRuslan Bukin return (ENOMEM); 7145d43fd68SRuslan Bukin } 7155d43fd68SRuslan Bukin 716e5232621SOleksandr Tymoshenko m = *mp; 7175d43fd68SRuslan Bukin 71898ea5a7bSEmmanuel Vadot if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) { 71998ea5a7bSEmmanuel Vadot if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0) { 72098ea5a7bSEmmanuel Vadot if (sc->mactype != DWC_GMAC_EXT_DESC) 72198ea5a7bSEmmanuel Vadot flags = NTDESC1_CIC_FULL; 72298ea5a7bSEmmanuel Vadot else 72398ea5a7bSEmmanuel Vadot flags = ETDESC0_CIC_FULL; 72498ea5a7bSEmmanuel Vadot } else { 72598ea5a7bSEmmanuel Vadot if (sc->mactype != DWC_GMAC_EXT_DESC) 72698ea5a7bSEmmanuel Vadot flags = NTDESC1_CIC_HDR; 72798ea5a7bSEmmanuel Vadot else 72898ea5a7bSEmmanuel Vadot flags = ETDESC0_CIC_HDR; 72998ea5a7bSEmmanuel Vadot } 73098ea5a7bSEmmanuel Vadot } 73198ea5a7bSEmmanuel Vadot 732e5232621SOleksandr Tymoshenko bus_dmamap_sync(sc->txbuf_tag, sc->txbuf_map[idx].map, 733e5232621SOleksandr Tymoshenko BUS_DMASYNC_PREWRITE); 734e5232621SOleksandr Tymoshenko 735e5232621SOleksandr Tymoshenko sc->txbuf_map[idx].mbuf = m; 736e5232621SOleksandr Tymoshenko 737e5232621SOleksandr Tymoshenko first = sc->tx_desc_head; 738e5232621SOleksandr Tymoshenko for (i = 0; i < nsegs; i++) { 739e5232621SOleksandr Tymoshenko dwc_setup_txdesc(sc, sc->tx_desc_head, 740e5232621SOleksandr Tymoshenko segs[i].ds_addr, segs[i].ds_len, 741e5232621SOleksandr Tymoshenko (i == 0) ? flags : 0, /* only first desc needs flags */ 742e5232621SOleksandr Tymoshenko (i == 0), 743e5232621SOleksandr Tymoshenko (i == nsegs - 1)); 744e5232621SOleksandr Tymoshenko if (i > 0) 745e5232621SOleksandr Tymoshenko dwc_set_owner(sc, sc->tx_desc_head); 746e5232621SOleksandr Tymoshenko last = sc->tx_desc_head; 747e5232621SOleksandr Tymoshenko sc->tx_desc_head = next_txidx(sc, sc->tx_desc_head); 748e5232621SOleksandr Tymoshenko } 749e5232621SOleksandr Tymoshenko 750e5232621SOleksandr Tymoshenko sc->txbuf_map[idx].last_desc_idx = last; 751e5232621SOleksandr Tymoshenko 752e5232621SOleksandr Tymoshenko dwc_set_owner(sc, first); 7535d43fd68SRuslan Bukin 7545d43fd68SRuslan Bukin return (0); 7555d43fd68SRuslan Bukin } 7565d43fd68SRuslan Bukin 7575d43fd68SRuslan Bukin inline static uint32_t 7585d43fd68SRuslan Bukin dwc_setup_rxdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr) 7595d43fd68SRuslan Bukin { 7605d43fd68SRuslan Bukin uint32_t nidx; 7615d43fd68SRuslan Bukin 7628d43a868SMichal Meloun sc->rxdesc_ring[idx].addr1 = (uint32_t)paddr; 7635d43fd68SRuslan Bukin nidx = next_rxidx(sc, idx); 7648d43a868SMichal Meloun sc->rxdesc_ring[idx].addr2 = sc->rxdesc_ring_paddr + 7655d43fd68SRuslan Bukin (nidx * sizeof(struct dwc_hwdesc)); 766188aee74SMichal Meloun if (sc->mactype != DWC_GMAC_EXT_DESC) 767b72e2878SMichal Meloun sc->rxdesc_ring[idx].desc1 = NRDESC1_RCH | 768b72e2878SMichal Meloun MIN(MCLBYTES, NRDESC1_RBS1_MASK); 7695df53927SLuiz Otavio O Souza else 770b72e2878SMichal Meloun sc->rxdesc_ring[idx].desc1 = ERDESC1_RCH | 771b72e2878SMichal Meloun MIN(MCLBYTES, ERDESC1_RBS1_MASK); 7725d43fd68SRuslan Bukin 7735d43fd68SRuslan Bukin wmb(); 7748d43a868SMichal Meloun sc->rxdesc_ring[idx].desc0 = RDESC0_OWN; 7755d43fd68SRuslan Bukin wmb(); 7765d43fd68SRuslan Bukin return (nidx); 7775d43fd68SRuslan Bukin } 7785d43fd68SRuslan Bukin 7795d43fd68SRuslan Bukin static int 7805d43fd68SRuslan Bukin dwc_setup_rxbuf(struct dwc_softc *sc, int idx, struct mbuf *m) 7815d43fd68SRuslan Bukin { 7825d43fd68SRuslan Bukin struct bus_dma_segment seg; 7835d43fd68SRuslan Bukin int error, nsegs; 7845d43fd68SRuslan Bukin 7855d43fd68SRuslan Bukin m_adj(m, ETHER_ALIGN); 7865d43fd68SRuslan Bukin 7875d43fd68SRuslan Bukin error = bus_dmamap_load_mbuf_sg(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 7885d43fd68SRuslan Bukin m, &seg, &nsegs, 0); 7897ed7d979SMichal Meloun if (error != 0) 7905d43fd68SRuslan Bukin return (error); 7915d43fd68SRuslan Bukin 7925d43fd68SRuslan Bukin KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 7935d43fd68SRuslan Bukin 7945d43fd68SRuslan Bukin bus_dmamap_sync(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 7955d43fd68SRuslan Bukin BUS_DMASYNC_PREREAD); 7965d43fd68SRuslan Bukin 7975d43fd68SRuslan Bukin sc->rxbuf_map[idx].mbuf = m; 7985d43fd68SRuslan Bukin dwc_setup_rxdesc(sc, idx, seg.ds_addr); 7995d43fd68SRuslan Bukin 8005d43fd68SRuslan Bukin return (0); 8015d43fd68SRuslan Bukin } 8025d43fd68SRuslan Bukin 8035d43fd68SRuslan Bukin static struct mbuf * 8045d43fd68SRuslan Bukin dwc_alloc_mbufcl(struct dwc_softc *sc) 8055d43fd68SRuslan Bukin { 8065d43fd68SRuslan Bukin struct mbuf *m; 8075d43fd68SRuslan Bukin 8085d43fd68SRuslan Bukin m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 809db8a143aSRuslan Bukin if (m != NULL) 8105d43fd68SRuslan Bukin m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; 8115d43fd68SRuslan Bukin 8125d43fd68SRuslan Bukin return (m); 8135d43fd68SRuslan Bukin } 8145d43fd68SRuslan Bukin 815b72e2878SMichal Meloun static struct mbuf * 816b72e2878SMichal Meloun dwc_rxfinish_one(struct dwc_softc *sc, struct dwc_hwdesc *desc, 817b72e2878SMichal Meloun struct dwc_bufmap *map) 818b72e2878SMichal Meloun { 819ca018790SMitchell Horne if_t ifp; 820b72e2878SMichal Meloun struct mbuf *m, *m0; 821b72e2878SMichal Meloun int len; 822b72e2878SMichal Meloun uint32_t rdesc0; 823b72e2878SMichal Meloun 824b72e2878SMichal Meloun m = map->mbuf; 825b72e2878SMichal Meloun ifp = sc->ifp; 826b72e2878SMichal Meloun rdesc0 = desc ->desc0; 827b72e2878SMichal Meloun /* Validate descriptor. */ 828b72e2878SMichal Meloun if (rdesc0 & RDESC0_ES) { 829b72e2878SMichal Meloun /* 830b72e2878SMichal Meloun * Errored packet. Statistic counters are updated 831b72e2878SMichal Meloun * globally, so do nothing 832b72e2878SMichal Meloun */ 833b72e2878SMichal Meloun return (NULL); 834b72e2878SMichal Meloun } 835b72e2878SMichal Meloun 836b72e2878SMichal Meloun if ((rdesc0 & (RDESC0_FS | RDESC0_LS)) != 837b72e2878SMichal Meloun (RDESC0_FS | RDESC0_LS)) { 838b72e2878SMichal Meloun /* 839b72e2878SMichal Meloun * Something very wrong happens. The whole packet should be 840b72e2878SMichal Meloun * recevied in one descriptr. Report problem. 841b72e2878SMichal Meloun */ 842b72e2878SMichal Meloun device_printf(sc->dev, 843b72e2878SMichal Meloun "%s: RX descriptor without FIRST and LAST bit set: 0x%08X", 844b72e2878SMichal Meloun __func__, rdesc0); 845b72e2878SMichal Meloun return (NULL); 846b72e2878SMichal Meloun } 847b72e2878SMichal Meloun 848b72e2878SMichal Meloun len = (rdesc0 >> RDESC0_FL_SHIFT) & RDESC0_FL_MASK; 849b72e2878SMichal Meloun if (len < 64) { 850b72e2878SMichal Meloun /* 851b72e2878SMichal Meloun * Lenght is invalid, recycle old mbuf 852b72e2878SMichal Meloun * Probably impossible case 853b72e2878SMichal Meloun */ 854b72e2878SMichal Meloun return (NULL); 855b72e2878SMichal Meloun } 856b72e2878SMichal Meloun 857b72e2878SMichal Meloun /* Allocate new buffer */ 858b72e2878SMichal Meloun m0 = dwc_alloc_mbufcl(sc); 859b72e2878SMichal Meloun if (m0 == NULL) { 860b72e2878SMichal Meloun /* no new mbuf available, recycle old */ 861b72e2878SMichal Meloun if_inc_counter(sc->ifp, IFCOUNTER_IQDROPS, 1); 862b72e2878SMichal Meloun return (NULL); 863b72e2878SMichal Meloun } 864b72e2878SMichal Meloun /* Do dmasync for newly received packet */ 865b72e2878SMichal Meloun bus_dmamap_sync(sc->rxbuf_tag, map->map, BUS_DMASYNC_POSTREAD); 866b72e2878SMichal Meloun bus_dmamap_unload(sc->rxbuf_tag, map->map); 867b72e2878SMichal Meloun 868b72e2878SMichal Meloun /* Received packet is valid, process it */ 869b72e2878SMichal Meloun m->m_pkthdr.rcvif = ifp; 870b72e2878SMichal Meloun m->m_pkthdr.len = len; 871b72e2878SMichal Meloun m->m_len = len; 872b72e2878SMichal Meloun if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 873b72e2878SMichal Meloun 87498ea5a7bSEmmanuel Vadot if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0 && 87598ea5a7bSEmmanuel Vadot (rdesc0 & RDESC0_FT) != 0) { 87698ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_flags = CSUM_IP_CHECKED; 87798ea5a7bSEmmanuel Vadot if ((rdesc0 & RDESC0_ICE) == 0) 87898ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_flags |= CSUM_IP_VALID; 87998ea5a7bSEmmanuel Vadot if ((rdesc0 & RDESC0_PCE) == 0) { 88098ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_flags |= 88198ea5a7bSEmmanuel Vadot CSUM_DATA_VALID | CSUM_PSEUDO_HDR; 88298ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_data = 0xffff; 88398ea5a7bSEmmanuel Vadot } 88498ea5a7bSEmmanuel Vadot } 88598ea5a7bSEmmanuel Vadot 886b72e2878SMichal Meloun /* Remove trailing FCS */ 887b72e2878SMichal Meloun m_adj(m, -ETHER_CRC_LEN); 888b72e2878SMichal Meloun 889b72e2878SMichal Meloun DWC_UNLOCK(sc); 890ca018790SMitchell Horne if_input(ifp, m); 891b72e2878SMichal Meloun DWC_LOCK(sc); 892b72e2878SMichal Meloun return (m0); 893b72e2878SMichal Meloun } 894b72e2878SMichal Meloun 8955d43fd68SRuslan Bukin static int 8965d43fd68SRuslan Bukin setup_dma(struct dwc_softc *sc) 8975d43fd68SRuslan Bukin { 8985d43fd68SRuslan Bukin struct mbuf *m; 8995d43fd68SRuslan Bukin int error; 9005d43fd68SRuslan Bukin int nidx; 9015d43fd68SRuslan Bukin int idx; 9025d43fd68SRuslan Bukin 9035d43fd68SRuslan Bukin /* 9045d43fd68SRuslan Bukin * Set up TX descriptor ring, descriptors, and dma maps. 9055d43fd68SRuslan Bukin */ 9065d43fd68SRuslan Bukin error = bus_dma_tag_create( 9075d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 9085d43fd68SRuslan Bukin DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ 9095d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9105d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9115d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 9125d43fd68SRuslan Bukin TX_DESC_SIZE, 1, /* maxsize, nsegments */ 9135d43fd68SRuslan Bukin TX_DESC_SIZE, /* maxsegsize */ 9145d43fd68SRuslan Bukin 0, /* flags */ 9155d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9165d43fd68SRuslan Bukin &sc->txdesc_tag); 9175d43fd68SRuslan Bukin if (error != 0) { 9185d43fd68SRuslan Bukin device_printf(sc->dev, 9195d43fd68SRuslan Bukin "could not create TX ring DMA tag.\n"); 9205d43fd68SRuslan Bukin goto out; 9215d43fd68SRuslan Bukin } 9225d43fd68SRuslan Bukin 9235d43fd68SRuslan Bukin error = bus_dmamem_alloc(sc->txdesc_tag, (void**)&sc->txdesc_ring, 9245d43fd68SRuslan Bukin BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, 9255d43fd68SRuslan Bukin &sc->txdesc_map); 9265d43fd68SRuslan Bukin if (error != 0) { 9275d43fd68SRuslan Bukin device_printf(sc->dev, 9285d43fd68SRuslan Bukin "could not allocate TX descriptor ring.\n"); 9295d43fd68SRuslan Bukin goto out; 9305d43fd68SRuslan Bukin } 9315d43fd68SRuslan Bukin 9325d43fd68SRuslan Bukin error = bus_dmamap_load(sc->txdesc_tag, sc->txdesc_map, 9335d43fd68SRuslan Bukin sc->txdesc_ring, TX_DESC_SIZE, dwc_get1paddr, 9345d43fd68SRuslan Bukin &sc->txdesc_ring_paddr, 0); 9355d43fd68SRuslan Bukin if (error != 0) { 9365d43fd68SRuslan Bukin device_printf(sc->dev, 9375d43fd68SRuslan Bukin "could not load TX descriptor ring map.\n"); 9385d43fd68SRuslan Bukin goto out; 9395d43fd68SRuslan Bukin } 9405d43fd68SRuslan Bukin 9415d43fd68SRuslan Bukin for (idx = 0; idx < TX_DESC_COUNT; idx++) { 9425d43fd68SRuslan Bukin nidx = next_txidx(sc, idx); 9438d43a868SMichal Meloun sc->txdesc_ring[idx].addr2 = sc->txdesc_ring_paddr + 9445d43fd68SRuslan Bukin (nidx * sizeof(struct dwc_hwdesc)); 9455d43fd68SRuslan Bukin } 9465d43fd68SRuslan Bukin 9475d43fd68SRuslan Bukin error = bus_dma_tag_create( 9485d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 9495d43fd68SRuslan Bukin 1, 0, /* alignment, boundary */ 9505d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9515d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9525d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 953e5232621SOleksandr Tymoshenko MCLBYTES*TX_MAP_MAX_SEGS, /* maxsize */ 954e5232621SOleksandr Tymoshenko TX_MAP_MAX_SEGS, /* nsegments */ 9555d43fd68SRuslan Bukin MCLBYTES, /* maxsegsize */ 9565d43fd68SRuslan Bukin 0, /* flags */ 9575d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9585d43fd68SRuslan Bukin &sc->txbuf_tag); 9595d43fd68SRuslan Bukin if (error != 0) { 9605d43fd68SRuslan Bukin device_printf(sc->dev, 9615d43fd68SRuslan Bukin "could not create TX ring DMA tag.\n"); 9625d43fd68SRuslan Bukin goto out; 9635d43fd68SRuslan Bukin } 9645d43fd68SRuslan Bukin 965e5232621SOleksandr Tymoshenko for (idx = 0; idx < TX_MAP_COUNT; idx++) { 9665d43fd68SRuslan Bukin error = bus_dmamap_create(sc->txbuf_tag, BUS_DMA_COHERENT, 9675d43fd68SRuslan Bukin &sc->txbuf_map[idx].map); 9685d43fd68SRuslan Bukin if (error != 0) { 9695d43fd68SRuslan Bukin device_printf(sc->dev, 9705d43fd68SRuslan Bukin "could not create TX buffer DMA map.\n"); 9715d43fd68SRuslan Bukin goto out; 9725d43fd68SRuslan Bukin } 9735d43fd68SRuslan Bukin } 9745d43fd68SRuslan Bukin 975e5232621SOleksandr Tymoshenko for (idx = 0; idx < TX_DESC_COUNT; idx++) 976e5232621SOleksandr Tymoshenko dwc_setup_txdesc(sc, idx, 0, 0, 0, false, false); 977e5232621SOleksandr Tymoshenko 9785d43fd68SRuslan Bukin /* 9795d43fd68SRuslan Bukin * Set up RX descriptor ring, descriptors, dma maps, and mbufs. 9805d43fd68SRuslan Bukin */ 9815d43fd68SRuslan Bukin error = bus_dma_tag_create( 9825d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 9835d43fd68SRuslan Bukin DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ 9845d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9855d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9865d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 9875d43fd68SRuslan Bukin RX_DESC_SIZE, 1, /* maxsize, nsegments */ 9885d43fd68SRuslan Bukin RX_DESC_SIZE, /* maxsegsize */ 9895d43fd68SRuslan Bukin 0, /* flags */ 9905d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9915d43fd68SRuslan Bukin &sc->rxdesc_tag); 9925d43fd68SRuslan Bukin if (error != 0) { 9935d43fd68SRuslan Bukin device_printf(sc->dev, 9945d43fd68SRuslan Bukin "could not create RX ring DMA tag.\n"); 9955d43fd68SRuslan Bukin goto out; 9965d43fd68SRuslan Bukin } 9975d43fd68SRuslan Bukin 9985d43fd68SRuslan Bukin error = bus_dmamem_alloc(sc->rxdesc_tag, (void **)&sc->rxdesc_ring, 9995d43fd68SRuslan Bukin BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, 10005d43fd68SRuslan Bukin &sc->rxdesc_map); 10015d43fd68SRuslan Bukin if (error != 0) { 10025d43fd68SRuslan Bukin device_printf(sc->dev, 10035d43fd68SRuslan Bukin "could not allocate RX descriptor ring.\n"); 10045d43fd68SRuslan Bukin goto out; 10055d43fd68SRuslan Bukin } 10065d43fd68SRuslan Bukin 10075d43fd68SRuslan Bukin error = bus_dmamap_load(sc->rxdesc_tag, sc->rxdesc_map, 10085d43fd68SRuslan Bukin sc->rxdesc_ring, RX_DESC_SIZE, dwc_get1paddr, 10095d43fd68SRuslan Bukin &sc->rxdesc_ring_paddr, 0); 10105d43fd68SRuslan Bukin if (error != 0) { 10115d43fd68SRuslan Bukin device_printf(sc->dev, 10125d43fd68SRuslan Bukin "could not load RX descriptor ring map.\n"); 10135d43fd68SRuslan Bukin goto out; 10145d43fd68SRuslan Bukin } 10155d43fd68SRuslan Bukin 10165d43fd68SRuslan Bukin error = bus_dma_tag_create( 10175d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 10185d43fd68SRuslan Bukin 1, 0, /* alignment, boundary */ 10195d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 10205d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 10215d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 10225d43fd68SRuslan Bukin MCLBYTES, 1, /* maxsize, nsegments */ 10235d43fd68SRuslan Bukin MCLBYTES, /* maxsegsize */ 10245d43fd68SRuslan Bukin 0, /* flags */ 10255d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 10265d43fd68SRuslan Bukin &sc->rxbuf_tag); 10275d43fd68SRuslan Bukin if (error != 0) { 10285d43fd68SRuslan Bukin device_printf(sc->dev, 10295d43fd68SRuslan Bukin "could not create RX buf DMA tag.\n"); 10305d43fd68SRuslan Bukin goto out; 10315d43fd68SRuslan Bukin } 10325d43fd68SRuslan Bukin 10335d43fd68SRuslan Bukin for (idx = 0; idx < RX_DESC_COUNT; idx++) { 10345d43fd68SRuslan Bukin error = bus_dmamap_create(sc->rxbuf_tag, BUS_DMA_COHERENT, 10355d43fd68SRuslan Bukin &sc->rxbuf_map[idx].map); 10365d43fd68SRuslan Bukin if (error != 0) { 10375d43fd68SRuslan Bukin device_printf(sc->dev, 10385d43fd68SRuslan Bukin "could not create RX buffer DMA map.\n"); 10395d43fd68SRuslan Bukin goto out; 10405d43fd68SRuslan Bukin } 10415d43fd68SRuslan Bukin if ((m = dwc_alloc_mbufcl(sc)) == NULL) { 10425d43fd68SRuslan Bukin device_printf(sc->dev, "Could not alloc mbuf\n"); 10435d43fd68SRuslan Bukin error = ENOMEM; 10445d43fd68SRuslan Bukin goto out; 10455d43fd68SRuslan Bukin } 10465d43fd68SRuslan Bukin if ((error = dwc_setup_rxbuf(sc, idx, m)) != 0) { 10475d43fd68SRuslan Bukin device_printf(sc->dev, 10485d43fd68SRuslan Bukin "could not create new RX buffer.\n"); 10495d43fd68SRuslan Bukin goto out; 10505d43fd68SRuslan Bukin } 10515d43fd68SRuslan Bukin } 10525d43fd68SRuslan Bukin 10535d43fd68SRuslan Bukin out: 10545d43fd68SRuslan Bukin if (error != 0) 10555d43fd68SRuslan Bukin return (ENXIO); 10565d43fd68SRuslan Bukin 10575d43fd68SRuslan Bukin return (0); 10585d43fd68SRuslan Bukin } 10595d43fd68SRuslan Bukin 106027b39e58SMitchell Horne static void 106127b39e58SMitchell Horne free_dma(struct dwc_softc *sc) 106227b39e58SMitchell Horne { 106327b39e58SMitchell Horne bus_dmamap_t map; 106427b39e58SMitchell Horne int idx; 106527b39e58SMitchell Horne 106627b39e58SMitchell Horne /* Clean up RX DMA resources and free mbufs. */ 106727b39e58SMitchell Horne for (idx = 0; idx < RX_DESC_COUNT; ++idx) { 106827b39e58SMitchell Horne if ((map = sc->rxbuf_map[idx].map) != NULL) { 106927b39e58SMitchell Horne bus_dmamap_unload(sc->rxbuf_tag, map); 107027b39e58SMitchell Horne bus_dmamap_destroy(sc->rxbuf_tag, map); 107127b39e58SMitchell Horne m_freem(sc->rxbuf_map[idx].mbuf); 107227b39e58SMitchell Horne } 107327b39e58SMitchell Horne } 107427b39e58SMitchell Horne if (sc->rxbuf_tag != NULL) 107527b39e58SMitchell Horne bus_dma_tag_destroy(sc->rxbuf_tag); 107627b39e58SMitchell Horne if (sc->rxdesc_map != NULL) { 107727b39e58SMitchell Horne bus_dmamap_unload(sc->rxdesc_tag, sc->rxdesc_map); 107827b39e58SMitchell Horne bus_dmamem_free(sc->rxdesc_tag, sc->rxdesc_ring, 107927b39e58SMitchell Horne sc->rxdesc_map); 108027b39e58SMitchell Horne } 108127b39e58SMitchell Horne if (sc->rxdesc_tag != NULL) 108227b39e58SMitchell Horne bus_dma_tag_destroy(sc->rxdesc_tag); 108327b39e58SMitchell Horne 108427b39e58SMitchell Horne /* Clean up TX DMA resources. */ 108527b39e58SMitchell Horne for (idx = 0; idx < TX_DESC_COUNT; ++idx) { 108627b39e58SMitchell Horne if ((map = sc->txbuf_map[idx].map) != NULL) { 108727b39e58SMitchell Horne /* TX maps are already unloaded. */ 108827b39e58SMitchell Horne bus_dmamap_destroy(sc->txbuf_tag, map); 108927b39e58SMitchell Horne } 109027b39e58SMitchell Horne } 109127b39e58SMitchell Horne if (sc->txbuf_tag != NULL) 109227b39e58SMitchell Horne bus_dma_tag_destroy(sc->txbuf_tag); 109327b39e58SMitchell Horne if (sc->txdesc_map != NULL) { 109427b39e58SMitchell Horne bus_dmamap_unload(sc->txdesc_tag, sc->txdesc_map); 109527b39e58SMitchell Horne bus_dmamem_free(sc->txdesc_tag, sc->txdesc_ring, 109627b39e58SMitchell Horne sc->txdesc_map); 109727b39e58SMitchell Horne } 109827b39e58SMitchell Horne if (sc->txdesc_tag != NULL) 109927b39e58SMitchell Horne bus_dma_tag_destroy(sc->txdesc_tag); 110027b39e58SMitchell Horne } 110127b39e58SMitchell Horne 11022a35d391SEmmanuel Vadot /* 11032a35d391SEmmanuel Vadot * if_ functions 11042a35d391SEmmanuel Vadot */ 11052a35d391SEmmanuel Vadot 1106f88e0af6SEmmanuel Vadot static void 11072a35d391SEmmanuel Vadot dwc_txstart_locked(struct dwc_softc *sc) 11085d43fd68SRuslan Bukin { 1109ca018790SMitchell Horne if_t ifp; 11102a35d391SEmmanuel Vadot struct mbuf *m; 11112a35d391SEmmanuel Vadot int enqueued; 11122a35d391SEmmanuel Vadot 11132a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 11142a35d391SEmmanuel Vadot 11152a35d391SEmmanuel Vadot if (!sc->link_is_up) 11162a35d391SEmmanuel Vadot return; 11172a35d391SEmmanuel Vadot 11182a35d391SEmmanuel Vadot ifp = sc->ifp; 11192a35d391SEmmanuel Vadot 11203bbd11eeSEmmanuel Vadot if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != 11213bbd11eeSEmmanuel Vadot IFF_DRV_RUNNING) 11222a35d391SEmmanuel Vadot return; 11232a35d391SEmmanuel Vadot 11242a35d391SEmmanuel Vadot enqueued = 0; 11252a35d391SEmmanuel Vadot 11262a35d391SEmmanuel Vadot for (;;) { 1127e5232621SOleksandr Tymoshenko if (sc->tx_desccount > (TX_DESC_COUNT - TX_MAP_MAX_SEGS + 1)) { 1128e5232621SOleksandr Tymoshenko if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 1129e5232621SOleksandr Tymoshenko break; 1130e5232621SOleksandr Tymoshenko } 1131e5232621SOleksandr Tymoshenko 1132e5232621SOleksandr Tymoshenko if (sc->tx_mapcount == (TX_MAP_COUNT - 1)) { 11333bbd11eeSEmmanuel Vadot if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 11342a35d391SEmmanuel Vadot break; 11352a35d391SEmmanuel Vadot } 11362a35d391SEmmanuel Vadot 11373bbd11eeSEmmanuel Vadot m = if_dequeue(ifp); 11382a35d391SEmmanuel Vadot if (m == NULL) 11392a35d391SEmmanuel Vadot break; 1140e5232621SOleksandr Tymoshenko if (dwc_setup_txbuf(sc, sc->tx_map_head, &m) != 0) { 11413bbd11eeSEmmanuel Vadot if_sendq_prepend(ifp, m); 1142e5232621SOleksandr Tymoshenko if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 11432a35d391SEmmanuel Vadot break; 11442a35d391SEmmanuel Vadot } 11453bbd11eeSEmmanuel Vadot if_bpfmtap(ifp, m); 1146e5232621SOleksandr Tymoshenko sc->tx_map_head = next_txidx(sc, sc->tx_map_head); 1147e5232621SOleksandr Tymoshenko sc->tx_mapcount++; 11482a35d391SEmmanuel Vadot ++enqueued; 11492a35d391SEmmanuel Vadot } 11502a35d391SEmmanuel Vadot 11512a35d391SEmmanuel Vadot if (enqueued != 0) { 11522a35d391SEmmanuel Vadot WRITE4(sc, TRANSMIT_POLL_DEMAND, 0x1); 11532a35d391SEmmanuel Vadot sc->tx_watchdog_count = WATCHDOG_TIMEOUT_SECS; 11542a35d391SEmmanuel Vadot } 11552a35d391SEmmanuel Vadot } 11562a35d391SEmmanuel Vadot 11572a35d391SEmmanuel Vadot static void 1158ca018790SMitchell Horne dwc_txstart(if_t ifp) 11592a35d391SEmmanuel Vadot { 1160ca018790SMitchell Horne struct dwc_softc *sc = if_getsoftc(ifp); 11612a35d391SEmmanuel Vadot 11622a35d391SEmmanuel Vadot DWC_LOCK(sc); 11632a35d391SEmmanuel Vadot dwc_txstart_locked(sc); 11642a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 11652a35d391SEmmanuel Vadot } 11662a35d391SEmmanuel Vadot 11672a35d391SEmmanuel Vadot static void 11682a35d391SEmmanuel Vadot dwc_init_locked(struct dwc_softc *sc) 11692a35d391SEmmanuel Vadot { 1170ca018790SMitchell Horne if_t ifp = sc->ifp; 11712a35d391SEmmanuel Vadot 11722a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 11732a35d391SEmmanuel Vadot 11743bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) 11752a35d391SEmmanuel Vadot return; 11762a35d391SEmmanuel Vadot 11772a35d391SEmmanuel Vadot dwc_setup_rxfilter(sc); 11782a35d391SEmmanuel Vadot dwc_setup_core(sc); 11792a35d391SEmmanuel Vadot dwc_enable_mac(sc, true); 1180*35c9edabSJiahao Li dwc_enable_csum_offload(sc); 11812a35d391SEmmanuel Vadot dwc_init_dma(sc); 11822a35d391SEmmanuel Vadot 11832a35d391SEmmanuel Vadot if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); 11845d43fd68SRuslan Bukin 11855d43fd68SRuslan Bukin /* 11862a35d391SEmmanuel Vadot * Call mii_mediachg() which will call back into dwc_miibus_statchg() 11872a35d391SEmmanuel Vadot * to set up the remaining config registers based on current media. 11885d43fd68SRuslan Bukin */ 11892a35d391SEmmanuel Vadot mii_mediachg(sc->mii_softc); 11902a35d391SEmmanuel Vadot callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); 11912a35d391SEmmanuel Vadot } 11922a35d391SEmmanuel Vadot 11932a35d391SEmmanuel Vadot static void 11942a35d391SEmmanuel Vadot dwc_init(void *if_softc) 11952a35d391SEmmanuel Vadot { 11962a35d391SEmmanuel Vadot struct dwc_softc *sc = if_softc; 11972a35d391SEmmanuel Vadot 11982a35d391SEmmanuel Vadot DWC_LOCK(sc); 11992a35d391SEmmanuel Vadot dwc_init_locked(sc); 12002a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 12012a35d391SEmmanuel Vadot } 12022a35d391SEmmanuel Vadot 12032a35d391SEmmanuel Vadot static void 12042a35d391SEmmanuel Vadot dwc_stop_locked(struct dwc_softc *sc) 12052a35d391SEmmanuel Vadot { 1206ca018790SMitchell Horne if_t ifp; 12072a35d391SEmmanuel Vadot 12082a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 12092a35d391SEmmanuel Vadot 12102a35d391SEmmanuel Vadot ifp = sc->ifp; 12113bbd11eeSEmmanuel Vadot if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 12122a35d391SEmmanuel Vadot sc->tx_watchdog_count = 0; 12132a35d391SEmmanuel Vadot sc->stats_harvest_count = 0; 12142a35d391SEmmanuel Vadot 12152a35d391SEmmanuel Vadot callout_stop(&sc->dwc_callout); 12162a35d391SEmmanuel Vadot 12172a35d391SEmmanuel Vadot dwc_stop_dma(sc); 12182a35d391SEmmanuel Vadot dwc_enable_mac(sc, false); 12192a35d391SEmmanuel Vadot } 12202a35d391SEmmanuel Vadot 12212a35d391SEmmanuel Vadot static int 1222ca018790SMitchell Horne dwc_ioctl(if_t ifp, u_long cmd, caddr_t data) 12232a35d391SEmmanuel Vadot { 12242a35d391SEmmanuel Vadot struct dwc_softc *sc; 12252a35d391SEmmanuel Vadot struct mii_data *mii; 12262a35d391SEmmanuel Vadot struct ifreq *ifr; 12273bbd11eeSEmmanuel Vadot int flags, mask, error; 12282a35d391SEmmanuel Vadot 1229ca018790SMitchell Horne sc = if_getsoftc(ifp); 12302a35d391SEmmanuel Vadot ifr = (struct ifreq *)data; 12312a35d391SEmmanuel Vadot 12322a35d391SEmmanuel Vadot error = 0; 12332a35d391SEmmanuel Vadot switch (cmd) { 12342a35d391SEmmanuel Vadot case SIOCSIFFLAGS: 12352a35d391SEmmanuel Vadot DWC_LOCK(sc); 12363bbd11eeSEmmanuel Vadot if (if_getflags(ifp) & IFF_UP) { 12373bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 12383bbd11eeSEmmanuel Vadot flags = if_getflags(ifp) ^ sc->if_flags; 12393bbd11eeSEmmanuel Vadot if ((flags & (IFF_PROMISC|IFF_ALLMULTI)) != 0) 12402a35d391SEmmanuel Vadot dwc_setup_rxfilter(sc); 12415d43fd68SRuslan Bukin } else { 12422a35d391SEmmanuel Vadot if (!sc->is_detaching) 12432a35d391SEmmanuel Vadot dwc_init_locked(sc); 12442a35d391SEmmanuel Vadot } 12452a35d391SEmmanuel Vadot } else { 12463bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) 12472a35d391SEmmanuel Vadot dwc_stop_locked(sc); 12482a35d391SEmmanuel Vadot } 12493bbd11eeSEmmanuel Vadot sc->if_flags = if_getflags(ifp); 12502a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 12512a35d391SEmmanuel Vadot break; 12522a35d391SEmmanuel Vadot case SIOCADDMULTI: 12532a35d391SEmmanuel Vadot case SIOCDELMULTI: 12543bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 12552a35d391SEmmanuel Vadot DWC_LOCK(sc); 12562a35d391SEmmanuel Vadot dwc_setup_rxfilter(sc); 12572a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 12582a35d391SEmmanuel Vadot } 12592a35d391SEmmanuel Vadot break; 12602a35d391SEmmanuel Vadot case SIOCSIFMEDIA: 12612a35d391SEmmanuel Vadot case SIOCGIFMEDIA: 12622a35d391SEmmanuel Vadot mii = sc->mii_softc; 12632a35d391SEmmanuel Vadot error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); 12642a35d391SEmmanuel Vadot break; 12652a35d391SEmmanuel Vadot case SIOCSIFCAP: 12663bbd11eeSEmmanuel Vadot mask = ifr->ifr_reqcap ^ if_getcapenable(ifp); 12672a35d391SEmmanuel Vadot if (mask & IFCAP_VLAN_MTU) { 12682a35d391SEmmanuel Vadot /* No work to do except acknowledge the change took */ 12693bbd11eeSEmmanuel Vadot if_togglecapenable(ifp, IFCAP_VLAN_MTU); 12702a35d391SEmmanuel Vadot } 127198ea5a7bSEmmanuel Vadot if (mask & IFCAP_RXCSUM) 127298ea5a7bSEmmanuel Vadot if_togglecapenable(ifp, IFCAP_RXCSUM); 127398ea5a7bSEmmanuel Vadot if (mask & IFCAP_TXCSUM) 127498ea5a7bSEmmanuel Vadot if_togglecapenable(ifp, IFCAP_TXCSUM); 127598ea5a7bSEmmanuel Vadot if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0) 127698ea5a7bSEmmanuel Vadot if_sethwassistbits(ifp, CSUM_IP | CSUM_UDP | CSUM_TCP, 0); 127798ea5a7bSEmmanuel Vadot else 127898ea5a7bSEmmanuel Vadot if_sethwassistbits(ifp, 0, CSUM_IP | CSUM_UDP | CSUM_TCP); 1279*35c9edabSJiahao Li 1280*35c9edabSJiahao Li if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 1281*35c9edabSJiahao Li DWC_LOCK(sc); 1282*35c9edabSJiahao Li dwc_enable_csum_offload(sc); 1283*35c9edabSJiahao Li DWC_UNLOCK(sc); 1284*35c9edabSJiahao Li } 12852a35d391SEmmanuel Vadot break; 12862a35d391SEmmanuel Vadot 12872a35d391SEmmanuel Vadot default: 12882a35d391SEmmanuel Vadot error = ether_ioctl(ifp, cmd, data); 12892a35d391SEmmanuel Vadot break; 12902a35d391SEmmanuel Vadot } 12912a35d391SEmmanuel Vadot 12922a35d391SEmmanuel Vadot return (error); 12932a35d391SEmmanuel Vadot } 12942a35d391SEmmanuel Vadot 12952a35d391SEmmanuel Vadot /* 12962a35d391SEmmanuel Vadot * Interrupts functions 12972a35d391SEmmanuel Vadot */ 12982a35d391SEmmanuel Vadot 12992a35d391SEmmanuel Vadot static void 13002a35d391SEmmanuel Vadot dwc_txfinish_locked(struct dwc_softc *sc) 13012a35d391SEmmanuel Vadot { 13022a35d391SEmmanuel Vadot struct dwc_bufmap *bmap; 13032a35d391SEmmanuel Vadot struct dwc_hwdesc *desc; 1304ca018790SMitchell Horne if_t ifp; 1305e5232621SOleksandr Tymoshenko int idx, last_idx; 1306e5232621SOleksandr Tymoshenko bool map_finished; 13072a35d391SEmmanuel Vadot 13082a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 13092a35d391SEmmanuel Vadot 13102a35d391SEmmanuel Vadot ifp = sc->ifp; 1311e5232621SOleksandr Tymoshenko /* check if all descriptors of the map are done */ 1312e5232621SOleksandr Tymoshenko while (sc->tx_map_tail != sc->tx_map_head) { 1313e5232621SOleksandr Tymoshenko map_finished = true; 1314e5232621SOleksandr Tymoshenko bmap = &sc->txbuf_map[sc->tx_map_tail]; 1315e5232621SOleksandr Tymoshenko idx = sc->tx_desc_tail; 1316e5232621SOleksandr Tymoshenko last_idx = next_txidx(sc, bmap->last_desc_idx); 1317e5232621SOleksandr Tymoshenko while (idx != last_idx) { 1318e5232621SOleksandr Tymoshenko desc = &sc->txdesc_ring[idx]; 1319e5232621SOleksandr Tymoshenko if ((desc->desc0 & TDESC0_OWN) != 0) { 1320e5232621SOleksandr Tymoshenko map_finished = false; 13212a35d391SEmmanuel Vadot break; 1322e5232621SOleksandr Tymoshenko } 1323e5232621SOleksandr Tymoshenko idx = next_txidx(sc, idx); 1324e5232621SOleksandr Tymoshenko } 1325e5232621SOleksandr Tymoshenko 1326e5232621SOleksandr Tymoshenko if (!map_finished) 1327e5232621SOleksandr Tymoshenko break; 13282a35d391SEmmanuel Vadot bus_dmamap_sync(sc->txbuf_tag, bmap->map, 13292a35d391SEmmanuel Vadot BUS_DMASYNC_POSTWRITE); 13302a35d391SEmmanuel Vadot bus_dmamap_unload(sc->txbuf_tag, bmap->map); 13312a35d391SEmmanuel Vadot m_freem(bmap->mbuf); 13322a35d391SEmmanuel Vadot bmap->mbuf = NULL; 1333e5232621SOleksandr Tymoshenko sc->tx_mapcount--; 1334e5232621SOleksandr Tymoshenko while (sc->tx_desc_tail != last_idx) { 1335e5232621SOleksandr Tymoshenko dwc_setup_txdesc(sc, sc->tx_desc_tail, 0, 0, 0, false, false); 1336e5232621SOleksandr Tymoshenko sc->tx_desc_tail = next_txidx(sc, sc->tx_desc_tail); 1337e5232621SOleksandr Tymoshenko } 1338e5232621SOleksandr Tymoshenko sc->tx_map_tail = next_txidx(sc, sc->tx_map_tail); 13393bbd11eeSEmmanuel Vadot if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); 13402a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 13412a35d391SEmmanuel Vadot } 13422a35d391SEmmanuel Vadot 13432a35d391SEmmanuel Vadot /* If there are no buffers outstanding, muzzle the watchdog. */ 1344e5232621SOleksandr Tymoshenko if (sc->tx_desc_tail == sc->tx_desc_head) { 13452a35d391SEmmanuel Vadot sc->tx_watchdog_count = 0; 13465d43fd68SRuslan Bukin } 13475d43fd68SRuslan Bukin } 13485d43fd68SRuslan Bukin 13492a35d391SEmmanuel Vadot static void 13502a35d391SEmmanuel Vadot dwc_rxfinish_locked(struct dwc_softc *sc) 13512a35d391SEmmanuel Vadot { 13522a35d391SEmmanuel Vadot struct mbuf *m; 13532a35d391SEmmanuel Vadot int error, idx; 13542a35d391SEmmanuel Vadot struct dwc_hwdesc *desc; 13552a35d391SEmmanuel Vadot 13562a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 13572a35d391SEmmanuel Vadot for (;;) { 13582a35d391SEmmanuel Vadot idx = sc->rx_idx; 13592a35d391SEmmanuel Vadot desc = sc->rxdesc_ring + idx; 13602a35d391SEmmanuel Vadot if ((desc->desc0 & RDESC0_OWN) != 0) 13612a35d391SEmmanuel Vadot break; 13622a35d391SEmmanuel Vadot 13632a35d391SEmmanuel Vadot m = dwc_rxfinish_one(sc, desc, sc->rxbuf_map + idx); 13642a35d391SEmmanuel Vadot if (m == NULL) { 13652a35d391SEmmanuel Vadot wmb(); 13662a35d391SEmmanuel Vadot desc->desc0 = RDESC0_OWN; 13672a35d391SEmmanuel Vadot wmb(); 13682a35d391SEmmanuel Vadot } else { 13692a35d391SEmmanuel Vadot /* We cannot create hole in RX ring */ 13702a35d391SEmmanuel Vadot error = dwc_setup_rxbuf(sc, idx, m); 13712a35d391SEmmanuel Vadot if (error != 0) 13722a35d391SEmmanuel Vadot panic("dwc_setup_rxbuf failed: error %d\n", 13732a35d391SEmmanuel Vadot error); 137498ea5a7bSEmmanuel Vadot 13752a35d391SEmmanuel Vadot } 13762a35d391SEmmanuel Vadot sc->rx_idx = next_rxidx(sc, sc->rx_idx); 13772a35d391SEmmanuel Vadot } 13782a35d391SEmmanuel Vadot } 13792a35d391SEmmanuel Vadot 13802a35d391SEmmanuel Vadot static void 13812a35d391SEmmanuel Vadot dwc_intr(void *arg) 13822a35d391SEmmanuel Vadot { 13832a35d391SEmmanuel Vadot struct dwc_softc *sc; 13842a35d391SEmmanuel Vadot uint32_t reg; 13852a35d391SEmmanuel Vadot 13862a35d391SEmmanuel Vadot sc = arg; 13872a35d391SEmmanuel Vadot 13882a35d391SEmmanuel Vadot DWC_LOCK(sc); 13892a35d391SEmmanuel Vadot 13902a35d391SEmmanuel Vadot reg = READ4(sc, INTERRUPT_STATUS); 13912a35d391SEmmanuel Vadot if (reg) 13922a35d391SEmmanuel Vadot READ4(sc, SGMII_RGMII_SMII_CTRL_STATUS); 13932a35d391SEmmanuel Vadot 13942a35d391SEmmanuel Vadot reg = READ4(sc, DMA_STATUS); 13952a35d391SEmmanuel Vadot if (reg & DMA_STATUS_NIS) { 13962a35d391SEmmanuel Vadot if (reg & DMA_STATUS_RI) 13972a35d391SEmmanuel Vadot dwc_rxfinish_locked(sc); 13982a35d391SEmmanuel Vadot 13992a35d391SEmmanuel Vadot if (reg & DMA_STATUS_TI) { 14002a35d391SEmmanuel Vadot dwc_txfinish_locked(sc); 14012a35d391SEmmanuel Vadot dwc_txstart_locked(sc); 14022a35d391SEmmanuel Vadot } 14032a35d391SEmmanuel Vadot } 14042a35d391SEmmanuel Vadot 14052a35d391SEmmanuel Vadot if (reg & DMA_STATUS_AIS) { 14062a35d391SEmmanuel Vadot if (reg & DMA_STATUS_FBI) { 14072a35d391SEmmanuel Vadot /* Fatal bus error */ 14082a35d391SEmmanuel Vadot device_printf(sc->dev, 14092a35d391SEmmanuel Vadot "Ethernet DMA error, restarting controller.\n"); 14102a35d391SEmmanuel Vadot dwc_stop_locked(sc); 14112a35d391SEmmanuel Vadot dwc_init_locked(sc); 14122a35d391SEmmanuel Vadot } 14132a35d391SEmmanuel Vadot } 14142a35d391SEmmanuel Vadot 14152a35d391SEmmanuel Vadot WRITE4(sc, DMA_STATUS, reg & DMA_STATUS_INTR_MASK); 14162a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 14172a35d391SEmmanuel Vadot } 14182a35d391SEmmanuel Vadot 14192a35d391SEmmanuel Vadot /* 14202a35d391SEmmanuel Vadot * Stats 14212a35d391SEmmanuel Vadot */ 14222a35d391SEmmanuel Vadot 14232a35d391SEmmanuel Vadot static void dwc_clear_stats(struct dwc_softc *sc) 14242a35d391SEmmanuel Vadot { 14252a35d391SEmmanuel Vadot uint32_t reg; 14262a35d391SEmmanuel Vadot 14272a35d391SEmmanuel Vadot reg = READ4(sc, MMC_CONTROL); 14282a35d391SEmmanuel Vadot reg |= (MMC_CONTROL_CNTRST); 14292a35d391SEmmanuel Vadot WRITE4(sc, MMC_CONTROL, reg); 14302a35d391SEmmanuel Vadot } 14312a35d391SEmmanuel Vadot 14322a35d391SEmmanuel Vadot static void 14332a35d391SEmmanuel Vadot dwc_harvest_stats(struct dwc_softc *sc) 14342a35d391SEmmanuel Vadot { 1435ca018790SMitchell Horne if_t ifp; 14362a35d391SEmmanuel Vadot 14372a35d391SEmmanuel Vadot /* We don't need to harvest too often. */ 14382a35d391SEmmanuel Vadot if (++sc->stats_harvest_count < STATS_HARVEST_INTERVAL) 14392a35d391SEmmanuel Vadot return; 14402a35d391SEmmanuel Vadot 14412a35d391SEmmanuel Vadot sc->stats_harvest_count = 0; 14422a35d391SEmmanuel Vadot ifp = sc->ifp; 14432a35d391SEmmanuel Vadot 14442a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_IPACKETS, READ4(sc, RXFRAMECOUNT_GB)); 14452a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_IMCASTS, READ4(sc, RXMULTICASTFRAMES_G)); 14462a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_IERRORS, 14472a35d391SEmmanuel Vadot READ4(sc, RXOVERSIZE_G) + READ4(sc, RXUNDERSIZE_G) + 14482a35d391SEmmanuel Vadot READ4(sc, RXCRCERROR) + READ4(sc, RXALIGNMENTERROR) + 14492a35d391SEmmanuel Vadot READ4(sc, RXRUNTERROR) + READ4(sc, RXJABBERERROR) + 14502a35d391SEmmanuel Vadot READ4(sc, RXLENGTHERROR)); 14512a35d391SEmmanuel Vadot 14522a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OPACKETS, READ4(sc, TXFRAMECOUNT_G)); 14532a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OMCASTS, READ4(sc, TXMULTICASTFRAMES_G)); 14542a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OERRORS, 14552a35d391SEmmanuel Vadot READ4(sc, TXOVERSIZE_G) + READ4(sc, TXEXCESSDEF) + 14562a35d391SEmmanuel Vadot READ4(sc, TXCARRIERERR) + READ4(sc, TXUNDERFLOWERROR)); 14572a35d391SEmmanuel Vadot 14582a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 14592a35d391SEmmanuel Vadot READ4(sc, TXEXESSCOL) + READ4(sc, TXLATECOL)); 14602a35d391SEmmanuel Vadot 14612a35d391SEmmanuel Vadot dwc_clear_stats(sc); 14622a35d391SEmmanuel Vadot } 14632a35d391SEmmanuel Vadot 14642a35d391SEmmanuel Vadot static void 14652a35d391SEmmanuel Vadot dwc_tick(void *arg) 14662a35d391SEmmanuel Vadot { 14672a35d391SEmmanuel Vadot struct dwc_softc *sc; 1468ca018790SMitchell Horne if_t ifp; 14692a35d391SEmmanuel Vadot int link_was_up; 14702a35d391SEmmanuel Vadot 14712a35d391SEmmanuel Vadot sc = arg; 14722a35d391SEmmanuel Vadot 14732a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 14742a35d391SEmmanuel Vadot 14752a35d391SEmmanuel Vadot ifp = sc->ifp; 14762a35d391SEmmanuel Vadot 14773bbd11eeSEmmanuel Vadot if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) 14782a35d391SEmmanuel Vadot return; 14792a35d391SEmmanuel Vadot 14802a35d391SEmmanuel Vadot /* 14812a35d391SEmmanuel Vadot * Typical tx watchdog. If this fires it indicates that we enqueued 14822a35d391SEmmanuel Vadot * packets for output and never got a txdone interrupt for them. Maybe 14832a35d391SEmmanuel Vadot * it's a missed interrupt somehow, just pretend we got one. 14842a35d391SEmmanuel Vadot */ 14852a35d391SEmmanuel Vadot if (sc->tx_watchdog_count > 0) { 14862a35d391SEmmanuel Vadot if (--sc->tx_watchdog_count == 0) { 14872a35d391SEmmanuel Vadot dwc_txfinish_locked(sc); 14882a35d391SEmmanuel Vadot } 14892a35d391SEmmanuel Vadot } 14902a35d391SEmmanuel Vadot 14912a35d391SEmmanuel Vadot /* Gather stats from hardware counters. */ 14922a35d391SEmmanuel Vadot dwc_harvest_stats(sc); 14932a35d391SEmmanuel Vadot 14942a35d391SEmmanuel Vadot /* Check the media status. */ 14952a35d391SEmmanuel Vadot link_was_up = sc->link_is_up; 14962a35d391SEmmanuel Vadot mii_tick(sc->mii_softc); 14972a35d391SEmmanuel Vadot if (sc->link_is_up && !link_was_up) 14982a35d391SEmmanuel Vadot dwc_txstart_locked(sc); 14992a35d391SEmmanuel Vadot 15002a35d391SEmmanuel Vadot /* Schedule another check one second from now. */ 15012a35d391SEmmanuel Vadot callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); 15022a35d391SEmmanuel Vadot } 15032a35d391SEmmanuel Vadot 15042a35d391SEmmanuel Vadot /* 15052a35d391SEmmanuel Vadot * Probe/Attach functions 15062a35d391SEmmanuel Vadot */ 15072a35d391SEmmanuel Vadot 1508d7acb49aSJared McNeill #define GPIO_ACTIVE_LOW 1 1509d7acb49aSJared McNeill 1510d7acb49aSJared McNeill static int 1511d7acb49aSJared McNeill dwc_reset(device_t dev) 1512d7acb49aSJared McNeill { 1513d7acb49aSJared McNeill pcell_t gpio_prop[4]; 1514d7acb49aSJared McNeill pcell_t delay_prop[3]; 1515d7acb49aSJared McNeill phandle_t node, gpio_node; 1516d7acb49aSJared McNeill device_t gpio; 1517d7acb49aSJared McNeill uint32_t pin, flags; 1518d7acb49aSJared McNeill uint32_t pin_value; 1519d7acb49aSJared McNeill 1520d7acb49aSJared McNeill node = ofw_bus_get_node(dev); 1521d7acb49aSJared McNeill if (OF_getencprop(node, "snps,reset-gpio", 1522d7acb49aSJared McNeill gpio_prop, sizeof(gpio_prop)) <= 0) 1523d7acb49aSJared McNeill return (0); 1524d7acb49aSJared McNeill 1525d7acb49aSJared McNeill if (OF_getencprop(node, "snps,reset-delays-us", 1526d7acb49aSJared McNeill delay_prop, sizeof(delay_prop)) <= 0) { 1527d7acb49aSJared McNeill device_printf(dev, 1528d7acb49aSJared McNeill "Wrong property for snps,reset-delays-us"); 1529d7acb49aSJared McNeill return (ENXIO); 1530d7acb49aSJared McNeill } 1531d7acb49aSJared McNeill 1532d7acb49aSJared McNeill gpio_node = OF_node_from_xref(gpio_prop[0]); 1533d7acb49aSJared McNeill if ((gpio = OF_device_from_xref(gpio_prop[0])) == NULL) { 1534d7acb49aSJared McNeill device_printf(dev, 1535d7acb49aSJared McNeill "Can't find gpio controller for phy reset\n"); 1536d7acb49aSJared McNeill return (ENXIO); 1537d7acb49aSJared McNeill } 1538d7acb49aSJared McNeill 1539d7acb49aSJared McNeill if (GPIO_MAP_GPIOS(gpio, node, gpio_node, 154073a1170aSPedro F. Giffuni nitems(gpio_prop) - 1, 1541d7acb49aSJared McNeill gpio_prop + 1, &pin, &flags) != 0) { 1542d7acb49aSJared McNeill device_printf(dev, "Can't map gpio for phy reset\n"); 1543d7acb49aSJared McNeill return (ENXIO); 1544d7acb49aSJared McNeill } 1545d7acb49aSJared McNeill 1546d7acb49aSJared McNeill pin_value = GPIO_PIN_LOW; 1547d7acb49aSJared McNeill if (OF_hasprop(node, "snps,reset-active-low")) 1548d7acb49aSJared McNeill pin_value = GPIO_PIN_HIGH; 1549d7acb49aSJared McNeill 1550d7acb49aSJared McNeill GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT); 1551d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 1552c069412eSEmmanuel Vadot DELAY(delay_prop[0] * 5); 1553d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, !pin_value); 1554c069412eSEmmanuel Vadot DELAY(delay_prop[1] * 5); 1555d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 1556c069412eSEmmanuel Vadot DELAY(delay_prop[2] * 5); 1557d7acb49aSJared McNeill 1558d7acb49aSJared McNeill return (0); 1559d7acb49aSJared McNeill } 1560d7acb49aSJared McNeill 15616a05f063SJared McNeill static int 15626a05f063SJared McNeill dwc_clock_init(device_t dev) 15636a05f063SJared McNeill { 15646a05f063SJared McNeill hwreset_t rst; 15656a05f063SJared McNeill clk_t clk; 15666a05f063SJared McNeill int error; 1567824cfb47SOleksandr Tymoshenko int64_t freq; 15686a05f063SJared McNeill 1569824cfb47SOleksandr Tymoshenko /* Enable clocks */ 1570dac93553SMichal Meloun if (clk_get_by_ofw_name(dev, 0, "stmmaceth", &clk) == 0) { 15716a05f063SJared McNeill error = clk_enable(clk); 15726a05f063SJared McNeill if (error != 0) { 15736a05f063SJared McNeill device_printf(dev, "could not enable main clock\n"); 15746a05f063SJared McNeill return (error); 15756a05f063SJared McNeill } 1576824cfb47SOleksandr Tymoshenko if (bootverbose) { 1577824cfb47SOleksandr Tymoshenko clk_get_freq(clk, &freq); 1578fc254a2eSLi-Wen Hsu device_printf(dev, "MAC clock(%s) freq: %jd\n", 1579fc254a2eSLi-Wen Hsu clk_get_name(clk), (intmax_t)freq); 1580824cfb47SOleksandr Tymoshenko } 1581824cfb47SOleksandr Tymoshenko } 1582824cfb47SOleksandr Tymoshenko else { 1583824cfb47SOleksandr Tymoshenko device_printf(dev, "could not find clock stmmaceth\n"); 15846a05f063SJared McNeill } 15856a05f063SJared McNeill 15866a05f063SJared McNeill /* De-assert reset */ 1587dac93553SMichal Meloun if (hwreset_get_by_ofw_name(dev, 0, "stmmaceth", &rst) == 0) { 15886a05f063SJared McNeill error = hwreset_deassert(rst); 15896a05f063SJared McNeill if (error != 0) { 15906a05f063SJared McNeill device_printf(dev, "could not de-assert reset\n"); 15916a05f063SJared McNeill return (error); 15926a05f063SJared McNeill } 15936a05f063SJared McNeill } 15946a05f063SJared McNeill 15956a05f063SJared McNeill return (0); 15966a05f063SJared McNeill } 15976a05f063SJared McNeill 15985d43fd68SRuslan Bukin static int 15995d43fd68SRuslan Bukin dwc_probe(device_t dev) 16005d43fd68SRuslan Bukin { 16015d43fd68SRuslan Bukin 16025d43fd68SRuslan Bukin if (!ofw_bus_status_okay(dev)) 16035d43fd68SRuslan Bukin return (ENXIO); 16045d43fd68SRuslan Bukin 16055d43fd68SRuslan Bukin if (!ofw_bus_is_compatible(dev, "snps,dwmac")) 16065d43fd68SRuslan Bukin return (ENXIO); 16075d43fd68SRuslan Bukin 16085d43fd68SRuslan Bukin device_set_desc(dev, "Gigabit Ethernet Controller"); 16095d43fd68SRuslan Bukin return (BUS_PROBE_DEFAULT); 16105d43fd68SRuslan Bukin } 16115d43fd68SRuslan Bukin 16125d43fd68SRuslan Bukin static int 16135d43fd68SRuslan Bukin dwc_attach(device_t dev) 16145d43fd68SRuslan Bukin { 16155d43fd68SRuslan Bukin uint8_t macaddr[ETHER_ADDR_LEN]; 16165d43fd68SRuslan Bukin struct dwc_softc *sc; 1617ca018790SMitchell Horne if_t ifp; 1618ff0752c8SLuiz Otavio O Souza int error, i; 1619ff0752c8SLuiz Otavio O Souza uint32_t reg; 1620824cfb47SOleksandr Tymoshenko phandle_t node; 16213e5cd548SEmmanuel Vadot uint32_t txpbl, rxpbl, pbl; 16225e38d9e4SEmmanuel Vadot bool nopblx8 = false; 16235e38d9e4SEmmanuel Vadot bool fixed_burst = false; 16245d43fd68SRuslan Bukin 16255d43fd68SRuslan Bukin sc = device_get_softc(dev); 16265d43fd68SRuslan Bukin sc->dev = dev; 16275d43fd68SRuslan Bukin sc->rx_idx = 0; 1628e5232621SOleksandr Tymoshenko sc->tx_desccount = TX_DESC_COUNT; 1629e5232621SOleksandr Tymoshenko sc->tx_mapcount = 0; 16305df53927SLuiz Otavio O Souza sc->mii_clk = IF_DWC_MII_CLK(dev); 16315df53927SLuiz Otavio O Souza sc->mactype = IF_DWC_MAC_TYPE(dev); 16325df53927SLuiz Otavio O Souza 1633824cfb47SOleksandr Tymoshenko node = ofw_bus_get_node(dev); 1634f77d8d10SEmmanuel Vadot switch (mii_fdt_get_contype(node)) { 1635f77d8d10SEmmanuel Vadot case MII_CONTYPE_RGMII: 1636f77d8d10SEmmanuel Vadot case MII_CONTYPE_RGMII_ID: 1637f77d8d10SEmmanuel Vadot case MII_CONTYPE_RGMII_RXID: 1638f77d8d10SEmmanuel Vadot case MII_CONTYPE_RGMII_TXID: 1639824cfb47SOleksandr Tymoshenko sc->phy_mode = PHY_MODE_RGMII; 1640f77d8d10SEmmanuel Vadot break; 1641f77d8d10SEmmanuel Vadot case MII_CONTYPE_RMII: 1642824cfb47SOleksandr Tymoshenko sc->phy_mode = PHY_MODE_RMII; 1643f77d8d10SEmmanuel Vadot break; 1644da6252a6SEmmanuel Vadot case MII_CONTYPE_MII: 1645da6252a6SEmmanuel Vadot sc->phy_mode = PHY_MODE_MII; 1646da6252a6SEmmanuel Vadot break; 1647f77d8d10SEmmanuel Vadot default: 1648f77d8d10SEmmanuel Vadot device_printf(dev, "Unsupported MII type\n"); 1649f77d8d10SEmmanuel Vadot return (ENXIO); 1650824cfb47SOleksandr Tymoshenko } 1651824cfb47SOleksandr Tymoshenko 16523e5cd548SEmmanuel Vadot if (OF_getencprop(node, "snps,pbl", &pbl, sizeof(uint32_t)) <= 0) 16533e5cd548SEmmanuel Vadot pbl = BUS_MODE_DEFAULT_PBL; 16545e38d9e4SEmmanuel Vadot if (OF_getencprop(node, "snps,txpbl", &txpbl, sizeof(uint32_t)) <= 0) 16553e5cd548SEmmanuel Vadot txpbl = pbl; 16565e38d9e4SEmmanuel Vadot if (OF_getencprop(node, "snps,rxpbl", &rxpbl, sizeof(uint32_t)) <= 0) 16573e5cd548SEmmanuel Vadot rxpbl = pbl; 16585e38d9e4SEmmanuel Vadot if (OF_hasprop(node, "snps,no-pbl-x8") == 1) 16595e38d9e4SEmmanuel Vadot nopblx8 = true; 16605e38d9e4SEmmanuel Vadot if (OF_hasprop(node, "snps,fixed-burst") == 1) 16615e38d9e4SEmmanuel Vadot fixed_burst = true; 16625e38d9e4SEmmanuel Vadot 16635df53927SLuiz Otavio O Souza if (IF_DWC_INIT(dev) != 0) 16645df53927SLuiz Otavio O Souza return (ENXIO); 16655d43fd68SRuslan Bukin 16666a05f063SJared McNeill if (dwc_clock_init(dev) != 0) 16676a05f063SJared McNeill return (ENXIO); 16686a05f063SJared McNeill 16695d43fd68SRuslan Bukin if (bus_alloc_resources(dev, dwc_spec, sc->res)) { 16705d43fd68SRuslan Bukin device_printf(dev, "could not allocate resources\n"); 16715d43fd68SRuslan Bukin return (ENXIO); 16725d43fd68SRuslan Bukin } 16735d43fd68SRuslan Bukin 16745d43fd68SRuslan Bukin /* Read MAC before reset */ 1675f88e0af6SEmmanuel Vadot dwc_get_hwaddr(sc, macaddr); 16765d43fd68SRuslan Bukin 1677d7acb49aSJared McNeill /* Reset the PHY if needed */ 1678d7acb49aSJared McNeill if (dwc_reset(dev) != 0) { 1679d7acb49aSJared McNeill device_printf(dev, "Can't reset the PHY\n"); 168030f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 1681d7acb49aSJared McNeill return (ENXIO); 1682d7acb49aSJared McNeill } 1683d7acb49aSJared McNeill 16845d43fd68SRuslan Bukin /* Reset */ 16855d43fd68SRuslan Bukin reg = READ4(sc, BUS_MODE); 16865d43fd68SRuslan Bukin reg |= (BUS_MODE_SWR); 16875d43fd68SRuslan Bukin WRITE4(sc, BUS_MODE, reg); 16885d43fd68SRuslan Bukin 1689d8e5258dSRuslan Bukin for (i = 0; i < MAC_RESET_TIMEOUT; i++) { 16905d43fd68SRuslan Bukin if ((READ4(sc, BUS_MODE) & BUS_MODE_SWR) == 0) 16915d43fd68SRuslan Bukin break; 16925d43fd68SRuslan Bukin DELAY(10); 16935d43fd68SRuslan Bukin } 1694d8e5258dSRuslan Bukin if (i >= MAC_RESET_TIMEOUT) { 16955d43fd68SRuslan Bukin device_printf(sc->dev, "Can't reset DWC.\n"); 169630f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 16975d43fd68SRuslan Bukin return (ENXIO); 16985d43fd68SRuslan Bukin } 16995d43fd68SRuslan Bukin 17005e38d9e4SEmmanuel Vadot reg = BUS_MODE_USP; 17015e38d9e4SEmmanuel Vadot if (!nopblx8) 17025e38d9e4SEmmanuel Vadot reg |= BUS_MODE_EIGHTXPBL; 17035e38d9e4SEmmanuel Vadot reg |= (txpbl << BUS_MODE_PBL_SHIFT); 17045e38d9e4SEmmanuel Vadot reg |= (rxpbl << BUS_MODE_RPBL_SHIFT); 17055e38d9e4SEmmanuel Vadot if (fixed_burst) 17065e38d9e4SEmmanuel Vadot reg |= BUS_MODE_FIXEDBURST; 17073e5cd548SEmmanuel Vadot 17085d43fd68SRuslan Bukin WRITE4(sc, BUS_MODE, reg); 17095d43fd68SRuslan Bukin 17105d43fd68SRuslan Bukin /* 17115d43fd68SRuslan Bukin * DMA must be stop while changing descriptor list addresses. 17125d43fd68SRuslan Bukin */ 17135d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 17145d43fd68SRuslan Bukin reg &= ~(MODE_ST | MODE_SR); 17155d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 17165d43fd68SRuslan Bukin 171730f16ad4SEmmanuel Vadot if (setup_dma(sc)) { 171830f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 17195d43fd68SRuslan Bukin return (ENXIO); 172030f16ad4SEmmanuel Vadot } 17215d43fd68SRuslan Bukin 17225d43fd68SRuslan Bukin /* Setup addresses */ 17235d43fd68SRuslan Bukin WRITE4(sc, RX_DESCR_LIST_ADDR, sc->rxdesc_ring_paddr); 17245d43fd68SRuslan Bukin WRITE4(sc, TX_DESCR_LIST_ADDR, sc->txdesc_ring_paddr); 17255d43fd68SRuslan Bukin 1726d8e5258dSRuslan Bukin mtx_init(&sc->mtx, device_get_nameunit(sc->dev), 1727d8e5258dSRuslan Bukin MTX_NETWORK_LOCK, MTX_DEF); 1728d8e5258dSRuslan Bukin 1729d8e5258dSRuslan Bukin callout_init_mtx(&sc->dwc_callout, &sc->mtx, 0); 1730d8e5258dSRuslan Bukin 1731d8e5258dSRuslan Bukin /* Setup interrupt handler. */ 1732d8e5258dSRuslan Bukin error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE, 1733d8e5258dSRuslan Bukin NULL, dwc_intr, sc, &sc->intr_cookie); 1734d8e5258dSRuslan Bukin if (error != 0) { 1735d8e5258dSRuslan Bukin device_printf(dev, "could not setup interrupt handler.\n"); 173630f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 1737d8e5258dSRuslan Bukin return (ENXIO); 1738d8e5258dSRuslan Bukin } 1739d8e5258dSRuslan Bukin 17405d43fd68SRuslan Bukin /* Set up the ethernet interface. */ 17415d43fd68SRuslan Bukin sc->ifp = ifp = if_alloc(IFT_ETHER); 17425d43fd68SRuslan Bukin 1743ca018790SMitchell Horne if_setsoftc(ifp, sc); 17445d43fd68SRuslan Bukin if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 17453bbd11eeSEmmanuel Vadot if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); 17463bbd11eeSEmmanuel Vadot if_setstartfn(ifp, dwc_txstart); 17473bbd11eeSEmmanuel Vadot if_setioctlfn(ifp, dwc_ioctl); 17483bbd11eeSEmmanuel Vadot if_setinitfn(ifp, dwc_init); 1749e5232621SOleksandr Tymoshenko if_setsendqlen(ifp, TX_MAP_COUNT - 1); 17503bbd11eeSEmmanuel Vadot if_setsendqready(sc->ifp); 175198ea5a7bSEmmanuel Vadot if_sethwassist(sc->ifp, CSUM_IP | CSUM_UDP | CSUM_TCP); 175298ea5a7bSEmmanuel Vadot if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM); 17533bbd11eeSEmmanuel Vadot if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp)); 17545d43fd68SRuslan Bukin 17555d43fd68SRuslan Bukin /* Attach the mii driver. */ 17565d43fd68SRuslan Bukin error = mii_attach(dev, &sc->miibus, ifp, dwc_media_change, 17575d43fd68SRuslan Bukin dwc_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, 17585d43fd68SRuslan Bukin MII_OFFSET_ANY, 0); 17595d43fd68SRuslan Bukin 17605d43fd68SRuslan Bukin if (error != 0) { 17615d43fd68SRuslan Bukin device_printf(dev, "PHY attach failed\n"); 176230f16ad4SEmmanuel Vadot bus_teardown_intr(dev, sc->res[1], sc->intr_cookie); 176330f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 17645d43fd68SRuslan Bukin return (ENXIO); 17655d43fd68SRuslan Bukin } 17665d43fd68SRuslan Bukin sc->mii_softc = device_get_softc(sc->miibus); 17675d43fd68SRuslan Bukin 17685d43fd68SRuslan Bukin /* All ready to run, attach the ethernet interface. */ 17695d43fd68SRuslan Bukin ether_ifattach(ifp, macaddr); 17705d43fd68SRuslan Bukin sc->is_attached = true; 17715d43fd68SRuslan Bukin 17725d43fd68SRuslan Bukin return (0); 17735d43fd68SRuslan Bukin } 17745d43fd68SRuslan Bukin 177527b39e58SMitchell Horne static int 177627b39e58SMitchell Horne dwc_detach(device_t dev) 177727b39e58SMitchell Horne { 177827b39e58SMitchell Horne struct dwc_softc *sc; 177927b39e58SMitchell Horne 178027b39e58SMitchell Horne sc = device_get_softc(dev); 178127b39e58SMitchell Horne 178227b39e58SMitchell Horne /* 178327b39e58SMitchell Horne * Disable and tear down interrupts before anything else, so we don't 178427b39e58SMitchell Horne * race with the handler. 178527b39e58SMitchell Horne */ 178627b39e58SMitchell Horne WRITE4(sc, INTERRUPT_ENABLE, 0); 178727b39e58SMitchell Horne if (sc->intr_cookie != NULL) { 178827b39e58SMitchell Horne bus_teardown_intr(dev, sc->res[1], sc->intr_cookie); 178927b39e58SMitchell Horne } 179027b39e58SMitchell Horne 179127b39e58SMitchell Horne if (sc->is_attached) { 179227b39e58SMitchell Horne DWC_LOCK(sc); 179327b39e58SMitchell Horne sc->is_detaching = true; 179427b39e58SMitchell Horne dwc_stop_locked(sc); 179527b39e58SMitchell Horne DWC_UNLOCK(sc); 179627b39e58SMitchell Horne callout_drain(&sc->dwc_callout); 179727b39e58SMitchell Horne ether_ifdetach(sc->ifp); 179827b39e58SMitchell Horne } 179927b39e58SMitchell Horne 180027b39e58SMitchell Horne if (sc->miibus != NULL) { 180127b39e58SMitchell Horne device_delete_child(dev, sc->miibus); 180227b39e58SMitchell Horne sc->miibus = NULL; 180327b39e58SMitchell Horne } 180427b39e58SMitchell Horne bus_generic_detach(dev); 180527b39e58SMitchell Horne 180627b39e58SMitchell Horne /* Free DMA descriptors */ 180727b39e58SMitchell Horne free_dma(sc); 180827b39e58SMitchell Horne 180927b39e58SMitchell Horne if (sc->ifp != NULL) { 181027b39e58SMitchell Horne if_free(sc->ifp); 181127b39e58SMitchell Horne sc->ifp = NULL; 181227b39e58SMitchell Horne } 181327b39e58SMitchell Horne 181427b39e58SMitchell Horne bus_release_resources(dev, dwc_spec, sc->res); 181527b39e58SMitchell Horne 181627b39e58SMitchell Horne mtx_destroy(&sc->mtx); 181727b39e58SMitchell Horne return (0); 181827b39e58SMitchell Horne } 181927b39e58SMitchell Horne 18205d43fd68SRuslan Bukin static device_method_t dwc_methods[] = { 18215d43fd68SRuslan Bukin DEVMETHOD(device_probe, dwc_probe), 18225d43fd68SRuslan Bukin DEVMETHOD(device_attach, dwc_attach), 182327b39e58SMitchell Horne DEVMETHOD(device_detach, dwc_detach), 18245d43fd68SRuslan Bukin 18255d43fd68SRuslan Bukin /* MII Interface */ 18265d43fd68SRuslan Bukin DEVMETHOD(miibus_readreg, dwc_miibus_read_reg), 18275d43fd68SRuslan Bukin DEVMETHOD(miibus_writereg, dwc_miibus_write_reg), 18285d43fd68SRuslan Bukin DEVMETHOD(miibus_statchg, dwc_miibus_statchg), 18295d43fd68SRuslan Bukin 18305d43fd68SRuslan Bukin { 0, 0 } 18315d43fd68SRuslan Bukin }; 18325d43fd68SRuslan Bukin 18335df53927SLuiz Otavio O Souza driver_t dwc_driver = { 18345d43fd68SRuslan Bukin "dwc", 18355d43fd68SRuslan Bukin dwc_methods, 18365d43fd68SRuslan Bukin sizeof(struct dwc_softc), 18375d43fd68SRuslan Bukin }; 18385d43fd68SRuslan Bukin 1839e55d0536SJohn Baldwin DRIVER_MODULE(dwc, simplebus, dwc_driver, 0, 0); 18403e38757dSJohn Baldwin DRIVER_MODULE(miibus, dwc, miibus_driver, 0, 0); 18415d43fd68SRuslan Bukin 18425d43fd68SRuslan Bukin MODULE_DEPEND(dwc, ether, 1, 1, 1); 18435d43fd68SRuslan Bukin MODULE_DEPEND(dwc, miibus, 1, 1, 1); 1844