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 5222a35d391SEmmanuel Vadot dwc_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr) 5232a35d391SEmmanuel Vadot { 5242a35d391SEmmanuel Vadot uint32_t hi, lo, rnd; 5252a35d391SEmmanuel Vadot 5262a35d391SEmmanuel Vadot /* 5272a35d391SEmmanuel Vadot * Try to recover a MAC address from the running hardware. If there's 5282a35d391SEmmanuel Vadot * something non-zero there, assume the bootloader did the right thing 5292a35d391SEmmanuel Vadot * and just use it. 5302a35d391SEmmanuel Vadot * 5312a35d391SEmmanuel Vadot * Otherwise, set the address to a convenient locally assigned address, 5322a35d391SEmmanuel Vadot * 'bsd' + random 24 low-order bits. 'b' is 0x62, which has the locally 5332a35d391SEmmanuel Vadot * assigned bit set, and the broadcast/multicast bit clear. 5342a35d391SEmmanuel Vadot */ 5352a35d391SEmmanuel Vadot lo = READ4(sc, MAC_ADDRESS_LOW(0)); 5362a35d391SEmmanuel Vadot hi = READ4(sc, MAC_ADDRESS_HIGH(0)) & 0xffff; 5372a35d391SEmmanuel Vadot if ((lo != 0xffffffff) || (hi != 0xffff)) { 5382a35d391SEmmanuel Vadot hwaddr[0] = (lo >> 0) & 0xff; 5392a35d391SEmmanuel Vadot hwaddr[1] = (lo >> 8) & 0xff; 5402a35d391SEmmanuel Vadot hwaddr[2] = (lo >> 16) & 0xff; 5412a35d391SEmmanuel Vadot hwaddr[3] = (lo >> 24) & 0xff; 5422a35d391SEmmanuel Vadot hwaddr[4] = (hi >> 0) & 0xff; 5432a35d391SEmmanuel Vadot hwaddr[5] = (hi >> 8) & 0xff; 5442a35d391SEmmanuel Vadot } else { 5452a35d391SEmmanuel Vadot rnd = arc4random() & 0x00ffffff; 5462a35d391SEmmanuel Vadot hwaddr[0] = 'b'; 5472a35d391SEmmanuel Vadot hwaddr[1] = 's'; 5482a35d391SEmmanuel Vadot hwaddr[2] = 'd'; 5492a35d391SEmmanuel Vadot hwaddr[3] = rnd >> 16; 5502a35d391SEmmanuel Vadot hwaddr[4] = rnd >> 8; 5512a35d391SEmmanuel Vadot hwaddr[5] = rnd >> 0; 5522a35d391SEmmanuel Vadot } 5532a35d391SEmmanuel Vadot } 5542a35d391SEmmanuel Vadot 5552a35d391SEmmanuel Vadot /* 5562a35d391SEmmanuel Vadot * DMA functions 5572a35d391SEmmanuel Vadot */ 5582a35d391SEmmanuel Vadot 5592a35d391SEmmanuel Vadot static void 5602a35d391SEmmanuel Vadot dwc_init_dma(struct dwc_softc *sc) 5612a35d391SEmmanuel Vadot { 5622a35d391SEmmanuel Vadot uint32_t reg; 5632a35d391SEmmanuel Vadot 5642a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 5652a35d391SEmmanuel Vadot 5662a35d391SEmmanuel Vadot /* Initializa DMA and enable transmitters */ 5672a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 5682a35d391SEmmanuel Vadot reg |= (MODE_TSF | MODE_OSF | MODE_FUF); 5692a35d391SEmmanuel Vadot reg &= ~(MODE_RSF); 5702a35d391SEmmanuel Vadot reg |= (MODE_RTC_LEV32 << MODE_RTC_SHIFT); 5712a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 5722a35d391SEmmanuel Vadot 5732a35d391SEmmanuel Vadot WRITE4(sc, INTERRUPT_ENABLE, INT_EN_DEFAULT); 5742a35d391SEmmanuel Vadot 5752a35d391SEmmanuel Vadot /* Start DMA */ 5762a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 5772a35d391SEmmanuel Vadot reg |= (MODE_ST | MODE_SR); 5782a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 5792a35d391SEmmanuel Vadot } 5802a35d391SEmmanuel Vadot 5812a35d391SEmmanuel Vadot static void 5822a35d391SEmmanuel Vadot dwc_stop_dma(struct dwc_softc *sc) 5832a35d391SEmmanuel Vadot { 5842a35d391SEmmanuel Vadot uint32_t reg; 5852a35d391SEmmanuel Vadot 5862a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 5872a35d391SEmmanuel Vadot 5882a35d391SEmmanuel Vadot /* Stop DMA TX */ 5892a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 5902a35d391SEmmanuel Vadot reg &= ~(MODE_ST); 5912a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 5922a35d391SEmmanuel Vadot 5932a35d391SEmmanuel Vadot /* Flush TX */ 5942a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 5952a35d391SEmmanuel Vadot reg |= (MODE_FTF); 5962a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 5972a35d391SEmmanuel Vadot 5982a35d391SEmmanuel Vadot /* Stop DMA RX */ 5992a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 6002a35d391SEmmanuel Vadot reg &= ~(MODE_SR); 6012a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 6022a35d391SEmmanuel Vadot } 6032a35d391SEmmanuel Vadot 6045d43fd68SRuslan Bukin static inline uint32_t 6055d43fd68SRuslan Bukin next_rxidx(struct dwc_softc *sc, uint32_t curidx) 6065d43fd68SRuslan Bukin { 6075d43fd68SRuslan Bukin 6085d43fd68SRuslan Bukin return ((curidx + 1) % RX_DESC_COUNT); 6095d43fd68SRuslan Bukin } 6105d43fd68SRuslan Bukin 6115d43fd68SRuslan Bukin static inline uint32_t 6125d43fd68SRuslan Bukin next_txidx(struct dwc_softc *sc, uint32_t curidx) 6135d43fd68SRuslan Bukin { 6145d43fd68SRuslan Bukin 6155d43fd68SRuslan Bukin return ((curidx + 1) % TX_DESC_COUNT); 6165d43fd68SRuslan Bukin } 6175d43fd68SRuslan Bukin 6185d43fd68SRuslan Bukin static void 6195d43fd68SRuslan Bukin dwc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 6205d43fd68SRuslan Bukin { 6215d43fd68SRuslan Bukin 6225d43fd68SRuslan Bukin if (error != 0) 6235d43fd68SRuslan Bukin return; 6245d43fd68SRuslan Bukin *(bus_addr_t *)arg = segs[0].ds_addr; 6255d43fd68SRuslan Bukin } 6265d43fd68SRuslan Bukin 627b72e2878SMichal Meloun inline static void 6285d43fd68SRuslan Bukin dwc_setup_txdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr, 629e5232621SOleksandr Tymoshenko uint32_t len, uint32_t flags, bool first, bool last) 6305d43fd68SRuslan Bukin { 631b72e2878SMichal Meloun uint32_t desc0, desc1; 6325d43fd68SRuslan Bukin 6335d43fd68SRuslan Bukin /* Addr/len 0 means we're clearing the descriptor after xmit done. */ 6345d43fd68SRuslan Bukin if (paddr == 0 || len == 0) { 635b72e2878SMichal Meloun desc0 = 0; 636b72e2878SMichal Meloun desc1 = 0; 637e5232621SOleksandr Tymoshenko --sc->tx_desccount; 6385d43fd68SRuslan Bukin } else { 639b72e2878SMichal Meloun if (sc->mactype != DWC_GMAC_EXT_DESC) { 640b72e2878SMichal Meloun desc0 = 0; 641e5232621SOleksandr Tymoshenko desc1 = NTDESC1_TCH | len | flags; 642e5232621SOleksandr Tymoshenko if (first) 643e5232621SOleksandr Tymoshenko desc1 |= NTDESC1_FS; 644e5232621SOleksandr Tymoshenko if (last) 645e5232621SOleksandr Tymoshenko desc1 |= NTDESC1_LS | NTDESC1_IC; 646b72e2878SMichal Meloun } else { 647e5232621SOleksandr Tymoshenko desc0 = ETDESC0_TCH | flags; 648e5232621SOleksandr Tymoshenko if (first) 649e5232621SOleksandr Tymoshenko desc0 |= ETDESC0_FS; 650e5232621SOleksandr Tymoshenko if (last) 651e5232621SOleksandr Tymoshenko desc0 |= ETDESC0_LS | ETDESC0_IC; 652b72e2878SMichal Meloun desc1 = len; 653b72e2878SMichal Meloun } 654e5232621SOleksandr Tymoshenko ++sc->tx_desccount; 6555d43fd68SRuslan Bukin } 6565d43fd68SRuslan Bukin 6578d43a868SMichal Meloun sc->txdesc_ring[idx].addr1 = (uint32_t)(paddr); 658b72e2878SMichal Meloun sc->txdesc_ring[idx].desc0 = desc0; 659b72e2878SMichal Meloun sc->txdesc_ring[idx].desc1 = desc1; 660e5232621SOleksandr Tymoshenko } 6615d43fd68SRuslan Bukin 662e5232621SOleksandr Tymoshenko inline static void 663e5232621SOleksandr Tymoshenko dwc_set_owner(struct dwc_softc *sc, int idx) 664e5232621SOleksandr Tymoshenko { 6655d43fd68SRuslan Bukin wmb(); 6668d43a868SMichal Meloun sc->txdesc_ring[idx].desc0 |= TDESC0_OWN; 6675d43fd68SRuslan Bukin wmb(); 6685d43fd68SRuslan Bukin } 6695d43fd68SRuslan Bukin 6705d43fd68SRuslan Bukin static int 6715d43fd68SRuslan Bukin dwc_setup_txbuf(struct dwc_softc *sc, int idx, struct mbuf **mp) 6725d43fd68SRuslan Bukin { 673e5232621SOleksandr Tymoshenko struct bus_dma_segment segs[TX_MAP_MAX_SEGS]; 6745d43fd68SRuslan Bukin int error, nsegs; 6755d43fd68SRuslan Bukin struct mbuf * m; 67698ea5a7bSEmmanuel Vadot uint32_t flags = 0; 677e5232621SOleksandr Tymoshenko int i; 678e5232621SOleksandr Tymoshenko int first, last; 6795d43fd68SRuslan Bukin 680e5232621SOleksandr Tymoshenko error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, 681e5232621SOleksandr Tymoshenko *mp, segs, &nsegs, 0); 682e5232621SOleksandr Tymoshenko if (error == EFBIG) { 683e5232621SOleksandr Tymoshenko /* 684e5232621SOleksandr Tymoshenko * The map may be partially mapped from the first call. 685e5232621SOleksandr Tymoshenko * Make sure to reset it. 686e5232621SOleksandr Tymoshenko */ 687e5232621SOleksandr Tymoshenko bus_dmamap_unload(sc->txbuf_tag, sc->txbuf_map[idx].map); 6885d43fd68SRuslan Bukin if ((m = m_defrag(*mp, M_NOWAIT)) == NULL) 6895d43fd68SRuslan Bukin return (ENOMEM); 6905d43fd68SRuslan Bukin *mp = m; 6915d43fd68SRuslan Bukin error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, 692e5232621SOleksandr Tymoshenko *mp, segs, &nsegs, 0); 693e5232621SOleksandr Tymoshenko } 694e5232621SOleksandr Tymoshenko if (error != 0) 695e5232621SOleksandr Tymoshenko return (ENOMEM); 696e5232621SOleksandr Tymoshenko 697e5232621SOleksandr Tymoshenko if (sc->tx_desccount + nsegs > TX_DESC_COUNT) { 698e5232621SOleksandr Tymoshenko bus_dmamap_unload(sc->txbuf_tag, sc->txbuf_map[idx].map); 6995d43fd68SRuslan Bukin return (ENOMEM); 7005d43fd68SRuslan Bukin } 7015d43fd68SRuslan Bukin 702e5232621SOleksandr Tymoshenko m = *mp; 7035d43fd68SRuslan Bukin 70498ea5a7bSEmmanuel Vadot if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) { 70598ea5a7bSEmmanuel Vadot if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0) { 70698ea5a7bSEmmanuel Vadot if (sc->mactype != DWC_GMAC_EXT_DESC) 70798ea5a7bSEmmanuel Vadot flags = NTDESC1_CIC_FULL; 70898ea5a7bSEmmanuel Vadot else 70998ea5a7bSEmmanuel Vadot flags = ETDESC0_CIC_FULL; 71098ea5a7bSEmmanuel Vadot } else { 71198ea5a7bSEmmanuel Vadot if (sc->mactype != DWC_GMAC_EXT_DESC) 71298ea5a7bSEmmanuel Vadot flags = NTDESC1_CIC_HDR; 71398ea5a7bSEmmanuel Vadot else 71498ea5a7bSEmmanuel Vadot flags = ETDESC0_CIC_HDR; 71598ea5a7bSEmmanuel Vadot } 71698ea5a7bSEmmanuel Vadot } 71798ea5a7bSEmmanuel Vadot 718e5232621SOleksandr Tymoshenko bus_dmamap_sync(sc->txbuf_tag, sc->txbuf_map[idx].map, 719e5232621SOleksandr Tymoshenko BUS_DMASYNC_PREWRITE); 720e5232621SOleksandr Tymoshenko 721e5232621SOleksandr Tymoshenko sc->txbuf_map[idx].mbuf = m; 722e5232621SOleksandr Tymoshenko 723e5232621SOleksandr Tymoshenko first = sc->tx_desc_head; 724e5232621SOleksandr Tymoshenko for (i = 0; i < nsegs; i++) { 725e5232621SOleksandr Tymoshenko dwc_setup_txdesc(sc, sc->tx_desc_head, 726e5232621SOleksandr Tymoshenko segs[i].ds_addr, segs[i].ds_len, 727e5232621SOleksandr Tymoshenko (i == 0) ? flags : 0, /* only first desc needs flags */ 728e5232621SOleksandr Tymoshenko (i == 0), 729e5232621SOleksandr Tymoshenko (i == nsegs - 1)); 730e5232621SOleksandr Tymoshenko if (i > 0) 731e5232621SOleksandr Tymoshenko dwc_set_owner(sc, sc->tx_desc_head); 732e5232621SOleksandr Tymoshenko last = sc->tx_desc_head; 733e5232621SOleksandr Tymoshenko sc->tx_desc_head = next_txidx(sc, sc->tx_desc_head); 734e5232621SOleksandr Tymoshenko } 735e5232621SOleksandr Tymoshenko 736e5232621SOleksandr Tymoshenko sc->txbuf_map[idx].last_desc_idx = last; 737e5232621SOleksandr Tymoshenko 738e5232621SOleksandr Tymoshenko dwc_set_owner(sc, first); 7395d43fd68SRuslan Bukin 7405d43fd68SRuslan Bukin return (0); 7415d43fd68SRuslan Bukin } 7425d43fd68SRuslan Bukin 7435d43fd68SRuslan Bukin inline static uint32_t 7445d43fd68SRuslan Bukin dwc_setup_rxdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr) 7455d43fd68SRuslan Bukin { 7465d43fd68SRuslan Bukin uint32_t nidx; 7475d43fd68SRuslan Bukin 7488d43a868SMichal Meloun sc->rxdesc_ring[idx].addr1 = (uint32_t)paddr; 7495d43fd68SRuslan Bukin nidx = next_rxidx(sc, idx); 7508d43a868SMichal Meloun sc->rxdesc_ring[idx].addr2 = sc->rxdesc_ring_paddr + 7515d43fd68SRuslan Bukin (nidx * sizeof(struct dwc_hwdesc)); 752188aee74SMichal Meloun if (sc->mactype != DWC_GMAC_EXT_DESC) 753b72e2878SMichal Meloun sc->rxdesc_ring[idx].desc1 = NRDESC1_RCH | 754b72e2878SMichal Meloun MIN(MCLBYTES, NRDESC1_RBS1_MASK); 7555df53927SLuiz Otavio O Souza else 756b72e2878SMichal Meloun sc->rxdesc_ring[idx].desc1 = ERDESC1_RCH | 757b72e2878SMichal Meloun MIN(MCLBYTES, ERDESC1_RBS1_MASK); 7585d43fd68SRuslan Bukin 7595d43fd68SRuslan Bukin wmb(); 7608d43a868SMichal Meloun sc->rxdesc_ring[idx].desc0 = RDESC0_OWN; 7615d43fd68SRuslan Bukin wmb(); 7625d43fd68SRuslan Bukin return (nidx); 7635d43fd68SRuslan Bukin } 7645d43fd68SRuslan Bukin 7655d43fd68SRuslan Bukin static int 7665d43fd68SRuslan Bukin dwc_setup_rxbuf(struct dwc_softc *sc, int idx, struct mbuf *m) 7675d43fd68SRuslan Bukin { 7685d43fd68SRuslan Bukin struct bus_dma_segment seg; 7695d43fd68SRuslan Bukin int error, nsegs; 7705d43fd68SRuslan Bukin 7715d43fd68SRuslan Bukin m_adj(m, ETHER_ALIGN); 7725d43fd68SRuslan Bukin 7735d43fd68SRuslan Bukin error = bus_dmamap_load_mbuf_sg(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 7745d43fd68SRuslan Bukin m, &seg, &nsegs, 0); 7757ed7d979SMichal Meloun if (error != 0) 7765d43fd68SRuslan Bukin return (error); 7775d43fd68SRuslan Bukin 7785d43fd68SRuslan Bukin KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 7795d43fd68SRuslan Bukin 7805d43fd68SRuslan Bukin bus_dmamap_sync(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 7815d43fd68SRuslan Bukin BUS_DMASYNC_PREREAD); 7825d43fd68SRuslan Bukin 7835d43fd68SRuslan Bukin sc->rxbuf_map[idx].mbuf = m; 7845d43fd68SRuslan Bukin dwc_setup_rxdesc(sc, idx, seg.ds_addr); 7855d43fd68SRuslan Bukin 7865d43fd68SRuslan Bukin return (0); 7875d43fd68SRuslan Bukin } 7885d43fd68SRuslan Bukin 7895d43fd68SRuslan Bukin static struct mbuf * 7905d43fd68SRuslan Bukin dwc_alloc_mbufcl(struct dwc_softc *sc) 7915d43fd68SRuslan Bukin { 7925d43fd68SRuslan Bukin struct mbuf *m; 7935d43fd68SRuslan Bukin 7945d43fd68SRuslan Bukin m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 795db8a143aSRuslan Bukin if (m != NULL) 7965d43fd68SRuslan Bukin m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; 7975d43fd68SRuslan Bukin 7985d43fd68SRuslan Bukin return (m); 7995d43fd68SRuslan Bukin } 8005d43fd68SRuslan Bukin 801b72e2878SMichal Meloun static struct mbuf * 802b72e2878SMichal Meloun dwc_rxfinish_one(struct dwc_softc *sc, struct dwc_hwdesc *desc, 803b72e2878SMichal Meloun struct dwc_bufmap *map) 804b72e2878SMichal Meloun { 805ca018790SMitchell Horne if_t ifp; 806b72e2878SMichal Meloun struct mbuf *m, *m0; 807b72e2878SMichal Meloun int len; 808b72e2878SMichal Meloun uint32_t rdesc0; 809b72e2878SMichal Meloun 810b72e2878SMichal Meloun m = map->mbuf; 811b72e2878SMichal Meloun ifp = sc->ifp; 812b72e2878SMichal Meloun rdesc0 = desc ->desc0; 813b72e2878SMichal Meloun /* Validate descriptor. */ 814b72e2878SMichal Meloun if (rdesc0 & RDESC0_ES) { 815b72e2878SMichal Meloun /* 816b72e2878SMichal Meloun * Errored packet. Statistic counters are updated 817b72e2878SMichal Meloun * globally, so do nothing 818b72e2878SMichal Meloun */ 819b72e2878SMichal Meloun return (NULL); 820b72e2878SMichal Meloun } 821b72e2878SMichal Meloun 822b72e2878SMichal Meloun if ((rdesc0 & (RDESC0_FS | RDESC0_LS)) != 823b72e2878SMichal Meloun (RDESC0_FS | RDESC0_LS)) { 824b72e2878SMichal Meloun /* 825b72e2878SMichal Meloun * Something very wrong happens. The whole packet should be 826b72e2878SMichal Meloun * recevied in one descriptr. Report problem. 827b72e2878SMichal Meloun */ 828b72e2878SMichal Meloun device_printf(sc->dev, 829b72e2878SMichal Meloun "%s: RX descriptor without FIRST and LAST bit set: 0x%08X", 830b72e2878SMichal Meloun __func__, rdesc0); 831b72e2878SMichal Meloun return (NULL); 832b72e2878SMichal Meloun } 833b72e2878SMichal Meloun 834b72e2878SMichal Meloun len = (rdesc0 >> RDESC0_FL_SHIFT) & RDESC0_FL_MASK; 835b72e2878SMichal Meloun if (len < 64) { 836b72e2878SMichal Meloun /* 837b72e2878SMichal Meloun * Lenght is invalid, recycle old mbuf 838b72e2878SMichal Meloun * Probably impossible case 839b72e2878SMichal Meloun */ 840b72e2878SMichal Meloun return (NULL); 841b72e2878SMichal Meloun } 842b72e2878SMichal Meloun 843b72e2878SMichal Meloun /* Allocate new buffer */ 844b72e2878SMichal Meloun m0 = dwc_alloc_mbufcl(sc); 845b72e2878SMichal Meloun if (m0 == NULL) { 846b72e2878SMichal Meloun /* no new mbuf available, recycle old */ 847b72e2878SMichal Meloun if_inc_counter(sc->ifp, IFCOUNTER_IQDROPS, 1); 848b72e2878SMichal Meloun return (NULL); 849b72e2878SMichal Meloun } 850b72e2878SMichal Meloun /* Do dmasync for newly received packet */ 851b72e2878SMichal Meloun bus_dmamap_sync(sc->rxbuf_tag, map->map, BUS_DMASYNC_POSTREAD); 852b72e2878SMichal Meloun bus_dmamap_unload(sc->rxbuf_tag, map->map); 853b72e2878SMichal Meloun 854b72e2878SMichal Meloun /* Received packet is valid, process it */ 855b72e2878SMichal Meloun m->m_pkthdr.rcvif = ifp; 856b72e2878SMichal Meloun m->m_pkthdr.len = len; 857b72e2878SMichal Meloun m->m_len = len; 858b72e2878SMichal Meloun if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 859b72e2878SMichal Meloun 86098ea5a7bSEmmanuel Vadot if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0 && 86198ea5a7bSEmmanuel Vadot (rdesc0 & RDESC0_FT) != 0) { 86298ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_flags = CSUM_IP_CHECKED; 86398ea5a7bSEmmanuel Vadot if ((rdesc0 & RDESC0_ICE) == 0) 86498ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_flags |= CSUM_IP_VALID; 86598ea5a7bSEmmanuel Vadot if ((rdesc0 & RDESC0_PCE) == 0) { 86698ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_flags |= 86798ea5a7bSEmmanuel Vadot CSUM_DATA_VALID | CSUM_PSEUDO_HDR; 86898ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_data = 0xffff; 86998ea5a7bSEmmanuel Vadot } 87098ea5a7bSEmmanuel Vadot } 87198ea5a7bSEmmanuel Vadot 872b72e2878SMichal Meloun /* Remove trailing FCS */ 873b72e2878SMichal Meloun m_adj(m, -ETHER_CRC_LEN); 874b72e2878SMichal Meloun 875b72e2878SMichal Meloun DWC_UNLOCK(sc); 876ca018790SMitchell Horne if_input(ifp, m); 877b72e2878SMichal Meloun DWC_LOCK(sc); 878b72e2878SMichal Meloun return (m0); 879b72e2878SMichal Meloun } 880b72e2878SMichal Meloun 8815d43fd68SRuslan Bukin static int 8825d43fd68SRuslan Bukin setup_dma(struct dwc_softc *sc) 8835d43fd68SRuslan Bukin { 8845d43fd68SRuslan Bukin struct mbuf *m; 8855d43fd68SRuslan Bukin int error; 8865d43fd68SRuslan Bukin int nidx; 8875d43fd68SRuslan Bukin int idx; 8885d43fd68SRuslan Bukin 8895d43fd68SRuslan Bukin /* 8905d43fd68SRuslan Bukin * Set up TX descriptor ring, descriptors, and dma maps. 8915d43fd68SRuslan Bukin */ 8925d43fd68SRuslan Bukin error = bus_dma_tag_create( 8935d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 8945d43fd68SRuslan Bukin DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ 8955d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 8965d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 8975d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 8985d43fd68SRuslan Bukin TX_DESC_SIZE, 1, /* maxsize, nsegments */ 8995d43fd68SRuslan Bukin TX_DESC_SIZE, /* maxsegsize */ 9005d43fd68SRuslan Bukin 0, /* flags */ 9015d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9025d43fd68SRuslan Bukin &sc->txdesc_tag); 9035d43fd68SRuslan Bukin if (error != 0) { 9045d43fd68SRuslan Bukin device_printf(sc->dev, 9055d43fd68SRuslan Bukin "could not create TX ring DMA tag.\n"); 9065d43fd68SRuslan Bukin goto out; 9075d43fd68SRuslan Bukin } 9085d43fd68SRuslan Bukin 9095d43fd68SRuslan Bukin error = bus_dmamem_alloc(sc->txdesc_tag, (void**)&sc->txdesc_ring, 9105d43fd68SRuslan Bukin BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, 9115d43fd68SRuslan Bukin &sc->txdesc_map); 9125d43fd68SRuslan Bukin if (error != 0) { 9135d43fd68SRuslan Bukin device_printf(sc->dev, 9145d43fd68SRuslan Bukin "could not allocate TX descriptor ring.\n"); 9155d43fd68SRuslan Bukin goto out; 9165d43fd68SRuslan Bukin } 9175d43fd68SRuslan Bukin 9185d43fd68SRuslan Bukin error = bus_dmamap_load(sc->txdesc_tag, sc->txdesc_map, 9195d43fd68SRuslan Bukin sc->txdesc_ring, TX_DESC_SIZE, dwc_get1paddr, 9205d43fd68SRuslan Bukin &sc->txdesc_ring_paddr, 0); 9215d43fd68SRuslan Bukin if (error != 0) { 9225d43fd68SRuslan Bukin device_printf(sc->dev, 9235d43fd68SRuslan Bukin "could not load TX descriptor ring map.\n"); 9245d43fd68SRuslan Bukin goto out; 9255d43fd68SRuslan Bukin } 9265d43fd68SRuslan Bukin 9275d43fd68SRuslan Bukin for (idx = 0; idx < TX_DESC_COUNT; idx++) { 9285d43fd68SRuslan Bukin nidx = next_txidx(sc, idx); 9298d43a868SMichal Meloun sc->txdesc_ring[idx].addr2 = sc->txdesc_ring_paddr + 9305d43fd68SRuslan Bukin (nidx * sizeof(struct dwc_hwdesc)); 9315d43fd68SRuslan Bukin } 9325d43fd68SRuslan Bukin 9335d43fd68SRuslan Bukin error = bus_dma_tag_create( 9345d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 9355d43fd68SRuslan Bukin 1, 0, /* alignment, boundary */ 9365d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9375d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9385d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 939e5232621SOleksandr Tymoshenko MCLBYTES*TX_MAP_MAX_SEGS, /* maxsize */ 940e5232621SOleksandr Tymoshenko TX_MAP_MAX_SEGS, /* nsegments */ 9415d43fd68SRuslan Bukin MCLBYTES, /* maxsegsize */ 9425d43fd68SRuslan Bukin 0, /* flags */ 9435d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9445d43fd68SRuslan Bukin &sc->txbuf_tag); 9455d43fd68SRuslan Bukin if (error != 0) { 9465d43fd68SRuslan Bukin device_printf(sc->dev, 9475d43fd68SRuslan Bukin "could not create TX ring DMA tag.\n"); 9485d43fd68SRuslan Bukin goto out; 9495d43fd68SRuslan Bukin } 9505d43fd68SRuslan Bukin 951e5232621SOleksandr Tymoshenko for (idx = 0; idx < TX_MAP_COUNT; idx++) { 9525d43fd68SRuslan Bukin error = bus_dmamap_create(sc->txbuf_tag, BUS_DMA_COHERENT, 9535d43fd68SRuslan Bukin &sc->txbuf_map[idx].map); 9545d43fd68SRuslan Bukin if (error != 0) { 9555d43fd68SRuslan Bukin device_printf(sc->dev, 9565d43fd68SRuslan Bukin "could not create TX buffer DMA map.\n"); 9575d43fd68SRuslan Bukin goto out; 9585d43fd68SRuslan Bukin } 9595d43fd68SRuslan Bukin } 9605d43fd68SRuslan Bukin 961e5232621SOleksandr Tymoshenko for (idx = 0; idx < TX_DESC_COUNT; idx++) 962e5232621SOleksandr Tymoshenko dwc_setup_txdesc(sc, idx, 0, 0, 0, false, false); 963e5232621SOleksandr Tymoshenko 9645d43fd68SRuslan Bukin /* 9655d43fd68SRuslan Bukin * Set up RX descriptor ring, descriptors, dma maps, and mbufs. 9665d43fd68SRuslan Bukin */ 9675d43fd68SRuslan Bukin error = bus_dma_tag_create( 9685d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 9695d43fd68SRuslan Bukin DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ 9705d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9715d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9725d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 9735d43fd68SRuslan Bukin RX_DESC_SIZE, 1, /* maxsize, nsegments */ 9745d43fd68SRuslan Bukin RX_DESC_SIZE, /* maxsegsize */ 9755d43fd68SRuslan Bukin 0, /* flags */ 9765d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9775d43fd68SRuslan Bukin &sc->rxdesc_tag); 9785d43fd68SRuslan Bukin if (error != 0) { 9795d43fd68SRuslan Bukin device_printf(sc->dev, 9805d43fd68SRuslan Bukin "could not create RX ring DMA tag.\n"); 9815d43fd68SRuslan Bukin goto out; 9825d43fd68SRuslan Bukin } 9835d43fd68SRuslan Bukin 9845d43fd68SRuslan Bukin error = bus_dmamem_alloc(sc->rxdesc_tag, (void **)&sc->rxdesc_ring, 9855d43fd68SRuslan Bukin BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, 9865d43fd68SRuslan Bukin &sc->rxdesc_map); 9875d43fd68SRuslan Bukin if (error != 0) { 9885d43fd68SRuslan Bukin device_printf(sc->dev, 9895d43fd68SRuslan Bukin "could not allocate RX descriptor ring.\n"); 9905d43fd68SRuslan Bukin goto out; 9915d43fd68SRuslan Bukin } 9925d43fd68SRuslan Bukin 9935d43fd68SRuslan Bukin error = bus_dmamap_load(sc->rxdesc_tag, sc->rxdesc_map, 9945d43fd68SRuslan Bukin sc->rxdesc_ring, RX_DESC_SIZE, dwc_get1paddr, 9955d43fd68SRuslan Bukin &sc->rxdesc_ring_paddr, 0); 9965d43fd68SRuslan Bukin if (error != 0) { 9975d43fd68SRuslan Bukin device_printf(sc->dev, 9985d43fd68SRuslan Bukin "could not load RX descriptor ring map.\n"); 9995d43fd68SRuslan Bukin goto out; 10005d43fd68SRuslan Bukin } 10015d43fd68SRuslan Bukin 10025d43fd68SRuslan Bukin error = bus_dma_tag_create( 10035d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 10045d43fd68SRuslan Bukin 1, 0, /* alignment, boundary */ 10055d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 10065d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 10075d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 10085d43fd68SRuslan Bukin MCLBYTES, 1, /* maxsize, nsegments */ 10095d43fd68SRuslan Bukin MCLBYTES, /* maxsegsize */ 10105d43fd68SRuslan Bukin 0, /* flags */ 10115d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 10125d43fd68SRuslan Bukin &sc->rxbuf_tag); 10135d43fd68SRuslan Bukin if (error != 0) { 10145d43fd68SRuslan Bukin device_printf(sc->dev, 10155d43fd68SRuslan Bukin "could not create RX buf DMA tag.\n"); 10165d43fd68SRuslan Bukin goto out; 10175d43fd68SRuslan Bukin } 10185d43fd68SRuslan Bukin 10195d43fd68SRuslan Bukin for (idx = 0; idx < RX_DESC_COUNT; idx++) { 10205d43fd68SRuslan Bukin error = bus_dmamap_create(sc->rxbuf_tag, BUS_DMA_COHERENT, 10215d43fd68SRuslan Bukin &sc->rxbuf_map[idx].map); 10225d43fd68SRuslan Bukin if (error != 0) { 10235d43fd68SRuslan Bukin device_printf(sc->dev, 10245d43fd68SRuslan Bukin "could not create RX buffer DMA map.\n"); 10255d43fd68SRuslan Bukin goto out; 10265d43fd68SRuslan Bukin } 10275d43fd68SRuslan Bukin if ((m = dwc_alloc_mbufcl(sc)) == NULL) { 10285d43fd68SRuslan Bukin device_printf(sc->dev, "Could not alloc mbuf\n"); 10295d43fd68SRuslan Bukin error = ENOMEM; 10305d43fd68SRuslan Bukin goto out; 10315d43fd68SRuslan Bukin } 10325d43fd68SRuslan Bukin if ((error = dwc_setup_rxbuf(sc, idx, m)) != 0) { 10335d43fd68SRuslan Bukin device_printf(sc->dev, 10345d43fd68SRuslan Bukin "could not create new RX buffer.\n"); 10355d43fd68SRuslan Bukin goto out; 10365d43fd68SRuslan Bukin } 10375d43fd68SRuslan Bukin } 10385d43fd68SRuslan Bukin 10395d43fd68SRuslan Bukin out: 10405d43fd68SRuslan Bukin if (error != 0) 10415d43fd68SRuslan Bukin return (ENXIO); 10425d43fd68SRuslan Bukin 10435d43fd68SRuslan Bukin return (0); 10445d43fd68SRuslan Bukin } 10455d43fd68SRuslan Bukin 1046*27b39e58SMitchell Horne static void 1047*27b39e58SMitchell Horne free_dma(struct dwc_softc *sc) 1048*27b39e58SMitchell Horne { 1049*27b39e58SMitchell Horne bus_dmamap_t map; 1050*27b39e58SMitchell Horne int idx; 1051*27b39e58SMitchell Horne 1052*27b39e58SMitchell Horne /* Clean up RX DMA resources and free mbufs. */ 1053*27b39e58SMitchell Horne for (idx = 0; idx < RX_DESC_COUNT; ++idx) { 1054*27b39e58SMitchell Horne if ((map = sc->rxbuf_map[idx].map) != NULL) { 1055*27b39e58SMitchell Horne bus_dmamap_unload(sc->rxbuf_tag, map); 1056*27b39e58SMitchell Horne bus_dmamap_destroy(sc->rxbuf_tag, map); 1057*27b39e58SMitchell Horne m_freem(sc->rxbuf_map[idx].mbuf); 1058*27b39e58SMitchell Horne } 1059*27b39e58SMitchell Horne } 1060*27b39e58SMitchell Horne if (sc->rxbuf_tag != NULL) 1061*27b39e58SMitchell Horne bus_dma_tag_destroy(sc->rxbuf_tag); 1062*27b39e58SMitchell Horne if (sc->rxdesc_map != NULL) { 1063*27b39e58SMitchell Horne bus_dmamap_unload(sc->rxdesc_tag, sc->rxdesc_map); 1064*27b39e58SMitchell Horne bus_dmamem_free(sc->rxdesc_tag, sc->rxdesc_ring, 1065*27b39e58SMitchell Horne sc->rxdesc_map); 1066*27b39e58SMitchell Horne } 1067*27b39e58SMitchell Horne if (sc->rxdesc_tag != NULL) 1068*27b39e58SMitchell Horne bus_dma_tag_destroy(sc->rxdesc_tag); 1069*27b39e58SMitchell Horne 1070*27b39e58SMitchell Horne /* Clean up TX DMA resources. */ 1071*27b39e58SMitchell Horne for (idx = 0; idx < TX_DESC_COUNT; ++idx) { 1072*27b39e58SMitchell Horne if ((map = sc->txbuf_map[idx].map) != NULL) { 1073*27b39e58SMitchell Horne /* TX maps are already unloaded. */ 1074*27b39e58SMitchell Horne bus_dmamap_destroy(sc->txbuf_tag, map); 1075*27b39e58SMitchell Horne } 1076*27b39e58SMitchell Horne } 1077*27b39e58SMitchell Horne if (sc->txbuf_tag != NULL) 1078*27b39e58SMitchell Horne bus_dma_tag_destroy(sc->txbuf_tag); 1079*27b39e58SMitchell Horne if (sc->txdesc_map != NULL) { 1080*27b39e58SMitchell Horne bus_dmamap_unload(sc->txdesc_tag, sc->txdesc_map); 1081*27b39e58SMitchell Horne bus_dmamem_free(sc->txdesc_tag, sc->txdesc_ring, 1082*27b39e58SMitchell Horne sc->txdesc_map); 1083*27b39e58SMitchell Horne } 1084*27b39e58SMitchell Horne if (sc->txdesc_tag != NULL) 1085*27b39e58SMitchell Horne bus_dma_tag_destroy(sc->txdesc_tag); 1086*27b39e58SMitchell Horne } 1087*27b39e58SMitchell Horne 10882a35d391SEmmanuel Vadot /* 10892a35d391SEmmanuel Vadot * if_ functions 10902a35d391SEmmanuel Vadot */ 10912a35d391SEmmanuel Vadot 1092f88e0af6SEmmanuel Vadot static void 10932a35d391SEmmanuel Vadot dwc_txstart_locked(struct dwc_softc *sc) 10945d43fd68SRuslan Bukin { 1095ca018790SMitchell Horne if_t ifp; 10962a35d391SEmmanuel Vadot struct mbuf *m; 10972a35d391SEmmanuel Vadot int enqueued; 10982a35d391SEmmanuel Vadot 10992a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 11002a35d391SEmmanuel Vadot 11012a35d391SEmmanuel Vadot if (!sc->link_is_up) 11022a35d391SEmmanuel Vadot return; 11032a35d391SEmmanuel Vadot 11042a35d391SEmmanuel Vadot ifp = sc->ifp; 11052a35d391SEmmanuel Vadot 11063bbd11eeSEmmanuel Vadot if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != 11073bbd11eeSEmmanuel Vadot IFF_DRV_RUNNING) 11082a35d391SEmmanuel Vadot return; 11092a35d391SEmmanuel Vadot 11102a35d391SEmmanuel Vadot enqueued = 0; 11112a35d391SEmmanuel Vadot 11122a35d391SEmmanuel Vadot for (;;) { 1113e5232621SOleksandr Tymoshenko if (sc->tx_desccount > (TX_DESC_COUNT - TX_MAP_MAX_SEGS + 1)) { 1114e5232621SOleksandr Tymoshenko if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 1115e5232621SOleksandr Tymoshenko break; 1116e5232621SOleksandr Tymoshenko } 1117e5232621SOleksandr Tymoshenko 1118e5232621SOleksandr Tymoshenko if (sc->tx_mapcount == (TX_MAP_COUNT - 1)) { 11193bbd11eeSEmmanuel Vadot if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 11202a35d391SEmmanuel Vadot break; 11212a35d391SEmmanuel Vadot } 11222a35d391SEmmanuel Vadot 11233bbd11eeSEmmanuel Vadot m = if_dequeue(ifp); 11242a35d391SEmmanuel Vadot if (m == NULL) 11252a35d391SEmmanuel Vadot break; 1126e5232621SOleksandr Tymoshenko if (dwc_setup_txbuf(sc, sc->tx_map_head, &m) != 0) { 11273bbd11eeSEmmanuel Vadot if_sendq_prepend(ifp, m); 1128e5232621SOleksandr Tymoshenko if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 11292a35d391SEmmanuel Vadot break; 11302a35d391SEmmanuel Vadot } 11313bbd11eeSEmmanuel Vadot if_bpfmtap(ifp, m); 1132e5232621SOleksandr Tymoshenko sc->tx_map_head = next_txidx(sc, sc->tx_map_head); 1133e5232621SOleksandr Tymoshenko sc->tx_mapcount++; 11342a35d391SEmmanuel Vadot ++enqueued; 11352a35d391SEmmanuel Vadot } 11362a35d391SEmmanuel Vadot 11372a35d391SEmmanuel Vadot if (enqueued != 0) { 11382a35d391SEmmanuel Vadot WRITE4(sc, TRANSMIT_POLL_DEMAND, 0x1); 11392a35d391SEmmanuel Vadot sc->tx_watchdog_count = WATCHDOG_TIMEOUT_SECS; 11402a35d391SEmmanuel Vadot } 11412a35d391SEmmanuel Vadot } 11422a35d391SEmmanuel Vadot 11432a35d391SEmmanuel Vadot static void 1144ca018790SMitchell Horne dwc_txstart(if_t ifp) 11452a35d391SEmmanuel Vadot { 1146ca018790SMitchell Horne struct dwc_softc *sc = if_getsoftc(ifp); 11472a35d391SEmmanuel Vadot 11482a35d391SEmmanuel Vadot DWC_LOCK(sc); 11492a35d391SEmmanuel Vadot dwc_txstart_locked(sc); 11502a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 11512a35d391SEmmanuel Vadot } 11522a35d391SEmmanuel Vadot 11532a35d391SEmmanuel Vadot static void 11542a35d391SEmmanuel Vadot dwc_init_locked(struct dwc_softc *sc) 11552a35d391SEmmanuel Vadot { 1156ca018790SMitchell Horne if_t ifp = sc->ifp; 11572a35d391SEmmanuel Vadot 11582a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 11592a35d391SEmmanuel Vadot 11603bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) 11612a35d391SEmmanuel Vadot return; 11622a35d391SEmmanuel Vadot 11632a35d391SEmmanuel Vadot dwc_setup_rxfilter(sc); 11642a35d391SEmmanuel Vadot dwc_setup_core(sc); 11652a35d391SEmmanuel Vadot dwc_enable_mac(sc, true); 11662a35d391SEmmanuel Vadot dwc_init_dma(sc); 11672a35d391SEmmanuel Vadot 11682a35d391SEmmanuel Vadot if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); 11695d43fd68SRuslan Bukin 11705d43fd68SRuslan Bukin /* 11712a35d391SEmmanuel Vadot * Call mii_mediachg() which will call back into dwc_miibus_statchg() 11722a35d391SEmmanuel Vadot * to set up the remaining config registers based on current media. 11735d43fd68SRuslan Bukin */ 11742a35d391SEmmanuel Vadot mii_mediachg(sc->mii_softc); 11752a35d391SEmmanuel Vadot callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); 11762a35d391SEmmanuel Vadot } 11772a35d391SEmmanuel Vadot 11782a35d391SEmmanuel Vadot static void 11792a35d391SEmmanuel Vadot dwc_init(void *if_softc) 11802a35d391SEmmanuel Vadot { 11812a35d391SEmmanuel Vadot struct dwc_softc *sc = if_softc; 11822a35d391SEmmanuel Vadot 11832a35d391SEmmanuel Vadot DWC_LOCK(sc); 11842a35d391SEmmanuel Vadot dwc_init_locked(sc); 11852a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 11862a35d391SEmmanuel Vadot } 11872a35d391SEmmanuel Vadot 11882a35d391SEmmanuel Vadot static void 11892a35d391SEmmanuel Vadot dwc_stop_locked(struct dwc_softc *sc) 11902a35d391SEmmanuel Vadot { 1191ca018790SMitchell Horne if_t ifp; 11922a35d391SEmmanuel Vadot 11932a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 11942a35d391SEmmanuel Vadot 11952a35d391SEmmanuel Vadot ifp = sc->ifp; 11963bbd11eeSEmmanuel Vadot if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 11972a35d391SEmmanuel Vadot sc->tx_watchdog_count = 0; 11982a35d391SEmmanuel Vadot sc->stats_harvest_count = 0; 11992a35d391SEmmanuel Vadot 12002a35d391SEmmanuel Vadot callout_stop(&sc->dwc_callout); 12012a35d391SEmmanuel Vadot 12022a35d391SEmmanuel Vadot dwc_stop_dma(sc); 12032a35d391SEmmanuel Vadot dwc_enable_mac(sc, false); 12042a35d391SEmmanuel Vadot } 12052a35d391SEmmanuel Vadot 12062a35d391SEmmanuel Vadot static int 1207ca018790SMitchell Horne dwc_ioctl(if_t ifp, u_long cmd, caddr_t data) 12082a35d391SEmmanuel Vadot { 12092a35d391SEmmanuel Vadot struct dwc_softc *sc; 12102a35d391SEmmanuel Vadot struct mii_data *mii; 12112a35d391SEmmanuel Vadot struct ifreq *ifr; 12123bbd11eeSEmmanuel Vadot int flags, mask, error; 12132a35d391SEmmanuel Vadot 1214ca018790SMitchell Horne sc = if_getsoftc(ifp); 12152a35d391SEmmanuel Vadot ifr = (struct ifreq *)data; 12162a35d391SEmmanuel Vadot 12172a35d391SEmmanuel Vadot error = 0; 12182a35d391SEmmanuel Vadot switch (cmd) { 12192a35d391SEmmanuel Vadot case SIOCSIFFLAGS: 12202a35d391SEmmanuel Vadot DWC_LOCK(sc); 12213bbd11eeSEmmanuel Vadot if (if_getflags(ifp) & IFF_UP) { 12223bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 12233bbd11eeSEmmanuel Vadot flags = if_getflags(ifp) ^ sc->if_flags; 12243bbd11eeSEmmanuel Vadot if ((flags & (IFF_PROMISC|IFF_ALLMULTI)) != 0) 12252a35d391SEmmanuel Vadot dwc_setup_rxfilter(sc); 12265d43fd68SRuslan Bukin } else { 12272a35d391SEmmanuel Vadot if (!sc->is_detaching) 12282a35d391SEmmanuel Vadot dwc_init_locked(sc); 12292a35d391SEmmanuel Vadot } 12302a35d391SEmmanuel Vadot } else { 12313bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) 12322a35d391SEmmanuel Vadot dwc_stop_locked(sc); 12332a35d391SEmmanuel Vadot } 12343bbd11eeSEmmanuel Vadot sc->if_flags = if_getflags(ifp); 12352a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 12362a35d391SEmmanuel Vadot break; 12372a35d391SEmmanuel Vadot case SIOCADDMULTI: 12382a35d391SEmmanuel Vadot case SIOCDELMULTI: 12393bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 12402a35d391SEmmanuel Vadot DWC_LOCK(sc); 12412a35d391SEmmanuel Vadot dwc_setup_rxfilter(sc); 12422a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 12432a35d391SEmmanuel Vadot } 12442a35d391SEmmanuel Vadot break; 12452a35d391SEmmanuel Vadot case SIOCSIFMEDIA: 12462a35d391SEmmanuel Vadot case SIOCGIFMEDIA: 12472a35d391SEmmanuel Vadot mii = sc->mii_softc; 12482a35d391SEmmanuel Vadot error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); 12492a35d391SEmmanuel Vadot break; 12502a35d391SEmmanuel Vadot case SIOCSIFCAP: 12513bbd11eeSEmmanuel Vadot mask = ifr->ifr_reqcap ^ if_getcapenable(ifp); 12522a35d391SEmmanuel Vadot if (mask & IFCAP_VLAN_MTU) { 12532a35d391SEmmanuel Vadot /* No work to do except acknowledge the change took */ 12543bbd11eeSEmmanuel Vadot if_togglecapenable(ifp, IFCAP_VLAN_MTU); 12552a35d391SEmmanuel Vadot } 125698ea5a7bSEmmanuel Vadot if (mask & IFCAP_RXCSUM) 125798ea5a7bSEmmanuel Vadot if_togglecapenable(ifp, IFCAP_RXCSUM); 125898ea5a7bSEmmanuel Vadot if (mask & IFCAP_TXCSUM) 125998ea5a7bSEmmanuel Vadot if_togglecapenable(ifp, IFCAP_TXCSUM); 126098ea5a7bSEmmanuel Vadot if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0) 126198ea5a7bSEmmanuel Vadot if_sethwassistbits(ifp, CSUM_IP | CSUM_UDP | CSUM_TCP, 0); 126298ea5a7bSEmmanuel Vadot else 126398ea5a7bSEmmanuel Vadot if_sethwassistbits(ifp, 0, CSUM_IP | CSUM_UDP | CSUM_TCP); 12642a35d391SEmmanuel Vadot break; 12652a35d391SEmmanuel Vadot 12662a35d391SEmmanuel Vadot default: 12672a35d391SEmmanuel Vadot error = ether_ioctl(ifp, cmd, data); 12682a35d391SEmmanuel Vadot break; 12692a35d391SEmmanuel Vadot } 12702a35d391SEmmanuel Vadot 12712a35d391SEmmanuel Vadot return (error); 12722a35d391SEmmanuel Vadot } 12732a35d391SEmmanuel Vadot 12742a35d391SEmmanuel Vadot /* 12752a35d391SEmmanuel Vadot * Interrupts functions 12762a35d391SEmmanuel Vadot */ 12772a35d391SEmmanuel Vadot 12782a35d391SEmmanuel Vadot static void 12792a35d391SEmmanuel Vadot dwc_txfinish_locked(struct dwc_softc *sc) 12802a35d391SEmmanuel Vadot { 12812a35d391SEmmanuel Vadot struct dwc_bufmap *bmap; 12822a35d391SEmmanuel Vadot struct dwc_hwdesc *desc; 1283ca018790SMitchell Horne if_t ifp; 1284e5232621SOleksandr Tymoshenko int idx, last_idx; 1285e5232621SOleksandr Tymoshenko bool map_finished; 12862a35d391SEmmanuel Vadot 12872a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 12882a35d391SEmmanuel Vadot 12892a35d391SEmmanuel Vadot ifp = sc->ifp; 1290e5232621SOleksandr Tymoshenko /* check if all descriptors of the map are done */ 1291e5232621SOleksandr Tymoshenko while (sc->tx_map_tail != sc->tx_map_head) { 1292e5232621SOleksandr Tymoshenko map_finished = true; 1293e5232621SOleksandr Tymoshenko bmap = &sc->txbuf_map[sc->tx_map_tail]; 1294e5232621SOleksandr Tymoshenko idx = sc->tx_desc_tail; 1295e5232621SOleksandr Tymoshenko last_idx = next_txidx(sc, bmap->last_desc_idx); 1296e5232621SOleksandr Tymoshenko while (idx != last_idx) { 1297e5232621SOleksandr Tymoshenko desc = &sc->txdesc_ring[idx]; 1298e5232621SOleksandr Tymoshenko if ((desc->desc0 & TDESC0_OWN) != 0) { 1299e5232621SOleksandr Tymoshenko map_finished = false; 13002a35d391SEmmanuel Vadot break; 1301e5232621SOleksandr Tymoshenko } 1302e5232621SOleksandr Tymoshenko idx = next_txidx(sc, idx); 1303e5232621SOleksandr Tymoshenko } 1304e5232621SOleksandr Tymoshenko 1305e5232621SOleksandr Tymoshenko if (!map_finished) 1306e5232621SOleksandr Tymoshenko break; 13072a35d391SEmmanuel Vadot bus_dmamap_sync(sc->txbuf_tag, bmap->map, 13082a35d391SEmmanuel Vadot BUS_DMASYNC_POSTWRITE); 13092a35d391SEmmanuel Vadot bus_dmamap_unload(sc->txbuf_tag, bmap->map); 13102a35d391SEmmanuel Vadot m_freem(bmap->mbuf); 13112a35d391SEmmanuel Vadot bmap->mbuf = NULL; 1312e5232621SOleksandr Tymoshenko sc->tx_mapcount--; 1313e5232621SOleksandr Tymoshenko while (sc->tx_desc_tail != last_idx) { 1314e5232621SOleksandr Tymoshenko dwc_setup_txdesc(sc, sc->tx_desc_tail, 0, 0, 0, false, false); 1315e5232621SOleksandr Tymoshenko sc->tx_desc_tail = next_txidx(sc, sc->tx_desc_tail); 1316e5232621SOleksandr Tymoshenko } 1317e5232621SOleksandr Tymoshenko sc->tx_map_tail = next_txidx(sc, sc->tx_map_tail); 13183bbd11eeSEmmanuel Vadot if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); 13192a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 13202a35d391SEmmanuel Vadot } 13212a35d391SEmmanuel Vadot 13222a35d391SEmmanuel Vadot /* If there are no buffers outstanding, muzzle the watchdog. */ 1323e5232621SOleksandr Tymoshenko if (sc->tx_desc_tail == sc->tx_desc_head) { 13242a35d391SEmmanuel Vadot sc->tx_watchdog_count = 0; 13255d43fd68SRuslan Bukin } 13265d43fd68SRuslan Bukin } 13275d43fd68SRuslan Bukin 13282a35d391SEmmanuel Vadot static void 13292a35d391SEmmanuel Vadot dwc_rxfinish_locked(struct dwc_softc *sc) 13302a35d391SEmmanuel Vadot { 13312a35d391SEmmanuel Vadot struct mbuf *m; 13322a35d391SEmmanuel Vadot int error, idx; 13332a35d391SEmmanuel Vadot struct dwc_hwdesc *desc; 13342a35d391SEmmanuel Vadot 13352a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 13362a35d391SEmmanuel Vadot for (;;) { 13372a35d391SEmmanuel Vadot idx = sc->rx_idx; 13382a35d391SEmmanuel Vadot desc = sc->rxdesc_ring + idx; 13392a35d391SEmmanuel Vadot if ((desc->desc0 & RDESC0_OWN) != 0) 13402a35d391SEmmanuel Vadot break; 13412a35d391SEmmanuel Vadot 13422a35d391SEmmanuel Vadot m = dwc_rxfinish_one(sc, desc, sc->rxbuf_map + idx); 13432a35d391SEmmanuel Vadot if (m == NULL) { 13442a35d391SEmmanuel Vadot wmb(); 13452a35d391SEmmanuel Vadot desc->desc0 = RDESC0_OWN; 13462a35d391SEmmanuel Vadot wmb(); 13472a35d391SEmmanuel Vadot } else { 13482a35d391SEmmanuel Vadot /* We cannot create hole in RX ring */ 13492a35d391SEmmanuel Vadot error = dwc_setup_rxbuf(sc, idx, m); 13502a35d391SEmmanuel Vadot if (error != 0) 13512a35d391SEmmanuel Vadot panic("dwc_setup_rxbuf failed: error %d\n", 13522a35d391SEmmanuel Vadot error); 135398ea5a7bSEmmanuel Vadot 13542a35d391SEmmanuel Vadot } 13552a35d391SEmmanuel Vadot sc->rx_idx = next_rxidx(sc, sc->rx_idx); 13562a35d391SEmmanuel Vadot } 13572a35d391SEmmanuel Vadot } 13582a35d391SEmmanuel Vadot 13592a35d391SEmmanuel Vadot static void 13602a35d391SEmmanuel Vadot dwc_intr(void *arg) 13612a35d391SEmmanuel Vadot { 13622a35d391SEmmanuel Vadot struct dwc_softc *sc; 13632a35d391SEmmanuel Vadot uint32_t reg; 13642a35d391SEmmanuel Vadot 13652a35d391SEmmanuel Vadot sc = arg; 13662a35d391SEmmanuel Vadot 13672a35d391SEmmanuel Vadot DWC_LOCK(sc); 13682a35d391SEmmanuel Vadot 13692a35d391SEmmanuel Vadot reg = READ4(sc, INTERRUPT_STATUS); 13702a35d391SEmmanuel Vadot if (reg) 13712a35d391SEmmanuel Vadot READ4(sc, SGMII_RGMII_SMII_CTRL_STATUS); 13722a35d391SEmmanuel Vadot 13732a35d391SEmmanuel Vadot reg = READ4(sc, DMA_STATUS); 13742a35d391SEmmanuel Vadot if (reg & DMA_STATUS_NIS) { 13752a35d391SEmmanuel Vadot if (reg & DMA_STATUS_RI) 13762a35d391SEmmanuel Vadot dwc_rxfinish_locked(sc); 13772a35d391SEmmanuel Vadot 13782a35d391SEmmanuel Vadot if (reg & DMA_STATUS_TI) { 13792a35d391SEmmanuel Vadot dwc_txfinish_locked(sc); 13802a35d391SEmmanuel Vadot dwc_txstart_locked(sc); 13812a35d391SEmmanuel Vadot } 13822a35d391SEmmanuel Vadot } 13832a35d391SEmmanuel Vadot 13842a35d391SEmmanuel Vadot if (reg & DMA_STATUS_AIS) { 13852a35d391SEmmanuel Vadot if (reg & DMA_STATUS_FBI) { 13862a35d391SEmmanuel Vadot /* Fatal bus error */ 13872a35d391SEmmanuel Vadot device_printf(sc->dev, 13882a35d391SEmmanuel Vadot "Ethernet DMA error, restarting controller.\n"); 13892a35d391SEmmanuel Vadot dwc_stop_locked(sc); 13902a35d391SEmmanuel Vadot dwc_init_locked(sc); 13912a35d391SEmmanuel Vadot } 13922a35d391SEmmanuel Vadot } 13932a35d391SEmmanuel Vadot 13942a35d391SEmmanuel Vadot WRITE4(sc, DMA_STATUS, reg & DMA_STATUS_INTR_MASK); 13952a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 13962a35d391SEmmanuel Vadot } 13972a35d391SEmmanuel Vadot 13982a35d391SEmmanuel Vadot /* 13992a35d391SEmmanuel Vadot * Stats 14002a35d391SEmmanuel Vadot */ 14012a35d391SEmmanuel Vadot 14022a35d391SEmmanuel Vadot static void dwc_clear_stats(struct dwc_softc *sc) 14032a35d391SEmmanuel Vadot { 14042a35d391SEmmanuel Vadot uint32_t reg; 14052a35d391SEmmanuel Vadot 14062a35d391SEmmanuel Vadot reg = READ4(sc, MMC_CONTROL); 14072a35d391SEmmanuel Vadot reg |= (MMC_CONTROL_CNTRST); 14082a35d391SEmmanuel Vadot WRITE4(sc, MMC_CONTROL, reg); 14092a35d391SEmmanuel Vadot } 14102a35d391SEmmanuel Vadot 14112a35d391SEmmanuel Vadot static void 14122a35d391SEmmanuel Vadot dwc_harvest_stats(struct dwc_softc *sc) 14132a35d391SEmmanuel Vadot { 1414ca018790SMitchell Horne if_t ifp; 14152a35d391SEmmanuel Vadot 14162a35d391SEmmanuel Vadot /* We don't need to harvest too often. */ 14172a35d391SEmmanuel Vadot if (++sc->stats_harvest_count < STATS_HARVEST_INTERVAL) 14182a35d391SEmmanuel Vadot return; 14192a35d391SEmmanuel Vadot 14202a35d391SEmmanuel Vadot sc->stats_harvest_count = 0; 14212a35d391SEmmanuel Vadot ifp = sc->ifp; 14222a35d391SEmmanuel Vadot 14232a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_IPACKETS, READ4(sc, RXFRAMECOUNT_GB)); 14242a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_IMCASTS, READ4(sc, RXMULTICASTFRAMES_G)); 14252a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_IERRORS, 14262a35d391SEmmanuel Vadot READ4(sc, RXOVERSIZE_G) + READ4(sc, RXUNDERSIZE_G) + 14272a35d391SEmmanuel Vadot READ4(sc, RXCRCERROR) + READ4(sc, RXALIGNMENTERROR) + 14282a35d391SEmmanuel Vadot READ4(sc, RXRUNTERROR) + READ4(sc, RXJABBERERROR) + 14292a35d391SEmmanuel Vadot READ4(sc, RXLENGTHERROR)); 14302a35d391SEmmanuel Vadot 14312a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OPACKETS, READ4(sc, TXFRAMECOUNT_G)); 14322a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OMCASTS, READ4(sc, TXMULTICASTFRAMES_G)); 14332a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OERRORS, 14342a35d391SEmmanuel Vadot READ4(sc, TXOVERSIZE_G) + READ4(sc, TXEXCESSDEF) + 14352a35d391SEmmanuel Vadot READ4(sc, TXCARRIERERR) + READ4(sc, TXUNDERFLOWERROR)); 14362a35d391SEmmanuel Vadot 14372a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 14382a35d391SEmmanuel Vadot READ4(sc, TXEXESSCOL) + READ4(sc, TXLATECOL)); 14392a35d391SEmmanuel Vadot 14402a35d391SEmmanuel Vadot dwc_clear_stats(sc); 14412a35d391SEmmanuel Vadot } 14422a35d391SEmmanuel Vadot 14432a35d391SEmmanuel Vadot static void 14442a35d391SEmmanuel Vadot dwc_tick(void *arg) 14452a35d391SEmmanuel Vadot { 14462a35d391SEmmanuel Vadot struct dwc_softc *sc; 1447ca018790SMitchell Horne if_t ifp; 14482a35d391SEmmanuel Vadot int link_was_up; 14492a35d391SEmmanuel Vadot 14502a35d391SEmmanuel Vadot sc = arg; 14512a35d391SEmmanuel Vadot 14522a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 14532a35d391SEmmanuel Vadot 14542a35d391SEmmanuel Vadot ifp = sc->ifp; 14552a35d391SEmmanuel Vadot 14563bbd11eeSEmmanuel Vadot if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) 14572a35d391SEmmanuel Vadot return; 14582a35d391SEmmanuel Vadot 14592a35d391SEmmanuel Vadot /* 14602a35d391SEmmanuel Vadot * Typical tx watchdog. If this fires it indicates that we enqueued 14612a35d391SEmmanuel Vadot * packets for output and never got a txdone interrupt for them. Maybe 14622a35d391SEmmanuel Vadot * it's a missed interrupt somehow, just pretend we got one. 14632a35d391SEmmanuel Vadot */ 14642a35d391SEmmanuel Vadot if (sc->tx_watchdog_count > 0) { 14652a35d391SEmmanuel Vadot if (--sc->tx_watchdog_count == 0) { 14662a35d391SEmmanuel Vadot dwc_txfinish_locked(sc); 14672a35d391SEmmanuel Vadot } 14682a35d391SEmmanuel Vadot } 14692a35d391SEmmanuel Vadot 14702a35d391SEmmanuel Vadot /* Gather stats from hardware counters. */ 14712a35d391SEmmanuel Vadot dwc_harvest_stats(sc); 14722a35d391SEmmanuel Vadot 14732a35d391SEmmanuel Vadot /* Check the media status. */ 14742a35d391SEmmanuel Vadot link_was_up = sc->link_is_up; 14752a35d391SEmmanuel Vadot mii_tick(sc->mii_softc); 14762a35d391SEmmanuel Vadot if (sc->link_is_up && !link_was_up) 14772a35d391SEmmanuel Vadot dwc_txstart_locked(sc); 14782a35d391SEmmanuel Vadot 14792a35d391SEmmanuel Vadot /* Schedule another check one second from now. */ 14802a35d391SEmmanuel Vadot callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); 14812a35d391SEmmanuel Vadot } 14822a35d391SEmmanuel Vadot 14832a35d391SEmmanuel Vadot /* 14842a35d391SEmmanuel Vadot * Probe/Attach functions 14852a35d391SEmmanuel Vadot */ 14862a35d391SEmmanuel Vadot 1487d7acb49aSJared McNeill #define GPIO_ACTIVE_LOW 1 1488d7acb49aSJared McNeill 1489d7acb49aSJared McNeill static int 1490d7acb49aSJared McNeill dwc_reset(device_t dev) 1491d7acb49aSJared McNeill { 1492d7acb49aSJared McNeill pcell_t gpio_prop[4]; 1493d7acb49aSJared McNeill pcell_t delay_prop[3]; 1494d7acb49aSJared McNeill phandle_t node, gpio_node; 1495d7acb49aSJared McNeill device_t gpio; 1496d7acb49aSJared McNeill uint32_t pin, flags; 1497d7acb49aSJared McNeill uint32_t pin_value; 1498d7acb49aSJared McNeill 1499d7acb49aSJared McNeill node = ofw_bus_get_node(dev); 1500d7acb49aSJared McNeill if (OF_getencprop(node, "snps,reset-gpio", 1501d7acb49aSJared McNeill gpio_prop, sizeof(gpio_prop)) <= 0) 1502d7acb49aSJared McNeill return (0); 1503d7acb49aSJared McNeill 1504d7acb49aSJared McNeill if (OF_getencprop(node, "snps,reset-delays-us", 1505d7acb49aSJared McNeill delay_prop, sizeof(delay_prop)) <= 0) { 1506d7acb49aSJared McNeill device_printf(dev, 1507d7acb49aSJared McNeill "Wrong property for snps,reset-delays-us"); 1508d7acb49aSJared McNeill return (ENXIO); 1509d7acb49aSJared McNeill } 1510d7acb49aSJared McNeill 1511d7acb49aSJared McNeill gpio_node = OF_node_from_xref(gpio_prop[0]); 1512d7acb49aSJared McNeill if ((gpio = OF_device_from_xref(gpio_prop[0])) == NULL) { 1513d7acb49aSJared McNeill device_printf(dev, 1514d7acb49aSJared McNeill "Can't find gpio controller for phy reset\n"); 1515d7acb49aSJared McNeill return (ENXIO); 1516d7acb49aSJared McNeill } 1517d7acb49aSJared McNeill 1518d7acb49aSJared McNeill if (GPIO_MAP_GPIOS(gpio, node, gpio_node, 151973a1170aSPedro F. Giffuni nitems(gpio_prop) - 1, 1520d7acb49aSJared McNeill gpio_prop + 1, &pin, &flags) != 0) { 1521d7acb49aSJared McNeill device_printf(dev, "Can't map gpio for phy reset\n"); 1522d7acb49aSJared McNeill return (ENXIO); 1523d7acb49aSJared McNeill } 1524d7acb49aSJared McNeill 1525d7acb49aSJared McNeill pin_value = GPIO_PIN_LOW; 1526d7acb49aSJared McNeill if (OF_hasprop(node, "snps,reset-active-low")) 1527d7acb49aSJared McNeill pin_value = GPIO_PIN_HIGH; 1528d7acb49aSJared McNeill 1529d7acb49aSJared McNeill GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT); 1530d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 1531c069412eSEmmanuel Vadot DELAY(delay_prop[0] * 5); 1532d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, !pin_value); 1533c069412eSEmmanuel Vadot DELAY(delay_prop[1] * 5); 1534d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 1535c069412eSEmmanuel Vadot DELAY(delay_prop[2] * 5); 1536d7acb49aSJared McNeill 1537d7acb49aSJared McNeill return (0); 1538d7acb49aSJared McNeill } 1539d7acb49aSJared McNeill 15406a05f063SJared McNeill static int 15416a05f063SJared McNeill dwc_clock_init(device_t dev) 15426a05f063SJared McNeill { 15436a05f063SJared McNeill hwreset_t rst; 15446a05f063SJared McNeill clk_t clk; 15456a05f063SJared McNeill int error; 1546824cfb47SOleksandr Tymoshenko int64_t freq; 15476a05f063SJared McNeill 1548824cfb47SOleksandr Tymoshenko /* Enable clocks */ 1549dac93553SMichal Meloun if (clk_get_by_ofw_name(dev, 0, "stmmaceth", &clk) == 0) { 15506a05f063SJared McNeill error = clk_enable(clk); 15516a05f063SJared McNeill if (error != 0) { 15526a05f063SJared McNeill device_printf(dev, "could not enable main clock\n"); 15536a05f063SJared McNeill return (error); 15546a05f063SJared McNeill } 1555824cfb47SOleksandr Tymoshenko if (bootverbose) { 1556824cfb47SOleksandr Tymoshenko clk_get_freq(clk, &freq); 1557fc254a2eSLi-Wen Hsu device_printf(dev, "MAC clock(%s) freq: %jd\n", 1558fc254a2eSLi-Wen Hsu clk_get_name(clk), (intmax_t)freq); 1559824cfb47SOleksandr Tymoshenko } 1560824cfb47SOleksandr Tymoshenko } 1561824cfb47SOleksandr Tymoshenko else { 1562824cfb47SOleksandr Tymoshenko device_printf(dev, "could not find clock stmmaceth\n"); 15636a05f063SJared McNeill } 15646a05f063SJared McNeill 15656a05f063SJared McNeill /* De-assert reset */ 1566dac93553SMichal Meloun if (hwreset_get_by_ofw_name(dev, 0, "stmmaceth", &rst) == 0) { 15676a05f063SJared McNeill error = hwreset_deassert(rst); 15686a05f063SJared McNeill if (error != 0) { 15696a05f063SJared McNeill device_printf(dev, "could not de-assert reset\n"); 15706a05f063SJared McNeill return (error); 15716a05f063SJared McNeill } 15726a05f063SJared McNeill } 15736a05f063SJared McNeill 15746a05f063SJared McNeill return (0); 15756a05f063SJared McNeill } 15766a05f063SJared McNeill 15775d43fd68SRuslan Bukin static int 15785d43fd68SRuslan Bukin dwc_probe(device_t dev) 15795d43fd68SRuslan Bukin { 15805d43fd68SRuslan Bukin 15815d43fd68SRuslan Bukin if (!ofw_bus_status_okay(dev)) 15825d43fd68SRuslan Bukin return (ENXIO); 15835d43fd68SRuslan Bukin 15845d43fd68SRuslan Bukin if (!ofw_bus_is_compatible(dev, "snps,dwmac")) 15855d43fd68SRuslan Bukin return (ENXIO); 15865d43fd68SRuslan Bukin 15875d43fd68SRuslan Bukin device_set_desc(dev, "Gigabit Ethernet Controller"); 15885d43fd68SRuslan Bukin return (BUS_PROBE_DEFAULT); 15895d43fd68SRuslan Bukin } 15905d43fd68SRuslan Bukin 15915d43fd68SRuslan Bukin static int 15925d43fd68SRuslan Bukin dwc_attach(device_t dev) 15935d43fd68SRuslan Bukin { 15945d43fd68SRuslan Bukin uint8_t macaddr[ETHER_ADDR_LEN]; 15955d43fd68SRuslan Bukin struct dwc_softc *sc; 1596ca018790SMitchell Horne if_t ifp; 1597ff0752c8SLuiz Otavio O Souza int error, i; 1598ff0752c8SLuiz Otavio O Souza uint32_t reg; 1599824cfb47SOleksandr Tymoshenko phandle_t node; 16003e5cd548SEmmanuel Vadot uint32_t txpbl, rxpbl, pbl; 16015e38d9e4SEmmanuel Vadot bool nopblx8 = false; 16025e38d9e4SEmmanuel Vadot bool fixed_burst = false; 16035d43fd68SRuslan Bukin 16045d43fd68SRuslan Bukin sc = device_get_softc(dev); 16055d43fd68SRuslan Bukin sc->dev = dev; 16065d43fd68SRuslan Bukin sc->rx_idx = 0; 1607e5232621SOleksandr Tymoshenko sc->tx_desccount = TX_DESC_COUNT; 1608e5232621SOleksandr Tymoshenko sc->tx_mapcount = 0; 16095df53927SLuiz Otavio O Souza sc->mii_clk = IF_DWC_MII_CLK(dev); 16105df53927SLuiz Otavio O Souza sc->mactype = IF_DWC_MAC_TYPE(dev); 16115df53927SLuiz Otavio O Souza 1612824cfb47SOleksandr Tymoshenko node = ofw_bus_get_node(dev); 1613f77d8d10SEmmanuel Vadot switch (mii_fdt_get_contype(node)) { 1614f77d8d10SEmmanuel Vadot case MII_CONTYPE_RGMII: 1615f77d8d10SEmmanuel Vadot case MII_CONTYPE_RGMII_ID: 1616f77d8d10SEmmanuel Vadot case MII_CONTYPE_RGMII_RXID: 1617f77d8d10SEmmanuel Vadot case MII_CONTYPE_RGMII_TXID: 1618824cfb47SOleksandr Tymoshenko sc->phy_mode = PHY_MODE_RGMII; 1619f77d8d10SEmmanuel Vadot break; 1620f77d8d10SEmmanuel Vadot case MII_CONTYPE_RMII: 1621824cfb47SOleksandr Tymoshenko sc->phy_mode = PHY_MODE_RMII; 1622f77d8d10SEmmanuel Vadot break; 1623da6252a6SEmmanuel Vadot case MII_CONTYPE_MII: 1624da6252a6SEmmanuel Vadot sc->phy_mode = PHY_MODE_MII; 1625da6252a6SEmmanuel Vadot break; 1626f77d8d10SEmmanuel Vadot default: 1627f77d8d10SEmmanuel Vadot device_printf(dev, "Unsupported MII type\n"); 1628f77d8d10SEmmanuel Vadot return (ENXIO); 1629824cfb47SOleksandr Tymoshenko } 1630824cfb47SOleksandr Tymoshenko 16313e5cd548SEmmanuel Vadot if (OF_getencprop(node, "snps,pbl", &pbl, sizeof(uint32_t)) <= 0) 16323e5cd548SEmmanuel Vadot pbl = BUS_MODE_DEFAULT_PBL; 16335e38d9e4SEmmanuel Vadot if (OF_getencprop(node, "snps,txpbl", &txpbl, sizeof(uint32_t)) <= 0) 16343e5cd548SEmmanuel Vadot txpbl = pbl; 16355e38d9e4SEmmanuel Vadot if (OF_getencprop(node, "snps,rxpbl", &rxpbl, sizeof(uint32_t)) <= 0) 16363e5cd548SEmmanuel Vadot rxpbl = pbl; 16375e38d9e4SEmmanuel Vadot if (OF_hasprop(node, "snps,no-pbl-x8") == 1) 16385e38d9e4SEmmanuel Vadot nopblx8 = true; 16395e38d9e4SEmmanuel Vadot if (OF_hasprop(node, "snps,fixed-burst") == 1) 16405e38d9e4SEmmanuel Vadot fixed_burst = true; 16415e38d9e4SEmmanuel Vadot 16425df53927SLuiz Otavio O Souza if (IF_DWC_INIT(dev) != 0) 16435df53927SLuiz Otavio O Souza return (ENXIO); 16445d43fd68SRuslan Bukin 16456a05f063SJared McNeill if (dwc_clock_init(dev) != 0) 16466a05f063SJared McNeill return (ENXIO); 16476a05f063SJared McNeill 16485d43fd68SRuslan Bukin if (bus_alloc_resources(dev, dwc_spec, sc->res)) { 16495d43fd68SRuslan Bukin device_printf(dev, "could not allocate resources\n"); 16505d43fd68SRuslan Bukin return (ENXIO); 16515d43fd68SRuslan Bukin } 16525d43fd68SRuslan Bukin 16535d43fd68SRuslan Bukin /* Read MAC before reset */ 1654f88e0af6SEmmanuel Vadot dwc_get_hwaddr(sc, macaddr); 16555d43fd68SRuslan Bukin 1656d7acb49aSJared McNeill /* Reset the PHY if needed */ 1657d7acb49aSJared McNeill if (dwc_reset(dev) != 0) { 1658d7acb49aSJared McNeill device_printf(dev, "Can't reset the PHY\n"); 165930f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 1660d7acb49aSJared McNeill return (ENXIO); 1661d7acb49aSJared McNeill } 1662d7acb49aSJared McNeill 16635d43fd68SRuslan Bukin /* Reset */ 16645d43fd68SRuslan Bukin reg = READ4(sc, BUS_MODE); 16655d43fd68SRuslan Bukin reg |= (BUS_MODE_SWR); 16665d43fd68SRuslan Bukin WRITE4(sc, BUS_MODE, reg); 16675d43fd68SRuslan Bukin 1668d8e5258dSRuslan Bukin for (i = 0; i < MAC_RESET_TIMEOUT; i++) { 16695d43fd68SRuslan Bukin if ((READ4(sc, BUS_MODE) & BUS_MODE_SWR) == 0) 16705d43fd68SRuslan Bukin break; 16715d43fd68SRuslan Bukin DELAY(10); 16725d43fd68SRuslan Bukin } 1673d8e5258dSRuslan Bukin if (i >= MAC_RESET_TIMEOUT) { 16745d43fd68SRuslan Bukin device_printf(sc->dev, "Can't reset DWC.\n"); 167530f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 16765d43fd68SRuslan Bukin return (ENXIO); 16775d43fd68SRuslan Bukin } 16785d43fd68SRuslan Bukin 16795e38d9e4SEmmanuel Vadot reg = BUS_MODE_USP; 16805e38d9e4SEmmanuel Vadot if (!nopblx8) 16815e38d9e4SEmmanuel Vadot reg |= BUS_MODE_EIGHTXPBL; 16825e38d9e4SEmmanuel Vadot reg |= (txpbl << BUS_MODE_PBL_SHIFT); 16835e38d9e4SEmmanuel Vadot reg |= (rxpbl << BUS_MODE_RPBL_SHIFT); 16845e38d9e4SEmmanuel Vadot if (fixed_burst) 16855e38d9e4SEmmanuel Vadot reg |= BUS_MODE_FIXEDBURST; 16863e5cd548SEmmanuel Vadot 16875d43fd68SRuslan Bukin WRITE4(sc, BUS_MODE, reg); 16885d43fd68SRuslan Bukin 16895d43fd68SRuslan Bukin /* 16905d43fd68SRuslan Bukin * DMA must be stop while changing descriptor list addresses. 16915d43fd68SRuslan Bukin */ 16925d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 16935d43fd68SRuslan Bukin reg &= ~(MODE_ST | MODE_SR); 16945d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 16955d43fd68SRuslan Bukin 169630f16ad4SEmmanuel Vadot if (setup_dma(sc)) { 169730f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 16985d43fd68SRuslan Bukin return (ENXIO); 169930f16ad4SEmmanuel Vadot } 17005d43fd68SRuslan Bukin 17015d43fd68SRuslan Bukin /* Setup addresses */ 17025d43fd68SRuslan Bukin WRITE4(sc, RX_DESCR_LIST_ADDR, sc->rxdesc_ring_paddr); 17035d43fd68SRuslan Bukin WRITE4(sc, TX_DESCR_LIST_ADDR, sc->txdesc_ring_paddr); 17045d43fd68SRuslan Bukin 1705d8e5258dSRuslan Bukin mtx_init(&sc->mtx, device_get_nameunit(sc->dev), 1706d8e5258dSRuslan Bukin MTX_NETWORK_LOCK, MTX_DEF); 1707d8e5258dSRuslan Bukin 1708d8e5258dSRuslan Bukin callout_init_mtx(&sc->dwc_callout, &sc->mtx, 0); 1709d8e5258dSRuslan Bukin 1710d8e5258dSRuslan Bukin /* Setup interrupt handler. */ 1711d8e5258dSRuslan Bukin error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE, 1712d8e5258dSRuslan Bukin NULL, dwc_intr, sc, &sc->intr_cookie); 1713d8e5258dSRuslan Bukin if (error != 0) { 1714d8e5258dSRuslan Bukin device_printf(dev, "could not setup interrupt handler.\n"); 171530f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 1716d8e5258dSRuslan Bukin return (ENXIO); 1717d8e5258dSRuslan Bukin } 1718d8e5258dSRuslan Bukin 17195d43fd68SRuslan Bukin /* Set up the ethernet interface. */ 17205d43fd68SRuslan Bukin sc->ifp = ifp = if_alloc(IFT_ETHER); 17215d43fd68SRuslan Bukin 1722ca018790SMitchell Horne if_setsoftc(ifp, sc); 17235d43fd68SRuslan Bukin if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 17243bbd11eeSEmmanuel Vadot if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); 17253bbd11eeSEmmanuel Vadot if_setstartfn(ifp, dwc_txstart); 17263bbd11eeSEmmanuel Vadot if_setioctlfn(ifp, dwc_ioctl); 17273bbd11eeSEmmanuel Vadot if_setinitfn(ifp, dwc_init); 1728e5232621SOleksandr Tymoshenko if_setsendqlen(ifp, TX_MAP_COUNT - 1); 17293bbd11eeSEmmanuel Vadot if_setsendqready(sc->ifp); 173098ea5a7bSEmmanuel Vadot if_sethwassist(sc->ifp, CSUM_IP | CSUM_UDP | CSUM_TCP); 173198ea5a7bSEmmanuel Vadot if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM); 17323bbd11eeSEmmanuel Vadot if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp)); 17335d43fd68SRuslan Bukin 17345d43fd68SRuslan Bukin /* Attach the mii driver. */ 17355d43fd68SRuslan Bukin error = mii_attach(dev, &sc->miibus, ifp, dwc_media_change, 17365d43fd68SRuslan Bukin dwc_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, 17375d43fd68SRuslan Bukin MII_OFFSET_ANY, 0); 17385d43fd68SRuslan Bukin 17395d43fd68SRuslan Bukin if (error != 0) { 17405d43fd68SRuslan Bukin device_printf(dev, "PHY attach failed\n"); 174130f16ad4SEmmanuel Vadot bus_teardown_intr(dev, sc->res[1], sc->intr_cookie); 174230f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 17435d43fd68SRuslan Bukin return (ENXIO); 17445d43fd68SRuslan Bukin } 17455d43fd68SRuslan Bukin sc->mii_softc = device_get_softc(sc->miibus); 17465d43fd68SRuslan Bukin 17475d43fd68SRuslan Bukin /* All ready to run, attach the ethernet interface. */ 17485d43fd68SRuslan Bukin ether_ifattach(ifp, macaddr); 17495d43fd68SRuslan Bukin sc->is_attached = true; 17505d43fd68SRuslan Bukin 17515d43fd68SRuslan Bukin return (0); 17525d43fd68SRuslan Bukin } 17535d43fd68SRuslan Bukin 1754*27b39e58SMitchell Horne static int 1755*27b39e58SMitchell Horne dwc_detach(device_t dev) 1756*27b39e58SMitchell Horne { 1757*27b39e58SMitchell Horne struct dwc_softc *sc; 1758*27b39e58SMitchell Horne 1759*27b39e58SMitchell Horne sc = device_get_softc(dev); 1760*27b39e58SMitchell Horne 1761*27b39e58SMitchell Horne /* 1762*27b39e58SMitchell Horne * Disable and tear down interrupts before anything else, so we don't 1763*27b39e58SMitchell Horne * race with the handler. 1764*27b39e58SMitchell Horne */ 1765*27b39e58SMitchell Horne WRITE4(sc, INTERRUPT_ENABLE, 0); 1766*27b39e58SMitchell Horne if (sc->intr_cookie != NULL) { 1767*27b39e58SMitchell Horne bus_teardown_intr(dev, sc->res[1], sc->intr_cookie); 1768*27b39e58SMitchell Horne } 1769*27b39e58SMitchell Horne 1770*27b39e58SMitchell Horne if (sc->is_attached) { 1771*27b39e58SMitchell Horne DWC_LOCK(sc); 1772*27b39e58SMitchell Horne sc->is_detaching = true; 1773*27b39e58SMitchell Horne dwc_stop_locked(sc); 1774*27b39e58SMitchell Horne DWC_UNLOCK(sc); 1775*27b39e58SMitchell Horne callout_drain(&sc->dwc_callout); 1776*27b39e58SMitchell Horne ether_ifdetach(sc->ifp); 1777*27b39e58SMitchell Horne } 1778*27b39e58SMitchell Horne 1779*27b39e58SMitchell Horne if (sc->miibus != NULL) { 1780*27b39e58SMitchell Horne device_delete_child(dev, sc->miibus); 1781*27b39e58SMitchell Horne sc->miibus = NULL; 1782*27b39e58SMitchell Horne } 1783*27b39e58SMitchell Horne bus_generic_detach(dev); 1784*27b39e58SMitchell Horne 1785*27b39e58SMitchell Horne /* Free DMA descriptors */ 1786*27b39e58SMitchell Horne free_dma(sc); 1787*27b39e58SMitchell Horne 1788*27b39e58SMitchell Horne if (sc->ifp != NULL) { 1789*27b39e58SMitchell Horne if_free(sc->ifp); 1790*27b39e58SMitchell Horne sc->ifp = NULL; 1791*27b39e58SMitchell Horne } 1792*27b39e58SMitchell Horne 1793*27b39e58SMitchell Horne bus_release_resources(dev, dwc_spec, sc->res); 1794*27b39e58SMitchell Horne 1795*27b39e58SMitchell Horne mtx_destroy(&sc->mtx); 1796*27b39e58SMitchell Horne return (0); 1797*27b39e58SMitchell Horne } 1798*27b39e58SMitchell Horne 17995d43fd68SRuslan Bukin static device_method_t dwc_methods[] = { 18005d43fd68SRuslan Bukin DEVMETHOD(device_probe, dwc_probe), 18015d43fd68SRuslan Bukin DEVMETHOD(device_attach, dwc_attach), 1802*27b39e58SMitchell Horne DEVMETHOD(device_detach, dwc_detach), 18035d43fd68SRuslan Bukin 18045d43fd68SRuslan Bukin /* MII Interface */ 18055d43fd68SRuslan Bukin DEVMETHOD(miibus_readreg, dwc_miibus_read_reg), 18065d43fd68SRuslan Bukin DEVMETHOD(miibus_writereg, dwc_miibus_write_reg), 18075d43fd68SRuslan Bukin DEVMETHOD(miibus_statchg, dwc_miibus_statchg), 18085d43fd68SRuslan Bukin 18095d43fd68SRuslan Bukin { 0, 0 } 18105d43fd68SRuslan Bukin }; 18115d43fd68SRuslan Bukin 18125df53927SLuiz Otavio O Souza driver_t dwc_driver = { 18135d43fd68SRuslan Bukin "dwc", 18145d43fd68SRuslan Bukin dwc_methods, 18155d43fd68SRuslan Bukin sizeof(struct dwc_softc), 18165d43fd68SRuslan Bukin }; 18175d43fd68SRuslan Bukin 1818e55d0536SJohn Baldwin DRIVER_MODULE(dwc, simplebus, dwc_driver, 0, 0); 18193e38757dSJohn Baldwin DRIVER_MODULE(miibus, dwc, miibus_driver, 0, 0); 18205d43fd68SRuslan Bukin 18215d43fd68SRuslan Bukin MODULE_DEPEND(dwc, ether, 1, 1, 1); 18225d43fd68SRuslan Bukin MODULE_DEPEND(dwc, miibus, 1, 1, 1); 1823