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 #include <sys/param.h> 415d43fd68SRuslan Bukin #include <sys/systm.h> 425d43fd68SRuslan Bukin #include <sys/bus.h> 43d7acb49aSJared McNeill #include <sys/gpio.h> 445d43fd68SRuslan Bukin #include <sys/kernel.h> 455d43fd68SRuslan Bukin #include <sys/lock.h> 46da9a326bSLuiz Otavio O Souza #include <sys/malloc.h> 475d43fd68SRuslan Bukin #include <sys/mbuf.h> 48da9a326bSLuiz Otavio O Souza #include <sys/module.h> 495d43fd68SRuslan Bukin #include <sys/mutex.h> 50da9a326bSLuiz Otavio O Souza #include <sys/rman.h> 515d43fd68SRuslan Bukin #include <sys/socket.h> 525d43fd68SRuslan Bukin #include <sys/sockio.h> 535d43fd68SRuslan Bukin 545d43fd68SRuslan Bukin #include <net/bpf.h> 555d43fd68SRuslan Bukin #include <net/if.h> 565d43fd68SRuslan Bukin #include <net/ethernet.h> 575d43fd68SRuslan Bukin #include <net/if_dl.h> 585d43fd68SRuslan Bukin #include <net/if_media.h> 595d43fd68SRuslan Bukin #include <net/if_types.h> 605d43fd68SRuslan Bukin #include <net/if_var.h> 615d43fd68SRuslan Bukin 625d43fd68SRuslan Bukin #include <machine/bus.h> 635d43fd68SRuslan Bukin 64da9a326bSLuiz Otavio O Souza #include <dev/dwc/if_dwc.h> 655df53927SLuiz Otavio O Souza #include <dev/dwc/if_dwcvar.h> 665d43fd68SRuslan Bukin #include <dev/mii/mii.h> 675d43fd68SRuslan Bukin #include <dev/mii/miivar.h> 68da9a326bSLuiz Otavio O Souza #include <dev/ofw/ofw_bus.h> 69da9a326bSLuiz Otavio O Souza #include <dev/ofw/ofw_bus_subr.h> 70f77d8d10SEmmanuel Vadot #include <dev/mii/mii_fdt.h> 71da9a326bSLuiz Otavio O Souza 726a05f063SJared McNeill #include <dev/extres/clk/clk.h> 736a05f063SJared McNeill #include <dev/extres/hwreset/hwreset.h> 746a05f063SJared McNeill 755df53927SLuiz Otavio O Souza #include "if_dwc_if.h" 76d7acb49aSJared McNeill #include "gpio_if.h" 775d43fd68SRuslan Bukin #include "miibus_if.h" 785d43fd68SRuslan Bukin 795d43fd68SRuslan Bukin #define READ4(_sc, _reg) \ 805d43fd68SRuslan Bukin bus_read_4((_sc)->res[0], _reg) 815d43fd68SRuslan Bukin #define WRITE4(_sc, _reg, _val) \ 825d43fd68SRuslan Bukin bus_write_4((_sc)->res[0], _reg, _val) 835d43fd68SRuslan Bukin 84d8e5258dSRuslan Bukin #define MAC_RESET_TIMEOUT 100 855d43fd68SRuslan Bukin #define WATCHDOG_TIMEOUT_SECS 5 865d43fd68SRuslan Bukin #define STATS_HARVEST_INTERVAL 2 875d43fd68SRuslan Bukin 885d43fd68SRuslan Bukin #define DWC_LOCK(sc) mtx_lock(&(sc)->mtx) 895d43fd68SRuslan Bukin #define DWC_UNLOCK(sc) mtx_unlock(&(sc)->mtx) 90ff0752c8SLuiz Otavio O Souza #define DWC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED) 91ff0752c8SLuiz Otavio O Souza #define DWC_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED) 925d43fd68SRuslan Bukin 938d43a868SMichal Meloun /* TX descriptors - TDESC0 is almost unified */ 948d43a868SMichal Meloun #define TDESC0_OWN (1U << 31) 958d43a868SMichal Meloun #define TDESC0_IHE (1U << 16) /* IP Header Error */ 968d43a868SMichal Meloun #define TDESC0_ES (1U << 15) /* Error Summary */ 978d43a868SMichal Meloun #define TDESC0_JT (1U << 14) /* Jabber Timeout */ 988d43a868SMichal Meloun #define TDESC0_FF (1U << 13) /* Frame Flushed */ 998d43a868SMichal Meloun #define TDESC0_PCE (1U << 12) /* Payload Checksum Error */ 1008d43a868SMichal Meloun #define TDESC0_LOC (1U << 11) /* Loss of Carrier */ 1018d43a868SMichal Meloun #define TDESC0_NC (1U << 10) /* No Carrier */ 1028d43a868SMichal Meloun #define TDESC0_LC (1U << 9) /* Late Collision */ 1038d43a868SMichal Meloun #define TDESC0_EC (1U << 8) /* Excessive Collision */ 1048d43a868SMichal Meloun #define TDESC0_VF (1U << 7) /* VLAN Frame */ 1058d43a868SMichal Meloun #define TDESC0_CC_MASK 0xf 1068d43a868SMichal Meloun #define TDESC0_CC_SHIFT 3 /* Collision Count */ 1078d43a868SMichal Meloun #define TDESC0_ED (1U << 2) /* Excessive Deferral */ 1088d43a868SMichal Meloun #define TDESC0_UF (1U << 1) /* Underflow Error */ 1098d43a868SMichal Meloun #define TDESC0_DB (1U << 0) /* Deferred Bit */ 1108d43a868SMichal Meloun /* TX descriptors - TDESC0 extended format only */ 1118d43a868SMichal Meloun #define ETDESC0_IC (1U << 30) /* Interrupt on Completion */ 1128d43a868SMichal Meloun #define ETDESC0_LS (1U << 29) /* Last Segment */ 1138d43a868SMichal Meloun #define ETDESC0_FS (1U << 28) /* First Segment */ 1148d43a868SMichal Meloun #define ETDESC0_DC (1U << 27) /* Disable CRC */ 1158d43a868SMichal Meloun #define ETDESC0_DP (1U << 26) /* Disable Padding */ 1168d43a868SMichal Meloun #define ETDESC0_CIC_NONE (0U << 22) /* Checksum Insertion Control */ 1178d43a868SMichal Meloun #define ETDESC0_CIC_HDR (1U << 22) 1188d43a868SMichal Meloun #define ETDESC0_CIC_SEG (2U << 22) 1198d43a868SMichal Meloun #define ETDESC0_CIC_FULL (3U << 22) 1208d43a868SMichal Meloun #define ETDESC0_TER (1U << 21) /* Transmit End of Ring */ 1218d43a868SMichal Meloun #define ETDESC0_TCH (1U << 20) /* Second Address Chained */ 1225d43fd68SRuslan Bukin 1238d43a868SMichal Meloun /* TX descriptors - TDESC1 normal format */ 1248d43a868SMichal Meloun #define NTDESC1_IC (1U << 31) /* Interrupt on Completion */ 1258d43a868SMichal Meloun #define NTDESC1_LS (1U << 30) /* Last Segment */ 1268d43a868SMichal Meloun #define NTDESC1_FS (1U << 29) /* First Segment */ 1278d43a868SMichal Meloun #define NTDESC1_CIC_NONE (0U << 27) /* Checksum Insertion Control */ 1288d43a868SMichal Meloun #define NTDESC1_CIC_HDR (1U << 27) 1298d43a868SMichal Meloun #define NTDESC1_CIC_SEG (2U << 27) 1308d43a868SMichal Meloun #define NTDESC1_CIC_FULL (3U << 27) 1318d43a868SMichal Meloun #define NTDESC1_DC (1U << 26) /* Disable CRC */ 1328d43a868SMichal Meloun #define NTDESC1_TER (1U << 25) /* Transmit End of Ring */ 1338d43a868SMichal Meloun #define NTDESC1_TCH (1U << 24) /* Second Address Chained */ 1348d43a868SMichal Meloun /* TX descriptors - TDESC1 extended format */ 1358d43a868SMichal Meloun #define ETDESC1_DP (1U << 23) /* Disable Padding */ 1368d43a868SMichal Meloun #define ETDESC1_TBS2_MASK 0x7ff 1378d43a868SMichal Meloun #define ETDESC1_TBS2_SHIFT 11 /* Receive Buffer 2 Size */ 1388d43a868SMichal Meloun #define ETDESC1_TBS1_MASK 0x7ff 1398d43a868SMichal Meloun #define ETDESC1_TBS1_SHIFT 0 /* Receive Buffer 1 Size */ 1405d43fd68SRuslan Bukin 1418d43a868SMichal Meloun /* RX descriptor - RDESC0 is unified */ 1428d43a868SMichal Meloun #define RDESC0_OWN (1U << 31) 1438d43a868SMichal Meloun #define RDESC0_AFM (1U << 30) /* Dest. Address Filter Fail */ 1448d43a868SMichal Meloun #define RDESC0_FL_MASK 0x3fff 1458d43a868SMichal Meloun #define RDESC0_FL_SHIFT 16 /* Frame Length */ 1468d43a868SMichal Meloun #define RDESC0_ES (1U << 15) /* Error Summary */ 1478d43a868SMichal Meloun #define RDESC0_DE (1U << 14) /* Descriptor Error */ 1488d43a868SMichal Meloun #define RDESC0_SAF (1U << 13) /* Source Address Filter Fail */ 1498d43a868SMichal Meloun #define RDESC0_LE (1U << 12) /* Length Error */ 1508d43a868SMichal Meloun #define RDESC0_OE (1U << 11) /* Overflow Error */ 1518d43a868SMichal Meloun #define RDESC0_VLAN (1U << 10) /* VLAN Tag */ 1528d43a868SMichal Meloun #define RDESC0_FS (1U << 9) /* First Descriptor */ 1538d43a868SMichal Meloun #define RDESC0_LS (1U << 8) /* Last Descriptor */ 1548d43a868SMichal Meloun #define RDESC0_ICE (1U << 7) /* IPC Checksum Error */ 1558d43a868SMichal Meloun #define RDESC0_LC (1U << 6) /* Late Collision */ 1568d43a868SMichal Meloun #define RDESC0_FT (1U << 5) /* Frame Type */ 1578d43a868SMichal Meloun #define RDESC0_RWT (1U << 4) /* Receive Watchdog Timeout */ 1588d43a868SMichal Meloun #define RDESC0_RE (1U << 3) /* Receive Error */ 1598d43a868SMichal Meloun #define RDESC0_DBE (1U << 2) /* Dribble Bit Error */ 1608d43a868SMichal Meloun #define RDESC0_CE (1U << 1) /* CRC Error */ 1618d43a868SMichal Meloun #define RDESC0_PCE (1U << 0) /* Payload Checksum Error */ 1628d43a868SMichal Meloun #define RDESC0_RXMA (1U << 0) /* Rx MAC Address */ 1635df53927SLuiz Otavio O Souza 1648d43a868SMichal Meloun /* RX descriptors - RDESC1 normal format */ 1658d43a868SMichal Meloun #define NRDESC1_DIC (1U << 31) /* Disable Intr on Completion */ 1668d43a868SMichal Meloun #define NRDESC1_RER (1U << 25) /* Receive End of Ring */ 1678d43a868SMichal Meloun #define NRDESC1_RCH (1U << 24) /* Second Address Chained */ 1688d43a868SMichal Meloun #define NRDESC1_RBS2_MASK 0x7ff 1698d43a868SMichal Meloun #define NRDESC1_RBS2_SHIFT 11 /* Receive Buffer 2 Size */ 1708d43a868SMichal Meloun #define NRDESC1_RBS1_MASK 0x7ff 1718d43a868SMichal Meloun #define NRDESC1_RBS1_SHIFT 0 /* Receive Buffer 1 Size */ 1728d43a868SMichal Meloun 1738d43a868SMichal Meloun /* RX descriptors - RDESC1 enhanced format */ 1748d43a868SMichal Meloun #define ERDESC1_DIC (1U << 31) /* Disable Intr on Completion */ 1758d43a868SMichal Meloun #define ERDESC1_RBS2_MASK 0x7ffff 1768d43a868SMichal Meloun #define ERDESC1_RBS2_SHIFT 16 /* Receive Buffer 2 Size */ 1778d43a868SMichal Meloun #define ERDESC1_RER (1U << 15) /* Receive End of Ring */ 1788d43a868SMichal Meloun #define ERDESC1_RCH (1U << 14) /* Second Address Chained */ 1798d43a868SMichal Meloun #define ERDESC1_RBS1_MASK 0x7ffff 1808d43a868SMichal Meloun #define ERDESC1_RBS1_SHIFT 0 /* Receive Buffer 1 Size */ 1815d43fd68SRuslan Bukin 1825d43fd68SRuslan Bukin /* 1835d43fd68SRuslan Bukin * A hardware buffer descriptor. Rx and Tx buffers have the same descriptor 1845df53927SLuiz Otavio O Souza * layout, but the bits in the fields have different meanings. 1855d43fd68SRuslan Bukin */ 1865d43fd68SRuslan Bukin struct dwc_hwdesc 1875d43fd68SRuslan Bukin { 1888d43a868SMichal Meloun uint32_t desc0; 1898d43a868SMichal Meloun uint32_t desc1; 1908d43a868SMichal Meloun uint32_t addr1; /* ptr to first buffer data */ 1918d43a868SMichal Meloun uint32_t addr2; /* ptr to next descriptor / second buffer data*/ 1925d43fd68SRuslan Bukin }; 1935d43fd68SRuslan Bukin 1942a35d391SEmmanuel Vadot 1952a35d391SEmmanuel Vadot struct dwc_hash_maddr_ctx { 1962a35d391SEmmanuel Vadot struct dwc_softc *sc; 1972a35d391SEmmanuel Vadot uint32_t hash[8]; 1982a35d391SEmmanuel Vadot }; 1992a35d391SEmmanuel Vadot 2005d43fd68SRuslan Bukin /* 2015d43fd68SRuslan Bukin * The hardware imposes alignment restrictions on various objects involved in 2025d43fd68SRuslan Bukin * DMA transfers. These values are expressed in bytes (not bits). 2035d43fd68SRuslan Bukin */ 2045d43fd68SRuslan Bukin #define DWC_DESC_RING_ALIGN 2048 2055d43fd68SRuslan Bukin 2065d43fd68SRuslan Bukin static struct resource_spec dwc_spec[] = { 2075d43fd68SRuslan Bukin { SYS_RES_MEMORY, 0, RF_ACTIVE }, 2085d43fd68SRuslan Bukin { SYS_RES_IRQ, 0, RF_ACTIVE }, 2095d43fd68SRuslan Bukin { -1, 0 } 2105d43fd68SRuslan Bukin }; 2115d43fd68SRuslan Bukin 2125d43fd68SRuslan Bukin static void dwc_txfinish_locked(struct dwc_softc *sc); 2135d43fd68SRuslan Bukin static void dwc_rxfinish_locked(struct dwc_softc *sc); 2145d43fd68SRuslan Bukin static void dwc_stop_locked(struct dwc_softc *sc); 2155d43fd68SRuslan Bukin static void dwc_setup_rxfilter(struct dwc_softc *sc); 216158ce7baSEmmanuel Vadot static void dwc_setup_core(struct dwc_softc *sc); 217f368f4b1SEmmanuel Vadot static void dwc_enable_mac(struct dwc_softc *sc, bool enable); 218158ce7baSEmmanuel Vadot static void dwc_init_dma(struct dwc_softc *sc); 219afd0c3c2SEmmanuel Vadot static void dwc_stop_dma(struct dwc_softc *sc); 2205d43fd68SRuslan Bukin 2212a35d391SEmmanuel Vadot static void dwc_tick(void *arg); 2222a35d391SEmmanuel Vadot 2232b4a66edSEmmanuel Vadot /* Pause time field in the transmitted control frame */ 2242b4a66edSEmmanuel Vadot static int dwc_pause_time = 0xffff; 2252b4a66edSEmmanuel Vadot TUNABLE_INT("hw.dwc.pause_time", &dwc_pause_time); 2262b4a66edSEmmanuel Vadot 2272a35d391SEmmanuel Vadot /* 2282a35d391SEmmanuel Vadot * MIIBUS functions 2292a35d391SEmmanuel Vadot */ 2302a35d391SEmmanuel Vadot 2312a35d391SEmmanuel Vadot static int 2322a35d391SEmmanuel Vadot dwc_miibus_read_reg(device_t dev, int phy, int reg) 2332a35d391SEmmanuel Vadot { 2342a35d391SEmmanuel Vadot struct dwc_softc *sc; 2352a35d391SEmmanuel Vadot uint16_t mii; 2362a35d391SEmmanuel Vadot size_t cnt; 2372a35d391SEmmanuel Vadot int rv = 0; 2382a35d391SEmmanuel Vadot 2392a35d391SEmmanuel Vadot sc = device_get_softc(dev); 2402a35d391SEmmanuel Vadot 2412a35d391SEmmanuel Vadot mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) 2422a35d391SEmmanuel Vadot | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) 2432a35d391SEmmanuel Vadot | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) 2442a35d391SEmmanuel Vadot | GMII_ADDRESS_GB; /* Busy flag */ 2452a35d391SEmmanuel Vadot 2462a35d391SEmmanuel Vadot WRITE4(sc, GMII_ADDRESS, mii); 2472a35d391SEmmanuel Vadot 2482a35d391SEmmanuel Vadot for (cnt = 0; cnt < 1000; cnt++) { 2492a35d391SEmmanuel Vadot if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { 2502a35d391SEmmanuel Vadot rv = READ4(sc, GMII_DATA); 2512a35d391SEmmanuel Vadot break; 2522a35d391SEmmanuel Vadot } 2532a35d391SEmmanuel Vadot DELAY(10); 2542a35d391SEmmanuel Vadot } 2552a35d391SEmmanuel Vadot 2562a35d391SEmmanuel Vadot return rv; 2572a35d391SEmmanuel Vadot } 2582a35d391SEmmanuel Vadot 2592a35d391SEmmanuel Vadot static int 2602a35d391SEmmanuel Vadot dwc_miibus_write_reg(device_t dev, int phy, int reg, int val) 2612a35d391SEmmanuel Vadot { 2622a35d391SEmmanuel Vadot struct dwc_softc *sc; 2632a35d391SEmmanuel Vadot uint16_t mii; 2642a35d391SEmmanuel Vadot size_t cnt; 2652a35d391SEmmanuel Vadot 2662a35d391SEmmanuel Vadot sc = device_get_softc(dev); 2672a35d391SEmmanuel Vadot 2682a35d391SEmmanuel Vadot mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) 2692a35d391SEmmanuel Vadot | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) 2702a35d391SEmmanuel Vadot | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) 2712a35d391SEmmanuel Vadot | GMII_ADDRESS_GB | GMII_ADDRESS_GW; 2722a35d391SEmmanuel Vadot 2732a35d391SEmmanuel Vadot WRITE4(sc, GMII_DATA, val); 2742a35d391SEmmanuel Vadot WRITE4(sc, GMII_ADDRESS, mii); 2752a35d391SEmmanuel Vadot 2762a35d391SEmmanuel Vadot for (cnt = 0; cnt < 1000; cnt++) { 2772a35d391SEmmanuel Vadot if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { 2782a35d391SEmmanuel Vadot break; 2792a35d391SEmmanuel Vadot } 2802a35d391SEmmanuel Vadot DELAY(10); 2812a35d391SEmmanuel Vadot } 2822a35d391SEmmanuel Vadot 2832a35d391SEmmanuel Vadot return (0); 2842a35d391SEmmanuel Vadot } 2852a35d391SEmmanuel Vadot 2862a35d391SEmmanuel Vadot static void 2872a35d391SEmmanuel Vadot dwc_miibus_statchg(device_t dev) 2882a35d391SEmmanuel Vadot { 2892a35d391SEmmanuel Vadot struct dwc_softc *sc; 2902a35d391SEmmanuel Vadot struct mii_data *mii; 2912a35d391SEmmanuel Vadot uint32_t reg; 2922a35d391SEmmanuel Vadot 2932a35d391SEmmanuel Vadot /* 2942a35d391SEmmanuel Vadot * Called by the MII bus driver when the PHY establishes 2952a35d391SEmmanuel Vadot * link to set the MAC interface registers. 2962a35d391SEmmanuel Vadot */ 2972a35d391SEmmanuel Vadot 2982a35d391SEmmanuel Vadot sc = device_get_softc(dev); 2992a35d391SEmmanuel Vadot 3002a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 3012a35d391SEmmanuel Vadot 3022a35d391SEmmanuel Vadot mii = sc->mii_softc; 3032a35d391SEmmanuel Vadot 3042a35d391SEmmanuel Vadot if (mii->mii_media_status & IFM_ACTIVE) 3052a35d391SEmmanuel Vadot sc->link_is_up = true; 3062a35d391SEmmanuel Vadot else 3072a35d391SEmmanuel Vadot sc->link_is_up = false; 3082a35d391SEmmanuel Vadot 3092a35d391SEmmanuel Vadot reg = READ4(sc, MAC_CONFIGURATION); 3102a35d391SEmmanuel Vadot switch (IFM_SUBTYPE(mii->mii_media_active)) { 3112a35d391SEmmanuel Vadot case IFM_1000_T: 3122a35d391SEmmanuel Vadot case IFM_1000_SX: 3132a35d391SEmmanuel Vadot reg &= ~(CONF_FES | CONF_PS); 3142a35d391SEmmanuel Vadot break; 3152a35d391SEmmanuel Vadot case IFM_100_TX: 3162a35d391SEmmanuel Vadot reg |= (CONF_FES | CONF_PS); 3172a35d391SEmmanuel Vadot break; 3182a35d391SEmmanuel Vadot case IFM_10_T: 3192a35d391SEmmanuel Vadot reg &= ~(CONF_FES); 3202a35d391SEmmanuel Vadot reg |= (CONF_PS); 3212a35d391SEmmanuel Vadot break; 3222a35d391SEmmanuel Vadot case IFM_NONE: 3232a35d391SEmmanuel Vadot sc->link_is_up = false; 3242a35d391SEmmanuel Vadot return; 3252a35d391SEmmanuel Vadot default: 3262a35d391SEmmanuel Vadot sc->link_is_up = false; 3272a35d391SEmmanuel Vadot device_printf(dev, "Unsupported media %u\n", 3282a35d391SEmmanuel Vadot IFM_SUBTYPE(mii->mii_media_active)); 3292a35d391SEmmanuel Vadot return; 3302a35d391SEmmanuel Vadot } 3312a35d391SEmmanuel Vadot if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 3322a35d391SEmmanuel Vadot reg |= (CONF_DM); 3332a35d391SEmmanuel Vadot else 3342a35d391SEmmanuel Vadot reg &= ~(CONF_DM); 3352a35d391SEmmanuel Vadot WRITE4(sc, MAC_CONFIGURATION, reg); 3362a35d391SEmmanuel Vadot 3372b4a66edSEmmanuel Vadot reg = FLOW_CONTROL_UP; 3382b4a66edSEmmanuel Vadot if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) 3392b4a66edSEmmanuel Vadot reg |= FLOW_CONTROL_TX; 3402b4a66edSEmmanuel Vadot if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) 3412b4a66edSEmmanuel Vadot reg |= FLOW_CONTROL_RX; 3422b4a66edSEmmanuel Vadot if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 3432b4a66edSEmmanuel Vadot reg |= dwc_pause_time << FLOW_CONTROL_PT_SHIFT; 3442b4a66edSEmmanuel Vadot WRITE4(sc, FLOW_CONTROL, reg); 3452b4a66edSEmmanuel Vadot 3462a35d391SEmmanuel Vadot IF_DWC_SET_SPEED(dev, IFM_SUBTYPE(mii->mii_media_active)); 3472a35d391SEmmanuel Vadot 3482a35d391SEmmanuel Vadot } 3492a35d391SEmmanuel Vadot 3502a35d391SEmmanuel Vadot /* 3512a35d391SEmmanuel Vadot * Media functions 3522a35d391SEmmanuel Vadot */ 3532a35d391SEmmanuel Vadot 3542a35d391SEmmanuel Vadot static void 355ca018790SMitchell Horne dwc_media_status(if_t ifp, struct ifmediareq *ifmr) 3562a35d391SEmmanuel Vadot { 3572a35d391SEmmanuel Vadot struct dwc_softc *sc; 3582a35d391SEmmanuel Vadot struct mii_data *mii; 3592a35d391SEmmanuel Vadot 360ca018790SMitchell Horne sc = if_getsoftc(ifp); 3612a35d391SEmmanuel Vadot mii = sc->mii_softc; 3622a35d391SEmmanuel Vadot DWC_LOCK(sc); 3632a35d391SEmmanuel Vadot mii_pollstat(mii); 3642a35d391SEmmanuel Vadot ifmr->ifm_active = mii->mii_media_active; 3652a35d391SEmmanuel Vadot ifmr->ifm_status = mii->mii_media_status; 3662a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 3672a35d391SEmmanuel Vadot } 3682a35d391SEmmanuel Vadot 3692a35d391SEmmanuel Vadot static int 3702a35d391SEmmanuel Vadot dwc_media_change_locked(struct dwc_softc *sc) 3712a35d391SEmmanuel Vadot { 3722a35d391SEmmanuel Vadot 3732a35d391SEmmanuel Vadot return (mii_mediachg(sc->mii_softc)); 3742a35d391SEmmanuel Vadot } 3752a35d391SEmmanuel Vadot 3762a35d391SEmmanuel Vadot static int 377ca018790SMitchell Horne dwc_media_change(if_t ifp) 3782a35d391SEmmanuel Vadot { 3792a35d391SEmmanuel Vadot struct dwc_softc *sc; 3802a35d391SEmmanuel Vadot int error; 3812a35d391SEmmanuel Vadot 382ca018790SMitchell Horne sc = if_getsoftc(ifp); 3832a35d391SEmmanuel Vadot 3842a35d391SEmmanuel Vadot DWC_LOCK(sc); 3852a35d391SEmmanuel Vadot error = dwc_media_change_locked(sc); 3862a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 3872a35d391SEmmanuel Vadot return (error); 3882a35d391SEmmanuel Vadot } 3892a35d391SEmmanuel Vadot 3902a35d391SEmmanuel Vadot /* 3912a35d391SEmmanuel Vadot * Core functions 3922a35d391SEmmanuel Vadot */ 3932a35d391SEmmanuel Vadot 3942a35d391SEmmanuel Vadot static const uint8_t nibbletab[] = { 3952a35d391SEmmanuel Vadot /* 0x0 0000 -> 0000 */ 0x0, 3962a35d391SEmmanuel Vadot /* 0x1 0001 -> 1000 */ 0x8, 3972a35d391SEmmanuel Vadot /* 0x2 0010 -> 0100 */ 0x4, 3982a35d391SEmmanuel Vadot /* 0x3 0011 -> 1100 */ 0xc, 3992a35d391SEmmanuel Vadot /* 0x4 0100 -> 0010 */ 0x2, 4002a35d391SEmmanuel Vadot /* 0x5 0101 -> 1010 */ 0xa, 4012a35d391SEmmanuel Vadot /* 0x6 0110 -> 0110 */ 0x6, 4022a35d391SEmmanuel Vadot /* 0x7 0111 -> 1110 */ 0xe, 4032a35d391SEmmanuel Vadot /* 0x8 1000 -> 0001 */ 0x1, 4042a35d391SEmmanuel Vadot /* 0x9 1001 -> 1001 */ 0x9, 4052a35d391SEmmanuel Vadot /* 0xa 1010 -> 0101 */ 0x5, 4062a35d391SEmmanuel Vadot /* 0xb 1011 -> 1101 */ 0xd, 4072a35d391SEmmanuel Vadot /* 0xc 1100 -> 0011 */ 0x3, 4082a35d391SEmmanuel Vadot /* 0xd 1101 -> 1011 */ 0xb, 4092a35d391SEmmanuel Vadot /* 0xe 1110 -> 0111 */ 0x7, 4102a35d391SEmmanuel Vadot /* 0xf 1111 -> 1111 */ 0xf, }; 4112a35d391SEmmanuel Vadot 4122a35d391SEmmanuel Vadot static uint8_t 4132a35d391SEmmanuel Vadot bitreverse(uint8_t x) 4142a35d391SEmmanuel Vadot { 4152a35d391SEmmanuel Vadot 4162a35d391SEmmanuel Vadot return (nibbletab[x & 0xf] << 4) | nibbletab[x >> 4]; 4172a35d391SEmmanuel Vadot } 4182a35d391SEmmanuel Vadot 4192a35d391SEmmanuel Vadot static u_int 4202a35d391SEmmanuel Vadot dwc_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) 4212a35d391SEmmanuel Vadot { 4222a35d391SEmmanuel Vadot struct dwc_hash_maddr_ctx *ctx = arg; 4232a35d391SEmmanuel Vadot uint32_t crc, hashbit, hashreg; 4242a35d391SEmmanuel Vadot uint8_t val; 4252a35d391SEmmanuel Vadot 4262a35d391SEmmanuel Vadot crc = ether_crc32_le(LLADDR(sdl), ETHER_ADDR_LEN); 4272a35d391SEmmanuel Vadot /* Take lower 8 bits and reverse it */ 4282a35d391SEmmanuel Vadot val = bitreverse(~crc & 0xff); 4292a35d391SEmmanuel Vadot if (ctx->sc->mactype != DWC_GMAC_EXT_DESC) 4302a35d391SEmmanuel Vadot val >>= 2; /* Only need lower 6 bits */ 4312a35d391SEmmanuel Vadot hashreg = (val >> 5); 4322a35d391SEmmanuel Vadot hashbit = (val & 31); 4332a35d391SEmmanuel Vadot ctx->hash[hashreg] |= (1 << hashbit); 4342a35d391SEmmanuel Vadot 4352a35d391SEmmanuel Vadot return (1); 4362a35d391SEmmanuel Vadot } 4372a35d391SEmmanuel Vadot 4382a35d391SEmmanuel Vadot static void 4392a35d391SEmmanuel Vadot dwc_setup_rxfilter(struct dwc_softc *sc) 4402a35d391SEmmanuel Vadot { 4412a35d391SEmmanuel Vadot struct dwc_hash_maddr_ctx ctx; 442ca018790SMitchell Horne if_t ifp; 4432a35d391SEmmanuel Vadot uint8_t *eaddr; 4442a35d391SEmmanuel Vadot uint32_t ffval, hi, lo; 4452a35d391SEmmanuel Vadot int nhash, i; 4462a35d391SEmmanuel Vadot 4472a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 4482a35d391SEmmanuel Vadot 4492a35d391SEmmanuel Vadot ifp = sc->ifp; 4502a35d391SEmmanuel Vadot nhash = sc->mactype != DWC_GMAC_EXT_DESC ? 2 : 8; 4512a35d391SEmmanuel Vadot 4522a35d391SEmmanuel Vadot /* 4532a35d391SEmmanuel Vadot * Set the multicast (group) filter hash. 4542a35d391SEmmanuel Vadot */ 455ca018790SMitchell Horne if ((if_getflags(ifp) & IFF_ALLMULTI) != 0) { 4562a35d391SEmmanuel Vadot ffval = (FRAME_FILTER_PM); 4572a35d391SEmmanuel Vadot for (i = 0; i < nhash; i++) 4582a35d391SEmmanuel Vadot ctx.hash[i] = ~0; 4592a35d391SEmmanuel Vadot } else { 4602a35d391SEmmanuel Vadot ffval = (FRAME_FILTER_HMC); 4612a35d391SEmmanuel Vadot for (i = 0; i < nhash; i++) 4622a35d391SEmmanuel Vadot ctx.hash[i] = 0; 4632a35d391SEmmanuel Vadot ctx.sc = sc; 4642a35d391SEmmanuel Vadot if_foreach_llmaddr(ifp, dwc_hash_maddr, &ctx); 4652a35d391SEmmanuel Vadot } 4662a35d391SEmmanuel Vadot 4672a35d391SEmmanuel Vadot /* 4682a35d391SEmmanuel Vadot * Set the individual address filter hash. 4692a35d391SEmmanuel Vadot */ 470ca018790SMitchell Horne if ((if_getflags(ifp) & IFF_PROMISC) != 0) 4712a35d391SEmmanuel Vadot ffval |= (FRAME_FILTER_PR); 4722a35d391SEmmanuel Vadot 4732a35d391SEmmanuel Vadot /* 4742a35d391SEmmanuel Vadot * Set the primary address. 4752a35d391SEmmanuel Vadot */ 476068f2c0eSJustin Hibbits eaddr = if_getlladdr(ifp); 4772a35d391SEmmanuel Vadot lo = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) | 4782a35d391SEmmanuel Vadot (eaddr[3] << 24); 4792a35d391SEmmanuel Vadot hi = eaddr[4] | (eaddr[5] << 8); 4802a35d391SEmmanuel Vadot WRITE4(sc, MAC_ADDRESS_LOW(0), lo); 4812a35d391SEmmanuel Vadot WRITE4(sc, MAC_ADDRESS_HIGH(0), hi); 4822a35d391SEmmanuel Vadot WRITE4(sc, MAC_FRAME_FILTER, ffval); 4832a35d391SEmmanuel Vadot if (sc->mactype != DWC_GMAC_EXT_DESC) { 4842a35d391SEmmanuel Vadot WRITE4(sc, GMAC_MAC_HTLOW, ctx.hash[0]); 4852a35d391SEmmanuel Vadot WRITE4(sc, GMAC_MAC_HTHIGH, ctx.hash[1]); 4862a35d391SEmmanuel Vadot } else { 4872a35d391SEmmanuel Vadot for (i = 0; i < nhash; i++) 4882a35d391SEmmanuel Vadot WRITE4(sc, HASH_TABLE_REG(i), ctx.hash[i]); 4892a35d391SEmmanuel Vadot } 4902a35d391SEmmanuel Vadot } 4912a35d391SEmmanuel Vadot 4922a35d391SEmmanuel Vadot static void 4932a35d391SEmmanuel Vadot dwc_setup_core(struct dwc_softc *sc) 4942a35d391SEmmanuel Vadot { 4952a35d391SEmmanuel Vadot uint32_t reg; 4962a35d391SEmmanuel Vadot 4972a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 4982a35d391SEmmanuel Vadot 4992a35d391SEmmanuel Vadot /* Enable core */ 5002a35d391SEmmanuel Vadot reg = READ4(sc, MAC_CONFIGURATION); 5012a35d391SEmmanuel Vadot reg |= (CONF_JD | CONF_ACS | CONF_BE); 5022a35d391SEmmanuel Vadot WRITE4(sc, MAC_CONFIGURATION, reg); 5032a35d391SEmmanuel Vadot } 5042a35d391SEmmanuel Vadot 5052a35d391SEmmanuel Vadot static void 5062a35d391SEmmanuel Vadot dwc_enable_mac(struct dwc_softc *sc, bool enable) 5072a35d391SEmmanuel Vadot { 5082a35d391SEmmanuel Vadot uint32_t reg; 5092a35d391SEmmanuel Vadot 5102a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 5112a35d391SEmmanuel Vadot reg = READ4(sc, MAC_CONFIGURATION); 5122a35d391SEmmanuel Vadot if (enable) 5132a35d391SEmmanuel Vadot reg |= CONF_TE | CONF_RE; 5142a35d391SEmmanuel Vadot else 5152a35d391SEmmanuel Vadot reg &= ~(CONF_TE | CONF_RE); 5162a35d391SEmmanuel Vadot WRITE4(sc, MAC_CONFIGURATION, reg); 5172a35d391SEmmanuel Vadot } 5182a35d391SEmmanuel Vadot 5192a35d391SEmmanuel Vadot static void 52035c9edabSJiahao Li dwc_enable_csum_offload(struct dwc_softc *sc) 52135c9edabSJiahao Li { 52235c9edabSJiahao Li uint32_t reg; 52335c9edabSJiahao Li 52435c9edabSJiahao Li DWC_ASSERT_LOCKED(sc); 52535c9edabSJiahao Li reg = READ4(sc, MAC_CONFIGURATION); 52635c9edabSJiahao Li if ((if_getcapenable(sc->ifp) & IFCAP_RXCSUM) != 0) 52735c9edabSJiahao Li reg |= CONF_IPC; 52835c9edabSJiahao Li else 52935c9edabSJiahao Li reg &= ~CONF_IPC; 53035c9edabSJiahao Li WRITE4(sc, MAC_CONFIGURATION, reg); 53135c9edabSJiahao Li } 53235c9edabSJiahao Li 53335c9edabSJiahao Li static void 5342a35d391SEmmanuel Vadot dwc_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr) 5352a35d391SEmmanuel Vadot { 5362a35d391SEmmanuel Vadot uint32_t hi, lo, rnd; 5372a35d391SEmmanuel Vadot 5382a35d391SEmmanuel Vadot /* 5392a35d391SEmmanuel Vadot * Try to recover a MAC address from the running hardware. If there's 5402a35d391SEmmanuel Vadot * something non-zero there, assume the bootloader did the right thing 5412a35d391SEmmanuel Vadot * and just use it. 5422a35d391SEmmanuel Vadot * 5432a35d391SEmmanuel Vadot * Otherwise, set the address to a convenient locally assigned address, 5442a35d391SEmmanuel Vadot * 'bsd' + random 24 low-order bits. 'b' is 0x62, which has the locally 5452a35d391SEmmanuel Vadot * assigned bit set, and the broadcast/multicast bit clear. 5462a35d391SEmmanuel Vadot */ 5472a35d391SEmmanuel Vadot lo = READ4(sc, MAC_ADDRESS_LOW(0)); 5482a35d391SEmmanuel Vadot hi = READ4(sc, MAC_ADDRESS_HIGH(0)) & 0xffff; 5492a35d391SEmmanuel Vadot if ((lo != 0xffffffff) || (hi != 0xffff)) { 5502a35d391SEmmanuel Vadot hwaddr[0] = (lo >> 0) & 0xff; 5512a35d391SEmmanuel Vadot hwaddr[1] = (lo >> 8) & 0xff; 5522a35d391SEmmanuel Vadot hwaddr[2] = (lo >> 16) & 0xff; 5532a35d391SEmmanuel Vadot hwaddr[3] = (lo >> 24) & 0xff; 5542a35d391SEmmanuel Vadot hwaddr[4] = (hi >> 0) & 0xff; 5552a35d391SEmmanuel Vadot hwaddr[5] = (hi >> 8) & 0xff; 5562a35d391SEmmanuel Vadot } else { 5572a35d391SEmmanuel Vadot rnd = arc4random() & 0x00ffffff; 5582a35d391SEmmanuel Vadot hwaddr[0] = 'b'; 5592a35d391SEmmanuel Vadot hwaddr[1] = 's'; 5602a35d391SEmmanuel Vadot hwaddr[2] = 'd'; 5612a35d391SEmmanuel Vadot hwaddr[3] = rnd >> 16; 5622a35d391SEmmanuel Vadot hwaddr[4] = rnd >> 8; 5632a35d391SEmmanuel Vadot hwaddr[5] = rnd >> 0; 5642a35d391SEmmanuel Vadot } 5652a35d391SEmmanuel Vadot } 5662a35d391SEmmanuel Vadot 5672a35d391SEmmanuel Vadot /* 5682a35d391SEmmanuel Vadot * DMA functions 5692a35d391SEmmanuel Vadot */ 5702a35d391SEmmanuel Vadot 5712a35d391SEmmanuel Vadot static void 5722a35d391SEmmanuel Vadot dwc_init_dma(struct dwc_softc *sc) 5732a35d391SEmmanuel Vadot { 5742a35d391SEmmanuel Vadot uint32_t reg; 5752a35d391SEmmanuel Vadot 5762a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 5772a35d391SEmmanuel Vadot 5782a35d391SEmmanuel Vadot /* Initializa DMA and enable transmitters */ 5792a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 5802a35d391SEmmanuel Vadot reg |= (MODE_TSF | MODE_OSF | MODE_FUF); 5812a35d391SEmmanuel Vadot reg &= ~(MODE_RSF); 5822a35d391SEmmanuel Vadot reg |= (MODE_RTC_LEV32 << MODE_RTC_SHIFT); 5832a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 5842a35d391SEmmanuel Vadot 5852a35d391SEmmanuel Vadot WRITE4(sc, INTERRUPT_ENABLE, INT_EN_DEFAULT); 5862a35d391SEmmanuel Vadot 5872a35d391SEmmanuel Vadot /* Start DMA */ 5882a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 5892a35d391SEmmanuel Vadot reg |= (MODE_ST | MODE_SR); 5902a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 5912a35d391SEmmanuel Vadot } 5922a35d391SEmmanuel Vadot 5932a35d391SEmmanuel Vadot static void 5942a35d391SEmmanuel Vadot dwc_stop_dma(struct dwc_softc *sc) 5952a35d391SEmmanuel Vadot { 5962a35d391SEmmanuel Vadot uint32_t reg; 5972a35d391SEmmanuel Vadot 5982a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 5992a35d391SEmmanuel Vadot 6002a35d391SEmmanuel Vadot /* Stop DMA TX */ 6012a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 6022a35d391SEmmanuel Vadot reg &= ~(MODE_ST); 6032a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 6042a35d391SEmmanuel Vadot 6052a35d391SEmmanuel Vadot /* Flush TX */ 6062a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 6072a35d391SEmmanuel Vadot reg |= (MODE_FTF); 6082a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 6092a35d391SEmmanuel Vadot 6102a35d391SEmmanuel Vadot /* Stop DMA RX */ 6112a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 6122a35d391SEmmanuel Vadot reg &= ~(MODE_SR); 6132a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 6142a35d391SEmmanuel Vadot } 6152a35d391SEmmanuel Vadot 6165d43fd68SRuslan Bukin static inline uint32_t 6175d43fd68SRuslan Bukin next_rxidx(struct dwc_softc *sc, uint32_t curidx) 6185d43fd68SRuslan Bukin { 6195d43fd68SRuslan Bukin 6205d43fd68SRuslan Bukin return ((curidx + 1) % RX_DESC_COUNT); 6215d43fd68SRuslan Bukin } 6225d43fd68SRuslan Bukin 6235d43fd68SRuslan Bukin static inline uint32_t 6245d43fd68SRuslan Bukin next_txidx(struct dwc_softc *sc, uint32_t curidx) 6255d43fd68SRuslan Bukin { 6265d43fd68SRuslan Bukin 6275d43fd68SRuslan Bukin return ((curidx + 1) % TX_DESC_COUNT); 6285d43fd68SRuslan Bukin } 6295d43fd68SRuslan Bukin 6305d43fd68SRuslan Bukin static void 6315d43fd68SRuslan Bukin dwc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 6325d43fd68SRuslan Bukin { 6335d43fd68SRuslan Bukin 6345d43fd68SRuslan Bukin if (error != 0) 6355d43fd68SRuslan Bukin return; 6365d43fd68SRuslan Bukin *(bus_addr_t *)arg = segs[0].ds_addr; 6375d43fd68SRuslan Bukin } 6385d43fd68SRuslan Bukin 639b72e2878SMichal Meloun inline static void 6405d43fd68SRuslan Bukin dwc_setup_txdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr, 641e5232621SOleksandr Tymoshenko uint32_t len, uint32_t flags, bool first, bool last) 6425d43fd68SRuslan Bukin { 643b72e2878SMichal Meloun uint32_t desc0, desc1; 6445d43fd68SRuslan Bukin 6455d43fd68SRuslan Bukin /* Addr/len 0 means we're clearing the descriptor after xmit done. */ 6465d43fd68SRuslan Bukin if (paddr == 0 || len == 0) { 647b72e2878SMichal Meloun desc0 = 0; 648b72e2878SMichal Meloun desc1 = 0; 649e5232621SOleksandr Tymoshenko --sc->tx_desccount; 6505d43fd68SRuslan Bukin } else { 651b72e2878SMichal Meloun if (sc->mactype != DWC_GMAC_EXT_DESC) { 652b72e2878SMichal Meloun desc0 = 0; 653e5232621SOleksandr Tymoshenko desc1 = NTDESC1_TCH | len | flags; 654e5232621SOleksandr Tymoshenko if (first) 655e5232621SOleksandr Tymoshenko desc1 |= NTDESC1_FS; 656e5232621SOleksandr Tymoshenko if (last) 657e5232621SOleksandr Tymoshenko desc1 |= NTDESC1_LS | NTDESC1_IC; 658b72e2878SMichal Meloun } else { 659e5232621SOleksandr Tymoshenko desc0 = ETDESC0_TCH | flags; 660e5232621SOleksandr Tymoshenko if (first) 661e5232621SOleksandr Tymoshenko desc0 |= ETDESC0_FS; 662e5232621SOleksandr Tymoshenko if (last) 663e5232621SOleksandr Tymoshenko desc0 |= ETDESC0_LS | ETDESC0_IC; 664b72e2878SMichal Meloun desc1 = len; 665b72e2878SMichal Meloun } 666e5232621SOleksandr Tymoshenko ++sc->tx_desccount; 6675d43fd68SRuslan Bukin } 6685d43fd68SRuslan Bukin 6698d43a868SMichal Meloun sc->txdesc_ring[idx].addr1 = (uint32_t)(paddr); 670b72e2878SMichal Meloun sc->txdesc_ring[idx].desc0 = desc0; 671b72e2878SMichal Meloun sc->txdesc_ring[idx].desc1 = desc1; 672e5232621SOleksandr Tymoshenko } 6735d43fd68SRuslan Bukin 674e5232621SOleksandr Tymoshenko inline static void 675e5232621SOleksandr Tymoshenko dwc_set_owner(struct dwc_softc *sc, int idx) 676e5232621SOleksandr Tymoshenko { 6775d43fd68SRuslan Bukin wmb(); 6788d43a868SMichal Meloun sc->txdesc_ring[idx].desc0 |= TDESC0_OWN; 6795d43fd68SRuslan Bukin wmb(); 6805d43fd68SRuslan Bukin } 6815d43fd68SRuslan Bukin 6825d43fd68SRuslan Bukin static int 6835d43fd68SRuslan Bukin dwc_setup_txbuf(struct dwc_softc *sc, int idx, struct mbuf **mp) 6845d43fd68SRuslan Bukin { 685e5232621SOleksandr Tymoshenko struct bus_dma_segment segs[TX_MAP_MAX_SEGS]; 6865d43fd68SRuslan Bukin int error, nsegs; 6875d43fd68SRuslan Bukin struct mbuf * m; 68898ea5a7bSEmmanuel Vadot uint32_t flags = 0; 689e5232621SOleksandr Tymoshenko int i; 690e5232621SOleksandr Tymoshenko int first, last; 6915d43fd68SRuslan Bukin 692e5232621SOleksandr Tymoshenko error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, 693e5232621SOleksandr Tymoshenko *mp, segs, &nsegs, 0); 694e5232621SOleksandr Tymoshenko if (error == EFBIG) { 695e5232621SOleksandr Tymoshenko /* 696e5232621SOleksandr Tymoshenko * The map may be partially mapped from the first call. 697e5232621SOleksandr Tymoshenko * Make sure to reset it. 698e5232621SOleksandr Tymoshenko */ 699e5232621SOleksandr Tymoshenko bus_dmamap_unload(sc->txbuf_tag, sc->txbuf_map[idx].map); 7005d43fd68SRuslan Bukin if ((m = m_defrag(*mp, M_NOWAIT)) == NULL) 7015d43fd68SRuslan Bukin return (ENOMEM); 7025d43fd68SRuslan Bukin *mp = m; 7035d43fd68SRuslan Bukin error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, 704e5232621SOleksandr Tymoshenko *mp, segs, &nsegs, 0); 705e5232621SOleksandr Tymoshenko } 706e5232621SOleksandr Tymoshenko if (error != 0) 707e5232621SOleksandr Tymoshenko return (ENOMEM); 708e5232621SOleksandr Tymoshenko 709e5232621SOleksandr Tymoshenko if (sc->tx_desccount + nsegs > TX_DESC_COUNT) { 710e5232621SOleksandr Tymoshenko bus_dmamap_unload(sc->txbuf_tag, sc->txbuf_map[idx].map); 7115d43fd68SRuslan Bukin return (ENOMEM); 7125d43fd68SRuslan Bukin } 7135d43fd68SRuslan Bukin 714e5232621SOleksandr Tymoshenko m = *mp; 7155d43fd68SRuslan Bukin 71698ea5a7bSEmmanuel Vadot if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) { 71798ea5a7bSEmmanuel Vadot if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0) { 71898ea5a7bSEmmanuel Vadot if (sc->mactype != DWC_GMAC_EXT_DESC) 71998ea5a7bSEmmanuel Vadot flags = NTDESC1_CIC_FULL; 72098ea5a7bSEmmanuel Vadot else 72198ea5a7bSEmmanuel Vadot flags = ETDESC0_CIC_FULL; 72298ea5a7bSEmmanuel Vadot } else { 72398ea5a7bSEmmanuel Vadot if (sc->mactype != DWC_GMAC_EXT_DESC) 72498ea5a7bSEmmanuel Vadot flags = NTDESC1_CIC_HDR; 72598ea5a7bSEmmanuel Vadot else 72698ea5a7bSEmmanuel Vadot flags = ETDESC0_CIC_HDR; 72798ea5a7bSEmmanuel Vadot } 72898ea5a7bSEmmanuel Vadot } 72998ea5a7bSEmmanuel Vadot 730e5232621SOleksandr Tymoshenko bus_dmamap_sync(sc->txbuf_tag, sc->txbuf_map[idx].map, 731e5232621SOleksandr Tymoshenko BUS_DMASYNC_PREWRITE); 732e5232621SOleksandr Tymoshenko 733e5232621SOleksandr Tymoshenko sc->txbuf_map[idx].mbuf = m; 734e5232621SOleksandr Tymoshenko 735e5232621SOleksandr Tymoshenko first = sc->tx_desc_head; 736e5232621SOleksandr Tymoshenko for (i = 0; i < nsegs; i++) { 737e5232621SOleksandr Tymoshenko dwc_setup_txdesc(sc, sc->tx_desc_head, 738e5232621SOleksandr Tymoshenko segs[i].ds_addr, segs[i].ds_len, 739e5232621SOleksandr Tymoshenko (i == 0) ? flags : 0, /* only first desc needs flags */ 740e5232621SOleksandr Tymoshenko (i == 0), 741e5232621SOleksandr Tymoshenko (i == nsegs - 1)); 742e5232621SOleksandr Tymoshenko if (i > 0) 743e5232621SOleksandr Tymoshenko dwc_set_owner(sc, sc->tx_desc_head); 744e5232621SOleksandr Tymoshenko last = sc->tx_desc_head; 745e5232621SOleksandr Tymoshenko sc->tx_desc_head = next_txidx(sc, sc->tx_desc_head); 746e5232621SOleksandr Tymoshenko } 747e5232621SOleksandr Tymoshenko 748e5232621SOleksandr Tymoshenko sc->txbuf_map[idx].last_desc_idx = last; 749e5232621SOleksandr Tymoshenko 750e5232621SOleksandr Tymoshenko dwc_set_owner(sc, first); 7515d43fd68SRuslan Bukin 7525d43fd68SRuslan Bukin return (0); 7535d43fd68SRuslan Bukin } 7545d43fd68SRuslan Bukin 7555d43fd68SRuslan Bukin inline static uint32_t 7565d43fd68SRuslan Bukin dwc_setup_rxdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr) 7575d43fd68SRuslan Bukin { 7585d43fd68SRuslan Bukin uint32_t nidx; 7595d43fd68SRuslan Bukin 7608d43a868SMichal Meloun sc->rxdesc_ring[idx].addr1 = (uint32_t)paddr; 7615d43fd68SRuslan Bukin nidx = next_rxidx(sc, idx); 7628d43a868SMichal Meloun sc->rxdesc_ring[idx].addr2 = sc->rxdesc_ring_paddr + 7635d43fd68SRuslan Bukin (nidx * sizeof(struct dwc_hwdesc)); 764188aee74SMichal Meloun if (sc->mactype != DWC_GMAC_EXT_DESC) 765b72e2878SMichal Meloun sc->rxdesc_ring[idx].desc1 = NRDESC1_RCH | 766b72e2878SMichal Meloun MIN(MCLBYTES, NRDESC1_RBS1_MASK); 7675df53927SLuiz Otavio O Souza else 768b72e2878SMichal Meloun sc->rxdesc_ring[idx].desc1 = ERDESC1_RCH | 769b72e2878SMichal Meloun MIN(MCLBYTES, ERDESC1_RBS1_MASK); 7705d43fd68SRuslan Bukin 7715d43fd68SRuslan Bukin wmb(); 7728d43a868SMichal Meloun sc->rxdesc_ring[idx].desc0 = RDESC0_OWN; 7735d43fd68SRuslan Bukin wmb(); 7745d43fd68SRuslan Bukin return (nidx); 7755d43fd68SRuslan Bukin } 7765d43fd68SRuslan Bukin 7775d43fd68SRuslan Bukin static int 7785d43fd68SRuslan Bukin dwc_setup_rxbuf(struct dwc_softc *sc, int idx, struct mbuf *m) 7795d43fd68SRuslan Bukin { 7805d43fd68SRuslan Bukin struct bus_dma_segment seg; 7815d43fd68SRuslan Bukin int error, nsegs; 7825d43fd68SRuslan Bukin 7835d43fd68SRuslan Bukin m_adj(m, ETHER_ALIGN); 7845d43fd68SRuslan Bukin 7855d43fd68SRuslan Bukin error = bus_dmamap_load_mbuf_sg(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 7865d43fd68SRuslan Bukin m, &seg, &nsegs, 0); 7877ed7d979SMichal Meloun if (error != 0) 7885d43fd68SRuslan Bukin return (error); 7895d43fd68SRuslan Bukin 7905d43fd68SRuslan Bukin KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 7915d43fd68SRuslan Bukin 7925d43fd68SRuslan Bukin bus_dmamap_sync(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 7935d43fd68SRuslan Bukin BUS_DMASYNC_PREREAD); 7945d43fd68SRuslan Bukin 7955d43fd68SRuslan Bukin sc->rxbuf_map[idx].mbuf = m; 7965d43fd68SRuslan Bukin dwc_setup_rxdesc(sc, idx, seg.ds_addr); 7975d43fd68SRuslan Bukin 7985d43fd68SRuslan Bukin return (0); 7995d43fd68SRuslan Bukin } 8005d43fd68SRuslan Bukin 8015d43fd68SRuslan Bukin static struct mbuf * 8025d43fd68SRuslan Bukin dwc_alloc_mbufcl(struct dwc_softc *sc) 8035d43fd68SRuslan Bukin { 8045d43fd68SRuslan Bukin struct mbuf *m; 8055d43fd68SRuslan Bukin 8065d43fd68SRuslan Bukin m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 807db8a143aSRuslan Bukin if (m != NULL) 8085d43fd68SRuslan Bukin m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; 8095d43fd68SRuslan Bukin 8105d43fd68SRuslan Bukin return (m); 8115d43fd68SRuslan Bukin } 8125d43fd68SRuslan Bukin 813b72e2878SMichal Meloun static struct mbuf * 814b72e2878SMichal Meloun dwc_rxfinish_one(struct dwc_softc *sc, struct dwc_hwdesc *desc, 815b72e2878SMichal Meloun struct dwc_bufmap *map) 816b72e2878SMichal Meloun { 817ca018790SMitchell Horne if_t ifp; 818b72e2878SMichal Meloun struct mbuf *m, *m0; 819b72e2878SMichal Meloun int len; 820b72e2878SMichal Meloun uint32_t rdesc0; 821b72e2878SMichal Meloun 822b72e2878SMichal Meloun m = map->mbuf; 823b72e2878SMichal Meloun ifp = sc->ifp; 824b72e2878SMichal Meloun rdesc0 = desc ->desc0; 825b72e2878SMichal Meloun 826b72e2878SMichal Meloun if ((rdesc0 & (RDESC0_FS | RDESC0_LS)) != 827b72e2878SMichal Meloun (RDESC0_FS | RDESC0_LS)) { 828b72e2878SMichal Meloun /* 829b72e2878SMichal Meloun * Something very wrong happens. The whole packet should be 830b72e2878SMichal Meloun * recevied in one descriptr. Report problem. 831b72e2878SMichal Meloun */ 832b72e2878SMichal Meloun device_printf(sc->dev, 833b72e2878SMichal Meloun "%s: RX descriptor without FIRST and LAST bit set: 0x%08X", 834b72e2878SMichal Meloun __func__, rdesc0); 835b72e2878SMichal Meloun return (NULL); 836b72e2878SMichal Meloun } 837b72e2878SMichal Meloun 838b72e2878SMichal Meloun len = (rdesc0 >> RDESC0_FL_SHIFT) & RDESC0_FL_MASK; 839b72e2878SMichal Meloun if (len < 64) { 840b72e2878SMichal Meloun /* 841b72e2878SMichal Meloun * Lenght is invalid, recycle old mbuf 842b72e2878SMichal Meloun * Probably impossible case 843b72e2878SMichal Meloun */ 844b72e2878SMichal Meloun return (NULL); 845b72e2878SMichal Meloun } 846b72e2878SMichal Meloun 847b72e2878SMichal Meloun /* Allocate new buffer */ 848b72e2878SMichal Meloun m0 = dwc_alloc_mbufcl(sc); 849b72e2878SMichal Meloun if (m0 == NULL) { 850b72e2878SMichal Meloun /* no new mbuf available, recycle old */ 851b72e2878SMichal Meloun if_inc_counter(sc->ifp, IFCOUNTER_IQDROPS, 1); 852b72e2878SMichal Meloun return (NULL); 853b72e2878SMichal Meloun } 854b72e2878SMichal Meloun /* Do dmasync for newly received packet */ 855b72e2878SMichal Meloun bus_dmamap_sync(sc->rxbuf_tag, map->map, BUS_DMASYNC_POSTREAD); 856b72e2878SMichal Meloun bus_dmamap_unload(sc->rxbuf_tag, map->map); 857b72e2878SMichal Meloun 858b72e2878SMichal Meloun /* Received packet is valid, process it */ 859b72e2878SMichal Meloun m->m_pkthdr.rcvif = ifp; 860b72e2878SMichal Meloun m->m_pkthdr.len = len; 861b72e2878SMichal Meloun m->m_len = len; 862b72e2878SMichal Meloun if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 863b72e2878SMichal Meloun 86498ea5a7bSEmmanuel Vadot if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0 && 86598ea5a7bSEmmanuel Vadot (rdesc0 & RDESC0_FT) != 0) { 86698ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_flags = CSUM_IP_CHECKED; 86798ea5a7bSEmmanuel Vadot if ((rdesc0 & RDESC0_ICE) == 0) 86898ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_flags |= CSUM_IP_VALID; 86998ea5a7bSEmmanuel Vadot if ((rdesc0 & RDESC0_PCE) == 0) { 87098ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_flags |= 87198ea5a7bSEmmanuel Vadot CSUM_DATA_VALID | CSUM_PSEUDO_HDR; 87298ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_data = 0xffff; 87398ea5a7bSEmmanuel Vadot } 87498ea5a7bSEmmanuel Vadot } 87598ea5a7bSEmmanuel Vadot 876b72e2878SMichal Meloun /* Remove trailing FCS */ 877b72e2878SMichal Meloun m_adj(m, -ETHER_CRC_LEN); 878b72e2878SMichal Meloun 879b72e2878SMichal Meloun DWC_UNLOCK(sc); 880ca018790SMitchell Horne if_input(ifp, m); 881b72e2878SMichal Meloun DWC_LOCK(sc); 882b72e2878SMichal Meloun return (m0); 883b72e2878SMichal Meloun } 884b72e2878SMichal Meloun 8855d43fd68SRuslan Bukin static int 8865d43fd68SRuslan Bukin setup_dma(struct dwc_softc *sc) 8875d43fd68SRuslan Bukin { 8885d43fd68SRuslan Bukin struct mbuf *m; 8895d43fd68SRuslan Bukin int error; 8905d43fd68SRuslan Bukin int nidx; 8915d43fd68SRuslan Bukin int idx; 8925d43fd68SRuslan Bukin 8935d43fd68SRuslan Bukin /* 8945d43fd68SRuslan Bukin * Set up TX descriptor ring, descriptors, and dma maps. 8955d43fd68SRuslan Bukin */ 8965d43fd68SRuslan Bukin error = bus_dma_tag_create( 8975d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 8985d43fd68SRuslan Bukin DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ 8995d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9005d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9015d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 9025d43fd68SRuslan Bukin TX_DESC_SIZE, 1, /* maxsize, nsegments */ 9035d43fd68SRuslan Bukin TX_DESC_SIZE, /* maxsegsize */ 9045d43fd68SRuslan Bukin 0, /* flags */ 9055d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9065d43fd68SRuslan Bukin &sc->txdesc_tag); 9075d43fd68SRuslan Bukin if (error != 0) { 9085d43fd68SRuslan Bukin device_printf(sc->dev, 9095d43fd68SRuslan Bukin "could not create TX ring DMA tag.\n"); 9105d43fd68SRuslan Bukin goto out; 9115d43fd68SRuslan Bukin } 9125d43fd68SRuslan Bukin 9135d43fd68SRuslan Bukin error = bus_dmamem_alloc(sc->txdesc_tag, (void**)&sc->txdesc_ring, 9145d43fd68SRuslan Bukin BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, 9155d43fd68SRuslan Bukin &sc->txdesc_map); 9165d43fd68SRuslan Bukin if (error != 0) { 9175d43fd68SRuslan Bukin device_printf(sc->dev, 9185d43fd68SRuslan Bukin "could not allocate TX descriptor ring.\n"); 9195d43fd68SRuslan Bukin goto out; 9205d43fd68SRuslan Bukin } 9215d43fd68SRuslan Bukin 9225d43fd68SRuslan Bukin error = bus_dmamap_load(sc->txdesc_tag, sc->txdesc_map, 9235d43fd68SRuslan Bukin sc->txdesc_ring, TX_DESC_SIZE, dwc_get1paddr, 9245d43fd68SRuslan Bukin &sc->txdesc_ring_paddr, 0); 9255d43fd68SRuslan Bukin if (error != 0) { 9265d43fd68SRuslan Bukin device_printf(sc->dev, 9275d43fd68SRuslan Bukin "could not load TX descriptor ring map.\n"); 9285d43fd68SRuslan Bukin goto out; 9295d43fd68SRuslan Bukin } 9305d43fd68SRuslan Bukin 9315d43fd68SRuslan Bukin for (idx = 0; idx < TX_DESC_COUNT; idx++) { 9325d43fd68SRuslan Bukin nidx = next_txidx(sc, idx); 9338d43a868SMichal Meloun sc->txdesc_ring[idx].addr2 = sc->txdesc_ring_paddr + 9345d43fd68SRuslan Bukin (nidx * sizeof(struct dwc_hwdesc)); 9355d43fd68SRuslan Bukin } 9365d43fd68SRuslan Bukin 9375d43fd68SRuslan Bukin error = bus_dma_tag_create( 9385d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 9395d43fd68SRuslan Bukin 1, 0, /* alignment, boundary */ 9405d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9415d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9425d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 943e5232621SOleksandr Tymoshenko MCLBYTES*TX_MAP_MAX_SEGS, /* maxsize */ 944e5232621SOleksandr Tymoshenko TX_MAP_MAX_SEGS, /* nsegments */ 9455d43fd68SRuslan Bukin MCLBYTES, /* maxsegsize */ 9465d43fd68SRuslan Bukin 0, /* flags */ 9475d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9485d43fd68SRuslan Bukin &sc->txbuf_tag); 9495d43fd68SRuslan Bukin if (error != 0) { 9505d43fd68SRuslan Bukin device_printf(sc->dev, 9515d43fd68SRuslan Bukin "could not create TX ring DMA tag.\n"); 9525d43fd68SRuslan Bukin goto out; 9535d43fd68SRuslan Bukin } 9545d43fd68SRuslan Bukin 955e5232621SOleksandr Tymoshenko for (idx = 0; idx < TX_MAP_COUNT; idx++) { 9565d43fd68SRuslan Bukin error = bus_dmamap_create(sc->txbuf_tag, BUS_DMA_COHERENT, 9575d43fd68SRuslan Bukin &sc->txbuf_map[idx].map); 9585d43fd68SRuslan Bukin if (error != 0) { 9595d43fd68SRuslan Bukin device_printf(sc->dev, 9605d43fd68SRuslan Bukin "could not create TX buffer DMA map.\n"); 9615d43fd68SRuslan Bukin goto out; 9625d43fd68SRuslan Bukin } 9635d43fd68SRuslan Bukin } 9645d43fd68SRuslan Bukin 965e5232621SOleksandr Tymoshenko for (idx = 0; idx < TX_DESC_COUNT; idx++) 966e5232621SOleksandr Tymoshenko dwc_setup_txdesc(sc, idx, 0, 0, 0, false, false); 967e5232621SOleksandr Tymoshenko 9685d43fd68SRuslan Bukin /* 9695d43fd68SRuslan Bukin * Set up RX descriptor ring, descriptors, dma maps, and mbufs. 9705d43fd68SRuslan Bukin */ 9715d43fd68SRuslan Bukin error = bus_dma_tag_create( 9725d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 9735d43fd68SRuslan Bukin DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ 9745d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9755d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9765d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 9775d43fd68SRuslan Bukin RX_DESC_SIZE, 1, /* maxsize, nsegments */ 9785d43fd68SRuslan Bukin RX_DESC_SIZE, /* maxsegsize */ 9795d43fd68SRuslan Bukin 0, /* flags */ 9805d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9815d43fd68SRuslan Bukin &sc->rxdesc_tag); 9825d43fd68SRuslan Bukin if (error != 0) { 9835d43fd68SRuslan Bukin device_printf(sc->dev, 9845d43fd68SRuslan Bukin "could not create RX ring DMA tag.\n"); 9855d43fd68SRuslan Bukin goto out; 9865d43fd68SRuslan Bukin } 9875d43fd68SRuslan Bukin 9885d43fd68SRuslan Bukin error = bus_dmamem_alloc(sc->rxdesc_tag, (void **)&sc->rxdesc_ring, 9895d43fd68SRuslan Bukin BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, 9905d43fd68SRuslan Bukin &sc->rxdesc_map); 9915d43fd68SRuslan Bukin if (error != 0) { 9925d43fd68SRuslan Bukin device_printf(sc->dev, 9935d43fd68SRuslan Bukin "could not allocate RX descriptor ring.\n"); 9945d43fd68SRuslan Bukin goto out; 9955d43fd68SRuslan Bukin } 9965d43fd68SRuslan Bukin 9975d43fd68SRuslan Bukin error = bus_dmamap_load(sc->rxdesc_tag, sc->rxdesc_map, 9985d43fd68SRuslan Bukin sc->rxdesc_ring, RX_DESC_SIZE, dwc_get1paddr, 9995d43fd68SRuslan Bukin &sc->rxdesc_ring_paddr, 0); 10005d43fd68SRuslan Bukin if (error != 0) { 10015d43fd68SRuslan Bukin device_printf(sc->dev, 10025d43fd68SRuslan Bukin "could not load RX descriptor ring map.\n"); 10035d43fd68SRuslan Bukin goto out; 10045d43fd68SRuslan Bukin } 10055d43fd68SRuslan Bukin 10065d43fd68SRuslan Bukin error = bus_dma_tag_create( 10075d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 10085d43fd68SRuslan Bukin 1, 0, /* alignment, boundary */ 10095d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 10105d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 10115d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 10125d43fd68SRuslan Bukin MCLBYTES, 1, /* maxsize, nsegments */ 10135d43fd68SRuslan Bukin MCLBYTES, /* maxsegsize */ 10145d43fd68SRuslan Bukin 0, /* flags */ 10155d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 10165d43fd68SRuslan Bukin &sc->rxbuf_tag); 10175d43fd68SRuslan Bukin if (error != 0) { 10185d43fd68SRuslan Bukin device_printf(sc->dev, 10195d43fd68SRuslan Bukin "could not create RX buf DMA tag.\n"); 10205d43fd68SRuslan Bukin goto out; 10215d43fd68SRuslan Bukin } 10225d43fd68SRuslan Bukin 10235d43fd68SRuslan Bukin for (idx = 0; idx < RX_DESC_COUNT; idx++) { 10245d43fd68SRuslan Bukin error = bus_dmamap_create(sc->rxbuf_tag, BUS_DMA_COHERENT, 10255d43fd68SRuslan Bukin &sc->rxbuf_map[idx].map); 10265d43fd68SRuslan Bukin if (error != 0) { 10275d43fd68SRuslan Bukin device_printf(sc->dev, 10285d43fd68SRuslan Bukin "could not create RX buffer DMA map.\n"); 10295d43fd68SRuslan Bukin goto out; 10305d43fd68SRuslan Bukin } 10315d43fd68SRuslan Bukin if ((m = dwc_alloc_mbufcl(sc)) == NULL) { 10325d43fd68SRuslan Bukin device_printf(sc->dev, "Could not alloc mbuf\n"); 10335d43fd68SRuslan Bukin error = ENOMEM; 10345d43fd68SRuslan Bukin goto out; 10355d43fd68SRuslan Bukin } 10365d43fd68SRuslan Bukin if ((error = dwc_setup_rxbuf(sc, idx, m)) != 0) { 10375d43fd68SRuslan Bukin device_printf(sc->dev, 10385d43fd68SRuslan Bukin "could not create new RX buffer.\n"); 10395d43fd68SRuslan Bukin goto out; 10405d43fd68SRuslan Bukin } 10415d43fd68SRuslan Bukin } 10425d43fd68SRuslan Bukin 10435d43fd68SRuslan Bukin out: 10445d43fd68SRuslan Bukin if (error != 0) 10455d43fd68SRuslan Bukin return (ENXIO); 10465d43fd68SRuslan Bukin 10475d43fd68SRuslan Bukin return (0); 10485d43fd68SRuslan Bukin } 10495d43fd68SRuslan Bukin 105027b39e58SMitchell Horne static void 105127b39e58SMitchell Horne free_dma(struct dwc_softc *sc) 105227b39e58SMitchell Horne { 105327b39e58SMitchell Horne bus_dmamap_t map; 105427b39e58SMitchell Horne int idx; 105527b39e58SMitchell Horne 105627b39e58SMitchell Horne /* Clean up RX DMA resources and free mbufs. */ 105727b39e58SMitchell Horne for (idx = 0; idx < RX_DESC_COUNT; ++idx) { 105827b39e58SMitchell Horne if ((map = sc->rxbuf_map[idx].map) != NULL) { 105927b39e58SMitchell Horne bus_dmamap_unload(sc->rxbuf_tag, map); 106027b39e58SMitchell Horne bus_dmamap_destroy(sc->rxbuf_tag, map); 106127b39e58SMitchell Horne m_freem(sc->rxbuf_map[idx].mbuf); 106227b39e58SMitchell Horne } 106327b39e58SMitchell Horne } 106427b39e58SMitchell Horne if (sc->rxbuf_tag != NULL) 106527b39e58SMitchell Horne bus_dma_tag_destroy(sc->rxbuf_tag); 106627b39e58SMitchell Horne if (sc->rxdesc_map != NULL) { 106727b39e58SMitchell Horne bus_dmamap_unload(sc->rxdesc_tag, sc->rxdesc_map); 106827b39e58SMitchell Horne bus_dmamem_free(sc->rxdesc_tag, sc->rxdesc_ring, 106927b39e58SMitchell Horne sc->rxdesc_map); 107027b39e58SMitchell Horne } 107127b39e58SMitchell Horne if (sc->rxdesc_tag != NULL) 107227b39e58SMitchell Horne bus_dma_tag_destroy(sc->rxdesc_tag); 107327b39e58SMitchell Horne 107427b39e58SMitchell Horne /* Clean up TX DMA resources. */ 107527b39e58SMitchell Horne for (idx = 0; idx < TX_DESC_COUNT; ++idx) { 107627b39e58SMitchell Horne if ((map = sc->txbuf_map[idx].map) != NULL) { 107727b39e58SMitchell Horne /* TX maps are already unloaded. */ 107827b39e58SMitchell Horne bus_dmamap_destroy(sc->txbuf_tag, map); 107927b39e58SMitchell Horne } 108027b39e58SMitchell Horne } 108127b39e58SMitchell Horne if (sc->txbuf_tag != NULL) 108227b39e58SMitchell Horne bus_dma_tag_destroy(sc->txbuf_tag); 108327b39e58SMitchell Horne if (sc->txdesc_map != NULL) { 108427b39e58SMitchell Horne bus_dmamap_unload(sc->txdesc_tag, sc->txdesc_map); 108527b39e58SMitchell Horne bus_dmamem_free(sc->txdesc_tag, sc->txdesc_ring, 108627b39e58SMitchell Horne sc->txdesc_map); 108727b39e58SMitchell Horne } 108827b39e58SMitchell Horne if (sc->txdesc_tag != NULL) 108927b39e58SMitchell Horne bus_dma_tag_destroy(sc->txdesc_tag); 109027b39e58SMitchell Horne } 109127b39e58SMitchell Horne 10922a35d391SEmmanuel Vadot /* 10932a35d391SEmmanuel Vadot * if_ functions 10942a35d391SEmmanuel Vadot */ 10952a35d391SEmmanuel Vadot 1096f88e0af6SEmmanuel Vadot static void 10972a35d391SEmmanuel Vadot dwc_txstart_locked(struct dwc_softc *sc) 10985d43fd68SRuslan Bukin { 1099ca018790SMitchell Horne if_t ifp; 11002a35d391SEmmanuel Vadot struct mbuf *m; 11012a35d391SEmmanuel Vadot int enqueued; 11022a35d391SEmmanuel Vadot 11032a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 11042a35d391SEmmanuel Vadot 11052a35d391SEmmanuel Vadot if (!sc->link_is_up) 11062a35d391SEmmanuel Vadot return; 11072a35d391SEmmanuel Vadot 11082a35d391SEmmanuel Vadot ifp = sc->ifp; 11092a35d391SEmmanuel Vadot 11103bbd11eeSEmmanuel Vadot if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != 11113bbd11eeSEmmanuel Vadot IFF_DRV_RUNNING) 11122a35d391SEmmanuel Vadot return; 11132a35d391SEmmanuel Vadot 11142a35d391SEmmanuel Vadot enqueued = 0; 11152a35d391SEmmanuel Vadot 11162a35d391SEmmanuel Vadot for (;;) { 1117e5232621SOleksandr Tymoshenko if (sc->tx_desccount > (TX_DESC_COUNT - TX_MAP_MAX_SEGS + 1)) { 1118e5232621SOleksandr Tymoshenko if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 1119e5232621SOleksandr Tymoshenko break; 1120e5232621SOleksandr Tymoshenko } 1121e5232621SOleksandr Tymoshenko 1122e5232621SOleksandr Tymoshenko if (sc->tx_mapcount == (TX_MAP_COUNT - 1)) { 11233bbd11eeSEmmanuel Vadot if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 11242a35d391SEmmanuel Vadot break; 11252a35d391SEmmanuel Vadot } 11262a35d391SEmmanuel Vadot 11273bbd11eeSEmmanuel Vadot m = if_dequeue(ifp); 11282a35d391SEmmanuel Vadot if (m == NULL) 11292a35d391SEmmanuel Vadot break; 1130e5232621SOleksandr Tymoshenko if (dwc_setup_txbuf(sc, sc->tx_map_head, &m) != 0) { 11313bbd11eeSEmmanuel Vadot if_sendq_prepend(ifp, m); 1132e5232621SOleksandr Tymoshenko if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 11332a35d391SEmmanuel Vadot break; 11342a35d391SEmmanuel Vadot } 11352a371643SJustin Hibbits bpf_mtap_if(ifp, m); 1136e5232621SOleksandr Tymoshenko sc->tx_map_head = next_txidx(sc, sc->tx_map_head); 1137e5232621SOleksandr Tymoshenko sc->tx_mapcount++; 11382a35d391SEmmanuel Vadot ++enqueued; 11392a35d391SEmmanuel Vadot } 11402a35d391SEmmanuel Vadot 11412a35d391SEmmanuel Vadot if (enqueued != 0) { 11422a35d391SEmmanuel Vadot WRITE4(sc, TRANSMIT_POLL_DEMAND, 0x1); 11432a35d391SEmmanuel Vadot sc->tx_watchdog_count = WATCHDOG_TIMEOUT_SECS; 11442a35d391SEmmanuel Vadot } 11452a35d391SEmmanuel Vadot } 11462a35d391SEmmanuel Vadot 11472a35d391SEmmanuel Vadot static void 1148ca018790SMitchell Horne dwc_txstart(if_t ifp) 11492a35d391SEmmanuel Vadot { 1150ca018790SMitchell Horne struct dwc_softc *sc = if_getsoftc(ifp); 11512a35d391SEmmanuel Vadot 11522a35d391SEmmanuel Vadot DWC_LOCK(sc); 11532a35d391SEmmanuel Vadot dwc_txstart_locked(sc); 11542a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 11552a35d391SEmmanuel Vadot } 11562a35d391SEmmanuel Vadot 11572a35d391SEmmanuel Vadot static void 11582a35d391SEmmanuel Vadot dwc_init_locked(struct dwc_softc *sc) 11592a35d391SEmmanuel Vadot { 1160ca018790SMitchell Horne if_t ifp = sc->ifp; 11612a35d391SEmmanuel Vadot 11622a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 11632a35d391SEmmanuel Vadot 11643bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) 11652a35d391SEmmanuel Vadot return; 11662a35d391SEmmanuel Vadot 11676501fcdcSJiahao LI /* 11686501fcdcSJiahao LI * Call mii_mediachg() which will call back into dwc_miibus_statchg() 11696501fcdcSJiahao LI * to set up the remaining config registers based on current media. 11706501fcdcSJiahao LI */ 11716501fcdcSJiahao LI mii_mediachg(sc->mii_softc); 11726501fcdcSJiahao LI 11732a35d391SEmmanuel Vadot dwc_setup_rxfilter(sc); 11742a35d391SEmmanuel Vadot dwc_setup_core(sc); 11752a35d391SEmmanuel Vadot dwc_enable_mac(sc, true); 117635c9edabSJiahao Li dwc_enable_csum_offload(sc); 11772a35d391SEmmanuel Vadot dwc_init_dma(sc); 11782a35d391SEmmanuel Vadot 11792a35d391SEmmanuel Vadot if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); 11805d43fd68SRuslan Bukin 11812a35d391SEmmanuel Vadot callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); 11822a35d391SEmmanuel Vadot } 11832a35d391SEmmanuel Vadot 11842a35d391SEmmanuel Vadot static void 11852a35d391SEmmanuel Vadot dwc_init(void *if_softc) 11862a35d391SEmmanuel Vadot { 11872a35d391SEmmanuel Vadot struct dwc_softc *sc = if_softc; 11882a35d391SEmmanuel Vadot 11892a35d391SEmmanuel Vadot DWC_LOCK(sc); 11902a35d391SEmmanuel Vadot dwc_init_locked(sc); 11912a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 11922a35d391SEmmanuel Vadot } 11932a35d391SEmmanuel Vadot 11942a35d391SEmmanuel Vadot static void 11952a35d391SEmmanuel Vadot dwc_stop_locked(struct dwc_softc *sc) 11962a35d391SEmmanuel Vadot { 1197ca018790SMitchell Horne if_t ifp; 11982a35d391SEmmanuel Vadot 11992a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 12002a35d391SEmmanuel Vadot 12012a35d391SEmmanuel Vadot ifp = sc->ifp; 12023bbd11eeSEmmanuel Vadot if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 12032a35d391SEmmanuel Vadot sc->tx_watchdog_count = 0; 12042a35d391SEmmanuel Vadot sc->stats_harvest_count = 0; 12052a35d391SEmmanuel Vadot 12062a35d391SEmmanuel Vadot callout_stop(&sc->dwc_callout); 12072a35d391SEmmanuel Vadot 12082a35d391SEmmanuel Vadot dwc_stop_dma(sc); 12092a35d391SEmmanuel Vadot dwc_enable_mac(sc, false); 12102a35d391SEmmanuel Vadot } 12112a35d391SEmmanuel Vadot 12122a35d391SEmmanuel Vadot static int 1213ca018790SMitchell Horne dwc_ioctl(if_t ifp, u_long cmd, caddr_t data) 12142a35d391SEmmanuel Vadot { 12152a35d391SEmmanuel Vadot struct dwc_softc *sc; 12162a35d391SEmmanuel Vadot struct mii_data *mii; 12172a35d391SEmmanuel Vadot struct ifreq *ifr; 12183bbd11eeSEmmanuel Vadot int flags, mask, error; 12192a35d391SEmmanuel Vadot 1220ca018790SMitchell Horne sc = if_getsoftc(ifp); 12212a35d391SEmmanuel Vadot ifr = (struct ifreq *)data; 12222a35d391SEmmanuel Vadot 12232a35d391SEmmanuel Vadot error = 0; 12242a35d391SEmmanuel Vadot switch (cmd) { 12252a35d391SEmmanuel Vadot case SIOCSIFFLAGS: 12262a35d391SEmmanuel Vadot DWC_LOCK(sc); 12273bbd11eeSEmmanuel Vadot if (if_getflags(ifp) & IFF_UP) { 12283bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 12293bbd11eeSEmmanuel Vadot flags = if_getflags(ifp) ^ sc->if_flags; 12303bbd11eeSEmmanuel Vadot if ((flags & (IFF_PROMISC|IFF_ALLMULTI)) != 0) 12312a35d391SEmmanuel Vadot dwc_setup_rxfilter(sc); 12325d43fd68SRuslan Bukin } else { 12332a35d391SEmmanuel Vadot if (!sc->is_detaching) 12342a35d391SEmmanuel Vadot dwc_init_locked(sc); 12352a35d391SEmmanuel Vadot } 12362a35d391SEmmanuel Vadot } else { 12373bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) 12382a35d391SEmmanuel Vadot dwc_stop_locked(sc); 12392a35d391SEmmanuel Vadot } 12403bbd11eeSEmmanuel Vadot sc->if_flags = if_getflags(ifp); 12412a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 12422a35d391SEmmanuel Vadot break; 12432a35d391SEmmanuel Vadot case SIOCADDMULTI: 12442a35d391SEmmanuel Vadot case SIOCDELMULTI: 12453bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 12462a35d391SEmmanuel Vadot DWC_LOCK(sc); 12472a35d391SEmmanuel Vadot dwc_setup_rxfilter(sc); 12482a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 12492a35d391SEmmanuel Vadot } 12502a35d391SEmmanuel Vadot break; 12512a35d391SEmmanuel Vadot case SIOCSIFMEDIA: 12522a35d391SEmmanuel Vadot case SIOCGIFMEDIA: 12532a35d391SEmmanuel Vadot mii = sc->mii_softc; 12542a35d391SEmmanuel Vadot error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); 12552a35d391SEmmanuel Vadot break; 12562a35d391SEmmanuel Vadot case SIOCSIFCAP: 12573bbd11eeSEmmanuel Vadot mask = ifr->ifr_reqcap ^ if_getcapenable(ifp); 12582a35d391SEmmanuel Vadot if (mask & IFCAP_VLAN_MTU) { 12592a35d391SEmmanuel Vadot /* No work to do except acknowledge the change took */ 12603bbd11eeSEmmanuel Vadot if_togglecapenable(ifp, IFCAP_VLAN_MTU); 12612a35d391SEmmanuel Vadot } 126298ea5a7bSEmmanuel Vadot if (mask & IFCAP_RXCSUM) 126398ea5a7bSEmmanuel Vadot if_togglecapenable(ifp, IFCAP_RXCSUM); 126498ea5a7bSEmmanuel Vadot if (mask & IFCAP_TXCSUM) 126598ea5a7bSEmmanuel Vadot if_togglecapenable(ifp, IFCAP_TXCSUM); 126698ea5a7bSEmmanuel Vadot if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0) 126798ea5a7bSEmmanuel Vadot if_sethwassistbits(ifp, CSUM_IP | CSUM_UDP | CSUM_TCP, 0); 126898ea5a7bSEmmanuel Vadot else 126998ea5a7bSEmmanuel Vadot if_sethwassistbits(ifp, 0, CSUM_IP | CSUM_UDP | CSUM_TCP); 127035c9edabSJiahao Li 127135c9edabSJiahao Li if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 127235c9edabSJiahao Li DWC_LOCK(sc); 127335c9edabSJiahao Li dwc_enable_csum_offload(sc); 127435c9edabSJiahao Li DWC_UNLOCK(sc); 127535c9edabSJiahao Li } 12762a35d391SEmmanuel Vadot break; 12772a35d391SEmmanuel Vadot 12782a35d391SEmmanuel Vadot default: 12792a35d391SEmmanuel Vadot error = ether_ioctl(ifp, cmd, data); 12802a35d391SEmmanuel Vadot break; 12812a35d391SEmmanuel Vadot } 12822a35d391SEmmanuel Vadot 12832a35d391SEmmanuel Vadot return (error); 12842a35d391SEmmanuel Vadot } 12852a35d391SEmmanuel Vadot 12862a35d391SEmmanuel Vadot /* 12872a35d391SEmmanuel Vadot * Interrupts functions 12882a35d391SEmmanuel Vadot */ 12892a35d391SEmmanuel Vadot 12902a35d391SEmmanuel Vadot static void 12912a35d391SEmmanuel Vadot dwc_txfinish_locked(struct dwc_softc *sc) 12922a35d391SEmmanuel Vadot { 12932a35d391SEmmanuel Vadot struct dwc_bufmap *bmap; 12942a35d391SEmmanuel Vadot struct dwc_hwdesc *desc; 1295ca018790SMitchell Horne if_t ifp; 1296e5232621SOleksandr Tymoshenko int idx, last_idx; 1297e5232621SOleksandr Tymoshenko bool map_finished; 12982a35d391SEmmanuel Vadot 12992a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 13002a35d391SEmmanuel Vadot 13012a35d391SEmmanuel Vadot ifp = sc->ifp; 1302e5232621SOleksandr Tymoshenko /* check if all descriptors of the map are done */ 1303e5232621SOleksandr Tymoshenko while (sc->tx_map_tail != sc->tx_map_head) { 1304e5232621SOleksandr Tymoshenko map_finished = true; 1305e5232621SOleksandr Tymoshenko bmap = &sc->txbuf_map[sc->tx_map_tail]; 1306e5232621SOleksandr Tymoshenko idx = sc->tx_desc_tail; 1307e5232621SOleksandr Tymoshenko last_idx = next_txidx(sc, bmap->last_desc_idx); 1308e5232621SOleksandr Tymoshenko while (idx != last_idx) { 1309e5232621SOleksandr Tymoshenko desc = &sc->txdesc_ring[idx]; 1310e5232621SOleksandr Tymoshenko if ((desc->desc0 & TDESC0_OWN) != 0) { 1311e5232621SOleksandr Tymoshenko map_finished = false; 13122a35d391SEmmanuel Vadot break; 1313e5232621SOleksandr Tymoshenko } 1314e5232621SOleksandr Tymoshenko idx = next_txidx(sc, idx); 1315e5232621SOleksandr Tymoshenko } 1316e5232621SOleksandr Tymoshenko 1317e5232621SOleksandr Tymoshenko if (!map_finished) 1318e5232621SOleksandr Tymoshenko break; 13192a35d391SEmmanuel Vadot bus_dmamap_sync(sc->txbuf_tag, bmap->map, 13202a35d391SEmmanuel Vadot BUS_DMASYNC_POSTWRITE); 13212a35d391SEmmanuel Vadot bus_dmamap_unload(sc->txbuf_tag, bmap->map); 13222a35d391SEmmanuel Vadot m_freem(bmap->mbuf); 13232a35d391SEmmanuel Vadot bmap->mbuf = NULL; 1324e5232621SOleksandr Tymoshenko sc->tx_mapcount--; 1325e5232621SOleksandr Tymoshenko while (sc->tx_desc_tail != last_idx) { 1326e5232621SOleksandr Tymoshenko dwc_setup_txdesc(sc, sc->tx_desc_tail, 0, 0, 0, false, false); 1327e5232621SOleksandr Tymoshenko sc->tx_desc_tail = next_txidx(sc, sc->tx_desc_tail); 1328e5232621SOleksandr Tymoshenko } 1329e5232621SOleksandr Tymoshenko sc->tx_map_tail = next_txidx(sc, sc->tx_map_tail); 13303bbd11eeSEmmanuel Vadot if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); 13312a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 13322a35d391SEmmanuel Vadot } 13332a35d391SEmmanuel Vadot 13342a35d391SEmmanuel Vadot /* If there are no buffers outstanding, muzzle the watchdog. */ 1335e5232621SOleksandr Tymoshenko if (sc->tx_desc_tail == sc->tx_desc_head) { 13362a35d391SEmmanuel Vadot sc->tx_watchdog_count = 0; 13375d43fd68SRuslan Bukin } 13385d43fd68SRuslan Bukin } 13395d43fd68SRuslan Bukin 13402a35d391SEmmanuel Vadot static void 13412a35d391SEmmanuel Vadot dwc_rxfinish_locked(struct dwc_softc *sc) 13422a35d391SEmmanuel Vadot { 13432a35d391SEmmanuel Vadot struct mbuf *m; 13442a35d391SEmmanuel Vadot int error, idx; 13452a35d391SEmmanuel Vadot struct dwc_hwdesc *desc; 13462a35d391SEmmanuel Vadot 13472a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 13482a35d391SEmmanuel Vadot for (;;) { 13492a35d391SEmmanuel Vadot idx = sc->rx_idx; 13502a35d391SEmmanuel Vadot desc = sc->rxdesc_ring + idx; 13512a35d391SEmmanuel Vadot if ((desc->desc0 & RDESC0_OWN) != 0) 13522a35d391SEmmanuel Vadot break; 13532a35d391SEmmanuel Vadot 13542a35d391SEmmanuel Vadot m = dwc_rxfinish_one(sc, desc, sc->rxbuf_map + idx); 13552a35d391SEmmanuel Vadot if (m == NULL) { 13562a35d391SEmmanuel Vadot wmb(); 13572a35d391SEmmanuel Vadot desc->desc0 = RDESC0_OWN; 13582a35d391SEmmanuel Vadot wmb(); 13592a35d391SEmmanuel Vadot } else { 13602a35d391SEmmanuel Vadot /* We cannot create hole in RX ring */ 13612a35d391SEmmanuel Vadot error = dwc_setup_rxbuf(sc, idx, m); 13622a35d391SEmmanuel Vadot if (error != 0) 13632a35d391SEmmanuel Vadot panic("dwc_setup_rxbuf failed: error %d\n", 13642a35d391SEmmanuel Vadot error); 136598ea5a7bSEmmanuel Vadot 13662a35d391SEmmanuel Vadot } 13672a35d391SEmmanuel Vadot sc->rx_idx = next_rxidx(sc, sc->rx_idx); 13682a35d391SEmmanuel Vadot } 13692a35d391SEmmanuel Vadot } 13702a35d391SEmmanuel Vadot 13712a35d391SEmmanuel Vadot static void 13722a35d391SEmmanuel Vadot dwc_intr(void *arg) 13732a35d391SEmmanuel Vadot { 13742a35d391SEmmanuel Vadot struct dwc_softc *sc; 13752a35d391SEmmanuel Vadot uint32_t reg; 13762a35d391SEmmanuel Vadot 13772a35d391SEmmanuel Vadot sc = arg; 13782a35d391SEmmanuel Vadot 13792a35d391SEmmanuel Vadot DWC_LOCK(sc); 13802a35d391SEmmanuel Vadot 13812a35d391SEmmanuel Vadot reg = READ4(sc, INTERRUPT_STATUS); 13822a35d391SEmmanuel Vadot if (reg) 13832a35d391SEmmanuel Vadot READ4(sc, SGMII_RGMII_SMII_CTRL_STATUS); 13842a35d391SEmmanuel Vadot 13852a35d391SEmmanuel Vadot reg = READ4(sc, DMA_STATUS); 13862a35d391SEmmanuel Vadot if (reg & DMA_STATUS_NIS) { 13872a35d391SEmmanuel Vadot if (reg & DMA_STATUS_RI) 13882a35d391SEmmanuel Vadot dwc_rxfinish_locked(sc); 13892a35d391SEmmanuel Vadot 13902a35d391SEmmanuel Vadot if (reg & DMA_STATUS_TI) { 13912a35d391SEmmanuel Vadot dwc_txfinish_locked(sc); 13922a35d391SEmmanuel Vadot dwc_txstart_locked(sc); 13932a35d391SEmmanuel Vadot } 13942a35d391SEmmanuel Vadot } 13952a35d391SEmmanuel Vadot 13962a35d391SEmmanuel Vadot if (reg & DMA_STATUS_AIS) { 13972a35d391SEmmanuel Vadot if (reg & DMA_STATUS_FBI) { 13982a35d391SEmmanuel Vadot /* Fatal bus error */ 13992a35d391SEmmanuel Vadot device_printf(sc->dev, 14002a35d391SEmmanuel Vadot "Ethernet DMA error, restarting controller.\n"); 14012a35d391SEmmanuel Vadot dwc_stop_locked(sc); 14022a35d391SEmmanuel Vadot dwc_init_locked(sc); 14032a35d391SEmmanuel Vadot } 14042a35d391SEmmanuel Vadot } 14052a35d391SEmmanuel Vadot 14062a35d391SEmmanuel Vadot WRITE4(sc, DMA_STATUS, reg & DMA_STATUS_INTR_MASK); 14072a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 14082a35d391SEmmanuel Vadot } 14092a35d391SEmmanuel Vadot 14102a35d391SEmmanuel Vadot /* 14112a35d391SEmmanuel Vadot * Stats 14122a35d391SEmmanuel Vadot */ 14132a35d391SEmmanuel Vadot 14142a35d391SEmmanuel Vadot static void dwc_clear_stats(struct dwc_softc *sc) 14152a35d391SEmmanuel Vadot { 14162a35d391SEmmanuel Vadot uint32_t reg; 14172a35d391SEmmanuel Vadot 14182a35d391SEmmanuel Vadot reg = READ4(sc, MMC_CONTROL); 14192a35d391SEmmanuel Vadot reg |= (MMC_CONTROL_CNTRST); 14202a35d391SEmmanuel Vadot WRITE4(sc, MMC_CONTROL, reg); 14212a35d391SEmmanuel Vadot } 14222a35d391SEmmanuel Vadot 14232a35d391SEmmanuel Vadot static void 14242a35d391SEmmanuel Vadot dwc_harvest_stats(struct dwc_softc *sc) 14252a35d391SEmmanuel Vadot { 1426ca018790SMitchell Horne if_t ifp; 14272a35d391SEmmanuel Vadot 14282a35d391SEmmanuel Vadot /* We don't need to harvest too often. */ 14292a35d391SEmmanuel Vadot if (++sc->stats_harvest_count < STATS_HARVEST_INTERVAL) 14302a35d391SEmmanuel Vadot return; 14312a35d391SEmmanuel Vadot 14322a35d391SEmmanuel Vadot sc->stats_harvest_count = 0; 14332a35d391SEmmanuel Vadot ifp = sc->ifp; 14342a35d391SEmmanuel Vadot 14352a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_IERRORS, 14362a35d391SEmmanuel Vadot READ4(sc, RXOVERSIZE_G) + READ4(sc, RXUNDERSIZE_G) + 14372a35d391SEmmanuel Vadot READ4(sc, RXCRCERROR) + READ4(sc, RXALIGNMENTERROR) + 14382a35d391SEmmanuel Vadot READ4(sc, RXRUNTERROR) + READ4(sc, RXJABBERERROR) + 14392a35d391SEmmanuel Vadot READ4(sc, RXLENGTHERROR)); 14402a35d391SEmmanuel Vadot 14412a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OERRORS, 14422a35d391SEmmanuel Vadot READ4(sc, TXOVERSIZE_G) + READ4(sc, TXEXCESSDEF) + 14432a35d391SEmmanuel Vadot READ4(sc, TXCARRIERERR) + READ4(sc, TXUNDERFLOWERROR)); 14442a35d391SEmmanuel Vadot 14452a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 14462a35d391SEmmanuel Vadot READ4(sc, TXEXESSCOL) + READ4(sc, TXLATECOL)); 14472a35d391SEmmanuel Vadot 14482a35d391SEmmanuel Vadot dwc_clear_stats(sc); 14492a35d391SEmmanuel Vadot } 14502a35d391SEmmanuel Vadot 14512a35d391SEmmanuel Vadot static void 14522a35d391SEmmanuel Vadot dwc_tick(void *arg) 14532a35d391SEmmanuel Vadot { 14542a35d391SEmmanuel Vadot struct dwc_softc *sc; 1455ca018790SMitchell Horne if_t ifp; 14562a35d391SEmmanuel Vadot int link_was_up; 14572a35d391SEmmanuel Vadot 14582a35d391SEmmanuel Vadot sc = arg; 14592a35d391SEmmanuel Vadot 14602a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 14612a35d391SEmmanuel Vadot 14622a35d391SEmmanuel Vadot ifp = sc->ifp; 14632a35d391SEmmanuel Vadot 14643bbd11eeSEmmanuel Vadot if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) 14652a35d391SEmmanuel Vadot return; 14662a35d391SEmmanuel Vadot 14672a35d391SEmmanuel Vadot /* 14682a35d391SEmmanuel Vadot * Typical tx watchdog. If this fires it indicates that we enqueued 14692a35d391SEmmanuel Vadot * packets for output and never got a txdone interrupt for them. Maybe 14702a35d391SEmmanuel Vadot * it's a missed interrupt somehow, just pretend we got one. 14712a35d391SEmmanuel Vadot */ 14722a35d391SEmmanuel Vadot if (sc->tx_watchdog_count > 0) { 14732a35d391SEmmanuel Vadot if (--sc->tx_watchdog_count == 0) { 14742a35d391SEmmanuel Vadot dwc_txfinish_locked(sc); 14752a35d391SEmmanuel Vadot } 14762a35d391SEmmanuel Vadot } 14772a35d391SEmmanuel Vadot 14782a35d391SEmmanuel Vadot /* Gather stats from hardware counters. */ 14792a35d391SEmmanuel Vadot dwc_harvest_stats(sc); 14802a35d391SEmmanuel Vadot 14812a35d391SEmmanuel Vadot /* Check the media status. */ 14822a35d391SEmmanuel Vadot link_was_up = sc->link_is_up; 14832a35d391SEmmanuel Vadot mii_tick(sc->mii_softc); 14842a35d391SEmmanuel Vadot if (sc->link_is_up && !link_was_up) 14852a35d391SEmmanuel Vadot dwc_txstart_locked(sc); 14862a35d391SEmmanuel Vadot 14872a35d391SEmmanuel Vadot /* Schedule another check one second from now. */ 14882a35d391SEmmanuel Vadot callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); 14892a35d391SEmmanuel Vadot } 14902a35d391SEmmanuel Vadot 1491d7acb49aSJared McNeill static int 1492f114aaddSEmmanuel Vadot dwc_reset_phy(struct dwc_softc *sc) 1493d7acb49aSJared McNeill { 1494d7acb49aSJared McNeill pcell_t gpio_prop[4]; 1495d7acb49aSJared McNeill pcell_t delay_prop[3]; 1496f114aaddSEmmanuel Vadot phandle_t gpio_node; 1497d7acb49aSJared McNeill device_t gpio; 1498d7acb49aSJared McNeill uint32_t pin, flags; 1499d7acb49aSJared McNeill uint32_t pin_value; 1500d7acb49aSJared McNeill 1501f114aaddSEmmanuel Vadot /* 1502f114aaddSEmmanuel Vadot * All those properties are deprecated but still used in some DTS. 1503f114aaddSEmmanuel Vadot * The new way to deal with this is to use the generic bindings 1504f114aaddSEmmanuel Vadot * present in the ethernet-phy node. 1505f114aaddSEmmanuel Vadot */ 1506f114aaddSEmmanuel Vadot if (OF_getencprop(sc->node, "snps,reset-gpio", 1507d7acb49aSJared McNeill gpio_prop, sizeof(gpio_prop)) <= 0) 1508d7acb49aSJared McNeill return (0); 1509d7acb49aSJared McNeill 1510f114aaddSEmmanuel Vadot if (OF_getencprop(sc->node, "snps,reset-delays-us", 1511d7acb49aSJared McNeill delay_prop, sizeof(delay_prop)) <= 0) { 1512f114aaddSEmmanuel Vadot device_printf(sc->dev, 1513d7acb49aSJared McNeill "Wrong property for snps,reset-delays-us"); 1514d7acb49aSJared McNeill return (ENXIO); 1515d7acb49aSJared McNeill } 1516d7acb49aSJared McNeill 1517d7acb49aSJared McNeill gpio_node = OF_node_from_xref(gpio_prop[0]); 1518d7acb49aSJared McNeill if ((gpio = OF_device_from_xref(gpio_prop[0])) == NULL) { 1519f114aaddSEmmanuel Vadot device_printf(sc->dev, 1520d7acb49aSJared McNeill "Can't find gpio controller for phy reset\n"); 1521d7acb49aSJared McNeill return (ENXIO); 1522d7acb49aSJared McNeill } 1523d7acb49aSJared McNeill 1524f114aaddSEmmanuel Vadot if (GPIO_MAP_GPIOS(gpio, sc->node, gpio_node, 152573a1170aSPedro F. Giffuni nitems(gpio_prop) - 1, 1526d7acb49aSJared McNeill gpio_prop + 1, &pin, &flags) != 0) { 1527f114aaddSEmmanuel Vadot device_printf(sc->dev, "Can't map gpio for phy reset\n"); 1528d7acb49aSJared McNeill return (ENXIO); 1529d7acb49aSJared McNeill } 1530d7acb49aSJared McNeill 1531d7acb49aSJared McNeill pin_value = GPIO_PIN_LOW; 1532f114aaddSEmmanuel Vadot if (OF_hasprop(sc->node, "snps,reset-active-low")) 1533d7acb49aSJared McNeill pin_value = GPIO_PIN_HIGH; 1534d7acb49aSJared McNeill 1535d7acb49aSJared McNeill GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT); 1536d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 1537c069412eSEmmanuel Vadot DELAY(delay_prop[0] * 5); 1538d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, !pin_value); 1539c069412eSEmmanuel Vadot DELAY(delay_prop[1] * 5); 1540d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 1541c069412eSEmmanuel Vadot DELAY(delay_prop[2] * 5); 1542d7acb49aSJared McNeill 1543d7acb49aSJared McNeill return (0); 1544d7acb49aSJared McNeill } 1545d7acb49aSJared McNeill 15466a05f063SJared McNeill static int 15476a05f063SJared McNeill dwc_clock_init(device_t dev) 15486a05f063SJared McNeill { 15496a05f063SJared McNeill hwreset_t rst; 15506a05f063SJared McNeill clk_t clk; 15516a05f063SJared McNeill int error; 1552824cfb47SOleksandr Tymoshenko int64_t freq; 15536a05f063SJared McNeill 1554824cfb47SOleksandr Tymoshenko /* Enable clocks */ 1555dac93553SMichal Meloun if (clk_get_by_ofw_name(dev, 0, "stmmaceth", &clk) == 0) { 15566a05f063SJared McNeill error = clk_enable(clk); 15576a05f063SJared McNeill if (error != 0) { 15586a05f063SJared McNeill device_printf(dev, "could not enable main clock\n"); 15596a05f063SJared McNeill return (error); 15606a05f063SJared McNeill } 1561824cfb47SOleksandr Tymoshenko if (bootverbose) { 1562824cfb47SOleksandr Tymoshenko clk_get_freq(clk, &freq); 1563fc254a2eSLi-Wen Hsu device_printf(dev, "MAC clock(%s) freq: %jd\n", 1564fc254a2eSLi-Wen Hsu clk_get_name(clk), (intmax_t)freq); 1565824cfb47SOleksandr Tymoshenko } 1566824cfb47SOleksandr Tymoshenko } 1567824cfb47SOleksandr Tymoshenko else { 1568824cfb47SOleksandr Tymoshenko device_printf(dev, "could not find clock stmmaceth\n"); 15696a05f063SJared McNeill } 15706a05f063SJared McNeill 15716a05f063SJared McNeill /* De-assert reset */ 1572dac93553SMichal Meloun if (hwreset_get_by_ofw_name(dev, 0, "stmmaceth", &rst) == 0) { 15736a05f063SJared McNeill error = hwreset_deassert(rst); 15746a05f063SJared McNeill if (error != 0) { 15756a05f063SJared McNeill device_printf(dev, "could not de-assert reset\n"); 15766a05f063SJared McNeill return (error); 15776a05f063SJared McNeill } 15786a05f063SJared McNeill } 15796a05f063SJared McNeill 15806a05f063SJared McNeill return (0); 15816a05f063SJared McNeill } 15826a05f063SJared McNeill 158327f03f16SEmmanuel Vadot /* 158427f03f16SEmmanuel Vadot * Probe/Attach functions 158527f03f16SEmmanuel Vadot */ 158627f03f16SEmmanuel Vadot 15875d43fd68SRuslan Bukin static int 15885d43fd68SRuslan Bukin dwc_probe(device_t dev) 15895d43fd68SRuslan Bukin { 15905d43fd68SRuslan Bukin 15915d43fd68SRuslan Bukin if (!ofw_bus_status_okay(dev)) 15925d43fd68SRuslan Bukin return (ENXIO); 15935d43fd68SRuslan Bukin 15945d43fd68SRuslan Bukin if (!ofw_bus_is_compatible(dev, "snps,dwmac")) 15955d43fd68SRuslan Bukin return (ENXIO); 15965d43fd68SRuslan Bukin 15975d43fd68SRuslan Bukin device_set_desc(dev, "Gigabit Ethernet Controller"); 15985d43fd68SRuslan Bukin return (BUS_PROBE_DEFAULT); 15995d43fd68SRuslan Bukin } 16005d43fd68SRuslan Bukin 16015d43fd68SRuslan Bukin static int 16025d43fd68SRuslan Bukin dwc_attach(device_t dev) 16035d43fd68SRuslan Bukin { 16045d43fd68SRuslan Bukin uint8_t macaddr[ETHER_ADDR_LEN]; 16055d43fd68SRuslan Bukin struct dwc_softc *sc; 1606ca018790SMitchell Horne if_t ifp; 1607ff0752c8SLuiz Otavio O Souza int error, i; 1608ff0752c8SLuiz Otavio O Souza uint32_t reg; 16093e5cd548SEmmanuel Vadot uint32_t txpbl, rxpbl, pbl; 16105e38d9e4SEmmanuel Vadot bool nopblx8 = false; 16115e38d9e4SEmmanuel Vadot bool fixed_burst = false; 1612f4bb6ea5SEmmanuel Vadot bool mixed_burst = false; 1613*b69c49d1SEmmanuel Vadot bool aal = false; 16145d43fd68SRuslan Bukin 16155d43fd68SRuslan Bukin sc = device_get_softc(dev); 16165d43fd68SRuslan Bukin sc->dev = dev; 16175d43fd68SRuslan Bukin sc->rx_idx = 0; 1618e5232621SOleksandr Tymoshenko sc->tx_desccount = TX_DESC_COUNT; 1619e5232621SOleksandr Tymoshenko sc->tx_mapcount = 0; 16205df53927SLuiz Otavio O Souza sc->mii_clk = IF_DWC_MII_CLK(dev); 16215df53927SLuiz Otavio O Souza sc->mactype = IF_DWC_MAC_TYPE(dev); 16225df53927SLuiz Otavio O Souza 1623f114aaddSEmmanuel Vadot sc->node = ofw_bus_get_node(dev); 1624f114aaddSEmmanuel Vadot switch (mii_fdt_get_contype(sc->node)) { 1625f77d8d10SEmmanuel Vadot case MII_CONTYPE_RGMII: 1626f77d8d10SEmmanuel Vadot case MII_CONTYPE_RGMII_ID: 1627f77d8d10SEmmanuel Vadot case MII_CONTYPE_RGMII_RXID: 1628f77d8d10SEmmanuel Vadot case MII_CONTYPE_RGMII_TXID: 1629824cfb47SOleksandr Tymoshenko sc->phy_mode = PHY_MODE_RGMII; 1630f77d8d10SEmmanuel Vadot break; 1631f77d8d10SEmmanuel Vadot case MII_CONTYPE_RMII: 1632824cfb47SOleksandr Tymoshenko sc->phy_mode = PHY_MODE_RMII; 1633f77d8d10SEmmanuel Vadot break; 1634da6252a6SEmmanuel Vadot case MII_CONTYPE_MII: 1635da6252a6SEmmanuel Vadot sc->phy_mode = PHY_MODE_MII; 1636da6252a6SEmmanuel Vadot break; 1637f77d8d10SEmmanuel Vadot default: 1638f77d8d10SEmmanuel Vadot device_printf(dev, "Unsupported MII type\n"); 1639f77d8d10SEmmanuel Vadot return (ENXIO); 1640824cfb47SOleksandr Tymoshenko } 1641824cfb47SOleksandr Tymoshenko 1642f114aaddSEmmanuel Vadot if (OF_getencprop(sc->node, "snps,pbl", &pbl, sizeof(uint32_t)) <= 0) 16433e5cd548SEmmanuel Vadot pbl = BUS_MODE_DEFAULT_PBL; 1644f114aaddSEmmanuel Vadot if (OF_getencprop(sc->node, "snps,txpbl", &txpbl, sizeof(uint32_t)) <= 0) 16453e5cd548SEmmanuel Vadot txpbl = pbl; 1646f114aaddSEmmanuel Vadot if (OF_getencprop(sc->node, "snps,rxpbl", &rxpbl, sizeof(uint32_t)) <= 0) 16473e5cd548SEmmanuel Vadot rxpbl = pbl; 1648f114aaddSEmmanuel Vadot if (OF_hasprop(sc->node, "snps,no-pbl-x8") == 1) 16495e38d9e4SEmmanuel Vadot nopblx8 = true; 1650f114aaddSEmmanuel Vadot if (OF_hasprop(sc->node, "snps,fixed-burst") == 1) 16515e38d9e4SEmmanuel Vadot fixed_burst = true; 1652f4bb6ea5SEmmanuel Vadot if (OF_hasprop(sc->node, "snps,mixed-burst") == 1) 1653f4bb6ea5SEmmanuel Vadot mixed_burst = true; 1654*b69c49d1SEmmanuel Vadot if (OF_hasprop(sc->node, "snps,aal") == 1) 1655*b69c49d1SEmmanuel Vadot aal = true; 16565e38d9e4SEmmanuel Vadot 16575df53927SLuiz Otavio O Souza if (IF_DWC_INIT(dev) != 0) 16585df53927SLuiz Otavio O Souza return (ENXIO); 16595d43fd68SRuslan Bukin 16606a05f063SJared McNeill if (dwc_clock_init(dev) != 0) 16616a05f063SJared McNeill return (ENXIO); 16626a05f063SJared McNeill 16635d43fd68SRuslan Bukin if (bus_alloc_resources(dev, dwc_spec, sc->res)) { 16645d43fd68SRuslan Bukin device_printf(dev, "could not allocate resources\n"); 16655d43fd68SRuslan Bukin return (ENXIO); 16665d43fd68SRuslan Bukin } 16675d43fd68SRuslan Bukin 16685d43fd68SRuslan Bukin /* Read MAC before reset */ 1669f88e0af6SEmmanuel Vadot dwc_get_hwaddr(sc, macaddr); 16705d43fd68SRuslan Bukin 1671d7acb49aSJared McNeill /* Reset the PHY if needed */ 1672f114aaddSEmmanuel Vadot if (dwc_reset_phy(sc) != 0) { 1673d7acb49aSJared McNeill device_printf(dev, "Can't reset the PHY\n"); 167430f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 1675d7acb49aSJared McNeill return (ENXIO); 1676d7acb49aSJared McNeill } 1677d7acb49aSJared McNeill 16785d43fd68SRuslan Bukin /* Reset */ 16795d43fd68SRuslan Bukin reg = READ4(sc, BUS_MODE); 16805d43fd68SRuslan Bukin reg |= (BUS_MODE_SWR); 16815d43fd68SRuslan Bukin WRITE4(sc, BUS_MODE, reg); 16825d43fd68SRuslan Bukin 1683d8e5258dSRuslan Bukin for (i = 0; i < MAC_RESET_TIMEOUT; i++) { 16845d43fd68SRuslan Bukin if ((READ4(sc, BUS_MODE) & BUS_MODE_SWR) == 0) 16855d43fd68SRuslan Bukin break; 16865d43fd68SRuslan Bukin DELAY(10); 16875d43fd68SRuslan Bukin } 1688d8e5258dSRuslan Bukin if (i >= MAC_RESET_TIMEOUT) { 16895d43fd68SRuslan Bukin device_printf(sc->dev, "Can't reset DWC.\n"); 169030f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 16915d43fd68SRuslan Bukin return (ENXIO); 16925d43fd68SRuslan Bukin } 16935d43fd68SRuslan Bukin 16945e38d9e4SEmmanuel Vadot reg = BUS_MODE_USP; 16955e38d9e4SEmmanuel Vadot if (!nopblx8) 16965e38d9e4SEmmanuel Vadot reg |= BUS_MODE_EIGHTXPBL; 16975e38d9e4SEmmanuel Vadot reg |= (txpbl << BUS_MODE_PBL_SHIFT); 16985e38d9e4SEmmanuel Vadot reg |= (rxpbl << BUS_MODE_RPBL_SHIFT); 16995e38d9e4SEmmanuel Vadot if (fixed_burst) 17005e38d9e4SEmmanuel Vadot reg |= BUS_MODE_FIXEDBURST; 1701f4bb6ea5SEmmanuel Vadot if (mixed_burst) 1702f4bb6ea5SEmmanuel Vadot reg |= BUS_MODE_MIXEDBURST; 1703*b69c49d1SEmmanuel Vadot if (aal) 1704*b69c49d1SEmmanuel Vadot reg |= BUS_MODE_AAL; 17053e5cd548SEmmanuel Vadot 17065d43fd68SRuslan Bukin WRITE4(sc, BUS_MODE, reg); 17075d43fd68SRuslan Bukin 17085d43fd68SRuslan Bukin /* 17095d43fd68SRuslan Bukin * DMA must be stop while changing descriptor list addresses. 17105d43fd68SRuslan Bukin */ 17115d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 17125d43fd68SRuslan Bukin reg &= ~(MODE_ST | MODE_SR); 17135d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 17145d43fd68SRuslan Bukin 171530f16ad4SEmmanuel Vadot if (setup_dma(sc)) { 171630f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 17175d43fd68SRuslan Bukin return (ENXIO); 171830f16ad4SEmmanuel Vadot } 17195d43fd68SRuslan Bukin 17205d43fd68SRuslan Bukin /* Setup addresses */ 17215d43fd68SRuslan Bukin WRITE4(sc, RX_DESCR_LIST_ADDR, sc->rxdesc_ring_paddr); 17225d43fd68SRuslan Bukin WRITE4(sc, TX_DESCR_LIST_ADDR, sc->txdesc_ring_paddr); 17235d43fd68SRuslan Bukin 1724d8e5258dSRuslan Bukin mtx_init(&sc->mtx, device_get_nameunit(sc->dev), 1725d8e5258dSRuslan Bukin MTX_NETWORK_LOCK, MTX_DEF); 1726d8e5258dSRuslan Bukin 1727d8e5258dSRuslan Bukin callout_init_mtx(&sc->dwc_callout, &sc->mtx, 0); 1728d8e5258dSRuslan Bukin 1729d8e5258dSRuslan Bukin /* Setup interrupt handler. */ 1730d8e5258dSRuslan Bukin error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE, 1731d8e5258dSRuslan Bukin NULL, dwc_intr, sc, &sc->intr_cookie); 1732d8e5258dSRuslan Bukin if (error != 0) { 1733d8e5258dSRuslan Bukin device_printf(dev, "could not setup interrupt handler.\n"); 173430f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 1735d8e5258dSRuslan Bukin return (ENXIO); 1736d8e5258dSRuslan Bukin } 1737d8e5258dSRuslan Bukin 17385d43fd68SRuslan Bukin /* Set up the ethernet interface. */ 17395d43fd68SRuslan Bukin sc->ifp = ifp = if_alloc(IFT_ETHER); 17405d43fd68SRuslan Bukin 1741ca018790SMitchell Horne if_setsoftc(ifp, sc); 17425d43fd68SRuslan Bukin if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 17433bbd11eeSEmmanuel Vadot if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); 17443bbd11eeSEmmanuel Vadot if_setstartfn(ifp, dwc_txstart); 17453bbd11eeSEmmanuel Vadot if_setioctlfn(ifp, dwc_ioctl); 17463bbd11eeSEmmanuel Vadot if_setinitfn(ifp, dwc_init); 1747e5232621SOleksandr Tymoshenko if_setsendqlen(ifp, TX_MAP_COUNT - 1); 17483bbd11eeSEmmanuel Vadot if_setsendqready(sc->ifp); 174998ea5a7bSEmmanuel Vadot if_sethwassist(sc->ifp, CSUM_IP | CSUM_UDP | CSUM_TCP); 175098ea5a7bSEmmanuel Vadot if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM); 17513bbd11eeSEmmanuel Vadot if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp)); 17525d43fd68SRuslan Bukin 17535d43fd68SRuslan Bukin /* Attach the mii driver. */ 17545d43fd68SRuslan Bukin error = mii_attach(dev, &sc->miibus, ifp, dwc_media_change, 17555d43fd68SRuslan Bukin dwc_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, 17565d43fd68SRuslan Bukin MII_OFFSET_ANY, 0); 17575d43fd68SRuslan Bukin 17585d43fd68SRuslan Bukin if (error != 0) { 17595d43fd68SRuslan Bukin device_printf(dev, "PHY attach failed\n"); 176030f16ad4SEmmanuel Vadot bus_teardown_intr(dev, sc->res[1], sc->intr_cookie); 176130f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 17625d43fd68SRuslan Bukin return (ENXIO); 17635d43fd68SRuslan Bukin } 17645d43fd68SRuslan Bukin sc->mii_softc = device_get_softc(sc->miibus); 17655d43fd68SRuslan Bukin 17665d43fd68SRuslan Bukin /* All ready to run, attach the ethernet interface. */ 17675d43fd68SRuslan Bukin ether_ifattach(ifp, macaddr); 17685d43fd68SRuslan Bukin sc->is_attached = true; 17695d43fd68SRuslan Bukin 17705d43fd68SRuslan Bukin return (0); 17715d43fd68SRuslan Bukin } 17725d43fd68SRuslan Bukin 177327b39e58SMitchell Horne static int 177427b39e58SMitchell Horne dwc_detach(device_t dev) 177527b39e58SMitchell Horne { 177627b39e58SMitchell Horne struct dwc_softc *sc; 177727b39e58SMitchell Horne 177827b39e58SMitchell Horne sc = device_get_softc(dev); 177927b39e58SMitchell Horne 178027b39e58SMitchell Horne /* 178127b39e58SMitchell Horne * Disable and tear down interrupts before anything else, so we don't 178227b39e58SMitchell Horne * race with the handler. 178327b39e58SMitchell Horne */ 178427b39e58SMitchell Horne WRITE4(sc, INTERRUPT_ENABLE, 0); 178527b39e58SMitchell Horne if (sc->intr_cookie != NULL) { 178627b39e58SMitchell Horne bus_teardown_intr(dev, sc->res[1], sc->intr_cookie); 178727b39e58SMitchell Horne } 178827b39e58SMitchell Horne 178927b39e58SMitchell Horne if (sc->is_attached) { 179027b39e58SMitchell Horne DWC_LOCK(sc); 179127b39e58SMitchell Horne sc->is_detaching = true; 179227b39e58SMitchell Horne dwc_stop_locked(sc); 179327b39e58SMitchell Horne DWC_UNLOCK(sc); 179427b39e58SMitchell Horne callout_drain(&sc->dwc_callout); 179527b39e58SMitchell Horne ether_ifdetach(sc->ifp); 179627b39e58SMitchell Horne } 179727b39e58SMitchell Horne 179827b39e58SMitchell Horne if (sc->miibus != NULL) { 179927b39e58SMitchell Horne device_delete_child(dev, sc->miibus); 180027b39e58SMitchell Horne sc->miibus = NULL; 180127b39e58SMitchell Horne } 180227b39e58SMitchell Horne bus_generic_detach(dev); 180327b39e58SMitchell Horne 180427b39e58SMitchell Horne /* Free DMA descriptors */ 180527b39e58SMitchell Horne free_dma(sc); 180627b39e58SMitchell Horne 180727b39e58SMitchell Horne if (sc->ifp != NULL) { 180827b39e58SMitchell Horne if_free(sc->ifp); 180927b39e58SMitchell Horne sc->ifp = NULL; 181027b39e58SMitchell Horne } 181127b39e58SMitchell Horne 181227b39e58SMitchell Horne bus_release_resources(dev, dwc_spec, sc->res); 181327b39e58SMitchell Horne 181427b39e58SMitchell Horne mtx_destroy(&sc->mtx); 181527b39e58SMitchell Horne return (0); 181627b39e58SMitchell Horne } 181727b39e58SMitchell Horne 18185d43fd68SRuslan Bukin static device_method_t dwc_methods[] = { 18195d43fd68SRuslan Bukin DEVMETHOD(device_probe, dwc_probe), 18205d43fd68SRuslan Bukin DEVMETHOD(device_attach, dwc_attach), 182127b39e58SMitchell Horne DEVMETHOD(device_detach, dwc_detach), 18225d43fd68SRuslan Bukin 18235d43fd68SRuslan Bukin /* MII Interface */ 18245d43fd68SRuslan Bukin DEVMETHOD(miibus_readreg, dwc_miibus_read_reg), 18255d43fd68SRuslan Bukin DEVMETHOD(miibus_writereg, dwc_miibus_write_reg), 18265d43fd68SRuslan Bukin DEVMETHOD(miibus_statchg, dwc_miibus_statchg), 18275d43fd68SRuslan Bukin 18285d43fd68SRuslan Bukin { 0, 0 } 18295d43fd68SRuslan Bukin }; 18305d43fd68SRuslan Bukin 18315df53927SLuiz Otavio O Souza driver_t dwc_driver = { 18325d43fd68SRuslan Bukin "dwc", 18335d43fd68SRuslan Bukin dwc_methods, 18345d43fd68SRuslan Bukin sizeof(struct dwc_softc), 18355d43fd68SRuslan Bukin }; 18365d43fd68SRuslan Bukin 1837e55d0536SJohn Baldwin DRIVER_MODULE(dwc, simplebus, dwc_driver, 0, 0); 18383e38757dSJohn Baldwin DRIVER_MODULE(miibus, dwc, miibus_driver, 0, 0); 18395d43fd68SRuslan Bukin 18405d43fd68SRuslan Bukin MODULE_DEPEND(dwc, ether, 1, 1, 1); 18415d43fd68SRuslan Bukin MODULE_DEPEND(dwc, miibus, 1, 1, 1); 1842