Index: head/sys/mips/atheros/ar71xx_chip.c =================================================================== --- head/sys/mips/atheros/ar71xx_chip.c (revision 285120) +++ head/sys/mips/atheros/ar71xx_chip.c (revision 285121) @@ -1,341 +1,339 @@ /*- * Copyright (c) 2010 Adrian Chadd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* XXX these should replace the current definitions in ar71xxreg.h */ /* XXX perhaps an ar71xx_chip.h header file? */ #define AR71XX_PLL_REG_CPU_CONFIG AR71XX_PLL_CPU_BASE + 0x00 #define AR71XX_PLL_REG_SEC_CONFIG AR71XX_PLL_CPU_BASE + 0x04 #define AR71XX_PLL_REG_ETH0_INT_CLOCK AR71XX_PLL_CPU_BASE + 0x10 #define AR71XX_PLL_REG_ETH1_INT_CLOCK AR71XX_PLL_CPU_BASE + 0x14 #define AR71XX_PLL_DIV_SHIFT 3 #define AR71XX_PLL_DIV_MASK 0x1f #define AR71XX_CPU_DIV_SHIFT 16 #define AR71XX_CPU_DIV_MASK 0x3 #define AR71XX_DDR_DIV_SHIFT 18 #define AR71XX_DDR_DIV_MASK 0x3 #define AR71XX_AHB_DIV_SHIFT 20 #define AR71XX_AHB_DIV_MASK 0x7 /* XXX these shouldn't be in here - this file is a per-chip file */ /* XXX these should be in the top-level ar71xx type, not ar71xx -chip */ uint32_t u_ar71xx_cpu_freq; uint32_t u_ar71xx_ahb_freq; uint32_t u_ar71xx_ddr_freq; uint32_t u_ar71xx_uart_freq; uint32_t u_ar71xx_wdt_freq; uint32_t u_ar71xx_refclk; uint32_t u_ar71xx_mdio_freq; static void ar71xx_chip_detect_mem_size(void) { } static void ar71xx_chip_detect_sys_frequency(void) { uint32_t pll; uint32_t freq; uint32_t div; u_ar71xx_mdio_freq = u_ar71xx_refclk = AR71XX_BASE_FREQ; pll = ATH_READ_REG(AR71XX_PLL_REG_CPU_CONFIG); div = ((pll >> AR71XX_PLL_DIV_SHIFT) & AR71XX_PLL_DIV_MASK) + 1; freq = div * AR71XX_BASE_FREQ; div = ((pll >> AR71XX_CPU_DIV_SHIFT) & AR71XX_CPU_DIV_MASK) + 1; u_ar71xx_cpu_freq = freq / div; div = ((pll >> AR71XX_DDR_DIV_SHIFT) & AR71XX_DDR_DIV_MASK) + 1; u_ar71xx_ddr_freq = freq / div; div = (((pll >> AR71XX_AHB_DIV_SHIFT) & AR71XX_AHB_DIV_MASK) + 1) * 2; u_ar71xx_ahb_freq = u_ar71xx_cpu_freq / div; u_ar71xx_wdt_freq = u_ar71xx_cpu_freq / div; u_ar71xx_uart_freq = u_ar71xx_cpu_freq / div; } /* * This does not lock the CPU whilst doing the work! */ static void ar71xx_chip_device_stop(uint32_t mask) { uint32_t reg; reg = ATH_READ_REG(AR71XX_RST_RESET); ATH_WRITE_REG(AR71XX_RST_RESET, reg | mask); } static void ar71xx_chip_device_start(uint32_t mask) { uint32_t reg; reg = ATH_READ_REG(AR71XX_RST_RESET); ATH_WRITE_REG(AR71XX_RST_RESET, reg & ~mask); } static int ar71xx_chip_device_stopped(uint32_t mask) { uint32_t reg; reg = ATH_READ_REG(AR71XX_RST_RESET); return ((reg & mask) == mask); } void ar71xx_chip_set_mii_speed(uint32_t unit, uint32_t speed) { uint32_t val, reg, ctrl; switch (unit) { case 0: reg = AR71XX_MII0_CTRL; break; case 1: reg = AR71XX_MII1_CTRL; break; default: printf("%s: invalid MII unit set for arge unit: %d\n", __func__, unit); return; } switch (speed) { case 10: ctrl = MII_CTRL_SPEED_10; break; case 100: ctrl = MII_CTRL_SPEED_100; break; case 1000: ctrl = MII_CTRL_SPEED_1000; break; default: printf("%s: invalid MII speed (%d) set for arge unit: %d\n", __func__, speed, unit); return; } val = ATH_READ_REG(reg); val &= ~(MII_CTRL_SPEED_MASK << MII_CTRL_SPEED_SHIFT); val |= (ctrl & MII_CTRL_SPEED_MASK) << MII_CTRL_SPEED_SHIFT; ATH_WRITE_REG(reg, val); } void ar71xx_chip_set_mii_if(uint32_t unit, uint32_t mii_mode) { uint32_t val, reg, mii_if; switch (unit) { case 0: reg = AR71XX_MII0_CTRL; if (mii_mode == AR71XX_MII_MODE_GMII) mii_if = MII0_CTRL_IF_GMII; else if (mii_mode == AR71XX_MII_MODE_MII) mii_if = MII0_CTRL_IF_MII; else if (mii_mode == AR71XX_MII_MODE_RGMII) mii_if = MII0_CTRL_IF_RGMII; else if (mii_mode == AR71XX_MII_MODE_RMII) mii_if = MII0_CTRL_IF_RMII; else { printf("%s: invalid MII mode (%d) for unit %d\n", __func__, mii_mode, unit); return; } break; case 1: reg = AR71XX_MII1_CTRL; if (mii_mode == AR71XX_MII_MODE_RGMII) mii_if = MII1_CTRL_IF_RGMII; else if (mii_mode == AR71XX_MII_MODE_RMII) mii_if = MII1_CTRL_IF_RMII; else { printf("%s: invalid MII mode (%d) for unit %d\n", __func__, mii_mode, unit); return; } break; default: printf("%s: invalid MII unit set for arge unit: %d\n", __func__, unit); return; } val = ATH_READ_REG(reg); val &= ~(MII_CTRL_IF_MASK << MII_CTRL_IF_SHIFT); val |= (mii_if & MII_CTRL_IF_MASK) << MII_CTRL_IF_SHIFT; ATH_WRITE_REG(reg, val); } /* Speed is either 10, 100 or 1000 */ static void ar71xx_chip_set_pll_ge(int unit, int speed, uint32_t pll) { switch (unit) { case 0: ar71xx_write_pll(AR71XX_PLL_SEC_CONFIG, AR71XX_PLL_ETH_INT0_CLK, pll, AR71XX_PLL_ETH0_SHIFT); break; case 1: ar71xx_write_pll(AR71XX_PLL_SEC_CONFIG, AR71XX_PLL_ETH_INT1_CLK, pll, AR71XX_PLL_ETH1_SHIFT); break; default: printf("%s: invalid PLL set for arge unit: %d\n", __func__, unit); return; } } static void -ar71xx_chip_ddr_flush_ge(int unit) +ar71xx_chip_ddr_flush(ar71xx_flush_ddr_id_t id) { - switch (unit) { - case 0: + switch (id) { + case AR71XX_CPU_DDR_FLUSH_GE0: ar71xx_ddr_flush(AR71XX_WB_FLUSH_GE0); break; - case 1: + case AR71XX_CPU_DDR_FLUSH_GE1: ar71xx_ddr_flush(AR71XX_WB_FLUSH_GE1); break; + case AR71XX_CPU_DDR_FLUSH_USB: + ar71xx_ddr_flush(AR71XX_WB_FLUSH_USB); + break; + case AR71XX_CPU_DDR_FLUSH_PCIE: + ar71xx_ddr_flush(AR71XX_WB_FLUSH_PCI); + break; default: - printf("%s: invalid DDR flush for arge unit: %d\n", - __func__, unit); - return; + printf("%s: invalid DDR flush id (%d)\n", __func__, id); + break; } } -static void -ar71xx_chip_ddr_flush_ip2(void) -{ - ar71xx_ddr_flush(AR71XX_WB_FLUSH_PCI); -} - static uint32_t ar71xx_chip_get_eth_pll(unsigned int mac, int speed) { uint32_t pll; switch (speed) { case 10: pll = PLL_ETH_INT_CLK_10; break; case 100: pll = PLL_ETH_INT_CLK_100; break; case 1000: pll = PLL_ETH_INT_CLK_1000; break; default: printf("%s%d: invalid speed %d\n", __func__, mac, speed); pll = 0; } return (pll); } static void ar71xx_chip_init_usb_peripheral(void) { ar71xx_device_stop(RST_RESET_USB_OHCI_DLL | RST_RESET_USB_HOST | RST_RESET_USB_PHY); DELAY(1000); ar71xx_device_start(RST_RESET_USB_OHCI_DLL | RST_RESET_USB_HOST | RST_RESET_USB_PHY); DELAY(1000); ATH_WRITE_REG(AR71XX_USB_CTRL_CONFIG, USB_CTRL_CONFIG_OHCI_DES_SWAP | USB_CTRL_CONFIG_OHCI_BUF_SWAP | USB_CTRL_CONFIG_EHCI_DES_SWAP | USB_CTRL_CONFIG_EHCI_BUF_SWAP); ATH_WRITE_REG(AR71XX_USB_CTRL_FLADJ, (32 << USB_CTRL_FLADJ_HOST_SHIFT) | (3 << USB_CTRL_FLADJ_A5_SHIFT)); DELAY(1000); } struct ar71xx_cpu_def ar71xx_chip_def = { &ar71xx_chip_detect_mem_size, &ar71xx_chip_detect_sys_frequency, &ar71xx_chip_device_stop, &ar71xx_chip_device_start, &ar71xx_chip_device_stopped, &ar71xx_chip_set_pll_ge, &ar71xx_chip_set_mii_speed, &ar71xx_chip_set_mii_if, - &ar71xx_chip_ddr_flush_ge, &ar71xx_chip_get_eth_pll, - &ar71xx_chip_ddr_flush_ip2, + &ar71xx_chip_ddr_flush, &ar71xx_chip_init_usb_peripheral, }; Index: head/sys/mips/atheros/ar71xx_cpudef.h =================================================================== --- head/sys/mips/atheros/ar71xx_cpudef.h (revision 285120) +++ head/sys/mips/atheros/ar71xx_cpudef.h (revision 285121) @@ -1,177 +1,181 @@ /*- * Copyright (c) 2010 Adrian Chadd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* $FreeBSD$ */ #ifndef __AR71XX_CPUDEF_H__ #define __AR71XX_CPUDEF_H__ +typedef enum { + AR71XX_CPU_DDR_FLUSH_GE0, + AR71XX_CPU_DDR_FLUSH_GE1, + AR71XX_CPU_DDR_FLUSH_USB, + AR71XX_CPU_DDR_FLUSH_PCIE, + AR71XX_CPU_DDR_FLUSH_WMAC, + AR71XX_CPU_DDR_FLUSH_PCIE_EP, + AR71XX_CPU_DDR_FLUSH_CHECKSUM, +} ar71xx_flush_ddr_id_t; + struct ar71xx_cpu_def { void (* detect_mem_size) (void); void (* detect_sys_frequency) (void); void (* ar71xx_chip_device_stop) (uint32_t); void (* ar71xx_chip_device_start) (uint32_t); int (* ar71xx_chip_device_stopped) (uint32_t); void (* ar71xx_chip_set_pll_ge) (int, int, uint32_t); void (* ar71xx_chip_set_mii_speed) (uint32_t, uint32_t); void (* ar71xx_chip_set_mii_if) (uint32_t, ar71xx_mii_mode); - void (* ar71xx_chip_ddr_flush_ge) (int); uint32_t (* ar71xx_chip_get_eth_pll) (unsigned int, int); /* * From Linux - Handling this IRQ is a bit special. * AR71xx - AR71XX_DDR_REG_FLUSH_PCI * AR724x - AR724X_DDR_REG_FLUSH_PCIE * AR91xx - AR91XX_DDR_REG_FLUSH_WMAC * * These are set when STATUSF_IP2 is set in regiser c0. * This flush is done before the IRQ is handled to make * sure the driver correctly sees any memory updates. */ - void (* ar71xx_chip_ddr_flush_ip2) (void); + void (* ar71xx_chip_ddr_flush) (ar71xx_flush_ddr_id_t id); /* * The USB peripheral init code is subtly different for * each chip. */ void (* ar71xx_chip_init_usb_peripheral) (void); void (* ar71xx_chip_reset_ethernet_switch) (void); void (* ar71xx_chip_reset_wmac) (void); void (* ar71xx_chip_init_gmac) (void); void (* ar71xx_chip_reset_nfc) (int); void (* ar71xx_chip_gpio_out_configure) (int, uint8_t); }; extern struct ar71xx_cpu_def * ar71xx_cpu_ops; static inline void ar71xx_detect_sys_frequency(void) { ar71xx_cpu_ops->detect_sys_frequency(); } static inline void ar71xx_device_stop(uint32_t mask) { ar71xx_cpu_ops->ar71xx_chip_device_stop(mask); } static inline void ar71xx_device_start(uint32_t mask) { ar71xx_cpu_ops->ar71xx_chip_device_start(mask); } static inline int ar71xx_device_stopped(uint32_t mask) { return ar71xx_cpu_ops->ar71xx_chip_device_stopped(mask); } static inline void ar71xx_device_set_pll_ge(int unit, int speed, uint32_t pll) { ar71xx_cpu_ops->ar71xx_chip_set_pll_ge(unit, speed, pll); } static inline void ar71xx_device_set_mii_speed(int unit, int speed) { ar71xx_cpu_ops->ar71xx_chip_set_mii_speed(unit, speed); } static inline void ar71xx_device_set_mii_if(int unit, ar71xx_mii_mode mii_cfg) { ar71xx_cpu_ops->ar71xx_chip_set_mii_if(unit, mii_cfg); } -static inline void ar71xx_device_flush_ddr_ge(int unit) +static inline void ar71xx_device_flush_ddr(ar71xx_flush_ddr_id_t id) { - ar71xx_cpu_ops->ar71xx_chip_ddr_flush_ge(unit); + ar71xx_cpu_ops->ar71xx_chip_ddr_flush(id); } static inline uint32_t ar71xx_device_get_eth_pll(unsigned int unit, int speed) { return (ar71xx_cpu_ops->ar71xx_chip_get_eth_pll(unit, speed)); } static inline void ar71xx_init_usb_peripheral(void) { ar71xx_cpu_ops->ar71xx_chip_init_usb_peripheral(); } static inline void ar71xx_reset_ethernet_switch(void) { if (ar71xx_cpu_ops->ar71xx_chip_reset_ethernet_switch) ar71xx_cpu_ops->ar71xx_chip_reset_ethernet_switch(); } static inline void ar71xx_reset_wmac(void) { if (ar71xx_cpu_ops->ar71xx_chip_reset_wmac) ar71xx_cpu_ops->ar71xx_chip_reset_wmac(); } static inline void ar71xx_init_gmac(void) { if (ar71xx_cpu_ops->ar71xx_chip_init_gmac) ar71xx_cpu_ops->ar71xx_chip_init_gmac(); -} - -static inline void ar71xx_device_ddr_flush_ip2(void) -{ - ar71xx_cpu_ops->ar71xx_chip_ddr_flush_ip2(); } static inline void ar71xx_reset_nfc(int active) { if (ar71xx_cpu_ops->ar71xx_chip_reset_nfc) ar71xx_cpu_ops->ar71xx_chip_reset_nfc(active); } static inline void ar71xx_gpio_ouput_configure(int gpio, uint8_t func) { if (ar71xx_cpu_ops->ar71xx_chip_gpio_out_configure) ar71xx_cpu_ops->ar71xx_chip_gpio_out_configure(gpio, func); } /* XXX shouldn't be here! */ extern uint32_t u_ar71xx_refclk; extern uint32_t u_ar71xx_cpu_freq; extern uint32_t u_ar71xx_ahb_freq; extern uint32_t u_ar71xx_ddr_freq; extern uint32_t u_ar71xx_uart_freq; extern uint32_t u_ar71xx_wdt_freq; extern uint32_t u_ar71xx_mdio_freq; static inline uint64_t ar71xx_refclk(void) { return u_ar71xx_refclk; } static inline uint64_t ar71xx_cpu_freq(void) { return u_ar71xx_cpu_freq; } static inline uint64_t ar71xx_ahb_freq(void) { return u_ar71xx_ahb_freq; } static inline uint64_t ar71xx_ddr_freq(void) { return u_ar71xx_ddr_freq; } static inline uint64_t ar71xx_uart_freq(void) { return u_ar71xx_uart_freq; } static inline uint64_t ar71xx_wdt_freq(void) { return u_ar71xx_wdt_freq; } static inline uint64_t ar71xx_mdio_freq(void) { return u_ar71xx_mdio_freq; } #endif Index: head/sys/mips/atheros/ar71xx_ehci.c =================================================================== --- head/sys/mips/atheros/ar71xx_ehci.c (revision 285120) +++ head/sys/mips/atheros/ar71xx_ehci.c (revision 285121) @@ -1,265 +1,276 @@ /*- * Copyright (c) 2008 Sam Leffler. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * AR71XX attachment driver for the USB Enhanced Host Controller. */ #include __FBSDID("$FreeBSD$"); #include "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include /* for stuff in ar71xx_cpudef.h */ +#include #include #define EHCI_HC_DEVSTR "AR71XX Integrated USB 2.0 controller" struct ar71xx_ehci_softc { ehci_softc_t base; /* storage for EHCI code */ }; static device_attach_t ar71xx_ehci_attach; static device_detach_t ar71xx_ehci_detach; bs_r_1_proto(reversed); bs_w_1_proto(reversed); static int ar71xx_ehci_probe(device_t self) { device_set_desc(self, EHCI_HC_DEVSTR); return (BUS_PROBE_NOWILDCARD); } +static void +ar71xx_ehci_intr(void *arg) +{ + + /* XXX TODO: should really see if this was our interrupt.. */ + ar71xx_device_flush_ddr(AR71XX_CPU_DDR_FLUSH_USB); + ehci_interrupt(arg); +} + static int ar71xx_ehci_attach(device_t self) { struct ar71xx_ehci_softc *isc = device_get_softc(self); ehci_softc_t *sc = &isc->base; int err; int rid; /* initialise some bus fields */ sc->sc_bus.parent = self; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { return (ENOMEM); } sc->sc_bus.usbrev = USB_REV_2_0; /* NB: hints fix the memory location and irq */ rid = 0; sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(self, "Could not map memory\n"); goto error; } /* * Craft special resource for bus space ops that handle * byte-alignment of non-word addresses. */ sc->sc_io_tag = ar71xx_bus_space_reversed; sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->sc_irq_res == NULL) { device_printf(self, "Could not allocate irq\n"); goto error; } sc->sc_bus.bdev = device_add_child(self, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); sprintf(sc->sc_vendor, "Atheros"); err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, - NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); + NULL, ar71xx_ehci_intr, sc, &sc->sc_intr_hdl); if (err) { device_printf(self, "Could not setup irq, %d\n", err); sc->sc_intr_hdl = NULL; goto error; } /* * Arrange to force Host mode, select big-endian byte alignment, * and arrange to not terminate reset operations (the adapter * will ignore it if we do but might as well save a reg write). * Also, the controller has an embedded Transaction Translator * which means port speed must be read from the Port Status * register following a port enable. */ sc->sc_flags = EHCI_SCFLG_SETMODE; switch (ar71xx_soc) { case AR71XX_SOC_AR7241: case AR71XX_SOC_AR7242: case AR71XX_SOC_AR9130: case AR71XX_SOC_AR9132: case AR71XX_SOC_AR9330: case AR71XX_SOC_AR9331: case AR71XX_SOC_AR9341: case AR71XX_SOC_AR9342: case AR71XX_SOC_AR9344: case AR71XX_SOC_QCA9556: case AR71XX_SOC_QCA9558: sc->sc_flags |= EHCI_SCFLG_TT | EHCI_SCFLG_NORESTERM; break; default: /* fallthrough */ break; } /* * ehci_reset() needs the correct offset to access the host controller * registers. The AR724x/AR913x offsets aren't 0. */ sc->sc_offs = EHCI_CAPLENGTH(EREAD4(sc, EHCI_CAPLEN_HCIVERSION)); (void) ehci_reset(sc); err = ehci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(self, "USB init failed err=%d\n", err); goto error; } return (0); error: ar71xx_ehci_detach(self); return (ENXIO); } static int ar71xx_ehci_detach(device_t self) { struct ar71xx_ehci_softc *isc = device_get_softc(self); ehci_softc_t *sc = &isc->base; device_t bdev; int err; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(self, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(self); if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * only call ehci_detach() after ehci_init() */ ehci_detach(sc); err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); if (err) /* XXX or should we panic? */ device_printf(self, "Could not tear down irq, %d\n", err); sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(self, SYS_RES_MEMORY, 0, sc->sc_io_res); sc->sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); return (0); } static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ar71xx_ehci_probe), DEVMETHOD(device_attach, ar71xx_ehci_attach), DEVMETHOD(device_detach, ar71xx_ehci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ehci_driver = { .name = "ehci", .methods = ehci_methods, .size = sizeof(struct ar71xx_ehci_softc), }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, nexus, ehci_driver, ehci_devclass, 0, 0); DRIVER_MODULE(ehci, apb, ehci_driver, ehci_devclass, 0, 0); MODULE_DEPEND(ehci, usb, 1, 1, 1); Index: head/sys/mips/atheros/ar71xx_ohci.c =================================================================== --- head/sys/mips/atheros/ar71xx_ohci.c (revision 285120) +++ head/sys/mips/atheros/ar71xx_ohci.c (revision 285121) @@ -1,213 +1,226 @@ /*- * Copyright (c) 2009, Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include /* for stuff in ar71xx_cpudef.h */ +#include + static int ar71xx_ohci_attach(device_t dev); static int ar71xx_ohci_detach(device_t dev); static int ar71xx_ohci_probe(device_t dev); struct ar71xx_ohci_softc { struct ohci_softc sc_ohci; }; static int ar71xx_ohci_probe(device_t dev) { device_set_desc(dev, "AR71XX integrated OHCI controller"); return (BUS_PROBE_DEFAULT); } +static void +ar71xx_ohci_intr(void *arg) +{ + + /* XXX TODO: should really see if this was our interrupt.. */ + ar71xx_device_flush_ddr(AR71XX_CPU_DDR_FLUSH_USB); + ohci_interrupt(arg); +} + + static int ar71xx_ohci_attach(device_t dev) { struct ar71xx_ohci_softc *sc = device_get_softc(dev); int err; int rid; /* initialise some bus fields */ sc->sc_ohci.sc_bus.parent = dev; sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices; sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES; sc->sc_ohci.sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_ohci.sc_bus, USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { return (ENOMEM); } sc->sc_ohci.sc_dev = dev; rid = 0; sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_ohci.sc_io_res == NULL) { err = ENOMEM; goto error; } sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res); sc->sc_ohci.sc_io_hdl = rman_get_bushandle(sc->sc_ohci.sc_io_res); sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res); rid = 0; sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_ohci.sc_irq_res == NULL) { err = ENOMEM; goto error; } sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (sc->sc_ohci.sc_bus.bdev == NULL) { err = ENOMEM; goto error; } device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus); err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, - (driver_intr_t *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl); + ar71xx_ohci_intr, sc, &sc->sc_ohci.sc_intr_hdl); if (err) { err = ENXIO; goto error; } strlcpy(sc->sc_ohci.sc_vendor, "Atheros", sizeof(sc->sc_ohci.sc_vendor)); bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0); err = ohci_init(&sc->sc_ohci); if (!err) err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev); if (err) goto error; return (0); error: if (err) { ar71xx_ohci_detach(dev); return (err); } return (err); } static int ar71xx_ohci_detach(device_t dev) { struct ar71xx_ohci_softc *sc = device_get_softc(dev); device_t bdev; if (sc->sc_ohci.sc_bus.bdev) { bdev = sc->sc_ohci.sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(dev); /* * Put the controller into reset, then disable clocks and do * the MI tear down. We have to disable the clocks/hardware * after we do the rest of the teardown. We also disable the * clocks in the opposite order we acquire them, but that * doesn't seem to be absolutely necessary. We free up the * clocks after we disable them, so the system could, in * theory, reuse them. */ bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0); if (sc->sc_ohci.sc_intr_hdl) { bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, sc->sc_ohci.sc_intr_hdl); sc->sc_ohci.sc_intr_hdl = NULL; } if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) { /* * only call ohci_detach() after ohci_init() */ ohci_detach(&sc->sc_ohci); bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.sc_irq_res); sc->sc_ohci.sc_irq_res = NULL; } if (sc->sc_ohci.sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_ohci.sc_io_res); sc->sc_ohci.sc_io_res = NULL; sc->sc_ohci.sc_io_tag = 0; sc->sc_ohci.sc_io_hdl = 0; } usb_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc); return (0); } static device_method_t ohci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ar71xx_ohci_probe), DEVMETHOD(device_attach, ar71xx_ohci_attach), DEVMETHOD(device_detach, ar71xx_ohci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ohci_driver = { .name = "ohci", .methods = ohci_methods, .size = sizeof(struct ar71xx_ohci_softc), }; static devclass_t ohci_devclass; DRIVER_MODULE(ohci, apb, ohci_driver, ohci_devclass, 0, 0); Index: head/sys/mips/atheros/ar71xx_pci.c =================================================================== --- head/sys/mips/atheros/ar71xx_pci.c (revision 285120) +++ head/sys/mips/atheros/ar71xx_pci.c (revision 285121) @@ -1,706 +1,706 @@ /*- * Copyright (c) 2009, Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ar71xx.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" #include #include #include #ifdef AR71XX_ATH_EEPROM #include #endif /* AR71XX_ATH_EEPROM */ #undef AR71XX_PCI_DEBUG #ifdef AR71XX_PCI_DEBUG #define dprintf printf #else #define dprintf(x, arg...) #endif struct mtx ar71xx_pci_mtx; MTX_SYSINIT(ar71xx_pci_mtx, &ar71xx_pci_mtx, "ar71xx PCI space mutex", MTX_SPIN); struct ar71xx_pci_softc { device_t sc_dev; int sc_busno; int sc_baseslot; struct rman sc_mem_rman; struct rman sc_irq_rman; struct intr_event *sc_eventstab[AR71XX_PCI_NIRQS]; mips_intrcnt_t sc_intr_counter[AR71XX_PCI_NIRQS]; struct resource *sc_irq; void *sc_ih; }; static int ar71xx_pci_setup_intr(device_t, device_t, struct resource *, int, driver_filter_t *, driver_intr_t *, void *, void **); static int ar71xx_pci_teardown_intr(device_t, device_t, struct resource *, void *); static int ar71xx_pci_intr(void *); static void ar71xx_pci_mask_irq(void *source) { uint32_t reg; unsigned int irq = (unsigned int)source; /* XXX is the PCI lock required here? */ reg = ATH_READ_REG(AR71XX_PCI_INTR_MASK); /* flush */ reg = ATH_READ_REG(AR71XX_PCI_INTR_MASK); ATH_WRITE_REG(AR71XX_PCI_INTR_MASK, reg & ~(1 << irq)); } static void ar71xx_pci_unmask_irq(void *source) { uint32_t reg; unsigned int irq = (unsigned int)source; /* XXX is the PCI lock required here? */ reg = ATH_READ_REG(AR71XX_PCI_INTR_MASK); ATH_WRITE_REG(AR71XX_PCI_INTR_MASK, reg | (1 << irq)); /* flush */ reg = ATH_READ_REG(AR71XX_PCI_INTR_MASK); } /* * get bitmask for bytes of interest: * 0 - we want this byte, 1 - ignore it. e.g: we read 1 byte * from register 7. Bitmask would be: 0111 */ static uint32_t ar71xx_get_bytes_to_read(int reg, int bytes) { uint32_t bytes_to_read = 0; if ((bytes % 4) == 0) bytes_to_read = 0; else if ((bytes % 4) == 1) bytes_to_read = (~(1 << (reg % 4))) & 0xf; else if ((bytes % 4) == 2) bytes_to_read = (~(3 << (reg % 4))) & 0xf; else panic("%s: wrong combination", __func__); return (bytes_to_read); } static int ar71xx_pci_check_bus_error(void) { uint32_t error, addr, has_errors = 0; mtx_assert(&ar71xx_pci_mtx, MA_OWNED); error = ATH_READ_REG(AR71XX_PCI_ERROR) & 0x3; dprintf("%s: PCI error = %02x\n", __func__, error); if (error) { addr = ATH_READ_REG(AR71XX_PCI_ERROR_ADDR); /* Do not report it yet */ #if 0 printf("PCI bus error %d at addr 0x%08x\n", error, addr); #endif ATH_WRITE_REG(AR71XX_PCI_ERROR, error); has_errors = 1; } error = ATH_READ_REG(AR71XX_PCI_AHB_ERROR) & 0x1; dprintf("%s: AHB error = %02x\n", __func__, error); if (error) { addr = ATH_READ_REG(AR71XX_PCI_AHB_ERROR_ADDR); /* Do not report it yet */ #if 0 printf("AHB bus error %d at addr 0x%08x\n", error, addr); #endif ATH_WRITE_REG(AR71XX_PCI_AHB_ERROR, error); has_errors = 1; } return (has_errors); } static uint32_t ar71xx_pci_make_addr(int bus, int slot, int func, int reg) { if (bus == 0) { return ((1 << slot) | (func << 8) | (reg & ~3)); } else { return ((bus << 16) | (slot << 11) | (func << 8) | (reg & ~3) | 1); } } static int ar71xx_pci_conf_setup(int bus, int slot, int func, int reg, int bytes, uint32_t cmd) { uint32_t addr = ar71xx_pci_make_addr(bus, slot, func, (reg & ~3)); mtx_assert(&ar71xx_pci_mtx, MA_OWNED); cmd |= (ar71xx_get_bytes_to_read(reg, bytes) << 4); ATH_WRITE_REG(AR71XX_PCI_CONF_ADDR, addr); ATH_WRITE_REG(AR71XX_PCI_CONF_CMD, cmd); dprintf("%s: tag (%x, %x, %x) %d/%d addr=%08x, cmd=%08x\n", __func__, bus, slot, func, reg, bytes, addr, cmd); return ar71xx_pci_check_bus_error(); } static uint32_t ar71xx_pci_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int bytes) { uint32_t data; uint32_t shift, mask; /* register access is 32-bit aligned */ shift = (reg & 3) * 8; /* Create a mask based on the width, post-shift */ if (bytes == 2) mask = 0xffff; else if (bytes == 1) mask = 0xff; else mask = 0xffffffff; dprintf("%s: tag (%x, %x, %x) reg %d(%d)\n", __func__, bus, slot, func, reg, bytes); mtx_lock_spin(&ar71xx_pci_mtx); if (ar71xx_pci_conf_setup(bus, slot, func, reg, bytes, PCI_CONF_CMD_READ) == 0) data = ATH_READ_REG(AR71XX_PCI_CONF_READ_DATA); else data = -1; mtx_unlock_spin(&ar71xx_pci_mtx); /* get request bytes from 32-bit word */ data = (data >> shift) & mask; dprintf("%s: read 0x%x\n", __func__, data); return (data); } static void ar71xx_pci_local_write(device_t dev, uint32_t reg, uint32_t data, int bytes) { uint32_t cmd; dprintf("%s: local write reg %d(%d)\n", __func__, reg, bytes); data = data << (8*(reg % 4)); cmd = PCI_LCONF_CMD_WRITE | (reg & ~3); cmd |= (ar71xx_get_bytes_to_read(reg, bytes) << 20); mtx_lock_spin(&ar71xx_pci_mtx); ATH_WRITE_REG(AR71XX_PCI_LCONF_CMD, cmd); ATH_WRITE_REG(AR71XX_PCI_LCONF_WRITE_DATA, data); mtx_unlock_spin(&ar71xx_pci_mtx); } static void ar71xx_pci_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, uint32_t data, int bytes) { dprintf("%s: tag (%x, %x, %x) reg %d(%d)\n", __func__, bus, slot, func, reg, bytes); data = data << (8*(reg % 4)); mtx_lock_spin(&ar71xx_pci_mtx); if (ar71xx_pci_conf_setup(bus, slot, func, reg, bytes, PCI_CONF_CMD_WRITE) == 0) ATH_WRITE_REG(AR71XX_PCI_CONF_WRITE_DATA, data); mtx_unlock_spin(&ar71xx_pci_mtx); } #ifdef AR71XX_ATH_EEPROM /* * Some embedded boards (eg AP94) have the MAC attached via PCI but they * don't have the MAC-attached EEPROM. The register initialisation * values and calibration data are stored in the on-board flash. * This routine initialises the NIC via the EEPROM register contents * before the probe/attach routines get a go at things. */ static void ar71xx_pci_fixup(device_t dev, u_int bus, u_int slot, u_int func, long flash_addr, int len) { uint16_t *cal_data = (uint16_t *) MIPS_PHYS_TO_KSEG1(flash_addr); uint32_t reg, val, bar0; if (bootverbose) device_printf(dev, "%s: flash_addr=%lx, cal_data=%p\n", __func__, flash_addr, cal_data); /* XXX check 0xa55a */ /* Save bar(0) address - just to flush bar(0) (SoC WAR) ? */ bar0 = ar71xx_pci_read_config(dev, bus, slot, func, PCIR_BAR(0), 4); ar71xx_pci_write_config(dev, bus, slot, func, PCIR_BAR(0), AR71XX_PCI_MEM_BASE, 4); val = ar71xx_pci_read_config(dev, bus, slot, func, PCIR_COMMAND, 2); val |= (PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN); ar71xx_pci_write_config(dev, bus, slot, func, PCIR_COMMAND, val, 2); cal_data += 3; while (*cal_data != 0xffff) { reg = *cal_data++; val = *cal_data++; val |= (*cal_data++) << 16; if (bootverbose) printf(" reg: %x, val=%x\n", reg, val); /* Write eeprom fixup data to device memory */ ATH_WRITE_REG(AR71XX_PCI_MEM_BASE + reg, val); DELAY(100); } val = ar71xx_pci_read_config(dev, bus, slot, func, PCIR_COMMAND, 2); val &= ~(PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN); ar71xx_pci_write_config(dev, bus, slot, func, PCIR_COMMAND, val, 2); /* Write the saved bar(0) address */ ar71xx_pci_write_config(dev, bus, slot, func, PCIR_BAR(0), bar0, 4); } static void ar71xx_pci_slot_fixup(device_t dev, u_int bus, u_int slot, u_int func) { long int flash_addr; char buf[64]; int size; /* * Check whether the given slot has a hint to poke. */ if (bootverbose) device_printf(dev, "%s: checking dev %s, %d/%d/%d\n", __func__, device_get_nameunit(dev), bus, slot, func); snprintf(buf, sizeof(buf), "bus.%d.%d.%d.ath_fixup_addr", bus, slot, func); if (resource_long_value(device_get_name(dev), device_get_unit(dev), buf, &flash_addr) == 0) { snprintf(buf, sizeof(buf), "bus.%d.%d.%d.ath_fixup_size", bus, slot, func); if (resource_int_value(device_get_name(dev), device_get_unit(dev), buf, &size) != 0) { device_printf(dev, "%s: missing hint '%s', aborting EEPROM\n", __func__, buf); return; } device_printf(dev, "found EEPROM at 0x%lx on %d.%d.%d\n", flash_addr, bus, slot, func); ar71xx_pci_fixup(dev, bus, slot, func, flash_addr, size); ar71xx_pci_slot_create_eeprom_firmware(dev, bus, slot, func, flash_addr, size); } } #endif /* AR71XX_ATH_EEPROM */ static int ar71xx_pci_probe(device_t dev) { return (BUS_PROBE_NOWILDCARD); } static int ar71xx_pci_attach(device_t dev) { int busno = 0; int rid = 0; struct ar71xx_pci_softc *sc = device_get_softc(dev); sc->sc_mem_rman.rm_type = RMAN_ARRAY; sc->sc_mem_rman.rm_descr = "ar71xx PCI memory window"; if (rman_init(&sc->sc_mem_rman) != 0 || rman_manage_region(&sc->sc_mem_rman, AR71XX_PCI_MEM_BASE, AR71XX_PCI_MEM_BASE + AR71XX_PCI_MEM_SIZE - 1) != 0) { panic("ar71xx_pci_attach: failed to set up I/O rman"); } sc->sc_irq_rman.rm_type = RMAN_ARRAY; sc->sc_irq_rman.rm_descr = "ar71xx PCI IRQs"; if (rman_init(&sc->sc_irq_rman) != 0 || rman_manage_region(&sc->sc_irq_rman, AR71XX_PCI_IRQ_START, AR71XX_PCI_IRQ_END) != 0) panic("ar71xx_pci_attach: failed to set up IRQ rman"); /* * Check if there is a base slot hint. Otherwise use default value. */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), "baseslot", &sc->sc_baseslot) != 0) { device_printf(dev, "%s: missing hint '%s', default to AR71XX_PCI_BASE_SLOT\n", __func__, "baseslot"); sc->sc_baseslot = AR71XX_PCI_BASE_SLOT; } ATH_WRITE_REG(AR71XX_PCI_INTR_STATUS, 0); ATH_WRITE_REG(AR71XX_PCI_INTR_MASK, 0); /* Hook up our interrupt handler. */ if ((sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { device_printf(dev, "unable to allocate IRQ resource\n"); return ENXIO; } if ((bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC, ar71xx_pci_intr, NULL, sc, &sc->sc_ih))) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); return ENXIO; } /* reset PCI core and PCI bus */ ar71xx_device_stop(RST_RESET_PCI_CORE | RST_RESET_PCI_BUS); DELAY(100000); ar71xx_device_start(RST_RESET_PCI_CORE | RST_RESET_PCI_BUS); DELAY(100000); /* Init PCI windows */ ATH_WRITE_REG(AR71XX_PCI_WINDOW0, PCI_WINDOW0_ADDR); ATH_WRITE_REG(AR71XX_PCI_WINDOW1, PCI_WINDOW1_ADDR); ATH_WRITE_REG(AR71XX_PCI_WINDOW2, PCI_WINDOW2_ADDR); ATH_WRITE_REG(AR71XX_PCI_WINDOW3, PCI_WINDOW3_ADDR); ATH_WRITE_REG(AR71XX_PCI_WINDOW4, PCI_WINDOW4_ADDR); ATH_WRITE_REG(AR71XX_PCI_WINDOW5, PCI_WINDOW5_ADDR); ATH_WRITE_REG(AR71XX_PCI_WINDOW6, PCI_WINDOW6_ADDR); ATH_WRITE_REG(AR71XX_PCI_WINDOW7, PCI_WINDOW7_CONF_ADDR); DELAY(100000); mtx_lock_spin(&ar71xx_pci_mtx); ar71xx_pci_check_bus_error(); mtx_unlock_spin(&ar71xx_pci_mtx); /* Fixup internal PCI bridge */ ar71xx_pci_local_write(dev, PCIR_COMMAND, PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN | PCIM_CMD_SERRESPEN | PCIM_CMD_BACKTOBACK | PCIM_CMD_PERRESPEN | PCIM_CMD_MWRICEN, 4); #ifdef AR71XX_ATH_EEPROM /* * Hard-code a check for slot 17 and 18 - these are * the two PCI slots which may have a PCI device that * requires "fixing". */ ar71xx_pci_slot_fixup(dev, 0, 17, 0); ar71xx_pci_slot_fixup(dev, 0, 18, 0); #endif /* AR71XX_ATH_EEPROM */ device_add_child(dev, "pci", busno); return (bus_generic_attach(dev)); } static int ar71xx_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct ar71xx_pci_softc *sc = device_get_softc(dev); switch (which) { case PCIB_IVAR_DOMAIN: *result = 0; return (0); case PCIB_IVAR_BUS: *result = sc->sc_busno; return (0); } return (ENOENT); } static int ar71xx_pci_write_ivar(device_t dev, device_t child, int which, uintptr_t result) { struct ar71xx_pci_softc * sc = device_get_softc(dev); switch (which) { case PCIB_IVAR_BUS: sc->sc_busno = result; return (0); } return (ENOENT); } static struct resource * ar71xx_pci_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct ar71xx_pci_softc *sc = device_get_softc(bus); struct resource *rv; struct rman *rm; switch (type) { case SYS_RES_IRQ: rm = &sc->sc_irq_rman; break; case SYS_RES_MEMORY: rm = &sc->sc_mem_rman; break; default: return (NULL); } rv = rman_reserve_resource(rm, start, end, count, flags, child); if (rv == NULL) return (NULL); rman_set_rid(rv, *rid); if (flags & RF_ACTIVE) { if (bus_activate_resource(child, type, *rid, rv)) { rman_release_resource(rv); return (NULL); } } return (rv); } static int ar71xx_pci_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { int res = (BUS_ACTIVATE_RESOURCE(device_get_parent(bus), child, type, rid, r)); if (!res) { switch(type) { case SYS_RES_MEMORY: case SYS_RES_IOPORT: rman_set_bustag(r, ar71xx_bus_space_pcimem); break; } } return (res); } static int ar71xx_pci_setup_intr(device_t bus, device_t child, struct resource *ires, int flags, driver_filter_t *filt, driver_intr_t *handler, void *arg, void **cookiep) { struct ar71xx_pci_softc *sc = device_get_softc(bus); struct intr_event *event; int irq, error; irq = rman_get_start(ires); if (irq > AR71XX_PCI_IRQ_END) panic("%s: bad irq %d", __func__, irq); event = sc->sc_eventstab[irq]; if (event == NULL) { error = intr_event_create(&event, (void *)irq, 0, irq, ar71xx_pci_mask_irq, ar71xx_pci_unmask_irq, NULL, NULL, "pci intr%d:", irq); if (error == 0) { sc->sc_eventstab[irq] = event; sc->sc_intr_counter[irq] = mips_intrcnt_create(event->ie_name); } else return (error); } intr_event_add_handler(event, device_get_nameunit(child), filt, handler, arg, intr_priority(flags), flags, cookiep); mips_intrcnt_setname(sc->sc_intr_counter[irq], event->ie_fullname); ar71xx_pci_unmask_irq((void*)irq); return (0); } static int ar71xx_pci_teardown_intr(device_t dev, device_t child, struct resource *ires, void *cookie) { struct ar71xx_pci_softc *sc = device_get_softc(dev); int irq, result; irq = rman_get_start(ires); if (irq > AR71XX_PCI_IRQ_END) panic("%s: bad irq %d", __func__, irq); if (sc->sc_eventstab[irq] == NULL) panic("Trying to teardown unoccupied IRQ"); ar71xx_pci_mask_irq((void*)irq); result = intr_event_remove_handler(cookie); if (!result) sc->sc_eventstab[irq] = NULL; return (result); } static int ar71xx_pci_intr(void *arg) { struct ar71xx_pci_softc *sc = arg; struct intr_event *event; uint32_t reg, irq, mask; reg = ATH_READ_REG(AR71XX_PCI_INTR_STATUS); mask = ATH_READ_REG(AR71XX_PCI_INTR_MASK); /* * Handle only unmasked interrupts */ reg &= mask; for (irq = AR71XX_PCI_IRQ_START; irq <= AR71XX_PCI_IRQ_END; irq++) { if (reg & (1 << irq)) { event = sc->sc_eventstab[irq]; if (!event || TAILQ_EMPTY(&event->ie_handlers)) { /* Ignore timer interrupts */ if (irq != 0) printf("Stray IRQ %d\n", irq); continue; } - /* Flush DDR FIFO for IP2 */ - ar71xx_device_ddr_flush_ip2(); + /* Flush DDR FIFO for PCI/PCIe */ + ar71xx_device_flush_ddr(AR71XX_CPU_DDR_FLUSH_PCIE); /* TODO: frame instead of NULL? */ intr_event_handle(event, NULL); mips_intrcnt_inc(sc->sc_intr_counter[irq]); } } return (FILTER_HANDLED); } static int ar71xx_pci_maxslots(device_t dev) { return (PCI_SLOTMAX); } static int ar71xx_pci_route_interrupt(device_t pcib, device_t device, int pin) { struct ar71xx_pci_softc *sc = device_get_softc(pcib); if (pci_get_slot(device) < sc->sc_baseslot) panic("%s: PCI slot %d is less then AR71XX_PCI_BASE_SLOT", __func__, pci_get_slot(device)); return (pci_get_slot(device) - sc->sc_baseslot); } static device_method_t ar71xx_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ar71xx_pci_probe), DEVMETHOD(device_attach, ar71xx_pci_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_read_ivar, ar71xx_pci_read_ivar), DEVMETHOD(bus_write_ivar, ar71xx_pci_write_ivar), DEVMETHOD(bus_alloc_resource, ar71xx_pci_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, ar71xx_pci_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, ar71xx_pci_setup_intr), DEVMETHOD(bus_teardown_intr, ar71xx_pci_teardown_intr), /* pcib interface */ DEVMETHOD(pcib_maxslots, ar71xx_pci_maxslots), DEVMETHOD(pcib_read_config, ar71xx_pci_read_config), DEVMETHOD(pcib_write_config, ar71xx_pci_write_config), DEVMETHOD(pcib_route_interrupt, ar71xx_pci_route_interrupt), DEVMETHOD_END }; static driver_t ar71xx_pci_driver = { "pcib", ar71xx_pci_methods, sizeof(struct ar71xx_pci_softc), }; static devclass_t ar71xx_pci_devclass; DRIVER_MODULE(ar71xx_pci, nexus, ar71xx_pci_driver, ar71xx_pci_devclass, 0, 0); Index: head/sys/mips/atheros/ar724x_chip.c =================================================================== --- head/sys/mips/atheros/ar724x_chip.c (revision 285120) +++ head/sys/mips/atheros/ar724x_chip.c (revision 285121) @@ -1,249 +1,246 @@ /*- * Copyright (c) 2010 Adrian Chadd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void ar724x_chip_detect_mem_size(void) { } static void ar724x_chip_detect_sys_frequency(void) { uint32_t pll; uint32_t freq; uint32_t div; u_ar71xx_mdio_freq = u_ar71xx_refclk = AR724X_BASE_FREQ; pll = ATH_READ_REG(AR724X_PLL_REG_CPU_CONFIG); div = ((pll >> AR724X_PLL_DIV_SHIFT) & AR724X_PLL_DIV_MASK); freq = div * AR724X_BASE_FREQ; div = ((pll >> AR724X_PLL_REF_DIV_SHIFT) & AR724X_PLL_REF_DIV_MASK); freq *= div; u_ar71xx_cpu_freq = freq; div = ((pll >> AR724X_DDR_DIV_SHIFT) & AR724X_DDR_DIV_MASK) + 1; u_ar71xx_ddr_freq = freq / div; div = (((pll >> AR724X_AHB_DIV_SHIFT) & AR724X_AHB_DIV_MASK) + 1) * 2; u_ar71xx_ahb_freq = u_ar71xx_cpu_freq / div; u_ar71xx_wdt_freq = u_ar71xx_cpu_freq / div; u_ar71xx_uart_freq = u_ar71xx_cpu_freq / div; } static void ar724x_chip_device_stop(uint32_t mask) { uint32_t mask_inv, reg; mask_inv = mask & AR724X_RESET_MODULE_USB_OHCI_DLL; reg = ATH_READ_REG(AR724X_RESET_REG_RESET_MODULE); reg |= mask; reg &= ~mask_inv; ATH_WRITE_REG(AR724X_RESET_REG_RESET_MODULE, reg); } static void ar724x_chip_device_start(uint32_t mask) { uint32_t mask_inv, reg; mask_inv = mask & AR724X_RESET_MODULE_USB_OHCI_DLL; reg = ATH_READ_REG(AR724X_RESET_REG_RESET_MODULE); reg &= ~mask; reg |= mask_inv; ATH_WRITE_REG(AR724X_RESET_REG_RESET_MODULE, reg); } static int ar724x_chip_device_stopped(uint32_t mask) { uint32_t reg; reg = ATH_READ_REG(AR724X_RESET_REG_RESET_MODULE); return ((reg & mask) == mask); } static void ar724x_chip_set_mii_speed(uint32_t unit, uint32_t speed) { /* XXX TODO */ return; } /* * XXX TODO: set the PLL for arge0 only on AR7242. * The PLL/clock requirements are different. * * Otherwise, it's a NULL function for AR7240, AR7241 and * AR7242 arge1. */ static void ar724x_chip_set_pll_ge(int unit, int speed, uint32_t pll) { switch (unit) { case 0: /* XXX TODO */ break; case 1: /* XXX TODO */ break; default: printf("%s: invalid PLL set for arge unit: %d\n", __func__, unit); return; } } static void -ar724x_chip_ddr_flush_ge(int unit) +ar724x_chip_ddr_flush(ar71xx_flush_ddr_id_t id) { - switch (unit) { - case 0: + switch (id) { + case AR71XX_CPU_DDR_FLUSH_GE0: ar71xx_ddr_flush(AR724X_DDR_REG_FLUSH_GE0); break; - case 1: + case AR71XX_CPU_DDR_FLUSH_GE1: ar71xx_ddr_flush(AR724X_DDR_REG_FLUSH_GE1); break; + case AR71XX_CPU_DDR_FLUSH_USB: + ar71xx_ddr_flush(AR724X_DDR_REG_FLUSH_USB); + break; + case AR71XX_CPU_DDR_FLUSH_PCIE: + ar71xx_ddr_flush(AR724X_DDR_REG_FLUSH_PCIE); + break; default: - printf("%s: invalid DDR flush for arge unit: %d\n", - __func__, unit); - return; + printf("%s: invalid DDR flush id (%d)\n", __func__, id); + break; } } -static void -ar724x_chip_ddr_flush_ip2(void) -{ - - ar71xx_ddr_flush(AR724X_DDR_REG_FLUSH_PCIE); -} - static uint32_t ar724x_chip_get_eth_pll(unsigned int mac, int speed) { return (0); } static void ar724x_chip_init_usb_peripheral(void) { switch (ar71xx_soc) { case AR71XX_SOC_AR7240: ar71xx_device_stop(AR724X_RESET_MODULE_USB_OHCI_DLL | AR724X_RESET_USB_HOST); DELAY(1000); ar71xx_device_start(AR724X_RESET_MODULE_USB_OHCI_DLL | AR724X_RESET_USB_HOST); DELAY(1000); /* * WAR for HW bug. Here it adjusts the duration * between two SOFS. */ ATH_WRITE_REG(AR71XX_USB_CTRL_FLADJ, (3 << USB_CTRL_FLADJ_A0_SHIFT)); break; case AR71XX_SOC_AR7241: case AR71XX_SOC_AR7242: ar71xx_device_start(AR724X_RESET_MODULE_USB_OHCI_DLL); DELAY(100); ar71xx_device_start(AR724X_RESET_USB_HOST); DELAY(100); ar71xx_device_start(AR724X_RESET_USB_PHY); DELAY(100); break; default: break; } } struct ar71xx_cpu_def ar724x_chip_def = { &ar724x_chip_detect_mem_size, &ar724x_chip_detect_sys_frequency, &ar724x_chip_device_stop, &ar724x_chip_device_start, &ar724x_chip_device_stopped, &ar724x_chip_set_pll_ge, &ar724x_chip_set_mii_speed, &ar71xx_chip_set_mii_if, - &ar724x_chip_ddr_flush_ge, &ar724x_chip_get_eth_pll, - &ar724x_chip_ddr_flush_ip2, + &ar724x_chip_ddr_flush, &ar724x_chip_init_usb_peripheral }; Index: head/sys/mips/atheros/ar724x_pci.c =================================================================== --- head/sys/mips/atheros/ar724x_pci.c (revision 285120) +++ head/sys/mips/atheros/ar724x_pci.c (revision 285121) @@ -1,664 +1,666 @@ /*- * Copyright (c) 2009, Oleksandr Tymoshenko * Copyright (c) 2011, Luiz Otavio O Souza. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ar71xx.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" #include #include #include #include #include #ifdef AR71XX_ATH_EEPROM #include #endif /* AR71XX_ATH_EEPROM */ #undef AR724X_PCI_DEBUG #ifdef AR724X_PCI_DEBUG #define dprintf printf #else #define dprintf(x, arg...) #endif struct ar71xx_pci_softc { device_t sc_dev; int sc_busno; struct rman sc_mem_rman; struct rman sc_irq_rman; struct intr_event *sc_eventstab[AR71XX_PCI_NIRQS]; mips_intrcnt_t sc_intr_counter[AR71XX_PCI_NIRQS]; struct resource *sc_irq; void *sc_ih; }; static int ar724x_pci_setup_intr(device_t, device_t, struct resource *, int, driver_filter_t *, driver_intr_t *, void *, void **); static int ar724x_pci_teardown_intr(device_t, device_t, struct resource *, void *); static int ar724x_pci_intr(void *); static void ar724x_pci_write(uint32_t reg, uint32_t offset, uint32_t data, int bytes) { uint32_t val, mask, shift; /* Register access is 32-bit aligned */ shift = (offset & 3) * 8; if (bytes % 4) mask = (1 << (bytes * 8)) - 1; else mask = 0xffffffff; val = ATH_READ_REG(reg + (offset & ~3)); val &= ~(mask << shift); val |= ((data & mask) << shift); ATH_WRITE_REG(reg + (offset & ~3), val); dprintf("%s: %#x/%#x addr=%#x, data=%#x(%#x), bytes=%d\n", __func__, reg, reg + (offset & ~3), offset, data, val, bytes); } static uint32_t ar724x_pci_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int bytes) { uint32_t data, shift, mask; /* Register access is 32-bit aligned */ shift = (reg & 3) * 8; /* Create a mask based on the width, post-shift */ if (bytes == 2) mask = 0xffff; else if (bytes == 1) mask = 0xff; else mask = 0xffffffff; dprintf("%s: tag (%x, %x, %x) reg %d(%d)\n", __func__, bus, slot, func, reg, bytes); if ((bus == 0) && (slot == 0) && (func == 0)) data = ATH_READ_REG(AR724X_PCI_CFG_BASE + (reg & ~3)); else data = -1; /* Get request bytes from 32-bit word */ data = (data >> shift) & mask; dprintf("%s: read 0x%x\n", __func__, data); return (data); } static void ar724x_pci_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, uint32_t data, int bytes) { dprintf("%s: tag (%x, %x, %x) reg %d(%d): %x\n", __func__, bus, slot, func, reg, bytes, data); if ((bus != 0) || (slot != 0) || (func != 0)) return; /* * WAR for BAR issue on AR7240 - We are unable to access the PCI * device space if we set the BAR with proper base address. * * However, we _do_ want to allow programming in the probe value * (0xffffffff) so the PCI code can find out how big the memory * map is for this device. Without it, it'll think the memory * map is 32 bits wide, the PCI code will then end up thinking * the register window is '0' and fail to allocate resources. */ if (reg == PCIR_BAR(0) && bytes == 4 && ar71xx_soc == AR71XX_SOC_AR7240 && data != 0xffffffff) ar724x_pci_write(AR724X_PCI_CFG_BASE, reg, 0xffff, bytes); else ar724x_pci_write(AR724X_PCI_CFG_BASE, reg, data, bytes); } static void ar724x_pci_mask_irq(void *source) { uint32_t reg; unsigned int irq = (unsigned int)source; /* XXX - Only one interrupt ? Only one device ? */ if (irq != AR71XX_PCI_IRQ_START) return; /* Update the interrupt mask reg */ reg = ATH_READ_REG(AR724X_PCI_INTR_MASK); ATH_WRITE_REG(AR724X_PCI_INTR_MASK, reg & ~AR724X_PCI_INTR_DEV0); /* Clear any pending interrupt */ reg = ATH_READ_REG(AR724X_PCI_INTR_STATUS); ATH_WRITE_REG(AR724X_PCI_INTR_STATUS, reg | AR724X_PCI_INTR_DEV0); } static void ar724x_pci_unmask_irq(void *source) { uint32_t reg; unsigned int irq = (unsigned int)source; /* XXX */ if (irq != AR71XX_PCI_IRQ_START) return; /* Update the interrupt mask reg */ reg = ATH_READ_REG(AR724X_PCI_INTR_MASK); ATH_WRITE_REG(AR724X_PCI_INTR_MASK, reg | AR724X_PCI_INTR_DEV0); } static int ar724x_pci_setup(device_t dev) { uint32_t reg; /* setup COMMAND register */ reg = PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN | PCIM_CMD_SERRESPEN | PCIM_CMD_BACKTOBACK | PCIM_CMD_PERRESPEN | PCIM_CMD_MWRICEN; ar724x_pci_write(AR724X_PCI_CRP_BASE, PCIR_COMMAND, reg, 2); ar724x_pci_write(AR724X_PCI_CRP_BASE, 0x20, 0x1ff01000, 4); ar724x_pci_write(AR724X_PCI_CRP_BASE, 0x24, 0x1ff01000, 4); reg = ATH_READ_REG(AR724X_PCI_RESET); if (reg != 0x7) { DELAY(100000); ATH_WRITE_REG(AR724X_PCI_RESET, 0); DELAY(100); ATH_WRITE_REG(AR724X_PCI_RESET, 4); DELAY(100000); } if (ar71xx_soc == AR71XX_SOC_AR7240) reg = AR724X_PCI_APP_LTSSM_ENABLE; else reg = 0x1ffc1; ATH_WRITE_REG(AR724X_PCI_APP, reg); /* Flush write */ (void) ATH_READ_REG(AR724X_PCI_APP); DELAY(1000); reg = ATH_READ_REG(AR724X_PCI_RESET); if ((reg & AR724X_PCI_RESET_LINK_UP) == 0) { device_printf(dev, "no PCIe controller found\n"); return (ENXIO); } if (ar71xx_soc == AR71XX_SOC_AR7241 || ar71xx_soc == AR71XX_SOC_AR7242) { reg = ATH_READ_REG(AR724X_PCI_APP); reg |= (1 << 16); ATH_WRITE_REG(AR724X_PCI_APP, reg); } return (0); } #ifdef AR71XX_ATH_EEPROM #define AR5416_EEPROM_MAGIC 0xa55a /* * XXX - This should not be here ! And this looks like Atheros (if_ath) only. */ static void ar724x_pci_fixup(device_t dev, long flash_addr, int len) { uint32_t bar0, reg, val; uint16_t *cal_data = (uint16_t *) MIPS_PHYS_TO_KSEG1(flash_addr); #if 0 if (cal_data[0] != AR5416_EEPROM_MAGIC) { device_printf(dev, "%s: Invalid calibration data from 0x%x\n", __func__, (uintptr_t) flash_addr); return; } #endif /* Save bar(0) address - just to flush bar(0) (SoC WAR) ? */ bar0 = ar724x_pci_read_config(dev, 0, 0, 0, PCIR_BAR(0), 4); /* Write temporary BAR0 to map the NIC into a fixed location */ ar724x_pci_write_config(dev, 0, 0, 0, PCIR_BAR(0), AR71XX_PCI_MEM_BASE, 4); val = ar724x_pci_read_config(dev, 0, 0, 0, PCIR_COMMAND, 2); val |= (PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN); ar724x_pci_write_config(dev, 0, 0, 0, PCIR_COMMAND, val, 2); /* set pointer to first reg address */ cal_data += 3; while (*cal_data != 0xffff) { reg = *cal_data++; val = *cal_data++; val |= (*cal_data++) << 16; if (bootverbose) printf(" 0x%08x=0x%04x\n", reg, val); /* Write eeprom fixup data to device memory */ ATH_WRITE_REG(AR71XX_PCI_MEM_BASE + reg, val); DELAY(100); } val = ar724x_pci_read_config(dev, 0, 0, 0, PCIR_COMMAND, 2); val &= ~(PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN); ar724x_pci_write_config(dev, 0, 0, 0, PCIR_COMMAND, val, 2); /* Write the saved bar(0) address */ ar724x_pci_write_config(dev, 0, 0, 0, PCIR_BAR(0), bar0, 4); } #undef AR5416_EEPROM_MAGIC /* * XXX This is (mostly) duplicated with ar71xx_pci.c. * It should at some point be fixed. */ static void ar724x_pci_slot_fixup(device_t dev) { long int flash_addr; char buf[64]; int size; /* * Check whether the given slot has a hint to poke. */ if (bootverbose) device_printf(dev, "%s: checking dev %s, %d/%d/%d\n", __func__, device_get_nameunit(dev), 0, 0, 0); snprintf(buf, sizeof(buf), "bus.%d.%d.%d.ath_fixup_addr", 0, 0, 0); if (resource_long_value(device_get_name(dev), device_get_unit(dev), buf, &flash_addr) == 0) { snprintf(buf, sizeof(buf), "bus.%d.%d.%d.ath_fixup_size", 0, 0, 0); if (resource_int_value(device_get_name(dev), device_get_unit(dev), buf, &size) != 0) { device_printf(dev, "%s: missing hint '%s', aborting EEPROM\n", __func__, buf); return; } device_printf(dev, "found EEPROM at 0x%lx on %d.%d.%d\n", flash_addr, 0, 0, 0); ar724x_pci_fixup(dev, flash_addr, size); ar71xx_pci_slot_create_eeprom_firmware(dev, 0, 0, 0, flash_addr, size); } } #endif /* AR71XX_ATH_EEPROM */ static int ar724x_pci_probe(device_t dev) { return (BUS_PROBE_NOWILDCARD); } static int ar724x_pci_attach(device_t dev) { struct ar71xx_pci_softc *sc = device_get_softc(dev); int busno = 0; int rid = 0; sc->sc_mem_rman.rm_type = RMAN_ARRAY; sc->sc_mem_rman.rm_descr = "ar724x PCI memory window"; if (rman_init(&sc->sc_mem_rman) != 0 || rman_manage_region(&sc->sc_mem_rman, AR71XX_PCI_MEM_BASE, AR71XX_PCI_MEM_BASE + AR71XX_PCI_MEM_SIZE - 1) != 0) { panic("ar724x_pci_attach: failed to set up I/O rman"); } sc->sc_irq_rman.rm_type = RMAN_ARRAY; sc->sc_irq_rman.rm_descr = "ar724x PCI IRQs"; if (rman_init(&sc->sc_irq_rman) != 0 || rman_manage_region(&sc->sc_irq_rman, AR71XX_PCI_IRQ_START, AR71XX_PCI_IRQ_END) != 0) panic("ar724x_pci_attach: failed to set up IRQ rman"); /* Disable interrupts */ ATH_WRITE_REG(AR724X_PCI_INTR_STATUS, 0); ATH_WRITE_REG(AR724X_PCI_INTR_MASK, 0); /* Hook up our interrupt handler. */ if ((sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { device_printf(dev, "unable to allocate IRQ resource\n"); return (ENXIO); } if ((bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC, ar724x_pci_intr, NULL, sc, &sc->sc_ih))) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); return (ENXIO); } /* Reset PCIe core and PCIe PHY */ ar71xx_device_stop(AR724X_RESET_PCIE); ar71xx_device_stop(AR724X_RESET_PCIE_PHY); ar71xx_device_stop(AR724X_RESET_PCIE_PHY_SERIAL); DELAY(100); ar71xx_device_start(AR724X_RESET_PCIE_PHY_SERIAL); DELAY(100); ar71xx_device_start(AR724X_RESET_PCIE_PHY); ar71xx_device_start(AR724X_RESET_PCIE); if (ar724x_pci_setup(dev)) return (ENXIO); #ifdef AR71XX_ATH_EEPROM ar724x_pci_slot_fixup(dev); #endif /* AR71XX_ATH_EEPROM */ /* Fixup internal PCI bridge */ ar724x_pci_write_config(dev, 0, 0, 0, PCIR_COMMAND, PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN | PCIM_CMD_SERRESPEN | PCIM_CMD_BACKTOBACK | PCIM_CMD_PERRESPEN | PCIM_CMD_MWRICEN, 2); device_add_child(dev, "pci", busno); return (bus_generic_attach(dev)); } static int ar724x_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct ar71xx_pci_softc *sc = device_get_softc(dev); switch (which) { case PCIB_IVAR_DOMAIN: *result = 0; return (0); case PCIB_IVAR_BUS: *result = sc->sc_busno; return (0); } return (ENOENT); } static int ar724x_pci_write_ivar(device_t dev, device_t child, int which, uintptr_t result) { struct ar71xx_pci_softc * sc = device_get_softc(dev); switch (which) { case PCIB_IVAR_BUS: sc->sc_busno = result; return (0); } return (ENOENT); } static struct resource * ar724x_pci_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct ar71xx_pci_softc *sc = device_get_softc(bus); struct resource *rv; struct rman *rm; switch (type) { case SYS_RES_IRQ: rm = &sc->sc_irq_rman; break; case SYS_RES_MEMORY: rm = &sc->sc_mem_rman; break; default: return (NULL); } rv = rman_reserve_resource(rm, start, end, count, flags, child); if (rv == NULL) return (NULL); rman_set_rid(rv, *rid); if (flags & RF_ACTIVE) { if (bus_activate_resource(child, type, *rid, rv)) { rman_release_resource(rv); return (NULL); } } return (rv); } static int ar724x_pci_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { int res = (BUS_ACTIVATE_RESOURCE(device_get_parent(bus), child, type, rid, r)); if (!res) { switch(type) { case SYS_RES_MEMORY: case SYS_RES_IOPORT: rman_set_bustag(r, ar71xx_bus_space_pcimem); break; } } return (res); } static int ar724x_pci_setup_intr(device_t bus, device_t child, struct resource *ires, int flags, driver_filter_t *filt, driver_intr_t *handler, void *arg, void **cookiep) { struct ar71xx_pci_softc *sc = device_get_softc(bus); struct intr_event *event; int irq, error; irq = rman_get_start(ires); if (irq > AR71XX_PCI_IRQ_END) panic("%s: bad irq %d", __func__, irq); event = sc->sc_eventstab[irq]; if (event == NULL) { error = intr_event_create(&event, (void *)irq, 0, irq, ar724x_pci_mask_irq, ar724x_pci_unmask_irq, NULL, NULL, "pci intr%d:", irq); if (error == 0) { sc->sc_eventstab[irq] = event; sc->sc_intr_counter[irq] = mips_intrcnt_create(event->ie_name); } else return error; } intr_event_add_handler(event, device_get_nameunit(child), filt, handler, arg, intr_priority(flags), flags, cookiep); mips_intrcnt_setname(sc->sc_intr_counter[irq], event->ie_fullname); ar724x_pci_unmask_irq((void*)irq); return (0); } static int ar724x_pci_teardown_intr(device_t dev, device_t child, struct resource *ires, void *cookie) { struct ar71xx_pci_softc *sc = device_get_softc(dev); int irq, result; irq = rman_get_start(ires); if (irq > AR71XX_PCI_IRQ_END) panic("%s: bad irq %d", __func__, irq); if (sc->sc_eventstab[irq] == NULL) panic("Trying to teardown unoccupied IRQ"); ar724x_pci_mask_irq((void*)irq); result = intr_event_remove_handler(cookie); if (!result) sc->sc_eventstab[irq] = NULL; return (result); } static int ar724x_pci_intr(void *arg) { struct ar71xx_pci_softc *sc = arg; struct intr_event *event; uint32_t reg, irq, mask; - ar71xx_device_ddr_flush_ip2(); reg = ATH_READ_REG(AR724X_PCI_INTR_STATUS); mask = ATH_READ_REG(AR724X_PCI_INTR_MASK); /* * Handle only unmasked interrupts */ reg &= mask; if (reg & AR724X_PCI_INTR_DEV0) { irq = AR71XX_PCI_IRQ_START; event = sc->sc_eventstab[irq]; if (!event || TAILQ_EMPTY(&event->ie_handlers)) { printf("Stray IRQ %d\n", irq); return (FILTER_STRAY); } + + /* Flush pending memory transactions */ + ar71xx_device_flush_ddr(AR71XX_CPU_DDR_FLUSH_PCIE); /* TODO: frame instead of NULL? */ intr_event_handle(event, NULL); mips_intrcnt_inc(sc->sc_intr_counter[irq]); } return (FILTER_HANDLED); } static int ar724x_pci_maxslots(device_t dev) { return (PCI_SLOTMAX); } static int ar724x_pci_route_interrupt(device_t pcib, device_t device, int pin) { return (pci_get_slot(device)); } static device_method_t ar724x_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ar724x_pci_probe), DEVMETHOD(device_attach, ar724x_pci_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_read_ivar, ar724x_pci_read_ivar), DEVMETHOD(bus_write_ivar, ar724x_pci_write_ivar), DEVMETHOD(bus_alloc_resource, ar724x_pci_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, ar724x_pci_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, ar724x_pci_setup_intr), DEVMETHOD(bus_teardown_intr, ar724x_pci_teardown_intr), /* pcib interface */ DEVMETHOD(pcib_maxslots, ar724x_pci_maxslots), DEVMETHOD(pcib_read_config, ar724x_pci_read_config), DEVMETHOD(pcib_write_config, ar724x_pci_write_config), DEVMETHOD(pcib_route_interrupt, ar724x_pci_route_interrupt), DEVMETHOD_END }; static driver_t ar724x_pci_driver = { "pcib", ar724x_pci_methods, sizeof(struct ar71xx_pci_softc), }; static devclass_t ar724x_pci_devclass; DRIVER_MODULE(ar724x_pci, nexus, ar724x_pci_driver, ar724x_pci_devclass, 0, 0); Index: head/sys/mips/atheros/ar91xx_chip.c =================================================================== --- head/sys/mips/atheros/ar91xx_chip.c (revision 285120) +++ head/sys/mips/atheros/ar91xx_chip.c (revision 285121) @@ -1,223 +1,219 @@ /*- * Copyright (c) 2010 Adrian Chadd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void ar91xx_chip_detect_mem_size(void) { } static void ar91xx_chip_detect_sys_frequency(void) { uint32_t pll; uint32_t freq; uint32_t div; u_ar71xx_mdio_freq = u_ar71xx_refclk = AR91XX_BASE_FREQ; pll = ATH_READ_REG(AR91XX_PLL_REG_CPU_CONFIG); div = ((pll >> AR91XX_PLL_DIV_SHIFT) & AR91XX_PLL_DIV_MASK); freq = div * AR91XX_BASE_FREQ; u_ar71xx_cpu_freq = freq; div = ((pll >> AR91XX_DDR_DIV_SHIFT) & AR91XX_DDR_DIV_MASK) + 1; u_ar71xx_ddr_freq = freq / div; div = (((pll >> AR91XX_AHB_DIV_SHIFT) & AR91XX_AHB_DIV_MASK) + 1) * 2; u_ar71xx_ahb_freq = u_ar71xx_cpu_freq / div; u_ar71xx_uart_freq = u_ar71xx_cpu_freq / div; u_ar71xx_wdt_freq = u_ar71xx_cpu_freq / div; } static void ar91xx_chip_device_stop(uint32_t mask) { uint32_t reg; reg = ATH_READ_REG(AR91XX_RESET_REG_RESET_MODULE); ATH_WRITE_REG(AR91XX_RESET_REG_RESET_MODULE, reg | mask); } static void ar91xx_chip_device_start(uint32_t mask) { uint32_t reg; reg = ATH_READ_REG(AR91XX_RESET_REG_RESET_MODULE); ATH_WRITE_REG(AR91XX_RESET_REG_RESET_MODULE, reg & ~mask); } static int ar91xx_chip_device_stopped(uint32_t mask) { uint32_t reg; reg = ATH_READ_REG(AR91XX_RESET_REG_RESET_MODULE); return ((reg & mask) == mask); } static void ar91xx_chip_set_pll_ge(int unit, int speed, uint32_t pll) { switch (unit) { case 0: ar71xx_write_pll(AR91XX_PLL_REG_ETH_CONFIG, AR91XX_PLL_REG_ETH0_INT_CLOCK, pll, AR91XX_ETH0_PLL_SHIFT); break; case 1: ar71xx_write_pll(AR91XX_PLL_REG_ETH_CONFIG, AR91XX_PLL_REG_ETH1_INT_CLOCK, pll, AR91XX_ETH1_PLL_SHIFT); break; default: printf("%s: invalid PLL set for arge unit: %d\n", __func__, unit); return; } } static void -ar91xx_chip_ddr_flush_ge(int unit) +ar91xx_chip_ddr_flush(ar71xx_flush_ddr_id_t id) { - switch (unit) { - case 0: + switch (id) { + case AR71XX_CPU_DDR_FLUSH_GE0: ar71xx_ddr_flush(AR91XX_DDR_REG_FLUSH_GE0); break; - case 1: + case AR71XX_CPU_DDR_FLUSH_GE1: ar71xx_ddr_flush(AR91XX_DDR_REG_FLUSH_GE1); break; + case AR71XX_CPU_DDR_FLUSH_USB: + ar71xx_ddr_flush(AR91XX_DDR_REG_FLUSH_USB); + break; + case AR71XX_CPU_DDR_FLUSH_WMAC: + ar71xx_ddr_flush(AR91XX_DDR_REG_FLUSH_WMAC); + break; default: - printf("%s: invalid DDR flush for arge unit: %d\n", - __func__, unit); - return; + printf("%s: invalid DDR flush id (%d)\n", __func__, id); + break; } } -static void -ar91xx_chip_ddr_flush_ip2(void) -{ - - ar71xx_ddr_flush(AR91XX_DDR_REG_FLUSH_WMAC); -} - - static uint32_t ar91xx_chip_get_eth_pll(unsigned int mac, int speed) { uint32_t pll; switch(speed) { case 10: pll = AR91XX_PLL_VAL_10; break; case 100: pll = AR91XX_PLL_VAL_100; break; case 1000: pll = AR91XX_PLL_VAL_1000; break; default: printf("%s%d: invalid speed %d\n", __func__, mac, speed); pll = 0; } return (pll); } static void ar91xx_chip_init_usb_peripheral(void) { ar71xx_device_stop(AR91XX_RST_RESET_MODULE_USBSUS_OVERRIDE); DELAY(100); ar71xx_device_start(RST_RESET_USB_HOST); DELAY(100); ar71xx_device_start(RST_RESET_USB_PHY); DELAY(100); /* Wireless */ ar71xx_device_stop(AR91XX_RST_RESET_MODULE_AMBA2WMAC); DELAY(1000); ar71xx_device_start(AR91XX_RST_RESET_MODULE_AMBA2WMAC); DELAY(1000); } struct ar71xx_cpu_def ar91xx_chip_def = { &ar91xx_chip_detect_mem_size, &ar91xx_chip_detect_sys_frequency, &ar91xx_chip_device_stop, &ar91xx_chip_device_start, &ar91xx_chip_device_stopped, &ar91xx_chip_set_pll_ge, &ar71xx_chip_set_mii_speed, &ar71xx_chip_set_mii_if, - &ar91xx_chip_ddr_flush_ge, &ar91xx_chip_get_eth_pll, - &ar91xx_chip_ddr_flush_ip2, + &ar91xx_chip_ddr_flush, &ar91xx_chip_init_usb_peripheral, }; Index: head/sys/mips/atheros/ar933x_chip.c =================================================================== --- head/sys/mips/atheros/ar933x_chip.c (revision 285120) +++ head/sys/mips/atheros/ar933x_chip.c (revision 285121) @@ -1,358 +1,356 @@ /*- * Copyright (c) 2012 Adrian Chadd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void ar933x_chip_detect_mem_size(void) { } static void ar933x_chip_detect_sys_frequency(void) { uint32_t clock_ctrl; uint32_t cpu_config; uint32_t freq; uint32_t t; t = ATH_READ_REG(AR933X_RESET_REG_BOOTSTRAP); if (t & AR933X_BOOTSTRAP_REF_CLK_40) u_ar71xx_refclk = (40 * 1000 * 1000); else u_ar71xx_refclk = (25 * 1000 * 1000); clock_ctrl = ATH_READ_REG(AR933X_PLL_CLOCK_CTRL_REG); if (clock_ctrl & AR933X_PLL_CLOCK_CTRL_BYPASS) { u_ar71xx_cpu_freq = u_ar71xx_refclk; u_ar71xx_ahb_freq = u_ar71xx_refclk; u_ar71xx_ddr_freq = u_ar71xx_refclk; } else { cpu_config = ATH_READ_REG(AR933X_PLL_CPU_CONFIG_REG); t = (cpu_config >> AR933X_PLL_CPU_CONFIG_REFDIV_SHIFT) & AR933X_PLL_CPU_CONFIG_REFDIV_MASK; freq = u_ar71xx_refclk / t; t = (cpu_config >> AR933X_PLL_CPU_CONFIG_NINT_SHIFT) & AR933X_PLL_CPU_CONFIG_NINT_MASK; freq *= t; t = (cpu_config >> AR933X_PLL_CPU_CONFIG_OUTDIV_SHIFT) & AR933X_PLL_CPU_CONFIG_OUTDIV_MASK; if (t == 0) t = 1; freq >>= t; t = ((clock_ctrl >> AR933X_PLL_CLOCK_CTRL_CPU_DIV_SHIFT) & AR933X_PLL_CLOCK_CTRL_CPU_DIV_MASK) + 1; u_ar71xx_cpu_freq = freq / t; t = ((clock_ctrl >> AR933X_PLL_CLOCK_CTRL_DDR_DIV_SHIFT) & AR933X_PLL_CLOCK_CTRL_DDR_DIV_MASK) + 1; u_ar71xx_ddr_freq = freq / t; t = ((clock_ctrl >> AR933X_PLL_CLOCK_CTRL_AHB_DIV_SHIFT) & AR933X_PLL_CLOCK_CTRL_AHB_DIV_MASK) + 1; u_ar71xx_ahb_freq = freq / t; } /* * On the AR933x, the UART frequency is the reference clock, * not the AHB bus clock. */ u_ar71xx_uart_freq = u_ar71xx_refclk; /* * XXX TODO: check whether the mdio frequency is always the * refclock frequency, or whether it's variable like on the * AR934x. */ u_ar71xx_mdio_freq = u_ar71xx_refclk; /* * XXX check what the watchdog frequency should be? */ u_ar71xx_wdt_freq = u_ar71xx_ahb_freq; } static void ar933x_chip_device_stop(uint32_t mask) { uint32_t reg; reg = ATH_READ_REG(AR933X_RESET_REG_RESET_MODULE); ATH_WRITE_REG(AR933X_RESET_REG_RESET_MODULE, reg | mask); } static void ar933x_chip_device_start(uint32_t mask) { uint32_t reg; reg = ATH_READ_REG(AR933X_RESET_REG_RESET_MODULE); ATH_WRITE_REG(AR933X_RESET_REG_RESET_MODULE, reg & ~mask); } static int ar933x_chip_device_stopped(uint32_t mask) { uint32_t reg; reg = ATH_READ_REG(AR933X_RESET_REG_RESET_MODULE); return ((reg & mask) == mask); } static void ar933x_chip_set_mii_speed(uint32_t unit, uint32_t speed) { /* XXX TODO */ return; } /* * XXX TODO !! */ static void ar933x_chip_set_pll_ge(int unit, int speed, uint32_t pll) { switch (unit) { case 0: /* XXX TODO */ break; case 1: /* XXX TODO */ break; default: printf("%s: invalid PLL set for arge unit: %d\n", __func__, unit); return; } } static void -ar933x_chip_ddr_flush_ge(int unit) +ar933x_chip_ddr_flush(ar71xx_flush_ddr_id_t id) { - switch (unit) { - case 0: + switch (id) { + case AR71XX_CPU_DDR_FLUSH_GE0: ar71xx_ddr_flush(AR933X_DDR_REG_FLUSH_GE0); break; - case 1: + case AR71XX_CPU_DDR_FLUSH_GE1: ar71xx_ddr_flush(AR933X_DDR_REG_FLUSH_GE1); break; + case AR71XX_CPU_DDR_FLUSH_USB: + ar71xx_ddr_flush(AR933X_DDR_REG_FLUSH_USB); + break; + case AR71XX_CPU_DDR_FLUSH_WMAC: + ar71xx_ddr_flush(AR933X_DDR_REG_FLUSH_WMAC); + break; default: - printf("%s: invalid DDR flush for arge unit: %d\n", - __func__, unit); - return; + printf("%s: invalid DDR flush id (%d)\n", __func__, id); + break; } } -static void -ar933x_chip_ddr_flush_ip2(void) -{ - ar71xx_ddr_flush(AR933X_DDR_REG_FLUSH_WMAC); -} - static uint32_t ar933x_chip_get_eth_pll(unsigned int mac, int speed) { uint32_t pll; switch (speed) { case 10: pll = AR933X_PLL_VAL_10; break; case 100: pll = AR933X_PLL_VAL_100; break; case 1000: pll = AR933X_PLL_VAL_1000; break; default: printf("%s%d: invalid speed %d\n", __func__, mac, speed); pll = 0; } return (pll); } static void ar933x_chip_init_usb_peripheral(void) { ar71xx_device_stop(AR933X_RESET_USBSUS_OVERRIDE); DELAY(100); ar71xx_device_start(AR933X_RESET_USB_HOST); DELAY(100); ar71xx_device_start(AR933X_RESET_USB_PHY); DELAY(100); } static void ar933x_configure_gmac(uint32_t gmac_cfg) { uint32_t reg; reg = ATH_READ_REG(AR933X_GMAC_REG_ETH_CFG); /* * The relevant bits here include: * * + AR933X_ETH_CFG_SW_PHY_SWAP * + AR933X_ETH_CFG_SW_PHY_ADDR_SWAP * * There are other things; look at what openwrt exposes so * it can be correctly exposed. * * TODO: what about ethernet switch support? How's that work? */ if (bootverbose) printf("%s: GMAC config was 0x%08x\n", __func__, reg); reg &= ~(AR933X_ETH_CFG_SW_PHY_SWAP | AR933X_ETH_CFG_SW_PHY_ADDR_SWAP); reg |= gmac_cfg; if (bootverbose) printf("%s: GMAC setting is 0x%08x; register is now 0x%08x\n", __func__, gmac_cfg, reg); ATH_WRITE_REG(AR933X_GMAC_REG_ETH_CFG, reg); } static void ar933x_chip_init_gmac(void) { int val; uint32_t gmac_cfg = 0; /* * These two bits need a bit better explanation. * * The default configuration in the hardware is to map both * ports to the internal switch. * * Here, GE0 == arge0, GE1 == arge1. * * The internal switch has: * + 5 MAC ports, MAC0->MAC4. * + 5 PHY ports, PHY0->PHY4, * + MAC0 connects to GE1; * + GE0 connects to PHY4; * + The other mappings are MAC1->PHY0, MAC2->PHY1 .. MAC4->PHY3. * * The GE1 port is linked in via 1000MBit/full, supplying what is * normally the 'WAN' switch ports. * * The switch is connected the MDIO bus on GE1. It looks like * a normal AR7240 on-board switch. * * The GE0 port is connected via MII to PHY4, and can operate in * 10/100mbit, full/half duplex. Ie, you can speak to PHY4 on * the MDIO bus and everything will simply 'work'. * * So far so good. This looks just like an AR7240 SoC. * * However, some configurations will just expose one or two * physical ports. In this case, some configuration bits can * be set to tweak this. * * + CFG_SW_PHY_ADDR_SWAP swaps PHY port 0 with PHY port 4. * Ie, GE0's PHY shows up as PHY 0. So if there's only * one physical port, there's no need to involve the * switch framework - it can just show up as a default, * normal single PHY. * * + CFG_SW_PHY_SWAP swaps the internal switch connection * between PHY0 and PHY4. Ie, PHY4 connects to MAc1, * PHY0 connects to GE0. */ if ((resource_int_value("ar933x_gmac", 0, "override_phy", &val) == 0) && (val == 0)) return; if ((resource_int_value("ar933x_gmac", 0, "swap_phy", &val) == 0) && (val == 1)) gmac_cfg |= AR933X_ETH_CFG_SW_PHY_SWAP; if ((resource_int_value("ar933x_gmac", 0, "swap_phy_addr", &val) == 0) && (val == 1)) gmac_cfg |= AR933X_ETH_CFG_SW_PHY_ADDR_SWAP; ar933x_configure_gmac(gmac_cfg); } struct ar71xx_cpu_def ar933x_chip_def = { &ar933x_chip_detect_mem_size, &ar933x_chip_detect_sys_frequency, &ar933x_chip_device_stop, &ar933x_chip_device_start, &ar933x_chip_device_stopped, &ar933x_chip_set_pll_ge, &ar933x_chip_set_mii_speed, &ar71xx_chip_set_mii_if, - &ar933x_chip_ddr_flush_ge, &ar933x_chip_get_eth_pll, - &ar933x_chip_ddr_flush_ip2, + &ar933x_chip_ddr_flush, &ar933x_chip_init_usb_peripheral, NULL, NULL, &ar933x_chip_init_gmac, }; Index: head/sys/mips/atheros/ar934x_chip.c =================================================================== --- head/sys/mips/atheros/ar934x_chip.c (revision 285120) +++ head/sys/mips/atheros/ar934x_chip.c (revision 285121) @@ -1,469 +1,470 @@ /*- * Copyright (c) 2013 Adrian Chadd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void ar934x_chip_detect_mem_size(void) { } static uint32_t ar934x_get_pll_freq(uint32_t ref, uint32_t ref_div, uint32_t nint, uint32_t nfrac, uint32_t frac, uint32_t out_div) { uint64_t t; uint32_t ret; t = u_ar71xx_refclk; t *= nint; t = t / ref_div; ret = t; t = u_ar71xx_refclk; t *= nfrac; t = t / (ref_div * frac); ret += t; ret /= (1 << out_div); return (ret); } static void ar934x_chip_detect_sys_frequency(void) { uint32_t pll, out_div, ref_div, nint, nfrac, frac, clk_ctrl, postdiv; uint32_t cpu_pll, ddr_pll; uint32_t bootstrap; uint32_t reg; bootstrap = ATH_READ_REG(AR934X_RESET_REG_BOOTSTRAP); if (bootstrap & AR934X_BOOTSTRAP_REF_CLK_40) u_ar71xx_refclk = 40 * 1000 * 1000; else u_ar71xx_refclk = 25 * 1000 * 1000; pll = ATH_READ_REG(AR934X_SRIF_CPU_DPLL2_REG); if (pll & AR934X_SRIF_DPLL2_LOCAL_PLL) { out_div = (pll >> AR934X_SRIF_DPLL2_OUTDIV_SHIFT) & AR934X_SRIF_DPLL2_OUTDIV_MASK; pll = ATH_READ_REG(AR934X_SRIF_CPU_DPLL1_REG); nint = (pll >> AR934X_SRIF_DPLL1_NINT_SHIFT) & AR934X_SRIF_DPLL1_NINT_MASK; nfrac = pll & AR934X_SRIF_DPLL1_NFRAC_MASK; ref_div = (pll >> AR934X_SRIF_DPLL1_REFDIV_SHIFT) & AR934X_SRIF_DPLL1_REFDIV_MASK; frac = 1 << 18; } else { pll = ATH_READ_REG(AR934X_PLL_CPU_CONFIG_REG); out_div = (pll >> AR934X_PLL_CPU_CONFIG_OUTDIV_SHIFT) & AR934X_PLL_CPU_CONFIG_OUTDIV_MASK; ref_div = (pll >> AR934X_PLL_CPU_CONFIG_REFDIV_SHIFT) & AR934X_PLL_CPU_CONFIG_REFDIV_MASK; nint = (pll >> AR934X_PLL_CPU_CONFIG_NINT_SHIFT) & AR934X_PLL_CPU_CONFIG_NINT_MASK; nfrac = (pll >> AR934X_PLL_CPU_CONFIG_NFRAC_SHIFT) & AR934X_PLL_CPU_CONFIG_NFRAC_MASK; frac = 1 << 6; } cpu_pll = ar934x_get_pll_freq(u_ar71xx_refclk, ref_div, nint, nfrac, frac, out_div); pll = ATH_READ_REG(AR934X_SRIF_DDR_DPLL2_REG); if (pll & AR934X_SRIF_DPLL2_LOCAL_PLL) { out_div = (pll >> AR934X_SRIF_DPLL2_OUTDIV_SHIFT) & AR934X_SRIF_DPLL2_OUTDIV_MASK; pll = ATH_READ_REG(AR934X_SRIF_DDR_DPLL1_REG); nint = (pll >> AR934X_SRIF_DPLL1_NINT_SHIFT) & AR934X_SRIF_DPLL1_NINT_MASK; nfrac = pll & AR934X_SRIF_DPLL1_NFRAC_MASK; ref_div = (pll >> AR934X_SRIF_DPLL1_REFDIV_SHIFT) & AR934X_SRIF_DPLL1_REFDIV_MASK; frac = 1 << 18; } else { pll = ATH_READ_REG(AR934X_PLL_DDR_CONFIG_REG); out_div = (pll >> AR934X_PLL_DDR_CONFIG_OUTDIV_SHIFT) & AR934X_PLL_DDR_CONFIG_OUTDIV_MASK; ref_div = (pll >> AR934X_PLL_DDR_CONFIG_REFDIV_SHIFT) & AR934X_PLL_DDR_CONFIG_REFDIV_MASK; nint = (pll >> AR934X_PLL_DDR_CONFIG_NINT_SHIFT) & AR934X_PLL_DDR_CONFIG_NINT_MASK; nfrac = (pll >> AR934X_PLL_DDR_CONFIG_NFRAC_SHIFT) & AR934X_PLL_DDR_CONFIG_NFRAC_MASK; frac = 1 << 10; } ddr_pll = ar934x_get_pll_freq(u_ar71xx_refclk, ref_div, nint, nfrac, frac, out_div); clk_ctrl = ATH_READ_REG(AR934X_PLL_CPU_DDR_CLK_CTRL_REG); postdiv = (clk_ctrl >> AR934X_PLL_CPU_DDR_CLK_CTRL_CPU_POST_DIV_SHIFT) & AR934X_PLL_CPU_DDR_CLK_CTRL_CPU_POST_DIV_MASK; if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_CPU_PLL_BYPASS) u_ar71xx_cpu_freq = u_ar71xx_refclk; else if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_CPUCLK_FROM_CPUPLL) u_ar71xx_cpu_freq = cpu_pll / (postdiv + 1); else u_ar71xx_cpu_freq = ddr_pll / (postdiv + 1); postdiv = (clk_ctrl >> AR934X_PLL_CPU_DDR_CLK_CTRL_DDR_POST_DIV_SHIFT) & AR934X_PLL_CPU_DDR_CLK_CTRL_DDR_POST_DIV_MASK; if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_DDR_PLL_BYPASS) u_ar71xx_ddr_freq = u_ar71xx_refclk; else if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_DDRCLK_FROM_DDRPLL) u_ar71xx_ddr_freq = ddr_pll / (postdiv + 1); else u_ar71xx_ddr_freq = cpu_pll / (postdiv + 1); postdiv = (clk_ctrl >> AR934X_PLL_CPU_DDR_CLK_CTRL_AHB_POST_DIV_SHIFT) & AR934X_PLL_CPU_DDR_CLK_CTRL_AHB_POST_DIV_MASK; if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_AHB_PLL_BYPASS) u_ar71xx_ahb_freq = u_ar71xx_refclk; else if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_AHBCLK_FROM_DDRPLL) u_ar71xx_ahb_freq = ddr_pll / (postdiv + 1); else u_ar71xx_ahb_freq = cpu_pll / (postdiv + 1); u_ar71xx_wdt_freq = u_ar71xx_refclk; u_ar71xx_uart_freq = u_ar71xx_refclk; /* * Next, fetch reference clock speed for MDIO bus. */ reg = ATH_READ_REG(AR934X_PLL_SWITCH_CLOCK_CONTROL_REG); if (reg & AR934X_PLL_SWITCH_CLOCK_CONTROL_MDIO_CLK_SEL) { printf("%s: mdio=100MHz\n", __func__); u_ar71xx_mdio_freq = (100 * 1000 * 1000); } else { printf("%s: mdio=%d Hz\n", __func__, u_ar71xx_refclk); u_ar71xx_mdio_freq = u_ar71xx_refclk; } } static void ar934x_chip_device_stop(uint32_t mask) { uint32_t reg; reg = ATH_READ_REG(AR934X_RESET_REG_RESET_MODULE); ATH_WRITE_REG(AR934X_RESET_REG_RESET_MODULE, reg | mask); } static void ar934x_chip_device_start(uint32_t mask) { uint32_t reg; reg = ATH_READ_REG(AR934X_RESET_REG_RESET_MODULE); ATH_WRITE_REG(AR934X_RESET_REG_RESET_MODULE, reg & ~mask); } static int ar934x_chip_device_stopped(uint32_t mask) { uint32_t reg; reg = ATH_READ_REG(AR934X_RESET_REG_RESET_MODULE); return ((reg & mask) == mask); } static void ar934x_chip_set_mii_speed(uint32_t unit, uint32_t speed) { /* XXX TODO */ return; } /* * XXX TODO !! */ static void ar934x_chip_set_pll_ge(int unit, int speed, uint32_t pll) { switch (unit) { case 0: ATH_WRITE_REG(AR934X_PLL_ETH_XMII_CONTROL_REG, pll); break; case 1: /* XXX nothing */ break; default: printf("%s: invalid PLL set for arge unit: %d\n", __func__, unit); return; } } static void -ar934x_chip_ddr_flush_ge(int unit) +ar934x_chip_ddr_flush(ar71xx_flush_ddr_id_t id) { - switch (unit) { - case 0: + switch (id) { + case AR71XX_CPU_DDR_FLUSH_GE0: ar71xx_ddr_flush(AR934X_DDR_REG_FLUSH_GE0); break; - case 1: + case AR71XX_CPU_DDR_FLUSH_GE1: ar71xx_ddr_flush(AR934X_DDR_REG_FLUSH_GE1); break; + case AR71XX_CPU_DDR_FLUSH_USB: + ar71xx_ddr_flush(AR934X_DDR_REG_FLUSH_USB); + break; + case AR71XX_CPU_DDR_FLUSH_PCIE: + ar71xx_ddr_flush(AR934X_DDR_REG_FLUSH_PCIE); + break; + case AR71XX_CPU_DDR_FLUSH_WMAC: + ar71xx_ddr_flush(AR934X_DDR_REG_FLUSH_WMAC); + break; default: - printf("%s: invalid DDR flush for arge unit: %d\n", - __func__, unit); - return; + printf("%s: invalid DDR flush id (%d)\n", __func__, id); + break; } } -static void -ar934x_chip_ddr_flush_ip2(void) -{ - ar71xx_ddr_flush(AR934X_DDR_REG_FLUSH_WMAC); -} - static uint32_t ar934x_chip_get_eth_pll(unsigned int mac, int speed) { uint32_t pll; switch (speed) { case 10: pll = AR934X_PLL_VAL_10; break; case 100: pll = AR934X_PLL_VAL_100; break; case 1000: pll = AR934X_PLL_VAL_1000; break; default: printf("%s%d: invalid speed %d\n", __func__, mac, speed); pll = 0; } return (pll); } static void ar934x_chip_reset_ethernet_switch(void) { ar71xx_device_stop(AR934X_RESET_ETH_SWITCH); DELAY(100); ar71xx_device_start(AR934X_RESET_ETH_SWITCH); DELAY(100); } static void ar934x_configure_gmac(uint32_t gmac_cfg) { uint32_t reg; reg = ATH_READ_REG(AR934X_GMAC_REG_ETH_CFG); printf("%s: ETH_CFG=0x%08x\n", __func__, reg); reg &= ~(AR934X_ETH_CFG_RGMII_GMAC0 | AR934X_ETH_CFG_MII_GMAC0 | AR934X_ETH_CFG_MII_GMAC0 | AR934X_ETH_CFG_SW_ONLY_MODE | AR934X_ETH_CFG_SW_PHY_SWAP); reg |= gmac_cfg; ATH_WRITE_REG(AR934X_GMAC_REG_ETH_CFG, reg); } static void ar934x_chip_init_usb_peripheral(void) { uint32_t reg; reg = ATH_READ_REG(AR934X_RESET_REG_BOOTSTRAP); if (reg & AR934X_BOOTSTRAP_USB_MODE_DEVICE) return; ar71xx_device_stop(AR934X_RESET_USBSUS_OVERRIDE); DELAY(100); ar71xx_device_start(AR934X_RESET_USB_PHY); DELAY(100); ar71xx_device_start(AR934X_RESET_USB_PHY_ANALOG); DELAY(100); ar71xx_device_start(AR934X_RESET_USB_HOST); DELAY(100); } static void ar934x_chip_set_mii_if(uint32_t unit, uint32_t mii_mode) { /* * XXX ! * * Nothing to see here; although gmac0 can have its * MII configuration changed, the register values * are slightly different. */ } /* * XXX TODO: fetch default MII divider configuration */ static void ar934x_chip_reset_wmac(void) { /* XXX TODO */ } static void ar934x_chip_init_gmac(void) { long gmac_cfg; if (resource_long_value("ar934x_gmac", 0, "gmac_cfg", &gmac_cfg) == 0) { printf("%s: gmac_cfg=0x%08lx\n", __func__, (long) gmac_cfg); ar934x_configure_gmac((uint32_t) gmac_cfg); } } /* * Reset the NAND Flash Controller. * * + active=1 means "make it active". * + active=0 means "make it inactive". */ static void ar934x_chip_reset_nfc(int active) { if (active) { ar71xx_device_start(AR934X_RESET_NANDF); DELAY(100); ar71xx_device_start(AR934X_RESET_ETH_SWITCH_ANALOG); DELAY(250); } else { ar71xx_device_stop(AR934X_RESET_ETH_SWITCH_ANALOG); DELAY(250); ar71xx_device_stop(AR934X_RESET_NANDF); DELAY(100); } } /* * Configure the GPIO output mux setup. * * The AR934x introduced an output mux which allowed * certain functions to be configured on any pin. * Specifically, the switch PHY link LEDs and * WMAC external RX LNA switches are not limited to * a specific GPIO pin. */ static void ar934x_chip_gpio_output_configure(int gpio, uint8_t func) { uint32_t reg, s; uint32_t t; if (gpio > AR934X_GPIO_COUNT) return; reg = AR934X_GPIO_REG_OUT_FUNC0 + 4 * (gpio / 4); s = 8 * (gpio % 4); /* read-modify-write */ t = ATH_READ_REG(AR71XX_GPIO_BASE + reg); t &= ~(0xff << s); t |= func << s; ATH_WRITE_REG(AR71XX_GPIO_BASE + reg, t); /* flush write */ ATH_READ_REG(AR71XX_GPIO_BASE + reg); } struct ar71xx_cpu_def ar934x_chip_def = { &ar934x_chip_detect_mem_size, &ar934x_chip_detect_sys_frequency, &ar934x_chip_device_stop, &ar934x_chip_device_start, &ar934x_chip_device_stopped, &ar934x_chip_set_pll_ge, &ar934x_chip_set_mii_speed, &ar934x_chip_set_mii_if, - &ar934x_chip_ddr_flush_ge, &ar934x_chip_get_eth_pll, - &ar934x_chip_ddr_flush_ip2, + &ar934x_chip_ddr_flush, &ar934x_chip_init_usb_peripheral, &ar934x_chip_reset_ethernet_switch, &ar934x_chip_reset_wmac, &ar934x_chip_init_gmac, &ar934x_chip_reset_nfc, &ar934x_chip_gpio_output_configure, }; Index: head/sys/mips/atheros/if_arge.c =================================================================== --- head/sys/mips/atheros/if_arge.c (revision 285120) +++ head/sys/mips/atheros/if_arge.c (revision 285121) @@ -1,2516 +1,2530 @@ /*- * Copyright (c) 2009, Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * AR71XX gigabit ethernet driver */ #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" #endif #include "opt_arge.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "opt_arge.h" #if defined(ARGE_MDIO) #include #include #include "mdio_if.h" #endif MODULE_DEPEND(arge, ether, 1, 1, 1); MODULE_DEPEND(arge, miibus, 1, 1, 1); MODULE_VERSION(arge, 1); #include "miibus_if.h" #include #include #include /* XXX tsk! */ #include /* XXX tsk! */ #include #include #include #include typedef enum { ARGE_DBG_MII = 0x00000001, ARGE_DBG_INTR = 0x00000002, ARGE_DBG_TX = 0x00000004, ARGE_DBG_RX = 0x00000008, ARGE_DBG_ERR = 0x00000010, ARGE_DBG_RESET = 0x00000020, ARGE_DBG_PLL = 0x00000040, } arge_debug_flags; static const char * arge_miicfg_str[] = { "NONE", "GMII", "MII", "RGMII", "RMII", "SGMII" }; #ifdef ARGE_DEBUG #define ARGEDEBUG(_sc, _m, ...) \ do { \ if ((_m) & (_sc)->arge_debug) \ device_printf((_sc)->arge_dev, __VA_ARGS__); \ } while (0) #else #define ARGEDEBUG(_sc, _m, ...) #endif static int arge_attach(device_t); static int arge_detach(device_t); static void arge_flush_ddr(struct arge_softc *); static int arge_ifmedia_upd(struct ifnet *); static void arge_ifmedia_sts(struct ifnet *, struct ifmediareq *); static int arge_ioctl(struct ifnet *, u_long, caddr_t); static void arge_init(void *); static void arge_init_locked(struct arge_softc *); static void arge_link_task(void *, int); static void arge_update_link_locked(struct arge_softc *sc); static void arge_set_pll(struct arge_softc *, int, int); static int arge_miibus_readreg(device_t, int, int); static void arge_miibus_statchg(device_t); static int arge_miibus_writereg(device_t, int, int, int); static int arge_probe(device_t); static void arge_reset_dma(struct arge_softc *); static int arge_resume(device_t); static int arge_rx_ring_init(struct arge_softc *); static void arge_rx_ring_free(struct arge_softc *sc); static int arge_tx_ring_init(struct arge_softc *); static void arge_tx_ring_free(struct arge_softc *); #ifdef DEVICE_POLLING static int arge_poll(struct ifnet *, enum poll_cmd, int); #endif static int arge_shutdown(device_t); static void arge_start(struct ifnet *); static void arge_start_locked(struct ifnet *); static void arge_stop(struct arge_softc *); static int arge_suspend(device_t); static int arge_rx_locked(struct arge_softc *); static void arge_tx_locked(struct arge_softc *); static void arge_intr(void *); static int arge_intr_filter(void *); static void arge_tick(void *); static void arge_hinted_child(device_t bus, const char *dname, int dunit); /* * ifmedia callbacks for multiPHY MAC */ void arge_multiphy_mediastatus(struct ifnet *, struct ifmediareq *); int arge_multiphy_mediachange(struct ifnet *); static void arge_dmamap_cb(void *, bus_dma_segment_t *, int, int); static int arge_dma_alloc(struct arge_softc *); static void arge_dma_free(struct arge_softc *); static int arge_newbuf(struct arge_softc *, int); static __inline void arge_fixup_rx(struct mbuf *); static device_method_t arge_methods[] = { /* Device interface */ DEVMETHOD(device_probe, arge_probe), DEVMETHOD(device_attach, arge_attach), DEVMETHOD(device_detach, arge_detach), DEVMETHOD(device_suspend, arge_suspend), DEVMETHOD(device_resume, arge_resume), DEVMETHOD(device_shutdown, arge_shutdown), /* MII interface */ DEVMETHOD(miibus_readreg, arge_miibus_readreg), DEVMETHOD(miibus_writereg, arge_miibus_writereg), DEVMETHOD(miibus_statchg, arge_miibus_statchg), /* bus interface */ DEVMETHOD(bus_add_child, device_add_child_ordered), DEVMETHOD(bus_hinted_child, arge_hinted_child), DEVMETHOD_END }; static driver_t arge_driver = { "arge", arge_methods, sizeof(struct arge_softc) }; static devclass_t arge_devclass; DRIVER_MODULE(arge, nexus, arge_driver, arge_devclass, 0, 0); DRIVER_MODULE(miibus, arge, miibus_driver, miibus_devclass, 0, 0); #if defined(ARGE_MDIO) static int argemdio_probe(device_t); static int argemdio_attach(device_t); static int argemdio_detach(device_t); /* * Declare an additional, separate driver for accessing the MDIO bus. */ static device_method_t argemdio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, argemdio_probe), DEVMETHOD(device_attach, argemdio_attach), DEVMETHOD(device_detach, argemdio_detach), /* bus interface */ DEVMETHOD(bus_add_child, device_add_child_ordered), /* MDIO access */ DEVMETHOD(mdio_readreg, arge_miibus_readreg), DEVMETHOD(mdio_writereg, arge_miibus_writereg), }; DEFINE_CLASS_0(argemdio, argemdio_driver, argemdio_methods, sizeof(struct arge_softc)); static devclass_t argemdio_devclass; DRIVER_MODULE(miiproxy, arge, miiproxy_driver, miiproxy_devclass, 0, 0); DRIVER_MODULE(argemdio, nexus, argemdio_driver, argemdio_devclass, 0, 0); DRIVER_MODULE(mdio, argemdio, mdio_driver, mdio_devclass, 0, 0); #endif static struct mtx miibus_mtx; MTX_SYSINIT(miibus_mtx, &miibus_mtx, "arge mii lock", MTX_DEF); /* * Flushes all + * + * XXX this needs to be done at interrupt time! Grr! */ static void arge_flush_ddr(struct arge_softc *sc) { - - ar71xx_device_flush_ddr_ge(sc->arge_mac_unit); + switch (sc->arge_mac_unit) { + case 0: + ar71xx_device_flush_ddr(AR71XX_CPU_DDR_FLUSH_GE0); + break; + case 1: + ar71xx_device_flush_ddr(AR71XX_CPU_DDR_FLUSH_GE1); + break; + default: + device_printf(sc->arge_dev, "%s: unknown unit (%d)\n", + __func__, + sc->arge_mac_unit); + break; + } } static int arge_probe(device_t dev) { device_set_desc(dev, "Atheros AR71xx built-in ethernet interface"); return (BUS_PROBE_NOWILDCARD); } static void arge_attach_sysctl(device_t dev) { struct arge_softc *sc = device_get_softc(dev); struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); struct sysctl_oid *tree = device_get_sysctl_tree(dev); #ifdef ARGE_DEBUG SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "debug", CTLFLAG_RW, &sc->arge_debug, 0, "arge interface debugging flags"); #endif SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_pkts_aligned", CTLFLAG_RW, &sc->stats.tx_pkts_aligned, 0, "number of TX aligned packets"); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_pkts_unaligned", CTLFLAG_RW, &sc->stats.tx_pkts_unaligned, 0, "number of TX unaligned packets"); #ifdef ARGE_DEBUG SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_prod", CTLFLAG_RW, &sc->arge_cdata.arge_tx_prod, 0, ""); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_cons", CTLFLAG_RW, &sc->arge_cdata.arge_tx_cons, 0, ""); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_cnt", CTLFLAG_RW, &sc->arge_cdata.arge_tx_cnt, 0, ""); #endif } static void arge_reset_mac(struct arge_softc *sc) { uint32_t reg; uint32_t reset_reg; ARGEDEBUG(sc, ARGE_DBG_RESET, "%s called\n", __func__); /* Step 1. Soft-reset MAC */ ARGE_SET_BITS(sc, AR71XX_MAC_CFG1, MAC_CFG1_SOFT_RESET); DELAY(20); /* Step 2. Punt the MAC core from the central reset register */ /* * XXX TODO: migrate this (and other) chip specific stuff into * a chipdef method. */ if (sc->arge_mac_unit == 0) { reset_reg = RST_RESET_GE0_MAC; } else { reset_reg = RST_RESET_GE1_MAC; } /* * AR934x (and later) also needs the MDIO block reset. * XXX should methodize this! */ if (ar71xx_soc == AR71XX_SOC_AR9341 || ar71xx_soc == AR71XX_SOC_AR9342 || ar71xx_soc == AR71XX_SOC_AR9344) { if (sc->arge_mac_unit == 0) { reset_reg |= AR934X_RESET_GE0_MDIO; } else { reset_reg |= AR934X_RESET_GE1_MDIO; } } if (ar71xx_soc == AR71XX_SOC_QCA9556 || ar71xx_soc == AR71XX_SOC_QCA9558) { if (sc->arge_mac_unit == 0) { reset_reg |= QCA955X_RESET_GE0_MDIO; } else { reset_reg |= QCA955X_RESET_GE1_MDIO; } } ar71xx_device_stop(reset_reg); DELAY(100); ar71xx_device_start(reset_reg); /* Step 3. Reconfigure MAC block */ ARGE_WRITE(sc, AR71XX_MAC_CFG1, MAC_CFG1_SYNC_RX | MAC_CFG1_RX_ENABLE | MAC_CFG1_SYNC_TX | MAC_CFG1_TX_ENABLE); reg = ARGE_READ(sc, AR71XX_MAC_CFG2); reg |= MAC_CFG2_ENABLE_PADCRC | MAC_CFG2_LENGTH_FIELD ; ARGE_WRITE(sc, AR71XX_MAC_CFG2, reg); ARGE_WRITE(sc, AR71XX_MAC_MAX_FRAME_LEN, 1536); } /* * These values map to the divisor values programmed into * AR71XX_MAC_MII_CFG. * * The index of each value corresponds to the divisor section * value in AR71XX_MAC_MII_CFG (ie, table[0] means '0' in * AR71XX_MAC_MII_CFG, table[1] means '1', etc.) */ static const uint32_t ar71xx_mdio_div_table[] = { 4, 4, 6, 8, 10, 14, 20, 28, }; static const uint32_t ar7240_mdio_div_table[] = { 2, 2, 4, 6, 8, 12, 18, 26, 32, 40, 48, 56, 62, 70, 78, 96, }; static const uint32_t ar933x_mdio_div_table[] = { 4, 4, 6, 8, 10, 14, 20, 28, 34, 42, 50, 58, 66, 74, 82, 98, }; /* * Lookup the divisor to use based on the given frequency. * * Returns the divisor to use, or -ve on error. */ static int arge_mdio_get_divider(struct arge_softc *sc, unsigned long mdio_clock) { unsigned long ref_clock, t; const uint32_t *table; int ndivs; int i; /* * This is the base MDIO frequency on the SoC. * The dividers .. well, divide. Duh. */ ref_clock = ar71xx_mdio_freq(); /* * If either clock is undefined, just tell the * caller to fall through to the defaults. */ if (ref_clock == 0 || mdio_clock == 0) return (-EINVAL); /* * Pick the correct table! */ switch (ar71xx_soc) { case AR71XX_SOC_AR9330: case AR71XX_SOC_AR9331: case AR71XX_SOC_AR9341: case AR71XX_SOC_AR9342: case AR71XX_SOC_AR9344: case AR71XX_SOC_QCA9556: case AR71XX_SOC_QCA9558: table = ar933x_mdio_div_table; ndivs = nitems(ar933x_mdio_div_table); break; case AR71XX_SOC_AR7240: case AR71XX_SOC_AR7241: case AR71XX_SOC_AR7242: table = ar7240_mdio_div_table; ndivs = nitems(ar7240_mdio_div_table); break; default: table = ar71xx_mdio_div_table; ndivs = nitems(ar71xx_mdio_div_table); } /* * Now, walk through the list and find the first divisor * that falls under the target MDIO frequency. * * The divisors go up, but the corresponding frequencies * are actually decreasing. */ for (i = 0; i < ndivs; i++) { t = ref_clock / table[i]; if (t <= mdio_clock) { return (i); } } ARGEDEBUG(sc, ARGE_DBG_RESET, "No divider found; MDIO=%lu Hz; target=%lu Hz\n", ref_clock, mdio_clock); return (-ENOENT); } /* * Fetch the MDIO bus clock rate. * * For now, the default is DIV_28 for everything * bar AR934x, which will be DIV_58. * * It will definitely need updating to take into account * the MDIO bus core clock rate and the target clock * rate for the chip. */ static uint32_t arge_fetch_mdiobus_clock_rate(struct arge_softc *sc) { int mdio_freq, div; /* * Is the MDIO frequency defined? If so, find a divisor that * makes reasonable sense. Don't overshoot the frequency. */ if (resource_int_value(device_get_name(sc->arge_dev), device_get_unit(sc->arge_dev), "mdio_freq", &mdio_freq) == 0) { sc->arge_mdiofreq = mdio_freq; div = arge_mdio_get_divider(sc, sc->arge_mdiofreq); if (bootverbose) device_printf(sc->arge_dev, "%s: mdio ref freq=%llu Hz, target freq=%llu Hz," " divisor index=%d\n", __func__, (unsigned long long) ar71xx_mdio_freq(), (unsigned long long) mdio_freq, div); if (div >= 0) return (div); } /* * Default value(s). * * XXX obviously these need .. fixing. * * From Linux/OpenWRT: * * + 7240? DIV_6 * + Builtin-switch port and not 934x? DIV_10 * + Not built-in switch port and 934x? DIV_58 * + .. else DIV_28. */ switch (ar71xx_soc) { case AR71XX_SOC_AR9341: case AR71XX_SOC_AR9342: case AR71XX_SOC_AR9344: case AR71XX_SOC_QCA9556: case AR71XX_SOC_QCA9558: return (MAC_MII_CFG_CLOCK_DIV_58); break; default: return (MAC_MII_CFG_CLOCK_DIV_28); } } static void arge_reset_miibus(struct arge_softc *sc) { uint32_t mdio_div; mdio_div = arge_fetch_mdiobus_clock_rate(sc); /* * XXX AR934x and later; should we be also resetting the * MDIO block(s) using the reset register block? */ /* Reset MII bus; program in the default divisor */ ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_RESET | mdio_div); DELAY(100); ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, mdio_div); DELAY(100); } static void arge_fetch_pll_config(struct arge_softc *sc) { long int val; if (resource_long_value(device_get_name(sc->arge_dev), device_get_unit(sc->arge_dev), "pll_10", &val) == 0) { sc->arge_pllcfg.pll_10 = val; device_printf(sc->arge_dev, "%s: pll_10 = 0x%x\n", __func__, (int) val); } if (resource_long_value(device_get_name(sc->arge_dev), device_get_unit(sc->arge_dev), "pll_100", &val) == 0) { sc->arge_pllcfg.pll_100 = val; device_printf(sc->arge_dev, "%s: pll_100 = 0x%x\n", __func__, (int) val); } if (resource_long_value(device_get_name(sc->arge_dev), device_get_unit(sc->arge_dev), "pll_1000", &val) == 0) { sc->arge_pllcfg.pll_1000 = val; device_printf(sc->arge_dev, "%s: pll_1000 = 0x%x\n", __func__, (int) val); } } static int arge_attach(device_t dev) { struct ifnet *ifp; struct arge_softc *sc; int error = 0, rid, i; uint32_t hint; long eeprom_mac_addr = 0; int miicfg = 0; int readascii = 0; int local_mac = 0; uint8_t local_macaddr[ETHER_ADDR_LEN]; char * local_macstr; char devid_str[32]; int count; sc = device_get_softc(dev); sc->arge_dev = dev; sc->arge_mac_unit = device_get_unit(dev); /* * See if there's a "board" MAC address hint available for * this particular device. * * This is in the environment - it'd be nice to use the resource_*() * routines, but at the moment the system is booting, the resource hints * are set to the 'static' map so they're not pulling from kenv. */ snprintf(devid_str, 32, "hint.%s.%d.macaddr", device_get_name(dev), device_get_unit(dev)); if ((local_macstr = kern_getenv(devid_str)) != NULL) { uint32_t tmpmac[ETHER_ADDR_LEN]; /* Have a MAC address; should use it */ device_printf(dev, "Overriding MAC address from environment: '%s'\n", local_macstr); /* Extract out the MAC address */ /* XXX this should all be a generic method */ count = sscanf(local_macstr, "%x%*c%x%*c%x%*c%x%*c%x%*c%x", &tmpmac[0], &tmpmac[1], &tmpmac[2], &tmpmac[3], &tmpmac[4], &tmpmac[5]); if (count == 6) { /* Valid! */ local_mac = 1; for (i = 0; i < ETHER_ADDR_LEN; i++) local_macaddr[i] = tmpmac[i]; } /* Done! */ freeenv(local_macstr); local_macstr = NULL; } /* * Some units (eg the TP-Link WR-1043ND) do not have a convenient * EEPROM location to read the ethernet MAC address from. * OpenWRT simply snaffles it from a fixed location. * * Since multiple units seem to use this feature, include * a method of setting the MAC address based on an flash location * in CPU address space. * * Some vendors have decided to store the mac address as a literal * string of 18 characters in xx:xx:xx:xx:xx:xx format instead of * an array of numbers. Expose a hint to turn on this conversion * feature via strtol() */ if (local_mac == 0 && resource_long_value(device_get_name(dev), device_get_unit(dev), "eeprommac", &eeprom_mac_addr) == 0) { local_mac = 1; int i; const char *mac = (const char *) MIPS_PHYS_TO_KSEG1(eeprom_mac_addr); device_printf(dev, "Overriding MAC from EEPROM\n"); if (resource_int_value(device_get_name(dev), device_get_unit(dev), "readascii", &readascii) == 0) { device_printf(dev, "Vendor stores MAC in ASCII format\n"); for (i = 0; i < 6; i++) { local_macaddr[i] = strtol(&(mac[i*3]), NULL, 16); } } else { for (i = 0; i < 6; i++) { local_macaddr[i] = mac[i]; } } } KASSERT(((sc->arge_mac_unit == 0) || (sc->arge_mac_unit == 1)), ("if_arge: Only MAC0 and MAC1 supported")); /* * Fetch the PLL configuration. */ arge_fetch_pll_config(sc); /* * Get the MII configuration, if applicable. */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), "miimode", &miicfg) == 0) { /* XXX bounds check? */ device_printf(dev, "%s: overriding MII mode to '%s'\n", __func__, arge_miicfg_str[miicfg]); sc->arge_miicfg = miicfg; } /* * Get which PHY of 5 available we should use for this unit */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), "phymask", &sc->arge_phymask) != 0) { /* * Use port 4 (WAN) for GE0. For any other port use * its PHY the same as its unit number */ if (sc->arge_mac_unit == 0) sc->arge_phymask = (1 << 4); else /* Use all phys up to 4 */ sc->arge_phymask = (1 << 4) - 1; device_printf(dev, "No PHY specified, using mask %d\n", sc->arge_phymask); } /* * Get default/hard-coded media & duplex mode. */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), "media", &hint) != 0) hint = 0; if (hint == 1000) sc->arge_media_type = IFM_1000_T; else if (hint == 100) sc->arge_media_type = IFM_100_TX; else if (hint == 10) sc->arge_media_type = IFM_10_T; else sc->arge_media_type = 0; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "fduplex", &hint) != 0) hint = 1; if (hint) sc->arge_duplex_mode = IFM_FDX; else sc->arge_duplex_mode = 0; mtx_init(&sc->arge_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init_mtx(&sc->arge_stat_callout, &sc->arge_mtx, 0); TASK_INIT(&sc->arge_link_task, 0, arge_link_task, sc); /* Map control/status registers. */ sc->arge_rid = 0; sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->arge_rid, RF_ACTIVE | RF_SHAREABLE); if (sc->arge_res == NULL) { device_printf(dev, "couldn't map memory\n"); error = ENXIO; goto fail; } /* Allocate interrupts */ rid = 0; sc->arge_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->arge_irq == NULL) { device_printf(dev, "couldn't map interrupt\n"); error = ENXIO; goto fail; } /* Allocate ifnet structure. */ ifp = sc->arge_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(dev, "couldn't allocate ifnet structure\n"); error = ENOSPC; goto fail; } ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = arge_ioctl; ifp->if_start = arge_start; ifp->if_init = arge_init; sc->arge_if_flags = ifp->if_flags; /* XXX: add real size */ IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); ifp->if_snd.ifq_maxlen = ifqmaxlen; IFQ_SET_READY(&ifp->if_snd); /* Tell the upper layer(s) we support long frames. */ ifp->if_capabilities |= IFCAP_VLAN_MTU; ifp->if_capenable = ifp->if_capabilities; #ifdef DEVICE_POLLING ifp->if_capabilities |= IFCAP_POLLING; #endif /* If there's a local mac defined, copy that in */ if (local_mac == 1) { (void) ar71xx_mac_addr_init(sc->arge_eaddr, local_macaddr, 0, 0); } else { /* * No MAC address configured. Generate the random one. */ if (bootverbose) device_printf(dev, "Generating random ethernet address.\n"); (void) ar71xx_mac_addr_random_init(sc->arge_eaddr); } if (arge_dma_alloc(sc) != 0) { error = ENXIO; goto fail; } /* * Don't do this for the MDIO bus case - it's already done * as part of the MDIO bus attachment. */ #if !defined(ARGE_MDIO) /* Initialize the MAC block */ arge_reset_mac(sc); arge_reset_miibus(sc); #endif /* Configure MII mode, just for convienence */ if (sc->arge_miicfg != 0) ar71xx_device_set_mii_if(sc->arge_mac_unit, sc->arge_miicfg); /* * Set all Ethernet address registers to the same initial values * set all four addresses to 66-88-aa-cc-dd-ee */ ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR1, (sc->arge_eaddr[2] << 24) | (sc->arge_eaddr[3] << 16) | (sc->arge_eaddr[4] << 8) | sc->arge_eaddr[5]); ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR2, (sc->arge_eaddr[0] << 8) | sc->arge_eaddr[1]); ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG0, FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT); switch (ar71xx_soc) { case AR71XX_SOC_AR7240: case AR71XX_SOC_AR7241: case AR71XX_SOC_AR7242: case AR71XX_SOC_AR9330: case AR71XX_SOC_AR9331: case AR71XX_SOC_AR9341: case AR71XX_SOC_AR9342: case AR71XX_SOC_AR9344: case AR71XX_SOC_QCA9556: case AR71XX_SOC_QCA9558: ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG1, 0x0010ffff); ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG2, 0x015500aa); break; /* AR71xx, AR913x */ default: ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG1, 0x0fff0000); ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG2, 0x00001fff); } ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMATCH, FIFO_RX_FILTMATCH_DEFAULT); ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK, FIFO_RX_FILTMASK_DEFAULT); #if defined(ARGE_MDIO) sc->arge_miiproxy = mii_attach_proxy(sc->arge_dev); #endif device_printf(sc->arge_dev, "finishing attachment, phymask %04x" ", proxy %s \n", sc->arge_phymask, sc->arge_miiproxy == NULL ? "null" : "set"); for (i = 0; i < ARGE_NPHY; i++) { if (((1 << i) & sc->arge_phymask) != 0) { error = mii_attach(sc->arge_miiproxy != NULL ? sc->arge_miiproxy : sc->arge_dev, &sc->arge_miibus, sc->arge_ifp, arge_ifmedia_upd, arge_ifmedia_sts, BMSR_DEFCAPMASK, i, MII_OFFSET_ANY, 0); if (error != 0) { device_printf(sc->arge_dev, "unable to attach" " PHY %d: %d\n", i, error); goto fail; } } } if (sc->arge_miibus == NULL) { /* no PHY, so use hard-coded values */ ifmedia_init(&sc->arge_ifmedia, 0, arge_multiphy_mediachange, arge_multiphy_mediastatus); ifmedia_add(&sc->arge_ifmedia, IFM_ETHER | sc->arge_media_type | sc->arge_duplex_mode, 0, NULL); ifmedia_set(&sc->arge_ifmedia, IFM_ETHER | sc->arge_media_type | sc->arge_duplex_mode); arge_set_pll(sc, sc->arge_media_type, sc->arge_duplex_mode); } /* Call MI attach routine. */ ether_ifattach(sc->arge_ifp, sc->arge_eaddr); /* Hook interrupt last to avoid having to lock softc */ error = bus_setup_intr(sc->arge_dev, sc->arge_irq, INTR_TYPE_NET | INTR_MPSAFE, arge_intr_filter, arge_intr, sc, &sc->arge_intrhand); if (error) { device_printf(sc->arge_dev, "couldn't set up irq\n"); ether_ifdetach(sc->arge_ifp); goto fail; } /* setup sysctl variables */ arge_attach_sysctl(sc->arge_dev); fail: if (error) arge_detach(dev); return (error); } static int arge_detach(device_t dev) { struct arge_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->arge_ifp; KASSERT(mtx_initialized(&sc->arge_mtx), ("arge mutex not initialized")); /* These should only be active if attach succeeded */ if (device_is_attached(dev)) { ARGE_LOCK(sc); sc->arge_detach = 1; #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) ether_poll_deregister(ifp); #endif arge_stop(sc); ARGE_UNLOCK(sc); taskqueue_drain(taskqueue_swi, &sc->arge_link_task); ether_ifdetach(ifp); } if (sc->arge_miibus) device_delete_child(dev, sc->arge_miibus); if (sc->arge_miiproxy) device_delete_child(dev, sc->arge_miiproxy); bus_generic_detach(dev); if (sc->arge_intrhand) bus_teardown_intr(dev, sc->arge_irq, sc->arge_intrhand); if (sc->arge_res) bus_release_resource(dev, SYS_RES_MEMORY, sc->arge_rid, sc->arge_res); if (ifp) if_free(ifp); arge_dma_free(sc); mtx_destroy(&sc->arge_mtx); return (0); } static int arge_suspend(device_t dev) { panic("%s", __func__); return 0; } static int arge_resume(device_t dev) { panic("%s", __func__); return 0; } static int arge_shutdown(device_t dev) { struct arge_softc *sc; sc = device_get_softc(dev); ARGE_LOCK(sc); arge_stop(sc); ARGE_UNLOCK(sc); return (0); } static void arge_hinted_child(device_t bus, const char *dname, int dunit) { BUS_ADD_CHILD(bus, 0, dname, dunit); device_printf(bus, "hinted child %s%d\n", dname, dunit); } static int arge_miibus_readreg(device_t dev, int phy, int reg) { struct arge_softc * sc = device_get_softc(dev); int i, result; uint32_t addr = (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK); mtx_lock(&miibus_mtx); ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_ADDR, addr); ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_READ); i = ARGE_MII_TIMEOUT; while ((ARGE_MDIO_READ(sc, AR71XX_MAC_MII_INDICATOR) & MAC_MII_INDICATOR_BUSY) && (i--)) DELAY(5); if (i < 0) { mtx_unlock(&miibus_mtx); ARGEDEBUG(sc, ARGE_DBG_MII, "%s timedout\n", __func__); /* XXX: return ERRNO istead? */ return (-1); } result = ARGE_MDIO_READ(sc, AR71XX_MAC_MII_STATUS) & MAC_MII_STATUS_MASK; ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); mtx_unlock(&miibus_mtx); ARGEDEBUG(sc, ARGE_DBG_MII, "%s: phy=%d, reg=%02x, value[%08x]=%04x\n", __func__, phy, reg, addr, result); return (result); } static int arge_miibus_writereg(device_t dev, int phy, int reg, int data) { struct arge_softc * sc = device_get_softc(dev); int i; uint32_t addr = (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK); ARGEDEBUG(sc, ARGE_DBG_MII, "%s: phy=%d, reg=%02x, value=%04x\n", __func__, phy, reg, data); mtx_lock(&miibus_mtx); ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_ADDR, addr); ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CONTROL, data); i = ARGE_MII_TIMEOUT; while ((ARGE_MDIO_READ(sc, AR71XX_MAC_MII_INDICATOR) & MAC_MII_INDICATOR_BUSY) && (i--)) DELAY(5); mtx_unlock(&miibus_mtx); if (i < 0) { ARGEDEBUG(sc, ARGE_DBG_MII, "%s timedout\n", __func__); /* XXX: return ERRNO istead? */ return (-1); } return (0); } static void arge_miibus_statchg(device_t dev) { struct arge_softc *sc; sc = device_get_softc(dev); taskqueue_enqueue(taskqueue_swi, &sc->arge_link_task); } static void arge_link_task(void *arg, int pending) { struct arge_softc *sc; sc = (struct arge_softc *)arg; ARGE_LOCK(sc); arge_update_link_locked(sc); ARGE_UNLOCK(sc); } static void arge_update_link_locked(struct arge_softc *sc) { struct mii_data *mii; struct ifnet *ifp; uint32_t media, duplex; mii = device_get_softc(sc->arge_miibus); ifp = sc->arge_ifp; if (mii == NULL || ifp == NULL || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { return; } /* * If we have a static media type configured, then * use that. Some PHY configurations (eg QCA955x -> AR8327) * use a static speed/duplex between the SoC and switch, * even though the front-facing PHY speed changes. */ if (sc->arge_media_type != 0) { ARGEDEBUG(sc, ARGE_DBG_MII, "%s: fixed; media=%d, duplex=%d\n", __func__, sc->arge_media_type, sc->arge_duplex_mode); if (mii->mii_media_status & IFM_ACTIVE) { sc->arge_link_status = 1; } else { sc->arge_link_status = 0; } arge_set_pll(sc, sc->arge_media_type, sc->arge_duplex_mode); } if (mii->mii_media_status & IFM_ACTIVE) { media = IFM_SUBTYPE(mii->mii_media_active); if (media != IFM_NONE) { sc->arge_link_status = 1; duplex = mii->mii_media_active & IFM_GMASK; ARGEDEBUG(sc, ARGE_DBG_MII, "%s: media=%d, duplex=%d\n", __func__, media, duplex); arge_set_pll(sc, media, duplex); } } else { sc->arge_link_status = 0; } } static void arge_set_pll(struct arge_softc *sc, int media, int duplex) { uint32_t cfg, ifcontrol, rx_filtmask; uint32_t fifo_tx, pll; int if_speed; /* * XXX Verify - is this valid for all chips? * QCA955x (and likely some of the earlier chips!) define * this as nibble mode and byte mode, and those have to do * with the interface type (MII/SMII versus GMII/RGMII.) */ ARGEDEBUG(sc, ARGE_DBG_PLL, "set_pll(%04x, %s)\n", media, duplex == IFM_FDX ? "full" : "half"); cfg = ARGE_READ(sc, AR71XX_MAC_CFG2); cfg &= ~(MAC_CFG2_IFACE_MODE_1000 | MAC_CFG2_IFACE_MODE_10_100 | MAC_CFG2_FULL_DUPLEX); if (duplex == IFM_FDX) cfg |= MAC_CFG2_FULL_DUPLEX; ifcontrol = ARGE_READ(sc, AR71XX_MAC_IFCONTROL); ifcontrol &= ~MAC_IFCONTROL_SPEED; rx_filtmask = ARGE_READ(sc, AR71XX_MAC_FIFO_RX_FILTMASK); rx_filtmask &= ~FIFO_RX_MASK_BYTE_MODE; switch(media) { case IFM_10_T: cfg |= MAC_CFG2_IFACE_MODE_10_100; if_speed = 10; break; case IFM_100_TX: cfg |= MAC_CFG2_IFACE_MODE_10_100; ifcontrol |= MAC_IFCONTROL_SPEED; if_speed = 100; break; case IFM_1000_T: case IFM_1000_SX: cfg |= MAC_CFG2_IFACE_MODE_1000; rx_filtmask |= FIFO_RX_MASK_BYTE_MODE; if_speed = 1000; break; default: if_speed = 100; device_printf(sc->arge_dev, "Unknown media %d\n", media); } ARGEDEBUG(sc, ARGE_DBG_PLL, "%s: if_speed=%d\n", __func__, if_speed); switch (ar71xx_soc) { case AR71XX_SOC_AR7240: case AR71XX_SOC_AR7241: case AR71XX_SOC_AR7242: case AR71XX_SOC_AR9330: case AR71XX_SOC_AR9331: case AR71XX_SOC_AR9341: case AR71XX_SOC_AR9342: case AR71XX_SOC_AR9344: case AR71XX_SOC_QCA9556: case AR71XX_SOC_QCA9558: fifo_tx = 0x01f00140; break; case AR71XX_SOC_AR9130: case AR71XX_SOC_AR9132: fifo_tx = 0x00780fff; break; /* AR71xx */ default: fifo_tx = 0x008001ff; } ARGE_WRITE(sc, AR71XX_MAC_CFG2, cfg); ARGE_WRITE(sc, AR71XX_MAC_IFCONTROL, ifcontrol); ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK, rx_filtmask); ARGE_WRITE(sc, AR71XX_MAC_FIFO_TX_THRESHOLD, fifo_tx); /* fetch PLL registers */ pll = ar71xx_device_get_eth_pll(sc->arge_mac_unit, if_speed); ARGEDEBUG(sc, ARGE_DBG_PLL, "%s: pll=0x%x\n", __func__, pll); /* Override if required by platform data */ if (if_speed == 10 && sc->arge_pllcfg.pll_10 != 0) pll = sc->arge_pllcfg.pll_10; else if (if_speed == 100 && sc->arge_pllcfg.pll_100 != 0) pll = sc->arge_pllcfg.pll_100; else if (if_speed == 1000 && sc->arge_pllcfg.pll_1000 != 0) pll = sc->arge_pllcfg.pll_1000; ARGEDEBUG(sc, ARGE_DBG_PLL, "%s: final pll=0x%x\n", __func__, pll); /* XXX ensure pll != 0 */ ar71xx_device_set_pll_ge(sc->arge_mac_unit, if_speed, pll); /* set MII registers */ /* * This was introduced to match what the Linux ag71xx ethernet * driver does. For the AR71xx case, it does set the port * MII speed. However, if this is done, non-gigabit speeds * are not at all reliable when speaking via RGMII through * 'bridge' PHY port that's pretending to be a local PHY. * * Until that gets root caused, and until an AR71xx + normal * PHY board is tested, leave this disabled. */ #if 0 ar71xx_device_set_mii_speed(sc->arge_mac_unit, if_speed); #endif } static void arge_reset_dma(struct arge_softc *sc) { ARGEDEBUG(sc, ARGE_DBG_RESET, "%s: called\n", __func__); ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, 0); ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, 0); ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, 0); ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, 0); /* Clear all possible RX interrupts */ while(ARGE_READ(sc, AR71XX_DMA_RX_STATUS) & DMA_RX_STATUS_PKT_RECVD) ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD); /* * Clear all possible TX interrupts */ while(ARGE_READ(sc, AR71XX_DMA_TX_STATUS) & DMA_TX_STATUS_PKT_SENT) ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_PKT_SENT); /* * Now Rx/Tx errors */ ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_BUS_ERROR | DMA_RX_STATUS_OVERFLOW); ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_BUS_ERROR | DMA_TX_STATUS_UNDERRUN); /* * Force a DDR flush so any pending data is properly * flushed to RAM before underlying buffers are freed. */ arge_flush_ddr(sc); } static void arge_init(void *xsc) { struct arge_softc *sc = xsc; ARGE_LOCK(sc); arge_init_locked(sc); ARGE_UNLOCK(sc); } static void arge_init_locked(struct arge_softc *sc) { struct ifnet *ifp = sc->arge_ifp; struct mii_data *mii; ARGE_LOCK_ASSERT(sc); if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) return; /* Init circular RX list. */ if (arge_rx_ring_init(sc) != 0) { device_printf(sc->arge_dev, "initialization failed: no memory for rx buffers\n"); arge_stop(sc); return; } /* Init tx descriptors. */ arge_tx_ring_init(sc); arge_reset_dma(sc); if (sc->arge_miibus) { mii = device_get_softc(sc->arge_miibus); mii_mediachg(mii); } else { /* * Sun always shines over multiPHY interface */ sc->arge_link_status = 1; } ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; if (sc->arge_miibus) { callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc); arge_update_link_locked(sc); } ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, ARGE_TX_RING_ADDR(sc, 0)); ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, ARGE_RX_RING_ADDR(sc, 0)); /* Start listening */ ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, DMA_RX_CONTROL_EN); /* Enable interrupts */ ARGE_WRITE(sc, AR71XX_DMA_INTR, DMA_INTR_ALL); } /* * Return whether the mbuf chain is correctly aligned * for the arge TX engine. * * The TX engine requires each fragment to be aligned to a * 4 byte boundary and the size of each fragment except * the last to be a multiple of 4 bytes. * * XXX TODO: I believe this is only a bug on the AR71xx and * AR913x MACs. The later MACs (AR724x and later) does not * need this workaround. */ static int arge_mbuf_chain_is_tx_aligned(struct mbuf *m0) { struct mbuf *m; for (m = m0; m != NULL; m = m->m_next) { if((mtod(m, intptr_t) & 3) != 0) return 0; if ((m->m_next != NULL) && ((m->m_len & 0x03) != 0)) return 0; } return 1; } /* * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data * pointers to the fragment pointers. */ static int arge_encap(struct arge_softc *sc, struct mbuf **m_head) { struct arge_txdesc *txd; struct arge_desc *desc, *prev_desc; bus_dma_segment_t txsegs[ARGE_MAXFRAGS]; int error, i, nsegs, prod, prev_prod; struct mbuf *m; ARGE_LOCK_ASSERT(sc); /* * Fix mbuf chain, all fragments should be 4 bytes aligned and * even 4 bytes * * XXX TODO: I believe this is only a bug on the AR71xx and * AR913x MACs. The later MACs (AR724x and later) does not * need this workaround. */ m = *m_head; if (! arge_mbuf_chain_is_tx_aligned(m)) { sc->stats.tx_pkts_unaligned++; m = m_defrag(*m_head, M_NOWAIT); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } *m_head = m; } else sc->stats.tx_pkts_aligned++; prod = sc->arge_cdata.arge_tx_prod; txd = &sc->arge_cdata.arge_txdesc[prod]; error = bus_dmamap_load_mbuf_sg(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap, *m_head, txsegs, &nsegs, BUS_DMA_NOWAIT); if (error == EFBIG) { panic("EFBIG"); } else if (error != 0) return (error); if (nsegs == 0) { m_freem(*m_head); *m_head = NULL; return (EIO); } /* Check number of available descriptors. */ if (sc->arge_cdata.arge_tx_cnt + nsegs >= (ARGE_TX_RING_COUNT - 1)) { bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap); return (ENOBUFS); } txd->tx_m = *m_head; bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap, BUS_DMASYNC_PREWRITE); /* * Make a list of descriptors for this packet. DMA controller will * walk through it while arge_link is not zero. */ prev_prod = prod; desc = prev_desc = NULL; for (i = 0; i < nsegs; i++) { desc = &sc->arge_rdata.arge_tx_ring[prod]; desc->packet_ctrl = ARGE_DMASIZE(txsegs[i].ds_len); if (txsegs[i].ds_addr & 3) panic("TX packet address unaligned\n"); desc->packet_addr = txsegs[i].ds_addr; /* link with previous descriptor */ if (prev_desc) prev_desc->packet_ctrl |= ARGE_DESC_MORE; sc->arge_cdata.arge_tx_cnt++; prev_desc = desc; ARGE_INC(prod, ARGE_TX_RING_COUNT); } /* Update producer index. */ sc->arge_cdata.arge_tx_prod = prod; /* Sync descriptors. */ bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag, sc->arge_cdata.arge_tx_ring_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); /* Start transmitting */ ARGEDEBUG(sc, ARGE_DBG_TX, "%s: setting DMA_TX_CONTROL_EN\n", __func__); ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, DMA_TX_CONTROL_EN); return (0); } static void arge_start(struct ifnet *ifp) { struct arge_softc *sc; sc = ifp->if_softc; ARGE_LOCK(sc); arge_start_locked(ifp); ARGE_UNLOCK(sc); } static void arge_start_locked(struct ifnet *ifp) { struct arge_softc *sc; struct mbuf *m_head; int enq = 0; sc = ifp->if_softc; ARGE_LOCK_ASSERT(sc); ARGEDEBUG(sc, ARGE_DBG_TX, "%s: beginning\n", __func__); if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING || sc->arge_link_status == 0 ) return; /* * Before we go any further, check whether we're already full. * The below check errors out immediately if the ring is full * and never gets a chance to set this flag. Although it's * likely never needed, this at least avoids an unexpected * situation. */ if (sc->arge_cdata.arge_tx_cnt >= ARGE_TX_RING_COUNT - 2) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; ARGEDEBUG(sc, ARGE_DBG_ERR, "%s: tx_cnt %d >= max %d; setting IFF_DRV_OACTIVE\n", __func__, sc->arge_cdata.arge_tx_cnt, ARGE_TX_RING_COUNT - 2); return; } arge_flush_ddr(sc); for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && sc->arge_cdata.arge_tx_cnt < ARGE_TX_RING_COUNT - 2; ) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; /* * Pack the data into the transmit ring. */ if (arge_encap(sc, &m_head)) { if (m_head == NULL) break; IFQ_DRV_PREPEND(&ifp->if_snd, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } enq++; /* * If there's a BPF listener, bounce a copy of this frame * to him. */ ETHER_BPF_MTAP(ifp, m_head); } ARGEDEBUG(sc, ARGE_DBG_TX, "%s: finished; queued %d packets\n", __func__, enq); } static void arge_stop(struct arge_softc *sc) { struct ifnet *ifp; ARGE_LOCK_ASSERT(sc); ifp = sc->arge_ifp; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); if (sc->arge_miibus) callout_stop(&sc->arge_stat_callout); /* mask out interrupts */ ARGE_WRITE(sc, AR71XX_DMA_INTR, 0); arge_reset_dma(sc); /* Flush FIFO and free any existing mbufs */ arge_flush_ddr(sc); arge_rx_ring_free(sc); arge_tx_ring_free(sc); } static int arge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct arge_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; struct mii_data *mii; int error; #ifdef DEVICE_POLLING int mask; #endif switch (command) { case SIOCSIFFLAGS: ARGE_LOCK(sc); if ((ifp->if_flags & IFF_UP) != 0) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { if (((ifp->if_flags ^ sc->arge_if_flags) & (IFF_PROMISC | IFF_ALLMULTI)) != 0) { /* XXX: handle promisc & multi flags */ } } else { if (!sc->arge_detach) arge_init_locked(sc); } } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { ifp->if_drv_flags &= ~IFF_DRV_RUNNING; arge_stop(sc); } sc->arge_if_flags = ifp->if_flags; ARGE_UNLOCK(sc); error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: /* XXX: implement SIOCDELMULTI */ error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: if (sc->arge_miibus) { mii = device_get_softc(sc->arge_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); } else error = ifmedia_ioctl(ifp, ifr, &sc->arge_ifmedia, command); break; case SIOCSIFCAP: /* XXX: Check other capabilities */ #ifdef DEVICE_POLLING mask = ifp->if_capenable ^ ifr->ifr_reqcap; if (mask & IFCAP_POLLING) { if (ifr->ifr_reqcap & IFCAP_POLLING) { ARGE_WRITE(sc, AR71XX_DMA_INTR, 0); error = ether_poll_register(arge_poll, ifp); if (error) return error; ARGE_LOCK(sc); ifp->if_capenable |= IFCAP_POLLING; ARGE_UNLOCK(sc); } else { ARGE_WRITE(sc, AR71XX_DMA_INTR, DMA_INTR_ALL); error = ether_poll_deregister(ifp); ARGE_LOCK(sc); ifp->if_capenable &= ~IFCAP_POLLING; ARGE_UNLOCK(sc); } } error = 0; break; #endif default: error = ether_ioctl(ifp, command, data); break; } return (error); } /* * Set media options. */ static int arge_ifmedia_upd(struct ifnet *ifp) { struct arge_softc *sc; struct mii_data *mii; struct mii_softc *miisc; int error; sc = ifp->if_softc; ARGE_LOCK(sc); mii = device_get_softc(sc->arge_miibus); LIST_FOREACH(miisc, &mii->mii_phys, mii_list) PHY_RESET(miisc); error = mii_mediachg(mii); ARGE_UNLOCK(sc); return (error); } /* * Report current media status. */ static void arge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct arge_softc *sc = ifp->if_softc; struct mii_data *mii; mii = device_get_softc(sc->arge_miibus); ARGE_LOCK(sc); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; ARGE_UNLOCK(sc); } struct arge_dmamap_arg { bus_addr_t arge_busaddr; }; static void arge_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct arge_dmamap_arg *ctx; if (error != 0) return; ctx = arg; ctx->arge_busaddr = segs[0].ds_addr; } static int arge_dma_alloc(struct arge_softc *sc) { struct arge_dmamap_arg ctx; struct arge_txdesc *txd; struct arge_rxdesc *rxd; int error, i; /* Create parent DMA tag. */ error = bus_dma_tag_create( bus_get_dma_tag(sc->arge_dev), /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ 0, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->arge_cdata.arge_parent_tag); if (error != 0) { device_printf(sc->arge_dev, "failed to create parent DMA tag\n"); goto fail; } /* Create tag for Tx ring. */ error = bus_dma_tag_create( sc->arge_cdata.arge_parent_tag, /* parent */ ARGE_RING_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ ARGE_TX_DMA_SIZE, /* maxsize */ 1, /* nsegments */ ARGE_TX_DMA_SIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->arge_cdata.arge_tx_ring_tag); if (error != 0) { device_printf(sc->arge_dev, "failed to create Tx ring DMA tag\n"); goto fail; } /* Create tag for Rx ring. */ error = bus_dma_tag_create( sc->arge_cdata.arge_parent_tag, /* parent */ ARGE_RING_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ ARGE_RX_DMA_SIZE, /* maxsize */ 1, /* nsegments */ ARGE_RX_DMA_SIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->arge_cdata.arge_rx_ring_tag); if (error != 0) { device_printf(sc->arge_dev, "failed to create Rx ring DMA tag\n"); goto fail; } /* Create tag for Tx buffers. */ error = bus_dma_tag_create( sc->arge_cdata.arge_parent_tag, /* parent */ sizeof(uint32_t), 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MCLBYTES * ARGE_MAXFRAGS, /* maxsize */ ARGE_MAXFRAGS, /* nsegments */ MCLBYTES, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->arge_cdata.arge_tx_tag); if (error != 0) { device_printf(sc->arge_dev, "failed to create Tx DMA tag\n"); goto fail; } /* Create tag for Rx buffers. */ error = bus_dma_tag_create( sc->arge_cdata.arge_parent_tag, /* parent */ ARGE_RX_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MCLBYTES, /* maxsize */ ARGE_MAXFRAGS, /* nsegments */ MCLBYTES, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->arge_cdata.arge_rx_tag); if (error != 0) { device_printf(sc->arge_dev, "failed to create Rx DMA tag\n"); goto fail; } /* Allocate DMA'able memory and load the DMA map for Tx ring. */ error = bus_dmamem_alloc(sc->arge_cdata.arge_tx_ring_tag, (void **)&sc->arge_rdata.arge_tx_ring, BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->arge_cdata.arge_tx_ring_map); if (error != 0) { device_printf(sc->arge_dev, "failed to allocate DMA'able memory for Tx ring\n"); goto fail; } ctx.arge_busaddr = 0; error = bus_dmamap_load(sc->arge_cdata.arge_tx_ring_tag, sc->arge_cdata.arge_tx_ring_map, sc->arge_rdata.arge_tx_ring, ARGE_TX_DMA_SIZE, arge_dmamap_cb, &ctx, 0); if (error != 0 || ctx.arge_busaddr == 0) { device_printf(sc->arge_dev, "failed to load DMA'able memory for Tx ring\n"); goto fail; } sc->arge_rdata.arge_tx_ring_paddr = ctx.arge_busaddr; /* Allocate DMA'able memory and load the DMA map for Rx ring. */ error = bus_dmamem_alloc(sc->arge_cdata.arge_rx_ring_tag, (void **)&sc->arge_rdata.arge_rx_ring, BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->arge_cdata.arge_rx_ring_map); if (error != 0) { device_printf(sc->arge_dev, "failed to allocate DMA'able memory for Rx ring\n"); goto fail; } ctx.arge_busaddr = 0; error = bus_dmamap_load(sc->arge_cdata.arge_rx_ring_tag, sc->arge_cdata.arge_rx_ring_map, sc->arge_rdata.arge_rx_ring, ARGE_RX_DMA_SIZE, arge_dmamap_cb, &ctx, 0); if (error != 0 || ctx.arge_busaddr == 0) { device_printf(sc->arge_dev, "failed to load DMA'able memory for Rx ring\n"); goto fail; } sc->arge_rdata.arge_rx_ring_paddr = ctx.arge_busaddr; /* Create DMA maps for Tx buffers. */ for (i = 0; i < ARGE_TX_RING_COUNT; i++) { txd = &sc->arge_cdata.arge_txdesc[i]; txd->tx_m = NULL; txd->tx_dmamap = NULL; error = bus_dmamap_create(sc->arge_cdata.arge_tx_tag, 0, &txd->tx_dmamap); if (error != 0) { device_printf(sc->arge_dev, "failed to create Tx dmamap\n"); goto fail; } } /* Create DMA maps for Rx buffers. */ if ((error = bus_dmamap_create(sc->arge_cdata.arge_rx_tag, 0, &sc->arge_cdata.arge_rx_sparemap)) != 0) { device_printf(sc->arge_dev, "failed to create spare Rx dmamap\n"); goto fail; } for (i = 0; i < ARGE_RX_RING_COUNT; i++) { rxd = &sc->arge_cdata.arge_rxdesc[i]; rxd->rx_m = NULL; rxd->rx_dmamap = NULL; error = bus_dmamap_create(sc->arge_cdata.arge_rx_tag, 0, &rxd->rx_dmamap); if (error != 0) { device_printf(sc->arge_dev, "failed to create Rx dmamap\n"); goto fail; } } fail: return (error); } static void arge_dma_free(struct arge_softc *sc) { struct arge_txdesc *txd; struct arge_rxdesc *rxd; int i; /* Tx ring. */ if (sc->arge_cdata.arge_tx_ring_tag) { if (sc->arge_rdata.arge_tx_ring_paddr) bus_dmamap_unload(sc->arge_cdata.arge_tx_ring_tag, sc->arge_cdata.arge_tx_ring_map); if (sc->arge_rdata.arge_tx_ring) bus_dmamem_free(sc->arge_cdata.arge_tx_ring_tag, sc->arge_rdata.arge_tx_ring, sc->arge_cdata.arge_tx_ring_map); sc->arge_rdata.arge_tx_ring = NULL; sc->arge_rdata.arge_tx_ring_paddr = 0; bus_dma_tag_destroy(sc->arge_cdata.arge_tx_ring_tag); sc->arge_cdata.arge_tx_ring_tag = NULL; } /* Rx ring. */ if (sc->arge_cdata.arge_rx_ring_tag) { if (sc->arge_rdata.arge_rx_ring_paddr) bus_dmamap_unload(sc->arge_cdata.arge_rx_ring_tag, sc->arge_cdata.arge_rx_ring_map); if (sc->arge_rdata.arge_rx_ring) bus_dmamem_free(sc->arge_cdata.arge_rx_ring_tag, sc->arge_rdata.arge_rx_ring, sc->arge_cdata.arge_rx_ring_map); sc->arge_rdata.arge_rx_ring = NULL; sc->arge_rdata.arge_rx_ring_paddr = 0; bus_dma_tag_destroy(sc->arge_cdata.arge_rx_ring_tag); sc->arge_cdata.arge_rx_ring_tag = NULL; } /* Tx buffers. */ if (sc->arge_cdata.arge_tx_tag) { for (i = 0; i < ARGE_TX_RING_COUNT; i++) { txd = &sc->arge_cdata.arge_txdesc[i]; if (txd->tx_dmamap) { bus_dmamap_destroy(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap); txd->tx_dmamap = NULL; } } bus_dma_tag_destroy(sc->arge_cdata.arge_tx_tag); sc->arge_cdata.arge_tx_tag = NULL; } /* Rx buffers. */ if (sc->arge_cdata.arge_rx_tag) { for (i = 0; i < ARGE_RX_RING_COUNT; i++) { rxd = &sc->arge_cdata.arge_rxdesc[i]; if (rxd->rx_dmamap) { bus_dmamap_destroy(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap); rxd->rx_dmamap = NULL; } } if (sc->arge_cdata.arge_rx_sparemap) { bus_dmamap_destroy(sc->arge_cdata.arge_rx_tag, sc->arge_cdata.arge_rx_sparemap); sc->arge_cdata.arge_rx_sparemap = 0; } bus_dma_tag_destroy(sc->arge_cdata.arge_rx_tag); sc->arge_cdata.arge_rx_tag = NULL; } if (sc->arge_cdata.arge_parent_tag) { bus_dma_tag_destroy(sc->arge_cdata.arge_parent_tag); sc->arge_cdata.arge_parent_tag = NULL; } } /* * Initialize the transmit descriptors. */ static int arge_tx_ring_init(struct arge_softc *sc) { struct arge_ring_data *rd; struct arge_txdesc *txd; bus_addr_t addr; int i; sc->arge_cdata.arge_tx_prod = 0; sc->arge_cdata.arge_tx_cons = 0; sc->arge_cdata.arge_tx_cnt = 0; rd = &sc->arge_rdata; bzero(rd->arge_tx_ring, sizeof(rd->arge_tx_ring)); for (i = 0; i < ARGE_TX_RING_COUNT; i++) { if (i == ARGE_TX_RING_COUNT - 1) addr = ARGE_TX_RING_ADDR(sc, 0); else addr = ARGE_TX_RING_ADDR(sc, i + 1); rd->arge_tx_ring[i].packet_ctrl = ARGE_DESC_EMPTY; rd->arge_tx_ring[i].next_desc = addr; txd = &sc->arge_cdata.arge_txdesc[i]; txd->tx_m = NULL; } bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag, sc->arge_cdata.arge_tx_ring_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); return (0); } /* * Free the Tx ring, unload any pending dma transaction and free the mbuf. */ static void arge_tx_ring_free(struct arge_softc *sc) { struct arge_txdesc *txd; int i; /* Free the Tx buffers. */ for (i = 0; i < ARGE_TX_RING_COUNT; i++) { txd = &sc->arge_cdata.arge_txdesc[i]; if (txd->tx_dmamap) { bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap); } if (txd->tx_m) m_freem(txd->tx_m); txd->tx_m = NULL; } } /* * Initialize the RX descriptors and allocate mbufs for them. Note that * we arrange the descriptors in a closed ring, so that the last descriptor * points back to the first. */ static int arge_rx_ring_init(struct arge_softc *sc) { struct arge_ring_data *rd; struct arge_rxdesc *rxd; bus_addr_t addr; int i; sc->arge_cdata.arge_rx_cons = 0; rd = &sc->arge_rdata; bzero(rd->arge_rx_ring, sizeof(rd->arge_rx_ring)); for (i = 0; i < ARGE_RX_RING_COUNT; i++) { rxd = &sc->arge_cdata.arge_rxdesc[i]; if (rxd->rx_m != NULL) { device_printf(sc->arge_dev, "%s: ring[%d] rx_m wasn't free?\n", __func__, i); } rxd->rx_m = NULL; rxd->desc = &rd->arge_rx_ring[i]; if (i == ARGE_RX_RING_COUNT - 1) addr = ARGE_RX_RING_ADDR(sc, 0); else addr = ARGE_RX_RING_ADDR(sc, i + 1); rd->arge_rx_ring[i].next_desc = addr; if (arge_newbuf(sc, i) != 0) { return (ENOBUFS); } } bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, sc->arge_cdata.arge_rx_ring_map, BUS_DMASYNC_PREWRITE); return (0); } /* * Free all the buffers in the RX ring. * * TODO: ensure that DMA is disabled and no pending DMA * is lurking in the FIFO. */ static void arge_rx_ring_free(struct arge_softc *sc) { int i; struct arge_rxdesc *rxd; ARGE_LOCK_ASSERT(sc); for (i = 0; i < ARGE_RX_RING_COUNT; i++) { rxd = &sc->arge_cdata.arge_rxdesc[i]; /* Unmap the mbuf */ if (rxd->rx_m != NULL) { bus_dmamap_unload(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap); m_free(rxd->rx_m); rxd->rx_m = NULL; } } } /* * Initialize an RX descriptor and attach an MBUF cluster. */ static int arge_newbuf(struct arge_softc *sc, int idx) { struct arge_desc *desc; struct arge_rxdesc *rxd; struct mbuf *m; bus_dma_segment_t segs[1]; bus_dmamap_t map; int nsegs; m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) return (ENOBUFS); m->m_len = m->m_pkthdr.len = MCLBYTES; m_adj(m, sizeof(uint64_t)); if (bus_dmamap_load_mbuf_sg(sc->arge_cdata.arge_rx_tag, sc->arge_cdata.arge_rx_sparemap, m, segs, &nsegs, 0) != 0) { m_freem(m); return (ENOBUFS); } KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); rxd = &sc->arge_cdata.arge_rxdesc[idx]; if (rxd->rx_m != NULL) { bus_dmamap_unload(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap); } map = rxd->rx_dmamap; rxd->rx_dmamap = sc->arge_cdata.arge_rx_sparemap; sc->arge_cdata.arge_rx_sparemap = map; rxd->rx_m = m; desc = rxd->desc; if (segs[0].ds_addr & 3) panic("RX packet address unaligned"); desc->packet_addr = segs[0].ds_addr; desc->packet_ctrl = ARGE_DESC_EMPTY | ARGE_DMASIZE(segs[0].ds_len); bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, sc->arge_cdata.arge_rx_ring_map, BUS_DMASYNC_PREWRITE); return (0); } static __inline void arge_fixup_rx(struct mbuf *m) { int i; uint16_t *src, *dst; src = mtod(m, uint16_t *); dst = src - 1; for (i = 0; i < m->m_len / sizeof(uint16_t); i++) { *dst++ = *src++; } if (m->m_len % sizeof(uint16_t)) *(uint8_t *)dst = *(uint8_t *)src; m->m_data -= ETHER_ALIGN; } #ifdef DEVICE_POLLING static int arge_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct arge_softc *sc = ifp->if_softc; int rx_npkts = 0; if (ifp->if_drv_flags & IFF_DRV_RUNNING) { ARGE_LOCK(sc); arge_tx_locked(sc); rx_npkts = arge_rx_locked(sc); ARGE_UNLOCK(sc); } return (rx_npkts); } #endif /* DEVICE_POLLING */ static void arge_tx_locked(struct arge_softc *sc) { struct arge_txdesc *txd; struct arge_desc *cur_tx; struct ifnet *ifp; uint32_t ctrl; int cons, prod; ARGE_LOCK_ASSERT(sc); cons = sc->arge_cdata.arge_tx_cons; prod = sc->arge_cdata.arge_tx_prod; ARGEDEBUG(sc, ARGE_DBG_TX, "%s: cons=%d, prod=%d\n", __func__, cons, prod); if (cons == prod) return; bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag, sc->arge_cdata.arge_tx_ring_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); ifp = sc->arge_ifp; /* * Go through our tx list and free mbufs for those * frames that have been transmitted. */ for (; cons != prod; ARGE_INC(cons, ARGE_TX_RING_COUNT)) { cur_tx = &sc->arge_rdata.arge_tx_ring[cons]; ctrl = cur_tx->packet_ctrl; /* Check if descriptor has "finished" flag */ if ((ctrl & ARGE_DESC_EMPTY) == 0) break; ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_PKT_SENT); sc->arge_cdata.arge_tx_cnt--; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; txd = &sc->arge_cdata.arge_txdesc[cons]; if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap); /* Free only if it's first descriptor in list */ if (txd->tx_m) m_freem(txd->tx_m); txd->tx_m = NULL; /* reset descriptor */ cur_tx->packet_addr = 0; } sc->arge_cdata.arge_tx_cons = cons; bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag, sc->arge_cdata.arge_tx_ring_map, BUS_DMASYNC_PREWRITE); } static int arge_rx_locked(struct arge_softc *sc) { struct arge_rxdesc *rxd; struct ifnet *ifp = sc->arge_ifp; int cons, prog, packet_len, i; struct arge_desc *cur_rx; struct mbuf *m; int rx_npkts = 0; ARGE_LOCK_ASSERT(sc); cons = sc->arge_cdata.arge_rx_cons; bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, sc->arge_cdata.arge_rx_ring_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (prog = 0; prog < ARGE_RX_RING_COUNT; ARGE_INC(cons, ARGE_RX_RING_COUNT)) { cur_rx = &sc->arge_rdata.arge_rx_ring[cons]; rxd = &sc->arge_cdata.arge_rxdesc[cons]; m = rxd->rx_m; if ((cur_rx->packet_ctrl & ARGE_DESC_EMPTY) != 0) break; ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD); prog++; packet_len = ARGE_DMASIZE(cur_rx->packet_ctrl); bus_dmamap_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap, BUS_DMASYNC_POSTREAD); m = rxd->rx_m; arge_fixup_rx(m); m->m_pkthdr.rcvif = ifp; /* Skip 4 bytes of CRC */ m->m_pkthdr.len = m->m_len = packet_len - ETHER_CRC_LEN; if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); rx_npkts++; ARGE_UNLOCK(sc); (*ifp->if_input)(ifp, m); ARGE_LOCK(sc); cur_rx->packet_addr = 0; } if (prog > 0) { i = sc->arge_cdata.arge_rx_cons; for (; prog > 0 ; prog--) { if (arge_newbuf(sc, i) != 0) { device_printf(sc->arge_dev, "Failed to allocate buffer\n"); break; } ARGE_INC(i, ARGE_RX_RING_COUNT); } bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, sc->arge_cdata.arge_rx_ring_map, BUS_DMASYNC_PREWRITE); sc->arge_cdata.arge_rx_cons = cons; } return (rx_npkts); } static int arge_intr_filter(void *arg) { struct arge_softc *sc = arg; uint32_t status, ints; status = ARGE_READ(sc, AR71XX_DMA_INTR_STATUS); ints = ARGE_READ(sc, AR71XX_DMA_INTR); ARGEDEBUG(sc, ARGE_DBG_INTR, "int mask(filter) = %b\n", ints, "\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD" "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT"); ARGEDEBUG(sc, ARGE_DBG_INTR, "status(filter) = %b\n", status, "\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD" "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT"); if (status & DMA_INTR_ALL) { sc->arge_intr_status |= status; ARGE_WRITE(sc, AR71XX_DMA_INTR, 0); return (FILTER_SCHEDULE_THREAD); } sc->arge_intr_status = 0; return (FILTER_STRAY); } static void arge_intr(void *arg) { struct arge_softc *sc = arg; uint32_t status; struct ifnet *ifp = sc->arge_ifp; status = ARGE_READ(sc, AR71XX_DMA_INTR_STATUS); status |= sc->arge_intr_status; ARGEDEBUG(sc, ARGE_DBG_INTR, "int status(intr) = %b\n", status, "\20\10\7RX_OVERFLOW\5RX_PKT_RCVD" "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT"); /* * Is it our interrupt at all? */ if (status == 0) return; if (status & DMA_INTR_RX_BUS_ERROR) { ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_BUS_ERROR); device_printf(sc->arge_dev, "RX bus error"); return; } if (status & DMA_INTR_TX_BUS_ERROR) { ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_BUS_ERROR); device_printf(sc->arge_dev, "TX bus error"); return; } ARGE_LOCK(sc); + arge_flush_ddr(sc); if (status & DMA_INTR_RX_PKT_RCVD) arge_rx_locked(sc); /* * RX overrun disables the receiver. * Clear indication and re-enable rx. */ if ( status & DMA_INTR_RX_OVERFLOW) { ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_OVERFLOW); ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, DMA_RX_CONTROL_EN); sc->stats.rx_overflow++; } if (status & DMA_INTR_TX_PKT_SENT) arge_tx_locked(sc); /* * Underrun turns off TX. Clear underrun indication. * If there's anything left in the ring, reactivate the tx. */ if (status & DMA_INTR_TX_UNDERRUN) { ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_UNDERRUN); sc->stats.tx_underflow++; ARGEDEBUG(sc, ARGE_DBG_TX, "%s: TX underrun; tx_cnt=%d\n", __func__, sc->arge_cdata.arge_tx_cnt); if (sc->arge_cdata.arge_tx_cnt > 0 ) { ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, DMA_TX_CONTROL_EN); } } /* * If we've finished TXing and there's space for more packets * to be queued for TX, do so. Otherwise we may end up in a * situation where the interface send queue was filled * whilst the hardware queue was full, then the hardware * queue was drained by the interface send queue wasn't, * and thus if_start() is never called to kick-start * the send process (and all subsequent packets are simply * discarded. * * XXX TODO: make sure that the hardware deals nicely * with the possibility of the queue being enabled above * after a TX underrun, then having the hardware queue added * to below. */ if (status & (DMA_INTR_TX_PKT_SENT | DMA_INTR_TX_UNDERRUN) && (ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { if (!IFQ_IS_EMPTY(&ifp->if_snd)) arge_start_locked(ifp); } /* * We handled all bits, clear status */ sc->arge_intr_status = 0; ARGE_UNLOCK(sc); /* * re-enable all interrupts */ ARGE_WRITE(sc, AR71XX_DMA_INTR, DMA_INTR_ALL); } static void arge_tick(void *xsc) { struct arge_softc *sc = xsc; struct mii_data *mii; ARGE_LOCK_ASSERT(sc); if (sc->arge_miibus) { mii = device_get_softc(sc->arge_miibus); mii_tick(mii); callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc); } } int arge_multiphy_mediachange(struct ifnet *ifp) { struct arge_softc *sc = ifp->if_softc; struct ifmedia *ifm = &sc->arge_ifmedia; struct ifmedia_entry *ife = ifm->ifm_cur; if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) return (EINVAL); if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) { device_printf(sc->arge_dev, "AUTO is not supported for multiphy MAC"); return (EINVAL); } /* * Ignore everything */ return (0); } void arge_multiphy_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) { struct arge_softc *sc = ifp->if_softc; ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; ifmr->ifm_active = IFM_ETHER | sc->arge_media_type | sc->arge_duplex_mode; } #if defined(ARGE_MDIO) static int argemdio_probe(device_t dev) { device_set_desc(dev, "Atheros AR71xx built-in ethernet interface, MDIO controller"); return (0); } static int argemdio_attach(device_t dev) { struct arge_softc *sc; int error = 0; sc = device_get_softc(dev); sc->arge_dev = dev; sc->arge_mac_unit = device_get_unit(dev); sc->arge_rid = 0; sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->arge_rid, RF_ACTIVE | RF_SHAREABLE); if (sc->arge_res == NULL) { device_printf(dev, "couldn't map memory\n"); error = ENXIO; goto fail; } /* Reset MAC - required for AR71xx MDIO to successfully occur */ arge_reset_mac(sc); /* Reset MII bus */ arge_reset_miibus(sc); bus_generic_probe(dev); bus_enumerate_hinted_children(dev); error = bus_generic_attach(dev); fail: return (error); } static int argemdio_detach(device_t dev) { return (0); } #endif Index: head/sys/mips/atheros/qca955x_chip.c =================================================================== --- head/sys/mips/atheros/qca955x_chip.c (revision 285120) +++ head/sys/mips/atheros/qca955x_chip.c (revision 285121) @@ -1,400 +1,402 @@ /*- * Copyright (c) 2015 Adrian Chadd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include static void qca955x_chip_detect_mem_size(void) { } static void qca955x_chip_detect_sys_frequency(void) { unsigned long ref_rate; unsigned long cpu_rate; unsigned long ddr_rate; unsigned long ahb_rate; uint32_t pll, out_div, ref_div, nint, frac, clk_ctrl, postdiv; uint32_t cpu_pll, ddr_pll; uint32_t bootstrap; bootstrap = ATH_READ_REG(QCA955X_RESET_REG_BOOTSTRAP); if (bootstrap & QCA955X_BOOTSTRAP_REF_CLK_40) ref_rate = 40 * 1000 * 1000; else ref_rate = 25 * 1000 * 1000; pll = ATH_READ_REG(QCA955X_PLL_CPU_CONFIG_REG); out_div = (pll >> QCA955X_PLL_CPU_CONFIG_OUTDIV_SHIFT) & QCA955X_PLL_CPU_CONFIG_OUTDIV_MASK; ref_div = (pll >> QCA955X_PLL_CPU_CONFIG_REFDIV_SHIFT) & QCA955X_PLL_CPU_CONFIG_REFDIV_MASK; nint = (pll >> QCA955X_PLL_CPU_CONFIG_NINT_SHIFT) & QCA955X_PLL_CPU_CONFIG_NINT_MASK; frac = (pll >> QCA955X_PLL_CPU_CONFIG_NFRAC_SHIFT) & QCA955X_PLL_CPU_CONFIG_NFRAC_MASK; cpu_pll = nint * ref_rate / ref_div; cpu_pll += frac * ref_rate / (ref_div * (1 << 6)); cpu_pll /= (1 << out_div); pll = ATH_READ_REG(QCA955X_PLL_DDR_CONFIG_REG); out_div = (pll >> QCA955X_PLL_DDR_CONFIG_OUTDIV_SHIFT) & QCA955X_PLL_DDR_CONFIG_OUTDIV_MASK; ref_div = (pll >> QCA955X_PLL_DDR_CONFIG_REFDIV_SHIFT) & QCA955X_PLL_DDR_CONFIG_REFDIV_MASK; nint = (pll >> QCA955X_PLL_DDR_CONFIG_NINT_SHIFT) & QCA955X_PLL_DDR_CONFIG_NINT_MASK; frac = (pll >> QCA955X_PLL_DDR_CONFIG_NFRAC_SHIFT) & QCA955X_PLL_DDR_CONFIG_NFRAC_MASK; ddr_pll = nint * ref_rate / ref_div; ddr_pll += frac * ref_rate / (ref_div * (1 << 10)); ddr_pll /= (1 << out_div); clk_ctrl = ATH_READ_REG(QCA955X_PLL_CLK_CTRL_REG); postdiv = (clk_ctrl >> QCA955X_PLL_CLK_CTRL_CPU_POST_DIV_SHIFT) & QCA955X_PLL_CLK_CTRL_CPU_POST_DIV_MASK; if (clk_ctrl & QCA955X_PLL_CLK_CTRL_CPU_PLL_BYPASS) cpu_rate = ref_rate; else if (clk_ctrl & QCA955X_PLL_CLK_CTRL_CPUCLK_FROM_CPUPLL) cpu_rate = ddr_pll / (postdiv + 1); else cpu_rate = cpu_pll / (postdiv + 1); postdiv = (clk_ctrl >> QCA955X_PLL_CLK_CTRL_DDR_POST_DIV_SHIFT) & QCA955X_PLL_CLK_CTRL_DDR_POST_DIV_MASK; if (clk_ctrl & QCA955X_PLL_CLK_CTRL_DDR_PLL_BYPASS) ddr_rate = ref_rate; else if (clk_ctrl & QCA955X_PLL_CLK_CTRL_DDRCLK_FROM_DDRPLL) ddr_rate = cpu_pll / (postdiv + 1); else ddr_rate = ddr_pll / (postdiv + 1); postdiv = (clk_ctrl >> QCA955X_PLL_CLK_CTRL_AHB_POST_DIV_SHIFT) & QCA955X_PLL_CLK_CTRL_AHB_POST_DIV_MASK; if (clk_ctrl & QCA955X_PLL_CLK_CTRL_AHB_PLL_BYPASS) ahb_rate = ref_rate; else if (clk_ctrl & QCA955X_PLL_CLK_CTRL_AHBCLK_FROM_DDRPLL) ahb_rate = ddr_pll / (postdiv + 1); else ahb_rate = cpu_pll / (postdiv + 1); u_ar71xx_ddr_freq = ddr_rate; u_ar71xx_cpu_freq = cpu_rate; u_ar71xx_ahb_freq = ahb_rate; u_ar71xx_wdt_freq = ref_rate; u_ar71xx_uart_freq = ref_rate; u_ar71xx_mdio_freq = ref_rate; u_ar71xx_refclk = ref_rate; } static void qca955x_chip_device_stop(uint32_t mask) { uint32_t reg; reg = ATH_READ_REG(QCA955X_RESET_REG_RESET_MODULE); ATH_WRITE_REG(QCA955X_RESET_REG_RESET_MODULE, reg | mask); } static void qca955x_chip_device_start(uint32_t mask) { uint32_t reg; reg = ATH_READ_REG(QCA955X_RESET_REG_RESET_MODULE); ATH_WRITE_REG(QCA955X_RESET_REG_RESET_MODULE, reg & ~mask); } static int qca955x_chip_device_stopped(uint32_t mask) { uint32_t reg; reg = ATH_READ_REG(QCA955X_RESET_REG_RESET_MODULE); return ((reg & mask) == mask); } static void qca955x_chip_set_mii_speed(uint32_t unit, uint32_t speed) { /* XXX TODO */ return; } static void qca955x_chip_set_pll_ge(int unit, int speed, uint32_t pll) { switch (unit) { case 0: ATH_WRITE_REG(QCA955X_PLL_ETH_XMII_CONTROL_REG, pll); break; case 1: ATH_WRITE_REG(QCA955X_PLL_ETH_SGMII_CONTROL_REG, pll); break; default: printf("%s: invalid PLL set for arge unit: %d\n", __func__, unit); return; } } static void -qca955x_chip_ddr_flush_ge(int unit) +qca955x_chip_ddr_flush(ar71xx_flush_ddr_id_t id) { - switch (unit) { - case 0: + switch (id) { + case AR71XX_CPU_DDR_FLUSH_GE0: ar71xx_ddr_flush(QCA955X_DDR_REG_FLUSH_GE0); break; - case 1: + case AR71XX_CPU_DDR_FLUSH_GE1: ar71xx_ddr_flush(QCA955X_DDR_REG_FLUSH_GE1); break; + case AR71XX_CPU_DDR_FLUSH_USB: + ar71xx_ddr_flush(QCA955X_DDR_REG_FLUSH_USB); + break; + case AR71XX_CPU_DDR_FLUSH_PCIE: + ar71xx_ddr_flush(QCA955X_DDR_REG_FLUSH_PCIE); + break; + case AR71XX_CPU_DDR_FLUSH_WMAC: + ar71xx_ddr_flush(QCA955X_DDR_REG_FLUSH_WMAC); + break; + case AR71XX_CPU_DDR_FLUSH_PCIE_EP: + ar71xx_ddr_flush(QCA955X_DDR_REG_FLUSH_SRC1); + break; + case AR71XX_CPU_DDR_FLUSH_CHECKSUM: + ar71xx_ddr_flush(QCA955X_DDR_REG_FLUSH_SRC2); + break; default: - printf("%s: invalid DDR flush for arge unit: %d\n", - __func__, unit); - return; + printf("%s: invalid flush (%d)\n", __func__, id); } } -/* XXX TODO: USB flush, PCIe flush, wmac flush */ - -static void -qca955x_chip_ddr_flush_ip2(void) -{ -#if 0 - ar71xx_ddr_flush(AR934X_DDR_REG_FLUSH_WMAC); -#endif -} - static uint32_t qca955x_chip_get_eth_pll(unsigned int mac, int speed) { uint32_t pll; switch (speed) { case 10: pll = QCA955X_PLL_VAL_10; break; case 100: pll = QCA955X_PLL_VAL_100; break; case 1000: pll = QCA955X_PLL_VAL_1000; break; default: printf("%s%d: invalid speed %d\n", __func__, mac, speed); pll = 0; } return (pll); } static void qca955x_chip_reset_ethernet_switch(void) { #if 0 ar71xx_device_stop(AR934X_RESET_ETH_SWITCH); DELAY(100); ar71xx_device_start(AR934X_RESET_ETH_SWITCH); DELAY(100); #endif } static void qca955x_configure_gmac(uint32_t gmac_cfg) { uint32_t reg; reg = ATH_READ_REG(QCA955X_GMAC_REG_ETH_CFG); printf("%s: ETH_CFG=0x%08x\n", __func__, reg); reg &= ~(QCA955X_ETH_CFG_RGMII_EN | QCA955X_ETH_CFG_GE0_SGMII); reg |= gmac_cfg; ATH_WRITE_REG(QCA955X_GMAC_REG_ETH_CFG, reg); } static void qca955x_chip_init_usb_peripheral(void) { } static void qca955x_chip_set_mii_if(uint32_t unit, uint32_t mii_mode) { /* * XXX ! * * Nothing to see here; although gmac0 can have its * MII configuration changed, the register values * are slightly different. */ } /* * XXX TODO: fetch default MII divider configuration */ static void qca955x_chip_reset_wmac(void) { /* XXX TODO */ } static void qca955x_chip_init_gmac(void) { long gmac_cfg; if (resource_long_value("qca955x_gmac", 0, "gmac_cfg", &gmac_cfg) == 0) { printf("%s: gmac_cfg=0x%08lx\n", __func__, (long) gmac_cfg); qca955x_configure_gmac((uint32_t) gmac_cfg); } } /* * Reset the NAND Flash Controller. * * + active=1 means "make it active". * + active=0 means "make it inactive". */ static void qca955x_chip_reset_nfc(int active) { #if 0 if (active) { ar71xx_device_start(AR934X_RESET_NANDF); DELAY(100); ar71xx_device_start(AR934X_RESET_ETH_SWITCH_ANALOG); DELAY(250); } else { ar71xx_device_stop(AR934X_RESET_ETH_SWITCH_ANALOG); DELAY(250); ar71xx_device_stop(AR934X_RESET_NANDF); DELAY(100); } #endif } /* * Configure the GPIO output mux setup. * * The QCA955x has an output mux which allowed * certain functions to be configured on any pin. * Specifically, the switch PHY link LEDs and * WMAC external RX LNA switches are not limited to * a specific GPIO pin. */ static void qca955x_chip_gpio_output_configure(int gpio, uint8_t func) { uint32_t reg, s; uint32_t t; if (gpio > QCA955X_GPIO_COUNT) return; reg = QCA955X_GPIO_REG_OUT_FUNC0 + 4 * (gpio / 4); s = 8 * (gpio % 4); /* read-modify-write */ t = ATH_READ_REG(AR71XX_GPIO_BASE + reg); t &= ~(0xff << s); t |= func << s; ATH_WRITE_REG(AR71XX_GPIO_BASE + reg, t); /* flush write */ ATH_READ_REG(AR71XX_GPIO_BASE + reg); } struct ar71xx_cpu_def qca955x_chip_def = { &qca955x_chip_detect_mem_size, &qca955x_chip_detect_sys_frequency, &qca955x_chip_device_stop, &qca955x_chip_device_start, &qca955x_chip_device_stopped, &qca955x_chip_set_pll_ge, &qca955x_chip_set_mii_speed, &qca955x_chip_set_mii_if, - &qca955x_chip_ddr_flush_ge, &qca955x_chip_get_eth_pll, - &qca955x_chip_ddr_flush_ip2, + &qca955x_chip_ddr_flush, &qca955x_chip_init_usb_peripheral, &qca955x_chip_reset_ethernet_switch, &qca955x_chip_reset_wmac, &qca955x_chip_init_gmac, &qca955x_chip_reset_nfc, &qca955x_chip_gpio_output_configure, }; Index: head/sys/mips/atheros/qca955x_pci.c =================================================================== --- head/sys/mips/atheros/qca955x_pci.c (revision 285120) +++ head/sys/mips/atheros/qca955x_pci.c (revision 285121) @@ -1,606 +1,607 @@ /*- * Copyright (c) 2009, Oleksandr Tymoshenko * Copyright (c) 2011, Luiz Otavio O Souza. * Copyright (c) 2015, Adrian Chadd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ar71xx.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" #include /* XXX aim to eliminate this! */ #include #include #include #include #undef AR724X_PCI_DEBUG //#define AR724X_PCI_DEBUG #ifdef AR724X_PCI_DEBUG #define dprintf printf #else #define dprintf(x, arg...) #endif /* * This is a PCI controller for the QCA955x and later SoCs. * It needs to be aware of >1 PCIe host endpoints. * * XXX TODO; it may be nice to merge this with ar724x_pci.c; * they're very similar. */ struct ar71xx_pci_irq { struct ar71xx_pci_softc *sc; int irq; }; struct ar71xx_pci_softc { device_t sc_dev; int sc_busno; struct rman sc_mem_rman; struct rman sc_irq_rman; uint32_t sc_pci_reg_base; /* XXX until bus stuff is done */ uint32_t sc_pci_crp_base; /* XXX until bus stuff is done */ uint32_t sc_pci_ctrl_base; /* XXX until bus stuff is done */ uint32_t sc_pci_mem_base; /* XXX until bus stuff is done */ uint32_t sc_pci_membase_limit; struct intr_event *sc_eventstab[AR71XX_PCI_NIRQS]; mips_intrcnt_t sc_intr_counter[AR71XX_PCI_NIRQS]; struct ar71xx_pci_irq sc_pci_irq[AR71XX_PCI_NIRQS]; struct resource *sc_irq; void *sc_ih; }; static int qca955x_pci_setup_intr(device_t, device_t, struct resource *, int, driver_filter_t *, driver_intr_t *, void *, void **); static int qca955x_pci_teardown_intr(device_t, device_t, struct resource *, void *); static int qca955x_pci_intr(void *); static void qca955x_pci_write(uint32_t reg, uint32_t offset, uint32_t data, int bytes) { uint32_t val, mask, shift; /* Register access is 32-bit aligned */ shift = (offset & 3) * 8; if (bytes % 4) mask = (1 << (bytes * 8)) - 1; else mask = 0xffffffff; val = ATH_READ_REG(reg + (offset & ~3)); val &= ~(mask << shift); val |= ((data & mask) << shift); ATH_WRITE_REG(reg + (offset & ~3), val); dprintf("%s: %#x/%#x addr=%#x, data=%#x(%#x), bytes=%d\n", __func__, reg, reg + (offset & ~3), offset, data, val, bytes); } static uint32_t qca955x_pci_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int bytes) { struct ar71xx_pci_softc *sc = device_get_softc(dev); uint32_t data, shift, mask; /* Register access is 32-bit aligned */ shift = (reg & 3) * 8; /* Create a mask based on the width, post-shift */ if (bytes == 2) mask = 0xffff; else if (bytes == 1) mask = 0xff; else mask = 0xffffffff; dprintf("%s: tag (%x, %x, %x) reg %d(%d)\n", __func__, bus, slot, func, reg, bytes); if ((bus == 0) && (slot == 0) && (func == 0)) data = ATH_READ_REG(sc->sc_pci_reg_base + (reg & ~3)); else data = -1; /* Get request bytes from 32-bit word */ data = (data >> shift) & mask; dprintf("%s: read 0x%x\n", __func__, data); return (data); } static void qca955x_pci_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, uint32_t data, int bytes) { struct ar71xx_pci_softc *sc = device_get_softc(dev); dprintf("%s: tag (%x, %x, %x) reg %d(%d): %x\n", __func__, bus, slot, func, reg, bytes, data); if ((bus != 0) || (slot != 0) || (func != 0)) return; qca955x_pci_write(sc->sc_pci_reg_base, reg, data, bytes); } static void qca955x_pci_mask_irq(void *source) { uint32_t reg; struct ar71xx_pci_irq *pirq = source; struct ar71xx_pci_softc *sc = pirq->sc; /* XXX - Only one interrupt ? Only one device ? */ if (pirq->irq != AR71XX_PCI_IRQ_START) return; /* Update the interrupt mask reg */ reg = ATH_READ_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_INTR_MASK); ATH_WRITE_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_INTR_MASK, reg & ~QCA955X_PCI_INTR_DEV0); /* Clear any pending interrupt */ reg = ATH_READ_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_INTR_STATUS); ATH_WRITE_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_INTR_STATUS, reg | QCA955X_PCI_INTR_DEV0); } static void qca955x_pci_unmask_irq(void *source) { uint32_t reg; struct ar71xx_pci_irq *pirq = source; struct ar71xx_pci_softc *sc = pirq->sc; if (pirq->irq != AR71XX_PCI_IRQ_START) return; /* Update the interrupt mask reg */ reg = ATH_READ_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_INTR_MASK); ATH_WRITE_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_INTR_MASK, reg | QCA955X_PCI_INTR_DEV0); } static int qca955x_pci_setup(device_t dev) { struct ar71xx_pci_softc *sc = device_get_softc(dev); uint32_t reg; /* setup COMMAND register */ reg = PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN | PCIM_CMD_SERRESPEN | PCIM_CMD_BACKTOBACK | PCIM_CMD_PERRESPEN | PCIM_CMD_MWRICEN; qca955x_pci_write(sc->sc_pci_crp_base, PCIR_COMMAND, reg, 2); /* These are the memory/prefetch base/limit parameters */ qca955x_pci_write(sc->sc_pci_crp_base, 0x20, sc->sc_pci_membase_limit, 4); qca955x_pci_write(sc->sc_pci_crp_base, 0x24, sc->sc_pci_membase_limit, 4); reg = ATH_READ_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_RESET); if (reg != 0x7) { DELAY(100000); ATH_WRITE_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_RESET, 0); ATH_READ_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_RESET); DELAY(100); ATH_WRITE_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_RESET, 4); ATH_READ_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_RESET); DELAY(100000); } ATH_WRITE_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_APP, 0x1ffc1); /* Flush write */ (void) ATH_READ_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_APP); DELAY(1000); reg = ATH_READ_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_RESET); if ((reg & QCA955X_PCI_RESET_LINK_UP) == 0) { device_printf(dev, "no PCIe controller found\n"); return (ENXIO); } return (0); } static int qca955x_pci_probe(device_t dev) { return (BUS_PROBE_NOWILDCARD); } static int qca955x_pci_attach(device_t dev) { struct ar71xx_pci_softc *sc = device_get_softc(dev); int unit = device_get_unit(dev); int busno = 0; int rid = 0; /* Dirty; maybe these could all just be hints */ if (unit == 0) { sc->sc_pci_reg_base = QCA955X_PCI_CFG_BASE0; sc->sc_pci_crp_base = QCA955X_PCI_CRP_BASE0; sc->sc_pci_ctrl_base = QCA955X_PCI_CTRL_BASE0; sc->sc_pci_mem_base = QCA955X_PCI_MEM_BASE0; /* XXX verify */ sc->sc_pci_membase_limit = 0x11f01000; } else if (unit == 1) { sc->sc_pci_reg_base = QCA955X_PCI_CFG_BASE1; sc->sc_pci_crp_base = QCA955X_PCI_CRP_BASE1; sc->sc_pci_ctrl_base = QCA955X_PCI_CTRL_BASE1; sc->sc_pci_mem_base = QCA955X_PCI_MEM_BASE1; /* XXX verify */ sc->sc_pci_membase_limit = 0x12f01200; } else { device_printf(dev, "%s: invalid unit (%d)\n", __func__, unit); return (ENXIO); } sc->sc_mem_rman.rm_type = RMAN_ARRAY; sc->sc_mem_rman.rm_descr = "qca955x PCI memory window"; if (rman_init(&sc->sc_mem_rman) != 0 || rman_manage_region(&sc->sc_mem_rman, sc->sc_pci_mem_base, sc->sc_pci_mem_base + QCA955X_PCI_MEM_SIZE - 1) != 0) { panic("qca955x_pci_attach: failed to set up I/O rman"); } sc->sc_irq_rman.rm_type = RMAN_ARRAY; sc->sc_irq_rman.rm_descr = "qca955x PCI IRQs"; if (rman_init(&sc->sc_irq_rman) != 0 || rman_manage_region(&sc->sc_irq_rman, AR71XX_PCI_IRQ_START, AR71XX_PCI_IRQ_END) != 0) panic("qca955x_pci_attach: failed to set up IRQ rman"); /* Disable interrupts */ ATH_WRITE_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_INTR_STATUS, 0); ATH_WRITE_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_INTR_MASK, 0); /* Hook up our interrupt handler. */ if ((sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { device_printf(dev, "unable to allocate IRQ resource\n"); return (ENXIO); } if ((bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC, qca955x_pci_intr, NULL, sc, &sc->sc_ih))) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); return (ENXIO); } /* Reset PCIe core and PCIe PHY */ ar71xx_device_stop(QCA955X_RESET_PCIE); ar71xx_device_stop(QCA955X_RESET_PCIE_PHY); DELAY(100); ar71xx_device_start(QCA955X_RESET_PCIE_PHY); ar71xx_device_start(QCA955X_RESET_PCIE); if (qca955x_pci_setup(dev)) return (ENXIO); /* * Write initial base address. * * I'm not yet sure why this is required and/or why it isn't * initialised like this. The AR71xx PCI code initialises * the PCI windows for each device, but neither it or the * 724x PCI bridge modules explicitly initialise the BAR. * * So before this gets committed, have a chat with jhb@ or * someone else who knows PCI well and figure out whether * the initial BAR is supposed to be determined by /other/ * means. */ qca955x_pci_write_config(dev, 0, 0, 0, PCIR_BAR(0), sc->sc_pci_mem_base, 4); /* Fixup internal PCI bridge */ qca955x_pci_write_config(dev, 0, 0, 0, PCIR_COMMAND, PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN | PCIM_CMD_SERRESPEN | PCIM_CMD_BACKTOBACK | PCIM_CMD_PERRESPEN | PCIM_CMD_MWRICEN, 2); device_add_child(dev, "pci", busno); return (bus_generic_attach(dev)); } static int qca955x_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct ar71xx_pci_softc *sc = device_get_softc(dev); switch (which) { case PCIB_IVAR_DOMAIN: *result = 0; return (0); case PCIB_IVAR_BUS: *result = sc->sc_busno; return (0); } return (ENOENT); } static int qca955x_pci_write_ivar(device_t dev, device_t child, int which, uintptr_t result) { struct ar71xx_pci_softc * sc = device_get_softc(dev); switch (which) { case PCIB_IVAR_BUS: sc->sc_busno = result; return (0); } return (ENOENT); } static struct resource * qca955x_pci_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct ar71xx_pci_softc *sc = device_get_softc(bus); struct resource *rv; struct rman *rm; switch (type) { case SYS_RES_IRQ: rm = &sc->sc_irq_rman; break; case SYS_RES_MEMORY: rm = &sc->sc_mem_rman; break; default: return (NULL); } rv = rman_reserve_resource(rm, start, end, count, flags, child); if (rv == NULL) return (NULL); rman_set_rid(rv, *rid); if (flags & RF_ACTIVE) { if (bus_activate_resource(child, type, *rid, rv)) { rman_release_resource(rv); return (NULL); } } return (rv); } static int qca955x_pci_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { int res = (BUS_ACTIVATE_RESOURCE(device_get_parent(bus), child, type, rid, r)); if (!res) { switch(type) { case SYS_RES_MEMORY: case SYS_RES_IOPORT: rman_set_bustag(r, ar71xx_bus_space_pcimem); break; } } return (res); } static int qca955x_pci_setup_intr(device_t bus, device_t child, struct resource *ires, int flags, driver_filter_t *filt, driver_intr_t *handler, void *arg, void **cookiep) { struct ar71xx_pci_softc *sc = device_get_softc(bus); struct intr_event *event; int irq, error; irq = rman_get_start(ires); if (irq > AR71XX_PCI_IRQ_END) panic("%s: bad irq %d", __func__, irq); event = sc->sc_eventstab[irq]; if (event == NULL) { sc->sc_pci_irq[irq].sc = sc; sc->sc_pci_irq[irq].irq = irq; error = intr_event_create(&event, (void *)&sc->sc_pci_irq[irq], 0, irq, qca955x_pci_mask_irq, qca955x_pci_unmask_irq, NULL, NULL, "pci intr%d:", irq); if (error == 0) { sc->sc_eventstab[irq] = event; sc->sc_intr_counter[irq] = mips_intrcnt_create(event->ie_name); } else return error; } intr_event_add_handler(event, device_get_nameunit(child), filt, handler, arg, intr_priority(flags), flags, cookiep); mips_intrcnt_setname(sc->sc_intr_counter[irq], event->ie_fullname); qca955x_pci_unmask_irq(&sc->sc_pci_irq[irq]); return (0); } static int qca955x_pci_teardown_intr(device_t dev, device_t child, struct resource *ires, void *cookie) { struct ar71xx_pci_softc *sc = device_get_softc(dev); int irq, result; irq = rman_get_start(ires); if (irq > AR71XX_PCI_IRQ_END) panic("%s: bad irq %d", __func__, irq); if (sc->sc_eventstab[irq] == NULL) panic("Trying to teardown unoccupied IRQ"); qca955x_pci_mask_irq(&sc->sc_pci_irq[irq]); result = intr_event_remove_handler(cookie); if (!result) sc->sc_eventstab[irq] = NULL; return (result); } static int qca955x_pci_intr(void *arg) { struct ar71xx_pci_softc *sc = arg; struct intr_event *event; uint32_t reg, irq, mask; - /* XXX TODO - may need to flush a different handler? */ - ar71xx_device_ddr_flush_ip2(); + /* There's only one PCIe DDR flush for both PCIe EPs */ + ar71xx_device_flush_ddr(AR71XX_CPU_DDR_FLUSH_PCIE); reg = ATH_READ_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_INTR_STATUS); mask = ATH_READ_REG(sc->sc_pci_ctrl_base + QCA955X_PCI_INTR_MASK); + /* * Handle only unmasked interrupts */ reg &= mask; /* * XXX TODO: handle >1 PCIe end point! */ if (reg & QCA955X_PCI_INTR_DEV0) { irq = AR71XX_PCI_IRQ_START; event = sc->sc_eventstab[irq]; if (!event || TAILQ_EMPTY(&event->ie_handlers)) { printf("Stray IRQ %d\n", irq); return (FILTER_STRAY); } /* TODO: frame instead of NULL? */ intr_event_handle(event, NULL); mips_intrcnt_inc(sc->sc_intr_counter[irq]); } return (FILTER_HANDLED); } static int qca955x_pci_maxslots(device_t dev) { return (PCI_SLOTMAX); } static int qca955x_pci_route_interrupt(device_t pcib, device_t device, int pin) { return (pci_get_slot(device)); } static device_method_t qca955x_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, qca955x_pci_probe), DEVMETHOD(device_attach, qca955x_pci_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_read_ivar, qca955x_pci_read_ivar), DEVMETHOD(bus_write_ivar, qca955x_pci_write_ivar), DEVMETHOD(bus_alloc_resource, qca955x_pci_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, qca955x_pci_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, qca955x_pci_setup_intr), DEVMETHOD(bus_teardown_intr, qca955x_pci_teardown_intr), /* pcib interface */ DEVMETHOD(pcib_maxslots, qca955x_pci_maxslots), DEVMETHOD(pcib_read_config, qca955x_pci_read_config), DEVMETHOD(pcib_write_config, qca955x_pci_write_config), DEVMETHOD(pcib_route_interrupt, qca955x_pci_route_interrupt), DEVMETHOD_END }; static driver_t qca955x_pci_driver = { "pcib", qca955x_pci_methods, sizeof(struct ar71xx_pci_softc), }; static devclass_t qca955x_pci_devclass; DRIVER_MODULE(qca955x_pci, nexus, qca955x_pci_driver, qca955x_pci_devclass, 0, 0); DRIVER_MODULE(qca955x_pci, apb, qca955x_pci_driver, qca955x_pci_devclass, 0, 0);