summaryrefslogtreecommitdiffstats
path: root/sys/mips/atheros
diff options
context:
space:
mode:
authorgonzo <gonzo@FreeBSD.org>2009-04-14 22:53:22 +0000
committergonzo <gonzo@FreeBSD.org>2009-04-14 22:53:22 +0000
commit821d317cbea839f0d98a13b4d1b444ec15b7dd16 (patch)
tree947489fdb684e228bb2ae3c4d68c2cf3606fddbf /sys/mips/atheros
parent16b2e4b851fc13d49105bf544ef5f7d89526ed3a (diff)
downloadFreeBSD-src-821d317cbea839f0d98a13b4d1b444ec15b7dd16.zip
FreeBSD-src-821d317cbea839f0d98a13b4d1b444ec15b7dd16.tar.gz
- Revert changes accidentally killed by merge operation
Diffstat (limited to 'sys/mips/atheros')
-rw-r--r--sys/mips/atheros/apb.c433
-rw-r--r--sys/mips/atheros/apbvar.h48
-rw-r--r--sys/mips/atheros/ar71xx_machdep.c161
-rw-r--r--sys/mips/atheros/ar71xx_ohci.c205
-rw-r--r--sys/mips/atheros/ar71xx_pci.c429
-rw-r--r--sys/mips/atheros/ar71xxreg.h317
-rw-r--r--sys/mips/atheros/files.ar71xx9
-rw-r--r--sys/mips/atheros/if_arge.c1657
-rw-r--r--sys/mips/atheros/if_argevar.h138
-rw-r--r--sys/mips/atheros/uart_bus_ar71xx.c79
-rw-r--r--sys/mips/atheros/uart_cpu_ar71xx.c78
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);
+}
OpenPOWER on IntegriCloud