15d43fd68SRuslan Bukin /*- 25d43fd68SRuslan Bukin * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com> 35d43fd68SRuslan Bukin * All rights reserved. 45d43fd68SRuslan Bukin * 55d43fd68SRuslan Bukin * This software was developed by SRI International and the University of 65d43fd68SRuslan Bukin * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 75d43fd68SRuslan Bukin * ("CTSRD"), as part of the DARPA CRASH research programme. 85d43fd68SRuslan Bukin * 95d43fd68SRuslan Bukin * Redistribution and use in source and binary forms, with or without 105d43fd68SRuslan Bukin * modification, are permitted provided that the following conditions 115d43fd68SRuslan Bukin * are met: 125d43fd68SRuslan Bukin * 1. Redistributions of source code must retain the above copyright 135d43fd68SRuslan Bukin * notice, this list of conditions and the following disclaimer. 145d43fd68SRuslan Bukin * 2. Redistributions in binary form must reproduce the above copyright 155d43fd68SRuslan Bukin * notice, this list of conditions and the following disclaimer in the 165d43fd68SRuslan Bukin * documentation and/or other materials provided with the distribution. 175d43fd68SRuslan Bukin * 185d43fd68SRuslan Bukin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 195d43fd68SRuslan Bukin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 205d43fd68SRuslan Bukin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 215d43fd68SRuslan Bukin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 225d43fd68SRuslan Bukin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 235d43fd68SRuslan Bukin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 245d43fd68SRuslan Bukin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 255d43fd68SRuslan Bukin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 265d43fd68SRuslan Bukin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 275d43fd68SRuslan Bukin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 285d43fd68SRuslan Bukin * SUCH DAMAGE. 295d43fd68SRuslan Bukin */ 305d43fd68SRuslan Bukin 315d43fd68SRuslan Bukin /* 325d43fd68SRuslan Bukin * Ethernet media access controller (EMAC) 335d43fd68SRuslan Bukin * Chapter 17, Altera Cyclone V Device Handbook (CV-5V2 2014.07.22) 345d43fd68SRuslan Bukin * 355d43fd68SRuslan Bukin * EMAC is an instance of the Synopsys DesignWare 3504-0 365d43fd68SRuslan Bukin * Universal 10/100/1000 Ethernet MAC (DWC_gmac). 375d43fd68SRuslan Bukin */ 385d43fd68SRuslan Bukin 395d43fd68SRuslan Bukin #include <sys/cdefs.h> 405d43fd68SRuslan Bukin __FBSDID("$FreeBSD$"); 415d43fd68SRuslan Bukin 425d43fd68SRuslan Bukin #include <sys/param.h> 435d43fd68SRuslan Bukin #include <sys/systm.h> 445d43fd68SRuslan Bukin #include <sys/bus.h> 45d7acb49aSJared McNeill #include <sys/gpio.h> 465d43fd68SRuslan Bukin #include <sys/kernel.h> 475d43fd68SRuslan Bukin #include <sys/lock.h> 48da9a326bSLuiz Otavio O Souza #include <sys/malloc.h> 495d43fd68SRuslan Bukin #include <sys/mbuf.h> 50da9a326bSLuiz Otavio O Souza #include <sys/module.h> 515d43fd68SRuslan Bukin #include <sys/mutex.h> 52da9a326bSLuiz Otavio O Souza #include <sys/rman.h> 535d43fd68SRuslan Bukin #include <sys/socket.h> 545d43fd68SRuslan Bukin #include <sys/sockio.h> 555d43fd68SRuslan Bukin 565d43fd68SRuslan Bukin #include <net/bpf.h> 575d43fd68SRuslan Bukin #include <net/if.h> 585d43fd68SRuslan Bukin #include <net/ethernet.h> 595d43fd68SRuslan Bukin #include <net/if_dl.h> 605d43fd68SRuslan Bukin #include <net/if_media.h> 615d43fd68SRuslan Bukin #include <net/if_types.h> 625d43fd68SRuslan Bukin #include <net/if_var.h> 635d43fd68SRuslan Bukin 645d43fd68SRuslan Bukin #include <machine/bus.h> 655d43fd68SRuslan Bukin 66da9a326bSLuiz Otavio O Souza #include <dev/dwc/if_dwc.h> 675df53927SLuiz Otavio O Souza #include <dev/dwc/if_dwcvar.h> 685d43fd68SRuslan Bukin #include <dev/mii/mii.h> 695d43fd68SRuslan Bukin #include <dev/mii/miivar.h> 70da9a326bSLuiz Otavio O Souza #include <dev/ofw/ofw_bus.h> 71da9a326bSLuiz Otavio O Souza #include <dev/ofw/ofw_bus_subr.h> 72f77d8d10SEmmanuel Vadot #include <dev/mii/mii_fdt.h> 73da9a326bSLuiz Otavio O Souza 746a05f063SJared McNeill #include <dev/extres/clk/clk.h> 756a05f063SJared McNeill #include <dev/extres/hwreset/hwreset.h> 766a05f063SJared McNeill 775df53927SLuiz Otavio O Souza #include "if_dwc_if.h" 78d7acb49aSJared McNeill #include "gpio_if.h" 795d43fd68SRuslan Bukin #include "miibus_if.h" 805d43fd68SRuslan Bukin 815d43fd68SRuslan Bukin #define READ4(_sc, _reg) \ 825d43fd68SRuslan Bukin bus_read_4((_sc)->res[0], _reg) 835d43fd68SRuslan Bukin #define WRITE4(_sc, _reg, _val) \ 845d43fd68SRuslan Bukin bus_write_4((_sc)->res[0], _reg, _val) 855d43fd68SRuslan Bukin 86d8e5258dSRuslan Bukin #define MAC_RESET_TIMEOUT 100 875d43fd68SRuslan Bukin #define WATCHDOG_TIMEOUT_SECS 5 885d43fd68SRuslan Bukin #define STATS_HARVEST_INTERVAL 2 895d43fd68SRuslan Bukin 905d43fd68SRuslan Bukin #define DWC_LOCK(sc) mtx_lock(&(sc)->mtx) 915d43fd68SRuslan Bukin #define DWC_UNLOCK(sc) mtx_unlock(&(sc)->mtx) 92ff0752c8SLuiz Otavio O Souza #define DWC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED) 93ff0752c8SLuiz Otavio O Souza #define DWC_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED) 945d43fd68SRuslan Bukin 958d43a868SMichal Meloun /* TX descriptors - TDESC0 is almost unified */ 968d43a868SMichal Meloun #define TDESC0_OWN (1U << 31) 978d43a868SMichal Meloun #define TDESC0_IHE (1U << 16) /* IP Header Error */ 988d43a868SMichal Meloun #define TDESC0_ES (1U << 15) /* Error Summary */ 998d43a868SMichal Meloun #define TDESC0_JT (1U << 14) /* Jabber Timeout */ 1008d43a868SMichal Meloun #define TDESC0_FF (1U << 13) /* Frame Flushed */ 1018d43a868SMichal Meloun #define TDESC0_PCE (1U << 12) /* Payload Checksum Error */ 1028d43a868SMichal Meloun #define TDESC0_LOC (1U << 11) /* Loss of Carrier */ 1038d43a868SMichal Meloun #define TDESC0_NC (1U << 10) /* No Carrier */ 1048d43a868SMichal Meloun #define TDESC0_LC (1U << 9) /* Late Collision */ 1058d43a868SMichal Meloun #define TDESC0_EC (1U << 8) /* Excessive Collision */ 1068d43a868SMichal Meloun #define TDESC0_VF (1U << 7) /* VLAN Frame */ 1078d43a868SMichal Meloun #define TDESC0_CC_MASK 0xf 1088d43a868SMichal Meloun #define TDESC0_CC_SHIFT 3 /* Collision Count */ 1098d43a868SMichal Meloun #define TDESC0_ED (1U << 2) /* Excessive Deferral */ 1108d43a868SMichal Meloun #define TDESC0_UF (1U << 1) /* Underflow Error */ 1118d43a868SMichal Meloun #define TDESC0_DB (1U << 0) /* Deferred Bit */ 1128d43a868SMichal Meloun /* TX descriptors - TDESC0 extended format only */ 1138d43a868SMichal Meloun #define ETDESC0_IC (1U << 30) /* Interrupt on Completion */ 1148d43a868SMichal Meloun #define ETDESC0_LS (1U << 29) /* Last Segment */ 1158d43a868SMichal Meloun #define ETDESC0_FS (1U << 28) /* First Segment */ 1168d43a868SMichal Meloun #define ETDESC0_DC (1U << 27) /* Disable CRC */ 1178d43a868SMichal Meloun #define ETDESC0_DP (1U << 26) /* Disable Padding */ 1188d43a868SMichal Meloun #define ETDESC0_CIC_NONE (0U << 22) /* Checksum Insertion Control */ 1198d43a868SMichal Meloun #define ETDESC0_CIC_HDR (1U << 22) 1208d43a868SMichal Meloun #define ETDESC0_CIC_SEG (2U << 22) 1218d43a868SMichal Meloun #define ETDESC0_CIC_FULL (3U << 22) 1228d43a868SMichal Meloun #define ETDESC0_TER (1U << 21) /* Transmit End of Ring */ 1238d43a868SMichal Meloun #define ETDESC0_TCH (1U << 20) /* Second Address Chained */ 1245d43fd68SRuslan Bukin 1258d43a868SMichal Meloun /* TX descriptors - TDESC1 normal format */ 1268d43a868SMichal Meloun #define NTDESC1_IC (1U << 31) /* Interrupt on Completion */ 1278d43a868SMichal Meloun #define NTDESC1_LS (1U << 30) /* Last Segment */ 1288d43a868SMichal Meloun #define NTDESC1_FS (1U << 29) /* First Segment */ 1298d43a868SMichal Meloun #define NTDESC1_CIC_NONE (0U << 27) /* Checksum Insertion Control */ 1308d43a868SMichal Meloun #define NTDESC1_CIC_HDR (1U << 27) 1318d43a868SMichal Meloun #define NTDESC1_CIC_SEG (2U << 27) 1328d43a868SMichal Meloun #define NTDESC1_CIC_FULL (3U << 27) 1338d43a868SMichal Meloun #define NTDESC1_DC (1U << 26) /* Disable CRC */ 1348d43a868SMichal Meloun #define NTDESC1_TER (1U << 25) /* Transmit End of Ring */ 1358d43a868SMichal Meloun #define NTDESC1_TCH (1U << 24) /* Second Address Chained */ 1368d43a868SMichal Meloun /* TX descriptors - TDESC1 extended format */ 1378d43a868SMichal Meloun #define ETDESC1_DP (1U << 23) /* Disable Padding */ 1388d43a868SMichal Meloun #define ETDESC1_TBS2_MASK 0x7ff 1398d43a868SMichal Meloun #define ETDESC1_TBS2_SHIFT 11 /* Receive Buffer 2 Size */ 1408d43a868SMichal Meloun #define ETDESC1_TBS1_MASK 0x7ff 1418d43a868SMichal Meloun #define ETDESC1_TBS1_SHIFT 0 /* Receive Buffer 1 Size */ 1425d43fd68SRuslan Bukin 1438d43a868SMichal Meloun /* RX descriptor - RDESC0 is unified */ 1448d43a868SMichal Meloun #define RDESC0_OWN (1U << 31) 1458d43a868SMichal Meloun #define RDESC0_AFM (1U << 30) /* Dest. Address Filter Fail */ 1468d43a868SMichal Meloun #define RDESC0_FL_MASK 0x3fff 1478d43a868SMichal Meloun #define RDESC0_FL_SHIFT 16 /* Frame Length */ 1488d43a868SMichal Meloun #define RDESC0_ES (1U << 15) /* Error Summary */ 1498d43a868SMichal Meloun #define RDESC0_DE (1U << 14) /* Descriptor Error */ 1508d43a868SMichal Meloun #define RDESC0_SAF (1U << 13) /* Source Address Filter Fail */ 1518d43a868SMichal Meloun #define RDESC0_LE (1U << 12) /* Length Error */ 1528d43a868SMichal Meloun #define RDESC0_OE (1U << 11) /* Overflow Error */ 1538d43a868SMichal Meloun #define RDESC0_VLAN (1U << 10) /* VLAN Tag */ 1548d43a868SMichal Meloun #define RDESC0_FS (1U << 9) /* First Descriptor */ 1558d43a868SMichal Meloun #define RDESC0_LS (1U << 8) /* Last Descriptor */ 1568d43a868SMichal Meloun #define RDESC0_ICE (1U << 7) /* IPC Checksum Error */ 1578d43a868SMichal Meloun #define RDESC0_LC (1U << 6) /* Late Collision */ 1588d43a868SMichal Meloun #define RDESC0_FT (1U << 5) /* Frame Type */ 1598d43a868SMichal Meloun #define RDESC0_RWT (1U << 4) /* Receive Watchdog Timeout */ 1608d43a868SMichal Meloun #define RDESC0_RE (1U << 3) /* Receive Error */ 1618d43a868SMichal Meloun #define RDESC0_DBE (1U << 2) /* Dribble Bit Error */ 1628d43a868SMichal Meloun #define RDESC0_CE (1U << 1) /* CRC Error */ 1638d43a868SMichal Meloun #define RDESC0_PCE (1U << 0) /* Payload Checksum Error */ 1648d43a868SMichal Meloun #define RDESC0_RXMA (1U << 0) /* Rx MAC Address */ 1655df53927SLuiz Otavio O Souza 1668d43a868SMichal Meloun /* RX descriptors - RDESC1 normal format */ 1678d43a868SMichal Meloun #define NRDESC1_DIC (1U << 31) /* Disable Intr on Completion */ 1688d43a868SMichal Meloun #define NRDESC1_RER (1U << 25) /* Receive End of Ring */ 1698d43a868SMichal Meloun #define NRDESC1_RCH (1U << 24) /* Second Address Chained */ 1708d43a868SMichal Meloun #define NRDESC1_RBS2_MASK 0x7ff 1718d43a868SMichal Meloun #define NRDESC1_RBS2_SHIFT 11 /* Receive Buffer 2 Size */ 1728d43a868SMichal Meloun #define NRDESC1_RBS1_MASK 0x7ff 1738d43a868SMichal Meloun #define NRDESC1_RBS1_SHIFT 0 /* Receive Buffer 1 Size */ 1748d43a868SMichal Meloun 1758d43a868SMichal Meloun /* RX descriptors - RDESC1 enhanced format */ 1768d43a868SMichal Meloun #define ERDESC1_DIC (1U << 31) /* Disable Intr on Completion */ 1778d43a868SMichal Meloun #define ERDESC1_RBS2_MASK 0x7ffff 1788d43a868SMichal Meloun #define ERDESC1_RBS2_SHIFT 16 /* Receive Buffer 2 Size */ 1798d43a868SMichal Meloun #define ERDESC1_RER (1U << 15) /* Receive End of Ring */ 1808d43a868SMichal Meloun #define ERDESC1_RCH (1U << 14) /* Second Address Chained */ 1818d43a868SMichal Meloun #define ERDESC1_RBS1_MASK 0x7ffff 1828d43a868SMichal Meloun #define ERDESC1_RBS1_SHIFT 0 /* Receive Buffer 1 Size */ 1835d43fd68SRuslan Bukin 1845d43fd68SRuslan Bukin /* 1855d43fd68SRuslan Bukin * A hardware buffer descriptor. Rx and Tx buffers have the same descriptor 1865df53927SLuiz Otavio O Souza * layout, but the bits in the fields have different meanings. 1875d43fd68SRuslan Bukin */ 1885d43fd68SRuslan Bukin struct dwc_hwdesc 1895d43fd68SRuslan Bukin { 1908d43a868SMichal Meloun uint32_t desc0; 1918d43a868SMichal Meloun uint32_t desc1; 1928d43a868SMichal Meloun uint32_t addr1; /* ptr to first buffer data */ 1938d43a868SMichal Meloun uint32_t addr2; /* ptr to next descriptor / second buffer data*/ 1945d43fd68SRuslan Bukin }; 1955d43fd68SRuslan Bukin 1962a35d391SEmmanuel Vadot 1972a35d391SEmmanuel Vadot struct dwc_hash_maddr_ctx { 1982a35d391SEmmanuel Vadot struct dwc_softc *sc; 1992a35d391SEmmanuel Vadot uint32_t hash[8]; 2002a35d391SEmmanuel Vadot }; 2012a35d391SEmmanuel Vadot 2025d43fd68SRuslan Bukin /* 2035d43fd68SRuslan Bukin * The hardware imposes alignment restrictions on various objects involved in 2045d43fd68SRuslan Bukin * DMA transfers. These values are expressed in bytes (not bits). 2055d43fd68SRuslan Bukin */ 2065d43fd68SRuslan Bukin #define DWC_DESC_RING_ALIGN 2048 2075d43fd68SRuslan Bukin 2085d43fd68SRuslan Bukin static struct resource_spec dwc_spec[] = { 2095d43fd68SRuslan Bukin { SYS_RES_MEMORY, 0, RF_ACTIVE }, 2105d43fd68SRuslan Bukin { SYS_RES_IRQ, 0, RF_ACTIVE }, 2115d43fd68SRuslan Bukin { -1, 0 } 2125d43fd68SRuslan Bukin }; 2135d43fd68SRuslan Bukin 2145d43fd68SRuslan Bukin static void dwc_txfinish_locked(struct dwc_softc *sc); 2155d43fd68SRuslan Bukin static void dwc_rxfinish_locked(struct dwc_softc *sc); 2165d43fd68SRuslan Bukin static void dwc_stop_locked(struct dwc_softc *sc); 2175d43fd68SRuslan Bukin static void dwc_setup_rxfilter(struct dwc_softc *sc); 218158ce7baSEmmanuel Vadot static void dwc_setup_core(struct dwc_softc *sc); 219f368f4b1SEmmanuel Vadot static void dwc_enable_mac(struct dwc_softc *sc, bool enable); 220158ce7baSEmmanuel Vadot static void dwc_init_dma(struct dwc_softc *sc); 221afd0c3c2SEmmanuel Vadot static void dwc_stop_dma(struct dwc_softc *sc); 2225d43fd68SRuslan Bukin 2232a35d391SEmmanuel Vadot static void dwc_tick(void *arg); 2242a35d391SEmmanuel Vadot 2252b4a66edSEmmanuel Vadot /* Pause time field in the transmitted control frame */ 2262b4a66edSEmmanuel Vadot static int dwc_pause_time = 0xffff; 2272b4a66edSEmmanuel Vadot TUNABLE_INT("hw.dwc.pause_time", &dwc_pause_time); 2282b4a66edSEmmanuel Vadot 2292a35d391SEmmanuel Vadot /* 2302a35d391SEmmanuel Vadot * MIIBUS functions 2312a35d391SEmmanuel Vadot */ 2322a35d391SEmmanuel Vadot 2332a35d391SEmmanuel Vadot static int 2342a35d391SEmmanuel Vadot dwc_miibus_read_reg(device_t dev, int phy, int reg) 2352a35d391SEmmanuel Vadot { 2362a35d391SEmmanuel Vadot struct dwc_softc *sc; 2372a35d391SEmmanuel Vadot uint16_t mii; 2382a35d391SEmmanuel Vadot size_t cnt; 2392a35d391SEmmanuel Vadot int rv = 0; 2402a35d391SEmmanuel Vadot 2412a35d391SEmmanuel Vadot sc = device_get_softc(dev); 2422a35d391SEmmanuel Vadot 2432a35d391SEmmanuel Vadot mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) 2442a35d391SEmmanuel Vadot | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) 2452a35d391SEmmanuel Vadot | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) 2462a35d391SEmmanuel Vadot | GMII_ADDRESS_GB; /* Busy flag */ 2472a35d391SEmmanuel Vadot 2482a35d391SEmmanuel Vadot WRITE4(sc, GMII_ADDRESS, mii); 2492a35d391SEmmanuel Vadot 2502a35d391SEmmanuel Vadot for (cnt = 0; cnt < 1000; cnt++) { 2512a35d391SEmmanuel Vadot if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { 2522a35d391SEmmanuel Vadot rv = READ4(sc, GMII_DATA); 2532a35d391SEmmanuel Vadot break; 2542a35d391SEmmanuel Vadot } 2552a35d391SEmmanuel Vadot DELAY(10); 2562a35d391SEmmanuel Vadot } 2572a35d391SEmmanuel Vadot 2582a35d391SEmmanuel Vadot return rv; 2592a35d391SEmmanuel Vadot } 2602a35d391SEmmanuel Vadot 2612a35d391SEmmanuel Vadot static int 2622a35d391SEmmanuel Vadot dwc_miibus_write_reg(device_t dev, int phy, int reg, int val) 2632a35d391SEmmanuel Vadot { 2642a35d391SEmmanuel Vadot struct dwc_softc *sc; 2652a35d391SEmmanuel Vadot uint16_t mii; 2662a35d391SEmmanuel Vadot size_t cnt; 2672a35d391SEmmanuel Vadot 2682a35d391SEmmanuel Vadot sc = device_get_softc(dev); 2692a35d391SEmmanuel Vadot 2702a35d391SEmmanuel Vadot mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) 2712a35d391SEmmanuel Vadot | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) 2722a35d391SEmmanuel Vadot | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) 2732a35d391SEmmanuel Vadot | GMII_ADDRESS_GB | GMII_ADDRESS_GW; 2742a35d391SEmmanuel Vadot 2752a35d391SEmmanuel Vadot WRITE4(sc, GMII_DATA, val); 2762a35d391SEmmanuel Vadot WRITE4(sc, GMII_ADDRESS, mii); 2772a35d391SEmmanuel Vadot 2782a35d391SEmmanuel Vadot for (cnt = 0; cnt < 1000; cnt++) { 2792a35d391SEmmanuel Vadot if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { 2802a35d391SEmmanuel Vadot break; 2812a35d391SEmmanuel Vadot } 2822a35d391SEmmanuel Vadot DELAY(10); 2832a35d391SEmmanuel Vadot } 2842a35d391SEmmanuel Vadot 2852a35d391SEmmanuel Vadot return (0); 2862a35d391SEmmanuel Vadot } 2872a35d391SEmmanuel Vadot 2882a35d391SEmmanuel Vadot static void 2892a35d391SEmmanuel Vadot dwc_miibus_statchg(device_t dev) 2902a35d391SEmmanuel Vadot { 2912a35d391SEmmanuel Vadot struct dwc_softc *sc; 2922a35d391SEmmanuel Vadot struct mii_data *mii; 2932a35d391SEmmanuel Vadot uint32_t reg; 2942a35d391SEmmanuel Vadot 2952a35d391SEmmanuel Vadot /* 2962a35d391SEmmanuel Vadot * Called by the MII bus driver when the PHY establishes 2972a35d391SEmmanuel Vadot * link to set the MAC interface registers. 2982a35d391SEmmanuel Vadot */ 2992a35d391SEmmanuel Vadot 3002a35d391SEmmanuel Vadot sc = device_get_softc(dev); 3012a35d391SEmmanuel Vadot 3022a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 3032a35d391SEmmanuel Vadot 3042a35d391SEmmanuel Vadot mii = sc->mii_softc; 3052a35d391SEmmanuel Vadot 3062a35d391SEmmanuel Vadot if (mii->mii_media_status & IFM_ACTIVE) 3072a35d391SEmmanuel Vadot sc->link_is_up = true; 3082a35d391SEmmanuel Vadot else 3092a35d391SEmmanuel Vadot sc->link_is_up = false; 3102a35d391SEmmanuel Vadot 3112a35d391SEmmanuel Vadot reg = READ4(sc, MAC_CONFIGURATION); 3122a35d391SEmmanuel Vadot switch (IFM_SUBTYPE(mii->mii_media_active)) { 3132a35d391SEmmanuel Vadot case IFM_1000_T: 3142a35d391SEmmanuel Vadot case IFM_1000_SX: 3152a35d391SEmmanuel Vadot reg &= ~(CONF_FES | CONF_PS); 3162a35d391SEmmanuel Vadot break; 3172a35d391SEmmanuel Vadot case IFM_100_TX: 3182a35d391SEmmanuel Vadot reg |= (CONF_FES | CONF_PS); 3192a35d391SEmmanuel Vadot break; 3202a35d391SEmmanuel Vadot case IFM_10_T: 3212a35d391SEmmanuel Vadot reg &= ~(CONF_FES); 3222a35d391SEmmanuel Vadot reg |= (CONF_PS); 3232a35d391SEmmanuel Vadot break; 3242a35d391SEmmanuel Vadot case IFM_NONE: 3252a35d391SEmmanuel Vadot sc->link_is_up = false; 3262a35d391SEmmanuel Vadot return; 3272a35d391SEmmanuel Vadot default: 3282a35d391SEmmanuel Vadot sc->link_is_up = false; 3292a35d391SEmmanuel Vadot device_printf(dev, "Unsupported media %u\n", 3302a35d391SEmmanuel Vadot IFM_SUBTYPE(mii->mii_media_active)); 3312a35d391SEmmanuel Vadot return; 3322a35d391SEmmanuel Vadot } 3332a35d391SEmmanuel Vadot if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 3342a35d391SEmmanuel Vadot reg |= (CONF_DM); 3352a35d391SEmmanuel Vadot else 3362a35d391SEmmanuel Vadot reg &= ~(CONF_DM); 3372a35d391SEmmanuel Vadot WRITE4(sc, MAC_CONFIGURATION, reg); 3382a35d391SEmmanuel Vadot 3392b4a66edSEmmanuel Vadot reg = FLOW_CONTROL_UP; 3402b4a66edSEmmanuel Vadot if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) 3412b4a66edSEmmanuel Vadot reg |= FLOW_CONTROL_TX; 3422b4a66edSEmmanuel Vadot if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) 3432b4a66edSEmmanuel Vadot reg |= FLOW_CONTROL_RX; 3442b4a66edSEmmanuel Vadot if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 3452b4a66edSEmmanuel Vadot reg |= dwc_pause_time << FLOW_CONTROL_PT_SHIFT; 3462b4a66edSEmmanuel Vadot WRITE4(sc, FLOW_CONTROL, reg); 3472b4a66edSEmmanuel Vadot 3482a35d391SEmmanuel Vadot IF_DWC_SET_SPEED(dev, IFM_SUBTYPE(mii->mii_media_active)); 3492a35d391SEmmanuel Vadot 3502a35d391SEmmanuel Vadot } 3512a35d391SEmmanuel Vadot 3522a35d391SEmmanuel Vadot /* 3532a35d391SEmmanuel Vadot * Media functions 3542a35d391SEmmanuel Vadot */ 3552a35d391SEmmanuel Vadot 3562a35d391SEmmanuel Vadot static void 3572a35d391SEmmanuel Vadot dwc_media_status(struct ifnet * ifp, struct ifmediareq *ifmr) 3582a35d391SEmmanuel Vadot { 3592a35d391SEmmanuel Vadot struct dwc_softc *sc; 3602a35d391SEmmanuel Vadot struct mii_data *mii; 3612a35d391SEmmanuel Vadot 3622a35d391SEmmanuel Vadot sc = ifp->if_softc; 3632a35d391SEmmanuel Vadot mii = sc->mii_softc; 3642a35d391SEmmanuel Vadot DWC_LOCK(sc); 3652a35d391SEmmanuel Vadot mii_pollstat(mii); 3662a35d391SEmmanuel Vadot ifmr->ifm_active = mii->mii_media_active; 3672a35d391SEmmanuel Vadot ifmr->ifm_status = mii->mii_media_status; 3682a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 3692a35d391SEmmanuel Vadot } 3702a35d391SEmmanuel Vadot 3712a35d391SEmmanuel Vadot static int 3722a35d391SEmmanuel Vadot dwc_media_change_locked(struct dwc_softc *sc) 3732a35d391SEmmanuel Vadot { 3742a35d391SEmmanuel Vadot 3752a35d391SEmmanuel Vadot return (mii_mediachg(sc->mii_softc)); 3762a35d391SEmmanuel Vadot } 3772a35d391SEmmanuel Vadot 3782a35d391SEmmanuel Vadot static int 3792a35d391SEmmanuel Vadot dwc_media_change(struct ifnet * ifp) 3802a35d391SEmmanuel Vadot { 3812a35d391SEmmanuel Vadot struct dwc_softc *sc; 3822a35d391SEmmanuel Vadot int error; 3832a35d391SEmmanuel Vadot 3842a35d391SEmmanuel Vadot sc = ifp->if_softc; 3852a35d391SEmmanuel Vadot 3862a35d391SEmmanuel Vadot DWC_LOCK(sc); 3872a35d391SEmmanuel Vadot error = dwc_media_change_locked(sc); 3882a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 3892a35d391SEmmanuel Vadot return (error); 3902a35d391SEmmanuel Vadot } 3912a35d391SEmmanuel Vadot 3922a35d391SEmmanuel Vadot /* 3932a35d391SEmmanuel Vadot * Core functions 3942a35d391SEmmanuel Vadot */ 3952a35d391SEmmanuel Vadot 3962a35d391SEmmanuel Vadot static const uint8_t nibbletab[] = { 3972a35d391SEmmanuel Vadot /* 0x0 0000 -> 0000 */ 0x0, 3982a35d391SEmmanuel Vadot /* 0x1 0001 -> 1000 */ 0x8, 3992a35d391SEmmanuel Vadot /* 0x2 0010 -> 0100 */ 0x4, 4002a35d391SEmmanuel Vadot /* 0x3 0011 -> 1100 */ 0xc, 4012a35d391SEmmanuel Vadot /* 0x4 0100 -> 0010 */ 0x2, 4022a35d391SEmmanuel Vadot /* 0x5 0101 -> 1010 */ 0xa, 4032a35d391SEmmanuel Vadot /* 0x6 0110 -> 0110 */ 0x6, 4042a35d391SEmmanuel Vadot /* 0x7 0111 -> 1110 */ 0xe, 4052a35d391SEmmanuel Vadot /* 0x8 1000 -> 0001 */ 0x1, 4062a35d391SEmmanuel Vadot /* 0x9 1001 -> 1001 */ 0x9, 4072a35d391SEmmanuel Vadot /* 0xa 1010 -> 0101 */ 0x5, 4082a35d391SEmmanuel Vadot /* 0xb 1011 -> 1101 */ 0xd, 4092a35d391SEmmanuel Vadot /* 0xc 1100 -> 0011 */ 0x3, 4102a35d391SEmmanuel Vadot /* 0xd 1101 -> 1011 */ 0xb, 4112a35d391SEmmanuel Vadot /* 0xe 1110 -> 0111 */ 0x7, 4122a35d391SEmmanuel Vadot /* 0xf 1111 -> 1111 */ 0xf, }; 4132a35d391SEmmanuel Vadot 4142a35d391SEmmanuel Vadot static uint8_t 4152a35d391SEmmanuel Vadot bitreverse(uint8_t x) 4162a35d391SEmmanuel Vadot { 4172a35d391SEmmanuel Vadot 4182a35d391SEmmanuel Vadot return (nibbletab[x & 0xf] << 4) | nibbletab[x >> 4]; 4192a35d391SEmmanuel Vadot } 4202a35d391SEmmanuel Vadot 4212a35d391SEmmanuel Vadot static u_int 4222a35d391SEmmanuel Vadot dwc_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) 4232a35d391SEmmanuel Vadot { 4242a35d391SEmmanuel Vadot struct dwc_hash_maddr_ctx *ctx = arg; 4252a35d391SEmmanuel Vadot uint32_t crc, hashbit, hashreg; 4262a35d391SEmmanuel Vadot uint8_t val; 4272a35d391SEmmanuel Vadot 4282a35d391SEmmanuel Vadot crc = ether_crc32_le(LLADDR(sdl), ETHER_ADDR_LEN); 4292a35d391SEmmanuel Vadot /* Take lower 8 bits and reverse it */ 4302a35d391SEmmanuel Vadot val = bitreverse(~crc & 0xff); 4312a35d391SEmmanuel Vadot if (ctx->sc->mactype != DWC_GMAC_EXT_DESC) 4322a35d391SEmmanuel Vadot val >>= 2; /* Only need lower 6 bits */ 4332a35d391SEmmanuel Vadot hashreg = (val >> 5); 4342a35d391SEmmanuel Vadot hashbit = (val & 31); 4352a35d391SEmmanuel Vadot ctx->hash[hashreg] |= (1 << hashbit); 4362a35d391SEmmanuel Vadot 4372a35d391SEmmanuel Vadot return (1); 4382a35d391SEmmanuel Vadot } 4392a35d391SEmmanuel Vadot 4402a35d391SEmmanuel Vadot static void 4412a35d391SEmmanuel Vadot dwc_setup_rxfilter(struct dwc_softc *sc) 4422a35d391SEmmanuel Vadot { 4432a35d391SEmmanuel Vadot struct dwc_hash_maddr_ctx ctx; 4442a35d391SEmmanuel Vadot struct ifnet *ifp; 4452a35d391SEmmanuel Vadot uint8_t *eaddr; 4462a35d391SEmmanuel Vadot uint32_t ffval, hi, lo; 4472a35d391SEmmanuel Vadot int nhash, i; 4482a35d391SEmmanuel Vadot 4492a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 4502a35d391SEmmanuel Vadot 4512a35d391SEmmanuel Vadot ifp = sc->ifp; 4522a35d391SEmmanuel Vadot nhash = sc->mactype != DWC_GMAC_EXT_DESC ? 2 : 8; 4532a35d391SEmmanuel Vadot 4542a35d391SEmmanuel Vadot /* 4552a35d391SEmmanuel Vadot * Set the multicast (group) filter hash. 4562a35d391SEmmanuel Vadot */ 4572a35d391SEmmanuel Vadot if ((ifp->if_flags & IFF_ALLMULTI) != 0) { 4582a35d391SEmmanuel Vadot ffval = (FRAME_FILTER_PM); 4592a35d391SEmmanuel Vadot for (i = 0; i < nhash; i++) 4602a35d391SEmmanuel Vadot ctx.hash[i] = ~0; 4612a35d391SEmmanuel Vadot } else { 4622a35d391SEmmanuel Vadot ffval = (FRAME_FILTER_HMC); 4632a35d391SEmmanuel Vadot for (i = 0; i < nhash; i++) 4642a35d391SEmmanuel Vadot ctx.hash[i] = 0; 4652a35d391SEmmanuel Vadot ctx.sc = sc; 4662a35d391SEmmanuel Vadot if_foreach_llmaddr(ifp, dwc_hash_maddr, &ctx); 4672a35d391SEmmanuel Vadot } 4682a35d391SEmmanuel Vadot 4692a35d391SEmmanuel Vadot /* 4702a35d391SEmmanuel Vadot * Set the individual address filter hash. 4712a35d391SEmmanuel Vadot */ 4722a35d391SEmmanuel Vadot if (ifp->if_flags & IFF_PROMISC) 4732a35d391SEmmanuel Vadot ffval |= (FRAME_FILTER_PR); 4742a35d391SEmmanuel Vadot 4752a35d391SEmmanuel Vadot /* 4762a35d391SEmmanuel Vadot * Set the primary address. 4772a35d391SEmmanuel Vadot */ 4782a35d391SEmmanuel Vadot eaddr = IF_LLADDR(ifp); 4792a35d391SEmmanuel Vadot lo = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) | 4802a35d391SEmmanuel Vadot (eaddr[3] << 24); 4812a35d391SEmmanuel Vadot hi = eaddr[4] | (eaddr[5] << 8); 4822a35d391SEmmanuel Vadot WRITE4(sc, MAC_ADDRESS_LOW(0), lo); 4832a35d391SEmmanuel Vadot WRITE4(sc, MAC_ADDRESS_HIGH(0), hi); 4842a35d391SEmmanuel Vadot WRITE4(sc, MAC_FRAME_FILTER, ffval); 4852a35d391SEmmanuel Vadot if (sc->mactype != DWC_GMAC_EXT_DESC) { 4862a35d391SEmmanuel Vadot WRITE4(sc, GMAC_MAC_HTLOW, ctx.hash[0]); 4872a35d391SEmmanuel Vadot WRITE4(sc, GMAC_MAC_HTHIGH, ctx.hash[1]); 4882a35d391SEmmanuel Vadot } else { 4892a35d391SEmmanuel Vadot for (i = 0; i < nhash; i++) 4902a35d391SEmmanuel Vadot WRITE4(sc, HASH_TABLE_REG(i), ctx.hash[i]); 4912a35d391SEmmanuel Vadot } 4922a35d391SEmmanuel Vadot } 4932a35d391SEmmanuel Vadot 4942a35d391SEmmanuel Vadot static void 4952a35d391SEmmanuel Vadot dwc_setup_core(struct dwc_softc *sc) 4962a35d391SEmmanuel Vadot { 4972a35d391SEmmanuel Vadot uint32_t reg; 4982a35d391SEmmanuel Vadot 4992a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 5002a35d391SEmmanuel Vadot 5012a35d391SEmmanuel Vadot /* Enable core */ 5022a35d391SEmmanuel Vadot reg = READ4(sc, MAC_CONFIGURATION); 5032a35d391SEmmanuel Vadot reg |= (CONF_JD | CONF_ACS | CONF_BE); 5042a35d391SEmmanuel Vadot WRITE4(sc, MAC_CONFIGURATION, reg); 5052a35d391SEmmanuel Vadot } 5062a35d391SEmmanuel Vadot 5072a35d391SEmmanuel Vadot static void 5082a35d391SEmmanuel Vadot dwc_enable_mac(struct dwc_softc *sc, bool enable) 5092a35d391SEmmanuel Vadot { 5102a35d391SEmmanuel Vadot uint32_t reg; 5112a35d391SEmmanuel Vadot 5122a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 5132a35d391SEmmanuel Vadot reg = READ4(sc, MAC_CONFIGURATION); 5142a35d391SEmmanuel Vadot if (enable) 5152a35d391SEmmanuel Vadot reg |= CONF_TE | CONF_RE; 5162a35d391SEmmanuel Vadot else 5172a35d391SEmmanuel Vadot reg &= ~(CONF_TE | CONF_RE); 5182a35d391SEmmanuel Vadot WRITE4(sc, MAC_CONFIGURATION, reg); 5192a35d391SEmmanuel Vadot } 5202a35d391SEmmanuel Vadot 5212a35d391SEmmanuel Vadot static void 5222a35d391SEmmanuel Vadot dwc_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr) 5232a35d391SEmmanuel Vadot { 5242a35d391SEmmanuel Vadot uint32_t hi, lo, rnd; 5252a35d391SEmmanuel Vadot 5262a35d391SEmmanuel Vadot /* 5272a35d391SEmmanuel Vadot * Try to recover a MAC address from the running hardware. If there's 5282a35d391SEmmanuel Vadot * something non-zero there, assume the bootloader did the right thing 5292a35d391SEmmanuel Vadot * and just use it. 5302a35d391SEmmanuel Vadot * 5312a35d391SEmmanuel Vadot * Otherwise, set the address to a convenient locally assigned address, 5322a35d391SEmmanuel Vadot * 'bsd' + random 24 low-order bits. 'b' is 0x62, which has the locally 5332a35d391SEmmanuel Vadot * assigned bit set, and the broadcast/multicast bit clear. 5342a35d391SEmmanuel Vadot */ 5352a35d391SEmmanuel Vadot lo = READ4(sc, MAC_ADDRESS_LOW(0)); 5362a35d391SEmmanuel Vadot hi = READ4(sc, MAC_ADDRESS_HIGH(0)) & 0xffff; 5372a35d391SEmmanuel Vadot if ((lo != 0xffffffff) || (hi != 0xffff)) { 5382a35d391SEmmanuel Vadot hwaddr[0] = (lo >> 0) & 0xff; 5392a35d391SEmmanuel Vadot hwaddr[1] = (lo >> 8) & 0xff; 5402a35d391SEmmanuel Vadot hwaddr[2] = (lo >> 16) & 0xff; 5412a35d391SEmmanuel Vadot hwaddr[3] = (lo >> 24) & 0xff; 5422a35d391SEmmanuel Vadot hwaddr[4] = (hi >> 0) & 0xff; 5432a35d391SEmmanuel Vadot hwaddr[5] = (hi >> 8) & 0xff; 5442a35d391SEmmanuel Vadot } else { 5452a35d391SEmmanuel Vadot rnd = arc4random() & 0x00ffffff; 5462a35d391SEmmanuel Vadot hwaddr[0] = 'b'; 5472a35d391SEmmanuel Vadot hwaddr[1] = 's'; 5482a35d391SEmmanuel Vadot hwaddr[2] = 'd'; 5492a35d391SEmmanuel Vadot hwaddr[3] = rnd >> 16; 5502a35d391SEmmanuel Vadot hwaddr[4] = rnd >> 8; 5512a35d391SEmmanuel Vadot hwaddr[5] = rnd >> 0; 5522a35d391SEmmanuel Vadot } 5532a35d391SEmmanuel Vadot } 5542a35d391SEmmanuel Vadot 5552a35d391SEmmanuel Vadot /* 5562a35d391SEmmanuel Vadot * DMA functions 5572a35d391SEmmanuel Vadot */ 5582a35d391SEmmanuel Vadot 5592a35d391SEmmanuel Vadot static void 5602a35d391SEmmanuel Vadot dwc_init_dma(struct dwc_softc *sc) 5612a35d391SEmmanuel Vadot { 5622a35d391SEmmanuel Vadot uint32_t reg; 5632a35d391SEmmanuel Vadot 5642a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 5652a35d391SEmmanuel Vadot 5662a35d391SEmmanuel Vadot /* Initializa DMA and enable transmitters */ 5672a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 5682a35d391SEmmanuel Vadot reg |= (MODE_TSF | MODE_OSF | MODE_FUF); 5692a35d391SEmmanuel Vadot reg &= ~(MODE_RSF); 5702a35d391SEmmanuel Vadot reg |= (MODE_RTC_LEV32 << MODE_RTC_SHIFT); 5712a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 5722a35d391SEmmanuel Vadot 5732a35d391SEmmanuel Vadot WRITE4(sc, INTERRUPT_ENABLE, INT_EN_DEFAULT); 5742a35d391SEmmanuel Vadot 5752a35d391SEmmanuel Vadot /* Start DMA */ 5762a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 5772a35d391SEmmanuel Vadot reg |= (MODE_ST | MODE_SR); 5782a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 5792a35d391SEmmanuel Vadot } 5802a35d391SEmmanuel Vadot 5812a35d391SEmmanuel Vadot static void 5822a35d391SEmmanuel Vadot dwc_stop_dma(struct dwc_softc *sc) 5832a35d391SEmmanuel Vadot { 5842a35d391SEmmanuel Vadot uint32_t reg; 5852a35d391SEmmanuel Vadot 5862a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 5872a35d391SEmmanuel Vadot 5882a35d391SEmmanuel Vadot /* Stop DMA TX */ 5892a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 5902a35d391SEmmanuel Vadot reg &= ~(MODE_ST); 5912a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 5922a35d391SEmmanuel Vadot 5932a35d391SEmmanuel Vadot /* Flush TX */ 5942a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 5952a35d391SEmmanuel Vadot reg |= (MODE_FTF); 5962a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 5972a35d391SEmmanuel Vadot 5982a35d391SEmmanuel Vadot /* Stop DMA RX */ 5992a35d391SEmmanuel Vadot reg = READ4(sc, OPERATION_MODE); 6002a35d391SEmmanuel Vadot reg &= ~(MODE_SR); 6012a35d391SEmmanuel Vadot WRITE4(sc, OPERATION_MODE, reg); 6022a35d391SEmmanuel Vadot } 6032a35d391SEmmanuel Vadot 6045d43fd68SRuslan Bukin static inline uint32_t 6055d43fd68SRuslan Bukin next_rxidx(struct dwc_softc *sc, uint32_t curidx) 6065d43fd68SRuslan Bukin { 6075d43fd68SRuslan Bukin 6085d43fd68SRuslan Bukin return ((curidx + 1) % RX_DESC_COUNT); 6095d43fd68SRuslan Bukin } 6105d43fd68SRuslan Bukin 6115d43fd68SRuslan Bukin static inline uint32_t 6125d43fd68SRuslan Bukin next_txidx(struct dwc_softc *sc, uint32_t curidx) 6135d43fd68SRuslan Bukin { 6145d43fd68SRuslan Bukin 6155d43fd68SRuslan Bukin return ((curidx + 1) % TX_DESC_COUNT); 6165d43fd68SRuslan Bukin } 6175d43fd68SRuslan Bukin 6185d43fd68SRuslan Bukin static void 6195d43fd68SRuslan Bukin dwc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 6205d43fd68SRuslan Bukin { 6215d43fd68SRuslan Bukin 6225d43fd68SRuslan Bukin if (error != 0) 6235d43fd68SRuslan Bukin return; 6245d43fd68SRuslan Bukin *(bus_addr_t *)arg = segs[0].ds_addr; 6255d43fd68SRuslan Bukin } 6265d43fd68SRuslan Bukin 627b72e2878SMichal Meloun inline static void 6285d43fd68SRuslan Bukin dwc_setup_txdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr, 629e5232621SOleksandr Tymoshenko uint32_t len, uint32_t flags, bool first, bool last) 6305d43fd68SRuslan Bukin { 631b72e2878SMichal Meloun uint32_t desc0, desc1; 6325d43fd68SRuslan Bukin 6335d43fd68SRuslan Bukin /* Addr/len 0 means we're clearing the descriptor after xmit done. */ 6345d43fd68SRuslan Bukin if (paddr == 0 || len == 0) { 635b72e2878SMichal Meloun desc0 = 0; 636b72e2878SMichal Meloun desc1 = 0; 637e5232621SOleksandr Tymoshenko --sc->tx_desccount; 6385d43fd68SRuslan Bukin } else { 639b72e2878SMichal Meloun if (sc->mactype != DWC_GMAC_EXT_DESC) { 640b72e2878SMichal Meloun desc0 = 0; 641e5232621SOleksandr Tymoshenko desc1 = NTDESC1_TCH | len | flags; 642e5232621SOleksandr Tymoshenko if (first) 643e5232621SOleksandr Tymoshenko desc1 |= NTDESC1_FS; 644e5232621SOleksandr Tymoshenko if (last) 645e5232621SOleksandr Tymoshenko desc1 |= NTDESC1_LS | NTDESC1_IC; 646b72e2878SMichal Meloun } else { 647e5232621SOleksandr Tymoshenko desc0 = ETDESC0_TCH | flags; 648e5232621SOleksandr Tymoshenko if (first) 649e5232621SOleksandr Tymoshenko desc0 |= ETDESC0_FS; 650e5232621SOleksandr Tymoshenko if (last) 651e5232621SOleksandr Tymoshenko desc0 |= ETDESC0_LS | ETDESC0_IC; 652b72e2878SMichal Meloun desc1 = len; 653b72e2878SMichal Meloun } 654e5232621SOleksandr Tymoshenko ++sc->tx_desccount; 6555d43fd68SRuslan Bukin } 6565d43fd68SRuslan Bukin 6578d43a868SMichal Meloun sc->txdesc_ring[idx].addr1 = (uint32_t)(paddr); 658b72e2878SMichal Meloun sc->txdesc_ring[idx].desc0 = desc0; 659b72e2878SMichal Meloun sc->txdesc_ring[idx].desc1 = desc1; 660e5232621SOleksandr Tymoshenko } 6615d43fd68SRuslan Bukin 662e5232621SOleksandr Tymoshenko inline static void 663e5232621SOleksandr Tymoshenko dwc_set_owner(struct dwc_softc *sc, int idx) 664e5232621SOleksandr Tymoshenko { 6655d43fd68SRuslan Bukin wmb(); 6668d43a868SMichal Meloun sc->txdesc_ring[idx].desc0 |= TDESC0_OWN; 6675d43fd68SRuslan Bukin wmb(); 6685d43fd68SRuslan Bukin } 6695d43fd68SRuslan Bukin 6705d43fd68SRuslan Bukin static int 6715d43fd68SRuslan Bukin dwc_setup_txbuf(struct dwc_softc *sc, int idx, struct mbuf **mp) 6725d43fd68SRuslan Bukin { 673e5232621SOleksandr Tymoshenko struct bus_dma_segment segs[TX_MAP_MAX_SEGS]; 6745d43fd68SRuslan Bukin int error, nsegs; 6755d43fd68SRuslan Bukin struct mbuf * m; 67698ea5a7bSEmmanuel Vadot uint32_t flags = 0; 677e5232621SOleksandr Tymoshenko int i; 678e5232621SOleksandr Tymoshenko int first, last; 6795d43fd68SRuslan Bukin 680e5232621SOleksandr Tymoshenko error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, 681e5232621SOleksandr Tymoshenko *mp, segs, &nsegs, 0); 682e5232621SOleksandr Tymoshenko if (error == EFBIG) { 683e5232621SOleksandr Tymoshenko /* 684e5232621SOleksandr Tymoshenko * The map may be partially mapped from the first call. 685e5232621SOleksandr Tymoshenko * Make sure to reset it. 686e5232621SOleksandr Tymoshenko */ 687e5232621SOleksandr Tymoshenko bus_dmamap_unload(sc->txbuf_tag, sc->txbuf_map[idx].map); 6885d43fd68SRuslan Bukin if ((m = m_defrag(*mp, M_NOWAIT)) == NULL) 6895d43fd68SRuslan Bukin return (ENOMEM); 6905d43fd68SRuslan Bukin *mp = m; 6915d43fd68SRuslan Bukin error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, 692e5232621SOleksandr Tymoshenko *mp, segs, &nsegs, 0); 693e5232621SOleksandr Tymoshenko } 694e5232621SOleksandr Tymoshenko if (error != 0) 695e5232621SOleksandr Tymoshenko return (ENOMEM); 696e5232621SOleksandr Tymoshenko 697e5232621SOleksandr Tymoshenko if (sc->tx_desccount + nsegs > TX_DESC_COUNT) { 698e5232621SOleksandr Tymoshenko bus_dmamap_unload(sc->txbuf_tag, sc->txbuf_map[idx].map); 6995d43fd68SRuslan Bukin return (ENOMEM); 7005d43fd68SRuslan Bukin } 7015d43fd68SRuslan Bukin 702e5232621SOleksandr Tymoshenko m = *mp; 7035d43fd68SRuslan Bukin 70498ea5a7bSEmmanuel Vadot if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) { 70598ea5a7bSEmmanuel Vadot if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0) { 70698ea5a7bSEmmanuel Vadot if (sc->mactype != DWC_GMAC_EXT_DESC) 70798ea5a7bSEmmanuel Vadot flags = NTDESC1_CIC_FULL; 70898ea5a7bSEmmanuel Vadot else 70998ea5a7bSEmmanuel Vadot flags = ETDESC0_CIC_FULL; 71098ea5a7bSEmmanuel Vadot } else { 71198ea5a7bSEmmanuel Vadot if (sc->mactype != DWC_GMAC_EXT_DESC) 71298ea5a7bSEmmanuel Vadot flags = NTDESC1_CIC_HDR; 71398ea5a7bSEmmanuel Vadot else 71498ea5a7bSEmmanuel Vadot flags = ETDESC0_CIC_HDR; 71598ea5a7bSEmmanuel Vadot } 71698ea5a7bSEmmanuel Vadot } 71798ea5a7bSEmmanuel Vadot 718e5232621SOleksandr Tymoshenko bus_dmamap_sync(sc->txbuf_tag, sc->txbuf_map[idx].map, 719e5232621SOleksandr Tymoshenko BUS_DMASYNC_PREWRITE); 720e5232621SOleksandr Tymoshenko 721e5232621SOleksandr Tymoshenko sc->txbuf_map[idx].mbuf = m; 722e5232621SOleksandr Tymoshenko 723e5232621SOleksandr Tymoshenko first = sc->tx_desc_head; 724e5232621SOleksandr Tymoshenko for (i = 0; i < nsegs; i++) { 725e5232621SOleksandr Tymoshenko dwc_setup_txdesc(sc, sc->tx_desc_head, 726e5232621SOleksandr Tymoshenko segs[i].ds_addr, segs[i].ds_len, 727e5232621SOleksandr Tymoshenko (i == 0) ? flags : 0, /* only first desc needs flags */ 728e5232621SOleksandr Tymoshenko (i == 0), 729e5232621SOleksandr Tymoshenko (i == nsegs - 1)); 730e5232621SOleksandr Tymoshenko if (i > 0) 731e5232621SOleksandr Tymoshenko dwc_set_owner(sc, sc->tx_desc_head); 732e5232621SOleksandr Tymoshenko last = sc->tx_desc_head; 733e5232621SOleksandr Tymoshenko sc->tx_desc_head = next_txidx(sc, sc->tx_desc_head); 734e5232621SOleksandr Tymoshenko } 735e5232621SOleksandr Tymoshenko 736e5232621SOleksandr Tymoshenko sc->txbuf_map[idx].last_desc_idx = last; 737e5232621SOleksandr Tymoshenko 738e5232621SOleksandr Tymoshenko dwc_set_owner(sc, first); 7395d43fd68SRuslan Bukin 7405d43fd68SRuslan Bukin return (0); 7415d43fd68SRuslan Bukin } 7425d43fd68SRuslan Bukin 7435d43fd68SRuslan Bukin inline static uint32_t 7445d43fd68SRuslan Bukin dwc_setup_rxdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr) 7455d43fd68SRuslan Bukin { 7465d43fd68SRuslan Bukin uint32_t nidx; 7475d43fd68SRuslan Bukin 7488d43a868SMichal Meloun sc->rxdesc_ring[idx].addr1 = (uint32_t)paddr; 7495d43fd68SRuslan Bukin nidx = next_rxidx(sc, idx); 7508d43a868SMichal Meloun sc->rxdesc_ring[idx].addr2 = sc->rxdesc_ring_paddr + 7515d43fd68SRuslan Bukin (nidx * sizeof(struct dwc_hwdesc)); 752188aee74SMichal Meloun if (sc->mactype != DWC_GMAC_EXT_DESC) 753b72e2878SMichal Meloun sc->rxdesc_ring[idx].desc1 = NRDESC1_RCH | 754b72e2878SMichal Meloun MIN(MCLBYTES, NRDESC1_RBS1_MASK); 7555df53927SLuiz Otavio O Souza else 756b72e2878SMichal Meloun sc->rxdesc_ring[idx].desc1 = ERDESC1_RCH | 757b72e2878SMichal Meloun MIN(MCLBYTES, ERDESC1_RBS1_MASK); 7585d43fd68SRuslan Bukin 7595d43fd68SRuslan Bukin wmb(); 7608d43a868SMichal Meloun sc->rxdesc_ring[idx].desc0 = RDESC0_OWN; 7615d43fd68SRuslan Bukin wmb(); 7625d43fd68SRuslan Bukin return (nidx); 7635d43fd68SRuslan Bukin } 7645d43fd68SRuslan Bukin 7655d43fd68SRuslan Bukin static int 7665d43fd68SRuslan Bukin dwc_setup_rxbuf(struct dwc_softc *sc, int idx, struct mbuf *m) 7675d43fd68SRuslan Bukin { 7685d43fd68SRuslan Bukin struct bus_dma_segment seg; 7695d43fd68SRuslan Bukin int error, nsegs; 7705d43fd68SRuslan Bukin 7715d43fd68SRuslan Bukin m_adj(m, ETHER_ALIGN); 7725d43fd68SRuslan Bukin 7735d43fd68SRuslan Bukin error = bus_dmamap_load_mbuf_sg(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 7745d43fd68SRuslan Bukin m, &seg, &nsegs, 0); 7757ed7d979SMichal Meloun if (error != 0) 7765d43fd68SRuslan Bukin return (error); 7775d43fd68SRuslan Bukin 7785d43fd68SRuslan Bukin KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 7795d43fd68SRuslan Bukin 7805d43fd68SRuslan Bukin bus_dmamap_sync(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 7815d43fd68SRuslan Bukin BUS_DMASYNC_PREREAD); 7825d43fd68SRuslan Bukin 7835d43fd68SRuslan Bukin sc->rxbuf_map[idx].mbuf = m; 7845d43fd68SRuslan Bukin dwc_setup_rxdesc(sc, idx, seg.ds_addr); 7855d43fd68SRuslan Bukin 7865d43fd68SRuslan Bukin return (0); 7875d43fd68SRuslan Bukin } 7885d43fd68SRuslan Bukin 7895d43fd68SRuslan Bukin static struct mbuf * 7905d43fd68SRuslan Bukin dwc_alloc_mbufcl(struct dwc_softc *sc) 7915d43fd68SRuslan Bukin { 7925d43fd68SRuslan Bukin struct mbuf *m; 7935d43fd68SRuslan Bukin 7945d43fd68SRuslan Bukin m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 795db8a143aSRuslan Bukin if (m != NULL) 7965d43fd68SRuslan Bukin m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; 7975d43fd68SRuslan Bukin 7985d43fd68SRuslan Bukin return (m); 7995d43fd68SRuslan Bukin } 8005d43fd68SRuslan Bukin 801b72e2878SMichal Meloun static struct mbuf * 802b72e2878SMichal Meloun dwc_rxfinish_one(struct dwc_softc *sc, struct dwc_hwdesc *desc, 803b72e2878SMichal Meloun struct dwc_bufmap *map) 804b72e2878SMichal Meloun { 805b72e2878SMichal Meloun struct ifnet *ifp; 806b72e2878SMichal Meloun struct mbuf *m, *m0; 807b72e2878SMichal Meloun int len; 808b72e2878SMichal Meloun uint32_t rdesc0; 809b72e2878SMichal Meloun 810b72e2878SMichal Meloun m = map->mbuf; 811b72e2878SMichal Meloun ifp = sc->ifp; 812b72e2878SMichal Meloun rdesc0 = desc ->desc0; 813b72e2878SMichal Meloun /* Validate descriptor. */ 814b72e2878SMichal Meloun if (rdesc0 & RDESC0_ES) { 815b72e2878SMichal Meloun /* 816b72e2878SMichal Meloun * Errored packet. Statistic counters are updated 817b72e2878SMichal Meloun * globally, so do nothing 818b72e2878SMichal Meloun */ 819b72e2878SMichal Meloun return (NULL); 820b72e2878SMichal Meloun } 821b72e2878SMichal Meloun 822b72e2878SMichal Meloun if ((rdesc0 & (RDESC0_FS | RDESC0_LS)) != 823b72e2878SMichal Meloun (RDESC0_FS | RDESC0_LS)) { 824b72e2878SMichal Meloun /* 825b72e2878SMichal Meloun * Something very wrong happens. The whole packet should be 826b72e2878SMichal Meloun * recevied in one descriptr. Report problem. 827b72e2878SMichal Meloun */ 828b72e2878SMichal Meloun device_printf(sc->dev, 829b72e2878SMichal Meloun "%s: RX descriptor without FIRST and LAST bit set: 0x%08X", 830b72e2878SMichal Meloun __func__, rdesc0); 831b72e2878SMichal Meloun return (NULL); 832b72e2878SMichal Meloun } 833b72e2878SMichal Meloun 834b72e2878SMichal Meloun len = (rdesc0 >> RDESC0_FL_SHIFT) & RDESC0_FL_MASK; 835b72e2878SMichal Meloun if (len < 64) { 836b72e2878SMichal Meloun /* 837b72e2878SMichal Meloun * Lenght is invalid, recycle old mbuf 838b72e2878SMichal Meloun * Probably impossible case 839b72e2878SMichal Meloun */ 840b72e2878SMichal Meloun return (NULL); 841b72e2878SMichal Meloun } 842b72e2878SMichal Meloun 843b72e2878SMichal Meloun /* Allocate new buffer */ 844b72e2878SMichal Meloun m0 = dwc_alloc_mbufcl(sc); 845b72e2878SMichal Meloun if (m0 == NULL) { 846b72e2878SMichal Meloun /* no new mbuf available, recycle old */ 847b72e2878SMichal Meloun if_inc_counter(sc->ifp, IFCOUNTER_IQDROPS, 1); 848b72e2878SMichal Meloun return (NULL); 849b72e2878SMichal Meloun } 850b72e2878SMichal Meloun /* Do dmasync for newly received packet */ 851b72e2878SMichal Meloun bus_dmamap_sync(sc->rxbuf_tag, map->map, BUS_DMASYNC_POSTREAD); 852b72e2878SMichal Meloun bus_dmamap_unload(sc->rxbuf_tag, map->map); 853b72e2878SMichal Meloun 854b72e2878SMichal Meloun /* Received packet is valid, process it */ 855b72e2878SMichal Meloun m->m_pkthdr.rcvif = ifp; 856b72e2878SMichal Meloun m->m_pkthdr.len = len; 857b72e2878SMichal Meloun m->m_len = len; 858b72e2878SMichal Meloun if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 859b72e2878SMichal Meloun 86098ea5a7bSEmmanuel Vadot if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0 && 86198ea5a7bSEmmanuel Vadot (rdesc0 & RDESC0_FT) != 0) { 86298ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_flags = CSUM_IP_CHECKED; 86398ea5a7bSEmmanuel Vadot if ((rdesc0 & RDESC0_ICE) == 0) 86498ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_flags |= CSUM_IP_VALID; 86598ea5a7bSEmmanuel Vadot if ((rdesc0 & RDESC0_PCE) == 0) { 86698ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_flags |= 86798ea5a7bSEmmanuel Vadot CSUM_DATA_VALID | CSUM_PSEUDO_HDR; 86898ea5a7bSEmmanuel Vadot m->m_pkthdr.csum_data = 0xffff; 86998ea5a7bSEmmanuel Vadot } 87098ea5a7bSEmmanuel Vadot } 87198ea5a7bSEmmanuel Vadot 872b72e2878SMichal Meloun /* Remove trailing FCS */ 873b72e2878SMichal Meloun m_adj(m, -ETHER_CRC_LEN); 874b72e2878SMichal Meloun 875b72e2878SMichal Meloun DWC_UNLOCK(sc); 876b72e2878SMichal Meloun (*ifp->if_input)(ifp, m); 877b72e2878SMichal Meloun DWC_LOCK(sc); 878b72e2878SMichal Meloun return (m0); 879b72e2878SMichal Meloun } 880b72e2878SMichal Meloun 8815d43fd68SRuslan Bukin static int 8825d43fd68SRuslan Bukin setup_dma(struct dwc_softc *sc) 8835d43fd68SRuslan Bukin { 8845d43fd68SRuslan Bukin struct mbuf *m; 8855d43fd68SRuslan Bukin int error; 8865d43fd68SRuslan Bukin int nidx; 8875d43fd68SRuslan Bukin int idx; 8885d43fd68SRuslan Bukin 8895d43fd68SRuslan Bukin /* 8905d43fd68SRuslan Bukin * Set up TX descriptor ring, descriptors, and dma maps. 8915d43fd68SRuslan Bukin */ 8925d43fd68SRuslan Bukin error = bus_dma_tag_create( 8935d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 8945d43fd68SRuslan Bukin DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ 8955d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 8965d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 8975d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 8985d43fd68SRuslan Bukin TX_DESC_SIZE, 1, /* maxsize, nsegments */ 8995d43fd68SRuslan Bukin TX_DESC_SIZE, /* maxsegsize */ 9005d43fd68SRuslan Bukin 0, /* flags */ 9015d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9025d43fd68SRuslan Bukin &sc->txdesc_tag); 9035d43fd68SRuslan Bukin if (error != 0) { 9045d43fd68SRuslan Bukin device_printf(sc->dev, 9055d43fd68SRuslan Bukin "could not create TX ring DMA tag.\n"); 9065d43fd68SRuslan Bukin goto out; 9075d43fd68SRuslan Bukin } 9085d43fd68SRuslan Bukin 9095d43fd68SRuslan Bukin error = bus_dmamem_alloc(sc->txdesc_tag, (void**)&sc->txdesc_ring, 9105d43fd68SRuslan Bukin BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, 9115d43fd68SRuslan Bukin &sc->txdesc_map); 9125d43fd68SRuslan Bukin if (error != 0) { 9135d43fd68SRuslan Bukin device_printf(sc->dev, 9145d43fd68SRuslan Bukin "could not allocate TX descriptor ring.\n"); 9155d43fd68SRuslan Bukin goto out; 9165d43fd68SRuslan Bukin } 9175d43fd68SRuslan Bukin 9185d43fd68SRuslan Bukin error = bus_dmamap_load(sc->txdesc_tag, sc->txdesc_map, 9195d43fd68SRuslan Bukin sc->txdesc_ring, TX_DESC_SIZE, dwc_get1paddr, 9205d43fd68SRuslan Bukin &sc->txdesc_ring_paddr, 0); 9215d43fd68SRuslan Bukin if (error != 0) { 9225d43fd68SRuslan Bukin device_printf(sc->dev, 9235d43fd68SRuslan Bukin "could not load TX descriptor ring map.\n"); 9245d43fd68SRuslan Bukin goto out; 9255d43fd68SRuslan Bukin } 9265d43fd68SRuslan Bukin 9275d43fd68SRuslan Bukin for (idx = 0; idx < TX_DESC_COUNT; idx++) { 9285d43fd68SRuslan Bukin nidx = next_txidx(sc, idx); 9298d43a868SMichal Meloun sc->txdesc_ring[idx].addr2 = sc->txdesc_ring_paddr + 9305d43fd68SRuslan Bukin (nidx * sizeof(struct dwc_hwdesc)); 9315d43fd68SRuslan Bukin } 9325d43fd68SRuslan Bukin 9335d43fd68SRuslan Bukin error = bus_dma_tag_create( 9345d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 9355d43fd68SRuslan Bukin 1, 0, /* alignment, boundary */ 9365d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9375d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9385d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 939e5232621SOleksandr Tymoshenko MCLBYTES*TX_MAP_MAX_SEGS, /* maxsize */ 940e5232621SOleksandr Tymoshenko TX_MAP_MAX_SEGS, /* nsegments */ 9415d43fd68SRuslan Bukin MCLBYTES, /* maxsegsize */ 9425d43fd68SRuslan Bukin 0, /* flags */ 9435d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9445d43fd68SRuslan Bukin &sc->txbuf_tag); 9455d43fd68SRuslan Bukin if (error != 0) { 9465d43fd68SRuslan Bukin device_printf(sc->dev, 9475d43fd68SRuslan Bukin "could not create TX ring DMA tag.\n"); 9485d43fd68SRuslan Bukin goto out; 9495d43fd68SRuslan Bukin } 9505d43fd68SRuslan Bukin 951e5232621SOleksandr Tymoshenko for (idx = 0; idx < TX_MAP_COUNT; idx++) { 9525d43fd68SRuslan Bukin error = bus_dmamap_create(sc->txbuf_tag, BUS_DMA_COHERENT, 9535d43fd68SRuslan Bukin &sc->txbuf_map[idx].map); 9545d43fd68SRuslan Bukin if (error != 0) { 9555d43fd68SRuslan Bukin device_printf(sc->dev, 9565d43fd68SRuslan Bukin "could not create TX buffer DMA map.\n"); 9575d43fd68SRuslan Bukin goto out; 9585d43fd68SRuslan Bukin } 9595d43fd68SRuslan Bukin } 9605d43fd68SRuslan Bukin 961e5232621SOleksandr Tymoshenko for (idx = 0; idx < TX_DESC_COUNT; idx++) 962e5232621SOleksandr Tymoshenko dwc_setup_txdesc(sc, idx, 0, 0, 0, false, false); 963e5232621SOleksandr Tymoshenko 9645d43fd68SRuslan Bukin /* 9655d43fd68SRuslan Bukin * Set up RX descriptor ring, descriptors, dma maps, and mbufs. 9665d43fd68SRuslan Bukin */ 9675d43fd68SRuslan Bukin error = bus_dma_tag_create( 9685d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 9695d43fd68SRuslan Bukin DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ 9705d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9715d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9725d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 9735d43fd68SRuslan Bukin RX_DESC_SIZE, 1, /* maxsize, nsegments */ 9745d43fd68SRuslan Bukin RX_DESC_SIZE, /* maxsegsize */ 9755d43fd68SRuslan Bukin 0, /* flags */ 9765d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9775d43fd68SRuslan Bukin &sc->rxdesc_tag); 9785d43fd68SRuslan Bukin if (error != 0) { 9795d43fd68SRuslan Bukin device_printf(sc->dev, 9805d43fd68SRuslan Bukin "could not create RX ring DMA tag.\n"); 9815d43fd68SRuslan Bukin goto out; 9825d43fd68SRuslan Bukin } 9835d43fd68SRuslan Bukin 9845d43fd68SRuslan Bukin error = bus_dmamem_alloc(sc->rxdesc_tag, (void **)&sc->rxdesc_ring, 9855d43fd68SRuslan Bukin BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, 9865d43fd68SRuslan Bukin &sc->rxdesc_map); 9875d43fd68SRuslan Bukin if (error != 0) { 9885d43fd68SRuslan Bukin device_printf(sc->dev, 9895d43fd68SRuslan Bukin "could not allocate RX descriptor ring.\n"); 9905d43fd68SRuslan Bukin goto out; 9915d43fd68SRuslan Bukin } 9925d43fd68SRuslan Bukin 9935d43fd68SRuslan Bukin error = bus_dmamap_load(sc->rxdesc_tag, sc->rxdesc_map, 9945d43fd68SRuslan Bukin sc->rxdesc_ring, RX_DESC_SIZE, dwc_get1paddr, 9955d43fd68SRuslan Bukin &sc->rxdesc_ring_paddr, 0); 9965d43fd68SRuslan Bukin if (error != 0) { 9975d43fd68SRuslan Bukin device_printf(sc->dev, 9985d43fd68SRuslan Bukin "could not load RX descriptor ring map.\n"); 9995d43fd68SRuslan Bukin goto out; 10005d43fd68SRuslan Bukin } 10015d43fd68SRuslan Bukin 10025d43fd68SRuslan Bukin error = bus_dma_tag_create( 10035d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 10045d43fd68SRuslan Bukin 1, 0, /* alignment, boundary */ 10055d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 10065d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 10075d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 10085d43fd68SRuslan Bukin MCLBYTES, 1, /* maxsize, nsegments */ 10095d43fd68SRuslan Bukin MCLBYTES, /* maxsegsize */ 10105d43fd68SRuslan Bukin 0, /* flags */ 10115d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 10125d43fd68SRuslan Bukin &sc->rxbuf_tag); 10135d43fd68SRuslan Bukin if (error != 0) { 10145d43fd68SRuslan Bukin device_printf(sc->dev, 10155d43fd68SRuslan Bukin "could not create RX buf DMA tag.\n"); 10165d43fd68SRuslan Bukin goto out; 10175d43fd68SRuslan Bukin } 10185d43fd68SRuslan Bukin 10195d43fd68SRuslan Bukin for (idx = 0; idx < RX_DESC_COUNT; idx++) { 10205d43fd68SRuslan Bukin error = bus_dmamap_create(sc->rxbuf_tag, BUS_DMA_COHERENT, 10215d43fd68SRuslan Bukin &sc->rxbuf_map[idx].map); 10225d43fd68SRuslan Bukin if (error != 0) { 10235d43fd68SRuslan Bukin device_printf(sc->dev, 10245d43fd68SRuslan Bukin "could not create RX buffer DMA map.\n"); 10255d43fd68SRuslan Bukin goto out; 10265d43fd68SRuslan Bukin } 10275d43fd68SRuslan Bukin if ((m = dwc_alloc_mbufcl(sc)) == NULL) { 10285d43fd68SRuslan Bukin device_printf(sc->dev, "Could not alloc mbuf\n"); 10295d43fd68SRuslan Bukin error = ENOMEM; 10305d43fd68SRuslan Bukin goto out; 10315d43fd68SRuslan Bukin } 10325d43fd68SRuslan Bukin if ((error = dwc_setup_rxbuf(sc, idx, m)) != 0) { 10335d43fd68SRuslan Bukin device_printf(sc->dev, 10345d43fd68SRuslan Bukin "could not create new RX buffer.\n"); 10355d43fd68SRuslan Bukin goto out; 10365d43fd68SRuslan Bukin } 10375d43fd68SRuslan Bukin } 10385d43fd68SRuslan Bukin 10395d43fd68SRuslan Bukin out: 10405d43fd68SRuslan Bukin if (error != 0) 10415d43fd68SRuslan Bukin return (ENXIO); 10425d43fd68SRuslan Bukin 10435d43fd68SRuslan Bukin return (0); 10445d43fd68SRuslan Bukin } 10455d43fd68SRuslan Bukin 10462a35d391SEmmanuel Vadot /* 10472a35d391SEmmanuel Vadot * if_ functions 10482a35d391SEmmanuel Vadot */ 10492a35d391SEmmanuel Vadot 1050f88e0af6SEmmanuel Vadot static void 10512a35d391SEmmanuel Vadot dwc_txstart_locked(struct dwc_softc *sc) 10525d43fd68SRuslan Bukin { 10532a35d391SEmmanuel Vadot struct ifnet *ifp; 10542a35d391SEmmanuel Vadot struct mbuf *m; 10552a35d391SEmmanuel Vadot int enqueued; 10562a35d391SEmmanuel Vadot 10572a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 10582a35d391SEmmanuel Vadot 10592a35d391SEmmanuel Vadot if (!sc->link_is_up) 10602a35d391SEmmanuel Vadot return; 10612a35d391SEmmanuel Vadot 10622a35d391SEmmanuel Vadot ifp = sc->ifp; 10632a35d391SEmmanuel Vadot 10643bbd11eeSEmmanuel Vadot if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != 10653bbd11eeSEmmanuel Vadot IFF_DRV_RUNNING) 10662a35d391SEmmanuel Vadot return; 10672a35d391SEmmanuel Vadot 10682a35d391SEmmanuel Vadot enqueued = 0; 10692a35d391SEmmanuel Vadot 10702a35d391SEmmanuel Vadot for (;;) { 1071e5232621SOleksandr Tymoshenko if (sc->tx_desccount > (TX_DESC_COUNT - TX_MAP_MAX_SEGS + 1)) { 1072e5232621SOleksandr Tymoshenko if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 1073e5232621SOleksandr Tymoshenko break; 1074e5232621SOleksandr Tymoshenko } 1075e5232621SOleksandr Tymoshenko 1076e5232621SOleksandr Tymoshenko if (sc->tx_mapcount == (TX_MAP_COUNT - 1)) { 10773bbd11eeSEmmanuel Vadot if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 10782a35d391SEmmanuel Vadot break; 10792a35d391SEmmanuel Vadot } 10802a35d391SEmmanuel Vadot 10813bbd11eeSEmmanuel Vadot m = if_dequeue(ifp); 10822a35d391SEmmanuel Vadot if (m == NULL) 10832a35d391SEmmanuel Vadot break; 1084e5232621SOleksandr Tymoshenko if (dwc_setup_txbuf(sc, sc->tx_map_head, &m) != 0) { 10853bbd11eeSEmmanuel Vadot if_sendq_prepend(ifp, m); 1086e5232621SOleksandr Tymoshenko if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 10872a35d391SEmmanuel Vadot break; 10882a35d391SEmmanuel Vadot } 10893bbd11eeSEmmanuel Vadot if_bpfmtap(ifp, m); 1090e5232621SOleksandr Tymoshenko sc->tx_map_head = next_txidx(sc, sc->tx_map_head); 1091e5232621SOleksandr Tymoshenko sc->tx_mapcount++; 10922a35d391SEmmanuel Vadot ++enqueued; 10932a35d391SEmmanuel Vadot } 10942a35d391SEmmanuel Vadot 10952a35d391SEmmanuel Vadot if (enqueued != 0) { 10962a35d391SEmmanuel Vadot WRITE4(sc, TRANSMIT_POLL_DEMAND, 0x1); 10972a35d391SEmmanuel Vadot sc->tx_watchdog_count = WATCHDOG_TIMEOUT_SECS; 10982a35d391SEmmanuel Vadot } 10992a35d391SEmmanuel Vadot } 11002a35d391SEmmanuel Vadot 11012a35d391SEmmanuel Vadot static void 11022a35d391SEmmanuel Vadot dwc_txstart(struct ifnet *ifp) 11032a35d391SEmmanuel Vadot { 11042a35d391SEmmanuel Vadot struct dwc_softc *sc = ifp->if_softc; 11052a35d391SEmmanuel Vadot 11062a35d391SEmmanuel Vadot DWC_LOCK(sc); 11072a35d391SEmmanuel Vadot dwc_txstart_locked(sc); 11082a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 11092a35d391SEmmanuel Vadot } 11102a35d391SEmmanuel Vadot 11112a35d391SEmmanuel Vadot static void 11122a35d391SEmmanuel Vadot dwc_init_locked(struct dwc_softc *sc) 11132a35d391SEmmanuel Vadot { 11142a35d391SEmmanuel Vadot struct ifnet *ifp = sc->ifp; 11152a35d391SEmmanuel Vadot 11162a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 11172a35d391SEmmanuel Vadot 11183bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) 11192a35d391SEmmanuel Vadot return; 11202a35d391SEmmanuel Vadot 11212a35d391SEmmanuel Vadot dwc_setup_rxfilter(sc); 11222a35d391SEmmanuel Vadot dwc_setup_core(sc); 11232a35d391SEmmanuel Vadot dwc_enable_mac(sc, true); 11242a35d391SEmmanuel Vadot dwc_init_dma(sc); 11252a35d391SEmmanuel Vadot 11262a35d391SEmmanuel Vadot if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); 11275d43fd68SRuslan Bukin 11285d43fd68SRuslan Bukin /* 11292a35d391SEmmanuel Vadot * Call mii_mediachg() which will call back into dwc_miibus_statchg() 11302a35d391SEmmanuel Vadot * to set up the remaining config registers based on current media. 11315d43fd68SRuslan Bukin */ 11322a35d391SEmmanuel Vadot mii_mediachg(sc->mii_softc); 11332a35d391SEmmanuel Vadot callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); 11342a35d391SEmmanuel Vadot } 11352a35d391SEmmanuel Vadot 11362a35d391SEmmanuel Vadot static void 11372a35d391SEmmanuel Vadot dwc_init(void *if_softc) 11382a35d391SEmmanuel Vadot { 11392a35d391SEmmanuel Vadot struct dwc_softc *sc = if_softc; 11402a35d391SEmmanuel Vadot 11412a35d391SEmmanuel Vadot DWC_LOCK(sc); 11422a35d391SEmmanuel Vadot dwc_init_locked(sc); 11432a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 11442a35d391SEmmanuel Vadot } 11452a35d391SEmmanuel Vadot 11462a35d391SEmmanuel Vadot static void 11472a35d391SEmmanuel Vadot dwc_stop_locked(struct dwc_softc *sc) 11482a35d391SEmmanuel Vadot { 11492a35d391SEmmanuel Vadot struct ifnet *ifp; 11502a35d391SEmmanuel Vadot 11512a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 11522a35d391SEmmanuel Vadot 11532a35d391SEmmanuel Vadot ifp = sc->ifp; 11543bbd11eeSEmmanuel Vadot if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 11552a35d391SEmmanuel Vadot sc->tx_watchdog_count = 0; 11562a35d391SEmmanuel Vadot sc->stats_harvest_count = 0; 11572a35d391SEmmanuel Vadot 11582a35d391SEmmanuel Vadot callout_stop(&sc->dwc_callout); 11592a35d391SEmmanuel Vadot 11602a35d391SEmmanuel Vadot dwc_stop_dma(sc); 11612a35d391SEmmanuel Vadot dwc_enable_mac(sc, false); 11622a35d391SEmmanuel Vadot } 11632a35d391SEmmanuel Vadot 11642a35d391SEmmanuel Vadot static int 11652a35d391SEmmanuel Vadot dwc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 11662a35d391SEmmanuel Vadot { 11672a35d391SEmmanuel Vadot struct dwc_softc *sc; 11682a35d391SEmmanuel Vadot struct mii_data *mii; 11692a35d391SEmmanuel Vadot struct ifreq *ifr; 11703bbd11eeSEmmanuel Vadot int flags, mask, error; 11712a35d391SEmmanuel Vadot 11722a35d391SEmmanuel Vadot sc = ifp->if_softc; 11732a35d391SEmmanuel Vadot ifr = (struct ifreq *)data; 11742a35d391SEmmanuel Vadot 11752a35d391SEmmanuel Vadot error = 0; 11762a35d391SEmmanuel Vadot switch (cmd) { 11772a35d391SEmmanuel Vadot case SIOCSIFFLAGS: 11782a35d391SEmmanuel Vadot DWC_LOCK(sc); 11793bbd11eeSEmmanuel Vadot if (if_getflags(ifp) & IFF_UP) { 11803bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 11813bbd11eeSEmmanuel Vadot flags = if_getflags(ifp) ^ sc->if_flags; 11823bbd11eeSEmmanuel Vadot if ((flags & (IFF_PROMISC|IFF_ALLMULTI)) != 0) 11832a35d391SEmmanuel Vadot dwc_setup_rxfilter(sc); 11845d43fd68SRuslan Bukin } else { 11852a35d391SEmmanuel Vadot if (!sc->is_detaching) 11862a35d391SEmmanuel Vadot dwc_init_locked(sc); 11872a35d391SEmmanuel Vadot } 11882a35d391SEmmanuel Vadot } else { 11893bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) 11902a35d391SEmmanuel Vadot dwc_stop_locked(sc); 11912a35d391SEmmanuel Vadot } 11923bbd11eeSEmmanuel Vadot sc->if_flags = if_getflags(ifp); 11932a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 11942a35d391SEmmanuel Vadot break; 11952a35d391SEmmanuel Vadot case SIOCADDMULTI: 11962a35d391SEmmanuel Vadot case SIOCDELMULTI: 11973bbd11eeSEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 11982a35d391SEmmanuel Vadot DWC_LOCK(sc); 11992a35d391SEmmanuel Vadot dwc_setup_rxfilter(sc); 12002a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 12012a35d391SEmmanuel Vadot } 12022a35d391SEmmanuel Vadot break; 12032a35d391SEmmanuel Vadot case SIOCSIFMEDIA: 12042a35d391SEmmanuel Vadot case SIOCGIFMEDIA: 12052a35d391SEmmanuel Vadot mii = sc->mii_softc; 12062a35d391SEmmanuel Vadot error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); 12072a35d391SEmmanuel Vadot break; 12082a35d391SEmmanuel Vadot case SIOCSIFCAP: 12093bbd11eeSEmmanuel Vadot mask = ifr->ifr_reqcap ^ if_getcapenable(ifp); 12102a35d391SEmmanuel Vadot if (mask & IFCAP_VLAN_MTU) { 12112a35d391SEmmanuel Vadot /* No work to do except acknowledge the change took */ 12123bbd11eeSEmmanuel Vadot if_togglecapenable(ifp, IFCAP_VLAN_MTU); 12132a35d391SEmmanuel Vadot } 121498ea5a7bSEmmanuel Vadot if (mask & IFCAP_RXCSUM) 121598ea5a7bSEmmanuel Vadot if_togglecapenable(ifp, IFCAP_RXCSUM); 121698ea5a7bSEmmanuel Vadot if (mask & IFCAP_TXCSUM) 121798ea5a7bSEmmanuel Vadot if_togglecapenable(ifp, IFCAP_TXCSUM); 121898ea5a7bSEmmanuel Vadot if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0) 121998ea5a7bSEmmanuel Vadot if_sethwassistbits(ifp, CSUM_IP | CSUM_UDP | CSUM_TCP, 0); 122098ea5a7bSEmmanuel Vadot else 122198ea5a7bSEmmanuel Vadot if_sethwassistbits(ifp, 0, CSUM_IP | CSUM_UDP | CSUM_TCP); 12222a35d391SEmmanuel Vadot break; 12232a35d391SEmmanuel Vadot 12242a35d391SEmmanuel Vadot default: 12252a35d391SEmmanuel Vadot error = ether_ioctl(ifp, cmd, data); 12262a35d391SEmmanuel Vadot break; 12272a35d391SEmmanuel Vadot } 12282a35d391SEmmanuel Vadot 12292a35d391SEmmanuel Vadot return (error); 12302a35d391SEmmanuel Vadot } 12312a35d391SEmmanuel Vadot 12322a35d391SEmmanuel Vadot /* 12332a35d391SEmmanuel Vadot * Interrupts functions 12342a35d391SEmmanuel Vadot */ 12352a35d391SEmmanuel Vadot 12362a35d391SEmmanuel Vadot static void 12372a35d391SEmmanuel Vadot dwc_txfinish_locked(struct dwc_softc *sc) 12382a35d391SEmmanuel Vadot { 12392a35d391SEmmanuel Vadot struct dwc_bufmap *bmap; 12402a35d391SEmmanuel Vadot struct dwc_hwdesc *desc; 12412a35d391SEmmanuel Vadot struct ifnet *ifp; 1242e5232621SOleksandr Tymoshenko int idx, last_idx; 1243e5232621SOleksandr Tymoshenko bool map_finished; 12442a35d391SEmmanuel Vadot 12452a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 12462a35d391SEmmanuel Vadot 12472a35d391SEmmanuel Vadot ifp = sc->ifp; 1248e5232621SOleksandr Tymoshenko /* check if all descriptors of the map are done */ 1249e5232621SOleksandr Tymoshenko while (sc->tx_map_tail != sc->tx_map_head) { 1250e5232621SOleksandr Tymoshenko map_finished = true; 1251e5232621SOleksandr Tymoshenko bmap = &sc->txbuf_map[sc->tx_map_tail]; 1252e5232621SOleksandr Tymoshenko idx = sc->tx_desc_tail; 1253e5232621SOleksandr Tymoshenko last_idx = next_txidx(sc, bmap->last_desc_idx); 1254e5232621SOleksandr Tymoshenko while (idx != last_idx) { 1255e5232621SOleksandr Tymoshenko desc = &sc->txdesc_ring[idx]; 1256e5232621SOleksandr Tymoshenko if ((desc->desc0 & TDESC0_OWN) != 0) { 1257e5232621SOleksandr Tymoshenko map_finished = false; 12582a35d391SEmmanuel Vadot break; 1259e5232621SOleksandr Tymoshenko } 1260e5232621SOleksandr Tymoshenko idx = next_txidx(sc, idx); 1261e5232621SOleksandr Tymoshenko } 1262e5232621SOleksandr Tymoshenko 1263e5232621SOleksandr Tymoshenko if (!map_finished) 1264e5232621SOleksandr Tymoshenko break; 12652a35d391SEmmanuel Vadot bus_dmamap_sync(sc->txbuf_tag, bmap->map, 12662a35d391SEmmanuel Vadot BUS_DMASYNC_POSTWRITE); 12672a35d391SEmmanuel Vadot bus_dmamap_unload(sc->txbuf_tag, bmap->map); 12682a35d391SEmmanuel Vadot m_freem(bmap->mbuf); 12692a35d391SEmmanuel Vadot bmap->mbuf = NULL; 1270e5232621SOleksandr Tymoshenko sc->tx_mapcount--; 1271e5232621SOleksandr Tymoshenko while (sc->tx_desc_tail != last_idx) { 1272e5232621SOleksandr Tymoshenko dwc_setup_txdesc(sc, sc->tx_desc_tail, 0, 0, 0, false, false); 1273e5232621SOleksandr Tymoshenko sc->tx_desc_tail = next_txidx(sc, sc->tx_desc_tail); 1274e5232621SOleksandr Tymoshenko } 1275e5232621SOleksandr Tymoshenko sc->tx_map_tail = next_txidx(sc, sc->tx_map_tail); 12763bbd11eeSEmmanuel Vadot if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); 12772a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 12782a35d391SEmmanuel Vadot } 12792a35d391SEmmanuel Vadot 12802a35d391SEmmanuel Vadot /* If there are no buffers outstanding, muzzle the watchdog. */ 1281e5232621SOleksandr Tymoshenko if (sc->tx_desc_tail == sc->tx_desc_head) { 12822a35d391SEmmanuel Vadot sc->tx_watchdog_count = 0; 12835d43fd68SRuslan Bukin } 12845d43fd68SRuslan Bukin } 12855d43fd68SRuslan Bukin 12862a35d391SEmmanuel Vadot static void 12872a35d391SEmmanuel Vadot dwc_rxfinish_locked(struct dwc_softc *sc) 12882a35d391SEmmanuel Vadot { 12892a35d391SEmmanuel Vadot struct mbuf *m; 12902a35d391SEmmanuel Vadot int error, idx; 12912a35d391SEmmanuel Vadot struct dwc_hwdesc *desc; 12922a35d391SEmmanuel Vadot 12932a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 12942a35d391SEmmanuel Vadot for (;;) { 12952a35d391SEmmanuel Vadot idx = sc->rx_idx; 12962a35d391SEmmanuel Vadot desc = sc->rxdesc_ring + idx; 12972a35d391SEmmanuel Vadot if ((desc->desc0 & RDESC0_OWN) != 0) 12982a35d391SEmmanuel Vadot break; 12992a35d391SEmmanuel Vadot 13002a35d391SEmmanuel Vadot m = dwc_rxfinish_one(sc, desc, sc->rxbuf_map + idx); 13012a35d391SEmmanuel Vadot if (m == NULL) { 13022a35d391SEmmanuel Vadot wmb(); 13032a35d391SEmmanuel Vadot desc->desc0 = RDESC0_OWN; 13042a35d391SEmmanuel Vadot wmb(); 13052a35d391SEmmanuel Vadot } else { 13062a35d391SEmmanuel Vadot /* We cannot create hole in RX ring */ 13072a35d391SEmmanuel Vadot error = dwc_setup_rxbuf(sc, idx, m); 13082a35d391SEmmanuel Vadot if (error != 0) 13092a35d391SEmmanuel Vadot panic("dwc_setup_rxbuf failed: error %d\n", 13102a35d391SEmmanuel Vadot error); 131198ea5a7bSEmmanuel Vadot 13122a35d391SEmmanuel Vadot } 13132a35d391SEmmanuel Vadot sc->rx_idx = next_rxidx(sc, sc->rx_idx); 13142a35d391SEmmanuel Vadot } 13152a35d391SEmmanuel Vadot } 13162a35d391SEmmanuel Vadot 13172a35d391SEmmanuel Vadot static void 13182a35d391SEmmanuel Vadot dwc_intr(void *arg) 13192a35d391SEmmanuel Vadot { 13202a35d391SEmmanuel Vadot struct dwc_softc *sc; 13212a35d391SEmmanuel Vadot uint32_t reg; 13222a35d391SEmmanuel Vadot 13232a35d391SEmmanuel Vadot sc = arg; 13242a35d391SEmmanuel Vadot 13252a35d391SEmmanuel Vadot DWC_LOCK(sc); 13262a35d391SEmmanuel Vadot 13272a35d391SEmmanuel Vadot reg = READ4(sc, INTERRUPT_STATUS); 13282a35d391SEmmanuel Vadot if (reg) 13292a35d391SEmmanuel Vadot READ4(sc, SGMII_RGMII_SMII_CTRL_STATUS); 13302a35d391SEmmanuel Vadot 13312a35d391SEmmanuel Vadot reg = READ4(sc, DMA_STATUS); 13322a35d391SEmmanuel Vadot if (reg & DMA_STATUS_NIS) { 13332a35d391SEmmanuel Vadot if (reg & DMA_STATUS_RI) 13342a35d391SEmmanuel Vadot dwc_rxfinish_locked(sc); 13352a35d391SEmmanuel Vadot 13362a35d391SEmmanuel Vadot if (reg & DMA_STATUS_TI) { 13372a35d391SEmmanuel Vadot dwc_txfinish_locked(sc); 13382a35d391SEmmanuel Vadot dwc_txstart_locked(sc); 13392a35d391SEmmanuel Vadot } 13402a35d391SEmmanuel Vadot } 13412a35d391SEmmanuel Vadot 13422a35d391SEmmanuel Vadot if (reg & DMA_STATUS_AIS) { 13432a35d391SEmmanuel Vadot if (reg & DMA_STATUS_FBI) { 13442a35d391SEmmanuel Vadot /* Fatal bus error */ 13452a35d391SEmmanuel Vadot device_printf(sc->dev, 13462a35d391SEmmanuel Vadot "Ethernet DMA error, restarting controller.\n"); 13472a35d391SEmmanuel Vadot dwc_stop_locked(sc); 13482a35d391SEmmanuel Vadot dwc_init_locked(sc); 13492a35d391SEmmanuel Vadot } 13502a35d391SEmmanuel Vadot } 13512a35d391SEmmanuel Vadot 13522a35d391SEmmanuel Vadot WRITE4(sc, DMA_STATUS, reg & DMA_STATUS_INTR_MASK); 13532a35d391SEmmanuel Vadot DWC_UNLOCK(sc); 13542a35d391SEmmanuel Vadot } 13552a35d391SEmmanuel Vadot 13562a35d391SEmmanuel Vadot /* 13572a35d391SEmmanuel Vadot * Stats 13582a35d391SEmmanuel Vadot */ 13592a35d391SEmmanuel Vadot 13602a35d391SEmmanuel Vadot static void dwc_clear_stats(struct dwc_softc *sc) 13612a35d391SEmmanuel Vadot { 13622a35d391SEmmanuel Vadot uint32_t reg; 13632a35d391SEmmanuel Vadot 13642a35d391SEmmanuel Vadot reg = READ4(sc, MMC_CONTROL); 13652a35d391SEmmanuel Vadot reg |= (MMC_CONTROL_CNTRST); 13662a35d391SEmmanuel Vadot WRITE4(sc, MMC_CONTROL, reg); 13672a35d391SEmmanuel Vadot } 13682a35d391SEmmanuel Vadot 13692a35d391SEmmanuel Vadot static void 13702a35d391SEmmanuel Vadot dwc_harvest_stats(struct dwc_softc *sc) 13712a35d391SEmmanuel Vadot { 13722a35d391SEmmanuel Vadot struct ifnet *ifp; 13732a35d391SEmmanuel Vadot 13742a35d391SEmmanuel Vadot /* We don't need to harvest too often. */ 13752a35d391SEmmanuel Vadot if (++sc->stats_harvest_count < STATS_HARVEST_INTERVAL) 13762a35d391SEmmanuel Vadot return; 13772a35d391SEmmanuel Vadot 13782a35d391SEmmanuel Vadot sc->stats_harvest_count = 0; 13792a35d391SEmmanuel Vadot ifp = sc->ifp; 13802a35d391SEmmanuel Vadot 13812a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_IPACKETS, READ4(sc, RXFRAMECOUNT_GB)); 13822a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_IMCASTS, READ4(sc, RXMULTICASTFRAMES_G)); 13832a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_IERRORS, 13842a35d391SEmmanuel Vadot READ4(sc, RXOVERSIZE_G) + READ4(sc, RXUNDERSIZE_G) + 13852a35d391SEmmanuel Vadot READ4(sc, RXCRCERROR) + READ4(sc, RXALIGNMENTERROR) + 13862a35d391SEmmanuel Vadot READ4(sc, RXRUNTERROR) + READ4(sc, RXJABBERERROR) + 13872a35d391SEmmanuel Vadot READ4(sc, RXLENGTHERROR)); 13882a35d391SEmmanuel Vadot 13892a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OPACKETS, READ4(sc, TXFRAMECOUNT_G)); 13902a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OMCASTS, READ4(sc, TXMULTICASTFRAMES_G)); 13912a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OERRORS, 13922a35d391SEmmanuel Vadot READ4(sc, TXOVERSIZE_G) + READ4(sc, TXEXCESSDEF) + 13932a35d391SEmmanuel Vadot READ4(sc, TXCARRIERERR) + READ4(sc, TXUNDERFLOWERROR)); 13942a35d391SEmmanuel Vadot 13952a35d391SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 13962a35d391SEmmanuel Vadot READ4(sc, TXEXESSCOL) + READ4(sc, TXLATECOL)); 13972a35d391SEmmanuel Vadot 13982a35d391SEmmanuel Vadot dwc_clear_stats(sc); 13992a35d391SEmmanuel Vadot } 14002a35d391SEmmanuel Vadot 14012a35d391SEmmanuel Vadot static void 14022a35d391SEmmanuel Vadot dwc_tick(void *arg) 14032a35d391SEmmanuel Vadot { 14042a35d391SEmmanuel Vadot struct dwc_softc *sc; 14052a35d391SEmmanuel Vadot struct ifnet *ifp; 14062a35d391SEmmanuel Vadot int link_was_up; 14072a35d391SEmmanuel Vadot 14082a35d391SEmmanuel Vadot sc = arg; 14092a35d391SEmmanuel Vadot 14102a35d391SEmmanuel Vadot DWC_ASSERT_LOCKED(sc); 14112a35d391SEmmanuel Vadot 14122a35d391SEmmanuel Vadot ifp = sc->ifp; 14132a35d391SEmmanuel Vadot 14143bbd11eeSEmmanuel Vadot if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) 14152a35d391SEmmanuel Vadot return; 14162a35d391SEmmanuel Vadot 14172a35d391SEmmanuel Vadot /* 14182a35d391SEmmanuel Vadot * Typical tx watchdog. If this fires it indicates that we enqueued 14192a35d391SEmmanuel Vadot * packets for output and never got a txdone interrupt for them. Maybe 14202a35d391SEmmanuel Vadot * it's a missed interrupt somehow, just pretend we got one. 14212a35d391SEmmanuel Vadot */ 14222a35d391SEmmanuel Vadot if (sc->tx_watchdog_count > 0) { 14232a35d391SEmmanuel Vadot if (--sc->tx_watchdog_count == 0) { 14242a35d391SEmmanuel Vadot dwc_txfinish_locked(sc); 14252a35d391SEmmanuel Vadot } 14262a35d391SEmmanuel Vadot } 14272a35d391SEmmanuel Vadot 14282a35d391SEmmanuel Vadot /* Gather stats from hardware counters. */ 14292a35d391SEmmanuel Vadot dwc_harvest_stats(sc); 14302a35d391SEmmanuel Vadot 14312a35d391SEmmanuel Vadot /* Check the media status. */ 14322a35d391SEmmanuel Vadot link_was_up = sc->link_is_up; 14332a35d391SEmmanuel Vadot mii_tick(sc->mii_softc); 14342a35d391SEmmanuel Vadot if (sc->link_is_up && !link_was_up) 14352a35d391SEmmanuel Vadot dwc_txstart_locked(sc); 14362a35d391SEmmanuel Vadot 14372a35d391SEmmanuel Vadot /* Schedule another check one second from now. */ 14382a35d391SEmmanuel Vadot callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); 14392a35d391SEmmanuel Vadot } 14402a35d391SEmmanuel Vadot 14412a35d391SEmmanuel Vadot /* 14422a35d391SEmmanuel Vadot * Probe/Attach functions 14432a35d391SEmmanuel Vadot */ 14442a35d391SEmmanuel Vadot 1445d7acb49aSJared McNeill #define GPIO_ACTIVE_LOW 1 1446d7acb49aSJared McNeill 1447d7acb49aSJared McNeill static int 1448d7acb49aSJared McNeill dwc_reset(device_t dev) 1449d7acb49aSJared McNeill { 1450d7acb49aSJared McNeill pcell_t gpio_prop[4]; 1451d7acb49aSJared McNeill pcell_t delay_prop[3]; 1452d7acb49aSJared McNeill phandle_t node, gpio_node; 1453d7acb49aSJared McNeill device_t gpio; 1454d7acb49aSJared McNeill uint32_t pin, flags; 1455d7acb49aSJared McNeill uint32_t pin_value; 1456d7acb49aSJared McNeill 1457d7acb49aSJared McNeill node = ofw_bus_get_node(dev); 1458d7acb49aSJared McNeill if (OF_getencprop(node, "snps,reset-gpio", 1459d7acb49aSJared McNeill gpio_prop, sizeof(gpio_prop)) <= 0) 1460d7acb49aSJared McNeill return (0); 1461d7acb49aSJared McNeill 1462d7acb49aSJared McNeill if (OF_getencprop(node, "snps,reset-delays-us", 1463d7acb49aSJared McNeill delay_prop, sizeof(delay_prop)) <= 0) { 1464d7acb49aSJared McNeill device_printf(dev, 1465d7acb49aSJared McNeill "Wrong property for snps,reset-delays-us"); 1466d7acb49aSJared McNeill return (ENXIO); 1467d7acb49aSJared McNeill } 1468d7acb49aSJared McNeill 1469d7acb49aSJared McNeill gpio_node = OF_node_from_xref(gpio_prop[0]); 1470d7acb49aSJared McNeill if ((gpio = OF_device_from_xref(gpio_prop[0])) == NULL) { 1471d7acb49aSJared McNeill device_printf(dev, 1472d7acb49aSJared McNeill "Can't find gpio controller for phy reset\n"); 1473d7acb49aSJared McNeill return (ENXIO); 1474d7acb49aSJared McNeill } 1475d7acb49aSJared McNeill 1476d7acb49aSJared McNeill if (GPIO_MAP_GPIOS(gpio, node, gpio_node, 147773a1170aSPedro F. Giffuni nitems(gpio_prop) - 1, 1478d7acb49aSJared McNeill gpio_prop + 1, &pin, &flags) != 0) { 1479d7acb49aSJared McNeill device_printf(dev, "Can't map gpio for phy reset\n"); 1480d7acb49aSJared McNeill return (ENXIO); 1481d7acb49aSJared McNeill } 1482d7acb49aSJared McNeill 1483d7acb49aSJared McNeill pin_value = GPIO_PIN_LOW; 1484d7acb49aSJared McNeill if (OF_hasprop(node, "snps,reset-active-low")) 1485d7acb49aSJared McNeill pin_value = GPIO_PIN_HIGH; 1486d7acb49aSJared McNeill 1487d7acb49aSJared McNeill GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT); 1488d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 1489c069412eSEmmanuel Vadot DELAY(delay_prop[0] * 5); 1490d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, !pin_value); 1491c069412eSEmmanuel Vadot DELAY(delay_prop[1] * 5); 1492d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 1493c069412eSEmmanuel Vadot DELAY(delay_prop[2] * 5); 1494d7acb49aSJared McNeill 1495d7acb49aSJared McNeill return (0); 1496d7acb49aSJared McNeill } 1497d7acb49aSJared McNeill 14986a05f063SJared McNeill static int 14996a05f063SJared McNeill dwc_clock_init(device_t dev) 15006a05f063SJared McNeill { 15016a05f063SJared McNeill hwreset_t rst; 15026a05f063SJared McNeill clk_t clk; 15036a05f063SJared McNeill int error; 1504824cfb47SOleksandr Tymoshenko int64_t freq; 15056a05f063SJared McNeill 1506824cfb47SOleksandr Tymoshenko /* Enable clocks */ 1507dac93553SMichal Meloun if (clk_get_by_ofw_name(dev, 0, "stmmaceth", &clk) == 0) { 15086a05f063SJared McNeill error = clk_enable(clk); 15096a05f063SJared McNeill if (error != 0) { 15106a05f063SJared McNeill device_printf(dev, "could not enable main clock\n"); 15116a05f063SJared McNeill return (error); 15126a05f063SJared McNeill } 1513824cfb47SOleksandr Tymoshenko if (bootverbose) { 1514824cfb47SOleksandr Tymoshenko clk_get_freq(clk, &freq); 1515fc254a2eSLi-Wen Hsu device_printf(dev, "MAC clock(%s) freq: %jd\n", 1516fc254a2eSLi-Wen Hsu clk_get_name(clk), (intmax_t)freq); 1517824cfb47SOleksandr Tymoshenko } 1518824cfb47SOleksandr Tymoshenko } 1519824cfb47SOleksandr Tymoshenko else { 1520824cfb47SOleksandr Tymoshenko device_printf(dev, "could not find clock stmmaceth\n"); 15216a05f063SJared McNeill } 15226a05f063SJared McNeill 15236a05f063SJared McNeill /* De-assert reset */ 1524dac93553SMichal Meloun if (hwreset_get_by_ofw_name(dev, 0, "stmmaceth", &rst) == 0) { 15256a05f063SJared McNeill error = hwreset_deassert(rst); 15266a05f063SJared McNeill if (error != 0) { 15276a05f063SJared McNeill device_printf(dev, "could not de-assert reset\n"); 15286a05f063SJared McNeill return (error); 15296a05f063SJared McNeill } 15306a05f063SJared McNeill } 15316a05f063SJared McNeill 15326a05f063SJared McNeill return (0); 15336a05f063SJared McNeill } 15346a05f063SJared McNeill 15355d43fd68SRuslan Bukin static int 15365d43fd68SRuslan Bukin dwc_probe(device_t dev) 15375d43fd68SRuslan Bukin { 15385d43fd68SRuslan Bukin 15395d43fd68SRuslan Bukin if (!ofw_bus_status_okay(dev)) 15405d43fd68SRuslan Bukin return (ENXIO); 15415d43fd68SRuslan Bukin 15425d43fd68SRuslan Bukin if (!ofw_bus_is_compatible(dev, "snps,dwmac")) 15435d43fd68SRuslan Bukin return (ENXIO); 15445d43fd68SRuslan Bukin 15455d43fd68SRuslan Bukin device_set_desc(dev, "Gigabit Ethernet Controller"); 15465d43fd68SRuslan Bukin return (BUS_PROBE_DEFAULT); 15475d43fd68SRuslan Bukin } 15485d43fd68SRuslan Bukin 15495d43fd68SRuslan Bukin static int 15505d43fd68SRuslan Bukin dwc_attach(device_t dev) 15515d43fd68SRuslan Bukin { 15525d43fd68SRuslan Bukin uint8_t macaddr[ETHER_ADDR_LEN]; 15535d43fd68SRuslan Bukin struct dwc_softc *sc; 15545d43fd68SRuslan Bukin struct ifnet *ifp; 1555ff0752c8SLuiz Otavio O Souza int error, i; 1556ff0752c8SLuiz Otavio O Souza uint32_t reg; 1557824cfb47SOleksandr Tymoshenko phandle_t node; 15583e5cd548SEmmanuel Vadot uint32_t txpbl, rxpbl, pbl; 15595e38d9e4SEmmanuel Vadot bool nopblx8 = false; 15605e38d9e4SEmmanuel Vadot bool fixed_burst = false; 15615d43fd68SRuslan Bukin 15625d43fd68SRuslan Bukin sc = device_get_softc(dev); 15635d43fd68SRuslan Bukin sc->dev = dev; 15645d43fd68SRuslan Bukin sc->rx_idx = 0; 1565e5232621SOleksandr Tymoshenko sc->tx_desccount = TX_DESC_COUNT; 1566e5232621SOleksandr Tymoshenko sc->tx_mapcount = 0; 15675df53927SLuiz Otavio O Souza sc->mii_clk = IF_DWC_MII_CLK(dev); 15685df53927SLuiz Otavio O Souza sc->mactype = IF_DWC_MAC_TYPE(dev); 15695df53927SLuiz Otavio O Souza 1570824cfb47SOleksandr Tymoshenko node = ofw_bus_get_node(dev); 1571f77d8d10SEmmanuel Vadot switch (mii_fdt_get_contype(node)) { 1572f77d8d10SEmmanuel Vadot case MII_CONTYPE_RGMII: 1573f77d8d10SEmmanuel Vadot case MII_CONTYPE_RGMII_ID: 1574f77d8d10SEmmanuel Vadot case MII_CONTYPE_RGMII_RXID: 1575f77d8d10SEmmanuel Vadot case MII_CONTYPE_RGMII_TXID: 1576824cfb47SOleksandr Tymoshenko sc->phy_mode = PHY_MODE_RGMII; 1577f77d8d10SEmmanuel Vadot break; 1578f77d8d10SEmmanuel Vadot case MII_CONTYPE_RMII: 1579824cfb47SOleksandr Tymoshenko sc->phy_mode = PHY_MODE_RMII; 1580f77d8d10SEmmanuel Vadot break; 1581f77d8d10SEmmanuel Vadot default: 1582f77d8d10SEmmanuel Vadot device_printf(dev, "Unsupported MII type\n"); 1583f77d8d10SEmmanuel Vadot return (ENXIO); 1584824cfb47SOleksandr Tymoshenko } 1585824cfb47SOleksandr Tymoshenko 15863e5cd548SEmmanuel Vadot if (OF_getencprop(node, "snps,pbl", &pbl, sizeof(uint32_t)) <= 0) 15873e5cd548SEmmanuel Vadot pbl = BUS_MODE_DEFAULT_PBL; 15885e38d9e4SEmmanuel Vadot if (OF_getencprop(node, "snps,txpbl", &txpbl, sizeof(uint32_t)) <= 0) 15893e5cd548SEmmanuel Vadot txpbl = pbl; 15905e38d9e4SEmmanuel Vadot if (OF_getencprop(node, "snps,rxpbl", &rxpbl, sizeof(uint32_t)) <= 0) 15913e5cd548SEmmanuel Vadot rxpbl = pbl; 15925e38d9e4SEmmanuel Vadot if (OF_hasprop(node, "snps,no-pbl-x8") == 1) 15935e38d9e4SEmmanuel Vadot nopblx8 = true; 15945e38d9e4SEmmanuel Vadot if (OF_hasprop(node, "snps,fixed-burst") == 1) 15955e38d9e4SEmmanuel Vadot fixed_burst = true; 15965e38d9e4SEmmanuel Vadot 15975df53927SLuiz Otavio O Souza if (IF_DWC_INIT(dev) != 0) 15985df53927SLuiz Otavio O Souza return (ENXIO); 15995d43fd68SRuslan Bukin 16006a05f063SJared McNeill if (dwc_clock_init(dev) != 0) 16016a05f063SJared McNeill return (ENXIO); 16026a05f063SJared McNeill 16035d43fd68SRuslan Bukin if (bus_alloc_resources(dev, dwc_spec, sc->res)) { 16045d43fd68SRuslan Bukin device_printf(dev, "could not allocate resources\n"); 16055d43fd68SRuslan Bukin return (ENXIO); 16065d43fd68SRuslan Bukin } 16075d43fd68SRuslan Bukin 16085d43fd68SRuslan Bukin /* Read MAC before reset */ 1609f88e0af6SEmmanuel Vadot dwc_get_hwaddr(sc, macaddr); 16105d43fd68SRuslan Bukin 1611d7acb49aSJared McNeill /* Reset the PHY if needed */ 1612d7acb49aSJared McNeill if (dwc_reset(dev) != 0) { 1613d7acb49aSJared McNeill device_printf(dev, "Can't reset the PHY\n"); 1614*30f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 1615d7acb49aSJared McNeill return (ENXIO); 1616d7acb49aSJared McNeill } 1617d7acb49aSJared McNeill 16185d43fd68SRuslan Bukin /* Reset */ 16195d43fd68SRuslan Bukin reg = READ4(sc, BUS_MODE); 16205d43fd68SRuslan Bukin reg |= (BUS_MODE_SWR); 16215d43fd68SRuslan Bukin WRITE4(sc, BUS_MODE, reg); 16225d43fd68SRuslan Bukin 1623d8e5258dSRuslan Bukin for (i = 0; i < MAC_RESET_TIMEOUT; i++) { 16245d43fd68SRuslan Bukin if ((READ4(sc, BUS_MODE) & BUS_MODE_SWR) == 0) 16255d43fd68SRuslan Bukin break; 16265d43fd68SRuslan Bukin DELAY(10); 16275d43fd68SRuslan Bukin } 1628d8e5258dSRuslan Bukin if (i >= MAC_RESET_TIMEOUT) { 16295d43fd68SRuslan Bukin device_printf(sc->dev, "Can't reset DWC.\n"); 1630*30f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 16315d43fd68SRuslan Bukin return (ENXIO); 16325d43fd68SRuslan Bukin } 16335d43fd68SRuslan Bukin 16345e38d9e4SEmmanuel Vadot reg = BUS_MODE_USP; 16355e38d9e4SEmmanuel Vadot if (!nopblx8) 16365e38d9e4SEmmanuel Vadot reg |= BUS_MODE_EIGHTXPBL; 16375e38d9e4SEmmanuel Vadot reg |= (txpbl << BUS_MODE_PBL_SHIFT); 16385e38d9e4SEmmanuel Vadot reg |= (rxpbl << BUS_MODE_RPBL_SHIFT); 16395e38d9e4SEmmanuel Vadot if (fixed_burst) 16405e38d9e4SEmmanuel Vadot reg |= BUS_MODE_FIXEDBURST; 16413e5cd548SEmmanuel Vadot 16425d43fd68SRuslan Bukin WRITE4(sc, BUS_MODE, reg); 16435d43fd68SRuslan Bukin 16445d43fd68SRuslan Bukin /* 16455d43fd68SRuslan Bukin * DMA must be stop while changing descriptor list addresses. 16465d43fd68SRuslan Bukin */ 16475d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 16485d43fd68SRuslan Bukin reg &= ~(MODE_ST | MODE_SR); 16495d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 16505d43fd68SRuslan Bukin 1651*30f16ad4SEmmanuel Vadot if (setup_dma(sc)) { 1652*30f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 16535d43fd68SRuslan Bukin return (ENXIO); 1654*30f16ad4SEmmanuel Vadot } 16555d43fd68SRuslan Bukin 16565d43fd68SRuslan Bukin /* Setup addresses */ 16575d43fd68SRuslan Bukin WRITE4(sc, RX_DESCR_LIST_ADDR, sc->rxdesc_ring_paddr); 16585d43fd68SRuslan Bukin WRITE4(sc, TX_DESCR_LIST_ADDR, sc->txdesc_ring_paddr); 16595d43fd68SRuslan Bukin 1660d8e5258dSRuslan Bukin mtx_init(&sc->mtx, device_get_nameunit(sc->dev), 1661d8e5258dSRuslan Bukin MTX_NETWORK_LOCK, MTX_DEF); 1662d8e5258dSRuslan Bukin 1663d8e5258dSRuslan Bukin callout_init_mtx(&sc->dwc_callout, &sc->mtx, 0); 1664d8e5258dSRuslan Bukin 1665d8e5258dSRuslan Bukin /* Setup interrupt handler. */ 1666d8e5258dSRuslan Bukin error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE, 1667d8e5258dSRuslan Bukin NULL, dwc_intr, sc, &sc->intr_cookie); 1668d8e5258dSRuslan Bukin if (error != 0) { 1669d8e5258dSRuslan Bukin device_printf(dev, "could not setup interrupt handler.\n"); 1670*30f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 1671d8e5258dSRuslan Bukin return (ENXIO); 1672d8e5258dSRuslan Bukin } 1673d8e5258dSRuslan Bukin 16745d43fd68SRuslan Bukin /* Set up the ethernet interface. */ 16755d43fd68SRuslan Bukin sc->ifp = ifp = if_alloc(IFT_ETHER); 16765d43fd68SRuslan Bukin 16775d43fd68SRuslan Bukin ifp->if_softc = sc; 16785d43fd68SRuslan Bukin if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 16793bbd11eeSEmmanuel Vadot if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); 16803bbd11eeSEmmanuel Vadot if_setstartfn(ifp, dwc_txstart); 16813bbd11eeSEmmanuel Vadot if_setioctlfn(ifp, dwc_ioctl); 16823bbd11eeSEmmanuel Vadot if_setinitfn(ifp, dwc_init); 1683e5232621SOleksandr Tymoshenko if_setsendqlen(ifp, TX_MAP_COUNT - 1); 16843bbd11eeSEmmanuel Vadot if_setsendqready(sc->ifp); 168598ea5a7bSEmmanuel Vadot if_sethwassist(sc->ifp, CSUM_IP | CSUM_UDP | CSUM_TCP); 168698ea5a7bSEmmanuel Vadot if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM); 16873bbd11eeSEmmanuel Vadot if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp)); 16885d43fd68SRuslan Bukin 16895d43fd68SRuslan Bukin /* Attach the mii driver. */ 16905d43fd68SRuslan Bukin error = mii_attach(dev, &sc->miibus, ifp, dwc_media_change, 16915d43fd68SRuslan Bukin dwc_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, 16925d43fd68SRuslan Bukin MII_OFFSET_ANY, 0); 16935d43fd68SRuslan Bukin 16945d43fd68SRuslan Bukin if (error != 0) { 16955d43fd68SRuslan Bukin device_printf(dev, "PHY attach failed\n"); 1696*30f16ad4SEmmanuel Vadot bus_teardown_intr(dev, sc->res[1], sc->intr_cookie); 1697*30f16ad4SEmmanuel Vadot bus_release_resources(dev, dwc_spec, sc->res); 16985d43fd68SRuslan Bukin return (ENXIO); 16995d43fd68SRuslan Bukin } 17005d43fd68SRuslan Bukin sc->mii_softc = device_get_softc(sc->miibus); 17015d43fd68SRuslan Bukin 17025d43fd68SRuslan Bukin /* All ready to run, attach the ethernet interface. */ 17035d43fd68SRuslan Bukin ether_ifattach(ifp, macaddr); 17045d43fd68SRuslan Bukin sc->is_attached = true; 17055d43fd68SRuslan Bukin 17065d43fd68SRuslan Bukin return (0); 17075d43fd68SRuslan Bukin } 17085d43fd68SRuslan Bukin 17095d43fd68SRuslan Bukin static device_method_t dwc_methods[] = { 17105d43fd68SRuslan Bukin DEVMETHOD(device_probe, dwc_probe), 17115d43fd68SRuslan Bukin DEVMETHOD(device_attach, dwc_attach), 17125d43fd68SRuslan Bukin 17135d43fd68SRuslan Bukin /* MII Interface */ 17145d43fd68SRuslan Bukin DEVMETHOD(miibus_readreg, dwc_miibus_read_reg), 17155d43fd68SRuslan Bukin DEVMETHOD(miibus_writereg, dwc_miibus_write_reg), 17165d43fd68SRuslan Bukin DEVMETHOD(miibus_statchg, dwc_miibus_statchg), 17175d43fd68SRuslan Bukin 17185d43fd68SRuslan Bukin { 0, 0 } 17195d43fd68SRuslan Bukin }; 17205d43fd68SRuslan Bukin 17215df53927SLuiz Otavio O Souza driver_t dwc_driver = { 17225d43fd68SRuslan Bukin "dwc", 17235d43fd68SRuslan Bukin dwc_methods, 17245d43fd68SRuslan Bukin sizeof(struct dwc_softc), 17255d43fd68SRuslan Bukin }; 17265d43fd68SRuslan Bukin 17275d43fd68SRuslan Bukin static devclass_t dwc_devclass; 17285d43fd68SRuslan Bukin 17295d43fd68SRuslan Bukin DRIVER_MODULE(dwc, simplebus, dwc_driver, dwc_devclass, 0, 0); 17305d43fd68SRuslan Bukin DRIVER_MODULE(miibus, dwc, miibus_driver, miibus_devclass, 0, 0); 17315d43fd68SRuslan Bukin 17325d43fd68SRuslan Bukin MODULE_DEPEND(dwc, ether, 1, 1, 1); 17335d43fd68SRuslan Bukin MODULE_DEPEND(dwc, miibus, 1, 1, 1); 1734