Index: sys/conf/options.mips =================================================================== --- sys/conf/options.mips +++ sys/conf/options.mips @@ -118,6 +118,17 @@ AR71XX_ATH_EEPROM opt_ar71xx.h # +# Options for AR531X SOC. AR531X_1ST_GENERATION is AR5311 to AR5314. +# + +AR531X_1ST_GENERATION opt_ar531x.h +AR531X_REALMEM opt_ar531x.h +AR531X_ENV_UBOOT opt_ar531x.h +AR531X_APB_DEBUG opt_ar531x.h +ARE_MDIO opt_ar531x.h +ARE_MII opt_ar531x.h + +# # Options that control the Ralink RT305xF Etherenet MAC. # IF_RT_DEBUG opt_if_rt.h Index: sys/geom/geom_redboot.c =================================================================== --- sys/geom/geom_redboot.c +++ sys/geom/geom_redboot.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -246,6 +247,16 @@ u_int blksize; /* NB: flash block size stored as stripesize */ u_char *buf; off_t offset; + const char *value; + char *op; + + offset = 0; + if (resource_string_value("redboot", 0, "fisoffset", &value) == 0) { + offset = strtouq(value, &op, 0); + if (*op != '\0') { + offset = 0; + } + } g_trace(G_T_TOPOLOGY, "redboot_taste(%s,%s)", mp->name, pp->name); g_topology_assert(); @@ -278,7 +289,8 @@ return (NULL); g_topology_unlock(); head = NULL; - offset = cp->provider->mediasize - blksize; + if(offset == 0) + offset = cp->provider->mediasize - blksize; again: buf = g_read_data(cp, offset, blksize, NULL); if (buf != NULL) Index: sys/mips/atheros/ar531x/apb.c =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/apb.c @@ -0,0 +1,756 @@ +/*- + * Copyright (c) 2016, Hiroki Mori + * 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 "opt_platform.h" +#include "opt_ar531x.h" + +#include +__FBSDID("$FreeBSD: head/sys/mips/atheros/apb.c 233318 2012-03-22 17:47:52Z gonzo $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef INTRNG +#include +#else +#include +#endif + +#ifdef INTRNG +#include "pic_if.h" + +#define PIC_INTR_ISRC(sc, irq) (&(sc)->pic_irqs[(irq)].isrc) +#endif + +#include +#include +#include +#include + +#ifdef AR531X_APB_DEBUG +#define dprintf printf +#else +#define dprintf(x, arg...) +#endif /* AR531X_APB_DEBUG */ + +static int apb_activate_resource(device_t, device_t, int, int, + struct resource *); +static device_t apb_add_child(device_t, u_int, const char *, int); +static struct resource * + apb_alloc_resource(device_t, device_t, int, int *, rman_res_t, + rman_res_t, rman_res_t, 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_filter(void *); +static int apb_probe(device_t); +static int apb_release_resource(device_t, device_t, int, int, + struct resource *); +#ifndef INTRNG +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 *); +#endif + +static void +apb_mask_irq(void *source) +{ + unsigned int irq = (unsigned int)source; + uint32_t reg; + + if(ar531x_soc >= AR531X_SOC_AR5315) { + reg = ATH_READ_REG(AR5315_SYSREG_BASE + + AR5315_SYSREG_MISC_INTMASK); + ATH_WRITE_REG(AR5315_SYSREG_BASE + + AR5315_SYSREG_MISC_INTMASK, reg & ~(1 << irq)); + } else { + reg = ATH_READ_REG(AR5312_SYSREG_BASE + + AR5312_SYSREG_MISC_INTMASK); + ATH_WRITE_REG(AR5312_SYSREG_BASE + + AR5312_SYSREG_MISC_INTMASK, reg & ~(1 << irq)); + } +} + +static void +apb_unmask_irq(void *source) +{ + uint32_t reg; + unsigned int irq = (unsigned int)source; + + if(ar531x_soc >= AR531X_SOC_AR5315) { + reg = ATH_READ_REG(AR5315_SYSREG_BASE + + AR5315_SYSREG_MISC_INTMASK); + ATH_WRITE_REG(AR5315_SYSREG_BASE + + AR5315_SYSREG_MISC_INTMASK, reg | (1 << irq)); + } else { + reg = ATH_READ_REG(AR5312_SYSREG_BASE + + AR5312_SYSREG_MISC_INTMASK); + ATH_WRITE_REG(AR5312_SYSREG_BASE + + AR5312_SYSREG_MISC_INTMASK, reg | (1 << irq)); + } +} + +#ifdef INTRNG +static int +apb_pic_register_isrcs(struct apb_softc *sc) +{ + int error; + uint32_t irq; + struct intr_irqsrc *isrc; + const char *name; + + name = device_get_nameunit(sc->apb_dev); + for (irq = 0; irq < APB_NIRQS; irq++) { + sc->pic_irqs[irq].irq = irq; + isrc = PIC_INTR_ISRC(sc, irq); + error = intr_isrc_register(isrc, sc->apb_dev, 0, "%s", name); + if (error != 0) { + /* XXX call intr_isrc_deregister */ + device_printf(sc->apb_dev, "%s failed", __func__); + return (error); + } + } + + return (0); +} + +static inline intptr_t +pic_xref(device_t dev) +{ + return (0); +} +#endif + +static int +apb_probe(device_t dev) +{ +#ifdef INTRNG + device_set_desc(dev, "APB Bus bridge INTRNG"); +#else + device_set_desc(dev, "APB Bus bridge"); +#endif + + return (0); +} + +static int +apb_attach(device_t dev) +{ + struct apb_softc *sc = device_get_softc(dev); +#ifdef INTRNG + intptr_t xref = pic_xref(dev); + int miscirq; +#else + int rid = 0; +#endif + + sc->apb_dev = dev; + + sc->apb_mem_rman.rm_type = RMAN_ARRAY; + sc->apb_mem_rman.rm_descr = "APB memory window"; + + if(ar531x_soc >= AR531X_SOC_AR5315) { + if (rman_init(&sc->apb_mem_rman) != 0 || + rman_manage_region(&sc->apb_mem_rman, + AR5315_APB_BASE, + AR5315_APB_BASE + AR5315_APB_SIZE - 1) != 0) + panic("apb_attach: failed to set up memory rman"); + } else { + if (rman_init(&sc->apb_mem_rman) != 0 || + rman_manage_region(&sc->apb_mem_rman, + AR5312_APB_BASE, + AR5312_APB_BASE + AR5312_APB_SIZE - 1) != 0) + panic("apb_attach: failed to set up memory rman"); + } + + 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"); + +#ifndef INTRNG + 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_filter, NULL, sc, &sc->sc_misc_ih))) { + device_printf(dev, + "WARNING: unable to register interrupt handler\n"); + return (ENXIO); + } +#else + /* Register the interrupts */ + if (apb_pic_register_isrcs(sc) != 0) { + device_printf(dev, "could not register PIC ISRCs\n"); + return (ENXIO); + } + + /* + * Now, when everything is initialized, it's right time to + * register interrupt controller to interrupt framefork. + */ + if (intr_pic_register(dev, xref) == NULL) { + device_printf(dev, "could not register PIC\n"); + return (ENXIO); + } + + if(ar531x_soc >= AR531X_SOC_AR5315) { + miscirq = AR5315_CPU_IRQ_MISC; + } else { + miscirq = AR5312_IRQ_MISC; + } + cpu_establish_hardintr("aric", apb_filter, NULL, sc, miscirq, + INTR_TYPE_MISC, NULL); +#endif + + /* mask all misc interrupt */ + if(ar531x_soc >= AR531X_SOC_AR5315) { + ATH_WRITE_REG(AR5315_SYSREG_BASE + + AR5315_SYSREG_MISC_INTMASK, 0); + } else { + ATH_WRITE_REG(AR5312_SYSREG_BASE + + AR5312_SYSREG_MISC_INTMASK, 0); + } + + 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, + rman_res_t start, rman_res_t end, rman_res_t 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 = (RMAN_IS_DEFAULT_RANGE(start, end)); + needactivate = flags & RF_ACTIVE; + /* + * Pass memory requests to nexus device + */ + passthrough = (device_get_parent(child) != bus); + rle = NULL; + + dprintf("%s: entry (%p, %p, %d, %d, %p, %p, %jd, %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, %jd)\n", + __func__, (void *)(intptr_t)start, + (void *)(intptr_t)end, count); + } + + switch (type) { + case SYS_RES_IRQ: + rm = &sc->apb_irq_rman; + break; + case SYS_RES_MEMORY: + rm = &sc->apb_mem_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 %d\n", __func__, type); + 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); + int error; + int irq; +#ifndef INTRNG + struct intr_event *event; +#endif + +#ifdef INTRNG + struct intr_irqsrc *isrc; + const char *name; + + if ((rman_get_flags(ires) & RF_SHAREABLE) == 0) + flags |= INTR_EXCL; + + irq = rman_get_start(ires); + isrc = PIC_INTR_ISRC(sc, irq); + if(isrc->isrc_event == 0) { + error = intr_event_create(&isrc->isrc_event, (void *)irq, + 0, irq, apb_mask_irq, apb_unmask_irq, + NULL, NULL, "apb intr%d:", irq); + if(error != 0) + return(error); + } + name = device_get_nameunit(child); + error = intr_event_add_handler(isrc->isrc_event, name, filt, handler, + arg, intr_priority(flags), flags, cookiep); + return(error); +#else + 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, + apb_mask_irq, apb_unmask_irq, + NULL, NULL, + "apb intr%d:", irq); + + if (error == 0) { + sc->sc_eventstab[irq] = event; + sc->sc_intr_counter[irq] = + mips_intrcnt_create(event->ie_name); + } + else + return (error); + } + + intr_event_add_handler(event, device_get_nameunit(child), filt, + handler, arg, intr_priority(flags), flags, cookiep); + mips_intrcnt_setname(sc->sc_intr_counter[irq], event->ie_fullname); + + apb_unmask_irq((void*)irq); + + return (0); +#endif +} + +#ifndef INTRNG +static int +apb_teardown_intr(device_t dev, device_t child, struct resource *ires, + void *cookie) +{ +#ifdef INTRNG + return (intr_teardown_irq(child, ires, cookie)); +#else + 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((void*)irq); + + result = intr_event_remove_handler(cookie); + if (!result) + sc->sc_eventstab[irq] = NULL; + + return (result); +#endif +} + + +static int +apb_filter(void *arg) +{ + struct apb_softc *sc = arg; + struct intr_event *event; + uint32_t reg, irq; + + if(ar531x_soc >= AR531X_SOC_AR5315) + reg = ATH_READ_REG(AR5315_SYSREG_BASE + + AR5315_SYSREG_MISC_INTSTAT); + else + reg = ATH_READ_REG(AR5312_SYSREG_BASE + + AR5312_SYSREG_MISC_INTSTAT); + + for (irq = 0; irq < APB_NIRQS; irq++) { + if (reg & (1 << irq)) { + + if(ar531x_soc >= AR531X_SOC_AR5315) { + ATH_WRITE_REG(AR5315_SYSREG_BASE + + AR5315_SYSREG_MISC_INTSTAT, + reg & ~(1 << irq)); + } else { + ATH_WRITE_REG(AR5312_SYSREG_BASE + + AR5312_SYSREG_MISC_INTSTAT, + reg & ~(1 << irq)); + } + + event = sc->sc_eventstab[irq]; + if (!event || TAILQ_EMPTY(&event->ie_handlers)) { + if(irq == 1 && ar531x_soc < AR531X_SOC_AR5315) { + ATH_READ_REG(AR5312_SYSREG_BASE + + AR5312_SYSREG_AHBPERR); + ATH_READ_REG(AR5312_SYSREG_BASE + + AR5312_SYSREG_AHBDMAE); + } + /* Ignore non handle interrupts */ + if (irq != 0 && irq != 6) + printf("Stray APB IRQ %d\n", irq); + + continue; + } + + intr_event_handle(event, PCPU_GET(curthread)->td_intr_frame); + mips_intrcnt_inc(sc->sc_intr_counter[irq]); + } + } + + return (FILTER_HANDLED); +} +#else +static int +apb_filter(void *arg) +{ + struct apb_softc *sc = arg; + struct thread *td; + uint32_t i, intr; + + td = curthread; + /* Workaround: do not inflate intr nesting level */ + td->td_intr_nesting_level--; + + if(ar531x_soc >= AR531X_SOC_AR5315) + intr = ATH_READ_REG(AR5315_SYSREG_BASE + + AR5315_SYSREG_MISC_INTSTAT); + else + intr = ATH_READ_REG(AR5312_SYSREG_BASE + + AR5312_SYSREG_MISC_INTSTAT); + + while ((i = fls(intr)) != 0) { + i--; + intr &= ~(1u << i); + + if(i == 1 && ar531x_soc < AR531X_SOC_AR5315) { + ATH_READ_REG(AR5312_SYSREG_BASE + + AR5312_SYSREG_AHBPERR); + ATH_READ_REG(AR5312_SYSREG_BASE + + AR5312_SYSREG_AHBDMAE); + } + + if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, i), + curthread->td_intr_frame) != 0) { + device_printf(sc->apb_dev, + "Stray interrupt %u detected\n", i); + apb_mask_irq((void*)i); + continue; + } + } + + KASSERT(i == 0, ("all interrupts handled")); + + td->td_intr_nesting_level++; + + return (FILTER_HANDLED); + +} + +#endif + +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, u_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)); +} + +#ifdef INTRNG +static void +apb_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + u_int irq; + + irq = ((struct apb_pic_irqsrc *)isrc)->irq; + apb_unmask_irq((void*)irq); +} + +static void +apb_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + u_int irq; + + irq = ((struct apb_pic_irqsrc *)isrc)->irq; + apb_mask_irq((void*)irq); +} + +static void +apb_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + apb_pic_disable_intr(dev, isrc); +} + +static void +apb_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + apb_pic_enable_intr(dev, isrc); +} + +static void +apb_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + uint32_t reg, irq; + + irq = ((struct apb_pic_irqsrc *)isrc)->irq; + if(ar531x_soc >= AR531X_SOC_AR5315) { + reg = ATH_READ_REG(AR5315_SYSREG_BASE + + AR5315_SYSREG_MISC_INTSTAT); + ATH_WRITE_REG(AR5315_SYSREG_BASE + AR5315_SYSREG_MISC_INTSTAT, + reg & ~(1 << irq)); + } else { + reg = ATH_READ_REG(AR5312_SYSREG_BASE + + AR5312_SYSREG_MISC_INTSTAT); + ATH_WRITE_REG(AR5312_SYSREG_BASE + AR5312_SYSREG_MISC_INTSTAT, + reg & ~(1 << irq)); + } +} + +static int +apb_pic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + return (ENOTSUP); +} + +#endif + +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_release_resource, apb_release_resource), + 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), +#ifdef INTRNG + DEVMETHOD(pic_disable_intr, apb_pic_disable_intr), + DEVMETHOD(pic_enable_intr, apb_pic_enable_intr), + DEVMETHOD(pic_map_intr, apb_pic_map_intr), + DEVMETHOD(pic_post_filter, apb_pic_post_filter), + DEVMETHOD(pic_post_ithread, apb_pic_post_ithread), + DEVMETHOD(pic_pre_ithread, apb_pic_pre_ithread), + +// DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), +#else + DEVMETHOD(bus_teardown_intr, apb_teardown_intr), +#endif + DEVMETHOD(bus_setup_intr, apb_setup_intr), + + DEVMETHOD_END +}; + +static driver_t apb_driver = { + "apb", + apb_methods, + sizeof(struct apb_softc), +}; +static devclass_t apb_devclass; + +EARLY_DRIVER_MODULE(apb, nexus, apb_driver, apb_devclass, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); Index: sys/mips/atheros/ar531x/apbvar.h =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/apbvar.h @@ -0,0 +1,63 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#ifndef _APBVAR_H_ +#define _APBVAR_H_ + +#define APB_IRQ_BASE 0 +#define APB_IRQ_END 31 +#define APB_NIRQS 32 + +struct apb_pic_irqsrc { + struct intr_irqsrc isrc; + u_int irq; +}; + +struct apb_softc { + device_t apb_dev; + struct rman apb_irq_rman; + struct rman apb_mem_rman; + /* IRQ events structs for child devices */ + struct intr_event *sc_eventstab[APB_NIRQS]; +#ifndef INTRNG + mips_intrcnt_t sc_intr_counter[APB_NIRQS]; +#endif + /* Resources and cookies for MIPS CPU INTs */ + struct resource *sc_misc_irq; + void *sc_misc_ih; +#ifdef INTRNG + struct apb_pic_irqsrc pic_irqs[APB_NIRQS]; +#endif +}; + +struct apb_ivar { + struct resource_list resources; +}; + +#endif /* _APBVAR_H_ */ Index: sys/mips/atheros/ar531x/ar5312_chip.h =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/ar5312_chip.h @@ -0,0 +1,34 @@ +/*- + * Copyright (c) 2010 Adrian Chadd + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $FreeBSD: head/sys/mips/atheros/ar5315_chip.h 234326 2012-04-15 22:34:22Z adrian $ */ + +#ifndef __AR5312_CHIP_H__ +#define __AR5312_CHIP_H__ + +extern struct ar5315_cpu_def ar5312_chip_def; + +#endif Index: sys/mips/atheros/ar531x/ar5312_chip.c =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/ar5312_chip.c @@ -0,0 +1,209 @@ +/*- + * Copyright (c) 2016 Hiroki Mori + * Copyright (c) 2010 Adrian Chadd + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: head/sys/mips/atheros/ar5312_chip.c 234326 2012-04-15 22:34:22Z adrian $"); + +#include "opt_ddb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static void +ar5312_chip_detect_mem_size(void) +{ + uint32_t memsize; + uint32_t memcfg, bank0, bank1; + + /* + * Determine the memory size as established by system + * firmware. + * + * NB: we allow compile time override + */ + memcfg = ATH_READ_REG(AR5312_SDRAMCTL_BASE + AR5312_SDRAMCTL_MEM_CFG1); + bank0 = __SHIFTOUT(memcfg, AR5312_MEM_CFG1_BANK0); + bank1 = __SHIFTOUT(memcfg, AR5312_MEM_CFG1_BANK1); + + memsize = (bank0 ? (1 << (bank0 + 1)) : 0) + + (bank1 ? (1 << (bank1 + 1)) : 0); + memsize <<= 20; + + realmem = memsize; +} + +static void +ar5312_chip_detect_sys_frequency(void) +{ + uint32_t predivisor; + uint32_t multiplier; + + + const uint32_t clockctl = ATH_READ_REG(AR5312_SYSREG_BASE + AR5312_SYSREG_CLOCKCTL); + if(ar531x_soc == AR531X_SOC_AR5313) { + predivisor = __SHIFTOUT(clockctl, AR2313_CLOCKCTL_PREDIVIDE); + multiplier = __SHIFTOUT(clockctl, AR2313_CLOCKCTL_MULTIPLIER); + } else { + predivisor = __SHIFTOUT(clockctl, AR5312_CLOCKCTL_PREDIVIDE); + multiplier = __SHIFTOUT(clockctl, AR5312_CLOCKCTL_MULTIPLIER); + } + + const uint32_t divisor = (0x5421 >> (predivisor * 4)) & 15; + + const uint32_t cpufreq = (40000000 / divisor) * multiplier; + + u_ar531x_cpu_freq = cpufreq; + u_ar531x_ahb_freq = cpufreq / 4; + u_ar531x_ddr_freq = 0; +} + +/* + * This does not lock the CPU whilst doing the work! + */ +static void +ar5312_chip_device_reset(void) +{ + ATH_WRITE_REG(AR5312_SYSREG_BASE + AR5312_SYSREG_RESETCTL, + AR5312_RESET_SYSTEM); +} + +static void +ar5312_chip_device_start(void) +{ + uint32_t cfg0, cfg1; + uint32_t bank0, bank1; + uint32_t size0, size1; + + cfg0 = ATH_READ_REG(AR5312_SDRAMCTL_BASE + AR5312_SDRAMCTL_MEM_CFG0); + cfg1 = ATH_READ_REG(AR5312_SDRAMCTL_BASE + AR5312_SDRAMCTL_MEM_CFG1); + + bank0 = __SHIFTOUT(cfg1, AR5312_MEM_CFG1_BANK0); + bank1 = __SHIFTOUT(cfg1, AR5312_MEM_CFG1_BANK1); + + size0 = bank0 ? (1 << (bank0 + 1)) : 0; + size1 = bank1 ? (1 << (bank1 + 1)) : 0; + + size0 <<= 20; + size1 <<= 20; + + printf("SDRMCTL %x %x %x %x\n", cfg0, cfg1, size0, size1); + + ATH_READ_REG(AR5312_SYSREG_BASE + AR5312_SYSREG_AHBPERR); + ATH_READ_REG(AR5312_SYSREG_BASE + AR5312_SYSREG_AHBDMAE); +// ATH_WRITE_REG(AR5312_SYSREG_BASE + AR5312_SYSREG_WDOG_CTL, 0); + ATH_WRITE_REG(AR5312_SYSREG_BASE + AR5312_SYSREG_ENABLE, 0); + + ATH_WRITE_REG(AR5312_SYSREG_BASE+AR5312_SYSREG_ENABLE, + ATH_READ_REG(AR5312_SYSREG_BASE+AR5312_SYSREG_ENABLE) | + AR5312_ENABLE_ENET0 | AR5312_ENABLE_ENET1); + +} + +static int +ar5312_chip_device_stopped(uint32_t mask) +{ + uint32_t reg; + + reg = ATH_READ_REG(AR5312_SYSREG_BASE + AR5312_SYSREG_RESETCTL); + return ((reg & mask) == mask); +} + +static void +ar5312_chip_set_mii_speed(uint32_t unit, uint32_t speed) +{ +} + +/* Speed is either 10, 100 or 1000 */ +static void +ar5312_chip_set_pll_ge(int unit, int speed) +{ +} + +static void +ar5312_chip_ddr_flush_ge(int unit) +{ +} + +static void +ar5312_chip_soc_init(void) +{ + + u_ar531x_uart_addr = MIPS_PHYS_TO_KSEG1(AR5312_UART0_BASE); + + u_ar531x_gpio_di = AR5312_GPIO_DI; + u_ar531x_gpio_do = AR5312_GPIO_DO; + u_ar531x_gpio_cr = AR5312_GPIO_CR; + u_ar531x_gpio_pins = AR5312_GPIO_PINS; + + u_ar531x_wdog_ctl = AR5312_SYSREG_WDOG_CTL; + u_ar531x_wdog_timer = AR5312_SYSREG_WDOG_TIMER; + +} + +static uint32_t +ar5312_chip_get_eth_pll(unsigned int mac, int speed) +{ + return 0; +} + +struct ar5315_cpu_def ar5312_chip_def = { + &ar5312_chip_detect_mem_size, + &ar5312_chip_detect_sys_frequency, + &ar5312_chip_device_reset, + &ar5312_chip_device_start, + &ar5312_chip_device_stopped, + &ar5312_chip_set_pll_ge, + &ar5312_chip_set_mii_speed, + &ar5312_chip_ddr_flush_ge, + &ar5312_chip_get_eth_pll, + &ar5312_chip_soc_init, +}; Index: sys/mips/atheros/ar531x/ar5312reg.h =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/ar5312reg.h @@ -0,0 +1,237 @@ +/* $Id: ar5312reg.h,v 1.4 2011/07/07 05:06:44 matt Exp $ */ +/* + * Copyright (c) 2006 Urbana-Champaign Independent Media Center. + * Copyright (c) 2006 Garrett D'Amore. + * All rights reserved. + * + * This code was written by Garrett D'Amore for the Champaign-Urbana + * Community Wireless Network Project. + * + * 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. + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgements: + * This product includes software developed by the Urbana-Champaign + * Independent Media Center. + * This product includes software developed by Garrett D'Amore. + * 4. Urbana-Champaign Independent Media Center's name and Garrett + * D'Amore's name may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT + * MEDIA CENTER AND GARRETT D'AMORE ``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 URBANA-CHAMPAIGN INDEPENDENT + * MEDIA CENTER OR GARRETT D'AMORE 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 _MIPS_ATHEROS_AR5312REG_H_ +#define _MIPS_ATHEROS_AR5312REG_H_ + +#define AR5312_MEM0_BASE 0x00000000 /* sdram */ +#define AR5312_MEM1_BASE 0x08000000 /* sdram/flash */ +#define AR5312_MEM3_BASE 0x10000000 /* flash */ +#define AR5312_WLAN0_BASE 0x18000000 +#define AR5312_ENET0_BASE 0x18100000 +#define AR5312_ENET1_BASE 0x18200000 +#define AR5312_SDRAMCTL_BASE 0x18300000 +#define AR5312_FLASHCTL_BASE 0x18400000 +#define AR5312_WLAN1_BASE 0x18500000 +#define AR5312_UART0_BASE 0x1C000000 /* high speed */ +#define AR5312_UART1_BASE 0x1C001000 +#define AR5312_GPIO_BASE 0x1C002000 +#define AR5312_SYSREG_BASE 0x1C003000 +#define AR5312_UARTDMA_BASE 0x1C004000 +#define AR5312_FLASH_BASE 0x1E000000 +#define AR5312_FLASH_END 0x20000000 /* possibly aliased */ + +/* + * FLASHCTL registers -- offset relative to AR531X_FLASHCTL_BASE + */ +#define AR5312_FLASHCTL_0 0x00 +#define AR5312_FLASHCTL_1 0x04 +#define AR5312_FLASHCTL_2 0x08 + +#define AR5312_FLASHCTL_IDCY __BITS(0,3) /* idle cycle turn */ +#define AR5312_FLASHCTL_WST1 __BITS(5,9) /* wait state 1 */ +#define AR5312_FLASHCTL_RBLE __BIT(10) /* rd byte enable */ +#define AR5312_FLASHCTL_WST2 __BITS(11,15) /* wait state 1 */ +#define AR5312_FLASHCTL_AC __BITS(16,18) /* addr chk */ +#define AR5312_FLASHCTL_AC_128K 0 +#define AR5312_FLASHCTL_AC_256K 1 +#define AR5312_FLASHCTL_AC_512K 2 +#define AR5312_FLASHCTL_AC_1M 3 +#define AR5312_FLASHCTL_AC_2M 4 +#define AR5312_FLASHCTL_AC_4M 5 +#define AR5312_FLASHCTL_AC_8M 6 +#define AR5312_FLASHCTL_AC_16M 7 +#define AR5312_FLASHCTL_E __BIT(19) /* enable */ +#define AR5312_FLASHCTL_BUSERR __BIT(24) /* buserr */ +#define AR5312_FLASHCTL_WPERR __BIT(25) /* wperr */ +#define AR5312_FLASHCTL_WP __BIT(26) /* wp */ +#define AR5312_FLASHCTL_BM __BIT(27) /* bm */ +#define AR5312_FLASHCTL_MW __BITS(28,29) /* mem width */ +#define AR5312_FLASHCTL_AT __BITS(31,30) /* access type */ + +/* + * GPIO registers -- offset relative to AR531X_GPIO_BASE + */ +#define AR5312_GPIO_DO 0 +#define AR5312_GPIO_DI 4 +#define AR5312_GPIO_CR 8 + +#define AR5312_GPIO_PINS 8 + +/* + * SYSREG registers -- offset relative to AR531X_SYSREG_BASE + */ +#define AR5312_SYSREG_TIMER 0x0000 +#define AR5312_SYSREG_TIMER_RELOAD 0x0004 +#define AR5312_SYSREG_WDOG_CTL 0x0008 +#define AR5312_SYSREG_WDOG_TIMER 0x000c +#define AR5312_SYSREG_MISC_INTSTAT 0x0010 +#define AR5312_SYSREG_MISC_INTMASK 0x0014 +#define AR5312_SYSREG_INTSTAT 0x0018 +#define AR5312_SYSREG_RESETCTL 0x0020 +#define AR5312_SYSREG_CLOCKCTL 0x0064 +#define AR5312_SYSREG_SCRATCH 0x006c +#define AR5312_SYSREG_AHBPERR 0x0070 +#define AR5312_SYSREG_PROC 0x0074 +#define AR5312_SYSREG_AHBDMAE 0x0078 +#define AR5312_SYSREG_ENABLE 0x0080 +#define AR5312_SYSREG_REVISION 0x0090 + +/* WDOG_CTL watchdog control bits */ +#define AR5312_WDOG_CTL_IGNORE 0x0000 +#define AR5312_WDOG_CTL_NMI 0x0001 +#define AR5312_WDOG_CTL_RESET 0x0002 + +/* Resets */ +#define AR5312_RESET_SYSTEM __BIT(0) +#define AR5312_RESET_CPU __BIT(1) +#define AR5312_RESET_WLAN0 __BIT(2) /* mac & bb */ +#define AR5312_RESET_PHY0 __BIT(3) /* enet phy */ +#define AR5312_RESET_PHY1 __BIT(4) /* enet phy */ +#define AR5312_RESET_ENET0 __BIT(5) /* mac */ +#define AR5312_RESET_ENET1 __BIT(6) /* mac */ +#define AR5312_RESET_UART0 __BIT(8) /* mac */ +#define AR5312_RESET_WLAN1 __BIT(9) /* mac & bb */ +#define AR5312_RESET_APB __BIT(10) /* bridge */ +#define AR5312_RESET_WARM_CPU __BIT(16) +#define AR5312_RESET_WARM_WLAN0_MAC __BIT(17) +#define AR5312_RESET_WARM_WLAN0_BB __BIT(18) +#define AR5312_RESET_NMI __BIT(20) +#define AR5312_RESET_WARM_WLAN1_MAC __BIT(21) +#define AR5312_RESET_WARM_WLAN1_BB __BIT(22) +#define AR5312_RESET_LOCAL_BUS __BIT(23) +#define AR5312_RESET_WDOG __BIT(24) + +/* AR5312/2312 clockctl bits */ +#define AR5312_CLOCKCTL_PREDIVIDE __BITS(4,5) +#define AR5312_CLOCKCTL_MULTIPLIER __BITS(8,12) +#define AR5312_CLOCKCTL_DOUBLER __BIT(16) + +/* AR2313 clockctl */ +#define AR2313_CLOCKCTL_PREDIVIDE __BITS(12,13) +#define AR2313_CLOCKCTL_MULTIPLIER __BITS(16,20) + +/* Enables */ +#define AR5312_ENABLE_WLAN0 __BIT(0) +#define AR5312_ENABLE_ENET0 __BIT(1) +#define AR5312_ENABLE_ENET1 __BIT(2) +#define AR5312_ENABLE_WLAN1 __BITS(7,8) /* both DMA and PIO */ + +/* Revision ids */ +#define AR5312_REVISION_WMAC_MAJOR(x) (((x) >> 12) & 0xf) +#define AR5312_REVISION_WMAC_MINOR(x) (((x) >> 8) & 0xf) +#define AR5312_REVISION_WMAC(x) (((x) >> 8) & 0xff) +#define AR5312_REVISION_MAJOR(x) (((x) >> 4) & 0xf) +#define AR5312_REVISION_MINOR(x) (((x) >> 0) & 0xf) + +#define AR5312_REVISION_MAJ_AR5311 0x1 +#define AR5312_REVISION_MAJ_AR5312 0x4 +#define AR5312_REVISION_MAJ_AR2313 0x5 +#define AR5312_REVISION_MAJ_AR5315 0xB + +/* + * SDRAMCTL registers -- offset relative to SDRAMCTL + */ +#define AR5312_SDRAMCTL_MEM_CFG0 0x0000 +#define AR5312_SDRAMCTL_MEM_CFG1 0x0004 + +/* memory config 1 bits */ +#define AR5312_MEM_CFG1_BANK0 __BITS(8,10) +#define AR5312_MEM_CFG1_BANK1 __BITS(12,15) + +/* helper macro for accessing system registers without bus space */ +#define REGVAL(x) *((volatile uint32_t *)(MIPS_PHYS_TO_KSEG1((x)))) +#define GETSYSREG(x) REGVAL((x) + AR5312_SYSREG_BASE) +#define PUTSYSREG(x,v) (REGVAL((x) + AR5312_SYSREG_BASE)) = (v) +#define GETSDRAMREG(x) REGVAL((x) + AR5312_SDRAMCTL_BASE) +#define PUTSDRAMREG(x,v) (REGVAL((x) + AR5312_SDRAMCTL_BASE)) = (v) + +/* + * Interrupts. + */ +#define AR5312_IRQ_WLAN0 0 +#define AR5312_IRQ_ENET0 1 +#define AR5312_IRQ_ENET1 2 +#define AR5312_IRQ_WLAN1 3 +#define AR5312_IRQ_MISC 4 + +#define AR5312_MISC_IRQ_TIMER 1 +#define AR5312_MISC_IRQ_AHBPERR 2 +#define AR5312_MISC_IRQ_AHBDMAE 3 +#define AR5312_MISC_IRQ_GPIO 4 +#define AR5312_MISC_IRQ_UART0 5 +#define AR5312_MISC_IRQ_UART0_DMA 6 +#define AR5312_MISC_IRQ_WDOG 7 + +/* + * Board data. This is located in flash somewhere, ar531x_board_info + * locates it. + */ +#include /* XXX really doesn't belong in hal */ + +/* XXX write-around for now */ +#define AR5312_BOARD_MAGIC AR531X_BD_MAGIC + +/* config bits */ +#define AR5312_BOARD_CONFIG_ENET0 BD_ENET0 +#define AR5312_BOARD_CONFIG_ENET1 BD_ENET1 +#define AR5312_BOARD_CONFIG_UART1 BD_UART1 +#define AR5312_BOARD_CONFIG_UART0 BD_UART0 +#define AR5312_BOARD_CONFIG_RSTFACTORY BD_RSTFACTORY +#define AR5312_BOARD_CONFIG_SYSLED BD_SYSLED +#define AR5312_BOARD_CONFIG_EXTUARTCLK BD_EXTUARTCLK +#define AR5312_BOARD_CONFIG_CPUFREQ BD_CPUFREQ +#define AR5312_BOARD_CONFIG_SYSFREQ BD_SYSFREQ +#define AR5312_BOARD_CONFIG_WLAN0 BD_WLAN0 +#define AR5312_BOARD_CONFIG_MEMCAP BD_MEMCAP +#define AR5312_BOARD_CONFIG_DISWDOG BD_DISWATCHDOG +#define AR5312_BOARD_CONFIG_WLAN1 BD_WLAN1 +#define AR5312_BOARD_CONFIG_AR2312 BD_ISCASPER +#define AR5312_BOARD_CONFIG_WLAN0_2G BD_WLAN0_2G_EN +#define AR5312_BOARD_CONFIG_WLAN0_5G BD_WLAN0_5G_EN +#define AR5312_BOARD_CONFIG_WLAN1_2G BD_WLAN1_2G_EN +#define AR5312_BOARD_CONFIG_WLAN1_5G BD_WLAN1_5G_EN + +#define AR5312_APB_BASE AR5312_UART0_BASE +#define AR5312_APB_SIZE 0x02000000 + +#endif /* _MIPS_ATHEROS_AR531XREG_H_ */ Index: sys/mips/atheros/ar531x/ar5315_chip.h =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/ar5315_chip.h @@ -0,0 +1,34 @@ +/*- + * Copyright (c) 2010 Adrian Chadd + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $FreeBSD: head/sys/mips/atheros/ar5315_chip.h 234326 2012-04-15 22:34:22Z adrian $ */ + +#ifndef __AR5315_CHIP_H__ +#define __AR5315_CHIP_H__ + +extern struct ar5315_cpu_def ar5315_chip_def; + +#endif Index: sys/mips/atheros/ar531x/ar5315_chip.c =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/ar5315_chip.c @@ -0,0 +1,256 @@ +/*- + * Copyright (c) 2010 Adrian Chadd + * All rights reserved. + * Copyright (c) 2016, Hiroki Mori + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: head/sys/mips/atheros/ar5315_chip.c 234326 2012-04-15 22:34:22Z adrian $"); + +#include "opt_ddb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* XXX these shouldn't be in here - this file is a per-chip file */ +/* XXX these should be in the top-level ar5315 type, not ar5315 -chip */ +uint32_t u_ar531x_cpu_freq; +uint32_t u_ar531x_ahb_freq; +uint32_t u_ar531x_ddr_freq; + +uint32_t u_ar531x_uart_addr; + +uint32_t u_ar531x_gpio_di; +uint32_t u_ar531x_gpio_do; +uint32_t u_ar531x_gpio_cr; +uint32_t u_ar531x_gpio_pins; + +uint32_t u_ar531x_wdog_ctl; +uint32_t u_ar531x_wdog_timer; + +static void +ar5315_chip_detect_mem_size(void) +{ + uint32_t memsize = 0; + uint32_t memcfg, cw, rw, dw; + + /* + * Determine the memory size. We query the board info. + */ + memcfg = ATH_READ_REG(AR5315_SDRAMCTL_BASE + AR5315_SDRAMCTL_MEM_CFG); + cw = __SHIFTOUT(memcfg, AR5315_MEM_CFG_COL_WIDTH); + cw += 1; + rw = __SHIFTOUT(memcfg, AR5315_MEM_CFG_ROW_WIDTH); + rw += 1; + + /* XXX: according to redboot, this could be wrong if DDR SDRAM */ + dw = __SHIFTOUT(memcfg, AR5315_MEM_CFG_DATA_WIDTH); + dw += 1; + dw *= 8; /* bits */ + + /* not too sure about this math, but it _seems_ to add up */ + memsize = (1 << cw) * (1 << rw) * dw; +#if 0 + printf("SDRAM_MEM_CFG =%x, cw=%d rw=%d dw=%d xmemsize=%d\n", memcfg, + cw, rw, dw, memsize); +#endif + realmem = memsize; +} + +static void +ar5315_chip_detect_sys_frequency(void) +{ + uint32_t freq_ref, freq_pll; + static const uint8_t pll_divide_table[] = { + 2, 3, 4, 6, 3, + /* + * these entries are bogus, but it avoids a possible + * bad table dereference + */ + 1, 1, 1 + }; + static const uint8_t pre_divide_table[] = { + 1, 2, 4, 5 + }; + + const uint32_t pllc = ATH_READ_REG(AR5315_SYSREG_BASE + + AR5315_SYSREG_PLLC_CTL); + + const uint32_t refdiv = pre_divide_table[AR5315_PLLC_REF_DIV(pllc)]; + const uint32_t fbdiv = AR5315_PLLC_FB_DIV(pllc); + const uint32_t div2 = (AR5315_PLLC_DIV_2(pllc) + 1) * 2; /* results in 2 or 4 */ + + freq_ref = 40000000; + + /* 40MHz reference clk, reference and feedback dividers */ + freq_pll = (freq_ref / refdiv) * div2 * fbdiv; + + const uint32_t pllout[4] = { + /* CLKM select */ + [0] = freq_pll / pll_divide_table[AR5315_PLLC_CLKM(pllc)], + [1] = freq_pll / pll_divide_table[AR5315_PLLC_CLKM(pllc)], + + /* CLKC select */ + [2] = freq_pll / pll_divide_table[AR5315_PLLC_CLKC(pllc)], + + /* ref_clk select */ + [3] = freq_ref, /* use original reference clock */ + }; + + const uint32_t amba_clkctl = ATH_READ_REG(AR5315_SYSREG_BASE + + AR5315_SYSREG_AMBACLK); + uint32_t ambadiv = AR5315_CLOCKCTL_DIV(amba_clkctl); + ambadiv = ambadiv ? (ambadiv * 2) : 1; + u_ar531x_ahb_freq = pllout[AR5315_CLOCKCTL_SELECT(amba_clkctl)] / ambadiv; + + const uint32_t cpu_clkctl = ATH_READ_REG(AR5315_SYSREG_BASE + + AR5315_SYSREG_CPUCLK); + uint32_t cpudiv = AR5315_CLOCKCTL_DIV(cpu_clkctl); + cpudiv = cpudiv ? (cpudiv * 2) : 1; + u_ar531x_cpu_freq = pllout[AR5315_CLOCKCTL_SELECT(cpu_clkctl)] / cpudiv; + + u_ar531x_ddr_freq = 0; +} + +/* + * This does not lock the CPU whilst doing the work! + */ +static void +ar5315_chip_device_reset(void) +{ + ATH_WRITE_REG(AR5315_SYSREG_BASE + AR5315_SYSREG_COLDRESET, + AR5315_COLD_AHB | AR5315_COLD_APB | AR5315_COLD_CPU); +} + +static void +ar5315_chip_device_start(void) +{ + ATH_WRITE_REG(AR5315_SYSREG_BASE+AR5315_SYSREG_AHB_ERR0, + AR5315_AHB_ERROR_DET); + ATH_READ_REG(AR5315_SYSREG_BASE+AR5315_SYSREG_AHB_ERR1); + ATH_WRITE_REG(AR5315_SYSREG_BASE+AR5315_SYSREG_WDOG_CTL, + AR5315_WDOG_CTL_IGNORE); + + // set Ethernet AHB master arbitration control + // Maybe RedBoot was enabled. But to make sure. + ATH_WRITE_REG(AR5315_SYSREG_BASE+AR5315_SYSREG_AHB_ARB_CTL, + ATH_READ_REG(AR5315_SYSREG_BASE+AR5315_SYSREG_AHB_ARB_CTL) | + AR5315_ARB_ENET); + + // set Ethernet controller byteswap control +/* + ATH_WRITE_REG(AR5315_SYSREG_BASE+AR5315_SYSREG_ENDIAN, + ATH_READ_REG(AR5315_SYSREG_BASE+AR5315_SYSREG_ENDIAN) | + AR5315_ENDIAN_ENET); +*/ + /* Disable interrupts for all gpio pins. */ + ATH_WRITE_REG(AR5315_SYSREG_BASE+AR5315_SYSREG_GPIO_INT, 0); + + printf("AHB Master Arbitration Control %08x\n", + ATH_READ_REG(AR5315_SYSREG_BASE+AR5315_SYSREG_AHB_ARB_CTL)); + printf("Byteswap Control %08x\n", + ATH_READ_REG(AR5315_SYSREG_BASE+AR5315_SYSREG_ENDIAN)); +} + +static int +ar5315_chip_device_stopped(uint32_t mask) +{ + uint32_t reg; + + reg = ATH_READ_REG(AR5315_SYSREG_BASE + AR5315_SYSREG_COLDRESET); + return ((reg & mask) == mask); +} + +static void +ar5315_chip_set_mii_speed(uint32_t unit, uint32_t speed) +{ +} + +/* Speed is either 10, 100 or 1000 */ +static void +ar5315_chip_set_pll_ge(int unit, int speed) +{ +} + +static void +ar5315_chip_ddr_flush_ge(int unit) +{ +} + +static void +ar5315_chip_soc_init(void) +{ + u_ar531x_uart_addr = MIPS_PHYS_TO_KSEG1(AR5315_UART_BASE); + + u_ar531x_gpio_di = AR5315_SYSREG_GPIO_DI; + u_ar531x_gpio_do = AR5315_SYSREG_GPIO_DO; + u_ar531x_gpio_cr = AR5315_SYSREG_GPIO_CR; + u_ar531x_gpio_pins = AR5315_GPIO_PINS; + + u_ar531x_wdog_ctl = AR5315_SYSREG_WDOG_CTL; + u_ar531x_wdog_timer = AR5315_SYSREG_WDOG_TIMER; +} + +static uint32_t +ar5315_chip_get_eth_pll(unsigned int mac, int speed) +{ + return 0; +} + +struct ar5315_cpu_def ar5315_chip_def = { + &ar5315_chip_detect_mem_size, + &ar5315_chip_detect_sys_frequency, + &ar5315_chip_device_reset, + &ar5315_chip_device_start, + &ar5315_chip_device_stopped, + &ar5315_chip_set_pll_ge, + &ar5315_chip_set_mii_speed, + &ar5315_chip_ddr_flush_ge, + &ar5315_chip_get_eth_pll, + &ar5315_chip_soc_init, +}; Index: sys/mips/atheros/ar531x/ar5315_cpudef.h =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/ar5315_cpudef.h @@ -0,0 +1,139 @@ +/*- + * Copyright (c) 2010 Adrian Chadd + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $FreeBSD: head/sys/mips/atheros/ar5315_cpudef.h 233081 2012-03-17 07:25:23Z adrian $ */ + +#ifndef __AR5315_CPUDEF_H__ +#define __AR5315_CPUDEF_H__ + +struct ar5315_cpu_def { + void (* detect_mem_size) (void); + void (* detect_sys_frequency) (void); + void (* ar5315_chip_device_reset) (void); + void (* ar5315_chip_device_start) (void); + int (* ar5315_chip_device_stopped) (uint32_t); + void (* ar5315_chip_set_pll_ge) (int, int); + void (* ar5315_chip_set_mii_speed) (uint32_t, uint32_t); + void (* ar5315_chip_ddr_flush_ge) (int); + uint32_t (* ar5315_chip_get_eth_pll) (unsigned int, int); + void (* ar5315_chip_soc_init) (void); + + /* + * Allow to change MII bus mode: + * AR5315_ARGE_MII_MODE_MII + * AR5315_ARGE_MII_MODE_RMII + * AR5315_ARGE_MII_MODE_GMII + * AR5315_ARGE_MII_MODE_RGMII + * mii_mode(unit, mode); + */ +#define AR5315_ARGE_MII_MODE_MII 0x0100 +#define AR5315_ARGE_MII_MODE_RMII 0x0101 +#define AR5315_ARGE_MII_MODE_GMII 0x1000 +#define AR5315_ARGE_MII_MODE_RGMII 0x1001 + void (* ar5315_chip_set_mii_mode) (int, int, int); +}; + +extern struct ar5315_cpu_def * ar5315_cpu_ops; + +static inline void ar531x_detect_mem_size(void) +{ + ar5315_cpu_ops->detect_mem_size(); +} + +static inline void ar531x_detect_sys_frequency(void) +{ + ar5315_cpu_ops->detect_sys_frequency(); +} + +static inline void ar531x_device_reset(void) +{ + ar5315_cpu_ops->ar5315_chip_device_reset(); +} + +static inline void ar531x_device_start(void) +{ + ar5315_cpu_ops->ar5315_chip_device_start(); +} + +static inline int ar531x_device_stopped(uint32_t mask) +{ + return ar5315_cpu_ops->ar5315_chip_device_stopped(mask); +} + +static inline void ar531x_device_set_pll_ge(int unit, int speed) +{ + ar5315_cpu_ops->ar5315_chip_set_pll_ge(unit, speed); +} + +static inline void ar531x_device_set_mii_speed(int unit, int speed) +{ + ar5315_cpu_ops->ar5315_chip_set_mii_speed(unit, speed); +} + +static inline void ar531x_device_flush_ddr_ge(int unit) +{ + ar5315_cpu_ops->ar5315_chip_ddr_flush_ge(unit); +} + +static inline void ar531x_device_soc_init(void) +{ + ar5315_cpu_ops->ar5315_chip_soc_init(); +} + +static inline void ar531x_device_set_mii_mode(int unit, int mode, int speed) +{ + ar5315_cpu_ops->ar5315_chip_set_mii_mode(unit, mode, speed); +} + +/* XXX shouldn't be here! */ +extern uint32_t u_ar531x_cpu_freq; +extern uint32_t u_ar531x_ahb_freq; +extern uint32_t u_ar531x_ddr_freq; + +extern uint32_t u_ar531x_uart_addr; + +extern uint32_t u_ar531x_gpio_di; +extern uint32_t u_ar531x_gpio_do; +extern uint32_t u_ar531x_gpio_cr; +extern uint32_t u_ar531x_gpio_pins; + +extern uint32_t u_ar531x_wdog_ctl; +extern uint32_t u_ar531x_wdog_timer; + +static inline uint32_t ar531x_cpu_freq(void) { return u_ar531x_cpu_freq; } +static inline uint32_t ar531x_ahb_freq(void) { return u_ar531x_ahb_freq; } +static inline uint32_t ar531x_ddr_freq(void) { return u_ar531x_ddr_freq; } + +static inline uint32_t ar531x_uart_addr(void) { return u_ar531x_uart_addr; } + +static inline uint32_t ar531x_gpio_di(void) { return u_ar531x_gpio_di; } +static inline uint32_t ar531x_gpio_cr(void) { return u_ar531x_gpio_cr; } +static inline uint32_t ar531x_gpio_do(void) { return u_ar531x_gpio_do; } +static inline uint32_t ar531x_gpio_pins(void) { return u_ar531x_gpio_pins; } + +static inline uint32_t ar531x_wdog_ctl(void) { return u_ar531x_wdog_ctl; } +static inline uint32_t ar531x_wdog_timer(void) { return u_ar531x_wdog_timer; } +#endif Index: sys/mips/atheros/ar531x/ar5315_gpio.c =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/ar5315_gpio.c @@ -0,0 +1,534 @@ +/*- + * Copyright (c) 2016, Hiroki Mori + * Copyright (c) 2009, Oleksandr Tymoshenko + * Copyright (c) 2009, Luiz Otavio O Souza. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * GPIO driver for AR5315 + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "gpio_if.h" + +#define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) + +/* + * Helpers + */ +static void ar5315_gpio_function_enable(struct ar5315_gpio_softc *sc, + uint32_t mask); +static void ar5315_gpio_function_disable(struct ar5315_gpio_softc *sc, + uint32_t mask); +static void ar5315_gpio_pin_configure(struct ar5315_gpio_softc *sc, + struct gpio_pin *pin, uint32_t flags); + +/* + * Driver stuff + */ +static int ar5315_gpio_probe(device_t dev); +static int ar5315_gpio_attach(device_t dev); +static int ar5315_gpio_detach(device_t dev); +static int ar5315_gpio_filter(void *arg); +static void ar5315_gpio_intr(void *arg); + +/* + * GPIO interface + */ +static device_t ar5315_gpio_get_bus(device_t); +static int ar5315_gpio_pin_max(device_t dev, int *maxpin); +static int ar5315_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps); +static int ar5315_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t + *flags); +static int ar5315_gpio_pin_getname(device_t dev, uint32_t pin, char *name); +static int ar5315_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags); +static int ar5315_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); +static int ar5315_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val); +static int ar5315_gpio_pin_toggle(device_t dev, uint32_t pin); + +/* + * Enable/disable the GPIO function control space. + * + * This is primarily for the AR5315, which has SPI CS1/CS2, UART, SLIC, I2S + * as GPIO pin options. + */ +static void +ar5315_gpio_function_enable(struct ar5315_gpio_softc *sc, uint32_t mask) +{ +// GPIO_SET_BITS(sc, AR5315_GPIO_FUNCTION, mask); +} + +static void +ar5315_gpio_function_disable(struct ar5315_gpio_softc *sc, uint32_t mask) +{ +// GPIO_CLEAR_BITS(sc, AR5315_GPIO_FUNCTION, mask); +} + +static void +ar5315_gpio_pin_configure(struct ar5315_gpio_softc *sc, struct gpio_pin *pin, + unsigned int flags) +{ + uint32_t mask; + + mask = 1 << pin->gp_pin; + + /* + * Manage input/output + */ + if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { + pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); + if (flags & GPIO_PIN_OUTPUT) { + pin->gp_flags |= GPIO_PIN_OUTPUT; + GPIO_SET_BITS(sc, ar531x_gpio_cr(), mask); + } + else { + pin->gp_flags |= GPIO_PIN_INPUT; + GPIO_CLEAR_BITS(sc, ar531x_gpio_cr(), mask); + } + } +} + +static device_t +ar5315_gpio_get_bus(device_t dev) +{ + struct ar5315_gpio_softc *sc; + + sc = device_get_softc(dev); + + return (sc->busdev); +} + +static int +ar5315_gpio_pin_max(device_t dev, int *maxpin) +{ + + *maxpin = ar531x_gpio_pins() - 1; + return (0); +} + +static int +ar5315_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct ar5315_gpio_softc *sc = device_get_softc(dev); + int i; + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + *caps = sc->gpio_pins[i].gp_caps; + GPIO_UNLOCK(sc); + + return (0); +} + +static int +ar5315_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct ar5315_gpio_softc *sc = device_get_softc(dev); + int i; + int dir; + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + dir = GPIO_READ(sc, ar531x_gpio_cr()) & (1 << pin); + + *flags = dir ? GPIO_PIN_OUTPUT : GPIO_PIN_INPUT; + +/* + GPIO_LOCK(sc); + *flags = sc->gpio_pins[i].gp_flags; + GPIO_UNLOCK(sc); +*/ + + return (0); +} + +static int +ar5315_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct ar5315_gpio_softc *sc = device_get_softc(dev); + int i; + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +ar5315_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + int i; + struct ar5315_gpio_softc *sc = device_get_softc(dev); + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + ar5315_gpio_pin_configure(sc, &sc->gpio_pins[i], flags); + + return (0); +} + +static int +ar5315_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) +{ + struct ar5315_gpio_softc *sc = device_get_softc(dev); + uint32_t state; + + state = GPIO_READ(sc, ar531x_gpio_do()); + + if(value == 1) { + state |= (1 << pin); + } else { + state &= ~(1 << pin); + } + + GPIO_WRITE(sc, ar531x_gpio_do(), state); + + return (0); +} + +static int +ar5315_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) +{ + struct ar5315_gpio_softc *sc = device_get_softc(dev); + int i; + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + *val = (GPIO_READ(sc, ar531x_gpio_di()) & (1 << pin)) ? 1 : 0; + + return (0); +} + +static int +ar5315_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + int res, i; + struct ar5315_gpio_softc *sc = device_get_softc(dev); + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + res = (GPIO_READ(sc, ar531x_gpio_do()) & (1 << pin)) ? 1 : 0; + if (res) + GPIO_CLEAR_BITS(sc, ar531x_gpio_do(), pin); + else + GPIO_SET_BITS(sc, ar531x_gpio_do(), pin); + + return (0); +} + +static int +ar5315_gpio_filter(void *arg) +{ + + /* TODO: something useful */ + return (FILTER_STRAY); +} + + + +static void +ar5315_gpio_intr(void *arg) +{ + struct ar5315_gpio_softc *sc = arg; + GPIO_LOCK(sc); + /* TODO: something useful */ + GPIO_UNLOCK(sc); +} + +static int +ar5315_gpio_probe(device_t dev) +{ + + device_set_desc(dev, "Atheros AR531x GPIO driver"); + return (0); +} + +static int +ar5315_gpio_attach(device_t dev) +{ + struct ar5315_gpio_softc *sc = device_get_softc(dev); + int i, j, maxpin; + int mask, pinon; + uint32_t oe; + + KASSERT((device_get_unit(dev) == 0), + ("ar5315_gpio: Only one gpio module supported")); + + mtx_init(&sc->gpio_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + /* Map control/status registers. */ + sc->gpio_mem_rid = 0; + sc->gpio_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->gpio_mem_rid, RF_ACTIVE); + + if (sc->gpio_mem_res == NULL) { + device_printf(dev, "couldn't map memory\n"); + ar5315_gpio_detach(dev); + return (ENXIO); + } + + if ((sc->gpio_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->gpio_irq_rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { + device_printf(dev, "unable to allocate IRQ resource\n"); + ar5315_gpio_detach(dev); + return (ENXIO); + } + + if ((bus_setup_intr(dev, sc->gpio_irq_res, INTR_TYPE_MISC, + ar5315_gpio_filter, ar5315_gpio_intr, sc, &sc->gpio_ih))) { + device_printf(dev, + "WARNING: unable to register interrupt handler\n"); + ar5315_gpio_detach(dev); + return (ENXIO); + } + + sc->dev = dev; + + /* Enable function bits that are required */ + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "function_set", &mask) == 0) { + device_printf(dev, "function_set: 0x%x\n", mask); + ar5315_gpio_function_enable(sc, mask); + } + /* Disable function bits that are required */ + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "function_clear", &mask) == 0) { + device_printf(dev, "function_clear: 0x%x\n", mask); + ar5315_gpio_function_disable(sc, mask); + } + + /* Initialise all pins specified in the mask, up to the pin count */ + (void) ar5315_gpio_pin_max(dev, &maxpin); + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "pinmask", &mask) != 0) + mask = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "pinon", &pinon) != 0) + pinon = 0; + device_printf(dev, "gpio pinmask=0x%x\n", mask); + for (j = 0; j <= maxpin; j++) { + if ((mask & (1 << j)) == 0) + continue; + sc->gpio_npins++; + } + + /* Iniatilize the GPIO pins, keep the loader settings. */ + oe = GPIO_READ(sc, ar531x_gpio_cr()); + sc->gpio_pins = malloc(sizeof(struct gpio_pin) * sc->gpio_npins, + M_DEVBUF, M_WAITOK | M_ZERO); + for (i = 0, j = 0; j <= maxpin; j++) { + if ((mask & (1 << j)) == 0) + continue; + snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, + "pin %d", j); + sc->gpio_pins[i].gp_pin = j; + sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; + if (oe & (1 << j)) + sc->gpio_pins[i].gp_flags = GPIO_PIN_OUTPUT; + else + sc->gpio_pins[i].gp_flags = GPIO_PIN_INPUT; + i++; + } + +#if 0 + /* Turn on the hinted pins. */ + for (i = 0; i < sc->gpio_npins; i++) { + j = sc->gpio_pins[i].gp_pin; + if ((pinon & (1 << j)) != 0) { + ar5315_gpio_pin_setflags(dev, j, GPIO_PIN_OUTPUT); + ar5315_gpio_pin_set(dev, j, 1); + } + } + + /* + * Search through the function hints, in case there's some + * overrides such as LNA control. + * + * hint.gpio.X.func..gpiofunc= + * hint.gpio.X.func..gpiomode=1 (for output, default low) + */ + for (i = 0; i <= maxpin; i++) { + char buf[32]; + int gpiofunc, gpiomode; + + snprintf(buf, 32, "func.%d.gpiofunc", i); + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), + buf, + &gpiofunc) != 0) + continue; + /* Get the mode too */ + snprintf(buf, 32, "func.%d.gpiomode", i); + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), + buf, + &gpiomode) != 0) + continue; + + /* We only handle mode=1 for now */ + if (gpiomode != 1) + continue; + + device_printf(dev, "%s: GPIO %d: func=%d, mode=%d\n", + __func__, + i, + gpiofunc, + gpiomode); + + /* Set output (bit == 0) */ + oe = GPIO_READ(sc, ar531x_gpio_cr()); + oe &= ~ (1 << i); + GPIO_WRITE(sc, ar531x_gpio_cr(), oe); + + /* Set pin value = 0, so it stays low by default */ + oe = GPIO_READ(sc, ar531x_gpio_do()); + oe &= ~ (1 << i); + GPIO_WRITE(sc, ar531x_gpio_do(), oe); + + /* Finally: Set the output config */ +// ar5315_gpio_ouput_configure(i, gpiofunc); + } +#endif + + sc->busdev = gpiobus_attach_bus(dev); + if (sc->busdev == NULL) { + ar5315_gpio_detach(dev); + return (ENXIO); + } + + return (0); +} + +static int +ar5315_gpio_detach(device_t dev) +{ + struct ar5315_gpio_softc *sc = device_get_softc(dev); + + KASSERT(mtx_initialized(&sc->gpio_mtx), ("gpio mutex not initialized")); + + gpiobus_detach_bus(dev); + if (sc->gpio_ih) + bus_teardown_intr(dev, sc->gpio_irq_res, sc->gpio_ih); + if (sc->gpio_irq_res) + bus_release_resource(dev, SYS_RES_IRQ, sc->gpio_irq_rid, + sc->gpio_irq_res); + if (sc->gpio_mem_res) + bus_release_resource(dev, SYS_RES_MEMORY, sc->gpio_mem_rid, + sc->gpio_mem_res); + if (sc->gpio_pins) + free(sc->gpio_pins, M_DEVBUF); + mtx_destroy(&sc->gpio_mtx); + + return(0); +} + +static device_method_t ar5315_gpio_methods[] = { + DEVMETHOD(device_probe, ar5315_gpio_probe), + DEVMETHOD(device_attach, ar5315_gpio_attach), + DEVMETHOD(device_detach, ar5315_gpio_detach), + + /* GPIO protocol */ + DEVMETHOD(gpio_get_bus, ar5315_gpio_get_bus), + DEVMETHOD(gpio_pin_max, ar5315_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, ar5315_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, ar5315_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, ar5315_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, ar5315_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, ar5315_gpio_pin_get), + DEVMETHOD(gpio_pin_set, ar5315_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, ar5315_gpio_pin_toggle), + {0, 0}, +}; + +static driver_t ar5315_gpio_driver = { + "gpio", + ar5315_gpio_methods, + sizeof(struct ar5315_gpio_softc), +}; +static devclass_t ar5315_gpio_devclass; + +DRIVER_MODULE(ar5315_gpio, apb, ar5315_gpio_driver, ar5315_gpio_devclass, 0, 0); Index: sys/mips/atheros/ar531x/ar5315_gpiovar.h =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/ar5315_gpiovar.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 2009, Oleksandr Tymoshenko + * Copyright (c) 2009, Luiz Otavio O Souza. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#ifndef __AR5315_GPIOVAR_H__ +#define __AR5315_GPIOVAR_H__ + +#include + +#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->gpio_mtx) +#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->gpio_mtx) +#define GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->gpio_mtx, MA_OWNED) + +/* + * register space access macros + */ +#define GPIO_WRITE(sc, reg, val) do { \ + bus_write_4(sc->gpio_mem_res, (reg), (val)); \ + } while (0) + +#define GPIO_READ(sc, reg) bus_read_4(sc->gpio_mem_res, (reg)) + +#define GPIO_SET_BITS(sc, reg, bits) \ + GPIO_WRITE(sc, reg, GPIO_READ(sc, (reg)) | (bits)) + +#define GPIO_CLEAR_BITS(sc, reg, bits) \ + GPIO_WRITE(sc, reg, GPIO_READ(sc, (reg)) & ~(bits)) + +#define AR5315_GPIO_PINS 23 +#define AR5312_GPIO_PINS 8 + +struct ar5315_gpio_softc { + device_t dev; + device_t busdev; + struct mtx gpio_mtx; + struct resource *gpio_mem_res; + int gpio_mem_rid; + struct resource *gpio_irq_res; + int gpio_irq_rid; + void *gpio_ih; + int gpio_npins; + struct gpio_pin *gpio_pins; + int gpio_ppspin; + struct pps_state gpio_pps; + uint32_t gpio_ppsenable; +}; + +#endif /* __AR5315_GPIOVAR_H__ */ Index: sys/mips/atheros/ar531x/ar5315_machdep.c =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/ar5315_machdep.c @@ -0,0 +1,351 @@ +/*- + * Copyright (c) 2016, Hiroki Mori + * 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 +__FBSDID("$FreeBSD: head/sys/mips/atheros/ar71xx_machdep.c 232853 2012-03-12 07:34:15Z jmallett $"); + +#include "opt_ddb.h" +#include "opt_ar531x.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +extern char edata[], end[]; + +uint32_t ar711_base_mac[ETHER_ADDR_LEN]; +/* 4KB static data aread to keep a copy of the bootload env until + the dynamic kenv is setup */ +char boot1_env[4096]; + +/* + * We get a string in from Redboot with the all the arguments together, + * "foo=bar bar=baz". Split them up and save in kenv. + */ +static void +parse_argv(char *str) +{ + char *n, *v; + + while ((v = strsep(&str, " ")) != NULL) { + if (*v == '\0') + continue; + if (*v == '-') { + while (*v != '\0') { + v++; + switch (*v) { + case 'a': boothowto |= RB_ASKNAME; break; + case 'd': boothowto |= RB_KDB; break; + case 'g': boothowto |= RB_GDB; break; + case 's': boothowto |= RB_SINGLE; break; + case 'v': boothowto |= RB_VERBOSE; break; + } + } + } else { + n = strsep(&v, "="); + if (v == NULL) + kern_setenv(n, "1"); + else + kern_setenv(n, v); + } + } +} + +void +platform_cpu_init() +{ + /* Nothing special */ +} + +void +platform_reset(void) +{ + ar531x_device_reset(); + /* Wait for reset */ + while(1) + ; +} + +/* + * Obtain the MAC address via the Redboot environment. + */ +static void +ar5315_redboot_get_macaddr(void) +{ + char *var; + int count = 0; + + /* + * "ethaddr" is passed via envp on RedBoot platforms + * "kmac" is passed via argv on RouterBOOT platforms + */ + if ((var = kern_getenv("ethaddr")) != NULL || + (var = kern_getenv("kmac")) != NULL) { + count = sscanf(var, "%x%*c%x%*c%x%*c%x%*c%x%*c%x", + &ar711_base_mac[0], &ar711_base_mac[1], + &ar711_base_mac[2], &ar711_base_mac[3], + &ar711_base_mac[4], &ar711_base_mac[5]); + if (count < 6) + memset(ar711_base_mac, 0, + sizeof(ar711_base_mac)); + freeenv(var); + } +} + +#if defined(SOC_VENDOR) || defined(SOC_MODEL) || defined(SOC_REV) +static SYSCTL_NODE(_hw, OID_AUTO, soc, CTLFLAG_RD, 0, + "System on Chip information"); +#endif +#if defined(SOC_VENDOR) +static char hw_soc_vendor[] = SOC_VENDOR; +SYSCTL_STRING(_hw_soc, OID_AUTO, vendor, CTLFLAG_RD, hw_soc_vendor, 0, + "SoC vendor"); +#endif +#if defined(SOC_MODEL) +static char hw_soc_model[] = SOC_MODEL; +SYSCTL_STRING(_hw_soc, OID_AUTO, model, CTLFLAG_RD, hw_soc_model, 0, + "SoC model"); +#endif +#if defined(SOC_REV) +static char hw_soc_revision[] = SOC_REV; +SYSCTL_STRING(_hw_soc, OID_AUTO, revision, CTLFLAG_RD, hw_soc_revision, 0, + "SoC revision"); +#endif + +#if defined(DEVICE_VENDOR) || defined(DEVICE_MODEL) || defined(DEVICE_REV) +static SYSCTL_NODE(_hw, OID_AUTO, device, CTLFLAG_RD, 0, "Board information"); +#endif +#if defined(DEVICE_VENDOR) +static char hw_device_vendor[] = DEVICE_VENDOR; +SYSCTL_STRING(_hw_device, OID_AUTO, vendor, CTLFLAG_RD, hw_device_vendor, 0, + "Board vendor"); +#endif +#if defined(DEVICE_MODEL) +static char hw_device_model[] = DEVICE_MODEL; +SYSCTL_STRING(_hw_device, OID_AUTO, model, CTLFLAG_RD, hw_device_model, 0, + "Board model"); +#endif +#if defined(DEVICE_REV) +static char hw_device_revision[] = DEVICE_REV; +SYSCTL_STRING(_hw_device, OID_AUTO, revision, CTLFLAG_RD, hw_device_revision, 0, + "Board revision"); +#endif + +void +platform_start(__register_t a0 __unused, __register_t a1 __unused, + __register_t a2 __unused, __register_t a3 __unused) +{ + uint64_t platform_counter_freq; + int argc = 0, i; + char **argv = NULL; +#ifndef AR531X_ENV_UBOOT + char **envp = NULL; +#endif + vm_offset_t kernend; + + /* + * clear the BSS and SBSS segments, this should be first call in + * the function + */ + kernend = (vm_offset_t)&end; + memset(&edata, 0, kernend - (vm_offset_t)(&edata)); + + mips_postboot_fixup(); + + /* Initialize pcpu stuff */ + mips_pcpu0_init(); + + /* + * Until some more sensible abstractions for uboot/redboot + * environment handling, we have to make this a compile-time + * hack. The existing code handles the uboot environment + * very incorrectly so we should just ignore initialising + * the relevant pointers. + */ +#ifndef AR531X_ENV_UBOOT + argc = a0; + argv = (char**)a1; + envp = (char**)a2; +#endif + /* + * Protect ourselves from garbage in registers + */ + if (MIPS_IS_VALID_PTR(envp)) { + for (i = 0; envp[i]; i += 2) { + if (strcmp(envp[i], "memsize") == 0) + realmem = btoc(strtoul(envp[i+1], NULL, 16)); + } + } + + ar5315_detect_sys_type(); + +// RedBoot SDRAM Detect is missing +// ar531x_detect_mem_size(); + + /* + * Just wild guess. RedBoot let us down and didn't reported + * memory size + */ + if (realmem == 0) + realmem = btoc(16*1024*1024); + + /* + * Allow build-time override in case Redboot lies + * or in other situations (eg where there's u-boot) + * where there isn't (yet) a convienent method of + * being told how much RAM is available. + * + * This happens on at least the Ubiquiti LS-SR71A + * board, where redboot says there's 16mb of RAM + * but in fact there's 32mb. + */ +#if defined(AR531X_REALMEM) + realmem = btoc(AR531X_REALMEM); +#endif + + /* phys_avail regions are in bytes */ + phys_avail[0] = MIPS_KSEG0_TO_PHYS(kernel_kseg0_end); + phys_avail[1] = ctob(realmem); + + dump_avail[0] = phys_avail[0]; + dump_avail[1] = phys_avail[1] - phys_avail[0]; + + 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(); + boothowto |= (RB_SERIAL | RB_MULTIPLE); /* Use multiple consoles */ +// boothowto |= RB_VERBOSE; +// boothowto |= (RB_SINGLE); + + /* Detect the system type - this is needed for subsequent chipset-specific calls */ + + + ar531x_device_soc_init(); + ar531x_detect_sys_frequency(); + + platform_counter_freq = ar531x_cpu_freq(); + mips_timer_init_params(platform_counter_freq, 1); + cninit(); + init_static_kenv(boot1_env, sizeof(boot1_env)); + + printf("CPU platform: %s\n", ar5315_get_system_type()); + printf("CPU Frequency=%d MHz\n", ar531x_cpu_freq() / 1000000); + printf("CPU DDR Frequency=%d MHz\n", ar531x_ddr_freq() / 1000000); + printf("CPU AHB Frequency=%d MHz\n", ar531x_ahb_freq() / 1000000); + + printf("platform frequency: %lld\n", platform_counter_freq); + printf("arguments: \n"); + printf(" a0 = %08x\n", a0); + printf(" a1 = %08x\n", a1); + printf(" a2 = %08x\n", a2); + printf(" a3 = %08x\n", a3); + + /* + * XXX this code is very redboot specific. + */ + printf("Cmd line:"); + if (MIPS_IS_VALID_PTR(argv)) { + for (i = 0; i < argc; i++) { + printf(" %s", argv[i]); + parse_argv(argv[i]); + } + } + else + printf ("argv is invalid"); + printf("\n"); + + printf("Environment:\n"); +#if 0 + if (MIPS_IS_VALID_PTR(envp)) { + if (envp[0] && strchr(envp[0], '=') ) { + char *env_val; // + for (i = 0; envp[i]; i++) { + env_val = strchr(envp[i], '='); + /* Not sure if we correct to change data, but env in RAM */ + *(env_val++) = '\0'; + printf("= %s = %s\n", envp[i], env_val); + kern_setenv(envp[i], env_val); + } + } else { + for (i = 0; envp[i]; i+=2) { + printf(" %s = %s\n", envp[i], envp[i+1]); + kern_setenv(envp[i], envp[i+1]); + } + } + } + else + printf ("envp is invalid\n"); +#else + printf ("envp skiped\n"); +#endif + + /* Redboot if_are MAC address is in the environment */ + ar5315_redboot_get_macaddr(); + + init_param2(physmem); + mips_cpu_init(); + pmap_bootstrap(); + mips_proc0_init(); + mutex_init(); + + ar531x_device_start(); + + kdb_init(); +#ifdef KDB + if (boothowto & RB_KDB) + kdb_enter(KDB_WHY_BOOTFLAGS, "Boot flags requested debugger"); +#endif + +} Index: sys/mips/atheros/ar531x/ar5315_setup.h =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/ar5315_setup.h @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 2010 Adrian Chadd + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $FreeBSD$ */ + +#ifndef __AR5315_SETUP_H__ +#define __AR5315_SETUP_H__ + +enum ar531x_soc_type { + AR531X_SOC_UNKNOWN, + AR531X_SOC_AR5311, + AR531X_SOC_AR5312, + AR531X_SOC_AR5313, + AR531X_SOC_AR5314, + AR531X_SOC_AR5315, + AR531X_SOC_AR5316, + AR531X_SOC_AR5317, + AR531X_SOC_AR5318, +}; +extern enum ar531x_soc_type ar531x_soc; + +extern void ar5315_detect_sys_type(void); +extern const char *ar5315_get_system_type(void); + +#define AR_FIRST_GEN 1 +#define AR_SECOND_GEN 2 + +#endif Index: sys/mips/atheros/ar531x/ar5315_setup.c =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/ar5315_setup.c @@ -0,0 +1,161 @@ +/*- + * Copyright (c) 2016, Hiroki Mori + * Copyright (c) 2010 Adrian Chadd + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: head/sys/mips/atheros/ar5315_setup.c 223562 2011-06-26 10:07:48Z kevlo $"); + +#include "opt_ddb.h" +#include "opt_ar531x.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#define AR5315_SYS_TYPE_LEN 128 + +static char ar5315_sys_type[AR5315_SYS_TYPE_LEN]; +enum ar531x_soc_type ar531x_soc; +struct ar5315_cpu_def * ar5315_cpu_ops = NULL; + +void +ar5315_detect_sys_type(void) +{ + char *chip = "????"; + uint32_t ver = 0; + uint32_t rev = 0; +#if 0 + const uint8_t *ptr, *end; + static const struct ar531x_boarddata *board = NULL; + + ptr = (const uint8_t *) MIPS_PHYS_TO_KSEG1(AR5315_CONFIG_END + - 0x1000); + + end = (const uint8_t *)AR5315_CONFIG_BASE; + + for (; ptr > end; ptr -= 0x1000) { + if (*(const uint32_t *)ptr == AR531X_BD_MAGIC) { + board = (const struct ar531x_boarddata *) ptr; + rev = board->major; + break; + } + } +#endif + int soctype; + +#ifdef AR531X_1ST_GENERATION + soctype = AR_FIRST_GEN; +#else + soctype = AR_SECOND_GEN; +#endif + + if(soctype == AR_SECOND_GEN) { + ar5315_cpu_ops = &ar5315_chip_def; + + ver = ATH_READ_REG(AR5315_SYSREG_BASE + + AR5315_SYSREG_SREV); + + switch (ver) { + case 0x86: + ar531x_soc = AR531X_SOC_AR5315; + chip = "2315"; + break; + case 0x87: + ar531x_soc = AR531X_SOC_AR5316; + chip = "2316"; + break; + case 0x90: + ar531x_soc = AR531X_SOC_AR5317; + chip = "2317"; + break; + case 0x91: + ar531x_soc = AR531X_SOC_AR5318; + chip = "2318"; + break; + } + } else { + ar5315_cpu_ops = &ar5312_chip_def; + + ver = ATH_READ_REG(AR5312_SYSREG_BASE + + AR5312_SYSREG_REVISION); + rev = AR5312_REVISION_MINOR(ver); + + switch (AR5312_REVISION_MAJOR(ver)) { + case AR5312_REVISION_MAJ_AR5311: + ar531x_soc = AR531X_SOC_AR5311; + chip = "5311"; + break; + case AR5312_REVISION_MAJ_AR5312: + ar531x_soc = AR531X_SOC_AR5312; + chip = "5312"; + break; + case AR5312_REVISION_MAJ_AR2313: + ar531x_soc = AR531X_SOC_AR5313; + chip = "2313"; + break; + } + } + + sprintf(ar5315_sys_type, "Atheros AR%s rev %u", chip, rev); +} + +const char * +ar5315_get_system_type(void) +{ + return ar5315_sys_type; +} + Index: sys/mips/atheros/ar531x/ar5315_spi.c =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/ar5315_spi.c @@ -0,0 +1,287 @@ +/*- + * Copyright (c) 2016, Hiroki Mori + * Copyright (c) 2009, Oleksandr Tymoshenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: head/sys/mips/atheros/ar5315_spi.c 202723 2010-01-21 00:15:59Z gonzo $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include "spibus_if.h" + +#include +#include + +#undef AR531X_SPI_DEBUG +#ifdef AR531X_SPI_DEBUG +#define dprintf printf +#else +#define dprintf(x, arg...) +#endif + +/* + * register space access macros + */ +#define SPI_WRITE(sc, reg, val) do { \ + bus_write_4(sc->sc_mem_res, (reg), (val)); \ + } while (0) + +#define SPI_READ(sc, reg) bus_read_4(sc->sc_mem_res, (reg)) + +#define SPI_SET_BITS(sc, reg, bits) \ + SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) | (bits)) + +#define SPI_CLEAR_BITS(sc, reg, bits) \ + SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) & ~(bits)) + +struct ar5315_spi_softc { + device_t sc_dev; + struct resource *sc_mem_res; + uint32_t sc_reg_ctrl; + uint32_t sc_debug; +}; + +static void +ar5315_spi_attach_sysctl(device_t dev) +{ + struct ar5315_spi_softc *sc; + struct sysctl_ctx_list *ctx; + struct sysctl_oid *tree; + + sc = device_get_softc(dev); + ctx = device_get_sysctl_ctx(dev); + tree = device_get_sysctl_tree(dev); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "debug", CTLFLAG_RW, &sc->sc_debug, 0, + "ar5315_spi debugging flags"); +} + +static int +ar5315_spi_probe(device_t dev) +{ + device_set_desc(dev, "AR5315 SPI"); + return (0); +} + +static int +ar5315_spi_attach(device_t dev) +{ + struct ar5315_spi_softc *sc = device_get_softc(dev); + int rid; + + sc->sc_dev = dev; + rid = 0; + sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (!sc->sc_mem_res) { + device_printf(dev, "Could not map memory\n"); + return (ENXIO); + } + + device_add_child(dev, "spibus", 0); + ar5315_spi_attach_sysctl(dev); + + return (bus_generic_attach(dev)); +} + +static void +ar5315_spi_chip_activate(struct ar5315_spi_softc *sc, int cs) +{ +} + +static void +ar5315_spi_chip_deactivate(struct ar5315_spi_softc *sc, int cs) +{ +} + +static int +ar5315_spi_get_block(off_t offset, caddr_t data, off_t count) +{ + int i; + for(i = 0; i < count / 4; ++i) { + *((uint32_t *)data + i) = ATH_READ_REG(AR5315_MEM1_BASE + offset + i * 4); + } +// printf("ar5315_spi_get_blockr: %x %x %x\n", +// (int)offset, (int)count, *(uint32_t *)data); + return (0); +} + +static int +ar5315_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) +{ + struct ar5315_spi_softc *sc; + uint8_t *buf_in, *buf_out; + struct spibus_ivar *devi = SPIBUS_IVAR(child); + int lin, lout; + uint32_t ctl, cnt, op, rdat; + int i, j; + + sc = device_get_softc(dev); + + if (sc->sc_debug & 0x8000) + printf("ar5315_spi_transfer: CMD "); + + /* Open SPI controller interface */ + ar5315_spi_chip_activate(sc, devi->cs); + + do { + ctl = SPI_READ(sc, ARSPI_REG_CTL); + } while (ctl & ARSPI_CTL_BUSY); + + /* + * Transfer command + */ + buf_out = (uint8_t *)cmd->tx_cmd; + op = buf_out[0]; + if(op == 0x0b) { + int offset = buf_out[1] << 16 | buf_out[2] << 8 | buf_out[3]; + ar5315_spi_get_block(offset, cmd->rx_data, cmd->rx_data_sz); + return (0); + } + do { + ctl = SPI_READ(sc, ARSPI_REG_CTL); + } while (ctl & ARSPI_CTL_BUSY); + if (sc->sc_debug & 0x8000) { + printf("%08x ", op); + printf("tx_cmd_sz=%d rx_cmd_sz=%d ", cmd->tx_cmd_sz, + cmd->rx_cmd_sz); + if(cmd->tx_cmd_sz != 1) { + printf("%08x ", *((uint32_t *)cmd->tx_cmd)); + printf("%08x ", *((uint32_t *)cmd->tx_cmd + 1)); + } + } + SPI_WRITE(sc, ARSPI_REG_OPCODE, op); + + /* clear all of the tx and rx bits */ + ctl &= ~(ARSPI_CTL_TXCNT_MASK | ARSPI_CTL_RXCNT_MASK); + + /* now set txcnt */ + cnt = 1; + + ctl |= (cnt << ARSPI_CTL_TXCNT_SHIFT); + + cnt = 24; + /* now set txcnt */ + if(cmd->rx_cmd_sz < 24) + cnt = cmd->rx_cmd_sz; + ctl |= (cnt << ARSPI_CTL_RXCNT_SHIFT); + + ctl |= ARSPI_CTL_START; + + SPI_WRITE(sc, ARSPI_REG_CTL, ctl); + + if(op == 0x0b) + SPI_WRITE(sc, ARSPI_REG_DATA, 0); + if (sc->sc_debug & 0x8000) + printf("\nDATA "); + /* + * Receive/transmit data (depends on command) + */ +// buf_out = (uint8_t *)cmd->tx_data; + buf_in = (uint8_t *)cmd->rx_cmd; +// lout = cmd->tx_data_sz; + lin = cmd->rx_cmd_sz; + if (sc->sc_debug & 0x8000) + printf("t%d r%d ", lout, lin); + for(i = 0; i <= (cnt - 1) / 4; ++i) { + do { + ctl = SPI_READ(sc, ARSPI_REG_CTL); + } while (ctl & ARSPI_CTL_BUSY); + + rdat = SPI_READ(sc, ARSPI_REG_DATA); + if (sc->sc_debug & 0x8000) + printf("I%08x ", rdat); + + for(j = 0; j < 4; ++j) { + buf_in[i * 4 + j + 1] = 0xff & (rdat >> (8 * j)); + if(i * 4 + j + 2 == cnt) + break; + } + } + + ar5315_spi_chip_deactivate(sc, devi->cs); + /* + * Close SPI controller interface, restore flash memory mapped access. + */ + if (sc->sc_debug & 0x8000) + printf("\n"); + + return (0); +} + +static int +ar5315_spi_detach(device_t dev) +{ + struct ar5315_spi_softc *sc = device_get_softc(dev); + + if (sc->sc_mem_res) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); + + return (0); +} + +static device_method_t ar5315_spi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ar5315_spi_probe), + DEVMETHOD(device_attach, ar5315_spi_attach), + DEVMETHOD(device_detach, ar5315_spi_detach), + + DEVMETHOD(spibus_transfer, ar5315_spi_transfer), +// DEVMETHOD(spibus_get_block, ar5315_spi_get_block), + + DEVMETHOD_END +}; + +static driver_t ar5315_spi_driver = { + "spi", + ar5315_spi_methods, + sizeof(struct ar5315_spi_softc), +}; + +static devclass_t ar5315_spi_devclass; + +DRIVER_MODULE(ar5315_spi, nexus, ar5315_spi_driver, ar5315_spi_devclass, 0, 0); Index: sys/mips/atheros/ar531x/ar5315_wdog.c =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/ar5315_wdog.c @@ -0,0 +1,150 @@ +/*- + * Copyright (c) 2016, Hiroki Mori + * 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. + */ + +/* + * Watchdog driver for AR5315 + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct ar5315_wdog_softc { + device_t dev; + int armed; + int reboot_from_watchdog; + int debug; +}; + +static void +ar5315_wdog_watchdog_fn(void *private, u_int cmd, int *error) +{ + struct ar5315_wdog_softc *sc = private; + uint64_t timer_val; + + cmd &= WD_INTERVAL; + if (sc->debug) + device_printf(sc->dev, "ar5315_wdog_watchdog_fn: cmd: %x\n", cmd); + if (cmd > 0) { + timer_val = (uint64_t)(1ULL << cmd) * ar531x_ahb_freq() / + 1000000000; + if (sc->debug) + device_printf(sc->dev, "ar5315_wdog_watchdog_fn: programming timer: %jx\n", (uintmax_t) timer_val); + /* + * Load timer with large enough value to prevent spurious + * reset + */ + ATH_WRITE_REG(ar531x_wdog_timer(), + ar531x_ahb_freq() * 10); + ATH_WRITE_REG(ar531x_wdog_ctl(), + AR5315_WDOG_CTL_RESET); + ATH_WRITE_REG(ar531x_wdog_timer(), + (timer_val & 0xffffffff)); + sc->armed = 1; + *error = 0; + } else { + if (sc->debug) + device_printf(sc->dev, "ar5315_wdog_watchdog_fn: disarming\n"); + if (sc->armed) { + ATH_WRITE_REG(ar531x_wdog_ctl(), + AR5315_WDOG_CTL_IGNORE); + sc->armed = 0; + } + } +} + +static int +ar5315_wdog_probe(device_t dev) +{ + + device_set_desc(dev, "Atheros AR531x watchdog timer"); + return (0); +} + +static void +ar5315_wdog_sysctl(device_t dev) +{ + struct ar5315_wdog_softc *sc = device_get_softc(dev); + + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev); + struct sysctl_oid *tree = device_get_sysctl_tree(sc->dev); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "debug", CTLFLAG_RW, &sc->debug, 0, + "enable watchdog debugging"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "armed", CTLFLAG_RD, &sc->armed, 0, + "whether the watchdog is armed"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "reboot_from_watchdog", CTLFLAG_RD, &sc->reboot_from_watchdog, 0, + "whether the system rebooted from the watchdog"); +} + + +static int +ar5315_wdog_attach(device_t dev) +{ + struct ar5315_wdog_softc *sc = device_get_softc(dev); + + /* Initialise */ + sc->reboot_from_watchdog = 0; + sc->armed = 0; + sc->debug = 0; + ATH_WRITE_REG(ar531x_wdog_ctl(), AR5315_WDOG_CTL_IGNORE); + + sc->dev = dev; + EVENTHANDLER_REGISTER(watchdog_list, ar5315_wdog_watchdog_fn, sc, 0); + ar5315_wdog_sysctl(dev); + + return (0); +} + +static device_method_t ar5315_wdog_methods[] = { + DEVMETHOD(device_probe, ar5315_wdog_probe), + DEVMETHOD(device_attach, ar5315_wdog_attach), + DEVMETHOD_END +}; + +static driver_t ar5315_wdog_driver = { + "ar5315_wdog", + ar5315_wdog_methods, + sizeof(struct ar5315_wdog_softc), +}; +static devclass_t ar5315_wdog_devclass; + +DRIVER_MODULE(ar5315_wdog, apb, ar5315_wdog_driver, ar5315_wdog_devclass, 0, 0); Index: sys/mips/atheros/ar531x/ar5315reg.h =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/ar5315reg.h @@ -0,0 +1,242 @@ +/* $Id: ar5315reg.h,v 1.3 2011/07/07 05:06:44 matt Exp $ */ +/* + * Copyright (c) 2006 Urbana-Champaign Independent Media Center. + * Copyright (c) 2006 Garrett D'Amore. + * All rights reserved. + * + * This code was written by Garrett D'Amore for the Champaign-Urbana + * Community Wireless Network Project. + * + * 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. + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgements: + * This product includes software developed by the Urbana-Champaign + * Independent Media Center. + * This product includes software developed by Garrett D'Amore. + * 4. Urbana-Champaign Independent Media Center's name and Garrett + * D'Amore's name may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT + * MEDIA CENTER AND GARRETT D'AMORE ``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 URBANA-CHAMPAIGN INDEPENDENT + * MEDIA CENTER OR GARRETT D'AMORE 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 _MIPS_ATHEROS_AR5315REG_H_ +#define _MIPS_ATHEROS_AR5315REG_H_ + +#define AR5315_MEM0_BASE 0x00000000 /* sdram */ +#define AR5315_MEM1_BASE 0x08000000 /* spi flash */ +#define AR5315_WLAN_BASE 0x10000000 +#define AR5315_PCI_BASE 0x10100000 +#define AR5315_SDRAMCTL_BASE 0x10300000 +#define AR5315_LOCAL_BASE 0x10400000 /* local bus */ +#define AR5315_ENET_BASE 0x10500000 +#define AR5315_SYSREG_BASE 0x11000000 +#define AR5315_UART_BASE 0x11100000 +#define AR5315_SPI_BASE 0x11300000 /* spi flash */ +#define AR5315_BOOTROM_BASE 0x1FC00000 /* boot rom */ +#define AR5315_CONFIG_BASE 0x087D0000 /* flash start */ +#define AR5315_CONFIG_END 0x087FF000 /* flash end */ +#define AR5315_RADIO_END 0x1FFFF000 /* radio end */ + +#if 0 +#define AR5315_PCIEXT_BASE 0x80000000 /* pci external */ +#define AR5315_RAM2_BASE 0xc0000000 +#define AR5315_RAM3_BASE 0xe0000000 +#endif + +/* + * SYSREG registers -- offset relative to AR531X_SYSREG_BASE + */ +#define AR5315_SYSREG_COLDRESET 0x0000 +#define AR5315_SYSREG_RESETCTL 0x0004 +#define AR5315_SYSREG_AHB_ARB_CTL 0x0008 +#define AR5315_SYSREG_ENDIAN 0x000c +#define AR5315_SYSREG_NMI_CTL 0x0010 +#define AR5315_SYSREG_SREV 0x0014 +#define AR5315_SYSREG_IF_CTL 0x0018 +#define AR5315_SYSREG_MISC_INTSTAT 0x0020 +#define AR5315_SYSREG_MISC_INTMASK 0x0024 +#define AR5315_SYSREG_GISR 0x0028 +#define AR5315_SYSREG_TIMER 0x0030 +#define AR5315_SYSREG_RELOAD 0x0034 +#define AR5315_SYSREG_WDOG_TIMER 0x0038 +#define AR5315_SYSREG_WDOG_CTL 0x003c +#define AR5315_SYSREG_PERFCNT0 0x0048 +#define AR5315_SYSREG_PERFCNT1 0x004c +#define AR5315_SYSREG_AHB_ERR0 0x0050 +#define AR5315_SYSREG_AHB_ERR1 0x0054 +#define AR5315_SYSREG_AHB_ERR2 0x0058 +#define AR5315_SYSREG_AHB_ERR3 0x005c +#define AR5315_SYSREG_AHB_ERR4 0x0060 +#define AR5315_SYSREG_PLLC_CTL 0x0064 +#define AR5315_SYSREG_PLLV_CTL 0x0068 +#define AR5315_SYSREG_CPUCLK 0x006c +#define AR5315_SYSREG_AMBACLK 0x0070 +#define AR5315_SYSREG_SYNCCLK 0x0074 +#define AR5315_SYSREG_DSL_SLEEP_CTL 0x0080 +#define AR5315_SYSREG_DSL_SLEEP_DUR 0x0084 +#define AR5315_SYSREG_GPIO_DI 0x0088 +#define AR5315_SYSREG_GPIO_DO 0x0090 +#define AR5315_SYSREG_GPIO_CR 0x0098 +#define AR5315_SYSREG_GPIO_INT 0x00a0 + +#define AR5315_GPIO_PINS 23 + +/* Cold resets (AR5315_SYSREG_COLDRESET) */ +#define AR5315_COLD_AHB 0x00000001 +#define AR5315_COLD_APB 0x00000002 +#define AR5315_COLD_CPU 0x00000004 +#define AR5315_COLD_CPU_WARM 0x00000008 + +/* Resets (AR5315_SYSREG_RESETCTL) */ +#define AR5315_RESET_WARM_WLAN0_MAC 0x00000001 +#define AR5315_RESET_WARM_WLAN0_BB 0x00000002 +#define AR5315_RESET_MPEGTS 0x00000004 /* MPEG-TS */ +#define AR5315_RESET_PCIDMA 0x00000008 /* PCI dma */ +#define AR5315_RESET_MEMCTL 0x00000010 +#define AR5315_RESET_LOCAL 0x00000020 /* local bus */ +#define AR5315_RESET_I2C 0x00000040 /* i2c */ +#define AR5315_RESET_SPI 0x00000080 /* SPI */ +#define AR5315_RESET_UART 0x00000100 +#define AR5315_RESET_IR 0x00000200 /* infrared */ +#define AR5315_RESET_PHY0 0x00000400 /* enet phy */ +#define AR5315_RESET_ENET0 0x00000800 + +/* Watchdog control (AR5315_SYSREG_WDOG_CTL) */ +#define AR5315_WDOG_CTL_IGNORE 0x0000 +#define AR5315_WDOG_CTL_NMI 0x0001 +#define AR5315_WDOG_CTL_RESET 0x0002 + +/* AR5315 AHB arbitration control (AR5315_SYSREG_AHB_ARB_CTL) */ +#define AR5315_ARB_CPU 0x00001 +#define AR5315_ARB_WLAN 0x00002 +#define AR5315_ARB_MPEGTS 0x00004 +#define AR5315_ARB_LOCAL 0x00008 +#define AR5315_ARB_PCI 0x00010 +#define AR5315_ARB_ENET 0x00020 +#define AR5315_ARB_RETRY 0x00100 + +/* AR5315 endianness control (AR5315_SYSREG_ENDIAN) */ +#define AR5315_ENDIAN_AHB 0x00001 +#define AR5315_ENDIAN_WLAN 0x00002 +#define AR5315_ENDIAN_MPEGTS 0x00004 +#define AR5315_ENDIAN_PCI 0x00008 +#define AR5315_ENDIAN_MEMCTL 0x00010 +#define AR5315_ENDIAN_LOCAL 0x00020 +#define AR5315_ENDIAN_ENET 0x00040 +#define AR5315_ENDIAN_MERGE 0x00200 +#define AR5315_ENDIAN_CPU 0x00400 +#define AR5315_ENDIAN_PCIAHB 0x00800 +#define AR5315_ENDIAN_PCIAHB_BRIDGE 0x01000 +#define AR5315_ENDIAN_SPI 0x08000 +#define AR5315_ENDIAN_CPU_DRAM 0x10000 +#define AR5315_ENDIAN_CPU_PCI 0x20000 +#define AR5315_ENDIAN_CPU_MMR 0x40000 + +/* AR5315 AHB error bits */ +#define AR5315_AHB_ERROR_DET 1 /* error detected */ +#define AR5315_AHB_ERROR_OVR 2 /* AHB overflow */ +#define AR5315_AHB_ERROR_WDT 4 /* wdt (not hresp) */ + +/* AR5315 clocks */ +#define AR5315_PLLC_REF_DIV(reg) ((reg) & 0x3) +#define AR5315_PLLC_FB_DIV(reg) (((reg) & 0x7c) >> 2) +#define AR5315_PLLC_DIV_2(reg) (((reg) & 0x80) >> 7) +#define AR5315_PLLC_CLKC(reg) (((reg) & 0x1c000) >> 14) +#define AR5315_PLLC_CLKM(reg) (((reg) & 0x700000) >> 20) + +#define AR5315_CLOCKCTL_SELECT(reg) ((reg) & 0x3) +#define AR5315_CLOCKCTL_DIV(reg) (((reg) & 0xc) >> 2) + +/* + * SDRAMCTL registers -- offset relative to SDRAMCTL + */ +#define AR5315_SDRAMCTL_MEM_CFG 0x0000 +#define AR5315_MEM_CFG_DATA_WIDTH __BITS(13,14) +#define AR5315_MEM_CFG_COL_WIDTH __BITS(9,12) +#define AR5315_MEM_CFG_ROW_WIDTH __BITS(5,8) + +/* memory config 1 bits */ +#define AR531X_MEM_CFG1_BANK0 __BITS(8,10) +#define AR531X_MEM_CFG1_BANK1 __BITS(12,14) + +/* + * PCI configuration stuff. I don't pretend to fully understand these + * registers, they seem to be magic numbers in the Linux code. + */ +#define AR5315_PCI_MAC_RC 0x4000 +#define AR5315_PCI_MAC_SCR 0x4004 +#define AR5315_PCI_MAC_INTPEND 0x4008 +#define AR5315_PCI_MAC_SFR 0x400c +#define AR5315_PCI_MAC_PCICFG 0x4010 +#define AR5315_PCI_MAC_SREV 0x4020 + +#define PCI_MAC_RC_MAC 0x1 +#define PCI_MAC_RC_BB 0x2 + +#define PCI_MAC_SCR_SLM_MASK 0x00030000 +#define PCI_MAC_SCR_SLM_FWAKE 0x00000000 +#define PCI_MAC_SCR_SLM_FSLEEP 0x00010000 +#define PCI_MAC_SCR_SLM_NORMAL 0x00020000 + +#define PCI_MAC_PCICFG_SPWR_DN 0x00010000 + +/* IRQS */ +#define AR5315_CPU_IRQ_MISC 0 +#define AR5315_CPU_IRQ_WLAN 1 +#define AR5315_CPU_IRQ_ENET 2 + +#define AR5315_MISC_IRQ_UART 0 +#define AR5315_MISC_IRQ_I2C 1 +#define AR5315_MISC_IRQ_SPI 2 +#define AR5315_MISC_IRQ_AHBE 3 +#define AR5315_MISC_IRQ_AHPE 4 +#define AR5315_MISC_IRQ_TIMER 5 +#define AR5315_MISC_IRQ_GPIO 6 +#define AR5315_MISC_IRQ_WDOG 7 +#define AR5315_MISC_IRQ_IR 8 + +#define AR5315_APB_BASE AR5315_SYSREG_BASE +#define AR5315_APB_SIZE 0x06000000 + +#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) + +/* Helpers from NetBSD cdefs.h */ +/* __BIT(n): nth bit, where __BIT(0) == 0x1. */ +#define __BIT(__n) \ + (((__n) >= NBBY * sizeof(uintmax_t)) ? 0 : ((uintmax_t)1 << (__n))) + +/* __BITS(m, n): bits m through n, m < n. */ +#define __BITS(__m, __n) \ + ((__BIT(MAX((__m), (__n)) + 1) - 1) ^ (__BIT(MIN((__m), (__n))) - 1)) + +/* find least significant bit that is set */ +#define __LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ (__mask)) + +#define __SHIFTOUT(__x, __mask) (((__x) & (__mask)) / __LOWEST_SET_BIT(__mask)) +#define __SHIFTOUT_MASK(__mask) __SHIFTOUT((__mask), (__mask)) +#endif /* _MIPS_ATHEROS_AR531XREG_H_ */ Index: sys/mips/atheros/ar531x/arspireg.h =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/arspireg.h @@ -0,0 +1,60 @@ +/* $NetBSD: arspireg.h,v 1.1 2006/10/14 15:33:23 gdamore Exp $ */ + +/*- + * Copyright (c) 2006 Urbana-Champaign Independent Media Center. + * Copyright (c) 2006 Garrett D'Amore. + * All rights reserved. + * + * Portions of this code were written by Garrett D'Amore for the + * Champaign-Urbana Community Wireless Network Project. + * + * 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. + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgements: + * This product includes software developed by the Urbana-Champaign + * Independent Media Center. + * This product includes software developed by Garrett D'Amore. + * 4. Urbana-Champaign Independent Media Center's name and Garrett + * D'Amore's name may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT + * MEDIA CENTER AND GARRETT D'AMORE ``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 URBANA-CHAMPAIGN INDEPENDENT + * MEDIA CENTER OR GARRETT D'AMORE 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 _MIPS_ATHEROS_DEV_ARSPIREG_H +#define _MIPS_ATHEROS_DEV_ARSPIREG_H + +#define ARSPI_REG_CTL 0x00 +#define ARSPI_REG_OPCODE 0x04 +#define ARSPI_REG_DATA 0x08 + +#define ARSPI_CTL_START 0x00000100 +#define ARSPI_CTL_BUSY 0x00010000 +#define ARSPI_CTL_TXCNT_MASK 0x0000000f +#define ARSPI_CTL_TXCNT_SHIFT 0 +#define ARSPI_CTL_RXCNT_MASK 0x000000f0 +#define ARSPI_CTL_RXCNT_SHIFT 4 +#define ARSPI_CTL_SIZE_MASK 0x00060000 +#define ARSPI_CTL_CLKSEL_MASK 0x03000000 + +#endif /* _MIPS_ATHEROS_DEV_ARSPIREG_H */ Index: sys/mips/atheros/ar531x/files.ar5315 =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/files.ar5315 @@ -0,0 +1,30 @@ +# $FreeBSD: head/sys/mips/atheros/files.ar71xx 234485 2012-04-20 08:26:05Z adrian $ + +mips/atheros/ar531x/apb.c standard +mips/atheros/ar531x/if_are.c optional are +mips/atheros/ar531x/ar5315_spi.c optional ar5315_spi +mips/atheros/ar531x/ar5315_wdog.c optional ar5315_wdog +mips/atheros/ar531x/ar5315_gpio.c optional gpio +mips/atheros/ar531x/ar5315_machdep.c standard +mips/atheros/ar531x/ar5315_chip.c standard +mips/atheros/ar531x/ar5315_setup.c standard +mips/atheros/ar531x/uart_bus_ar5315.c optional uart_ar5315 +mips/atheros/ar531x/uart_cpu_ar5315.c optional uart_ar5315 + +mips/atheros/ar531x/ar5312_chip.c standard + +mips/atheros/ar71xx_bus_space_reversed.c standard +#mips/mips/intr_machdep.c standard +mips/mips/tick.c standard + +dev/etherswitch/e6000sw/e6060sw.c optional etherswitch +dev/etherswitch/realtek/rtl830x.c optional etherswitch + +# Hack to reuse ARM intrng code +kern/subr_intr.c standard +kern/msi_if.m standard +kern/pic_if.m standard + +# Intrng compatible MIPS32 interrupt controller +mips/mips/mips_pic.c standard + Index: sys/mips/atheros/ar531x/if_are.c =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/if_are.c @@ -0,0 +1,1685 @@ +/*- + * Copyright (c) 2016 Hiroki Mori. All rights reserved. + * Copyright (C) 2007 + * 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 ``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 HIS RELATIVES 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 MIND, 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. + * + * $Id: $ + * + */ + +#include "opt_platform.h" +#include "opt_ar531x.h" + +#include +__FBSDID("$FreeBSD$"); + +/* + * AR231x Ethernet interface driver + * copy from mips/idt/if_kr.c and netbsd code + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#ifdef INTRNG +#include +#endif + +#include +#include + +#ifdef ARE_MDIO +#include +#include +#include "mdio_if.h" +#endif + +MODULE_DEPEND(are, ether, 1, 1, 1); +MODULE_DEPEND(are, miibus, 1, 1, 1); + +#include "miibus_if.h" + +#include +#include +#include +#include + +//#define ARE_DEBUG + +#ifdef ARE_DEBUG +void dump_txdesc(struct are_softc *, int); +void dump_status_reg(struct are_softc *); +#endif + +static int are_attach(device_t); +static int are_detach(device_t); +static int are_ifmedia_upd(struct ifnet *); +static void are_ifmedia_sts(struct ifnet *, struct ifmediareq *); +static int are_ioctl(struct ifnet *, u_long, caddr_t); +static void are_init(void *); +static void are_init_locked(struct are_softc *); +static void are_link_task(void *, int); +static int are_miibus_readreg(device_t, int, int); +static void are_miibus_statchg(device_t); +static int are_miibus_writereg(device_t, int, int, int); +static int are_probe(device_t); +static void are_reset(struct are_softc *); +static int are_resume(device_t); +static int are_rx_ring_init(struct are_softc *); +static int are_tx_ring_init(struct are_softc *); +static int are_shutdown(device_t); +static void are_start(struct ifnet *); +static void are_start_locked(struct ifnet *); +static void are_stop(struct are_softc *); +static int are_suspend(device_t); + +static void are_rx(struct are_softc *); +static void are_tx(struct are_softc *); +static void are_intr(void *); +static void are_tick(void *); + +static void are_dmamap_cb(void *, bus_dma_segment_t *, int, int); +static int are_dma_alloc(struct are_softc *); +static void are_dma_free(struct are_softc *); +static int are_newbuf(struct are_softc *, int); +static __inline void are_fixup_rx(struct mbuf *); + +static void are_hinted_child(device_t bus, const char *dname, int dunit); + +static device_method_t are_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, are_probe), + DEVMETHOD(device_attach, are_attach), + DEVMETHOD(device_detach, are_detach), + DEVMETHOD(device_suspend, are_suspend), + DEVMETHOD(device_resume, are_resume), + DEVMETHOD(device_shutdown, are_shutdown), + + /* MII interface */ + DEVMETHOD(miibus_readreg, are_miibus_readreg), + DEVMETHOD(miibus_writereg, are_miibus_writereg), + DEVMETHOD(miibus_statchg, are_miibus_statchg), + + /* bus interface */ + DEVMETHOD(bus_add_child, device_add_child_ordered), + DEVMETHOD(bus_hinted_child, are_hinted_child), + + DEVMETHOD_END +}; + +static driver_t are_driver = { + "are", + are_methods, + sizeof(struct are_softc) +}; + +static devclass_t are_devclass; + +DRIVER_MODULE(are, nexus, are_driver, are_devclass, 0, 0); +#ifdef ARE_MII +DRIVER_MODULE(miibus, are, miibus_driver, miibus_devclass, 0, 0); +#endif + +#ifdef ARE_MDIO +static int aremdio_probe(device_t); +static int aremdio_attach(device_t); +static int aremdio_detach(device_t); + +/* + * Declare an additional, separate driver for accessing the MDIO bus. + */ +static device_method_t aremdio_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aremdio_probe), + DEVMETHOD(device_attach, aremdio_attach), + DEVMETHOD(device_detach, aremdio_detach), + + /* bus interface */ + DEVMETHOD(bus_add_child, device_add_child_ordered), + + /* MDIO access */ + DEVMETHOD(mdio_readreg, are_miibus_readreg), + DEVMETHOD(mdio_writereg, are_miibus_writereg), +}; + +DEFINE_CLASS_0(aremdio, aremdio_driver, aremdio_methods, + sizeof(struct are_softc)); +static devclass_t aremdio_devclass; + +DRIVER_MODULE(miiproxy, are, miiproxy_driver, miiproxy_devclass, 0, 0); +DRIVER_MODULE(aremdio, nexus, aremdio_driver, aremdio_devclass, 0, 0); +DRIVER_MODULE(mdio, aremdio, mdio_driver, mdio_devclass, 0, 0); +#endif + + +static int +are_probe(device_t dev) +{ + + device_set_desc(dev, "AR531x Ethernet interface"); + return (0); +} + +static int +are_attach(device_t dev) +{ + struct ifnet *ifp; + struct are_softc *sc; + int error = 0; +#ifdef INTRNG + int enetirq; +#else + int rid; +#endif + int unit; + char * local_macstr; + int count; + int i; + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + sc->are_dev = dev; + + // hardcode macaddress + sc->are_eaddr[0] = 0x00; + sc->are_eaddr[1] = 0x0C; + sc->are_eaddr[2] = 0x42; + sc->are_eaddr[3] = 0x09; + sc->are_eaddr[4] = 0x5E; + sc->are_eaddr[5] = 0x6B; + + // try to get from hints + if (!resource_string_value(device_get_name(dev), + device_get_unit(dev), "macaddr", (const char **)&local_macstr)) { + uint32_t tmpmac[ETHER_ADDR_LEN]; + + /* Have a MAC address; should use it */ + device_printf(dev, "Overriding MAC address from environment: '%s'\n", + local_macstr); + + /* Extract out the MAC address */ + /* XXX this should all be a generic method */ + count = sscanf(local_macstr, "%x%*c%x%*c%x%*c%x%*c%x%*c%x", + &tmpmac[0], &tmpmac[1], + &tmpmac[2], &tmpmac[3], + &tmpmac[4], &tmpmac[5]); + if (count == 6) { + /* Valid! */ + for (i = 0; i < ETHER_ADDR_LEN; i++) + sc->are_eaddr[i] = tmpmac[i]; + } + } + + mtx_init(&sc->are_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); + callout_init_mtx(&sc->are_stat_callout, &sc->are_mtx, 0); + TASK_INIT(&sc->are_link_task, 0, are_link_task, sc); + + /* Map control/status registers. */ + sc->are_rid = 0; + sc->are_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->are_rid, + RF_ACTIVE | RF_SHAREABLE); + + if (sc->are_res == NULL) { + device_printf(dev, "couldn't map memory\n"); + error = ENXIO; + goto fail; + } + + sc->are_btag = rman_get_bustag(sc->are_res); + sc->are_bhandle = rman_get_bushandle(sc->are_res); + +#ifndef INTRNG + /* Allocate interrupts */ + rid = 0; + sc->are_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->are_irq == NULL) { + device_printf(dev, "couldn't map interrupt\n"); + error = ENXIO; + goto fail; + } +#endif + + /* Allocate ifnet structure. */ + ifp = sc->are_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 = are_ioctl; + ifp->if_start = are_start; + ifp->if_init = are_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; + + if (are_dma_alloc(sc) != 0) { + error = ENXIO; + goto fail; + } + + /* TODO: calculate prescale */ +/* + CSR_WRITE_4(sc, ARE_ETHMCP, (165000000 / (1250000 + 1)) & ~1); + + CSR_WRITE_4(sc, ARE_MIIMCFG, ARE_MIIMCFG_R); + DELAY(1000); + CSR_WRITE_4(sc, ARE_MIIMCFG, 0); +*/ + CSR_WRITE_4(sc, CSR_BUSMODE, BUSMODE_SWR); + DELAY(1000); + +#ifdef ARE_MDIO + sc->are_miiproxy = mii_attach_proxy(sc->are_dev); +#endif + +#ifdef ARE_MII + /* Do MII setup. */ + error = mii_attach(dev, &sc->are_miibus, ifp, are_ifmedia_upd, + are_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0); + if (error != 0) { + device_printf(dev, "attaching PHYs failed\n"); + goto fail; + } +#else + ifmedia_init(&sc->are_ifmedia, 0, are_ifmedia_upd, are_ifmedia_sts); + + ifmedia_add(&sc->are_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL); + ifmedia_set(&sc->are_ifmedia, IFM_ETHER | IFM_AUTO); +#endif + + /* Call MI attach routine. */ + ether_ifattach(ifp, sc->are_eaddr); + +#ifdef INTRNG + char *name; + if(ar531x_soc >= AR531X_SOC_AR5315) { + enetirq = AR5315_CPU_IRQ_ENET; + name = "enet"; + } else { + if(device_get_unit(dev) == 0) { + enetirq = AR5312_IRQ_ENET0; + name = "enet0"; + } else { + enetirq = AR5312_IRQ_ENET1; + name = "enet1"; + } + } + cpu_establish_hardintr(name, NULL, are_intr, sc, enetirq, + INTR_TYPE_NET, NULL); +#else + /* Hook interrupt last to avoid having to lock softc */ + error = bus_setup_intr(dev, sc->are_irq, INTR_TYPE_NET | INTR_MPSAFE, + NULL, are_intr, sc, &sc->are_intrhand); + + if (error) { + device_printf(dev, "couldn't set up irq\n"); + ether_ifdetach(ifp); + goto fail; + } +#endif + +fail: + if (error) + are_detach(dev); + + return (error); +} + +static int +are_detach(device_t dev) +{ + struct are_softc *sc = device_get_softc(dev); + struct ifnet *ifp = sc->are_ifp; + + KASSERT(mtx_initialized(&sc->are_mtx), ("vr mutex not initialized")); + + /* These should only be active if attach succeeded */ + if (device_is_attached(dev)) { + ARE_LOCK(sc); + sc->are_detach = 1; + are_stop(sc); + ARE_UNLOCK(sc); + taskqueue_drain(taskqueue_swi, &sc->are_link_task); + ether_ifdetach(ifp); + } +#ifdef ARE_MII + if (sc->are_miibus) + device_delete_child(dev, sc->are_miibus); +#endif + bus_generic_detach(dev); + + if (sc->are_intrhand) + bus_teardown_intr(dev, sc->are_irq, sc->are_intrhand); + if (sc->are_irq) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->are_irq); + + if (sc->are_res) + bus_release_resource(dev, SYS_RES_MEMORY, sc->are_rid, + sc->are_res); + + if (ifp) + if_free(ifp); + + are_dma_free(sc); + + mtx_destroy(&sc->are_mtx); + + return (0); + +} + +static int +are_suspend(device_t dev) +{ + + panic("%s", __func__); + return 0; +} + +static int +are_resume(device_t dev) +{ + + panic("%s", __func__); + return 0; +} + +static int +are_shutdown(device_t dev) +{ + struct are_softc *sc; + + sc = device_get_softc(dev); + + ARE_LOCK(sc); + are_stop(sc); + ARE_UNLOCK(sc); + + return (0); +} + +static int +are_miibus_readreg(device_t dev, int phy, int reg) +{ + struct are_softc * sc = device_get_softc(dev); + uint32_t addr; + int i; + + addr = (phy << MIIADDR_PHY_SHIFT) | (reg << MIIADDR_REG_SHIFT); + CSR_WRITE_4(sc, CSR_MIIADDR, addr); +// AE_BARRIER(sc); + for (i = 0; i < 100000000; i++) { + if ((CSR_READ_4(sc, CSR_MIIADDR) & MIIADDR_BUSY) == 0) + break; + } + + return (CSR_READ_4(sc, CSR_MIIDATA) & 0xffff); +} + +static int +are_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct are_softc * sc = device_get_softc(dev); + uint32_t addr; + int i; + + /* write the data register */ + CSR_WRITE_4(sc, CSR_MIIDATA, data); + + /* write the address to latch it in */ + addr = (phy << MIIADDR_PHY_SHIFT) | (reg << MIIADDR_REG_SHIFT) | + MIIADDR_WRITE; + CSR_WRITE_4(sc, CSR_MIIADDR, addr); +// AE_BARRIER(sc); + + for (i = 0; i < 100000000; i++) { + if ((CSR_READ_4(sc, CSR_MIIADDR) & MIIADDR_BUSY) == 0) + break; + } + + return (0); +} + +static void +are_miibus_statchg(device_t dev) +{ + struct are_softc *sc; + + sc = device_get_softc(dev); + taskqueue_enqueue(taskqueue_swi, &sc->are_link_task); +} + +static void +are_link_task(void *arg, int pending) +{ +#ifdef ARE_MII + struct are_softc *sc; + struct mii_data *mii; + struct ifnet *ifp; + /* int lfdx, mfdx; */ + + sc = (struct are_softc *)arg; + + ARE_LOCK(sc); + mii = device_get_softc(sc->are_miibus); + ifp = sc->are_ifp; + if (mii == NULL || ifp == NULL || + (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + ARE_UNLOCK(sc); + return; + } + + if (mii->mii_media_status & IFM_ACTIVE) { + if (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) + sc->are_link_status = 1; + } else + sc->are_link_status = 0; + + ARE_UNLOCK(sc); +#endif +} + +static void +are_reset(struct are_softc *sc) +{ + int i; + + CSR_WRITE_4(sc, CSR_BUSMODE, BUSMODE_SWR); + + /* + * The chip doesn't take itself out of reset automatically. + * We need to do so after 2us. + */ + DELAY(10); + CSR_WRITE_4(sc, CSR_BUSMODE, 0); + + for (i = 0; i < 1000; i++) { + /* + * Wait a bit for the reset to complete before peeking + * at the chip again. + */ + DELAY(10); + if ((CSR_READ_4(sc, CSR_BUSMODE) & BUSMODE_SWR) == 0) + break; + } + + if (CSR_READ_4(sc, CSR_BUSMODE) & BUSMODE_SWR) + device_printf(sc->are_dev, "reset time out\n"); + + DELAY(1000); +} + +static void +are_init(void *xsc) +{ + struct are_softc *sc = xsc; + + ARE_LOCK(sc); + are_init_locked(sc); + ARE_UNLOCK(sc); +} + +static void +are_init_locked(struct are_softc *sc) +{ + struct ifnet *ifp = sc->are_ifp; +#ifdef ARE_MII + struct mii_data *mii; +#endif + + ARE_LOCK_ASSERT(sc); + +#ifdef ARE_MII + mii = device_get_softc(sc->are_miibus); +#endif + + are_stop(sc); + are_reset(sc); + + /* Init circular RX list. */ + if (are_rx_ring_init(sc) != 0) { + device_printf(sc->are_dev, + "initialization failed: no memory for rx buffers\n"); + are_stop(sc); + return; + } + + /* Init tx descriptors. */ + are_tx_ring_init(sc); + + /* + * Initialize the BUSMODE register. + */ + CSR_WRITE_4(sc, CSR_BUSMODE, + /* XXX: not sure if this is a good thing or not... */ + //BUSMODE_ALIGN_16B | + BUSMODE_BAR | BUSMODE_BLE | BUSMODE_PBL_4LW); + + /* + * Initialize the interrupt mask and enable interrupts. + */ + /* normal interrupts */ + sc->sc_inten = STATUS_TI | STATUS_TU | STATUS_RI | STATUS_NIS; + + /* abnormal interrupts */ + sc->sc_inten |= STATUS_TPS | STATUS_TJT | STATUS_UNF | + STATUS_RU | STATUS_RPS | STATUS_SE | STATUS_AIS; + + sc->sc_rxint_mask = STATUS_RI|STATUS_RU; + sc->sc_txint_mask = STATUS_TI|STATUS_UNF|STATUS_TJT; + + sc->sc_rxint_mask &= sc->sc_inten; + sc->sc_txint_mask &= sc->sc_inten; + + CSR_WRITE_4(sc, CSR_INTEN, sc->sc_inten); + CSR_WRITE_4(sc, CSR_STATUS, 0xffffffff); + + /* + * Give the transmit and receive rings to the chip. + */ + CSR_WRITE_4(sc, CSR_TXLIST, ARE_TX_RING_ADDR(sc, 0)); + CSR_WRITE_4(sc, CSR_RXLIST, ARE_RX_RING_ADDR(sc, 0)); + + /* + * Set the station address. + */ + CSR_WRITE_4(sc, CSR_MACHI, sc->are_eaddr[5] << 16 | sc->are_eaddr[4]); + CSR_WRITE_4(sc, CSR_MACLO, sc->are_eaddr[3] << 24 | + sc->are_eaddr[2] << 16 | sc->are_eaddr[1] << 8 | sc->are_eaddr[0]); + + /* + * Start the mac. + */ + CSR_WRITE_4(sc, CSR_MACCTL, CSR_READ_4(sc, CSR_MACCTL) | + (MACCTL_RE | MACCTL_TE)); + + /* + * Write out the opmode. + */ + CSR_WRITE_4(sc, CSR_OPMODE, OPMODE_SR | OPMODE_ST | +// ae_txthresh[sc->sc_txthresh].txth_opmode); + OPMODE_TR_64); + /* + * Start the receive process. + */ + CSR_WRITE_4(sc, CSR_RXPOLL, RXPOLL_RPD); + + sc->are_link_status = 1; +#ifdef ARE_MII + mii_mediachg(mii); +#endif + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + callout_reset(&sc->are_stat_callout, hz, are_tick, sc); +} + +static void +are_start(struct ifnet *ifp) +{ + struct are_softc *sc; + + sc = ifp->if_softc; + + ARE_LOCK(sc); + are_start_locked(ifp); + ARE_UNLOCK(sc); +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int +are_encap(struct are_softc *sc, struct mbuf **m_head) +{ + struct are_txdesc *txd; + struct are_desc *desc, *prev_desc; + bus_dma_segment_t txsegs[ARE_MAXFRAGS]; + uint32_t link_addr; + int error, i, nsegs, prod, si, prev_prod; + int txstat; + + ARE_LOCK_ASSERT(sc); + + prod = sc->are_cdata.are_tx_prod; + txd = &sc->are_cdata.are_txdesc[prod]; + error = bus_dmamap_load_mbuf_sg(sc->are_cdata.are_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->are_cdata.are_tx_cnt + nsegs >= (ARE_TX_RING_CNT - 1)) { + bus_dmamap_unload(sc->are_cdata.are_tx_tag, txd->tx_dmamap); + return (ENOBUFS); + } + + txd->tx_m = *m_head; + bus_dmamap_sync(sc->are_cdata.are_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 are_link is not zero. The last one should + * have COF flag set, to pickup next chain from NDPTR + */ + prev_prod = prod; + desc = prev_desc = NULL; + for (i = 0; i < nsegs; i++) { + desc = &sc->are_rdata.are_tx_ring[prod]; + desc->are_stat = ADSTAT_OWN; + desc->are_devcs = ARE_DMASIZE(txsegs[i].ds_len) | ADCTL_CH; + if (i == 0) + desc->are_devcs |= ADCTL_Tx_FS; + desc->are_addr = txsegs[i].ds_addr; +// desc->are_link = 0; + /* link with previous descriptor */ + if (prev_desc) + prev_desc->are_link = ARE_TX_RING_ADDR(sc, prod); + + sc->are_cdata.are_tx_cnt++; + prev_desc = desc; + ARE_INC(prod, ARE_TX_RING_CNT); + } + + /* + * Set mark last fragment with LD flag + */ + if (desc) { + desc->are_devcs |= ADCTL_Tx_IC; + desc->are_devcs |= ADCTL_Tx_LS; + } + + /* Update producer index. */ + sc->are_cdata.are_tx_prod = prod; + + /* Sync descriptors. */ + bus_dmamap_sync(sc->are_cdata.are_tx_ring_tag, + sc->are_cdata.are_tx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + /* Start transmitting */ + /* Check if new list is queued in NDPTR */ + txstat = (CSR_READ_4(sc, CSR_STATUS) >> 20) & 7; + if (txstat == 0 || txstat == 6) { + /* Transmit Process Stat is stop or suspended */ + CSR_WRITE_4(sc, CSR_TXPOLL, TXPOLL_TPD); + } + else { + link_addr = ARE_TX_RING_ADDR(sc, si); + /* Get previous descriptor */ + si = (si + ARE_TX_RING_CNT - 1) % ARE_TX_RING_CNT; + desc = &sc->are_rdata.are_tx_ring[si]; + desc->are_link = link_addr; + } + + return (0); +} + +static void +are_start_locked(struct ifnet *ifp) +{ + struct are_softc *sc; + struct mbuf *m_head; + int enq; + + sc = ifp->if_softc; + + ARE_LOCK_ASSERT(sc); + + if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING || sc->are_link_status == 0 ) + return; + + for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && + sc->are_cdata.are_tx_cnt < ARE_TX_RING_CNT - 2; ) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + /* + * Pack the data into the transmit ring. If we + * don't have room, set the OACTIVE flag and wait + * for the NIC to drain the ring. + */ + if (are_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 +are_stop(struct are_softc *sc) +{ + struct ifnet *ifp; + + ARE_LOCK_ASSERT(sc); + + ifp = sc->are_ifp; + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + callout_stop(&sc->are_stat_callout); + + /* Disable interrupts. */ + CSR_WRITE_4(sc, CSR_INTEN, 0); + + /* Stop the transmit and receive processes. */ + CSR_WRITE_4(sc, CSR_OPMODE, 0); + CSR_WRITE_4(sc, CSR_RXLIST, 0); + CSR_WRITE_4(sc, CSR_TXLIST, 0); + CSR_WRITE_4(sc, CSR_MACCTL, + CSR_READ_4(sc, CSR_MACCTL) & ~(MACCTL_TE | MACCTL_RE)); + +} + + +static int +are_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct are_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; +#ifdef ARE_MII + struct mii_data *mii; +#endif + int error; + + switch (command) { + case SIOCSIFFLAGS: +#if 0 + ARE_LOCK(sc); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if ((ifp->if_flags ^ sc->are_if_flags) & + (IFF_PROMISC | IFF_ALLMULTI)) + are_set_filter(sc); + } else { + if (sc->are_detach == 0) + are_init_locked(sc); + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + are_stop(sc); + } + sc->are_if_flags = ifp->if_flags; + ARE_UNLOCK(sc); +#endif + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: +#if 0 + ARE_LOCK(sc); + are_set_filter(sc); + ARE_UNLOCK(sc); +#endif + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: +#ifdef ARE_MII + mii = device_get_softc(sc->are_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); +#else + error = ifmedia_ioctl(ifp, ifr, &sc->are_ifmedia, command); +#endif + break; + case SIOCSIFCAP: + error = 0; +#if 0 + mask = ifr->ifr_reqcap ^ ifp->if_capenable; + if ((mask & IFCAP_HWCSUM) != 0) { + ifp->if_capenable ^= IFCAP_HWCSUM; + if ((IFCAP_HWCSUM & ifp->if_capenable) && + (IFCAP_HWCSUM & ifp->if_capabilities)) + ifp->if_hwassist = ARE_CSUM_FEATURES; + else + ifp->if_hwassist = 0; + } + if ((mask & IFCAP_VLAN_HWTAGGING) != 0) { + ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; + if (IFCAP_VLAN_HWTAGGING & ifp->if_capenable && + IFCAP_VLAN_HWTAGGING & ifp->if_capabilities && + ifp->if_drv_flags & IFF_DRV_RUNNING) { + ARE_LOCK(sc); + are_vlan_setup(sc); + ARE_UNLOCK(sc); + } + } + VLAN_CAPABILITIES(ifp); +#endif + break; + default: + error = ether_ioctl(ifp, command, data); + break; + } + + return (error); +} + +/* + * Set media options. + */ +static int +are_ifmedia_upd(struct ifnet *ifp) +{ +#ifdef ARE_MII + struct are_softc *sc; + struct mii_data *mii; + struct mii_softc *miisc; + int error; + + sc = ifp->if_softc; + ARE_LOCK(sc); + mii = device_get_softc(sc->are_miibus); + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + PHY_RESET(miisc); + error = mii_mediachg(mii); + ARE_UNLOCK(sc); + + return (error); +#else + return (0); +#endif +} + +/* + * Report current media status. + */ +static void +are_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ +#ifdef ARE_MII + struct are_softc *sc = ifp->if_softc; + struct mii_data *mii; + + mii = device_get_softc(sc->are_miibus); + ARE_LOCK(sc); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + ARE_UNLOCK(sc); +#else + ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; +#endif +} + +struct are_dmamap_arg { + bus_addr_t are_busaddr; +}; + +static void +are_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct are_dmamap_arg *ctx; + + if (error != 0) + return; + ctx = arg; + ctx->are_busaddr = segs[0].ds_addr; +} + +static int +are_dma_alloc(struct are_softc *sc) +{ + struct are_dmamap_arg ctx; + struct are_txdesc *txd; + struct are_rxdesc *rxd; + int error, i; + + /* Create parent DMA tag. */ + error = bus_dma_tag_create( + bus_get_dma_tag(sc->are_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->are_cdata.are_parent_tag); + if (error != 0) { + device_printf(sc->are_dev, "failed to create parent DMA tag\n"); + goto fail; + } + /* Create tag for Tx ring. */ + error = bus_dma_tag_create( + sc->are_cdata.are_parent_tag, /* parent */ + ARE_RING_ALIGN, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + ARE_TX_RING_SIZE, /* maxsize */ + 1, /* nsegments */ + ARE_TX_RING_SIZE, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->are_cdata.are_tx_ring_tag); + if (error != 0) { + device_printf(sc->are_dev, "failed to create Tx ring DMA tag\n"); + goto fail; + } + + /* Create tag for Rx ring. */ + error = bus_dma_tag_create( + sc->are_cdata.are_parent_tag, /* parent */ + ARE_RING_ALIGN, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + ARE_RX_RING_SIZE, /* maxsize */ + 1, /* nsegments */ + ARE_RX_RING_SIZE, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->are_cdata.are_rx_ring_tag); + if (error != 0) { + device_printf(sc->are_dev, "failed to create Rx ring DMA tag\n"); + goto fail; + } + + /* Create tag for Tx buffers. */ + error = bus_dma_tag_create( + sc->are_cdata.are_parent_tag, /* parent */ + sizeof(uint32_t), 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES * ARE_MAXFRAGS, /* maxsize */ + ARE_MAXFRAGS, /* nsegments */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->are_cdata.are_tx_tag); + if (error != 0) { + device_printf(sc->are_dev, "failed to create Tx DMA tag\n"); + goto fail; + } + + /* Create tag for Rx buffers. */ + error = bus_dma_tag_create( + sc->are_cdata.are_parent_tag, /* parent */ + ARE_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->are_cdata.are_rx_tag); + if (error != 0) { + device_printf(sc->are_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->are_cdata.are_tx_ring_tag, + (void **)&sc->are_rdata.are_tx_ring, BUS_DMA_WAITOK | + BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->are_cdata.are_tx_ring_map); + if (error != 0) { + device_printf(sc->are_dev, + "failed to allocate DMA'able memory for Tx ring\n"); + goto fail; + } + + ctx.are_busaddr = 0; + error = bus_dmamap_load(sc->are_cdata.are_tx_ring_tag, + sc->are_cdata.are_tx_ring_map, sc->are_rdata.are_tx_ring, + ARE_TX_RING_SIZE, are_dmamap_cb, &ctx, 0); + if (error != 0 || ctx.are_busaddr == 0) { + device_printf(sc->are_dev, + "failed to load DMA'able memory for Tx ring\n"); + goto fail; + } + sc->are_rdata.are_tx_ring_paddr = ctx.are_busaddr; + + /* Allocate DMA'able memory and load the DMA map for Rx ring. */ + error = bus_dmamem_alloc(sc->are_cdata.are_rx_ring_tag, + (void **)&sc->are_rdata.are_rx_ring, BUS_DMA_WAITOK | + BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->are_cdata.are_rx_ring_map); + if (error != 0) { + device_printf(sc->are_dev, + "failed to allocate DMA'able memory for Rx ring\n"); + goto fail; + } + + ctx.are_busaddr = 0; + error = bus_dmamap_load(sc->are_cdata.are_rx_ring_tag, + sc->are_cdata.are_rx_ring_map, sc->are_rdata.are_rx_ring, + ARE_RX_RING_SIZE, are_dmamap_cb, &ctx, 0); + if (error != 0 || ctx.are_busaddr == 0) { + device_printf(sc->are_dev, + "failed to load DMA'able memory for Rx ring\n"); + goto fail; + } + sc->are_rdata.are_rx_ring_paddr = ctx.are_busaddr; + + /* Create DMA maps for Tx buffers. */ + for (i = 0; i < ARE_TX_RING_CNT; i++) { + txd = &sc->are_cdata.are_txdesc[i]; + txd->tx_m = NULL; + txd->tx_dmamap = NULL; + error = bus_dmamap_create(sc->are_cdata.are_tx_tag, 0, + &txd->tx_dmamap); + if (error != 0) { + device_printf(sc->are_dev, + "failed to create Tx dmamap\n"); + goto fail; + } + } + /* Create DMA maps for Rx buffers. */ + if ((error = bus_dmamap_create(sc->are_cdata.are_rx_tag, 0, + &sc->are_cdata.are_rx_sparemap)) != 0) { + device_printf(sc->are_dev, + "failed to create spare Rx dmamap\n"); + goto fail; + } + for (i = 0; i < ARE_RX_RING_CNT; i++) { + rxd = &sc->are_cdata.are_rxdesc[i]; + rxd->rx_m = NULL; + rxd->rx_dmamap = NULL; + error = bus_dmamap_create(sc->are_cdata.are_rx_tag, 0, + &rxd->rx_dmamap); + if (error != 0) { + device_printf(sc->are_dev, + "failed to create Rx dmamap\n"); + goto fail; + } + } + +fail: + return (error); +} + +static void +are_dma_free(struct are_softc *sc) +{ + struct are_txdesc *txd; + struct are_rxdesc *rxd; + int i; + + /* Tx ring. */ + if (sc->are_cdata.are_tx_ring_tag) { + if (sc->are_rdata.are_tx_ring_paddr) + bus_dmamap_unload(sc->are_cdata.are_tx_ring_tag, + sc->are_cdata.are_tx_ring_map); + if (sc->are_rdata.are_tx_ring) + bus_dmamem_free(sc->are_cdata.are_tx_ring_tag, + sc->are_rdata.are_tx_ring, + sc->are_cdata.are_tx_ring_map); + sc->are_rdata.are_tx_ring = NULL; + sc->are_rdata.are_tx_ring_paddr = 0; + bus_dma_tag_destroy(sc->are_cdata.are_tx_ring_tag); + sc->are_cdata.are_tx_ring_tag = NULL; + } + /* Rx ring. */ + if (sc->are_cdata.are_rx_ring_tag) { + if (sc->are_rdata.are_rx_ring_paddr) + bus_dmamap_unload(sc->are_cdata.are_rx_ring_tag, + sc->are_cdata.are_rx_ring_map); + if (sc->are_rdata.are_rx_ring) + bus_dmamem_free(sc->are_cdata.are_rx_ring_tag, + sc->are_rdata.are_rx_ring, + sc->are_cdata.are_rx_ring_map); + sc->are_rdata.are_rx_ring = NULL; + sc->are_rdata.are_rx_ring_paddr = 0; + bus_dma_tag_destroy(sc->are_cdata.are_rx_ring_tag); + sc->are_cdata.are_rx_ring_tag = NULL; + } + /* Tx buffers. */ + if (sc->are_cdata.are_tx_tag) { + for (i = 0; i < ARE_TX_RING_CNT; i++) { + txd = &sc->are_cdata.are_txdesc[i]; + if (txd->tx_dmamap) { + bus_dmamap_destroy(sc->are_cdata.are_tx_tag, + txd->tx_dmamap); + txd->tx_dmamap = NULL; + } + } + bus_dma_tag_destroy(sc->are_cdata.are_tx_tag); + sc->are_cdata.are_tx_tag = NULL; + } + /* Rx buffers. */ + if (sc->are_cdata.are_rx_tag) { + for (i = 0; i < ARE_RX_RING_CNT; i++) { + rxd = &sc->are_cdata.are_rxdesc[i]; + if (rxd->rx_dmamap) { + bus_dmamap_destroy(sc->are_cdata.are_rx_tag, + rxd->rx_dmamap); + rxd->rx_dmamap = NULL; + } + } + if (sc->are_cdata.are_rx_sparemap) { + bus_dmamap_destroy(sc->are_cdata.are_rx_tag, + sc->are_cdata.are_rx_sparemap); + sc->are_cdata.are_rx_sparemap = 0; + } + bus_dma_tag_destroy(sc->are_cdata.are_rx_tag); + sc->are_cdata.are_rx_tag = NULL; + } + + if (sc->are_cdata.are_parent_tag) { + bus_dma_tag_destroy(sc->are_cdata.are_parent_tag); + sc->are_cdata.are_parent_tag = NULL; + } +} + +/* + * Initialize the transmit descriptors. + */ +static int +are_tx_ring_init(struct are_softc *sc) +{ + struct are_ring_data *rd; + struct are_txdesc *txd; + bus_addr_t addr; + int i; + + sc->are_cdata.are_tx_prod = 0; + sc->are_cdata.are_tx_cons = 0; + sc->are_cdata.are_tx_cnt = 0; + sc->are_cdata.are_tx_pkts = 0; + + rd = &sc->are_rdata; + bzero(rd->are_tx_ring, ARE_TX_RING_SIZE); + for (i = 0; i < ARE_TX_RING_CNT; i++) { + if (i == ARE_TX_RING_CNT - 1) + addr = ARE_TX_RING_ADDR(sc, 0); + else + addr = ARE_TX_RING_ADDR(sc, i + 1); + rd->are_tx_ring[i].are_stat = 0; + rd->are_tx_ring[i].are_devcs = 0; + rd->are_tx_ring[i].are_addr = 0; + rd->are_tx_ring[i].are_link = addr; + txd = &sc->are_cdata.are_txdesc[i]; + txd->tx_m = NULL; + } + + bus_dmamap_sync(sc->are_cdata.are_tx_ring_tag, + sc->are_cdata.are_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 +are_rx_ring_init(struct are_softc *sc) +{ + struct are_ring_data *rd; + struct are_rxdesc *rxd; + bus_addr_t addr; + int i; + + sc->are_cdata.are_rx_cons = 0; + + rd = &sc->are_rdata; + bzero(rd->are_rx_ring, ARE_RX_RING_SIZE); + for (i = 0; i < ARE_RX_RING_CNT; i++) { + rxd = &sc->are_cdata.are_rxdesc[i]; + rxd->rx_m = NULL; + rxd->desc = &rd->are_rx_ring[i]; + if (i == ARE_RX_RING_CNT - 1) + addr = ARE_RX_RING_ADDR(sc, 0); + else + addr = ARE_RX_RING_ADDR(sc, i + 1); + rd->are_rx_ring[i].are_stat = ADSTAT_OWN; + rd->are_rx_ring[i].are_devcs = ADCTL_CH; + if (i == ARE_RX_RING_CNT - 1) + rd->are_rx_ring[i].are_devcs |= ADCTL_ER; + rd->are_rx_ring[i].are_addr = 0; + rd->are_rx_ring[i].are_link = addr; + if (are_newbuf(sc, i) != 0) + return (ENOBUFS); + } + + bus_dmamap_sync(sc->are_cdata.are_rx_ring_tag, + sc->are_cdata.are_rx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + return (0); +} + +/* + * Initialize an RX descriptor and attach an MBUF cluster. + */ +static int +are_newbuf(struct are_softc *sc, int idx) +{ + struct are_desc *desc; + struct are_rxdesc *rxd; + struct mbuf *m; + bus_dma_segment_t segs[1]; + bus_dmamap_t map; + int nsegs; + + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) + return (ENOBUFS); + m->m_len = m->m_pkthdr.len = MCLBYTES; + + // tcp header boundary margin + m_adj(m, 4); + + if (bus_dmamap_load_mbuf_sg(sc->are_cdata.are_rx_tag, + sc->are_cdata.are_rx_sparemap, m, segs, &nsegs, 0) != 0) { + m_freem(m); + return (ENOBUFS); + } + KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); + + rxd = &sc->are_cdata.are_rxdesc[idx]; + if (rxd->rx_m != NULL) { +// This code make bug. Make scranble on buffer data. +// bus_dmamap_sync(sc->are_cdata.are_rx_tag, rxd->rx_dmamap, +// BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->are_cdata.are_rx_tag, rxd->rx_dmamap); + } + map = rxd->rx_dmamap; + rxd->rx_dmamap = sc->are_cdata.are_rx_sparemap; + sc->are_cdata.are_rx_sparemap = map; + bus_dmamap_sync(sc->are_cdata.are_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_PREREAD); + rxd->rx_m = m; + desc = rxd->desc; + desc->are_addr = segs[0].ds_addr; + desc->are_devcs |= ARE_DMASIZE(segs[0].ds_len); + rxd->saved_ca = desc->are_addr ; + rxd->saved_ctl = desc->are_stat ; + + return (0); +} + +static __inline void +are_fixup_rx(struct mbuf *m) +{ + int i; + uint16_t *src, *dst; + + src = mtod(m, uint16_t *); + dst = src - 1; + + for (i = 0; i < m->m_len / sizeof(uint16_t); i++) { + *dst++ = *src++; + } + + if (m->m_len % sizeof(uint16_t)) + *(uint8_t *)dst = *(uint8_t *)src; + + m->m_data -= ETHER_ALIGN; +} + + +static void +are_tx(struct are_softc *sc) +{ + struct are_txdesc *txd; + struct are_desc *cur_tx; + struct ifnet *ifp; + uint32_t ctl, devcs; + int cons, prod; + + ARE_LOCK_ASSERT(sc); + + cons = sc->are_cdata.are_tx_cons; + prod = sc->are_cdata.are_tx_prod; + if (cons == prod) + return; + + bus_dmamap_sync(sc->are_cdata.are_tx_ring_tag, + sc->are_cdata.are_tx_ring_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + ifp = sc->are_ifp; + /* + * Go through our tx list and free mbufs for those + * frames that have been transmitted. + */ + for (; cons != prod; ARE_INC(cons, ARE_TX_RING_CNT)) { + cur_tx = &sc->are_rdata.are_tx_ring[cons]; + ctl = cur_tx->are_stat; + devcs = cur_tx->are_devcs; + /* Check if descriptor has "finished" flag */ + if (ARE_DMASIZE(devcs) == 0) + break; + + sc->are_cdata.are_tx_cnt--; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + txd = &sc->are_cdata.are_txdesc[cons]; + + if ((ctl & ADSTAT_Tx_ES) == 0) + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + else { + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + } + + bus_dmamap_sync(sc->are_cdata.are_tx_tag, txd->tx_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->are_cdata.are_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->are_stat = 0; + cur_tx->are_devcs = 0; + cur_tx->are_addr = 0; + } + + sc->are_cdata.are_tx_cons = cons; + + bus_dmamap_sync(sc->are_cdata.are_tx_ring_tag, + sc->are_cdata.are_tx_ring_map, BUS_DMASYNC_PREWRITE); +} + + +static void +are_rx(struct are_softc *sc) +{ + struct are_rxdesc *rxd; + struct ifnet *ifp = sc->are_ifp; + int cons, prog, packet_len, error; + struct are_desc *cur_rx; + struct mbuf *m; + + ARE_LOCK_ASSERT(sc); + + cons = sc->are_cdata.are_rx_cons; + + bus_dmamap_sync(sc->are_cdata.are_rx_ring_tag, + sc->are_cdata.are_rx_ring_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + for (prog = 0; prog < ARE_RX_RING_CNT; ARE_INC(cons, ARE_RX_RING_CNT)) { + cur_rx = &sc->are_rdata.are_rx_ring[cons]; + rxd = &sc->are_cdata.are_rxdesc[cons]; + m = rxd->rx_m; + + if ((cur_rx->are_stat & ADSTAT_OWN) == ADSTAT_OWN) + break; + + prog++; + + packet_len = ADSTAT_Rx_LENGTH(cur_rx->are_stat); + /* Assume it's error */ + error = 1; + + if (packet_len < 64) + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + else if ((cur_rx->are_stat & ADSTAT_Rx_DE) == 0) { + error = 0; + bus_dmamap_sync(sc->are_cdata.are_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + m = rxd->rx_m; + /* Skip 4 bytes of CRC */ + m->m_pkthdr.len = m->m_len = packet_len - ETHER_CRC_LEN; + are_fixup_rx(m); + m->m_pkthdr.rcvif = ifp; + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); + + ARE_UNLOCK(sc); + (*ifp->if_input)(ifp, m); + ARE_LOCK(sc); + } + + if (error) { + /* Restore CONTROL and CA values, reset DEVCS */ + cur_rx->are_stat = rxd->saved_ctl; + cur_rx->are_addr = rxd->saved_ca; + cur_rx->are_devcs = 0; + } + else { + /* Reinit descriptor */ + cur_rx->are_stat = ADSTAT_OWN; + cur_rx->are_devcs = 0; + if (cons == ARE_RX_RING_CNT - 1) + cur_rx->are_devcs |= ADCTL_ER; + cur_rx->are_addr = 0; + if (are_newbuf(sc, cons) != 0) { + device_printf(sc->are_dev, + "Failed to allocate buffer\n"); + break; + } + } + + bus_dmamap_sync(sc->are_cdata.are_rx_ring_tag, + sc->are_cdata.are_rx_ring_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + } + + if (prog > 0) { + sc->are_cdata.are_rx_cons = cons; + + bus_dmamap_sync(sc->are_cdata.are_rx_ring_tag, + sc->are_cdata.are_rx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + } +} + +static void +are_intr(void *arg) +{ + struct are_softc *sc = arg; + uint32_t status; + struct ifnet *ifp = sc->are_ifp; +//kdb_break(); + + ARE_LOCK(sc); + + /* mask out interrupts */ + + status = CSR_READ_4(sc, CSR_STATUS); + if (status) { + CSR_WRITE_4(sc, CSR_STATUS, status); + } + if (status & sc->sc_rxint_mask) { + are_rx(sc); + } + if (status & sc->sc_txint_mask) { + are_tx(sc); + } + + /* Try to get more packets going. */ + are_start(ifp); + + ARE_UNLOCK(sc); +} + +static void +are_tick(void *xsc) +{ +#ifdef ARE_MII + struct are_softc *sc = xsc; + struct mii_data *mii; + + ARE_LOCK_ASSERT(sc); + + mii = device_get_softc(sc->are_miibus); + mii_tick(mii); + callout_reset(&sc->are_stat_callout, hz, are_tick, sc); +#endif +} + +static void +are_hinted_child(device_t bus, const char *dname, int dunit) +{ + BUS_ADD_CHILD(bus, 0, dname, dunit); + device_printf(bus, "hinted child %s%d\n", dname, dunit); +} + +#ifdef ARE_MDIO +static int +aremdio_probe(device_t dev) +{ + device_set_desc(dev, "Atheros AR531x built-in ethernet interface, MDIO controller"); + return(0); +} + +static int +aremdio_attach(device_t dev) +{ + struct are_softc *sc; + int error = 0; + + sc = device_get_softc(dev); + sc->are_dev = dev; + sc->are_rid = 0; + sc->are_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->are_rid, RF_ACTIVE | RF_SHAREABLE); + if (sc->are_res == NULL) { + device_printf(dev, "couldn't map memory\n"); + error = ENXIO; + goto fail; + } + + sc->are_btag = rman_get_bustag(sc->are_res); + sc->are_bhandle = rman_get_bushandle(sc->are_res); + + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + error = bus_generic_attach(dev); +fail: + return (error); +} + +static int +aremdio_detach(device_t dev) +{ + return(0); +} +#endif + +#ifdef ARE_DEBUG +void +dump_txdesc(struct are_softc *sc, int pos) +{ + struct are_desc *desc; + + desc = &sc->are_rdata.are_tx_ring[pos]; + device_printf(sc->are_dev, "CSR_TXLIST %08x\n", CSR_READ_4(sc, CSR_TXLIST)); + device_printf(sc->are_dev, "CSR_HTBA %08x\n", CSR_READ_4(sc, CSR_HTBA)); + device_printf(sc->are_dev, "%d TDES0:%08x TDES1:%08x TDES2:%08x TDES3:%08x\n", + pos, desc->are_stat, desc->are_devcs, desc->are_addr, desc->are_link); +} + +void +dump_status_reg(struct are_softc *sc) +{ + uint32_t status; + + /* mask out interrupts */ + + device_printf(sc->are_dev, "CSR_HTBA %08x\n", CSR_READ_4(sc, CSR_HTBA)); + status = CSR_READ_4(sc, CSR_STATUS); + device_printf(sc->are_dev, "CSR5 Status Register EB:%d TS:%d RS:%d NIS:%d AIS:%d ER:%d SE:%d LNF:%d TM:%d RWT:%d RPS:%d RU:%d RI:%d UNF:%d LNP/ANC:%d TJT:%d TU:%d TPS:%d TI:%d\n", + (status >> 23 ) & 7, + (status >> 20 ) & 7, + (status >> 17 ) & 7, + (status >> 16 ) & 1, + (status >> 15 ) & 1, + (status >> 14 ) & 1, + (status >> 13 ) & 1, + (status >> 12 ) & 1, + (status >> 11 ) & 1, + (status >> 9 ) & 1, + (status >> 8 ) & 1, + (status >> 7 ) & 1, + (status >> 6 ) & 1, + (status >> 5 ) & 1, + (status >> 4 ) & 1, + (status >> 3 ) & 1, + (status >> 2 ) & 1, + (status >> 1 ) & 1, + (status >> 0 ) & 1); + +} +#endif Index: sys/mips/atheros/ar531x/if_arereg.h =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/if_arereg.h @@ -0,0 +1,401 @@ +/*- + * Copyright (C) 2007 + * 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 ``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 HIS RELATIVES 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 MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#ifndef __IF_AREREG_H__ +#define __IF_AREREG_H__ + +struct are_desc { + uint32_t are_stat; + uint32_t are_devcs; + uint32_t are_addr; + uint32_t are_link; +}; + +#define ARE_DMASIZE(len) ((len) & ((1 << 11)-1)) +#define ARE_PKTSIZE(len) ((len & 0xffff0000) >> 16) + +#define ARE_RX_RING_CNT 128 +#define ARE_TX_RING_CNT 128 +#define ARE_TX_RING_SIZE sizeof(struct are_desc) * ARE_TX_RING_CNT +#define ARE_RX_RING_SIZE sizeof(struct are_desc) * ARE_RX_RING_CNT +#define ARE_RING_ALIGN sizeof(struct are_desc) +#define ARE_RX_ALIGN sizeof(uint32_t) +#define ARE_MAXFRAGS 8 +#define ARE_TX_INTR_THRESH 8 + +#define ARE_TX_RING_ADDR(sc, i) \ + ((sc)->are_rdata.are_tx_ring_paddr + sizeof(struct are_desc) * (i)) +#define ARE_RX_RING_ADDR(sc, i) \ + ((sc)->are_rdata.are_rx_ring_paddr + sizeof(struct are_desc) * (i)) +#define ARE_INC(x,y) (x) = (((x) + 1) % y) + +struct are_txdesc { + struct mbuf *tx_m; + bus_dmamap_t tx_dmamap; +}; + +struct are_rxdesc { + struct mbuf *rx_m; + bus_dmamap_t rx_dmamap; + struct are_desc *desc; + /* Use this values on error instead of allocating new mbuf */ + uint32_t saved_ctl, saved_ca; +}; + +struct are_chain_data { + bus_dma_tag_t are_parent_tag; + bus_dma_tag_t are_tx_tag; + struct are_txdesc are_txdesc[ARE_TX_RING_CNT]; + bus_dma_tag_t are_rx_tag; + struct are_rxdesc are_rxdesc[ARE_RX_RING_CNT]; + bus_dma_tag_t are_tx_ring_tag; + bus_dma_tag_t are_rx_ring_tag; + bus_dmamap_t are_tx_ring_map; + bus_dmamap_t are_rx_ring_map; + bus_dmamap_t are_rx_sparemap; + int are_tx_pkts; + int are_tx_prod; + int are_tx_cons; + int are_tx_cnt; + int are_rx_cons; +}; + +struct are_ring_data { + struct are_desc *are_rx_ring; + struct are_desc *are_tx_ring; + bus_addr_t are_rx_ring_paddr; + bus_addr_t are_tx_ring_paddr; +}; + +struct are_softc { + struct ifnet *are_ifp; /* interface info */ + bus_space_handle_t are_bhandle; /* bus space handle */ + bus_space_tag_t are_btag; /* bus space tag */ + device_t are_dev; + uint8_t are_eaddr[ETHER_ADDR_LEN]; + struct resource *are_res; + int are_rid; + struct resource *are_irq; + void *are_intrhand; + u_int32_t sc_inten; /* copy of CSR_INTEN */ + u_int32_t sc_rxint_mask; /* mask of Rx interrupts we want */ + u_int32_t sc_txint_mask; /* mask of Tx interrupts we want */ +#ifdef ARE_MII + device_t are_miibus; +#else + struct ifmedia are_ifmedia; +#endif +#ifdef ARE_MDIO + device_t are_miiproxy; +#endif + bus_dma_tag_t are_parent_tag; + bus_dma_tag_t are_tag; + struct mtx are_mtx; + struct callout are_stat_callout; + struct task are_link_task; + struct are_chain_data are_cdata; + struct are_ring_data are_rdata; + int are_link_status; + int are_detach; +}; + +#define ARE_LOCK(_sc) mtx_lock(&(_sc)->are_mtx) +#define ARE_UNLOCK(_sc) mtx_unlock(&(_sc)->are_mtx) +#define ARE_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->are_mtx, MA_OWNED) + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->are_btag, sc->are_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->are_btag, sc->are_bhandle, reg) + + +/* $NetBSD: aereg.h,v 1.2 2008/04/28 20:23:28 martin Exp $ */ + +/*- + * Copyright (c) 1999, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Descriptor Status bits common to transmit and receive. + */ +#define ADSTAT_OWN 0x80000000 /* Tulip owns descriptor */ +#define ADSTAT_ES 0x00008000 /* Error Summary */ + +/* + * Descriptor Status bits for Receive Descriptor. + */ +#define ADSTAT_Rx_FF 0x40000000 /* Filtering Fail */ +#define ADSTAT_Rx_FL 0x3fff0000 /* Frame Length including CRC */ +#define ADSTAT_Rx_DE 0x00004000 /* Descriptor Error */ +#define ADSTAT_Rx_LE 0x00001000 /* Length Error */ +#define ADSTAT_Rx_RF 0x00000800 /* Runt Frame */ +#define ADSTAT_Rx_MF 0x00000400 /* Multicast Frame */ +#define ADSTAT_Rx_FS 0x00000200 /* First Descriptor */ +#define ADSTAT_Rx_LS 0x00000100 /* Last Descriptor */ +#define ADSTAT_Rx_TL 0x00000080 /* Frame Too Long */ +#define ADSTAT_Rx_CS 0x00000040 /* Collision Seen */ +#define ADSTAT_Rx_RT 0x00000020 /* Frame Type */ +#define ADSTAT_Rx_RW 0x00000010 /* Receive Watchdog */ +#define ADSTAT_Rx_RE 0x00000008 /* Report on MII Error */ +#define ADSTAT_Rx_DB 0x00000004 /* Dribbling Bit */ +#define ADSTAT_Rx_CE 0x00000002 /* CRC Error */ +#define ADSTAT_Rx_ZER 0x00000001 /* Zero (always 0) */ + +#define ADSTAT_Rx_LENGTH(x) (((x) & ADSTAT_Rx_FL) >> 16) + +/* + * Descriptor Status bits for Transmit Descriptor. + */ +#define ADSTAT_Tx_ES 0x00008000 /* Error Summary */ +#define ADSTAT_Tx_TO 0x00004000 /* Transmit Jabber Timeout */ +#define ADSTAT_Tx_LO 0x00000800 /* Loss of Carrier */ +#define ADSTAT_Tx_NC 0x00000400 /* No Carrier */ +#define ADSTAT_Tx_LC 0x00000200 /* Late Collision */ +#define ADSTAT_Tx_EC 0x00000100 /* Excessive Collisions */ +#define ADSTAT_Tx_HF 0x00000080 /* Heartbeat Fail */ +#define ADSTAT_Tx_CC 0x00000078 /* Collision Count */ +#define ADSTAT_Tx_ED 0x00000004 /* Excessive Deferral */ +#define ADSTAT_Tx_UF 0x00000002 /* Underflow Error */ +#define ADSTAT_Tx_DE 0x00000001 /* Deferred */ + +#define ADSTAT_Tx_COLLISIONS(x) (((x) & ADSTAT_Tx_CC) >> 3) + +/* + * Descriptor Control bits common to transmit and receive. + */ +#define ADCTL_SIZE1 0x000007ff /* Size of buffer 1 */ +#define ADCTL_SIZE1_SHIFT 0 + +#define ADCTL_SIZE2 0x003ff800 /* Size of buffer 2 */ +#define ADCTL_SIZE2_SHIFT 11 + +#define ADCTL_ER 0x02000000 /* End of Ring */ +#define ADCTL_CH 0x01000000 /* Second Address Chained */ + +/* + * Descriptor Control bits for Transmit Descriptor. + */ +#define ADCTL_Tx_IC 0x80000000 /* Interrupt on Completion */ +#define ADCTL_Tx_LS 0x40000000 /* Last Segment */ +#define ADCTL_Tx_FS 0x20000000 /* First Segment */ +#define ADCTL_Tx_AC 0x04000000 /* Add CRC Disable */ +#define ADCTL_Tx_DPD 0x00800000 /* Disabled Padding */ + +/* + * Control registers. + */ + +/* tese are registers only found on this part */ +#define CSR_MACCTL 0x0000 /* mac control */ +#define CSR_MACHI 0x0004 +#define CSR_MACLO 0x0008 +#define CSR_HTHI 0x000C /* multicast table high */ +#define CSR_HTLO 0x0010 /* multicast table low */ +#define CSR_MIIADDR 0x0014 /* mii address */ +#define CSR_MIIDATA 0x0018 /* mii data */ +#define CSR_FLOWC 0x001C /* flow control */ +#define CSR_VL1 0x0020 /* vlan 1 tag */ + +/* these are more or less normal Tulip registers */ +#define CSR_BUSMODE 0x1000 /* bus mode */ +#define CSR_TXPOLL 0x1004 /* tx poll demand */ +#define CSR_RXPOLL 0x1008 /* rx poll demand */ +#define CSR_RXLIST 0x100C /* rx base descriptor address */ +#define CSR_TXLIST 0x1010 /* tx base descriptor address */ +#define CSR_STATUS 0x1014 /* (interrupt) status */ +#define CSR_OPMODE 0x1018 /* operation mode */ +#define CSR_INTEN 0x101C /* interrupt enable */ +#define CSR_MISSED 0x1020 /* missed frame counter */ +#define CSR_HTBA 0x1050 /* host tx buffer address (ro) */ +#define CSR_HRBA 0x1054 /* host rx buffer address (ro) */ + +/* CSR_MACCTL - Mac Control */ +#define MACCTL_RE 0x00000004 /* rx enable */ +#define MACCTL_TE 0x00000008 /* tx enable */ +#define MACCTL_DC 0x00000020 /* deferral check */ +#define MACCTL_PSTR 0x00000100 /* automatic pad strip */ +#define MACCTL_DTRY 0x00000400 /* disable retry */ +#define MACCTL_DBF 0x00000800 /* disable broadcast frames */ +#define MACCTL_LCC 0x00001000 /* late collision control */ +#define MACCTL_HASH 0x00002000 /* hash filtering enable */ +#define MACCTL_HO 0x00008000 /* disable perfect filtering */ +#define MACCTL_PB 0x00010000 /* pass bad frames */ +#define MACCTL_IF 0x00020000 /* inverse filtering */ +#define MACCTL_PR 0x00040000 /* promiscuous mode */ +#define MACCTL_PM 0x00080000 /* pass all multicast */ +#define MACCTL_FDX 0x00100000 /* full duplex mode */ +#define MACCTL_LOOP 0x00600000 /* loopback mask */ +#define MACCTL_LOOP_INT 0x00200000 /* internal loopback */ +#define MACCTL_LOOP_EXT 0x00400000 /* external loopback */ +#define MACCTL_LOOP_NONE 0x00000000 +#define MACCTL_DRO 0x00800000 /* disable receive own */ +#define MACCTL_PS 0x08000000 /* port select, 0 = mii */ +#define MACCTL_HBD 0x10000000 /* heartbeat disable */ +#define MACCTL_BLE 0x40000000 /* mac big endian */ +#define MACCTL_RA 0x80000000 /* receive all packets */ + +/* CSR_MIIADDR - MII Addess */ +#define MIIADDR_BUSY 0x00000001 /* mii busy */ +#define MIIADDR_WRITE 0x00000002 /* mii write */ +#define MIIADDR_REG_MASK 0x000007C0 /* mii register */ +#define MIIADDR_REG_SHIFT 6 +#define MIIADDR_PHY_MASK 0x0000F800 /* mii phy */ +#define MIIADDR_PHY_SHIFT 11 + +#define MIIADDR_GETREG(x) (((x) & MIIADDR_REG) >> 6) +#define MIIADDR_PUTREG(x) (((x) << 6) & MIIADR_REG) +#define MIIADDR_GETPHY(x) (((x) & MIIADDR_PHY) >> 11) +#define MIIADDR_PUTPHY(x) (((x) << 6) & MIIADR_PHY) + +/* CSR_FLOWC - Flow Control */ +#define FLOWC_FCB 0x00000001 /* flow control busy */ +#define FLOWC_FCE 0x00000002 /* flow control enable */ +#define FLOWC_PCF 0x00000004 /* pass control frames */ +#define FLOWC_PT 0xffff0000 /* pause time */ + +/* CSR_BUSMODE - Bus Mode */ +#define BUSMODE_SWR 0x00000001 /* software reset */ +#define BUSMODE_BAR 0x00000002 /* bus arbitration */ +#define BUSMODE_DSL 0x0000007c /* descriptor skip length */ +#define BUSMODE_BLE 0x00000080 /* data buf endian */ + /* programmable burst length */ +#define BUSMODE_PBL_DEFAULT 0x00000000 /* default value */ +#define BUSMODE_PBL_1LW 0x00000100 /* 1 longword */ +#define BUSMODE_PBL_2LW 0x00000200 /* 2 longwords */ +#define BUSMODE_PBL_4LW 0x00000400 /* 4 longwords */ +#define BUSMODE_PBL_8LW 0x00000800 /* 8 longwords */ +#define BUSMODE_PBL_16LW 0x00001000 /* 16 longwords */ +#define BUSMODE_PBL_32LW 0x00002000 /* 32 longwords */ +#define BUSMODE_DBO 0x00100000 /* descriptor endian */ +#define BUSMODE_ALIGN_16B 0x01000000 /* force oddhw rx buf align */ + +/* CSR_TXPOLL - Transmit Poll Demand */ +#define TXPOLL_TPD 0x00000001 /* transmit poll demand */ + + +/* CSR_RXPOLL - Receive Poll Demand */ +#define RXPOLL_RPD 0x00000001 /* receive poll demand */ + +/* CSR_STATUS - Status */ +#define STATUS_TI 0x00000001 /* transmit interrupt */ +#define STATUS_TPS 0x00000002 /* transmit process stopped */ +#define STATUS_TU 0x00000004 /* transmit buffer unavail */ +#define STATUS_TJT 0x00000008 /* transmit jabber timeout */ +#define STATUS_UNF 0x00000020 /* transmit underflow */ +#define STATUS_RI 0x00000040 /* receive interrupt */ +#define STATUS_RU 0x00000080 /* receive buffer unavail */ +#define STATUS_RPS 0x00000100 /* receive process stopped */ +#define STATUS_ETI 0x00000400 /* early transmit interrupt */ +#define STATUS_SE 0x00002000 /* system error */ +#define STATUS_ER 0x00004000 /* early receive (21041) */ +#define STATUS_AIS 0x00008000 /* abnormal intr summary */ +#define STATUS_NIS 0x00010000 /* normal interrupt summary */ +#define STATUS_RS 0x000e0000 /* receive process state */ +#define STATUS_RS_STOPPED 0x00000000 /* Stopped */ +#define STATUS_RS_FETCH 0x00020000 /* Running - fetch receive + descriptor */ +#define STATUS_RS_CHECK 0x00040000 /* Running - check for end + of receive */ +#define STATUS_RS_WAIT 0x00060000 /* Running - wait for packet */ +#define STATUS_RS_SUSPENDED 0x00080000 /* Suspended */ +#define STATUS_RS_CLOSE 0x000a0000 /* Running - close receive + descriptor */ +#define STATUS_RS_FLUSH 0x000c0000 /* Running - flush current + frame from FIFO */ +#define STATUS_RS_QUEUE 0x000e0000 /* Running - queue current + frame from FIFO into + buffer */ +#define STATUS_TS 0x00700000 /* transmit process state */ +#define STATUS_TS_STOPPED 0x00000000 /* Stopped */ +#define STATUS_TS_FETCH 0x00100000 /* Running - fetch transmit + descriptor */ +#define STATUS_TS_WAIT 0x00200000 /* Running - wait for end + of transmission */ +#define STATUS_TS_READING 0x00300000 /* Running - read buffer from + memory and queue into + FIFO */ +#define STATUS_TS_SUSPENDED 0x00600000 /* Suspended */ +#define STATUS_TS_CLOSE 0x00700000 /* Running - close transmit + descriptor */ +#define STATUS_TX_ABORT 0x00800000 /* Transmit bus abort */ +#define STATUS_RX_ABORT 0x01000000 /* Transmit bus abort */ + +/* CSR_OPMODE - Operation Mode */ +#define OPMODE_SR 0x00000002 /* start receive */ +#define OPMODE_OSF 0x00000004 /* operate on second frame */ +#define OPMODE_ST 0x00002000 /* start transmitter */ +#define OPMODE_TR 0x0000c000 /* threshold control */ +#define OPMODE_TR_32 0x00000000 /* 32 words */ +#define OPMODE_TR_64 0x00004000 /* 64 words */ +#define OPMODE_TR_128 0x00008000 /* 128 words */ +#define OPMODE_TR_256 0x0000c000 /* 256 words */ +#define OPMODE_SF 0x00200000 /* store and forward mode */ + +/* CSR_INTEN - Interrupt Enable */ + /* See bits for CSR_STATUS -- Status */ + + +/* CSR_MISSED - Missed Frames */ +#define MISSED_MFC 0xffff0000 /* missed packet count */ +#define MISSED_FOC 0x0000ffff /* fifo overflow counter */ + +#define MISSED_GETMFC(x) ((x) & MISSED_MFC) +#define MISSED_GETFOC(x) (((x) & MISSED_FOC) >> 16) + +#endif /* __IF_AREREG_H__ */ Index: sys/mips/atheros/ar531x/uart_bus_ar5315.c =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/uart_bus_ar5315.c @@ -0,0 +1,89 @@ +/*- + * 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 + */ +#include "opt_uart.h" + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include "uart_if.h" + +static int uart_ar5315_probe(device_t dev); +extern struct uart_class uart_ar5315_uart_class; + +static device_method_t uart_ar5315_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uart_ar5315_probe), + DEVMETHOD(device_attach, uart_bus_attach), + DEVMETHOD(device_detach, uart_bus_detach), + DEVMETHOD_END +}; + +static driver_t uart_ar5315_driver = { + uart_driver_name, + uart_ar5315_methods, + sizeof(struct uart_softc), +}; + +extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; + +static int +uart_ar5315_probe(device_t dev) +{ + struct uart_softc *sc; + uint64_t freq; + + freq = ar531x_ahb_freq(); + + 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)); + sc->sc_sysdev->bas.regshft = 2; + sc->sc_sysdev->bas.bst = mips_bus_space_generic; + sc->sc_sysdev->bas.bsh = ar531x_uart_addr() + 3; + sc->sc_bas.regshft = 2; + sc->sc_bas.bst = mips_bus_space_generic; + sc->sc_bas.bsh = ar531x_uart_addr() + 3; + + return (uart_bus_probe(dev, 2, freq, 0, 0)); +} + +DRIVER_MODULE(uart, apb, uart_ar5315_driver, uart_devclass, 0, 0); Index: sys/mips/atheros/ar531x/uart_cpu_ar5315.c =================================================================== --- /dev/null +++ sys/mips/atheros/ar531x/uart_cpu_ar5315.c @@ -0,0 +1,76 @@ +/*- + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +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) +{ + uint64_t freq; + + freq = ar531x_ahb_freq(); + + di->ops = uart_getops(&uart_ns8250_class); + di->bas.chan = 0; + di->bas.bst = ar71xx_bus_space_reversed; + di->bas.regshft = 2; + di->bas.rclk = freq; + di->baudrate = 9600; // RedBoot default is 9600 + di->databits = 8; + di->stopbits = 1; + + di->parity = UART_PARITY_NONE; + + uart_bus_space_io = NULL; + uart_bus_space_mem = ar71xx_bus_space_reversed; + di->bas.bsh = ar531x_uart_addr(); + return (0); +} Index: sys/mips/conf/AR5312_BASE =================================================================== --- /dev/null +++ sys/mips/conf/AR5312_BASE @@ -0,0 +1,80 @@ +# +# AR5312 -- Kernel configuration file for FreeBSD/MIPS for Atheros 5312 systems +# +# This includes all the common drivers for the AR5312 boards +# +# $FreeBSD$ +# + +machine mips mips +ident AR5312_BASE +cpu CPU_MIPS4KC +makeoptions KERNLOADADDR=0x80050000 +options HZ=1000 + +makeoptions MODULES_OVERRIDE="" + +files "../atheros/ar531x/files.ar5315" + +options INTRNG +options AR531X_1ST_GENERATION + +# For now, hints are per-board. + +hints "AR5312_BASE.hints" + +makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols + +# For small memory footprints +options VM_KMEM_SIZE_SCALE=1 + +options DDB +options KDB + +options SCHED_4BSD #4BSD scheduler +options INET #InterNETworking +options INET6 # IPv6 + +# options NFSCL #Network Filesystem Client + +options PSEUDOFS #Pseudo-filesystem framework +options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions + +# options NFS_LEGACYRPC +# Debugging for use in -current +options INVARIANTS +options INVARIANT_SUPPORT +options WITNESS +options WITNESS_SKIPSPIN +options DEBUG_REDZONE +options DEBUG_MEMGUARD + +options FFS #Berkeley Fast Filesystem +# options SOFTUPDATES #Enable FFS soft updates support +# options UFS_ACL #Support for access control lists +# options UFS_DIRHASH #Improve performance on big directories +# options MSDOSFS # Read MSDOS filesystems; useful for USB/CF + +device mii +device are + +device cfi +options CFI_HARDWAREBYTESWAP +device geom_redboot + +device ar5315_wdog + +device uart +device uart_ar5315 + +device loop +device ether +device md +device bpf +device random + +options ARGE_DEBUG # Enable if_arge debugging for now + +# Enable GPIO +device gpio +device gpioled Index: sys/mips/conf/AR5312_BASE.hints =================================================================== --- /dev/null +++ sys/mips/conf/AR5312_BASE.hints @@ -0,0 +1,29 @@ +# $FreeBSD$ +hint.apb.0.at="nexus0" +hint.apb.0.irq=4 + +# uart0 +hint.uart.0.at="apb0" +# see atheros/uart_cpu_ar71xx.c why +3 +hint.uart.0.maddr=0x1C000003 +hint.uart.0.msize=0x20 +#hint.uart.0.irq=4 +#hint.uart.0.flags="0x30" + +# Watchdog +hint.ar5315_wdog.0.at="apb0" +hint.ar5315_wdog.0.irq=6 + +# Ethernet +hint.are.0.at="nexus0" +hint.are.0.maddr=0x18100000 +hint.are.0.msize=0x00100000 +hint.are.0.irq=1 + +hint.are.1.at="nexus0" +hint.are.1.maddr=0x18200000 +hint.are.1.msize=0x00100000 +hint.are.1.irq=2 + +# GEOM redboot FIS directory offset +#hint.redboot.0.fisoffset="0x007e0000" Index: sys/mips/conf/AR5315_BASE =================================================================== --- /dev/null +++ sys/mips/conf/AR5315_BASE @@ -0,0 +1,80 @@ +# +# AR5315 -- Kernel configuration file for FreeBSD/MIPS for Atheros 5315 systems +# +# This includes all the common drivers for the AR5315 boards +# +# $FreeBSD$ +# + +machine mips mips +ident AR5315_BASE +cpu CPU_MIPS4KC +makeoptions KERNLOADADDR=0x80050000 +options HZ=1000 + +makeoptions MODULES_OVERRIDE="" + +files "../atheros/ar531x/files.ar5315" + +options INTRNG + +# For now, hints are per-board. + +hints "AR5315_BASE.hints" + +makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols + +# For small memory footprints +options VM_KMEM_SIZE_SCALE=1 + +options DDB +options KDB + +options SCHED_4BSD #4BSD scheduler +options INET #InterNETworking +options INET6 # IPv6 + +# options NFSCL #Network Filesystem Client + +options PSEUDOFS #Pseudo-filesystem framework +options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions + +# options NFS_LEGACYRPC +# Debugging for use in -current +options INVARIANTS +options INVARIANT_SUPPORT +options WITNESS +options WITNESS_SKIPSPIN +options DEBUG_REDZONE +options DEBUG_MEMGUARD + +options FFS #Berkeley Fast Filesystem +# options SOFTUPDATES #Enable FFS soft updates support +# options UFS_ACL #Support for access control lists +# options UFS_DIRHASH #Improve performance on big directories +# options MSDOSFS # Read MSDOS filesystems; useful for USB/CF + +device mii +device are + +device ar5315_spi +device spibus +device mx25l +device geom_redboot + +device ar5315_wdog + +device uart +device uart_ar5315 + +device loop +device ether +device md +device bpf +device random + +options ARGE_DEBUG # Enable if_arge debugging for now + +# Enable GPIO +device gpio +device gpioled Index: sys/mips/conf/AR5315_BASE.hints =================================================================== --- /dev/null +++ sys/mips/conf/AR5315_BASE.hints @@ -0,0 +1,34 @@ +# $FreeBSD$ +hint.apb.0.at="nexus0" +hint.apb.0.irq=0 + +# uart0 +hint.uart.0.at="apb0" +hint.uart.0.maddr=0x11100003 +hint.uart.0.msize=0x20 +#hint.uart.0.irq=0 +#hint.uart.0.flags="0x30" + +# Watchdog +hint.ar5315_wdog.0.at="apb0" +hint.ar5315_wdog.0.irq=7 + +# SPI +hint.spi.0.at="nexus0" +hint.spi.0.maddr=0x11300000 +hint.spi.0.msize=0x0000000c +#hint.spi.0.irq=2 + +# Ethernet +hint.are.0.at="nexus0" +hint.are.0.maddr=0x10500000 +hint.are.0.msize=0x500000 +hint.are.0.irq=2 + +# Flash +hint.mx25l.0.at="spibus0" +hint.mx25l.0.cs=0 + +# GEOM redboot FIS directory offset +#hint.redboot.0.fisoffset="0x007e0000" +