diff options
Diffstat (limited to 'sys/mips/atheros')
-rw-r--r-- | sys/mips/atheros/apb.c | 433 | ||||
-rw-r--r-- | sys/mips/atheros/apbvar.h | 48 | ||||
-rw-r--r-- | sys/mips/atheros/ar71xx_machdep.c | 161 | ||||
-rw-r--r-- | sys/mips/atheros/ar71xx_ohci.c | 205 | ||||
-rw-r--r-- | sys/mips/atheros/ar71xx_pci.c | 429 | ||||
-rw-r--r-- | sys/mips/atheros/ar71xxreg.h | 317 | ||||
-rw-r--r-- | sys/mips/atheros/files.ar71xx | 9 | ||||
-rw-r--r-- | sys/mips/atheros/if_arge.c | 1657 | ||||
-rw-r--r-- | sys/mips/atheros/if_argevar.h | 138 | ||||
-rw-r--r-- | sys/mips/atheros/uart_bus_ar71xx.c | 79 | ||||
-rw-r--r-- | sys/mips/atheros/uart_cpu_ar71xx.c | 78 |
11 files changed, 3554 insertions, 0 deletions
diff --git a/sys/mips/atheros/apb.c b/sys/mips/atheros/apb.c new file mode 100644 index 0000000..d53408c --- /dev/null +++ b/sys/mips/atheros/apb.c @@ -0,0 +1,433 @@ +/*- + * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/interrupt.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/malloc.h> + +#include <machine/bus.h> + +#include <mips/atheros/apbvar.h> +#include <mips/atheros/ar71xxreg.h> + +#undef APB_DEBUG +#ifdef APB_DEBUG +#define dprintf printf +#else +#define dprintf(x, arg...) +#endif /* APB_DEBUG */ + +static int apb_activate_resource(device_t, device_t, int, int, + struct resource *); +static device_t apb_add_child(device_t, int, const char *, int); +static struct resource * + apb_alloc_resource(device_t, device_t, int, int *, u_long, + u_long, u_long, u_int); +static int apb_attach(device_t); +static int apb_deactivate_resource(device_t, device_t, int, int, + struct resource *); +static struct resource_list * + apb_get_resource_list(device_t, device_t); +static void apb_hinted_child(device_t, const char *, int); +static int apb_intr(void *); +static int apb_probe(device_t); +static int apb_release_resource(device_t, device_t, int, int, + struct resource *); +static int apb_setup_intr(device_t, device_t, struct resource *, int, + driver_filter_t *, driver_intr_t *, void *, void **); +static int apb_teardown_intr(device_t, device_t, struct resource *, + void *); + +static void apb_mask_irq(unsigned int irq) +{ + uint32_t reg; + + reg = ATH_READ_REG(AR71XX_MISC_INTR_MASK); + ATH_WRITE_REG(AR71XX_MISC_INTR_MASK, reg & ~(1 << irq)); + +} + +static void apb_unmask_irq(unsigned int irq) +{ + uint32_t reg; + + reg = ATH_READ_REG(AR71XX_MISC_INTR_MASK); + ATH_WRITE_REG(AR71XX_MISC_INTR_MASK, reg | (1 << irq)); +} + +static int +apb_probe(device_t dev) +{ + + return (0); +} + +static int +apb_attach(device_t dev) +{ + struct apb_softc *sc = device_get_softc(dev); + int rid = 0; + + device_set_desc(dev, "APB Bus bridge"); + sc->apb_irq_rman.rm_type = RMAN_ARRAY; + sc->apb_irq_rman.rm_descr = "APB IRQ"; + + if (rman_init(&sc->apb_irq_rman) != 0 || + rman_manage_region(&sc->apb_irq_rman, + APB_IRQ_BASE, APB_IRQ_END) != 0) + panic("apb_attach: failed to set up IRQ rman"); + + if ((sc->sc_misc_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_misc_irq, INTR_TYPE_MISC, + apb_intr, NULL, sc, &sc->sc_misc_ih))) { + device_printf(dev, + "WARNING: unable to register interrupt handler\n"); + return (ENXIO); + } + + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + bus_generic_attach(dev); + + return (0); +} + +static struct resource * +apb_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 apb_softc *sc = device_get_softc(bus); + struct apb_ivar *ivar = device_get_ivars(child); + struct resource *rv; + struct resource_list_entry *rle; + struct rman *rm; + int isdefault, needactivate, passthrough; + + isdefault = (start == 0UL && end == ~0UL); + needactivate = flags & RF_ACTIVE; + /* + * Pass memory requests to nexus device + */ + passthrough = (device_get_parent(child) != bus) || + (type == SYS_RES_MEMORY); + rle = NULL; + + dprintf("%s: entry (%p, %p, %d, %p, %p, %p, %ld, %d)\n", + __func__, bus, child, type, rid, (void *)(intptr_t)start, + (void *)(intptr_t)end, count, flags); + + if (passthrough) + return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, + rid, start, end, count, flags)); + + /* + * If this is an allocation of the "default" range for a given RID, + * and we know what the resources for this device are (ie. they aren't + * maintained by a child bus), then work out the start/end values. + */ + + if (isdefault) { + rle = resource_list_find(&ivar->resources, type, *rid); + if (rle == NULL) { + return (NULL); + } + + if (rle->res != NULL) { + panic("%s: resource entry is busy", __func__); + } + start = rle->start; + end = rle->end; + count = rle->count; + + dprintf("%s: default resource (%p, %p, %ld)\n", + __func__, (void *)(intptr_t)start, + (void *)(intptr_t)end, count); + } + + switch (type) { + case SYS_RES_IRQ: + rm = &sc->apb_irq_rman; + break; + default: + printf("%s: unknown resource type %d\n", __func__, type); + return (0); + } + + rv = rman_reserve_resource(rm, start, end, count, flags, child); + if (rv == 0) { + printf("%s: could not reserve resource\n", __func__); + return (0); + } + + rman_set_rid(rv, *rid); + + if (needactivate) { + if (bus_activate_resource(child, type, *rid, rv)) { + printf("%s: could not activate resource\n", __func__); + rman_release_resource(rv); + return (0); + } + } + + return (rv); +} + +static int +apb_activate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + + /* XXX: should we mask/unmask IRQ here? */ + return (BUS_ACTIVATE_RESOURCE(device_get_parent(bus), child, + type, rid, r)); +} + +static int +apb_deactivate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + + /* XXX: should we mask/unmask IRQ here? */ + return (BUS_DEACTIVATE_RESOURCE(device_get_parent(bus), child, + type, rid, r)); +} + +static int +apb_release_resource(device_t dev, device_t child, int type, + int rid, struct resource *r) +{ + struct resource_list *rl; + struct resource_list_entry *rle; + + rl = apb_get_resource_list(dev, child); + if (rl == NULL) + return (EINVAL); + rle = resource_list_find(rl, type, rid); + if (rle == NULL) + return (EINVAL); + rman_release_resource(r); + rle->res = NULL; + + return (0); +} + +static int +apb_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 apb_softc *sc = device_get_softc(bus); + struct intr_event *event; + int irq, error; + + irq = rman_get_start(ires); + + if (irq > APB_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, + (mask_fn)apb_mask_irq, (mask_fn)apb_unmask_irq, + NULL, NULL, + "apb intr%d:", irq); + + sc->sc_eventstab[irq] = event; + } + + intr_event_add_handler(event, device_get_nameunit(child), filt, + handler, arg, intr_priority(flags), flags, cookiep); + + return (0); +} + +static int +apb_teardown_intr(device_t dev, device_t child, struct resource *ires, + void *cookie) +{ + struct apb_softc *sc = device_get_softc(dev); + int irq, result; + + irq = rman_get_start(ires); + if (irq > APB_IRQ_END) + panic("%s: bad irq %d", __func__, irq); + + if (sc->sc_eventstab[irq] == NULL) + panic("Trying to teardown unoccupied IRQ"); + + apb_mask_irq(irq); + + result = intr_event_remove_handler(cookie); + if (!result) + sc->sc_eventstab[irq] = NULL; + + return (result); +} + +static int +apb_intr(void *arg) +{ + struct apb_softc *sc = arg; + struct intr_event *event; + uint32_t reg, irq; + + reg = ATH_READ_REG(AR71XX_MISC_INTR_STATUS); + for (irq = 0; irq < APB_NIRQS; irq++) { + if (reg & (1 << irq)) { + event = sc->sc_eventstab[irq]; + if (!event || TAILQ_EMPTY(&event->ie_handlers)) { + printf("Stray IRQ %d\n", irq); + continue; + } + + /* TODO: frame instead of NULL? */ + intr_event_handle(event, NULL); + } + } + + return (FILTER_HANDLED); +} + +static void +apb_hinted_child(device_t bus, const char *dname, int dunit) +{ + device_t child; + long maddr; + int msize; + int irq; + int result; + int mem_hints_count; + + child = BUS_ADD_CHILD(bus, 0, dname, dunit); + + /* + * Set hard-wired resources for hinted child using + * specific RIDs. + */ + mem_hints_count = 0; + if (resource_long_value(dname, dunit, "maddr", &maddr) == 0) + mem_hints_count++; + if (resource_int_value(dname, dunit, "msize", &msize) == 0) + mem_hints_count++; + + /* check if all info for mem resource has been provided */ + if ((mem_hints_count > 0) && (mem_hints_count < 2)) { + printf("Either maddr or msize hint is missing for %s%d\n", + dname, dunit); + } else if (mem_hints_count) { + result = bus_set_resource(child, SYS_RES_MEMORY, 0, + maddr, msize); + if (result != 0) + device_printf(bus, + "warning: bus_set_resource() failed\n"); + } + + if (resource_int_value(dname, dunit, "irq", &irq) == 0) { + result = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1); + if (result != 0) + device_printf(bus, + "warning: bus_set_resource() failed\n"); + } +} + +static device_t +apb_add_child(device_t bus, int order, const char *name, int unit) +{ + device_t child; + struct apb_ivar *ivar; + + ivar = malloc(sizeof(struct apb_ivar), M_DEVBUF, M_WAITOK | M_ZERO); + if (ivar == NULL) { + printf("Failed to allocate ivar\n"); + return (0); + } + resource_list_init(&ivar->resources); + + child = device_add_child_ordered(bus, order, name, unit); + if (child == NULL) { + printf("Can't add child %s%d ordered\n", name, unit); + return (0); + } + + device_set_ivars(child, ivar); + + return (child); +} + +/* + * Helper routine for bus_generic_rl_get_resource/bus_generic_rl_set_resource + * Provides pointer to resource_list for these routines + */ +static struct resource_list * +apb_get_resource_list(device_t dev, device_t child) +{ + struct apb_ivar *ivar; + + ivar = device_get_ivars(child); + return (&(ivar->resources)); +} + +static device_method_t apb_methods[] = { + DEVMETHOD(bus_activate_resource, apb_activate_resource), + DEVMETHOD(bus_add_child, apb_add_child), + DEVMETHOD(bus_alloc_resource, apb_alloc_resource), + DEVMETHOD(bus_deactivate_resource, apb_deactivate_resource), + DEVMETHOD(bus_get_resource_list, apb_get_resource_list), + DEVMETHOD(bus_hinted_child, apb_hinted_child), + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_release_resource, apb_release_resource), + DEVMETHOD(bus_setup_intr, apb_setup_intr), + DEVMETHOD(bus_teardown_intr, apb_teardown_intr), + DEVMETHOD(device_attach, apb_attach), + DEVMETHOD(device_probe, apb_probe), + DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), + DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), + + {0, 0}, +}; + +static driver_t apb_driver = { + "apb", + apb_methods, + sizeof(struct apb_softc), +}; +static devclass_t apb_devclass; + +DRIVER_MODULE(apb, nexus, apb_driver, apb_devclass, 0, 0); diff --git a/sys/mips/atheros/apbvar.h b/sys/mips/atheros/apbvar.h new file mode 100644 index 0000000..e1d29f4 --- /dev/null +++ b/sys/mips/atheros/apbvar.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> + * 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. + */ + +#ifndef _APBVAR_H_ +#define _APBVAR_H_ + +#define APB_IRQ_BASE 0 +#define APB_IRQ_END 7 +#define APB_NIRQS 8 + +struct apb_softc { + struct rman apb_irq_rman; + /* IRQ events structs for child devices */ + struct intr_event *sc_eventstab[APB_NIRQS]; + /* Resources and cookies for MIPS CPU INTs */ + struct resource *sc_misc_irq; + void *sc_misc_ih; +}; + +struct apb_ivar { + struct resource_list resources; +}; + +#endif /* _APBVAR_H_ */ diff --git a/sys/mips/atheros/ar71xx_machdep.c b/sys/mips/atheros/ar71xx_machdep.c new file mode 100644 index 0000000..a5699ed --- /dev/null +++ b/sys/mips/atheros/ar71xx_machdep.c @@ -0,0 +1,161 @@ +/*- + * 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, 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <machine/cpuregs.h> + +#include <mips/sentry5/s5reg.h> + +#include "opt_ddb.h" + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/cons.h> +#include <sys/kdb.h> + +#include <vm/vm.h> +#include <vm/vm_page.h> + +#include <machine/clock.h> +#include <machine/cpu.h> +#include <machine/hwfunc.h> +#include <machine/md_var.h> +#include <machine/trap.h> +#include <machine/vmparam.h> + +#include <mips/atheros/ar71xxreg.h> + +extern int *edata; +extern int *end; + +void +platform_halt(void) +{ + +} + +void +platform_identify(void) +{ + +} + +void +platform_reset(void) +{ + uint32_t reg = ATH_READ_REG(AR71XX_RST_RESET); + + ATH_WRITE_REG(AR71XX_RST_RESET, reg | RST_RESET_FULL_CHIP); + /* Wait for reset */ + while(1) + ; +} + +void +platform_trap_enter(void) +{ + +} + +void +platform_trap_exit(void) +{ + +} + +void +platform_start(__register_t a0 __unused, __register_t a1 __unused, + __register_t a2 __unused, __register_t a3 __unused) +{ + vm_offset_t kernend; + uint64_t platform_counter_freq; + uint32_t reg; + + /* clear the BSS and SBSS segments */ + kernend = round_page((vm_offset_t)&end); + memset(&edata, 0, kernend - (vm_offset_t)(&edata)); + + /* TODO: Get available memory from RedBoot. Is it possible? */ + realmem = btoc(64*1024*1024); + /* phys_avail regions are in bytes */ + phys_avail[0] = MIPS_KSEG0_TO_PHYS((vm_offset_t)&end); + phys_avail[1] = ctob(realmem); + + physmem = realmem; + + /* + * ns8250 uart code uses DELAY so ticker should be inititalized + * before cninit. And tick_init_params refers to hz, so * init_param1 + * should be called first. + */ + init_param1(); + /* TODO: Get CPU freq from RedBoot. Is it possible? */ + platform_counter_freq = 680000000UL; + mips_timer_init_params(platform_counter_freq, 0); + cninit(); + + printf("arguments: \n"); + printf(" a0 = %08x\n", a0); + printf(" a1 = %08x\n", a1); + printf(" a2 = %08x\n", a2); + printf(" a3 = %08x\n", a3); + + init_param2(physmem); + mips_cpu_init(); + pmap_bootstrap(); + mips_proc0_init(); + mutex_init(); + + /* + * Reset USB devices + */ + reg = ATH_READ_REG(AR71XX_RST_RESET); + reg |= + RST_RESET_USB_OHCI_DLL | RST_RESET_USB_HOST | RST_RESET_USB_PHY; + ATH_WRITE_REG(AR71XX_RST_RESET, reg); + DELAY(1000); + reg &= + ~(RST_RESET_USB_OHCI_DLL | RST_RESET_USB_HOST | RST_RESET_USB_PHY); + ATH_WRITE_REG(AR71XX_RST_RESET, reg); + + 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); + +#ifdef DDB + kdb_init(); +#endif +} diff --git a/sys/mips/atheros/ar71xx_ohci.c b/sys/mips/atheros/ar71xx_ohci.c new file mode 100644 index 0000000..b7adc61 --- /dev/null +++ b/sys/mips/atheros/ar71xx_ohci.c @@ -0,0 +1,205 @@ +/*- + * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/bus.h> +#include <sys/queue.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usb_mem.h> + +#include <dev/usb/ohcireg.h> +#include <dev/usb/ohcivar.h> + +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 int +ar71xx_ohci_attach(device_t dev) +{ + struct ar71xx_ohci_softc *sc = device_get_softc(dev); + int err; + int rid; + + rid = 0; + sc->sc_ohci.io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->sc_ohci.io_res == NULL) { + err = ENOMEM; + goto error; + } + sc->sc_ohci.iot = rman_get_bustag(sc->sc_ohci.io_res); + sc->sc_ohci.ioh = rman_get_bushandle(sc->sc_ohci.io_res); + + rid = 0; + sc->sc_ohci.irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->sc_ohci.irq_res == NULL) { + err = ENOMEM; + goto error; + } + sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usb", -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); + + /* Allocate a parent dma tag for DMA maps */ + err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0, + NULL, NULL, &sc->sc_ohci.sc_bus.parent_dmatag); + if (err) { + device_printf(dev, "Could not allocate parent DMA tag (%d)\n", + err); + err = ENXIO; + goto error; + } + + /* Allocate a dma tag for transfer buffers */ + err = bus_dma_tag_create(sc->sc_ohci.sc_bus.parent_dmatag, 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0, + busdma_lock_mutex, &Giant, &sc->sc_ohci.sc_bus.buffer_dmatag); + if (err) { + device_printf(dev, "Could not allocate transfer tag (%d)\n", + err); + err = ENXIO; + goto error; + } + + err = bus_setup_intr(dev, sc->sc_ohci.irq_res, INTR_TYPE_BIO, NULL, + ohci_intr, sc, &sc->sc_ohci.ih); + 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.iot, sc->sc_ohci.ioh, OHCI_CONTROL, 0); + + err = ohci_init(&sc->sc_ohci); + if (!err) { + sc->sc_ohci.sc_flags |= OHCI_SCFLG_DONEINIT; + err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev); + } + +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); + + if (sc->sc_ohci.sc_flags & OHCI_SCFLG_DONEINIT) { + ohci_detach(&sc->sc_ohci, 0); + sc->sc_ohci.sc_flags &= ~OHCI_SCFLG_DONEINIT; + } + + if (sc->sc_ohci.ih) { + bus_teardown_intr(dev, sc->sc_ohci.irq_res, sc->sc_ohci.ih); + sc->sc_ohci.ih = NULL; + } + + if (sc->sc_ohci.sc_bus.parent_dmatag != NULL) + bus_dma_tag_destroy(sc->sc_ohci.sc_bus.parent_dmatag); + if (sc->sc_ohci.sc_bus.buffer_dmatag != NULL) + bus_dma_tag_destroy(sc->sc_ohci.sc_bus.buffer_dmatag); + + if (sc->sc_ohci.sc_bus.bdev) { + device_delete_child(dev, sc->sc_ohci.sc_bus.bdev); + sc->sc_ohci.sc_bus.bdev = NULL; + } + if (sc->sc_ohci.irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.irq_res); + sc->sc_ohci.irq_res = NULL; + } + if (sc->sc_ohci.io_res) { + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_ohci.io_res); + sc->sc_ohci.io_res = NULL; + sc->sc_ohci.iot = 0; + sc->sc_ohci.ioh = 0; + } + 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_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + {0, 0} +}; + +static driver_t ohci_driver = { + "ohci", + ohci_methods, + sizeof(struct ar71xx_ohci_softc), +}; + +static devclass_t ohci_devclass; + +DRIVER_MODULE(ohci, apb, ohci_driver, ohci_devclass, 0, 0); diff --git a/sys/mips/atheros/ar71xx_pci.c b/sys/mips/atheros/ar71xx_pci.c new file mode 100644 index 0000000..d3a9295 --- /dev/null +++ b/sys/mips/atheros/ar71xx_pci.c @@ -0,0 +1,429 @@ +/*- + * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> + +#include <sys/bus.h> +#include <sys/interrupt.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/rman.h> + +#include <vm/vm.h> +#include <vm/pmap.h> +#include <vm/vm_extern.h> + +#include <machine/bus.h> +#include <machine/cpu.h> +#include <machine/pmap.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> + +#include <dev/pci/pcib_private.h> +#include "pcib_if.h" + +#include "mips/atheros/ar71xxreg.h" + +#undef AR71XX_PCI_DEBUG +#ifdef AR71XX_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 resource *sc_irq; + void *sc_ih; +}; + +/* + * 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; + 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)); + 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, int bus, int slot, int func, int reg, + int bytes) +{ + uint32_t data; + uint32_t cmd, shift, mask; + + /* register access is 32-bit aligned */ + shift = (reg & 3) * 8; + if (shift) + mask = (1 << shift) - 1; + 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)) { + cmd = PCI_LCONF_CMD_READ | (reg & ~3); + ATH_WRITE_REG(AR71XX_PCI_LCONF_CMD, cmd); + data = ATH_READ_REG(AR71XX_PCI_LCONF_READ_DATA); + } else { + 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; + } + + /* 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_write_config(device_t dev, int bus, int slot, int func, int reg, + uint32_t data, int bytes) +{ + uint32_t cmd; + + dprintf("%s: tag (%x, %x, %x) reg %d(%d)\n", __func__, bus, slot, + func, reg, bytes); + + data = data << (8*(reg % 4)); + + if ((bus == 0) && (slot == 0) && (func == 0)) { + cmd = PCI_LCONF_CMD_WRITE | (reg & ~3); + cmd |= ar71xx_get_bytes_to_read(reg, bytes) << 20; + ATH_WRITE_REG(AR71XX_PCI_LCONF_CMD, cmd); + ATH_WRITE_REG(AR71XX_PCI_LCONF_WRITE_DATA, data); + } else { + if (ar71xx_pci_conf_setup(bus, slot, func, reg, bytes, + PCI_CONF_CMD_WRITE) == 0) + ATH_WRITE_REG(AR71XX_PCI_CONF_WRITE_DATA, data); + } +} + +static int +at71xx_pci_intr(void *v) +{ + panic("Implement me: %s\n", __func__); + return FILTER_HANDLED; +} + +static int +ar71xx_pci_probe(device_t dev) +{ + + return (0); +} + +static int +ar71xx_pci_attach(device_t dev) +{ + int busno = 0; + int rid = 0; + uint32_t reset; + 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"); + + + 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, + at71xx_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 */ + reset = ATH_READ_REG(AR71XX_RST_RESET); + reset |= (RST_RESET_PCI_CORE | RST_RESET_PCI_BUS); + ATH_WRITE_REG(AR71XX_RST_RESET, reset); + DELAY(1000); + + reset &= ~(RST_RESET_PCI_CORE | RST_RESET_PCI_BUS); + ATH_WRITE_REG(AR71XX_RST_RESET, reset); + DELAY(1000); + + /* 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(1000); + + ar71xx_pci_check_bus_error(); + + /* Fixup internal PCI bridge */ + ar71xx_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 +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 = NULL; + 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_teardown_intr(device_t dev, device_t child, struct resource *res, + void *cookie) +{ + + return (intr_event_remove_handler(cookie)); +} + +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) +{ + + return (pin); +} + +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_print_child, bus_generic_print_child), + 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, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_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), + + {0, 0} +}; + +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); diff --git a/sys/mips/atheros/ar71xxreg.h b/sys/mips/atheros/ar71xxreg.h new file mode 100644 index 0000000..9367792 --- /dev/null +++ b/sys/mips/atheros/ar71xxreg.h @@ -0,0 +1,317 @@ +/*- + * 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, 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. + */ +#ifndef _AR71XX_REG_H_ +#define _AR71XX_REG_H_ + +#define ATH_READ_REG(reg) \ + *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1((reg))) + +#define ATH_WRITE_REG(reg, val) \ + *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1((reg))) = (val) + +/* PCI region */ +#define AR71XX_PCI_MEM_BASE 0x10000000 +/* + * PCI mem windows is 0x08000000 bytes long but we exclude control + * region from the resource manager + */ +#define AR71XX_PCI_MEM_SIZE 0x07000000 +#define AR71XX_PCI_IRQ_START 0 +#define AR71XX_PCI_IRQ_END 2 + +/* PCI config registers */ +#define AR71XX_PCI_LCONF_CMD 0x17010000 +#define PCI_LCONF_CMD_READ 0x00000000 +#define PCI_LCONF_CMD_WRITE 0x00010000 +#define AR71XX_PCI_LCONF_WRITE_DATA 0x17010004 +#define AR71XX_PCI_LCONF_READ_DATA 0x17010008 +#define AR71XX_PCI_CONF_ADDR 0x1701000C +#define AR71XX_PCI_CONF_CMD 0x17010010 +#define PCI_CONF_CMD_READ 0x0000000A +#define PCI_CONF_CMD_WRITE 0x0000000B +#define AR71XX_PCI_CONF_WRITE_DATA 0x17010014 +#define AR71XX_PCI_CONF_READ_DATA 0x17010018 +#define AR71XX_PCI_ERROR 0x1701001C +#define AR71XX_PCI_ERROR_ADDR 0x17010020 +#define AR71XX_PCI_AHB_ERROR 0x17010024 +#define AR71XX_PCI_AHB_ERROR_ADDR 0x17010028 + +/* APB region */ +/* DDR registers */ +#define AR71XX_DDR_CONFIG 0x18000000 +#define AR71XX_DDR_CONFIG2 0x18000004 +#define AR71XX_DDR_MODE_REGISTER 0x18000008 +#define AR71XX_DDR_EXT_MODE_REGISTER 0x1800000C +#define AR71XX_DDR_CONTROL 0x18000010 +#define AR71XX_DDR_REFRESH 0x18000014 +#define AR71XX_DDR_RD_DATA_THIS_CYCLE 0x18000018 +#define AR71XX_TAP_CONTROL0 0x1800001C +#define AR71XX_TAP_CONTROL1 0x18000020 +#define AR71XX_TAP_CONTROL2 0x18000024 +#define AR71XX_TAP_CONTROL3 0x18000028 +#define AR71XX_PCI_WINDOW0 0x1800007C +#define AR71XX_PCI_WINDOW1 0x18000080 +#define AR71XX_PCI_WINDOW2 0x18000084 +#define AR71XX_PCI_WINDOW3 0x18000088 +#define AR71XX_PCI_WINDOW4 0x1800008C +#define AR71XX_PCI_WINDOW5 0x18000090 +#define AR71XX_PCI_WINDOW6 0x18000094 +#define AR71XX_PCI_WINDOW7 0x18000098 +#define AR71XX_WB_FLUSH_GE0 0x1800009C +#define AR71XX_WB_FLUSH_GE1 0x180000A0 +#define AR71XX_WB_FLUSH_USB 0x180000A4 +#define AR71XX_WB_FLUSH_PCI 0x180000A8 + +/* + * Values for PCI_WINDOW_X registers + */ +#define PCI_WINDOW0_ADDR 0x10000000 +#define PCI_WINDOW1_ADDR 0x11000000 +#define PCI_WINDOW2_ADDR 0x12000000 +#define PCI_WINDOW3_ADDR 0x13000000 +#define PCI_WINDOW4_ADDR 0x14000000 +#define PCI_WINDOW5_ADDR 0x15000000 +#define PCI_WINDOW6_ADDR 0x16000000 +#define PCI_WINDOW7_ADDR 0x17000000 +/* This value enables acces to PCI config registers */ +#define PCI_WINDOW7_CONF_ADDR 0x07000000 + +#define AR71XX_UART_ADDR 0x18020000 + +#define AR71XX_USB_CTRL_FLADJ 0x18030000 +#define USB_CTRL_FLADJ_HOST_SHIFT 12 +#define USB_CTRL_FLADJ_A5_SHIFT 10 +#define USB_CTRL_FLADJ_A4_SHIFT 8 +#define USB_CTRL_FLADJ_A3_SHIFT 6 +#define USB_CTRL_FLADJ_A2_SHIFT 4 +#define USB_CTRL_FLADJ_A1_SHIFT 2 +#define USB_CTRL_FLADJ_A0_SHIFT 0 +#define AR71XX_USB_CTRL_CONFIG 0x18030004 +#define USB_CTRL_CONFIG_OHCI_DES_SWAP (1 << 19) +#define USB_CTRL_CONFIG_OHCI_BUF_SWAP (1 << 18) +#define USB_CTRL_CONFIG_EHCI_DES_SWAP (1 << 17) +#define USB_CTRL_CONFIG_EHCI_BUF_SWAP (1 << 16) +#define USB_CTRL_CONFIG_DISABLE_XTL (1 << 13) +#define USB_CTRL_CONFIG_OVERRIDE_XTL (1 << 12) +#define USB_CTRL_CONFIG_CLK_SEL_SHIFT 4 +#define USB_CTRL_CONFIG_CLK_SEL_MASK 3 +#define USB_CTRL_CONFIG_CLK_SEL_12 0 +#define USB_CTRL_CONFIG_CLK_SEL_24 1 +#define USB_CTRL_CONFIG_CLK_SEL_48 2 +#define USB_CTRL_CONFIG_OVER_CURRENT_AS_GPIO (1 << 8) +#define USB_CTRL_CONFIG_SS_SIMULATION_MODE (1 << 2) +#define USB_CTRL_CONFIG_RESUME_UTMI_PLS_DIS (1 << 1) +#define USB_CTRL_CONFIG_UTMI_BACKWARD_ENB (1 << 0) + +#define AR71XX_PLL_CPU_CONFIG 0x18050000 +#define AR71XX_PLL_SEC_CONFIG 0x18050004 +#define AR71XX_PLL_CPU_CLK_CTRL 0x18050008 +#define AR71XX_PLL_ETH_INT0_CLK 0x18050010 +#define AR71XX_PLL_ETH_INT1_CLK 0x18050014 +#define XPLL_ETH_INT_CLK_10 0x00991099 +#define XPLL_ETH_INT_CLK_100 0x00441011 +#define XPLL_ETH_INT_CLK_1000 0x13110000 +#define XPLL_ETH_INT_CLK_1000_GMII 0x14110000 +#define PLL_ETH_INT_CLK_10 0x00991099 +#define PLL_ETH_INT_CLK_100 0x00001099 +#define PLL_ETH_INT_CLK_1000 0x00110000 +#define AR71XX_PLL_ETH_EXT_CLK 0x18050018 +#define AR71XX_PLL_PCI_CLK 0x1805001C + +/* + * APB interrupt status and mask register and interrupt bit numbers for + */ +#define AR71XX_MISC_INTR_STATUS 0x18060010 +#define AR71XX_MISC_INTR_MASK 0x18060014 +#define MISC_INTR_TIMER 0 +#define MISC_INTR_ERROR 1 +#define MISC_INTR_GPIO 2 +#define MISC_INTR_UART 3 +#define MISC_INTR_WATCHDOG 4 +#define MISC_INTR_PERF 5 +#define MISC_INTR_OHCI 6 +#define MISC_INTR_DMA 7 + +#define AR71XX_PCI_INTR_STATUS 0x18060018 +#define AR71XX_PCI_INTR_MASK 0x1806001C +#define PCI_INTR_CORE (1 << 4) + +#define AR71XX_RST_RESET 0x18060024 +#define RST_RESET_FULL_CHIP (1 << 24) /* Same as pulling + the reset pin */ +#define RST_RESET_CPU_COLD (1 << 20) /* Cold reset */ +#define RST_RESET_GE1_MAC (1 << 13) +#define RST_RESET_GE1_PHY (1 << 12) +#define RST_RESET_GE0_MAC (1 << 9) +#define RST_RESET_GE0_PHY (1 << 8) +#define RST_RESET_USB_OHCI_DLL (1 << 6) +#define RST_RESET_USB_HOST (1 << 5) +#define RST_RESET_USB_PHY (1 << 4) +#define RST_RESET_PCI_BUS (1 << 1) +#define RST_RESET_PCI_CORE (1 << 0) + +/* + * GigE adapters region + */ +#define AR71XX_MAC0_BASE 0x19000000 +#define AR71XX_MAC1_BASE 0x1A000000 + +#define AR71XX_MAC_CFG1 0x00 +#define MAC_CFG1_SOFT_RESET (1 << 31) +#define MAC_CFG1_SIMUL_RESET (1 << 30) +#define MAC_CFG1_MAC_RX_BLOCK_RESET (1 << 19) +#define MAC_CFG1_MAC_TX_BLOCK_RESET (1 << 18) +#define MAC_CFG1_RX_FUNC_RESET (1 << 17) +#define MAC_CFG1_TX_FUNC_RESET (1 << 16) +#define MAC_CFG1_LOOPBACK (1 << 8) +#define MAC_CFG1_RXFLOW_CTRL (1 << 5) +#define MAC_CFG1_TXFLOW_CTRL (1 << 4) +#define MAC_CFG1_SYNC_RX (1 << 3) +#define MAC_CFG1_RX_ENABLE (1 << 2) +#define MAC_CFG1_SYNC_TX (1 << 1) +#define MAC_CFG1_TX_ENABLE (1 << 0) +#define AR71XX_MAC_CFG2 0x04 +#define MAC_CFG2_PREAMBLE_LEN_MASK 0xf +#define MAC_CFG2_PREAMBLE_LEN_SHIFT 12 +#define MAC_CFG2_IFACE_MODE_1000 (2 << 8) +#define MAC_CFG2_IFACE_MODE_10_100 (1 << 8) +#define MAC_CFG2_IFACE_MODE_SHIFT 8 +#define MAC_CFG2_IFACE_MODE_MASK 3 +#define MAC_CFG2_HUGE_FRAME (1 << 5) +#define MAC_CFG2_LENGTH_FIELD (1 << 4) +#define MAC_CFG2_ENABLE_PADCRC (1 << 2) +#define MAC_CFG2_ENABLE_CRC (1 << 1) +#define MAC_CFG2_FULL_DUPLEX (1 << 0) +#define AR71XX_MAC_IFG 0x08 +#define AR71XX_MAC_HDUPLEX 0x0C +#define AR71XX_MAC_MAX_FRAME_LEN 0x10 +#define AR71XX_MAC_MII_CFG 0x20 +#define MAC_MII_CFG_RESET (1 << 31) +#define MAC_MII_CFG_SCAN_AUTO_INC (1 << 5) +#define MAC_MII_CFG_PREAMBLE_SUP (1 << 4) +#define MAC_MII_CFG_CLOCK_SELECT_MASK 0x7 +#define MAC_MII_CFG_CLOCK_DIV_4 0 +#define MAC_MII_CFG_CLOCK_DIV_6 2 +#define MAC_MII_CFG_CLOCK_DIV_8 3 +#define MAC_MII_CFG_CLOCK_DIV_10 4 +#define MAC_MII_CFG_CLOCK_DIV_14 5 +#define MAC_MII_CFG_CLOCK_DIV_20 6 +#define MAC_MII_CFG_CLOCK_DIV_28 7 +#define AR71XX_MAC_MII_CMD 0x24 +#define MAC_MII_CMD_SCAN_CYCLE (1 << 1) +#define MAC_MII_CMD_READ 1 +#define MAC_MII_CMD_WRITE 0 +#define AR71XX_MAC_MII_ADDR 0x28 +#define MAC_MII_PHY_ADDR_SHIFT 8 +#define MAC_MII_PHY_ADDR_MASK 0xff +#define MAC_MII_REG_MASK 0x1f +#define AR71XX_MAC_MII_CONTROL 0x2C +#define MAC_MII_CONTROL_MASK 0xffff +#define AR71XX_MAC_MII_STATUS 0x30 +#define MAC_MII_STATUS_MASK 0xffff +#define AR71XX_MAC_MII_INDICATOR 0x34 +#define MAC_MII_INDICATOR_NOT_VALID (1 << 2) +#define MAC_MII_INDICATOR_SCANNING (1 << 1) +#define MAC_MII_INDICATOR_BUSY (1 << 0) +#define AR71XX_MAC_IFCONTROL 0x38 +#define MAC_IFCONTROL_SPEED (1 << 16) +#define AR71XX_MAC_STA_ADDR1 0x40 +#define AR71XX_MAC_STA_ADDR2 0x44 +#define AR71XX_MAC_FIFO_CFG0 0x48 +#define FIFO_CFG0_TX_FABRIC (1 << 4) +#define FIFO_CFG0_TX_SYSTEM (1 << 3) +#define FIFO_CFG0_RX_FABRIC (1 << 2) +#define FIFO_CFG0_RX_SYSTEM (1 << 1) +#define FIFO_CFG0_WATERMARK (1 << 0) +#define FIFO_CFG0_ALL ((1 << 5) - 1) +#define FIFO_CFG0_ENABLE_SHIFT 8 +#define AR71XX_MAC_FIFO_CFG1 0x4C +#define AR71XX_MAC_FIFO_CFG2 0x50 +#define AR71XX_MAC_FIFO_TX_THRESHOLD 0x54 +#define AR71XX_MAC_FIFO_RX_FILTMATCH 0x58 +#define FIFO_RX_FILTMATCH_ALL ((1 << 18) - 1) +#define AR71XX_MAC_FIFO_RX_FILTMASK 0x5C +#define FIFO_RX_FILTMASK_BYTE_MODE (1 << 19) +#define FIFO_RX_FILTMASK_NO_SHORT_FRAME (1 << 18) +#define FIFO_RX_FILTMASK_ALL ((1 << 20) - 1) +/* + * These flags applicable both to AR71XX_MAC_FIFO_RX_FILTMASK and + * to AR71XX_MAC_FIFO_RX_FILTMATCH + */ +#define FIFO_RX_FILT_UNICAST (1 << 17) +#define FIFO_RX_FILT_TRUNC_FRAME (1 << 16) +#define FIFO_RX_FILT_VLAN_TAG (1 << 15) +#define FIFO_RX_FILT_UNSUP_OPCODE (1 << 14) +#define FIFO_RX_FILT_PAUSE_FRAME (1 << 13) +#define FIFO_RX_FILT_CTRL_FRAME (1 << 12) +#define FIFO_RX_FILT_LONG_EVENT (1 << 11) +#define FIFO_RX_FILT_DRIBBLE_NIBBLE (1 << 10) +#define FIFO_RX_FILT_BCAST (1 << 9) +#define FIFO_RX_FILT_MCAST (1 << 8) +#define FIFO_RX_FILT_OK (1 << 7) +#define FIFO_RX_FILT_OORANGE (1 << 6) +#define FIFO_RX_FILT_LEN_MSMTCH (1 << 5) +#define FIFO_RX_FILT_CRC_ERROR (1 << 4) +#define FIFO_RX_FILT_CODE_ERROR (1 << 3) +#define FIFO_RX_FILT_FALSE_CARRIER (1 << 2) +#define FIFO_RX_FILT_RX_DV_EVENT (1 << 1) +#define FIFO_RX_FILT_DROP_EVENT (1 << 0) +#define AR71XX_MAC_FIFO_RAM0 0x60 +#define AR71XX_MAC_FIFO_RAM1 0x64 +#define AR71XX_MAC_FIFO_RAM2 0x68 +#define AR71XX_MAC_FIFO_RAM3 0x6C +#define AR71XX_MAC_FIFO_RAM4 0x70 +#define AR71XX_MAC_FIFO_RAM5 0x74 +#define AR71XX_MAC_FIFO_RAM6 0x78 +#define AR71XX_DMA_TX_CONTROL 0x180 +#define DMA_TX_CONTROL_EN (1 << 0) +#define AR71XX_DMA_TX_DESC 0x184 +#define AR71XX_DMA_TX_STATUS 0x188 +#define DMA_TX_STATUS_PCOUNT_MASK 0xff +#define DMA_TX_STATUS_PCOUNT_SHIFT 16 +#define DMA_TX_STATUS_BUS_ERROR (1 << 3) +#define DMA_TX_STATUS_UNDERRUN (1 << 1) +#define DMA_TX_STATUS_PKT_SENT (1 << 0) +#define AR71XX_DMA_RX_CONTROL 0x18C +#define DMA_RX_CONTROL_EN (1 << 0) +#define AR71XX_DMA_RX_DESC 0x190 +#define AR71XX_DMA_RX_STATUS 0x194 +#define DMA_RX_STATUS_PCOUNT_MASK 0xff +#define DMA_RX_STATUS_PCOUNT_SHIFT 16 +#define DMA_RX_STATUS_BUS_ERROR (1 << 3) +#define DMA_RX_STATUS_OVERFLOW (1 << 1) +#define DMA_RX_STATUS_PKT_RECVD (1 << 0) +#define AR71XX_DMA_INTR 0x198 +#define AR71XX_DMA_INTR_STATUS 0x19C +#define DMA_INTR_ALL ((1 << 8) - 1) +#define DMA_INTR_RX_BUS_ERROR (1 << 7) +#define DMA_INTR_RX_OVERFLOW (1 << 6) +#define DMA_INTR_RX_PKT_RCVD (1 << 4) +#define DMA_INTR_TX_BUS_ERROR (1 << 3) +#define DMA_INTR_TX_UNDERRUN (1 << 1) +#define DMA_INTR_TX_PKT_SENT (1 << 0) + +#endif /* _AR71XX_REG_H_ */ diff --git a/sys/mips/atheros/files.ar71xx b/sys/mips/atheros/files.ar71xx new file mode 100644 index 0000000..78e1d9c --- /dev/null +++ b/sys/mips/atheros/files.ar71xx @@ -0,0 +1,9 @@ +# $FreeBSD$ + +mips/atheros/apb.c standard +mips/atheros/ar71xx_machdep.c standard +mips/atheros/ar71xx_ohci.c optional ohci +mips/atheros/ar71xx_pci.c optional pci +mips/atheros/if_arge.c optional arge +mips/atheros/uart_bus_ar71xx.c optional uart +mips/atheros/uart_cpu_ar71xx.c optional uart diff --git a/sys/mips/atheros/if_arge.c b/sys/mips/atheros/if_arge.c new file mode 100644 index 0000000..7dbfe70 --- /dev/null +++ b/sys/mips/atheros/if_arge.c @@ -0,0 +1,1657 @@ +/*- + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * AR71XX gigabit ethernet driver + */ +#include <sys/param.h> +#include <sys/endian.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/taskqueue.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#include <net/bpf.h> + +#include <machine/bus.h> +#include <machine/cache.h> +#include <machine/resource.h> +#include <vm/vm_param.h> +#include <vm/vm.h> +#include <vm/pmap.h> +#include <machine/pmap.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +MODULE_DEPEND(arge, ether, 1, 1, 1); +MODULE_DEPEND(arge, miibus, 1, 1, 1); + +#include "miibus_if.h" + +#include <mips/atheros/ar71xxreg.h> +#include <mips/atheros/if_argevar.h> + +#undef ARGE_DEBUG +#ifdef ARGE_DEBUG +#define dprintf printf +#else +#define dprintf(x, arg...) +#endif + +static int arge_attach(device_t); +static int arge_detach(device_t); +static int arge_fix_chain(struct mbuf **mp); +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 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 int arge_tx_ring_init(struct arge_softc *); +static void 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 void 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_tx_intr(struct arge_softc *, uint32_t); +static void arge_rx_intr(struct arge_softc *, uint32_t); +static void arge_tick(void *); + +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), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, arge_miibus_readreg), + DEVMETHOD(miibus_writereg, arge_miibus_writereg), + DEVMETHOD(miibus_statchg, arge_miibus_statchg), + + { 0, 0 } +}; + +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); + +/* + * Flushes all + */ +static void +arge_flush_ddr(struct arge_softc *sc) +{ + + ATH_WRITE_REG(sc->arge_ddr_flush_reg, 1); + while (ATH_READ_REG(sc->arge_ddr_flush_reg) & 1) + ; + + ATH_WRITE_REG(sc->arge_ddr_flush_reg, 1); + while (ATH_READ_REG(sc->arge_ddr_flush_reg) & 1) + ; +} + +static int +arge_probe(device_t dev) +{ + + device_set_desc(dev, "Atheros AR71xx built-in ethernet interface"); + return (0); +} + +static int +arge_attach(device_t dev) +{ + uint8_t eaddr[ETHER_ADDR_LEN]; + struct ifnet *ifp; + struct arge_softc *sc; + int error = 0, rid, phynum; + uint32_t reg; + + sc = device_get_softc(dev); + sc->arge_dev = dev; + sc->arge_mac_unit = device_get_unit(dev); + + KASSERT(((sc->arge_mac_unit == 0) || (sc->arge_mac_unit == 1)), + ("if_arge: Only MAC0 and MAC1 supported")); + if (sc->arge_mac_unit == 0) { + sc->arge_ddr_flush_reg = AR71XX_WB_FLUSH_GE0; + sc->arge_pll_reg = AR71XX_PLL_ETH_INT0_CLK; + } else { + sc->arge_ddr_flush_reg = AR71XX_WB_FLUSH_GE1; + sc->arge_pll_reg = AR71XX_PLL_ETH_INT1_CLK; + } + + /* + * Get which PHY of 5 available we should use for this unit + */ + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "phy", &phynum) != 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) + phynum = 4; + else + phynum = sc->arge_mac_unit; + + device_printf(dev, "No PHY specified, using %d\n", phynum); + } + + sc->arge_phy_num = phynum; + + + 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); + + 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; + + /* XXX: add real size */ + IFQ_SET_MAXLEN(&ifp->if_snd, 9); + ifp->if_snd.ifq_maxlen = 9; + IFQ_SET_READY(&ifp->if_snd); + + ifp->if_capenable = ifp->if_capabilities; + + eaddr[0] = 0x00; + eaddr[1] = 0x15; + eaddr[2] = 0x6d; + eaddr[3] = 0xc1; + eaddr[4] = 0x28; + eaddr[5] = 0x2e; + + if (arge_dma_alloc(sc) != 0) { + error = ENXIO; + goto fail; + } + + 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); + + /* Reset MII bus */ + ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_RESET); + DELAY(100); + ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_CLOCK_DIV_28); + DELAY(100); + + /* + * 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, 0x6dc1282e); + ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR2, 0x00000015); + + ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG0, + FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT); + ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG1, 0x0fff0000); + ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG2, 0x00001fff); + + reg = FIFO_RX_FILTMATCH_ALL; + ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMATCH, reg); + + reg = FIFO_RX_FILTMASK_ALL; + reg &= ~FIFO_RX_FILTMASK_BYTE_MODE; + ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK, reg); + + /* Do MII setup. */ + if (mii_phy_probe(dev, &sc->arge_miibus, + arge_ifmedia_upd, arge_ifmedia_sts)) { + device_printf(dev, "MII without any phy!\n"); + error = ENXIO; + goto fail; + } + + /* Call MI attach routine. */ + ether_ifattach(ifp, eaddr); + + /* Hook interrupt last to avoid having to lock softc */ + error = bus_setup_intr(dev, sc->arge_irq, INTR_TYPE_NET | INTR_MPSAFE, + arge_intr_filter, arge_intr, sc, &sc->arge_intrhand); + + if (error) { + device_printf(dev, "couldn't set up irq\n"); + ether_ifdetach(ifp); + goto fail; + } + +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; + 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); + 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 void +arge_shutdown(device_t dev) +{ + struct arge_softc *sc; + + sc = device_get_softc(dev); + + ARGE_LOCK(sc); + arge_stop(sc); + ARGE_UNLOCK(sc); +} + +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 = 0x1000 | (phy << MAC_MII_PHY_ADDR_SHIFT) + | (reg & MAC_MII_REG_MASK); + + if (phy != sc->arge_phy_num) + return (0); + + ARGE_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); + ARGE_WRITE(sc, AR71XX_MAC_MII_ADDR, addr); + ARGE_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_READ); + + i = ARGE_MII_TIMEOUT; + while ((ARGE_READ(sc, AR71XX_MAC_MII_INDICATOR) & + MAC_MII_INDICATOR_BUSY) && (i--)) + DELAY(5); + + if (i < 0) { + dprintf("%s timedout\n", __func__); + /* XXX: return ERRNO istead? */ + return (-1); + } + + result = ARGE_READ(sc, AR71XX_MAC_MII_STATUS) & MAC_MII_STATUS_MASK; + ARGE_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); + dprintf("%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 = 0x1000 + | (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK); + + dprintf("%s: phy=%d, reg=%02x, value=%04x\n", __func__, + phy, reg, data); + + ARGE_WRITE(sc, AR71XX_MAC_MII_ADDR, addr); + ARGE_WRITE(sc, AR71XX_MAC_MII_CONTROL, data); + + i = ARGE_MII_TIMEOUT; + while ((ARGE_READ(sc, AR71XX_MAC_MII_INDICATOR) & + MAC_MII_INDICATOR_BUSY) && (i--)) + DELAY(5); + + if (i < 0) { + dprintf("%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; + struct mii_data *mii; + struct ifnet *ifp; + uint32_t media; + uint32_t cfg, ifcontrol, rx_filtmask, pll, sec_cfg; + + sc = (struct arge_softc *)arg; + + ARGE_LOCK(sc); + mii = device_get_softc(sc->arge_miibus); + ifp = sc->arge_ifp; + if (mii == NULL || ifp == NULL || + (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + ARGE_UNLOCK(sc); + return; + } + + if (mii->mii_media_status & IFM_ACTIVE) { + + media = IFM_SUBTYPE(mii->mii_media_active); + + if (media != IFM_NONE) { + sc->arge_link_status = 1; + + cfg = ARGE_READ(sc, AR71XX_MAC_CFG2); + ifcontrol = ARGE_READ(sc, AR71XX_MAC_IFCONTROL); + rx_filtmask = + ARGE_READ(sc, AR71XX_MAC_FIFO_RX_FILTMASK); + + cfg &= ~(MAC_CFG2_IFACE_MODE_1000 + | MAC_CFG2_IFACE_MODE_10_100 + | MAC_CFG2_FULL_DUPLEX); + ifcontrol &= ~MAC_IFCONTROL_SPEED; + rx_filtmask &= ~FIFO_RX_FILTMASK_BYTE_MODE; + + switch(media) { + case IFM_10_T: + cfg |= MAC_CFG2_IFACE_MODE_10_100; + pll = PLL_ETH_INT_CLK_10; + break; + case IFM_100_TX: + cfg |= MAC_CFG2_IFACE_MODE_10_100; + ifcontrol |= MAC_IFCONTROL_SPEED; + pll = PLL_ETH_INT_CLK_100; + break; + case IFM_1000_T: + case IFM_1000_SX: + cfg |= MAC_CFG2_IFACE_MODE_1000; + rx_filtmask |= FIFO_RX_FILTMASK_BYTE_MODE; + pll = PLL_ETH_INT_CLK_1000; + break; + default: + pll = PLL_ETH_INT_CLK_100; + device_printf(sc->arge_dev, + "Unknown media %d\n", media); + } + + ARGE_WRITE(sc, AR71XX_MAC_FIFO_TX_THRESHOLD, + 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); + + /* set PLL registers */ + sec_cfg = ATH_READ_REG(AR71XX_PLL_CPU_CONFIG); + sec_cfg &= ~(3 << 17); + sec_cfg |= (2 << 17); + + ATH_WRITE_REG(AR71XX_PLL_CPU_CONFIG, sec_cfg); + DELAY(100); + + ATH_WRITE_REG(sc->arge_pll_reg, pll); + + sec_cfg |= (3 << 17); + ATH_WRITE_REG(AR71XX_PLL_CPU_CONFIG, sec_cfg); + DELAY(100); + + sec_cfg &= ~(3 << 17); + ATH_WRITE_REG(AR71XX_PLL_CPU_CONFIG, sec_cfg); + DELAY(100); + } + } else + sc->arge_link_status = 0; + + ARGE_UNLOCK(sc); +} + +static void +arge_reset_dma(struct arge_softc *sc) +{ + unsigned int i; + + 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 */ + for (i = 0; i < ARGE_RX_RING_COUNT; i++) + ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD); + + /* + * Clear all possible TX interrupts + */ + for (i = 0; i < ARGE_TX_RING_COUNT; i++) + 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); +} + + + +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); + + mii = device_get_softc(sc->arge_miibus); + + arge_stop(sc); + + /* 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); + + sc->arge_link_status = 0; + mii_mediachg(mii); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + callout_reset(&sc->arge_stat_callout, hz, arge_tick, 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); +} + +/* + * 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, si, prev_prod; + + ARGE_LOCK_ASSERT(sc); + + 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); + + si = prod; + + /* + * 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); + + 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 */ + 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; + + sc = ifp->if_softc; + + ARGE_LOCK_ASSERT(sc); + + if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING || sc->arge_link_status == 0 ) + 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; + + /* + * Fix mbuf chain, all fragments should be 4 bytes aligned and + * even 4 bytes + */ + arge_fix_chain(&m_head); + + if (m_head == NULL) { + dprintf("failed to adjust mbuf chain\n"); + } + + /* + * 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); + } +} + +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); + callout_stop(&sc->arge_stat_callout); + + /* mask out interrupts */ + ARGE_WRITE(sc, AR71XX_DMA_INTR, 0); + + arge_reset_dma(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; + + switch (command) { + case SIOCSIFFLAGS: + printf("Implement me: SIOCSIFFLAGS\n"); + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + printf("Implement me: SIOCDELMULTI\n"); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + printf("Implement me: SIOCSIFMEDIA\n"); + mii = device_get_softc(sc->arge_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + break; + case SIOCSIFCAP: + error = 0; + ifp->if_hwassist = 0; + printf("Implement me: SIOCSIFCAP\n"); + break; + 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); + if (mii->mii_instance) { + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + mii_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); + ARGE_UNLOCK(sc); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; +} + +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 */ + 1, /* 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_cdata.arge_tx_ring_map) + bus_dmamap_unload(sc->arge_cdata.arge_tx_ring_tag, + sc->arge_cdata.arge_tx_ring_map); + if (sc->arge_cdata.arge_tx_ring_map && + 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_cdata.arge_tx_ring_map = NULL; + 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_cdata.arge_rx_ring_map) + bus_dmamap_unload(sc->arge_cdata.arge_rx_ring_tag, + sc->arge_cdata.arge_rx_ring_map); + if (sc->arge_cdata.arge_rx_ring_map && + 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_cdata.arge_rx_ring_map = NULL; + 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; + sc->arge_cdata.arge_tx_pkts = 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); +} + +/* + * 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]; + 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].packet_ctrl = ARGE_DESC_EMPTY; + 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_PREREAD | BUS_DMASYNC_PREWRITE); + + return (0); +} + +/* + * 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_DONTWAIT, 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_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_POSTREAD); + 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; + bus_dmamap_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_PREREAD); + rxd->rx_m = m; + desc = rxd->desc; + desc->packet_addr = segs[0].ds_addr; + desc->packet_ctrl = (desc->packet_ctrl & ~ARGE_DESC_SIZE_MASK) + | ARGE_DMASIZE(segs[0].ds_len); + + 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) + 1); i++) + *dst++ = *src++; + + m->m_data -= ETHER_ALIGN; +} + + +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; + 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]; + + cur_tx->packet_ctrl = ARGE_DESC_EMPTY; + + ifp->if_opackets++; + + 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_ctrl = ARGE_DESC_EMPTY; + 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 void +arge_rx_locked(struct arge_softc *sc) +{ + struct arge_rxdesc *rxd; + struct ifnet *ifp = sc->arge_ifp; + int cons, prog, packet_len; + struct arge_desc *cur_rx; + struct mbuf *m; + + 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_PREREAD); + 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; + ifp->if_ipackets++; + + ARGE_UNLOCK(sc); + (*ifp->if_input)(ifp, m); + ARGE_LOCK(sc); + + /* Reinit descriptor */ + cur_rx->packet_ctrl = ARGE_DESC_EMPTY; + cur_rx->packet_addr = 0; + if (arge_newbuf(sc, cons) != 0) { + device_printf(sc->arge_dev, + "Failed to allocate buffer\n"); + break; + } + + bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, + sc->arge_cdata.arge_rx_ring_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + } + + if (prog > 0) { + sc->arge_cdata.arge_rx_cons = cons; + + bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, + sc->arge_cdata.arge_rx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + } +} + +static void +arge_rx_intr(struct arge_softc *sc, uint32_t status) +{ + + ARGE_LOCK(sc); + /* interrupts are masked by filter */ + arge_rx_locked(sc); + + /* unmask interrupts */ + ARGE_SET_BITS(sc, + AR71XX_DMA_INTR, DMA_INTR_RX_OVERFLOW | DMA_INTR_RX_PKT_RCVD); + ARGE_UNLOCK(sc); +} + +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); + +#if 0 + dprintf("int mask(filter) = %b\n", ints, + "\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD" + "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT"); + dprintf("status(filter) = %b\n", status, + "\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD" + "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT"); +#endif + + if (status & DMA_INTR_ALL) { + if (status & (DMA_INTR_RX_PKT_RCVD | DMA_INTR_RX_OVERFLOW)) + ARGE_CLEAR_BITS(sc, AR71XX_DMA_INTR, + DMA_INTR_RX_OVERFLOW | DMA_INTR_RX_PKT_RCVD); + + if (status & (DMA_INTR_TX_PKT_SENT | DMA_INTR_TX_UNDERRUN)) + ARGE_CLEAR_BITS(sc, AR71XX_DMA_INTR, + DMA_INTR_TX_UNDERRUN | DMA_INTR_TX_PKT_SENT); + + sc->arge_intr_status = status; + 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; + + status = sc->arge_intr_status; + +#if 0 + dprintf("int status(intr) = %b\n", status, + "\20\10\7RX_OVERFLOW\5RX_PKT_RCVD" + "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT"); +#endif + + /* + * 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; + } + + if (status & (DMA_INTR_RX_PKT_RCVD | DMA_INTR_RX_OVERFLOW)) + arge_rx_intr(sc, status); + + if (status & (DMA_INTR_TX_PKT_SENT | DMA_INTR_TX_UNDERRUN)) + arge_tx_intr(sc, status); +} + +static void +arge_tx_intr(struct arge_softc *sc, uint32_t status) +{ + ARGE_LOCK(sc); + + /* Interrupts are masked by filter */ + arge_tx_locked(sc); + + ARGE_UNLOCK(sc); +} + +static void +arge_tick(void *xsc) +{ + struct arge_softc *sc = xsc; + struct mii_data *mii; + + ARGE_LOCK_ASSERT(sc); + + mii = device_get_softc(sc->arge_miibus); + mii_tick(mii); + callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc); +} + +/* + * Create a copy of a single mbuf. It can have either internal or + * external data, it may have a packet header. External data is really + * copied, so the new buffer is writeable. + */ +static struct mbuf * +copy_mbuf(struct mbuf *m) +{ + struct mbuf *new; + + MGET(new, M_DONTWAIT, MT_DATA); + if (new == NULL) + return (NULL); + + if (m->m_flags & M_PKTHDR) { + M_MOVE_PKTHDR(new, m); + if (m->m_len > MHLEN) + MCLGET(new, M_WAIT); + } else { + if (m->m_len > MLEN) + MCLGET(new, M_WAIT); + } + + bcopy(m->m_data, new->m_data, m->m_len); + new->m_len = m->m_len; + new->m_flags &= ~M_RDONLY; + + return (new); +} + + + +static int +arge_fix_chain(struct mbuf **mp) +{ + struct mbuf *m = *mp, *prev = NULL, *next, *new; + u_int mlen = 0, fill = 0; + int first, off; + u_char *d, *cp; + + do { + next = m->m_next; + + if ((uintptr_t)mtod(m, void *) % 4 != 0 || + (m->m_len % 4 != 0 && next)) { + /* + * Needs fixing + */ + first = (m == *mp); + + d = mtod(m, u_char *); + if ((off = (uintptr_t)(void *)d % 4) != 0) { + if (M_WRITABLE(m)) { + bcopy(d, d - off, m->m_len); + m->m_data = (caddr_t)(d - off); + } else { + if ((new = copy_mbuf(m)) == NULL) { + goto fail; + } + if (prev) + prev->m_next = new; + new->m_next = next; + m_free(m); + m = new; + } + } + + if ((off = m->m_len % 4) != 0) { + if (!M_WRITABLE(m)) { + if ((new = copy_mbuf(m)) == NULL) { + goto fail; + } + if (prev) + prev->m_next = new; + new->m_next = next; + m_free(m); + m = new; + } + d = mtod(m, u_char *) + m->m_len; + off = 4 - off; + while (off) { + if (next == NULL) { + *d++ = 0; + fill++; + } else if (next->m_len == 0) { + next = m_free(next); + continue; + } else { + cp = mtod(next, u_char *); + *d++ = *cp++; + next->m_len--; + next->m_data = (caddr_t)cp; + } + off--; + m->m_len++; + } + } + + if (first) + *mp = m; + } + + mlen += m->m_len; + prev = m; + } while ((m = next) != NULL); + + return (mlen - fill); + + fail: + m_freem(*mp); + *mp = NULL; + return (0); +} diff --git a/sys/mips/atheros/if_argevar.h b/sys/mips/atheros/if_argevar.h new file mode 100644 index 0000000..1966e61 --- /dev/null +++ b/sys/mips/atheros/if_argevar.h @@ -0,0 +1,138 @@ +/*- + * 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. + */ + +#ifndef __IF_ARGEVAR_H__ +#define __IF_ARGEVAR_H__ + +#define ARGE_TX_RING_COUNT 128 +#define ARGE_RX_RING_COUNT 128 +#define ARGE_RX_DMA_SIZE ARGE_RX_RING_COUNT * sizeof(struct arge_desc) +#define ARGE_TX_DMA_SIZE ARGE_TX_RING_COUNT * sizeof(struct arge_desc) +#define ARGE_MAXFRAGS 8 +#define ARGE_RING_ALIGN sizeof(struct arge_desc) +#define ARGE_RX_ALIGN sizeof(uint32_t) +#define ARGE_MAXFRAGS 8 +#define ARGE_TX_RING_ADDR(sc, i) \ + ((sc)->arge_rdata.arge_tx_ring_paddr + sizeof(struct arge_desc) * (i)) +#define ARGE_RX_RING_ADDR(sc, i) \ + ((sc)->arge_rdata.arge_rx_ring_paddr + sizeof(struct arge_desc) * (i)) +#define ARGE_INC(x,y) (x) = (((x) + 1) % y) + + +#define ARGE_MII_TIMEOUT 1000 + +#define ARGE_LOCK(_sc) mtx_lock(&(_sc)->arge_mtx) +#define ARGE_UNLOCK(_sc) mtx_unlock(&(_sc)->arge_mtx) +#define ARGE_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->arge_mtx, MA_OWNED) + +/* + * register space access macros + */ +#define ARGE_WRITE(sc, reg, val) do { \ + bus_write_4(sc->arge_res, (reg), (val)); \ + } while (0) + +#define ARGE_READ(sc, reg) bus_read_4(sc->arge_res, (reg)) + +#define ARGE_SET_BITS(sc, reg, bits) \ + ARGE_WRITE(sc, reg, ARGE_READ(sc, (reg)) | (bits)) + +#define ARGE_CLEAR_BITS(sc, reg, bits) \ + ARGE_WRITE(sc, reg, ARGE_READ(sc, (reg)) & ~(bits)) + +#define ARGE_DESC_EMPTY (1 << 31) +#define ARGE_DESC_MORE (1 << 24) +#define ARGE_DESC_SIZE_MASK ((1 << 12) - 1) +#define ARGE_DMASIZE(len) ((len) & ARGE_DESC_SIZE_MASK) +struct arge_desc { + uint32_t packet_addr; + uint32_t packet_ctrl; + uint32_t next_desc; + uint32_t padding; +}; + +struct arge_txdesc { + struct mbuf *tx_m; + bus_dmamap_t tx_dmamap; +}; + +struct arge_rxdesc { + struct mbuf *rx_m; + bus_dmamap_t rx_dmamap; + struct arge_desc *desc; +}; + +struct arge_chain_data { + bus_dma_tag_t arge_parent_tag; + bus_dma_tag_t arge_tx_tag; + struct arge_txdesc arge_txdesc[ARGE_TX_RING_COUNT]; + bus_dma_tag_t arge_rx_tag; + struct arge_rxdesc arge_rxdesc[ARGE_RX_RING_COUNT]; + bus_dma_tag_t arge_tx_ring_tag; + bus_dma_tag_t arge_rx_ring_tag; + bus_dmamap_t arge_tx_ring_map; + bus_dmamap_t arge_rx_ring_map; + bus_dmamap_t arge_rx_sparemap; + int arge_tx_pkts; + int arge_tx_prod; + int arge_tx_cons; + int arge_tx_cnt; + int arge_rx_cons; +}; + +struct arge_ring_data { + struct arge_desc *arge_rx_ring; + struct arge_desc *arge_tx_ring; + bus_addr_t arge_rx_ring_paddr; + bus_addr_t arge_tx_ring_paddr; +}; + +struct arge_softc { + struct ifnet *arge_ifp; /* interface info */ + device_t arge_dev; + struct resource *arge_res; + int arge_rid; + struct resource *arge_irq; + void *arge_intrhand; + device_t arge_miibus; + bus_dma_tag_t arge_parent_tag; + bus_dma_tag_t arge_tag; + struct mtx arge_mtx; + struct callout arge_stat_callout; + struct task arge_link_task; + struct arge_chain_data arge_cdata; + struct arge_ring_data arge_rdata; + int arge_link_status; + int arge_detach; + uint32_t arge_intr_status; + int arge_mac_unit; + int arge_phy_num; + uint32_t arge_ddr_flush_reg; + uint32_t arge_pll_reg; +}; + +#endif /* __IF_ARGEVAR_H__ */ diff --git a/sys/mips/atheros/uart_bus_ar71xx.c b/sys/mips/atheros/uart_bus_ar71xx.c new file mode 100644 index 0000000..1cb611c --- /dev/null +++ b/sys/mips/atheros/uart_bus_ar71xx.c @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> + * 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 + */ +#include "opt_uart.h" + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/module.h> + +#include <machine/bus.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_cpu.h> +#include <dev/uart/uart_bus.h> + +#include <mips/atheros/ar71xxreg.h> + +#include "uart_if.h" + +static int uart_ar71xx_probe(device_t dev); +extern struct uart_class uart_ar71xx_uart_class; + +static device_method_t uart_ar71xx_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uart_ar71xx_probe), + DEVMETHOD(device_attach, uart_bus_attach), + DEVMETHOD(device_detach, uart_bus_detach), + { 0, 0 } +}; + +static driver_t uart_ar71xx_driver = { + uart_driver_name, + uart_ar71xx_methods, + sizeof(struct uart_softc), +}; + +extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; + +static int +uart_ar71xx_probe(device_t dev) +{ + struct uart_softc *sc; + + sc = device_get_softc(dev); + sc->sc_sysdev = SLIST_FIRST(&uart_sysdevs); + sc->sc_class = &uart_ns8250_class; + bcopy(&sc->sc_sysdev->bas, &sc->sc_bas, sizeof(sc->sc_bas)); + + return (uart_bus_probe(dev, 2, 85000000, 0, 0)); +} + +DRIVER_MODULE(uart, apb, uart_ar71xx_driver, uart_devclass, 0, 0); diff --git a/sys/mips/atheros/uart_cpu_ar71xx.c b/sys/mips/atheros/uart_cpu_ar71xx.c new file mode 100644 index 0000000..4d6b7e7 --- /dev/null +++ b/sys/mips/atheros/uart_cpu_ar71xx.c @@ -0,0 +1,78 @@ +/*- + * 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, 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 "opt_uart.h" + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> + +#include <machine/bus.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_cpu.h> + +#include <mips/atheros/ar71xxreg.h> + +bus_space_tag_t uart_bus_space_io; +bus_space_tag_t uart_bus_space_mem; + +int +uart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) +{ + return ((b1->bsh == b2->bsh && b1->bst == b2->bst) ? 1 : 0); +} + +int +uart_cpu_getdev(int devtype, struct uart_devinfo *di) +{ + di->ops = uart_getops(&uart_ns8250_class); + di->bas.chan = 0; + di->bas.bst = MIPS_BUS_SPACE_MEM; + di->bas.regshft = 2; + /* TODO: calculate proper AHB freq using PLL registers */ + di->bas.rclk = 85000000; + di->baudrate = 115200; + di->databits = 8; + di->stopbits = 1; + di->parity = UART_PARITY_NONE; + + /* TODO: check if uart_bus_space_io mandatory to set */ + uart_bus_space_io = MIPS_BUS_SPACE_IO; + uart_bus_space_mem = MIPS_BUS_SPACE_MEM; + /* + * FIXME: + * 3 is to compensate big endian, uart operates + * with bus_space_read_1/bus_space_write_1 and hence gets + * highest byte instead of lowest one. Actual fix will involve + * MIPS bus_space fixing. + */ + di->bas.bsh = MIPS_PHYS_TO_KSEG1(AR71XX_UART_ADDR) + 3; + return (0); +} |