diff options
Diffstat (limited to 'sys/pci')
61 files changed, 76006 insertions, 0 deletions
diff --git a/sys/pci/agp.c b/sys/pci/agp.c new file mode 100644 index 0000000..b16d1ee --- /dev/null +++ b/sys/pci/agp.c @@ -0,0 +1,801 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * 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$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/ioccom.h> +#include <sys/agpio.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/proc.h> + +#include <pci/pcivar.h> +#include <pci/pcireg.h> +#include <pci/agppriv.h> +#include <pci/agpvar.h> +#include <pci/agpreg.h> + +#include <vm/vm.h> +#include <vm/vm_object.h> +#include <vm/vm_page.h> +#include <vm/vm_pageout.h> +#include <vm/pmap.h> + +#include <machine/md_var.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +MODULE_VERSION(agp, 1); + +MALLOC_DEFINE(M_AGP, "agp", "AGP data structures"); + +#define CDEV_MAJOR 148 + /* agp_drv.c */ +static d_open_t agp_open; +static d_close_t agp_close; +static d_ioctl_t agp_ioctl; +static d_mmap_t agp_mmap; + +static struct cdevsw agp_cdevsw = { + /* open */ agp_open, + /* close */ agp_close, + /* read */ noread, + /* write */ nowrite, + /* ioctl */ agp_ioctl, + /* poll */ nopoll, + /* mmap */ agp_mmap, + /* strategy */ nostrategy, + /* name */ "agp", + /* maj */ CDEV_MAJOR, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ D_TTY, +}; + +static devclass_t agp_devclass; +#define KDEV2DEV(kdev) devclass_get_device(agp_devclass, minor(kdev)) + +/* Helper functions for implementing chipset mini drivers. */ + +void +agp_flush_cache() +{ +#ifdef __i386__ + wbinvd(); +#endif +} + +u_int8_t +agp_find_caps(device_t dev) +{ + u_int32_t status; + u_int8_t ptr, next; + + /* + * Check the CAP_LIST bit of the PCI status register first. + */ + status = pci_read_config(dev, PCIR_STATUS, 2); + if (!(status & 0x10)) + return 0; + + /* + * Traverse the capabilities list. + */ + for (ptr = pci_read_config(dev, AGP_CAPPTR, 1); + ptr != 0; + ptr = next) { + u_int32_t capid = pci_read_config(dev, ptr, 4); + next = AGP_CAPID_GET_NEXT_PTR(capid); + + /* + * If this capability entry ID is 2, then we are done. + */ + if (AGP_CAPID_GET_CAP_ID(capid) == 2) + return ptr; + } + + return 0; +} + +/* + * Find an AGP display device (if any). + */ +static device_t +agp_find_display(void) +{ + devclass_t pci = devclass_find("pci"); + device_t bus, dev = 0; + device_t *kids; + int busnum, numkids, i; + + for (busnum = 0; busnum < devclass_get_maxunit(pci); busnum++) { + bus = devclass_get_device(pci, busnum); + if (!bus) + continue; + device_get_children(bus, &kids, &numkids); + for (i = 0; i < numkids; i++) { + dev = kids[i]; + if (pci_get_class(dev) == PCIC_DISPLAY + && pci_get_subclass(dev) == PCIS_DISPLAY_VGA) + if (agp_find_caps(dev)) { + free(kids, M_TEMP); + return dev; + } + + } + free(kids, M_TEMP); + } + + return 0; +} + +struct agp_gatt * +agp_alloc_gatt(device_t dev) +{ + u_int32_t apsize = AGP_GET_APERTURE(dev); + u_int32_t entries = apsize >> AGP_PAGE_SHIFT; + struct agp_gatt *gatt; + + if (bootverbose) + device_printf(dev, + "allocating GATT for aperture of size %dM\n", + apsize / (1024*1024)); + + gatt = malloc(sizeof(struct agp_gatt), M_AGP, M_NOWAIT); + if (!gatt) + return 0; + + gatt->ag_entries = entries; + gatt->ag_virtual = contigmalloc(entries * sizeof(u_int32_t), M_AGP, 0, + 0, ~0, PAGE_SIZE, 0); + if (!gatt->ag_virtual) { + if (bootverbose) + device_printf(dev, "contiguous allocation failed\n"); + free(gatt, M_AGP); + return 0; + } + bzero(gatt->ag_virtual, entries * sizeof(u_int32_t)); + gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual); + agp_flush_cache(); + + return gatt; +} + +void +agp_free_gatt(struct agp_gatt *gatt) +{ + contigfree(gatt->ag_virtual, + gatt->ag_entries * sizeof(u_int32_t), M_AGP); + free(gatt, M_AGP); +} + +static int agp_max[][2] = { + {0, 0}, + {32, 4}, + {64, 28}, + {128, 96}, + {256, 204}, + {512, 440}, + {1024, 942}, + {2048, 1920}, + {4096, 3932} +}; +#define agp_max_size (sizeof(agp_max) / sizeof(agp_max[0])) + +int +agp_generic_attach(device_t dev) +{ + struct agp_softc *sc = device_get_softc(dev); + int rid, memsize, i; + + /* + * Find and map the aperture. + */ + rid = AGP_APBASE; + sc->as_aperture = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!sc->as_aperture) + return ENOMEM; + + /* + * Work out an upper bound for agp memory allocation. This + * uses a heurisitc table from the Linux driver. + */ + memsize = ptoa(Maxmem) >> 20; + for (i = 0; i < agp_max_size; i++) { + if (memsize <= agp_max[i][0]) + break; + } + if (i == agp_max_size) i = agp_max_size - 1; + sc->as_maxmem = agp_max[i][1] << 20U; + + /* + * The lock is used to prevent re-entry to + * agp_generic_bind_memory() since that function can sleep. + */ + lockinit(&sc->as_lock, PZERO|PCATCH, "agplk", 0, 0); + + /* + * Initialise stuff for the userland device. + */ + agp_devclass = devclass_find("agp"); + TAILQ_INIT(&sc->as_memory); + sc->as_nextid = 1; + + sc->as_devnode = make_dev(&agp_cdevsw, + device_get_unit(dev), + UID_ROOT, + GID_WHEEL, + 0600, + "agpgart"); + + return 0; +} + +int +agp_generic_detach(device_t dev) +{ + struct agp_softc *sc = device_get_softc(dev); + bus_release_resource(dev, SYS_RES_MEMORY, AGP_APBASE, sc->as_aperture); + lockmgr(&sc->as_lock, LK_DRAIN, 0, curthread); + lockdestroy(&sc->as_lock); + destroy_dev(sc->as_devnode); + agp_flush_cache(); + return 0; +} + +int +agp_generic_enable(device_t dev, u_int32_t mode) +{ + device_t mdev = agp_find_display(); + u_int32_t tstatus, mstatus; + u_int32_t command; + int rq, sba, fw, rate;; + + if (!mdev) { + AGP_DPF("can't find display\n"); + return ENXIO; + } + + tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); + mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4); + + /* Set RQ to the min of mode, tstatus and mstatus */ + rq = AGP_MODE_GET_RQ(mode); + if (AGP_MODE_GET_RQ(tstatus) < rq) + rq = AGP_MODE_GET_RQ(tstatus); + if (AGP_MODE_GET_RQ(mstatus) < rq) + rq = AGP_MODE_GET_RQ(mstatus); + + /* Set SBA if all three can deal with SBA */ + sba = (AGP_MODE_GET_SBA(tstatus) + & AGP_MODE_GET_SBA(mstatus) + & AGP_MODE_GET_SBA(mode)); + + /* Similar for FW */ + fw = (AGP_MODE_GET_FW(tstatus) + & AGP_MODE_GET_FW(mstatus) + & AGP_MODE_GET_FW(mode)); + + /* Figure out the max rate */ + rate = (AGP_MODE_GET_RATE(tstatus) + & AGP_MODE_GET_RATE(mstatus) + & AGP_MODE_GET_RATE(mode)); + if (rate & AGP_MODE_RATE_4x) + rate = AGP_MODE_RATE_4x; + else if (rate & AGP_MODE_RATE_2x) + rate = AGP_MODE_RATE_2x; + else + rate = AGP_MODE_RATE_1x; + + /* Construct the new mode word and tell the hardware */ + command = AGP_MODE_SET_RQ(0, rq); + command = AGP_MODE_SET_SBA(command, sba); + command = AGP_MODE_SET_FW(command, fw); + command = AGP_MODE_SET_RATE(command, rate); + command = AGP_MODE_SET_AGP(command, 1); + pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, command, 4); + pci_write_config(mdev, agp_find_caps(mdev) + AGP_COMMAND, command, 4); + + return 0; +} + +struct agp_memory * +agp_generic_alloc_memory(device_t dev, int type, vm_size_t size) +{ + struct agp_softc *sc = device_get_softc(dev); + struct agp_memory *mem; + + if ((size & (AGP_PAGE_SIZE - 1)) != 0) + return 0; + + if (sc->as_allocated + size > sc->as_maxmem) + return 0; + + if (type != 0) { + printf("agp_generic_alloc_memory: unsupported type %d\n", + type); + return 0; + } + + mem = malloc(sizeof *mem, M_AGP, M_WAITOK); + mem->am_id = sc->as_nextid++; + mem->am_size = size; + mem->am_type = 0; + mem->am_obj = vm_object_allocate(OBJT_DEFAULT, atop(round_page(size))); + mem->am_physical = 0; + mem->am_offset = 0; + mem->am_is_bound = 0; + TAILQ_INSERT_TAIL(&sc->as_memory, mem, am_link); + sc->as_allocated += size; + + return mem; +} + +int +agp_generic_free_memory(device_t dev, struct agp_memory *mem) +{ + struct agp_softc *sc = device_get_softc(dev); + + if (mem->am_is_bound) + return EBUSY; + + sc->as_allocated -= mem->am_size; + TAILQ_REMOVE(&sc->as_memory, mem, am_link); + vm_object_deallocate(mem->am_obj); + free(mem, M_AGP); + return 0; +} + +int +agp_generic_bind_memory(device_t dev, struct agp_memory *mem, + vm_offset_t offset) +{ + struct agp_softc *sc = device_get_softc(dev); + vm_offset_t i, j, k; + vm_page_t m; + int error; + + lockmgr(&sc->as_lock, LK_EXCLUSIVE, 0, curthread); + + if (mem->am_is_bound) { + device_printf(dev, "memory already bound\n"); + return EINVAL; + } + + if (offset < 0 + || (offset & (AGP_PAGE_SIZE - 1)) != 0 + || offset + mem->am_size > AGP_GET_APERTURE(dev)) { + device_printf(dev, "binding memory at bad offset %#x\n", + (int) offset); + return EINVAL; + } + + /* + * Bind the individual pages and flush the chipset's + * TLB. + * + * XXX Presumably, this needs to be the pci address on alpha + * (i.e. use alpha_XXX_dmamap()). I don't have access to any + * alpha AGP hardware to check. + */ + for (i = 0; i < mem->am_size; i += PAGE_SIZE) { + /* + * Find a page from the object and wire it + * down. This page will be mapped using one or more + * entries in the GATT (assuming that PAGE_SIZE >= + * AGP_PAGE_SIZE. If this is the first call to bind, + * the pages will be allocated and zeroed. + */ + m = vm_page_grab(mem->am_obj, OFF_TO_IDX(i), + VM_ALLOC_ZERO | VM_ALLOC_RETRY); + AGP_DPF("found page pa=%#x\n", VM_PAGE_TO_PHYS(m)); + vm_page_wire(m); + + /* + * Install entries in the GATT, making sure that if + * AGP_PAGE_SIZE < PAGE_SIZE and mem->am_size is not + * aligned to PAGE_SIZE, we don't modify too many GATT + * entries. + */ + for (j = 0; j < PAGE_SIZE && i + j < mem->am_size; + j += AGP_PAGE_SIZE) { + vm_offset_t pa = VM_PAGE_TO_PHYS(m) + j; + AGP_DPF("binding offset %#x to pa %#x\n", + offset + i + j, pa); + error = AGP_BIND_PAGE(dev, offset + i + j, pa); + if (error) { + /* + * Bail out. Reverse all the mappings + * and unwire the pages. + */ + vm_page_wakeup(m); + for (k = 0; k < i + j; k += AGP_PAGE_SIZE) + AGP_UNBIND_PAGE(dev, offset + k); + for (k = 0; k <= i; k += PAGE_SIZE) { + m = vm_page_lookup(mem->am_obj, + OFF_TO_IDX(k)); + vm_page_unwire(m, 0); + } + lockmgr(&sc->as_lock, LK_RELEASE, 0, curthread); + return error; + } + } + vm_page_wakeup(m); + } + + /* + * Flush the cpu cache since we are providing a new mapping + * for these pages. + */ + agp_flush_cache(); + + /* + * Make sure the chipset gets the new mappings. + */ + AGP_FLUSH_TLB(dev); + + mem->am_offset = offset; + mem->am_is_bound = 1; + + lockmgr(&sc->as_lock, LK_RELEASE, 0, curthread); + + return 0; +} + +int +agp_generic_unbind_memory(device_t dev, struct agp_memory *mem) +{ + struct agp_softc *sc = device_get_softc(dev); + vm_page_t m; + int i; + + lockmgr(&sc->as_lock, LK_EXCLUSIVE, 0, curthread); + + if (!mem->am_is_bound) { + device_printf(dev, "memory is not bound\n"); + return EINVAL; + } + + + /* + * Unbind the individual pages and flush the chipset's + * TLB. Unwire the pages so they can be swapped. + */ + for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) + AGP_UNBIND_PAGE(dev, mem->am_offset + i); + for (i = 0; i < mem->am_size; i += PAGE_SIZE) { + m = vm_page_lookup(mem->am_obj, atop(i)); + vm_page_unwire(m, 0); + } + + agp_flush_cache(); + AGP_FLUSH_TLB(dev); + + mem->am_offset = 0; + mem->am_is_bound = 0; + + lockmgr(&sc->as_lock, LK_RELEASE, 0, curthread); + + return 0; +} + +/* Helper functions for implementing user/kernel api */ + +static int +agp_acquire_helper(device_t dev, enum agp_acquire_state state) +{ + struct agp_softc *sc = device_get_softc(dev); + + if (sc->as_state != AGP_ACQUIRE_FREE) + return EBUSY; + sc->as_state = state; + + return 0; +} + +static int +agp_release_helper(device_t dev, enum agp_acquire_state state) +{ + struct agp_softc *sc = device_get_softc(dev); + + if (sc->as_state == AGP_ACQUIRE_FREE) + return 0; + + if (sc->as_state != state) + return EBUSY; + + sc->as_state = AGP_ACQUIRE_FREE; + return 0; +} + +static struct agp_memory * +agp_find_memory(device_t dev, int id) +{ + struct agp_softc *sc = device_get_softc(dev); + struct agp_memory *mem; + + AGP_DPF("searching for memory block %d\n", id); + TAILQ_FOREACH(mem, &sc->as_memory, am_link) { + AGP_DPF("considering memory block %d\n", mem->am_id); + if (mem->am_id == id) + return mem; + } + return 0; +} + +/* Implementation of the userland ioctl api */ + +static int +agp_info_user(device_t dev, agp_info *info) +{ + struct agp_softc *sc = device_get_softc(dev); + + bzero(info, sizeof *info); + info->bridge_id = pci_get_devid(dev); + info->agp_mode = + pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); + info->aper_base = rman_get_start(sc->as_aperture); + info->aper_size = AGP_GET_APERTURE(dev) >> 20; + info->pg_total = info->pg_system = sc->as_maxmem >> AGP_PAGE_SHIFT; + info->pg_used = sc->as_allocated >> AGP_PAGE_SHIFT; + + return 0; +} + +static int +agp_setup_user(device_t dev, agp_setup *setup) +{ + return AGP_ENABLE(dev, setup->agp_mode); +} + +static int +agp_allocate_user(device_t dev, agp_allocate *alloc) +{ + struct agp_memory *mem; + + mem = AGP_ALLOC_MEMORY(dev, + alloc->type, + alloc->pg_count << AGP_PAGE_SHIFT); + if (mem) { + alloc->key = mem->am_id; + alloc->physical = mem->am_physical; + return 0; + } else { + return ENOMEM; + } +} + +static int +agp_deallocate_user(device_t dev, int id) +{ + struct agp_memory *mem = agp_find_memory(dev, id);; + + if (mem) { + AGP_FREE_MEMORY(dev, mem); + return 0; + } else { + return ENOENT; + } +} + +static int +agp_bind_user(device_t dev, agp_bind *bind) +{ + struct agp_memory *mem = agp_find_memory(dev, bind->key); + + if (!mem) + return ENOENT; + + return AGP_BIND_MEMORY(dev, mem, bind->pg_start << AGP_PAGE_SHIFT); +} + +static int +agp_unbind_user(device_t dev, agp_unbind *unbind) +{ + struct agp_memory *mem = agp_find_memory(dev, unbind->key); + + if (!mem) + return ENOENT; + + return AGP_UNBIND_MEMORY(dev, mem); +} + +static int +agp_open(dev_t kdev, int oflags, int devtype, struct thread *td) +{ + device_t dev = KDEV2DEV(kdev); + struct agp_softc *sc = device_get_softc(dev); + + if (!sc->as_isopen) { + sc->as_isopen = 1; + device_busy(dev); + } + + return 0; +} + +static int +agp_close(dev_t kdev, int fflag, int devtype, struct thread *td) +{ + device_t dev = KDEV2DEV(kdev); + struct agp_softc *sc = device_get_softc(dev); + struct agp_memory *mem; + + /* + * Clear the GATT and force release on last close + */ + while ((mem = TAILQ_FIRST(&sc->as_memory)) != 0) { + if (mem->am_is_bound) + AGP_UNBIND_MEMORY(dev, mem); + AGP_FREE_MEMORY(dev, mem); + } + if (sc->as_state == AGP_ACQUIRE_USER) + agp_release_helper(dev, AGP_ACQUIRE_USER); + sc->as_isopen = 0; + device_unbusy(dev); + + return 0; +} + +static int +agp_ioctl(dev_t kdev, u_long cmd, caddr_t data, int fflag, struct thread *td) +{ + device_t dev = KDEV2DEV(kdev); + + switch (cmd) { + case AGPIOC_INFO: + return agp_info_user(dev, (agp_info *) data); + + case AGPIOC_ACQUIRE: + return agp_acquire_helper(dev, AGP_ACQUIRE_USER); + + case AGPIOC_RELEASE: + return agp_release_helper(dev, AGP_ACQUIRE_USER); + + case AGPIOC_SETUP: + return agp_setup_user(dev, (agp_setup *)data); + + case AGPIOC_ALLOCATE: + return agp_allocate_user(dev, (agp_allocate *)data); + + case AGPIOC_DEALLOCATE: + return agp_deallocate_user(dev, *(int *) data); + + case AGPIOC_BIND: + return agp_bind_user(dev, (agp_bind *)data); + + case AGPIOC_UNBIND: + return agp_unbind_user(dev, (agp_unbind *)data); + + } + + return EINVAL; +} + +static int +agp_mmap(dev_t kdev, vm_offset_t offset, int prot) +{ + device_t dev = KDEV2DEV(kdev); + struct agp_softc *sc = device_get_softc(dev); + + if (offset > AGP_GET_APERTURE(dev)) + return -1; + return atop(rman_get_start(sc->as_aperture) + offset); +} + +/* Implementation of the kernel api */ + +device_t +agp_find_device() +{ + if (!agp_devclass) + return 0; + return devclass_get_device(agp_devclass, 0); +} + +enum agp_acquire_state +agp_state(device_t dev) +{ + struct agp_softc *sc = device_get_softc(dev); + return sc->as_state; +} + +void +agp_get_info(device_t dev, struct agp_info *info) +{ + struct agp_softc *sc = device_get_softc(dev); + + info->ai_mode = + pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); + info->ai_aperture_base = rman_get_start(sc->as_aperture); + info->ai_aperture_size = rman_get_size(sc->as_aperture); + info->ai_aperture_va = (vm_offset_t) rman_get_virtual(sc->as_aperture); + info->ai_memory_allowed = sc->as_maxmem; + info->ai_memory_used = sc->as_allocated; +} + +int +agp_acquire(device_t dev) +{ + return agp_acquire_helper(dev, AGP_ACQUIRE_KERNEL); +} + +int +agp_release(device_t dev) +{ + return agp_release_helper(dev, AGP_ACQUIRE_KERNEL); +} + +int +agp_enable(device_t dev, u_int32_t mode) +{ + return AGP_ENABLE(dev, mode); +} + +void *agp_alloc_memory(device_t dev, int type, vm_size_t bytes) +{ + return (void *) AGP_ALLOC_MEMORY(dev, type, bytes); +} + +void agp_free_memory(device_t dev, void *handle) +{ + struct agp_memory *mem = (struct agp_memory *) handle; + AGP_FREE_MEMORY(dev, mem); +} + +int agp_bind_memory(device_t dev, void *handle, vm_offset_t offset) +{ + struct agp_memory *mem = (struct agp_memory *) handle; + return AGP_BIND_MEMORY(dev, mem, offset); +} + +int agp_unbind_memory(device_t dev, void *handle) +{ + struct agp_memory *mem = (struct agp_memory *) handle; + return AGP_UNBIND_MEMORY(dev, mem); +} + +void agp_memory_info(device_t dev, void *handle, struct + agp_memory_info *mi) +{ + struct agp_memory *mem = (struct agp_memory *) handle; + + mi->ami_size = mem->am_size; + mi->ami_physical = mem->am_physical; + mi->ami_offset = mem->am_offset; + mi->ami_is_bound = mem->am_is_bound; +} diff --git a/sys/pci/agp_ali.c b/sys/pci/agp_ali.c new file mode 100644 index 0000000..ac281e8 --- /dev/null +++ b/sys/pci/agp_ali.c @@ -0,0 +1,266 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * 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$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/proc.h> + +#include <pci/pcivar.h> +#include <pci/pcireg.h> +#include <pci/agppriv.h> +#include <pci/agpreg.h> + +#include <vm/vm.h> +#include <vm/vm_object.h> +#include <vm/pmap.h> + +struct agp_ali_softc { + struct agp_softc agp; + u_int32_t initial_aperture; /* aperture size at startup */ + struct agp_gatt *gatt; +}; + +static const char* +agp_ali_match(device_t dev) +{ + if (pci_get_class(dev) != PCIC_BRIDGE + || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) + return NULL; + + if (agp_find_caps(dev) == 0) + return NULL; + + switch (pci_get_devid(dev)) { + case 0x154110b9: + return ("Ali M1541 host to AGP bridge"); + }; + + if (pci_get_vendor(dev) == 0x10b9) + return ("Ali Generic host to PCI bridge"); + + return NULL; +} + +static int +agp_ali_probe(device_t dev) +{ + const char *desc; + + desc = agp_ali_match(dev); + if (desc) { + device_verbose(dev); + device_set_desc(dev, desc); + return 0; + } + + return ENXIO; +} + +static int +agp_ali_attach(device_t dev) +{ + struct agp_ali_softc *sc = device_get_softc(dev); + struct agp_gatt *gatt; + int error; + + error = agp_generic_attach(dev); + if (error) + return error; + + sc->initial_aperture = AGP_GET_APERTURE(dev); + + for (;;) { + gatt = agp_alloc_gatt(dev); + if (gatt) + break; + + /* + * Probably contigmalloc failure. Try reducing the + * aperture so that the gatt size reduces. + */ + if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) { + agp_generic_detach(dev); + return ENOMEM; + } + } + sc->gatt = gatt; + + /* Install the gatt. */ + pci_write_config(dev, AGP_ALI_ATTBASE, + (gatt->ag_physical + | (pci_read_config(dev, AGP_ALI_ATTBASE, 4) & 0xff)), + 4); + + /* Enable the TLB. */ + pci_write_config(dev, AGP_ALI_TLBCTRL, 0x10, 1); + + return 0; +} + +static int +agp_ali_detach(device_t dev) +{ + struct agp_ali_softc *sc = device_get_softc(dev); + int error; + + error = agp_generic_detach(dev); + if (error) + return error; + + /* Disable the TLB.. */ + pci_write_config(dev, AGP_ALI_TLBCTRL, 0x90, 1); + + /* Put the aperture back the way it started. */ + AGP_SET_APERTURE(dev, sc->initial_aperture); + pci_write_config(dev, AGP_ALI_ATTBASE, + pci_read_config(dev, AGP_ALI_ATTBASE, 4) & 0xff, + 4); + + agp_free_gatt(sc->gatt); + return 0; +} + +#define M 1024*1024 + +static u_int32_t agp_ali_table[] = { + 0, /* 0 - invalid */ + 1, /* 1 - invalid */ + 2, /* 2 - invalid */ + 4*M, /* 3 - invalid */ + 8*M, /* 4 - invalid */ + 0, /* 5 - invalid */ + 16*M, /* 6 - invalid */ + 32*M, /* 7 - invalid */ + 64*M, /* 8 - invalid */ + 128*M, /* 9 - invalid */ + 256*M, /* 10 - invalid */ +}; +#define agp_ali_table_size (sizeof(agp_ali_table) / sizeof(agp_ali_table[0])) + +static u_int32_t +agp_ali_get_aperture(device_t dev) +{ + /* + * The aperture size is derived from the low bits of attbase. + * I'm not sure this is correct.. + */ + int i = pci_read_config(dev, AGP_ALI_ATTBASE, 4) & 0xff; + if (i >= agp_ali_table_size) + return 0; + return agp_ali_table[i]; +} + +static int +agp_ali_set_aperture(device_t dev, u_int32_t aperture) +{ + int i; + + for (i = 0; i < agp_ali_table_size; i++) + if (agp_ali_table[i] == aperture) + break; + if (i == agp_ali_table_size) + return EINVAL; + + pci_write_config(dev, AGP_ALI_ATTBASE, + ((pci_read_config(dev, AGP_ALI_ATTBASE, 4) & ~0xff) + | i), 4); + return 0; +} + +static int +agp_ali_bind_page(device_t dev, int offset, vm_offset_t physical) +{ + struct agp_ali_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical; + return 0; +} + +static int +agp_ali_unbind_page(device_t dev, int offset) +{ + struct agp_ali_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; + return 0; +} + +static void +agp_ali_flush_tlb(device_t dev) +{ + pci_write_config(dev, AGP_ALI_TLBCTRL, 0x90, 1); + pci_write_config(dev, AGP_ALI_TLBCTRL, 0x10, 1); +} + +static device_method_t agp_ali_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, agp_ali_probe), + DEVMETHOD(device_attach, agp_ali_attach), + DEVMETHOD(device_detach, agp_ali_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* AGP interface */ + DEVMETHOD(agp_get_aperture, agp_ali_get_aperture), + DEVMETHOD(agp_set_aperture, agp_ali_set_aperture), + DEVMETHOD(agp_bind_page, agp_ali_bind_page), + DEVMETHOD(agp_unbind_page, agp_ali_unbind_page), + DEVMETHOD(agp_flush_tlb, agp_ali_flush_tlb), + DEVMETHOD(agp_enable, agp_generic_enable), + DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), + DEVMETHOD(agp_free_memory, agp_generic_free_memory), + DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), + DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), + + { 0, 0 } +}; + +static driver_t agp_ali_driver = { + "agp", + agp_ali_methods, + sizeof(struct agp_ali_softc), +}; + +static devclass_t agp_devclass; + +DRIVER_MODULE(agp_ali, pci, agp_ali_driver, agp_devclass, 0, 0); diff --git a/sys/pci/agp_amd.c b/sys/pci/agp_amd.c new file mode 100644 index 0000000..527765d --- /dev/null +++ b/sys/pci/agp_amd.c @@ -0,0 +1,419 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * 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$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/proc.h> + +#include <pci/pcivar.h> +#include <pci/pcireg.h> +#include <pci/agppriv.h> +#include <pci/agpreg.h> + +#include <vm/vm.h> +#include <vm/vm_object.h> +#include <vm/pmap.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +MALLOC_DECLARE(M_AGP); + +#define READ2(off) bus_space_read_2(sc->bst, sc->bsh, off) +#define READ4(off) bus_space_read_4(sc->bst, sc->bsh, off) +#define WRITE2(off,v) bus_space_write_2(sc->bst, sc->bsh, off, v) +#define WRITE4(off,v) bus_space_write_4(sc->bst, sc->bsh, off, v) + +struct agp_amd_gatt { + u_int32_t ag_entries; + u_int32_t *ag_virtual; /* virtual address of gatt */ + vm_offset_t ag_physical; + u_int32_t *ag_vdir; /* virtual address of page dir */ + vm_offset_t ag_pdir; /* physical address of page dir */ +}; + +struct agp_amd_softc { + struct agp_softc agp; + struct resource *regs; /* memory mapped control registers */ + bus_space_tag_t bst; /* bus_space tag */ + bus_space_handle_t bsh; /* bus_space handle */ + u_int32_t initial_aperture; /* aperture size at startup */ + struct agp_amd_gatt *gatt; +}; + +static struct agp_amd_gatt * +agp_amd_alloc_gatt(device_t dev) +{ + u_int32_t apsize = AGP_GET_APERTURE(dev); + u_int32_t entries = apsize >> AGP_PAGE_SHIFT; + struct agp_amd_gatt *gatt; + int i, npages, pdir_offset; + + if (bootverbose) + device_printf(dev, + "allocating GATT for aperture of size %dM\n", + apsize / (1024*1024)); + + gatt = malloc(sizeof(struct agp_amd_gatt), M_AGP, M_NOWAIT); + if (!gatt) + return 0; + + /* + * The AMD751 uses a page directory to map a non-contiguous + * gatt so we don't need to use contigmalloc. + * Malloc individual gatt pages and map them into the page + * directory. + */ + gatt->ag_entries = entries; + gatt->ag_virtual = malloc(entries * sizeof(u_int32_t), + M_AGP, M_NOWAIT); + if (!gatt->ag_virtual) { + if (bootverbose) + device_printf(dev, "allocation failed\n"); + free(gatt, M_AGP); + return 0; + } + bzero(gatt->ag_virtual, entries * sizeof(u_int32_t)); + + /* + * Allocate the page directory. + */ + gatt->ag_vdir = malloc(AGP_PAGE_SIZE, M_AGP, M_NOWAIT); + if (!gatt->ag_vdir) { + if (bootverbose) + device_printf(dev, + "failed to allocate page directory\n"); + free(gatt->ag_virtual, M_AGP); + free(gatt, M_AGP); + return 0; + } + bzero(gatt->ag_vdir, AGP_PAGE_SIZE); + + gatt->ag_pdir = vtophys((vm_offset_t) gatt->ag_vdir); + if(bootverbose) + device_printf(dev, "gatt -> ag_pdir %8x\n", + (vm_offset_t)gatt->ag_pdir); + /* + * Allocate the gatt pages + */ + gatt->ag_entries = entries; + if(bootverbose) + device_printf(dev, "allocating GATT for %d AGP page entries\n", + gatt->ag_entries); + + gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual); + + /* + * Map the pages of the GATT into the page directory. + * + * The GATT page addresses are mapped into the directory offset by + * an amount dependent on the base address of the aperture. This + * is and offset into the page directory, not an offset added to + * the addresses of the gatt pages. + */ + + pdir_offset = pci_read_config(dev, AGP_AMD751_APBASE, 4) >> 22; + + npages = ((entries * sizeof(u_int32_t) + AGP_PAGE_SIZE - 1) + >> AGP_PAGE_SHIFT); + + for (i = 0; i < npages; i++) { + vm_offset_t va; + vm_offset_t pa; + + va = ((vm_offset_t) gatt->ag_virtual) + i * AGP_PAGE_SIZE; + pa = vtophys(va); + gatt->ag_vdir[i + pdir_offset] = pa | 1; + } + + /* + * Make sure the chipset can see everything. + */ + agp_flush_cache(); + + return gatt; +} + +static void +agp_amd_free_gatt(struct agp_amd_gatt *gatt) +{ + free(gatt->ag_virtual, M_AGP); + free(gatt->ag_vdir, M_AGP); + free(gatt, M_AGP); +} + +static const char* +agp_amd_match(device_t dev) +{ + if (pci_get_class(dev) != PCIC_BRIDGE + || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) + return NULL; + + if (agp_find_caps(dev) == 0) + return NULL; + + switch (pci_get_devid(dev)) { + + case 0x700e1022: + return ("AMD 761 host to AGP bridge"); + + case 0x70061022: + return ("AMD 751 host to AGP bridge"); + + case 0x700c1022: + return ("AMD 762 host to AGP bridge"); + + }; + + return NULL; +} + +static int +agp_amd_probe(device_t dev) +{ + const char *desc; + + desc = agp_amd_match(dev); + if (desc) { + device_verbose(dev); + device_set_desc(dev, desc); + return 0; + } + + return ENXIO; +} + +static int +agp_amd_attach(device_t dev) +{ + struct agp_amd_softc *sc = device_get_softc(dev); + struct agp_amd_gatt *gatt; + int error, rid; + + error = agp_generic_attach(dev); + if (error) + return error; + + rid = AGP_AMD751_REGISTERS; + sc->regs = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!sc->regs) { + agp_generic_detach(dev); + return ENOMEM; + } + + sc->bst = rman_get_bustag(sc->regs); + sc->bsh = rman_get_bushandle(sc->regs); + + sc->initial_aperture = AGP_GET_APERTURE(dev); + + for (;;) { + gatt = agp_amd_alloc_gatt(dev); + if (gatt) + break; + + /* + * Probably contigmalloc failure. Try reducing the + * aperture so that the gatt size reduces. + */ + if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) + return ENOMEM; + } + sc->gatt = gatt; + + /* Install the gatt. */ + WRITE4(AGP_AMD751_ATTBASE, gatt->ag_pdir); + + /* Enable synchronisation between host and agp. */ + pci_write_config(dev, + AGP_AMD751_MODECTRL, + AGP_AMD751_MODECTRL_SYNEN, 1); + + /* Set indexing mode for two-level and enable page dir cache */ + pci_write_config(dev, + AGP_AMD751_MODECTRL2, + AGP_AMD751_MODECTRL2_GPDCE, 1); + + /* Enable the TLB and flush */ + WRITE2(AGP_AMD751_STATUS, + READ2(AGP_AMD751_STATUS) | AGP_AMD751_STATUS_GCE); + AGP_FLUSH_TLB(dev); + + return 0; +} + +static int +agp_amd_detach(device_t dev) +{ + struct agp_amd_softc *sc = device_get_softc(dev); + int error; + + error = agp_generic_detach(dev); + if (error) + return error; + + /* Disable the TLB.. */ + WRITE2(AGP_AMD751_STATUS, + READ2(AGP_AMD751_STATUS) & ~AGP_AMD751_STATUS_GCE); + + /* Disable host-agp sync */ + pci_write_config(dev, AGP_AMD751_MODECTRL, 0x00, 1); + + /* Clear the GATT base */ + WRITE4(AGP_AMD751_ATTBASE, 0); + + /* Put the aperture back the way it started. */ + AGP_SET_APERTURE(dev, sc->initial_aperture); + + agp_amd_free_gatt(sc->gatt); + + bus_release_resource(dev, SYS_RES_MEMORY, + AGP_AMD751_REGISTERS, sc->regs); + + return 0; +} + +static u_int32_t +agp_amd_get_aperture(device_t dev) +{ + int vas; + + /* + * The aperture size is equal to 32M<<vas. + */ + vas = (pci_read_config(dev, AGP_AMD751_APCTRL, 1) & 0x06) >> 1; + return (32*1024*1024) << vas; +} + +static int +agp_amd_set_aperture(device_t dev, u_int32_t aperture) +{ + int vas; + + /* + * Check for a power of two and make sure its within the + * programmable range. + */ + if (aperture & (aperture - 1) + || aperture < 32*1024*1024 + || aperture > 2U*1024*1024*1024) + return EINVAL; + + vas = ffs(aperture / 32*1024*1024) - 1; + + /* + * While the size register is bits 1-3 of APCTRL, bit 0 must be + * set for the size value to be 'valid' + */ + pci_write_config(dev, AGP_AMD751_APCTRL, + (((pci_read_config(dev, AGP_AMD751_APCTRL, 1) & ~0x06) + | ((vas << 1) | 1))), 1); + + return 0; +} + +static int +agp_amd_bind_page(device_t dev, int offset, vm_offset_t physical) +{ + struct agp_amd_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 1; + + /* invalidate the cache */ + AGP_FLUSH_TLB(dev); + return 0; +} + +static int +agp_amd_unbind_page(device_t dev, int offset) +{ + struct agp_amd_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; + return 0; +} + +static void +agp_amd_flush_tlb(device_t dev) +{ + struct agp_amd_softc *sc = device_get_softc(dev); + + /* Set the cache invalidate bit and wait for the chipset to clear */ + WRITE4(AGP_AMD751_TLBCTRL, 1); + do { + DELAY(1); + } while (READ4(AGP_AMD751_TLBCTRL)); +} + +static device_method_t agp_amd_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, agp_amd_probe), + DEVMETHOD(device_attach, agp_amd_attach), + DEVMETHOD(device_detach, agp_amd_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* AGP interface */ + DEVMETHOD(agp_get_aperture, agp_amd_get_aperture), + DEVMETHOD(agp_set_aperture, agp_amd_set_aperture), + DEVMETHOD(agp_bind_page, agp_amd_bind_page), + DEVMETHOD(agp_unbind_page, agp_amd_unbind_page), + DEVMETHOD(agp_flush_tlb, agp_amd_flush_tlb), + DEVMETHOD(agp_enable, agp_generic_enable), + DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), + DEVMETHOD(agp_free_memory, agp_generic_free_memory), + DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), + DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), + + { 0, 0 } +}; + +static driver_t agp_amd_driver = { + "agp", + agp_amd_methods, + sizeof(struct agp_amd_softc), +}; + +static devclass_t agp_devclass; + +DRIVER_MODULE(agp_amd, pci, agp_amd_driver, agp_devclass, 0, 0); diff --git a/sys/pci/agp_i810.c b/sys/pci/agp_i810.c new file mode 100644 index 0000000..ed5f6d2 --- /dev/null +++ b/sys/pci/agp_i810.c @@ -0,0 +1,478 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * Copyright (c) 2000 Ruslan Ermilov + * 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$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/proc.h> + +#include <pci/pcivar.h> +#include <pci/pcireg.h> +#include <pci/agppriv.h> +#include <pci/agpreg.h> + +#include <vm/vm.h> +#include <vm/vm_object.h> +#include <vm/vm_page.h> +#include <vm/vm_pageout.h> +#include <vm/pmap.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +MALLOC_DECLARE(M_AGP); + +#define READ1(off) bus_space_read_1(sc->bst, sc->bsh, off) +#define WRITE4(off,v) bus_space_write_4(sc->bst, sc->bsh, off, v) + +struct agp_i810_softc { + struct agp_softc agp; + u_int32_t initial_aperture; /* aperture size at startup */ + struct agp_gatt *gatt; + u_int32_t dcache_size; + device_t bdev; /* bridge device */ + struct resource *regs; /* memory mapped GC registers */ + bus_space_tag_t bst; /* bus_space tag */ + bus_space_handle_t bsh; /* bus_space handle */ +}; + +static const char* +agp_i810_match(device_t dev) +{ + if (pci_get_class(dev) != PCIC_DISPLAY + || pci_get_subclass(dev) != PCIS_DISPLAY_VGA) + return NULL; + + switch (pci_get_devid(dev)) { + case 0x71218086: + return ("Intel 82810 (i810 GMCH) SVGA controller"); + + case 0x71238086: + return ("Intel 82810-DC100 (i810-DC100 GMCH) SVGA controller"); + + case 0x71258086: + return ("Intel 82810E (i810E GMCH) SVGA controller"); + + case 0x11328086: + return ("Intel 82815 (i815 GMCH) SVGA controller"); + }; + + return NULL; +} + +/* + * Find bridge device. + */ +static device_t +agp_i810_find_bridge(device_t dev) +{ + device_t *children, child; + int nchildren, i; + u_int32_t devid; + + /* + * Calculate bridge device's ID. + */ + devid = pci_get_devid(dev); + switch (devid) { + case 0x71218086: + case 0x71238086: + case 0x71258086: + devid -= 0x10000; + break; + + case 0x11328086: + devid = 0x11308086; + break; + }; + if (device_get_children(device_get_parent(dev), &children, &nchildren)) + return 0; + + for (i = 0; i < nchildren; i++) { + child = children[i]; + + if (pci_get_devid(child) == devid) { + free(children, M_TEMP); + return child; + } + } + free(children, M_TEMP); + return 0; +} + +static int +agp_i810_probe(device_t dev) +{ + const char *desc; + + desc = agp_i810_match(dev); + if (desc) { + device_t bdev; + u_int8_t smram; + + bdev = agp_i810_find_bridge(dev); + if (!bdev) { + if (bootverbose) + printf("I810: can't find bridge device\n"); + return ENXIO; + } + + smram = pci_read_config(bdev, AGP_I810_SMRAM, 1); + if ((smram & AGP_I810_SMRAM_GMS) + == AGP_I810_SMRAM_GMS_DISABLED) { + if (bootverbose) + printf("I810: disabled, not probing\n"); + return ENXIO; + } + + device_verbose(dev); + device_set_desc(dev, desc); + return 0; + } + + return ENXIO; +} + +static int +agp_i810_attach(device_t dev) +{ + struct agp_i810_softc *sc = device_get_softc(dev); + struct agp_gatt *gatt; + int error, rid; + + sc->bdev = agp_i810_find_bridge(dev); + if (!sc->bdev) + return ENOENT; + + error = agp_generic_attach(dev); + if (error) + return error; + + rid = AGP_I810_MMADR; + sc->regs = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!sc->regs) { + agp_generic_detach(dev); + return ENOMEM; + } + sc->bst = rman_get_bustag(sc->regs); + sc->bsh = rman_get_bushandle(sc->regs); + + sc->initial_aperture = AGP_GET_APERTURE(dev); + + if (READ1(AGP_I810_DRT) & AGP_I810_DRT_POPULATED) + sc->dcache_size = 4 * 1024 * 1024; + else + sc->dcache_size = 0; + + for (;;) { + gatt = agp_alloc_gatt(dev); + if (gatt) + break; + + /* + * Probably contigmalloc failure. Try reducing the + * aperture so that the gatt size reduces. + */ + if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) { + agp_generic_detach(dev); + return ENOMEM; + } + } + sc->gatt = gatt; + + /* Install the GATT. */ + WRITE4(AGP_I810_PGTBL_CTL, gatt->ag_physical | 1); + + /* + * Make sure the chipset can see everything. + */ + agp_flush_cache(); + + return 0; +} + +static int +agp_i810_detach(device_t dev) +{ + struct agp_i810_softc *sc = device_get_softc(dev); + int error; + + error = agp_generic_detach(dev); + if (error) + return error; + + /* Clear the GATT base. */ + WRITE4(AGP_I810_PGTBL_CTL, 0); + + /* Put the aperture back the way it started. */ + AGP_SET_APERTURE(dev, sc->initial_aperture); + + agp_free_gatt(sc->gatt); + + bus_release_resource(dev, SYS_RES_MEMORY, + AGP_I810_MMADR, sc->regs); + + return 0; +} + +static u_int32_t +agp_i810_get_aperture(device_t dev) +{ + struct agp_i810_softc *sc = device_get_softc(dev); + u_int16_t miscc; + + miscc = pci_read_config(sc->bdev, AGP_I810_MISCC, 2); + if ((miscc & AGP_I810_MISCC_WINSIZE) == AGP_I810_MISCC_WINSIZE_32) + return 32 * 1024 * 1024; + else + return 64 * 1024 * 1024; +} + +static int +agp_i810_set_aperture(device_t dev, u_int32_t aperture) +{ + struct agp_i810_softc *sc = device_get_softc(dev); + u_int16_t miscc; + + /* + * Double check for sanity. + */ + if (aperture != 32 * 1024 * 1024 && aperture != 64 * 1024 * 1024) { + device_printf(dev, "bad aperture size %d\n", aperture); + return EINVAL; + } + + miscc = pci_read_config(sc->bdev, AGP_I810_MISCC, 2); + miscc &= ~AGP_I810_MISCC_WINSIZE; + if (aperture == 32 * 1024 * 1024) + miscc |= AGP_I810_MISCC_WINSIZE_32; + else + miscc |= AGP_I810_MISCC_WINSIZE_64; + + pci_write_config(sc->bdev, AGP_I810_MISCC, miscc, 2); + + return 0; +} + +static int +agp_i810_bind_page(device_t dev, int offset, vm_offset_t physical) +{ + struct agp_i810_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + WRITE4(AGP_I810_GTT + (offset >> AGP_PAGE_SHIFT) * 4, physical | 1); + return 0; +} + +static int +agp_i810_unbind_page(device_t dev, int offset) +{ + struct agp_i810_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + WRITE4(AGP_I810_GTT + (offset >> AGP_PAGE_SHIFT) * 4, 0); + return 0; +} + +/* + * Writing via memory mapped registers already flushes all TLBs. + */ +static void +agp_i810_flush_tlb(device_t dev) +{ +} + +static int +agp_i810_enable(device_t dev, u_int32_t mode) +{ + + return 0; +} + +static struct agp_memory * +agp_i810_alloc_memory(device_t dev, int type, vm_size_t size) +{ + struct agp_i810_softc *sc = device_get_softc(dev); + struct agp_memory *mem; + + if ((size & (AGP_PAGE_SIZE - 1)) != 0) + return 0; + + if (sc->agp.as_allocated + size > sc->agp.as_maxmem) + return 0; + + if (type == 1) { + /* + * Mapping local DRAM into GATT. + */ + if (size != sc->dcache_size) + return 0; + } else if (type == 2) { + /* + * Bogus mapping of a single page for the hardware cursor. + */ + if (size != AGP_PAGE_SIZE) + return 0; + } + + mem = malloc(sizeof *mem, M_AGP, M_WAITOK); + mem->am_id = sc->agp.as_nextid++; + mem->am_size = size; + mem->am_type = type; + if (type != 1) + mem->am_obj = vm_object_allocate(OBJT_DEFAULT, + atop(round_page(size))); + else + mem->am_obj = 0; + + if (type == 2) { + /* + * Allocate and wire down the page now so that we can + * get its physical address. + */ + vm_page_t m; + m = vm_page_grab(mem->am_obj, 0, VM_ALLOC_ZERO|VM_ALLOC_RETRY); + vm_page_wire(m); + mem->am_physical = VM_PAGE_TO_PHYS(m); + vm_page_wakeup(m); + } else { + mem->am_physical = 0; + } + + mem->am_offset = 0; + mem->am_is_bound = 0; + TAILQ_INSERT_TAIL(&sc->agp.as_memory, mem, am_link); + sc->agp.as_allocated += size; + + return mem; +} + +static int +agp_i810_free_memory(device_t dev, struct agp_memory *mem) +{ + struct agp_i810_softc *sc = device_get_softc(dev); + + if (mem->am_is_bound) + return EBUSY; + + if (mem->am_type == 2) { + /* + * Unwire the page which we wired in alloc_memory. + */ + vm_page_t m = vm_page_lookup(mem->am_obj, 0); + vm_page_unwire(m, 0); + } + + sc->agp.as_allocated -= mem->am_size; + TAILQ_REMOVE(&sc->agp.as_memory, mem, am_link); + if (mem->am_obj) + vm_object_deallocate(mem->am_obj); + free(mem, M_AGP); + return 0; +} + +static int +agp_i810_bind_memory(device_t dev, struct agp_memory *mem, + vm_offset_t offset) +{ + struct agp_i810_softc *sc = device_get_softc(dev); + vm_offset_t i; + + if (mem->am_type != 1) + return agp_generic_bind_memory(dev, mem, offset); + + for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) { + WRITE4(AGP_I810_GTT + (offset >> AGP_PAGE_SHIFT) * 4, + i | 3); + } + + return 0; +} + +static int +agp_i810_unbind_memory(device_t dev, struct agp_memory *mem) +{ + struct agp_i810_softc *sc = device_get_softc(dev); + vm_offset_t i; + + if (mem->am_type != 1) + return agp_generic_unbind_memory(dev, mem); + + for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) + WRITE4(AGP_I810_GTT + (i >> AGP_PAGE_SHIFT) * 4, 0); + + return 0; +} + +static device_method_t agp_i810_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, agp_i810_probe), + DEVMETHOD(device_attach, agp_i810_attach), + DEVMETHOD(device_detach, agp_i810_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* AGP interface */ + DEVMETHOD(agp_get_aperture, agp_i810_get_aperture), + DEVMETHOD(agp_set_aperture, agp_i810_set_aperture), + DEVMETHOD(agp_bind_page, agp_i810_bind_page), + DEVMETHOD(agp_unbind_page, agp_i810_unbind_page), + DEVMETHOD(agp_flush_tlb, agp_i810_flush_tlb), + DEVMETHOD(agp_enable, agp_i810_enable), + DEVMETHOD(agp_alloc_memory, agp_i810_alloc_memory), + DEVMETHOD(agp_free_memory, agp_i810_free_memory), + DEVMETHOD(agp_bind_memory, agp_i810_bind_memory), + DEVMETHOD(agp_unbind_memory, agp_i810_unbind_memory), + + { 0, 0 } +}; + +static driver_t agp_i810_driver = { + "agp", + agp_i810_methods, + sizeof(struct agp_i810_softc), +}; + +static devclass_t agp_devclass; + +DRIVER_MODULE(agp_i810, pci, agp_i810_driver, agp_devclass, 0, 0); diff --git a/sys/pci/agp_if.m b/sys/pci/agp_if.m new file mode 100644 index 0000000..06f64d3 --- /dev/null +++ b/sys/pci/agp_if.m @@ -0,0 +1,134 @@ +# +# Copyright (c) 2000 Doug Rabson +# 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$ +# + +#include <sys/bus.h> + +# +# The AGP interface is used internally to the agp driver to isolate the +# differences between various AGP chipsets into chipset mini drivers. It +# should not be used outside the AGP driver. The kernel api for accessing +# AGP functionality is described in <pci/agpvar,h> +# +INTERFACE agp; + +# +# Return the current aperture size. +# +METHOD u_int32_t get_aperture { + device_t dev; +}; + +# +# Set the size of the aperture. Return EINVAL on error or 0 on success. +# +METHOD int set_aperture { + device_t dev; + u_int32_t aperture; +}; + +# +# Bind a single page in the AGP aperture to a given physical address. +# The offset is a byte offset within the aperture which must be +# aligned to an AGP page boundary. +# +METHOD int bind_page { + device_t dev; + vm_offset_t offset; + vm_offset_t physical; +}; + +# +# Unbind a single page in the AGP aperture. +# +METHOD int unbind_page { + device_t dev; + vm_offset_t offset; +}; + +# +# Flush the GATT TLB. This is used after a call to bind_page to +# ensure that any mappings cached in the chipset are discarded. +# +METHOD void flush_tlb { + device_t dev; +}; + +# +# Enable the agp hardware with the relavent mode. The mode bits are +# defined in <pci/agpreg.h> +# +METHOD int enable { + device_t dev; + u_int32_t mode; +}; + +# +# Allocate memory of a given type. The type is a chipset-specific +# code which is used by certain integrated agp graphics chips +# (basically just the i810 for now) to access special features of +# the chipset. An opaque handle representing the memory region is +# returned and can be used as an argument to free_memory, bind_memory +# and unbind_memory. +# +# The size is specified in bytes but must be a multiple of the AGP +# page size. +# +METHOD struct agp_memory * alloc_memory { + device_t dev; + int type; + vm_size_t size; +}; + +# +# Free a memory region previously allocated with alloc_memory. Return +# EBUSY if the memory is bound. +# +METHOD int free_memory { + device_t dev; + struct agp_memory *mem; +}; + +# +# Bind a memory region to a specific byte offset within the chipset's +# AGP aperture. This effectively defines a range of contiguous +# physical address which alias the (possibly uncontiguous) pages in +# the memory region. +# +METHOD int bind_memory { + device_t dev; + struct agp_memory *mem; + vm_offset_t offset; +}; + +# +# Unbind a memory region bound with bind_memory. +# +METHOD int unbind_memory { + device_t dev; + struct agp_memory *handle; +}; diff --git a/sys/pci/agp_intel.c b/sys/pci/agp_intel.c new file mode 100644 index 0000000..165409d --- /dev/null +++ b/sys/pci/agp_intel.c @@ -0,0 +1,361 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * 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$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/proc.h> + +#include <pci/pcivar.h> +#include <pci/pcireg.h> +#include <pci/agppriv.h> +#include <pci/agpreg.h> + +#include <vm/vm.h> +#include <vm/vm_object.h> +#include <vm/pmap.h> + +struct agp_intel_softc { + struct agp_softc agp; + u_int32_t initial_aperture; /* aperture size at startup */ + struct agp_gatt *gatt; +}; + +static const char* +agp_intel_match(device_t dev) +{ + if (pci_get_class(dev) != PCIC_BRIDGE + || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) + return NULL; + + if (agp_find_caps(dev) == 0) + return NULL; + + switch (pci_get_devid(dev)) { + /* Intel -- vendor 0x8086 */ + case 0x71808086: + return ("Intel 82443LX (440 LX) host to PCI bridge"); + + case 0x71908086: + return ("Intel 82443BX (440 BX) host to PCI bridge"); + + case 0x71a08086: + return ("Intel 82443GX host to PCI bridge"); + + case 0x71a18086: + return ("Intel 82443GX host to AGP bridge"); + + case 0x11308086: + return ("Intel 82815 (i815 GMCH) host to PCI bridge"); + + case 0x25008086: + return ("Intel 82820 host to AGP bridge"); + + case 0x35758086: + return ("Intel 82830 host to AGP bridge"); + + case 0x1a218086: + return ("Intel 82840 host to AGP bridge"); + + case 0x1a308086: + return ("Intel 82845 host to AGP bridge"); + + case 0x25308086: + return ("Intel 82850 host to AGP bridge"); + + case 0x25318086: + return ("Intel 82860 host to AGP bridge"); + }; + + if (pci_get_vendor(dev) == 0x8086) + return ("Intel Generic host to PCI bridge"); + + return NULL; +} + +static int +agp_intel_probe(device_t dev) +{ + const char *desc; + + desc = agp_intel_match(dev); + if (desc) { + device_verbose(dev); + device_set_desc(dev, desc); + return 0; + } + + return ENXIO; +} + +static int +agp_intel_attach(device_t dev) +{ + struct agp_intel_softc *sc = device_get_softc(dev); + struct agp_gatt *gatt; + u_int32_t type = pci_get_devid(dev); + int error; + + error = agp_generic_attach(dev); + if (error) + return error; + + sc->initial_aperture = AGP_GET_APERTURE(dev); + + for (;;) { + gatt = agp_alloc_gatt(dev); + if (gatt) + break; + + /* + * Probably contigmalloc failure. Try reducing the + * aperture so that the gatt size reduces. + */ + if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) { + agp_generic_detach(dev); + return ENOMEM; + } + } + sc->gatt = gatt; + + /* Install the gatt. */ + pci_write_config(dev, AGP_INTEL_ATTBASE, gatt->ag_physical, 4); + + /* Enable things, clear errors etc. */ + switch (type) { + case 0x1a218086: /* i840 */ + case 0x25308086: /* i850 */ + case 0x25318086: /* i860 */ + pci_write_config(dev, AGP_INTEL_AGPCTRL, 0x0000, 4); + pci_write_config(dev, AGP_INTEL_MCHCFG, + (pci_read_config(dev, AGP_INTEL_MCHCFG, 2) + | (1 << 9)), 2); + break; + + case 0x25008086: /* i820 */ + pci_write_config(dev, AGP_INTEL_AGPCTRL, 0x0000, 4); + pci_write_config(dev, AGP_INTEL_I820_RDCR, + (pci_read_config(dev, AGP_INTEL_I820_RDCR, 1) + | (1 << 1)), 1); + break; + + case 0x1a308086: /* i845 */ + pci_write_config(dev, AGP_INTEL_AGPCTRL, 0x0000, 4); + pci_write_config(dev, AGP_INTEL_I845_MCHCFG, + (pci_read_config(dev, AGP_INTEL_I845_MCHCFG, 1) + | (1 << 1)), 1); + break; + + default: /* Intel Generic (maybe) */ + pci_write_config(dev, AGP_INTEL_AGPCTRL, 0x2280, 4); + pci_write_config(dev, AGP_INTEL_NBXCFG, + (pci_read_config(dev, AGP_INTEL_NBXCFG, 4) + & ~(1 << 10)) | (1 << 9), 4); + } + + switch (type) { + case 0x1a218086: /* i840 */ + pci_write_config(dev, AGP_INTEL_I8XX_ERRSTS, 0xc000, 2); + break; + + case 0x25008086: /* i820 */ + case 0x1a308086: /* i845 */ + case 0x25308086: /* i850 */ + case 0x25318086: /* i860 */ + pci_write_config(dev, AGP_INTEL_I8XX_ERRSTS, 0x001c, 2); + break; + + default: /* Intel Generic (maybe) */ + pci_write_config(dev, AGP_INTEL_ERRSTS + 1, 7, 1); + } + + return 0; +} + +static int +agp_intel_detach(device_t dev) +{ + struct agp_intel_softc *sc = device_get_softc(dev); + u_int32_t type = pci_get_devid(dev); + int error; + + error = agp_generic_detach(dev); + if (error) + return error; + + switch (type) { + case 0x1a218086: /* i840 */ + case 0x25308086: /* i850 */ + case 0x25318086: /* i860 */ + printf("%s: set MCHCFG to %x\n", __func__, (unsigned) + (pci_read_config(dev, AGP_INTEL_MCHCFG, 2) + & ~(1 << 9))); + pci_write_config(dev, AGP_INTEL_MCHCFG, + (pci_read_config(dev, AGP_INTEL_MCHCFG, 2) + & ~(1 << 9)), 2); + + case 0x25008086: /* i820 */ + printf("%s: set RDCR to %x\n", __func__, (unsigned) + (pci_read_config(dev, AGP_INTEL_I820_RDCR, 1) + & ~(1 << 1))); + pci_write_config(dev, AGP_INTEL_I820_RDCR, + (pci_read_config(dev, AGP_INTEL_I820_RDCR, 1) + & ~(1 << 1)), 1); + + case 0x1a308086: /* i845 */ + printf("%s: set MCHCFG to %x\n", __func__, (unsigned) + (pci_read_config(dev, AGP_INTEL_I845_MCHCFG, 1) + & ~(1 << 1))); + pci_write_config(dev, AGP_INTEL_MCHCFG, + (pci_read_config(dev, AGP_INTEL_I845_MCHCFG, 1) + & ~(1 << 1)), 1); + + default: /* Intel Generic (maybe) */ + printf("%s: set NBXCFG to %x\n", __func__, + (pci_read_config(dev, AGP_INTEL_NBXCFG, 4) + & ~(1 << 9))); + pci_write_config(dev, AGP_INTEL_NBXCFG, + (pci_read_config(dev, AGP_INTEL_NBXCFG, 4) + & ~(1 << 9)), 4); + } + pci_write_config(dev, AGP_INTEL_ATTBASE, 0, 4); + AGP_SET_APERTURE(dev, sc->initial_aperture); + agp_free_gatt(sc->gatt); + + return 0; +} + +static u_int32_t +agp_intel_get_aperture(device_t dev) +{ + u_int32_t apsize; + + apsize = pci_read_config(dev, AGP_INTEL_APSIZE, 1) & 0x1f; + + /* + * The size is determined by the number of low bits of + * register APBASE which are forced to zero. The low 22 bits + * are always forced to zero and each zero bit in the apsize + * field just read forces the corresponding bit in the 27:22 + * to be zero. We calculate the aperture size accordingly. + */ + return (((apsize ^ 0x1f) << 22) | ((1 << 22) - 1)) + 1; +} + +static int +agp_intel_set_aperture(device_t dev, u_int32_t aperture) +{ + u_int32_t apsize; + + /* + * Reverse the magic from get_aperture. + */ + apsize = ((aperture - 1) >> 22) ^ 0x1f; + + /* + * Double check for sanity. + */ + if ((((apsize ^ 0x1f) << 22) | ((1 << 22) - 1)) + 1 != aperture) + return EINVAL; + + pci_write_config(dev, AGP_INTEL_APSIZE, apsize, 1); + + return 0; +} + +static int +agp_intel_bind_page(device_t dev, int offset, vm_offset_t physical) +{ + struct agp_intel_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 0x17; + return 0; +} + +static int +agp_intel_unbind_page(device_t dev, int offset) +{ + struct agp_intel_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; + return 0; +} + +static void +agp_intel_flush_tlb(device_t dev) +{ + pci_write_config(dev, AGP_INTEL_AGPCTRL, 0x2200, 4); + pci_write_config(dev, AGP_INTEL_AGPCTRL, 0x2280, 4); +} + +static device_method_t agp_intel_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, agp_intel_probe), + DEVMETHOD(device_attach, agp_intel_attach), + DEVMETHOD(device_detach, agp_intel_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* AGP interface */ + DEVMETHOD(agp_get_aperture, agp_intel_get_aperture), + DEVMETHOD(agp_set_aperture, agp_intel_set_aperture), + DEVMETHOD(agp_bind_page, agp_intel_bind_page), + DEVMETHOD(agp_unbind_page, agp_intel_unbind_page), + DEVMETHOD(agp_flush_tlb, agp_intel_flush_tlb), + DEVMETHOD(agp_enable, agp_generic_enable), + DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), + DEVMETHOD(agp_free_memory, agp_generic_free_memory), + DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), + DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), + + { 0, 0 } +}; + +static driver_t agp_intel_driver = { + "agp", + agp_intel_methods, + sizeof(struct agp_intel_softc), +}; + +static devclass_t agp_devclass; + +DRIVER_MODULE(agp_intel, pci, agp_intel_driver, agp_devclass, 0, 0); diff --git a/sys/pci/agp_sis.c b/sys/pci/agp_sis.c new file mode 100644 index 0000000..adc258f --- /dev/null +++ b/sys/pci/agp_sis.c @@ -0,0 +1,258 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * 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$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/proc.h> + +#include <pci/pcivar.h> +#include <pci/pcireg.h> +#include <pci/agppriv.h> +#include <pci/agpreg.h> + +#include <vm/vm.h> +#include <vm/vm_object.h> +#include <vm/pmap.h> + +struct agp_sis_softc { + struct agp_softc agp; + u_int32_t initial_aperture; /* aperture size at startup */ + struct agp_gatt *gatt; +}; + +static const char* +agp_sis_match(device_t dev) +{ + if (pci_get_class(dev) != PCIC_BRIDGE + || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) + return NULL; + + if (agp_find_caps(dev) == 0) + return NULL; + + switch (pci_get_devid(dev)) { + case 0x00011039: + return ("SiS 5591 host to AGP bridge"); + }; + + if (pci_get_vendor(dev) == 0x1039) + return ("SIS Generic host to PCI bridge"); + + return NULL; +} + +static int +agp_sis_probe(device_t dev) +{ + const char *desc; + + desc = agp_sis_match(dev); + if (desc) { + device_verbose(dev); + device_set_desc(dev, desc); + return 0; + } + + return ENXIO; +} + +static int +agp_sis_attach(device_t dev) +{ + struct agp_sis_softc *sc = device_get_softc(dev); + struct agp_gatt *gatt; + int error; + + error = agp_generic_attach(dev); + if (error) + return error; + + sc->initial_aperture = AGP_GET_APERTURE(dev); + + for (;;) { + gatt = agp_alloc_gatt(dev); + if (gatt) + break; + + /* + * Probably contigmalloc failure. Try reducing the + * aperture so that the gatt size reduces. + */ + if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) { + agp_generic_detach(dev); + return ENOMEM; + } + } + sc->gatt = gatt; + + /* Install the gatt. */ + pci_write_config(dev, AGP_SIS_ATTBASE, gatt->ag_physical, 4); + + /* Enable the aperture. */ + pci_write_config(dev, AGP_SIS_WINCTRL, + pci_read_config(dev, AGP_SIS_WINCTRL, 1) | 3, 1); + + /* + * Enable the TLB and make it automatically invalidate entries + * when the GATT is written. + */ + pci_write_config(dev, AGP_SIS_TLBCTRL, 0x05, 1); + + return 0; +} + +static int +agp_sis_detach(device_t dev) +{ + struct agp_sis_softc *sc = device_get_softc(dev); + int error; + + error = agp_generic_detach(dev); + if (error) + return error; + + /* Disable the aperture.. */ + pci_write_config(dev, AGP_SIS_WINCTRL, + pci_read_config(dev, AGP_SIS_WINCTRL, 1) & ~3, 1); + + /* and the TLB. */ + pci_write_config(dev, AGP_SIS_TLBCTRL, 0, 1); + + /* Put the aperture back the way it started. */ + AGP_SET_APERTURE(dev, sc->initial_aperture); + + agp_free_gatt(sc->gatt); + return 0; +} + +static u_int32_t +agp_sis_get_aperture(device_t dev) +{ + int gws; + + /* + * The aperture size is equal to 4M<<gws. + */ + gws = (pci_read_config(dev, AGP_SIS_WINCTRL, 1) & 0x70) >> 4; + return (4*1024*1024) << gws; +} + +static int +agp_sis_set_aperture(device_t dev, u_int32_t aperture) +{ + int gws; + + /* + * Check for a power of two and make sure its within the + * programmable range. + */ + if (aperture & (aperture - 1) + || aperture < 4*1024*1024 + || aperture > 256*1024*1024) + return EINVAL; + + gws = ffs(aperture / 4*1024*1024) - 1; + + pci_write_config(dev, AGP_SIS_WINCTRL, + ((pci_read_config(dev, AGP_SIS_WINCTRL, 1) & ~0x70) + | gws << 4), 1); + + return 0; +} + +static int +agp_sis_bind_page(device_t dev, int offset, vm_offset_t physical) +{ + struct agp_sis_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical; + return 0; +} + +static int +agp_sis_unbind_page(device_t dev, int offset) +{ + struct agp_sis_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; + return 0; +} + +static void +agp_sis_flush_tlb(device_t dev) +{ + pci_write_config(dev, AGP_SIS_TLBFLUSH, 0x02, 1); +} + +static device_method_t agp_sis_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, agp_sis_probe), + DEVMETHOD(device_attach, agp_sis_attach), + DEVMETHOD(device_detach, agp_sis_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* AGP interface */ + DEVMETHOD(agp_get_aperture, agp_sis_get_aperture), + DEVMETHOD(agp_set_aperture, agp_sis_set_aperture), + DEVMETHOD(agp_bind_page, agp_sis_bind_page), + DEVMETHOD(agp_unbind_page, agp_sis_unbind_page), + DEVMETHOD(agp_flush_tlb, agp_sis_flush_tlb), + DEVMETHOD(agp_enable, agp_generic_enable), + DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), + DEVMETHOD(agp_free_memory, agp_generic_free_memory), + DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), + DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), + + { 0, 0 } +}; + +static driver_t agp_sis_driver = { + "agp", + agp_sis_methods, + sizeof(struct agp_sis_softc), +}; + +static devclass_t agp_devclass; + +DRIVER_MODULE(agp_sis, pci, agp_sis_driver, agp_devclass, 0, 0); diff --git a/sys/pci/agp_via.c b/sys/pci/agp_via.c new file mode 100644 index 0000000..3651cf2 --- /dev/null +++ b/sys/pci/agp_via.c @@ -0,0 +1,257 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * 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$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/proc.h> + +#include <pci/pcivar.h> +#include <pci/pcireg.h> +#include <pci/agppriv.h> +#include <pci/agpreg.h> + +#include <vm/vm.h> +#include <vm/vm_object.h> +#include <vm/pmap.h> + +struct agp_via_softc { + struct agp_softc agp; + u_int32_t initial_aperture; /* aperture size at startup */ + struct agp_gatt *gatt; +}; + +static const char* +agp_via_match(device_t dev) +{ + if (pci_get_class(dev) != PCIC_BRIDGE + || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) + return NULL; + + if (agp_find_caps(dev) == 0) + return NULL; + + switch (pci_get_devid(dev)) { + case 0x05011106: + return ("VIA 8501 (Apollo MVP4) host to PCI bridge"); + case 0x05971106: + return ("VIA 82C597 (Apollo VP3) host to PCI bridge"); + case 0x05981106: + return ("VIA 82C598 (Apollo MVP3) host to PCI bridge"); + case 0x06911106: + return ("VIA 82C691 (Apollo Pro) host to PCI bridge"); + case 0x03051106: + return ("VIA 82C8363 (Apollo KT133A) host to PCI bridge"); + }; + + if (pci_get_vendor(dev) == 0x1106) + return ("VIA Generic host to PCI bridge"); + + return NULL; +} + +static int +agp_via_probe(device_t dev) +{ + const char *desc; + + desc = agp_via_match(dev); + if (desc) { + device_verbose(dev); + device_set_desc(dev, desc); + return 0; + } + + return ENXIO; +} + +static int +agp_via_attach(device_t dev) +{ + struct agp_via_softc *sc = device_get_softc(dev); + struct agp_gatt *gatt; + int error; + + error = agp_generic_attach(dev); + if (error) + return error; + + sc->initial_aperture = AGP_GET_APERTURE(dev); + + for (;;) { + gatt = agp_alloc_gatt(dev); + if (gatt) + break; + + /* + * Probably contigmalloc failure. Try reducing the + * aperture so that the gatt size reduces. + */ + if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) { + agp_generic_detach(dev); + return ENOMEM; + } + } + sc->gatt = gatt; + + /* Install the gatt. */ + pci_write_config(dev, AGP_VIA_ATTBASE, gatt->ag_physical | 3, 4); + + /* Enable the aperture. */ + pci_write_config(dev, AGP_VIA_GARTCTRL, 0x0f, 4); + + return 0; +} + +static int +agp_via_detach(device_t dev) +{ + struct agp_via_softc *sc = device_get_softc(dev); + int error; + + error = agp_generic_detach(dev); + if (error) + return error; + + pci_write_config(dev, AGP_VIA_GARTCTRL, 0, 4); + pci_write_config(dev, AGP_VIA_ATTBASE, 0, 4); + AGP_SET_APERTURE(dev, sc->initial_aperture); + agp_free_gatt(sc->gatt); + + return 0; +} + +static u_int32_t +agp_via_get_aperture(device_t dev) +{ + u_int32_t apsize; + + apsize = pci_read_config(dev, AGP_VIA_APSIZE, 1) & 0x1f; + + /* + * The size is determined by the number of low bits of + * register APBASE which are forced to zero. The low 20 bits + * are always forced to zero and each zero bit in the apsize + * field just read forces the corresponding bit in the 27:20 + * to be zero. We calculate the aperture size accordingly. + */ + return (((apsize ^ 0xff) << 20) | ((1 << 20) - 1)) + 1; +} + +static int +agp_via_set_aperture(device_t dev, u_int32_t aperture) +{ + u_int32_t apsize; + + /* + * Reverse the magic from get_aperture. + */ + apsize = ((aperture - 1) >> 20) ^ 0xff; + + /* + * Double check for sanity. + */ + if ((((apsize ^ 0xff) << 20) | ((1 << 20) - 1)) + 1 != aperture) + return EINVAL; + + pci_write_config(dev, AGP_VIA_APSIZE, apsize, 1); + + return 0; +} + +static int +agp_via_bind_page(device_t dev, int offset, vm_offset_t physical) +{ + struct agp_via_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical; + return 0; +} + +static int +agp_via_unbind_page(device_t dev, int offset) +{ + struct agp_via_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; + return 0; +} + +static void +agp_via_flush_tlb(device_t dev) +{ + pci_write_config(dev, AGP_VIA_GARTCTRL, 0x8f, 4); + pci_write_config(dev, AGP_VIA_GARTCTRL, 0x0f, 4); +} + +static device_method_t agp_via_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, agp_via_probe), + DEVMETHOD(device_attach, agp_via_attach), + DEVMETHOD(device_detach, agp_via_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* AGP interface */ + DEVMETHOD(agp_get_aperture, agp_via_get_aperture), + DEVMETHOD(agp_set_aperture, agp_via_set_aperture), + DEVMETHOD(agp_bind_page, agp_via_bind_page), + DEVMETHOD(agp_unbind_page, agp_via_unbind_page), + DEVMETHOD(agp_flush_tlb, agp_via_flush_tlb), + DEVMETHOD(agp_enable, agp_generic_enable), + DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), + DEVMETHOD(agp_free_memory, agp_generic_free_memory), + DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), + DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), + + { 0, 0 } +}; + +static driver_t agp_via_driver = { + "agp", + agp_via_methods, + sizeof(struct agp_via_softc), +}; + +static devclass_t agp_devclass; + +DRIVER_MODULE(agp_via, pci, agp_via_driver, agp_devclass, 0, 0); diff --git a/sys/pci/agppriv.h b/sys/pci/agppriv.h new file mode 100644 index 0000000..1c5f022 --- /dev/null +++ b/sys/pci/agppriv.h @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * 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 _PCI_AGPPRIV_H_ +#define _PCI_AGPPRIV_H_ + +/* + * This file *must not* be included by code outside the agp driver itself. + */ + +#include <sys/agpio.h> +#include <pci/agpvar.h> + +#define AGP_DEBUGxx + +#ifdef AGP_DEBUG +#define AGP_DPF(x...) do { \ + printf("agp: "); \ + printf(##x); \ +} while (0) +#else +#define AGP_DPF(x...) do {} while (0) +#endif + +#include "agp_if.h" + +/* + * Data structure to describe an AGP memory allocation. + */ +TAILQ_HEAD(agp_memory_list, agp_memory); +struct agp_memory { + TAILQ_ENTRY(agp_memory) am_link; /* wiring for the tailq */ + int am_id; /* unique id for block */ + vm_size_t am_size; /* number of bytes allocated */ + int am_type; /* chipset specific type */ + struct vm_object *am_obj; /* VM object owning pages */ + vm_offset_t am_physical; /* bogus hack for i810 */ + vm_offset_t am_offset; /* page offset if bound */ + int am_is_bound; /* non-zero if bound */ +}; + +/* + * All chipset drivers must have this at the start of their softc. + */ +struct agp_softc { + struct resource *as_aperture; /* location of aperture */ + u_int32_t as_maxmem; /* allocation upper bound */ + u_int32_t as_allocated; /* amount allocated */ + enum agp_acquire_state as_state; + struct agp_memory_list as_memory; /* list of allocated memory */ + int as_nextid; /* next memory block id */ + int as_isopen; /* user device is open */ + dev_t as_devnode; /* from make_dev */ + struct lock as_lock; /* lock for access to GATT */ +}; + +struct agp_gatt { + u_int32_t ag_entries; + u_int32_t *ag_virtual; + vm_offset_t ag_physical; +}; + +void agp_flush_cache(void); +u_int8_t agp_find_caps(device_t dev); +struct agp_gatt *agp_alloc_gatt(device_t dev); +void agp_free_gatt(struct agp_gatt *gatt); +int agp_generic_attach(device_t dev); +int agp_generic_detach(device_t dev); +int agp_generic_enable(device_t dev, u_int32_t mode); +struct agp_memory *agp_generic_alloc_memory(device_t dev, int type, + vm_size_t size); +int agp_generic_free_memory(device_t dev, + struct agp_memory *mem); +int agp_generic_bind_memory(device_t dev, + struct agp_memory *mem, + vm_offset_t offset); +int agp_generic_unbind_memory(device_t dev, + struct agp_memory *mem); + +#endif /* !_PCI_AGPPRIV_H_ */ diff --git a/sys/pci/agpreg.h b/sys/pci/agpreg.h new file mode 100644 index 0000000..13f08e3 --- /dev/null +++ b/sys/pci/agpreg.h @@ -0,0 +1,164 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * 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 _PCI_AGPREG_H_ +#define _PCI_AGPREG_H_ + +/* + * Offsets for various AGP configuration registers. + */ +#define AGP_APBASE 0x10 +#define AGP_CAPPTR 0x34 + +/* + * Offsets from the AGP Capability pointer. + */ +#define AGP_CAPID 0x0 +#define AGP_CAPID_GET_MAJOR(x) (((x) & 0x00f00000U) >> 20) +#define AGP_CAPID_GET_MINOR(x) (((x) & 0x000f0000U) >> 16) +#define AGP_CAPID_GET_NEXT_PTR(x) (((x) & 0x0000ff00U) >> 8) +#define AGP_CAPID_GET_CAP_ID(x) (((x) & 0x000000ffU) >> 0) + +#define AGP_STATUS 0x4 +#define AGP_COMMAND 0x8 + +/* + * Config offsets for Intel AGP chipsets. + */ +#define AGP_INTEL_NBXCFG 0x50 +#define AGP_INTEL_ERRSTS 0x91 +#define AGP_INTEL_AGPCTRL 0xb0 +#define AGP_INTEL_APSIZE 0xb4 +#define AGP_INTEL_ATTBASE 0xb8 + +/* + * Config offsets for Intel i820/i840/i845/i850/i860 AGP chipsets. + */ +#define AGP_INTEL_MCHCFG 0x50 +#define AGP_INTEL_I820_RDCR 0x51 +#define AGP_INTEL_I845_MCHCFG 0x51 +#define AGP_INTEL_I8XX_ERRSTS 0xc8 + +/* + * Config offsets for VIA AGP chipsets. + */ +#define AGP_VIA_GARTCTRL 0x80 +#define AGP_VIA_APSIZE 0x84 +#define AGP_VIA_ATTBASE 0x88 + +/* + * Config offsets for SiS AGP chipsets. + */ +#define AGP_SIS_ATTBASE 0x90 +#define AGP_SIS_WINCTRL 0x94 +#define AGP_SIS_TLBCTRL 0x97 +#define AGP_SIS_TLBFLUSH 0x98 + +/* + * Config offsets for Ali AGP chipsets. + */ +#define AGP_ALI_AGPCTRL 0xb8 +#define AGP_ALI_ATTBASE 0xbc +#define AGP_ALI_TLBCTRL 0xc0 + +/* + * Config offsets for the AMD 751 chipset. + */ +#define AGP_AMD751_APBASE 0x10 +#define AGP_AMD751_REGISTERS 0x14 +#define AGP_AMD751_APCTRL 0xac +#define AGP_AMD751_MODECTRL 0xb0 +#define AGP_AMD751_MODECTRL_SYNEN 0x80 +#define AGP_AMD751_MODECTRL2 0xb2 +#define AGP_AMD751_MODECTRL2_G1LM 0x01 +#define AGP_AMD751_MODECTRL2_GPDCE 0x02 +#define AGP_AMD751_MODECTRL2_NGSE 0x08 + +/* + * Memory mapped register offsets for AMD 751 chipset. + */ +#define AGP_AMD751_CAPS 0x00 +#define AGP_AMD751_CAPS_EHI 0x0800 +#define AGP_AMD751_CAPS_P2P 0x0400 +#define AGP_AMD751_CAPS_MPC 0x0200 +#define AGP_AMD751_CAPS_VBE 0x0100 +#define AGP_AMD751_CAPS_REV 0x00ff +#define AGP_AMD751_STATUS 0x02 +#define AGP_AMD751_STATUS_P2PS 0x0800 +#define AGP_AMD751_STATUS_GCS 0x0400 +#define AGP_AMD751_STATUS_MPS 0x0200 +#define AGP_AMD751_STATUS_VBES 0x0100 +#define AGP_AMD751_STATUS_P2PE 0x0008 +#define AGP_AMD751_STATUS_GCE 0x0004 +#define AGP_AMD751_STATUS_VBEE 0x0001 +#define AGP_AMD751_ATTBASE 0x04 +#define AGP_AMD751_TLBCTRL 0x0c + +/* + * Config registers for i810 device 0 + */ +#define AGP_I810_SMRAM 0x70 +#define AGP_I810_SMRAM_GMS 0xc0 +#define AGP_I810_SMRAM_GMS_DISABLED 0x00 +#define AGP_I810_SMRAM_GMS_ENABLED_0 0x40 +#define AGP_I810_SMRAM_GMS_ENABLED_512 0x80 +#define AGP_I810_SMRAM_GMS_ENABLED_1024 0xc0 +#define AGP_I810_MISCC 0x72 +#define AGP_I810_MISCC_WINSIZE 0x0001 +#define AGP_I810_MISCC_WINSIZE_64 0x0000 +#define AGP_I810_MISCC_WINSIZE_32 0x0001 +#define AGP_I810_MISCC_PLCK 0x0008 +#define AGP_I810_MISCC_PLCK_UNLOCKED 0x0000 +#define AGP_I810_MISCC_PLCK_LOCKED 0x0008 +#define AGP_I810_MISCC_WPTC 0x0030 +#define AGP_I810_MISCC_WPTC_NOLIMIT 0x0000 +#define AGP_I810_MISCC_WPTC_62 0x0010 +#define AGP_I810_MISCC_WPTC_50 0x0020 +#define AGP_I810_MISCC_WPTC_37 0x0030 +#define AGP_I810_MISCC_RPTC 0x00c0 +#define AGP_I810_MISCC_RPTC_NOLIMIT 0x0000 +#define AGP_I810_MISCC_RPTC_62 0x0040 +#define AGP_I810_MISCC_RPTC_50 0x0080 +#define AGP_I810_MISCC_RPTC_37 0x00c0 + +/* + * Config registers for i810 device 1 + */ +#define AGP_I810_GMADR 0x10 +#define AGP_I810_MMADR 0x14 + +/* + * Memory mapped register offsets for i810 chipset. + */ +#define AGP_I810_PGTBL_CTL 0x2020 +#define AGP_I810_DRT 0x3000 +#define AGP_I810_DRT_UNPOPULATED 0x00 +#define AGP_I810_DRT_POPULATED 0x01 +#define AGP_I810_GTT 0x10000 + +#endif /* !_PCI_AGPREG_H_ */ diff --git a/sys/pci/agpvar.h b/sys/pci/agpvar.h new file mode 100644 index 0000000..0869c26 --- /dev/null +++ b/sys/pci/agpvar.h @@ -0,0 +1,126 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * 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 _PCI_AGPVAR_H_ +#define _PCI_AGPVAR_H_ + +/* + * The AGP chipset can be acquired by user or kernel code. If the + * chipset has already been acquired, it cannot be acquired by another + * user until the previous user has released it. + */ +enum agp_acquire_state { + AGP_ACQUIRE_FREE, + AGP_ACQUIRE_USER, + AGP_ACQUIRE_KERNEL +}; + +/* + * This structure is used to query the state of the AGP system. + */ +struct agp_info { + u_int32_t ai_mode; + vm_offset_t ai_aperture_base; + vm_size_t ai_aperture_size; + vm_offset_t ai_aperture_va; + vm_size_t ai_memory_allowed; + vm_size_t ai_memory_used; + u_int32_t ai_devid; +}; + +struct agp_memory_info { + vm_size_t ami_size; /* size in bytes */ + vm_offset_t ami_physical; /* bogus hack for i810 */ + vm_offset_t ami_offset; /* page offset if bound */ + int ami_is_bound; /* non-zero if bound */ +}; + +/* + * Find the AGP device and return it. + */ +device_t agp_find_device(void); + +/* + * Return the current owner of the AGP chipset. + */ +enum agp_acquire_state agp_state(device_t dev); + +/* + * Query the state of the AGP system. + */ +void agp_get_info(device_t dev, struct agp_info *info); + +/* + * Acquire the AGP chipset for use by the kernel. Returns EBUSY if the + * AGP chipset is already acquired by another user. + */ +int agp_acquire(device_t dev); + +/* + * Release the AGP chipset. + */ +int agp_release(device_t dev); + +/* + * Enable the agp hardware with the relavent mode. The mode bits are + * defined in <pci/agpreg.h> + */ +int agp_enable(device_t dev, u_int32_t mode); + +/* + * Allocate physical memory suitable for mapping into the AGP + * aperture. The value returned is an opaque handle which can be + * passed to agp_bind(), agp_unbind() or agp_deallocate(). + */ +void *agp_alloc_memory(device_t dev, int type, vm_size_t bytes); + +/* + * Free memory which was allocated with agp_allocate(). + */ +void agp_free_memory(device_t dev, void *handle); + +/* + * Bind memory allocated with agp_allocate() at a given offset within + * the AGP aperture. Returns EINVAL if the memory is already bound or + * the offset is not at an AGP page boundary. + */ +int agp_bind_memory(device_t dev, void *handle, vm_offset_t offset); + +/* + * Unbind memory from the AGP aperture. Returns EINVAL if the memory + * is not bound. + */ +int agp_unbind_memory(device_t dev, void *handle); + +/* + * Retrieve information about a memory block allocated with + * agp_alloc_memory(). + */ +void agp_memory_info(device_t dev, void *handle, struct agp_memory_info *mi); + +#endif /* !_PCI_AGPVAR_H_ */ diff --git a/sys/pci/alpm.c b/sys/pci/alpm.c new file mode 100644 index 0000000..ae12197f --- /dev/null +++ b/sys/pci/alpm.c @@ -0,0 +1,641 @@ +/*- + * Copyright (c) 1998, 1999, 2001 Nicolas Souchu + * 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$ + * + */ + +/* + * Power Management support for the Acer M15x3 chipsets + */ +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/uio.h> + +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <pci/pcivar.h> +#include <pci/pcireg.h> + +#include <dev/iicbus/iiconf.h> +#include <dev/smbus/smbconf.h> +#include "smbus_if.h" + +#define ALPM_DEBUG(x) if (alpm_debug) (x) + +#ifdef DEBUG +static int alpm_debug = 1; +#else +static int alpm_debug = 0; +#endif + +#define ACER_M1543_PMU_ID 0x710110b9 + +/* Uncomment this line to force another I/O base address for SMB */ +/* #define ALPM_SMBIO_BASE_ADDR 0x3a80 */ + +/* I/O registers offsets - the base address is programmed via the + * SMBBA PCI configuration register + */ +#define SMBSTS 0x0 /* SMBus host/slave status register */ +#define SMBCMD 0x1 /* SMBus host/slave command register */ +#define SMBSTART 0x2 /* start to generate programmed cycle */ +#define SMBHADDR 0x3 /* host address register */ +#define SMBHDATA 0x4 /* data A register for host controller */ +#define SMBHDATB 0x5 /* data B register for host controller */ +#define SMBHBLOCK 0x6 /* block register for host controller */ +#define SMBHCMD 0x7 /* command register for host controller */ + +/* SMBSTS masks */ +#define TERMINATE 0x80 +#define BUS_COLLI 0x40 +#define DEVICE_ERR 0x20 +#define SMI_I_STS 0x10 +#define HST_BSY 0x08 +#define IDL_STS 0x04 +#define HSTSLV_STS 0x02 +#define HSTSLV_BSY 0x01 + +/* SMBCMD masks */ +#define SMB_BLK_CLR 0x80 +#define T_OUT_CMD 0x08 +#define ABORT_HOST 0x04 + +/* SMBus commands */ +#define SMBQUICK 0x00 +#define SMBSRBYTE 0x10 /* send/receive byte */ +#define SMBWRBYTE 0x20 /* write/read byte */ +#define SMBWRWORD 0x30 /* write/read word */ +#define SMBWRBLOCK 0x40 /* write/read block */ + +/* PCI configuration registers and masks + */ +#define COM 0x4 +#define COM_ENABLE_IO 0x1 + +#define SMBBA 0x14 + +#define ATPC 0x5b +#define ATPC_SMBCTRL 0x04 /* XX linux has this as 0x6 */ + +#define SMBHSI 0xe0 +#define SMBHSI_SLAVE 0x2 +#define SMBHSI_HOST 0x1 + +#define SMBHCBC 0xe2 +#define SMBHCBC_CLOCK 0x70 + +#define SMBCLOCK_149K 0x0 +#define SMBCLOCK_74K 0x20 +#define SMBCLOCK_37K 0x40 +#define SMBCLOCK_223K 0x80 +#define SMBCLOCK_111K 0xa0 +#define SMBCLOCK_55K 0xc0 + +struct alpm_softc { + int base; + struct resource *res; + bus_space_tag_t smbst; + bus_space_handle_t smbsh; + device_t smbus; +}; + +#define ALPM_SMBINB(alpm,register) \ + (bus_space_read_1(alpm->smbst, alpm->smbsh, register)) +#define ALPM_SMBOUTB(alpm,register,value) \ + (bus_space_write_1(alpm->smbst, alpm->smbsh, register, value)) + +static int +alpm_probe(device_t dev) +{ +#ifdef ALPM_SMBIO_BASE_ADDR + u_int32_t l; +#endif + + if (pci_get_devid(dev) == ACER_M1543_PMU_ID) { + device_set_desc(dev, "AcerLabs M15x3 Power Management Unit"); + +#ifdef ALPM_SMBIO_BASE_ADDR + if (bootverbose || alpm_debug) + device_printf(dev, "forcing base I/O at 0x%x\n", + ALPM_SMBIO_BASE_ADDR); + + /* disable I/O */ + l = pci_read_config(dev, COM, 2); + pci_write_config(dev, COM, l & ~COM_ENABLE_IO, 2); + + /* set the I/O base address */ + pci_write_config(dev, SMBBA, ALPM_SMBIO_BASE_ADDR | 0x1, 4); + + /* enable I/O */ + pci_write_config(dev, COM, l | COM_ENABLE_IO, 2); + + if (bus_set_resource(dev, SYS_RES_IOPORT, SMBBA, + ALPM_SMBIO_BASE_ADDR, 256)) { + device_printf(dev, "could not set bus resource\n"); + return (ENXIO); + } +#endif + return (0); + } + + return (ENXIO); +} + +static int +alpm_attach(device_t dev) +{ + int rid, unit; + u_int32_t l; + struct alpm_softc *alpm; + + alpm = device_get_softc(dev); + unit = device_get_unit(dev); + + /* Unlock SMBIO base register access */ + l = pci_read_config(dev, ATPC, 1); + pci_write_config(dev, ATPC, l & ~ATPC_SMBCTRL, 1); + + /* + * XX linux sets clock to 74k, should we? + l = pci_read_config(dev, SMBHCBC, 1); + l &= 0x1f; + l |= SMBCLOCK_74K; + pci_write_config(dev, SMBHCBC, l, 1); + */ + + if (bootverbose || alpm_debug) { + l = pci_read_config(dev, SMBHSI, 1); + device_printf(dev, "%s/%s", + (l & SMBHSI_HOST) ? "host":"nohost", + (l & SMBHSI_SLAVE) ? "slave":"noslave"); + + l = pci_read_config(dev, SMBHCBC, 1); + switch (l & SMBHCBC_CLOCK) { + case SMBCLOCK_149K: + printf(" 149K"); + break; + case SMBCLOCK_74K: + printf(" 74K"); + break; + case SMBCLOCK_37K: + printf(" 37K"); + break; + case SMBCLOCK_223K: + printf(" 223K"); + break; + case SMBCLOCK_111K: + printf(" 111K"); + break; + case SMBCLOCK_55K: + printf(" 55K"); + break; + default: + printf("unkown"); + break; + } + printf("\n"); + } + + rid = SMBBA; + alpm->res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (alpm->res == NULL) { + device_printf(dev,"Could not allocate Bus space\n"); + return (ENXIO); + } + alpm->smbst = rman_get_bustag(alpm->res); + alpm->smbsh = rman_get_bushandle(alpm->res); + + /* attach the smbus */ + alpm->smbus = device_add_child(dev, "smbus", -1); + bus_generic_attach(dev); + + return (0); +} + +static int +alpm_detach(device_t dev) +{ + struct alpm_softc *alpm = device_get_softc(dev); + + if (alpm->smbus) { + device_delete_child(dev, alpm->smbus); + alpm->smbus = NULL; + } + + if (alpm->res) + bus_release_resource(dev, SYS_RES_IOPORT, SMBBA, alpm->res); + + return (0); +} + +static int +alpm_callback(device_t dev, int index, caddr_t *data) +{ + int error = 0; + + switch (index) { + case SMB_REQUEST_BUS: + case SMB_RELEASE_BUS: + /* ok, bus allocation accepted */ + break; + default: + error = EINVAL; + } + + return (error); +} + +static int +alpm_clear(struct alpm_softc *sc) +{ + ALPM_SMBOUTB(sc, SMBSTS, 0xff); + DELAY(10); + + return (0); +} + +#if 0 +static int +alpm_abort(struct alpm_softc *sc) +{ + ALPM_SMBOUTB(sc, SMBCMD, T_OUT_CMD | ABORT_HOST); + + return (0); +} +#endif + +static int +alpm_idle(struct alpm_softc *sc) +{ + u_char sts; + + sts = ALPM_SMBINB(sc, SMBSTS); + + ALPM_DEBUG(printf("alpm: idle? STS=0x%x\n", sts)); + + return (sts & IDL_STS); +} + +/* + * Poll the SMBus controller + */ +static int +alpm_wait(struct alpm_softc *sc) +{ + int count = 10000; + u_char sts = 0; + int error; + + /* wait for command to complete and SMBus controller is idle */ + while(count--) { + DELAY(10); + sts = ALPM_SMBINB(sc, SMBSTS); + if (sts & SMI_I_STS) + break; + } + + ALPM_DEBUG(printf("alpm: STS=0x%x\n", sts)); + + error = SMB_ENOERR; + + if (!count) + error |= SMB_ETIMEOUT; + + if (sts & TERMINATE) + error |= SMB_EABORT; + + if (sts & BUS_COLLI) + error |= SMB_ENOACK; + + if (sts & DEVICE_ERR) + error |= SMB_EBUSERR; + + if (error != SMB_ENOERR) + alpm_clear(sc); + + return (error); +} + +static int +alpm_quick(device_t dev, u_char slave, int how) +{ + struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); + int error; + + alpm_clear(sc); + if (!alpm_idle(sc)) + return (EBUSY); + + switch (how) { + case SMB_QWRITE: + ALPM_DEBUG(printf("alpm: QWRITE to 0x%x", slave)); + ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); + break; + case SMB_QREAD: + ALPM_DEBUG(printf("alpm: QREAD to 0x%x", slave)); + ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); + break; + default: + panic("%s: unknown QUICK command (%x)!", __func__, + how); + } + ALPM_SMBOUTB(sc, SMBCMD, SMBQUICK); + ALPM_SMBOUTB(sc, SMBSTART, 0xff); + + error = alpm_wait(sc); + + ALPM_DEBUG(printf(", error=0x%x\n", error)); + + return (error); +} + +static int +alpm_sendb(device_t dev, u_char slave, char byte) +{ + struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); + int error; + + alpm_clear(sc); + if (!alpm_idle(sc)) + return (SMB_EBUSY); + + ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); + ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE); + ALPM_SMBOUTB(sc, SMBHDATA, byte); + ALPM_SMBOUTB(sc, SMBSTART, 0xff); + + error = alpm_wait(sc); + + ALPM_DEBUG(printf("alpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error)); + + return (error); +} + +static int +alpm_recvb(device_t dev, u_char slave, char *byte) +{ + struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); + int error; + + alpm_clear(sc); + if (!alpm_idle(sc)) + return (SMB_EBUSY); + + ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); + ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE); + ALPM_SMBOUTB(sc, SMBSTART, 0xff); + + if ((error = alpm_wait(sc)) == SMB_ENOERR) + *byte = ALPM_SMBINB(sc, SMBHDATA); + + ALPM_DEBUG(printf("alpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error)); + + return (error); +} + +static int +alpm_writeb(device_t dev, u_char slave, char cmd, char byte) +{ + struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); + int error; + + alpm_clear(sc); + if (!alpm_idle(sc)) + return (SMB_EBUSY); + + ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); + ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE); + ALPM_SMBOUTB(sc, SMBHDATA, byte); + ALPM_SMBOUTB(sc, SMBHCMD, cmd); + ALPM_SMBOUTB(sc, SMBSTART, 0xff); + + error = alpm_wait(sc); + + ALPM_DEBUG(printf("alpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error)); + + return (error); +} + +static int +alpm_readb(device_t dev, u_char slave, char cmd, char *byte) +{ + struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); + int error; + + alpm_clear(sc); + if (!alpm_idle(sc)) + return (SMB_EBUSY); + + ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); + ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE); + ALPM_SMBOUTB(sc, SMBHCMD, cmd); + ALPM_SMBOUTB(sc, SMBSTART, 0xff); + + if ((error = alpm_wait(sc)) == SMB_ENOERR) + *byte = ALPM_SMBINB(sc, SMBHDATA); + + ALPM_DEBUG(printf("alpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error)); + + return (error); +} + +static int +alpm_writew(device_t dev, u_char slave, char cmd, short word) +{ + struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); + int error; + + alpm_clear(sc); + if (!alpm_idle(sc)) + return (SMB_EBUSY); + + ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); + ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD); + ALPM_SMBOUTB(sc, SMBHDATA, word & 0x00ff); + ALPM_SMBOUTB(sc, SMBHDATB, (word & 0xff00) >> 8); + ALPM_SMBOUTB(sc, SMBHCMD, cmd); + ALPM_SMBOUTB(sc, SMBSTART, 0xff); + + error = alpm_wait(sc); + + ALPM_DEBUG(printf("alpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error)); + + return (error); +} + +static int +alpm_readw(device_t dev, u_char slave, char cmd, short *word) +{ + struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); + int error; + u_char high, low; + + alpm_clear(sc); + if (!alpm_idle(sc)) + return (SMB_EBUSY); + + ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); + ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD); + ALPM_SMBOUTB(sc, SMBHCMD, cmd); + ALPM_SMBOUTB(sc, SMBSTART, 0xff); + + if ((error = alpm_wait(sc)) == SMB_ENOERR) { + low = ALPM_SMBINB(sc, SMBHDATA); + high = ALPM_SMBINB(sc, SMBHDATB); + + *word = ((high & 0xff) << 8) | (low & 0xff); + } + + ALPM_DEBUG(printf("alpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error)); + + return (error); +} + +static int +alpm_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) +{ + struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); + u_char remain, len, i; + int error = SMB_ENOERR; + + alpm_clear(sc); + if(!alpm_idle(sc)) + return (SMB_EBUSY); + + remain = count; + while (remain) { + len = min(remain, 32); + + ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); + + /* set the cmd and reset the + * 32-byte long internal buffer */ + ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR); + + ALPM_SMBOUTB(sc, SMBHDATA, len); + + /* fill the 32-byte internal buffer */ + for (i=0; i<len; i++) { + ALPM_SMBOUTB(sc, SMBHBLOCK, buf[count-remain+i]); + DELAY(2); + } + ALPM_SMBOUTB(sc, SMBHCMD, cmd); + ALPM_SMBOUTB(sc, SMBSTART, 0xff); + + if ((error = alpm_wait(sc)) != SMB_ENOERR) + goto error; + + remain -= len; + } + +error: + ALPM_DEBUG(printf("alpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error)); + + return (error); +} + +static int +alpm_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf) +{ + struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); + u_char remain, len, i; + int error = SMB_ENOERR; + + alpm_clear(sc); + if (!alpm_idle(sc)) + return (SMB_EBUSY); + + remain = count; + while (remain) { + ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); + + /* set the cmd and reset the + * 32-byte long internal buffer */ + ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR); + + ALPM_SMBOUTB(sc, SMBHCMD, cmd); + ALPM_SMBOUTB(sc, SMBSTART, 0xff); + + if ((error = alpm_wait(sc)) != SMB_ENOERR) + goto error; + + len = ALPM_SMBINB(sc, SMBHDATA); + + /* read the 32-byte internal buffer */ + for (i=0; i<len; i++) { + buf[count-remain+i] = ALPM_SMBINB(sc, SMBHBLOCK); + DELAY(2); + } + + remain -= len; + } +error: + ALPM_DEBUG(printf("alpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error)); + + return (error); +} + +static devclass_t alpm_devclass; + +static device_method_t alpm_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, alpm_probe), + DEVMETHOD(device_attach, alpm_attach), + DEVMETHOD(device_detach, alpm_detach), + + /* smbus interface */ + DEVMETHOD(smbus_callback, alpm_callback), + DEVMETHOD(smbus_quick, alpm_quick), + DEVMETHOD(smbus_sendb, alpm_sendb), + DEVMETHOD(smbus_recvb, alpm_recvb), + DEVMETHOD(smbus_writeb, alpm_writeb), + DEVMETHOD(smbus_readb, alpm_readb), + DEVMETHOD(smbus_writew, alpm_writew), + DEVMETHOD(smbus_readw, alpm_readw), + DEVMETHOD(smbus_bwrite, alpm_bwrite), + DEVMETHOD(smbus_bread, alpm_bread), + + { 0, 0 } +}; + +static driver_t alpm_driver = { + "alpm", + alpm_methods, + sizeof(struct alpm_softc) +}; + +DRIVER_MODULE(alpm, pci, alpm_driver, alpm_devclass, 0, 0); +MODULE_DEPEND(alpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); +MODULE_VERSION(alpm, 1); diff --git a/sys/pci/amd.c b/sys/pci/amd.c new file mode 100644 index 0000000..5089371 --- /dev/null +++ b/sys/pci/amd.c @@ -0,0 +1,2453 @@ +/* + ********************************************************************* + * FILE NAME : amd.c + * BY : C.L. Huang (ching@tekram.com.tw) + * Erich Chen (erich@tekram.com.tw) + * Description: Device Driver for the amd53c974 PCI Bus Master + * SCSI Host adapter found on cards such as + * the Tekram DC-390(T). + * (C)Copyright 1995-1999 Tekram Technology Co., Ltd. + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* + * $FreeBSD$ + */ + +/* + ********************************************************************* + * HISTORY: + * + * REV# DATE NAME DESCRIPTION + * 1.00 07/02/96 CLH First release for RELEASE-2.1.0 + * 1.01 08/20/96 CLH Update for RELEASE-2.1.5 + * 1.02 11/06/96 CLH Fixed more than 1 LUN scanning + * 1.03 12/20/96 CLH Modify to support 2.2-ALPHA + * 1.04 12/26/97 CLH Modify to support RELEASE-2.2.5 + * 1.05 01/01/99 ERICH CHEN Modify to support RELEASE-3.0.x (CAM) + ********************************************************************* + */ + +/* #define AMD_DEBUG0 */ +/* #define AMD_DEBUG_SCSI_PHASE */ + +#include <sys/param.h> + +#include <sys/systm.h> +#include <sys/queue.h> +#include <sys/kernel.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <machine/bus_pio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/cam_sim.h> +#include <cam/cam_xpt_sim.h> +#include <cam/cam_debug.h> + +#include <cam/scsi/scsi_all.h> +#include <cam/scsi/scsi_message.h> + +#include <pci/pcivar.h> +#include <pci/pcireg.h> +#include <pci/amd.h> + +#define PCI_DEVICE_ID_AMD53C974 0x20201022ul +#define PCI_BASE_ADDR0 0x10 + +typedef u_int (phase_handler_t)(struct amd_softc *, struct amd_srb *, u_int); +typedef phase_handler_t *phase_handler_func_t; + +static void amd_intr(void *vamd); +static int amdstart(struct amd_softc *amd, struct amd_srb * pSRB); +static phase_handler_t amd_NopPhase; + +static phase_handler_t amd_DataOutPhase0; +static phase_handler_t amd_DataInPhase0; +#define amd_CommandPhase0 amd_NopPhase +static phase_handler_t amd_StatusPhase0; +static phase_handler_t amd_MsgOutPhase0; +static phase_handler_t amd_MsgInPhase0; +static phase_handler_t amd_DataOutPhase1; +static phase_handler_t amd_DataInPhase1; +static phase_handler_t amd_CommandPhase1; +static phase_handler_t amd_StatusPhase1; +static phase_handler_t amd_MsgOutPhase1; +static phase_handler_t amd_MsgInPhase1; + +static void amdsetupcommand(struct amd_softc *amd, struct amd_srb *srb); +static int amdparsemsg(struct amd_softc *amd); +static int amdhandlemsgreject(struct amd_softc *amd); +static void amdconstructsdtr(struct amd_softc *amd, + u_int period, u_int offset); +static u_int amdfindclockrate(struct amd_softc *amd, u_int *period); +static int amdsentmsg(struct amd_softc *amd, u_int msgtype, int full); + +static void DataIO_Comm(struct amd_softc *amd, struct amd_srb *pSRB, u_int dir); +static void amd_Disconnect(struct amd_softc *amd); +static void amd_Reselect(struct amd_softc *amd); +static void SRBdone(struct amd_softc *amd, struct amd_srb *pSRB); +static void amd_ScsiRstDetect(struct amd_softc *amd); +static void amd_ResetSCSIBus(struct amd_softc *amd); +static void RequestSense(struct amd_softc *amd, struct amd_srb *pSRB); +static void amd_InvalidCmd(struct amd_softc *amd); + +#if 0 +static void amd_timeout(void *arg1); +static void amd_reset(struct amd_softc *amd); +#endif +static u_int8_t * phystovirt(struct amd_srb *pSRB, u_int32_t xferCnt); + +void amd_linkSRB(struct amd_softc *amd); +static int amd_init(device_t); +static void amd_load_defaults(struct amd_softc *amd); +static void amd_load_eeprom_or_defaults(struct amd_softc *amd); +static int amd_EEpromInDO(struct amd_softc *amd); +static u_int16_t EEpromGetData1(struct amd_softc *amd); +static void amd_EnDisableCE(struct amd_softc *amd, int mode, int *regval); +static void amd_EEpromOutDI(struct amd_softc *amd, int *regval, int Carry); +static void amd_Prepare(struct amd_softc *amd, int *regval, u_int8_t EEpromCmd); +static void amd_ReadEEprom(struct amd_softc *amd); + +static int amd_probe(device_t); +static int amd_attach(device_t); +static void amdcompletematch(struct amd_softc *amd, target_id_t target, + lun_id_t lun, u_int tag, struct srb_queue *queue, + cam_status status); +static void amdsetsync(struct amd_softc *amd, u_int target, u_int clockrate, + u_int period, u_int offset, u_int type); +static void amdsettags(struct amd_softc *amd, u_int target, int tagenb); + +static __inline void amd_clear_msg_state(struct amd_softc *amd); + +static __inline void +amd_clear_msg_state(struct amd_softc *amd) +{ + amd->msgout_len = 0; + amd->msgout_index = 0; + amd->msgin_index = 0; +} + +/* CAM SIM entry points */ +#define ccb_srb_ptr spriv_ptr0 +#define ccb_amd_ptr spriv_ptr1 +static void amd_action(struct cam_sim *sim, union ccb *ccb); +static void amd_poll(struct cam_sim *sim); + +/* + * State engine function tables indexed by SCSI phase number + */ +phase_handler_func_t amd_SCSI_phase0[] = { + amd_DataOutPhase0, + amd_DataInPhase0, + amd_CommandPhase0, + amd_StatusPhase0, + amd_NopPhase, + amd_NopPhase, + amd_MsgOutPhase0, + amd_MsgInPhase0 +}; + +phase_handler_func_t amd_SCSI_phase1[] = { + amd_DataOutPhase1, + amd_DataInPhase1, + amd_CommandPhase1, + amd_StatusPhase1, + amd_NopPhase, + amd_NopPhase, + amd_MsgOutPhase1, + amd_MsgInPhase1 +}; + +/* + * EEProm/BIOS negotiation periods + */ +u_int8_t eeprom_period[] = { + 25, /* 10.0MHz */ + 32, /* 8.0MHz */ + 38, /* 6.6MHz */ + 44, /* 5.7MHz */ + 50, /* 5.0MHz */ + 63, /* 4.0MHz */ + 83, /* 3.0MHz */ + 125 /* 2.0MHz */ +}; + +/* + * chip clock setting to SCSI specified sync parameter table. + */ +u_int8_t tinfo_sync_period[] = { + 25, /* 10.0 */ + 32, /* 8.0 */ + 38, /* 6.6 */ + 44, /* 5.7 */ + 50, /* 5.0 */ + 57, /* 4.4 */ + 63, /* 4.0 */ + 70, /* 3.6 */ + 76, /* 3.3 */ + 83 /* 3.0 */ +}; + +static __inline struct amd_srb * +amdgetsrb(struct amd_softc * amd) +{ + int intflag; + struct amd_srb * pSRB; + + intflag = splcam(); + pSRB = TAILQ_FIRST(&amd->free_srbs); + if (pSRB) + TAILQ_REMOVE(&amd->free_srbs, pSRB, links); + splx(intflag); + return (pSRB); +} + +static void +amdsetupcommand(struct amd_softc *amd, struct amd_srb *srb) +{ + struct scsi_request_sense sense_cmd; + struct ccb_scsiio *csio; + u_int8_t *cdb; + u_int cdb_len; + + csio = &srb->pccb->csio; + + if (srb->SRBFlag & AUTO_REQSENSE) { + sense_cmd.opcode = REQUEST_SENSE; + sense_cmd.byte2 = srb->pccb->ccb_h.target_lun << 5; + sense_cmd.unused[0] = 0; + sense_cmd.unused[1] = 0; + sense_cmd.length = csio->sense_len; + sense_cmd.control = 0; + cdb = &sense_cmd.opcode; + cdb_len = sizeof(sense_cmd); + } else { + cdb = &srb->CmdBlock[0]; + cdb_len = srb->ScsiCmdLen; + } + amd_write8_multi(amd, SCSIFIFOREG, cdb, cdb_len); +} + +/* + * Attempt to start a waiting transaction. Interrupts must be disabled + * upon entry to this function. + */ +static void +amdrunwaiting(struct amd_softc *amd) { + struct amd_srb *srb; + + if (amd->last_phase != SCSI_BUS_FREE) + return; + + srb = TAILQ_FIRST(&amd->waiting_srbs); + if (srb == NULL) + return; + + if (amdstart(amd, srb) == 0) { + TAILQ_REMOVE(&amd->waiting_srbs, srb, links); + TAILQ_INSERT_HEAD(&amd->running_srbs, srb, links); + } +} + +static void +amdexecutesrb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) +{ + struct amd_srb *srb; + union ccb *ccb; + struct amd_softc *amd; + int s; + + srb = (struct amd_srb *)arg; + ccb = srb->pccb; + amd = (struct amd_softc *)ccb->ccb_h.ccb_amd_ptr; + + if (error != 0) { + if (error != EFBIG) + printf("amd%d: Unexepected error 0x%x returned from " + "bus_dmamap_load\n", amd->unit, error); + if (ccb->ccb_h.status == CAM_REQ_INPROG) { + xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); + ccb->ccb_h.status = CAM_REQ_TOO_BIG|CAM_DEV_QFRZN; + } + TAILQ_INSERT_HEAD(&amd->free_srbs, srb, links); + xpt_done(ccb); + return; + } + + if (nseg != 0) { + struct amd_sg *sg; + bus_dma_segment_t *end_seg; + bus_dmasync_op_t op; + + end_seg = dm_segs + nseg; + + /* Copy the segments into our SG list */ + srb->pSGlist = &srb->SGsegment[0]; + sg = srb->pSGlist; + while (dm_segs < end_seg) { + sg->SGXLen = dm_segs->ds_len; + sg->SGXPtr = dm_segs->ds_addr; + sg++; + dm_segs++; + } + + if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) + op = BUS_DMASYNC_PREREAD; + else + op = BUS_DMASYNC_PREWRITE; + + bus_dmamap_sync(amd->buffer_dmat, srb->dmamap, op); + + } + srb->SGcount = nseg; + srb->SGIndex = 0; + srb->AdaptStatus = 0; + srb->TargetStatus = 0; + srb->MsgCnt = 0; + srb->SRBStatus = 0; + srb->SRBFlag = 0; + srb->SRBState = 0; + srb->TotalXferredLen = 0; + srb->SGPhysAddr = 0; + srb->SGToBeXferLen = 0; + srb->EndMessage = 0; + + s = splcam(); + + /* + * Last time we need to check if this CCB needs to + * be aborted. + */ + if (ccb->ccb_h.status != CAM_REQ_INPROG) { + if (nseg != 0) + bus_dmamap_unload(amd->buffer_dmat, srb->dmamap); + TAILQ_INSERT_HEAD(&amd->free_srbs, srb, links); + xpt_done(ccb); + splx(s); + return; + } + ccb->ccb_h.status |= CAM_SIM_QUEUED; +#if 0 + /* XXX Need a timeout handler */ + ccb->ccb_h.timeout_ch = + timeout(amdtimeout, (caddr_t)srb, + (ccb->ccb_h.timeout * hz) / 1000); +#endif + TAILQ_INSERT_TAIL(&amd->waiting_srbs, srb, links); + amdrunwaiting(amd); + splx(s); +} + +static void +amd_action(struct cam_sim * psim, union ccb * pccb) +{ + struct amd_softc * amd; + u_int target_id, target_lun; + + CAM_DEBUG(pccb->ccb_h.path, CAM_DEBUG_TRACE, ("amd_action\n")); + + amd = (struct amd_softc *) cam_sim_softc(psim); + target_id = pccb->ccb_h.target_id; + target_lun = pccb->ccb_h.target_lun; + + switch (pccb->ccb_h.func_code) { + case XPT_SCSI_IO: + { + struct amd_srb * pSRB; + struct ccb_scsiio *pcsio; + + pcsio = &pccb->csio; + + /* + * Assign an SRB and connect it with this ccb. + */ + pSRB = amdgetsrb(amd); + + if (!pSRB) { + /* Freeze SIMQ */ + pccb->ccb_h.status = CAM_RESRC_UNAVAIL; + xpt_done(pccb); + return; + } + pSRB->pccb = pccb; + pccb->ccb_h.ccb_srb_ptr = pSRB; + pccb->ccb_h.ccb_amd_ptr = amd; + pSRB->ScsiCmdLen = pcsio->cdb_len; + bcopy(pcsio->cdb_io.cdb_bytes, pSRB->CmdBlock, pcsio->cdb_len); + if ((pccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { + if ((pccb->ccb_h.flags & CAM_SCATTER_VALID) == 0) { + /* + * We've been given a pointer + * to a single buffer. + */ + if ((pccb->ccb_h.flags & CAM_DATA_PHYS) == 0) { + int s; + int error; + + s = splsoftvm(); + error = + bus_dmamap_load(amd->buffer_dmat, + pSRB->dmamap, + pcsio->data_ptr, + pcsio->dxfer_len, + amdexecutesrb, + pSRB, /*flags*/0); + if (error == EINPROGRESS) { + /* + * So as to maintain + * ordering, freeze the + * controller queue + * until our mapping is + * returned. + */ + xpt_freeze_simq(amd->psim, 1); + pccb->ccb_h.status |= + CAM_RELEASE_SIMQ; + } + splx(s); + } else { + struct bus_dma_segment seg; + + /* Pointer to physical buffer */ + seg.ds_addr = + (bus_addr_t)pcsio->data_ptr; + seg.ds_len = pcsio->dxfer_len; + amdexecutesrb(pSRB, &seg, 1, 0); + } + } else { + struct bus_dma_segment *segs; + + if ((pccb->ccb_h.flags & CAM_SG_LIST_PHYS) == 0 + || (pccb->ccb_h.flags & CAM_DATA_PHYS) != 0) { + TAILQ_INSERT_HEAD(&amd->free_srbs, + pSRB, links); + pccb->ccb_h.status = CAM_PROVIDE_FAIL; + xpt_done(pccb); + return; + } + + /* Just use the segments provided */ + segs = + (struct bus_dma_segment *)pcsio->data_ptr; + amdexecutesrb(pSRB, segs, pcsio->sglist_cnt, 0); + } + } else + amdexecutesrb(pSRB, NULL, 0, 0); + break; + } + case XPT_PATH_INQ: + { + struct ccb_pathinq *cpi = &pccb->cpi; + + cpi->version_num = 1; + cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE; + cpi->target_sprt = 0; + cpi->hba_misc = 0; + cpi->hba_eng_cnt = 0; + cpi->max_target = 7; + cpi->max_lun = amd->max_lun; /* 7 or 0 */ + cpi->initiator_id = amd->AdaptSCSIID; + cpi->bus_id = cam_sim_bus(psim); + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, "TRM-AMD", HBA_IDLEN); + strncpy(cpi->dev_name, cam_sim_name(psim), DEV_IDLEN); + cpi->unit_number = cam_sim_unit(psim); + cpi->ccb_h.status = CAM_REQ_CMP; + xpt_done(pccb); + break; + } + case XPT_ABORT: + pccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(pccb); + break; + case XPT_RESET_BUS: + { + + int i; + + amd_ResetSCSIBus(amd); + amd->ACBFlag = 0; + + for (i = 0; i < 500; i++) { + DELAY(1000); /* Wait until our interrupt + * handler sees it */ + } + + pccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(pccb); + break; + } + case XPT_RESET_DEV: + pccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(pccb); + break; + case XPT_TERM_IO: + pccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(pccb); + case XPT_GET_TRAN_SETTINGS: + { + struct ccb_trans_settings *cts; + struct amd_target_info *targ_info; + struct amd_transinfo *tinfo; + int intflag; + + cts = &pccb->cts; + intflag = splcam(); + targ_info = &amd->tinfo[target_id]; + if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { + /* current transfer settings */ + if (targ_info->disc_tag & AMD_CUR_DISCENB) { + cts->flags = CCB_TRANS_DISC_ENB; + } else { + cts->flags = 0; /* no tag & disconnect */ + } + if (targ_info->disc_tag & AMD_CUR_TAGENB) { + cts->flags |= CCB_TRANS_TAG_ENB; + } + tinfo = &targ_info->current; + } else { + /* default(user) transfer settings */ + if (targ_info->disc_tag & AMD_USR_DISCENB) { + cts->flags = CCB_TRANS_DISC_ENB; + } else { + cts->flags = 0; + } + if (targ_info->disc_tag & AMD_USR_TAGENB) { + cts->flags |= CCB_TRANS_TAG_ENB; + } + tinfo = &targ_info->user; + } + + cts->sync_period = tinfo->period; + cts->sync_offset = tinfo->offset; + cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; + splx(intflag); + cts->valid = CCB_TRANS_SYNC_RATE_VALID + | CCB_TRANS_SYNC_OFFSET_VALID + | CCB_TRANS_BUS_WIDTH_VALID + | CCB_TRANS_DISC_VALID + | CCB_TRANS_TQ_VALID; + pccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(pccb); + break; + } + case XPT_SET_TRAN_SETTINGS: + { + struct ccb_trans_settings *cts; + struct amd_target_info *targ_info; + u_int update_type; + int intflag; + int last_entry; + + cts = &pccb->cts; + update_type = 0; + if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { + update_type |= AMD_TRANS_GOAL; + } else if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) { + update_type |= AMD_TRANS_USER; + } + if (update_type == 0 + || update_type == (AMD_TRANS_USER|AMD_TRANS_GOAL)) { + cts->ccb_h.status = CAM_REQ_INVALID; + xpt_done(pccb); + } + + intflag = splcam(); + targ_info = &amd->tinfo[target_id]; + + if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { + if (update_type & AMD_TRANS_GOAL) { + if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) { + targ_info->disc_tag |= AMD_CUR_DISCENB; + } else { + targ_info->disc_tag &= ~AMD_CUR_DISCENB; + } + } + if (update_type & AMD_TRANS_USER) { + if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) { + targ_info->disc_tag |= AMD_USR_DISCENB; + } else { + targ_info->disc_tag &= ~AMD_USR_DISCENB; + } + } + } + if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { + if (update_type & AMD_TRANS_GOAL) { + if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) { + targ_info->disc_tag |= AMD_CUR_TAGENB; + } else { + targ_info->disc_tag &= ~AMD_CUR_TAGENB; + } + } + if (update_type & AMD_TRANS_USER) { + if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) { + targ_info->disc_tag |= AMD_USR_TAGENB; + } else { + targ_info->disc_tag &= ~AMD_USR_TAGENB; + } + } + } + + if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) == 0) { + if (update_type & AMD_TRANS_GOAL) + cts->sync_offset = targ_info->goal.offset; + else + cts->sync_offset = targ_info->user.offset; + } + + if (cts->sync_offset > AMD_MAX_SYNC_OFFSET) + cts->sync_offset = AMD_MAX_SYNC_OFFSET; + + if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) == 0) { + if (update_type & AMD_TRANS_GOAL) + cts->sync_period = targ_info->goal.period; + else + cts->sync_period = targ_info->user.period; + } + + last_entry = sizeof(tinfo_sync_period) - 1; + if ((cts->sync_period != 0) + && (cts->sync_period < tinfo_sync_period[0])) + cts->sync_period = tinfo_sync_period[0]; + if (cts->sync_period > tinfo_sync_period[last_entry]) + cts->sync_period = 0; + if (cts->sync_offset == 0) + cts->sync_period = 0; + + if ((update_type & AMD_TRANS_USER) != 0) { + targ_info->user.period = cts->sync_period; + targ_info->user.offset = cts->sync_offset; + } + if ((update_type & AMD_TRANS_GOAL) != 0) { + targ_info->goal.period = cts->sync_period; + targ_info->goal.offset = cts->sync_offset; + } + splx(intflag); + pccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(pccb); + break; + } + case XPT_CALC_GEOMETRY: + { + struct ccb_calc_geometry *ccg; + u_int32_t size_mb; + u_int32_t secs_per_cylinder; + int extended; + + ccg = &pccb->ccg; + size_mb = ccg->volume_size/((1024L * 1024L)/ccg->block_size); + extended = (amd->eepromBuf[EE_MODE2] & GREATER_1G) != 0; + + if (size_mb > 1024 && extended) { + ccg->heads = 255; + ccg->secs_per_track = 63; + } else { + ccg->heads = 64; + ccg->secs_per_track = 32; + } + secs_per_cylinder = ccg->heads * ccg->secs_per_track; + ccg->cylinders = ccg->volume_size / secs_per_cylinder; + pccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(pccb); + break; + } + default: + pccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(pccb); + break; + } +} + +static void +amd_poll(struct cam_sim * psim) +{ + amd_intr(cam_sim_softc(psim)); +} + +static u_int8_t * +phystovirt(struct amd_srb * pSRB, u_int32_t xferCnt) +{ + int dataPtr; + struct ccb_scsiio *pcsio; + u_int8_t i; + struct amd_sg * pseg; + + dataPtr = 0; + pcsio = &pSRB->pccb->csio; + + dataPtr = (int) pcsio->data_ptr; + pseg = pSRB->SGsegment; + for (i = 0; i < pSRB->SGIndex; i++) { + dataPtr += (int) pseg->SGXLen; + pseg++; + } + dataPtr += (int) xferCnt; + return ((u_int8_t *) dataPtr); +} + +static void +ResetDevParam(struct amd_softc * amd) +{ + u_int target; + + for (target = 0; target <= amd->max_id; target++) { + if (amd->AdaptSCSIID != target) { + amdsetsync(amd, target, /*clockrate*/0, + /*period*/0, /*offset*/0, AMD_TRANS_CUR); + } + } +} + +static void +amdcompletematch(struct amd_softc *amd, target_id_t target, lun_id_t lun, + u_int tag, struct srb_queue *queue, cam_status status) +{ + struct amd_srb *srb; + struct amd_srb *next_srb; + + for (srb = TAILQ_FIRST(queue); srb != NULL; srb = next_srb) { + union ccb *ccb; + + next_srb = TAILQ_NEXT(srb, links); + if (srb->pccb->ccb_h.target_id != target + && target != CAM_TARGET_WILDCARD) + continue; + + if (srb->pccb->ccb_h.target_lun != lun + && lun != CAM_LUN_WILDCARD) + continue; + + if (srb->TagNumber != tag + && tag != AMD_TAG_WILDCARD) + continue; + + ccb = srb->pccb; + TAILQ_REMOVE(queue, srb, links); + TAILQ_INSERT_HEAD(&amd->free_srbs, srb, links); + if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0 + && (status & CAM_DEV_QFRZN) != 0) + xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); + ccb->ccb_h.status = status; + xpt_done(ccb); + } + +} + +static void +amdsetsync(struct amd_softc *amd, u_int target, u_int clockrate, + u_int period, u_int offset, u_int type) +{ + struct amd_target_info *tinfo; + u_int old_period; + u_int old_offset; + + tinfo = &amd->tinfo[target]; + old_period = tinfo->current.period; + old_offset = tinfo->current.offset; + if ((type & AMD_TRANS_CUR) != 0 + && (old_period != period || old_offset != offset)) { + struct cam_path *path; + + tinfo->current.period = period; + tinfo->current.offset = offset; + tinfo->sync_period_reg = clockrate; + tinfo->sync_offset_reg = offset; + tinfo->CtrlR3 &= ~FAST_SCSI; + tinfo->CtrlR4 &= ~EATER_25NS; + if (clockrate > 7) + tinfo->CtrlR4 |= EATER_25NS; + else + tinfo->CtrlR3 |= FAST_SCSI; + + if ((type & AMD_TRANS_ACTIVE) == AMD_TRANS_ACTIVE) { + amd_write8(amd, SYNCPERIOREG, tinfo->sync_period_reg); + amd_write8(amd, SYNCOFFREG, tinfo->sync_offset_reg); + amd_write8(amd, CNTLREG3, tinfo->CtrlR3); + amd_write8(amd, CNTLREG4, tinfo->CtrlR4); + } + /* If possible, update the XPT's notion of our transfer rate */ + if (xpt_create_path(&path, /*periph*/NULL, + cam_sim_path(amd->psim), target, + CAM_LUN_WILDCARD) == CAM_REQ_CMP) { + struct ccb_trans_settings neg; + + xpt_setup_ccb(&neg.ccb_h, path, /*priority*/1); + neg.sync_period = period; + neg.sync_offset = offset; + neg.valid = CCB_TRANS_SYNC_RATE_VALID + | CCB_TRANS_SYNC_OFFSET_VALID; + xpt_async(AC_TRANSFER_NEG, path, &neg); + xpt_free_path(path); + } + } + if ((type & AMD_TRANS_GOAL) != 0) { + tinfo->goal.period = period; + tinfo->goal.offset = offset; + } + + if ((type & AMD_TRANS_USER) != 0) { + tinfo->user.period = period; + tinfo->user.offset = offset; + } +} + +static void +amdsettags(struct amd_softc *amd, u_int target, int tagenb) +{ + panic("Implement me!\n"); +} + + +#if 0 +/* + ********************************************************************** + * Function : amd_reset (struct amd_softc * amd) + * Purpose : perform a hard reset on the SCSI bus( and AMD chip). + * Inputs : cmd - command which caused the SCSI RESET + ********************************************************************** + */ +static void +amd_reset(struct amd_softc * amd) +{ + int intflag; + u_int8_t bval; + u_int16_t i; + + +#ifdef AMD_DEBUG0 + printf("DC390: RESET"); +#endif + + intflag = splcam(); + bval = amd_read8(amd, CNTLREG1); + bval |= DIS_INT_ON_SCSI_RST; + amd_write8(amd, CNTLREG1, bval); /* disable interrupt */ + amd_ResetSCSIBus(amd); + + for (i = 0; i < 500; i++) { + DELAY(1000); + } + + bval = amd_read8(amd, CNTLREG1); + bval &= ~DIS_INT_ON_SCSI_RST; + amd_write8(amd, CNTLREG1, bval); /* re-enable interrupt */ + + amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD); + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + + ResetDevParam(amd); + amdcompletematch(amd, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD, + AMD_TAG_WILDCARD, &amd->running_srbs, + CAM_DEV_QFRZN|CAM_SCSI_BUS_RESET); + amdcompletematch(amd, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD, + AMD_TAG_WILDCARD, &amd->waiting_srbs, + CAM_DEV_QFRZN|CAM_SCSI_BUS_RESET); + amd->active_srb = NULL; + amd->ACBFlag = 0; + splx(intflag); + return; +} + +void +amd_timeout(void *arg1) +{ + struct amd_srb * pSRB; + + pSRB = (struct amd_srb *) arg1; +} +#endif + +static int +amdstart(struct amd_softc *amd, struct amd_srb *pSRB) +{ + union ccb *pccb; + struct ccb_scsiio *pcsio; + struct amd_target_info *targ_info; + u_int identify_msg; + u_int command; + u_int target; + u_int lun; + int tagged; + + pccb = pSRB->pccb; + pcsio = &pccb->csio; + target = pccb->ccb_h.target_id; + lun = pccb->ccb_h.target_lun; + targ_info = &amd->tinfo[target]; + + amd_clear_msg_state(amd); + amd_write8(amd, SCSIDESTIDREG, target); + amd_write8(amd, SYNCPERIOREG, targ_info->sync_period_reg); + amd_write8(amd, SYNCOFFREG, targ_info->sync_offset_reg); + amd_write8(amd, CNTLREG1, targ_info->CtrlR1); + amd_write8(amd, CNTLREG3, targ_info->CtrlR3); + amd_write8(amd, CNTLREG4, targ_info->CtrlR4); + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + + identify_msg = MSG_IDENTIFYFLAG | lun; + if ((targ_info->disc_tag & AMD_CUR_DISCENB) != 0 + && (pccb->ccb_h.flags & CAM_DIS_DISCONNECT) == 0 + && (pSRB->CmdBlock[0] != REQUEST_SENSE) + && (pSRB->SRBFlag & AUTO_REQSENSE) == 0) + identify_msg |= MSG_IDENTIFY_DISCFLAG; + + amd_write8(amd, SCSIFIFOREG, identify_msg); + tagged = 0; + if ((targ_info->disc_tag & AMD_CUR_TAGENB) == 0 + || (identify_msg & MSG_IDENTIFY_DISCFLAG) == 0) + pccb->ccb_h.flags &= ~CAM_TAG_ACTION_VALID; + if (targ_info->current.period != targ_info->goal.period + || targ_info->current.offset != targ_info->goal.offset) { + command = SEL_W_ATN_STOP; + amdconstructsdtr(amd, targ_info->goal.period, + targ_info->goal.offset); + } else if ((pccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0) { + command = SEL_W_ATN2; + pSRB->SRBState = SRB_START; + amd_write8(amd, SCSIFIFOREG, pcsio->tag_action); + amd_write8(amd, SCSIFIFOREG, pSRB->TagNumber); + tagged++; + } else { + command = SEL_W_ATN; + pSRB->SRBState = SRB_START; + } + if (command != SEL_W_ATN_STOP) + amdsetupcommand(amd, pSRB); + + if (amd_read8(amd, SCSISTATREG) & INTERRUPT) { + pSRB->SRBState = SRB_READY; + return (1); + } else { + amd->last_phase = SCSI_ARBITRATING; + amd_write8(amd, SCSICMDREG, command); + amd->active_srb = pSRB; + amd->cur_target = target; + amd->cur_lun = lun; + return (0); + } +} + +/* + * Catch an interrupt from the adapter. + * Process pending device interrupts. + */ +static void +amd_intr(void *arg) +{ + struct amd_softc *amd; + struct amd_srb *pSRB; + u_int internstat = 0; + u_int scsistat; + u_int intstat; + + amd = (struct amd_softc *)arg; + + if (amd == NULL) { +#ifdef AMD_DEBUG0 + printf("amd_intr: amd NULL return......"); +#endif + return; + } + + scsistat = amd_read8(amd, SCSISTATREG); + if (!(scsistat & INTERRUPT)) { +#ifdef AMD_DEBUG0 + printf("amd_intr: scsistat = NULL ,return......"); +#endif + return; + } +#ifdef AMD_DEBUG_SCSI_PHASE + printf("scsistat=%2x,", scsistat); +#endif + + internstat = amd_read8(amd, INTERNSTATREG); + intstat = amd_read8(amd, INTSTATREG); + +#ifdef AMD_DEBUG_SCSI_PHASE + printf("intstat=%2x,", intstat); +#endif + + if (intstat & DISCONNECTED) { + amd_Disconnect(amd); + return; + } + if (intstat & RESELECTED) { + amd_Reselect(amd); + return; + } + if (intstat & INVALID_CMD) { + amd_InvalidCmd(amd); + return; + } + if (intstat & SCSI_RESET_) { + amd_ScsiRstDetect(amd); + return; + } + if (intstat & (SUCCESSFUL_OP + SERVICE_REQUEST)) { + pSRB = amd->active_srb; + /* + * Run our state engine. First perform + * post processing for the last phase we + * were in, followed by any processing + * required to handle the current phase. + */ + scsistat = + amd_SCSI_phase0[amd->last_phase](amd, pSRB, scsistat); + amd->last_phase = scsistat & SCSI_PHASE_MASK; + (void)amd_SCSI_phase1[amd->last_phase](amd, pSRB, scsistat); + } +} + +static u_int +amd_DataOutPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + struct amd_sg *psgl; + u_int32_t ResidCnt, xferCnt; + + if (!(pSRB->SRBState & SRB_XFERPAD)) { + if (scsistat & PARITY_ERR) { + pSRB->SRBStatus |= PARITY_ERROR; + } + if (scsistat & COUNT_2_ZERO) { + while ((amd_read8(amd, DMA_Status)&DMA_XFER_DONE) == 0) + ; + pSRB->TotalXferredLen += pSRB->SGToBeXferLen; + pSRB->SGIndex++; + if (pSRB->SGIndex < pSRB->SGcount) { + pSRB->pSGlist++; + psgl = pSRB->pSGlist; + pSRB->SGPhysAddr = psgl->SGXPtr; + pSRB->SGToBeXferLen = psgl->SGXLen; + } else { + pSRB->SGToBeXferLen = 0; + } + } else { + ResidCnt = amd_read8(amd, CURRENTFIFOREG) & 0x1f; + ResidCnt += amd_read8(amd, CTCREG_LOW) + | (amd_read8(amd, CTCREG_MID) << 8) + | (amd_read8(amd, CURTXTCNTREG) << 16); + + xferCnt = pSRB->SGToBeXferLen - ResidCnt; + pSRB->SGPhysAddr += xferCnt; + pSRB->TotalXferredLen += xferCnt; + pSRB->SGToBeXferLen = ResidCnt; + } + } + amd_write8(amd, DMA_Cmd, WRITE_DIRECTION | DMA_IDLE_CMD); + return (scsistat); +} + +static u_int +amd_DataInPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + u_int8_t bval; + u_int16_t i, residual; + struct amd_sg *psgl; + u_int32_t ResidCnt, xferCnt; + u_int8_t * ptr; + + if (!(pSRB->SRBState & SRB_XFERPAD)) { + if (scsistat & PARITY_ERR) { + pSRB->SRBStatus |= PARITY_ERROR; + } + if (scsistat & COUNT_2_ZERO) { + while (1) { + bval = amd_read8(amd, DMA_Status); + if ((bval & DMA_XFER_DONE) != 0) + break; + } + amd_write8(amd, DMA_Cmd, READ_DIRECTION|DMA_IDLE_CMD); + + pSRB->TotalXferredLen += pSRB->SGToBeXferLen; + pSRB->SGIndex++; + if (pSRB->SGIndex < pSRB->SGcount) { + pSRB->pSGlist++; + psgl = pSRB->pSGlist; + pSRB->SGPhysAddr = psgl->SGXPtr; + pSRB->SGToBeXferLen = psgl->SGXLen; + } else { + pSRB->SGToBeXferLen = 0; + } + } else { /* phase changed */ + residual = 0; + bval = amd_read8(amd, CURRENTFIFOREG); + while (bval & 0x1f) { + if ((bval & 0x1f) == 1) { + for (i = 0; i < 0x100; i++) { + bval = amd_read8(amd, CURRENTFIFOREG); + if (!(bval & 0x1f)) { + goto din_1; + } else if (i == 0x0ff) { + residual = 1; + goto din_1; + } + } + } else { + bval = amd_read8(amd, CURRENTFIFOREG); + } + } + din_1: + amd_write8(amd, DMA_Cmd, READ_DIRECTION|DMA_BLAST_CMD); + for (i = 0; i < 0x8000; i++) { + if ((amd_read8(amd, DMA_Status)&BLAST_COMPLETE)) + break; + } + amd_write8(amd, DMA_Cmd, READ_DIRECTION|DMA_IDLE_CMD); + + ResidCnt = amd_read8(amd, CTCREG_LOW) + | (amd_read8(amd, CTCREG_MID) << 8) + | (amd_read8(amd, CURTXTCNTREG) << 16); + xferCnt = pSRB->SGToBeXferLen - ResidCnt; + pSRB->SGPhysAddr += xferCnt; + pSRB->TotalXferredLen += xferCnt; + pSRB->SGToBeXferLen = ResidCnt; + if (residual) { + /* get residual byte */ + bval = amd_read8(amd, SCSIFIFOREG); + ptr = phystovirt(pSRB, xferCnt); + *ptr = bval; + pSRB->SGPhysAddr++; + pSRB->TotalXferredLen++; + pSRB->SGToBeXferLen--; + } + } + } + return (scsistat); +} + +static u_int +amd_StatusPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + pSRB->TargetStatus = amd_read8(amd, SCSIFIFOREG); + /* get message */ + pSRB->EndMessage = amd_read8(amd, SCSIFIFOREG); + pSRB->SRBState = SRB_COMPLETED; + amd_write8(amd, SCSICMDREG, MSG_ACCEPTED_CMD); + return (SCSI_NOP0); +} + +static u_int +amd_MsgOutPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + if (pSRB->SRBState & (SRB_UNEXPECT_RESEL + SRB_ABORT_SENT)) { + scsistat = SCSI_NOP0; + } + return (scsistat); +} + +static u_int +amd_MsgInPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + int done; + + amd->msgin_buf[amd->msgin_index] = amd_read8(amd, SCSIFIFOREG); + + done = amdparsemsg(amd); + if (done) + amd->msgin_index = 0; + else + amd->msgin_index++; + return (SCSI_NOP0); +} + +static int +amdparsemsg(struct amd_softc *amd) +{ + struct amd_target_info *targ_info; + int reject; + int done; + int response; + + done = FALSE; + response = FALSE; + reject = FALSE; + + targ_info = &amd->tinfo[amd->cur_target]; + + /* + * Parse as much of the message as is availible, + * rejecting it if we don't support it. When + * the entire message is availible and has been + * handled, return TRUE indicating that we have + * parsed an entire message. + */ + switch (amd->msgin_buf[0]) { + case MSG_DISCONNECT: + amd->active_srb->SRBState = SRB_DISCONNECT; + amd->disc_count[amd->cur_target][amd->cur_lun]++; + done = TRUE; + break; + case MSG_SIMPLE_Q_TAG: + { + struct amd_srb *disc_srb; + + if (amd->msgin_index < 1) + break; + disc_srb = &amd->SRB_array[amd->msgin_buf[1]]; + if (amd->active_srb != NULL + || disc_srb->SRBState != SRB_DISCONNECT + || disc_srb->pccb->ccb_h.target_id != amd->cur_target + || disc_srb->pccb->ccb_h.target_lun != amd->cur_lun) { + printf("amd%d: Unexpected tagged reselection " + "for target %d, Issuing Abort\n", amd->unit, + amd->cur_target); + amd->msgout_buf[0] = MSG_ABORT; + amd->msgout_len = 1; + response = TRUE; + break; + } + amd->active_srb = disc_srb; + amd->disc_count[amd->cur_target][amd->cur_lun]--; + done = TRUE; + break; + } + case MSG_MESSAGE_REJECT: + response = amdhandlemsgreject(amd); + if (response == FALSE) + amd_write8(amd, SCSICMDREG, RESET_ATN_CMD); + /* FALLTHROUGH */ + case MSG_NOOP: + done = TRUE; + break; + case MSG_EXTENDED: + { + u_int clockrate; + u_int period; + u_int offset; + u_int saved_offset; + + /* Wait for enough of the message to begin validation */ + if (amd->msgin_index < 1) + break; + if (amd->msgin_buf[1] != MSG_EXT_SDTR_LEN) { + reject = TRUE; + break; + } + + /* Wait for opcode */ + if (amd->msgin_index < 2) + break; + + if (amd->msgin_buf[2] != MSG_EXT_SDTR) { + reject = TRUE; + break; + } + + /* + * Wait until we have both args before validating + * and acting on this message. + * + * Add one to MSG_EXT_SDTR_LEN to account for + * the extended message preamble. + */ + if (amd->msgin_index < (MSG_EXT_SDTR_LEN + 1)) + break; + + period = amd->msgin_buf[3]; + saved_offset = offset = amd->msgin_buf[4]; + clockrate = amdfindclockrate(amd, &period); + if (offset > AMD_MAX_SYNC_OFFSET) + offset = AMD_MAX_SYNC_OFFSET; + if (period == 0 || offset == 0) { + offset = 0; + period = 0; + clockrate = 0; + } + amdsetsync(amd, amd->cur_target, clockrate, period, offset, + AMD_TRANS_ACTIVE|AMD_TRANS_GOAL); + + /* + * See if we initiated Sync Negotiation + * and didn't have to fall down to async + * transfers. + */ + if (amdsentmsg(amd, MSG_EXT_SDTR, /*full*/TRUE)) { + /* We started it */ + if (saved_offset != offset) { + /* Went too low - force async */ + reject = TRUE; + } + } else { + /* + * Send our own SDTR in reply + */ + if (bootverbose) + printf("Sending SDTR!\n"); + amd->msgout_index = 0; + amd->msgout_len = 0; + amdconstructsdtr(amd, period, offset); + amd->msgout_index = 0; + response = TRUE; + } + done = TRUE; + break; + } + case MSG_SAVEDATAPOINTER: + case MSG_RESTOREPOINTERS: + /* XXX Implement!!! */ + done = TRUE; + break; + default: + reject = TRUE; + break; + } + + if (reject) { + amd->msgout_index = 0; + amd->msgout_len = 1; + amd->msgout_buf[0] = MSG_MESSAGE_REJECT; + done = TRUE; + response = TRUE; + } + + if (response) + amd_write8(amd, SCSICMDREG, SET_ATN_CMD); + + if (done && !response) + /* Clear the outgoing message buffer */ + amd->msgout_len = 0; + + /* Drop Ack */ + amd_write8(amd, SCSICMDREG, MSG_ACCEPTED_CMD); + + return (done); +} + +static u_int +amdfindclockrate(struct amd_softc *amd, u_int *period) +{ + u_int i; + u_int clockrate; + + for (i = 0; i < sizeof(tinfo_sync_period); i++) { + u_int8_t *table_entry; + + table_entry = &tinfo_sync_period[i]; + if (*period <= *table_entry) { + /* + * When responding to a target that requests + * sync, the requested rate may fall between + * two rates that we can output, but still be + * a rate that we can receive. Because of this, + * we want to respond to the target with + * the same rate that it sent to us even + * if the period we use to send data to it + * is lower. Only lower the response period + * if we must. + */ + if (i == 0) { + *period = *table_entry; + } + break; + } + } + + if (i == sizeof(tinfo_sync_period)) { + /* Too slow for us. Use asnyc transfers. */ + *period = 0; + clockrate = 0; + } else + clockrate = i + 4; + + return (clockrate); +} + +/* + * See if we sent a particular extended message to the target. + * If "full" is true, the target saw the full message. + * If "full" is false, the target saw at least the first + * byte of the message. + */ +static int +amdsentmsg(struct amd_softc *amd, u_int msgtype, int full) +{ + int found; + int index; + + found = FALSE; + index = 0; + + while (index < amd->msgout_len) { + if ((amd->msgout_buf[index] & MSG_IDENTIFYFLAG) != 0 + || amd->msgout_buf[index] == MSG_MESSAGE_REJECT) + index++; + else if (amd->msgout_buf[index] >= MSG_SIMPLE_Q_TAG + && amd->msgout_buf[index] < MSG_IGN_WIDE_RESIDUE) { + /* Skip tag type and tag id */ + index += 2; + } else if (amd->msgout_buf[index] == MSG_EXTENDED) { + /* Found a candidate */ + if (amd->msgout_buf[index+2] == msgtype) { + u_int end_index; + + end_index = index + 1 + + amd->msgout_buf[index + 1]; + if (full) { + if (amd->msgout_index > end_index) + found = TRUE; + } else if (amd->msgout_index > index) + found = TRUE; + } + break; + } else { + panic("amdsentmsg: Inconsistent msg buffer"); + } + } + return (found); +} + +static void +amdconstructsdtr(struct amd_softc *amd, u_int period, u_int offset) +{ + amd->msgout_buf[amd->msgout_index++] = MSG_EXTENDED; + amd->msgout_buf[amd->msgout_index++] = MSG_EXT_SDTR_LEN; + amd->msgout_buf[amd->msgout_index++] = MSG_EXT_SDTR; + amd->msgout_buf[amd->msgout_index++] = period; + amd->msgout_buf[amd->msgout_index++] = offset; + amd->msgout_len += 5; +} + +static int +amdhandlemsgreject(struct amd_softc *amd) +{ + /* + * If we had an outstanding SDTR for this + * target, this is a signal that the target + * is refusing negotiation. Also watch out + * for rejected tag messages. + */ + struct amd_srb *srb; + struct amd_target_info *targ_info; + int response = FALSE; + + srb = amd->active_srb; + targ_info = &amd->tinfo[amd->cur_target]; + if (amdsentmsg(amd, MSG_EXT_SDTR, /*full*/FALSE)) { + /* note asynch xfers and clear flag */ + amdsetsync(amd, amd->cur_target, /*clockrate*/0, + /*period*/0, /*offset*/0, + AMD_TRANS_ACTIVE|AMD_TRANS_GOAL); + printf("amd%d:%d: refuses synchronous negotiation. " + "Using asynchronous transfers\n", + amd->unit, amd->cur_target); + } else if ((srb != NULL) + && (srb->pccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0) { + struct ccb_trans_settings neg; + + printf("amd%d:%d: refuses tagged commands. Performing " + "non-tagged I/O\n", amd->unit, amd->cur_target); + + amdsettags(amd, amd->cur_target, FALSE); + neg.flags = 0; + neg.valid = CCB_TRANS_TQ_VALID; + xpt_setup_ccb(&neg.ccb_h, srb->pccb->ccb_h.path, /*priority*/1); + xpt_async(AC_TRANSFER_NEG, srb->pccb->ccb_h.path, &neg); + + /* + * Resend the identify for this CCB as the target + * may believe that the selection is invalid otherwise. + */ + if (amd->msgout_len != 0) + bcopy(&amd->msgout_buf[0], &amd->msgout_buf[1], + amd->msgout_len); + amd->msgout_buf[0] = MSG_IDENTIFYFLAG + | srb->pccb->ccb_h.target_lun; + amd->msgout_len++; + if ((targ_info->disc_tag & AMD_CUR_DISCENB) != 0 + && (srb->pccb->ccb_h.flags & CAM_DIS_DISCONNECT) == 0) + amd->msgout_buf[0] |= MSG_IDENTIFY_DISCFLAG; + + srb->pccb->ccb_h.flags &= ~CAM_TAG_ACTION_VALID; + + /* + * Requeue all tagged commands for this target + * currently in our posession so they can be + * converted to untagged commands. + */ + amdcompletematch(amd, amd->cur_target, amd->cur_lun, + AMD_TAG_WILDCARD, &amd->waiting_srbs, + CAM_DEV_QFRZN|CAM_REQUEUE_REQ); + } else { + /* + * Otherwise, we ignore it. + */ + printf("amd%d:%d: Message reject received -- ignored\n", + amd->unit, amd->cur_target); + } + return (response); +} + +#if 0 + if (!(pSRB->SRBState & SRB_MSGIN_MULTI)) { + if (bval == MSG_DISCONNECT) { + pSRB->SRBState = SRB_DISCONNECT; + } else if (bval == MSG_SAVEDATAPOINTER) { + goto min6; + } else if ((bval == MSG_EXTENDED) + || ((bval >= MSG_SIMPLE_Q_TAG) + && (bval <= MSG_ORDERED_Q_TAG))) { + pSRB->SRBState |= SRB_MSGIN_MULTI; + pSRB->MsgInBuf[0] = bval; + pSRB->MsgCnt = 1; + pSRB->pMsgPtr = &pSRB->MsgInBuf[1]; + } else if (bval == MSG_MESSAGE_REJECT) { + amd_write8(amd, SCSICMDREG, RESET_ATN_CMD); + + if (pSRB->SRBState & DO_SYNC_NEGO) { + goto set_async; + } + } else if (bval == MSG_RESTOREPOINTERS) { + goto min6; + } else { + goto min6; + } + } else { /* minx: */ + *pSRB->pMsgPtr = bval; + pSRB->MsgCnt++; + pSRB->pMsgPtr++; + if ((pSRB->MsgInBuf[0] >= MSG_SIMPLE_Q_TAG) + && (pSRB->MsgInBuf[0] <= MSG_ORDERED_Q_TAG)) { + if (pSRB->MsgCnt == 2) { + pSRB->SRBState = 0; + pSRB = &amd->SRB_array[pSRB->MsgInBuf[1]]; + if (pSRB->SRBState & SRB_DISCONNECT) == 0) { + pSRB = amd->pTmpSRB; + pSRB->SRBState = SRB_UNEXPECT_RESEL; + pDCB->pActiveSRB = pSRB; + pSRB->MsgOutBuf[0] = MSG_ABORT_TAG; + EnableMsgOut2(amd, pSRB); + } else { + if (pDCB->DCBFlag & ABORT_DEV_) { + pSRB->SRBState = SRB_ABORT_SENT; + EnableMsgOut1(amd, pSRB); + } + pDCB->pActiveSRB = pSRB; + pSRB->SRBState = SRB_DATA_XFER; + } + } + } else if ((pSRB->MsgInBuf[0] == MSG_EXTENDED) + && (pSRB->MsgCnt == 5)) { + pSRB->SRBState &= ~(SRB_MSGIN_MULTI + DO_SYNC_NEGO); + if ((pSRB->MsgInBuf[1] != 3) + || (pSRB->MsgInBuf[2] != 1)) { /* reject_msg: */ + pSRB->MsgCnt = 1; + pSRB->MsgInBuf[0] = MSG_MESSAGE_REJECT; + amd_write8(amd, SCSICMDREG, SET_ATN_CMD); + } else if (!(pSRB->MsgInBuf[3]) + || !(pSRB->MsgInBuf[4])) { + set_async: /* set async */ + + pDCB = pSRB->pSRBDCB; + /* disable sync & sync nego */ + pDCB->SyncMode &= ~(SYNC_ENABLE|SYNC_NEGO_DONE); + pDCB->SyncPeriod = 0; + pDCB->SyncOffset = 0; + + pDCB->tinfo.goal.period = 0; + pDCB->tinfo.goal.offset = 0; + + pDCB->tinfo.current.period = 0; + pDCB->tinfo.current.offset = 0; + pDCB->tinfo.current.width = + MSG_EXT_WDTR_BUS_8_BIT; + + pDCB->CtrlR3 = FAST_CLK; /* non_fast */ + pDCB->CtrlR4 &= 0x3f; + pDCB->CtrlR4 |= EATER_25NS; + goto re_prog; + } else {/* set sync */ + + pDCB = pSRB->pSRBDCB; + /* enable sync & sync nego */ + pDCB->SyncMode |= SYNC_ENABLE|SYNC_NEGO_DONE; + + /* set sync offset */ + pDCB->SyncOffset &= 0x0f0; + pDCB->SyncOffset |= pSRB->MsgInBuf[4]; + + /* set sync period */ + pDCB->MaxNegoPeriod = pSRB->MsgInBuf[3]; + + wval = (u_int16_t) pSRB->MsgInBuf[3]; + wval = wval << 2; + wval--; + wval1 = wval / 25; + if ((wval1 * 25) != wval) { + wval1++; + } + bval = FAST_CLK|FAST_SCSI; + pDCB->CtrlR4 &= 0x3f; + if (wval1 >= 8) { + /* Fast SCSI */ + wval1--; + bval = FAST_CLK; + pDCB->CtrlR4 |= EATER_25NS; + } + pDCB->CtrlR3 = bval; + pDCB->SyncPeriod = (u_int8_t) wval1; + + pDCB->tinfo.goal.period = + tinfo_sync_period[pDCB->SyncPeriod - 4]; + pDCB->tinfo.goal.offset = pDCB->SyncOffset; + pDCB->tinfo.current.period = + tinfo_sync_period[pDCB->SyncPeriod - 4];; + pDCB->tinfo.current.offset = pDCB->SyncOffset; + + /* + * program SCSI control register + */ + re_prog: + amd_write8(amd, SYNCPERIOREG, pDCB->SyncPeriod); + amd_write8(amd, SYNCOFFREG, pDCB->SyncOffset); + amd_write8(amd, CNTLREG3, pDCB->CtrlR3); + amd_write8(amd, CNTLREG4, pDCB->CtrlR4); + } + } + } +min6: + amd_write8(amd, SCSICMDREG, MSG_ACCEPTED_CMD); + return (SCSI_NOP0); +} +#endif + +static u_int +amd_DataOutPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + DataIO_Comm(amd, pSRB, WRITE_DIRECTION); + return (scsistat); +} + +static u_int +amd_DataInPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + DataIO_Comm(amd, pSRB, READ_DIRECTION); + return (scsistat); +} + +static void +DataIO_Comm(struct amd_softc *amd, struct amd_srb *pSRB, u_int ioDir) +{ + struct amd_sg * psgl; + u_int32_t lval; + + if (pSRB->SGIndex < pSRB->SGcount) { + amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD|ioDir);/* |EN_DMA_INT */ + + if (!pSRB->SGToBeXferLen) { + psgl = pSRB->pSGlist; + pSRB->SGPhysAddr = psgl->SGXPtr; + pSRB->SGToBeXferLen = psgl->SGXLen; + } + lval = pSRB->SGToBeXferLen; + amd_write8(amd, CTCREG_LOW, lval); + amd_write8(amd, CTCREG_MID, lval >> 8); + amd_write8(amd, CURTXTCNTREG, lval >> 16); + + amd_write32(amd, DMA_XferCnt, pSRB->SGToBeXferLen); + + amd_write32(amd, DMA_XferAddr, pSRB->SGPhysAddr); + + pSRB->SRBState = SRB_DATA_XFER; + + amd_write8(amd, SCSICMDREG, DMA_COMMAND|INFO_XFER_CMD); + + amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD|ioDir); /* |EN_DMA_INT */ + + amd_write8(amd, DMA_Cmd, DMA_START_CMD|ioDir);/* |EN_DMA_INT */ + } else { /* xfer pad */ + if (pSRB->SGcount) { + pSRB->AdaptStatus = H_OVER_UNDER_RUN; + pSRB->SRBStatus |= OVER_RUN; + } + amd_write8(amd, CTCREG_LOW, 0); + amd_write8(amd, CTCREG_MID, 0); + amd_write8(amd, CURTXTCNTREG, 0); + + pSRB->SRBState |= SRB_XFERPAD; + amd_write8(amd, SCSICMDREG, DMA_COMMAND|XFER_PAD_BYTE); + } +} + +static u_int +amd_CommandPhase1(struct amd_softc *amd, struct amd_srb *srb, u_int scsistat) +{ + amd_write8(amd, SCSICMDREG, RESET_ATN_CMD); + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + + amdsetupcommand(amd, srb); + + srb->SRBState = SRB_COMMAND; + amd_write8(amd, SCSICMDREG, INFO_XFER_CMD); + return (scsistat); +} + +static u_int +amd_StatusPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + pSRB->SRBState = SRB_STATUS; + amd_write8(amd, SCSICMDREG, INITIATOR_CMD_CMPLTE); + return (scsistat); +} + +static u_int +amd_MsgOutPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + + if (amd->msgout_len == 0) { + amd->msgout_buf[0] = MSG_NOOP; + amd->msgout_len = 1; + } + amd_write8_multi(amd, SCSIFIFOREG, amd->msgout_buf, amd->msgout_len); + amd_write8(amd, SCSICMDREG, INFO_XFER_CMD); + return (scsistat); +} + +static u_int +amd_MsgInPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + amd_write8(amd, SCSICMDREG, INFO_XFER_CMD); + return (scsistat); +} + +static u_int +amd_NopPhase(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + return (scsistat); +} + +static void +amd_Disconnect(struct amd_softc * amd) +{ + struct amd_srb *srb; + int target; + int lun; + + srb = amd->active_srb; + amd->active_srb = NULL; + amd->last_phase = SCSI_BUS_FREE; + amd_write8(amd, SCSICMDREG, EN_SEL_RESEL); + target = amd->cur_target; + lun = amd->cur_lun; + + if (srb == NULL) { + /* Invalid reselection */ + amdrunwaiting(amd); + } else if (srb->SRBState & SRB_ABORT_SENT) { + /* Clean up and done this srb */ +#if 0 + while (( = TAILQ_FIRST(&amd->running_srbs)) != NULL) { + /* XXX What about "done'ing" these srbs??? */ + if (pSRB->pSRBDCB == pDCB) { + TAILQ_REMOVE(&amd->running_srbs, pSRB, links); + TAILQ_INSERT_HEAD(&amd->free_srbs, pSRB, links); + } + } + amdrunwaiting(amd); +#endif + } else { + if ((srb->SRBState & (SRB_START | SRB_MSGOUT)) + || !(srb->SRBState & (SRB_DISCONNECT | SRB_COMPLETED))) { + srb->TargetStatus = AMD_SCSI_STAT_SEL_TIMEOUT; + goto disc1; + } else if (srb->SRBState & SRB_DISCONNECT) { + if (!(srb->pccb->ccb_h.flags & CAM_TAG_ACTION_VALID)) + amd->untagged_srbs[target][lun] = srb; + amdrunwaiting(amd); + } else if (srb->SRBState & SRB_COMPLETED) { + disc1: + srb->SRBState = SRB_FREE; + SRBdone(amd, srb); + } + } + return; +} + +static void +amd_Reselect(struct amd_softc *amd) +{ + struct amd_target_info *tinfo; + u_int16_t disc_count; + + amd_clear_msg_state(amd); + if (amd->active_srb != NULL) { + /* Requeue the SRB for our attempted Selection */ + TAILQ_REMOVE(&amd->running_srbs, amd->active_srb, links); + TAILQ_INSERT_HEAD(&amd->waiting_srbs, amd->active_srb, links); + amd->active_srb = NULL; + } + /* get ID */ + amd->cur_target = amd_read8(amd, SCSIFIFOREG); + amd->cur_target ^= amd->HostID_Bit; + amd->cur_target = ffs(amd->cur_target) - 1; + amd->cur_lun = amd_read8(amd, SCSIFIFOREG) & 7; + tinfo = &amd->tinfo[amd->cur_target]; + amd->active_srb = amd->untagged_srbs[amd->cur_target][amd->cur_lun]; + disc_count = amd->disc_count[amd->cur_target][amd->cur_lun]; + if (disc_count == 0) { + printf("amd%d: Unexpected reselection for target %d, " + "Issuing Abort\n", amd->unit, amd->cur_target); + amd->msgout_buf[0] = MSG_ABORT; + amd->msgout_len = 1; + amd_write8(amd, SCSICMDREG, SET_ATN_CMD); + } + if (amd->active_srb != NULL) { + amd->disc_count[amd->cur_target][amd->cur_lun]--; + amd->untagged_srbs[amd->cur_target][amd->cur_lun] = NULL; + } + + amd_write8(amd, SCSIDESTIDREG, amd->cur_target); + amd_write8(amd, SYNCPERIOREG, tinfo->sync_period_reg); + amd_write8(amd, SYNCOFFREG, tinfo->sync_offset_reg); + amd_write8(amd, CNTLREG1, tinfo->CtrlR1); + amd_write8(amd, CNTLREG3, tinfo->CtrlR3); + amd_write8(amd, CNTLREG4, tinfo->CtrlR4); + amd_write8(amd, SCSICMDREG, MSG_ACCEPTED_CMD);/* drop /ACK */ + amd->last_phase = SCSI_NOP0; +} + +static void +SRBdone(struct amd_softc *amd, struct amd_srb *pSRB) +{ + u_int8_t bval, i, status; + union ccb *pccb; + struct ccb_scsiio *pcsio; + int intflag; + struct amd_sg *ptr2; + u_int32_t swlval; + u_int target_id, target_lun; + + pccb = pSRB->pccb; + pcsio = &pccb->csio; + target_id = pSRB->pccb->ccb_h.target_id; + target_lun = pSRB->pccb->ccb_h.target_lun; + + CAM_DEBUG(pccb->ccb_h.path, CAM_DEBUG_TRACE, + ("SRBdone - TagNumber %d\n", pSRB->TagNumber)); + + if ((pccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { + bus_dmasync_op_t op; + + if ((pccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) + op = BUS_DMASYNC_POSTREAD; + else + op = BUS_DMASYNC_POSTWRITE; + bus_dmamap_sync(amd->buffer_dmat, pSRB->dmamap, op); + bus_dmamap_unload(amd->buffer_dmat, pSRB->dmamap); + } + + status = pSRB->TargetStatus; + pccb->ccb_h.status = CAM_REQ_CMP; + pccb->ccb_h.status = CAM_REQ_CMP; + if (pSRB->SRBFlag & AUTO_REQSENSE) { + pSRB->SRBFlag &= ~AUTO_REQSENSE; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = SCSI_STATUS_CHECK_COND; + + if (status == SCSI_STATUS_CHECK_COND) { + pccb->ccb_h.status = CAM_SEL_TIMEOUT; + goto ckc_e; + } + *((u_int32_t *)&(pSRB->CmdBlock[0])) = pSRB->Segment0[0]; + + pcsio->sense_resid = pcsio->sense_len + - pSRB->TotalXferredLen; + pSRB->TotalXferredLen = pSRB->Segment1[1]; + if (pSRB->TotalXferredLen) { + /* ???? */ + pcsio->resid = pcsio->dxfer_len + - pSRB->TotalXferredLen; + /* The resid field contains valid data */ + /* Flush resid bytes on complete */ + } else { + pcsio->scsi_status = SCSI_STATUS_CHECK_COND; + } + pccb->ccb_h.status = CAM_AUTOSNS_VALID|CAM_SCSI_STATUS_ERROR; + goto ckc_e; + } + if (status) { + if (status == SCSI_STATUS_CHECK_COND) { + + if ((pSRB->SGIndex < pSRB->SGcount) + && (pSRB->SGcount) && (pSRB->SGToBeXferLen)) { + bval = pSRB->SGcount; + swlval = pSRB->SGToBeXferLen; + ptr2 = pSRB->pSGlist; + ptr2++; + for (i = pSRB->SGIndex + 1; i < bval; i++) { + swlval += ptr2->SGXLen; + ptr2++; + } + /* ??????? */ + pcsio->resid = (u_int32_t) swlval; + +#ifdef AMD_DEBUG0 + printf("XferredLen=%8x,NotYetXferLen=%8x,", + pSRB->TotalXferredLen, swlval); +#endif + } + if ((pcsio->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) { +#ifdef AMD_DEBUG0 + printf("RequestSense..................\n"); +#endif + RequestSense(amd, pSRB); + return; + } + pcsio->scsi_status = SCSI_STATUS_CHECK_COND; + pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; + goto ckc_e; + } else if (status == SCSI_STATUS_QUEUE_FULL) { + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + pcsio->scsi_status = SCSI_STATUS_QUEUE_FULL; + pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; + goto ckc_e; + } else if (status == AMD_SCSI_STAT_SEL_TIMEOUT) { + pSRB->AdaptStatus = H_SEL_TIMEOUT; + pSRB->TargetStatus = 0; + + pcsio->scsi_status = AMD_SCSI_STAT_SEL_TIMEOUT; + pccb->ccb_h.status = CAM_SEL_TIMEOUT; + } else if (status == SCSI_STATUS_BUSY) { +#ifdef AMD_DEBUG0 + printf("DC390: target busy at %s %d\n", + __FILE__, __LINE__); +#endif + pcsio->scsi_status = SCSI_STATUS_BUSY; + pccb->ccb_h.status = CAM_SCSI_BUSY; + } else if (status == SCSI_STATUS_RESERV_CONFLICT) { +#ifdef AMD_DEBUG0 + printf("DC390: target reserved at %s %d\n", + __FILE__, __LINE__); +#endif + pcsio->scsi_status = SCSI_STATUS_RESERV_CONFLICT; + pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; /* XXX */ + } else { + pSRB->AdaptStatus = 0; +#ifdef AMD_DEBUG0 + printf("DC390: driver stuffup at %s %d\n", + __FILE__, __LINE__); +#endif + pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; + } + } else { + status = pSRB->AdaptStatus; + if (status & H_OVER_UNDER_RUN) { + pSRB->TargetStatus = 0; + + pccb->ccb_h.status = CAM_DATA_RUN_ERR; + } else if (pSRB->SRBStatus & PARITY_ERROR) { +#ifdef AMD_DEBUG0 + printf("DC390: driver stuffup %s %d\n", + __FILE__, __LINE__); +#endif + /* Driver failed to perform operation */ + pccb->ccb_h.status = CAM_UNCOR_PARITY; + } else { /* No error */ + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + pcsio->resid = 0; + /* there is no error, (sense is invalid) */ + } + } +ckc_e: + intflag = splcam(); + if ((pccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + /* CAM request not yet complete =>device_Q frozen */ + xpt_freeze_devq(pccb->ccb_h.path, 1); + pccb->ccb_h.status |= CAM_DEV_QFRZN; + } + TAILQ_REMOVE(&amd->running_srbs, pSRB, links); + TAILQ_INSERT_HEAD(&amd->free_srbs, pSRB, links); + amdrunwaiting(amd); + splx(intflag); + xpt_done(pccb); + +} + +static void +amd_ResetSCSIBus(struct amd_softc * amd) +{ + int intflag; + + intflag = splcam(); + amd->ACBFlag |= RESET_DEV; + amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD); + amd_write8(amd, SCSICMDREG, RST_SCSI_BUS_CMD); + splx(intflag); + return; +} + +static void +amd_ScsiRstDetect(struct amd_softc * amd) +{ + int intflag; + u_int32_t wlval; + +#ifdef AMD_DEBUG0 + printf("amd_ScsiRstDetect \n"); +#endif + + wlval = 1000; + while (--wlval) { /* delay 1 sec */ + DELAY(1000); + } + intflag = splcam(); + + amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD); + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + + if (amd->ACBFlag & RESET_DEV) { + amd->ACBFlag |= RESET_DONE; + } else { + amd->ACBFlag |= RESET_DETECT; + ResetDevParam(amd); + amdcompletematch(amd, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD, + AMD_TAG_WILDCARD, &amd->running_srbs, + CAM_DEV_QFRZN|CAM_SCSI_BUS_RESET); + amdcompletematch(amd, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD, + AMD_TAG_WILDCARD, &amd->waiting_srbs, + CAM_DEV_QFRZN|CAM_SCSI_BUS_RESET); + amd->active_srb = NULL; + amd->ACBFlag = 0; + amdrunwaiting(amd); + } + splx(intflag); + return; +} + +static void +RequestSense(struct amd_softc *amd, struct amd_srb *pSRB) +{ + union ccb *pccb; + struct ccb_scsiio *pcsio; + + pccb = pSRB->pccb; + pcsio = &pccb->csio; + + pSRB->SRBFlag |= AUTO_REQSENSE; + pSRB->Segment0[0] = *((u_int32_t *) & (pSRB->CmdBlock[0])); + pSRB->Segment0[1] = *((u_int32_t *) & (pSRB->CmdBlock[4])); + pSRB->Segment1[0] = (pSRB->ScsiCmdLen << 8) + pSRB->SGcount; + pSRB->Segment1[1] = pSRB->TotalXferredLen; + + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + + pSRB->Segmentx.SGXPtr = (u_int32_t) vtophys(&pcsio->sense_data); + pSRB->Segmentx.SGXLen = (u_int32_t) pcsio->sense_len; + + pSRB->pSGlist = &pSRB->Segmentx; + pSRB->SGcount = 1; + pSRB->SGIndex = 0; + + *((u_int32_t *) & (pSRB->CmdBlock[0])) = 0x00000003; + pSRB->CmdBlock[1] = pSRB->pccb->ccb_h.target_lun << 5; + *((u_int16_t *) & (pSRB->CmdBlock[4])) = pcsio->sense_len; + pSRB->ScsiCmdLen = 6; + + pSRB->TotalXferredLen = 0; + pSRB->SGToBeXferLen = 0; + if (amdstart(amd, pSRB) != 0) { + TAILQ_REMOVE(&amd->running_srbs, pSRB, links); + TAILQ_INSERT_HEAD(&amd->waiting_srbs, pSRB, links); + } +} + +static void +amd_InvalidCmd(struct amd_softc * amd) +{ + struct amd_srb *srb; + + srb = amd->active_srb; + if (srb->SRBState & (SRB_START|SRB_MSGOUT)) + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); +} + +void +amd_linkSRB(struct amd_softc *amd) +{ + u_int16_t count, i; + struct amd_srb *psrb; + + count = amd->SRBCount; + + for (i = 0; i < count; i++) { + psrb = (struct amd_srb *)&amd->SRB_array[i]; + psrb->TagNumber = i; + TAILQ_INSERT_TAIL(&amd->free_srbs, psrb, links); + } +} + +void +amd_EnDisableCE(struct amd_softc *amd, int mode, int *regval) +{ + if (mode == ENABLE_CE) { + *regval = 0xc0; + } else { + *regval = 0x80; + } + pci_write_config(amd->dev, *regval, 0, /*bytes*/1); + if (mode == DISABLE_CE) { + pci_write_config(amd->dev, *regval, 0, /*bytes*/1); + } + DELAY(160); +} + +void +amd_EEpromOutDI(struct amd_softc *amd, int *regval, int Carry) +{ + u_int bval; + + bval = 0; + if (Carry) { + bval = 0x40; + *regval = 0x80; + pci_write_config(amd->dev, *regval, bval, /*bytes*/1); + } + DELAY(160); + bval |= 0x80; + pci_write_config(amd->dev, *regval, bval, /*bytes*/1); + DELAY(160); + pci_write_config(amd->dev, *regval, 0, /*bytes*/1); + DELAY(160); +} + +static int +amd_EEpromInDO(struct amd_softc *amd) +{ + pci_write_config(amd->dev, 0x80, 0x80, /*bytes*/1); + DELAY(160); + pci_write_config(amd->dev, 0x80, 0x40, /*bytes*/1); + DELAY(160); + if (pci_read_config(amd->dev, 0, /*bytes*/1) == 0x22) + return (1); + return (0); +} + +static u_int16_t +EEpromGetData1(struct amd_softc *amd) +{ + u_int i; + u_int carryFlag; + u_int16_t wval; + + wval = 0; + for (i = 0; i < 16; i++) { + wval <<= 1; + carryFlag = amd_EEpromInDO(amd); + wval |= carryFlag; + } + return (wval); +} + +static void +amd_Prepare(struct amd_softc *amd, int *regval, u_int8_t EEpromCmd) +{ + u_int i, j; + int carryFlag; + + carryFlag = 1; + j = 0x80; + for (i = 0; i < 9; i++) { + amd_EEpromOutDI(amd, regval, carryFlag); + carryFlag = (EEpromCmd & j) ? 1 : 0; + j >>= 1; + } +} + +static void +amd_ReadEEprom(struct amd_softc *amd) +{ + int regval; + u_int i; + u_int16_t *ptr; + u_int8_t cmd; + + ptr = (u_int16_t *)&amd->eepromBuf[0]; + cmd = EEPROM_READ; + for (i = 0; i < 0x40; i++) { + amd_EnDisableCE(amd, ENABLE_CE, ®val); + amd_Prepare(amd, ®val, cmd); + *ptr = EEpromGetData1(amd); + ptr++; + cmd++; + amd_EnDisableCE(amd, DISABLE_CE, ®val); + } +} + +static void +amd_load_defaults(struct amd_softc *amd) +{ + int target; + + bzero(&amd->eepromBuf, sizeof amd->eepromBuf); + for (target = 0; target < MAX_SCSI_ID; target++) + amd->eepromBuf[target << 2] = + (TAG_QUEUING|EN_DISCONNECT|SYNC_NEGO|PARITY_CHK); + amd->eepromBuf[EE_ADAPT_SCSI_ID] = 7; + amd->eepromBuf[EE_MODE2] = ACTIVE_NEGATION|LUN_CHECK|GREATER_1G; + amd->eepromBuf[EE_TAG_CMD_NUM] = 4; +} + +static void +amd_load_eeprom_or_defaults(struct amd_softc *amd) +{ + u_int16_t wval, *ptr; + u_int8_t i; + + amd_ReadEEprom(amd); + wval = 0; + ptr = (u_int16_t *) & amd->eepromBuf[0]; + for (i = 0; i < EE_DATA_SIZE; i += 2, ptr++) + wval += *ptr; + + if (wval != EE_CHECKSUM) { + if (bootverbose) + printf("amd%d: SEEPROM data unavailable. " + "Using default device parameters.\n", + amd->unit); + amd_load_defaults(amd); + } +} + +/* + ********************************************************************** + * Function : static int amd_init (struct Scsi_Host *host) + * Purpose : initialize the internal structures for a given SCSI host + * Inputs : host - pointer to this host adapter's structure/ + ********************************************************************** + */ +static int +amd_init(device_t dev) +{ + struct amd_softc *amd = device_get_softc(dev); + struct resource *iores; + int i, rid; + u_int bval; + + rid = PCI_BASE_ADDR0; + iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, + RF_ACTIVE); + if (iores == NULL) { + if (bootverbose) + printf("amd_init: bus_alloc_resource failure!\n"); + return ENXIO; + } + amd->tag = rman_get_bustag(iores); + amd->bsh = rman_get_bushandle(iores); + + /* DMA tag for mapping buffers into device visible space. */ + if (bus_dma_tag_create(/*parent_dmat*/NULL, /*alignment*/1, + /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/MAXBSIZE, /*nsegments*/AMD_NSEG, + /*maxsegsz*/AMD_MAXTRANSFER_SIZE, + /*flags*/BUS_DMA_ALLOCNOW, + &amd->buffer_dmat) != 0) { + if (bootverbose) + printf("amd_init: bus_dma_tag_create failure!\n"); + return ENXIO; + } + TAILQ_INIT(&amd->free_srbs); + TAILQ_INIT(&amd->running_srbs); + TAILQ_INIT(&amd->waiting_srbs); + amd->last_phase = SCSI_BUS_FREE; + amd->dev = dev; + amd->unit = device_get_unit(dev); + amd->SRBCount = MAX_SRB_CNT; + amd->status = 0; + amd_load_eeprom_or_defaults(amd); + amd->max_id = 7; + if (amd->eepromBuf[EE_MODE2] & LUN_CHECK) { + amd->max_lun = 7; + } else { + amd->max_lun = 0; + } + amd->AdaptSCSIID = amd->eepromBuf[EE_ADAPT_SCSI_ID]; + amd->HostID_Bit = (1 << amd->AdaptSCSIID); + amd->AdaptSCSILUN = 0; + /* (eepromBuf[EE_TAG_CMD_NUM]) << 2; */ + amd->ACBFlag = 0; + amd->Gmode2 = amd->eepromBuf[EE_MODE2]; + amd_linkSRB(amd); + for (i = 0; i <= amd->max_id; i++) { + + if (amd->AdaptSCSIID != i) { + struct amd_target_info *tinfo; + PEEprom prom; + + tinfo = &amd->tinfo[i]; + prom = (PEEprom)&amd->eepromBuf[i << 2]; + if ((prom->EE_MODE1 & EN_DISCONNECT) != 0) { + tinfo->disc_tag |= AMD_USR_DISCENB; + if ((prom->EE_MODE1 & TAG_QUEUING) != 0) + tinfo->disc_tag |= AMD_USR_TAGENB; + } + if ((prom->EE_MODE1 & SYNC_NEGO) != 0) { + tinfo->user.period = + eeprom_period[prom->EE_SPEED]; + tinfo->user.offset = AMD_MAX_SYNC_OFFSET; + } + tinfo->CtrlR1 = amd->AdaptSCSIID; + if ((prom->EE_MODE1 & PARITY_CHK) != 0) + tinfo->CtrlR1 |= PARITY_ERR_REPO; + tinfo->CtrlR3 = FAST_CLK; + tinfo->CtrlR4 = EATER_25NS; + if ((amd->eepromBuf[EE_MODE2] & ACTIVE_NEGATION) != 0) + tinfo->CtrlR4 |= NEGATE_REQACKDATA; + } + } + amd_write8(amd, SCSITIMEOUTREG, 153); /* 250ms selection timeout */ + /* Conversion factor = 0 , 40MHz clock */ + amd_write8(amd, CLKFACTREG, CLK_FREQ_40MHZ); + /* NOP cmd - clear command register */ + amd_write8(amd, SCSICMDREG, NOP_CMD); + amd_write8(amd, CNTLREG2, EN_FEATURE|EN_SCSI2_CMD); + amd_write8(amd, CNTLREG3, FAST_CLK); + bval = EATER_25NS; + if (amd->eepromBuf[EE_MODE2] & ACTIVE_NEGATION) { + bval |= NEGATE_REQACKDATA; + } + amd_write8(amd, CNTLREG4, bval); + + /* Disable SCSI bus reset interrupt */ + amd_write8(amd, CNTLREG1, DIS_INT_ON_SCSI_RST); + + return 0; +} + +/* + * attach and init a host adapter + */ +static int +amd_attach(device_t dev) +{ + struct cam_devq *devq; /* Device Queue to use for this SIM */ + u_int8_t intstat; + struct amd_softc *amd = device_get_softc(dev); + int unit = device_get_unit(dev); + int rid; + void *ih; + struct resource *irqres; + + if (amd_init(dev)) { + if (bootverbose) + printf("amd_attach: amd_init failure!\n"); + return ENXIO; + } + + /* Reset Pending INT */ + intstat = amd_read8(amd, INTSTATREG); + + /* After setting up the adapter, map our interrupt */ + rid = 0; + irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + if (irqres == NULL || + bus_setup_intr(dev, irqres, INTR_TYPE_CAM | INTR_ENTROPY, + amd_intr, amd, &ih)) { + if (bootverbose) + printf("amd%d: unable to register interrupt handler!\n", + unit); + return ENXIO; + } + + /* + * Now let the CAM generic SCSI layer find the SCSI devices on + * the bus * start queue to reset to the idle loop. * + * Create device queue of SIM(s) * (MAX_START_JOB - 1) : + * max_sim_transactions + */ + devq = cam_simq_alloc(MAX_START_JOB); + if (devq == NULL) { + if (bootverbose) + printf("amd_attach: cam_simq_alloc failure!\n"); + return ENXIO; + } + + amd->psim = cam_sim_alloc(amd_action, amd_poll, "amd", + amd, amd->unit, 1, MAX_TAGS_CMD_QUEUE, + devq); + if (amd->psim == NULL) { + cam_simq_free(devq); + if (bootverbose) + printf("amd_attach: cam_sim_alloc failure!\n"); + return ENXIO; + } + + if (xpt_bus_register(amd->psim, 0) != CAM_SUCCESS) { + cam_sim_free(amd->psim, /*free_devq*/TRUE); + if (bootverbose) + printf("amd_attach: xpt_bus_register failure!\n"); + return ENXIO; + } + + if (xpt_create_path(&amd->ppath, /* periph */ NULL, + cam_sim_path(amd->psim), CAM_TARGET_WILDCARD, + CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + xpt_bus_deregister(cam_sim_path(amd->psim)); + cam_sim_free(amd->psim, /* free_simq */ TRUE); + if (bootverbose) + printf("amd_attach: xpt_create_path failure!\n"); + return ENXIO; + } + + return 0; +} + +static int +amd_probe(device_t dev) +{ + if (pci_get_devid(dev) == PCI_DEVICE_ID_AMD53C974) { + device_set_desc(dev, + "Tekram DC390(T)/AMD53c974 SCSI Host Adapter"); + return 0; + } + return ENXIO; +} + +static device_method_t amd_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, amd_probe), + DEVMETHOD(device_attach, amd_attach), + { 0, 0 } +}; + +static driver_t amd_driver = { + "amd", amd_methods, sizeof(struct amd_softc) +}; + +static devclass_t amd_devclass; +DRIVER_MODULE(amd, pci, amd_driver, amd_devclass, 0, 0); diff --git a/sys/pci/amd.h b/sys/pci/amd.h new file mode 100644 index 0000000..85f4f80 --- /dev/null +++ b/sys/pci/amd.h @@ -0,0 +1,580 @@ +/* + ********************************************************************* + * FILE NAME : amd.h + * BY : C.L. Huang (ching@tekram.com.tw) + * Erich Chen (erich@tekram.com.tw) + * Description: Device Driver for the amd53c974 PCI Bus Master + * SCSI Host adapter found on cards such as + * the Tekram DC-390(T). + * (C)Copyright 1995-1999 Tekram Technology Co., Ltd. + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* + * $FreeBSD$ + */ + +#ifndef AMD_H +#define AMD_H + +#define AMD_TRANS_CUR 0x01 /* Modify current neogtiation status */ +#define AMD_TRANS_ACTIVE 0x03 /* Assume this is the active target */ +#define AMD_TRANS_GOAL 0x04 /* Modify negotiation goal */ +#define AMD_TRANS_USER 0x08 /* Modify user negotiation settings */ + +/* + * Per target transfer parameters. + */ +struct amd_transinfo { + u_int8_t period; + u_int8_t offset; +}; + +struct amd_target_info { + /* + * Records the currently active and user/default settings for + * tagged queueing and disconnection for each target. + */ + u_int8_t disc_tag; +#define AMD_CUR_DISCENB 0x01 +#define AMD_CUR_TAGENB 0x02 +#define AMD_USR_DISCENB 0x04 +#define AMD_USR_TAGENB 0x08 + u_int8_t CtrlR1; + u_int8_t CtrlR3; + u_int8_t CtrlR4; + u_int8_t sync_period_reg; + u_int8_t sync_offset_reg; + + /* + * Currently active transfer settings. + */ + struct amd_transinfo current; + /* + * Transfer settings we wish to achieve + * through negotiation. + */ + struct amd_transinfo goal; + /* + * User defined or default transfer settings. + */ + struct amd_transinfo user; +}; + +/* + * Scatter/Gather Segment entry. + */ +struct amd_sg { + u_int32_t SGXLen; + u_int32_t SGXPtr; +}; + +/* + * Chipset feature limits + */ +#define MAX_SCSI_ID 8 +#define AMD_MAX_SYNC_OFFSET 15 +#define AMD_TARGET_MAX 7 +#define AMD_LUN_MAX 7 +#define AMD_NSEG (btoc(MAXPHYS) + 1) +#define AMD_MAXTRANSFER_SIZE 0xFFFFFF /* restricted by 24 bit counter */ +#define MAX_DEVICES 10 +#define MAX_TAGS_CMD_QUEUE 256 +#define MAX_CMD_PER_LUN 6 +#define MAX_SRB_CNT 256 +#define MAX_START_JOB 256 + +/* + * BIT position to integer mapping. + */ +#define BIT(N) (0x01 << N) + +/* + * EEPROM storage offsets and data structures. + */ +typedef struct _EEprom { + u_int8_t EE_MODE1; + u_int8_t EE_SPEED; + u_int8_t xx1; + u_int8_t xx2; +} EEprom, *PEEprom; + +#define EE_ADAPT_SCSI_ID 64 +#define EE_MODE2 65 +#define EE_DELAY 66 +#define EE_TAG_CMD_NUM 67 +#define EE_DATA_SIZE 128 +#define EE_CHECKSUM 0x1234 + +/* + * EE_MODE1 bits definition + */ +#define PARITY_CHK BIT(0) +#define SYNC_NEGO BIT(1) +#define EN_DISCONNECT BIT(2) +#define SEND_START BIT(3) +#define TAG_QUEUING BIT(4) + +/* + * EE_MODE2 bits definition + */ +#define MORE2_DRV BIT(0) +#define GREATER_1G BIT(1) +#define RST_SCSI_BUS BIT(2) +#define ACTIVE_NEGATION BIT(3) +#define NO_SEEK BIT(4) +#define LUN_CHECK BIT(5) + +#define ENABLE_CE 1 +#define DISABLE_CE 0 +#define EEPROM_READ 0x80 + +#define AMD_TAG_WILDCARD ((u_int)(~0)) + +/* + * SCSI Request Block + */ +struct amd_srb { + TAILQ_ENTRY(amd_srb) links; + u_int8_t CmdBlock[12]; + union ccb *pccb; + bus_dmamap_t dmamap; + struct amd_sg *pSGlist; + + u_int32_t TotalXferredLen; + u_int32_t SGPhysAddr; /* a segment starting address */ + u_int32_t SGToBeXferLen; /* to be xfer length */ + u_int32_t Segment0[2]; + u_int32_t Segment1[2]; + + struct amd_sg SGsegment[AMD_NSEG]; + struct amd_sg Segmentx;/* a one entry of S/G list table */ + u_int8_t *pMsgPtr; + u_int16_t SRBState; + + u_int8_t AdaptStatus; + u_int8_t TargetStatus; + u_int8_t MsgCnt; + u_int8_t EndMessage; + u_int8_t TagNumber; + u_int8_t SGcount; + u_int8_t SGIndex; + u_int8_t IORBFlag; /* ;81h-Reset, 2-retry */ + + u_int8_t SRBStatus; + u_int8_t SRBFlag; + /* ; b0-AutoReqSense,b6-Read,b7-write */ + /* ; b4-settimeout,b5-Residual valid */ + u_int8_t ScsiCmdLen; +}; + +TAILQ_HEAD(srb_queue, amd_srb); + +/* + * Per-adapter, software configuration. + */ +struct amd_softc { + device_t dev; + bus_space_tag_t tag; + bus_space_handle_t bsh; + bus_dma_tag_t buffer_dmat; /* dmat for buffer I/O */ + int unit; + + int last_phase; + int cur_target; + int cur_lun; + struct amd_srb *active_srb; + struct amd_srb *untagged_srbs[AMD_TARGET_MAX+1][AMD_LUN_MAX+1]; + struct amd_target_info tinfo[AMD_TARGET_MAX+1]; + u_int16_t disc_count[AMD_TARGET_MAX+1][AMD_LUN_MAX+1]; + + struct srb_queue free_srbs; + struct srb_queue waiting_srbs; + struct srb_queue running_srbs; + + struct amd_srb *pTmpSRB; + + u_int16_t SRBCount; + + u_int16_t max_id; + u_int16_t max_lun; + + /* Hooks into the CAM XPT */ + struct cam_sim *psim; + struct cam_path *ppath; + + u_int8_t msgin_buf[6]; + u_int8_t msgout_buf[6]; + u_int msgin_index; + u_int msgout_index; + u_int msgout_len; + + u_int8_t status; + u_int8_t AdaptSCSIID; /* ; Adapter SCSI Target ID */ + u_int8_t AdaptSCSILUN; /* ; Adapter SCSI LUN */ + + u_int8_t ACBFlag; + + u_int8_t Gmode2; + + u_int8_t HostID_Bit; + + u_int8_t InitDCB_flag[8][8]; /* flag of initDCB for device */ + struct amd_srb SRB_array[MAX_SRB_CNT]; /* +45Ch, Len= */ + struct amd_srb TmpSRB; + /* Setup data stored in an 93c46 serial eeprom */ + u_int8_t eepromBuf[EE_DATA_SIZE]; +}; + +/* + * ----SRB State machine definition + */ +#define SRB_FREE 0 +#define SRB_READY BIT(1) +#define SRB_MSGOUT BIT(2) /* ;arbitration+msg_out 1st byte */ +#define SRB_MSGIN BIT(3) +#define SRB_MSGIN_MULTI BIT(4) +#define SRB_COMMAND BIT(5) +#define SRB_START BIT(6) /* ;arbitration+msg_out+command_out */ +#define SRB_DISCONNECT BIT(7) +#define SRB_DATA_XFER BIT(8) +#define SRB_XFERPAD BIT(9) +#define SRB_STATUS BIT(10) +#define SRB_COMPLETED BIT(11) +#define SRB_ABORT_SENT BIT(12) +#define DO_SYNC_NEGO BIT(13) +#define SRB_UNEXPECT_RESEL BIT(14) + +/* + * ---ACB Flag + */ +#define RESET_DEV BIT(0) +#define RESET_DETECT BIT(1) +#define RESET_DONE BIT(2) + +/* + * ---DCB Flag + */ +#define ABORT_DEV_ BIT(0) + +/* + * ---SRB status + */ +#define SRB_OK BIT(0) +#define ABORTION BIT(1) +#define OVER_RUN BIT(2) +#define UNDER_RUN BIT(3) +#define PARITY_ERROR BIT(4) +#define SRB_ERROR BIT(5) + +/* + * ---SRB Flags + */ +#define DATAOUT BIT(7) +#define DATAIN BIT(6) +#define RESIDUAL_VALID BIT(5) +#define ENABLE_TIMER BIT(4) +#define RESET_DEV0 BIT(2) +#define ABORT_DEV BIT(1) +#define AUTO_REQSENSE BIT(0) + +/* + * ---Adapter status + */ +#define H_STATUS_GOOD 0 +#define H_SEL_TIMEOUT 0x11 +#define H_OVER_UNDER_RUN 0x12 +#define H_UNEXP_BUS_FREE 0x13 +#define H_TARGET_PHASE_F 0x14 +#define H_INVALID_CCB_OP 0x16 +#define H_LINK_CCB_BAD 0x17 +#define H_BAD_TARGET_DIR 0x18 +#define H_DUPLICATE_CCB 0x19 +#define H_BAD_CCB_OR_SG 0x1A +#define H_ABORT 0x0FF + +/* + * AMD specific "status" codes returned in the SCSI status byte. + */ +#define AMD_SCSI_STAT_UNEXP_BUS_F 0xFD /* ; Unexpect Bus Free */ +#define AMD_SCSI_STAT_BUS_RST_DETECT 0xFE /* ; Scsi Bus Reset detected */ +#define AMD_SCSI_STAT_SEL_TIMEOUT 0xFF /* ; Selection Time out */ + +/* + * ---Sync_Mode + */ +#define SYNC_DISABLE 0 +#define SYNC_ENABLE BIT(0) +#define SYNC_NEGO_DONE BIT(1) +#define WIDE_ENABLE BIT(2) +#define WIDE_NEGO_DONE BIT(3) +#define EN_TAG_QUEUING BIT(4) +#define EN_ATN_STOP BIT(5) + +#define SYNC_NEGO_OFFSET 15 + +/* + * ---SCSI bus phase + */ +#define SCSI_DATA_OUT 0 +#define SCSI_DATA_IN 1 +#define SCSI_COMMAND 2 +#define SCSI_STATUS 3 +#define SCSI_NOP0 4 +#define SCSI_ARBITRATING 5 +#define SCSI_MSG_OUT 6 +#define SCSI_MSG_IN 7 +#define SCSI_BUS_FREE 8 + +/* + *========================================================== + * AMD 53C974 Registers bit Definition + *========================================================== + */ + +/* + * ------SCSI Register------- + * Command Reg.(+0CH) + */ +#define DMA_COMMAND BIT(7) +#define NOP_CMD 0 +#define CLEAR_FIFO_CMD 1 +#define RST_DEVICE_CMD 2 +#define RST_SCSI_BUS_CMD 3 +#define INFO_XFER_CMD 0x10 +#define INITIATOR_CMD_CMPLTE 0x11 +#define MSG_ACCEPTED_CMD 0x12 +#define XFER_PAD_BYTE 0x18 +#define SET_ATN_CMD 0x1A +#define RESET_ATN_CMD 0x1B +#define SEL_W_ATN 0x42 +#define SEL_W_ATN_STOP 0x43 +#define EN_SEL_RESEL 0x44 +#define SEL_W_ATN2 0x46 +#define DATA_XFER_CMD INFO_XFER_CMD + + +/* + * ------SCSI Register------- + * SCSI Status Reg.(+10H) + */ +#define INTERRUPT BIT(7) +#define ILLEGAL_OP_ERR BIT(6) +#define PARITY_ERR BIT(5) +#define COUNT_2_ZERO BIT(4) +#define GROUP_CODE_VALID BIT(3) +#define SCSI_PHASE_MASK (BIT(2)+BIT(1)+BIT(0)) + +/* + * ------SCSI Register------- + * Interrupt Status Reg.(+14H) + */ +#define SCSI_RESET_ BIT(7) +#define INVALID_CMD BIT(6) +#define DISCONNECTED BIT(5) +#define SERVICE_REQUEST BIT(4) +#define SUCCESSFUL_OP BIT(3) +#define RESELECTED BIT(2) +#define SEL_ATTENTION BIT(1) +#define SELECTED BIT(0) + +/* + * ------SCSI Register------- + * Internal State Reg.(+18H) + */ +#define SYNC_OFFSET_FLAG BIT(3) +#define INTRN_STATE_MASK (BIT(2)+BIT(1)+BIT(0)) + +/* + * ------SCSI Register------- + * Clock Factor Reg.(+24H) + */ +#define CLK_FREQ_40MHZ 0 +#define CLK_FREQ_35MHZ (BIT(2)+BIT(1)+BIT(0)) +#define CLK_FREQ_30MHZ (BIT(2)+BIT(1)) +#define CLK_FREQ_25MHZ (BIT(2)+BIT(0)) +#define CLK_FREQ_20MHZ BIT(2) +#define CLK_FREQ_15MHZ (BIT(1)+BIT(0)) +#define CLK_FREQ_10MHZ BIT(1) + +/* + * ------SCSI Register------- + * Control Reg. 1(+20H) + */ +#define EXTENDED_TIMING BIT(7) +#define DIS_INT_ON_SCSI_RST BIT(6) +#define PARITY_ERR_REPO BIT(4) +#define SCSI_ID_ON_BUS (BIT(2)+BIT(1)+BIT(0)) + +/* + * ------SCSI Register------- + * Control Reg. 2(+2CH) + */ +#define EN_FEATURE BIT(6) +#define EN_SCSI2_CMD BIT(3) + +/* + * ------SCSI Register------- + * Control Reg. 3(+30H) + */ +#define ID_MSG_CHECK BIT(7) +#define EN_QTAG_MSG BIT(6) +#define EN_GRP2_CMD BIT(5) +#define FAST_SCSI BIT(4) /* ;10MB/SEC */ +#define FAST_CLK BIT(3) /* ;25 - 40 MHZ */ + +/* + * ------SCSI Register------- + * Control Reg. 4(+34H) + */ +#define EATER_12NS 0 +#define EATER_25NS BIT(7) +#define EATER_35NS BIT(6) +#define EATER_0NS (BIT(7)+BIT(6)) +#define NEGATE_REQACKDATA BIT(2) +#define NEGATE_REQACK BIT(3) + +/* + *======================================== + * DMA Register + *======================================== + */ + +/* + * -------DMA Register-------- + * DMA Command Reg.(+40H) + */ +#define READ_DIRECTION BIT(7) +#define WRITE_DIRECTION 0 +#define EN_DMA_INT BIT(6) +#define MAP_TO_MDL BIT(5) +#define DMA_DIAGNOSTIC BIT(4) +#define DMA_IDLE_CMD 0 +#define DMA_BLAST_CMD BIT(0) +#define DMA_ABORT_CMD BIT(1) +#define DMA_START_CMD (BIT(1)|BIT(0)) + +/* + * -------DMA Register-------- + * DMA Status Reg.(+54H) + */ +#define PCI_MS_ABORT BIT(6) +#define BLAST_COMPLETE BIT(5) +#define SCSI_INTERRUPT BIT(4) +#define DMA_XFER_DONE BIT(3) +#define DMA_XFER_ABORT BIT(2) +#define DMA_XFER_ERROR BIT(1) +#define POWER_DOWN BIT(0) + +/* + * -------DMA Register-------- + * DMA SCSI Bus and Ctrl.(+70H) + * EN_INT_ON_PCI_ABORT + */ + +/* + *========================================================== + * SCSI Chip register address offset + *========================================================== + */ +#define CTCREG_LOW 0x00 /* (R) current transfer count register low */ +#define STCREG_LOW 0x00 /* (W) start transfer count register low */ + +#define CTCREG_MID 0x04 /* (R) current transfer count register + * middle */ +#define STCREG_MID 0x04 /* (W) start transfer count register middle */ + +#define SCSIFIFOREG 0x08 /* (R/W) SCSI FIFO register */ + +#define SCSICMDREG 0x0C /* (R/W) SCSI command register */ + +#define SCSISTATREG 0x10 /* (R) SCSI status register */ +#define SCSIDESTIDREG 0x10 /* (W) SCSI destination ID register */ + +#define INTSTATREG 0x14 /* (R) interrupt status register */ +#define SCSITIMEOUTREG 0x14 /* (W) SCSI timeout register */ + + +#define INTERNSTATREG 0x18 /* (R) internal state register */ +#define SYNCPERIOREG 0x18 /* (W) synchronous transfer period register */ + +#define CURRENTFIFOREG 0x1C /* (R) current FIFO/internal state register */ +#define SYNCOFFREG 0x1C/* (W) synchronous transfer period register */ + +#define CNTLREG1 0x20 /* (R/W) control register 1 */ +#define CLKFACTREG 0x24 /* (W) clock factor register */ +#define CNTLREG2 0x2C /* (R/W) control register 2 */ +#define CNTLREG3 0x30 /* (R/W) control register 3 */ +#define CNTLREG4 0x34 /* (R/W) control register 4 */ + +#define CURTXTCNTREG 0x38 /* (R) current transfer count register + * high/part-unique ID code */ +#define STCREG_HIGH 0x38 /* (W) Start current transfer count register + * high */ + +/* + ********************************************************* + * + * SCSI DMA register + * + ********************************************************* + */ +#define DMA_Cmd 0x40 /* (R/W) command register */ +#define DMA_XferCnt 0x44 /* (R/W) starting transfer count */ +#define DMA_XferAddr 0x48 /* (R/W) starting Physical address */ +#define DMA_Wk_ByteCntr 0x4C /* ( R ) working byte counter */ +#define DMA_Wk_AddrCntr 0x50 /* ( R ) working address counter */ +#define DMA_Status 0x54 /* ( R ) status register */ +#define DMA_MDL_Addr 0x58 /* (R/W) starting memory descriptor list (MDL) + * address */ +#define DMA_Wk_MDL_Cntr 0x5C /* ( R ) working MDL counter */ +#define DMA_ScsiBusCtrl 0x70 /* (bits R/W) SCSI BUS and control */ + +/* ******************************************************* */ +#define am_target SCSISTATREG +#define am_timeout INTSTATREG +#define am_seq_step SYNCPERIOREG +#define am_fifo_count SYNCOFFREG + + +#define amd_read8(amd, port) \ + bus_space_read_1((amd)->tag, (amd)->bsh, port) + +#define amd_read16(amd, port) \ + bus_space_read_2((amd)->tag, (amd)->bsh, port) + +#define amd_read32(amd, port) \ + bus_space_read_4((amd)->tag, (amd)->bsh, port) + +#define amd_write8(amd, port, value) \ + bus_space_write_1((amd)->tag, (amd)->bsh, port, value) + +#define amd_write8_multi(amd, port, ptr, len) \ + bus_space_write_multi_1((amd)->tag, (amd)->bsh, port, ptr, len) + +#define amd_write16(amd, port, value) \ + bus_space_write_2((amd)->tag, (amd)->bsh, port, value) + +#define amd_write32(amd, port, value) \ + bus_space_write_4((amd)->tag, (amd)->bsh, port, value) + +#endif /* AMD_H */ diff --git a/sys/pci/amdpm.c b/sys/pci/amdpm.c new file mode 100644 index 0000000..9664ce2 --- /dev/null +++ b/sys/pci/amdpm.c @@ -0,0 +1,640 @@ +/*- + * Copyright (c) 2000 Matthew C. Forman + * + * Based (heavily) on alpm.c which is: + * + * Copyright (c) 1998, 1999 Nicolas Souchu + * 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$ + * + */ + +/* + * Power management function/SMBus function support for the AMD 756 chip. + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/uio.h> + +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/clock.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <pci/pcivar.h> +#include <pci/pcireg.h> + +#include <dev/iicbus/iiconf.h> +#include <dev/smbus/smbconf.h> +#include "smbus_if.h" + +#define AMDPM_DEBUG(x) if (amdpm_debug) (x) + +#ifdef DEBUG +static int amdpm_debug = 1; +#else +static int amdpm_debug = 0; +#endif + +#define AMDPM_VENDORID_AMD 0x1022 +#define AMDPM_DEVICEID_AMD756PM 0x740b + +/* PCI Configuration space registers */ +#define AMDPCI_PMBASE 0x58 + +#define AMDPCI_GEN_CONFIG_PM 0x41 +#define AMDPCI_PMIOEN (1<<7) + +#define AMDPCI_SCIINT_CONFIG_PM 0x42 +#define AMDPCI_SCISEL_IRQ11 11 + +#define AMDPCI_REVID 0x08 + +/* + * I/O registers. + * Base address programmed via AMDPCI_PMBASE. + */ +#define AMDSMB_GLOBAL_STATUS 0xE0 +#define AMDSMB_GS_TO_STS (1<<5) +#define AMDSMB_GS_HCYC_STS (1<<4) +#define AMDSMB_GS_HST_STS (1<<3) +#define AMDSMB_GS_PRERR_STS (1<<2) +#define AMDSMB_GS_COL_STS (1<<1) +#define AMDSMB_GS_ABRT_STS (1<<0) +#define AMDSMB_GS_CLEAR_STS (AMDSMB_GS_TO_STS|AMDSMB_GS_HCYC_STS|AMDSMB_GS_PRERR_STS|AMDSMB_GS_COL_STS|AMDSMB_GS_ABRT_STS) + +#define AMDSMB_GLOBAL_ENABLE 0xE2 +#define AMDSMB_GE_ABORT (1<<5) +#define AMDSMB_GE_HCYC_EN (1<<4) +#define AMDSMB_GE_HOST_STC (1<<3) +#define AMDSMB_GE_CYC_QUICK 0 +#define AMDSMB_GE_CYC_BYTE 1 +#define AMDSMB_GE_CYC_BDATA 2 +#define AMDSMB_GE_CYC_WDATA 3 +#define AMDSMB_GE_CYC_PROCCALL 4 +#define AMDSMB_GE_CYC_BLOCK 5 + +#define AMDSMB_HSTADDR 0xE4 +#define AMDSMB_HSTDATA 0xE6 +#define AMDSMB_HSTCMD 0xE8 +#define AMDSMB_HSTDFIFO 0xE9 +#define AMDSMB_HSLVDATA 0xEA +#define AMDSMB_HSLVDA 0xEC +#define AMDSMB_HSLVDDR 0xEE +#define AMDSMB_SNPADDR 0xEF + +struct amdpm_softc { + int base; + int rid; + struct resource *res; + bus_space_tag_t smbst; + bus_space_handle_t smbsh; +}; + +struct amdsmb_softc { + int base; + device_t smbus; + struct amdpm_softc *amdpm; +}; + +#define AMDPM_SMBINB(amdsmb,register) \ + (bus_space_read_1(amdsmb->amdpm->smbst, amdsmb->amdpm->smbsh, register)) +#define AMDPM_SMBOUTB(amdsmb,register,value) \ + (bus_space_write_1(amdsmb->amdpm->smbst, amdsmb->amdpm->smbsh, register, value)) +#define AMDPM_SMBINW(amdsmb,register) \ + (bus_space_read_2(amdsmb->amdpm->smbst, amdsmb->amdpm->smbsh, register)) +#define AMDPM_SMBOUTW(amdsmb,register,value) \ + (bus_space_write_2(amdsmb->amdpm->smbst, amdsmb->amdpm->smbsh, register, value)) + +static int amdsmb_probe(device_t); +static int amdsmb_attach(device_t); +static int amdsmb_smb_callback(device_t, int, caddr_t *); +static int amdsmb_smb_quick(device_t dev, u_char slave, int how); +static int amdsmb_smb_sendb(device_t dev, u_char slave, char byte); +static int amdsmb_smb_recvb(device_t dev, u_char slave, char *byte); +static int amdsmb_smb_writeb(device_t dev, u_char slave, char cmd, char byte); +static int amdsmb_smb_readb(device_t dev, u_char slave, char cmd, char *byte); +static int amdsmb_smb_writew(device_t dev, u_char slave, char cmd, short word); +static int amdsmb_smb_readw(device_t dev, u_char slave, char cmd, short *word); +static int amdsmb_smb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); +static int amdsmb_smb_bread(device_t dev, u_char slave, char cmd, u_char count, char *byte); + +static int amdpm_probe(device_t); +static int amdpm_attach(device_t); + + +static int +amdpm_probe(device_t dev) +{ + u_long base; + + if ((pci_get_vendor(dev) == AMDPM_VENDORID_AMD) && + (pci_get_device(dev) == AMDPM_DEVICEID_AMD756PM)) { + device_set_desc(dev, "AMD 756 Power Management Controller"); + + /* + * We have to do this, since the BIOS won't give us the + * resource info (not mine, anyway). + */ + base = pci_read_config(dev, AMDPCI_PMBASE, 4); + base &= 0xff00; + bus_set_resource(dev, SYS_RES_IOPORT, AMDPCI_PMBASE, base, 256); + return (0); + } + return ENXIO; +} + +static int +amdpm_attach(device_t dev) +{ + struct amdpm_softc *amdpm_sc = device_get_softc(dev); + u_char val_b; + int unit = device_get_unit(dev); + device_t smbinterface; + + /* Enable I/O block access */ + val_b = pci_read_config(dev, AMDPCI_GEN_CONFIG_PM, 1); + pci_write_config(dev, AMDPCI_GEN_CONFIG_PM, val_b | AMDPCI_PMIOEN, 1); + + /* Allocate I/O space */ + amdpm_sc->rid = AMDPCI_PMBASE; + amdpm_sc->res = bus_alloc_resource(dev, SYS_RES_IOPORT, &amdpm_sc->rid, 0, ~0, 1, RF_ACTIVE); + + if (amdpm_sc->res == NULL) { + device_printf(dev, "could not map i/o space\n"); + return (ENXIO); + } + + amdpm_sc->smbst = rman_get_bustag(amdpm_sc->res); + amdpm_sc->smbsh = rman_get_bushandle(amdpm_sc->res); + + smbinterface = device_add_child(dev, "amdsmb", unit); + if (!smbinterface) + device_printf(dev, "could not add SMBus device\n"); + else + device_probe_and_attach(smbinterface); + + return (0); +} + +static int +amdsmb_probe(device_t dev) +{ + struct amdsmb_softc *amdsmb_sc = (struct amdsmb_softc *)device_get_softc(dev); + + device_set_desc(dev, "AMD 756 SMBus interface"); + device_printf(dev, "AMD 756 SMBus interface\n"); + + /* Allocate a new smbus device */ + amdsmb_sc->smbus = device_add_child(dev, "smbus", -1); + if (!amdsmb_sc->smbus) + return (EINVAL); + + bus_generic_attach(dev); + + return (0); +} + +static int +amdsmb_attach(device_t dev) +{ + struct amdsmb_softc *amdsmb_sc = (struct amdsmb_softc *)device_get_softc(dev); + + amdsmb_sc->amdpm = device_get_softc(device_get_parent(dev)); + + /* Probe and attach the smbus */ + device_probe_and_attach(amdsmb_sc->smbus); + + return (0); +} + +static int +amdsmb_smb_callback(device_t dev, int index, caddr_t *data) +{ + int error = 0; + + switch (index) { + case SMB_REQUEST_BUS: + case SMB_RELEASE_BUS: + break; + default: + error = EINVAL; + } + + return (error); +} + +static int +amdsmb_clear(struct amdsmb_softc *sc) +{ + AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_STATUS, AMDSMB_GS_CLEAR_STS); + DELAY(10); + + return (0); +} + +#if 0 +static int +amdsmb_abort(struct amdsmb_softc *sc) +{ + u_short l; + + l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); + AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, l | AMDSMB_GE_ABORT); + + return (0); +} +#endif + +static int +amdsmb_idle(struct amdsmb_softc *sc) +{ + u_short sts; + + sts = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_STATUS); + + AMDPM_DEBUG(printf("amdpm: busy? STS=0x%x\n", sts)); + + return (~(sts & AMDSMB_GS_HST_STS)); +} + +/* + * Poll the SMBus controller + */ +static int +amdsmb_wait(struct amdsmb_softc *sc) +{ + int count = 10000; + u_short sts = 0; + int error; + + /* Wait for command to complete (SMBus controller is idle) */ + while(count--) { + DELAY(10); + sts = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_STATUS); + if (!(sts & AMDSMB_GS_HST_STS)) + break; + } + + AMDPM_DEBUG(printf("amdpm: STS=0x%x (count=%d)\n", sts, count)); + + error = SMB_ENOERR; + + if (!count) + error |= SMB_ETIMEOUT; + + if (sts & AMDSMB_GS_ABRT_STS) + error |= SMB_EABORT; + + if (sts & AMDSMB_GS_COL_STS) + error |= SMB_ENOACK; + + if (sts & AMDSMB_GS_PRERR_STS) + error |= SMB_EBUSERR; + + if (error != SMB_ENOERR) + amdsmb_clear(sc); + + return (error); +} + +static int +amdsmb_smb_quick(device_t dev, u_char slave, int how) +{ + struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); + int error; + u_short l; + + amdsmb_clear(sc); + if (!amdsmb_idle(sc)) + return (EBUSY); + + switch (how) { + case SMB_QWRITE: + AMDPM_DEBUG(printf("amdpm: QWRITE to 0x%x", slave)); + AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB); + break; + case SMB_QREAD: + AMDPM_DEBUG(printf("amdpm: QREAD to 0x%x", slave)); + AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB); + break; + default: + panic("%s: unknown QUICK command (%x)!", __func__, how); + } + l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); + AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_QUICK | AMDSMB_GE_HOST_STC); + + error = amdsmb_wait(sc); + + AMDPM_DEBUG(printf(", error=0x%x\n", error)); + + return (error); +} + +static int +amdsmb_smb_sendb(device_t dev, u_char slave, char byte) +{ + struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); + int error; + u_short l; + + amdsmb_clear(sc); + if (!amdsmb_idle(sc)) + return (SMB_EBUSY); + + AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB); + AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, byte); + l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); + AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BYTE | AMDSMB_GE_HOST_STC); + + error = amdsmb_wait(sc); + + AMDPM_DEBUG(printf("amdpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error)); + + return (error); +} + +static int +amdsmb_smb_recvb(device_t dev, u_char slave, char *byte) +{ + struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); + int error; + u_short l; + + amdsmb_clear(sc); + if (!amdsmb_idle(sc)) + return (SMB_EBUSY); + + AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB); + l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); + AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BYTE | AMDSMB_GE_HOST_STC); + + if ((error = amdsmb_wait(sc)) == SMB_ENOERR) + *byte = AMDPM_SMBINW(sc, AMDSMB_HSTDATA); + + AMDPM_DEBUG(printf("amdpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error)); + + return (error); +} + +static int +amdsmb_smb_writeb(device_t dev, u_char slave, char cmd, char byte) +{ + struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); + int error; + u_short l; + + amdsmb_clear(sc); + if (!amdsmb_idle(sc)) + return (SMB_EBUSY); + + AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB); + AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, byte); + AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); + l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); + AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BDATA | AMDSMB_GE_HOST_STC); + + error = amdsmb_wait(sc); + + AMDPM_DEBUG(printf("amdpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error)); + + return (error); +} + +static int +amdsmb_smb_readb(device_t dev, u_char slave, char cmd, char *byte) +{ + struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); + int error; + u_short l; + + amdsmb_clear(sc); + if (!amdsmb_idle(sc)) + return (SMB_EBUSY); + + AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB); + AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); + l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); + AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BDATA | AMDSMB_GE_HOST_STC); + + if ((error = amdsmb_wait(sc)) == SMB_ENOERR) + *byte = AMDPM_SMBINW(sc, AMDSMB_HSTDATA); + + AMDPM_DEBUG(printf("amdpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error)); + + return (error); +} + +static int +amdsmb_smb_writew(device_t dev, u_char slave, char cmd, short word) +{ + struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); + int error; + u_short l; + + amdsmb_clear(sc); + if (!amdsmb_idle(sc)) + return (SMB_EBUSY); + + AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB); + AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, word); + AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); + l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); + AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_WDATA | AMDSMB_GE_HOST_STC); + + error = amdsmb_wait(sc); + + AMDPM_DEBUG(printf("amdpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error)); + + return (error); +} + +static int +amdsmb_smb_readw(device_t dev, u_char slave, char cmd, short *word) +{ + struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); + int error; + u_short l; + + amdsmb_clear(sc); + if (!amdsmb_idle(sc)) + return (SMB_EBUSY); + + AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB); + AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); + l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); + AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_WDATA | AMDSMB_GE_HOST_STC); + + if ((error = amdsmb_wait(sc)) == SMB_ENOERR) + *word = AMDPM_SMBINW(sc, AMDSMB_HSTDATA); + + AMDPM_DEBUG(printf("amdpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error)); + + return (error); +} + +static int +amdsmb_smb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) +{ + struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); + u_char remain, len, i; + int error = SMB_ENOERR; + u_short l; + + amdsmb_clear(sc); + if(!amdsmb_idle(sc)) + return (SMB_EBUSY); + + remain = count; + while (remain) { + len = min(remain, 32); + + AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB); + + /* + * Do we have to reset the internal 32-byte buffer? + * Can't see how to do this from the data sheet. + */ + + AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, len); + + /* Fill the 32-byte internal buffer */ + for (i=0; i<len; i++) { + AMDPM_SMBOUTB(sc, AMDSMB_HSTDFIFO, buf[count-remain+i]); + DELAY(2); + } + AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); + l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); + AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BLOCK | AMDSMB_GE_HOST_STC); + + if ((error = amdsmb_wait(sc)) != SMB_ENOERR) + goto error; + + remain -= len; + } + +error: + AMDPM_DEBUG(printf("amdpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error)); + + return (error); +} + +static int +amdsmb_smb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf) +{ + struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev); + u_char remain, len, i; + int error = SMB_ENOERR; + u_short l; + + amdsmb_clear(sc); + if (!amdsmb_idle(sc)) + return (SMB_EBUSY); + + remain = count; + while (remain) { + AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB); + + AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); + + l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); + AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BLOCK | AMDSMB_GE_HOST_STC); + + if ((error = amdsmb_wait(sc)) != SMB_ENOERR) + goto error; + + len = AMDPM_SMBINW(sc, AMDSMB_HSTDATA); + + /* Read the 32-byte internal buffer */ + for (i=0; i<len; i++) { + buf[count-remain+i] = AMDPM_SMBINB(sc, AMDSMB_HSTDFIFO); + DELAY(2); + } + + remain -= len; + } +error: + AMDPM_DEBUG(printf("amdpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error)); + + return (error); +} + +static devclass_t amdpm_devclass; + +static device_method_t amdpm_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, amdpm_probe), + DEVMETHOD(device_attach, amdpm_attach), + + { 0, 0 } +}; + +static driver_t amdpm_driver = { + "amdpm", + amdpm_methods, + sizeof(struct amdpm_softc), +}; + +static devclass_t amdsmb_devclass; + +static device_method_t amdsmb_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, amdsmb_probe), + DEVMETHOD(device_attach, amdsmb_attach), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + /* SMBus interface */ + DEVMETHOD(smbus_callback, amdsmb_smb_callback), + DEVMETHOD(smbus_quick, amdsmb_smb_quick), + DEVMETHOD(smbus_sendb, amdsmb_smb_sendb), + DEVMETHOD(smbus_recvb, amdsmb_smb_recvb), + DEVMETHOD(smbus_writeb, amdsmb_smb_writeb), + DEVMETHOD(smbus_readb, amdsmb_smb_readb), + DEVMETHOD(smbus_writew, amdsmb_smb_writew), + DEVMETHOD(smbus_readw, amdsmb_smb_readw), + DEVMETHOD(smbus_bwrite, amdsmb_smb_bwrite), + DEVMETHOD(smbus_bread, amdsmb_smb_bread), + + { 0, 0 } +}; + +static driver_t amdsmb_driver = { + "amdsmb", + amdsmb_methods, + sizeof(struct amdsmb_softc), +}; + +DRIVER_MODULE(amdpm, pci, amdpm_driver, amdpm_devclass, 0, 0); +DRIVER_MODULE(amdsmb, amdpm, amdsmb_driver, amdsmb_devclass, 0, 0); +MODULE_DEPEND(amdpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); +MODULE_VERSION(amdpm, 1); diff --git a/sys/pci/cy_pci.c b/sys/pci/cy_pci.c new file mode 100644 index 0000000..72cb961 --- /dev/null +++ b/sys/pci/cy_pci.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 1996, David Greenman + * 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$ + */ + +/* + * Cyclades Y PCI serial interface driver + */ + +#include "opt_cy_pci_fastintr.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> + +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <dev/pci/pcivar.h> + +#define CY_PCI_BASE_ADDR0 0x10 +#define CY_PCI_BASE_ADDR1 0x14 +#define CY_PCI_BASE_ADDR2 0x18 + +#define CY_PLX_9050_ICS 0x4c +#define CY_PLX_9060_ICS 0x68 +#define CY_PLX_9050_ICS_IENABLE 0x040 +#define CY_PLX_9050_ICS_LOCAL_IENABLE 0x001 +#define CY_PLX_9050_ICS_LOCAL_IPOLARITY 0x002 +#define CY_PLX_9060_ICS_IENABLE 0x100 +#define CY_PLX_9060_ICS_LOCAL_IENABLE 0x800 + +/* Cyclom-Y Custom Register for PLX ID. */ +#define PLX_VER 0x3400 +#define PLX_9050 0x0b +#define PLX_9060 0x0c +#define PLX_9080 0x0d + +extern int cyattach_common(void *, int); /* Not exactly correct */ +extern void cyintr(int); + +static int cy_pci_attach(device_t dev); +static int cy_pci_probe(device_t dev); + +static device_method_t cy_pci_methods[] = { + /* Device interface. */ + DEVMETHOD(device_probe, cy_pci_probe), + DEVMETHOD(device_attach, cy_pci_attach), + + { 0, 0 } +}; + +static driver_t cy_pci_driver = { + "cy", + cy_pci_methods, + 0, +}; + +static devclass_t cy_devclass; + +DRIVER_MODULE(cy, pci, cy_pci_driver, cy_devclass, 0, 0); + +static int +cy_pci_probe(dev) + device_t dev; +{ + u_int32_t device_id; + + device_id = pci_get_devid(dev); + device_id &= ~0x00060000; + if (device_id != 0x0100120e && device_id != 0x0101120e) + return (ENXIO); + device_set_desc(dev, "Cyclades Cyclom-Y Serial Adapter"); + return (0); +} + +static int +cy_pci_attach(dev) + device_t dev; +{ + struct resource *ioport_res, *irq_res, *mem_res; + void *irq_cookie, *vaddr; + u_int32_t ioport; + int adapter, irq_setup, ioport_rid, irq_rid, mem_rid; + u_char plx_ver; + + ioport_res = NULL; + irq_res = NULL; + mem_res = NULL; + + ioport_rid = CY_PCI_BASE_ADDR1; + ioport_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &ioport_rid, + 0ul, ~0ul, 0ul, RF_ACTIVE); + if (ioport_res == NULL) { + device_printf(dev, "ioport resource allocation failed\n"); + goto fail; + } + ioport = rman_get_start(ioport_res); + + mem_rid = CY_PCI_BASE_ADDR2; + mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &mem_rid, + 0ul, ~0ul, 0ul, RF_ACTIVE); + if (mem_res == NULL) { + device_printf(dev, "memory resource allocation failed\n"); + goto fail; + } + vaddr = rman_get_virtual(mem_res); + + adapter = cyattach_common(vaddr, 1); + if (adapter < 0) { + device_printf(dev, "no ports found!\n"); + goto fail; + } + + /* + * Allocate our interrupt. + * XXX Using the ISA interrupt handler directly is a bit of a violation + * since it doesn't actually take the same argument. For PCI, the + * argument is a void * token, but for ISA it is a unit. Since + * there is no overlap in PCI/ISA unit numbers for this driver, and + * since the ISA driver must handle the interrupt anyway, we use + * the unit number as the token even for PCI. + */ + irq_rid = 0; + irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &irq_rid, 0ul, ~0ul, 0ul, + RF_SHAREABLE | RF_ACTIVE); + if (irq_res == NULL) { + device_printf(dev, "interrupt resource allocation failed\n"); + goto fail; + } +#ifdef CY_PCI_FASTINTR + irq_setup = bus_setup_intr(dev, irq_res, INTR_TYPE_TTY | INTR_FAST, + (driver_intr_t *)cyintr, (void *)adapter, &irq_cookie); +#else + irq_setup = ENXIO; +#endif + if (irq_setup != 0) + irq_setup = bus_setup_intr(dev, irq_res, INTR_TYPE_TTY, + (driver_intr_t *)cyintr, (void *)adapter, &irq_cookie); + if (irq_setup != 0) { + device_printf(dev, "interrupt setup failed\n"); + goto fail; + } + + /* + * Enable the "local" interrupt input to generate a + * PCI interrupt. + */ + plx_ver = *((u_char *)vaddr + PLX_VER) & 0x0f; + switch (plx_ver) { + case PLX_9050: + outw(ioport + CY_PLX_9050_ICS, + CY_PLX_9050_ICS_IENABLE | CY_PLX_9050_ICS_LOCAL_IENABLE | + CY_PLX_9050_ICS_LOCAL_IPOLARITY); + break; + case PLX_9060: + case PLX_9080: + default: /* Old board, use PLX_9060 values. */ + outw(ioport + CY_PLX_9060_ICS, + inw(ioport + CY_PLX_9060_ICS) | CY_PLX_9060_ICS_IENABLE | + CY_PLX_9060_ICS_LOCAL_IENABLE); + break; + } + + return (0); + +fail: + if (ioport_res != NULL) + bus_release_resource(dev, SYS_RES_IOPORT, ioport_rid, + ioport_res); + if (irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, irq_rid, irq_res); + if (mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, mem_rid, mem_res); + return (ENXIO); +} diff --git a/sys/pci/dc21040reg.h b/sys/pci/dc21040reg.h new file mode 100644 index 0000000..c0ff191 --- /dev/null +++ b/sys/pci/dc21040reg.h @@ -0,0 +1,583 @@ +/* $NetBSD: dc21040reg.h,v 1.15 1998/05/22 18:50:59 matt Exp $ */ + +/* $FreeBSD$ */ + +/*- + * Copyright (c) 1994, 1995, 1996 Matt Thomas <matt@3am-software.com> + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Id: dc21040reg.h,v 1.24 1997/05/16 19:47:09 thomas Exp + */ + +#if !defined(_DC21040_H) +#define _DC21040_H + +#if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN +#define TULIP_BITFIELD2(a, b) b, a +#define TULIP_BITFIELD3(a, b, c) c, b, a +#define TULIP_BITFIELD4(a, b, c, d) d, c, b, a +#else +#define TULIP_BITFIELD2(a, b) a, b +#define TULIP_BITFIELD3(a, b, c) a, b, c +#define TULIP_BITFIELD4(a, b, c, d) a, b, c, d +#endif + +typedef struct { + u_int32_t d_status; + u_int32_t TULIP_BITFIELD3(d_length1 : 11, + d_length2 : 11, + d_flag : 10); + u_int32_t d_addr1; + u_int32_t d_addr2; +} tulip_desc_t; + +#define TULIP_DSTS_OWNER 0x80000000 /* Owner (1 = 21040) */ +#define TULIP_DSTS_ERRSUM 0x00008000 /* Error Summary */ +/* + * Transmit Status + */ +#define TULIP_DSTS_TxBABBLE 0x00004000 /* Transmitter Babbled */ +#define TULIP_DSTS_TxCARRLOSS 0x00000800 /* Carrier Loss */ +#define TULIP_DSTS_TxNOCARR 0x00000400 /* No Carrier */ +#define TULIP_DSTS_TxLATECOLL 0x00000200 /* Late Collision */ +#define TULIP_DSTS_TxEXCCOLL 0x00000100 /* Excessive Collisions */ +#define TULIP_DSTS_TxNOHRTBT 0x00000080 /* No Heartbeat */ +#define TULIP_DSTS_TxCOLLMASK 0x00000078 /* Collision Count (mask) */ +#define TULIP_DSTS_V_TxCOLLCNT 0x00000003 /* Collision Count (bit) */ +#define TULIP_DSTS_TxLINKFAIL 0x00000004 /* Link Failure */ +#define TULIP_DSTS_TxUNDERFLOW 0x00000002 /* Underflow Error */ +#define TULIP_DSTS_TxDEFERRED 0x00000001 /* Initially Deferred */ +/* + * Receive Status + */ +#define TULIP_DSTS_RxBADLENGTH 0x00004000 /* Length Error */ +#define TULIP_DSTS_RxDATATYPE 0x00003000 /* Data Type */ +#define TULIP_DSTS_RxRUNT 0x00000800 /* Runt Frame */ +#define TULIP_DSTS_RxMULTICAST 0x00000400 /* Multicast Frame */ +#define TULIP_DSTS_RxFIRSTDESC 0x00000200 /* First Descriptor */ +#define TULIP_DSTS_RxLASTDESC 0x00000100 /* Last Descriptor */ +#define TULIP_DSTS_RxTOOLONG 0x00000080 /* Frame Too Long */ +#define TULIP_DSTS_RxCOLLSEEN 0x00000040 /* Collision Seen */ +#define TULIP_DSTS_RxFRAMETYPE 0x00000020 /* Frame Type */ +#define TULIP_DSTS_RxWATCHDOG 0x00000010 /* Receive Watchdog */ +#define TULIP_DSTS_RxDRBBLBIT 0x00000004 /* Dribble Bit */ +#define TULIP_DSTS_RxBADCRC 0x00000002 /* CRC Error */ +#define TULIP_DSTS_RxOVERFLOW 0x00000001 /* Overflow */ + + +#define TULIP_DFLAG_ENDRING 0x0008 /* End of Transmit Ring */ +#define TULIP_DFLAG_CHAIN 0x0004 /* Chain using d_addr2 */ + +#define TULIP_DFLAG_TxWANTINTR 0x0200 /* Signal Interrupt on Completion */ +#define TULIP_DFLAG_TxLASTSEG 0x0100 /* Last Segment */ +#define TULIP_DFLAG_TxFIRSTSEG 0x0080 /* First Segment */ +#define TULIP_DFLAG_TxINVRSFILT 0x0040 /* Inverse Filtering */ +#define TULIP_DFLAG_TxSETUPPKT 0x0020 /* Setup Packet */ +#define TULIP_DFLAG_TxHASCRC 0x0010 /* Don't Append the CRC */ +#define TULIP_DFLAG_TxNOPADDING 0x0002 /* Don't AutoPad */ +#define TULIP_DFLAG_TxHASHFILT 0x0001 /* Hash/Perfect Filtering */ + +/* + * The 21040 Registers (IO Space Addresses) + */ +#define TULIP_REG_BUSMODE 0x00 /* CSR0 -- Bus Mode */ +#define TULIP_REG_TXPOLL 0x08 /* CSR1 -- Transmit Poll Demand */ +#define TULIP_REG_RXPOLL 0x10 /* CSR2 -- Receive Poll Demand */ +#define TULIP_REG_RXLIST 0x18 /* CSR3 -- Receive List Base Addr */ +#define TULIP_REG_TXLIST 0x20 /* CSR4 -- Transmit List Base Addr */ +#define TULIP_REG_STATUS 0x28 /* CSR5 -- Status */ +#define TULIP_REG_CMD 0x30 /* CSR6 -- Command */ +#define TULIP_REG_INTR 0x38 /* CSR7 -- Interrupt Control */ +#define TULIP_REG_MISSES 0x40 /* CSR8 -- Missed Frame Counter */ +#define TULIP_REG_ADDRROM 0x48 /* CSR9 -- ENET ROM Register */ +#define TULIP_REG_RSRVD 0x50 /* CSR10 -- Reserved */ +#define TULIP_REG_FULL_DUPLEX 0x58 /* CSR11 -- Full Duplex */ +#define TULIP_REG_SIA_STATUS 0x60 /* CSR12 -- SIA Status */ +#define TULIP_REG_SIA_CONN 0x68 /* CSR13 -- SIA Connectivity */ +#define TULIP_REG_SIA_TXRX 0x70 /* CSR14 -- SIA Tx Rx */ +#define TULIP_REG_SIA_GEN 0x78 /* CSR15 -- SIA General */ + +/* + * CSR5 -- Status Register + * CSR7 -- Interrupt Control + */ +#define TULIP_STS_ERRORMASK 0x03800000L /* ( R) Error Bits (Valid when SYSERROR is set) */ +#define TULIP_STS_ERR_PARITY 0x00000000L /* 000 - Parity Error (Perform Reset) */ +#define TULIP_STS_ERR_MASTER 0x00800000L /* 001 - Master Abort */ +#define TULIP_STS_ERR_TARGET 0x01000000L /* 010 - Target Abort */ +#define TULIP_STS_ERR_SHIFT 23 +#define TULIP_STS_TXSTATEMASK 0x00700000L /* ( R) Transmission Process State */ +#define TULIP_STS_TXS_RESET 0x00000000L /* 000 - Rset or transmit jabber expired */ +#define TULIP_STS_TXS_FETCH 0x00100000L /* 001 - Fetching transmit descriptor */ +#define TULIP_STS_TXS_WAITEND 0x00200000L /* 010 - Wait for end of transmission */ +#define TULIP_STS_TXS_READING 0x00300000L /* 011 - Read buffer and enqueue data */ +#define TULIP_STS_TXS_RSRVD 0x00400000L /* 100 - Reserved */ +#define TULIP_STS_TXS_SETUP 0x00500000L /* 101 - Setup Packet */ +#define TULIP_STS_TXS_SUSPEND 0x00600000L /* 110 - Transmit FIFO underflow or an + unavailable transmit descriptor */ +#define TULIP_STS_TXS_CLOSE 0x00700000L /* 111 - Close transmit descriptor */ +#define TULIP_STS_RXSTATEMASK 0x000E0000L /* ( R) Receive Process State*/ +#define TULIP_STS_RXS_STOPPED 0x00000000L /* 000 - Stopped */ +#define TULIP_STS_RXS_FETCH 0x00020000L /* 001 - Running -- Fetch receive descriptor */ +#define TULIP_STS_RXS_ENDCHECK 0x00040000L /* 010 - Running -- Check for end of receive + packet before prefetch of next descriptor */ +#define TULIP_STS_RXS_WAIT 0x00060000L /* 011 - Running -- Wait for receive packet */ +#define TULIP_STS_RXS_SUSPEND 0x00080000L /* 100 - Suspended -- As a result of + unavailable receive buffers */ +#define TULIP_STS_RXS_CLOSE 0x000A0000L /* 101 - Running -- Close receive descriptor */ +#define TULIP_STS_RXS_FLUSH 0x000C0000L /* 110 - Running -- Flush the current frame + from the receive FIFO as a result of + an unavailable receive buffer */ +#define TULIP_STS_RXS_DEQUEUE 0x000E0000L /* 111 - Running -- Dequeue the receive frame + from the receive FIFO into the receive + buffer. */ +#define TULIP_STS_NORMALINTR 0x00010000L /* (RW) Normal Interrupt */ +#define TULIP_STS_ABNRMLINTR 0x00008000L /* (RW) Abnormal Interrupt */ +#define TULIP_STS_SYSERROR 0x00002000L /* (RW) System Error */ +#define TULIP_STS_LINKFAIL 0x00001000L /* (RW) Link Failure (21040) */ +#define TULIP_STS_FULDPLXSHRT 0x00000800L /* (RW) Full Duplex Short Fram Rcvd (21040) */ +#define TULIP_STS_GPTIMEOUT 0x00000800L /* (RW) General Purpose Timeout (21140) */ +#define TULIP_STS_AUI 0x00000400L /* (RW) AUI/TP Switch (21040) */ +#define TULIP_STS_RXTIMEOUT 0x00000200L /* (RW) Receive Watchbog Timeout */ +#define TULIP_STS_RXSTOPPED 0x00000100L /* (RW) Receive Process Stopped */ +#define TULIP_STS_RXNOBUF 0x00000080L /* (RW) Receive Buffer Unavailable */ +#define TULIP_STS_RXINTR 0x00000040L /* (RW) Receive Interrupt */ +#define TULIP_STS_TXUNDERFLOW 0x00000020L /* (RW) Transmit Underflow */ +#define TULIP_STS_LINKPASS 0x00000010L /* (RW) LinkPass (21041) */ +#define TULIP_STS_TXBABBLE 0x00000008L /* (RW) Transmit Jabber Timeout */ +#define TULIP_STS_TXNOBUF 0x00000004L /* (RW) Transmit Buffer Unavailable */ +#define TULIP_STS_TXSTOPPED 0x00000002L /* (RW) Transmit Process Stopped */ +#define TULIP_STS_TXINTR 0x00000001L /* (RW) Transmit Interrupt */ + +/* + * CSR6 -- Command (Operation Mode) Register + */ +#define TULIP_CMD_MUSTBEONE 0x02000000L /* (RW) Must Be One (21140) */ +#define TULIP_CMD_SCRAMBLER 0x01000000L /* (RW) Scrambler Mode (21140) */ +#define TULIP_CMD_PCSFUNCTION 0x00800000L /* (RW) PCS Function (21140) */ +#define TULIP_CMD_TXTHRSHLDCTL 0x00400000L /* (RW) Transmit Threshold Mode (21140) */ +#define TULIP_CMD_STOREFWD 0x00200000L /* (RW) Store and Foward (21140) */ +#define TULIP_CMD_NOHEARTBEAT 0x00080000L /* (RW) No Heartbeat (21140) */ +#define TULIP_CMD_PORTSELECT 0x00040000L /* (RW) Post Select (100Mb) (21140) */ +#define TULIP_CMD_ENHCAPTEFFCT 0x00040000L /* (RW) Enhanced Capture Effecty (21041) */ +#define TULIP_CMD_CAPTREFFCT 0x00020000L /* (RW) Capture Effect (!802.3) */ +#define TULIP_CMD_BACKPRESSURE 0x00010000L /* (RW) Back Pressure (!802.3) (21040) */ +#define TULIP_CMD_THRESHOLDCTL 0x0000C000L /* (RW) Threshold Control */ +#define TULIP_CMD_THRSHLD72 0x00000000L /* 00 - 72 Bytes */ +#define TULIP_CMD_THRSHLD96 0x00004000L /* 01 - 96 Bytes */ +#define TULIP_CMD_THRSHLD128 0x00008000L /* 10 - 128 bytes */ +#define TULIP_CMD_THRSHLD160 0x0000C000L /* 11 - 160 Bytes */ +#define TULIP_CMD_TXRUN 0x00002000L /* (RW) Start/Stop Transmitter */ +#define TULIP_CMD_FORCECOLL 0x00001000L /* (RW) Force Collisions */ +#define TULIP_CMD_OPERMODE 0x00000C00L /* (RW) Operating Mode */ +#define TULIP_CMD_FULLDUPLEX 0x00000200L /* (RW) Full Duplex Mode */ +#define TULIP_CMD_FLAKYOSCDIS 0x00000100L /* (RW) Flakey Oscillator Disable */ +#define TULIP_CMD_ALLMULTI 0x00000080L /* (RW) Pass All Multicasts */ +#define TULIP_CMD_PROMISCUOUS 0x00000040L /* (RW) Promiscuous Mode */ +#define TULIP_CMD_BACKOFFCTR 0x00000020L /* (RW) Start/Stop Backoff Counter (!802.3) */ +#define TULIP_CMD_INVFILTER 0x00000010L /* (R ) Inverse Filtering */ +#define TULIP_CMD_PASSBADPKT 0x00000008L /* (RW) Pass Bad Frames */ +#define TULIP_CMD_HASHONLYFLTR 0x00000004L /* (R ) Hash Only Filtering */ +#define TULIP_CMD_RXRUN 0x00000002L /* (RW) Start/Stop Receive Filtering */ +#define TULIP_CMD_HASHPRFCTFLTR 0x00000001L /* (R ) Hash/Perfect Receive Filtering */ + +#define TULIP_SIASTS_OTHERRXACTIVITY 0x00000200L +#define TULIP_SIASTS_RXACTIVITY 0x00000100L +#define TULIP_SIASTS_LINKFAIL 0x00000004L +#define TULIP_SIASTS_LINK100FAIL 0x00000002L +#define TULIP_SIACONN_RESET 0x00000000L + +/* + * 21040 SIA definitions + */ +#define TULIP_21040_PROBE_10BASET_TIMEOUT 2500 +#define TULIP_21040_PROBE_AUIBNC_TIMEOUT 300 +#define TULIP_21040_PROBE_EXTSIA_TIMEOUT 300 + +#define TULIP_21040_SIACONN_10BASET 0x0000EF01L +#define TULIP_21040_SIATXRX_10BASET 0x0000FFFFL +#define TULIP_21040_SIAGEN_10BASET 0x00000000L + +#define TULIP_21040_SIACONN_10BASET_FD 0x0000EF01L +#define TULIP_21040_SIATXRX_10BASET_FD 0x0000FFFDL +#define TULIP_21040_SIAGEN_10BASET_FD 0x00000000L + +#define TULIP_21040_SIACONN_AUIBNC 0x0000EF09L +#define TULIP_21040_SIATXRX_AUIBNC 0x00000705L +#define TULIP_21040_SIAGEN_AUIBNC 0x00000006L + +#define TULIP_21040_SIACONN_EXTSIA 0x00003041L +#define TULIP_21040_SIATXRX_EXTSIA 0x00000000L +#define TULIP_21040_SIAGEN_EXTSIA 0x00000006L + +/* + * 21041 SIA definitions + */ + +#define TULIP_21041_PROBE_10BASET_TIMEOUT 2500 +#define TULIP_21041_PROBE_AUIBNC_TIMEOUT 300 + +#define TULIP_21041_SIACONN_10BASET 0x0000EF01L +#define TULIP_21041_SIATXRX_10BASET 0x0000FF3FL +#define TULIP_21041_SIAGEN_10BASET 0x00000000L + +#define TULIP_21041P2_SIACONN_10BASET 0x0000EF01L +#define TULIP_21041P2_SIATXRX_10BASET 0x0000FFFFL +#define TULIP_21041P2_SIAGEN_10BASET 0x00000000L + +#define TULIP_21041_SIACONN_10BASET_FD 0x0000EF01L +#define TULIP_21041_SIATXRX_10BASET_FD 0x0000FF3DL +#define TULIP_21041_SIAGEN_10BASET_FD 0x00000000L + +#define TULIP_21041P2_SIACONN_10BASET_FD 0x0000EF01L +#define TULIP_21041P2_SIATXRX_10BASET_FD 0x0000FFFFL +#define TULIP_21041P2_SIAGEN_10BASET_FD 0x00000000L + +#define TULIP_21041_SIACONN_AUI 0x0000EF09L +#define TULIP_21041_SIATXRX_AUI 0x0000F73DL +#define TULIP_21041_SIAGEN_AUI 0x0000000EL + +#define TULIP_21041P2_SIACONN_AUI 0x0000EF09L +#define TULIP_21041P2_SIATXRX_AUI 0x0000F7FDL +#define TULIP_21041P2_SIAGEN_AUI 0x0000000EL + +#define TULIP_21041_SIACONN_BNC 0x0000EF09L +#define TULIP_21041_SIATXRX_BNC 0x0000F73DL +#define TULIP_21041_SIAGEN_BNC 0x00000006L + +#define TULIP_21041P2_SIACONN_BNC 0x0000EF09L +#define TULIP_21041P2_SIATXRX_BNC 0x0000F7FDL +#define TULIP_21041P2_SIAGEN_BNC 0x00000006L + +/* + * 21142 SIA definitions + */ + +#define TULIP_21142_PROBE_10BASET_TIMEOUT 2500 +#define TULIP_21142_PROBE_AUIBNC_TIMEOUT 300 + +#define TULIP_21142_SIACONN_10BASET 0x00000001L +#define TULIP_21142_SIATXRX_10BASET 0x00007F3FL +#define TULIP_21142_SIAGEN_10BASET 0x00000008L + +#define TULIP_21142_SIACONN_10BASET_FD 0x00000001L +#define TULIP_21142_SIATXRX_10BASET_FD 0x00007F3DL +#define TULIP_21142_SIAGEN_10BASET_FD 0x00000008L + +#define TULIP_21142_SIACONN_AUI 0x00000009L +#define TULIP_21142_SIATXRX_AUI 0x00000705L +#define TULIP_21142_SIAGEN_AUI 0x0000000EL + +#define TULIP_21142_SIACONN_BNC 0x00000009L +#define TULIP_21142_SIATXRX_BNC 0x00000705L +#define TULIP_21142_SIAGEN_BNC 0x00000006L + + + + +#define TULIP_WATCHDOG_TXDISABLE 0x00000001L +#define TULIP_WATCHDOG_RXDISABLE 0x00000010L + +#define TULIP_BUSMODE_SWRESET 0x00000001L +#define TULIP_BUSMODE_DESCSKIPLEN_MASK 0x0000007CL +#define TULIP_BUSMODE_BIGENDIAN 0x00000080L +#define TULIP_BUSMODE_BURSTLEN_MASK 0x00003F00L +#define TULIP_BUSMODE_BURSTLEN_DEFAULT 0x00000000L +#define TULIP_BUSMODE_BURSTLEN_1LW 0x00000100L +#define TULIP_BUSMODE_BURSTLEN_2LW 0x00000200L +#define TULIP_BUSMODE_BURSTLEN_4LW 0x00000400L +#define TULIP_BUSMODE_BURSTLEN_8LW 0x00000800L +#define TULIP_BUSMODE_BURSTLEN_16LW 0x00001000L +#define TULIP_BUSMODE_BURSTLEN_32LW 0x00002000L +#define TULIP_BUSMODE_CACHE_NOALIGN 0x00000000L +#define TULIP_BUSMODE_CACHE_ALIGN8 0x00004000L +#define TULIP_BUSMODE_CACHE_ALIGN16 0x00008000L +#define TULIP_BUSMODE_CACHE_ALIGN32 0x0000C000L +#define TULIP_BUSMODE_TXPOLL_NEVER 0x00000000L +#define TULIP_BUSMODE_TXPOLL_200000ns 0x00020000L +#define TULIP_BUSMODE_TXPOLL_800000ns 0x00040000L +#define TULIP_BUSMODE_TXPOLL_1600000ns 0x00060000L +#define TULIP_BUSMODE_TXPOLL_12800ns 0x00080000L /* 21041 only */ +#define TULIP_BUSMODE_TXPOLL_25600ns 0x000A0000L /* 21041 only */ +#define TULIP_BUSMODE_TXPOLL_51200ns 0x000C0000L /* 21041 only */ +#define TULIP_BUSMODE_TXPOLL_102400ns 0x000E0000L /* 21041 only */ +#define TULIP_BUSMODE_DESC_BIGENDIAN 0x00100000L /* 21041 only */ +#define TULIP_BUSMODE_READMULTIPLE 0x00200000L /* */ + +#define TULIP_REG_CFDA 0x40 +#define TULIP_CFDA_SLEEP 0x80000000L +#define TULIP_CFDA_SNOOZE 0x40000000L + +#define TULIP_GP_PINSET 0x00000100L +/* + * These are the defintitions used for the DEC 21140 + * evaluation board. + */ +#define TULIP_GP_EB_PINS 0x0000001F /* General Purpose Pin directions */ +#define TULIP_GP_EB_OK10 0x00000080 /* 10 Mb/sec Signal Detect gep<7> */ +#define TULIP_GP_EB_OK100 0x00000040 /* 100 Mb/sec Signal Detect gep<6> */ +#define TULIP_GP_EB_INIT 0x0000000B /* No loopback --- point-to-point */ + +/* + * These are the defintitions used for the SMC9332 (21140) board. + */ +#define TULIP_GP_SMC_9332_PINS 0x0000003F /* General Purpose Pin directions */ +#define TULIP_GP_SMC_9332_OK10 0x00000080 /* 10 Mb/sec Signal Detect gep<7> */ +#define TULIP_GP_SMC_9332_OK100 0x00000040 /* 100 Mb/sec Signal Detect gep<6> */ +#define TULIP_GP_SMC_9332_INIT 0x00000009 /* No loopback --- point-to-point */ + +/* + * There are the definitions used for the DEC DE500 + * 10/100 family of boards + */ +#define TULIP_GP_DE500_PINS 0x0000001FL +#define TULIP_GP_DE500_LINK_PASS 0x00000080L +#define TULIP_GP_DE500_SYM_LINK 0x00000040L +#define TULIP_GP_DE500_SIGNAL_DETECT 0x00000020L +#define TULIP_GP_DE500_PHY_RESET 0x00000010L +#define TULIP_GP_DE500_HALFDUPLEX 0x00000008L +#define TULIP_GP_DE500_PHY_LOOPBACK 0x00000004L +#define TULIP_GP_DE500_FORCE_LED 0x00000002L +#define TULIP_GP_DE500_FORCE_100 0x00000001L + +/* + * These are the defintitions used for the Cogent EM100 + * 21140 board. + */ +#define TULIP_GP_EM100_PINS 0x0000003F /* General Purpose Pin directions */ +#define TULIP_GP_EM100_INIT 0x00000009 /* No loopback --- point-to-point */ +#define TULIP_COGENT_EM100TX_ID 0x12 +#define TULIP_COGENT_EM100FX_ID 0x15 + + +/* + * These are the defintitions used for the Znyx ZX342 + * 10/100 board + */ +#define TULIP_ZNYX_ID_ZX312 0x0602 +#define TULIP_ZNYX_ID_ZX312T 0x0622 +#define TULIP_ZNYX_ID_ZX314_INTA 0x0701 +#define TULIP_ZNYX_ID_ZX314 0x0711 +#define TULIP_ZNYX_ID_ZX315_INTA 0x0801 +#define TULIP_ZNYX_ID_ZX315 0x0811 +#define TULIP_ZNYX_ID_ZX342 0x0901 +#define TULIP_ZNYX_ID_ZX342B 0x0921 +#define TULIP_ZNYX_ID_ZX342_X3 0x0902 +#define TULIP_ZNYX_ID_ZX342_X4 0x0903 +#define TULIP_ZNYX_ID_ZX344 0x0A01 +#define TULIP_ZNYX_ID_ZX351 0x0B01 +#define TULIP_ZNYX_ID_ZX345 0x0C01 +#define TULIP_ZNYX_ID_ZX311 0x0D01 +#define TULIP_ZNYX_ID_ZX346 0x0E01 + +#define TULIP_GP_ZX34X_PINS 0x0000001F /* General Purpose Pin directions */ +#define TULIP_GP_ZX344_PINS 0x0000000B /* General Purpose Pin directions */ +#define TULIP_GP_ZX345_PINS 0x00000003 /* General Purpose Pin directions */ +#define TULIP_GP_ZX346_PINS 0x00000043 /* General Purpose Pin directions */ +#define TULIP_GP_ZX34X_LNKFAIL 0x00000080 /* 10Mb/s Link Failure */ +#define TULIP_GP_ZX34X_SYMDET 0x00000040 /* 100Mb/s Symbol Detect */ +#define TULIP_GP_ZX345_PHYACT 0x00000040 /* PHY Activity */ +#define TULIP_GP_ZX34X_SIGDET 0x00000020 /* 100Mb/s Signal Detect */ +#define TULIP_GP_ZX346_AUTONEG_ENABLED 0x00000020 /* 802.3u autoneg enabled */ +#define TULIP_GP_ZX342_COLENA 0x00000008 /* 10t Ext LB */ +#define TULIP_GP_ZX344_ROTINT 0x00000008 /* PPB IRQ rotation */ +#define TULIP_GP_ZX345_SPEED10 0x00000008 /* 10Mb speed detect */ +#define TULIP_GP_ZX346_SPEED100 0x00000008 /* 100Mb speed detect */ +#define TULIP_GP_ZX34X_NCOLENA 0x00000004 /* 10t Int LB */ +#define TULIP_GP_ZX34X_RXMATCH 0x00000004 /* RX Match */ +#define TULIP_GP_ZX346_FULLDUPLEX 0x00000004 /* Full Duplex Sensed */ +#define TULIP_GP_ZX34X_LB102 0x00000002 /* 100tx twister LB */ +#define TULIP_GP_ZX34X_NLB101 0x00000001 /* PDT/PDR LB */ +#define TULIP_GP_ZX34X_INIT 0x00000009 + +/* + * Asante's stuff... + */ +#define TULIP_GP_ASANTE_PINS 0x000000bf /* GP pin config */ +#define TULIP_GP_ASANTE_PHYRESET 0x00000008 /* Reset PHY */ + +/* + * ACCTON EN1207 specialties + */ + +#define TULIP_CSR8_EN1207 0x08 +#define TULIP_CSR9_EN1207 0x00 +#define TULIP_CSR10_EN1207 0x03 +#define TULIP_CSR11_EN1207 0x1F + +#define TULIP_GP_EN1207_BNC_INIT 0x0000011B +#define TULIP_GP_EN1207_UTP_INIT 0x9E00000B +#define TULIP_GP_EN1207_100_INIT 0x6D00031B + +/* + * SROM definitions for the 21140 and 21041. + */ +#define SROMXREG 0x0400 +#define SROMSEL 0x0800 +#define SROMRD 0x4000 +#define SROMWR 0x2000 +#define SROMDIN 0x0008 +#define SROMDOUT 0x0004 +#define SROMDOUTON 0x0004 +#define SROMDOUTOFF 0x0004 +#define SROMCLKON 0x0002 +#define SROMCLKOFF 0x0002 +#define SROMCSON 0x0001 +#define SROMCSOFF 0x0001 +#define SROMCS 0x0001 + +#define SROMCMD_MODE 4 +#define SROMCMD_WR 5 +#define SROMCMD_RD 6 + +#define SROM_BITWIDTH 6 + +/* + * MII Definitions for the 21041 and 21140/21140A/21142 + */ +#define MII_PREAMBLE (~0) +#define MII_TEST 0xAAAAAAAA +#define MII_RDCMD 0xF6 /* 1111.0110 */ +#define MII_WRCMD 0xF5 /* 1111.0101 */ +#define MII_DIN 0x00080000 +#define MII_RD 0x00040000 +#define MII_WR 0x00000000 +#define MII_DOUT 0x00020000 +#define MII_CLK 0x00010000 +#define MII_CLKON MII_CLK +#define MII_CLKOFF MII_CLK + +#define PHYREG_CONTROL 0 +#define PHYREG_STATUS 1 +#define PHYREG_IDLOW 2 +#define PHYREG_IDHIGH 3 +#define PHYREG_AUTONEG_ADVERTISEMENT 4 +#define PHYREG_AUTONEG_ABILITIES 5 +#define PHYREG_AUTONEG_EXPANSION 6 +#define PHYREG_AUTONEG_NEXTPAGE 7 + +#define PHYSTS_100BASET4 0x8000 +#define PHYSTS_100BASETX_FD 0x4000 +#define PHYSTS_100BASETX 0x2000 +#define PHYSTS_10BASET_FD 0x1000 +#define PHYSTS_10BASET 0x0800 +#define PHYSTS_AUTONEG_DONE 0x0020 +#define PHYSTS_REMOTE_FAULT 0x0010 +#define PHYSTS_CAN_AUTONEG 0x0008 +#define PHYSTS_LINK_UP 0x0004 +#define PHYSTS_JABBER_DETECT 0x0002 +#define PHYSTS_EXTENDED_REGS 0x0001 + +#define PHYCTL_RESET 0x8000 +#define PHYCTL_SELECT_100MB 0x2000 +#define PHYCTL_AUTONEG_ENABLE 0x1000 +#define PHYCTL_ISOLATE 0x0400 +#define PHYCTL_AUTONEG_RESTART 0x0200 +#define PHYCTL_FULL_DUPLEX 0x0100 + +/* + * Definitions for the DE425. + */ +#define DE425_CFID 0x08 /* Configuration Id */ +#define DE425_CFCS 0x0C /* Configuration Command-Status */ +#define DE425_CFRV 0x18 /* Configuration Revision */ +#define DE425_CFLT 0x1C /* Configuration Latency Timer */ +#define DE425_CBIO 0x28 /* Configuration Base IO Address */ +#define DE425_CFDA 0x2C /* Configuration Driver Area */ +#define DE425_ENETROM_OFFSET 0xC90 /* Offset in I/O space for ENETROM */ +#define DE425_CFG0 0xC88 /* IRQ register */ +#define DE425_EISAID 0x10a34250 /* EISA device id */ +#define DE425_EISA_IOSIZE 0x100 + +#define DEC_VENDORID 0x1011 +#define CHIPID_21040 0x0002 +#define CHIPID_21140 0x0009 +#define CHIPID_21041 0x0014 +#define CHIPID_21142 0x0019 +#define PCI_VENDORID(x) ((x) & 0xFFFF) +#define PCI_CHIPID(x) (((x) >> 16) & 0xFFFF) + +/* + * Generic SROM Format + * + * + */ + +typedef struct { + u_int8_t sh_idbuf[18]; + u_int8_t sh_version; + u_int8_t sh_adapter_count; + u_int8_t sh_ieee802_address[6]; +} tulip_srom_header_t; + +typedef struct { + u_int8_t sai_device; + u_int8_t sai_leaf_offset_lowbyte; + u_int8_t sai_leaf_offset_highbyte; +} tulip_srom_adapter_info_t; + +typedef enum { + TULIP_SROM_CONNTYPE_10BASET =0x0000, + TULIP_SROM_CONNTYPE_BNC =0x0001, + TULIP_SROM_CONNTYPE_AUI =0x0002, + TULIP_SROM_CONNTYPE_100BASETX =0x0003, + TULIP_SROM_CONNTYPE_100BASET4 =0x0006, + TULIP_SROM_CONNTYPE_100BASEFX =0x0007, + TULIP_SROM_CONNTYPE_MII_10BASET =0x0009, + TULIP_SROM_CONNTYPE_MII_100BASETX =0x000D, + TULIP_SROM_CONNTYPE_MII_100BASET4 =0x000F, + TULIP_SROM_CONNTYPE_MII_100BASEFX =0x0010, + TULIP_SROM_CONNTYPE_10BASET_NWAY =0x0100, + TULIP_SROM_CONNTYPE_10BASET_FD =0x0204, + TULIP_SROM_CONNTYPE_MII_10BASET_FD =0x020A, + TULIP_SROM_CONNTYPE_100BASETX_FD =0x020E, + TULIP_SROM_CONNTYPE_MII_100BASETX_FD =0x0211, + TULIP_SROM_CONNTYPE_10BASET_NOLINKPASS =0x0400, + TULIP_SROM_CONNTYPE_AUTOSENSE =0x0800, + TULIP_SROM_CONNTYPE_AUTOSENSE_POWERUP =0x8800, + TULIP_SROM_CONNTYPE_AUTOSENSE_NWAY =0x9000, + TULIP_SROM_CONNTYPE_NOT_USED =0xFFFF +} tulip_srom_connection_t; + +typedef enum { + TULIP_SROM_MEDIA_10BASET =0x0000, + TULIP_SROM_MEDIA_BNC =0x0001, + TULIP_SROM_MEDIA_AUI =0x0002, + TULIP_SROM_MEDIA_100BASETX =0x0003, + TULIP_SROM_MEDIA_10BASET_FD =0x0004, + TULIP_SROM_MEDIA_100BASETX_FD =0x0005, + TULIP_SROM_MEDIA_100BASET4 =0x0006, + TULIP_SROM_MEDIA_100BASEFX =0x0007, + TULIP_SROM_MEDIA_100BASEFX_FD =0x0008 +} tulip_srom_media_t; + +#define TULIP_SROM_21041_EXTENDED 0x40 + +#define TULIP_SROM_2114X_NOINDICATOR 0x8000 +#define TULIP_SROM_2114X_DEFAULT 0x4000 +#define TULIP_SROM_2114X_POLARITY 0x0080 +#define TULIP_SROM_2114X_CMDBITS(n) (((n) & 0x0071) << 18) +#define TULIP_SROM_2114X_BITPOS(b) (1 << (((b) & 0x0E) >> 1)) + + + +#endif /* !defined(_DC21040_H) */ diff --git a/sys/pci/if_dc.c b/sys/pci/if_dc.c new file mode 100644 index 0000000..189835c --- /dev/null +++ b/sys/pci/if_dc.c @@ -0,0 +1,3619 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ee.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * DEC "tulip" clone ethernet driver. Supports the DEC/Intel 21143 + * series chips and several workalikes including the following: + * + * Macronix 98713/98715/98725/98727/98732 PMAC (www.macronix.com) + * Macronix/Lite-On 82c115 PNIC II (www.macronix.com) + * Lite-On 82c168/82c169 PNIC (www.litecom.com) + * ASIX Electronics AX88140A (www.asix.com.tw) + * ASIX Electronics AX88141 (www.asix.com.tw) + * ADMtek AL981 (www.admtek.com.tw) + * ADMtek AN985 (www.admtek.com.tw) + * Davicom DM9100, DM9102, DM9102A (www.davicom8.com) + * Accton EN1217 (www.accton.com) + * Xircom X3201 (www.xircom.com) + * Abocom FE2500 + * Conexant LANfinity (www.conexant.com) + * + * Datasheets for the 21143 are available at developer.intel.com. + * Datasheets for the clone parts can be found at their respective sites. + * (Except for the PNIC; see www.freebsd.org/~wpaul/PNIC/pnic.ps.gz.) + * The PNIC II is essentially a Macronix 98715A chip; the only difference + * worth noting is that its multicast hash table is only 128 bits wide + * instead of 512. + * + * Written by Bill Paul <wpaul@ee.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * The Intel 21143 is the successor to the DEC 21140. It is basically + * the same as the 21140 but with a few new features. The 21143 supports + * three kinds of media attachments: + * + * o MII port, for 10Mbps and 100Mbps support and NWAY + * autonegotiation provided by an external PHY. + * o SYM port, for symbol mode 100Mbps support. + * o 10baseT port. + * o AUI/BNC port. + * + * The 100Mbps SYM port and 10baseT port can be used together in + * combination with the internal NWAY support to create a 10/100 + * autosensing configuration. + * + * Note that not all tulip workalikes are handled in this driver: we only + * deal with those which are relatively well behaved. The Winbond is + * handled separately due to its different register offsets and the + * special handling needed for its various bugs. The PNIC is handled + * here, but I'm not thrilled about it. + * + * All of the workalike chips use some form of MII transceiver support + * with the exception of the Macronix chips, which also have a SYM port. + * The ASIX AX88140A is also documented to have a SYM port, but all + * the cards I've seen use an MII transceiver, probably because the + * AX88140A doesn't support internal NWAY. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/if_vlan_var.h> + +#include <net/bpf.h> + +#include <vm/vm.h> /* for vtophys */ +#include <vm/pmap.h> /* for vtophys */ +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +#define DC_USEIOSPACE +#ifdef __alpha__ +#define SRM_MEDIA +#endif + +#include <pci/if_dcreg.h> + +MODULE_DEPEND(dc, miibus, 1, 1, 1); + +/* "controller miibus0" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif + +/* + * Various supported device vendors/types and their names. + */ +static struct dc_type dc_devs[] = { + { DC_VENDORID_DEC, DC_DEVICEID_21143, + "Intel 21143 10/100BaseTX" }, + { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9100, + "Davicom DM9100 10/100BaseTX" }, + { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9102, + "Davicom DM9102 10/100BaseTX" }, + { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9102, + "Davicom DM9102A 10/100BaseTX" }, + { DC_VENDORID_ADMTEK, DC_DEVICEID_AL981, + "ADMtek AL981 10/100BaseTX" }, + { DC_VENDORID_ADMTEK, DC_DEVICEID_AN985, + "ADMtek AN985 10/100BaseTX" }, + { DC_VENDORID_ASIX, DC_DEVICEID_AX88140A, + "ASIX AX88140A 10/100BaseTX" }, + { DC_VENDORID_ASIX, DC_DEVICEID_AX88140A, + "ASIX AX88141 10/100BaseTX" }, + { DC_VENDORID_MX, DC_DEVICEID_98713, + "Macronix 98713 10/100BaseTX" }, + { DC_VENDORID_MX, DC_DEVICEID_98713, + "Macronix 98713A 10/100BaseTX" }, + { DC_VENDORID_CP, DC_DEVICEID_98713_CP, + "Compex RL100-TX 10/100BaseTX" }, + { DC_VENDORID_CP, DC_DEVICEID_98713_CP, + "Compex RL100-TX 10/100BaseTX" }, + { DC_VENDORID_MX, DC_DEVICEID_987x5, + "Macronix 98715/98715A 10/100BaseTX" }, + { DC_VENDORID_MX, DC_DEVICEID_987x5, + "Macronix 98715AEC-C 10/100BaseTX" }, + { DC_VENDORID_MX, DC_DEVICEID_987x5, + "Macronix 98725 10/100BaseTX" }, + { DC_VENDORID_MX, DC_DEVICEID_98727, + "Macronix 98727/98732 10/100BaseTX" }, + { DC_VENDORID_LO, DC_DEVICEID_82C115, + "LC82C115 PNIC II 10/100BaseTX" }, + { DC_VENDORID_LO, DC_DEVICEID_82C168, + "82c168 PNIC 10/100BaseTX" }, + { DC_VENDORID_LO, DC_DEVICEID_82C168, + "82c169 PNIC 10/100BaseTX" }, + { DC_VENDORID_ACCTON, DC_DEVICEID_EN1217, + "Accton EN1217 10/100BaseTX" }, + { DC_VENDORID_ACCTON, DC_DEVICEID_EN2242, + "Accton EN2242 MiniPCI 10/100BaseTX" }, + { DC_VENDORID_XIRCOM, DC_DEVICEID_X3201, + "Xircom X3201 10/100BaseTX" }, + { DC_VENDORID_ABOCOM, DC_DEVICEID_FE2500, + "Abocom FE2500 10/100BaseTX" }, + { DC_VENDORID_CONEXANT, DC_DEVICEID_RS7112, + "Conexant LANfinity MiniPCI 10/100BaseTX" }, + { 0, 0, NULL } +}; + +static int dc_probe (device_t); +static int dc_attach (device_t); +static int dc_detach (device_t); +static int dc_suspend (device_t); +static int dc_resume (device_t); +static void dc_acpi (device_t); +static struct dc_type *dc_devtype (device_t); +static int dc_newbuf (struct dc_softc *, int, struct mbuf *); +static int dc_encap (struct dc_softc *, struct mbuf *, u_int32_t *); +static int dc_coal (struct dc_softc *, struct mbuf **); +static void dc_pnic_rx_bug_war (struct dc_softc *, int); +static int dc_rx_resync (struct dc_softc *); +static void dc_rxeof (struct dc_softc *); +static void dc_txeof (struct dc_softc *); +static void dc_tick (void *); +static void dc_tx_underrun (struct dc_softc *); +static void dc_intr (void *); +static void dc_start (struct ifnet *); +static int dc_ioctl (struct ifnet *, u_long, caddr_t); +static void dc_init (void *); +static void dc_stop (struct dc_softc *); +static void dc_watchdog (struct ifnet *); +static void dc_shutdown (device_t); +static int dc_ifmedia_upd (struct ifnet *); +static void dc_ifmedia_sts (struct ifnet *, struct ifmediareq *); + +static void dc_delay (struct dc_softc *); +static void dc_eeprom_idle (struct dc_softc *); +static void dc_eeprom_putbyte (struct dc_softc *, int); +static void dc_eeprom_getword (struct dc_softc *, int, u_int16_t *); +static void dc_eeprom_getword_pnic + (struct dc_softc *, int, u_int16_t *); +static void dc_eeprom_getword_xircom + (struct dc_softc *, int, u_int16_t *); +static void dc_read_eeprom (struct dc_softc *, caddr_t, int, int, int); + +static void dc_mii_writebit (struct dc_softc *, int); +static int dc_mii_readbit (struct dc_softc *); +static void dc_mii_sync (struct dc_softc *); +static void dc_mii_send (struct dc_softc *, u_int32_t, int); +static int dc_mii_readreg (struct dc_softc *, struct dc_mii_frame *); +static int dc_mii_writereg (struct dc_softc *, struct dc_mii_frame *); +static int dc_miibus_readreg (device_t, int, int); +static int dc_miibus_writereg (device_t, int, int, int); +static void dc_miibus_statchg (device_t); +static void dc_miibus_mediainit (device_t); + +static void dc_setcfg (struct dc_softc *, int); +static u_int32_t dc_crc_le (struct dc_softc *, caddr_t); +static u_int32_t dc_crc_be (caddr_t); +static void dc_setfilt_21143 (struct dc_softc *); +static void dc_setfilt_asix (struct dc_softc *); +static void dc_setfilt_admtek (struct dc_softc *); +static void dc_setfilt_xircom (struct dc_softc *); + +static void dc_setfilt (struct dc_softc *); + +static void dc_reset (struct dc_softc *); +static int dc_list_rx_init (struct dc_softc *); +static int dc_list_tx_init (struct dc_softc *); + +static void dc_parse_21143_srom (struct dc_softc *); +static void dc_decode_leaf_sia (struct dc_softc *, struct dc_eblock_sia *); +static void dc_decode_leaf_mii (struct dc_softc *, struct dc_eblock_mii *); +static void dc_decode_leaf_sym (struct dc_softc *, struct dc_eblock_sym *); +static void dc_apply_fixup (struct dc_softc *, int); + +#ifdef DC_USEIOSPACE +#define DC_RES SYS_RES_IOPORT +#define DC_RID DC_PCI_CFBIO +#else +#define DC_RES SYS_RES_MEMORY +#define DC_RID DC_PCI_CFBMA +#endif + +static device_method_t dc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dc_probe), + DEVMETHOD(device_attach, dc_attach), + DEVMETHOD(device_detach, dc_detach), + DEVMETHOD(device_suspend, dc_suspend), + DEVMETHOD(device_resume, dc_resume), + DEVMETHOD(device_shutdown, dc_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, dc_miibus_readreg), + DEVMETHOD(miibus_writereg, dc_miibus_writereg), + DEVMETHOD(miibus_statchg, dc_miibus_statchg), + DEVMETHOD(miibus_mediainit, dc_miibus_mediainit), + + { 0, 0 } +}; + +static driver_t dc_driver = { + "dc", + dc_methods, + sizeof(struct dc_softc) +}; + +static devclass_t dc_devclass; +#ifdef __i386__ +static int dc_quick=1; +SYSCTL_INT(_hw, OID_AUTO, dc_quick, CTLFLAG_RW, + &dc_quick,0,"do not mdevget in dc driver"); +#endif + +DRIVER_MODULE(if_dc, cardbus, dc_driver, dc_devclass, 0, 0); +DRIVER_MODULE(if_dc, pci, dc_driver, dc_devclass, 0, 0); +DRIVER_MODULE(miibus, dc, miibus_driver, miibus_devclass, 0, 0); + +#define DC_SETBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | (x)) + +#define DC_CLRBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~(x)) + +#define SIO_SET(x) DC_SETBIT(sc, DC_SIO, (x)) +#define SIO_CLR(x) DC_CLRBIT(sc, DC_SIO, (x)) + +#define IS_MPSAFE 0 + +static void dc_delay(sc) + struct dc_softc *sc; +{ + int idx; + + for (idx = (300 / 33) + 1; idx > 0; idx--) + CSR_READ_4(sc, DC_BUSCTL); +} + +static void dc_eeprom_idle(sc) + struct dc_softc *sc; +{ + register int i; + + CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL); + dc_delay(sc); + DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ); + dc_delay(sc); + DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); + dc_delay(sc); + DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS); + dc_delay(sc); + + for (i = 0; i < 25; i++) { + DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); + dc_delay(sc); + DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK); + dc_delay(sc); + } + + DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); + dc_delay(sc); + DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CS); + dc_delay(sc); + CSR_WRITE_4(sc, DC_SIO, 0x00000000); + + return; +} + +/* + * Send a read command and address to the EEPROM, check for ACK. + */ +static void dc_eeprom_putbyte(sc, addr) + struct dc_softc *sc; + int addr; +{ + register int d, i; + + /* + * The AN985 has a 93C66 EEPROM on it instead of + * a 93C46. It uses a different bit sequence for + * specifying the "read" opcode. + */ + if (DC_IS_CENTAUR(sc) || DC_IS_CONEXANT(sc)) + d = addr | (DC_EECMD_READ << 2); + else + d = addr | DC_EECMD_READ; + + /* + * Feed in each bit and strobe the clock. + */ + for (i = 0x400; i; i >>= 1) { + if (d & i) { + SIO_SET(DC_SIO_EE_DATAIN); + } else { + SIO_CLR(DC_SIO_EE_DATAIN); + } + dc_delay(sc); + SIO_SET(DC_SIO_EE_CLK); + dc_delay(sc); + SIO_CLR(DC_SIO_EE_CLK); + dc_delay(sc); + } + + return; +} + +/* + * Read a word of data stored in the EEPROM at address 'addr.' + * The PNIC 82c168/82c169 has its own non-standard way to read + * the EEPROM. + */ +static void dc_eeprom_getword_pnic(sc, addr, dest) + struct dc_softc *sc; + int addr; + u_int16_t *dest; +{ + register int i; + u_int32_t r; + + CSR_WRITE_4(sc, DC_PN_SIOCTL, DC_PN_EEOPCODE_READ|addr); + + for (i = 0; i < DC_TIMEOUT; i++) { + DELAY(1); + r = CSR_READ_4(sc, DC_SIO); + if (!(r & DC_PN_SIOCTL_BUSY)) { + *dest = (u_int16_t)(r & 0xFFFF); + return; + } + } + + return; +} + +/* + * Read a word of data stored in the EEPROM at address 'addr.' + * The Xircom X3201 has its own non-standard way to read + * the EEPROM, too. + */ +static void dc_eeprom_getword_xircom(sc, addr, dest) + struct dc_softc *sc; + int addr; + u_int16_t *dest; +{ + SIO_SET(DC_SIO_ROMSEL | DC_SIO_ROMCTL_READ); + + addr *= 2; + CSR_WRITE_4(sc, DC_ROM, addr | 0x160); + *dest = (u_int16_t)CSR_READ_4(sc, DC_SIO)&0xff; + addr += 1; + CSR_WRITE_4(sc, DC_ROM, addr | 0x160); + *dest |= ((u_int16_t)CSR_READ_4(sc, DC_SIO)&0xff) << 8; + + SIO_CLR(DC_SIO_ROMSEL | DC_SIO_ROMCTL_READ); + return; +} + +/* + * Read a word of data stored in the EEPROM at address 'addr.' + */ +static void dc_eeprom_getword(sc, addr, dest) + struct dc_softc *sc; + int addr; + u_int16_t *dest; +{ + register int i; + u_int16_t word = 0; + + /* Force EEPROM to idle state. */ + dc_eeprom_idle(sc); + + /* Enter EEPROM access mode. */ + CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL); + dc_delay(sc); + DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ); + dc_delay(sc); + DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); + dc_delay(sc); + DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS); + dc_delay(sc); + + /* + * Send address of word we want to read. + */ + dc_eeprom_putbyte(sc, addr); + + /* + * Start reading bits from EEPROM. + */ + for (i = 0x8000; i; i >>= 1) { + SIO_SET(DC_SIO_EE_CLK); + dc_delay(sc); + if (CSR_READ_4(sc, DC_SIO) & DC_SIO_EE_DATAOUT) + word |= i; + dc_delay(sc); + SIO_CLR(DC_SIO_EE_CLK); + dc_delay(sc); + } + + /* Turn off EEPROM access mode. */ + dc_eeprom_idle(sc); + + *dest = word; + + return; +} + +/* + * Read a sequence of words from the EEPROM. + */ +static void dc_read_eeprom(sc, dest, off, cnt, swap) + struct dc_softc *sc; + caddr_t dest; + int off; + int cnt; + int swap; +{ + int i; + u_int16_t word = 0, *ptr; + + for (i = 0; i < cnt; i++) { + if (DC_IS_PNIC(sc)) + dc_eeprom_getword_pnic(sc, off + i, &word); + else if (DC_IS_XIRCOM(sc)) + dc_eeprom_getword_xircom(sc, off + i, &word); + else + dc_eeprom_getword(sc, off + i, &word); + ptr = (u_int16_t *)(dest + (i * 2)); + if (swap) + *ptr = ntohs(word); + else + *ptr = word; + } + + return; +} + +/* + * The following two routines are taken from the Macronix 98713 + * Application Notes pp.19-21. + */ +/* + * Write a bit to the MII bus. + */ +static void dc_mii_writebit(sc, bit) + struct dc_softc *sc; + int bit; +{ + if (bit) + CSR_WRITE_4(sc, DC_SIO, + DC_SIO_ROMCTL_WRITE|DC_SIO_MII_DATAOUT); + else + CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_WRITE); + + DC_SETBIT(sc, DC_SIO, DC_SIO_MII_CLK); + DC_CLRBIT(sc, DC_SIO, DC_SIO_MII_CLK); + + return; +} + +/* + * Read a bit from the MII bus. + */ +static int dc_mii_readbit(sc) + struct dc_softc *sc; +{ + CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_READ|DC_SIO_MII_DIR); + CSR_READ_4(sc, DC_SIO); + DC_SETBIT(sc, DC_SIO, DC_SIO_MII_CLK); + DC_CLRBIT(sc, DC_SIO, DC_SIO_MII_CLK); + if (CSR_READ_4(sc, DC_SIO) & DC_SIO_MII_DATAIN) + return(1); + + return(0); +} + +/* + * Sync the PHYs by setting data bit and strobing the clock 32 times. + */ +static void dc_mii_sync(sc) + struct dc_softc *sc; +{ + register int i; + + CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_WRITE); + + for (i = 0; i < 32; i++) + dc_mii_writebit(sc, 1); + + return; +} + +/* + * Clock a series of bits through the MII. + */ +static void dc_mii_send(sc, bits, cnt) + struct dc_softc *sc; + u_int32_t bits; + int cnt; +{ + int i; + + for (i = (0x1 << (cnt - 1)); i; i >>= 1) + dc_mii_writebit(sc, bits & i); +} + +/* + * Read an PHY register through the MII. + */ +static int dc_mii_readreg(sc, frame) + struct dc_softc *sc; + struct dc_mii_frame *frame; + +{ + int i, ack; + + DC_LOCK(sc); + + /* + * Set up frame for RX. + */ + frame->mii_stdelim = DC_MII_STARTDELIM; + frame->mii_opcode = DC_MII_READOP; + frame->mii_turnaround = 0; + frame->mii_data = 0; + + /* + * Sync the PHYs. + */ + dc_mii_sync(sc); + + /* + * Send command/address info. + */ + dc_mii_send(sc, frame->mii_stdelim, 2); + dc_mii_send(sc, frame->mii_opcode, 2); + dc_mii_send(sc, frame->mii_phyaddr, 5); + dc_mii_send(sc, frame->mii_regaddr, 5); + +#ifdef notdef + /* Idle bit */ + dc_mii_writebit(sc, 1); + dc_mii_writebit(sc, 0); +#endif + + /* Check for ack */ + ack = dc_mii_readbit(sc); + + /* + * Now try reading data bits. If the ack failed, we still + * need to clock through 16 cycles to keep the PHY(s) in sync. + */ + if (ack) { + for(i = 0; i < 16; i++) { + dc_mii_readbit(sc); + } + goto fail; + } + + for (i = 0x8000; i; i >>= 1) { + if (!ack) { + if (dc_mii_readbit(sc)) + frame->mii_data |= i; + } + } + +fail: + + dc_mii_writebit(sc, 0); + dc_mii_writebit(sc, 0); + + DC_UNLOCK(sc); + + if (ack) + return(1); + return(0); +} + +/* + * Write to a PHY register through the MII. + */ +static int dc_mii_writereg(sc, frame) + struct dc_softc *sc; + struct dc_mii_frame *frame; + +{ + DC_LOCK(sc); + /* + * Set up frame for TX. + */ + + frame->mii_stdelim = DC_MII_STARTDELIM; + frame->mii_opcode = DC_MII_WRITEOP; + frame->mii_turnaround = DC_MII_TURNAROUND; + + /* + * Sync the PHYs. + */ + dc_mii_sync(sc); + + dc_mii_send(sc, frame->mii_stdelim, 2); + dc_mii_send(sc, frame->mii_opcode, 2); + dc_mii_send(sc, frame->mii_phyaddr, 5); + dc_mii_send(sc, frame->mii_regaddr, 5); + dc_mii_send(sc, frame->mii_turnaround, 2); + dc_mii_send(sc, frame->mii_data, 16); + + /* Idle bit. */ + dc_mii_writebit(sc, 0); + dc_mii_writebit(sc, 0); + + DC_UNLOCK(sc); + + return(0); +} + +static int dc_miibus_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct dc_mii_frame frame; + struct dc_softc *sc; + int i, rval, phy_reg = 0; + + sc = device_get_softc(dev); + bzero((char *)&frame, sizeof(frame)); + + /* + * Note: both the AL981 and AN985 have internal PHYs, + * however the AL981 provides direct access to the PHY + * registers while the AN985 uses a serial MII interface. + * The AN985's MII interface is also buggy in that you + * can read from any MII address (0 to 31), but only address 1 + * behaves normally. To deal with both cases, we pretend + * that the PHY is at MII address 1. + */ + if (DC_IS_ADMTEK(sc) && phy != DC_ADMTEK_PHYADDR) + return(0); + + /* + * Note: the ukphy probes of the RS7112 report a PHY at + * MII address 0 (possibly HomePNA?) and 1 (ethernet) + * so we only respond to correct one. + */ + if (DC_IS_CONEXANT(sc) && phy != DC_CONEXANT_PHYADDR) + return(0); + + if (sc->dc_pmode != DC_PMODE_MII) { + if (phy == (MII_NPHY - 1)) { + switch(reg) { + case MII_BMSR: + /* + * Fake something to make the probe + * code think there's a PHY here. + */ + return(BMSR_MEDIAMASK); + break; + case MII_PHYIDR1: + if (DC_IS_PNIC(sc)) + return(DC_VENDORID_LO); + return(DC_VENDORID_DEC); + break; + case MII_PHYIDR2: + if (DC_IS_PNIC(sc)) + return(DC_DEVICEID_82C168); + return(DC_DEVICEID_21143); + break; + default: + return(0); + break; + } + } else + return(0); + } + + if (DC_IS_PNIC(sc)) { + CSR_WRITE_4(sc, DC_PN_MII, DC_PN_MIIOPCODE_READ | + (phy << 23) | (reg << 18)); + for (i = 0; i < DC_TIMEOUT; i++) { + DELAY(1); + rval = CSR_READ_4(sc, DC_PN_MII); + if (!(rval & DC_PN_MII_BUSY)) { + rval &= 0xFFFF; + return(rval == 0xFFFF ? 0 : rval); + } + } + return(0); + } + + if (DC_IS_COMET(sc)) { + switch(reg) { + case MII_BMCR: + phy_reg = DC_AL_BMCR; + break; + case MII_BMSR: + phy_reg = DC_AL_BMSR; + break; + case MII_PHYIDR1: + phy_reg = DC_AL_VENID; + break; + case MII_PHYIDR2: + phy_reg = DC_AL_DEVID; + break; + case MII_ANAR: + phy_reg = DC_AL_ANAR; + break; + case MII_ANLPAR: + phy_reg = DC_AL_LPAR; + break; + case MII_ANER: + phy_reg = DC_AL_ANER; + break; + default: + printf("dc%d: phy_read: bad phy register %x\n", + sc->dc_unit, reg); + return(0); + break; + } + + rval = CSR_READ_4(sc, phy_reg) & 0x0000FFFF; + + if (rval == 0xFFFF) + return(0); + return(rval); + } + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + if (sc->dc_type == DC_TYPE_98713) { + phy_reg = CSR_READ_4(sc, DC_NETCFG); + CSR_WRITE_4(sc, DC_NETCFG, phy_reg & ~DC_NETCFG_PORTSEL); + } + dc_mii_readreg(sc, &frame); + if (sc->dc_type == DC_TYPE_98713) + CSR_WRITE_4(sc, DC_NETCFG, phy_reg); + + return(frame.mii_data); +} + +static int dc_miibus_writereg(dev, phy, reg, data) + device_t dev; + int phy, reg, data; +{ + struct dc_softc *sc; + struct dc_mii_frame frame; + int i, phy_reg = 0; + + sc = device_get_softc(dev); + bzero((char *)&frame, sizeof(frame)); + + if (DC_IS_ADMTEK(sc) && phy != DC_ADMTEK_PHYADDR) + return(0); + + if (DC_IS_CONEXANT(sc) && phy != DC_CONEXANT_PHYADDR) + return(0); + + if (DC_IS_PNIC(sc)) { + CSR_WRITE_4(sc, DC_PN_MII, DC_PN_MIIOPCODE_WRITE | + (phy << 23) | (reg << 10) | data); + for (i = 0; i < DC_TIMEOUT; i++) { + if (!(CSR_READ_4(sc, DC_PN_MII) & DC_PN_MII_BUSY)) + break; + } + return(0); + } + + if (DC_IS_COMET(sc)) { + switch(reg) { + case MII_BMCR: + phy_reg = DC_AL_BMCR; + break; + case MII_BMSR: + phy_reg = DC_AL_BMSR; + break; + case MII_PHYIDR1: + phy_reg = DC_AL_VENID; + break; + case MII_PHYIDR2: + phy_reg = DC_AL_DEVID; + break; + case MII_ANAR: + phy_reg = DC_AL_ANAR; + break; + case MII_ANLPAR: + phy_reg = DC_AL_LPAR; + break; + case MII_ANER: + phy_reg = DC_AL_ANER; + break; + default: + printf("dc%d: phy_write: bad phy register %x\n", + sc->dc_unit, reg); + return(0); + break; + } + + CSR_WRITE_4(sc, phy_reg, data); + return(0); + } + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + frame.mii_data = data; + + if (sc->dc_type == DC_TYPE_98713) { + phy_reg = CSR_READ_4(sc, DC_NETCFG); + CSR_WRITE_4(sc, DC_NETCFG, phy_reg & ~DC_NETCFG_PORTSEL); + } + dc_mii_writereg(sc, &frame); + if (sc->dc_type == DC_TYPE_98713) + CSR_WRITE_4(sc, DC_NETCFG, phy_reg); + + return(0); +} + +static void dc_miibus_statchg(dev) + device_t dev; +{ + struct dc_softc *sc; + struct mii_data *mii; + struct ifmedia *ifm; + + sc = device_get_softc(dev); + if (DC_IS_ADMTEK(sc)) + return; + + mii = device_get_softc(sc->dc_miibus); + ifm = &mii->mii_media; + if (DC_IS_DAVICOM(sc) && + IFM_SUBTYPE(ifm->ifm_media) == IFM_HPNA_1) { + dc_setcfg(sc, ifm->ifm_media); + sc->dc_if_media = ifm->ifm_media; + } else { + dc_setcfg(sc, mii->mii_media_active); + sc->dc_if_media = mii->mii_media_active; + } + + return; +} + +/* + * Special support for DM9102A cards with HomePNA PHYs. Note: + * with the Davicom DM9102A/DM9801 eval board that I have, it seems + * to be impossible to talk to the management interface of the DM9801 + * PHY (its MDIO pin is not connected to anything). Consequently, + * the driver has to just 'know' about the additional mode and deal + * with it itself. *sigh* + */ +static void dc_miibus_mediainit(dev) + device_t dev; +{ + struct dc_softc *sc; + struct mii_data *mii; + struct ifmedia *ifm; + int rev; + + rev = pci_read_config(dev, DC_PCI_CFRV, 4) & 0xFF; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->dc_miibus); + ifm = &mii->mii_media; + + if (DC_IS_DAVICOM(sc) && rev >= DC_REVISION_DM9102A) + ifmedia_add(ifm, IFM_ETHER|IFM_HPNA_1, 0, NULL); + + return; +} + +#define DC_POLY 0xEDB88320 +#define DC_BITS_512 9 +#define DC_BITS_128 7 +#define DC_BITS_64 6 + +static u_int32_t dc_crc_le(sc, addr) + struct dc_softc *sc; + caddr_t addr; +{ + u_int32_t idx, bit, data, crc; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (idx = 0; idx < 6; idx++) { + for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) + crc = (crc >> 1) ^ (((crc ^ data) & 1) ? DC_POLY : 0); + } + + /* + * The hash table on the PNIC II and the MX98715AEC-C/D/E + * chips is only 128 bits wide. + */ + if (sc->dc_flags & DC_128BIT_HASH) + return (crc & ((1 << DC_BITS_128) - 1)); + + /* The hash table on the MX98715BEC is only 64 bits wide. */ + if (sc->dc_flags & DC_64BIT_HASH) + return (crc & ((1 << DC_BITS_64) - 1)); + + /* Xircom's hash filtering table is different (read: weird) */ + /* Xircom uses the LEAST significant bits */ + if (DC_IS_XIRCOM(sc)) { + if ((crc & 0x180) == 0x180) + return (crc & 0x0F) + (crc & 0x70)*3 + (14 << 4); + else + return (crc & 0x1F) + ((crc>>1) & 0xF0)*3 + (12 << 4); + } + + return (crc & ((1 << DC_BITS_512) - 1)); +} + +/* + * Calculate CRC of a multicast group address, return the lower 6 bits. + */ +static u_int32_t dc_crc_be(addr) + caddr_t addr; +{ + u_int32_t crc, carry; + int i, j; + u_int8_t c; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (i = 0; i < 6; i++) { + c = *(addr + i); + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); + crc <<= 1; + c >>= 1; + if (carry) + crc = (crc ^ 0x04c11db6) | carry; + } + } + + /* return the filter bit position */ + return((crc >> 26) & 0x0000003F); +} + +/* + * 21143-style RX filter setup routine. Filter programming is done by + * downloading a special setup frame into the TX engine. 21143, Macronix, + * PNIC, PNIC II and Davicom chips are programmed this way. + * + * We always program the chip using 'hash perfect' mode, i.e. one perfect + * address (our node address) and a 512-bit hash filter for multicast + * frames. We also sneak the broadcast address into the hash filter since + * we need that too. + */ +void dc_setfilt_21143(sc) + struct dc_softc *sc; +{ + struct dc_desc *sframe; + u_int32_t h, *sp; + struct ifmultiaddr *ifma; + struct ifnet *ifp; + int i; + + ifp = &sc->arpcom.ac_if; + + i = sc->dc_cdata.dc_tx_prod; + DC_INC(sc->dc_cdata.dc_tx_prod, DC_TX_LIST_CNT); + sc->dc_cdata.dc_tx_cnt++; + sframe = &sc->dc_ldata->dc_tx_list[i]; + sp = (u_int32_t *)&sc->dc_cdata.dc_sbuf; + bzero((char *)sp, DC_SFRAME_LEN); + + sframe->dc_data = vtophys(&sc->dc_cdata.dc_sbuf); + sframe->dc_ctl = DC_SFRAME_LEN | DC_TXCTL_SETUP | DC_TXCTL_TLINK | + DC_FILTER_HASHPERF | DC_TXCTL_FINT; + + sc->dc_cdata.dc_tx_chain[i] = (struct mbuf *)&sc->dc_cdata.dc_sbuf; + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + + if (ifp->if_flags & IFF_ALLMULTI) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = dc_crc_le(sc, + LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + sp[h >> 4] |= 1 << (h & 0xF); + } + + if (ifp->if_flags & IFF_BROADCAST) { + h = dc_crc_le(sc, (caddr_t)ðerbroadcastaddr); + sp[h >> 4] |= 1 << (h & 0xF); + } + + /* Set our MAC address */ + sp[39] = ((u_int16_t *)sc->arpcom.ac_enaddr)[0]; + sp[40] = ((u_int16_t *)sc->arpcom.ac_enaddr)[1]; + sp[41] = ((u_int16_t *)sc->arpcom.ac_enaddr)[2]; + + sframe->dc_status = DC_TXSTAT_OWN; + CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); + + /* + * The PNIC takes an exceedingly long time to process its + * setup frame; wait 10ms after posting the setup frame + * before proceeding, just so it has time to swallow its + * medicine. + */ + DELAY(10000); + + ifp->if_timer = 5; + + return; +} + +void dc_setfilt_admtek(sc) + struct dc_softc *sc; +{ + struct ifnet *ifp; + int h = 0; + u_int32_t hashes[2] = { 0, 0 }; + struct ifmultiaddr *ifma; + + ifp = &sc->arpcom.ac_if; + + /* Init our MAC address */ + CSR_WRITE_4(sc, DC_AL_PAR0, *(u_int32_t *)(&sc->arpcom.ac_enaddr[0])); + CSR_WRITE_4(sc, DC_AL_PAR1, *(u_int32_t *)(&sc->arpcom.ac_enaddr[4])); + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + + if (ifp->if_flags & IFF_ALLMULTI) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + + /* first, zot all the existing hash bits */ + CSR_WRITE_4(sc, DC_AL_MAR0, 0); + CSR_WRITE_4(sc, DC_AL_MAR1, 0); + + /* + * If we're already in promisc or allmulti mode, we + * don't have to bother programming the multicast filter. + */ + if (ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI)) + return; + + /* now program new ones */ + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = dc_crc_be(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + } + + CSR_WRITE_4(sc, DC_AL_MAR0, hashes[0]); + CSR_WRITE_4(sc, DC_AL_MAR1, hashes[1]); + + return; +} + +void dc_setfilt_asix(sc) + struct dc_softc *sc; +{ + struct ifnet *ifp; + int h = 0; + u_int32_t hashes[2] = { 0, 0 }; + struct ifmultiaddr *ifma; + + ifp = &sc->arpcom.ac_if; + + /* Init our MAC address */ + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_PAR0); + CSR_WRITE_4(sc, DC_AX_FILTDATA, + *(u_int32_t *)(&sc->arpcom.ac_enaddr[0])); + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_PAR1); + CSR_WRITE_4(sc, DC_AX_FILTDATA, + *(u_int32_t *)(&sc->arpcom.ac_enaddr[4])); + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + + if (ifp->if_flags & IFF_ALLMULTI) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + + /* + * The ASIX chip has a special bit to enable reception + * of broadcast frames. + */ + if (ifp->if_flags & IFF_BROADCAST) + DC_SETBIT(sc, DC_NETCFG, DC_AX_NETCFG_RX_BROAD); + else + DC_CLRBIT(sc, DC_NETCFG, DC_AX_NETCFG_RX_BROAD); + + /* first, zot all the existing hash bits */ + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR0); + CSR_WRITE_4(sc, DC_AX_FILTDATA, 0); + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR1); + CSR_WRITE_4(sc, DC_AX_FILTDATA, 0); + + /* + * If we're already in promisc or allmulti mode, we + * don't have to bother programming the multicast filter. + */ + if (ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI)) + return; + + /* now program new ones */ + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = dc_crc_be(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + } + + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR0); + CSR_WRITE_4(sc, DC_AX_FILTDATA, hashes[0]); + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR1); + CSR_WRITE_4(sc, DC_AX_FILTDATA, hashes[1]); + + return; +} + +void dc_setfilt_xircom(sc) + struct dc_softc *sc; +{ + struct dc_desc *sframe; + u_int32_t h, *sp; + struct ifmultiaddr *ifma; + struct ifnet *ifp; + int i; + + ifp = &sc->arpcom.ac_if; + DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_TX_ON|DC_NETCFG_RX_ON)); + + i = sc->dc_cdata.dc_tx_prod; + DC_INC(sc->dc_cdata.dc_tx_prod, DC_TX_LIST_CNT); + sc->dc_cdata.dc_tx_cnt++; + sframe = &sc->dc_ldata->dc_tx_list[i]; + sp = (u_int32_t *)&sc->dc_cdata.dc_sbuf; + bzero((char *)sp, DC_SFRAME_LEN); + + sframe->dc_data = vtophys(&sc->dc_cdata.dc_sbuf); + sframe->dc_ctl = DC_SFRAME_LEN | DC_TXCTL_SETUP | DC_TXCTL_TLINK | + DC_FILTER_HASHPERF | DC_TXCTL_FINT; + + sc->dc_cdata.dc_tx_chain[i] = (struct mbuf *)&sc->dc_cdata.dc_sbuf; + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + + if (ifp->if_flags & IFF_ALLMULTI) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = dc_crc_le(sc, + LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + sp[h >> 4] |= 1 << (h & 0xF); + } + + if (ifp->if_flags & IFF_BROADCAST) { + h = dc_crc_le(sc, (caddr_t)ðerbroadcastaddr); + sp[h >> 4] |= 1 << (h & 0xF); + } + + /* Set our MAC address */ + sp[0] = ((u_int16_t *)sc->arpcom.ac_enaddr)[0]; + sp[1] = ((u_int16_t *)sc->arpcom.ac_enaddr)[1]; + sp[2] = ((u_int16_t *)sc->arpcom.ac_enaddr)[2]; + + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ON); + ifp->if_flags |= IFF_RUNNING; + sframe->dc_status = DC_TXSTAT_OWN; + CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); + + /* + * wait some time... + */ + DELAY(1000); + + ifp->if_timer = 5; + + return; +} + +static void dc_setfilt(sc) + struct dc_softc *sc; +{ + if (DC_IS_INTEL(sc) || DC_IS_MACRONIX(sc) || DC_IS_PNIC(sc) || + DC_IS_PNICII(sc) || DC_IS_DAVICOM(sc) || DC_IS_CONEXANT(sc)) + dc_setfilt_21143(sc); + + if (DC_IS_ASIX(sc)) + dc_setfilt_asix(sc); + + if (DC_IS_ADMTEK(sc)) + dc_setfilt_admtek(sc); + + if (DC_IS_XIRCOM(sc)) + dc_setfilt_xircom(sc); + + return; +} + +/* + * In order to fiddle with the + * 'full-duplex' and '100Mbps' bits in the netconfig register, we + * first have to put the transmit and/or receive logic in the idle state. + */ +static void dc_setcfg(sc, media) + struct dc_softc *sc; + int media; +{ + int i, restart = 0; + u_int32_t isr; + + if (IFM_SUBTYPE(media) == IFM_NONE) + return; + + if (CSR_READ_4(sc, DC_NETCFG) & (DC_NETCFG_TX_ON|DC_NETCFG_RX_ON)) { + restart = 1; + DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_TX_ON|DC_NETCFG_RX_ON)); + + for (i = 0; i < DC_TIMEOUT; i++) { + isr = CSR_READ_4(sc, DC_ISR); + if (isr & DC_ISR_TX_IDLE && + (isr & DC_ISR_RX_STATE) == DC_RXSTATE_STOPPED) + break; + DELAY(10); + } + + if (i == DC_TIMEOUT) + printf("dc%d: failed to force tx and " + "rx to idle state\n", sc->dc_unit); + } + + if (IFM_SUBTYPE(media) == IFM_100_TX) { + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_SPEEDSEL); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_HEARTBEAT); + if (sc->dc_pmode == DC_PMODE_MII) { + int watchdogreg; + + if (DC_IS_INTEL(sc)) { + /* there's a write enable bit here that reads as 1 */ + watchdogreg = CSR_READ_4(sc, DC_WATCHDOG); + watchdogreg &= ~DC_WDOG_CTLWREN; + watchdogreg |= DC_WDOG_JABBERDIS; + CSR_WRITE_4(sc, DC_WATCHDOG, watchdogreg); + } else { + DC_SETBIT(sc, DC_WATCHDOG, DC_WDOG_JABBERDIS); + } + DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_PCS| + DC_NETCFG_PORTSEL|DC_NETCFG_SCRAMBLER)); + if (sc->dc_type == DC_TYPE_98713) + DC_SETBIT(sc, DC_NETCFG, (DC_NETCFG_PCS| + DC_NETCFG_SCRAMBLER)); + if (!DC_IS_DAVICOM(sc)) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF); + if (DC_IS_INTEL(sc)) + dc_apply_fixup(sc, IFM_AUTO); + } else { + if (DC_IS_PNIC(sc)) { + DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_SPEEDSEL); + DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_100TX_LOOP); + DC_SETBIT(sc, DC_PN_NWAY, DC_PN_NWAY_SPEEDSEL); + } + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PCS); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_SCRAMBLER); + if (DC_IS_INTEL(sc)) + dc_apply_fixup(sc, + (media & IFM_GMASK) == IFM_FDX ? + IFM_100_TX|IFM_FDX : IFM_100_TX); + } + } + + if (IFM_SUBTYPE(media) == IFM_10_T) { + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_SPEEDSEL); + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_HEARTBEAT); + if (sc->dc_pmode == DC_PMODE_MII) { + int watchdogreg; + + /* there's a write enable bit here that reads as 1 */ + if (DC_IS_INTEL(sc)) { + watchdogreg = CSR_READ_4(sc, DC_WATCHDOG); + watchdogreg &= ~DC_WDOG_CTLWREN; + watchdogreg |= DC_WDOG_JABBERDIS; + CSR_WRITE_4(sc, DC_WATCHDOG, watchdogreg); + } else { + DC_SETBIT(sc, DC_WATCHDOG, DC_WDOG_JABBERDIS); + } + DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_PCS| + DC_NETCFG_PORTSEL|DC_NETCFG_SCRAMBLER)); + if (sc->dc_type == DC_TYPE_98713) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PCS); + if (!DC_IS_DAVICOM(sc)) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF); + if (DC_IS_INTEL(sc)) + dc_apply_fixup(sc, IFM_AUTO); + } else { + if (DC_IS_PNIC(sc)) { + DC_PN_GPIO_CLRBIT(sc, DC_PN_GPIO_SPEEDSEL); + DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_100TX_LOOP); + DC_CLRBIT(sc, DC_PN_NWAY, DC_PN_NWAY_SPEEDSEL); + } + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PCS); + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_SCRAMBLER); + if (DC_IS_INTEL(sc)) { + DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); + DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF); + if ((media & IFM_GMASK) == IFM_FDX) + DC_SETBIT(sc, DC_10BTCTRL, 0x7F3D); + else + DC_SETBIT(sc, DC_10BTCTRL, 0x7F3F); + DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); + DC_CLRBIT(sc, DC_10BTCTRL, + DC_TCTL_AUTONEGENBL); + dc_apply_fixup(sc, + (media & IFM_GMASK) == IFM_FDX ? + IFM_10_T|IFM_FDX : IFM_10_T); + DELAY(20000); + } + } + } + + /* + * If this is a Davicom DM9102A card with a DM9801 HomePNA + * PHY and we want HomePNA mode, set the portsel bit to turn + * on the external MII port. + */ + if (DC_IS_DAVICOM(sc)) { + if (IFM_SUBTYPE(media) == IFM_HPNA_1) { + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + sc->dc_link = 1; + } else { + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + } + } + + if ((media & IFM_GMASK) == IFM_FDX) { + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX); + if (sc->dc_pmode == DC_PMODE_SYM && DC_IS_PNIC(sc)) + DC_SETBIT(sc, DC_PN_NWAY, DC_PN_NWAY_DUPLEX); + } else { + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX); + if (sc->dc_pmode == DC_PMODE_SYM && DC_IS_PNIC(sc)) + DC_CLRBIT(sc, DC_PN_NWAY, DC_PN_NWAY_DUPLEX); + } + + if (restart) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON|DC_NETCFG_RX_ON); + + return; +} + +static void dc_reset(sc) + struct dc_softc *sc; +{ + register int i; + + DC_SETBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET); + + for (i = 0; i < DC_TIMEOUT; i++) { + DELAY(10); + if (!(CSR_READ_4(sc, DC_BUSCTL) & DC_BUSCTL_RESET)) + break; + } + + if (DC_IS_ASIX(sc) || DC_IS_ADMTEK(sc) || DC_IS_CONEXANT(sc) || + DC_IS_XIRCOM(sc) || DC_IS_INTEL(sc)) { + DELAY(10000); + DC_CLRBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET); + i = 0; + } + + if (i == DC_TIMEOUT) + printf("dc%d: reset never completed!\n", sc->dc_unit); + + /* Wait a little while for the chip to get its brains in order. */ + DELAY(1000); + + CSR_WRITE_4(sc, DC_IMR, 0x00000000); + CSR_WRITE_4(sc, DC_BUSCTL, 0x00000000); + CSR_WRITE_4(sc, DC_NETCFG, 0x00000000); + + /* + * Bring the SIA out of reset. In some cases, it looks + * like failing to unreset the SIA soon enough gets it + * into a state where it will never come out of reset + * until we reset the whole chip again. + */ + if (DC_IS_INTEL(sc)) { + DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); + CSR_WRITE_4(sc, DC_10BTCTRL, 0); + CSR_WRITE_4(sc, DC_WATCHDOG, 0); + } + + return; +} + +static struct dc_type *dc_devtype(dev) + device_t dev; +{ + struct dc_type *t; + u_int32_t rev; + + t = dc_devs; + + while(t->dc_name != NULL) { + if ((pci_get_vendor(dev) == t->dc_vid) && + (pci_get_device(dev) == t->dc_did)) { + /* Check the PCI revision */ + rev = pci_read_config(dev, DC_PCI_CFRV, 4) & 0xFF; + if (t->dc_did == DC_DEVICEID_98713 && + rev >= DC_REVISION_98713A) + t++; + if (t->dc_did == DC_DEVICEID_98713_CP && + rev >= DC_REVISION_98713A) + t++; + if (t->dc_did == DC_DEVICEID_987x5 && + rev >= DC_REVISION_98715AEC_C) + t++; + if (t->dc_did == DC_DEVICEID_987x5 && + rev >= DC_REVISION_98725) + t++; + if (t->dc_did == DC_DEVICEID_AX88140A && + rev >= DC_REVISION_88141) + t++; + if (t->dc_did == DC_DEVICEID_82C168 && + rev >= DC_REVISION_82C169) + t++; + if (t->dc_did == DC_DEVICEID_DM9102 && + rev >= DC_REVISION_DM9102A) + t++; + return(t); + } + t++; + } + + return(NULL); +} + +/* + * Probe for a 21143 or clone chip. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + * We do a little bit of extra work to identify the exact type of + * chip. The MX98713 and MX98713A have the same PCI vendor/device ID, + * but different revision IDs. The same is true for 98715/98715A + * chips and the 98725, as well as the ASIX and ADMtek chips. In some + * cases, the exact chip revision affects driver behavior. + */ +static int dc_probe(dev) + device_t dev; +{ + struct dc_type *t; + + t = dc_devtype(dev); + + if (t != NULL) { + device_set_desc(dev, t->dc_name); + return(0); + } + + return(ENXIO); +} + +static void dc_acpi(dev) + device_t dev; +{ + int unit; + + unit = device_get_unit(dev); + + if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { + u_int32_t iobase, membase, irq; + + /* Save important PCI config data. */ + iobase = pci_read_config(dev, DC_PCI_CFBIO, 4); + membase = pci_read_config(dev, DC_PCI_CFBMA, 4); + irq = pci_read_config(dev, DC_PCI_CFIT, 4); + + /* Reset the power state. */ + printf("dc%d: chip is in D%d power mode " + "-- setting to D0\n", unit, + pci_get_powerstate(dev)); + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + + /* Restore PCI config data. */ + pci_write_config(dev, DC_PCI_CFBIO, iobase, 4); + pci_write_config(dev, DC_PCI_CFBMA, membase, 4); + pci_write_config(dev, DC_PCI_CFIT, irq, 4); + } + + return; +} + +static void dc_apply_fixup(sc, media) + struct dc_softc *sc; + int media; +{ + struct dc_mediainfo *m; + u_int8_t *p; + int i; + u_int32_t reg; + + m = sc->dc_mi; + + while (m != NULL) { + if (m->dc_media == media) + break; + m = m->dc_next; + } + + if (m == NULL) + return; + + for (i = 0, p = m->dc_reset_ptr; i < m->dc_reset_len; i++, p += 2) { + reg = (p[0] | (p[1] << 8)) << 16; + CSR_WRITE_4(sc, DC_WATCHDOG, reg); + } + + for (i = 0, p = m->dc_gp_ptr; i < m->dc_gp_len; i++, p += 2) { + reg = (p[0] | (p[1] << 8)) << 16; + CSR_WRITE_4(sc, DC_WATCHDOG, reg); + } + + return; +} + +static void dc_decode_leaf_sia(sc, l) + struct dc_softc *sc; + struct dc_eblock_sia *l; +{ + struct dc_mediainfo *m; + + m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT); + bzero(m, sizeof(struct dc_mediainfo)); + if (l->dc_sia_code == DC_SIA_CODE_10BT) + m->dc_media = IFM_10_T; + + if (l->dc_sia_code == DC_SIA_CODE_10BT_FDX) + m->dc_media = IFM_10_T|IFM_FDX; + + if (l->dc_sia_code == DC_SIA_CODE_10B2) + m->dc_media = IFM_10_2; + + if (l->dc_sia_code == DC_SIA_CODE_10B5) + m->dc_media = IFM_10_5; + + m->dc_gp_len = 2; + m->dc_gp_ptr = (u_int8_t *)&l->dc_sia_gpio_ctl; + + m->dc_next = sc->dc_mi; + sc->dc_mi = m; + + sc->dc_pmode = DC_PMODE_SIA; + + return; +} + +static void dc_decode_leaf_sym(sc, l) + struct dc_softc *sc; + struct dc_eblock_sym *l; +{ + struct dc_mediainfo *m; + + m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT); + bzero(m, sizeof(struct dc_mediainfo)); + if (l->dc_sym_code == DC_SYM_CODE_100BT) + m->dc_media = IFM_100_TX; + + if (l->dc_sym_code == DC_SYM_CODE_100BT_FDX) + m->dc_media = IFM_100_TX|IFM_FDX; + + m->dc_gp_len = 2; + m->dc_gp_ptr = (u_int8_t *)&l->dc_sym_gpio_ctl; + + m->dc_next = sc->dc_mi; + sc->dc_mi = m; + + sc->dc_pmode = DC_PMODE_SYM; + + return; +} + +static void dc_decode_leaf_mii(sc, l) + struct dc_softc *sc; + struct dc_eblock_mii *l; +{ + u_int8_t *p; + struct dc_mediainfo *m; + + m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT); + bzero(m, sizeof(struct dc_mediainfo)); + /* We abuse IFM_AUTO to represent MII. */ + m->dc_media = IFM_AUTO; + m->dc_gp_len = l->dc_gpr_len; + + p = (u_int8_t *)l; + p += sizeof(struct dc_eblock_mii); + m->dc_gp_ptr = p; + p += 2 * l->dc_gpr_len; + m->dc_reset_len = *p; + p++; + m->dc_reset_ptr = p; + + m->dc_next = sc->dc_mi; + sc->dc_mi = m; + + return; +} + +static void dc_parse_21143_srom(sc) + struct dc_softc *sc; +{ + struct dc_leaf_hdr *lhdr; + struct dc_eblock_hdr *hdr; + int i, loff; + char *ptr; + + loff = sc->dc_srom[27]; + lhdr = (struct dc_leaf_hdr *)&(sc->dc_srom[loff]); + + ptr = (char *)lhdr; + ptr += sizeof(struct dc_leaf_hdr) - 1; + for (i = 0; i < lhdr->dc_mcnt; i++) { + hdr = (struct dc_eblock_hdr *)ptr; + switch(hdr->dc_type) { + case DC_EBLOCK_MII: + dc_decode_leaf_mii(sc, (struct dc_eblock_mii *)hdr); + break; + case DC_EBLOCK_SIA: + dc_decode_leaf_sia(sc, (struct dc_eblock_sia *)hdr); + break; + case DC_EBLOCK_SYM: + dc_decode_leaf_sym(sc, (struct dc_eblock_sym *)hdr); + break; + default: + /* Don't care. Yet. */ + break; + } + ptr += (hdr->dc_len & 0x7F); + ptr++; + } + + return; +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int dc_attach(dev) + device_t dev; +{ + int tmp = 0; + u_char eaddr[ETHER_ADDR_LEN]; + u_int32_t command; + struct dc_softc *sc; + struct ifnet *ifp; + u_int32_t revision; + int unit, error = 0, rid, mac_offset; + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + bzero(sc, sizeof(struct dc_softc)); + + mtx_init(&sc->dc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF | MTX_RECURSE); + + /* + * Handle power management nonsense. + */ + dc_acpi(dev); + + /* + * Map control/status registers. + */ + pci_enable_busmaster(dev); + pci_enable_io(dev, SYS_RES_IOPORT); + pci_enable_io(dev, SYS_RES_MEMORY); + command = pci_read_config(dev, PCIR_COMMAND, 4); + +#ifdef DC_USEIOSPACE + if (!(command & PCIM_CMD_PORTEN)) { + printf("dc%d: failed to enable I/O ports!\n", unit); + error = ENXIO; + goto fail_nolock; + } +#else + if (!(command & PCIM_CMD_MEMEN)) { + printf("dc%d: failed to enable memory mapping!\n", unit); + error = ENXIO; + goto fail_nolock; + } +#endif + + rid = DC_RID; + sc->dc_res = bus_alloc_resource(dev, DC_RES, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (sc->dc_res == NULL) { + printf("dc%d: couldn't map ports/memory\n", unit); + error = ENXIO; + goto fail_nolock; + } + + sc->dc_btag = rman_get_bustag(sc->dc_res); + sc->dc_bhandle = rman_get_bushandle(sc->dc_res); + + /* Allocate interrupt */ + rid = 0; + sc->dc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->dc_irq == NULL) { + printf("dc%d: couldn't map interrupt\n", unit); + bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res); + error = ENXIO; + goto fail_nolock; + } + + error = bus_setup_intr(dev, sc->dc_irq, INTR_TYPE_NET | + (IS_MPSAFE ? INTR_MPSAFE : 0), + dc_intr, sc, &sc->dc_intrhand); + + if (error) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq); + bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res); + printf("dc%d: couldn't set up irq\n", unit); + goto fail_nolock; + } + DC_LOCK(sc); + + /* Need this info to decide on a chip type. */ + sc->dc_info = dc_devtype(dev); + revision = pci_read_config(dev, DC_PCI_CFRV, 4) & 0x000000FF; + + switch(sc->dc_info->dc_did) { + case DC_DEVICEID_21143: + sc->dc_type = DC_TYPE_21143; + sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR; + sc->dc_flags |= DC_REDUCED_MII_POLL; + /* Save EEPROM contents so we can parse them later. */ + dc_read_eeprom(sc, (caddr_t)&sc->dc_srom, 0, 512, 0); + break; + case DC_DEVICEID_DM9100: + case DC_DEVICEID_DM9102: + sc->dc_type = DC_TYPE_DM9102; + sc->dc_flags |= DC_TX_COALESCE|DC_TX_INTR_ALWAYS; + sc->dc_flags |= DC_REDUCED_MII_POLL|DC_TX_STORENFWD; + sc->dc_pmode = DC_PMODE_MII; + /* Increase the latency timer value. */ + command = pci_read_config(dev, DC_PCI_CFLT, 4); + command &= 0xFFFF00FF; + command |= 0x00008000; + pci_write_config(dev, DC_PCI_CFLT, command, 4); + break; + case DC_DEVICEID_AL981: + sc->dc_type = DC_TYPE_AL981; + sc->dc_flags |= DC_TX_USE_TX_INTR; + sc->dc_flags |= DC_TX_ADMTEK_WAR; + sc->dc_pmode = DC_PMODE_MII; + break; + case DC_DEVICEID_AN985: + case DC_DEVICEID_FE2500: + case DC_DEVICEID_EN2242: + sc->dc_type = DC_TYPE_AN985; + sc->dc_flags |= DC_TX_USE_TX_INTR; + sc->dc_flags |= DC_TX_ADMTEK_WAR; + sc->dc_pmode = DC_PMODE_MII; + break; + case DC_DEVICEID_98713: + case DC_DEVICEID_98713_CP: + if (revision < DC_REVISION_98713A) { + sc->dc_type = DC_TYPE_98713; + } + if (revision >= DC_REVISION_98713A) { + sc->dc_type = DC_TYPE_98713A; + sc->dc_flags |= DC_21143_NWAY; + } + sc->dc_flags |= DC_REDUCED_MII_POLL; + sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR; + break; + case DC_DEVICEID_987x5: + case DC_DEVICEID_EN1217: + /* + * Macronix MX98715AEC-C/D/E parts have only a + * 128-bit hash table. We need to deal with these + * in the same manner as the PNIC II so that we + * get the right number of bits out of the + * CRC routine. + */ + if (revision >= DC_REVISION_98715AEC_C && + revision < DC_REVISION_98725) + sc->dc_flags |= DC_128BIT_HASH; + sc->dc_type = DC_TYPE_987x5; + sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR; + sc->dc_flags |= DC_REDUCED_MII_POLL|DC_21143_NWAY; + break; + case DC_DEVICEID_98727: + sc->dc_type = DC_TYPE_987x5; + sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR; + sc->dc_flags |= DC_REDUCED_MII_POLL|DC_21143_NWAY; + break; + case DC_DEVICEID_82C115: + sc->dc_type = DC_TYPE_PNICII; + sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR|DC_128BIT_HASH; + sc->dc_flags |= DC_REDUCED_MII_POLL|DC_21143_NWAY; + break; + case DC_DEVICEID_82C168: + sc->dc_type = DC_TYPE_PNIC; + sc->dc_flags |= DC_TX_STORENFWD|DC_TX_INTR_ALWAYS; + sc->dc_flags |= DC_PNIC_RX_BUG_WAR; + sc->dc_pnic_rx_buf = malloc(DC_RXLEN * 5, M_DEVBUF, M_NOWAIT); + if (revision < DC_REVISION_82C169) + sc->dc_pmode = DC_PMODE_SYM; + break; + case DC_DEVICEID_AX88140A: + sc->dc_type = DC_TYPE_ASIX; + sc->dc_flags |= DC_TX_USE_TX_INTR|DC_TX_INTR_FIRSTFRAG; + sc->dc_flags |= DC_REDUCED_MII_POLL; + sc->dc_pmode = DC_PMODE_MII; + break; + case DC_DEVICEID_X3201: + sc->dc_type = DC_TYPE_XIRCOM; + sc->dc_flags |= DC_TX_INTR_ALWAYS | DC_TX_COALESCE | + DC_TX_ALIGN; + /* + * We don't actually need to coalesce, but we're doing + * it to obtain a double word aligned buffer. + * The DC_TX_COALESCE flag is required. + */ + break; + case DC_DEVICEID_RS7112: + sc->dc_type = DC_TYPE_CONEXANT; + sc->dc_flags |= DC_TX_INTR_ALWAYS; + sc->dc_flags |= DC_REDUCED_MII_POLL; + sc->dc_pmode = DC_PMODE_MII; + dc_read_eeprom(sc, (caddr_t)&sc->dc_srom, 0, 256, 0); + break; + default: + printf("dc%d: unknown device: %x\n", sc->dc_unit, + sc->dc_info->dc_did); + break; + } + + /* Save the cache line size. */ + if (DC_IS_DAVICOM(sc)) + sc->dc_cachesize = 0; + else + sc->dc_cachesize = pci_read_config(dev, + DC_PCI_CFLT, 4) & 0xFF; + + /* Reset the adapter. */ + dc_reset(sc); + + /* Take 21143 out of snooze mode */ + if (DC_IS_INTEL(sc) || DC_IS_XIRCOM(sc)) { + command = pci_read_config(dev, DC_PCI_CFDD, 4); + command &= ~(DC_CFDD_SNOOZE_MODE|DC_CFDD_SLEEP_MODE); + pci_write_config(dev, DC_PCI_CFDD, command, 4); + } + + /* + * Try to learn something about the supported media. + * We know that ASIX and ADMtek and Davicom devices + * will *always* be using MII media, so that's a no-brainer. + * The tricky ones are the Macronix/PNIC II and the + * Intel 21143. + */ + if (DC_IS_INTEL(sc)) + dc_parse_21143_srom(sc); + else if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) { + if (sc->dc_type == DC_TYPE_98713) + sc->dc_pmode = DC_PMODE_MII; + else + sc->dc_pmode = DC_PMODE_SYM; + } else if (!sc->dc_pmode) + sc->dc_pmode = DC_PMODE_MII; + + /* + * Get station address from the EEPROM. + */ + switch(sc->dc_type) { + case DC_TYPE_98713: + case DC_TYPE_98713A: + case DC_TYPE_987x5: + case DC_TYPE_PNICII: + dc_read_eeprom(sc, (caddr_t)&mac_offset, + (DC_EE_NODEADDR_OFFSET / 2), 1, 0); + dc_read_eeprom(sc, (caddr_t)&eaddr, (mac_offset / 2), 3, 0); + break; + case DC_TYPE_PNIC: + dc_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 1); + break; + case DC_TYPE_DM9102: + case DC_TYPE_21143: + case DC_TYPE_ASIX: + dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0); + break; + case DC_TYPE_AL981: + case DC_TYPE_AN985: + dc_read_eeprom(sc, (caddr_t)&eaddr, DC_AL_EE_NODEADDR, 3, 0); + break; + case DC_TYPE_CONEXANT: + bcopy(sc->dc_srom + DC_CONEXANT_EE_NODEADDR, &eaddr, 6); + break; + case DC_TYPE_XIRCOM: + dc_read_eeprom(sc, (caddr_t)&eaddr, 3, 3, 0); + break; + default: + dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0); + break; + } + + /* + * A 21143 or clone chip was detected. Inform the world. + */ + printf("dc%d: Ethernet address: %6D\n", unit, eaddr, ":"); + + sc->dc_unit = unit; + bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); + + sc->dc_ldata = contigmalloc(sizeof(struct dc_list_data), M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc->dc_ldata == NULL) { + printf("dc%d: no memory for list buffers!\n", unit); + bus_teardown_intr(dev, sc->dc_irq, sc->dc_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq); + bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res); + error = ENXIO; + goto fail; + } + + bzero(sc->dc_ldata, sizeof(struct dc_list_data)); + + ifp = &sc->arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = "dc"; + /* XXX: bleah, MTU gets overwritten in ether_ifattach() */ + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = dc_ioctl; + ifp->if_output = ether_output; + ifp->if_start = dc_start; + ifp->if_watchdog = dc_watchdog; + ifp->if_init = dc_init; + ifp->if_baudrate = 10000000; + ifp->if_snd.ifq_maxlen = DC_TX_LIST_CNT - 1; + + /* + * Do MII setup. If this is a 21143, check for a PHY on the + * MII bus after applying any necessary fixups to twiddle the + * GPIO bits. If we don't end up finding a PHY, restore the + * old selection (SIA only or SIA/SYM) and attach the dcphy + * driver instead. + */ + if (DC_IS_INTEL(sc)) { + dc_apply_fixup(sc, IFM_AUTO); + tmp = sc->dc_pmode; + sc->dc_pmode = DC_PMODE_MII; + } + + error = mii_phy_probe(dev, &sc->dc_miibus, + dc_ifmedia_upd, dc_ifmedia_sts); + + if (error && DC_IS_INTEL(sc)) { + sc->dc_pmode = tmp; + if (sc->dc_pmode != DC_PMODE_SIA) + sc->dc_pmode = DC_PMODE_SYM; + sc->dc_flags |= DC_21143_NWAY; + mii_phy_probe(dev, &sc->dc_miibus, + dc_ifmedia_upd, dc_ifmedia_sts); + /* + * For non-MII cards, we need to have the 21143 + * drive the LEDs. Except there are some systems + * like the NEC VersaPro NoteBook PC which have no + * LEDs, and twiddling these bits has adverse effects + * on them. (I.e. you suddenly can't get a link.) + */ + if (pci_read_config(dev, DC_PCI_CSID, 4) != 0x80281033) + sc->dc_flags |= DC_TULIP_LEDS; + error = 0; + } + + if (error) { + printf("dc%d: MII without any PHY!\n", sc->dc_unit); + bus_teardown_intr(dev, sc->dc_irq, sc->dc_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq); + bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res); + error = ENXIO; + goto fail; + } + + if (DC_IS_XIRCOM(sc)) { + /* + * setup General Purpose Port mode and data so the tulip + * can talk to the MII. + */ + CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_WRITE_EN | DC_SIAGP_INT1_EN | + DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT); + DELAY(10); + CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_INT1_EN | + DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT); + DELAY(10); + } + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); + + /* + * Tell the upper layer(s) we support long frames. + */ + ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); + + callout_init(&sc->dc_stat_ch, IS_MPSAFE); + +#ifdef SRM_MEDIA + sc->dc_srm_media = 0; + + /* Remember the SRM console media setting */ + if (DC_IS_INTEL(sc)) { + command = pci_read_config(dev, DC_PCI_CFDD, 4); + command &= ~(DC_CFDD_SNOOZE_MODE|DC_CFDD_SLEEP_MODE); + switch ((command >> 8) & 0xff) { + case 3: + sc->dc_srm_media = IFM_10_T; + break; + case 4: + sc->dc_srm_media = IFM_10_T | IFM_FDX; + break; + case 5: + sc->dc_srm_media = IFM_100_TX; + break; + case 6: + sc->dc_srm_media = IFM_100_TX | IFM_FDX; + break; + } + if (sc->dc_srm_media) + sc->dc_srm_media |= IFM_ACTIVE | IFM_ETHER; + } +#endif + + DC_UNLOCK(sc); + return(0); + +fail: + DC_UNLOCK(sc); +fail_nolock: + mtx_destroy(&sc->dc_mtx); + return(error); +} + +static int dc_detach(dev) + device_t dev; +{ + struct dc_softc *sc; + struct ifnet *ifp; + struct dc_mediainfo *m; + + sc = device_get_softc(dev); + + DC_LOCK(sc); + + ifp = &sc->arpcom.ac_if; + + dc_stop(sc); + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + + bus_generic_detach(dev); + device_delete_child(dev, sc->dc_miibus); + + bus_teardown_intr(dev, sc->dc_irq, sc->dc_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq); + bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res); + + contigfree(sc->dc_ldata, sizeof(struct dc_list_data), M_DEVBUF); + if (sc->dc_pnic_rx_buf != NULL) + free(sc->dc_pnic_rx_buf, M_DEVBUF); + + while(sc->dc_mi != NULL) { + m = sc->dc_mi->dc_next; + free(sc->dc_mi, M_DEVBUF); + sc->dc_mi = m; + } + + DC_UNLOCK(sc); + mtx_destroy(&sc->dc_mtx); + + return(0); +} + +/* + * Initialize the transmit descriptors. + */ +static int dc_list_tx_init(sc) + struct dc_softc *sc; +{ + struct dc_chain_data *cd; + struct dc_list_data *ld; + int i, nexti; + + cd = &sc->dc_cdata; + ld = sc->dc_ldata; + for (i = 0; i < DC_TX_LIST_CNT; i++) { + nexti = (i == (DC_TX_LIST_CNT - 1)) ? 0 : i+1; + ld->dc_tx_list[i].dc_next = vtophys(&ld->dc_tx_list[nexti]); + cd->dc_tx_chain[i] = NULL; + ld->dc_tx_list[i].dc_data = 0; + ld->dc_tx_list[i].dc_ctl = 0; + } + + cd->dc_tx_prod = cd->dc_tx_cons = cd->dc_tx_cnt = 0; + + 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 dc_list_rx_init(sc) + struct dc_softc *sc; +{ + struct dc_chain_data *cd; + struct dc_list_data *ld; + int i, nexti; + + cd = &sc->dc_cdata; + ld = sc->dc_ldata; + + for (i = 0; i < DC_RX_LIST_CNT; i++) { + if (dc_newbuf(sc, i, NULL) == ENOBUFS) + return(ENOBUFS); + nexti = (i == (DC_RX_LIST_CNT - 1)) ? 0 : i+1; + ld->dc_rx_list[i].dc_next = vtophys(&ld->dc_rx_list[nexti]); + } + + cd->dc_rx_prod = 0; + + return(0); +} + +/* + * Initialize an RX descriptor and attach an MBUF cluster. + */ +static int dc_newbuf(sc, i, m) + struct dc_softc *sc; + int i; + struct mbuf *m; +{ + struct mbuf *m_new = NULL; + struct dc_desc *c; + + c = &sc->dc_ldata->dc_rx_list[i]; + + if (m == NULL) { + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(ENOBUFS); + + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + return(ENOBUFS); + } + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + } else { + m_new = m; + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + m_new->m_data = m_new->m_ext.ext_buf; + } + + m_adj(m_new, sizeof(u_int64_t)); + + /* + * If this is a PNIC chip, zero the buffer. This is part + * of the workaround for the receive bug in the 82c168 and + * 82c169 chips. + */ + if (sc->dc_flags & DC_PNIC_RX_BUG_WAR) + bzero((char *)mtod(m_new, char *), m_new->m_len); + + sc->dc_cdata.dc_rx_chain[i] = m_new; + c->dc_data = vtophys(mtod(m_new, caddr_t)); + c->dc_ctl = DC_RXCTL_RLINK | DC_RXLEN; + c->dc_status = DC_RXSTAT_OWN; + + return(0); +} + +/* + * Grrrrr. + * The PNIC chip has a terrible bug in it that manifests itself during + * periods of heavy activity. The exact mode of failure if difficult to + * pinpoint: sometimes it only happens in promiscuous mode, sometimes it + * will happen on slow machines. The bug is that sometimes instead of + * uploading one complete frame during reception, it uploads what looks + * like the entire contents of its FIFO memory. The frame we want is at + * the end of the whole mess, but we never know exactly how much data has + * been uploaded, so salvaging the frame is hard. + * + * There is only one way to do it reliably, and it's disgusting. + * Here's what we know: + * + * - We know there will always be somewhere between one and three extra + * descriptors uploaded. + * + * - We know the desired received frame will always be at the end of the + * total data upload. + * + * - We know the size of the desired received frame because it will be + * provided in the length field of the status word in the last descriptor. + * + * Here's what we do: + * + * - When we allocate buffers for the receive ring, we bzero() them. + * This means that we know that the buffer contents should be all + * zeros, except for data uploaded by the chip. + * + * - We also force the PNIC chip to upload frames that include the + * ethernet CRC at the end. + * + * - We gather all of the bogus frame data into a single buffer. + * + * - We then position a pointer at the end of this buffer and scan + * backwards until we encounter the first non-zero byte of data. + * This is the end of the received frame. We know we will encounter + * some data at the end of the frame because the CRC will always be + * there, so even if the sender transmits a packet of all zeros, + * we won't be fooled. + * + * - We know the size of the actual received frame, so we subtract + * that value from the current pointer location. This brings us + * to the start of the actual received packet. + * + * - We copy this into an mbuf and pass it on, along with the actual + * frame length. + * + * The performance hit is tremendous, but it beats dropping frames all + * the time. + */ + +#define DC_WHOLEFRAME (DC_RXSTAT_FIRSTFRAG|DC_RXSTAT_LASTFRAG) +static void dc_pnic_rx_bug_war(sc, idx) + struct dc_softc *sc; + int idx; +{ + struct dc_desc *cur_rx; + struct dc_desc *c = NULL; + struct mbuf *m = NULL; + unsigned char *ptr; + int i, total_len; + u_int32_t rxstat = 0; + + i = sc->dc_pnic_rx_bug_save; + cur_rx = &sc->dc_ldata->dc_rx_list[idx]; + ptr = sc->dc_pnic_rx_buf; + bzero(ptr, sizeof(DC_RXLEN * 5)); + + /* Copy all the bytes from the bogus buffers. */ + while (1) { + c = &sc->dc_ldata->dc_rx_list[i]; + rxstat = c->dc_status; + m = sc->dc_cdata.dc_rx_chain[i]; + bcopy(mtod(m, char *), ptr, DC_RXLEN); + ptr += DC_RXLEN; + /* If this is the last buffer, break out. */ + if (i == idx || rxstat & DC_RXSTAT_LASTFRAG) + break; + dc_newbuf(sc, i, m); + DC_INC(i, DC_RX_LIST_CNT); + } + + /* Find the length of the actual receive frame. */ + total_len = DC_RXBYTES(rxstat); + + /* Scan backwards until we hit a non-zero byte. */ + while(*ptr == 0x00) + ptr--; + + /* Round off. */ + if ((uintptr_t)(ptr) & 0x3) + ptr -= 1; + + /* Now find the start of the frame. */ + ptr -= total_len; + if (ptr < sc->dc_pnic_rx_buf) + ptr = sc->dc_pnic_rx_buf; + + /* + * Now copy the salvaged frame to the last mbuf and fake up + * the status word to make it look like a successful + * frame reception. + */ + dc_newbuf(sc, i, m); + bcopy(ptr, mtod(m, char *), total_len); + cur_rx->dc_status = rxstat | DC_RXSTAT_FIRSTFRAG; + + return; +} + +/* + * This routine searches the RX ring for dirty descriptors in the + * event that the rxeof routine falls out of sync with the chip's + * current descriptor pointer. This may happen sometimes as a result + * of a "no RX buffer available" condition that happens when the chip + * consumes all of the RX buffers before the driver has a chance to + * process the RX ring. This routine may need to be called more than + * once to bring the driver back in sync with the chip, however we + * should still be getting RX DONE interrupts to drive the search + * for new packets in the RX ring, so we should catch up eventually. + */ +static int dc_rx_resync(sc) + struct dc_softc *sc; +{ + int i, pos; + struct dc_desc *cur_rx; + + pos = sc->dc_cdata.dc_rx_prod; + + for (i = 0; i < DC_RX_LIST_CNT; i++) { + cur_rx = &sc->dc_ldata->dc_rx_list[pos]; + if (!(cur_rx->dc_status & DC_RXSTAT_OWN)) + break; + DC_INC(pos, DC_RX_LIST_CNT); + } + + /* If the ring really is empty, then just return. */ + if (i == DC_RX_LIST_CNT) + return(0); + + /* We've fallen behing the chip: catch it. */ + sc->dc_cdata.dc_rx_prod = pos; + + return(EAGAIN); +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +static void dc_rxeof(sc) + struct dc_softc *sc; +{ + struct ether_header *eh; + struct mbuf *m; + struct ifnet *ifp; + struct dc_desc *cur_rx; + int i, total_len = 0; + u_int32_t rxstat; + + ifp = &sc->arpcom.ac_if; + i = sc->dc_cdata.dc_rx_prod; + + while(!(sc->dc_ldata->dc_rx_list[i].dc_status & DC_RXSTAT_OWN)) { + +#ifdef DEVICE_POLLING + if (ifp->if_ipending & IFF_POLLING) { + if (sc->rxcycles <= 0) + break; + sc->rxcycles--; + } +#endif /* DEVICE_POLLING */ + cur_rx = &sc->dc_ldata->dc_rx_list[i]; + rxstat = cur_rx->dc_status; + m = sc->dc_cdata.dc_rx_chain[i]; + total_len = DC_RXBYTES(rxstat); + + if (sc->dc_flags & DC_PNIC_RX_BUG_WAR) { + if ((rxstat & DC_WHOLEFRAME) != DC_WHOLEFRAME) { + if (rxstat & DC_RXSTAT_FIRSTFRAG) + sc->dc_pnic_rx_bug_save = i; + if ((rxstat & DC_RXSTAT_LASTFRAG) == 0) { + DC_INC(i, DC_RX_LIST_CNT); + continue; + } + dc_pnic_rx_bug_war(sc, i); + rxstat = cur_rx->dc_status; + total_len = DC_RXBYTES(rxstat); + } + } + + sc->dc_cdata.dc_rx_chain[i] = NULL; + + /* + * If an error occurs, update stats, clear the + * status word and leave the mbuf cluster in place: + * it should simply get re-used next time this descriptor + * comes up in the ring. However, don't report long + * frames as errors since they could be vlans + */ + if ((rxstat & DC_RXSTAT_RXERR)){ + if (!(rxstat & DC_RXSTAT_GIANT) || + (rxstat & (DC_RXSTAT_CRCERR | DC_RXSTAT_DRIBBLE | + DC_RXSTAT_MIIERE | DC_RXSTAT_COLLSEEN | + DC_RXSTAT_RUNT | DC_RXSTAT_DE))) { + ifp->if_ierrors++; + if (rxstat & DC_RXSTAT_COLLSEEN) + ifp->if_collisions++; + dc_newbuf(sc, i, m); + if (rxstat & DC_RXSTAT_CRCERR) { + DC_INC(i, DC_RX_LIST_CNT); + continue; + } else { + dc_init(sc); + return; + } + } + } + + /* No errors; receive the packet. */ + total_len -= ETHER_CRC_LEN; +#ifdef __i386__ + /* + * On the x86 we do not have alignment problems, so try to + * allocate a new buffer for the receive ring, and pass up + * the one where the packet is already, saving the expensive + * copy done in m_devget(). + * If we are on an architecture with alignment problems, or + * if the allocation fails, then use m_devget and leave the + * existing buffer in the receive ring. + */ + if (dc_quick && dc_newbuf(sc, i, NULL) == 0) { + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = total_len; + DC_INC(i, DC_RX_LIST_CNT); + } else +#endif + { + struct mbuf *m0; + + m0 = m_devget(mtod(m, char *), total_len, + ETHER_ALIGN, ifp, NULL); + dc_newbuf(sc, i, m); + DC_INC(i, DC_RX_LIST_CNT); + if (m0 == NULL) { + ifp->if_ierrors++; + continue; + } + m = m0; + } + + ifp->if_ipackets++; + eh = mtod(m, struct ether_header *); + + /* Remove header from mbuf and pass it on. */ + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, eh, m); + } + + sc->dc_cdata.dc_rx_prod = i; +} + +/* + * A frame was downloaded to the chip. It's safe for us to clean up + * the list buffers. + */ + +static void dc_txeof(sc) + struct dc_softc *sc; +{ + struct dc_desc *cur_tx = NULL; + struct ifnet *ifp; + int idx; + + ifp = &sc->arpcom.ac_if; + + /* Clear the timeout timer. */ + ifp->if_timer = 0; + + /* + * Go through our tx list and free mbufs for those + * frames that have been transmitted. + */ + idx = sc->dc_cdata.dc_tx_cons; + while(idx != sc->dc_cdata.dc_tx_prod) { + u_int32_t txstat; + + cur_tx = &sc->dc_ldata->dc_tx_list[idx]; + txstat = cur_tx->dc_status; + + if (txstat & DC_TXSTAT_OWN) + break; + + if (!(cur_tx->dc_ctl & DC_TXCTL_LASTFRAG) || + cur_tx->dc_ctl & DC_TXCTL_SETUP) { + sc->dc_cdata.dc_tx_cnt--; + if (cur_tx->dc_ctl & DC_TXCTL_SETUP) { + /* + * Yes, the PNIC is so brain damaged + * that it will sometimes generate a TX + * underrun error while DMAing the RX + * filter setup frame. If we detect this, + * we have to send the setup frame again, + * or else the filter won't be programmed + * correctly. + */ + if (DC_IS_PNIC(sc)) { + if (txstat & DC_TXSTAT_ERRSUM) + dc_setfilt(sc); + } + sc->dc_cdata.dc_tx_chain[idx] = NULL; + } + DC_INC(idx, DC_TX_LIST_CNT); + continue; + } + + if (DC_IS_XIRCOM(sc) || DC_IS_CONEXANT(sc)) { + /* + * XXX: Why does my Xircom taunt me so? + * For some reason it likes setting the CARRLOST flag + * even when the carrier is there. wtf?!? + * Who knows, but Conexant chips have the + * same problem. Maybe they took lessons + * from Xircom. + */ + if (/*sc->dc_type == DC_TYPE_21143 &&*/ + sc->dc_pmode == DC_PMODE_MII && + ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM| + DC_TXSTAT_NOCARRIER))) + txstat &= ~DC_TXSTAT_ERRSUM; + } else { + if (/*sc->dc_type == DC_TYPE_21143 &&*/ + sc->dc_pmode == DC_PMODE_MII && + ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM| + DC_TXSTAT_NOCARRIER|DC_TXSTAT_CARRLOST))) + txstat &= ~DC_TXSTAT_ERRSUM; + } + + if (txstat & DC_TXSTAT_ERRSUM) { + ifp->if_oerrors++; + if (txstat & DC_TXSTAT_EXCESSCOLL) + ifp->if_collisions++; + if (txstat & DC_TXSTAT_LATECOLL) + ifp->if_collisions++; + if (!(txstat & DC_TXSTAT_UNDERRUN)) { + dc_init(sc); + return; + } + } + + ifp->if_collisions += (txstat & DC_TXSTAT_COLLCNT) >> 3; + + ifp->if_opackets++; + if (sc->dc_cdata.dc_tx_chain[idx] != NULL) { + m_freem(sc->dc_cdata.dc_tx_chain[idx]); + sc->dc_cdata.dc_tx_chain[idx] = NULL; + } + + sc->dc_cdata.dc_tx_cnt--; + DC_INC(idx, DC_TX_LIST_CNT); + } + + sc->dc_cdata.dc_tx_cons = idx; + if (cur_tx != NULL) + ifp->if_flags &= ~IFF_OACTIVE; + + return; +} + +static void dc_tick(xsc) + void *xsc; +{ + struct dc_softc *sc; + struct mii_data *mii; + struct ifnet *ifp; + u_int32_t r; + + sc = xsc; + DC_LOCK(sc); + ifp = &sc->arpcom.ac_if; + mii = device_get_softc(sc->dc_miibus); + + if (sc->dc_flags & DC_REDUCED_MII_POLL) { + if (sc->dc_flags & DC_21143_NWAY) { + r = CSR_READ_4(sc, DC_10BTSTAT); + if (IFM_SUBTYPE(mii->mii_media_active) == + IFM_100_TX && (r & DC_TSTAT_LS100)) { + sc->dc_link = 0; + mii_mediachg(mii); + } + if (IFM_SUBTYPE(mii->mii_media_active) == + IFM_10_T && (r & DC_TSTAT_LS10)) { + sc->dc_link = 0; + mii_mediachg(mii); + } + if (sc->dc_link == 0) + mii_tick(mii); + } else { + r = CSR_READ_4(sc, DC_ISR); + if ((r & DC_ISR_RX_STATE) == DC_RXSTATE_WAIT && + sc->dc_cdata.dc_tx_cnt == 0) + mii_tick(mii); + if (!(mii->mii_media_status & IFM_ACTIVE)) + sc->dc_link = 0; + } + } else + mii_tick(mii); + + /* + * When the init routine completes, we expect to be able to send + * packets right away, and in fact the network code will send a + * gratuitous ARP the moment the init routine marks the interface + * as running. However, even though the MAC may have been initialized, + * there may be a delay of a few seconds before the PHY completes + * autonegotiation and the link is brought up. Any transmissions + * made during that delay will be lost. Dealing with this is tricky: + * we can't just pause in the init routine while waiting for the + * PHY to come ready since that would bring the whole system to + * a screeching halt for several seconds. + * + * What we do here is prevent the TX start routine from sending + * any packets until a link has been established. After the + * interface has been initialized, the tick routine will poll + * the state of the PHY until the IFM_ACTIVE flag is set. Until + * that time, packets will stay in the send queue, and once the + * link comes up, they will be flushed out to the wire. + */ + if (!sc->dc_link && mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->dc_link++; + if (ifp->if_snd.ifq_head != NULL) + dc_start(ifp); + } + + if (sc->dc_flags & DC_21143_NWAY && !sc->dc_link) + callout_reset(&sc->dc_stat_ch, hz/10, dc_tick, sc); + else + callout_reset(&sc->dc_stat_ch, hz, dc_tick, sc); + + DC_UNLOCK(sc); + + return; +} + +/* + * A transmit underrun has occurred. Back off the transmit threshold, + * or switch to store and forward mode if we have to. + */ +static void dc_tx_underrun(sc) + struct dc_softc *sc; +{ + u_int32_t isr; + int i; + + if (DC_IS_DAVICOM(sc)) + dc_init(sc); + + if (DC_IS_INTEL(sc)) { + /* + * The real 21143 requires that the transmitter be idle + * in order to change the transmit threshold or store + * and forward state. + */ + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); + + for (i = 0; i < DC_TIMEOUT; i++) { + isr = CSR_READ_4(sc, DC_ISR); + if (isr & DC_ISR_TX_IDLE) + break; + DELAY(10); + } + if (i == DC_TIMEOUT) { + printf("dc%d: failed to force tx to idle state\n", + sc->dc_unit); + dc_init(sc); + } + } + + printf("dc%d: TX underrun -- ", sc->dc_unit); + sc->dc_txthresh += DC_TXTHRESH_INC; + if (sc->dc_txthresh > DC_TXTHRESH_MAX) { + printf("using store and forward mode\n"); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); + } else { + printf("increasing TX threshold\n"); + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_THRESH); + DC_SETBIT(sc, DC_NETCFG, sc->dc_txthresh); + } + + if (DC_IS_INTEL(sc)) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); + + return; +} + +#ifdef DEVICE_POLLING +static poll_handler_t dc_poll; + +static void +dc_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + struct dc_softc *sc = ifp->if_softc; + + if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */ + /* Re-enable interrupts. */ + CSR_WRITE_4(sc, DC_IMR, DC_INTRS); + return; + } + sc->rxcycles = count; + dc_rxeof(sc); + dc_txeof(sc); + if (ifp->if_snd.ifq_head != NULL && !(ifp->if_flags & IFF_OACTIVE)) + dc_start(ifp); + + if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */ + u_int32_t status; + + status = CSR_READ_4(sc, DC_ISR); + status &= (DC_ISR_RX_WATDOGTIMEO|DC_ISR_RX_NOBUF| + DC_ISR_TX_NOBUF|DC_ISR_TX_IDLE|DC_ISR_TX_UNDERRUN| + DC_ISR_BUS_ERR); + if (!status) + return; + /* ack what we have */ + CSR_WRITE_4(sc, DC_ISR, status); + + if (status & (DC_ISR_RX_WATDOGTIMEO|DC_ISR_RX_NOBUF)) { + u_int32_t r = CSR_READ_4(sc, DC_FRAMESDISCARDED); + ifp->if_ierrors += (r & 0xffff) + ((r >> 17) & 0x7ff); + + if (dc_rx_resync(sc)) + dc_rxeof(sc); + } + /* restart transmit unit if necessary */ + if (status & DC_ISR_TX_IDLE && sc->dc_cdata.dc_tx_cnt) + CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); + + if (status & DC_ISR_TX_UNDERRUN) + dc_tx_underrun(sc); + + if (status & DC_ISR_BUS_ERR) { + printf("dc_poll: dc%d bus error\n", sc->dc_unit); + dc_reset(sc); + dc_init(sc); + } + } +} +#endif /* DEVICE_POLLING */ + +static void dc_intr(arg) + void *arg; +{ + struct dc_softc *sc; + struct ifnet *ifp; + u_int32_t status; + + sc = arg; + + if (sc->suspended) { + return; + } + + if ((CSR_READ_4(sc, DC_ISR) & DC_INTRS) == 0) + return; + + DC_LOCK(sc); + ifp = &sc->arpcom.ac_if; +#ifdef DEVICE_POLLING + if (ifp->if_ipending & IFF_POLLING) + goto done; + if (ether_poll_register(dc_poll, ifp)) { /* ok, disable interrupts */ + CSR_WRITE_4(sc, DC_IMR, 0x00000000); + goto done; + } +#endif /* DEVICE_POLLING */ + + /* Suppress unwanted interrupts */ + if (!(ifp->if_flags & IFF_UP)) { + if (CSR_READ_4(sc, DC_ISR) & DC_INTRS) + dc_stop(sc); + DC_UNLOCK(sc); + return; + } + + /* Disable interrupts. */ + CSR_WRITE_4(sc, DC_IMR, 0x00000000); + + while(((status = CSR_READ_4(sc, DC_ISR)) & DC_INTRS) + && status != 0xFFFFFFFF) { + + CSR_WRITE_4(sc, DC_ISR, status); + + if (status & DC_ISR_RX_OK) { + int curpkts; + curpkts = ifp->if_ipackets; + dc_rxeof(sc); + if (curpkts == ifp->if_ipackets) { + while(dc_rx_resync(sc)) + dc_rxeof(sc); + } + } + + if (status & (DC_ISR_TX_OK|DC_ISR_TX_NOBUF)) + dc_txeof(sc); + + if (status & DC_ISR_TX_IDLE) { + dc_txeof(sc); + if (sc->dc_cdata.dc_tx_cnt) { + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); + CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); + } + } + + if (status & DC_ISR_TX_UNDERRUN) + dc_tx_underrun(sc); + + if ((status & DC_ISR_RX_WATDOGTIMEO) + || (status & DC_ISR_RX_NOBUF)) { + int curpkts; + curpkts = ifp->if_ipackets; + dc_rxeof(sc); + if (curpkts == ifp->if_ipackets) { + while(dc_rx_resync(sc)) + dc_rxeof(sc); + } + } + + if (status & DC_ISR_BUS_ERR) { + dc_reset(sc); + dc_init(sc); + } + } + + /* Re-enable interrupts. */ + CSR_WRITE_4(sc, DC_IMR, DC_INTRS); + + if (ifp->if_snd.ifq_head != NULL) + dc_start(ifp); + +#ifdef DEVICE_POLLING +done: +#endif /* DEVICE_POLLING */ + + DC_UNLOCK(sc); + + return; +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int dc_encap(sc, m_head, txidx) + struct dc_softc *sc; + struct mbuf *m_head; + u_int32_t *txidx; +{ + struct dc_desc *f = NULL; + struct mbuf *m; + int frag, cur, cnt = 0; + + /* + * Start packing the mbufs in this chain into + * the fragment pointers. Stop when we run out + * of fragments or hit the end of the mbuf chain. + */ + m = m_head; + cur = frag = *txidx; + + for (m = m_head; m != NULL; m = m->m_next) { + if (m->m_len != 0) { + if (sc->dc_flags & DC_TX_ADMTEK_WAR) { + if (*txidx != sc->dc_cdata.dc_tx_prod && + frag == (DC_TX_LIST_CNT - 1)) + return(ENOBUFS); + } + if ((DC_TX_LIST_CNT - + (sc->dc_cdata.dc_tx_cnt + cnt)) < 5) + return(ENOBUFS); + + f = &sc->dc_ldata->dc_tx_list[frag]; + f->dc_ctl = DC_TXCTL_TLINK | m->m_len; + if (cnt == 0) { + f->dc_status = 0; + f->dc_ctl |= DC_TXCTL_FIRSTFRAG; + } else + f->dc_status = DC_TXSTAT_OWN; + f->dc_data = vtophys(mtod(m, vm_offset_t)); + cur = frag; + DC_INC(frag, DC_TX_LIST_CNT); + cnt++; + } + } + + if (m != NULL) + return(ENOBUFS); + + sc->dc_cdata.dc_tx_cnt += cnt; + sc->dc_cdata.dc_tx_chain[cur] = m_head; + sc->dc_ldata->dc_tx_list[cur].dc_ctl |= DC_TXCTL_LASTFRAG; + if (sc->dc_flags & DC_TX_INTR_FIRSTFRAG) + sc->dc_ldata->dc_tx_list[*txidx].dc_ctl |= DC_TXCTL_FINT; + if (sc->dc_flags & DC_TX_INTR_ALWAYS) + sc->dc_ldata->dc_tx_list[cur].dc_ctl |= DC_TXCTL_FINT; + if (sc->dc_flags & DC_TX_USE_TX_INTR && sc->dc_cdata.dc_tx_cnt > 64) + sc->dc_ldata->dc_tx_list[cur].dc_ctl |= DC_TXCTL_FINT; + sc->dc_ldata->dc_tx_list[*txidx].dc_status = DC_TXSTAT_OWN; + *txidx = frag; + + return(0); +} + +/* + * Coalesce an mbuf chain into a single mbuf cluster buffer. + * Needed for some really badly behaved chips that just can't + * do scatter/gather correctly. + */ +static int dc_coal(sc, m_head) + struct dc_softc *sc; + struct mbuf **m_head; +{ + struct mbuf *m_new, *m; + + m = *m_head; + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(ENOBUFS); + if (m->m_pkthdr.len > MHLEN) { + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + return(ENOBUFS); + } + } + m_copydata(m, 0, m->m_pkthdr.len, mtod(m_new, caddr_t)); + m_new->m_pkthdr.len = m_new->m_len = m->m_pkthdr.len; + m_freem(m); + *m_head = m_new; + + return(0); +} + +/* + * Main transmit routine. To avoid having to do mbuf copies, we put pointers + * to the mbuf data regions directly in the transmit lists. We also save a + * copy of the pointers since the transmit list fragment pointers are + * physical addresses. + */ + +static void dc_start(ifp) + struct ifnet *ifp; +{ + struct dc_softc *sc; + struct mbuf *m_head = NULL; + int idx; + + sc = ifp->if_softc; + + DC_LOCK(sc); + + if (!sc->dc_link && ifp->if_snd.ifq_len < 10) { + DC_UNLOCK(sc); + return; + } + + if (ifp->if_flags & IFF_OACTIVE) { + DC_UNLOCK(sc); + return; + } + + idx = sc->dc_cdata.dc_tx_prod; + + while(sc->dc_cdata.dc_tx_chain[idx] == NULL) { + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + if (sc->dc_flags & DC_TX_COALESCE && + (m_head->m_next != NULL || + sc->dc_flags & DC_TX_ALIGN)) { + if (dc_coal(sc, &m_head)) { + IF_PREPEND(&ifp->if_snd, m_head); + ifp->if_flags |= IFF_OACTIVE; + break; + } + } + + if (dc_encap(sc, m_head, &idx)) { + IF_PREPEND(&ifp->if_snd, m_head); + ifp->if_flags |= IFF_OACTIVE; + break; + } + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, m_head); + + if (sc->dc_flags & DC_TX_ONE) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + } + + /* Transmit */ + sc->dc_cdata.dc_tx_prod = idx; + if (!(sc->dc_flags & DC_TX_POLL)) + CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + + DC_UNLOCK(sc); + + return; +} + +static void dc_init(xsc) + void *xsc; +{ + struct dc_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mii_data *mii; + + DC_LOCK(sc); + + mii = device_get_softc(sc->dc_miibus); + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + dc_stop(sc); + dc_reset(sc); + + /* + * Set cache alignment and burst length. + */ + if (DC_IS_ASIX(sc) || DC_IS_DAVICOM(sc)) + CSR_WRITE_4(sc, DC_BUSCTL, 0); + else + CSR_WRITE_4(sc, DC_BUSCTL, DC_BUSCTL_MRME|DC_BUSCTL_MRLE); + /* + * Evenly share the bus between receive and transmit process. + */ + if (DC_IS_INTEL(sc)) + DC_SETBIT(sc, DC_BUSCTL, DC_BUSCTL_ARBITRATION); + if (DC_IS_DAVICOM(sc) || DC_IS_INTEL(sc)) { + DC_SETBIT(sc, DC_BUSCTL, DC_BURSTLEN_USECA); + } else { + DC_SETBIT(sc, DC_BUSCTL, DC_BURSTLEN_16LONG); + } + if (sc->dc_flags & DC_TX_POLL) + DC_SETBIT(sc, DC_BUSCTL, DC_TXPOLL_1); + switch(sc->dc_cachesize) { + case 32: + DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_32LONG); + break; + case 16: + DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_16LONG); + break; + case 8: + DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_8LONG); + break; + case 0: + default: + DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_NONE); + break; + } + + if (sc->dc_flags & DC_TX_STORENFWD) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); + else { + if (sc->dc_txthresh > DC_TXTHRESH_MAX) { + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); + } else { + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); + DC_SETBIT(sc, DC_NETCFG, sc->dc_txthresh); + } + } + + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_NO_RXCRC); + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_BACKOFF); + + if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) { + /* + * The app notes for the 98713 and 98715A say that + * in order to have the chips operate properly, a magic + * number must be written to CSR16. Macronix does not + * document the meaning of these bits so there's no way + * to know exactly what they do. The 98713 has a magic + * number all its own; the rest all use a different one. + */ + DC_CLRBIT(sc, DC_MX_MAGICPACKET, 0xFFFF0000); + if (sc->dc_type == DC_TYPE_98713) + DC_SETBIT(sc, DC_MX_MAGICPACKET, DC_MX_MAGIC_98713); + else + DC_SETBIT(sc, DC_MX_MAGICPACKET, DC_MX_MAGIC_98715); + } + + if (DC_IS_XIRCOM(sc)) { + /* + * setup General Purpose Port mode and data so the tulip + * can talk to the MII. + */ + CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_WRITE_EN | DC_SIAGP_INT1_EN | + DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT); + DELAY(10); + CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_INT1_EN | + DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT); + DELAY(10); + } + + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_THRESH); + DC_SETBIT(sc, DC_NETCFG, DC_TXTHRESH_MIN); + + /* Init circular RX list. */ + if (dc_list_rx_init(sc) == ENOBUFS) { + printf("dc%d: initialization failed: no " + "memory for rx buffers\n", sc->dc_unit); + dc_stop(sc); + DC_UNLOCK(sc); + return; + } + + /* + * Init tx descriptors. + */ + dc_list_tx_init(sc); + + /* + * Load the address of the RX list. + */ + CSR_WRITE_4(sc, DC_RXADDR, vtophys(&sc->dc_ldata->dc_rx_list[0])); + CSR_WRITE_4(sc, DC_TXADDR, vtophys(&sc->dc_ldata->dc_tx_list[0])); + + /* + * Enable interrupts. + */ +#ifdef DEVICE_POLLING + /* + * ... but only if we are not polling, and make sure they are off in + * the case of polling. Some cards (e.g. fxp) turn interrupts on + * after a reset. + */ + if (ifp->if_ipending & IFF_POLLING) + CSR_WRITE_4(sc, DC_IMR, 0x00000000); + else +#endif + CSR_WRITE_4(sc, DC_IMR, DC_INTRS); + CSR_WRITE_4(sc, DC_ISR, 0xFFFFFFFF); + + /* Enable transmitter. */ + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); + + /* + * If this is an Intel 21143 and we're not using the + * MII port, program the LED control pins so we get + * link and activity indications. + */ + if (sc->dc_flags & DC_TULIP_LEDS) { + CSR_WRITE_4(sc, DC_WATCHDOG, + DC_WDOG_CTLWREN|DC_WDOG_LINK|DC_WDOG_ACTIVITY); + CSR_WRITE_4(sc, DC_WATCHDOG, 0); + } + + /* + * Load the RX/multicast filter. We do this sort of late + * because the filter programming scheme on the 21143 and + * some clones requires DMAing a setup frame via the TX + * engine, and we need the transmitter enabled for that. + */ + dc_setfilt(sc); + + /* Enable receiver. */ + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ON); + CSR_WRITE_4(sc, DC_RXSTART, 0xFFFFFFFF); + + mii_mediachg(mii); + dc_setcfg(sc, sc->dc_if_media); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + /* Don't start the ticker if this is a homePNA link. */ + if (IFM_SUBTYPE(mii->mii_media.ifm_media) == IFM_HPNA_1) + sc->dc_link = 1; + else { + if (sc->dc_flags & DC_21143_NWAY) + callout_reset(&sc->dc_stat_ch, hz/10, dc_tick, sc); + else + callout_reset(&sc->dc_stat_ch, hz, dc_tick, sc); + } + +#ifdef SRM_MEDIA + if(sc->dc_srm_media) { + struct ifreq ifr; + + ifr.ifr_media = sc->dc_srm_media; + ifmedia_ioctl(ifp, &ifr, &mii->mii_media, SIOCSIFMEDIA); + sc->dc_srm_media = 0; + } +#endif + DC_UNLOCK(sc); + return; +} + +/* + * Set media options. + */ +static int dc_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct dc_softc *sc; + struct mii_data *mii; + struct ifmedia *ifm; + + sc = ifp->if_softc; + mii = device_get_softc(sc->dc_miibus); + mii_mediachg(mii); + ifm = &mii->mii_media; + + if (DC_IS_DAVICOM(sc) && + IFM_SUBTYPE(ifm->ifm_media) == IFM_HPNA_1) + dc_setcfg(sc, ifm->ifm_media); + else + sc->dc_link = 0; + + return(0); +} + +/* + * Report current media status. + */ +static void dc_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct dc_softc *sc; + struct mii_data *mii; + struct ifmedia *ifm; + + sc = ifp->if_softc; + mii = device_get_softc(sc->dc_miibus); + mii_pollstat(mii); + ifm = &mii->mii_media; + if (DC_IS_DAVICOM(sc)) { + if (IFM_SUBTYPE(ifm->ifm_media) == IFM_HPNA_1) { + ifmr->ifm_active = ifm->ifm_media; + ifmr->ifm_status = 0; + return; + } + } + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + + return; +} + +static int dc_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct dc_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + struct mii_data *mii; + int error = 0; + + DC_LOCK(sc); + + switch(command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING && + ifp->if_flags & IFF_PROMISC && + !(sc->dc_if_flags & IFF_PROMISC)) { + dc_setfilt(sc); + } else if (ifp->if_flags & IFF_RUNNING && + !(ifp->if_flags & IFF_PROMISC) && + sc->dc_if_flags & IFF_PROMISC) { + dc_setfilt(sc); + } else if (!(ifp->if_flags & IFF_RUNNING)) { + sc->dc_txthresh = 0; + dc_init(sc); + } + } else { + if (ifp->if_flags & IFF_RUNNING) + dc_stop(sc); + } + sc->dc_if_flags = ifp->if_flags; + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + dc_setfilt(sc); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = device_get_softc(sc->dc_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); +#ifdef SRM_MEDIA + if (sc->dc_srm_media) + sc->dc_srm_media = 0; +#endif + break; + default: + error = EINVAL; + break; + } + + DC_UNLOCK(sc); + + return(error); +} + +static void dc_watchdog(ifp) + struct ifnet *ifp; +{ + struct dc_softc *sc; + + sc = ifp->if_softc; + + DC_LOCK(sc); + + ifp->if_oerrors++; + printf("dc%d: watchdog timeout\n", sc->dc_unit); + + dc_stop(sc); + dc_reset(sc); + dc_init(sc); + + if (ifp->if_snd.ifq_head != NULL) + dc_start(ifp); + + DC_UNLOCK(sc); + + return; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void dc_stop(sc) + struct dc_softc *sc; +{ + register int i; + struct ifnet *ifp; + + DC_LOCK(sc); + + ifp = &sc->arpcom.ac_if; + ifp->if_timer = 0; + + callout_stop(&sc->dc_stat_ch); + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); +#ifdef DEVICE_POLLING + ether_poll_deregister(ifp); +#endif + + DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_RX_ON|DC_NETCFG_TX_ON)); + CSR_WRITE_4(sc, DC_IMR, 0x00000000); + CSR_WRITE_4(sc, DC_TXADDR, 0x00000000); + CSR_WRITE_4(sc, DC_RXADDR, 0x00000000); + sc->dc_link = 0; + + /* + * Free data in the RX lists. + */ + for (i = 0; i < DC_RX_LIST_CNT; i++) { + if (sc->dc_cdata.dc_rx_chain[i] != NULL) { + m_freem(sc->dc_cdata.dc_rx_chain[i]); + sc->dc_cdata.dc_rx_chain[i] = NULL; + } + } + bzero((char *)&sc->dc_ldata->dc_rx_list, + sizeof(sc->dc_ldata->dc_rx_list)); + + /* + * Free the TX list buffers. + */ + for (i = 0; i < DC_TX_LIST_CNT; i++) { + if (sc->dc_cdata.dc_tx_chain[i] != NULL) { + if (sc->dc_ldata->dc_tx_list[i].dc_ctl & + DC_TXCTL_SETUP) { + sc->dc_cdata.dc_tx_chain[i] = NULL; + continue; + } + m_freem(sc->dc_cdata.dc_tx_chain[i]); + sc->dc_cdata.dc_tx_chain[i] = NULL; + } + } + + bzero((char *)&sc->dc_ldata->dc_tx_list, + sizeof(sc->dc_ldata->dc_tx_list)); + + DC_UNLOCK(sc); + + return; +} + +/* + * Device suspend routine. Stop the interface and save some PCI + * settings in case the BIOS doesn't restore them properly on + * resume. + */ +static int dc_suspend(dev) + device_t dev; +{ + register int i; + int s; + struct dc_softc *sc; + + s = splimp(); + + sc = device_get_softc(dev); + + dc_stop(sc); + + for (i = 0; i < 5; i++) + sc->saved_maps[i] = pci_read_config(dev, PCIR_MAPS + i * 4, 4); + sc->saved_biosaddr = pci_read_config(dev, PCIR_BIOS, 4); + sc->saved_intline = pci_read_config(dev, PCIR_INTLINE, 1); + sc->saved_cachelnsz = pci_read_config(dev, PCIR_CACHELNSZ, 1); + sc->saved_lattimer = pci_read_config(dev, PCIR_LATTIMER, 1); + + sc->suspended = 1; + + splx(s); + return (0); +} + +/* + * Device resume routine. Restore some PCI settings in case the BIOS + * doesn't, re-enable busmastering, and restart the interface if + * appropriate. + */ +static int dc_resume(dev) + device_t dev; +{ + register int i; + int s; + struct dc_softc *sc; + struct ifnet *ifp; + + s = splimp(); + + sc = device_get_softc(dev); + ifp = &sc->arpcom.ac_if; + + dc_acpi(dev); + + /* better way to do this? */ + for (i = 0; i < 5; i++) + pci_write_config(dev, PCIR_MAPS + i * 4, sc->saved_maps[i], 4); + pci_write_config(dev, PCIR_BIOS, sc->saved_biosaddr, 4); + pci_write_config(dev, PCIR_INTLINE, sc->saved_intline, 1); + pci_write_config(dev, PCIR_CACHELNSZ, sc->saved_cachelnsz, 1); + pci_write_config(dev, PCIR_LATTIMER, sc->saved_lattimer, 1); + + /* reenable busmastering */ + pci_enable_busmaster(dev); + pci_enable_io(dev, DC_RES); + + /* reinitialize interface if necessary */ + if (ifp->if_flags & IFF_UP) + dc_init(sc); + + sc->suspended = 0; + + splx(s); + return (0); +} + +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static void dc_shutdown(dev) + device_t dev; +{ + struct dc_softc *sc; + + sc = device_get_softc(dev); + + dc_stop(sc); + + return; +} diff --git a/sys/pci/if_dcreg.h b/sys/pci/if_dcreg.h new file mode 100644 index 0000000..5c0879a --- /dev/null +++ b/sys/pci/if_dcreg.h @@ -0,0 +1,1147 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ee.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * 21143 and clone common register definitions. + */ + +#define DC_BUSCTL 0x00 /* bus control */ +#define DC_TXSTART 0x08 /* tx start demand */ +#define DC_RXSTART 0x10 /* rx start demand */ +#define DC_RXADDR 0x18 /* rx descriptor list start addr */ +#define DC_TXADDR 0x20 /* tx descriptor list start addr */ +#define DC_ISR 0x28 /* interrupt status register */ +#define DC_NETCFG 0x30 /* network config register */ +#define DC_IMR 0x38 /* interrupt mask */ +#define DC_FRAMESDISCARDED 0x40 /* # of discarded frames */ +#define DC_SIO 0x48 /* MII and ROM/EEPROM access */ +#define DC_ROM 0x50 /* ROM programming address */ +#define DC_TIMER 0x58 /* general timer */ +#define DC_10BTSTAT 0x60 /* SIA status */ +#define DC_SIARESET 0x68 /* SIA connectivity */ +#define DC_10BTCTRL 0x70 /* SIA transmit and receive */ +#define DC_WATCHDOG 0x78 /* SIA and general purpose port */ +#define DC_SIAGP 0x78 /* SIA and general purpose port (X3201) */ + +/* + * There are two general 'types' of MX chips that we need to be + * concerned with. One is the original 98713, which has its internal + * NWAY support controlled via the MDIO bits in the serial I/O + * register. The other is everything else (from the 98713A on up), + * which has its internal NWAY controlled via CSR13, CSR14 and CSR15, + * just like the 21143. This type setting also governs which of the + * 'magic' numbers we write to CSR16. The PNIC II falls into the + * 98713A/98715/98715A/98725 category. + */ +#define DC_TYPE_98713 0x1 +#define DC_TYPE_98713A 0x2 +#define DC_TYPE_987x5 0x3 + +/* Other type of supported chips. */ +#define DC_TYPE_21143 0x4 /* Intel 21143 */ +#define DC_TYPE_ASIX 0x5 /* ASIX AX88140A/AX88141 */ +#define DC_TYPE_AL981 0x6 /* ADMtek AL981 Comet */ +#define DC_TYPE_AN985 0x7 /* ADMtek AN985 Centaur */ +#define DC_TYPE_DM9102 0x8 /* Davicom DM9102 */ +#define DC_TYPE_PNICII 0x9 /* 82c115 PNIC II */ +#define DC_TYPE_PNIC 0xA /* 82c168/82c169 PNIC I */ +#define DC_TYPE_XIRCOM 0xB /* Xircom X3201 */ +#define DC_TYPE_CONEXANT 0xC /* Conexant LANfinity RS7112 */ + +#define DC_IS_MACRONIX(x) \ + (x->dc_type == DC_TYPE_98713 || \ + x->dc_type == DC_TYPE_98713A || \ + x->dc_type == DC_TYPE_987x5) + +#define DC_IS_ADMTEK(x) \ + (x->dc_type == DC_TYPE_AL981 || \ + x->dc_type == DC_TYPE_AN985) + +#define DC_IS_INTEL(x) (x->dc_type == DC_TYPE_21143) +#define DC_IS_ASIX(x) (x->dc_type == DC_TYPE_ASIX) +#define DC_IS_COMET(x) (x->dc_type == DC_TYPE_AL981) +#define DC_IS_CENTAUR(x) (x->dc_type == DC_TYPE_AN985) +#define DC_IS_DAVICOM(x) (x->dc_type == DC_TYPE_DM9102) +#define DC_IS_PNICII(x) (x->dc_type == DC_TYPE_PNICII) +#define DC_IS_PNIC(x) (x->dc_type == DC_TYPE_PNIC) +#define DC_IS_XIRCOM(x) (x->dc_type == DC_TYPE_XIRCOM) +#define DC_IS_CONEXANT(x) (x->dc_type == DC_TYPE_CONEXANT) + +/* MII/symbol mode port types */ +#define DC_PMODE_MII 0x1 +#define DC_PMODE_SYM 0x2 +#define DC_PMODE_SIA 0x3 + +/* + * Bus control bits. + */ +#define DC_BUSCTL_RESET 0x00000001 +#define DC_BUSCTL_ARBITRATION 0x00000002 +#define DC_BUSCTL_SKIPLEN 0x0000007C +#define DC_BUSCTL_BUF_BIGENDIAN 0x00000080 +#define DC_BUSCTL_BURSTLEN 0x00003F00 +#define DC_BUSCTL_CACHEALIGN 0x0000C000 +#define DC_BUSCTL_TXPOLL 0x000E0000 +#define DC_BUSCTL_DBO 0x00100000 +#define DC_BUSCTL_MRME 0x00200000 +#define DC_BUSCTL_MRLE 0x00800000 +#define DC_BUSCTL_MWIE 0x01000000 +#define DC_BUSCTL_ONNOW_ENB 0x04000000 + +#define DC_SKIPLEN_1LONG 0x00000004 +#define DC_SKIPLEN_2LONG 0x00000008 +#define DC_SKIPLEN_3LONG 0x00000010 +#define DC_SKIPLEN_4LONG 0x00000020 +#define DC_SKIPLEN_5LONG 0x00000040 + +#define DC_CACHEALIGN_NONE 0x00000000 +#define DC_CACHEALIGN_8LONG 0x00004000 +#define DC_CACHEALIGN_16LONG 0x00008000 +#define DC_CACHEALIGN_32LONG 0x0000C000 + +#define DC_BURSTLEN_USECA 0x00000000 +#define DC_BURSTLEN_1LONG 0x00000100 +#define DC_BURSTLEN_2LONG 0x00000200 +#define DC_BURSTLEN_4LONG 0x00000400 +#define DC_BURSTLEN_8LONG 0x00000800 +#define DC_BURSTLEN_16LONG 0x00001000 +#define DC_BURSTLEN_32LONG 0x00002000 + +#define DC_TXPOLL_OFF 0x00000000 +#define DC_TXPOLL_1 0x00020000 +#define DC_TXPOLL_2 0x00040000 +#define DC_TXPOLL_3 0x00060000 +#define DC_TXPOLL_4 0x00080000 +#define DC_TXPOLL_5 0x000A0000 +#define DC_TXPOLL_6 0x000C0000 +#define DC_TXPOLL_7 0x000E0000 + +/* + * Interrupt status bits. + */ +#define DC_ISR_TX_OK 0x00000001 +#define DC_ISR_TX_IDLE 0x00000002 +#define DC_ISR_TX_NOBUF 0x00000004 +#define DC_ISR_TX_JABBERTIMEO 0x00000008 +#define DC_ISR_LINKGOOD 0x00000010 +#define DC_ISR_TX_UNDERRUN 0x00000020 +#define DC_ISR_RX_OK 0x00000040 +#define DC_ISR_RX_NOBUF 0x00000080 +#define DC_ISR_RX_READ 0x00000100 +#define DC_ISR_RX_WATDOGTIMEO 0x00000200 +#define DC_ISR_TX_EARLY 0x00000400 +#define DC_ISR_TIMER_EXPIRED 0x00000800 +#define DC_ISR_LINKFAIL 0x00001000 +#define DC_ISR_BUS_ERR 0x00002000 +#define DC_ISR_RX_EARLY 0x00004000 +#define DC_ISR_ABNORMAL 0x00008000 +#define DC_ISR_NORMAL 0x00010000 +#define DC_ISR_RX_STATE 0x000E0000 +#define DC_ISR_TX_STATE 0x00700000 +#define DC_ISR_BUSERRTYPE 0x03800000 +#define DC_ISR_100MBPSLINK 0x08000000 +#define DC_ISR_MAGICKPACK 0x10000000 + +#define DC_RXSTATE_STOPPED 0x00000000 /* 000 - Stopped */ +#define DC_RXSTATE_FETCH 0x00020000 /* 001 - Fetching descriptor */ +#define DC_RXSTATE_ENDCHECK 0x00040000 /* 010 - check for rx end */ +#define DC_RXSTATE_WAIT 0x00060000 /* 011 - waiting for packet */ +#define DC_RXSTATE_SUSPEND 0x00080000 /* 100 - suspend rx */ +#define DC_RXSTATE_CLOSE 0x000A0000 /* 101 - close tx desc */ +#define DC_RXSTATE_FLUSH 0x000C0000 /* 110 - flush from FIFO */ +#define DC_RXSTATE_DEQUEUE 0x000E0000 /* 111 - dequeue from FIFO */ + +#define DC_TXSTATE_RESET 0x00000000 /* 000 - reset */ +#define DC_TXSTATE_FETCH 0x00100000 /* 001 - fetching descriptor */ +#define DC_TXSTATE_WAITEND 0x00200000 /* 010 - wait for tx end */ +#define DC_TXSTATE_READING 0x00300000 /* 011 - read and enqueue */ +#define DC_TXSTATE_RSVD 0x00400000 /* 100 - reserved */ +#define DC_TXSTATE_SETUP 0x00500000 /* 101 - setup packet */ +#define DC_TXSTATE_SUSPEND 0x00600000 /* 110 - suspend tx */ +#define DC_TXSTATE_CLOSE 0x00700000 /* 111 - close tx desc */ + +/* + * Network config bits. + */ +#define DC_NETCFG_RX_HASHPERF 0x00000001 +#define DC_NETCFG_RX_ON 0x00000002 +#define DC_NETCFG_RX_HASHONLY 0x00000004 +#define DC_NETCFG_RX_BADFRAMES 0x00000008 +#define DC_NETCFG_RX_INVFILT 0x00000010 +#define DC_NETCFG_BACKOFFCNT 0x00000020 +#define DC_NETCFG_RX_PROMISC 0x00000040 +#define DC_NETCFG_RX_ALLMULTI 0x00000080 +#define DC_NETCFG_FULLDUPLEX 0x00000200 +#define DC_NETCFG_LOOPBACK 0x00000C00 +#define DC_NETCFG_FORCECOLL 0x00001000 +#define DC_NETCFG_TX_ON 0x00002000 +#define DC_NETCFG_TX_THRESH 0x0000C000 +#define DC_NETCFG_TX_BACKOFF 0x00020000 +#define DC_NETCFG_PORTSEL 0x00040000 /* 0 == 10, 1 == 100 */ +#define DC_NETCFG_HEARTBEAT 0x00080000 +#define DC_NETCFG_STORENFWD 0x00200000 +#define DC_NETCFG_SPEEDSEL 0x00400000 /* 1 == 10, 0 == 100 */ +#define DC_NETCFG_PCS 0x00800000 +#define DC_NETCFG_SCRAMBLER 0x01000000 +#define DC_NETCFG_NO_RXCRC 0x02000000 +#define DC_NETCFG_RX_ALL 0x40000000 +#define DC_NETCFG_CAPEFFECT 0x80000000 + +#define DC_OPMODE_NORM 0x00000000 +#define DC_OPMODE_INTLOOP 0x00000400 +#define DC_OPMODE_EXTLOOP 0x00000800 + +#if 0 +#define DC_TXTHRESH_72BYTES 0x00000000 +#define DC_TXTHRESH_96BYTES 0x00004000 +#define DC_TXTHRESH_128BYTES 0x00008000 +#define DC_TXTHRESH_160BYTES 0x0000C000 +#endif + +#define DC_TXTHRESH_MIN 0x00000000 +#define DC_TXTHRESH_INC 0x00004000 +#define DC_TXTHRESH_MAX 0x0000C000 + + +/* + * Interrupt mask bits. + */ +#define DC_IMR_TX_OK 0x00000001 +#define DC_IMR_TX_IDLE 0x00000002 +#define DC_IMR_TX_NOBUF 0x00000004 +#define DC_IMR_TX_JABBERTIMEO 0x00000008 +#define DC_IMR_LINKGOOD 0x00000010 +#define DC_IMR_TX_UNDERRUN 0x00000020 +#define DC_IMR_RX_OK 0x00000040 +#define DC_IMR_RX_NOBUF 0x00000080 +#define DC_IMR_RX_READ 0x00000100 +#define DC_IMR_RX_WATDOGTIMEO 0x00000200 +#define DC_IMR_TX_EARLY 0x00000400 +#define DC_IMR_TIMER_EXPIRED 0x00000800 +#define DC_IMR_LINKFAIL 0x00001000 +#define DC_IMR_BUS_ERR 0x00002000 +#define DC_IMR_RX_EARLY 0x00004000 +#define DC_IMR_ABNORMAL 0x00008000 +#define DC_IMR_NORMAL 0x00010000 +#define DC_IMR_100MBPSLINK 0x08000000 +#define DC_IMR_MAGICKPACK 0x10000000 + +#define DC_INTRS \ + (DC_IMR_RX_OK|DC_IMR_TX_OK|DC_IMR_RX_NOBUF|DC_IMR_RX_WATDOGTIMEO|\ + DC_IMR_TX_NOBUF|DC_IMR_TX_UNDERRUN|DC_IMR_BUS_ERR| \ + DC_IMR_ABNORMAL|DC_IMR_NORMAL/*|DC_IMR_TX_EARLY*/) +/* + * Serial I/O (EEPROM/ROM) bits. + */ +#define DC_SIO_EE_CS 0x00000001 /* EEPROM chip select */ +#define DC_SIO_EE_CLK 0x00000002 /* EEPROM clock */ +#define DC_SIO_EE_DATAIN 0x00000004 /* EEPROM data output */ +#define DC_SIO_EE_DATAOUT 0x00000008 /* EEPROM data input */ +#define DC_SIO_ROMDATA4 0x00000010 +#define DC_SIO_ROMDATA5 0x00000020 +#define DC_SIO_ROMDATA6 0x00000040 +#define DC_SIO_ROMDATA7 0x00000080 +#define DC_SIO_EESEL 0x00000800 +#define DC_SIO_ROMSEL 0x00001000 +#define DC_SIO_ROMCTL_WRITE 0x00002000 +#define DC_SIO_ROMCTL_READ 0x00004000 +#define DC_SIO_MII_CLK 0x00010000 /* MDIO clock */ +#define DC_SIO_MII_DATAOUT 0x00020000 /* MDIO data out */ +#define DC_SIO_MII_DIR 0x00040000 /* MDIO dir */ +#define DC_SIO_MII_DATAIN 0x00080000 /* MDIO data in */ + +#define DC_EECMD_WRITE 0x140 +#define DC_EECMD_READ 0x180 +#define DC_EECMD_ERASE 0x1c0 + +#define DC_EE_NODEADDR_OFFSET 0x70 +#define DC_EE_NODEADDR 10 + +/* + * General purpose timer register + */ +#define DC_TIMER_VALUE 0x0000FFFF +#define DC_TIMER_CONTINUOUS 0x00010000 + +/* + * 10baseT status register + */ +#define DC_TSTAT_MIIACT 0x00000001 /* MII port activity */ +#define DC_TSTAT_LS100 0x00000002 /* link status of 100baseTX */ +#define DC_TSTAT_LS10 0x00000004 /* link status of 10baseT */ +#define DC_TSTAT_AUTOPOLARITY 0x00000008 +#define DC_TSTAT_AUIACT 0x00000100 /* AUI activity */ +#define DC_TSTAT_10BTACT 0x00000200 /* 10baseT activity */ +#define DC_TSTAT_NSN 0x00000400 /* non-stable FLPs detected */ +#define DC_TSTAT_REMFAULT 0x00000800 +#define DC_TSTAT_ANEGSTAT 0x00007000 +#define DC_TSTAT_LP_CAN_NWAY 0x00008000 /* link partner supports NWAY */ +#define DC_TSTAT_LPCODEWORD 0xFFFF0000 /* link partner's code word */ + +#define DC_ASTAT_DISABLE 0x00000000 +#define DC_ASTAT_TXDISABLE 0x00001000 +#define DC_ASTAT_ABDETECT 0x00002000 +#define DC_ASTAT_ACKDETECT 0x00003000 +#define DC_ASTAT_CMPACKDETECT 0x00004000 +#define DC_ASTAT_AUTONEGCMP 0x00005000 +#define DC_ASTAT_LINKCHECK 0x00006000 + +/* + * PHY reset register + */ +#define DC_SIA_RESET 0x00000001 +#define DC_SIA_AUI 0x00000008 /* AUI or 10baseT */ + +/* + * 10baseT control register + */ +#define DC_TCTL_ENCODER_ENB 0x00000001 +#define DC_TCTL_LOOPBACK 0x00000002 +#define DC_TCTL_DRIVER_ENB 0x00000004 +#define DC_TCTL_LNKPULSE_ENB 0x00000008 +#define DC_TCTL_HALFDUPLEX 0x00000040 +#define DC_TCTL_AUTONEGENBL 0x00000080 +#define DC_TCTL_RX_SQUELCH 0x00000100 +#define DC_TCTL_COLL_SQUELCH 0x00000200 +#define DC_TCTL_COLL_DETECT 0x00000400 +#define DC_TCTL_SQE_ENB 0x00000800 +#define DC_TCTL_LINKTEST 0x00001000 +#define DC_TCTL_AUTOPOLARITY 0x00002000 +#define DC_TCTL_SET_POL_PLUS 0x00004000 +#define DC_TCTL_AUTOSENSE 0x00008000 /* 10bt/AUI autosense */ +#define DC_TCTL_100BTXHALF 0x00010000 +#define DC_TCTL_100BTXFULL 0x00020000 +#define DC_TCTL_100BT4 0x00040000 + +/* + * Watchdog timer register + */ +#define DC_WDOG_JABBERDIS 0x00000001 +#define DC_WDOG_HOSTUNJAB 0x00000002 +#define DC_WDOG_JABBERCLK 0x00000004 +#define DC_WDOG_RXWDOGDIS 0x00000010 +#define DC_WDOG_RXWDOGCLK 0x00000020 +#define DC_WDOG_MUSTBEZERO 0x00000100 +#define DC_WDOG_AUIBNC 0x00100000 +#define DC_WDOG_ACTIVITY 0x00200000 +#define DC_WDOG_RX_MATCH 0x00400000 +#define DC_WDOG_LINK 0x00800000 +#define DC_WDOG_CTLWREN 0x08000000 + +/* + * SIA and General Purpose Port register (X3201) + */ +#define DC_SIAGP_RXMATCH 0x40000000 +#define DC_SIAGP_INT1 0x20000000 +#define DC_SIAGP_INT0 0x10000000 +#define DC_SIAGP_WRITE_EN 0x08000000 +#define DC_SIAGP_RXMATCH_EN 0x04000000 +#define DC_SIAGP_INT1_EN 0x02000000 +#define DC_SIAGP_INT0_EN 0x01000000 +#define DC_SIAGP_LED3 0x00800000 +#define DC_SIAGP_LED2 0x00400000 +#define DC_SIAGP_LED1 0x00200000 +#define DC_SIAGP_LED0 0x00100000 +#define DC_SIAGP_MD_GP3_OUTPUT 0x00080000 +#define DC_SIAGP_MD_GP2_OUTPUT 0x00040000 +#define DC_SIAGP_MD_GP1_OUTPUT 0x00020000 +#define DC_SIAGP_MD_GP0_OUTPUT 0x00010000 + +/* + * Size of a setup frame. + */ +#define DC_SFRAME_LEN 192 + +/* + * 21x4x TX/RX list structure. + */ + +struct dc_desc { + u_int32_t dc_status; + u_int32_t dc_ctl; + u_int32_t dc_ptr1; + u_int32_t dc_ptr2; +}; + +#define dc_data dc_ptr1 +#define dc_next dc_ptr2 + +#define DC_RXSTAT_FIFOOFLOW 0x00000001 +#define DC_RXSTAT_CRCERR 0x00000002 +#define DC_RXSTAT_DRIBBLE 0x00000004 +#define DC_RXSTAT_MIIERE 0x00000008 +#define DC_RXSTAT_WATCHDOG 0x00000010 +#define DC_RXSTAT_FRAMETYPE 0x00000020 /* 0 == IEEE 802.3 */ +#define DC_RXSTAT_COLLSEEN 0x00000040 +#define DC_RXSTAT_GIANT 0x00000080 +#define DC_RXSTAT_LASTFRAG 0x00000100 +#define DC_RXSTAT_FIRSTFRAG 0x00000200 +#define DC_RXSTAT_MULTICAST 0x00000400 +#define DC_RXSTAT_RUNT 0x00000800 +#define DC_RXSTAT_RXTYPE 0x00003000 +#define DC_RXSTAT_DE 0x00004000 +#define DC_RXSTAT_RXERR 0x00008000 +#define DC_RXSTAT_RXLEN 0x3FFF0000 +#define DC_RXSTAT_OWN 0x80000000 + +#define DC_RXBYTES(x) ((x & DC_RXSTAT_RXLEN) >> 16) +#define DC_RXSTAT (DC_RXSTAT_FIRSTFRAG|DC_RXSTAT_LASTFRAG|DC_RXSTAT_OWN) + +#define DC_RXCTL_BUFLEN1 0x00000FFF +#define DC_RXCTL_BUFLEN2 0x00FFF000 +#define DC_RXCTL_RLINK 0x01000000 +#define DC_RXCTL_RLAST 0x02000000 + +#define DC_TXSTAT_DEFER 0x00000001 +#define DC_TXSTAT_UNDERRUN 0x00000002 +#define DC_TXSTAT_LINKFAIL 0x00000003 +#define DC_TXSTAT_COLLCNT 0x00000078 +#define DC_TXSTAT_SQE 0x00000080 +#define DC_TXSTAT_EXCESSCOLL 0x00000100 +#define DC_TXSTAT_LATECOLL 0x00000200 +#define DC_TXSTAT_NOCARRIER 0x00000400 +#define DC_TXSTAT_CARRLOST 0x00000800 +#define DC_TXSTAT_JABTIMEO 0x00004000 +#define DC_TXSTAT_ERRSUM 0x00008000 +#define DC_TXSTAT_OWN 0x80000000 + +#define DC_TXCTL_BUFLEN1 0x000007FF +#define DC_TXCTL_BUFLEN2 0x003FF800 +#define DC_TXCTL_FILTTYPE0 0x00400000 +#define DC_TXCTL_PAD 0x00800000 +#define DC_TXCTL_TLINK 0x01000000 +#define DC_TXCTL_TLAST 0x02000000 +#define DC_TXCTL_NOCRC 0x04000000 +#define DC_TXCTL_SETUP 0x08000000 +#define DC_TXCTL_FILTTYPE1 0x10000000 +#define DC_TXCTL_FIRSTFRAG 0x20000000 +#define DC_TXCTL_LASTFRAG 0x40000000 +#define DC_TXCTL_FINT 0x80000000 + +#define DC_FILTER_PERFECT 0x00000000 +#define DC_FILTER_HASHPERF 0x00400000 +#define DC_FILTER_INVERSE 0x10000000 +#define DC_FILTER_HASHONLY 0x10400000 + +#define DC_MAXFRAGS 16 +#ifdef DEVICE_POLLING +#define DC_RX_LIST_CNT 192 +#else +#define DC_RX_LIST_CNT 64 +#endif +#define DC_TX_LIST_CNT 256 +#define DC_MIN_FRAMELEN 60 +#define DC_RXLEN 1536 + +#define DC_INC(x, y) (x) = (x + 1) % y + +struct dc_list_data { + struct dc_desc dc_rx_list[DC_RX_LIST_CNT]; + struct dc_desc dc_tx_list[DC_TX_LIST_CNT]; +}; + +struct dc_chain_data { + struct mbuf *dc_rx_chain[DC_RX_LIST_CNT]; + struct mbuf *dc_tx_chain[DC_TX_LIST_CNT]; + u_int32_t dc_sbuf[DC_SFRAME_LEN/sizeof(u_int32_t)]; + u_int8_t dc_pad[DC_MIN_FRAMELEN]; + int dc_tx_prod; + int dc_tx_cons; + int dc_tx_cnt; + int dc_rx_prod; +}; + +struct dc_mediainfo { + int dc_media; + u_int8_t *dc_gp_ptr; + u_int8_t dc_gp_len; + u_int8_t *dc_reset_ptr; + u_int8_t dc_reset_len; + struct dc_mediainfo *dc_next; +}; + + +struct dc_type { + u_int16_t dc_vid; + u_int16_t dc_did; + char *dc_name; +}; + +struct dc_mii_frame { + u_int8_t mii_stdelim; + u_int8_t mii_opcode; + u_int8_t mii_phyaddr; + u_int8_t mii_regaddr; + u_int8_t mii_turnaround; + u_int16_t mii_data; +}; + +/* + * MII constants + */ +#define DC_MII_STARTDELIM 0x01 +#define DC_MII_READOP 0x02 +#define DC_MII_WRITEOP 0x01 +#define DC_MII_TURNAROUND 0x02 + + +/* + * Registers specific to clone devices. + * This mainly relates to RX filter programming: not all 21x4x clones + * use the standard DEC filter programming mechanism. + */ + +/* + * ADMtek specific registers and constants for the AL981 and AN985. + * The AN985 doesn't use the magic PHY registers. + */ +#define DC_AL_PAR0 0xA4 /* station address */ +#define DC_AL_PAR1 0xA8 /* station address */ +#define DC_AL_MAR0 0xAC /* multicast hash filter */ +#define DC_AL_MAR1 0xB0 /* multicast hash filter */ +#define DC_AL_BMCR 0xB4 /* built in PHY control */ +#define DC_AL_BMSR 0xB8 /* built in PHY status */ +#define DC_AL_VENID 0xBC /* built in PHY ID0 */ +#define DC_AL_DEVID 0xC0 /* built in PHY ID1 */ +#define DC_AL_ANAR 0xC4 /* built in PHY autoneg advert */ +#define DC_AL_LPAR 0xC8 /* bnilt in PHY link part. ability */ +#define DC_AL_ANER 0xCC /* built in PHY autoneg expansion */ + +#define DC_ADMTEK_PHYADDR 0x1 +#define DC_AL_EE_NODEADDR 4 +/* End of ADMtek specific registers */ + +/* + * ASIX specific registers. + */ +#define DC_AX_FILTIDX 0x68 /* RX filter index */ +#define DC_AX_FILTDATA 0x70 /* RX filter data */ + +/* + * Special ASIX-specific bits in the ASIX NETCFG register (CSR6). + */ +#define DC_AX_NETCFG_RX_BROAD 0x00000100 + +/* + * RX Filter Index Register values + */ +#define DC_AX_FILTIDX_PAR0 0x00000000 +#define DC_AX_FILTIDX_PAR1 0x00000001 +#define DC_AX_FILTIDX_MAR0 0x00000002 +#define DC_AX_FILTIDX_MAR1 0x00000003 +/* End of ASIX specific registers */ + +/* + * Macronix specific registers. The Macronix chips have a special + * register for reading the NWAY status, which we don't use, plus + * a magic packet register, which we need to tweak a bit per the + * Macronix application notes. + */ +#define DC_MX_MAGICPACKET 0x80 +#define DC_MX_NWAYSTAT 0xA0 + +/* + * Magic packet register + */ +#define DC_MX_MPACK_DISABLE 0x00400000 + +/* + * NWAY status register. + */ +#define DC_MX_NWAY_10BTHALF 0x08000000 +#define DC_MX_NWAY_10BTFULL 0x10000000 +#define DC_MX_NWAY_100BTHALF 0x20000000 +#define DC_MX_NWAY_100BTFULL 0x40000000 +#define DC_MX_NWAY_100BT4 0x80000000 + +/* + * These are magic values that must be written into CSR16 + * (DC_MX_MAGICPACKET) in order to put the chip into proper + * operating mode. The magic numbers are documented in the + * Macronix 98715 application notes. + */ +#define DC_MX_MAGIC_98713 0x0F370000 +#define DC_MX_MAGIC_98713A 0x0B3C0000 +#define DC_MX_MAGIC_98715 0x0B3C0000 +#define DC_MX_MAGIC_98725 0x0B3C0000 +/* End of Macronix specific registers */ + +/* + * PNIC 82c168/82c169 specific registers. + * The PNIC has its own special NWAY support, which doesn't work, + * and shortcut ways of reading the EEPROM and MII bus. + */ +#define DC_PN_GPIO 0x60 /* general purpose pins control */ +#define DC_PN_PWRUP_CFG 0x90 /* config register, set by EEPROM */ +#define DC_PN_SIOCTL 0x98 /* serial EEPROM control register */ +#define DC_PN_MII 0xA0 /* MII access register */ +#define DC_PN_NWAY 0xB8 /* Internal NWAY register */ + +/* Serial I/O EEPROM register */ +#define DC_PN_SIOCTL_DATA 0x0000003F +#define DC_PN_SIOCTL_OPCODE 0x00000300 +#define DC_PN_SIOCTL_BUSY 0x80000000 + +#define DC_PN_EEOPCODE_ERASE 0x00000300 +#define DC_PN_EEOPCODE_READ 0x00000600 +#define DC_PN_EEOPCODE_WRITE 0x00000100 + +/* + * The first two general purpose pins control speed selection and + * 100Mbps loopback on the 82c168 chip. The control bits should always + * be set (to make the data pins outputs) and the speed selction and + * loopback bits set accordingly when changing media. Physically, this + * will set the state of a relay mounted on the card. + */ +#define DC_PN_GPIO_DATA0 0x000000001 +#define DC_PN_GPIO_DATA1 0x000000002 +#define DC_PN_GPIO_DATA2 0x000000004 +#define DC_PN_GPIO_DATA3 0x000000008 +#define DC_PN_GPIO_CTL0 0x000000010 +#define DC_PN_GPIO_CTL1 0x000000020 +#define DC_PN_GPIO_CTL2 0x000000040 +#define DC_PN_GPIO_CTL3 0x000000080 +#define DC_PN_GPIO_SPEEDSEL DC_PN_GPIO_DATA0/* 1 == 100Mbps, 0 == 10Mbps */ +#define DC_PN_GPIO_100TX_LOOP DC_PN_GPIO_DATA1/* 1 == normal, 0 == loop */ +#define DC_PN_GPIO_BNC_ENB DC_PN_GPIO_DATA2 +#define DC_PN_GPIO_100TX_LNK DC_PN_GPIO_DATA3 +#define DC_PN_GPIO_SETBIT(sc, r) \ + DC_SETBIT(sc, DC_PN_GPIO, ((r) | (r << 4))) +#define DC_PN_GPIO_CLRBIT(sc, r) \ + { \ + DC_SETBIT(sc, DC_PN_GPIO, ((r) << 4)); \ + DC_CLRBIT(sc, DC_PN_GPIO, (r)); \ + } + +/* shortcut MII access register */ +#define DC_PN_MII_DATA 0x0000FFFF +#define DC_PN_MII_RESERVER 0x00020000 +#define DC_PN_MII_REGADDR 0x007C0000 +#define DC_PN_MII_PHYADDR 0x0F800000 +#define DC_PN_MII_OPCODE 0x30000000 +#define DC_PN_MII_BUSY 0x80000000 + +#define DC_PN_MIIOPCODE_READ 0x60020000 +#define DC_PN_MIIOPCODE_WRITE 0x50020000 + +/* Internal NWAY bits */ +#define DC_PN_NWAY_RESET 0x00000001 /* reset */ +#define DC_PN_NWAY_PDOWN 0x00000002 /* power down */ +#define DC_PN_NWAY_BYPASS 0x00000004 /* bypass */ +#define DC_PN_NWAY_AUILOWCUR 0x00000008 /* AUI low current */ +#define DC_PN_NWAY_TPEXTEND 0x00000010 /* low squelch voltage */ +#define DC_PN_NWAY_POLARITY 0x00000020 /* 0 == on, 1 == off */ +#define DC_PN_NWAY_TP 0x00000040 /* 1 == tp, 0 == AUI */ +#define DC_PN_NWAY_AUIVOLT 0x00000080 /* 1 == full, 0 == half */ +#define DC_PN_NWAY_DUPLEX 0x00000100 /* LED, 1 == full, 0 == half */ +#define DC_PN_NWAY_LINKTEST 0x00000200 /* 0 == on, 1 == off */ +#define DC_PN_NWAY_AUTODETECT 0x00000400 /* 1 == off, 0 == on */ +#define DC_PN_NWAY_SPEEDSEL 0x00000800 /* LED, 0 = 10, 1 == 100 */ +#define DC_PN_NWAY_NWAY_ENB 0x00001000 /* 0 == off, 1 == on */ +#define DC_PN_NWAY_CAP10HDX 0x00002000 +#define DC_PN_NWAY_CAP10FDX 0x00004000 +#define DC_PN_NWAY_CAP100FDX 0x00008000 +#define DC_PN_NWAY_CAP100HDX 0x00010000 +#define DC_PN_NWAY_CAP100T4 0x00020000 +#define DC_PN_NWAY_ANEGRESTART 0x02000000 /* resets when aneg done */ +#define DC_PN_NWAY_REMFAULT 0x04000000 +#define DC_PN_NWAY_LPAR10HDX 0x08000000 +#define DC_PN_NWAY_LPAR10FDX 0x10000000 +#define DC_PN_NWAY_LPAR100FDX 0x20000000 +#define DC_PN_NWAY_LPAR100HDX 0x40000000 +#define DC_PN_NWAY_LPAR100T4 0x80000000 + +/* End of PNIC specific registers */ + +/* + * CONEXANT specific registers. + */ + +#define DC_CONEXANT_PHYADDR 0x1 +#define DC_CONEXANT_EE_NODEADDR 0x19A + +/* End of CONEXANT specific registers */ + + +struct dc_softc { + struct arpcom arpcom; /* interface info */ + bus_space_handle_t dc_bhandle; /* bus space handle */ + bus_space_tag_t dc_btag; /* bus space tag */ + void *dc_intrhand; + struct resource *dc_irq; + struct resource *dc_res; + struct dc_type *dc_info; /* adapter info */ + device_t dc_miibus; + u_int8_t dc_unit; /* interface number */ + u_int8_t dc_type; + u_int8_t dc_pmode; + u_int8_t dc_link; + u_int8_t dc_cachesize; + int dc_pnic_rx_bug_save; + unsigned char *dc_pnic_rx_buf; + int dc_if_flags; + int dc_if_media; + u_int32_t dc_flags; + u_int32_t dc_txthresh; + u_int8_t dc_srom[1024]; + struct dc_mediainfo *dc_mi; + struct dc_list_data *dc_ldata; + struct dc_chain_data dc_cdata; + struct callout dc_stat_ch; +#ifdef SRM_MEDIA + int dc_srm_media; +#endif + struct mtx dc_mtx; +#ifdef DEVICE_POLLING + int rxcycles; /* ... when polling */ +#endif + int suspended; /* 0 = normal 1 = suspended */ + + u_int32_t saved_maps[5]; /* pci data */ + u_int32_t saved_biosaddr; + u_int8_t saved_intline; + u_int8_t saved_cachelnsz; + u_int8_t saved_lattimer; +}; + + +#define DC_LOCK(_sc) mtx_lock(&(_sc)->dc_mtx) +#define DC_UNLOCK(_sc) mtx_unlock(&(_sc)->dc_mtx) + +#define DC_TX_POLL 0x00000001 +#define DC_TX_COALESCE 0x00000002 +#define DC_TX_ADMTEK_WAR 0x00000004 +#define DC_TX_USE_TX_INTR 0x00000008 +#define DC_RX_FILTER_TULIP 0x00000010 +#define DC_TX_INTR_FIRSTFRAG 0x00000020 +#define DC_PNIC_RX_BUG_WAR 0x00000040 +#define DC_TX_FIXED_RING 0x00000080 +#define DC_TX_STORENFWD 0x00000100 +#define DC_REDUCED_MII_POLL 0x00000200 +#define DC_TX_INTR_ALWAYS 0x00000400 +#define DC_21143_NWAY 0x00000800 +#define DC_128BIT_HASH 0x00001000 +#define DC_64BIT_HASH 0x00002000 +#define DC_TULIP_LEDS 0x00004000 +#define DC_TX_ONE 0x00008000 +#define DC_TX_ALIGN 0x00010000 /* align mbuf on tx */ + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->dc_btag, sc->dc_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->dc_btag, sc->dc_bhandle, reg) + +#define DC_TIMEOUT 1000 +#define ETHER_ALIGN 2 + +/* + * General constants that are fun to know. + */ + +/* + * DEC PCI vendor ID + */ +#define DC_VENDORID_DEC 0x1011 + +/* + * DEC/Intel 21143 PCI device ID + */ +#define DC_DEVICEID_21143 0x0019 + +/* + * Macronix PCI vendor ID + */ +#define DC_VENDORID_MX 0x10D9 + +/* + * Macronix PMAC device IDs. + */ +#define DC_DEVICEID_98713 0x0512 +#define DC_DEVICEID_987x5 0x0531 +#define DC_DEVICEID_98727 0x0532 +#define DC_DEVICEID_98732 0x0532 + +/* Macronix PCI revision codes. */ +#define DC_REVISION_98713 0x00 +#define DC_REVISION_98713A 0x10 +#define DC_REVISION_98715 0x20 +#define DC_REVISION_98715AEC_C 0x25 +#define DC_REVISION_98725 0x30 + +/* + * Compex PCI vendor ID. + */ +#define DC_VENDORID_CP 0x11F6 + +/* + * Compex PMAC PCI device IDs. + */ +#define DC_DEVICEID_98713_CP 0x9881 + +/* + * Lite-On PNIC PCI vendor ID + */ +#define DC_VENDORID_LO 0x11AD + +/* + * 82c168/82c169 PNIC device IDs. Both chips have the same device + * ID but different revisions. Revision 0x10 is the 82c168, and + * 0x20 is the 82c169. + */ +#define DC_DEVICEID_82C168 0x0002 + +#define DC_REVISION_82C168 0x10 +#define DC_REVISION_82C169 0x20 + +/* + * Lite-On PNIC II device ID. Note: this is actually a Macronix 98715A + * with wake on lan/magic packet support. + */ +#define DC_DEVICEID_82C115 0xc115 + +/* + * Davicom vendor ID. + */ +#define DC_VENDORID_DAVICOM 0x1282 + +/* + * Davicom device IDs. + */ +#define DC_DEVICEID_DM9100 0x9100 +#define DC_DEVICEID_DM9102 0x9102 + +/* + * The DM9102A has the same PCI device ID as the DM9102, + * but a higher revision code. + */ +#define DC_REVISION_DM9102 0x10 +#define DC_REVISION_DM9102A 0x30 + +/* + * ADMtek vendor ID. + */ +#define DC_VENDORID_ADMTEK 0x1317 + +/* + * ADMtek device IDs. + */ +#define DC_DEVICEID_AL981 0x0981 +#define DC_DEVICEID_AN985 0x0985 + +/* + * ASIX vendor ID. + */ +#define DC_VENDORID_ASIX 0x125B + +/* + * ASIX device IDs. + */ +#define DC_DEVICEID_AX88140A 0x1400 + +/* + * The ASIX AX88140 and ASIX AX88141 have the same vendor and + * device IDs but different revision values. + */ +#define DC_REVISION_88140 0x00 +#define DC_REVISION_88141 0x10 + +/* + * Accton vendor ID. + */ +#define DC_VENDORID_ACCTON 0x1113 + +/* + * Accton device IDs. + */ +#define DC_DEVICEID_EN1217 0x1217 +#define DC_DEVICEID_EN2242 0x1216 + +/* + * Xircom vendor ID + */ +#define DC_VENDORID_XIRCOM 0x115d + +/* + * Xircom device IDs. + */ +#define DC_DEVICEID_X3201 0x0003 + +/* + * Abocom vendor ID + */ +#define DC_VENDORID_ABOCOM 0x13d1 + +/* + * Abocom device IDs. + */ +#define DC_DEVICEID_FE2500 0xAB02 + +/* + * Conexant vendor ID. + */ +#define DC_VENDORID_CONEXANT 0x14f1 + +/* + * Conexant device IDs. + */ +#define DC_DEVICEID_RS7112 0x1803 + +/* + * PCI low memory base and low I/O base register, and + * other PCI registers. + */ + +#define DC_PCI_CFID 0x00 /* Id */ +#define DC_PCI_CFCS 0x04 /* Command and status */ +#define DC_PCI_CFRV 0x08 /* Revision */ +#define DC_PCI_CFLT 0x0C /* Latency timer */ +#define DC_PCI_CFBIO 0x10 /* Base I/O address */ +#define DC_PCI_CFBMA 0x14 /* Base memory address */ +#define DC_PCI_CCIS 0x28 /* Card info struct */ +#define DC_PCI_CSID 0x2C /* Subsystem ID */ +#define DC_PCI_CBER 0x30 /* Expansion ROM base address */ +#define DC_PCI_CCAP 0x34 /* Caps pointer - PD/TD chip only */ +#define DC_PCI_CFIT 0x3C /* Interrupt */ +#define DC_PCI_CFDD 0x40 /* Device and driver area */ +#define DC_PCI_CWUA0 0x44 /* Wake-Up LAN addr 0 */ +#define DC_PCI_CWUA1 0x48 /* Wake-Up LAN addr 1 */ +#define DC_PCI_SOP0 0x4C /* SecureON passwd 0 */ +#define DC_PCI_SOP1 0x50 /* SecureON passwd 1 */ +#define DC_PCI_CWUC 0x54 /* Configuration Wake-Up cmd */ +#define DC_PCI_CCID 0xDC /* Capability ID - PD/TD only */ +#define DC_PCI_CPMC 0xE0 /* Pwrmgmt ctl & sts - PD/TD only */ + +/* PCI ID register */ +#define DC_CFID_VENDOR 0x0000FFFF +#define DC_CFID_DEVICE 0xFFFF0000 + +/* PCI command/status register */ +#define DC_CFCS_IOSPACE 0x00000001 /* I/O space enable */ +#define DC_CFCS_MEMSPACE 0x00000002 /* memory space enable */ +#define DC_CFCS_BUSMASTER 0x00000004 /* bus master enable */ +#define DC_CFCS_MWI_ENB 0x00000010 /* mem write and inval enable */ +#define DC_CFCS_PARITYERR_ENB 0x00000040 /* parity error enable */ +#define DC_CFCS_SYSERR_ENB 0x00000100 /* system error enable */ +#define DC_CFCS_NEWCAPS 0x00100000 /* new capabilities */ +#define DC_CFCS_FAST_B2B 0x00800000 /* fast back-to-back capable */ +#define DC_CFCS_DATAPARITY 0x01000000 /* Parity error report */ +#define DC_CFCS_DEVSELTIM 0x06000000 /* devsel timing */ +#define DC_CFCS_TGTABRT 0x10000000 /* received target abort */ +#define DC_CFCS_MASTERABRT 0x20000000 /* received master abort */ +#define DC_CFCS_SYSERR 0x40000000 /* asserted system error */ +#define DC_CFCS_PARITYERR 0x80000000 /* asserted parity error */ + +/* PCI revision register */ +#define DC_CFRV_STEPPING 0x0000000F +#define DC_CFRV_REVISION 0x000000F0 +#define DC_CFRV_SUBCLASS 0x00FF0000 +#define DC_CFRV_BASECLASS 0xFF000000 + +#define DC_21143_PB_REV 0x00000030 +#define DC_21143_TB_REV 0x00000030 +#define DC_21143_PC_REV 0x00000030 +#define DC_21143_TC_REV 0x00000030 +#define DC_21143_PD_REV 0x00000041 +#define DC_21143_TD_REV 0x00000041 + +/* PCI latency timer register */ +#define DC_CFLT_CACHELINESIZE 0x000000FF +#define DC_CFLT_LATENCYTIMER 0x0000FF00 + +/* PCI subsystem ID register */ +#define DC_CSID_VENDOR 0x0000FFFF +#define DC_CSID_DEVICE 0xFFFF0000 + +/* PCI cababilities pointer */ +#define DC_CCAP_OFFSET 0x000000FF + +/* PCI interrupt config register */ +#define DC_CFIT_INTLINE 0x000000FF +#define DC_CFIT_INTPIN 0x0000FF00 +#define DC_CFIT_MIN_GNT 0x00FF0000 +#define DC_CFIT_MAX_LAT 0xFF000000 + +/* PCI capability register */ +#define DC_CCID_CAPID 0x000000FF +#define DC_CCID_NEXTPTR 0x0000FF00 +#define DC_CCID_PM_VERS 0x00070000 +#define DC_CCID_PME_CLK 0x00080000 +#define DC_CCID_DVSPEC_INT 0x00200000 +#define DC_CCID_STATE_D1 0x02000000 +#define DC_CCID_STATE_D2 0x04000000 +#define DC_CCID_PME_D0 0x08000000 +#define DC_CCID_PME_D1 0x10000000 +#define DC_CCID_PME_D2 0x20000000 +#define DC_CCID_PME_D3HOT 0x40000000 +#define DC_CCID_PME_D3COLD 0x80000000 + +/* PCI power management control/status register */ +#define DC_CPMC_STATE 0x00000003 +#define DC_CPMC_PME_ENB 0x00000100 +#define DC_CPMC_PME_STS 0x00008000 + +#define DC_PSTATE_D0 0x0 +#define DC_PSTATE_D1 0x1 +#define DC_PSTATE_D2 0x2 +#define DC_PSTATE_D3 0x3 + +/* Device specific region */ +/* Configuration and driver area */ +#define DC_CFDD_DRVUSE 0x0000FFFF +#define DC_CFDD_SNOOZE_MODE 0x40000000 +#define DC_CFDD_SLEEP_MODE 0x80000000 + +/* Configuration wake-up command register */ +#define DC_CWUC_MUST_BE_ZERO 0x00000001 +#define DC_CWUC_SECUREON_ENB 0x00000002 +#define DC_CWUC_FORCE_WUL 0x00000004 +#define DC_CWUC_BNC_ABILITY 0x00000008 +#define DC_CWUC_AUI_ABILITY 0x00000010 +#define DC_CWUC_TP10_ABILITY 0x00000020 +#define DC_CWUC_MII_ABILITY 0x00000040 +#define DC_CWUC_SYM_ABILITY 0x00000080 +#define DC_CWUC_LOCK 0x00000100 + +/* + * SROM nonsense. + */ + +#define DC_IB_CTLRCNT 0x13 +#define DC_IB_LEAF0_CNUM 0x1A +#define DC_IB_LEAF0_OFFSET 0x1B + +struct dc_info_leaf { + u_int16_t dc_conntype; + u_int8_t dc_blkcnt; + u_int8_t dc_rsvd; + u_int16_t dc_infoblk; +}; + +#define DC_CTYPE_10BT 0x0000 +#define DC_CTYPE_10BT_NWAY 0x0100 +#define DC_CTYPE_10BT_FDX 0x0204 +#define DC_CTYPE_10B2 0x0001 +#define DC_CTYPE_10B5 0x0002 +#define DC_CTYPE_100BT 0x0003 +#define DC_CTYPE_100BT_FDX 0x0205 +#define DC_CTYPE_100T4 0x0006 +#define DC_CTYPE_100FX 0x0007 +#define DC_CTYPE_100FX_FDX 0x0208 +#define DC_CTYPE_MII_10BT 0x0009 +#define DC_CTYPE_MII_10BT_FDX 0x020A +#define DC_CTYPE_MII_100BT 0x000D +#define DC_CTYPE_MII_100BT_FDX 0x020E +#define DC_CTYPE_MII_100T4 0x000F +#define DC_CTYPE_MII_100FX 0x0010 +#define DC_CTYPE_MII_100FX_FDX 0x0211 +#define DC_CTYPE_DYN_PUP_AUTOSENSE 0x0800 +#define DC_CTYPE_PUP_AUTOSENSe 0x8800 +#define DC_CTYPE_NOMEDIA 0xFFFF + +#define DC_EBLOCK_SIA 0x0002 +#define DC_EBLOCK_MII 0x0003 +#define DC_EBLOCK_SYM 0x0004 +#define DC_EBLOCK_RESET 0x0005 +#define DC_EBLOCK_PHY_SHUTDOWN 0x0006 + +struct dc_leaf_hdr { + u_int16_t dc_mtype; + u_int8_t dc_mcnt; + u_int8_t dc_rsvd; +}; + +struct dc_eblock_hdr { + u_int8_t dc_len; + u_int8_t dc_type; +}; + +struct dc_eblock_sia { + struct dc_eblock_hdr dc_sia_hdr; + u_int8_t dc_sia_code; + u_int8_t dc_sia_mediaspec[6]; /* CSR13, CSR14, CSR15 */ + u_int8_t dc_sia_gpio_ctl[2]; + u_int8_t dc_sia_gpio_dat[2]; +}; + +#define DC_SIA_CODE_10BT 0x00 +#define DC_SIA_CODE_10B2 0x01 +#define DC_SIA_CODE_10B5 0x02 +#define DC_SIA_CODE_10BT_FDX 0x04 +#define DC_SIA_CODE_EXT 0x40 + +/* + * Note that the first word in the gpr and reset + * sequences is always a control word. + */ +struct dc_eblock_mii { + struct dc_eblock_hdr dc_mii_hdr; + u_int8_t dc_mii_phynum; + u_int8_t dc_gpr_len; +/* u_int16_t dc_gpr_dat[n]; */ +/* u_int8_t dc_reset_len; */ +/* u_int16_t dc_reset_dat[n]; */ +/* There are other fields after these, but we don't + * care about them since they can be determined by looking + * at the PHY. + */ +}; + +struct dc_eblock_sym { + struct dc_eblock_hdr dc_sym_hdr; + u_int8_t dc_sym_code; + u_int8_t dc_sym_gpio_ctl[2]; + u_int8_t dc_sym_gpio_dat[2]; + u_int8_t dc_sym_cmd[2]; +}; + +#define DC_SYM_CODE_100BT 0x03 +#define DC_SYM_CODE_100BT_FDX 0x05 +#define DC_SYM_CODE_100T4 0x06 +#define DC_SYM_CODE_100FX 0x07 +#define DC_SYM_CODE_100FX_FDX 0x08 + +struct dc_eblock_reset { + struct dc_eblock_hdr dc_reset_hdr; + u_int8_t dc_reset_len; +/* u_int16_t dc_reset_dat[n]; */ +}; + +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) +#endif diff --git a/sys/pci/if_de.c b/sys/pci/if_de.c new file mode 100644 index 0000000..a48d93c --- /dev/null +++ b/sys/pci/if_de.c @@ -0,0 +1,5271 @@ +/* $NetBSD: if_de.c,v 1.86 1999/06/01 19:17:59 thorpej Exp $ */ + +/* $FreeBSD$ */ + +/*- + * Copyright (c) 1994-1997 Matt Thomas (matt@3am-software.com) + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Id: if_de.c,v 1.94 1997/07/03 16:55:07 thomas Exp + * + */ + +/* + * DEC 21040 PCI Ethernet Controller + * + * Written by Matt Thomas + * BPF support code stolen directly from if_ec.c + * + * This driver supports the DEC DE435 or any other PCI + * board which support 21040, 21041, or 21140 (mostly). + */ +#define TULIP_HDR_DATA + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/endian.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/eventhandler.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_media.h> +#include <net/if_dl.h> +#ifdef TULIP_USE_SOFTINTR +#include <net/netisr.h> +#endif + +#include <net/bpf.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/if_ether.h> +#endif + +#include <vm/vm.h> + +#include <net/if_var.h> +#include <vm/pmap.h> +#include <pci/pcivar.h> +#include <pci/pcireg.h> +#include <pci/dc21040reg.h> + +/* + * Intel CPUs should use I/O mapped access. + */ +#if defined(__i386__) +#define TULIP_IOMAPPED +#endif + +#if 0 +/* + * This turns on all sort of debugging stuff and make the + * driver much larger. + */ +#define TULIP_DEBUG +#endif + +#if 0 +#define TULIP_PERFSTATS +#endif + +#if 0 +#define TULIP_USE_SOFTINTR +#endif + +#define TULIP_HZ 10 + +#include <pci/if_devar.h> + +/* + * This module supports + * the DEC 21040 PCI Ethernet Controller. + * the DEC 21041 PCI Ethernet Controller. + * the DEC 21140 PCI Fast Ethernet Controller. + */ +static void tulip_mii_autonegotiate(tulip_softc_t * const sc, const unsigned phyaddr); +static void tulip_intr_shared(void *arg); +static void tulip_intr_normal(void *arg); +static void tulip_init(tulip_softc_t * const sc); +static void tulip_ifinit(void *); +static void tulip_reset(tulip_softc_t * const sc); +static void tulip_ifstart_one(struct ifnet *ifp); +static void tulip_ifstart(struct ifnet *ifp); +static struct mbuf *tulip_txput(tulip_softc_t * const sc, struct mbuf *m); +static void tulip_txput_setup(tulip_softc_t * const sc); +static void tulip_rx_intr(tulip_softc_t * const sc); +static void tulip_addr_filter(tulip_softc_t * const sc); +static unsigned tulip_mii_readreg(tulip_softc_t * const sc, unsigned devaddr, unsigned regno); +static void tulip_mii_writereg(tulip_softc_t * const sc, unsigned devaddr, unsigned regno, unsigned data); +static int tulip_mii_map_abilities(tulip_softc_t * const sc, unsigned abilities); +static tulip_media_t tulip_mii_phy_readspecific(tulip_softc_t * const sc); +static int tulip_srom_decode(tulip_softc_t * const sc); +static int tulip_ifmedia_change(struct ifnet * const ifp); +static void tulip_ifmedia_status(struct ifnet * const ifp, struct ifmediareq *req); +/* static void tulip_21140_map_media(tulip_softc_t *sc); */ + +static void +tulip_timeout_callback( + void *arg) +{ + tulip_softc_t * const sc = arg; + int s = splimp(); + + TULIP_PERFSTART(timeout) + + sc->tulip_flags &= ~TULIP_TIMEOUTPENDING; + sc->tulip_probe_timeout -= 1000 / TULIP_HZ; + (sc->tulip_boardsw->bd_media_poll)(sc, TULIP_MEDIAPOLL_TIMER); + + TULIP_PERFEND(timeout); + splx(s); +} + +static void +tulip_timeout( + tulip_softc_t * const sc) +{ + if (sc->tulip_flags & TULIP_TIMEOUTPENDING) + return; + sc->tulip_flags |= TULIP_TIMEOUTPENDING; + timeout(tulip_timeout_callback, sc, (hz + TULIP_HZ / 2) / TULIP_HZ); +} + +#if defined(TULIP_NEED_FASTTIMEOUT) +static void +tulip_fasttimeout_callback( + void *arg) +{ + tulip_softc_t * const sc = arg; + int s = splimp(); + + sc->tulip_flags &= ~TULIP_FASTTIMEOUTPENDING; + (sc->tulip_boardsw->bd_media_poll)(sc, TULIP_MEDIAPOLL_FASTTIMER); + splx(s); +} + +static void +tulip_fasttimeout( + tulip_softc_t * const sc) +{ + if (sc->tulip_flags & TULIP_FASTTIMEOUTPENDING) + return; + sc->tulip_flags |= TULIP_FASTTIMEOUTPENDING; + timeout(tulip_fasttimeout_callback, sc, 1); +} +#endif + +static int +tulip_txprobe( + tulip_softc_t * const sc) +{ + struct mbuf *m; + /* + * Before we are sure this is the right media we need + * to send a small packet to make sure there's carrier. + * Strangely, BNC and AUI will "see" receive data if + * either is connected so the transmit is the only way + * to verify the connectivity. + */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return 0; + /* + * Construct a LLC TEST message which will point to ourselves. + */ + bcopy(sc->tulip_enaddr, mtod(m, struct ether_header *)->ether_dhost, 6); + bcopy(sc->tulip_enaddr, mtod(m, struct ether_header *)->ether_shost, 6); + mtod(m, struct ether_header *)->ether_type = htons(3); + mtod(m, unsigned char *)[14] = 0; + mtod(m, unsigned char *)[15] = 0; + mtod(m, unsigned char *)[16] = 0xE3; /* LLC Class1 TEST (no poll) */ + m->m_len = m->m_pkthdr.len = sizeof(struct ether_header) + 3; + /* + * send it! + */ + sc->tulip_cmdmode |= TULIP_CMD_TXRUN; + sc->tulip_intrmask |= TULIP_STS_TXINTR; + sc->tulip_flags |= TULIP_TXPROBE_ACTIVE; + TULIP_CSR_WRITE(sc, csr_command, sc->tulip_cmdmode); + TULIP_CSR_WRITE(sc, csr_intr, sc->tulip_intrmask); + if ((m = tulip_txput(sc, m)) != NULL) + m_freem(m); + sc->tulip_probe.probe_txprobes++; + return 1; +} + +#ifdef BIG_PACKET +#define TULIP_SIAGEN_WATCHDOG (sc->tulip_if.if_mtu > ETHERMTU ? TULIP_WATCHDOG_RXDISABLE|TULIP_WATCHDOG_TXDISABLE : 0) +#else +#define TULIP_SIAGEN_WATCHDOG 0 +#endif + +static void +tulip_media_set( + tulip_softc_t * const sc, + tulip_media_t media) +{ + const tulip_media_info_t *mi = sc->tulip_mediums[media]; + + if (mi == NULL) + return; + + /* + * If we are switching media, make sure we don't think there's + * any stale RX activity + */ + sc->tulip_flags &= ~TULIP_RXACT; + if (mi->mi_type == TULIP_MEDIAINFO_SIA) { + TULIP_CSR_WRITE(sc, csr_sia_connectivity, TULIP_SIACONN_RESET); + TULIP_CSR_WRITE(sc, csr_sia_tx_rx, mi->mi_sia_tx_rx); + if (sc->tulip_features & TULIP_HAVE_SIAGP) { + TULIP_CSR_WRITE(sc, csr_sia_general, mi->mi_sia_gp_control|mi->mi_sia_general|TULIP_SIAGEN_WATCHDOG); + DELAY(50); + TULIP_CSR_WRITE(sc, csr_sia_general, mi->mi_sia_gp_data|mi->mi_sia_general|TULIP_SIAGEN_WATCHDOG); + } else { + TULIP_CSR_WRITE(sc, csr_sia_general, mi->mi_sia_general|TULIP_SIAGEN_WATCHDOG); + } + TULIP_CSR_WRITE(sc, csr_sia_connectivity, mi->mi_sia_connectivity); + } else if (mi->mi_type == TULIP_MEDIAINFO_GPR) { +#define TULIP_GPR_CMDBITS (TULIP_CMD_PORTSELECT|TULIP_CMD_PCSFUNCTION|TULIP_CMD_SCRAMBLER|TULIP_CMD_TXTHRSHLDCTL) + /* + * If the cmdmode bits don't match the currently operating mode, + * set the cmdmode appropriately and reset the chip. + */ + if (((mi->mi_cmdmode ^ TULIP_CSR_READ(sc, csr_command)) & TULIP_GPR_CMDBITS) != 0) { + sc->tulip_cmdmode &= ~TULIP_GPR_CMDBITS; + sc->tulip_cmdmode |= mi->mi_cmdmode; + tulip_reset(sc); + } + TULIP_CSR_WRITE(sc, csr_gp, TULIP_GP_PINSET|sc->tulip_gpinit); + DELAY(10); + TULIP_CSR_WRITE(sc, csr_gp, (u_int8_t) mi->mi_gpdata); + } else if (mi->mi_type == TULIP_MEDIAINFO_SYM) { + /* + * If the cmdmode bits don't match the currently operating mode, + * set the cmdmode appropriately and reset the chip. + */ + if (((mi->mi_cmdmode ^ TULIP_CSR_READ(sc, csr_command)) & TULIP_GPR_CMDBITS) != 0) { + sc->tulip_cmdmode &= ~TULIP_GPR_CMDBITS; + sc->tulip_cmdmode |= mi->mi_cmdmode; + tulip_reset(sc); + } + TULIP_CSR_WRITE(sc, csr_sia_general, mi->mi_gpcontrol); + TULIP_CSR_WRITE(sc, csr_sia_general, mi->mi_gpdata); + } else if (mi->mi_type == TULIP_MEDIAINFO_MII + && sc->tulip_probe_state != TULIP_PROBE_INACTIVE) { + int idx; + if (sc->tulip_features & TULIP_HAVE_SIAGP) { + const u_int8_t *dp; + dp = &sc->tulip_rombuf[mi->mi_reset_offset]; + for (idx = 0; idx < mi->mi_reset_length; idx++, dp += 2) { + DELAY(10); + TULIP_CSR_WRITE(sc, csr_sia_general, (dp[0] + 256 * dp[1]) << 16); + } + sc->tulip_phyaddr = mi->mi_phyaddr; + dp = &sc->tulip_rombuf[mi->mi_gpr_offset]; + for (idx = 0; idx < mi->mi_gpr_length; idx++, dp += 2) { + DELAY(10); + TULIP_CSR_WRITE(sc, csr_sia_general, (dp[0] + 256 * dp[1]) << 16); + } + } else { + for (idx = 0; idx < mi->mi_reset_length; idx++) { + DELAY(10); + TULIP_CSR_WRITE(sc, csr_gp, sc->tulip_rombuf[mi->mi_reset_offset + idx]); + } + sc->tulip_phyaddr = mi->mi_phyaddr; + for (idx = 0; idx < mi->mi_gpr_length; idx++) { + DELAY(10); + TULIP_CSR_WRITE(sc, csr_gp, sc->tulip_rombuf[mi->mi_gpr_offset + idx]); + } + } + if (sc->tulip_flags & TULIP_TRYNWAY) { + tulip_mii_autonegotiate(sc, sc->tulip_phyaddr); + } else if ((sc->tulip_flags & TULIP_DIDNWAY) == 0) { + u_int32_t data = tulip_mii_readreg(sc, sc->tulip_phyaddr, PHYREG_CONTROL); + data &= ~(PHYCTL_SELECT_100MB|PHYCTL_FULL_DUPLEX|PHYCTL_AUTONEG_ENABLE); + sc->tulip_flags &= ~TULIP_DIDNWAY; + if (TULIP_IS_MEDIA_FD(media)) + data |= PHYCTL_FULL_DUPLEX; + if (TULIP_IS_MEDIA_100MB(media)) + data |= PHYCTL_SELECT_100MB; + tulip_mii_writereg(sc, sc->tulip_phyaddr, PHYREG_CONTROL, data); + } + } +} + +static void +tulip_linkup( + tulip_softc_t * const sc, + tulip_media_t media) +{ + if ((sc->tulip_flags & TULIP_LINKUP) == 0) + sc->tulip_flags |= TULIP_PRINTLINKUP; + sc->tulip_flags |= TULIP_LINKUP; + sc->tulip_if.if_flags &= ~IFF_OACTIVE; +#if 0 /* XXX how does with work with ifmedia? */ + if ((sc->tulip_flags & TULIP_DIDNWAY) == 0) { + if (sc->tulip_if.if_flags & IFF_FULLDUPLEX) { + if (TULIP_CAN_MEDIA_FD(media) + && sc->tulip_mediums[TULIP_FD_MEDIA_OF(media)] != NULL) + media = TULIP_FD_MEDIA_OF(media); + } else { + if (TULIP_IS_MEDIA_FD(media) + && sc->tulip_mediums[TULIP_HD_MEDIA_OF(media)] != NULL) + media = TULIP_HD_MEDIA_OF(media); + } + } +#endif + if (sc->tulip_media != media) { +#ifdef TULIP_DEBUG + sc->tulip_dbg.dbg_last_media = sc->tulip_media; +#endif + sc->tulip_media = media; + sc->tulip_flags |= TULIP_PRINTMEDIA; + if (TULIP_IS_MEDIA_FD(sc->tulip_media)) { + sc->tulip_cmdmode |= TULIP_CMD_FULLDUPLEX; + } else if (sc->tulip_chipid != TULIP_21041 || (sc->tulip_flags & TULIP_DIDNWAY) == 0) { + sc->tulip_cmdmode &= ~TULIP_CMD_FULLDUPLEX; + } + } + /* + * We could set probe_timeout to 0 but setting to 3000 puts this + * in one central place and the only matters is tulip_link is + * followed by a tulip_timeout. Therefore setting it should not + * result in aberrant behavour. + */ + sc->tulip_probe_timeout = 3000; + sc->tulip_probe_state = TULIP_PROBE_INACTIVE; + sc->tulip_flags &= ~(TULIP_TXPROBE_ACTIVE|TULIP_TRYNWAY); + if (sc->tulip_flags & TULIP_INRESET) { + tulip_media_set(sc, sc->tulip_media); + } else if (sc->tulip_probe_media != sc->tulip_media) { + /* + * No reason to change media if we have the right media. + */ + tulip_reset(sc); + } + tulip_init(sc); +} + +static void +tulip_media_print( + tulip_softc_t * const sc) +{ + if ((sc->tulip_flags & TULIP_LINKUP) == 0) + return; + if (sc->tulip_flags & TULIP_PRINTMEDIA) { + printf("%s%d: enabling %s port\n", + sc->tulip_name, sc->tulip_unit, + tulip_mediums[sc->tulip_media]); + sc->tulip_flags &= ~(TULIP_PRINTMEDIA|TULIP_PRINTLINKUP); + } else if (sc->tulip_flags & TULIP_PRINTLINKUP) { + printf("%s%d: link up\n", sc->tulip_name, sc->tulip_unit); + sc->tulip_flags &= ~TULIP_PRINTLINKUP; + } +} + +#if defined(TULIP_DO_GPR_SENSE) +static tulip_media_t +tulip_21140_gpr_media_sense( + tulip_softc_t * const sc) +{ + tulip_media_t maybe_media = TULIP_MEDIA_UNKNOWN; + tulip_media_t last_media = TULIP_MEDIA_UNKNOWN; + tulip_media_t media; + + /* + * If one of the media blocks contained a default media flag, + * use that. + */ + for (media = TULIP_MEDIA_UNKNOWN; media < TULIP_MEDIA_MAX; media++) { + const tulip_media_info_t *mi; + /* + * Media is not supported (or is full-duplex). + */ + if ((mi = sc->tulip_mediums[media]) == NULL || TULIP_IS_MEDIA_FD(media)) + continue; + if (mi->mi_type != TULIP_MEDIAINFO_GPR) + continue; + + /* + * Remember the media is this is the "default" media. + */ + if (mi->mi_default && maybe_media == TULIP_MEDIA_UNKNOWN) + maybe_media = media; + + /* + * No activity mask? Can't see if it is active if there's no mask. + */ + if (mi->mi_actmask == 0) + continue; + + /* + * Does the activity data match? + */ + if ((TULIP_CSR_READ(sc, csr_gp) & mi->mi_actmask) != mi->mi_actdata) + continue; + +#if defined(TULIP_DEBUG) + printf("%s%d: gpr_media_sense: %s: 0x%02x & 0x%02x == 0x%02x\n", + sc->tulip_name, sc->tulip_unit, tulip_mediums[media], + TULIP_CSR_READ(sc, csr_gp) & 0xFF, + mi->mi_actmask, mi->mi_actdata); +#endif + /* + * It does! If this is the first media we detected, then + * remember this media. If isn't the first, then there were + * multiple matches which we equate to no match (since we don't + * which to select (if any). + */ + if (last_media == TULIP_MEDIA_UNKNOWN) { + last_media = media; + } else if (last_media != media) { + last_media = TULIP_MEDIA_UNKNOWN; + } + } + return (last_media != TULIP_MEDIA_UNKNOWN) ? last_media : maybe_media; +} +#endif /* TULIP_DO_GPR_SENSE */ + +static tulip_link_status_t +tulip_media_link_monitor( + tulip_softc_t * const sc) +{ + const tulip_media_info_t * const mi = sc->tulip_mediums[sc->tulip_media]; + tulip_link_status_t linkup = TULIP_LINK_DOWN; + + if (mi == NULL) { +#if defined(DIAGNOSTIC) || defined(TULIP_DEBUG) + panic("tulip_media_link_monitor: %s: botch at line %d\n", + tulip_mediums[sc->tulip_media],__LINE__); +#endif + return TULIP_LINK_UNKNOWN; + } + + + /* + * Have we seen some packets? If so, the link must be good. + */ + if ((sc->tulip_flags & (TULIP_RXACT|TULIP_LINKUP)) == (TULIP_RXACT|TULIP_LINKUP)) { + sc->tulip_flags &= ~TULIP_RXACT; + sc->tulip_probe_timeout = 3000; + return TULIP_LINK_UP; + } + + sc->tulip_flags &= ~TULIP_RXACT; + if (mi->mi_type == TULIP_MEDIAINFO_MII) { + u_int32_t status; + /* + * Read the PHY status register. + */ + status = tulip_mii_readreg(sc, sc->tulip_phyaddr, PHYREG_STATUS); + if (status & PHYSTS_AUTONEG_DONE) { + /* + * If the PHY has completed autonegotiation, see the if the + * remote systems abilities have changed. If so, upgrade or + * downgrade as appropriate. + */ + u_int32_t abilities = tulip_mii_readreg(sc, sc->tulip_phyaddr, PHYREG_AUTONEG_ABILITIES); + abilities = (abilities << 6) & status; + if (abilities != sc->tulip_abilities) { +#if defined(TULIP_DEBUG) + loudprintf("%s%d(phy%d): autonegotiation changed: 0x%04x -> 0x%04x\n", + sc->tulip_name, sc->tulip_unit, sc->tulip_phyaddr, + sc->tulip_abilities, abilities); +#endif + if (tulip_mii_map_abilities(sc, abilities)) { + tulip_linkup(sc, sc->tulip_probe_media); + return TULIP_LINK_UP; + } + /* + * if we had selected media because of autonegotiation, + * we need to probe for the new media. + */ + sc->tulip_probe_state = TULIP_PROBE_INACTIVE; + if (sc->tulip_flags & TULIP_DIDNWAY) + return TULIP_LINK_DOWN; + } + } + /* + * The link is now up. If was down, say its back up. + */ + if ((status & (PHYSTS_LINK_UP|PHYSTS_REMOTE_FAULT)) == PHYSTS_LINK_UP) + linkup = TULIP_LINK_UP; + } else if (mi->mi_type == TULIP_MEDIAINFO_GPR) { + /* + * No activity sensor? Assume all's well. + */ + if (mi->mi_actmask == 0) + return TULIP_LINK_UNKNOWN; + /* + * Does the activity data match? + */ + if ((TULIP_CSR_READ(sc, csr_gp) & mi->mi_actmask) == mi->mi_actdata) + linkup = TULIP_LINK_UP; + } else if (mi->mi_type == TULIP_MEDIAINFO_SIA) { + /* + * Assume non TP ok for now. + */ + if (!TULIP_IS_MEDIA_TP(sc->tulip_media)) + return TULIP_LINK_UNKNOWN; + if ((TULIP_CSR_READ(sc, csr_sia_status) & TULIP_SIASTS_LINKFAIL) == 0) + linkup = TULIP_LINK_UP; +#if defined(TULIP_DEBUG) + if (sc->tulip_probe_timeout <= 0) + printf("%s%d: sia status = 0x%08x\n", sc->tulip_name, + sc->tulip_unit, TULIP_CSR_READ(sc, csr_sia_status)); +#endif + } else if (mi->mi_type == TULIP_MEDIAINFO_SYM) { + return TULIP_LINK_UNKNOWN; + } + /* + * We will wait for 3 seconds until the link goes into suspect mode. + */ + if (sc->tulip_flags & TULIP_LINKUP) { + if (linkup == TULIP_LINK_UP) + sc->tulip_probe_timeout = 3000; + if (sc->tulip_probe_timeout > 0) + return TULIP_LINK_UP; + + sc->tulip_flags &= ~TULIP_LINKUP; + printf("%s%d: link down: cable problem?\n", sc->tulip_name, sc->tulip_unit); + } +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_link_downed++; +#endif + return TULIP_LINK_DOWN; +} + +static void +tulip_media_poll( + tulip_softc_t * const sc, + tulip_mediapoll_event_t event) +{ +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_events[event]++; +#endif + if (sc->tulip_probe_state == TULIP_PROBE_INACTIVE + && event == TULIP_MEDIAPOLL_TIMER) { + switch (tulip_media_link_monitor(sc)) { + case TULIP_LINK_DOWN: { + /* + * Link Monitor failed. Probe for new media. + */ + event = TULIP_MEDIAPOLL_LINKFAIL; + break; + } + case TULIP_LINK_UP: { + /* + * Check again soon. + */ + tulip_timeout(sc); + return; + } + case TULIP_LINK_UNKNOWN: { + /* + * We can't tell so don't bother. + */ + return; + } + } + } + + if (event == TULIP_MEDIAPOLL_LINKFAIL) { + if (sc->tulip_probe_state == TULIP_PROBE_INACTIVE) { + if (TULIP_DO_AUTOSENSE(sc)) { +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_link_failures++; +#endif + sc->tulip_media = TULIP_MEDIA_UNKNOWN; + if (sc->tulip_if.if_flags & IFF_UP) + tulip_reset(sc); /* restart probe */ + } + return; + } +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_link_pollintrs++; +#endif + } + + if (event == TULIP_MEDIAPOLL_START) { + sc->tulip_if.if_flags |= IFF_OACTIVE; + if (sc->tulip_probe_state != TULIP_PROBE_INACTIVE) + return; + sc->tulip_probe_mediamask = 0; + sc->tulip_probe_passes = 0; +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_media_probes++; +#endif + /* + * If the SROM contained an explicit media to use, use it. + */ + sc->tulip_cmdmode &= ~(TULIP_CMD_RXRUN|TULIP_CMD_FULLDUPLEX); + sc->tulip_flags |= TULIP_TRYNWAY|TULIP_PROBE1STPASS; + sc->tulip_flags &= ~(TULIP_DIDNWAY|TULIP_PRINTMEDIA|TULIP_PRINTLINKUP); + /* + * connidx is defaulted to a media_unknown type. + */ + sc->tulip_probe_media = tulip_srom_conninfo[sc->tulip_connidx].sc_media; + if (sc->tulip_probe_media != TULIP_MEDIA_UNKNOWN) { + tulip_linkup(sc, sc->tulip_probe_media); + tulip_timeout(sc); + return; + } + + if (sc->tulip_features & TULIP_HAVE_GPR) { + sc->tulip_probe_state = TULIP_PROBE_GPRTEST; + sc->tulip_probe_timeout = 2000; + } else { + sc->tulip_probe_media = TULIP_MEDIA_MAX; + sc->tulip_probe_timeout = 0; + sc->tulip_probe_state = TULIP_PROBE_MEDIATEST; + } + } + + /* + * Ignore txprobe failures or spurious callbacks. + */ + if (event == TULIP_MEDIAPOLL_TXPROBE_FAILED + && sc->tulip_probe_state != TULIP_PROBE_MEDIATEST) { + sc->tulip_flags &= ~TULIP_TXPROBE_ACTIVE; + return; + } + + /* + * If we really transmitted a packet, then that's the media we'll use. + */ + if (event == TULIP_MEDIAPOLL_TXPROBE_OK || event == TULIP_MEDIAPOLL_LINKPASS) { + if (event == TULIP_MEDIAPOLL_LINKPASS) { + /* XXX Check media status just to be sure */ + sc->tulip_probe_media = TULIP_MEDIA_10BASET; +#if defined(TULIP_DEBUG) + } else { + sc->tulip_dbg.dbg_txprobes_ok[sc->tulip_probe_media]++; +#endif + } + tulip_linkup(sc, sc->tulip_probe_media); + tulip_timeout(sc); + return; + } + + if (sc->tulip_probe_state == TULIP_PROBE_GPRTEST) { +#if defined(TULIP_DO_GPR_SENSE) + /* + * Check for media via the general purpose register. + * + * Try to sense the media via the GPR. If the same value + * occurs 3 times in a row then just use that. + */ + if (sc->tulip_probe_timeout > 0) { + tulip_media_t new_probe_media = tulip_21140_gpr_media_sense(sc); +#if defined(TULIP_DEBUG) + printf("%s%d: media_poll: gpr sensing = %s\n", + sc->tulip_name, sc->tulip_unit, tulip_mediums[new_probe_media]); +#endif + if (new_probe_media != TULIP_MEDIA_UNKNOWN) { + if (new_probe_media == sc->tulip_probe_media) { + if (--sc->tulip_probe_count == 0) + tulip_linkup(sc, sc->tulip_probe_media); + } else { + sc->tulip_probe_count = 10; + } + } + sc->tulip_probe_media = new_probe_media; + tulip_timeout(sc); + return; + } +#endif /* TULIP_DO_GPR_SENSE */ + /* + * Brute force. We cycle through each of the media types + * and try to transmit a packet. + */ + sc->tulip_probe_state = TULIP_PROBE_MEDIATEST; + sc->tulip_probe_media = TULIP_MEDIA_MAX; + sc->tulip_probe_timeout = 0; + tulip_timeout(sc); + return; + } + + if (sc->tulip_probe_state != TULIP_PROBE_MEDIATEST + && (sc->tulip_features & TULIP_HAVE_MII)) { + tulip_media_t old_media = sc->tulip_probe_media; + tulip_mii_autonegotiate(sc, sc->tulip_phyaddr); + switch (sc->tulip_probe_state) { + case TULIP_PROBE_FAILED: + case TULIP_PROBE_MEDIATEST: { + /* + * Try the next media. + */ + sc->tulip_probe_mediamask |= sc->tulip_mediums[sc->tulip_probe_media]->mi_mediamask; + sc->tulip_probe_timeout = 0; +#ifdef notyet + if (sc->tulip_probe_state == TULIP_PROBE_FAILED) + break; + if (sc->tulip_probe_media != tulip_mii_phy_readspecific(sc)) + break; + sc->tulip_probe_timeout = TULIP_IS_MEDIA_TP(sc->tulip_probe_media) ? 2500 : 300; +#endif + break; + } + case TULIP_PROBE_PHYAUTONEG: { + return; + } + case TULIP_PROBE_INACTIVE: { + /* + * Only probe if we autonegotiated a media that hasn't failed. + */ + sc->tulip_probe_timeout = 0; + if (sc->tulip_probe_mediamask & TULIP_BIT(sc->tulip_probe_media)) { + sc->tulip_probe_media = old_media; + break; + } + tulip_linkup(sc, sc->tulip_probe_media); + tulip_timeout(sc); + return; + } + default: { +#if defined(DIAGNOSTIC) || defined(TULIP_DEBUG) + panic("tulip_media_poll: botch at line %d\n", __LINE__); +#endif + break; + } + } + } + + if (event == TULIP_MEDIAPOLL_TXPROBE_FAILED) { +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_txprobes_failed[sc->tulip_probe_media]++; +#endif + sc->tulip_flags &= ~TULIP_TXPROBE_ACTIVE; + return; + } + + /* + * switch to another media if we tried this one enough. + */ + if (/* event == TULIP_MEDIAPOLL_TXPROBE_FAILED || */ sc->tulip_probe_timeout <= 0) { +#if defined(TULIP_DEBUG) + if (sc->tulip_probe_media == TULIP_MEDIA_UNKNOWN) { + printf("%s%d: poll media unknown!\n", + sc->tulip_name, sc->tulip_unit); + sc->tulip_probe_media = TULIP_MEDIA_MAX; + } +#endif + /* + * Find the next media type to check for. Full Duplex + * types are not allowed. + */ + do { + sc->tulip_probe_media -= 1; + if (sc->tulip_probe_media == TULIP_MEDIA_UNKNOWN) { + if (++sc->tulip_probe_passes == 3) { + printf("%s%d: autosense failed: cable problem?\n", + sc->tulip_name, sc->tulip_unit); + if ((sc->tulip_if.if_flags & IFF_UP) == 0) { + sc->tulip_if.if_flags &= ~IFF_RUNNING; + sc->tulip_probe_state = TULIP_PROBE_INACTIVE; + return; + } + } + sc->tulip_flags ^= TULIP_TRYNWAY; /* XXX */ + sc->tulip_probe_mediamask = 0; + sc->tulip_probe_media = TULIP_MEDIA_MAX - 1; + } + } while (sc->tulip_mediums[sc->tulip_probe_media] == NULL + || (sc->tulip_probe_mediamask & TULIP_BIT(sc->tulip_probe_media)) + || TULIP_IS_MEDIA_FD(sc->tulip_probe_media)); + +#if defined(TULIP_DEBUG) + printf("%s%d: %s: probing %s\n", sc->tulip_name, sc->tulip_unit, + event == TULIP_MEDIAPOLL_TXPROBE_FAILED ? "txprobe failed" : "timeout", + tulip_mediums[sc->tulip_probe_media]); +#endif + sc->tulip_probe_timeout = TULIP_IS_MEDIA_TP(sc->tulip_probe_media) ? 2500 : 1000; + sc->tulip_probe_state = TULIP_PROBE_MEDIATEST; + sc->tulip_probe.probe_txprobes = 0; + tulip_reset(sc); + tulip_media_set(sc, sc->tulip_probe_media); + sc->tulip_flags &= ~TULIP_TXPROBE_ACTIVE; + } + tulip_timeout(sc); + + /* + * If this is hanging off a phy, we know are doing NWAY and we have + * forced the phy to a specific speed. Wait for link up before + * before sending a packet. + */ + switch (sc->tulip_mediums[sc->tulip_probe_media]->mi_type) { + case TULIP_MEDIAINFO_MII: { + if (sc->tulip_probe_media != tulip_mii_phy_readspecific(sc)) + return; + break; + } + case TULIP_MEDIAINFO_SIA: { + if (TULIP_IS_MEDIA_TP(sc->tulip_probe_media)) { + if (TULIP_CSR_READ(sc, csr_sia_status) & TULIP_SIASTS_LINKFAIL) + return; + tulip_linkup(sc, sc->tulip_probe_media); +#ifdef notyet + if (sc->tulip_features & TULIP_HAVE_MII) + tulip_timeout(sc); +#endif + return; + } + break; + } + case TULIP_MEDIAINFO_RESET: + case TULIP_MEDIAINFO_SYM: + case TULIP_MEDIAINFO_NONE: + case TULIP_MEDIAINFO_GPR: { + break; + } + } + /* + * Try to send a packet. + */ + tulip_txprobe(sc); +} + +static void +tulip_media_select( + tulip_softc_t * const sc) +{ + if (sc->tulip_features & TULIP_HAVE_GPR) { + TULIP_CSR_WRITE(sc, csr_gp, TULIP_GP_PINSET|sc->tulip_gpinit); + DELAY(10); + TULIP_CSR_WRITE(sc, csr_gp, sc->tulip_gpdata); + } + /* + * If this board has no media, just return + */ + if (sc->tulip_features & TULIP_HAVE_NOMEDIA) + return; + + if (sc->tulip_media == TULIP_MEDIA_UNKNOWN) { + TULIP_CSR_WRITE(sc, csr_intr, sc->tulip_intrmask); + (*sc->tulip_boardsw->bd_media_poll)(sc, TULIP_MEDIAPOLL_START); + } else { + tulip_media_set(sc, sc->tulip_media); + } +} + +static void +tulip_21040_mediainfo_init( + tulip_softc_t * const sc, + tulip_media_t media) +{ + sc->tulip_cmdmode |= TULIP_CMD_CAPTREFFCT|TULIP_CMD_THRSHLD160 + |TULIP_CMD_BACKOFFCTR; + sc->tulip_if.if_baudrate = 10000000; + + if (media == TULIP_MEDIA_10BASET || media == TULIP_MEDIA_UNKNOWN) { + TULIP_MEDIAINFO_SIA_INIT(sc, &sc->tulip_mediainfo[0], 21040, 10BASET); + TULIP_MEDIAINFO_SIA_INIT(sc, &sc->tulip_mediainfo[1], 21040, 10BASET_FD); + sc->tulip_intrmask |= TULIP_STS_LINKPASS|TULIP_STS_LINKFAIL; + } + + if (media == TULIP_MEDIA_AUIBNC || media == TULIP_MEDIA_UNKNOWN) { + TULIP_MEDIAINFO_SIA_INIT(sc, &sc->tulip_mediainfo[2], 21040, AUIBNC); + } + + if (media == TULIP_MEDIA_UNKNOWN) { + TULIP_MEDIAINFO_SIA_INIT(sc, &sc->tulip_mediainfo[3], 21040, EXTSIA); + } +} + +static void +tulip_21040_media_probe( + tulip_softc_t * const sc) +{ + tulip_21040_mediainfo_init(sc, TULIP_MEDIA_UNKNOWN); + return; +} + +static void +tulip_21040_10baset_only_media_probe( + tulip_softc_t * const sc) +{ + tulip_21040_mediainfo_init(sc, TULIP_MEDIA_10BASET); + tulip_media_set(sc, TULIP_MEDIA_10BASET); + sc->tulip_media = TULIP_MEDIA_10BASET; +} + +static void +tulip_21040_10baset_only_media_select( + tulip_softc_t * const sc) +{ + sc->tulip_flags |= TULIP_LINKUP; + if (sc->tulip_media == TULIP_MEDIA_10BASET_FD) { + sc->tulip_cmdmode |= TULIP_CMD_FULLDUPLEX; + sc->tulip_flags &= ~TULIP_SQETEST; + } else { + sc->tulip_cmdmode &= ~TULIP_CMD_FULLDUPLEX; + sc->tulip_flags |= TULIP_SQETEST; + } + tulip_media_set(sc, sc->tulip_media); +} + +static void +tulip_21040_auibnc_only_media_probe( + tulip_softc_t * const sc) +{ + tulip_21040_mediainfo_init(sc, TULIP_MEDIA_AUIBNC); + sc->tulip_flags |= TULIP_SQETEST|TULIP_LINKUP; + tulip_media_set(sc, TULIP_MEDIA_AUIBNC); + sc->tulip_media = TULIP_MEDIA_AUIBNC; +} + +static void +tulip_21040_auibnc_only_media_select( + tulip_softc_t * const sc) +{ + tulip_media_set(sc, TULIP_MEDIA_AUIBNC); + sc->tulip_cmdmode &= ~TULIP_CMD_FULLDUPLEX; +} + +static const tulip_boardsw_t tulip_21040_boardsw = { + TULIP_21040_GENERIC, + tulip_21040_media_probe, + tulip_media_select, + tulip_media_poll, +}; + +static const tulip_boardsw_t tulip_21040_10baset_only_boardsw = { + TULIP_21040_GENERIC, + tulip_21040_10baset_only_media_probe, + tulip_21040_10baset_only_media_select, + NULL, +}; + +static const tulip_boardsw_t tulip_21040_auibnc_only_boardsw = { + TULIP_21040_GENERIC, + tulip_21040_auibnc_only_media_probe, + tulip_21040_auibnc_only_media_select, + NULL, +}; + +static void +tulip_21041_mediainfo_init( + tulip_softc_t * const sc) +{ + tulip_media_info_t * const mi = sc->tulip_mediainfo; + +#ifdef notyet + if (sc->tulip_revinfo >= 0x20) { + TULIP_MEDIAINFO_SIA_INIT(sc, &mi[0], 21041P2, 10BASET); + TULIP_MEDIAINFO_SIA_INIT(sc, &mi[1], 21041P2, 10BASET_FD); + TULIP_MEDIAINFO_SIA_INIT(sc, &mi[0], 21041P2, AUI); + TULIP_MEDIAINFO_SIA_INIT(sc, &mi[1], 21041P2, BNC); + return; + } +#endif + TULIP_MEDIAINFO_SIA_INIT(sc, &mi[0], 21041, 10BASET); + TULIP_MEDIAINFO_SIA_INIT(sc, &mi[1], 21041, 10BASET_FD); + TULIP_MEDIAINFO_SIA_INIT(sc, &mi[2], 21041, AUI); + TULIP_MEDIAINFO_SIA_INIT(sc, &mi[3], 21041, BNC); +} + +static void +tulip_21041_media_probe( + tulip_softc_t * const sc) +{ + sc->tulip_if.if_baudrate = 10000000; + sc->tulip_cmdmode |= TULIP_CMD_CAPTREFFCT|TULIP_CMD_ENHCAPTEFFCT + |TULIP_CMD_THRSHLD160|TULIP_CMD_BACKOFFCTR; + sc->tulip_intrmask |= TULIP_STS_LINKPASS|TULIP_STS_LINKFAIL; + tulip_21041_mediainfo_init(sc); +} + +static void +tulip_21041_media_poll( + tulip_softc_t * const sc, + const tulip_mediapoll_event_t event) +{ + u_int32_t sia_status; + +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_events[event]++; +#endif + + if (event == TULIP_MEDIAPOLL_LINKFAIL) { + if (sc->tulip_probe_state != TULIP_PROBE_INACTIVE + || !TULIP_DO_AUTOSENSE(sc)) + return; + sc->tulip_media = TULIP_MEDIA_UNKNOWN; + tulip_reset(sc); /* start probe */ + return; + } + + /* + * If we've been been asked to start a poll or link change interrupt + * restart the probe (and reset the tulip to a known state). + */ + if (event == TULIP_MEDIAPOLL_START) { + sc->tulip_if.if_flags |= IFF_OACTIVE; + sc->tulip_cmdmode &= ~(TULIP_CMD_FULLDUPLEX|TULIP_CMD_RXRUN); +#ifdef notyet + if (sc->tulip_revinfo >= 0x20) { + sc->tulip_cmdmode |= TULIP_CMD_FULLDUPLEX; + sc->tulip_flags |= TULIP_DIDNWAY; + } +#endif + TULIP_CSR_WRITE(sc, csr_command, sc->tulip_cmdmode); + sc->tulip_probe_state = TULIP_PROBE_MEDIATEST; + sc->tulip_probe_media = TULIP_MEDIA_10BASET; + sc->tulip_probe_timeout = TULIP_21041_PROBE_10BASET_TIMEOUT; + tulip_media_set(sc, TULIP_MEDIA_10BASET); + tulip_timeout(sc); + return; + } + + if (sc->tulip_probe_state == TULIP_PROBE_INACTIVE) + return; + + if (event == TULIP_MEDIAPOLL_TXPROBE_OK) { +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_txprobes_ok[sc->tulip_probe_media]++; +#endif + tulip_linkup(sc, sc->tulip_probe_media); + return; + } + + sia_status = TULIP_CSR_READ(sc, csr_sia_status); + TULIP_CSR_WRITE(sc, csr_sia_status, sia_status); + if ((sia_status & TULIP_SIASTS_LINKFAIL) == 0) { + if (sc->tulip_revinfo >= 0x20) { + if (sia_status & (PHYSTS_10BASET_FD << (16 - 6))) + sc->tulip_probe_media = TULIP_MEDIA_10BASET_FD; + } + /* + * If the link has passed LinkPass, 10baseT is the + * proper media to use. + */ + tulip_linkup(sc, sc->tulip_probe_media); + return; + } + + /* + * wait for up to 2.4 seconds for the link to reach pass state. + * Only then start scanning the other media for activity. + * choose media with receive activity over those without. + */ + if (sc->tulip_probe_media == TULIP_MEDIA_10BASET) { + if (event != TULIP_MEDIAPOLL_TIMER) + return; + if (sc->tulip_probe_timeout > 0 + && (sia_status & TULIP_SIASTS_OTHERRXACTIVITY) == 0) { + tulip_timeout(sc); + return; + } + sc->tulip_probe_timeout = TULIP_21041_PROBE_AUIBNC_TIMEOUT; + sc->tulip_flags |= TULIP_WANTRXACT; + if (sia_status & TULIP_SIASTS_OTHERRXACTIVITY) { + sc->tulip_probe_media = TULIP_MEDIA_BNC; + } else { + sc->tulip_probe_media = TULIP_MEDIA_AUI; + } + tulip_media_set(sc, sc->tulip_probe_media); + tulip_timeout(sc); + return; + } + + /* + * If we failed, clear the txprobe active flag. + */ + if (event == TULIP_MEDIAPOLL_TXPROBE_FAILED) + sc->tulip_flags &= ~TULIP_TXPROBE_ACTIVE; + + + if (event == TULIP_MEDIAPOLL_TIMER) { + /* + * If we've received something, then that's our link! + */ + if (sc->tulip_flags & TULIP_RXACT) { + tulip_linkup(sc, sc->tulip_probe_media); + return; + } + /* + * if no txprobe active + */ + if ((sc->tulip_flags & TULIP_TXPROBE_ACTIVE) == 0 + && ((sc->tulip_flags & TULIP_WANTRXACT) == 0 + || (sia_status & TULIP_SIASTS_RXACTIVITY))) { + sc->tulip_probe_timeout = TULIP_21041_PROBE_AUIBNC_TIMEOUT; + tulip_txprobe(sc); + tulip_timeout(sc); + return; + } + /* + * Take 2 passes through before deciding to not + * wait for receive activity. Then take another + * two passes before spitting out a warning. + */ + if (sc->tulip_probe_timeout <= 0) { + if (sc->tulip_flags & TULIP_WANTRXACT) { + sc->tulip_flags &= ~TULIP_WANTRXACT; + sc->tulip_probe_timeout = TULIP_21041_PROBE_AUIBNC_TIMEOUT; + } else { + printf("%s%d: autosense failed: cable problem?\n", + sc->tulip_name, sc->tulip_unit); + if ((sc->tulip_if.if_flags & IFF_UP) == 0) { + sc->tulip_if.if_flags &= ~IFF_RUNNING; + sc->tulip_probe_state = TULIP_PROBE_INACTIVE; + return; + } + } + } + } + + /* + * Since this media failed to probe, try the other one. + */ + sc->tulip_probe_timeout = TULIP_21041_PROBE_AUIBNC_TIMEOUT; + if (sc->tulip_probe_media == TULIP_MEDIA_AUI) { + sc->tulip_probe_media = TULIP_MEDIA_BNC; + } else { + sc->tulip_probe_media = TULIP_MEDIA_AUI; + } + tulip_media_set(sc, sc->tulip_probe_media); + sc->tulip_flags &= ~TULIP_TXPROBE_ACTIVE; + tulip_timeout(sc); +} + +static const tulip_boardsw_t tulip_21041_boardsw = { + TULIP_21041_GENERIC, + tulip_21041_media_probe, + tulip_media_select, + tulip_21041_media_poll +}; + +static const tulip_phy_attr_t tulip_mii_phy_attrlist[] = { + { 0x20005c00, 0, /* 08-00-17 */ + { + { 0x19, 0x0040, 0x0040 }, /* 10TX */ + { 0x19, 0x0040, 0x0000 }, /* 100TX */ + }, +#if defined(TULIP_DEBUG) + "NS DP83840", +#endif + }, + { 0x0281F400, 0, /* 00-A0-7D */ + { + { 0x12, 0x0010, 0x0000 }, /* 10T */ + { }, /* 100TX */ + { 0x12, 0x0010, 0x0010 }, /* 100T4 */ + { 0x12, 0x0008, 0x0008 }, /* FULL_DUPLEX */ + }, +#if defined(TULIP_DEBUG) + "Seeq 80C240" +#endif + }, +#if 0 + { 0x0015F420, 0, /* 00-A0-7D */ + { + { 0x12, 0x0010, 0x0000 }, /* 10T */ + { }, /* 100TX */ + { 0x12, 0x0010, 0x0010 }, /* 100T4 */ + { 0x12, 0x0008, 0x0008 }, /* FULL_DUPLEX */ + }, +#if defined(TULIP_DEBUG) + "Broadcom BCM5000" +#endif + }, +#endif + { 0x0281F400, 0, /* 00-A0-BE */ + { + { 0x11, 0x8000, 0x0000 }, /* 10T */ + { 0x11, 0x8000, 0x8000 }, /* 100TX */ + { }, /* 100T4 */ + { 0x11, 0x4000, 0x4000 }, /* FULL_DUPLEX */ + }, +#if defined(TULIP_DEBUG) + "ICS 1890" +#endif + }, + { 0 } +}; + +static tulip_media_t +tulip_mii_phy_readspecific( + tulip_softc_t * const sc) +{ + const tulip_phy_attr_t *attr; + u_int16_t data; + u_int32_t id; + unsigned idx = 0; + static const tulip_media_t table[] = { + TULIP_MEDIA_UNKNOWN, + TULIP_MEDIA_10BASET, + TULIP_MEDIA_100BASETX, + TULIP_MEDIA_100BASET4, + TULIP_MEDIA_UNKNOWN, + TULIP_MEDIA_10BASET_FD, + TULIP_MEDIA_100BASETX_FD, + TULIP_MEDIA_UNKNOWN + }; + + /* + * Don't read phy specific registers if link is not up. + */ + data = tulip_mii_readreg(sc, sc->tulip_phyaddr, PHYREG_STATUS); + if ((data & (PHYSTS_LINK_UP|PHYSTS_EXTENDED_REGS)) != (PHYSTS_LINK_UP|PHYSTS_EXTENDED_REGS)) + return TULIP_MEDIA_UNKNOWN; + + id = (tulip_mii_readreg(sc, sc->tulip_phyaddr, PHYREG_IDLOW) << 16) | + tulip_mii_readreg(sc, sc->tulip_phyaddr, PHYREG_IDHIGH); + for (attr = tulip_mii_phy_attrlist;; attr++) { + if (attr->attr_id == 0) + return TULIP_MEDIA_UNKNOWN; + if ((id & ~0x0F) == attr->attr_id) + break; + } + + if (attr->attr_modes[PHY_MODE_100TX].pm_regno) { + const tulip_phy_modedata_t * const pm = &attr->attr_modes[PHY_MODE_100TX]; + data = tulip_mii_readreg(sc, sc->tulip_phyaddr, pm->pm_regno); + if ((data & pm->pm_mask) == pm->pm_value) + idx = 2; + } + if (idx == 0 && attr->attr_modes[PHY_MODE_100T4].pm_regno) { + const tulip_phy_modedata_t * const pm = &attr->attr_modes[PHY_MODE_100T4]; + data = tulip_mii_readreg(sc, sc->tulip_phyaddr, pm->pm_regno); + if ((data & pm->pm_mask) == pm->pm_value) + idx = 3; + } + if (idx == 0 && attr->attr_modes[PHY_MODE_10T].pm_regno) { + const tulip_phy_modedata_t * const pm = &attr->attr_modes[PHY_MODE_10T]; + data = tulip_mii_readreg(sc, sc->tulip_phyaddr, pm->pm_regno); + if ((data & pm->pm_mask) == pm->pm_value) + idx = 1; + } + if (idx != 0 && attr->attr_modes[PHY_MODE_FULLDUPLEX].pm_regno) { + const tulip_phy_modedata_t * const pm = &attr->attr_modes[PHY_MODE_FULLDUPLEX]; + data = tulip_mii_readreg(sc, sc->tulip_phyaddr, pm->pm_regno); + idx += ((data & pm->pm_mask) == pm->pm_value ? 4 : 0); + } + return table[idx]; +} + +static unsigned +tulip_mii_get_phyaddr( + tulip_softc_t * const sc, + unsigned offset) +{ + unsigned phyaddr; + + for (phyaddr = 1; phyaddr < 32; phyaddr++) { + unsigned status = tulip_mii_readreg(sc, phyaddr, PHYREG_STATUS); + if (status == 0 || status == 0xFFFF || status < PHYSTS_10BASET) + continue; + if (offset == 0) + return phyaddr; + offset--; + } + if (offset == 0) { + unsigned status = tulip_mii_readreg(sc, 0, PHYREG_STATUS); + if (status == 0 || status == 0xFFFF || status < PHYSTS_10BASET) + return TULIP_MII_NOPHY; + return 0; + } + return TULIP_MII_NOPHY; +} + +static int +tulip_mii_map_abilities( + tulip_softc_t * const sc, + unsigned abilities) +{ + sc->tulip_abilities = abilities; + if (abilities & PHYSTS_100BASETX_FD) { + sc->tulip_probe_media = TULIP_MEDIA_100BASETX_FD; + } else if (abilities & PHYSTS_100BASET4) { + sc->tulip_probe_media = TULIP_MEDIA_100BASET4; + } else if (abilities & PHYSTS_100BASETX) { + sc->tulip_probe_media = TULIP_MEDIA_100BASETX; + } else if (abilities & PHYSTS_10BASET_FD) { + sc->tulip_probe_media = TULIP_MEDIA_10BASET_FD; + } else if (abilities & PHYSTS_10BASET) { + sc->tulip_probe_media = TULIP_MEDIA_10BASET; + } else { + sc->tulip_probe_state = TULIP_PROBE_MEDIATEST; + return 0; + } + sc->tulip_probe_state = TULIP_PROBE_INACTIVE; + return 1; +} + +static void +tulip_mii_autonegotiate( + tulip_softc_t * const sc, + const unsigned phyaddr) +{ + switch (sc->tulip_probe_state) { + case TULIP_PROBE_MEDIATEST: + case TULIP_PROBE_INACTIVE: { + sc->tulip_flags |= TULIP_DIDNWAY; + tulip_mii_writereg(sc, phyaddr, PHYREG_CONTROL, PHYCTL_RESET); + sc->tulip_probe_timeout = 3000; + sc->tulip_intrmask |= TULIP_STS_ABNRMLINTR|TULIP_STS_NORMALINTR; + sc->tulip_probe_state = TULIP_PROBE_PHYRESET; + /* FALL THROUGH */ + } + case TULIP_PROBE_PHYRESET: { + u_int32_t status; + u_int32_t data = tulip_mii_readreg(sc, phyaddr, PHYREG_CONTROL); + if (data & PHYCTL_RESET) { + if (sc->tulip_probe_timeout > 0) { + tulip_timeout(sc); + return; + } + printf("%s%d(phy%d): error: reset of PHY never completed!\n", + sc->tulip_name, sc->tulip_unit, phyaddr); + sc->tulip_flags &= ~TULIP_TXPROBE_ACTIVE; + sc->tulip_probe_state = TULIP_PROBE_FAILED; + sc->tulip_if.if_flags &= ~(IFF_UP|IFF_RUNNING); + return; + } + status = tulip_mii_readreg(sc, phyaddr, PHYREG_STATUS); + if ((status & PHYSTS_CAN_AUTONEG) == 0) { +#if defined(TULIP_DEBUG) + loudprintf("%s%d(phy%d): autonegotiation disabled\n", + sc->tulip_name, sc->tulip_unit, phyaddr); +#endif + sc->tulip_flags &= ~TULIP_DIDNWAY; + sc->tulip_probe_state = TULIP_PROBE_MEDIATEST; + return; + } + if (tulip_mii_readreg(sc, phyaddr, PHYREG_AUTONEG_ADVERTISEMENT) != ((status >> 6) | 0x01)) + tulip_mii_writereg(sc, phyaddr, PHYREG_AUTONEG_ADVERTISEMENT, (status >> 6) | 0x01); + tulip_mii_writereg(sc, phyaddr, PHYREG_CONTROL, data|PHYCTL_AUTONEG_RESTART|PHYCTL_AUTONEG_ENABLE); + data = tulip_mii_readreg(sc, phyaddr, PHYREG_CONTROL); +#if defined(TULIP_DEBUG) + if ((data & PHYCTL_AUTONEG_ENABLE) == 0) + loudprintf("%s%d(phy%d): oops: enable autonegotiation failed: 0x%04x\n", + sc->tulip_name, sc->tulip_unit, phyaddr, data); + else + loudprintf("%s%d(phy%d): autonegotiation restarted: 0x%04x\n", + sc->tulip_name, sc->tulip_unit, phyaddr, data); + sc->tulip_dbg.dbg_nway_starts++; +#endif + sc->tulip_probe_state = TULIP_PROBE_PHYAUTONEG; + sc->tulip_probe_timeout = 3000; + /* FALL THROUGH */ + } + case TULIP_PROBE_PHYAUTONEG: { + u_int32_t status = tulip_mii_readreg(sc, phyaddr, PHYREG_STATUS); + u_int32_t data; + if ((status & PHYSTS_AUTONEG_DONE) == 0) { + if (sc->tulip_probe_timeout > 0) { + tulip_timeout(sc); + return; + } +#if defined(TULIP_DEBUG) + loudprintf("%s%d(phy%d): autonegotiation timeout: sts=0x%04x, ctl=0x%04x\n", + sc->tulip_name, sc->tulip_unit, phyaddr, status, + tulip_mii_readreg(sc, phyaddr, PHYREG_CONTROL)); +#endif + sc->tulip_flags &= ~TULIP_DIDNWAY; + sc->tulip_probe_state = TULIP_PROBE_MEDIATEST; + return; + } + data = tulip_mii_readreg(sc, phyaddr, PHYREG_AUTONEG_ABILITIES); +#if defined(TULIP_DEBUG) + loudprintf("%s%d(phy%d): autonegotiation complete: 0x%04x\n", + sc->tulip_name, sc->tulip_unit, phyaddr, data); +#endif + data = (data << 6) & status; + if (!tulip_mii_map_abilities(sc, data)) + sc->tulip_flags &= ~TULIP_DIDNWAY; + return; + } + default: { +#if defined(DIAGNOSTIC) + panic("tulip_media_poll: botch at line %d\n", __LINE__); +#endif + break; + } + } +#if defined(TULIP_DEBUG) + loudprintf("%s%d(phy%d): autonegotiation failure: state = %d\n", + sc->tulip_name, sc->tulip_unit, phyaddr, sc->tulip_probe_state); + sc->tulip_dbg.dbg_nway_failures++; +#endif +} + +static void +tulip_2114x_media_preset( + tulip_softc_t * const sc) +{ + const tulip_media_info_t *mi = NULL; + tulip_media_t media = sc->tulip_media; + + if (sc->tulip_probe_state == TULIP_PROBE_INACTIVE) + media = sc->tulip_media; + else + media = sc->tulip_probe_media; + + sc->tulip_cmdmode &= ~TULIP_CMD_PORTSELECT; + sc->tulip_flags &= ~TULIP_SQETEST; + if (media != TULIP_MEDIA_UNKNOWN && media != TULIP_MEDIA_MAX) { +#if defined(TULIP_DEBUG) + if (media < TULIP_MEDIA_MAX && sc->tulip_mediums[media] != NULL) { +#endif + mi = sc->tulip_mediums[media]; + if (mi->mi_type == TULIP_MEDIAINFO_MII) { + sc->tulip_cmdmode |= TULIP_CMD_PORTSELECT; + } else if (mi->mi_type == TULIP_MEDIAINFO_GPR + || mi->mi_type == TULIP_MEDIAINFO_SYM) { + sc->tulip_cmdmode &= ~TULIP_GPR_CMDBITS; + sc->tulip_cmdmode |= mi->mi_cmdmode; + } else if (mi->mi_type == TULIP_MEDIAINFO_SIA) { + TULIP_CSR_WRITE(sc, csr_sia_connectivity, TULIP_SIACONN_RESET); + } +#if defined(TULIP_DEBUG) + } else { + printf("%s%d: preset: bad media %d!\n", + sc->tulip_name, sc->tulip_unit, media); + } +#endif + } + switch (media) { + case TULIP_MEDIA_BNC: + case TULIP_MEDIA_AUI: + case TULIP_MEDIA_10BASET: { + sc->tulip_cmdmode &= ~TULIP_CMD_FULLDUPLEX; + sc->tulip_cmdmode |= TULIP_CMD_TXTHRSHLDCTL; + sc->tulip_if.if_baudrate = 10000000; + sc->tulip_flags |= TULIP_SQETEST; + break; + } + case TULIP_MEDIA_10BASET_FD: { + sc->tulip_cmdmode |= TULIP_CMD_FULLDUPLEX|TULIP_CMD_TXTHRSHLDCTL; + sc->tulip_if.if_baudrate = 10000000; + break; + } + case TULIP_MEDIA_100BASEFX: + case TULIP_MEDIA_100BASET4: + case TULIP_MEDIA_100BASETX: { + sc->tulip_cmdmode &= ~(TULIP_CMD_FULLDUPLEX|TULIP_CMD_TXTHRSHLDCTL); + sc->tulip_cmdmode |= TULIP_CMD_PORTSELECT; + sc->tulip_if.if_baudrate = 100000000; + break; + } + case TULIP_MEDIA_100BASEFX_FD: + case TULIP_MEDIA_100BASETX_FD: { + sc->tulip_cmdmode |= TULIP_CMD_FULLDUPLEX|TULIP_CMD_PORTSELECT; + sc->tulip_cmdmode &= ~TULIP_CMD_TXTHRSHLDCTL; + sc->tulip_if.if_baudrate = 100000000; + break; + } + default: { + break; + } + } + TULIP_CSR_WRITE(sc, csr_command, sc->tulip_cmdmode); +} + +/* + ******************************************************************** + * Start of 21140/21140A support which does not use the MII interface + */ + +static void +tulip_null_media_poll( + tulip_softc_t * const sc, + tulip_mediapoll_event_t event) +{ +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_events[event]++; +#endif +#if defined(DIAGNOSTIC) + printf("%s%d: botch(media_poll) at line %d\n", + sc->tulip_name, sc->tulip_unit, __LINE__); +#endif +} + +__inline__ static void +tulip_21140_mediainit( + tulip_softc_t * const sc, + tulip_media_info_t * const mip, + tulip_media_t const media, + unsigned gpdata, + unsigned cmdmode) +{ + sc->tulip_mediums[media] = mip; + mip->mi_type = TULIP_MEDIAINFO_GPR; + mip->mi_cmdmode = cmdmode; + mip->mi_gpdata = gpdata; +} + +static void +tulip_21140_evalboard_media_probe( + tulip_softc_t * const sc) +{ + tulip_media_info_t *mip = sc->tulip_mediainfo; + + sc->tulip_gpinit = TULIP_GP_EB_PINS; + sc->tulip_gpdata = TULIP_GP_EB_INIT; + TULIP_CSR_WRITE(sc, csr_gp, TULIP_GP_EB_PINS); + TULIP_CSR_WRITE(sc, csr_gp, TULIP_GP_EB_INIT); + TULIP_CSR_WRITE(sc, csr_command, + TULIP_CSR_READ(sc, csr_command) | TULIP_CMD_PORTSELECT | + TULIP_CMD_PCSFUNCTION | TULIP_CMD_SCRAMBLER | TULIP_CMD_MUSTBEONE); + TULIP_CSR_WRITE(sc, csr_command, + TULIP_CSR_READ(sc, csr_command) & ~TULIP_CMD_TXTHRSHLDCTL); + DELAY(1000000); + if ((TULIP_CSR_READ(sc, csr_gp) & TULIP_GP_EB_OK100) != 0) { + sc->tulip_media = TULIP_MEDIA_10BASET; + } else { + sc->tulip_media = TULIP_MEDIA_100BASETX; + } + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_10BASET, + TULIP_GP_EB_INIT, + TULIP_CMD_TXTHRSHLDCTL); + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_10BASET_FD, + TULIP_GP_EB_INIT, + TULIP_CMD_TXTHRSHLDCTL|TULIP_CMD_FULLDUPLEX); + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_100BASETX, + TULIP_GP_EB_INIT, + TULIP_CMD_PORTSELECT|TULIP_CMD_PCSFUNCTION + |TULIP_CMD_SCRAMBLER); + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_100BASETX_FD, + TULIP_GP_EB_INIT, + TULIP_CMD_PORTSELECT|TULIP_CMD_PCSFUNCTION + |TULIP_CMD_SCRAMBLER|TULIP_CMD_FULLDUPLEX); +} + +static const tulip_boardsw_t tulip_21140_eb_boardsw = { + TULIP_21140_DEC_EB, + tulip_21140_evalboard_media_probe, + tulip_media_select, + tulip_null_media_poll, + tulip_2114x_media_preset, +}; + +static void +tulip_21140_accton_media_probe( + tulip_softc_t * const sc) +{ + tulip_media_info_t *mip = sc->tulip_mediainfo; + unsigned gpdata; + + sc->tulip_gpinit = TULIP_GP_EB_PINS; + sc->tulip_gpdata = TULIP_GP_EB_INIT; + TULIP_CSR_WRITE(sc, csr_gp, TULIP_GP_EB_PINS); + TULIP_CSR_WRITE(sc, csr_gp, TULIP_GP_EB_INIT); + TULIP_CSR_WRITE(sc, csr_command, + TULIP_CSR_READ(sc, csr_command) | TULIP_CMD_PORTSELECT | + TULIP_CMD_PCSFUNCTION | TULIP_CMD_SCRAMBLER | TULIP_CMD_MUSTBEONE); + TULIP_CSR_WRITE(sc, csr_command, + TULIP_CSR_READ(sc, csr_command) & ~TULIP_CMD_TXTHRSHLDCTL); + DELAY(1000000); + gpdata = TULIP_CSR_READ(sc, csr_gp); + if ((gpdata & TULIP_GP_EN1207_UTP_INIT) == 0) { + sc->tulip_media = TULIP_MEDIA_10BASET; + } else { + if ((gpdata & TULIP_GP_EN1207_BNC_INIT) == 0) { + sc->tulip_media = TULIP_MEDIA_BNC; + } else { + sc->tulip_media = TULIP_MEDIA_100BASETX; + } + } + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_BNC, + TULIP_GP_EN1207_BNC_INIT, + TULIP_CMD_TXTHRSHLDCTL); + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_10BASET, + TULIP_GP_EN1207_UTP_INIT, + TULIP_CMD_TXTHRSHLDCTL); + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_10BASET_FD, + TULIP_GP_EN1207_UTP_INIT, + TULIP_CMD_TXTHRSHLDCTL|TULIP_CMD_FULLDUPLEX); + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_100BASETX, + TULIP_GP_EN1207_100_INIT, + TULIP_CMD_PORTSELECT|TULIP_CMD_PCSFUNCTION + |TULIP_CMD_SCRAMBLER); + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_100BASETX_FD, + TULIP_GP_EN1207_100_INIT, + TULIP_CMD_PORTSELECT|TULIP_CMD_PCSFUNCTION + |TULIP_CMD_SCRAMBLER|TULIP_CMD_FULLDUPLEX); +} + +static const tulip_boardsw_t tulip_21140_accton_boardsw = { + TULIP_21140_EN1207, + tulip_21140_accton_media_probe, + tulip_media_select, + tulip_null_media_poll, + tulip_2114x_media_preset, +}; + +static void +tulip_21140_smc9332_media_probe( + tulip_softc_t * const sc) +{ + tulip_media_info_t *mip = sc->tulip_mediainfo; + int idx, cnt = 0; + + TULIP_CSR_WRITE(sc, csr_command, TULIP_CMD_PORTSELECT|TULIP_CMD_MUSTBEONE); + TULIP_CSR_WRITE(sc, csr_busmode, TULIP_BUSMODE_SWRESET); + DELAY(10); /* Wait 10 microseconds (actually 50 PCI cycles but at + 33MHz that comes to two microseconds but wait a + bit longer anyways) */ + TULIP_CSR_WRITE(sc, csr_command, TULIP_CMD_PORTSELECT | + TULIP_CMD_PCSFUNCTION | TULIP_CMD_SCRAMBLER | TULIP_CMD_MUSTBEONE); + sc->tulip_gpinit = TULIP_GP_SMC_9332_PINS; + sc->tulip_gpdata = TULIP_GP_SMC_9332_INIT; + TULIP_CSR_WRITE(sc, csr_gp, TULIP_GP_SMC_9332_PINS|TULIP_GP_PINSET); + TULIP_CSR_WRITE(sc, csr_gp, TULIP_GP_SMC_9332_INIT); + DELAY(200000); + for (idx = 1000; idx > 0; idx--) { + u_int32_t csr = TULIP_CSR_READ(sc, csr_gp); + if ((csr & (TULIP_GP_SMC_9332_OK10|TULIP_GP_SMC_9332_OK100)) == (TULIP_GP_SMC_9332_OK10|TULIP_GP_SMC_9332_OK100)) { + if (++cnt > 100) + break; + } else if ((csr & TULIP_GP_SMC_9332_OK10) == 0) { + break; + } else { + cnt = 0; + } + DELAY(1000); + } + sc->tulip_media = cnt > 100 ? TULIP_MEDIA_100BASETX : TULIP_MEDIA_10BASET; + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_100BASETX, + TULIP_GP_SMC_9332_INIT, + TULIP_CMD_PORTSELECT|TULIP_CMD_PCSFUNCTION + |TULIP_CMD_SCRAMBLER); + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_100BASETX_FD, + TULIP_GP_SMC_9332_INIT, + TULIP_CMD_PORTSELECT|TULIP_CMD_PCSFUNCTION + |TULIP_CMD_SCRAMBLER|TULIP_CMD_FULLDUPLEX); + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_10BASET, + TULIP_GP_SMC_9332_INIT, + TULIP_CMD_TXTHRSHLDCTL); + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_10BASET_FD, + TULIP_GP_SMC_9332_INIT, + TULIP_CMD_TXTHRSHLDCTL|TULIP_CMD_FULLDUPLEX); +} + +static const tulip_boardsw_t tulip_21140_smc9332_boardsw = { + TULIP_21140_SMC_9332, + tulip_21140_smc9332_media_probe, + tulip_media_select, + tulip_null_media_poll, + tulip_2114x_media_preset, +}; + +static void +tulip_21140_cogent_em100_media_probe( + tulip_softc_t * const sc) +{ + tulip_media_info_t *mip = sc->tulip_mediainfo; + u_int32_t cmdmode = TULIP_CSR_READ(sc, csr_command); + + sc->tulip_gpinit = TULIP_GP_EM100_PINS; + sc->tulip_gpdata = TULIP_GP_EM100_INIT; + TULIP_CSR_WRITE(sc, csr_gp, TULIP_GP_EM100_PINS); + TULIP_CSR_WRITE(sc, csr_gp, TULIP_GP_EM100_INIT); + + cmdmode = TULIP_CMD_PORTSELECT|TULIP_CMD_PCSFUNCTION|TULIP_CMD_MUSTBEONE; + cmdmode &= ~(TULIP_CMD_TXTHRSHLDCTL|TULIP_CMD_SCRAMBLER); + if (sc->tulip_rombuf[32] == TULIP_COGENT_EM100FX_ID) { + TULIP_CSR_WRITE(sc, csr_command, cmdmode); + sc->tulip_media = TULIP_MEDIA_100BASEFX; + + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_100BASEFX, + TULIP_GP_EM100_INIT, + TULIP_CMD_PORTSELECT|TULIP_CMD_PCSFUNCTION); + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_100BASEFX_FD, + TULIP_GP_EM100_INIT, + TULIP_CMD_PORTSELECT|TULIP_CMD_PCSFUNCTION + |TULIP_CMD_FULLDUPLEX); + } else { + TULIP_CSR_WRITE(sc, csr_command, cmdmode|TULIP_CMD_SCRAMBLER); + sc->tulip_media = TULIP_MEDIA_100BASETX; + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_100BASETX, + TULIP_GP_EM100_INIT, + TULIP_CMD_PORTSELECT|TULIP_CMD_PCSFUNCTION + |TULIP_CMD_SCRAMBLER); + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_100BASETX_FD, + TULIP_GP_EM100_INIT, + TULIP_CMD_PORTSELECT|TULIP_CMD_PCSFUNCTION + |TULIP_CMD_SCRAMBLER|TULIP_CMD_FULLDUPLEX); + } +} + +static const tulip_boardsw_t tulip_21140_cogent_em100_boardsw = { + TULIP_21140_COGENT_EM100, + tulip_21140_cogent_em100_media_probe, + tulip_media_select, + tulip_null_media_poll, + tulip_2114x_media_preset +}; + +static void +tulip_21140_znyx_zx34x_media_probe( + tulip_softc_t * const sc) +{ + tulip_media_info_t *mip = sc->tulip_mediainfo; + int cnt10 = 0, cnt100 = 0, idx; + + sc->tulip_gpinit = TULIP_GP_ZX34X_PINS; + sc->tulip_gpdata = TULIP_GP_ZX34X_INIT; + TULIP_CSR_WRITE(sc, csr_gp, TULIP_GP_ZX34X_PINS); + TULIP_CSR_WRITE(sc, csr_gp, TULIP_GP_ZX34X_INIT); + TULIP_CSR_WRITE(sc, csr_command, + TULIP_CSR_READ(sc, csr_command) | TULIP_CMD_PORTSELECT | + TULIP_CMD_PCSFUNCTION | TULIP_CMD_SCRAMBLER | TULIP_CMD_MUSTBEONE); + TULIP_CSR_WRITE(sc, csr_command, + TULIP_CSR_READ(sc, csr_command) & ~TULIP_CMD_TXTHRSHLDCTL); + + DELAY(200000); + for (idx = 1000; idx > 0; idx--) { + u_int32_t csr = TULIP_CSR_READ(sc, csr_gp); + if ((csr & (TULIP_GP_ZX34X_LNKFAIL|TULIP_GP_ZX34X_SYMDET|TULIP_GP_ZX34X_SIGDET)) == (TULIP_GP_ZX34X_LNKFAIL|TULIP_GP_ZX34X_SYMDET|TULIP_GP_ZX34X_SIGDET)) { + if (++cnt100 > 100) + break; + } else if ((csr & TULIP_GP_ZX34X_LNKFAIL) == 0) { + if (++cnt10 > 100) + break; + } else { + cnt10 = 0; + cnt100 = 0; + } + DELAY(1000); + } + sc->tulip_media = cnt100 > 100 ? TULIP_MEDIA_100BASETX : TULIP_MEDIA_10BASET; + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_10BASET, + TULIP_GP_ZX34X_INIT, + TULIP_CMD_TXTHRSHLDCTL); + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_10BASET_FD, + TULIP_GP_ZX34X_INIT, + TULIP_CMD_TXTHRSHLDCTL|TULIP_CMD_FULLDUPLEX); + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_100BASETX, + TULIP_GP_ZX34X_INIT, + TULIP_CMD_PORTSELECT|TULIP_CMD_PCSFUNCTION + |TULIP_CMD_SCRAMBLER); + tulip_21140_mediainit(sc, mip++, TULIP_MEDIA_100BASETX_FD, + TULIP_GP_ZX34X_INIT, + TULIP_CMD_PORTSELECT|TULIP_CMD_PCSFUNCTION + |TULIP_CMD_SCRAMBLER|TULIP_CMD_FULLDUPLEX); +} + +static const tulip_boardsw_t tulip_21140_znyx_zx34x_boardsw = { + TULIP_21140_ZNYX_ZX34X, + tulip_21140_znyx_zx34x_media_probe, + tulip_media_select, + tulip_null_media_poll, + tulip_2114x_media_preset, +}; + +static void +tulip_2114x_media_probe( + tulip_softc_t * const sc) +{ + sc->tulip_cmdmode |= TULIP_CMD_MUSTBEONE + |TULIP_CMD_BACKOFFCTR|TULIP_CMD_THRSHLD72; +} + +static const tulip_boardsw_t tulip_2114x_isv_boardsw = { + TULIP_21140_ISV, + tulip_2114x_media_probe, + tulip_media_select, + tulip_media_poll, + tulip_2114x_media_preset, +}; + +/* + * ******** END of chip-specific handlers. *********** + */ + +/* + * Code the read the SROM and MII bit streams (I2C) + */ +#define EMIT do { TULIP_CSR_WRITE(sc, csr_srom_mii, csr); DELAY(1); } while (0) + +static void +tulip_srom_idle( + tulip_softc_t * const sc) +{ + unsigned bit, csr; + + csr = SROMSEL ; EMIT; + csr = SROMSEL | SROMRD; EMIT; + csr ^= SROMCS; EMIT; + csr ^= SROMCLKON; EMIT; + + /* + * Write 25 cycles of 0 which will force the SROM to be idle. + */ + for (bit = 3 + SROM_BITWIDTH + 16; bit > 0; bit--) { + csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */ + csr ^= SROMCLKON; EMIT; /* clock high; data valid */ + } + csr ^= SROMCLKOFF; EMIT; + csr ^= SROMCS; EMIT; + csr = 0; EMIT; +} + + +static void +tulip_srom_read( + tulip_softc_t * const sc) +{ + unsigned idx; + const unsigned bitwidth = SROM_BITWIDTH; + const unsigned cmdmask = (SROMCMD_RD << bitwidth); + const unsigned msb = 1 << (bitwidth + 3 - 1); + unsigned lastidx = (1 << bitwidth) - 1; + + tulip_srom_idle(sc); + + for (idx = 0; idx <= lastidx; idx++) { + unsigned lastbit, data, bits, bit, csr; + csr = SROMSEL ; EMIT; + csr = SROMSEL | SROMRD; EMIT; + csr ^= SROMCSON; EMIT; + csr ^= SROMCLKON; EMIT; + + lastbit = 0; + for (bits = idx|cmdmask, bit = bitwidth + 3; bit > 0; bit--, bits <<= 1) { + const unsigned thisbit = bits & msb; + csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */ + if (thisbit != lastbit) { + csr ^= SROMDOUT; EMIT; /* clock low; invert data */ + } else { + EMIT; + } + csr ^= SROMCLKON; EMIT; /* clock high; data valid */ + lastbit = thisbit; + } + csr ^= SROMCLKOFF; EMIT; + + for (data = 0, bits = 0; bits < 16; bits++) { + data <<= 1; + csr ^= SROMCLKON; EMIT; /* clock high; data valid */ + data |= TULIP_CSR_READ(sc, csr_srom_mii) & SROMDIN ? 1 : 0; + csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */ + } + sc->tulip_rombuf[idx*2] = data & 0xFF; + sc->tulip_rombuf[idx*2+1] = data >> 8; + csr = SROMSEL | SROMRD; EMIT; + csr = 0; EMIT; + } + tulip_srom_idle(sc); +} + +#define MII_EMIT do { TULIP_CSR_WRITE(sc, csr_srom_mii, csr); DELAY(1); } while (0) + +static void +tulip_mii_writebits( + tulip_softc_t * const sc, + unsigned data, + unsigned bits) +{ + unsigned msb = 1 << (bits - 1); + unsigned csr = TULIP_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK); + unsigned lastbit = (csr & MII_DOUT) ? msb : 0; + + csr |= MII_WR; MII_EMIT; /* clock low; assert write */ + + for (; bits > 0; bits--, data <<= 1) { + const unsigned thisbit = data & msb; + if (thisbit != lastbit) { + csr ^= MII_DOUT; MII_EMIT; /* clock low; invert data */ + } + csr ^= MII_CLKON; MII_EMIT; /* clock high; data valid */ + lastbit = thisbit; + csr ^= MII_CLKOFF; MII_EMIT; /* clock low; data not valid */ + } +} + +static void +tulip_mii_turnaround( + tulip_softc_t * const sc, + unsigned cmd) +{ + unsigned csr = TULIP_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK); + + if (cmd == MII_WRCMD) { + csr |= MII_DOUT; MII_EMIT; /* clock low; change data */ + csr ^= MII_CLKON; MII_EMIT; /* clock high; data valid */ + csr ^= MII_CLKOFF; MII_EMIT; /* clock low; data not valid */ + csr ^= MII_DOUT; MII_EMIT; /* clock low; change data */ + } else { + csr |= MII_RD; MII_EMIT; /* clock low; switch to read */ + } + csr ^= MII_CLKON; MII_EMIT; /* clock high; data valid */ + csr ^= MII_CLKOFF; MII_EMIT; /* clock low; data not valid */ +} + +static unsigned +tulip_mii_readbits( + tulip_softc_t * const sc) +{ + unsigned data; + unsigned csr = TULIP_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK); + int idx; + + for (idx = 0, data = 0; idx < 16; idx++) { + data <<= 1; /* this is NOOP on the first pass through */ + csr ^= MII_CLKON; MII_EMIT; /* clock high; data valid */ + if (TULIP_CSR_READ(sc, csr_srom_mii) & MII_DIN) + data |= 1; + csr ^= MII_CLKOFF; MII_EMIT; /* clock low; data not valid */ + } + csr ^= MII_RD; MII_EMIT; /* clock low; turn off read */ + + return data; +} + +static unsigned +tulip_mii_readreg( + tulip_softc_t * const sc, + unsigned devaddr, + unsigned regno) +{ + unsigned csr = TULIP_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK); + unsigned data; + + csr &= ~(MII_RD|MII_CLK); MII_EMIT; + tulip_mii_writebits(sc, MII_PREAMBLE, 32); + tulip_mii_writebits(sc, MII_RDCMD, 8); + tulip_mii_writebits(sc, devaddr, 5); + tulip_mii_writebits(sc, regno, 5); + tulip_mii_turnaround(sc, MII_RDCMD); + + data = tulip_mii_readbits(sc); +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_phyregs[regno][0] = data; + sc->tulip_dbg.dbg_phyregs[regno][1]++; +#endif + return data; +} + +static void +tulip_mii_writereg( + tulip_softc_t * const sc, + unsigned devaddr, + unsigned regno, + unsigned data) +{ + unsigned csr = TULIP_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK); + csr &= ~(MII_RD|MII_CLK); MII_EMIT; + tulip_mii_writebits(sc, MII_PREAMBLE, 32); + tulip_mii_writebits(sc, MII_WRCMD, 8); + tulip_mii_writebits(sc, devaddr, 5); + tulip_mii_writebits(sc, regno, 5); + tulip_mii_turnaround(sc, MII_WRCMD); + tulip_mii_writebits(sc, data, 16); +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_phyregs[regno][2] = data; + sc->tulip_dbg.dbg_phyregs[regno][3]++; +#endif +} + +#define tulip_mchash(mca) (tulip_crc32(mca, 6) & 0x1FF) +#define tulip_srom_crcok(databuf) ( \ + ((tulip_crc32(databuf, 126) & 0xFFFFU) ^ 0xFFFFU) == \ + ((databuf)[126] | ((databuf)[127] << 8))) + +static unsigned +tulip_crc32( + const unsigned char *databuf, + size_t datalen) +{ + u_int idx, crc = 0xFFFFFFFFUL; + static const u_int crctab[] = { + 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, + 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, + 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c + }; + + for (idx = 0; idx < datalen; idx++) { + crc ^= *databuf++; + crc = (crc >> 4) ^ crctab[crc & 0xf]; + crc = (crc >> 4) ^ crctab[crc & 0xf]; + } + return crc; +} + +static void +tulip_identify_dec_nic( + tulip_softc_t * const sc) +{ + strcpy(sc->tulip_boardid, "DEC "); +#define D0 4 + if (sc->tulip_chipid <= TULIP_21040) + return; + if (bcmp(sc->tulip_rombuf + 29, "DE500", 5) == 0 + || bcmp(sc->tulip_rombuf + 29, "DE450", 5) == 0) { + bcopy(sc->tulip_rombuf + 29, &sc->tulip_boardid[D0], 8); + sc->tulip_boardid[D0+8] = ' '; + } +#undef D0 +} + +static void +tulip_identify_znyx_nic( + tulip_softc_t * const sc) +{ + unsigned id = 0; + strcpy(sc->tulip_boardid, "ZNYX ZX3XX "); + if (sc->tulip_chipid == TULIP_21140 || sc->tulip_chipid == TULIP_21140A) { + unsigned znyx_ptr; + sc->tulip_boardid[8] = '4'; + znyx_ptr = sc->tulip_rombuf[124] + 256 * sc->tulip_rombuf[125]; + if (znyx_ptr < 26 || znyx_ptr > 116) { + sc->tulip_boardsw = &tulip_21140_znyx_zx34x_boardsw; + return; + } + /* ZX344 = 0010 .. 0013FF + */ + if (sc->tulip_rombuf[znyx_ptr] == 0x4A + && sc->tulip_rombuf[znyx_ptr + 1] == 0x52 + && sc->tulip_rombuf[znyx_ptr + 2] == 0x01) { + id = sc->tulip_rombuf[znyx_ptr + 5] + 256 * sc->tulip_rombuf[znyx_ptr + 4]; + if ((id >> 8) == (TULIP_ZNYX_ID_ZX342 >> 8)) { + sc->tulip_boardid[9] = '2'; + if (id == TULIP_ZNYX_ID_ZX342B) { + sc->tulip_boardid[10] = 'B'; + sc->tulip_boardid[11] = ' '; + } + sc->tulip_boardsw = &tulip_21140_znyx_zx34x_boardsw; + } else if (id == TULIP_ZNYX_ID_ZX344) { + sc->tulip_boardid[10] = '4'; + sc->tulip_boardsw = &tulip_21140_znyx_zx34x_boardsw; + } else if (id == TULIP_ZNYX_ID_ZX345) { + sc->tulip_boardid[9] = (sc->tulip_rombuf[19] > 1) ? '8' : '5'; + } else if (id == TULIP_ZNYX_ID_ZX346) { + sc->tulip_boardid[9] = '6'; + } else if (id == TULIP_ZNYX_ID_ZX351) { + sc->tulip_boardid[8] = '5'; + sc->tulip_boardid[9] = '1'; + } + } + if (id == 0) { + /* + * Assume it's a ZX342... + */ + sc->tulip_boardsw = &tulip_21140_znyx_zx34x_boardsw; + } + return; + } + sc->tulip_boardid[8] = '1'; + if (sc->tulip_chipid == TULIP_21041) { + sc->tulip_boardid[10] = '1'; + return; + } + if (sc->tulip_rombuf[32] == 0x4A && sc->tulip_rombuf[33] == 0x52) { + id = sc->tulip_rombuf[37] + 256 * sc->tulip_rombuf[36]; + if (id == TULIP_ZNYX_ID_ZX312T) { + sc->tulip_boardid[9] = '2'; + sc->tulip_boardid[10] = 'T'; + sc->tulip_boardid[11] = ' '; + sc->tulip_boardsw = &tulip_21040_10baset_only_boardsw; + } else if (id == TULIP_ZNYX_ID_ZX314_INTA) { + sc->tulip_boardid[9] = '4'; + sc->tulip_boardsw = &tulip_21040_10baset_only_boardsw; + sc->tulip_features |= TULIP_HAVE_SHAREDINTR|TULIP_HAVE_BASEROM; + } else if (id == TULIP_ZNYX_ID_ZX314) { + sc->tulip_boardid[9] = '4'; + sc->tulip_boardsw = &tulip_21040_10baset_only_boardsw; + sc->tulip_features |= TULIP_HAVE_BASEROM; + } else if (id == TULIP_ZNYX_ID_ZX315_INTA) { + sc->tulip_boardid[9] = '5'; + sc->tulip_features |= TULIP_HAVE_SHAREDINTR|TULIP_HAVE_BASEROM; + } else if (id == TULIP_ZNYX_ID_ZX315) { + sc->tulip_boardid[9] = '5'; + sc->tulip_features |= TULIP_HAVE_BASEROM; + } else { + id = 0; + } + } + if (id == 0) { + if ((sc->tulip_enaddr[3] & ~3) == 0xF0 && (sc->tulip_enaddr[5] & 2) == 0) { + sc->tulip_boardid[9] = '4'; + sc->tulip_boardsw = &tulip_21040_10baset_only_boardsw; + sc->tulip_features |= TULIP_HAVE_SHAREDINTR|TULIP_HAVE_BASEROM; + } else if ((sc->tulip_enaddr[3] & ~3) == 0xF4 && (sc->tulip_enaddr[5] & 1) == 0) { + sc->tulip_boardid[9] = '5'; + sc->tulip_boardsw = &tulip_21040_boardsw; + sc->tulip_features |= TULIP_HAVE_SHAREDINTR|TULIP_HAVE_BASEROM; + } else if ((sc->tulip_enaddr[3] & ~3) == 0xEC) { + sc->tulip_boardid[9] = '2'; + sc->tulip_boardsw = &tulip_21040_boardsw; + } + } +} + +static void +tulip_identify_smc_nic( + tulip_softc_t * const sc) +{ + u_int32_t id1, id2, ei; + int auibnc = 0, utp = 0; + char *cp; + + strcpy(sc->tulip_boardid, "SMC "); + if (sc->tulip_chipid == TULIP_21041) + return; + if (sc->tulip_chipid != TULIP_21040) { + if (sc->tulip_boardsw != &tulip_2114x_isv_boardsw) { + strcpy(&sc->tulip_boardid[4], "9332DST "); + sc->tulip_boardsw = &tulip_21140_smc9332_boardsw; + } else if (sc->tulip_features & (TULIP_HAVE_BASEROM|TULIP_HAVE_SLAVEDROM)) { + strcpy(&sc->tulip_boardid[4], "9334BDT "); + } else { + strcpy(&sc->tulip_boardid[4], "9332BDT "); + } + return; + } + id1 = sc->tulip_rombuf[0x60] | (sc->tulip_rombuf[0x61] << 8); + id2 = sc->tulip_rombuf[0x62] | (sc->tulip_rombuf[0x63] << 8); + ei = sc->tulip_rombuf[0x66] | (sc->tulip_rombuf[0x67] << 8); + + strcpy(&sc->tulip_boardid[4], "8432"); + cp = &sc->tulip_boardid[8]; + if ((id1 & 1) == 0) + *cp++ = 'B', auibnc = 1; + if ((id1 & 0xFF) > 0x32) + *cp++ = 'T', utp = 1; + if ((id1 & 0x4000) == 0) + *cp++ = 'A', auibnc = 1; + if (id2 == 0x15) { + sc->tulip_boardid[7] = '4'; + *cp++ = '-'; + *cp++ = 'C'; + *cp++ = 'H'; + *cp++ = (ei ? '2' : '1'); + } + *cp++ = ' '; + *cp = '\0'; + if (utp && !auibnc) + sc->tulip_boardsw = &tulip_21040_10baset_only_boardsw; + else if (!utp && auibnc) + sc->tulip_boardsw = &tulip_21040_auibnc_only_boardsw; +} + +static void +tulip_identify_cogent_nic( + tulip_softc_t * const sc) +{ + strcpy(sc->tulip_boardid, "Cogent "); + if (sc->tulip_chipid == TULIP_21140 || sc->tulip_chipid == TULIP_21140A) { + if (sc->tulip_rombuf[32] == TULIP_COGENT_EM100TX_ID) { + strcat(sc->tulip_boardid, "EM100TX "); + sc->tulip_boardsw = &tulip_21140_cogent_em100_boardsw; +#if defined(TULIP_COGENT_EM110TX_ID) + } else if (sc->tulip_rombuf[32] == TULIP_COGENT_EM110TX_ID) { + strcat(sc->tulip_boardid, "EM110TX "); + sc->tulip_boardsw = &tulip_21140_cogent_em100_boardsw; +#endif + } else if (sc->tulip_rombuf[32] == TULIP_COGENT_EM100FX_ID) { + strcat(sc->tulip_boardid, "EM100FX "); + sc->tulip_boardsw = &tulip_21140_cogent_em100_boardsw; + } + /* + * Magic number (0x24001109U) is the SubVendor (0x2400) and + * SubDevId (0x1109) for the ANA6944TX (EM440TX). + */ + if (*(u_int32_t *) sc->tulip_rombuf == 0x24001109U + && (sc->tulip_features & TULIP_HAVE_BASEROM)) { + /* + * Cogent (Adaptec) is still mapping all INTs to INTA of + * first 21140. Dumb! Dumb! + */ + strcat(sc->tulip_boardid, "EM440TX "); + sc->tulip_features |= TULIP_HAVE_SHAREDINTR; + } + } else if (sc->tulip_chipid == TULIP_21040) { + sc->tulip_features |= TULIP_HAVE_SHAREDINTR|TULIP_HAVE_BASEROM; + } +} + +static void +tulip_identify_accton_nic( + tulip_softc_t * const sc) +{ + strcpy(sc->tulip_boardid, "ACCTON "); + switch (sc->tulip_chipid) { + case TULIP_21140A: + strcat(sc->tulip_boardid, "EN1207 "); + if (sc->tulip_boardsw != &tulip_2114x_isv_boardsw) + sc->tulip_boardsw = &tulip_21140_accton_boardsw; + break; + case TULIP_21140: + strcat(sc->tulip_boardid, "EN1207TX "); + if (sc->tulip_boardsw != &tulip_2114x_isv_boardsw) + sc->tulip_boardsw = &tulip_21140_eb_boardsw; + break; + case TULIP_21040: + strcat(sc->tulip_boardid, "EN1203 "); + sc->tulip_boardsw = &tulip_21040_boardsw; + break; + case TULIP_21041: + strcat(sc->tulip_boardid, "EN1203 "); + sc->tulip_boardsw = &tulip_21041_boardsw; + break; + default: + sc->tulip_boardsw = &tulip_2114x_isv_boardsw; + break; + } +} + +static void +tulip_identify_asante_nic( + tulip_softc_t * const sc) +{ + strcpy(sc->tulip_boardid, "Asante "); + if ((sc->tulip_chipid == TULIP_21140 || sc->tulip_chipid == TULIP_21140A) + && sc->tulip_boardsw != &tulip_2114x_isv_boardsw) { + tulip_media_info_t *mi = sc->tulip_mediainfo; + int idx; + /* + * The Asante Fast Ethernet doesn't always ship with a valid + * new format SROM. So if isn't in the new format, we cheat + * set it up as if we had. + */ + + sc->tulip_gpinit = TULIP_GP_ASANTE_PINS; + sc->tulip_gpdata = 0; + + TULIP_CSR_WRITE(sc, csr_gp, TULIP_GP_ASANTE_PINS|TULIP_GP_PINSET); + TULIP_CSR_WRITE(sc, csr_gp, TULIP_GP_ASANTE_PHYRESET); + DELAY(100); + TULIP_CSR_WRITE(sc, csr_gp, 0); + + mi->mi_type = TULIP_MEDIAINFO_MII; + mi->mi_gpr_length = 0; + mi->mi_gpr_offset = 0; + mi->mi_reset_length = 0; + mi->mi_reset_offset = 0;; + + mi->mi_phyaddr = TULIP_MII_NOPHY; + for (idx = 20; idx > 0 && mi->mi_phyaddr == TULIP_MII_NOPHY; idx--) { + DELAY(10000); + mi->mi_phyaddr = tulip_mii_get_phyaddr(sc, 0); + } + if (mi->mi_phyaddr == TULIP_MII_NOPHY) { + printf("%s%d: can't find phy 0\n", sc->tulip_name, sc->tulip_unit); + return; + } + + sc->tulip_features |= TULIP_HAVE_MII; + mi->mi_capabilities = PHYSTS_10BASET|PHYSTS_10BASET_FD|PHYSTS_100BASETX|PHYSTS_100BASETX_FD; + mi->mi_advertisement = PHYSTS_10BASET|PHYSTS_10BASET_FD|PHYSTS_100BASETX|PHYSTS_100BASETX_FD; + mi->mi_full_duplex = PHYSTS_10BASET_FD|PHYSTS_100BASETX_FD; + mi->mi_tx_threshold = PHYSTS_10BASET|PHYSTS_10BASET_FD; + TULIP_MEDIAINFO_ADD_CAPABILITY(sc, mi, 100BASETX_FD); + TULIP_MEDIAINFO_ADD_CAPABILITY(sc, mi, 100BASETX); + TULIP_MEDIAINFO_ADD_CAPABILITY(sc, mi, 100BASET4); + TULIP_MEDIAINFO_ADD_CAPABILITY(sc, mi, 10BASET_FD); + TULIP_MEDIAINFO_ADD_CAPABILITY(sc, mi, 10BASET); + mi->mi_phyid = (tulip_mii_readreg(sc, mi->mi_phyaddr, PHYREG_IDLOW) << 16) | + tulip_mii_readreg(sc, mi->mi_phyaddr, PHYREG_IDHIGH); + + sc->tulip_boardsw = &tulip_2114x_isv_boardsw; + } +} + +static void +tulip_identify_compex_nic( + tulip_softc_t * const sc) +{ + strcpy(sc->tulip_boardid, "COMPEX "); + if (sc->tulip_chipid == TULIP_21140A) { + int root_unit; + tulip_softc_t *root_sc = NULL; + + strcat(sc->tulip_boardid, "400TX/PCI "); + /* + * All 4 chips on these boards share an interrupt. This code + * copied from tulip_read_macaddr. + */ + sc->tulip_features |= TULIP_HAVE_SHAREDINTR; + for (root_unit = sc->tulip_unit - 1; root_unit >= 0; root_unit--) { + root_sc = tulips[root_unit]; + if (root_sc == NULL + || !(root_sc->tulip_features & TULIP_HAVE_SLAVEDINTR)) + break; + root_sc = NULL; + } + if (root_sc != NULL + && root_sc->tulip_chipid == sc->tulip_chipid + && root_sc->tulip_pci_busno == sc->tulip_pci_busno) { + sc->tulip_features |= TULIP_HAVE_SLAVEDINTR; + sc->tulip_slaves = root_sc->tulip_slaves; + root_sc->tulip_slaves = sc; + } else if(sc->tulip_features & TULIP_HAVE_SLAVEDINTR) { + printf("\nCannot find master device for de%d interrupts", + sc->tulip_unit); + } + } else { + strcat(sc->tulip_boardid, "unknown "); + } + /* sc->tulip_boardsw = &tulip_21140_eb_boardsw; */ + return; +} + +static int +tulip_srom_decode( + tulip_softc_t * const sc) +{ + unsigned idx1, idx2, idx3; + + const tulip_srom_header_t *shp = (const tulip_srom_header_t *) &sc->tulip_rombuf[0]; + const tulip_srom_adapter_info_t *saip = (const tulip_srom_adapter_info_t *) (shp + 1); + tulip_srom_media_t srom_media; + tulip_media_info_t *mi = sc->tulip_mediainfo; + const u_int8_t *dp; + u_int32_t leaf_offset, blocks, data; + + for (idx1 = 0; idx1 < shp->sh_adapter_count; idx1++, saip++) { + if (shp->sh_adapter_count == 1) + break; + if (saip->sai_device == sc->tulip_pci_devno) + break; + } + /* + * Didn't find the right media block for this card. + */ + if (idx1 == shp->sh_adapter_count) + return 0; + + /* + * Save the hardware address. + */ + bcopy(shp->sh_ieee802_address, sc->tulip_enaddr, 6); + /* + * If this is a multiple port card, add the adapter index to the last + * byte of the hardware address. (if it isn't multiport, adding 0 + * won't hurt. + */ + sc->tulip_enaddr[5] += idx1; + + leaf_offset = saip->sai_leaf_offset_lowbyte + + saip->sai_leaf_offset_highbyte * 256; + dp = sc->tulip_rombuf + leaf_offset; + + sc->tulip_conntype = (tulip_srom_connection_t) (dp[0] + dp[1] * 256); dp += 2; + + for (idx2 = 0;; idx2++) { + if (tulip_srom_conninfo[idx2].sc_type == sc->tulip_conntype + || tulip_srom_conninfo[idx2].sc_type == TULIP_SROM_CONNTYPE_NOT_USED) + break; + } + sc->tulip_connidx = idx2; + + if (sc->tulip_chipid == TULIP_21041) { + blocks = *dp++; + for (idx2 = 0; idx2 < blocks; idx2++) { + tulip_media_t media; + data = *dp++; + srom_media = (tulip_srom_media_t) (data & 0x3F); + for (idx3 = 0; tulip_srom_mediums[idx3].sm_type != TULIP_MEDIA_UNKNOWN; idx3++) { + if (tulip_srom_mediums[idx3].sm_srom_type == srom_media) + break; + } + media = tulip_srom_mediums[idx3].sm_type; + if (media != TULIP_MEDIA_UNKNOWN) { + if (data & TULIP_SROM_21041_EXTENDED) { + mi->mi_type = TULIP_MEDIAINFO_SIA; + sc->tulip_mediums[media] = mi; + mi->mi_sia_connectivity = dp[0] + dp[1] * 256; + mi->mi_sia_tx_rx = dp[2] + dp[3] * 256; + mi->mi_sia_general = dp[4] + dp[5] * 256; + mi++; + } else { + switch (media) { + case TULIP_MEDIA_BNC: { + TULIP_MEDIAINFO_SIA_INIT(sc, mi, 21041, BNC); + mi++; + break; + } + case TULIP_MEDIA_AUI: { + TULIP_MEDIAINFO_SIA_INIT(sc, mi, 21041, AUI); + mi++; + break; + } + case TULIP_MEDIA_10BASET: { + TULIP_MEDIAINFO_SIA_INIT(sc, mi, 21041, 10BASET); + mi++; + break; + } + case TULIP_MEDIA_10BASET_FD: { + TULIP_MEDIAINFO_SIA_INIT(sc, mi, 21041, 10BASET_FD); + mi++; + break; + } + default: { + break; + } + } + } + } + if (data & TULIP_SROM_21041_EXTENDED) + dp += 6; + } +#ifdef notdef + if (blocks == 0) { + TULIP_MEDIAINFO_SIA_INIT(sc, mi, 21041, BNC); mi++; + TULIP_MEDIAINFO_SIA_INIT(sc, mi, 21041, AUI); mi++; + TULIP_MEDIAINFO_SIA_INIT(sc, mi, 21041, 10BASET); mi++; + TULIP_MEDIAINFO_SIA_INIT(sc, mi, 21041, 10BASET_FD); mi++; + } +#endif + } else { + unsigned length, type; + tulip_media_t gp_media = TULIP_MEDIA_UNKNOWN; + if (sc->tulip_features & TULIP_HAVE_GPR) + sc->tulip_gpinit = *dp++; + blocks = *dp++; + for (idx2 = 0; idx2 < blocks; idx2++) { + const u_int8_t *ep; + if ((*dp & 0x80) == 0) { + length = 4; + type = 0; + } else { + length = (*dp++ & 0x7f) - 1; + type = *dp++ & 0x3f; + } + ep = dp + length; + switch (type & 0x3f) { + case 0: { /* 21140[A] GPR block */ + tulip_media_t media; + srom_media = (tulip_srom_media_t)(dp[0] & 0x3f); + for (idx3 = 0; tulip_srom_mediums[idx3].sm_type != TULIP_MEDIA_UNKNOWN; idx3++) { + if (tulip_srom_mediums[idx3].sm_srom_type == srom_media) + break; + } + media = tulip_srom_mediums[idx3].sm_type; + if (media == TULIP_MEDIA_UNKNOWN) + break; + mi->mi_type = TULIP_MEDIAINFO_GPR; + sc->tulip_mediums[media] = mi; + mi->mi_gpdata = dp[1]; + if (media > gp_media && !TULIP_IS_MEDIA_FD(media)) { + sc->tulip_gpdata = mi->mi_gpdata; + gp_media = media; + } + data = dp[2] + dp[3] * 256; + mi->mi_cmdmode = TULIP_SROM_2114X_CMDBITS(data); + if (data & TULIP_SROM_2114X_NOINDICATOR) { + mi->mi_actmask = 0; + } else { +#if 0 + mi->mi_default = (data & TULIP_SROM_2114X_DEFAULT) != 0; +#endif + mi->mi_actmask = TULIP_SROM_2114X_BITPOS(data); + mi->mi_actdata = (data & TULIP_SROM_2114X_POLARITY) ? 0 : mi->mi_actmask; + } + mi++; + break; + } + case 1: { /* 21140[A] MII block */ + const unsigned phyno = *dp++; + mi->mi_type = TULIP_MEDIAINFO_MII; + mi->mi_gpr_length = *dp++; + mi->mi_gpr_offset = dp - sc->tulip_rombuf; + dp += mi->mi_gpr_length; + mi->mi_reset_length = *dp++; + mi->mi_reset_offset = dp - sc->tulip_rombuf; + dp += mi->mi_reset_length; + + /* + * Before we probe for a PHY, use the GPR information + * to select it. If we don't, it may be inaccessible. + */ + TULIP_CSR_WRITE(sc, csr_gp, sc->tulip_gpinit|TULIP_GP_PINSET); + for (idx3 = 0; idx3 < mi->mi_reset_length; idx3++) { + DELAY(10); + TULIP_CSR_WRITE(sc, csr_gp, sc->tulip_rombuf[mi->mi_reset_offset + idx3]); + } + sc->tulip_phyaddr = mi->mi_phyaddr; + for (idx3 = 0; idx3 < mi->mi_gpr_length; idx3++) { + DELAY(10); + TULIP_CSR_WRITE(sc, csr_gp, sc->tulip_rombuf[mi->mi_gpr_offset + idx3]); + } + + /* + * At least write something! + */ + if (mi->mi_reset_length == 0 && mi->mi_gpr_length == 0) + TULIP_CSR_WRITE(sc, csr_gp, 0); + + mi->mi_phyaddr = TULIP_MII_NOPHY; + for (idx3 = 20; idx3 > 0 && mi->mi_phyaddr == TULIP_MII_NOPHY; idx3--) { + DELAY(10000); + mi->mi_phyaddr = tulip_mii_get_phyaddr(sc, phyno); + } + if (mi->mi_phyaddr == TULIP_MII_NOPHY) { +#if defined(TULIP_DEBUG) + printf("%s%d: can't find phy %d\n", + sc->tulip_name, sc->tulip_unit, phyno); +#endif + break; + } + sc->tulip_features |= TULIP_HAVE_MII; + mi->mi_capabilities = dp[0] + dp[1] * 256; dp += 2; + mi->mi_advertisement = dp[0] + dp[1] * 256; dp += 2; + mi->mi_full_duplex = dp[0] + dp[1] * 256; dp += 2; + mi->mi_tx_threshold = dp[0] + dp[1] * 256; dp += 2; + TULIP_MEDIAINFO_ADD_CAPABILITY(sc, mi, 100BASETX_FD); + TULIP_MEDIAINFO_ADD_CAPABILITY(sc, mi, 100BASETX); + TULIP_MEDIAINFO_ADD_CAPABILITY(sc, mi, 100BASET4); + TULIP_MEDIAINFO_ADD_CAPABILITY(sc, mi, 10BASET_FD); + TULIP_MEDIAINFO_ADD_CAPABILITY(sc, mi, 10BASET); + mi->mi_phyid = (tulip_mii_readreg(sc, mi->mi_phyaddr, PHYREG_IDLOW) << 16) | + tulip_mii_readreg(sc, mi->mi_phyaddr, PHYREG_IDHIGH); + mi++; + break; + } + case 2: { /* 2114[23] SIA block */ + tulip_media_t media; + srom_media = (tulip_srom_media_t)(dp[0] & 0x3f); + for (idx3 = 0; tulip_srom_mediums[idx3].sm_type != TULIP_MEDIA_UNKNOWN; idx3++) { + if (tulip_srom_mediums[idx3].sm_srom_type == srom_media) + break; + } + media = tulip_srom_mediums[idx3].sm_type; + if (media == TULIP_MEDIA_UNKNOWN) + break; + mi->mi_type = TULIP_MEDIAINFO_SIA; + sc->tulip_mediums[media] = mi; + if (dp[0] & 0x40) { + mi->mi_sia_connectivity = dp[1] + dp[2] * 256; + mi->mi_sia_tx_rx = dp[3] + dp[4] * 256; + mi->mi_sia_general = dp[5] + dp[6] * 256; + dp += 6; + } else { + switch (media) { + case TULIP_MEDIA_BNC: { + TULIP_MEDIAINFO_SIA_INIT(sc, mi, 21142, BNC); + break; + } + case TULIP_MEDIA_AUI: { + TULIP_MEDIAINFO_SIA_INIT(sc, mi, 21142, AUI); + break; + } + case TULIP_MEDIA_10BASET: { + TULIP_MEDIAINFO_SIA_INIT(sc, mi, 21142, 10BASET); + sc->tulip_intrmask |= TULIP_STS_LINKPASS|TULIP_STS_LINKFAIL; + break; + } + case TULIP_MEDIA_10BASET_FD: { + TULIP_MEDIAINFO_SIA_INIT(sc, mi, 21142, 10BASET_FD); + sc->tulip_intrmask |= TULIP_STS_LINKPASS|TULIP_STS_LINKFAIL; + break; + } + default: { + goto bad_media; + } + } + } + mi->mi_sia_gp_control = (dp[1] + dp[2] * 256) << 16; + mi->mi_sia_gp_data = (dp[3] + dp[4] * 256) << 16; + mi++; + bad_media: + break; + } + case 3: { /* 2114[23] MII PHY block */ + const unsigned phyno = *dp++; + const u_int8_t *dp0; + mi->mi_type = TULIP_MEDIAINFO_MII; + mi->mi_gpr_length = *dp++; + mi->mi_gpr_offset = dp - sc->tulip_rombuf; + dp += 2 * mi->mi_gpr_length; + mi->mi_reset_length = *dp++; + mi->mi_reset_offset = dp - sc->tulip_rombuf; + dp += 2 * mi->mi_reset_length; + + dp0 = &sc->tulip_rombuf[mi->mi_reset_offset]; + for (idx3 = 0; idx3 < mi->mi_reset_length; idx3++, dp0 += 2) { + DELAY(10); + TULIP_CSR_WRITE(sc, csr_sia_general, (dp0[0] + 256 * dp0[1]) << 16); + } + sc->tulip_phyaddr = mi->mi_phyaddr; + dp0 = &sc->tulip_rombuf[mi->mi_gpr_offset]; + for (idx3 = 0; idx3 < mi->mi_gpr_length; idx3++, dp0 += 2) { + DELAY(10); + TULIP_CSR_WRITE(sc, csr_sia_general, (dp0[0] + 256 * dp0[1]) << 16); + } + + if (mi->mi_reset_length == 0 && mi->mi_gpr_length == 0) + TULIP_CSR_WRITE(sc, csr_sia_general, 0); + + mi->mi_phyaddr = TULIP_MII_NOPHY; + for (idx3 = 20; idx3 > 0 && mi->mi_phyaddr == TULIP_MII_NOPHY; idx3--) { + DELAY(10000); + mi->mi_phyaddr = tulip_mii_get_phyaddr(sc, phyno); + } + if (mi->mi_phyaddr == TULIP_MII_NOPHY) { +#if defined(TULIP_DEBUG) + printf("%s%d: can't find phy %d\n", + sc->tulip_name, sc->tulip_unit, phyno); +#endif + break; + } + sc->tulip_features |= TULIP_HAVE_MII; + mi->mi_capabilities = dp[0] + dp[1] * 256; dp += 2; + mi->mi_advertisement = dp[0] + dp[1] * 256; dp += 2; + mi->mi_full_duplex = dp[0] + dp[1] * 256; dp += 2; + mi->mi_tx_threshold = dp[0] + dp[1] * 256; dp += 2; + mi->mi_mii_interrupt = dp[0] + dp[1] * 256; dp += 2; + TULIP_MEDIAINFO_ADD_CAPABILITY(sc, mi, 100BASETX_FD); + TULIP_MEDIAINFO_ADD_CAPABILITY(sc, mi, 100BASETX); + TULIP_MEDIAINFO_ADD_CAPABILITY(sc, mi, 100BASET4); + TULIP_MEDIAINFO_ADD_CAPABILITY(sc, mi, 10BASET_FD); + TULIP_MEDIAINFO_ADD_CAPABILITY(sc, mi, 10BASET); + mi->mi_phyid = (tulip_mii_readreg(sc, mi->mi_phyaddr, PHYREG_IDLOW) << 16) | + tulip_mii_readreg(sc, mi->mi_phyaddr, PHYREG_IDHIGH); + mi++; + break; + } + case 4: { /* 21143 SYM block */ + tulip_media_t media; + srom_media = (tulip_srom_media_t) dp[0]; + for (idx3 = 0; tulip_srom_mediums[idx3].sm_type != TULIP_MEDIA_UNKNOWN; idx3++) { + if (tulip_srom_mediums[idx3].sm_srom_type == srom_media) + break; + } + media = tulip_srom_mediums[idx3].sm_type; + if (media == TULIP_MEDIA_UNKNOWN) + break; + mi->mi_type = TULIP_MEDIAINFO_SYM; + sc->tulip_mediums[media] = mi; + mi->mi_gpcontrol = (dp[1] + dp[2] * 256) << 16; + mi->mi_gpdata = (dp[3] + dp[4] * 256) << 16; + data = dp[5] + dp[6] * 256; + mi->mi_cmdmode = TULIP_SROM_2114X_CMDBITS(data); + if (data & TULIP_SROM_2114X_NOINDICATOR) { + mi->mi_actmask = 0; + } else { + mi->mi_default = (data & TULIP_SROM_2114X_DEFAULT) != 0; + mi->mi_actmask = TULIP_SROM_2114X_BITPOS(data); + mi->mi_actdata = (data & TULIP_SROM_2114X_POLARITY) ? 0 : mi->mi_actmask; + } + if (TULIP_IS_MEDIA_TP(media)) + sc->tulip_intrmask |= TULIP_STS_LINKPASS|TULIP_STS_LINKFAIL; + mi++; + break; + } +#if 0 + case 5: { /* 21143 Reset block */ + mi->mi_type = TULIP_MEDIAINFO_RESET; + mi->mi_reset_length = *dp++; + mi->mi_reset_offset = dp - sc->tulip_rombuf; + dp += 2 * mi->mi_reset_length; + mi++; + break; + } +#endif + default: { + } + } + dp = ep; + } + } + return mi - sc->tulip_mediainfo; +} + +static const struct { + void (*vendor_identify_nic)(tulip_softc_t * const sc); + unsigned char vendor_oui[3]; +} tulip_vendors[] = { + { tulip_identify_dec_nic, { 0x08, 0x00, 0x2B } }, + { tulip_identify_dec_nic, { 0x00, 0x00, 0xF8 } }, + { tulip_identify_smc_nic, { 0x00, 0x00, 0xC0 } }, + { tulip_identify_smc_nic, { 0x00, 0xE0, 0x29 } }, + { tulip_identify_znyx_nic, { 0x00, 0xC0, 0x95 } }, + { tulip_identify_cogent_nic, { 0x00, 0x00, 0x92 } }, + { tulip_identify_asante_nic, { 0x00, 0x00, 0x94 } }, + { tulip_identify_cogent_nic, { 0x00, 0x00, 0xD1 } }, + { tulip_identify_accton_nic, { 0x00, 0x00, 0xE8 } }, + { tulip_identify_compex_nic, { 0x00, 0x80, 0x48 } }, + { NULL } +}; + +/* + * This deals with the vagaries of the address roms and the + * brain-deadness that various vendors commit in using them. + */ +static int +tulip_read_macaddr( + tulip_softc_t * const sc) +{ + unsigned cksum, rom_cksum, idx; + u_int32_t csr; + unsigned char tmpbuf[8]; + static const u_char testpat[] = { 0xFF, 0, 0x55, 0xAA, 0xFF, 0, 0x55, 0xAA }; + + sc->tulip_connidx = TULIP_SROM_LASTCONNIDX; + + if (sc->tulip_chipid == TULIP_21040) { + TULIP_CSR_WRITE(sc, csr_enetrom, 1); + for (idx = 0; idx < sizeof(sc->tulip_rombuf); idx++) { + int cnt = 0; + while (((csr = TULIP_CSR_READ(sc, csr_enetrom)) & 0x80000000L) && cnt < 10000) + cnt++; + sc->tulip_rombuf[idx] = csr & 0xFF; + } + sc->tulip_boardsw = &tulip_21040_boardsw; + } else { + if (sc->tulip_chipid == TULIP_21041) { + /* + * Thankfully all 21041's act the same. + */ + sc->tulip_boardsw = &tulip_21041_boardsw; + } else { + /* + * Assume all 21140 board are compatible with the + * DEC 10/100 evaluation board. Not really valid but + * it's the best we can do until every one switches to + * the new SROM format. + */ + + sc->tulip_boardsw = &tulip_21140_eb_boardsw; + } + tulip_srom_read(sc); + if (tulip_srom_crcok(sc->tulip_rombuf)) { + /* + * SROM CRC is valid therefore it must be in the + * new format. + */ + sc->tulip_features |= TULIP_HAVE_ISVSROM|TULIP_HAVE_OKSROM; + } else if (sc->tulip_rombuf[126] == 0xff && sc->tulip_rombuf[127] == 0xFF) { + /* + * No checksum is present. See if the SROM id checks out; + * the first 18 bytes should be 0 followed by a 1 followed + * by the number of adapters (which we don't deal with yet). + */ + for (idx = 0; idx < 18; idx++) { + if (sc->tulip_rombuf[idx] != 0) + break; + } + if (idx == 18 && sc->tulip_rombuf[18] == 1 && sc->tulip_rombuf[19] != 0) + sc->tulip_features |= TULIP_HAVE_ISVSROM; + } else if (sc->tulip_chipid >= TULIP_21142) { + sc->tulip_features |= TULIP_HAVE_ISVSROM; + sc->tulip_boardsw = &tulip_2114x_isv_boardsw; + } + if ((sc->tulip_features & TULIP_HAVE_ISVSROM) && tulip_srom_decode(sc)) { + if (sc->tulip_chipid != TULIP_21041) + sc->tulip_boardsw = &tulip_2114x_isv_boardsw; + + /* + * If the SROM specifies more than one adapter, tag this as a + * BASE rom. + */ + if (sc->tulip_rombuf[19] > 1) + sc->tulip_features |= TULIP_HAVE_BASEROM; + if (sc->tulip_boardsw == NULL) + return -6; + goto check_oui; + } + } + + + if (bcmp(&sc->tulip_rombuf[0], &sc->tulip_rombuf[16], 8) != 0) { + /* + * Some folks don't use the standard ethernet rom format + * but instead just put the address in the first 6 bytes + * of the rom and let the rest be all 0xffs. (Can we say + * ZNYX?) (well sometimes they put in a checksum so we'll + * start at 8). + */ + for (idx = 8; idx < 32; idx++) { + if (sc->tulip_rombuf[idx] != 0xFF) + return -4; + } + /* + * Make sure the address is not multicast or locally assigned + * that the OUI is not 00-00-00. + */ + if ((sc->tulip_rombuf[0] & 3) != 0) + return -4; + if (sc->tulip_rombuf[0] == 0 && sc->tulip_rombuf[1] == 0 + && sc->tulip_rombuf[2] == 0) + return -4; + bcopy(sc->tulip_rombuf, sc->tulip_enaddr, 6); + sc->tulip_features |= TULIP_HAVE_OKROM; + goto check_oui; + } else { + /* + * A number of makers of multiport boards (ZNYX and Cogent) + * only put on one address ROM on their 21040 boards. So + * if the ROM is all zeros (or all 0xFFs), look at the + * previous configured boards (as long as they are on the same + * PCI bus and the bus number is non-zero) until we find the + * master board with address ROM. We then use its address ROM + * as the base for this board. (we add our relative board + * to the last byte of its address). + */ + for (idx = 0; idx < sizeof(sc->tulip_rombuf); idx++) { + if (sc->tulip_rombuf[idx] != 0 && sc->tulip_rombuf[idx] != 0xFF) + break; + } + if (idx == sizeof(sc->tulip_rombuf)) { + int root_unit; + tulip_softc_t *root_sc = NULL; + for (root_unit = sc->tulip_unit - 1; root_unit >= 0; root_unit--) { + root_sc = tulips[root_unit]; + if (root_sc == NULL || (root_sc->tulip_features & (TULIP_HAVE_OKROM|TULIP_HAVE_SLAVEDROM)) == TULIP_HAVE_OKROM) + break; + root_sc = NULL; + } + if (root_sc != NULL && (root_sc->tulip_features & TULIP_HAVE_BASEROM) + && root_sc->tulip_chipid == sc->tulip_chipid + && root_sc->tulip_pci_busno == sc->tulip_pci_busno) { + sc->tulip_features |= TULIP_HAVE_SLAVEDROM; + sc->tulip_boardsw = root_sc->tulip_boardsw; + strcpy(sc->tulip_boardid, root_sc->tulip_boardid); + if (sc->tulip_boardsw->bd_type == TULIP_21140_ISV) { + bcopy(root_sc->tulip_rombuf, sc->tulip_rombuf, + sizeof(sc->tulip_rombuf)); + if (!tulip_srom_decode(sc)) + return -5; + } else { + bcopy(root_sc->tulip_enaddr, sc->tulip_enaddr, 6); + sc->tulip_enaddr[5] += sc->tulip_unit - root_sc->tulip_unit; + } + /* + * Now for a truly disgusting kludge: all 4 21040s on + * the ZX314 share the same INTA line so the mapping + * setup by the BIOS on the PCI bridge is worthless. + * Rather than reprogramming the value in the config + * register, we will handle this internally. + */ + if (root_sc->tulip_features & TULIP_HAVE_SHAREDINTR) { + sc->tulip_slaves = root_sc->tulip_slaves; + root_sc->tulip_slaves = sc; + sc->tulip_features |= TULIP_HAVE_SLAVEDINTR; + } + return 0; + } + } + } + + /* + * This is the standard DEC address ROM test. + */ + + if (bcmp(&sc->tulip_rombuf[24], testpat, 8) != 0) + return -3; + + tmpbuf[0] = sc->tulip_rombuf[15]; tmpbuf[1] = sc->tulip_rombuf[14]; + tmpbuf[2] = sc->tulip_rombuf[13]; tmpbuf[3] = sc->tulip_rombuf[12]; + tmpbuf[4] = sc->tulip_rombuf[11]; tmpbuf[5] = sc->tulip_rombuf[10]; + tmpbuf[6] = sc->tulip_rombuf[9]; tmpbuf[7] = sc->tulip_rombuf[8]; + if (bcmp(&sc->tulip_rombuf[0], tmpbuf, 8) != 0) + return -2; + + bcopy(sc->tulip_rombuf, sc->tulip_enaddr, 6); + + cksum = *(u_int16_t *) &sc->tulip_enaddr[0]; + cksum *= 2; + if (cksum > 65535) cksum -= 65535; + cksum += *(u_int16_t *) &sc->tulip_enaddr[2]; + if (cksum > 65535) cksum -= 65535; + cksum *= 2; + if (cksum > 65535) cksum -= 65535; + cksum += *(u_int16_t *) &sc->tulip_enaddr[4]; + if (cksum >= 65535) cksum -= 65535; + + rom_cksum = *(u_int16_t *) &sc->tulip_rombuf[6]; + + if (cksum != rom_cksum) + return -1; + + check_oui: + /* + * Check for various boards based on OUI. Did I say braindead? + */ + for (idx = 0; tulip_vendors[idx].vendor_identify_nic != NULL; idx++) { + if (bcmp(sc->tulip_enaddr, tulip_vendors[idx].vendor_oui, 3) == 0) { + (*tulip_vendors[idx].vendor_identify_nic)(sc); + break; + } + } + + sc->tulip_features |= TULIP_HAVE_OKROM; + return 0; +} + +static void +tulip_ifmedia_add( + tulip_softc_t * const sc) +{ + tulip_media_t media; + int medias = 0; + + for (media = TULIP_MEDIA_UNKNOWN; media < TULIP_MEDIA_MAX; media++) { + if (sc->tulip_mediums[media] != NULL) { + ifmedia_add(&sc->tulip_ifmedia, tulip_media_to_ifmedia[media], + 0, 0); + medias++; + } + } + if (medias == 0) { + sc->tulip_features |= TULIP_HAVE_NOMEDIA; + ifmedia_add(&sc->tulip_ifmedia, IFM_ETHER | IFM_NONE, 0, 0); + ifmedia_set(&sc->tulip_ifmedia, IFM_ETHER | IFM_NONE); + } else if (sc->tulip_media == TULIP_MEDIA_UNKNOWN) { + ifmedia_add(&sc->tulip_ifmedia, IFM_ETHER | IFM_AUTO, 0, 0); + ifmedia_set(&sc->tulip_ifmedia, IFM_ETHER | IFM_AUTO); + } else { + ifmedia_set(&sc->tulip_ifmedia, tulip_media_to_ifmedia[sc->tulip_media]); + sc->tulip_flags |= TULIP_PRINTMEDIA; + tulip_linkup(sc, sc->tulip_media); + } +} + +static int +tulip_ifmedia_change( + struct ifnet * const ifp) +{ + tulip_softc_t * const sc = (tulip_softc_t *)ifp->if_softc; + + sc->tulip_flags |= TULIP_NEEDRESET; + sc->tulip_probe_state = TULIP_PROBE_INACTIVE; + sc->tulip_media = TULIP_MEDIA_UNKNOWN; + if (IFM_SUBTYPE(sc->tulip_ifmedia.ifm_media) != IFM_AUTO) { + tulip_media_t media; + for (media = TULIP_MEDIA_UNKNOWN; media < TULIP_MEDIA_MAX; media++) { + if (sc->tulip_mediums[media] != NULL + && sc->tulip_ifmedia.ifm_media == tulip_media_to_ifmedia[media]) { + sc->tulip_flags |= TULIP_PRINTMEDIA; + sc->tulip_flags &= ~TULIP_DIDNWAY; + tulip_linkup(sc, media); + return 0; + } + } + } + sc->tulip_flags &= ~(TULIP_TXPROBE_ACTIVE|TULIP_WANTRXACT); + tulip_reset(sc); + tulip_init(sc); + return 0; +} + +/* + * Media status callback + */ +static void +tulip_ifmedia_status( + struct ifnet * const ifp, + struct ifmediareq *req) +{ + tulip_softc_t *sc = (tulip_softc_t *)ifp->if_softc; + + if (sc->tulip_media == TULIP_MEDIA_UNKNOWN) + return; + + req->ifm_status = IFM_AVALID; + if (sc->tulip_flags & TULIP_LINKUP) + req->ifm_status |= IFM_ACTIVE; + + req->ifm_active = tulip_media_to_ifmedia[sc->tulip_media]; +} + +static void +tulip_addr_filter( + tulip_softc_t * const sc) +{ + struct ifmultiaddr *ifma; + u_char *addrp; + int multicnt; + + sc->tulip_flags &= ~(TULIP_WANTHASHPERFECT|TULIP_WANTHASHONLY|TULIP_ALLMULTI); + sc->tulip_flags |= TULIP_WANTSETUP|TULIP_WANTTXSTART; + sc->tulip_cmdmode &= ~TULIP_CMD_RXRUN; + sc->tulip_intrmask &= ~TULIP_STS_RXSTOPPED; +#if defined(IFF_ALLMULTI) + if (sc->tulip_if.if_flags & IFF_ALLMULTI) + sc->tulip_flags |= TULIP_ALLMULTI ; +#endif + + multicnt = 0; + TAILQ_FOREACH(ifma, &sc->tulip_if.if_multiaddrs, ifma_link) { + + if (ifma->ifma_addr->sa_family == AF_LINK) + multicnt++; + } + + sc->tulip_if.if_start = tulip_ifstart; /* so the setup packet gets queued */ + if (multicnt > 14) { + u_int32_t *sp = sc->tulip_setupdata; + unsigned hash; + /* + * Some early passes of the 21140 have broken implementations of + * hash-perfect mode. When we get too many multicasts for perfect + * filtering with these chips, we need to switch into hash-only + * mode (this is better than all-multicast on network with lots + * of multicast traffic). + */ + if (sc->tulip_features & TULIP_HAVE_BROKEN_HASH) + sc->tulip_flags |= TULIP_WANTHASHONLY; + else + sc->tulip_flags |= TULIP_WANTHASHPERFECT; + /* + * If we have more than 14 multicasts, we have + * go into hash perfect mode (512 bit multicast + * hash and one perfect hardware). + */ + bzero(sc->tulip_setupdata, sizeof(sc->tulip_setupdata)); + + TAILQ_FOREACH(ifma, &sc->tulip_if.if_multiaddrs, ifma_link) { + + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + + hash = tulip_mchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); +#if BYTE_ORDER == BIG_ENDIAN + sp[hash >> 4] |= bswap32(1 << (hash & 0xF)); +#else + sp[hash >> 4] |= 1 << (hash & 0xF); +#endif + } + /* + * No reason to use a hash if we are going to be + * receiving every multicast. + */ + if ((sc->tulip_flags & TULIP_ALLMULTI) == 0) { + hash = tulip_mchash(etherbroadcastaddr); +#if BYTE_ORDER == BIG_ENDIAN + sp[hash >> 4] |= bswap32(1 << (hash & 0xF)); +#else + sp[hash >> 4] |= 1 << (hash & 0xF); +#endif + if (sc->tulip_flags & TULIP_WANTHASHONLY) { + hash = tulip_mchash(sc->tulip_enaddr); +#if BYTE_ORDER == BIG_ENDIAN + sp[hash >> 4] |= bswap32(1 << (hash & 0xF)); +#else + sp[hash >> 4] |= 1 << (hash & 0xF); +#endif + } else { +#if BYTE_ORDER == BIG_ENDIAN + sp[39] = ((u_int16_t *) sc->tulip_enaddr)[0] << 16; + sp[40] = ((u_int16_t *) sc->tulip_enaddr)[1] << 16; + sp[41] = ((u_int16_t *) sc->tulip_enaddr)[2] << 16; +#else + sp[39] = ((u_int16_t *) sc->tulip_enaddr)[0]; + sp[40] = ((u_int16_t *) sc->tulip_enaddr)[1]; + sp[41] = ((u_int16_t *) sc->tulip_enaddr)[2]; +#endif + } + } + } + if ((sc->tulip_flags & (TULIP_WANTHASHPERFECT|TULIP_WANTHASHONLY)) == 0) { + u_int32_t *sp = sc->tulip_setupdata; + int idx = 0; + if ((sc->tulip_flags & TULIP_ALLMULTI) == 0) { + /* + * Else can get perfect filtering for 16 addresses. + */ + TAILQ_FOREACH(ifma, &sc->tulip_if.if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + addrp = LLADDR((struct sockaddr_dl *)ifma->ifma_addr); +#if BYTE_ORDER == BIG_ENDIAN + *sp++ = ((u_int16_t *) addrp)[0] << 16; + *sp++ = ((u_int16_t *) addrp)[1] << 16; + *sp++ = ((u_int16_t *) addrp)[2] << 16; +#else + *sp++ = ((u_int16_t *) addrp)[0]; + *sp++ = ((u_int16_t *) addrp)[1]; + *sp++ = ((u_int16_t *) addrp)[2]; +#endif + idx++; + } + /* + * Add the broadcast address. + */ + idx++; +#if BYTE_ORDER == BIG_ENDIAN + *sp++ = 0xFFFF << 16; + *sp++ = 0xFFFF << 16; + *sp++ = 0xFFFF << 16; +#else + *sp++ = 0xFFFF; + *sp++ = 0xFFFF; + *sp++ = 0xFFFF; +#endif + } + /* + * Pad the rest with our hardware address + */ + for (; idx < 16; idx++) { +#if BYTE_ORDER == BIG_ENDIAN + *sp++ = ((u_int16_t *) sc->tulip_enaddr)[0] << 16; + *sp++ = ((u_int16_t *) sc->tulip_enaddr)[1] << 16; + *sp++ = ((u_int16_t *) sc->tulip_enaddr)[2] << 16; +#else + *sp++ = ((u_int16_t *) sc->tulip_enaddr)[0]; + *sp++ = ((u_int16_t *) sc->tulip_enaddr)[1]; + *sp++ = ((u_int16_t *) sc->tulip_enaddr)[2]; +#endif + } + } +#if defined(IFF_ALLMULTI) + if (sc->tulip_flags & TULIP_ALLMULTI) + sc->tulip_if.if_flags |= IFF_ALLMULTI; +#endif +} + +static void +tulip_reset( + tulip_softc_t * const sc) +{ + tulip_ringinfo_t *ri; + tulip_desc_t *di; + u_int32_t inreset = (sc->tulip_flags & TULIP_INRESET); + + /* + * Brilliant. Simply brilliant. When switching modes/speeds + * on a 2114*, you need to set the appriopriate MII/PCS/SCL/PS + * bits in CSR6 and then do a software reset to get the 21140 + * to properly reset its internal pathways to the right places. + * Grrrr. + */ + if ((sc->tulip_flags & TULIP_DEVICEPROBE) == 0 + && sc->tulip_boardsw->bd_media_preset != NULL) + (*sc->tulip_boardsw->bd_media_preset)(sc); + + TULIP_CSR_WRITE(sc, csr_busmode, TULIP_BUSMODE_SWRESET); + DELAY(10); /* Wait 10 microseconds (actually 50 PCI cycles but at + 33MHz that comes to two microseconds but wait a + bit longer anyways) */ + + if (!inreset) { + sc->tulip_flags |= TULIP_INRESET; + sc->tulip_flags &= ~(TULIP_NEEDRESET|TULIP_RXBUFSLOW); + sc->tulip_if.if_flags &= ~IFF_OACTIVE; + sc->tulip_if.if_start = tulip_ifstart; + } + +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NOTX) + TULIP_CSR_WRITE(sc, csr_txlist, sc->tulip_txdescmap->dm_segs[0].ds_addr); +#else + TULIP_CSR_WRITE(sc, csr_txlist, TULIP_KVATOPHYS(sc, &sc->tulip_txinfo.ri_first[0])); +#endif +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NORX) + TULIP_CSR_WRITE(sc, csr_rxlist, sc->tulip_rxdescmap->dm_segs[0].ds_addr); +#else + TULIP_CSR_WRITE(sc, csr_rxlist, TULIP_KVATOPHYS(sc, &sc->tulip_rxinfo.ri_first[0])); +#endif + TULIP_CSR_WRITE(sc, csr_busmode, + (1 << (3 /*pci_max_burst_len*/ + 8)) + |TULIP_BUSMODE_CACHE_ALIGN8 + |TULIP_BUSMODE_READMULTIPLE + |(BYTE_ORDER != LITTLE_ENDIAN ? + TULIP_BUSMODE_DESC_BIGENDIAN : 0)); + + sc->tulip_txtimer = 0; + sc->tulip_txq.ifq_maxlen = TULIP_TXDESCS; + /* + * Free all the mbufs that were on the transmit ring. + */ + for (;;) { +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NOTX) + bus_dmamap_t map; +#endif + struct mbuf *m; + _IF_DEQUEUE(&sc->tulip_txq, m); + if (m == NULL) + break; +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NOTX) + map = M_GETCTX(m, bus_dmamap_t); + bus_dmamap_unload(sc->tulip_dmatag, map); + sc->tulip_txmaps[sc->tulip_txmaps_free++] = map; +#endif + m_freem(m); + } + + ri = &sc->tulip_txinfo; + ri->ri_nextin = ri->ri_nextout = ri->ri_first; + ri->ri_free = ri->ri_max; + for (di = ri->ri_first; di < ri->ri_last; di++) + di->d_status = 0; +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NOTX) + bus_dmamap_sync(sc->tulip_dmatag, sc->tulip_txdescmap, + 0, sc->tulip_txdescmap->dm_mapsize, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); +#endif + + /* + * We need to collect all the mbufs were on the + * receive ring before we reinit it either to put + * them back on or to know if we have to allocate + * more. + */ + ri = &sc->tulip_rxinfo; + ri->ri_nextin = ri->ri_nextout = ri->ri_first; + ri->ri_free = ri->ri_max; + for (di = ri->ri_first; di < ri->ri_last; di++) { + di->d_status = 0; + di->d_length1 = 0; di->d_addr1 = 0; + di->d_length2 = 0; di->d_addr2 = 0; + } +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NORX) + bus_dmamap_sync(sc->tulip_dmatag, sc->tulip_rxdescmap, + 0, sc->tulip_rxdescmap->dm_mapsize, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); +#endif + for (;;) { +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NORX) + bus_dmamap_t map; +#endif + struct mbuf *m; + _IF_DEQUEUE(&sc->tulip_rxq, m); + if (m == NULL) + break; +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NORX) + map = M_GETCTX(m, bus_dmamap_t); + bus_dmamap_unload(sc->tulip_dmatag, map); + sc->tulip_rxmaps[sc->tulip_rxmaps_free++] = map; +#endif + m_freem(m); + } + + /* + * If tulip_reset is being called recurisvely, exit quickly knowing + * that when the outer tulip_reset returns all the right stuff will + * have happened. + */ + if (inreset) + return; + + sc->tulip_intrmask |= TULIP_STS_NORMALINTR|TULIP_STS_RXINTR|TULIP_STS_TXINTR + |TULIP_STS_ABNRMLINTR|TULIP_STS_SYSERROR|TULIP_STS_TXSTOPPED + |TULIP_STS_TXUNDERFLOW|TULIP_STS_TXBABBLE + |TULIP_STS_RXSTOPPED; + + if ((sc->tulip_flags & TULIP_DEVICEPROBE) == 0) + (*sc->tulip_boardsw->bd_media_select)(sc); +#if defined(TULIP_DEBUG) + if ((sc->tulip_flags & TULIP_NEEDRESET) == TULIP_NEEDRESET) + printf("%s%d: tulip_reset: additional reset needed?!?\n", + sc->tulip_name, sc->tulip_unit); +#endif + tulip_media_print(sc); + if (sc->tulip_features & TULIP_HAVE_DUALSENSE) + TULIP_CSR_WRITE(sc, csr_sia_status, TULIP_CSR_READ(sc, csr_sia_status)); + + sc->tulip_flags &= ~(TULIP_DOINGSETUP|TULIP_WANTSETUP|TULIP_INRESET + |TULIP_RXACT); + tulip_addr_filter(sc); +} + + +static void +tulip_ifinit( + void * sc) +{ + tulip_init((tulip_softc_t *)sc); +} + +static void +tulip_init( + tulip_softc_t * const sc) +{ + if (sc->tulip_if.if_flags & IFF_UP) { + if ((sc->tulip_if.if_flags & IFF_RUNNING) == 0) { + /* initialize the media */ + tulip_reset(sc); + } + sc->tulip_if.if_flags |= IFF_RUNNING; + if (sc->tulip_if.if_flags & IFF_PROMISC) { + sc->tulip_flags |= TULIP_PROMISC; + sc->tulip_cmdmode |= TULIP_CMD_PROMISCUOUS; + sc->tulip_intrmask |= TULIP_STS_TXINTR; + } else { + sc->tulip_flags &= ~TULIP_PROMISC; + sc->tulip_cmdmode &= ~TULIP_CMD_PROMISCUOUS; + if (sc->tulip_flags & TULIP_ALLMULTI) { + sc->tulip_cmdmode |= TULIP_CMD_ALLMULTI; + } else { + sc->tulip_cmdmode &= ~TULIP_CMD_ALLMULTI; + } + } + sc->tulip_cmdmode |= TULIP_CMD_TXRUN; + if ((sc->tulip_flags & (TULIP_TXPROBE_ACTIVE|TULIP_WANTSETUP)) == 0) { + tulip_rx_intr(sc); + sc->tulip_cmdmode |= TULIP_CMD_RXRUN; + sc->tulip_intrmask |= TULIP_STS_RXSTOPPED; + } else { + sc->tulip_if.if_flags |= IFF_OACTIVE; + sc->tulip_cmdmode &= ~TULIP_CMD_RXRUN; + sc->tulip_intrmask &= ~TULIP_STS_RXSTOPPED; + } + TULIP_CSR_WRITE(sc, csr_intr, sc->tulip_intrmask); + TULIP_CSR_WRITE(sc, csr_command, sc->tulip_cmdmode); + if ((sc->tulip_flags & (TULIP_WANTSETUP|TULIP_TXPROBE_ACTIVE)) == TULIP_WANTSETUP) + tulip_txput_setup(sc); + } else { + sc->tulip_if.if_flags &= ~IFF_RUNNING; + tulip_reset(sc); + } +} + +static void +tulip_rx_intr( + tulip_softc_t * const sc) +{ + TULIP_PERFSTART(rxintr) + tulip_ringinfo_t * const ri = &sc->tulip_rxinfo; + struct ifnet * const ifp = &sc->tulip_if; + int fillok = 1; +#if defined(TULIP_DEBUG) + int cnt = 0; +#endif + + for (;;) { + TULIP_PERFSTART(rxget) + struct ether_header eh; + tulip_desc_t *eop = ri->ri_nextin; + int total_len = 0, last_offset = 0; + struct mbuf *ms = NULL, *me = NULL; + int accept = 0; +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NORX) + bus_dmamap_t map; + int error; +#endif + + if (fillok && sc->tulip_rxq.ifq_len < TULIP_RXQ_TARGET) + goto queue_mbuf; + +#if defined(TULIP_DEBUG) + if (cnt == ri->ri_max) + break; +#endif + /* + * If the TULIP has no descriptors, there can't be any receive + * descriptors to process. + */ + if (eop == ri->ri_nextout) + break; + + /* + * 90% of the packets will fit in one descriptor. So we optimize + * for that case. + */ + TULIP_RXDESC_POSTSYNC(sc, eop, sizeof(*eop)); + if ((((volatile tulip_desc_t *) eop)->d_status & (TULIP_DSTS_OWNER|TULIP_DSTS_RxFIRSTDESC|TULIP_DSTS_RxLASTDESC)) == (TULIP_DSTS_RxFIRSTDESC|TULIP_DSTS_RxLASTDESC)) { + _IF_DEQUEUE(&sc->tulip_rxq, ms); + me = ms; + } else { + /* + * If still owned by the TULIP, don't touch it. + */ + if (((volatile tulip_desc_t *) eop)->d_status & TULIP_DSTS_OWNER) + break; + + /* + * It is possible (though improbable unless the BIG_PACKET support + * is enabled or MCLBYTES < 1518) for a received packet to cross + * more than one receive descriptor. + */ + while ((((volatile tulip_desc_t *) eop)->d_status & TULIP_DSTS_RxLASTDESC) == 0) { + if (++eop == ri->ri_last) + eop = ri->ri_first; + TULIP_RXDESC_POSTSYNC(sc, eop, sizeof(*eop)); + if (eop == ri->ri_nextout || ((((volatile tulip_desc_t *) eop)->d_status & TULIP_DSTS_OWNER))) { +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_rxintrs++; + sc->tulip_dbg.dbg_rxpktsperintr[cnt]++; +#endif + TULIP_PERFEND(rxget); + TULIP_PERFEND(rxintr); + return; + } + total_len++; + } + + /* + * Dequeue the first buffer for the start of the packet. Hopefully + * this will be the only one we need to dequeue. However, if the + * packet consumed multiple descriptors, then we need to dequeue + * those buffers and chain to the starting mbuf. All buffers but + * the last buffer have the same length so we can set that now. + * (we add to last_offset instead of multiplying since we normally + * won't go into the loop and thereby saving a ourselves from + * doing a multiplication by 0 in the normal case). + */ + _IF_DEQUEUE(&sc->tulip_rxq, ms); + for (me = ms; total_len > 0; total_len--) { +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NORX) + map = M_GETCTX(me, bus_dmamap_t); + TULIP_RXMAP_POSTSYNC(sc, map); + bus_dmamap_unload(sc->tulip_dmatag, map); + sc->tulip_rxmaps[sc->tulip_rxmaps_free++] = map; +#if defined(DIAGNOSTIC) + M_SETCTX(me, NULL); +#endif +#endif /* TULIP_BUS_DMA */ + me->m_len = TULIP_RX_BUFLEN; + last_offset += TULIP_RX_BUFLEN; + _IF_DEQUEUE(&sc->tulip_rxq, me->m_next); + me = me->m_next; + } + } + + /* + * Now get the size of received packet (minus the CRC). + */ + total_len = ((eop->d_status >> 16) & 0x7FFF) - 4; + if ((sc->tulip_flags & TULIP_RXIGNORE) == 0 + && ((eop->d_status & TULIP_DSTS_ERRSUM) == 0 +#ifdef BIG_PACKET + || (total_len <= sc->tulip_if.if_mtu + sizeof(struct ether_header) && + (eop->d_status & (TULIP_DSTS_RxBADLENGTH|TULIP_DSTS_RxRUNT| + TULIP_DSTS_RxCOLLSEEN|TULIP_DSTS_RxBADCRC| + TULIP_DSTS_RxOVERFLOW)) == 0) +#endif + )) { + me->m_len = total_len - last_offset; + +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NORX) + map = M_GETCTX(me, bus_dmamap_t); + bus_dmamap_sync(sc->tulip_dmatag, map, 0, me->m_len, + BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->tulip_dmatag, map); + sc->tulip_rxmaps[sc->tulip_rxmaps_free++] = map; +#if defined(DIAGNOSTIC) + M_SETCTX(me, NULL); +#endif +#endif /* TULIP_BUS_DMA */ + + eh = *mtod(ms, struct ether_header *); +#ifndef __FreeBSD__ + if (sc->tulip_if.if_bpf != NULL) { + if (me == ms) + bpf_tap(&sc->tulip_if, mtod(ms, caddr_t), total_len); + else + bpf_mtap(&sc->tulip_if, ms); + } +#endif + sc->tulip_flags |= TULIP_RXACT; + accept = 1; + } else { + ifp->if_ierrors++; + if (eop->d_status & (TULIP_DSTS_RxBADLENGTH|TULIP_DSTS_RxOVERFLOW|TULIP_DSTS_RxWATCHDOG)) { + sc->tulip_dot3stats.dot3StatsInternalMacReceiveErrors++; + } else { +#if defined(TULIP_VERBOSE) + const char *error = NULL; +#endif + if (eop->d_status & TULIP_DSTS_RxTOOLONG) { + sc->tulip_dot3stats.dot3StatsFrameTooLongs++; +#if defined(TULIP_VERBOSE) + error = "frame too long"; +#endif + } + if (eop->d_status & TULIP_DSTS_RxBADCRC) { + if (eop->d_status & TULIP_DSTS_RxDRBBLBIT) { + sc->tulip_dot3stats.dot3StatsAlignmentErrors++; +#if defined(TULIP_VERBOSE) + error = "alignment error"; +#endif + } else { + sc->tulip_dot3stats.dot3StatsFCSErrors++; +#if defined(TULIP_VERBOSE) + error = "bad crc"; +#endif + } + } +#if defined(TULIP_VERBOSE) + if (error != NULL && (sc->tulip_flags & TULIP_NOMESSAGES) == 0) { + printf("%s%d: receive: %6D: %s\n", + sc->tulip_name, sc->tulip_unit, + mtod(ms, u_char *) + 6, ":", + error); + sc->tulip_flags |= TULIP_NOMESSAGES; + } +#endif + } + +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NORX) + map = M_GETCTX(me, bus_dmamap_t); + bus_dmamap_unload(sc->tulip_dmatag, map); + sc->tulip_rxmaps[sc->tulip_rxmaps_free++] = map; +#if defined(DIAGNOSTIC) + M_SETCTX(me, NULL); +#endif +#endif /* TULIP_BUS_DMA */ + } +#if defined(TULIP_DEBUG) + cnt++; +#endif + ifp->if_ipackets++; + if (++eop == ri->ri_last) + eop = ri->ri_first; + ri->ri_nextin = eop; + queue_mbuf: + /* + * Either we are priming the TULIP with mbufs (m == NULL) + * or we are about to accept an mbuf for the upper layers + * so we need to allocate an mbuf to replace it. If we + * can't replace it, send up it anyways. This may cause + * us to drop packets in the future but that's better than + * being caught in livelock. + * + * Note that if this packet crossed multiple descriptors + * we don't even try to reallocate all the mbufs here. + * Instead we rely on the test of the beginning of + * the loop to refill for the extra consumed mbufs. + */ + if (accept || ms == NULL) { + struct mbuf *m0; + MGETHDR(m0, M_DONTWAIT, MT_DATA); + if (m0 != NULL) { +#if defined(TULIP_COPY_RXDATA) + if (!accept || total_len >= (MHLEN - 2)) { +#endif + MCLGET(m0, M_DONTWAIT); + if ((m0->m_flags & M_EXT) == 0) { + m_freem(m0); + m0 = NULL; + } +#if defined(TULIP_COPY_RXDATA) + } +#endif + } + if (accept +#if defined(TULIP_COPY_RXDATA) + && m0 != NULL +#endif + ) { +#if !defined(TULIP_COPY_RXDATA) + ms->m_pkthdr.len = total_len; + ms->m_pkthdr.rcvif = ifp; + m_adj(ms, sizeof(struct ether_header)); + ether_input(ifp, &eh, ms); +#else +#ifdef BIG_PACKET +#error BIG_PACKET is incompatible with TULIP_COPY_RXDATA +#endif + m0->m_data += 2; /* align data after header */ + m_copydata(ms, 0, total_len, mtod(m0, caddr_t)); + m0->m_len = m0->m_pkthdr.len = total_len; + m0->m_pkthdr.rcvif = ifp; + m_adj(m0, sizeof(struct ether_header)); + ether_input(ifp, &eh, m0); + m0 = ms; +#endif /* ! TULIP_COPY_RXDATA */ + } + ms = m0; + } + if (ms == NULL) { + /* + * Couldn't allocate a new buffer. Don't bother + * trying to replenish the receive queue. + */ + fillok = 0; + sc->tulip_flags |= TULIP_RXBUFSLOW; +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_rxlowbufs++; +#endif + TULIP_PERFEND(rxget); + continue; + } + /* + * Now give the buffer(s) to the TULIP and save in our + * receive queue. + */ + do { + tulip_desc_t * const nextout = ri->ri_nextout; +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NORX) + if (sc->tulip_rxmaps_free > 0) { + map = sc->tulip_rxmaps[--sc->tulip_rxmaps_free]; + } else { + m_freem(ms); + sc->tulip_flags |= TULIP_RXBUFSLOW; +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_rxlowbufs++; +#endif + break; + } + M_SETCTX(ms, map); + error = bus_dmamap_load(sc->tulip_dmatag, map, mtod(ms, void *), + TULIP_RX_BUFLEN, NULL, BUS_DMA_NOWAIT); + if (error) { + printf("%s%d: unable to load rx map, " + "error = %d\n", sc->tulip_name, sc->tulip_unit, error); + panic("tulip_rx_intr"); /* XXX */ + } + nextout->d_addr1 = map->dm_segs[0].ds_addr; + nextout->d_length1 = map->dm_segs[0].ds_len; + if (map->dm_nsegs == 2) { + nextout->d_addr2 = map->dm_segs[1].ds_addr; + nextout->d_length2 = map->dm_segs[1].ds_len; + } else { + nextout->d_addr2 = 0; + nextout->d_length2 = 0; + } + TULIP_RXDESC_POSTSYNC(sc, nextout, sizeof(*nextout)); +#else /* TULIP_BUS_DMA */ + nextout->d_addr1 = TULIP_KVATOPHYS(sc, mtod(ms, caddr_t)); + nextout->d_length1 = TULIP_RX_BUFLEN; +#endif /* TULIP_BUS_DMA */ + nextout->d_status = TULIP_DSTS_OWNER; + TULIP_RXDESC_POSTSYNC(sc, nextout, sizeof(u_int32_t)); + if (++ri->ri_nextout == ri->ri_last) + ri->ri_nextout = ri->ri_first; + me = ms->m_next; + ms->m_next = NULL; + _IF_ENQUEUE(&sc->tulip_rxq, ms); + } while ((ms = me) != NULL); + + if (sc->tulip_rxq.ifq_len >= TULIP_RXQ_TARGET) + sc->tulip_flags &= ~TULIP_RXBUFSLOW; + TULIP_PERFEND(rxget); + } + +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_rxintrs++; + sc->tulip_dbg.dbg_rxpktsperintr[cnt]++; +#endif + TULIP_PERFEND(rxintr); +} + +static int +tulip_tx_intr( + tulip_softc_t * const sc) +{ + TULIP_PERFSTART(txintr) + tulip_ringinfo_t * const ri = &sc->tulip_txinfo; + struct mbuf *m; + int xmits = 0; + int descs = 0; + + while (ri->ri_free < ri->ri_max) { + u_int32_t d_flag; + + TULIP_TXDESC_POSTSYNC(sc, ri->ri_nextin, sizeof(*ri->ri_nextin)); + if (((volatile tulip_desc_t *) ri->ri_nextin)->d_status & TULIP_DSTS_OWNER) + break; + + ri->ri_free++; + descs++; + d_flag = ri->ri_nextin->d_flag; + if (d_flag & TULIP_DFLAG_TxLASTSEG) { + if (d_flag & TULIP_DFLAG_TxSETUPPKT) { + /* + * We've just finished processing a setup packet. + * Mark that we finished it. If there's not + * another pending, startup the TULIP receiver. + * Make sure we ack the RXSTOPPED so we won't get + * an abormal interrupt indication. + */ + TULIP_TXMAP_POSTSYNC(sc, sc->tulip_setupmap); + sc->tulip_flags &= ~(TULIP_DOINGSETUP|TULIP_HASHONLY); + if (ri->ri_nextin->d_flag & TULIP_DFLAG_TxINVRSFILT) + sc->tulip_flags |= TULIP_HASHONLY; + if ((sc->tulip_flags & (TULIP_WANTSETUP|TULIP_TXPROBE_ACTIVE)) == 0) { + tulip_rx_intr(sc); + sc->tulip_cmdmode |= TULIP_CMD_RXRUN; + sc->tulip_intrmask |= TULIP_STS_RXSTOPPED; + TULIP_CSR_WRITE(sc, csr_status, TULIP_STS_RXSTOPPED); + TULIP_CSR_WRITE(sc, csr_intr, sc->tulip_intrmask); + TULIP_CSR_WRITE(sc, csr_command, sc->tulip_cmdmode); + } + } else { + const u_int32_t d_status = ri->ri_nextin->d_status; + _IF_DEQUEUE(&sc->tulip_txq, m); + if (m != NULL) { +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NOTX) + bus_dmamap_t map = M_GETCTX(m, bus_dmamap_t); + TULIP_TXMAP_POSTSYNC(sc, map); + sc->tulip_txmaps[sc->tulip_txmaps_free++] = map; +#endif /* TULIP_BUS_DMA */ + m_freem(m); +#if defined(TULIP_DEBUG) + } else { + printf("%s%d: tx_intr: failed to dequeue mbuf?!?\n", + sc->tulip_name, sc->tulip_unit); +#endif + } + if (sc->tulip_flags & TULIP_TXPROBE_ACTIVE) { + tulip_mediapoll_event_t event = TULIP_MEDIAPOLL_TXPROBE_OK; + if (d_status & (TULIP_DSTS_TxNOCARR|TULIP_DSTS_TxEXCCOLL)) { +#if defined(TULIP_DEBUG) + if (d_status & TULIP_DSTS_TxNOCARR) + sc->tulip_dbg.dbg_txprobe_nocarr++; + if (d_status & TULIP_DSTS_TxEXCCOLL) + sc->tulip_dbg.dbg_txprobe_exccoll++; +#endif + event = TULIP_MEDIAPOLL_TXPROBE_FAILED; + } + (*sc->tulip_boardsw->bd_media_poll)(sc, event); + /* + * Escape from the loop before media poll has reset the TULIP! + */ + break; + } else { + xmits++; + if (d_status & TULIP_DSTS_ERRSUM) { + sc->tulip_if.if_oerrors++; + if (d_status & TULIP_DSTS_TxEXCCOLL) + sc->tulip_dot3stats.dot3StatsExcessiveCollisions++; + if (d_status & TULIP_DSTS_TxLATECOLL) + sc->tulip_dot3stats.dot3StatsLateCollisions++; + if (d_status & (TULIP_DSTS_TxNOCARR|TULIP_DSTS_TxCARRLOSS)) + sc->tulip_dot3stats.dot3StatsCarrierSenseErrors++; + if (d_status & (TULIP_DSTS_TxUNDERFLOW|TULIP_DSTS_TxBABBLE)) + sc->tulip_dot3stats.dot3StatsInternalMacTransmitErrors++; + if (d_status & TULIP_DSTS_TxUNDERFLOW) + sc->tulip_dot3stats.dot3StatsInternalTransmitUnderflows++; + if (d_status & TULIP_DSTS_TxBABBLE) + sc->tulip_dot3stats.dot3StatsInternalTransmitBabbles++; + } else { + u_int32_t collisions = + (d_status & TULIP_DSTS_TxCOLLMASK) + >> TULIP_DSTS_V_TxCOLLCNT; + sc->tulip_if.if_collisions += collisions; + if (collisions == 1) + sc->tulip_dot3stats.dot3StatsSingleCollisionFrames++; + else if (collisions > 1) + sc->tulip_dot3stats.dot3StatsMultipleCollisionFrames++; + else if (d_status & TULIP_DSTS_TxDEFERRED) + sc->tulip_dot3stats.dot3StatsDeferredTransmissions++; + /* + * SQE is only valid for 10baseT/BNC/AUI when not + * running in full-duplex. In order to speed up the + * test, the corresponding bit in tulip_flags needs to + * set as well to get us to count SQE Test Errors. + */ + if (d_status & TULIP_DSTS_TxNOHRTBT & sc->tulip_flags) + sc->tulip_dot3stats.dot3StatsSQETestErrors++; + } + } + } + } + + if (++ri->ri_nextin == ri->ri_last) + ri->ri_nextin = ri->ri_first; + + if ((sc->tulip_flags & TULIP_TXPROBE_ACTIVE) == 0) + sc->tulip_if.if_flags &= ~IFF_OACTIVE; + } + /* + * If nothing left to transmit, disable the timer. + * Else if progress, reset the timer back to 2 ticks. + */ + if (ri->ri_free == ri->ri_max || (sc->tulip_flags & TULIP_TXPROBE_ACTIVE)) + sc->tulip_txtimer = 0; + else if (xmits > 0) + sc->tulip_txtimer = TULIP_TXTIMER; + sc->tulip_if.if_opackets += xmits; + TULIP_PERFEND(txintr); + return descs; +} + +static void +tulip_print_abnormal_interrupt( + tulip_softc_t * const sc, + u_int32_t csr) +{ + const char * const *msgp = tulip_status_bits; + const char *sep; + u_int32_t mask; + const char thrsh[] = "72|128\0\0\0" "96|256\0\0\0" "128|512\0\0" "160|1024"; + + csr &= (1 << (sizeof(tulip_status_bits)/sizeof(tulip_status_bits[0]))) - 1; + printf("%s%d: abnormal interrupt:", sc->tulip_name, sc->tulip_unit); + for (sep = " ", mask = 1; mask <= csr; mask <<= 1, msgp++) { + if ((csr & mask) && *msgp != NULL) { + printf("%s%s", sep, *msgp); + if (mask == TULIP_STS_TXUNDERFLOW && (sc->tulip_flags & TULIP_NEWTXTHRESH)) { + sc->tulip_flags &= ~TULIP_NEWTXTHRESH; + if (sc->tulip_cmdmode & TULIP_CMD_STOREFWD) { + printf(" (switching to store-and-forward mode)"); + } else { + printf(" (raising TX threshold to %s)", + &thrsh[9 * ((sc->tulip_cmdmode & TULIP_CMD_THRESHOLDCTL) >> 14)]); + } + } + sep = ", "; + } + } + printf("\n"); +} + +static void +tulip_intr_handler( + tulip_softc_t * const sc, + int *progress_p) +{ + TULIP_PERFSTART(intr) + u_int32_t csr; + + while ((csr = TULIP_CSR_READ(sc, csr_status)) & sc->tulip_intrmask) { + *progress_p = 1; + TULIP_CSR_WRITE(sc, csr_status, csr); + + if (csr & TULIP_STS_SYSERROR) { + sc->tulip_last_system_error = (csr & TULIP_STS_ERRORMASK) >> TULIP_STS_ERR_SHIFT; + if (sc->tulip_flags & TULIP_NOMESSAGES) { + sc->tulip_flags |= TULIP_SYSTEMERROR; + } else { + printf("%s%d: system error: %s\n", + sc->tulip_name, sc->tulip_unit, + tulip_system_errors[sc->tulip_last_system_error]); + } + sc->tulip_flags |= TULIP_NEEDRESET; + sc->tulip_system_errors++; + break; + } + if (csr & (TULIP_STS_LINKPASS|TULIP_STS_LINKFAIL) & sc->tulip_intrmask) { +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_link_intrs++; +#endif + if (sc->tulip_boardsw->bd_media_poll != NULL) { + (*sc->tulip_boardsw->bd_media_poll)(sc, csr & TULIP_STS_LINKFAIL + ? TULIP_MEDIAPOLL_LINKFAIL + : TULIP_MEDIAPOLL_LINKPASS); + csr &= ~TULIP_STS_ABNRMLINTR; + } + tulip_media_print(sc); + } + if (csr & (TULIP_STS_RXINTR|TULIP_STS_RXNOBUF)) { + u_int32_t misses = TULIP_CSR_READ(sc, csr_missed_frames); + if (csr & TULIP_STS_RXNOBUF) + sc->tulip_dot3stats.dot3StatsMissedFrames += misses & 0xFFFF; + /* + * Pass 2.[012] of the 21140A-A[CDE] may hang and/or corrupt data + * on receive overflows. + */ + if ((misses & 0x0FFE0000) && (sc->tulip_features & TULIP_HAVE_RXBADOVRFLW)) { + sc->tulip_dot3stats.dot3StatsInternalMacReceiveErrors++; + /* + * Stop the receiver process and spin until it's stopped. + * Tell rx_intr to drop the packets it dequeues. + */ + TULIP_CSR_WRITE(sc, csr_command, sc->tulip_cmdmode & ~TULIP_CMD_RXRUN); + while ((TULIP_CSR_READ(sc, csr_status) & TULIP_STS_RXSTOPPED) == 0) + ; + TULIP_CSR_WRITE(sc, csr_status, TULIP_STS_RXSTOPPED); + sc->tulip_flags |= TULIP_RXIGNORE; + } + tulip_rx_intr(sc); + if (sc->tulip_flags & TULIP_RXIGNORE) { + /* + * Restart the receiver. + */ + sc->tulip_flags &= ~TULIP_RXIGNORE; + TULIP_CSR_WRITE(sc, csr_command, sc->tulip_cmdmode); + } + } + if (csr & TULIP_STS_ABNRMLINTR) { + u_int32_t tmp = csr & sc->tulip_intrmask + & ~(TULIP_STS_NORMALINTR|TULIP_STS_ABNRMLINTR); + if (csr & TULIP_STS_TXUNDERFLOW) { + if ((sc->tulip_cmdmode & TULIP_CMD_THRESHOLDCTL) != TULIP_CMD_THRSHLD160) { + sc->tulip_cmdmode += TULIP_CMD_THRSHLD96; + sc->tulip_flags |= TULIP_NEWTXTHRESH; + } else if (sc->tulip_features & TULIP_HAVE_STOREFWD) { + sc->tulip_cmdmode |= TULIP_CMD_STOREFWD; + sc->tulip_flags |= TULIP_NEWTXTHRESH; + } + } + if (sc->tulip_flags & TULIP_NOMESSAGES) { + sc->tulip_statusbits |= tmp; + } else { + tulip_print_abnormal_interrupt(sc, tmp); + sc->tulip_flags |= TULIP_NOMESSAGES; + } + TULIP_CSR_WRITE(sc, csr_command, sc->tulip_cmdmode); + } + if (sc->tulip_flags & (TULIP_WANTTXSTART|TULIP_TXPROBE_ACTIVE|TULIP_DOINGSETUP|TULIP_PROMISC)) { + tulip_tx_intr(sc); + if ((sc->tulip_flags & TULIP_TXPROBE_ACTIVE) == 0) + tulip_ifstart(&sc->tulip_if); + } + } + if (sc->tulip_flags & TULIP_NEEDRESET) { + tulip_reset(sc); + tulip_init(sc); + } + TULIP_PERFEND(intr); +} + +#if defined(TULIP_USE_SOFTINTR) +/* + * This is a experimental idea to alleviate problems due to interrupt + * livelock. What is interrupt livelock? It's when you spend all your + * time servicing device interrupts and never drop below device ipl + * to do "useful" work. + * + * So what we do here is see if the device needs service and if so, + * disable interrupts (dismiss the interrupt), place it in a list of devices + * needing service, and issue a network software interrupt. + * + * When our network software interrupt routine gets called, we simply + * walk done the list of devices that we have created and deal with them + * at splnet/splsoftnet. + * + */ +static void +tulip_hardintr_handler( + tulip_softc_t * const sc, + int *progress_p) +{ + if (TULIP_CSR_READ(sc, csr_status) & (TULIP_STS_NORMALINTR|TULIP_STS_ABNRMLINTR) == 0) + return; + *progress_p = 1; + /* + * disable interrupts + */ + TULIP_CSR_WRITE(sc, csr_intr, 0); + /* + * mark it as needing a software interrupt + */ + tulip_softintr_mask |= (1U << sc->tulip_unit); +} + +static void +tulip_softintr( + void) +{ + u_int32_t softintr_mask, mask; + int progress = 0; + int unit; + int s; + + /* + * Copy mask to local copy and reset global one to 0. + */ + s = splimp(); + softintr_mask = tulip_softintr_mask; + tulip_softintr_mask = 0; + splx(s); + + /* + * Optimize for the single unit case. + */ + if (tulip_softintr_max_unit == 0) { + if (softintr_mask & 1) { + tulip_softc_t * const sc = tulips[0]; + /* + * Handle the "interrupt" and then reenable interrupts + */ + softintr_mask = 0; + tulip_intr_handler(sc, &progress); + TULIP_CSR_WRITE(sc, csr_intr, sc->tulip_intrmask); + } + return; + } + + /* + * Handle all "queued" interrupts in a round robin fashion. + * This is done so as not to favor a particular interface. + */ + unit = tulip_softintr_last_unit; + mask = (1U << unit); + while (softintr_mask != 0) { + if (tulip_softintr_max_unit == unit) { + unit = 0; mask = 1; + } else { + unit += 1; mask <<= 1; + } + if (softintr_mask & mask) { + tulip_softc_t * const sc = tulips[unit]; + /* + * Handle the "interrupt" and then reenable interrupts + */ + softintr_mask ^= mask; + tulip_intr_handler(sc, &progress); + TULIP_CSR_WRITE(sc, csr_intr, sc->tulip_intrmask); + } + } + + /* + * Save where we ending up. + */ + tulip_softintr_last_unit = unit; +} +#endif /* TULIP_USE_SOFTINTR */ + +static void +tulip_intr_shared( + void *arg) +{ + tulip_softc_t * sc = arg; + int progress = 0; + + for (; sc != NULL; sc = sc->tulip_slaves) { +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_intrs++; +#endif +#if defined(TULIP_USE_SOFTINTR) + tulip_hardintr_handler(sc, &progress); +#else + tulip_intr_handler(sc, &progress); +#endif + } +#if defined(TULIP_USE_SOFTINTR) + if (progress) + schednetisr(NETISR_DE); +#endif +} + +static void +tulip_intr_normal( + void *arg) +{ + tulip_softc_t * sc = (tulip_softc_t *) arg; + int progress = 0; + +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_intrs++; +#endif +#if defined(TULIP_USE_SOFTINTR) + tulip_hardintr_handler(sc, &progress); + if (progress) + schednetisr(NETISR_DE); +#else + tulip_intr_handler(sc, &progress); +#endif +} + +static struct mbuf * +tulip_mbuf_compress( + struct mbuf *m) +{ + struct mbuf *m0; +#if MCLBYTES >= ETHERMTU + 18 && !defined(BIG_PACKET) + MGETHDR(m0, M_DONTWAIT, MT_DATA); + if (m0 != NULL) { + if (m->m_pkthdr.len > MHLEN) { + MCLGET(m0, M_DONTWAIT); + if ((m0->m_flags & M_EXT) == 0) { + m_freem(m); + m_freem(m0); + return NULL; + } + } + m_copydata(m, 0, m->m_pkthdr.len, mtod(m0, caddr_t)); + m0->m_pkthdr.len = m0->m_len = m->m_pkthdr.len; + } +#else + int mlen = MHLEN; + int len = m->m_pkthdr.len; + struct mbuf **mp = &m0; + + while (len > 0) { + if (mlen == MHLEN) { + MGETHDR(*mp, M_DONTWAIT, MT_DATA); + } else { + MGET(*mp, M_DONTWAIT, MT_DATA); + } + if (*mp == NULL) { + m_freem(m0); + m0 = NULL; + break; + } + if (len > MLEN) { + MCLGET(*mp, M_DONTWAIT); + if (((*mp)->m_flags & M_EXT) == 0) { + m_freem(m0); + m0 = NULL; + break; + } + (*mp)->m_len = len <= MCLBYTES ? len : MCLBYTES; + } else { + (*mp)->m_len = len <= mlen ? len : mlen; + } + m_copydata(m, m->m_pkthdr.len - len, + (*mp)->m_len, mtod((*mp), caddr_t)); + len -= (*mp)->m_len; + mp = &(*mp)->m_next; + mlen = MLEN; + } +#endif + m_freem(m); + return m0; +} + +static struct mbuf * +tulip_txput( + tulip_softc_t * const sc, + struct mbuf *m) +{ + TULIP_PERFSTART(txput) + tulip_ringinfo_t * const ri = &sc->tulip_txinfo; + tulip_desc_t *eop, *nextout; + int segcnt, free; + u_int32_t d_status; +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NOTX) + bus_dmamap_t map; + int error; +#else + struct mbuf *m0; +#endif + +#if defined(TULIP_DEBUG) + if ((sc->tulip_cmdmode & TULIP_CMD_TXRUN) == 0) { + printf("%s%d: txput%s: tx not running\n", + sc->tulip_name, sc->tulip_unit, + (sc->tulip_flags & TULIP_TXPROBE_ACTIVE) ? "(probe)" : ""); + sc->tulip_flags |= TULIP_WANTTXSTART; + sc->tulip_dbg.dbg_txput_finishes[0]++; + goto finish; + } +#endif + + /* + * Now we try to fill in our transmit descriptors. This is + * a bit reminiscent of going on the Ark two by two + * since each descriptor for the TULIP can describe + * two buffers. So we advance through packet filling + * each of the two entries at a time to to fill each + * descriptor. Clear the first and last segment bits + * in each descriptor (actually just clear everything + * but the end-of-ring or chain bits) to make sure + * we don't get messed up by previously sent packets. + * + * We may fail to put the entire packet on the ring if + * there is either not enough ring entries free or if the + * packet has more than MAX_TXSEG segments. In the former + * case we will just wait for the ring to empty. In the + * latter case we have to recopy. + */ +#if !defined(TULIP_BUS_DMA) || defined(TULIP_BUS_DMA_NOTX) + again: + m0 = m; +#endif + d_status = 0; + eop = nextout = ri->ri_nextout; + segcnt = 0; + free = ri->ri_free; + +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NOTX) + /* + * Reclaim some dma maps from if we are out. + */ + if (sc->tulip_txmaps_free == 0) { +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_no_txmaps++; +#endif + free += tulip_tx_intr(sc); + } + if (sc->tulip_txmaps_free > 0) { + map = sc->tulip_txmaps[sc->tulip_txmaps_free-1]; + } else { + sc->tulip_flags |= TULIP_WANTTXSTART; +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_txput_finishes[1]++; +#endif + goto finish; + } + error = bus_dmamap_load_mbuf(sc->tulip_dmatag, map, m, BUS_DMA_NOWAIT); + if (error != 0) { + if (error == EFBIG) { + /* + * The packet exceeds the number of transmit buffer + * entries that we can use for one packet, so we have + * to recopy it into one mbuf and then try again. + */ + m = tulip_mbuf_compress(m); + if (m == NULL) { +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_txput_finishes[2]++; +#endif + goto finish; + } + error = bus_dmamap_load_mbuf(sc->tulip_dmatag, map, m, BUS_DMA_NOWAIT); + } + if (error != 0) { + printf("%s%d: unable to load tx map, " + "error = %d\n", sc->tulip_name, sc->tulip_unit, error); +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_txput_finishes[3]++; +#endif + goto finish; + } + } + if ((free -= (map->dm_nsegs + 1) / 2) <= 0 + /* + * See if there's any unclaimed space in the transmit ring. + */ + && (free += tulip_tx_intr(sc)) <= 0) { + /* + * There's no more room but since nothing + * has been committed at this point, just + * show output is active, put back the + * mbuf and return. + */ + sc->tulip_flags |= TULIP_WANTTXSTART; +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_txput_finishes[4]++; +#endif + bus_dmamap_unload(sc->tulip_dmatag, map); + goto finish; + } + for (; map->dm_nsegs - segcnt > 1; segcnt += 2) { + eop = nextout; + eop->d_flag &= TULIP_DFLAG_ENDRING|TULIP_DFLAG_CHAIN; + eop->d_status = d_status; + eop->d_addr1 = map->dm_segs[segcnt].ds_addr; + eop->d_length1 = map->dm_segs[segcnt].ds_len; + eop->d_addr2 = map->dm_segs[segcnt+1].ds_addr; + eop->d_length2 = map->dm_segs[segcnt+1].ds_len; + d_status = TULIP_DSTS_OWNER; + if (++nextout == ri->ri_last) + nextout = ri->ri_first; + } + if (segcnt < map->dm_nsegs) { + eop = nextout; + eop->d_flag &= TULIP_DFLAG_ENDRING|TULIP_DFLAG_CHAIN; + eop->d_status = d_status; + eop->d_addr1 = map->dm_segs[segcnt].ds_addr; + eop->d_length1 = map->dm_segs[segcnt].ds_len; + eop->d_addr2 = 0; + eop->d_length2 = 0; + if (++nextout == ri->ri_last) + nextout = ri->ri_first; + } + TULIP_TXMAP_PRESYNC(sc, map); + M_SETCTX(m, map); + map = NULL; + --sc->tulip_txmaps_free; /* commit to using the dmamap */ + +#else /* !TULIP_BUS_DMA */ + + do { + int len = m0->m_len; + caddr_t addr = mtod(m0, caddr_t); + unsigned clsize = PAGE_SIZE - (((uintptr_t) addr) & (PAGE_SIZE-1)); + + while (len > 0) { + unsigned slen = min(len, clsize); +#ifdef BIG_PACKET + int partial = 0; + if (slen >= 2048) + slen = 2040, partial = 1; +#endif + segcnt++; + if (segcnt > TULIP_MAX_TXSEG) { + /* + * The packet exceeds the number of transmit buffer + * entries that we can use for one packet, so we have + * recopy it into one mbuf and then try again. + */ + m = tulip_mbuf_compress(m); + if (m == NULL) + goto finish; + goto again; + } + if (segcnt & 1) { + if (--free == 0) { + /* + * See if there's any unclaimed space in the + * transmit ring. + */ + if ((free += tulip_tx_intr(sc)) == 0) { + /* + * There's no more room but since nothing + * has been committed at this point, just + * show output is active, put back the + * mbuf and return. + */ + sc->tulip_flags |= TULIP_WANTTXSTART; +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_txput_finishes[1]++; +#endif + goto finish; + } + } + eop = nextout; + if (++nextout == ri->ri_last) + nextout = ri->ri_first; + eop->d_flag &= TULIP_DFLAG_ENDRING|TULIP_DFLAG_CHAIN; + eop->d_status = d_status; + eop->d_addr1 = TULIP_KVATOPHYS(sc, addr); + eop->d_length1 = slen; + } else { + /* + * Fill in second half of descriptor + */ + eop->d_addr2 = TULIP_KVATOPHYS(sc, addr); + eop->d_length2 = slen; + } + d_status = TULIP_DSTS_OWNER; + len -= slen; + addr += slen; +#ifdef BIG_PACKET + if (partial) + continue; +#endif + clsize = PAGE_SIZE; + } + } while ((m0 = m0->m_next) != NULL); +#endif /* TULIP_BUS_DMA */ + + /* + * bounce a copy to the bpf listener, if any. + */ + if (sc->tulip_if.if_bpf != NULL) + bpf_mtap(&sc->tulip_if, m); + + /* + * The descriptors have been filled in. Now get ready + * to transmit. + */ + _IF_ENQUEUE(&sc->tulip_txq, m); + m = NULL; + + /* + * Make sure the next descriptor after this packet is owned + * by us since it may have been set up above if we ran out + * of room in the ring. + */ + nextout->d_status = 0; + TULIP_TXDESC_PRESYNC(sc, nextout, sizeof(u_int32_t)); + +#if !defined(TULIP_BUS_DMA) || defined(TULIP_BUS_DMA_NOTX) + /* + * If we only used the first segment of the last descriptor, + * make sure the second segment will not be used. + */ + if (segcnt & 1) { + eop->d_addr2 = 0; + eop->d_length2 = 0; + } +#endif /* TULIP_BUS_DMA */ + + /* + * Mark the last and first segments, indicate we want a transmit + * complete interrupt, and tell it to transmit! + */ + eop->d_flag |= TULIP_DFLAG_TxLASTSEG|TULIP_DFLAG_TxWANTINTR; + + /* + * Note that ri->ri_nextout is still the start of the packet + * and until we set the OWNER bit, we can still back out of + * everything we have done. + */ + ri->ri_nextout->d_flag |= TULIP_DFLAG_TxFIRSTSEG; +#if defined(TULIP_BUS_MAP) && !defined(TULIP_BUS_DMA_NOTX) + if (eop < ri->ri_nextout) { + TULIP_TXDESC_PRESYNC(sc, ri->ri_nextout, + (caddr_t) ri->ri_last - (caddr_t) ri->ri_nextout); + TULIP_TXDESC_PRESYNC(sc, ri->ri_first, + (caddr_t) (eop + 1) - (caddr_t) ri->ri_first); + } else { + TULIP_TXDESC_PRESYNC(sc, ri->ri_nextout, + (caddr_t) (eop + 1) - (caddr_t) ri->ri_nextout); + } +#endif + ri->ri_nextout->d_status = TULIP_DSTS_OWNER; + TULIP_TXDESC_PRESYNC(sc, ri->ri_nextout, sizeof(u_int32_t)); + + /* + * This advances the ring for us. + */ + ri->ri_nextout = nextout; + ri->ri_free = free; + + TULIP_PERFEND(txput); + + if (sc->tulip_flags & TULIP_TXPROBE_ACTIVE) { + TULIP_CSR_WRITE(sc, csr_txpoll, 1); + sc->tulip_if.if_flags |= IFF_OACTIVE; + sc->tulip_if.if_start = tulip_ifstart; + TULIP_PERFEND(txput); + return NULL; + } + + /* + * switch back to the single queueing ifstart. + */ + sc->tulip_flags &= ~TULIP_WANTTXSTART; + if (sc->tulip_txtimer == 0) + sc->tulip_txtimer = TULIP_TXTIMER; +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_txput_finishes[5]++; +#endif + + /* + * If we want a txstart, there must be not enough space in the + * transmit ring. So we want to enable transmit done interrupts + * so we can immediately reclaim some space. When the transmit + * interrupt is posted, the interrupt handler will call tx_intr + * to reclaim space and then txstart (since WANTTXSTART is set). + * txstart will move the packet into the transmit ring and clear + * WANTTXSTART thereby causing TXINTR to be cleared. + */ + finish: +#if defined(TULIP_DEBUG) + sc->tulip_dbg.dbg_txput_finishes[6]++; +#endif + if (sc->tulip_flags & (TULIP_WANTTXSTART|TULIP_DOINGSETUP)) { + sc->tulip_if.if_flags |= IFF_OACTIVE; + sc->tulip_if.if_start = tulip_ifstart; + if ((sc->tulip_intrmask & TULIP_STS_TXINTR) == 0) { + sc->tulip_intrmask |= TULIP_STS_TXINTR; + TULIP_CSR_WRITE(sc, csr_intr, sc->tulip_intrmask); + } + } else if ((sc->tulip_flags & TULIP_PROMISC) == 0) { + if (sc->tulip_intrmask & TULIP_STS_TXINTR) { + sc->tulip_intrmask &= ~TULIP_STS_TXINTR; + TULIP_CSR_WRITE(sc, csr_intr, sc->tulip_intrmask); + } + } + TULIP_CSR_WRITE(sc, csr_txpoll, 1); + TULIP_PERFEND(txput); + return m; +} + +static void +tulip_txput_setup( + tulip_softc_t * const sc) +{ + tulip_ringinfo_t * const ri = &sc->tulip_txinfo; + tulip_desc_t *nextout; + + /* + * We will transmit, at most, one setup packet per call to ifstart. + */ + +#if defined(TULIP_DEBUG) + if ((sc->tulip_cmdmode & TULIP_CMD_TXRUN) == 0) { + printf("%s%d: txput_setup: tx not running\n", + sc->tulip_name, sc->tulip_unit); + sc->tulip_flags |= TULIP_WANTTXSTART; + sc->tulip_if.if_start = tulip_ifstart; + return; + } +#endif + /* + * Try to reclaim some free descriptors.. + */ + if (ri->ri_free < 2) + tulip_tx_intr(sc); + if ((sc->tulip_flags & TULIP_DOINGSETUP) || ri->ri_free == 1) { + sc->tulip_flags |= TULIP_WANTTXSTART; + sc->tulip_if.if_start = tulip_ifstart; + return; + } + bcopy(sc->tulip_setupdata, sc->tulip_setupbuf, + sizeof(sc->tulip_setupbuf)); + /* + * Clear WANTSETUP and set DOINGSETUP. Set know that WANTSETUP is + * set and DOINGSETUP is clear doing an XOR of the two will DTRT. + */ + sc->tulip_flags ^= TULIP_WANTSETUP|TULIP_DOINGSETUP; + ri->ri_free--; + nextout = ri->ri_nextout; + nextout->d_flag &= TULIP_DFLAG_ENDRING|TULIP_DFLAG_CHAIN; + nextout->d_flag |= TULIP_DFLAG_TxFIRSTSEG|TULIP_DFLAG_TxLASTSEG + |TULIP_DFLAG_TxSETUPPKT|TULIP_DFLAG_TxWANTINTR; + if (sc->tulip_flags & TULIP_WANTHASHPERFECT) + nextout->d_flag |= TULIP_DFLAG_TxHASHFILT; + else if (sc->tulip_flags & TULIP_WANTHASHONLY) + nextout->d_flag |= TULIP_DFLAG_TxHASHFILT|TULIP_DFLAG_TxINVRSFILT; + + nextout->d_length2 = 0; + nextout->d_addr2 = 0; +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NOTX) + nextout->d_length1 = sc->tulip_setupmap->dm_segs[0].ds_len; + nextout->d_addr1 = sc->tulip_setupmap->dm_segs[0].ds_addr; + if (sc->tulip_setupmap->dm_nsegs == 2) { + nextout->d_length2 = sc->tulip_setupmap->dm_segs[1].ds_len; + nextout->d_addr2 = sc->tulip_setupmap->dm_segs[1].ds_addr; + } + TULIP_TXMAP_PRESYNC(sc, sc->tulip_setupmap); + TULIP_TXDESC_PRESYNC(sc, nextout, sizeof(*nextout)); +#else + nextout->d_length1 = sizeof(sc->tulip_setupbuf); + nextout->d_addr1 = TULIP_KVATOPHYS(sc, sc->tulip_setupbuf); +#endif + + /* + * Advance the ring for the next transmit packet. + */ + if (++ri->ri_nextout == ri->ri_last) + ri->ri_nextout = ri->ri_first; + + /* + * Make sure the next descriptor is owned by us since it + * may have been set up above if we ran out of room in the + * ring. + */ + ri->ri_nextout->d_status = 0; + TULIP_TXDESC_PRESYNC(sc, ri->ri_nextout, sizeof(u_int32_t)); + nextout->d_status = TULIP_DSTS_OWNER; + /* + * Flush the ownwership of the current descriptor + */ + TULIP_TXDESC_PRESYNC(sc, nextout, sizeof(u_int32_t)); + TULIP_CSR_WRITE(sc, csr_txpoll, 1); + if ((sc->tulip_intrmask & TULIP_STS_TXINTR) == 0) { + sc->tulip_intrmask |= TULIP_STS_TXINTR; + TULIP_CSR_WRITE(sc, csr_intr, sc->tulip_intrmask); + } +} + + +/* + * This routine is entered at splnet() (splsoftnet() on NetBSD) + * and thereby imposes no problems when TULIP_USE_SOFTINTR is + * defined or not. + */ +static int +tulip_ifioctl( + struct ifnet * ifp, + u_long cmd, + caddr_t data) +{ + TULIP_PERFSTART(ifioctl) + tulip_softc_t * const sc = (tulip_softc_t *)ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + int s; + int error = 0; + +#if defined(TULIP_USE_SOFTINTR) + s = splnet(); +#else + s = splimp(); +#endif + switch (cmd) { + case SIOCSIFADDR: + case SIOCGIFADDR: { + error = ether_ioctl(ifp, cmd, data); + break; + } + + case SIOCSIFFLAGS: { + tulip_addr_filter(sc); /* reinit multicast filter */ + tulip_init(sc); + break; + } + + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: { + error = ifmedia_ioctl(ifp, ifr, &sc->tulip_ifmedia, cmd); + break; + } + + case SIOCADDMULTI: + case SIOCDELMULTI: { + /* + * Update multicast listeners + */ + tulip_addr_filter(sc); /* reset multicast filtering */ + tulip_init(sc); + error = 0; + break; + } + + case SIOCSIFMTU: + /* + * Set the interface MTU. + */ + if (ifr->ifr_mtu > ETHERMTU +#ifdef BIG_PACKET + && sc->tulip_chipid != TULIP_21140 + && sc->tulip_chipid != TULIP_21140A + && sc->tulip_chipid != TULIP_21041 +#endif + ) { + error = EINVAL; + break; + } + ifp->if_mtu = ifr->ifr_mtu; +#ifdef BIG_PACKET + tulip_reset(sc); + tulip_init(sc); +#endif + break; + +#ifdef SIOCGADDRROM + case SIOCGADDRROM: { + error = copyout(sc->tulip_rombuf, ifr->ifr_data, sizeof(sc->tulip_rombuf)); + break; + } +#endif +#ifdef SIOCGCHIPID + case SIOCGCHIPID: { + ifr->ifr_metric = (int) sc->tulip_chipid; + break; + } +#endif + default: { + error = EINVAL; + break; + } + } + + splx(s); + TULIP_PERFEND(ifioctl); + return error; +} + +/* + * These routines gets called at device spl (from ether_output). This might + * pose a problem for TULIP_USE_SOFTINTR if ether_output is called at + * device spl from another driver. + */ + +static void +tulip_ifstart( + struct ifnet * const ifp) +{ + TULIP_PERFSTART(ifstart) + tulip_softc_t * const sc = (tulip_softc_t *)ifp->if_softc; + + if (sc->tulip_if.if_flags & IFF_RUNNING) { + + if ((sc->tulip_flags & (TULIP_WANTSETUP|TULIP_TXPROBE_ACTIVE)) == TULIP_WANTSETUP) + tulip_txput_setup(sc); + + while (sc->tulip_if.if_snd.ifq_head != NULL) { + struct mbuf *m; + IF_DEQUEUE(&sc->tulip_if.if_snd, m); + if ((m = tulip_txput(sc, m)) != NULL) { + IF_PREPEND(&sc->tulip_if.if_snd, m); + break; + } + } + if (sc->tulip_if.if_snd.ifq_head == NULL) + sc->tulip_if.if_start = tulip_ifstart_one; + } + + TULIP_PERFEND(ifstart); +} + +static void +tulip_ifstart_one( + struct ifnet * const ifp) +{ + TULIP_PERFSTART(ifstart_one) + tulip_softc_t * const sc = (tulip_softc_t *)ifp->if_softc; + + if ((sc->tulip_if.if_flags & IFF_RUNNING) + && sc->tulip_if.if_snd.ifq_head != NULL) { + struct mbuf *m; + IF_DEQUEUE(&sc->tulip_if.if_snd, m); + if ((m = tulip_txput(sc, m)) != NULL) + IF_PREPEND(&sc->tulip_if.if_snd, m); + } + TULIP_PERFEND(ifstart_one); +} + +/* + * Even though this routine runs at device spl, it does not break + * our use of splnet (splsoftnet under NetBSD) for the majority + * of this driver (if TULIP_USE_SOFTINTR defined) since + * if_watcbog is called from if_watchdog which is called from + * splsoftclock which is below spl[soft]net. + */ +static void +tulip_ifwatchdog( + struct ifnet *ifp) +{ + TULIP_PERFSTART(ifwatchdog) + tulip_softc_t * const sc = (tulip_softc_t *)ifp->if_softc; + +#if defined(TULIP_DEBUG) + u_int32_t rxintrs = sc->tulip_dbg.dbg_rxintrs - sc->tulip_dbg.dbg_last_rxintrs; + if (rxintrs > sc->tulip_dbg.dbg_high_rxintrs_hz) + sc->tulip_dbg.dbg_high_rxintrs_hz = rxintrs; + sc->tulip_dbg.dbg_last_rxintrs = sc->tulip_dbg.dbg_rxintrs; +#endif /* TULIP_DEBUG */ + + sc->tulip_if.if_timer = 1; + /* + * These should be rare so do a bulk test up front so we can just skip + * them if needed. + */ + if (sc->tulip_flags & (TULIP_SYSTEMERROR|TULIP_RXBUFSLOW|TULIP_NOMESSAGES)) { + /* + * If the number of receive buffer is low, try to refill + */ + if (sc->tulip_flags & TULIP_RXBUFSLOW) + tulip_rx_intr(sc); + + if (sc->tulip_flags & TULIP_SYSTEMERROR) { + printf("%s%d: %d system errors: last was %s\n", + sc->tulip_name, sc->tulip_unit, sc->tulip_system_errors, + tulip_system_errors[sc->tulip_last_system_error]); + } + if (sc->tulip_statusbits) { + tulip_print_abnormal_interrupt(sc, sc->tulip_statusbits); + sc->tulip_statusbits = 0; + } + + sc->tulip_flags &= ~(TULIP_NOMESSAGES|TULIP_SYSTEMERROR); + } + + if (sc->tulip_txtimer) + tulip_tx_intr(sc); + if (sc->tulip_txtimer && --sc->tulip_txtimer == 0) { + printf("%s%d: transmission timeout\n", sc->tulip_name, sc->tulip_unit); + if (TULIP_DO_AUTOSENSE(sc)) { + sc->tulip_media = TULIP_MEDIA_UNKNOWN; + sc->tulip_probe_state = TULIP_PROBE_INACTIVE; + sc->tulip_flags &= ~(TULIP_WANTRXACT|TULIP_LINKUP); + } + tulip_reset(sc); + tulip_init(sc); + } + + TULIP_PERFEND(ifwatchdog); + TULIP_PERFMERGE(sc, perf_intr_cycles); + TULIP_PERFMERGE(sc, perf_ifstart_cycles); + TULIP_PERFMERGE(sc, perf_ifioctl_cycles); + TULIP_PERFMERGE(sc, perf_ifwatchdog_cycles); + TULIP_PERFMERGE(sc, perf_timeout_cycles); + TULIP_PERFMERGE(sc, perf_ifstart_one_cycles); + TULIP_PERFMERGE(sc, perf_txput_cycles); + TULIP_PERFMERGE(sc, perf_txintr_cycles); + TULIP_PERFMERGE(sc, perf_rxintr_cycles); + TULIP_PERFMERGE(sc, perf_rxget_cycles); + TULIP_PERFMERGE(sc, perf_intr); + TULIP_PERFMERGE(sc, perf_ifstart); + TULIP_PERFMERGE(sc, perf_ifioctl); + TULIP_PERFMERGE(sc, perf_ifwatchdog); + TULIP_PERFMERGE(sc, perf_timeout); + TULIP_PERFMERGE(sc, perf_ifstart_one); + TULIP_PERFMERGE(sc, perf_txput); + TULIP_PERFMERGE(sc, perf_txintr); + TULIP_PERFMERGE(sc, perf_rxintr); + TULIP_PERFMERGE(sc, perf_rxget); +} + +/* + * All printf's are real as of now! + */ +#ifdef printf +#undef printf +#endif + +static void +tulip_attach( + tulip_softc_t * const sc) +{ + struct ifnet * const ifp = &sc->tulip_if; + + ifp->if_flags = IFF_BROADCAST|IFF_SIMPLEX|IFF_MULTICAST; + ifp->if_ioctl = tulip_ifioctl; + ifp->if_start = tulip_ifstart; + ifp->if_watchdog = tulip_ifwatchdog; + ifp->if_timer = 1; + ifp->if_output = ether_output; + ifp->if_init = tulip_ifinit; + + printf("%s%d: %s%s pass %d.%d%s\n", + sc->tulip_name, sc->tulip_unit, + sc->tulip_boardid, + tulip_chipdescs[sc->tulip_chipid], + (sc->tulip_revinfo & 0xF0) >> 4, + sc->tulip_revinfo & 0x0F, + (sc->tulip_features & (TULIP_HAVE_ISVSROM|TULIP_HAVE_OKSROM)) + == TULIP_HAVE_ISVSROM ? " (invalid EESPROM checksum)" : ""); + printf("%s%d: address %6D\n", + sc->tulip_name, sc->tulip_unit, sc->tulip_enaddr, ":"); + +#if defined(__alpha__) + /* + * In case the SRM console told us about a bogus media, + * we need to check to be safe. + */ + if (sc->tulip_mediums[sc->tulip_media] == NULL) + sc->tulip_media = TULIP_MEDIA_UNKNOWN; +#endif + + (*sc->tulip_boardsw->bd_media_probe)(sc); + ifmedia_init(&sc->tulip_ifmedia, 0, + tulip_ifmedia_change, + tulip_ifmedia_status); + sc->tulip_flags &= ~TULIP_DEVICEPROBE; + tulip_ifmedia_add(sc); + + tulip_reset(sc); + + ether_ifattach(&(sc)->tulip_if, ETHER_BPF_SUPPORTED); + ifp->if_snd.ifq_maxlen = ifqmaxlen; +} + +#if defined(TULIP_BUS_DMA) +#if !defined(TULIP_BUS_DMA_NOTX) || !defined(TULIP_BUS_DMA_NORX) +static int +tulip_busdma_allocmem( + tulip_softc_t * const sc, + size_t size, + bus_dmamap_t *map_p, + tulip_desc_t **desc_p) +{ + bus_dma_segment_t segs[1]; + int nsegs, error; + error = bus_dmamem_alloc(sc->tulip_dmatag, size, 1, PAGE_SIZE, + segs, sizeof(segs)/sizeof(segs[0]), + &nsegs, BUS_DMA_NOWAIT); + if (error == 0) { + void *desc; + error = bus_dmamem_map(sc->tulip_dmatag, segs, nsegs, size, + (void *) &desc, BUS_DMA_NOWAIT|BUS_DMA_COHERENT); + if (error == 0) { + bus_dmamap_t map; + error = bus_dmamap_create(sc->tulip_dmatag, size, 1, size, 0, + BUS_DMA_NOWAIT, &map); + if (error == 0) { + error = bus_dmamap_load(sc->tulip_dmatag, map, desc, + size, NULL, BUS_DMA_NOWAIT); + if (error) + bus_dmamap_destroy(sc->tulip_dmatag, map); + else + *map_p = map; + } + if (error) + bus_dmamem_unmap(sc->tulip_dmatag, desc, size); + } + if (error) + bus_dmamem_free(sc->tulip_dmatag, segs, nsegs); + else + *desc_p = desc; + } + return error; +} +#endif + +static int +tulip_busdma_init( + tulip_softc_t * const sc) +{ + int error = 0; + +#if !defined(TULIP_BUS_DMA_NOTX) + /* + * Allocate dmamap for setup descriptor + */ + error = bus_dmamap_create(sc->tulip_dmatag, sizeof(sc->tulip_setupbuf), 2, + sizeof(sc->tulip_setupbuf), 0, BUS_DMA_NOWAIT, + &sc->tulip_setupmap); + if (error == 0) { + error = bus_dmamap_load(sc->tulip_dmatag, sc->tulip_setupmap, + sc->tulip_setupbuf, sizeof(sc->tulip_setupbuf), + NULL, BUS_DMA_NOWAIT); + if (error) + bus_dmamap_destroy(sc->tulip_dmatag, sc->tulip_setupmap); + } + /* + * Allocate space and dmamap for transmit ring + */ + if (error == 0) { + error = tulip_busdma_allocmem(sc, sizeof(tulip_desc_t) * TULIP_TXDESCS, + &sc->tulip_txdescmap, + &sc->tulip_txdescs); + } + + /* + * Allocate dmamaps for each transmit descriptors + */ + if (error == 0) { + while (error == 0 && sc->tulip_txmaps_free < TULIP_TXDESCS) { + bus_dmamap_t map; + if ((error = TULIP_TXMAP_CREATE(sc, &map)) == 0) + sc->tulip_txmaps[sc->tulip_txmaps_free++] = map; + } + if (error) { + while (sc->tulip_txmaps_free > 0) + bus_dmamap_destroy(sc->tulip_dmatag, + sc->tulip_txmaps[--sc->tulip_txmaps_free]); + } + } +#else + if (error == 0) { + sc->tulip_txdescs = (tulip_desc_t *) malloc(TULIP_TXDESCS * sizeof(tulip_desc_t), M_DEVBUF, M_NOWAIT); + if (sc->tulip_txdescs == NULL) + error = ENOMEM; + } +#endif +#if !defined(TULIP_BUS_DMA_NORX) + /* + * Allocate space and dmamap for receive ring + */ + if (error == 0) { + error = tulip_busdma_allocmem(sc, sizeof(tulip_desc_t) * TULIP_RXDESCS, + &sc->tulip_rxdescmap, + &sc->tulip_rxdescs); + } + + /* + * Allocate dmamaps for each receive descriptors + */ + if (error == 0) { + while (error == 0 && sc->tulip_rxmaps_free < TULIP_RXDESCS) { + bus_dmamap_t map; + if ((error = TULIP_RXMAP_CREATE(sc, &map)) == 0) + sc->tulip_rxmaps[sc->tulip_rxmaps_free++] = map; + } + if (error) { + while (sc->tulip_rxmaps_free > 0) + bus_dmamap_destroy(sc->tulip_dmatag, + sc->tulip_rxmaps[--sc->tulip_rxmaps_free]); + } + } +#else + if (error == 0) { + sc->tulip_rxdescs = (tulip_desc_t *) malloc(TULIP_RXDESCS * sizeof(tulip_desc_t), M_DEVBUF, M_NOWAIT); + if (sc->tulip_rxdescs == NULL) + error = ENOMEM; + } +#endif + return error; +} +#endif /* TULIP_BUS_DMA */ + +static void +tulip_initcsrs( + tulip_softc_t * const sc, + tulip_csrptr_t csr_base, + size_t csr_size) +{ + sc->tulip_csrs.csr_busmode = csr_base + 0 * csr_size; + sc->tulip_csrs.csr_txpoll = csr_base + 1 * csr_size; + sc->tulip_csrs.csr_rxpoll = csr_base + 2 * csr_size; + sc->tulip_csrs.csr_rxlist = csr_base + 3 * csr_size; + sc->tulip_csrs.csr_txlist = csr_base + 4 * csr_size; + sc->tulip_csrs.csr_status = csr_base + 5 * csr_size; + sc->tulip_csrs.csr_command = csr_base + 6 * csr_size; + sc->tulip_csrs.csr_intr = csr_base + 7 * csr_size; + sc->tulip_csrs.csr_missed_frames = csr_base + 8 * csr_size; + sc->tulip_csrs.csr_9 = csr_base + 9 * csr_size; + sc->tulip_csrs.csr_10 = csr_base + 10 * csr_size; + sc->tulip_csrs.csr_11 = csr_base + 11 * csr_size; + sc->tulip_csrs.csr_12 = csr_base + 12 * csr_size; + sc->tulip_csrs.csr_13 = csr_base + 13 * csr_size; + sc->tulip_csrs.csr_14 = csr_base + 14 * csr_size; + sc->tulip_csrs.csr_15 = csr_base + 15 * csr_size; +} + +static void +tulip_initring( + tulip_softc_t * const sc, + tulip_ringinfo_t * const ri, + tulip_desc_t *descs, + int ndescs) +{ + ri->ri_max = ndescs; + ri->ri_first = descs; + ri->ri_last = ri->ri_first + ri->ri_max; + bzero((caddr_t) ri->ri_first, sizeof(ri->ri_first[0]) * ri->ri_max); + ri->ri_last[-1].d_flag = TULIP_DFLAG_ENDRING; +} + +/* + * This is the PCI configuration support. + */ + +#define PCI_CBIO 0x10 /* Configuration Base IO Address */ +#define PCI_CBMA 0x14 /* Configuration Base Memory Address */ +#define PCI_CFDA 0x40 /* Configuration Driver Area */ + +static int +tulip_pci_probe(device_t dev) +{ + const char *name = NULL; + + if (pci_get_vendor(dev) != DEC_VENDORID) + return ENXIO; + + /* + * Some LanMedia WAN cards use the Tulip chip, but they have + * their own driver, and we should not recognize them + */ + if (pci_get_subvendor(dev) == 0x1376) + return ENXIO; + + switch (pci_get_device(dev)) { + case CHIPID_21040: + name = "Digital 21040 Ethernet"; + break; + case CHIPID_21041: + name = "Digital 21041 Ethernet"; + break; + case CHIPID_21140: + if (pci_get_revid(dev) >= 0x20) + name = "Digital 21140A Fast Ethernet"; + else + name = "Digital 21140 Fast Ethernet"; + break; + case CHIPID_21142: + if (pci_get_revid(dev) >= 0x20) + name = "Digital 21143 Fast Ethernet"; + else + name = "Digital 21142 Fast Ethernet"; + break; + } + if (name) { + device_set_desc(dev, name); + return -200; + } + return ENXIO; +} + +static int +tulip_shutdown(device_t dev) +{ + tulip_softc_t * const sc = device_get_softc(dev); + TULIP_CSR_WRITE(sc, csr_busmode, TULIP_BUSMODE_SWRESET); + DELAY(10); /* Wait 10 microseconds (actually 50 PCI cycles but at + 33MHz that comes to two microseconds but wait a + bit longer anyways) */ + return 0; +} + +static int +tulip_pci_attach(device_t dev) +{ + tulip_softc_t *sc; +#if defined(__alpha__) + tulip_media_t media = TULIP_MEDIA_UNKNOWN; +#endif + int retval, idx; + u_int32_t revinfo, cfdainfo, cfcsinfo; + unsigned csroffset = TULIP_PCI_CSROFFSET; + unsigned csrsize = TULIP_PCI_CSRSIZE; + tulip_csrptr_t csr_base; + tulip_chipid_t chipid = TULIP_CHIPID_UNKNOWN; + struct resource *res; + int rid, unit; + + unit = device_get_unit(dev); + + if (unit >= TULIP_MAX_DEVICES) { + printf("de%d", unit); + printf(": not configured; limit of %d reached or exceeded\n", + TULIP_MAX_DEVICES); + return ENXIO; + } + + revinfo = pci_get_revid(dev); + cfdainfo = pci_read_config(dev, PCI_CFDA, 4); + cfcsinfo = pci_read_config(dev, PCIR_COMMAND, 4); + + /* turn busmaster on in case BIOS doesn't set it */ + if(!(cfcsinfo & PCIM_CMD_BUSMASTEREN)) { + cfcsinfo |= PCIM_CMD_BUSMASTEREN; + pci_write_config(dev, PCIR_COMMAND, cfcsinfo, 4); + } + + if (pci_get_vendor(dev) == DEC_VENDORID) { + if (pci_get_device(dev) == CHIPID_21040) + chipid = TULIP_21040; + else if (pci_get_device(dev) == CHIPID_21041) + chipid = TULIP_21041; + else if (pci_get_device(dev) == CHIPID_21140) + chipid = (revinfo >= 0x20) ? TULIP_21140A : TULIP_21140; + else if (pci_get_device(dev) == CHIPID_21142) + chipid = (revinfo >= 0x20) ? TULIP_21143 : TULIP_21142; + } + if (chipid == TULIP_CHIPID_UNKNOWN) + return ENXIO; + + if (chipid == TULIP_21040 && revinfo < 0x20) { + printf("de%d", unit); + printf(": not configured; 21040 pass 2.0 required (%d.%d found)\n", + revinfo >> 4, revinfo & 0x0f); + return ENXIO; + } else if (chipid == TULIP_21140 && revinfo < 0x11) { + printf("de%d: not configured; 21140 pass 1.1 required (%d.%d found)\n", + unit, revinfo >> 4, revinfo & 0x0f); + return ENXIO; + } + + sc = device_get_softc(dev); + sc->tulip_pci_busno = pci_get_bus(dev); + sc->tulip_pci_devno = pci_get_slot(dev); + sc->tulip_chipid = chipid; + sc->tulip_flags |= TULIP_DEVICEPROBE; + if (chipid == TULIP_21140 || chipid == TULIP_21140A) + sc->tulip_features |= TULIP_HAVE_GPR|TULIP_HAVE_STOREFWD; + if (chipid == TULIP_21140A && revinfo <= 0x22) + sc->tulip_features |= TULIP_HAVE_RXBADOVRFLW; + if (chipid == TULIP_21140) + sc->tulip_features |= TULIP_HAVE_BROKEN_HASH; + if (chipid != TULIP_21040 && chipid != TULIP_21140) + sc->tulip_features |= TULIP_HAVE_POWERMGMT; + if (chipid == TULIP_21041 || chipid == TULIP_21142 || chipid == TULIP_21143) { + sc->tulip_features |= TULIP_HAVE_DUALSENSE; + if (chipid != TULIP_21041 || revinfo >= 0x20) + sc->tulip_features |= TULIP_HAVE_SIANWAY; + if (chipid != TULIP_21041) + sc->tulip_features |= TULIP_HAVE_SIAGP|TULIP_HAVE_RXBADOVRFLW|TULIP_HAVE_STOREFWD; + if (chipid != TULIP_21041 && revinfo >= 0x20) + sc->tulip_features |= TULIP_HAVE_SIA100; + } + + if (sc->tulip_features & TULIP_HAVE_POWERMGMT + && (cfdainfo & (TULIP_CFDA_SLEEP|TULIP_CFDA_SNOOZE))) { + cfdainfo &= ~(TULIP_CFDA_SLEEP|TULIP_CFDA_SNOOZE); + pci_write_config(dev, PCI_CFDA, cfdainfo, 4); + DELAY(11*1000); + } +#if defined(__alpha__) + /* + * The Alpha SRM console encodes a console set media in the driver + * part of the CFDA register. Note that the Multia presents a + * problem in that its BNC mode is really EXTSIA. So in that case + * force a probe. + */ + switch ((cfdainfo >> 8) & 0xff) { + case 1: media = chipid > TULIP_21040 ? TULIP_MEDIA_AUI : TULIP_MEDIA_AUIBNC; break; + case 2: media = chipid > TULIP_21040 ? TULIP_MEDIA_BNC : TULIP_MEDIA_UNKNOWN; break; + case 3: media = TULIP_MEDIA_10BASET; break; + case 4: media = TULIP_MEDIA_10BASET_FD; break; + case 5: media = TULIP_MEDIA_100BASETX; break; + case 6: media = TULIP_MEDIA_100BASETX_FD; break; + default: media = TULIP_MEDIA_UNKNOWN; break; + } +#endif + + sc->tulip_unit = unit; + sc->tulip_name = "de"; + sc->tulip_revinfo = revinfo; + sc->tulip_if.if_softc = sc; +#if defined(TULIP_IOMAPPED) + rid = PCI_CBIO; + res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0, ~0, 1, RF_ACTIVE); +#else + rid = PCI_CBMA; + res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); +#endif + if (!res) + return ENXIO; + sc->tulip_csrs_bst = rman_get_bustag(res); + sc->tulip_csrs_bsh = rman_get_bushandle(res); + csr_base = 0; + + tulips[unit] = sc; + + tulip_initcsrs(sc, csr_base + csroffset, csrsize); + +#if defined(TULIP_BUS_DMA) + if ((retval = tulip_busdma_init(sc)) != 0) { + printf("error initing bus_dma: %d\n", retval); + return ENXIO; + } +#else + sc->tulip_rxdescs = (tulip_desc_t *) malloc(sizeof(tulip_desc_t) * TULIP_RXDESCS, M_DEVBUF, M_NOWAIT); + sc->tulip_txdescs = (tulip_desc_t *) malloc(sizeof(tulip_desc_t) * TULIP_TXDESCS, M_DEVBUF, M_NOWAIT); + if (sc->tulip_rxdescs == NULL || sc->tulip_txdescs == NULL) { + printf("malloc failed\n"); + if (sc->tulip_rxdescs) + free((caddr_t) sc->tulip_rxdescs, M_DEVBUF); + if (sc->tulip_txdescs) + free((caddr_t) sc->tulip_txdescs, M_DEVBUF); + return ENXIO; + } +#endif + + tulip_initring(sc, &sc->tulip_rxinfo, sc->tulip_rxdescs, TULIP_RXDESCS); + tulip_initring(sc, &sc->tulip_txinfo, sc->tulip_txdescs, TULIP_TXDESCS); + + /* + * Make sure there won't be any interrupts or such... + */ + TULIP_CSR_WRITE(sc, csr_busmode, TULIP_BUSMODE_SWRESET); + DELAY(100); /* Wait 10 microseconds (actually 50 PCI cycles but at + 33MHz that comes to two microseconds but wait a + bit longer anyways) */ + + if ((retval = tulip_read_macaddr(sc)) < 0) { + printf("%s%d", sc->tulip_name, sc->tulip_unit); + printf(": can't read ENET ROM (why=%d) (", retval); + for (idx = 0; idx < 32; idx++) + printf("%02x", sc->tulip_rombuf[idx]); + printf("\n"); + printf("%s%d: %s%s pass %d.%d\n", + sc->tulip_name, sc->tulip_unit, + sc->tulip_boardid, tulip_chipdescs[sc->tulip_chipid], + (sc->tulip_revinfo & 0xF0) >> 4, sc->tulip_revinfo & 0x0F); + printf("%s%d: address unknown\n", sc->tulip_name, sc->tulip_unit); + } else { + int s; + void (*intr_rtn)(void *) = tulip_intr_normal; + + if (sc->tulip_features & TULIP_HAVE_SHAREDINTR) + intr_rtn = tulip_intr_shared; + + if ((sc->tulip_features & TULIP_HAVE_SLAVEDINTR) == 0) { + void *ih; + + rid = 0; + res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, + 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); + if (res == 0 || bus_setup_intr(dev, res, INTR_TYPE_NET, + intr_rtn, sc, &ih)) { + printf("%s%d: couldn't map interrupt\n", + sc->tulip_name, sc->tulip_unit); + free((caddr_t) sc->tulip_rxdescs, M_DEVBUF); + free((caddr_t) sc->tulip_txdescs, M_DEVBUF); + return ENXIO; + } + } +#if defined(TULIP_USE_SOFTINTR) + if (sc->tulip_unit > tulip_softintr_max_unit) + tulip_softintr_max_unit = sc->tulip_unit; +#endif + + s = splimp(); +#if defined(__alpha__) + sc->tulip_media = media; +#endif + tulip_attach(sc); +#if defined(__alpha__) + if (sc->tulip_media != TULIP_MEDIA_UNKNOWN) + tulip_linkup(sc, media); +#endif + splx(s); + } + return 0; +} + +static device_method_t tulip_pci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tulip_pci_probe), + DEVMETHOD(device_attach, tulip_pci_attach), + DEVMETHOD(device_shutdown, tulip_shutdown), + { 0, 0 } +}; +static driver_t tulip_pci_driver = { + "de", + tulip_pci_methods, + sizeof(tulip_softc_t), +}; +static devclass_t tulip_devclass; +DRIVER_MODULE(if_de, pci, tulip_pci_driver, tulip_devclass, 0, 0); diff --git a/sys/pci/if_devar.h b/sys/pci/if_devar.h new file mode 100644 index 0000000..f9e63b3 --- /dev/null +++ b/sys/pci/if_devar.h @@ -0,0 +1,924 @@ +/* $NetBSD: if_devar.h,v 1.32 1999/04/01 14:55:25 tsubai Exp $ */ + +/* $FreeBSD$ */ + +/*- + * Copyright (c) 1994-1997 Matt Thomas (matt@3am-software.com) + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Id: if_devar.h,v 1.28 1997/07/03 16:55:07 thomas Exp + */ + +#if !defined(_DEVAR_H) +#define _DEVAR_H + +typedef bus_addr_t tulip_csrptr_t; + +#define TULIP_PCI_CSRSIZE 8 +#define TULIP_PCI_CSROFFSET 0 + +#define TULIP_CSR_READ(sc, csr) \ + bus_space_read_4((sc)->tulip_csrs_bst, \ + (sc)->tulip_csrs_bsh, \ + (sc)->tulip_csrs.csr) +#define TULIP_CSR_WRITE(sc, csr, val) \ + bus_space_write_4((sc)->tulip_csrs_bst, \ + (sc)->tulip_csrs_bsh, \ + (sc)->tulip_csrs.csr, val) + +#define TULIP_CSR_READBYTE(sc, csr) \ + bus_space_read_1((sc)->tulip_csrs_bst, \ + (sc)->tulip_csrs_bsh, \ + (sc)->tulip_csrs.csr) +#define TULIP_CSR_WRITEBYTE(sc, csr, val) \ + bus_space_write_1((sc)->tulip_csrs_bst, \ + (sc)->tulip_csrs_bsh, \ + (sc)->tulip_csrs.csr, val) + +/* + * This structure contains "pointers" for the registers on + * the various 21x4x chips. CSR0 through CSR8 are common + * to all chips. After that, it gets messy... + */ +typedef struct { + tulip_csrptr_t csr_busmode; /* CSR0 */ + tulip_csrptr_t csr_txpoll; /* CSR1 */ + tulip_csrptr_t csr_rxpoll; /* CSR2 */ + tulip_csrptr_t csr_rxlist; /* CSR3 */ + tulip_csrptr_t csr_txlist; /* CSR4 */ + tulip_csrptr_t csr_status; /* CSR5 */ + tulip_csrptr_t csr_command; /* CSR6 */ + tulip_csrptr_t csr_intr; /* CSR7 */ + tulip_csrptr_t csr_missed_frames; /* CSR8 */ + tulip_csrptr_t csr_9; /* CSR9 */ + tulip_csrptr_t csr_10; /* CSR10 */ + tulip_csrptr_t csr_11; /* CSR11 */ + tulip_csrptr_t csr_12; /* CSR12 */ + tulip_csrptr_t csr_13; /* CSR13 */ + tulip_csrptr_t csr_14; /* CSR14 */ + tulip_csrptr_t csr_15; /* CSR15 */ +} tulip_regfile_t; + +#define csr_enetrom csr_9 /* 21040 */ +#define csr_reserved csr_10 /* 21040 */ +#define csr_full_duplex csr_11 /* 21040 */ +#define csr_bootrom csr_10 /* 21041/21140A/?? */ +#define csr_gp csr_12 /* 21140* */ +#define csr_watchdog csr_15 /* 21140* */ +#define csr_gp_timer csr_11 /* 21041/21140* */ +#define csr_srom_mii csr_9 /* 21041/21140* */ +#define csr_sia_status csr_12 /* 2104x */ +#define csr_sia_connectivity csr_13 /* 2104x */ +#define csr_sia_tx_rx csr_14 /* 2104x */ +#define csr_sia_general csr_15 /* 2104x */ + +/* + * While 21x4x allows chaining of its descriptors, this driver + * doesn't take advantage of it. We keep the descriptors in a + * traditional FIFO ring. + */ +typedef struct { + tulip_desc_t *ri_first; /* first entry in ring */ + tulip_desc_t *ri_last; /* one after last entry */ + tulip_desc_t *ri_nextin; /* next to processed by host */ + tulip_desc_t *ri_nextout; /* next to processed by adapter */ + int ri_max; + int ri_free; +} tulip_ringinfo_t; + +/* + * The 21040 has a stupid restriction in that the receive + * buffers must be longword aligned. But since Ethernet + * headers are not a multiple of longwords in size this forces + * the data to non-longword aligned. Since IP requires the + * data to be longword aligned, we need to copy it after it has + * been DMA'ed in our memory. + * + * Since we have to copy it anyways, we might as well as allocate + * dedicated receive space for the input. This allows to use a + * small receive buffer size and more ring entries to be able to + * better keep with a flood of tiny Ethernet packets. + * + * The receive space MUST ALWAYS be a multiple of the page size. + * And the number of receive descriptors multiplied by the size + * of the receive buffers must equal the recevive space. This + * is so that we can manipulate the page tables so that even if a + * packet wraps around the end of the receive space, we can + * treat it as virtually contiguous. + * + * The above used to be true (the stupid restriction is still true) + * but we gone to directly DMA'ing into MBUFs (unless it's on an + * architecture which can't handle unaligned accesses) because with + * 100Mb/s cards the copying is just too much of a hit. + */ +#if !defined(__i386__) +#define TULIP_COPY_RXDATA 1 +#endif + +#define TULIP_DATA_PER_DESC 2032 +#define TULIP_TXTIMER 4 +#define TULIP_RXDESCS 48 +#define TULIP_TXDESCS 128 +#define TULIP_RXQ_TARGET 32 +#if TULIP_RXQ_TARGET >= TULIP_RXDESCS +#error TULIP_RXQ_TARGET must be less than TULIP_RXDESCS +#endif +#define TULIP_RX_BUFLEN ((MCLBYTES < 2048 ? MCLBYTES : 2048) - 16) + +/* + * Forward reference to make C happy. + */ +typedef struct _tulip_softc_t tulip_softc_t; + +/* + * The various controllers support. + */ + +typedef enum { + TULIP_21040, + TULIP_21041, + TULIP_21140, TULIP_21140A, TULIP_21142, + TULIP_21143, + TULIP_CHIPID_UNKNOWN +} tulip_chipid_t; + +/* + * Various physical media types supported. + * BNCAUI is BNC or AUI since on the 21040 you can't really tell + * which is in use. + */ +typedef enum { + TULIP_MEDIA_UNKNOWN, + TULIP_MEDIA_10BASET, + TULIP_MEDIA_10BASET_FD, + TULIP_MEDIA_BNC, + TULIP_MEDIA_AUI, + TULIP_MEDIA_EXTSIA, + TULIP_MEDIA_AUIBNC, + TULIP_MEDIA_100BASETX, + TULIP_MEDIA_100BASETX_FD, + TULIP_MEDIA_100BASET4, + TULIP_MEDIA_100BASEFX, + TULIP_MEDIA_100BASEFX_FD, + TULIP_MEDIA_MAX +} tulip_media_t; + +#define TULIP_BIT(b) (1L << ((int)(b))) +#define TULIP_FDBIT(m) (1L << ((int)TULIP_MEDIA_ ## m ## _FD)) +#define TULIP_MBIT(m) (1L << ((int)TULIP_MEDIA_ ## m )) +#define TULIP_IS_MEDIA_FD(m) (TULIP_BIT(m) & \ + (TULIP_FDBIT(10BASET) \ + |TULIP_FDBIT(100BASETX) \ + |TULIP_FDBIT(100BASEFX))) +#define TULIP_CAN_MEDIA_FD(m) (TULIP_BIT(m) & \ + (TULIP_MBIT(10BASET) \ + |TULIP_MBIT(100BASETX) \ + |TULIP_MBIT(100BASEFX))) +#define TULIP_FD_MEDIA_OF(m) ((tulip_media_t)((m) + 1)) +#define TULIP_HD_MEDIA_OF(m) ((tulip_media_t)((m) - 1)) +#define TULIP_IS_MEDIA_100MB(m) ((m) >= TULIP_MEDIA_100BASETX) +#define TULIP_IS_MEDIA_TP(m) ((TULIP_BIT(m) & \ + (TULIP_MBIT(BNC) \ + |TULIP_MBIT(AUI) \ + |TULIP_MBIT(AUIBNC) \ + |TULIP_MBIT(EXTSIA))) == 0) + +#define TULIP_SROM_ATTR_MII 0x0100 +#define TULIP_SROM_ATTR_NWAY 0x0200 +#define TULIP_SROM_ATTR_AUTOSENSE 0x0400 +#define TULIP_SROM_ATTR_POWERUP 0x0800 +#define TULIP_SROM_ATTR_NOLINKPASS 0x1000 + +typedef struct { + enum { + TULIP_MEDIAINFO_NONE, + TULIP_MEDIAINFO_SIA, + TULIP_MEDIAINFO_GPR, + TULIP_MEDIAINFO_MII, + TULIP_MEDIAINFO_RESET, + TULIP_MEDIAINFO_SYM + } mi_type; + union { + struct { + u_int16_t sia_connectivity; + u_int16_t sia_tx_rx; + u_int16_t sia_general; + u_int32_t sia_gp_control; /* 21142/21143 */ + u_int32_t sia_gp_data; /* 21142/21143 */ + } un_sia; + struct { + u_int32_t gpr_cmdmode; + u_int32_t gpr_gpcontrol; /* 21142/21143 */ + u_int32_t gpr_gpdata; + u_int8_t gpr_actmask; + u_int8_t gpr_actdata; + u_int gpr_default : 1; + } un_gpr; + struct { + u_int32_t mii_mediamask; + u_int16_t mii_capabilities; + u_int16_t mii_advertisement; + u_int16_t mii_full_duplex; + u_int16_t mii_tx_threshold; + u_int16_t mii_interrupt; /* 21142/21143 */ + u_int8_t mii_phyaddr; + u_int8_t mii_gpr_length; + u_int8_t mii_gpr_offset; + u_int8_t mii_reset_length; + u_int8_t mii_reset_offset; + u_int32_t mii_phyid; + } un_mii; + } mi_un; +} tulip_media_info_t; + +#define mi_sia_connectivity mi_un.un_sia.sia_connectivity +#define mi_sia_tx_rx mi_un.un_sia.sia_tx_rx +#define mi_sia_general mi_un.un_sia.sia_general +#define mi_sia_gp_control mi_un.un_sia.sia_gp_control +#define mi_sia_gp_data mi_un.un_sia.sia_gp_data + +#define mi_gpcontrol mi_un.un_gpr.gpr_gpcontrol +#define mi_gpdata mi_un.un_gpr.gpr_gpdata +#define mi_actmask mi_un.un_gpr.gpr_actmask +#define mi_actdata mi_un.un_gpr.gpr_actdata +#define mi_default mi_un.un_gpr.gpr_default +#define mi_cmdmode mi_un.un_gpr.gpr_cmdmode + +#define mi_phyaddr mi_un.un_mii.mii_phyaddr +#define mi_gpr_length mi_un.un_mii.mii_gpr_length +#define mi_gpr_offset mi_un.un_mii.mii_gpr_offset +#define mi_reset_length mi_un.un_mii.mii_reset_length +#define mi_reset_offset mi_un.un_mii.mii_reset_offset +#define mi_capabilities mi_un.un_mii.mii_capabilities +#define mi_advertisement mi_un.un_mii.mii_advertisement +#define mi_full_duplex mi_un.un_mii.mii_full_duplex +#define mi_tx_threshold mi_un.un_mii.mii_tx_threshold +#define mi_mediamask mi_un.un_mii.mii_mediamask +#define mi_mii_interrupt mi_un.un_mii.mii_interrupt +#define mi_phyid mi_un.un_mii.mii_phyid + +#define TULIP_MEDIAINFO_SIA_INIT(sc, mi, chipid, media) do { \ + (mi)->mi_type = TULIP_MEDIAINFO_SIA; \ + sc->tulip_mediums[TULIP_MEDIA_ ## media] = (mi); \ + (mi)->mi_sia_connectivity = TULIP_ ## chipid ## _SIACONN_ ## media; \ + (mi)->mi_sia_tx_rx = TULIP_ ## chipid ## _SIATXRX_ ## media; \ + (mi)->mi_sia_general = TULIP_ ## chipid ## _SIAGEN_ ## media; \ +} while (0) + +#define TULIP_MEDIAINFO_ADD_CAPABILITY(sc, mi, media) do { \ + if ((sc)->tulip_mediums[TULIP_MEDIA_ ## media] == NULL \ + && ((mi)->mi_capabilities & PHYSTS_ ## media)) { \ + (sc)->tulip_mediums[TULIP_MEDIA_ ## media] = (mi); \ + (mi)->mi_mediamask |= TULIP_BIT(TULIP_MEDIA_ ## media); \ + } \ +} while (0) + +#define TULIP_MII_NOPHY 32 +/* + * Some boards need to treated specially. The following enumeration + * identifies the cards with quirks (or those we just want to single + * out for special merit or scorn). + */ +typedef enum { + TULIP_21040_GENERIC, /* Generic 21040 (works with most any board) */ + TULIP_21140_ISV, /* Digital Semicondutor 21140 ISV SROM Format */ + TULIP_21142_ISV, /* Digital Semicondutor 21142 ISV SROM Format */ + TULIP_21143_ISV, /* Digital Semicondutor 21143 ISV SROM Format */ + TULIP_21140_DEC_EB, /* Digital Semicondutor 21140 Evaluation Board */ + TULIP_21140_MII, /* 21140[A] with MII */ + TULIP_21140_DEC_DE500, /* Digital DE500-?? 10/100 */ + TULIP_21140_SMC_9332, /* SMC 9332 */ + TULIP_21140_COGENT_EM100, /* Cogent EM100 100 only */ + TULIP_21140_ZNYX_ZX34X, /* ZNYX ZX342 10/100 */ + TULIP_21140_ASANTE, /* AsanteFast 10/100 */ + TULIP_21140_EN1207, /* Accton EN2107 10/100 BNC */ + TULIP_21041_GENERIC /* Generic 21041 card */ +} tulip_board_t; + +typedef enum { + TULIP_MEDIAPOLL_TIMER, /* 100ms timer fired */ + TULIP_MEDIAPOLL_FASTTIMER, /* <100ms timer fired */ + TULIP_MEDIAPOLL_LINKFAIL, /* called from interrupt routine */ + TULIP_MEDIAPOLL_LINKPASS, /* called from interrupt routine */ + TULIP_MEDIAPOLL_START, /* start a media probe (called from reset) */ + TULIP_MEDIAPOLL_TXPROBE_OK, /* txprobe succeeded */ + TULIP_MEDIAPOLL_TXPROBE_FAILED, /* txprobe failed */ + TULIP_MEDIAPOLL_MAX +} tulip_mediapoll_event_t; + +typedef enum { + TULIP_LINK_DOWN, /* Link is down */ + TULIP_LINK_UP, /* link is ok */ + TULIP_LINK_UNKNOWN /* we can't tell either way */ +} tulip_link_status_t; + + +/* + * This data structure is used to abstract out the quirks. + * media_probe = tries to determine the media type. + * media_select = enables the current media (or autosenses) + * media_poll = autosenses media + * media_preset = 21140, etal requires bit to set before the + * the software reset; hence pre-set. Should be + * pre-reset but that's ugly. + */ + +typedef struct { + tulip_board_t bd_type; + void (*bd_media_probe)(tulip_softc_t * const sc); + void (*bd_media_select)(tulip_softc_t * const sc); + void (*bd_media_poll)(tulip_softc_t * const sc, tulip_mediapoll_event_t event); + void (*bd_media_preset)(tulip_softc_t * const sc); +} tulip_boardsw_t; + +/* + * The next few declarations are for MII/PHY based board. + * + * The first enumeration identifies a superset of various datums + * that can be obtained from various PHY chips. Not all PHYs will + * support all datums. + * The modedata structure indicates what register contains + * a datum, what mask is applied the register contents, and what the + * result should be. + * The attr structure records information about a supported PHY. + * The phy structure records information about a PHY instance. + */ + +typedef enum { + PHY_MODE_10T, + PHY_MODE_100TX, + PHY_MODE_100T4, + PHY_MODE_FULLDUPLEX, + PHY_MODE_MAX +} tulip_phy_mode_t; + +typedef struct { + u_int16_t pm_regno; + u_int16_t pm_mask; + u_int16_t pm_value; +} tulip_phy_modedata_t; + +typedef struct { + u_int32_t attr_id; + u_int16_t attr_flags; +#define PHY_NEED_HARD_RESET 0x0001 +#define PHY_DUAL_CYCLE_TA 0x0002 + tulip_phy_modedata_t attr_modes[PHY_MODE_MAX]; +#ifdef TULIP_DEBUG + const char *attr_name; +#endif +} tulip_phy_attr_t; + +/* + * Various probe states used when trying to autosense the media. + */ + +typedef enum { + TULIP_PROBE_INACTIVE, + TULIP_PROBE_PHYRESET, + TULIP_PROBE_PHYAUTONEG, + TULIP_PROBE_GPRTEST, + TULIP_PROBE_MEDIATEST, + TULIP_PROBE_FAILED +} tulip_probe_state_t; + +typedef struct { + /* + * Transmit Statistics + */ + u_int32_t dot3StatsSingleCollisionFrames; + u_int32_t dot3StatsMultipleCollisionFrames; + u_int32_t dot3StatsSQETestErrors; + u_int32_t dot3StatsDeferredTransmissions; + u_int32_t dot3StatsLateCollisions; + u_int32_t dot3StatsExcessiveCollisions; + u_int32_t dot3StatsCarrierSenseErrors; + u_int32_t dot3StatsInternalMacTransmitErrors; + u_int32_t dot3StatsInternalTransmitUnderflows; /* not in rfc1650! */ + u_int32_t dot3StatsInternalTransmitBabbles; /* not in rfc1650! */ + /* + * Receive Statistics + */ + u_int32_t dot3StatsMissedFrames; /* not in rfc1650! */ + u_int32_t dot3StatsAlignmentErrors; + u_int32_t dot3StatsFCSErrors; + u_int32_t dot3StatsFrameTooLongs; + u_int32_t dot3StatsInternalMacReceiveErrors; +} tulip_dot3_stats_t; + +/* + * Now to important stuff. This is softc structure (where does softc + * come from??? No idea) for the tulip device. + */ +struct _tulip_softc_t { + struct ifmedia tulip_ifmedia; +#if defined(TULIP_BUS_DMA) + bus_dma_tag_t tulip_dmatag; /* bus DMA tag */ +#if !defined(TULIP_BUS_DMA_NOTX) + bus_dmamap_t tulip_setupmap; + bus_dmamap_t tulip_txdescmap; + bus_dmamap_t tulip_txmaps[TULIP_TXDESCS]; + unsigned tulip_txmaps_free; +#endif +#if !defined(TULIP_BUS_DMA_NORX) + bus_dmamap_t tulip_rxdescmap; + bus_dmamap_t tulip_rxmaps[TULIP_RXDESCS]; + unsigned tulip_rxmaps_free; +#endif +#endif + struct arpcom tulip_ac; + bus_space_tag_t tulip_csrs_bst; + bus_space_handle_t tulip_csrs_bsh; + tulip_regfile_t tulip_csrs; + u_int32_t tulip_flags; +#define TULIP_WANTSETUP 0x00000001 +#define TULIP_WANTHASHPERFECT 0x00000002 +#define TULIP_WANTHASHONLY 0x00000004 +#define TULIP_DOINGSETUP 0x00000008 +#define TULIP_PRINTMEDIA 0x00000010 +#define TULIP_TXPROBE_ACTIVE 0x00000020 +#define TULIP_ALLMULTI 0x00000040 +#define TULIP_WANTRXACT 0x00000080 +#define TULIP_RXACT 0x00000100 +#define TULIP_INRESET 0x00000200 +#define TULIP_NEEDRESET 0x00000400 +#define TULIP_SQETEST 0x00000800 +#define TULIP_xxxxxx0 0x00001000 +#define TULIP_xxxxxx1 0x00002000 +#define TULIP_WANTTXSTART 0x00004000 +#define TULIP_NEWTXTHRESH 0x00008000 +#define TULIP_NOAUTOSENSE 0x00010000 +#define TULIP_PRINTLINKUP 0x00020000 +#define TULIP_LINKUP 0x00040000 +#define TULIP_RXBUFSLOW 0x00080000 +#define TULIP_NOMESSAGES 0x00100000 +#define TULIP_SYSTEMERROR 0x00200000 +#define TULIP_TIMEOUTPENDING 0x00400000 +#define TULIP_xxxxxx2 0x00800000 +#define TULIP_TRYNWAY 0x01000000 +#define TULIP_DIDNWAY 0x02000000 +#define TULIP_RXIGNORE 0x04000000 +#define TULIP_PROBE1STPASS 0x08000000 +#define TULIP_DEVICEPROBE 0x10000000 +#define TULIP_PROMISC 0x20000000 +#define TULIP_HASHONLY 0x40000000 +#define TULIP_xxxxxx3 0x80000000 + /* only 4 bits left! */ + u_int32_t tulip_features; /* static bits indicating features of chip */ +#define TULIP_HAVE_GPR 0x00000001 /* have gp register (140[A]) */ +#define TULIP_HAVE_RXBADOVRFLW 0x00000002 /* RX corrupts on overflow */ +#define TULIP_HAVE_POWERMGMT 0x00000004 /* Snooze/sleep modes */ +#define TULIP_HAVE_MII 0x00000008 /* Some medium on MII */ +#define TULIP_HAVE_SIANWAY 0x00000010 /* SIA does NWAY */ +#define TULIP_HAVE_DUALSENSE 0x00000020 /* SIA senses both AUI & TP */ +#define TULIP_HAVE_SIAGP 0x00000040 /* SIA has a GP port */ +#define TULIP_HAVE_BROKEN_HASH 0x00000080 /* Broken Multicast Hash */ +#define TULIP_HAVE_ISVSROM 0x00000100 /* uses ISV SROM Format */ +#define TULIP_HAVE_BASEROM 0x00000200 /* Board ROM can be cloned */ +#define TULIP_HAVE_SLAVEDROM 0x00000400 /* Board ROM cloned */ +#define TULIP_HAVE_SLAVEDINTR 0x00000800 /* Board slaved interrupt */ +#define TULIP_HAVE_SHAREDINTR 0x00001000 /* Board shares interrupts */ +#define TULIP_HAVE_OKROM 0x00002000 /* ROM was recognized */ +#define TULIP_HAVE_NOMEDIA 0x00004000 /* did not detect any media */ +#define TULIP_HAVE_STOREFWD 0x00008000 /* have CMD_STOREFWD */ +#define TULIP_HAVE_SIA100 0x00010000 /* has LS100 in SIA status */ +#define TULIP_HAVE_OKSROM 0x00020000 /* SROM CRC is OK */ + u_int32_t tulip_intrmask; /* our copy of csr_intr */ + u_int32_t tulip_cmdmode; /* our copy of csr_cmdmode */ + u_int32_t tulip_last_system_error : 3; /* last system error (only value is TULIP_SYSTEMERROR is also set) */ + u_int32_t tulip_txtimer : 2; /* transmission timer */ + u_int32_t tulip_system_errors; /* number of system errors encountered */ + u_int32_t tulip_statusbits; /* status bits from CSR5 that may need to be printed */ + + tulip_media_info_t *tulip_mediums[TULIP_MEDIA_MAX]; /* indexes into mediainfo */ + tulip_media_t tulip_media; /* current media type */ + u_int32_t tulip_abilities; /* remote system's abiltities (as defined in IEEE 802.3u) */ + + u_int8_t tulip_revinfo; /* revision of chip */ + u_int8_t tulip_phyaddr; /* 0..31 -- address of current phy */ + u_int8_t tulip_gpinit; /* active pins on 21140 */ + u_int8_t tulip_gpdata; /* default gpdata for 21140 */ + + struct { + u_int8_t probe_count; /* count of probe operations */ + int32_t probe_timeout; /* time in ms of probe timeout */ + tulip_probe_state_t probe_state; /* current media probe state */ + tulip_media_t probe_media; /* current media being probed */ + u_int32_t probe_mediamask; /* medias checked */ + u_int32_t probe_passes; /* times autosense failed */ + u_int32_t probe_txprobes; /* txprobes attempted */ + } tulip_probe; +#define tulip_probe_count tulip_probe.probe_count +#define tulip_probe_timeout tulip_probe.probe_timeout +#define tulip_probe_state tulip_probe.probe_state +#define tulip_probe_media tulip_probe.probe_media +#define tulip_probe_mediamask tulip_probe.probe_mediamask +#define tulip_probe_passes tulip_probe.probe_passes + + tulip_chipid_t tulip_chipid; /* type of chip we are using */ + const tulip_boardsw_t *tulip_boardsw; /* board/chip characteristics */ + tulip_softc_t *tulip_slaves; /* slaved devices (ZX3xx) */ +#if defined(TULIP_DEBUG) + /* + * Debugging/Statistical information + */ + struct { + tulip_media_t dbg_last_media; + u_int32_t dbg_intrs; + u_int32_t dbg_media_probes; + u_int32_t dbg_txprobe_nocarr; + u_int32_t dbg_txprobe_exccoll; + u_int32_t dbg_link_downed; + u_int32_t dbg_link_suspected; + u_int32_t dbg_link_intrs; + u_int32_t dbg_link_pollintrs; + u_int32_t dbg_link_failures; + u_int32_t dbg_nway_starts; + u_int32_t dbg_nway_failures; + u_int16_t dbg_phyregs[32][4]; + u_int32_t dbg_rxlowbufs; + u_int32_t dbg_rxintrs; + u_int32_t dbg_last_rxintrs; + u_int32_t dbg_high_rxintrs_hz; + u_int32_t dbg_no_txmaps; + u_int32_t dbg_txput_finishes[8]; + u_int32_t dbg_txprobes_ok[TULIP_MEDIA_MAX]; + u_int32_t dbg_txprobes_failed[TULIP_MEDIA_MAX]; + u_int32_t dbg_events[TULIP_MEDIAPOLL_MAX]; + u_int32_t dbg_rxpktsperintr[TULIP_RXDESCS]; + } tulip_dbg; +#endif +#if defined(TULIP_PERFSTATS) +#define TULIP_PERF_CURRENT 0 +#define TULIP_PERF_PREVIOUS 1 +#define TULIP_PERF_TOTAL 2 +#define TULIP_PERF_MAX 3 + struct tulip_perfstats { + u_quad_t perf_intr_cycles; + u_quad_t perf_ifstart_cycles; + u_quad_t perf_ifstart_one_cycles; + u_quad_t perf_ifioctl_cycles; + u_quad_t perf_ifwatchdog_cycles; + u_quad_t perf_timeout_cycles; + u_quad_t perf_txput_cycles; + u_quad_t perf_txintr_cycles; + u_quad_t perf_rxintr_cycles; + u_quad_t perf_rxget_cycles; + unsigned perf_intr; + unsigned perf_ifstart; + unsigned perf_ifstart_one; + unsigned perf_ifioctl; + unsigned perf_ifwatchdog; + unsigned perf_timeout; + unsigned perf_txput; + unsigned perf_txintr; + unsigned perf_rxintr; + unsigned perf_rxget; + } tulip_perfstats[TULIP_PERF_MAX]; +#define tulip_curperfstats tulip_perfstats[TULIP_PERF_CURRENT] +#endif + struct ifqueue tulip_txq; + struct ifqueue tulip_rxq; + tulip_dot3_stats_t tulip_dot3stats; + tulip_ringinfo_t tulip_rxinfo; + tulip_ringinfo_t tulip_txinfo; + tulip_media_info_t tulip_mediainfo[10]; + /* + * The setup buffers for sending the setup frame to the chip. + * one is the one being sent while the other is the one being + * filled. + */ + u_int32_t tulip_setupbuf[192/sizeof(u_int32_t)]; + u_int32_t tulip_setupdata[192/sizeof(u_int32_t)]; + char tulip_boardid[16]; /* buffer for board ID */ + u_int8_t tulip_rombuf[128]; + u_int8_t tulip_pci_busno; /* needed for multiport boards */ + u_int8_t tulip_pci_devno; /* needed for multiport boards */ + u_int8_t tulip_connidx; + tulip_srom_connection_t tulip_conntype; + tulip_desc_t *tulip_rxdescs; + tulip_desc_t *tulip_txdescs; +}; + +#define TULIP_DO_AUTOSENSE(sc) (IFM_SUBTYPE((sc)->tulip_ifmedia.ifm_media) == IFM_AUTO) + + +#if defined(TULIP_HDR_DATA) +static const char * const tulip_chipdescs[] = { + "21040 [10Mb/s]", + "21041 [10Mb/s]", + "21140 [10-100Mb/s]", + "21140A [10-100Mb/s]", + "21142 [10-100Mb/s]", + "21143 [10-100Mb/s]", +}; + +static const char * const tulip_mediums[] = { + "unknown", /* TULIP_MEDIA_UNKNOWN */ + "10baseT", /* TULIP_MEDIA_10BASET */ + "Full Duplex 10baseT", /* TULIP_MEDIA_10BASET_FD */ + "BNC", /* TULIP_MEDIA_BNC */ + "AUI", /* TULIP_MEDIA_AUI */ + "External SIA", /* TULIP_MEDIA_EXTSIA */ + "AUI/BNC", /* TULIP_MEDIA_AUIBNC */ + "100baseTX", /* TULIP_MEDIA_100BASET */ + "Full Duplex 100baseTX", /* TULIP_MEDIA_100BASET_FD */ + "100baseT4", /* TULIP_MEDIA_100BASET4 */ + "100baseFX", /* TULIP_MEDIA_100BASEFX */ + "Full Duplex 100baseFX", /* TULIP_MEDIA_100BASEFX_FD */ +}; + +static const int tulip_media_to_ifmedia[] = { + IFM_ETHER | IFM_NONE, /* TULIP_MEDIA_UNKNOWN */ + IFM_ETHER | IFM_10_T, /* TULIP_MEDIA_10BASET */ + IFM_ETHER | IFM_10_T | IFM_FDX, /* TULIP_MEDIA_10BASET_FD */ + IFM_ETHER | IFM_10_2, /* TULIP_MEDIA_BNC */ + IFM_ETHER | IFM_10_5, /* TULIP_MEDIA_AUI */ + IFM_ETHER | IFM_MANUAL, /* TULIP_MEDIA_EXTSIA */ + IFM_ETHER | IFM_10_5, /* TULIP_MEDIA_AUIBNC */ + IFM_ETHER | IFM_100_TX, /* TULIP_MEDIA_100BASET */ + IFM_ETHER | IFM_100_TX | IFM_FDX, /* TULIP_MEDIA_100BASET_FD */ + IFM_ETHER | IFM_100_T4, /* TULIP_MEDIA_100BASET4 */ + IFM_ETHER | IFM_100_FX, /* TULIP_MEDIA_100BASEFX */ + IFM_ETHER | IFM_100_FX | IFM_FDX, /* TULIP_MEDIA_100BASEFX_FD */ +}; + +static const char * const tulip_system_errors[] = { + "parity error", + "master abort", + "target abort", + "reserved #3", + "reserved #4", + "reserved #5", + "reserved #6", + "reserved #7", +}; + +static const char * const tulip_status_bits[] = { + NULL, + "transmit process stopped", + NULL, + "transmit jabber timeout", + + NULL, + "transmit underflow", + NULL, + "receive underflow", + + "receive process stopped", + "receive watchdog timeout", + NULL, + NULL, + + "link failure", + NULL, + NULL, +}; + +static const struct { + tulip_srom_connection_t sc_type; + tulip_media_t sc_media; + u_int32_t sc_attrs; +} tulip_srom_conninfo[] = { + { TULIP_SROM_CONNTYPE_10BASET, TULIP_MEDIA_10BASET }, + { TULIP_SROM_CONNTYPE_BNC, TULIP_MEDIA_BNC }, + { TULIP_SROM_CONNTYPE_AUI, TULIP_MEDIA_AUI }, + { TULIP_SROM_CONNTYPE_100BASETX, TULIP_MEDIA_100BASETX }, + { TULIP_SROM_CONNTYPE_100BASET4, TULIP_MEDIA_100BASET4 }, + { TULIP_SROM_CONNTYPE_100BASEFX, TULIP_MEDIA_100BASEFX }, + { TULIP_SROM_CONNTYPE_MII_10BASET, TULIP_MEDIA_10BASET, + TULIP_SROM_ATTR_MII }, + { TULIP_SROM_CONNTYPE_MII_100BASETX, TULIP_MEDIA_100BASETX, + TULIP_SROM_ATTR_MII }, + { TULIP_SROM_CONNTYPE_MII_100BASET4, TULIP_MEDIA_100BASET4, + TULIP_SROM_ATTR_MII }, + { TULIP_SROM_CONNTYPE_MII_100BASEFX, TULIP_MEDIA_100BASEFX, + TULIP_SROM_ATTR_MII }, + { TULIP_SROM_CONNTYPE_10BASET_NWAY, TULIP_MEDIA_10BASET, + TULIP_SROM_ATTR_NWAY }, + { TULIP_SROM_CONNTYPE_10BASET_FD, TULIP_MEDIA_10BASET_FD }, + { TULIP_SROM_CONNTYPE_MII_10BASET_FD, TULIP_MEDIA_10BASET_FD, + TULIP_SROM_ATTR_MII }, + { TULIP_SROM_CONNTYPE_100BASETX_FD, TULIP_MEDIA_100BASETX_FD }, + { TULIP_SROM_CONNTYPE_MII_100BASETX_FD, TULIP_MEDIA_100BASETX_FD, + TULIP_SROM_ATTR_MII }, + { TULIP_SROM_CONNTYPE_10BASET_NOLINKPASS, TULIP_MEDIA_10BASET, + TULIP_SROM_ATTR_NOLINKPASS }, + { TULIP_SROM_CONNTYPE_AUTOSENSE, TULIP_MEDIA_UNKNOWN, + TULIP_SROM_ATTR_AUTOSENSE }, + { TULIP_SROM_CONNTYPE_AUTOSENSE_POWERUP, TULIP_MEDIA_UNKNOWN, + TULIP_SROM_ATTR_AUTOSENSE|TULIP_SROM_ATTR_POWERUP }, + { TULIP_SROM_CONNTYPE_AUTOSENSE_NWAY, TULIP_MEDIA_UNKNOWN, + TULIP_SROM_ATTR_AUTOSENSE|TULIP_SROM_ATTR_NWAY }, + { TULIP_SROM_CONNTYPE_NOT_USED, TULIP_MEDIA_UNKNOWN } +}; +#define TULIP_SROM_LASTCONNIDX \ + (sizeof(tulip_srom_conninfo)/sizeof(tulip_srom_conninfo[0]) - 1) + +static const struct { + tulip_media_t sm_type; + tulip_srom_media_t sm_srom_type; +} tulip_srom_mediums[] = { + { TULIP_MEDIA_100BASEFX_FD, TULIP_SROM_MEDIA_100BASEFX_FD }, + { TULIP_MEDIA_100BASEFX, TULIP_SROM_MEDIA_100BASEFX }, + { TULIP_MEDIA_100BASET4, TULIP_SROM_MEDIA_100BASET4 }, + { TULIP_MEDIA_100BASETX_FD, TULIP_SROM_MEDIA_100BASETX_FD }, + { TULIP_MEDIA_100BASETX, TULIP_SROM_MEDIA_100BASETX }, + { TULIP_MEDIA_10BASET_FD, TULIP_SROM_MEDIA_10BASET_FD }, + { TULIP_MEDIA_AUI, TULIP_SROM_MEDIA_AUI }, + { TULIP_MEDIA_BNC, TULIP_SROM_MEDIA_BNC }, + { TULIP_MEDIA_10BASET, TULIP_SROM_MEDIA_10BASET }, + { TULIP_MEDIA_UNKNOWN } +}; +#endif /* TULIP_HDR_DATA */ + +/* + * This driver supports a maximum of 32 tulip boards. + * This should be enough for the forseeable future. + */ +#define TULIP_MAX_DEVICES 32 + +#if defined(TULIP_USE_SOFTINTR) && defined(TULIP_HDR_DATA) +static u_int32_t tulip_softintr_mask; +static int tulip_softintr_last_unit; +static int tulip_softintr_max_unit; +static void tulip_softintr(void); +#endif + +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NORX) +#define TULIP_RXDESC_PRESYNC(sc, di, s) \ + bus_dmamap_sync((sc)->tulip_dmatag, (sc)->tulip_rxdescmap, \ + (caddr_t) di - (caddr_t) (sc)->tulip_rxdescs, \ + (s), BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE) +#define TULIP_RXDESC_POSTSYNC(sc, di, s) \ + bus_dmamap_sync((sc)->tulip_dmatag, (sc)->tulip_rxdescmap, \ + (caddr_t) di - (caddr_t) (sc)->tulip_rxdescs, \ + (s), BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE) +#define TULIP_RXMAP_PRESYNC(sc, map) \ + bus_dmamap_sync((sc)->tulip_dmatag, (map), 0, (map)->dm_mapsize, \ + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE) +#define TULIP_RXMAP_POSTSYNC(sc, map) \ + bus_dmamap_sync((sc)->tulip_dmatag, (map), 0, (map)->dm_mapsize, \ + BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE) +#define TULIP_RXMAP_CREATE(sc, mapp) \ + bus_dmamap_create((sc)->tulip_dmatag, TULIP_RX_BUFLEN, 2, \ + TULIP_DATA_PER_DESC, 0, \ + BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, (mapp)) +#else +#ifdef __alpha__ +#define TULIP_RXDESC_PRESYNC(sc, di, s) alpha_mb() +#define TULIP_RXDESC_POSTSYNC(sc, di, s) alpha_mb() +#define TULIP_RXMAP_PRESYNC(sc, map) alpha_mb() +#define TULIP_RXMAP_POSTSYNC(sc, map) alpha_mb() +#else +#define TULIP_RXDESC_PRESYNC(sc, di, s) do { } while (0) +#define TULIP_RXDESC_POSTSYNC(sc, di, s) do { } while (0) +#define TULIP_RXMAP_PRESYNC(sc, map) do { } while (0) +#define TULIP_RXMAP_POSTSYNC(sc, map) do { } while (0) +#endif +#define TULIP_RXMAP_CREATE(sc, mapp) do { } while (0) +#endif + +#if defined(TULIP_BUS_DMA) && !defined(TULIP_BUS_DMA_NOTX) +#define TULIP_TXDESC_PRESYNC(sc, di, s) \ + bus_dmamap_sync((sc)->tulip_dmatag, (sc)->tulip_txdescmap, \ + (caddr_t) di - (caddr_t) (sc)->tulip_txdescs, \ + (s), BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE) +#define TULIP_TXDESC_POSTSYNC(sc, di, s) \ + bus_dmamap_sync((sc)->tulip_dmatag, (sc)->tulip_txdescmap, \ + (caddr_t) di - (caddr_t) (sc)->tulip_txdescs, \ + (s), BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE) +#define TULIP_TXMAP_PRESYNC(sc, map) \ + bus_dmamap_sync((sc)->tulip_dmatag, (map), 0, (map)->dm_mapsize, \ + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE) +#define TULIP_TXMAP_POSTSYNC(sc, map) \ + bus_dmamap_sync((sc)->tulip_dmatag, (map), 0, (map)->dm_mapsize, \ + BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE) +#define TULIP_TXMAP_CREATE(sc, mapp) \ + bus_dmamap_create((sc)->tulip_dmatag, TULIP_DATA_PER_DESC, \ + TULIP_MAX_TXSEG, TULIP_DATA_PER_DESC, \ + 0, BUS_DMA_NOWAIT, (mapp)) +#else +#ifdef __alpha__ +#define TULIP_TXDESC_PRESYNC(sc, di, s) alpha_mb() +#define TULIP_TXDESC_POSTSYNC(sc, di, s) alpha_mb() +#define TULIP_TXMAP_PRESYNC(sc, map) alpha_mb() +#define TULIP_TXMAP_POSTSYNC(sc, map) alpha_mb() +#else +#define TULIP_TXDESC_PRESYNC(sc, di, s) do { } while (0) +#define TULIP_TXDESC_POSTSYNC(sc, di, s) do { } while (0) +#define TULIP_TXMAP_PRESYNC(sc, map) do { } while (0) +#define TULIP_TXMAP_POSTSYNC(sc, map) do { } while (0) +#endif +#define TULIP_TXMAP_CREATE(sc, mapp) do { } while (0) +#endif + +#ifdef notyet +#define SIOCGADDRROM _IOW('i', 240, struct ifreq) /* get 128 bytes of ROM */ +#define SIOCGCHIPID _IOWR('i', 241, struct ifreq) /* get chipid */ +#endif + +#if defined(TULIP_HDR_DATA) +static tulip_softc_t *tulips[TULIP_MAX_DEVICES]; +#endif + +#if defined(TULIP_USE_SOFTINTR) +NETISR_SET(NETISR_DE, tulip_softintr); +#endif + +#define loudprintf if (bootverbose) printf + +#ifndef tulip_if +#define tulip_if tulip_ac.ac_if +#endif +#ifndef tulip_unit +#define tulip_unit tulip_if.if_unit +#endif +#define tulip_name tulip_if.if_name +#ifndef tulip_enaddr +#define tulip_enaddr tulip_ac.ac_enaddr +#endif + +#if !defined(TULIP_KVATOPHYS) && (!defined(TULIP_BUS_DMA) || defined(TULIP_BUS_DMA_NORX) || defined(TULIP_BUS_DMA_NOTX)) +#if defined(__alpha__) +/* XXX XXX NEED REAL DMA MAPPING SUPPORT XXX XXX */ +#define vtobus(va) alpha_XXX_dmamap((vm_offset_t)va) +#else +#define vtobus(va) vtophys(va) +#endif +#define TULIP_KVATOPHYS(sc, va) vtobus(va) +#endif + +#if defined(TULIP_PERFSTATS) +#define TULIP_PERFMERGE(sc, member) \ + do { (sc)->tulip_perfstats[TULIP_PERF_TOTAL].member \ + += (sc)->tulip_perfstats[TULIP_PERF_CURRENT].member; \ + (sc)->tulip_perfstats[TULIP_PERF_PREVIOUS].member \ + = (sc)->tulip_perfstats[TULIP_PERF_CURRENT].member; \ + (sc)->tulip_perfstats[TULIP_PERF_CURRENT].member = 0; } while (0) +#define TULIP_PERFSTART(name) const tulip_cycle_t perfstart_ ## name = TULIP_PERFREAD(); +#define TULIP_PERFEND(name) do { \ + (sc)->tulip_curperfstats.perf_ ## name ## _cycles += TULIP_PERFDIFF(perfstart_ ## name, TULIP_PERFREAD()); \ + (sc)->tulip_curperfstats.perf_ ## name ++; \ + } while (0) +#if defined(__i386__) +typedef u_quad_t tulip_cycle_t; +static __inline__ tulip_cycle_t +TULIP_PERFREAD( + void) +{ + tulip_cycle_t x; + __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); + return x; +} +#define TULIP_PERFDIFF(s, f) ((f) - (s)) +#elif defined(__alpha__) +typedef unsigned long tulip_cycle_t; +static __inline__ tulip_cycle_t +TULIP_PERFREAD( + void) +{ + tulip_cycle_t x; + __asm__ volatile ("rpcc %0" : "=r" (x)); + return x; +} +#define TULIP_PERFDIFF(s, f) ((unsigned int) ((f) - (s))) +#endif +#else +#define TULIP_PERFSTART(name) +#define TULIP_PERFEND(name) do { } while (0) +#define TULIP_PERFMERGE(s,n) do { } while (0) +#endif /* TULIP_PERFSTATS */ + +#define TULIP_CRC32_POLY 0xEDB88320UL /* CRC-32 Poly -- Little Endian */ +#define TULIP_MAX_TXSEG 30 + +#define TULIP_ADDREQUAL(a1, a2) \ + (((u_int16_t *)a1)[0] == ((u_int16_t *)a2)[0] \ + && ((u_int16_t *)a1)[1] == ((u_int16_t *)a2)[1] \ + && ((u_int16_t *)a1)[2] == ((u_int16_t *)a2)[2]) +#define TULIP_ADDRBRDCST(a1) \ + (((u_int16_t *)a1)[0] == 0xFFFFU \ + && ((u_int16_t *)a1)[1] == 0xFFFFU \ + && ((u_int16_t *)a1)[2] == 0xFFFFU) + +#endif /* !defined(_DEVAR_H) */ diff --git a/sys/pci/if_en_pci.c b/sys/pci/if_en_pci.c new file mode 100644 index 0000000..4b99da7 --- /dev/null +++ b/sys/pci/if_en_pci.c @@ -0,0 +1,485 @@ +/* $NetBSD: if_en_pci.c,v 1.1 1996/06/22 02:00:31 chuck Exp $ */ + +/* + * + * Copyright (c) 1996 Charles D. Cranor and Washington University. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Charles D. Cranor and + * Washington University. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * + * i f _ e n _ p c i . c + * + * author: Chuck Cranor <chuck@ccrc.wustl.edu> + * started: spring, 1996. + * + * FreeBSD PCI glue for the eni155p card. + * thanks to Matt Thomas for figuring out FreeBSD vs NetBSD vs etc.. diffs. + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/socket.h> + +#include <sys/bus.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <net/if.h> + +#include <pci/pcivar.h> +#include <pci/pcireg.h> + +#include <dev/en/midwayreg.h> +#include <dev/en/midwayvar.h> + +/* + * prototypes + */ + +static int en_pci_probe(device_t); +static int en_pci_attach(device_t); +static int en_pci_detach(device_t); +static int en_pci_shutdown(device_t); + +/* + * local structures + */ + +struct en_pci_softc { + /* bus independent stuff */ + struct en_softc esc; /* includes "device" structure */ + + /* freebsd newbus glue */ + struct resource *res; /* resource descriptor for registers */ + struct resource *irq; /* resource descriptor for interrupt */ + void *ih; /* interrupt handle */ +}; + +#if !defined(MIDWAY_ENIONLY) +static void eni_get_macaddr(device_t, struct en_pci_softc *); +#endif +#if !defined(MIDWAY_ADPONLY) +static void adp_get_macaddr(struct en_pci_softc *); +#endif + +/* + * local defines (PCI specific stuff) + */ + +/* + * address of config base memory address register in PCI config space + * (this is card specific) + */ + +#define PCI_CBMA 0x10 + +/* + * tonga (pci bridge). ENI cards only! + */ + +#define EN_TONGA 0x60 /* PCI config addr of tonga reg */ + +#define TONGA_SWAP_DMA 0x80 /* endian swap control */ +#define TONGA_SWAP_BYTE 0x40 +#define TONGA_SWAP_WORD 0x20 + +/* + * adaptec pci bridge. ADP cards only! + */ + +#define ADP_PCIREG 0x050040 /* PCI control register */ + +#define ADP_PCIREG_RESET 0x1 /* reset card */ +#define ADP_PCIREG_IENABLE 0x2 /* interrupt enable */ +#define ADP_PCIREG_SWAP_WORD 0x4 /* swap byte on slave access */ +#define ADP_PCIREG_SWAP_DMA 0x8 /* swap byte on DMA */ + +#define PCI_VENDOR_EFFICIENTNETS 0x111a /* Efficent Networks */ +#define PCI_PRODUCT_EFFICIENTNETS_ENI155PF 0x0000 /* ENI-155P ATM */ +#define PCI_PRODUCT_EFFICIENTNETS_ENI155PA 0x0002 /* ENI-155P ATM */ +#define PCI_VENDOR_ADP 0x9004 /* adaptec */ +#define PCI_PRODUCT_ADP_AIC5900 0x5900 +#define PCI_PRODUCT_ADP_AIC5905 0x5905 +#define PCI_VENDOR(x) ((x) & 0xFFFF) +#define PCI_CHIPID(x) (((x) >> 16) & 0xFFFF) + +#if !defined(MIDWAY_ENIONLY) + +static void adp_busreset(void *); + +/* + * bus specific reset function [ADP only!] + */ + +static void adp_busreset(v) + +void *v; + +{ + struct en_softc *sc = (struct en_softc *) v; + u_int32_t dummy; + + bus_space_write_4(sc->en_memt, sc->en_base, ADP_PCIREG, ADP_PCIREG_RESET); + DELAY(1000); /* let it reset */ + dummy = bus_space_read_4(sc->en_memt, sc->en_base, ADP_PCIREG); + bus_space_write_4(sc->en_memt, sc->en_base, ADP_PCIREG, + (ADP_PCIREG_SWAP_WORD|ADP_PCIREG_SWAP_DMA|ADP_PCIREG_IENABLE)); + dummy = bus_space_read_4(sc->en_memt, sc->en_base, ADP_PCIREG); + if ((dummy & (ADP_PCIREG_SWAP_WORD|ADP_PCIREG_SWAP_DMA)) != + (ADP_PCIREG_SWAP_WORD|ADP_PCIREG_SWAP_DMA)) + printf("adp_busreset: Adaptec ATM did NOT reset!\n"); +} +#endif + +/***********************************************************************/ + +/* + * autoconfig stuff + */ + +static int +en_pci_probe(device_t dev) +{ + switch (pci_get_vendor(dev)) { +#if !defined(MIDWAY_ADPONLY) + case PCI_VENDOR_EFFICIENTNETS: + switch (pci_get_device(dev)) { + case PCI_PRODUCT_EFFICIENTNETS_ENI155PF: + case PCI_PRODUCT_EFFICIENTNETS_ENI155PA: + device_set_desc(dev, "Efficient Networks ENI-155p"); + return 0; + } + break; +#endif +#if !defined(MIDWAY_ENIONLY) + case PCI_VENDOR_ADP: + switch (pci_get_device(dev)) { + case PCI_PRODUCT_ADP_AIC5900: + case PCI_PRODUCT_ADP_AIC5905: + device_set_desc(dev, "Adaptec 155 ATM"); + return 0; + } + break; +#endif + } + return ENXIO; +} + +static int +en_pci_attach(device_t dev) +{ + struct en_softc *sc; + struct en_pci_softc *scp; + u_long val; + int rid, s, unit, error = 0; + + sc = device_get_softc(dev); + scp = (struct en_pci_softc *)sc; + bzero(scp, sizeof(*scp)); /* zero */ + + s = splimp(); + + /* + * Enable bus mastering. + */ + val = pci_read_config(dev, PCIR_COMMAND, 2); + val |= (PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); + pci_write_config(dev, PCIR_COMMAND, val, 2); + + /* + * Map control/status registers. + */ + rid = PCI_CBMA; + scp->res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!scp->res) { + device_printf(dev, "could not map memory\n"); + error = ENXIO; + goto fail; + } + + sc->en_memt = rman_get_bustag(scp->res); + sc->en_base = rman_get_bushandle(scp->res); + + /* + * Allocate our interrupt. + */ + rid = 0; + scp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + if (scp->irq == NULL) { + device_printf(dev, "could not map interrupt\n"); + bus_release_resource(dev, SYS_RES_MEMORY, PCI_CBMA, scp->res); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, scp->irq, INTR_TYPE_NET, + en_intr, sc, &scp->ih); + if (error) { + device_printf(dev, "could not setup irq\n"); + bus_release_resource(dev, SYS_RES_IRQ, 0, scp->irq); + bus_release_resource(dev, SYS_RES_MEMORY, PCI_CBMA, scp->res); + goto fail; + } + sc->ipl = 1; /* XXX (required to enable interrupt on midway) */ + + unit = device_get_unit(dev); + snprintf(sc->sc_dev.dv_xname, sizeof(sc->sc_dev.dv_xname), "en%d", unit); + sc->enif.if_unit = unit; + sc->enif.if_name = "en"; + + /* figure out if we are an adaptec card or not */ + sc->is_adaptec = (pci_get_vendor(dev) == PCI_VENDOR_ADP) ? 1 : 0; + + /* + * set up pci bridge + */ +#if !defined(MIDWAY_ENIONLY) + if (sc->is_adaptec) { + adp_get_macaddr(scp); + sc->en_busreset = adp_busreset; + adp_busreset(sc); + } +#endif + +#if !defined(MIDWAY_ADPONLY) + if (!sc->is_adaptec) { + eni_get_macaddr(dev, scp); + sc->en_busreset = NULL; + pci_write_config(dev, EN_TONGA, (TONGA_SWAP_DMA|TONGA_SWAP_WORD), 4); + } +#endif + + /* + * done PCI specific stuff + */ + + en_attach(sc); + + splx(s); + + return 0; + + fail: + splx(s); + return error; +} + +static int +en_pci_detach(device_t dev) +{ + struct en_softc *sc = device_get_softc(dev); + struct en_pci_softc *scp = (struct en_pci_softc *)sc; + int s; + + s = splimp(); + + /* + * Close down routes etc. + */ + if_detach(&sc->enif); + + /* + * Stop DMA and drop transmit queue. + */ + en_reset(sc); + + /* + * Deallocate resources. + */ + bus_teardown_intr(dev, scp->irq, scp->ih); + bus_release_resource(dev, SYS_RES_IRQ, 0, scp->irq); + bus_release_resource(dev, SYS_RES_MEMORY, PCI_CBMA, scp->res); + +#ifdef notyet + /* + * Free all the driver internal resources + */ +#endif + + splx(s); + + return 0; +} + +static int +en_pci_shutdown(device_t dev) +{ + struct en_pci_softc *psc = (struct en_pci_softc *)device_get_softc(dev); + + en_reset(&psc->esc); + DELAY(10); /* is this necessary? */ + return (0); +} + +#if !defined(MIDWAY_ENIONLY) + +#if defined(sparc) +#define bus_space_read_1(t, h, o) \ + ((void)t, (*(volatile u_int8_t *)((h) + (o)))) +#endif + +static void +adp_get_macaddr(scp) + struct en_pci_softc *scp; +{ + struct en_softc * sc = (struct en_softc *)scp; + int lcv; + + for (lcv = 0; lcv < sizeof(sc->macaddr); lcv++) + sc->macaddr[lcv] = bus_space_read_1(sc->en_memt, sc->en_base, + MID_ADPMACOFF + lcv); +} + +#endif /* MIDWAY_ENIONLY */ + +#if !defined(MIDWAY_ADPONLY) + +/* + * Read station (MAC) address from serial EEPROM. + * derived from linux drivers/atm/eni.c by Werner Almesberger, EPFL LRC. + */ +#define EN_PROM_MAGIC 0x0c +#define EN_PROM_DATA 0x02 +#define EN_PROM_CLK 0x01 +#define EN_ESI 64 + +static void +eni_get_macaddr(device_t dev, struct en_pci_softc *scp) +{ + struct en_softc * sc = (struct en_softc *)scp; + int i, j, address, status; + u_int32_t data, t_data; + u_int8_t tmp; + + t_data = pci_read_config(dev, EN_TONGA, 4) & 0xffffff00; + + data = EN_PROM_MAGIC | EN_PROM_DATA | EN_PROM_CLK; + pci_write_config(dev, EN_TONGA, data, 4); + + for (i = 0; i < sizeof(sc->macaddr); i ++){ + /* start operation */ + data |= EN_PROM_DATA ; + pci_write_config(dev, EN_TONGA, data, 4); + data |= EN_PROM_CLK ; + pci_write_config(dev, EN_TONGA, data, 4); + data &= ~EN_PROM_DATA ; + pci_write_config(dev, EN_TONGA, data, 4); + data &= ~EN_PROM_CLK ; + pci_write_config(dev, EN_TONGA, data, 4); + /* send address with serial line */ + address = ((i + EN_ESI) << 1) + 1; + for ( j = 7 ; j >= 0 ; j --){ + data = (address >> j) & 1 ? data | EN_PROM_DATA : + data & ~EN_PROM_DATA; + pci_write_config(dev, EN_TONGA, data, 4); + data |= EN_PROM_CLK ; + pci_write_config(dev, EN_TONGA, data, 4); + data &= ~EN_PROM_CLK ; + pci_write_config(dev, EN_TONGA, data, 4); + } + /* get ack */ + data |= EN_PROM_DATA ; + pci_write_config(dev, EN_TONGA, data, 4); + data |= EN_PROM_CLK ; + pci_write_config(dev, EN_TONGA, data, 4); + data = pci_read_config(dev, EN_TONGA, 4); + status = data & EN_PROM_DATA; + data &= ~EN_PROM_CLK ; + pci_write_config(dev, EN_TONGA, data, 4); + data |= EN_PROM_DATA ; + pci_write_config(dev, EN_TONGA, data, 4); + + tmp = 0; + + for ( j = 7 ; j >= 0 ; j --){ + tmp <<= 1; + data |= EN_PROM_DATA ; + pci_write_config(dev, EN_TONGA, data, 4); + data |= EN_PROM_CLK ; + pci_write_config(dev, EN_TONGA, data, 4); + data = pci_read_config(dev, EN_TONGA, 4); + if(data & EN_PROM_DATA) tmp |= 1; + data &= ~EN_PROM_CLK ; + pci_write_config(dev, EN_TONGA, data, 4); + data |= EN_PROM_DATA ; + pci_write_config(dev, EN_TONGA, data, 4); + } + /* get ack */ + data |= EN_PROM_DATA ; + pci_write_config(dev, EN_TONGA, data, 4); + data |= EN_PROM_CLK ; + pci_write_config(dev, EN_TONGA, data, 4); + data = pci_read_config(dev, EN_TONGA, 4); + status = data & EN_PROM_DATA; + data &= ~EN_PROM_CLK ; + pci_write_config(dev, EN_TONGA, data, 4); + data |= EN_PROM_DATA ; + pci_write_config(dev, EN_TONGA, data, 4); + + sc->macaddr[i] = tmp; + } + /* stop operation */ + data &= ~EN_PROM_DATA; + pci_write_config(dev, EN_TONGA, data, 4); + data |= EN_PROM_CLK; + pci_write_config(dev, EN_TONGA, data, 4); + data |= EN_PROM_DATA; + pci_write_config(dev, EN_TONGA, data, 4); + pci_write_config(dev, EN_TONGA, t_data, 4); +} + +#endif /* !MIDWAY_ADPONLY */ + +static device_method_t en_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, en_pci_probe), + DEVMETHOD(device_attach, en_pci_attach), + DEVMETHOD(device_detach, en_pci_detach), + DEVMETHOD(device_shutdown, en_pci_shutdown), + + { 0, 0 } +}; + +static driver_t en_driver = { + "en", + en_methods, + sizeof(struct en_pci_softc), +}; + +static devclass_t en_devclass; + +DRIVER_MODULE(if_en, pci, en_driver, en_devclass, 0, 0); + diff --git a/sys/pci/if_mn.c b/sys/pci/if_mn.c new file mode 100644 index 0000000..8d19e22 --- /dev/null +++ b/sys/pci/if_mn.c @@ -0,0 +1,1470 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + * + * $Id: if_mn.c,v 1.1 1999/02/01 13:06:40 phk Exp $ + * + * Driver for Siemens reference design card "Easy321-R1". + * + * This card contains a FALC54 E1/T1 framer and a MUNICH32X 32-channel HDLC + * controller. + * + * The driver supports E1 mode with up to 31 channels. We send CRC4 but don't + * check it coming in. + * + * The FALC54 and MUNICH32X have far too many registers and weird modes for + * comfort, so I have not bothered typing it all into a "fooreg.h" file, + * you will (badly!) need the documentation anyway if you want to mess with + * this gadget. + * + * $FreeBSD$ + */ + +/* + * Stuff to describe the MUNIC32X and FALC54 chips. + */ + +#define M32_CHAN 32 /* We have 32 channels */ +#define M32_TS 32 /* We have 32 timeslots */ + +#define NG_MN_NODE_TYPE "mn" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/sysctl.h> +#include <sys/bus.h> +#include <sys/mbuf.h> +#include <sys/systm.h> +#include <sys/malloc.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> +#include "pci_if.h" + +#include <machine/bus.h> +#include <machine/resource.h> + +#include <sys/rman.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> + + +static int mn_maxlatency = 1000; +SYSCTL_INT(_debug, OID_AUTO, mn_maxlatency, CTLFLAG_RW, + &mn_maxlatency, 0, + "The number of milliseconds a packet is allowed to spend in the output queue. " + "If the output queue is longer than this number of milliseconds when the packet " + "arrives for output, the packet will be dropped." +); + +#ifndef NMN +/* Most machines don't support more than 4 busmaster PCI slots, if even that many */ +#define NMN 4 +#endif + +/* From: PEB 20321 data sheet, p187, table 22 */ +struct m32xreg { + u_int32_t conf, cmd, stat, imask; + u_int32_t fill10, piqba, piql, fill1c; + u_int32_t mode1, mode2, ccba, txpoll; + u_int32_t tiqba, tiql, riqba, riql; + u_int32_t lconf, lccba, fill48, ltran; + u_int32_t ltiqba, ltiql, lriqba, lriql; + u_int32_t lreg0, lreg1, lreg2, lreg3; + u_int32_t lreg4, lreg5, lre6, lstat; + u_int32_t gpdir, gpdata, gpod, fill8c; + u_int32_t ssccon, sscbr, ssctb, sscrb; + u_int32_t ssccse, sscim, fillab, fillac; + u_int32_t iomcon1, iomcon2, iomstat, fillbc; + u_int32_t iomcit0, iomcit1, iomcir0, iomcir1; + u_int32_t iomtmo, iomrmo, filld8, filldc; + u_int32_t mbcmd, mbdata1, mbdata2, mbdata3; + u_int32_t mbdata4, mbdata5, mbdata6, mbdata7; +}; + +/* From: PEB 2254 data sheet, p80, table 10 */ +struct f54wreg { + u_int16_t xfifo; + u_int8_t cmdr, mode, rah1, rah2, ral1, ral2; + u_int8_t ipc, ccr1, ccr3, pre, rtr1, rtr2, rtr3, rtr4; + u_int8_t ttr1, ttr2, ttr3, ttr4, imr0, imr1, imr2, imr3; + u_int8_t imr4, fill19, fmr0, fmr1, fmr2, loop, xsw, xsp; + u_int8_t xc0, xc1, rc0, rc1, xpm0, xpm1, xpm2, tswm; + u_int8_t test1, idle, xsa4, xsa5, xsa6, xsa7, xsa8, fmr3; + u_int8_t icb1, icb2, icb3, icb4, lim0, lim1, pcd, pcr; + u_int8_t lim2, fill39[7]; + u_int8_t fill40[8]; + u_int8_t fill48[8]; + u_int8_t fill50[8]; + u_int8_t fill58[8]; + u_int8_t dec, fill61, test2, fill63[5]; + u_int8_t fill68[8]; + u_int8_t xs[16]; +}; + +/* From: PEB 2254 data sheet, p117, table 10 */ +struct f54rreg { + u_int16_t rfifo; + u_int8_t fill2, mode, rah1, rah2, ral1, ral2; + u_int8_t ipc, ccr1, ccr3, pre, rtr1, rtr2, rtr3, rtr4; + u_int8_t ttr1, ttr2, ttr3, ttr4, imr0, imr1, imr2, imr3; + u_int8_t imr4, fill19, fmr0, fmr1, fmr2, loop, xsw, xsp; + u_int8_t xc0, xc1, rc0, rc1, xpm0, xpm1, xpm2, tswm; + u_int8_t test, idle, xsa4, xsa5, xsa6, xsa7, xsa8, fmr13; + u_int8_t icb1, icb2, icb3, icb4, lim0, lim1, pcd, pcr; + u_int8_t lim2, fill39[7]; + u_int8_t fill40[8]; + u_int8_t fill48[4], frs0, frs1, rsw, rsp; + u_int16_t fec, cvc, cec1, ebc; + u_int16_t cec2, cec3; + u_int8_t rsa4, rsa5, rsa6, rsa7; + u_int8_t rsa8, rsa6s, tsr0, tsr1, sis, rsis; + u_int16_t rbc; + u_int8_t isr0, isr1, isr2, isr3, fill6c, fill6d, gis, vstr; + u_int8_t rs[16]; +}; + +/* Transmit & receive descriptors */ +struct trxd { + u_int32_t flags; + vm_offset_t next; + vm_offset_t data; + u_int32_t status; /* only used for receive */ + struct mbuf *m; /* software use only */ + struct trxd *vnext; /* software use only */ +}; + +/* Channel specification */ +struct cspec { + u_int32_t flags; + vm_offset_t rdesc; + vm_offset_t tdesc; + u_int32_t itbs; +}; + +struct m32_mem { + vm_offset_t csa; + u_int32_t ccb; + u_int32_t reserve1[2]; + u_int32_t ts[M32_TS]; + struct cspec cs[M32_CHAN]; + vm_offset_t crxd[M32_CHAN]; + vm_offset_t ctxd[M32_CHAN]; +}; + +struct softc; +struct sockaddr; +struct rtentry; + +static int mn_probe (device_t self); +static int mn_attach (device_t self); +static void mn_create_channel(struct softc *sc, int chan); +static int mn_reset(struct softc *sc); +static struct trxd * mn_alloc_desc(void); +static void mn_free_desc(struct trxd *dp); +static void mn_intr(void *xsc); +static u_int32_t mn_parse_ts(const char *s, int *nbit); +#ifdef notyet +static void m32_dump(struct softc *sc); +static void f54_dump(struct softc *sc); +static void mn_fmt_ts(char *p, u_int32_t ts); +#endif /* notyet */ +static void f54_init(struct softc *sc); + +static ng_constructor_t ngmn_constructor; +static ng_rcvmsg_t ngmn_rcvmsg; +static ng_shutdown_t ngmn_shutdown; +static ng_newhook_t ngmn_newhook; +static ng_connect_t ngmn_connect; +static ng_rcvdata_t ngmn_rcvdata; +static ng_disconnect_t ngmn_disconnect; + +static struct ng_type mntypestruct = { + NG_ABI_VERSION, + NG_MN_NODE_TYPE, + NULL, + ngmn_constructor, + ngmn_rcvmsg, + ngmn_shutdown, + ngmn_newhook, + NULL, + ngmn_connect, + ngmn_rcvdata, + ngmn_disconnect, + NULL +}; + +static MALLOC_DEFINE(M_MN, "mn", "Mx driver related"); + +#define NIQB 64 + +struct schan { + enum {DOWN, UP} state; + struct softc *sc; + int chan; + u_int32_t ts; + char name[8]; + struct trxd *r1, *rl; + struct trxd *x1, *xl; + hook_p hook; + + time_t last_recv; + time_t last_rxerr; + time_t last_xmit; + + u_long rx_error; + + u_long short_error; + u_long crc_error; + u_long dribble_error; + u_long long_error; + u_long abort_error; + u_long overflow_error; + + int last_error; + int prev_error; + + u_long tx_pending; + u_long tx_limit; +}; + +enum framing {WHOKNOWS, E1, E1U, T1, T1U}; + +struct softc { + int unit; + device_t dev; + struct resource *irq; + void *intrhand; + enum framing framing; + int nhooks; + void *m0v, *m1v; + vm_offset_t m0p, m1p; + struct m32xreg *m32x; + struct f54wreg *f54w; + struct f54rreg *f54r; + struct m32_mem m32_mem; + u_int32_t tiqb[NIQB]; + u_int32_t riqb[NIQB]; + u_int32_t piqb[NIQB]; + u_int32_t ltiqb[NIQB]; + u_int32_t lriqb[NIQB]; + char name[8]; + u_int32_t falc_irq, falc_state, framer_state; + struct schan *ch[M32_CHAN]; + char nodename[NG_NODELEN + 1]; + node_p node; + + u_long cnt_fec; + u_long cnt_cvc; + u_long cnt_cec1; + u_long cnt_ebc; + u_long cnt_cec2; + u_long cnt_cec3; + u_long cnt_rbc; +}; + +static int +ngmn_constructor(node_p node) +{ + + return (EINVAL); +} + +static int +ngmn_shutdown(node_p nodep) +{ + + return (EINVAL); +} + +static void +ngmn_config(node_p node, char *set, char *ret) +{ + struct softc *sc; + enum framing wframing; + + sc = NG_NODE_PRIVATE(node); + + if (set != NULL) { + if (!strncmp(set, "line ", 5)) { + wframing = sc->framing; + if (!strcmp(set, "line e1")) { + wframing = E1; + } else if (!strcmp(set, "line e1u")) { + wframing = E1U; + } else { + strcat(ret, "ENOGROK\n"); + return; + } + if (wframing == sc->framing) + return; + if (sc->nhooks > 0) { + sprintf(ret, "Cannot change line when %d hooks open\n", sc->nhooks); + return; + } + sc->framing = wframing; +#if 1 + f54_init(sc); +#else + mn_reset(sc); +#endif + } else { + printf("%s CONFIG SET [%s]\n", sc->nodename, set); + strcat(ret, "ENOGROK\n"); + return; + } + } + +} + +static int +ngmn_rcvmsg(node_p node, item_p item, hook_p lasthook) +{ + struct softc *sc; + struct ng_mesg *resp = NULL; + struct schan *sch; + char *s, *r; + int pos, i; + struct ng_mesg *msg; + + NGI_GET_MSG(item, msg); + sc = NG_NODE_PRIVATE(node); + + if (msg->header.typecookie != NGM_GENERIC_COOKIE) { + NG_FREE_ITEM(item); + NG_FREE_MSG(msg); + return (EINVAL); + } + + if (msg->header.cmd != NGM_TEXT_CONFIG && + msg->header.cmd != NGM_TEXT_STATUS) { + NG_FREE_ITEM(item); + NG_FREE_MSG(msg); + return (EINVAL); + } + + NG_MKRESPONSE(resp, msg, sizeof(struct ng_mesg) + NG_TEXTRESPONSE, + M_NOWAIT); + if (resp == NULL) { + NG_FREE_ITEM(item); + NG_FREE_MSG(msg); + return (ENOMEM); + } + + if (msg->header.arglen) + s = (char *)msg->data; + else + s = NULL; + r = (char *)resp->data; + *r = '\0'; + + if (msg->header.cmd == NGM_TEXT_CONFIG) { + ngmn_config(node, s, r); + resp->header.arglen = strlen(r) + 1; + FREE(msg, M_NETGRAPH); + return (0); + } + pos = 0; + pos += sprintf(pos + r,"Framer status %b;\n", sc->framer_state, "\20" + "\40LOS\37AIS\36LFA\35RRA" + "\34AUXP\33NMF\32LMFA\31frs0.0" + "\30frs1.7\27TS16RA\26TS16LOS\25TS16AIS" + "\24TS16LFA\23frs1.2\22XLS\21XLO" + "\20RS1\17rsw.6\16RRA\15RY0" + "\14RY1\13RY2\12RY3\11RY4" + "\10SI1\7SI2\6rsp.5\5rsp.4" + "\4rsp.3\3RSIF\2RS13\1RS15"); + pos += sprintf(pos + r," Framing errors: %lu", sc->cnt_fec); + pos += sprintf(pos + r," Code Violations: %lu\n", sc->cnt_cvc); + + pos += sprintf(pos + r," Falc State %b;\n", sc->falc_state, "\20" + "\40LOS\37AIS\36LFA\35RRA" + "\34AUXP\33NMF\32LMFA\31frs0.0" + "\30frs1.7\27TS16RA\26TS16LOS\25TS16AIS" + "\24TS16LFA\23frs1.2\22XLS\21XLO" + "\20RS1\17rsw.6\16RRA\15RY0" + "\14RY1\13RY2\12RY3\11RY4" + "\10SI1\7SI2\6rsp.5\5rsp.4" + "\4rsp.3\3RSIF\2RS13\1RS15"); + pos += sprintf(pos + r, " Falc IRQ %b\n", sc->falc_irq, "\20" + "\40RME\37RFS\36T8MS\35RMB\34CASC\33CRC4\32SA6SC\31RPF" + "\30b27\27RDO\26ALLS\25XDU\24XMB\23b22\22XLSC\21XPR" + "\20FAR\17LFA\16MFAR\15T400MS\14AIS\13LOS\12RAR\11RA" + "\10ES\7SEC\6LMFA16\5AIS16\4RA16\3API\2SLN\1SLP"); + for (i = 0; i < M32_CHAN; i++) { + if (!sc->ch[i]) + continue; + sch = sc->ch[i]; + + pos += sprintf(r + pos, " Chan %d <%s> ", + i, NG_HOOK_NAME(sch->hook)); + + pos += sprintf(r + pos, " Last Rx: "); + if (sch->last_recv) + pos += sprintf(r + pos, "%lu s", + (unsigned long)(time_second - sch->last_recv)); + else + pos += sprintf(r + pos, "never"); + + pos += sprintf(r + pos, ", last RxErr: "); + if (sch->last_rxerr) + pos += sprintf(r + pos, "%lu s", + (unsigned long)(time_second - sch->last_rxerr)); + else + pos += sprintf(r + pos, "never"); + + pos += sprintf(r + pos, ", last Tx: "); + if (sch->last_xmit) + pos += sprintf(r + pos, "%lu s\n", + (unsigned long)(time_second - sch->last_xmit)); + else + pos += sprintf(r + pos, "never\n"); + + pos += sprintf(r + pos, " RX error(s) %lu", sch->rx_error); + pos += sprintf(r + pos, " Short: %lu", sch->short_error); + pos += sprintf(r + pos, " CRC: %lu", sch->crc_error); + pos += sprintf(r + pos, " Mod8: %lu", sch->dribble_error); + pos += sprintf(r + pos, " Long: %lu", sch->long_error); + pos += sprintf(r + pos, " Abort: %lu", sch->abort_error); + pos += sprintf(r + pos, " Overflow: %lu\n", sch->overflow_error); + + pos += sprintf(r + pos, " Last error: %b Prev error: %b\n", + sch->last_error, "\20\7SHORT\5CRC\4MOD8\3LONG\2ABORT\1OVERRUN", + sch->prev_error, "\20\7SHORT\5CRC\4MOD8\3LONG\2ABORT\1OVERRUN"); + pos += sprintf(r + pos, " Xmit bytes pending %ld\n", + sch->tx_pending); + } + resp->header.arglen = pos + 1; + + /* Take care of synchronous response, if any */ + NG_RESPOND_MSG(i, node, item, resp); + NG_FREE_MSG(msg); + return (0); +} + +static int +ngmn_newhook(node_p node, hook_p hook, const char *name) +{ + u_int32_t ts, chan; + struct softc *sc; + int nbit; + + sc = NG_NODE_PRIVATE(node); + + if (name[0] != 't' || name[1] != 's') + return (EINVAL); + + ts = mn_parse_ts(name + 2, &nbit); + printf("%d bits %x\n", nbit, ts); + if (sc->framing == E1 && (ts & 1)) + return (EINVAL); + if (sc->framing == E1U && nbit != 32) + return (EINVAL); + if (ts == 0) + return (EINVAL); + if (sc->framing == E1) + chan = ffs(ts) - 1; + else + chan = 1; + if (!sc->ch[chan]) + mn_create_channel(sc, chan); + else if (sc->ch[chan]->state == UP) + return (EBUSY); + sc->ch[chan]->ts = ts; + sc->ch[chan]->hook = hook; + sc->ch[chan]->tx_limit = nbit * 8; + NG_HOOK_SET_PRIVATE(hook, sc->ch[chan]); + sc->nhooks++; + return(0); +} + + +static struct trxd *mn_desc_free; + +static struct trxd * +mn_alloc_desc(void) +{ + struct trxd *dp; + + dp = mn_desc_free; + if (dp) + mn_desc_free = dp->vnext; + else + dp = (struct trxd *)malloc(sizeof *dp, M_MN, M_NOWAIT); + return (dp); +} + +static void +mn_free_desc(struct trxd *dp) +{ + dp->vnext = mn_desc_free; + mn_desc_free = dp; +} + +static u_int32_t +mn_parse_ts(const char *s, int *nbit) +{ + unsigned r; + int i, j; + char *p; + + r = 0; + j = -1; + *nbit = 0; + while(*s) { + i = strtol(s, &p, 0); + if (i < 0 || i > 31) + return (0); + while (j != -1 && j < i) { + r |= 1 << j++; + (*nbit)++; + } + j = -1; + r |= 1 << i; + (*nbit)++; + if (*p == ',') { + s = p + 1; + continue; + } else if (*p == '-') { + j = i + 1; + s = p + 1; + continue; + } else if (!*p) { + break; + } else { + return (0); + } + } + return (r); +} + +#ifdef notyet +static void +mn_fmt_ts(char *p, u_int32_t ts) +{ + char *s; + int j; + + s = ""; + ts &= 0xffffffff; + for (j = 0; j < 32; j++) { + if (!(ts & (1 << j))) + continue; + sprintf(p, "%s%d", s, j); + p += strlen(p); + s = ","; + if (!(ts & (1 << (j+1)))) + continue; + for (; j < 32; j++) + if (!(ts & (1 << (j+1)))) + break; + sprintf(p, "-%d", j); + p += strlen(p); + s = ","; + } +} +#endif /* notyet */ + +/* + * OUTPUT + */ + +static int +ngmn_rcvdata(hook_p hook, item_p item) +{ + struct mbuf *m2; + struct trxd *dp, *dp2; + struct schan *sch; + struct softc *sc; + int chan, pitch, len; + struct mbuf *m; + + sch = NG_HOOK_PRIVATE(hook); + sc = sch->sc; + chan = sch->chan; + + if (sch->state != UP) { + NG_FREE_ITEM(item); + return (0); + } + NGI_GET_M(item, m); + if (sch->tx_pending + m->m_pkthdr.len > sch->tx_limit * mn_maxlatency) { + NG_FREE_M(m); + NG_FREE_ITEM(item); + return (0); + } + NG_FREE_ITEM(item); + pitch = 0; + m2 = m; + dp2 = sc->ch[chan]->xl; + len = m->m_pkthdr.len; + while (len) { + dp = mn_alloc_desc(); + if (!dp) { + pitch++; + m_freem(m); + sc->ch[chan]->xl = dp2; + dp = dp2->vnext; + while (dp) { + dp2 = dp->vnext; + mn_free_desc(dp); + dp = dp2; + } + sc->ch[chan]->xl->vnext = 0; + break; + } + dp->data = vtophys(m2->m_data); + dp->flags = m2->m_len << 16; + dp->flags += 1; + len -= m2->m_len; + dp->next = vtophys(dp); + dp->vnext = 0; + sc->ch[chan]->xl->next = vtophys(dp); + sc->ch[chan]->xl->vnext = dp; + sc->ch[chan]->xl = dp; + if (!len) { + dp->m = m; + dp->flags |= 0xc0000000; + dp2->flags &= ~0x40000000; + } else { + dp->m = 0; + m2 = m2->m_next; + } + } + if (pitch) + printf("%s%d: Short on mem, pitched %d packets\n", + sc->name, chan, pitch); + else { +#if 0 + printf("%d = %d + %d (%p)\n", + sch->tx_pending + m->m_pkthdr.len, + sch->tx_pending , m->m_pkthdr.len, m); +#endif + sch->tx_pending += m->m_pkthdr.len; + sc->m32x->txpoll &= ~(1 << chan); + } + return (0); +} + +/* + * OPEN + */ +static int +ngmn_connect(hook_p hook) +{ + int i, nts, chan; + struct trxd *dp, *dp2; + struct mbuf *m; + struct softc *sc; + struct schan *sch; + u_int32_t u; + + sch = NG_HOOK_PRIVATE(hook); + chan = sch->chan; + sc = sch->sc; + + if (sch->state == UP) + return (0); + sch->state = UP; + + /* Count and configure the timeslots for this channel */ + for (nts = i = 0; i < 32; i++) + if (sch->ts & (1 << i)) { + sc->m32_mem.ts[i] = 0x00ff00ff | + (chan << 24) | (chan << 8); + nts++; + } + + /* Init the receiver & xmitter to HDLC */ + sc->m32_mem.cs[chan].flags = 0x80e90006; + /* Allocate two buffers per timeslot */ + if (nts == 32) + sc->m32_mem.cs[chan].itbs = 63; + else + sc->m32_mem.cs[chan].itbs = nts * 2; + + /* Setup a transmit chain with one descriptor */ + /* XXX: we actually send a 1 byte packet */ + dp = mn_alloc_desc(); + MGETHDR(m, M_TRYWAIT, MT_DATA); + if (m == NULL) + return ENOBUFS; + m->m_pkthdr.len = 0; + dp->m = m; + dp->flags = 0xc0000000 + (1 << 16); + dp->next = vtophys(dp); + dp->vnext = 0; + dp->data = vtophys(sc->name); + sc->m32_mem.cs[chan].tdesc = vtophys(dp); + sc->ch[chan]->x1 = dp; + sc->ch[chan]->xl = dp; + + /* Setup a receive chain with 5 + NTS descriptors */ + + dp = mn_alloc_desc(); + m = NULL; + MGETHDR(m, M_TRYWAIT, MT_DATA); + if (m == NULL) { + mn_free_desc(dp); + return (ENOBUFS); + } + MCLGET(m, M_TRYWAIT); + if ((m->m_flags & M_EXT) == 0) { + mn_free_desc(dp); + m_freem(m); + return (ENOBUFS); + } + dp->m = m; + dp->data = vtophys(m->m_data); + dp->flags = 0x40000000; + dp->flags += 1600 << 16; + dp->next = vtophys(dp); + dp->vnext = 0; + sc->ch[chan]->rl = dp; + + for (i = 0; i < (nts + 10); i++) { + dp2 = dp; + dp = mn_alloc_desc(); + m = NULL; + MGETHDR(m, M_TRYWAIT, MT_DATA); + if (m == NULL) { + mn_free_desc(dp); + m_freem(m); + return (ENOBUFS); + } + MCLGET(m, M_TRYWAIT); + if ((m->m_flags & M_EXT) == 0) { + mn_free_desc(dp); + m_freem(m); + return (ENOBUFS); + } + dp->m = m; + dp->data = vtophys(m->m_data); + dp->flags = 0x00000000; + dp->flags += 1600 << 16; + dp->next = vtophys(dp2); + dp->vnext = dp2; + } + sc->m32_mem.cs[chan].rdesc = vtophys(dp); + sc->ch[chan]->r1 = dp; + + /* Initialize this channel */ + sc->m32_mem.ccb = 0x00008000 + (chan << 8); + sc->m32x->cmd = 0x1; + DELAY(1000); + u = sc->m32x->stat; + if (!(u & 1)) + printf("%s: init chan %d stat %08x\n", sc->name, chan, u); + sc->m32x->stat = 1; + /* probably not at splnet, force outward queueing */ + NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); + + return (0); +} + +/* + * CLOSE + */ +static int +ngmn_disconnect(hook_p hook) +{ + int chan, i; + struct softc *sc; + struct schan *sch; + struct trxd *dp, *dp2; + u_int32_t u; + + sch = NG_HOOK_PRIVATE(hook); + chan = sch->chan; + sc = sch->sc; + + if (sch->state == DOWN) + return (0); + sch->state = DOWN; + + /* Set receiver & transmitter off */ + sc->m32_mem.cs[chan].flags = 0x80920006; + sc->m32_mem.cs[chan].itbs = 0; + + /* free the timeslots */ + for (i = 0; i < 32; i++) + if (sc->ch[chan]->ts & (1 << i)) + sc->m32_mem.ts[i] = 0x20002000; + + /* Initialize this channel */ + sc->m32_mem.ccb = 0x00008000 + (chan << 8); + sc->m32x->cmd = 0x1; + DELAY(30); + u = sc->m32x->stat; + if (!(u & 1)) + printf("%s: zap chan %d stat %08x\n", sc->name, chan, u); + sc->m32x->stat = 1; + + /* Free all receive descriptors and mbufs */ + for (dp = sc->ch[chan]->r1; dp ; dp = dp2) { + if (dp->m) + m_freem(dp->m); + sc->ch[chan]->r1 = dp2 = dp->vnext; + mn_free_desc(dp); + } + + /* Free all transmit descriptors and mbufs */ + for (dp = sc->ch[chan]->x1; dp ; dp = dp2) { + if (dp->m) { + sc->ch[chan]->tx_pending -= dp->m->m_pkthdr.len; + m_freem(dp->m); + } + sc->ch[chan]->x1 = dp2 = dp->vnext; + mn_free_desc(dp); + } + sc->nhooks--; + return(0); +} + +/* + * Create a new channel. + */ +static void +mn_create_channel(struct softc *sc, int chan) +{ + struct schan *sch; + + sch = sc->ch[chan] = (struct schan *)malloc(sizeof *sc->ch[chan], + M_MN, M_WAITOK | M_ZERO); + sch->sc = sc; + sch->state = DOWN; + sch->chan = chan; + sprintf(sch->name, "%s%d", sc->name, chan); + return; +} + +#ifdef notyet +/* + * Dump Munich32x state + */ +static void +m32_dump(struct softc *sc) +{ + u_int32_t *tp4; + int i, j; + + printf("mn%d: MUNICH32X dump\n", sc->unit); + tp4 = (u_int32_t *)sc->m0v; + for(j = 0; j < 64; j += 8) { + printf("%02x", j * sizeof *tp4); + for(i = 0; i < 8; i++) + printf(" %08x", tp4[i+j]); + printf("\n"); + } + for(j = 0; j < M32_CHAN; j++) { + if (!sc->ch[j]) + continue; + printf("CH%d: state %d ts %08x", + j, sc->ch[j]->state, sc->ch[j]->ts); + printf(" %08x %08x %08x %08x %08x %08x\n", + sc->m32_mem.cs[j].flags, + sc->m32_mem.cs[j].rdesc, + sc->m32_mem.cs[j].tdesc, + sc->m32_mem.cs[j].itbs, + sc->m32_mem.crxd[j], + sc->m32_mem.ctxd[j] ); + } +} + +/* + * Dump Falch54 state + */ +static void +f54_dump(struct softc *sc) +{ + u_int8_t *tp1; + int i, j; + + printf("%s: FALC54 dump\n", sc->name); + tp1 = (u_int8_t *)sc->m1v; + for(j = 0; j < 128; j += 16) { + printf("%s: %02x |", sc->name, j * sizeof *tp1); + for(i = 0; i < 16; i++) + printf(" %02x", tp1[i+j]); + printf("\n"); + } +} +#endif /* notyet */ + +/* + * Init Munich32x + */ +static void +m32_init(struct softc *sc) +{ + + sc->m32x->conf = 0x00000000; + sc->m32x->mode1 = 0x81048000 + 1600; /* XXX: temp */ +#if 1 + sc->m32x->mode2 = 0x00000081; + sc->m32x->txpoll = 0xffffffff; +#elif 1 + sc->m32x->mode2 = 0x00000081; + sc->m32x->txpoll = 0xffffffff; +#else + sc->m32x->mode2 = 0x00000101; +#endif + sc->m32x->lconf = 0x6060009B; + sc->m32x->imask = 0x00000000; +} + +/* + * Init the Falc54 + */ +static void +f54_init(struct softc *sc) +{ + sc->f54w->ipc = 0x07; + + sc->f54w->xpm0 = 0xbd; + sc->f54w->xpm1 = 0x03; + sc->f54w->xpm2 = 0x00; + + sc->f54w->imr0 = 0x18; /* RMB, CASC */ + sc->f54w->imr1 = 0x08; /* XMB */ + sc->f54w->imr2 = 0x00; + sc->f54w->imr3 = 0x38; /* LMFA16, AIS16, RA16 */ + sc->f54w->imr4 = 0x00; + + sc->f54w->fmr0 = 0xf0; /* X: HDB3, R: HDB3 */ + sc->f54w->fmr1 = 0x0e; /* Send CRC4, 2Mbit, ECM */ + if (sc->framing == E1) + sc->f54w->fmr2 = 0x03; /* Auto Rem-Alarm, Auto resync */ + else if (sc->framing == E1U) + sc->f54w->fmr2 = 0x33; /* dais, rtm, Auto Rem-Alarm, Auto resync */ + + sc->f54w->lim1 = 0xb0; /* XCLK=8kHz, .62V threshold */ + sc->f54w->pcd = 0x0a; + sc->f54w->pcr = 0x15; + sc->f54w->xsw = 0x9f; /* fmr4 */ + if (sc->framing == E1) + sc->f54w->xsp = 0x1c; /* fmr5 */ + else if (sc->framing == E1U) + sc->f54w->xsp = 0x3c; /* tt0, fmr5 */ + sc->f54w->xc0 = 0x07; + sc->f54w->xc1 = 0x3d; + sc->f54w->rc0 = 0x05; + sc->f54w->rc1 = 0x00; + sc->f54w->cmdr = 0x51; +} + +static int +mn_reset(struct softc *sc) +{ + u_int32_t u; + int i; + + sc->m32x->ccba = vtophys(&sc->m32_mem.csa); + sc->m32_mem.csa = vtophys(&sc->m32_mem.ccb); + + bzero(sc->tiqb, sizeof sc->tiqb); + sc->m32x->tiqba = vtophys(&sc->tiqb); + sc->m32x->tiql = NIQB / 16 - 1; + + bzero(sc->riqb, sizeof sc->riqb); + sc->m32x->riqba = vtophys(&sc->riqb); + sc->m32x->riql = NIQB / 16 - 1; + + bzero(sc->ltiqb, sizeof sc->ltiqb); + sc->m32x->ltiqba = vtophys(&sc->ltiqb); + sc->m32x->ltiql = NIQB / 16 - 1; + + bzero(sc->lriqb, sizeof sc->lriqb); + sc->m32x->lriqba = vtophys(&sc->lriqb); + sc->m32x->lriql = NIQB / 16 - 1; + + bzero(sc->piqb, sizeof sc->piqb); + sc->m32x->piqba = vtophys(&sc->piqb); + sc->m32x->piql = NIQB / 16 - 1; + + m32_init(sc); + f54_init(sc); + + u = sc->m32x->stat; + sc->m32x->stat = u; + sc->m32_mem.ccb = 0x4; + sc->m32x->cmd = 0x1; + DELAY(1000); + u = sc->m32x->stat; + sc->m32x->stat = u; + + /* set all timeslots to known state */ + for (i = 0; i < 32; i++) + sc->m32_mem.ts[i] = 0x20002000; + + if (!(u & 1)) { + printf( +"mn%d: WARNING: Controller failed the PCI bus-master test.\n" +"mn%d: WARNING: Use a PCI slot which can support bus-master cards.\n", + sc->unit, sc->unit); + return (0); + } + return (1); +} + +/* + * FALC54 interrupt handling + */ +static void +f54_intr(struct softc *sc) +{ + unsigned g, u, s; + + g = sc->f54r->gis; + u = sc->f54r->isr0 << 24; + u |= sc->f54r->isr1 << 16; + u |= sc->f54r->isr2 << 8; + u |= sc->f54r->isr3; + sc->falc_irq = u; + /* don't chat about the 1 sec heart beat */ + if (u & ~0x40) { +#if 0 + printf("%s*: FALC54 IRQ GIS:%02x %b\n", sc->name, g, u, "\20" + "\40RME\37RFS\36T8MS\35RMB\34CASC\33CRC4\32SA6SC\31RPF" + "\30b27\27RDO\26ALLS\25XDU\24XMB\23b22\22XLSC\21XPR" + "\20FAR\17LFA\16MFAR\15T400MS\14AIS\13LOS\12RAR\11RA" + "\10ES\7SEC\6LMFA16\5AIS16\4RA16\3API\2SLN\1SLP"); +#endif + s = sc->f54r->frs0 << 24; + s |= sc->f54r->frs1 << 16; + s |= sc->f54r->rsw << 8; + s |= sc->f54r->rsp; + sc->falc_state = s; + + s &= ~0x01844038; /* undefined or static bits */ + s &= ~0x00009fc7; /* bits we don't care about */ + s &= ~0x00780000; /* XXX: TS16 related */ + s &= ~0x06000000; /* XXX: Multiframe related */ +#if 0 + printf("%s*: FALC54 Status %b\n", sc->name, s, "\20" + "\40LOS\37AIS\36LFA\35RRA\34AUXP\33NMF\32LMFA\31frs0.0" + "\30frs1.7\27TS16RA\26TS16LOS\25TS16AIS\24TS16LFA\23frs1.2\22XLS\21XLO" + "\20RS1\17rsw.6\16RRA\15RY0\14RY1\13RY2\12RY3\11RY4" + "\10SI1\7SI2\6rsp.5\5rsp.4\4rsp.3\3RSIF\2RS13\1RS15"); +#endif + if (s != sc->framer_state) { +#if 0 + for (i = 0; i < M32_CHAN; i++) { + if (!sc->ch[i]) + continue; + sp = &sc->ch[i]->ifsppp; + if (!(sp->pp_if.if_flags & IFF_UP)) + continue; + if (s) + timeout((timeout_t *)sp->pp_down, sp, 1 * hz); + else + timeout((timeout_t *)sp->pp_up, sp, 1 * hz); + } +#endif + sc->framer_state = s; + } + } + /* Once per second check error counters */ + /* XXX: not clear if this is actually ok */ + if (!(u & 0x40)) + return; + sc->cnt_fec += sc->f54r->fec; + sc->cnt_cvc += sc->f54r->cvc; + sc->cnt_cec1 += sc->f54r->cec1; + sc->cnt_ebc += sc->f54r->ebc; + sc->cnt_cec2 += sc->f54r->cec2; + sc->cnt_cec3 += sc->f54r->cec3; + sc->cnt_rbc += sc->f54r->rbc; +} + +/* + * Transmit interrupt for one channel + */ +static void +mn_tx_intr(struct softc *sc, u_int32_t vector) +{ + u_int32_t chan; + struct trxd *dp; + struct mbuf *m; + + chan = vector & 0x1f; + if (!sc->ch[chan]) + return; + if (sc->ch[chan]->state != UP) { + printf("%s: tx_intr when not UP\n", sc->name); + return; + } + for (;;) { + dp = sc->ch[chan]->x1; + if (vtophys(dp) == sc->m32_mem.ctxd[chan]) + return; + m = dp->m; + if (m) { +#if 0 + printf("%d = %d - %d (%p)\n", + sc->ch[chan]->tx_pending - m->m_pkthdr.len, + sc->ch[chan]->tx_pending , m->m_pkthdr.len, m); +#endif + sc->ch[chan]->tx_pending -= m->m_pkthdr.len; + m_freem(m); + } + sc->ch[chan]->last_xmit = time_second; + sc->ch[chan]->x1 = dp->vnext; + mn_free_desc(dp); + } +} + +/* + * Receive interrupt for one channel + */ +static void +mn_rx_intr(struct softc *sc, u_int32_t vector) +{ + u_int32_t chan, err; + struct trxd *dp; + struct mbuf *m; + struct schan *sch; + + chan = vector & 0x1f; + if (!sc->ch[chan]) + return; + sch = sc->ch[chan]; + if (sch->state != UP) { + printf("%s: rx_intr when not UP\n", sc->name); + return; + } + vector &= ~0x1f; + if (vector == 0x30000b00) + sch->rx_error++; + for (;;) { + dp = sch->r1; + if (vtophys(dp) == sc->m32_mem.crxd[chan]) + return; + m = dp->m; + dp->m = 0; + m->m_pkthdr.len = m->m_len = (dp->status >> 16) & 0x1fff; + err = (dp->status >> 8) & 0xff; + if (!err) { + int error; + NG_SEND_DATA_ONLY(error, sch->hook, m); + sch->last_recv = time_second; + /* we could be down by now... */ + if (sch->state != UP) + return; + } else if (err & 0x40) { + sch->short_error++; + } else if (err & 0x10) { + sch->crc_error++; + } else if (err & 0x08) { + sch->dribble_error++; + } else if (err & 0x04) { + sch->long_error++; + } else if (err & 0x02) { + sch->abort_error++; + } else if (err & 0x01) { + sch->overflow_error++; + } + if (err) { + sch->last_rxerr = time_second; + sch->prev_error = sch->last_error; + sch->last_error = err; + } + + sc->ch[chan]->r1 = dp->vnext; + + /* Replenish desc + mbuf supplies */ + if (!m) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + mn_free_desc(dp); + return; /* ENOBUFS */ + } + MCLGET(m, M_DONTWAIT); + if((m->m_flags & M_EXT) == 0) { + mn_free_desc(dp); + m_freem(m); + return; /* ENOBUFS */ + } + } + dp->m = m; + dp->data = vtophys(m->m_data); + dp->flags = 0x40000000; + dp->flags += 1600 << 16; + dp->next = vtophys(dp); + dp->vnext = 0; + sc->ch[chan]->rl->next = vtophys(dp); + sc->ch[chan]->rl->vnext = dp; + sc->ch[chan]->rl->flags &= ~0x40000000; + sc->ch[chan]->rl = dp; + } +} + + +/* + * Interupt handler + */ + +static void +mn_intr(void *xsc) +{ + struct softc *sc; + u_int32_t stat, lstat, u; + int i, j; + + sc = xsc; + stat = sc->m32x->stat; + lstat = sc->m32x->lstat; +#if 0 + if (!stat && !(lstat & 2)) + return; +#endif + + if (stat & ~0xc200) { + printf("%s: I stat=%08x lstat=%08x\n", sc->name, stat, lstat); + } + + if ((stat & 0x200) || (lstat & 2)) + f54_intr(sc); + + for (j = i = 0; i < 64; i ++) { + u = sc->riqb[i]; + if (u) { + sc->riqb[i] = 0; + mn_rx_intr(sc, u); + if ((u & ~0x1f) == 0x30000800 || (u & ~0x1f) == 0x30000b00) + continue; + u &= ~0x30000400; /* bits we don't care about */ + if ((u & ~0x1f) == 0x00000900) + continue; + if (!(u & ~0x1f)) + continue; + if (!j) + printf("%s*: RIQB:", sc->name); + printf(" [%d]=%08x", i, u); + j++; + } + } + if (j) + printf("\n"); + + for (j = i = 0; i < 64; i ++) { + u = sc->tiqb[i]; + if (u) { + sc->tiqb[i] = 0; + mn_tx_intr(sc, u); + if ((u & ~0x1f) == 0x20000800) + continue; + u &= ~0x20000000; /* bits we don't care about */ + if (!u) + continue; + if (!j) + printf("%s*: TIQB:", sc->name); + printf(" [%d]=%08x", i, u); + j++; + } + } + if (j) + printf("\n"); + sc->m32x->stat = stat; +} + +static void +mn_timeout(void *xsc) +{ + static int round = 0; + struct softc *sc; + + mn_intr(xsc); + sc = xsc; + timeout(mn_timeout, xsc, 10 * hz); + round++; + if (round == 2) { + sc->m32_mem.ccb = 0x00008004; + sc->m32x->cmd = 0x1; + } else if (round > 2) { + printf("%s: timeout\n", sc->name); + } +} + +/* + * PCI initialization stuff + */ + +static int +mn_probe (device_t self) +{ + u_int id = pci_get_devid(self); + + if (sizeof (struct m32xreg) != 256) { + printf("MN: sizeof(struct m32xreg) = %d, should have been 256\n", sizeof (struct m32xreg)); + return (ENXIO); + } + if (sizeof (struct f54rreg) != 128) { + printf("MN: sizeof(struct f54rreg) = %d, should have been 128\n", sizeof (struct f54rreg)); + return (ENXIO); + } + if (sizeof (struct f54wreg) != 128) { + printf("MN: sizeof(struct f54wreg) = %d, should have been 128\n", sizeof (struct f54wreg)); + return (ENXIO); + } + + if (id != 0x2101110a) + return (ENXIO); + + device_set_desc_copy(self, "Munich32X E1/T1 HDLC Controller"); + return (0); +} + +static int +mn_attach (device_t self) +{ + struct softc *sc; + u_int32_t u; + u_int32_t ver; + static int once; + int rid, error; + struct resource *res; + + if (!once) { + if (ng_newtype(&mntypestruct)) + printf("ng_newtype failed\n"); + once++; + } + + sc = (struct softc *)malloc(sizeof *sc, M_MN, M_WAITOK | M_ZERO); + device_set_softc(self, sc); + + sc->dev = self; + sc->unit = device_get_unit(self); + sc->framing = E1; + sprintf(sc->name, "mn%d", sc->unit); + + rid = PCIR_MAPS; + res = bus_alloc_resource(self, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (res == NULL) { + device_printf(self, "Could not map memory\n"); + return ENXIO; + } + sc->m0v = rman_get_virtual(res); + sc->m0p = rman_get_start(res); + + rid = PCIR_MAPS + 4; + res = bus_alloc_resource(self, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (res == NULL) { + device_printf(self, "Could not map memory\n"); + return ENXIO; + } + sc->m1v = rman_get_virtual(res); + sc->m1p = rman_get_start(res); + + /* Allocate interrupt */ + rid = 0; + sc->irq = bus_alloc_resource(self, SYS_RES_IRQ, &rid, 0, ~0, + 1, RF_SHAREABLE | RF_ACTIVE); + + if (sc->irq == NULL) { + printf("couldn't map interrupt\n"); + return(ENXIO); + } + + error = bus_setup_intr(self, sc->irq, INTR_TYPE_NET, mn_intr, sc, &sc->intrhand); + + if (error) { + printf("couldn't set up irq\n"); + return(ENXIO); + } + + u = pci_read_config(self, PCIR_COMMAND, 1); + printf("%x\n", u); + pci_write_config(self, PCIR_COMMAND, u | PCIM_CMD_PERRESPEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN, 1); +#if 0 + pci_write_config(self, PCIR_COMMAND, 0x02800046, 4); +#endif + u = pci_read_config(self, PCIR_COMMAND, 1); + printf("%x\n", u); + + ver = pci_get_revid(self); + + sc->m32x = (struct m32xreg *) sc->m0v; + sc->f54w = (struct f54wreg *) sc->m1v; + sc->f54r = (struct f54rreg *) sc->m1v; + + /* We must reset before poking at FALC54 registers */ + u = mn_reset(sc); + if (!u) + return (0); + + printf("mn%d: Munich32X", sc->unit); + switch (ver) { + case 0x13: + printf(" Rev 2.2"); + break; + default: + printf(" Rev 0x%x\n", ver); + } + printf(", Falc54"); + switch (sc->f54r->vstr) { + case 0: + printf(" Rev < 1.3\n"); + break; + case 1: + printf(" Rev 1.3\n"); + break; + case 2: + printf(" Rev 1.4\n"); + break; + case 0x10: + printf("-LH Rev 1.1\n"); + break; + case 0x13: + printf("-LH Rev 1.3\n"); + break; + default: + printf(" Rev 0x%x\n", sc->f54r->vstr); + } + + if (ng_make_node_common(&mntypestruct, &sc->node) != 0) { + printf("ng_make_node_common failed\n"); + return (0); + } + NG_NODE_SET_PRIVATE(sc->node, sc); + sprintf(sc->nodename, "%s%d", NG_MN_NODE_TYPE, sc->unit); + if (ng_name_node(sc->node, sc->nodename)) { + NG_NODE_UNREF(sc->node); + return (0); + } + + return (0); +} + + +static device_method_t mn_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, mn_probe), + DEVMETHOD(device_attach, mn_attach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + {0, 0} +}; + +static driver_t mn_driver = { + "mn", + mn_methods, + 0 +}; + +static devclass_t mn_devclass; + +DRIVER_MODULE(mn, pci, mn_driver, mn_devclass, 0, 0); diff --git a/sys/pci/if_pcn.c b/sys/pci/if_pcn.c new file mode 100644 index 0000000..f027900 --- /dev/null +++ b/sys/pci/if_pcn.c @@ -0,0 +1,1434 @@ +/* + * Copyright (c) 2000 Berkeley Software Design, Inc. + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul <wpaul@osd.bsdi.com>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * AMD Am79c972 fast ethernet PCI NIC driver. Datatheets are available + * from http://www.amd.com. + * + * Written by Bill Paul <wpaul@osd.bsdi.com> + */ + +/* + * The AMD PCnet/PCI controllers are more advanced and functional + * versions of the venerable 7990 LANCE. The PCnet/PCI chips retain + * backwards compatibility with the LANCE and thus can be made + * to work with older LANCE drivers. This is in fact how the + * PCnet/PCI chips were supported in FreeBSD originally. The trouble + * is that the PCnet/PCI devices offer several performance enhancements + * which can't be exploited in LANCE compatibility mode. Chief among + * these enhancements is the ability to perform PCI DMA operations + * using 32-bit addressing (which eliminates the need for ISA + * bounce-buffering), and special receive buffer alignment (which + * allows the receive handler to pass packets to the upper protocol + * layers without copying on both the x86 and alpha platforms). + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#include <net/bpf.h> + +#include <vm/vm.h> /* for vtophys */ +#include <vm/pmap.h> /* for vtophys */ +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +#define PCN_USEIOSPACE + +#include <pci/if_pcnreg.h> + +MODULE_DEPEND(pcn, miibus, 1, 1, 1); + +/* "controller miibus0" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif + +/* + * Various supported device vendors/types and their names. + */ +static struct pcn_type pcn_devs[] = { + { PCN_VENDORID, PCN_DEVICEID_PCNET, "AMD PCnet/PCI 10/100BaseTX" }, + { PCN_VENDORID, PCN_DEVICEID_HOME, "AMD PCnet/Home HomePNA" }, + { 0, 0, NULL } +}; + +static u_int32_t pcn_csr_read (struct pcn_softc *, int); +static u_int16_t pcn_csr_read16 (struct pcn_softc *, int); +static u_int16_t pcn_bcr_read16 (struct pcn_softc *, int); +static void pcn_csr_write (struct pcn_softc *, int, int); +static u_int32_t pcn_bcr_read (struct pcn_softc *, int); +static void pcn_bcr_write (struct pcn_softc *, int, int); + +static int pcn_probe (device_t); +static int pcn_attach (device_t); +static int pcn_detach (device_t); + +static int pcn_newbuf (struct pcn_softc *, int, struct mbuf *); +static int pcn_encap (struct pcn_softc *, + struct mbuf *, u_int32_t *); +static void pcn_rxeof (struct pcn_softc *); +static void pcn_txeof (struct pcn_softc *); +static void pcn_intr (void *); +static void pcn_tick (void *); +static void pcn_start (struct ifnet *); +static int pcn_ioctl (struct ifnet *, u_long, caddr_t); +static void pcn_init (void *); +static void pcn_stop (struct pcn_softc *); +static void pcn_watchdog (struct ifnet *); +static void pcn_shutdown (device_t); +static int pcn_ifmedia_upd (struct ifnet *); +static void pcn_ifmedia_sts (struct ifnet *, struct ifmediareq *); + +static int pcn_miibus_readreg (device_t, int, int); +static int pcn_miibus_writereg (device_t, int, int, int); +static void pcn_miibus_statchg (device_t); + +static void pcn_setfilt (struct ifnet *); +static void pcn_setmulti (struct pcn_softc *); +static u_int32_t pcn_crc (caddr_t); +static void pcn_reset (struct pcn_softc *); +static int pcn_list_rx_init (struct pcn_softc *); +static int pcn_list_tx_init (struct pcn_softc *); + +#ifdef PCN_USEIOSPACE +#define PCN_RES SYS_RES_IOPORT +#define PCN_RID PCN_PCI_LOIO +#else +#define PCN_RES SYS_RES_MEMORY +#define PCN_RID PCN_PCI_LOMEM +#endif + +static device_method_t pcn_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pcn_probe), + DEVMETHOD(device_attach, pcn_attach), + DEVMETHOD(device_detach, pcn_detach), + DEVMETHOD(device_shutdown, pcn_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, pcn_miibus_readreg), + DEVMETHOD(miibus_writereg, pcn_miibus_writereg), + DEVMETHOD(miibus_statchg, pcn_miibus_statchg), + + { 0, 0 } +}; + +static driver_t pcn_driver = { + "pcn", + pcn_methods, + sizeof(struct pcn_softc) +}; + +static devclass_t pcn_devclass; + +DRIVER_MODULE(if_pcn, pci, pcn_driver, pcn_devclass, 0, 0); +DRIVER_MODULE(miibus, pcn, miibus_driver, miibus_devclass, 0, 0); + +#define PCN_CSR_SETBIT(sc, reg, x) \ + pcn_csr_write(sc, reg, pcn_csr_read(sc, reg) | (x)) + +#define PCN_CSR_CLRBIT(sc, reg, x) \ + pcn_csr_write(sc, reg, pcn_csr_read(sc, reg) & ~(x)) + +#define PCN_BCR_SETBIT(sc, reg, x) \ + pcn_bcr_write(sc, reg, pcn_bcr_read(sc, reg) | (x)) + +#define PCN_BCR_CLRBIT(sc, reg, x) \ + pcn_bcr_write(sc, reg, pcn_bcr_read(sc, reg) & ~(x)) + +static u_int32_t pcn_csr_read(sc, reg) + struct pcn_softc *sc; + int reg; +{ + CSR_WRITE_4(sc, PCN_IO32_RAP, reg); + return(CSR_READ_4(sc, PCN_IO32_RDP)); +} + +static u_int16_t pcn_csr_read16(sc, reg) + struct pcn_softc *sc; + int reg; +{ + CSR_WRITE_2(sc, PCN_IO16_RAP, reg); + return(CSR_READ_2(sc, PCN_IO16_RDP)); +} + +static void pcn_csr_write(sc, reg, val) + struct pcn_softc *sc; + int reg; +{ + CSR_WRITE_4(sc, PCN_IO32_RAP, reg); + CSR_WRITE_4(sc, PCN_IO32_RDP, val); + return; +} + +static u_int32_t pcn_bcr_read(sc, reg) + struct pcn_softc *sc; + int reg; +{ + CSR_WRITE_4(sc, PCN_IO32_RAP, reg); + return(CSR_READ_4(sc, PCN_IO32_BDP)); +} + +static u_int16_t pcn_bcr_read16(sc, reg) + struct pcn_softc *sc; + int reg; +{ + CSR_WRITE_2(sc, PCN_IO16_RAP, reg); + return(CSR_READ_2(sc, PCN_IO16_BDP)); +} + +static void pcn_bcr_write(sc, reg, val) + struct pcn_softc *sc; + int reg; +{ + CSR_WRITE_4(sc, PCN_IO32_RAP, reg); + CSR_WRITE_4(sc, PCN_IO32_BDP, val); + return; +} + +static int pcn_miibus_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct pcn_softc *sc; + int val; + + sc = device_get_softc(dev); + + if (sc->pcn_phyaddr && phy > sc->pcn_phyaddr) + return(0); + + pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); + val = pcn_bcr_read(sc, PCN_BCR_MIIDATA) & 0xFFFF; + if (val == 0xFFFF) + return(0); + + sc->pcn_phyaddr = phy; + + return(val); +} + +static int pcn_miibus_writereg(dev, phy, reg, data) + device_t dev; + int phy, reg, data; +{ + struct pcn_softc *sc; + + sc = device_get_softc(dev); + + pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); + pcn_bcr_write(sc, PCN_BCR_MIIDATA, data); + + return(0); +} + +static void pcn_miibus_statchg(dev) + device_t dev; +{ + struct pcn_softc *sc; + struct mii_data *mii; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->pcn_miibus); + + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { + PCN_BCR_SETBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN); + } else { + PCN_BCR_CLRBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN); + } + + return; +} + +#define DC_POLY 0xEDB88320 + +static u_int32_t pcn_crc(addr) + caddr_t addr; +{ + u_int32_t idx, bit, data, crc; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (idx = 0; idx < 6; idx++) { + for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) + crc = (crc >> 1) ^ (((crc ^ data) & 1) ? DC_POLY : 0); + } + + return ((crc >> 26) & 0x3F); +} + +static void pcn_setmulti(sc) + struct pcn_softc *sc; +{ + struct ifnet *ifp; + struct ifmultiaddr *ifma; + u_int32_t h, i; + u_int16_t hashes[4] = { 0, 0, 0, 0 }; + + ifp = &sc->arpcom.ac_if; + + PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + for (i = 0; i < 4; i++) + pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0xFFFF); + PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); + return; + } + + /* first, zot all the existing hash bits */ + for (i = 0; i < 4; i++) + pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0); + + /* now program new ones */ + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = pcn_crc(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + hashes[h >> 4] |= 1 << (h & 0xF); + } + + for (i = 0; i < 4; i++) + pcn_csr_write(sc, PCN_CSR_MAR0 + i, hashes[i]); + + PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); + + return; +} + +static void pcn_reset(sc) + struct pcn_softc *sc; +{ + /* + * Issue a reset by reading from the RESET register. + * Note that we don't know if the chip is operating in + * 16-bit or 32-bit mode at this point, so we attempt + * to reset the chip both ways. If one fails, the other + * will succeed. + */ + CSR_READ_2(sc, PCN_IO16_RESET); + CSR_READ_4(sc, PCN_IO32_RESET); + + /* Wait a little while for the chip to get its brains in order. */ + DELAY(1000); + + /* Select 32-bit (DWIO) mode */ + CSR_WRITE_4(sc, PCN_IO32_RDP, 0); + + /* Select software style 3. */ + pcn_bcr_write(sc, PCN_BCR_SSTYLE, PCN_SWSTYLE_PCNETPCI_BURST); + + return; +} + +/* + * Probe for an AMD chip. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + */ +static int pcn_probe(dev) + device_t dev; +{ + struct pcn_type *t; + struct pcn_softc *sc; + int rid; + u_int32_t chip_id; + + t = pcn_devs; + sc = device_get_softc(dev); + + while(t->pcn_name != NULL) { + if ((pci_get_vendor(dev) == t->pcn_vid) && + (pci_get_device(dev) == t->pcn_did)) { + /* + * Temporarily map the I/O space + * so we can read the chip ID register. + */ + rid = PCN_RID; + sc->pcn_res = bus_alloc_resource(dev, PCN_RES, &rid, + 0, ~0, 1, RF_ACTIVE); + if (sc->pcn_res == NULL) { + device_printf(dev, + "couldn't map ports/memory\n"); + return(ENXIO); + } + sc->pcn_btag = rman_get_bustag(sc->pcn_res); + sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res); + mtx_init(&sc->pcn_mtx, + device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); + PCN_LOCK(sc); + /* + * Note: we can *NOT* put the chip into + * 32-bit mode yet. The lnc driver will only + * work in 16-bit mode, and once the chip + * goes into 32-bit mode, the only way to + * get it out again is with a hardware reset. + * So if pcn_probe() is called before the + * lnc driver's probe routine, the chip will + * be locked into 32-bit operation and the lnc + * driver will be unable to attach to it. + * Note II: if the chip happens to already + * be in 32-bit mode, we still need to check + * the chip ID, but first we have to detect + * 32-bit mode using only 16-bit operations. + * The safest way to do this is to read the + * PCI subsystem ID from BCR23/24 and compare + * that with the value read from PCI config + * space. + */ + chip_id = pcn_bcr_read16(sc, PCN_BCR_PCISUBSYSID); + chip_id <<= 16; + chip_id |= pcn_bcr_read16(sc, PCN_BCR_PCISUBVENID); + /* + * Note III: the test for 0x10001000 is a hack to + * pacify VMware, who's pseudo-PCnet interface is + * broken. Reading the subsystem register from PCI + * config space yeilds 0x00000000 while reading the + * same value from I/O space yeilds 0x10001000. It's + * not supposed to be that way. + */ + if (chip_id == pci_read_config(dev, + PCIR_SUBVEND_0, 4) || chip_id == 0x10001000) { + /* We're in 16-bit mode. */ + chip_id = pcn_csr_read16(sc, PCN_CSR_CHIPID1); + chip_id <<= 16; + chip_id |= pcn_csr_read16(sc, PCN_CSR_CHIPID0); + } else { + /* We're in 32-bit mode. */ + chip_id = pcn_csr_read(sc, PCN_CSR_CHIPID1); + chip_id <<= 16; + chip_id |= pcn_csr_read(sc, PCN_CSR_CHIPID0); + } + bus_release_resource(dev, PCN_RES, + PCN_RID, sc->pcn_res); + PCN_UNLOCK(sc); + mtx_destroy(&sc->pcn_mtx); + chip_id >>= 12; + sc->pcn_type = chip_id & PART_MASK; + switch(sc->pcn_type) { + case Am79C971: + case Am79C972: + case Am79C973: + case Am79C975: + case Am79C976: + case Am79C978: + break; + default: + return(ENXIO); + break; + } + device_set_desc(dev, t->pcn_name); + return(0); + } + t++; + } + + return(ENXIO); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int pcn_attach(dev) + device_t dev; +{ + u_int32_t eaddr[2]; + u_int32_t command; + struct pcn_softc *sc; + struct ifnet *ifp; + int unit, error = 0, rid; + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + + /* Initialize our mutex. */ + mtx_init(&sc->pcn_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF | MTX_RECURSE); + PCN_LOCK(sc); + + /* + * Handle power management nonsense. + */ + if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { + u_int32_t iobase, membase, irq; + + /* Save important PCI config data. */ + iobase = pci_read_config(dev, PCN_PCI_LOIO, 4); + membase = pci_read_config(dev, PCN_PCI_LOMEM, 4); + irq = pci_read_config(dev, PCN_PCI_INTLINE, 4); + + /* Reset the power state. */ + printf("pcn%d: chip is in D%d power mode " + "-- setting to D0\n", unit, + pci_get_powerstate(dev)); + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + + /* Restore PCI config data. */ + pci_write_config(dev, PCN_PCI_LOIO, iobase, 4); + pci_write_config(dev, PCN_PCI_LOMEM, membase, 4); + pci_write_config(dev, PCN_PCI_INTLINE, irq, 4); + } + + /* + * Map control/status registers. + */ + pci_enable_busmaster(dev); + pci_enable_io(dev, SYS_RES_IOPORT); + pci_enable_io(dev, SYS_RES_MEMORY); + command = pci_read_config(dev, PCIR_COMMAND, 4); + +#ifdef PCN_USEIOSPACE + if (!(command & PCIM_CMD_PORTEN)) { + printf("pcn%d: failed to enable I/O ports!\n", unit); + error = ENXIO;; + goto fail; + } +#else + if (!(command & PCIM_CMD_MEMEN)) { + printf("pcn%d: failed to enable memory mapping!\n", unit); + error = ENXIO;; + goto fail; + } +#endif + + rid = PCN_RID; + sc->pcn_res = bus_alloc_resource(dev, PCN_RES, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (sc->pcn_res == NULL) { + printf("pcn%d: couldn't map ports/memory\n", unit); + error = ENXIO; + goto fail; + } + + sc->pcn_btag = rman_get_bustag(sc->pcn_res); + sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res); + + /* Allocate interrupt */ + rid = 0; + sc->pcn_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->pcn_irq == NULL) { + printf("pcn%d: couldn't map interrupt\n", unit); + bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->pcn_irq, INTR_TYPE_NET, + pcn_intr, sc, &sc->pcn_intrhand); + + if (error) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_res); + bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); + printf("pcn%d: couldn't set up irq\n", unit); + goto fail; + } + + /* Reset the adapter. */ + pcn_reset(sc); + + /* + * Get station address from the EEPROM. + */ + eaddr[0] = CSR_READ_4(sc, PCN_IO32_APROM00); + eaddr[1] = CSR_READ_4(sc, PCN_IO32_APROM01); + bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); + + /* + * An AMD chip was detected. Inform the world. + */ + printf("pcn%d: Ethernet address: %6D\n", unit, + sc->arpcom.ac_enaddr, ":"); + + sc->pcn_unit = unit; + callout_handle_init(&sc->pcn_stat_ch); + + sc->pcn_ldata = contigmalloc(sizeof(struct pcn_list_data), M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc->pcn_ldata == NULL) { + printf("pcn%d: no memory for list buffers!\n", unit); + bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); + bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); + error = ENXIO; + goto fail; + } + bzero(sc->pcn_ldata, sizeof(struct pcn_list_data)); + + ifp = &sc->arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = "pcn"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = pcn_ioctl; + ifp->if_output = ether_output; + ifp->if_start = pcn_start; + ifp->if_watchdog = pcn_watchdog; + ifp->if_init = pcn_init; + ifp->if_baudrate = 10000000; + ifp->if_snd.ifq_maxlen = PCN_TX_LIST_CNT - 1; + + /* + * Do MII setup. + */ + if (mii_phy_probe(dev, &sc->pcn_miibus, + pcn_ifmedia_upd, pcn_ifmedia_sts)) { + printf("pcn%d: MII without any PHY!\n", sc->pcn_unit); + bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); + bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); + error = ENXIO; + goto fail; + } + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); + callout_handle_init(&sc->pcn_stat_ch); + PCN_UNLOCK(sc); + return(0); + +fail: + PCN_UNLOCK(sc); + mtx_destroy(&sc->pcn_mtx); + + return(error); +} + +static int pcn_detach(dev) + device_t dev; +{ + struct pcn_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + ifp = &sc->arpcom.ac_if; + + PCN_LOCK(sc); + + pcn_reset(sc); + pcn_stop(sc); + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + + if (sc->pcn_miibus != NULL) { + bus_generic_detach(dev); + device_delete_child(dev, sc->pcn_miibus); + } + + bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); + bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); + + contigfree(sc->pcn_ldata, sizeof(struct pcn_list_data), M_DEVBUF); + PCN_UNLOCK(sc); + + mtx_destroy(&sc->pcn_mtx); + + return(0); +} + +/* + * Initialize the transmit descriptors. + */ +static int pcn_list_tx_init(sc) + struct pcn_softc *sc; +{ + struct pcn_list_data *ld; + struct pcn_ring_data *cd; + int i; + + cd = &sc->pcn_cdata; + ld = sc->pcn_ldata; + + for (i = 0; i < PCN_TX_LIST_CNT; i++) { + cd->pcn_tx_chain[i] = NULL; + ld->pcn_tx_list[i].pcn_tbaddr = 0; + ld->pcn_tx_list[i].pcn_txctl = 0; + ld->pcn_tx_list[i].pcn_txstat = 0; + } + + cd->pcn_tx_prod = cd->pcn_tx_cons = cd->pcn_tx_cnt = 0; + + return(0); +} + + +/* + * Initialize the RX descriptors and allocate mbufs for them. + */ +static int pcn_list_rx_init(sc) + struct pcn_softc *sc; +{ + struct pcn_list_data *ld; + struct pcn_ring_data *cd; + int i; + + ld = sc->pcn_ldata; + cd = &sc->pcn_cdata; + + for (i = 0; i < PCN_RX_LIST_CNT; i++) { + if (pcn_newbuf(sc, i, NULL) == ENOBUFS) + return(ENOBUFS); + } + + cd->pcn_rx_prod = 0; + + return(0); +} + +/* + * Initialize an RX descriptor and attach an MBUF cluster. + */ +static int pcn_newbuf(sc, idx, m) + struct pcn_softc *sc; + int idx; + struct mbuf *m; +{ + struct mbuf *m_new = NULL; + struct pcn_rx_desc *c; + + c = &sc->pcn_ldata->pcn_rx_list[idx]; + + if (m == NULL) { + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(ENOBUFS); + + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + return(ENOBUFS); + } + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + } else { + m_new = m; + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + m_new->m_data = m_new->m_ext.ext_buf; + } + + m_adj(m_new, ETHER_ALIGN); + + sc->pcn_cdata.pcn_rx_chain[idx] = m_new; + c->pcn_rbaddr = vtophys(mtod(m_new, caddr_t)); + c->pcn_bufsz = (~(PCN_RXLEN) + 1) & PCN_RXLEN_BUFSZ; + c->pcn_bufsz |= PCN_RXLEN_MBO; + c->pcn_rxstat = PCN_RXSTAT_STP|PCN_RXSTAT_ENP|PCN_RXSTAT_OWN; + + return(0); +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +static void pcn_rxeof(sc) + struct pcn_softc *sc; +{ + struct ether_header *eh; + struct mbuf *m; + struct ifnet *ifp; + struct pcn_rx_desc *cur_rx; + int i; + + ifp = &sc->arpcom.ac_if; + i = sc->pcn_cdata.pcn_rx_prod; + + while(PCN_OWN_RXDESC(&sc->pcn_ldata->pcn_rx_list[i])) { + cur_rx = &sc->pcn_ldata->pcn_rx_list[i]; + m = sc->pcn_cdata.pcn_rx_chain[i]; + sc->pcn_cdata.pcn_rx_chain[i] = NULL; + + /* + * If an error occurs, update stats, clear the + * status word and leave the mbuf cluster in place: + * it should simply get re-used next time this descriptor + * comes up in the ring. + */ + if (cur_rx->pcn_rxstat & PCN_RXSTAT_ERR) { + ifp->if_ierrors++; + pcn_newbuf(sc, i, m); + PCN_INC(i, PCN_RX_LIST_CNT); + continue; + } + + if (pcn_newbuf(sc, i, NULL)) { + /* Ran out of mbufs; recycle this one. */ + pcn_newbuf(sc, i, m); + ifp->if_ierrors++; + PCN_INC(i, PCN_RX_LIST_CNT); + continue; + } + + PCN_INC(i, PCN_RX_LIST_CNT); + + /* No errors; receive the packet. */ + ifp->if_ipackets++; + eh = mtod(m, struct ether_header *); + m->m_len = m->m_pkthdr.len = + cur_rx->pcn_rxlen - ETHER_CRC_LEN; + m->m_pkthdr.rcvif = ifp; + + /* Remove header from mbuf and pass it on. */ + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, eh, m); + } + + sc->pcn_cdata.pcn_rx_prod = i; + + return; +} + +/* + * A frame was downloaded to the chip. It's safe for us to clean up + * the list buffers. + */ + +static void pcn_txeof(sc) + struct pcn_softc *sc; +{ + struct pcn_tx_desc *cur_tx = NULL; + struct ifnet *ifp; + u_int32_t idx; + + ifp = &sc->arpcom.ac_if; + + /* Clear the timeout timer. */ + ifp->if_timer = 0; + + /* + * Go through our tx list and free mbufs for those + * frames that have been transmitted. + */ + idx = sc->pcn_cdata.pcn_tx_cons; + while (idx != sc->pcn_cdata.pcn_tx_prod) { + cur_tx = &sc->pcn_ldata->pcn_tx_list[idx]; + + if (!PCN_OWN_TXDESC(cur_tx)) + break; + + if (!(cur_tx->pcn_txctl & PCN_TXCTL_ENP)) { + sc->pcn_cdata.pcn_tx_cnt--; + PCN_INC(idx, PCN_TX_LIST_CNT); + continue; + } + + if (cur_tx->pcn_txctl & PCN_TXCTL_ERR) { + ifp->if_oerrors++; + if (cur_tx->pcn_txstat & PCN_TXSTAT_EXDEF) + ifp->if_collisions++; + if (cur_tx->pcn_txstat & PCN_TXSTAT_RTRY) + ifp->if_collisions++; + } + + ifp->if_collisions += + cur_tx->pcn_txstat & PCN_TXSTAT_TRC; + + ifp->if_opackets++; + if (sc->pcn_cdata.pcn_tx_chain[idx] != NULL) { + m_freem(sc->pcn_cdata.pcn_tx_chain[idx]); + sc->pcn_cdata.pcn_tx_chain[idx] = NULL; + } + + sc->pcn_cdata.pcn_tx_cnt--; + PCN_INC(idx, PCN_TX_LIST_CNT); + ifp->if_timer = 0; + } + + sc->pcn_cdata.pcn_tx_cons = idx; + + if (cur_tx != NULL) + ifp->if_flags &= ~IFF_OACTIVE; + + return; +} + +static void pcn_tick(xsc) + void *xsc; +{ + struct pcn_softc *sc; + struct mii_data *mii; + struct ifnet *ifp; + + sc = xsc; + ifp = &sc->arpcom.ac_if; + PCN_LOCK(sc); + + mii = device_get_softc(sc->pcn_miibus); + mii_tick(mii); + + /* link just died */ + if (sc->pcn_link & !(mii->mii_media_status & IFM_ACTIVE)) + sc->pcn_link = 0; + + /* link just came up, restart */ + if (!sc->pcn_link && mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->pcn_link++; + if (ifp->if_snd.ifq_head != NULL) + pcn_start(ifp); + } + + sc->pcn_stat_ch = timeout(pcn_tick, sc, hz); + + PCN_UNLOCK(sc); + + return; +} + +static void pcn_intr(arg) + void *arg; +{ + struct pcn_softc *sc; + struct ifnet *ifp; + u_int32_t status; + + sc = arg; + ifp = &sc->arpcom.ac_if; + + /* Supress unwanted interrupts */ + if (!(ifp->if_flags & IFF_UP)) { + pcn_stop(sc); + return; + } + + CSR_WRITE_4(sc, PCN_IO32_RAP, PCN_CSR_CSR); + + while ((status = CSR_READ_4(sc, PCN_IO32_RDP)) & PCN_CSR_INTR) { + CSR_WRITE_4(sc, PCN_IO32_RDP, status); + + if (status & PCN_CSR_RINT) + pcn_rxeof(sc); + + if (status & PCN_CSR_TINT) + pcn_txeof(sc); + + if (status & PCN_CSR_ERR) { + pcn_init(sc); + break; + } + } + + if (ifp->if_snd.ifq_head != NULL) + pcn_start(ifp); + + return; +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int pcn_encap(sc, m_head, txidx) + struct pcn_softc *sc; + struct mbuf *m_head; + u_int32_t *txidx; +{ + struct pcn_tx_desc *f = NULL; + struct mbuf *m; + int frag, cur, cnt = 0; + + /* + * Start packing the mbufs in this chain into + * the fragment pointers. Stop when we run out + * of fragments or hit the end of the mbuf chain. + */ + m = m_head; + cur = frag = *txidx; + + for (m = m_head; m != NULL; m = m->m_next) { + if (m->m_len != 0) { + if ((PCN_TX_LIST_CNT - + (sc->pcn_cdata.pcn_tx_cnt + cnt)) < 2) + return(ENOBUFS); + f = &sc->pcn_ldata->pcn_tx_list[frag]; + f->pcn_txctl = (~(m->m_len) + 1) & PCN_TXCTL_BUFSZ; + f->pcn_txctl |= PCN_TXCTL_MBO; + f->pcn_tbaddr = vtophys(mtod(m, vm_offset_t)); + if (cnt == 0) + f->pcn_txctl |= PCN_TXCTL_STP; + else + f->pcn_txctl |= PCN_TXCTL_OWN; + cur = frag; + PCN_INC(frag, PCN_TX_LIST_CNT); + cnt++; + } + } + + if (m != NULL) + return(ENOBUFS); + + sc->pcn_cdata.pcn_tx_chain[cur] = m_head; + sc->pcn_ldata->pcn_tx_list[cur].pcn_txctl |= + PCN_TXCTL_ENP|PCN_TXCTL_ADD_FCS|PCN_TXCTL_MORE_LTINT; + sc->pcn_ldata->pcn_tx_list[*txidx].pcn_txctl |= PCN_TXCTL_OWN; + sc->pcn_cdata.pcn_tx_cnt += cnt; + *txidx = frag; + + return(0); +} + +/* + * Main transmit routine. To avoid having to do mbuf copies, we put pointers + * to the mbuf data regions directly in the transmit lists. We also save a + * copy of the pointers since the transmit list fragment pointers are + * physical addresses. + */ +static void pcn_start(ifp) + struct ifnet *ifp; +{ + struct pcn_softc *sc; + struct mbuf *m_head = NULL; + u_int32_t idx; + + sc = ifp->if_softc; + + PCN_LOCK(sc); + + if (!sc->pcn_link) { + PCN_UNLOCK(sc); + return; + } + + idx = sc->pcn_cdata.pcn_tx_prod; + + if (ifp->if_flags & IFF_OACTIVE) { + PCN_UNLOCK(sc); + return; + } + + while(sc->pcn_cdata.pcn_tx_chain[idx] == NULL) { + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + if (pcn_encap(sc, m_head, &idx)) { + IF_PREPEND(&ifp->if_snd, m_head); + ifp->if_flags |= IFF_OACTIVE; + break; + } + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, m_head); + + } + + /* Transmit */ + sc->pcn_cdata.pcn_tx_prod = idx; + pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_TX|PCN_CSR_INTEN); + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + + PCN_UNLOCK(sc); + + return; +} + +static void pcn_setfilt(ifp) + struct ifnet *ifp; +{ + struct pcn_softc *sc; + + sc = ifp->if_softc; + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) { + PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); + } else { + PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); + } + + /* Set the capture broadcast bit to capture broadcast frames. */ + if (ifp->if_flags & IFF_BROADCAST) { + PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); + } else { + PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); + } + + return; +} + +static void pcn_init(xsc) + void *xsc; +{ + struct pcn_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mii_data *mii = NULL; + + PCN_LOCK(sc); + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + pcn_stop(sc); + pcn_reset(sc); + + mii = device_get_softc(sc->pcn_miibus); + + /* Set MAC address */ + pcn_csr_write(sc, PCN_CSR_PAR0, + ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); + pcn_csr_write(sc, PCN_CSR_PAR1, + ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); + pcn_csr_write(sc, PCN_CSR_PAR2, + ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); + + /* Init circular RX list. */ + if (pcn_list_rx_init(sc) == ENOBUFS) { + printf("pcn%d: initialization failed: no " + "memory for rx buffers\n", sc->pcn_unit); + pcn_stop(sc); + PCN_UNLOCK(sc); + return; + } + + /* + * Init tx descriptors. + */ + pcn_list_tx_init(sc); + + /* Set up the mode register. */ + pcn_csr_write(sc, PCN_CSR_MODE, PCN_PORT_MII); + + /* Set up RX filter. */ + pcn_setfilt(ifp); + + /* + * Load the multicast filter. + */ + pcn_setmulti(sc); + + /* + * Load the addresses of the RX and TX lists. + */ + pcn_csr_write(sc, PCN_CSR_RXADDR0, + vtophys(&sc->pcn_ldata->pcn_rx_list[0]) & 0xFFFF); + pcn_csr_write(sc, PCN_CSR_RXADDR1, + (vtophys(&sc->pcn_ldata->pcn_rx_list[0]) >> 16) & 0xFFFF); + pcn_csr_write(sc, PCN_CSR_TXADDR0, + vtophys(&sc->pcn_ldata->pcn_tx_list[0]) & 0xFFFF); + pcn_csr_write(sc, PCN_CSR_TXADDR1, + (vtophys(&sc->pcn_ldata->pcn_tx_list[0]) >> 16) & 0xFFFF); + + /* Set the RX and TX ring sizes. */ + pcn_csr_write(sc, PCN_CSR_RXRINGLEN, (~PCN_RX_LIST_CNT) + 1); + pcn_csr_write(sc, PCN_CSR_TXRINGLEN, (~PCN_TX_LIST_CNT) + 1); + + /* We're not using the initialization block. */ + pcn_csr_write(sc, PCN_CSR_IAB1, 0); + + /* Enable fast suspend mode. */ + PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL2, PCN_EXTCTL2_FASTSPNDE); + + /* + * Enable burst read and write. Also set the no underflow + * bit. This will avoid transmit underruns in certain + * conditions while still providing decent performance. + */ + PCN_BCR_SETBIT(sc, PCN_BCR_BUSCTL, PCN_BUSCTL_NOUFLOW| + PCN_BUSCTL_BREAD|PCN_BUSCTL_BWRITE); + + /* Enable graceful recovery from underflow. */ + PCN_CSR_SETBIT(sc, PCN_CSR_IMR, PCN_IMR_DXSUFLO); + + /* Enable auto-padding of short TX frames. */ + PCN_CSR_SETBIT(sc, PCN_CSR_TFEAT, PCN_TFEAT_PAD_TX); + + /* Disable MII autoneg (we handle this ourselves). */ + PCN_BCR_SETBIT(sc, PCN_BCR_MIICTL, PCN_MIICTL_DANAS); + + if (sc->pcn_type == Am79C978) + pcn_bcr_write(sc, PCN_BCR_PHYSEL, + PCN_PHYSEL_PCNET|PCN_PHY_HOMEPNA); + + /* Enable interrupts and start the controller running. */ + pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_INTEN|PCN_CSR_START); + + mii_mediachg(mii); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + sc->pcn_stat_ch = timeout(pcn_tick, sc, hz); + PCN_UNLOCK(sc); + + return; +} + +/* + * Set media options. + */ +static int pcn_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct pcn_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = device_get_softc(sc->pcn_miibus); + + sc->pcn_link = 0; + if (mii->mii_instance) { + struct mii_softc *miisc; + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + mii_phy_reset(miisc); + } + mii_mediachg(mii); + + return(0); +} + +/* + * Report current media status. + */ +static void pcn_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct pcn_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + + mii = device_get_softc(sc->pcn_miibus); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + + return; +} + +static int pcn_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct pcn_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + struct mii_data *mii = NULL; + int error = 0; + + PCN_LOCK(sc); + + switch(command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING && + ifp->if_flags & IFF_PROMISC && + !(sc->pcn_if_flags & IFF_PROMISC)) { + PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, + PCN_EXTCTL1_SPND); + pcn_setfilt(ifp); + PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, + PCN_EXTCTL1_SPND); + pcn_csr_write(sc, PCN_CSR_CSR, + PCN_CSR_INTEN|PCN_CSR_START); + } else if (ifp->if_flags & IFF_RUNNING && + !(ifp->if_flags & IFF_PROMISC) && + sc->pcn_if_flags & IFF_PROMISC) { + PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, + PCN_EXTCTL1_SPND); + pcn_setfilt(ifp); + PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, + PCN_EXTCTL1_SPND); + pcn_csr_write(sc, PCN_CSR_CSR, + PCN_CSR_INTEN|PCN_CSR_START); + } else if (!(ifp->if_flags & IFF_RUNNING)) + pcn_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) + pcn_stop(sc); + } + sc->pcn_if_flags = ifp->if_flags; + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + pcn_setmulti(sc); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = device_get_softc(sc->pcn_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + break; + default: + error = EINVAL; + break; + } + + PCN_UNLOCK(sc); + + return(error); +} + +static void pcn_watchdog(ifp) + struct ifnet *ifp; +{ + struct pcn_softc *sc; + + sc = ifp->if_softc; + + PCN_LOCK(sc); + + ifp->if_oerrors++; + printf("pcn%d: watchdog timeout\n", sc->pcn_unit); + + pcn_stop(sc); + pcn_reset(sc); + pcn_init(sc); + + if (ifp->if_snd.ifq_head != NULL) + pcn_start(ifp); + + PCN_UNLOCK(sc); + + return; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void pcn_stop(sc) + struct pcn_softc *sc; +{ + register int i; + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + PCN_LOCK(sc); + ifp->if_timer = 0; + + untimeout(pcn_tick, sc, sc->pcn_stat_ch); + PCN_CSR_SETBIT(sc, PCN_CSR_CSR, PCN_CSR_STOP); + sc->pcn_link = 0; + + /* + * Free data in the RX lists. + */ + for (i = 0; i < PCN_RX_LIST_CNT; i++) { + if (sc->pcn_cdata.pcn_rx_chain[i] != NULL) { + m_freem(sc->pcn_cdata.pcn_rx_chain[i]); + sc->pcn_cdata.pcn_rx_chain[i] = NULL; + } + } + bzero((char *)&sc->pcn_ldata->pcn_rx_list, + sizeof(sc->pcn_ldata->pcn_rx_list)); + + /* + * Free the TX list buffers. + */ + for (i = 0; i < PCN_TX_LIST_CNT; i++) { + if (sc->pcn_cdata.pcn_tx_chain[i] != NULL) { + m_freem(sc->pcn_cdata.pcn_tx_chain[i]); + sc->pcn_cdata.pcn_tx_chain[i] = NULL; + } + } + + bzero((char *)&sc->pcn_ldata->pcn_tx_list, + sizeof(sc->pcn_ldata->pcn_tx_list)); + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + PCN_UNLOCK(sc); + + return; +} + +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static void pcn_shutdown(dev) + device_t dev; +{ + struct pcn_softc *sc; + + sc = device_get_softc(dev); + + PCN_LOCK(sc); + pcn_reset(sc); + pcn_stop(sc); + PCN_UNLOCK(sc); + + return; +} diff --git a/sys/pci/if_pcnreg.h b/sys/pci/if_pcnreg.h new file mode 100644 index 0000000..79ef531 --- /dev/null +++ b/sys/pci/if_pcnreg.h @@ -0,0 +1,519 @@ +/* + * Copyright (c) 2000 Berkeley Software Design, Inc. + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul <wpaul@ee.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * I/O map in 16-bit mode. To switch to 32-bit mode, + * you need to perform a 32-bit write to the RDP register + * (writing a 0 is recommended). + */ +#define PCN_IO16_APROM00 0x00 +#define PCN_IO16_APROM01 0x02 +#define PCN_IO16_APROM02 0x04 +#define PCN_IO16_APROM03 0x06 +#define PCN_IO16_APROM04 0x08 +#define PCN_IO16_APROM05 0x0A +#define PCN_IO16_APROM06 0x0C +#define PCN_IO16_APROM07 0x0E +#define PCN_IO16_RDP 0x10 +#define PCN_IO16_RAP 0x12 +#define PCN_IO16_RESET 0x14 +#define PCN_IO16_BDP 0x16 + +/* + * I/O map in 32-bit mode. + */ +#define PCN_IO32_APROM00 0x00 +#define PCN_IO32_APROM01 0x04 +#define PCN_IO32_APROM02 0x08 +#define PCN_IO32_APROM03 0x0C +#define PCN_IO32_RDP 0x10 +#define PCN_IO32_RAP 0x14 +#define PCN_IO32_RESET 0x18 +#define PCN_IO32_BDP 0x1C + +/* + * CSR registers + */ +#define PCN_CSR_CSR 0x00 +#define PCN_CSR_IAB0 0x01 +#define PCN_CSR_IAB1 0x02 +#define PCN_CSR_IMR 0x03 +#define PCN_CSR_TFEAT 0x04 +#define PCN_CSR_EXTCTL1 0x05 +#define PCN_CSR_DTBLLEN 0x06 +#define PCN_CSR_EXTCTL2 0x07 +#define PCN_CSR_MAR0 0x08 +#define PCN_CSR_MAR1 0x09 +#define PCN_CSR_MAR2 0x0A +#define PCN_CSR_MAR3 0x0B +#define PCN_CSR_PAR0 0x0C +#define PCN_CSR_PAR1 0x0D +#define PCN_CSR_PAR2 0x0E +#define PCN_CSR_MODE 0x0F +#define PCN_CSR_RXADDR0 0x18 +#define PCN_CSR_RXADDR1 0x19 +#define PCN_CSR_TXADDR0 0x1E +#define PCN_CSR_TXADDR1 0x1F +#define PCN_CSR_TXPOLL 0x2F +#define PCN_CSR_RXPOLL 0x31 +#define PCN_CSR_RXRINGLEN 0x4C +#define PCN_CSR_TXRINGLEN 0x4E +#define PCN_CSR_DMACTL 0x50 +#define PCN_CSR_BUSTIMER 0x52 +#define PCN_CSR_MEMERRTIMEO 0x64 +#define PCN_CSR_ONNOWMISC 0x74 +#define PCN_CSR_ADVFEAT 0x7A +#define PCN_CSR_MACCFG 0x7D +#define PCN_CSR_CHIPID0 0x58 +#define PCN_CSR_CHIPID1 0x59 + +/* + * Control and status register (CSR0) + */ +#define PCN_CSR_INIT 0x0001 +#define PCN_CSR_START 0x0002 +#define PCN_CSR_STOP 0x0004 +#define PCN_CSR_TX 0x0008 +#define PCN_CSR_TXON 0x0010 +#define PCN_CSR_RXON 0x0020 +#define PCN_CSR_INTEN 0x0040 +#define PCN_CSR_INTR 0x0080 +#define PCN_CSR_IDONE 0x0100 +#define PCN_CSR_TINT 0x0200 +#define PCN_CSR_RINT 0x0400 +#define PCN_CSR_MERR 0x0800 +#define PCN_CSR_MISS 0x1000 +#define PCN_CSR_CERR 0x2000 +#define PCN_CSR_ERR 0x8000 + +/* + * Interrupt masks and deferral control (CSR3) + */ +#define PCN_IMR_BSWAP 0x0004 +#define PCN_IMR_ENMBA 0x0008 /* enable modified backoff alg */ +#define PCN_IMR_DXMT2PD 0x0010 +#define PCN_IMR_LAPPEN 0x0020 /* lookahead packet processing enb */ +#define PCN_IMR_DXSUFLO 0x0040 /* disable TX stop on underflow */ +#define PCN_IMR_IDONE 0x0100 +#define PCN_IMR_TINT 0x0200 +#define PCN_IMR_RINT 0x0400 +#define PCN_IMR_MERR 0x0800 +#define PCN_IMR_MISS 0x1000 + +/* + * Test and features control (CSR4) + */ +#define PCN_TFEAT_TXSTRTMASK 0x0004 +#define PCN_TFEAT_TXSTRT 0x0008 +#define PCN_TFEAT_RXCCOFLOWM 0x0010 /* Rx collision counter oflow */ +#define PCN_TFEAT_RXCCOFLOW 0x0020 +#define PCN_TFEAT_UINT 0x0040 +#define PCN_TFEAT_UINTREQ 0x0080 +#define PCN_TFEAT_MISSOFLOWM 0x0100 +#define PCN_TFEAT_MISSOFLOW 0x0200 +#define PCN_TFEAT_STRIP_FCS 0x0400 +#define PCN_TFEAT_PAD_TX 0x0800 +#define PCN_TFEAT_TXDPOLL 0x1000 +#define PCN_TFEAT_DMAPLUS 0x4000 + +/* + * Extended control and interrupt 1 (CSR5) + */ +#define PCN_EXTCTL1_SPND 0x0001 /* suspend */ +#define PCN_EXTCTL1_MPMODE 0x0002 /* magic packet mode */ +#define PCN_EXTCTL1_MPENB 0x0004 /* magic packet enable */ +#define PCN_EXTCTL1_MPINTEN 0x0008 /* magic packet interrupt enable */ +#define PCN_EXTCTL1_MPINT 0x0010 /* magic packet interrupt */ +#define PCN_EXTCTL1_MPPLBA 0x0020 /* magic packet phys. logical bcast */ +#define PCN_EXTCTL1_EXDEFEN 0x0040 /* excessive deferral interrupt enb. */ +#define PCN_EXTCTL1_EXDEF 0x0080 /* excessive deferral interrupt */ +#define PCN_EXTCTL1_SINTEN 0x0400 /* system interrupt enable */ +#define PCN_EXTCTL1_SINT 0x0800 /* system interrupt */ +#define PCN_EXTCTL1_LTINTEN 0x4000 /* last TX interrupt enb */ +#define PCN_EXTCTL1_TXOKINTD 0x8000 /* TX OK interrupt disable */ + +/* + * RX/TX descriptor len (CSR6) + */ +#define PCN_DTBLLEN_RLEN 0x0F00 +#define PCN_DTBLLEN_TLEN 0xF000 + +/* + * Extended control and interrupt 2 (CSR7) + */ +#define PCN_EXTCTL2_MIIPDTINTE 0x0001 +#define PCN_EXTCTL2_MIIPDTINT 0x0002 +#define PCN_EXTCTL2_MCCIINTE 0x0004 +#define PCN_EXTCTL2_MCCIINT 0x0008 +#define PCN_EXTCTL2_MCCINTE 0x0010 +#define PCN_EXTCTL2_MCCINT 0x0020 +#define PCN_EXTCTL2_MAPINTE 0x0040 +#define PCN_EXTCTL2_MAPINT 0x0080 +#define PCN_EXTCTL2_MREINTE 0x0100 +#define PCN_EXTCTL2_MREINT 0x0200 +#define PCN_EXTCTL2_STINTE 0x0400 +#define PCN_EXTCTL2_STINT 0x0800 +#define PCN_EXTCTL2_RXDPOLL 0x1000 +#define PCN_EXTCTL2_RDMD 0x2000 +#define PCN_EXTCTL2_RXFRTG 0x4000 +#define PCN_EXTCTL2_FASTSPNDE 0x8000 + + +/* + * Mode (CSR15) + */ +#define PCN_MODE_RXD 0x0001 /* RX disable */ +#define PCN_MODE_TXD 0x0002 /* TX disable */ +#define PCN_MODE_LOOP 0x0004 /* loopback enable */ +#define PCN_MODE_TXCRCD 0x0008 +#define PCN_MODE_FORCECOLL 0x0010 +#define PCN_MODE_RETRYD 0x0020 +#define PCN_MODE_INTLOOP 0x0040 +#define PCN_MODE_PORTSEL 0x0180 +#define PCN_MODE_RXVPAD 0x2000 +#define PCN_MODE_RXNOBROAD 0x4000 +#define PCN_MODE_PROMISC 0x8000 + +#define PCN_PORT_GPSI 0x0100 +#define PCN_PORT_MII 0x0180 + +/* + * Chip ID values. + */ +/* CSR88-89: Chip ID masks */ +#define AMD_MASK 0x003 +#define PART_MASK 0xffff +#define Am79C960 0x0003 +#define Am79C961 0x2260 +#define Am79C961A 0x2261 +#define Am79C965 0x2430 +#define Am79C970 0x0242 +#define Am79C970A 0x2621 +#define Am79C971 0x2623 +#define Am79C972 0x2624 +#define Am79C973 0x2625 +#define Am79C978 0x2626 +#define Am79C975 0x2627 +#define Am79C976 0x2628 + +/* + * Advanced feature control (CSR122) + */ +#define PCN_AFC_RXALIGN 0x0001 + +/* + * BCR (bus control) registers + */ +#define PCN_BCR_MISCCFG 0x02 +#define PCN_BCR_LED0 0x04 +#define PCN_BCR_LED1 0x05 +#define PCN_BCR_LED2 0x06 +#define PCN_BCR_LED3 0x07 +#define PCN_BCR_DUPLEX 0x09 +#define PCN_BCR_BUSCTL 0x12 +#define PCN_BCR_EECTL 0x13 +#define PCN_BCR_SSTYLE 0x14 +#define PCN_BCR_PCILAT 0x16 +#define PCN_BCR_PCISUBVENID 0x17 +#define PCN_BCR_PCISUBSYSID 0x18 +#define PCN_BCR_SRAMSIZE 0x19 +#define PCN_BCR_SRAMBOUND 0x1A +#define PCN_BCR_SRAMCTL 0x1B +#define PCN_BCR_MIICTL 0x20 +#define PCN_BCR_MIIADDR 0x21 +#define PCN_BCR_MIIDATA 0x22 +#define PCN_BCR_PCIVENID 0x23 +#define PCN_BCR_PCIPCAP 0x24 +#define PCN_BCR_DATA0 0x25 +#define PCN_BCR_DATA1 0x26 +#define PCN_BCR_DATA2 0x27 +#define PCN_BCR_DATA3 0x28 +#define PCN_BCR_DATA4 0x29 +#define PCN_BCR_DATA5 0x2A +#define PCN_BCR_DATA6 0x2B +#define PCN_BCR_DATA7 0x2C +#define PCN_BCR_ONNOWPAT0 0x2D +#define PCN_BCR_ONNOWPAT1 0x2E +#define PCN_BCR_ONNOWPAT2 0x2F +#define PCN_BCR_PHYSEL 0x31 + +/* + * Full duplex control (BCR9) + */ +#define PCN_DUPLEX_FDEN 0x0001 /* Full-duplex enable */ +#define PCN_DUPLEX_FDRPAD 0x0004 /* Full-duplex runt pkt accept dis. */ + +/* + * Burst and bus control register (BCR18) + */ +#define PCN_BUSCTL_BWRITE 0x0020 +#define PCN_BUSCTL_BREAD 0x0040 +#define PCN_BUSCTL_DWIO 0x0080 +#define PCN_BUSCTL_EXTREQ 0x0100 +#define PCN_BUSCTL_MEMCMD 0x0200 +#define PCN_BUSCTL_NOUFLOW 0x0800 +#define PCN_BUSCTL_ROMTMG 0xF000 + +/* + * EEPROM control (BCR19) + */ +#define PCN_EECTL_EDATA 0x0001 +#define PCN_EECTL_ECLK 0x0002 +#define PCN_EECTL_EECS 0x0004 +#define PCN_EECTL_EEN 0x0100 +#define PCN_EECTL_EEDET 0x2000 +#define PCN_EECTL_PREAD 0x4000 +#define PCN_EECTL_PVALID 0x8000 + +/* + * Software style (BCR20) + */ +#define PCN_SSTYLE_APERREN 0x0400 /* advanced parity error checking */ +#define PCN_SSTYLE_SSIZE32 0x0100 +#define PCN_SSTYLE_SWSTYLE 0x00FF + +#define PCN_SWSTYLE_LANCE 0x0000 +#define PCN_SWSTYLE_PCNETPCI 0x0102 +#define PCN_SWSTYLE_PCNETPCI_BURST 0x0103 + +/* + * MII control and status (BCR32) + */ +#define PCN_MIICTL_MIILP 0x0002 /* MII internal loopback */ +#define PCN_MIICTL_XPHYSP 0x0008 /* external PHY speed */ +#define PCN_MIICTL_XPHYFD 0x0010 /* external PHY full duplex */ +#define PCN_MIICTL_XPHYANE 0x0020 /* external phy auto-neg enable */ +#define PCN_MIICTL_XPHYRST 0x0040 /* external PHY reset */ +#define PCN_MIICTL_DANAS 0x0080 /* disable auto-neg auto-setup */ +#define PCN_MIICTL_APDW 0x0700 /* auto-poll dwell time */ +#define PCN_MIICTL_APEP 0x0100 /* auto-poll external PHY */ +#define PCN_MIICTL_FMDC 0x3000 /* data clock speed */ +#define PCN_MIICTL_MIIPD 0x4000 /* PHY detect */ +#define PCN_MIICTL_ANTST 0x8000 /* Manufacturing test */ + +/* + * MII address register (BCR33) + */ +#define PCN_MIIADDR_REGAD 0x001F +#define PCN_MIIADDR_PHYADD 0x03E0 + +/* + * MII data register (BCR34) + */ +#define PCN_MIIDATA_MIIMD 0xFFFF + +/* + * PHY selection (BCR49) (HomePNA NIC only) + */ +#define PCN_PHYSEL_PHYSEL 0x0003 +#define PCN_PHYSEL_DEFAULT 0x0300 +#define PCN_PHYSEL_PCNET 0x8000 + +#define PCN_PHY_10BT 0x0000 +#define PCN_PHY_HOMEPNA 0x0001 +#define PCN_PHY_EXTERNAL 0x0002 + +struct pcn_rx_desc { + u_int16_t pcn_rxlen; + u_int16_t pcn_rsvd0; + u_int16_t pcn_bufsz; + u_int16_t pcn_rxstat; + u_int32_t pcn_rbaddr; + u_int32_t pcn_uspace; +}; + +#define PCN_RXSTAT_BPE 0x0080 /* bus parity error */ +#define PCN_RXSTAT_ENP 0x0100 /* end of packet */ +#define PCN_RXSTAT_STP 0x0200 /* start of packet */ +#define PCN_RXSTAT_BUFF 0x0400 /* buffer error */ +#define PCN_RXSTAT_CRC 0x0800 /* CRC error */ +#define PCN_RXSTAT_OFLOW 0x1000 /* rx overrun */ +#define PCN_RXSTAT_FRAM 0x2000 /* framing error */ +#define PCN_RXSTAT_ERR 0x4000 /* error summary */ +#define PCN_RXSTAT_OWN 0x8000 + +#define PCN_RXLEN_MBO 0xF000 +#define PCN_RXLEN_BUFSZ 0x0FFF + +#define PCN_OWN_RXDESC(x) (((x)->pcn_rxstat & PCN_RXSTAT_OWN) == 0) + +struct pcn_tx_desc { + u_int32_t pcn_txstat; + u_int32_t pcn_txctl; + u_int32_t pcn_tbaddr; + u_int32_t pcn_uspace; +}; + +#define PCN_TXSTAT_TRC 0x0000000F /* transmit retries */ +#define PCN_TXSTAT_RTRY 0x04000000 /* retry */ +#define PCN_TXSTAT_LCAR 0x08000000 /* lost carrier */ +#define PCN_TXSTAT_LCOL 0x10000000 /* late collision */ +#define PCN_TXSTAT_EXDEF 0x20000000 /* excessive deferrals */ +#define PCN_TXSTAT_UFLOW 0x40000000 /* transmit underrun */ +#define PCN_TXSTAT_BUFF 0x80000000 /* buffer error */ + +#define PCN_TXCTL_OWN 0x80000000 +#define PCN_TXCTL_ERR 0x40000000 /* error summary */ +#define PCN_TXCTL_ADD_FCS 0x20000000 /* add FCS to pkt */ +#define PCN_TXCTL_MORE_LTINT 0x10000000 +#define PCN_TXCTL_ONE 0x08000000 +#define PCN_TXCTL_DEF 0x04000000 +#define PCN_TXCTL_STP 0x02000000 +#define PCN_TXCTL_ENP 0x01000000 +#define PCN_TXCTL_BPE 0x00800000 +#define PCN_TXCTL_MBO 0x0000F000 +#define PCN_TXCTL_BUFSZ 0x00000FFF + +#define PCN_OWN_TXDESC(x) (((x)->pcn_txctl & PCN_TXCTL_OWN) == 0) + +#define PCN_RX_LIST_CNT 64 +#define PCN_TX_LIST_CNT 256 + +struct pcn_list_data { + struct pcn_rx_desc pcn_rx_list[PCN_RX_LIST_CNT]; + struct pcn_tx_desc pcn_tx_list[PCN_TX_LIST_CNT]; +}; + +struct pcn_ring_data { + struct mbuf *pcn_rx_chain[PCN_RX_LIST_CNT]; + struct mbuf *pcn_tx_chain[PCN_TX_LIST_CNT]; + int pcn_rx_prod; + int pcn_tx_prod; + int pcn_tx_cons; + int pcn_tx_cnt; +}; + +/* + * AMD PCI vendor ID. + */ +#define PCN_VENDORID 0x1022 + +/* + * AMD PCnet/PCI device IDs + */ +#define PCN_DEVICEID_PCNET 0x2000 +#define PCN_DEVICEID_HOME 0x2001 + +struct pcn_type { + u_int16_t pcn_vid; + u_int16_t pcn_did; + char *pcn_name; +}; + +struct pcn_softc { + struct arpcom arpcom; /* interface info */ + bus_space_handle_t pcn_bhandle; + bus_space_tag_t pcn_btag; + struct resource *pcn_res; + struct resource *pcn_irq; + void *pcn_intrhand; + device_t pcn_miibus; + u_int8_t pcn_unit; + u_int8_t pcn_link; + u_int8_t pcn_phyaddr; + int pcn_if_flags; + int pcn_type; + struct pcn_list_data *pcn_ldata; + struct pcn_ring_data pcn_cdata; + struct callout_handle pcn_stat_ch; + struct mtx pcn_mtx; +}; + +#define PCN_LOCK(_sc) mtx_lock(&(_sc)->pcn_mtx) +#define PCN_UNLOCK(_sc) mtx_unlock(&(_sc)->pcn_mtx) + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->pcn_btag, sc->pcn_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->pcn_btag, sc->pcn_bhandle, reg) + +#define CSR_WRITE_2(sc, reg, val) \ + bus_space_write_2(sc->pcn_btag, sc->pcn_bhandle, reg, val) + +#define CSR_READ_2(sc, reg) \ + bus_space_read_2(sc->pcn_btag, sc->pcn_bhandle, reg) + + +#define PCN_TIMEOUT 1000 +#define ETHER_ALIGN 2 +#define PCN_RXLEN 1536 +#define PCN_MIN_FRAMELEN 60 +#define PCN_INC(x, y) (x) = (x + 1) % y +/* + * PCI low memory base and low I/O base register, and + * other PCI registers. + */ + +#define PCN_PCI_VENDOR_ID 0x00 +#define PCN_PCI_DEVICE_ID 0x02 +#define PCN_PCI_COMMAND 0x04 +#define PCN_PCI_STATUS 0x06 +#define PCN_PCI_REVID 0x08 +#define PCN_PCI_CLASSCODE 0x09 +#define PCN_PCI_CACHELEN 0x0C +#define PCN_PCI_LATENCY_TIMER 0x0D +#define PCN_PCI_HEADER_TYPE 0x0E +#define PCN_PCI_LOIO 0x10 +#define PCN_PCI_LOMEM 0x14 +#define PCN_PCI_BIOSROM 0x30 +#define PCN_PCI_INTLINE 0x3C +#define PCN_PCI_INTPIN 0x3D +#define PCN_PCI_MINGNT 0x3E +#define PCN_PCI_MINLAT 0x0F +#define PCN_PCI_RESETOPT 0x48 +#define PCN_PCI_EEPROM_DATA 0x4C + +/* power management registers */ +#define PCN_PCI_CAPID 0x50 /* 8 bits */ +#define PCN_PCI_NEXTPTR 0x51 /* 8 bits */ +#define PCN_PCI_PWRMGMTCAP 0x52 /* 16 bits */ +#define PCN_PCI_PWRMGMTCTRL 0x54 /* 16 bits */ + +#define PCN_PSTATE_MASK 0x0003 +#define PCN_PSTATE_D0 0x0000 +#define PCN_PSTATE_D1 0x0001 +#define PCN_PSTATE_D2 0x0002 +#define PCN_PSTATE_D3 0x0003 +#define PCN_PME_EN 0x0010 +#define PCN_PME_STATUS 0x8000 + +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) +#endif diff --git a/sys/pci/if_rl.c b/sys/pci/if_rl.c new file mode 100644 index 0000000..99ef4dd --- /dev/null +++ b/sys/pci/if_rl.c @@ -0,0 +1,1904 @@ +/* + * Copyright (c) 1997, 1998 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * RealTek 8129/8139 PCI NIC driver + * + * Supports several extremely cheap PCI 10/100 adapters based on + * the RealTek chipset. Datasheets can be obtained from + * www.realtek.com.tw. + * + * Written by Bill Paul <wpaul@ctr.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * The RealTek 8139 PCI NIC redefines the meaning of 'low end.' This is + * probably the worst PCI ethernet controller ever made, with the possible + * exception of the FEAST chip made by SMC. The 8139 supports bus-master + * DMA, but it has a terrible interface that nullifies any performance + * gains that bus-master DMA usually offers. + * + * For transmission, the chip offers a series of four TX descriptor + * registers. Each transmit frame must be in a contiguous buffer, aligned + * on a longword (32-bit) boundary. This means we almost always have to + * do mbuf copies in order to transmit a frame, except in the unlikely + * case where a) the packet fits into a single mbuf, and b) the packet + * is 32-bit aligned within the mbuf's data area. The presence of only + * four descriptor registers means that we can never have more than four + * packets queued for transmission at any one time. + * + * Reception is not much better. The driver has to allocate a single large + * buffer area (up to 64K in size) into which the chip will DMA received + * frames. Because we don't know where within this region received packets + * will begin or end, we have no choice but to copy data from the buffer + * area into mbufs in order to pass the packets up to the higher protocol + * levels. + * + * It's impossible given this rotten design to really achieve decent + * performance at 100Mbps, unless you happen to have a 400Mhz PII or + * some equally overmuscled CPU to drive it. + * + * On the bright side, the 8139 does have a built-in PHY, although + * rather than using an MDIO serial interface like most other NICs, the + * PHY registers are directly accessible through the 8139's register + * space. The 8139 supports autonegotiation, as well as a 64-bit multicast + * filter. + * + * The 8129 chip is an older version of the 8139 that uses an external PHY + * chip. The 8129 has a serial MDIO interface for accessing the MII where + * the 8139 lets you directly access the on-board PHY registers. We need + * to select which interface to use depending on the chip type. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#include <net/bpf.h> + +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +MODULE_DEPEND(rl, miibus, 1, 1, 1); + +/* "controller miibus0" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +/* + * Default to using PIO access for this driver. On SMP systems, + * there appear to be problems with memory mapped mode: it looks like + * doing too many memory mapped access back to back in rapid succession + * can hang the bus. I'm inclined to blame this on crummy design/construction + * on the part of RealTek. Memory mapped mode does appear to work on + * uniprocessor systems though. + */ +#define RL_USEIOSPACE + +#include <pci/if_rlreg.h> + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif + +/* + * Various supported device vendors/types and their names. + */ +static struct rl_type rl_devs[] = { + { RT_VENDORID, RT_DEVICEID_8129, + "RealTek 8129 10/100BaseTX" }, + { RT_VENDORID, RT_DEVICEID_8139, + "RealTek 8139 10/100BaseTX" }, + { RT_VENDORID, RT_DEVICEID_8138, + "RealTek 8139 10/100BaseTX CardBus" }, + { ACCTON_VENDORID, ACCTON_DEVICEID_5030, + "Accton MPX 5030/5038 10/100BaseTX" }, + { DELTA_VENDORID, DELTA_DEVICEID_8139, + "Delta Electronics 8139 10/100BaseTX" }, + { ADDTRON_VENDORID, ADDTRON_DEVICEID_8139, + "Addtron Technolgy 8139 10/100BaseTX" }, + { DLINK_VENDORID, DLINK_DEVICEID_530TXPLUS, + "D-Link DFE-530TX+ 10/100BaseTX" }, + { DLINK_VENDORID, DLINK_DEVICEID_690TXD, + "D-Link DFE-690TXD 10/100BaseTX" }, + { NORTEL_VENDORID, ACCTON_DEVICEID_5030, + "Nortel Networks 10/100BaseTX" }, + { 0, 0, NULL } +}; + +static int rl_probe (device_t); +static int rl_attach (device_t); +static int rl_detach (device_t); + +static int rl_encap (struct rl_softc *, struct mbuf * ); + +static void rl_rxeof (struct rl_softc *); +static void rl_txeof (struct rl_softc *); +static void rl_intr (void *); +static void rl_tick (void *); +static void rl_start (struct ifnet *); +static int rl_ioctl (struct ifnet *, u_long, caddr_t); +static void rl_init (void *); +static void rl_stop (struct rl_softc *); +static void rl_watchdog (struct ifnet *); +static int rl_suspend (device_t); +static int rl_resume (device_t); +static void rl_shutdown (device_t); +static int rl_ifmedia_upd (struct ifnet *); +static void rl_ifmedia_sts (struct ifnet *, struct ifmediareq *); + +static void rl_eeprom_putbyte (struct rl_softc *, int); +static void rl_eeprom_getword (struct rl_softc *, int, u_int16_t *); +static void rl_read_eeprom (struct rl_softc *, caddr_t, int, int, int); +static void rl_mii_sync (struct rl_softc *); +static void rl_mii_send (struct rl_softc *, u_int32_t, int); +static int rl_mii_readreg (struct rl_softc *, struct rl_mii_frame *); +static int rl_mii_writereg (struct rl_softc *, struct rl_mii_frame *); + +static int rl_miibus_readreg (device_t, int, int); +static int rl_miibus_writereg (device_t, int, int, int); +static void rl_miibus_statchg (device_t); + +static u_int8_t rl_calchash (caddr_t); +static void rl_setmulti (struct rl_softc *); +static void rl_reset (struct rl_softc *); +static int rl_list_tx_init (struct rl_softc *); + +static void rl_dma_map_rxbuf (void *, bus_dma_segment_t *, int, int); +static void rl_dma_map_txbuf (void *, bus_dma_segment_t *, int, int); + +#ifdef RL_USEIOSPACE +#define RL_RES SYS_RES_IOPORT +#define RL_RID RL_PCI_LOIO +#else +#define RL_RES SYS_RES_MEMORY +#define RL_RID RL_PCI_LOMEM +#endif + +static device_method_t rl_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, rl_probe), + DEVMETHOD(device_attach, rl_attach), + DEVMETHOD(device_detach, rl_detach), + DEVMETHOD(device_suspend, rl_suspend), + DEVMETHOD(device_resume, rl_resume), + DEVMETHOD(device_shutdown, rl_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, rl_miibus_readreg), + DEVMETHOD(miibus_writereg, rl_miibus_writereg), + DEVMETHOD(miibus_statchg, rl_miibus_statchg), + + { 0, 0 } +}; + +static driver_t rl_driver = { + "rl", + rl_methods, + sizeof(struct rl_softc) +}; + +static devclass_t rl_devclass; + +DRIVER_MODULE(if_rl, pci, rl_driver, rl_devclass, 0, 0); +DRIVER_MODULE(if_rl, cardbus, rl_driver, rl_devclass, 0, 0); +DRIVER_MODULE(miibus, rl, miibus_driver, miibus_devclass, 0, 0); + +#define EE_SET(x) \ + CSR_WRITE_1(sc, RL_EECMD, \ + CSR_READ_1(sc, RL_EECMD) | x) + +#define EE_CLR(x) \ + CSR_WRITE_1(sc, RL_EECMD, \ + CSR_READ_1(sc, RL_EECMD) & ~x) + +static void +rl_dma_map_rxbuf(arg, segs, nseg, error) + void *arg; + bus_dma_segment_t *segs; + int nseg, error; +{ + struct rl_softc *sc; + + sc = arg; + CSR_WRITE_4(sc, RL_RXADDR, segs->ds_addr & 0xFFFFFFFF); + + return; +} + +static void +rl_dma_map_txbuf(arg, segs, nseg, error) + void *arg; + bus_dma_segment_t *segs; + int nseg, error; +{ + struct rl_softc *sc; + + sc = arg; + CSR_WRITE_4(sc, RL_CUR_TXADDR(sc), segs->ds_addr & 0xFFFFFFFF); + + return; +} + +/* + * Send a read command and address to the EEPROM, check for ACK. + */ +static void rl_eeprom_putbyte(sc, addr) + struct rl_softc *sc; + int addr; +{ + register int d, i; + + d = addr | sc->rl_eecmd_read; + + /* + * Feed in each bit and strobe the clock. + */ + for (i = 0x400; i; i >>= 1) { + if (d & i) { + EE_SET(RL_EE_DATAIN); + } else { + EE_CLR(RL_EE_DATAIN); + } + DELAY(100); + EE_SET(RL_EE_CLK); + DELAY(150); + EE_CLR(RL_EE_CLK); + DELAY(100); + } + + return; +} + +/* + * Read a word of data stored in the EEPROM at address 'addr.' + */ +static void rl_eeprom_getword(sc, addr, dest) + struct rl_softc *sc; + int addr; + u_int16_t *dest; +{ + register int i; + u_int16_t word = 0; + + /* Enter EEPROM access mode. */ + CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_PROGRAM|RL_EE_SEL); + + /* + * Send address of word we want to read. + */ + rl_eeprom_putbyte(sc, addr); + + CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_PROGRAM|RL_EE_SEL); + + /* + * Start reading bits from EEPROM. + */ + for (i = 0x8000; i; i >>= 1) { + EE_SET(RL_EE_CLK); + DELAY(100); + if (CSR_READ_1(sc, RL_EECMD) & RL_EE_DATAOUT) + word |= i; + EE_CLR(RL_EE_CLK); + DELAY(100); + } + + /* Turn off EEPROM access mode. */ + CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); + + *dest = word; + + return; +} + +/* + * Read a sequence of words from the EEPROM. + */ +static void rl_read_eeprom(sc, dest, off, cnt, swap) + struct rl_softc *sc; + caddr_t dest; + int off; + int cnt; + int swap; +{ + int i; + u_int16_t word = 0, *ptr; + + for (i = 0; i < cnt; i++) { + rl_eeprom_getword(sc, off + i, &word); + ptr = (u_int16_t *)(dest + (i * 2)); + if (swap) + *ptr = ntohs(word); + else + *ptr = word; + } + + return; +} + + +/* + * MII access routines are provided for the 8129, which + * doesn't have a built-in PHY. For the 8139, we fake things + * up by diverting rl_phy_readreg()/rl_phy_writereg() to the + * direct access PHY registers. + */ +#define MII_SET(x) \ + CSR_WRITE_1(sc, RL_MII, \ + CSR_READ_1(sc, RL_MII) | x) + +#define MII_CLR(x) \ + CSR_WRITE_1(sc, RL_MII, \ + CSR_READ_1(sc, RL_MII) & ~x) + +/* + * Sync the PHYs by setting data bit and strobing the clock 32 times. + */ +static void rl_mii_sync(sc) + struct rl_softc *sc; +{ + register int i; + + MII_SET(RL_MII_DIR|RL_MII_DATAOUT); + + for (i = 0; i < 32; i++) { + MII_SET(RL_MII_CLK); + DELAY(1); + MII_CLR(RL_MII_CLK); + DELAY(1); + } + + return; +} + +/* + * Clock a series of bits through the MII. + */ +static void rl_mii_send(sc, bits, cnt) + struct rl_softc *sc; + u_int32_t bits; + int cnt; +{ + int i; + + MII_CLR(RL_MII_CLK); + + for (i = (0x1 << (cnt - 1)); i; i >>= 1) { + if (bits & i) { + MII_SET(RL_MII_DATAOUT); + } else { + MII_CLR(RL_MII_DATAOUT); + } + DELAY(1); + MII_CLR(RL_MII_CLK); + DELAY(1); + MII_SET(RL_MII_CLK); + } +} + +/* + * Read an PHY register through the MII. + */ +static int rl_mii_readreg(sc, frame) + struct rl_softc *sc; + struct rl_mii_frame *frame; + +{ + int i, ack; + + RL_LOCK(sc); + + /* + * Set up frame for RX. + */ + frame->mii_stdelim = RL_MII_STARTDELIM; + frame->mii_opcode = RL_MII_READOP; + frame->mii_turnaround = 0; + frame->mii_data = 0; + + CSR_WRITE_2(sc, RL_MII, 0); + + /* + * Turn on data xmit. + */ + MII_SET(RL_MII_DIR); + + rl_mii_sync(sc); + + /* + * Send command/address info. + */ + rl_mii_send(sc, frame->mii_stdelim, 2); + rl_mii_send(sc, frame->mii_opcode, 2); + rl_mii_send(sc, frame->mii_phyaddr, 5); + rl_mii_send(sc, frame->mii_regaddr, 5); + + /* Idle bit */ + MII_CLR((RL_MII_CLK|RL_MII_DATAOUT)); + DELAY(1); + MII_SET(RL_MII_CLK); + DELAY(1); + + /* Turn off xmit. */ + MII_CLR(RL_MII_DIR); + + /* Check for ack */ + MII_CLR(RL_MII_CLK); + DELAY(1); + MII_SET(RL_MII_CLK); + DELAY(1); + ack = CSR_READ_2(sc, RL_MII) & RL_MII_DATAIN; + + /* + * Now try reading data bits. If the ack failed, we still + * need to clock through 16 cycles to keep the PHY(s) in sync. + */ + if (ack) { + for(i = 0; i < 16; i++) { + MII_CLR(RL_MII_CLK); + DELAY(1); + MII_SET(RL_MII_CLK); + DELAY(1); + } + goto fail; + } + + for (i = 0x8000; i; i >>= 1) { + MII_CLR(RL_MII_CLK); + DELAY(1); + if (!ack) { + if (CSR_READ_2(sc, RL_MII) & RL_MII_DATAIN) + frame->mii_data |= i; + DELAY(1); + } + MII_SET(RL_MII_CLK); + DELAY(1); + } + +fail: + + MII_CLR(RL_MII_CLK); + DELAY(1); + MII_SET(RL_MII_CLK); + DELAY(1); + + RL_UNLOCK(sc); + + if (ack) + return(1); + return(0); +} + +/* + * Write to a PHY register through the MII. + */ +static int rl_mii_writereg(sc, frame) + struct rl_softc *sc; + struct rl_mii_frame *frame; + +{ + RL_LOCK(sc); + + /* + * Set up frame for TX. + */ + + frame->mii_stdelim = RL_MII_STARTDELIM; + frame->mii_opcode = RL_MII_WRITEOP; + frame->mii_turnaround = RL_MII_TURNAROUND; + + /* + * Turn on data output. + */ + MII_SET(RL_MII_DIR); + + rl_mii_sync(sc); + + rl_mii_send(sc, frame->mii_stdelim, 2); + rl_mii_send(sc, frame->mii_opcode, 2); + rl_mii_send(sc, frame->mii_phyaddr, 5); + rl_mii_send(sc, frame->mii_regaddr, 5); + rl_mii_send(sc, frame->mii_turnaround, 2); + rl_mii_send(sc, frame->mii_data, 16); + + /* Idle bit. */ + MII_SET(RL_MII_CLK); + DELAY(1); + MII_CLR(RL_MII_CLK); + DELAY(1); + + /* + * Turn off xmit. + */ + MII_CLR(RL_MII_DIR); + + RL_UNLOCK(sc); + + return(0); +} + +static int rl_miibus_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct rl_softc *sc; + struct rl_mii_frame frame; + u_int16_t rval = 0; + u_int16_t rl8139_reg = 0; + + sc = device_get_softc(dev); + RL_LOCK(sc); + + if (sc->rl_type == RL_8139) { + /* Pretend the internal PHY is only at address 0 */ + if (phy) { + RL_UNLOCK(sc); + return(0); + } + switch(reg) { + case MII_BMCR: + rl8139_reg = RL_BMCR; + break; + case MII_BMSR: + rl8139_reg = RL_BMSR; + break; + case MII_ANAR: + rl8139_reg = RL_ANAR; + break; + case MII_ANER: + rl8139_reg = RL_ANER; + break; + case MII_ANLPAR: + rl8139_reg = RL_LPAR; + break; + case MII_PHYIDR1: + case MII_PHYIDR2: + RL_UNLOCK(sc); + return(0); + break; + /* + * Allow the rlphy driver to read the media status + * register. If we have a link partner which does not + * support NWAY, this is the register which will tell + * us the results of parallel detection. + */ + case RL_MEDIASTAT: + rval = CSR_READ_1(sc, RL_MEDIASTAT); + RL_UNLOCK(sc); + return(rval); + break; + default: + printf("rl%d: bad phy register\n", sc->rl_unit); + RL_UNLOCK(sc); + return(0); + } + rval = CSR_READ_2(sc, rl8139_reg); + RL_UNLOCK(sc); + return(rval); + } + + bzero((char *)&frame, sizeof(frame)); + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + rl_mii_readreg(sc, &frame); + RL_UNLOCK(sc); + + return(frame.mii_data); +} + +static int rl_miibus_writereg(dev, phy, reg, data) + device_t dev; + int phy, reg, data; +{ + struct rl_softc *sc; + struct rl_mii_frame frame; + u_int16_t rl8139_reg = 0; + + sc = device_get_softc(dev); + RL_LOCK(sc); + + if (sc->rl_type == RL_8139) { + /* Pretend the internal PHY is only at address 0 */ + if (phy) { + RL_UNLOCK(sc); + return(0); + } + switch(reg) { + case MII_BMCR: + rl8139_reg = RL_BMCR; + break; + case MII_BMSR: + rl8139_reg = RL_BMSR; + break; + case MII_ANAR: + rl8139_reg = RL_ANAR; + break; + case MII_ANER: + rl8139_reg = RL_ANER; + break; + case MII_ANLPAR: + rl8139_reg = RL_LPAR; + break; + case MII_PHYIDR1: + case MII_PHYIDR2: + RL_UNLOCK(sc); + return(0); + break; + default: + printf("rl%d: bad phy register\n", sc->rl_unit); + RL_UNLOCK(sc); + return(0); + } + CSR_WRITE_2(sc, rl8139_reg, data); + RL_UNLOCK(sc); + return(0); + } + + bzero((char *)&frame, sizeof(frame)); + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + frame.mii_data = data; + + rl_mii_writereg(sc, &frame); + + RL_UNLOCK(sc); + return(0); +} + +static void rl_miibus_statchg(dev) + device_t dev; +{ + return; +} + +/* + * Calculate CRC of a multicast group address, return the upper 6 bits. + */ +static u_int8_t rl_calchash(addr) + caddr_t addr; +{ + u_int32_t crc, carry; + int i, j; + u_int8_t c; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (i = 0; i < 6; i++) { + c = *(addr + i); + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); + crc <<= 1; + c >>= 1; + if (carry) + crc = (crc ^ 0x04c11db6) | carry; + } + } + + /* return the filter bit position */ + return(crc >> 26); +} + +/* + * Program the 64-bit multicast hash filter. + */ +static void rl_setmulti(sc) + struct rl_softc *sc; +{ + struct ifnet *ifp; + int h = 0; + u_int32_t hashes[2] = { 0, 0 }; + struct ifmultiaddr *ifma; + u_int32_t rxfilt; + int mcnt = 0; + + ifp = &sc->arpcom.ac_if; + + rxfilt = CSR_READ_4(sc, RL_RXCFG); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + rxfilt |= RL_RXCFG_RX_MULTI; + CSR_WRITE_4(sc, RL_RXCFG, rxfilt); + CSR_WRITE_4(sc, RL_MAR0, 0xFFFFFFFF); + CSR_WRITE_4(sc, RL_MAR4, 0xFFFFFFFF); + return; + } + + /* first, zot all the existing hash bits */ + CSR_WRITE_4(sc, RL_MAR0, 0); + CSR_WRITE_4(sc, RL_MAR4, 0); + + /* now program new ones */ + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = rl_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + mcnt++; + } + + if (mcnt) + rxfilt |= RL_RXCFG_RX_MULTI; + else + rxfilt &= ~RL_RXCFG_RX_MULTI; + + CSR_WRITE_4(sc, RL_RXCFG, rxfilt); + CSR_WRITE_4(sc, RL_MAR0, hashes[0]); + CSR_WRITE_4(sc, RL_MAR4, hashes[1]); + + return; +} + +static void rl_reset(sc) + struct rl_softc *sc; +{ + register int i; + + CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_RESET); + + for (i = 0; i < RL_TIMEOUT; i++) { + DELAY(10); + if (!(CSR_READ_1(sc, RL_COMMAND) & RL_CMD_RESET)) + break; + } + if (i == RL_TIMEOUT) + printf("rl%d: reset never completed!\n", sc->rl_unit); + + return; +} + +/* + * Probe for a RealTek 8129/8139 chip. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + */ +static int rl_probe(dev) + device_t dev; +{ + struct rl_type *t; + + t = rl_devs; + + while(t->rl_name != NULL) { + if ((pci_get_vendor(dev) == t->rl_vid) && + (pci_get_device(dev) == t->rl_did)) { + device_set_desc(dev, t->rl_name); + return(0); + } + t++; + } + + return(ENXIO); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int rl_attach(dev) + device_t dev; +{ + u_char eaddr[ETHER_ADDR_LEN]; + u_int32_t command; + struct rl_softc *sc; + struct ifnet *ifp; + u_int16_t rl_did = 0; + int unit, error = 0, rid; + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + bzero(sc, sizeof(struct rl_softc)); + + mtx_init(&sc->rl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF | MTX_RECURSE); + RL_LOCK(sc); + + /* + * Handle power management nonsense. + */ + + if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { + u_int32_t iobase, membase, irq; + + /* Save important PCI config data. */ + iobase = pci_read_config(dev, RL_PCI_LOIO, 4); + membase = pci_read_config(dev, RL_PCI_LOMEM, 4); + irq = pci_read_config(dev, RL_PCI_INTLINE, 4); + + /* Reset the power state. */ + printf("rl%d: chip is is in D%d power mode " + "-- setting to D0\n", unit, + pci_get_powerstate(dev)); + + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + + /* Restore PCI config data. */ + pci_write_config(dev, RL_PCI_LOIO, iobase, 4); + pci_write_config(dev, RL_PCI_LOMEM, membase, 4); + pci_write_config(dev, RL_PCI_INTLINE, irq, 4); + } + + /* + * Map control/status registers. + */ + pci_enable_busmaster(dev); + pci_enable_io(dev, SYS_RES_IOPORT); + pci_enable_io(dev, SYS_RES_MEMORY); + command = pci_read_config(dev, PCIR_COMMAND, 4); + +#ifdef RL_USEIOSPACE + if (!(command & PCIM_CMD_PORTEN)) { + printf("rl%d: failed to enable I/O ports!\n", unit); + error = ENXIO; + goto fail; + } +#else + if (!(command & PCIM_CMD_MEMEN)) { + printf("rl%d: failed to enable memory mapping!\n", unit); + error = ENXIO; + goto fail; + } +#endif + + rid = RL_RID; + sc->rl_res = bus_alloc_resource(dev, RL_RES, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (sc->rl_res == NULL) { + printf ("rl%d: couldn't map ports/memory\n", unit); + error = ENXIO; + goto fail; + } + + /* Detect the Realtek 8139B. For some reason, this chip is very + * unstable when left to autoselect the media + * The best workaround is to set the device to the required + * media type or to set it to the 10 Meg speed. + */ + + if ((rman_get_end(sc->rl_res)-rman_get_start(sc->rl_res))==0xff) { + printf("rl%d: Realtek 8139B detected. Warning, this may be unstable in autoselect mode\n", unit); + } + + sc->rl_btag = rman_get_bustag(sc->rl_res); + sc->rl_bhandle = rman_get_bushandle(sc->rl_res); + + rid = 0; + sc->rl_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->rl_irq == NULL) { + printf("rl%d: couldn't map interrupt\n", unit); + bus_release_resource(dev, RL_RES, RL_RID, sc->rl_res); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->rl_irq, INTR_TYPE_NET, + rl_intr, sc, &sc->rl_intrhand); + + if (error) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->rl_irq); + bus_release_resource(dev, RL_RES, RL_RID, sc->rl_res); + printf("rl%d: couldn't set up irq\n", unit); + goto fail; + } + + callout_handle_init(&sc->rl_stat_ch); + + /* Reset the adapter. */ + rl_reset(sc); + sc->rl_eecmd_read = RL_EECMD_READ_6BIT; + rl_read_eeprom(sc, (caddr_t)&rl_did, 0, 1, 0); + if (rl_did != 0x8129) + sc->rl_eecmd_read = RL_EECMD_READ_8BIT; + + /* + * Get station address from the EEPROM. + */ + rl_read_eeprom(sc, (caddr_t)&eaddr, RL_EE_EADDR, 3, 0); + + /* + * A RealTek chip was detected. Inform the world. + */ + printf("rl%d: Ethernet address: %6D\n", unit, eaddr, ":"); + + sc->rl_unit = unit; + bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); + + /* + * Now read the exact device type from the EEPROM to find + * out if it's an 8129 or 8139. + */ + rl_read_eeprom(sc, (caddr_t)&rl_did, RL_EE_PCI_DID, 1, 0); + + if (rl_did == RT_DEVICEID_8139 || rl_did == ACCTON_DEVICEID_5030 || + rl_did == DELTA_DEVICEID_8139 || rl_did == ADDTRON_DEVICEID_8139 || + rl_did == RT_DEVICEID_8138 || rl_did == DLINK_DEVICEID_530TXPLUS || + rl_did == DLINK_DEVICEID_690TXD) + sc->rl_type = RL_8139; + else if (rl_did == RT_DEVICEID_8129) + sc->rl_type = RL_8129; + else { + printf("rl%d: unknown device ID: %x\n", unit, rl_did); + bus_teardown_intr(dev, sc->rl_irq, sc->rl_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->rl_irq); + bus_release_resource(dev, RL_RES, RL_RID, sc->rl_res); + error = ENXIO; + goto fail; + } + + /* + * Allocate the parent bus DMA tag appropriate for PCI. + */ +#define RL_NSEG_NEW 32 + error = bus_dma_tag_create(NULL, /* parent */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MAXBSIZE, RL_NSEG_NEW, /* maxsize, nsegments */ + BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + &sc->rl_parent_tag); + + /* + * Now allocate a tag for the DMA descriptor lists. + * All of our lists are allocated as a contiguous block + * of memory. + */ + error = bus_dma_tag_create(sc->rl_parent_tag, /* parent */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + RL_RXBUFLEN + 1518, 1, /* maxsize,nsegments */ + BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ + 0, /* flags */ + &sc->rl_tag); + + /* + * Now allocate a chunk of DMA-able memory based on the + * tag we just created. + */ + error = bus_dmamem_alloc(sc->rl_tag, + (void **)&sc->rl_cdata.rl_rx_buf, BUS_DMA_NOWAIT, + &sc->rl_cdata.rl_rx_dmamap); + + if (sc->rl_cdata.rl_rx_buf == NULL) { + printf("rl%d: no memory for list buffers!\n", unit); + bus_teardown_intr(dev, sc->rl_irq, sc->rl_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->rl_irq); + bus_release_resource(dev, RL_RES, RL_RID, sc->rl_res); + bus_dma_tag_destroy(sc->rl_tag); + error = ENXIO; + goto fail; + } + + /* Leave a few bytes before the start of the RX ring buffer. */ + sc->rl_cdata.rl_rx_buf_ptr = sc->rl_cdata.rl_rx_buf; + sc->rl_cdata.rl_rx_buf += sizeof(u_int64_t); + + /* Do MII setup */ + if (mii_phy_probe(dev, &sc->rl_miibus, + rl_ifmedia_upd, rl_ifmedia_sts)) { + printf("rl%d: MII without any phy!\n", sc->rl_unit); + bus_teardown_intr(dev, sc->rl_irq, sc->rl_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->rl_irq); + bus_release_resource(dev, RL_RES, RL_RID, sc->rl_res); + bus_dmamem_free(sc->rl_tag, + sc->rl_cdata.rl_rx_buf, sc->rl_cdata.rl_rx_dmamap); + bus_dma_tag_destroy(sc->rl_tag); + error = ENXIO; + goto fail; + } + + ifp = &sc->arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = "rl"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = rl_ioctl; + ifp->if_output = ether_output; + ifp->if_start = rl_start; + ifp->if_watchdog = rl_watchdog; + ifp->if_init = rl_init; + ifp->if_baudrate = 10000000; + ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); + RL_UNLOCK(sc); + return(0); + +fail: + RL_UNLOCK(sc); + mtx_destroy(&sc->rl_mtx); + return(error); +} + +static int rl_detach(dev) + device_t dev; +{ + struct rl_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + RL_LOCK(sc); + ifp = &sc->arpcom.ac_if; + + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + rl_stop(sc); + + bus_generic_detach(dev); + device_delete_child(dev, sc->rl_miibus); + + bus_teardown_intr(dev, sc->rl_irq, sc->rl_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->rl_irq); + bus_release_resource(dev, RL_RES, RL_RID, sc->rl_res); + + bus_dmamap_unload(sc->rl_tag, sc->rl_cdata.rl_rx_dmamap); + bus_dmamem_free(sc->rl_tag, sc->rl_cdata.rl_rx_buf, + sc->rl_cdata.rl_rx_dmamap); + bus_dma_tag_destroy(sc->rl_tag); + bus_dma_tag_destroy(sc->rl_parent_tag); + + RL_UNLOCK(sc); + mtx_destroy(&sc->rl_mtx); + + return(0); +} + +/* + * Initialize the transmit descriptors. + */ +static int rl_list_tx_init(sc) + struct rl_softc *sc; +{ + struct rl_chain_data *cd; + int i; + + cd = &sc->rl_cdata; + for (i = 0; i < RL_TX_LIST_CNT; i++) { + cd->rl_tx_chain[i] = NULL; + CSR_WRITE_4(sc, + RL_TXADDR0 + (i * sizeof(u_int32_t)), 0x0000000); + } + + sc->rl_cdata.cur_tx = 0; + sc->rl_cdata.last_tx = 0; + + return(0); +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + * + * You know there's something wrong with a PCI bus-master chip design + * when you have to use m_devget(). + * + * The receive operation is badly documented in the datasheet, so I'll + * attempt to document it here. The driver provides a buffer area and + * places its base address in the RX buffer start address register. + * The chip then begins copying frames into the RX buffer. Each frame + * is preceded by a 32-bit RX status word which specifies the length + * of the frame and certain other status bits. Each frame (starting with + * the status word) is also 32-bit aligned. The frame length is in the + * first 16 bits of the status word; the lower 15 bits correspond with + * the 'rx status register' mentioned in the datasheet. + * + * Note: to make the Alpha happy, the frame payload needs to be aligned + * on a 32-bit boundary. To achieve this, we pass RL_ETHER_ALIGN (2 bytes) + * as the offset argument to m_devget(). + */ +static void rl_rxeof(sc) + struct rl_softc *sc; +{ + struct ether_header *eh; + struct mbuf *m; + struct ifnet *ifp; + int total_len = 0; + u_int32_t rxstat; + caddr_t rxbufpos; + int wrap = 0; + u_int16_t cur_rx; + u_int16_t limit; + u_int16_t rx_bytes = 0, max_bytes; + + ifp = &sc->arpcom.ac_if; + + bus_dmamap_sync(sc->rl_tag, sc->rl_cdata.rl_rx_dmamap, + BUS_DMASYNC_POSTWRITE); + + cur_rx = (CSR_READ_2(sc, RL_CURRXADDR) + 16) % RL_RXBUFLEN; + + /* Do not try to read past this point. */ + limit = CSR_READ_2(sc, RL_CURRXBUF) % RL_RXBUFLEN; + + if (limit < cur_rx) + max_bytes = (RL_RXBUFLEN - cur_rx) + limit; + else + max_bytes = limit - cur_rx; + + while((CSR_READ_1(sc, RL_COMMAND) & RL_CMD_EMPTY_RXBUF) == 0) { +#ifdef DEVICE_POLLING + if (ifp->if_ipending & IFF_POLLING) { + if (sc->rxcycles <= 0) + break; + sc->rxcycles--; + } +#endif /* DEVICE_POLLING */ + rxbufpos = sc->rl_cdata.rl_rx_buf + cur_rx; + rxstat = *(u_int32_t *)rxbufpos; + + /* + * Here's a totally undocumented fact for you. When the + * RealTek chip is in the process of copying a packet into + * RAM for you, the length will be 0xfff0. If you spot a + * packet header with this value, you need to stop. The + * datasheet makes absolutely no mention of this and + * RealTek should be shot for this. + */ + if ((u_int16_t)(rxstat >> 16) == RL_RXSTAT_UNFINISHED) + break; + + if (!(rxstat & RL_RXSTAT_RXOK)) { + ifp->if_ierrors++; + rl_init(sc); + return; + } + + /* No errors; receive the packet. */ + total_len = rxstat >> 16; + rx_bytes += total_len + 4; + + /* + * XXX The RealTek chip includes the CRC with every + * received frame, and there's no way to turn this + * behavior off (at least, I can't find anything in + * the manual that explains how to do it) so we have + * to trim off the CRC manually. + */ + total_len -= ETHER_CRC_LEN; + + /* + * Avoid trying to read more bytes than we know + * the chip has prepared for us. + */ + if (rx_bytes > max_bytes) + break; + + rxbufpos = sc->rl_cdata.rl_rx_buf + + ((cur_rx + sizeof(u_int32_t)) % RL_RXBUFLEN); + + if (rxbufpos == (sc->rl_cdata.rl_rx_buf + RL_RXBUFLEN)) + rxbufpos = sc->rl_cdata.rl_rx_buf; + + wrap = (sc->rl_cdata.rl_rx_buf + RL_RXBUFLEN) - rxbufpos; + + if (total_len > wrap) { + m = m_devget(rxbufpos, total_len, RL_ETHER_ALIGN, ifp, + NULL); + if (m == NULL) { + ifp->if_ierrors++; + } else { + m_copyback(m, wrap, total_len - wrap, + sc->rl_cdata.rl_rx_buf); + } + cur_rx = (total_len - wrap + ETHER_CRC_LEN); + } else { + m = m_devget(rxbufpos, total_len, RL_ETHER_ALIGN, ifp, + NULL); + if (m == NULL) { + ifp->if_ierrors++; + } + cur_rx += total_len + 4 + ETHER_CRC_LEN; + } + + /* + * Round up to 32-bit boundary. + */ + cur_rx = (cur_rx + 3) & ~3; + CSR_WRITE_2(sc, RL_CURRXADDR, cur_rx - 16); + + if (m == NULL) + continue; + + eh = mtod(m, struct ether_header *); + ifp->if_ipackets++; + + /* Remove header from mbuf and pass it on. */ + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, eh, m); + } + + return; +} + +/* + * A frame was downloaded to the chip. It's safe for us to clean up + * the list buffers. + */ +static void rl_txeof(sc) + struct rl_softc *sc; +{ + struct ifnet *ifp; + u_int32_t txstat; + + ifp = &sc->arpcom.ac_if; + + /* Clear the timeout timer. */ + ifp->if_timer = 0; + + /* + * Go through our tx list and free mbufs for those + * frames that have been uploaded. + */ + do { + txstat = CSR_READ_4(sc, RL_LAST_TXSTAT(sc)); + if (!(txstat & (RL_TXSTAT_TX_OK| + RL_TXSTAT_TX_UNDERRUN|RL_TXSTAT_TXABRT))) + break; + + ifp->if_collisions += (txstat & RL_TXSTAT_COLLCNT) >> 24; + + if (RL_LAST_TXMBUF(sc) != NULL) { + bus_dmamap_unload(sc->rl_tag, RL_LAST_DMAMAP(sc)); + bus_dmamap_destroy(sc->rl_tag, RL_LAST_DMAMAP(sc)); + m_freem(RL_LAST_TXMBUF(sc)); + RL_LAST_TXMBUF(sc) = NULL; + } + if (txstat & RL_TXSTAT_TX_OK) + ifp->if_opackets++; + else { + int oldthresh; + ifp->if_oerrors++; + if ((txstat & RL_TXSTAT_TXABRT) || + (txstat & RL_TXSTAT_OUTOFWIN)) + CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG); + oldthresh = sc->rl_txthresh; + /* error recovery */ + rl_reset(sc); + rl_init(sc); + /* + * If there was a transmit underrun, + * bump the TX threshold. + */ + if (txstat & RL_TXSTAT_TX_UNDERRUN) + sc->rl_txthresh = oldthresh + 32; + return; + } + RL_INC(sc->rl_cdata.last_tx); + ifp->if_flags &= ~IFF_OACTIVE; + } while (sc->rl_cdata.last_tx != sc->rl_cdata.cur_tx); + + return; +} + +static void rl_tick(xsc) + void *xsc; +{ + struct rl_softc *sc; + struct mii_data *mii; + + sc = xsc; + RL_LOCK(sc); + mii = device_get_softc(sc->rl_miibus); + + mii_tick(mii); + + sc->rl_stat_ch = timeout(rl_tick, sc, hz); + RL_UNLOCK(sc); + + return; +} + +#ifdef DEVICE_POLLING +static void +rl_poll (struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + struct rl_softc *sc = ifp->if_softc; + + RL_LOCK(sc); + if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */ + CSR_WRITE_2(sc, RL_IMR, RL_INTRS); + goto done; + } + + sc->rxcycles = count; + rl_rxeof(sc); + rl_txeof(sc); + if (ifp->if_snd.ifq_head != NULL) + rl_start(ifp); + + if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */ + u_int16_t status; + + status = CSR_READ_2(sc, RL_ISR); + if (status) + CSR_WRITE_2(sc, RL_ISR, status); + + /* + * XXX check behaviour on receiver stalls. + */ + + if (status & RL_ISR_SYSTEM_ERR) { + rl_reset(sc); + rl_init(sc); + } + } +done: + RL_UNLOCK(sc); +} +#endif /* DEVICE_POLLING */ + +static void rl_intr(arg) + void *arg; +{ + struct rl_softc *sc; + struct ifnet *ifp; + u_int16_t status; + + sc = arg; + + if (sc->suspended) { + return; + } + + RL_LOCK(sc); + ifp = &sc->arpcom.ac_if; + +#ifdef DEVICE_POLLING + if (ifp->if_ipending & IFF_POLLING) + goto done; + if (ether_poll_register(rl_poll, ifp)) { /* ok, disable interrupts */ + CSR_WRITE_2(sc, RL_IMR, 0x0000); + rl_poll(ifp, 0, 1); + goto done; + } +#endif /* DEVICE_POLLING */ + + for (;;) { + + status = CSR_READ_2(sc, RL_ISR); + if (status) + CSR_WRITE_2(sc, RL_ISR, status); + + if ((status & RL_INTRS) == 0) + break; + + if (status & RL_ISR_RX_OK) + rl_rxeof(sc); + + if (status & RL_ISR_RX_ERR) + rl_rxeof(sc); + + if ((status & RL_ISR_TX_OK) || (status & RL_ISR_TX_ERR)) + rl_txeof(sc); + + if (status & RL_ISR_SYSTEM_ERR) { + rl_reset(sc); + rl_init(sc); + } + + } + + if (ifp->if_snd.ifq_head != NULL) + rl_start(ifp); + +#ifdef DEVICE_POLLING +done: +#endif + RL_UNLOCK(sc); + + return; +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int rl_encap(sc, m_head) + struct rl_softc *sc; + struct mbuf *m_head; +{ + struct mbuf *m_new = NULL; + + /* + * The RealTek is brain damaged and wants longword-aligned + * TX buffers, plus we can only have one fragment buffer + * per packet. We have to copy pretty much all the time. + */ + + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(1); + if (m_head->m_pkthdr.len > MHLEN) { + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + return(1); + } + } + m_copydata(m_head, 0, m_head->m_pkthdr.len, mtod(m_new, caddr_t)); + m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; + m_freem(m_head); + m_head = m_new; + + /* Pad frames to at least 60 bytes. */ + if (m_head->m_pkthdr.len < RL_MIN_FRAMELEN) { + /* + * Make security concious people happy: zero out the + * bytes in the pad area, since we don't know what + * this mbuf cluster buffer's previous user might + * have left in it. + */ + bzero(mtod(m_head, char *) + m_head->m_pkthdr.len, + RL_MIN_FRAMELEN - m_head->m_pkthdr.len); + m_head->m_pkthdr.len += + (RL_MIN_FRAMELEN - m_head->m_pkthdr.len); + m_head->m_len = m_head->m_pkthdr.len; + } + + RL_CUR_TXMBUF(sc) = m_head; + + return(0); +} + +/* + * Main transmit routine. + */ + +static void rl_start(ifp) + struct ifnet *ifp; +{ + struct rl_softc *sc; + struct mbuf *m_head = NULL; + + sc = ifp->if_softc; + RL_LOCK(sc); + + while(RL_CUR_TXMBUF(sc) == NULL) { + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + if (rl_encap(sc, m_head)) { + IF_PREPEND(&ifp->if_snd, m_head); + ifp->if_flags |= IFF_OACTIVE; + break; + } + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, RL_CUR_TXMBUF(sc)); + + /* + * Transmit the frame. + */ + bus_dmamap_create(sc->rl_tag, 0, &RL_CUR_DMAMAP(sc)); + bus_dmamap_load(sc->rl_tag, RL_CUR_DMAMAP(sc), + mtod(RL_CUR_TXMBUF(sc), void *), + RL_CUR_TXMBUF(sc)->m_pkthdr.len, rl_dma_map_txbuf, sc, 0); + bus_dmamap_sync(sc->rl_tag, RL_CUR_DMAMAP(sc), + BUS_DMASYNC_PREREAD); + CSR_WRITE_4(sc, RL_CUR_TXSTAT(sc), + RL_TXTHRESH(sc->rl_txthresh) | + RL_CUR_TXMBUF(sc)->m_pkthdr.len); + + RL_INC(sc->rl_cdata.cur_tx); + } + + /* + * We broke out of the loop because all our TX slots are + * full. Mark the NIC as busy until it drains some of the + * packets from the queue. + */ + if (RL_CUR_TXMBUF(sc) != NULL) + ifp->if_flags |= IFF_OACTIVE; + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + RL_UNLOCK(sc); + + return; +} + +static void rl_init(xsc) + void *xsc; +{ + struct rl_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mii_data *mii; + int i; + u_int32_t rxcfg = 0; + + RL_LOCK(sc); + mii = device_get_softc(sc->rl_miibus); + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + rl_stop(sc); + + /* Init our MAC address */ + for (i = 0; i < ETHER_ADDR_LEN; i++) { + CSR_WRITE_1(sc, RL_IDR0 + i, sc->arpcom.ac_enaddr[i]); + } + + /* Init the RX buffer pointer register. */ + bus_dmamap_load(sc->rl_tag, sc->rl_cdata.rl_rx_dmamap, + sc->rl_cdata.rl_rx_buf, RL_RXBUFLEN, rl_dma_map_rxbuf, sc, 0); + bus_dmamap_sync(sc->rl_tag, sc->rl_cdata.rl_rx_dmamap, + BUS_DMASYNC_PREWRITE); + + /* Init TX descriptors. */ + rl_list_tx_init(sc); + + /* + * Enable transmit and receive. + */ + CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB); + + /* + * Set the initial TX and RX configuration. + */ + CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG); + CSR_WRITE_4(sc, RL_RXCFG, RL_RXCFG_CONFIG); + + /* Set the individual bit to receive frames for this host only. */ + rxcfg = CSR_READ_4(sc, RL_RXCFG); + rxcfg |= RL_RXCFG_RX_INDIV; + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) { + rxcfg |= RL_RXCFG_RX_ALLPHYS; + CSR_WRITE_4(sc, RL_RXCFG, rxcfg); + } else { + rxcfg &= ~RL_RXCFG_RX_ALLPHYS; + CSR_WRITE_4(sc, RL_RXCFG, rxcfg); + } + + /* + * Set capture broadcast bit to capture broadcast frames. + */ + if (ifp->if_flags & IFF_BROADCAST) { + rxcfg |= RL_RXCFG_RX_BROAD; + CSR_WRITE_4(sc, RL_RXCFG, rxcfg); + } else { + rxcfg &= ~RL_RXCFG_RX_BROAD; + CSR_WRITE_4(sc, RL_RXCFG, rxcfg); + } + + /* + * Program the multicast filter, if necessary. + */ + rl_setmulti(sc); + +#ifdef DEVICE_POLLING + /* + * Disable interrupts if we are polling. + */ + if (ifp->if_ipending & IFF_POLLING) + CSR_WRITE_2(sc, RL_IMR, 0); + else /* otherwise ... */ +#endif /* DEVICE_POLLING */ + /* + * Enable interrupts. + */ + CSR_WRITE_2(sc, RL_IMR, RL_INTRS); + + /* Set initial TX threshold */ + sc->rl_txthresh = RL_TX_THRESH_INIT; + + /* Start RX/TX process. */ + CSR_WRITE_4(sc, RL_MISSEDPKT, 0); + + /* Enable receiver and transmitter. */ + CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB); + + mii_mediachg(mii); + + CSR_WRITE_1(sc, RL_CFG1, RL_CFG1_DRVLOAD|RL_CFG1_FULLDUPLEX); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + sc->rl_stat_ch = timeout(rl_tick, sc, hz); + RL_UNLOCK(sc); + + return; +} + +/* + * Set media options. + */ +static int rl_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct rl_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = device_get_softc(sc->rl_miibus); + mii_mediachg(mii); + + return(0); +} + +/* + * Report current media status. + */ +static void rl_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct rl_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = device_get_softc(sc->rl_miibus); + + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + + return; +} + +static int rl_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct rl_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + struct mii_data *mii; + int error = 0; + + RL_LOCK(sc); + + switch(command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + rl_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) + rl_stop(sc); + } + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + rl_setmulti(sc); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = device_get_softc(sc->rl_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + break; + default: + error = EINVAL; + break; + } + + RL_UNLOCK(sc); + + return(error); +} + +static void rl_watchdog(ifp) + struct ifnet *ifp; +{ + struct rl_softc *sc; + + sc = ifp->if_softc; + RL_LOCK(sc); + printf("rl%d: watchdog timeout\n", sc->rl_unit); + ifp->if_oerrors++; + + rl_txeof(sc); + rl_rxeof(sc); + rl_init(sc); + RL_UNLOCK(sc); + + return; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void rl_stop(sc) + struct rl_softc *sc; +{ + register int i; + struct ifnet *ifp; + + RL_LOCK(sc); + ifp = &sc->arpcom.ac_if; + ifp->if_timer = 0; + + untimeout(rl_tick, sc, sc->rl_stat_ch); + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); +#ifdef DEVICE_POLLING + ether_poll_deregister(ifp); +#endif /* DEVICE_POLLING */ + + CSR_WRITE_1(sc, RL_COMMAND, 0x00); + CSR_WRITE_2(sc, RL_IMR, 0x0000); + bus_dmamap_unload(sc->rl_tag, sc->rl_cdata.rl_rx_dmamap); + + /* + * Free the TX list buffers. + */ + for (i = 0; i < RL_TX_LIST_CNT; i++) { + if (sc->rl_cdata.rl_tx_chain[i] != NULL) { + bus_dmamap_unload(sc->rl_tag, + sc->rl_cdata.rl_tx_dmamap[i]); + bus_dmamap_destroy(sc->rl_tag, + sc->rl_cdata.rl_tx_dmamap[i]); + m_freem(sc->rl_cdata.rl_tx_chain[i]); + sc->rl_cdata.rl_tx_chain[i] = NULL; + CSR_WRITE_4(sc, RL_TXADDR0 + i, 0x0000000); + } + } + + RL_UNLOCK(sc); + return; +} + +/* + * Device suspend routine. Stop the interface and save some PCI + * settings in case the BIOS doesn't restore them properly on + * resume. + */ +static int rl_suspend(dev) + device_t dev; +{ + register int i; + struct rl_softc *sc; + + sc = device_get_softc(dev); + + rl_stop(sc); + + for (i = 0; i < 5; i++) + sc->saved_maps[i] = pci_read_config(dev, PCIR_MAPS + i * 4, 4); + sc->saved_biosaddr = pci_read_config(dev, PCIR_BIOS, 4); + sc->saved_intline = pci_read_config(dev, PCIR_INTLINE, 1); + sc->saved_cachelnsz = pci_read_config(dev, PCIR_CACHELNSZ, 1); + sc->saved_lattimer = pci_read_config(dev, PCIR_LATTIMER, 1); + + sc->suspended = 1; + + return (0); +} + +/* + * Device resume routine. Restore some PCI settings in case the BIOS + * doesn't, re-enable busmastering, and restart the interface if + * appropriate. + */ +static int rl_resume(dev) + device_t dev; +{ + register int i; + struct rl_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + ifp = &sc->arpcom.ac_if; + + /* better way to do this? */ + for (i = 0; i < 5; i++) + pci_write_config(dev, PCIR_MAPS + i * 4, sc->saved_maps[i], 4); + pci_write_config(dev, PCIR_BIOS, sc->saved_biosaddr, 4); + pci_write_config(dev, PCIR_INTLINE, sc->saved_intline, 1); + pci_write_config(dev, PCIR_CACHELNSZ, sc->saved_cachelnsz, 1); + pci_write_config(dev, PCIR_LATTIMER, sc->saved_lattimer, 1); + + /* reenable busmastering */ + pci_enable_busmaster(dev); + pci_enable_io(dev, RL_RES); + + /* reinitialize interface if necessary */ + if (ifp->if_flags & IFF_UP) + rl_init(sc); + + sc->suspended = 0; + + return (0); +} + +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static void rl_shutdown(dev) + device_t dev; +{ + struct rl_softc *sc; + + sc = device_get_softc(dev); + + rl_stop(sc); + + return; +} diff --git a/sys/pci/if_rlreg.h b/sys/pci/if_rlreg.h new file mode 100644 index 0000000..19b1852 --- /dev/null +++ b/sys/pci/if_rlreg.h @@ -0,0 +1,511 @@ +/* + * Copyright (c) 1997, 1998 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * RealTek 8129/8139 register offsets + */ +#define RL_IDR0 0x0000 /* ID register 0 (station addr) */ +#define RL_IDR1 0x0001 /* Must use 32-bit accesses (?) */ +#define RL_IDR2 0x0002 +#define RL_IDR3 0x0003 +#define RL_IDR4 0x0004 +#define RL_IDR5 0x0005 + /* 0006-0007 reserved */ +#define RL_MAR0 0x0008 /* Multicast hash table */ +#define RL_MAR1 0x0009 +#define RL_MAR2 0x000A +#define RL_MAR3 0x000B +#define RL_MAR4 0x000C +#define RL_MAR5 0x000D +#define RL_MAR6 0x000E +#define RL_MAR7 0x000F + +#define RL_TXSTAT0 0x0010 /* status of TX descriptor 0 */ +#define RL_TXSTAT1 0x0014 /* status of TX descriptor 1 */ +#define RL_TXSTAT2 0x0018 /* status of TX descriptor 2 */ +#define RL_TXSTAT3 0x001C /* status of TX descriptor 3 */ + +#define RL_TXADDR0 0x0020 /* address of TX descriptor 0 */ +#define RL_TXADDR1 0x0024 /* address of TX descriptor 1 */ +#define RL_TXADDR2 0x0028 /* address of TX descriptor 2 */ +#define RL_TXADDR3 0x002C /* address of TX descriptor 3 */ + +#define RL_RXADDR 0x0030 /* RX ring start address */ +#define RL_RX_EARLY_BYTES 0x0034 /* RX early byte count */ +#define RL_RX_EARLY_STAT 0x0036 /* RX early status */ +#define RL_COMMAND 0x0037 /* command register */ +#define RL_CURRXADDR 0x0038 /* current address of packet read */ +#define RL_CURRXBUF 0x003A /* current RX buffer address */ +#define RL_IMR 0x003C /* interrupt mask register */ +#define RL_ISR 0x003E /* interrupt status register */ +#define RL_TXCFG 0x0040 /* transmit config */ +#define RL_RXCFG 0x0044 /* receive config */ +#define RL_TIMERCNT 0x0048 /* timer count register */ +#define RL_MISSEDPKT 0x004C /* missed packet counter */ +#define RL_EECMD 0x0050 /* EEPROM command register */ +#define RL_CFG0 0x0051 /* config register #0 */ +#define RL_CFG1 0x0052 /* config register #1 */ + /* 0053-0057 reserved */ +#define RL_MEDIASTAT 0x0058 /* media status register (8139) */ + /* 0059-005A reserved */ +#define RL_MII 0x005A /* 8129 chip only */ +#define RL_HALTCLK 0x005B +#define RL_MULTIINTR 0x005C /* multiple interrupt */ +#define RL_PCIREV 0x005E /* PCI revision value */ + /* 005F reserved */ +#define RL_TXSTAT_ALL 0x0060 /* TX status of all descriptors */ + +/* Direct PHY access registers only available on 8139 */ +#define RL_BMCR 0x0062 /* PHY basic mode control */ +#define RL_BMSR 0x0064 /* PHY basic mode status */ +#define RL_ANAR 0x0066 /* PHY autoneg advert */ +#define RL_LPAR 0x0068 /* PHY link partner ability */ +#define RL_ANER 0x006A /* PHY autoneg expansion */ + +#define RL_DISCCNT 0x006C /* disconnect counter */ +#define RL_FALSECAR 0x006E /* false carrier counter */ +#define RL_NWAYTST 0x0070 /* NWAY test register */ +#define RL_RX_ER 0x0072 /* RX_ER counter */ +#define RL_CSCFG 0x0074 /* CS configuration register */ + + +/* + * TX config register bits + */ +#define RL_TXCFG_CLRABRT 0x00000001 /* retransmit aborted pkt */ +#define RL_TXCFG_MAXDMA 0x00000700 /* max DMA burst size */ +#define RL_TXCFG_CRCAPPEND 0x00010000 /* CRC append (0 = yes) */ +#define RL_TXCFG_LOOPBKTST 0x00060000 /* loopback test */ +#define RL_TXCFG_IFG 0x03000000 /* interframe gap */ + +#define RL_TXDMA_16BYTES 0x00000000 +#define RL_TXDMA_32BYTES 0x00000100 +#define RL_TXDMA_64BYTES 0x00000200 +#define RL_TXDMA_128BYTES 0x00000300 +#define RL_TXDMA_256BYTES 0x00000400 +#define RL_TXDMA_512BYTES 0x00000500 +#define RL_TXDMA_1024BYTES 0x00000600 +#define RL_TXDMA_2048BYTES 0x00000700 + +/* + * Transmit descriptor status register bits. + */ +#define RL_TXSTAT_LENMASK 0x00001FFF +#define RL_TXSTAT_OWN 0x00002000 +#define RL_TXSTAT_TX_UNDERRUN 0x00004000 +#define RL_TXSTAT_TX_OK 0x00008000 +#define RL_TXSTAT_EARLY_THRESH 0x003F0000 +#define RL_TXSTAT_COLLCNT 0x0F000000 +#define RL_TXSTAT_CARR_HBEAT 0x10000000 +#define RL_TXSTAT_OUTOFWIN 0x20000000 +#define RL_TXSTAT_TXABRT 0x40000000 +#define RL_TXSTAT_CARRLOSS 0x80000000 + +/* + * Interrupt status register bits. + */ +#define RL_ISR_RX_OK 0x0001 +#define RL_ISR_RX_ERR 0x0002 +#define RL_ISR_TX_OK 0x0004 +#define RL_ISR_TX_ERR 0x0008 +#define RL_ISR_RX_OVERRUN 0x0010 +#define RL_ISR_PKT_UNDERRUN 0x0020 +#define RL_ISR_FIFO_OFLOW 0x0040 /* 8139 only */ +#define RL_ISR_PCS_TIMEOUT 0x4000 /* 8129 only */ +#define RL_ISR_SYSTEM_ERR 0x8000 + +#define RL_INTRS \ + (RL_ISR_TX_OK|RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_TX_ERR| \ + RL_ISR_RX_OVERRUN|RL_ISR_PKT_UNDERRUN|RL_ISR_FIFO_OFLOW| \ + RL_ISR_PCS_TIMEOUT|RL_ISR_SYSTEM_ERR) + +/* + * Media status register. (8139 only) + */ +#define RL_MEDIASTAT_RXPAUSE 0x01 +#define RL_MEDIASTAT_TXPAUSE 0x02 +#define RL_MEDIASTAT_LINK 0x04 +#define RL_MEDIASTAT_SPEED10 0x08 +#define RL_MEDIASTAT_RXFLOWCTL 0x40 /* duplex mode */ +#define RL_MEDIASTAT_TXFLOWCTL 0x80 /* duplex mode */ + +/* + * Receive config register. + */ +#define RL_RXCFG_RX_ALLPHYS 0x00000001 /* accept all nodes */ +#define RL_RXCFG_RX_INDIV 0x00000002 /* match filter */ +#define RL_RXCFG_RX_MULTI 0x00000004 /* accept all multicast */ +#define RL_RXCFG_RX_BROAD 0x00000008 /* accept all broadcast */ +#define RL_RXCFG_RX_RUNT 0x00000010 +#define RL_RXCFG_RX_ERRPKT 0x00000020 +#define RL_RXCFG_WRAP 0x00000080 +#define RL_RXCFG_MAXDMA 0x00000700 +#define RL_RXCFG_BUFSZ 0x00001800 +#define RL_RXCFG_FIFOTHRESH 0x0000E000 +#define RL_RXCFG_EARLYTHRESH 0x07000000 + +#define RL_RXDMA_16BYTES 0x00000000 +#define RL_RXDMA_32BYTES 0x00000100 +#define RL_RXDMA_64BYTES 0x00000200 +#define RL_RXDMA_128BYTES 0x00000300 +#define RL_RXDMA_256BYTES 0x00000400 +#define RL_RXDMA_512BYTES 0x00000500 +#define RL_RXDMA_1024BYTES 0x00000600 +#define RL_RXDMA_UNLIMITED 0x00000700 + +#define RL_RXBUF_8 0x00000000 +#define RL_RXBUF_16 0x00000800 +#define RL_RXBUF_32 0x00001000 +#define RL_RXBUF_64 0x00001800 + +#define RL_RXFIFO_16BYTES 0x00000000 +#define RL_RXFIFO_32BYTES 0x00002000 +#define RL_RXFIFO_64BYTES 0x00004000 +#define RL_RXFIFO_128BYTES 0x00006000 +#define RL_RXFIFO_256BYTES 0x00008000 +#define RL_RXFIFO_512BYTES 0x0000A000 +#define RL_RXFIFO_1024BYTES 0x0000C000 +#define RL_RXFIFO_NOTHRESH 0x0000E000 + +/* + * Bits in RX status header (included with RX'ed packet + * in ring buffer). + */ +#define RL_RXSTAT_RXOK 0x00000001 +#define RL_RXSTAT_ALIGNERR 0x00000002 +#define RL_RXSTAT_CRCERR 0x00000004 +#define RL_RXSTAT_GIANT 0x00000008 +#define RL_RXSTAT_RUNT 0x00000010 +#define RL_RXSTAT_BADSYM 0x00000020 +#define RL_RXSTAT_BROAD 0x00002000 +#define RL_RXSTAT_INDIV 0x00004000 +#define RL_RXSTAT_MULTI 0x00008000 +#define RL_RXSTAT_LENMASK 0xFFFF0000 + +#define RL_RXSTAT_UNFINISHED 0xFFF0 /* DMA still in progress */ +/* + * Command register. + */ +#define RL_CMD_EMPTY_RXBUF 0x0001 +#define RL_CMD_TX_ENB 0x0004 +#define RL_CMD_RX_ENB 0x0008 +#define RL_CMD_RESET 0x0010 + +/* + * EEPROM control register + */ +#define RL_EE_DATAOUT 0x01 /* Data out */ +#define RL_EE_DATAIN 0x02 /* Data in */ +#define RL_EE_CLK 0x04 /* clock */ +#define RL_EE_SEL 0x08 /* chip select */ +#define RL_EE_MODE (0x40|0x80) + +#define RL_EEMODE_OFF 0x00 +#define RL_EEMODE_AUTOLOAD 0x40 +#define RL_EEMODE_PROGRAM 0x80 +#define RL_EEMODE_WRITECFG (0x80|0x40) + +/* 9346 EEPROM commands */ +#define RL_EECMD_WRITE 0x140 +#define RL_EECMD_READ_6BIT 0x180 +#define RL_EECMD_READ_8BIT 0x600 +#define RL_EECMD_ERASE 0x1c0 + +#define RL_EE_ID 0x00 +#define RL_EE_PCI_VID 0x01 +#define RL_EE_PCI_DID 0x02 +/* Location of station address inside EEPROM */ +#define RL_EE_EADDR 0x07 + +/* + * MII register (8129 only) + */ +#define RL_MII_CLK 0x01 +#define RL_MII_DATAIN 0x02 +#define RL_MII_DATAOUT 0x04 +#define RL_MII_DIR 0x80 /* 0 == input, 1 == output */ + +/* + * Config 0 register + */ +#define RL_CFG0_ROM0 0x01 +#define RL_CFG0_ROM1 0x02 +#define RL_CFG0_ROM2 0x04 +#define RL_CFG0_PL0 0x08 +#define RL_CFG0_PL1 0x10 +#define RL_CFG0_10MBPS 0x20 /* 10 Mbps internal mode */ +#define RL_CFG0_PCS 0x40 +#define RL_CFG0_SCR 0x80 + +/* + * Config 1 register + */ +#define RL_CFG1_PWRDWN 0x01 +#define RL_CFG1_SLEEP 0x02 +#define RL_CFG1_IOMAP 0x04 +#define RL_CFG1_MEMMAP 0x08 +#define RL_CFG1_RSVD 0x10 +#define RL_CFG1_DRVLOAD 0x20 +#define RL_CFG1_LED0 0x40 +#define RL_CFG1_FULLDUPLEX 0x40 /* 8129 only */ +#define RL_CFG1_LED1 0x80 + +/* + * The RealTek doesn't use a fragment-based descriptor mechanism. + * Instead, there are only four register sets, each or which represents + * one 'descriptor.' Basically, each TX descriptor is just a contiguous + * packet buffer (32-bit aligned!) and we place the buffer addresses in + * the registers so the chip knows where they are. + * + * We can sort of kludge together the same kind of buffer management + * used in previous drivers, but we have to do buffer copies almost all + * the time, so it doesn't really buy us much. + * + * For reception, there's just one large buffer where the chip stores + * all received packets. + */ + +#define RL_RX_BUF_SZ RL_RXBUF_64 +#define RL_RXBUFLEN (1 << ((RL_RX_BUF_SZ >> 11) + 13)) +#define RL_TX_LIST_CNT 4 +#define RL_MIN_FRAMELEN 60 +#define RL_TXTHRESH(x) ((x) << 11) +#define RL_TX_THRESH_INIT 96 +#define RL_RX_FIFOTHRESH RL_RXFIFO_256BYTES +#define RL_RX_MAXDMA RL_RXDMA_1024BYTES /*RL_RXDMA_UNLIMITED*/ +#define RL_TX_MAXDMA RL_TXDMA_2048BYTES + +#define RL_RXCFG_CONFIG (RL_RX_FIFOTHRESH|RL_RX_MAXDMA|RL_RX_BUF_SZ) +#define RL_TXCFG_CONFIG (RL_TXCFG_IFG|RL_TX_MAXDMA) + +#define RL_ETHER_ALIGN 2 + +struct rl_chain_data { + u_int16_t cur_rx; + caddr_t rl_rx_buf; + caddr_t rl_rx_buf_ptr; + bus_dmamap_t rl_rx_dmamap; + + struct mbuf *rl_tx_chain[RL_TX_LIST_CNT]; + bus_dmamap_t rl_tx_dmamap[RL_TX_LIST_CNT]; + u_int8_t last_tx; + u_int8_t cur_tx; +}; + +#define RL_INC(x) (x = (x + 1) % RL_TX_LIST_CNT) +#define RL_CUR_TXADDR(x) ((x->rl_cdata.cur_tx * 4) + RL_TXADDR0) +#define RL_CUR_TXSTAT(x) ((x->rl_cdata.cur_tx * 4) + RL_TXSTAT0) +#define RL_CUR_TXMBUF(x) (x->rl_cdata.rl_tx_chain[x->rl_cdata.cur_tx]) +#define RL_CUR_DMAMAP(x) (x->rl_cdata.rl_tx_dmamap[x->rl_cdata.cur_tx]) +#define RL_LAST_TXADDR(x) ((x->rl_cdata.last_tx * 4) + RL_TXADDR0) +#define RL_LAST_TXSTAT(x) ((x->rl_cdata.last_tx * 4) + RL_TXSTAT0) +#define RL_LAST_TXMBUF(x) (x->rl_cdata.rl_tx_chain[x->rl_cdata.last_tx]) +#define RL_LAST_DMAMAP(x) (x->rl_cdata.rl_tx_dmamap[x->rl_cdata.last_tx]) + +struct rl_type { + u_int16_t rl_vid; + u_int16_t rl_did; + char *rl_name; +}; + +struct rl_mii_frame { + u_int8_t mii_stdelim; + u_int8_t mii_opcode; + u_int8_t mii_phyaddr; + u_int8_t mii_regaddr; + u_int8_t mii_turnaround; + u_int16_t mii_data; +}; + +/* + * MII constants + */ +#define RL_MII_STARTDELIM 0x01 +#define RL_MII_READOP 0x02 +#define RL_MII_WRITEOP 0x01 +#define RL_MII_TURNAROUND 0x02 + +#define RL_8129 1 +#define RL_8139 2 + +struct rl_softc { + struct arpcom arpcom; /* interface info */ + bus_space_handle_t rl_bhandle; /* bus space handle */ + bus_space_tag_t rl_btag; /* bus space tag */ + struct resource *rl_res; + struct resource *rl_irq; + void *rl_intrhand; + device_t rl_miibus; + bus_dma_tag_t rl_parent_tag; + bus_dma_tag_t rl_tag; + u_int8_t rl_unit; /* interface number */ + u_int8_t rl_type; + int rl_eecmd_read; + u_int8_t rl_stats_no_timeout; + int rl_txthresh; + struct rl_chain_data rl_cdata; + struct callout_handle rl_stat_ch; + struct mtx rl_mtx; + int suspended; /* 0 = normal 1 = suspended */ +#ifdef DEVICE_POLLING + int rxcycles; +#endif + + u_int32_t saved_maps[5]; /* pci data */ + u_int32_t saved_biosaddr; + u_int8_t saved_intline; + u_int8_t saved_cachelnsz; + u_int8_t saved_lattimer; +}; + +#define RL_LOCK(_sc) mtx_lock(&(_sc)->rl_mtx) +#define RL_UNLOCK(_sc) mtx_unlock(&(_sc)->rl_mtx) + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->rl_btag, sc->rl_bhandle, reg, val) +#define CSR_WRITE_2(sc, reg, val) \ + bus_space_write_2(sc->rl_btag, sc->rl_bhandle, reg, val) +#define CSR_WRITE_1(sc, reg, val) \ + bus_space_write_1(sc->rl_btag, sc->rl_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->rl_btag, sc->rl_bhandle, reg) +#define CSR_READ_2(sc, reg) \ + bus_space_read_2(sc->rl_btag, sc->rl_bhandle, reg) +#define CSR_READ_1(sc, reg) \ + bus_space_read_1(sc->rl_btag, sc->rl_bhandle, reg) + +#define RL_TIMEOUT 1000 + +/* + * General constants that are fun to know. + * + * RealTek PCI vendor ID + */ +#define RT_VENDORID 0x10EC + +/* + * RealTek chip device IDs. + */ +#define RT_DEVICEID_8129 0x8129 +#define RT_DEVICEID_8138 0x8138 +#define RT_DEVICEID_8139 0x8139 + +/* + * Accton PCI vendor ID + */ +#define ACCTON_VENDORID 0x1113 + +/* + * Accton MPX 5030/5038 device ID. + */ +#define ACCTON_DEVICEID_5030 0x1211 + +/* + * Nortel PCI vendor ID + */ +#define NORTEL_VENDORID 0x126C + +/* + * Delta Electronics Vendor ID. + */ +#define DELTA_VENDORID 0x1500 + +/* + * Delta device IDs. + */ +#define DELTA_DEVICEID_8139 0x1360 + +/* + * Addtron vendor ID. + */ +#define ADDTRON_VENDORID 0x4033 + +/* + * Addtron device IDs. + */ +#define ADDTRON_DEVICEID_8139 0x1360 + +/* + * D-Link vendor ID. + */ +#define DLINK_VENDORID 0x1186 + +/* + * D-Link DFE-530TX+ device ID + */ +#define DLINK_DEVICEID_530TXPLUS 0x1300 + +/* + * D-Link DFE-690TXD device ID + */ +#define DLINK_DEVICEID_690TXD 0x1340 + +/* + * PCI low memory base and low I/O base register, and + * other PCI registers. + */ + +#define RL_PCI_VENDOR_ID 0x00 +#define RL_PCI_DEVICE_ID 0x02 +#define RL_PCI_COMMAND 0x04 +#define RL_PCI_STATUS 0x06 +#define RL_PCI_CLASSCODE 0x09 +#define RL_PCI_LATENCY_TIMER 0x0D +#define RL_PCI_HEADER_TYPE 0x0E +#define RL_PCI_LOIO 0x10 +#define RL_PCI_LOMEM 0x14 +#define RL_PCI_BIOSROM 0x30 +#define RL_PCI_INTLINE 0x3C +#define RL_PCI_INTPIN 0x3D +#define RL_PCI_MINGNT 0x3E +#define RL_PCI_MINLAT 0x0F +#define RL_PCI_RESETOPT 0x48 +#define RL_PCI_EEPROM_DATA 0x4C + +#define RL_PCI_CAPID 0x50 /* 8 bits */ +#define RL_PCI_NEXTPTR 0x51 /* 8 bits */ +#define RL_PCI_PWRMGMTCAP 0x52 /* 16 bits */ +#define RL_PCI_PWRMGMTCTRL 0x54 /* 16 bits */ + +#define RL_PSTATE_MASK 0x0003 +#define RL_PSTATE_D0 0x0000 +#define RL_PSTATE_D1 0x0002 +#define RL_PSTATE_D2 0x0002 +#define RL_PSTATE_D3 0x0003 +#define RL_PME_EN 0x0010 +#define RL_PME_STATUS 0x8000 diff --git a/sys/pci/if_sf.c b/sys/pci/if_sf.c new file mode 100644 index 0000000..6322a3b --- /dev/null +++ b/sys/pci/if_sf.c @@ -0,0 +1,1537 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * Adaptec AIC-6915 "Starfire" PCI fast ethernet driver for FreeBSD. + * Programming manual is available from: + * ftp.adaptec.com:/pub/BBS/userguides/aic6915_pg.pdf. + * + * Written by Bill Paul <wpaul@ctr.columbia.edu> + * Department of Electical Engineering + * Columbia University, New York City + */ + +/* + * The Adaptec AIC-6915 "Starfire" is a 64-bit 10/100 PCI ethernet + * controller designed with flexibility and reducing CPU load in mind. + * The Starfire offers high and low priority buffer queues, a + * producer/consumer index mechanism and several different buffer + * queue and completion queue descriptor types. Any one of a number + * of different driver designs can be used, depending on system and + * OS requirements. This driver makes use of type0 transmit frame + * descriptors (since BSD fragments packets across an mbuf chain) + * and two RX buffer queues prioritized on size (one queue for small + * frames that will fit into a single mbuf, another with full size + * mbuf clusters for everything else). The producer/consumer indexes + * and completion queues are also used. + * + * One downside to the Starfire has to do with alignment: buffer + * queues must be aligned on 256-byte boundaries, and receive buffers + * must be aligned on longword boundaries. The receive buffer alignment + * causes problems on the Alpha platform, where the packet payload + * should be longword aligned. There is no simple way around this. + * + * For receive filtering, the Starfire offers 16 perfect filter slots + * and a 512-bit hash table. + * + * The Starfire has no internal transceiver, relying instead on an + * external MII-based transceiver. Accessing registers on external + * PHYs is done through a special register map rather than with the + * usual bitbang MDIO method. + * + * Acesssing the registers on the Starfire is a little tricky. The + * Starfire has a 512K internal register space. When programmed for + * PCI memory mapped mode, the entire register space can be accessed + * directly. However in I/O space mode, only 256 bytes are directly + * mapped into PCI I/O space. The other registers can be accessed + * indirectly using the SF_INDIRECTIO_ADDR and SF_INDIRECTIO_DATA + * registers inside the 256-byte I/O window. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#include <net/bpf.h> + +#include <vm/vm.h> /* for vtophys */ +#include <vm/pmap.h> /* for vtophys */ +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +/* "controller miibus0" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +#define SF_USEIOSPACE + +#include <pci/if_sfreg.h> + +MODULE_DEPEND(sf, miibus, 1, 1, 1); + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif + +static struct sf_type sf_devs[] = { + { AD_VENDORID, AD_DEVICEID_STARFIRE, + "Adaptec AIC-6915 10/100BaseTX" }, + { 0, 0, NULL } +}; + +static int sf_probe (device_t); +static int sf_attach (device_t); +static int sf_detach (device_t); +static void sf_intr (void *); +static void sf_stats_update (void *); +static void sf_rxeof (struct sf_softc *); +static void sf_txeof (struct sf_softc *); +static int sf_encap (struct sf_softc *, + struct sf_tx_bufdesc_type0 *, + struct mbuf *); +static void sf_start (struct ifnet *); +static int sf_ioctl (struct ifnet *, u_long, caddr_t); +static void sf_init (void *); +static void sf_stop (struct sf_softc *); +static void sf_watchdog (struct ifnet *); +static void sf_shutdown (device_t); +static int sf_ifmedia_upd (struct ifnet *); +static void sf_ifmedia_sts (struct ifnet *, struct ifmediareq *); +static void sf_reset (struct sf_softc *); +static int sf_init_rx_ring (struct sf_softc *); +static void sf_init_tx_ring (struct sf_softc *); +static int sf_newbuf (struct sf_softc *, + struct sf_rx_bufdesc_type0 *, + struct mbuf *); +static void sf_setmulti (struct sf_softc *); +static int sf_setperf (struct sf_softc *, int, caddr_t); +static int sf_sethash (struct sf_softc *, caddr_t, int); +#ifdef notdef +static int sf_setvlan (struct sf_softc *, int, u_int32_t); +#endif + +static u_int8_t sf_read_eeprom (struct sf_softc *, int); +static u_int32_t sf_calchash (caddr_t); + +static int sf_miibus_readreg (device_t, int, int); +static int sf_miibus_writereg (device_t, int, int, int); +static void sf_miibus_statchg (device_t); + +static u_int32_t csr_read_4 (struct sf_softc *, int); +static void csr_write_4 (struct sf_softc *, int, u_int32_t); +static void sf_txthresh_adjust (struct sf_softc *); + +#ifdef SF_USEIOSPACE +#define SF_RES SYS_RES_IOPORT +#define SF_RID SF_PCI_LOIO +#else +#define SF_RES SYS_RES_MEMORY +#define SF_RID SF_PCI_LOMEM +#endif + +static device_method_t sf_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sf_probe), + DEVMETHOD(device_attach, sf_attach), + DEVMETHOD(device_detach, sf_detach), + DEVMETHOD(device_shutdown, sf_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, sf_miibus_readreg), + DEVMETHOD(miibus_writereg, sf_miibus_writereg), + DEVMETHOD(miibus_statchg, sf_miibus_statchg), + + { 0, 0 } +}; + +static driver_t sf_driver = { + "sf", + sf_methods, + sizeof(struct sf_softc), +}; + +static devclass_t sf_devclass; + +DRIVER_MODULE(if_sf, pci, sf_driver, sf_devclass, 0, 0); +DRIVER_MODULE(miibus, sf, miibus_driver, miibus_devclass, 0, 0); + +#define SF_SETBIT(sc, reg, x) \ + csr_write_4(sc, reg, csr_read_4(sc, reg) | x) + +#define SF_CLRBIT(sc, reg, x) \ + csr_write_4(sc, reg, csr_read_4(sc, reg) & ~x) + +static u_int32_t csr_read_4(sc, reg) + struct sf_softc *sc; + int reg; +{ + u_int32_t val; + +#ifdef SF_USEIOSPACE + CSR_WRITE_4(sc, SF_INDIRECTIO_ADDR, reg + SF_RMAP_INTREG_BASE); + val = CSR_READ_4(sc, SF_INDIRECTIO_DATA); +#else + val = CSR_READ_4(sc, (reg + SF_RMAP_INTREG_BASE)); +#endif + + return(val); +} + +static u_int8_t sf_read_eeprom(sc, reg) + struct sf_softc *sc; + int reg; +{ + u_int8_t val; + + val = (csr_read_4(sc, SF_EEADDR_BASE + + (reg & 0xFFFFFFFC)) >> (8 * (reg & 3))) & 0xFF; + + return(val); +} + +static void csr_write_4(sc, reg, val) + struct sf_softc *sc; + int reg; + u_int32_t val; +{ +#ifdef SF_USEIOSPACE + CSR_WRITE_4(sc, SF_INDIRECTIO_ADDR, reg + SF_RMAP_INTREG_BASE); + CSR_WRITE_4(sc, SF_INDIRECTIO_DATA, val); +#else + CSR_WRITE_4(sc, (reg + SF_RMAP_INTREG_BASE), val); +#endif + return; +} + +static u_int32_t sf_calchash(addr) + caddr_t addr; +{ + u_int32_t crc, carry; + int i, j; + u_int8_t c; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (i = 0; i < 6; i++) { + c = *(addr + i); + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); + crc <<= 1; + c >>= 1; + if (carry) + crc = (crc ^ 0x04c11db6) | carry; + } + } + + /* return the filter bit position */ + return(crc >> 23 & 0x1FF); +} + +/* + * Copy the address 'mac' into the perfect RX filter entry at + * offset 'idx.' The perfect filter only has 16 entries so do + * some sanity tests. + */ +static int sf_setperf(sc, idx, mac) + struct sf_softc *sc; + int idx; + caddr_t mac; +{ + u_int16_t *p; + + if (idx < 0 || idx > SF_RXFILT_PERFECT_CNT) + return(EINVAL); + + if (mac == NULL) + return(EINVAL); + + p = (u_int16_t *)mac; + + csr_write_4(sc, SF_RXFILT_PERFECT_BASE + + (idx * SF_RXFILT_PERFECT_SKIP), htons(p[2])); + csr_write_4(sc, SF_RXFILT_PERFECT_BASE + + (idx * SF_RXFILT_PERFECT_SKIP) + 4, htons(p[1])); + csr_write_4(sc, SF_RXFILT_PERFECT_BASE + + (idx * SF_RXFILT_PERFECT_SKIP) + 8, htons(p[0])); + + return(0); +} + +/* + * Set the bit in the 512-bit hash table that corresponds to the + * specified mac address 'mac.' If 'prio' is nonzero, update the + * priority hash table instead of the filter hash table. + */ +static int sf_sethash(sc, mac, prio) + struct sf_softc *sc; + caddr_t mac; + int prio; +{ + u_int32_t h = 0; + + if (mac == NULL) + return(EINVAL); + + h = sf_calchash(mac); + + if (prio) { + SF_SETBIT(sc, SF_RXFILT_HASH_BASE + SF_RXFILT_HASH_PRIOOFF + + (SF_RXFILT_HASH_SKIP * (h >> 4)), (1 << (h & 0xF))); + } else { + SF_SETBIT(sc, SF_RXFILT_HASH_BASE + SF_RXFILT_HASH_ADDROFF + + (SF_RXFILT_HASH_SKIP * (h >> 4)), (1 << (h & 0xF))); + } + + return(0); +} + +#ifdef notdef +/* + * Set a VLAN tag in the receive filter. + */ +static int sf_setvlan(sc, idx, vlan) + struct sf_softc *sc; + int idx; + u_int32_t vlan; +{ + if (idx < 0 || idx >> SF_RXFILT_HASH_CNT) + return(EINVAL); + + csr_write_4(sc, SF_RXFILT_HASH_BASE + + (idx * SF_RXFILT_HASH_SKIP) + SF_RXFILT_HASH_VLANOFF, vlan); + + return(0); +} +#endif + +static int sf_miibus_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct sf_softc *sc; + int i; + u_int32_t val = 0; + + sc = device_get_softc(dev); + + for (i = 0; i < SF_TIMEOUT; i++) { + val = csr_read_4(sc, SF_PHY_REG(phy, reg)); + if (val & SF_MII_DATAVALID) + break; + } + + if (i == SF_TIMEOUT) + return(0); + + if ((val & 0x0000FFFF) == 0xFFFF) + return(0); + + return(val & 0x0000FFFF); +} + +static int sf_miibus_writereg(dev, phy, reg, val) + device_t dev; + int phy, reg, val; +{ + struct sf_softc *sc; + int i; + int busy; + + sc = device_get_softc(dev); + + csr_write_4(sc, SF_PHY_REG(phy, reg), val); + + for (i = 0; i < SF_TIMEOUT; i++) { + busy = csr_read_4(sc, SF_PHY_REG(phy, reg)); + if (!(busy & SF_MII_BUSY)) + break; + } + + return(0); +} + +static void sf_miibus_statchg(dev) + device_t dev; +{ + struct sf_softc *sc; + struct mii_data *mii; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->sf_miibus); + + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { + SF_SETBIT(sc, SF_MACCFG_1, SF_MACCFG1_FULLDUPLEX); + csr_write_4(sc, SF_BKTOBKIPG, SF_IPGT_FDX); + } else { + SF_CLRBIT(sc, SF_MACCFG_1, SF_MACCFG1_FULLDUPLEX); + csr_write_4(sc, SF_BKTOBKIPG, SF_IPGT_HDX); + } + + return; +} + +static void sf_setmulti(sc) + struct sf_softc *sc; +{ + struct ifnet *ifp; + int i; + struct ifmultiaddr *ifma; + u_int8_t dummy[] = { 0, 0, 0, 0, 0, 0 }; + + ifp = &sc->arpcom.ac_if; + + /* First zot all the existing filters. */ + for (i = 1; i < SF_RXFILT_PERFECT_CNT; i++) + sf_setperf(sc, i, (char *)&dummy); + for (i = SF_RXFILT_HASH_BASE; + i < (SF_RXFILT_HASH_MAX + 1); i += 4) + csr_write_4(sc, i, 0); + SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_ALLMULTI); + + /* Now program new ones. */ + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_ALLMULTI); + } else { + i = 1; + TAILQ_FOREACH_REVERSE(ifma, &ifp->if_multiaddrs, ifmultihead, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + /* + * Program the first 15 multicast groups + * into the perfect filter. For all others, + * use the hash table. + */ + if (i < SF_RXFILT_PERFECT_CNT) { + sf_setperf(sc, i, + LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + i++; + continue; + } + + sf_sethash(sc, + LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 0); + } + } + + return; +} + +/* + * Set media options. + */ +static int sf_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct sf_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = device_get_softc(sc->sf_miibus); + sc->sf_link = 0; + if (mii->mii_instance) { + struct mii_softc *miisc; + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + mii_phy_reset(miisc); + } + mii_mediachg(mii); + + return(0); +} + +/* + * Report current media status. + */ +static void sf_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct sf_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = device_get_softc(sc->sf_miibus); + + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + + return; +} + +static int sf_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct sf_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + struct mii_data *mii; + int error = 0; + + SF_LOCK(sc); + + switch(command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING && + ifp->if_flags & IFF_PROMISC && + !(sc->sf_if_flags & IFF_PROMISC)) { + SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC); + } else if (ifp->if_flags & IFF_RUNNING && + !(ifp->if_flags & IFF_PROMISC) && + sc->sf_if_flags & IFF_PROMISC) { + SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC); + } else if (!(ifp->if_flags & IFF_RUNNING)) + sf_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) + sf_stop(sc); + } + sc->sf_if_flags = ifp->if_flags; + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + sf_setmulti(sc); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = device_get_softc(sc->sf_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + break; + default: + error = EINVAL; + break; + } + + SF_UNLOCK(sc); + + return(error); +} + +static void sf_reset(sc) + struct sf_softc *sc; +{ + register int i; + + csr_write_4(sc, SF_GEN_ETH_CTL, 0); + SF_SETBIT(sc, SF_MACCFG_1, SF_MACCFG1_SOFTRESET); + DELAY(1000); + SF_CLRBIT(sc, SF_MACCFG_1, SF_MACCFG1_SOFTRESET); + + SF_SETBIT(sc, SF_PCI_DEVCFG, SF_PCIDEVCFG_RESET); + + for (i = 0; i < SF_TIMEOUT; i++) { + DELAY(10); + if (!(csr_read_4(sc, SF_PCI_DEVCFG) & SF_PCIDEVCFG_RESET)) + break; + } + + if (i == SF_TIMEOUT) + printf("sf%d: reset never completed!\n", sc->sf_unit); + + /* Wait a little while for the chip to get its brains in order. */ + DELAY(1000); + return; +} + +/* + * Probe for an Adaptec AIC-6915 chip. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + * We also check the subsystem ID so that we can identify exactly which + * NIC has been found, if possible. + */ +static int sf_probe(dev) + device_t dev; +{ + struct sf_type *t; + + t = sf_devs; + + while(t->sf_name != NULL) { + if ((pci_get_vendor(dev) == t->sf_vid) && + (pci_get_device(dev) == t->sf_did)) { + switch((pci_read_config(dev, + SF_PCI_SUBVEN_ID, 4) >> 16) & 0xFFFF) { + case AD_SUBSYSID_62011_REV0: + case AD_SUBSYSID_62011_REV1: + device_set_desc(dev, + "Adaptec ANA-62011 10/100BaseTX"); + return(0); + break; + case AD_SUBSYSID_62022: + device_set_desc(dev, + "Adaptec ANA-62022 10/100BaseTX"); + return(0); + break; + case AD_SUBSYSID_62044_REV0: + case AD_SUBSYSID_62044_REV1: + device_set_desc(dev, + "Adaptec ANA-62044 10/100BaseTX"); + return(0); + break; + case AD_SUBSYSID_62020: + device_set_desc(dev, + "Adaptec ANA-62020 10/100BaseFX"); + return(0); + break; + case AD_SUBSYSID_69011: + device_set_desc(dev, + "Adaptec ANA-69011 10/100BaseTX"); + return(0); + break; + default: + device_set_desc(dev, t->sf_name); + return(0); + break; + } + } + t++; + } + + return(ENXIO); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int sf_attach(dev) + device_t dev; +{ + int i; + u_int32_t command; + struct sf_softc *sc; + struct ifnet *ifp; + int unit, rid, error = 0; + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + bzero(sc, sizeof(struct sf_softc)); + + mtx_init(&sc->sf_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF | MTX_RECURSE); + SF_LOCK(sc); + /* + * Handle power management nonsense. + */ + if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { + u_int32_t iobase, membase, irq; + + /* Save important PCI config data. */ + iobase = pci_read_config(dev, SF_PCI_LOIO, 4); + membase = pci_read_config(dev, SF_PCI_LOMEM, 4); + irq = pci_read_config(dev, SF_PCI_INTLINE, 4); + + /* Reset the power state. */ + printf("sf%d: chip is in D%d power mode " + "-- setting to D0\n", unit, + pci_get_powerstate(dev)); + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + + /* Restore PCI config data. */ + pci_write_config(dev, SF_PCI_LOIO, iobase, 4); + pci_write_config(dev, SF_PCI_LOMEM, membase, 4); + pci_write_config(dev, SF_PCI_INTLINE, irq, 4); + } + + /* + * Map control/status registers. + */ + pci_enable_busmaster(dev); + pci_enable_io(dev, SYS_RES_IOPORT); + pci_enable_io(dev, SYS_RES_MEMORY); + command = pci_read_config(dev, PCIR_COMMAND, 4); + +#ifdef SF_USEIOSPACE + if (!(command & PCIM_CMD_PORTEN)) { + printf("sf%d: failed to enable I/O ports!\n", unit); + error = ENXIO; + goto fail; + } +#else + if (!(command & PCIM_CMD_MEMEN)) { + printf("sf%d: failed to enable memory mapping!\n", unit); + error = ENXIO; + goto fail; + } +#endif + + rid = SF_RID; + sc->sf_res = bus_alloc_resource(dev, SF_RES, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (sc->sf_res == NULL) { + printf ("sf%d: couldn't map ports\n", unit); + error = ENXIO; + goto fail; + } + + sc->sf_btag = rman_get_bustag(sc->sf_res); + sc->sf_bhandle = rman_get_bushandle(sc->sf_res); + + /* Allocate interrupt */ + rid = 0; + sc->sf_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->sf_irq == NULL) { + printf("sf%d: couldn't map interrupt\n", unit); + bus_release_resource(dev, SF_RES, SF_RID, sc->sf_res); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->sf_irq, INTR_TYPE_NET, + sf_intr, sc, &sc->sf_intrhand); + + if (error) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sf_res); + bus_release_resource(dev, SF_RES, SF_RID, sc->sf_res); + printf("sf%d: couldn't set up irq\n", unit); + goto fail; + } + + callout_handle_init(&sc->sf_stat_ch); + /* Reset the adapter. */ + sf_reset(sc); + + /* + * Get station address from the EEPROM. + */ + for (i = 0; i < ETHER_ADDR_LEN; i++) + sc->arpcom.ac_enaddr[i] = + sf_read_eeprom(sc, SF_EE_NODEADDR + ETHER_ADDR_LEN - i); + + /* + * An Adaptec chip was detected. Inform the world. + */ + printf("sf%d: Ethernet address: %6D\n", unit, + sc->arpcom.ac_enaddr, ":"); + + sc->sf_unit = unit; + + /* Allocate the descriptor queues. */ + sc->sf_ldata = contigmalloc(sizeof(struct sf_list_data), M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc->sf_ldata == NULL) { + printf("sf%d: no memory for list buffers!\n", unit); + bus_teardown_intr(dev, sc->sf_irq, sc->sf_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sf_irq); + bus_release_resource(dev, SF_RES, SF_RID, sc->sf_res); + error = ENXIO; + goto fail; + } + + bzero(sc->sf_ldata, sizeof(struct sf_list_data)); + + /* Do MII setup. */ + if (mii_phy_probe(dev, &sc->sf_miibus, + sf_ifmedia_upd, sf_ifmedia_sts)) { + printf("sf%d: MII without any phy!\n", sc->sf_unit); + contigfree(sc->sf_ldata,sizeof(struct sf_list_data),M_DEVBUF); + bus_teardown_intr(dev, sc->sf_irq, sc->sf_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sf_irq); + bus_release_resource(dev, SF_RES, SF_RID, sc->sf_res); + error = ENXIO; + goto fail; + } + + ifp = &sc->arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = "sf"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = sf_ioctl; + ifp->if_output = ether_output; + ifp->if_start = sf_start; + ifp->if_watchdog = sf_watchdog; + ifp->if_init = sf_init; + ifp->if_baudrate = 10000000; + ifp->if_snd.ifq_maxlen = SF_TX_DLIST_CNT - 1; + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); + SF_UNLOCK(sc); + return(0); + +fail: + SF_UNLOCK(sc); + mtx_destroy(&sc->sf_mtx); + return(error); +} + +static int sf_detach(dev) + device_t dev; +{ + struct sf_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + SF_LOCK(sc); + ifp = &sc->arpcom.ac_if; + + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + sf_stop(sc); + + bus_generic_detach(dev); + device_delete_child(dev, sc->sf_miibus); + + bus_teardown_intr(dev, sc->sf_irq, sc->sf_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sf_irq); + bus_release_resource(dev, SF_RES, SF_RID, sc->sf_res); + + contigfree(sc->sf_ldata, sizeof(struct sf_list_data), M_DEVBUF); + + SF_UNLOCK(sc); + mtx_destroy(&sc->sf_mtx); + + return(0); +} + +static int sf_init_rx_ring(sc) + struct sf_softc *sc; +{ + struct sf_list_data *ld; + int i; + + ld = sc->sf_ldata; + + bzero((char *)ld->sf_rx_dlist_big, + sizeof(struct sf_rx_bufdesc_type0) * SF_RX_DLIST_CNT); + bzero((char *)ld->sf_rx_clist, + sizeof(struct sf_rx_cmpdesc_type3) * SF_RX_CLIST_CNT); + + for (i = 0; i < SF_RX_DLIST_CNT; i++) { + if (sf_newbuf(sc, &ld->sf_rx_dlist_big[i], NULL) == ENOBUFS) + return(ENOBUFS); + } + + return(0); +} + +static void sf_init_tx_ring(sc) + struct sf_softc *sc; +{ + struct sf_list_data *ld; + int i; + + ld = sc->sf_ldata; + + bzero((char *)ld->sf_tx_dlist, + sizeof(struct sf_tx_bufdesc_type0) * SF_TX_DLIST_CNT); + bzero((char *)ld->sf_tx_clist, + sizeof(struct sf_tx_cmpdesc_type0) * SF_TX_CLIST_CNT); + + for (i = 0; i < SF_TX_DLIST_CNT; i++) + ld->sf_tx_dlist[i].sf_id = SF_TX_BUFDESC_ID; + for (i = 0; i < SF_TX_CLIST_CNT; i++) + ld->sf_tx_clist[i].sf_type = SF_TXCMPTYPE_TX; + + ld->sf_tx_dlist[SF_TX_DLIST_CNT - 1].sf_end = 1; + sc->sf_tx_cnt = 0; + + return; +} + +static int sf_newbuf(sc, c, m) + struct sf_softc *sc; + struct sf_rx_bufdesc_type0 *c; + struct mbuf *m; +{ + struct mbuf *m_new = NULL; + + if (m == NULL) { + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(ENOBUFS); + + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + return(ENOBUFS); + } + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + } else { + m_new = m; + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + m_new->m_data = m_new->m_ext.ext_buf; + } + + m_adj(m_new, sizeof(u_int64_t)); + + c->sf_mbuf = m_new; + c->sf_addrlo = SF_RX_HOSTADDR(vtophys(mtod(m_new, caddr_t))); + c->sf_valid = 1; + + return(0); +} + +/* + * The starfire is programmed to use 'normal' mode for packet reception, + * which means we use the consumer/producer model for both the buffer + * descriptor queue and the completion descriptor queue. The only problem + * with this is that it involves a lot of register accesses: we have to + * read the RX completion consumer and producer indexes and the RX buffer + * producer index, plus the RX completion consumer and RX buffer producer + * indexes have to be updated. It would have been easier if Adaptec had + * put each index in a separate register, especially given that the damn + * NIC has a 512K register space. + * + * In spite of all the lovely features that Adaptec crammed into the 6915, + * it is marred by one truly stupid design flaw, which is that receive + * buffer addresses must be aligned on a longword boundary. This forces + * the packet payload to be unaligned, which is suboptimal on the x86 and + * completely unuseable on the Alpha. Our only recourse is to copy received + * packets into properly aligned buffers before handing them off. + */ + +static void sf_rxeof(sc) + struct sf_softc *sc; +{ + struct ether_header *eh; + struct mbuf *m; + struct ifnet *ifp; + struct sf_rx_bufdesc_type0 *desc; + struct sf_rx_cmpdesc_type3 *cur_rx; + u_int32_t rxcons, rxprod; + int cmpprodidx, cmpconsidx, bufprodidx; + + ifp = &sc->arpcom.ac_if; + + rxcons = csr_read_4(sc, SF_CQ_CONSIDX); + rxprod = csr_read_4(sc, SF_RXDQ_PTR_Q1); + cmpprodidx = SF_IDX_LO(csr_read_4(sc, SF_CQ_PRODIDX)); + cmpconsidx = SF_IDX_LO(rxcons); + bufprodidx = SF_IDX_LO(rxprod); + + while (cmpconsidx != cmpprodidx) { + struct mbuf *m0; + + cur_rx = &sc->sf_ldata->sf_rx_clist[cmpconsidx]; + desc = &sc->sf_ldata->sf_rx_dlist_big[cur_rx->sf_endidx]; + m = desc->sf_mbuf; + SF_INC(cmpconsidx, SF_RX_CLIST_CNT); + SF_INC(bufprodidx, SF_RX_DLIST_CNT); + + if (!(cur_rx->sf_status1 & SF_RXSTAT1_OK)) { + ifp->if_ierrors++; + sf_newbuf(sc, desc, m); + continue; + } + + m0 = m_devget(mtod(m, char *), cur_rx->sf_len, ETHER_ALIGN, + ifp, NULL); + sf_newbuf(sc, desc, m); + if (m0 == NULL) { + ifp->if_ierrors++; + continue; + } + m = m0; + + eh = mtod(m, struct ether_header *); + ifp->if_ipackets++; + + /* Remove header from mbuf and pass it on. */ + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, eh, m); + } + + csr_write_4(sc, SF_CQ_CONSIDX, + (rxcons & ~SF_CQ_CONSIDX_RXQ1) | cmpconsidx); + csr_write_4(sc, SF_RXDQ_PTR_Q1, + (rxprod & ~SF_RXDQ_PRODIDX) | bufprodidx); + + return; +} + +/* + * Read the transmit status from the completion queue and release + * mbufs. Note that the buffer descriptor index in the completion + * descriptor is an offset from the start of the transmit buffer + * descriptor list in bytes. This is important because the manual + * gives the impression that it should match the producer/consumer + * index, which is the offset in 8 byte blocks. + */ +static void sf_txeof(sc) + struct sf_softc *sc; +{ + int txcons, cmpprodidx, cmpconsidx; + struct sf_tx_cmpdesc_type1 *cur_cmp; + struct sf_tx_bufdesc_type0 *cur_tx; + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + + txcons = csr_read_4(sc, SF_CQ_CONSIDX); + cmpprodidx = SF_IDX_HI(csr_read_4(sc, SF_CQ_PRODIDX)); + cmpconsidx = SF_IDX_HI(txcons); + + while (cmpconsidx != cmpprodidx) { + cur_cmp = &sc->sf_ldata->sf_tx_clist[cmpconsidx]; + cur_tx = &sc->sf_ldata->sf_tx_dlist[cur_cmp->sf_index >> 7]; + + if (cur_cmp->sf_txstat & SF_TXSTAT_TX_OK) + ifp->if_opackets++; + else { + if (cur_cmp->sf_txstat & SF_TXSTAT_TX_UNDERRUN) + sf_txthresh_adjust(sc); + ifp->if_oerrors++; + } + + sc->sf_tx_cnt--; + if (cur_tx->sf_mbuf != NULL) { + m_freem(cur_tx->sf_mbuf); + cur_tx->sf_mbuf = NULL; + } else + break; + SF_INC(cmpconsidx, SF_TX_CLIST_CNT); + } + + ifp->if_timer = 0; + ifp->if_flags &= ~IFF_OACTIVE; + + csr_write_4(sc, SF_CQ_CONSIDX, + (txcons & ~SF_CQ_CONSIDX_TXQ) | + ((cmpconsidx << 16) & 0xFFFF0000)); + + return; +} + +static void sf_txthresh_adjust(sc) + struct sf_softc *sc; +{ + u_int32_t txfctl; + u_int8_t txthresh; + + txfctl = csr_read_4(sc, SF_TX_FRAMCTL); + txthresh = txfctl & SF_TXFRMCTL_TXTHRESH; + if (txthresh < 0xFF) { + txthresh++; + txfctl &= ~SF_TXFRMCTL_TXTHRESH; + txfctl |= txthresh; +#ifdef DIAGNOSTIC + printf("sf%d: tx underrun, increasing " + "tx threshold to %d bytes\n", + sc->sf_unit, txthresh * 4); +#endif + csr_write_4(sc, SF_TX_FRAMCTL, txfctl); + } + + return; +} + +static void sf_intr(arg) + void *arg; +{ + struct sf_softc *sc; + struct ifnet *ifp; + u_int32_t status; + + sc = arg; + SF_LOCK(sc); + + ifp = &sc->arpcom.ac_if; + + if (!(csr_read_4(sc, SF_ISR_SHADOW) & SF_ISR_PCIINT_ASSERTED)) { + SF_UNLOCK(sc); + return; + } + + /* Disable interrupts. */ + csr_write_4(sc, SF_IMR, 0x00000000); + + for (;;) { + status = csr_read_4(sc, SF_ISR); + if (status) + csr_write_4(sc, SF_ISR, status); + + if (!(status & SF_INTRS)) + break; + + if (status & SF_ISR_RXDQ1_DMADONE) + sf_rxeof(sc); + + if (status & SF_ISR_TX_TXDONE || + status & SF_ISR_TX_DMADONE || + status & SF_ISR_TX_QUEUEDONE) + sf_txeof(sc); + + if (status & SF_ISR_TX_LOFIFO) + sf_txthresh_adjust(sc); + + if (status & SF_ISR_ABNORMALINTR) { + if (status & SF_ISR_STATSOFLOW) { + untimeout(sf_stats_update, sc, + sc->sf_stat_ch); + sf_stats_update(sc); + } else + sf_init(sc); + } + } + + /* Re-enable interrupts. */ + csr_write_4(sc, SF_IMR, SF_INTRS); + + if (ifp->if_snd.ifq_head != NULL) + sf_start(ifp); + + SF_UNLOCK(sc); + return; +} + +static void sf_init(xsc) + void *xsc; +{ + struct sf_softc *sc; + struct ifnet *ifp; + struct mii_data *mii; + int i; + + sc = xsc; + SF_LOCK(sc); + ifp = &sc->arpcom.ac_if; + mii = device_get_softc(sc->sf_miibus); + + sf_stop(sc); + sf_reset(sc); + + /* Init all the receive filter registers */ + for (i = SF_RXFILT_PERFECT_BASE; + i < (SF_RXFILT_HASH_MAX + 1); i += 4) + csr_write_4(sc, i, 0); + + /* Empty stats counter registers. */ + for (i = 0; i < sizeof(struct sf_stats)/sizeof(u_int32_t); i++) + csr_write_4(sc, SF_STATS_BASE + + (i + sizeof(u_int32_t)), 0); + + /* Init our MAC address */ + csr_write_4(sc, SF_PAR0, *(u_int32_t *)(&sc->arpcom.ac_enaddr[0])); + csr_write_4(sc, SF_PAR1, *(u_int32_t *)(&sc->arpcom.ac_enaddr[4])); + sf_setperf(sc, 0, (caddr_t)&sc->arpcom.ac_enaddr); + + if (sf_init_rx_ring(sc) == ENOBUFS) { + printf("sf%d: initialization failed: no " + "memory for rx buffers\n", sc->sf_unit); + SF_UNLOCK(sc); + return; + } + + sf_init_tx_ring(sc); + + csr_write_4(sc, SF_RXFILT, SF_PERFMODE_NORMAL|SF_HASHMODE_WITHVLAN); + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) { + SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC); + } else { + SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC); + } + + if (ifp->if_flags & IFF_BROADCAST) { + SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_BROAD); + } else { + SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_BROAD); + } + + /* + * Load the multicast filter. + */ + sf_setmulti(sc); + + /* Init the completion queue indexes */ + csr_write_4(sc, SF_CQ_CONSIDX, 0); + csr_write_4(sc, SF_CQ_PRODIDX, 0); + + /* Init the RX completion queue */ + csr_write_4(sc, SF_RXCQ_CTL_1, + vtophys(sc->sf_ldata->sf_rx_clist) & SF_RXCQ_ADDR); + SF_SETBIT(sc, SF_RXCQ_CTL_1, SF_RXCQTYPE_3); + + /* Init RX DMA control. */ + SF_SETBIT(sc, SF_RXDMA_CTL, SF_RXDMA_REPORTBADPKTS); + + /* Init the RX buffer descriptor queue. */ + csr_write_4(sc, SF_RXDQ_ADDR_Q1, + vtophys(sc->sf_ldata->sf_rx_dlist_big)); + csr_write_4(sc, SF_RXDQ_CTL_1, (MCLBYTES << 16) | SF_DESCSPACE_16BYTES); + csr_write_4(sc, SF_RXDQ_PTR_Q1, SF_RX_DLIST_CNT - 1); + + /* Init the TX completion queue */ + csr_write_4(sc, SF_TXCQ_CTL, + vtophys(sc->sf_ldata->sf_tx_clist) & SF_RXCQ_ADDR); + + /* Init the TX buffer descriptor queue. */ + csr_write_4(sc, SF_TXDQ_ADDR_HIPRIO, + vtophys(sc->sf_ldata->sf_tx_dlist)); + SF_SETBIT(sc, SF_TX_FRAMCTL, SF_TXFRMCTL_CPLAFTERTX); + csr_write_4(sc, SF_TXDQ_CTL, + SF_TXBUFDESC_TYPE0|SF_TXMINSPACE_128BYTES|SF_TXSKIPLEN_8BYTES); + SF_SETBIT(sc, SF_TXDQ_CTL, SF_TXDQCTL_NODMACMP); + + /* Enable autopadding of short TX frames. */ + SF_SETBIT(sc, SF_MACCFG_1, SF_MACCFG1_AUTOPAD); + + /* Enable interrupts. */ + csr_write_4(sc, SF_IMR, SF_INTRS); + SF_SETBIT(sc, SF_PCI_DEVCFG, SF_PCIDEVCFG_INTR_ENB); + + /* Enable the RX and TX engines. */ + SF_SETBIT(sc, SF_GEN_ETH_CTL, SF_ETHCTL_RX_ENB|SF_ETHCTL_RXDMA_ENB); + SF_SETBIT(sc, SF_GEN_ETH_CTL, SF_ETHCTL_TX_ENB|SF_ETHCTL_TXDMA_ENB); + + /*mii_mediachg(mii);*/ + sf_ifmedia_upd(ifp); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + sc->sf_stat_ch = timeout(sf_stats_update, sc, hz); + + SF_UNLOCK(sc); + + return; +} + +static int sf_encap(sc, c, m_head) + struct sf_softc *sc; + struct sf_tx_bufdesc_type0 *c; + struct mbuf *m_head; +{ + int frag = 0; + struct sf_frag *f = NULL; + struct mbuf *m; + + m = m_head; + + for (m = m_head, frag = 0; m != NULL; m = m->m_next) { + if (m->m_len != 0) { + if (frag == SF_MAXFRAGS) + break; + f = &c->sf_frags[frag]; + if (frag == 0) + f->sf_pktlen = m_head->m_pkthdr.len; + f->sf_fraglen = m->m_len; + f->sf_addr = vtophys(mtod(m, vm_offset_t)); + frag++; + } + } + + if (m != NULL) { + struct mbuf *m_new = NULL; + + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) { + printf("sf%d: no memory for tx list", sc->sf_unit); + return(1); + } + + if (m_head->m_pkthdr.len > MHLEN) { + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + printf("sf%d: no memory for tx list", + sc->sf_unit); + return(1); + } + } + m_copydata(m_head, 0, m_head->m_pkthdr.len, + mtod(m_new, caddr_t)); + m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; + m_freem(m_head); + m_head = m_new; + f = &c->sf_frags[0]; + f->sf_fraglen = f->sf_pktlen = m_head->m_pkthdr.len; + f->sf_addr = vtophys(mtod(m_head, caddr_t)); + frag = 1; + } + + c->sf_mbuf = m_head; + c->sf_id = SF_TX_BUFDESC_ID; + c->sf_fragcnt = frag; + c->sf_intr = 1; + c->sf_caltcp = 0; + c->sf_crcen = 1; + + return(0); +} + +static void sf_start(ifp) + struct ifnet *ifp; +{ + struct sf_softc *sc; + struct sf_tx_bufdesc_type0 *cur_tx = NULL; + struct mbuf *m_head = NULL; + int i, txprod; + + sc = ifp->if_softc; + SF_LOCK(sc); + + if (!sc->sf_link && ifp->if_snd.ifq_len < 10) { + SF_UNLOCK(sc); + return; + } + + if (ifp->if_flags & IFF_OACTIVE) { + SF_UNLOCK(sc); + return; + } + + txprod = csr_read_4(sc, SF_TXDQ_PRODIDX); + i = SF_IDX_HI(txprod) >> 4; + + if (sc->sf_ldata->sf_tx_dlist[i].sf_mbuf != NULL) { + printf("sf%d: TX ring full, resetting\n", sc->sf_unit); + sf_init(sc); + txprod = csr_read_4(sc, SF_TXDQ_PRODIDX); + i = SF_IDX_HI(txprod) >> 4; + } + + while(sc->sf_ldata->sf_tx_dlist[i].sf_mbuf == NULL) { + if (sc->sf_tx_cnt >= (SF_TX_DLIST_CNT - 5)) { + ifp->if_flags |= IFF_OACTIVE; + cur_tx = NULL; + break; + } + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + cur_tx = &sc->sf_ldata->sf_tx_dlist[i]; + if (sf_encap(sc, cur_tx, m_head)) { + IF_PREPEND(&ifp->if_snd, m_head); + ifp->if_flags |= IFF_OACTIVE; + cur_tx = NULL; + break; + } + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, m_head); + + SF_INC(i, SF_TX_DLIST_CNT); + sc->sf_tx_cnt++; + /* + * Don't get the TX DMA queue get too full. + */ + if (sc->sf_tx_cnt > 64) + break; + } + + if (cur_tx == NULL) { + SF_UNLOCK(sc); + return; + } + + /* Transmit */ + csr_write_4(sc, SF_TXDQ_PRODIDX, + (txprod & ~SF_TXDQ_PRODIDX_HIPRIO) | + ((i << 20) & 0xFFFF0000)); + + ifp->if_timer = 5; + + SF_UNLOCK(sc); + + return; +} + +static void sf_stop(sc) + struct sf_softc *sc; +{ + int i; + struct ifnet *ifp; + + SF_LOCK(sc); + + ifp = &sc->arpcom.ac_if; + + untimeout(sf_stats_update, sc, sc->sf_stat_ch); + + csr_write_4(sc, SF_GEN_ETH_CTL, 0); + csr_write_4(sc, SF_CQ_CONSIDX, 0); + csr_write_4(sc, SF_CQ_PRODIDX, 0); + csr_write_4(sc, SF_RXDQ_ADDR_Q1, 0); + csr_write_4(sc, SF_RXDQ_CTL_1, 0); + csr_write_4(sc, SF_RXDQ_PTR_Q1, 0); + csr_write_4(sc, SF_TXCQ_CTL, 0); + csr_write_4(sc, SF_TXDQ_ADDR_HIPRIO, 0); + csr_write_4(sc, SF_TXDQ_CTL, 0); + sf_reset(sc); + + sc->sf_link = 0; + + for (i = 0; i < SF_RX_DLIST_CNT; i++) { + if (sc->sf_ldata->sf_rx_dlist_big[i].sf_mbuf != NULL) { + m_freem(sc->sf_ldata->sf_rx_dlist_big[i].sf_mbuf); + sc->sf_ldata->sf_rx_dlist_big[i].sf_mbuf = NULL; + } + } + + for (i = 0; i < SF_TX_DLIST_CNT; i++) { + if (sc->sf_ldata->sf_tx_dlist[i].sf_mbuf != NULL) { + m_freem(sc->sf_ldata->sf_tx_dlist[i].sf_mbuf); + sc->sf_ldata->sf_tx_dlist[i].sf_mbuf = NULL; + } + } + + ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); + SF_UNLOCK(sc); + + return; +} + +/* + * Note: it is important that this function not be interrupted. We + * use a two-stage register access scheme: if we are interrupted in + * between setting the indirect address register and reading from the + * indirect data register, the contents of the address register could + * be changed out from under us. + */ +static void sf_stats_update(xsc) + void *xsc; +{ + struct sf_softc *sc; + struct ifnet *ifp; + struct mii_data *mii; + struct sf_stats stats; + u_int32_t *ptr; + int i; + + sc = xsc; + SF_LOCK(sc); + ifp = &sc->arpcom.ac_if; + mii = device_get_softc(sc->sf_miibus); + + ptr = (u_int32_t *)&stats; + for (i = 0; i < sizeof(stats)/sizeof(u_int32_t); i++) + ptr[i] = csr_read_4(sc, SF_STATS_BASE + + (i + sizeof(u_int32_t))); + + for (i = 0; i < sizeof(stats)/sizeof(u_int32_t); i++) + csr_write_4(sc, SF_STATS_BASE + + (i + sizeof(u_int32_t)), 0); + + ifp->if_collisions += stats.sf_tx_single_colls + + stats.sf_tx_multi_colls + stats.sf_tx_excess_colls; + + mii_tick(mii); + + if (!sc->sf_link && mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->sf_link++; + if (ifp->if_snd.ifq_head != NULL) + sf_start(ifp); + } + + sc->sf_stat_ch = timeout(sf_stats_update, sc, hz); + + SF_UNLOCK(sc); + + return; +} + +static void sf_watchdog(ifp) + struct ifnet *ifp; +{ + struct sf_softc *sc; + + sc = ifp->if_softc; + + SF_LOCK(sc); + + ifp->if_oerrors++; + printf("sf%d: watchdog timeout\n", sc->sf_unit); + + sf_stop(sc); + sf_reset(sc); + sf_init(sc); + + if (ifp->if_snd.ifq_head != NULL) + sf_start(ifp); + + SF_UNLOCK(sc); + + return; +} + +static void sf_shutdown(dev) + device_t dev; +{ + struct sf_softc *sc; + + sc = device_get_softc(dev); + + sf_stop(sc); + + return; +} diff --git a/sys/pci/if_sfreg.h b/sys/pci/if_sfreg.h new file mode 100644 index 0000000..1975cfc --- /dev/null +++ b/sys/pci/if_sfreg.h @@ -0,0 +1,1060 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * Registers for the Adaptec AIC-6915 Starfire. The Starfire has a 512K + * register space. These registers can be accessed in the following way: + * - PCI config registers are always accessible through PCI config space + * - Full 512K space mapped into memory using PCI memory mapped access + * - 256-byte I/O space mapped through PCI I/O access + * - Full 512K space mapped through indirect I/O using PCI I/O access + * It's possible to use either memory mapped mode or I/O mode to access + * the registers, but memory mapped is usually the easiest. All registers + * are 32 bits wide and must be accessed using 32-bit operations. + */ + +/* + * Adaptec PCI vendor ID. + */ +#define AD_VENDORID 0x9004 + +/* + * AIC-6915 PCI device ID. + */ +#define AD_DEVICEID_STARFIRE 0x6915 + +/* + * AIC-6915 subsystem IDs. Adaptec uses the subsystem ID to identify + * the exact kind of NIC on which the ASIC is mounted. Currently there + * are six different variations. Note: the Adaptec manual lists code 0x28 + * for two different NICs: the 62044 and the 69011/TX. This is a typo: + * the code for the 62044 is really 0x18. + * + * Note that there also appears to be an 0x19 code for a newer rev + * 62044 card. + */ +#define AD_SUBSYSID_62011_REV0 0x0008 /* single port 10/100baseTX 64-bit */ +#define AD_SUBSYSID_62011_REV1 0x0009 /* single port 10/100baseTX 64-bit */ +#define AD_SUBSYSID_62022 0x0010 /* dual port 10/100baseTX 64-bit */ +#define AD_SUBSYSID_62044_REV0 0x0018 /* quad port 10/100baseTX 64-bit */ +#define AD_SUBSYSID_62044_REV1 0x0019 /* quad port 10/100baseTX 64-bit */ +#define AD_SUBSYSID_62020 0x0020 /* single port 10/100baseFX 64-bit */ +#define AD_SUBSYSID_69011 0x0028 /* single port 10/100baseTX 32-bit */ + +/* + * Starfire internal register space map. The entire register space + * is available using PCI memory mapped mode. The SF_RMAP_INTREG + * space is available using PCI I/O mode. The entire space can be + * accessed using indirect I/O using the indirect I/O addr and + * indirect I/O data registers located within the SF_RMAP_INTREG space. + */ +#define SF_RMAP_ROMADDR_BASE 0x00000 /* Expansion ROM space */ +#define SF_RMAP_ROMADDR_MAX 0x3FFFF + +#define SF_RMAP_EXGPIO_BASE 0x40000 /* External general purpose regs */ +#define SF_RMAP_EXGPIO_MAX 0x3FFFF + +#define SF_RMAP_INTREG_BASE 0x50000 /* Internal functional registers */ +#define SF_RMAP_INTREG_MAX 0x500FF +#define SF_RMAP_GENREG_BASE 0x50100 /* General purpose registers */ +#define SF_RMAP_GENREG_MAX 0x5FFFF + +#define SF_RMAP_FIFO_BASE 0x60000 +#define SF_RMAP_FIFO_MAX 0x6FFFF + +#define SF_RMAP_STS_BASE 0x70000 +#define SF_RMAP_STS_MAX 0x70083 + +#define SF_RMAP_RSVD_BASE 0x70084 +#define SF_RMAP_RSVD_MAX 0x7FFFF + +/* + * PCI config header registers, 0x0000 to 0x003F + */ +#define SF_PCI_VENDOR_ID 0x0000 +#define SF_PCI_DEVICE_ID 0x0002 +#define SF_PCI_COMMAND 0x0004 +#define SF_PCI_STATUS 0x0006 +#define SF_PCI_REVID 0x0008 +#define SF_PCI_CLASSCODE 0x0009 +#define SF_PCI_CACHELEN 0x000C +#define SF_PCI_LATENCY_TIMER 0x000D +#define SF_PCI_HEADER_TYPE 0x000E +#define SF_PCI_LOMEM 0x0010 +#define SF_PCI_LOIO 0x0014 +#define SF_PCI_SUBVEN_ID 0x002C +#define SF_PCI_SYBSYS_ID 0x002E +#define SF_PCI_BIOSROM 0x0030 +#define SF_PCI_INTLINE 0x003C +#define SF_PCI_INTPIN 0x003D +#define SF_PCI_MINGNT 0x003E +#define SF_PCI_MINLAT 0x003F + +/* + * PCI registers, 0x0040 to 0x006F + */ +#define SF_PCI_DEVCFG 0x0040 +#define SF_BACCTL 0x0044 +#define SF_PCI_MON1 0x0048 +#define SF_PCI_MON2 0x004C +#define SF_PCI_CAPID 0x0050 /* 8 bits */ +#define SF_PCI_NEXTPTR 0x0051 /* 8 bits */ +#define SF_PCI_PWRMGMTCAP 0x0052 /* 16 bits */ +#define SF_PCI_PWRMGMTCTRL 0x0054 /* 16 bits */ +#define SF_PCI_PME_EVENT 0x0058 +#define SF_PCI_EECTL 0x0060 +#define SF_PCI_COMPLIANCE 0x0064 +#define SF_INDIRECTIO_ADDR 0x0068 +#define SF_INDIRECTIO_DATA 0x006C + +#define SF_PCIDEVCFG_RESET 0x00000001 +#define SF_PCIDEVCFG_FORCE64 0x00000002 +#define SF_PCIDEVCFG_SYSTEM64 0x00000004 +#define SF_PCIDEVCFG_RSVD0 0x00000008 +#define SF_PCIDEVCFG_INCR_INB 0x00000010 +#define SF_PCIDEVCFG_ABTONPERR 0x00000020 +#define SF_PCIDEVCFG_STPONPERR 0x00000040 +#define SF_PCIDEVCFG_MR_ENB 0x00000080 +#define SF_PCIDEVCFG_FIFOTHR 0x00000F00 +#define SF_PCIDEVCFG_STPONCA 0x00001000 +#define SF_PCIDEVCFG_PCIMEN 0x00002000 /* enable PCI bus master */ +#define SF_PCIDEVCFG_LATSTP 0x00004000 +#define SF_PCIDEVCFG_BYTE_ENB 0x00008000 +#define SF_PCIDEVCFG_EECSWIDTH 0x00070000 +#define SF_PCIDEVCFG_STPMWCA 0x00080000 +#define SF_PCIDEVCFG_REGCSWIDTH 0x00700000 +#define SF_PCIDEVCFG_INTR_ENB 0x00800000 +#define SF_PCIDEVCFG_DPR_ENB 0x01000000 +#define SF_PCIDEVCFG_RSVD1 0x02000000 +#define SF_PCIDEVCFG_RSVD2 0x04000000 +#define SF_PCIDEVCFG_STA_ENB 0x08000000 +#define SF_PCIDEVCFG_RTA_ENB 0x10000000 +#define SF_PCIDEVCFG_RMA_ENB 0x20000000 +#define SF_PCIDEVCFG_SSE_ENB 0x40000000 +#define SF_PCIDEVCFG_DPE_ENB 0x80000000 + +#define SF_BACCTL_BACDMA_ENB 0x00000001 +#define SF_BACCTL_PREFER_RXDMA 0x00000002 +#define SF_BACCTL_PREFER_TXDMA 0x00000004 +#define SF_BACCTL_SINGLE_DMA 0x00000008 +#define SF_BACCTL_SWAPMODE_DATA 0x00000030 +#define SF_BACCTL_SWAPMODE_DESC 0x000000C0 + +#define SF_SWAPMODE_LE 0x00000000 +#define SF_SWAPMODE_BE 0x00000010 + +#define SF_PSTATE_MASK 0x0003 +#define SF_PSTATE_D0 0x0000 +#define SF_PSTATE_D1 0x0001 +#define SF_PSTATE_D2 0x0002 +#define SF_PSTATE_D3 0x0003 +#define SF_PME_EN 0x0010 +#define SF_PME_STATUS 0x8000 + + +/* + * Ethernet registers 0x0070 to 0x00FF + */ +#define SF_GEN_ETH_CTL 0x0070 +#define SF_TIMER_CTL 0x0074 +#define SF_CURTIME 0x0078 +#define SF_ISR 0x0080 +#define SF_ISR_SHADOW 0x0084 +#define SF_IMR 0x0088 +#define SF_GPIO 0x008C +#define SF_TXDQ_CTL 0x0090 +#define SF_TXDQ_ADDR_HIPRIO 0x0094 +#define SF_TXDQ_ADDR_LOPRIO 0x0098 +#define SF_TXDQ_ADDR_HIADDR 0x009C +#define SF_TXDQ_PRODIDX 0x00A0 +#define SF_TXDQ_CONSIDX 0x00A4 +#define SF_TXDMA_STS1 0x00A8 +#define SF_TXDMA_STS2 0x00AC +#define SF_TX_FRAMCTL 0x00B0 +#define SF_TXCQ_ADDR_HI 0x00B4 +#define SF_TXCQ_CTL 0x00B8 +#define SF_RXCQ_CTL_1 0x00BC +#define SF_RXCQ_CTL_2 0x00C0 +#define SF_CQ_CONSIDX 0x00C4 +#define SF_CQ_PRODIDX 0x00C8 +#define SF_CQ_RXQ2 0x00CC +#define SF_RXDMA_CTL 0x00D0 +#define SF_RXDQ_CTL_1 0x00D4 +#define SF_RXDQ_CTL_2 0x00D8 +#define SF_RXDQ_ADDR_HIADDR 0x00DC +#define SF_RXDQ_ADDR_Q1 0x00E0 +#define SF_RXDQ_ADDR_Q2 0x00E4 +#define SF_RXDQ_PTR_Q1 0x00E8 +#define SF_RXDQ_PTR_Q2 0x00EC +#define SF_RXDMA_STS 0x00F0 +#define SF_RXFILT 0x00F4 +#define SF_RX_FRAMETEST_OUT 0x00F8 + +/* Ethernet control register */ +#define SF_ETHCTL_RX_ENB 0x00000001 +#define SF_ETHCTL_TX_ENB 0x00000002 +#define SF_ETHCTL_RXDMA_ENB 0x00000004 +#define SF_ETHCTL_TXDMA_ENB 0x00000008 +#define SF_ETHCTL_RXGFP_ENB 0x00000010 +#define SF_ETHCTL_TXGFP_ENB 0x00000020 +#define SF_ETHCTL_SOFTINTR 0x00000800 + +/* Timer control register */ +#define SF_TIMER_IMASK_INTERVAL 0x0000001F +#define SF_TIMER_IMASK_MODE 0x00000060 +#define SF_TIMER_SMALLFRAME_BYP 0x00000100 +#define SF_TIMER_SMALLRX_FRAME 0x00000600 +#define SF_TIMER_TIMES_TEN 0x00000800 +#define SF_TIMER_RXHIPRIO_BYP 0x00001000 +#define SF_TIMER_TX_DMADONE_DLY 0x00002000 +#define SF_TIMER_TX_QDONE_DLY 0x00004000 +#define SF_TIMER_TX_FRDONE_DLY 0x00008000 +#define SF_TIMER_GENTIMER 0x00FF0000 +#define SF_TIMER_ONESHOT 0x01000000 +#define SF_TIMER_GENTIMER_RES 0x02000000 +#define SF_TIMER_TIMEST_RES 0x04000000 +#define SF_TIMER_RXQ2DONE_DLY 0x10000000 +#define SF_TIMER_EARLYRX2_DLY 0x20000000 +#define SF_TIMER_RXQ1DONE_DLY 0x40000000 +#define SF_TIMER_EARLYRX1_DLY 0x80000000 + +/* Interrupt status register */ +#define SF_ISR_PCIINT_ASSERTED 0x00000001 +#define SF_ISR_GFP_TX 0x00000002 +#define SF_ISR_GFP_RX 0x00000004 +#define SF_ISR_TX_BADID_HIPRIO 0x00000008 +#define SF_ISR_TX_BADID_LOPRIO 0x00000010 +#define SF_ISR_NO_TX_CSUM 0x00000020 +#define SF_ISR_RXDQ2_NOBUFS 0x00000040 +#define SF_ISR_RXGFP_NORESP 0x00000080 +#define SF_ISR_RXDQ1_DMADONE 0x00000100 +#define SF_ISR_RXDQ2_DMADONE 0x00000200 +#define SF_ISR_RXDQ1_EARLY 0x00000400 +#define SF_ISR_RXDQ2_EARLY 0x00000800 +#define SF_ISR_TX_QUEUEDONE 0x00001000 +#define SF_ISR_TX_DMADONE 0x00002000 +#define SF_ISR_TX_TXDONE 0x00004000 +#define SF_ISR_NORMALINTR 0x00008000 +#define SF_ISR_RXDQ1_NOBUFS 0x00010000 +#define SF_ISR_RXCQ2_NOBUFS 0x00020000 +#define SF_ISR_TX_LOFIFO 0x00040000 +#define SF_ISR_DMAERR 0x00080000 +#define SF_ISR_PCIINT 0x00100000 +#define SF_ISR_TXCQ_NOBUFS 0x00200000 +#define SF_ISR_RXCQ1_NOBUFS 0x00400000 +#define SF_ISR_SOFTINTR 0x00800000 +#define SF_ISR_GENTIMER 0x01000000 +#define SF_ISR_ABNORMALINTR 0x02000000 +#define SF_ISR_RSVD0 0x04000000 +#define SF_ISR_STATSOFLOW 0x08000000 +#define SF_ISR_GPIO 0xF0000000 + +/* + * Shadow interrupt status register. Unlike the normal IRQ register, + * reading bits here does not automatically cause them to reset. + */ +#define SF_SISR_PCIINT_ASSERTED 0x00000001 +#define SF_SISR_GFP_TX 0x00000002 +#define SF_SISR_GFP_RX 0x00000004 +#define SF_SISR_TX_BADID_HIPRIO 0x00000008 +#define SF_SISR_TX_BADID_LOPRIO 0x00000010 +#define SF_SISR_NO_TX_CSUM 0x00000020 +#define SF_SISR_RXDQ2_NOBUFS 0x00000040 +#define SF_SISR_RXGFP_NORESP 0x00000080 +#define SF_SISR_RXDQ1_DMADONE 0x00000100 +#define SF_SISR_RXDQ2_DMADONE 0x00000200 +#define SF_SISR_RXDQ1_EARLY 0x00000400 +#define SF_SISR_RXDQ2_EARLY 0x00000800 +#define SF_SISR_TX_QUEUEDONE 0x00001000 +#define SF_SISR_TX_DMADONE 0x00002000 +#define SF_SISR_TX_TXDONE 0x00004000 +#define SF_SISR_NORMALINTR 0x00008000 +#define SF_SISR_RXDQ1_NOBUFS 0x00010000 +#define SF_SISR_RXCQ2_NOBUFS 0x00020000 +#define SF_SISR_TX_LOFIFO 0x00040000 +#define SF_SISR_DMAERR 0x00080000 +#define SF_SISR_PCIINT 0x00100000 +#define SF_SISR_TXCQ_NOBUFS 0x00200000 +#define SF_SISR_RXCQ1_NOBUFS 0x00400000 +#define SF_SISR_SOFTINTR 0x00800000 +#define SF_SISR_GENTIMER 0x01000000 +#define SF_SISR_ABNORMALINTR 0x02000000 +#define SF_SISR_RSVD0 0x04000000 +#define SF_SISR_STATSOFLOW 0x08000000 +#define SF_SISR_GPIO 0xF0000000 + +/* Interrupt mask register */ +#define SF_IMR_PCIINT_ASSERTED 0x00000001 +#define SF_IMR_GFP_TX 0x00000002 +#define SF_IMR_GFP_RX 0x00000004 +#define SF_IMR_TX_BADID_HIPRIO 0x00000008 +#define SF_IMR_TX_BADID_LOPRIO 0x00000010 +#define SF_IMR_NO_TX_CSUM 0x00000020 +#define SF_IMR_RXDQ2_NOBUFS 0x00000040 +#define SF_IMR_RXGFP_NORESP 0x00000080 +#define SF_IMR_RXDQ1_DMADONE 0x00000100 +#define SF_IMR_RXDQ2_DMADONE 0x00000200 +#define SF_IMR_RXDQ1_EARLY 0x00000400 +#define SF_IMR_RXDQ2_EARLY 0x00000800 +#define SF_IMR_TX_QUEUEDONE 0x00001000 +#define SF_IMR_TX_DMADONE 0x00002000 +#define SF_IMR_TX_TXDONE 0x00004000 +#define SF_IMR_NORMALINTR 0x00008000 +#define SF_IMR_RXDQ1_NOBUFS 0x00010000 +#define SF_IMR_RXCQ2_NOBUFS 0x00020000 +#define SF_IMR_TX_LOFIFO 0x00040000 +#define SF_IMR_DMAERR 0x00080000 +#define SF_IMR_PCIINT 0x00100000 +#define SF_IMR_TXCQ_NOBUFS 0x00200000 +#define SF_IMR_RXCQ1_NOBUFS 0x00400000 +#define SF_IMR_SOFTINTR 0x00800000 +#define SF_IMR_GENTIMER 0x01000000 +#define SF_IMR_ABNORMALINTR 0x02000000 +#define SF_IMR_RSVD0 0x04000000 +#define SF_IMR_STATSOFLOW 0x08000000 +#define SF_IMR_GPIO 0xF0000000 + +#define SF_INTRS \ + (SF_IMR_RXDQ2_NOBUFS|SF_IMR_RXDQ1_DMADONE|SF_IMR_RXDQ2_DMADONE| \ + SF_IMR_TX_TXDONE|SF_IMR_RXDQ1_NOBUFS|SF_IMR_RXDQ2_DMADONE| \ + SF_IMR_NORMALINTR|SF_IMR_ABNORMALINTR|SF_IMR_TXCQ_NOBUFS| \ + SF_IMR_RXCQ1_NOBUFS|SF_IMR_RXCQ2_NOBUFS|SF_IMR_STATSOFLOW| \ + SF_IMR_TX_LOFIFO) + +/* TX descriptor queue control registers */ +#define SF_TXDQCTL_DESCTYPE 0x00000007 +#define SF_TXDQCTL_NODMACMP 0x00000008 +#define SF_TXDQCTL_MINSPACE 0x00000070 +#define SF_TXDQCTL_64BITADDR 0x00000080 +#define SF_TXDQCTL_BURSTLEN 0x00003F00 +#define SF_TXDQCTL_SKIPLEN 0x001F0000 +#define SF_TXDQCTL_HIPRIOTHRESH 0xFF000000 + +#define SF_TXBUFDESC_TYPE0 0x00000000 +#define SF_TXBUFDESC_TYPE1 0x00000001 +#define SF_TXBUFDESC_TYPE2 0x00000002 +#define SF_TXBUFDESC_TYPE3 0x00000003 +#define SF_TXBUFDESC_TYPE4 0x00000004 + +#define SF_TXMINSPACE_UNLIMIT 0x00000000 +#define SF_TXMINSPACE_32BYTES 0x00000010 +#define SF_TXMINSPACE_64BYTES 0x00000020 +#define SF_TXMINSPACE_128BYTES 0x00000030 +#define SF_TXMINSPACE_256BYTES 0x00000040 + +#define SF_TXSKIPLEN_0BYTES 0x00000000 +#define SF_TXSKIPLEN_8BYTES 0x00010000 +#define SF_TXSKIPLEN_16BYTES 0x00020000 +#define SF_TXSKIPLEN_24BYTES 0x00030000 +#define SF_TXSKIPLEN_32BYTES 0x00040000 + +/* TX frame control register */ +#define SF_TXFRMCTL_TXTHRESH 0x000000FF +#define SF_TXFRMCTL_CPLAFTERTX 0x00000100 +#define SF_TXFRMCRL_DEBUG 0x0000FE00 +#define SF_TXFRMCTL_STATUS 0x01FF0000 +#define SF_TXFRMCTL_MAC_TXIF 0xFE000000 + +/* TX completion queue control register */ +#define SF_TXCQ_THRESH 0x0000000F +#define SF_TXCQ_COMMON 0x00000010 +#define SF_TXCQ_SIZE 0x00000020 +#define SF_TXCQ_WRITEENB 0x00000040 +#define SF_TXCQ_USE_64BIT 0x00000080 +#define SF_TXCQ_ADDR 0xFFFFFF00 + +/* RX completion queue control register */ +#define SF_RXCQ_THRESH 0x0000000F +#define SF_RXCQ_TYPE 0x00000030 +#define SF_RXCQ_WRITEENB 0x00000040 +#define SF_RXCQ_USE_64BIT 0x00000080 +#define SF_RXCQ_ADDR 0xFFFFFF00 + +#define SF_RXCQTYPE_0 0x00000000 +#define SF_RXCQTYPE_1 0x00000010 +#define SF_RXCQTYPE_2 0x00000020 +#define SF_RXCQTYPE_3 0x00000030 + +/* TX descriptor queue producer index register */ +#define SF_TXDQ_PRODIDX_LOPRIO 0x000007FF +#define SF_TXDQ_PRODIDX_HIPRIO 0x07FF0000 + +/* TX descriptor queue consumer index register */ +#define SF_TXDQ_CONSIDX_LOPRIO 0x000007FF +#define SF_TXDQ_CONSIDX_HIPRIO 0x07FF0000 + +/* Completion queue consumer index register */ +#define SF_CQ_CONSIDX_RXQ1 0x000003FF +#define SF_CQ_CONSIDX_RXTHRMODE 0x00008000 +#define SF_CQ_CONSIDX_TXQ 0x03FF0000 +#define SF_CQ_CONSIDX_TXTHRMODE 0x80000000 + +/* Completion queue producer index register */ +#define SF_CQ_PRODIDX_RXQ1 0x000003FF +#define SF_CQ_PRODIDX_TXQ 0x03FF0000 + +/* RX completion queue 2 consumer/producer index register */ +#define SF_CQ_RXQ2_CONSIDX 0x000003FF +#define SF_CQ_RXQ2_RXTHRMODE 0x00008000 +#define SF_CQ_RXQ2_PRODIDX 0x03FF0000 + +#define SF_CQ_RXTHRMODE_INT_ON 0x00008000 +#define SF_CQ_RXTHRMODE_INT_OFF 0x00000000 +#define SF_CQ_TXTHRMODE_INT_ON 0x80000000 +#define SF_CQ_TXTHRMODE_INT_OFF 0x00000000 + +#define SF_IDX_LO(x) ((x) & 0x000007FF) +#define SF_IDX_HI(x) (((x) >> 16) & 0x000007FF) + +/* RX DMA control register */ +#define SF_RXDMA_BURSTSIZE 0x0000007F +#define SF_RXDMA_FPTESTMODE 0x00000080 +#define SF_RXDMA_HIPRIOTHRESH 0x00000F00 +#define SF_RXDMA_RXEARLYTHRESH 0x0001F000 +#define SF_RXDMA_DMACRC 0x00040000 +#define SF_RXDMA_USEBKUPQUEUE 0x00080000 +#define SF_RXDMA_QUEUEMODE 0x00700000 +#define SF_RXDMA_RXCQ2_ON 0x00800000 +#define SF_RXDMA_CSUMMODE 0x03000000 +#define SF_RXDMA_DMAPAUSEPKTS 0x04000000 +#define SF_RXDMA_DMACTLPKTS 0x08000000 +#define SF_RXDMA_DMACRXERRPKTS 0x10000000 +#define SF_RXDMA_DMABADPKTS 0x20000000 +#define SF_RXDMA_DMARUNTS 0x40000000 +#define SF_RXDMA_REPORTBADPKTS 0x80000000 + +#define SF_RXDQMODE_Q1ONLY 0x00100000 +#define SF_RXDQMODE_Q2_ON_FP 0x00200000 +#define SF_RXDQMODE_Q2_ON_SHORT 0x00300000 +#define SF_RXDQMODE_Q2_ON_PRIO 0x00400000 +#define SF_RXDQMODE_SPLITHDR 0x00500000 + +#define SF_RXCSUMMODE_IGNORE 0x00000000 +#define SF_RXCSUMMODE_REJECT_BAD_TCP 0x01000000 +#define SF_RXCSUMMODE_REJECT_BAD_TCPUDP 0x02000000 +#define SF_RXCSUMMODE_RSVD 0x03000000 + +/* RX descriptor queue control registers */ +#define SF_RXDQCTL_MINDESCTHR 0x0000007F +#define SF_RXDQCTL_Q1_WE 0x00000080 +#define SF_RXDQCTL_DESCSPACE 0x00000700 +#define SF_RXDQCTL_64BITDADDR 0x00000800 +#define SF_RXDQCTL_64BITBADDR 0x00001000 +#define SF_RXDQCTL_VARIABLE 0x00002000 +#define SF_RXDQCTL_ENTRIES 0x00004000 +#define SF_RXDQCTL_PREFETCH 0x00008000 +#define SF_RXDQCTL_BUFLEN 0xFFFF0000 + +#define SF_DESCSPACE_4BYTES 0x00000000 +#define SF_DESCSPACE_8BYTES 0x00000100 +#define SF_DESCSPACE_16BYTES 0x00000200 +#define SF_DESCSPACE_32BYTES 0x00000300 +#define SF_DESCSPACE_64BYTES 0x00000400 +#define SF_DESCSPACE_128_BYTES 0x00000500 + +/* RX buffer consumer/producer index registers */ +#define SF_RXDQ_PRODIDX 0x000007FF +#define SF_RXDQ_CONSIDX 0x07FF0000 + +/* RX filter control register */ +#define SF_RXFILT_PROMISC 0x00000001 +#define SF_RXFILT_ALLMULTI 0x00000002 +#define SF_RXFILT_BROAD 0x00000004 +#define SF_RXFILT_HASHPRIO 0x00000008 +#define SF_RXFILT_HASHMODE 0x00000030 +#define SF_RXFILT_PERFMODE 0x000000C0 +#define SF_RXFILT_VLANMODE 0x00000300 +#define SF_RXFILT_WAKEMODE 0x00000C00 +#define SF_RXFILT_MULTI_NOBROAD 0x00001000 +#define SF_RXFILT_MIN_VLANPRIO 0x0000E000 +#define SF_RXFILT_PEFECTPRIO 0xFFFF0000 + +/* Hash filtering mode */ +#define SF_HASHMODE_OFF 0x00000000 +#define SF_HASHMODE_WITHVLAN 0x00000010 +#define SF_HASHMODE_ANYVLAN 0x00000020 +#define SF_HASHMODE_ANY 0x00000030 + +/* Perfect filtering mode */ +#define SF_PERFMODE_OFF 0x00000000 +#define SF_PERFMODE_NORMAL 0x00000040 +#define SF_PERFMODE_INVERSE 0x00000080 +#define SF_PERFMODE_VLAN 0x000000C0 + +/* VLAN mode */ +#define SF_VLANMODE_OFF 0x00000000 +#define SF_VLANMODE_NOSTRIP 0x00000100 +#define SF_VLANMODE_STRIP 0x00000200 +#define SF_VLANMODE_RSVD 0x00000300 + +/* Wakeup mode */ +#define SF_WAKEMODE_OFF 0x00000000 +#define SF_WAKEMODE_FILTER 0x00000400 +#define SF_WAKEMODE_FP 0x00000800 +#define SF_WAKEMODE_HIPRIO 0x00000C00 + +/* + * Extra PCI registers 0x0100 to 0x0FFF + */ +#define SF_PCI_TARGSTAT 0x0100 +#define SF_PCI_MASTSTAT1 0x0104 +#define SF_PCI_MASTSTAT2 0x0108 +#define SF_PCI_DMAHOSTADDR_LO 0x010C +#define SF_BAC_DMADIAG0 0x0110 +#define SF_BAC_DMADIAG1 0x0114 +#define SF_BAC_DMADIAG2 0x0118 +#define SF_BAC_DMADIAG3 0x011C +#define SF_PAR0 0x0120 +#define SF_PAR1 0x0124 +#define SF_PCICB_FUNCEVENT 0x0130 +#define SF_PCICB_FUNCEVENT_MASK 0x0134 +#define SF_PCICB_FUNCSTATE 0x0138 +#define SF_PCICB_FUNCFORCE 0x013C + +/* + * Serial EEPROM registers 0x1000 to 0x1FFF + * Presumeably the EEPROM is mapped into this 8K window. + */ +#define SF_EEADDR_BASE 0x1000 +#define SF_EEADDR_MAX 0x1FFF + +#define SF_EE_NODEADDR 14 + +/* + * MII registers registers 0x2000 to 0x3FFF + * There are 32 sets of 32 registers, one set for each possible + * PHY address. Each 32 bit register is split into a 16-bit data + * port and a couple of status bits. + */ + +#define SF_MIIADDR_BASE 0x2000 +#define SF_MIIADDR_MAX 0x3FFF +#define SF_MII_BLOCKS 32 + +#define SF_MII_DATAVALID 0x80000000 +#define SF_MII_BUSY 0x40000000 +#define SF_MII_DATAPORT 0x0000FFFF + +#define SF_PHY_REG(phy, reg) \ + (SF_MIIADDR_BASE + (phy * SF_MII_BLOCKS * sizeof(u_int32_t)) + \ + (reg * sizeof(u_int32_t))) + +/* + * Ethernet extra registers 0x4000 to 0x4FFF + */ +#define SF_TESTMODE 0x4000 +#define SF_RX_FRAMEPROC_CTL 0x4004 +#define SF_TX_FRAMEPROC_CTL 0x4008 + +/* + * MAC registers 0x5000 to 0x5FFF + */ +#define SF_MACCFG_1 0x5000 +#define SF_MACCFG_2 0x5004 +#define SF_BKTOBKIPG 0x5008 +#define SF_NONBKTOBKIPG 0x500C +#define SF_COLRETRY 0x5010 +#define SF_MAXLEN 0x5014 +#define SF_TXNIBBLECNT 0x5018 +#define SF_TXBYTECNT 0x501C +#define SF_RETXCNT 0x5020 +#define SF_RANDNUM 0x5024 +#define SF_RANDNUM_MASK 0x5028 +#define SF_TOTALTXCNT 0x5034 +#define SF_RXBYTECNT 0x5040 +#define SF_TXPAUSETIMER 0x5060 +#define SF_VLANTYPE 0x5064 +#define SF_MIISTATUS 0x5070 + +#define SF_MACCFG1_HUGEFRAMES 0x00000001 +#define SF_MACCFG1_FULLDUPLEX 0x00000002 +#define SF_MACCFG1_AUTOPAD 0x00000004 +#define SF_MACCFG1_HDJAM 0x00000008 +#define SF_MACCFG1_DELAYCRC 0x00000010 +#define SF_MACCFG1_NOBACKOFF 0x00000020 +#define SF_MACCFG1_LENGTHCHECK 0x00000040 +#define SF_MACCFG1_PUREPREAMBLE 0x00000080 +#define SF_MACCFG1_PASSALLRX 0x00000100 +#define SF_MACCFG1_PREAM_DETCNT 0x00000200 +#define SF_MACCFG1_RX_FLOWENB 0x00000400 +#define SF_MACCFG1_TX_FLOWENB 0x00000800 +#define SF_MACCFG1_TESTMODE 0x00003000 +#define SF_MACCFG1_MIILOOPBK 0x00004000 +#define SF_MACCFG1_SOFTRESET 0x00008000 + +/* + * There are the recommended IPG nibble counter settings + * specified in the Adaptec manual for full duplex and + * half duplex operation. + */ +#define SF_IPGT_FDX 0x15 +#define SF_IPGT_HDX 0x11 + +/* + * RX filter registers 0x6000 to 0x6FFF + */ +#define SF_RXFILT_PERFECT_BASE 0x6000 +#define SF_RXFILT_PERFECT_MAX 0x60FF +#define SF_RXFILT_PERFECT_SKIP 0x0010 +#define SF_RXFILT_PERFECT_CNT 0x0010 + +#define SF_RXFILT_HASH_BASE 0x6100 +#define SF_RXFILT_HASH_MAX 0x62FF +#define SF_RXFILT_HASH_SKIP 0x0010 +#define SF_RXFILT_HASH_CNT 0x001F +#define SF_RXFILT_HASH_ADDROFF 0x0000 +#define SF_RXFILT_HASH_PRIOOFF 0x0004 +#define SF_RXFILT_HASH_VLANOFF 0x0008 + +/* + * Statistics registers 0x7000 to 0x7FFF + */ +#define SF_STATS_BASE 0x7000 +#define SF_STATS_END 0x7FFF + +/* + * TX frame processor instruction space 0x8000 to 0x9FFF + */ + +/* + * RX frame processor instruction space 0xA000 to 0xBFFF + */ + +/* + * Ethernet FIFO access space 0xC000 to 0xDFFF + */ + +/* + * Reserved 0xE000 to 0xFFFF + */ + +/* + * Descriptor data structures. + */ + + +/* Receive descriptor formats. */ +#define SF_RX_MINSPACING 8 +#define SF_RX_DLIST_CNT 256 +#define SF_RX_CLIST_CNT 1024 +#define SF_RX_HOSTADDR(x) (((x) >> 2) & 0x3FFFFFFF) + +/* + * RX buffer descriptor type 0, 32-bit addressing. Note that we + * program the RX buffer queue control register(s) to allow a + * descriptor spacing of 16 bytes, which leaves room after each + * descriptor to store a pointer to the mbuf for each buffer. + */ +struct sf_rx_bufdesc_type0 { + u_int32_t sf_valid:1, + sf_end:1, + sf_addrlo:30; + u_int32_t sf_pad0; +#ifdef __i386__ + u_int32_t sf_pad1; +#endif + struct mbuf *sf_mbuf; +}; + +/* + * RX buffer descriptor type 0, 64-bit addressing. + */ +struct sf_rx_bufdesc_type1 { + u_int32_t sf_valid:1, + sf_end:1, + sf_addrlo:30; + u_int32_t sf_addrhi; +#ifdef __i386__ + u_int32_t sf_pad; +#endif + struct mbuf *sf_mbuf; +}; + +/* + * RX completion descriptor, type 0 (short). + */ +struct sf_rx_cmpdesc_type0 { + u_int32_t sf_len:16, + sf_endidx:11, + sf_status1:3, + sf_id:2; +}; + +/* + * RX completion descriptor, type 1 (basic). Includes vlan ID + * if this is a vlan-addressed packet, plus extended status. + */ +struct sf_rx_cmpdesc_type1 { + u_int32_t sf_len:16, + sf_endidx:11, + sf_status1:3, + sf_id:2; + u_int16_t sf_status2; + u_int16_t sf_vlanid; +}; + +/* + * RX completion descriptor, type 2 (checksum). Includes partial TCP/IP + * checksum instead of vlan tag, plus extended status. + */ +struct sf_rx_cmpdesc_type2 { + u_int32_t sf_len:16, + sf_endidx:11, + sf_status1:3, + sf_id:2; + u_int16_t sf_status2; + u_int16_t sf_cksum; +}; + +/* + * RX completion descriptor type 3 (full). Includes timestamp, partial + * TCP/IP checksum, vlan tag plus priority, two extended status fields. + */ +struct sf_rx_cmpdesc_type3 { + u_int32_t sf_len:16, + sf_endidx:11, + sf_status1:3, + sf_id:2; + u_int32_t sf_startidx:10, + sf_status3:6, + sf_status2:16; + u_int16_t sf_cksum; + u_int16_t sf_vlanid_prio; + u_int32_t sf_timestamp; +}; + +#define SF_RXSTAT1_QUEUE 0x1 +#define SF_RXSTAT1_FIFOFULL 0x2 +#define SF_RXSTAT1_OK 0x4 + + /* 0=unknown,5=unsupported */ +#define SF_RXSTAT2_FRAMETYPE 0x0007 /* 1=IPv4,2=IPv2,3=IPX,4=ICMP */ +#define SF_RXSTAT2_UDP 0x0008 +#define SF_RXSTAT2_TCP 0x0010 +#define SF_RXSTAT2_FRAG 0x0020 +#define SF_RXSTAT2_PCSUM_OK 0x0040 /* partial checksum ok */ +#define SF_RXSTAT2_CSUM_BAD 0x0080 /* TCP/IP checksum bad */ +#define SF_RXSTAT2_CSUM_OK 0x0100 /* TCP/IP checksum ok */ +#define SF_RXSTAT2_VLAN 0x0200 +#define SF_RXSTAT2_BADRXCODE 0x0400 +#define SF_RXSTAT2_DRIBBLE 0x0800 +#define SF_RXSTAT2_ISL_CRCERR 0x1000 +#define SF_RXSTAT2_CRCERR 0x2000 +#define SF_RXSTAT2_HASH 0x4000 +#define SF_RXSTAT2_PERFECT 0x8000 + +#define SF_RXSTAT3_TRAILER 0x01 +#define SF_RXSTAT3_HEADER 0x02 +#define SF_RXSTAT3_CONTROL 0x04 +#define SF_RXSTAT3_PAUSE 0x08 +#define SF_RXSTAT3_ISL 0x10 + +/* + * Transmit descriptor formats. + * Each transmit descriptor type allows for a skip field at the + * start of each structure. The size of the skip field can vary, + * however we always set it for 8 bytes, which is enough to hold + * a pointer (32 bits on x86, 64-bits on alpha) that we can use + * to hold the address of the head of the mbuf chain for the + * frame or fragment associated with the descriptor. This saves + * us from having to create a separate pointer array to hold + * the mbuf addresses. + */ +#define SF_TX_BUFDESC_ID 0xB +#define SF_MAXFRAGS 14 +#define SF_TX_MINSPACING 128 +#define SF_TX_DLIST_CNT 128 +#define SF_TX_DLIST_SIZE 16384 +#define SF_TX_SKIPLEN 1 +#define SF_TX_CLIST_CNT 1024 + +struct sf_frag { + u_int32_t sf_addr; + u_int16_t sf_fraglen; + u_int16_t sf_pktlen; +}; + +struct sf_frag_msdos { + u_int16_t sf_pktlen; + u_int16_t sf_fraglen; + u_int32_t sf_addr; +}; + +/* + * TX frame descriptor type 0, 32-bit addressing. One descriptor can + * be used to map multiple packet fragments. We use this format since + * BSD networking fragments packet data across mbuf chains. Note that + * the number of fragments can be variable depending on how the descriptor + * spacing is specified in the TX descriptor queue control register. + * We always use a spacing of 128 bytes, and a skipfield length of 8 + * bytes: this means 16 bytes for the descriptor, including the skipfield, + * with 121 bytes left for fragment maps. Each fragment requires 8 bytes, + * which allows for 14 fragments per descriptor. The total size of the + * transmit buffer queue is limited to 16384 bytes, so with a spacing of + * 128 bytes per descriptor, we have room for 128 descriptors in the queue. + */ +struct sf_tx_bufdesc_type0 { +#ifdef __i386__ + u_int32_t sf_pad; +#endif + struct mbuf *sf_mbuf; + u_int32_t sf_rsvd0:24, + sf_crcen:1, + sf_caltcp:1, + sf_end:1, + sf_intr:1, + sf_id:4; + u_int8_t sf_fragcnt; + u_int8_t sf_rsvd2; + u_int16_t sf_rsvd1; + struct sf_frag sf_frags[14]; +}; + +/* + * TX buffer descriptor type 1, 32-bit addressing. Each descriptor + * maps a single fragment. + */ +struct sf_tx_bufdesc_type1 { +#ifdef __i386__ + u_int32_t sf_pad; +#endif + struct mbuf *sf_mbuf; + u_int32_t sf_fraglen:16, + sf_fragcnt:8, + sf_crcen:1, + sf_caltcp:1, + sf_end:1, + sf_intr:1, + sf_id:4; + u_int32_t sf_addr; +}; + +/* + * TX buffer descriptor type 2, 64-bit addressing. Each descriptor + * maps a single fragment. + */ +struct sf_tx_bufdesc_type2 { +#ifdef __i386__ + u_int32_t sf_pad; +#endif + struct mbuf *sf_mbuf; + u_int32_t sf_fraglen:16, + sf_fragcnt:8, + sf_crcen:1, + sf_caltcp:1, + sf_end:1, + sf_intr:1, + sf_id:4; + u_int32_t sf_addrlo; + u_int32_t sf_addrhi; +}; + +/* TX buffer descriptor type 3 is not defined. */ + +/* + * TX frame descriptor type 4, 32-bit addressing. This is a special + * case of the type 0 descriptor, identical except that the fragment + * address and length fields are ordered differently. This is done + * to optimize copies in MS-DOS and OS/2 drivers. + */ +struct sf_tx_bufdesc_type4 { +#ifdef __i386__ + u_int32_t sf_pad; +#endif + struct mbuf *sf_mbuf; + u_int32_t sf_rsvd0:24, + sf_crcen:1, + sf_caltcp:1, + sf_end:1, + sf_intr:1, + sf_id:4; + u_int8_t sf_fragcnt; + u_int8_t sf_rsvd2; + u_int16_t sf_rsvd1; + struct sf_frag_msdos sf_frags[14]; +}; + +/* + * Transmit completion queue descriptor formats. + */ + +/* + * Transmit DMA completion descriptor, type 0. + */ +#define SF_TXCMPTYPE_DMA 0x4 +struct sf_tx_cmpdesc_type0 { + u_int32_t sf_index:15, + sf_priority:1, + sf_timestamp:13, + sf_type:3; +}; + +/* + * Transmit completion descriptor, type 1. + */ +#define SF_TXCMPTYPE_TX 0x5 +struct sf_tx_cmpdesc_type1 { + u_int32_t sf_index:15, + sf_priority:1, + sf_txstat:13, + sf_type:3; +}; + +#define SF_TXSTAT_CRCERR 0x0001 +#define SF_TXSTAT_LENCHECKERR 0x0002 +#define SF_TXSTAT_LENRANGEERR 0x0004 +#define SF_TXSTAT_TX_OK 0x0008 +#define SF_TXSTAT_TX_DEFERED 0x0010 +#define SF_TXSTAT_EXCESS_DEFER 0x0020 +#define SF_TXSTAT_EXCESS_COLL 0x0040 +#define SF_TXSTAT_LATE_COLL 0x0080 +#define SF_TXSTAT_TOOBIG 0x0100 +#define SF_TXSTAT_TX_UNDERRUN 0x0200 +#define SF_TXSTAT_CTLFRAME_OK 0x0400 +#define SF_TXSTAT_PAUSEFRAME_OK 0x0800 +#define SF_TXSTAT_PAUSED 0x1000 + +/* Statistics counters. */ +struct sf_stats { + u_int32_t sf_tx_frames; + u_int32_t sf_tx_single_colls; + u_int32_t sf_tx_multi_colls; + u_int32_t sf_tx_crcerrs; + u_int32_t sf_tx_bytes; + u_int32_t sf_tx_defered; + u_int32_t sf_tx_late_colls; + u_int32_t sf_tx_pause_frames; + u_int32_t sf_tx_control_frames; + u_int32_t sf_tx_excess_colls; + u_int32_t sf_tx_excess_defer; + u_int32_t sf_tx_mcast_frames; + u_int32_t sf_tx_bcast_frames; + u_int32_t sf_tx_frames_lost; + u_int32_t sf_rx_rx_frames; + u_int32_t sf_rx_crcerrs; + u_int32_t sf_rx_alignerrs; + u_int32_t sf_rx_bytes; + u_int32_t sf_rx_control_frames; + u_int32_t sf_rx_unsup_control_frames; + u_int32_t sf_rx_giants; + u_int32_t sf_rx_runts; + u_int32_t sf_rx_jabbererrs; + u_int32_t sf_rx_pkts_64; + u_int32_t sf_rx_pkts_65_127; + u_int32_t sf_rx_pkts_128_255; + u_int32_t sf_rx_pkts_256_511; + u_int32_t sf_rx_pkts_512_1023; + u_int32_t sf_rx_pkts_1024_1518; + u_int32_t sf_rx_frames_lost; + u_int16_t sf_tx_underruns; + u_int16_t sf_pad; +}; + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->sf_btag, sc->sf_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->sf_btag, sc->sf_bhandle, reg) + +#define CSR_READ_1(sc, reg) \ + bus_space_read_1(sc->sf_btag, sc->sf_bhandle, reg) + + +struct sf_type { + u_int16_t sf_vid; + u_int16_t sf_did; + char *sf_name; +}; + +#define SF_INC(x, y) (x) = (x + 1) % y + +#define ETHER_ALIGN 2 + +/* + * Note: alignment is important here: each list must be aligned to + * a 256-byte boundary. It turns out that each ring is some multiple + * of 4K in length, so we can stack them all on top of each other + * and just worry about aligning the whole mess. There's one transmit + * buffer ring and two receive buffer rings: one RX ring is for small + * packets and the other is for large packets. Each buffer ring also + * has a companion completion queue. + */ +struct sf_list_data { + struct sf_tx_bufdesc_type0 sf_tx_dlist[SF_TX_DLIST_CNT]; + struct sf_tx_cmpdesc_type1 sf_tx_clist[SF_TX_CLIST_CNT]; + struct sf_rx_bufdesc_type0 sf_rx_dlist_big[SF_RX_DLIST_CNT]; +#ifdef notdef + /* + * Unfortunately, because the Starfire doesn't allow arbitrary + * byte alignment, we have to copy packets in the RX handler in + * order to align the payload correctly. This means that we + * don't gain anything by having separate large and small descriptor + * lists, so for now we don't bother with the small one. + */ + struct sf_rx_bufdesc_type0 sf_rx_dlist_small[SF_RX_DLIST_CNT]; +#endif + struct sf_rx_cmpdesc_type3 sf_rx_clist[SF_RX_CLIST_CNT]; +}; + +struct sf_softc { + struct arpcom arpcom; /* interface info */ + bus_space_handle_t sf_bhandle; /* bus space handle */ + bus_space_tag_t sf_btag; /* bus space tag */ + void *sf_intrhand; /* interrupt handler cookie */ + struct resource *sf_irq; /* irq resource descriptor */ + struct resource *sf_res; /* mem/ioport resource */ + struct sf_type *sf_info; /* Starfire adapter info */ + device_t sf_miibus; + u_int8_t sf_unit; /* interface number */ + struct sf_list_data *sf_ldata; + int sf_tx_cnt; + u_int8_t sf_link; + int sf_if_flags; + struct callout_handle sf_stat_ch; + struct mtx sf_mtx; +}; + + +#define SF_LOCK(_sc) mtx_lock(&(_sc)->sf_mtx) +#define SF_UNLOCK(_sc) mtx_unlock(&(_sc)->sf_mtx) + +#define SF_TIMEOUT 1000 + +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) +#endif diff --git a/sys/pci/if_sis.c b/sys/pci/if_sis.c new file mode 100644 index 0000000..22a2163 --- /dev/null +++ b/sys/pci/if_sis.c @@ -0,0 +1,2085 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * SiS 900/SiS 7016 fast ethernet PCI NIC driver. Datasheets are + * available from http://www.sis.com.tw. + * + * This driver also supports the NatSemi DP83815. Datasheets are + * available from http://www.national.com. + * + * Written by Bill Paul <wpaul@ee.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * The SiS 900 is a fairly simple chip. It uses bus master DMA with + * simple TX and RX descriptors of 3 longwords in size. The receiver + * has a single perfect filter entry for the station address and a + * 128-bit multicast hash table. The SiS 900 has a built-in MII-based + * transceiver while the 7016 requires an external transceiver chip. + * Both chips offer the standard bit-bang MII interface as well as + * an enchanced PHY interface which simplifies accessing MII registers. + * + * The only downside to this chipset is that RX descriptors must be + * longword aligned. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/if_vlan_var.h> + +#include <net/bpf.h> + +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +#define SIS_USEIOSPACE + +#include <pci/if_sisreg.h> + +MODULE_DEPEND(sis, miibus, 1, 1, 1); + +/* "controller miibus0" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif + +/* + * Various supported device vendors/types and their names. + */ +static struct sis_type sis_devs[] = { + { SIS_VENDORID, SIS_DEVICEID_900, "SiS 900 10/100BaseTX" }, + { SIS_VENDORID, SIS_DEVICEID_7016, "SiS 7016 10/100BaseTX" }, + { NS_VENDORID, NS_DEVICEID_DP83815, "NatSemi DP83815 10/100BaseTX" }, + { 0, 0, NULL } +}; + +static int sis_probe (device_t); +static int sis_attach (device_t); +static int sis_detach (device_t); + +static int sis_newbuf (struct sis_softc *, + struct sis_desc *, struct mbuf *); +static int sis_encap (struct sis_softc *, + struct mbuf *, u_int32_t *); +static void sis_rxeof (struct sis_softc *); +static void sis_rxeoc (struct sis_softc *); +static void sis_txeof (struct sis_softc *); +static void sis_intr (void *); +static void sis_tick (void *); +static void sis_start (struct ifnet *); +static int sis_ioctl (struct ifnet *, u_long, caddr_t); +static void sis_init (void *); +static void sis_stop (struct sis_softc *); +static void sis_watchdog (struct ifnet *); +static void sis_shutdown (device_t); +static int sis_ifmedia_upd (struct ifnet *); +static void sis_ifmedia_sts (struct ifnet *, struct ifmediareq *); + +static u_int16_t sis_reverse (u_int16_t); +static void sis_delay (struct sis_softc *); +static void sis_eeprom_idle (struct sis_softc *); +static void sis_eeprom_putbyte (struct sis_softc *, int); +static void sis_eeprom_getword (struct sis_softc *, int, u_int16_t *); +static void sis_read_eeprom (struct sis_softc *, caddr_t, int, int, int); +#ifdef __i386__ +static void sis_read_cmos (struct sis_softc *, device_t, caddr_t, + int, int); +static void sis_read_mac (struct sis_softc *, device_t, caddr_t); +static device_t sis_find_bridge (device_t); +#endif + +static int sis_miibus_readreg (device_t, int, int); +static int sis_miibus_writereg (device_t, int, int, int); +static void sis_miibus_statchg (device_t); + +static void sis_setmulti_sis (struct sis_softc *); +static void sis_setmulti_ns (struct sis_softc *); +static u_int32_t sis_crc (struct sis_softc *, caddr_t); +static void sis_reset (struct sis_softc *); +static int sis_list_rx_init (struct sis_softc *); +static int sis_list_tx_init (struct sis_softc *); + +static void sis_dma_map_desc_ptr (void *, bus_dma_segment_t *, int, int); +static void sis_dma_map_desc_next (void *, bus_dma_segment_t *, int, int); +static void sis_dma_map_ring (void *, bus_dma_segment_t *, int, int); +#ifdef SIS_USEIOSPACE +#define SIS_RES SYS_RES_IOPORT +#define SIS_RID SIS_PCI_LOIO +#else +#define SIS_RES SYS_RES_MEMORY +#define SIS_RID SIS_PCI_LOMEM +#endif + +static device_method_t sis_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sis_probe), + DEVMETHOD(device_attach, sis_attach), + DEVMETHOD(device_detach, sis_detach), + DEVMETHOD(device_shutdown, sis_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, sis_miibus_readreg), + DEVMETHOD(miibus_writereg, sis_miibus_writereg), + DEVMETHOD(miibus_statchg, sis_miibus_statchg), + + { 0, 0 } +}; + +static driver_t sis_driver = { + "sis", + sis_methods, + sizeof(struct sis_softc) +}; + +static devclass_t sis_devclass; + +#ifdef __i386__ +static int sis_quick=1; +SYSCTL_INT(_hw, OID_AUTO, sis_quick, CTLFLAG_RW, + &sis_quick,0,"do not mdevget in sis driver"); +#endif + +DRIVER_MODULE(if_sis, pci, sis_driver, sis_devclass, 0, 0); +DRIVER_MODULE(miibus, sis, miibus_driver, miibus_devclass, 0, 0); + +#define SIS_SETBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, \ + CSR_READ_4(sc, reg) | (x)) + +#define SIS_CLRBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, \ + CSR_READ_4(sc, reg) & ~(x)) + +#define SIO_SET(x) \ + CSR_WRITE_4(sc, SIS_EECTL, CSR_READ_4(sc, SIS_EECTL) | x) + +#define SIO_CLR(x) \ + CSR_WRITE_4(sc, SIS_EECTL, CSR_READ_4(sc, SIS_EECTL) & ~x) + +static void +sis_dma_map_desc_next(arg, segs, nseg, error) + void *arg; + bus_dma_segment_t *segs; + int nseg, error; +{ + struct sis_desc *r; + + r = arg; + r->sis_next = segs->ds_addr; + + return; +} + +static void +sis_dma_map_desc_ptr(arg, segs, nseg, error) + void *arg; + bus_dma_segment_t *segs; + int nseg, error; +{ + struct sis_desc *r; + + r = arg; + r->sis_ptr = segs->ds_addr; + + return; +} + +static void +sis_dma_map_ring(arg, segs, nseg, error) + void *arg; + bus_dma_segment_t *segs; + int nseg, error; +{ + u_int32_t *p; + + p = arg; + *p = segs->ds_addr; + + return; +} + +/* + * Routine to reverse the bits in a word. Stolen almost + * verbatim from /usr/games/fortune. + */ +static u_int16_t sis_reverse(n) + u_int16_t n; +{ + n = ((n >> 1) & 0x5555) | ((n << 1) & 0xaaaa); + n = ((n >> 2) & 0x3333) | ((n << 2) & 0xcccc); + n = ((n >> 4) & 0x0f0f) | ((n << 4) & 0xf0f0); + n = ((n >> 8) & 0x00ff) | ((n << 8) & 0xff00); + + return(n); +} + +static void sis_delay(sc) + struct sis_softc *sc; +{ + int idx; + + for (idx = (300 / 33) + 1; idx > 0; idx--) + CSR_READ_4(sc, SIS_CSR); + + return; +} + +static void sis_eeprom_idle(sc) + struct sis_softc *sc; +{ + register int i; + + SIO_SET(SIS_EECTL_CSEL); + sis_delay(sc); + SIO_SET(SIS_EECTL_CLK); + sis_delay(sc); + + for (i = 0; i < 25; i++) { + SIO_CLR(SIS_EECTL_CLK); + sis_delay(sc); + SIO_SET(SIS_EECTL_CLK); + sis_delay(sc); + } + + SIO_CLR(SIS_EECTL_CLK); + sis_delay(sc); + SIO_CLR(SIS_EECTL_CSEL); + sis_delay(sc); + CSR_WRITE_4(sc, SIS_EECTL, 0x00000000); + + return; +} + +/* + * Send a read command and address to the EEPROM, check for ACK. + */ +static void sis_eeprom_putbyte(sc, addr) + struct sis_softc *sc; + int addr; +{ + register int d, i; + + d = addr | SIS_EECMD_READ; + + /* + * Feed in each bit and stobe the clock. + */ + for (i = 0x400; i; i >>= 1) { + if (d & i) { + SIO_SET(SIS_EECTL_DIN); + } else { + SIO_CLR(SIS_EECTL_DIN); + } + sis_delay(sc); + SIO_SET(SIS_EECTL_CLK); + sis_delay(sc); + SIO_CLR(SIS_EECTL_CLK); + sis_delay(sc); + } + + return; +} + +/* + * Read a word of data stored in the EEPROM at address 'addr.' + */ +static void sis_eeprom_getword(sc, addr, dest) + struct sis_softc *sc; + int addr; + u_int16_t *dest; +{ + register int i; + u_int16_t word = 0; + + /* Force EEPROM to idle state. */ + sis_eeprom_idle(sc); + + /* Enter EEPROM access mode. */ + sis_delay(sc); + SIO_CLR(SIS_EECTL_CLK); + sis_delay(sc); + SIO_SET(SIS_EECTL_CSEL); + sis_delay(sc); + + /* + * Send address of word we want to read. + */ + sis_eeprom_putbyte(sc, addr); + + /* + * Start reading bits from EEPROM. + */ + for (i = 0x8000; i; i >>= 1) { + SIO_SET(SIS_EECTL_CLK); + sis_delay(sc); + if (CSR_READ_4(sc, SIS_EECTL) & SIS_EECTL_DOUT) + word |= i; + sis_delay(sc); + SIO_CLR(SIS_EECTL_CLK); + sis_delay(sc); + } + + /* Turn off EEPROM access mode. */ + sis_eeprom_idle(sc); + + *dest = word; + + return; +} + +/* + * Read a sequence of words from the EEPROM. + */ +static void sis_read_eeprom(sc, dest, off, cnt, swap) + struct sis_softc *sc; + caddr_t dest; + int off; + int cnt; + int swap; +{ + int i; + u_int16_t word = 0, *ptr; + + for (i = 0; i < cnt; i++) { + sis_eeprom_getword(sc, off + i, &word); + ptr = (u_int16_t *)(dest + (i * 2)); + if (swap) + *ptr = ntohs(word); + else + *ptr = word; + } + + return; +} + +#ifdef __i386__ +static device_t sis_find_bridge(dev) + device_t dev; +{ + devclass_t pci_devclass; + device_t *pci_devices; + int pci_count = 0; + device_t *pci_children; + int pci_childcount = 0; + device_t *busp, *childp; + device_t child = NULL; + int i, j; + + if ((pci_devclass = devclass_find("pci")) == NULL) + return(NULL); + + devclass_get_devices(pci_devclass, &pci_devices, &pci_count); + + for (i = 0, busp = pci_devices; i < pci_count; i++, busp++) { + pci_childcount = 0; + device_get_children(*busp, &pci_children, &pci_childcount); + for (j = 0, childp = pci_children; + j < pci_childcount; j++, childp++) { + if (pci_get_vendor(*childp) == SIS_VENDORID && + pci_get_device(*childp) == 0x0008) { + child = *childp; + goto done; + } + } + } + +done: + free(pci_devices, M_TEMP); + free(pci_children, M_TEMP); + return(child); +} + +static void sis_read_cmos(sc, dev, dest, off, cnt) + struct sis_softc *sc; + device_t dev; + caddr_t dest; + int off; + int cnt; +{ + device_t bridge; + u_int8_t reg; + int i; + bus_space_tag_t btag; + + bridge = sis_find_bridge(dev); + if (bridge == NULL) + return; + reg = pci_read_config(bridge, 0x48, 1); + pci_write_config(bridge, 0x48, reg|0x40, 1); + + /* XXX */ + btag = I386_BUS_SPACE_IO; + + for (i = 0; i < cnt; i++) { + bus_space_write_1(btag, 0x0, 0x70, i + off); + *(dest + i) = bus_space_read_1(btag, 0x0, 0x71); + } + + pci_write_config(bridge, 0x48, reg & ~0x40, 1); + return; +} + +static void sis_read_mac(sc, dev, dest) + struct sis_softc *sc; + device_t dev; + caddr_t dest; +{ + u_int32_t filtsave, csrsave; + + filtsave = CSR_READ_4(sc, SIS_RXFILT_CTL); + csrsave = CSR_READ_4(sc, SIS_CSR); + + CSR_WRITE_4(sc, SIS_CSR, SIS_CSR_RELOAD | filtsave); + CSR_WRITE_4(sc, SIS_CSR, 0); + + CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave & ~SIS_RXFILTCTL_ENABLE); + + CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR0); + ((u_int16_t *)dest)[0] = CSR_READ_2(sc, SIS_RXFILT_DATA); + CSR_WRITE_4(sc, SIS_RXFILT_CTL,SIS_FILTADDR_PAR1); + ((u_int16_t *)dest)[1] = CSR_READ_2(sc, SIS_RXFILT_DATA); + CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR2); + ((u_int16_t *)dest)[2] = CSR_READ_2(sc, SIS_RXFILT_DATA); + + CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave); + CSR_WRITE_4(sc, SIS_CSR, csrsave); + return; +} +#endif + +static int sis_miibus_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct sis_softc *sc; + int i, val = 0; + + sc = device_get_softc(dev); + + if (sc->sis_type == SIS_TYPE_83815) { + if (phy != 0) + return(0); + /* + * The NatSemi chip can take a while after + * a reset to come ready, during which the BMSR + * returns a value of 0. This is *never* supposed + * to happen: some of the BMSR bits are meant to + * be hardwired in the on position, and this can + * confuse the miibus code a bit during the probe + * and attach phase. So we make an effort to check + * for this condition and wait for it to clear. + */ + if (!CSR_READ_4(sc, NS_BMSR)) + DELAY(1000); + val = CSR_READ_4(sc, NS_BMCR + (reg * 4)); + return(val); + } + + if (sc->sis_type == SIS_TYPE_900 && + sc->sis_rev < SIS_REV_635 && phy != 0) + return(0); + + CSR_WRITE_4(sc, SIS_PHYCTL, (phy << 11) | (reg << 6) | SIS_PHYOP_READ); + SIS_SETBIT(sc, SIS_PHYCTL, SIS_PHYCTL_ACCESS); + + for (i = 0; i < SIS_TIMEOUT; i++) { + if (!(CSR_READ_4(sc, SIS_PHYCTL) & SIS_PHYCTL_ACCESS)) + break; + } + + if (i == SIS_TIMEOUT) { + printf("sis%d: PHY failed to come ready\n", sc->sis_unit); + return(0); + } + + val = (CSR_READ_4(sc, SIS_PHYCTL) >> 16) & 0xFFFF; + + if (val == 0xFFFF) + return(0); + + return(val); +} + +static int sis_miibus_writereg(dev, phy, reg, data) + device_t dev; + int phy, reg, data; +{ + struct sis_softc *sc; + int i; + + sc = device_get_softc(dev); + + if (sc->sis_type == SIS_TYPE_83815) { + if (phy != 0) + return(0); + CSR_WRITE_4(sc, NS_BMCR + (reg * 4), data); + return(0); + } + + if (sc->sis_type == SIS_TYPE_900 && phy != 0) + return(0); + + CSR_WRITE_4(sc, SIS_PHYCTL, (data << 16) | (phy << 11) | + (reg << 6) | SIS_PHYOP_WRITE); + SIS_SETBIT(sc, SIS_PHYCTL, SIS_PHYCTL_ACCESS); + + for (i = 0; i < SIS_TIMEOUT; i++) { + if (!(CSR_READ_4(sc, SIS_PHYCTL) & SIS_PHYCTL_ACCESS)) + break; + } + + if (i == SIS_TIMEOUT) + printf("sis%d: PHY failed to come ready\n", sc->sis_unit); + + return(0); +} + +static void sis_miibus_statchg(dev) + device_t dev; +{ + struct sis_softc *sc; + + sc = device_get_softc(dev); + sis_init(sc); + + return; +} + +static u_int32_t sis_crc(sc, addr) + struct sis_softc *sc; + caddr_t addr; +{ + u_int32_t crc, carry; + int i, j; + u_int8_t c; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (i = 0; i < 6; i++) { + c = *(addr + i); + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); + crc <<= 1; + c >>= 1; + if (carry) + crc = (crc ^ 0x04c11db6) | carry; + } + } + + /* + * return the filter bit position + * + * The NatSemi chip has a 512-bit filter, which is + * different than the SiS, so we special-case it. + */ + if (sc->sis_type == SIS_TYPE_83815) + return((crc >> 23) & 0x1FF); + + return((crc >> 25) & 0x0000007F); +} + +static void sis_setmulti_ns(sc) + struct sis_softc *sc; +{ + struct ifnet *ifp; + struct ifmultiaddr *ifma; + u_int32_t h = 0, i, filtsave; + int bit, index; + + ifp = &sc->arpcom.ac_if; + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + SIS_CLRBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_MCHASH); + SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); + return; + } + + /* + * We have to explicitly enable the multicast hash table + * on the NatSemi chip if we want to use it, which we do. + */ + SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_MCHASH); + SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); + + filtsave = CSR_READ_4(sc, SIS_RXFILT_CTL); + + /* first, zot all the existing hash bits */ + for (i = 0; i < 32; i++) { + CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_FMEM_LO + (i*2)); + CSR_WRITE_4(sc, SIS_RXFILT_DATA, 0); + } + + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = sis_crc(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + index = h >> 3; + bit = h & 0x1F; + CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_FMEM_LO + index); + if (bit > 0xF) + bit -= 0x10; + SIS_SETBIT(sc, SIS_RXFILT_DATA, (1 << bit)); + } + + CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave); + + return; +} + +static void sis_setmulti_sis(sc) + struct sis_softc *sc; +{ + struct ifnet *ifp; + struct ifmultiaddr *ifma; + u_int32_t h = 0, i, filtsave; + + ifp = &sc->arpcom.ac_if; + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); + return; + } + + SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); + + filtsave = CSR_READ_4(sc, SIS_RXFILT_CTL); + + /* first, zot all the existing hash bits */ + for (i = 0; i < 8; i++) { + CSR_WRITE_4(sc, SIS_RXFILT_CTL, (4 + ((i * 16) >> 4)) << 16); + CSR_WRITE_4(sc, SIS_RXFILT_DATA, 0); + } + + /* now program new ones */ + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = sis_crc(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + CSR_WRITE_4(sc, SIS_RXFILT_CTL, (4 + (h >> 4)) << 16); + SIS_SETBIT(sc, SIS_RXFILT_DATA, (1 << (h & 0xF))); + } + + CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave); + + return; +} + +static void sis_reset(sc) + struct sis_softc *sc; +{ + register int i; + + SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RESET); + + for (i = 0; i < SIS_TIMEOUT; i++) { + if (!(CSR_READ_4(sc, SIS_CSR) & SIS_CSR_RESET)) + break; + } + + if (i == SIS_TIMEOUT) + printf("sis%d: reset never completed\n", sc->sis_unit); + + /* Wait a little while for the chip to get its brains in order. */ + DELAY(1000); + + /* + * If this is a NetSemi chip, make sure to clear + * PME mode. + */ + if (sc->sis_type == SIS_TYPE_83815) { + CSR_WRITE_4(sc, NS_CLKRUN, NS_CLKRUN_PMESTS); + CSR_WRITE_4(sc, NS_CLKRUN, 0); + } + + return; +} + +/* + * Probe for an SiS chip. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + */ +static int sis_probe(dev) + device_t dev; +{ + struct sis_type *t; + + t = sis_devs; + + while(t->sis_name != NULL) { + if ((pci_get_vendor(dev) == t->sis_vid) && + (pci_get_device(dev) == t->sis_did)) { + device_set_desc(dev, t->sis_name); + return(0); + } + t++; + } + + return(ENXIO); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int sis_attach(dev) + device_t dev; +{ + u_char eaddr[ETHER_ADDR_LEN]; + u_int32_t command; + struct sis_softc *sc; + struct ifnet *ifp; + int unit, error = 0, rid; + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + bzero(sc, sizeof(struct sis_softc)); + + mtx_init(&sc->sis_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF | MTX_RECURSE); + SIS_LOCK(sc); + + if (pci_get_device(dev) == SIS_DEVICEID_900) + sc->sis_type = SIS_TYPE_900; + if (pci_get_device(dev) == SIS_DEVICEID_7016) + sc->sis_type = SIS_TYPE_7016; + if (pci_get_vendor(dev) == NS_VENDORID) + sc->sis_type = SIS_TYPE_83815; + + sc->sis_rev = pci_read_config(dev, PCIR_REVID, 1); + + /* + * Handle power management nonsense. + */ + if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { + u_int32_t iobase, membase, irq; + + /* Save important PCI config data. */ + iobase = pci_read_config(dev, SIS_PCI_LOIO, 4); + membase = pci_read_config(dev, SIS_PCI_LOMEM, 4); + irq = pci_read_config(dev, SIS_PCI_INTLINE, 4); + + /* Reset the power state. */ + printf("sis%d: chip is in D%d power mode " + "-- setting to D0\n", unit, + pci_get_powerstate(dev)); + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + + /* Restore PCI config data. */ + pci_write_config(dev, SIS_PCI_LOIO, iobase, 4); + pci_write_config(dev, SIS_PCI_LOMEM, membase, 4); + pci_write_config(dev, SIS_PCI_INTLINE, irq, 4); + } + + /* + * Map control/status registers. + */ + pci_enable_busmaster(dev); + pci_enable_io(dev, SYS_RES_IOPORT); + pci_enable_io(dev, SYS_RES_MEMORY); + command = pci_read_config(dev, PCIR_COMMAND, 4); + +#ifdef SIS_USEIOSPACE + if (!(command & PCIM_CMD_PORTEN)) { + printf("sis%d: failed to enable I/O ports!\n", unit); + error = ENXIO;; + goto fail; + } +#else + if (!(command & PCIM_CMD_MEMEN)) { + printf("sis%d: failed to enable memory mapping!\n", unit); + error = ENXIO;; + goto fail; + } +#endif + + rid = SIS_RID; + sc->sis_res = bus_alloc_resource(dev, SIS_RES, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (sc->sis_res == NULL) { + printf("sis%d: couldn't map ports/memory\n", unit); + error = ENXIO; + goto fail; + } + + sc->sis_btag = rman_get_bustag(sc->sis_res); + sc->sis_bhandle = rman_get_bushandle(sc->sis_res); + + /* Allocate interrupt */ + rid = 0; + sc->sis_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->sis_irq == NULL) { + printf("sis%d: couldn't map interrupt\n", unit); + bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->sis_irq, INTR_TYPE_NET, + sis_intr, sc, &sc->sis_intrhand); + + if (error) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq); + bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); + printf("sis%d: couldn't set up irq\n", unit); + goto fail; + } + + /* Reset the adapter. */ + sis_reset(sc); + + /* + * Get station address from the EEPROM. + */ + switch (pci_get_vendor(dev)) { + case NS_VENDORID: + /* + * Reading the MAC address out of the EEPROM on + * the NatSemi chip takes a bit more work than + * you'd expect. The address spans 4 16-bit words, + * with the first word containing only a single bit. + * You have to shift everything over one bit to + * get it aligned properly. Also, the bits are + * stored backwards (the LSB is really the MSB, + * and so on) so you have to reverse them in order + * to get the MAC address into the form we want. + * Why? Who the hell knows. + */ + { + u_int16_t tmp[4]; + + sis_read_eeprom(sc, (caddr_t)&tmp, + NS_EE_NODEADDR, 4, 0); + + /* Shift everything over one bit. */ + tmp[3] = tmp[3] >> 1; + tmp[3] |= tmp[2] << 15; + tmp[2] = tmp[2] >> 1; + tmp[2] |= tmp[1] << 15; + tmp[1] = tmp[1] >> 1; + tmp[1] |= tmp[0] << 15; + + /* Now reverse all the bits. */ + tmp[3] = sis_reverse(tmp[3]); + tmp[2] = sis_reverse(tmp[2]); + tmp[1] = sis_reverse(tmp[1]); + + bcopy((char *)&tmp[1], eaddr, ETHER_ADDR_LEN); + } + break; + case SIS_VENDORID: + default: +#ifdef __i386__ + /* + * If this is a SiS 630E chipset with an embedded + * SiS 900 controller, we have to read the MAC address + * from the APC CMOS RAM. Our method for doing this + * is very ugly since we have to reach out and grab + * ahold of hardware for which we cannot properly + * allocate resources. This code is only compiled on + * the i386 architecture since the SiS 630E chipset + * is for x86 motherboards only. Note that there are + * a lot of magic numbers in this hack. These are + * taken from SiS's Linux driver. I'd like to replace + * them with proper symbolic definitions, but that + * requires some datasheets that I don't have access + * to at the moment. + */ + if (sc->sis_rev == SIS_REV_630S || + sc->sis_rev == SIS_REV_630E || + sc->sis_rev == SIS_REV_630EA1) + sis_read_cmos(sc, dev, (caddr_t)&eaddr, 0x9, 6); + + else if (sc->sis_rev == SIS_REV_635 || + sc->sis_rev == SIS_REV_630ET) + sis_read_mac(sc, dev, (caddr_t)&eaddr); + else +#endif + sis_read_eeprom(sc, (caddr_t)&eaddr, + SIS_EE_NODEADDR, 3, 0); + break; + } + + /* + * A SiS chip was detected. Inform the world. + */ + printf("sis%d: Ethernet address: %6D\n", unit, eaddr, ":"); + + sc->sis_unit = unit; + callout_handle_init(&sc->sis_stat_ch); + bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); + + /* + * Allocate the parent bus DMA tag appropriate for PCI. + */ +#define SIS_NSEG_NEW 32 + error = bus_dma_tag_create(NULL, /* parent */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MAXBSIZE, SIS_NSEG_NEW, /* maxsize, nsegments */ + BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + &sc->sis_parent_tag); + + /* + * Now allocate a tag for the DMA descriptor lists. + * All of our lists are allocated as a contiguous block + * of memory. + */ + error = bus_dma_tag_create(sc->sis_parent_tag, /* parent */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + SIS_RX_LIST_SZ, 1, /* maxsize,nsegments */ + BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ + 0, /* flags */ + &sc->sis_ldata.sis_rx_tag); + + error = bus_dma_tag_create(sc->sis_parent_tag, /* parent */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + SIS_TX_LIST_SZ, 1, /* maxsize,nsegments */ + BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ + 0, /* flags */ + &sc->sis_ldata.sis_tx_tag); + + error = bus_dma_tag_create(sc->sis_parent_tag, /* parent */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + SIS_TX_LIST_SZ, 1, /* maxsize,nsegments */ + BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ + 0, /* flags */ + &sc->sis_tag); + + /* + * Now allocate a chunk of DMA-able memory based on the + * tag we just created. + */ + error = bus_dmamem_alloc(sc->sis_ldata.sis_tx_tag, + (void **)&sc->sis_ldata.sis_tx_list, BUS_DMA_NOWAIT, + &sc->sis_ldata.sis_tx_dmamap); + + if (error) { + printf("sis%d: no memory for list buffers!\n", unit); + bus_teardown_intr(dev, sc->sis_irq, sc->sis_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq); + bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); + bus_dma_tag_destroy(sc->sis_ldata.sis_rx_tag); + bus_dma_tag_destroy(sc->sis_ldata.sis_tx_tag); + error = ENXIO; + goto fail; + } + + error = bus_dmamem_alloc(sc->sis_ldata.sis_rx_tag, + (void **)&sc->sis_ldata.sis_rx_list, BUS_DMA_NOWAIT, + &sc->sis_ldata.sis_rx_dmamap); + + if (error) { + printf("sis%d: no memory for list buffers!\n", unit); + bus_teardown_intr(dev, sc->sis_irq, sc->sis_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq); + bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); + bus_dmamem_free(sc->sis_ldata.sis_rx_tag, + sc->sis_ldata.sis_tx_list, sc->sis_ldata.sis_tx_dmamap); + bus_dma_tag_destroy(sc->sis_ldata.sis_rx_tag); + bus_dma_tag_destroy(sc->sis_ldata.sis_tx_tag); + error = ENXIO; + goto fail; + } + + + bzero(sc->sis_ldata.sis_tx_list, SIS_TX_LIST_SZ); + bzero(sc->sis_ldata.sis_rx_list, SIS_RX_LIST_SZ); + + /* + * Obtain the physical addresses of the RX and TX + * rings which we'll need later in the init routine. + */ + bus_dmamap_load(sc->sis_ldata.sis_tx_tag, + sc->sis_ldata.sis_tx_dmamap, &(sc->sis_ldata.sis_tx_list[0]), + sizeof(struct sis_desc), sis_dma_map_ring, + &sc->sis_cdata.sis_tx_paddr, 0); + bus_dmamap_load(sc->sis_ldata.sis_rx_tag, + sc->sis_ldata.sis_rx_dmamap, &(sc->sis_ldata.sis_rx_list[0]), + sizeof(struct sis_desc), sis_dma_map_ring, + &sc->sis_cdata.sis_rx_paddr, 0); + + ifp = &sc->arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = "sis"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = sis_ioctl; + ifp->if_output = ether_output; + ifp->if_start = sis_start; + ifp->if_watchdog = sis_watchdog; + ifp->if_init = sis_init; + ifp->if_baudrate = 10000000; + ifp->if_snd.ifq_maxlen = SIS_TX_LIST_CNT - 1; + + /* + * Do MII setup. + */ + if (mii_phy_probe(dev, &sc->sis_miibus, + sis_ifmedia_upd, sis_ifmedia_sts)) { + printf("sis%d: MII without any PHY!\n", sc->sis_unit); + bus_teardown_intr(dev, sc->sis_irq, sc->sis_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq); + bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); + bus_dmamem_free(sc->sis_ldata.sis_rx_tag, + sc->sis_ldata.sis_rx_list, sc->sis_ldata.sis_rx_dmamap); + bus_dmamem_free(sc->sis_ldata.sis_rx_tag, + sc->sis_ldata.sis_tx_list, sc->sis_ldata.sis_tx_dmamap); + bus_dma_tag_destroy(sc->sis_ldata.sis_rx_tag); + bus_dma_tag_destroy(sc->sis_ldata.sis_tx_tag); + error = ENXIO; + goto fail; + } + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); + + /* + * Tell the upper layer(s) we support long frames. + */ + ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); + + callout_handle_init(&sc->sis_stat_ch); + SIS_UNLOCK(sc); + return(0); + +fail: + SIS_UNLOCK(sc); + mtx_destroy(&sc->sis_mtx); + return(error); +} + +static int sis_detach(dev) + device_t dev; +{ + struct sis_softc *sc; + struct ifnet *ifp; + + + sc = device_get_softc(dev); + SIS_LOCK(sc); + ifp = &sc->arpcom.ac_if; + + sis_reset(sc); + sis_stop(sc); + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + + bus_generic_detach(dev); + device_delete_child(dev, sc->sis_miibus); + + bus_teardown_intr(dev, sc->sis_irq, sc->sis_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq); + bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); + + bus_dmamap_unload(sc->sis_ldata.sis_rx_tag, + sc->sis_ldata.sis_rx_dmamap); + bus_dmamap_unload(sc->sis_ldata.sis_tx_tag, + sc->sis_ldata.sis_tx_dmamap); + bus_dmamem_free(sc->sis_ldata.sis_rx_tag, + sc->sis_ldata.sis_rx_list, sc->sis_ldata.sis_rx_dmamap); + bus_dmamem_free(sc->sis_ldata.sis_rx_tag, + sc->sis_ldata.sis_tx_list, sc->sis_ldata.sis_tx_dmamap); + bus_dma_tag_destroy(sc->sis_ldata.sis_rx_tag); + bus_dma_tag_destroy(sc->sis_ldata.sis_tx_tag); + bus_dma_tag_destroy(sc->sis_parent_tag); + + SIS_UNLOCK(sc); + mtx_destroy(&sc->sis_mtx); + + return(0); +} + +/* + * Initialize the transmit descriptors. + */ +static int sis_list_tx_init(sc) + struct sis_softc *sc; +{ + struct sis_list_data *ld; + struct sis_ring_data *cd; + int i, nexti; + + cd = &sc->sis_cdata; + ld = &sc->sis_ldata; + + for (i = 0; i < SIS_TX_LIST_CNT; i++) { + nexti = (i == (SIS_TX_LIST_CNT - 1)) ? 0 : i+1; + ld->sis_tx_list[i].sis_nextdesc = + &ld->sis_tx_list[nexti]; + bus_dmamap_load(sc->sis_ldata.sis_tx_tag, + sc->sis_ldata.sis_tx_dmamap, + &ld->sis_tx_list[nexti], sizeof(struct sis_desc), + sis_dma_map_desc_next, &ld->sis_tx_list[i], 0); + ld->sis_tx_list[i].sis_mbuf = NULL; + ld->sis_tx_list[i].sis_ptr = 0; + ld->sis_tx_list[i].sis_ctl = 0; + } + + cd->sis_tx_prod = cd->sis_tx_cons = cd->sis_tx_cnt = 0; + + bus_dmamap_sync(sc->sis_ldata.sis_tx_tag, + sc->sis_ldata.sis_rx_dmamap, 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 sis_list_rx_init(sc) + struct sis_softc *sc; +{ + struct sis_list_data *ld; + struct sis_ring_data *cd; + int i,nexti; + + ld = &sc->sis_ldata; + cd = &sc->sis_cdata; + + for (i = 0; i < SIS_RX_LIST_CNT; i++) { + if (sis_newbuf(sc, &ld->sis_rx_list[i], NULL) == ENOBUFS) + return(ENOBUFS); + nexti = (i == (SIS_RX_LIST_CNT - 1)) ? 0 : i+1; + ld->sis_rx_list[i].sis_nextdesc = + &ld->sis_rx_list[nexti]; + bus_dmamap_load(sc->sis_ldata.sis_rx_tag, + sc->sis_ldata.sis_rx_dmamap, + &ld->sis_rx_list[nexti], + sizeof(struct sis_desc), sis_dma_map_desc_next, + &ld->sis_rx_list[i], 0); + } + + bus_dmamap_sync(sc->sis_ldata.sis_rx_tag, + sc->sis_ldata.sis_rx_dmamap, BUS_DMASYNC_PREWRITE); + + cd->sis_rx_prod = 0; + + return(0); +} + +/* + * Initialize an RX descriptor and attach an MBUF cluster. + */ +static int sis_newbuf(sc, c, m) + struct sis_softc *sc; + struct sis_desc *c; + struct mbuf *m; +{ + struct mbuf *m_new = NULL; + + if (c == NULL) + return(EINVAL); + + if (m == NULL) { + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(ENOBUFS); + + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + return(ENOBUFS); + } + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + } else { + m_new = m; + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + m_new->m_data = m_new->m_ext.ext_buf; + } + + m_adj(m_new, sizeof(u_int64_t)); + + c->sis_mbuf = m_new; + c->sis_ctl = SIS_RXLEN; + + bus_dmamap_create(sc->sis_tag, 0, &c->sis_map); + bus_dmamap_load(sc->sis_tag, c->sis_map, + mtod(m_new, void *), m_new->m_len, + sis_dma_map_desc_ptr, c, 0); + bus_dmamap_sync(sc->sis_tag, c->sis_map, BUS_DMASYNC_PREWRITE); + + return(0); +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +static void sis_rxeof(sc) + struct sis_softc *sc; +{ + struct ether_header *eh; + struct mbuf *m; + struct ifnet *ifp; + struct sis_desc *cur_rx; + int i, total_len = 0; + u_int32_t rxstat; + + ifp = &sc->arpcom.ac_if; + i = sc->sis_cdata.sis_rx_prod; + + while(SIS_OWNDESC(&sc->sis_ldata.sis_rx_list[i])) { + +#ifdef DEVICE_POLLING + if (ifp->if_ipending & IFF_POLLING) { + if (sc->rxcycles <= 0) + break; + sc->rxcycles--; + } +#endif /* DEVICE_POLLING */ + cur_rx = &sc->sis_ldata.sis_rx_list[i]; + rxstat = cur_rx->sis_rxstat; + bus_dmamap_sync(sc->sis_tag, + cur_rx->sis_map, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sis_tag, cur_rx->sis_map); + bus_dmamap_destroy(sc->sis_tag, cur_rx->sis_map); + m = cur_rx->sis_mbuf; + cur_rx->sis_mbuf = NULL; + total_len = SIS_RXBYTES(cur_rx); + SIS_INC(i, SIS_RX_LIST_CNT); + + /* + * If an error occurs, update stats, clear the + * status word and leave the mbuf cluster in place: + * it should simply get re-used next time this descriptor + * comes up in the ring. + */ + if (!(rxstat & SIS_CMDSTS_PKT_OK)) { + ifp->if_ierrors++; + if (rxstat & SIS_RXSTAT_COLL) + ifp->if_collisions++; + sis_newbuf(sc, cur_rx, m); + continue; + } + + /* No errors; receive the packet. */ +#ifdef __i386__ + /* + * On the x86 we do not have alignment problems, so try to + * allocate a new buffer for the receive ring, and pass up + * the one where the packet is already, saving the expensive + * copy done in m_devget(). + * If we are on an architecture with alignment problems, or + * if the allocation fails, then use m_devget and leave the + * existing buffer in the receive ring. + */ + if (sis_quick && sis_newbuf(sc, cur_rx, NULL) == 0) { + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = total_len; + } else +#endif + { + struct mbuf *m0; + m0 = m_devget(mtod(m, char *), total_len, + ETHER_ALIGN, ifp, NULL); + sis_newbuf(sc, cur_rx, m); + if (m0 == NULL) { + ifp->if_ierrors++; + continue; + } + m = m0; + } + + ifp->if_ipackets++; + eh = mtod(m, struct ether_header *); + + /* Remove header from mbuf and pass it on. */ + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, eh, m); + } + + sc->sis_cdata.sis_rx_prod = i; + + return; +} + +void sis_rxeoc(sc) + struct sis_softc *sc; +{ + sis_rxeof(sc); + sis_init(sc); + return; +} + +/* + * A frame was downloaded to the chip. It's safe for us to clean up + * the list buffers. + */ + +static void sis_txeof(sc) + struct sis_softc *sc; +{ + struct sis_desc *cur_tx = NULL; + struct ifnet *ifp; + u_int32_t idx; + + ifp = &sc->arpcom.ac_if; + + /* Clear the timeout timer. */ + ifp->if_timer = 0; + + /* + * Go through our tx list and free mbufs for those + * frames that have been transmitted. + */ + idx = sc->sis_cdata.sis_tx_cons; + while (idx != sc->sis_cdata.sis_tx_prod) { + cur_tx = &sc->sis_ldata.sis_tx_list[idx]; + + if (SIS_OWNDESC(cur_tx)) + break; + + if (cur_tx->sis_ctl & SIS_CMDSTS_MORE) { + sc->sis_cdata.sis_tx_cnt--; + SIS_INC(idx, SIS_TX_LIST_CNT); + continue; + } + + if (!(cur_tx->sis_ctl & SIS_CMDSTS_PKT_OK)) { + ifp->if_oerrors++; + if (cur_tx->sis_txstat & SIS_TXSTAT_EXCESSCOLLS) + ifp->if_collisions++; + if (cur_tx->sis_txstat & SIS_TXSTAT_OUTOFWINCOLL) + ifp->if_collisions++; + } + + ifp->if_collisions += + (cur_tx->sis_txstat & SIS_TXSTAT_COLLCNT) >> 16; + + ifp->if_opackets++; + if (cur_tx->sis_mbuf != NULL) { + m_freem(cur_tx->sis_mbuf); + cur_tx->sis_mbuf = NULL; + bus_dmamap_unload(sc->sis_tag, cur_tx->sis_map); + bus_dmamap_destroy(sc->sis_tag, cur_tx->sis_map); + } + + sc->sis_cdata.sis_tx_cnt--; + SIS_INC(idx, SIS_TX_LIST_CNT); + ifp->if_timer = 0; + } + + sc->sis_cdata.sis_tx_cons = idx; + + if (cur_tx != NULL) + ifp->if_flags &= ~IFF_OACTIVE; + + return; +} + +static void sis_tick(xsc) + void *xsc; +{ + struct sis_softc *sc; + struct mii_data *mii; + struct ifnet *ifp; + + sc = xsc; + SIS_LOCK(sc); + ifp = &sc->arpcom.ac_if; + + mii = device_get_softc(sc->sis_miibus); + mii_tick(mii); + + if (!sc->sis_link && mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->sis_link++; + if (ifp->if_snd.ifq_head != NULL) + sis_start(ifp); + } + + sc->sis_stat_ch = timeout(sis_tick, sc, hz); + + SIS_UNLOCK(sc); + + return; +} + +#ifdef DEVICE_POLLING +static poll_handler_t sis_poll; + +static void +sis_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + struct sis_softc *sc = ifp->if_softc; + + SIS_LOCK(sc); + if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */ + CSR_WRITE_4(sc, SIS_IER, 1); + goto done; + } + + /* + * On the sis, reading the status register also clears it. + * So before returning to intr mode we must make sure that all + * possible pending sources of interrupts have been served. + * In practice this means run to completion the *eof routines, + * and then call the interrupt routine + */ + sc->rxcycles = count; + sis_rxeof(sc); + sis_txeof(sc); + if (ifp->if_snd.ifq_head != NULL) + sis_start(ifp); + + if (sc->rxcycles > 0 || cmd == POLL_AND_CHECK_STATUS) { + u_int32_t status; + + /* Reading the ISR register clears all interrupts. */ + status = CSR_READ_4(sc, SIS_ISR); + + if (status & (SIS_ISR_RX_ERR|SIS_ISR_RX_OFLOW)) + sis_rxeoc(sc); + + if (status & (SIS_ISR_RX_IDLE)) + SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE); + + if (status & SIS_ISR_SYSERR) { + sis_reset(sc); + sis_init(sc); + } + } +done: + SIS_UNLOCK(sc); + return; +} +#endif /* DEVICE_POLLING */ + +static void sis_intr(arg) + void *arg; +{ + struct sis_softc *sc; + struct ifnet *ifp; + u_int32_t status; + + sc = arg; + ifp = &sc->arpcom.ac_if; + + SIS_LOCK(sc); +#ifdef DEVICE_POLLING + if (ifp->if_ipending & IFF_POLLING) + goto done; + if (ether_poll_register(sis_poll, ifp)) { /* ok, disable interrupts */ + CSR_WRITE_4(sc, SIS_IER, 0); + goto done; + } +#endif /* DEVICE_POLLING */ + + /* Supress unwanted interrupts */ + if (!(ifp->if_flags & IFF_UP)) { + sis_stop(sc); + goto done; + } + + /* Disable interrupts. */ + CSR_WRITE_4(sc, SIS_IER, 0); + + for (;;) { + /* Reading the ISR register clears all interrupts. */ + status = CSR_READ_4(sc, SIS_ISR); + + if ((status & SIS_INTRS) == 0) + break; + + if (status & + (SIS_ISR_TX_DESC_OK | SIS_ISR_TX_ERR | + SIS_ISR_TX_OK | SIS_ISR_TX_IDLE) ) + sis_txeof(sc); + + if (status & (SIS_ISR_RX_DESC_OK|SIS_ISR_RX_OK|SIS_ISR_RX_IDLE)) + sis_rxeof(sc); + + if (status & (SIS_ISR_RX_ERR | SIS_ISR_RX_OFLOW)) + sis_rxeoc(sc); + + if (status & (SIS_ISR_RX_IDLE)) + SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE); + + if (status & SIS_ISR_SYSERR) { + sis_reset(sc); + sis_init(sc); + } + } + + /* Re-enable interrupts. */ + CSR_WRITE_4(sc, SIS_IER, 1); + + if (ifp->if_snd.ifq_head != NULL) + sis_start(ifp); +done: + SIS_UNLOCK(sc); + + return; +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int sis_encap(sc, m_head, txidx) + struct sis_softc *sc; + struct mbuf *m_head; + u_int32_t *txidx; +{ + struct sis_desc *f = NULL; + struct mbuf *m; + int frag, cur, cnt = 0; + + /* + * Start packing the mbufs in this chain into + * the fragment pointers. Stop when we run out + * of fragments or hit the end of the mbuf chain. + */ + m = m_head; + cur = frag = *txidx; + + for (m = m_head; m != NULL; m = m->m_next) { + if (m->m_len != 0) { + if ((SIS_TX_LIST_CNT - + (sc->sis_cdata.sis_tx_cnt + cnt)) < 2) + return(ENOBUFS); + f = &sc->sis_ldata.sis_tx_list[frag]; + f->sis_ctl = SIS_CMDSTS_MORE | m->m_len; + bus_dmamap_create(sc->sis_tag, 0, &f->sis_map); + bus_dmamap_load(sc->sis_tag, f->sis_map, + mtod(m, void *), m->m_len, + sis_dma_map_desc_ptr, f, 0); + bus_dmamap_sync(sc->sis_tag, + f->sis_map, BUS_DMASYNC_PREREAD); + if (cnt != 0) + f->sis_ctl |= SIS_CMDSTS_OWN; + cur = frag; + SIS_INC(frag, SIS_TX_LIST_CNT); + cnt++; + } + } + + if (m != NULL) + return(ENOBUFS); + + sc->sis_ldata.sis_tx_list[cur].sis_mbuf = m_head; + sc->sis_ldata.sis_tx_list[cur].sis_ctl &= ~SIS_CMDSTS_MORE; + sc->sis_ldata.sis_tx_list[*txidx].sis_ctl |= SIS_CMDSTS_OWN; + sc->sis_cdata.sis_tx_cnt += cnt; + *txidx = frag; + + return(0); +} + +/* + * Main transmit routine. To avoid having to do mbuf copies, we put pointers + * to the mbuf data regions directly in the transmit lists. We also save a + * copy of the pointers since the transmit list fragment pointers are + * physical addresses. + */ + +static void sis_start(ifp) + struct ifnet *ifp; +{ + struct sis_softc *sc; + struct mbuf *m_head = NULL; + u_int32_t idx; + + sc = ifp->if_softc; + SIS_LOCK(sc); + + if (!sc->sis_link) { + SIS_UNLOCK(sc); + return; + } + + idx = sc->sis_cdata.sis_tx_prod; + + if (ifp->if_flags & IFF_OACTIVE) { + SIS_UNLOCK(sc); + return; + } + + while(sc->sis_ldata.sis_tx_list[idx].sis_mbuf == NULL) { + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + if (sis_encap(sc, m_head, &idx)) { + IF_PREPEND(&ifp->if_snd, m_head); + ifp->if_flags |= IFF_OACTIVE; + break; + } + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, m_head); + + } + + /* Transmit */ + sc->sis_cdata.sis_tx_prod = idx; + SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_ENABLE); + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + + SIS_UNLOCK(sc); + + return; +} + +static void sis_init(xsc) + void *xsc; +{ + struct sis_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mii_data *mii; + + SIS_LOCK(sc); + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + sis_stop(sc); + + mii = device_get_softc(sc->sis_miibus); + + /* Set MAC address */ + if (sc->sis_type == SIS_TYPE_83815) { + CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR0); + CSR_WRITE_4(sc, SIS_RXFILT_DATA, + ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); + CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR1); + CSR_WRITE_4(sc, SIS_RXFILT_DATA, + ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); + CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR2); + CSR_WRITE_4(sc, SIS_RXFILT_DATA, + ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); + } else { + CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR0); + CSR_WRITE_4(sc, SIS_RXFILT_DATA, + ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); + CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR1); + CSR_WRITE_4(sc, SIS_RXFILT_DATA, + ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); + CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR2); + CSR_WRITE_4(sc, SIS_RXFILT_DATA, + ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); + } + + /* Init circular RX list. */ + if (sis_list_rx_init(sc) == ENOBUFS) { + printf("sis%d: initialization failed: no " + "memory for rx buffers\n", sc->sis_unit); + sis_stop(sc); + SIS_UNLOCK(sc); + return; + } + + /* + * Init tx descriptors. + */ + sis_list_tx_init(sc); + + /* + * For the NatSemi chip, we have to explicitly enable the + * reception of ARP frames, as well as turn on the 'perfect + * match' filter where we store the station address, otherwise + * we won't receive unicasts meant for this host. + */ + if (sc->sis_type == SIS_TYPE_83815) { + SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_ARP); + SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_PERFECT); + } + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) { + SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLPHYS); + } else { + SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLPHYS); + } + + /* + * Set the capture broadcast bit to capture broadcast frames. + */ + if (ifp->if_flags & IFF_BROADCAST) { + SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_BROAD); + } else { + SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_BROAD); + } + + /* + * Load the multicast filter. + */ + if (sc->sis_type == SIS_TYPE_83815) + sis_setmulti_ns(sc); + else + sis_setmulti_sis(sc); + + /* Turn the receive filter on */ + SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ENABLE); + + /* + * Load the address of the RX and TX lists. + */ + CSR_WRITE_4(sc, SIS_RX_LISTPTR, sc->sis_cdata.sis_rx_paddr); + CSR_WRITE_4(sc, SIS_TX_LISTPTR, sc->sis_cdata.sis_tx_paddr); + + /* Set RX configuration */ + CSR_WRITE_4(sc, SIS_RX_CFG, SIS_RXCFG); + + /* Accept Long Packets for VLAN support */ + SIS_SETBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_JABBER); + + /* Set TX configuration */ + if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) { + CSR_WRITE_4(sc, SIS_TX_CFG, SIS_TXCFG_10); + } else { + CSR_WRITE_4(sc, SIS_TX_CFG, SIS_TXCFG_100); + } + + /* Set full/half duplex mode. */ + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { + SIS_SETBIT(sc, SIS_TX_CFG, + (SIS_TXCFG_IGN_HBEAT|SIS_TXCFG_IGN_CARR)); + SIS_SETBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_TXPKTS); + } else { + SIS_CLRBIT(sc, SIS_TX_CFG, + (SIS_TXCFG_IGN_HBEAT|SIS_TXCFG_IGN_CARR)); + SIS_CLRBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_TXPKTS); + } + + /* + * Enable interrupts. + */ + CSR_WRITE_4(sc, SIS_IMR, SIS_INTRS); +#ifdef DEVICE_POLLING + /* + * ... only enable interrupts if we are not polling, make sure + * they are off otherwise. + */ + if (ifp->if_ipending & IFF_POLLING) + CSR_WRITE_4(sc, SIS_IER, 0); + else +#endif /* DEVICE_POLLING */ + CSR_WRITE_4(sc, SIS_IER, 1); + + /* Enable receiver and transmitter. */ + SIS_CLRBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE|SIS_CSR_RX_DISABLE); + SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE); + +#ifdef notdef + mii_mediachg(mii); +#endif + + /* + * Page 75 of the DP83815 manual recommends the + * following register settings "for optimum + * performance." Note however that at least three + * of the registers are listed as "reserved" in + * the register map, so who knows what they do. + */ + if (sc->sis_type == SIS_TYPE_83815) { + CSR_WRITE_4(sc, NS_PHY_PAGE, 0x0001); + CSR_WRITE_4(sc, NS_PHY_CR, 0x189C); + CSR_WRITE_4(sc, NS_PHY_TDATA, 0x0000); + CSR_WRITE_4(sc, NS_PHY_DSPCFG, 0x5040); + CSR_WRITE_4(sc, NS_PHY_SDCFG, 0x008C); + } + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + sc->sis_stat_ch = timeout(sis_tick, sc, hz); + + SIS_UNLOCK(sc); + + return; +} + +/* + * Set media options. + */ +static int sis_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct sis_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + + mii = device_get_softc(sc->sis_miibus); + sc->sis_link = 0; + if (mii->mii_instance) { + struct mii_softc *miisc; + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + mii_phy_reset(miisc); + } + mii_mediachg(mii); + + return(0); +} + +/* + * Report current media status. + */ +static void sis_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct sis_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + + mii = device_get_softc(sc->sis_miibus); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + + return; +} + +static int sis_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct sis_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + struct mii_data *mii; + int error = 0; + + switch(command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + sis_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) + sis_stop(sc); + } + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + SIS_LOCK(sc); + if (sc->sis_type == SIS_TYPE_83815) + sis_setmulti_ns(sc); + else + sis_setmulti_sis(sc); + SIS_UNLOCK(sc); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = device_get_softc(sc->sis_miibus); + SIS_LOCK(sc); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + SIS_UNLOCK(sc); + break; + default: + error = EINVAL; + break; + } + + return(error); +} + +static void sis_watchdog(ifp) + struct ifnet *ifp; +{ + struct sis_softc *sc; + + sc = ifp->if_softc; + + SIS_LOCK(sc); + + ifp->if_oerrors++; + printf("sis%d: watchdog timeout\n", sc->sis_unit); + + sis_stop(sc); + sis_reset(sc); + sis_init(sc); + + if (ifp->if_snd.ifq_head != NULL) + sis_start(ifp); + + SIS_UNLOCK(sc); + + return; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void sis_stop(sc) + struct sis_softc *sc; +{ + register int i; + struct ifnet *ifp; + + SIS_LOCK(sc); + ifp = &sc->arpcom.ac_if; + ifp->if_timer = 0; + + untimeout(sis_tick, sc, sc->sis_stat_ch); + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); +#ifdef DEVICE_POLLING + ether_poll_deregister(ifp); +#endif + CSR_WRITE_4(sc, SIS_IER, 0); + CSR_WRITE_4(sc, SIS_IMR, 0); + SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE|SIS_CSR_RX_DISABLE); + DELAY(1000); + CSR_WRITE_4(sc, SIS_TX_LISTPTR, 0); + CSR_WRITE_4(sc, SIS_RX_LISTPTR, 0); + + sc->sis_link = 0; + + /* + * Free data in the RX lists. + */ + for (i = 0; i < SIS_RX_LIST_CNT; i++) { + if (sc->sis_ldata.sis_rx_list[i].sis_mbuf != NULL) { + bus_dmamap_unload(sc->sis_tag, + sc->sis_ldata.sis_rx_list[i].sis_map); + bus_dmamap_destroy(sc->sis_tag, + sc->sis_ldata.sis_rx_list[i].sis_map); + m_freem(sc->sis_ldata.sis_rx_list[i].sis_mbuf); + sc->sis_ldata.sis_rx_list[i].sis_mbuf = NULL; + } + } + bzero(sc->sis_ldata.sis_rx_list, + sizeof(sc->sis_ldata.sis_rx_list)); + + /* + * Free the TX list buffers. + */ + for (i = 0; i < SIS_TX_LIST_CNT; i++) { + if (sc->sis_ldata.sis_tx_list[i].sis_mbuf != NULL) { + bus_dmamap_unload(sc->sis_tag, + sc->sis_ldata.sis_tx_list[i].sis_map); + bus_dmamap_destroy(sc->sis_tag, + sc->sis_ldata.sis_tx_list[i].sis_map); + m_freem(sc->sis_ldata.sis_tx_list[i].sis_mbuf); + sc->sis_ldata.sis_tx_list[i].sis_mbuf = NULL; + } + } + + bzero(sc->sis_ldata.sis_tx_list, + sizeof(sc->sis_ldata.sis_tx_list)); + + SIS_UNLOCK(sc); + + return; +} + +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static void sis_shutdown(dev) + device_t dev; +{ + struct sis_softc *sc; + + sc = device_get_softc(dev); + SIS_LOCK(sc); + sis_reset(sc); + sis_stop(sc); + SIS_UNLOCK(sc); + + return; +} diff --git a/sys/pci/if_sisreg.h b/sys/pci/if_sisreg.h new file mode 100644 index 0000000..8244f2a --- /dev/null +++ b/sys/pci/if_sisreg.h @@ -0,0 +1,488 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ee.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * Register definitions for the SiS 900 and SiS 7016 chipsets. The + * 7016 is actually an older chip and some of its registers differ + * from the 900, however the core operational registers are the same: + * the differences lie in the OnNow/Wake on LAN stuff which we don't + * use anyway. The 7016 needs an external MII compliant PHY while the + * SiS 900 has one built in. All registers are 32-bits wide. + */ + +/* Registers common to SiS 900 and SiS 7016 */ +#define SIS_CSR 0x00 +#define SIS_CFG 0x04 +#define SIS_EECTL 0x08 +#define SIS_PCICTL 0x0C +#define SIS_ISR 0x10 +#define SIS_IMR 0x14 +#define SIS_IER 0x18 +#define SIS_PHYCTL 0x1C +#define SIS_TX_LISTPTR 0x20 +#define SIS_TX_CFG 0x24 +#define SIS_RX_LISTPTR 0x30 +#define SIS_RX_CFG 0x34 +#define SIS_FLOWCTL 0x38 +#define SIS_RXFILT_CTL 0x48 +#define SIS_RXFILT_DATA 0x4C +#define SIS_PWRMAN_CTL 0xB0 +#define SIS_PWERMAN_WKUP_EVENT 0xB4 +#define SIS_WKUP_FRAME_CRC 0xBC +#define SIS_WKUP_FRAME_MASK0 0xC0 +#define SIS_WKUP_FRAME_MASKXX 0xEC + +/* SiS 7016 specific registers */ +#define SIS_SILICON_REV 0x5C +#define SIS_MIB_CTL0 0x60 +#define SIS_MIB_CTL1 0x64 +#define SIS_MIB_CTL2 0x68 +#define SIS_MIB_CTL3 0x6C +#define SIS_MIB 0x80 +#define SIS_LINKSTS 0xA0 +#define SIS_TIMEUNIT 0xA4 +#define SIS_GPIO 0xB8 + +/* NS DP83815 registers */ +#define NS_CLKRUN 0x3C +#define NS_BMCR 0x80 +#define NS_BMSR 0x84 +#define NS_PHYIDR1 0x88 +#define NS_PHYIDR2 0x8C +#define NS_ANAR 0x90 +#define NS_ANLPAR 0x94 +#define NS_ANER 0x98 +#define NS_ANNPTR 0x9C + +#define NS_PHY_CR 0xE4 +#define NS_PHY_10BTSCR 0xE8 +#define NS_PHY_PAGE 0xCC +#define NS_PHY_EXTCFG 0xF0 +#define NS_PHY_DSPCFG 0xF4 +#define NS_PHY_SDCFG 0xF8 +#define NS_PHY_TDATA 0xFC + +#define NS_CLKRUN_PMESTS 0x00008000 +#define NS_CLKRUN_PMEENB 0x00000100 +#define NS_CLNRUN_CLKRUN_ENB 0x00000001 + +#define SIS_CSR_TX_ENABLE 0x00000001 +#define SIS_CSR_TX_DISABLE 0x00000002 +#define SIS_CSR_RX_ENABLE 0x00000004 +#define SIS_CSR_RX_DISABLE 0x00000008 +#define SIS_CSR_TX_RESET 0x00000010 +#define SIS_CSR_RX_RESET 0x00000020 +#define SIS_CSR_SOFTINTR 0x00000080 +#define SIS_CSR_RESET 0x00000100 +#define SIS_CSR_ACCESS_MODE 0x00000200 +#define SIS_CSR_RELOAD 0x00000400 + +#define SIS_CFG_BIGENDIAN 0x00000001 +#define SIS_CFG_PERR_DETECT 0x00000008 +#define SIS_CFG_DEFER_DISABLE 0x00000010 +#define SIS_CFG_OUTOFWIN_TIMER 0x00000020 +#define SIS_CFG_SINGLE_BACKOFF 0x00000040 +#define SIS_CFG_PCIREQ_ALG 0x00000080 + +#define SIS_EECTL_DIN 0x00000001 +#define SIS_EECTL_DOUT 0x00000002 +#define SIS_EECTL_CLK 0x00000004 +#define SIS_EECTL_CSEL 0x00000008 + +#define SIS_EECMD_WRITE 0x140 +#define SIS_EECMD_READ 0x180 +#define SIS_EECMD_ERASE 0x1c0 + +#define SIS_EE_NODEADDR 0x8 +#define NS_EE_NODEADDR 0x6 + +#define SIS_PCICTL_SRAMADDR 0x0000001F +#define SIS_PCICTL_RAMTSTENB 0x00000020 +#define SIS_PCICTL_TXTSTENB 0x00000040 +#define SIS_PCICTL_RXTSTENB 0x00000080 +#define SIS_PCICTL_BMTSTENB 0x00000200 +#define SIS_PCICTL_RAMADDR 0x001F0000 +#define SIS_PCICTL_ROMTIME 0x0F000000 +#define SIS_PCICTL_DISCTEST 0x40000000 + +#define SIS_ISR_RX_OK 0x00000001 +#define SIS_ISR_RX_DESC_OK 0x00000002 +#define SIS_ISR_RX_ERR 0x00000004 +#define SIS_ISR_RX_EARLY 0x00000008 +#define SIS_ISR_RX_IDLE 0x00000010 +#define SIS_ISR_RX_OFLOW 0x00000020 +#define SIS_ISR_TX_OK 0x00000040 +#define SIS_ISR_TX_DESC_OK 0x00000080 +#define SIS_ISR_TX_ERR 0x00000100 +#define SIS_ISR_TX_IDLE 0x00000200 +#define SIS_ISR_TX_UFLOW 0x00000400 +#define SIS_ISR_SOFTINTR 0x00000800 +#define SIS_ISR_HIBITS 0x00008000 +#define SIS_ISR_RX_FIFO_OFLOW 0x00010000 +#define SIS_ISR_TGT_ABRT 0x00100000 +#define SIS_ISR_BM_ABRT 0x00200000 +#define SIS_ISR_SYSERR 0x00400000 +#define SIS_ISR_PARITY_ERR 0x00800000 +#define SIS_ISR_RX_RESET_DONE 0x01000000 +#define SIS_ISR_TX_RESET_DONE 0x02000000 +#define SIS_ISR_TX_PAUSE_START 0x04000000 +#define SIS_ISR_TX_PAUSE_DONE 0x08000000 +#define SIS_ISR_WAKE_EVENT 0x10000000 + +#define SIS_IMR_RX_OK 0x00000001 +#define SIS_IMR_RX_DESC_OK 0x00000002 +#define SIS_IMR_RX_ERR 0x00000004 +#define SIS_IMR_RX_EARLY 0x00000008 +#define SIS_IMR_RX_IDLE 0x00000010 +#define SIS_IMR_RX_OFLOW 0x00000020 +#define SIS_IMR_TX_OK 0x00000040 +#define SIS_IMR_TX_DESC_OK 0x00000080 +#define SIS_IMR_TX_ERR 0x00000100 +#define SIS_IMR_TX_IDLE 0x00000200 +#define SIS_IMR_TX_UFLOW 0x00000400 +#define SIS_IMR_SOFTINTR 0x00000800 +#define SIS_IMR_HIBITS 0x00008000 +#define SIS_IMR_RX_FIFO_OFLOW 0x00010000 +#define SIS_IMR_TGT_ABRT 0x00100000 +#define SIS_IMR_BM_ABRT 0x00200000 +#define SIS_IMR_SYSERR 0x00400000 +#define SIS_IMR_PARITY_ERR 0x00800000 +#define SIS_IMR_RX_RESET_DONE 0x01000000 +#define SIS_IMR_TX_RESET_DONE 0x02000000 +#define SIS_IMR_TX_PAUSE_START 0x04000000 +#define SIS_IMR_TX_PAUSE_DONE 0x08000000 +#define SIS_IMR_WAKE_EVENT 0x10000000 + +#define SIS_INTRS \ + (SIS_IMR_RX_OFLOW|SIS_IMR_TX_UFLOW|SIS_IMR_TX_OK|\ + SIS_IMR_TX_IDLE|SIS_IMR_RX_OK|SIS_IMR_RX_ERR|\ + SIS_IMR_RX_IDLE|\ + SIS_IMR_SYSERR) + +#define SIS_IER_INTRENB 0x00000001 + +#define SIS_PHYCTL_ACCESS 0x00000010 +#define SIS_PHYCTL_OP 0x00000020 +#define SIS_PHYCTL_REGADDR 0x000007C0 +#define SIS_PHYCTL_PHYADDR 0x0000F800 +#define SIS_PHYCTL_PHYDATA 0xFFFF0000 + +#define SIS_PHYOP_READ 0x00000020 +#define SIS_PHYOP_WRITE 0x00000000 + +#define SIS_TXCFG_DRAIN_THRESH 0x0000003F /* 32-byte units */ +#define SIS_TXCFG_FILL_THRESH 0x00003F00 /* 32-byte units */ +#define SIS_TXCFG_DMABURST 0x00700000 +#define SIS_TXCFG_AUTOPAD 0x10000000 +#define SIS_TXCFG_LOOPBK 0x20000000 +#define SIS_TXCFG_IGN_HBEAT 0x40000000 +#define SIS_TXCFG_IGN_CARR 0x80000000 + +#define SIS_TXCFG_DRAIN(x) (((x) >> 5) & SIS_TXCFG_DRAIN_THRESH) +#define SIS_TXCFG_FILL(x) ((((x) >> 5) << 8) & SIS_TXCFG_FILL_THRESH) + +#define SIS_TXDMA_512BYTES 0x00000000 +#define SIS_TXDMA_4BYTES 0x00100000 +#define SIS_TXDMA_8BYTES 0x00200000 +#define SIS_TXDMA_16BYTES 0x00300000 +#define SIS_TXDMA_32BYTES 0x00400000 +#define SIS_TXDMA_64BYTES 0x00500000 +#define SIS_TXDMA_128BYTES 0x00600000 +#define SIS_TXDMA_256BYTES 0x00700000 + +#define SIS_TXCFG_100 \ + (SIS_TXDMA_64BYTES|SIS_TXCFG_AUTOPAD|\ + SIS_TXCFG_FILL(64)|SIS_TXCFG_DRAIN(1536)) + +#define SIS_TXCFG_10 \ + (SIS_TXDMA_32BYTES|SIS_TXCFG_AUTOPAD|\ + SIS_TXCFG_FILL(64)|SIS_TXCFG_DRAIN(1536)) + +#define SIS_RXCFG_DRAIN_THRESH 0x0000003E /* 8-byte units */ +#define SIS_RXCFG_DMABURST 0x00700000 +#define SIS_RXCFG_RX_JABBER 0x08000000 +#define SIS_RXCFG_RX_TXPKTS 0x10000000 +#define SIS_RXCFG_RX_RUNTS 0x40000000 +#define SIS_RXCFG_RX_GIANTS 0x80000000 + +#define SIS_RXCFG_DRAIN(x) ((((x) >> 3) << 1) & SIS_RXCFG_DRAIN_THRESH) + +#define SIS_RXDMA_512BYTES 0x00000000 +#define SIS_RXDMA_4BYTES 0x00100000 +#define SIS_RXDMA_8BYTES 0x00200000 +#define SIS_RXDMA_16BYTES 0x00300000 +#define SIS_RXDMA_32BYTES 0x00400000 +#define SIS_RXDMA_64BYTES 0x00500000 +#define SIS_RXDMA_128BYTES 0x00600000 +#define SIS_RXDMA_256BYTES 0x00700000 + +#define SIS_RXCFG \ + (SIS_RXCFG_DRAIN(64)|SIS_RXDMA_256BYTES) + +#define SIS_RXFILTCTL_ADDR 0x000F0000 +#define NS_RXFILTCTL_MCHASH 0x00200000 +#define NS_RXFILTCTL_ARP 0x00400000 +#define NS_RXFILTCTL_PERFECT 0x08000000 +#define SIS_RXFILTCTL_ALLPHYS 0x10000000 +#define SIS_RXFILTCTL_ALLMULTI 0x20000000 +#define SIS_RXFILTCTL_BROAD 0x40000000 +#define SIS_RXFILTCTL_ENABLE 0x80000000 + +#define SIS_FILTADDR_PAR0 0x00000000 +#define SIS_FILTADDR_PAR1 0x00010000 +#define SIS_FILTADDR_PAR2 0x00020000 +#define SIS_FILTADDR_MAR0 0x00040000 +#define SIS_FILTADDR_MAR1 0x00050000 +#define SIS_FILTADDR_MAR2 0x00060000 +#define SIS_FILTADDR_MAR3 0x00070000 +#define SIS_FILTADDR_MAR4 0x00080000 +#define SIS_FILTADDR_MAR5 0x00090000 +#define SIS_FILTADDR_MAR6 0x000A0000 +#define SIS_FILTADDR_MAR7 0x000B0000 + +#define NS_FILTADDR_PAR0 0x00000000 +#define NS_FILTADDR_PAR1 0x00000002 +#define NS_FILTADDR_PAR2 0x00000004 + +#define NS_FILTADDR_FMEM_LO 0x00000200 +#define NS_FILTADDR_FMEM_HI 0x000003FE + +/* + * DMA descriptor structures. The first part of the descriptor + * is the hardware descriptor format, which is just three longwords. + * After this, we include some additional structure members for + * use by the driver. Note that for this structure will be a different + * size on the alpha, but that's okay as long as it's a multiple of 4 + * bytes in size. + */ +struct sis_desc { + /* SiS hardware descriptor section */ + u_int32_t sis_next; + u_int32_t sis_cmdsts; +#define sis_rxstat sis_cmdsts +#define sis_txstat sis_cmdsts +#define sis_ctl sis_cmdsts + u_int32_t sis_ptr; + /* Driver software section */ + struct mbuf *sis_mbuf; + struct sis_desc *sis_nextdesc; + bus_dmamap_t sis_map; +}; + +#define SIS_CMDSTS_BUFLEN 0x00000FFF +#define SIS_CMDSTS_PKT_OK 0x08000000 +#define SIS_CMDSTS_CRC 0x10000000 +#define SIS_CMDSTS_INTR 0x20000000 +#define SIS_CMDSTS_MORE 0x40000000 +#define SIS_CMDSTS_OWN 0x80000000 + +#define SIS_LASTDESC(x) (!((x)->sis_ctl & SIS_CMDSTS_MORE))) +#define SIS_OWNDESC(x) ((x)->sis_ctl & SIS_CMDSTS_OWN) +#define SIS_INC(x, y) { if (++(x) == y) x = 0; } +#define SIS_RXBYTES(x) (((x)->sis_ctl & SIS_CMDSTS_BUFLEN) - ETHER_CRC_LEN) + +#define SIS_RXSTAT_COLL 0x00010000 +#define SIS_RXSTAT_LOOPBK 0x00020000 +#define SIS_RXSTAT_ALIGNERR 0x00040000 +#define SIS_RXSTAT_CRCERR 0x00080000 +#define SIS_RXSTAT_SYMBOLERR 0x00100000 +#define SIS_RXSTAT_RUNT 0x00200000 +#define SIS_RXSTAT_GIANT 0x00400000 +#define SIS_RXSTAT_DSTCLASS 0x01800000 +#define SIS_RXSTAT_OVERRUN 0x02000000 +#define SIS_RXSTAT_RX_ABORT 0x04000000 + +#define SIS_DSTCLASS_REJECT 0x00000000 +#define SIS_DSTCLASS_UNICAST 0x00800000 +#define SIS_DSTCLASS_MULTICAST 0x01000000 +#define SIS_DSTCLASS_BROADCAST 0x02000000 + +#define SIS_TXSTAT_COLLCNT 0x000F0000 +#define SIS_TXSTAT_EXCESSCOLLS 0x00100000 +#define SIS_TXSTAT_OUTOFWINCOLL 0x00200000 +#define SIS_TXSTAT_EXCESS_DEFER 0x00400000 +#define SIS_TXSTAT_DEFERED 0x00800000 +#define SIS_TXSTAT_CARR_LOST 0x01000000 +#define SIS_TXSTAT_UNDERRUN 0x02000000 +#define SIS_TXSTAT_TX_ABORT 0x04000000 + +#define SIS_RX_LIST_CNT 64 +#define SIS_TX_LIST_CNT 128 + +#define SIS_RX_LIST_SZ SIS_RX_LIST_CNT * sizeof(struct sis_desc) +#define SIS_TX_LIST_SZ SIS_TX_LIST_CNT * sizeof(struct sis_desc) + +struct sis_list_data { +#ifdef foo + struct sis_desc sis_rx_list[SIS_RX_LIST_CNT]; + struct sis_desc sis_tx_list[SIS_TX_LIST_CNT]; +#endif + struct sis_desc *sis_rx_list; + struct sis_desc *sis_tx_list; + bus_dma_tag_t sis_rx_tag; + bus_dmamap_t sis_rx_dmamap; + bus_dma_tag_t sis_tx_tag; + bus_dmamap_t sis_tx_dmamap; +}; + +struct sis_ring_data { + int sis_rx_prod; + int sis_tx_prod; + int sis_tx_cons; + int sis_tx_cnt; + u_int32_t sis_rx_paddr; + u_int32_t sis_tx_paddr; +}; + + +/* + * SiS PCI vendor ID. + */ +#define SIS_VENDORID 0x1039 + +/* + * SiS PCI device IDs + */ +#define SIS_DEVICEID_900 0x0900 +#define SIS_DEVICEID_7016 0x7016 + +/* + * SiS 900 PCI revision codes. + */ +#define SIS_REV_630E 0x0081 +#define SIS_REV_630S 0x0082 +#define SIS_REV_630EA1 0x0083 +#define SIS_REV_630ET 0x0084 +#define SIS_REV_635 0x0090 + +/* + * NatSemi vendor ID + */ +#define NS_VENDORID 0x100B + +/* + * DP83815 device ID + */ +#define NS_DEVICEID_DP83815 0x0020 + +struct sis_type { + u_int16_t sis_vid; + u_int16_t sis_did; + char *sis_name; +}; + +#define SIS_TYPE_900 1 +#define SIS_TYPE_7016 2 +#define SIS_TYPE_83815 3 + +struct sis_softc { + struct arpcom arpcom; /* interface info */ + bus_space_handle_t sis_bhandle; + bus_space_tag_t sis_btag; + struct resource *sis_res; + struct resource *sis_irq; + void *sis_intrhand; + device_t sis_miibus; + u_int8_t sis_unit; + u_int8_t sis_type; + u_int8_t sis_rev; + u_int8_t sis_link; + struct sis_list_data sis_ldata; + bus_dma_tag_t sis_parent_tag; + bus_dma_tag_t sis_tag; + struct sis_ring_data sis_cdata; + struct callout_handle sis_stat_ch; +#ifdef DEVICE_POLLING + int rxcycles; +#endif + struct mtx sis_mtx; +}; + +#define SIS_LOCK(_sc) mtx_lock(&(_sc)->sis_mtx) +#define SIS_UNLOCK(_sc) mtx_unlock(&(_sc)->sis_mtx) + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->sis_btag, sc->sis_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->sis_btag, sc->sis_bhandle, reg) + +#define CSR_READ_2(sc, reg) \ + bus_space_read_2(sc->sis_btag, sc->sis_bhandle, reg) + +#define SIS_TIMEOUT 1000 +#define ETHER_ALIGN 2 +#define SIS_RXLEN 1536 +#define SIS_MIN_FRAMELEN 60 + +/* + * PCI low memory base and low I/O base register, and + * other PCI registers. + */ + +#define SIS_PCI_VENDOR_ID 0x00 +#define SIS_PCI_DEVICE_ID 0x02 +#define SIS_PCI_COMMAND 0x04 +#define SIS_PCI_STATUS 0x06 +#define SIS_PCI_REVID 0x08 +#define SIS_PCI_CLASSCODE 0x09 +#define SIS_PCI_CACHELEN 0x0C +#define SIS_PCI_LATENCY_TIMER 0x0D +#define SIS_PCI_HEADER_TYPE 0x0E +#define SIS_PCI_LOIO 0x10 +#define SIS_PCI_LOMEM 0x14 +#define SIS_PCI_BIOSROM 0x30 +#define SIS_PCI_INTLINE 0x3C +#define SIS_PCI_INTPIN 0x3D +#define SIS_PCI_MINGNT 0x3E +#define SIS_PCI_MINLAT 0x0F +#define SIS_PCI_RESETOPT 0x48 +#define SIS_PCI_EEPROM_DATA 0x4C + +/* power management registers */ +#define SIS_PCI_CAPID 0x50 /* 8 bits */ +#define SIS_PCI_NEXTPTR 0x51 /* 8 bits */ +#define SIS_PCI_PWRMGMTCAP 0x52 /* 16 bits */ +#define SIS_PCI_PWRMGMTCTRL 0x54 /* 16 bits */ + +#define SIS_PSTATE_MASK 0x0003 +#define SIS_PSTATE_D0 0x0000 +#define SIS_PSTATE_D1 0x0001 +#define SIS_PSTATE_D2 0x0002 +#define SIS_PSTATE_D3 0x0003 +#define SIS_PME_EN 0x0010 +#define SIS_PME_STATUS 0x8000 diff --git a/sys/pci/if_sk.c b/sys/pci/if_sk.c new file mode 100644 index 0000000..adb523c --- /dev/null +++ b/sys/pci/if_sk.c @@ -0,0 +1,2218 @@ +/* + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * SysKonnect SK-NET gigabit ethernet driver for FreeBSD. Supports + * the SK-984x series adapters, both single port and dual port. + * References: + * The XaQti XMAC II datasheet, + * http://www.freebsd.org/~wpaul/SysKonnect/xmacii_datasheet_rev_c_9-29.pdf + * The SysKonnect GEnesis manual, http://www.syskonnect.com + * + * Note: XaQti has been aquired by Vitesse, and Vitesse does not have the + * XMAC II datasheet online. I have put my copy at people.freebsd.org as a + * convenience to others until Vitesse corrects this problem: + * + * http://people.freebsd.org/~wpaul/SysKonnect/xmacii_datasheet_rev_c_9-29.pdf + * + * Written by Bill Paul <wpaul@ee.columbia.edu> + * Department of Electrical Engineering + * Columbia University, New York City + */ + +/* + * The SysKonnect gigabit ethernet adapters consist of two main + * components: the SysKonnect GEnesis controller chip and the XaQti Corp. + * XMAC II gigabit ethernet MAC. The XMAC provides all of the MAC + * components and a PHY while the GEnesis controller provides a PCI + * interface with DMA support. Each card may have between 512K and + * 2MB of SRAM on board depending on the configuration. + * + * The SysKonnect GEnesis controller can have either one or two XMAC + * chips connected to it, allowing single or dual port NIC configurations. + * SysKonnect has the distinction of being the only vendor on the market + * with a dual port gigabit ethernet NIC. The GEnesis provides dual FIFOs, + * dual DMA queues, packet/MAC/transmit arbiters and direct access to the + * XMAC registers. This driver takes advantage of these features to allow + * both XMACs to operate as independent interfaces. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/queue.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#include <net/bpf.h> + +#include <vm/vm.h> /* for vtophys */ +#include <vm/pmap.h> /* for vtophys */ +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> +#include <dev/mii/brgphyreg.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +#define SK_USEIOSPACE + +#include <pci/if_skreg.h> +#include <pci/xmaciireg.h> + +MODULE_DEPEND(sk, miibus, 1, 1, 1); + +/* "controller miibus0" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif + +static struct sk_type sk_devs[] = { + { SK_VENDORID, SK_DEVICEID_GE, "SysKonnect Gigabit Ethernet" }, + { 0, 0, NULL } +}; + +static int sk_probe (device_t); +static int sk_attach (device_t); +static int sk_detach (device_t); +static int sk_detach_xmac (device_t); +static int sk_probe_xmac (device_t); +static int sk_attach_xmac (device_t); +static void sk_tick (void *); +static void sk_intr (void *); +static void sk_intr_xmac (struct sk_if_softc *); +static void sk_intr_bcom (struct sk_if_softc *); +static void sk_rxeof (struct sk_if_softc *); +static void sk_txeof (struct sk_if_softc *); +static int sk_encap (struct sk_if_softc *, struct mbuf *, + u_int32_t *); +static void sk_start (struct ifnet *); +static int sk_ioctl (struct ifnet *, u_long, caddr_t); +static void sk_init (void *); +static void sk_init_xmac (struct sk_if_softc *); +static void sk_stop (struct sk_if_softc *); +static void sk_watchdog (struct ifnet *); +static void sk_shutdown (device_t); +static int sk_ifmedia_upd (struct ifnet *); +static void sk_ifmedia_sts (struct ifnet *, struct ifmediareq *); +static void sk_reset (struct sk_softc *); +static int sk_newbuf (struct sk_if_softc *, + struct sk_chain *, struct mbuf *); +static int sk_alloc_jumbo_mem (struct sk_if_softc *); +static void *sk_jalloc (struct sk_if_softc *); +static void sk_jfree (void *, void *); +static int sk_init_rx_ring (struct sk_if_softc *); +static void sk_init_tx_ring (struct sk_if_softc *); +static u_int32_t sk_win_read_4 (struct sk_softc *, int); +static u_int16_t sk_win_read_2 (struct sk_softc *, int); +static u_int8_t sk_win_read_1 (struct sk_softc *, int); +static void sk_win_write_4 (struct sk_softc *, int, u_int32_t); +static void sk_win_write_2 (struct sk_softc *, int, u_int32_t); +static void sk_win_write_1 (struct sk_softc *, int, u_int32_t); +static u_int8_t sk_vpd_readbyte (struct sk_softc *, int); +static void sk_vpd_read_res (struct sk_softc *, struct vpd_res *, int); +static void sk_vpd_read (struct sk_softc *); + +static int sk_miibus_readreg (device_t, int, int); +static int sk_miibus_writereg (device_t, int, int, int); +static void sk_miibus_statchg (device_t); + +static u_int32_t sk_calchash (caddr_t); +static void sk_setfilt (struct sk_if_softc *, caddr_t, int); +static void sk_setmulti (struct sk_if_softc *); + +#ifdef SK_USEIOSPACE +#define SK_RES SYS_RES_IOPORT +#define SK_RID SK_PCI_LOIO +#else +#define SK_RES SYS_RES_MEMORY +#define SK_RID SK_PCI_LOMEM +#endif + +/* + * Note that we have newbus methods for both the GEnesis controller + * itself and the XMAC(s). The XMACs are children of the GEnesis, and + * the miibus code is a child of the XMACs. We need to do it this way + * so that the miibus drivers can access the PHY registers on the + * right PHY. It's not quite what I had in mind, but it's the only + * design that achieves the desired effect. + */ +static device_method_t skc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sk_probe), + DEVMETHOD(device_attach, sk_attach), + DEVMETHOD(device_detach, sk_detach), + DEVMETHOD(device_shutdown, sk_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + { 0, 0 } +}; + +static driver_t skc_driver = { + "skc", + skc_methods, + sizeof(struct sk_softc) +}; + +static devclass_t skc_devclass; + +static device_method_t sk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sk_probe_xmac), + DEVMETHOD(device_attach, sk_attach_xmac), + DEVMETHOD(device_detach, sk_detach_xmac), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, sk_miibus_readreg), + DEVMETHOD(miibus_writereg, sk_miibus_writereg), + DEVMETHOD(miibus_statchg, sk_miibus_statchg), + + { 0, 0 } +}; + +static driver_t sk_driver = { + "sk", + sk_methods, + sizeof(struct sk_if_softc) +}; + +static devclass_t sk_devclass; + +DRIVER_MODULE(if_sk, pci, skc_driver, skc_devclass, 0, 0); +DRIVER_MODULE(sk, skc, sk_driver, sk_devclass, 0, 0); +DRIVER_MODULE(miibus, sk, miibus_driver, miibus_devclass, 0, 0); + +#define SK_SETBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | x) + +#define SK_CLRBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~x) + +#define SK_WIN_SETBIT_4(sc, reg, x) \ + sk_win_write_4(sc, reg, sk_win_read_4(sc, reg) | x) + +#define SK_WIN_CLRBIT_4(sc, reg, x) \ + sk_win_write_4(sc, reg, sk_win_read_4(sc, reg) & ~x) + +#define SK_WIN_SETBIT_2(sc, reg, x) \ + sk_win_write_2(sc, reg, sk_win_read_2(sc, reg) | x) + +#define SK_WIN_CLRBIT_2(sc, reg, x) \ + sk_win_write_2(sc, reg, sk_win_read_2(sc, reg) & ~x) + +static u_int32_t sk_win_read_4(sc, reg) + struct sk_softc *sc; + int reg; +{ + CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); + return(CSR_READ_4(sc, SK_WIN_BASE + SK_REG(reg))); +} + +static u_int16_t sk_win_read_2(sc, reg) + struct sk_softc *sc; + int reg; +{ + CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); + return(CSR_READ_2(sc, SK_WIN_BASE + SK_REG(reg))); +} + +static u_int8_t sk_win_read_1(sc, reg) + struct sk_softc *sc; + int reg; +{ + CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); + return(CSR_READ_1(sc, SK_WIN_BASE + SK_REG(reg))); +} + +static void sk_win_write_4(sc, reg, val) + struct sk_softc *sc; + int reg; + u_int32_t val; +{ + CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); + CSR_WRITE_4(sc, SK_WIN_BASE + SK_REG(reg), val); + return; +} + +static void sk_win_write_2(sc, reg, val) + struct sk_softc *sc; + int reg; + u_int32_t val; +{ + CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); + CSR_WRITE_2(sc, SK_WIN_BASE + SK_REG(reg), (u_int32_t)val); + return; +} + +static void sk_win_write_1(sc, reg, val) + struct sk_softc *sc; + int reg; + u_int32_t val; +{ + CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); + CSR_WRITE_1(sc, SK_WIN_BASE + SK_REG(reg), val); + return; +} + +/* + * The VPD EEPROM contains Vital Product Data, as suggested in + * the PCI 2.1 specification. The VPD data is separared into areas + * denoted by resource IDs. The SysKonnect VPD contains an ID string + * resource (the name of the adapter), a read-only area resource + * containing various key/data fields and a read/write area which + * can be used to store asset management information or log messages. + * We read the ID string and read-only into buffers attached to + * the controller softc structure for later use. At the moment, + * we only use the ID string during sk_attach(). + */ +static u_int8_t sk_vpd_readbyte(sc, addr) + struct sk_softc *sc; + int addr; +{ + int i; + + sk_win_write_2(sc, SK_PCI_REG(SK_PCI_VPD_ADDR), addr); + for (i = 0; i < SK_TIMEOUT; i++) { + DELAY(1); + if (sk_win_read_2(sc, + SK_PCI_REG(SK_PCI_VPD_ADDR)) & SK_VPD_FLAG) + break; + } + + if (i == SK_TIMEOUT) + return(0); + + return(sk_win_read_1(sc, SK_PCI_REG(SK_PCI_VPD_DATA))); +} + +static void sk_vpd_read_res(sc, res, addr) + struct sk_softc *sc; + struct vpd_res *res; + int addr; +{ + int i; + u_int8_t *ptr; + + ptr = (u_int8_t *)res; + for (i = 0; i < sizeof(struct vpd_res); i++) + ptr[i] = sk_vpd_readbyte(sc, i + addr); + + return; +} + +static void sk_vpd_read(sc) + struct sk_softc *sc; +{ + int pos = 0, i; + struct vpd_res res; + + if (sc->sk_vpd_prodname != NULL) + free(sc->sk_vpd_prodname, M_DEVBUF); + if (sc->sk_vpd_readonly != NULL) + free(sc->sk_vpd_readonly, M_DEVBUF); + sc->sk_vpd_prodname = NULL; + sc->sk_vpd_readonly = NULL; + + sk_vpd_read_res(sc, &res, pos); + + if (res.vr_id != VPD_RES_ID) { + printf("skc%d: bad VPD resource id: expected %x got %x\n", + sc->sk_unit, VPD_RES_ID, res.vr_id); + return; + } + + pos += sizeof(res); + sc->sk_vpd_prodname = malloc(res.vr_len + 1, M_DEVBUF, M_NOWAIT); + for (i = 0; i < res.vr_len; i++) + sc->sk_vpd_prodname[i] = sk_vpd_readbyte(sc, i + pos); + sc->sk_vpd_prodname[i] = '\0'; + pos += i; + + sk_vpd_read_res(sc, &res, pos); + + if (res.vr_id != VPD_RES_READ) { + printf("skc%d: bad VPD resource id: expected %x got %x\n", + sc->sk_unit, VPD_RES_READ, res.vr_id); + return; + } + + pos += sizeof(res); + sc->sk_vpd_readonly = malloc(res.vr_len, M_DEVBUF, M_NOWAIT); + for (i = 0; i < res.vr_len + 1; i++) + sc->sk_vpd_readonly[i] = sk_vpd_readbyte(sc, i + pos); + + return; +} + +static int sk_miibus_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct sk_if_softc *sc_if; + int i; + + sc_if = device_get_softc(dev); + + if (sc_if->sk_phytype == SK_PHYTYPE_XMAC && phy != 0) + return(0); + + SK_IF_LOCK(sc_if); + + SK_XM_WRITE_2(sc_if, XM_PHY_ADDR, reg|(phy << 8)); + SK_XM_READ_2(sc_if, XM_PHY_DATA); + if (sc_if->sk_phytype != SK_PHYTYPE_XMAC) { + for (i = 0; i < SK_TIMEOUT; i++) { + DELAY(1); + if (SK_XM_READ_2(sc_if, XM_MMUCMD) & + XM_MMUCMD_PHYDATARDY) + break; + } + + if (i == SK_TIMEOUT) { + printf("sk%d: phy failed to come ready\n", + sc_if->sk_unit); + return(0); + } + } + DELAY(1); + i = SK_XM_READ_2(sc_if, XM_PHY_DATA); + SK_IF_UNLOCK(sc_if); + return(i); +} + +static int sk_miibus_writereg(dev, phy, reg, val) + device_t dev; + int phy, reg, val; +{ + struct sk_if_softc *sc_if; + int i; + + sc_if = device_get_softc(dev); + SK_IF_LOCK(sc_if); + + SK_XM_WRITE_2(sc_if, XM_PHY_ADDR, reg|(phy << 8)); + for (i = 0; i < SK_TIMEOUT; i++) { + if (!(SK_XM_READ_2(sc_if, XM_MMUCMD) & XM_MMUCMD_PHYBUSY)) + break; + } + + if (i == SK_TIMEOUT) { + printf("sk%d: phy failed to come ready\n", sc_if->sk_unit); + return(ETIMEDOUT); + } + + SK_XM_WRITE_2(sc_if, XM_PHY_DATA, val); + for (i = 0; i < SK_TIMEOUT; i++) { + DELAY(1); + if (!(SK_XM_READ_2(sc_if, XM_MMUCMD) & XM_MMUCMD_PHYBUSY)) + break; + } + + SK_IF_UNLOCK(sc_if); + + if (i == SK_TIMEOUT) + printf("sk%d: phy write timed out\n", sc_if->sk_unit); + + return(0); +} + +static void sk_miibus_statchg(dev) + device_t dev; +{ + struct sk_if_softc *sc_if; + struct mii_data *mii; + + sc_if = device_get_softc(dev); + mii = device_get_softc(sc_if->sk_miibus); + SK_IF_LOCK(sc_if); + /* + * If this is a GMII PHY, manually set the XMAC's + * duplex mode accordingly. + */ + if (sc_if->sk_phytype != SK_PHYTYPE_XMAC) { + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { + SK_XM_SETBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_GMIIFDX); + } else { + SK_XM_CLRBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_GMIIFDX); + } + } + SK_IF_UNLOCK(sc_if); + + return; +} + +#define SK_POLY 0xEDB88320 +#define SK_BITS 6 + +static u_int32_t sk_calchash(addr) + caddr_t addr; +{ + u_int32_t idx, bit, data, crc; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (idx = 0; idx < 6; idx++) { + for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) + crc = (crc >> 1) ^ (((crc ^ data) & 1) ? SK_POLY : 0); + } + + return (~crc & ((1 << SK_BITS) - 1)); +} + +static void sk_setfilt(sc_if, addr, slot) + struct sk_if_softc *sc_if; + caddr_t addr; + int slot; +{ + int base; + + base = XM_RXFILT_ENTRY(slot); + + SK_XM_WRITE_2(sc_if, base, *(u_int16_t *)(&addr[0])); + SK_XM_WRITE_2(sc_if, base + 2, *(u_int16_t *)(&addr[2])); + SK_XM_WRITE_2(sc_if, base + 4, *(u_int16_t *)(&addr[4])); + + return; +} + +static void sk_setmulti(sc_if) + struct sk_if_softc *sc_if; +{ + struct ifnet *ifp; + u_int32_t hashes[2] = { 0, 0 }; + int h, i; + struct ifmultiaddr *ifma; + u_int8_t dummy[] = { 0, 0, 0, 0, 0 ,0 }; + + ifp = &sc_if->arpcom.ac_if; + + /* First, zot all the existing filters. */ + for (i = 1; i < XM_RXFILT_MAX; i++) + sk_setfilt(sc_if, (caddr_t)&dummy, i); + SK_XM_WRITE_4(sc_if, XM_MAR0, 0); + SK_XM_WRITE_4(sc_if, XM_MAR2, 0); + + /* Now program new ones. */ + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + hashes[0] = 0xFFFFFFFF; + hashes[1] = 0xFFFFFFFF; + } else { + i = 1; + TAILQ_FOREACH_REVERSE(ifma, &ifp->if_multiaddrs, ifmultihead, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + /* + * Program the first XM_RXFILT_MAX multicast groups + * into the perfect filter. For all others, + * use the hash table. + */ + if (i < XM_RXFILT_MAX) { + sk_setfilt(sc_if, + LLADDR((struct sockaddr_dl *)ifma->ifma_addr), i); + i++; + continue; + } + + h = sk_calchash( + LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + } + } + + SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_USE_HASH| + XM_MODE_RX_USE_PERFECT); + SK_XM_WRITE_4(sc_if, XM_MAR0, hashes[0]); + SK_XM_WRITE_4(sc_if, XM_MAR2, hashes[1]); + + return; +} + +static int sk_init_rx_ring(sc_if) + struct sk_if_softc *sc_if; +{ + struct sk_chain_data *cd; + struct sk_ring_data *rd; + int i; + + cd = &sc_if->sk_cdata; + rd = sc_if->sk_rdata; + + bzero((char *)rd->sk_rx_ring, + sizeof(struct sk_rx_desc) * SK_RX_RING_CNT); + + for (i = 0; i < SK_RX_RING_CNT; i++) { + cd->sk_rx_chain[i].sk_desc = &rd->sk_rx_ring[i]; + if (sk_newbuf(sc_if, &cd->sk_rx_chain[i], NULL) == ENOBUFS) + return(ENOBUFS); + if (i == (SK_RX_RING_CNT - 1)) { + cd->sk_rx_chain[i].sk_next = + &cd->sk_rx_chain[0]; + rd->sk_rx_ring[i].sk_next = + vtophys(&rd->sk_rx_ring[0]); + } else { + cd->sk_rx_chain[i].sk_next = + &cd->sk_rx_chain[i + 1]; + rd->sk_rx_ring[i].sk_next = + vtophys(&rd->sk_rx_ring[i + 1]); + } + } + + sc_if->sk_cdata.sk_rx_prod = 0; + sc_if->sk_cdata.sk_rx_cons = 0; + + return(0); +} + +static void sk_init_tx_ring(sc_if) + struct sk_if_softc *sc_if; +{ + struct sk_chain_data *cd; + struct sk_ring_data *rd; + int i; + + cd = &sc_if->sk_cdata; + rd = sc_if->sk_rdata; + + bzero((char *)sc_if->sk_rdata->sk_tx_ring, + sizeof(struct sk_tx_desc) * SK_TX_RING_CNT); + + for (i = 0; i < SK_TX_RING_CNT; i++) { + cd->sk_tx_chain[i].sk_desc = &rd->sk_tx_ring[i]; + if (i == (SK_TX_RING_CNT - 1)) { + cd->sk_tx_chain[i].sk_next = + &cd->sk_tx_chain[0]; + rd->sk_tx_ring[i].sk_next = + vtophys(&rd->sk_tx_ring[0]); + } else { + cd->sk_tx_chain[i].sk_next = + &cd->sk_tx_chain[i + 1]; + rd->sk_tx_ring[i].sk_next = + vtophys(&rd->sk_tx_ring[i + 1]); + } + } + + sc_if->sk_cdata.sk_tx_prod = 0; + sc_if->sk_cdata.sk_tx_cons = 0; + sc_if->sk_cdata.sk_tx_cnt = 0; + + return; +} + +static int sk_newbuf(sc_if, c, m) + struct sk_if_softc *sc_if; + struct sk_chain *c; + struct mbuf *m; +{ + struct mbuf *m_new = NULL; + struct sk_rx_desc *r; + + if (m == NULL) { + caddr_t *buf = NULL; + + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(ENOBUFS); + + /* Allocate the jumbo buffer */ + buf = sk_jalloc(sc_if); + if (buf == NULL) { + m_freem(m_new); +#ifdef SK_VERBOSE + printf("sk%d: jumbo allocation failed " + "-- packet dropped!\n", sc_if->sk_unit); +#endif + return(ENOBUFS); + } + + /* Attach the buffer to the mbuf */ + MEXTADD(m_new, buf, SK_JLEN, sk_jfree, + (struct sk_if_softc *)sc_if, 0, EXT_NET_DRV); + m_new->m_data = (void *)buf; + m_new->m_pkthdr.len = m_new->m_len = SK_JLEN; + } else { + /* + * We're re-using a previously allocated mbuf; + * be sure to re-init pointers and lengths to + * default values. + */ + m_new = m; + m_new->m_len = m_new->m_pkthdr.len = SK_JLEN; + m_new->m_data = m_new->m_ext.ext_buf; + } + + /* + * Adjust alignment so packet payload begins on a + * longword boundary. Mandatory for Alpha, useful on + * x86 too. + */ + m_adj(m_new, ETHER_ALIGN); + + r = c->sk_desc; + c->sk_mbuf = m_new; + r->sk_data_lo = vtophys(mtod(m_new, caddr_t)); + r->sk_ctl = m_new->m_len | SK_RXSTAT; + + return(0); +} + +/* + * Allocate jumbo buffer storage. The SysKonnect adapters support + * "jumbograms" (9K frames), although SysKonnect doesn't currently + * use them in their drivers. In order for us to use them, we need + * large 9K receive buffers, however standard mbuf clusters are only + * 2048 bytes in size. Consequently, we need to allocate and manage + * our own jumbo buffer pool. Fortunately, this does not require an + * excessive amount of additional code. + */ +static int sk_alloc_jumbo_mem(sc_if) + struct sk_if_softc *sc_if; +{ + caddr_t ptr; + register int i; + struct sk_jpool_entry *entry; + + /* Grab a big chunk o' storage. */ + sc_if->sk_cdata.sk_jumbo_buf = contigmalloc(SK_JMEM, M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc_if->sk_cdata.sk_jumbo_buf == NULL) { + printf("sk%d: no memory for jumbo buffers!\n", sc_if->sk_unit); + return(ENOBUFS); + } + + SLIST_INIT(&sc_if->sk_jfree_listhead); + SLIST_INIT(&sc_if->sk_jinuse_listhead); + + /* + * Now divide it up into 9K pieces and save the addresses + * in an array. + */ + ptr = sc_if->sk_cdata.sk_jumbo_buf; + for (i = 0; i < SK_JSLOTS; i++) { + sc_if->sk_cdata.sk_jslots[i] = ptr; + ptr += SK_JLEN; + entry = malloc(sizeof(struct sk_jpool_entry), + M_DEVBUF, M_NOWAIT); + if (entry == NULL) { + free(sc_if->sk_cdata.sk_jumbo_buf, M_DEVBUF); + sc_if->sk_cdata.sk_jumbo_buf = NULL; + printf("sk%d: no memory for jumbo " + "buffer queue!\n", sc_if->sk_unit); + return(ENOBUFS); + } + entry->slot = i; + SLIST_INSERT_HEAD(&sc_if->sk_jfree_listhead, + entry, jpool_entries); + } + + return(0); +} + +/* + * Allocate a jumbo buffer. + */ +static void *sk_jalloc(sc_if) + struct sk_if_softc *sc_if; +{ + struct sk_jpool_entry *entry; + + entry = SLIST_FIRST(&sc_if->sk_jfree_listhead); + + if (entry == NULL) { +#ifdef SK_VERBOSE + printf("sk%d: no free jumbo buffers\n", sc_if->sk_unit); +#endif + return(NULL); + } + + SLIST_REMOVE_HEAD(&sc_if->sk_jfree_listhead, jpool_entries); + SLIST_INSERT_HEAD(&sc_if->sk_jinuse_listhead, entry, jpool_entries); + return(sc_if->sk_cdata.sk_jslots[entry->slot]); +} + +/* + * Release a jumbo buffer. + */ +static void sk_jfree(buf, args) + void *buf; + void *args; +{ + struct sk_if_softc *sc_if; + int i; + struct sk_jpool_entry *entry; + + /* Extract the softc struct pointer. */ + sc_if = (struct sk_if_softc *)args; + + if (sc_if == NULL) + panic("sk_jfree: didn't get softc pointer!"); + + /* calculate the slot this buffer belongs to */ + i = ((vm_offset_t)buf + - (vm_offset_t)sc_if->sk_cdata.sk_jumbo_buf) / SK_JLEN; + + if ((i < 0) || (i >= SK_JSLOTS)) + panic("sk_jfree: asked to free buffer that we don't manage!"); + + entry = SLIST_FIRST(&sc_if->sk_jinuse_listhead); + if (entry == NULL) + panic("sk_jfree: buffer not in use!"); + entry->slot = i; + SLIST_REMOVE_HEAD(&sc_if->sk_jinuse_listhead, jpool_entries); + SLIST_INSERT_HEAD(&sc_if->sk_jfree_listhead, entry, jpool_entries); + + return; +} + +/* + * Set media options. + */ +static int sk_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct sk_if_softc *sc_if; + struct mii_data *mii; + + sc_if = ifp->if_softc; + mii = device_get_softc(sc_if->sk_miibus); + sk_init(sc_if); + mii_mediachg(mii); + + return(0); +} + +/* + * Report current media status. + */ +static void sk_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct sk_if_softc *sc_if; + struct mii_data *mii; + + sc_if = ifp->if_softc; + mii = device_get_softc(sc_if->sk_miibus); + + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + + return; +} + +static int sk_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct sk_if_softc *sc_if = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + int error = 0; + struct mii_data *mii; + + SK_IF_LOCK(sc_if); + + switch(command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFMTU: + if (ifr->ifr_mtu > SK_JUMBO_MTU) + error = EINVAL; + else { + ifp->if_mtu = ifr->ifr_mtu; + sk_init(sc_if); + } + break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING && + ifp->if_flags & IFF_PROMISC && + !(sc_if->sk_if_flags & IFF_PROMISC)) { + SK_XM_SETBIT_4(sc_if, XM_MODE, + XM_MODE_RX_PROMISC); + sk_setmulti(sc_if); + } else if (ifp->if_flags & IFF_RUNNING && + !(ifp->if_flags & IFF_PROMISC) && + sc_if->sk_if_flags & IFF_PROMISC) { + SK_XM_CLRBIT_4(sc_if, XM_MODE, + XM_MODE_RX_PROMISC); + sk_setmulti(sc_if); + } else + sk_init(sc_if); + } else { + if (ifp->if_flags & IFF_RUNNING) + sk_stop(sc_if); + } + sc_if->sk_if_flags = ifp->if_flags; + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + sk_setmulti(sc_if); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = device_get_softc(sc_if->sk_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + break; + default: + error = EINVAL; + break; + } + + SK_IF_UNLOCK(sc_if); + + return(error); +} + +/* + * Probe for a SysKonnect GEnesis chip. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + */ +static int sk_probe(dev) + device_t dev; +{ + struct sk_type *t; + + t = sk_devs; + + while(t->sk_name != NULL) { + if ((pci_get_vendor(dev) == t->sk_vid) && + (pci_get_device(dev) == t->sk_did)) { + device_set_desc(dev, t->sk_name); + return(0); + } + t++; + } + + return(ENXIO); +} + +/* + * Force the GEnesis into reset, then bring it out of reset. + */ +static void sk_reset(sc) + struct sk_softc *sc; +{ + CSR_WRITE_4(sc, SK_CSR, SK_CSR_SW_RESET); + CSR_WRITE_4(sc, SK_CSR, SK_CSR_MASTER_RESET); + DELAY(1000); + CSR_WRITE_4(sc, SK_CSR, SK_CSR_SW_UNRESET); + CSR_WRITE_4(sc, SK_CSR, SK_CSR_MASTER_UNRESET); + + /* Configure packet arbiter */ + sk_win_write_2(sc, SK_PKTARB_CTL, SK_PKTARBCTL_UNRESET); + sk_win_write_2(sc, SK_RXPA1_TINIT, SK_PKTARB_TIMEOUT); + sk_win_write_2(sc, SK_TXPA1_TINIT, SK_PKTARB_TIMEOUT); + sk_win_write_2(sc, SK_RXPA2_TINIT, SK_PKTARB_TIMEOUT); + sk_win_write_2(sc, SK_TXPA2_TINIT, SK_PKTARB_TIMEOUT); + + /* Enable RAM interface */ + sk_win_write_4(sc, SK_RAMCTL, SK_RAMCTL_UNRESET); + + /* + * Configure interrupt moderation. The moderation timer + * defers interrupts specified in the interrupt moderation + * timer mask based on the timeout specified in the interrupt + * moderation timer init register. Each bit in the timer + * register represents 18.825ns, so to specify a timeout in + * microseconds, we have to multiply by 54. + */ + sk_win_write_4(sc, SK_IMTIMERINIT, SK_IM_USECS(200)); + sk_win_write_4(sc, SK_IMMR, SK_ISR_TX1_S_EOF|SK_ISR_TX2_S_EOF| + SK_ISR_RX1_EOF|SK_ISR_RX2_EOF); + sk_win_write_1(sc, SK_IMTIMERCTL, SK_IMCTL_START); + + return; +} + +static int sk_probe_xmac(dev) + device_t dev; +{ + /* + * Not much to do here. We always know there will be + * at least one XMAC present, and if there are two, + * sk_attach() will create a second device instance + * for us. + */ + device_set_desc(dev, "XaQti Corp. XMAC II"); + + return(0); +} + +/* + * Each XMAC chip is attached as a separate logical IP interface. + * Single port cards will have only one logical interface of course. + */ +static int sk_attach_xmac(dev) + device_t dev; +{ + struct sk_softc *sc; + struct sk_if_softc *sc_if; + struct ifnet *ifp; + int i, port; + + if (dev == NULL) + return(EINVAL); + + sc_if = device_get_softc(dev); + sc = device_get_softc(device_get_parent(dev)); + SK_LOCK(sc); + port = *(int *)device_get_ivars(dev); + free(device_get_ivars(dev), M_DEVBUF); + device_set_ivars(dev, NULL); + sc_if->sk_dev = dev; + + bzero((char *)sc_if, sizeof(struct sk_if_softc)); + + sc_if->sk_dev = dev; + sc_if->sk_unit = device_get_unit(dev); + sc_if->sk_port = port; + sc_if->sk_softc = sc; + sc->sk_if[port] = sc_if; + if (port == SK_PORT_A) + sc_if->sk_tx_bmu = SK_BMU_TXS_CSR0; + if (port == SK_PORT_B) + sc_if->sk_tx_bmu = SK_BMU_TXS_CSR1; + + /* + * Get station address for this interface. Note that + * dual port cards actually come with three station + * addresses: one for each port, plus an extra. The + * extra one is used by the SysKonnect driver software + * as a 'virtual' station address for when both ports + * are operating in failover mode. Currently we don't + * use this extra address. + */ + for (i = 0; i < ETHER_ADDR_LEN; i++) + sc_if->arpcom.ac_enaddr[i] = + sk_win_read_1(sc, SK_MAC0_0 + (port * 8) + i); + + printf("sk%d: Ethernet address: %6D\n", + sc_if->sk_unit, sc_if->arpcom.ac_enaddr, ":"); + + /* + * Set up RAM buffer addresses. The NIC will have a certain + * amount of SRAM on it, somewhere between 512K and 2MB. We + * need to divide this up a) between the transmitter and + * receiver and b) between the two XMACs, if this is a + * dual port NIC. Our algotithm is to divide up the memory + * evenly so that everyone gets a fair share. + */ + if (sk_win_read_1(sc, SK_CONFIG) & SK_CONFIG_SINGLEMAC) { + u_int32_t chunk, val; + + chunk = sc->sk_ramsize / 2; + val = sc->sk_rboff / sizeof(u_int64_t); + sc_if->sk_rx_ramstart = val; + val += (chunk / sizeof(u_int64_t)); + sc_if->sk_rx_ramend = val - 1; + sc_if->sk_tx_ramstart = val; + val += (chunk / sizeof(u_int64_t)); + sc_if->sk_tx_ramend = val - 1; + } else { + u_int32_t chunk, val; + + chunk = sc->sk_ramsize / 4; + val = (sc->sk_rboff + (chunk * 2 * sc_if->sk_port)) / + sizeof(u_int64_t); + sc_if->sk_rx_ramstart = val; + val += (chunk / sizeof(u_int64_t)); + sc_if->sk_rx_ramend = val - 1; + sc_if->sk_tx_ramstart = val; + val += (chunk / sizeof(u_int64_t)); + sc_if->sk_tx_ramend = val - 1; + } + + /* Read and save PHY type and set PHY address */ + sc_if->sk_phytype = sk_win_read_1(sc, SK_EPROM1) & 0xF; + switch(sc_if->sk_phytype) { + case SK_PHYTYPE_XMAC: + sc_if->sk_phyaddr = SK_PHYADDR_XMAC; + break; + case SK_PHYTYPE_BCOM: + sc_if->sk_phyaddr = SK_PHYADDR_BCOM; + break; + default: + printf("skc%d: unsupported PHY type: %d\n", + sc->sk_unit, sc_if->sk_phytype); + SK_UNLOCK(sc); + return(ENODEV); + } + + /* Allocate the descriptor queues. */ + sc_if->sk_rdata = contigmalloc(sizeof(struct sk_ring_data), M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc_if->sk_rdata == NULL) { + printf("sk%d: no memory for list buffers!\n", sc_if->sk_unit); + sc->sk_if[port] = NULL; + SK_UNLOCK(sc); + return(ENOMEM); + } + + bzero(sc_if->sk_rdata, sizeof(struct sk_ring_data)); + + /* Try to allocate memory for jumbo buffers. */ + if (sk_alloc_jumbo_mem(sc_if)) { + printf("sk%d: jumbo buffer allocation failed\n", + sc_if->sk_unit); + contigfree(sc_if->sk_rdata, + sizeof(struct sk_ring_data), M_DEVBUF); + sc->sk_if[port] = NULL; + SK_UNLOCK(sc); + return(ENOMEM); + } + + ifp = &sc_if->arpcom.ac_if; + ifp->if_softc = sc_if; + ifp->if_unit = sc_if->sk_unit; + ifp->if_name = "sk"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = sk_ioctl; + ifp->if_output = ether_output; + ifp->if_start = sk_start; + ifp->if_watchdog = sk_watchdog; + ifp->if_init = sk_init; + ifp->if_baudrate = 1000000000; + ifp->if_snd.ifq_maxlen = SK_TX_RING_CNT - 1; + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); + callout_handle_init(&sc_if->sk_tick_ch); + + /* + * Do miibus setup. + */ + sk_init_xmac(sc_if); + if (mii_phy_probe(dev, &sc_if->sk_miibus, + sk_ifmedia_upd, sk_ifmedia_sts)) { + printf("skc%d: no PHY found!\n", sc_if->sk_unit); + contigfree(sc_if->sk_rdata, + sizeof(struct sk_ring_data), M_DEVBUF); + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + SK_UNLOCK(sc); + return(ENXIO); + } + + SK_UNLOCK(sc); + + return(0); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int sk_attach(dev) + device_t dev; +{ + u_int32_t command; + struct sk_softc *sc; + int unit, error = 0, rid, *port; + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + bzero(sc, sizeof(struct sk_softc)); + + mtx_init(&sc->sk_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF | MTX_RECURSE); + SK_LOCK(sc); + + /* + * Handle power management nonsense. + */ + if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { + u_int32_t iobase, membase, irq; + + /* Save important PCI config data. */ + iobase = pci_read_config(dev, SK_PCI_LOIO, 4); + membase = pci_read_config(dev, SK_PCI_LOMEM, 4); + irq = pci_read_config(dev, SK_PCI_INTLINE, 4); + + /* Reset the power state. */ + printf("skc%d: chip is in D%d power mode " + "-- setting to D0\n", unit, + pci_get_powerstate(dev)); + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + + /* Restore PCI config data. */ + pci_write_config(dev, SK_PCI_LOIO, iobase, 4); + pci_write_config(dev, SK_PCI_LOMEM, membase, 4); + pci_write_config(dev, SK_PCI_INTLINE, irq, 4); + } + + /* + * Map control/status registers. + */ + pci_enable_busmaster(dev); + pci_enable_io(dev, SYS_RES_IOPORT); + pci_enable_io(dev, SYS_RES_MEMORY); + command = pci_read_config(dev, PCIR_COMMAND, 4); + +#ifdef SK_USEIOSPACE + if (!(command & PCIM_CMD_PORTEN)) { + printf("skc%d: failed to enable I/O ports!\n", unit); + error = ENXIO; + goto fail; + } +#else + if (!(command & PCIM_CMD_MEMEN)) { + printf("skc%d: failed to enable memory mapping!\n", unit); + error = ENXIO; + goto fail; + } +#endif + + rid = SK_RID; + sc->sk_res = bus_alloc_resource(dev, SK_RES, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (sc->sk_res == NULL) { + printf("sk%d: couldn't map ports/memory\n", unit); + error = ENXIO; + goto fail; + } + + sc->sk_btag = rman_get_bustag(sc->sk_res); + sc->sk_bhandle = rman_get_bushandle(sc->sk_res); + + /* Allocate interrupt */ + rid = 0; + sc->sk_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->sk_irq == NULL) { + printf("skc%d: couldn't map interrupt\n", unit); + bus_release_resource(dev, SK_RES, SK_RID, sc->sk_res); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->sk_irq, INTR_TYPE_NET, + sk_intr, sc, &sc->sk_intrhand); + + if (error) { + printf("skc%d: couldn't set up irq\n", unit); + bus_release_resource(dev, SK_RES, SK_RID, sc->sk_res); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sk_irq); + goto fail; + } + + /* Reset the adapter. */ + sk_reset(sc); + + sc->sk_unit = unit; + + /* Read and save vital product data from EEPROM. */ + sk_vpd_read(sc); + + /* Read and save RAM size and RAMbuffer offset */ + switch(sk_win_read_1(sc, SK_EPROM0)) { + case SK_RAMSIZE_512K_64: + sc->sk_ramsize = 0x80000; + sc->sk_rboff = SK_RBOFF_0; + break; + case SK_RAMSIZE_1024K_64: + sc->sk_ramsize = 0x100000; + sc->sk_rboff = SK_RBOFF_80000; + break; + case SK_RAMSIZE_1024K_128: + sc->sk_ramsize = 0x100000; + sc->sk_rboff = SK_RBOFF_0; + break; + case SK_RAMSIZE_2048K_128: + sc->sk_ramsize = 0x200000; + sc->sk_rboff = SK_RBOFF_0; + break; + default: + printf("skc%d: unknown ram size: %d\n", + sc->sk_unit, sk_win_read_1(sc, SK_EPROM0)); + bus_teardown_intr(dev, sc->sk_irq, sc->sk_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sk_irq); + bus_release_resource(dev, SK_RES, SK_RID, sc->sk_res); + error = ENXIO; + goto fail; + break; + } + + /* Read and save physical media type */ + switch(sk_win_read_1(sc, SK_PMDTYPE)) { + case SK_PMD_1000BASESX: + sc->sk_pmd = IFM_1000_SX; + break; + case SK_PMD_1000BASELX: + sc->sk_pmd = IFM_1000_LX; + break; + case SK_PMD_1000BASECX: + sc->sk_pmd = IFM_1000_CX; + break; + case SK_PMD_1000BASETX: + sc->sk_pmd = IFM_1000_T; + break; + default: + printf("skc%d: unknown media type: 0x%x\n", + sc->sk_unit, sk_win_read_1(sc, SK_PMDTYPE)); + bus_teardown_intr(dev, sc->sk_irq, sc->sk_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sk_irq); + bus_release_resource(dev, SK_RES, SK_RID, sc->sk_res); + error = ENXIO; + goto fail; + } + + /* Announce the product name. */ + printf("skc%d: %s\n", sc->sk_unit, sc->sk_vpd_prodname); + sc->sk_devs[SK_PORT_A] = device_add_child(dev, "sk", -1); + port = malloc(sizeof(int), M_DEVBUF, M_NOWAIT); + *port = SK_PORT_A; + device_set_ivars(sc->sk_devs[SK_PORT_A], port); + + if (!(sk_win_read_1(sc, SK_CONFIG) & SK_CONFIG_SINGLEMAC)) { + sc->sk_devs[SK_PORT_B] = device_add_child(dev, "sk", -1); + port = malloc(sizeof(int), M_DEVBUF, M_NOWAIT); + *port = SK_PORT_B; + device_set_ivars(sc->sk_devs[SK_PORT_B], port); + } + + /* Turn on the 'driver is loaded' LED. */ + CSR_WRITE_2(sc, SK_LED, SK_LED_GREEN_ON); + + bus_generic_attach(dev); + SK_UNLOCK(sc); + return(0); + +fail: + SK_UNLOCK(sc); + mtx_destroy(&sc->sk_mtx); + return(error); +} + +static int sk_detach_xmac(dev) + device_t dev; +{ + struct sk_softc *sc; + struct sk_if_softc *sc_if; + struct ifnet *ifp; + + sc = device_get_softc(device_get_parent(dev)); + sc_if = device_get_softc(dev); + SK_IF_LOCK(sc_if); + + ifp = &sc_if->arpcom.ac_if; + sk_stop(sc_if); + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + bus_generic_detach(dev); + if (sc_if->sk_miibus != NULL) + device_delete_child(dev, sc_if->sk_miibus); + contigfree(sc_if->sk_cdata.sk_jumbo_buf, SK_JMEM, M_DEVBUF); + contigfree(sc_if->sk_rdata, sizeof(struct sk_ring_data), M_DEVBUF); + SK_IF_UNLOCK(sc_if); + + return(0); +} + +static int sk_detach(dev) + device_t dev; +{ + struct sk_softc *sc; + + sc = device_get_softc(dev); + SK_LOCK(sc); + + bus_generic_detach(dev); + if (sc->sk_devs[SK_PORT_A] != NULL) + device_delete_child(dev, sc->sk_devs[SK_PORT_A]); + if (sc->sk_devs[SK_PORT_B] != NULL) + device_delete_child(dev, sc->sk_devs[SK_PORT_B]); + + bus_teardown_intr(dev, sc->sk_irq, sc->sk_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sk_irq); + bus_release_resource(dev, SK_RES, SK_RID, sc->sk_res); + + SK_UNLOCK(sc); + mtx_destroy(&sc->sk_mtx); + + return(0); +} + +static int sk_encap(sc_if, m_head, txidx) + struct sk_if_softc *sc_if; + struct mbuf *m_head; + u_int32_t *txidx; +{ + struct sk_tx_desc *f = NULL; + struct mbuf *m; + u_int32_t frag, cur, cnt = 0; + + m = m_head; + cur = frag = *txidx; + + /* + * Start packing the mbufs in this chain into + * the fragment pointers. Stop when we run out + * of fragments or hit the end of the mbuf chain. + */ + for (m = m_head; m != NULL; m = m->m_next) { + if (m->m_len != 0) { + if ((SK_TX_RING_CNT - + (sc_if->sk_cdata.sk_tx_cnt + cnt)) < 2) + return(ENOBUFS); + f = &sc_if->sk_rdata->sk_tx_ring[frag]; + f->sk_data_lo = vtophys(mtod(m, vm_offset_t)); + f->sk_ctl = m->m_len | SK_OPCODE_DEFAULT; + if (cnt == 0) + f->sk_ctl |= SK_TXCTL_FIRSTFRAG; + else + f->sk_ctl |= SK_TXCTL_OWN; + cur = frag; + SK_INC(frag, SK_TX_RING_CNT); + cnt++; + } + } + + if (m != NULL) + return(ENOBUFS); + + sc_if->sk_rdata->sk_tx_ring[cur].sk_ctl |= + SK_TXCTL_LASTFRAG|SK_TXCTL_EOF_INTR; + sc_if->sk_cdata.sk_tx_chain[cur].sk_mbuf = m_head; + sc_if->sk_rdata->sk_tx_ring[*txidx].sk_ctl |= SK_TXCTL_OWN; + sc_if->sk_cdata.sk_tx_cnt += cnt; + + *txidx = frag; + + return(0); +} + +static void sk_start(ifp) + struct ifnet *ifp; +{ + struct sk_softc *sc; + struct sk_if_softc *sc_if; + struct mbuf *m_head = NULL; + u_int32_t idx; + + sc_if = ifp->if_softc; + sc = sc_if->sk_softc; + + SK_IF_LOCK(sc_if); + + idx = sc_if->sk_cdata.sk_tx_prod; + + while(sc_if->sk_cdata.sk_tx_chain[idx].sk_mbuf == NULL) { + IF_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 (sk_encap(sc_if, m_head, &idx)) { + IF_PREPEND(&ifp->if_snd, m_head); + ifp->if_flags |= IFF_OACTIVE; + break; + } + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, m_head); + } + + /* Transmit */ + sc_if->sk_cdata.sk_tx_prod = idx; + CSR_WRITE_4(sc, sc_if->sk_tx_bmu, SK_TXBMU_TX_START); + + /* Set a timeout in case the chip goes out to lunch. */ + ifp->if_timer = 5; + SK_IF_UNLOCK(sc_if); + + return; +} + + +static void sk_watchdog(ifp) + struct ifnet *ifp; +{ + struct sk_if_softc *sc_if; + + sc_if = ifp->if_softc; + + printf("sk%d: watchdog timeout\n", sc_if->sk_unit); + sk_init(sc_if); + + return; +} + +static void sk_shutdown(dev) + device_t dev; +{ + struct sk_softc *sc; + + sc = device_get_softc(dev); + SK_LOCK(sc); + + /* Turn off the 'driver is loaded' LED. */ + CSR_WRITE_2(sc, SK_LED, SK_LED_GREEN_OFF); + + /* + * Reset the GEnesis controller. Doing this should also + * assert the resets on the attached XMAC(s). + */ + sk_reset(sc); + SK_UNLOCK(sc); + + return; +} + +static void sk_rxeof(sc_if) + struct sk_if_softc *sc_if; +{ + struct ether_header *eh; + struct mbuf *m; + struct ifnet *ifp; + struct sk_chain *cur_rx; + int total_len = 0; + int i; + u_int32_t rxstat; + + ifp = &sc_if->arpcom.ac_if; + i = sc_if->sk_cdata.sk_rx_prod; + cur_rx = &sc_if->sk_cdata.sk_rx_chain[i]; + + while(!(sc_if->sk_rdata->sk_rx_ring[i].sk_ctl & SK_RXCTL_OWN)) { + + cur_rx = &sc_if->sk_cdata.sk_rx_chain[i]; + rxstat = sc_if->sk_rdata->sk_rx_ring[i].sk_xmac_rxstat; + m = cur_rx->sk_mbuf; + cur_rx->sk_mbuf = NULL; + total_len = SK_RXBYTES(sc_if->sk_rdata->sk_rx_ring[i].sk_ctl); + SK_INC(i, SK_RX_RING_CNT); + + if (rxstat & XM_RXSTAT_ERRFRAME) { + ifp->if_ierrors++; + sk_newbuf(sc_if, cur_rx, m); + continue; + } + + /* + * Try to allocate a new jumbo buffer. If that + * fails, copy the packet to mbufs and put the + * jumbo buffer back in the ring so it can be + * re-used. If allocating mbufs fails, then we + * have to drop the packet. + */ + if (sk_newbuf(sc_if, cur_rx, NULL) == ENOBUFS) { + struct mbuf *m0; + m0 = m_devget(mtod(m, char *), total_len, ETHER_ALIGN, + ifp, NULL); + sk_newbuf(sc_if, cur_rx, m); + if (m0 == NULL) { + printf("sk%d: no receive buffers " + "available -- packet dropped!\n", + sc_if->sk_unit); + ifp->if_ierrors++; + continue; + } + m = m0; + } else { + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = total_len; + } + + ifp->if_ipackets++; + eh = mtod(m, struct ether_header *); + + /* Remove header from mbuf and pass it on. */ + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, eh, m); + } + + sc_if->sk_cdata.sk_rx_prod = i; + + return; +} + +static void sk_txeof(sc_if) + struct sk_if_softc *sc_if; +{ + struct sk_tx_desc *cur_tx = NULL; + struct ifnet *ifp; + u_int32_t idx; + + ifp = &sc_if->arpcom.ac_if; + + /* + * Go through our tx ring and free mbufs for those + * frames that have been sent. + */ + idx = sc_if->sk_cdata.sk_tx_cons; + while(idx != sc_if->sk_cdata.sk_tx_prod) { + cur_tx = &sc_if->sk_rdata->sk_tx_ring[idx]; + if (cur_tx->sk_ctl & SK_TXCTL_OWN) + break; + if (cur_tx->sk_ctl & SK_TXCTL_LASTFRAG) + ifp->if_opackets++; + if (sc_if->sk_cdata.sk_tx_chain[idx].sk_mbuf != NULL) { + m_freem(sc_if->sk_cdata.sk_tx_chain[idx].sk_mbuf); + sc_if->sk_cdata.sk_tx_chain[idx].sk_mbuf = NULL; + } + sc_if->sk_cdata.sk_tx_cnt--; + SK_INC(idx, SK_TX_RING_CNT); + ifp->if_timer = 0; + } + + sc_if->sk_cdata.sk_tx_cons = idx; + + if (cur_tx != NULL) + ifp->if_flags &= ~IFF_OACTIVE; + + return; +} + +static void sk_tick(xsc_if) + void *xsc_if; +{ + struct sk_if_softc *sc_if; + struct mii_data *mii; + struct ifnet *ifp; + int i; + + sc_if = xsc_if; + SK_IF_LOCK(sc_if); + ifp = &sc_if->arpcom.ac_if; + mii = device_get_softc(sc_if->sk_miibus); + + if (!(ifp->if_flags & IFF_UP)) { + SK_IF_UNLOCK(sc_if); + return; + } + + if (sc_if->sk_phytype == SK_PHYTYPE_BCOM) { + sk_intr_bcom(sc_if); + SK_IF_UNLOCK(sc_if); + return; + } + + /* + * According to SysKonnect, the correct way to verify that + * the link has come back up is to poll bit 0 of the GPIO + * register three times. This pin has the signal from the + * link_sync pin connected to it; if we read the same link + * state 3 times in a row, we know the link is up. + */ + for (i = 0; i < 3; i++) { + if (SK_XM_READ_2(sc_if, XM_GPIO) & XM_GPIO_GP0_SET) + break; + } + + if (i != 3) { + sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz); + SK_IF_UNLOCK(sc_if); + return; + } + + /* Turn the GP0 interrupt back on. */ + SK_XM_CLRBIT_2(sc_if, XM_IMR, XM_IMR_GP0_SET); + SK_XM_READ_2(sc_if, XM_ISR); + mii_tick(mii); + untimeout(sk_tick, sc_if, sc_if->sk_tick_ch); + + SK_IF_UNLOCK(sc_if); + return; +} + +static void sk_intr_bcom(sc_if) + struct sk_if_softc *sc_if; +{ + struct sk_softc *sc; + struct mii_data *mii; + struct ifnet *ifp; + int status; + + sc = sc_if->sk_softc; + mii = device_get_softc(sc_if->sk_miibus); + ifp = &sc_if->arpcom.ac_if; + + SK_XM_CLRBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_TX_ENB|XM_MMUCMD_RX_ENB); + + /* + * Read the PHY interrupt register to make sure + * we clear any pending interrupts. + */ + status = sk_miibus_readreg(sc_if->sk_dev, + SK_PHYADDR_BCOM, BRGPHY_MII_ISR); + + if (!(ifp->if_flags & IFF_RUNNING)) { + sk_init_xmac(sc_if); + return; + } + + if (status & (BRGPHY_ISR_LNK_CHG|BRGPHY_ISR_AN_PR)) { + int lstat; + lstat = sk_miibus_readreg(sc_if->sk_dev, + SK_PHYADDR_BCOM, BRGPHY_MII_AUXSTS); + + if (!(lstat & BRGPHY_AUXSTS_LINK) && sc_if->sk_link) { + mii_mediachg(mii); + /* Turn off the link LED. */ + SK_IF_WRITE_1(sc_if, 0, + SK_LINKLED1_CTL, SK_LINKLED_OFF); + sc_if->sk_link = 0; + } else if (status & BRGPHY_ISR_LNK_CHG) { + sk_miibus_writereg(sc_if->sk_dev, SK_PHYADDR_BCOM, + BRGPHY_MII_IMR, 0xFF00); + mii_tick(mii); + sc_if->sk_link = 1; + /* Turn on the link LED. */ + SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, + SK_LINKLED_ON|SK_LINKLED_LINKSYNC_OFF| + SK_LINKLED_BLINK_OFF); + } else { + mii_tick(mii); + sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz); + } + } + + SK_XM_SETBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_TX_ENB|XM_MMUCMD_RX_ENB); + + return; +} + +static void sk_intr_xmac(sc_if) + struct sk_if_softc *sc_if; +{ + struct sk_softc *sc; + u_int16_t status; + struct mii_data *mii; + + sc = sc_if->sk_softc; + mii = device_get_softc(sc_if->sk_miibus); + status = SK_XM_READ_2(sc_if, XM_ISR); + + /* + * Link has gone down. Start MII tick timeout to + * watch for link resync. + */ + if (sc_if->sk_phytype == SK_PHYTYPE_XMAC) { + if (status & XM_ISR_GP0_SET) { + SK_XM_SETBIT_2(sc_if, XM_IMR, XM_IMR_GP0_SET); + sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz); + } + + if (status & XM_ISR_AUTONEG_DONE) { + sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz); + } + } + + if (status & XM_IMR_TX_UNDERRUN) + SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_FLUSH_TXFIFO); + + if (status & XM_IMR_RX_OVERRUN) + SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_FLUSH_RXFIFO); + + status = SK_XM_READ_2(sc_if, XM_ISR); + + return; +} + +static void sk_intr(xsc) + void *xsc; +{ + struct sk_softc *sc = xsc; + struct sk_if_softc *sc_if0 = NULL, *sc_if1 = NULL; + struct ifnet *ifp0 = NULL, *ifp1 = NULL; + u_int32_t status; + + SK_LOCK(sc); + + sc_if0 = sc->sk_if[SK_PORT_A]; + sc_if1 = sc->sk_if[SK_PORT_B]; + + if (sc_if0 != NULL) + ifp0 = &sc_if0->arpcom.ac_if; + if (sc_if1 != NULL) + ifp1 = &sc_if1->arpcom.ac_if; + + for (;;) { + status = CSR_READ_4(sc, SK_ISSR); + if (!(status & sc->sk_intrmask)) + break; + + /* Handle receive interrupts first. */ + if (status & SK_ISR_RX1_EOF) { + sk_rxeof(sc_if0); + CSR_WRITE_4(sc, SK_BMU_RX_CSR0, + SK_RXBMU_CLR_IRQ_EOF|SK_RXBMU_RX_START); + } + if (status & SK_ISR_RX2_EOF) { + sk_rxeof(sc_if1); + CSR_WRITE_4(sc, SK_BMU_RX_CSR1, + SK_RXBMU_CLR_IRQ_EOF|SK_RXBMU_RX_START); + } + + /* Then transmit interrupts. */ + if (status & SK_ISR_TX1_S_EOF) { + sk_txeof(sc_if0); + CSR_WRITE_4(sc, SK_BMU_TXS_CSR0, + SK_TXBMU_CLR_IRQ_EOF); + } + if (status & SK_ISR_TX2_S_EOF) { + sk_txeof(sc_if1); + CSR_WRITE_4(sc, SK_BMU_TXS_CSR1, + SK_TXBMU_CLR_IRQ_EOF); + } + + /* Then MAC interrupts. */ + if (status & SK_ISR_MAC1 && + ifp0->if_flags & IFF_RUNNING) + sk_intr_xmac(sc_if0); + + if (status & SK_ISR_MAC2 && + ifp1->if_flags & IFF_RUNNING) + sk_intr_xmac(sc_if1); + + if (status & SK_ISR_EXTERNAL_REG) { + if (ifp0 != NULL) + sk_intr_bcom(sc_if0); + if (ifp1 != NULL) + sk_intr_bcom(sc_if1); + } + } + + CSR_WRITE_4(sc, SK_IMR, sc->sk_intrmask); + + if (ifp0 != NULL && ifp0->if_snd.ifq_head != NULL) + sk_start(ifp0); + if (ifp1 != NULL && ifp1->if_snd.ifq_head != NULL) + sk_start(ifp1); + + SK_UNLOCK(sc); + + return; +} + +static void sk_init_xmac(sc_if) + struct sk_if_softc *sc_if; +{ + struct sk_softc *sc; + struct ifnet *ifp; + struct sk_bcom_hack bhack[] = { + { 0x18, 0x0c20 }, { 0x17, 0x0012 }, { 0x15, 0x1104 }, { 0x17, 0x0013 }, + { 0x15, 0x0404 }, { 0x17, 0x8006 }, { 0x15, 0x0132 }, { 0x17, 0x8006 }, + { 0x15, 0x0232 }, { 0x17, 0x800D }, { 0x15, 0x000F }, { 0x18, 0x0420 }, + { 0, 0 } }; + + sc = sc_if->sk_softc; + ifp = &sc_if->arpcom.ac_if; + + /* Unreset the XMAC. */ + SK_IF_WRITE_2(sc_if, 0, SK_TXF1_MACCTL, SK_TXMACCTL_XMAC_UNRESET); + DELAY(1000); + + /* Reset the XMAC's internal state. */ + SK_XM_SETBIT_2(sc_if, XM_GPIO, XM_GPIO_RESETMAC); + + /* Save the XMAC II revision */ + sc_if->sk_xmac_rev = XM_XMAC_REV(SK_XM_READ_4(sc_if, XM_DEVID)); + + /* + * Perform additional initialization for external PHYs, + * namely for the 1000baseTX cards that use the XMAC's + * GMII mode. + */ + if (sc_if->sk_phytype == SK_PHYTYPE_BCOM) { + int i = 0; + u_int32_t val; + + /* Take PHY out of reset. */ + val = sk_win_read_4(sc, SK_GPIO); + if (sc_if->sk_port == SK_PORT_A) + val |= SK_GPIO_DIR0|SK_GPIO_DAT0; + else + val |= SK_GPIO_DIR2|SK_GPIO_DAT2; + sk_win_write_4(sc, SK_GPIO, val); + + /* Enable GMII mode on the XMAC. */ + SK_XM_SETBIT_2(sc_if, XM_HWCFG, XM_HWCFG_GMIIMODE); + + sk_miibus_writereg(sc_if->sk_dev, SK_PHYADDR_BCOM, + BRGPHY_MII_BMCR, BRGPHY_BMCR_RESET); + DELAY(10000); + sk_miibus_writereg(sc_if->sk_dev, SK_PHYADDR_BCOM, + BRGPHY_MII_IMR, 0xFFF0); + + /* + * Early versions of the BCM5400 apparently have + * a bug that requires them to have their reserved + * registers initialized to some magic values. I don't + * know what the numbers do, I'm just the messenger. + */ + if (sk_miibus_readreg(sc_if->sk_dev, + SK_PHYADDR_BCOM, 0x03) == 0x6041) { + while(bhack[i].reg) { + sk_miibus_writereg(sc_if->sk_dev, + SK_PHYADDR_BCOM, bhack[i].reg, + bhack[i].val); + i++; + } + } + } + + /* Set station address */ + SK_XM_WRITE_2(sc_if, XM_PAR0, + *(u_int16_t *)(&sc_if->arpcom.ac_enaddr[0])); + SK_XM_WRITE_2(sc_if, XM_PAR1, + *(u_int16_t *)(&sc_if->arpcom.ac_enaddr[2])); + SK_XM_WRITE_2(sc_if, XM_PAR2, + *(u_int16_t *)(&sc_if->arpcom.ac_enaddr[4])); + SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_USE_STATION); + + if (ifp->if_flags & IFF_PROMISC) { + SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_PROMISC); + } else { + SK_XM_CLRBIT_4(sc_if, XM_MODE, XM_MODE_RX_PROMISC); + } + + if (ifp->if_flags & IFF_BROADCAST) { + SK_XM_CLRBIT_4(sc_if, XM_MODE, XM_MODE_RX_NOBROAD); + } else { + SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_NOBROAD); + } + + /* We don't need the FCS appended to the packet. */ + SK_XM_SETBIT_2(sc_if, XM_RXCMD, XM_RXCMD_STRIPFCS); + + /* We want short frames padded to 60 bytes. */ + SK_XM_SETBIT_2(sc_if, XM_TXCMD, XM_TXCMD_AUTOPAD); + + /* + * Enable the reception of all error frames. This is is + * a necessary evil due to the design of the XMAC. The + * XMAC's receive FIFO is only 8K in size, however jumbo + * frames can be up to 9000 bytes in length. When bad + * frame filtering is enabled, the XMAC's RX FIFO operates + * in 'store and forward' mode. For this to work, the + * entire frame has to fit into the FIFO, but that means + * that jumbo frames larger than 8192 bytes will be + * truncated. Disabling all bad frame filtering causes + * the RX FIFO to operate in streaming mode, in which + * case the XMAC will start transfering frames out of the + * RX FIFO as soon as the FIFO threshold is reached. + */ + SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_BADFRAMES| + XM_MODE_RX_GIANTS|XM_MODE_RX_RUNTS|XM_MODE_RX_CRCERRS| + XM_MODE_RX_INRANGELEN); + + if (ifp->if_mtu > (ETHERMTU + ETHER_HDR_LEN + ETHER_CRC_LEN)) + SK_XM_SETBIT_2(sc_if, XM_RXCMD, XM_RXCMD_BIGPKTOK); + else + SK_XM_CLRBIT_2(sc_if, XM_RXCMD, XM_RXCMD_BIGPKTOK); + + /* + * Bump up the transmit threshold. This helps hold off transmit + * underruns when we're blasting traffic from both ports at once. + */ + SK_XM_WRITE_2(sc_if, XM_TX_REQTHRESH, SK_XM_TX_FIFOTHRESH); + + /* Set multicast filter */ + sk_setmulti(sc_if); + + /* Clear and enable interrupts */ + SK_XM_READ_2(sc_if, XM_ISR); + if (sc_if->sk_phytype == SK_PHYTYPE_XMAC) + SK_XM_WRITE_2(sc_if, XM_IMR, XM_INTRS); + else + SK_XM_WRITE_2(sc_if, XM_IMR, 0xFFFF); + + /* Configure MAC arbiter */ + switch(sc_if->sk_xmac_rev) { + case XM_XMAC_REV_B2: + sk_win_write_1(sc, SK_RCINIT_RX1, SK_RCINIT_XMAC_B2); + sk_win_write_1(sc, SK_RCINIT_TX1, SK_RCINIT_XMAC_B2); + sk_win_write_1(sc, SK_RCINIT_RX2, SK_RCINIT_XMAC_B2); + sk_win_write_1(sc, SK_RCINIT_TX2, SK_RCINIT_XMAC_B2); + sk_win_write_1(sc, SK_MINIT_RX1, SK_MINIT_XMAC_B2); + sk_win_write_1(sc, SK_MINIT_TX1, SK_MINIT_XMAC_B2); + sk_win_write_1(sc, SK_MINIT_RX2, SK_MINIT_XMAC_B2); + sk_win_write_1(sc, SK_MINIT_TX2, SK_MINIT_XMAC_B2); + sk_win_write_1(sc, SK_RECOVERY_CTL, SK_RECOVERY_XMAC_B2); + break; + case XM_XMAC_REV_C1: + sk_win_write_1(sc, SK_RCINIT_RX1, SK_RCINIT_XMAC_C1); + sk_win_write_1(sc, SK_RCINIT_TX1, SK_RCINIT_XMAC_C1); + sk_win_write_1(sc, SK_RCINIT_RX2, SK_RCINIT_XMAC_C1); + sk_win_write_1(sc, SK_RCINIT_TX2, SK_RCINIT_XMAC_C1); + sk_win_write_1(sc, SK_MINIT_RX1, SK_MINIT_XMAC_C1); + sk_win_write_1(sc, SK_MINIT_TX1, SK_MINIT_XMAC_C1); + sk_win_write_1(sc, SK_MINIT_RX2, SK_MINIT_XMAC_C1); + sk_win_write_1(sc, SK_MINIT_TX2, SK_MINIT_XMAC_C1); + sk_win_write_1(sc, SK_RECOVERY_CTL, SK_RECOVERY_XMAC_B2); + break; + default: + break; + } + sk_win_write_2(sc, SK_MACARB_CTL, + SK_MACARBCTL_UNRESET|SK_MACARBCTL_FASTOE_OFF); + + sc_if->sk_link = 1; + + return; +} + +/* + * Note that to properly initialize any part of the GEnesis chip, + * you first have to take it out of reset mode. + */ +static void sk_init(xsc) + void *xsc; +{ + struct sk_if_softc *sc_if = xsc; + struct sk_softc *sc; + struct ifnet *ifp; + struct mii_data *mii; + + SK_IF_LOCK(sc_if); + + ifp = &sc_if->arpcom.ac_if; + sc = sc_if->sk_softc; + mii = device_get_softc(sc_if->sk_miibus); + + /* Cancel pending I/O and free all RX/TX buffers. */ + sk_stop(sc_if); + + /* Configure LINK_SYNC LED */ + SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, SK_LINKLED_ON); + SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, SK_LINKLED_LINKSYNC_ON); + + /* Configure RX LED */ + SK_IF_WRITE_1(sc_if, 0, SK_RXLED1_CTL, SK_RXLEDCTL_COUNTER_START); + + /* Configure TX LED */ + SK_IF_WRITE_1(sc_if, 0, SK_TXLED1_CTL, SK_TXLEDCTL_COUNTER_START); + + /* Configure I2C registers */ + + /* Configure XMAC(s) */ + sk_init_xmac(sc_if); + mii_mediachg(mii); + + /* Configure MAC FIFOs */ + SK_IF_WRITE_4(sc_if, 0, SK_RXF1_CTL, SK_FIFO_UNRESET); + SK_IF_WRITE_4(sc_if, 0, SK_RXF1_END, SK_FIFO_END); + SK_IF_WRITE_4(sc_if, 0, SK_RXF1_CTL, SK_FIFO_ON); + + SK_IF_WRITE_4(sc_if, 0, SK_TXF1_CTL, SK_FIFO_UNRESET); + SK_IF_WRITE_4(sc_if, 0, SK_TXF1_END, SK_FIFO_END); + SK_IF_WRITE_4(sc_if, 0, SK_TXF1_CTL, SK_FIFO_ON); + + /* Configure transmit arbiter(s) */ + SK_IF_WRITE_1(sc_if, 0, SK_TXAR1_COUNTERCTL, + SK_TXARCTL_ON|SK_TXARCTL_FSYNC_ON); + + /* Configure RAMbuffers */ + SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_CTLTST, SK_RBCTL_UNRESET); + SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_START, sc_if->sk_rx_ramstart); + SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_WR_PTR, sc_if->sk_rx_ramstart); + SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_RD_PTR, sc_if->sk_rx_ramstart); + SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_END, sc_if->sk_rx_ramend); + SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_CTLTST, SK_RBCTL_ON); + + SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_CTLTST, SK_RBCTL_UNRESET); + SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_CTLTST, SK_RBCTL_STORENFWD_ON); + SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_START, sc_if->sk_tx_ramstart); + SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_WR_PTR, sc_if->sk_tx_ramstart); + SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_RD_PTR, sc_if->sk_tx_ramstart); + SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_END, sc_if->sk_tx_ramend); + SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_CTLTST, SK_RBCTL_ON); + + /* Configure BMUs */ + SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_BMU_CSR, SK_RXBMU_ONLINE); + SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_CURADDR_LO, + vtophys(&sc_if->sk_rdata->sk_rx_ring[0])); + SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_CURADDR_HI, 0); + + SK_IF_WRITE_4(sc_if, 1, SK_TXQS1_BMU_CSR, SK_TXBMU_ONLINE); + SK_IF_WRITE_4(sc_if, 1, SK_TXQS1_CURADDR_LO, + vtophys(&sc_if->sk_rdata->sk_tx_ring[0])); + SK_IF_WRITE_4(sc_if, 1, SK_TXQS1_CURADDR_HI, 0); + + /* Init descriptors */ + if (sk_init_rx_ring(sc_if) == ENOBUFS) { + printf("sk%d: initialization failed: no " + "memory for rx buffers\n", sc_if->sk_unit); + sk_stop(sc_if); + SK_IF_UNLOCK(sc_if); + return; + } + sk_init_tx_ring(sc_if); + + /* Configure interrupt handling */ + CSR_READ_4(sc, SK_ISSR); + if (sc_if->sk_port == SK_PORT_A) + sc->sk_intrmask |= SK_INTRS1; + else + sc->sk_intrmask |= SK_INTRS2; + + sc->sk_intrmask |= SK_ISR_EXTERNAL_REG; + + CSR_WRITE_4(sc, SK_IMR, sc->sk_intrmask); + + /* Start BMUs. */ + SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_BMU_CSR, SK_RXBMU_RX_START); + + /* Enable XMACs TX and RX state machines */ + SK_XM_CLRBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_IGNPAUSE); + SK_XM_SETBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_TX_ENB|XM_MMUCMD_RX_ENB); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + SK_IF_UNLOCK(sc_if); + + return; +} + +static void sk_stop(sc_if) + struct sk_if_softc *sc_if; +{ + int i; + struct sk_softc *sc; + struct ifnet *ifp; + + SK_IF_LOCK(sc_if); + sc = sc_if->sk_softc; + ifp = &sc_if->arpcom.ac_if; + + untimeout(sk_tick, sc_if, sc_if->sk_tick_ch); + + if (sc_if->sk_phytype == SK_PHYTYPE_BCOM) { + u_int32_t val; + + /* Put PHY back into reset. */ + val = sk_win_read_4(sc, SK_GPIO); + if (sc_if->sk_port == SK_PORT_A) { + val |= SK_GPIO_DIR0; + val &= ~SK_GPIO_DAT0; + } else { + val |= SK_GPIO_DIR2; + val &= ~SK_GPIO_DAT2; + } + sk_win_write_4(sc, SK_GPIO, val); + } + + /* Turn off various components of this interface. */ + SK_XM_SETBIT_2(sc_if, XM_GPIO, XM_GPIO_RESETMAC); + SK_IF_WRITE_2(sc_if, 0, SK_TXF1_MACCTL, SK_TXMACCTL_XMAC_RESET); + SK_IF_WRITE_4(sc_if, 0, SK_RXF1_CTL, SK_FIFO_RESET); + SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_BMU_CSR, SK_RXBMU_OFFLINE); + SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_CTLTST, SK_RBCTL_RESET|SK_RBCTL_OFF); + SK_IF_WRITE_4(sc_if, 1, SK_TXQS1_BMU_CSR, SK_TXBMU_OFFLINE); + SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_CTLTST, SK_RBCTL_RESET|SK_RBCTL_OFF); + SK_IF_WRITE_1(sc_if, 0, SK_TXAR1_COUNTERCTL, SK_TXARCTL_OFF); + SK_IF_WRITE_1(sc_if, 0, SK_RXLED1_CTL, SK_RXLEDCTL_COUNTER_STOP); + SK_IF_WRITE_1(sc_if, 0, SK_TXLED1_CTL, SK_RXLEDCTL_COUNTER_STOP); + SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, SK_LINKLED_OFF); + SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, SK_LINKLED_LINKSYNC_OFF); + + /* Disable interrupts */ + if (sc_if->sk_port == SK_PORT_A) + sc->sk_intrmask &= ~SK_INTRS1; + else + sc->sk_intrmask &= ~SK_INTRS2; + CSR_WRITE_4(sc, SK_IMR, sc->sk_intrmask); + + SK_XM_READ_2(sc_if, XM_ISR); + SK_XM_WRITE_2(sc_if, XM_IMR, 0xFFFF); + + /* Free RX and TX mbufs still in the queues. */ + for (i = 0; i < SK_RX_RING_CNT; i++) { + if (sc_if->sk_cdata.sk_rx_chain[i].sk_mbuf != NULL) { + m_freem(sc_if->sk_cdata.sk_rx_chain[i].sk_mbuf); + sc_if->sk_cdata.sk_rx_chain[i].sk_mbuf = NULL; + } + } + + for (i = 0; i < SK_TX_RING_CNT; i++) { + if (sc_if->sk_cdata.sk_tx_chain[i].sk_mbuf != NULL) { + m_freem(sc_if->sk_cdata.sk_tx_chain[i].sk_mbuf); + sc_if->sk_cdata.sk_tx_chain[i].sk_mbuf = NULL; + } + } + + ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); + SK_IF_UNLOCK(sc_if); + return; +} diff --git a/sys/pci/if_skreg.h b/sys/pci/if_skreg.h new file mode 100644 index 0000000..061707c --- /dev/null +++ b/sys/pci/if_skreg.h @@ -0,0 +1,1223 @@ +/* + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * SysKonnect PCI vendor ID + */ +#define SK_VENDORID 0x1148 + +/* + * SK-NET gigabit ethernet device ID + */ +#define SK_DEVICEID_GE 0x4300 + +/* + * GEnesis registers. The GEnesis chip has a 256-byte I/O window + * but internally it has a 16K register space. This 16K space is + * divided into 128-byte blocks. The first 128 bytes of the I/O + * window represent the first block, which is permanently mapped + * at the start of the window. The other 127 blocks can be mapped + * to the second 128 bytes of the I/O window by setting the desired + * block value in the RAP register in block 0. Not all of the 127 + * blocks are actually used. Most registers are 32 bits wide, but + * there are a few 16-bit and 8-bit ones as well. + */ + + +/* Start of remappable register window. */ +#define SK_WIN_BASE 0x0080 + +/* Size of a window */ +#define SK_WIN_LEN 0x80 + +#define SK_WIN_MASK 0x3F80 +#define SK_REG_MASK 0x7F + +/* Compute the window of a given register (for the RAP register) */ +#define SK_WIN(reg) (((reg) & SK_WIN_MASK) / SK_WIN_LEN) + +/* Compute the relative offset of a register within the window */ +#define SK_REG(reg) ((reg) & SK_REG_MASK) + +#define SK_PORT_A 0 +#define SK_PORT_B 1 + +/* + * Compute offset of port-specific register. Since there are two + * ports, there are two of some GEnesis modules (e.g. two sets of + * DMA queues, two sets of FIFO control registers, etc...). Normally, + * the block for port 0 is at offset 0x0 and the block for port 1 is + * at offset 0x80 (i.e. the next page over). However for the transmit + * BMUs and RAMbuffers, there are two blocks for each port: one for + * the sync transmit queue and one for the async queue (which we don't + * use). However instead of ordering them like this: + * TX sync 1 / TX sync 2 / TX async 1 / TX async 2 + * SysKonnect has instead ordered them like this: + * TX sync 1 / TX async 1 / TX sync 2 / TX async 2 + * This means that when referencing the TX BMU and RAMbuffer registers, + * we have to double the block offset (0x80 * 2) in order to reach the + * second queue. This prevents us from using the same formula + * (sk_port * 0x80) to compute the offsets for all of the port-specific + * blocks: we need an extra offset for the BMU and RAMbuffer registers. + * The simplest thing is to provide an extra argument to these macros: + * the 'skip' parameter. The 'skip' value is the number of extra pages + * for skip when computing the port0/port1 offsets. For most registers, + * the skip value is 0; for the BMU and RAMbuffer registers, it's 1. + */ +#define SK_IF_READ_4(sc_if, skip, reg) \ + sk_win_read_4(sc_if->sk_softc, reg + \ + ((sc_if->sk_port * (skip + 1)) * SK_WIN_LEN)) +#define SK_IF_READ_2(sc_if, skip, reg) \ + sk_win_read_2(sc_if->sk_softc, reg + \ + ((sc_if->sk_port * (skip + 1)) * SK_WIN_LEN)) +#define SK_IF_READ_1(sc_if, skip, reg) \ + sk_win_read_1(sc_if->sk_softc, reg + \ + ((sc_if->sk_port * (skip + 1)) * SK_WIN_LEN)) + +#define SK_IF_WRITE_4(sc_if, skip, reg, val) \ + sk_win_write_4(sc_if->sk_softc, \ + reg + ((sc_if->sk_port * (skip + 1)) * SK_WIN_LEN), val) +#define SK_IF_WRITE_2(sc_if, skip, reg, val) \ + sk_win_write_2(sc_if->sk_softc, \ + reg + ((sc_if->sk_port * (skip + 1)) * SK_WIN_LEN), val) +#define SK_IF_WRITE_1(sc_if, skip, reg, val) \ + sk_win_write_1(sc_if->sk_softc, \ + reg + ((sc_if->sk_port * (skip + 1)) * SK_WIN_LEN), val) + +/* Block 0 registers, permanently mapped at iobase. */ +#define SK_RAP 0x0000 +#define SK_CSR 0x0004 +#define SK_LED 0x0006 +#define SK_ISR 0x0008 /* interrupt source */ +#define SK_IMR 0x000C /* interrupt mask */ +#define SK_IESR 0x0010 /* interrupt hardware error source */ +#define SK_IEMR 0x0014 /* interrupt hardware error mask */ +#define SK_ISSR 0x0018 /* special interrupt source */ +#define SK_XM_IMR0 0x0020 +#define SK_XM_ISR0 0x0028 +#define SK_XM_PHYADDR0 0x0030 +#define SK_XM_PHYDATA0 0x0034 +#define SK_XM_IMR1 0x0040 +#define SK_XM_ISR1 0x0048 +#define SK_XM_PHYADDR1 0x0050 +#define SK_XM_PHYDATA1 0x0054 +#define SK_BMU_RX_CSR0 0x0060 +#define SK_BMU_RX_CSR1 0x0064 +#define SK_BMU_TXS_CSR0 0x0068 +#define SK_BMU_TXA_CSR0 0x006C +#define SK_BMU_TXS_CSR1 0x0070 +#define SK_BMU_TXA_CSR1 0x0074 + +/* SK_CSR register */ +#define SK_CSR_SW_RESET 0x0001 +#define SK_CSR_SW_UNRESET 0x0002 +#define SK_CSR_MASTER_RESET 0x0004 +#define SK_CSR_MASTER_UNRESET 0x0008 +#define SK_CSR_MASTER_STOP 0x0010 +#define SK_CSR_MASTER_DONE 0x0020 +#define SK_CSR_SW_IRQ_CLEAR 0x0040 +#define SK_CSR_SW_IRQ_SET 0x0080 +#define SK_CSR_SLOTSIZE 0x0100 /* 1 == 64 bits, 0 == 32 */ +#define SK_CSR_BUSCLOCK 0x0200 /* 1 == 33/66 Mhz, = 33 */ + +/* SK_LED register */ +#define SK_LED_GREEN_OFF 0x01 +#define SK_LED_GREEN_ON 0x02 + +/* SK_ISR register */ +#define SK_ISR_TX2_AS_CHECK 0x00000001 +#define SK_ISR_TX2_AS_EOF 0x00000002 +#define SK_ISR_TX2_AS_EOB 0x00000004 +#define SK_ISR_TX2_S_CHECK 0x00000008 +#define SK_ISR_TX2_S_EOF 0x00000010 +#define SK_ISR_TX2_S_EOB 0x00000020 +#define SK_ISR_TX1_AS_CHECK 0x00000040 +#define SK_ISR_TX1_AS_EOF 0x00000080 +#define SK_ISR_TX1_AS_EOB 0x00000100 +#define SK_ISR_TX1_S_CHECK 0x00000200 +#define SK_ISR_TX1_S_EOF 0x00000400 +#define SK_ISR_TX1_S_EOB 0x00000800 +#define SK_ISR_RX2_CHECK 0x00001000 +#define SK_ISR_RX2_EOF 0x00002000 +#define SK_ISR_RX2_EOB 0x00004000 +#define SK_ISR_RX1_CHECK 0x00008000 +#define SK_ISR_RX1_EOF 0x00010000 +#define SK_ISR_RX1_EOB 0x00020000 +#define SK_ISR_LINK2_OFLOW 0x00040000 +#define SK_ISR_MAC2 0x00080000 +#define SK_ISR_LINK1_OFLOW 0x00100000 +#define SK_ISR_MAC1 0x00200000 +#define SK_ISR_TIMER 0x00400000 +#define SK_ISR_EXTERNAL_REG 0x00800000 +#define SK_ISR_SW 0x01000000 +#define SK_ISR_I2C_RDY 0x02000000 +#define SK_ISR_TX2_TIMEO 0x04000000 +#define SK_ISR_TX1_TIMEO 0x08000000 +#define SK_ISR_RX2_TIMEO 0x10000000 +#define SK_ISR_RX1_TIMEO 0x20000000 +#define SK_ISR_RSVD 0x40000000 +#define SK_ISR_HWERR 0x80000000 + +/* SK_IMR register */ +#define SK_IMR_TX2_AS_CHECK 0x00000001 +#define SK_IMR_TX2_AS_EOF 0x00000002 +#define SK_IMR_TX2_AS_EOB 0x00000004 +#define SK_IMR_TX2_S_CHECK 0x00000008 +#define SK_IMR_TX2_S_EOF 0x00000010 +#define SK_IMR_TX2_S_EOB 0x00000020 +#define SK_IMR_TX1_AS_CHECK 0x00000040 +#define SK_IMR_TX1_AS_EOF 0x00000080 +#define SK_IMR_TX1_AS_EOB 0x00000100 +#define SK_IMR_TX1_S_CHECK 0x00000200 +#define SK_IMR_TX1_S_EOF 0x00000400 +#define SK_IMR_TX1_S_EOB 0x00000800 +#define SK_IMR_RX2_CHECK 0x00001000 +#define SK_IMR_RX2_EOF 0x00002000 +#define SK_IMR_RX2_EOB 0x00004000 +#define SK_IMR_RX1_CHECK 0x00008000 +#define SK_IMR_RX1_EOF 0x00010000 +#define SK_IMR_RX1_EOB 0x00020000 +#define SK_IMR_LINK2_OFLOW 0x00040000 +#define SK_IMR_MAC2 0x00080000 +#define SK_IMR_LINK1_OFLOW 0x00100000 +#define SK_IMR_MAC1 0x00200000 +#define SK_IMR_TIMER 0x00400000 +#define SK_IMR_EXTERNAL_REG 0x00800000 +#define SK_IMR_SW 0x01000000 +#define SK_IMR_I2C_RDY 0x02000000 +#define SK_IMR_TX2_TIMEO 0x04000000 +#define SK_IMR_TX1_TIMEO 0x08000000 +#define SK_IMR_RX2_TIMEO 0x10000000 +#define SK_IMR_RX1_TIMEO 0x20000000 +#define SK_IMR_RSVD 0x40000000 +#define SK_IMR_HWERR 0x80000000 + +#define SK_INTRS1 \ + (SK_IMR_RX1_EOF|SK_IMR_TX1_S_EOF|SK_IMR_MAC1) + +#define SK_INTRS2 \ + (SK_IMR_RX2_EOF|SK_IMR_TX2_S_EOF|SK_IMR_MAC2) + +/* SK_IESR register */ +#define SK_IESR_PAR_RX2 0x00000001 +#define SK_IESR_PAR_RX1 0x00000002 +#define SK_IESR_PAR_MAC2 0x00000004 +#define SK_IESR_PAR_MAC1 0x00000008 +#define SK_IESR_PAR_WR_RAM 0x00000010 +#define SK_IESR_PAR_RD_RAM 0x00000020 +#define SK_IESR_NO_TSTAMP_MAC2 0x00000040 +#define SK_IESR_NO_TSTAMO_MAC1 0x00000080 +#define SK_IESR_NO_STS_MAC2 0x00000100 +#define SK_IESR_NO_STS_MAC1 0x00000200 +#define SK_IESR_IRQ_STS 0x00000400 +#define SK_IESR_MASTERERR 0x00000800 + +/* SK_IEMR register */ +#define SK_IEMR_PAR_RX2 0x00000001 +#define SK_IEMR_PAR_RX1 0x00000002 +#define SK_IEMR_PAR_MAC2 0x00000004 +#define SK_IEMR_PAR_MAC1 0x00000008 +#define SK_IEMR_PAR_WR_RAM 0x00000010 +#define SK_IEMR_PAR_RD_RAM 0x00000020 +#define SK_IEMR_NO_TSTAMP_MAC2 0x00000040 +#define SK_IEMR_NO_TSTAMO_MAC1 0x00000080 +#define SK_IEMR_NO_STS_MAC2 0x00000100 +#define SK_IEMR_NO_STS_MAC1 0x00000200 +#define SK_IEMR_IRQ_STS 0x00000400 +#define SK_IEMR_MASTERERR 0x00000800 + +/* Block 2 */ +#define SK_MAC0_0 0x0100 +#define SK_MAC0_1 0x0104 +#define SK_MAC1_0 0x0108 +#define SK_MAC1_1 0x010C +#define SK_MAC2_0 0x0110 +#define SK_MAC2_1 0x0114 +#define SK_CONNTYPE 0x0118 +#define SK_PMDTYPE 0x0119 +#define SK_CONFIG 0x011A +#define SK_CHIPVER 0x011B +#define SK_EPROM0 0x011C +#define SK_EPROM1 0x011D +#define SK_EPROM2 0x011E +#define SK_EPROM3 0x011F +#define SK_EP_ADDR 0x0120 +#define SK_EP_DATA 0x0124 +#define SK_EP_LOADCTL 0x0128 +#define SK_EP_LOADTST 0x0129 +#define SK_TIMERINIT 0x0130 +#define SK_TIMER 0x0134 +#define SK_TIMERCTL 0x0138 +#define SK_TIMERTST 0x0139 +#define SK_IMTIMERINIT 0x0140 +#define SK_IMTIMER 0x0144 +#define SK_IMTIMERCTL 0x0148 +#define SK_IMTIMERTST 0x0149 +#define SK_IMMR 0x014C +#define SK_IHWEMR 0x0150 +#define SK_TESTCTL1 0x0158 +#define SK_TESTCTL2 0x0159 +#define SK_GPIO 0x015C +#define SK_I2CHWCTL 0x0160 +#define SK_I2CHWDATA 0x0164 +#define SK_I2CHWIRQ 0x0168 +#define SK_I2CSW 0x016C +#define SK_BLNKINIT 0x0170 +#define SK_BLNKCOUNT 0x0174 +#define SK_BLNKCTL 0x0178 +#define SK_BLNKSTS 0x0179 +#define SK_BLNKTST 0x017A + +#define SK_IMCTL_STOP 0x02 +#define SK_IMCTL_START 0x04 + +#define SK_IMTIMER_TICKS 54 +#define SK_IM_USECS(x) ((x) * SK_IMTIMER_TICKS) + +/* + * The SK_EPROM0 register contains a byte that describes the + * amount of SRAM mounted on the NIC. The value also tells if + * the chips are 64K or 128K. This affects the RAMbuffer address + * offset that we need to use. + */ +#define SK_RAMSIZE_512K_64 0x1 +#define SK_RAMSIZE_1024K_128 0x2 +#define SK_RAMSIZE_1024K_64 0x3 +#define SK_RAMSIZE_2048K_128 0x4 + +#define SK_RBOFF_0 0x0 +#define SK_RBOFF_80000 0x80000 + +/* + * SK_EEPROM1 contains the PHY type, which may be XMAC for + * fiber-based cards or BCOM for 1000baseT cards with a Broadcom + * PHY. + */ +#define SK_PHYTYPE_XMAC 0 /* integeated XMAC II PHY */ +#define SK_PHYTYPE_BCOM 1 /* Broadcom BCM5400 */ +#define SK_PHYTYPE_LONE 2 /* Level One LXT1000 */ +#define SK_PHYTYPE_NAT 3 /* National DP83891 */ + +/* + * PHY addresses. + */ +#define SK_PHYADDR_XMAC 0x0 +#define SK_PHYADDR_BCOM 0x1 +#define SK_PHYADDR_LONE 0x3 +#define SK_PHYADDR_NAT 0x0 + +#define SK_CONFIG_SINGLEMAC 0x01 +#define SK_CONFIG_DIS_DSL_CLK 0x02 + +#define SK_PMD_1000BASELX 0x4C +#define SK_PMD_1000BASESX 0x53 +#define SK_PMD_1000BASECX 0x43 +#define SK_PMD_1000BASETX 0x54 + +/* GPIO bits */ +#define SK_GPIO_DAT0 0x00000001 +#define SK_GPIO_DAT1 0x00000002 +#define SK_GPIO_DAT2 0x00000004 +#define SK_GPIO_DAT3 0x00000008 +#define SK_GPIO_DAT4 0x00000010 +#define SK_GPIO_DAT5 0x00000020 +#define SK_GPIO_DAT6 0x00000040 +#define SK_GPIO_DAT7 0x00000080 +#define SK_GPIO_DAT8 0x00000100 +#define SK_GPIO_DAT9 0x00000200 +#define SK_GPIO_DIR0 0x00010000 +#define SK_GPIO_DIR1 0x00020000 +#define SK_GPIO_DIR2 0x00040000 +#define SK_GPIO_DIR3 0x00080000 +#define SK_GPIO_DIR4 0x00100000 +#define SK_GPIO_DIR5 0x00200000 +#define SK_GPIO_DIR6 0x00400000 +#define SK_GPIO_DIR7 0x00800000 +#define SK_GPIO_DIR8 0x01000000 +#define SK_GPIO_DIR9 0x02000000 + +/* Block 3 Ram interface and MAC arbiter registers */ +#define SK_RAMADDR 0x0180 +#define SK_RAMDATA0 0x0184 +#define SK_RAMDATA1 0x0188 +#define SK_TO0 0x0190 +#define SK_TO1 0x0191 +#define SK_TO2 0x0192 +#define SK_TO3 0x0193 +#define SK_TO4 0x0194 +#define SK_TO5 0x0195 +#define SK_TO6 0x0196 +#define SK_TO7 0x0197 +#define SK_TO8 0x0198 +#define SK_TO9 0x0199 +#define SK_TO10 0x019A +#define SK_TO11 0x019B +#define SK_RITIMEO_TMR 0x019C +#define SK_RAMCTL 0x01A0 +#define SK_RITIMER_TST 0x01A2 + +#define SK_RAMCTL_RESET 0x0001 +#define SK_RAMCTL_UNRESET 0x0002 +#define SK_RAMCTL_CLR_IRQ_WPAR 0x0100 +#define SK_RAMCTL_CLR_IRQ_RPAR 0x0200 + +/* Mac arbiter registers */ +#define SK_MINIT_RX1 0x01B0 +#define SK_MINIT_RX2 0x01B1 +#define SK_MINIT_TX1 0x01B2 +#define SK_MINIT_TX2 0x01B3 +#define SK_MTIMEO_RX1 0x01B4 +#define SK_MTIMEO_RX2 0x01B5 +#define SK_MTIMEO_TX1 0x01B6 +#define SK_MTIEMO_TX2 0x01B7 +#define SK_MACARB_CTL 0x01B8 +#define SK_MTIMER_TST 0x01BA +#define SK_RCINIT_RX1 0x01C0 +#define SK_RCINIT_RX2 0x01C1 +#define SK_RCINIT_TX1 0x01C2 +#define SK_RCINIT_TX2 0x01C3 +#define SK_RCTIMEO_RX1 0x01C4 +#define SK_RCTIMEO_RX2 0x01C5 +#define SK_RCTIMEO_TX1 0x01C6 +#define SK_RCTIMEO_TX2 0x01C7 +#define SK_RECOVERY_CTL 0x01C8 +#define SK_RCTIMER_TST 0x01CA + +/* Packet arbiter registers */ +#define SK_RXPA1_TINIT 0x01D0 +#define SK_RXPA2_TINIT 0x01D4 +#define SK_TXPA1_TINIT 0x01D8 +#define SK_TXPA2_TINIT 0x01DC +#define SK_RXPA1_TIMEO 0x01E0 +#define SK_RXPA2_TIMEO 0x01E4 +#define SK_TXPA1_TIMEO 0x01E8 +#define SK_TXPA2_TIMEO 0x01EC +#define SK_PKTARB_CTL 0x01F0 +#define SK_PKTATB_TST 0x01F2 + +#define SK_PKTARB_TIMEOUT 0x2000 + +#define SK_PKTARBCTL_RESET 0x0001 +#define SK_PKTARBCTL_UNRESET 0x0002 +#define SK_PKTARBCTL_RXTO1_OFF 0x0004 +#define SK_PKTARBCTL_RXTO1_ON 0x0008 +#define SK_PKTARBCTL_RXTO2_OFF 0x0010 +#define SK_PKTARBCTL_RXTO2_ON 0x0020 +#define SK_PKTARBCTL_TXTO1_OFF 0x0040 +#define SK_PKTARBCTL_TXTO1_ON 0x0080 +#define SK_PKTARBCTL_TXTO2_OFF 0x0100 +#define SK_PKTARBCTL_TXTO2_ON 0x0200 +#define SK_PKTARBCTL_CLR_IRQ_RXTO1 0x0400 +#define SK_PKTARBCTL_CLR_IRQ_RXTO2 0x0800 +#define SK_PKTARBCTL_CLR_IRQ_TXTO1 0x1000 +#define SK_PKTARBCTL_CLR_IRQ_TXTO2 0x2000 + +#define SK_MINIT_XMAC_B2 54 +#define SK_MINIT_XMAC_C1 63 + +#define SK_MACARBCTL_RESET 0x0001 +#define SK_MACARBCTL_UNRESET 0x0002 +#define SK_MACARBCTL_FASTOE_OFF 0x0004 +#define SK_MACARBCRL_FASTOE_ON 0x0008 + +#define SK_RCINIT_XMAC_B2 54 +#define SK_RCINIT_XMAC_C1 0 + +#define SK_RECOVERYCTL_RX1_OFF 0x0001 +#define SK_RECOVERYCTL_RX1_ON 0x0002 +#define SK_RECOVERYCTL_RX2_OFF 0x0004 +#define SK_RECOVERYCTL_RX2_ON 0x0008 +#define SK_RECOVERYCTL_TX1_OFF 0x0010 +#define SK_RECOVERYCTL_TX1_ON 0x0020 +#define SK_RECOVERYCTL_TX2_OFF 0x0040 +#define SK_RECOVERYCTL_TX2_ON 0x0080 + +#define SK_RECOVERY_XMAC_B2 \ + (SK_RECOVERYCTL_RX1_ON|SK_RECOVERYCTL_RX2_ON| \ + SK_RECOVERYCTL_TX1_ON|SK_RECOVERYCTL_TX2_ON) + +#define SK_RECOVERY_XMAC_C1 \ + (SK_RECOVERYCTL_RX1_OFF|SK_RECOVERYCTL_RX2_OFF| \ + SK_RECOVERYCTL_TX1_OFF|SK_RECOVERYCTL_TX2_OFF) + +/* Block 4 -- TX Arbiter MAC 1 */ +#define SK_TXAR1_TIMERINIT 0x0200 +#define SK_TXAR1_TIMERVAL 0x0204 +#define SK_TXAR1_LIMITINIT 0x0208 +#define SK_TXAR1_LIMITCNT 0x020C +#define SK_TXAR1_COUNTERCTL 0x0210 +#define SK_TXAR1_COUNTERTST 0x0212 +#define SK_TXAR1_COUNTERSTS 0x0212 + +/* Block 5 -- TX Arbiter MAC 2 */ +#define SK_TXAR2_TIMERINIT 0x0280 +#define SK_TXAR2_TIMERVAL 0x0284 +#define SK_TXAR2_LIMITINIT 0x0288 +#define SK_TXAR2_LIMITCNT 0x028C +#define SK_TXAR2_COUNTERCTL 0x0290 +#define SK_TXAR2_COUNTERTST 0x0291 +#define SK_TXAR2_COUNTERSTS 0x0292 + +#define SK_TXARCTL_OFF 0x01 +#define SK_TXARCTL_ON 0x02 +#define SK_TXARCTL_RATECTL_OFF 0x04 +#define SK_TXARCTL_RATECTL_ON 0x08 +#define SK_TXARCTL_ALLOC_OFF 0x10 +#define SK_TXARCTL_ALLOC_ON 0x20 +#define SK_TXARCTL_FSYNC_OFF 0x40 +#define SK_TXARCTL_FSYNC_ON 0x80 + +/* Block 6 -- External registers */ +#define SK_EXTREG_BASE 0x300 +#define SK_EXTREG_END 0x37C + +/* Block 7 -- PCI config registers */ +#define SK_PCI_BASE 0x0380 +#define SK_PCI_END 0x03FC + +/* Compute offset of mirrored PCI register */ +#define SK_PCI_REG(reg) ((reg) + SK_PCI_BASE) + +/* Block 8 -- RX queue 1 */ +#define SK_RXQ1_BUFCNT 0x0400 +#define SK_RXQ1_BUFCTL 0x0402 +#define SK_RXQ1_NEXTDESC 0x0404 +#define SK_RXQ1_RXBUF_LO 0x0408 +#define SK_RXQ1_RXBUF_HI 0x040C +#define SK_RXQ1_RXSTAT 0x0410 +#define SK_RXQ1_TIMESTAMP 0x0414 +#define SK_RXQ1_CSUM1 0x0418 +#define SK_RXQ1_CSUM2 0x041A +#define SK_RXQ1_CSUM1_START 0x041C +#define SK_RXQ1_CSUM2_START 0x041E +#define SK_RXQ1_CURADDR_LO 0x0420 +#define SK_RXQ1_CURADDR_HI 0x0424 +#define SK_RXQ1_CURCNT_LO 0x0428 +#define SK_RXQ1_CURCNT_HI 0x042C +#define SK_RXQ1_CURBYTES 0x0430 +#define SK_RXQ1_BMU_CSR 0x0434 +#define SK_RXQ1_WATERMARK 0x0438 +#define SK_RXQ1_FLAG 0x043A +#define SK_RXQ1_TEST1 0x043C +#define SK_RXQ1_TEST2 0x0440 +#define SK_RXQ1_TEST3 0x0444 + +/* Block 9 -- RX queue 2 */ +#define SK_RXQ2_BUFCNT 0x0480 +#define SK_RXQ2_BUFCTL 0x0482 +#define SK_RXQ2_NEXTDESC 0x0484 +#define SK_RXQ2_RXBUF_LO 0x0488 +#define SK_RXQ2_RXBUF_HI 0x048C +#define SK_RXQ2_RXSTAT 0x0490 +#define SK_RXQ2_TIMESTAMP 0x0494 +#define SK_RXQ2_CSUM1 0x0498 +#define SK_RXQ2_CSUM2 0x049A +#define SK_RXQ2_CSUM1_START 0x049C +#define SK_RXQ2_CSUM2_START 0x049E +#define SK_RXQ2_CURADDR_LO 0x04A0 +#define SK_RXQ2_CURADDR_HI 0x04A4 +#define SK_RXQ2_CURCNT_LO 0x04A8 +#define SK_RXQ2_CURCNT_HI 0x04AC +#define SK_RXQ2_CURBYTES 0x04B0 +#define SK_RXQ2_BMU_CSR 0x04B4 +#define SK_RXQ2_WATERMARK 0x04B8 +#define SK_RXQ2_FLAG 0x04BA +#define SK_RXQ2_TEST1 0x04BC +#define SK_RXQ2_TEST2 0x04C0 +#define SK_RXQ2_TEST3 0x04C4 + +#define SK_RXBMU_CLR_IRQ_ERR 0x00000001 +#define SK_RXBMU_CLR_IRQ_EOF 0x00000002 +#define SK_RXBMU_CLR_IRQ_EOB 0x00000004 +#define SK_RXBMU_CLR_IRQ_PAR 0x00000008 +#define SK_RXBMU_RX_START 0x00000010 +#define SK_RXBMU_RX_STOP 0x00000020 +#define SK_RXBMU_POLL_OFF 0x00000040 +#define SK_RXBMU_POLL_ON 0x00000080 +#define SK_RXBMU_TRANSFER_SM_RESET 0x00000100 +#define SK_RXBMU_TRANSFER_SM_UNRESET 0x00000200 +#define SK_RXBMU_DESCWR_SM_RESET 0x00000400 +#define SK_RXBMU_DESCWR_SM_UNRESET 0x00000800 +#define SK_RXBMU_DESCRD_SM_RESET 0x00001000 +#define SK_RXBMU_DESCRD_SM_UNRESET 0x00002000 +#define SK_RXBMU_SUPERVISOR_SM_RESET 0x00004000 +#define SK_RXBMU_SUPERVISOR_SM_UNRESET 0x00008000 +#define SK_RXBMU_PFI_SM_RESET 0x00010000 +#define SK_RXBMU_PFI_SM_UNRESET 0x00020000 +#define SK_RXBMU_FIFO_RESET 0x00040000 +#define SK_RXBMU_FIFO_UNRESET 0x00080000 +#define SK_RXBMU_DESC_RESET 0x00100000 +#define SK_RXBMU_DESC_UNRESET 0x00200000 +#define SK_RXBMU_SUPERVISOR_IDLE 0x01000000 + +#define SK_RXBMU_ONLINE \ + (SK_RXBMU_TRANSFER_SM_UNRESET|SK_RXBMU_DESCWR_SM_UNRESET| \ + SK_RXBMU_DESCRD_SM_UNRESET|SK_RXBMU_SUPERVISOR_SM_UNRESET| \ + SK_RXBMU_PFI_SM_UNRESET|SK_RXBMU_FIFO_UNRESET| \ + SK_RXBMU_DESC_UNRESET) + +#define SK_RXBMU_OFFLINE \ + (SK_RXBMU_TRANSFER_SM_RESET|SK_RXBMU_DESCWR_SM_RESET| \ + SK_RXBMU_DESCRD_SM_RESET|SK_RXBMU_SUPERVISOR_SM_RESET| \ + SK_RXBMU_PFI_SM_RESET|SK_RXBMU_FIFO_RESET| \ + SK_RXBMU_DESC_RESET) + +/* Block 12 -- TX sync queue 1 */ +#define SK_TXQS1_BUFCNT 0x0600 +#define SK_TXQS1_BUFCTL 0x0602 +#define SK_TXQS1_NEXTDESC 0x0604 +#define SK_TXQS1_RXBUF_LO 0x0608 +#define SK_TXQS1_RXBUF_HI 0x060C +#define SK_TXQS1_RXSTAT 0x0610 +#define SK_TXQS1_CSUM_STARTVAL 0x0614 +#define SK_TXQS1_CSUM_STARTPOS 0x0618 +#define SK_TXQS1_CSUM_WRITEPOS 0x061A +#define SK_TXQS1_CURADDR_LO 0x0620 +#define SK_TXQS1_CURADDR_HI 0x0624 +#define SK_TXQS1_CURCNT_LO 0x0628 +#define SK_TXQS1_CURCNT_HI 0x062C +#define SK_TXQS1_CURBYTES 0x0630 +#define SK_TXQS1_BMU_CSR 0x0634 +#define SK_TXQS1_WATERMARK 0x0638 +#define SK_TXQS1_FLAG 0x063A +#define SK_TXQS1_TEST1 0x063C +#define SK_TXQS1_TEST2 0x0640 +#define SK_TXQS1_TEST3 0x0644 + +/* Block 13 -- TX async queue 1 */ +#define SK_TXQA1_BUFCNT 0x0680 +#define SK_TXQA1_BUFCTL 0x0682 +#define SK_TXQA1_NEXTDESC 0x0684 +#define SK_TXQA1_RXBUF_LO 0x0688 +#define SK_TXQA1_RXBUF_HI 0x068C +#define SK_TXQA1_RXSTAT 0x0690 +#define SK_TXQA1_CSUM_STARTVAL 0x0694 +#define SK_TXQA1_CSUM_STARTPOS 0x0698 +#define SK_TXQA1_CSUM_WRITEPOS 0x069A +#define SK_TXQA1_CURADDR_LO 0x06A0 +#define SK_TXQA1_CURADDR_HI 0x06A4 +#define SK_TXQA1_CURCNT_LO 0x06A8 +#define SK_TXQA1_CURCNT_HI 0x06AC +#define SK_TXQA1_CURBYTES 0x06B0 +#define SK_TXQA1_BMU_CSR 0x06B4 +#define SK_TXQA1_WATERMARK 0x06B8 +#define SK_TXQA1_FLAG 0x06BA +#define SK_TXQA1_TEST1 0x06BC +#define SK_TXQA1_TEST2 0x06C0 +#define SK_TXQA1_TEST3 0x06C4 + +/* Block 14 -- TX sync queue 2 */ +#define SK_TXQS2_BUFCNT 0x0700 +#define SK_TXQS2_BUFCTL 0x0702 +#define SK_TXQS2_NEXTDESC 0x0704 +#define SK_TXQS2_RXBUF_LO 0x0708 +#define SK_TXQS2_RXBUF_HI 0x070C +#define SK_TXQS2_RXSTAT 0x0710 +#define SK_TXQS2_CSUM_STARTVAL 0x0714 +#define SK_TXQS2_CSUM_STARTPOS 0x0718 +#define SK_TXQS2_CSUM_WRITEPOS 0x071A +#define SK_TXQS2_CURADDR_LO 0x0720 +#define SK_TXQS2_CURADDR_HI 0x0724 +#define SK_TXQS2_CURCNT_LO 0x0728 +#define SK_TXQS2_CURCNT_HI 0x072C +#define SK_TXQS2_CURBYTES 0x0730 +#define SK_TXQS2_BMU_CSR 0x0734 +#define SK_TXQS2_WATERMARK 0x0738 +#define SK_TXQS2_FLAG 0x073A +#define SK_TXQS2_TEST1 0x073C +#define SK_TXQS2_TEST2 0x0740 +#define SK_TXQS2_TEST3 0x0744 + +/* Block 15 -- TX async queue 2 */ +#define SK_TXQA2_BUFCNT 0x0780 +#define SK_TXQA2_BUFCTL 0x0782 +#define SK_TXQA2_NEXTDESC 0x0784 +#define SK_TXQA2_RXBUF_LO 0x0788 +#define SK_TXQA2_RXBUF_HI 0x078C +#define SK_TXQA2_RXSTAT 0x0790 +#define SK_TXQA2_CSUM_STARTVAL 0x0794 +#define SK_TXQA2_CSUM_STARTPOS 0x0798 +#define SK_TXQA2_CSUM_WRITEPOS 0x079A +#define SK_TXQA2_CURADDR_LO 0x07A0 +#define SK_TXQA2_CURADDR_HI 0x07A4 +#define SK_TXQA2_CURCNT_LO 0x07A8 +#define SK_TXQA2_CURCNT_HI 0x07AC +#define SK_TXQA2_CURBYTES 0x07B0 +#define SK_TXQA2_BMU_CSR 0x07B4 +#define SK_TXQA2_WATERMARK 0x07B8 +#define SK_TXQA2_FLAG 0x07BA +#define SK_TXQA2_TEST1 0x07BC +#define SK_TXQA2_TEST2 0x07C0 +#define SK_TXQA2_TEST3 0x07C4 + +#define SK_TXBMU_CLR_IRQ_ERR 0x00000001 +#define SK_TXBMU_CLR_IRQ_EOF 0x00000002 +#define SK_TXBMU_CLR_IRQ_EOB 0x00000004 +#define SK_TXBMU_TX_START 0x00000010 +#define SK_TXBMU_TX_STOP 0x00000020 +#define SK_TXBMU_POLL_OFF 0x00000040 +#define SK_TXBMU_POLL_ON 0x00000080 +#define SK_TXBMU_TRANSFER_SM_RESET 0x00000100 +#define SK_TXBMU_TRANSFER_SM_UNRESET 0x00000200 +#define SK_TXBMU_DESCWR_SM_RESET 0x00000400 +#define SK_TXBMU_DESCWR_SM_UNRESET 0x00000800 +#define SK_TXBMU_DESCRD_SM_RESET 0x00001000 +#define SK_TXBMU_DESCRD_SM_UNRESET 0x00002000 +#define SK_TXBMU_SUPERVISOR_SM_RESET 0x00004000 +#define SK_TXBMU_SUPERVISOR_SM_UNRESET 0x00008000 +#define SK_TXBMU_PFI_SM_RESET 0x00010000 +#define SK_TXBMU_PFI_SM_UNRESET 0x00020000 +#define SK_TXBMU_FIFO_RESET 0x00040000 +#define SK_TXBMU_FIFO_UNRESET 0x00080000 +#define SK_TXBMU_DESC_RESET 0x00100000 +#define SK_TXBMU_DESC_UNRESET 0x00200000 +#define SK_TXBMU_SUPERVISOR_IDLE 0x01000000 + +#define SK_TXBMU_ONLINE \ + (SK_TXBMU_TRANSFER_SM_UNRESET|SK_TXBMU_DESCWR_SM_UNRESET| \ + SK_TXBMU_DESCRD_SM_UNRESET|SK_TXBMU_SUPERVISOR_SM_UNRESET| \ + SK_TXBMU_PFI_SM_UNRESET|SK_TXBMU_FIFO_UNRESET| \ + SK_TXBMU_DESC_UNRESET) + +#define SK_TXBMU_OFFLINE \ + (SK_TXBMU_TRANSFER_SM_RESET|SK_TXBMU_DESCWR_SM_RESET| \ + SK_TXBMU_DESCRD_SM_RESET|SK_TXBMU_SUPERVISOR_SM_RESET| \ + SK_TXBMU_PFI_SM_RESET|SK_TXBMU_FIFO_RESET| \ + SK_TXBMU_DESC_RESET) + +/* Block 16 -- Receive RAMbuffer 1 */ +#define SK_RXRB1_START 0x0800 +#define SK_RXRB1_END 0x0804 +#define SK_RXRB1_WR_PTR 0x0808 +#define SK_RXRB1_RD_PTR 0x080C +#define SK_RXRB1_UTHR_PAUSE 0x0810 +#define SK_RXRB1_LTHR_PAUSE 0x0814 +#define SK_RXRB1_UTHR_HIPRIO 0x0818 +#define SK_RXRB1_UTHR_LOPRIO 0x081C +#define SK_RXRB1_PKTCNT 0x0820 +#define SK_RXRB1_LVL 0x0824 +#define SK_RXRB1_CTLTST 0x0828 + +/* Block 17 -- Receive RAMbuffer 2 */ +#define SK_RXRB2_START 0x0880 +#define SK_RXRB2_END 0x0884 +#define SK_RXRB2_WR_PTR 0x0888 +#define SK_RXRB2_RD_PTR 0x088C +#define SK_RXRB2_UTHR_PAUSE 0x0890 +#define SK_RXRB2_LTHR_PAUSE 0x0894 +#define SK_RXRB2_UTHR_HIPRIO 0x0898 +#define SK_RXRB2_UTHR_LOPRIO 0x089C +#define SK_RXRB2_PKTCNT 0x08A0 +#define SK_RXRB2_LVL 0x08A4 +#define SK_RXRB2_CTLTST 0x08A8 + +/* Block 20 -- Sync. Transmit RAMbuffer 1 */ +#define SK_TXRBS1_START 0x0A00 +#define SK_TXRBS1_END 0x0A04 +#define SK_TXRBS1_WR_PTR 0x0A08 +#define SK_TXRBS1_RD_PTR 0x0A0C +#define SK_TXRBS1_PKTCNT 0x0A20 +#define SK_TXRBS1_LVL 0x0A24 +#define SK_TXRBS1_CTLTST 0x0A28 + +/* Block 21 -- Async. Transmit RAMbuffer 1 */ +#define SK_TXRBA1_START 0x0A80 +#define SK_TXRBA1_END 0x0A84 +#define SK_TXRBA1_WR_PTR 0x0A88 +#define SK_TXRBA1_RD_PTR 0x0A8C +#define SK_TXRBA1_PKTCNT 0x0AA0 +#define SK_TXRBA1_LVL 0x0AA4 +#define SK_TXRBA1_CTLTST 0x0AA8 + +/* Block 22 -- Sync. Transmit RAMbuffer 2 */ +#define SK_TXRBS2_START 0x0B00 +#define SK_TXRBS2_END 0x0B04 +#define SK_TXRBS2_WR_PTR 0x0B08 +#define SK_TXRBS2_RD_PTR 0x0B0C +#define SK_TXRBS2_PKTCNT 0x0B20 +#define SK_TXRBS2_LVL 0x0B24 +#define SK_TXRBS2_CTLTST 0x0B28 + +/* Block 23 -- Async. Transmit RAMbuffer 2 */ +#define SK_TXRBA2_START 0x0B80 +#define SK_TXRBA2_END 0x0B84 +#define SK_TXRBA2_WR_PTR 0x0B88 +#define SK_TXRBA2_RD_PTR 0x0B8C +#define SK_TXRBA2_PKTCNT 0x0BA0 +#define SK_TXRBA2_LVL 0x0BA4 +#define SK_TXRBA2_CTLTST 0x0BA8 + +#define SK_RBCTL_RESET 0x00000001 +#define SK_RBCTL_UNRESET 0x00000002 +#define SK_RBCTL_OFF 0x00000004 +#define SK_RBCTL_ON 0x00000008 +#define SK_RBCTL_STORENFWD_OFF 0x00000010 +#define SK_RBCTL_STORENFWD_ON 0x00000020 + +/* Block 24 -- RX MAC FIFO 1 regisrers and LINK_SYNC counter */ +#define SK_RXF1_END 0x0C00 +#define SK_RXF1_WPTR 0x0C04 +#define SK_RXF1_RPTR 0x0C0C +#define SK_RXF1_PKTCNT 0x0C10 +#define SK_RXF1_LVL 0x0C14 +#define SK_RXF1_MACCTL 0x0C18 +#define SK_RXF1_CTL 0x0C1C +#define SK_RXLED1_CNTINIT 0x0C20 +#define SK_RXLED1_COUNTER 0x0C24 +#define SK_RXLED1_CTL 0x0C28 +#define SK_RXLED1_TST 0x0C29 +#define SK_LINK_SYNC1_CINIT 0x0C30 +#define SK_LINK_SYNC1_COUNTER 0x0C34 +#define SK_LINK_SYNC1_CTL 0x0C38 +#define SK_LINK_SYNC1_TST 0x0C39 +#define SK_LINKLED1_CTL 0x0C3C + +#define SK_FIFO_END 0x3F + +/* Block 25 -- RX MAC FIFO 2 regisrers and LINK_SYNC counter */ +#define SK_RXF2_END 0x0C80 +#define SK_RXF2_WPTR 0x0C84 +#define SK_RXF2_RPTR 0x0C8C +#define SK_RXF2_PKTCNT 0x0C90 +#define SK_RXF2_LVL 0x0C94 +#define SK_RXF2_MACCTL 0x0C98 +#define SK_RXF2_CTL 0x0C9C +#define SK_RXLED2_CNTINIT 0x0CA0 +#define SK_RXLED2_COUNTER 0x0CA4 +#define SK_RXLED2_CTL 0x0CA8 +#define SK_RXLED2_TST 0x0CA9 +#define SK_LINK_SYNC2_CINIT 0x0CB0 +#define SK_LINK_SYNC2_COUNTER 0x0CB4 +#define SK_LINK_SYNC2_CTL 0x0CB8 +#define SK_LINK_SYNC2_TST 0x0CB9 +#define SK_LINKLED2_CTL 0x0CBC + +#define SK_RXMACCTL_CLR_IRQ_NOSTS 0x00000001 +#define SK_RXMACCTL_CLR_IRQ_NOTSTAMP 0x00000002 +#define SK_RXMACCTL_TSTAMP_OFF 0x00000004 +#define SK_RXMACCTL_RSTAMP_ON 0x00000008 +#define SK_RXMACCTL_FLUSH_OFF 0x00000010 +#define SK_RXMACCTL_FLUSH_ON 0x00000020 +#define SK_RXMACCTL_PAUSE_OFF 0x00000040 +#define SK_RXMACCTL_PAUSE_ON 0x00000080 +#define SK_RXMACCTL_AFULL_OFF 0x00000100 +#define SK_RXMACCTL_AFULL_ON 0x00000200 +#define SK_RXMACCTL_VALIDTIME_PATCH_OFF 0x00000400 +#define SK_RXMACCTL_VALIDTIME_PATCH_ON 0x00000800 +#define SK_RXMACCTL_RXRDY_PATCH_OFF 0x00001000 +#define SK_RXMACCTL_RXRDY_PATCH_ON 0x00002000 +#define SK_RXMACCTL_STS_TIMEO 0x00FF0000 +#define SK_RXMACCTL_TSTAMP_TIMEO 0xFF000000 + +#define SK_RXLEDCTL_ENABLE 0x0001 +#define SK_RXLEDCTL_COUNTER_STOP 0x0002 +#define SK_RXLEDCTL_COUNTER_START 0x0004 + +#define SK_LINKLED_OFF 0x0001 +#define SK_LINKLED_ON 0x0002 +#define SK_LINKLED_LINKSYNC_OFF 0x0004 +#define SK_LINKLED_LINKSYNC_ON 0x0008 +#define SK_LINKLED_BLINK_OFF 0x0010 +#define SK_LINKLED_BLINK_ON 0x0020 + +/* Block 26 -- TX MAC FIFO 1 regisrers */ +#define SK_TXF1_END 0x0D00 +#define SK_TXF1_WPTR 0x0D04 +#define SK_TXF1_RPTR 0x0D0C +#define SK_TXF1_PKTCNT 0x0D10 +#define SK_TXF1_LVL 0x0D14 +#define SK_TXF1_MACCTL 0x0D18 +#define SK_TXF1_CTL 0x0D1C +#define SK_TXLED1_CNTINIT 0x0D20 +#define SK_TXLED1_COUNTER 0x0D24 +#define SK_TXLED1_CTL 0x0D28 +#define SK_TXLED1_TST 0x0D29 + +/* Block 27 -- TX MAC FIFO 2 regisrers */ +#define SK_TXF2_END 0x0D80 +#define SK_TXF2_WPTR 0x0D84 +#define SK_TXF2_RPTR 0x0D8C +#define SK_TXF2_PKTCNT 0x0D90 +#define SK_TXF2_LVL 0x0D94 +#define SK_TXF2_MACCTL 0x0D98 +#define SK_TXF2_CTL 0x0D9C +#define SK_TXLED2_CNTINIT 0x0DA0 +#define SK_TXLED2_COUNTER 0x0DA4 +#define SK_TXLED2_CTL 0x0DA8 +#define SK_TXLED2_TST 0x0DA9 + +#define SK_TXMACCTL_XMAC_RESET 0x00000001 +#define SK_TXMACCTL_XMAC_UNRESET 0x00000002 +#define SK_TXMACCTL_LOOP_OFF 0x00000004 +#define SK_TXMACCTL_LOOP_ON 0x00000008 +#define SK_TXMACCTL_FLUSH_OFF 0x00000010 +#define SK_TXMACCTL_FLUSH_ON 0x00000020 +#define SK_TXMACCTL_WAITEMPTY_OFF 0x00000040 +#define SK_TXMACCTL_WAITEMPTY_ON 0x00000080 +#define SK_TXMACCTL_AFULL_OFF 0x00000100 +#define SK_TXMACCTL_AFULL_ON 0x00000200 +#define SK_TXMACCTL_TXRDY_PATCH_OFF 0x00000400 +#define SK_TXMACCTL_RXRDY_PATCH_ON 0x00000800 +#define SK_TXMACCTL_PKT_RECOVERY_OFF 0x00001000 +#define SK_TXMACCTL_PKT_RECOVERY_ON 0x00002000 +#define SK_TXMACCTL_CLR_IRQ_PERR 0x00008000 +#define SK_TXMACCTL_WAITAFTERFLUSH 0x00010000 + +#define SK_TXLEDCTL_ENABLE 0x0001 +#define SK_TXLEDCTL_COUNTER_STOP 0x0002 +#define SK_TXLEDCTL_COUNTER_START 0x0004 + +#define SK_FIFO_RESET 0x00000001 +#define SK_FIFO_UNRESET 0x00000002 +#define SK_FIFO_OFF 0x00000004 +#define SK_FIFO_ON 0x00000008 + +/* Block 0x40 to 0x4F -- XMAC 1 registers */ +#define SK_XMAC1_BASE 0x2000 +#define SK_XMAC1_END 0x23FF + +/* Block 0x60 to 0x6F -- XMAC 2 registers */ +#define SK_XMAC2_BASE 0x3000 +#define SK_XMAC2_END 0x33FF + +/* Compute relative offset of an XMAC register in the XMAC window(s). */ +#define SK_XMAC_REG(reg, mac) (((reg) * 2) + SK_XMAC1_BASE + \ + (mac * (SK_XMAC2_BASE - SK_XMAC1_BASE))) + +#define SK_XM_READ_4(sc, reg) \ + (sk_win_read_2(sc->sk_softc, \ + SK_XMAC_REG(reg, sc->sk_port)) & 0xFFFF) | \ + ((sk_win_read_2(sc->sk_softc, \ + SK_XMAC_REG(reg + 2, sc->sk_port)) << 16) & 0xFFFF0000) + +#define SK_XM_WRITE_4(sc, reg, val) \ + sk_win_write_2(sc->sk_softc, \ + SK_XMAC_REG(reg, sc->sk_port), ((val) & 0xFFFF)); \ + sk_win_write_2(sc->sk_softc, \ + SK_XMAC_REG(reg + 2, sc->sk_port), ((val) >> 16) & 0xFFFF); + +#define SK_XM_READ_2(sc, reg) \ + sk_win_read_2(sc->sk_softc, SK_XMAC_REG(reg, sc->sk_port)) + +#define SK_XM_WRITE_2(sc, reg, val) \ + sk_win_write_2(sc->sk_softc, SK_XMAC_REG(reg, sc->sk_port), val) + +#define SK_XM_SETBIT_4(sc, reg, x) \ + SK_XM_WRITE_4(sc, reg, (SK_XM_READ_4(sc, reg)) | (x)) + +#define SK_XM_CLRBIT_4(sc, reg, x) \ + SK_XM_WRITE_4(sc, reg, (SK_XM_READ_4(sc, reg)) & ~(x)) + +#define SK_XM_SETBIT_2(sc, reg, x) \ + SK_XM_WRITE_2(sc, reg, (SK_XM_READ_2(sc, reg)) | (x)) + +#define SK_XM_CLRBIT_2(sc, reg, x) \ + SK_XM_WRITE_2(sc, reg, (SK_XM_READ_2(sc, reg)) & ~(x)) + + +/* + * The default FIFO threshold on the XMAC II is 4 bytes. On + * dual port NICs, this often leads to transmit underruns, so we + * bump the threshold a little. + */ +#define SK_XM_TX_FIFOTHRESH 512 + +#define SK_PCI_VENDOR_ID 0x0000 +#define SK_PCI_DEVICE_ID 0x0002 +#define SK_PCI_COMMAND 0x0004 +#define SK_PCI_STATUS 0x0006 +#define SK_PCI_REVID 0x0008 +#define SK_PCI_CLASSCODE 0x0009 +#define SK_PCI_CACHELEN 0x000C +#define SK_PCI_LATENCY_TIMER 0x000D +#define SK_PCI_HEADER_TYPE 0x000E +#define SK_PCI_LOMEM 0x0010 +#define SK_PCI_LOIO 0x0014 +#define SK_PCI_SUBVEN_ID 0x002C +#define SK_PCI_SYBSYS_ID 0x002E +#define SK_PCI_BIOSROM 0x0030 +#define SK_PCI_INTLINE 0x003C +#define SK_PCI_INTPIN 0x003D +#define SK_PCI_MINGNT 0x003E +#define SK_PCI_MINLAT 0x003F + +/* device specific PCI registers */ +#define SK_PCI_OURREG1 0x0040 +#define SK_PCI_OURREG2 0x0044 +#define SK_PCI_CAPID 0x0048 /* 8 bits */ +#define SK_PCI_NEXTPTR 0x0049 /* 8 bits */ +#define SK_PCI_PWRMGMTCAP 0x004A /* 16 bits */ +#define SK_PCI_PWRMGMTCTRL 0x004C /* 16 bits */ +#define SK_PCI_PME_EVENT 0x004F +#define SK_PCI_VPD_CAPID 0x0050 +#define SK_PCI_VPD_NEXTPTR 0x0051 +#define SK_PCI_VPD_ADDR 0x0052 +#define SK_PCI_VPD_DATA 0x0054 + +#define SK_PSTATE_MASK 0x0003 +#define SK_PSTATE_D0 0x0000 +#define SK_PSTATE_D1 0x0001 +#define SK_PSTATE_D2 0x0002 +#define SK_PSTATE_D3 0x0003 +#define SK_PME_EN 0x0010 +#define SK_PME_STATUS 0x8000 + +/* + * VPD flag bit. Set to 0 to initiate a read, will become 1 when + * read is complete. Set to 1 to initiate a write, will become 0 + * when write is finished. + */ +#define SK_VPD_FLAG 0x8000 + +/* VPD structures */ +struct vpd_res { + u_int8_t vr_id; + u_int8_t vr_len; + u_int8_t vr_pad; +}; + +struct vpd_key { + char vk_key[2]; + u_int8_t vk_len; +}; + +#define VPD_RES_ID 0x82 /* ID string */ +#define VPD_RES_READ 0x90 /* start of read only area */ +#define VPD_RES_WRITE 0x81 /* start of read/write area */ +#define VPD_RES_END 0x78 /* end tag */ + +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->sk_btag, sc->sk_bhandle, reg, val) +#define CSR_WRITE_2(sc, reg, val) \ + bus_space_write_2(sc->sk_btag, sc->sk_bhandle, reg, val) +#define CSR_WRITE_1(sc, reg, val) \ + bus_space_write_1(sc->sk_btag, sc->sk_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->sk_btag, sc->sk_bhandle, reg) +#define CSR_READ_2(sc, reg) \ + bus_space_read_2(sc->sk_btag, sc->sk_bhandle, reg) +#define CSR_READ_1(sc, reg) \ + bus_space_read_1(sc->sk_btag, sc->sk_bhandle, reg) + +struct sk_type { + u_int16_t sk_vid; + u_int16_t sk_did; + char *sk_name; +}; + +/* RX queue descriptor data structure */ +struct sk_rx_desc { + u_int32_t sk_ctl; + u_int32_t sk_next; + u_int32_t sk_data_lo; + u_int32_t sk_data_hi; + u_int32_t sk_xmac_rxstat; + u_int32_t sk_timestamp; + u_int16_t sk_csum2; + u_int16_t sk_csum1; + u_int16_t sk_csum2_start; + u_int16_t sk_csum1_start; +}; + +#define SK_OPCODE_DEFAULT 0x00550000 +#define SK_OPCODE_CSUM 0x00560000 + +#define SK_RXCTL_LEN 0x0000FFFF +#define SK_RXCTL_OPCODE 0x00FF0000 +#define SK_RXCTL_TSTAMP_VALID 0x01000000 +#define SK_RXCTL_STATUS_VALID 0x02000000 +#define SK_RXCTL_DEV0 0x04000000 +#define SK_RXCTL_EOF_INTR 0x08000000 +#define SK_RXCTL_EOB_INTR 0x10000000 +#define SK_RXCTL_LASTFRAG 0x20000000 +#define SK_RXCTL_FIRSTFRAG 0x40000000 +#define SK_RXCTL_OWN 0x80000000 + +#define SK_RXSTAT \ + (SK_OPCODE_DEFAULT|SK_RXCTL_EOF_INTR|SK_RXCTL_LASTFRAG| \ + SK_RXCTL_FIRSTFRAG|SK_RXCTL_OWN) + +struct sk_tx_desc { + u_int32_t sk_ctl; + u_int32_t sk_next; + u_int32_t sk_data_lo; + u_int32_t sk_data_hi; + u_int32_t sk_xmac_txstat; + u_int16_t sk_rsvd0; + u_int16_t sk_csum_startval; + u_int16_t sk_csum_startpos; + u_int16_t sk_csum_writepos; + u_int32_t sk_rsvd1; +}; + +#define SK_TXCTL_LEN 0x0000FFFF +#define SK_TXCTL_OPCODE 0x00FF0000 +#define SK_TXCTL_SW 0x01000000 +#define SK_TXCTL_NOCRC 0x02000000 +#define SK_TXCTL_STORENFWD 0x04000000 +#define SK_TXCTL_EOF_INTR 0x08000000 +#define SK_TXCTL_EOB_INTR 0x10000000 +#define SK_TXCTL_LASTFRAG 0x20000000 +#define SK_TXCTL_FIRSTFRAG 0x40000000 +#define SK_TXCTL_OWN 0x80000000 + +#define SK_TXSTAT \ + (SK_OPCODE_DEFAULT|SK_TXCTL_EOF_INTR|SK_TXCTL_LASTFRAG|SK_TXCTL_OWN) + +#define SK_RXBYTES(x) (x) & 0x0000FFFF; +#define SK_TXBYTES SK_RXBYTES + +#define SK_TX_RING_CNT 512 +#define SK_RX_RING_CNT 256 + +/* + * Jumbo buffer stuff. Note that we must allocate more jumbo + * buffers than there are descriptors in the receive ring. This + * is because we don't know how long it will take for a packet + * to be released after we hand it off to the upper protocol + * layers. To be safe, we allocate 1.5 times the number of + * receive descriptors. + */ +#define SK_JUMBO_FRAMELEN 9018 +#define SK_JUMBO_MTU (SK_JUMBO_FRAMELEN-ETHER_HDR_LEN-ETHER_CRC_LEN) +#define SK_JSLOTS 384 + +#define SK_JRAWLEN (SK_JUMBO_FRAMELEN + ETHER_ALIGN) +#define SK_JLEN (SK_JRAWLEN + (sizeof(u_int64_t) - \ + (SK_JRAWLEN % sizeof(u_int64_t)))) +#define SK_JPAGESZ PAGE_SIZE +#define SK_RESID (SK_JPAGESZ - (SK_JLEN * SK_JSLOTS) % SK_JPAGESZ) +#define SK_JMEM ((SK_JLEN * SK_JSLOTS) + SK_RESID) + +struct sk_jpool_entry { + int slot; + SLIST_ENTRY(sk_jpool_entry) jpool_entries; +}; + +struct sk_chain { + void *sk_desc; + struct mbuf *sk_mbuf; + struct sk_chain *sk_next; +}; + +struct sk_chain_data { + struct sk_chain sk_tx_chain[SK_TX_RING_CNT]; + struct sk_chain sk_rx_chain[SK_RX_RING_CNT]; + int sk_tx_prod; + int sk_tx_cons; + int sk_tx_cnt; + int sk_rx_prod; + int sk_rx_cons; + int sk_rx_cnt; + /* Stick the jumbo mem management stuff here too. */ + caddr_t sk_jslots[SK_JSLOTS]; + void *sk_jumbo_buf; + +}; + +struct sk_ring_data { + struct sk_tx_desc sk_tx_ring[SK_TX_RING_CNT]; + struct sk_rx_desc sk_rx_ring[SK_RX_RING_CNT]; +}; + +struct sk_bcom_hack { + int reg; + int val; +}; + +#define SK_INC(x, y) (x) = (x + 1) % y + +/* Forward decl. */ +struct sk_if_softc; + +/* Softc for the GEnesis controller. */ +struct sk_softc { + bus_space_handle_t sk_bhandle; /* bus space handle */ + bus_space_tag_t sk_btag; /* bus space tag */ + void *sk_intrhand; /* irq handler handle */ + struct resource *sk_irq; /* IRQ resource handle */ + struct resource *sk_res; /* I/O or shared mem handle */ + u_int8_t sk_unit; /* controller number */ + u_int8_t sk_type; + char *sk_vpd_prodname; + char *sk_vpd_readonly; + u_int32_t sk_rboff; /* RAMbuffer offset */ + u_int32_t sk_ramsize; /* amount of RAM on NIC */ + u_int32_t sk_pmd; /* physical media type */ + u_int32_t sk_intrmask; + struct sk_if_softc *sk_if[2]; + device_t sk_devs[2]; + struct mtx sk_mtx; +}; + +#define SK_LOCK(_sc) mtx_lock(&(_sc)->sk_mtx) +#define SK_UNLOCK(_sc) mtx_unlock(&(_sc)->sk_mtx) +#define SK_IF_LOCK(_sc) mtx_lock(&(_sc)->sk_softc->sk_mtx) +#define SK_IF_UNLOCK(_sc) mtx_unlock(&(_sc)->sk_softc->sk_mtx) + +/* Softc for each logical interface */ +struct sk_if_softc { + struct arpcom arpcom; /* interface info */ + device_t sk_miibus; + u_int8_t sk_unit; /* interface number */ + u_int8_t sk_port; /* port # on controller */ + u_int8_t sk_xmac_rev; /* XMAC chip rev (B2 or C1) */ + u_int32_t sk_rx_ramstart; + u_int32_t sk_rx_ramend; + u_int32_t sk_tx_ramstart; + u_int32_t sk_tx_ramend; + int sk_phytype; + int sk_phyaddr; + device_t sk_dev; + int sk_cnt; + int sk_link; + struct callout_handle sk_tick_ch; + struct sk_chain_data sk_cdata; + struct sk_ring_data *sk_rdata; + struct sk_softc *sk_softc; /* parent controller */ + int sk_tx_bmu; /* TX BMU register */ + int sk_if_flags; + SLIST_HEAD(__sk_jfreehead, sk_jpool_entry) sk_jfree_listhead; + SLIST_HEAD(__sk_jinusehead, sk_jpool_entry) sk_jinuse_listhead; +}; + +#define SK_MAXUNIT 256 +#define SK_TIMEOUT 1000 +#define ETHER_ALIGN 2 + +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) +#endif diff --git a/sys/pci/if_ste.c b/sys/pci/if_ste.c new file mode 100644 index 0000000..6b65dcf --- /dev/null +++ b/sys/pci/if_ste.c @@ -0,0 +1,1583 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#include <net/bpf.h> + +#include <vm/vm.h> /* for vtophys */ +#include <vm/pmap.h> /* for vtophys */ +#include <machine/bus_memio.h> +#include <machine/bus_pio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +/* "controller miibus0" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +#define STE_USEIOSPACE + +#include <pci/if_stereg.h> + +MODULE_DEPEND(ste, miibus, 1, 1, 1); + +#if !defined(lint) +static const char rcsid[] = + "$FreeBSD$"; +#endif + +/* + * Various supported device vendors/types and their names. + */ +static struct ste_type ste_devs[] = { + { ST_VENDORID, ST_DEVICEID_ST201, "Sundance ST201 10/100BaseTX" }, + { DL_VENDORID, DL_DEVICEID_550TX, "D-Link DFE-550TX 10/100BaseTX" }, + { 0, 0, NULL } +}; + +static int ste_probe (device_t); +static int ste_attach (device_t); +static int ste_detach (device_t); +static void ste_init (void *); +static void ste_intr (void *); +static void ste_rxeof (struct ste_softc *); +static void ste_txeoc (struct ste_softc *); +static void ste_txeof (struct ste_softc *); +static void ste_stats_update (void *); +static void ste_stop (struct ste_softc *); +static void ste_reset (struct ste_softc *); +static int ste_ioctl (struct ifnet *, u_long, caddr_t); +static int ste_encap (struct ste_softc *, struct ste_chain *, + struct mbuf *); +static void ste_start (struct ifnet *); +static void ste_watchdog (struct ifnet *); +static void ste_shutdown (device_t); +static int ste_newbuf (struct ste_softc *, + struct ste_chain_onefrag *, + struct mbuf *); +static int ste_ifmedia_upd (struct ifnet *); +static void ste_ifmedia_sts (struct ifnet *, struct ifmediareq *); + +static void ste_mii_sync (struct ste_softc *); +static void ste_mii_send (struct ste_softc *, u_int32_t, int); +static int ste_mii_readreg (struct ste_softc *, struct ste_mii_frame *); +static int ste_mii_writereg (struct ste_softc *, struct ste_mii_frame *); +static int ste_miibus_readreg (device_t, int, int); +static int ste_miibus_writereg (device_t, int, int, int); +static void ste_miibus_statchg (device_t); + +static int ste_eeprom_wait (struct ste_softc *); +static int ste_read_eeprom (struct ste_softc *, caddr_t, int, int, int); +static void ste_wait (struct ste_softc *); +static u_int8_t ste_calchash (caddr_t); +static void ste_setmulti (struct ste_softc *); +static int ste_init_rx_list (struct ste_softc *); +static void ste_init_tx_list (struct ste_softc *); + +#ifdef STE_USEIOSPACE +#define STE_RES SYS_RES_IOPORT +#define STE_RID STE_PCI_LOIO +#else +#define STE_RES SYS_RES_MEMORY +#define STE_RID STE_PCI_LOMEM +#endif + +static device_method_t ste_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ste_probe), + DEVMETHOD(device_attach, ste_attach), + DEVMETHOD(device_detach, ste_detach), + DEVMETHOD(device_shutdown, ste_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, ste_miibus_readreg), + DEVMETHOD(miibus_writereg, ste_miibus_writereg), + DEVMETHOD(miibus_statchg, ste_miibus_statchg), + + { 0, 0 } +}; + +static driver_t ste_driver = { + "ste", + ste_methods, + sizeof(struct ste_softc) +}; + +static devclass_t ste_devclass; + +DRIVER_MODULE(if_ste, pci, ste_driver, ste_devclass, 0, 0); +DRIVER_MODULE(miibus, ste, miibus_driver, miibus_devclass, 0, 0); + +#define STE_SETBIT4(sc, reg, x) \ + CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | x) + +#define STE_CLRBIT4(sc, reg, x) \ + CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~x) + +#define STE_SETBIT2(sc, reg, x) \ + CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) | x) + +#define STE_CLRBIT2(sc, reg, x) \ + CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) & ~x) + +#define STE_SETBIT1(sc, reg, x) \ + CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) | x) + +#define STE_CLRBIT1(sc, reg, x) \ + CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) & ~x) + + +#define MII_SET(x) STE_SETBIT1(sc, STE_PHYCTL, x) +#define MII_CLR(x) STE_CLRBIT1(sc, STE_PHYCTL, x) + +/* + * Sync the PHYs by setting data bit and strobing the clock 32 times. + */ +static void ste_mii_sync(sc) + struct ste_softc *sc; +{ + register int i; + + MII_SET(STE_PHYCTL_MDIR|STE_PHYCTL_MDATA); + + for (i = 0; i < 32; i++) { + MII_SET(STE_PHYCTL_MCLK); + DELAY(1); + MII_CLR(STE_PHYCTL_MCLK); + DELAY(1); + } + + return; +} + +/* + * Clock a series of bits through the MII. + */ +static void ste_mii_send(sc, bits, cnt) + struct ste_softc *sc; + u_int32_t bits; + int cnt; +{ + int i; + + MII_CLR(STE_PHYCTL_MCLK); + + for (i = (0x1 << (cnt - 1)); i; i >>= 1) { + if (bits & i) { + MII_SET(STE_PHYCTL_MDATA); + } else { + MII_CLR(STE_PHYCTL_MDATA); + } + DELAY(1); + MII_CLR(STE_PHYCTL_MCLK); + DELAY(1); + MII_SET(STE_PHYCTL_MCLK); + } +} + +/* + * Read an PHY register through the MII. + */ +static int ste_mii_readreg(sc, frame) + struct ste_softc *sc; + struct ste_mii_frame *frame; + +{ + int i, ack; + + STE_LOCK(sc); + + /* + * Set up frame for RX. + */ + frame->mii_stdelim = STE_MII_STARTDELIM; + frame->mii_opcode = STE_MII_READOP; + frame->mii_turnaround = 0; + frame->mii_data = 0; + + CSR_WRITE_2(sc, STE_PHYCTL, 0); + /* + * Turn on data xmit. + */ + MII_SET(STE_PHYCTL_MDIR); + + ste_mii_sync(sc); + + /* + * Send command/address info. + */ + ste_mii_send(sc, frame->mii_stdelim, 2); + ste_mii_send(sc, frame->mii_opcode, 2); + ste_mii_send(sc, frame->mii_phyaddr, 5); + ste_mii_send(sc, frame->mii_regaddr, 5); + + /* Turn off xmit. */ + MII_CLR(STE_PHYCTL_MDIR); + + /* Idle bit */ + MII_CLR((STE_PHYCTL_MCLK|STE_PHYCTL_MDATA)); + DELAY(1); + MII_SET(STE_PHYCTL_MCLK); + DELAY(1); + + /* Check for ack */ + MII_CLR(STE_PHYCTL_MCLK); + DELAY(1); + MII_SET(STE_PHYCTL_MCLK); + DELAY(1); + ack = CSR_READ_2(sc, STE_PHYCTL) & STE_PHYCTL_MDATA; + + /* + * Now try reading data bits. If the ack failed, we still + * need to clock through 16 cycles to keep the PHY(s) in sync. + */ + if (ack) { + for(i = 0; i < 16; i++) { + MII_CLR(STE_PHYCTL_MCLK); + DELAY(1); + MII_SET(STE_PHYCTL_MCLK); + DELAY(1); + } + goto fail; + } + + for (i = 0x8000; i; i >>= 1) { + MII_CLR(STE_PHYCTL_MCLK); + DELAY(1); + if (!ack) { + if (CSR_READ_2(sc, STE_PHYCTL) & STE_PHYCTL_MDATA) + frame->mii_data |= i; + DELAY(1); + } + MII_SET(STE_PHYCTL_MCLK); + DELAY(1); + } + +fail: + + MII_CLR(STE_PHYCTL_MCLK); + DELAY(1); + MII_SET(STE_PHYCTL_MCLK); + DELAY(1); + + STE_UNLOCK(sc); + + if (ack) + return(1); + return(0); +} + +/* + * Write to a PHY register through the MII. + */ +static int ste_mii_writereg(sc, frame) + struct ste_softc *sc; + struct ste_mii_frame *frame; + +{ + STE_LOCK(sc); + + /* + * Set up frame for TX. + */ + + frame->mii_stdelim = STE_MII_STARTDELIM; + frame->mii_opcode = STE_MII_WRITEOP; + frame->mii_turnaround = STE_MII_TURNAROUND; + + /* + * Turn on data output. + */ + MII_SET(STE_PHYCTL_MDIR); + + ste_mii_sync(sc); + + ste_mii_send(sc, frame->mii_stdelim, 2); + ste_mii_send(sc, frame->mii_opcode, 2); + ste_mii_send(sc, frame->mii_phyaddr, 5); + ste_mii_send(sc, frame->mii_regaddr, 5); + ste_mii_send(sc, frame->mii_turnaround, 2); + ste_mii_send(sc, frame->mii_data, 16); + + /* Idle bit. */ + MII_SET(STE_PHYCTL_MCLK); + DELAY(1); + MII_CLR(STE_PHYCTL_MCLK); + DELAY(1); + + /* + * Turn off xmit. + */ + MII_CLR(STE_PHYCTL_MDIR); + + STE_UNLOCK(sc); + + return(0); +} + +static int ste_miibus_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct ste_softc *sc; + struct ste_mii_frame frame; + + sc = device_get_softc(dev); + + bzero((char *)&frame, sizeof(frame)); + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + ste_mii_readreg(sc, &frame); + + return(frame.mii_data); +} + +static int ste_miibus_writereg(dev, phy, reg, data) + device_t dev; + int phy, reg, data; +{ + struct ste_softc *sc; + struct ste_mii_frame frame; + + sc = device_get_softc(dev); + bzero((char *)&frame, sizeof(frame)); + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + frame.mii_data = data; + + ste_mii_writereg(sc, &frame); + + return(0); +} + +static void ste_miibus_statchg(dev) + device_t dev; +{ + struct ste_softc *sc; + struct mii_data *mii; + + sc = device_get_softc(dev); + STE_LOCK(sc); + mii = device_get_softc(sc->ste_miibus); + + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { + STE_SETBIT2(sc, STE_MACCTL0, STE_MACCTL0_FULLDUPLEX); + } else { + STE_CLRBIT2(sc, STE_MACCTL0, STE_MACCTL0_FULLDUPLEX); + } + STE_UNLOCK(sc); + + return; +} + +static int ste_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct ste_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = device_get_softc(sc->ste_miibus); + sc->ste_link = 0; + if (mii->mii_instance) { + struct mii_softc *miisc; + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + mii_phy_reset(miisc); + } + mii_mediachg(mii); + + return(0); +} + +static void ste_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct ste_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = device_get_softc(sc->ste_miibus); + + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + + return; +} + +static void ste_wait(sc) + struct ste_softc *sc; +{ + register int i; + + for (i = 0; i < STE_TIMEOUT; i++) { + if (!(CSR_READ_4(sc, STE_DMACTL) & STE_DMACTL_DMA_HALTINPROG)) + break; + } + + if (i == STE_TIMEOUT) + printf("ste%d: command never completed!\n", sc->ste_unit); + + return; +} + +/* + * The EEPROM is slow: give it time to come ready after issuing + * it a command. + */ +static int ste_eeprom_wait(sc) + struct ste_softc *sc; +{ + int i; + + DELAY(1000); + + for (i = 0; i < 100; i++) { + if (CSR_READ_2(sc, STE_EEPROM_CTL) & STE_EECTL_BUSY) + DELAY(1000); + else + break; + } + + if (i == 100) { + printf("ste%d: eeprom failed to come ready\n", sc->ste_unit); + return(1); + } + + return(0); +} + +/* + * Read a sequence of words from the EEPROM. Note that ethernet address + * data is stored in the EEPROM in network byte order. + */ +static int ste_read_eeprom(sc, dest, off, cnt, swap) + struct ste_softc *sc; + caddr_t dest; + int off; + int cnt; + int swap; +{ + int err = 0, i; + u_int16_t word = 0, *ptr; + + if (ste_eeprom_wait(sc)) + return(1); + + for (i = 0; i < cnt; i++) { + CSR_WRITE_2(sc, STE_EEPROM_CTL, STE_EEOPCODE_READ | (off + i)); + err = ste_eeprom_wait(sc); + if (err) + break; + word = CSR_READ_2(sc, STE_EEPROM_DATA); + ptr = (u_int16_t *)(dest + (i * 2)); + if (swap) + *ptr = ntohs(word); + else + *ptr = word; + } + + return(err ? 1 : 0); +} + +static u_int8_t ste_calchash(addr) + caddr_t addr; +{ + + u_int32_t crc, carry; + int i, j; + u_int8_t c; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (i = 0; i < 6; i++) { + c = *(addr + i); + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); + crc <<= 1; + c >>= 1; + if (carry) + crc = (crc ^ 0x04c11db6) | carry; + } + } + + /* return the filter bit position */ + return(crc & 0x0000003F); +} + +static void ste_setmulti(sc) + struct ste_softc *sc; +{ + struct ifnet *ifp; + int h = 0; + u_int32_t hashes[2] = { 0, 0 }; + struct ifmultiaddr *ifma; + + ifp = &sc->arpcom.ac_if; + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_ALLMULTI); + STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_MULTIHASH); + return; + } + + /* first, zot all the existing hash bits */ + CSR_WRITE_2(sc, STE_MAR0, 0); + CSR_WRITE_2(sc, STE_MAR1, 0); + CSR_WRITE_2(sc, STE_MAR2, 0); + CSR_WRITE_2(sc, STE_MAR3, 0); + + /* now program new ones */ + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = ste_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + } + + CSR_WRITE_2(sc, STE_MAR0, hashes[0] & 0xFFFF); + CSR_WRITE_2(sc, STE_MAR1, (hashes[0] >> 16) & 0xFFFF); + CSR_WRITE_2(sc, STE_MAR2, hashes[1] & 0xFFFF); + CSR_WRITE_2(sc, STE_MAR3, (hashes[1] >> 16) & 0xFFFF); + STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_ALLMULTI); + STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_MULTIHASH); + + return; +} + +static void ste_intr(xsc) + void *xsc; +{ + struct ste_softc *sc; + struct ifnet *ifp; + u_int16_t status; + + sc = xsc; + STE_LOCK(sc); + ifp = &sc->arpcom.ac_if; + + /* See if this is really our interrupt. */ + if (!(CSR_READ_2(sc, STE_ISR) & STE_ISR_INTLATCH)) { + STE_UNLOCK(sc); + return; + } + + for (;;) { + status = CSR_READ_2(sc, STE_ISR_ACK); + + if (!(status & STE_INTRS)) + break; + + if (status & STE_ISR_RX_DMADONE) + ste_rxeof(sc); + + if (status & STE_ISR_TX_DMADONE) + ste_txeof(sc); + + if (status & STE_ISR_TX_DONE) + ste_txeoc(sc); + + if (status & STE_ISR_STATS_OFLOW) { + untimeout(ste_stats_update, sc, sc->ste_stat_ch); + ste_stats_update(sc); + } + + if (status & STE_ISR_HOSTERR) { + ste_reset(sc); + ste_init(sc); + } + } + + /* Re-enable interrupts */ + CSR_WRITE_2(sc, STE_IMR, STE_INTRS); + + if (ifp->if_snd.ifq_head != NULL) + ste_start(ifp); + + STE_UNLOCK(sc); + + return; +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +static void ste_rxeof(sc) + struct ste_softc *sc; +{ + struct ether_header *eh; + struct mbuf *m; + struct ifnet *ifp; + struct ste_chain_onefrag *cur_rx; + int total_len = 0; + u_int32_t rxstat; + + ifp = &sc->arpcom.ac_if; + +again: + + while((rxstat = sc->ste_cdata.ste_rx_head->ste_ptr->ste_status)) { + cur_rx = sc->ste_cdata.ste_rx_head; + sc->ste_cdata.ste_rx_head = cur_rx->ste_next; + + /* + * If an error occurs, update stats, clear the + * status word and leave the mbuf cluster in place: + * it should simply get re-used next time this descriptor + * comes up in the ring. + */ + if (rxstat & STE_RXSTAT_FRAME_ERR) { + ifp->if_ierrors++; + cur_rx->ste_ptr->ste_status = 0; + continue; + } + + /* + * If there error bit was not set, the upload complete + * bit should be set which means we have a valid packet. + * If not, something truly strange has happened. + */ + if (!(rxstat & STE_RXSTAT_DMADONE)) { + printf("ste%d: bad receive status -- packet dropped", + sc->ste_unit); + ifp->if_ierrors++; + cur_rx->ste_ptr->ste_status = 0; + continue; + } + + /* No errors; receive the packet. */ + m = cur_rx->ste_mbuf; + total_len = cur_rx->ste_ptr->ste_status & STE_RXSTAT_FRAMELEN; + + /* + * Try to conjure up a new mbuf cluster. If that + * fails, it means we have an out of memory condition and + * should leave the buffer in place and continue. This will + * result in a lost packet, but there's little else we + * can do in this situation. + */ + if (ste_newbuf(sc, cur_rx, NULL) == ENOBUFS) { + ifp->if_ierrors++; + cur_rx->ste_ptr->ste_status = 0; + continue; + } + + ifp->if_ipackets++; + eh = mtod(m, struct ether_header *); + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = total_len; + + /* Remove header from mbuf and pass it on. */ + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, eh, m); + } + + /* + * Handle the 'end of channel' condition. When the upload + * engine hits the end of the RX ring, it will stall. This + * is our cue to flush the RX ring, reload the uplist pointer + * register and unstall the engine. + * XXX This is actually a little goofy. With the ThunderLAN + * chip, you get an interrupt when the receiver hits the end + * of the receive ring, which tells you exactly when you + * you need to reload the ring pointer. Here we have to + * fake it. I'm mad at myself for not being clever enough + * to avoid the use of a goto here. + */ + if (CSR_READ_4(sc, STE_RX_DMALIST_PTR) == 0 || + CSR_READ_4(sc, STE_DMACTL) & STE_DMACTL_RXDMA_STOPPED) { + STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_STALL); + ste_wait(sc); + CSR_WRITE_4(sc, STE_RX_DMALIST_PTR, + vtophys(&sc->ste_ldata->ste_rx_list[0])); + sc->ste_cdata.ste_rx_head = &sc->ste_cdata.ste_rx_chain[0]; + STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL); + goto again; + } + + return; +} + +static void ste_txeoc(sc) + struct ste_softc *sc; +{ + u_int8_t txstat; + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + + while ((txstat = CSR_READ_1(sc, STE_TX_STATUS)) & + STE_TXSTATUS_TXDONE) { + if (txstat & STE_TXSTATUS_UNDERRUN || + txstat & STE_TXSTATUS_EXCESSCOLLS || + txstat & STE_TXSTATUS_RECLAIMERR) { + ifp->if_oerrors++; + printf("ste%d: transmission error: %x\n", + sc->ste_unit, txstat); + + ste_reset(sc); + ste_init(sc); + + if (txstat & STE_TXSTATUS_UNDERRUN && + sc->ste_tx_thresh < STE_PACKET_SIZE) { + sc->ste_tx_thresh += STE_MIN_FRAMELEN; + printf("ste%d: tx underrun, increasing tx" + " start threshold to %d bytes\n", + sc->ste_unit, sc->ste_tx_thresh); + } + CSR_WRITE_2(sc, STE_TX_STARTTHRESH, sc->ste_tx_thresh); + CSR_WRITE_2(sc, STE_TX_RECLAIM_THRESH, + (STE_PACKET_SIZE >> 4)); + } + ste_init(sc); + CSR_WRITE_2(sc, STE_TX_STATUS, txstat); + } + + return; +} + +static void ste_txeof(sc) + struct ste_softc *sc; +{ + struct ste_chain *cur_tx = NULL; + struct ifnet *ifp; + int idx; + + ifp = &sc->arpcom.ac_if; + + idx = sc->ste_cdata.ste_tx_cons; + while(idx != sc->ste_cdata.ste_tx_prod) { + cur_tx = &sc->ste_cdata.ste_tx_chain[idx]; + + if (!(cur_tx->ste_ptr->ste_ctl & STE_TXCTL_DMADONE)) + break; + + if (cur_tx->ste_mbuf != NULL) { + m_freem(cur_tx->ste_mbuf); + cur_tx->ste_mbuf = NULL; + } + + ifp->if_opackets++; + + sc->ste_cdata.ste_tx_cnt--; + STE_INC(idx, STE_TX_LIST_CNT); + ifp->if_timer = 0; + } + + sc->ste_cdata.ste_tx_cons = idx; + + if (cur_tx != NULL) + ifp->if_flags &= ~IFF_OACTIVE; + + return; +} + +static void ste_stats_update(xsc) + void *xsc; +{ + struct ste_softc *sc; + struct ste_stats stats; + struct ifnet *ifp; + struct mii_data *mii; + int i; + u_int8_t *p; + + sc = xsc; + STE_LOCK(sc); + + ifp = &sc->arpcom.ac_if; + mii = device_get_softc(sc->ste_miibus); + + p = (u_int8_t *)&stats; + + for (i = 0; i < sizeof(stats); i++) { + *p = CSR_READ_1(sc, STE_STATS + i); + p++; + } + + ifp->if_collisions += stats.ste_single_colls + + stats.ste_multi_colls + stats.ste_late_colls; + + mii_tick(mii); + if (!sc->ste_link && mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->ste_link++; + if (ifp->if_snd.ifq_head != NULL) + ste_start(ifp); + } + + sc->ste_stat_ch = timeout(ste_stats_update, sc, hz); + STE_UNLOCK(sc); + + return; +} + + +/* + * Probe for a Sundance ST201 chip. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + */ +static int ste_probe(dev) + device_t dev; +{ + struct ste_type *t; + + t = ste_devs; + + while(t->ste_name != NULL) { + if ((pci_get_vendor(dev) == t->ste_vid) && + (pci_get_device(dev) == t->ste_did)) { + device_set_desc(dev, t->ste_name); + return(0); + } + t++; + } + + return(ENXIO); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int ste_attach(dev) + device_t dev; +{ + u_int32_t command; + struct ste_softc *sc; + struct ifnet *ifp; + int unit, error = 0, rid; + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + bzero(sc, sizeof(struct ste_softc)); + + mtx_init(&sc->ste_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF | MTX_RECURSE); + STE_LOCK(sc); + + /* + * Handle power management nonsense. + */ + if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { + u_int32_t iobase, membase, irq; + + /* Save important PCI config data. */ + iobase = pci_read_config(dev, STE_PCI_LOIO, 4); + membase = pci_read_config(dev, STE_PCI_LOMEM, 4); + irq = pci_read_config(dev, STE_PCI_INTLINE, 4); + + /* Reset the power state. */ + printf("ste%d: chip is in D%d power mode " + "-- setting to D0\n", unit, + pci_get_powerstate(dev)); + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + + /* Restore PCI config data. */ + pci_write_config(dev, STE_PCI_LOIO, iobase, 4); + pci_write_config(dev, STE_PCI_LOMEM, membase, 4); + pci_write_config(dev, STE_PCI_INTLINE, irq, 4); + } + + /* + * Map control/status registers. + */ + pci_enable_busmaster(dev); + pci_enable_io(dev, SYS_RES_IOPORT); + pci_enable_io(dev, SYS_RES_MEMORY); + command = pci_read_config(dev, PCIR_COMMAND, 4); + +#ifdef STE_USEIOSPACE + if (!(command & PCIM_CMD_PORTEN)) { + printf("ste%d: failed to enable I/O ports!\n", unit); + error = ENXIO; + goto fail; + } +#else + if (!(command & PCIM_CMD_MEMEN)) { + printf("ste%d: failed to enable memory mapping!\n", unit); + error = ENXIO; + goto fail; + } +#endif + + rid = STE_RID; + sc->ste_res = bus_alloc_resource(dev, STE_RES, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (sc->ste_res == NULL) { + printf ("ste%d: couldn't map ports/memory\n", unit); + error = ENXIO; + goto fail; + } + + sc->ste_btag = rman_get_bustag(sc->ste_res); + sc->ste_bhandle = rman_get_bushandle(sc->ste_res); + + rid = 0; + sc->ste_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->ste_irq == NULL) { + printf("ste%d: couldn't map interrupt\n", unit); + bus_release_resource(dev, STE_RES, STE_RID, sc->ste_res); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->ste_irq, INTR_TYPE_NET, + ste_intr, sc, &sc->ste_intrhand); + + if (error) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ste_irq); + bus_release_resource(dev, STE_RES, STE_RID, sc->ste_res); + printf("ste%d: couldn't set up irq\n", unit); + goto fail; + } + + callout_handle_init(&sc->ste_stat_ch); + + /* Reset the adapter. */ + ste_reset(sc); + + /* + * Get station address from the EEPROM. + */ + if (ste_read_eeprom(sc, (caddr_t)&sc->arpcom.ac_enaddr, + STE_EEADDR_NODE0, 3, 0)) { + printf("ste%d: failed to read station address\n", unit); + bus_teardown_intr(dev, sc->ste_irq, sc->ste_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ste_irq); + bus_release_resource(dev, STE_RES, STE_RID, sc->ste_res); + error = ENXIO;; + goto fail; + } + + /* + * A Sundance chip was detected. Inform the world. + */ + printf("ste%d: Ethernet address: %6D\n", unit, + sc->arpcom.ac_enaddr, ":"); + + sc->ste_unit = unit; + + /* Allocate the descriptor queues. */ + sc->ste_ldata = contigmalloc(sizeof(struct ste_list_data), M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc->ste_ldata == NULL) { + printf("ste%d: no memory for list buffers!\n", unit); + bus_teardown_intr(dev, sc->ste_irq, sc->ste_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ste_irq); + bus_release_resource(dev, STE_RES, STE_RID, sc->ste_res); + error = ENXIO; + goto fail; + } + + bzero(sc->ste_ldata, sizeof(struct ste_list_data)); + + /* Do MII setup. */ + if (mii_phy_probe(dev, &sc->ste_miibus, + ste_ifmedia_upd, ste_ifmedia_sts)) { + printf("ste%d: MII without any phy!\n", sc->ste_unit); + bus_teardown_intr(dev, sc->ste_irq, sc->ste_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ste_irq); + bus_release_resource(dev, STE_RES, STE_RID, sc->ste_res); + contigfree(sc->ste_ldata, + sizeof(struct ste_list_data), M_DEVBUF); + error = ENXIO; + goto fail; + } + + ifp = &sc->arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = "ste"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = ste_ioctl; + ifp->if_output = ether_output; + ifp->if_start = ste_start; + ifp->if_watchdog = ste_watchdog; + ifp->if_init = ste_init; + ifp->if_baudrate = 10000000; + ifp->if_snd.ifq_maxlen = STE_TX_LIST_CNT - 1; + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); + STE_UNLOCK(sc); + return(0); + +fail: + STE_UNLOCK(sc); + mtx_destroy(&sc->ste_mtx); + return(error); +} + +static int ste_detach(dev) + device_t dev; +{ + struct ste_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + STE_LOCK(sc); + ifp = &sc->arpcom.ac_if; + + ste_stop(sc); + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + + bus_generic_detach(dev); + device_delete_child(dev, sc->ste_miibus); + + bus_teardown_intr(dev, sc->ste_irq, sc->ste_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ste_irq); + bus_release_resource(dev, STE_RES, STE_RID, sc->ste_res); + + contigfree(sc->ste_ldata, sizeof(struct ste_list_data), M_DEVBUF); + + STE_UNLOCK(sc); + mtx_destroy(&sc->ste_mtx); + + return(0); +} + +static int ste_newbuf(sc, c, m) + struct ste_softc *sc; + struct ste_chain_onefrag *c; + struct mbuf *m; +{ + struct mbuf *m_new = NULL; + + if (m == NULL) { + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(ENOBUFS); + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + return(ENOBUFS); + } + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + } else { + m_new = m; + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + m_new->m_data = m_new->m_ext.ext_buf; + } + + m_adj(m_new, ETHER_ALIGN); + + c->ste_mbuf = m_new; + c->ste_ptr->ste_status = 0; + c->ste_ptr->ste_frag.ste_addr = vtophys(mtod(m_new, caddr_t)); + c->ste_ptr->ste_frag.ste_len = 1536 | STE_FRAG_LAST; + + return(0); +} + +static int ste_init_rx_list(sc) + struct ste_softc *sc; +{ + struct ste_chain_data *cd; + struct ste_list_data *ld; + int i; + + cd = &sc->ste_cdata; + ld = sc->ste_ldata; + + for (i = 0; i < STE_RX_LIST_CNT; i++) { + cd->ste_rx_chain[i].ste_ptr = &ld->ste_rx_list[i]; + if (ste_newbuf(sc, &cd->ste_rx_chain[i], NULL) == ENOBUFS) + return(ENOBUFS); + if (i == (STE_RX_LIST_CNT - 1)) { + cd->ste_rx_chain[i].ste_next = + &cd->ste_rx_chain[0]; + ld->ste_rx_list[i].ste_next = + vtophys(&ld->ste_rx_list[0]); + } else { + cd->ste_rx_chain[i].ste_next = + &cd->ste_rx_chain[i + 1]; + ld->ste_rx_list[i].ste_next = + vtophys(&ld->ste_rx_list[i + 1]); + } + + } + + cd->ste_rx_head = &cd->ste_rx_chain[0]; + + return(0); +} + +static void ste_init_tx_list(sc) + struct ste_softc *sc; +{ + struct ste_chain_data *cd; + struct ste_list_data *ld; + int i; + + cd = &sc->ste_cdata; + ld = sc->ste_ldata; + for (i = 0; i < STE_TX_LIST_CNT; i++) { + cd->ste_tx_chain[i].ste_ptr = &ld->ste_tx_list[i]; + cd->ste_tx_chain[i].ste_phys = vtophys(&ld->ste_tx_list[i]); + if (i == (STE_TX_LIST_CNT - 1)) + cd->ste_tx_chain[i].ste_next = + &cd->ste_tx_chain[0]; + else + cd->ste_tx_chain[i].ste_next = + &cd->ste_tx_chain[i + 1]; + if (i == 0) + cd->ste_tx_chain[i].ste_prev = + &cd->ste_tx_chain[STE_TX_LIST_CNT - 1]; + else + cd->ste_tx_chain[i].ste_prev = + &cd->ste_tx_chain[i - 1]; + } + + + bzero((char *)ld->ste_tx_list, + sizeof(struct ste_desc) * STE_TX_LIST_CNT); + + cd->ste_tx_prod = 0; + cd->ste_tx_cons = 0; + cd->ste_tx_cnt = 0; + + return; +} + +static void ste_init(xsc) + void *xsc; +{ + struct ste_softc *sc; + int i; + struct ifnet *ifp; + struct mii_data *mii; + + sc = xsc; + STE_LOCK(sc); + ifp = &sc->arpcom.ac_if; + mii = device_get_softc(sc->ste_miibus); + + ste_stop(sc); + + /* Init our MAC address */ + for (i = 0; i < ETHER_ADDR_LEN; i++) { + CSR_WRITE_1(sc, STE_PAR0 + i, sc->arpcom.ac_enaddr[i]); + } + + /* Init RX list */ + if (ste_init_rx_list(sc) == ENOBUFS) { + printf("ste%d: initialization failed: no " + "memory for RX buffers\n", sc->ste_unit); + ste_stop(sc); + STE_UNLOCK(sc); + return; + } + + /* Init TX descriptors */ + ste_init_tx_list(sc); + + /* Set the TX freethresh value */ + CSR_WRITE_1(sc, STE_TX_DMABURST_THRESH, STE_PACKET_SIZE >> 8); + + /* Set the TX start threshold for best performance. */ + CSR_WRITE_2(sc, STE_TX_STARTTHRESH, sc->ste_tx_thresh); + + /* Set the TX reclaim threshold. */ + CSR_WRITE_1(sc, STE_TX_RECLAIM_THRESH, (STE_PACKET_SIZE >> 4)); + + /* Set up the RX filter. */ + CSR_WRITE_1(sc, STE_RX_MODE, STE_RXMODE_UNICAST); + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) { + STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_PROMISC); + } else { + STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_PROMISC); + } + + /* Set capture broadcast bit to accept broadcast frames. */ + if (ifp->if_flags & IFF_BROADCAST) { + STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_BROADCAST); + } else { + STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_BROADCAST); + } + + ste_setmulti(sc); + + /* Load the address of the RX list. */ + STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_STALL); + ste_wait(sc); + CSR_WRITE_4(sc, STE_RX_DMALIST_PTR, + vtophys(&sc->ste_ldata->ste_rx_list[0])); + STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL); + STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL); + + /* Set TX polling interval */ + CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 64); + + /* Load address of the TX list */ + STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); + ste_wait(sc); + CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, + vtophys(&sc->ste_ldata->ste_tx_list[0])); + STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); + STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); + ste_wait(sc); + + /* Enable receiver and transmitter */ + CSR_WRITE_2(sc, STE_MACCTL0, 0); + STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_TX_ENABLE); + STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_RX_ENABLE); + + /* Enable stats counters. */ + STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_STATS_ENABLE); + + /* Enable interrupts. */ + CSR_WRITE_2(sc, STE_ISR, 0xFFFF); + CSR_WRITE_2(sc, STE_IMR, STE_INTRS); + + ste_ifmedia_upd(ifp); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + sc->ste_stat_ch = timeout(ste_stats_update, sc, hz); + STE_UNLOCK(sc); + + return; +} + +static void ste_stop(sc) + struct ste_softc *sc; +{ + int i; + struct ifnet *ifp; + + STE_LOCK(sc); + ifp = &sc->arpcom.ac_if; + + untimeout(ste_stats_update, sc, sc->ste_stat_ch); + + CSR_WRITE_2(sc, STE_IMR, 0); + STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_TX_DISABLE); + STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_RX_DISABLE); + STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_STATS_DISABLE); + STE_SETBIT2(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); + STE_SETBIT2(sc, STE_DMACTL, STE_DMACTL_RXDMA_STALL); + ste_wait(sc); + + sc->ste_link = 0; + + for (i = 0; i < STE_RX_LIST_CNT; i++) { + if (sc->ste_cdata.ste_rx_chain[i].ste_mbuf != NULL) { + m_freem(sc->ste_cdata.ste_rx_chain[i].ste_mbuf); + sc->ste_cdata.ste_rx_chain[i].ste_mbuf = NULL; + } + } + + for (i = 0; i < STE_TX_LIST_CNT; i++) { + if (sc->ste_cdata.ste_tx_chain[i].ste_mbuf != NULL) { + m_freem(sc->ste_cdata.ste_tx_chain[i].ste_mbuf); + sc->ste_cdata.ste_tx_chain[i].ste_mbuf = NULL; + } + } + + ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); + STE_UNLOCK(sc); + + return; +} + +static void ste_reset(sc) + struct ste_softc *sc; +{ + int i; + + STE_SETBIT4(sc, STE_ASICCTL, + STE_ASICCTL_GLOBAL_RESET|STE_ASICCTL_RX_RESET| + STE_ASICCTL_TX_RESET|STE_ASICCTL_DMA_RESET| + STE_ASICCTL_FIFO_RESET|STE_ASICCTL_NETWORK_RESET| + STE_ASICCTL_AUTOINIT_RESET|STE_ASICCTL_HOST_RESET| + STE_ASICCTL_EXTRESET_RESET); + + DELAY(100000); + + for (i = 0; i < STE_TIMEOUT; i++) { + if (!(CSR_READ_4(sc, STE_ASICCTL) & STE_ASICCTL_RESET_BUSY)) + break; + } + + if (i == STE_TIMEOUT) + printf("ste%d: global reset never completed\n", sc->ste_unit); + + return; +} + +static int ste_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct ste_softc *sc; + struct ifreq *ifr; + struct mii_data *mii; + int error = 0; + + sc = ifp->if_softc; + STE_LOCK(sc); + ifr = (struct ifreq *)data; + + switch(command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING && + ifp->if_flags & IFF_PROMISC && + !(sc->ste_if_flags & IFF_PROMISC)) { + STE_SETBIT1(sc, STE_RX_MODE, + STE_RXMODE_PROMISC); + } else if (ifp->if_flags & IFF_RUNNING && + !(ifp->if_flags & IFF_PROMISC) && + sc->ste_if_flags & IFF_PROMISC) { + STE_CLRBIT1(sc, STE_RX_MODE, + STE_RXMODE_PROMISC); + } else if (!(ifp->if_flags & IFF_RUNNING)) { + sc->ste_tx_thresh = STE_MIN_FRAMELEN; + ste_init(sc); + } + } else { + if (ifp->if_flags & IFF_RUNNING) + ste_stop(sc); + } + sc->ste_if_flags = ifp->if_flags; + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + ste_setmulti(sc); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = device_get_softc(sc->ste_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + break; + default: + error = EINVAL; + break; + } + + STE_UNLOCK(sc); + + return(error); +} + +static int ste_encap(sc, c, m_head) + struct ste_softc *sc; + struct ste_chain *c; + struct mbuf *m_head; +{ + int frag = 0; + struct ste_frag *f = NULL; + struct mbuf *m; + struct ste_desc *d; + int total_len = 0; + + d = c->ste_ptr; + d->ste_ctl = 0; + d->ste_next = 0; + + for (m = m_head, frag = 0; m != NULL; m = m->m_next) { + if (m->m_len != 0) { + if (frag == STE_MAXFRAGS) + break; + total_len += m->m_len; + f = &c->ste_ptr->ste_frags[frag]; + f->ste_addr = vtophys(mtod(m, vm_offset_t)); + f->ste_len = m->m_len; + frag++; + } + } + + c->ste_mbuf = m_head; + c->ste_ptr->ste_frags[frag - 1].ste_len |= STE_FRAG_LAST; + c->ste_ptr->ste_ctl = total_len; + + return(0); +} + +static void ste_start(ifp) + struct ifnet *ifp; +{ + struct ste_softc *sc; + struct mbuf *m_head = NULL; + struct ste_chain *prev = NULL, *cur_tx = NULL, *start_tx; + int idx; + + sc = ifp->if_softc; + STE_LOCK(sc); + + if (!sc->ste_link) { + STE_UNLOCK(sc); + return; + } + + if (ifp->if_flags & IFF_OACTIVE) { + STE_UNLOCK(sc); + return; + } + + idx = sc->ste_cdata.ste_tx_prod; + start_tx = &sc->ste_cdata.ste_tx_chain[idx]; + + while(sc->ste_cdata.ste_tx_chain[idx].ste_mbuf == NULL) { + + if ((STE_TX_LIST_CNT - sc->ste_cdata.ste_tx_cnt) < 3) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + cur_tx = &sc->ste_cdata.ste_tx_chain[idx]; + + ste_encap(sc, cur_tx, m_head); + + if (prev != NULL) + prev->ste_ptr->ste_next = cur_tx->ste_phys; + prev = cur_tx; + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, cur_tx->ste_mbuf); + + STE_INC(idx, STE_TX_LIST_CNT); + sc->ste_cdata.ste_tx_cnt++; + } + + if (cur_tx == NULL) { + STE_UNLOCK(sc); + return; + } + + cur_tx->ste_ptr->ste_ctl |= STE_TXCTL_DMAINTR; + + /* Start transmission */ + sc->ste_cdata.ste_tx_prod = idx; + start_tx->ste_prev->ste_ptr->ste_next = start_tx->ste_phys; + + ifp->if_timer = 5; + STE_UNLOCK(sc); + + return; +} + +static void ste_watchdog(ifp) + struct ifnet *ifp; +{ + struct ste_softc *sc; + + sc = ifp->if_softc; + STE_LOCK(sc); + + ifp->if_oerrors++; + printf("ste%d: watchdog timeout\n", sc->ste_unit); + + ste_txeoc(sc); + ste_txeof(sc); + ste_rxeof(sc); + ste_reset(sc); + ste_init(sc); + + if (ifp->if_snd.ifq_head != NULL) + ste_start(ifp); + STE_UNLOCK(sc); + + return; +} + +static void ste_shutdown(dev) + device_t dev; +{ + struct ste_softc *sc; + + sc = device_get_softc(dev); + + ste_stop(sc); + + return; +} diff --git a/sys/pci/if_stereg.h b/sys/pci/if_stereg.h new file mode 100644 index 0000000..b369d6d --- /dev/null +++ b/sys/pci/if_stereg.h @@ -0,0 +1,545 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * Sundance PCI device/vendor ID for the + * ST201 chip. + */ +#define ST_VENDORID 0x13F0 +#define ST_DEVICEID_ST201 0x0201 + +/* + * D-Link PCI device/vendor ID for the DFE-550TX. + */ +#define DL_VENDORID 0x1186 +#define DL_DEVICEID_550TX 0x1002 + +/* + * Register definitions for the Sundance Technologies ST201 PCI + * fast ethernet controller. The register space is 128 bytes long and + * can be accessed using either PCI I/O space or PCI memory mapping. + * There are 32-bit, 16-bit and 8-bit registers. + */ + +#define STE_DMACTL 0x00 +#define STE_TX_DMALIST_PTR 0x04 +#define STE_TX_DMABURST_THRESH 0x08 +#define STE_TX_DMAURG_THRESH 0x09 +#define STE_TX_DMAPOLL_PERIOD 0x0A +#define STE_RX_DMASTATUS 0x0C +#define STE_RX_DMALIST_PTR 0x10 +#define STE_RX_DMABURST_THRESH 0x14 +#define STE_RX_DMAURG_THRESH 0x15 +#define STE_RX_DMAPOLL_PERIOD 0x16 +#define STE_DEBUGCTL 0x1A +#define STE_ASICCTL 0x30 +#define STE_EEPROM_DATA 0x34 +#define STE_EEPROM_CTL 0x36 +#define STE_FIFOCTL 0x3A +#define STE_TX_STARTTHRESH 0x3C +#define STE_RX_EARLYTHRESH 0x3E +#define STE_EXT_ROMADDR 0x40 +#define STE_EXT_ROMDATA 0x44 +#define STE_WAKE_EVENT 0x45 +#define STE_TX_STATUS 0x46 +#define STE_TX_FRAMEID 0x47 +#define STE_COUNTDOWN 0x48 +#define STE_ISR_ACK 0x4A +#define STE_IMR 0x4C +#define STE_ISR 0x4E +#define STE_MACCTL0 0x50 +#define STE_MACCTL1 0x52 +#define STE_PAR0 0x54 +#define STE_PAR1 0x56 +#define STE_PAR2 0x58 +#define STE_MAX_FRAMELEN 0x5A +#define STE_RX_MODE 0x5C +#define STE_TX_RECLAIM_THRESH 0x5D +#define STE_PHYCTL 0x5E +#define STE_MAR0 0x60 +#define STE_MAR1 0x62 +#define STE_MAR2 0x64 +#define STE_MAR3 0x66 +#define STE_STATS 0x68 + +#define STE_DMACTL_RXDMA_STOPPED 0x00000001 +#define STE_DMACTL_TXDMA_CMPREQ 0x00000002 +#define STE_DMACTL_TXDMA_STOPPED 0x00000004 +#define STE_DMACTL_RXDMA_COMPLETE 0x00000008 +#define STE_DMACTL_TXDMA_COMPLETE 0x00000010 +#define STE_DMACTL_RXDMA_STALL 0x00000100 +#define STE_DMACTL_RXDMA_UNSTALL 0x00000200 +#define STE_DMACTL_TXDMA_STALL 0x00000400 +#define STE_DMACTL_TXDMA_UNSTALL 0x00000800 +#define STE_DMACTL_TXDMA_INPROG 0x00004000 +#define STE_DMACTL_DMA_HALTINPROG 0x00008000 +#define STE_DMACTL_RXEARLY_ENABLE 0x00020000 +#define STE_DMACTL_COUNTDOWN_SPEED 0x00040000 +#define STE_DMACTL_COUNTDOWN_MODE 0x00080000 +#define STE_DMACTL_MWI_DISABLE 0x00100000 +#define STE_DMACTL_RX_DISCARD_OFLOWS 0x00400000 +#define STE_DMACTL_COUNTDOWN_ENABLE 0x00800000 +#define STE_DMACTL_TARGET_ABORT 0x40000000 +#define STE_DMACTL_MASTER_ABORT 0x80000000 + +/* + * TX DMA burst thresh is the number of 32-byte blocks that + * must be loaded into the TX Fifo before a TXDMA burst request + * will be issued. + */ +#define STE_TXDMABURST_THRESH 0x1F + +/* + * The number of 32-byte blocks in the TX FIFO falls below the + * TX DMA urgent threshold, a TX DMA urgent request will be + * generated. + */ +#define STE_TXDMAURG_THRESH 0x3F + +/* + * Number of 320ns intervals between polls of the TXDMA next + * descriptor pointer (if we're using polling mode). + */ +#define STE_TXDMA_POLL_PERIOD 0x7F + +#define STE_RX_DMASTATUS_FRAMELEN 0x00001FFF +#define STE_RX_DMASTATUS_RXERR 0x00004000 +#define STE_RX_DMASTATUS_DMADONE 0x00008000 +#define STE_RX_DMASTATUS_FIFO_OFLOW 0x00010000 +#define STE_RX_DMASTATUS_RUNT 0x00020000 +#define STE_RX_DMASTATUS_ALIGNERR 0x00040000 +#define STE_RX_DMASTATUS_CRCERR 0x00080000 +#define STE_RX_DMASTATUS_GIANT 0x00100000 +#define STE_RX_DMASTATUS_DRIBBLE 0x00800000 +#define STE_RX_DMASTATUS_DMA_OFLOW 0x01000000 + +/* + * RX DMA burst thresh is the number of 32-byte blocks that + * must be present in the RX FIFO before a RXDMA bus master + * request will be issued. + */ +#define STE_RXDMABURST_THRESH 0xFF + +/* + * The number of 32-byte blocks in the RX FIFO falls below the + * RX DMA urgent threshold, a RX DMA urgent request will be + * generated. + */ +#define STE_RXDMAURG_THRESH 0x1F + +/* + * Number of 320ns intervals between polls of the RXDMA complete + * bit in the status field on the current RX descriptor (if we're + * using polling mode). + */ +#define STE_RXDMA_POLL_PERIOD 0x7F + +#define STE_DEBUGCTL_GPIO0_CTL 0x0001 +#define STE_DEBUGCTL_GPIO1_CTL 0x0002 +#define STE_DEBUGCTL_GPIO0_DATA 0x0004 +#define STE_DEBUGCTL_GPIO1_DATA 0x0008 + +#define STE_ASICCTL_ROMSIZE 0x00000002 +#define STE_ASICCTL_TX_LARGEPKTS 0x00000004 +#define STE_ASICCTL_RX_LARGEPKTS 0x00000008 +#define STE_ASICCTL_EXTROM_DISABLE 0x00000010 +#define STE_ASICCTL_PHYSPEED_10 0x00000020 +#define STE_ASICCTL_PHYSPEED_100 0x00000040 +#define STE_ASICCTL_PHYMEDIA 0x00000080 +#define STE_ASICCTL_FORCEDCONFIG 0x00000700 +#define STE_ASICCTL_D3RESET_DISABLE 0x00000800 +#define STE_ASICCTL_SPEEDUPMODE 0x00002000 +#define STE_ASICCTL_LEDMODE 0x00004000 +#define STE_ASICCTL_RSTOUT_POLARITY 0x00008000 +#define STE_ASICCTL_GLOBAL_RESET 0x00010000 +#define STE_ASICCTL_RX_RESET 0x00020000 +#define STE_ASICCTL_TX_RESET 0x00040000 +#define STE_ASICCTL_DMA_RESET 0x00080000 +#define STE_ASICCTL_FIFO_RESET 0x00100000 +#define STE_ASICCTL_NETWORK_RESET 0x00200000 +#define STE_ASICCTL_HOST_RESET 0x00400000 +#define STE_ASICCTL_AUTOINIT_RESET 0x00800000 +#define STE_ASICCTL_EXTRESET_RESET 0x01000000 +#define STE_ASICCTL_SOFTINTR 0x02000000 +#define STE_ASICCTL_RESET_BUSY 0x04000000 + +#define STE_ASICCTL1_GLOBAL_RESET 0x0001 +#define STE_ASICCTL1_RX_RESET 0x0002 +#define STE_ASICCTL1_TX_RESET 0x0004 +#define STE_ASICCTL1_DMA_RESET 0x0008 +#define STE_ASICCTL1_FIFO_RESET 0x0010 +#define STE_ASICCTL1_NETWORK_RESET 0x0020 +#define STE_ASICCTL1_HOST_RESET 0x0040 +#define STE_ASICCTL1_AUTOINIT_RESET 0x0080 +#define STE_ASICCTL1_EXTRESET_RESET 0x0100 +#define STE_ASICCTL1_SOFTINTR 0x0200 +#define STE_ASICCTL1_RESET_BUSY 0x0400 + +#define STE_EECTL_ADDR 0x00FF +#define STE_EECTL_OPCODE 0x0300 +#define STE_EECTL_BUSY 0x1000 + +#define STE_EEOPCODE_WRITE 0x0100 +#define STE_EEOPCODE_READ 0x0200 +#define STE_EEOPCODE_ERASE 0x0300 + +#define STE_FIFOCTL_RAMTESTMODE 0x0001 +#define STE_FIFOCTL_OVERRUNMODE 0x0200 +#define STE_FIFOCTL_RXFIFOFULL 0x0800 +#define STE_FIFOCTL_TX_BUSY 0x4000 +#define STE_FIFOCTL_RX_BUSY 0x8000 + +/* + * The number of bytes that must in present in the TX FIFO before + * transmission begins. Value should be in increments of 4 bytes. + */ +#define STE_TXSTART_THRESH 0x1FFF + +/* + * Number of bytes that must be present in the RX FIFO before + * an RX EARLY interrupt is generated. + */ +#define STE_RXEARLY_THRESH 0x1FFF + +#define STE_WAKEEVENT_WAKEPKT_ENB 0x01 +#define STE_WAKEEVENT_MAGICPKT_ENB 0x02 +#define STE_WAKEEVENT_LINKEVT_ENB 0x04 +#define STE_WAKEEVENT_WAKEPOLARITY 0x08 +#define STE_WAKEEVENT_WAKEPKTEVENT 0x10 +#define STE_WAKEEVENT_MAGICPKTEVENT 0x20 +#define STE_WAKEEVENT_LINKEVENT 0x40 +#define STE_WAKEEVENT_WAKEONLAN_ENB 0x80 + +#define STE_TXSTATUS_RECLAIMERR 0x02 +#define STE_TXSTATUS_STATSOFLOW 0x04 +#define STE_TXSTATUS_EXCESSCOLLS 0x08 +#define STE_TXSTATUS_UNDERRUN 0x10 +#define STE_TXSTATUS_TXINTR_REQ 0x40 +#define STE_TXSTATUS_TXDONE 0x80 + +#define STE_ISRACK_INTLATCH 0x0001 +#define STE_ISRACK_HOSTERR 0x0002 +#define STE_ISRACK_TX_DONE 0x0004 +#define STE_ISRACK_MACCTL_FRAME 0x0008 +#define STE_ISRACK_RX_DONE 0x0010 +#define STE_ISRACK_RX_EARLY 0x0020 +#define STE_ISRACK_SOFTINTR 0x0040 +#define STE_ISRACK_STATS_OFLOW 0x0080 +#define STE_ISRACK_LINKEVENT 0x0100 +#define STE_ISRACK_TX_DMADONE 0x0200 +#define STE_ISRACK_RX_DMADONE 0x0400 + +#define STE_IMR_HOSTERR 0x0002 +#define STE_IMR_TX_DONE 0x0004 +#define STE_IMR_MACCTL_FRAME 0x0008 +#define STE_IMR_RX_DONE 0x0010 +#define STE_IMR_RX_EARLY 0x0020 +#define STE_IMR_SOFTINTR 0x0040 +#define STE_IMR_STATS_OFLOW 0x0080 +#define STE_IMR_LINKEVENT 0x0100 +#define STE_IMR_TX_DMADONE 0x0200 +#define STE_IMR_RX_DMADONE 0x0400 + +#define STE_INTRS \ + (STE_IMR_RX_DMADONE|STE_IMR_TX_DMADONE|STE_IMR_STATS_OFLOW| \ + STE_IMR_TX_DONE|STE_IMR_HOSTERR|STE_IMR_RX_EARLY) + +#define STE_ISR_INTLATCH 0x0001 +#define STE_ISR_HOSTERR 0x0002 +#define STE_ISR_TX_DONE 0x0004 +#define STE_ISR_MACCTL_FRAME 0x0008 +#define STE_ISR_RX_DONE 0x0010 +#define STE_ISR_RX_EARLY 0x0020 +#define STE_ISR_SOFTINTR 0x0040 +#define STE_ISR_STATS_OFLOW 0x0080 +#define STE_ISR_LINKEVENT 0x0100 +#define STE_ISR_TX_DMADONE 0x0200 +#define STE_ISR_RX_DMADONE 0x0400 + +/* + * Note: the Sundance manual gives the impression that the's + * only one 32-bit MACCTL register. In fact, there are two + * 16-bit registers side by side, and you have to access them + * separately. + */ +#define STE_MACCTL0_IPG 0x0003 +#define STE_MACCTL0_FULLDUPLEX 0x0020 +#define STE_MACCTL0_RX_GIANTS 0x0040 +#define STE_MACCTL0_FLOWCTL_ENABLE 0x0100 +#define STE_MACCTL0_RX_FCS 0x0200 +#define STE_MACCTL0_FIFOLOOPBK 0x0400 +#define STE_MACCTL0_MACLOOPBK 0x0800 + +#define STE_MACCTL1_COLLDETECT 0x0001 +#define STE_MACCTL1_CARRSENSE 0x0002 +#define STE_MACCTL1_TX_BUSY 0x0004 +#define STE_MACCTL1_TX_ERROR 0x0008 +#define STE_MACCTL1_STATS_ENABLE 0x0020 +#define STE_MACCTL1_STATS_DISABLE 0x0040 +#define STE_MACCTL1_STATS_ENABLED 0x0080 +#define STE_MACCTL1_TX_ENABLE 0x0100 +#define STE_MACCTL1_TX_DISABLE 0x0200 +#define STE_MACCTL1_TX_ENABLED 0x0400 +#define STE_MACCTL1_RX_ENABLE 0x0800 +#define STE_MACCTL1_RX_DISABLE 0x1000 +#define STE_MACCTL1_RX_ENABLED 0x2000 +#define STE_MACCTL1_PAUSED 0x4000 + +#define STE_IPG_96BT 0x00000000 +#define STE_IPG_128BT 0x00000001 +#define STE_IPG_224BT 0x00000002 +#define STE_IPG_544BT 0x00000003 + +#define STE_RXMODE_UNICAST 0x01 +#define STE_RXMODE_ALLMULTI 0x02 +#define STE_RXMODE_BROADCAST 0x04 +#define STE_RXMODE_PROMISC 0x08 +#define STE_RXMODE_MULTIHASH 0x10 +#define STE_RXMODE_ALLIPMULTI 0x20 + +#define STE_PHYCTL_MCLK 0x01 +#define STE_PHYCTL_MDATA 0x02 +#define STE_PHYCTL_MDIR 0x04 +#define STE_PHYCTL_CLK25_DISABLE 0x08 +#define STE_PHYCTL_DUPLEXPOLARITY 0x10 +#define STE_PHYCTL_DUPLEXSTAT 0x20 +#define STE_PHYCTL_SPEEDSTAT 0x40 +#define STE_PHYCTL_LINKSTAT 0x80 + +/* + * EEPROM offsets. + */ +#define STE_EEADDR_CONFIGPARM 0x00 +#define STE_EEADDR_ASICCTL 0x02 +#define STE_EEADDR_SUBSYS_ID 0x04 +#define STE_EEADDR_SUBVEN_ID 0x08 + +#define STE_EEADDR_NODE0 0x10 +#define STE_EEADDR_NODE1 0x12 +#define STE_EEADDR_NODE2 0x14 + +/* PCI registers */ +#define STE_PCI_VENDOR_ID 0x00 +#define STE_PCI_DEVICE_ID 0x02 +#define STE_PCI_COMMAND 0x04 +#define STE_PCI_STATUS 0x06 +#define STE_PCI_CLASSCODE 0x09 +#define STE_PCI_LATENCY_TIMER 0x0D +#define STE_PCI_HEADER_TYPE 0x0E +#define STE_PCI_LOIO 0x10 +#define STE_PCI_LOMEM 0x14 +#define STE_PCI_BIOSROM 0x30 +#define STE_PCI_INTLINE 0x3C +#define STE_PCI_INTPIN 0x3D +#define STE_PCI_MINGNT 0x3E +#define STE_PCI_MINLAT 0x0F + +#define STE_PCI_CAPID 0x50 /* 8 bits */ +#define STE_PCI_NEXTPTR 0x51 /* 8 bits */ +#define STE_PCI_PWRMGMTCAP 0x52 /* 16 bits */ +#define STE_PCI_PWRMGMTCTRL 0x54 /* 16 bits */ + +#define STE_PSTATE_MASK 0x0003 +#define STE_PSTATE_D0 0x0000 +#define STE_PSTATE_D1 0x0002 +#define STE_PSTATE_D2 0x0002 +#define STE_PSTATE_D3 0x0003 +#define STE_PME_EN 0x0010 +#define STE_PME_STATUS 0x8000 + + +struct ste_stats { + u_int32_t ste_rx_bytes; + u_int32_t ste_tx_bytes; + u_int16_t ste_tx_frames; + u_int16_t ste_rx_frames; + u_int8_t ste_carrsense_errs; + u_int8_t ste_late_colls; + u_int8_t ste_multi_colls; + u_int8_t ste_single_colls; + u_int8_t ste_tx_frames_defered; + u_int8_t ste_rx_lost_frames; + u_int8_t ste_tx_excess_defers; + u_int8_t ste_tx_abort_excess_colls; + u_int8_t ste_tx_bcast_frames; + u_int8_t ste_rx_bcast_frames; + u_int8_t ste_tx_mcast_frames; + u_int8_t ste_rx_mcast_frames; +}; + +struct ste_frag { + u_int32_t ste_addr; + u_int32_t ste_len; +}; + +#define STE_FRAG_LAST 0x80000000 +#define STE_FRAG_LEN 0x00001FFF + +#define STE_MAXFRAGS 63 + +struct ste_desc { + u_int32_t ste_next; + u_int32_t ste_ctl; + struct ste_frag ste_frags[STE_MAXFRAGS]; +}; + +struct ste_desc_onefrag { + u_int32_t ste_next; + u_int32_t ste_status; + struct ste_frag ste_frag; +}; + +#define STE_TXCTL_WORDALIGN 0x00000003 +#define STE_TXCTL_FRAMEID 0x000003FC +#define STE_TXCTL_NOCRC 0x00002000 +#define STE_TXCTL_TXINTR 0x00008000 +#define STE_TXCTL_DMADONE 0x00010000 +#define STE_TXCTL_DMAINTR 0x80000000 + +#define STE_RXSTAT_FRAMELEN 0x00001FFF +#define STE_RXSTAT_FRAME_ERR 0x00004000 +#define STE_RXSTAT_DMADONE 0x00008000 +#define STE_RXSTAT_FIFO_OFLOW 0x00010000 +#define STE_RXSTAT_RUNT 0x00020000 +#define STE_RXSTAT_ALIGNERR 0x00040000 +#define STE_RXSTAT_CRCERR 0x00080000 +#define STE_RXSTAT_GIANT 0x00100000 +#define STE_RXSTAT_DRIBBLEBITS 0x00800000 +#define STE_RXSTAT_DMA_OFLOW 0x01000000 +#define STE_RXATAT_ONEBUF 0x10000000 + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->ste_btag, sc->ste_bhandle, reg, val) +#define CSR_WRITE_2(sc, reg, val) \ + bus_space_write_2(sc->ste_btag, sc->ste_bhandle, reg, val) +#define CSR_WRITE_1(sc, reg, val) \ + bus_space_write_1(sc->ste_btag, sc->ste_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->ste_btag, sc->ste_bhandle, reg) +#define CSR_READ_2(sc, reg) \ + bus_space_read_2(sc->ste_btag, sc->ste_bhandle, reg) +#define CSR_READ_1(sc, reg) \ + bus_space_read_1(sc->ste_btag, sc->ste_bhandle, reg) + +#define STE_TIMEOUT 1000 +#define STE_MIN_FRAMELEN 60 +#define STE_PACKET_SIZE 1536 +#define ETHER_ALIGN 2 +#define STE_RX_LIST_CNT 128 +#define STE_TX_LIST_CNT 256 +#define STE_INC(x, y) (x) = (x + 1) % y + +struct ste_type { + u_int16_t ste_vid; + u_int16_t ste_did; + char *ste_name; +}; + +struct ste_list_data { + struct ste_desc_onefrag ste_rx_list[STE_RX_LIST_CNT]; + struct ste_desc ste_tx_list[STE_TX_LIST_CNT]; + u_int8_t ste_pad[STE_MIN_FRAMELEN]; +}; + +struct ste_chain { + struct ste_desc *ste_ptr; + struct mbuf *ste_mbuf; + struct ste_chain *ste_next; + struct ste_chain *ste_prev; + u_int32_t ste_phys; +}; + +struct ste_chain_onefrag { + struct ste_desc_onefrag *ste_ptr; + struct mbuf *ste_mbuf; + struct ste_chain_onefrag *ste_next; +}; + +struct ste_chain_data { + struct ste_chain_onefrag ste_rx_chain[STE_RX_LIST_CNT]; + struct ste_chain ste_tx_chain[STE_TX_LIST_CNT]; + struct ste_chain_onefrag *ste_rx_head; + + int ste_tx_prod; + int ste_tx_cons; + int ste_tx_cnt; +}; + +struct ste_softc { + struct arpcom arpcom; + bus_space_tag_t ste_btag; + bus_space_handle_t ste_bhandle; + struct resource *ste_res; + struct resource *ste_irq; + void *ste_intrhand; + struct ste_type *ste_info; + device_t ste_miibus; + int ste_unit; + int ste_tx_thresh; + u_int8_t ste_link; + int ste_if_flags; + struct ste_list_data *ste_ldata; + struct ste_chain_data ste_cdata; + struct callout_handle ste_stat_ch; + struct mtx ste_mtx; +}; + +#define STE_LOCK(_sc) mtx_lock(&(_sc)->ste_mtx) +#define STE_UNLOCK(_sc) mtx_unlock(&(_sc)->ste_mtx) + +struct ste_mii_frame { + u_int8_t mii_stdelim; + u_int8_t mii_opcode; + u_int8_t mii_phyaddr; + u_int8_t mii_regaddr; + u_int8_t mii_turnaround; + u_int16_t mii_data; +}; + +/* + * MII constants + */ +#define STE_MII_STARTDELIM 0x01 +#define STE_MII_READOP 0x02 +#define STE_MII_WRITEOP 0x01 +#define STE_MII_TURNAROUND 0x02 + +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) +#endif diff --git a/sys/pci/if_ti.c b/sys/pci/if_ti.c new file mode 100644 index 0000000..a4ea930 --- /dev/null +++ b/sys/pci/if_ti.c @@ -0,0 +1,3628 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * Alteon Networks Tigon PCI gigabit ethernet driver for FreeBSD. + * Manuals, sample driver and firmware source kits are available + * from http://www.alteon.com/support/openkits. + * + * Written by Bill Paul <wpaul@ctr.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * The Alteon Networks Tigon chip contains an embedded R4000 CPU, + * gigabit MAC, dual DMA channels and a PCI interface unit. NICs + * using the Tigon may have anywhere from 512K to 2MB of SRAM. The + * Tigon supports hardware IP, TCP and UCP checksumming, multicast + * filtering and jumbo (9014 byte) frames. The hardware is largely + * controlled by firmware, which must be loaded into the NIC during + * initialization. + * + * The Tigon 2 contains 2 R4000 CPUs and requires a newer firmware + * revision, which supports new features such as extended commands, + * extended jumbo receive ring desciptors and a mini receive ring. + * + * Alteon Networks is to be commended for releasing such a vast amount + * of development material for the Tigon NIC without requiring an NDA + * (although they really should have done it a long time ago). With + * any luck, the other vendors will finally wise up and follow Alteon's + * stellar example. + * + * The firmware for the Tigon 1 and 2 NICs is compiled directly into + * this driver by #including it as a C header file. This bloats the + * driver somewhat, but it's the easiest method considering that the + * driver code and firmware code need to be kept in sync. The source + * for the firmware is not provided with the FreeBSD distribution since + * compiling it requires a GNU toolchain targeted for mips-sgi-irix5.3. + * + * The following people deserve special thanks: + * - Terry Murphy of 3Com, for providing a 3c985 Tigon 1 board + * for testing + * - Raymond Lee of Netgear, for providing a pair of Netgear + * GA620 Tigon 2 boards for testing + * - Ulf Zimmermann, for bringing the GA260 to my attention and + * convincing me to write this driver. + * - Andrew Gallatin for providing FreeBSD/Alpha support. + */ + +#include "opt_ti.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/conf.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/if_vlan_var.h> + +#include <net/bpf.h> + +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> + +#include <vm/vm.h> /* for vtophys */ +#include <vm/pmap.h> /* for vtophys */ +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +/* #define TI_PRIVATE_JUMBOS */ + +#if !defined(TI_PRIVATE_JUMBOS) +#include <sys/sockio.h> +#include <sys/uio.h> +#include <sys/lock.h> +#include <vm/vm_extern.h> +#include <vm/pmap.h> +#include <vm/vm_map.h> +#include <vm/vm_map.h> +#include <vm/vm_param.h> +#include <vm/vm_pageout.h> +#include <sys/vmmeter.h> +#include <vm/vm_page.h> +#include <vm/vm_object.h> +#include <vm/vm_kern.h> +#include <sys/proc.h> +#include <sys/jumbo.h> +#endif /* !TI_PRIVATE_JUMBOS */ +#include <sys/vnode.h> /* for vfindev, vgone */ + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +#include <sys/tiio.h> +#include <pci/if_tireg.h> +#include <pci/ti_fw.h> +#include <pci/ti_fw2.h> + +#define TI_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP | CSUM_IP_FRAGS) +/* + * We can only turn on header splitting if we're using extended receive + * BDs. + */ +#if defined(TI_JUMBO_HDRSPLIT) && defined(TI_PRIVATE_JUMBOS) +#error "options TI_JUMBO_HDRSPLIT and TI_PRIVATE_JUMBOS are mutually exclusive" +#endif /* TI_JUMBO_HDRSPLIT && TI_JUMBO_HDRSPLIT */ + +#if !defined(lint) +static const char rcsid[] = + "$FreeBSD$"; +#endif + +struct ti_softc *tis[8]; + +typedef enum { + TI_SWAP_HTON, + TI_SWAP_NTOH +} ti_swap_type; + + +/* + * Various supported device vendors/types and their names. + */ + +static struct ti_type ti_devs[] = { + { ALT_VENDORID, ALT_DEVICEID_ACENIC, + "Alteon AceNIC 1000baseSX Gigabit Ethernet" }, + { ALT_VENDORID, ALT_DEVICEID_ACENIC_COPPER, + "Alteon AceNIC 1000baseT Gigabit Ethernet" }, + { TC_VENDORID, TC_DEVICEID_3C985, + "3Com 3c985-SX Gigabit Ethernet" }, + { NG_VENDORID, NG_DEVICEID_GA620, + "Netgear GA620 1000baseSX Gigabit Ethernet" }, + { NG_VENDORID, NG_DEVICEID_GA620T, + "Netgear GA620 1000baseT Gigabit Ethernet" }, + { SGI_VENDORID, SGI_DEVICEID_TIGON, + "Silicon Graphics Gigabit Ethernet" }, + { DEC_VENDORID, DEC_DEVICEID_FARALLON_PN9000SX, + "Farallon PN9000SX Gigabit Ethernet" }, + { 0, 0, NULL } +}; + +#define TI_CDEV_MAJOR 153 + +static d_open_t ti_open; +static d_close_t ti_close; +static d_ioctl_t ti_ioctl2; + +static struct cdevsw ti_cdevsw = { + /* open */ ti_open, + /* close */ ti_close, + /* read */ NULL, + /* write */ NULL, + /* ioctl */ ti_ioctl2, + /* poll */ seltrue, + /* mmap */ nommap, + /* strategy */ nostrategy, + /* name */ "ti", + /* maj */ TI_CDEV_MAJOR, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ 0, +}; + +static int ti_probe (device_t); +static int ti_attach (device_t); +static int ti_detach (device_t); +static void ti_txeof (struct ti_softc *); +static void ti_rxeof (struct ti_softc *); + +static void ti_stats_update (struct ti_softc *); +static int ti_encap (struct ti_softc *, struct mbuf *, u_int32_t *); + +static void ti_intr (void *); +static void ti_start (struct ifnet *); +static int ti_ioctl (struct ifnet *, u_long, caddr_t); +static void ti_init (void *); +static void ti_init2 (struct ti_softc *); +static void ti_stop (struct ti_softc *); +static void ti_watchdog (struct ifnet *); +static void ti_shutdown (device_t); +static int ti_ifmedia_upd (struct ifnet *); +static void ti_ifmedia_sts (struct ifnet *, struct ifmediareq *); + +static u_int32_t ti_eeprom_putbyte (struct ti_softc *, int); +static u_int8_t ti_eeprom_getbyte (struct ti_softc *, int, u_int8_t *); +static int ti_read_eeprom (struct ti_softc *, caddr_t, int, int); + +static void ti_add_mcast (struct ti_softc *, struct ether_addr *); +static void ti_del_mcast (struct ti_softc *, struct ether_addr *); +static void ti_setmulti (struct ti_softc *); + +static void ti_mem (struct ti_softc *, u_int32_t, + u_int32_t, caddr_t); +static int ti_copy_mem (struct ti_softc *, u_int32_t, + u_int32_t, caddr_t, int, int); +static int ti_copy_scratch (struct ti_softc *, u_int32_t, + u_int32_t, caddr_t, int, int, int); +static int ti_bcopy_swap (const void *, void *, size_t, + ti_swap_type); +static void ti_loadfw (struct ti_softc *); +static void ti_cmd (struct ti_softc *, struct ti_cmd_desc *); +static void ti_cmd_ext (struct ti_softc *, struct ti_cmd_desc *, + caddr_t, int); +static void ti_handle_events (struct ti_softc *); +#ifdef TI_PRIVATE_JUMBOS +static int ti_alloc_jumbo_mem (struct ti_softc *); +static void *ti_jalloc (struct ti_softc *); +static void ti_jfree (void *, void *); +#endif /* TI_PRIVATE_JUMBOS */ +static int ti_newbuf_std (struct ti_softc *, int, struct mbuf *); +static int ti_newbuf_mini (struct ti_softc *, int, struct mbuf *); +static int ti_newbuf_jumbo (struct ti_softc *, int, struct mbuf *); +static int ti_init_rx_ring_std (struct ti_softc *); +static void ti_free_rx_ring_std (struct ti_softc *); +static int ti_init_rx_ring_jumbo (struct ti_softc *); +static void ti_free_rx_ring_jumbo (struct ti_softc *); +static int ti_init_rx_ring_mini (struct ti_softc *); +static void ti_free_rx_ring_mini (struct ti_softc *); +static void ti_free_tx_ring (struct ti_softc *); +static int ti_init_tx_ring (struct ti_softc *); + +static int ti_64bitslot_war (struct ti_softc *); +static int ti_chipinit (struct ti_softc *); +static int ti_gibinit (struct ti_softc *); + +#ifdef TI_JUMBO_HDRSPLIT +static __inline void ti_hdr_split (struct mbuf *top, int hdr_len, + int pkt_len, int idx); +#endif /* TI_JUMBO_HDRSPLIT */ + +static device_method_t ti_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ti_probe), + DEVMETHOD(device_attach, ti_attach), + DEVMETHOD(device_detach, ti_detach), + DEVMETHOD(device_shutdown, ti_shutdown), + { 0, 0 } +}; + +static driver_t ti_driver = { + "ti", + ti_methods, + sizeof(struct ti_softc) +}; + +static devclass_t ti_devclass; + +DRIVER_MODULE(if_ti, pci, ti_driver, ti_devclass, 0, 0); + +/* List of Tigon softcs */ +static STAILQ_HEAD(ti_softc_list, ti_softc) ti_sc_list; + +static struct ti_softc * +ti_lookup_softc(int unit) +{ + struct ti_softc *sc; + for (sc = STAILQ_FIRST(&ti_sc_list); sc != NULL; + sc = STAILQ_NEXT(sc, ti_links)) + if (sc->ti_unit == unit) + return(sc); + return(NULL); +} + +/* + * Send an instruction or address to the EEPROM, check for ACK. + */ +static u_int32_t ti_eeprom_putbyte(sc, byte) + struct ti_softc *sc; + int byte; +{ + register int i, ack = 0; + + /* + * Make sure we're in TX mode. + */ + TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_TXEN); + + /* + * Feed in each bit and stobe the clock. + */ + for (i = 0x80; i; i >>= 1) { + if (byte & i) { + TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_DOUT); + } else { + TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_DOUT); + } + DELAY(1); + TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_CLK); + DELAY(1); + TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_CLK); + } + + /* + * Turn off TX mode. + */ + TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_TXEN); + + /* + * Check for ack. + */ + TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_CLK); + ack = CSR_READ_4(sc, TI_MISC_LOCAL_CTL) & TI_MLC_EE_DIN; + TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_CLK); + + return(ack); +} + +/* + * Read a byte of data stored in the EEPROM at address 'addr.' + * We have to send two address bytes since the EEPROM can hold + * more than 256 bytes of data. + */ +static u_int8_t ti_eeprom_getbyte(sc, addr, dest) + struct ti_softc *sc; + int addr; + u_int8_t *dest; +{ + register int i; + u_int8_t byte = 0; + + EEPROM_START; + + /* + * Send write control code to EEPROM. + */ + if (ti_eeprom_putbyte(sc, EEPROM_CTL_WRITE)) { + printf("ti%d: failed to send write command, status: %x\n", + sc->ti_unit, CSR_READ_4(sc, TI_MISC_LOCAL_CTL)); + return(1); + } + + /* + * Send first byte of address of byte we want to read. + */ + if (ti_eeprom_putbyte(sc, (addr >> 8) & 0xFF)) { + printf("ti%d: failed to send address, status: %x\n", + sc->ti_unit, CSR_READ_4(sc, TI_MISC_LOCAL_CTL)); + return(1); + } + /* + * Send second byte address of byte we want to read. + */ + if (ti_eeprom_putbyte(sc, addr & 0xFF)) { + printf("ti%d: failed to send address, status: %x\n", + sc->ti_unit, CSR_READ_4(sc, TI_MISC_LOCAL_CTL)); + return(1); + } + + EEPROM_STOP; + EEPROM_START; + /* + * Send read control code to EEPROM. + */ + if (ti_eeprom_putbyte(sc, EEPROM_CTL_READ)) { + printf("ti%d: failed to send read command, status: %x\n", + sc->ti_unit, CSR_READ_4(sc, TI_MISC_LOCAL_CTL)); + return(1); + } + + /* + * Start reading bits from EEPROM. + */ + TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_TXEN); + for (i = 0x80; i; i >>= 1) { + TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_CLK); + DELAY(1); + if (CSR_READ_4(sc, TI_MISC_LOCAL_CTL) & TI_MLC_EE_DIN) + byte |= i; + TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_CLK); + DELAY(1); + } + + EEPROM_STOP; + + /* + * No ACK generated for read, so just return byte. + */ + + *dest = byte; + + return(0); +} + +/* + * Read a sequence of bytes from the EEPROM. + */ +static int ti_read_eeprom(sc, dest, off, cnt) + struct ti_softc *sc; + caddr_t dest; + int off; + int cnt; +{ + int err = 0, i; + u_int8_t byte = 0; + + for (i = 0; i < cnt; i++) { + err = ti_eeprom_getbyte(sc, off + i, &byte); + if (err) + break; + *(dest + i) = byte; + } + + return(err ? 1 : 0); +} + +/* + * NIC memory access function. Can be used to either clear a section + * of NIC local memory or (if buf is non-NULL) copy data into it. + */ +static void ti_mem(sc, addr, len, buf) + struct ti_softc *sc; + u_int32_t addr, len; + caddr_t buf; +{ + int segptr, segsize, cnt; + caddr_t ti_winbase, ptr; + + segptr = addr; + cnt = len; + ti_winbase = (caddr_t)(sc->ti_vhandle + TI_WINDOW); + ptr = buf; + + while(cnt) { + if (cnt < TI_WINLEN) + segsize = cnt; + else + segsize = TI_WINLEN - (segptr % TI_WINLEN); + CSR_WRITE_4(sc, TI_WINBASE, (segptr & ~(TI_WINLEN - 1))); + if (buf == NULL) + bzero((char *)ti_winbase + (segptr & + (TI_WINLEN - 1)), segsize); + else { + bcopy((char *)ptr, (char *)ti_winbase + + (segptr & (TI_WINLEN - 1)), segsize); + ptr += segsize; + } + segptr += segsize; + cnt -= segsize; + } + + return; +} + +static int +ti_copy_mem(sc, tigon_addr, len, buf, useraddr, readdata) + struct ti_softc *sc; + u_int32_t tigon_addr, len; + caddr_t buf; + int useraddr, readdata; +{ + int segptr, segsize, cnt; + caddr_t ptr; + u_int32_t origwin; + u_int8_t tmparray[TI_WINLEN], tmparray2[TI_WINLEN]; + int resid, segresid; + int first_pass; + + /* + * At the moment, we don't handle non-aligned cases, we just bail. + * If this proves to be a problem, it will be fixed. + */ + if ((readdata == 0) + && (tigon_addr & 0x3)) { + printf("ti%d: ti_copy_mem: tigon address %#x isn't " + "word-aligned\n", sc->ti_unit, tigon_addr); + printf("ti%d: ti_copy_mem: unaligned writes aren't yet " + "supported\n", sc->ti_unit); + return(EINVAL); + } + + segptr = tigon_addr & ~0x3; + segresid = tigon_addr - segptr; + + /* + * This is the non-aligned amount left over that we'll need to + * copy. + */ + resid = len & 0x3; + + /* Add in the left over amount at the front of the buffer */ + resid += segresid; + + cnt = len & ~0x3; + /* + * If resid + segresid is >= 4, add multiples of 4 to the count and + * decrease the residual by that much. + */ + cnt += resid & ~0x3; + resid -= resid & ~0x3; + + ptr = buf; + + first_pass = 1; + + /* + * Make sure we aren't interrupted while we're changing the window + * pointer. + */ + TI_LOCK(sc); + + /* + * Save the old window base value. + */ + origwin = CSR_READ_4(sc, TI_WINBASE); + + while(cnt) { + bus_size_t ti_offset; + + if (cnt < TI_WINLEN) + segsize = cnt; + else + segsize = TI_WINLEN - (segptr % TI_WINLEN); + CSR_WRITE_4(sc, TI_WINBASE, (segptr & ~(TI_WINLEN - 1))); + + ti_offset = TI_WINDOW + (segptr & (TI_WINLEN -1)); + + if (readdata) { + + bus_space_read_region_4(sc->ti_btag, + sc->ti_bhandle, ti_offset, + (u_int32_t *)tmparray, + segsize >> 2); + if (useraddr) { + /* + * Yeah, this is a little on the kludgy + * side, but at least this code is only + * used for debugging. + */ + ti_bcopy_swap(tmparray, tmparray2, segsize, + TI_SWAP_NTOH); + + if (first_pass) { + copyout(&tmparray2[segresid], ptr, + segsize - segresid); + first_pass = 0; + } else + copyout(tmparray2, ptr, segsize); + } else { + if (first_pass) { + + ti_bcopy_swap(tmparray, tmparray2, + segsize, TI_SWAP_NTOH); + bcopy(&tmparray2[segresid], ptr, + segsize - segresid); + first_pass = 0; + } else + ti_bcopy_swap(tmparray, ptr, segsize, + TI_SWAP_NTOH); + } + + } else { + if (useraddr) { + copyin(ptr, tmparray2, segsize); + ti_bcopy_swap(tmparray2, tmparray, segsize, + TI_SWAP_HTON); + } else + ti_bcopy_swap(ptr, tmparray, segsize, + TI_SWAP_HTON); + + bus_space_write_region_4(sc->ti_btag, + sc->ti_bhandle, ti_offset, + (u_int32_t *)tmparray, + segsize >> 2); + } + segptr += segsize; + ptr += segsize; + cnt -= segsize; + } + + /* + * Handle leftover, non-word-aligned bytes. + */ + if (resid != 0) { + u_int32_t tmpval, tmpval2; + bus_size_t ti_offset; + + /* + * Set the segment pointer. + */ + CSR_WRITE_4(sc, TI_WINBASE, (segptr & ~(TI_WINLEN - 1))); + + ti_offset = TI_WINDOW + (segptr & (TI_WINLEN - 1)); + + /* + * First, grab whatever is in our source/destination. + * We'll obviously need this for reads, but also for + * writes, since we'll be doing read/modify/write. + */ + bus_space_read_region_4(sc->ti_btag, sc->ti_bhandle, + ti_offset, &tmpval, 1); + + /* + * Next, translate this from little-endian to big-endian + * (at least on i386 boxes). + */ + tmpval2 = ntohl(tmpval); + + if (readdata) { + /* + * If we're reading, just copy the leftover number + * of bytes from the host byte order buffer to + * the user's buffer. + */ + if (useraddr) + copyout(&tmpval2, ptr, resid); + else + bcopy(&tmpval2, ptr, resid); + } else { + /* + * If we're writing, first copy the bytes to be + * written into the network byte order buffer, + * leaving the rest of the buffer with whatever was + * originally in there. Then, swap the bytes + * around into host order and write them out. + * + * XXX KDM the read side of this has been verified + * to work, but the write side of it has not been + * verified. So user beware. + */ + if (useraddr) + copyin(ptr, &tmpval2, resid); + else + bcopy(ptr, &tmpval2, resid); + + tmpval = htonl(tmpval2); + + bus_space_write_region_4(sc->ti_btag, sc->ti_bhandle, + ti_offset, &tmpval, 1); + } + } + + CSR_WRITE_4(sc, TI_WINBASE, origwin); + + TI_UNLOCK(sc); + + return(0); +} + +static int +ti_copy_scratch(sc, tigon_addr, len, buf, useraddr, readdata, cpu) + struct ti_softc *sc; + u_int32_t tigon_addr, len; + caddr_t buf; + int useraddr, readdata; + int cpu; +{ + u_int32_t segptr; + int cnt; + u_int32_t tmpval, tmpval2; + caddr_t ptr; + + /* + * At the moment, we don't handle non-aligned cases, we just bail. + * If this proves to be a problem, it will be fixed. + */ + if (tigon_addr & 0x3) { + printf("ti%d: ti_copy_scratch: tigon address %#x isn't " + "word-aligned\n", sc->ti_unit, tigon_addr); + return(EINVAL); + } + + if (len & 0x3) { + printf("ti%d: ti_copy_scratch: transfer length %d isn't " + "word-aligned\n", sc->ti_unit, len); + return(EINVAL); + } + + segptr = tigon_addr; + cnt = len; + ptr = buf; + + TI_LOCK(sc); + + while (cnt) { + CSR_WRITE_4(sc, CPU_REG(TI_SRAM_ADDR, cpu), segptr); + + if (readdata) { + tmpval2 = CSR_READ_4(sc, CPU_REG(TI_SRAM_DATA, cpu)); + + tmpval = ntohl(tmpval2); + + /* + * Note: I've used this debugging interface + * extensively with Alteon's 12.3.15 firmware, + * compiled with GCC 2.7.2.1 and binutils 2.9.1. + * + * When you compile the firmware without + * optimization, which is necessary sometimes in + * order to properly step through it, you sometimes + * read out a bogus value of 0xc0017c instead of + * whatever was supposed to be in that scratchpad + * location. That value is on the stack somewhere, + * but I've never been able to figure out what was + * causing the problem. + * + * The address seems to pop up in random places, + * often not in the same place on two subsequent + * reads. + * + * In any case, the underlying data doesn't seem + * to be affected, just the value read out. + * + * KDM, 3/7/2000 + */ + + if (tmpval2 == 0xc0017c) + printf("ti%d: found 0xc0017c at %#x " + "(tmpval2)\n", sc->ti_unit, segptr); + + if (tmpval == 0xc0017c) + printf("ti%d: found 0xc0017c at %#x " + "(tmpval)\n", sc->ti_unit, segptr); + + if (useraddr) + copyout(&tmpval, ptr, 4); + else + bcopy(&tmpval, ptr, 4); + } else { + if (useraddr) + copyin(ptr, &tmpval2, 4); + else + bcopy(ptr, &tmpval2, 4); + + tmpval = htonl(tmpval2); + + CSR_WRITE_4(sc, CPU_REG(TI_SRAM_DATA, cpu), tmpval); + } + + cnt -= 4; + segptr += 4; + ptr += 4; + } + + TI_UNLOCK(sc); + + return(0); +} + +static int +ti_bcopy_swap(src, dst, len, swap_type) + const void *src; + void *dst; + size_t len; + ti_swap_type swap_type; +{ + const u_int8_t *tmpsrc; + u_int8_t *tmpdst; + size_t tmplen; + + if (len & 0x3) { + printf("ti_bcopy_swap: length %d isn't 32-bit aligned\n", + len); + return(-1); + } + + tmpsrc = src; + tmpdst = dst; + tmplen = len; + + while (tmplen) { + if (swap_type == TI_SWAP_NTOH) + *(u_int32_t *)tmpdst = + ntohl(*(const u_int32_t *)tmpsrc); + else + *(u_int32_t *)tmpdst = + htonl(*(const u_int32_t *)tmpsrc); + + tmpsrc += 4; + tmpdst += 4; + tmplen -= 4; + } + + return(0); +} + +/* + * Load firmware image into the NIC. Check that the firmware revision + * is acceptable and see if we want the firmware for the Tigon 1 or + * Tigon 2. + */ +static void ti_loadfw(sc) + struct ti_softc *sc; +{ + switch(sc->ti_hwrev) { + case TI_HWREV_TIGON: + if (tigonFwReleaseMajor != TI_FIRMWARE_MAJOR || + tigonFwReleaseMinor != TI_FIRMWARE_MINOR || + tigonFwReleaseFix != TI_FIRMWARE_FIX) { + printf("ti%d: firmware revision mismatch; want " + "%d.%d.%d, got %d.%d.%d\n", sc->ti_unit, + TI_FIRMWARE_MAJOR, TI_FIRMWARE_MINOR, + TI_FIRMWARE_FIX, tigonFwReleaseMajor, + tigonFwReleaseMinor, tigonFwReleaseFix); + return; + } + ti_mem(sc, tigonFwTextAddr, tigonFwTextLen, + (caddr_t)tigonFwText); + ti_mem(sc, tigonFwDataAddr, tigonFwDataLen, + (caddr_t)tigonFwData); + ti_mem(sc, tigonFwRodataAddr, tigonFwRodataLen, + (caddr_t)tigonFwRodata); + ti_mem(sc, tigonFwBssAddr, tigonFwBssLen, NULL); + ti_mem(sc, tigonFwSbssAddr, tigonFwSbssLen, NULL); + CSR_WRITE_4(sc, TI_CPU_PROGRAM_COUNTER, tigonFwStartAddr); + break; + case TI_HWREV_TIGON_II: + if (tigon2FwReleaseMajor != TI_FIRMWARE_MAJOR || + tigon2FwReleaseMinor != TI_FIRMWARE_MINOR || + tigon2FwReleaseFix != TI_FIRMWARE_FIX) { + printf("ti%d: firmware revision mismatch; want " + "%d.%d.%d, got %d.%d.%d\n", sc->ti_unit, + TI_FIRMWARE_MAJOR, TI_FIRMWARE_MINOR, + TI_FIRMWARE_FIX, tigon2FwReleaseMajor, + tigon2FwReleaseMinor, tigon2FwReleaseFix); + return; + } + ti_mem(sc, tigon2FwTextAddr, tigon2FwTextLen, + (caddr_t)tigon2FwText); + ti_mem(sc, tigon2FwDataAddr, tigon2FwDataLen, + (caddr_t)tigon2FwData); + ti_mem(sc, tigon2FwRodataAddr, tigon2FwRodataLen, + (caddr_t)tigon2FwRodata); + ti_mem(sc, tigon2FwBssAddr, tigon2FwBssLen, NULL); + ti_mem(sc, tigon2FwSbssAddr, tigon2FwSbssLen, NULL); + CSR_WRITE_4(sc, TI_CPU_PROGRAM_COUNTER, tigon2FwStartAddr); + break; + default: + printf("ti%d: can't load firmware: unknown hardware rev\n", + sc->ti_unit); + break; + } + + return; +} + +/* + * Send the NIC a command via the command ring. + */ +static void ti_cmd(sc, cmd) + struct ti_softc *sc; + struct ti_cmd_desc *cmd; +{ + u_int32_t index; + + if (sc->ti_rdata->ti_cmd_ring == NULL) + return; + + index = sc->ti_cmd_saved_prodidx; + CSR_WRITE_4(sc, TI_GCR_CMDRING + (index * 4), *(u_int32_t *)(cmd)); + TI_INC(index, TI_CMD_RING_CNT); + CSR_WRITE_4(sc, TI_MB_CMDPROD_IDX, index); + sc->ti_cmd_saved_prodidx = index; + + return; +} + +/* + * Send the NIC an extended command. The 'len' parameter specifies the + * number of command slots to include after the initial command. + */ +static void ti_cmd_ext(sc, cmd, arg, len) + struct ti_softc *sc; + struct ti_cmd_desc *cmd; + caddr_t arg; + int len; +{ + u_int32_t index; + register int i; + + if (sc->ti_rdata->ti_cmd_ring == NULL) + return; + + index = sc->ti_cmd_saved_prodidx; + CSR_WRITE_4(sc, TI_GCR_CMDRING + (index * 4), *(u_int32_t *)(cmd)); + TI_INC(index, TI_CMD_RING_CNT); + for (i = 0; i < len; i++) { + CSR_WRITE_4(sc, TI_GCR_CMDRING + (index * 4), + *(u_int32_t *)(&arg[i * 4])); + TI_INC(index, TI_CMD_RING_CNT); + } + CSR_WRITE_4(sc, TI_MB_CMDPROD_IDX, index); + sc->ti_cmd_saved_prodidx = index; + + return; +} + +/* + * Handle events that have triggered interrupts. + */ +static void ti_handle_events(sc) + struct ti_softc *sc; +{ + struct ti_event_desc *e; + + if (sc->ti_rdata->ti_event_ring == NULL) + return; + + while (sc->ti_ev_saved_considx != sc->ti_ev_prodidx.ti_idx) { + e = &sc->ti_rdata->ti_event_ring[sc->ti_ev_saved_considx]; + switch(e->ti_event) { + case TI_EV_LINKSTAT_CHANGED: + sc->ti_linkstat = e->ti_code; + if (e->ti_code == TI_EV_CODE_LINK_UP) + printf("ti%d: 10/100 link up\n", sc->ti_unit); + else if (e->ti_code == TI_EV_CODE_GIG_LINK_UP) + printf("ti%d: gigabit link up\n", sc->ti_unit); + else if (e->ti_code == TI_EV_CODE_LINK_DOWN) + printf("ti%d: link down\n", sc->ti_unit); + break; + case TI_EV_ERROR: + if (e->ti_code == TI_EV_CODE_ERR_INVAL_CMD) + printf("ti%d: invalid command\n", sc->ti_unit); + else if (e->ti_code == TI_EV_CODE_ERR_UNIMP_CMD) + printf("ti%d: unknown command\n", sc->ti_unit); + else if (e->ti_code == TI_EV_CODE_ERR_BADCFG) + printf("ti%d: bad config data\n", sc->ti_unit); + break; + case TI_EV_FIRMWARE_UP: + ti_init2(sc); + break; + case TI_EV_STATS_UPDATED: + ti_stats_update(sc); + break; + case TI_EV_RESET_JUMBO_RING: + case TI_EV_MCAST_UPDATED: + /* Who cares. */ + break; + default: + printf("ti%d: unknown event: %d\n", + sc->ti_unit, e->ti_event); + break; + } + /* Advance the consumer index. */ + TI_INC(sc->ti_ev_saved_considx, TI_EVENT_RING_CNT); + CSR_WRITE_4(sc, TI_GCR_EVENTCONS_IDX, sc->ti_ev_saved_considx); + } + + return; +} + +#ifdef TI_PRIVATE_JUMBOS + +/* + * Memory management for the jumbo receive ring is a pain in the + * butt. We need to allocate at least 9018 bytes of space per frame, + * _and_ it has to be contiguous (unless you use the extended + * jumbo descriptor format). Using malloc() all the time won't + * work: malloc() allocates memory in powers of two, which means we + * would end up wasting a considerable amount of space by allocating + * 9K chunks. We don't have a jumbo mbuf cluster pool. Thus, we have + * to do our own memory management. + * + * The driver needs to allocate a contiguous chunk of memory at boot + * time. We then chop this up ourselves into 9K pieces and use them + * as external mbuf storage. + * + * One issue here is how much memory to allocate. The jumbo ring has + * 256 slots in it, but at 9K per slot than can consume over 2MB of + * RAM. This is a bit much, especially considering we also need + * RAM for the standard ring and mini ring (on the Tigon 2). To + * save space, we only actually allocate enough memory for 64 slots + * by default, which works out to between 500 and 600K. This can + * be tuned by changing a #define in if_tireg.h. + */ + +static int ti_alloc_jumbo_mem(sc) + struct ti_softc *sc; +{ + caddr_t ptr; + register int i; + struct ti_jpool_entry *entry; + + /* Grab a big chunk o' storage. */ + sc->ti_cdata.ti_jumbo_buf = contigmalloc(TI_JMEM, M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc->ti_cdata.ti_jumbo_buf == NULL) { + printf("ti%d: no memory for jumbo buffers!\n", sc->ti_unit); + return(ENOBUFS); + } + + SLIST_INIT(&sc->ti_jfree_listhead); + SLIST_INIT(&sc->ti_jinuse_listhead); + + /* + * Now divide it up into 9K pieces and save the addresses + * in an array. + */ + ptr = sc->ti_cdata.ti_jumbo_buf; + for (i = 0; i < TI_JSLOTS; i++) { + sc->ti_cdata.ti_jslots[i] = ptr; + ptr += TI_JLEN; + entry = malloc(sizeof(struct ti_jpool_entry), + M_DEVBUF, M_NOWAIT); + if (entry == NULL) { + contigfree(sc->ti_cdata.ti_jumbo_buf, TI_JMEM, + M_DEVBUF); + sc->ti_cdata.ti_jumbo_buf = NULL; + printf("ti%d: no memory for jumbo " + "buffer queue!\n", sc->ti_unit); + return(ENOBUFS); + } + entry->slot = i; + SLIST_INSERT_HEAD(&sc->ti_jfree_listhead, entry, jpool_entries); + } + + return(0); +} + +/* + * Allocate a jumbo buffer. + */ +static void *ti_jalloc(sc) + struct ti_softc *sc; +{ + struct ti_jpool_entry *entry; + + entry = SLIST_FIRST(&sc->ti_jfree_listhead); + + if (entry == NULL) { + printf("ti%d: no free jumbo buffers\n", sc->ti_unit); + return(NULL); + } + + SLIST_REMOVE_HEAD(&sc->ti_jfree_listhead, jpool_entries); + SLIST_INSERT_HEAD(&sc->ti_jinuse_listhead, entry, jpool_entries); + return(sc->ti_cdata.ti_jslots[entry->slot]); +} + +/* + * Release a jumbo buffer. + */ +static void ti_jfree(buf, args) + void *buf; + void *args; +{ + struct ti_softc *sc; + int i; + struct ti_jpool_entry *entry; + + /* Extract the softc struct pointer. */ + sc = (struct ti_softc *)args; + + if (sc == NULL) + panic("ti_jfree: didn't get softc pointer!"); + + /* calculate the slot this buffer belongs to */ + i = ((vm_offset_t)buf + - (vm_offset_t)sc->ti_cdata.ti_jumbo_buf) / TI_JLEN; + + if ((i < 0) || (i >= TI_JSLOTS)) + panic("ti_jfree: asked to free buffer that we don't manage!"); + + entry = SLIST_FIRST(&sc->ti_jinuse_listhead); + if (entry == NULL) + panic("ti_jfree: buffer not in use!"); + entry->slot = i; + SLIST_REMOVE_HEAD(&sc->ti_jinuse_listhead, jpool_entries); + SLIST_INSERT_HEAD(&sc->ti_jfree_listhead, entry, jpool_entries); + + return; +} + +#endif /* TI_PRIVATE_JUMBOS */ + +/* + * Intialize a standard receive ring descriptor. + */ +static int ti_newbuf_std(sc, i, m) + struct ti_softc *sc; + int i; + struct mbuf *m; +{ + struct mbuf *m_new = NULL; + struct ti_rx_desc *r; + + if (m == NULL) { + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(ENOBUFS); + + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + return(ENOBUFS); + } + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + } else { + m_new = m; + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + m_new->m_data = m_new->m_ext.ext_buf; + } + + m_adj(m_new, ETHER_ALIGN); + sc->ti_cdata.ti_rx_std_chain[i] = m_new; + r = &sc->ti_rdata->ti_rx_std_ring[i]; + TI_HOSTADDR(r->ti_addr) = vtophys(mtod(m_new, caddr_t)); + r->ti_type = TI_BDTYPE_RECV_BD; + r->ti_flags = 0; + if (sc->arpcom.ac_if.if_hwassist) + r->ti_flags |= TI_BDFLAG_TCP_UDP_CKSUM | TI_BDFLAG_IP_CKSUM; + r->ti_len = m_new->m_len; + r->ti_idx = i; + + return(0); +} + +/* + * Intialize a mini receive ring descriptor. This only applies to + * the Tigon 2. + */ +static int ti_newbuf_mini(sc, i, m) + struct ti_softc *sc; + int i; + struct mbuf *m; +{ + struct mbuf *m_new = NULL; + struct ti_rx_desc *r; + + if (m == NULL) { + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) { + return(ENOBUFS); + } + m_new->m_len = m_new->m_pkthdr.len = MHLEN; + } else { + m_new = m; + m_new->m_data = m_new->m_pktdat; + m_new->m_len = m_new->m_pkthdr.len = MHLEN; + } + + m_adj(m_new, ETHER_ALIGN); + r = &sc->ti_rdata->ti_rx_mini_ring[i]; + sc->ti_cdata.ti_rx_mini_chain[i] = m_new; + TI_HOSTADDR(r->ti_addr) = vtophys(mtod(m_new, caddr_t)); + r->ti_type = TI_BDTYPE_RECV_BD; + r->ti_flags = TI_BDFLAG_MINI_RING; + if (sc->arpcom.ac_if.if_hwassist) + r->ti_flags |= TI_BDFLAG_TCP_UDP_CKSUM | TI_BDFLAG_IP_CKSUM; + r->ti_len = m_new->m_len; + r->ti_idx = i; + + return(0); +} + +#ifdef TI_PRIVATE_JUMBOS + +/* + * Initialize a jumbo receive ring descriptor. This allocates + * a jumbo buffer from the pool managed internally by the driver. + */ +static int ti_newbuf_jumbo(sc, i, m) + struct ti_softc *sc; + int i; + struct mbuf *m; +{ + struct mbuf *m_new = NULL; + struct ti_rx_desc *r; + + if (m == NULL) { + caddr_t *buf = NULL; + + /* Allocate the mbuf. */ + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) { + return(ENOBUFS); + } + + /* Allocate the jumbo buffer */ + buf = ti_jalloc(sc); + if (buf == NULL) { + m_freem(m_new); + printf("ti%d: jumbo allocation failed " + "-- packet dropped!\n", sc->ti_unit); + return(ENOBUFS); + } + + /* Attach the buffer to the mbuf. */ + m_new->m_data = (void *) buf; + m_new->m_len = m_new->m_pkthdr.len = TI_JUMBO_FRAMELEN; + MEXTADD(m_new, buf, TI_JUMBO_FRAMELEN, ti_jfree, + (struct ti_softc *)sc, 0, EXT_NET_DRV); + } else { + m_new = m; + m_new->m_data = m_new->m_ext.ext_buf; + m_new->m_ext.ext_size = TI_JUMBO_FRAMELEN; + } + + m_adj(m_new, ETHER_ALIGN); + /* Set up the descriptor. */ + r = &sc->ti_rdata->ti_rx_jumbo_ring[i]; + sc->ti_cdata.ti_rx_jumbo_chain[i] = m_new; + TI_HOSTADDR(r->ti_addr) = vtophys(mtod(m_new, caddr_t)); + r->ti_type = TI_BDTYPE_RECV_JUMBO_BD; + r->ti_flags = TI_BDFLAG_JUMBO_RING; + if (sc->arpcom.ac_if.if_hwassist) + r->ti_flags |= TI_BDFLAG_TCP_UDP_CKSUM | TI_BDFLAG_IP_CKSUM; + r->ti_len = m_new->m_len; + r->ti_idx = i; + + return(0); +} + +#else +#include <vm/vm_page.h> + +#if (PAGE_SIZE == 4096) +#define NPAYLOAD 2 +#else +#define NPAYLOAD 1 +#endif + +#define TCP_HDR_LEN (52 + sizeof(struct ether_header)) +#define UDP_HDR_LEN (28 + sizeof(struct ether_header)) +#define NFS_HDR_LEN (UDP_HDR_LEN) +int HDR_LEN = TCP_HDR_LEN; + + + /* + * Initialize a jumbo receive ring descriptor. This allocates + * a jumbo buffer from the pool managed internally by the driver. + */ +static int +ti_newbuf_jumbo(sc, idx, m_old) + struct ti_softc *sc; + int idx; + struct mbuf *m_old; +{ + struct mbuf *cur, *m_new = NULL; + struct mbuf *m[3] = {NULL, NULL, NULL}; + struct ti_rx_desc_ext *r; + vm_page_t frame; + /* 1 extra buf to make nobufs easy*/ + caddr_t buf[3] = {NULL, NULL, NULL}; + int i; + + if (m_old != NULL) { + m_new = m_old; + cur = m_old->m_next; + for (i = 0; i <= NPAYLOAD; i++){ + m[i] = cur; + cur = cur->m_next; + } + } else { + /* Allocate the mbufs. */ + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) { + printf("ti%d: mbuf allocation failed " + "-- packet dropped!\n", sc->ti_unit); + goto nobufs; + } + MGET(m[NPAYLOAD], M_DONTWAIT, MT_DATA); + if (m[NPAYLOAD] == NULL) { + printf("ti%d: cluster mbuf allocation failed " + "-- packet dropped!\n", sc->ti_unit); + goto nobufs; + } + MCLGET(m[NPAYLOAD], M_DONTWAIT); + if ((m[NPAYLOAD]->m_flags & M_EXT) == 0) { + printf("ti%d: mbuf allocation failed " + "-- packet dropped!\n", sc->ti_unit); + goto nobufs; + } + m[NPAYLOAD]->m_len = MCLBYTES; + + for (i = 0; i < NPAYLOAD; i++){ + MGET(m[i], M_DONTWAIT, MT_DATA); + if (m[i] == NULL) { + printf("ti%d: mbuf allocation failed " + "-- packet dropped!\n", sc->ti_unit); + goto nobufs; + } + if (!(frame = jumbo_pg_alloc())){ + printf("ti%d: buffer allocation failed " + "-- packet dropped!\n", sc->ti_unit); + printf(" index %d page %d\n", idx, i); + goto nobufs; + } + buf[i] = jumbo_phys_to_kva(VM_PAGE_TO_PHYS(frame)); + } + for (i = 0; i < NPAYLOAD; i++){ + /* Attach the buffer to the mbuf. */ + m[i]->m_data = (void *)buf[i]; + m[i]->m_len = PAGE_SIZE; + MEXTADD(m[i], (void *)buf[i], PAGE_SIZE, + jumbo_freem, NULL, 0, EXT_DISPOSABLE); + m[i]->m_next = m[i+1]; + } + /* link the buffers to the header */ + m_new->m_next = m[0]; + m_new->m_data += ETHER_ALIGN; + if (sc->ti_hdrsplit) + m_new->m_len = MHLEN - ETHER_ALIGN; + else + m_new->m_len = HDR_LEN; + m_new->m_pkthdr.len = NPAYLOAD * PAGE_SIZE + m_new->m_len; + } + + /* Set up the descriptor. */ + r = &sc->ti_rdata->ti_rx_jumbo_ring[idx]; + sc->ti_cdata.ti_rx_jumbo_chain[idx] = m_new; + TI_HOSTADDR(r->ti_addr0) = vtophys(mtod(m_new, caddr_t)); + r->ti_len0 = m_new->m_len; + + TI_HOSTADDR(r->ti_addr1) = vtophys(mtod(m[0], caddr_t)); + r->ti_len1 = PAGE_SIZE; + + TI_HOSTADDR(r->ti_addr2) = vtophys(mtod(m[1], caddr_t)); + r->ti_len2 = m[1]->m_ext.ext_size; /* could be PAGE_SIZE or MCLBYTES */ + + if (PAGE_SIZE == 4096) { + TI_HOSTADDR(r->ti_addr3) = vtophys(mtod(m[2], caddr_t)); + r->ti_len3 = MCLBYTES; + } else { + r->ti_len3 = 0; + } + r->ti_type = TI_BDTYPE_RECV_JUMBO_BD; + + r->ti_flags = TI_BDFLAG_JUMBO_RING|TI_RCB_FLAG_USE_EXT_RX_BD; + + if (sc->arpcom.ac_if.if_hwassist) + r->ti_flags |= TI_BDFLAG_TCP_UDP_CKSUM|TI_BDFLAG_IP_CKSUM; + + r->ti_idx = idx; + + return(0); + + nobufs: + + /* + * Warning! : + * This can only be called before the mbufs are strung together. + * If the mbufs are strung together, m_freem() will free the chain, + * so that the later mbufs will be freed multiple times. + */ + if (m_new) + m_freem(m_new); + + for(i = 0; i < 3; i++){ + if (m[i]) + m_freem(m[i]); + if (buf[i]) + jumbo_pg_free((vm_offset_t)buf[i]); + } + return ENOBUFS; +} +#endif + + + +/* + * The standard receive ring has 512 entries in it. At 2K per mbuf cluster, + * that's 1MB or memory, which is a lot. For now, we fill only the first + * 256 ring entries and hope that our CPU is fast enough to keep up with + * the NIC. + */ +static int ti_init_rx_ring_std(sc) + struct ti_softc *sc; +{ + register int i; + struct ti_cmd_desc cmd; + + for (i = 0; i < TI_SSLOTS; i++) { + if (ti_newbuf_std(sc, i, NULL) == ENOBUFS) + return(ENOBUFS); + }; + + TI_UPDATE_STDPROD(sc, i - 1); + sc->ti_std = i - 1; + + return(0); +} + +static void ti_free_rx_ring_std(sc) + struct ti_softc *sc; +{ + register int i; + + for (i = 0; i < TI_STD_RX_RING_CNT; i++) { + if (sc->ti_cdata.ti_rx_std_chain[i] != NULL) { + m_freem(sc->ti_cdata.ti_rx_std_chain[i]); + sc->ti_cdata.ti_rx_std_chain[i] = NULL; + } + bzero((char *)&sc->ti_rdata->ti_rx_std_ring[i], + sizeof(struct ti_rx_desc)); + } + + return; +} + +static int ti_init_rx_ring_jumbo(sc) + struct ti_softc *sc; +{ + register int i; + struct ti_cmd_desc cmd; + + for (i = 0; i < TI_JUMBO_RX_RING_CNT; i++) { + if (ti_newbuf_jumbo(sc, i, NULL) == ENOBUFS) + return(ENOBUFS); + }; + + TI_UPDATE_JUMBOPROD(sc, i - 1); + sc->ti_jumbo = i - 1; + + return(0); +} + +static void ti_free_rx_ring_jumbo(sc) + struct ti_softc *sc; +{ + register int i; + + for (i = 0; i < TI_JUMBO_RX_RING_CNT; i++) { + if (sc->ti_cdata.ti_rx_jumbo_chain[i] != NULL) { + m_freem(sc->ti_cdata.ti_rx_jumbo_chain[i]); + sc->ti_cdata.ti_rx_jumbo_chain[i] = NULL; + } + bzero((char *)&sc->ti_rdata->ti_rx_jumbo_ring[i], + sizeof(struct ti_rx_desc)); + } + + return; +} + +static int ti_init_rx_ring_mini(sc) + struct ti_softc *sc; +{ + register int i; + + for (i = 0; i < TI_MSLOTS; i++) { + if (ti_newbuf_mini(sc, i, NULL) == ENOBUFS) + return(ENOBUFS); + }; + + TI_UPDATE_MINIPROD(sc, i - 1); + sc->ti_mini = i - 1; + + return(0); +} + +static void ti_free_rx_ring_mini(sc) + struct ti_softc *sc; +{ + register int i; + + for (i = 0; i < TI_MINI_RX_RING_CNT; i++) { + if (sc->ti_cdata.ti_rx_mini_chain[i] != NULL) { + m_freem(sc->ti_cdata.ti_rx_mini_chain[i]); + sc->ti_cdata.ti_rx_mini_chain[i] = NULL; + } + bzero((char *)&sc->ti_rdata->ti_rx_mini_ring[i], + sizeof(struct ti_rx_desc)); + } + + return; +} + +static void ti_free_tx_ring(sc) + struct ti_softc *sc; +{ + register int i; + + if (sc->ti_rdata->ti_tx_ring == NULL) + return; + + for (i = 0; i < TI_TX_RING_CNT; i++) { + if (sc->ti_cdata.ti_tx_chain[i] != NULL) { + m_freem(sc->ti_cdata.ti_tx_chain[i]); + sc->ti_cdata.ti_tx_chain[i] = NULL; + } + bzero((char *)&sc->ti_rdata->ti_tx_ring[i], + sizeof(struct ti_tx_desc)); + } + + return; +} + +static int ti_init_tx_ring(sc) + struct ti_softc *sc; +{ + sc->ti_txcnt = 0; + sc->ti_tx_saved_considx = 0; + CSR_WRITE_4(sc, TI_MB_SENDPROD_IDX, 0); + return(0); +} + +/* + * The Tigon 2 firmware has a new way to add/delete multicast addresses, + * but we have to support the old way too so that Tigon 1 cards will + * work. + */ +void ti_add_mcast(sc, addr) + struct ti_softc *sc; + struct ether_addr *addr; +{ + struct ti_cmd_desc cmd; + u_int16_t *m; + u_int32_t ext[2] = {0, 0}; + + m = (u_int16_t *)&addr->octet[0]; + + switch(sc->ti_hwrev) { + case TI_HWREV_TIGON: + CSR_WRITE_4(sc, TI_GCR_MAR0, htons(m[0])); + CSR_WRITE_4(sc, TI_GCR_MAR1, (htons(m[1]) << 16) | htons(m[2])); + TI_DO_CMD(TI_CMD_ADD_MCAST_ADDR, 0, 0); + break; + case TI_HWREV_TIGON_II: + ext[0] = htons(m[0]); + ext[1] = (htons(m[1]) << 16) | htons(m[2]); + TI_DO_CMD_EXT(TI_CMD_EXT_ADD_MCAST, 0, 0, (caddr_t)&ext, 2); + break; + default: + printf("ti%d: unknown hwrev\n", sc->ti_unit); + break; + } + + return; +} + +void ti_del_mcast(sc, addr) + struct ti_softc *sc; + struct ether_addr *addr; +{ + struct ti_cmd_desc cmd; + u_int16_t *m; + u_int32_t ext[2] = {0, 0}; + + m = (u_int16_t *)&addr->octet[0]; + + switch(sc->ti_hwrev) { + case TI_HWREV_TIGON: + CSR_WRITE_4(sc, TI_GCR_MAR0, htons(m[0])); + CSR_WRITE_4(sc, TI_GCR_MAR1, (htons(m[1]) << 16) | htons(m[2])); + TI_DO_CMD(TI_CMD_DEL_MCAST_ADDR, 0, 0); + break; + case TI_HWREV_TIGON_II: + ext[0] = htons(m[0]); + ext[1] = (htons(m[1]) << 16) | htons(m[2]); + TI_DO_CMD_EXT(TI_CMD_EXT_DEL_MCAST, 0, 0, (caddr_t)&ext, 2); + break; + default: + printf("ti%d: unknown hwrev\n", sc->ti_unit); + break; + } + + return; +} + +/* + * Configure the Tigon's multicast address filter. + * + * The actual multicast table management is a bit of a pain, thanks to + * slight brain damage on the part of both Alteon and us. With our + * multicast code, we are only alerted when the multicast address table + * changes and at that point we only have the current list of addresses: + * we only know the current state, not the previous state, so we don't + * actually know what addresses were removed or added. The firmware has + * state, but we can't get our grubby mits on it, and there is no 'delete + * all multicast addresses' command. Hence, we have to maintain our own + * state so we know what addresses have been programmed into the NIC at + * any given time. + */ +static void ti_setmulti(sc) + struct ti_softc *sc; +{ + struct ifnet *ifp; + struct ifmultiaddr *ifma; + struct ti_cmd_desc cmd; + struct ti_mc_entry *mc; + u_int32_t intrs; + + ifp = &sc->arpcom.ac_if; + + if (ifp->if_flags & IFF_ALLMULTI) { + TI_DO_CMD(TI_CMD_SET_ALLMULTI, TI_CMD_CODE_ALLMULTI_ENB, 0); + return; + } else { + TI_DO_CMD(TI_CMD_SET_ALLMULTI, TI_CMD_CODE_ALLMULTI_DIS, 0); + } + + /* Disable interrupts. */ + intrs = CSR_READ_4(sc, TI_MB_HOSTINTR); + CSR_WRITE_4(sc, TI_MB_HOSTINTR, 1); + + /* First, zot all the existing filters. */ + while (SLIST_FIRST(&sc->ti_mc_listhead) != NULL) { + mc = SLIST_FIRST(&sc->ti_mc_listhead); + ti_del_mcast(sc, &mc->mc_addr); + SLIST_REMOVE_HEAD(&sc->ti_mc_listhead, mc_entries); + free(mc, M_DEVBUF); + } + + /* Now program new ones. */ + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + mc = malloc(sizeof(struct ti_mc_entry), M_DEVBUF, M_NOWAIT); + bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), + (char *)&mc->mc_addr, ETHER_ADDR_LEN); + SLIST_INSERT_HEAD(&sc->ti_mc_listhead, mc, mc_entries); + ti_add_mcast(sc, &mc->mc_addr); + } + + /* Re-enable interrupts. */ + CSR_WRITE_4(sc, TI_MB_HOSTINTR, intrs); + + return; +} + +/* + * Check to see if the BIOS has configured us for a 64 bit slot when + * we aren't actually in one. If we detect this condition, we can work + * around it on the Tigon 2 by setting a bit in the PCI state register, + * but for the Tigon 1 we must give up and abort the interface attach. + */ +static int ti_64bitslot_war(sc) + struct ti_softc *sc; +{ + if (!(CSR_READ_4(sc, TI_PCI_STATE) & TI_PCISTATE_32BIT_BUS)) { + CSR_WRITE_4(sc, 0x600, 0); + CSR_WRITE_4(sc, 0x604, 0); + CSR_WRITE_4(sc, 0x600, 0x5555AAAA); + if (CSR_READ_4(sc, 0x604) == 0x5555AAAA) { + if (sc->ti_hwrev == TI_HWREV_TIGON) + return(EINVAL); + else { + TI_SETBIT(sc, TI_PCI_STATE, + TI_PCISTATE_32BIT_BUS); + return(0); + } + } + } + + return(0); +} + +/* + * Do endian, PCI and DMA initialization. Also check the on-board ROM + * self-test results. + */ +static int ti_chipinit(sc) + struct ti_softc *sc; +{ + u_int32_t cacheline; + u_int32_t pci_writemax = 0; + u_int32_t hdrsplit; + + /* Initialize link to down state. */ + sc->ti_linkstat = TI_EV_CODE_LINK_DOWN; + + if (sc->arpcom.ac_if.if_capenable & IFCAP_HWCSUM) + sc->arpcom.ac_if.if_hwassist = TI_CSUM_FEATURES; + else + sc->arpcom.ac_if.if_hwassist = 0; + + /* Set endianness before we access any non-PCI registers. */ +#if BYTE_ORDER == BIG_ENDIAN + CSR_WRITE_4(sc, TI_MISC_HOST_CTL, + TI_MHC_BIGENDIAN_INIT | (TI_MHC_BIGENDIAN_INIT << 24)); +#else + CSR_WRITE_4(sc, TI_MISC_HOST_CTL, + TI_MHC_LITTLEENDIAN_INIT | (TI_MHC_LITTLEENDIAN_INIT << 24)); +#endif + + /* Check the ROM failed bit to see if self-tests passed. */ + if (CSR_READ_4(sc, TI_CPU_STATE) & TI_CPUSTATE_ROMFAIL) { + printf("ti%d: board self-diagnostics failed!\n", sc->ti_unit); + return(ENODEV); + } + + /* Halt the CPU. */ + TI_SETBIT(sc, TI_CPU_STATE, TI_CPUSTATE_HALT); + + /* Figure out the hardware revision. */ + switch(CSR_READ_4(sc, TI_MISC_HOST_CTL) & TI_MHC_CHIP_REV_MASK) { + case TI_REV_TIGON_I: + sc->ti_hwrev = TI_HWREV_TIGON; + break; + case TI_REV_TIGON_II: + sc->ti_hwrev = TI_HWREV_TIGON_II; + break; + default: + printf("ti%d: unsupported chip revision\n", sc->ti_unit); + return(ENODEV); + } + + /* Do special setup for Tigon 2. */ + if (sc->ti_hwrev == TI_HWREV_TIGON_II) { + TI_SETBIT(sc, TI_CPU_CTL_B, TI_CPUSTATE_HALT); + TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_SRAM_BANK_512K); + TI_SETBIT(sc, TI_MISC_CONF, TI_MCR_SRAM_SYNCHRONOUS); + } + + /* + * We don't have firmware source for the Tigon 1, so Tigon 1 boards + * can't do header splitting. + */ +#ifdef TI_JUMBO_HDRSPLIT + if (sc->ti_hwrev != TI_HWREV_TIGON) + sc->ti_hdrsplit = 1; + else + printf("ti%d: can't do header splitting on a Tigon I board\n", + sc->ti_unit); +#endif /* TI_JUMBO_HDRSPLIT */ + + /* Set up the PCI state register. */ + CSR_WRITE_4(sc, TI_PCI_STATE, TI_PCI_READ_CMD|TI_PCI_WRITE_CMD); + if (sc->ti_hwrev == TI_HWREV_TIGON_II) { + TI_SETBIT(sc, TI_PCI_STATE, TI_PCISTATE_USE_MEM_RD_MULT); + } + + /* Clear the read/write max DMA parameters. */ + TI_CLRBIT(sc, TI_PCI_STATE, (TI_PCISTATE_WRITE_MAXDMA| + TI_PCISTATE_READ_MAXDMA)); + + /* Get cache line size. */ + cacheline = CSR_READ_4(sc, TI_PCI_BIST) & 0xFF; + + /* + * If the system has set enabled the PCI memory write + * and invalidate command in the command register, set + * the write max parameter accordingly. This is necessary + * to use MWI with the Tigon 2. + */ + if (CSR_READ_4(sc, TI_PCI_CMDSTAT) & PCIM_CMD_MWIEN) { + switch(cacheline) { + case 1: + case 4: + case 8: + case 16: + case 32: + case 64: + break; + default: + /* Disable PCI memory write and invalidate. */ + if (bootverbose) + printf("ti%d: cache line size %d not " + "supported; disabling PCI MWI\n", + sc->ti_unit, cacheline); + CSR_WRITE_4(sc, TI_PCI_CMDSTAT, CSR_READ_4(sc, + TI_PCI_CMDSTAT) & ~PCIM_CMD_MWIEN); + break; + } + } + +#ifdef __brokenalpha__ + /* + * From the Alteon sample driver: + * Must insure that we do not cross an 8K (bytes) boundary + * for DMA reads. Our highest limit is 1K bytes. This is a + * restriction on some ALPHA platforms with early revision + * 21174 PCI chipsets, such as the AlphaPC 164lx + */ + TI_SETBIT(sc, TI_PCI_STATE, pci_writemax|TI_PCI_READMAX_1024); +#else + TI_SETBIT(sc, TI_PCI_STATE, pci_writemax); +#endif + + /* This sets the min dma param all the way up (0xff). */ + TI_SETBIT(sc, TI_PCI_STATE, TI_PCISTATE_MINDMA); + + if (sc->ti_hdrsplit) + hdrsplit = TI_OPMODE_JUMBO_HDRSPLIT; + else + hdrsplit = 0; + + /* Configure DMA variables. */ +#if BYTE_ORDER == BIG_ENDIAN + CSR_WRITE_4(sc, TI_GCR_OPMODE, TI_OPMODE_BYTESWAP_BD | + TI_OPMODE_BYTESWAP_DATA | TI_OPMODE_WORDSWAP_BD | + TI_OPMODE_WARN_ENB | TI_OPMODE_FATAL_ENB | + TI_OPMODE_DONT_FRAG_JUMBO | hdrsplit); +#else /* BYTE_ORDER */ + CSR_WRITE_4(sc, TI_GCR_OPMODE, TI_OPMODE_BYTESWAP_DATA| + TI_OPMODE_WORDSWAP_BD|TI_OPMODE_DONT_FRAG_JUMBO| + TI_OPMODE_WARN_ENB|TI_OPMODE_FATAL_ENB | hdrsplit); +#endif /* BYTE_ORDER */ + + /* + * Only allow 1 DMA channel to be active at a time. + * I don't think this is a good idea, but without it + * the firmware racks up lots of nicDmaReadRingFull + * errors. This is not compatible with hardware checksums. + */ + if (sc->arpcom.ac_if.if_hwassist == 0) + TI_SETBIT(sc, TI_GCR_OPMODE, TI_OPMODE_1_DMA_ACTIVE); + + /* Recommended settings from Tigon manual. */ + CSR_WRITE_4(sc, TI_GCR_DMA_WRITECFG, TI_DMA_STATE_THRESH_8W); + CSR_WRITE_4(sc, TI_GCR_DMA_READCFG, TI_DMA_STATE_THRESH_8W); + + if (ti_64bitslot_war(sc)) { + printf("ti%d: bios thinks we're in a 64 bit slot, " + "but we aren't", sc->ti_unit); + return(EINVAL); + } + + return(0); +} + +/* + * Initialize the general information block and firmware, and + * start the CPU(s) running. + */ +static int ti_gibinit(sc) + struct ti_softc *sc; +{ + struct ti_rcb *rcb; + int i; + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + + /* Disable interrupts for now. */ + CSR_WRITE_4(sc, TI_MB_HOSTINTR, 1); + + /* Tell the chip where to find the general information block. */ + CSR_WRITE_4(sc, TI_GCR_GENINFO_HI, 0); + CSR_WRITE_4(sc, TI_GCR_GENINFO_LO, vtophys(&sc->ti_rdata->ti_info)); + + /* Load the firmware into SRAM. */ + ti_loadfw(sc); + + /* Set up the contents of the general info and ring control blocks. */ + + /* Set up the event ring and producer pointer. */ + rcb = &sc->ti_rdata->ti_info.ti_ev_rcb; + + TI_HOSTADDR(rcb->ti_hostaddr) = vtophys(&sc->ti_rdata->ti_event_ring); + rcb->ti_flags = 0; + TI_HOSTADDR(sc->ti_rdata->ti_info.ti_ev_prodidx_ptr) = + vtophys(&sc->ti_ev_prodidx); + sc->ti_ev_prodidx.ti_idx = 0; + CSR_WRITE_4(sc, TI_GCR_EVENTCONS_IDX, 0); + sc->ti_ev_saved_considx = 0; + + /* Set up the command ring and producer mailbox. */ + rcb = &sc->ti_rdata->ti_info.ti_cmd_rcb; + + sc->ti_rdata->ti_cmd_ring = + (struct ti_cmd_desc *)(sc->ti_vhandle + TI_GCR_CMDRING); + TI_HOSTADDR(rcb->ti_hostaddr) = TI_GCR_NIC_ADDR(TI_GCR_CMDRING); + rcb->ti_flags = 0; + rcb->ti_max_len = 0; + for (i = 0; i < TI_CMD_RING_CNT; i++) { + CSR_WRITE_4(sc, TI_GCR_CMDRING + (i * 4), 0); + } + CSR_WRITE_4(sc, TI_GCR_CMDCONS_IDX, 0); + CSR_WRITE_4(sc, TI_MB_CMDPROD_IDX, 0); + sc->ti_cmd_saved_prodidx = 0; + + /* + * Assign the address of the stats refresh buffer. + * We re-use the current stats buffer for this to + * conserve memory. + */ + TI_HOSTADDR(sc->ti_rdata->ti_info.ti_refresh_stats_ptr) = + vtophys(&sc->ti_rdata->ti_info.ti_stats); + + /* Set up the standard receive ring. */ + rcb = &sc->ti_rdata->ti_info.ti_std_rx_rcb; + TI_HOSTADDR(rcb->ti_hostaddr) = vtophys(&sc->ti_rdata->ti_rx_std_ring); + rcb->ti_max_len = TI_FRAMELEN; + rcb->ti_flags = 0; + if (sc->arpcom.ac_if.if_hwassist) + rcb->ti_flags |= TI_RCB_FLAG_TCP_UDP_CKSUM | + TI_RCB_FLAG_IP_CKSUM | TI_RCB_FLAG_NO_PHDR_CKSUM; + rcb->ti_flags |= TI_RCB_FLAG_VLAN_ASSIST; + + /* Set up the jumbo receive ring. */ + rcb = &sc->ti_rdata->ti_info.ti_jumbo_rx_rcb; + TI_HOSTADDR(rcb->ti_hostaddr) = + vtophys(&sc->ti_rdata->ti_rx_jumbo_ring); + +#ifdef TI_PRIVATE_JUMBOS + rcb->ti_max_len = TI_JUMBO_FRAMELEN; + rcb->ti_flags = 0; +#else + rcb->ti_max_len = PAGE_SIZE; + rcb->ti_flags = TI_RCB_FLAG_USE_EXT_RX_BD; +#endif + if (sc->arpcom.ac_if.if_hwassist) + rcb->ti_flags |= TI_RCB_FLAG_TCP_UDP_CKSUM | + TI_RCB_FLAG_IP_CKSUM | TI_RCB_FLAG_NO_PHDR_CKSUM; + rcb->ti_flags |= TI_RCB_FLAG_VLAN_ASSIST; + + /* + * Set up the mini ring. Only activated on the + * Tigon 2 but the slot in the config block is + * still there on the Tigon 1. + */ + rcb = &sc->ti_rdata->ti_info.ti_mini_rx_rcb; + TI_HOSTADDR(rcb->ti_hostaddr) = + vtophys(&sc->ti_rdata->ti_rx_mini_ring); + rcb->ti_max_len = MHLEN - ETHER_ALIGN; + if (sc->ti_hwrev == TI_HWREV_TIGON) + rcb->ti_flags = TI_RCB_FLAG_RING_DISABLED; + else + rcb->ti_flags = 0; + if (sc->arpcom.ac_if.if_hwassist) + rcb->ti_flags |= TI_RCB_FLAG_TCP_UDP_CKSUM | + TI_RCB_FLAG_IP_CKSUM | TI_RCB_FLAG_NO_PHDR_CKSUM; + rcb->ti_flags |= TI_RCB_FLAG_VLAN_ASSIST; + + /* + * Set up the receive return ring. + */ + rcb = &sc->ti_rdata->ti_info.ti_return_rcb; + TI_HOSTADDR(rcb->ti_hostaddr) = + vtophys(&sc->ti_rdata->ti_rx_return_ring); + rcb->ti_flags = 0; + rcb->ti_max_len = TI_RETURN_RING_CNT; + TI_HOSTADDR(sc->ti_rdata->ti_info.ti_return_prodidx_ptr) = + vtophys(&sc->ti_return_prodidx); + + /* + * Set up the tx ring. Note: for the Tigon 2, we have the option + * of putting the transmit ring in the host's address space and + * letting the chip DMA it instead of leaving the ring in the NIC's + * memory and accessing it through the shared memory region. We + * do this for the Tigon 2, but it doesn't work on the Tigon 1, + * so we have to revert to the shared memory scheme if we detect + * a Tigon 1 chip. + */ + CSR_WRITE_4(sc, TI_WINBASE, TI_TX_RING_BASE); + if (sc->ti_hwrev == TI_HWREV_TIGON) { + sc->ti_rdata->ti_tx_ring_nic = + (struct ti_tx_desc *)(sc->ti_vhandle + TI_WINDOW); + } + bzero((char *)sc->ti_rdata->ti_tx_ring, + TI_TX_RING_CNT * sizeof(struct ti_tx_desc)); + rcb = &sc->ti_rdata->ti_info.ti_tx_rcb; + if (sc->ti_hwrev == TI_HWREV_TIGON) + rcb->ti_flags = 0; + else + rcb->ti_flags = TI_RCB_FLAG_HOST_RING; + rcb->ti_flags |= TI_RCB_FLAG_VLAN_ASSIST; + if (sc->arpcom.ac_if.if_hwassist) + rcb->ti_flags |= TI_RCB_FLAG_TCP_UDP_CKSUM | + TI_RCB_FLAG_IP_CKSUM | TI_RCB_FLAG_NO_PHDR_CKSUM; + rcb->ti_max_len = TI_TX_RING_CNT; + if (sc->ti_hwrev == TI_HWREV_TIGON) + TI_HOSTADDR(rcb->ti_hostaddr) = TI_TX_RING_BASE; + else + TI_HOSTADDR(rcb->ti_hostaddr) = + vtophys(&sc->ti_rdata->ti_tx_ring); + TI_HOSTADDR(sc->ti_rdata->ti_info.ti_tx_considx_ptr) = + vtophys(&sc->ti_tx_considx); + + /* Set up tuneables */ +#if 0 + if (ifp->if_mtu > (ETHERMTU + ETHER_HDR_LEN + ETHER_CRC_LEN)) + CSR_WRITE_4(sc, TI_GCR_RX_COAL_TICKS, + (sc->ti_rx_coal_ticks / 10)); + else +#endif + CSR_WRITE_4(sc, TI_GCR_RX_COAL_TICKS, sc->ti_rx_coal_ticks); + CSR_WRITE_4(sc, TI_GCR_TX_COAL_TICKS, sc->ti_tx_coal_ticks); + CSR_WRITE_4(sc, TI_GCR_STAT_TICKS, sc->ti_stat_ticks); + CSR_WRITE_4(sc, TI_GCR_RX_MAX_COAL_BD, sc->ti_rx_max_coal_bds); + CSR_WRITE_4(sc, TI_GCR_TX_MAX_COAL_BD, sc->ti_tx_max_coal_bds); + CSR_WRITE_4(sc, TI_GCR_TX_BUFFER_RATIO, sc->ti_tx_buf_ratio); + + /* Turn interrupts on. */ + CSR_WRITE_4(sc, TI_GCR_MASK_INTRS, 0); + CSR_WRITE_4(sc, TI_MB_HOSTINTR, 0); + + /* Start CPU. */ + TI_CLRBIT(sc, TI_CPU_STATE, (TI_CPUSTATE_HALT|TI_CPUSTATE_STEP)); + + return(0); +} + +/* + * Probe for a Tigon chip. Check the PCI vendor and device IDs + * against our list and return its name if we find a match. + */ +static int ti_probe(dev) + device_t dev; +{ + struct ti_type *t; + + t = ti_devs; + + while(t->ti_name != NULL) { + if ((pci_get_vendor(dev) == t->ti_vid) && + (pci_get_device(dev) == t->ti_did)) { + device_set_desc(dev, t->ti_name); + return(0); + } + t++; + } + + return(ENXIO); +} + +#ifdef KLD_MODULE +static int +log2rndup(int len) +{ + int log2size = 0, t = len; + while (t > 1) { + log2size++; + t >>= 1; + } + if (len != (1 << log2size)) + log2size++; + return log2size; +} + +static int +ti_mbuf_sanity(device_t dev) +{ + if ((mbstat.m_msize != MSIZE) || mbstat.m_mclbytes != MCLBYTES){ + device_printf(dev, "\n"); + device_printf(dev, "This module was compiled with " + "-DMCLSHIFT=%d -DMSIZE=%d\n", MCLSHIFT, + MSIZE); + device_printf(dev, "The kernel was compiled with MCLSHIFT=%d," + " MSIZE=%d\n", log2rndup(mbstat.m_mclbytes), + (int)mbstat.m_msize); + return(EINVAL); + } + return(0); +} +#endif + + +static int ti_attach(dev) + device_t dev; +{ + u_int32_t command; + struct ifnet *ifp; + struct ti_softc *sc; + int unit, error = 0, rid; + + sc = NULL; + +#ifdef KLD_MODULE + if (ti_mbuf_sanity(dev)){ + device_printf(dev, "Module mbuf constants do not match " + "kernel constants!\n"); + device_printf(dev, "Rebuild the module or the kernel so " + "they match\n"); + device_printf(dev, "\n"); + error = EINVAL; + goto fail; + } +#endif + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + bzero(sc, sizeof(struct ti_softc)); + + mtx_init(&sc->ti_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF | MTX_RECURSE); + sc->arpcom.ac_if.if_capabilities = IFCAP_HWCSUM; + sc->arpcom.ac_if.if_capenable = sc->arpcom.ac_if.if_capabilities; + + /* + * Map control/status registers. + */ + pci_enable_busmaster(dev); + pci_enable_io(dev, SYS_RES_MEMORY); + command = pci_read_config(dev, PCIR_COMMAND, 4); + + if (!(command & PCIM_CMD_MEMEN)) { + printf("ti%d: failed to enable memory mapping!\n", unit); + error = ENXIO; + goto fail; + } + + rid = TI_PCI_LOMEM; + sc->ti_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE|PCI_RF_DENSE); + + if (sc->ti_res == NULL) { + printf ("ti%d: couldn't map memory\n", unit); + error = ENXIO; + goto fail; + } + + sc->ti_btag = rman_get_bustag(sc->ti_res); + sc->ti_bhandle = rman_get_bushandle(sc->ti_res); + sc->ti_vhandle = (vm_offset_t)rman_get_virtual(sc->ti_res); + + /* Allocate interrupt */ + rid = 0; + + sc->ti_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->ti_irq == NULL) { + printf("ti%d: couldn't map interrupt\n", unit); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->ti_irq, INTR_TYPE_NET, + ti_intr, sc, &sc->ti_intrhand); + + if (error) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ti_irq); + bus_release_resource(dev, SYS_RES_MEMORY, + TI_PCI_LOMEM, sc->ti_res); + printf("ti%d: couldn't set up irq\n", unit); + goto fail; + } + + sc->ti_unit = unit; + + if (ti_chipinit(sc)) { + printf("ti%d: chip initialization failed\n", sc->ti_unit); + bus_teardown_intr(dev, sc->ti_irq, sc->ti_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ti_irq); + bus_release_resource(dev, SYS_RES_MEMORY, + TI_PCI_LOMEM, sc->ti_res); + error = ENXIO; + goto fail; + } + + /* Zero out the NIC's on-board SRAM. */ + ti_mem(sc, 0x2000, 0x100000 - 0x2000, NULL); + + /* Init again -- zeroing memory may have clobbered some registers. */ + if (ti_chipinit(sc)) { + printf("ti%d: chip initialization failed\n", sc->ti_unit); + bus_teardown_intr(dev, sc->ti_irq, sc->ti_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ti_irq); + bus_release_resource(dev, SYS_RES_MEMORY, + TI_PCI_LOMEM, sc->ti_res); + error = ENXIO; + goto fail; + } + + /* + * Get station address from the EEPROM. Note: the manual states + * that the MAC address is at offset 0x8c, however the data is + * stored as two longwords (since that's how it's loaded into + * the NIC). This means the MAC address is actually preceded + * by two zero bytes. We need to skip over those. + */ + if (ti_read_eeprom(sc, (caddr_t)&sc->arpcom.ac_enaddr, + TI_EE_MAC_OFFSET + 2, ETHER_ADDR_LEN)) { + printf("ti%d: failed to read station address\n", unit); + bus_teardown_intr(dev, sc->ti_irq, sc->ti_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ti_irq); + bus_release_resource(dev, SYS_RES_MEMORY, + TI_PCI_LOMEM, sc->ti_res); + error = ENXIO; + goto fail; + } + + /* + * A Tigon chip was detected. Inform the world. + */ + printf("ti%d: Ethernet address: %6D\n", unit, + sc->arpcom.ac_enaddr, ":"); + + /* Allocate the general information block and ring buffers. */ + sc->ti_rdata = contigmalloc(sizeof(struct ti_ring_data), M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc->ti_rdata == NULL) { + bus_teardown_intr(dev, sc->ti_irq, sc->ti_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ti_irq); + bus_release_resource(dev, SYS_RES_MEMORY, + TI_PCI_LOMEM, sc->ti_res); + error = ENXIO; + printf("ti%d: no memory for list buffers!\n", sc->ti_unit); + goto fail; + } + + bzero(sc->ti_rdata, sizeof(struct ti_ring_data)); + + /* Try to allocate memory for jumbo buffers. */ +#ifdef TI_PRIVATE_JUMBOS + if (ti_alloc_jumbo_mem(sc)) { + printf("ti%d: jumbo buffer allocation failed\n", sc->ti_unit); + bus_teardown_intr(dev, sc->ti_irq, sc->ti_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ti_irq); + bus_release_resource(dev, SYS_RES_MEMORY, + TI_PCI_LOMEM, sc->ti_res); + contigfree(sc->ti_rdata, sizeof(struct ti_ring_data), + M_DEVBUF); + error = ENXIO; + goto fail; + } +#else + if (!jumbo_vm_init()) { + printf("ti%d: VM initialization failed!\n", sc->ti_unit); + bus_teardown_intr(dev, sc->ti_irq, sc->ti_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ti_irq); + bus_release_resource(dev, SYS_RES_MEMORY, + TI_PCI_LOMEM, sc->ti_res); + free(sc->ti_rdata, M_DEVBUF); + error = ENOMEM; + goto fail; + } +#endif + + /* + * We really need a better way to tell a 1000baseTX card + * from a 1000baseSX one, since in theory there could be + * OEMed 1000baseTX cards from lame vendors who aren't + * clever enough to change the PCI ID. For the moment + * though, the AceNIC is the only copper card available. + */ + if (pci_get_vendor(dev) == ALT_VENDORID && + pci_get_device(dev) == ALT_DEVICEID_ACENIC_COPPER) + sc->ti_copper = 1; + /* Ok, it's not the only copper card available. */ + if (pci_get_vendor(dev) == NG_VENDORID && + pci_get_device(dev) == NG_DEVICEID_GA620T) + sc->ti_copper = 1; + + /* Set default tuneable values. */ + sc->ti_stat_ticks = 2 * TI_TICKS_PER_SEC; +#if 0 + sc->ti_rx_coal_ticks = TI_TICKS_PER_SEC / 5000; +#endif + sc->ti_rx_coal_ticks = 170; + sc->ti_tx_coal_ticks = TI_TICKS_PER_SEC / 500; + sc->ti_rx_max_coal_bds = 64; +#if 0 + sc->ti_tx_max_coal_bds = 128; +#endif + sc->ti_tx_max_coal_bds = 32; + sc->ti_tx_buf_ratio = 21; + + /* Set up ifnet structure */ + ifp = &sc->arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_unit = sc->ti_unit; + ifp->if_name = "ti"; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + tis[unit] = sc; + ifp->if_ioctl = ti_ioctl; + ifp->if_output = ether_output; + ifp->if_start = ti_start; + ifp->if_watchdog = ti_watchdog; + ifp->if_init = ti_init; + ifp->if_mtu = ETHERMTU; + ifp->if_snd.ifq_maxlen = TI_TX_RING_CNT - 1; + + /* Set up ifmedia support. */ + ifmedia_init(&sc->ifmedia, IFM_IMASK, ti_ifmedia_upd, ti_ifmedia_sts); + if (sc->ti_copper) { + /* + * Copper cards allow manual 10/100 mode selection, + * but not manual 1000baseTX mode selection. Why? + * Becuase currently there's no way to specify the + * master/slave setting through the firmware interface, + * so Alteon decided to just bag it and handle it + * via autonegotiation. + */ + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); + ifmedia_add(&sc->ifmedia, + IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); + ifmedia_add(&sc->ifmedia, + IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_1000_T, 0, NULL); + ifmedia_add(&sc->ifmedia, + IFM_ETHER|IFM_1000_T|IFM_FDX, 0, NULL); + } else { + /* Fiber cards don't support 10/100 modes. */ + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_1000_SX, 0, NULL); + ifmedia_add(&sc->ifmedia, + IFM_ETHER|IFM_1000_SX|IFM_FDX, 0, NULL); + } + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); + ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_AUTO); + + /* + * We're assuming here that card initialization is a sequential + * thing. If it isn't, multiple cards probing at the same time + * could stomp on the list of softcs here. + */ + /* + * If this is the first card to be initialized, initialize the + * softc queue. + */ + if (unit == 0) + STAILQ_INIT(&ti_sc_list); + + STAILQ_INSERT_TAIL(&ti_sc_list, sc, ti_links); + + /* Register the device */ + sc->dev = make_dev(&ti_cdevsw, sc->ti_unit, UID_ROOT, GID_OPERATOR, + 0600, "ti%d", sc->ti_unit); + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); + return(0); + +fail: + mtx_destroy(&sc->ti_mtx); + return(error); +} + +/* + * Verify that our character special device is not currently + * open. Also track down any cached vnodes & kill them before + * the module is unloaded + */ +static int +ti_unref_special(device_t dev) +{ + struct vnode *ti_vn; + int count; + struct ti_softc *sc = sc = device_get_softc(dev); + + if (!vfinddev(sc->dev, VCHR, &ti_vn)) { + return 0; + } + + if ((count = vcount(ti_vn))) { + device_printf(dev, "%d refs to special device, " + "denying unload\n", count); + return count; + } + /* now we know that there's a vnode in the cache. We hunt it + down and kill it now, before unloading */ + vgone(ti_vn); + return(0); +} + + +static int ti_detach(dev) + device_t dev; +{ + struct ti_softc *sc; + struct ifnet *ifp; + + if (ti_unref_special(dev)) + return EBUSY; + + sc = device_get_softc(dev); + TI_LOCK(sc); + ifp = &sc->arpcom.ac_if; + + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + ti_stop(sc); + + bus_teardown_intr(dev, sc->ti_irq, sc->ti_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ti_irq); + bus_release_resource(dev, SYS_RES_MEMORY, TI_PCI_LOMEM, sc->ti_res); + +#ifdef TI_PRIVATE_JUMBOS + contigfree(sc->ti_cdata.ti_jumbo_buf, TI_JMEM, M_DEVBUF); +#endif + contigfree(sc->ti_rdata, sizeof(struct ti_ring_data), M_DEVBUF); + ifmedia_removeall(&sc->ifmedia); + + TI_UNLOCK(sc); + mtx_destroy(&sc->ti_mtx); + + return(0); +} + +#ifdef TI_JUMBO_HDRSPLIT +/* + * If hdr_len is 0, that means that header splitting wasn't done on + * this packet for some reason. The two most likely reasons are that + * the protocol isn't a supported protocol for splitting, or this + * packet had a fragment offset that wasn't 0. + * + * The header length, if it is non-zero, will always be the length of + * the headers on the packet, but that length could be longer than the + * first mbuf. So we take the minimum of the two as the actual + * length. + */ +static __inline void +ti_hdr_split(struct mbuf *top, int hdr_len, int pkt_len, int idx) +{ + int i = 0; + int lengths[4] = {0, 0, 0, 0}; + struct mbuf *m, *mp; + + if (hdr_len != 0) + top->m_len = min(hdr_len, top->m_len); + pkt_len -= top->m_len; + lengths[i++] = top->m_len; + + mp = top; + for (m = top->m_next; m && pkt_len; m = m->m_next) { + m->m_len = m->m_ext.ext_size = min(m->m_len, pkt_len); + pkt_len -= m->m_len; + lengths[i++] = m->m_len; + mp = m; + } + +#if 0 + if (hdr_len != 0) + printf("got split packet: "); + else + printf("got non-split packet: "); + + printf("%d,%d,%d,%d = %d\n", lengths[0], + lengths[1], lengths[2], lengths[3], + lengths[0] + lengths[1] + lengths[2] + + lengths[3]); +#endif + + if (pkt_len) + panic("header splitting didn't"); + + if (m) { + m_freem(m); + mp->m_next = NULL; + + } + if (mp->m_next != NULL) + panic("ti_hdr_split: last mbuf in chain should be null"); +} +#endif /* TI_JUMBO_HDRSPLIT */ + +/* + * Frame reception handling. This is called if there's a frame + * on the receive return list. + * + * Note: we have to be able to handle three possibilities here: + * 1) the frame is from the mini receive ring (can only happen) + * on Tigon 2 boards) + * 2) the frame is from the jumbo recieve ring + * 3) the frame is from the standard receive ring + */ + +static void ti_rxeof(sc) + struct ti_softc *sc; +{ + struct ifnet *ifp; + struct ti_cmd_desc cmd; + + ifp = &sc->arpcom.ac_if; + + while(sc->ti_rx_saved_considx != sc->ti_return_prodidx.ti_idx) { + struct ti_rx_desc *cur_rx; + u_int32_t rxidx; + struct ether_header *eh; + struct mbuf *m = NULL; + u_int16_t vlan_tag = 0; + int have_tag = 0; + + cur_rx = + &sc->ti_rdata->ti_rx_return_ring[sc->ti_rx_saved_considx]; + rxidx = cur_rx->ti_idx; + TI_INC(sc->ti_rx_saved_considx, TI_RETURN_RING_CNT); + + if (cur_rx->ti_flags & TI_BDFLAG_VLAN_TAG) { + have_tag = 1; + vlan_tag = cur_rx->ti_vlan_tag & 0xfff; + } + + if (cur_rx->ti_flags & TI_BDFLAG_JUMBO_RING) { + + TI_INC(sc->ti_jumbo, TI_JUMBO_RX_RING_CNT); + m = sc->ti_cdata.ti_rx_jumbo_chain[rxidx]; + sc->ti_cdata.ti_rx_jumbo_chain[rxidx] = NULL; + if (cur_rx->ti_flags & TI_BDFLAG_ERROR) { + ifp->if_ierrors++; + ti_newbuf_jumbo(sc, sc->ti_jumbo, m); + continue; + } + if (ti_newbuf_jumbo(sc, sc->ti_jumbo, NULL) == ENOBUFS) { + ifp->if_ierrors++; + ti_newbuf_jumbo(sc, sc->ti_jumbo, m); + continue; + } +#ifdef TI_PRIVATE_JUMBOS + m->m_len = cur_rx->ti_len; +#else /* TI_PRIVATE_JUMBOS */ +#ifdef TI_JUMBO_HDRSPLIT + if (sc->ti_hdrsplit) + ti_hdr_split(m, TI_HOSTADDR(cur_rx->ti_addr), + cur_rx->ti_len, rxidx); + else +#endif /* TI_JUMBO_HDRSPLIT */ + m_adj(m, cur_rx->ti_len - m->m_pkthdr.len); +#endif /* TI_PRIVATE_JUMBOS */ + } else if (cur_rx->ti_flags & TI_BDFLAG_MINI_RING) { + TI_INC(sc->ti_mini, TI_MINI_RX_RING_CNT); + m = sc->ti_cdata.ti_rx_mini_chain[rxidx]; + sc->ti_cdata.ti_rx_mini_chain[rxidx] = NULL; + if (cur_rx->ti_flags & TI_BDFLAG_ERROR) { + ifp->if_ierrors++; + ti_newbuf_mini(sc, sc->ti_mini, m); + continue; + } + if (ti_newbuf_mini(sc, sc->ti_mini, NULL) == ENOBUFS) { + ifp->if_ierrors++; + ti_newbuf_mini(sc, sc->ti_mini, m); + continue; + } + m->m_len = cur_rx->ti_len; + } else { + TI_INC(sc->ti_std, TI_STD_RX_RING_CNT); + m = sc->ti_cdata.ti_rx_std_chain[rxidx]; + sc->ti_cdata.ti_rx_std_chain[rxidx] = NULL; + if (cur_rx->ti_flags & TI_BDFLAG_ERROR) { + ifp->if_ierrors++; + ti_newbuf_std(sc, sc->ti_std, m); + continue; + } + if (ti_newbuf_std(sc, sc->ti_std, NULL) == ENOBUFS) { + ifp->if_ierrors++; + ti_newbuf_std(sc, sc->ti_std, m); + continue; + } + m->m_len = cur_rx->ti_len; + } + + m->m_pkthdr.len = cur_rx->ti_len; + ifp->if_ipackets++; + eh = mtod(m, struct ether_header *); + m->m_pkthdr.rcvif = ifp; + + /* Remove header from mbuf and pass it on. */ + m_adj(m, sizeof(struct ether_header)); + + if (ifp->if_hwassist) { + m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED | + CSUM_DATA_VALID; + if ((cur_rx->ti_ip_cksum ^ 0xffff) == 0) + m->m_pkthdr.csum_flags |= CSUM_IP_VALID; + m->m_pkthdr.csum_data = cur_rx->ti_tcp_udp_cksum; + } + + /* + * If we received a packet with a vlan tag, pass it + * to vlan_input() instead of ether_input(). + */ + if (have_tag) { + VLAN_INPUT_TAG(eh, m, vlan_tag); + have_tag = vlan_tag = 0; + continue; + } + ether_input(ifp, eh, m); + } + + /* Only necessary on the Tigon 1. */ + if (sc->ti_hwrev == TI_HWREV_TIGON) + CSR_WRITE_4(sc, TI_GCR_RXRETURNCONS_IDX, + sc->ti_rx_saved_considx); + + TI_UPDATE_STDPROD(sc, sc->ti_std); + TI_UPDATE_MINIPROD(sc, sc->ti_mini); + TI_UPDATE_JUMBOPROD(sc, sc->ti_jumbo); + + return; +} + +static void ti_txeof(sc) + struct ti_softc *sc; +{ + struct ti_tx_desc *cur_tx = NULL; + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + + /* + * Go through our tx ring and free mbufs for those + * frames that have been sent. + */ + while (sc->ti_tx_saved_considx != sc->ti_tx_considx.ti_idx) { + u_int32_t idx = 0; + + idx = sc->ti_tx_saved_considx; + if (sc->ti_hwrev == TI_HWREV_TIGON) { + if (idx > 383) + CSR_WRITE_4(sc, TI_WINBASE, + TI_TX_RING_BASE + 6144); + else if (idx > 255) + CSR_WRITE_4(sc, TI_WINBASE, + TI_TX_RING_BASE + 4096); + else if (idx > 127) + CSR_WRITE_4(sc, TI_WINBASE, + TI_TX_RING_BASE + 2048); + else + CSR_WRITE_4(sc, TI_WINBASE, + TI_TX_RING_BASE); + cur_tx = &sc->ti_rdata->ti_tx_ring_nic[idx % 128]; + } else + cur_tx = &sc->ti_rdata->ti_tx_ring[idx]; + if (cur_tx->ti_flags & TI_BDFLAG_END) + ifp->if_opackets++; + if (sc->ti_cdata.ti_tx_chain[idx] != NULL) { + m_freem(sc->ti_cdata.ti_tx_chain[idx]); + sc->ti_cdata.ti_tx_chain[idx] = NULL; + } + sc->ti_txcnt--; + TI_INC(sc->ti_tx_saved_considx, TI_TX_RING_CNT); + ifp->if_timer = 0; + } + + if (cur_tx != NULL) + ifp->if_flags &= ~IFF_OACTIVE; + + return; +} + +static void ti_intr(xsc) + void *xsc; +{ + struct ti_softc *sc; + struct ifnet *ifp; + + sc = xsc; + TI_LOCK(sc); + ifp = &sc->arpcom.ac_if; + +/*#ifdef notdef*/ + /* Avoid this for now -- checking this register is expensive. */ + /* Make sure this is really our interrupt. */ + if (!(CSR_READ_4(sc, TI_MISC_HOST_CTL) & TI_MHC_INTSTATE)) { + TI_UNLOCK(sc); + return; + } +/*#endif*/ + + /* Ack interrupt and stop others from occuring. */ + CSR_WRITE_4(sc, TI_MB_HOSTINTR, 1); + + if (ifp->if_flags & IFF_RUNNING) { + /* Check RX return ring producer/consumer */ + ti_rxeof(sc); + + /* Check TX ring producer/consumer */ + ti_txeof(sc); + } + + ti_handle_events(sc); + + /* Re-enable interrupts. */ + CSR_WRITE_4(sc, TI_MB_HOSTINTR, 0); + + if (ifp->if_flags & IFF_RUNNING && ifp->if_snd.ifq_head != NULL) + ti_start(ifp); + + TI_UNLOCK(sc); + + return; +} + +static void ti_stats_update(sc) + struct ti_softc *sc; +{ + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + + ifp->if_collisions += + (sc->ti_rdata->ti_info.ti_stats.dot3StatsSingleCollisionFrames + + sc->ti_rdata->ti_info.ti_stats.dot3StatsMultipleCollisionFrames + + sc->ti_rdata->ti_info.ti_stats.dot3StatsExcessiveCollisions + + sc->ti_rdata->ti_info.ti_stats.dot3StatsLateCollisions) - + ifp->if_collisions; + + return; +} + +/* + * Encapsulate an mbuf chain in the tx ring by coupling the mbuf data + * pointers to descriptors. + */ +static int ti_encap(sc, m_head, txidx) + struct ti_softc *sc; + struct mbuf *m_head; + u_int32_t *txidx; +{ + struct ti_tx_desc *f = NULL; + struct mbuf *m; + u_int32_t frag, cur, cnt = 0; + u_int16_t csum_flags = 0; + struct ifvlan *ifv = NULL; + + if ((m_head->m_flags & (M_PROTO1|M_PKTHDR)) == (M_PROTO1|M_PKTHDR) && + m_head->m_pkthdr.rcvif != NULL && + m_head->m_pkthdr.rcvif->if_type == IFT_L2VLAN) + ifv = m_head->m_pkthdr.rcvif->if_softc; + + m = m_head; + cur = frag = *txidx; + + if (m_head->m_pkthdr.csum_flags) { + if (m_head->m_pkthdr.csum_flags & CSUM_IP) + csum_flags |= TI_BDFLAG_IP_CKSUM; + if (m_head->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP)) + csum_flags |= TI_BDFLAG_TCP_UDP_CKSUM; + if (m_head->m_flags & M_LASTFRAG) + csum_flags |= TI_BDFLAG_IP_FRAG_END; + else if (m_head->m_flags & M_FRAG) + csum_flags |= TI_BDFLAG_IP_FRAG; + } + /* + * Start packing the mbufs in this chain into + * the fragment pointers. Stop when we run out + * of fragments or hit the end of the mbuf chain. + */ + for (m = m_head; m != NULL; m = m->m_next) { + if (m->m_len != 0) { + if (sc->ti_hwrev == TI_HWREV_TIGON) { + if (frag > 383) + CSR_WRITE_4(sc, TI_WINBASE, + TI_TX_RING_BASE + 6144); + else if (frag > 255) + CSR_WRITE_4(sc, TI_WINBASE, + TI_TX_RING_BASE + 4096); + else if (frag > 127) + CSR_WRITE_4(sc, TI_WINBASE, + TI_TX_RING_BASE + 2048); + else + CSR_WRITE_4(sc, TI_WINBASE, + TI_TX_RING_BASE); + f = &sc->ti_rdata->ti_tx_ring_nic[frag % 128]; + } else + f = &sc->ti_rdata->ti_tx_ring[frag]; + if (sc->ti_cdata.ti_tx_chain[frag] != NULL) + break; + TI_HOSTADDR(f->ti_addr) = vtophys(mtod(m, vm_offset_t)); + f->ti_len = m->m_len; + f->ti_flags = csum_flags; + + if (ifv != NULL) { + f->ti_flags |= TI_BDFLAG_VLAN_TAG; + f->ti_vlan_tag = ifv->ifv_tag & 0xfff; + } else { + f->ti_vlan_tag = 0; + } + + /* + * Sanity check: avoid coming within 16 descriptors + * of the end of the ring. + */ + if ((TI_TX_RING_CNT - (sc->ti_txcnt + cnt)) < 16) + return(ENOBUFS); + cur = frag; + TI_INC(frag, TI_TX_RING_CNT); + cnt++; + } + } + + if (m != NULL) + return(ENOBUFS); + + if (frag == sc->ti_tx_saved_considx) + return(ENOBUFS); + + if (sc->ti_hwrev == TI_HWREV_TIGON) + sc->ti_rdata->ti_tx_ring_nic[cur % 128].ti_flags |= + TI_BDFLAG_END; + else + sc->ti_rdata->ti_tx_ring[cur].ti_flags |= TI_BDFLAG_END; + sc->ti_cdata.ti_tx_chain[cur] = m_head; + sc->ti_txcnt += cnt; + + *txidx = frag; + + return(0); +} + +/* + * Main transmit routine. To avoid having to do mbuf copies, we put pointers + * to the mbuf data regions directly in the transmit descriptors. + */ +static void ti_start(ifp) + struct ifnet *ifp; +{ + struct ti_softc *sc; + struct mbuf *m_head = NULL; + u_int32_t prodidx = 0; + + sc = ifp->if_softc; + TI_LOCK(sc); + + prodidx = CSR_READ_4(sc, TI_MB_SENDPROD_IDX); + + while(sc->ti_cdata.ti_tx_chain[prodidx] == NULL) { + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + /* + * XXX + * safety overkill. If this is a fragmented packet chain + * with delayed TCP/UDP checksums, then only encapsulate + * it if we have enough descriptors to handle the entire + * chain at once. + * (paranoia -- may not actually be needed) + */ + if (m_head->m_flags & M_FIRSTFRAG && + m_head->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) { + if ((TI_TX_RING_CNT - sc->ti_txcnt) < + m_head->m_pkthdr.csum_data + 16) { + IF_PREPEND(&ifp->if_snd, m_head); + ifp->if_flags |= IFF_OACTIVE; + 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 (ti_encap(sc, m_head, &prodidx)) { + IF_PREPEND(&ifp->if_snd, m_head); + ifp->if_flags |= IFF_OACTIVE; + break; + } + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, m_head); + } + + /* Transmit */ + CSR_WRITE_4(sc, TI_MB_SENDPROD_IDX, prodidx); + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + TI_UNLOCK(sc); + + return; +} + +static void ti_init(xsc) + void *xsc; +{ + struct ti_softc *sc = xsc; + + /* Cancel pending I/O and flush buffers. */ + ti_stop(sc); + + TI_LOCK(sc); + /* Init the gen info block, ring control blocks and firmware. */ + if (ti_gibinit(sc)) { + printf("ti%d: initialization failure\n", sc->ti_unit); + TI_UNLOCK(sc); + return; + } + + TI_UNLOCK(sc); + + return; +} + +static void ti_init2(sc) + struct ti_softc *sc; +{ + struct ti_cmd_desc cmd; + struct ifnet *ifp; + u_int16_t *m; + struct ifmedia *ifm; + int tmp; + + ifp = &sc->arpcom.ac_if; + + /* Specify MTU and interface index. */ + CSR_WRITE_4(sc, TI_GCR_IFINDEX, ifp->if_unit); + CSR_WRITE_4(sc, TI_GCR_IFMTU, ifp->if_mtu + + ETHER_HDR_LEN + ETHER_CRC_LEN); + TI_DO_CMD(TI_CMD_UPDATE_GENCOM, 0, 0); + + /* Load our MAC address. */ + m = (u_int16_t *)&sc->arpcom.ac_enaddr[0]; + CSR_WRITE_4(sc, TI_GCR_PAR0, htons(m[0])); + CSR_WRITE_4(sc, TI_GCR_PAR1, (htons(m[1]) << 16) | htons(m[2])); + TI_DO_CMD(TI_CMD_SET_MAC_ADDR, 0, 0); + + /* Enable or disable promiscuous mode as needed. */ + if (ifp->if_flags & IFF_PROMISC) { + TI_DO_CMD(TI_CMD_SET_PROMISC_MODE, TI_CMD_CODE_PROMISC_ENB, 0); + } else { + TI_DO_CMD(TI_CMD_SET_PROMISC_MODE, TI_CMD_CODE_PROMISC_DIS, 0); + } + + /* Program multicast filter. */ + ti_setmulti(sc); + + /* + * If this is a Tigon 1, we should tell the + * firmware to use software packet filtering. + */ + if (sc->ti_hwrev == TI_HWREV_TIGON) { + TI_DO_CMD(TI_CMD_FDR_FILTERING, TI_CMD_CODE_FILT_ENB, 0); + } + + /* Init RX ring. */ + ti_init_rx_ring_std(sc); + + /* Init jumbo RX ring. */ + if (ifp->if_mtu > (ETHERMTU + ETHER_HDR_LEN + ETHER_CRC_LEN)) + ti_init_rx_ring_jumbo(sc); + + /* + * If this is a Tigon 2, we can also configure the + * mini ring. + */ + if (sc->ti_hwrev == TI_HWREV_TIGON_II) + ti_init_rx_ring_mini(sc); + + CSR_WRITE_4(sc, TI_GCR_RXRETURNCONS_IDX, 0); + sc->ti_rx_saved_considx = 0; + + /* Init TX ring. */ + ti_init_tx_ring(sc); + + /* Tell firmware we're alive. */ + TI_DO_CMD(TI_CMD_HOST_STATE, TI_CMD_CODE_STACK_UP, 0); + + /* Enable host interrupts. */ + CSR_WRITE_4(sc, TI_MB_HOSTINTR, 0); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + /* + * Make sure to set media properly. We have to do this + * here since we have to issue commands in order to set + * the link negotiation and we can't issue commands until + * the firmware is running. + */ + ifm = &sc->ifmedia; + tmp = ifm->ifm_media; + ifm->ifm_media = ifm->ifm_cur->ifm_media; + ti_ifmedia_upd(ifp); + ifm->ifm_media = tmp; + + return; +} + +/* + * Set media options. + */ +static int ti_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct ti_softc *sc; + struct ifmedia *ifm; + struct ti_cmd_desc cmd; + u_int32_t flowctl; + + sc = ifp->if_softc; + ifm = &sc->ifmedia; + + if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) + return(EINVAL); + + flowctl = 0; + + switch(IFM_SUBTYPE(ifm->ifm_media)) { + case IFM_AUTO: + /* + * Transmit flow control doesn't work on the Tigon 1. + */ + flowctl = TI_GLNK_RX_FLOWCTL_Y; + + /* + * Transmit flow control can also cause problems on the + * Tigon 2, apparantly with both the copper and fiber + * boards. The symptom is that the interface will just + * hang. This was reproduced with Alteon 180 switches. + */ +#if 0 + if (sc->ti_hwrev != TI_HWREV_TIGON) + flowctl |= TI_GLNK_TX_FLOWCTL_Y; +#endif + + CSR_WRITE_4(sc, TI_GCR_GLINK, TI_GLNK_PREF|TI_GLNK_1000MB| + TI_GLNK_FULL_DUPLEX| flowctl | + TI_GLNK_AUTONEGENB|TI_GLNK_ENB); + + flowctl = TI_LNK_RX_FLOWCTL_Y; +#if 0 + if (sc->ti_hwrev != TI_HWREV_TIGON) + flowctl |= TI_LNK_TX_FLOWCTL_Y; +#endif + + CSR_WRITE_4(sc, TI_GCR_LINK, TI_LNK_100MB|TI_LNK_10MB| + TI_LNK_FULL_DUPLEX|TI_LNK_HALF_DUPLEX| flowctl | + TI_LNK_AUTONEGENB|TI_LNK_ENB); + TI_DO_CMD(TI_CMD_LINK_NEGOTIATION, + TI_CMD_CODE_NEGOTIATE_BOTH, 0); + break; + case IFM_1000_SX: + case IFM_1000_T: + flowctl = TI_GLNK_RX_FLOWCTL_Y; +#if 0 + if (sc->ti_hwrev != TI_HWREV_TIGON) + flowctl |= TI_GLNK_TX_FLOWCTL_Y; +#endif + + CSR_WRITE_4(sc, TI_GCR_GLINK, TI_GLNK_PREF|TI_GLNK_1000MB| + flowctl |TI_GLNK_ENB); + CSR_WRITE_4(sc, TI_GCR_LINK, 0); + if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) { + TI_SETBIT(sc, TI_GCR_GLINK, TI_GLNK_FULL_DUPLEX); + } + TI_DO_CMD(TI_CMD_LINK_NEGOTIATION, + TI_CMD_CODE_NEGOTIATE_GIGABIT, 0); + break; + case IFM_100_FX: + case IFM_10_FL: + case IFM_100_TX: + case IFM_10_T: + flowctl = TI_LNK_RX_FLOWCTL_Y; +#if 0 + if (sc->ti_hwrev != TI_HWREV_TIGON) + flowctl |= TI_LNK_TX_FLOWCTL_Y; +#endif + + CSR_WRITE_4(sc, TI_GCR_GLINK, 0); + CSR_WRITE_4(sc, TI_GCR_LINK, TI_LNK_ENB|TI_LNK_PREF|flowctl); + if (IFM_SUBTYPE(ifm->ifm_media) == IFM_100_FX || + IFM_SUBTYPE(ifm->ifm_media) == IFM_100_TX) { + TI_SETBIT(sc, TI_GCR_LINK, TI_LNK_100MB); + } else { + TI_SETBIT(sc, TI_GCR_LINK, TI_LNK_10MB); + } + if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) { + TI_SETBIT(sc, TI_GCR_LINK, TI_LNK_FULL_DUPLEX); + } else { + TI_SETBIT(sc, TI_GCR_LINK, TI_LNK_HALF_DUPLEX); + } + TI_DO_CMD(TI_CMD_LINK_NEGOTIATION, + TI_CMD_CODE_NEGOTIATE_10_100, 0); + break; + } + + return(0); +} + +/* + * Report current media status. + */ +static void ti_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct ti_softc *sc; + u_int32_t media = 0; + + sc = ifp->if_softc; + + ifmr->ifm_status = IFM_AVALID; + ifmr->ifm_active = IFM_ETHER; + + if (sc->ti_linkstat == TI_EV_CODE_LINK_DOWN) + return; + + ifmr->ifm_status |= IFM_ACTIVE; + + if (sc->ti_linkstat == TI_EV_CODE_GIG_LINK_UP) { + media = CSR_READ_4(sc, TI_GCR_GLINK_STAT); + if (sc->ti_copper) + ifmr->ifm_active |= IFM_1000_T; + else + ifmr->ifm_active |= IFM_1000_SX; + if (media & TI_GLNK_FULL_DUPLEX) + ifmr->ifm_active |= IFM_FDX; + else + ifmr->ifm_active |= IFM_HDX; + } else if (sc->ti_linkstat == TI_EV_CODE_LINK_UP) { + media = CSR_READ_4(sc, TI_GCR_LINK_STAT); + if (sc->ti_copper) { + if (media & TI_LNK_100MB) + ifmr->ifm_active |= IFM_100_TX; + if (media & TI_LNK_10MB) + ifmr->ifm_active |= IFM_10_T; + } else { + if (media & TI_LNK_100MB) + ifmr->ifm_active |= IFM_100_FX; + if (media & TI_LNK_10MB) + ifmr->ifm_active |= IFM_10_FL; + } + if (media & TI_LNK_FULL_DUPLEX) + ifmr->ifm_active |= IFM_FDX; + if (media & TI_LNK_HALF_DUPLEX) + ifmr->ifm_active |= IFM_HDX; + } + + return; +} + +static int ti_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct ti_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + int mask, error = 0; + struct ti_cmd_desc cmd; + + TI_LOCK(sc); + + switch(command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFMTU: + if (ifr->ifr_mtu > TI_JUMBO_MTU) + error = EINVAL; + else { + ifp->if_mtu = ifr->ifr_mtu; + ti_init(sc); + } + break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + /* + * If only the state of the PROMISC flag changed, + * then just use the 'set promisc mode' command + * instead of reinitializing the entire NIC. Doing + * a full re-init means reloading the firmware and + * waiting for it to start up, which may take a + * second or two. + */ + if (ifp->if_flags & IFF_RUNNING && + ifp->if_flags & IFF_PROMISC && + !(sc->ti_if_flags & IFF_PROMISC)) { + TI_DO_CMD(TI_CMD_SET_PROMISC_MODE, + TI_CMD_CODE_PROMISC_ENB, 0); + } else if (ifp->if_flags & IFF_RUNNING && + !(ifp->if_flags & IFF_PROMISC) && + sc->ti_if_flags & IFF_PROMISC) { + TI_DO_CMD(TI_CMD_SET_PROMISC_MODE, + TI_CMD_CODE_PROMISC_DIS, 0); + } else + ti_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) { + ti_stop(sc); + } + } + sc->ti_if_flags = ifp->if_flags; + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + if (ifp->if_flags & IFF_RUNNING) { + ti_setmulti(sc); + error = 0; + } + break; + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); + break; + case SIOCSIFCAP: + mask = ifr->ifr_reqcap ^ ifp->if_capenable; + if (mask & IFCAP_HWCSUM) { + if (IFCAP_HWCSUM & ifp->if_capenable) + ifp->if_capenable &= ~IFCAP_HWCSUM; + else + ifp->if_capenable |= IFCAP_HWCSUM; + if (ifp->if_flags & IFF_RUNNING) + ti_init(sc); + } + error = 0; + break; + default: + error = EINVAL; + break; + } + + TI_UNLOCK(sc); + + return(error); +} + +static int +ti_open(dev_t dev, int flags, int fmt, struct thread *td) +{ + int unit; + struct ti_softc *sc; + + unit = minor(dev) & 0xff; + + sc = ti_lookup_softc(unit); + + if (sc == NULL) + return(ENODEV); + + TI_LOCK(sc); + sc->ti_flags |= TI_FLAG_DEBUGING; + TI_UNLOCK(sc); + + return(0); +} + +static int +ti_close(dev_t dev, int flag, int fmt, struct thread *td) +{ + int unit; + struct ti_softc *sc; + + unit = minor(dev) & 0xff; + + sc = ti_lookup_softc(unit); + + if (sc == NULL) + return(ENODEV); + + TI_LOCK(sc); + sc->ti_flags &= ~TI_FLAG_DEBUGING; + TI_UNLOCK(sc); + + return(0); +} + +/* + * This ioctl routine goes along with the Tigon character device. + */ +static int +ti_ioctl2(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td) +{ + int unit, error; + struct ti_softc *sc; + + unit = minor(dev) & 0xff; + + sc = ti_lookup_softc(unit); + + if (sc == NULL) + return(ENODEV); + + error = 0; + + switch(cmd) { + case TIIOCGETSTATS: + { + struct ti_stats *outstats; + + outstats = (struct ti_stats *)addr; + + bcopy(&sc->ti_rdata->ti_info.ti_stats, outstats, + sizeof(struct ti_stats)); + break; + } + case TIIOCGETPARAMS: + { + struct ti_params *params; + + params = (struct ti_params *)addr; + + params->ti_stat_ticks = sc->ti_stat_ticks; + params->ti_rx_coal_ticks = sc->ti_rx_coal_ticks; + params->ti_tx_coal_ticks = sc->ti_tx_coal_ticks; + params->ti_rx_max_coal_bds = sc->ti_rx_max_coal_bds; + params->ti_tx_max_coal_bds = sc->ti_tx_max_coal_bds; + params->ti_tx_buf_ratio = sc->ti_tx_buf_ratio; + params->param_mask = TI_PARAM_ALL; + + error = 0; + + break; + } + case TIIOCSETPARAMS: + { + struct ti_params *params; + + params = (struct ti_params *)addr; + + if (params->param_mask & TI_PARAM_STAT_TICKS) { + sc->ti_stat_ticks = params->ti_stat_ticks; + CSR_WRITE_4(sc, TI_GCR_STAT_TICKS, sc->ti_stat_ticks); + } + + if (params->param_mask & TI_PARAM_RX_COAL_TICKS) { + sc->ti_rx_coal_ticks = params->ti_rx_coal_ticks; + CSR_WRITE_4(sc, TI_GCR_RX_COAL_TICKS, + sc->ti_rx_coal_ticks); + } + + if (params->param_mask & TI_PARAM_TX_COAL_TICKS) { + sc->ti_tx_coal_ticks = params->ti_tx_coal_ticks; + CSR_WRITE_4(sc, TI_GCR_TX_COAL_TICKS, + sc->ti_tx_coal_ticks); + } + + if (params->param_mask & TI_PARAM_RX_COAL_BDS) { + sc->ti_rx_max_coal_bds = params->ti_rx_max_coal_bds; + CSR_WRITE_4(sc, TI_GCR_RX_MAX_COAL_BD, + sc->ti_rx_max_coal_bds); + } + + if (params->param_mask & TI_PARAM_TX_COAL_BDS) { + sc->ti_tx_max_coal_bds = params->ti_tx_max_coal_bds; + CSR_WRITE_4(sc, TI_GCR_TX_MAX_COAL_BD, + sc->ti_tx_max_coal_bds); + } + + if (params->param_mask & TI_PARAM_TX_BUF_RATIO) { + sc->ti_tx_buf_ratio = params->ti_tx_buf_ratio; + CSR_WRITE_4(sc, TI_GCR_TX_BUFFER_RATIO, + sc->ti_tx_buf_ratio); + } + + error = 0; + + break; + } + case TIIOCSETTRACE: { + ti_trace_type trace_type; + + trace_type = *(ti_trace_type *)addr; + + /* + * Set tracing to whatever the user asked for. Setting + * this register to 0 should have the effect of disabling + * tracing. + */ + CSR_WRITE_4(sc, TI_GCR_NIC_TRACING, trace_type); + + error = 0; + + break; + } + case TIIOCGETTRACE: { + struct ti_trace_buf *trace_buf; + u_int32_t trace_start, cur_trace_ptr, trace_len; + + trace_buf = (struct ti_trace_buf *)addr; + + trace_start = CSR_READ_4(sc, TI_GCR_NICTRACE_START); + cur_trace_ptr = CSR_READ_4(sc, TI_GCR_NICTRACE_PTR); + trace_len = CSR_READ_4(sc, TI_GCR_NICTRACE_LEN); + +#if 0 + printf("ti%d: trace_start = %#x, cur_trace_ptr = %#x, " + "trace_len = %d\n", sc->ti_unit, trace_start, + cur_trace_ptr, trace_len); + printf("ti%d: trace_buf->buf_len = %d\n", sc->ti_unit, + trace_buf->buf_len); +#endif + + error = ti_copy_mem(sc, trace_start, min(trace_len, + trace_buf->buf_len), + (caddr_t)trace_buf->buf, 1, 1); + + if (error == 0) { + trace_buf->fill_len = min(trace_len, + trace_buf->buf_len); + if (cur_trace_ptr < trace_start) + trace_buf->cur_trace_ptr = + trace_start - cur_trace_ptr; + else + trace_buf->cur_trace_ptr = + cur_trace_ptr - trace_start; + } else + trace_buf->fill_len = 0; + + + break; + } + + /* + * For debugging, five ioctls are needed: + * ALT_ATTACH + * ALT_READ_TG_REG + * ALT_WRITE_TG_REG + * ALT_READ_TG_MEM + * ALT_WRITE_TG_MEM + */ + case ALT_ATTACH: + /* + * From what I can tell, Alteon's Solaris Tigon driver + * only has one character device, so you have to attach + * to the Tigon board you're interested in. This seems + * like a not-so-good way to do things, since unless you + * subsequently specify the unit number of the device + * you're interested in in every ioctl, you'll only be + * able to debug one board at a time. + */ + error = 0; + break; + case ALT_READ_TG_MEM: + case ALT_WRITE_TG_MEM: + { + struct tg_mem *mem_param; + u_int32_t sram_end, scratch_end; + + mem_param = (struct tg_mem *)addr; + + if (sc->ti_hwrev == TI_HWREV_TIGON) { + sram_end = TI_END_SRAM_I; + scratch_end = TI_END_SCRATCH_I; + } else { + sram_end = TI_END_SRAM_II; + scratch_end = TI_END_SCRATCH_II; + } + + /* + * For now, we'll only handle accessing regular SRAM, + * nothing else. + */ + if ((mem_param->tgAddr >= TI_BEG_SRAM) + && ((mem_param->tgAddr + mem_param->len) <= sram_end)) { + /* + * In this instance, we always copy to/from user + * space, so the user space argument is set to 1. + */ + error = ti_copy_mem(sc, mem_param->tgAddr, + mem_param->len, + mem_param->userAddr, 1, + (cmd == ALT_READ_TG_MEM) ? 1 : 0); + } else if ((mem_param->tgAddr >= TI_BEG_SCRATCH) + && (mem_param->tgAddr <= scratch_end)) { + error = ti_copy_scratch(sc, mem_param->tgAddr, + mem_param->len, + mem_param->userAddr, 1, + (cmd == ALT_READ_TG_MEM) ? + 1 : 0, TI_PROCESSOR_A); + } else if ((mem_param->tgAddr >= TI_BEG_SCRATCH_B_DEBUG) + && (mem_param->tgAddr <= TI_BEG_SCRATCH_B_DEBUG)) { + if (sc->ti_hwrev == TI_HWREV_TIGON) { + printf("ti%d: invalid memory range for " + "Tigon I\n", sc->ti_unit); + error = EINVAL; + break; + } + error = ti_copy_scratch(sc, mem_param->tgAddr - + TI_SCRATCH_DEBUG_OFF, + mem_param->len, + mem_param->userAddr, 1, + (cmd == ALT_READ_TG_MEM) ? + 1 : 0, TI_PROCESSOR_B); + } else { + printf("ti%d: memory address %#x len %d is out of " + "supported range\n", sc->ti_unit, + mem_param->tgAddr, mem_param->len); + error = EINVAL; + } + + break; + } + case ALT_READ_TG_REG: + case ALT_WRITE_TG_REG: + { + struct tg_reg *regs; + u_int32_t tmpval; + + regs = (struct tg_reg *)addr; + + /* + * Make sure the address in question isn't out of range. + */ + if (regs->addr > TI_REG_MAX) { + error = EINVAL; + break; + } + if (cmd == ALT_READ_TG_REG) { + bus_space_read_region_4(sc->ti_btag, sc->ti_bhandle, + regs->addr, &tmpval, 1); + regs->data = ntohl(tmpval); +#if 0 + if ((regs->addr == TI_CPU_STATE) + || (regs->addr == TI_CPU_CTL_B)) { + printf("ti%d: register %#x = %#x\n", + sc->ti_unit, regs->addr, tmpval); + } +#endif + } else { + tmpval = htonl(regs->data); + bus_space_write_region_4(sc->ti_btag, sc->ti_bhandle, + regs->addr, &tmpval, 1); + } + + break; + } + default: + error = ENOTTY; + break; + } + return(error); +} + +static void ti_watchdog(ifp) + struct ifnet *ifp; +{ + struct ti_softc *sc; + + sc = ifp->if_softc; + TI_LOCK(sc); + + /* + * When we're debugging, the chip is often stopped for long periods + * of time, and that would normally cause the watchdog timer to fire. + * Since that impedes debugging, we don't want to do that. + */ + if (sc->ti_flags & TI_FLAG_DEBUGING) { + TI_UNLOCK(sc); + return; + } + + printf("ti%d: watchdog timeout -- resetting\n", sc->ti_unit); + ti_stop(sc); + ti_init(sc); + + ifp->if_oerrors++; + TI_UNLOCK(sc); + + return; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void ti_stop(sc) + struct ti_softc *sc; +{ + struct ifnet *ifp; + struct ti_cmd_desc cmd; + + TI_LOCK(sc); + + ifp = &sc->arpcom.ac_if; + + /* Disable host interrupts. */ + CSR_WRITE_4(sc, TI_MB_HOSTINTR, 1); + /* + * Tell firmware we're shutting down. + */ + TI_DO_CMD(TI_CMD_HOST_STATE, TI_CMD_CODE_STACK_DOWN, 0); + + /* Halt and reinitialize. */ + ti_chipinit(sc); + ti_mem(sc, 0x2000, 0x100000 - 0x2000, NULL); + ti_chipinit(sc); + + /* Free the RX lists. */ + ti_free_rx_ring_std(sc); + + /* Free jumbo RX list. */ + ti_free_rx_ring_jumbo(sc); + + /* Free mini RX list. */ + ti_free_rx_ring_mini(sc); + + /* Free TX buffers. */ + ti_free_tx_ring(sc); + + sc->ti_ev_prodidx.ti_idx = 0; + sc->ti_return_prodidx.ti_idx = 0; + sc->ti_tx_considx.ti_idx = 0; + sc->ti_tx_saved_considx = TI_TXCONS_UNSET; + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + TI_UNLOCK(sc); + + return; +} + +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static void ti_shutdown(dev) + device_t dev; +{ + struct ti_softc *sc; + + sc = device_get_softc(dev); + TI_LOCK(sc); + ti_chipinit(sc); + TI_UNLOCK(sc); + + return; +} diff --git a/sys/pci/if_tireg.h b/sys/pci/if_tireg.h new file mode 100644 index 0000000..e1d56f8 --- /dev/null +++ b/sys/pci/if_tireg.h @@ -0,0 +1,1067 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * Tigon register offsets. These are memory mapped registers + * which can be accessed with the CSR_READ_4()/CSR_WRITE_4() macros. + * Each register must be accessed using 32 bit operations. + * + * All reegisters are accessed through a 16K shared memory block. + * The first group of registers are actually copies of the PCI + * configuration space registers. + */ + +#define TI_PCI_ID 0x000 /* PCI device/vendor ID */ +#define TI_PCI_CMDSTAT 0x004 +#define TI_PCI_CLASSCODE 0x008 +#define TI_PCI_BIST 0x00C +#define TI_PCI_LOMEM 0x010 /* Shared memory base address */ +#define TI_PCI_SUBSYS 0x02C +#define TI_PCI_ROMBASE 0x030 +#define TI_PCI_INT 0x03C + +#ifndef PCIM_CMD_MWIEN +#define PCIM_CMD_MWIEN 0x0010 +#endif + +/* + * Alteon AceNIC PCI vendor/device ID. + */ +#define ALT_VENDORID 0x12AE +#define ALT_DEVICEID_ACENIC 0x0001 +#define ALT_DEVICEID_ACENIC_COPPER 0x0002 + +/* + * 3Com 3c985 PCI vendor/device ID. + */ +#define TC_VENDORID 0x10B7 +#define TC_DEVICEID_3C985 0x0001 + +/* + * Netgear GA620 PCI vendor/device ID. + */ +#define NG_VENDORID 0x1385 +#define NG_DEVICEID_GA620 0x620A +#define NG_DEVICEID_GA620T 0x630A + +/* + * SGI device/vendor ID. + */ +#define SGI_VENDORID 0x10A9 +#define SGI_DEVICEID_TIGON 0x0009 + +/* + * DEC vendor ID, Farallon device ID. Apparently, Farallon used + * the DEC vendor ID in their cards by mistake. + */ +#define DEC_VENDORID 0x1011 +#define DEC_DEVICEID_FARALLON_PN9000SX 0x001a + +/* + * Tigon configuration and control registers. + */ +#define TI_MISC_HOST_CTL 0x040 +#define TI_MISC_LOCAL_CTL 0x044 +#define TI_SEM_AB 0x048 /* Tigon 2 only */ +#define TI_MISC_CONF 0x050 /* Tigon 2 only */ +#define TI_TIMER_BITS 0x054 +#define TI_TIMERREF 0x058 +#define TI_PCI_STATE 0x05C +#define TI_MAIN_EVENT_A 0x060 +#define TI_MAILBOX_EVENT_A 0x064 +#define TI_WINBASE 0x068 +#define TI_WINDATA 0x06C +#define TI_MAIN_EVENT_B 0x070 /* Tigon 2 only */ +#define TI_MAILBOX_EVENT_B 0x074 /* Tigon 2 only */ +#define TI_TIMERREF_B 0x078 /* Tigon 2 only */ +#define TI_SERIAL 0x07C + +/* + * Misc host control bits. + */ +#define TI_MHC_INTSTATE 0x00000001 +#define TI_MHC_CLEARINT 0x00000002 +#define TI_MHC_RESET 0x00000008 +#define TI_MHC_BYTE_SWAP_ENB 0x00000010 +#define TI_MHC_WORD_SWAP_ENB 0x00000020 +#define TI_MHC_MASK_INTS 0x00000040 +#define TI_MHC_CHIP_REV_MASK 0xF0000000 + +#define TI_MHC_BIGENDIAN_INIT \ + (TI_MHC_BYTE_SWAP_ENB|TI_MHC_WORD_SWAP_ENB|TI_MHC_CLEARINT) + +#define TI_MHC_LITTLEENDIAN_INIT \ + (TI_MHC_WORD_SWAP_ENB|TI_MHC_CLEARINT) + +/* + * Tigon chip rev values. Rev 4 is the Tigon 1. Rev 6 is the Tigon 2. + * Rev 5 is also the Tigon 2, but is a broken version which was never + * used in any actual hardware, so we ignore it. + */ +#define TI_REV_TIGON_I 0x40000000 +#define TI_REV_TIGON_II 0x60000000 + +/* + * Firmware revision that we want. + */ +#define TI_FIRMWARE_MAJOR 0xc +#define TI_FIRMWARE_MINOR 0x4 +#define TI_FIRMWARE_FIX 0xb + +/* + * Miscelaneous Local Control register. + */ +#define TI_MLC_EE_WRITE_ENB 0x00000010 +#define TI_MLC_SRAM_BANK_SIZE 0x00000300 /* Tigon 2 only */ +#define TI_MLC_LOCALADDR_21 0x00004000 +#define TI_MLC_LOCALADDR_22 0x00008000 +#define TI_MLC_SBUS_WRITEERR 0x00080000 +#define TI_MLC_EE_CLK 0x00100000 +#define TI_MLC_EE_TXEN 0x00200000 +#define TI_MLC_EE_DOUT 0x00400000 +#define TI_MLC_EE_DIN 0x00800000 + +/* Possible memory sizes. */ +#define TI_MLC_SRAM_BANK_DISA 0x00000000 +#define TI_MLC_SRAM_BANK_1024K 0x00000100 +#define TI_MLC_SRAM_BANK_512K 0x00000200 +#define TI_MLC_SRAM_BANK_256K 0x00000300 + +/* + * Offset of MAC address inside EEPROM. + */ +#define TI_EE_MAC_OFFSET 0x8c + +#define TI_DMA_ASSIST 0x11C +#define TI_CPU_STATE 0x140 +#define TI_CPU_PROGRAM_COUNTER 0x144 +#define TI_SRAM_ADDR 0x154 +#define TI_SRAM_DATA 0x158 +#define TI_GEN_0 0x180 +#define TI_GEN_X 0x1FC +#define TI_MAC_TX_STATE 0x200 +#define TI_MAC_RX_STATE 0x220 +#define TI_CPU_CTL_B 0x240 /* Tigon 2 only */ +#define TI_CPU_PROGRAM_COUNTER_B 0x244 /* Tigon 2 only */ +#define TI_SRAM_ADDR_B 0x254 /* Tigon 2 only */ +#define TI_SRAM_DATA_B 0x258 /* Tigon 2 only */ +#define TI_GEN_B_0 0x280 /* Tigon 2 only */ +#define TI_GEN_B_X 0x2FC /* Tigon 2 only */ + +/* + * Misc config register. + */ +#define TI_MCR_SRAM_SYNCHRONOUS 0x00100000 /* Tigon 2 only */ + +/* + * PCI state register. + */ +#define TI_PCISTATE_FORCE_RESET 0x00000001 +#define TI_PCISTATE_PROVIDE_LEN 0x00000002 +#define TI_PCISTATE_READ_MAXDMA 0x0000001C +#define TI_PCISTATE_WRITE_MAXDMA 0x000000E0 +#define TI_PCISTATE_MINDMA 0x0000FF00 +#define TI_PCISTATE_FIFO_RETRY_ENB 0x00010000 +#define TI_PCISTATE_USE_MEM_RD_MULT 0x00020000 +#define TI_PCISTATE_NO_SWAP_READ_DMA 0x00040000 +#define TI_PCISTATE_NO_SWAP_WRITE_DMA 0x00080000 +#define TI_PCISTATE_66MHZ_BUS 0x00080000 /* Tigon 2 only */ +#define TI_PCISTATE_32BIT_BUS 0x00100000 /* Tigon 2 only */ +#define TI_PCISTATE_ENB_BYTE_ENABLES 0x00800000 /* Tigon 2 only */ +#define TI_PCISTATE_READ_CMD 0x0F000000 +#define TI_PCISTATE_WRITE_CMD 0xF0000000 + +#define TI_PCI_READMAX_4 0x04 +#define TI_PCI_READMAX_16 0x08 +#define TI_PCI_READMAX_32 0x0C +#define TI_PCI_READMAX_64 0x10 +#define TI_PCI_READMAX_128 0x14 +#define TI_PCI_READMAX_256 0x18 +#define TI_PCI_READMAX_1024 0x1C + +#define TI_PCI_WRITEMAX_4 0x20 +#define TI_PCI_WRITEMAX_16 0x40 +#define TI_PCI_WRITEMAX_32 0x60 +#define TI_PCI_WRITEMAX_64 0x80 +#define TI_PCI_WRITEMAX_128 0xA0 +#define TI_PCI_WRITEMAX_256 0xC0 +#define TI_PCI_WRITEMAX_1024 0xE0 + +#define TI_PCI_READ_CMD 0x06000000 +#define TI_PCI_WRITE_CMD 0x70000000 + +/* + * DMA state register. + */ +#define TI_DMASTATE_ENABLE 0x00000001 +#define TI_DMASTATE_PAUSE 0x00000002 + +/* + * CPU state register. + */ +#define TI_CPUSTATE_RESET 0x00000001 +#define TI_CPUSTATE_STEP 0x00000002 +#define TI_CPUSTATE_ROMFAIL 0x00000010 +#define TI_CPUSTATE_HALT 0x00010000 +/* + * MAC TX state register + */ +#define TI_TXSTATE_RESET 0x00000001 +#define TI_TXSTATE_ENB 0x00000002 +#define TI_TXSTATE_STOP 0x00000004 + +/* + * MAC RX state register + */ +#define TI_RXSTATE_RESET 0x00000001 +#define TI_RXSTATE_ENB 0x00000002 +#define TI_RXSTATE_STOP 0x00000004 + +/* + * Tigon 2 mailbox registers. The mailbox area consists of 256 bytes + * split into 64 bit registers. Only the lower 32 bits of each mailbox + * are used. + */ +#define TI_MB_HOSTINTR_HI 0x500 +#define TI_MB_HOSTINTR_LO 0x504 +#define TI_MB_HOSTINTR TI_MB_HOSTINTR_LO +#define TI_MB_CMDPROD_IDX_HI 0x508 +#define TI_MB_CMDPROD_IDX_LO 0x50C +#define TI_MB_CMDPROD_IDX TI_MB_CMDPROD_IDX_LO +#define TI_MB_SENDPROD_IDX_HI 0x510 +#define TI_MB_SENDPROD_IDX_LO 0x514 +#define TI_MB_SENDPROD_IDX TI_MB_SENDPROD_IDX_LO +#define TI_MB_STDRXPROD_IDX_HI 0x518 /* Tigon 2 only */ +#define TI_MB_STDRXPROD_IDX_LO 0x51C /* Tigon 2 only */ +#define TI_MB_STDRXPROD_IDX TI_MB_STDRXPROD_IDX_LO +#define TI_MB_JUMBORXPROD_IDX_HI 0x520 /* Tigon 2 only */ +#define TI_MB_JUMBORXPROD_IDX_LO 0x524 /* Tigon 2 only */ +#define TI_MB_JUMBORXPROD_IDX TI_MB_JUMBORXPROD_IDX_LO +#define TI_MB_MINIRXPROD_IDX_HI 0x528 /* Tigon 2 only */ +#define TI_MB_MINIRXPROD_IDX_LO 0x52C /* Tigon 2 only */ +#define TI_MB_MINIRXPROD_IDX TI_MB_MINIRXPROD_IDX_LO +#define TI_MB_RSVD 0x530 + +/* + * Tigon 2 general communication registers. These are 64 and 32 bit + * registers which are only valid after the firmware has been + * loaded and started. They actually exist in NIC memory but are + * mapped into the host memory via the shared memory region. + * + * The NIC internally maps these registers starting at address 0, + * so to determine the NIC address of any of these registers, we + * subtract 0x600 (the address of the first register). + */ + +#define TI_GCR_BASE 0x600 +#define TI_GCR_MACADDR 0x600 +#define TI_GCR_PAR0 0x600 +#define TI_GCR_PAR1 0x604 +#define TI_GCR_GENINFO_HI 0x608 +#define TI_GCR_GENINFO_LO 0x60C +#define TI_GCR_MCASTADDR 0x610 /* obsolete */ +#define TI_GCR_MAR0 0x610 /* obsolete */ +#define TI_GCR_MAR1 0x614 /* obsolete */ +#define TI_GCR_OPMODE 0x618 +#define TI_GCR_DMA_READCFG 0x61C +#define TI_GCR_DMA_WRITECFG 0x620 +#define TI_GCR_TX_BUFFER_RATIO 0x624 +#define TI_GCR_EVENTCONS_IDX 0x628 +#define TI_GCR_CMDCONS_IDX 0x62C +#define TI_GCR_TUNEPARMS 0x630 +#define TI_GCR_RX_COAL_TICKS 0x630 +#define TI_GCR_TX_COAL_TICKS 0x634 +#define TI_GCR_STAT_TICKS 0x638 +#define TI_GCR_TX_MAX_COAL_BD 0x63C +#define TI_GCR_RX_MAX_COAL_BD 0x640 +#define TI_GCR_NIC_TRACING 0x644 +#define TI_GCR_GLINK 0x648 +#define TI_GCR_LINK 0x64C +#define TI_GCR_NICTRACE_PTR 0x650 +#define TI_GCR_NICTRACE_START 0x654 +#define TI_GCR_NICTRACE_LEN 0x658 +#define TI_GCR_IFINDEX 0x65C +#define TI_GCR_IFMTU 0x660 +#define TI_GCR_MASK_INTRS 0x664 +#define TI_GCR_GLINK_STAT 0x668 +#define TI_GCR_LINK_STAT 0x66C +#define TI_GCR_RXRETURNCONS_IDX 0x680 +#define TI_GCR_CMDRING 0x700 + +#define TI_GCR_NIC_ADDR(x) (x - TI_GCR_BASE); + +/* + * Local memory window. The local memory window is a 2K shared + * memory region which can be used to access the NIC's internal + * SRAM. The window can be mapped to a given 2K region using + * the TI_WINDOW_BASE register. + */ +#define TI_WINDOW 0x800 +#define TI_WINLEN 0x800 + +#define TI_TICKS_PER_SEC 1000000 + +/* + * Operation mode register. + */ +#define TI_OPMODE_BYTESWAP_BD 0x00000002 +#define TI_OPMODE_WORDSWAP_BD 0x00000004 +#define TI_OPMODE_WARN_ENB 0x00000008 /* not yet implimented */ +#define TI_OPMODE_BYTESWAP_DATA 0x00000010 +#define TI_OPMODE_1_DMA_ACTIVE 0x00000040 +#define TI_OPMODE_SBUS 0x00000100 +#define TI_OPMODE_DONT_FRAG_JUMBO 0x00000200 +#define TI_OPMODE_INCLUDE_CRC 0x00000400 +#define TI_OPMODE_RX_BADFRAMES 0x00000800 +#define TI_OPMODE_NO_EVENT_INTRS 0x00001000 +#define TI_OPMODE_NO_TX_INTRS 0x00002000 +#define TI_OPMODE_NO_RX_INTRS 0x00004000 +#define TI_OPMODE_FATAL_ENB 0x40000000 /* not yet implimented */ +#define TI_OPMODE_JUMBO_HDRSPLIT 0x00008000 + +/* + * DMA configuration thresholds. + */ +#define TI_DMA_STATE_THRESH_16W 0x00000100 +#define TI_DMA_STATE_THRESH_8W 0x00000080 +#define TI_DMA_STATE_THRESH_4W 0x00000040 +#define TI_DMA_STATE_THRESH_2W 0x00000020 +#define TI_DMA_STATE_THRESH_1W 0x00000010 + +#define TI_DMA_STATE_FORCE_32_BIT 0x00000008 + +/* + * Gigabit link status bits. + */ +#define TI_GLNK_SENSE_NO_BEG 0x00002000 +#define TI_GLNK_LOOPBACK 0x00004000 +#define TI_GLNK_PREF 0x00008000 +#define TI_GLNK_1000MB 0x00040000 +#define TI_GLNK_FULL_DUPLEX 0x00080000 +#define TI_GLNK_TX_FLOWCTL_Y 0x00200000 /* Tigon 2 only */ +#define TI_GLNK_RX_FLOWCTL_Y 0x00800000 +#define TI_GLNK_AUTONEGENB 0x20000000 +#define TI_GLNK_ENB 0x40000000 + +/* + * Link status bits. + */ +#define TI_LNK_LOOPBACK 0x00004000 +#define TI_LNK_PREF 0x00008000 +#define TI_LNK_10MB 0x00010000 +#define TI_LNK_100MB 0x00020000 +#define TI_LNK_1000MB 0x00040000 +#define TI_LNK_FULL_DUPLEX 0x00080000 +#define TI_LNK_HALF_DUPLEX 0x00100000 +#define TI_LNK_TX_FLOWCTL_Y 0x00200000 /* Tigon 2 only */ +#define TI_LNK_RX_FLOWCTL_Y 0x00800000 +#define TI_LNK_AUTONEGENB 0x20000000 +#define TI_LNK_ENB 0x40000000 + +/* + * Ring size constants. + */ +#define TI_EVENT_RING_CNT 256 +#define TI_CMD_RING_CNT 64 +#define TI_STD_RX_RING_CNT 512 +#define TI_JUMBO_RX_RING_CNT 256 +#define TI_MINI_RX_RING_CNT 1024 +#define TI_RETURN_RING_CNT 2048 + +/* + * Possible TX ring sizes. + */ +#define TI_TX_RING_CNT_128 128 +#define TI_TX_RING_BASE_128 0x3800 + +#define TI_TX_RING_CNT_256 256 +#define TI_TX_RING_BASE_256 0x3000 + +#define TI_TX_RING_CNT_512 512 +#define TI_TX_RING_BASE_512 0x2000 + +#define TI_TX_RING_CNT TI_TX_RING_CNT_512 +#define TI_TX_RING_BASE TI_TX_RING_BASE_512 + +/* + * The Tigon can have up to 8MB of external SRAM, however the Tigon 1 + * is limited to 2MB total, and in general I think most adapters have + * around 1MB. We use this value for zeroing the NIC's SRAM, so to + * be safe we use the largest possible value (zeroing memory that + * isn't there doesn't hurt anything). + */ +#define TI_MEM_MAX 0x7FFFFF + +/* + * Maximum register address on the Tigon. + */ +#define TI_REG_MAX 0x3fff + +/* + * These values were taken from Alteon's tg.h. + */ +#define TI_BEG_SRAM 0x0 /* host thinks it's here */ +#define TI_BEG_SCRATCH 0xc00000 /* beg of scratch pad area */ +#define TI_END_SRAM_II 0x800000 /* end of SRAM, for 2 MB stuffed */ +#define TI_END_SCRATCH_II 0xc04000 /* end of scratch pad CPU A (16KB) */ +#define TI_END_SCRATCH_B 0xc02000 /* end of scratch pad CPU B (8KB) */ +#define TI_BEG_SCRATCH_B_DEBUG 0xd00000 /* beg of scratch pad for ioctl */ +#define TI_END_SCRATCH_B_DEBUG 0xd02000 /* end of scratch pad for ioctl */ +#define TI_SCRATCH_DEBUG_OFF 0x100000 /* offset for ioctl usage */ +#define TI_END_SRAM_I 0x200000 /* end of SRAM, for 2 MB stuffed */ +#define TI_END_SCRATCH_I 0xc00800 /* end of scratch pad area (2KB) */ +#define TI_BEG_PROM 0x40000000 /* beg of PROM, special access */ +#define TI_BEG_FLASH 0x80000000 /* beg of EEPROM, special access */ +#define TI_END_FLASH 0x80100000 /* end of EEPROM for 1 MB stuff */ +#define TI_BEG_SER_EEPROM 0xa0000000 /* beg of Serial EEPROM (fake out) */ +#define TI_END_SER_EEPROM 0xa0002000 /* end of Serial EEPROM (fake out) */ +#define TI_BEG_REGS 0xc0000000 /* beg of register area */ +#define TI_END_REGS 0xc0000400 /* end of register area */ +#define TI_END_WRITE_REGS 0xc0000180 /* can't write GPRs currently */ +#define TI_BEG_REGS2 0xc0000200 /* beg of second writeable reg area */ +/* the EEPROM is byte addressable in a pretty odd way */ +#define EEPROM_BYTE_LOC 0xff000000 + +/* + * From Alteon's tg.h. + */ +#define TI_PROCESSOR_A 0 +#define TI_PROCESSOR_B 1 +#define TI_CPU_A TG_PROCESSOR_A +#define TI_CPU_B TG_PROCESSOR_B + +/* + * Following macro can be used to access to any of the CPU registers + * It will adjust the address appropriately. + * Parameters: + * reg - The register to access, e.g TI_CPU_CONTROL + * cpu - cpu, i.e PROCESSOR_A or PROCESSOR_B (or TI_CPU_A or TI_CPU_B) + */ +#define CPU_REG(reg, cpu) ((reg) + (cpu) * 0x100) + +/* + * Even on the alpha, pci addresses are 32-bit quantities + */ + +#ifdef __64_bit_pci_addressing__ +typedef struct { + u_int64_t ti_addr; +} ti_hostaddr; +#define TI_HOSTADDR(x) x.ti_addr +#else +typedef struct { + u_int32_t ti_addr_hi; + u_int32_t ti_addr_lo; +} ti_hostaddr; +#define TI_HOSTADDR(x) x.ti_addr_lo +#endif + +/* + * Ring control block structure. The rules for the max_len field + * are as follows: + * + * For the send ring, max_len indicates the number of entries in the + * ring (128, 256 or 512). + * + * For the standard receive ring, max_len indicates the threshold + * used to decide when a frame should be put in the jumbo receive ring + * instead of the standard one. + * + * For the mini ring, max_len indicates the size of the buffers in the + * ring. This is the value used to decide when a frame is small enough + * to be placed in the mini ring. + * + * For the return receive ring, max_len indicates the number of entries + * in the ring. It can be one of 2048, 1024 or 0 (which is the same as + * 2048 for backwards compatibility). The value 1024 can only be used + * if the mini ring is disabled. + */ +struct ti_rcb { + ti_hostaddr ti_hostaddr; +#if BYTE_ORDER == BIG_ENDIAN + u_int16_t ti_max_len; + u_int16_t ti_flags; +#else + u_int16_t ti_flags; + u_int16_t ti_max_len; +#endif + u_int32_t ti_unused; +}; + +#define TI_RCB_FLAG_TCP_UDP_CKSUM 0x00000001 +#define TI_RCB_FLAG_IP_CKSUM 0x00000002 +#define TI_RCB_FLAG_NO_PHDR_CKSUM 0x00000008 +#define TI_RCB_FLAG_VLAN_ASSIST 0x00000010 +#define TI_RCB_FLAG_COAL_UPD_ONLY 0x00000020 +#define TI_RCB_FLAG_HOST_RING 0x00000040 +#define TI_RCB_FLAG_IEEE_SNAP_CKSUM 0x00000080 +#define TI_RCB_FLAG_USE_EXT_RX_BD 0x00000100 +#define TI_RCB_FLAG_RING_DISABLED 0x00000200 + +struct ti_producer { + u_int32_t ti_idx; + u_int32_t ti_unused; +}; + +/* + * Tigon general information block. This resides in host memory + * and contains the status counters, ring control blocks and + * producer pointers. + */ + +struct ti_gib { + struct ti_stats ti_stats; + struct ti_rcb ti_ev_rcb; + struct ti_rcb ti_cmd_rcb; + struct ti_rcb ti_tx_rcb; + struct ti_rcb ti_std_rx_rcb; + struct ti_rcb ti_jumbo_rx_rcb; + struct ti_rcb ti_mini_rx_rcb; + struct ti_rcb ti_return_rcb; + ti_hostaddr ti_ev_prodidx_ptr; + ti_hostaddr ti_return_prodidx_ptr; + ti_hostaddr ti_tx_considx_ptr; + ti_hostaddr ti_refresh_stats_ptr; +}; + +/* + * Buffer descriptor structures. There are basically three types + * of structures: normal receive descriptors, extended receive + * descriptors and transmit descriptors. The extended receive + * descriptors are optionally used only for the jumbo receive ring. + */ + +struct ti_rx_desc { + ti_hostaddr ti_addr; +#if BYTE_ORDER == BIG_ENDIAN + u_int16_t ti_idx; + u_int16_t ti_len; +#else + u_int16_t ti_len; + u_int16_t ti_idx; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u_int16_t ti_type; + u_int16_t ti_flags; +#else + u_int16_t ti_flags; + u_int16_t ti_type; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u_int16_t ti_ip_cksum; + u_int16_t ti_tcp_udp_cksum; +#else + u_int16_t ti_tcp_udp_cksum; + u_int16_t ti_ip_cksum; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u_int16_t ti_error_flags; + u_int16_t ti_vlan_tag; +#else + u_int16_t ti_vlan_tag; + u_int16_t ti_error_flags; +#endif + u_int32_t ti_rsvd; + u_int32_t ti_opaque; +}; + +struct ti_rx_desc_ext { + ti_hostaddr ti_addr1; + ti_hostaddr ti_addr2; + ti_hostaddr ti_addr3; +#if BYTE_ORDER == BIG_ENDIAN + u_int16_t ti_len1; + u_int16_t ti_len2; +#else + u_int16_t ti_len2; + u_int16_t ti_len1; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u_int16_t ti_len3; + u_int16_t ti_rsvd0; +#else + u_int16_t ti_rsvd0; + u_int16_t ti_len3; +#endif + ti_hostaddr ti_addr0; +#if BYTE_ORDER == BIG_ENDIAN + u_int16_t ti_idx; + u_int16_t ti_len0; +#else + u_int16_t ti_len0; + u_int16_t ti_idx; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u_int16_t ti_type; + u_int16_t ti_flags; +#else + u_int16_t ti_flags; + u_int16_t ti_type; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u_int16_t ti_ip_cksum; + u_int16_t ti_tcp_udp_cksum; +#else + u_int16_t ti_tcp_udp_cksum; + u_int16_t ti_ip_cksum; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u_int16_t ti_error_flags; + u_int16_t ti_vlan_tag; +#else + u_int16_t ti_vlan_tag; + u_int16_t ti_error_flags; +#endif + u_int32_t ti_rsvd1; + u_int32_t ti_opaque; +}; + +/* + * Transmit descriptors are, mercifully, very small. + */ +struct ti_tx_desc { + ti_hostaddr ti_addr; +#if BYTE_ORDER == BIG_ENDIAN + u_int16_t ti_len; + u_int16_t ti_flags; +#else + u_int16_t ti_flags; + u_int16_t ti_len; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u_int16_t ti_rsvd; + u_int16_t ti_vlan_tag; +#else + u_int16_t ti_vlan_tag; + u_int16_t ti_rsvd; +#endif +}; + +/* + * NOTE! On the Alpha, we have an alignment constraint. + * The first thing in the packet is a 14-byte Ethernet header. + * This means that the packet is misaligned. To compensate, + * we actually offset the data 2 bytes into the cluster. This + * alignes the packet after the Ethernet header at a 32-bit + * boundary. + */ + +#define ETHER_ALIGN 2 + +#define TI_FRAMELEN 1518 +#define TI_JUMBO_FRAMELEN 9018 +#define TI_JUMBO_MTU (TI_JUMBO_FRAMELEN-ETHER_HDR_LEN-ETHER_CRC_LEN) +#define TI_PAGE_SIZE PAGE_SIZE +#define TI_MIN_FRAMELEN 60 + +/* + * Buffer descriptor error flags. + */ +#define TI_BDERR_CRC 0x0001 +#define TI_BDERR_COLLDETECT 0x0002 +#define TI_BDERR_LINKLOST 0x0004 +#define TI_BDERR_DECODE 0x0008 +#define TI_BDERR_ODD_NIBBLES 0x0010 +#define TI_BDERR_MAC_ABRT 0x0020 +#define TI_BDERR_RUNT 0x0040 +#define TI_BDERR_TRUNC 0x0080 +#define TI_BDERR_GIANT 0x0100 + +/* + * Buffer descriptor flags. + */ +#define TI_BDFLAG_TCP_UDP_CKSUM 0x0001 +#define TI_BDFLAG_IP_CKSUM 0x0002 +#define TI_BDFLAG_END 0x0004 +#define TI_BDFLAG_MORE 0x0008 +#define TI_BDFLAG_JUMBO_RING 0x0010 +#define TI_BDFLAG_UCAST_PKT 0x0020 +#define TI_BDFLAG_MCAST_PKT 0x0040 +#define TI_BDFLAG_BCAST_PKT 0x0060 +#define TI_BDFLAG_IP_FRAG 0x0080 +#define TI_BDFLAG_IP_FRAG_END 0x0100 +#define TI_BDFLAG_VLAN_TAG 0x0200 +#define TI_BDFLAG_ERROR 0x0400 +#define TI_BDFLAG_COAL_NOW 0x0800 +#define TI_BDFLAG_MINI_RING 0x1000 + +/* + * Descriptor type flags. I think these only have meaning for + * the Tigon 1. I had to extract them from the sample driver source + * since they aren't in the manual. + */ +#define TI_BDTYPE_TYPE_NULL 0x0000 +#define TI_BDTYPE_SEND_BD 0x0001 +#define TI_BDTYPE_RECV_BD 0x0002 +#define TI_BDTYPE_RECV_JUMBO_BD 0x0003 +#define TI_BDTYPE_RECV_BD_LAST 0x0004 +#define TI_BDTYPE_SEND_DATA 0x0005 +#define TI_BDTYPE_SEND_DATA_LAST 0x0006 +#define TI_BDTYPE_RECV_DATA 0x0007 +#define TI_BDTYPE_RECV_DATA_LAST 0x000b +#define TI_BDTYPE_EVENT_RUPT 0x000c +#define TI_BDTYPE_EVENT_NO_RUPT 0x000d +#define TI_BDTYPE_ODD_START 0x000e +#define TI_BDTYPE_UPDATE_STATS 0x000f +#define TI_BDTYPE_SEND_DUMMY_DMA 0x0010 +#define TI_BDTYPE_EVENT_PROD 0x0011 +#define TI_BDTYPE_TX_CONS 0x0012 +#define TI_BDTYPE_RX_PROD 0x0013 +#define TI_BDTYPE_REFRESH_STATS 0x0014 +#define TI_BDTYPE_SEND_DATA_LAST_VLAN 0x0015 +#define TI_BDTYPE_SEND_DATA_COAL 0x0016 +#define TI_BDTYPE_SEND_DATA_LAST_COAL 0x0017 +#define TI_BDTYPE_SEND_DATA_LAST_VLAN_COAL 0x0018 +#define TI_BDTYPE_TX_CONS_NO_INTR 0x0019 + +/* + * Tigon command structure. + */ +struct ti_cmd_desc { +#if BYTE_ORDER == BIG_ENDIAN + u_int32_t ti_cmd:8; + u_int32_t ti_code:12; + u_int32_t ti_idx:12; +#else + u_int32_t ti_idx:12; + u_int32_t ti_code:12; + u_int32_t ti_cmd:8; +#endif +}; + +#define TI_CMD_HOST_STATE 0x01 +#define TI_CMD_CODE_STACK_UP 0x01 +#define TI_CMD_CODE_STACK_DOWN 0x02 + +/* + * This command enables software address filtering. It's a workaround + * for a bug in the Tigon 1 and not implemented for the Tigon 2. + */ +#define TI_CMD_FDR_FILTERING 0x02 +#define TI_CMD_CODE_FILT_ENB 0x01 +#define TI_CMD_CODE_FILT_DIS 0x02 + +#define TI_CMD_SET_RX_PROD_IDX 0x03 /* obsolete */ +#define TI_CMD_UPDATE_GENCOM 0x04 +#define TI_CMD_RESET_JUMBO_RING 0x05 +#define TI_CMD_SET_PARTIAL_RX_CNT 0x06 +#define TI_CMD_ADD_MCAST_ADDR 0x08 /* obsolete */ +#define TI_CMD_DEL_MCAST_ADDR 0x09 /* obsolete */ + +#define TI_CMD_SET_PROMISC_MODE 0x0A +#define TI_CMD_CODE_PROMISC_ENB 0x01 +#define TI_CMD_CODE_PROMISC_DIS 0x02 + +#define TI_CMD_LINK_NEGOTIATION 0x0B +#define TI_CMD_CODE_NEGOTIATE_BOTH 0x00 +#define TI_CMD_CODE_NEGOTIATE_GIGABIT 0x01 +#define TI_CMD_CODE_NEGOTIATE_10_100 0x02 + +#define TI_CMD_SET_MAC_ADDR 0x0C +#define TI_CMD_CLR_PROFILE 0x0D + +#define TI_CMD_SET_ALLMULTI 0x0E +#define TI_CMD_CODE_ALLMULTI_ENB 0x01 +#define TI_CMD_CODE_ALLMULTI_DIS 0x02 + +#define TI_CMD_CLR_STATS 0x0F +#define TI_CMD_SET_RX_JUMBO_PROD_IDX 0x10 /* obsolete */ +#define TI_CMD_RFRSH_STATS 0x11 + +#define TI_CMD_EXT_ADD_MCAST 0x12 +#define TI_CMD_EXT_DEL_MCAST 0x13 + +/* + * Utility macros to make issuing commands a little simpler. Assumes + * that 'sc' and 'cmd' are in local scope. + */ +#define TI_DO_CMD(x, y, z) \ + cmd.ti_cmd = x; \ + cmd.ti_code = y; \ + cmd.ti_idx = z; \ + ti_cmd(sc, &cmd); + +#define TI_DO_CMD_EXT(x, y, z, v, w) \ + cmd.ti_cmd = x; \ + cmd.ti_code = y; \ + cmd.ti_idx = z; \ + ti_cmd_ext(sc, &cmd, v, w); + +/* + * Other utility macros. + */ +#define TI_INC(x, y) (x) = (x + 1) % y + +#define TI_UPDATE_JUMBOPROD(x, y) \ + if (x->ti_hwrev == TI_HWREV_TIGON) { \ + TI_DO_CMD(TI_CMD_SET_RX_JUMBO_PROD_IDX, 0, y); \ + } else { \ + CSR_WRITE_4(x, TI_MB_JUMBORXPROD_IDX, y); \ + } + +#define TI_UPDATE_MINIPROD(x, y) \ + CSR_WRITE_4(x, TI_MB_MINIRXPROD_IDX, y); + +#define TI_UPDATE_STDPROD(x, y) \ + if (x->ti_hwrev == TI_HWREV_TIGON) { \ + TI_DO_CMD(TI_CMD_SET_RX_PROD_IDX, 0, y); \ + } else { \ + CSR_WRITE_4(x, TI_MB_STDRXPROD_IDX, y); \ + } + + +/* + * Tigon event structure. + */ +struct ti_event_desc { +#if BYTE_ORDER == BIG_ENDIAN + u_int32_t ti_event:8; + u_int32_t ti_code:12; + u_int32_t ti_idx:12; +#else + u_int32_t ti_idx:12; + u_int32_t ti_code:12; + u_int32_t ti_event:8; +#endif + u_int32_t ti_rsvd; +}; + +/* + * Tigon events. + */ +#define TI_EV_FIRMWARE_UP 0x01 +#define TI_EV_STATS_UPDATED 0x04 + +#define TI_EV_LINKSTAT_CHANGED 0x06 +#define TI_EV_CODE_GIG_LINK_UP 0x01 +#define TI_EV_CODE_LINK_DOWN 0x02 +#define TI_EV_CODE_LINK_UP 0x03 + +#define TI_EV_ERROR 0x07 +#define TI_EV_CODE_ERR_INVAL_CMD 0x01 +#define TI_EV_CODE_ERR_UNIMP_CMD 0x02 +#define TI_EV_CODE_ERR_BADCFG 0x03 + +#define TI_EV_MCAST_UPDATED 0x08 +#define TI_EV_CODE_MCAST_ADD 0x01 +#define TI_EV_CODE_MCAST_DEL 0x02 + +#define TI_EV_RESET_JUMBO_RING 0x09 +/* + * Register access macros. The Tigon always uses memory mapped register + * accesses and all registers must be accessed with 32 bit operations. + */ + +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->ti_btag, sc->ti_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->ti_btag, sc->ti_bhandle, reg) + +#define TI_SETBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, (CSR_READ_4(sc, reg) | x)) +#define TI_CLRBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, (CSR_READ_4(sc, reg) & ~x)) + +/* + * Memory management stuff. Note: the SSLOTS, MSLOTS and JSLOTS + * values are tuneable. They control the actual amount of buffers + * allocated for the standard, mini and jumbo receive rings. + */ + +#define TI_SSLOTS 256 +#define TI_MSLOTS 256 +#define TI_JSLOTS 384 + +#define TI_JRAWLEN (TI_JUMBO_FRAMELEN + ETHER_ALIGN) +#define TI_JLEN (TI_JRAWLEN + (sizeof(u_int64_t) - \ + (TI_JRAWLEN % sizeof(u_int64_t)))) +#define TI_JPAGESZ PAGE_SIZE +#define TI_RESID (TI_JPAGESZ - (TI_JLEN * TI_JSLOTS) % TI_JPAGESZ) +#define TI_JMEM ((TI_JLEN * TI_JSLOTS) + TI_RESID) + +/* + * Ring structures. Most of these reside in host memory and we tell + * the NIC where they are via the ring control blocks. The exceptions + * are the tx and command rings, which live in NIC memory and which + * we access via the shared memory window. + */ +struct ti_ring_data { + struct ti_rx_desc ti_rx_std_ring[TI_STD_RX_RING_CNT]; +#ifdef PRIVATE_JUMBOS + struct ti_rx_desc ti_rx_jumbo_ring[TI_JUMBO_RX_RING_CNT]; +#else + struct ti_rx_desc_ext ti_rx_jumbo_ring[TI_JUMBO_RX_RING_CNT]; +#endif + struct ti_rx_desc ti_rx_mini_ring[TI_MINI_RX_RING_CNT]; + struct ti_rx_desc ti_rx_return_ring[TI_RETURN_RING_CNT]; + struct ti_event_desc ti_event_ring[TI_EVENT_RING_CNT]; + struct ti_tx_desc ti_tx_ring[TI_TX_RING_CNT]; + /* + * Make sure producer structures are aligned on 32-byte cache + * line boundaries. + */ + struct ti_producer ti_ev_prodidx_r; + u_int32_t ti_pad0[6]; + struct ti_producer ti_return_prodidx_r; + u_int32_t ti_pad1[6]; + struct ti_producer ti_tx_considx_r; + u_int32_t ti_pad2[6]; + struct ti_tx_desc *ti_tx_ring_nic;/* pointer to shared mem */ + struct ti_cmd_desc *ti_cmd_ring; /* pointer to shared mem */ + struct ti_gib ti_info; +}; + +/* + * Mbuf pointers. We need these to keep track of the virtual addresses + * of our mbuf chains since we can only convert from physical to virtual, + * not the other way around. + */ +struct ti_chain_data { + struct mbuf *ti_tx_chain[TI_TX_RING_CNT]; + struct mbuf *ti_rx_std_chain[TI_STD_RX_RING_CNT]; + struct mbuf *ti_rx_jumbo_chain[TI_JUMBO_RX_RING_CNT]; + struct mbuf *ti_rx_mini_chain[TI_MINI_RX_RING_CNT]; + /* Stick the jumbo mem management stuff here too. */ + caddr_t ti_jslots[TI_JSLOTS]; + void *ti_jumbo_buf; +}; + +struct ti_type { + u_int16_t ti_vid; + u_int16_t ti_did; + char *ti_name; +}; + +#define TI_HWREV_TIGON 0x01 +#define TI_HWREV_TIGON_II 0x02 +#define TI_TIMEOUT 1000 +#define TI_TXCONS_UNSET 0xFFFF /* impossible value */ + +struct ti_mc_entry { + struct ether_addr mc_addr; + SLIST_ENTRY(ti_mc_entry) mc_entries; +}; + +struct ti_jpool_entry { + int slot; + SLIST_ENTRY(ti_jpool_entry) jpool_entries; +}; + +typedef enum { + TI_FLAG_NONE = 0x00, + TI_FLAG_DEBUGING = 0x01, + TI_FLAG_WAIT_FOR_LINK = 0x02 +} ti_flag_vals; + +struct ti_softc { + STAILQ_ENTRY(ti_softc) ti_links; + struct arpcom arpcom; /* interface info */ + bus_space_handle_t ti_bhandle; + vm_offset_t ti_vhandle; + bus_space_tag_t ti_btag; + void *ti_intrhand; + struct resource *ti_irq; + struct resource *ti_res; + struct ifmedia ifmedia; /* media info */ + u_int8_t ti_unit; /* interface number */ + u_int8_t ti_hwrev; /* Tigon rev (1 or 2) */ + u_int8_t ti_copper; /* 1000baseTX card */ + u_int8_t ti_linkstat; /* Link state */ + int ti_hdrsplit; /* enable header splitting */ + struct ti_ring_data *ti_rdata; /* rings */ + struct ti_chain_data ti_cdata; /* mbufs */ +#define ti_ev_prodidx ti_rdata->ti_ev_prodidx_r +#define ti_return_prodidx ti_rdata->ti_return_prodidx_r +#define ti_tx_considx ti_rdata->ti_tx_considx_r + u_int16_t ti_tx_saved_considx; + u_int16_t ti_rx_saved_considx; + u_int16_t ti_ev_saved_considx; + u_int16_t ti_cmd_saved_prodidx; + u_int16_t ti_std; /* current std ring head */ + u_int16_t ti_mini; /* current mini ring head */ + u_int16_t ti_jumbo; /* current jumo ring head */ + SLIST_HEAD(__ti_mchead, ti_mc_entry) ti_mc_listhead; + SLIST_HEAD(__ti_jfreehead, ti_jpool_entry) ti_jfree_listhead; + SLIST_HEAD(__ti_jinusehead, ti_jpool_entry) ti_jinuse_listhead; + u_int32_t ti_stat_ticks; + u_int32_t ti_rx_coal_ticks; + u_int32_t ti_tx_coal_ticks; + u_int32_t ti_rx_max_coal_bds; + u_int32_t ti_tx_max_coal_bds; + u_int32_t ti_tx_buf_ratio; + int ti_if_flags; + int ti_txcnt; + struct mtx ti_mtx; + ti_flag_vals ti_flags; + dev_t dev; +}; + +#define TI_LOCK(_sc) mtx_lock(&(_sc)->ti_mtx) +#define TI_UNLOCK(_sc) mtx_unlock(&(_sc)->ti_mtx) + +/* + * Microchip Technology 24Cxx EEPROM control bytes + */ +#define EEPROM_CTL_READ 0xA1 /* 0101 0001 */ +#define EEPROM_CTL_WRITE 0xA0 /* 0101 0000 */ + +/* + * Note that EEPROM_START leaves transmission enabled. + */ +#define EEPROM_START \ + TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_CLK); /* Pull clock pin high */\ + TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_DOUT); /* Set DATA bit to 1 */ \ + TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_TXEN); /* Enable xmit to write bit */\ + TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_DOUT); /* Pull DATA bit to 0 again */\ + TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_CLK); /* Pull clock low again */ + +/* + * EEPROM_STOP ends access to the EEPROM and clears the ETXEN bit so + * that no further data can be written to the EEPROM I/O pin. + */ +#define EEPROM_STOP \ + TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_TXEN); /* Disable xmit */ \ + TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_DOUT); /* Pull DATA to 0 */ \ + TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_CLK); /* Pull clock high */ \ + TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_TXEN); /* Enable xmit */ \ + TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_DOUT); /* Toggle DATA to 1 */ \ + TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_TXEN); /* Disable xmit. */ \ + TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_CLK); /* Pull clock low again */ + + +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) +#endif diff --git a/sys/pci/if_tl.c b/sys/pci/if_tl.c new file mode 100644 index 0000000..1c52f9a --- /dev/null +++ b/sys/pci/if_tl.c @@ -0,0 +1,2335 @@ +/* + * Copyright (c) 1997, 1998 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * Texas Instruments ThunderLAN driver for FreeBSD 2.2.6 and 3.x. + * Supports many Compaq PCI NICs based on the ThunderLAN ethernet controller, + * the National Semiconductor DP83840A physical interface and the + * Microchip Technology 24Cxx series serial EEPROM. + * + * Written using the following four documents: + * + * Texas Instruments ThunderLAN Programmer's Guide (www.ti.com) + * National Semiconductor DP83840A data sheet (www.national.com) + * Microchip Technology 24C02C data sheet (www.microchip.com) + * Micro Linear ML6692 100BaseTX only PHY data sheet (www.microlinear.com) + * + * Written by Bill Paul <wpaul@ctr.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * Some notes about the ThunderLAN: + * + * The ThunderLAN controller is a single chip containing PCI controller + * logic, approximately 3K of on-board SRAM, a LAN controller, and media + * independent interface (MII) bus. The MII allows the ThunderLAN chip to + * control up to 32 different physical interfaces (PHYs). The ThunderLAN + * also has a built-in 10baseT PHY, allowing a single ThunderLAN controller + * to act as a complete ethernet interface. + * + * Other PHYs may be attached to the ThunderLAN; the Compaq 10/100 cards + * use a National Semiconductor DP83840A PHY that supports 10 or 100Mb/sec + * in full or half duplex. Some of the Compaq Deskpro machines use a + * Level 1 LXT970 PHY with the same capabilities. Certain Olicom adapters + * use a Micro Linear ML6692 100BaseTX only PHY, which can be used in + * concert with the ThunderLAN's internal PHY to provide full 10/100 + * support. This is cheaper than using a standalone external PHY for both + * 10/100 modes and letting the ThunderLAN's internal PHY go to waste. + * A serial EEPROM is also attached to the ThunderLAN chip to provide + * power-up default register settings and for storing the adapter's + * station address. Although not supported by this driver, the ThunderLAN + * chip can also be connected to token ring PHYs. + * + * The ThunderLAN has a set of registers which can be used to issue + * commands, acknowledge interrupts, and to manipulate other internal + * registers on its DIO bus. The primary registers can be accessed + * using either programmed I/O (inb/outb) or via PCI memory mapping, + * depending on how the card is configured during the PCI probing + * phase. It is even possible to have both PIO and memory mapped + * access turned on at the same time. + * + * Frame reception and transmission with the ThunderLAN chip is done + * using frame 'lists.' A list structure looks more or less like this: + * + * struct tl_frag { + * u_int32_t fragment_address; + * u_int32_t fragment_size; + * }; + * struct tl_list { + * u_int32_t forward_pointer; + * u_int16_t cstat; + * u_int16_t frame_size; + * struct tl_frag fragments[10]; + * }; + * + * The forward pointer in the list header can be either a 0 or the address + * of another list, which allows several lists to be linked together. Each + * list contains up to 10 fragment descriptors. This means the chip allows + * ethernet frames to be broken up into up to 10 chunks for transfer to + * and from the SRAM. Note that the forward pointer and fragment buffer + * addresses are physical memory addresses, not virtual. Note also that + * a single ethernet frame can not span lists: if the host wants to + * transmit a frame and the frame data is split up over more than 10 + * buffers, the frame has to collapsed before it can be transmitted. + * + * To receive frames, the driver sets up a number of lists and populates + * the fragment descriptors, then it sends an RX GO command to the chip. + * When a frame is received, the chip will DMA it into the memory regions + * specified by the fragment descriptors and then trigger an RX 'end of + * frame interrupt' when done. The driver may choose to use only one + * fragment per list; this may result is slighltly less efficient use + * of memory in exchange for improving performance. + * + * To transmit frames, the driver again sets up lists and fragment + * descriptors, only this time the buffers contain frame data that + * is to be DMA'ed into the chip instead of out of it. Once the chip + * has transfered the data into its on-board SRAM, it will trigger a + * TX 'end of frame' interrupt. It will also generate an 'end of channel' + * interrupt when it reaches the end of the list. + */ + +/* + * Some notes about this driver: + * + * The ThunderLAN chip provides a couple of different ways to organize + * reception, transmission and interrupt handling. The simplest approach + * is to use one list each for transmission and reception. In this mode, + * the ThunderLAN will generate two interrupts for every received frame + * (one RX EOF and one RX EOC) and two for each transmitted frame (one + * TX EOF and one TX EOC). This may make the driver simpler but it hurts + * performance to have to handle so many interrupts. + * + * Initially I wanted to create a circular list of receive buffers so + * that the ThunderLAN chip would think there was an infinitely long + * receive channel and never deliver an RXEOC interrupt. However this + * doesn't work correctly under heavy load: while the manual says the + * chip will trigger an RXEOF interrupt each time a frame is copied into + * memory, you can't count on the chip waiting around for you to acknowledge + * the interrupt before it starts trying to DMA the next frame. The result + * is that the chip might traverse the entire circular list and then wrap + * around before you have a chance to do anything about it. Consequently, + * the receive list is terminated (with a 0 in the forward pointer in the + * last element). Each time an RXEOF interrupt arrives, the used list + * is shifted to the end of the list. This gives the appearance of an + * infinitely large RX chain so long as the driver doesn't fall behind + * the chip and allow all of the lists to be filled up. + * + * If all the lists are filled, the adapter will deliver an RX 'end of + * channel' interrupt when it hits the 0 forward pointer at the end of + * the chain. The RXEOC handler then cleans out the RX chain and resets + * the list head pointer in the ch_parm register and restarts the receiver. + * + * For frame transmission, it is possible to program the ThunderLAN's + * transmit interrupt threshold so that the chip can acknowledge multiple + * lists with only a single TX EOF interrupt. This allows the driver to + * queue several frames in one shot, and only have to handle a total + * two interrupts (one TX EOF and one TX EOC) no matter how many frames + * are transmitted. Frame transmission is done directly out of the + * mbufs passed to the tl_start() routine via the interface send queue. + * The driver simply sets up the fragment descriptors in the transmit + * lists to point to the mbuf data regions and sends a TX GO command. + * + * Note that since the RX and TX lists themselves are always used + * only by the driver, the are malloc()ed once at driver initialization + * time and never free()ed. + * + * Also, in order to remain as platform independent as possible, this + * driver uses memory mapped register access to manipulate the card + * as opposed to programmed I/O. This avoids the use of the inb/outb + * (and related) instructions which are specific to the i386 platform. + * + * Using these techniques, this driver achieves very high performance + * by minimizing the amount of interrupts generated during large + * transfers and by completely avoiding buffer copies. Frame transfer + * to and from the ThunderLAN chip is performed entirely by the chip + * itself thereby reducing the load on the host CPU. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#include <net/bpf.h> + +#include <vm/vm.h> /* for vtophys */ +#include <vm/pmap.h> /* for vtophys */ +#include <machine/bus_memio.h> +#include <machine/bus_pio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +/* + * Default to using PIO register access mode to pacify certain + * laptop docking stations with built-in ThunderLAN chips that + * don't seem to handle memory mapped mode properly. + */ +#define TL_USEIOSPACE + +#include <pci/if_tlreg.h> + +MODULE_DEPEND(tl, miibus, 1, 1, 1); + +/* "controller miibus0" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +#if !defined(lint) +static const char rcsid[] = + "$FreeBSD$"; +#endif + +/* + * Various supported device vendors/types and their names. + */ + +static struct tl_type tl_devs[] = { + { TI_VENDORID, TI_DEVICEID_THUNDERLAN, + "Texas Instruments ThunderLAN" }, + { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10, + "Compaq Netelligent 10" }, + { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100, + "Compaq Netelligent 10/100" }, + { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_PROLIANT, + "Compaq Netelligent 10/100 Proliant" }, + { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_DUAL, + "Compaq Netelligent 10/100 Dual Port" }, + { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_INTEGRATED, + "Compaq NetFlex-3/P Integrated" }, + { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P, + "Compaq NetFlex-3/P" }, + { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_BNC, + "Compaq NetFlex 3/P w/ BNC" }, + { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_EMBEDDED, + "Compaq Netelligent 10/100 TX Embedded UTP" }, + { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_T2_UTP_COAX, + "Compaq Netelligent 10 T/2 PCI UTP/Coax" }, + { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_TX_UTP, + "Compaq Netelligent 10/100 TX UTP" }, + { OLICOM_VENDORID, OLICOM_DEVICEID_OC2183, + "Olicom OC-2183/2185" }, + { OLICOM_VENDORID, OLICOM_DEVICEID_OC2325, + "Olicom OC-2325" }, + { OLICOM_VENDORID, OLICOM_DEVICEID_OC2326, + "Olicom OC-2326 10/100 TX UTP" }, + { 0, 0, NULL } +}; + +static int tl_probe (device_t); +static int tl_attach (device_t); +static int tl_detach (device_t); +static int tl_intvec_rxeoc (void *, u_int32_t); +static int tl_intvec_txeoc (void *, u_int32_t); +static int tl_intvec_txeof (void *, u_int32_t); +static int tl_intvec_rxeof (void *, u_int32_t); +static int tl_intvec_adchk (void *, u_int32_t); +static int tl_intvec_netsts (void *, u_int32_t); + +static int tl_newbuf (struct tl_softc *, struct tl_chain_onefrag *); +static void tl_stats_update (void *); +static int tl_encap (struct tl_softc *, struct tl_chain *, + struct mbuf *); + +static void tl_intr (void *); +static void tl_start (struct ifnet *); +static int tl_ioctl (struct ifnet *, u_long, caddr_t); +static void tl_init (void *); +static void tl_stop (struct tl_softc *); +static void tl_watchdog (struct ifnet *); +static void tl_shutdown (device_t); +static int tl_ifmedia_upd (struct ifnet *); +static void tl_ifmedia_sts (struct ifnet *, struct ifmediareq *); + +static u_int8_t tl_eeprom_putbyte (struct tl_softc *, int); +static u_int8_t tl_eeprom_getbyte (struct tl_softc *, int, u_int8_t *); +static int tl_read_eeprom (struct tl_softc *, caddr_t, int, int); + +static void tl_mii_sync (struct tl_softc *); +static void tl_mii_send (struct tl_softc *, u_int32_t, int); +static int tl_mii_readreg (struct tl_softc *, struct tl_mii_frame *); +static int tl_mii_writereg (struct tl_softc *, struct tl_mii_frame *); +static int tl_miibus_readreg (device_t, int, int); +static int tl_miibus_writereg (device_t, int, int, int); +static void tl_miibus_statchg (device_t); + +static void tl_setmode (struct tl_softc *, int); +static int tl_calchash (caddr_t); +static void tl_setmulti (struct tl_softc *); +static void tl_setfilt (struct tl_softc *, caddr_t, int); +static void tl_softreset (struct tl_softc *, int); +static void tl_hardreset (device_t); +static int tl_list_rx_init (struct tl_softc *); +static int tl_list_tx_init (struct tl_softc *); + +static u_int8_t tl_dio_read8 (struct tl_softc *, int); +static u_int16_t tl_dio_read16 (struct tl_softc *, int); +static u_int32_t tl_dio_read32 (struct tl_softc *, int); +static void tl_dio_write8 (struct tl_softc *, int, int); +static void tl_dio_write16 (struct tl_softc *, int, int); +static void tl_dio_write32 (struct tl_softc *, int, int); +static void tl_dio_setbit (struct tl_softc *, int, int); +static void tl_dio_clrbit (struct tl_softc *, int, int); +static void tl_dio_setbit16 (struct tl_softc *, int, int); +static void tl_dio_clrbit16 (struct tl_softc *, int, int); + +#ifdef TL_USEIOSPACE +#define TL_RES SYS_RES_IOPORT +#define TL_RID TL_PCI_LOIO +#else +#define TL_RES SYS_RES_MEMORY +#define TL_RID TL_PCI_LOMEM +#endif + +static device_method_t tl_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tl_probe), + DEVMETHOD(device_attach, tl_attach), + DEVMETHOD(device_detach, tl_detach), + DEVMETHOD(device_shutdown, tl_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, tl_miibus_readreg), + DEVMETHOD(miibus_writereg, tl_miibus_writereg), + DEVMETHOD(miibus_statchg, tl_miibus_statchg), + + { 0, 0 } +}; + +static driver_t tl_driver = { + "tl", + tl_methods, + sizeof(struct tl_softc) +}; + +static devclass_t tl_devclass; + +DRIVER_MODULE(if_tl, pci, tl_driver, tl_devclass, 0, 0); +DRIVER_MODULE(miibus, tl, miibus_driver, miibus_devclass, 0, 0); + +static u_int8_t tl_dio_read8(sc, reg) + struct tl_softc *sc; + int reg; +{ + CSR_WRITE_2(sc, TL_DIO_ADDR, reg); + return(CSR_READ_1(sc, TL_DIO_DATA + (reg & 3))); +} + +static u_int16_t tl_dio_read16(sc, reg) + struct tl_softc *sc; + int reg; +{ + CSR_WRITE_2(sc, TL_DIO_ADDR, reg); + return(CSR_READ_2(sc, TL_DIO_DATA + (reg & 3))); +} + +static u_int32_t tl_dio_read32(sc, reg) + struct tl_softc *sc; + int reg; +{ + CSR_WRITE_2(sc, TL_DIO_ADDR, reg); + return(CSR_READ_4(sc, TL_DIO_DATA + (reg & 3))); +} + +static void tl_dio_write8(sc, reg, val) + struct tl_softc *sc; + int reg; + int val; +{ + CSR_WRITE_2(sc, TL_DIO_ADDR, reg); + CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), val); + return; +} + +static void tl_dio_write16(sc, reg, val) + struct tl_softc *sc; + int reg; + int val; +{ + CSR_WRITE_2(sc, TL_DIO_ADDR, reg); + CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), val); + return; +} + +static void tl_dio_write32(sc, reg, val) + struct tl_softc *sc; + int reg; + int val; +{ + CSR_WRITE_2(sc, TL_DIO_ADDR, reg); + CSR_WRITE_4(sc, TL_DIO_DATA + (reg & 3), val); + return; +} + +static void tl_dio_setbit(sc, reg, bit) + struct tl_softc *sc; + int reg; + int bit; +{ + u_int8_t f; + + CSR_WRITE_2(sc, TL_DIO_ADDR, reg); + f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); + f |= bit; + CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); + + return; +} + +static void tl_dio_clrbit(sc, reg, bit) + struct tl_softc *sc; + int reg; + int bit; +{ + u_int8_t f; + + CSR_WRITE_2(sc, TL_DIO_ADDR, reg); + f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); + f &= ~bit; + CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); + + return; +} + +static void tl_dio_setbit16(sc, reg, bit) + struct tl_softc *sc; + int reg; + int bit; +{ + u_int16_t f; + + CSR_WRITE_2(sc, TL_DIO_ADDR, reg); + f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); + f |= bit; + CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); + + return; +} + +static void tl_dio_clrbit16(sc, reg, bit) + struct tl_softc *sc; + int reg; + int bit; +{ + u_int16_t f; + + CSR_WRITE_2(sc, TL_DIO_ADDR, reg); + f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); + f &= ~bit; + CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); + + return; +} + +/* + * Send an instruction or address to the EEPROM, check for ACK. + */ +static u_int8_t tl_eeprom_putbyte(sc, byte) + struct tl_softc *sc; + int byte; +{ + register int i, ack = 0; + + /* + * Make sure we're in TX mode. + */ + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ETXEN); + + /* + * Feed in each bit and stobe the clock. + */ + for (i = 0x80; i; i >>= 1) { + if (byte & i) { + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_EDATA); + } else { + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_EDATA); + } + DELAY(1); + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); + DELAY(1); + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); + } + + /* + * Turn off TX mode. + */ + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); + + /* + * Check for ack. + */ + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); + ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA; + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); + + return(ack); +} + +/* + * Read a byte of data stored in the EEPROM at address 'addr.' + */ +static u_int8_t tl_eeprom_getbyte(sc, addr, dest) + struct tl_softc *sc; + int addr; + u_int8_t *dest; +{ + register int i; + u_int8_t byte = 0; + + tl_dio_write8(sc, TL_NETSIO, 0); + + EEPROM_START; + + /* + * Send write control code to EEPROM. + */ + if (tl_eeprom_putbyte(sc, EEPROM_CTL_WRITE)) { + printf("tl%d: failed to send write command, status: %x\n", + sc->tl_unit, tl_dio_read8(sc, TL_NETSIO)); + return(1); + } + + /* + * Send address of byte we want to read. + */ + if (tl_eeprom_putbyte(sc, addr)) { + printf("tl%d: failed to send address, status: %x\n", + sc->tl_unit, tl_dio_read8(sc, TL_NETSIO)); + return(1); + } + + EEPROM_STOP; + EEPROM_START; + /* + * Send read control code to EEPROM. + */ + if (tl_eeprom_putbyte(sc, EEPROM_CTL_READ)) { + printf("tl%d: failed to send write command, status: %x\n", + sc->tl_unit, tl_dio_read8(sc, TL_NETSIO)); + return(1); + } + + /* + * Start reading bits from EEPROM. + */ + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); + for (i = 0x80; i; i >>= 1) { + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); + DELAY(1); + if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA) + byte |= i; + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); + DELAY(1); + } + + EEPROM_STOP; + + /* + * No ACK generated for read, so just return byte. + */ + + *dest = byte; + + return(0); +} + +/* + * Read a sequence of bytes from the EEPROM. + */ +static int tl_read_eeprom(sc, dest, off, cnt) + struct tl_softc *sc; + caddr_t dest; + int off; + int cnt; +{ + int err = 0, i; + u_int8_t byte = 0; + + for (i = 0; i < cnt; i++) { + err = tl_eeprom_getbyte(sc, off + i, &byte); + if (err) + break; + *(dest + i) = byte; + } + + return(err ? 1 : 0); +} + +static void tl_mii_sync(sc) + struct tl_softc *sc; +{ + register int i; + + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); + + for (i = 0; i < 32; i++) { + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); + } + + return; +} + +static void tl_mii_send(sc, bits, cnt) + struct tl_softc *sc; + u_int32_t bits; + int cnt; +{ + int i; + + for (i = (0x1 << (cnt - 1)); i; i >>= 1) { + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); + if (bits & i) { + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MDATA); + } else { + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MDATA); + } + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); + } +} + +static int tl_mii_readreg(sc, frame) + struct tl_softc *sc; + struct tl_mii_frame *frame; + +{ + int i, ack; + int minten = 0; + + TL_LOCK(sc); + + tl_mii_sync(sc); + + /* + * Set up frame for RX. + */ + frame->mii_stdelim = TL_MII_STARTDELIM; + frame->mii_opcode = TL_MII_READOP; + frame->mii_turnaround = 0; + frame->mii_data = 0; + + /* + * Turn off MII interrupt by forcing MINTEN low. + */ + minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; + if (minten) { + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); + } + + /* + * Turn on data xmit. + */ + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN); + + /* + * Send command/address info. + */ + tl_mii_send(sc, frame->mii_stdelim, 2); + tl_mii_send(sc, frame->mii_opcode, 2); + tl_mii_send(sc, frame->mii_phyaddr, 5); + tl_mii_send(sc, frame->mii_regaddr, 5); + + /* + * Turn off xmit. + */ + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); + + /* Idle bit */ + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); + + /* Check for ack */ + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); + ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA; + + /* Complete the cycle */ + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); + + /* + * Now try reading data bits. If the ack failed, we still + * need to clock through 16 cycles to keep the PHYs in sync. + */ + if (ack) { + for(i = 0; i < 16; i++) { + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); + } + goto fail; + } + + for (i = 0x8000; i; i >>= 1) { + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); + if (!ack) { + if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA) + frame->mii_data |= i; + } + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); + } + +fail: + + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); + + /* Reenable interrupts */ + if (minten) { + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); + } + + TL_UNLOCK(sc); + + if (ack) + return(1); + return(0); +} + +static int tl_mii_writereg(sc, frame) + struct tl_softc *sc; + struct tl_mii_frame *frame; + +{ + int minten; + + TL_LOCK(sc); + + tl_mii_sync(sc); + + /* + * Set up frame for TX. + */ + + frame->mii_stdelim = TL_MII_STARTDELIM; + frame->mii_opcode = TL_MII_WRITEOP; + frame->mii_turnaround = TL_MII_TURNAROUND; + + /* + * Turn off MII interrupt by forcing MINTEN low. + */ + minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; + if (minten) { + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); + } + + /* + * Turn on data output. + */ + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN); + + tl_mii_send(sc, frame->mii_stdelim, 2); + tl_mii_send(sc, frame->mii_opcode, 2); + tl_mii_send(sc, frame->mii_phyaddr, 5); + tl_mii_send(sc, frame->mii_regaddr, 5); + tl_mii_send(sc, frame->mii_turnaround, 2); + tl_mii_send(sc, frame->mii_data, 16); + + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); + + /* + * Turn off xmit. + */ + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); + + /* Reenable interrupts */ + if (minten) + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); + + TL_UNLOCK(sc); + + return(0); +} + +static int tl_miibus_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct tl_softc *sc; + struct tl_mii_frame frame; + + sc = device_get_softc(dev); + bzero((char *)&frame, sizeof(frame)); + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + tl_mii_readreg(sc, &frame); + + return(frame.mii_data); +} + +static int tl_miibus_writereg(dev, phy, reg, data) + device_t dev; + int phy, reg, data; +{ + struct tl_softc *sc; + struct tl_mii_frame frame; + + sc = device_get_softc(dev); + bzero((char *)&frame, sizeof(frame)); + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + frame.mii_data = data; + + tl_mii_writereg(sc, &frame); + + return(0); +} + +static void tl_miibus_statchg(dev) + device_t dev; +{ + struct tl_softc *sc; + struct mii_data *mii; + + sc = device_get_softc(dev); + TL_LOCK(sc); + mii = device_get_softc(sc->tl_miibus); + + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { + tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); + } else { + tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); + } + TL_UNLOCK(sc); + + return; +} + +/* + * Set modes for bitrate devices. + */ +static void tl_setmode(sc, media) + struct tl_softc *sc; + int media; +{ + if (IFM_SUBTYPE(media) == IFM_10_5) + tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD1); + if (IFM_SUBTYPE(media) == IFM_10_T) { + tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD1); + if ((media & IFM_GMASK) == IFM_FDX) { + tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD3); + tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); + } else { + tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD3); + tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); + } + } + + return; +} + +/* + * Calculate the hash of a MAC address for programming the multicast hash + * table. This hash is simply the address split into 6-bit chunks + * XOR'd, e.g. + * byte: 000000|00 1111|1111 22|222222|333333|33 4444|4444 55|555555 + * bit: 765432|10 7654|3210 76|543210|765432|10 7654|3210 76|543210 + * Bytes 0-2 and 3-5 are symmetrical, so are folded together. Then + * the folded 24-bit value is split into 6-bit portions and XOR'd. + */ +static int tl_calchash(addr) + caddr_t addr; +{ + int t; + + t = (addr[0] ^ addr[3]) << 16 | (addr[1] ^ addr[4]) << 8 | + (addr[2] ^ addr[5]); + return ((t >> 18) ^ (t >> 12) ^ (t >> 6) ^ t) & 0x3f; +} + +/* + * The ThunderLAN has a perfect MAC address filter in addition to + * the multicast hash filter. The perfect filter can be programmed + * with up to four MAC addresses. The first one is always used to + * hold the station address, which leaves us free to use the other + * three for multicast addresses. + */ +static void tl_setfilt(sc, addr, slot) + struct tl_softc *sc; + caddr_t addr; + int slot; +{ + int i; + u_int16_t regaddr; + + regaddr = TL_AREG0_B5 + (slot * ETHER_ADDR_LEN); + + for (i = 0; i < ETHER_ADDR_LEN; i++) + tl_dio_write8(sc, regaddr + i, *(addr + i)); + + return; +} + +/* + * XXX In FreeBSD 3.0, multicast addresses are managed using a doubly + * linked list. This is fine, except addresses are added from the head + * end of the list. We want to arrange for 224.0.0.1 (the "all hosts") + * group to always be in the perfect filter, but as more groups are added, + * the 224.0.0.1 entry (which is always added first) gets pushed down + * the list and ends up at the tail. So after 3 or 4 multicast groups + * are added, the all-hosts entry gets pushed out of the perfect filter + * and into the hash table. + * + * Because the multicast list is a doubly-linked list as opposed to a + * circular queue, we don't have the ability to just grab the tail of + * the list and traverse it backwards. Instead, we have to traverse + * the list once to find the tail, then traverse it again backwards to + * update the multicast filter. + */ +static void tl_setmulti(sc) + struct tl_softc *sc; +{ + struct ifnet *ifp; + u_int32_t hashes[2] = { 0, 0 }; + int h, i; + struct ifmultiaddr *ifma; + u_int8_t dummy[] = { 0, 0, 0, 0, 0 ,0 }; + ifp = &sc->arpcom.ac_if; + + /* First, zot all the existing filters. */ + for (i = 1; i < 4; i++) + tl_setfilt(sc, (caddr_t)&dummy, i); + tl_dio_write32(sc, TL_HASH1, 0); + tl_dio_write32(sc, TL_HASH2, 0); + + /* Now program new ones. */ + if (ifp->if_flags & IFF_ALLMULTI) { + hashes[0] = 0xFFFFFFFF; + hashes[1] = 0xFFFFFFFF; + } else { + i = 1; + TAILQ_FOREACH_REVERSE(ifma, &ifp->if_multiaddrs, ifmultihead, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + /* + * Program the first three multicast groups + * into the perfect filter. For all others, + * use the hash table. + */ + if (i < 4) { + tl_setfilt(sc, + LLADDR((struct sockaddr_dl *)ifma->ifma_addr), i); + i++; + continue; + } + + h = tl_calchash( + LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + } + } + + tl_dio_write32(sc, TL_HASH1, hashes[0]); + tl_dio_write32(sc, TL_HASH2, hashes[1]); + + return; +} + +/* + * This routine is recommended by the ThunderLAN manual to insure that + * the internal PHY is powered up correctly. It also recommends a one + * second pause at the end to 'wait for the clocks to start' but in my + * experience this isn't necessary. + */ +static void tl_hardreset(dev) + device_t dev; +{ + struct tl_softc *sc; + int i; + u_int16_t flags; + + sc = device_get_softc(dev); + + tl_mii_sync(sc); + + flags = BMCR_LOOP|BMCR_ISO|BMCR_PDOWN; + + for (i = 0; i < MII_NPHY; i++) + tl_miibus_writereg(dev, i, MII_BMCR, flags); + + tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_ISO); + DELAY(50000); + tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_LOOP|BMCR_ISO); + tl_mii_sync(sc); + while(tl_miibus_readreg(dev, 31, MII_BMCR) & BMCR_RESET); + + DELAY(50000); + return; +} + +static void tl_softreset(sc, internal) + struct tl_softc *sc; + int internal; +{ + u_int32_t cmd, dummy, i; + + /* Assert the adapter reset bit. */ + CMD_SET(sc, TL_CMD_ADRST); + + /* Turn off interrupts */ + CMD_SET(sc, TL_CMD_INTSOFF); + + /* First, clear the stats registers. */ + for (i = 0; i < 5; i++) + dummy = tl_dio_read32(sc, TL_TXGOODFRAMES); + + /* Clear Areg and Hash registers */ + for (i = 0; i < 8; i++) + tl_dio_write32(sc, TL_AREG0_B5, 0x00000000); + + /* + * Set up Netconfig register. Enable one channel and + * one fragment mode. + */ + tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_ONECHAN|TL_CFG_ONEFRAG); + if (internal && !sc->tl_bitrate) { + tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); + } else { + tl_dio_clrbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); + } + + /* Handle cards with bitrate devices. */ + if (sc->tl_bitrate) + tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_BITRATE); + + /* + * Load adapter irq pacing timer and tx threshold. + * We make the transmit threshold 1 initially but we may + * change that later. + */ + cmd = CSR_READ_4(sc, TL_HOSTCMD); + cmd |= TL_CMD_NES; + cmd &= ~(TL_CMD_RT|TL_CMD_EOC|TL_CMD_ACK_MASK|TL_CMD_CHSEL_MASK); + CMD_PUT(sc, cmd | (TL_CMD_LDTHR | TX_THR)); + CMD_PUT(sc, cmd | (TL_CMD_LDTMR | 0x00000003)); + + /* Unreset the MII */ + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_NMRST); + + /* Take the adapter out of reset */ + tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NRESET|TL_CMD_NWRAP); + + /* Wait for things to settle down a little. */ + DELAY(500); + + return; +} + +/* + * Probe for a ThunderLAN chip. Check the PCI vendor and device IDs + * against our list and return its name if we find a match. + */ +static int tl_probe(dev) + device_t dev; +{ + struct tl_type *t; + + t = tl_devs; + + while(t->tl_name != NULL) { + if ((pci_get_vendor(dev) == t->tl_vid) && + (pci_get_device(dev) == t->tl_did)) { + device_set_desc(dev, t->tl_name); + return(0); + } + t++; + } + + return(ENXIO); +} + +static int tl_attach(dev) + device_t dev; +{ + int i; + u_int32_t command; + u_int16_t did, vid; + struct tl_type *t; + struct ifnet *ifp; + struct tl_softc *sc; + int unit, error = 0, rid; + + vid = pci_get_vendor(dev); + did = pci_get_device(dev); + sc = device_get_softc(dev); + unit = device_get_unit(dev); + bzero(sc, sizeof(struct tl_softc)); + + t = tl_devs; + while(t->tl_name != NULL) { + if (vid == t->tl_vid && did == t->tl_did) + break; + t++; + } + + if (t->tl_name == NULL) { + printf("tl%d: unknown device!?\n", unit); + goto fail; + } + + mtx_init(&sc->tl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF | MTX_RECURSE); + TL_LOCK(sc); + + /* + * Map control/status registers. + */ + pci_enable_busmaster(dev); + pci_enable_io(dev, SYS_RES_IOPORT); + pci_enable_io(dev, SYS_RES_MEMORY); + command = pci_read_config(dev, PCIR_COMMAND, 4); + +#ifdef TL_USEIOSPACE + if (!(command & PCIM_CMD_PORTEN)) { + printf("tl%d: failed to enable I/O ports!\n", unit); + error = ENXIO; + goto fail; + } + + rid = TL_PCI_LOIO; + sc->tl_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0, ~0, 1, RF_ACTIVE); + + /* + * Some cards have the I/O and memory mapped address registers + * reversed. Try both combinations before giving up. + */ + if (sc->tl_res == NULL) { + rid = TL_PCI_LOMEM; + sc->tl_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0, ~0, 1, RF_ACTIVE); + } +#else + if (!(command & PCIM_CMD_MEMEN)) { + printf("tl%d: failed to enable memory mapping!\n", unit); + error = ENXIO; + goto fail; + } + + rid = TL_PCI_LOMEM; + sc->tl_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (sc->tl_res == NULL) { + rid = TL_PCI_LOIO; + sc->tl_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + } +#endif + + if (sc->tl_res == NULL) { + printf("tl%d: couldn't map ports/memory\n", unit); + error = ENXIO; + goto fail; + } + + sc->tl_btag = rman_get_bustag(sc->tl_res); + sc->tl_bhandle = rman_get_bushandle(sc->tl_res); + +#ifdef notdef + /* + * The ThunderLAN manual suggests jacking the PCI latency + * timer all the way up to its maximum value. I'm not sure + * if this is really necessary, but what the manual wants, + * the manual gets. + */ + command = pci_read_config(dev, TL_PCI_LATENCY_TIMER, 4); + command |= 0x0000FF00; + pci_write_config(dev, TL_PCI_LATENCY_TIMER, command, 4); +#endif + + /* Allocate interrupt */ + rid = 0; + sc->tl_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->tl_irq == NULL) { + bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); + printf("tl%d: couldn't map interrupt\n", unit); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->tl_irq, INTR_TYPE_NET, + tl_intr, sc, &sc->tl_intrhand); + + if (error) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); + bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); + printf("tl%d: couldn't set up irq\n", unit); + goto fail; + } + + /* + * Now allocate memory for the TX and RX lists. + */ + sc->tl_ldata = contigmalloc(sizeof(struct tl_list_data), M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc->tl_ldata == NULL) { + bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); + bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); + printf("tl%d: no memory for list buffers!\n", unit); + error = ENXIO; + goto fail; + } + + bzero(sc->tl_ldata, sizeof(struct tl_list_data)); + + sc->tl_unit = unit; + sc->tl_dinfo = t; + if (t->tl_vid == COMPAQ_VENDORID || t->tl_vid == TI_VENDORID) + sc->tl_eeaddr = TL_EEPROM_EADDR; + if (t->tl_vid == OLICOM_VENDORID) + sc->tl_eeaddr = TL_EEPROM_EADDR_OC; + + /* Reset the adapter. */ + tl_softreset(sc, 1); + tl_hardreset(dev); + tl_softreset(sc, 1); + + /* + * Get station address from the EEPROM. + */ + if (tl_read_eeprom(sc, (caddr_t)&sc->arpcom.ac_enaddr, + sc->tl_eeaddr, ETHER_ADDR_LEN)) { + bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); + bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); + contigfree(sc->tl_ldata, + sizeof(struct tl_list_data), M_DEVBUF); + printf("tl%d: failed to read station address\n", unit); + error = ENXIO; + goto fail; + } + + /* + * XXX Olicom, in its desire to be different from the + * rest of the world, has done strange things with the + * encoding of the station address in the EEPROM. First + * of all, they store the address at offset 0xF8 rather + * than at 0x83 like the ThunderLAN manual suggests. + * Second, they store the address in three 16-bit words in + * network byte order, as opposed to storing it sequentially + * like all the other ThunderLAN cards. In order to get + * the station address in a form that matches what the Olicom + * diagnostic utility specifies, we have to byte-swap each + * word. To make things even more confusing, neither 00:00:28 + * nor 00:00:24 appear in the IEEE OUI database. + */ + if (sc->tl_dinfo->tl_vid == OLICOM_VENDORID) { + for (i = 0; i < ETHER_ADDR_LEN; i += 2) { + u_int16_t *p; + p = (u_int16_t *)&sc->arpcom.ac_enaddr[i]; + *p = ntohs(*p); + } + } + + /* + * A ThunderLAN chip was detected. Inform the world. + */ + printf("tl%d: Ethernet address: %6D\n", unit, + sc->arpcom.ac_enaddr, ":"); + + ifp = &sc->arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_unit = sc->tl_unit; + ifp->if_name = "tl"; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = tl_ioctl; + ifp->if_output = ether_output; + ifp->if_start = tl_start; + ifp->if_watchdog = tl_watchdog; + ifp->if_init = tl_init; + ifp->if_mtu = ETHERMTU; + ifp->if_snd.ifq_maxlen = TL_TX_LIST_CNT - 1; + callout_handle_init(&sc->tl_stat_ch); + + /* Reset the adapter again. */ + tl_softreset(sc, 1); + tl_hardreset(dev); + tl_softreset(sc, 1); + + /* + * Do MII setup. If no PHYs are found, then this is a + * bitrate ThunderLAN chip that only supports 10baseT + * and AUI/BNC. + */ + if (mii_phy_probe(dev, &sc->tl_miibus, + tl_ifmedia_upd, tl_ifmedia_sts)) { + struct ifmedia *ifm; + sc->tl_bitrate = 1; + ifmedia_init(&sc->ifmedia, 0, tl_ifmedia_upd, tl_ifmedia_sts); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); + ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_10_T); + /* Reset again, this time setting bitrate mode. */ + tl_softreset(sc, 1); + ifm = &sc->ifmedia; + ifm->ifm_media = ifm->ifm_cur->ifm_media; + tl_ifmedia_upd(ifp); + } + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); + TL_UNLOCK(sc); + return(0); + +fail: + TL_UNLOCK(sc); + mtx_destroy(&sc->tl_mtx); + return(error); +} + +static int tl_detach(dev) + device_t dev; +{ + struct tl_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + TL_LOCK(sc); + ifp = &sc->arpcom.ac_if; + + tl_stop(sc); + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + + bus_generic_detach(dev); + device_delete_child(dev, sc->tl_miibus); + + contigfree(sc->tl_ldata, sizeof(struct tl_list_data), M_DEVBUF); + if (sc->tl_bitrate) + ifmedia_removeall(&sc->ifmedia); + + bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); + bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); + + TL_UNLOCK(sc); + mtx_destroy(&sc->tl_mtx); + + return(0); +} + +/* + * Initialize the transmit lists. + */ +static int tl_list_tx_init(sc) + struct tl_softc *sc; +{ + struct tl_chain_data *cd; + struct tl_list_data *ld; + int i; + + cd = &sc->tl_cdata; + ld = sc->tl_ldata; + for (i = 0; i < TL_TX_LIST_CNT; i++) { + cd->tl_tx_chain[i].tl_ptr = &ld->tl_tx_list[i]; + if (i == (TL_TX_LIST_CNT - 1)) + cd->tl_tx_chain[i].tl_next = NULL; + else + cd->tl_tx_chain[i].tl_next = &cd->tl_tx_chain[i + 1]; + } + + cd->tl_tx_free = &cd->tl_tx_chain[0]; + cd->tl_tx_tail = cd->tl_tx_head = NULL; + sc->tl_txeoc = 1; + + return(0); +} + +/* + * Initialize the RX lists and allocate mbufs for them. + */ +static int tl_list_rx_init(sc) + struct tl_softc *sc; +{ + struct tl_chain_data *cd; + struct tl_list_data *ld; + int i; + + cd = &sc->tl_cdata; + ld = sc->tl_ldata; + + for (i = 0; i < TL_RX_LIST_CNT; i++) { + cd->tl_rx_chain[i].tl_ptr = + (struct tl_list_onefrag *)&ld->tl_rx_list[i]; + if (tl_newbuf(sc, &cd->tl_rx_chain[i]) == ENOBUFS) + return(ENOBUFS); + if (i == (TL_RX_LIST_CNT - 1)) { + cd->tl_rx_chain[i].tl_next = NULL; + ld->tl_rx_list[i].tlist_fptr = 0; + } else { + cd->tl_rx_chain[i].tl_next = &cd->tl_rx_chain[i + 1]; + ld->tl_rx_list[i].tlist_fptr = + vtophys(&ld->tl_rx_list[i + 1]); + } + } + + cd->tl_rx_head = &cd->tl_rx_chain[0]; + cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; + + return(0); +} + +static int tl_newbuf(sc, c) + struct tl_softc *sc; + struct tl_chain_onefrag *c; +{ + struct mbuf *m_new = NULL; + + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(ENOBUFS); + + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + return(ENOBUFS); + } + +#ifdef __alpha__ + m_new->m_data += 2; +#endif + + c->tl_mbuf = m_new; + c->tl_next = NULL; + c->tl_ptr->tlist_frsize = MCLBYTES; + c->tl_ptr->tlist_fptr = 0; + c->tl_ptr->tl_frag.tlist_dadr = vtophys(mtod(m_new, caddr_t)); + c->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; + c->tl_ptr->tlist_cstat = TL_CSTAT_READY; + + return(0); +} +/* + * Interrupt handler for RX 'end of frame' condition (EOF). This + * tells us that a full ethernet frame has been captured and we need + * to handle it. + * + * Reception is done using 'lists' which consist of a header and a + * series of 10 data count/data address pairs that point to buffers. + * Initially you're supposed to create a list, populate it with pointers + * to buffers, then load the physical address of the list into the + * ch_parm register. The adapter is then supposed to DMA the received + * frame into the buffers for you. + * + * To make things as fast as possible, we have the chip DMA directly + * into mbufs. This saves us from having to do a buffer copy: we can + * just hand the mbufs directly to ether_input(). Once the frame has + * been sent on its way, the 'list' structure is assigned a new buffer + * and moved to the end of the RX chain. As long we we stay ahead of + * the chip, it will always think it has an endless receive channel. + * + * If we happen to fall behind and the chip manages to fill up all of + * the buffers, it will generate an end of channel interrupt and wait + * for us to empty the chain and restart the receiver. + */ +static int tl_intvec_rxeof(xsc, type) + void *xsc; + u_int32_t type; +{ + struct tl_softc *sc; + int r = 0, total_len = 0; + struct ether_header *eh; + struct mbuf *m; + struct ifnet *ifp; + struct tl_chain_onefrag *cur_rx; + + sc = xsc; + ifp = &sc->arpcom.ac_if; + + while(sc->tl_cdata.tl_rx_head != NULL) { + cur_rx = sc->tl_cdata.tl_rx_head; + if (!(cur_rx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) + break; + r++; + sc->tl_cdata.tl_rx_head = cur_rx->tl_next; + m = cur_rx->tl_mbuf; + total_len = cur_rx->tl_ptr->tlist_frsize; + + if (tl_newbuf(sc, cur_rx) == ENOBUFS) { + ifp->if_ierrors++; + cur_rx->tl_ptr->tlist_frsize = MCLBYTES; + cur_rx->tl_ptr->tlist_cstat = TL_CSTAT_READY; + cur_rx->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; + continue; + } + + sc->tl_cdata.tl_rx_tail->tl_ptr->tlist_fptr = + vtophys(cur_rx->tl_ptr); + sc->tl_cdata.tl_rx_tail->tl_next = cur_rx; + sc->tl_cdata.tl_rx_tail = cur_rx; + + eh = mtod(m, struct ether_header *); + m->m_pkthdr.rcvif = ifp; + + /* + * Note: when the ThunderLAN chip is in 'capture all + * frames' mode, it will receive its own transmissions. + * We drop don't need to process our own transmissions, + * so we drop them here and continue. + */ + /*if (ifp->if_flags & IFF_PROMISC && */ + if (!bcmp(eh->ether_shost, sc->arpcom.ac_enaddr, + ETHER_ADDR_LEN)) { + m_freem(m); + continue; + } + + /* Remove header from mbuf and pass it on. */ + m->m_pkthdr.len = m->m_len = + total_len - sizeof(struct ether_header); + m->m_data += sizeof(struct ether_header); + ether_input(ifp, eh, m); + } + + return(r); +} + +/* + * The RX-EOC condition hits when the ch_parm address hasn't been + * initialized or the adapter reached a list with a forward pointer + * of 0 (which indicates the end of the chain). In our case, this means + * the card has hit the end of the receive buffer chain and we need to + * empty out the buffers and shift the pointer back to the beginning again. + */ +static int tl_intvec_rxeoc(xsc, type) + void *xsc; + u_int32_t type; +{ + struct tl_softc *sc; + int r; + struct tl_chain_data *cd; + + + sc = xsc; + cd = &sc->tl_cdata; + + /* Flush out the receive queue and ack RXEOF interrupts. */ + r = tl_intvec_rxeof(xsc, type); + CMD_PUT(sc, TL_CMD_ACK | r | (type & ~(0x00100000))); + r = 1; + cd->tl_rx_head = &cd->tl_rx_chain[0]; + cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; + CSR_WRITE_4(sc, TL_CH_PARM, vtophys(sc->tl_cdata.tl_rx_head->tl_ptr)); + r |= (TL_CMD_GO|TL_CMD_RT); + return(r); +} + +static int tl_intvec_txeof(xsc, type) + void *xsc; + u_int32_t type; +{ + struct tl_softc *sc; + int r = 0; + struct tl_chain *cur_tx; + + sc = xsc; + + /* + * Go through our tx list and free mbufs for those + * frames that have been sent. + */ + while (sc->tl_cdata.tl_tx_head != NULL) { + cur_tx = sc->tl_cdata.tl_tx_head; + if (!(cur_tx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) + break; + sc->tl_cdata.tl_tx_head = cur_tx->tl_next; + + r++; + m_freem(cur_tx->tl_mbuf); + cur_tx->tl_mbuf = NULL; + + cur_tx->tl_next = sc->tl_cdata.tl_tx_free; + sc->tl_cdata.tl_tx_free = cur_tx; + if (!cur_tx->tl_ptr->tlist_fptr) + break; + } + + return(r); +} + +/* + * The transmit end of channel interrupt. The adapter triggers this + * interrupt to tell us it hit the end of the current transmit list. + * + * A note about this: it's possible for a condition to arise where + * tl_start() may try to send frames between TXEOF and TXEOC interrupts. + * You have to avoid this since the chip expects things to go in a + * particular order: transmit, acknowledge TXEOF, acknowledge TXEOC. + * When the TXEOF handler is called, it will free all of the transmitted + * frames and reset the tx_head pointer to NULL. However, a TXEOC + * interrupt should be received and acknowledged before any more frames + * are queued for transmission. If tl_statrt() is called after TXEOF + * resets the tx_head pointer but _before_ the TXEOC interrupt arrives, + * it could attempt to issue a transmit command prematurely. + * + * To guard against this, tl_start() will only issue transmit commands + * if the tl_txeoc flag is set, and only the TXEOC interrupt handler + * can set this flag once tl_start() has cleared it. + */ +static int tl_intvec_txeoc(xsc, type) + void *xsc; + u_int32_t type; +{ + struct tl_softc *sc; + struct ifnet *ifp; + u_int32_t cmd; + + sc = xsc; + ifp = &sc->arpcom.ac_if; + + /* Clear the timeout timer. */ + ifp->if_timer = 0; + + if (sc->tl_cdata.tl_tx_head == NULL) { + ifp->if_flags &= ~IFF_OACTIVE; + sc->tl_cdata.tl_tx_tail = NULL; + sc->tl_txeoc = 1; + } else { + sc->tl_txeoc = 0; + /* First we have to ack the EOC interrupt. */ + CMD_PUT(sc, TL_CMD_ACK | 0x00000001 | type); + /* Then load the address of the next TX list. */ + CSR_WRITE_4(sc, TL_CH_PARM, + vtophys(sc->tl_cdata.tl_tx_head->tl_ptr)); + /* Restart TX channel. */ + cmd = CSR_READ_4(sc, TL_HOSTCMD); + cmd &= ~TL_CMD_RT; + cmd |= TL_CMD_GO|TL_CMD_INTSON; + CMD_PUT(sc, cmd); + return(0); + } + + return(1); +} + +static int tl_intvec_adchk(xsc, type) + void *xsc; + u_int32_t type; +{ + struct tl_softc *sc; + + sc = xsc; + + if (type) + printf("tl%d: adapter check: %x\n", sc->tl_unit, + (unsigned int)CSR_READ_4(sc, TL_CH_PARM)); + + tl_softreset(sc, 1); + tl_stop(sc); + tl_init(sc); + CMD_SET(sc, TL_CMD_INTSON); + + return(0); +} + +static int tl_intvec_netsts(xsc, type) + void *xsc; + u_int32_t type; +{ + struct tl_softc *sc; + u_int16_t netsts; + + sc = xsc; + + netsts = tl_dio_read16(sc, TL_NETSTS); + tl_dio_write16(sc, TL_NETSTS, netsts); + + printf("tl%d: network status: %x\n", sc->tl_unit, netsts); + + return(1); +} + +static void tl_intr(xsc) + void *xsc; +{ + struct tl_softc *sc; + struct ifnet *ifp; + int r = 0; + u_int32_t type = 0; + u_int16_t ints = 0; + u_int8_t ivec = 0; + + sc = xsc; + TL_LOCK(sc); + + /* Disable interrupts */ + ints = CSR_READ_2(sc, TL_HOST_INT); + CSR_WRITE_2(sc, TL_HOST_INT, ints); + type = (ints << 16) & 0xFFFF0000; + ivec = (ints & TL_VEC_MASK) >> 5; + ints = (ints & TL_INT_MASK) >> 2; + + ifp = &sc->arpcom.ac_if; + + switch(ints) { + case (TL_INTR_INVALID): +#ifdef DIAGNOSTIC + printf("tl%d: got an invalid interrupt!\n", sc->tl_unit); +#endif + /* Re-enable interrupts but don't ack this one. */ + CMD_PUT(sc, type); + r = 0; + break; + case (TL_INTR_TXEOF): + r = tl_intvec_txeof((void *)sc, type); + break; + case (TL_INTR_TXEOC): + r = tl_intvec_txeoc((void *)sc, type); + break; + case (TL_INTR_STATOFLOW): + tl_stats_update(sc); + r = 1; + break; + case (TL_INTR_RXEOF): + r = tl_intvec_rxeof((void *)sc, type); + break; + case (TL_INTR_DUMMY): + printf("tl%d: got a dummy interrupt\n", sc->tl_unit); + r = 1; + break; + case (TL_INTR_ADCHK): + if (ivec) + r = tl_intvec_adchk((void *)sc, type); + else + r = tl_intvec_netsts((void *)sc, type); + break; + case (TL_INTR_RXEOC): + r = tl_intvec_rxeoc((void *)sc, type); + break; + default: + printf("tl%d: bogus interrupt type\n", ifp->if_unit); + break; + } + + /* Re-enable interrupts */ + if (r) { + CMD_PUT(sc, TL_CMD_ACK | r | type); + } + + if (ifp->if_snd.ifq_head != NULL) + tl_start(ifp); + + TL_UNLOCK(sc); + + return; +} + +static void tl_stats_update(xsc) + void *xsc; +{ + struct tl_softc *sc; + struct ifnet *ifp; + struct tl_stats tl_stats; + struct mii_data *mii; + u_int32_t *p; + + bzero((char *)&tl_stats, sizeof(struct tl_stats)); + + sc = xsc; + TL_LOCK(sc); + ifp = &sc->arpcom.ac_if; + + p = (u_int32_t *)&tl_stats; + + CSR_WRITE_2(sc, TL_DIO_ADDR, TL_TXGOODFRAMES|TL_DIO_ADDR_INC); + *p++ = CSR_READ_4(sc, TL_DIO_DATA); + *p++ = CSR_READ_4(sc, TL_DIO_DATA); + *p++ = CSR_READ_4(sc, TL_DIO_DATA); + *p++ = CSR_READ_4(sc, TL_DIO_DATA); + *p++ = CSR_READ_4(sc, TL_DIO_DATA); + + ifp->if_opackets += tl_tx_goodframes(tl_stats); + ifp->if_collisions += tl_stats.tl_tx_single_collision + + tl_stats.tl_tx_multi_collision; + ifp->if_ipackets += tl_rx_goodframes(tl_stats); + ifp->if_ierrors += tl_stats.tl_crc_errors + tl_stats.tl_code_errors + + tl_rx_overrun(tl_stats); + ifp->if_oerrors += tl_tx_underrun(tl_stats); + + if (tl_tx_underrun(tl_stats)) { + u_int8_t tx_thresh; + tx_thresh = tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_TXTHRESH; + if (tx_thresh != TL_AC_TXTHRESH_WHOLEPKT) { + tx_thresh >>= 4; + tx_thresh++; + printf("tl%d: tx underrun -- increasing " + "tx threshold to %d bytes\n", sc->tl_unit, + (64 * (tx_thresh * 4))); + tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH); + tl_dio_setbit(sc, TL_ACOMMIT, tx_thresh << 4); + } + } + + sc->tl_stat_ch = timeout(tl_stats_update, sc, hz); + + if (!sc->tl_bitrate) { + mii = device_get_softc(sc->tl_miibus); + mii_tick(mii); + } + + TL_UNLOCK(sc); + + return; +} + +/* + * Encapsulate an mbuf chain in a list by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int tl_encap(sc, c, m_head) + struct tl_softc *sc; + struct tl_chain *c; + struct mbuf *m_head; +{ + int frag = 0; + struct tl_frag *f = NULL; + int total_len; + struct mbuf *m; + + /* + * Start packing the mbufs in this chain into + * the fragment pointers. Stop when we run out + * of fragments or hit the end of the mbuf chain. + */ + m = m_head; + total_len = 0; + + for (m = m_head, frag = 0; m != NULL; m = m->m_next) { + if (m->m_len != 0) { + if (frag == TL_MAXFRAGS) + break; + total_len+= m->m_len; + c->tl_ptr->tl_frag[frag].tlist_dadr = + vtophys(mtod(m, vm_offset_t)); + c->tl_ptr->tl_frag[frag].tlist_dcnt = m->m_len; + frag++; + } + } + + /* + * Handle special cases. + * Special case #1: we used up all 10 fragments, but + * we have more mbufs left in the chain. Copy the + * data into an mbuf cluster. Note that we don't + * bother clearing the values in the other fragment + * pointers/counters; it wouldn't gain us anything, + * and would waste cycles. + */ + if (m != NULL) { + struct mbuf *m_new = NULL; + + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) { + printf("tl%d: no memory for tx list\n", sc->tl_unit); + return(1); + } + if (m_head->m_pkthdr.len > MHLEN) { + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + printf("tl%d: no memory for tx list\n", + sc->tl_unit); + return(1); + } + } + m_copydata(m_head, 0, m_head->m_pkthdr.len, + mtod(m_new, caddr_t)); + m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; + m_freem(m_head); + m_head = m_new; + f = &c->tl_ptr->tl_frag[0]; + f->tlist_dadr = vtophys(mtod(m_new, caddr_t)); + f->tlist_dcnt = total_len = m_new->m_len; + frag = 1; + } + + /* + * Special case #2: the frame is smaller than the minimum + * frame size. We have to pad it to make the chip happy. + */ + if (total_len < TL_MIN_FRAMELEN) { + if (frag == TL_MAXFRAGS) + printf("tl%d: all frags filled but " + "frame still to small!\n", sc->tl_unit); + f = &c->tl_ptr->tl_frag[frag]; + f->tlist_dcnt = TL_MIN_FRAMELEN - total_len; + f->tlist_dadr = vtophys(&sc->tl_ldata->tl_pad); + total_len += f->tlist_dcnt; + frag++; + } + + c->tl_mbuf = m_head; + c->tl_ptr->tl_frag[frag - 1].tlist_dcnt |= TL_LAST_FRAG; + c->tl_ptr->tlist_frsize = total_len; + c->tl_ptr->tlist_cstat = TL_CSTAT_READY; + c->tl_ptr->tlist_fptr = 0; + + return(0); +} + +/* + * Main transmit routine. To avoid having to do mbuf copies, we put pointers + * to the mbuf data regions directly in the transmit lists. We also save a + * copy of the pointers since the transmit list fragment pointers are + * physical addresses. + */ +static void tl_start(ifp) + struct ifnet *ifp; +{ + struct tl_softc *sc; + struct mbuf *m_head = NULL; + u_int32_t cmd; + struct tl_chain *prev = NULL, *cur_tx = NULL, *start_tx; + + sc = ifp->if_softc; + TL_LOCK(sc); + + /* + * Check for an available queue slot. If there are none, + * punt. + */ + if (sc->tl_cdata.tl_tx_free == NULL) { + ifp->if_flags |= IFF_OACTIVE; + TL_UNLOCK(sc); + return; + } + + start_tx = sc->tl_cdata.tl_tx_free; + + while(sc->tl_cdata.tl_tx_free != NULL) { + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + /* Pick a chain member off the free list. */ + cur_tx = sc->tl_cdata.tl_tx_free; + sc->tl_cdata.tl_tx_free = cur_tx->tl_next; + + cur_tx->tl_next = NULL; + + /* Pack the data into the list. */ + tl_encap(sc, cur_tx, m_head); + + /* Chain it together */ + if (prev != NULL) { + prev->tl_next = cur_tx; + prev->tl_ptr->tlist_fptr = vtophys(cur_tx->tl_ptr); + } + prev = cur_tx; + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, cur_tx->tl_mbuf); + } + + /* + * If there are no packets queued, bail. + */ + if (cur_tx == NULL) { + TL_UNLOCK(sc); + return; + } + + /* + * That's all we can stands, we can't stands no more. + * If there are no other transfers pending, then issue the + * TX GO command to the adapter to start things moving. + * Otherwise, just leave the data in the queue and let + * the EOF/EOC interrupt handler send. + */ + if (sc->tl_cdata.tl_tx_head == NULL) { + sc->tl_cdata.tl_tx_head = start_tx; + sc->tl_cdata.tl_tx_tail = cur_tx; + + if (sc->tl_txeoc) { + sc->tl_txeoc = 0; + CSR_WRITE_4(sc, TL_CH_PARM, vtophys(start_tx->tl_ptr)); + cmd = CSR_READ_4(sc, TL_HOSTCMD); + cmd &= ~TL_CMD_RT; + cmd |= TL_CMD_GO|TL_CMD_INTSON; + CMD_PUT(sc, cmd); + } + } else { + sc->tl_cdata.tl_tx_tail->tl_next = start_tx; + sc->tl_cdata.tl_tx_tail = cur_tx; + } + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + TL_UNLOCK(sc); + + return; +} + +static void tl_init(xsc) + void *xsc; +{ + struct tl_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mii_data *mii; + + TL_LOCK(sc); + + ifp = &sc->arpcom.ac_if; + + /* + * Cancel pending I/O. + */ + tl_stop(sc); + + /* Initialize TX FIFO threshold */ + tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH); + tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH_16LONG); + + /* Set PCI burst size */ + tl_dio_write8(sc, TL_BSIZEREG, TL_RXBURST_16LONG|TL_TXBURST_16LONG); + + /* + * Set 'capture all frames' bit for promiscuous mode. + */ + if (ifp->if_flags & IFF_PROMISC) + tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); + else + tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); + + /* + * Set capture broadcast bit to capture broadcast frames. + */ + if (ifp->if_flags & IFF_BROADCAST) + tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_NOBRX); + else + tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NOBRX); + + tl_dio_write16(sc, TL_MAXRX, MCLBYTES); + + /* Init our MAC address */ + tl_setfilt(sc, (caddr_t)&sc->arpcom.ac_enaddr, 0); + + /* Init multicast filter, if needed. */ + tl_setmulti(sc); + + /* Init circular RX list. */ + if (tl_list_rx_init(sc) == ENOBUFS) { + printf("tl%d: initialization failed: no " + "memory for rx buffers\n", sc->tl_unit); + tl_stop(sc); + TL_UNLOCK(sc); + return; + } + + /* Init TX pointers. */ + tl_list_tx_init(sc); + + /* Enable PCI interrupts. */ + CMD_SET(sc, TL_CMD_INTSON); + + /* Load the address of the rx list */ + CMD_SET(sc, TL_CMD_RT); + CSR_WRITE_4(sc, TL_CH_PARM, vtophys(&sc->tl_ldata->tl_rx_list[0])); + + if (!sc->tl_bitrate) { + if (sc->tl_miibus != NULL) { + mii = device_get_softc(sc->tl_miibus); + mii_mediachg(mii); + } + } + + /* Send the RX go command */ + CMD_SET(sc, TL_CMD_GO|TL_CMD_NES|TL_CMD_RT); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + /* Start the stats update counter */ + sc->tl_stat_ch = timeout(tl_stats_update, sc, hz); + TL_UNLOCK(sc); + + return; +} + +/* + * Set media options. + */ +static int tl_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct tl_softc *sc; + struct mii_data *mii = NULL; + + sc = ifp->if_softc; + + if (sc->tl_bitrate) + tl_setmode(sc, sc->ifmedia.ifm_media); + else { + mii = device_get_softc(sc->tl_miibus); + mii_mediachg(mii); + } + + return(0); +} + +/* + * Report current media status. + */ +static void tl_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct tl_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + + ifmr->ifm_active = IFM_ETHER; + + if (sc->tl_bitrate) { + if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD1) + ifmr->ifm_active = IFM_ETHER|IFM_10_5; + else + ifmr->ifm_active = IFM_ETHER|IFM_10_T; + if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD3) + ifmr->ifm_active |= IFM_HDX; + else + ifmr->ifm_active |= IFM_FDX; + return; + } else { + mii = device_get_softc(sc->tl_miibus); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + } + + return; +} + +static int tl_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct tl_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + int s, error = 0; + + s = splimp(); + + switch(command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING && + ifp->if_flags & IFF_PROMISC && + !(sc->tl_if_flags & IFF_PROMISC)) { + tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); + tl_setmulti(sc); + } else if (ifp->if_flags & IFF_RUNNING && + !(ifp->if_flags & IFF_PROMISC) && + sc->tl_if_flags & IFF_PROMISC) { + tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); + tl_setmulti(sc); + } else + tl_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) { + tl_stop(sc); + } + } + sc->tl_if_flags = ifp->if_flags; + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + tl_setmulti(sc); + error = 0; + break; + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + if (sc->tl_bitrate) + error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); + else { + struct mii_data *mii; + mii = device_get_softc(sc->tl_miibus); + error = ifmedia_ioctl(ifp, ifr, + &mii->mii_media, command); + } + break; + default: + error = EINVAL; + break; + } + + (void)splx(s); + + return(error); +} + +static void tl_watchdog(ifp) + struct ifnet *ifp; +{ + struct tl_softc *sc; + + sc = ifp->if_softc; + + printf("tl%d: device timeout\n", sc->tl_unit); + + ifp->if_oerrors++; + + tl_softreset(sc, 1); + tl_init(sc); + + return; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void tl_stop(sc) + struct tl_softc *sc; +{ + register int i; + struct ifnet *ifp; + + TL_LOCK(sc); + + ifp = &sc->arpcom.ac_if; + + /* Stop the stats updater. */ + untimeout(tl_stats_update, sc, sc->tl_stat_ch); + + /* Stop the transmitter */ + CMD_CLR(sc, TL_CMD_RT); + CMD_SET(sc, TL_CMD_STOP); + CSR_WRITE_4(sc, TL_CH_PARM, 0); + + /* Stop the receiver */ + CMD_SET(sc, TL_CMD_RT); + CMD_SET(sc, TL_CMD_STOP); + CSR_WRITE_4(sc, TL_CH_PARM, 0); + + /* + * Disable host interrupts. + */ + CMD_SET(sc, TL_CMD_INTSOFF); + + /* + * Clear list pointer. + */ + CSR_WRITE_4(sc, TL_CH_PARM, 0); + + /* + * Free the RX lists. + */ + for (i = 0; i < TL_RX_LIST_CNT; i++) { + if (sc->tl_cdata.tl_rx_chain[i].tl_mbuf != NULL) { + m_freem(sc->tl_cdata.tl_rx_chain[i].tl_mbuf); + sc->tl_cdata.tl_rx_chain[i].tl_mbuf = NULL; + } + } + bzero((char *)&sc->tl_ldata->tl_rx_list, + sizeof(sc->tl_ldata->tl_rx_list)); + + /* + * Free the TX list buffers. + */ + for (i = 0; i < TL_TX_LIST_CNT; i++) { + if (sc->tl_cdata.tl_tx_chain[i].tl_mbuf != NULL) { + m_freem(sc->tl_cdata.tl_tx_chain[i].tl_mbuf); + sc->tl_cdata.tl_tx_chain[i].tl_mbuf = NULL; + } + } + bzero((char *)&sc->tl_ldata->tl_tx_list, + sizeof(sc->tl_ldata->tl_tx_list)); + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + TL_UNLOCK(sc); + + return; +} + +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static void tl_shutdown(dev) + device_t dev; +{ + struct tl_softc *sc; + + sc = device_get_softc(dev); + + tl_stop(sc); + + return; +} diff --git a/sys/pci/if_tlreg.h b/sys/pci/if_tlreg.h new file mode 100644 index 0000000..bd7ea5b --- /dev/null +++ b/sys/pci/if_tlreg.h @@ -0,0 +1,596 @@ +/* + * Copyright (c) 1997, 1998 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + + +struct tl_type { + u_int16_t tl_vid; + u_int16_t tl_did; + char *tl_name; +}; + +/* + * ThunderLAN TX/RX list format. The TX and RX lists are pretty much + * identical: the list begins with a 32-bit forward pointer which points + * at the next list in the chain, followed by 16 bits for the total + * frame size, and a 16 bit status field. This is followed by a series + * of 10 32-bit data count/data address pairs that point to the fragments + * that make up the complete frame. + */ + +#define TL_MAXFRAGS 10 +#define TL_RX_LIST_CNT 64 +#define TL_TX_LIST_CNT 128 +#define TL_MIN_FRAMELEN 64 + +struct tl_frag { + u_int32_t tlist_dcnt; + u_int32_t tlist_dadr; +}; + +struct tl_list { + u_int32_t tlist_fptr; /* phys address of next list */ + u_int16_t tlist_cstat; /* status word */ + u_int16_t tlist_frsize; /* size of data in frame */ + struct tl_frag tl_frag[TL_MAXFRAGS]; +}; + +/* + * This is a special case of an RX list. By setting the One_Frag + * bit in the NETCONFIG register, the driver can force the ThunderLAN + * chip to use only one fragment when DMAing RX frames. + */ + +struct tl_list_onefrag { + u_int32_t tlist_fptr; + u_int16_t tlist_cstat; + u_int16_t tlist_frsize; + struct tl_frag tl_frag; +}; + +struct tl_list_data { + struct tl_list_onefrag tl_rx_list[TL_RX_LIST_CNT]; + struct tl_list tl_tx_list[TL_TX_LIST_CNT]; + unsigned char tl_pad[TL_MIN_FRAMELEN]; +}; + +struct tl_chain { + struct tl_list *tl_ptr; + struct mbuf *tl_mbuf; + struct tl_chain *tl_next; +}; + +struct tl_chain_onefrag { + struct tl_list_onefrag *tl_ptr; + struct mbuf *tl_mbuf; + struct tl_chain_onefrag *tl_next; +}; + +struct tl_chain_data { + struct tl_chain_onefrag tl_rx_chain[TL_RX_LIST_CNT]; + struct tl_chain tl_tx_chain[TL_TX_LIST_CNT]; + + struct tl_chain_onefrag *tl_rx_head; + struct tl_chain_onefrag *tl_rx_tail; + + struct tl_chain *tl_tx_head; + struct tl_chain *tl_tx_tail; + struct tl_chain *tl_tx_free; +}; + +struct tl_softc { + struct arpcom arpcom; /* interface info */ + struct ifmedia ifmedia; /* media info */ + bus_space_handle_t tl_bhandle; + bus_space_tag_t tl_btag; + void *tl_intrhand; + struct resource *tl_irq; + struct resource *tl_res; + device_t tl_miibus; + struct tl_type *tl_dinfo; /* ThunderLAN adapter info */ + u_int8_t tl_unit; /* interface number */ + u_int8_t tl_eeaddr; + struct tl_list_data *tl_ldata; /* TX/RX lists and mbufs */ + struct tl_chain_data tl_cdata; + u_int8_t tl_txeoc; + u_int8_t tl_bitrate; + int tl_if_flags; + struct callout_handle tl_stat_ch; + struct mtx tl_mtx; +}; + +#define TL_LOCK(_sc) mtx_lock(&(_sc)->tl_mtx) +#define TL_UNLOCK(_sc) mtx_unlock(&(_sc)->tl_mtx) + +/* + * Transmit interrupt threshold. + */ +#define TX_THR 0x00000004 + +/* + * General constants that are fun to know. + * + * The ThunderLAN controller is made by Texas Instruments. The + * manual indicates that if the EEPROM checksum fails, the PCI + * vendor and device ID registers will be loaded with TI-specific + * values. + */ +#define TI_VENDORID 0x104C +#define TI_DEVICEID_THUNDERLAN 0x0500 + +/* + * These are the PCI vendor and device IDs for Compaq ethernet + * adapters based on the ThunderLAN controller. + */ +#define COMPAQ_VENDORID 0x0E11 +#define COMPAQ_DEVICEID_NETEL_10_100 0xAE32 +#define COMPAQ_DEVICEID_NETEL_UNKNOWN 0xAE33 +#define COMPAQ_DEVICEID_NETEL_10 0xAE34 +#define COMPAQ_DEVICEID_NETFLEX_3P_INTEGRATED 0xAE35 +#define COMPAQ_DEVICEID_NETEL_10_100_DUAL 0xAE40 +#define COMPAQ_DEVICEID_NETEL_10_100_PROLIANT 0xAE43 +#define COMPAQ_DEVICEID_NETEL_10_100_EMBEDDED 0xB011 +#define COMPAQ_DEVICEID_NETEL_10_T2_UTP_COAX 0xB012 +#define COMPAQ_DEVICEID_NETEL_10_100_TX_UTP 0xB030 +#define COMPAQ_DEVICEID_NETFLEX_3P 0xF130 +#define COMPAQ_DEVICEID_NETFLEX_3P_BNC 0xF150 + +/* + * These are the PCI vendor and device IDs for Olicom + * adapters based on the ThunderLAN controller. + */ +#define OLICOM_VENDORID 0x108D +#define OLICOM_DEVICEID_OC2183 0x0013 +#define OLICOM_DEVICEID_OC2325 0x0012 +#define OLICOM_DEVICEID_OC2326 0x0014 + +/* + * PCI low memory base and low I/O base + */ +#define TL_PCI_LOIO 0x10 +#define TL_PCI_LOMEM 0x14 + +/* + * PCI latency timer (it's actually 0x0D, but we want a value + * that's longword aligned). + */ +#define TL_PCI_LATENCY_TIMER 0x0C + +#define TL_DIO_ADDR_INC 0x8000 /* Increment addr on each read */ +#define TL_DIO_RAM_SEL 0x4000 /* RAM address select */ +#define TL_DIO_ADDR_MASK 0x3FFF /* address bits mask */ + +/* + * Interrupt types + */ +#define TL_INTR_INVALID 0x0 +#define TL_INTR_TXEOF 0x1 +#define TL_INTR_STATOFLOW 0x2 +#define TL_INTR_RXEOF 0x3 +#define TL_INTR_DUMMY 0x4 +#define TL_INTR_TXEOC 0x5 +#define TL_INTR_ADCHK 0x6 +#define TL_INTR_RXEOC 0x7 + +#define TL_INT_MASK 0x001C +#define TL_VEC_MASK 0x1FE0 +/* + * Host command register bits + */ +#define TL_CMD_GO 0x80000000 +#define TL_CMD_STOP 0x40000000 +#define TL_CMD_ACK 0x20000000 +#define TL_CMD_CHSEL7 0x10000000 +#define TL_CMD_CHSEL6 0x08000000 +#define TL_CMD_CHSEL5 0x04000000 +#define TL_CMD_CHSEL4 0x02000000 +#define TL_CMD_CHSEL3 0x01000000 +#define TL_CMD_CHSEL2 0x00800000 +#define TL_CMD_CHSEL1 0x00400000 +#define TL_CMD_CHSEL0 0x00200000 +#define TL_CMD_EOC 0x00100000 +#define TL_CMD_RT 0x00080000 +#define TL_CMD_NES 0x00040000 +#define TL_CMD_ZERO0 0x00020000 +#define TL_CMD_ZERO1 0x00010000 +#define TL_CMD_ADRST 0x00008000 +#define TL_CMD_LDTMR 0x00004000 +#define TL_CMD_LDTHR 0x00002000 +#define TL_CMD_REQINT 0x00001000 +#define TL_CMD_INTSOFF 0x00000800 +#define TL_CMD_INTSON 0x00000400 +#define TL_CMD_RSVD0 0x00000200 +#define TL_CMD_RSVD1 0x00000100 +#define TL_CMD_ACK7 0x00000080 +#define TL_CMD_ACK6 0x00000040 +#define TL_CMD_ACK5 0x00000020 +#define TL_CMD_ACK4 0x00000010 +#define TL_CMD_ACK3 0x00000008 +#define TL_CMD_ACK2 0x00000004 +#define TL_CMD_ACK1 0x00000002 +#define TL_CMD_ACK0 0x00000001 + +#define TL_CMD_CHSEL_MASK 0x01FE0000 +#define TL_CMD_ACK_MASK 0xFF + +/* + * EEPROM address where station address resides. + */ +#define TL_EEPROM_EADDR 0x83 +#define TL_EEPROM_EADDR2 0x99 +#define TL_EEPROM_EADDR3 0xAF +#define TL_EEPROM_EADDR_OC 0xF8 /* Olicom cards use a different + address than Compaqs. */ +/* + * ThunderLAN host command register offsets. + * (Can be accessed either by IO ports or memory map.) + */ +#define TL_HOSTCMD 0x00 +#define TL_CH_PARM 0x04 +#define TL_DIO_ADDR 0x08 +#define TL_HOST_INT 0x0A +#define TL_DIO_DATA 0x0C + +/* + * ThunderLAN internal registers + */ +#define TL_NETCMD 0x00 +#define TL_NETSIO 0x01 +#define TL_NETSTS 0x02 +#define TL_NETMASK 0x03 + +#define TL_NETCONFIG 0x04 +#define TL_MANTEST 0x06 + +#define TL_VENID_LSB 0x08 +#define TL_VENID_MSB 0x09 +#define TL_DEVID_LSB 0x0A +#define TL_DEVID_MSB 0x0B + +#define TL_REVISION 0x0C +#define TL_SUBCLASS 0x0D +#define TL_MINLAT 0x0E +#define TL_MAXLAT 0x0F + +#define TL_AREG0_B5 0x10 +#define TL_AREG0_B4 0x11 +#define TL_AREG0_B3 0x12 +#define TL_AREG0_B2 0x13 + +#define TL_AREG0_B1 0x14 +#define TL_AREG0_B0 0x15 +#define TL_AREG1_B5 0x16 +#define TL_AREG1_B4 0x17 + +#define TL_AREG1_B3 0x18 +#define TL_AREG1_B2 0x19 +#define TL_AREG1_B1 0x1A +#define TL_AREG1_B0 0x1B + +#define TL_AREG2_B5 0x1C +#define TL_AREG2_B4 0x1D +#define TL_AREG2_B3 0x1E +#define TL_AREG2_B2 0x1F + +#define TL_AREG2_B1 0x20 +#define TL_AREG2_B0 0x21 +#define TL_AREG3_B5 0x22 +#define TL_AREG3_B4 0x23 + +#define TL_AREG3_B3 0x24 +#define TL_AREG3_B2 0x25 +#define TL_AREG3_B1 0x26 +#define TL_AREG3_B0 0x27 + +#define TL_HASH1 0x28 +#define TL_HASH2 0x2C +#define TL_TXGOODFRAMES 0x30 +#define TL_TXUNDERRUN 0x33 +#define TL_RXGOODFRAMES 0x34 +#define TL_RXOVERRUN 0x37 +#define TL_DEFEREDTX 0x38 +#define TL_CRCERROR 0x3A +#define TL_CODEERROR 0x3B +#define TL_MULTICOLTX 0x3C +#define TL_SINGLECOLTX 0x3E +#define TL_EXCESSIVECOL 0x40 +#define TL_LATECOL 0x41 +#define TL_CARRIERLOSS 0x42 +#define TL_ACOMMIT 0x43 +#define TL_LDREG 0x44 +#define TL_BSIZEREG 0x45 +#define TL_MAXRX 0x46 + +/* + * ThunderLAN SIO register bits + */ +#define TL_SIO_MINTEN 0x80 +#define TL_SIO_ECLOK 0x40 +#define TL_SIO_ETXEN 0x20 +#define TL_SIO_EDATA 0x10 +#define TL_SIO_NMRST 0x08 +#define TL_SIO_MCLK 0x04 +#define TL_SIO_MTXEN 0x02 +#define TL_SIO_MDATA 0x01 + +/* + * Thunderlan NETCONFIG bits + */ +#define TL_CFG_RCLKTEST 0x8000 +#define TL_CFG_TCLKTEST 0x4000 +#define TL_CFG_BITRATE 0x2000 +#define TL_CFG_RXCRC 0x1000 +#define TL_CFG_PEF 0x0800 +#define TL_CFG_ONEFRAG 0x0400 +#define TL_CFG_ONECHAN 0x0200 +#define TL_CFG_MTEST 0x0100 +#define TL_CFG_PHYEN 0x0080 +#define TL_CFG_MACSEL6 0x0040 +#define TL_CFG_MACSEL5 0x0020 +#define TL_CFG_MACSEL4 0x0010 +#define TL_CFG_MACSEL3 0x0008 +#define TL_CFG_MACSEL2 0x0004 +#define TL_CFG_MACSEL1 0x0002 +#define TL_CFG_MACSEL0 0x0001 + +/* + * ThunderLAN NETSTS bits + */ +#define TL_STS_MIRQ 0x80 +#define TL_STS_HBEAT 0x40 +#define TL_STS_TXSTOP 0x20 +#define TL_STS_RXSTOP 0x10 + +/* + * ThunderLAN NETCMD bits + */ +#define TL_CMD_NRESET 0x80 +#define TL_CMD_NWRAP 0x40 +#define TL_CMD_CSF 0x20 +#define TL_CMD_CAF 0x10 +#define TL_CMD_NOBRX 0x08 +#define TL_CMD_DUPLEX 0x04 +#define TL_CMD_TRFRAM 0x02 +#define TL_CMD_TXPACE 0x01 + +/* + * ThunderLAN NETMASK bits + */ +#define TL_MASK_MASK7 0x80 +#define TL_MASK_MASK6 0x40 +#define TL_MASK_MASK5 0x20 +#define TL_MASK_MASK4 0x10 + +/* + * MII frame format + */ +#ifdef ANSI_DOESNT_ALLOW_BITFIELDS +struct tl_mii_frame { + u_int16_t mii_stdelim:2, + mii_opcode:2, + mii_phyaddr:5, + mii_regaddr:5, + mii_turnaround:2; + u_int16_t mii_data; +}; +#else +struct tl_mii_frame { + u_int8_t mii_stdelim; + u_int8_t mii_opcode; + u_int8_t mii_phyaddr; + u_int8_t mii_regaddr; + u_int8_t mii_turnaround; + u_int16_t mii_data; +}; +#endif +/* + * MII constants + */ +#define TL_MII_STARTDELIM 0x01 +#define TL_MII_READOP 0x02 +#define TL_MII_WRITEOP 0x01 +#define TL_MII_TURNAROUND 0x02 + +#define TL_LAST_FRAG 0x80000000 +#define TL_CSTAT_UNUSED 0x8000 +#define TL_CSTAT_FRAMECMP 0x4000 +#define TL_CSTAT_READY 0x3000 +#define TL_CSTAT_UNUSED13 0x2000 +#define TL_CSTAT_UNUSED12 0x1000 +#define TL_CSTAT_EOC 0x0800 +#define TL_CSTAT_RXERROR 0x0400 +#define TL_CSTAT_PASSCRC 0x0200 +#define TL_CSTAT_DPRIO 0x0100 + +#define TL_FRAME_MASK 0x00FFFFFF +#define tl_tx_goodframes(x) (x.tl_txstat & TL_FRAME_MASK) +#define tl_tx_underrun(x) ((x.tl_txstat & ~TL_FRAME_MASK) >> 24) +#define tl_rx_goodframes(x) (x.tl_rxstat & TL_FRAME_MASK) +#define tl_rx_overrun(x) ((x.tl_rxstat & ~TL_FRAME_MASK) >> 24) + +struct tl_stats { + u_int32_t tl_txstat; + u_int32_t tl_rxstat; + u_int16_t tl_deferred; + u_int8_t tl_crc_errors; + u_int8_t tl_code_errors; + u_int16_t tl_tx_multi_collision; + u_int16_t tl_tx_single_collision; + u_int8_t tl_excessive_collision; + u_int8_t tl_late_collision; + u_int8_t tl_carrier_loss; + u_int8_t acommit; +}; + +/* + * ACOMMIT register bits. These are used only when a bitrate + * PHY is selected ('bitrate' bit in netconfig register is set). + */ +#define TL_AC_MTXER 0x01 /* reserved */ +#define TL_AC_MTXD1 0x02 /* 0 == 10baseT 1 == AUI */ +#define TL_AC_MTXD2 0x04 /* loopback disable */ +#define TL_AC_MTXD3 0x08 /* full duplex disable */ + +#define TL_AC_TXTHRESH 0xF0 +#define TL_AC_TXTHRESH_16LONG 0x00 +#define TL_AC_TXTHRESH_32LONG 0x10 +#define TL_AC_TXTHRESH_64LONG 0x20 +#define TL_AC_TXTHRESH_128LONG 0x30 +#define TL_AC_TXTHRESH_256LONG 0x40 +#define TL_AC_TXTHRESH_WHOLEPKT 0x50 + +/* + * PCI burst size register (TL_BSIZEREG). + */ +#define TL_RXBURST 0x0F +#define TL_TXBURST 0xF0 + +#define TL_RXBURST_4LONG 0x00 +#define TL_RXBURST_8LONG 0x01 +#define TL_RXBURST_16LONG 0x02 +#define TL_RXBURST_32LONG 0x03 +#define TL_RXBURST_64LONG 0x04 +#define TL_RXBURST_128LONG 0x05 + +#define TL_TXBURST_4LONG 0x00 +#define TL_TXBURST_8LONG 0x10 +#define TL_TXBURST_16LONG 0x20 +#define TL_TXBURST_32LONG 0x30 +#define TL_TXBURST_64LONG 0x40 +#define TL_TXBURST_128LONG 0x50 + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->tl_btag, sc->tl_bhandle, reg, val) +#define CSR_WRITE_2(sc, reg, val) \ + bus_space_write_2(sc->tl_btag, sc->tl_bhandle, reg, val) +#define CSR_WRITE_1(sc, reg, val) \ + bus_space_write_1(sc->tl_btag, sc->tl_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->tl_btag, sc->tl_bhandle, reg) +#define CSR_READ_2(sc, reg) \ + bus_space_read_2(sc->tl_btag, sc->tl_bhandle, reg) +#define CSR_READ_1(sc, reg) \ + bus_space_read_1(sc->tl_btag, sc->tl_bhandle, reg) + +#define CMD_PUT(sc, x) CSR_WRITE_4(sc, TL_HOSTCMD, x) +#define CMD_SET(sc, x) \ + CSR_WRITE_4(sc, TL_HOSTCMD, CSR_READ_4(sc, TL_HOSTCMD) | (x)) +#define CMD_CLR(sc, x) \ + CSR_WRITE_4(sc, TL_HOSTCMD, CSR_READ_4(sc, TL_HOSTCMD) & ~(x)) + +/* + * ThunderLAN adapters typically have a serial EEPROM containing + * configuration information. The main reason we're interested in + * it is because it also contains the adapters's station address. + * + * Access to the EEPROM is a bit goofy since it is a serial device: + * you have to do reads and writes one bit at a time. The state of + * the DATA bit can only change while the CLOCK line is held low. + * Transactions work basically like this: + * + * 1) Send the EEPROM_START sequence to prepare the EEPROM for + * accepting commands. This pulls the clock high, sets + * the data bit to 0, enables transmission to the EEPROM, + * pulls the data bit up to 1, then pulls the clock low. + * The idea is to do a 0 to 1 transition of the data bit + * while the clock pin is held high. + * + * 2) To write a bit to the EEPROM, set the TXENABLE bit, then + * set the EDATA bit to send a 1 or clear it to send a 0. + * Finally, set and then clear ECLOK. Strobing the clock + * transmits the bit. After 8 bits have been written, the + * EEPROM should respond with an ACK, which should be read. + * + * 3) To read a bit from the EEPROM, clear the TXENABLE bit, + * then set ECLOK. The bit can then be read by reading EDATA. + * ECLOCK should then be cleared again. This can be repeated + * 8 times to read a whole byte, after which the + * + * 4) We need to send the address byte to the EEPROM. For this + * we have to send the write control byte to the EEPROM to + * tell it to accept data. The byte is 0xA0. The EEPROM should + * ack this. The address byte can be send after that. + * + * 5) Now we have to tell the EEPROM to send us data. For that we + * have to transmit the read control byte, which is 0xA1. This + * byte should also be acked. We can then read the data bits + * from the EEPROM. + * + * 6) When we're all finished, send the EEPROM_STOP sequence. + * + * Note that we use the ThunderLAN's NetSio register to access the + * EEPROM, however there is an alternate method. There is a PCI NVRAM + * register at PCI offset 0xB4 which can also be used with minor changes. + * The difference is that access to PCI registers via pci_conf_read() + * and pci_conf_write() is done using programmed I/O, which we want to + * avoid. + */ + +/* + * Note that EEPROM_START leaves transmission enabled. + */ +#define EEPROM_START \ + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); /* Pull clock pin high */\ + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_EDATA); /* Set DATA bit to 1 */ \ + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ETXEN); /* Enable xmit to write bit */\ + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_EDATA); /* Pull DATA bit to 0 again */\ + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); /* Pull clock low again */ + +/* + * EEPROM_STOP ends access to the EEPROM and clears the ETXEN bit so + * that no further data can be written to the EEPROM I/O pin. + */ +#define EEPROM_STOP \ + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); /* Disable xmit */ \ + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_EDATA); /* Pull DATA to 0 */ \ + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); /* Pull clock high */ \ + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ETXEN); /* Enable xmit */ \ + tl_dio_setbit(sc, TL_NETSIO, TL_SIO_EDATA); /* Toggle DATA to 1 */ \ + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); /* Disable xmit. */ \ + tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); /* Pull clock low again */ + + +/* + * Microchip Technology 24Cxx EEPROM control bytes + */ +#define EEPROM_CTL_READ 0xA1 /* 0101 0001 */ +#define EEPROM_CTL_WRITE 0xA0 /* 0101 0000 */ + +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) +#endif diff --git a/sys/pci/if_vr.c b/sys/pci/if_vr.c new file mode 100644 index 0000000..1f4a07e --- /dev/null +++ b/sys/pci/if_vr.c @@ -0,0 +1,1650 @@ +/* + * Copyright (c) 1997, 1998 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * VIA Rhine fast ethernet PCI NIC driver + * + * Supports various network adapters based on the VIA Rhine + * and Rhine II PCI controllers, including the D-Link DFE530TX. + * Datasheets are available at http://www.via.com.tw. + * + * Written by Bill Paul <wpaul@ctr.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * The VIA Rhine controllers are similar in some respects to the + * the DEC tulip chips, except less complicated. The controller + * uses an MII bus and an external physical layer interface. The + * receiver has a one entry perfect filter and a 64-bit hash table + * multicast filter. Transmit and receive descriptors are similar + * to the tulip. + * + * The Rhine has a serious flaw in its transmit DMA mechanism: + * transmit buffers must be longword aligned. Unfortunately, + * FreeBSD doesn't guarantee that mbufs will be filled in starting + * at longword boundaries, so we have to do a buffer copy before + * transmission. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#include <net/bpf.h> + +#include <vm/vm.h> /* for vtophys */ +#include <vm/pmap.h> /* for vtophys */ +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +#define VR_USEIOSPACE + +#include <pci/if_vrreg.h> + +MODULE_DEPEND(vr, miibus, 1, 1, 1); + +/* "controller miibus0" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif + +/* + * Various supported device vendors/types and their names. + */ +static struct vr_type vr_devs[] = { + { VIA_VENDORID, VIA_DEVICEID_RHINE, + "VIA VT3043 Rhine I 10/100BaseTX" }, + { VIA_VENDORID, VIA_DEVICEID_RHINE_II, + "VIA VT86C100A Rhine II 10/100BaseTX" }, + { VIA_VENDORID, VIA_DEVICEID_RHINE_II_2, + "VIA VT6102 Rhine II 10/100BaseTX" }, + { DELTA_VENDORID, DELTA_DEVICEID_RHINE_II, + "Delta Electronics Rhine II 10/100BaseTX" }, + { ADDTRON_VENDORID, ADDTRON_DEVICEID_RHINE_II, + "Addtron Technology Rhine II 10/100BaseTX" }, + { 0, 0, NULL } +}; + +static int vr_probe (device_t); +static int vr_attach (device_t); +static int vr_detach (device_t); + +static int vr_newbuf (struct vr_softc *, + struct vr_chain_onefrag *, + struct mbuf *); +static int vr_encap (struct vr_softc *, struct vr_chain *, + struct mbuf * ); + +static void vr_rxeof (struct vr_softc *); +static void vr_rxeoc (struct vr_softc *); +static void vr_txeof (struct vr_softc *); +static void vr_txeoc (struct vr_softc *); +static void vr_tick (void *); +static void vr_intr (void *); +static void vr_start (struct ifnet *); +static int vr_ioctl (struct ifnet *, u_long, caddr_t); +static void vr_init (void *); +static void vr_stop (struct vr_softc *); +static void vr_watchdog (struct ifnet *); +static void vr_shutdown (device_t); +static int vr_ifmedia_upd (struct ifnet *); +static void vr_ifmedia_sts (struct ifnet *, struct ifmediareq *); + +static void vr_mii_sync (struct vr_softc *); +static void vr_mii_send (struct vr_softc *, u_int32_t, int); +static int vr_mii_readreg (struct vr_softc *, struct vr_mii_frame *); +static int vr_mii_writereg (struct vr_softc *, struct vr_mii_frame *); +static int vr_miibus_readreg (device_t, int, int); +static int vr_miibus_writereg (device_t, int, int, int); +static void vr_miibus_statchg (device_t); + +static void vr_setcfg (struct vr_softc *, int); +static u_int8_t vr_calchash (u_int8_t *); +static void vr_setmulti (struct vr_softc *); +static void vr_reset (struct vr_softc *); +static int vr_list_rx_init (struct vr_softc *); +static int vr_list_tx_init (struct vr_softc *); + +#ifdef VR_USEIOSPACE +#define VR_RES SYS_RES_IOPORT +#define VR_RID VR_PCI_LOIO +#else +#define VR_RES SYS_RES_MEMORY +#define VR_RID VR_PCI_LOMEM +#endif + +static device_method_t vr_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, vr_probe), + DEVMETHOD(device_attach, vr_attach), + DEVMETHOD(device_detach, vr_detach), + DEVMETHOD(device_shutdown, vr_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, vr_miibus_readreg), + DEVMETHOD(miibus_writereg, vr_miibus_writereg), + DEVMETHOD(miibus_statchg, vr_miibus_statchg), + + { 0, 0 } +}; + +static driver_t vr_driver = { + "vr", + vr_methods, + sizeof(struct vr_softc) +}; + +static devclass_t vr_devclass; + +DRIVER_MODULE(if_vr, pci, vr_driver, vr_devclass, 0, 0); +DRIVER_MODULE(miibus, vr, miibus_driver, miibus_devclass, 0, 0); + +#define VR_SETBIT(sc, reg, x) \ + CSR_WRITE_1(sc, reg, \ + CSR_READ_1(sc, reg) | x) + +#define VR_CLRBIT(sc, reg, x) \ + CSR_WRITE_1(sc, reg, \ + CSR_READ_1(sc, reg) & ~x) + +#define VR_SETBIT16(sc, reg, x) \ + CSR_WRITE_2(sc, reg, \ + CSR_READ_2(sc, reg) | x) + +#define VR_CLRBIT16(sc, reg, x) \ + CSR_WRITE_2(sc, reg, \ + CSR_READ_2(sc, reg) & ~x) + +#define VR_SETBIT32(sc, reg, x) \ + CSR_WRITE_4(sc, reg, \ + CSR_READ_4(sc, reg) | x) + +#define VR_CLRBIT32(sc, reg, x) \ + CSR_WRITE_4(sc, reg, \ + CSR_READ_4(sc, reg) & ~x) + +#define SIO_SET(x) \ + CSR_WRITE_1(sc, VR_MIICMD, \ + CSR_READ_1(sc, VR_MIICMD) | x) + +#define SIO_CLR(x) \ + CSR_WRITE_1(sc, VR_MIICMD, \ + CSR_READ_1(sc, VR_MIICMD) & ~x) + +/* + * Sync the PHYs by setting data bit and strobing the clock 32 times. + */ +static void vr_mii_sync(sc) + struct vr_softc *sc; +{ + register int i; + + SIO_SET(VR_MIICMD_DIR|VR_MIICMD_DATAIN); + + for (i = 0; i < 32; i++) { + SIO_SET(VR_MIICMD_CLK); + DELAY(1); + SIO_CLR(VR_MIICMD_CLK); + DELAY(1); + } + + return; +} + +/* + * Clock a series of bits through the MII. + */ +static void vr_mii_send(sc, bits, cnt) + struct vr_softc *sc; + u_int32_t bits; + int cnt; +{ + int i; + + SIO_CLR(VR_MIICMD_CLK); + + for (i = (0x1 << (cnt - 1)); i; i >>= 1) { + if (bits & i) { + SIO_SET(VR_MIICMD_DATAIN); + } else { + SIO_CLR(VR_MIICMD_DATAIN); + } + DELAY(1); + SIO_CLR(VR_MIICMD_CLK); + DELAY(1); + SIO_SET(VR_MIICMD_CLK); + } +} + +/* + * Read an PHY register through the MII. + */ +static int vr_mii_readreg(sc, frame) + struct vr_softc *sc; + struct vr_mii_frame *frame; + +{ + int i, ack; + + VR_LOCK(sc); + + /* + * Set up frame for RX. + */ + frame->mii_stdelim = VR_MII_STARTDELIM; + frame->mii_opcode = VR_MII_READOP; + frame->mii_turnaround = 0; + frame->mii_data = 0; + + CSR_WRITE_1(sc, VR_MIICMD, 0); + VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); + + /* + * Turn on data xmit. + */ + SIO_SET(VR_MIICMD_DIR); + + vr_mii_sync(sc); + + /* + * Send command/address info. + */ + vr_mii_send(sc, frame->mii_stdelim, 2); + vr_mii_send(sc, frame->mii_opcode, 2); + vr_mii_send(sc, frame->mii_phyaddr, 5); + vr_mii_send(sc, frame->mii_regaddr, 5); + + /* Idle bit */ + SIO_CLR((VR_MIICMD_CLK|VR_MIICMD_DATAIN)); + DELAY(1); + SIO_SET(VR_MIICMD_CLK); + DELAY(1); + + /* Turn off xmit. */ + SIO_CLR(VR_MIICMD_DIR); + + /* Check for ack */ + SIO_CLR(VR_MIICMD_CLK); + DELAY(1); + SIO_SET(VR_MIICMD_CLK); + DELAY(1); + ack = CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT; + + /* + * Now try reading data bits. If the ack failed, we still + * need to clock through 16 cycles to keep the PHY(s) in sync. + */ + if (ack) { + for(i = 0; i < 16; i++) { + SIO_CLR(VR_MIICMD_CLK); + DELAY(1); + SIO_SET(VR_MIICMD_CLK); + DELAY(1); + } + goto fail; + } + + for (i = 0x8000; i; i >>= 1) { + SIO_CLR(VR_MIICMD_CLK); + DELAY(1); + if (!ack) { + if (CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT) + frame->mii_data |= i; + DELAY(1); + } + SIO_SET(VR_MIICMD_CLK); + DELAY(1); + } + +fail: + + SIO_CLR(VR_MIICMD_CLK); + DELAY(1); + SIO_SET(VR_MIICMD_CLK); + DELAY(1); + + VR_UNLOCK(sc); + + if (ack) + return(1); + return(0); +} + +/* + * Write to a PHY register through the MII. + */ +static int vr_mii_writereg(sc, frame) + struct vr_softc *sc; + struct vr_mii_frame *frame; + +{ + VR_LOCK(sc); + + CSR_WRITE_1(sc, VR_MIICMD, 0); + VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); + + /* + * Set up frame for TX. + */ + + frame->mii_stdelim = VR_MII_STARTDELIM; + frame->mii_opcode = VR_MII_WRITEOP; + frame->mii_turnaround = VR_MII_TURNAROUND; + + /* + * Turn on data output. + */ + SIO_SET(VR_MIICMD_DIR); + + vr_mii_sync(sc); + + vr_mii_send(sc, frame->mii_stdelim, 2); + vr_mii_send(sc, frame->mii_opcode, 2); + vr_mii_send(sc, frame->mii_phyaddr, 5); + vr_mii_send(sc, frame->mii_regaddr, 5); + vr_mii_send(sc, frame->mii_turnaround, 2); + vr_mii_send(sc, frame->mii_data, 16); + + /* Idle bit. */ + SIO_SET(VR_MIICMD_CLK); + DELAY(1); + SIO_CLR(VR_MIICMD_CLK); + DELAY(1); + + /* + * Turn off xmit. + */ + SIO_CLR(VR_MIICMD_DIR); + + VR_UNLOCK(sc); + + return(0); +} + +static int vr_miibus_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct vr_softc *sc; + struct vr_mii_frame frame; + + sc = device_get_softc(dev); + bzero((char *)&frame, sizeof(frame)); + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + vr_mii_readreg(sc, &frame); + + return(frame.mii_data); +} + +static int vr_miibus_writereg(dev, phy, reg, data) + device_t dev; + u_int16_t phy, reg, data; +{ + struct vr_softc *sc; + struct vr_mii_frame frame; + + sc = device_get_softc(dev); + bzero((char *)&frame, sizeof(frame)); + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + frame.mii_data = data; + + vr_mii_writereg(sc, &frame); + + return(0); +} + +static void vr_miibus_statchg(dev) + device_t dev; +{ + struct vr_softc *sc; + struct mii_data *mii; + + sc = device_get_softc(dev); + VR_LOCK(sc); + mii = device_get_softc(sc->vr_miibus); + vr_setcfg(sc, mii->mii_media_active); + VR_UNLOCK(sc); + + return; +} + +/* + * Calculate CRC of a multicast group address, return the lower 6 bits. + */ +static u_int8_t vr_calchash(addr) + u_int8_t *addr; +{ + u_int32_t crc, carry; + int i, j; + u_int8_t c; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (i = 0; i < 6; i++) { + c = *(addr + i); + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); + crc <<= 1; + c >>= 1; + if (carry) + crc = (crc ^ 0x04c11db6) | carry; + } + } + + /* return the filter bit position */ + return((crc >> 26) & 0x0000003F); +} + +/* + * Program the 64-bit multicast hash filter. + */ +static void vr_setmulti(sc) + struct vr_softc *sc; +{ + struct ifnet *ifp; + int h = 0; + u_int32_t hashes[2] = { 0, 0 }; + struct ifmultiaddr *ifma; + u_int8_t rxfilt; + int mcnt = 0; + + ifp = &sc->arpcom.ac_if; + + rxfilt = CSR_READ_1(sc, VR_RXCFG); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + rxfilt |= VR_RXCFG_RX_MULTI; + CSR_WRITE_1(sc, VR_RXCFG, rxfilt); + CSR_WRITE_4(sc, VR_MAR0, 0xFFFFFFFF); + CSR_WRITE_4(sc, VR_MAR1, 0xFFFFFFFF); + return; + } + + /* first, zot all the existing hash bits */ + CSR_WRITE_4(sc, VR_MAR0, 0); + CSR_WRITE_4(sc, VR_MAR1, 0); + + /* now program new ones */ + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = vr_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + mcnt++; + } + + if (mcnt) + rxfilt |= VR_RXCFG_RX_MULTI; + else + rxfilt &= ~VR_RXCFG_RX_MULTI; + + CSR_WRITE_4(sc, VR_MAR0, hashes[0]); + CSR_WRITE_4(sc, VR_MAR1, hashes[1]); + CSR_WRITE_1(sc, VR_RXCFG, rxfilt); + + return; +} + +/* + * In order to fiddle with the + * 'full-duplex' and '100Mbps' bits in the netconfig register, we + * first have to put the transmit and/or receive logic in the idle state. + */ +static void vr_setcfg(sc, media) + struct vr_softc *sc; + int media; +{ + int restart = 0; + + if (CSR_READ_2(sc, VR_COMMAND) & (VR_CMD_TX_ON|VR_CMD_RX_ON)) { + restart = 1; + VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_TX_ON|VR_CMD_RX_ON)); + } + + if ((media & IFM_GMASK) == IFM_FDX) + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); + else + VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); + + if (restart) + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON|VR_CMD_RX_ON); + + return; +} + +static void vr_reset(sc) + struct vr_softc *sc; +{ + register int i; + + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RESET); + + for (i = 0; i < VR_TIMEOUT; i++) { + DELAY(10); + if (!(CSR_READ_2(sc, VR_COMMAND) & VR_CMD_RESET)) + break; + } + if (i == VR_TIMEOUT) + printf("vr%d: reset never completed!\n", sc->vr_unit); + + /* Wait a little while for the chip to get its brains in order. */ + DELAY(1000); + + return; +} + +/* + * Probe for a VIA Rhine chip. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + */ +static int vr_probe(dev) + device_t dev; +{ + struct vr_type *t; + + t = vr_devs; + + while(t->vr_name != NULL) { + if ((pci_get_vendor(dev) == t->vr_vid) && + (pci_get_device(dev) == t->vr_did)) { + device_set_desc(dev, t->vr_name); + return(0); + } + t++; + } + + return(ENXIO); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int vr_attach(dev) + device_t dev; +{ + int i; + u_char eaddr[ETHER_ADDR_LEN]; + u_int32_t command; + struct vr_softc *sc; + struct ifnet *ifp; + int unit, error = 0, rid; + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + bzero(sc, sizeof(struct vr_softc *)); + + mtx_init(&sc->vr_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF | MTX_RECURSE); + VR_LOCK(sc); + + /* + * Handle power management nonsense. + */ + if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { + u_int32_t iobase, membase, irq; + + /* Save important PCI config data. */ + iobase = pci_read_config(dev, VR_PCI_LOIO, 4); + membase = pci_read_config(dev, VR_PCI_LOMEM, 4); + irq = pci_read_config(dev, VR_PCI_INTLINE, 4); + + /* Reset the power state. */ + printf("vr%d: chip is in D%d power mode " + "-- setting to D0\n", unit, + pci_get_powerstate(dev)); + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + + /* Restore PCI config data. */ + pci_write_config(dev, VR_PCI_LOIO, iobase, 4); + pci_write_config(dev, VR_PCI_LOMEM, membase, 4); + pci_write_config(dev, VR_PCI_INTLINE, irq, 4); + } + + /* + * Map control/status registers. + */ + pci_enable_busmaster(dev); + pci_enable_io(dev, SYS_RES_IOPORT); + pci_enable_io(dev, SYS_RES_MEMORY); + command = pci_read_config(dev, PCIR_COMMAND, 4); + +#ifdef VR_USEIOSPACE + if (!(command & PCIM_CMD_PORTEN)) { + printf("vr%d: failed to enable I/O ports!\n", unit); + free(sc, M_DEVBUF); + goto fail; + } +#else + if (!(command & PCIM_CMD_MEMEN)) { + printf("vr%d: failed to enable memory mapping!\n", unit); + goto fail; + } +#endif + + rid = VR_RID; + sc->vr_res = bus_alloc_resource(dev, VR_RES, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (sc->vr_res == NULL) { + printf("vr%d: couldn't map ports/memory\n", unit); + error = ENXIO; + goto fail; + } + + sc->vr_btag = rman_get_bustag(sc->vr_res); + sc->vr_bhandle = rman_get_bushandle(sc->vr_res); + + /* Allocate interrupt */ + rid = 0; + sc->vr_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->vr_irq == NULL) { + printf("vr%d: couldn't map interrupt\n", unit); + bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->vr_irq, INTR_TYPE_NET, + vr_intr, sc, &sc->vr_intrhand); + + if (error) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); + bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); + printf("vr%d: couldn't set up irq\n", unit); + goto fail; + } + + /* + * Windows may put the chip in suspend mode when it + * shuts down. Be sure to kick it in the head to wake it + * up again. + */ + VR_CLRBIT(sc, VR_STICKHW, (VR_STICKHW_DS0|VR_STICKHW_DS1)); + + /* Reset the adapter. */ + vr_reset(sc); + + /* + * Get station address. The way the Rhine chips work, + * you're not allowed to directly access the EEPROM once + * they've been programmed a special way. Consequently, + * we need to read the node address from the PAR0 and PAR1 + * registers. + */ + VR_SETBIT(sc, VR_EECSR, VR_EECSR_LOAD); + DELAY(200); + for (i = 0; i < ETHER_ADDR_LEN; i++) + eaddr[i] = CSR_READ_1(sc, VR_PAR0 + i); + + /* + * A Rhine chip was detected. Inform the world. + */ + printf("vr%d: Ethernet address: %6D\n", unit, eaddr, ":"); + + sc->vr_unit = unit; + bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); + + sc->vr_ldata = contigmalloc(sizeof(struct vr_list_data), M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc->vr_ldata == NULL) { + printf("vr%d: no memory for list buffers!\n", unit); + bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); + bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); + error = ENXIO; + goto fail; + } + + bzero(sc->vr_ldata, sizeof(struct vr_list_data)); + + ifp = &sc->arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = "vr"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = vr_ioctl; + ifp->if_output = ether_output; + ifp->if_start = vr_start; + ifp->if_watchdog = vr_watchdog; + ifp->if_init = vr_init; + ifp->if_baudrate = 10000000; + ifp->if_snd.ifq_maxlen = VR_TX_LIST_CNT - 1; + + /* + * Do MII setup. + */ + if (mii_phy_probe(dev, &sc->vr_miibus, + vr_ifmedia_upd, vr_ifmedia_sts)) { + printf("vr%d: MII without any phy!\n", sc->vr_unit); + bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); + bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); + contigfree(sc->vr_ldata, + sizeof(struct vr_list_data), M_DEVBUF); + error = ENXIO; + goto fail; + } + + callout_handle_init(&sc->vr_stat_ch); + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); + VR_UNLOCK(sc); + return(0); + +fail: + VR_UNLOCK(sc); + mtx_destroy(&sc->vr_mtx); + + return(error); +} + +static int vr_detach(dev) + device_t dev; +{ + struct vr_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + VR_LOCK(sc); + ifp = &sc->arpcom.ac_if; + + vr_stop(sc); + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + + bus_generic_detach(dev); + device_delete_child(dev, sc->vr_miibus); + + bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); + bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); + + contigfree(sc->vr_ldata, sizeof(struct vr_list_data), M_DEVBUF); + + VR_UNLOCK(sc); + mtx_destroy(&sc->vr_mtx); + + return(0); +} + +/* + * Initialize the transmit descriptors. + */ +static int vr_list_tx_init(sc) + struct vr_softc *sc; +{ + struct vr_chain_data *cd; + struct vr_list_data *ld; + int i; + + cd = &sc->vr_cdata; + ld = sc->vr_ldata; + for (i = 0; i < VR_TX_LIST_CNT; i++) { + cd->vr_tx_chain[i].vr_ptr = &ld->vr_tx_list[i]; + if (i == (VR_TX_LIST_CNT - 1)) + cd->vr_tx_chain[i].vr_nextdesc = + &cd->vr_tx_chain[0]; + else + cd->vr_tx_chain[i].vr_nextdesc = + &cd->vr_tx_chain[i + 1]; + } + + cd->vr_tx_free = &cd->vr_tx_chain[0]; + cd->vr_tx_tail = cd->vr_tx_head = NULL; + + 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 vr_list_rx_init(sc) + struct vr_softc *sc; +{ + struct vr_chain_data *cd; + struct vr_list_data *ld; + int i; + + cd = &sc->vr_cdata; + ld = sc->vr_ldata; + + for (i = 0; i < VR_RX_LIST_CNT; i++) { + cd->vr_rx_chain[i].vr_ptr = + (struct vr_desc *)&ld->vr_rx_list[i]; + if (vr_newbuf(sc, &cd->vr_rx_chain[i], NULL) == ENOBUFS) + return(ENOBUFS); + if (i == (VR_RX_LIST_CNT - 1)) { + cd->vr_rx_chain[i].vr_nextdesc = + &cd->vr_rx_chain[0]; + ld->vr_rx_list[i].vr_next = + vtophys(&ld->vr_rx_list[0]); + } else { + cd->vr_rx_chain[i].vr_nextdesc = + &cd->vr_rx_chain[i + 1]; + ld->vr_rx_list[i].vr_next = + vtophys(&ld->vr_rx_list[i + 1]); + } + } + + cd->vr_rx_head = &cd->vr_rx_chain[0]; + + return(0); +} + +/* + * Initialize an RX descriptor and attach an MBUF cluster. + * Note: the length fields are only 11 bits wide, which means the + * largest size we can specify is 2047. This is important because + * MCLBYTES is 2048, so we have to subtract one otherwise we'll + * overflow the field and make a mess. + */ +static int vr_newbuf(sc, c, m) + struct vr_softc *sc; + struct vr_chain_onefrag *c; + struct mbuf *m; +{ + struct mbuf *m_new = NULL; + + if (m == NULL) { + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(ENOBUFS); + + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + return(ENOBUFS); + } + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + } else { + m_new = m; + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + m_new->m_data = m_new->m_ext.ext_buf; + } + + m_adj(m_new, sizeof(u_int64_t)); + + c->vr_mbuf = m_new; + c->vr_ptr->vr_status = VR_RXSTAT; + c->vr_ptr->vr_data = vtophys(mtod(m_new, caddr_t)); + c->vr_ptr->vr_ctl = VR_RXCTL | VR_RXLEN; + + return(0); +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +static void vr_rxeof(sc) + struct vr_softc *sc; +{ + struct ether_header *eh; + struct mbuf *m; + struct ifnet *ifp; + struct vr_chain_onefrag *cur_rx; + int total_len = 0; + u_int32_t rxstat; + + ifp = &sc->arpcom.ac_if; + + while(!((rxstat = sc->vr_cdata.vr_rx_head->vr_ptr->vr_status) & + VR_RXSTAT_OWN)) { + struct mbuf *m0 = NULL; + + cur_rx = sc->vr_cdata.vr_rx_head; + sc->vr_cdata.vr_rx_head = cur_rx->vr_nextdesc; + m = cur_rx->vr_mbuf; + + /* + * If an error occurs, update stats, clear the + * status word and leave the mbuf cluster in place: + * it should simply get re-used next time this descriptor + * comes up in the ring. + */ + if (rxstat & VR_RXSTAT_RXERR) { + ifp->if_ierrors++; + printf("vr%d: rx error: ", sc->vr_unit); + switch(rxstat & 0x000000FF) { + case VR_RXSTAT_CRCERR: + printf("crc error\n"); + break; + case VR_RXSTAT_FRAMEALIGNERR: + printf("frame alignment error\n"); + break; + case VR_RXSTAT_FIFOOFLOW: + printf("FIFO overflow\n"); + break; + case VR_RXSTAT_GIANT: + printf("received giant packet\n"); + break; + case VR_RXSTAT_RUNT: + printf("received runt packet\n"); + break; + case VR_RXSTAT_BUSERR: + printf("system bus error\n"); + break; + case VR_RXSTAT_BUFFERR: + printf("rx buffer error\n"); + break; + default: + printf("unknown rx error\n"); + break; + } + vr_newbuf(sc, cur_rx, m); + continue; + } + + /* No errors; receive the packet. */ + total_len = VR_RXBYTES(cur_rx->vr_ptr->vr_status); + + /* + * XXX The VIA Rhine chip includes the CRC with every + * received frame, and there's no way to turn this + * behavior off (at least, I can't find anything in + * the manual that explains how to do it) so we have + * to trim off the CRC manually. + */ + total_len -= ETHER_CRC_LEN; + + m0 = m_devget(mtod(m, char *), total_len, ETHER_ALIGN, ifp, + NULL); + vr_newbuf(sc, cur_rx, m); + if (m0 == NULL) { + ifp->if_ierrors++; + continue; + } + m = m0; + + ifp->if_ipackets++; + eh = mtod(m, struct ether_header *); + + /* Remove header from mbuf and pass it on. */ + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, eh, m); + } + + return; +} + +void vr_rxeoc(sc) + struct vr_softc *sc; +{ + + vr_rxeof(sc); + VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); + CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_GO); + + return; +} + +/* + * A frame was downloaded to the chip. It's safe for us to clean up + * the list buffers. + */ + +static void vr_txeof(sc) + struct vr_softc *sc; +{ + struct vr_chain *cur_tx; + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + + /* Reset the timeout timer; if_txeoc will clear it. */ + ifp->if_timer = 5; + + /* Sanity check. */ + if (sc->vr_cdata.vr_tx_head == NULL) + return; + + /* + * Go through our tx list and free mbufs for those + * frames that have been transmitted. + */ + while(sc->vr_cdata.vr_tx_head->vr_mbuf != NULL) { + u_int32_t txstat; + + cur_tx = sc->vr_cdata.vr_tx_head; + txstat = cur_tx->vr_ptr->vr_status; + + if (txstat & VR_TXSTAT_OWN) + break; + + if (txstat & VR_TXSTAT_ERRSUM) { + ifp->if_oerrors++; + if (txstat & VR_TXSTAT_DEFER) + ifp->if_collisions++; + if (txstat & VR_TXSTAT_LATECOLL) + ifp->if_collisions++; + } + + ifp->if_collisions +=(txstat & VR_TXSTAT_COLLCNT) >> 3; + + ifp->if_opackets++; + if (cur_tx->vr_mbuf != NULL) { + m_freem(cur_tx->vr_mbuf); + cur_tx->vr_mbuf = NULL; + } + + if (sc->vr_cdata.vr_tx_head == sc->vr_cdata.vr_tx_tail) { + sc->vr_cdata.vr_tx_head = NULL; + sc->vr_cdata.vr_tx_tail = NULL; + break; + } + + sc->vr_cdata.vr_tx_head = cur_tx->vr_nextdesc; + } + + return; +} + +/* + * TX 'end of channel' interrupt handler. + */ +static void vr_txeoc(sc) + struct vr_softc *sc; +{ + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + + if (sc->vr_cdata.vr_tx_head == NULL) { + ifp->if_flags &= ~IFF_OACTIVE; + sc->vr_cdata.vr_tx_tail = NULL; + ifp->if_timer = 0; + } + + return; +} + +static void vr_tick(xsc) + void *xsc; +{ + struct vr_softc *sc; + struct mii_data *mii; + + sc = xsc; + VR_LOCK(sc); + mii = device_get_softc(sc->vr_miibus); + mii_tick(mii); + + sc->vr_stat_ch = timeout(vr_tick, sc, hz); + + VR_UNLOCK(sc); + + return; +} + +static void vr_intr(arg) + void *arg; +{ + struct vr_softc *sc; + struct ifnet *ifp; + u_int16_t status; + + sc = arg; + VR_LOCK(sc); + ifp = &sc->arpcom.ac_if; + + /* Supress unwanted interrupts. */ + if (!(ifp->if_flags & IFF_UP)) { + vr_stop(sc); + VR_UNLOCK(sc); + return; + } + + /* Disable interrupts. */ + CSR_WRITE_2(sc, VR_IMR, 0x0000); + + for (;;) { + + status = CSR_READ_2(sc, VR_ISR); + if (status) + CSR_WRITE_2(sc, VR_ISR, status); + + if ((status & VR_INTRS) == 0) + break; + + if (status & VR_ISR_RX_OK) + vr_rxeof(sc); + + if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) || + (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_OFLOW) || + (status & VR_ISR_RX_DROPPED)) { + vr_rxeof(sc); + vr_rxeoc(sc); + } + + if (status & VR_ISR_TX_OK) { + vr_txeof(sc); + vr_txeoc(sc); + } + + if ((status & VR_ISR_TX_UNDERRUN)||(status & VR_ISR_TX_ABRT)){ + ifp->if_oerrors++; + vr_txeof(sc); + if (sc->vr_cdata.vr_tx_head != NULL) { + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON); + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO); + } + } + + if (status & VR_ISR_BUSERR) { + vr_reset(sc); + vr_init(sc); + } + } + + /* Re-enable interrupts. */ + CSR_WRITE_2(sc, VR_IMR, VR_INTRS); + + if (ifp->if_snd.ifq_head != NULL) { + vr_start(ifp); + } + + VR_UNLOCK(sc); + + return; +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int vr_encap(sc, c, m_head) + struct vr_softc *sc; + struct vr_chain *c; + struct mbuf *m_head; +{ + int frag = 0; + struct vr_desc *f = NULL; + int total_len; + struct mbuf *m; + + m = m_head; + total_len = 0; + + /* + * The VIA Rhine wants packet buffers to be longword + * aligned, but very often our mbufs aren't. Rather than + * waste time trying to decide when to copy and when not + * to copy, just do it all the time. + */ + if (m != NULL) { + struct mbuf *m_new = NULL; + + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) { + printf("vr%d: no memory for tx list\n", sc->vr_unit); + return(1); + } + if (m_head->m_pkthdr.len > MHLEN) { + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + printf("vr%d: no memory for tx list\n", + sc->vr_unit); + return(1); + } + } + m_copydata(m_head, 0, m_head->m_pkthdr.len, + mtod(m_new, caddr_t)); + m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; + m_freem(m_head); + m_head = m_new; + /* + * The Rhine chip doesn't auto-pad, so we have to make + * sure to pad short frames out to the minimum frame length + * ourselves. + */ + if (m_head->m_len < VR_MIN_FRAMELEN) { + m_new->m_pkthdr.len += VR_MIN_FRAMELEN - m_new->m_len; + m_new->m_len = m_new->m_pkthdr.len; + } + f = c->vr_ptr; + f->vr_data = vtophys(mtod(m_new, caddr_t)); + f->vr_ctl = total_len = m_new->m_len; + f->vr_ctl |= VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG; + f->vr_status = 0; + frag = 1; + } + + c->vr_mbuf = m_head; + c->vr_ptr->vr_ctl |= VR_TXCTL_LASTFRAG|VR_TXCTL_FINT; + c->vr_ptr->vr_next = vtophys(c->vr_nextdesc->vr_ptr); + + return(0); +} + +/* + * Main transmit routine. To avoid having to do mbuf copies, we put pointers + * to the mbuf data regions directly in the transmit lists. We also save a + * copy of the pointers since the transmit list fragment pointers are + * physical addresses. + */ + +static void vr_start(ifp) + struct ifnet *ifp; +{ + struct vr_softc *sc; + struct mbuf *m_head = NULL; + struct vr_chain *cur_tx = NULL, *start_tx; + + sc = ifp->if_softc; + + VR_LOCK(sc); + if (ifp->if_flags & IFF_OACTIVE) { + VR_UNLOCK(sc); + return; + } + + /* + * Check for an available queue slot. If there are none, + * punt. + */ + if (sc->vr_cdata.vr_tx_free->vr_mbuf != NULL) { + ifp->if_flags |= IFF_OACTIVE; + return; + } + + start_tx = sc->vr_cdata.vr_tx_free; + + while(sc->vr_cdata.vr_tx_free->vr_mbuf == NULL) { + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + /* Pick a descriptor off the free list. */ + cur_tx = sc->vr_cdata.vr_tx_free; + sc->vr_cdata.vr_tx_free = cur_tx->vr_nextdesc; + + /* Pack the data into the descriptor. */ + if (vr_encap(sc, cur_tx, m_head)) { + IF_PREPEND(&ifp->if_snd, m_head); + ifp->if_flags |= IFF_OACTIVE; + cur_tx = NULL; + break; + } + + if (cur_tx != start_tx) + VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, cur_tx->vr_mbuf); + + VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; + VR_SETBIT16(sc, VR_COMMAND, /*VR_CMD_TX_ON|*/VR_CMD_TX_GO); + } + + /* + * If there are no frames queued, bail. + */ + if (cur_tx == NULL) { + VR_UNLOCK(sc); + return; + } + + sc->vr_cdata.vr_tx_tail = cur_tx; + + if (sc->vr_cdata.vr_tx_head == NULL) + sc->vr_cdata.vr_tx_head = start_tx; + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + VR_UNLOCK(sc); + + return; +} + +static void vr_init(xsc) + void *xsc; +{ + struct vr_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mii_data *mii; + int i; + + VR_LOCK(sc); + + mii = device_get_softc(sc->vr_miibus); + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + vr_stop(sc); + vr_reset(sc); + + /* + * Set our station address. + */ + for (i = 0; i < ETHER_ADDR_LEN; i++) + CSR_WRITE_1(sc, VR_PAR0 + i, sc->arpcom.ac_enaddr[i]); + + VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_THRESH); + VR_SETBIT(sc, VR_RXCFG, VR_RXTHRESH_STORENFWD); + + VR_CLRBIT(sc, VR_TXCFG, VR_TXCFG_TX_THRESH); + VR_SETBIT(sc, VR_TXCFG, VR_TXTHRESH_STORENFWD); + + /* Init circular RX list. */ + if (vr_list_rx_init(sc) == ENOBUFS) { + printf("vr%d: initialization failed: no " + "memory for rx buffers\n", sc->vr_unit); + vr_stop(sc); + VR_UNLOCK(sc); + return; + } + + /* + * Init tx descriptors. + */ + vr_list_tx_init(sc); + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); + else + VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); + + /* Set capture broadcast bit to capture broadcast frames. */ + if (ifp->if_flags & IFF_BROADCAST) + VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); + else + VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); + + /* + * Program the multicast filter, if necessary. + */ + vr_setmulti(sc); + + /* + * Load the address of the RX list. + */ + CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); + + /* Enable receiver and transmitter. */ + CSR_WRITE_2(sc, VR_COMMAND, VR_CMD_TX_NOPOLL|VR_CMD_START| + VR_CMD_TX_ON|VR_CMD_RX_ON| + VR_CMD_RX_GO); + + CSR_WRITE_4(sc, VR_TXADDR, vtophys(&sc->vr_ldata->vr_tx_list[0])); + + /* + * Enable interrupts. + */ + CSR_WRITE_2(sc, VR_ISR, 0xFFFF); + CSR_WRITE_2(sc, VR_IMR, VR_INTRS); + + mii_mediachg(mii); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + sc->vr_stat_ch = timeout(vr_tick, sc, hz); + + VR_UNLOCK(sc); + + return; +} + +/* + * Set media options. + */ +static int vr_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct vr_softc *sc; + + sc = ifp->if_softc; + + if (ifp->if_flags & IFF_UP) + vr_init(sc); + + return(0); +} + +/* + * Report current media status. + */ +static void vr_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct vr_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = device_get_softc(sc->vr_miibus); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + + return; +} + +static int vr_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct vr_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + struct mii_data *mii; + int error = 0; + + VR_LOCK(sc); + + switch(command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + vr_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) + vr_stop(sc); + } + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + vr_setmulti(sc); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = device_get_softc(sc->vr_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + break; + default: + error = EINVAL; + break; + } + + VR_UNLOCK(sc); + + return(error); +} + +static void vr_watchdog(ifp) + struct ifnet *ifp; +{ + struct vr_softc *sc; + + sc = ifp->if_softc; + + VR_LOCK(sc); + ifp->if_oerrors++; + printf("vr%d: watchdog timeout\n", sc->vr_unit); + + vr_stop(sc); + vr_reset(sc); + vr_init(sc); + + if (ifp->if_snd.ifq_head != NULL) + vr_start(ifp); + + VR_UNLOCK(sc); + + return; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void vr_stop(sc) + struct vr_softc *sc; +{ + register int i; + struct ifnet *ifp; + + VR_LOCK(sc); + + ifp = &sc->arpcom.ac_if; + ifp->if_timer = 0; + + untimeout(vr_tick, sc, sc->vr_stat_ch); + + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_STOP); + VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_RX_ON|VR_CMD_TX_ON)); + CSR_WRITE_2(sc, VR_IMR, 0x0000); + CSR_WRITE_4(sc, VR_TXADDR, 0x00000000); + CSR_WRITE_4(sc, VR_RXADDR, 0x00000000); + + /* + * Free data in the RX lists. + */ + for (i = 0; i < VR_RX_LIST_CNT; i++) { + if (sc->vr_cdata.vr_rx_chain[i].vr_mbuf != NULL) { + m_freem(sc->vr_cdata.vr_rx_chain[i].vr_mbuf); + sc->vr_cdata.vr_rx_chain[i].vr_mbuf = NULL; + } + } + bzero((char *)&sc->vr_ldata->vr_rx_list, + sizeof(sc->vr_ldata->vr_rx_list)); + + /* + * Free the TX list buffers. + */ + for (i = 0; i < VR_TX_LIST_CNT; i++) { + if (sc->vr_cdata.vr_tx_chain[i].vr_mbuf != NULL) { + m_freem(sc->vr_cdata.vr_tx_chain[i].vr_mbuf); + sc->vr_cdata.vr_tx_chain[i].vr_mbuf = NULL; + } + } + + bzero((char *)&sc->vr_ldata->vr_tx_list, + sizeof(sc->vr_ldata->vr_tx_list)); + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + VR_UNLOCK(sc); + + return; +} + +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static void vr_shutdown(dev) + device_t dev; +{ + struct vr_softc *sc; + + sc = device_get_softc(dev); + + vr_stop(sc); + + return; +} diff --git a/sys/pci/if_vrreg.h b/sys/pci/if_vrreg.h new file mode 100644 index 0000000..c84f5c2 --- /dev/null +++ b/sys/pci/if_vrreg.h @@ -0,0 +1,523 @@ +/* + * Copyright (c) 1997, 1998 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * Rhine register definitions. + */ + +#define VR_PAR0 0x00 /* node address 0 to 4 */ +#define VR_PAR1 0x04 /* node address 2 to 6 */ +#define VR_RXCFG 0x06 /* receiver config register */ +#define VR_TXCFG 0x07 /* transmit config register */ +#define VR_COMMAND 0x08 /* command register */ +#define VR_ISR 0x0C /* interrupt/status register */ +#define VR_IMR 0x0E /* interrupt mask register */ +#define VR_MAR0 0x10 /* multicast hash 0 */ +#define VR_MAR1 0x14 /* multicast hash 1 */ +#define VR_RXADDR 0x18 /* rx descriptor list start addr */ +#define VR_TXADDR 0x1C /* tx descriptor list start addr */ +#define VR_CURRXDESC0 0x20 +#define VR_CURRXDESC1 0x24 +#define VR_CURRXDESC2 0x28 +#define VR_CURRXDESC3 0x2C +#define VR_NEXTRXDESC0 0x30 +#define VR_NEXTRXDESC1 0x34 +#define VR_NEXTRXDESC2 0x38 +#define VR_NEXTRXDESC3 0x3C +#define VR_CURTXDESC0 0x40 +#define VR_CURTXDESC1 0x44 +#define VR_CURTXDESC2 0x48 +#define VR_CURTXDESC3 0x4C +#define VR_NEXTTXDESC0 0x50 +#define VR_NEXTTXDESC1 0x54 +#define VR_NEXTTXDESC2 0x58 +#define VR_NEXTTXDESC3 0x5C +#define VR_CURRXDMA 0x60 /* current RX DMA address */ +#define VR_CURTXDMA 0x64 /* current TX DMA address */ +#define VR_TALLYCNT 0x68 /* tally counter test register */ +#define VR_PHYADDR 0x6C +#define VR_MIISTAT 0x6D +#define VR_BCR0 0x6E +#define VR_BCR1 0x6F +#define VR_MIICMD 0x70 +#define VR_MIIADDR 0x71 +#define VR_MIIDATA 0x72 +#define VR_EECSR 0x74 +#define VR_TEST 0x75 +#define VR_GPIO 0x76 +#define VR_CONFIG 0x78 +#define VR_MPA_CNT 0x7C +#define VR_CRC_CNT 0x7E +#define VR_STICKHW 0x83 + +/* + * RX config bits. + */ +#define VR_RXCFG_RX_ERRPKTS 0x01 +#define VR_RXCFG_RX_RUNT 0x02 +#define VR_RXCFG_RX_MULTI 0x04 +#define VR_RXCFG_RX_BROAD 0x08 +#define VR_RXCFG_RX_PROMISC 0x10 +#define VR_RXCFG_RX_THRESH 0xE0 + +#define VR_RXTHRESH_32BYTES 0x00 +#define VR_RXTHRESH_64BYTES 0x20 +#define VR_RXTHRESH_128BYTES 0x40 +#define VR_RXTHRESH_256BYTES 0x60 +#define VR_RXTHRESH_512BYTES 0x80 +#define VR_RXTHRESH_768BYTES 0xA0 +#define VR_RXTHRESH_1024BYTES 0xC0 +#define VR_RXTHRESH_STORENFWD 0xE0 + +/* + * TX config bits. + */ +#define VR_TXCFG_RSVD0 0x01 +#define VR_TXCFG_LOOPBKMODE 0x06 +#define VR_TXCFG_BACKOFF 0x08 +#define VR_TXCFG_RSVD1 0x10 +#define VR_TXCFG_TX_THRESH 0xE0 + +#define VR_TXTHRESH_32BYTES 0x00 +#define VR_TXTHRESH_64BYTES 0x20 +#define VR_TXTHRESH_128BYTES 0x40 +#define VR_TXTHRESH_256BYTES 0x60 +#define VR_TXTHRESH_512BYTES 0x80 +#define VR_TXTHRESH_768BYTES 0xA0 +#define VR_TXTHRESH_1024BYTES 0xC0 +#define VR_TXTHRESH_STORENFWD 0xE0 + +/* + * Command register bits. + */ +#define VR_CMD_INIT 0x0001 +#define VR_CMD_START 0x0002 +#define VR_CMD_STOP 0x0004 +#define VR_CMD_RX_ON 0x0008 +#define VR_CMD_TX_ON 0x0010 +#define VR_CMD_TX_GO 0x0020 +#define VR_CMD_RX_GO 0x0040 +#define VR_CMD_RSVD 0x0080 +#define VR_CMD_RX_EARLY 0x0100 +#define VR_CMD_TX_EARLY 0x0200 +#define VR_CMD_FULLDUPLEX 0x0400 +#define VR_CMD_TX_NOPOLL 0x0800 + +#define VR_CMD_RESET 0x8000 + +/* + * Interrupt status bits. + */ +#define VR_ISR_RX_OK 0x0001 /* packet rx ok */ +#define VR_ISR_TX_OK 0x0002 /* packet tx ok */ +#define VR_ISR_RX_ERR 0x0004 /* packet rx with err */ +#define VR_ISR_TX_ABRT 0x0008 /* tx aborted due to excess colls */ +#define VR_ISR_TX_UNDERRUN 0x0010 /* tx buffer underflow */ +#define VR_ISR_RX_NOBUF 0x0020 /* no rx buffer available */ +#define VR_ISR_BUSERR 0x0040 /* PCI bus error */ +#define VR_ISR_STATSOFLOW 0x0080 /* stats counter oflow */ +#define VR_ISR_RX_EARLY 0x0100 /* rx early */ +#define VR_ISR_LINKSTAT 0x0200 /* MII status change */ +#define VR_ISR_RX_OFLOW 0x0400 /* rx FIFO overflow */ +#define VR_ISR_RX_DROPPED 0x0800 +#define VR_ISR_RX_NOBUF2 0x1000 +#define VR_ISR_TX_ABRT2 0x2000 +#define VR_ISR_LINKSTAT2 0x4000 +#define VR_ISR_MAGICPACKET 0x8000 + +/* + * Interrupt mask bits. + */ +#define VR_IMR_RX_OK 0x0001 /* packet rx ok */ +#define VR_IMR_TX_OK 0x0002 /* packet tx ok */ +#define VR_IMR_RX_ERR 0x0004 /* packet rx with err */ +#define VR_IMR_TX_ABRT 0x0008 /* tx aborted due to excess colls */ +#define VR_IMR_TX_UNDERRUN 0x0010 /* tx buffer underflow */ +#define VR_IMR_RX_NOBUF 0x0020 /* no rx buffer available */ +#define VR_IMR_BUSERR 0x0040 /* PCI bus error */ +#define VR_IMR_STATSOFLOW 0x0080 /* stats counter oflow */ +#define VR_IMR_RX_EARLY 0x0100 /* rx early */ +#define VR_IMR_LINKSTAT 0x0200 /* MII status change */ +#define VR_IMR_RX_OFLOW 0x0400 /* rx FIFO overflow */ +#define VR_IMR_RX_DROPPED 0x0800 +#define VR_IMR_RX_NOBUF2 0x1000 +#define VR_IMR_TX_ABRT2 0x2000 +#define VR_IMR_LINKSTAT2 0x4000 +#define VR_IMR_MAGICPACKET 0x8000 + +#define VR_INTRS \ + (VR_IMR_RX_OK|VR_IMR_TX_OK|VR_IMR_RX_NOBUF| \ + VR_IMR_TX_ABRT|VR_IMR_TX_UNDERRUN|VR_IMR_BUSERR| \ + VR_IMR_RX_ERR|VR_ISR_RX_DROPPED) + +/* + * MII status register. + */ + +#define VR_MIISTAT_SPEED 0x01 +#define VR_MIISTAT_LINKFAULT 0x02 +#define VR_MIISTAT_MGTREADERR 0x04 +#define VR_MIISTAT_MIIERR 0x08 +#define VR_MIISTAT_PHYOPT 0x10 +#define VR_MIISTAT_MDC_SPEED 0x20 +#define VR_MIISTAT_RSVD 0x40 +#define VR_MIISTAT_GPIO1POLL 0x80 + +/* + * MII command register bits. + */ +#define VR_MIICMD_CLK 0x01 +#define VR_MIICMD_DATAOUT 0x02 +#define VR_MIICMD_DATAIN 0x04 +#define VR_MIICMD_DIR 0x08 +#define VR_MIICMD_DIRECTPGM 0x10 +#define VR_MIICMD_WRITE_ENB 0x20 +#define VR_MIICMD_READ_ENB 0x40 +#define VR_MIICMD_AUTOPOLL 0x80 + +/* + * EEPROM control bits. + */ +#define VR_EECSR_DATAIN 0x01 /* data out */ +#define VR_EECSR_DATAOUT 0x02 /* data in */ +#define VR_EECSR_CLK 0x04 /* clock */ +#define VR_EECSR_CS 0x08 /* chip select */ +#define VR_EECSR_DPM 0x10 +#define VR_EECSR_LOAD 0x20 +#define VR_EECSR_EMBP 0x40 +#define VR_EECSR_EEPR 0x80 + +#define VR_EECMD_WRITE 0x140 +#define VR_EECMD_READ 0x180 +#define VR_EECMD_ERASE 0x1c0 + +/* + * Test register bits. + */ +#define VR_TEST_TEST0 0x01 +#define VR_TEST_TEST1 0x02 +#define VR_TEST_TEST2 0x04 +#define VR_TEST_TSTUD 0x08 +#define VR_TEST_TSTOV 0x10 +#define VR_TEST_BKOFF 0x20 +#define VR_TEST_FCOL 0x40 +#define VR_TEST_HBDES 0x80 + +/* + * Config register bits. + */ +#define VR_CFG_GPIO2OUTENB 0x00000001 +#define VR_CFG_GPIO2OUT 0x00000002 /* gen. purp. pin */ +#define VR_CFG_GPIO2IN 0x00000004 /* gen. purp. pin */ +#define VR_CFG_AUTOOPT 0x00000008 /* enable rx/tx autopoll */ +#define VR_CFG_MIIOPT 0x00000010 +#define VR_CFG_MMIENB 0x00000020 /* memory mapped mode enb */ +#define VR_CFG_JUMPER 0x00000040 /* PHY and oper. mode select */ +#define VR_CFG_EELOAD 0x00000080 /* enable EEPROM programming */ +#define VR_CFG_LATMENB 0x00000100 /* larency timer effect enb. */ +#define VR_CFG_MRREADWAIT 0x00000200 +#define VR_CFG_MRWRITEWAIT 0x00000400 +#define VR_CFG_RX_ARB 0x00000800 +#define VR_CFG_TX_ARB 0x00001000 +#define VR_CFG_READMULTI 0x00002000 +#define VR_CFG_TX_PACE 0x00004000 +#define VR_CFG_TX_QDIS 0x00008000 +#define VR_CFG_ROMSEL0 0x00010000 +#define VR_CFG_ROMSEL1 0x00020000 +#define VR_CFG_ROMSEL2 0x00040000 +#define VR_CFG_ROMTIMESEL 0x00080000 +#define VR_CFG_RSVD0 0x00100000 +#define VR_CFG_ROMDLY 0x00200000 +#define VR_CFG_ROMOPT 0x00400000 +#define VR_CFG_RSVD1 0x00800000 +#define VR_CFG_BACKOFFOPT 0x01000000 +#define VR_CFG_BACKOFFMOD 0x02000000 +#define VR_CFG_CAPEFFECT 0x04000000 +#define VR_CFG_BACKOFFRAND 0x08000000 +#define VR_CFG_MAGICKPACKET 0x10000000 +#define VR_CFG_PCIREADLINE 0x20000000 +#define VR_CFG_DIAG 0x40000000 +#define VR_CFG_GPIOEN 0x80000000 + +/* Sticky HW bits */ +#define VR_STICKHW_DS0 0x01 +#define VR_STICKHW_DS1 0x02 +#define VR_STICKHW_WOL_ENB 0x04 +#define VR_STICKHW_WOL_STS 0x08 +#define VR_STICKHW_LEGWOL_ENB 0x80 + +/* + * Rhine TX/RX list structure. + */ + +struct vr_desc { + u_int32_t vr_status; + u_int32_t vr_ctl; + u_int32_t vr_ptr1; + u_int32_t vr_ptr2; +}; + +#define vr_data vr_ptr1 +#define vr_next vr_ptr2 + + +#define VR_RXSTAT_RXERR 0x00000001 +#define VR_RXSTAT_CRCERR 0x00000002 +#define VR_RXSTAT_FRAMEALIGNERR 0x00000004 +#define VR_RXSTAT_FIFOOFLOW 0x00000008 +#define VR_RXSTAT_GIANT 0x00000010 +#define VR_RXSTAT_RUNT 0x00000020 +#define VR_RXSTAT_BUSERR 0x00000040 +#define VR_RXSTAT_BUFFERR 0x00000080 +#define VR_RXSTAT_LASTFRAG 0x00000100 +#define VR_RXSTAT_FIRSTFRAG 0x00000200 +#define VR_RXSTAT_RLINK 0x00000400 +#define VR_RXSTAT_RX_PHYS 0x00000800 +#define VR_RXSTAT_RX_BROAD 0x00001000 +#define VR_RXSTAT_RX_MULTI 0x00002000 +#define VR_RXSTAT_RX_OK 0x00004000 +#define VR_RXSTAT_RXLEN 0x07FF0000 +#define VR_RXSTAT_RXLEN_EXT 0x78000000 +#define VR_RXSTAT_OWN 0x80000000 + +#define VR_RXBYTES(x) ((x & VR_RXSTAT_RXLEN) >> 16) +#define VR_RXSTAT (VR_RXSTAT_FIRSTFRAG|VR_RXSTAT_LASTFRAG|VR_RXSTAT_OWN) + +#define VR_RXCTL_BUFLEN 0x000007FF +#define VR_RXCTL_BUFLEN_EXT 0x00007800 +#define VR_RXCTL_CHAIN 0x00008000 +#define VR_RXCTL_RX_INTR 0x00800000 + +#define VR_RXCTL (VR_RXCTL_CHAIN|VR_RXCTL_RX_INTR) + +#define VR_TXSTAT_DEFER 0x00000001 +#define VR_TXSTAT_UNDERRUN 0x00000002 +#define VR_TXSTAT_COLLCNT 0x00000078 +#define VR_TXSTAT_SQE 0x00000080 +#define VR_TXSTAT_ABRT 0x00000100 +#define VR_TXSTAT_LATECOLL 0x00000200 +#define VR_TXSTAT_CARRLOST 0x00000400 +#define VR_TXSTAT_BUSERR 0x00002000 +#define VR_TXSTAT_JABTIMEO 0x00004000 +#define VR_TXSTAT_ERRSUM 0x00008000 +#define VR_TXSTAT_OWN 0x80000000 + +#define VR_TXCTL_BUFLEN 0x000007FF +#define VR_TXCTL_BUFLEN_EXT 0x00007800 +#define VR_TXCTL_TLINK 0x00008000 +#define VR_TXCTL_FIRSTFRAG 0x00200000 +#define VR_TXCTL_LASTFRAG 0x00400000 +#define VR_TXCTL_FINT 0x00800000 + + +#define VR_MAXFRAGS 16 +#define VR_RX_LIST_CNT 64 +#define VR_TX_LIST_CNT 128 +#define VR_MIN_FRAMELEN 60 +#define VR_FRAMELEN 1536 +#define VR_RXLEN 1520 + +#define VR_TXOWN(x) x->vr_ptr->vr_status + +struct vr_list_data { + struct vr_desc vr_rx_list[VR_RX_LIST_CNT]; + struct vr_desc vr_tx_list[VR_TX_LIST_CNT]; +}; + +struct vr_chain { + struct vr_desc *vr_ptr; + struct mbuf *vr_mbuf; + struct vr_chain *vr_nextdesc; +}; + +struct vr_chain_onefrag { + struct vr_desc *vr_ptr; + struct mbuf *vr_mbuf; + struct vr_chain_onefrag *vr_nextdesc; +}; + +struct vr_chain_data { + struct vr_chain_onefrag vr_rx_chain[VR_RX_LIST_CNT]; + struct vr_chain vr_tx_chain[VR_TX_LIST_CNT]; + + struct vr_chain_onefrag *vr_rx_head; + + struct vr_chain *vr_tx_head; + struct vr_chain *vr_tx_tail; + struct vr_chain *vr_tx_free; +}; + +struct vr_type { + u_int16_t vr_vid; + u_int16_t vr_did; + char *vr_name; +}; + +struct vr_mii_frame { + u_int8_t mii_stdelim; + u_int8_t mii_opcode; + u_int8_t mii_phyaddr; + u_int8_t mii_regaddr; + u_int8_t mii_turnaround; + u_int16_t mii_data; +}; + +/* + * MII constants + */ +#define VR_MII_STARTDELIM 0x01 +#define VR_MII_READOP 0x02 +#define VR_MII_WRITEOP 0x01 +#define VR_MII_TURNAROUND 0x02 + +#define VR_FLAG_FORCEDELAY 1 +#define VR_FLAG_SCHEDDELAY 2 +#define VR_FLAG_DELAYTIMEO 3 + +struct vr_softc { + struct arpcom arpcom; /* interface info */ + bus_space_handle_t vr_bhandle; /* bus space handle */ + bus_space_tag_t vr_btag; /* bus space tag */ + struct resource *vr_res; + struct resource *vr_irq; + void *vr_intrhand; + device_t vr_miibus; + struct vr_type *vr_info; /* Rhine adapter info */ + u_int8_t vr_unit; /* interface number */ + u_int8_t vr_type; + struct vr_list_data *vr_ldata; + struct vr_chain_data vr_cdata; + struct callout_handle vr_stat_ch; + struct mtx vr_mtx; +}; + +#define VR_LOCK(_sc) mtx_lock(&(_sc)->vr_mtx) +#define VR_UNLOCK(_sc) mtx_unlock(&(_sc)->vr_mtx) + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->vr_btag, sc->vr_bhandle, reg, val) +#define CSR_WRITE_2(sc, reg, val) \ + bus_space_write_2(sc->vr_btag, sc->vr_bhandle, reg, val) +#define CSR_WRITE_1(sc, reg, val) \ + bus_space_write_1(sc->vr_btag, sc->vr_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->vr_btag, sc->vr_bhandle, reg) +#define CSR_READ_2(sc, reg) \ + bus_space_read_2(sc->vr_btag, sc->vr_bhandle, reg) +#define CSR_READ_1(sc, reg) \ + bus_space_read_1(sc->vr_btag, sc->vr_bhandle, reg) + +#define VR_TIMEOUT 1000 +#define ETHER_ALIGN 2 + +/* + * General constants that are fun to know. + * + * VIA vendor ID + */ +#define VIA_VENDORID 0x1106 + +/* + * VIA Rhine device IDs. + */ +#define VIA_DEVICEID_RHINE 0x3043 +#define VIA_DEVICEID_RHINE_II 0x6100 +#define VIA_DEVICEID_RHINE_II_2 0x3065 + +/* + * Delta Electronics device ID. + */ +#define DELTA_VENDORID 0x1500 + +/* + * Delta device IDs. + */ +#define DELTA_DEVICEID_RHINE_II 0x1320 + +/* + * Addtron vendor ID. + */ +#define ADDTRON_VENDORID 0x4033 + +/* + * Addtron device IDs. + */ +#define ADDTRON_DEVICEID_RHINE_II 0x1320 + + +/* + * PCI low memory base and low I/O base register, and + * other PCI registers. + */ + +#define VR_PCI_VENDOR_ID 0x00 +#define VR_PCI_DEVICE_ID 0x02 +#define VR_PCI_COMMAND 0x04 +#define VR_PCI_STATUS 0x06 +#define VR_PCI_CLASSCODE 0x09 +#define VR_PCI_LATENCY_TIMER 0x0D +#define VR_PCI_HEADER_TYPE 0x0E +#define VR_PCI_LOIO 0x10 +#define VR_PCI_LOMEM 0x14 +#define VR_PCI_BIOSROM 0x30 +#define VR_PCI_INTLINE 0x3C +#define VR_PCI_INTPIN 0x3D +#define VR_PCI_MINGNT 0x3E +#define VR_PCI_MINLAT 0x0F +#define VR_PCI_RESETOPT 0x48 +#define VR_PCI_EEPROM_DATA 0x4C + +/* power management registers */ +#define VR_PCI_CAPID 0xDC /* 8 bits */ +#define VR_PCI_NEXTPTR 0xDD /* 8 bits */ +#define VR_PCI_PWRMGMTCAP 0xDE /* 16 bits */ +#define VR_PCI_PWRMGMTCTRL 0xE0 /* 16 bits */ + +#define VR_PSTATE_MASK 0x0003 +#define VR_PSTATE_D0 0x0000 +#define VR_PSTATE_D1 0x0002 +#define VR_PSTATE_D2 0x0002 +#define VR_PSTATE_D3 0x0003 +#define VR_PME_EN 0x0010 +#define VR_PME_STATUS 0x8000 + + +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) +#endif diff --git a/sys/pci/if_wb.c b/sys/pci/if_wb.c new file mode 100644 index 0000000..aa2f161 --- /dev/null +++ b/sys/pci/if_wb.c @@ -0,0 +1,1880 @@ +/* + * Copyright (c) 1997, 1998 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * Winbond fast ethernet PCI NIC driver + * + * Supports various cheap network adapters based on the Winbond W89C840F + * fast ethernet controller chip. This includes adapters manufactured by + * Winbond itself and some made by Linksys. + * + * Written by Bill Paul <wpaul@ctr.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * The Winbond W89C840F chip is a bus master; in some ways it resembles + * a DEC 'tulip' chip, only not as complicated. Unfortunately, it has + * one major difference which is that while the registers do many of + * the same things as a tulip adapter, the offsets are different: where + * tulip registers are typically spaced 8 bytes apart, the Winbond + * registers are spaced 4 bytes apart. The receiver filter is also + * programmed differently. + * + * Like the tulip, the Winbond chip uses small descriptors containing + * a status word, a control word and 32-bit areas that can either be used + * to point to two external data blocks, or to point to a single block + * and another descriptor in a linked list. Descriptors can be grouped + * together in blocks to form fixed length rings or can be chained + * together in linked lists. A single packet may be spread out over + * several descriptors if necessary. + * + * For the receive ring, this driver uses a linked list of descriptors, + * each pointing to a single mbuf cluster buffer, which us large enough + * to hold an entire packet. The link list is looped back to created a + * closed ring. + * + * For transmission, the driver creates a linked list of 'super descriptors' + * which each contain several individual descriptors linked toghether. + * Each 'super descriptor' contains WB_MAXFRAGS descriptors, which we + * abuse as fragment pointers. This allows us to use a buffer managment + * scheme very similar to that used in the ThunderLAN and Etherlink XL + * drivers. + * + * Autonegotiation is performed using the external PHY via the MII bus. + * The sample boards I have all use a Davicom PHY. + * + * Note: the author of the Linux driver for the Winbond chip alludes + * to some sort of flaw in the chip's design that seems to mandate some + * drastic workaround which signigicantly impairs transmit performance. + * I have no idea what he's on about: transmit performance with all + * three of my test boards seems fine. + */ + +#include "opt_bdg.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/queue.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#include <net/bpf.h> + +#include <vm/vm.h> /* for vtophys */ +#include <vm/pmap.h> /* for vtophys */ +#include <machine/bus_memio.h> +#include <machine/bus_pio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +/* "controller miibus0" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +#define WB_USEIOSPACE + +#include <pci/if_wbreg.h> + +MODULE_DEPEND(wb, miibus, 1, 1, 1); + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif + +/* + * Various supported device vendors/types and their names. + */ +static struct wb_type wb_devs[] = { + { WB_VENDORID, WB_DEVICEID_840F, + "Winbond W89C840F 10/100BaseTX" }, + { CP_VENDORID, CP_DEVICEID_RL100, + "Compex RL100-ATX 10/100baseTX" }, + { 0, 0, NULL } +}; + +static int wb_probe (device_t); +static int wb_attach (device_t); +static int wb_detach (device_t); + +static void wb_bfree (void *addr, void *args); +static int wb_newbuf (struct wb_softc *, + struct wb_chain_onefrag *, + struct mbuf *); +static int wb_encap (struct wb_softc *, struct wb_chain *, + struct mbuf *); + +static void wb_rxeof (struct wb_softc *); +static void wb_rxeoc (struct wb_softc *); +static void wb_txeof (struct wb_softc *); +static void wb_txeoc (struct wb_softc *); +static void wb_intr (void *); +static void wb_tick (void *); +static void wb_start (struct ifnet *); +static int wb_ioctl (struct ifnet *, u_long, caddr_t); +static void wb_init (void *); +static void wb_stop (struct wb_softc *); +static void wb_watchdog (struct ifnet *); +static void wb_shutdown (device_t); +static int wb_ifmedia_upd (struct ifnet *); +static void wb_ifmedia_sts (struct ifnet *, struct ifmediareq *); + +static void wb_eeprom_putbyte (struct wb_softc *, int); +static void wb_eeprom_getword (struct wb_softc *, int, u_int16_t *); +static void wb_read_eeprom (struct wb_softc *, caddr_t, int, int, int); +static void wb_mii_sync (struct wb_softc *); +static void wb_mii_send (struct wb_softc *, u_int32_t, int); +static int wb_mii_readreg (struct wb_softc *, struct wb_mii_frame *); +static int wb_mii_writereg (struct wb_softc *, struct wb_mii_frame *); + +static void wb_setcfg (struct wb_softc *, u_int32_t); +static u_int8_t wb_calchash (caddr_t); +static void wb_setmulti (struct wb_softc *); +static void wb_reset (struct wb_softc *); +static void wb_fixmedia (struct wb_softc *); +static int wb_list_rx_init (struct wb_softc *); +static int wb_list_tx_init (struct wb_softc *); + +static int wb_miibus_readreg (device_t, int, int); +static int wb_miibus_writereg (device_t, int, int, int); +static void wb_miibus_statchg (device_t); + +#ifdef WB_USEIOSPACE +#define WB_RES SYS_RES_IOPORT +#define WB_RID WB_PCI_LOIO +#else +#define WB_RES SYS_RES_MEMORY +#define WB_RID WB_PCI_LOMEM +#endif + +static device_method_t wb_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, wb_probe), + DEVMETHOD(device_attach, wb_attach), + DEVMETHOD(device_detach, wb_detach), + DEVMETHOD(device_shutdown, wb_shutdown), + + /* bus interface, for miibus */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, wb_miibus_readreg), + DEVMETHOD(miibus_writereg, wb_miibus_writereg), + DEVMETHOD(miibus_statchg, wb_miibus_statchg), + { 0, 0 } +}; + +static driver_t wb_driver = { + "wb", + wb_methods, + sizeof(struct wb_softc) +}; + +static devclass_t wb_devclass; + +DRIVER_MODULE(if_wb, pci, wb_driver, wb_devclass, 0, 0); +DRIVER_MODULE(miibus, wb, miibus_driver, miibus_devclass, 0, 0); + +#define WB_SETBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, \ + CSR_READ_4(sc, reg) | x) + +#define WB_CLRBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, \ + CSR_READ_4(sc, reg) & ~x) + +#define SIO_SET(x) \ + CSR_WRITE_4(sc, WB_SIO, \ + CSR_READ_4(sc, WB_SIO) | x) + +#define SIO_CLR(x) \ + CSR_WRITE_4(sc, WB_SIO, \ + CSR_READ_4(sc, WB_SIO) & ~x) + +/* + * Send a read command and address to the EEPROM, check for ACK. + */ +static void wb_eeprom_putbyte(sc, addr) + struct wb_softc *sc; + int addr; +{ + register int d, i; + + d = addr | WB_EECMD_READ; + + /* + * Feed in each bit and stobe the clock. + */ + for (i = 0x400; i; i >>= 1) { + if (d & i) { + SIO_SET(WB_SIO_EE_DATAIN); + } else { + SIO_CLR(WB_SIO_EE_DATAIN); + } + DELAY(100); + SIO_SET(WB_SIO_EE_CLK); + DELAY(150); + SIO_CLR(WB_SIO_EE_CLK); + DELAY(100); + } + + return; +} + +/* + * Read a word of data stored in the EEPROM at address 'addr.' + */ +static void wb_eeprom_getword(sc, addr, dest) + struct wb_softc *sc; + int addr; + u_int16_t *dest; +{ + register int i; + u_int16_t word = 0; + + /* Enter EEPROM access mode. */ + CSR_WRITE_4(sc, WB_SIO, WB_SIO_EESEL|WB_SIO_EE_CS); + + /* + * Send address of word we want to read. + */ + wb_eeprom_putbyte(sc, addr); + + CSR_WRITE_4(sc, WB_SIO, WB_SIO_EESEL|WB_SIO_EE_CS); + + /* + * Start reading bits from EEPROM. + */ + for (i = 0x8000; i; i >>= 1) { + SIO_SET(WB_SIO_EE_CLK); + DELAY(100); + if (CSR_READ_4(sc, WB_SIO) & WB_SIO_EE_DATAOUT) + word |= i; + SIO_CLR(WB_SIO_EE_CLK); + DELAY(100); + } + + /* Turn off EEPROM access mode. */ + CSR_WRITE_4(sc, WB_SIO, 0); + + *dest = word; + + return; +} + +/* + * Read a sequence of words from the EEPROM. + */ +static void wb_read_eeprom(sc, dest, off, cnt, swap) + struct wb_softc *sc; + caddr_t dest; + int off; + int cnt; + int swap; +{ + int i; + u_int16_t word = 0, *ptr; + + for (i = 0; i < cnt; i++) { + wb_eeprom_getword(sc, off + i, &word); + ptr = (u_int16_t *)(dest + (i * 2)); + if (swap) + *ptr = ntohs(word); + else + *ptr = word; + } + + return; +} + +/* + * Sync the PHYs by setting data bit and strobing the clock 32 times. + */ +static void wb_mii_sync(sc) + struct wb_softc *sc; +{ + register int i; + + SIO_SET(WB_SIO_MII_DIR|WB_SIO_MII_DATAIN); + + for (i = 0; i < 32; i++) { + SIO_SET(WB_SIO_MII_CLK); + DELAY(1); + SIO_CLR(WB_SIO_MII_CLK); + DELAY(1); + } + + return; +} + +/* + * Clock a series of bits through the MII. + */ +static void wb_mii_send(sc, bits, cnt) + struct wb_softc *sc; + u_int32_t bits; + int cnt; +{ + int i; + + SIO_CLR(WB_SIO_MII_CLK); + + for (i = (0x1 << (cnt - 1)); i; i >>= 1) { + if (bits & i) { + SIO_SET(WB_SIO_MII_DATAIN); + } else { + SIO_CLR(WB_SIO_MII_DATAIN); + } + DELAY(1); + SIO_CLR(WB_SIO_MII_CLK); + DELAY(1); + SIO_SET(WB_SIO_MII_CLK); + } +} + +/* + * Read an PHY register through the MII. + */ +static int wb_mii_readreg(sc, frame) + struct wb_softc *sc; + struct wb_mii_frame *frame; + +{ + int i, ack; + + WB_LOCK(sc); + + /* + * Set up frame for RX. + */ + frame->mii_stdelim = WB_MII_STARTDELIM; + frame->mii_opcode = WB_MII_READOP; + frame->mii_turnaround = 0; + frame->mii_data = 0; + + CSR_WRITE_4(sc, WB_SIO, 0); + + /* + * Turn on data xmit. + */ + SIO_SET(WB_SIO_MII_DIR); + + wb_mii_sync(sc); + + /* + * Send command/address info. + */ + wb_mii_send(sc, frame->mii_stdelim, 2); + wb_mii_send(sc, frame->mii_opcode, 2); + wb_mii_send(sc, frame->mii_phyaddr, 5); + wb_mii_send(sc, frame->mii_regaddr, 5); + + /* Idle bit */ + SIO_CLR((WB_SIO_MII_CLK|WB_SIO_MII_DATAIN)); + DELAY(1); + SIO_SET(WB_SIO_MII_CLK); + DELAY(1); + + /* Turn off xmit. */ + SIO_CLR(WB_SIO_MII_DIR); + /* Check for ack */ + SIO_CLR(WB_SIO_MII_CLK); + DELAY(1); + SIO_SET(WB_SIO_MII_CLK); + DELAY(1); + ack = CSR_READ_4(sc, WB_SIO) & WB_SIO_MII_DATAOUT; + SIO_CLR(WB_SIO_MII_CLK); + DELAY(1); + SIO_SET(WB_SIO_MII_CLK); + DELAY(1); + + /* + * Now try reading data bits. If the ack failed, we still + * need to clock through 16 cycles to keep the PHY(s) in sync. + */ + if (ack) { + for(i = 0; i < 16; i++) { + SIO_CLR(WB_SIO_MII_CLK); + DELAY(1); + SIO_SET(WB_SIO_MII_CLK); + DELAY(1); + } + goto fail; + } + + for (i = 0x8000; i; i >>= 1) { + SIO_CLR(WB_SIO_MII_CLK); + DELAY(1); + if (!ack) { + if (CSR_READ_4(sc, WB_SIO) & WB_SIO_MII_DATAOUT) + frame->mii_data |= i; + DELAY(1); + } + SIO_SET(WB_SIO_MII_CLK); + DELAY(1); + } + +fail: + + SIO_CLR(WB_SIO_MII_CLK); + DELAY(1); + SIO_SET(WB_SIO_MII_CLK); + DELAY(1); + + WB_UNLOCK(sc); + + if (ack) + return(1); + return(0); +} + +/* + * Write to a PHY register through the MII. + */ +static int wb_mii_writereg(sc, frame) + struct wb_softc *sc; + struct wb_mii_frame *frame; + +{ + WB_LOCK(sc); + + /* + * Set up frame for TX. + */ + + frame->mii_stdelim = WB_MII_STARTDELIM; + frame->mii_opcode = WB_MII_WRITEOP; + frame->mii_turnaround = WB_MII_TURNAROUND; + + /* + * Turn on data output. + */ + SIO_SET(WB_SIO_MII_DIR); + + wb_mii_sync(sc); + + wb_mii_send(sc, frame->mii_stdelim, 2); + wb_mii_send(sc, frame->mii_opcode, 2); + wb_mii_send(sc, frame->mii_phyaddr, 5); + wb_mii_send(sc, frame->mii_regaddr, 5); + wb_mii_send(sc, frame->mii_turnaround, 2); + wb_mii_send(sc, frame->mii_data, 16); + + /* Idle bit. */ + SIO_SET(WB_SIO_MII_CLK); + DELAY(1); + SIO_CLR(WB_SIO_MII_CLK); + DELAY(1); + + /* + * Turn off xmit. + */ + SIO_CLR(WB_SIO_MII_DIR); + + WB_UNLOCK(sc); + + return(0); +} + +static int wb_miibus_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct wb_softc *sc; + struct wb_mii_frame frame; + + sc = device_get_softc(dev); + + bzero((char *)&frame, sizeof(frame)); + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + wb_mii_readreg(sc, &frame); + + return(frame.mii_data); +} + +static int wb_miibus_writereg(dev, phy, reg, data) + device_t dev; + int phy, reg, data; +{ + struct wb_softc *sc; + struct wb_mii_frame frame; + + sc = device_get_softc(dev); + + bzero((char *)&frame, sizeof(frame)); + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + frame.mii_data = data; + + wb_mii_writereg(sc, &frame); + + return(0); +} + +static void wb_miibus_statchg(dev) + device_t dev; +{ + struct wb_softc *sc; + struct mii_data *mii; + + sc = device_get_softc(dev); + WB_LOCK(sc); + mii = device_get_softc(sc->wb_miibus); + wb_setcfg(sc, mii->mii_media_active); + WB_UNLOCK(sc); + + return; +} + +static u_int8_t wb_calchash(addr) + caddr_t addr; +{ + u_int32_t crc, carry; + int i, j; + u_int8_t c; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (i = 0; i < 6; i++) { + c = *(addr + i); + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); + crc <<= 1; + c >>= 1; + if (carry) + crc = (crc ^ 0x04c11db6) | carry; + } + } + + /* + * return the filter bit position + * Note: I arrived at the following nonsense + * through experimentation. It's not the usual way to + * generate the bit position but it's the only thing + * I could come up with that works. + */ + return(~(crc >> 26) & 0x0000003F); +} + +/* + * Program the 64-bit multicast hash filter. + */ +static void wb_setmulti(sc) + struct wb_softc *sc; +{ + struct ifnet *ifp; + int h = 0; + u_int32_t hashes[2] = { 0, 0 }; + struct ifmultiaddr *ifma; + u_int32_t rxfilt; + int mcnt = 0; + + ifp = &sc->arpcom.ac_if; + + rxfilt = CSR_READ_4(sc, WB_NETCFG); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + rxfilt |= WB_NETCFG_RX_MULTI; + CSR_WRITE_4(sc, WB_NETCFG, rxfilt); + CSR_WRITE_4(sc, WB_MAR0, 0xFFFFFFFF); + CSR_WRITE_4(sc, WB_MAR1, 0xFFFFFFFF); + return; + } + + /* first, zot all the existing hash bits */ + CSR_WRITE_4(sc, WB_MAR0, 0); + CSR_WRITE_4(sc, WB_MAR1, 0); + + /* now program new ones */ + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = wb_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + mcnt++; + } + + if (mcnt) + rxfilt |= WB_NETCFG_RX_MULTI; + else + rxfilt &= ~WB_NETCFG_RX_MULTI; + + CSR_WRITE_4(sc, WB_MAR0, hashes[0]); + CSR_WRITE_4(sc, WB_MAR1, hashes[1]); + CSR_WRITE_4(sc, WB_NETCFG, rxfilt); + + return; +} + +/* + * The Winbond manual states that in order to fiddle with the + * 'full-duplex' and '100Mbps' bits in the netconfig register, we + * first have to put the transmit and/or receive logic in the idle state. + */ +static void wb_setcfg(sc, media) + struct wb_softc *sc; + u_int32_t media; +{ + int i, restart = 0; + + if (CSR_READ_4(sc, WB_NETCFG) & (WB_NETCFG_TX_ON|WB_NETCFG_RX_ON)) { + restart = 1; + WB_CLRBIT(sc, WB_NETCFG, (WB_NETCFG_TX_ON|WB_NETCFG_RX_ON)); + + for (i = 0; i < WB_TIMEOUT; i++) { + DELAY(10); + if ((CSR_READ_4(sc, WB_ISR) & WB_ISR_TX_IDLE) && + (CSR_READ_4(sc, WB_ISR) & WB_ISR_RX_IDLE)) + break; + } + + if (i == WB_TIMEOUT) + printf("wb%d: failed to force tx and " + "rx to idle state\n", sc->wb_unit); + } + + if (IFM_SUBTYPE(media) == IFM_10_T) + WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_100MBPS); + else + WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_100MBPS); + + if ((media & IFM_GMASK) == IFM_FDX) + WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_FULLDUPLEX); + else + WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_FULLDUPLEX); + + if (restart) + WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON|WB_NETCFG_RX_ON); + + return; +} + +static void wb_reset(sc) + struct wb_softc *sc; +{ + register int i; + struct mii_data *mii; + + CSR_WRITE_4(sc, WB_NETCFG, 0); + CSR_WRITE_4(sc, WB_BUSCTL, 0); + CSR_WRITE_4(sc, WB_TXADDR, 0); + CSR_WRITE_4(sc, WB_RXADDR, 0); + + WB_SETBIT(sc, WB_BUSCTL, WB_BUSCTL_RESET); + WB_SETBIT(sc, WB_BUSCTL, WB_BUSCTL_RESET); + + for (i = 0; i < WB_TIMEOUT; i++) { + DELAY(10); + if (!(CSR_READ_4(sc, WB_BUSCTL) & WB_BUSCTL_RESET)) + break; + } + if (i == WB_TIMEOUT) + printf("wb%d: reset never completed!\n", sc->wb_unit); + + /* Wait a little while for the chip to get its brains in order. */ + DELAY(1000); + + if (sc->wb_miibus == NULL) + return; + + mii = device_get_softc(sc->wb_miibus); + if (mii == NULL) + return; + + if (mii->mii_instance) { + struct mii_softc *miisc; + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + mii_phy_reset(miisc); + } + + return; +} + +static void wb_fixmedia(sc) + struct wb_softc *sc; +{ + struct mii_data *mii = NULL; + struct ifnet *ifp; + u_int32_t media; + + if (sc->wb_miibus == NULL) + return; + + mii = device_get_softc(sc->wb_miibus); + ifp = &sc->arpcom.ac_if; + + mii_pollstat(mii); + if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) { + media = mii->mii_media_active & ~IFM_10_T; + media |= IFM_100_TX; + } else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) { + media = mii->mii_media_active & ~IFM_100_TX; + media |= IFM_10_T; + } else + return; + + ifmedia_set(&mii->mii_media, media); + + return; +} + +/* + * Probe for a Winbond chip. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + */ +static int wb_probe(dev) + device_t dev; +{ + struct wb_type *t; + + t = wb_devs; + + while(t->wb_name != NULL) { + if ((pci_get_vendor(dev) == t->wb_vid) && + (pci_get_device(dev) == t->wb_did)) { + device_set_desc(dev, t->wb_name); + return(0); + } + t++; + } + + return(ENXIO); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int wb_attach(dev) + device_t dev; +{ + u_char eaddr[ETHER_ADDR_LEN]; + u_int32_t command; + struct wb_softc *sc; + struct ifnet *ifp; + int unit, error = 0, rid; + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + + mtx_init(&sc->wb_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF | MTX_RECURSE); + WB_LOCK(sc); + + /* + * Handle power management nonsense. + */ + + if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { + u_int32_t iobase, membase, irq; + + /* Save important PCI config data. */ + iobase = pci_read_config(dev, WB_PCI_LOIO, 4); + membase = pci_read_config(dev, WB_PCI_LOMEM, 4); + irq = pci_read_config(dev, WB_PCI_INTLINE, 4); + + /* Reset the power state. */ + printf("wb%d: chip is in D%d power mode " + "-- setting to D0\n", unit, + pci_get_powerstate(dev)); + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + + /* Restore PCI config data. */ + pci_write_config(dev, WB_PCI_LOIO, iobase, 4); + pci_write_config(dev, WB_PCI_LOMEM, membase, 4); + pci_write_config(dev, WB_PCI_INTLINE, irq, 4); + } + + /* + * Map control/status registers. + */ + pci_enable_busmaster(dev); + pci_enable_io(dev, SYS_RES_IOPORT); + pci_enable_io(dev, SYS_RES_MEMORY); + command = pci_read_config(dev, PCIR_COMMAND, 4); + +#ifdef WB_USEIOSPACE + if (!(command & PCIM_CMD_PORTEN)) { + printf("wb%d: failed to enable I/O ports!\n", unit); + error = ENXIO; + goto fail; + } +#else + if (!(command & PCIM_CMD_MEMEN)) { + printf("wb%d: failed to enable memory mapping!\n", unit); + error = ENXIO; + goto fail; + } +#endif + + rid = WB_RID; + sc->wb_res = bus_alloc_resource(dev, WB_RES, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (sc->wb_res == NULL) { + printf("wb%d: couldn't map ports/memory\n", unit); + error = ENXIO; + goto fail; + } + + sc->wb_btag = rman_get_bustag(sc->wb_res); + sc->wb_bhandle = rman_get_bushandle(sc->wb_res); + + /* Allocate interrupt */ + rid = 0; + sc->wb_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->wb_irq == NULL) { + printf("wb%d: couldn't map interrupt\n", unit); + bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->wb_irq, INTR_TYPE_NET, + wb_intr, sc, &sc->wb_intrhand); + + if (error) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); + bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); + printf("wb%d: couldn't set up irq\n", unit); + goto fail; + } + + /* Save the cache line size. */ + sc->wb_cachesize = pci_read_config(dev, WB_PCI_CACHELEN, 4) & 0xFF; + + /* Reset the adapter. */ + wb_reset(sc); + + /* + * Get station address from the EEPROM. + */ + wb_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 0); + + /* + * A Winbond chip was detected. Inform the world. + */ + printf("wb%d: Ethernet address: %6D\n", unit, eaddr, ":"); + + sc->wb_unit = unit; + bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); + + sc->wb_ldata = contigmalloc(sizeof(struct wb_list_data) + 8, M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc->wb_ldata == NULL) { + printf("wb%d: no memory for list buffers!\n", unit); + bus_teardown_intr(dev, sc->wb_irq, sc->wb_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); + bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); + error = ENXIO; + goto fail; + } + + bzero(sc->wb_ldata, sizeof(struct wb_list_data)); + + ifp = &sc->arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = "wb"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = wb_ioctl; + ifp->if_output = ether_output; + ifp->if_start = wb_start; + ifp->if_watchdog = wb_watchdog; + ifp->if_init = wb_init; + ifp->if_baudrate = 10000000; + ifp->if_snd.ifq_maxlen = WB_TX_LIST_CNT - 1; + + /* + * Do MII setup. + */ + if (mii_phy_probe(dev, &sc->wb_miibus, + wb_ifmedia_upd, wb_ifmedia_sts)) { + bus_teardown_intr(dev, sc->wb_irq, sc->wb_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); + bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); + free(sc->wb_ldata_ptr, M_DEVBUF); + error = ENXIO; + goto fail; + } + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); + WB_UNLOCK(sc); + return(0); + +fail: + if (error) + device_delete_child(dev, sc->wb_miibus); + WB_UNLOCK(sc); + mtx_destroy(&sc->wb_mtx); + + return(error); +} + +static int wb_detach(dev) + device_t dev; +{ + struct wb_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + WB_LOCK(sc); + ifp = &sc->arpcom.ac_if; + + wb_stop(sc); + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + + /* Delete any miibus and phy devices attached to this interface */ + bus_generic_detach(dev); + device_delete_child(dev, sc->wb_miibus); + + bus_teardown_intr(dev, sc->wb_irq, sc->wb_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); + bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); + + free(sc->wb_ldata_ptr, M_DEVBUF); + + WB_UNLOCK(sc); + mtx_destroy(&sc->wb_mtx); + + return(0); +} + +/* + * Initialize the transmit descriptors. + */ +static int wb_list_tx_init(sc) + struct wb_softc *sc; +{ + struct wb_chain_data *cd; + struct wb_list_data *ld; + int i; + + cd = &sc->wb_cdata; + ld = sc->wb_ldata; + + for (i = 0; i < WB_TX_LIST_CNT; i++) { + cd->wb_tx_chain[i].wb_ptr = &ld->wb_tx_list[i]; + if (i == (WB_TX_LIST_CNT - 1)) { + cd->wb_tx_chain[i].wb_nextdesc = + &cd->wb_tx_chain[0]; + } else { + cd->wb_tx_chain[i].wb_nextdesc = + &cd->wb_tx_chain[i + 1]; + } + } + + cd->wb_tx_free = &cd->wb_tx_chain[0]; + cd->wb_tx_tail = cd->wb_tx_head = NULL; + + 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 wb_list_rx_init(sc) + struct wb_softc *sc; +{ + struct wb_chain_data *cd; + struct wb_list_data *ld; + int i; + + cd = &sc->wb_cdata; + ld = sc->wb_ldata; + + for (i = 0; i < WB_RX_LIST_CNT; i++) { + cd->wb_rx_chain[i].wb_ptr = + (struct wb_desc *)&ld->wb_rx_list[i]; + cd->wb_rx_chain[i].wb_buf = (void *)&ld->wb_rxbufs[i]; + if (wb_newbuf(sc, &cd->wb_rx_chain[i], NULL) == ENOBUFS) + return(ENOBUFS); + if (i == (WB_RX_LIST_CNT - 1)) { + cd->wb_rx_chain[i].wb_nextdesc = &cd->wb_rx_chain[0]; + ld->wb_rx_list[i].wb_next = + vtophys(&ld->wb_rx_list[0]); + } else { + cd->wb_rx_chain[i].wb_nextdesc = + &cd->wb_rx_chain[i + 1]; + ld->wb_rx_list[i].wb_next = + vtophys(&ld->wb_rx_list[i + 1]); + } + } + + cd->wb_rx_head = &cd->wb_rx_chain[0]; + + return(0); +} + +static void wb_bfree(buf, args) + void *buf; + void *args; +{ + return; +} + +/* + * Initialize an RX descriptor and attach an MBUF cluster. + */ +static int wb_newbuf(sc, c, m) + struct wb_softc *sc; + struct wb_chain_onefrag *c; + struct mbuf *m; +{ + struct mbuf *m_new = NULL; + + if (m == NULL) { + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(ENOBUFS); + m_new->m_data = c->wb_buf; + m_new->m_pkthdr.len = m_new->m_len = WB_BUFBYTES; + MEXTADD(m_new, c->wb_buf, WB_BUFBYTES, wb_bfree, NULL, 0, + EXT_NET_DRV); + } else { + m_new = m; + m_new->m_len = m_new->m_pkthdr.len = WB_BUFBYTES; + m_new->m_data = m_new->m_ext.ext_buf; + } + + m_adj(m_new, sizeof(u_int64_t)); + + c->wb_mbuf = m_new; + c->wb_ptr->wb_data = vtophys(mtod(m_new, caddr_t)); + c->wb_ptr->wb_ctl = WB_RXCTL_RLINK | 1536; + c->wb_ptr->wb_status = WB_RXSTAT; + + return(0); +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +static void wb_rxeof(sc) + struct wb_softc *sc; +{ + struct ether_header *eh; + struct mbuf *m = NULL; + struct ifnet *ifp; + struct wb_chain_onefrag *cur_rx; + int total_len = 0; + u_int32_t rxstat; + + ifp = &sc->arpcom.ac_if; + + while(!((rxstat = sc->wb_cdata.wb_rx_head->wb_ptr->wb_status) & + WB_RXSTAT_OWN)) { + struct mbuf *m0 = NULL; + + cur_rx = sc->wb_cdata.wb_rx_head; + sc->wb_cdata.wb_rx_head = cur_rx->wb_nextdesc; + + m = cur_rx->wb_mbuf; + + if ((rxstat & WB_RXSTAT_MIIERR) || + (WB_RXBYTES(cur_rx->wb_ptr->wb_status) < WB_MIN_FRAMELEN) || + (WB_RXBYTES(cur_rx->wb_ptr->wb_status) > 1536) || + !(rxstat & WB_RXSTAT_LASTFRAG) || + !(rxstat & WB_RXSTAT_RXCMP)) { + ifp->if_ierrors++; + wb_newbuf(sc, cur_rx, m); + printf("wb%x: receiver babbling: possible chip " + "bug, forcing reset\n", sc->wb_unit); + wb_fixmedia(sc); + wb_reset(sc); + wb_init(sc); + return; + } + + if (rxstat & WB_RXSTAT_RXERR) { + ifp->if_ierrors++; + wb_newbuf(sc, cur_rx, m); + break; + } + + /* No errors; receive the packet. */ + total_len = WB_RXBYTES(cur_rx->wb_ptr->wb_status); + + /* + * XXX The Winbond chip includes the CRC with every + * received frame, and there's no way to turn this + * behavior off (at least, I can't find anything in + * the manual that explains how to do it) so we have + * to trim off the CRC manually. + */ + total_len -= ETHER_CRC_LEN; + + m0 = m_devget(mtod(m, char *), total_len, ETHER_ALIGN, ifp, + NULL); + wb_newbuf(sc, cur_rx, m); + if (m0 == NULL) { + ifp->if_ierrors++; + break; + } + m = m0; + + ifp->if_ipackets++; + eh = mtod(m, struct ether_header *); + + /* Remove header from mbuf and pass it on. */ + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, eh, m); + } +} + +void wb_rxeoc(sc) + struct wb_softc *sc; +{ + wb_rxeof(sc); + + WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); + CSR_WRITE_4(sc, WB_RXADDR, vtophys(&sc->wb_ldata->wb_rx_list[0])); + WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); + if (CSR_READ_4(sc, WB_ISR) & WB_RXSTATE_SUSPEND) + CSR_WRITE_4(sc, WB_RXSTART, 0xFFFFFFFF); + + return; +} + +/* + * A frame was downloaded to the chip. It's safe for us to clean up + * the list buffers. + */ +static void wb_txeof(sc) + struct wb_softc *sc; +{ + struct wb_chain *cur_tx; + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + + /* Clear the timeout timer. */ + ifp->if_timer = 0; + + if (sc->wb_cdata.wb_tx_head == NULL) + return; + + /* + * Go through our tx list and free mbufs for those + * frames that have been transmitted. + */ + while(sc->wb_cdata.wb_tx_head->wb_mbuf != NULL) { + u_int32_t txstat; + + cur_tx = sc->wb_cdata.wb_tx_head; + txstat = WB_TXSTATUS(cur_tx); + + if ((txstat & WB_TXSTAT_OWN) || txstat == WB_UNSENT) + break; + + if (txstat & WB_TXSTAT_TXERR) { + ifp->if_oerrors++; + if (txstat & WB_TXSTAT_ABORT) + ifp->if_collisions++; + if (txstat & WB_TXSTAT_LATECOLL) + ifp->if_collisions++; + } + + ifp->if_collisions += (txstat & WB_TXSTAT_COLLCNT) >> 3; + + ifp->if_opackets++; + m_freem(cur_tx->wb_mbuf); + cur_tx->wb_mbuf = NULL; + + if (sc->wb_cdata.wb_tx_head == sc->wb_cdata.wb_tx_tail) { + sc->wb_cdata.wb_tx_head = NULL; + sc->wb_cdata.wb_tx_tail = NULL; + break; + } + + sc->wb_cdata.wb_tx_head = cur_tx->wb_nextdesc; + } + + return; +} + +/* + * TX 'end of channel' interrupt handler. + */ +static void wb_txeoc(sc) + struct wb_softc *sc; +{ + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + + ifp->if_timer = 0; + + if (sc->wb_cdata.wb_tx_head == NULL) { + ifp->if_flags &= ~IFF_OACTIVE; + sc->wb_cdata.wb_tx_tail = NULL; + } else { + if (WB_TXOWN(sc->wb_cdata.wb_tx_head) == WB_UNSENT) { + WB_TXOWN(sc->wb_cdata.wb_tx_head) = WB_TXSTAT_OWN; + ifp->if_timer = 5; + CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); + } + } + + return; +} + +static void wb_intr(arg) + void *arg; +{ + struct wb_softc *sc; + struct ifnet *ifp; + u_int32_t status; + + sc = arg; + WB_LOCK(sc); + ifp = &sc->arpcom.ac_if; + + if (!(ifp->if_flags & IFF_UP)) { + WB_UNLOCK(sc); + return; + } + + /* Disable interrupts. */ + CSR_WRITE_4(sc, WB_IMR, 0x00000000); + + for (;;) { + + status = CSR_READ_4(sc, WB_ISR); + if (status) + CSR_WRITE_4(sc, WB_ISR, status); + + if ((status & WB_INTRS) == 0) + break; + + if ((status & WB_ISR_RX_NOBUF) || (status & WB_ISR_RX_ERR)) { + ifp->if_ierrors++; + wb_reset(sc); + if (status & WB_ISR_RX_ERR) + wb_fixmedia(sc); + wb_init(sc); + continue; + } + + if (status & WB_ISR_RX_OK) + wb_rxeof(sc); + + if (status & WB_ISR_RX_IDLE) + wb_rxeoc(sc); + + if (status & WB_ISR_TX_OK) + wb_txeof(sc); + + if (status & WB_ISR_TX_NOBUF) + wb_txeoc(sc); + + if (status & WB_ISR_TX_IDLE) { + wb_txeof(sc); + if (sc->wb_cdata.wb_tx_head != NULL) { + WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); + CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); + } + } + + if (status & WB_ISR_TX_UNDERRUN) { + ifp->if_oerrors++; + wb_txeof(sc); + WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); + /* Jack up TX threshold */ + sc->wb_txthresh += WB_TXTHRESH_CHUNK; + WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_THRESH); + WB_SETBIT(sc, WB_NETCFG, WB_TXTHRESH(sc->wb_txthresh)); + WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); + } + + if (status & WB_ISR_BUS_ERR) { + wb_reset(sc); + wb_init(sc); + } + + } + + /* Re-enable interrupts. */ + CSR_WRITE_4(sc, WB_IMR, WB_INTRS); + + if (ifp->if_snd.ifq_head != NULL) { + wb_start(ifp); + } + + WB_UNLOCK(sc); + + return; +} + +static void wb_tick(xsc) + void *xsc; +{ + struct wb_softc *sc; + struct mii_data *mii; + + sc = xsc; + WB_LOCK(sc); + mii = device_get_softc(sc->wb_miibus); + + mii_tick(mii); + + sc->wb_stat_ch = timeout(wb_tick, sc, hz); + + WB_UNLOCK(sc); + + return; +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int wb_encap(sc, c, m_head) + struct wb_softc *sc; + struct wb_chain *c; + struct mbuf *m_head; +{ + int frag = 0; + struct wb_desc *f = NULL; + int total_len; + struct mbuf *m; + + /* + * Start packing the mbufs in this chain into + * the fragment pointers. Stop when we run out + * of fragments or hit the end of the mbuf chain. + */ + m = m_head; + total_len = 0; + + for (m = m_head, frag = 0; m != NULL; m = m->m_next) { + if (m->m_len != 0) { + if (frag == WB_MAXFRAGS) + break; + total_len += m->m_len; + f = &c->wb_ptr->wb_frag[frag]; + f->wb_ctl = WB_TXCTL_TLINK | m->m_len; + if (frag == 0) { + f->wb_ctl |= WB_TXCTL_FIRSTFRAG; + f->wb_status = 0; + } else + f->wb_status = WB_TXSTAT_OWN; + f->wb_next = vtophys(&c->wb_ptr->wb_frag[frag + 1]); + f->wb_data = vtophys(mtod(m, vm_offset_t)); + frag++; + } + } + + /* + * Handle special case: we used up all 16 fragments, + * but we have more mbufs left in the chain. Copy the + * data into an mbuf cluster. Note that we don't + * bother clearing the values in the other fragment + * pointers/counters; it wouldn't gain us anything, + * and would waste cycles. + */ + if (m != NULL) { + struct mbuf *m_new = NULL; + + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(1); + if (m_head->m_pkthdr.len > MHLEN) { + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + return(1); + } + } + m_copydata(m_head, 0, m_head->m_pkthdr.len, + mtod(m_new, caddr_t)); + m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; + m_freem(m_head); + m_head = m_new; + f = &c->wb_ptr->wb_frag[0]; + f->wb_status = 0; + f->wb_data = vtophys(mtod(m_new, caddr_t)); + f->wb_ctl = total_len = m_new->m_len; + f->wb_ctl |= WB_TXCTL_TLINK|WB_TXCTL_FIRSTFRAG; + frag = 1; + } + + if (total_len < WB_MIN_FRAMELEN) { + f = &c->wb_ptr->wb_frag[frag]; + f->wb_ctl = WB_MIN_FRAMELEN - total_len; + f->wb_data = vtophys(&sc->wb_cdata.wb_pad); + f->wb_ctl |= WB_TXCTL_TLINK; + f->wb_status = WB_TXSTAT_OWN; + frag++; + } + + c->wb_mbuf = m_head; + c->wb_lastdesc = frag - 1; + WB_TXCTL(c) |= WB_TXCTL_LASTFRAG; + WB_TXNEXT(c) = vtophys(&c->wb_nextdesc->wb_ptr->wb_frag[0]); + + return(0); +} + +/* + * Main transmit routine. To avoid having to do mbuf copies, we put pointers + * to the mbuf data regions directly in the transmit lists. We also save a + * copy of the pointers since the transmit list fragment pointers are + * physical addresses. + */ + +static void wb_start(ifp) + struct ifnet *ifp; +{ + struct wb_softc *sc; + struct mbuf *m_head = NULL; + struct wb_chain *cur_tx = NULL, *start_tx; + + sc = ifp->if_softc; + WB_LOCK(sc); + + /* + * Check for an available queue slot. If there are none, + * punt. + */ + if (sc->wb_cdata.wb_tx_free->wb_mbuf != NULL) { + ifp->if_flags |= IFF_OACTIVE; + WB_UNLOCK(sc); + return; + } + + start_tx = sc->wb_cdata.wb_tx_free; + + while(sc->wb_cdata.wb_tx_free->wb_mbuf == NULL) { + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + /* Pick a descriptor off the free list. */ + cur_tx = sc->wb_cdata.wb_tx_free; + sc->wb_cdata.wb_tx_free = cur_tx->wb_nextdesc; + + /* Pack the data into the descriptor. */ + wb_encap(sc, cur_tx, m_head); + + if (cur_tx != start_tx) + WB_TXOWN(cur_tx) = WB_TXSTAT_OWN; + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, cur_tx->wb_mbuf); + } + + /* + * If there are no packets queued, bail. + */ + if (cur_tx == NULL) { + WB_UNLOCK(sc); + return; + } + + /* + * Place the request for the upload interrupt + * in the last descriptor in the chain. This way, if + * we're chaining several packets at once, we'll only + * get an interupt once for the whole chain rather than + * once for each packet. + */ + WB_TXCTL(cur_tx) |= WB_TXCTL_FINT; + cur_tx->wb_ptr->wb_frag[0].wb_ctl |= WB_TXCTL_FINT; + sc->wb_cdata.wb_tx_tail = cur_tx; + + if (sc->wb_cdata.wb_tx_head == NULL) { + sc->wb_cdata.wb_tx_head = start_tx; + WB_TXOWN(start_tx) = WB_TXSTAT_OWN; + CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); + } else { + /* + * We need to distinguish between the case where + * the own bit is clear because the chip cleared it + * and where the own bit is clear because we haven't + * set it yet. The magic value WB_UNSET is just some + * ramdomly chosen number which doesn't have the own + * bit set. When we actually transmit the frame, the + * status word will have _only_ the own bit set, so + * the txeoc handler will be able to tell if it needs + * to initiate another transmission to flush out pending + * frames. + */ + WB_TXOWN(start_tx) = WB_UNSENT; + } + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + WB_UNLOCK(sc); + + return; +} + +static void wb_init(xsc) + void *xsc; +{ + struct wb_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + int i; + struct mii_data *mii; + + WB_LOCK(sc); + mii = device_get_softc(sc->wb_miibus); + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + wb_stop(sc); + wb_reset(sc); + + sc->wb_txthresh = WB_TXTHRESH_INIT; + + /* + * Set cache alignment and burst length. + */ +#ifdef foo + CSR_WRITE_4(sc, WB_BUSCTL, WB_BUSCTL_CONFIG); + WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_THRESH); + WB_SETBIT(sc, WB_NETCFG, WB_TXTHRESH(sc->wb_txthresh)); +#endif + + CSR_WRITE_4(sc, WB_BUSCTL, WB_BUSCTL_MUSTBEONE|WB_BUSCTL_ARBITRATION); + WB_SETBIT(sc, WB_BUSCTL, WB_BURSTLEN_16LONG); + switch(sc->wb_cachesize) { + case 32: + WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_32LONG); + break; + case 16: + WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_16LONG); + break; + case 8: + WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_8LONG); + break; + case 0: + default: + WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_NONE); + break; + } + + /* This doesn't tend to work too well at 100Mbps. */ + WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_EARLY_ON); + + /* Init our MAC address */ + for (i = 0; i < ETHER_ADDR_LEN; i++) { + CSR_WRITE_1(sc, WB_NODE0 + i, sc->arpcom.ac_enaddr[i]); + } + + /* Init circular RX list. */ + if (wb_list_rx_init(sc) == ENOBUFS) { + printf("wb%d: initialization failed: no " + "memory for rx buffers\n", sc->wb_unit); + wb_stop(sc); + WB_UNLOCK(sc); + return; + } + + /* Init TX descriptors. */ + wb_list_tx_init(sc); + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) { + WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ALLPHYS); + } else { + WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ALLPHYS); + } + + /* + * Set capture broadcast bit to capture broadcast frames. + */ + if (ifp->if_flags & IFF_BROADCAST) { + WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_BROAD); + } else { + WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_BROAD); + } + + /* + * Program the multicast filter, if necessary. + */ + wb_setmulti(sc); + + /* + * Load the address of the RX list. + */ + WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); + CSR_WRITE_4(sc, WB_RXADDR, vtophys(&sc->wb_ldata->wb_rx_list[0])); + + /* + * Enable interrupts. + */ + CSR_WRITE_4(sc, WB_IMR, WB_INTRS); + CSR_WRITE_4(sc, WB_ISR, 0xFFFFFFFF); + + /* Enable receiver and transmitter. */ + WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); + CSR_WRITE_4(sc, WB_RXSTART, 0xFFFFFFFF); + + WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); + CSR_WRITE_4(sc, WB_TXADDR, vtophys(&sc->wb_ldata->wb_tx_list[0])); + WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); + + mii_mediachg(mii); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + sc->wb_stat_ch = timeout(wb_tick, sc, hz); + WB_UNLOCK(sc); + + return; +} + +/* + * Set media options. + */ +static int wb_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct wb_softc *sc; + + sc = ifp->if_softc; + + if (ifp->if_flags & IFF_UP) + wb_init(sc); + + return(0); +} + +/* + * Report current media status. + */ +static void wb_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct wb_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + + mii = device_get_softc(sc->wb_miibus); + + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + + return; +} + +static int wb_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct wb_softc *sc = ifp->if_softc; + struct mii_data *mii; + struct ifreq *ifr = (struct ifreq *) data; + int error = 0; + + WB_LOCK(sc); + + switch(command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + wb_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) + wb_stop(sc); + } + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + wb_setmulti(sc); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = device_get_softc(sc->wb_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + break; + default: + error = EINVAL; + break; + } + + WB_UNLOCK(sc); + + return(error); +} + +static void wb_watchdog(ifp) + struct ifnet *ifp; +{ + struct wb_softc *sc; + + sc = ifp->if_softc; + + WB_LOCK(sc); + ifp->if_oerrors++; + printf("wb%d: watchdog timeout\n", sc->wb_unit); +#ifdef foo + if (!(wb_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT)) + printf("wb%d: no carrier - transceiver cable problem?\n", + sc->wb_unit); +#endif + wb_stop(sc); + wb_reset(sc); + wb_init(sc); + + if (ifp->if_snd.ifq_head != NULL) + wb_start(ifp); + WB_UNLOCK(sc); + + return; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void wb_stop(sc) + struct wb_softc *sc; +{ + register int i; + struct ifnet *ifp; + + WB_LOCK(sc); + ifp = &sc->arpcom.ac_if; + ifp->if_timer = 0; + + untimeout(wb_tick, sc, sc->wb_stat_ch); + + WB_CLRBIT(sc, WB_NETCFG, (WB_NETCFG_RX_ON|WB_NETCFG_TX_ON)); + CSR_WRITE_4(sc, WB_IMR, 0x00000000); + CSR_WRITE_4(sc, WB_TXADDR, 0x00000000); + CSR_WRITE_4(sc, WB_RXADDR, 0x00000000); + + /* + * Free data in the RX lists. + */ + for (i = 0; i < WB_RX_LIST_CNT; i++) { + if (sc->wb_cdata.wb_rx_chain[i].wb_mbuf != NULL) { + m_freem(sc->wb_cdata.wb_rx_chain[i].wb_mbuf); + sc->wb_cdata.wb_rx_chain[i].wb_mbuf = NULL; + } + } + bzero((char *)&sc->wb_ldata->wb_rx_list, + sizeof(sc->wb_ldata->wb_rx_list)); + + /* + * Free the TX list buffers. + */ + for (i = 0; i < WB_TX_LIST_CNT; i++) { + if (sc->wb_cdata.wb_tx_chain[i].wb_mbuf != NULL) { + m_freem(sc->wb_cdata.wb_tx_chain[i].wb_mbuf); + sc->wb_cdata.wb_tx_chain[i].wb_mbuf = NULL; + } + } + + bzero((char *)&sc->wb_ldata->wb_tx_list, + sizeof(sc->wb_ldata->wb_tx_list)); + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + WB_UNLOCK(sc); + + return; +} + +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static void wb_shutdown(dev) + device_t dev; +{ + struct wb_softc *sc; + + sc = device_get_softc(dev); + wb_stop(sc); + + return; +} diff --git a/sys/pci/if_wbreg.h b/sys/pci/if_wbreg.h new file mode 100644 index 0000000..983886b --- /dev/null +++ b/sys/pci/if_wbreg.h @@ -0,0 +1,468 @@ +/* + * Copyright (c) 1997, 1998 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * Winbond register definitions. + */ + +#define WB_BUSCTL 0x00 /* bus control */ +#define WB_TXSTART 0x04 /* tx start demand */ +#define WB_RXSTART 0x08 /* rx start demand */ +#define WB_RXADDR 0x0C /* rx descriptor list start addr */ +#define WB_TXADDR 0x10 /* tx descriptor list start addr */ +#define WB_ISR 0x14 /* interrupt status register */ +#define WB_NETCFG 0x18 /* network config register */ +#define WB_IMR 0x1C /* interrupt mask */ +#define WB_FRAMESDISCARDED 0x20 /* # of discarded frames */ +#define WB_SIO 0x24 /* MII and ROM/EEPROM access */ +#define WB_BOOTROMADDR 0x28 +#define WB_TIMER 0x2C /* general timer */ +#define WB_CURRXCTL 0x30 /* current RX descriptor */ +#define WB_CURRXBUF 0x34 /* current RX buffer */ +#define WB_MAR0 0x38 /* multicast filter 0 */ +#define WB_MAR1 0x3C /* multicast filter 1 */ +#define WB_NODE0 0x40 /* station address 0 */ +#define WB_NODE1 0x44 /* station address 1 */ +#define WB_BOOTROMSIZE 0x48 /* boot ROM size */ +#define WB_CURTXCTL 0x4C /* current TX descriptor */ +#define WB_CURTXBUF 0x50 /* current TX buffer */ + +/* + * Bus control bits. + */ +#define WB_BUSCTL_RESET 0x00000001 +#define WB_BUSCTL_ARBITRATION 0x00000002 +#define WB_BUSCTL_SKIPLEN 0x0000007C +#define WB_BUSCTL_BUF_BIGENDIAN 0x00000080 +#define WB_BUSCTL_BURSTLEN 0x00003F00 +#define WB_BUSCTL_CACHEALIGN 0x0000C000 +#define WB_BUSCTL_DES_BIGENDIAN 0x00100000 +#define WB_BUSCTL_WAIT 0x00200000 +#define WB_BUSCTL_MUSTBEONE 0x00400000 + +#define WB_SKIPLEN_1LONG 0x00000004 +#define WB_SKIPLEN_2LONG 0x00000008 +#define WB_SKIPLEN_3LONG 0x00000010 +#define WB_SKIPLEN_4LONG 0x00000020 +#define WB_SKIPLEN_5LONG 0x00000040 + +#define WB_CACHEALIGN_NONE 0x00000000 +#define WB_CACHEALIGN_8LONG 0x00004000 +#define WB_CACHEALIGN_16LONG 0x00008000 +#define WB_CACHEALIGN_32LONG 0x0000C000 + +#define WB_BURSTLEN_USECA 0x00000000 +#define WB_BURSTLEN_1LONG 0x00000100 +#define WB_BURSTLEN_2LONG 0x00000200 +#define WB_BURSTLEN_4LONG 0x00000400 +#define WB_BURSTLEN_8LONG 0x00000800 +#define WB_BURSTLEN_16LONG 0x00001000 +#define WB_BURSTLEN_32LONG 0x00002000 + +#define WB_BUSCTL_CONFIG (WB_CACHEALIGN_8LONG|WB_SKIPLEN_3LONG| \ + WB_BURSTLEN_8LONG) + +/* + * Interrupt status bits. + */ +#define WB_ISR_TX_OK 0x00000001 +#define WB_ISR_TX_IDLE 0x00000002 +#define WB_ISR_TX_NOBUF 0x00000004 +#define WB_ISR_RX_EARLY 0x00000008 +#define WB_ISR_RX_ERR 0x00000010 +#define WB_ISR_TX_UNDERRUN 0x00000020 +#define WB_ISR_RX_OK 0x00000040 +#define WB_ISR_RX_NOBUF 0x00000080 +#define WB_ISR_RX_IDLE 0x00000100 +#define WB_ISR_TX_EARLY 0x00000400 +#define WB_ISR_TIMER_EXPIRED 0x00000800 +#define WB_ISR_BUS_ERR 0x00002000 +#define WB_ISR_ABNORMAL 0x00008000 +#define WB_ISR_NORMAL 0x00010000 +#define WB_ISR_RX_STATE 0x000E0000 +#define WB_ISR_TX_STATE 0x00700000 +#define WB_ISR_BUSERRTYPE 0x03800000 + +/* + * The RX_STATE and TX_STATE fields are not described anywhere in the + * Winbond datasheet, however it appears that the Winbond chip is an + * attempt at a DEC 'tulip' clone, hence the ISR register is identical + * to that of the tulip chip and we can steal the bit definitions from + * the tulip documentation. + */ +#define WB_RXSTATE_STOPPED 0x00000000 /* 000 - Stopped */ +#define WB_RXSTATE_FETCH 0x00020000 /* 001 - Fetching descriptor */ +#define WB_RXSTATE_ENDCHECK 0x00040000 /* 010 - check for rx end */ +#define WB_RXSTATE_WAIT 0x00060000 /* 011 - waiting for packet */ +#define WB_RXSTATE_SUSPEND 0x00080000 /* 100 - suspend rx */ +#define WB_RXSTATE_CLOSE 0x000A0000 /* 101 - close tx desc */ +#define WB_RXSTATE_FLUSH 0x000C0000 /* 110 - flush from FIFO */ +#define WB_RXSTATE_DEQUEUE 0x000E0000 /* 111 - dequeue from FIFO */ + +#define WB_TXSTATE_RESET 0x00000000 /* 000 - reset */ +#define WB_TXSTATE_FETCH 0x00100000 /* 001 - fetching descriptor */ +#define WB_TXSTATE_WAITEND 0x00200000 /* 010 - wait for tx end */ +#define WB_TXSTATE_READING 0x00300000 /* 011 - read and enqueue */ +#define WB_TXSTATE_RSVD 0x00400000 /* 100 - reserved */ +#define WB_TXSTATE_SETUP 0x00500000 /* 101 - setup packet */ +#define WB_TXSTATE_SUSPEND 0x00600000 /* 110 - suspend tx */ +#define WB_TXSTATE_CLOSE 0x00700000 /* 111 - close tx desc */ + +/* + * Network config bits. + */ +#define WB_NETCFG_RX_ON 0x00000002 +#define WB_NETCFG_RX_ALLPHYS 0x00000008 +#define WB_NETCFG_RX_MULTI 0x00000010 +#define WB_NETCFG_RX_BROAD 0x00000020 +#define WB_NETCFG_RX_RUNT 0x00000040 +#define WB_NETCFG_RX_ERR 0x00000080 +#define WB_NETCFG_FULLDUPLEX 0x00000200 +#define WB_NETCFG_LOOPBACK 0x00000C00 +#define WB_NETCFG_TX_ON 0x00002000 +#define WB_NETCFG_TX_THRESH 0x001FC000 +#define WB_NETCFG_RX_EARLYTHRSH 0x1FE00000 +#define WB_NETCFG_100MBPS 0x20000000 +#define WB_NETCFG_TX_EARLY_ON 0x40000000 +#define WB_NETCFG_RX_EARLY_ON 0x80000000 + +/* + * The tx threshold can be adjusted in increments of 32 bytes. + */ +#define WB_TXTHRESH(x) ((x >> 5) << 14) +#define WB_TXTHRESH_CHUNK 32 +#define WB_TXTHRESH_INIT 0 /*72*/ + +/* + * Interrupt mask bits. + */ +#define WB_IMR_TX_OK 0x00000001 +#define WB_IMR_TX_IDLE 0x00000002 +#define WB_IMR_TX_NOBUF 0x00000004 +#define WB_IMR_RX_EARLY 0x00000008 +#define WB_IMR_RX_ERR 0x00000010 +#define WB_IMR_TX_UNDERRUN 0x00000020 +#define WB_IMR_RX_OK 0x00000040 +#define WB_IMR_RX_NOBUF 0x00000080 +#define WB_IMR_RX_IDLE 0x00000100 +#define WB_IMR_TX_EARLY 0x00000400 +#define WB_IMR_TIMER_EXPIRED 0x00000800 +#define WB_IMR_BUS_ERR 0x00002000 +#define WB_IMR_ABNORMAL 0x00008000 +#define WB_IMR_NORMAL 0x00010000 + +#define WB_INTRS \ + (WB_IMR_RX_OK|WB_IMR_TX_OK|WB_IMR_RX_NOBUF|WB_IMR_RX_ERR| \ + WB_IMR_TX_NOBUF|WB_IMR_TX_UNDERRUN|WB_IMR_BUS_ERR| \ + WB_IMR_ABNORMAL|WB_IMR_NORMAL|WB_IMR_TX_EARLY) +/* + * Serial I/O (EEPROM/ROM) bits. + */ +#define WB_SIO_EE_CS 0x00000001 /* EEPROM chip select */ +#define WB_SIO_EE_CLK 0x00000002 /* EEPROM clock */ +#define WB_SIO_EE_DATAIN 0x00000004 /* EEPROM data output */ +#define WB_SIO_EE_DATAOUT 0x00000008 /* EEPROM data input */ +#define WB_SIO_ROMDATA4 0x00000010 +#define WB_SIO_ROMDATA5 0x00000020 +#define WB_SIO_ROMDATA6 0x00000040 +#define WB_SIO_ROMDATA7 0x00000080 +#define WB_SIO_ROMCTL_WRITE 0x00000200 +#define WB_SIO_ROMCTL_READ 0x00000400 +#define WB_SIO_EESEL 0x00000800 +#define WB_SIO_MII_CLK 0x00010000 /* MDIO clock */ +#define WB_SIO_MII_DATAIN 0x00020000 /* MDIO data out */ +#define WB_SIO_MII_DIR 0x00040000 /* MDIO dir */ +#define WB_SIO_MII_DATAOUT 0x00080000 /* MDIO data in */ + +#define WB_EECMD_WRITE 0x140 +#define WB_EECMD_READ 0x180 +#define WB_EECMD_ERASE 0x1c0 + +/* + * Winbond TX/RX descriptor structure. + */ + +struct wb_desc { + u_int32_t wb_status; + u_int32_t wb_ctl; + u_int32_t wb_ptr1; + u_int32_t wb_ptr2; +}; + +#define wb_data wb_ptr1 +#define wb_next wb_ptr2 + +#define WB_RXSTAT_CRCERR 0x00000002 +#define WB_RXSTAT_DRIBBLE 0x00000004 +#define WB_RXSTAT_MIIERR 0x00000008 +#define WB_RXSTAT_LATEEVENT 0x00000040 +#define WB_RXSTAT_GIANT 0x00000080 +#define WB_RXSTAT_LASTFRAG 0x00000100 +#define WB_RXSTAT_FIRSTFRAG 0x00000200 +#define WB_RXSTAT_MULTICAST 0x00000400 +#define WB_RXSTAT_RUNT 0x00000800 +#define WB_RXSTAT_RXTYPE 0x00003000 +#define WB_RXSTAT_RXERR 0x00008000 +#define WB_RXSTAT_RXLEN 0x3FFF0000 +#define WB_RXSTAT_RXCMP 0x40000000 +#define WB_RXSTAT_OWN 0x80000000 + +#define WB_RXBYTES(x) ((x & WB_RXSTAT_RXLEN) >> 16) +#define WB_RXSTAT (WB_RXSTAT_FIRSTFRAG|WB_RXSTAT_LASTFRAG|WB_RXSTAT_OWN) + +#define WB_RXCTL_BUFLEN1 0x00000FFF +#define WB_RXCTL_BUFLEN2 0x00FFF000 +#define WB_RXCTL_RLINK 0x01000000 +#define WB_RXCTL_RLAST 0x02000000 + +#define WB_TXSTAT_DEFER 0x00000001 +#define WB_TXSTAT_UNDERRUN 0x00000002 +#define WB_TXSTAT_COLLCNT 0x00000078 +#define WB_TXSTAT_SQE 0x00000080 +#define WB_TXSTAT_ABORT 0x00000100 +#define WB_TXSTAT_LATECOLL 0x00000200 +#define WB_TXSTAT_NOCARRIER 0x00000400 +#define WB_TXSTAT_CARRLOST 0x00000800 +#define WB_TXSTAT_TXERR 0x00001000 +#define WB_TXSTAT_OWN 0x80000000 + +#define WB_TXCTL_BUFLEN1 0x000007FF +#define WB_TXCTL_BUFLEN2 0x003FF800 +#define WB_TXCTL_PAD 0x00800000 +#define WB_TXCTL_TLINK 0x01000000 +#define WB_TXCTL_TLAST 0x02000000 +#define WB_TXCTL_NOCRC 0x08000000 +#define WB_TXCTL_FIRSTFRAG 0x20000000 +#define WB_TXCTL_LASTFRAG 0x40000000 +#define WB_TXCTL_FINT 0x80000000 + +#define WB_MAXFRAGS 16 +#define WB_RX_LIST_CNT 64 +#define WB_TX_LIST_CNT 128 +#define WB_MIN_FRAMELEN 60 +#define ETHER_ALIGN 2 + +/* + * A transmit 'super descriptor' is actually WB_MAXFRAGS regular + * descriptors clumped together. The idea here is to emulate the + * multi-fragment descriptor layout found in devices such as the + * Texas Instruments ThunderLAN and 3Com boomerang and cylone chips. + * The advantage to using this scheme is that it avoids buffer copies. + * The disadvantage is that there's a certain amount of overhead due + * to the fact that each 'fragment' is 16 bytes long. In my tests, + * this limits top speed to about 10.5MB/sec. It should be more like + * 11.5MB/sec. However, the upshot is that you can achieve better + * results on slower machines: a Pentium 200 can pump out packets at + * same speed as a PII 400. + */ +struct wb_txdesc { + struct wb_desc wb_frag[WB_MAXFRAGS]; +}; + +#define WB_TXNEXT(x) x->wb_ptr->wb_frag[x->wb_lastdesc].wb_next +#define WB_TXSTATUS(x) x->wb_ptr->wb_frag[x->wb_lastdesc].wb_status +#define WB_TXCTL(x) x->wb_ptr->wb_frag[x->wb_lastdesc].wb_ctl +#define WB_TXDATA(x) x->wb_ptr->wb_frag[x->wb_lastdesc].wb_data + +#define WB_TXOWN(x) x->wb_ptr->wb_frag[0].wb_status + +#define WB_UNSENT 0x1234 + +#define WB_BUFBYTES (1024 * sizeof(u_int32_t)) + +struct wb_buf { + u_int32_t wb_data[1024]; +}; + +struct wb_list_data { + struct wb_buf wb_rxbufs[WB_RX_LIST_CNT]; + struct wb_desc wb_rx_list[WB_RX_LIST_CNT]; + struct wb_txdesc wb_tx_list[WB_TX_LIST_CNT]; +}; + +struct wb_chain { + struct wb_txdesc *wb_ptr; + struct mbuf *wb_mbuf; + struct wb_chain *wb_nextdesc; + u_int8_t wb_lastdesc; +}; + +struct wb_chain_onefrag { + struct wb_desc *wb_ptr; + struct mbuf *wb_mbuf; + void *wb_buf; + struct wb_chain_onefrag *wb_nextdesc; + u_int8_t wb_rlast; +}; + +struct wb_chain_data { + u_int8_t wb_pad[WB_MIN_FRAMELEN]; + struct wb_chain_onefrag wb_rx_chain[WB_RX_LIST_CNT]; + struct wb_chain wb_tx_chain[WB_TX_LIST_CNT]; + + struct wb_chain_onefrag *wb_rx_head; + + struct wb_chain *wb_tx_head; + struct wb_chain *wb_tx_tail; + struct wb_chain *wb_tx_free; +}; + +struct wb_type { + u_int16_t wb_vid; + u_int16_t wb_did; + char *wb_name; +}; + +struct wb_mii_frame { + u_int8_t mii_stdelim; + u_int8_t mii_opcode; + u_int8_t mii_phyaddr; + u_int8_t mii_regaddr; + u_int8_t mii_turnaround; + u_int16_t mii_data; +}; + +/* + * MII constants + */ +#define WB_MII_STARTDELIM 0x01 +#define WB_MII_READOP 0x02 +#define WB_MII_WRITEOP 0x01 +#define WB_MII_TURNAROUND 0x02 + +struct wb_softc { + struct arpcom arpcom; /* interface info */ + device_t wb_miibus; + bus_space_handle_t wb_bhandle; + bus_space_tag_t wb_btag; + struct resource *wb_res; + struct resource *wb_irq; + void *wb_intrhand; + struct wb_type *wb_info; /* Winbond adapter info */ + u_int8_t wb_unit; /* interface number */ + u_int8_t wb_type; + u_int16_t wb_txthresh; + int wb_cachesize; + caddr_t wb_ldata_ptr; + struct wb_list_data *wb_ldata; + struct wb_chain_data wb_cdata; + struct callout_handle wb_stat_ch; + struct mtx wb_mtx; +}; + +#define WB_LOCK(_sc) mtx_lock(&(_sc)->wb_mtx) +#define WB_UNLOCK(_sc) mtx_unlock(&(_sc)->wb_mtx) + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->wb_btag, sc->wb_bhandle, reg, val) +#define CSR_WRITE_2(sc, reg, val) \ + bus_space_write_2(sc->wb_btag, sc->wb_bhandle, reg, val) +#define CSR_WRITE_1(sc, reg, val) \ + bus_space_write_1(sc->wb_btag, sc->wb_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->wb_btag, sc->wb_bhandle, reg) +#define CSR_READ_2(sc, reg) \ + bus_space_read_2(sc->wb_btag, sc->wb_bhandle, reg) +#define CSR_READ_1(sc, reg) \ + bus_space_read_1(sc->wb_btag, sc->wb_bhandle, reg) + +#define WB_TIMEOUT 1000 + +/* + * General constants that are fun to know. + * + * Winbond PCI vendor ID + */ +#define WB_VENDORID 0x1050 + +/* + * Winbond device IDs. + */ +#define WB_DEVICEID_840F 0x0840 + +/* + * Compex vendor ID. + */ +#define CP_VENDORID 0x11F6 + +/* + * Compex device IDs. + */ +#define CP_DEVICEID_RL100 0x2011 + +/* + * PCI low memory base and low I/O base register, and + * other PCI registers. + */ + +#define WB_PCI_VENDOR_ID 0x00 +#define WB_PCI_DEVICE_ID 0x02 +#define WB_PCI_COMMAND 0x04 +#define WB_PCI_STATUS 0x06 +#define WB_PCI_CLASSCODE 0x09 +#define WB_PCI_CACHELEN 0x0C +#define WB_PCI_LATENCY_TIMER 0x0D +#define WB_PCI_HEADER_TYPE 0x0E +#define WB_PCI_LOIO 0x10 +#define WB_PCI_LOMEM 0x14 +#define WB_PCI_BIOSROM 0x30 +#define WB_PCI_INTLINE 0x3C +#define WB_PCI_INTPIN 0x3D +#define WB_PCI_MINGNT 0x3E +#define WB_PCI_MINLAT 0x0F +#define WB_PCI_RESETOPT 0x48 +#define WB_PCI_EEPROM_DATA 0x4C + +/* power management registers */ +#define WB_PCI_CAPID 0xDC /* 8 bits */ +#define WB_PCI_NEXTPTR 0xDD /* 8 bits */ +#define WB_PCI_PWRMGMTCAP 0xDE /* 16 bits */ +#define WB_PCI_PWRMGMTCTRL 0xE0 /* 16 bits */ + +#define WB_PSTATE_MASK 0x0003 +#define WB_PSTATE_D0 0x0000 +#define WB_PSTATE_D1 0x0002 +#define WB_PSTATE_D2 0x0002 +#define WB_PSTATE_D3 0x0003 +#define WB_PME_EN 0x0010 +#define WB_PME_STATUS 0x8000 + +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) +#endif diff --git a/sys/pci/if_xl.c b/sys/pci/if_xl.c new file mode 100644 index 0000000..782361c --- /dev/null +++ b/sys/pci/if_xl.c @@ -0,0 +1,3082 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * 3Com 3c90x Etherlink XL PCI NIC driver + * + * Supports the 3Com "boomerang", "cyclone" and "hurricane" PCI + * bus-master chips (3c90x cards and embedded controllers) including + * the following: + * + * 3Com 3c900-TPO 10Mbps/RJ-45 + * 3Com 3c900-COMBO 10Mbps/RJ-45,AUI,BNC + * 3Com 3c905-TX 10/100Mbps/RJ-45 + * 3Com 3c905-T4 10/100Mbps/RJ-45 + * 3Com 3c900B-TPO 10Mbps/RJ-45 + * 3Com 3c900B-COMBO 10Mbps/RJ-45,AUI,BNC + * 3Com 3c900B-TPC 10Mbps/RJ-45,BNC + * 3Com 3c900B-FL 10Mbps/Fiber-optic + * 3Com 3c905B-COMBO 10/100Mbps/RJ-45,AUI,BNC + * 3Com 3c905B-TX 10/100Mbps/RJ-45 + * 3Com 3c905B-FL/FX 10/100Mbps/Fiber-optic + * 3Com 3c905C-TX 10/100Mbps/RJ-45 (Tornado ASIC) + * 3Com 3c980-TX 10/100Mbps server adapter (Hurricane ASIC) + * 3Com 3c980C-TX 10/100Mbps server adapter (Tornado ASIC) + * 3Com 3cSOHO100-TX 10/100Mbps/RJ-45 (Hurricane ASIC) + * 3Com 3c450-TX 10/100Mbps/RJ-45 (Tornado ASIC) + * 3Com 3c556 10/100Mbps/RJ-45 (MiniPCI, Hurricane ASIC) + * 3Com 3c556B 10/100Mbps/RJ-45 (MiniPCI, Hurricane ASIC) + * 3Com 3c575TX 10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC) + * 3Com 3c575B 10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC) + * 3Com 3c575C 10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC) + * 3Com 3cxfem656 10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC) + * 3Com 3cxfem656b 10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC) + * 3Com 3cxfem656c 10/100Mbps/RJ-45 (Cardbus, Tornado ASIC) + * Dell Optiplex GX1 on-board 3c918 10/100Mbps/RJ-45 + * Dell on-board 3c920 10/100Mbps/RJ-45 + * Dell Precision on-board 3c905B 10/100Mbps/RJ-45 + * Dell Latitude laptop docking station embedded 3c905-TX + * + * Written by Bill Paul <wpaul@ctr.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * The 3c90x series chips use a bus-master DMA interface for transfering + * packets to and from the controller chip. Some of the "vortex" cards + * (3c59x) also supported a bus master mode, however for those chips + * you could only DMA packets to/from a contiguous memory buffer. For + * transmission this would mean copying the contents of the queued mbuf + * chain into a an mbuf cluster and then DMAing the cluster. This extra + * copy would sort of defeat the purpose of the bus master support for + * any packet that doesn't fit into a single mbuf. + * + * By contrast, the 3c90x cards support a fragment-based bus master + * mode where mbuf chains can be encapsulated using TX descriptors. + * This is similar to other PCI chips such as the Texas Instruments + * ThunderLAN and the Intel 82557/82558. + * + * The "vortex" driver (if_vx.c) happens to work for the "boomerang" + * bus master chips because they maintain the old PIO interface for + * backwards compatibility, but starting with the 3c905B and the + * "cyclone" chips, the compatibility interface has been dropped. + * Since using bus master DMA is a big win, we use this driver to + * support the PCI "boomerang" chips even though they work with the + * "vortex" driver in order to obtain better performance. + * + * This driver is in the /sys/pci directory because it only supports + * PCI-based NICs. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#include <net/bpf.h> + +#include <vm/vm.h> /* for vtophys */ +#include <vm/pmap.h> /* for vtophys */ +#include <machine/bus_memio.h> +#include <machine/bus_pio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +MODULE_DEPEND(xl, miibus, 1, 1, 1); + +/* "controller miibus0" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +/* + * The following #define causes the code to use PIO to access the + * chip's registers instead of memory mapped mode. The reason PIO mode + * is on by default is that the Etherlink XL manual seems to indicate + * that only the newer revision chips (3c905B) support both PIO and + * memory mapped access. Since we want to be compatible with the older + * bus master chips, we use PIO here. If you comment this out, the + * driver will use memory mapped I/O, which may be faster but which + * might not work on some devices. + */ +#define XL_USEIOSPACE + +#include <pci/if_xlreg.h> + +#if !defined(lint) +static const char rcsid[] = + "$FreeBSD$"; +#endif + +#define XL905B_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) + +/* + * Various supported device vendors/types and their names. + */ +static struct xl_type xl_devs[] = { + { TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT, + "3Com 3c900-TPO Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT_COMBO, + "3Com 3c900-COMBO Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_BOOMERANG_10_100BT, + "3Com 3c905-TX Fast Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_BOOMERANG_100BT4, + "3Com 3c905-T4 Fast Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT, + "3Com 3c900B-TPO Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT_COMBO, + "3Com 3c900B-COMBO Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT_TPC, + "3Com 3c900B-TPC Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_CYCLONE_10FL, + "3Com 3c900B-FL Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_HURRICANE_10_100BT, + "3Com 3c905B-TX Fast Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100BT4, + "3Com 3c905B-T4 Fast Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100FX, + "3Com 3c905B-FX/SC Fast Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100_COMBO, + "3Com 3c905B-COMBO Fast Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT, + "3Com 3c905C-TX Fast Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_HURRICANE_10_100BT_SERV, + "3Com 3c980 Fast Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_SERV, + "3Com 3c980C Fast Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_HURRICANE_SOHO100TX, + "3Com 3cSOHO100-TX OfficeConnect" }, + { TC_VENDORID, TC_DEVICEID_TORNADO_HOMECONNECT, + "3Com 3c450-TX HomeConnect" }, + { TC_VENDORID, TC_DEVICEID_HURRICANE_556, + "3Com 3c556 Fast Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_HURRICANE_556B, + "3Com 3c556B Fast Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_HURRICANE_575A, + "3Com 3c575TX Fast Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_HURRICANE_575B, + "3Com 3c575B Fast Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_HURRICANE_575C, + "3Com 3c575C Fast Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_HURRICANE_656, + "3Com 3c656 Fast Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_HURRICANE_656B, + "3Com 3c656B Fast Etherlink XL" }, + { TC_VENDORID, TC_DEVICEID_TORNADO_656C, + "3Com 3c656C Fast Etherlink XL" }, + { 0, 0, NULL } +}; + +static int xl_probe (device_t); +static int xl_attach (device_t); +static int xl_detach (device_t); + +static int xl_newbuf (struct xl_softc *, struct xl_chain_onefrag *); +static void xl_stats_update (void *); +static int xl_encap (struct xl_softc *, struct xl_chain *, + struct mbuf *); +static int xl_encap_90xB (struct xl_softc *, struct xl_chain *, + struct mbuf *); + +static void xl_rxeof (struct xl_softc *); +static int xl_rx_resync (struct xl_softc *); +static void xl_txeof (struct xl_softc *); +static void xl_txeof_90xB (struct xl_softc *); +static void xl_txeoc (struct xl_softc *); +static void xl_intr (void *); +static void xl_start (struct ifnet *); +static void xl_start_90xB (struct ifnet *); +static int xl_ioctl (struct ifnet *, u_long, caddr_t); +static void xl_init (void *); +static void xl_stop (struct xl_softc *); +static void xl_watchdog (struct ifnet *); +static void xl_shutdown (device_t); +static int xl_suspend (device_t); +static int xl_resume (device_t); + +static int xl_ifmedia_upd (struct ifnet *); +static void xl_ifmedia_sts (struct ifnet *, struct ifmediareq *); + +static int xl_eeprom_wait (struct xl_softc *); +static int xl_read_eeprom (struct xl_softc *, caddr_t, int, int, int); +static void xl_mii_sync (struct xl_softc *); +static void xl_mii_send (struct xl_softc *, u_int32_t, int); +static int xl_mii_readreg (struct xl_softc *, struct xl_mii_frame *); +static int xl_mii_writereg (struct xl_softc *, struct xl_mii_frame *); + +static void xl_setcfg (struct xl_softc *); +static void xl_setmode (struct xl_softc *, int); +static u_int8_t xl_calchash (caddr_t); +static void xl_setmulti (struct xl_softc *); +static void xl_setmulti_hash (struct xl_softc *); +static void xl_reset (struct xl_softc *); +static int xl_list_rx_init (struct xl_softc *); +static int xl_list_tx_init (struct xl_softc *); +static int xl_list_tx_init_90xB (struct xl_softc *); +static void xl_wait (struct xl_softc *); +static void xl_mediacheck (struct xl_softc *); +static void xl_choose_xcvr (struct xl_softc *, int); +#ifdef notdef +static void xl_testpacket (struct xl_softc *); +#endif + +static int xl_miibus_readreg (device_t, int, int); +static int xl_miibus_writereg (device_t, int, int, int); +static void xl_miibus_statchg (device_t); +static void xl_miibus_mediainit (device_t); + +#ifdef XL_USEIOSPACE +#define XL_RES SYS_RES_IOPORT +#define XL_RID XL_PCI_LOIO +#else +#define XL_RES SYS_RES_MEMORY +#define XL_RID XL_PCI_LOMEM +#endif + +static device_method_t xl_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, xl_probe), + DEVMETHOD(device_attach, xl_attach), + DEVMETHOD(device_detach, xl_detach), + DEVMETHOD(device_shutdown, xl_shutdown), + DEVMETHOD(device_suspend, xl_suspend), + DEVMETHOD(device_resume, xl_resume), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, xl_miibus_readreg), + DEVMETHOD(miibus_writereg, xl_miibus_writereg), + DEVMETHOD(miibus_statchg, xl_miibus_statchg), + DEVMETHOD(miibus_mediainit, xl_miibus_mediainit), + + { 0, 0 } +}; + +static driver_t xl_driver = { + "xl", + xl_methods, + sizeof(struct xl_softc) +}; + +static devclass_t xl_devclass; + +DRIVER_MODULE(if_xl, cardbus, xl_driver, xl_devclass, 0, 0); +DRIVER_MODULE(if_xl, pci, xl_driver, xl_devclass, 0, 0); +DRIVER_MODULE(miibus, xl, miibus_driver, miibus_devclass, 0, 0); + +/* + * Murphy's law says that it's possible the chip can wedge and + * the 'command in progress' bit may never clear. Hence, we wait + * only a finite amount of time to avoid getting caught in an + * infinite loop. Normally this delay routine would be a macro, + * but it isn't called during normal operation so we can afford + * to make it a function. + */ +static void xl_wait(sc) + struct xl_softc *sc; +{ + register int i; + + for (i = 0; i < XL_TIMEOUT; i++) { + if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY)) + break; + } + + if (i == XL_TIMEOUT) + printf("xl%d: command never completed!\n", sc->xl_unit); + + return; +} + +/* + * MII access routines are provided for adapters with external + * PHYs (3c905-TX, 3c905-T4, 3c905B-T4) and those with built-in + * autoneg logic that's faked up to look like a PHY (3c905B-TX). + * Note: if you don't perform the MDIO operations just right, + * it's possible to end up with code that works correctly with + * some chips/CPUs/processor speeds/bus speeds/etc but not + * with others. + */ +#define MII_SET(x) \ + CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \ + CSR_READ_2(sc, XL_W4_PHY_MGMT) | x) + +#define MII_CLR(x) \ + CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \ + CSR_READ_2(sc, XL_W4_PHY_MGMT) & ~x) + +/* + * Sync the PHYs by setting data bit and strobing the clock 32 times. + */ +static void xl_mii_sync(sc) + struct xl_softc *sc; +{ + register int i; + + XL_SEL_WIN(4); + MII_SET(XL_MII_DIR|XL_MII_DATA); + + for (i = 0; i < 32; i++) { + MII_SET(XL_MII_CLK); + DELAY(1); + MII_CLR(XL_MII_CLK); + DELAY(1); + } + + return; +} + +/* + * Clock a series of bits through the MII. + */ +static void xl_mii_send(sc, bits, cnt) + struct xl_softc *sc; + u_int32_t bits; + int cnt; +{ + int i; + + XL_SEL_WIN(4); + MII_CLR(XL_MII_CLK); + + for (i = (0x1 << (cnt - 1)); i; i >>= 1) { + if (bits & i) { + MII_SET(XL_MII_DATA); + } else { + MII_CLR(XL_MII_DATA); + } + DELAY(1); + MII_CLR(XL_MII_CLK); + DELAY(1); + MII_SET(XL_MII_CLK); + } +} + +/* + * Read an PHY register through the MII. + */ +static int xl_mii_readreg(sc, frame) + struct xl_softc *sc; + struct xl_mii_frame *frame; + +{ + int i, ack; + + XL_LOCK(sc); + + /* + * Set up frame for RX. + */ + frame->mii_stdelim = XL_MII_STARTDELIM; + frame->mii_opcode = XL_MII_READOP; + frame->mii_turnaround = 0; + frame->mii_data = 0; + + /* + * Select register window 4. + */ + + XL_SEL_WIN(4); + + CSR_WRITE_2(sc, XL_W4_PHY_MGMT, 0); + /* + * Turn on data xmit. + */ + MII_SET(XL_MII_DIR); + + xl_mii_sync(sc); + + /* + * Send command/address info. + */ + xl_mii_send(sc, frame->mii_stdelim, 2); + xl_mii_send(sc, frame->mii_opcode, 2); + xl_mii_send(sc, frame->mii_phyaddr, 5); + xl_mii_send(sc, frame->mii_regaddr, 5); + + /* Idle bit */ + MII_CLR((XL_MII_CLK|XL_MII_DATA)); + DELAY(1); + MII_SET(XL_MII_CLK); + DELAY(1); + + /* Turn off xmit. */ + MII_CLR(XL_MII_DIR); + + /* Check for ack */ + MII_CLR(XL_MII_CLK); + DELAY(1); + MII_SET(XL_MII_CLK); + DELAY(1); + ack = CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA; + + /* + * Now try reading data bits. If the ack failed, we still + * need to clock through 16 cycles to keep the PHY(s) in sync. + */ + if (ack) { + for(i = 0; i < 16; i++) { + MII_CLR(XL_MII_CLK); + DELAY(1); + MII_SET(XL_MII_CLK); + DELAY(1); + } + goto fail; + } + + for (i = 0x8000; i; i >>= 1) { + MII_CLR(XL_MII_CLK); + DELAY(1); + if (!ack) { + if (CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA) + frame->mii_data |= i; + DELAY(1); + } + MII_SET(XL_MII_CLK); + DELAY(1); + } + +fail: + + MII_CLR(XL_MII_CLK); + DELAY(1); + MII_SET(XL_MII_CLK); + DELAY(1); + + XL_UNLOCK(sc); + + if (ack) + return(1); + return(0); +} + +/* + * Write to a PHY register through the MII. + */ +static int xl_mii_writereg(sc, frame) + struct xl_softc *sc; + struct xl_mii_frame *frame; + +{ + XL_LOCK(sc); + + /* + * Set up frame for TX. + */ + + frame->mii_stdelim = XL_MII_STARTDELIM; + frame->mii_opcode = XL_MII_WRITEOP; + frame->mii_turnaround = XL_MII_TURNAROUND; + + /* + * Select the window 4. + */ + XL_SEL_WIN(4); + + /* + * Turn on data output. + */ + MII_SET(XL_MII_DIR); + + xl_mii_sync(sc); + + xl_mii_send(sc, frame->mii_stdelim, 2); + xl_mii_send(sc, frame->mii_opcode, 2); + xl_mii_send(sc, frame->mii_phyaddr, 5); + xl_mii_send(sc, frame->mii_regaddr, 5); + xl_mii_send(sc, frame->mii_turnaround, 2); + xl_mii_send(sc, frame->mii_data, 16); + + /* Idle bit. */ + MII_SET(XL_MII_CLK); + DELAY(1); + MII_CLR(XL_MII_CLK); + DELAY(1); + + /* + * Turn off xmit. + */ + MII_CLR(XL_MII_DIR); + + XL_UNLOCK(sc); + + return(0); +} + +static int xl_miibus_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct xl_softc *sc; + struct xl_mii_frame frame; + + sc = device_get_softc(dev); + + /* + * Pretend that PHYs are only available at MII address 24. + * This is to guard against problems with certain 3Com ASIC + * revisions that incorrectly map the internal transceiver + * control registers at all MII addresses. This can cause + * the miibus code to attach the same PHY several times over. + */ + if ((!(sc->xl_flags & XL_FLAG_PHYOK)) && phy != 24) + return(0); + + bzero((char *)&frame, sizeof(frame)); + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + xl_mii_readreg(sc, &frame); + + return(frame.mii_data); +} + +static int xl_miibus_writereg(dev, phy, reg, data) + device_t dev; + int phy, reg, data; +{ + struct xl_softc *sc; + struct xl_mii_frame frame; + + sc = device_get_softc(dev); + + if ((!(sc->xl_flags & XL_FLAG_PHYOK)) && phy != 24) + return(0); + + bzero((char *)&frame, sizeof(frame)); + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + frame.mii_data = data; + + xl_mii_writereg(sc, &frame); + + return(0); +} + +static void xl_miibus_statchg(dev) + device_t dev; +{ + struct xl_softc *sc; + struct mii_data *mii; + + + sc = device_get_softc(dev); + mii = device_get_softc(sc->xl_miibus); + + XL_LOCK(sc); + + xl_setcfg(sc); + + /* Set ASIC's duplex mode to match the PHY. */ + XL_SEL_WIN(3); + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) + CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX); + else + CSR_WRITE_1(sc, XL_W3_MAC_CTRL, + (CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX)); + + XL_UNLOCK(sc); + + return; +} + +/* + * Special support for the 3c905B-COMBO. This card has 10/100 support + * plus BNC and AUI ports. This means we will have both an miibus attached + * plus some non-MII media settings. In order to allow this, we have to + * add the extra media to the miibus's ifmedia struct, but we can't do + * that during xl_attach() because the miibus hasn't been attached yet. + * So instead, we wait until the miibus probe/attach is done, at which + * point we will get a callback telling is that it's safe to add our + * extra media. + */ +static void xl_miibus_mediainit(dev) + device_t dev; +{ + struct xl_softc *sc; + struct mii_data *mii; + struct ifmedia *ifm; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->xl_miibus); + ifm = &mii->mii_media; + + XL_LOCK(sc); + + if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) { + /* + * Check for a 10baseFL board in disguise. + */ + if (sc->xl_type == XL_TYPE_905B && + sc->xl_media == XL_MEDIAOPT_10FL) { + if (bootverbose) + printf("xl%d: found 10baseFL\n", sc->xl_unit); + ifmedia_add(ifm, IFM_ETHER|IFM_10_FL, 0, NULL); + ifmedia_add(ifm, IFM_ETHER|IFM_10_FL|IFM_HDX, 0, NULL); + if (sc->xl_caps & XL_CAPS_FULL_DUPLEX) + ifmedia_add(ifm, + IFM_ETHER|IFM_10_FL|IFM_FDX, 0, NULL); + } else { + if (bootverbose) + printf("xl%d: found AUI\n", sc->xl_unit); + ifmedia_add(ifm, IFM_ETHER|IFM_10_5, 0, NULL); + } + } + + if (sc->xl_media & XL_MEDIAOPT_BNC) { + if (bootverbose) + printf("xl%d: found BNC\n", sc->xl_unit); + ifmedia_add(ifm, IFM_ETHER|IFM_10_2, 0, NULL); + } + + XL_UNLOCK(sc); + + return; +} + +/* + * The EEPROM is slow: give it time to come ready after issuing + * it a command. + */ +static int xl_eeprom_wait(sc) + struct xl_softc *sc; +{ + int i; + + for (i = 0; i < 100; i++) { + if (CSR_READ_2(sc, XL_W0_EE_CMD) & XL_EE_BUSY) + DELAY(162); + else + break; + } + + if (i == 100) { + printf("xl%d: eeprom failed to come ready\n", sc->xl_unit); + return(1); + } + + return(0); +} + +/* + * Read a sequence of words from the EEPROM. Note that ethernet address + * data is stored in the EEPROM in network byte order. + */ +static int xl_read_eeprom(sc, dest, off, cnt, swap) + struct xl_softc *sc; + caddr_t dest; + int off; + int cnt; + int swap; +{ + int err = 0, i; + u_int16_t word = 0, *ptr; +#define EEPROM_5BIT_OFFSET(A) ((((A) << 2) & 0x7F00) | ((A) & 0x003F)) +#define EEPROM_8BIT_OFFSET(A) ((A) & 0x003F) + /* WARNING! DANGER! + * It's easy to accidentally overwrite the rom content! + * Note: the 3c575 uses 8bit EEPROM offsets. + */ + XL_SEL_WIN(0); + + if (xl_eeprom_wait(sc)) + return(1); + + if (sc->xl_flags & XL_FLAG_EEPROM_OFFSET_30) + off += 0x30; + + for (i = 0; i < cnt; i++) { + if (sc->xl_flags & XL_FLAG_8BITROM) + CSR_WRITE_2(sc, XL_W0_EE_CMD, + XL_EE_8BIT_READ | EEPROM_8BIT_OFFSET(off + i)); + else + CSR_WRITE_2(sc, XL_W0_EE_CMD, + XL_EE_READ | EEPROM_5BIT_OFFSET(off + i)); + err = xl_eeprom_wait(sc); + if (err) + break; + word = CSR_READ_2(sc, XL_W0_EE_DATA); + ptr = (u_int16_t *)(dest + (i * 2)); + if (swap) + *ptr = ntohs(word); + else + *ptr = word; + } + + return(err ? 1 : 0); +} + +/* + * This routine is taken from the 3Com Etherlink XL manual, + * page 10-7. It calculates a CRC of the supplied multicast + * group address and returns the lower 8 bits, which are used + * as the multicast filter position. + * Note: the 3c905B currently only supports a 64-bit hash table, + * which means we really only need 6 bits, but the manual indicates + * that future chip revisions will have a 256-bit hash table, + * hence the routine is set up to calculate 8 bits of position + * info in case we need it some day. + * Note II, The Sequel: _CURRENT_ versions of the 3c905B have a + * 256 bit hash table. This means we have to use all 8 bits regardless. + * On older cards, the upper 2 bits will be ignored. Grrrr.... + */ +static u_int8_t xl_calchash(addr) + caddr_t addr; +{ + u_int32_t crc, carry; + int i, j; + u_int8_t c; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (i = 0; i < 6; i++) { + c = *(addr + i); + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); + crc <<= 1; + c >>= 1; + if (carry) + crc = (crc ^ 0x04c11db6) | carry; + } + } + + /* return the filter bit position */ + return(crc & 0x000000FF); +} + +/* + * NICs older than the 3c905B have only one multicast option, which + * is to enable reception of all multicast frames. + */ +static void xl_setmulti(sc) + struct xl_softc *sc; +{ + struct ifnet *ifp; + struct ifmultiaddr *ifma; + u_int8_t rxfilt; + int mcnt = 0; + + ifp = &sc->arpcom.ac_if; + + XL_SEL_WIN(5); + rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); + + if (ifp->if_flags & IFF_ALLMULTI) { + rxfilt |= XL_RXFILTER_ALLMULTI; + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); + return; + } + + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) + mcnt++; + + if (mcnt) + rxfilt |= XL_RXFILTER_ALLMULTI; + else + rxfilt &= ~XL_RXFILTER_ALLMULTI; + + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); + + return; +} + +/* + * 3c905B adapters have a hash filter that we can program. + */ +static void xl_setmulti_hash(sc) + struct xl_softc *sc; +{ + struct ifnet *ifp; + int h = 0, i; + struct ifmultiaddr *ifma; + u_int8_t rxfilt; + int mcnt = 0; + + ifp = &sc->arpcom.ac_if; + + XL_SEL_WIN(5); + rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); + + if (ifp->if_flags & IFF_ALLMULTI) { + rxfilt |= XL_RXFILTER_ALLMULTI; + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); + return; + } else + rxfilt &= ~XL_RXFILTER_ALLMULTI; + + + /* first, zot all the existing hash bits */ + for (i = 0; i < XL_HASHFILT_SIZE; i++) + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_HASH|i); + + /* now program new ones */ + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = xl_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_HASH|XL_HASH_SET|h); + mcnt++; + } + + if (mcnt) + rxfilt |= XL_RXFILTER_MULTIHASH; + else + rxfilt &= ~XL_RXFILTER_MULTIHASH; + + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); + + return; +} + +#ifdef notdef +static void xl_testpacket(sc) + struct xl_softc *sc; +{ + struct mbuf *m; + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + + if (m == NULL) + return; + + bcopy(&sc->arpcom.ac_enaddr, + mtod(m, struct ether_header *)->ether_dhost, ETHER_ADDR_LEN); + bcopy(&sc->arpcom.ac_enaddr, + mtod(m, struct ether_header *)->ether_shost, ETHER_ADDR_LEN); + mtod(m, struct ether_header *)->ether_type = htons(3); + mtod(m, unsigned char *)[14] = 0; + mtod(m, unsigned char *)[15] = 0; + mtod(m, unsigned char *)[16] = 0xE3; + m->m_len = m->m_pkthdr.len = sizeof(struct ether_header) + 3; + IF_ENQUEUE(&ifp->if_snd, m); + xl_start(ifp); + + return; +} +#endif + +static void xl_setcfg(sc) + struct xl_softc *sc; +{ + u_int32_t icfg; + + XL_SEL_WIN(3); + icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG); + icfg &= ~XL_ICFG_CONNECTOR_MASK; + if (sc->xl_media & XL_MEDIAOPT_MII || + sc->xl_media & XL_MEDIAOPT_BT4) + icfg |= (XL_XCVR_MII << XL_ICFG_CONNECTOR_BITS); + if (sc->xl_media & XL_MEDIAOPT_BTX) + icfg |= (XL_XCVR_AUTO << XL_ICFG_CONNECTOR_BITS); + + CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); + + return; +} + +static void xl_setmode(sc, media) + struct xl_softc *sc; + int media; +{ + u_int32_t icfg; + u_int16_t mediastat; + + printf("xl%d: selecting ", sc->xl_unit); + + XL_SEL_WIN(4); + mediastat = CSR_READ_2(sc, XL_W4_MEDIA_STATUS); + XL_SEL_WIN(3); + icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG); + + if (sc->xl_media & XL_MEDIAOPT_BT) { + if (IFM_SUBTYPE(media) == IFM_10_T) { + printf("10baseT transceiver, "); + sc->xl_xcvr = XL_XCVR_10BT; + icfg &= ~XL_ICFG_CONNECTOR_MASK; + icfg |= (XL_XCVR_10BT << XL_ICFG_CONNECTOR_BITS); + mediastat |= XL_MEDIASTAT_LINKBEAT| + XL_MEDIASTAT_JABGUARD; + mediastat &= ~XL_MEDIASTAT_SQEENB; + } + } + + if (sc->xl_media & XL_MEDIAOPT_BFX) { + if (IFM_SUBTYPE(media) == IFM_100_FX) { + printf("100baseFX port, "); + sc->xl_xcvr = XL_XCVR_100BFX; + icfg &= ~XL_ICFG_CONNECTOR_MASK; + icfg |= (XL_XCVR_100BFX << XL_ICFG_CONNECTOR_BITS); + mediastat |= XL_MEDIASTAT_LINKBEAT; + mediastat &= ~XL_MEDIASTAT_SQEENB; + } + } + + if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) { + if (IFM_SUBTYPE(media) == IFM_10_5) { + printf("AUI port, "); + sc->xl_xcvr = XL_XCVR_AUI; + icfg &= ~XL_ICFG_CONNECTOR_MASK; + icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS); + mediastat &= ~(XL_MEDIASTAT_LINKBEAT| + XL_MEDIASTAT_JABGUARD); + mediastat |= ~XL_MEDIASTAT_SQEENB; + } + if (IFM_SUBTYPE(media) == IFM_10_FL) { + printf("10baseFL transceiver, "); + sc->xl_xcvr = XL_XCVR_AUI; + icfg &= ~XL_ICFG_CONNECTOR_MASK; + icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS); + mediastat &= ~(XL_MEDIASTAT_LINKBEAT| + XL_MEDIASTAT_JABGUARD); + mediastat |= ~XL_MEDIASTAT_SQEENB; + } + } + + if (sc->xl_media & XL_MEDIAOPT_BNC) { + if (IFM_SUBTYPE(media) == IFM_10_2) { + printf("BNC port, "); + sc->xl_xcvr = XL_XCVR_COAX; + icfg &= ~XL_ICFG_CONNECTOR_MASK; + icfg |= (XL_XCVR_COAX << XL_ICFG_CONNECTOR_BITS); + mediastat &= ~(XL_MEDIASTAT_LINKBEAT| + XL_MEDIASTAT_JABGUARD| + XL_MEDIASTAT_SQEENB); + } + } + + if ((media & IFM_GMASK) == IFM_FDX || + IFM_SUBTYPE(media) == IFM_100_FX) { + printf("full duplex\n"); + XL_SEL_WIN(3); + CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX); + } else { + printf("half duplex\n"); + XL_SEL_WIN(3); + CSR_WRITE_1(sc, XL_W3_MAC_CTRL, + (CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX)); + } + + if (IFM_SUBTYPE(media) == IFM_10_2) + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START); + else + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); + CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg); + XL_SEL_WIN(4); + CSR_WRITE_2(sc, XL_W4_MEDIA_STATUS, mediastat); + DELAY(800); + XL_SEL_WIN(7); + + return; +} + +static void xl_reset(sc) + struct xl_softc *sc; +{ + register int i; + + XL_SEL_WIN(0); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RESET | + ((sc->xl_flags & XL_FLAG_WEIRDRESET) ? + XL_RESETOPT_DISADVFD:0)); + + for (i = 0; i < XL_TIMEOUT; i++) { + DELAY(10); + if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY)) + break; + } + + if (i == XL_TIMEOUT) + printf("xl%d: reset didn't complete\n", sc->xl_unit); + + /* Reset TX and RX. */ + /* Note: the RX reset takes an absurd amount of time + * on newer versions of the Tornado chips such as those + * on the 3c905CX and newer 3c908C cards. We wait an + * extra amount of time so that xl_wait() doesn't complain + * and annoy the users. + */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); + DELAY(100000); + xl_wait(sc); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); + xl_wait(sc); + + if (sc->xl_flags & XL_FLAG_INVERT_LED_PWR || + sc->xl_flags & XL_FLAG_INVERT_MII_PWR) { + XL_SEL_WIN(2); + CSR_WRITE_2(sc, XL_W2_RESET_OPTIONS, CSR_READ_2(sc, + XL_W2_RESET_OPTIONS) + | ((sc->xl_flags & XL_FLAG_INVERT_LED_PWR)?XL_RESETOPT_INVERT_LED:0) + | ((sc->xl_flags & XL_FLAG_INVERT_MII_PWR)?XL_RESETOPT_INVERT_MII:0) + ); + } + + /* Wait a little while for the chip to get its brains in order. */ + DELAY(100000); + return; +} + +/* + * Probe for a 3Com Etherlink XL chip. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + */ +static int xl_probe(dev) + device_t dev; +{ + struct xl_type *t; + + t = xl_devs; + + while(t->xl_name != NULL) { + if ((pci_get_vendor(dev) == t->xl_vid) && + (pci_get_device(dev) == t->xl_did)) { + device_set_desc(dev, t->xl_name); + return(0); + } + t++; + } + + return(ENXIO); +} + +/* + * This routine is a kludge to work around possible hardware faults + * or manufacturing defects that can cause the media options register + * (or reset options register, as it's called for the first generation + * 3c90x adapters) to return an incorrect result. I have encountered + * one Dell Latitude laptop docking station with an integrated 3c905-TX + * which doesn't have any of the 'mediaopt' bits set. This screws up + * the attach routine pretty badly because it doesn't know what media + * to look for. If we find ourselves in this predicament, this routine + * will try to guess the media options values and warn the user of a + * possible manufacturing defect with his adapter/system/whatever. + */ +static void xl_mediacheck(sc) + struct xl_softc *sc; +{ + + /* + * If some of the media options bits are set, assume they are + * correct. If not, try to figure it out down below. + * XXX I should check for 10baseFL, but I don't have an adapter + * to test with. + */ + if (sc->xl_media & (XL_MEDIAOPT_MASK & ~XL_MEDIAOPT_VCO)) { + /* + * Check the XCVR value. If it's not in the normal range + * of values, we need to fake it up here. + */ + if (sc->xl_xcvr <= XL_XCVR_AUTO) + return; + else { + printf("xl%d: bogus xcvr value " + "in EEPROM (%x)\n", sc->xl_unit, sc->xl_xcvr); + printf("xl%d: choosing new default based " + "on card type\n", sc->xl_unit); + } + } else { + if (sc->xl_type == XL_TYPE_905B && + sc->xl_media & XL_MEDIAOPT_10FL) + return; + printf("xl%d: WARNING: no media options bits set in " + "the media options register!!\n", sc->xl_unit); + printf("xl%d: this could be a manufacturing defect in " + "your adapter or system\n", sc->xl_unit); + printf("xl%d: attempting to guess media type; you " + "should probably consult your vendor\n", sc->xl_unit); + } + + xl_choose_xcvr(sc, 1); + + return; +} + +static void xl_choose_xcvr(sc, verbose) + struct xl_softc *sc; + int verbose; +{ + u_int16_t devid; + + /* + * Read the device ID from the EEPROM. + * This is what's loaded into the PCI device ID register, so it has + * to be correct otherwise we wouldn't have gotten this far. + */ + xl_read_eeprom(sc, (caddr_t)&devid, XL_EE_PRODID, 1, 0); + + switch(devid) { + case TC_DEVICEID_BOOMERANG_10BT: /* 3c900-TPO */ + case TC_DEVICEID_KRAKATOA_10BT: /* 3c900B-TPO */ + sc->xl_media = XL_MEDIAOPT_BT; + sc->xl_xcvr = XL_XCVR_10BT; + if (verbose) + printf("xl%d: guessing 10BaseT " + "transceiver\n", sc->xl_unit); + break; + case TC_DEVICEID_BOOMERANG_10BT_COMBO: /* 3c900-COMBO */ + case TC_DEVICEID_KRAKATOA_10BT_COMBO: /* 3c900B-COMBO */ + sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI; + sc->xl_xcvr = XL_XCVR_10BT; + if (verbose) + printf("xl%d: guessing COMBO " + "(AUI/BNC/TP)\n", sc->xl_unit); + break; + case TC_DEVICEID_KRAKATOA_10BT_TPC: /* 3c900B-TPC */ + sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC; + sc->xl_xcvr = XL_XCVR_10BT; + if (verbose) + printf("xl%d: guessing TPC (BNC/TP)\n", sc->xl_unit); + break; + case TC_DEVICEID_CYCLONE_10FL: /* 3c900B-FL */ + sc->xl_media = XL_MEDIAOPT_10FL; + sc->xl_xcvr = XL_XCVR_AUI; + if (verbose) + printf("xl%d: guessing 10baseFL\n", sc->xl_unit); + break; + case TC_DEVICEID_BOOMERANG_10_100BT: /* 3c905-TX */ + case TC_DEVICEID_HURRICANE_556: /* 3c556 */ + case TC_DEVICEID_HURRICANE_556B: /* 3c556B */ + case TC_DEVICEID_HURRICANE_575A: /* 3c575TX */ + case TC_DEVICEID_HURRICANE_575B: /* 3c575B */ + case TC_DEVICEID_HURRICANE_575C: /* 3c575C */ + case TC_DEVICEID_HURRICANE_656: /* 3c656 */ + case TC_DEVICEID_HURRICANE_656B: /* 3c656B */ + case TC_DEVICEID_TORNADO_656C: /* 3c656C */ + sc->xl_media = XL_MEDIAOPT_MII; + sc->xl_xcvr = XL_XCVR_MII; + if (verbose) + printf("xl%d: guessing MII\n", sc->xl_unit); + break; + case TC_DEVICEID_BOOMERANG_100BT4: /* 3c905-T4 */ + case TC_DEVICEID_CYCLONE_10_100BT4: /* 3c905B-T4 */ + sc->xl_media = XL_MEDIAOPT_BT4; + sc->xl_xcvr = XL_XCVR_MII; + if (verbose) + printf("xl%d: guessing 100BaseT4/MII\n", sc->xl_unit); + break; + case TC_DEVICEID_HURRICANE_10_100BT: /* 3c905B-TX */ + case TC_DEVICEID_HURRICANE_10_100BT_SERV:/*3c980-TX */ + case TC_DEVICEID_TORNADO_10_100BT_SERV: /* 3c980C-TX */ + case TC_DEVICEID_HURRICANE_SOHO100TX: /* 3cSOHO100-TX */ + case TC_DEVICEID_TORNADO_10_100BT: /* 3c905C-TX */ + case TC_DEVICEID_TORNADO_HOMECONNECT: /* 3c450-TX */ + sc->xl_media = XL_MEDIAOPT_BTX; + sc->xl_xcvr = XL_XCVR_AUTO; + if (verbose) + printf("xl%d: guessing 10/100 internal\n", sc->xl_unit); + break; + case TC_DEVICEID_CYCLONE_10_100_COMBO: /* 3c905B-COMBO */ + sc->xl_media = XL_MEDIAOPT_BTX|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI; + sc->xl_xcvr = XL_XCVR_AUTO; + if (verbose) + printf("xl%d: guessing 10/100 " + "plus BNC/AUI\n", sc->xl_unit); + break; + default: + printf("xl%d: unknown device ID: %x -- " + "defaulting to 10baseT\n", sc->xl_unit, devid); + sc->xl_media = XL_MEDIAOPT_BT; + break; + } + + return; +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int xl_attach(dev) + device_t dev; +{ + u_char eaddr[ETHER_ADDR_LEN]; + u_int32_t command; + struct xl_softc *sc; + struct ifnet *ifp; + int media = IFM_ETHER|IFM_100_TX|IFM_FDX; + int unit, error = 0, rid; + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + + mtx_init(&sc->xl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF | MTX_RECURSE); + XL_LOCK(sc); + + sc->xl_flags = 0; + if (pci_get_device(dev) == TC_DEVICEID_HURRICANE_556 || + pci_get_device(dev) == TC_DEVICEID_HURRICANE_556B) + sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK | + XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_WEIRDRESET | + XL_FLAG_INVERT_LED_PWR | XL_FLAG_INVERT_MII_PWR; + if (pci_get_device(dev) == TC_DEVICEID_HURRICANE_556) + sc->xl_flags |= XL_FLAG_8BITROM; + + if (pci_get_device(dev) == TC_DEVICEID_HURRICANE_575A || + pci_get_device(dev) == TC_DEVICEID_HURRICANE_575B || + pci_get_device(dev) == TC_DEVICEID_HURRICANE_575C || + pci_get_device(dev) == TC_DEVICEID_HURRICANE_656B || + pci_get_device(dev) == TC_DEVICEID_TORNADO_656C) + sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK | + XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_8BITROM; + if (pci_get_device(dev) == TC_DEVICEID_HURRICANE_656) + sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK; + if (pci_get_device(dev) == TC_DEVICEID_HURRICANE_575B) + sc->xl_flags |= XL_FLAG_INVERT_LED_PWR; + if (pci_get_device(dev) == TC_DEVICEID_HURRICANE_575C) + sc->xl_flags |= XL_FLAG_INVERT_MII_PWR; + if (pci_get_device(dev) == TC_DEVICEID_TORNADO_656C) + sc->xl_flags |= XL_FLAG_INVERT_MII_PWR; + if (pci_get_device(dev) == TC_DEVICEID_HURRICANE_656 || + pci_get_device(dev) == TC_DEVICEID_HURRICANE_656B) + sc->xl_flags |= XL_FLAG_INVERT_MII_PWR | + XL_FLAG_INVERT_LED_PWR; + + /* + * If this is a 3c905B, we have to check one extra thing. + * The 905B supports power management and may be placed in + * a low-power mode (D3 mode), typically by certain operating + * systems which shall not be named. The PCI BIOS is supposed + * to reset the NIC and bring it out of low-power mode, but + * some do not. Consequently, we have to see if this chip + * supports power management, and if so, make sure it's not + * in low-power mode. If power management is available, the + * capid byte will be 0x01. + * + * I _think_ that what actually happens is that the chip + * loses its PCI configuration during the transition from + * D3 back to D0; this means that it should be possible for + * us to save the PCI iobase, membase and IRQ, put the chip + * back in the D0 state, then restore the PCI config ourselves. + */ + + if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { + u_int32_t iobase, membase, irq; + + /* Save important PCI config data. */ + iobase = pci_read_config(dev, XL_PCI_LOIO, 4); + membase = pci_read_config(dev, XL_PCI_LOMEM, 4); + irq = pci_read_config(dev, XL_PCI_INTLINE, 4); + + /* Reset the power state. */ + printf("xl%d: chip is in D%d power mode " + "-- setting to D0\n", unit, + pci_get_powerstate(dev)); + + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + + /* Restore PCI config data. */ + pci_write_config(dev, XL_PCI_LOIO, iobase, 4); + pci_write_config(dev, XL_PCI_LOMEM, membase, 4); + pci_write_config(dev, XL_PCI_INTLINE, irq, 4); + } + + /* + * Map control/status registers. + */ + pci_enable_busmaster(dev); + pci_enable_io(dev, SYS_RES_IOPORT); + pci_enable_io(dev, SYS_RES_MEMORY); + command = pci_read_config(dev, PCIR_COMMAND, 4); + +#ifdef XL_USEIOSPACE + if (!(command & PCIM_CMD_PORTEN)) { + printf("xl%d: failed to enable I/O ports!\n", unit); + error = ENXIO; + goto fail; + } +#else + if (!(command & PCIM_CMD_MEMEN)) { + printf("xl%d: failed to enable memory mapping!\n", unit); + error = ENXIO; + goto fail; + } +#endif + + rid = XL_RID; + sc->xl_res = bus_alloc_resource(dev, XL_RES, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (sc->xl_res == NULL) { + printf ("xl%d: couldn't map ports/memory\n", unit); + error = ENXIO; + goto fail; + } + + sc->xl_btag = rman_get_bustag(sc->xl_res); + sc->xl_bhandle = rman_get_bushandle(sc->xl_res); + + if (sc->xl_flags & XL_FLAG_FUNCREG) { + rid = XL_PCI_FUNCMEM; + sc->xl_fres = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (sc->xl_fres == NULL) { + printf ("xl%d: couldn't map ports/memory\n", unit); + bus_release_resource(dev, XL_RES, XL_RID, sc->xl_res); + error = ENXIO; + goto fail; + } + + sc->xl_ftag = rman_get_bustag(sc->xl_fres); + sc->xl_fhandle = rman_get_bushandle(sc->xl_fres); + } + + rid = 0; + sc->xl_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->xl_irq == NULL) { + printf("xl%d: couldn't map interrupt\n", unit); + if (sc->xl_fres != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, + XL_PCI_FUNCMEM, sc->xl_fres); + bus_release_resource(dev, XL_RES, XL_RID, sc->xl_res); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->xl_irq, INTR_TYPE_NET, + xl_intr, sc, &sc->xl_intrhand); + + if (error) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->xl_irq); + if (sc->xl_fres != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, + XL_PCI_FUNCMEM, sc->xl_fres); + bus_release_resource(dev, XL_RES, XL_RID, sc->xl_res); + printf("xl%d: couldn't set up irq\n", unit); + goto fail; + } + + /* Reset the adapter. */ + xl_reset(sc); + + /* + * Get station address from the EEPROM. + */ + if (xl_read_eeprom(sc, (caddr_t)&eaddr, XL_EE_OEM_ADR0, 3, 1)) { + printf("xl%d: failed to read station address\n", sc->xl_unit); + bus_teardown_intr(dev, sc->xl_irq, sc->xl_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->xl_irq); + if (sc->xl_fres != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, + XL_PCI_FUNCMEM, sc->xl_fres); + bus_release_resource(dev, XL_RES, XL_RID, sc->xl_res); + error = ENXIO; + goto fail; + } + + /* + * A 3Com chip was detected. Inform the world. + */ + printf("xl%d: Ethernet address: %6D\n", unit, eaddr, ":"); + + sc->xl_unit = unit; + callout_handle_init(&sc->xl_stat_ch); + bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); + + sc->xl_ldata = contigmalloc(sizeof(struct xl_list_data), M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc->xl_ldata == NULL) { + printf("xl%d: no memory for list buffers!\n", unit); + bus_teardown_intr(dev, sc->xl_irq, sc->xl_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->xl_irq); + if (sc->xl_fres != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, + XL_PCI_FUNCMEM, sc->xl_fres); + bus_release_resource(dev, XL_RES, XL_RID, sc->xl_res); + error = ENXIO; + goto fail; + } + + bzero(sc->xl_ldata, sizeof(struct xl_list_data)); + + /* + * Figure out the card type. 3c905B adapters have the + * 'supportsNoTxLength' bit set in the capabilities + * word in the EEPROM. + */ + xl_read_eeprom(sc, (caddr_t)&sc->xl_caps, XL_EE_CAPS, 1, 0); + if (sc->xl_caps & XL_CAPS_NO_TXLENGTH) + sc->xl_type = XL_TYPE_905B; + else + sc->xl_type = XL_TYPE_90X; + + ifp = &sc->arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = "xl"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = xl_ioctl; + ifp->if_output = ether_output; + if (sc->xl_type == XL_TYPE_905B) { + ifp->if_start = xl_start_90xB; + ifp->if_hwassist = XL905B_CSUM_FEATURES; + ifp->if_capabilities = IFCAP_HWCSUM; + } else + ifp->if_start = xl_start; + ifp->if_watchdog = xl_watchdog; + ifp->if_init = xl_init; + ifp->if_baudrate = 10000000; + ifp->if_snd.ifq_maxlen = XL_TX_LIST_CNT - 1; + ifp->if_capenable = ifp->if_capabilities; + + /* + * Now we have to see what sort of media we have. + * This includes probing for an MII interace and a + * possible PHY. + */ + XL_SEL_WIN(3); + sc->xl_media = CSR_READ_2(sc, XL_W3_MEDIA_OPT); + if (bootverbose) + printf("xl%d: media options word: %x\n", sc->xl_unit, + sc->xl_media); + + xl_read_eeprom(sc, (char *)&sc->xl_xcvr, XL_EE_ICFG_0, 2, 0); + sc->xl_xcvr &= XL_ICFG_CONNECTOR_MASK; + sc->xl_xcvr >>= XL_ICFG_CONNECTOR_BITS; + + xl_mediacheck(sc); + + if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BTX + || sc->xl_media & XL_MEDIAOPT_BT4) { + if (bootverbose) + printf("xl%d: found MII/AUTO\n", sc->xl_unit); + xl_setcfg(sc); + if (mii_phy_probe(dev, &sc->xl_miibus, + xl_ifmedia_upd, xl_ifmedia_sts)) { + printf("xl%d: no PHY found!\n", sc->xl_unit); + bus_teardown_intr(dev, sc->xl_irq, sc->xl_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->xl_irq); + bus_release_resource(dev, XL_RES, XL_RID, sc->xl_res); + contigfree(sc->xl_ldata, + sizeof(struct xl_list_data), M_DEVBUF); + error = ENXIO; + goto fail; + } + + goto done; + } + + /* + * Sanity check. If the user has selected "auto" and this isn't + * a 10/100 card of some kind, we need to force the transceiver + * type to something sane. + */ + if (sc->xl_xcvr == XL_XCVR_AUTO) + xl_choose_xcvr(sc, bootverbose); + + /* + * Do ifmedia setup. + */ + + ifmedia_init(&sc->ifmedia, 0, xl_ifmedia_upd, xl_ifmedia_sts); + + if (sc->xl_media & XL_MEDIAOPT_BT) { + if (bootverbose) + printf("xl%d: found 10baseT\n", sc->xl_unit); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); + if (sc->xl_caps & XL_CAPS_FULL_DUPLEX) + ifmedia_add(&sc->ifmedia, + IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); + } + + if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) { + /* + * Check for a 10baseFL board in disguise. + */ + if (sc->xl_type == XL_TYPE_905B && + sc->xl_media == XL_MEDIAOPT_10FL) { + if (bootverbose) + printf("xl%d: found 10baseFL\n", sc->xl_unit); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_FL, 0, NULL); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_FL|IFM_HDX, + 0, NULL); + if (sc->xl_caps & XL_CAPS_FULL_DUPLEX) + ifmedia_add(&sc->ifmedia, + IFM_ETHER|IFM_10_FL|IFM_FDX, 0, NULL); + } else { + if (bootverbose) + printf("xl%d: found AUI\n", sc->xl_unit); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); + } + } + + if (sc->xl_media & XL_MEDIAOPT_BNC) { + if (bootverbose) + printf("xl%d: found BNC\n", sc->xl_unit); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL); + } + + if (sc->xl_media & XL_MEDIAOPT_BFX) { + if (bootverbose) + printf("xl%d: found 100baseFX\n", sc->xl_unit); + ifp->if_baudrate = 100000000; + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_FX, 0, NULL); + } + + /* Choose a default media. */ + switch(sc->xl_xcvr) { + case XL_XCVR_10BT: + media = IFM_ETHER|IFM_10_T; + xl_setmode(sc, media); + break; + case XL_XCVR_AUI: + if (sc->xl_type == XL_TYPE_905B && + sc->xl_media == XL_MEDIAOPT_10FL) { + media = IFM_ETHER|IFM_10_FL; + xl_setmode(sc, media); + } else { + media = IFM_ETHER|IFM_10_5; + xl_setmode(sc, media); + } + break; + case XL_XCVR_COAX: + media = IFM_ETHER|IFM_10_2; + xl_setmode(sc, media); + break; + case XL_XCVR_AUTO: + case XL_XCVR_100BTX: + case XL_XCVR_MII: + /* Chosen by miibus */ + break; + case XL_XCVR_100BFX: + media = IFM_ETHER|IFM_100_FX; + break; + default: + printf("xl%d: unknown XCVR type: %d\n", sc->xl_unit, + sc->xl_xcvr); + /* + * This will probably be wrong, but it prevents + * the ifmedia code from panicking. + */ + media = IFM_ETHER|IFM_10_T; + break; + } + + if (sc->xl_miibus == NULL) + ifmedia_set(&sc->ifmedia, media); + +done: + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); + XL_UNLOCK(sc); + return(0); + +fail: + XL_UNLOCK(sc); + mtx_destroy(&sc->xl_mtx); + + return(error); +} + +static int xl_detach(dev) + device_t dev; +{ + struct xl_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + XL_LOCK(sc); + ifp = &sc->arpcom.ac_if; + + xl_reset(sc); + xl_stop(sc); + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + + /* Delete any miibus and phy devices attached to this interface */ + if (sc->xl_miibus != NULL) { + bus_generic_detach(dev); + device_delete_child(dev, sc->xl_miibus); + } + + bus_teardown_intr(dev, sc->xl_irq, sc->xl_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->xl_irq); + if (sc->xl_fres != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, + XL_PCI_FUNCMEM, sc->xl_fres); + bus_release_resource(dev, XL_RES, XL_RID, sc->xl_res); + + ifmedia_removeall(&sc->ifmedia); + contigfree(sc->xl_ldata, sizeof(struct xl_list_data), M_DEVBUF); + + XL_UNLOCK(sc); + mtx_destroy(&sc->xl_mtx); + + return(0); +} + +/* + * Initialize the transmit descriptors. + */ +static int xl_list_tx_init(sc) + struct xl_softc *sc; +{ + struct xl_chain_data *cd; + struct xl_list_data *ld; + int i; + + cd = &sc->xl_cdata; + ld = sc->xl_ldata; + for (i = 0; i < XL_TX_LIST_CNT; i++) { + cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i]; + if (i == (XL_TX_LIST_CNT - 1)) + cd->xl_tx_chain[i].xl_next = NULL; + else + cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[i + 1]; + } + + cd->xl_tx_free = &cd->xl_tx_chain[0]; + cd->xl_tx_tail = cd->xl_tx_head = NULL; + + return(0); +} + +/* + * Initialize the transmit descriptors. + */ +static int xl_list_tx_init_90xB(sc) + struct xl_softc *sc; +{ + struct xl_chain_data *cd; + struct xl_list_data *ld; + int i; + + cd = &sc->xl_cdata; + ld = sc->xl_ldata; + for (i = 0; i < XL_TX_LIST_CNT; i++) { + cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i]; + cd->xl_tx_chain[i].xl_phys = vtophys(&ld->xl_tx_list[i]); + if (i == (XL_TX_LIST_CNT - 1)) + cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[0]; + else + cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[i + 1]; + if (i == 0) + cd->xl_tx_chain[i].xl_prev = + &cd->xl_tx_chain[XL_TX_LIST_CNT - 1]; + else + cd->xl_tx_chain[i].xl_prev = + &cd->xl_tx_chain[i - 1]; + } + + bzero((char *)ld->xl_tx_list, + sizeof(struct xl_list) * XL_TX_LIST_CNT); + ld->xl_tx_list[0].xl_status = XL_TXSTAT_EMPTY; + + cd->xl_tx_prod = 1; + cd->xl_tx_cons = 1; + cd->xl_tx_cnt = 0; + + 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 xl_list_rx_init(sc) + struct xl_softc *sc; +{ + struct xl_chain_data *cd; + struct xl_list_data *ld; + int i; + + cd = &sc->xl_cdata; + ld = sc->xl_ldata; + + for (i = 0; i < XL_RX_LIST_CNT; i++) { + cd->xl_rx_chain[i].xl_ptr = + (struct xl_list_onefrag *)&ld->xl_rx_list[i]; + if (xl_newbuf(sc, &cd->xl_rx_chain[i]) == ENOBUFS) + return(ENOBUFS); + if (i == (XL_RX_LIST_CNT - 1)) { + cd->xl_rx_chain[i].xl_next = &cd->xl_rx_chain[0]; + ld->xl_rx_list[i].xl_next = + vtophys(&ld->xl_rx_list[0]); + } else { + cd->xl_rx_chain[i].xl_next = &cd->xl_rx_chain[i + 1]; + ld->xl_rx_list[i].xl_next = + vtophys(&ld->xl_rx_list[i + 1]); + } + } + + cd->xl_rx_head = &cd->xl_rx_chain[0]; + + return(0); +} + +/* + * Initialize an RX descriptor and attach an MBUF cluster. + */ +static int xl_newbuf(sc, c) + struct xl_softc *sc; + struct xl_chain_onefrag *c; +{ + struct mbuf *m_new = NULL; + + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(ENOBUFS); + + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + return(ENOBUFS); + } + + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + + /* Force longword alignment for packet payload. */ + m_adj(m_new, ETHER_ALIGN); + + c->xl_mbuf = m_new; + c->xl_ptr->xl_frag.xl_addr = vtophys(mtod(m_new, caddr_t)); + c->xl_ptr->xl_frag.xl_len = MCLBYTES | XL_LAST_FRAG; + c->xl_ptr->xl_status = 0; + + return(0); +} + +static int xl_rx_resync(sc) + struct xl_softc *sc; +{ + struct xl_chain_onefrag *pos; + int i; + + pos = sc->xl_cdata.xl_rx_head; + + for (i = 0; i < XL_RX_LIST_CNT; i++) { + if (pos->xl_ptr->xl_status) + break; + pos = pos->xl_next; + } + + if (i == XL_RX_LIST_CNT) + return(0); + + sc->xl_cdata.xl_rx_head = pos; + + return(EAGAIN); +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +static void xl_rxeof(sc) + struct xl_softc *sc; +{ + struct ether_header *eh; + struct mbuf *m; + struct ifnet *ifp; + struct xl_chain_onefrag *cur_rx; + int total_len = 0; + u_int32_t rxstat; + + ifp = &sc->arpcom.ac_if; + +again: + + while((rxstat = sc->xl_cdata.xl_rx_head->xl_ptr->xl_status)) { + cur_rx = sc->xl_cdata.xl_rx_head; + sc->xl_cdata.xl_rx_head = cur_rx->xl_next; + + /* + * If an error occurs, update stats, clear the + * status word and leave the mbuf cluster in place: + * it should simply get re-used next time this descriptor + * comes up in the ring. + */ + if (rxstat & XL_RXSTAT_UP_ERROR) { + ifp->if_ierrors++; + cur_rx->xl_ptr->xl_status = 0; + continue; + } + + /* + * If there error bit was not set, the upload complete + * bit should be set which means we have a valid packet. + * If not, something truly strange has happened. + */ + if (!(rxstat & XL_RXSTAT_UP_CMPLT)) { + printf("xl%d: bad receive status -- " + "packet dropped", sc->xl_unit); + ifp->if_ierrors++; + cur_rx->xl_ptr->xl_status = 0; + continue; + } + + /* No errors; receive the packet. */ + m = cur_rx->xl_mbuf; + total_len = cur_rx->xl_ptr->xl_status & XL_RXSTAT_LENMASK; + + /* + * Try to conjure up a new mbuf cluster. If that + * fails, it means we have an out of memory condition and + * should leave the buffer in place and continue. This will + * result in a lost packet, but there's little else we + * can do in this situation. + */ + if (xl_newbuf(sc, cur_rx) == ENOBUFS) { + ifp->if_ierrors++; + cur_rx->xl_ptr->xl_status = 0; + continue; + } + + ifp->if_ipackets++; + eh = mtod(m, struct ether_header *); + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = total_len; + + /* Remove header from mbuf and pass it on. */ + m_adj(m, sizeof(struct ether_header)); + + if (sc->xl_type == XL_TYPE_905B) { + /* Do IP checksum checking. */ + if (rxstat & XL_RXSTAT_IPCKOK) + m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; + if (!(rxstat & XL_RXSTAT_IPCKERR)) + m->m_pkthdr.csum_flags |= CSUM_IP_VALID; + if ((rxstat & XL_RXSTAT_TCPCOK && + !(rxstat & XL_RXSTAT_TCPCKERR)) || + (rxstat & XL_RXSTAT_UDPCKOK && + !(rxstat & XL_RXSTAT_UDPCKERR))) { + m->m_pkthdr.csum_flags |= + CSUM_DATA_VALID|CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xffff; + } + } + ether_input(ifp, eh, m); + } + + /* + * Handle the 'end of channel' condition. When the upload + * engine hits the end of the RX ring, it will stall. This + * is our cue to flush the RX ring, reload the uplist pointer + * register and unstall the engine. + * XXX This is actually a little goofy. With the ThunderLAN + * chip, you get an interrupt when the receiver hits the end + * of the receive ring, which tells you exactly when you + * you need to reload the ring pointer. Here we have to + * fake it. I'm mad at myself for not being clever enough + * to avoid the use of a goto here. + */ + if (CSR_READ_4(sc, XL_UPLIST_PTR) == 0 || + CSR_READ_4(sc, XL_UPLIST_STATUS) & XL_PKTSTAT_UP_STALLED) { + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL); + xl_wait(sc); + CSR_WRITE_4(sc, XL_UPLIST_PTR, + vtophys(&sc->xl_ldata->xl_rx_list[0])); + sc->xl_cdata.xl_rx_head = &sc->xl_cdata.xl_rx_chain[0]; + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL); + goto again; + } + + return; +} + +/* + * A frame was downloaded to the chip. It's safe for us to clean up + * the list buffers. + */ +static void xl_txeof(sc) + struct xl_softc *sc; +{ + struct xl_chain *cur_tx; + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + + /* Clear the timeout timer. */ + ifp->if_timer = 0; + + /* + * Go through our tx list and free mbufs for those + * frames that have been uploaded. Note: the 3c905B + * sets a special bit in the status word to let us + * know that a frame has been downloaded, but the + * original 3c900/3c905 adapters don't do that. + * Consequently, we have to use a different test if + * xl_type != XL_TYPE_905B. + */ + while(sc->xl_cdata.xl_tx_head != NULL) { + cur_tx = sc->xl_cdata.xl_tx_head; + + if (CSR_READ_4(sc, XL_DOWNLIST_PTR)) + break; + + sc->xl_cdata.xl_tx_head = cur_tx->xl_next; + m_freem(cur_tx->xl_mbuf); + cur_tx->xl_mbuf = NULL; + ifp->if_opackets++; + + cur_tx->xl_next = sc->xl_cdata.xl_tx_free; + sc->xl_cdata.xl_tx_free = cur_tx; + } + + if (sc->xl_cdata.xl_tx_head == NULL) { + ifp->if_flags &= ~IFF_OACTIVE; + sc->xl_cdata.xl_tx_tail = NULL; + } else { + if (CSR_READ_4(sc, XL_DMACTL) & XL_DMACTL_DOWN_STALLED || + !CSR_READ_4(sc, XL_DOWNLIST_PTR)) { + CSR_WRITE_4(sc, XL_DOWNLIST_PTR, + vtophys(sc->xl_cdata.xl_tx_head->xl_ptr)); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); + } + } + + return; +} + +static void xl_txeof_90xB(sc) + struct xl_softc *sc; +{ + struct xl_chain *cur_tx = NULL; + struct ifnet *ifp; + int idx; + + ifp = &sc->arpcom.ac_if; + + idx = sc->xl_cdata.xl_tx_cons; + while(idx != sc->xl_cdata.xl_tx_prod) { + + cur_tx = &sc->xl_cdata.xl_tx_chain[idx]; + + if (!(cur_tx->xl_ptr->xl_status & XL_TXSTAT_DL_COMPLETE)) + break; + + if (cur_tx->xl_mbuf != NULL) { + m_freem(cur_tx->xl_mbuf); + cur_tx->xl_mbuf = NULL; + } + + ifp->if_opackets++; + + sc->xl_cdata.xl_tx_cnt--; + XL_INC(idx, XL_TX_LIST_CNT); + ifp->if_timer = 0; + } + + sc->xl_cdata.xl_tx_cons = idx; + + if (cur_tx != NULL) + ifp->if_flags &= ~IFF_OACTIVE; + + return; +} + +/* + * TX 'end of channel' interrupt handler. Actually, we should + * only get a 'TX complete' interrupt if there's a transmit error, + * so this is really TX error handler. + */ +static void xl_txeoc(sc) + struct xl_softc *sc; +{ + u_int8_t txstat; + + while((txstat = CSR_READ_1(sc, XL_TX_STATUS))) { + if (txstat & XL_TXSTATUS_UNDERRUN || + txstat & XL_TXSTATUS_JABBER || + txstat & XL_TXSTATUS_RECLAIM) { + printf("xl%d: transmission error: %x\n", + sc->xl_unit, txstat); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); + xl_wait(sc); + if (sc->xl_type == XL_TYPE_905B) { + if (sc->xl_cdata.xl_tx_cnt) { + int i; + struct xl_chain *c; + i = sc->xl_cdata.xl_tx_cons; + c = &sc->xl_cdata.xl_tx_chain[i]; + CSR_WRITE_4(sc, XL_DOWNLIST_PTR, + c->xl_phys); + CSR_WRITE_1(sc, XL_DOWN_POLL, 64); + } + } else { + if (sc->xl_cdata.xl_tx_head != NULL) + CSR_WRITE_4(sc, XL_DOWNLIST_PTR, + vtophys(sc->xl_cdata.xl_tx_head->xl_ptr)); + } + /* + * Remember to set this for the + * first generation 3c90X chips. + */ + CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8); + if (txstat & XL_TXSTATUS_UNDERRUN && + sc->xl_tx_thresh < XL_PACKET_SIZE) { + sc->xl_tx_thresh += XL_MIN_FRAMELEN; + printf("xl%d: tx underrun, increasing tx start" + " threshold to %d bytes\n", sc->xl_unit, + sc->xl_tx_thresh); + } + CSR_WRITE_2(sc, XL_COMMAND, + XL_CMD_TX_SET_START|sc->xl_tx_thresh); + if (sc->xl_type == XL_TYPE_905B) { + CSR_WRITE_2(sc, XL_COMMAND, + XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4)); + } + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); + } else { + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); + } + /* + * Write an arbitrary byte to the TX_STATUS register + * to clear this interrupt/error and advance to the next. + */ + CSR_WRITE_1(sc, XL_TX_STATUS, 0x01); + } + + return; +} + +static void xl_intr(arg) + void *arg; +{ + struct xl_softc *sc; + struct ifnet *ifp; + u_int16_t status; + + sc = arg; + XL_LOCK(sc); + ifp = &sc->arpcom.ac_if; + + while((status = CSR_READ_2(sc, XL_STATUS)) & XL_INTRS && status != 0xFFFF) { + + CSR_WRITE_2(sc, XL_COMMAND, + XL_CMD_INTR_ACK|(status & XL_INTRS)); + + if (status & XL_STAT_UP_COMPLETE) { + int curpkts; + + curpkts = ifp->if_ipackets; + xl_rxeof(sc); + if (curpkts == ifp->if_ipackets) { + while (xl_rx_resync(sc)) + xl_rxeof(sc); + } + } + + if (status & XL_STAT_DOWN_COMPLETE) { + if (sc->xl_type == XL_TYPE_905B) + xl_txeof_90xB(sc); + else + xl_txeof(sc); + } + + if (status & XL_STAT_TX_COMPLETE) { + ifp->if_oerrors++; + xl_txeoc(sc); + } + + if (status & XL_STAT_ADFAIL) { + xl_reset(sc); + xl_init(sc); + } + + if (status & XL_STAT_STATSOFLOW) { + sc->xl_stats_no_timeout = 1; + xl_stats_update(sc); + sc->xl_stats_no_timeout = 0; + } + } + + if (ifp->if_snd.ifq_head != NULL) + (*ifp->if_start)(ifp); + + XL_UNLOCK(sc); + + return; +} + +static void xl_stats_update(xsc) + void *xsc; +{ + struct xl_softc *sc; + struct ifnet *ifp; + struct xl_stats xl_stats; + u_int8_t *p; + int i; + struct mii_data *mii = NULL; + + bzero((char *)&xl_stats, sizeof(struct xl_stats)); + + sc = xsc; + ifp = &sc->arpcom.ac_if; + if (sc->xl_miibus != NULL) + mii = device_get_softc(sc->xl_miibus); + + p = (u_int8_t *)&xl_stats; + + /* Read all the stats registers. */ + XL_SEL_WIN(6); + + for (i = 0; i < 16; i++) + *p++ = CSR_READ_1(sc, XL_W6_CARRIER_LOST + i); + + ifp->if_ierrors += xl_stats.xl_rx_overrun; + + ifp->if_collisions += xl_stats.xl_tx_multi_collision + + xl_stats.xl_tx_single_collision + + xl_stats.xl_tx_late_collision; + + /* + * Boomerang and cyclone chips have an extra stats counter + * in window 4 (BadSSD). We have to read this too in order + * to clear out all the stats registers and avoid a statsoflow + * interrupt. + */ + XL_SEL_WIN(4); + CSR_READ_1(sc, XL_W4_BADSSD); + + if ((mii != NULL) && (!sc->xl_stats_no_timeout)) + mii_tick(mii); + + XL_SEL_WIN(7); + + if (!sc->xl_stats_no_timeout) + sc->xl_stat_ch = timeout(xl_stats_update, sc, hz); + + return; +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int xl_encap(sc, c, m_head) + struct xl_softc *sc; + struct xl_chain *c; + struct mbuf *m_head; +{ + int frag = 0; + struct xl_frag *f = NULL; + int total_len; + struct mbuf *m; + + /* + * Start packing the mbufs in this chain into + * the fragment pointers. Stop when we run out + * of fragments or hit the end of the mbuf chain. + */ + m = m_head; + total_len = 0; + + for (m = m_head, frag = 0; m != NULL; m = m->m_next) { + if (m->m_len != 0) { + if (frag == XL_MAXFRAGS) + break; + total_len+= m->m_len; + c->xl_ptr->xl_frag[frag].xl_addr = + vtophys(mtod(m, vm_offset_t)); + c->xl_ptr->xl_frag[frag].xl_len = m->m_len; + frag++; + } + } + + /* + * Handle special case: we used up all 63 fragments, + * but we have more mbufs left in the chain. Copy the + * data into an mbuf cluster. Note that we don't + * bother clearing the values in the other fragment + * pointers/counters; it wouldn't gain us anything, + * and would waste cycles. + */ + if (m != NULL) { + struct mbuf *m_new = NULL; + + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) { + printf("xl%d: no memory for tx list", sc->xl_unit); + return(1); + } + if (m_head->m_pkthdr.len > MHLEN) { + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + printf("xl%d: no memory for tx list", + sc->xl_unit); + return(1); + } + } + m_copydata(m_head, 0, m_head->m_pkthdr.len, + mtod(m_new, caddr_t)); + m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; + m_freem(m_head); + m_head = m_new; + f = &c->xl_ptr->xl_frag[0]; + f->xl_addr = vtophys(mtod(m_new, caddr_t)); + f->xl_len = total_len = m_new->m_len; + frag = 1; + } + + c->xl_mbuf = m_head; + c->xl_ptr->xl_frag[frag - 1].xl_len |= XL_LAST_FRAG; + c->xl_ptr->xl_status = total_len; + c->xl_ptr->xl_next = 0; + + return(0); +} + +/* + * Main transmit routine. To avoid having to do mbuf copies, we put pointers + * to the mbuf data regions directly in the transmit lists. We also save a + * copy of the pointers since the transmit list fragment pointers are + * physical addresses. + */ +static void xl_start(ifp) + struct ifnet *ifp; +{ + struct xl_softc *sc; + struct mbuf *m_head = NULL; + struct xl_chain *prev = NULL, *cur_tx = NULL, *start_tx; + + sc = ifp->if_softc; + XL_LOCK(sc); + /* + * Check for an available queue slot. If there are none, + * punt. + */ + if (sc->xl_cdata.xl_tx_free == NULL) { + xl_txeoc(sc); + xl_txeof(sc); + if (sc->xl_cdata.xl_tx_free == NULL) { + ifp->if_flags |= IFF_OACTIVE; + XL_UNLOCK(sc); + return; + } + } + + start_tx = sc->xl_cdata.xl_tx_free; + + while(sc->xl_cdata.xl_tx_free != NULL) { + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + /* Pick a descriptor off the free list. */ + cur_tx = sc->xl_cdata.xl_tx_free; + sc->xl_cdata.xl_tx_free = cur_tx->xl_next; + + cur_tx->xl_next = NULL; + + /* Pack the data into the descriptor. */ + xl_encap(sc, cur_tx, m_head); + + /* Chain it together. */ + if (prev != NULL) { + prev->xl_next = cur_tx; + prev->xl_ptr->xl_next = vtophys(cur_tx->xl_ptr); + } + prev = cur_tx; + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, cur_tx->xl_mbuf); + } + + /* + * If there are no packets queued, bail. + */ + if (cur_tx == NULL) { + XL_UNLOCK(sc); + return; + } + + /* + * Place the request for the upload interrupt + * in the last descriptor in the chain. This way, if + * we're chaining several packets at once, we'll only + * get an interupt once for the whole chain rather than + * once for each packet. + */ + cur_tx->xl_ptr->xl_status |= XL_TXSTAT_DL_INTR; + + /* + * Queue the packets. If the TX channel is clear, update + * the downlist pointer register. + */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL); + xl_wait(sc); + + if (sc->xl_cdata.xl_tx_head != NULL) { + sc->xl_cdata.xl_tx_tail->xl_next = start_tx; + sc->xl_cdata.xl_tx_tail->xl_ptr->xl_next = + vtophys(start_tx->xl_ptr); + sc->xl_cdata.xl_tx_tail->xl_ptr->xl_status &= + ~XL_TXSTAT_DL_INTR; + sc->xl_cdata.xl_tx_tail = cur_tx; + } else { + sc->xl_cdata.xl_tx_head = start_tx; + sc->xl_cdata.xl_tx_tail = cur_tx; + } + if (!CSR_READ_4(sc, XL_DOWNLIST_PTR)) + CSR_WRITE_4(sc, XL_DOWNLIST_PTR, vtophys(start_tx->xl_ptr)); + + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); + + XL_SEL_WIN(7); + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + + /* + * XXX Under certain conditions, usually on slower machines + * where interrupts may be dropped, it's possible for the + * adapter to chew up all the buffers in the receive ring + * and stall, without us being able to do anything about it. + * To guard against this, we need to make a pass over the + * RX queue to make sure there aren't any packets pending. + * Doing it here means we can flush the receive ring at the + * same time the chip is DMAing the transmit descriptors we + * just gave it. + * + * 3Com goes to some lengths to emphasize the Parallel Tasking (tm) + * nature of their chips in all their marketing literature; + * we may as well take advantage of it. :) + */ + xl_rxeof(sc); + + XL_UNLOCK(sc); + + return; +} + +static int xl_encap_90xB(sc, c, m_head) + struct xl_softc *sc; + struct xl_chain *c; + struct mbuf *m_head; +{ + int frag = 0; + struct xl_frag *f = NULL; + struct mbuf *m; + struct xl_list *d; + + /* + * Start packing the mbufs in this chain into + * the fragment pointers. Stop when we run out + * of fragments or hit the end of the mbuf chain. + */ + d = c->xl_ptr; + d->xl_status = 0; + d->xl_next = 0; + + for (m = m_head, frag = 0; m != NULL; m = m->m_next) { + if (m->m_len != 0) { + if (frag == XL_MAXFRAGS) + break; + f = &d->xl_frag[frag]; + f->xl_addr = vtophys(mtod(m, vm_offset_t)); + f->xl_len = m->m_len; + frag++; + } + } + + c->xl_mbuf = m_head; + c->xl_ptr->xl_frag[frag - 1].xl_len |= XL_LAST_FRAG; + c->xl_ptr->xl_status = XL_TXSTAT_RND_DEFEAT; + + if (m_head->m_pkthdr.csum_flags) { + if (m_head->m_pkthdr.csum_flags & CSUM_IP) + c->xl_ptr->xl_status |= XL_TXSTAT_IPCKSUM; + if (m_head->m_pkthdr.csum_flags & CSUM_TCP) + c->xl_ptr->xl_status |= XL_TXSTAT_TCPCKSUM; + if (m_head->m_pkthdr.csum_flags & CSUM_UDP) + c->xl_ptr->xl_status |= XL_TXSTAT_UDPCKSUM; + } + return(0); +} + +static void xl_start_90xB(ifp) + struct ifnet *ifp; +{ + struct xl_softc *sc; + struct mbuf *m_head = NULL; + struct xl_chain *prev = NULL, *cur_tx = NULL, *start_tx; + int idx; + + sc = ifp->if_softc; + XL_LOCK(sc); + + if (ifp->if_flags & IFF_OACTIVE) { + XL_UNLOCK(sc); + return; + } + + idx = sc->xl_cdata.xl_tx_prod; + start_tx = &sc->xl_cdata.xl_tx_chain[idx]; + + while (sc->xl_cdata.xl_tx_chain[idx].xl_mbuf == NULL) { + + if ((XL_TX_LIST_CNT - sc->xl_cdata.xl_tx_cnt) < 3) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + cur_tx = &sc->xl_cdata.xl_tx_chain[idx]; + + /* Pack the data into the descriptor. */ + xl_encap_90xB(sc, cur_tx, m_head); + + /* Chain it together. */ + if (prev != NULL) + prev->xl_ptr->xl_next = cur_tx->xl_phys; + prev = cur_tx; + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, cur_tx->xl_mbuf); + + XL_INC(idx, XL_TX_LIST_CNT); + sc->xl_cdata.xl_tx_cnt++; + } + + /* + * If there are no packets queued, bail. + */ + if (cur_tx == NULL) { + XL_UNLOCK(sc); + return; + } + + /* + * Place the request for the upload interrupt + * in the last descriptor in the chain. This way, if + * we're chaining several packets at once, we'll only + * get an interupt once for the whole chain rather than + * once for each packet. + */ + cur_tx->xl_ptr->xl_status |= XL_TXSTAT_DL_INTR; + + /* Start transmission */ + sc->xl_cdata.xl_tx_prod = idx; + start_tx->xl_prev->xl_ptr->xl_next = start_tx->xl_phys; + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + + XL_UNLOCK(sc); + + return; +} + +static void xl_init(xsc) + void *xsc; +{ + struct xl_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + int i; + u_int16_t rxfilt = 0; + struct mii_data *mii = NULL; + + XL_LOCK(sc); + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + xl_stop(sc); + + if (sc->xl_miibus == NULL) { + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); + xl_wait(sc); + } + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); + xl_wait(sc); + DELAY(10000); + + if (sc->xl_miibus != NULL) + mii = device_get_softc(sc->xl_miibus); + + /* Init our MAC address */ + XL_SEL_WIN(2); + for (i = 0; i < ETHER_ADDR_LEN; i++) { + CSR_WRITE_1(sc, XL_W2_STATION_ADDR_LO + i, + sc->arpcom.ac_enaddr[i]); + } + + /* Clear the station mask. */ + for (i = 0; i < 3; i++) + CSR_WRITE_2(sc, XL_W2_STATION_MASK_LO + (i * 2), 0); +#ifdef notdef + /* Reset TX and RX. */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); + xl_wait(sc); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); + xl_wait(sc); +#endif + /* Init circular RX list. */ + if (xl_list_rx_init(sc) == ENOBUFS) { + printf("xl%d: initialization failed: no " + "memory for rx buffers\n", sc->xl_unit); + xl_stop(sc); + XL_UNLOCK(sc); + return; + } + + /* Init TX descriptors. */ + if (sc->xl_type == XL_TYPE_905B) + xl_list_tx_init_90xB(sc); + else + xl_list_tx_init(sc); + + /* + * Set the TX freethresh value. + * Note that this has no effect on 3c905B "cyclone" + * cards but is required for 3c900/3c905 "boomerang" + * cards in order to enable the download engine. + */ + CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8); + + /* Set the TX start threshold for best performance. */ + sc->xl_tx_thresh = XL_MIN_FRAMELEN; + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_SET_START|sc->xl_tx_thresh); + + /* + * If this is a 3c905B, also set the tx reclaim threshold. + * This helps cut down on the number of tx reclaim errors + * that could happen on a busy network. The chip multiplies + * the register value by 16 to obtain the actual threshold + * in bytes, so we divide by 16 when setting the value here. + * The existing threshold value can be examined by reading + * the register at offset 9 in window 5. + */ + if (sc->xl_type == XL_TYPE_905B) { + CSR_WRITE_2(sc, XL_COMMAND, + XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4)); + } + + /* Set RX filter bits. */ + XL_SEL_WIN(5); + rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); + + /* Set the individual bit to receive frames for this host only. */ + rxfilt |= XL_RXFILTER_INDIVIDUAL; + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) { + rxfilt |= XL_RXFILTER_ALLFRAMES; + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); + } else { + rxfilt &= ~XL_RXFILTER_ALLFRAMES; + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); + } + + /* + * Set capture broadcast bit to capture broadcast frames. + */ + if (ifp->if_flags & IFF_BROADCAST) { + rxfilt |= XL_RXFILTER_BROADCAST; + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); + } else { + rxfilt &= ~XL_RXFILTER_BROADCAST; + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); + } + + /* + * Program the multicast filter, if necessary. + */ + if (sc->xl_type == XL_TYPE_905B) + xl_setmulti_hash(sc); + else + xl_setmulti(sc); + + /* + * Load the address of the RX list. We have to + * stall the upload engine before we can manipulate + * the uplist pointer register, then unstall it when + * we're finished. We also have to wait for the + * stall command to complete before proceeding. + * Note that we have to do this after any RX resets + * have completed since the uplist register is cleared + * by a reset. + */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL); + xl_wait(sc); + CSR_WRITE_4(sc, XL_UPLIST_PTR, vtophys(&sc->xl_ldata->xl_rx_list[0])); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL); + xl_wait(sc); + + + if (sc->xl_type == XL_TYPE_905B) { + /* Set polling interval */ + CSR_WRITE_1(sc, XL_DOWN_POLL, 64); + /* Load the address of the TX list */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL); + xl_wait(sc); + CSR_WRITE_4(sc, XL_DOWNLIST_PTR, + vtophys(&sc->xl_ldata->xl_tx_list[0])); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); + xl_wait(sc); + } + + /* + * If the coax transceiver is on, make sure to enable + * the DC-DC converter. + */ + XL_SEL_WIN(3); + if (sc->xl_xcvr == XL_XCVR_COAX) + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START); + else + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); + + /* increase packet size to allow reception of 802.1q or ISL packets */ + if (sc->xl_type == XL_TYPE_905B) + CSR_WRITE_2(sc, XL_W3_MAXPKTSIZE, XL_PACKET_SIZE); + /* Clear out the stats counters. */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE); + sc->xl_stats_no_timeout = 1; + xl_stats_update(sc); + sc->xl_stats_no_timeout = 0; + XL_SEL_WIN(4); + CSR_WRITE_2(sc, XL_W4_NET_DIAG, XL_NETDIAG_UPPER_BYTES_ENABLE); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_ENABLE); + + /* + * Enable interrupts. + */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|0xFF); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|XL_INTRS); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|XL_INTRS); + if (sc->xl_flags & XL_FLAG_FUNCREG) + bus_space_write_4(sc->xl_ftag, sc->xl_fhandle, 4, 0x8000); + + /* Set the RX early threshold */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_THRESH|(XL_PACKET_SIZE >>2)); + CSR_WRITE_2(sc, XL_DMACTL, XL_DMACTL_UP_RX_EARLY); + + /* Enable receiver and transmitter. */ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); + xl_wait(sc); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_ENABLE); + xl_wait(sc); + + if (mii != NULL) + mii_mediachg(mii); + + /* Select window 7 for normal operations. */ + XL_SEL_WIN(7); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + sc->xl_stat_ch = timeout(xl_stats_update, sc, hz); + + XL_UNLOCK(sc); + + return; +} + +/* + * Set media options. + */ +static int xl_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct xl_softc *sc; + struct ifmedia *ifm = NULL; + struct mii_data *mii = NULL; + + sc = ifp->if_softc; + if (sc->xl_miibus != NULL) + mii = device_get_softc(sc->xl_miibus); + if (mii == NULL) + ifm = &sc->ifmedia; + else + ifm = &mii->mii_media; + + switch(IFM_SUBTYPE(ifm->ifm_media)) { + case IFM_100_FX: + case IFM_10_FL: + case IFM_10_2: + case IFM_10_5: + xl_setmode(sc, ifm->ifm_media); + return(0); + break; + default: + break; + } + + if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BTX + || sc->xl_media & XL_MEDIAOPT_BT4) { + xl_init(sc); + } else { + xl_setmode(sc, ifm->ifm_media); + } + + return(0); +} + +/* + * Report current media status. + */ +static void xl_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct xl_softc *sc; + u_int32_t icfg; + struct mii_data *mii = NULL; + + sc = ifp->if_softc; + if (sc->xl_miibus != NULL) + mii = device_get_softc(sc->xl_miibus); + + XL_SEL_WIN(3); + icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG) & XL_ICFG_CONNECTOR_MASK; + icfg >>= XL_ICFG_CONNECTOR_BITS; + + ifmr->ifm_active = IFM_ETHER; + + switch(icfg) { + case XL_XCVR_10BT: + ifmr->ifm_active = IFM_ETHER|IFM_10_T; + if (CSR_READ_1(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX) + ifmr->ifm_active |= IFM_FDX; + else + ifmr->ifm_active |= IFM_HDX; + break; + case XL_XCVR_AUI: + if (sc->xl_type == XL_TYPE_905B && + sc->xl_media == XL_MEDIAOPT_10FL) { + ifmr->ifm_active = IFM_ETHER|IFM_10_FL; + if (CSR_READ_1(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX) + ifmr->ifm_active |= IFM_FDX; + else + ifmr->ifm_active |= IFM_HDX; + } else + ifmr->ifm_active = IFM_ETHER|IFM_10_5; + break; + case XL_XCVR_COAX: + ifmr->ifm_active = IFM_ETHER|IFM_10_2; + break; + /* + * XXX MII and BTX/AUTO should be separate cases. + */ + + case XL_XCVR_100BTX: + case XL_XCVR_AUTO: + case XL_XCVR_MII: + if (mii != NULL) { + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + } + break; + case XL_XCVR_100BFX: + ifmr->ifm_active = IFM_ETHER|IFM_100_FX; + break; + default: + printf("xl%d: unknown XCVR type: %d\n", sc->xl_unit, icfg); + break; + } + + return; +} + +static int xl_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct xl_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + int error = 0; + struct mii_data *mii = NULL; + u_int8_t rxfilt; + + XL_LOCK(sc); + + switch(command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFFLAGS: + XL_SEL_WIN(5); + rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING && + ifp->if_flags & IFF_PROMISC && + !(sc->xl_if_flags & IFF_PROMISC)) { + rxfilt |= XL_RXFILTER_ALLFRAMES; + CSR_WRITE_2(sc, XL_COMMAND, + XL_CMD_RX_SET_FILT|rxfilt); + XL_SEL_WIN(7); + } else if (ifp->if_flags & IFF_RUNNING && + !(ifp->if_flags & IFF_PROMISC) && + sc->xl_if_flags & IFF_PROMISC) { + rxfilt &= ~XL_RXFILTER_ALLFRAMES; + CSR_WRITE_2(sc, XL_COMMAND, + XL_CMD_RX_SET_FILT|rxfilt); + XL_SEL_WIN(7); + } else + xl_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) + xl_stop(sc); + } + sc->xl_if_flags = ifp->if_flags; + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + if (sc->xl_type == XL_TYPE_905B) + xl_setmulti_hash(sc); + else + xl_setmulti(sc); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + if (sc->xl_miibus != NULL) + mii = device_get_softc(sc->xl_miibus); + if (mii == NULL) + error = ifmedia_ioctl(ifp, ifr, + &sc->ifmedia, command); + else + error = ifmedia_ioctl(ifp, ifr, + &mii->mii_media, command); + break; + default: + error = EINVAL; + break; + } + + XL_UNLOCK(sc); + + return(error); +} + +static void xl_watchdog(ifp) + struct ifnet *ifp; +{ + struct xl_softc *sc; + u_int16_t status = 0; + + sc = ifp->if_softc; + + XL_LOCK(sc); + + ifp->if_oerrors++; + XL_SEL_WIN(4); + status = CSR_READ_2(sc, XL_W4_MEDIA_STATUS); + printf("xl%d: watchdog timeout\n", sc->xl_unit); + + if (status & XL_MEDIASTAT_CARRIER) + printf("xl%d: no carrier - transceiver cable problem?\n", + sc->xl_unit); + xl_txeoc(sc); + xl_txeof(sc); + xl_rxeof(sc); + xl_reset(sc); + xl_init(sc); + + if (ifp->if_snd.ifq_head != NULL) + (*ifp->if_start)(ifp); + + XL_UNLOCK(sc); + + return; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void xl_stop(sc) + struct xl_softc *sc; +{ + register int i; + struct ifnet *ifp; + + XL_LOCK(sc); + + ifp = &sc->arpcom.ac_if; + ifp->if_timer = 0; + + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISABLE); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISCARD); + xl_wait(sc); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); + DELAY(800); + +#ifdef foo + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); + xl_wait(sc); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); + xl_wait(sc); +#endif + + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|XL_STAT_INTLATCH); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|0); + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0); + if (sc->xl_flags & XL_FLAG_FUNCREG) bus_space_write_4 (sc->xl_ftag, sc->xl_fhandle, 4, 0x8000); + + /* Stop the stats updater. */ + untimeout(xl_stats_update, sc, sc->xl_stat_ch); + + /* + * Free data in the RX lists. + */ + for (i = 0; i < XL_RX_LIST_CNT; i++) { + if (sc->xl_cdata.xl_rx_chain[i].xl_mbuf != NULL) { + m_freem(sc->xl_cdata.xl_rx_chain[i].xl_mbuf); + sc->xl_cdata.xl_rx_chain[i].xl_mbuf = NULL; + } + } + bzero((char *)&sc->xl_ldata->xl_rx_list, + sizeof(sc->xl_ldata->xl_rx_list)); + /* + * Free the TX list buffers. + */ + for (i = 0; i < XL_TX_LIST_CNT; i++) { + if (sc->xl_cdata.xl_tx_chain[i].xl_mbuf != NULL) { + m_freem(sc->xl_cdata.xl_tx_chain[i].xl_mbuf); + sc->xl_cdata.xl_tx_chain[i].xl_mbuf = NULL; + } + } + bzero((char *)&sc->xl_ldata->xl_tx_list, + sizeof(sc->xl_ldata->xl_tx_list)); + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + XL_UNLOCK(sc); + + return; +} + +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static void xl_shutdown(dev) + device_t dev; +{ + struct xl_softc *sc; + + sc = device_get_softc(dev); + + XL_LOCK(sc); + xl_reset(sc); + xl_stop(sc); + XL_UNLOCK(sc); + + return; +} + +static int xl_suspend(dev) + device_t dev; +{ + struct xl_softc *sc; + + sc = device_get_softc(dev); + + XL_LOCK(sc); + xl_stop(sc); + XL_UNLOCK(sc); + + return(0); +} + +static int xl_resume(dev) + device_t dev; +{ + struct xl_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + XL_LOCK(sc); + ifp = &sc->arpcom.ac_if; + + xl_reset(sc); + if (ifp->if_flags & IFF_UP) + xl_init(sc); + + XL_UNLOCK(sc); + return(0); +} diff --git a/sys/pci/if_xlreg.h b/sys/pci/if_xlreg.h new file mode 100644 index 0000000..5e1b920 --- /dev/null +++ b/sys/pci/if_xlreg.h @@ -0,0 +1,720 @@ +/* + * Copyright (c) 1997, 1998 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +#define XL_EE_READ 0x0080 /* read, 5 bit address */ +#define XL_EE_WRITE 0x0040 /* write, 5 bit address */ +#define XL_EE_ERASE 0x00c0 /* erase, 5 bit address */ +#define XL_EE_EWEN 0x0030 /* erase, no data needed */ +#define XL_EE_8BIT_READ 0x0200 /* read, 8 bit address */ +#define XL_EE_BUSY 0x8000 + +#define XL_EE_EADDR0 0x00 /* station address, first word */ +#define XL_EE_EADDR1 0x01 /* station address, next word, */ +#define XL_EE_EADDR2 0x02 /* station address, last word */ +#define XL_EE_PRODID 0x03 /* product ID code */ +#define XL_EE_MDATA_DATE 0x04 /* manufacturing data, date */ +#define XL_EE_MDATA_DIV 0x05 /* manufacturing data, division */ +#define XL_EE_MDATA_PCODE 0x06 /* manufacturing data, product code */ +#define XL_EE_MFG_ID 0x07 +#define XL_EE_PCI_PARM 0x08 +#define XL_EE_ROM_ONFO 0x09 +#define XL_EE_OEM_ADR0 0x0A +#define XL_EE_OEM_ADR1 0x0B +#define XL_EE_OEM_ADR2 0x0C +#define XL_EE_SOFTINFO1 0x0D +#define XL_EE_COMPAT 0x0E +#define XL_EE_SOFTINFO2 0x0F +#define XL_EE_CAPS 0x10 /* capabilities word */ +#define XL_EE_RSVD0 0x11 +#define XL_EE_ICFG_0 0x12 +#define XL_EE_ICFG_1 0x13 +#define XL_EE_RSVD1 0x14 +#define XL_EE_SOFTINFO3 0x15 +#define XL_EE_RSVD_2 0x16 + +/* + * Bits in the capabilities word + */ +#define XL_CAPS_PNP 0x0001 +#define XL_CAPS_FULL_DUPLEX 0x0002 +#define XL_CAPS_LARGE_PKTS 0x0004 +#define XL_CAPS_SLAVE_DMA 0x0008 +#define XL_CAPS_SECOND_DMA 0x0010 +#define XL_CAPS_FULL_BM 0x0020 +#define XL_CAPS_FRAG_BM 0x0040 +#define XL_CAPS_CRC_PASSTHRU 0x0080 +#define XL_CAPS_TXDONE 0x0100 +#define XL_CAPS_NO_TXLENGTH 0x0200 +#define XL_CAPS_RX_REPEAT 0x0400 +#define XL_CAPS_SNOOPING 0x0800 +#define XL_CAPS_100MBPS 0x1000 +#define XL_CAPS_PWRMGMT 0x2000 + +#define XL_PACKET_SIZE 1540 + +/* + * Register layouts. + */ +#define XL_COMMAND 0x0E +#define XL_STATUS 0x0E + +#define XL_TX_STATUS 0x1B +#define XL_TX_FREE 0x1C +#define XL_DMACTL 0x20 +#define XL_DOWNLIST_PTR 0x24 +#define XL_DOWN_POLL 0x2D /* 3c90xB only */ +#define XL_TX_FREETHRESH 0x2F +#define XL_UPLIST_PTR 0x38 +#define XL_UPLIST_STATUS 0x30 +#define XL_UP_POLL 0x3D /* 3c90xB only */ + +#define XL_PKTSTAT_UP_STALLED 0x00002000 +#define XL_PKTSTAT_UP_ERROR 0x00004000 +#define XL_PKTSTAT_UP_CMPLT 0x00008000 + +#define XL_DMACTL_DN_CMPLT_REQ 0x00000002 +#define XL_DMACTL_DOWN_STALLED 0x00000004 +#define XL_DMACTL_UP_CMPLT 0x00000008 +#define XL_DMACTL_DOWN_CMPLT 0x00000010 +#define XL_DMACTL_UP_RX_EARLY 0x00000020 +#define XL_DMACTL_ARM_COUNTDOWN 0x00000040 +#define XL_DMACTL_DOWN_INPROG 0x00000080 +#define XL_DMACTL_COUNTER_SPEED 0x00000100 +#define XL_DMACTL_DOWNDOWN_MODE 0x00000200 +#define XL_DMACTL_TARGET_ABORT 0x40000000 +#define XL_DMACTL_MASTER_ABORT 0x80000000 + +/* + * Command codes. Some command codes require that we wait for + * the CMD_BUSY flag to clear. Those codes are marked as 'mustwait.' + */ +#define XL_CMD_RESET 0x0000 /* mustwait */ +#define XL_CMD_WINSEL 0x0800 +#define XL_CMD_COAX_START 0x1000 +#define XL_CMD_RX_DISABLE 0x1800 +#define XL_CMD_RX_ENABLE 0x2000 +#define XL_CMD_RX_RESET 0x2800 /* mustwait */ +#define XL_CMD_UP_STALL 0x3000 /* mustwait */ +#define XL_CMD_UP_UNSTALL 0x3001 +#define XL_CMD_DOWN_STALL 0x3002 /* mustwait */ +#define XL_CMD_DOWN_UNSTALL 0x3003 +#define XL_CMD_RX_DISCARD 0x4000 +#define XL_CMD_TX_ENABLE 0x4800 +#define XL_CMD_TX_DISABLE 0x5000 +#define XL_CMD_TX_RESET 0x5800 /* mustwait */ +#define XL_CMD_INTR_FAKE 0x6000 +#define XL_CMD_INTR_ACK 0x6800 +#define XL_CMD_INTR_ENB 0x7000 +#define XL_CMD_STAT_ENB 0x7800 +#define XL_CMD_RX_SET_FILT 0x8000 +#define XL_CMD_RX_SET_THRESH 0x8800 +#define XL_CMD_TX_SET_THRESH 0x9000 +#define XL_CMD_TX_SET_START 0x9800 +#define XL_CMD_DMA_UP 0xA000 +#define XL_CMD_DMA_STOP 0xA001 +#define XL_CMD_STATS_ENABLE 0xA800 +#define XL_CMD_STATS_DISABLE 0xB000 +#define XL_CMD_COAX_STOP 0xB800 + +#define XL_CMD_SET_TX_RECLAIM 0xC000 /* 3c905B only */ +#define XL_CMD_RX_SET_HASH 0xC800 /* 3c905B only */ + +#define XL_HASH_SET 0x0400 +#define XL_HASHFILT_SIZE 256 + +/* + * status codes + * Note that bits 15 to 13 indicate the currently visible register window + * which may be anything from 0 to 7. + */ +#define XL_STAT_INTLATCH 0x0001 /* 0 */ +#define XL_STAT_ADFAIL 0x0002 /* 1 */ +#define XL_STAT_TX_COMPLETE 0x0004 /* 2 */ +#define XL_STAT_TX_AVAIL 0x0008 /* 3 first generation */ +#define XL_STAT_RX_COMPLETE 0x0010 /* 4 */ +#define XL_STAT_RX_EARLY 0x0020 /* 5 */ +#define XL_STAT_INTREQ 0x0040 /* 6 */ +#define XL_STAT_STATSOFLOW 0x0080 /* 7 */ +#define XL_STAT_DMADONE 0x0100 /* 8 first generation */ +#define XL_STAT_LINKSTAT 0x0100 /* 8 3c509B */ +#define XL_STAT_DOWN_COMPLETE 0x0200 /* 9 */ +#define XL_STAT_UP_COMPLETE 0x0400 /* 10 */ +#define XL_STAT_DMABUSY 0x0800 /* 11 first generation */ +#define XL_STAT_CMDBUSY 0x1000 /* 12 */ + +/* + * Interrupts we normally want enabled. + */ +#define XL_INTRS \ + (XL_STAT_UP_COMPLETE|XL_STAT_STATSOFLOW|XL_STAT_ADFAIL| \ + XL_STAT_DOWN_COMPLETE|XL_STAT_TX_COMPLETE|XL_STAT_INTLATCH) + +/* + * Window 0 registers + */ +#define XL_W0_EE_DATA 0x0C +#define XL_W0_EE_CMD 0x0A +#define XL_W0_RSRC_CFG 0x08 +#define XL_W0_ADDR_CFG 0x06 +#define XL_W0_CFG_CTRL 0x04 + +#define XL_W0_PROD_ID 0x02 +#define XL_W0_MFG_ID 0x00 + +/* + * Window 1 + */ + +#define XL_W1_TX_FIFO 0x10 + +#define XL_W1_FREE_TX 0x0C +#define XL_W1_TX_STATUS 0x0B +#define XL_W1_TX_TIMER 0x0A +#define XL_W1_RX_STATUS 0x08 +#define XL_W1_RX_FIFO 0x00 + +/* + * RX status codes + */ +#define XL_RXSTATUS_OVERRUN 0x01 +#define XL_RXSTATUS_RUNT 0x02 +#define XL_RXSTATUS_ALIGN 0x04 +#define XL_RXSTATUS_CRC 0x08 +#define XL_RXSTATUS_OVERSIZE 0x10 +#define XL_RXSTATUS_DRIBBLE 0x20 + +/* + * TX status codes + */ +#define XL_TXSTATUS_RECLAIM 0x02 /* 3c905B only */ +#define XL_TXSTATUS_OVERFLOW 0x04 +#define XL_TXSTATUS_MAXCOLS 0x08 +#define XL_TXSTATUS_UNDERRUN 0x10 +#define XL_TXSTATUS_JABBER 0x20 +#define XL_TXSTATUS_INTREQ 0x40 +#define XL_TXSTATUS_COMPLETE 0x80 + +/* + * Window 2 + */ +#define XL_W2_RESET_OPTIONS 0x0C /* 3c905B only */ +#define XL_W2_STATION_MASK_HI 0x0A +#define XL_W2_STATION_MASK_MID 0x08 +#define XL_W2_STATION_MASK_LO 0x06 +#define XL_W2_STATION_ADDR_HI 0x04 +#define XL_W2_STATION_ADDR_MID 0x02 +#define XL_W2_STATION_ADDR_LO 0x00 + +#define XL_RESETOPT_FEATUREMASK 0x0001|0x0002|0x004 +#define XL_RESETOPT_D3RESETDIS 0x0008 +#define XL_RESETOPT_DISADVFD 0x0010 +#define XL_RESETOPT_DISADV100 0x0020 +#define XL_RESETOPT_DISAUTONEG 0x0040 +#define XL_RESETOPT_DEBUGMODE 0x0080 +#define XL_RESETOPT_FASTAUTO 0x0100 +#define XL_RESETOPT_FASTEE 0x0200 +#define XL_RESETOPT_FORCEDCONF 0x0400 +#define XL_RESETOPT_TESTPDTPDR 0x0800 +#define XL_RESETOPT_TEST100TX 0x1000 +#define XL_RESETOPT_TEST100RX 0x2000 + +#define XL_RESETOPT_INVERT_LED 0x0010 +#define XL_RESETOPT_INVERT_MII 0x4000 + +/* + * Window 3 (fifo management) + */ +#define XL_W3_INTERNAL_CFG 0x00 +#define XL_W3_MAXPKTSIZE 0x04 /* 3c905B only */ +#define XL_W3_RESET_OPT 0x08 +#define XL_W3_FREE_TX 0x0C +#define XL_W3_FREE_RX 0x0A +#define XL_W3_MAC_CTRL 0x06 + +#define XL_ICFG_CONNECTOR_MASK 0x00F00000 +#define XL_ICFG_CONNECTOR_BITS 20 + +#define XL_ICFG_RAMSIZE_MASK 0x00000007 +#define XL_ICFG_RAMWIDTH 0x00000008 +#define XL_ICFG_ROMSIZE_MASK (0x00000040|0x00000080) +#define XL_ICFG_DISABLE_BASSD 0x00000100 +#define XL_ICFG_RAMLOC 0x00000200 +#define XL_ICFG_RAMPART (0x00010000|0x00020000) +#define XL_ICFG_XCVRSEL (0x00100000|0x00200000|0x00400000) +#define XL_ICFG_AUTOSEL 0x01000000 + +#define XL_XCVR_10BT 0x00 +#define XL_XCVR_AUI 0x01 +#define XL_XCVR_RSVD_0 0x02 +#define XL_XCVR_COAX 0x03 +#define XL_XCVR_100BTX 0x04 +#define XL_XCVR_100BFX 0x05 +#define XL_XCVR_MII 0x06 +#define XL_XCVR_RSVD_1 0x07 +#define XL_XCVR_AUTO 0x08 /* 3c905B only */ + +#define XL_MACCTRL_DEFER_EXT_END 0x0001 +#define XL_MACCTRL_DEFER_0 0x0002 +#define XL_MACCTRL_DEFER_1 0x0004 +#define XL_MACCTRL_DEFER_2 0x0008 +#define XL_MACCTRL_DEFER_3 0x0010 +#define XL_MACCTRL_DUPLEX 0x0020 +#define XL_MACCTRL_ALLOW_LARGE_PACK 0x0040 +#define XL_MACCTRL_EXTEND_AFTER_COL 0x0080 (3c905B only) +#define XL_MACCTRL_FLOW_CONTROL_ENB 0x0100 (3c905B only) +#define XL_MACCTRL_VLT_END 0x0200 (3c905B only) + +/* + * The 'reset options' register contains power-on reset values + * loaded from the EEPROM. This includes the supported media + * types on the card. It is also known as the media options register. + */ +#define XL_W3_MEDIA_OPT 0x08 + +#define XL_MEDIAOPT_BT4 0x0001 /* MII */ +#define XL_MEDIAOPT_BTX 0x0002 /* on-chip */ +#define XL_MEDIAOPT_BFX 0x0004 /* on-chip */ +#define XL_MEDIAOPT_BT 0x0008 /* on-chip */ +#define XL_MEDIAOPT_BNC 0x0010 /* on-chip */ +#define XL_MEDIAOPT_AUI 0x0020 /* on-chip */ +#define XL_MEDIAOPT_MII 0x0040 /* MII */ +#define XL_MEDIAOPT_VCO 0x0100 /* 1st gen chip only */ + +#define XL_MEDIAOPT_10FL 0x0100 /* 3x905B only, on-chip */ +#define XL_MEDIAOPT_MASK 0x01FF + +/* + * Window 4 (diagnostics) + */ +#define XL_W4_UPPERBYTESOK 0x0D +#define XL_W4_BADSSD 0x0C +#define XL_W4_MEDIA_STATUS 0x0A +#define XL_W4_PHY_MGMT 0x08 +#define XL_W4_NET_DIAG 0x06 +#define XL_W4_FIFO_DIAG 0x04 +#define XL_W4_VCO_DIAG 0x02 + +#define XL_W4_CTRLR_STAT 0x08 +#define XL_W4_TX_DIAG 0x00 + +#define XL_MII_CLK 0x01 +#define XL_MII_DATA 0x02 +#define XL_MII_DIR 0x04 + +#define XL_MEDIA_SQE 0x0008 +#define XL_MEDIA_10TP 0x00C0 +#define XL_MEDIA_LNK 0x0080 +#define XL_MEDIA_LNKBEAT 0x0800 + +#define XL_MEDIASTAT_CRCSTRIP 0x0004 +#define XL_MEDIASTAT_SQEENB 0x0008 +#define XL_MEDIASTAT_COLDET 0x0010 +#define XL_MEDIASTAT_CARRIER 0x0020 +#define XL_MEDIASTAT_JABGUARD 0x0040 +#define XL_MEDIASTAT_LINKBEAT 0x0080 +#define XL_MEDIASTAT_JABDETECT 0x0200 +#define XL_MEDIASTAT_POLREVERS 0x0400 +#define XL_MEDIASTAT_LINKDETECT 0x0800 +#define XL_MEDIASTAT_TXINPROG 0x1000 +#define XL_MEDIASTAT_DCENB 0x4000 +#define XL_MEDIASTAT_AUIDIS 0x8000 + +#define XL_NETDIAG_TEST_LOWVOLT 0x0001 +#define XL_NETDIAG_ASIC_REVMASK (0x0002|0x0004|0x0008|0x0010|0x0020) +#define XL_NETDIAG_UPPER_BYTES_ENABLE 0x0040 +#define XL_NETDIAG_STATS_ENABLED 0x0080 +#define XL_NETDIAG_TX_FATALERR 0x0100 +#define XL_NETDIAG_TRANSMITTING 0x0200 +#define XL_NETDIAG_RX_ENABLED 0x0400 +#define XL_NETDIAG_TX_ENABLED 0x0800 +#define XL_NETDIAG_FIFO_LOOPBACK 0x1000 +#define XL_NETDIAG_MAC_LOOPBACK 0x2000 +#define XL_NETDIAG_ENDEC_LOOPBACK 0x4000 +#define XL_NETDIAG_EXTERNAL_LOOP 0x8000 + +/* + * Window 5 + */ +#define XL_W5_STAT_ENB 0x0C +#define XL_W5_INTR_ENB 0x0A +#define XL_W5_RECLAIM_THRESH 0x09 /* 3c905B only */ +#define XL_W5_RX_FILTER 0x08 +#define XL_W5_RX_EARLYTHRESH 0x06 +#define XL_W5_TX_AVAILTHRESH 0x02 +#define XL_W5_TX_STARTTHRESH 0x00 + +/* + * RX filter bits + */ +#define XL_RXFILTER_INDIVIDUAL 0x01 +#define XL_RXFILTER_ALLMULTI 0x02 +#define XL_RXFILTER_BROADCAST 0x04 +#define XL_RXFILTER_ALLFRAMES 0x08 +#define XL_RXFILTER_MULTIHASH 0x10 /* 3c905B only */ + +/* + * Window 6 (stats) + */ +#define XL_W6_TX_BYTES_OK 0x0C +#define XL_W6_RX_BYTES_OK 0x0A +#define XL_W6_UPPER_FRAMES_OK 0x09 +#define XL_W6_DEFERRED 0x08 +#define XL_W6_RX_OK 0x07 +#define XL_W6_TX_OK 0x06 +#define XL_W6_RX_OVERRUN 0x05 +#define XL_W6_COL_LATE 0x04 +#define XL_W6_COL_SINGLE 0x03 +#define XL_W6_COL_MULTIPLE 0x02 +#define XL_W6_SQE_ERRORS 0x01 +#define XL_W6_CARRIER_LOST 0x00 + +/* + * Window 7 (bus master control) + */ +#define XL_W7_BM_ADDR 0x00 +#define XL_W7_BM_LEN 0x06 +#define XL_W7_BM_STATUS 0x0B +#define XL_W7_BM_TIMEr 0x0A + +/* + * bus master control registers + */ +#define XL_BM_PKTSTAT 0x20 +#define XL_BM_DOWNLISTPTR 0x24 +#define XL_BM_FRAGADDR 0x28 +#define XL_BM_FRAGLEN 0x2C +#define XL_BM_TXFREETHRESH 0x2F +#define XL_BM_UPPKTSTAT 0x30 +#define XL_BM_UPLISTPTR 0x38 + +#define XL_LAST_FRAG 0x80000000 + +/* + * Boomerang/Cyclone TX/RX list structure. + * For the TX lists, bits 0 to 12 of the status word indicate + * length. + * This looks suspiciously like the ThunderLAN, doesn't it. + */ +struct xl_frag { + u_int32_t xl_addr; /* 63 addr/len pairs */ + u_int32_t xl_len; +}; + +struct xl_list { + u_int32_t xl_next; /* final entry has 0 nextptr */ + u_int32_t xl_status; + struct xl_frag xl_frag[63]; +}; + +struct xl_list_onefrag { + u_int32_t xl_next; /* final entry has 0 nextptr */ + u_int32_t xl_status; + struct xl_frag xl_frag; +}; + +#define XL_MAXFRAGS 63 +#define XL_RX_LIST_CNT 128 +#define XL_TX_LIST_CNT 256 +#define XL_MIN_FRAMELEN 60 +#define ETHER_ALIGN 2 +#define XL_INC(x, y) (x) = (x + 1) % y + +struct xl_list_data { + struct xl_list_onefrag xl_rx_list[XL_RX_LIST_CNT]; + struct xl_list xl_tx_list[XL_TX_LIST_CNT]; + unsigned char xl_pad[XL_MIN_FRAMELEN]; +}; + +struct xl_chain { + struct xl_list *xl_ptr; + struct mbuf *xl_mbuf; + struct xl_chain *xl_next; + struct xl_chain *xl_prev; + u_int32_t xl_phys; +}; + +struct xl_chain_onefrag { + struct xl_list_onefrag *xl_ptr; + struct mbuf *xl_mbuf; + struct xl_chain_onefrag *xl_next; +}; + +struct xl_chain_data { + struct xl_chain_onefrag xl_rx_chain[XL_RX_LIST_CNT]; + struct xl_chain xl_tx_chain[XL_TX_LIST_CNT]; + + struct xl_chain_onefrag *xl_rx_head; + + /* 3c90x "boomerang" queuing stuff */ + struct xl_chain *xl_tx_head; + struct xl_chain *xl_tx_tail; + struct xl_chain *xl_tx_free; + + /* 3c90xB "cyclone/hurricane/tornado" stuff */ + int xl_tx_prod; + int xl_tx_cons; + int xl_tx_cnt; +}; + +#define XL_RXSTAT_LENMASK 0x00001FFF +#define XL_RXSTAT_UP_ERROR 0x00004000 +#define XL_RXSTAT_UP_CMPLT 0x00008000 +#define XL_RXSTAT_UP_OVERRUN 0x00010000 +#define XL_RXSTAT_RUNT 0x00020000 +#define XL_RXSTAT_ALIGN 0x00040000 +#define XL_RXSTAT_CRC 0x00080000 +#define XL_RXSTAT_OVERSIZE 0x00100000 +#define XL_RXSTAT_DRIBBLE 0x00800000 +#define XL_RXSTAT_UP_OFLOW 0x01000000 +#define XL_RXSTAT_IPCKERR 0x02000000 /* 3c905B only */ +#define XL_RXSTAT_TCPCKERR 0x04000000 /* 3c905B only */ +#define XL_RXSTAT_UDPCKERR 0x08000000 /* 3c905B only */ +#define XL_RXSTAT_BUFEN 0x10000000 /* 3c905B only */ +#define XL_RXSTAT_IPCKOK 0x20000000 /* 3c905B only */ +#define XL_RXSTAT_TCPCOK 0x40000000 /* 3c905B only */ +#define XL_RXSTAT_UDPCKOK 0x80000000 /* 3c905B only */ + +#define XL_TXSTAT_LENMASK 0x00001FFF +#define XL_TXSTAT_CRCDIS 0x00002000 +#define XL_TXSTAT_TX_INTR 0x00008000 +#define XL_TXSTAT_DL_COMPLETE 0x00010000 +#define XL_TXSTAT_IPCKSUM 0x02000000 /* 3c905B only */ +#define XL_TXSTAT_TCPCKSUM 0x04000000 /* 3c905B only */ +#define XL_TXSTAT_UDPCKSUM 0x08000000 /* 3c905B only */ +#define XL_TXSTAT_RND_DEFEAT 0x10000000 /* 3c905B only */ +#define XL_TXSTAT_EMPTY 0x20000000 /* 3c905B only */ +#define XL_TXSTAT_DL_INTR 0x80000000 + +#define XL_CAPABILITY_BM 0x20 + +struct xl_type { + u_int16_t xl_vid; + u_int16_t xl_did; + char *xl_name; +}; + +struct xl_mii_frame { + u_int8_t mii_stdelim; + u_int8_t mii_opcode; + u_int8_t mii_phyaddr; + u_int8_t mii_regaddr; + u_int8_t mii_turnaround; + u_int16_t mii_data; +}; + +/* + * MII constants + */ +#define XL_MII_STARTDELIM 0x01 +#define XL_MII_READOP 0x02 +#define XL_MII_WRITEOP 0x01 +#define XL_MII_TURNAROUND 0x02 + +/* + * The 3C905B adapters implement a few features that we want to + * take advantage of, namely the multicast hash filter. With older + * chips, you only have the option of turning on reception of all + * multicast frames, which is kind of lame. + * + * We also use this to decide on a transmit strategy. For the 3c90xB + * cards, we can use polled descriptor mode, which reduces CPU overhead. + */ +#define XL_TYPE_905B 1 +#define XL_TYPE_90X 2 + +#define XL_FLAG_FUNCREG 0x0001 +#define XL_FLAG_PHYOK 0x0002 +#define XL_FLAG_EEPROM_OFFSET_30 0x0004 +#define XL_FLAG_WEIRDRESET 0x0008 +#define XL_FLAG_8BITROM 0x0010 +#define XL_FLAG_INVERT_LED_PWR 0x0020 +#define XL_FLAG_INVERT_MII_PWR 0x0040 + +struct xl_softc { + struct arpcom arpcom; /* interface info */ + struct ifmedia ifmedia; /* media info */ + bus_space_handle_t xl_bhandle; + bus_space_tag_t xl_btag; + void *xl_intrhand; + struct resource *xl_irq; + struct resource *xl_res; + device_t xl_miibus; + struct xl_type *xl_info; /* 3Com adapter info */ + u_int8_t xl_unit; /* interface number */ + u_int8_t xl_type; + u_int32_t xl_xcvr; + u_int16_t xl_media; + u_int16_t xl_caps; + u_int8_t xl_stats_no_timeout; + u_int16_t xl_tx_thresh; + int xl_if_flags; + struct xl_list_data *xl_ldata; + struct xl_chain_data xl_cdata; + struct callout_handle xl_stat_ch; + int xl_flags; + struct resource *xl_fres; + bus_space_handle_t xl_fhandle; + bus_space_tag_t xl_ftag; + struct mtx xl_mtx; +}; + +#define XL_LOCK(_sc) mtx_lock(&(_sc)->xl_mtx) +#define XL_UNLOCK(_sc) mtx_unlock(&(_sc)->xl_mtx) + +#define xl_rx_goodframes(x) \ + ((x.xl_upper_frames_ok & 0x03) << 8) | x.xl_rx_frames_ok + +#define xl_tx_goodframes(x) \ + ((x.xl_upper_frames_ok & 0x30) << 4) | x.xl_tx_frames_ok + +struct xl_stats { + u_int8_t xl_carrier_lost; + u_int8_t xl_sqe_errs; + u_int8_t xl_tx_multi_collision; + u_int8_t xl_tx_single_collision; + u_int8_t xl_tx_late_collision; + u_int8_t xl_rx_overrun; + u_int8_t xl_tx_frames_ok; + u_int8_t xl_rx_frames_ok; + u_int8_t xl_tx_deferred; + u_int8_t xl_upper_frames_ok; + u_int16_t xl_rx_bytes_ok; + u_int16_t xl_tx_bytes_ok; + u_int16_t status; +}; + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->xl_btag, sc->xl_bhandle, reg, val) +#define CSR_WRITE_2(sc, reg, val) \ + bus_space_write_2(sc->xl_btag, sc->xl_bhandle, reg, val) +#define CSR_WRITE_1(sc, reg, val) \ + bus_space_write_1(sc->xl_btag, sc->xl_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->xl_btag, sc->xl_bhandle, reg) +#define CSR_READ_2(sc, reg) \ + bus_space_read_2(sc->xl_btag, sc->xl_bhandle, reg) +#define CSR_READ_1(sc, reg) \ + bus_space_read_1(sc->xl_btag, sc->xl_bhandle, reg) + +#define XL_SEL_WIN(x) \ + CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_WINSEL | x) +#define XL_TIMEOUT 1000 + +/* + * General constants that are fun to know. + * + * 3Com PCI vendor ID + */ +#define TC_VENDORID 0x10B7 + +/* + * 3Com chip device IDs. + */ +#define TC_DEVICEID_BOOMERANG_10BT 0x9000 +#define TC_DEVICEID_BOOMERANG_10BT_COMBO 0x9001 +#define TC_DEVICEID_BOOMERANG_10_100BT 0x9050 +#define TC_DEVICEID_BOOMERANG_100BT4 0x9051 +#define TC_DEVICEID_KRAKATOA_10BT 0x9004 +#define TC_DEVICEID_KRAKATOA_10BT_COMBO 0x9005 +#define TC_DEVICEID_KRAKATOA_10BT_TPC 0x9006 +#define TC_DEVICEID_CYCLONE_10FL 0x900A +#define TC_DEVICEID_HURRICANE_10_100BT 0x9055 +#define TC_DEVICEID_CYCLONE_10_100BT4 0x9056 +#define TC_DEVICEID_CYCLONE_10_100_COMBO 0x9058 +#define TC_DEVICEID_CYCLONE_10_100FX 0x905A +#define TC_DEVICEID_TORNADO_10_100BT 0x9200 +#define TC_DEVICEID_HURRICANE_10_100BT_SERV 0x9800 +#define TC_DEVICEID_TORNADO_10_100BT_SERV 0x9805 +#define TC_DEVICEID_HURRICANE_SOHO100TX 0x7646 +#define TC_DEVICEID_TORNADO_HOMECONNECT 0x4500 +#define TC_DEVICEID_HURRICANE_556 0x6055 +#define TC_DEVICEID_HURRICANE_556B 0x6056 +#define TC_DEVICEID_HURRICANE_575A 0x5057 +#define TC_DEVICEID_HURRICANE_575B 0x5157 +#define TC_DEVICEID_HURRICANE_575C 0x5257 +#define TC_DEVICEID_HURRICANE_656 0x6560 +#define TC_DEVICEID_HURRICANE_656B 0x6562 +#define TC_DEVICEID_TORNADO_656C 0x6564 + +/* + * PCI low memory base and low I/O base register, and + * other PCI registers. Note: some are only available on + * the 3c905B, in particular those that related to power management. + */ + +#define XL_PCI_VENDOR_ID 0x00 +#define XL_PCI_DEVICE_ID 0x02 +#define XL_PCI_COMMAND 0x04 +#define XL_PCI_STATUS 0x06 +#define XL_PCI_CLASSCODE 0x09 +#define XL_PCI_LATENCY_TIMER 0x0D +#define XL_PCI_HEADER_TYPE 0x0E +#define XL_PCI_LOIO 0x10 +#define XL_PCI_LOMEM 0x14 +#define XL_PCI_FUNCMEM 0x18 +#define XL_PCI_BIOSROM 0x30 +#define XL_PCI_INTLINE 0x3C +#define XL_PCI_INTPIN 0x3D +#define XL_PCI_MINGNT 0x3E +#define XL_PCI_MINLAT 0x0F +#define XL_PCI_RESETOPT 0x48 +#define XL_PCI_EEPROM_DATA 0x4C + +/* 3c905B-only registers */ +#define XL_PCI_CAPID 0xDC /* 8 bits */ +#define XL_PCI_NEXTPTR 0xDD /* 8 bits */ +#define XL_PCI_PWRMGMTCAP 0xDE /* 16 bits */ +#define XL_PCI_PWRMGMTCTRL 0xE0 /* 16 bits */ + +#define XL_PSTATE_MASK 0x0003 +#define XL_PSTATE_D0 0x0000 +#define XL_PSTATE_D1 0x0002 +#define XL_PSTATE_D2 0x0002 +#define XL_PSTATE_D3 0x0003 +#define XL_PME_EN 0x0010 +#define XL_PME_STATUS 0x8000 + +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) + +#endif + +#ifndef IFM_10_FL +#define IFM_10_FL 13 /* 10baseFL - Fiber */ +#endif diff --git a/sys/pci/intpm.c b/sys/pci/intpm.c new file mode 100644 index 0000000..747f192 --- /dev/null +++ b/sys/pci/intpm.c @@ -0,0 +1,752 @@ +/*- + * Copyright (c) 1998, 1999 Takanori Watanabe + * 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$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> + +#include <sys/uio.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> +#include <dev/smbus/smbconf.h> + +#include "smbus_if.h" + +/*This should be removed if force_pci_map_int supported*/ +#include <sys/interrupt.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> +#include <pci/intpmreg.h> + +#include "opt_intpm.h" + +static struct _pcsid +{ + u_int32_t type; + char *desc; +} pci_ids[] = +{ + { 0x71138086,"Intel 82371AB Power management controller"}, + { 0x719b8086,"Intel 82443MX Power management controller"}, +#if 0 + /* Not a good idea yet, this stops isab0 functioning */ + { 0x02001166,"ServerWorks OSB4 PCI to ISA Bridge"}, +#endif + + { 0x00000000, NULL } +}; +static int intsmb_probe(device_t); +static int intsmb_attach(device_t); + +static int intsmb_intr(device_t dev); +static int intsmb_slvintr(device_t dev); +static void intsmb_alrintr(device_t dev); +static int intsmb_callback(device_t dev, int index, caddr_t data); +static int intsmb_quick(device_t dev, u_char slave, int how); +static int intsmb_sendb(device_t dev, u_char slave, char byte); +static int intsmb_recvb(device_t dev, u_char slave, char *byte); +static int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte); +static int intsmb_writew(device_t dev, u_char slave, char cmd, short word); +static int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte); +static int intsmb_readw(device_t dev, u_char slave, char cmd, short *word); +static int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata); +static int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); +static int intsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf); +static void intsmb_start(device_t dev,u_char cmd,int nointr); +static int intsmb_stop(device_t dev); +static int intsmb_stop_poll(device_t dev); +static int intsmb_free(device_t dev); +static int intpm_probe (device_t dev); +static int intpm_attach (device_t dev); +static devclass_t intsmb_devclass; + +static device_method_t intpm_methods[]={ + DEVMETHOD(device_probe,intsmb_probe), + DEVMETHOD(device_attach,intsmb_attach), + + DEVMETHOD(bus_print_child, bus_generic_print_child), + + DEVMETHOD(smbus_callback,intsmb_callback), + DEVMETHOD(smbus_quick,intsmb_quick), + DEVMETHOD(smbus_sendb,intsmb_sendb), + DEVMETHOD(smbus_recvb,intsmb_recvb), + DEVMETHOD(smbus_writeb,intsmb_writeb), + DEVMETHOD(smbus_writew,intsmb_writew), + DEVMETHOD(smbus_readb,intsmb_readb), + DEVMETHOD(smbus_readw,intsmb_readw), + DEVMETHOD(smbus_pcall,intsmb_pcall), + DEVMETHOD(smbus_bwrite,intsmb_bwrite), + DEVMETHOD(smbus_bread,intsmb_bread), + {0,0} +}; + +struct intpm_pci_softc{ + bus_space_tag_t smbst; + bus_space_handle_t smbsh; + bus_space_tag_t pmst; + bus_space_handle_t pmsh; + device_t smbus; +}; + + +struct intsmb_softc{ + struct intpm_pci_softc *pci_sc; + bus_space_tag_t st; + bus_space_handle_t sh; + device_t smbus; + int isbusy; +}; + +static driver_t intpm_driver = { + "intsmb", + intpm_methods, + sizeof(struct intsmb_softc), +}; + +static devclass_t intpm_devclass; +static device_method_t intpm_pci_methods[] = { + DEVMETHOD(device_probe,intpm_probe), + DEVMETHOD(device_attach,intpm_attach), + {0,0} +}; +static driver_t intpm_pci_driver = { + "intpm", + intpm_pci_methods, + sizeof(struct intpm_pci_softc) +}; + +static int +intsmb_probe(device_t dev) +{ + struct intsmb_softc *sc =(struct intsmb_softc *) device_get_softc(dev); + sc->smbus=device_add_child(dev, "smbus", -1); + if (!sc->smbus) + return (EINVAL); /* XXX don't know what to return else */ + device_set_desc(dev,"Intel PIIX4 SMBUS Interface"); + + return (0); /* XXX don't know what to return else */ +} +static int +intsmb_attach(device_t dev) +{ + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + sc->pci_sc=device_get_softc(device_get_parent(dev)); + sc->isbusy=0; + sc->sh=sc->pci_sc->smbsh; + sc->st=sc->pci_sc->smbst; + sc->pci_sc->smbus=dev; + device_probe_and_attach(sc->smbus); +#ifdef ENABLE_ALART + /*Enable Arart*/ + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT, + PIIX4_SMBSLVCNT_ALTEN); +#endif + return (0); +} + +static int +intsmb_callback(device_t dev, int index, caddr_t data) +{ + int error = 0; + intrmask_t s; + s=splnet(); + switch (index) { + case SMB_REQUEST_BUS: + break; + case SMB_RELEASE_BUS: + break; + default: + error = EINVAL; + } + splx(s); + return (error); +} +/*counterpart of smbtx_smb_free*/ +static int +intsmb_free(device_t dev){ + intrmask_t s; + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS)& + PIIX4_SMBHSTSTAT_BUSY) +#ifdef ENABLE_ALART + ||(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS)& + PIIX4_SMBSLVSTS_BUSY) +#endif + || sc->isbusy) + return EBUSY; + s=splhigh(); + sc->isbusy=1; + /*Disable Intrrupt in slave part*/ +#ifndef ENABLE_ALART + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,0); +#endif + /*Reset INTR Flag to prepare INTR*/ + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTSTS, + (PIIX4_SMBHSTSTAT_INTR| + PIIX4_SMBHSTSTAT_ERR| + PIIX4_SMBHSTSTAT_BUSC| + PIIX4_SMBHSTSTAT_FAIL) + ); + splx(s); + return 0; +} + +static int +intsmb_intr(device_t dev) +{ + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + int status; + status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS); + if(status&PIIX4_SMBHSTSTAT_BUSY){ + return 1; + + } + if(status&(PIIX4_SMBHSTSTAT_INTR| + PIIX4_SMBHSTSTAT_ERR| + PIIX4_SMBHSTSTAT_BUSC| + PIIX4_SMBHSTSTAT_FAIL)){ + int tmp; + tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT, + tmp&~PIIX4_SMBHSTCNT_INTREN); + if(sc->isbusy){ + sc->isbusy=0; + wakeup(sc); + } + return 0; + } + return 1;/* Not Completed*/ +} +static int +intsmb_slvintr(device_t dev) +{ + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + int status,retval; + retval=1; + status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS); + if(status&PIIX4_SMBSLVSTS_BUSY) + return retval; + if(status&PIIX4_SMBSLVSTS_ALART){ + intsmb_alrintr(dev); + retval=0; + }else if(status&~(PIIX4_SMBSLVSTS_ALART|PIIX4_SMBSLVSTS_SDW2 + |PIIX4_SMBSLVSTS_SDW1)){ + retval=0; + } + /*Reset Status Register*/ + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVSTS,PIIX4_SMBSLVSTS_ALART| + PIIX4_SMBSLVSTS_SDW2|PIIX4_SMBSLVSTS_SDW1| + PIIX4_SMBSLVSTS_SLV); + return retval; +} + +static void intsmb_alrintr(device_t dev) +{ + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + int slvcnt; +#ifdef ENABLE_ALART + int error; +#endif + + /*stop generating INTR from ALART*/ + slvcnt=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVCNT); +#ifdef ENABLE_ALART + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT, + slvcnt&~PIIX4_SMBSLVCNT_ALTEN) ; +#endif + DELAY(5); + /*ask bus who assert it and then ask it what's the matter. */ +#ifdef ENABLE_ALART + error=intsmb_free(dev); + if(!error){ + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,SMBALTRESP + |LSB); + intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,1); + if(!(error=intsmb_stop_poll(dev))){ + u_int8_t addr; + addr=bus_space_read_1(sc->st,sc->sh, + PIIX4_SMBHSTDAT0); + printf("ALART_RESPONSE: 0x%x\n", addr); + } + }else{ + printf("ERROR\n"); + } + + /*Re-enable INTR from ALART*/ + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT, + slvcnt|PIIX4_SMBSLVCNT_ALTEN) ; + DELAY(5); +#endif + + return; +} +static void +intsmb_start(device_t dev,unsigned char cmd,int nointr) +{ + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + unsigned char tmp; + tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); + tmp&= 0xe0; + tmp |= cmd; + tmp |=PIIX4_SMBHSTCNT_START; + /*While not in autoconfiguration Intrrupt Enabled*/ + if(!cold||!nointr) + tmp |=PIIX4_SMBHSTCNT_INTREN; + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,tmp); +} + +/*Polling Code. Polling is not encouraged + * because It is required to wait for the device get busy. + *(29063505.pdf from Intel) + * But during boot,intrrupt cannot be used. + * so use polling code while in autoconfiguration. + */ + +static int +intsmb_stop_poll(device_t dev){ + int error,i; + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + + /* + * In smbtx driver ,Simply waiting. + * This loops 100-200 times. + */ + for(i=0;i<0x7fff;i++){ + if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS) + &PIIX4_SMBHSTSTAT_BUSY)){ + break; + } + } + for(i=0;i<0x7fff;i++){ + int status; + status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS); + if(!(status&PIIX4_SMBHSTSTAT_BUSY)){ + sc->isbusy=0; + error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO : + (status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY: + (status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0; + if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){ + printf("unknown cause why?"); + } + return error; + } + } + { + int tmp; + sc->isbusy=0; + tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT, + tmp&~PIIX4_SMBHSTCNT_INTREN); + } + return EIO; +} +/* + *wait for completion and return result. + */ +static int +intsmb_stop(device_t dev){ + int error; + intrmask_t s; + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + if(cold){ + /*So that it can use device during probing device on SMBus.*/ + error=intsmb_stop_poll(dev); + return error; + }else{ + if(!tsleep(sc,(PWAIT)|PCATCH,"SMBWAI",hz/8)){ + int status; + status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS); + if(!(status&PIIX4_SMBHSTSTAT_BUSY)){ + error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO : + (status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY: + (status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0; + if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){ + printf("intsmb%d:unknown cause why?\n", + device_get_unit(dev)); + } +#ifdef ENABLE_ALART + bus_space_write_1(sc->st,sc->sh, + PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN); +#endif + return error; + } + } + } + /*Timeout Procedure*/ + s=splhigh(); + sc->isbusy=0; + /*Re-enable supressed intrrupt from slave part*/ + bus_space_write_1(sc->st,sc->sh, + PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN); + splx(s); + return EIO; +} + +static int +intsmb_quick(device_t dev, u_char slave, int how) +{ + int error=0; + u_char data; + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + data=slave; + /*Quick command is part of Address, I think*/ + switch(how){ + case SMB_QWRITE: + data&=~LSB; + break; + case SMB_QREAD: + data|=LSB; + break; + default: + error=EINVAL; + } + if(!error){ + error=intsmb_free(dev); + if(!error){ + bus_space_write_1(sc->st,sc->sh, + PIIX4_SMBHSTADD,data); + intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_QUICK,0); + error=intsmb_stop(dev); + } + } + + return (error); +} + +static int +intsmb_sendb(device_t dev, u_char slave, char byte) +{ + int error; + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + error=intsmb_free(dev); + if(!error){ + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,byte); + intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0); + error=intsmb_stop(dev); + } + return (error); +} +static int +intsmb_recvb(device_t dev, u_char slave, char *byte) +{ + int error; + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + error=intsmb_free(dev); + if(!error){ + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave + |LSB); + intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0); + if(!(error=intsmb_stop(dev))){ +#ifdef RECV_IS_IN_CMD + /*Linux SMBus stuff also troubles + Because Intel's datasheet will not make clear. + */ + *byte=bus_space_read_1(sc->st,sc->sh, + PIIX4_SMBHSTCMD); +#else + *byte=bus_space_read_1(sc->st,sc->sh, + PIIX4_SMBHSTDAT0); +#endif + } + } + return (error); +} +static int +intsmb_writeb(device_t dev, u_char slave, char cmd, char byte) +{ + int error; + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + error=intsmb_free(dev); + if(!error){ + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,byte); + intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0); + error=intsmb_stop(dev); + } + return (error); +} +static int +intsmb_writew(device_t dev, u_char slave, char cmd, short word) +{ + int error; + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + error=intsmb_free(dev); + if(!error){ + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0, + word&0xff); + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1, + (word>>8)&0xff); + intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0); + error=intsmb_stop(dev); + } + return (error); +} + +static int +intsmb_readb(device_t dev, u_char slave, char cmd, char *byte) +{ + int error; + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + error=intsmb_free(dev); + if(!error){ + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB); + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); + intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0); + if(!(error=intsmb_stop(dev))){ + *byte=bus_space_read_1(sc->st,sc->sh, + PIIX4_SMBHSTDAT0); + } + } + return (error); +} +static int +intsmb_readw(device_t dev, u_char slave, char cmd, short *word) +{ + int error; + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + error=intsmb_free(dev); + if(!error){ + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB); + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); + intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0); + if(!(error=intsmb_stop(dev))){ + *word=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff; + *word|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8; + } + } + return (error); +} +/* + * Data sheet claims that it implements all function, but also claims + * that it implements 7 function and not mention PCALL. So I don't know + * whether it will work. + */ +static int +intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) +{ +#ifdef PROCCALL_TEST + int error; + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + error=intsmb_free(dev); + if(!error){ + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,sdata&0xff); + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1,(sdata&0xff)>>8); + intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0); + } + if(!(error=intsmb_stop(dev))){ + *rdata=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff; + *rdata|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8; + } + return error; +#else + return 0; +#endif +} +static int +intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) +{ + int error,i; + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + error=intsmb_free(dev); + if(count>SMBBLOCKTRANS_MAX||count==0) + error=EINVAL; + if(!error){ + /*Reset internal array index*/ + bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); + + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); + for(i=0;i<count;i++){ + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBBLKDAT,buf[i]); + } + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count); + intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0); + error=intsmb_stop(dev); + } + return (error); +} + +static int +intsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf) +{ + int error,i; + struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); + error=intsmb_free(dev); + if(count>SMBBLOCKTRANS_MAX||count==0) + error=EINVAL; + if(!error){ + /*Reset internal array index*/ + bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); + + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB); + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); + bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count); + intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0); + error=intsmb_stop(dev); + if(!error){ + bzero(buf,count);/*Is it needed?*/ + count= bus_space_read_1(sc->st,sc->sh, + PIIX4_SMBHSTDAT0); + if(count!=0&&count<=SMBBLOCKTRANS_MAX){ + for(i=0;i<count;i++){ + buf[i]=bus_space_read_1(sc->st, + sc->sh, + PIIX4_SMBBLKDAT); + } + } + else{ + error=EIO; + } + } + } + return (error); +} + +DRIVER_MODULE(intsmb, intpm , intpm_driver, intsmb_devclass, 0, 0); + + +static void intpm_intr(void *arg); +static int +intpm_attach(device_t dev) +{ + int value; + int unit=device_get_unit(dev); + void *ih; + int error; + char * str; + { + struct intpm_pci_softc *sciic; + device_t smbinterface; + int rid; + struct resource *res; + + sciic=device_get_softc(dev); + if(sciic==NULL){ + return ENOMEM; + } + + rid=PCI_BASE_ADDR_SMB; + res=bus_alloc_resource(dev,SYS_RES_IOPORT,&rid, + 0,~0,1,RF_ACTIVE); + if(res==NULL){ + device_printf(dev,"Could not allocate Bus space\n"); + return ENXIO; + } + sciic->smbst=rman_get_bustag(res); + sciic->smbsh=rman_get_bushandle(res); + + device_printf(dev,"%s %x\n", + (sciic->smbst==I386_BUS_SPACE_IO)? + "I/O mapped":"Memory", + sciic->smbsh); + + +#ifndef NO_CHANGE_PCICONF + pci_write_config(dev,PCIR_INTLINE,0x9,1); + pci_write_config(dev,PCI_HST_CFG_SMB, + PCI_INTR_SMB_IRQ9|PCI_INTR_SMB_ENABLE,1); +#endif + value=pci_read_config(dev,PCI_HST_CFG_SMB,1); + switch(value&0xe){ + case PCI_INTR_SMB_SMI: + str="SMI"; + break; + case PCI_INTR_SMB_IRQ9: + str="IRQ 9"; + break; + default: + str="BOGUS"; + } + device_printf(dev,"intr %s %s ",str,((value&1)? "enabled":"disabled")); + value=pci_read_config(dev,PCI_REVID_SMB,1); + printf("revision %d\n",value); + /* + * Install intr HANDLER here + */ + rid=0; + res=bus_alloc_resource(dev,SYS_RES_IRQ,&rid,9,9,1,RF_SHAREABLE|RF_ACTIVE); + if(res==NULL){ + device_printf(dev,"could not allocate irq"); + return ENOMEM; + } + error=bus_setup_intr(dev,res,INTR_TYPE_MISC, (driver_intr_t *) intpm_intr,sciic,&ih); + if(error){ + device_printf(dev,"Failed to map intr\n"); + return error; + } + smbinterface=device_add_child(dev,"intsmb",unit); + if(!smbinterface){ + printf("intsmb%d:could not add SMBus device\n",unit); + } + device_probe_and_attach(smbinterface); + } + + value=pci_read_config(dev,PCI_BASE_ADDR_PM,4); + printf("intpm%d: PM %s %x \n",unit,(value&1)?"I/O mapped":"Memory",value&0xfffe); + return 0; +} +static int +intpm_probe(device_t dev) +{ + struct _pcsid *ep =pci_ids; + u_int32_t device_id=pci_get_devid(dev); + + while (ep->type && ep->type != device_id) + ++ep; + if(ep->desc!=NULL){ + device_set_desc(dev,ep->desc); + bus_set_resource(dev,SYS_RES_IRQ,0,9,1); /* XXX setup intr resource */ + return 0; + }else{ + return ENXIO; + } +} +DRIVER_MODULE(intpm, pci , intpm_pci_driver, intpm_devclass, 0, 0); +MODULE_DEPEND(intpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); +MODULE_VERSION(intpm, 1); + +static void intpm_intr(void *arg) +{ + struct intpm_pci_softc *sc; + sc=(struct intpm_pci_softc *)arg; + intsmb_intr(sc->smbus); + intsmb_slvintr(sc->smbus); + +} diff --git a/sys/pci/intpmreg.h b/sys/pci/intpmreg.h new file mode 100644 index 0000000..73816e7 --- /dev/null +++ b/sys/pci/intpmreg.h @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 1998, 1999 Takanori Watanabe + * 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$ + */ + +/*Register Difinition for Intel Chipset with ACPI Support*/ +#define PCI_BASE_ADDR_SMB 0x90 /*Where to MAP IO*/ +#define PCI_BASE_ADDR_PM 0x40 +#define PCI_HST_CFG_SMB 0xd2 /*Host Configuration*/ +#define PCI_INTR_SMB_SMI 0 +#define PCI_INTR_SMB_IRQ9 8 +#define PCI_INTR_SMB_ENABLE 1 +#define PCI_SLV_CMD_SMB 0xd3 /*SLAVE COMMAND*/ +#define PCI_SLV_SDW_SMB_1 0xd4 /*SLAVE SHADOW PORT 1*/ +#define PCI_SLV_SDW_SMB_2 0xd5 /*SLAVE SHADOW PORT 2*/ +#define PCI_REVID_SMB 0xd6 +#define LSB 0x1 +#define PIIX4_SMBHSTSTS 0x00 +#define PIIX4_SMBHSTSTAT_BUSY (1<<0) +#define PIIX4_SMBHSTSTAT_INTR (1<<1) +#define PIIX4_SMBHSTSTAT_ERR (1<<2) +#define PIIX4_SMBHSTSTAT_BUSC (1<<3) +#define PIIX4_SMBHSTSTAT_FAIL (1<<4) +#define PIIX4_SMBSLVSTS 0x01 +#define PIIX4_SMBSLVSTS_ALART (1<<5) +#define PIIX4_SMBSLVSTS_SDW2 (1<<4) +#define PIIX4_SMBSLVSTS_SDW1 (1<<3) +#define PIIX4_SMBSLVSTS_SLV (1<<2) +#define PIIX4_SMBSLVSTS_BUSY (1<<0) +#define PIIX4_SMBHSTCNT 0x02 +#define PIIX4_SMBHSTCNT_START (1<<6) +#define PIIX4_SMBHSTCNT_PROT_QUICK 0 +#define PIIX4_SMBHSTCNT_PROT_BYTE (1<<2) +#define PIIX4_SMBHSTCNT_PROT_BDATA (2<<2) +#define PIIX4_SMBHSTCNT_PROT_WDATA (3<<2) +#define PIIX4_SMBHSTCNT_PROT_BLOCK (5<<2) +#define SMBBLOCKTRANS_MAX 32 +#define PIIX4_SMBHSTCNT_KILL (1<<1) +#define PIIX4_SMBHSTCNT_INTREN (1) +#define PIIX4_SMBHSTCMD 0x03 +#define PIIX4_SMBHSTADD 0x04 +#define PIIX4_SMBHSTDAT0 0x05 +#define PIIX4_SMBHSTDAT1 0x06 +#define PIIX4_SMBBLKDAT 0x07 +#define PIIX4_SMBSLVCNT 0x08 +#define PIIX4_SMBSLVCNT_ALTEN (1<<3) +#define PIIX4_SMBSLVCNT_SD2EN (1<<2) +#define PIIX4_SMBSLVCNT_SD1EN (1<<1) +#define PIIX4_SMBSLVCNT_SLVEN (1) +#define PIIX4_SMBSLVCMD 0x09 +#define PIIX4_SMBSLVEVT 0x0a +#define PIIX4_SMBSLVDAT 0x0c +/*This is SMBus alart response address*/ +#define SMBALTRESP 0x18 diff --git a/sys/pci/locate.pl b/sys/pci/locate.pl new file mode 100755 index 0000000..89688e1 --- /dev/null +++ b/sys/pci/locate.pl @@ -0,0 +1,45 @@ +#!/usr/bin/perl -w +# $FreeBSD$ + +use strict; + +if (!defined($ARGV[0])) { + print( +" +Perl script to convert NCR script address into label+offset. +Useful to find the failed NCR instruction ... + +usage: $0 <address> +"); + exit(1); +} + +my $errpos = hex($ARGV[0])/4; +my $ofs=0; + +open (INPUT, "cc -E ncr.c 2>/dev/null |"); + +while ($_ = <INPUT>) +{ + last if /^struct script \{/; +} + +while ($_ = <INPUT>) +{ + last if /^\}\;/; + my ($label, $size) = /ncrcmd\s+(\S+)\s+\[([^]]+)/; + $size = eval($size); + if (defined($label) && $label) { + if ($errpos) { + if ($ofs + $size > $errpos) { + printf ("%4x: %s\n", $ofs * 4, $label); + printf ("%4x: %s + %d\n", $errpos * 4, $label, $errpos - $ofs); + last; + } + $ofs += $size; + } else { + printf ("%4x: %s\n", $ofs * 4, $label); + } + } +} + diff --git a/sys/pci/meteor.c b/sys/pci/meteor.c new file mode 100644 index 0000000..340c709 --- /dev/null +++ b/sys/pci/meteor.c @@ -0,0 +1,2131 @@ +/* + * Copyright (c) 1995 Mark Tinguely and Jim Lowe + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Mark Tinguely and Jim Lowe + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* Change History: + 8/21/95 Release + 8/23/95 On advice from Stefan Esser, added volatile to PCI + memory pointers to remove PCI caching . + 8/29/95 Fixes suggested by Bruce Evans. + meteor_mmap should return -1 on error rather than 0. + unit # > NMETEOR should be unit # >= NMETEOR. + 10/24/95 Turn 50 Hz processing for SECAM and 60 Hz processing + off for AUTOMODE. + 11/11/95 Change UV from always begin signed to ioctl selected + to either signed or unsigned. + 12/07/95 Changed 7196 startup codes for 50 Hz as recommended + by Luigi Rizzo (luigi@iet.unipi.it) + 12/08/95 Clear SECAM bit in PAL/NTSC and set input field count + bits for 50 Hz mode (PAL/SECAM) before I was setting the + output count bits. by Luigi Rizzo (luigi@iet.unipi.it) + 12/18/95 Correct odd DMA field (never exceed, but good for safety + Changed 7196 startup codes for 50 Hz as recommended + by Luigi Rizzo (luigi@iet.unipi.it) + 12/19/95 Changed field toggle mode to enable (offset 0x3c) + recommended by luigi@iet.unipi.it + Added in prototyping, include file, staticizing, + and DEVFS changes from FreeBSD team. + Changed the default allocated pages from 151 (NTSC) + to 217 (PAL). + Cleaned up some old comments in iic_write(). + Added a Field (even or odd) only capture mode to + eliminate the high frequency problems with compression + algorithms. Recommended by luigi@iet.unipi.it. + Changed geometry ioctl so if it couldn't allocated a + large enough contiguous space, it wouldn't free the + stuff it already had. + Added new mode called YUV_422 which delivers the + data in planer Y followed by U followed by V. This + differs from the standard YUV_PACKED mode in that + the chrominance (UV) data is in the correct (different) + order. This is for programs like vic and mpeg_encode + so they don't have to reorder the chrominance data. + Added field count to stats. + Increment frame count stat if capturing continuous on + even frame grabs. + Added my email address to these comments + (james@cs.uwm.edu) suggested by (luigi@iet.unipt.it :-). + Changed the user mode signal mechanism to allow the + user program to be interrupted at the end of a frame + in any one of the modes. Added SSIGNAL ioctl. + Added a SFPS/GFPS ioctl so one may set the frames per + second that the card catpures. This code needs to be + completed. + Changed the interrupt routine so synchronous capture + will work on fields or frames and the starting frame + can be either even or odd. + Added HALT_N_FRAMES and CONT_N_FRAMES so one could + stop and continue synchronous capture mode. + Change the tsleep/wakeup function to wait on mtr + rather than &read_intr_wait. + 1/22/96 Add option (METEOR_FreeBSD_210) for FreeBSD 2.1 + to compile. + Changed intr so it only printed errors every 50 times. + Added unit number to error messages. + Added get_meteor_mem and enabled range checking. + 1/30/96 Added prelim test stuff for direct video dma transfers + from Amancio Hasty (hasty@rah.star-gate.com). Until + we get some stuff sorted out, this will be ifdef'ed + with METEOR_DIRECT_VIDEO. This is very dangerous to + use at present since we don't check the address that + is passed by the user!!!!! + 2/26/96 Added special SVIDEO input device type. + 2/27/96 Added meteor_reg.h file and associate types Converted + meteor.c over to using meteor.h file. Prompted by + Lars Jonas Olsson <ljo@po.cwru.edu>. + 2/28/96 Added meteor RGB code from Lars Jonas Olsson + <ljo@po.cwru.edu>. I make some mods to this code, so + I hope it still works as I don't have an rgb card to + test with. + 2/29/96 <ljo@po.cwru.edu> tested the meteor RGB and supplied + me with diffs. Thanks, we now have a working RGB + version of the driver. Still need to clean up this + code. + 3/1/96 Fixed a nasty little bug that was clearing the VTR + mode bit when the 7196 status was requested. + 3/15/96 Fixed bug introduced in previous version that + stopped the only fields mode from working. + Added METEOR{GS}TS ioctl, still needs work. + 3/25/96 Added YUV_9 and YUV_12 modes. Cleaned up some of the + code and converted variables to use the new register + types. + 4/8/96 Fixed the a bug in with the range enable. Pointed + out by Jim Bray. + 5/13/96 Fix the FPS ioctl so it actually sets the frames + per second. Code supplied by ian@robots.ox.ac.uk. + The new code implements a new define: + METEOR_SYSTEM_DEFAULT which should be defined as + METEOR_PAL, METEOR_SECAM, or METEOR_NTSC in your system + configuration file. If METEOR_SYSTEM_DEFAULT isn't + defined, and there is not a signal when set_fps is + called, then the call has no effect. + Changed the spelling of PLANER to PLANAR as pointed + out by Paco Hope <paco@cs.virigina.edu> and define + PLANER to be PLANAR for backward compatibility. + 5/28/95 METEOR_INPUT_DEV_RCA -> METEOR_INPUT_DEV0, not + METEOR_GEO_DEV0. Pointed out by Ian Reid, + <ian@robots.ox.ac.uk>. + METEOR_DEV_MASK should be 0x0000f000 and not + 0x2000f000, otherwise METEOR_RGB gets masked + out. Pointed out by Ian Reid. + Changed the fps code to give even distribution for + low frame rates. Code supplied by Ian Reid. + Fix some problems with the RGB version. Patch supplied + by <ljo@po.cwru.edu>. + Added METEOR_FIELD_MODE to include files for a + future version of this driver. +*/ + +#ifdef COMPILING_LINT +#warning "The meteor driver is broken and is not compiled with LINT" +#else + +#include "meteor.h" + +#include "opt_meteor.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/signalvar.h> +#include <sys/mman.h> +#include <sys/uio.h> + +#if defined(METEOR_FreeBSD_210) +#include <machine/cpu.h> /* bootverbose */ +#endif + +#include <vm/vm.h> +#include <vm/vm_kern.h> +#include <vm/pmap.h> +#include <vm/vm_extern.h> + +#include <pci/pcivar.h> +#include <pci/pcireg.h> +#include <machine/ioctl_meteor.h> +#include <pci/meteor_reg.h> + +#ifndef COMPAT_OLDPCI +#error "The meteor device requires the old pci compatibility shims" +#endif + +static void meteor_intr(void *arg); + +/* + * Allocate enough memory for: + * 768x576 RGB 16 or YUV (16 storage bits/pixel) = 884736 = 216 pages + * + * You may override this using the options "METEOR_ALLOC_PAGES=value" in your + * kernel configuration file. + */ +#ifndef METEOR_ALLOC_PAGES +#define METEOR_ALLOC_PAGES 217 +#endif +#define METEOR_ALLOC (METEOR_ALLOC_PAGES * PAGE_SIZE) + +static meteor_reg_t meteor[NMETEOR]; +#define METEOR_NUM(mtr) ((mtr - &meteor[0])/sizeof(meteor_reg_t)) + +#define METPRI (PZERO+8)|PCATCH + +static const char* met_probe (pcici_t tag, pcidi_t type); +static void met_attach(pcici_t tag, int unit); +static u_long met_count; + +static struct pci_device met_device = { + "meteor", + met_probe, + met_attach, + &met_count +}; + +COMPAT_PCI_DRIVER (meteor, met_device); + +#if defined(METEOR_FreeBSD_210) /* XXX */ +d_open_t meteor_open; +d_close_t meteor_close; +d_read_t meteor_read; +d_write_t meteor_write; +d_ioctl_t meteor_ioctl; +d_mmap_t meteor_mmap; +#else +static d_open_t meteor_open; +static d_close_t meteor_close; +static d_read_t meteor_read; +static d_write_t meteor_write; +static d_ioctl_t meteor_ioctl; +static d_mmap_t meteor_mmap; + +#define CDEV_MAJOR 67 +static struct cdevsw meteor_cdevsw = { + /* open */ meteor_open, + /* close */ meteor_close, + /* read */ meteor_read, + /* write */ meteor_write, + /* ioctl */ meteor_ioctl, + /* poll */ nopoll, + /* mmap */ meteor_mmap, + /* strategy */ nostrategy, + /* name */ "meteor", + /* maj */ CDEV_MAJOR, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ 0, +}; +#endif + +static mreg_t saa7116_pci_default[sizeof(struct saa7116_regs)/sizeof(mreg_t)]={ + /* PCI Memory registers */ + /* BITS Type Description */ +/* 0x00 */ 0x00000000, /* 31:1 e*RW DMA 1 (Even) + 0 RO 0x0 */ +/* 0x04 */ 0x00000000, /* 31:2 e*RW DMA 2 (Even) + 1:0 RO 0x0 */ +/* 0x08 */ 0x00000000, /* 31:2 e*RW DMA 3 (Even) + 1:0 RO 0x0 */ +/* 0x0c */ 0x00000000, /* 31:1 o*RW DMA 1 (Odd) + 0 RO 0x0 */ +/* 0x10 */ 0x00000000, /* 31:2 o*RW DMA 2 (Odd) + 1:0 RO 0x0 */ +/* 0x14 */ 0x00000000, /* 31:2 o*RW DMA 3 (Odd) + 1:0 RO 0x0 */ +/* 0x18 */ 0x00000500, /* 15:2 e*RW Stride 1 (Even) + 1:0 RO 0x0 */ +/* 0x1c */ 0x00000000, /* 15:2 e*RW Stride 2 (Even) + 1:0 RO 0x0 */ +/* 0x20 */ 0x00000000, /* 15:2 e*RW Stride 3 (Even) + 1:0 RO 0x0 */ +/* 0x24 */ 0x00000500, /* 15:2 o*RW Stride 1 (Odd) + 1:0 RO 0x0 */ +/* 0x28 */ 0x00000000, /* 15:2 o*RW Stride 2 (Odd) + 1:0 RO 0x0 */ +/* 0x2c */ 0x00000000, /* 15:2 o*RW Stride 3 (Odd) + 1:0 RO 0x0 */ +/* 0x30 */ 0xeeeeee01, /* 31:8 *RW Route (Even) + 7:0 *RW Mode (Even) */ +/* 0x34 */ 0xeeeeee01, /* 31:8 *RW Route (Odd) + 7:0 *RW Mode (Odd) */ +/* 0x38 */ 0x00200020, /* 22:16 *RW FIFO Trigger Planer Mode, + 6:0 *RW FIFO Trigger Packed Mode */ +/* 0x3c */ 0x00000107, /* 9:8 *RW Reserved (0x0) + 2 *RW Field Toggle + 1 *RW Reserved (0x1) + 0 *RW Reserved (0x1) */ +/* 0x40 */ 0x000000c0, /* 15 *RW Range Enable + 14 *RW Corrupt Disable + 11 *RR Address Error (Odd) + 10 *RR Address Error (Even) + 9 *RR Field Corrupt (Odd) + 8 *RR Field Corrupt (Even) + 7 *RW Fifo Enable + 6 *RW VRSTN# + 5 *RR Field Done (Odd) + 4 *RR Field Done (Even) + 3 *RS Single Field Capture (Odd) + 2 *RS Single Field Capture (Even) + 1 *RW Capture (ODD) Continous + 0 *RW Capture (Even) Continous */ +/* 0x44 */ 0x00000000, /* 7:0 *RW Retry Wait Counter */ +/* 0x48 */ 0x00000307, /* 10 *RW Interrupt mask, start of field + 9 *RW Interrupt mask, end odd field + 8 *RW Interrupt mask, end even field + 2 *RR Interrupt status, start of field + 1 *RR Interrupt status, end of odd + 0 *RR Interrupt status, end of even */ +/* 0x4c */ 0x00000001, /* 31:0 *RW Field Mask (Even) continous */ +/* 0x50 */ 0x00000001, /* 31:0 *RW Field Mask (Odd) continous */ +/* 0x54 */ 0x00000000, /* 20:16 *RW Mask Length (Odd) + 4:0 *RW Mask Length (Even) */ +/* 0x58 */ 0x0005007c, /* 22:16 *RW FIFO almost empty + 6:0 *RW FIFO almost full */ +/* 0x5c */ 0x461e1e0f, /* 31:24 *RW I2C Phase 4 + 23:16 *RW I2C Phase 3 + 15:8 *RW I2C Phase 2 + 7:0 *RW I2C Phase 1 */ +/* 0x60 */ 0x00000300, /* 31:24 *RO I2C Read Data + 23:16 **RW I2C Auto Address + 11 RO I2C SCL Input + 10 RO I2C SDA Input + 9 RR I2C Direct Abort + 8 RR I2C Auto Abort + 3 RW I2C SCL Output + 2 RW I2C SDA Output + 1 RW I2C Bypass + 0 RW I2C Auto Enable */ +/* 0x64 */ 0x00000000, /* 24 RS I2C New Cycle + 23:16 **RW I2C Direct Address + 15:8 **RW I2C Direct Sub-address + 7:0 **RW I2C Direct Write Address */ +/* 0x68 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 1 (Even) + 23:16 **RW I2C Auto Data 1 (Even) + 15:8 **RW I2C Auto Sub-address 0 (Even) + 7:0 **RW I2C Auto Data 0 (Even) */ +/* 0x6c */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 3 (Even) + 23:16 **RW I2C Auto Data 3 (Even) + 15:8 **RW I2C Auto Sub-address 2 (Even) + 7:0 **RW I2C Auto Data 2 (Even) */ +/* 0x70 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 5 (Even) + 23:16 **RW I2C Auto Data 5 (Even) + 15:8 **RW I2C Auto Sub-address 4 (Even) + 7:0 **RW I2C Auto Data 4 (Even) */ +/* 0x74 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 7 (Even) + 23:16 **RW I2C Auto Data 7 (Even) + 15:8 **RW I2C Auto Sub-address 6 (Even) + 7:0 **RW I2C Auto Data 6 (Even) */ +/* 0x78 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 1 (Odd) + 23:16 **RW I2C Auto Data 1 (Odd) + 15:8 **RW I2C Auto Sub-address 0 (Odd) + 7:0 **RW I2C Auto Data 0 (Odd) */ +/* 0x7c */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 3 (Odd) + 23:16 **RW I2C Auto Data 3 (Odd) + 15:8 **RW I2C Auto Sub-address 2 (Odd) + 7:0 **RW I2C Auto Data 2 (Odd) */ +/* 0x80 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 5 (Odd) + 23:16 **RW I2C Auto Data 5 (Odd) + 15:8 **RW I2C Auto Sub-address 4 (Odd) + 7:0 **RW I2C Auto Data 4 (Odd) */ +/* 0x84 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 7 (Odd) + 23:16 **RW I2C Auto Data 7 (Odd) + 15:8 **RW I2C Auto Sub-address 6 (Odd) + 7:0 **RW I2C Auto Data 6 (Odd) */ +/* 0x88 */ 0x00000000, /* 23:16 **RW I2C Register Enable (Odd) + 7:0 **RW I2C Register Enable (Even) */ +/* 0x8c */ 0x00000000, /* 23:2 e*RW DMA End (Even) + 1:0 RO 0x0 */ +/* 0x90 */ 0x00000000 /* 23:2 e*RW DMA End (Odd) + 1:0 RO 0x0 */ +}; + +static u_char saa7196_i2c_default[NUM_SAA7196_I2C_REGS] = { + /* SAA7196 I2C bus control */ + /* BITS Function */ +/* 00 */ 0x50, /* 7:0 Increment Delay */ +/* 01 */ 0x30, /* 7:0 Horizontal Sync Begin for 50hz */ +/* 02 */ 0x00, /* 7:0 Horizontal Sync Stop for 50hz */ +/* 03 */ 0xe8, /* 7:0 Horizontal Sync Clamp Start for 50hz */ +/* 04 */ 0xb6, /* 7:0 Horizontal Sync Clamp Stop for 50hz */ +/* 05 */ 0xf4, /* 7:0 Horizontal Sync Start after PH1 for 50hz */ +/* 06 */ 0x46, /* 7 Input mode =0 CVBS, =1 S-Video + 6 Pre filter + 5:4 Aperture Bandpass characteristics + 3:2 Coring range for high freq + 1:0 Aperture bandpass filter weights */ +/* 07 */ 0x00, /* 7:0 Hue */ +/* 08 */ 0x7f, /* 7:3 Colour-killer threshold QAM (PAL, NTSC) */ +/* 09 */ 0x7f, /* 7:3 Colour-killer threshold SECAM */ +/* 0a */ 0x7f, /* 7:0 PAL switch sensitivity */ +/* 0b */ 0x7f, /* 7:0 SECAM switch sensitivity */ +/* 0c */ 0x40, /* 7 Colour-on bit + 6:5 AGC filter */ +/* 0d */ 0x84, /* 7 VTR/TV mode bit = 1->VTR mode + 3 Realtime output mode select bit + 2 HREF position select + 1 Status byte select + 0 SECAM mode bit */ +/* 0e */ 0x38, /* 7 Horizontal clock PLL + 5 Select interal/external clock source + 4 Output enable of Horizontal/Vertical sync + 3 Data output YUV enable + 2 S-VHS bit + 1 GPSW2 + 0 GPSW1 */ +/* 0f */ 0x50, /* 7 Automatic Field detection + 6 Field Select 0 = 50hz, 1=60hz + 5 SECAM cross-colour reduction + 4 Enable sync and clamping pulse + 3:1 Luminance delay compensation */ +/* 10 */ 0x00, /* 2 Select HREF Position + 1:0 Vertical noise reduction */ +/* 11 */ 0x2c, /* 7:0 Chrominance gain conrtol for QAM */ +/* 12 */ 0x40, /* 7:0 Chrominance saturation control for VRAM port */ +/* 13 */ 0x40, /* 7:0 Luminance contract control for VRAM port */ +/* 14 */ 0x34, /* 7:0 Horizontal sync begin for 60hz */ +#ifdef notdef +/* 15 */ 0x0c, /* 7:0 Horizontal sync stop for 60hz */ +/* 16 */ 0xfb, /* 7:0 Horizontal clamp begin for 60hz */ +/* 17 */ 0xd4, /* 7:0 Horizontal clamp stop for 60hz */ +/* 18 */ 0xec, /* 7:0 Horizontal sync start after PH1 for 60hz */ +#else + 0x0a, 0xf4, 0xce, 0xf4, +#endif +/* 19 */ 0x80, /* 7:0 Luminance brightness control for VRAM port */ +/* 1a */ 0x00, +/* 1b */ 0x00, +/* 1c */ 0x00, +/* 1d */ 0x00, +/* 1e */ 0x00, +/* 1f */ 0x00, +/* 20 */ 0x90, /* 7 ROM table bypass switch + 6:5 Set output field mode + 4 VRAM port outputs enable + 3:2 First pixel position in VRO data + 1:0 FIFO output register select */ +/* 21 */ 0x80, /* 7:0 [7:0] Pixel number per line on output */ +/* 22 */ 0x80, /* 7:0 [7:0] Pixel number per line on input */ +/* 23 */ 0x03, /* 7:0 [7:0] Horizontal start position of scaling win*/ +/* 24 */ 0x8a, /* 7:5 Horizontal decimation filter + 4 [8] Horizontal start position of scaling win + 3:2 [9:8] Pixel number per line on input + 1:0 [9:8] Pixel number per line on output */ +/* 25 */ 0xf0, /* 7:0 [7:0] Line number per output field */ +/* 26 */ 0xf0, /* 7:0 [7:0] Line number per input field */ +/* 27 */ 0x0f, /* 7:0 [7:0] Vertical start of scaling window */ +/* 28 */ 0x80, /* 7 Adaptive filter switch + 6:5 Vertical luminance data processing + 4 [8] Vertical start of scaling window + 3:2 [9:8] Line number per input field + 1:0 [9:8] Line number per output field */ +/* 29 */ 0x16, /* 7:0 [7:0] Vertical bypass start */ +/* 2a */ 0x00, /* 7:0 [7:0] Vertical bypass count */ +/* 2b */ 0x00, /* 4 [8] Vertical bypass start + 2 [8] Vertical bypass count + 0 Polarity, internally detected odd even flag */ +/* 2c */ 0x80, /* 7:0 Set lower limit V for colour-keying */ +/* 2d */ 0x7f, /* 7:0 Set upper limit V for colour-keying */ +/* 2e */ 0x80, /* 7:0 Set lower limit U for colour-keying */ +/* 2f */ 0x7f, /* 7:0 Set upper limit U for colour-keying */ +/* 30 */ 0xbf /* 7 VRAM bus output format + 6 Adaptive geometrical filter + 5 Luminance limiting value + 4 Monochrome and two's complement output data sel + 3 Line quailifier flag + 2 Pixel qualifier flag + 1 Transparent data transfer + 0 Extended formats enable bit */ +}; + +static u_char bt254_default[NUM_BT254_REGS] = { + 0x00, /* 24 bpp */ + 0xa0, + 0xa0, + 0xa0, + 0x50, + 0x50, + 0x50, +} ; + +/* + * i2c_write: + * Returns 0 Succesful completion. + * Returns 1 If transfer aborted or timeout occured. + * + */ +static int i2c_print_err = 1; +static int +i2c_write(meteor_reg_t * mtr, u_char slave, u_char rw, u_char reg, u_char data) +{ +register unsigned long wait_counter = 0x0001ffff; +register mreg_t * iic_write_loc = &mtr->base->i2c_write; +register int err = 0; + + + /* Write the data the the i2c write register */ + *iic_write_loc = SAA7116_IIC_NEW_CYCLE | + (((u_long)slave|(u_long)rw) << 16) | + ((u_long)reg << 8) | (u_long)data; + + /* Wait until the i2c cycle is compeleted */ + while((*iic_write_loc & SAA7116_IIC_NEW_CYCLE)) { + if(!wait_counter) break; + wait_counter--; + } + + /* 1ffff should be enough delay time for the i2c cycle to complete */ + if(!wait_counter) { + if(i2c_print_err) + printf("meteor%d: %d i2c %s transfer timeout 0x%x", + METEOR_NUM(mtr), slave, + rw ? "read" : "write", *iic_write_loc); + + err=1; + } + + /* Check for error on direct write, clear if any */ + if(mtr->base->i2c_read & SAA7116_IIC_DIRECT_TRANSFER_ABORTED){ + mtr->base->i2c_read |= SAA7116_IIC_DIRECT_TRANSFER_ABORTED; + if(i2c_print_err) + printf("meteor%d: 0x%x i2c %s tranfer aborted", + METEOR_NUM(mtr), slave, + rw ? "read" : "write" ); + err= 1; + } + + if(err) { + if(i2c_print_err) + printf(" - reg=0x%x, value=0x%x.\n", reg, data); + } + + return err; +} +#undef i2c_print + +static const char * +met_probe (pcici_t tag, pcidi_t type) +{ + + switch (type) { + case SAA7116_PHILIPS_ID: /* meteor */ + return("Philips SAA 7116"); + }; + return ((char *)0); +} + + /* interrupt handling routine + complete meteor_read() if using interrupts + */ +static void +meteor_intr(void *arg) +{ + meteor_reg_t *mtr = (meteor_reg_t *) arg; + mreg_t *cap = &mtr->base->cap_cntl, + *base = &mtr->base->dma1e, + *stat = &mtr->base->irq_stat; + u_long status = *stat, + cap_err = *cap & 0x00000f00, +#ifdef METEOR_CHECK_PCI_BUS + pci_err = pci_conf_read(mtr->tag, + PCI_COMMAND_STATUS_REG), +#endif + next_base = (u_long)(vtophys(mtr->bigbuf)); + + /* + * Disable future interrupts if a capture mode is not selected. + * This can happen when we are in the process of closing or + * changing capture modes, otherwise it shouldn't happen. + */ + if(!(mtr->flags & METEOR_CAP_MASK)) { + *cap &= 0x8ff0; /* disable future interrupts */ + } +#ifdef METEOR_CHECK_PCI_BUS + /* + * Check for pci bus errors. + */ +#define METEOR_MASTER_ABORT 0x20000000 +#define METEOR_TARGET_ABORT 0x10000000 + if(pci_err & METEOR_MASTER_ABORT) { + printf("meteor%d: intr: pci bus master dma abort: 0x%x 0x%x.\n", + METEOR_NUM(mtr), *base, *(base+3)); + pci_conf_write(mtr->tag, PCI_COMMAND_STATUS_REG, pci_err); + } + if(pci_err & METEOR_TARGET_ABORT) { + printf("meteor%d: intr: pci bus target dma abort: 0x%x 0x%x.\n", + METEOR_NUM(mtr), *base, *(base+3)); + pci_conf_write(mtr->tag, PCI_COMMAND_STATUS_REG, pci_err); + } +#endif + /* + * Check for errors. + */ + if (cap_err) { + if (cap_err & 0x300) { + if(mtr->fifo_errors % 50 == 0) { + printf("meteor%d: capture error", METEOR_NUM(mtr)); + printf(": %s FIFO overflow.\n", + cap_err&0x0100? "even" : "odd"); + } + mtr->fifo_errors++ ; /* increment fifo capture errors cnt */ + } + if (cap_err & 0xc00) { + if(mtr->dma_errors % 50 == 0) { + printf("meteor%d: capture error", METEOR_NUM(mtr)); + printf(": %s DMA address.\n", + cap_err&0x0400? "even" : "odd"); + } + mtr->dma_errors++ ; /* increment DMA capture errors cnt */ + } + } + *cap |= 0x0f30; /* clear error and field done */ + + /* + * In synchronous capture mode we need to know what the address + * offset for the next field/frame will be. next_base holds the + * value for the even dma buffers (for odd, one must add stride). + */ + if((mtr->flags & METEOR_SYNCAP) && !mtr->synch_wait && + (mtr->current < mtr->frames)) { /* could be !=, but < is safer */ + /* next_base is initialized to mtr->bigbuf */ + next_base += mtr->frame_size * mtr->current; + if(mtr->flags & METEOR_WANT_TS) + next_base += sizeof(struct timeval) * mtr->current; + } + + /* + * Count the field and clear the field flag. + * + * In single mode capture, clear the continuous capture mode. + * + * In synchronous capture mode, if we have room for another field, + * adjust DMA buffer pointers. + * When we are above the hi water mark (hiwat), mtr->synch_wait will + * be set and we will not bump the DMA buffer pointers. Thus, once + * we reach the hi water mark, the driver acts like a continuous mode + * capture on the mtr->current frame until we hit the low water + * mark (lowat). The user had the option of stopping or halting + * the capture if this is not the desired effect. + */ + if (status & 0x1) { /* even field */ + mtr->even_fields_captured++; + mtr->flags &= ~METEOR_WANT_EVEN; + if((mtr->flags & METEOR_SYNCAP) && !mtr->synch_wait) { + *base = next_base; + /* XXX should add adjustments for YUV_422 & PLANAR */ + } + /* + * If the user requested to be notified via signal, + * let them know the field is complete. + */ + if(mtr->proc && (mtr->signal & METEOR_SIG_MODE_MASK)) { + PROC_LOCK(mtr->proc); + psignal(mtr->proc, mtr->signal&(~METEOR_SIG_MODE_MASK)); + PROC_UNLOCK(mtr->proc); + } + } + if (status & 0x2) { /* odd field */ + mtr->odd_fields_captured++; + mtr->flags &= ~METEOR_WANT_ODD; + if((mtr->flags & METEOR_SYNCAP) && !mtr->synch_wait) { + *(base+3) = next_base + *(base+6); + /* XXX should add adjustments for YUV_422 & PLANAR */ + } + /* + * If the user requested to be notified via signal, + * let them know the field is complete. + */ + if(mtr->proc && (mtr->signal & METEOR_SIG_MODE_MASK)) { + PROC_LOCK(mtr->proc); + psignal(mtr->proc, mtr->signal&(~METEOR_SIG_MODE_MASK)); + PROC_UNLOCK(mtr->proc); + } + } + + /* + * If we have a complete frame. + */ + if(!(mtr->flags & METEOR_WANT_MASK)) { + mtr->frames_captured++; + /* + * post the completion time. + */ + if(mtr->flags & METEOR_WANT_TS) { + struct timeval *ts; + + if(mtr->alloc_pages * PAGE_SIZE <= (mtr->frame_size + + sizeof(struct timeval))) { + ts =(struct timeval *)mtr->bigbuf + + mtr->frame_size; + /* doesn't work in synch mode except for first frame */ + /* XXX */ + microtime(ts); + } + } + /* + * Wake up the user in single capture mode. + */ + if(mtr->flags & METEOR_SINGLE) + wakeup((caddr_t)mtr); + /* + * If the user requested to be notified via signal, + * let them know the frame is complete. + */ + if(mtr->proc && !(mtr->signal & METEOR_SIG_MODE_MASK)) { + PROC_LOCK(mtr->proc); + psignal(mtr->proc, mtr->signal&(~METEOR_SIG_MODE_MASK)); + PROC_UNLOCK(mtr->proc); + } + /* + * Reset the want flags if in continuous or + * synchronous capture mode. + */ + if(mtr->flags & (METEOR_CONTIN|METEOR_SYNCAP)) { + switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) { + case METEOR_ONLY_ODD_FIELDS: + mtr->flags |= METEOR_WANT_ODD; + break; + case METEOR_ONLY_EVEN_FIELDS: + mtr->flags |= METEOR_WANT_EVEN; + break; + default: + mtr->flags |= METEOR_WANT_MASK; + break; + } + } + /* + * Special handling for synchronous capture mode. + */ + if(mtr->flags & METEOR_SYNCAP) { + struct meteor_mem *mm = mtr->mem; + /* + * Mark the current frame as active. It is up to + * the user to clear this, but we will clear it + * for the user for the current frame being captured + * if we are within the water marks (see below). + */ + mm->active |= 1 << (mtr->current - 1); + + /* + * Since the user can muck with these values, we need + * to check and see if they are sane. If they don't + * pass the sanity check, disable the capture mode. + * This is rather rude, but then so was the user. + * + * Do we really need all of this or should we just + * eliminate the possiblity of allowing the + * user to change hi and lo water marks while it + * is running? XXX + */ + if(mm->num_active_bufs < 0 || + mm->num_active_bufs > mtr->frames || + mm->lowat < 1 || mm->lowat >= mtr->frames || + mm->hiwat < 1 || mm->hiwat >= mtr->frames || + mm->lowat > mm->hiwat ) { + *cap &= 0x8ff0; + mtr->flags &= ~(METEOR_SYNCAP|METEOR_WANT_MASK); + } else { + /* + * Ok, they are sane, now we want to + * check the water marks. + */ + if(mm->num_active_bufs <= mm->lowat) + mtr->synch_wait = 0; + if(mm->num_active_bufs >= mm->hiwat) + mtr->synch_wait = 1; + /* + * Clear the active frame bit for this frame + * and advance the counters if we are within + * the banks of the water marks. + */ + if(!mtr->synch_wait) { + mm->active &= ~(1 << mtr->current); + mtr->current++; + if(mtr->current > mtr->frames) + mtr->current = 1; + mm->num_active_bufs++; + } + } + } + } + + *stat |= 0x7; /* clear interrupt status */ + return; +} + +static void +set_fps(meteor_reg_t *mtr, u_short fps) +{ + struct saa7116_regs *s7116 = mtr->base; + unsigned status; + unsigned maxfps, mask = 0x1, length = 0; + + SAA7196_WRITE(mtr, SAA7196_STDC, SAA7196_REG(mtr, SAA7196_STDC) | 0x02); + SAA7196_READ(mtr); + status = (s7116->i2c_read & 0xff000000L) >> 24; + + /* + * Determine if there is an input signal. Depending on the + * frequency we either have a max of 25 fps (50 hz) or 30 fps (60 hz). + * If there is no input signal, then we need some defaults. If the + * user neglected to specify any defaults, just set to the fps to max. + */ + if((status & 0x40) == 0) { /* Is there a signal ? */ + if(status & 0x20) { + maxfps = 30; /* 60 hz system */ + } else { + maxfps = 25; /* 50 hz system */ + } + } else { /* We have no signal, check defaults */ +#if METEOR_SYSTEM_DEFAULT == METEOR_PAL || METEOR_SYSTEM_DEFAULT == METEOR_SECAM + maxfps = 25; +#elif METEOR_SYSTEM_DEFAULT == METEOR_NTSC + maxfps = 30; +#else + /* Don't really know what to do, just set max */ + maxfps = 30; + fps = 30; +#endif + } + + /* + * A little sanity checking... + */ + if(fps < 1) fps = 1; + if(fps > maxfps) fps = maxfps; + + /* + * Compute the mask/length using the fps. + */ + if(fps == maxfps) { + mask = 0x1; + length = 0x0; + } else if ((float)fps == maxfps/2.0) { + mask = 0x1; + length = 0x1; + } else if (fps > maxfps/2) { + float step, b; + + mask = (1<<maxfps) - 1; + length = maxfps - 1; + step = (float)(maxfps - 1)/(float)(maxfps - fps); + for(b=step; b < maxfps; b += step) { + mask &= ~(1<<((int)b)); /* mask out the bth frame */ + } + } else { /* fps < maxfps/2 */ + float step, b; + + mask = 0x1; + length = maxfps - 1; + step = (float)(maxfps -1)/(float)(fps); + for(b = step + 1; b < maxfps - 1; b += step) { + mask |= (1<<((int)b)); /* mask in the bth frame */ + } + } + + /* + * Set the fps. + */ + s7116->fme = s7116->fmo = mask; + s7116->fml = (length << 16) | length;; + + mtr->fps = fps; + + return; + +} + +/* + * There is also a problem with range checking on the 7116. + * It seems to only work for 22 bits, so the max size we can allocate + * is 22 bits long or 4194304 bytes assuming that we put the beginning + * of the buffer on a 2^24 bit boundary. The range registers will use + * the top 8 bits of the dma start registers along with the bottom 22 + * bits of the range register to determine if we go out of range. + * This makes getting memory a real kludge. + * + */ +#define RANGE_BOUNDARY (1<<22) +static vm_offset_t +get_meteor_mem(int unit, unsigned size) +{ +vm_offset_t addr = 0; + + addr = vm_page_alloc_contig(size, 0, 0xffffffff, 1<<24); + if(addr == 0) + addr = vm_page_alloc_contig(size, 0, 0xffffffff, PAGE_SIZE); + if(addr == 0) { + printf("meteor%d: Unable to allocate %d bytes of memory.\n", + unit, size); + } + + return addr; +} + +static void +bt254_write(meteor_reg_t *mtr, u_char addr, u_char data) +{ + addr &= 0x7; /* sanity? */ + mtr->bt254_reg[addr] = data; + PCF8574_DATA_WRITE(mtr, data); /* set data */ + PCF8574_CTRL_WRITE(mtr, (PCF8574_CTRL_REG(mtr) & ~0x7) | addr); + PCF8574_CTRL_WRITE(mtr, PCF8574_CTRL_REG(mtr) & ~0x10); /* WR/ to 0 */ + PCF8574_CTRL_WRITE(mtr, PCF8574_CTRL_REG(mtr) | 0x10); /* WR to 1 */ + PCF8574_DATA_WRITE(mtr, 0xff); /* clr data */ + +} + +static void +bt254_init(meteor_reg_t *mtr) +{ +int i; + + PCF8574_CTRL_WRITE(mtr, 0x7f); + PCF8574_DATA_WRITE(mtr, 0xff); /* data port must be 0xff */ + PCF8574_CTRL_WRITE(mtr, 0x7f); + + /* init RGB module for 24bpp, composite input */ + for(i=0; i<NUM_BT254_REGS; i++) + bt254_write(mtr, i, bt254_default[i]); + + bt254_write(mtr, BT254_COMMAND, 0x00); /* 24 bpp */ +} + +static void +bt254_ntsc(meteor_reg_t *mtr, int arg) +{ + if (arg){ + /* Set NTSC bit */ + PCF8574_CTRL_WRITE(mtr, PCF8574_CTRL_REG(mtr) | 0x20); + } + else { + /* reset NTSC bit */ + PCF8574_CTRL_WRITE(mtr, PCF8574_CTRL_REG(mtr) &= ~0x20); + } +} + +static void +select_bt254(meteor_reg_t *mtr) +{ + /* disable saa7196, saaen = 1 */ + PCF8574_CTRL_WRITE(mtr, PCF8574_CTRL_REG(mtr) | 0x80); + /* enable Bt254, bten = 0 */ + PCF8574_CTRL_WRITE(mtr, PCF8574_CTRL_REG(mtr) & ~0x40); +} + +static void +select_saa7196(meteor_reg_t *mtr) +{ + /* disable Bt254, bten = 1 */ + PCF8574_CTRL_WRITE(mtr, PCF8574_CTRL_REG(mtr) | 0x40); + /* enable saa7196, saaen = 0 */ + PCF8574_CTRL_WRITE(mtr, PCF8574_CTRL_REG(mtr) & ~0x80); +} + +/* + * Initialize the 7116, 7196 and the RGB module. + */ +static void +meteor_init ( meteor_reg_t *mtr ) +{ + mreg_t *vbase_addr; + int i; + + /* + * Initialize the Philips SAA7116 + */ + mtr->base->cap_cntl = 0x00000040L; + vbase_addr = &mtr->base->dma1e; + for (i = 0 ; i < (sizeof(struct saa7116_regs)/sizeof(mreg_t)); i++) + *vbase_addr++ = saa7116_pci_default[i]; + + /* + * Check for the Philips SAA7196 + */ + i2c_print_err = 0; + if(i2c_write(mtr, SAA7196_I2C_ADDR, SAA7116_I2C_WRITE, 0, 0xff) == 0) { + i2c_print_err = 1; + /* + * Initialize 7196 + */ + for (i = 0; i < NUM_SAA7196_I2C_REGS; i++) + SAA7196_WRITE(mtr, i, saa7196_i2c_default[i]); + /* + * Get version number. + */ + SAA7196_WRITE(mtr, SAA7196_STDC, + SAA7196_REG(mtr, SAA7196_STDC) & ~0x02); + SAA7196_READ(mtr); + printf("meteor%d: <Philips SAA 7196> rev 0x%x\n", + METEOR_NUM(mtr), + (unsigned)((mtr->base->i2c_read & 0xff000000L) >> 28)); + } else { + i2c_print_err = 1; + printf("meteor%d: <Philips SAA 7196 NOT FOUND>\n", + METEOR_NUM(mtr)); + } + /* + * Check for RGB module, initialized if found. + */ + i2c_print_err = 0; + if(i2c_write(mtr,PCF8574_DATA_I2C_ADDR,SAA7116_I2C_WRITE,0,0xff) == 0) { + i2c_print_err = 1; + printf("meteor%d: <Booktree 254 (RGB module)>\n", + METEOR_NUM(mtr)); /* does this have a rev #? */ + bt254_init(mtr); /* Set up RGB module */ + mtr->flags = METEOR_RGB; + } else { + i2c_print_err = 1; + mtr->flags = 0; + } + + set_fps(mtr, 30); + +} + +static void +met_attach(pcici_t tag, int unit) +{ +#ifdef METEOR_IRQ + u_long old_irq, new_irq; +#endif METEOR_IRQ + meteor_reg_t *mtr; + vm_offset_t buf; + u_long latency; + + if (unit >= NMETEOR) { + printf("meteor%d: attach: only %d units configured.\n", + unit, NMETEOR); + printf("meteor%d: attach: invalid unit number.\n", unit); + return ; + } + + /* + * Check for Meteor/PPB (PCI-PCI Bridge) + * Reprogram IBM Bridge if detected. + * New Meteor cards have an IBM PCI-PCI bridge, creating a secondary + * PCI bus. The SAA chip is connected to this secondary bus. + */ + + /* If we are not on PCI Bus 0, check for the Bridge */ + if ( pci_get_bus_from_tag( tag ) != 0) { + pcici_t bridge_tag; + + /* get tag of parent bridge */ + bridge_tag = pci_get_parent_from_tag( tag ); + + /* Look for IBM 82351, 82352 or 82353 */ + if (pci_conf_read(bridge_tag, PCI_ID_REG) == 0x00221014) { + + if ( bootverbose) + printf("meteor%d: PPB device detected, reprogramming IBM bridge.\n", unit); + + /* disable SERR */ + pci_cfgwrite(bridge_tag, 0x05, 0x00, 1); + /* set LATENCY */ + pci_cfgwrite(bridge_tag, 0x0d, 0x20, 1); + /* write posting enable, prefetch enabled --> GRAB direction */ + pci_cfgwrite(bridge_tag, 0x42, 0x14, 1); + /* set PRTR Primary retry timer register */ + pci_cfgwrite(bridge_tag, 0x4c, 0x10, 1); + } + } + + mtr = &meteor[unit]; + mtr->tag = tag; + pci_map_mem(tag, PCI_MAP_REG_START, (vm_offset_t *)&mtr->base, + &mtr->phys_base); + +#ifdef METEOR_IRQ /* from the configuration file */ + old_irq = pci_conf_read(tag, PCI_INTERRUPT_REG); + pci_conf_write(tag, PCI_INTERRUPT_REG, METEOR_IRQ); + new_irq = pci_conf_read(tag, PCI_INTERRUPT_REG); + printf("meteor%d: attach: irq changed from %d to %d\n", + unit, (old_irq & 0xff), (new_irq & 0xff)); +#endif METEOR_IRQ + /* setup the interrupt handling routine */ + pci_map_int(tag, meteor_intr, (void*) mtr, &net_imask); + +/* + * PCI latency timer. 32 is a good value for 4 bus mastering slots, if + * you have more than for, then 16 would probably be a better value. + * + */ +#ifndef METEOR_DEF_LATENCY_VALUE +#define METEOR_DEF_LATENCY_VALUE 32 +#endif + latency = pci_conf_read(tag, PCI_LATENCY_TIMER); + latency = (latency >> 8) & 0xff; + if(bootverbose) { + if(latency) + printf("meteor%d: PCI bus latency is", unit); + else + printf("meteor%d: PCI bus latency was 0 changing to", + unit); + } + if(!latency) { + latency = METEOR_DEF_LATENCY_VALUE; + pci_conf_write(tag, PCI_LATENCY_TIMER, latency<<8); + } + if(bootverbose) { + printf(" %lu.\n", latency); + } + + meteor_init(mtr); /* set up saa7116, saa7196, and rgb module */ + + if(METEOR_ALLOC) + buf = get_meteor_mem(unit, METEOR_ALLOC); + else + buf = 0; + if(bootverbose) { + printf("meteor%d: buffer size %d, addr 0x%x\n", + unit, METEOR_ALLOC, vtophys(buf)); + } + + mtr->bigbuf = buf; + mtr->alloc_pages = METEOR_ALLOC_PAGES; + if(buf != 0) { + bzero((caddr_t) buf, METEOR_ALLOC); + buf = vtophys(buf); + /* 640x480 RGB 16 */ + mtr->base->dma1e = buf; + mtr->base->dma1o = buf + 0x500; + mtr->base->dma_end_e = + mtr->base->dma_end_o = buf + METEOR_ALLOC; + } + /* 1 frame of 640x480 RGB 16 */ + mtr->cols = 640; + mtr->rows = 480; + mtr->depth = 2; /* two bytes per pixel */ + mtr->frames = 1; /* one frame */ + + mtr->flags |= METEOR_INITALIZED | METEOR_AUTOMODE | METEOR_DEV0 | + METEOR_RGB16; + make_dev(&meteor_cdevsw, unit, 0, 0, 0644, "meteor"); +} + +#define UNIT(x) ((x) & 0x07) + +#ifdef unused +static int +meteor_reset(dev_t dev) +{ +int unit = UNIT(minor(dev)); +struct saa7116_regs *m; + + if(unit >= NMETEOR) + return ENXIO; + + m = meteor[unit].base; + + m->cap_cntl = 0x0; + tsleep((caddr_t)m, METPRI, "Mreset", hz/50); + + m->cap_cntl = 0x8ff0; + m->cap_cntl = 0x80c0; + m->cap_cntl = 0x8040; + tsleep((caddr_t)m, METPRI, "Mreset", hz/10); + m->cap_cntl = 0x80c0; + + return 0; + +} +#endif + +/*--------------------------------------------------------- +** +** Meteor character device driver routines +** +**--------------------------------------------------------- +*/ + + +int +meteor_open(dev_t dev, int flags, int fmt, struct thread *td) +{ + meteor_reg_t *mtr; + int unit; + int i; + + unit = UNIT(minor(dev)); + if (unit >= NMETEOR) /* unit out of range */ + return(ENXIO); + + mtr = &(meteor[unit]); + + if (!(mtr->flags & METEOR_INITALIZED)) /* device not found */ + return(ENXIO); + + if (mtr->flags & METEOR_OPEN) /* device is busy */ + return(EBUSY); + + mtr->flags |= METEOR_OPEN; + /* + * Make sure that the i2c regs are set the same for each open. + */ + for(i=0; i< NUM_SAA7196_I2C_REGS; i++) { + SAA7196_WRITE(mtr, i, saa7196_i2c_default[i]); + } + + mtr->fifo_errors = 0; + mtr->dma_errors = 0; + mtr->frames_captured = 0; + mtr->even_fields_captured = 0; + mtr->odd_fields_captured = 0; + mtr->proc = (struct proc *)0; + set_fps(mtr, 30); +#ifdef METEOR_TEST_VIDEO + mtr->video.addr = 0; + mtr->video.width = 0; + mtr->video.banksize = 0; + mtr->video.ramsize = 0; +#endif + + return(0); +} + +int +meteor_close(dev_t dev, int flags, int fmt, struct thread *td) +{ + meteor_reg_t *mtr; + int unit; +#ifdef METEOR_DEALLOC_ABOVE + int temp; +#endif + + unit = UNIT(minor(dev)); + if (unit >= NMETEOR) /* unit out of range */ + return(ENXIO); + + mtr = &(meteor[unit]); + mtr->flags &= ~METEOR_OPEN; + + if(mtr->flags & METEOR_SINGLE) + /* this should not happen, the read capture + should have completed or in the very least + recieved a signal before close is called. */ + wakeup((caddr_t)mtr); /* continue read */ + /* + * Turn off capture mode. + */ + mtr->base->cap_cntl = 0x8ff0; + mtr->flags &= ~(METEOR_CAP_MASK|METEOR_WANT_MASK); + mtr->proc = (struct proc *)0; + +#ifdef METEOR_DEALLOC_PAGES + if (mtr->bigbuf != NULL) { + kmem_free(kernel_map,mtr->bigbuf,(mtr->alloc_pages*PAGE_SIZE)); + mtr->bigbuf = NULL; + mtr->alloc_pages = 0; + } +#else +#ifdef METEOR_DEALLOC_ABOVE + if (mtr->bigbuf != NULL && mtr->alloc_pages > METEOR_DEALLOC_ABOVE) { + temp = METEOR_DEALLOC_ABOVE - mtr->alloc_pages; + kmem_free(kernel_map, + mtr->bigbuf+((mtr->alloc_pages - temp) * PAGE_SIZE), + (temp * PAGE_SIZE)); + mtr->alloc_pages = METEOR_DEALLOC_ABOVE; + } +#endif +#endif + + return(0); +} + +static void +start_capture(meteor_reg_t *mtr, unsigned type) +{ +mreg_t *cap = &mtr->base->cap_cntl; + + mtr->flags |= type; + switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) { + case METEOR_ONLY_EVEN_FIELDS: + mtr->flags |= METEOR_WANT_EVEN; + if(type == METEOR_SINGLE) + *cap = 0x0ff4 | mtr->range_enable; + else + *cap = 0x0ff1 | mtr->range_enable; + break; + case METEOR_ONLY_ODD_FIELDS: + mtr->flags |= METEOR_WANT_ODD; + if(type == METEOR_SINGLE) + *cap = 0x0ff8 | mtr->range_enable; + else + *cap = 0x0ff2 | mtr->range_enable; + break; + default: + mtr->flags |= METEOR_WANT_MASK; + if(type == METEOR_SINGLE) + *cap = 0x0ffc | mtr->range_enable; + else + *cap = 0x0ff3 | mtr->range_enable; + break; + } +} + +int +meteor_read(dev_t dev, struct uio *uio, int ioflag) +{ + meteor_reg_t *mtr; + int unit; + int status; + int count; + + unit = UNIT(minor(dev)); + if (unit >= NMETEOR) /* unit out of range */ + return(ENXIO); + + mtr = &(meteor[unit]); + if (mtr->bigbuf == 0)/* no frame buffer allocated (ioctl failed) */ + return(ENOMEM); + + if (mtr->flags & METEOR_CAP_MASK) + return(EIO); /* already capturing */ + + count = mtr->rows * mtr->cols * mtr->depth; + if (uio->uio_iov->iov_len < count) + return(EINVAL); + + /* Start capture */ + start_capture(mtr, METEOR_SINGLE); + + status=tsleep((caddr_t)mtr, METPRI, "capturing", 0); + if (!status) /* successful capture */ + status = uiomove((caddr_t)mtr->bigbuf, count, uio); + else + printf ("meteor%d: read: tsleep error %d\n", unit, status); + + mtr->flags &= ~(METEOR_SINGLE | METEOR_WANT_MASK); + + return(status); +} + +int +meteor_write(dev_t dev, struct uio *uio, int ioflag) +{ + return(0); +} + +int +meteor_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct thread *td) +{ + int error; + int unit; + unsigned int temp; + meteor_reg_t *mtr; + struct meteor_counts *cnt; + struct meteor_geomet *geo; + struct meteor_mem *mem; + struct meteor_capframe *frame; +#ifdef METEOR_TEST_VIDEO + struct meteor_video *video; +#endif + vm_offset_t buf; + struct saa7116_regs *base; + + error = 0; + + if (!arg) return(EINVAL); + unit = UNIT(minor(dev)); + if (unit >= NMETEOR) /* unit out of range */ + return(ENXIO); + + mtr = &(meteor[unit]); + base = mtr->base; + + switch (cmd) { + case METEORSTS: + if(*arg) + mtr->flags |= METEOR_WANT_TS; + else + mtr->flags &= ~METEOR_WANT_TS; + break; + case METEORGTS: + if(mtr->flags & METEOR_WANT_TS) + *arg = 1; + else + *arg = 0; + break; +#ifdef METEOR_TEST_VIDEO + case METEORGVIDEO: + video = (struct meteor_video *)arg; + video->addr = mtr->video.addr; + video->width = mtr->video.width; + video->banksize = mtr->video.banksize; + video->ramsize = mtr->video.ramsize; + break; + case METEORSVIDEO: + video = (struct meteor_video *)arg; + mtr->video.addr = video->addr; + mtr->video.width = video->width; + mtr->video.banksize = video->banksize; + mtr->video.ramsize = video->ramsize; + break; +#endif + case METEORSFPS: + set_fps(mtr, *(u_short *)arg); + break; + case METEORGFPS: + *(u_short *)arg = mtr->fps; + break; + case METEORSSIGNAL: + mtr->signal = *(int *) arg; + if (mtr->signal) { + mtr->proc = td->td_proc; + } else { + mtr->proc = (struct proc *)0; + } + break; + case METEORGSIGNAL: + *(int *)arg = mtr->signal; + break; + case METEORSTATUS: /* get 7196 status */ + temp = 0; + SAA7196_WRITE(mtr, SAA7196_STDC, + SAA7196_REG(mtr, SAA7196_STDC) | 0x02); + SAA7196_READ(mtr); + temp |= (base->i2c_read & 0xff000000L) >> 24; + SAA7196_WRITE(mtr, SAA7196_STDC, + SAA7196_REG(mtr, SAA7196_STDC) & ~0x02); + SAA7196_READ(mtr); + temp |= (base->i2c_read & 0xff000000L) >> 16; + *(u_short *)arg = temp; + break; + case METEORSHUE: /* set hue */ + SAA7196_WRITE(mtr, SAA7196_HUEC, *(char *)arg); + break; + case METEORGHUE: /* get hue */ + *(char *)arg = SAA7196_REG(mtr, SAA7196_HUEC); + break; + case METEORSCHCV: /* set chrominance gain */ + SAA7196_WRITE(mtr, SAA7196_CGAINR, *(char *)arg); + break; + case METEORGCHCV: /* get chrominance gain */ + *(char *)arg = SAA7196_REG(mtr, SAA7196_CGAINR); + break; + case METEORSBRIG: /* set brightness */ + SAA7196_WRITE(mtr, SAA7196_BRIG, *(char *)arg); + break; + case METEORGBRIG: /* get brightness */ + *(char *)arg = SAA7196_REG(mtr, SAA7196_BRIG); + break; + case METEORSCSAT: /* set chroma saturation */ + SAA7196_WRITE(mtr, SAA7196_CSAT, *(char *)arg); + break; + case METEORGCSAT: /* get chroma saturation */ + *(char *)arg = SAA7196_REG(mtr, SAA7196_CSAT); + break; + case METEORSCONT: /* set contrast */ + SAA7196_WRITE(mtr, SAA7196_CONT, *(char *)arg); + break; + case METEORGCONT: /* get contrast */ + *(char *)arg = SAA7196_REG(mtr, SAA7196_CONT); + break; + case METEORSBT254: + if((mtr->flags & METEOR_RGB) == 0) + return EINVAL; + temp = *(unsigned short *)arg; + bt254_write(mtr, temp & 0xf, (temp & 0x0ff0) >> 4); + break; + case METEORGBT254: + if((mtr->flags & METEOR_RGB) == 0) + return EINVAL; + temp = *(unsigned short *)arg & 0x7; + *(unsigned short *)arg = mtr->bt254_reg[temp] << 4 | temp; + break; + case METEORSHWS: /* set horizontal window start */ + SAA7196_WRITE(mtr, SAA7196_HWS, *(char *)arg); + break; + case METEORGHWS: /* get horizontal window start */ + *(char *)arg = SAA7196_REG(mtr, SAA7196_HWS); + break; + case METEORSVWS: /* set vertical window start */ + SAA7196_WRITE(mtr, SAA7196_VWS, *(char *)arg); + break; + case METEORGVWS: /* get vertical window start */ + *(char *)arg = SAA7196_REG(mtr, SAA7196_VWS); + break; + case METEORSINPUT: /* set input device */ + switch(*(unsigned long *)arg & METEOR_DEV_MASK) { + case 0: /* default */ + case METEOR_INPUT_DEV0: + if(mtr->flags & METEOR_RGB) + select_saa7196(mtr); + mtr->flags = (mtr->flags & ~METEOR_DEV_MASK) + | METEOR_DEV0; + SAA7196_WRITE(mtr, 0x0e, + (SAA7196_REG(mtr, 0x0e) & ~0x3) | 0x0); + SAA7196_WRITE(mtr, 0x06, + (SAA7196_REG(mtr, 0x06) & ~0x80)); + break; + case METEOR_INPUT_DEV1: + if(mtr->flags & METEOR_RGB) + select_saa7196(mtr); + mtr->flags = (mtr->flags & ~METEOR_DEV_MASK) + | METEOR_DEV1; + SAA7196_WRITE(mtr, 0x0e, + (SAA7196_REG(mtr, 0x0e) & ~0x3) | 0x1); + SAA7196_WRITE(mtr, 0x06, + (SAA7196_REG(mtr, 0x06) & ~0x80)); + break; + case METEOR_INPUT_DEV2: + if(mtr->flags & METEOR_RGB) + select_saa7196(mtr); + mtr->flags = (mtr->flags & ~METEOR_DEV_MASK) + | METEOR_DEV2; + SAA7196_WRITE(mtr, 0x0e, + (SAA7196_REG(mtr, 0x0e) & ~0x3) | 0x2); + SAA7196_WRITE(mtr, 0x06, + (SAA7196_REG(mtr, 0x06) & ~0x80)); + break; + case METEOR_INPUT_DEV3: + if(mtr->flags & METEOR_RGB) + select_saa7196(mtr); + mtr->flags = (mtr->flags & ~METEOR_DEV_MASK) + | METEOR_DEV3; + SAA7196_WRITE(mtr, 0x0e, + (SAA7196_REG(mtr, 0x0e) | 0x3)); + SAA7196_WRITE(mtr, 0x06, + (SAA7196_REG(mtr, 0x06) & ~0x80) ); + break; + case METEOR_INPUT_DEV_SVIDEO: + if(mtr->flags & METEOR_RGB) + select_saa7196(mtr); + mtr->flags = (mtr->flags & ~METEOR_DEV_MASK) + | METEOR_DEV_SVIDEO; + SAA7196_WRITE(mtr, 0x0e, + (SAA7196_REG(mtr, 0x0e) & ~0x3) | 0x2); + SAA7196_WRITE(mtr, 0x06, + (SAA7196_REG(mtr, 0x06) & ~0x80) | 0x80); + break; + case METEOR_INPUT_DEV_RGB: + if((mtr->flags & METEOR_RGB) == 0) + return EINVAL; + mtr->flags = (mtr->flags & ~METEOR_DEV_MASK) + | METEOR_DEV_RGB; + SAA7196_WRITE(mtr, 0x0e, + (SAA7196_REG(mtr, 0x0e) & ~0x3) | 0x3); + SAA7196_WRITE(mtr, 0x06, + (SAA7196_REG(mtr, 0x06) & ~0x80)); + select_bt254(mtr); + SAA7196_WRITE(mtr, 0x0e, /* chn 3 for synch */ + (SAA7196_REG(mtr, 0x0e) & ~0x3) | 0x3); + break; + default: + return EINVAL; + } + break; + case METEORGINPUT: /* get input device */ + *(u_long *)arg = mtr->flags & METEOR_DEV_MASK; + break; + case METEORSFMT: /* set input format */ + switch(*(unsigned long *)arg & METEOR_FORM_MASK ) { + case 0: /* default */ + case METEOR_FMT_NTSC: + mtr->flags = (mtr->flags & ~METEOR_FORM_MASK) | + METEOR_NTSC; + SAA7196_WRITE(mtr, SAA7196_STDC, + (SAA7196_REG(mtr, SAA7196_STDC) & ~0x01)); + SAA7196_WRITE(mtr, 0x0f, + (SAA7196_REG(mtr, 0x0f) & ~0xe0) | 0x40); + SAA7196_WRITE(mtr, 0x22, 0x80); + SAA7196_WRITE(mtr, 0x24, + (SAA7196_REG(mtr, 0x24) & ~0x0c) | 0x08); + SAA7196_WRITE(mtr, 0x26, 0xf0); + SAA7196_WRITE(mtr, 0x28, + (SAA7196_REG(mtr, 0x28) & ~0x0c)) ; + if(mtr->flags & METEOR_RGB){ + bt254_ntsc(mtr, 1); + } + break; + case METEOR_FMT_PAL: + mtr->flags = (mtr->flags & ~METEOR_FORM_MASK) | + METEOR_PAL; + SAA7196_WRITE(mtr, SAA7196_STDC, + (SAA7196_REG(mtr, SAA7196_STDC) & ~0x01)); + SAA7196_WRITE(mtr, 0x0f, + (SAA7196_REG(mtr, 0x0f) & ~0xe0)); + SAA7196_WRITE(mtr, 0x22, 0x00); + SAA7196_WRITE(mtr, 0x24, + (SAA7196_REG(mtr, 0x24) | 0x0c)); + SAA7196_WRITE(mtr, 0x26, 0x20); + SAA7196_WRITE(mtr, 0x28, + (SAA7196_REG(mtr, 0x28) & ~0x0c) | 0x04) ; + if(mtr->flags & METEOR_RGB){ + bt254_ntsc(mtr, 0); + } + break; + case METEOR_FMT_SECAM: + mtr->flags = (mtr->flags & ~METEOR_FORM_MASK) | + METEOR_SECAM; + SAA7196_WRITE(mtr, SAA7196_STDC, + (SAA7196_REG(mtr, SAA7196_STDC) & ~0x01) | 0x1); + SAA7196_WRITE(mtr, 0x0f, + (SAA7196_REG(mtr, 0x0f) & ~0xe0) | 0x20); + SAA7196_WRITE(mtr, 0x22, 0x00); + SAA7196_WRITE(mtr, 0x24, + (SAA7196_REG(mtr, 0x24) | 0x0c)); + SAA7196_WRITE(mtr, 0x26, 0x20); + SAA7196_WRITE(mtr, 0x28, + (SAA7196_REG(mtr, 0x28) & ~0x0c) | 0x04) ; + if(mtr->flags & METEOR_RGB){ + bt254_ntsc(mtr, 0); + } + break; + case METEOR_FMT_AUTOMODE: + mtr->flags = (mtr->flags & ~METEOR_FORM_MASK) | + METEOR_AUTOMODE; + SAA7196_WRITE(mtr, SAA7196_STDC, + (SAA7196_REG(mtr, SAA7196_STDC) & ~0x01)); + SAA7196_WRITE(mtr, 0x0f, + (SAA7196_REG(mtr, 0x0f) & ~0xe0) | 0x80); + break; + default: + return EINVAL; + } + break; + case METEORGFMT: /* get input format */ + *(u_long *)arg = mtr->flags & METEOR_FORM_MASK; + break; + case METEORCAPTUR: + temp = mtr->flags; + switch (*(int *) arg) { + case METEOR_CAP_SINGLE: + if (mtr->bigbuf==0) /* no frame buffer allocated */ + return(ENOMEM); + + if (temp & METEOR_CAP_MASK) + return(EIO); /* already capturing */ + + start_capture(mtr, METEOR_SINGLE); + + /* wait for capture to complete */ + error=tsleep((caddr_t)mtr, METPRI, "capturing", 0); + if(error) + printf("meteor%d: ioctl: tsleep error %d\n", + unit, error); + mtr->flags &= ~(METEOR_SINGLE|METEOR_WANT_MASK); + break; + case METEOR_CAP_CONTINOUS: + if (mtr->bigbuf==0) /* no frame buffer allocated */ + return(ENOMEM); + + if (temp & METEOR_CAP_MASK) + return(EIO); /* already capturing */ + + start_capture(mtr, METEOR_CONTIN); + + break; + case METEOR_CAP_STOP_CONT: + if (mtr->flags & METEOR_CONTIN) { + /* turn off capture */ + base->cap_cntl = 0x8ff0; + mtr->flags &= ~(METEOR_CONTIN|METEOR_WANT_MASK); + } + break; + + default: + error = EINVAL; + break; + } + break; + case METEORCAPFRM: + frame = (struct meteor_capframe *) arg; + if (!frame) + return(EINVAL); + switch (frame->command) { + case METEOR_CAP_N_FRAMES: + if (mtr->flags & METEOR_CAP_MASK) + return(EIO); + if (mtr->flags & (METEOR_YUV_PLANAR | METEOR_YUV_422)) /* XXX */ + return(EINVAL); /* should fix intr so we allow these */ + if (mtr->bigbuf == 0) + return(ENOMEM); + if ((mtr->frames < 2) || + (frame->lowat < 1 || frame->lowat >= mtr->frames) || + (frame->hiwat < 1 || frame->hiwat >= mtr->frames) || + (frame->lowat > frame->hiwat)) + return(EINVAL); + /* meteor_mem structure is on the page after the data */ + mem = mtr->mem = (struct meteor_mem *) (mtr->bigbuf + + (round_page(mtr->frame_size * mtr->frames))); + mtr->current = 1; + mtr->synch_wait = 0; + mem->num_bufs = mtr->frames; + mem->frame_size= mtr->frame_size; + /* user and kernel change these */ + mem->lowat = frame->lowat; + mem->hiwat = frame->hiwat; + mem->active = 0; + mem->num_active_bufs = 0; + /* Start capture */ + start_capture(mtr, METEOR_SYNCAP); + break; + case METEOR_CAP_STOP_FRAMES: + if (mtr->flags & METEOR_SYNCAP) { + /* turn off capture */ + base->cap_cntl = 0x8ff0; + mtr->flags &= ~(METEOR_SYNCAP|METEOR_WANT_MASK); + } + break; + case METEOR_HALT_N_FRAMES: + if(mtr->flags & METEOR_SYNCAP) { + base->cap_cntl = 0x8ff0; + mtr->flags &= ~(METEOR_WANT_MASK); + } + break; + case METEOR_CONT_N_FRAMES: + if(!(mtr->flags & METEOR_SYNCAP)) { + error = EINVAL; + break; + } + start_capture(mtr, METEOR_SYNCAP); + break; + default: + error = EINVAL; + break; + } + break; + + case METEORSETGEO: + geo = (struct meteor_geomet *) arg; + + /* Either even or odd, if even & odd, then these a zero */ + if((geo->oformat & METEOR_GEO_ODD_ONLY) && + (geo->oformat & METEOR_GEO_EVEN_ONLY)) { + printf("meteor%d: ioctl: Geometry odd or even only.\n", + unit); + return EINVAL; + } + /* set/clear even/odd flags */ + if(geo->oformat & METEOR_GEO_ODD_ONLY) + mtr->flags |= METEOR_ONLY_ODD_FIELDS; + else + mtr->flags &= ~METEOR_ONLY_ODD_FIELDS; + if(geo->oformat & METEOR_GEO_EVEN_ONLY) + mtr->flags |= METEOR_ONLY_EVEN_FIELDS; + else + mtr->flags &= ~METEOR_ONLY_EVEN_FIELDS; + + /* can't change parameters while capturing */ + if (mtr->flags & METEOR_CAP_MASK) + return(EBUSY); + + if ((geo->columns & 0x3fe) != geo->columns) { + printf( + "meteor%d: ioctl: %d: columns too large or not even.\n", + unit, geo->columns); + error = EINVAL; + } + if (((geo->rows & 0x7fe) != geo->rows) || + ((geo->oformat & METEOR_GEO_FIELD_MASK) && + ((geo->rows & 0x3fe) != geo->rows)) ) { + printf( + "meteor%d: ioctl: %d: rows too large or not even.\n", + unit, geo->rows); + error = EINVAL; + } + if (geo->frames > 32) { + printf("meteor%d: ioctl: too many frames.\n", unit); + error = EINVAL; + } + if(error) return error; + + if ((temp=geo->rows * geo->columns * geo->frames * 2) != 0) { + if (geo->oformat & METEOR_GEO_RGB24) temp = temp * 2; + + /* meteor_mem structure for SYNC Capture */ + if (geo->frames > 1) temp += PAGE_SIZE; + + temp = btoc(temp); + if (temp > mtr->alloc_pages +#ifdef METEOR_TEST_VIDEO + && mtr->video.addr == 0 +#endif + ) { + buf = get_meteor_mem(unit, temp*PAGE_SIZE); + if(buf != 0) { + kmem_free(kernel_map, mtr->bigbuf, + (mtr->alloc_pages * PAGE_SIZE)); + mtr->bigbuf = buf; + mtr->alloc_pages = temp; + if(bootverbose) + printf( + "meteor%d: ioctl: Allocating %d bytes\n", + unit, temp*PAGE_SIZE); + } else { + error = ENOMEM; + } + } + } + if(error) return error; + + mtr->rows = geo->rows; + mtr->cols = geo->columns; + mtr->frames = geo->frames; + +#ifdef METEOR_TEST_VIDEO + if(mtr->video.addr) + buf = vtophys(mtr->video.addr); + else +#endif + buf = vtophys(mtr->bigbuf); + + /* set defaults and end of buffer locations */ + base->dma1e = buf; + base->dma2e = buf; + base->dma3e = buf; + base->dma1o = buf; + base->dma2o = buf; + base->dma3o = buf; + base->stride1e = 0; + base->stride2e = 0; + base->stride3e = 0; + base->stride1o = 0; + base->stride2o = 0; + base->stride3o = 0; + /* set end of DMA location, even/odd */ + base->dma_end_e = + base->dma_end_o = buf + mtr->alloc_pages * PAGE_SIZE; + + /* + * Determine if we can use the hardware range detect. + */ + if(mtr->alloc_pages * PAGE_SIZE < RANGE_BOUNDARY && + ((buf & 0xff000000) | base->dma_end_e) == + (buf + mtr->alloc_pages * PAGE_SIZE) ) + mtr->range_enable = 0x8000; + else { + mtr->range_enable = 0x0; + base->dma_end_e = + base->dma_end_o = 0xffffffff; + } + + + switch (geo->oformat & METEOR_GEO_OUTPUT_MASK) { + case 0: /* default */ + case METEOR_GEO_RGB16: + mtr->depth = 2; + mtr->frame_size = mtr->rows * mtr->cols * mtr->depth; + mtr->flags &= ~METEOR_OUTPUT_FMT_MASK; + mtr->flags |= METEOR_RGB16; + temp = mtr->cols * mtr->depth; + /* recal stride and starting point */ + switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) { + case METEOR_ONLY_ODD_FIELDS: + base->dma1o = buf; +#ifdef METEOR_TEST_VIDEO + if(mtr->video.addr && mtr->video.width) + base->stride1o = mtr->video.width-temp; +#endif + SAA7196_WRITE(mtr, 0x20, 0xd0); + break; + case METEOR_ONLY_EVEN_FIELDS: + base->dma1e = buf; +#ifdef METEOR_TEST_VIDEO + if(mtr->video.addr && mtr->video.width) + base->stride1e = mtr->video.width-temp; +#endif + SAA7196_WRITE(mtr, 0x20, 0xf0); + break; + default: /* interlaced even/odd */ + base->dma1e = buf; + base->dma1o = buf + temp; + base->stride1e = base->stride1o = temp; +#ifdef METEOR_TEST_VIDEO + if(mtr->video.addr && mtr->video.width) { + base->dma1o = buf + mtr->video.width; + base->stride1e = base->stride1o = + mtr->video.width - + temp + mtr->video.width; + } +#endif + SAA7196_WRITE(mtr, 0x20, 0x90); + break; + } + base->routee = base->routeo = 0xeeeeee01; + break; + case METEOR_GEO_RGB24: + mtr->depth = 4; + mtr->frame_size = mtr->rows * mtr->cols * mtr->depth; + mtr->flags &= ~METEOR_OUTPUT_FMT_MASK; + mtr->flags |= METEOR_RGB24; + temp = mtr->cols * mtr->depth; + /* recal stride and starting point */ + switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) { + case METEOR_ONLY_ODD_FIELDS: + base->dma1o = buf; +#ifdef METEOR_TEST_VIDEO + if(mtr->video.addr && mtr->video.width) + base->stride1o = mtr->video.width-temp; +#endif + SAA7196_WRITE(mtr, 0x20, 0xd2); + break; + case METEOR_ONLY_EVEN_FIELDS: + base->dma1e = buf; +#ifdef METEOR_TEST_VIDEO + if(mtr->video.addr && mtr->video.width) + base->stride1e = mtr->video.width-temp; +#endif + SAA7196_WRITE(mtr, 0x20, 0xf2); + break; + default: /* interlaced even/odd */ + base->dma1e = buf; + base->dma1o = buf + mtr->cols * mtr->depth; + base->stride1e = base->stride1o = + mtr->cols * mtr->depth; +#ifdef METEOR_TEST_VIDEO + if(mtr->video.addr && mtr->video.width) { + base->dma1o = buf + mtr->video.width; + base->stride1e = base->stride1o = + mtr->video.width - + temp + mtr->video.width; + } +#endif + SAA7196_WRITE(mtr, 0x20, 0x92); + break; + } + base->routee= base->routeo= 0x39393900; + break; + case METEOR_GEO_YUV_PLANAR: + mtr->depth = 2; + temp = mtr->rows * mtr->cols; /* compute frame size */ + mtr->frame_size = temp * mtr->depth; + mtr->flags &= ~METEOR_OUTPUT_FMT_MASK; + mtr->flags |= METEOR_YUV_PLANAR; + /* recal stride and starting point */ + switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) { + case METEOR_ONLY_ODD_FIELDS: + base->dma1o = buf; /* Y Odd */ + base->dma2o = buf + temp; /* U Odd */ + temp >>= 1; + base->dma3o = base->dma2o + temp; /* V Odd */ + SAA7196_WRITE(mtr, 0x20, 0xd1); + break; + case METEOR_ONLY_EVEN_FIELDS: + base->dma1e = buf; /* Y Even */ + base->dma2e = buf + temp; /* U Even */ + temp >>= 1; + base->dma2e= base->dma2e + temp; /* V Even */ + SAA7196_WRITE(mtr, 0x20, 0xf1); + break; + default: /* interlaced even/odd */ + base->dma1e = buf; /* Y Even */ + base->dma2e = buf + temp; /* U Even */ + temp >>= 2; + base->dma3e = base->dma2e + temp; /* V Even */ + base->dma1o = base->dma1e+mtr->cols;/* Y Odd */ + base->dma2o = base->dma3e + temp; /* U Odd */ + base->dma3o = base->dma2o + temp; /* V Odd */ + base->stride1e = base->stride1o = mtr->cols; + SAA7196_WRITE(mtr, 0x20, 0x91); + break; + } + switch (geo->oformat & + (METEOR_GEO_YUV_12 | METEOR_GEO_YUV_9)) { + case METEOR_GEO_YUV_9: + base->routee=base->routeo = 0xaaaaffc3; + break; + case METEOR_GEO_YUV_12: + base->routee=base->routeo = 0xaaaaffc2; + break; + default: + base->routee=base->routeo = 0xaaaaffc1; + break; + } + break; + case METEOR_GEO_YUV_422:/* same as planer, different uv order */ + mtr->depth = 2; + temp = mtr->rows * mtr->cols; /* compute frame size */ + mtr->frame_size = temp * mtr->depth; + mtr->flags &= ~METEOR_OUTPUT_FMT_MASK; + mtr->flags |= METEOR_YUV_422; + switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) { + case METEOR_ONLY_ODD_FIELDS: + base->dma1o = buf; + base->dma2o = buf + temp; + base->dma3o = base->dma2o + (temp >> 1); + SAA7196_WRITE(mtr, 0x20, 0xd1); + break; + case METEOR_ONLY_EVEN_FIELDS: + base->dma1e = buf; + base->dma2e = buf + temp; + base->dma3e = base->dma2e + (temp >> 1); + SAA7196_WRITE(mtr, 0x20, 0xf1); + break; + default: /* interlaced even/odd */ + base->dma1e = buf; /* Y even */ + base->dma2e = buf + temp; /* U even */ + base->dma3e = + base->dma2e + (temp >> 1);/* V even */ + base->dma1o = base->dma1e+mtr->cols;/* Y odd */ + temp = mtr->cols >> 1; + base->dma2o = base->dma2e+temp; /* U odd */ + base->dma3o = base->dma3e+temp; /* V odd */ + base->stride1e = + base->stride1o = mtr->cols; /* Y stride */ + base->stride2e = + base->stride2o = temp; /* U stride */ + base->stride3e = + base->stride3o = temp; /* V stride */ + SAA7196_WRITE(mtr, 0x20, 0x91); + break; + } + switch (geo->oformat & + (METEOR_GEO_YUV_12 | METEOR_GEO_YUV_9)) { + case METEOR_GEO_YUV_9: + base->routee=base->routeo = 0xaaaaffc3; + break; + case METEOR_GEO_YUV_12: + base->routee=base->routeo = 0xaaaaffc2; + break; + default: + base->routee=base->routeo = 0xaaaaffc1; + break; + } + break; + case METEOR_GEO_YUV_PACKED: + mtr->depth = 2; + mtr->frame_size = mtr->rows * mtr->cols * mtr->depth; + mtr->flags &= ~METEOR_OUTPUT_FMT_MASK; + mtr->flags |= METEOR_YUV_PACKED; + /* recal stride and odd starting point */ + switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) { + case METEOR_ONLY_ODD_FIELDS: + base->dma1o = buf; + SAA7196_WRITE(mtr, 0x20, 0xd1); + break; + case METEOR_ONLY_EVEN_FIELDS: + base->dma1e = buf; + SAA7196_WRITE(mtr, 0x20, 0xf1); + break; + default: /* interlaced even/odd */ + base->dma1e = buf; + base->dma1o = buf + mtr->cols * mtr->depth; + base->stride1e = base->stride1o = + mtr->cols * mtr->depth; + SAA7196_WRITE(mtr, 0x20, 0x91); + break; + } + base->routee = base->routeo = 0xeeeeee41; + break; + default: + error = EINVAL; /* invalid argument */ + printf("meteor%d: ioctl: invalid output format\n",unit); + break; + } + /* set cols */ + SAA7196_WRITE(mtr, 0x21, mtr->cols & 0xff); + SAA7196_WRITE(mtr, 0x24, + ((SAA7196_REG(mtr, 0x24) & ~0x03) | + ((mtr->cols >> 8) & 0x03))); + /* set rows */ + if(mtr->flags & METEOR_ONLY_FIELDS_MASK) { + SAA7196_WRITE(mtr, 0x25, ((mtr->rows) & 0xff)); + SAA7196_WRITE(mtr, 0x28, + ((SAA7196_REG(mtr, 0x28) & ~0x03) | + ((mtr->rows >> 8) & 0x03))); + } else { /* Interlaced */ + SAA7196_WRITE(mtr, 0x25, ((mtr->rows >> 1) & 0xff)); + SAA7196_WRITE(mtr, 0x28, + ((SAA7196_REG(mtr, 0x28) & ~0x03) | + ((mtr->rows >> 9) & 0x03))); + } + /* set signed/unsigned chrominance */ + SAA7196_WRITE(mtr, 0x30, (SAA7196_REG(mtr, 0x30) & ~0x10) | + ((geo->oformat&METEOR_GEO_UNSIGNED)?0:0x10)); + break; + case METEORGETGEO: + geo = (struct meteor_geomet *) arg; + geo->rows = mtr->rows; + geo->columns = mtr->cols; + geo->frames = mtr->frames; + geo->oformat = (mtr->flags & METEOR_OUTPUT_FMT_MASK) | + (mtr->flags & METEOR_ONLY_FIELDS_MASK) | + (SAA7196_REG(mtr, 0x30) & 0x10 ? + 0:METEOR_GEO_UNSIGNED); + switch(base->routee & 0xff) { + case 0xc3: + geo->oformat |= METEOR_GEO_YUV_9; + break; + case 0xc2: + geo->oformat |= METEOR_GEO_YUV_12; + break; + default: + break; + } + break; + case METEORSCOUNT: /* (re)set error counts */ + cnt = (struct meteor_counts *) arg; + mtr->fifo_errors = cnt->fifo_errors; + mtr->dma_errors = cnt->dma_errors; + mtr->frames_captured = cnt->frames_captured; + mtr->even_fields_captured = cnt->even_fields_captured; + mtr->odd_fields_captured = cnt->odd_fields_captured; + break; + case METEORGCOUNT: /* get error counts */ + cnt = (struct meteor_counts *) arg; + cnt->fifo_errors = mtr->fifo_errors; + cnt->dma_errors = mtr->dma_errors; + cnt->frames_captured = mtr->frames_captured; + cnt->even_fields_captured = mtr->even_fields_captured; + cnt->odd_fields_captured = mtr->odd_fields_captured; + break; + default: + printf("meteor%d: ioctl: invalid ioctl request\n", unit); + error = ENOTTY; + break; + } + return(error); +} + +int +meteor_mmap(dev_t dev, vm_offset_t offset, int nprot) +{ + + int unit; + meteor_reg_t *mtr; + + unit = UNIT(minor(dev)); + if (unit >= NMETEOR) /* at this point could this happen? */ + return(-1); + + mtr = &(meteor[unit]); + + + if(nprot & PROT_EXEC) + return -1; + + if(offset >= mtr->alloc_pages * PAGE_SIZE) + return -1; + + return i386_btop(vtophys(mtr->bigbuf) + offset); +} +#endif diff --git a/sys/pci/meteor_reg.h b/sys/pci/meteor_reg.h new file mode 100644 index 0000000..a1f5ecc --- /dev/null +++ b/sys/pci/meteor_reg.h @@ -0,0 +1,246 @@ +/* + * Copyright (c) 1995 Mark Tinguely and Jim Lowe + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Mark Tinguely and Jim Lowe + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef PCI_LATENCY_TIMER +#define PCI_LATENCY_TIMER 0x0c /* pci timer register */ +#endif + +/* + * Definitions for the Philips SAA7116 digital video to pci interface. + */ +#define SAA7116_PHILIPS_ID 0x12238086ul +#define SAA7116_I2C_WRITE 0x00 +#define SAA7116_I2C_READ 0x01 +#define SAA7116_IIC_NEW_CYCLE 0x1000000L +#define SAA7116_IIC_DIRECT_TRANSFER_ABORTED 0x0000200L + +typedef volatile u_int mreg_t; +struct saa7116_regs { + mreg_t dma1e; /* Base address for even field dma chn 1 */ + mreg_t dma2e; /* Base address for even field dma chn 2 */ + mreg_t dma3e; /* Base address for even field dma chn 3 */ + mreg_t dma1o; /* Base address for odd field dma chn 1 */ + mreg_t dma2o; /* Base address for odd field dma chn 2 */ + mreg_t dma3o; /* Base address for odd field dma chn 3 */ + mreg_t stride1e; /* Address stride for even field dma chn 1 */ + mreg_t stride2e; /* Address stride for even field dma chn 2 */ + mreg_t stride3e; /* Address stride for even field dma chn 3 */ + mreg_t stride1o; /* Address stride for odd field dma chn 1 */ + mreg_t stride2o; /* Address stride for odd field dma chn 2 */ + mreg_t stride3o; /* Address stride for odd field dma chn 3 */ + mreg_t routee; /* Route/mode even */ + mreg_t routeo; /* Route/mode odd */ + mreg_t fifo_t; /* FIFO trigger for PCI int */ + mreg_t field_t; /* Field toggle */ + mreg_t cap_cntl; /* Capture control */ + mreg_t retry_wait_cnt; /* Clks for master to wait after disconnect */ + mreg_t irq_stat; /* IRQ mask and status reg */ + mreg_t fme; /* Field Mask even */ + mreg_t fmo; /* Field mask odd */ + mreg_t fml; /* Field mask length */ + mreg_t fifo_t_err; /* FIFO almost empty/almost full ptrs */ + mreg_t i2c_phase; /* i2c phase register */ + mreg_t i2c_read; /* i2c read register */ + mreg_t i2c_write; /* i2c write register */ + mreg_t i2c_auto_a_e; /* i2c auto register a, even */ + mreg_t i2c_auto_b_e; /* i2c auto register b, even */ + mreg_t i2c_auto_c_e; /* i2c auto register c, even */ + mreg_t i2c_auto_d_e; /* i2c auto register d, even */ + mreg_t i2c_auto_a_o; /* i2c auto register a, odd */ + mreg_t i2c_auto_b_o; /* i2c auto register b, odd */ + mreg_t i2c_auto_c_o; /* i2c auto register c, odd */ + mreg_t i2c_auto_d_o; /* i2c auto register d, odd */ + mreg_t i2c_auto_enable;/* enable above auto registers */ + mreg_t dma_end_e; /* DMA end even (range) */ + mreg_t dma_end_o; /* DMA end odd (range) */ +}; + + +/* + * Definitions for the Philips SAA7196 digital video decoder, + * scalar, and clock generator circuit (DESCpro). + */ +#define NUM_SAA7196_I2C_REGS 49 +#define SAA7196_I2C_ADDR 0x40 +#define SAA7196_WRITE(mtr, reg, data) \ + i2c_write(mtr, SAA7196_I2C_ADDR, SAA7116_I2C_WRITE, reg, data), \ + mtr->saa7196_i2c[reg] = data +#define SAA7196_REG(mtr, reg) mtr->saa7196_i2c[reg] +#define SAA7196_READ(mtr) \ + i2c_write(mtr, SAA7196_I2C_ADDR, SAA7116_I2C_READ, 0x0, 0x0) + +#define SAA7196_IDEL 0x00 /* Increment delay */ +#define SAA7196_HSB5 0x01 /* H-sync begin; 50 hz */ +#define SAA7196_HSS5 0x02 /* H-sync stop; 50 hz */ +#define SAA7196_HCB5 0x03 /* H-clamp begin; 50 hz */ +#define SAA7196_HCS5 0x04 /* H-clamp stop; 50 hz */ +#define SAA7196_HSP5 0x05 /* H-sync after PHI1; 50 hz */ +#define SAA7196_LUMC 0x06 /* Luminance control */ +#define SAA7196_HUEC 0x07 /* Hue control */ +#define SAA7196_CKTQ 0x08 /* Colour Killer Threshold QAM (PAL, NTSC) */ +#define SAA7196_CKTS 0x09 /* Colour Killer Threshold SECAM */ +#define SAA7196_PALS 0x0a /* PAL switch sensitivity */ +#define SAA7196_SECAMS 0x0b /* SECAM switch sensitivity */ +#define SAA7196_CGAINC 0x0c /* Chroma gain control */ +#define SAA7196_STDC 0x0d /* Standard/Mode control */ +#define SAA7196_IOCC 0x0e /* I/O and Clock Control */ +#define SAA7196_CTRL1 0x0f /* Control #1 */ +#define SAA7196_CTRL2 0x10 /* Control #2 */ +#define SAA7196_CGAINR 0x11 /* Chroma Gain Reference */ +#define SAA7196_CSAT 0x12 /* Chroma Saturation */ +#define SAA7196_CONT 0x13 /* Luminance Contrast */ +#define SAA7196_HSB6 0x14 /* H-sync begin; 60 hz */ +#define SAA7196_HSS6 0x15 /* H-sync stop; 60 hz */ +#define SAA7196_HCB6 0x16 /* H-clamp begin; 60 hz */ +#define SAA7196_HCS6 0x17 /* H-clamp stop; 60 hz */ +#define SAA7196_HSP6 0x18 /* H-sync after PHI1; 60 hz */ +#define SAA7196_BRIG 0x19 /* Luminance Brightness */ +#define SAA7196_FMTS 0x20 /* Formats and sequence */ +#define SAA7196_OUTPIX 0x21 /* Output data pixel/line */ +#define SAA7196_INPIX 0x22 /* Input data pixel/line */ +#define SAA7196_HWS 0x23 /* Horiz. window start */ +#define SAA7196_HFILT 0x24 /* Horiz. filter */ +#define SAA7196_OUTLINE 0x25 /* Output data lines/field */ +#define SAA7196_INLINE 0x26 /* Input data lines/field */ +#define SAA7196_VWS 0x27 /* Vertical window start */ +#define SAA7196_VYP 0x28 /* AFS/vertical Y processing */ +#define SAA7196_VBS 0x29 /* Vertical Bypass start */ +#define SAA7196_VBCNT 0x2a /* Vertical Bypass count */ +#define SAA7196_VBP 0x2b /* veritcal Bypass Polarity */ +#define SAA7196_VLOW 0x2c /* Colour-keying lower V limit */ +#define SAA7196_VHIGH 0x2d /* Colour-keying upper V limit */ +#define SAA7196_ULOW 0x2e /* Colour-keying lower U limit */ +#define SAA7196_UHIGH 0x2f /* Colour-keying upper U limit */ +#define SAA7196_DPATH 0x30 /* Data path setting */ + +/* + * Defines for the PCF8574. + */ +#define NUM_PCF8574_I2C_REGS 2 +#define PCF8574_CTRL_I2C_ADDR 0x70 +#define PCF8574_DATA_I2C_ADDR 0x72 +#define PCF8574_CTRL_WRITE(mtr, data) \ + i2c_write(mtr, PCF8574_CTRL_I2C_ADDR, SAA7116_I2C_WRITE, data, data), \ + mtr->pcf_i2c[0] = data +#define PCF8574_DATA_WRITE(mtr, data) \ + i2c_write(mtr, PCF8574_DATA_I2C_ADDR, SAA7116_I2C_WRITE, data, data), \ + mtr->pcf_i2c[1] = data +#define PCF8574_CTRL_REG(mtr) mtr->pcf_i2c[0] +#define PCF8574_DATA_REG(mtr) mtr->pcf_i2c[1] + + +/* + * Defines for the BT254. + */ +#define NUM_BT254_REGS 7 + +#define BT254_COMMAND 0 +#define BT254_IOUT1 1 +#define BT254_IOUT2 2 +#define BT254_IOUT3 3 +#define BT254_IOUT4 4 +#define BT254_IOUT5 5 +#define BT254_IOUT6 6 + +/* + * Meteor info structure, one per meteor card installed. + */ +typedef struct meteor_softc { + struct saa7116_regs *base; /* saa7116 register virtual address */ + vm_offset_t phys_base; /* saa7116 register physical address */ + pcici_t tag; /* PCI tag, for doing PCI commands */ + vm_offset_t bigbuf; /* buffer that holds the captured image */ + int alloc_pages; /* number of pages in bigbuf */ + struct proc *proc; /* process to receive raised signal */ + int signal; /* signal to send to process */ +#define METEOR_SIG_MODE_MASK 0xffff0000 +#define METEOR_SIG_FIELD_MODE 0x00010000 +#define METEOR_SIG_FRAME_MODE 0x00000000 + struct meteor_mem *mem; /* used to control sync. multi-frame output */ + u_long synch_wait; /* wait for free buffer before continuing */ + short current; /* frame number in buffer (1-frames) */ + short rows; /* number of rows in a frame */ + short cols; /* number of columns in a frame */ + short depth; /* number of byte per pixel */ + short frames; /* number of frames allocated */ + int frame_size; /* number of bytes in a frame */ + u_long fifo_errors; /* number of fifo capture errors since open */ + u_long dma_errors; /* number of DMA capture errors since open */ + u_long frames_captured;/* number of frames captured since open */ + u_long even_fields_captured; /* number of even fields captured */ + u_long odd_fields_captured; /* number of odd fields captured */ + u_long range_enable; /* enable range checking ?? */ + unsigned flags; +#define METEOR_INITALIZED 0x00000001 +#define METEOR_OPEN 0x00000002 +#define METEOR_MMAP 0x00000004 +#define METEOR_INTR 0x00000008 +#define METEOR_READ 0x00000010 /* XXX never gets referenced */ +#define METEOR_SINGLE 0x00000020 /* get single frame */ +#define METEOR_CONTIN 0x00000040 /* continuously get frames */ +#define METEOR_SYNCAP 0x00000080 /* synchronously get frames */ +#define METEOR_CAP_MASK 0x000000f0 +#define METEOR_NTSC 0x00000100 +#define METEOR_PAL 0x00000200 +#define METEOR_SECAM 0x00000400 +#define METEOR_AUTOMODE 0x00000800 +#define METEOR_FORM_MASK 0x00000f00 +#define METEOR_DEV0 0x00001000 +#define METEOR_DEV1 0x00002000 +#define METEOR_DEV2 0x00004000 +#define METEOR_DEV3 0x00008000 +#define METEOR_DEV_SVIDEO 0x00006000 +#define METEOR_DEV_RGB 0x0000a000 +#define METEOR_DEV_MASK 0x0000f000 +#define METEOR_RGB16 0x00010000 +#define METEOR_RGB24 0x00020000 +#define METEOR_YUV_PACKED 0x00040000 +#define METEOR_YUV_PLANAR 0x00080000 +#define METEOR_WANT_EVEN 0x00100000 /* want even frame */ +#define METEOR_WANT_ODD 0x00200000 /* want odd frame */ +#define METEOR_WANT_MASK 0x00300000 +#define METEOR_ONLY_EVEN_FIELDS 0x01000000 +#define METEOR_ONLY_ODD_FIELDS 0x02000000 +#define METEOR_ONLY_FIELDS_MASK 0x03000000 +#define METEOR_YUV_422 0x04000000 +#define METEOR_OUTPUT_FMT_MASK 0x040f0000 +#define METEOR_WANT_TS 0x08000000 /* time-stamp a frame */ +#define METEOR_RGB 0x20000000 /* meteor rgb unit */ +#define METEOR_FIELD_MODE 0x80000000 + u_char saa7196_i2c[NUM_SAA7196_I2C_REGS]; /* saa7196 register values */ + u_char pcf_i2c[NUM_PCF8574_I2C_REGS]; /* PCF8574 register values */ + u_char bt254_reg[NUM_BT254_REGS]; /* BT254 register values */ + u_short fps; /* frames per second */ +#ifdef METEOR_TEST_VIDEO + struct meteor_video video; +#endif +} meteor_reg_t; diff --git a/sys/pci/ncr.c b/sys/pci/ncr.c new file mode 100644 index 0000000..8d6fb90 --- /dev/null +++ b/sys/pci/ncr.c @@ -0,0 +1,7163 @@ +/************************************************************************** +** +** $FreeBSD$ +** +** Device driver for the NCR 53C8XX PCI-SCSI-Controller Family. +** +**------------------------------------------------------------------------- +** +** Written for 386bsd and FreeBSD by +** Wolfgang Stanglmeier <wolf@cologne.de> +** Stefan Esser <se@mi.Uni-Koeln.de> +** +**------------------------------------------------------------------------- +** +** Copyright (c) 1994 Wolfgang Stanglmeier. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** +*************************************************************************** +*/ + +#define NCR_DATE "pl30 98/1/1" + +#define NCR_VERSION (2) +#define MAX_UNITS (16) + +#define NCR_GETCC_WITHMSG + +#if defined (__FreeBSD__) && defined(_KERNEL) +#include "opt_ncr.h" +#endif + +/*========================================================== +** +** Configuration and Debugging +** +** May be overwritten in <arch/conf/xxxx> +** +**========================================================== +*/ + +/* +** SCSI address of this device. +** The boot routines should have set it. +** If not, use this. +*/ + +#ifndef SCSI_NCR_MYADDR +#define SCSI_NCR_MYADDR (7) +#endif /* SCSI_NCR_MYADDR */ + +/* +** The default synchronous period factor +** (0=asynchronous) +** If maximum synchronous frequency is defined, use it instead. +*/ + +#ifndef SCSI_NCR_MAX_SYNC + +#ifndef SCSI_NCR_DFLT_SYNC +#define SCSI_NCR_DFLT_SYNC (12) +#endif /* SCSI_NCR_DFLT_SYNC */ + +#else + +#if SCSI_NCR_MAX_SYNC == 0 +#define SCSI_NCR_DFLT_SYNC 0 +#else +#define SCSI_NCR_DFLT_SYNC (250000 / SCSI_NCR_MAX_SYNC) +#endif + +#endif + +/* +** The minimal asynchronous pre-scaler period (ns) +** Shall be 40. +*/ + +#ifndef SCSI_NCR_MIN_ASYNC +#define SCSI_NCR_MIN_ASYNC (40) +#endif /* SCSI_NCR_MIN_ASYNC */ + +/* +** The maximal bus with (in log2 byte) +** (0=8 bit, 1=16 bit) +*/ + +#ifndef SCSI_NCR_MAX_WIDE +#define SCSI_NCR_MAX_WIDE (1) +#endif /* SCSI_NCR_MAX_WIDE */ + +/*========================================================== +** +** Configuration and Debugging +** +**========================================================== +*/ + +/* +** Number of targets supported by the driver. +** n permits target numbers 0..n-1. +** Default is 7, meaning targets #0..#6. +** #7 .. is myself. +*/ + +#define MAX_TARGET (16) + +/* +** Number of logic units supported by the driver. +** n enables logic unit numbers 0..n-1. +** The common SCSI devices require only +** one lun, so take 1 as the default. +*/ + +#ifndef MAX_LUN +#define MAX_LUN (8) +#endif /* MAX_LUN */ + +/* +** The maximum number of jobs scheduled for starting. +** There should be one slot per target, and one slot +** for each tag of each target in use. +*/ + +#define MAX_START (256) + +/* +** The maximum number of segments a transfer is split into. +*/ + +#define MAX_SCATTER (33) + +/* +** The maximum transfer length (should be >= 64k). +** MUST NOT be greater than (MAX_SCATTER-1) * PAGE_SIZE. +*/ + +#define MAX_SIZE ((MAX_SCATTER-1) * (long) PAGE_SIZE) + +/* +** other +*/ + +#define NCR_SNOOP_TIMEOUT (1000000) + +/*========================================================== +** +** Include files +** +**========================================================== +*/ + +#include <sys/param.h> +#include <sys/time.h> + +#ifdef _KERNEL +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/sysctl.h> +#include <sys/bus.h> +#include <machine/md_var.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> +#include <vm/vm.h> +#include <vm/pmap.h> +#include <vm/vm_extern.h> +#endif + +#include <pci/pcivar.h> +#include <pci/pcireg.h> +#include <pci/ncrreg.h> + +#include <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/cam_sim.h> +#include <cam/cam_xpt_sim.h> +#include <cam/cam_debug.h> + +#include <cam/scsi/scsi_all.h> +#include <cam/scsi/scsi_message.h> + +/*========================================================== +** +** Debugging tags +** +**========================================================== +*/ + +#define DEBUG_ALLOC (0x0001) +#define DEBUG_PHASE (0x0002) +#define DEBUG_POLL (0x0004) +#define DEBUG_QUEUE (0x0008) +#define DEBUG_RESULT (0x0010) +#define DEBUG_SCATTER (0x0020) +#define DEBUG_SCRIPT (0x0040) +#define DEBUG_TINY (0x0080) +#define DEBUG_TIMING (0x0100) +#define DEBUG_NEGO (0x0200) +#define DEBUG_TAGS (0x0400) +#define DEBUG_FREEZE (0x0800) +#define DEBUG_RESTART (0x1000) + +/* +** Enable/Disable debug messages. +** Can be changed at runtime too. +*/ +#ifdef SCSI_NCR_DEBUG + #define DEBUG_FLAGS ncr_debug +#else /* SCSI_NCR_DEBUG */ + #define SCSI_NCR_DEBUG 0 + #define DEBUG_FLAGS 0 +#endif /* SCSI_NCR_DEBUG */ + + + +/*========================================================== +** +** assert () +** +**========================================================== +** +** modified copy from 386bsd:/usr/include/sys/assert.h +** +**---------------------------------------------------------- +*/ + +#ifdef DIAGNOSTIC +#define assert(expression) { \ + if (!(expression)) { \ + (void)printf("assertion \"%s\" failed: " \ + "file \"%s\", line %d\n", \ + #expression, __FILE__, __LINE__); \ + Debugger(""); \ + } \ +} +#else +#define assert(expression) { \ + if (!(expression)) { \ + (void)printf("assertion \"%s\" failed: " \ + "file \"%s\", line %d\n", \ + #expression, __FILE__, __LINE__); \ + } \ +} +#endif + +/*========================================================== +** +** Access to the controller chip. +** +**========================================================== +*/ + +#ifdef __alpha__ +/* XXX */ +#undef vtophys +#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) +#endif + +#define INB(r) bus_space_read_1(np->bst, np->bsh, offsetof(struct ncr_reg, r)) +#define INW(r) bus_space_read_2(np->bst, np->bsh, offsetof(struct ncr_reg, r)) +#define INL(r) bus_space_read_4(np->bst, np->bsh, offsetof(struct ncr_reg, r)) + +#define OUTB(r, val) bus_space_write_1(np->bst, np->bsh, \ + offsetof(struct ncr_reg, r), val) +#define OUTW(r, val) bus_space_write_2(np->bst, np->bsh, \ + offsetof(struct ncr_reg, r), val) +#define OUTL(r, val) bus_space_write_4(np->bst, np->bsh, \ + offsetof(struct ncr_reg, r), val) +#define OUTL_OFF(o, val) bus_space_write_4(np->bst, np->bsh, o, val) + +#define INB_OFF(o) bus_space_read_1(np->bst, np->bsh, o) +#define INW_OFF(o) bus_space_read_2(np->bst, np->bsh, o) +#define INL_OFF(o) bus_space_read_4(np->bst, np->bsh, o) + +#define READSCRIPT_OFF(base, off) \ + (base ? *((volatile u_int32_t *)((volatile char *)base + (off))) : \ + bus_space_read_4(np->bst2, np->bsh2, off)) + +#define WRITESCRIPT_OFF(base, off, val) \ + do { \ + if (base) \ + *((volatile u_int32_t *) \ + ((volatile char *)base + (off))) = (val); \ + else \ + bus_space_write_4(np->bst2, np->bsh2, off, val); \ + } while (0) + +#define READSCRIPT(r) \ + READSCRIPT_OFF(np->script, offsetof(struct script, r)) + +#define WRITESCRIPT(r, val) \ + WRITESCRIPT_OFF(np->script, offsetof(struct script, r), val) + +/* +** Set bit field ON, OFF +*/ + +#define OUTONB(r, m) OUTB(r, INB(r) | (m)) +#define OUTOFFB(r, m) OUTB(r, INB(r) & ~(m)) +#define OUTONW(r, m) OUTW(r, INW(r) | (m)) +#define OUTOFFW(r, m) OUTW(r, INW(r) & ~(m)) +#define OUTONL(r, m) OUTL(r, INL(r) | (m)) +#define OUTOFFL(r, m) OUTL(r, INL(r) & ~(m)) + +/*========================================================== +** +** Command control block states. +** +**========================================================== +*/ + +#define HS_IDLE (0) +#define HS_BUSY (1) +#define HS_NEGOTIATE (2) /* sync/wide data transfer*/ +#define HS_DISCONNECT (3) /* Disconnected by target */ + +#define HS_COMPLETE (4) +#define HS_SEL_TIMEOUT (5) /* Selection timeout */ +#define HS_RESET (6) /* SCSI reset */ +#define HS_ABORTED (7) /* Transfer aborted */ +#define HS_TIMEOUT (8) /* Software timeout */ +#define HS_FAIL (9) /* SCSI or PCI bus errors */ +#define HS_UNEXPECTED (10) /* Unexpected disconnect */ +#define HS_STALL (11) /* QUEUE FULL or BUSY */ + +#define HS_DONEMASK (0xfc) + +/*========================================================== +** +** Software Interrupt Codes +** +**========================================================== +*/ + +#define SIR_SENSE_RESTART (1) +#define SIR_SENSE_FAILED (2) +#define SIR_STALL_RESTART (3) +#define SIR_STALL_QUEUE (4) +#define SIR_NEGO_SYNC (5) +#define SIR_NEGO_WIDE (6) +#define SIR_NEGO_FAILED (7) +#define SIR_NEGO_PROTO (8) +#define SIR_REJECT_RECEIVED (9) +#define SIR_REJECT_SENT (10) +#define SIR_IGN_RESIDUE (11) +#define SIR_MISSING_SAVE (12) +#define SIR_MAX (12) + +/*========================================================== +** +** Extended error codes. +** xerr_status field of struct nccb. +** +**========================================================== +*/ + +#define XE_OK (0) +#define XE_EXTRA_DATA (1) /* unexpected data phase */ +#define XE_BAD_PHASE (2) /* illegal phase (4/5) */ + +/*========================================================== +** +** Negotiation status. +** nego_status field of struct nccb. +** +**========================================================== +*/ + +#define NS_SYNC (1) +#define NS_WIDE (2) + +/*========================================================== +** +** XXX These are no longer used. Remove once the +** script is updated. +** "Special features" of targets. +** quirks field of struct tcb. +** actualquirks field of struct nccb. +** +**========================================================== +*/ + +#define QUIRK_AUTOSAVE (0x01) +#define QUIRK_NOMSG (0x02) +#define QUIRK_NOSYNC (0x10) +#define QUIRK_NOWIDE16 (0x20) +#define QUIRK_NOTAGS (0x40) +#define QUIRK_UPDATE (0x80) + +/*========================================================== +** +** Misc. +** +**========================================================== +*/ + +#define CCB_MAGIC (0xf2691ad2) +#define MAX_TAGS (32) /* hard limit */ + +/*========================================================== +** +** OS dependencies. +** +**========================================================== +*/ + +#define PRINT_ADDR(ccb) xpt_print_path((ccb)->ccb_h.path) + +/*========================================================== +** +** Declaration of structs. +** +**========================================================== +*/ + +struct tcb; +struct lcb; +struct nccb; +struct ncb; +struct script; + +typedef struct ncb * ncb_p; +typedef struct tcb * tcb_p; +typedef struct lcb * lcb_p; +typedef struct nccb * nccb_p; + +struct link { + ncrcmd l_cmd; + ncrcmd l_paddr; +}; + +struct usrcmd { + u_long target; + u_long lun; + u_long data; + u_long cmd; +}; + +#define UC_SETSYNC 10 +#define UC_SETTAGS 11 +#define UC_SETDEBUG 12 +#define UC_SETORDER 13 +#define UC_SETWIDE 14 +#define UC_SETFLAG 15 + +#define UF_TRACE (0x01) + +/*--------------------------------------- +** +** Timestamps for profiling +** +**--------------------------------------- +*/ + +/* Type of the kernel variable `ticks'. XXX should be declared with the var. */ +typedef int ticks_t; + +struct tstamp { + ticks_t start; + ticks_t end; + ticks_t select; + ticks_t command; + ticks_t data; + ticks_t status; + ticks_t disconnect; +}; + +/* +** profiling data (per device) +*/ + +struct profile { + u_long num_trans; + u_long num_bytes; + u_long num_disc; + u_long num_break; + u_long num_int; + u_long num_fly; + u_long ms_setup; + u_long ms_data; + u_long ms_disc; + u_long ms_post; +}; + +/*========================================================== +** +** Declaration of structs: target control block +** +**========================================================== +*/ + +#define NCR_TRANS_CUR 0x01 /* Modify current neogtiation status */ +#define NCR_TRANS_ACTIVE 0x03 /* Assume this is the active target */ +#define NCR_TRANS_GOAL 0x04 /* Modify negotiation goal */ +#define NCR_TRANS_USER 0x08 /* Modify user negotiation settings */ + +struct ncr_transinfo { + u_int8_t width; + u_int8_t period; + u_int8_t offset; +}; + +struct ncr_target_tinfo { + /* Hardware version of our sync settings */ + u_int8_t disc_tag; +#define NCR_CUR_DISCENB 0x01 +#define NCR_CUR_TAGENB 0x02 +#define NCR_USR_DISCENB 0x04 +#define NCR_USR_TAGENB 0x08 + u_int8_t sval; + struct ncr_transinfo current; + struct ncr_transinfo goal; + struct ncr_transinfo user; + /* Hardware version of our wide settings */ + u_int8_t wval; +}; + +struct tcb { + /* + ** during reselection the ncr jumps to this point + ** with SFBR set to the encoded target number + ** with bit 7 set. + ** if it's not this target, jump to the next. + ** + ** JUMP IF (SFBR != #target#) + ** @(next tcb) + */ + + struct link jump_tcb; + + /* + ** load the actual values for the sxfer and the scntl3 + ** register (sync/wide mode). + ** + ** SCR_COPY (1); + ** @(sval field of this tcb) + ** @(sxfer register) + ** SCR_COPY (1); + ** @(wval field of this tcb) + ** @(scntl3 register) + */ + + ncrcmd getscr[6]; + + /* + ** if next message is "identify" + ** then load the message to SFBR, + ** else load 0 to SFBR. + ** + ** CALL + ** <RESEL_LUN> + */ + + struct link call_lun; + + /* + ** now look for the right lun. + ** + ** JUMP + ** @(first nccb of this lun) + */ + + struct link jump_lcb; + + /* + ** pointer to interrupted getcc nccb + */ + + nccb_p hold_cp; + + /* + ** pointer to nccb used for negotiating. + ** Avoid to start a nego for all queued commands + ** when tagged command queuing is enabled. + */ + + nccb_p nego_cp; + + /* + ** statistical data + */ + + u_long transfers; + u_long bytes; + + /* + ** user settable limits for sync transfer + ** and tagged commands. + */ + + struct ncr_target_tinfo tinfo; + + /* + ** the lcb's of this tcb + */ + + lcb_p lp[MAX_LUN]; +}; + +/*========================================================== +** +** Declaration of structs: lun control block +** +**========================================================== +*/ + +struct lcb { + /* + ** during reselection the ncr jumps to this point + ** with SFBR set to the "Identify" message. + ** if it's not this lun, jump to the next. + ** + ** JUMP IF (SFBR != #lun#) + ** @(next lcb of this target) + */ + + struct link jump_lcb; + + /* + ** if next message is "simple tag", + ** then load the tag to SFBR, + ** else load 0 to SFBR. + ** + ** CALL + ** <RESEL_TAG> + */ + + struct link call_tag; + + /* + ** now look for the right nccb. + ** + ** JUMP + ** @(first nccb of this lun) + */ + + struct link jump_nccb; + + /* + ** start of the nccb chain + */ + + nccb_p next_nccb; + + /* + ** Control of tagged queueing + */ + + u_char reqnccbs; + u_char reqlink; + u_char actlink; + u_char usetags; + u_char lasttag; +}; + +/*========================================================== +** +** Declaration of structs: COMMAND control block +** +**========================================================== +** +** This substructure is copied from the nccb to a +** global address after selection (or reselection) +** and copied back before disconnect. +** +** These fields are accessible to the script processor. +** +**---------------------------------------------------------- +*/ + +struct head { + /* + ** Execution of a nccb starts at this point. + ** It's a jump to the "SELECT" label + ** of the script. + ** + ** After successful selection the script + ** processor overwrites it with a jump to + ** the IDLE label of the script. + */ + + struct link launch; + + /* + ** Saved data pointer. + ** Points to the position in the script + ** responsible for the actual transfer + ** of data. + ** It's written after reception of a + ** "SAVE_DATA_POINTER" message. + ** The goalpointer points after + ** the last transfer command. + */ + + u_int32_t savep; + u_int32_t lastp; + u_int32_t goalp; + + /* + ** The virtual address of the nccb + ** containing this header. + */ + + nccb_p cp; + + /* + ** space for some timestamps to gather + ** profiling data about devices and this driver. + */ + + struct tstamp stamp; + + /* + ** status fields. + */ + + u_char status[8]; +}; + +/* +** The status bytes are used by the host and the script processor. +** +** The first four byte are copied to the scratchb register +** (declared as scr0..scr3 in ncr_reg.h) just after the select/reselect, +** and copied back just after disconnecting. +** Inside the script the XX_REG are used. +** +** The last four bytes are used inside the script by "COPY" commands. +** Because source and destination must have the same alignment +** in a longword, the fields HAVE to be at the choosen offsets. +** xerr_st (4) 0 (0x34) scratcha +** sync_st (5) 1 (0x05) sxfer +** wide_st (7) 3 (0x03) scntl3 +*/ + +/* +** First four bytes (script) +*/ +#define QU_REG scr0 +#define HS_REG scr1 +#define HS_PRT nc_scr1 +#define SS_REG scr2 +#define PS_REG scr3 + +/* +** First four bytes (host) +*/ +#define actualquirks phys.header.status[0] +#define host_status phys.header.status[1] +#define s_status phys.header.status[2] +#define parity_status phys.header.status[3] + +/* +** Last four bytes (script) +*/ +#define xerr_st header.status[4] /* MUST be ==0 mod 4 */ +#define sync_st header.status[5] /* MUST be ==1 mod 4 */ +#define nego_st header.status[6] +#define wide_st header.status[7] /* MUST be ==3 mod 4 */ + +/* +** Last four bytes (host) +*/ +#define xerr_status phys.xerr_st +#define sync_status phys.sync_st +#define nego_status phys.nego_st +#define wide_status phys.wide_st + +/*========================================================== +** +** Declaration of structs: Data structure block +** +**========================================================== +** +** During execution of a nccb by the script processor, +** the DSA (data structure address) register points +** to this substructure of the nccb. +** This substructure contains the header with +** the script-processor-changable data and +** data blocks for the indirect move commands. +** +**---------------------------------------------------------- +*/ + +struct dsb { + + /* + ** Header. + ** Has to be the first entry, + ** because it's jumped to by the + ** script processor + */ + + struct head header; + + /* + ** Table data for Script + */ + + struct scr_tblsel select; + struct scr_tblmove smsg ; + struct scr_tblmove smsg2 ; + struct scr_tblmove cmd ; + struct scr_tblmove scmd ; + struct scr_tblmove sense ; + struct scr_tblmove data [MAX_SCATTER]; +}; + +/*========================================================== +** +** Declaration of structs: Command control block. +** +**========================================================== +** +** During execution of a nccb by the script processor, +** the DSA (data structure address) register points +** to this substructure of the nccb. +** This substructure contains the header with +** the script-processor-changable data and then +** data blocks for the indirect move commands. +** +**---------------------------------------------------------- +*/ + + +struct nccb { + /* + ** This filler ensures that the global header is + ** cache line size aligned. + */ + ncrcmd filler[4]; + + /* + ** during reselection the ncr jumps to this point. + ** If a "SIMPLE_TAG" message was received, + ** then SFBR is set to the tag. + ** else SFBR is set to 0 + ** If looking for another tag, jump to the next nccb. + ** + ** JUMP IF (SFBR != #TAG#) + ** @(next nccb of this lun) + */ + + struct link jump_nccb; + + /* + ** After execution of this call, the return address + ** (in the TEMP register) points to the following + ** data structure block. + ** So copy it to the DSA register, and start + ** processing of this data structure. + ** + ** CALL + ** <RESEL_TMP> + */ + + struct link call_tmp; + + /* + ** This is the data structure which is + ** to be executed by the script processor. + */ + + struct dsb phys; + + /* + ** If a data transfer phase is terminated too early + ** (after reception of a message (i.e. DISCONNECT)), + ** we have to prepare a mini script to transfer + ** the rest of the data. + */ + + ncrcmd patch[8]; + + /* + ** The general SCSI driver provides a + ** pointer to a control block. + */ + + union ccb *ccb; + + /* + ** We prepare a message to be sent after selection, + ** and a second one to be sent after getcc selection. + ** Contents are IDENTIFY and SIMPLE_TAG. + ** While negotiating sync or wide transfer, + ** a SDTM or WDTM message is appended. + */ + + u_char scsi_smsg [8]; + u_char scsi_smsg2[8]; + + /* + ** Lock this nccb. + ** Flag is used while looking for a free nccb. + */ + + u_long magic; + + /* + ** Physical address of this instance of nccb + */ + + u_long p_nccb; + + /* + ** Completion time out for this job. + ** It's set to time of start + allowed number of seconds. + */ + + time_t tlimit; + + /* + ** All nccbs of one hostadapter are chained. + */ + + nccb_p link_nccb; + + /* + ** All nccbs of one target/lun are chained. + */ + + nccb_p next_nccb; + + /* + ** Sense command + */ + + u_char sensecmd[6]; + + /* + ** Tag for this transfer. + ** It's patched into jump_nccb. + ** If it's not zero, a SIMPLE_TAG + ** message is included in smsg. + */ + + u_char tag; +}; + +#define CCB_PHYS(cp,lbl) (cp->p_nccb + offsetof(struct nccb, lbl)) + +/*========================================================== +** +** Declaration of structs: NCR device descriptor +** +**========================================================== +*/ + +struct ncb { + /* + ** The global header. + ** Accessible to both the host and the + ** script-processor. + ** We assume it is cache line size aligned. + */ + struct head header; + + int unit; + + /*----------------------------------------------- + ** Scripts .. + **----------------------------------------------- + ** + ** During reselection the ncr jumps to this point. + ** The SFBR register is loaded with the encoded target id. + ** + ** Jump to the first target. + ** + ** JUMP + ** @(next tcb) + */ + struct link jump_tcb; + + /*----------------------------------------------- + ** Configuration .. + **----------------------------------------------- + ** + ** virtual and physical addresses + ** of the 53c810 chip. + */ + int reg_rid; + struct resource *reg_res; + bus_space_tag_t bst; + bus_space_handle_t bsh; + + int sram_rid; + struct resource *sram_res; + bus_space_tag_t bst2; + bus_space_handle_t bsh2; + + struct resource *irq_res; + void *irq_handle; + + /* + ** Scripts instance virtual address. + */ + struct script *script; + struct scripth *scripth; + + /* + ** Scripts instance physical address. + */ + u_long p_script; + u_long p_scripth; + + /* + ** The SCSI address of the host adapter. + */ + u_char myaddr; + + /* + ** timing parameters + */ + u_char minsync; /* Minimum sync period factor */ + u_char maxsync; /* Maximum sync period factor */ + u_char maxoffs; /* Max scsi offset */ + u_char clock_divn; /* Number of clock divisors */ + u_long clock_khz; /* SCSI clock frequency in KHz */ + u_long features; /* Chip features map */ + u_char multiplier; /* Clock multiplier (1,2,4) */ + + u_char maxburst; /* log base 2 of dwords burst */ + + /* + ** BIOS supplied PCI bus options + */ + u_char rv_scntl3; + u_char rv_dcntl; + u_char rv_dmode; + u_char rv_ctest3; + u_char rv_ctest4; + u_char rv_ctest5; + u_char rv_gpcntl; + u_char rv_stest2; + + /*----------------------------------------------- + ** CAM SIM information for this instance + **----------------------------------------------- + */ + + struct cam_sim *sim; + struct cam_path *path; + + /*----------------------------------------------- + ** Job control + **----------------------------------------------- + ** + ** Commands from user + */ + struct usrcmd user; + + /* + ** Target data + */ + struct tcb target[MAX_TARGET]; + + /* + ** Start queue. + */ + u_int32_t squeue [MAX_START]; + u_short squeueput; + + /* + ** Timeout handler + */ + time_t heartbeat; + u_short ticks; + u_short latetime; + time_t lasttime; + struct callout_handle timeout_ch; + + /*----------------------------------------------- + ** Debug and profiling + **----------------------------------------------- + ** + ** register dump + */ + struct ncr_reg regdump; + time_t regtime; + + /* + ** Profiling data + */ + struct profile profile; + u_long disc_phys; + u_long disc_ref; + + /* + ** Head of list of all nccbs for this controller. + */ + nccb_p link_nccb; + + /* + ** message buffers. + ** Should be longword aligned, + ** because they're written with a + ** COPY script command. + */ + u_char msgout[8]; + u_char msgin [8]; + u_int32_t lastmsg; + + /* + ** Buffer for STATUS_IN phase. + */ + u_char scratch; + + /* + ** controller chip dependent maximal transfer width. + */ + u_char maxwide; + +#ifdef NCR_IOMAPPED + /* + ** address of the ncr control registers in io space + */ + pci_port_t port; +#endif +}; + +#define NCB_SCRIPT_PHYS(np,lbl) (np->p_script + offsetof (struct script, lbl)) +#define NCB_SCRIPTH_PHYS(np,lbl) (np->p_scripth + offsetof (struct scripth,lbl)) + +/*========================================================== +** +** +** Script for NCR-Processor. +** +** Use ncr_script_fill() to create the variable parts. +** Use ncr_script_copy_and_bind() to make a copy and +** bind to physical addresses. +** +** +**========================================================== +** +** We have to know the offsets of all labels before +** we reach them (for forward jumps). +** Therefore we declare a struct here. +** If you make changes inside the script, +** DONT FORGET TO CHANGE THE LENGTHS HERE! +** +**---------------------------------------------------------- +*/ + +/* +** Script fragments which are loaded into the on-board RAM +** of 825A, 875 and 895 chips. +*/ +struct script { + ncrcmd start [ 7]; + ncrcmd start0 [ 2]; + ncrcmd start1 [ 3]; + ncrcmd startpos [ 1]; + ncrcmd trysel [ 8]; + ncrcmd skip [ 8]; + ncrcmd skip2 [ 3]; + ncrcmd idle [ 2]; + ncrcmd select [ 18]; + ncrcmd prepare [ 4]; + ncrcmd loadpos [ 14]; + ncrcmd prepare2 [ 24]; + ncrcmd setmsg [ 5]; + ncrcmd clrack [ 2]; + ncrcmd dispatch [ 33]; + ncrcmd no_data [ 17]; + ncrcmd checkatn [ 10]; + ncrcmd command [ 15]; + ncrcmd status [ 27]; + ncrcmd msg_in [ 26]; + ncrcmd msg_bad [ 6]; + ncrcmd complete [ 13]; + ncrcmd cleanup [ 12]; + ncrcmd cleanup0 [ 9]; + ncrcmd signal [ 12]; + ncrcmd save_dp [ 5]; + ncrcmd restore_dp [ 5]; + ncrcmd disconnect [ 12]; + ncrcmd disconnect0 [ 5]; + ncrcmd disconnect1 [ 23]; + ncrcmd msg_out [ 9]; + ncrcmd msg_out_done [ 7]; + ncrcmd badgetcc [ 6]; + ncrcmd reselect [ 8]; + ncrcmd reselect1 [ 8]; + ncrcmd reselect2 [ 8]; + ncrcmd resel_tmp [ 5]; + ncrcmd resel_lun [ 18]; + ncrcmd resel_tag [ 24]; + ncrcmd data_in [MAX_SCATTER * 4 + 7]; + ncrcmd data_out [MAX_SCATTER * 4 + 7]; +}; + +/* +** Script fragments which stay in main memory for all chips. +*/ +struct scripth { + ncrcmd tryloop [MAX_START*5+2]; + ncrcmd msg_parity [ 6]; + ncrcmd msg_reject [ 8]; + ncrcmd msg_ign_residue [ 32]; + ncrcmd msg_extended [ 18]; + ncrcmd msg_ext_2 [ 18]; + ncrcmd msg_wdtr [ 27]; + ncrcmd msg_ext_3 [ 18]; + ncrcmd msg_sdtr [ 27]; + ncrcmd msg_out_abort [ 10]; + ncrcmd getcc [ 4]; + ncrcmd getcc1 [ 5]; +#ifdef NCR_GETCC_WITHMSG + ncrcmd getcc2 [ 29]; +#else + ncrcmd getcc2 [ 14]; +#endif + ncrcmd getcc3 [ 6]; + ncrcmd aborttag [ 4]; + ncrcmd abort [ 22]; + ncrcmd snooptest [ 9]; + ncrcmd snoopend [ 2]; +}; + +/*========================================================== +** +** +** Function headers. +** +** +**========================================================== +*/ + +#ifdef _KERNEL +static nccb_p ncr_alloc_nccb (ncb_p np, u_long target, u_long lun); +static void ncr_complete (ncb_p np, nccb_p cp); +static int ncr_delta (int * from, int * to); +static void ncr_exception (ncb_p np); +static void ncr_free_nccb (ncb_p np, nccb_p cp); +static void ncr_freeze_devq (ncb_p np, struct cam_path *path); +static void ncr_selectclock (ncb_p np, u_char scntl3); +static void ncr_getclock (ncb_p np, u_char multiplier); +static nccb_p ncr_get_nccb (ncb_p np, u_long t,u_long l); +#if 0 +static u_int32_t ncr_info (int unit); +#endif +static void ncr_init (ncb_p np, char * msg, u_long code); +static void ncr_intr (void *vnp); +static void ncr_int_ma (ncb_p np, u_char dstat); +static void ncr_int_sir (ncb_p np); +static void ncr_int_sto (ncb_p np); +#if 0 +static void ncr_min_phys (struct buf *bp); +#endif +static void ncr_poll (struct cam_sim *sim); +static void ncb_profile (ncb_p np, nccb_p cp); +static void ncr_script_copy_and_bind + (ncb_p np, ncrcmd *src, ncrcmd *dst, int len); +static void ncr_script_fill (struct script * scr, struct scripth *scrh); +static int ncr_scatter (struct dsb* phys, vm_offset_t vaddr, + vm_size_t datalen); +static void ncr_getsync (ncb_p np, u_char sfac, u_char *fakp, + u_char *scntl3p); +static void ncr_setsync (ncb_p np, nccb_p cp,u_char scntl3,u_char sxfer, + u_char period); +static void ncr_setwide (ncb_p np, nccb_p cp, u_char wide, u_char ack); +static int ncr_show_msg (u_char * msg); +static int ncr_snooptest (ncb_p np); +static void ncr_action (struct cam_sim *sim, union ccb *ccb); +static void ncr_timeout (void *arg); +static void ncr_wakeup (ncb_p np, u_long code); + +static int ncr_probe (device_t dev); +static int ncr_attach (device_t dev); + +#endif /* _KERNEL */ + +/*========================================================== +** +** +** Global static data. +** +** +**========================================================== +*/ + + +#if !defined(lint) +static const char ident[] = + "\n$FreeBSD$\n"; +#endif + +static const u_long ncr_version = NCR_VERSION * 11 + + (u_long) sizeof (struct ncb) * 7 + + (u_long) sizeof (struct nccb) * 5 + + (u_long) sizeof (struct lcb) * 3 + + (u_long) sizeof (struct tcb) * 2; + +#ifdef _KERNEL + +static int ncr_debug = SCSI_NCR_DEBUG; +SYSCTL_INT(_debug, OID_AUTO, ncr_debug, CTLFLAG_RW, &ncr_debug, 0, ""); + +static int ncr_cache; /* to be aligned _NOT_ static */ + +/*========================================================== +** +** +** Global static data: auto configure +** +** +**========================================================== +*/ + +#define NCR_810_ID (0x00011000ul) +#define NCR_815_ID (0x00041000ul) +#define NCR_820_ID (0x00021000ul) +#define NCR_825_ID (0x00031000ul) +#define NCR_860_ID (0x00061000ul) +#define NCR_875_ID (0x000f1000ul) +#define NCR_875_ID2 (0x008f1000ul) +#define NCR_885_ID (0x000d1000ul) +#define NCR_895_ID (0x000c1000ul) +#define NCR_896_ID (0x000b1000ul) +#define NCR_895A_ID (0x00121000ul) +#define NCR_1510D_ID (0x000a1000ul) + + +static char *ncr_name (ncb_p np) +{ + static char name[10]; + snprintf(name, sizeof(name), "ncr%d", np->unit); + return (name); +} + +/*========================================================== +** +** +** Scripts for NCR-Processor. +** +** Use ncr_script_bind for binding to physical addresses. +** +** +**========================================================== +** +** NADDR generates a reference to a field of the controller data. +** PADDR generates a reference to another part of the script. +** RADDR generates a reference to a script processor register. +** FADDR generates a reference to a script processor register +** with offset. +** +**---------------------------------------------------------- +*/ + +#define RELOC_SOFTC 0x40000000 +#define RELOC_LABEL 0x50000000 +#define RELOC_REGISTER 0x60000000 +#define RELOC_KVAR 0x70000000 +#define RELOC_LABELH 0x80000000 +#define RELOC_MASK 0xf0000000 + +#define NADDR(label) (RELOC_SOFTC | offsetof(struct ncb, label)) +#define PADDR(label) (RELOC_LABEL | offsetof(struct script, label)) +#define PADDRH(label) (RELOC_LABELH | offsetof(struct scripth, label)) +#define RADDR(label) (RELOC_REGISTER | REG(label)) +#define FADDR(label,ofs)(RELOC_REGISTER | ((REG(label))+(ofs))) +#define KVAR(which) (RELOC_KVAR | (which)) + +#define KVAR_SECOND (0) +#define KVAR_TICKS (1) +#define KVAR_NCR_CACHE (2) + +#define SCRIPT_KVAR_FIRST (0) +#define SCRIPT_KVAR_LAST (3) + +/* + * Kernel variables referenced in the scripts. + * THESE MUST ALL BE ALIGNED TO A 4-BYTE BOUNDARY. + */ +static void *script_kvars[] = + { &time_second, &ticks, &ncr_cache }; + +static struct script script0 = { +/*--------------------------< START >-----------------------*/ { + /* + ** Claim to be still alive ... + */ + SCR_COPY (sizeof (((struct ncb *)0)->heartbeat)), + KVAR (KVAR_SECOND), + NADDR (heartbeat), + /* + ** Make data structure address invalid. + ** clear SIGP. + */ + SCR_LOAD_REG (dsa, 0xff), + 0, + SCR_FROM_REG (ctest2), + 0, +}/*-------------------------< START0 >----------------------*/,{ + /* + ** Hook for interrupted GetConditionCode. + ** Will be patched to ... IFTRUE by + ** the interrupt handler. + */ + SCR_INT ^ IFFALSE (0), + SIR_SENSE_RESTART, + +}/*-------------------------< START1 >----------------------*/,{ + /* + ** Hook for stalled start queue. + ** Will be patched to IFTRUE by the interrupt handler. + */ + SCR_INT ^ IFFALSE (0), + SIR_STALL_RESTART, + /* + ** Then jump to a certain point in tryloop. + ** Due to the lack of indirect addressing the code + ** is self modifying here. + */ + SCR_JUMP, +}/*-------------------------< STARTPOS >--------------------*/,{ + PADDRH(tryloop), + +}/*-------------------------< TRYSEL >----------------------*/,{ + /* + ** Now: + ** DSA: Address of a Data Structure + ** or Address of the IDLE-Label. + ** + ** TEMP: Address of a script, which tries to + ** start the NEXT entry. + ** + ** Save the TEMP register into the SCRATCHA register. + ** Then copy the DSA to TEMP and RETURN. + ** This is kind of an indirect jump. + ** (The script processor has NO stack, so the + ** CALL is actually a jump and link, and the + ** RETURN is an indirect jump.) + ** + ** If the slot was empty, DSA contains the address + ** of the IDLE part of this script. The processor + ** jumps to IDLE and waits for a reselect. + ** It will wake up and try the same slot again + ** after the SIGP bit becomes set by the host. + ** + ** If the slot was not empty, DSA contains + ** the address of the phys-part of a nccb. + ** The processor jumps to this address. + ** phys starts with head, + ** head starts with launch, + ** so actually the processor jumps to + ** the lauch part. + ** If the entry is scheduled for execution, + ** then launch contains a jump to SELECT. + ** If it's not scheduled, it contains a jump to IDLE. + */ + SCR_COPY (4), + RADDR (temp), + RADDR (scratcha), + SCR_COPY (4), + RADDR (dsa), + RADDR (temp), + SCR_RETURN, + 0 + +}/*-------------------------< SKIP >------------------------*/,{ + /* + ** This entry has been canceled. + ** Next time use the next slot. + */ + SCR_COPY (4), + RADDR (scratcha), + PADDR (startpos), + /* + ** patch the launch field. + ** should look like an idle process. + */ + SCR_COPY_F (4), + RADDR (dsa), + PADDR (skip2), + SCR_COPY (8), + PADDR (idle), +}/*-------------------------< SKIP2 >-----------------------*/,{ + 0, + SCR_JUMP, + PADDR(start), +}/*-------------------------< IDLE >------------------------*/,{ + /* + ** Nothing to do? + ** Wait for reselect. + */ + SCR_JUMP, + PADDR(reselect), + +}/*-------------------------< SELECT >----------------------*/,{ + /* + ** DSA contains the address of a scheduled + ** data structure. + ** + ** SCRATCHA contains the address of the script, + ** which starts the next entry. + ** + ** Set Initiator mode. + ** + ** (Target mode is left as an exercise for the reader) + */ + + SCR_CLR (SCR_TRG), + 0, + SCR_LOAD_REG (HS_REG, 0xff), + 0, + + /* + ** And try to select this target. + */ + SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select), + PADDR (reselect), + + /* + ** Now there are 4 possibilities: + ** + ** (1) The ncr looses arbitration. + ** This is ok, because it will try again, + ** when the bus becomes idle. + ** (But beware of the timeout function!) + ** + ** (2) The ncr is reselected. + ** Then the script processor takes the jump + ** to the RESELECT label. + ** + ** (3) The ncr completes the selection. + ** Then it will execute the next statement. + ** + ** (4) There is a selection timeout. + ** Then the ncr should interrupt the host and stop. + ** Unfortunately, it seems to continue execution + ** of the script. But it will fail with an + ** IID-interrupt on the next WHEN. + */ + + SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)), + 0, + + /* + ** Send the IDENTIFY and SIMPLE_TAG messages + ** (and the MSG_EXT_SDTR message) + */ + SCR_MOVE_TBL ^ SCR_MSG_OUT, + offsetof (struct dsb, smsg), +#ifdef undef /* XXX better fail than try to deal with this ... */ + SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_OUT)), + -16, +#endif + SCR_CLR (SCR_ATN), + 0, + SCR_COPY (1), + RADDR (sfbr), + NADDR (lastmsg), + /* + ** Selection complete. + ** Next time use the next slot. + */ + SCR_COPY (4), + RADDR (scratcha), + PADDR (startpos), +}/*-------------------------< PREPARE >----------------------*/,{ + /* + ** The ncr doesn't have an indirect load + ** or store command. So we have to + ** copy part of the control block to a + ** fixed place, where we can access it. + ** + ** We patch the address part of a + ** COPY command with the DSA-register. + */ + SCR_COPY_F (4), + RADDR (dsa), + PADDR (loadpos), + /* + ** then we do the actual copy. + */ + SCR_COPY (sizeof (struct head)), + /* + ** continued after the next label ... + */ + +}/*-------------------------< LOADPOS >---------------------*/,{ + 0, + NADDR (header), + /* + ** Mark this nccb as not scheduled. + */ + SCR_COPY (8), + PADDR (idle), + NADDR (header.launch), + /* + ** Set a time stamp for this selection + */ + SCR_COPY (sizeof (ticks)), + KVAR (KVAR_TICKS), + NADDR (header.stamp.select), + /* + ** load the savep (saved pointer) into + ** the TEMP register (actual pointer) + */ + SCR_COPY (4), + NADDR (header.savep), + RADDR (temp), + /* + ** Initialize the status registers + */ + SCR_COPY (4), + NADDR (header.status), + RADDR (scr0), + +}/*-------------------------< PREPARE2 >---------------------*/,{ + /* + ** Load the synchronous mode register + */ + SCR_COPY (1), + NADDR (sync_st), + RADDR (sxfer), + /* + ** Load the wide mode and timing register + */ + SCR_COPY (1), + NADDR (wide_st), + RADDR (scntl3), + /* + ** Initialize the msgout buffer with a NOOP message. + */ + SCR_LOAD_REG (scratcha, MSG_NOOP), + 0, + SCR_COPY (1), + RADDR (scratcha), + NADDR (msgout), + SCR_COPY (1), + RADDR (scratcha), + NADDR (msgin), + /* + ** Message in phase ? + */ + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDR (dispatch), + /* + ** Extended or reject message ? + */ + SCR_FROM_REG (sbdl), + 0, + SCR_JUMP ^ IFTRUE (DATA (MSG_EXTENDED)), + PADDR (msg_in), + SCR_JUMP ^ IFTRUE (DATA (MSG_MESSAGE_REJECT)), + PADDRH (msg_reject), + /* + ** normal processing + */ + SCR_JUMP, + PADDR (dispatch), +}/*-------------------------< SETMSG >----------------------*/,{ + SCR_COPY (1), + RADDR (scratcha), + NADDR (msgout), + SCR_SET (SCR_ATN), + 0, +}/*-------------------------< CLRACK >----------------------*/,{ + /* + ** Terminate possible pending message phase. + */ + SCR_CLR (SCR_ACK), + 0, + +}/*-----------------------< DISPATCH >----------------------*/,{ + SCR_FROM_REG (HS_REG), + 0, + SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)), + SIR_NEGO_FAILED, + /* + ** remove bogus output signals + */ + SCR_REG_REG (socl, SCR_AND, CACK|CATN), + 0, + SCR_RETURN ^ IFTRUE (WHEN (SCR_DATA_OUT)), + 0, + SCR_RETURN ^ IFTRUE (IF (SCR_DATA_IN)), + 0, + SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)), + PADDR (msg_out), + SCR_JUMP ^ IFTRUE (IF (SCR_MSG_IN)), + PADDR (msg_in), + SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND)), + PADDR (command), + SCR_JUMP ^ IFTRUE (IF (SCR_STATUS)), + PADDR (status), + /* + ** Discard one illegal phase byte, if required. + */ + SCR_LOAD_REG (scratcha, XE_BAD_PHASE), + 0, + SCR_COPY (1), + RADDR (scratcha), + NADDR (xerr_st), + SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_OUT)), + 8, + SCR_MOVE_ABS (1) ^ SCR_ILG_OUT, + NADDR (scratch), + SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_IN)), + 8, + SCR_MOVE_ABS (1) ^ SCR_ILG_IN, + NADDR (scratch), + SCR_JUMP, + PADDR (dispatch), + +}/*-------------------------< NO_DATA >--------------------*/,{ + /* + ** The target wants to tranfer too much data + ** or in the wrong direction. + ** Remember that in extended error. + */ + SCR_LOAD_REG (scratcha, XE_EXTRA_DATA), + 0, + SCR_COPY (1), + RADDR (scratcha), + NADDR (xerr_st), + /* + ** Discard one data byte, if required. + */ + SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_OUT)), + 8, + SCR_MOVE_ABS (1) ^ SCR_DATA_OUT, + NADDR (scratch), + SCR_JUMPR ^ IFFALSE (IF (SCR_DATA_IN)), + 8, + SCR_MOVE_ABS (1) ^ SCR_DATA_IN, + NADDR (scratch), + /* + ** .. and repeat as required. + */ + SCR_CALL, + PADDR (dispatch), + SCR_JUMP, + PADDR (no_data), +}/*-------------------------< CHECKATN >--------------------*/,{ + /* + ** If AAP (bit 1 of scntl0 register) is set + ** and a parity error is detected, + ** the script processor asserts ATN. + ** + ** The target should switch to a MSG_OUT phase + ** to get the message. + */ + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFFALSE (MASK (CATN, CATN)), + PADDR (dispatch), + /* + ** count it + */ + SCR_REG_REG (PS_REG, SCR_ADD, 1), + 0, + /* + ** Prepare a MSG_INITIATOR_DET_ERR message + ** (initiator detected error). + ** The target should retry the transfer. + */ + SCR_LOAD_REG (scratcha, MSG_INITIATOR_DET_ERR), + 0, + SCR_JUMP, + PADDR (setmsg), + +}/*-------------------------< COMMAND >--------------------*/,{ + /* + ** If this is not a GETCC transfer ... + */ + SCR_FROM_REG (SS_REG), + 0, +/*<<<*/ SCR_JUMPR ^ IFTRUE (DATA (SCSI_STATUS_CHECK_COND)), + 28, + /* + ** ... set a timestamp ... + */ + SCR_COPY (sizeof (ticks)), + KVAR (KVAR_TICKS), + NADDR (header.stamp.command), + /* + ** ... and send the command + */ + SCR_MOVE_TBL ^ SCR_COMMAND, + offsetof (struct dsb, cmd), + SCR_JUMP, + PADDR (dispatch), + /* + ** Send the GETCC command + */ +/*>>>*/ SCR_MOVE_TBL ^ SCR_COMMAND, + offsetof (struct dsb, scmd), + SCR_JUMP, + PADDR (dispatch), + +}/*-------------------------< STATUS >--------------------*/,{ + /* + ** set the timestamp. + */ + SCR_COPY (sizeof (ticks)), + KVAR (KVAR_TICKS), + NADDR (header.stamp.status), + /* + ** If this is a GETCC transfer, + */ + SCR_FROM_REG (SS_REG), + 0, +/*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (SCSI_STATUS_CHECK_COND)), + 40, + /* + ** get the status + */ + SCR_MOVE_ABS (1) ^ SCR_STATUS, + NADDR (scratch), + /* + ** Save status to scsi_status. + ** Mark as complete. + ** And wait for disconnect. + */ + SCR_TO_REG (SS_REG), + 0, + SCR_REG_REG (SS_REG, SCR_OR, SCSI_STATUS_SENSE), + 0, + SCR_LOAD_REG (HS_REG, HS_COMPLETE), + 0, + SCR_JUMP, + PADDR (checkatn), + /* + ** If it was no GETCC transfer, + ** save the status to scsi_status. + */ +/*>>>*/ SCR_MOVE_ABS (1) ^ SCR_STATUS, + NADDR (scratch), + SCR_TO_REG (SS_REG), + 0, + /* + ** if it was no check condition ... + */ + SCR_JUMP ^ IFTRUE (DATA (SCSI_STATUS_CHECK_COND)), + PADDR (checkatn), + /* + ** ... mark as complete. + */ + SCR_LOAD_REG (HS_REG, HS_COMPLETE), + 0, + SCR_JUMP, + PADDR (checkatn), + +}/*-------------------------< MSG_IN >--------------------*/,{ + /* + ** Get the first byte of the message + ** and save it to SCRATCHA. + ** + ** The script processor doesn't negate the + ** ACK signal after this transfer. + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + NADDR (msgin[0]), + /* + ** Check for message parity error. + */ + SCR_TO_REG (scratcha), + 0, + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), + PADDRH (msg_parity), + SCR_FROM_REG (scratcha), + 0, + /* + ** Parity was ok, handle this message. + */ + SCR_JUMP ^ IFTRUE (DATA (MSG_CMDCOMPLETE)), + PADDR (complete), + SCR_JUMP ^ IFTRUE (DATA (MSG_SAVEDATAPOINTER)), + PADDR (save_dp), + SCR_JUMP ^ IFTRUE (DATA (MSG_RESTOREPOINTERS)), + PADDR (restore_dp), + SCR_JUMP ^ IFTRUE (DATA (MSG_DISCONNECT)), + PADDR (disconnect), + SCR_JUMP ^ IFTRUE (DATA (MSG_EXTENDED)), + PADDRH (msg_extended), + SCR_JUMP ^ IFTRUE (DATA (MSG_NOOP)), + PADDR (clrack), + SCR_JUMP ^ IFTRUE (DATA (MSG_MESSAGE_REJECT)), + PADDRH (msg_reject), + SCR_JUMP ^ IFTRUE (DATA (MSG_IGN_WIDE_RESIDUE)), + PADDRH (msg_ign_residue), + /* + ** Rest of the messages left as + ** an exercise ... + ** + ** Unimplemented messages: + ** fall through to MSG_BAD. + */ +}/*-------------------------< MSG_BAD >------------------*/,{ + /* + ** unimplemented message - reject it. + */ + SCR_INT, + SIR_REJECT_SENT, + SCR_LOAD_REG (scratcha, MSG_MESSAGE_REJECT), + 0, + SCR_JUMP, + PADDR (setmsg), + +}/*-------------------------< COMPLETE >-----------------*/,{ + /* + ** Complete message. + ** + ** If it's not the get condition code, + ** copy TEMP register to LASTP in header. + */ + SCR_FROM_REG (SS_REG), + 0, +/*<<<*/ SCR_JUMPR ^ IFTRUE (MASK (SCSI_STATUS_SENSE, SCSI_STATUS_SENSE)), + 12, + SCR_COPY (4), + RADDR (temp), + NADDR (header.lastp), +/*>>>*/ /* + ** When we terminate the cycle by clearing ACK, + ** the target may disconnect immediately. + ** + ** We don't want to be told of an + ** "unexpected disconnect", + ** so we disable this feature. + */ + SCR_REG_REG (scntl2, SCR_AND, 0x7f), + 0, + /* + ** Terminate cycle ... + */ + SCR_CLR (SCR_ACK|SCR_ATN), + 0, + /* + ** ... and wait for the disconnect. + */ + SCR_WAIT_DISC, + 0, +}/*-------------------------< CLEANUP >-------------------*/,{ + /* + ** dsa: Pointer to nccb + ** or xxxxxxFF (no nccb) + ** + ** HS_REG: Host-Status (<>0!) + */ + SCR_FROM_REG (dsa), + 0, + SCR_JUMP ^ IFTRUE (DATA (0xff)), + PADDR (signal), + /* + ** dsa is valid. + ** save the status registers + */ + SCR_COPY (4), + RADDR (scr0), + NADDR (header.status), + /* + ** and copy back the header to the nccb. + */ + SCR_COPY_F (4), + RADDR (dsa), + PADDR (cleanup0), + SCR_COPY (sizeof (struct head)), + NADDR (header), +}/*-------------------------< CLEANUP0 >--------------------*/,{ + 0, + + /* + ** If command resulted in "check condition" + ** status and is not yet completed, + ** try to get the condition code. + */ + SCR_FROM_REG (HS_REG), + 0, +/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (0, HS_DONEMASK)), + 16, + SCR_FROM_REG (SS_REG), + 0, + SCR_JUMP ^ IFTRUE (DATA (SCSI_STATUS_CHECK_COND)), + PADDRH(getcc2), +}/*-------------------------< SIGNAL >----------------------*/,{ + /* + ** if status = queue full, + ** reinsert in startqueue and stall queue. + */ +/*>>>*/ SCR_FROM_REG (SS_REG), + 0, + SCR_INT ^ IFTRUE (DATA (SCSI_STATUS_QUEUE_FULL)), + SIR_STALL_QUEUE, + /* + ** And make the DSA register invalid. + */ + SCR_LOAD_REG (dsa, 0xff), /* invalid */ + 0, + /* + ** if job completed ... + */ + SCR_FROM_REG (HS_REG), + 0, + /* + ** ... signal completion to the host + */ + SCR_INT_FLY ^ IFFALSE (MASK (0, HS_DONEMASK)), + 0, + /* + ** Auf zu neuen Schandtaten! + */ + SCR_JUMP, + PADDR(start), + +}/*-------------------------< SAVE_DP >------------------*/,{ + /* + ** SAVE_DP message: + ** Copy TEMP register to SAVEP in header. + */ + SCR_COPY (4), + RADDR (temp), + NADDR (header.savep), + SCR_JUMP, + PADDR (clrack), +}/*-------------------------< RESTORE_DP >---------------*/,{ + /* + ** RESTORE_DP message: + ** Copy SAVEP in header to TEMP register. + */ + SCR_COPY (4), + NADDR (header.savep), + RADDR (temp), + SCR_JUMP, + PADDR (clrack), + +}/*-------------------------< DISCONNECT >---------------*/,{ + /* + ** If QUIRK_AUTOSAVE is set, + ** do an "save pointer" operation. + */ + SCR_FROM_REG (QU_REG), + 0, +/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (QUIRK_AUTOSAVE, QUIRK_AUTOSAVE)), + 12, + /* + ** like SAVE_DP message: + ** Copy TEMP register to SAVEP in header. + */ + SCR_COPY (4), + RADDR (temp), + NADDR (header.savep), +/*>>>*/ /* + ** Check if temp==savep or temp==goalp: + ** if not, log a missing save pointer message. + ** In fact, it's a comparison mod 256. + ** + ** Hmmm, I hadn't thought that I would be urged to + ** write this kind of ugly self modifying code. + ** + ** It's unbelievable, but the ncr53c8xx isn't able + ** to subtract one register from another. + */ + SCR_FROM_REG (temp), + 0, + /* + ** You are not expected to understand this .. + ** + ** CAUTION: only little endian architectures supported! XXX + */ + SCR_COPY_F (1), + NADDR (header.savep), + PADDR (disconnect0), +}/*-------------------------< DISCONNECT0 >--------------*/,{ +/*<<<*/ SCR_JUMPR ^ IFTRUE (DATA (1)), + 20, + /* + ** neither this + */ + SCR_COPY_F (1), + NADDR (header.goalp), + PADDR (disconnect1), +}/*-------------------------< DISCONNECT1 >--------------*/,{ + SCR_INT ^ IFFALSE (DATA (1)), + SIR_MISSING_SAVE, +/*>>>*/ + + /* + ** DISCONNECTing ... + ** + ** disable the "unexpected disconnect" feature, + ** and remove the ACK signal. + */ + SCR_REG_REG (scntl2, SCR_AND, 0x7f), + 0, + SCR_CLR (SCR_ACK|SCR_ATN), + 0, + /* + ** Wait for the disconnect. + */ + SCR_WAIT_DISC, + 0, + /* + ** Profiling: + ** Set a time stamp, + ** and count the disconnects. + */ + SCR_COPY (sizeof (ticks)), + KVAR (KVAR_TICKS), + NADDR (header.stamp.disconnect), + SCR_COPY (4), + NADDR (disc_phys), + RADDR (temp), + SCR_REG_REG (temp, SCR_ADD, 0x01), + 0, + SCR_COPY (4), + RADDR (temp), + NADDR (disc_phys), + /* + ** Status is: DISCONNECTED. + */ + SCR_LOAD_REG (HS_REG, HS_DISCONNECT), + 0, + SCR_JUMP, + PADDR (cleanup), + +}/*-------------------------< MSG_OUT >-------------------*/,{ + /* + ** The target requests a message. + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_OUT, + NADDR (msgout), + SCR_COPY (1), + RADDR (sfbr), + NADDR (lastmsg), + /* + ** If it was no ABORT message ... + */ + SCR_JUMP ^ IFTRUE (DATA (MSG_ABORT)), + PADDRH (msg_out_abort), + /* + ** ... wait for the next phase + ** if it's a message out, send it again, ... + */ + SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)), + PADDR (msg_out), +}/*-------------------------< MSG_OUT_DONE >--------------*/,{ + /* + ** ... else clear the message ... + */ + SCR_LOAD_REG (scratcha, MSG_NOOP), + 0, + SCR_COPY (4), + RADDR (scratcha), + NADDR (msgout), + /* + ** ... and process the next phase + */ + SCR_JUMP, + PADDR (dispatch), + +}/*------------------------< BADGETCC >---------------------*/,{ + /* + ** If SIGP was set, clear it and try again. + */ + SCR_FROM_REG (ctest2), + 0, + SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)), + PADDRH (getcc2), + SCR_INT, + SIR_SENSE_FAILED, +}/*-------------------------< RESELECT >--------------------*/,{ + /* + ** This NOP will be patched with LED OFF + ** SCR_REG_REG (gpreg, SCR_OR, 0x01) + */ + SCR_NO_OP, + 0, + + /* + ** make the DSA invalid. + */ + SCR_LOAD_REG (dsa, 0xff), + 0, + SCR_CLR (SCR_TRG), + 0, + /* + ** Sleep waiting for a reselection. + ** If SIGP is set, special treatment. + ** + ** Zu allem bereit .. + */ + SCR_WAIT_RESEL, + PADDR(reselect2), +}/*-------------------------< RESELECT1 >--------------------*/,{ + /* + ** This NOP will be patched with LED ON + ** SCR_REG_REG (gpreg, SCR_AND, 0xfe) + */ + SCR_NO_OP, + 0, + /* + ** ... zu nichts zu gebrauchen ? + ** + ** load the target id into the SFBR + ** and jump to the control block. + ** + ** Look at the declarations of + ** - struct ncb + ** - struct tcb + ** - struct lcb + ** - struct nccb + ** to understand what's going on. + */ + SCR_REG_SFBR (ssid, SCR_AND, 0x8F), + 0, + SCR_TO_REG (sdid), + 0, + SCR_JUMP, + NADDR (jump_tcb), +}/*-------------------------< RESELECT2 >-------------------*/,{ + /* + ** This NOP will be patched with LED ON + ** SCR_REG_REG (gpreg, SCR_AND, 0xfe) + */ + SCR_NO_OP, + 0, + /* + ** If it's not connected :( + ** -> interrupted by SIGP bit. + ** Jump to start. + */ + SCR_FROM_REG (ctest2), + 0, + SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)), + PADDR (start), + SCR_JUMP, + PADDR (reselect), + +}/*-------------------------< RESEL_TMP >-------------------*/,{ + /* + ** The return address in TEMP + ** is in fact the data structure address, + ** so copy it to the DSA register. + */ + SCR_COPY (4), + RADDR (temp), + RADDR (dsa), + SCR_JUMP, + PADDR (prepare), + +}/*-------------------------< RESEL_LUN >-------------------*/,{ + /* + ** come back to this point + ** to get an IDENTIFY message + ** Wait for a msg_in phase. + */ +/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)), + 48, + /* + ** message phase + ** It's not a sony, it's a trick: + ** read the data without acknowledging it. + */ + SCR_FROM_REG (sbdl), + 0, +/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (MSG_IDENTIFYFLAG, 0x98)), + 32, + /* + ** It WAS an Identify message. + ** get it and ack it! + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + NADDR (msgin), + SCR_CLR (SCR_ACK), + 0, + /* + ** Mask out the lun. + */ + SCR_REG_REG (sfbr, SCR_AND, 0x07), + 0, + SCR_RETURN, + 0, + /* + ** No message phase or no IDENTIFY message: + ** return 0. + */ +/*>>>*/ SCR_LOAD_SFBR (0), + 0, + SCR_RETURN, + 0, + +}/*-------------------------< RESEL_TAG >-------------------*/,{ + /* + ** come back to this point + ** to get a SIMPLE_TAG message + ** Wait for a MSG_IN phase. + */ +/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)), + 64, + /* + ** message phase + ** It's a trick - read the data + ** without acknowledging it. + */ + SCR_FROM_REG (sbdl), + 0, +/*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (MSG_SIMPLE_Q_TAG)), + 48, + /* + ** It WAS a SIMPLE_TAG message. + ** get it and ack it! + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + NADDR (msgin), + SCR_CLR (SCR_ACK), + 0, + /* + ** Wait for the second byte (the tag) + */ +/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)), + 24, + /* + ** Get it and ack it! + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + NADDR (msgin), + SCR_CLR (SCR_ACK|SCR_CARRY), + 0, + SCR_RETURN, + 0, + /* + ** No message phase or no SIMPLE_TAG message + ** or no second byte: return 0. + */ +/*>>>*/ SCR_LOAD_SFBR (0), + 0, + SCR_SET (SCR_CARRY), + 0, + SCR_RETURN, + 0, + +}/*-------------------------< DATA_IN >--------------------*/,{ +/* +** Because the size depends on the +** #define MAX_SCATTER parameter, +** it is filled in at runtime. +** +** SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)), +** PADDR (no_data), +** SCR_COPY (sizeof (ticks)), +** KVAR (KVAR_TICKS), +** NADDR (header.stamp.data), +** SCR_MOVE_TBL ^ SCR_DATA_IN, +** offsetof (struct dsb, data[ 0]), +** +** ##===========< i=1; i<MAX_SCATTER >========= +** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)), +** || PADDR (checkatn), +** || SCR_MOVE_TBL ^ SCR_DATA_IN, +** || offsetof (struct dsb, data[ i]), +** ##========================================== +** +** SCR_CALL, +** PADDR (checkatn), +** SCR_JUMP, +** PADDR (no_data), +*/ +0 +}/*-------------------------< DATA_OUT >-------------------*/,{ +/* +** Because the size depends on the +** #define MAX_SCATTER parameter, +** it is filled in at runtime. +** +** SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_OUT)), +** PADDR (no_data), +** SCR_COPY (sizeof (ticks)), +** KVAR (KVAR_TICKS), +** NADDR (header.stamp.data), +** SCR_MOVE_TBL ^ SCR_DATA_OUT, +** offsetof (struct dsb, data[ 0]), +** +** ##===========< i=1; i<MAX_SCATTER >========= +** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)), +** || PADDR (dispatch), +** || SCR_MOVE_TBL ^ SCR_DATA_OUT, +** || offsetof (struct dsb, data[ i]), +** ##========================================== +** +** SCR_CALL, +** PADDR (dispatch), +** SCR_JUMP, +** PADDR (no_data), +** +**--------------------------------------------------------- +*/ +(u_long)0 + +}/*--------------------------------------------------------*/ +}; + + +static struct scripth scripth0 = { +/*-------------------------< TRYLOOP >---------------------*/{ +/* +** Load an entry of the start queue into dsa +** and try to start it by jumping to TRYSEL. +** +** Because the size depends on the +** #define MAX_START parameter, it is filled +** in at runtime. +** +**----------------------------------------------------------- +** +** ##===========< I=0; i<MAX_START >=========== +** || SCR_COPY (4), +** || NADDR (squeue[i]), +** || RADDR (dsa), +** || SCR_CALL, +** || PADDR (trysel), +** ##========================================== +** +** SCR_JUMP, +** PADDRH(tryloop), +** +**----------------------------------------------------------- +*/ +0 +}/*-------------------------< MSG_PARITY >---------------*/,{ + /* + ** count it + */ + SCR_REG_REG (PS_REG, SCR_ADD, 0x01), + 0, + /* + ** send a "message parity error" message. + */ + SCR_LOAD_REG (scratcha, MSG_PARITY_ERROR), + 0, + SCR_JUMP, + PADDR (setmsg), +}/*-------------------------< MSG_MESSAGE_REJECT >---------------*/,{ + /* + ** If a negotiation was in progress, + ** negotiation failed. + */ + SCR_FROM_REG (HS_REG), + 0, + SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)), + SIR_NEGO_FAILED, + /* + ** else make host log this message + */ + SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)), + SIR_REJECT_RECEIVED, + SCR_JUMP, + PADDR (clrack), + +}/*-------------------------< MSG_IGN_RESIDUE >----------*/,{ + /* + ** Terminate cycle + */ + SCR_CLR (SCR_ACK), + 0, + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDR (dispatch), + /* + ** get residue size. + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + NADDR (msgin[1]), + /* + ** Check for message parity error. + */ + SCR_TO_REG (scratcha), + 0, + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), + PADDRH (msg_parity), + SCR_FROM_REG (scratcha), + 0, + /* + ** Size is 0 .. ignore message. + */ + SCR_JUMP ^ IFTRUE (DATA (0)), + PADDR (clrack), + /* + ** Size is not 1 .. have to interrupt. + */ +/*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (1)), + 40, + /* + ** Check for residue byte in swide register + */ + SCR_FROM_REG (scntl2), + 0, +/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)), + 16, + /* + ** There IS data in the swide register. + ** Discard it. + */ + SCR_REG_REG (scntl2, SCR_OR, WSR), + 0, + SCR_JUMP, + PADDR (clrack), + /* + ** Load again the size to the sfbr register. + */ +/*>>>*/ SCR_FROM_REG (scratcha), + 0, +/*>>>*/ SCR_INT, + SIR_IGN_RESIDUE, + SCR_JUMP, + PADDR (clrack), + +}/*-------------------------< MSG_EXTENDED >-------------*/,{ + /* + ** Terminate cycle + */ + SCR_CLR (SCR_ACK), + 0, + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDR (dispatch), + /* + ** get length. + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + NADDR (msgin[1]), + /* + ** Check for message parity error. + */ + SCR_TO_REG (scratcha), + 0, + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), + PADDRH (msg_parity), + SCR_FROM_REG (scratcha), + 0, + /* + */ + SCR_JUMP ^ IFTRUE (DATA (3)), + PADDRH (msg_ext_3), + SCR_JUMP ^ IFFALSE (DATA (2)), + PADDR (msg_bad), +}/*-------------------------< MSG_EXT_2 >----------------*/,{ + SCR_CLR (SCR_ACK), + 0, + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDR (dispatch), + /* + ** get extended message code. + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + NADDR (msgin[2]), + /* + ** Check for message parity error. + */ + SCR_TO_REG (scratcha), + 0, + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), + PADDRH (msg_parity), + SCR_FROM_REG (scratcha), + 0, + SCR_JUMP ^ IFTRUE (DATA (MSG_EXT_WDTR)), + PADDRH (msg_wdtr), + /* + ** unknown extended message + */ + SCR_JUMP, + PADDR (msg_bad) +}/*-------------------------< MSG_WDTR >-----------------*/,{ + SCR_CLR (SCR_ACK), + 0, + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDR (dispatch), + /* + ** get data bus width + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + NADDR (msgin[3]), + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), + PADDRH (msg_parity), + /* + ** let the host do the real work. + */ + SCR_INT, + SIR_NEGO_WIDE, + /* + ** let the target fetch our answer. + */ + SCR_SET (SCR_ATN), + 0, + SCR_CLR (SCR_ACK), + 0, + + SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)), + SIR_NEGO_PROTO, + /* + ** Send the MSG_EXT_WDTR + */ + SCR_MOVE_ABS (4) ^ SCR_MSG_OUT, + NADDR (msgout), + SCR_CLR (SCR_ATN), + 0, + SCR_COPY (1), + RADDR (sfbr), + NADDR (lastmsg), + SCR_JUMP, + PADDR (msg_out_done), + +}/*-------------------------< MSG_EXT_3 >----------------*/,{ + SCR_CLR (SCR_ACK), + 0, + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDR (dispatch), + /* + ** get extended message code. + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + NADDR (msgin[2]), + /* + ** Check for message parity error. + */ + SCR_TO_REG (scratcha), + 0, + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), + PADDRH (msg_parity), + SCR_FROM_REG (scratcha), + 0, + SCR_JUMP ^ IFTRUE (DATA (MSG_EXT_SDTR)), + PADDRH (msg_sdtr), + /* + ** unknown extended message + */ + SCR_JUMP, + PADDR (msg_bad) + +}/*-------------------------< MSG_SDTR >-----------------*/,{ + SCR_CLR (SCR_ACK), + 0, + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDR (dispatch), + /* + ** get period and offset + */ + SCR_MOVE_ABS (2) ^ SCR_MSG_IN, + NADDR (msgin[3]), + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), + PADDRH (msg_parity), + /* + ** let the host do the real work. + */ + SCR_INT, + SIR_NEGO_SYNC, + /* + ** let the target fetch our answer. + */ + SCR_SET (SCR_ATN), + 0, + SCR_CLR (SCR_ACK), + 0, + + SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)), + SIR_NEGO_PROTO, + /* + ** Send the MSG_EXT_SDTR + */ + SCR_MOVE_ABS (5) ^ SCR_MSG_OUT, + NADDR (msgout), + SCR_CLR (SCR_ATN), + 0, + SCR_COPY (1), + RADDR (sfbr), + NADDR (lastmsg), + SCR_JUMP, + PADDR (msg_out_done), + +}/*-------------------------< MSG_OUT_ABORT >-------------*/,{ + /* + ** After ABORT message, + ** + ** expect an immediate disconnect, ... + */ + SCR_REG_REG (scntl2, SCR_AND, 0x7f), + 0, + SCR_CLR (SCR_ACK|SCR_ATN), + 0, + SCR_WAIT_DISC, + 0, + /* + ** ... and set the status to "ABORTED" + */ + SCR_LOAD_REG (HS_REG, HS_ABORTED), + 0, + SCR_JUMP, + PADDR (cleanup), + +}/*-------------------------< GETCC >-----------------------*/,{ + /* + ** The ncr doesn't have an indirect load + ** or store command. So we have to + ** copy part of the control block to a + ** fixed place, where we can modify it. + ** + ** We patch the address part of a COPY command + ** with the address of the dsa register ... + */ + SCR_COPY_F (4), + RADDR (dsa), + PADDRH (getcc1), + /* + ** ... then we do the actual copy. + */ + SCR_COPY (sizeof (struct head)), +}/*-------------------------< GETCC1 >----------------------*/,{ + 0, + NADDR (header), + /* + ** Initialize the status registers + */ + SCR_COPY (4), + NADDR (header.status), + RADDR (scr0), +}/*-------------------------< GETCC2 >----------------------*/,{ + /* + ** Get the condition code from a target. + ** + ** DSA points to a data structure. + ** Set TEMP to the script location + ** that receives the condition code. + ** + ** Because there is no script command + ** to load a longword into a register, + ** we use a CALL command. + */ +/*<<<*/ SCR_CALLR, + 24, + /* + ** Get the condition code. + */ + SCR_MOVE_TBL ^ SCR_DATA_IN, + offsetof (struct dsb, sense), + /* + ** No data phase may follow! + */ + SCR_CALL, + PADDR (checkatn), + SCR_JUMP, + PADDR (no_data), +/*>>>*/ + + /* + ** The CALL jumps to this point. + ** Prepare for a RESTORE_POINTER message. + ** Save the TEMP register into the saved pointer. + */ + SCR_COPY (4), + RADDR (temp), + NADDR (header.savep), + /* + ** Load scratcha, because in case of a selection timeout, + ** the host will expect a new value for startpos in + ** the scratcha register. + */ + SCR_COPY (4), + PADDR (startpos), + RADDR (scratcha), +#ifdef NCR_GETCC_WITHMSG + /* + ** If QUIRK_NOMSG is set, select without ATN. + ** and don't send a message. + */ + SCR_FROM_REG (QU_REG), + 0, + SCR_JUMP ^ IFTRUE (MASK (QUIRK_NOMSG, QUIRK_NOMSG)), + PADDRH(getcc3), + /* + ** Then try to connect to the target. + ** If we are reselected, special treatment + ** of the current job is required before + ** accepting the reselection. + */ + SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select), + PADDR(badgetcc), + /* + ** Send the IDENTIFY message. + ** In case of short transfer, remove ATN. + */ + SCR_MOVE_TBL ^ SCR_MSG_OUT, + offsetof (struct dsb, smsg2), + SCR_CLR (SCR_ATN), + 0, + /* + ** save the first byte of the message. + */ + SCR_COPY (1), + RADDR (sfbr), + NADDR (lastmsg), + SCR_JUMP, + PADDR (prepare2), + +#endif +}/*-------------------------< GETCC3 >----------------------*/,{ + /* + ** Try to connect to the target. + ** If we are reselected, special treatment + ** of the current job is required before + ** accepting the reselection. + ** + ** Silly target won't accept a message. + ** Select without ATN. + */ + SCR_SEL_TBL ^ offsetof (struct dsb, select), + PADDR(badgetcc), + /* + ** Force error if selection timeout + */ + SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)), + 0, + /* + ** don't negotiate. + */ + SCR_JUMP, + PADDR (prepare2), +}/*-------------------------< ABORTTAG >-------------------*/,{ + /* + ** Abort a bad reselection. + ** Set the message to ABORT vs. ABORT_TAG + */ + SCR_LOAD_REG (scratcha, MSG_ABORT_TAG), + 0, + SCR_JUMPR ^ IFFALSE (CARRYSET), + 8, +}/*-------------------------< ABORT >----------------------*/,{ + SCR_LOAD_REG (scratcha, MSG_ABORT), + 0, + SCR_COPY (1), + RADDR (scratcha), + NADDR (msgout), + SCR_SET (SCR_ATN), + 0, + SCR_CLR (SCR_ACK), + 0, + /* + ** and send it. + ** we expect an immediate disconnect + */ + SCR_REG_REG (scntl2, SCR_AND, 0x7f), + 0, + SCR_MOVE_ABS (1) ^ SCR_MSG_OUT, + NADDR (msgout), + SCR_COPY (1), + RADDR (sfbr), + NADDR (lastmsg), + SCR_CLR (SCR_ACK|SCR_ATN), + 0, + SCR_WAIT_DISC, + 0, + SCR_JUMP, + PADDR (start), +}/*-------------------------< SNOOPTEST >-------------------*/,{ + /* + ** Read the variable. + */ + SCR_COPY (4), + KVAR (KVAR_NCR_CACHE), + RADDR (scratcha), + /* + ** Write the variable. + */ + SCR_COPY (4), + RADDR (temp), + KVAR (KVAR_NCR_CACHE), + /* + ** Read back the variable. + */ + SCR_COPY (4), + KVAR (KVAR_NCR_CACHE), + RADDR (temp), +}/*-------------------------< SNOOPEND >-------------------*/,{ + /* + ** And stop. + */ + SCR_INT, + 99, +}/*--------------------------------------------------------*/ +}; + + +/*========================================================== +** +** +** Fill in #define dependent parts of the script +** +** +**========================================================== +*/ + +void ncr_script_fill (struct script * scr, struct scripth * scrh) +{ + int i; + ncrcmd *p; + + p = scrh->tryloop; + for (i=0; i<MAX_START; i++) { + *p++ =SCR_COPY (4); + *p++ =NADDR (squeue[i]); + *p++ =RADDR (dsa); + *p++ =SCR_CALL; + *p++ =PADDR (trysel); + }; + *p++ =SCR_JUMP; + *p++ =PADDRH(tryloop); + + assert ((char *)p == (char *)&scrh->tryloop + sizeof (scrh->tryloop)); + + p = scr->data_in; + + *p++ =SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)); + *p++ =PADDR (no_data); + *p++ =SCR_COPY (sizeof (ticks)); + *p++ =(ncrcmd) KVAR (KVAR_TICKS); + *p++ =NADDR (header.stamp.data); + *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN; + *p++ =offsetof (struct dsb, data[ 0]); + + for (i=1; i<MAX_SCATTER; i++) { + *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)); + *p++ =PADDR (checkatn); + *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN; + *p++ =offsetof (struct dsb, data[i]); + }; + + *p++ =SCR_CALL; + *p++ =PADDR (checkatn); + *p++ =SCR_JUMP; + *p++ =PADDR (no_data); + + assert ((char *)p == (char *)&scr->data_in + sizeof (scr->data_in)); + + p = scr->data_out; + + *p++ =SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_OUT)); + *p++ =PADDR (no_data); + *p++ =SCR_COPY (sizeof (ticks)); + *p++ =(ncrcmd) KVAR (KVAR_TICKS); + *p++ =NADDR (header.stamp.data); + *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT; + *p++ =offsetof (struct dsb, data[ 0]); + + for (i=1; i<MAX_SCATTER; i++) { + *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)); + *p++ =PADDR (dispatch); + *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT; + *p++ =offsetof (struct dsb, data[i]); + }; + + *p++ =SCR_CALL; + *p++ =PADDR (dispatch); + *p++ =SCR_JUMP; + *p++ =PADDR (no_data); + + assert ((char *)p == (char *)&scr->data_out + sizeof (scr->data_out)); +} + +/*========================================================== +** +** +** Copy and rebind a script. +** +** +**========================================================== +*/ + +static void ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len) +{ + ncrcmd opcode, new, old, tmp1, tmp2; + ncrcmd *start, *end; + int relocs, offset; + + start = src; + end = src + len/4; + offset = 0; + + while (src < end) { + + opcode = *src++; + WRITESCRIPT_OFF(dst, offset, opcode); + offset += 4; + + /* + ** If we forget to change the length + ** in struct script, a field will be + ** padded with 0. This is an illegal + ** command. + */ + + if (opcode == 0) { + printf ("%s: ERROR0 IN SCRIPT at %d.\n", + ncr_name(np), (int) (src-start-1)); + DELAY (1000000); + }; + + if (DEBUG_FLAGS & DEBUG_SCRIPT) + printf ("%p: <%x>\n", + (src-1), (unsigned)opcode); + + /* + ** We don't have to decode ALL commands + */ + switch (opcode >> 28) { + + case 0xc: + /* + ** COPY has TWO arguments. + */ + relocs = 2; + tmp1 = src[0]; + if ((tmp1 & RELOC_MASK) == RELOC_KVAR) + tmp1 = 0; + tmp2 = src[1]; + if ((tmp2 & RELOC_MASK) == RELOC_KVAR) + tmp2 = 0; + if ((tmp1 ^ tmp2) & 3) { + printf ("%s: ERROR1 IN SCRIPT at %d.\n", + ncr_name(np), (int) (src-start-1)); + DELAY (1000000); + } + /* + ** If PREFETCH feature not enabled, remove + ** the NO FLUSH bit if present. + */ + if ((opcode & SCR_NO_FLUSH) && !(np->features&FE_PFEN)) + WRITESCRIPT_OFF(dst, offset - 4, + (opcode & ~SCR_NO_FLUSH)); + break; + + case 0x0: + /* + ** MOVE (absolute address) + */ + relocs = 1; + break; + + case 0x8: + /* + ** JUMP / CALL + ** dont't relocate if relative :-) + */ + if (opcode & 0x00800000) + relocs = 0; + else + relocs = 1; + break; + + case 0x4: + case 0x5: + case 0x6: + case 0x7: + relocs = 1; + break; + + default: + relocs = 0; + break; + }; + + if (relocs) { + while (relocs--) { + old = *src++; + + switch (old & RELOC_MASK) { + case RELOC_REGISTER: + new = (old & ~RELOC_MASK) + rman_get_start(np->reg_res); + break; + case RELOC_LABEL: + new = (old & ~RELOC_MASK) + np->p_script; + break; + case RELOC_LABELH: + new = (old & ~RELOC_MASK) + np->p_scripth; + break; + case RELOC_SOFTC: + new = (old & ~RELOC_MASK) + vtophys(np); + break; + case RELOC_KVAR: + if (((old & ~RELOC_MASK) < + SCRIPT_KVAR_FIRST) || + ((old & ~RELOC_MASK) > + SCRIPT_KVAR_LAST)) + panic("ncr KVAR out of range"); + new = vtophys(script_kvars[old & + ~RELOC_MASK]); + break; + case 0: + /* Don't relocate a 0 address. */ + if (old == 0) { + new = old; + break; + } + /* fall through */ + default: + panic("ncr_script_copy_and_bind: weird relocation %x @ %d\n", old, (int)(src - start)); + break; + } + + WRITESCRIPT_OFF(dst, offset, new); + offset += 4; + } + } else { + WRITESCRIPT_OFF(dst, offset, *src++); + offset += 4; + } + + }; +} + +/*========================================================== +** +** +** Auto configuration. +** +** +**========================================================== +*/ + +#if 0 +/*---------------------------------------------------------- +** +** Reduce the transfer length to the max value +** we can transfer safely. +** +** Reading a block greater then MAX_SIZE from the +** raw (character) device exercises a memory leak +** in the vm subsystem. This is common to ALL devices. +** We have submitted a description of this bug to +** <FreeBSD-bugs@freefall.cdrom.com>. +** It should be fixed in the current release. +** +**---------------------------------------------------------- +*/ + +void ncr_min_phys (struct buf *bp) +{ + if ((unsigned long)bp->b_bcount > MAX_SIZE) bp->b_bcount = MAX_SIZE; +} + +#endif + +#if 0 +/*---------------------------------------------------------- +** +** Maximal number of outstanding requests per target. +** +**---------------------------------------------------------- +*/ + +u_int32_t ncr_info (int unit) +{ + return (1); /* may be changed later */ +} + +#endif + +/*---------------------------------------------------------- +** +** NCR chip devices table and chip look up function. +** Features bit are defined in ncrreg.h. Is it the +** right place? +** +**---------------------------------------------------------- +*/ +typedef struct { + unsigned long device_id; + unsigned short minrevid; + char *name; + unsigned char maxburst; + unsigned char maxoffs; + unsigned char clock_divn; + unsigned int features; +} ncr_chip; + +static ncr_chip ncr_chip_table[] = { + {NCR_810_ID, 0x00, "ncr 53c810 fast10 scsi", 4, 8, 4, + FE_ERL} + , + {NCR_810_ID, 0x10, "ncr 53c810a fast10 scsi", 4, 8, 4, + FE_ERL|FE_LDSTR|FE_PFEN|FE_BOF} + , + {NCR_815_ID, 0x00, "ncr 53c815 fast10 scsi", 4, 8, 4, + FE_ERL|FE_BOF} + , + {NCR_820_ID, 0x00, "ncr 53c820 fast10 wide scsi", 4, 8, 4, + FE_WIDE|FE_ERL} + , + {NCR_825_ID, 0x00, "ncr 53c825 fast10 wide scsi", 4, 8, 4, + FE_WIDE|FE_ERL|FE_BOF} + , + {NCR_825_ID, 0x10, "ncr 53c825a fast10 wide scsi", 7, 8, 4, + FE_WIDE|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} + , + {NCR_860_ID, 0x00, "ncr 53c860 fast20 scsi", 4, 8, 5, + FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_LDSTR|FE_PFEN} + , + {NCR_875_ID, 0x00, "ncr 53c875 fast20 wide scsi", 7, 16, 5, + FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} + , + {NCR_875_ID, 0x02, "ncr 53c875 fast20 wide scsi", 7, 16, 5, + FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} + , + {NCR_875_ID2, 0x00, "ncr 53c875j fast20 wide scsi", 7, 16, 5, + FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} + , + {NCR_885_ID, 0x00, "ncr 53c885 fast20 wide scsi", 7, 16, 5, + FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} + , + {NCR_895_ID, 0x00, "ncr 53c895 fast40 wide scsi", 7, 31, 7, + FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} + , + {NCR_896_ID, 0x00, "ncr 53c896 fast40 wide scsi", 7, 31, 7, + FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} + , + {NCR_895A_ID, 0x00, "ncr 53c895a fast40 wide scsi", 7, 31, 7, + FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} + , + {NCR_1510D_ID, 0x00, "ncr 53c1510d fast40 wide scsi", 7, 31, 7, + FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} +}; + +static int ncr_chip_lookup(u_long device_id, u_char revision_id) +{ + int i, found; + + found = -1; + for (i = 0; i < sizeof(ncr_chip_table)/sizeof(ncr_chip_table[0]); i++) { + if (device_id == ncr_chip_table[i].device_id && + ncr_chip_table[i].minrevid <= revision_id) { + if (found < 0 || + ncr_chip_table[found].minrevid + < ncr_chip_table[i].minrevid) { + found = i; + } + } + } + return found; +} + +/*---------------------------------------------------------- +** +** Probe the hostadapter. +** +**---------------------------------------------------------- +*/ + + + +static int ncr_probe (device_t dev) +{ + int i; + + i = ncr_chip_lookup(pci_get_devid(dev), pci_get_revid(dev)); + if (i >= 0) { + device_set_desc(dev, ncr_chip_table[i].name); + return (0); + } + + return (ENXIO); +} + + + +/*========================================================== +** +** NCR chip clock divisor table. +** Divisors are multiplied by 10,000,000 in order to make +** calculations more simple. +** +**========================================================== +*/ + +#define _5M 5000000 +static u_long div_10M[] = + {2*_5M, 3*_5M, 4*_5M, 6*_5M, 8*_5M, 12*_5M, 16*_5M}; + +/*=============================================================== +** +** NCR chips allow burst lengths of 2, 4, 8, 16, 32, 64, 128 +** transfers. 32,64,128 are only supported by 875 and 895 chips. +** We use log base 2 (burst length) as internal code, with +** value 0 meaning "burst disabled". +** +**=============================================================== +*/ + +/* + * Burst length from burst code. + */ +#define burst_length(bc) (!(bc))? 0 : 1 << (bc) + +/* + * Burst code from io register bits. + */ +#define burst_code(dmode, ctest4, ctest5) \ + (ctest4) & 0x80? 0 : (((dmode) & 0xc0) >> 6) + ((ctest5) & 0x04) + 1 + +/* + * Set initial io register bits from burst code. + */ +static void +ncr_init_burst(ncb_p np, u_char bc) +{ + np->rv_ctest4 &= ~0x80; + np->rv_dmode &= ~(0x3 << 6); + np->rv_ctest5 &= ~0x4; + + if (!bc) { + np->rv_ctest4 |= 0x80; + } + else { + --bc; + np->rv_dmode |= ((bc & 0x3) << 6); + np->rv_ctest5 |= (bc & 0x4); + } +} + +/*========================================================== +** +** +** Auto configuration: attach and init a host adapter. +** +** +**========================================================== +*/ + + +static int +ncr_attach (device_t dev) +{ + ncb_p np = (struct ncb*) device_get_softc(dev); + u_char rev = 0; + u_long period; + int i, rid; + u_int8_t usrsync; + u_int8_t usrwide; + struct cam_devq *devq; + + /* + ** allocate and initialize structures. + */ + + np->unit = device_get_unit(dev); + + /* + ** Try to map the controller chip to + ** virtual and physical memory. + */ + + np->reg_rid = 0x14; + np->reg_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &np->reg_rid, + 0, ~0, 1, RF_ACTIVE); + if (!np->reg_res) { + device_printf(dev, "could not map memory\n"); + return ENXIO; + } + + /* + ** Make the controller's registers available. + ** Now the INB INW INL OUTB OUTW OUTL macros + ** can be used safely. + */ + + np->bst = rman_get_bustag(np->reg_res); + np->bsh = rman_get_bushandle(np->reg_res); + + +#ifdef NCR_IOMAPPED + /* + ** Try to map the controller chip into iospace. + */ + + if (!pci_map_port (config_id, 0x10, &np->port)) + return; +#endif + + + /* + ** Save some controller register default values + */ + + np->rv_scntl3 = INB(nc_scntl3) & 0x77; + np->rv_dmode = INB(nc_dmode) & 0xce; + np->rv_dcntl = INB(nc_dcntl) & 0xa9; + np->rv_ctest3 = INB(nc_ctest3) & 0x01; + np->rv_ctest4 = INB(nc_ctest4) & 0x88; + np->rv_ctest5 = INB(nc_ctest5) & 0x24; + np->rv_gpcntl = INB(nc_gpcntl); + np->rv_stest2 = INB(nc_stest2) & 0x20; + + if (bootverbose >= 2) { + printf ("\tBIOS values: SCNTL3:%02x DMODE:%02x DCNTL:%02x\n", + np->rv_scntl3, np->rv_dmode, np->rv_dcntl); + printf ("\t CTEST3:%02x CTEST4:%02x CTEST5:%02x\n", + np->rv_ctest3, np->rv_ctest4, np->rv_ctest5); + } + + np->rv_dcntl |= NOCOM; + + /* + ** Do chip dependent initialization. + */ + + rev = pci_get_revid(dev); + + /* + ** Get chip features from chips table. + */ + i = ncr_chip_lookup(pci_get_devid(dev), rev); + + if (i >= 0) { + np->maxburst = ncr_chip_table[i].maxburst; + np->maxoffs = ncr_chip_table[i].maxoffs; + np->clock_divn = ncr_chip_table[i].clock_divn; + np->features = ncr_chip_table[i].features; + } else { /* Should'nt happen if probe() is ok */ + np->maxburst = 4; + np->maxoffs = 8; + np->clock_divn = 4; + np->features = FE_ERL; + } + + np->maxwide = np->features & FE_WIDE ? 1 : 0; + np->clock_khz = np->features & FE_CLK80 ? 80000 : 40000; + if (np->features & FE_QUAD) np->multiplier = 4; + else if (np->features & FE_DBLR) np->multiplier = 2; + else np->multiplier = 1; + + /* + ** Get the frequency of the chip's clock. + ** Find the right value for scntl3. + */ + if (np->features & (FE_ULTRA|FE_ULTRA2)) + ncr_getclock(np, np->multiplier); + +#ifdef NCR_TEKRAM_EEPROM + if (bootverbose) { + printf ("%s: Tekram EEPROM read %s\n", + ncr_name(np), + read_tekram_eeprom (np, NULL) ? + "succeeded" : "failed"); + } +#endif /* NCR_TEKRAM_EEPROM */ + + /* + * If scntl3 != 0, we assume BIOS is present. + */ + if (np->rv_scntl3) + np->features |= FE_BIOS; + + /* + * Divisor to be used for async (timer pre-scaler). + */ + i = np->clock_divn - 1; + while (i >= 0) { + --i; + if (10ul * SCSI_NCR_MIN_ASYNC * np->clock_khz > div_10M[i]) { + ++i; + break; + } + } + np->rv_scntl3 = i+1; + + /* + * Minimum synchronous period factor supported by the chip. + * Btw, 'period' is in tenths of nanoseconds. + */ + + period = (4 * div_10M[0] + np->clock_khz - 1) / np->clock_khz; + if (period <= 250) np->minsync = 10; + else if (period <= 303) np->minsync = 11; + else if (period <= 500) np->minsync = 12; + else np->minsync = (period + 40 - 1) / 40; + + /* + * Check against chip SCSI standard support (SCSI-2,ULTRA,ULTRA2). + */ + + if (np->minsync < 25 && !(np->features & (FE_ULTRA|FE_ULTRA2))) + np->minsync = 25; + else if (np->minsync < 12 && !(np->features & FE_ULTRA2)) + np->minsync = 12; + + /* + * Maximum synchronous period factor supported by the chip. + */ + + period = (11 * div_10M[np->clock_divn - 1]) / (4 * np->clock_khz); + np->maxsync = period > 2540 ? 254 : period / 10; + + /* + * Now, some features available with Symbios compatible boards. + * LED support through GPIO0 and DIFF support. + */ + +#ifdef SCSI_NCR_SYMBIOS_COMPAT + if (!(np->rv_gpcntl & 0x01)) + np->features |= FE_LED0; +#if 0 /* Not safe enough without NVRAM support or user settable option */ + if (!(INB(nc_gpreg) & 0x08)) + np->features |= FE_DIFF; +#endif +#endif /* SCSI_NCR_SYMBIOS_COMPAT */ + + /* + * Prepare initial IO registers settings. + * Trust BIOS only if we believe we have one and if we want to. + */ +#ifdef SCSI_NCR_TRUST_BIOS + if (!(np->features & FE_BIOS)) { +#else + if (1) { +#endif + np->rv_dmode = 0; + np->rv_dcntl = NOCOM; + np->rv_ctest3 = 0; + np->rv_ctest4 = MPEE; + np->rv_ctest5 = 0; + np->rv_stest2 = 0; + + if (np->features & FE_ERL) + np->rv_dmode |= ERL; /* Enable Read Line */ + if (np->features & FE_BOF) + np->rv_dmode |= BOF; /* Burst Opcode Fetch */ + if (np->features & FE_ERMP) + np->rv_dmode |= ERMP; /* Enable Read Multiple */ + if (np->features & FE_CLSE) + np->rv_dcntl |= CLSE; /* Cache Line Size Enable */ + if (np->features & FE_WRIE) + np->rv_ctest3 |= WRIE; /* Write and Invalidate */ + if (np->features & FE_PFEN) + np->rv_dcntl |= PFEN; /* Prefetch Enable */ + if (np->features & FE_DFS) + np->rv_ctest5 |= DFS; /* Dma Fifo Size */ + if (np->features & FE_DIFF) + np->rv_stest2 |= 0x20; /* Differential mode */ + ncr_init_burst(np, np->maxburst); /* Max dwords burst length */ + } else { + np->maxburst = + burst_code(np->rv_dmode, np->rv_ctest4, np->rv_ctest5); + } + + /* + ** Get on-chip SRAM address, if supported + */ + if ((np->features & FE_RAM) && sizeof(struct script) <= 4096) { + np->sram_rid = 0x18; + np->sram_res = bus_alloc_resource(dev, SYS_RES_MEMORY, + &np->sram_rid, + 0, ~0, 1, RF_ACTIVE); + } + + /* + ** Allocate structure for script relocation. + */ + if (np->sram_res != NULL) { + np->script = NULL; + np->p_script = rman_get_start(np->sram_res); + np->bst2 = rman_get_bustag(np->sram_res); + np->bsh2 = rman_get_bushandle(np->sram_res); + } else if (sizeof (struct script) > PAGE_SIZE) { + np->script = (struct script*) vm_page_alloc_contig + (round_page(sizeof (struct script)), + 0, 0xffffffff, PAGE_SIZE); + } else { + np->script = (struct script *) + malloc (sizeof (struct script), M_DEVBUF, M_WAITOK); + } + + /* XXX JGibbs - Use contigmalloc */ + if (sizeof (struct scripth) > PAGE_SIZE) { + np->scripth = (struct scripth*) vm_page_alloc_contig + (round_page(sizeof (struct scripth)), + 0, 0xffffffff, PAGE_SIZE); + } else + { + np->scripth = (struct scripth *) + malloc (sizeof (struct scripth), M_DEVBUF, M_WAITOK); + } + +#ifdef SCSI_NCR_PCI_CONFIG_FIXUP + /* + ** If cache line size is enabled, check PCI config space and + ** try to fix it up if necessary. + */ +#ifdef PCIR_CACHELNSZ /* To be sure that new PCI stuff is present */ + { + u_char cachelnsz = pci_read_config(dev, PCIR_CACHELNSZ, 1); + u_short command = pci_read_config(dev, PCIR_COMMAND, 2); + + if (!cachelnsz) { + cachelnsz = 8; + printf("%s: setting PCI cache line size register to %d.\n", + ncr_name(np), (int)cachelnsz); + pci_write_config(dev, PCIR_CACHELNSZ, cachelnsz, 1); + } + + if (!(command & (1<<4))) { + command |= (1<<4); + printf("%s: setting PCI command write and invalidate.\n", + ncr_name(np)); + pci_write_config(dev, PCIR_COMMAND, command, 2); + } + } +#endif /* PCIR_CACHELNSZ */ + +#endif /* SCSI_NCR_PCI_CONFIG_FIXUP */ + + /* Initialize per-target user settings */ + usrsync = 0; + if (SCSI_NCR_DFLT_SYNC) { + usrsync = SCSI_NCR_DFLT_SYNC; + if (usrsync > np->maxsync) + usrsync = np->maxsync; + if (usrsync < np->minsync) + usrsync = np->minsync; + }; + + usrwide = (SCSI_NCR_MAX_WIDE); + if (usrwide > np->maxwide) usrwide=np->maxwide; + + for (i=0;i<MAX_TARGET;i++) { + tcb_p tp = &np->target[i]; + + tp->tinfo.user.period = usrsync; + tp->tinfo.user.offset = usrsync != 0 ? np->maxoffs : 0; + tp->tinfo.user.width = usrwide; + tp->tinfo.disc_tag = NCR_CUR_DISCENB + | NCR_CUR_TAGENB + | NCR_USR_DISCENB + | NCR_USR_TAGENB; + } + + /* + ** Bells and whistles ;-) + */ + if (bootverbose) + printf("%s: minsync=%d, maxsync=%d, maxoffs=%d, %d dwords burst, %s dma fifo\n", + ncr_name(np), np->minsync, np->maxsync, np->maxoffs, + burst_length(np->maxburst), + (np->rv_ctest5 & DFS) ? "large" : "normal"); + + /* + ** Print some complementary information that can be helpfull. + */ + if (bootverbose) + printf("%s: %s, %s IRQ driver%s\n", + ncr_name(np), + np->rv_stest2 & 0x20 ? "differential" : "single-ended", + np->rv_dcntl & IRQM ? "totem pole" : "open drain", + np->sram_res ? ", using on-chip SRAM" : ""); + + /* + ** Patch scripts to physical addresses + */ + ncr_script_fill (&script0, &scripth0); + + if (np->script) + np->p_script = vtophys(np->script); + np->p_scripth = vtophys(np->scripth); + + ncr_script_copy_and_bind (np, (ncrcmd *) &script0, + (ncrcmd *) np->script, sizeof(struct script)); + + ncr_script_copy_and_bind (np, (ncrcmd *) &scripth0, + (ncrcmd *) np->scripth, sizeof(struct scripth)); + + /* + ** Patch the script for LED support. + */ + + if (np->features & FE_LED0) { + WRITESCRIPT(reselect[0], SCR_REG_REG(gpreg, SCR_OR, 0x01)); + WRITESCRIPT(reselect1[0], SCR_REG_REG(gpreg, SCR_AND, 0xfe)); + WRITESCRIPT(reselect2[0], SCR_REG_REG(gpreg, SCR_AND, 0xfe)); + } + + /* + ** init data structure + */ + + np->jump_tcb.l_cmd = SCR_JUMP; + np->jump_tcb.l_paddr = NCB_SCRIPTH_PHYS (np, abort); + + /* + ** Get SCSI addr of host adapter (set by bios?). + */ + + np->myaddr = INB(nc_scid) & 0x07; + if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR; + +#ifdef NCR_DUMP_REG + /* + ** Log the initial register contents + */ + { + int reg; + for (reg=0; reg<256; reg+=4) { + if (reg%16==0) printf ("reg[%2x]", reg); + printf (" %08x", (int)pci_conf_read (config_id, reg)); + if (reg%16==12) printf ("\n"); + } + } +#endif /* NCR_DUMP_REG */ + + /* + ** Reset chip. + */ + + OUTB (nc_istat, SRST); + DELAY (1000); + OUTB (nc_istat, 0 ); + + + /* + ** Now check the cache handling of the pci chipset. + */ + + if (ncr_snooptest (np)) { + printf ("CACHE INCORRECTLY CONFIGURED.\n"); + return EINVAL; + }; + + /* + ** Install the interrupt handler. + */ + + rid = 0; + np->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + if (np->irq_res == NULL) { + device_printf(dev, + "interruptless mode: reduced performance.\n"); + } else { + bus_setup_intr(dev, np->irq_res, INTR_TYPE_CAM | INTR_ENTROPY, + ncr_intr, np, &np->irq_handle); + } + + /* + ** Create the device queue. We only allow MAX_START-1 concurrent + ** transactions so we can be sure to have one element free in our + ** start queue to reset to the idle loop. + */ + devq = cam_simq_alloc(MAX_START - 1); + if (devq == NULL) + return ENOMEM; + + /* + ** Now tell the generic SCSI layer + ** about our bus. + */ + np->sim = cam_sim_alloc(ncr_action, ncr_poll, "ncr", np, np->unit, + 1, MAX_TAGS, devq); + if (np->sim == NULL) { + cam_simq_free(devq); + return ENOMEM; + } + + + if (xpt_bus_register(np->sim, 0) != CAM_SUCCESS) { + cam_sim_free(np->sim, /*free_devq*/ TRUE); + return ENOMEM; + } + + if (xpt_create_path(&np->path, /*periph*/NULL, + cam_sim_path(np->sim), CAM_TARGET_WILDCARD, + CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + xpt_bus_deregister(cam_sim_path(np->sim)); + cam_sim_free(np->sim, /*free_devq*/TRUE); + return ENOMEM; + } + + /* + ** start the timeout daemon + */ + ncr_timeout (np); + np->lasttime=0; + + return 0; +} + +/*========================================================== +** +** +** Process pending device interrupts. +** +** +**========================================================== +*/ + +static void +ncr_intr(vnp) + void *vnp; +{ + ncb_p np = vnp; + int oldspl = splcam(); + + if (DEBUG_FLAGS & DEBUG_TINY) printf ("["); + + if (INB(nc_istat) & (INTF|SIP|DIP)) { + /* + ** Repeat until no outstanding ints + */ + do { + ncr_exception (np); + } while (INB(nc_istat) & (INTF|SIP|DIP)); + + np->ticks = 100; + }; + + if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n"); + + splx (oldspl); +} + +/*========================================================== +** +** +** Start execution of a SCSI command. +** This is called from the generic SCSI driver. +** +** +**========================================================== +*/ + +static void +ncr_action (struct cam_sim *sim, union ccb *ccb) +{ + ncb_p np; + + np = (ncb_p) cam_sim_softc(sim); + + switch (ccb->ccb_h.func_code) { + /* Common cases first */ + case XPT_SCSI_IO: /* Execute the requested I/O operation */ + { + nccb_p cp; + lcb_p lp; + tcb_p tp; + int oldspl; + struct ccb_scsiio *csio; + u_int8_t *msgptr; + u_int msglen; + u_int msglen2; + int segments; + u_int8_t nego; + u_int8_t idmsg; + u_int8_t qidx; + + tp = &np->target[ccb->ccb_h.target_id]; + csio = &ccb->csio; + + oldspl = splcam(); + + /* + * Last time we need to check if this CCB needs to + * be aborted. + */ + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_INPROG) { + xpt_done(ccb); + splx(oldspl); + return; + } + ccb->ccb_h.status |= CAM_SIM_QUEUED; + + /*--------------------------------------------------- + ** + ** Assign an nccb / bind ccb + ** + **---------------------------------------------------- + */ + cp = ncr_get_nccb (np, ccb->ccb_h.target_id, + ccb->ccb_h.target_lun); + if (cp == NULL) { + /* XXX JGibbs - Freeze SIMQ */ + ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + xpt_done(ccb); + return; + }; + + cp->ccb = ccb; + + /*--------------------------------------------------- + ** + ** timestamp + ** + **---------------------------------------------------- + */ + /* + ** XXX JGibbs - Isn't this expensive + ** enough to be conditionalized?? + */ + + bzero (&cp->phys.header.stamp, sizeof (struct tstamp)); + cp->phys.header.stamp.start = ticks; + + nego = 0; + if (tp->nego_cp == NULL) { + + if (tp->tinfo.current.width + != tp->tinfo.goal.width) { + tp->nego_cp = cp; + nego = NS_WIDE; + } else if ((tp->tinfo.current.period + != tp->tinfo.goal.period) + || (tp->tinfo.current.offset + != tp->tinfo.goal.offset)) { + tp->nego_cp = cp; + nego = NS_SYNC; + }; + }; + + /*--------------------------------------------------- + ** + ** choose a new tag ... + ** + **---------------------------------------------------- + */ + lp = tp->lp[ccb->ccb_h.target_lun]; + + if ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0 + && (ccb->csio.tag_action != CAM_TAG_ACTION_NONE) + && (nego == 0)) { + /* + ** assign a tag to this nccb + */ + while (!cp->tag) { + nccb_p cp2 = lp->next_nccb; + lp->lasttag = lp->lasttag % 255 + 1; + while (cp2 && cp2->tag != lp->lasttag) + cp2 = cp2->next_nccb; + if (cp2) continue; + cp->tag=lp->lasttag; + if (DEBUG_FLAGS & DEBUG_TAGS) { + PRINT_ADDR(ccb); + printf ("using tag #%d.\n", cp->tag); + }; + }; + } else { + cp->tag=0; + }; + + /*---------------------------------------------------- + ** + ** Build the identify / tag / sdtr message + ** + **---------------------------------------------------- + */ + idmsg = MSG_IDENTIFYFLAG | ccb->ccb_h.target_lun; + if (tp->tinfo.disc_tag & NCR_CUR_DISCENB) + idmsg |= MSG_IDENTIFY_DISCFLAG; + + msgptr = cp->scsi_smsg; + msglen = 0; + msgptr[msglen++] = idmsg; + + if (cp->tag) { + msgptr[msglen++] = ccb->csio.tag_action; + msgptr[msglen++] = cp->tag; + } + + switch (nego) { + case NS_SYNC: + msgptr[msglen++] = MSG_EXTENDED; + msgptr[msglen++] = MSG_EXT_SDTR_LEN; + msgptr[msglen++] = MSG_EXT_SDTR; + msgptr[msglen++] = tp->tinfo.goal.period; + msgptr[msglen++] = tp->tinfo.goal.offset;; + if (DEBUG_FLAGS & DEBUG_NEGO) { + PRINT_ADDR(ccb); + printf ("sync msgout: "); + ncr_show_msg (&cp->scsi_smsg [msglen-5]); + printf (".\n"); + }; + break; + case NS_WIDE: + msgptr[msglen++] = MSG_EXTENDED; + msgptr[msglen++] = MSG_EXT_WDTR_LEN; + msgptr[msglen++] = MSG_EXT_WDTR; + msgptr[msglen++] = tp->tinfo.goal.width; + if (DEBUG_FLAGS & DEBUG_NEGO) { + PRINT_ADDR(ccb); + printf ("wide msgout: "); + ncr_show_msg (&cp->scsi_smsg [msglen-4]); + printf (".\n"); + }; + break; + }; + + /*---------------------------------------------------- + ** + ** Build the identify message for getcc. + ** + **---------------------------------------------------- + */ + + cp->scsi_smsg2 [0] = idmsg; + msglen2 = 1; + + /*---------------------------------------------------- + ** + ** Build the data descriptors + ** + **---------------------------------------------------- + */ + + /* XXX JGibbs - Handle other types of I/O */ + if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { + segments = ncr_scatter(&cp->phys, + (vm_offset_t)csio->data_ptr, + (vm_size_t)csio->dxfer_len); + + if (segments < 0) { + ccb->ccb_h.status = CAM_REQ_TOO_BIG; + ncr_free_nccb(np, cp); + splx(oldspl); + xpt_done(ccb); + return; + } + if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_in); + cp->phys.header.goalp = cp->phys.header.savep +20 +segments*16; + } else { /* CAM_DIR_OUT */ + cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_out); + cp->phys.header.goalp = cp->phys.header.savep +20 +segments*16; + } + } else { + cp->phys.header.savep = NCB_SCRIPT_PHYS (np, no_data); + cp->phys.header.goalp = cp->phys.header.savep; + } + + cp->phys.header.lastp = cp->phys.header.savep; + + + /*---------------------------------------------------- + ** + ** fill in nccb + ** + **---------------------------------------------------- + ** + ** + ** physical -> virtual backlink + ** Generic SCSI command + */ + cp->phys.header.cp = cp; + /* + ** Startqueue + */ + cp->phys.header.launch.l_paddr = NCB_SCRIPT_PHYS (np, select); + cp->phys.header.launch.l_cmd = SCR_JUMP; + /* + ** select + */ + cp->phys.select.sel_id = ccb->ccb_h.target_id; + cp->phys.select.sel_scntl3 = tp->tinfo.wval; + cp->phys.select.sel_sxfer = tp->tinfo.sval; + /* + ** message + */ + cp->phys.smsg.addr = CCB_PHYS (cp, scsi_smsg); + cp->phys.smsg.size = msglen; + + cp->phys.smsg2.addr = CCB_PHYS (cp, scsi_smsg2); + cp->phys.smsg2.size = msglen2; + /* + ** command + */ + /* XXX JGibbs - Support other command types */ + cp->phys.cmd.addr = vtophys (csio->cdb_io.cdb_bytes); + cp->phys.cmd.size = csio->cdb_len; + /* + ** sense command + */ + cp->phys.scmd.addr = CCB_PHYS (cp, sensecmd); + cp->phys.scmd.size = 6; + /* + ** patch requested size into sense command + */ + cp->sensecmd[0] = 0x03; + cp->sensecmd[1] = ccb->ccb_h.target_lun << 5; + cp->sensecmd[4] = sizeof(struct scsi_sense_data); + cp->sensecmd[4] = csio->sense_len; + /* + ** sense data + */ + cp->phys.sense.addr = vtophys (&csio->sense_data); + cp->phys.sense.size = csio->sense_len; + /* + ** status + */ + cp->actualquirks = QUIRK_NOMSG; + cp->host_status = nego ? HS_NEGOTIATE : HS_BUSY; + cp->s_status = SCSI_STATUS_ILLEGAL; + cp->parity_status = 0; + + cp->xerr_status = XE_OK; + cp->sync_status = tp->tinfo.sval; + cp->nego_status = nego; + cp->wide_status = tp->tinfo.wval; + + /*---------------------------------------------------- + ** + ** Critical region: start this job. + ** + **---------------------------------------------------- + */ + + /* + ** reselect pattern and activate this job. + */ + + cp->jump_nccb.l_cmd = (SCR_JUMP ^ IFFALSE (DATA (cp->tag))); + cp->tlimit = time_second + + ccb->ccb_h.timeout / 1000 + 2; + cp->magic = CCB_MAGIC; + + /* + ** insert into start queue. + */ + + qidx = np->squeueput + 1; + if (qidx >= MAX_START) qidx=0; + np->squeue [qidx ] = NCB_SCRIPT_PHYS (np, idle); + np->squeue [np->squeueput] = CCB_PHYS (cp, phys); + np->squeueput = qidx; + + if(DEBUG_FLAGS & DEBUG_QUEUE) + printf("%s: queuepos=%d tryoffset=%d.\n", + ncr_name (np), np->squeueput, + (unsigned)(READSCRIPT(startpos[0]) - + (NCB_SCRIPTH_PHYS (np, tryloop)))); + + /* + ** Script processor may be waiting for reselect. + ** Wake it up. + */ + OUTB (nc_istat, SIGP); + + /* + ** and reenable interrupts + */ + splx (oldspl); + break; + } + case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ + case XPT_EN_LUN: /* Enable LUN as a target */ + case XPT_TARGET_IO: /* Execute target I/O request */ + case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ + case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ + case XPT_ABORT: /* Abort the specified CCB */ + /* XXX Implement */ + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + case XPT_SET_TRAN_SETTINGS: + { + struct ccb_trans_settings *cts; + tcb_p tp; + u_int update_type; + int s; + + cts = &ccb->cts; + update_type = 0; + if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) + update_type |= NCR_TRANS_GOAL; + if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) + update_type |= NCR_TRANS_USER; + + s = splcam(); + tp = &np->target[ccb->ccb_h.target_id]; + /* Tag and disc enables */ + if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { + if (update_type & NCR_TRANS_GOAL) { + if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) + tp->tinfo.disc_tag |= NCR_CUR_DISCENB; + else + tp->tinfo.disc_tag &= ~NCR_CUR_DISCENB; + } + + if (update_type & NCR_TRANS_USER) { + if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) + tp->tinfo.disc_tag |= NCR_USR_DISCENB; + else + tp->tinfo.disc_tag &= ~NCR_USR_DISCENB; + } + + } + + if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { + if (update_type & NCR_TRANS_GOAL) { + if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) + tp->tinfo.disc_tag |= NCR_CUR_TAGENB; + else + tp->tinfo.disc_tag &= ~NCR_CUR_TAGENB; + } + + if (update_type & NCR_TRANS_USER) { + if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) + tp->tinfo.disc_tag |= NCR_USR_TAGENB; + else + tp->tinfo.disc_tag &= ~NCR_USR_TAGENB; + } + } + + /* Filter bus width and sync negotiation settings */ + if ((cts->valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) { + if (cts->bus_width > np->maxwide) + cts->bus_width = np->maxwide; + } + + if (((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) + || ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0)) { + if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) { + if (cts->sync_period != 0 + && (cts->sync_period < np->minsync)) + cts->sync_period = np->minsync; + } + if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) { + if (cts->sync_offset == 0) + cts->sync_period = 0; + if (cts->sync_offset > np->maxoffs) + cts->sync_offset = np->maxoffs; + } + } + if ((update_type & NCR_TRANS_USER) != 0) { + if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) + tp->tinfo.user.period = cts->sync_period; + if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) + tp->tinfo.user.offset = cts->sync_offset; + if ((cts->valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) + tp->tinfo.user.width = cts->bus_width; + } + if ((update_type & NCR_TRANS_GOAL) != 0) { + if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) + tp->tinfo.goal.period = cts->sync_period; + + if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) + tp->tinfo.goal.offset = cts->sync_offset; + + if ((cts->valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) + tp->tinfo.goal.width = cts->bus_width; + } + splx(s); + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } + case XPT_GET_TRAN_SETTINGS: + /* Get default/user set transfer settings for the target */ + { + struct ccb_trans_settings *cts; + struct ncr_transinfo *tinfo; + tcb_p tp; + int s; + + cts = &ccb->cts; + tp = &np->target[ccb->ccb_h.target_id]; + + s = splcam(); + if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { + tinfo = &tp->tinfo.current; + if (tp->tinfo.disc_tag & NCR_CUR_DISCENB) + cts->flags |= CCB_TRANS_DISC_ENB; + else + cts->flags &= ~CCB_TRANS_DISC_ENB; + + if (tp->tinfo.disc_tag & NCR_CUR_TAGENB) + cts->flags |= CCB_TRANS_TAG_ENB; + else + cts->flags &= ~CCB_TRANS_TAG_ENB; + } else { + tinfo = &tp->tinfo.user; + if (tp->tinfo.disc_tag & NCR_USR_DISCENB) + cts->flags |= CCB_TRANS_DISC_ENB; + else + cts->flags &= ~CCB_TRANS_DISC_ENB; + + if (tp->tinfo.disc_tag & NCR_USR_TAGENB) + cts->flags |= CCB_TRANS_TAG_ENB; + else + cts->flags &= ~CCB_TRANS_TAG_ENB; + } + + cts->sync_period = tinfo->period; + cts->sync_offset = tinfo->offset; + cts->bus_width = tinfo->width; + + splx(s); + + cts->valid = CCB_TRANS_SYNC_RATE_VALID + | CCB_TRANS_SYNC_OFFSET_VALID + | CCB_TRANS_BUS_WIDTH_VALID + | CCB_TRANS_DISC_VALID + | CCB_TRANS_TQ_VALID; + + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } + case XPT_CALC_GEOMETRY: + { + struct ccb_calc_geometry *ccg; + u_int32_t size_mb; + u_int32_t secs_per_cylinder; + int extended; + + /* XXX JGibbs - I'm sure the NCR uses a different strategy, + * but it should be able to deal with Adaptec + * geometry too. + */ + extended = 1; + ccg = &ccb->ccg; + size_mb = ccg->volume_size + / ((1024L * 1024L) / ccg->block_size); + + if (size_mb > 1024 && extended) { + ccg->heads = 255; + ccg->secs_per_track = 63; + } else { + ccg->heads = 64; + ccg->secs_per_track = 32; + } + secs_per_cylinder = ccg->heads * ccg->secs_per_track; + ccg->cylinders = ccg->volume_size / secs_per_cylinder; + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } + case XPT_RESET_BUS: /* Reset the specified SCSI bus */ + { + OUTB (nc_scntl1, CRST); + ccb->ccb_h.status = CAM_REQ_CMP; + DELAY(10000); /* Wait until our interrupt handler sees it */ + xpt_done(ccb); + break; + } + case XPT_TERM_IO: /* Terminate the I/O process */ + /* XXX Implement */ + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + case XPT_PATH_INQ: /* Path routing inquiry */ + { + struct ccb_pathinq *cpi = &ccb->cpi; + + cpi->version_num = 1; /* XXX??? */ + cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE; + if ((np->features & FE_WIDE) != 0) + cpi->hba_inquiry |= PI_WIDE_16; + cpi->target_sprt = 0; + cpi->hba_misc = 0; + cpi->hba_eng_cnt = 0; + cpi->max_target = (np->features & FE_WIDE) ? 15 : 7; + cpi->max_lun = MAX_LUN - 1; + cpi->initiator_id = np->myaddr; + cpi->bus_id = cam_sim_bus(sim); + cpi->base_transfer_speed = 3300; + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, "Symbios", HBA_IDLEN); + strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); + cpi->unit_number = cam_sim_unit(sim); + cpi->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } + default: + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } +} + +/*========================================================== +** +** +** Complete execution of a SCSI command. +** Signal completion to the generic SCSI driver. +** +** +**========================================================== +*/ + +void +ncr_complete (ncb_p np, nccb_p cp) +{ + union ccb *ccb; + tcb_p tp; + lcb_p lp; + + /* + ** Sanity check + */ + + if (!cp || (cp->magic!=CCB_MAGIC) || !cp->ccb) return; + cp->magic = 1; + cp->tlimit= 0; + + /* + ** No Reselect anymore. + */ + cp->jump_nccb.l_cmd = (SCR_JUMP); + + /* + ** No starting. + */ + cp->phys.header.launch.l_paddr= NCB_SCRIPT_PHYS (np, idle); + + /* + ** timestamp + */ + ncb_profile (np, cp); + + if (DEBUG_FLAGS & DEBUG_TINY) + printf ("CCB=%x STAT=%x/%x\n", (int)(intptr_t)cp & 0xfff, + cp->host_status,cp->s_status); + + ccb = cp->ccb; + cp->ccb = NULL; + tp = &np->target[ccb->ccb_h.target_id]; + lp = tp->lp[ccb->ccb_h.target_lun]; + + /* + ** We do not queue more than 1 nccb per target + ** with negotiation at any time. If this nccb was + ** used for negotiation, clear this info in the tcb. + */ + + if (cp == tp->nego_cp) + tp->nego_cp = NULL; + + /* + ** Check for parity errors. + */ + /* XXX JGibbs - What about reporting them??? */ + + if (cp->parity_status) { + PRINT_ADDR(ccb); + printf ("%d parity error(s), fallback.\n", cp->parity_status); + /* + ** fallback to asynch transfer. + */ + tp->tinfo.goal.period = 0; + tp->tinfo.goal.offset = 0; + }; + + /* + ** Check for extended errors. + */ + + if (cp->xerr_status != XE_OK) { + PRINT_ADDR(ccb); + switch (cp->xerr_status) { + case XE_EXTRA_DATA: + printf ("extraneous data discarded.\n"); + break; + case XE_BAD_PHASE: + printf ("illegal scsi phase (4/5).\n"); + break; + default: + printf ("extended error %d.\n", cp->xerr_status); + break; + }; + if (cp->host_status==HS_COMPLETE) + cp->host_status = HS_FAIL; + }; + + /* + ** Check the status. + */ + if (cp->host_status == HS_COMPLETE) { + + if (cp->s_status == SCSI_STATUS_OK) { + + /* + ** All went well. + */ + /* XXX JGibbs - Properly calculate residual */ + + tp->bytes += ccb->csio.dxfer_len; + tp->transfers ++; + + ccb->ccb_h.status = CAM_REQ_CMP; + } else if ((cp->s_status & SCSI_STATUS_SENSE) != 0) { + + /* + * XXX Could be TERMIO too. Should record + * original status. + */ + ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; + cp->s_status &= ~SCSI_STATUS_SENSE; + if (cp->s_status == SCSI_STATUS_OK) { + ccb->ccb_h.status = + CAM_AUTOSNS_VALID|CAM_SCSI_STATUS_ERROR; + } else { + ccb->ccb_h.status = CAM_AUTOSENSE_FAIL; + } + } else { + ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; + ccb->csio.scsi_status = cp->s_status; + } + + + } else if (cp->host_status == HS_SEL_TIMEOUT) { + + /* + ** Device failed selection + */ + ccb->ccb_h.status = CAM_SEL_TIMEOUT; + + } else if (cp->host_status == HS_TIMEOUT) { + + /* + ** No response + */ + ccb->ccb_h.status = CAM_CMD_TIMEOUT; + } else if (cp->host_status == HS_STALL) { + ccb->ccb_h.status = CAM_REQUEUE_REQ; + } else { + + /* + ** Other protocol messes + */ + PRINT_ADDR(ccb); + printf ("COMMAND FAILED (%x %x) @%p.\n", + cp->host_status, cp->s_status, cp); + + ccb->ccb_h.status = CAM_CMD_TIMEOUT; + } + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); + ccb->ccb_h.status |= CAM_DEV_QFRZN; + } + + /* + ** Free this nccb + */ + ncr_free_nccb (np, cp); + + /* + ** signal completion to generic driver. + */ + xpt_done (ccb); +} + +/*========================================================== +** +** +** Signal all (or one) control block done. +** +** +**========================================================== +*/ + +void +ncr_wakeup (ncb_p np, u_long code) +{ + /* + ** Starting at the default nccb and following + ** the links, complete all jobs with a + ** host_status greater than "disconnect". + ** + ** If the "code" parameter is not zero, + ** complete all jobs that are not IDLE. + */ + + nccb_p cp = np->link_nccb; + while (cp) { + switch (cp->host_status) { + + case HS_IDLE: + break; + + case HS_DISCONNECT: + if(DEBUG_FLAGS & DEBUG_TINY) printf ("D"); + /* fall through */ + + case HS_BUSY: + case HS_NEGOTIATE: + if (!code) break; + cp->host_status = code; + + /* fall through */ + + default: + ncr_complete (np, cp); + break; + }; + cp = cp -> link_nccb; + }; +} + +static void +ncr_freeze_devq (ncb_p np, struct cam_path *path) +{ + nccb_p cp; + int i; + int count; + int firstskip; + /* + ** Starting at the first nccb and following + ** the links, complete all jobs that match + ** the passed in path and are in the start queue. + */ + + cp = np->link_nccb; + count = 0; + firstskip = 0; + while (cp) { + switch (cp->host_status) { + + case HS_BUSY: + case HS_NEGOTIATE: + if ((cp->phys.header.launch.l_paddr + == NCB_SCRIPT_PHYS (np, select)) + && (xpt_path_comp(path, cp->ccb->ccb_h.path) >= 0)) { + + /* Mark for removal from the start queue */ + for (i = 1; i < MAX_START; i++) { + int idx; + + idx = np->squeueput - i; + + if (idx < 0) + idx = MAX_START + idx; + if (np->squeue[idx] + == CCB_PHYS(cp, phys)) { + np->squeue[idx] = + NCB_SCRIPT_PHYS (np, skip); + if (i > firstskip) + firstskip = i; + break; + } + } + cp->host_status=HS_STALL; + ncr_complete (np, cp); + count++; + } + break; + default: + break; + } + cp = cp->link_nccb; + } + + if (count > 0) { + int j; + int bidx; + + /* Compress the start queue */ + j = 0; + bidx = np->squeueput; + i = np->squeueput - firstskip; + if (i < 0) + i = MAX_START + i; + for (;;) { + + bidx = i - j; + if (bidx < 0) + bidx = MAX_START + bidx; + + if (np->squeue[i] == NCB_SCRIPT_PHYS (np, skip)) { + j++; + } else if (j != 0) { + np->squeue[bidx] = np->squeue[i]; + if (np->squeue[bidx] + == NCB_SCRIPT_PHYS(np, idle)) + break; + } + i = (i + 1) % MAX_START; + } + np->squeueput = bidx; + } +} + +/*========================================================== +** +** +** Start NCR chip. +** +** +**========================================================== +*/ + +void +ncr_init(ncb_p np, char * msg, u_long code) +{ + int i; + + /* + ** Reset chip. + */ + + OUTB (nc_istat, SRST); + DELAY (1000); + OUTB (nc_istat, 0); + + /* + ** Message. + */ + + if (msg) printf ("%s: restart (%s).\n", ncr_name (np), msg); + + /* + ** Clear Start Queue + */ + + for (i=0;i<MAX_START;i++) + np -> squeue [i] = NCB_SCRIPT_PHYS (np, idle); + + /* + ** Start at first entry. + */ + + np->squeueput = 0; + WRITESCRIPT(startpos[0], NCB_SCRIPTH_PHYS (np, tryloop)); + WRITESCRIPT(start0 [0], SCR_INT ^ IFFALSE (0)); + + /* + ** Wakeup all pending jobs. + */ + + ncr_wakeup (np, code); + + /* + ** Init chip. + */ + + OUTB (nc_istat, 0x00 ); /* Remove Reset, abort ... */ + OUTB (nc_scntl0, 0xca ); /* full arb., ena parity, par->ATN */ + OUTB (nc_scntl1, 0x00 ); /* odd parity, and remove CRST!! */ + ncr_selectclock(np, np->rv_scntl3); /* Select SCSI clock */ + OUTB (nc_scid , RRE|np->myaddr);/* host adapter SCSI address */ + OUTW (nc_respid, 1ul<<np->myaddr);/* id to respond to */ + OUTB (nc_istat , SIGP ); /* Signal Process */ + OUTB (nc_dmode , np->rv_dmode); /* XXX modify burstlen ??? */ + OUTB (nc_dcntl , np->rv_dcntl); + OUTB (nc_ctest3, np->rv_ctest3); + OUTB (nc_ctest5, np->rv_ctest5); + OUTB (nc_ctest4, np->rv_ctest4);/* enable master parity checking */ + OUTB (nc_stest2, np->rv_stest2|EXT); /* Extended Sreq/Sack filtering */ + OUTB (nc_stest3, TE ); /* TolerANT enable */ + OUTB (nc_stime0, 0x0b ); /* HTH = disabled, STO = 0.1 sec. */ + + if (bootverbose >= 2) { + printf ("\tACTUAL values:SCNTL3:%02x DMODE:%02x DCNTL:%02x\n", + np->rv_scntl3, np->rv_dmode, np->rv_dcntl); + printf ("\t CTEST3:%02x CTEST4:%02x CTEST5:%02x\n", + np->rv_ctest3, np->rv_ctest4, np->rv_ctest5); + } + + /* + ** Enable GPIO0 pin for writing if LED support. + */ + + if (np->features & FE_LED0) { + OUTOFFB (nc_gpcntl, 0x01); + } + + /* + ** Fill in target structure. + */ + for (i=0;i<MAX_TARGET;i++) { + tcb_p tp = &np->target[i]; + + tp->tinfo.sval = 0; + tp->tinfo.wval = np->rv_scntl3; + + tp->tinfo.current.period = 0; + tp->tinfo.current.offset = 0; + tp->tinfo.current.width = MSG_EXT_WDTR_BUS_8_BIT; + } + + /* + ** enable ints + */ + + OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST); + OUTB (nc_dien , MDPE|BF|ABRT|SSI|SIR|IID); + + /* + ** Start script processor. + */ + + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, start)); + + /* + * Notify the XPT of the event + */ + if (code == HS_RESET) + xpt_async(AC_BUS_RESET, np->path, NULL); +} + +static void +ncr_poll(struct cam_sim *sim) +{ + ncr_intr(cam_sim_softc(sim)); +} + + +/*========================================================== +** +** Get clock factor and sync divisor for a given +** synchronous factor period. +** Returns the clock factor (in sxfer) and scntl3 +** synchronous divisor field. +** +**========================================================== +*/ + +static void ncr_getsync(ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p) +{ + u_long clk = np->clock_khz; /* SCSI clock frequency in kHz */ + int div = np->clock_divn; /* Number of divisors supported */ + u_long fak; /* Sync factor in sxfer */ + u_long per; /* Period in tenths of ns */ + u_long kpc; /* (per * clk) */ + + /* + ** Compute the synchronous period in tenths of nano-seconds + */ + if (sfac <= 10) per = 250; + else if (sfac == 11) per = 303; + else if (sfac == 12) per = 500; + else per = 40 * sfac; + + /* + ** Look for the greatest clock divisor that allows an + ** input speed faster than the period. + */ + kpc = per * clk; + while (--div >= 0) + if (kpc >= (div_10M[div] * 4)) break; + + /* + ** Calculate the lowest clock factor that allows an output + ** speed not faster than the period. + */ + fak = (kpc - 1) / div_10M[div] + 1; + +#if 0 /* You can #if 1 if you think this optimization is usefull */ + + per = (fak * div_10M[div]) / clk; + + /* + ** Why not to try the immediate lower divisor and to choose + ** the one that allows the fastest output speed ? + ** We dont want input speed too much greater than output speed. + */ + if (div >= 1 && fak < 6) { + u_long fak2, per2; + fak2 = (kpc - 1) / div_10M[div-1] + 1; + per2 = (fak2 * div_10M[div-1]) / clk; + if (per2 < per && fak2 <= 6) { + fak = fak2; + per = per2; + --div; + } + } +#endif + + if (fak < 4) fak = 4; /* Should never happen, too bad ... */ + + /* + ** Compute and return sync parameters for the ncr + */ + *fakp = fak - 4; + *scntl3p = ((div+1) << 4) + (sfac < 25 ? 0x80 : 0); +} + +/*========================================================== +** +** Switch sync mode for current job and its target +** +**========================================================== +*/ + +static void +ncr_setsync(ncb_p np, nccb_p cp, u_char scntl3, u_char sxfer, u_char period) +{ + union ccb *ccb; + struct ccb_trans_settings neg; + tcb_p tp; + int div; + u_int target = INB (nc_sdid) & 0x0f; + u_int period_10ns; + + assert (cp); + if (!cp) return; + + ccb = cp->ccb; + assert (ccb); + if (!ccb) return; + assert (target == ccb->ccb_h.target_id); + + tp = &np->target[target]; + + if (!scntl3 || !(sxfer & 0x1f)) + scntl3 = np->rv_scntl3; + scntl3 = (scntl3 & 0xf0) | (tp->tinfo.wval & EWS) + | (np->rv_scntl3 & 0x07); + + /* + ** Deduce the value of controller sync period from scntl3. + ** period is in tenths of nano-seconds. + */ + + div = ((scntl3 >> 4) & 0x7); + if ((sxfer & 0x1f) && div) + period_10ns = + (((sxfer>>5)+4)*div_10M[div-1])/np->clock_khz; + else + period_10ns = 0; + + tp->tinfo.goal.period = period; + tp->tinfo.goal.offset = sxfer & 0x1f; + tp->tinfo.current.period = period; + tp->tinfo.current.offset = sxfer & 0x1f; + + /* + ** Stop there if sync parameters are unchanged + */ + if (tp->tinfo.sval == sxfer && tp->tinfo.wval == scntl3) return; + tp->tinfo.sval = sxfer; + tp->tinfo.wval = scntl3; + + if (sxfer & 0x1f) { + /* + ** Disable extended Sreq/Sack filtering + */ + if (period_10ns <= 2000) OUTOFFB (nc_stest2, EXT); + } + + /* + ** Tell the SCSI layer about the + ** new transfer parameters. + */ + neg.sync_period = period; + neg.sync_offset = sxfer & 0x1f; + neg.valid = CCB_TRANS_SYNC_RATE_VALID + | CCB_TRANS_SYNC_OFFSET_VALID; + xpt_setup_ccb(&neg.ccb_h, ccb->ccb_h.path, + /*priority*/1); + xpt_async(AC_TRANSFER_NEG, ccb->ccb_h.path, &neg); + + /* + ** set actual value and sync_status + */ + OUTB (nc_sxfer, sxfer); + np->sync_st = sxfer; + OUTB (nc_scntl3, scntl3); + np->wide_st = scntl3; + + /* + ** patch ALL nccbs of this target. + */ + for (cp = np->link_nccb; cp; cp = cp->link_nccb) { + if (!cp->ccb) continue; + if (cp->ccb->ccb_h.target_id != target) continue; + cp->sync_status = sxfer; + cp->wide_status = scntl3; + }; +} + +/*========================================================== +** +** Switch wide mode for current job and its target +** SCSI specs say: a SCSI device that accepts a WDTR +** message shall reset the synchronous agreement to +** asynchronous mode. +** +**========================================================== +*/ + +static void ncr_setwide (ncb_p np, nccb_p cp, u_char wide, u_char ack) +{ + union ccb *ccb; + struct ccb_trans_settings neg; + u_int target = INB (nc_sdid) & 0x0f; + tcb_p tp; + u_char scntl3; + u_char sxfer; + + assert (cp); + if (!cp) return; + + ccb = cp->ccb; + assert (ccb); + if (!ccb) return; + assert (target == ccb->ccb_h.target_id); + + tp = &np->target[target]; + tp->tinfo.current.width = wide; + tp->tinfo.goal.width = wide; + tp->tinfo.current.period = 0; + tp->tinfo.current.offset = 0; + + scntl3 = (tp->tinfo.wval & (~EWS)) | (wide ? EWS : 0); + + sxfer = ack ? 0 : tp->tinfo.sval; + + /* + ** Stop there if sync/wide parameters are unchanged + */ + if (tp->tinfo.sval == sxfer && tp->tinfo.wval == scntl3) return; + tp->tinfo.sval = sxfer; + tp->tinfo.wval = scntl3; + + /* Tell the SCSI layer about the new transfer params */ + neg.bus_width = (scntl3 & EWS) ? MSG_EXT_WDTR_BUS_16_BIT + : MSG_EXT_WDTR_BUS_8_BIT; + neg.sync_period = 0; + neg.sync_offset = 0; + neg.valid = CCB_TRANS_BUS_WIDTH_VALID + | CCB_TRANS_SYNC_RATE_VALID + | CCB_TRANS_SYNC_OFFSET_VALID; + xpt_setup_ccb(&neg.ccb_h, ccb->ccb_h.path, + /*priority*/1); + xpt_async(AC_TRANSFER_NEG, ccb->ccb_h.path, &neg); + + /* + ** set actual value and sync_status + */ + OUTB (nc_sxfer, sxfer); + np->sync_st = sxfer; + OUTB (nc_scntl3, scntl3); + np->wide_st = scntl3; + + /* + ** patch ALL nccbs of this target. + */ + for (cp = np->link_nccb; cp; cp = cp->link_nccb) { + if (!cp->ccb) continue; + if (cp->ccb->ccb_h.target_id != target) continue; + cp->sync_status = sxfer; + cp->wide_status = scntl3; + }; +} + +/*========================================================== +** +** +** ncr timeout handler. +** +** +**========================================================== +** +** Misused to keep the driver running when +** interrupts are not configured correctly. +** +**---------------------------------------------------------- +*/ + +static void +ncr_timeout (void *arg) +{ + ncb_p np = arg; + time_t thistime = time_second; + ticks_t step = np->ticks; + u_long count = 0; + long signed t; + nccb_p cp; + + if (np->lasttime != thistime) { + /* + ** block ncr interrupts + */ + int oldspl = splcam(); + np->lasttime = thistime; + + /*---------------------------------------------------- + ** + ** handle ncr chip timeouts + ** + ** Assumption: + ** We have a chance to arbitrate for the + ** SCSI bus at least every 10 seconds. + ** + **---------------------------------------------------- + */ + + t = thistime - np->heartbeat; + + if (t<2) np->latetime=0; else np->latetime++; + + if (np->latetime>2) { + /* + ** If there are no requests, the script + ** processor will sleep on SEL_WAIT_RESEL. + ** But we have to check whether it died. + ** Let's try to wake it up. + */ + OUTB (nc_istat, SIGP); + }; + + /*---------------------------------------------------- + ** + ** handle nccb timeouts + ** + **---------------------------------------------------- + */ + + for (cp=np->link_nccb; cp; cp=cp->link_nccb) { + /* + ** look for timed out nccbs. + */ + if (!cp->host_status) continue; + count++; + if (cp->tlimit > thistime) continue; + + /* + ** Disable reselect. + ** Remove it from startqueue. + */ + cp->jump_nccb.l_cmd = (SCR_JUMP); + if (cp->phys.header.launch.l_paddr == + NCB_SCRIPT_PHYS (np, select)) { + printf ("%s: timeout nccb=%p (skip)\n", + ncr_name (np), cp); + cp->phys.header.launch.l_paddr + = NCB_SCRIPT_PHYS (np, skip); + }; + + switch (cp->host_status) { + + case HS_BUSY: + case HS_NEGOTIATE: + /* fall through */ + case HS_DISCONNECT: + cp->host_status=HS_TIMEOUT; + }; + cp->tag = 0; + + /* + ** wakeup this nccb. + */ + ncr_complete (np, cp); + }; + splx (oldspl); + } + + np->timeout_ch = + timeout (ncr_timeout, (caddr_t) np, step ? step : 1); + + if (INB(nc_istat) & (INTF|SIP|DIP)) { + + /* + ** Process pending interrupts. + */ + + int oldspl = splcam(); + if (DEBUG_FLAGS & DEBUG_TINY) printf ("{"); + ncr_exception (np); + if (DEBUG_FLAGS & DEBUG_TINY) printf ("}"); + splx (oldspl); + }; +} + +/*========================================================== +** +** log message for real hard errors +** +** "ncr0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ name (dsp:dbc)." +** " reg: r0 r1 r2 r3 r4 r5 r6 ..... rf." +** +** exception register: +** ds: dstat +** si: sist +** +** SCSI bus lines: +** so: control lines as driver by NCR. +** si: control lines as seen by NCR. +** sd: scsi data lines as seen by NCR. +** +** wide/fastmode: +** sxfer: (see the manual) +** scntl3: (see the manual) +** +** current script command: +** dsp: script address (relative to start of script). +** dbc: first word of script command. +** +** First 16 register of the chip: +** r0..rf +** +**========================================================== +*/ + +static void ncr_log_hard_error(ncb_p np, u_short sist, u_char dstat) +{ + u_int32_t dsp; + int script_ofs; + int script_size; + char *script_name; + u_char *script_base; + int i; + + dsp = INL (nc_dsp); + + if (np->p_script < dsp && + dsp <= np->p_script + sizeof(struct script)) { + script_ofs = dsp - np->p_script; + script_size = sizeof(struct script); + script_base = (u_char *) np->script; + script_name = "script"; + } + else if (np->p_scripth < dsp && + dsp <= np->p_scripth + sizeof(struct scripth)) { + script_ofs = dsp - np->p_scripth; + script_size = sizeof(struct scripth); + script_base = (u_char *) np->scripth; + script_name = "scripth"; + } else { + script_ofs = dsp; + script_size = 0; + script_base = 0; + script_name = "mem"; + } + + printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ (%s %x:%08x).\n", + ncr_name (np), (unsigned)INB (nc_sdid)&0x0f, dstat, sist, + (unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl), + (unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_name, script_ofs, + (unsigned)INL (nc_dbc)); + + if (((script_ofs & 3) == 0) && + (unsigned)script_ofs < script_size) { + printf ("%s: script cmd = %08x\n", ncr_name(np), + (int)READSCRIPT_OFF(script_base, script_ofs)); + } + + printf ("%s: regdump:", ncr_name(np)); + for (i=0; i<16;i++) + printf (" %02x", (unsigned)INB_OFF(i)); + printf (".\n"); +} + +/*========================================================== +** +** +** ncr chip exception handler. +** +** +**========================================================== +*/ + +void ncr_exception (ncb_p np) +{ + u_char istat, dstat; + u_short sist; + + /* + ** interrupt on the fly ? + */ + while ((istat = INB (nc_istat)) & INTF) { + if (DEBUG_FLAGS & DEBUG_TINY) printf ("F "); + OUTB (nc_istat, INTF); + np->profile.num_fly++; + ncr_wakeup (np, 0); + }; + if (!(istat & (SIP|DIP))) { + return; + } + + /* + ** Steinbach's Guideline for Systems Programming: + ** Never test for an error condition you don't know how to handle. + */ + + sist = (istat & SIP) ? INW (nc_sist) : 0; + dstat = (istat & DIP) ? INB (nc_dstat) : 0; + np->profile.num_int++; + + if (DEBUG_FLAGS & DEBUG_TINY) + printf ("<%d|%x:%x|%x:%x>", + INB(nc_scr0), + dstat,sist, + (unsigned)INL(nc_dsp), + (unsigned)INL(nc_dbc)); + if ((dstat==DFE) && (sist==PAR)) return; + +/*========================================================== +** +** First the normal cases. +** +**========================================================== +*/ + /*------------------------------------------- + ** SCSI reset + **------------------------------------------- + */ + + if (sist & RST) { + ncr_init (np, bootverbose ? "scsi reset" : NULL, HS_RESET); + return; + }; + + /*------------------------------------------- + ** selection timeout + ** + ** IID excluded from dstat mask! + ** (chip bug) + **------------------------------------------- + */ + + if ((sist & STO) && + !(sist & (GEN|HTH|MA|SGE|UDC|RST|PAR)) && + !(dstat & (MDPE|BF|ABRT|SIR))) { + ncr_int_sto (np); + return; + }; + + /*------------------------------------------- + ** Phase mismatch. + **------------------------------------------- + */ + + if ((sist & MA) && + !(sist & (STO|GEN|HTH|SGE|UDC|RST|PAR)) && + !(dstat & (MDPE|BF|ABRT|SIR|IID))) { + ncr_int_ma (np, dstat); + return; + }; + + /*---------------------------------------- + ** move command with length 0 + **---------------------------------------- + */ + + if ((dstat & IID) && + !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) && + !(dstat & (MDPE|BF|ABRT|SIR)) && + ((INL(nc_dbc) & 0xf8000000) == SCR_MOVE_TBL)) { + /* + ** Target wants more data than available. + ** The "no_data" script will do it. + */ + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, no_data)); + return; + }; + + /*------------------------------------------- + ** Programmed interrupt + **------------------------------------------- + */ + + if ((dstat & SIR) && + !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) && + !(dstat & (MDPE|BF|ABRT|IID)) && + (INB(nc_dsps) <= SIR_MAX)) { + ncr_int_sir (np); + return; + }; + + /*======================================== + ** log message for real hard errors + **======================================== + */ + + ncr_log_hard_error(np, sist, dstat); + + /*======================================== + ** do the register dump + **======================================== + */ + + if (time_second - np->regtime > 10) { + int i; + np->regtime = time_second; + for (i=0; i<sizeof(np->regdump); i++) + ((volatile char*)&np->regdump)[i] = INB_OFF(i); + np->regdump.nc_dstat = dstat; + np->regdump.nc_sist = sist; + }; + + + /*---------------------------------------- + ** clean up the dma fifo + **---------------------------------------- + */ + + if ( (INB(nc_sstat0) & (ILF|ORF|OLF) ) || + (INB(nc_sstat1) & (FF3210) ) || + (INB(nc_sstat2) & (ILF1|ORF1|OLF1)) || /* wide .. */ + !(dstat & DFE)) { + printf ("%s: have to clear fifos.\n", ncr_name (np)); + OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */ + OUTB (nc_ctest3, np->rv_ctest3 | CLF); + /* clear dma fifo */ + } + + /*---------------------------------------- + ** handshake timeout + **---------------------------------------- + */ + + if (sist & HTH) { + printf ("%s: handshake timeout\n", ncr_name(np)); + OUTB (nc_scntl1, CRST); + DELAY (1000); + OUTB (nc_scntl1, 0x00); + OUTB (nc_scr0, HS_FAIL); + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup)); + return; + } + + /*---------------------------------------- + ** unexpected disconnect + **---------------------------------------- + */ + + if ((sist & UDC) && + !(sist & (STO|GEN|HTH|MA|SGE|RST|PAR)) && + !(dstat & (MDPE|BF|ABRT|SIR|IID))) { + OUTB (nc_scr0, HS_UNEXPECTED); + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup)); + return; + }; + + /*---------------------------------------- + ** cannot disconnect + **---------------------------------------- + */ + + if ((dstat & IID) && + !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) && + !(dstat & (MDPE|BF|ABRT|SIR)) && + ((INL(nc_dbc) & 0xf8000000) == SCR_WAIT_DISC)) { + /* + ** Unexpected data cycle while waiting for disconnect. + */ + if (INB(nc_sstat2) & LDSC) { + /* + ** It's an early reconnect. + ** Let's continue ... + */ + OUTB (nc_dcntl, np->rv_dcntl | STD); + /* + ** info message + */ + printf ("%s: INFO: LDSC while IID.\n", + ncr_name (np)); + return; + }; + printf ("%s: target %d doesn't release the bus.\n", + ncr_name (np), INB (nc_sdid)&0x0f); + /* + ** return without restarting the NCR. + ** timeout will do the real work. + */ + return; + }; + + /*---------------------------------------- + ** single step + **---------------------------------------- + */ + + if ((dstat & SSI) && + !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) && + !(dstat & (MDPE|BF|ABRT|SIR|IID))) { + OUTB (nc_dcntl, np->rv_dcntl | STD); + return; + }; + +/* +** @RECOVER@ HTH, SGE, ABRT. +** +** We should try to recover from these interrupts. +** They may occur if there are problems with synch transfers, or +** if targets are switched on or off while the driver is running. +*/ + + if (sist & SGE) { + /* clear scsi offsets */ + OUTB (nc_ctest3, np->rv_ctest3 | CLF); + } + + /* + ** Freeze controller to be able to read the messages. + */ + + if (DEBUG_FLAGS & DEBUG_FREEZE) { + int i; + unsigned char val; + for (i=0; i<0x60; i++) { + switch (i%16) { + + case 0: + printf ("%s: reg[%d0]: ", + ncr_name(np),i/16); + break; + case 4: + case 8: + case 12: + printf (" "); + break; + }; + val = bus_space_read_1(np->bst, np->bsh, i); + printf (" %x%x", val/16, val%16); + if (i%16==15) printf (".\n"); + }; + + untimeout (ncr_timeout, (caddr_t) np, np->timeout_ch); + + printf ("%s: halted!\n", ncr_name(np)); + /* + ** don't restart controller ... + */ + OUTB (nc_istat, SRST); + return; + }; + +#ifdef NCR_FREEZE + /* + ** Freeze system to be able to read the messages. + */ + printf ("ncr: fatal error: system halted - press reset to reboot ..."); + (void) splhigh(); + for (;;); +#endif + + /* + ** sorry, have to kill ALL jobs ... + */ + + ncr_init (np, "fatal error", HS_FAIL); +} + +/*========================================================== +** +** ncr chip exception handler for selection timeout +** +**========================================================== +** +** There seems to be a bug in the 53c810. +** Although a STO-Interrupt is pending, +** it continues executing script commands. +** But it will fail and interrupt (IID) on +** the next instruction where it's looking +** for a valid phase. +** +**---------------------------------------------------------- +*/ + +void ncr_int_sto (ncb_p np) +{ + u_long dsa, scratcha, diff; + nccb_p cp; + if (DEBUG_FLAGS & DEBUG_TINY) printf ("T"); + + /* + ** look for nccb and set the status. + */ + + dsa = INL (nc_dsa); + cp = np->link_nccb; + while (cp && (CCB_PHYS (cp, phys) != dsa)) + cp = cp->link_nccb; + + if (cp) { + cp-> host_status = HS_SEL_TIMEOUT; + ncr_complete (np, cp); + }; + + /* + ** repair start queue + */ + + scratcha = INL (nc_scratcha); + diff = scratcha - NCB_SCRIPTH_PHYS (np, tryloop); + +/* assert ((diff <= MAX_START * 20) && !(diff % 20));*/ + + if ((diff <= MAX_START * 20) && !(diff % 20)) { + WRITESCRIPT(startpos[0], scratcha); + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, start)); + return; + }; + ncr_init (np, "selection timeout", HS_FAIL); +} + +/*========================================================== +** +** +** ncr chip exception handler for phase errors. +** +** +**========================================================== +** +** We have to construct a new transfer descriptor, +** to transfer the rest of the current block. +** +**---------------------------------------------------------- +*/ + +static void ncr_int_ma (ncb_p np, u_char dstat) +{ + u_int32_t dbc; + u_int32_t rest; + u_int32_t dsa; + u_int32_t dsp; + u_int32_t nxtdsp; + volatile void *vdsp_base; + size_t vdsp_off; + u_int32_t oadr, olen; + u_int32_t *tblp, *newcmd; + u_char cmd, sbcl, ss0, ss2, ctest5; + u_short delta; + nccb_p cp; + + dsp = INL (nc_dsp); + dsa = INL (nc_dsa); + dbc = INL (nc_dbc); + ss0 = INB (nc_sstat0); + ss2 = INB (nc_sstat2); + sbcl= INB (nc_sbcl); + + cmd = dbc >> 24; + rest= dbc & 0xffffff; + + ctest5 = (np->rv_ctest5 & DFS) ? INB (nc_ctest5) : 0; + if (ctest5 & DFS) + delta=(((ctest5<<8) | (INB (nc_dfifo) & 0xff)) - rest) & 0x3ff; + else + delta=(INB (nc_dfifo) - rest) & 0x7f; + + + /* + ** The data in the dma fifo has not been transfered to + ** the target -> add the amount to the rest + ** and clear the data. + ** Check the sstat2 register in case of wide transfer. + */ + + if (!(dstat & DFE)) rest += delta; + if (ss0 & OLF) rest++; + if (ss0 & ORF) rest++; + if (INB(nc_scntl3) & EWS) { + if (ss2 & OLF1) rest++; + if (ss2 & ORF1) rest++; + }; + OUTB (nc_ctest3, np->rv_ctest3 | CLF); /* clear dma fifo */ + OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */ + + /* + ** locate matching cp + */ + cp = np->link_nccb; + while (cp && (CCB_PHYS (cp, phys) != dsa)) + cp = cp->link_nccb; + + if (!cp) { + printf ("%s: SCSI phase error fixup: CCB already dequeued (%p)\n", + ncr_name (np), (void *) np->header.cp); + return; + } + if (cp != np->header.cp) { + printf ("%s: SCSI phase error fixup: CCB address mismatch " + "(%p != %p) np->nccb = %p\n", + ncr_name (np), (void *)cp, (void *)np->header.cp, + (void *)np->link_nccb); +/* return;*/ + } + + /* + ** find the interrupted script command, + ** and the address at which to continue. + */ + + if (dsp == vtophys (&cp->patch[2])) { + vdsp_base = cp; + vdsp_off = offsetof(struct nccb, patch[0]); + nxtdsp = READSCRIPT_OFF(vdsp_base, vdsp_off + 3*4); + } else if (dsp == vtophys (&cp->patch[6])) { + vdsp_base = cp; + vdsp_off = offsetof(struct nccb, patch[4]); + nxtdsp = READSCRIPT_OFF(vdsp_base, vdsp_off + 3*4); + } else if (dsp > np->p_script && + dsp <= np->p_script + sizeof(struct script)) { + vdsp_base = np->script; + vdsp_off = dsp - np->p_script - 8; + nxtdsp = dsp; + } else { + vdsp_base = np->scripth; + vdsp_off = dsp - np->p_scripth - 8; + nxtdsp = dsp; + }; + + /* + ** log the information + */ + if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE)) { + printf ("P%x%x ",cmd&7, sbcl&7); + printf ("RL=%d D=%d SS0=%x ", + (unsigned) rest, (unsigned) delta, ss0); + }; + if (DEBUG_FLAGS & DEBUG_PHASE) { + printf ("\nCP=%p CP2=%p DSP=%x NXT=%x VDSP=%p CMD=%x ", + cp, np->header.cp, + dsp, + nxtdsp, (volatile char*)vdsp_base+vdsp_off, cmd); + }; + + /* + ** get old startaddress and old length. + */ + + oadr = READSCRIPT_OFF(vdsp_base, vdsp_off + 1*4); + + if (cmd & 0x10) { /* Table indirect */ + tblp = (u_int32_t *) ((char*) &cp->phys + oadr); + olen = tblp[0]; + oadr = tblp[1]; + } else { + tblp = (u_int32_t *) 0; + olen = READSCRIPT_OFF(vdsp_base, vdsp_off) & 0xffffff; + }; + + if (DEBUG_FLAGS & DEBUG_PHASE) { + printf ("OCMD=%x\nTBLP=%p OLEN=%lx OADR=%lx\n", + (unsigned) (READSCRIPT_OFF(vdsp_base, vdsp_off) >> 24), + (void *) tblp, + (u_long) olen, + (u_long) oadr); + }; + + /* + ** if old phase not dataphase, leave here. + */ + + if (cmd != (READSCRIPT_OFF(vdsp_base, vdsp_off) >> 24)) { + PRINT_ADDR(cp->ccb); + printf ("internal error: cmd=%02x != %02x=(vdsp[0] >> 24)\n", + (unsigned)cmd, + (unsigned)READSCRIPT_OFF(vdsp_base, vdsp_off) >> 24); + + return; + } + if (cmd & 0x06) { + PRINT_ADDR(cp->ccb); + printf ("phase change %x-%x %d@%08x resid=%d.\n", + cmd&7, sbcl&7, (unsigned)olen, + (unsigned)oadr, (unsigned)rest); + + OUTB (nc_dcntl, np->rv_dcntl | STD); + return; + }; + + /* + ** choose the correct patch area. + ** if savep points to one, choose the other. + */ + + newcmd = cp->patch; + if (cp->phys.header.savep == vtophys (newcmd)) newcmd+=4; + + /* + ** fillin the commands + */ + + newcmd[0] = ((cmd & 0x0f) << 24) | rest; + newcmd[1] = oadr + olen - rest; + newcmd[2] = SCR_JUMP; + newcmd[3] = nxtdsp; + + if (DEBUG_FLAGS & DEBUG_PHASE) { + PRINT_ADDR(cp->ccb); + printf ("newcmd[%d] %x %x %x %x.\n", + (int)(newcmd - cp->patch), + (unsigned)newcmd[0], + (unsigned)newcmd[1], + (unsigned)newcmd[2], + (unsigned)newcmd[3]); + } + /* + ** fake the return address (to the patch). + ** and restart script processor at dispatcher. + */ + np->profile.num_break++; + OUTL (nc_temp, vtophys (newcmd)); + if ((cmd & 7) == 0) + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch)); + else + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, checkatn)); +} + +/*========================================================== +** +** +** ncr chip exception handler for programmed interrupts. +** +** +**========================================================== +*/ + +static int ncr_show_msg (u_char * msg) +{ + u_char i; + printf ("%x",*msg); + if (*msg==MSG_EXTENDED) { + for (i=1;i<8;i++) { + if (i-1>msg[1]) break; + printf ("-%x",msg[i]); + }; + return (i+1); + } else if ((*msg & 0xf0) == 0x20) { + printf ("-%x",msg[1]); + return (2); + }; + return (1); +} + +void ncr_int_sir (ncb_p np) +{ + u_char scntl3; + u_char chg, ofs, per, fak, wide; + u_char num = INB (nc_dsps); + nccb_p cp=0; + u_long dsa; + u_int target = INB (nc_sdid) & 0x0f; + tcb_p tp = &np->target[target]; + int i; + if (DEBUG_FLAGS & DEBUG_TINY) printf ("I#%d", num); + + switch (num) { + case SIR_SENSE_RESTART: + case SIR_STALL_RESTART: + break; + + default: + /* + ** lookup the nccb + */ + dsa = INL (nc_dsa); + cp = np->link_nccb; + while (cp && (CCB_PHYS (cp, phys) != dsa)) + cp = cp->link_nccb; + + assert (cp); + if (!cp) + goto out; + assert (cp == np->header.cp); + if (cp != np->header.cp) + goto out; + } + + switch (num) { + +/*-------------------------------------------------------------------- +** +** Processing of interrupted getcc selects +** +**-------------------------------------------------------------------- +*/ + + case SIR_SENSE_RESTART: + /*------------------------------------------ + ** Script processor is idle. + ** Look for interrupted "check cond" + **------------------------------------------ + */ + + if (DEBUG_FLAGS & DEBUG_RESTART) + printf ("%s: int#%d",ncr_name (np),num); + cp = (nccb_p) 0; + for (i=0; i<MAX_TARGET; i++) { + if (DEBUG_FLAGS & DEBUG_RESTART) printf (" t%d", i); + tp = &np->target[i]; + if (DEBUG_FLAGS & DEBUG_RESTART) printf ("+"); + cp = tp->hold_cp; + if (!cp) continue; + if (DEBUG_FLAGS & DEBUG_RESTART) printf ("+"); + if ((cp->host_status==HS_BUSY) && + (cp->s_status==SCSI_STATUS_CHECK_COND)) + break; + if (DEBUG_FLAGS & DEBUG_RESTART) printf ("- (remove)"); + tp->hold_cp = cp = (nccb_p) 0; + }; + + if (cp) { + if (DEBUG_FLAGS & DEBUG_RESTART) + printf ("+ restart job ..\n"); + OUTL (nc_dsa, CCB_PHYS (cp, phys)); + OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, getcc)); + return; + }; + + /* + ** no job, resume normal processing + */ + if (DEBUG_FLAGS & DEBUG_RESTART) printf (" -- remove trap\n"); + WRITESCRIPT(start0[0], SCR_INT ^ IFFALSE (0)); + break; + + case SIR_SENSE_FAILED: + /*------------------------------------------- + ** While trying to select for + ** getting the condition code, + ** a target reselected us. + **------------------------------------------- + */ + if (DEBUG_FLAGS & DEBUG_RESTART) { + PRINT_ADDR(cp->ccb); + printf ("in getcc reselect by t%d.\n", + INB(nc_ssid) & 0x0f); + } + + /* + ** Mark this job + */ + cp->host_status = HS_BUSY; + cp->s_status = SCSI_STATUS_CHECK_COND; + np->target[cp->ccb->ccb_h.target_id].hold_cp = cp; + + /* + ** And patch code to restart it. + */ + WRITESCRIPT(start0[0], SCR_INT); + break; + +/*----------------------------------------------------------------------------- +** +** Was Sie schon immer ueber transfermode negotiation wissen wollten ... +** +** We try to negotiate sync and wide transfer only after +** a successfull inquire command. We look at byte 7 of the +** inquire data to determine the capabilities if the target. +** +** When we try to negotiate, we append the negotiation message +** to the identify and (maybe) simple tag message. +** The host status field is set to HS_NEGOTIATE to mark this +** situation. +** +** If the target doesn't answer this message immidiately +** (as required by the standard), the SIR_NEGO_FAIL interrupt +** will be raised eventually. +** The handler removes the HS_NEGOTIATE status, and sets the +** negotiated value to the default (async / nowide). +** +** If we receive a matching answer immediately, we check it +** for validity, and set the values. +** +** If we receive a Reject message immediately, we assume the +** negotiation has failed, and fall back to standard values. +** +** If we receive a negotiation message while not in HS_NEGOTIATE +** state, it's a target initiated negotiation. We prepare a +** (hopefully) valid answer, set our parameters, and send back +** this answer to the target. +** +** If the target doesn't fetch the answer (no message out phase), +** we assume the negotiation has failed, and fall back to default +** settings. +** +** When we set the values, we adjust them in all nccbs belonging +** to this target, in the controller's register, and in the "phys" +** field of the controller's struct ncb. +** +** Possible cases: hs sir msg_in value send goto +** We try try to negotiate: +** -> target doesnt't msgin NEG FAIL noop defa. - dispatch +** -> target rejected our msg NEG FAIL reject defa. - dispatch +** -> target answered (ok) NEG SYNC sdtr set - clrack +** -> target answered (!ok) NEG SYNC sdtr defa. REJ--->msg_bad +** -> target answered (ok) NEG WIDE wdtr set - clrack +** -> target answered (!ok) NEG WIDE wdtr defa. REJ--->msg_bad +** -> any other msgin NEG FAIL noop defa. - dispatch +** +** Target tries to negotiate: +** -> incoming message --- SYNC sdtr set SDTR - +** -> incoming message --- WIDE wdtr set WDTR - +** We sent our answer: +** -> target doesn't msgout --- PROTO ? defa. - dispatch +** +**----------------------------------------------------------------------------- +*/ + + case SIR_NEGO_FAILED: + /*------------------------------------------------------- + ** + ** Negotiation failed. + ** Target doesn't send an answer message, + ** or target rejected our message. + ** + ** Remove negotiation request. + ** + **------------------------------------------------------- + */ + OUTB (HS_PRT, HS_BUSY); + + /* fall through */ + + case SIR_NEGO_PROTO: + /*------------------------------------------------------- + ** + ** Negotiation failed. + ** Target doesn't fetch the answer message. + ** + **------------------------------------------------------- + */ + + if (DEBUG_FLAGS & DEBUG_NEGO) { + PRINT_ADDR(cp->ccb); + printf ("negotiation failed sir=%x status=%x.\n", + num, cp->nego_status); + }; + + /* + ** any error in negotiation: + ** fall back to default mode. + */ + switch (cp->nego_status) { + + case NS_SYNC: + ncr_setsync (np, cp, 0, 0xe0, 0); + break; + + case NS_WIDE: + ncr_setwide (np, cp, 0, 0); + break; + + }; + np->msgin [0] = MSG_NOOP; + np->msgout[0] = MSG_NOOP; + cp->nego_status = 0; + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch)); + break; + + case SIR_NEGO_SYNC: + /* + ** Synchronous request message received. + */ + + if (DEBUG_FLAGS & DEBUG_NEGO) { + PRINT_ADDR(cp->ccb); + printf ("sync msgin: "); + (void) ncr_show_msg (np->msgin); + printf (".\n"); + }; + + /* + ** get requested values. + */ + + chg = 0; + per = np->msgin[3]; + ofs = np->msgin[4]; + if (ofs==0) per=255; + + /* + ** check values against driver limits. + */ + if (per < np->minsync) + {chg = 1; per = np->minsync;} + if (per < tp->tinfo.user.period) + {chg = 1; per = tp->tinfo.user.period;} + if (ofs > tp->tinfo.user.offset) + {chg = 1; ofs = tp->tinfo.user.offset;} + + /* + ** Check against controller limits. + */ + + fak = 7; + scntl3 = 0; + if (ofs != 0) { + ncr_getsync(np, per, &fak, &scntl3); + if (fak > 7) { + chg = 1; + ofs = 0; + } + } + if (ofs == 0) { + fak = 7; + per = 0; + scntl3 = 0; + } + + if (DEBUG_FLAGS & DEBUG_NEGO) { + PRINT_ADDR(cp->ccb); + printf ("sync: per=%d scntl3=0x%x ofs=%d fak=%d chg=%d.\n", + per, scntl3, ofs, fak, chg); + } + + if (INB (HS_PRT) == HS_NEGOTIATE) { + OUTB (HS_PRT, HS_BUSY); + switch (cp->nego_status) { + + case NS_SYNC: + /* + ** This was an answer message + */ + if (chg) { + /* + ** Answer wasn't acceptable. + */ + ncr_setsync (np, cp, 0, 0xe0, 0); + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad)); + } else { + /* + ** Answer is ok. + */ + ncr_setsync (np,cp,scntl3,(fak<<5)|ofs, per); + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack)); + }; + return; + + case NS_WIDE: + ncr_setwide (np, cp, 0, 0); + break; + }; + }; + + /* + ** It was a request. Set value and + ** prepare an answer message + */ + + ncr_setsync (np, cp, scntl3, (fak<<5)|ofs, per); + + np->msgout[0] = MSG_EXTENDED; + np->msgout[1] = 3; + np->msgout[2] = MSG_EXT_SDTR; + np->msgout[3] = per; + np->msgout[4] = ofs; + + cp->nego_status = NS_SYNC; + + if (DEBUG_FLAGS & DEBUG_NEGO) { + PRINT_ADDR(cp->ccb); + printf ("sync msgout: "); + (void) ncr_show_msg (np->msgout); + printf (".\n"); + } + + if (!ofs) { + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad)); + return; + } + np->msgin [0] = MSG_NOOP; + + break; + + case SIR_NEGO_WIDE: + /* + ** Wide request message received. + */ + if (DEBUG_FLAGS & DEBUG_NEGO) { + PRINT_ADDR(cp->ccb); + printf ("wide msgin: "); + (void) ncr_show_msg (np->msgin); + printf (".\n"); + }; + + /* + ** get requested values. + */ + + chg = 0; + wide = np->msgin[3]; + + /* + ** check values against driver limits. + */ + + if (wide > tp->tinfo.user.width) + {chg = 1; wide = tp->tinfo.user.width;} + + if (DEBUG_FLAGS & DEBUG_NEGO) { + PRINT_ADDR(cp->ccb); + printf ("wide: wide=%d chg=%d.\n", wide, chg); + } + + if (INB (HS_PRT) == HS_NEGOTIATE) { + OUTB (HS_PRT, HS_BUSY); + switch (cp->nego_status) { + + case NS_WIDE: + /* + ** This was an answer message + */ + if (chg) { + /* + ** Answer wasn't acceptable. + */ + ncr_setwide (np, cp, 0, 1); + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad)); + } else { + /* + ** Answer is ok. + */ + ncr_setwide (np, cp, wide, 1); + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack)); + }; + return; + + case NS_SYNC: + ncr_setsync (np, cp, 0, 0xe0, 0); + break; + }; + }; + + /* + ** It was a request, set value and + ** prepare an answer message + */ + + ncr_setwide (np, cp, wide, 1); + + np->msgout[0] = MSG_EXTENDED; + np->msgout[1] = 2; + np->msgout[2] = MSG_EXT_WDTR; + np->msgout[3] = wide; + + np->msgin [0] = MSG_NOOP; + + cp->nego_status = NS_WIDE; + + if (DEBUG_FLAGS & DEBUG_NEGO) { + PRINT_ADDR(cp->ccb); + printf ("wide msgout: "); + (void) ncr_show_msg (np->msgout); + printf (".\n"); + } + break; + +/*-------------------------------------------------------------------- +** +** Processing of special messages +** +**-------------------------------------------------------------------- +*/ + + case SIR_REJECT_RECEIVED: + /*----------------------------------------------- + ** + ** We received a MSG_MESSAGE_REJECT message. + ** + **----------------------------------------------- + */ + + PRINT_ADDR(cp->ccb); + printf ("MSG_MESSAGE_REJECT received (%x:%x).\n", + (unsigned)np->lastmsg, np->msgout[0]); + break; + + case SIR_REJECT_SENT: + /*----------------------------------------------- + ** + ** We received an unknown message + ** + **----------------------------------------------- + */ + + PRINT_ADDR(cp->ccb); + printf ("MSG_MESSAGE_REJECT sent for "); + (void) ncr_show_msg (np->msgin); + printf (".\n"); + break; + +/*-------------------------------------------------------------------- +** +** Processing of special messages +** +**-------------------------------------------------------------------- +*/ + + case SIR_IGN_RESIDUE: + /*----------------------------------------------- + ** + ** We received an IGNORE RESIDUE message, + ** which couldn't be handled by the script. + ** + **----------------------------------------------- + */ + + PRINT_ADDR(cp->ccb); + printf ("MSG_IGN_WIDE_RESIDUE received, but not yet implemented.\n"); + break; + + case SIR_MISSING_SAVE: + /*----------------------------------------------- + ** + ** We received an DISCONNECT message, + ** but the datapointer wasn't saved before. + ** + **----------------------------------------------- + */ + + PRINT_ADDR(cp->ccb); + printf ("MSG_DISCONNECT received, but datapointer not saved:\n" + "\tdata=%x save=%x goal=%x.\n", + (unsigned) INL (nc_temp), + (unsigned) np->header.savep, + (unsigned) np->header.goalp); + break; + +/*-------------------------------------------------------------------- +** +** Processing of a "SCSI_STATUS_QUEUE_FULL" status. +** +** XXX JGibbs - We should do the same thing for BUSY status. +** +** The current command has been rejected, +** because there are too many in the command queue. +** We have started too many commands for that target. +** +**-------------------------------------------------------------------- +*/ + case SIR_STALL_QUEUE: + cp->xerr_status = XE_OK; + cp->host_status = HS_COMPLETE; + cp->s_status = SCSI_STATUS_QUEUE_FULL; + ncr_freeze_devq(np, cp->ccb->ccb_h.path); + ncr_complete(np, cp); + + /* FALL THROUGH */ + + case SIR_STALL_RESTART: + /*----------------------------------------------- + ** + ** Enable selecting again, + ** if NO disconnected jobs. + ** + **----------------------------------------------- + */ + /* + ** Look for a disconnected job. + */ + cp = np->link_nccb; + while (cp && cp->host_status != HS_DISCONNECT) + cp = cp->link_nccb; + + /* + ** if there is one, ... + */ + if (cp) { + /* + ** wait for reselection + */ + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, reselect)); + return; + }; + + /* + ** else remove the interrupt. + */ + + printf ("%s: queue empty.\n", ncr_name (np)); + WRITESCRIPT(start1[0], SCR_INT ^ IFFALSE (0)); + break; + }; + +out: + OUTB (nc_dcntl, np->rv_dcntl | STD); +} + +/*========================================================== +** +** +** Aquire a control block +** +** +**========================================================== +*/ + +static nccb_p ncr_get_nccb + (ncb_p np, u_long target, u_long lun) +{ + lcb_p lp; + int s; + nccb_p cp = NULL; + + /* Keep our timeout handler out */ + s = splsoftclock(); + + /* + ** Lun structure available ? + */ + + lp = np->target[target].lp[lun]; + if (lp) { + cp = lp->next_nccb; + + /* + ** Look for free CCB + */ + + while (cp && cp->magic) { + cp = cp->next_nccb; + } + } + + /* + ** if nothing available, create one. + */ + + if (cp == NULL) + cp = ncr_alloc_nccb(np, target, lun); + + if (cp != NULL) { + if (cp->magic) { + printf("%s: Bogus free cp found\n", ncr_name(np)); + splx(s); + return (NULL); + } + cp->magic = 1; + } + splx(s); + return (cp); +} + +/*========================================================== +** +** +** Release one control block +** +** +**========================================================== +*/ + +void ncr_free_nccb (ncb_p np, nccb_p cp) +{ + /* + ** sanity + */ + + assert (cp != NULL); + + cp -> host_status = HS_IDLE; + cp -> magic = 0; +} + +/*========================================================== +** +** +** Allocation of resources for Targets/Luns/Tags. +** +** +**========================================================== +*/ + +static nccb_p +ncr_alloc_nccb (ncb_p np, u_long target, u_long lun) +{ + tcb_p tp; + lcb_p lp; + nccb_p cp; + + assert (np != NULL); + + if (target>=MAX_TARGET) return(NULL); + if (lun >=MAX_LUN ) return(NULL); + + tp=&np->target[target]; + + if (!tp->jump_tcb.l_cmd) { + + /* + ** initialize it. + */ + tp->jump_tcb.l_cmd = (SCR_JUMP^IFFALSE (DATA (0x80 + target))); + tp->jump_tcb.l_paddr = np->jump_tcb.l_paddr; + + tp->getscr[0] = + (np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1); + tp->getscr[1] = vtophys (&tp->tinfo.sval); + tp->getscr[2] = rman_get_start(np->reg_res) + offsetof (struct ncr_reg, nc_sxfer); + tp->getscr[3] = + (np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1); + tp->getscr[4] = vtophys (&tp->tinfo.wval); + tp->getscr[5] = rman_get_start(np->reg_res) + offsetof (struct ncr_reg, nc_scntl3); + + assert (((offsetof(struct ncr_reg, nc_sxfer) ^ + (offsetof(struct tcb ,tinfo) + + offsetof(struct ncr_target_tinfo, sval))) & 3) == 0); + assert (((offsetof(struct ncr_reg, nc_scntl3) ^ + (offsetof(struct tcb, tinfo) + + offsetof(struct ncr_target_tinfo, wval))) &3) == 0); + + tp->call_lun.l_cmd = (SCR_CALL); + tp->call_lun.l_paddr = NCB_SCRIPT_PHYS (np, resel_lun); + + tp->jump_lcb.l_cmd = (SCR_JUMP); + tp->jump_lcb.l_paddr = NCB_SCRIPTH_PHYS (np, abort); + np->jump_tcb.l_paddr = vtophys (&tp->jump_tcb); + } + + /* + ** Logic unit control block + */ + lp = tp->lp[lun]; + if (!lp) { + /* + ** Allocate a lcb + */ + lp = (lcb_p) malloc (sizeof (struct lcb), M_DEVBUF, + M_NOWAIT | M_ZERO); + if (!lp) return(NULL); + + /* + ** Initialize it + */ + lp->jump_lcb.l_cmd = (SCR_JUMP ^ IFFALSE (DATA (lun))); + lp->jump_lcb.l_paddr = tp->jump_lcb.l_paddr; + + lp->call_tag.l_cmd = (SCR_CALL); + lp->call_tag.l_paddr = NCB_SCRIPT_PHYS (np, resel_tag); + + lp->jump_nccb.l_cmd = (SCR_JUMP); + lp->jump_nccb.l_paddr = NCB_SCRIPTH_PHYS (np, aborttag); + + lp->actlink = 1; + + /* + ** Chain into LUN list + */ + tp->jump_lcb.l_paddr = vtophys (&lp->jump_lcb); + tp->lp[lun] = lp; + + } + + /* + ** Allocate a nccb + */ + cp = (nccb_p) malloc (sizeof (struct nccb), M_DEVBUF, M_NOWAIT|M_ZERO); + + if (!cp) + return (NULL); + + if (DEBUG_FLAGS & DEBUG_ALLOC) { + printf ("new nccb @%p.\n", cp); + } + + /* + ** Fill in physical addresses + */ + + cp->p_nccb = vtophys (cp); + + /* + ** Chain into reselect list + */ + cp->jump_nccb.l_cmd = SCR_JUMP; + cp->jump_nccb.l_paddr = lp->jump_nccb.l_paddr; + lp->jump_nccb.l_paddr = CCB_PHYS (cp, jump_nccb); + cp->call_tmp.l_cmd = SCR_CALL; + cp->call_tmp.l_paddr = NCB_SCRIPT_PHYS (np, resel_tmp); + + /* + ** Chain into wakeup list + */ + cp->link_nccb = np->link_nccb; + np->link_nccb = cp; + + /* + ** Chain into CCB list + */ + cp->next_nccb = lp->next_nccb; + lp->next_nccb = cp; + + return (cp); +} + +/*========================================================== +** +** +** Build Scatter Gather Block +** +** +**========================================================== +** +** The transfer area may be scattered among +** several non adjacent physical pages. +** +** We may use MAX_SCATTER blocks. +** +**---------------------------------------------------------- +*/ + +static int ncr_scatter + (struct dsb* phys, vm_offset_t vaddr, vm_size_t datalen) +{ + u_long paddr, pnext; + + u_short segment = 0; + u_long segsize, segaddr; + u_long size, csize = 0; + u_long chunk = MAX_SIZE; + int free; + + bzero (&phys->data, sizeof (phys->data)); + if (!datalen) return (0); + + paddr = vtophys (vaddr); + + /* + ** insert extra break points at a distance of chunk. + ** We try to reduce the number of interrupts caused + ** by unexpected phase changes due to disconnects. + ** A typical harddisk may disconnect before ANY block. + ** If we wanted to avoid unexpected phase changes at all + ** we had to use a break point every 512 bytes. + ** Of course the number of scatter/gather blocks is + ** limited. + */ + + free = MAX_SCATTER - 1; + + if (vaddr & PAGE_MASK) free -= datalen / PAGE_SIZE; + + if (free>1) + while ((chunk * free >= 2 * datalen) && (chunk>=1024)) + chunk /= 2; + + if(DEBUG_FLAGS & DEBUG_SCATTER) + printf("ncr?:\tscattering virtual=%p size=%d chunk=%d.\n", + (void *) vaddr, (unsigned) datalen, (unsigned) chunk); + + /* + ** Build data descriptors. + */ + while (datalen && (segment < MAX_SCATTER)) { + + /* + ** this segment is empty + */ + segsize = 0; + segaddr = paddr; + pnext = paddr; + + if (!csize) csize = chunk; + + while ((datalen) && (paddr == pnext) && (csize)) { + + /* + ** continue this segment + */ + pnext = (paddr & (~PAGE_MASK)) + PAGE_SIZE; + + /* + ** Compute max size + */ + + size = pnext - paddr; /* page size */ + if (size > datalen) size = datalen; /* data size */ + if (size > csize ) size = csize ; /* chunksize */ + + segsize += size; + vaddr += size; + csize -= size; + datalen -= size; + paddr = vtophys (vaddr); + }; + + if(DEBUG_FLAGS & DEBUG_SCATTER) + printf ("\tseg #%d addr=%x size=%d (rest=%d).\n", + segment, + (unsigned) segaddr, + (unsigned) segsize, + (unsigned) datalen); + + phys->data[segment].addr = segaddr; + phys->data[segment].size = segsize; + segment++; + } + + if (datalen) { + printf("ncr?: scatter/gather failed (residue=%d).\n", + (unsigned) datalen); + return (-1); + }; + + return (segment); +} + +/*========================================================== +** +** +** Test the pci bus snoop logic :-( +** +** Has to be called with interrupts disabled. +** +** +**========================================================== +*/ + +#ifndef NCR_IOMAPPED +static int ncr_regtest (struct ncb* np) +{ + register volatile u_int32_t data; + /* + ** ncr registers may NOT be cached. + ** write 0xffffffff to a read only register area, + ** and try to read it back. + */ + data = 0xffffffff; + OUTL_OFF(offsetof(struct ncr_reg, nc_dstat), data); + data = INL_OFF(offsetof(struct ncr_reg, nc_dstat)); +#if 1 + if (data == 0xffffffff) { +#else + if ((data & 0xe2f0fffd) != 0x02000080) { +#endif + printf ("CACHE TEST FAILED: reg dstat-sstat2 readback %x.\n", + (unsigned) data); + return (0x10); + }; + return (0); +} +#endif + +static int ncr_snooptest (struct ncb* np) +{ + u_int32_t ncr_rd, ncr_wr, ncr_bk, host_rd, host_wr, pc; + int i, err=0; +#ifndef NCR_IOMAPPED + err |= ncr_regtest (np); + if (err) return (err); +#endif + /* + ** init + */ + pc = NCB_SCRIPTH_PHYS (np, snooptest); + host_wr = 1; + ncr_wr = 2; + /* + ** Set memory and register. + */ + ncr_cache = host_wr; + OUTL (nc_temp, ncr_wr); + /* + ** Start script (exchange values) + */ + OUTL (nc_dsp, pc); + /* + ** Wait 'til done (with timeout) + */ + for (i=0; i<NCR_SNOOP_TIMEOUT; i++) + if (INB(nc_istat) & (INTF|SIP|DIP)) + break; + /* + ** Save termination position. + */ + pc = INL (nc_dsp); + /* + ** Read memory and register. + */ + host_rd = ncr_cache; + ncr_rd = INL (nc_scratcha); + ncr_bk = INL (nc_temp); + /* + ** Reset ncr chip + */ + OUTB (nc_istat, SRST); + DELAY (1000); + OUTB (nc_istat, 0 ); + /* + ** check for timeout + */ + if (i>=NCR_SNOOP_TIMEOUT) { + printf ("CACHE TEST FAILED: timeout.\n"); + return (0x20); + }; + /* + ** Check termination position. + */ + if (pc != NCB_SCRIPTH_PHYS (np, snoopend)+8) { + printf ("CACHE TEST FAILED: script execution failed.\n"); + printf ("start=%08lx, pc=%08lx, end=%08lx\n", + (u_long) NCB_SCRIPTH_PHYS (np, snooptest), (u_long) pc, + (u_long) NCB_SCRIPTH_PHYS (np, snoopend) +8); + return (0x40); + }; + /* + ** Show results. + */ + if (host_wr != ncr_rd) { + printf ("CACHE TEST FAILED: host wrote %d, ncr read %d.\n", + (int) host_wr, (int) ncr_rd); + err |= 1; + }; + if (host_rd != ncr_wr) { + printf ("CACHE TEST FAILED: ncr wrote %d, host read %d.\n", + (int) ncr_wr, (int) host_rd); + err |= 2; + }; + if (ncr_bk != ncr_wr) { + printf ("CACHE TEST FAILED: ncr wrote %d, read back %d.\n", + (int) ncr_wr, (int) ncr_bk); + err |= 4; + }; + return (err); +} + +/*========================================================== +** +** +** Profiling the drivers and targets performance. +** +** +**========================================================== +*/ + +/* +** Compute the difference in milliseconds. +**/ + +static int ncr_delta (int *from, int *to) +{ + if (!from) return (-1); + if (!to) return (-2); + return ((to - from) * 1000 / hz); +} + +#define PROFILE cp->phys.header.stamp +static void ncb_profile (ncb_p np, nccb_p cp) +{ + int co, da, st, en, di, se, post,work,disc; + u_long diff; + + PROFILE.end = ticks; + + st = ncr_delta (&PROFILE.start,&PROFILE.status); + if (st<0) return; /* status not reached */ + + da = ncr_delta (&PROFILE.start,&PROFILE.data); + if (da<0) return; /* No data transfer phase */ + + co = ncr_delta (&PROFILE.start,&PROFILE.command); + if (co<0) return; /* command not executed */ + + en = ncr_delta (&PROFILE.start,&PROFILE.end), + di = ncr_delta (&PROFILE.start,&PROFILE.disconnect), + se = ncr_delta (&PROFILE.start,&PROFILE.select); + post = en - st; + + /* + ** @PROFILE@ Disconnect time invalid if multiple disconnects + */ + + if (di>=0) disc = se-di; else disc = 0; + + work = (st - co) - disc; + + diff = (np->disc_phys - np->disc_ref) & 0xff; + np->disc_ref += diff; + + np->profile.num_trans += 1; + if (cp->ccb) + np->profile.num_bytes += cp->ccb->csio.dxfer_len; + np->profile.num_disc += diff; + np->profile.ms_setup += co; + np->profile.ms_data += work; + np->profile.ms_disc += disc; + np->profile.ms_post += post; +} +#undef PROFILE + +/*========================================================== +** +** Determine the ncr's clock frequency. +** This is essential for the negotiation +** of the synchronous transfer rate. +** +**========================================================== +** +** Note: we have to return the correct value. +** THERE IS NO SAVE DEFAULT VALUE. +** +** Most NCR/SYMBIOS boards are delivered with a 40 Mhz clock. +** 53C860 and 53C875 rev. 1 support fast20 transfers but +** do not have a clock doubler and so are provided with a +** 80 MHz clock. All other fast20 boards incorporate a doubler +** and so should be delivered with a 40 MHz clock. +** The future fast40 chips (895/895) use a 40 Mhz base clock +** and provide a clock quadrupler (160 Mhz). The code below +** tries to deal as cleverly as possible with all this stuff. +** +**---------------------------------------------------------- +*/ + +/* + * Select NCR SCSI clock frequency + */ +static void ncr_selectclock(ncb_p np, u_char scntl3) +{ + if (np->multiplier < 2) { + OUTB(nc_scntl3, scntl3); + return; + } + + if (bootverbose >= 2) + printf ("%s: enabling clock multiplier\n", ncr_name(np)); + + OUTB(nc_stest1, DBLEN); /* Enable clock multiplier */ + if (np->multiplier > 2) { /* Poll bit 5 of stest4 for quadrupler */ + int i = 20; + while (!(INB(nc_stest4) & LCKFRQ) && --i > 0) + DELAY(20); + if (!i) + printf("%s: the chip cannot lock the frequency\n", ncr_name(np)); + } else /* Wait 20 micro-seconds for doubler */ + DELAY(20); + OUTB(nc_stest3, HSC); /* Halt the scsi clock */ + OUTB(nc_scntl3, scntl3); + OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock multiplier */ + OUTB(nc_stest3, 0x00); /* Restart scsi clock */ +} + +/* + * calculate NCR SCSI clock frequency (in KHz) + */ +static unsigned +ncrgetfreq (ncb_p np, int gen) +{ + int ms = 0; + /* + * Measure GEN timer delay in order + * to calculate SCSI clock frequency + * + * This code will never execute too + * many loop iterations (if DELAY is + * reasonably correct). It could get + * too low a delay (too high a freq.) + * if the CPU is slow executing the + * loop for some reason (an NMI, for + * example). For this reason we will + * if multiple measurements are to be + * performed trust the higher delay + * (lower frequency returned). + */ + OUTB (nc_stest1, 0); /* make sure clock doubler is OFF */ + OUTW (nc_sien , 0); /* mask all scsi interrupts */ + (void) INW (nc_sist); /* clear pending scsi interrupt */ + OUTB (nc_dien , 0); /* mask all dma interrupts */ + (void) INW (nc_sist); /* another one, just to be sure :) */ + OUTB (nc_scntl3, 4); /* set pre-scaler to divide by 3 */ + OUTB (nc_stime1, 0); /* disable general purpose timer */ + OUTB (nc_stime1, gen); /* set to nominal delay of (1<<gen) * 125us */ + while (!(INW(nc_sist) & GEN) && ms++ < 1000) + DELAY(1000); /* count ms */ + OUTB (nc_stime1, 0); /* disable general purpose timer */ + OUTB (nc_scntl3, 0); + /* + * Set prescaler to divide by whatever "0" means. + * "0" ought to choose divide by 2, but appears + * to set divide by 3.5 mode in my 53c810 ... + */ + OUTB (nc_scntl3, 0); + + if (bootverbose >= 2) + printf ("\tDelay (GEN=%d): %u msec\n", gen, ms); + /* + * adjust for prescaler, and convert into KHz + */ + return ms ? ((1 << gen) * 4440) / ms : 0; +} + +static void ncr_getclock (ncb_p np, u_char multiplier) +{ + unsigned char scntl3; + unsigned char stest1; + scntl3 = INB(nc_scntl3); + stest1 = INB(nc_stest1); + + np->multiplier = 1; + + if (multiplier > 1) { + np->multiplier = multiplier; + np->clock_khz = 40000 * multiplier; + } else { + if ((scntl3 & 7) == 0) { + unsigned f1, f2; + /* throw away first result */ + (void) ncrgetfreq (np, 11); + f1 = ncrgetfreq (np, 11); + f2 = ncrgetfreq (np, 11); + + if (bootverbose >= 2) + printf ("\tNCR clock is %uKHz, %uKHz\n", f1, f2); + if (f1 > f2) f1 = f2; /* trust lower result */ + if (f1 > 45000) { + scntl3 = 5; /* >45Mhz: assume 80MHz */ + } else { + scntl3 = 3; /* <45Mhz: assume 40MHz */ + } + } + else if ((scntl3 & 7) == 5) + np->clock_khz = 80000; /* Probably a 875 rev. 1 ? */ + } +} + +/*=========================================================================*/ + +#ifdef NCR_TEKRAM_EEPROM + +struct tekram_eeprom_dev { + u_char devmode; +#define TKR_PARCHK 0x01 +#define TKR_TRYSYNC 0x02 +#define TKR_ENDISC 0x04 +#define TKR_STARTUNIT 0x08 +#define TKR_USETAGS 0x10 +#define TKR_TRYWIDE 0x20 + u_char syncparam; /* max. sync transfer rate (table ?) */ + u_char filler1; + u_char filler2; +}; + + +struct tekram_eeprom { + struct tekram_eeprom_dev + dev[16]; + u_char adaptid; + u_char adaptmode; +#define TKR_ADPT_GT2DRV 0x01 +#define TKR_ADPT_GT1GB 0x02 +#define TKR_ADPT_RSTBUS 0x04 +#define TKR_ADPT_ACTNEG 0x08 +#define TKR_ADPT_NOSEEK 0x10 +#define TKR_ADPT_MORLUN 0x20 + u_char delay; /* unit ? ( table ??? ) */ + u_char tags; /* use 4 times as many ... */ + u_char filler[60]; +}; + +static void +tekram_write_bit (ncb_p np, int bit) +{ + u_char val = 0x10 + ((bit & 1) << 1); + + DELAY(10); + OUTB (nc_gpreg, val); + DELAY(10); + OUTB (nc_gpreg, val | 0x04); + DELAY(10); + OUTB (nc_gpreg, val); + DELAY(10); +} + +static int +tekram_read_bit (ncb_p np) +{ + OUTB (nc_gpreg, 0x10); + DELAY(10); + OUTB (nc_gpreg, 0x14); + DELAY(10); + return INB (nc_gpreg) & 1; +} + +static u_short +read_tekram_eeprom_reg (ncb_p np, int reg) +{ + int bit; + u_short result = 0; + int cmd = 0x80 | reg; + + OUTB (nc_gpreg, 0x10); + + tekram_write_bit (np, 1); + for (bit = 7; bit >= 0; bit--) + { + tekram_write_bit (np, cmd >> bit); + } + + for (bit = 0; bit < 16; bit++) + { + result <<= 1; + result |= tekram_read_bit (np); + } + + OUTB (nc_gpreg, 0x00); + return result; +} + +static int +read_tekram_eeprom(ncb_p np, struct tekram_eeprom *buffer) +{ + u_short *p = (u_short *) buffer; + u_short sum = 0; + int i; + + if (INB (nc_gpcntl) != 0x09) + { + return 0; + } + for (i = 0; i < 64; i++) + { + u_short val; +if((i&0x0f) == 0) printf ("%02x:", i*2); + val = read_tekram_eeprom_reg (np, i); + if (p) + *p++ = val; + sum += val; +if((i&0x01) == 0x00) printf (" "); + printf ("%02x%02x", val & 0xff, (val >> 8) & 0xff); +if((i&0x0f) == 0x0f) printf ("\n"); + } +printf ("Sum = %04x\n", sum); + return sum == 0x1234; +} +#endif /* NCR_TEKRAM_EEPROM */ + +static device_method_t ncr_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ncr_probe), + DEVMETHOD(device_attach, ncr_attach), + + { 0, 0 } +}; + +static driver_t ncr_driver = { + "ncr", + ncr_methods, + sizeof(struct ncb), +}; + +static devclass_t ncr_devclass; + +DRIVER_MODULE(if_ncr, pci, ncr_driver, ncr_devclass, 0, 0); + +/*=========================================================================*/ +#endif /* _KERNEL */ diff --git a/sys/pci/ncrreg.h b/sys/pci/ncrreg.h new file mode 100644 index 0000000..fcc7212 --- /dev/null +++ b/sys/pci/ncrreg.h @@ -0,0 +1,573 @@ +/************************************************************************** +** +** $FreeBSD$ +** +** Device driver for the NCR 53C810 PCI-SCSI-Controller. +** +** 386bsd / FreeBSD / NetBSD +** +**------------------------------------------------------------------------- +** +** Written for 386bsd and FreeBSD by +** wolf@cologne.de Wolfgang Stanglmeier +** se@mi.Uni-Koeln.de Stefan Esser +** +** Ported to NetBSD by +** mycroft@gnu.ai.mit.edu +** +**------------------------------------------------------------------------- +** +** Copyright (c) 1994 Wolfgang Stanglmeier. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** +*************************************************************************** +*/ + +#ifndef __NCR_REG_H__ +#define __NCR_REG_H__ + +/*----------------------------------------------------------------- +** +** The ncr 53c810 register structure. +** +**----------------------------------------------------------------- +*/ + +struct ncr_reg { +/*00*/ u_char nc_scntl0; /* full arb., ena parity, par->ATN */ + +/*01*/ u_char nc_scntl1; /* no reset */ + #define ISCON 0x10 /* connected to scsi */ + #define CRST 0x08 /* force reset */ + +/*02*/ u_char nc_scntl2; /* no disconnect expected */ + #define SDU 0x80 /* cmd: disconnect will raise error */ + #define CHM 0x40 /* sta: chained mode */ + #define WSS 0x08 /* sta: wide scsi send [W]*/ + #define WSR 0x01 /* sta: wide scsi received [W]*/ + +/*03*/ u_char nc_scntl3; /* cnf system clock dependent */ + #define EWS 0x08 /* cmd: enable wide scsi [W]*/ + +/*04*/ u_char nc_scid; /* cnf host adapter scsi address */ + #define RRE 0x40 /* r/w:e enable response to resel. */ + #define SRE 0x20 /* r/w:e enable response to select */ + +/*05*/ u_char nc_sxfer; /* ### Sync speed and count */ + +/*06*/ u_char nc_sdid; /* ### Destination-ID */ + +/*07*/ u_char nc_gpreg; /* ??? IO-Pins */ + +/*08*/ u_char nc_sfbr; /* ### First byte in phase */ + +/*09*/ u_char nc_socl; + #define CREQ 0x80 /* r/w: SCSI-REQ */ + #define CACK 0x40 /* r/w: SCSI-ACK */ + #define CBSY 0x20 /* r/w: SCSI-BSY */ + #define CSEL 0x10 /* r/w: SCSI-SEL */ + #define CATN 0x08 /* r/w: SCSI-ATN */ + #define CMSG 0x04 /* r/w: SCSI-MSG */ + #define CC_D 0x02 /* r/w: SCSI-C_D */ + #define CI_O 0x01 /* r/w: SCSI-I_O */ + +/*0a*/ u_char nc_ssid; + +/*0b*/ u_char nc_sbcl; + +/*0c*/ u_char nc_dstat; + #define DFE 0x80 /* sta: dma fifo empty */ + #define MDPE 0x40 /* int: master data parity error */ + #define BF 0x20 /* int: script: bus fault */ + #define ABRT 0x10 /* int: script: command aborted */ + #define SSI 0x08 /* int: script: single step */ + #define SIR 0x04 /* int: script: interrupt instruct. */ + #define IID 0x01 /* int: script: illegal instruct. */ + +/*0d*/ u_char nc_sstat0; + #define ILF 0x80 /* sta: data in SIDL register lsb */ + #define ORF 0x40 /* sta: data in SODR register lsb */ + #define OLF 0x20 /* sta: data in SODL register lsb */ + #define AIP 0x10 /* sta: arbitration in progress */ + #define LOA 0x08 /* sta: arbitration lost */ + #define WOA 0x04 /* sta: arbitration won */ + #define IRST 0x02 /* sta: scsi reset signal */ + #define SDP 0x01 /* sta: scsi parity signal */ + +/*0e*/ u_char nc_sstat1; + #define FF3210 0xf0 /* sta: bytes in the scsi fifo */ + +/*0f*/ u_char nc_sstat2; + #define ILF1 0x80 /* sta: data in SIDL register msb[W]*/ + #define ORF1 0x40 /* sta: data in SODR register msb[W]*/ + #define OLF1 0x20 /* sta: data in SODL register msb[W]*/ + #define LDSC 0x02 /* sta: disconnect & reconnect */ + +/*10*/ u_int32_t nc_dsa; /* --> Base page */ + +/*14*/ u_char nc_istat; /* --> Main Command and status */ + #define CABRT 0x80 /* cmd: abort current operation */ + #define SRST 0x40 /* mod: reset chip */ + #define SIGP 0x20 /* r/w: message from host to ncr */ + #define SEM 0x10 /* r/w: message between host + ncr */ + #define CON 0x08 /* sta: connected to scsi */ + #define INTF 0x04 /* sta: int on the fly (reset by wr)*/ + #define SIP 0x02 /* sta: scsi-interrupt */ + #define DIP 0x01 /* sta: host/script interrupt */ + +/*15*/ u_char nc_15_; +/*16*/ u_char nc_16_; +/*17*/ u_char nc_17_; + +/*18*/ u_char nc_ctest0; +/*19*/ u_char nc_ctest1; + +/*1a*/ u_char nc_ctest2; + #define CSIGP 0x40 + +/*1b*/ u_char nc_ctest3; + #define FLF 0x08 /* cmd: flush dma fifo */ + #define CLF 0x04 /* cmd: clear dma fifo */ + #define FM 0x02 /* mod: fetch pin mode */ + #define WRIE 0x01 /* mod: write and invalidate enable */ + +/*1c*/ u_int32_t nc_temp; /* ### Temporary stack */ + +/*20*/ u_char nc_dfifo; +/*21*/ u_char nc_ctest4; + #define BDIS 0x80 /* mod: burst disable */ + #define MPEE 0x08 /* mod: master parity error enable */ + +/*22*/ u_char nc_ctest5; + #define DFS 0x20 /* mod: dma fifo size */ +/*23*/ u_char nc_ctest6; + +/*24*/ u_int32_t nc_dbc; /* ### Byte count and command */ +/*28*/ u_int32_t nc_dnad; /* ### Next command register */ +/*2c*/ u_int32_t nc_dsp; /* --> Script Pointer */ +/*30*/ u_int32_t nc_dsps; /* --> Script pointer save/opcode#2 */ +/*34*/ u_int32_t nc_scratcha; /* ??? Temporary register a */ + +/*38*/ u_char nc_dmode; + #define BL_2 0x80 /* mod: burst length shift value +2 */ + #define BL_1 0x40 /* mod: burst length shift value +1 */ + #define ERL 0x08 /* mod: enable read line */ + #define ERMP 0x04 /* mod: enable read multiple */ + #define BOF 0x02 /* mod: burst op code fetch */ + +/*39*/ u_char nc_dien; +/*3a*/ u_char nc_dwt; + +/*3b*/ u_char nc_dcntl; /* --> Script execution control */ + #define CLSE 0x80 /* mod: cache line size enable */ + #define PFF 0x40 /* cmd: pre-fetch flush */ + #define PFEN 0x20 /* mod: pre-fetch enable */ + #define SSM 0x10 /* mod: single step mode */ + #define IRQM 0x08 /* mod: irq mode (1 = totem pole !) */ + #define STD 0x04 /* cmd: start dma mode */ + #define IRQD 0x02 /* mod: irq disable */ + #define NOCOM 0x01 /* cmd: protect sfbr while reselect */ + +/*3c*/ u_int32_t nc_adder; + +/*40*/ u_short nc_sien; /* -->: interrupt enable */ +/*42*/ u_short nc_sist; /* <--: interrupt status */ + #define STO 0x0400/* sta: timeout (select) */ + #define GEN 0x0200/* sta: timeout (general) */ + #define HTH 0x0100/* sta: timeout (handshake) */ + #define MA 0x80 /* sta: phase mismatch */ + #define CMP 0x40 /* sta: arbitration complete */ + #define SEL 0x20 /* sta: selected by another device */ + #define RSL 0x10 /* sta: reselected by another device*/ + #define SGE 0x08 /* sta: gross error (over/underflow)*/ + #define UDC 0x04 /* sta: unexpected disconnect */ + #define RST 0x02 /* sta: scsi bus reset detected */ + #define PAR 0x01 /* sta: scsi parity error */ + +/*44*/ u_char nc_slpar; +/*45*/ u_char nc_swide; +/*46*/ u_char nc_macntl; +/*47*/ u_char nc_gpcntl; +/*48*/ u_char nc_stime0; /* cmd: timeout for select&handshake*/ +/*49*/ u_char nc_stime1; /* cmd: timeout user defined */ +/*4a*/ u_short nc_respid; /* sta: Reselect-IDs */ + +/*4c*/ u_char nc_stest0; + +/*4d*/ u_char nc_stest1; + #define DBLEN 0x08 /* clock doubler running */ + #define DBLSEL 0x04 /* clock doubler selected */ + +/*4e*/ u_char nc_stest2; + #define ROF 0x40 /* reset scsi offset (after gross error!) */ + #define EXT 0x02 /* extended filtering */ + +/*4f*/ u_char nc_stest3; + #define TE 0x80 /* c: tolerAnt enable */ + #define HSC 0x20 /* c: Halt SCSI Clock */ + #define CSF 0x02 /* c: clear scsi fifo */ + +/*50*/ u_short nc_sidl; /* Lowlevel: latched from scsi data */ +/*52*/ u_char nc_stest4; + #define SMODE 0xc0 /* SCSI bus mode (895/6 only) */ + #define SMODE_HVD 0x40 /* High Voltage Differential */ + #define SMODE_SE 0x80 /* Single Ended */ + #define SMODE_LVD 0xc0 /* Low Voltage Differential */ + #define LCKFRQ 0x20 /* Frequency Lock (895/6 only) */ + +/*53*/ u_char nc_53_; +/*54*/ u_short nc_sodl; /* Lowlevel: data out to scsi data */ +/*56*/ u_short nc_56_; +/*58*/ u_short nc_sbdl; /* Lowlevel: data from scsi data */ +/*5a*/ u_short nc_5a_; +/*5c*/ u_char nc_scr0; /* Working register B */ +/*5d*/ u_char nc_scr1; /* */ +/*5e*/ u_char nc_scr2; /* */ +/*5f*/ u_char nc_scr3; /* */ +/*60*/ +}; + +/*----------------------------------------------------------- +** +** Utility macros for the script. +** +**----------------------------------------------------------- +*/ + +#define REGJ(p,r) (offsetof(struct ncr_reg, p ## r)) +#define REG(r) REGJ (nc_, r) + +#ifndef TARGET_MODE +#define TARGET_MODE 0 +#endif + +typedef u_int32_t ncrcmd; + +/*----------------------------------------------------------- +** +** SCSI phases +** +**----------------------------------------------------------- +*/ + +#define SCR_DATA_OUT 0x00000000 +#define SCR_DATA_IN 0x01000000 +#define SCR_COMMAND 0x02000000 +#define SCR_STATUS 0x03000000 +#define SCR_ILG_OUT 0x04000000 +#define SCR_ILG_IN 0x05000000 +#define SCR_MSG_OUT 0x06000000 +#define SCR_MSG_IN 0x07000000 + +/*----------------------------------------------------------- +** +** Data transfer via SCSI. +** +**----------------------------------------------------------- +** +** MOVE_ABS (LEN) +** <<start address>> +** +** MOVE_IND (LEN) +** <<dnad_offset>> +** +** MOVE_TBL +** <<dnad_offset>> +** +**----------------------------------------------------------- +*/ + +#define SCR_MOVE_ABS(l) ((0x08000000 ^ (TARGET_MODE << 1ul)) | (l)) +#define SCR_MOVE_IND(l) ((0x28000000 ^ (TARGET_MODE << 1ul)) | (l)) +#define SCR_MOVE_TBL (0x18000000 ^ (TARGET_MODE << 1ul)) + +struct scr_tblmove { + u_int32_t size; + u_int32_t addr; +}; + +/*----------------------------------------------------------- +** +** Selection +** +**----------------------------------------------------------- +** +** SEL_ABS | SCR_ID (0..7) [ | REL_JMP] +** <<alternate_address>> +** +** SEL_TBL | << dnad_offset>> [ | REL_JMP] +** <<alternate_address>> +** +**----------------------------------------------------------- +*/ + +#define SCR_SEL_ABS 0x40000000 +#define SCR_SEL_ABS_ATN 0x41000000 +#define SCR_SEL_TBL 0x42000000 +#define SCR_SEL_TBL_ATN 0x43000000 + +struct scr_tblsel { + u_char sel_0; + u_char sel_sxfer; + u_char sel_id; + u_char sel_scntl3; +}; + +#define SCR_JMP_REL 0x04000000 +#define SCR_ID(id) (((u_int32_t)(id)) << 16) + +/*----------------------------------------------------------- +** +** Waiting for Disconnect or Reselect +** +**----------------------------------------------------------- +** +** WAIT_DISC +** dummy: <<alternate_address>> +** +** WAIT_RESEL +** <<alternate_address>> +** +**----------------------------------------------------------- +*/ + +#define SCR_WAIT_DISC 0x48000000 +#define SCR_WAIT_RESEL 0x50000000 + +/*----------------------------------------------------------- +** +** Bit Set / Reset +** +**----------------------------------------------------------- +** +** SET (flags {|.. }) +** +** CLR (flags {|.. }) +** +**----------------------------------------------------------- +*/ + +#define SCR_SET(f) (0x58000000 | (f)) +#define SCR_CLR(f) (0x60000000 | (f)) + +#define SCR_CARRY 0x00000400 +#define SCR_TRG 0x00000200 +#define SCR_ACK 0x00000040 +#define SCR_ATN 0x00000008 + + +/*----------------------------------------------------------- +** +** Memory to memory move +** +**----------------------------------------------------------- +** +** COPY (bytecount) +** << source_address >> +** << destination_address >> +** +** SCR_COPY sets the NO FLUSH option by default. +** SCR_COPY_F does not set this option. +** +** For chips which do not support this option, +** ncr_copy_and_bind() will remove this bit. +**----------------------------------------------------------- +*/ + +#define SCR_NO_FLUSH 0x01000000 + +#define SCR_COPY(n) (0xc0000000 | SCR_NO_FLUSH | (n)) +#define SCR_COPY_F(n) (0xc0000000 | (n)) + + +/*----------------------------------------------------------- +** +** Register move and binary operations +** +**----------------------------------------------------------- +** +** SFBR_REG (reg, op, data) reg = SFBR op data +** << 0 >> +** +** REG_SFBR (reg, op, data) SFBR = reg op data +** << 0 >> +** +** REG_REG (reg, op, data) reg = reg op data +** << 0 >> +** +**----------------------------------------------------------- +*/ + +#define SCR_REG_OFS(ofs) ((ofs) << 16ul) + +#define SCR_SFBR_REG(reg,op,data) \ + (0x68000000 | (SCR_REG_OFS(REG(reg))) | (op) | ((data)<<8ul)) + +#define SCR_REG_SFBR(reg,op,data) \ + (0x70000000 | (SCR_REG_OFS(REG(reg))) | (op) | ((data)<<8ul)) + +#define SCR_REG_REG(reg,op,data) \ + (0x78000000 | (SCR_REG_OFS(REG(reg))) | (op) | ((data)<<8ul)) + + +#define SCR_LOAD 0x00000000 +#define SCR_SHL 0x01000000 +#define SCR_OR 0x02000000 +#define SCR_XOR 0x03000000 +#define SCR_AND 0x04000000 +#define SCR_SHR 0x05000000 +#define SCR_ADD 0x06000000 +#define SCR_ADDC 0x07000000 + +/*----------------------------------------------------------- +** +** FROM_REG (reg) reg = SFBR +** << 0 >> +** +** TO_REG (reg) SFBR = reg +** << 0 >> +** +** LOAD_REG (reg, data) reg = <data> +** << 0 >> +** +** LOAD_SFBR(data) SFBR = <data> +** << 0 >> +** +**----------------------------------------------------------- +*/ + +#define SCR_FROM_REG(reg) \ + SCR_REG_SFBR(reg,SCR_OR,0) + +#define SCR_TO_REG(reg) \ + SCR_SFBR_REG(reg,SCR_OR,0) + +#define SCR_LOAD_REG(reg,data) \ + SCR_REG_REG(reg,SCR_LOAD,data) + +#define SCR_LOAD_SFBR(data) \ + (SCR_REG_SFBR (gpreg, SCR_LOAD, data)) + +/*----------------------------------------------------------- +** +** Waiting for Disconnect or Reselect +** +**----------------------------------------------------------- +** +** JUMP [ | IFTRUE/IFFALSE ( ... ) ] +** <<address>> +** +** JUMPR [ | IFTRUE/IFFALSE ( ... ) ] +** <<distance>> +** +** CALL [ | IFTRUE/IFFALSE ( ... ) ] +** <<address>> +** +** CALLR [ | IFTRUE/IFFALSE ( ... ) ] +** <<distance>> +** +** RETURN [ | IFTRUE/IFFALSE ( ... ) ] +** <<dummy>> +** +** INT [ | IFTRUE/IFFALSE ( ... ) ] +** <<ident>> +** +** INT_FLY [ | IFTRUE/IFFALSE ( ... ) ] +** <<ident>> +** +** Conditions: +** WHEN (phase) +** IF (phase) +** CARRY +** DATA (data, mask) +** +**----------------------------------------------------------- +*/ + +#define SCR_NO_OP 0x80000000 +#define SCR_JUMP 0x80080000 +#define SCR_JUMPR 0x80880000 +#define SCR_CALL 0x88080000 +#define SCR_CALLR 0x88880000 +#define SCR_RETURN 0x90080000 +#define SCR_INT 0x98080000 +#define SCR_INT_FLY 0x98180000 + +#define IFFALSE(arg) (0x00080000 | (arg)) +#define IFTRUE(arg) (0x00000000 | (arg)) + +#define WHEN(phase) (0x00030000 | (phase)) +#define IF(phase) (0x00020000 | (phase)) + +#define DATA(D) (0x00040000 | ((D) & 0xff)) +#define MASK(D,M) (0x00040000 | (((M ^ 0xff) & 0xff) << 8ul)|((D) & 0xff)) + +#define CARRYSET (0x00200000) + +/*----------------------------------------------------------- +** +** SCSI constants. +** +**----------------------------------------------------------- +*/ + +/* +** Messages +*/ +#define M_X_MODIFY_DP (0x00) + +/* +** Status +*/ +#define SCSI_STATUS_ILLEGAL (0xff) +#define SCSI_STATUS_SENSE (0x80) + +/* +** Bits defining chip features. +** For now only some of them are used, since we explicitely +** deal with PCI device id and revision id. +*/ +#define FE_LED0 (1<<0) +#define FE_WIDE (1<<1) +#define FE_ULTRA (1<<2) +#define FE_ULTRA2 (1<<3) +#define FE_DBLR (1<<4) +#define FE_QUAD (1<<5) +#define FE_ERL (1<<6) +#define FE_CLSE (1<<7) +#define FE_WRIE (1<<8) +#define FE_ERMP (1<<9) +#define FE_BOF (1<<10) +#define FE_DFS (1<<11) +#define FE_PFEN (1<<12) +#define FE_LDSTR (1<<13) +#define FE_RAM (1<<14) +#define FE_CLK80 (1<<15) +#define FE_DIFF (1<<16) +#define FE_BIOS (1<<17) +#define FE_CACHE_SET (FE_ERL|FE_CLSE|FE_WRIE|FE_ERMP) +#define FE_SCSI_SET (FE_WIDE|FE_ULTRA|FE_ULTRA2|FE_DBLR|FE_QUAD|F_CLK80) +#define FE_SPECIAL_SET (FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM) + +#endif /*__NCR_REG_H__*/ diff --git a/sys/pci/ohci_pci.c b/sys/pci/ohci_pci.c new file mode 100644 index 0000000..3b6f6b9 --- /dev/null +++ b/sys/pci/ohci_pci.c @@ -0,0 +1,322 @@ +/* $FreeBSD$ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (augustss@carlstedt.se) at + * Carlstedt Research & Technology. + * + * 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 acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +/* + * USB Open Host Controller driver. + * + * OHCI spec: http://www.intel.com/design/usb/ohci11d.pdf + */ + +/* The low level controller code for OHCI has been split into + * PCI probes and OHCI specific code. This was done to facilitate the + * sharing of code between *BSD's + */ + + +#include "opt_bus.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/queue.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <pci/pcivar.h> +#include <pci/pcireg.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usb_mem.h> + +#include <dev/usb/ohcireg.h> +#include <dev/usb/ohcivar.h> + +#define PCI_OHCI_DEVICEID_ALADDIN_V 0x523710b9 +static const char *ohci_device_aladdin_v = "AcerLabs M5237 (Aladdin-V) USB controller"; + +#define PCI_OHCI_DEVICEID_AMD756 0x740c1022 +static const char *ohci_device_amd756 = "AMD-756 USB Controller"; + +#define PCI_OHCI_DEVICEID_AMD766 0x74141022 +static const char *ohci_device_amd766 = "AMD-766 USB Controller"; + +#define PCI_OHCI_DEVICEID_FIRELINK 0xc8611045 +static const char *ohci_device_firelink = "OPTi 82C861 (FireLink) USB controller"; + +#define PCI_OHCI_DEVICEID_NEC 0x00351033 +static const char *ohci_device_nec = "NEC uPD 9210 USB controller"; + +#define PCI_OHCI_DEVICEID_USB0670 0x06701095 +static const char *ohci_device_usb0670 = "CMD Tech 670 (USB0670) USB controller"; + +#define PCI_OHCI_DEVICEID_USB0673 0x06731095 +static const char *ohci_device_usb0673 = "CMD Tech 673 (USB0673) USB controller"; + +#define PCI_OHCI_DEVICEID_SIS5571 0x70011039 +static const char *ohci_device_sis5571 = "SiS 5571 USB controller"; + +#define PCI_OHCI_DEVICEID_KEYLARGO 0x0019106b +static const char *ohci_device_keylargo = "Apple KeyLargo USB controller"; + +static const char *ohci_device_generic = "OHCI (generic) USB controller"; + +#define PCI_OHCI_BASE_REG 0x10 + + +static int ohci_pci_attach(device_t self); +static int ohci_pci_detach(device_t self); + +static const char * +ohci_pci_match(device_t self) +{ + u_int32_t device_id = pci_get_devid(self); + + switch (device_id) { + case PCI_OHCI_DEVICEID_ALADDIN_V: + return (ohci_device_aladdin_v); + case PCI_OHCI_DEVICEID_AMD756: + return (ohci_device_amd756); + case PCI_OHCI_DEVICEID_AMD766: + return (ohci_device_amd766); + case PCI_OHCI_DEVICEID_USB0670: + return (ohci_device_usb0670); + case PCI_OHCI_DEVICEID_USB0673: + return (ohci_device_usb0673); + case PCI_OHCI_DEVICEID_FIRELINK: + return (ohci_device_firelink); + case PCI_OHCI_DEVICEID_NEC: + return (ohci_device_nec); + case PCI_OHCI_DEVICEID_SIS5571: + return (ohci_device_sis5571); + case PCI_OHCI_DEVICEID_KEYLARGO: + return (ohci_device_keylargo); + default: + if (pci_get_class(self) == PCIC_SERIALBUS + && pci_get_subclass(self) == PCIS_SERIALBUS_USB + && pci_get_progif(self) == PCI_INTERFACE_OHCI) { + return (ohci_device_generic); + } + } + + return NULL; /* dunno */ +} + +static int +ohci_pci_probe(device_t self) +{ + const char *desc = ohci_pci_match(self); + + if (desc) { + device_set_desc(self, desc); + return 0; + } else { + return ENXIO; + } +} + +static int +ohci_pci_attach(device_t self) +{ + ohci_softc_t *sc = device_get_softc(self); + int err; + int rid; + + /* XXX where does it say so in the spec? */ + sc->sc_bus.usbrev = USBREV_1_0; + + rid = PCI_CBMEM; + sc->io_res = bus_alloc_resource(self, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!sc->io_res) { + device_printf(self, "Could not map memory\n"); + return ENXIO; + } + sc->iot = rman_get_bustag(sc->io_res); + sc->ioh = rman_get_bushandle(sc->io_res); + + rid = 0; + sc->irq_res = bus_alloc_resource(self, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(self, "Could not allocate irq\n"); + ohci_pci_detach(self); + return ENXIO; + } + sc->sc_bus.bdev = device_add_child(self, "usb", -1); + if (!sc->sc_bus.bdev) { + device_printf(self, "Could not add USB device\n"); + ohci_pci_detach(self); + return ENOMEM; + } + device_set_ivars(sc->sc_bus.bdev, sc); + + switch (pci_get_devid(self)) { + case PCI_OHCI_DEVICEID_ALADDIN_V: + device_set_desc(sc->sc_bus.bdev, ohci_device_aladdin_v); + sprintf(sc->sc_vendor, "AcerLabs"); + break; + case PCI_OHCI_DEVICEID_AMD756: + device_set_desc(sc->sc_bus.bdev, ohci_device_amd756); + sprintf(sc->sc_vendor, "AMD"); + break; + case PCI_OHCI_DEVICEID_AMD766: + device_set_desc(sc->sc_bus.bdev, ohci_device_amd766); + sprintf(sc->sc_vendor, "AMD"); + break; + case PCI_OHCI_DEVICEID_FIRELINK: + device_set_desc(sc->sc_bus.bdev, ohci_device_firelink); + sprintf(sc->sc_vendor, "OPTi"); + break; + case PCI_OHCI_DEVICEID_NEC: + device_set_desc(sc->sc_bus.bdev, ohci_device_nec); + sprintf(sc->sc_vendor, "NEC"); + break; + case PCI_OHCI_DEVICEID_USB0670: + device_set_desc(sc->sc_bus.bdev, ohci_device_usb0670); + sprintf(sc->sc_vendor, "CMDTECH"); + break; + case PCI_OHCI_DEVICEID_USB0673: + device_set_desc(sc->sc_bus.bdev, ohci_device_usb0673); + sprintf(sc->sc_vendor, "CMDTECH"); + break; + case PCI_OHCI_DEVICEID_SIS5571: + device_set_desc(sc->sc_bus.bdev, ohci_device_sis5571); + sprintf(sc->sc_vendor, "SiS"); + break; + case PCI_OHCI_DEVICEID_KEYLARGO: + device_set_desc(sc->sc_bus.bdev, ohci_device_keylargo); + sprintf(sc->sc_vendor, "Apple"); + break; + default: + if (bootverbose) + device_printf(self, "(New OHCI DeviceId=0x%08x)\n", + pci_get_devid(self)); + device_set_desc(sc->sc_bus.bdev, ohci_device_generic); + sprintf(sc->sc_vendor, "(unknown)"); + } + + err = bus_setup_intr(self, sc->irq_res, INTR_TYPE_BIO, + (driver_intr_t *) ohci_intr, sc, &sc->ih); + if (err) { + device_printf(self, "Could not setup irq, %d\n", err); + sc->ih = NULL; + ohci_pci_detach(self); + return ENXIO; + } + err = ohci_init(sc); + if (!err) + err = device_probe_and_attach(sc->sc_bus.bdev); + + if (err) { + device_printf(self, "USB init failed\n"); + ohci_pci_detach(self); + return EIO; + } + return 0; +} + +static int +ohci_pci_detach(device_t self) +{ + ohci_softc_t *sc = device_get_softc(self); + + /* + * XXX this code is not yet fit to be used as detach for the OHCI + * controller + */ + + /* + * disable interrupts that might have been switched on in ohci_init + */ + if (sc->iot && sc->ioh) + bus_space_write_4(sc->iot, sc->ioh, + OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); + + if (sc->irq_res && sc->ih) { + int err = bus_teardown_intr(self, sc->irq_res, sc->ih); + + if (err) + /* XXX or should we panic? */ + device_printf(self, "Could not tear down irq, %d\n", + err); + sc->ih = NULL; + } + if (sc->sc_bus.bdev) { + device_delete_child(self, sc->sc_bus.bdev); + sc->sc_bus.bdev = NULL; + } + if (sc->irq_res) { + bus_release_resource(self, SYS_RES_IRQ, 0, sc->irq_res); + sc->irq_res = NULL; + } + if (sc->io_res) { + bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM, sc->io_res); + sc->io_res = NULL; + sc->iot = 0; + sc->ioh = 0; + } + return 0; +} + +static device_method_t ohci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ohci_pci_probe), + DEVMETHOD(device_attach, ohci_pci_attach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + {0, 0} +}; + +static driver_t ohci_driver = { + "ohci", + ohci_methods, + sizeof(ohci_softc_t), +}; + +static devclass_t ohci_devclass; + +DRIVER_MODULE(ohci, pci, ohci_driver, ohci_devclass, 0, 0); diff --git a/sys/pci/simos.c b/sys/pci/simos.c new file mode 100644 index 0000000..7944d5e --- /dev/null +++ b/sys/pci/simos.c @@ -0,0 +1,350 @@ +/*- + * Copyright (c) 1998 Doug Rabson + * 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$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/bio.h> +#include <sys/buf.h> +#include <sys/proc.h> + +#include <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/cam_sim.h> +#include <cam/cam_xpt_sim.h> +#include <cam/cam_debug.h> + +#include <cam/scsi/scsi_all.h> +#include <cam/scsi/scsi_message.h> + +#include <machine/clock.h> +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/pmap.h> +#include <sys/kernel.h> + +#include <pci/simos.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +#ifndef COMPAT_OLDPCI +#error "The simos device requires the old pci compatibility shims" +#endif + +#include <machine/alpha_cpu.h> + +struct simos_softc { + int sc_unit; + SimOS_SCSI* sc_regs; + + /* + * SimOS only supports one pending command. + */ + struct cam_sim *sc_sim; + struct cam_path *sc_path; + struct ccb_scsiio *sc_pending; +}; + +struct simos_softc* simosp[10]; + +static u_long simos_unit; + +static const char *simos_probe(pcici_t tag, pcidi_t type); +static void simos_attach(pcici_t config_d, int unit); +static void simos_action(struct cam_sim *sim, union ccb *ccb); +static void simos_poll(struct cam_sim *sim); + +struct pci_device simos_driver = { + "simos", + simos_probe, + simos_attach, + &simos_unit, + NULL +}; +COMPAT_PCI_DRIVER (simos, simos_driver); + +static const char * +simos_probe(pcici_t tag, pcidi_t type) +{ + switch (type) { + case 0x1291|(0x1291<<16): + return "SimOS SCSI"; + default: + return NULL; + } + +} + +static void +simos_attach(pcici_t config_id, int unit) +{ + struct simos_softc* sc; + struct cam_devq *devq; + + sc = malloc(sizeof(struct simos_softc), M_DEVBUF, M_WAITOK | M_ZERO); + simosp[unit] = sc; + + sc->sc_unit = unit; + sc->sc_regs = (SimOS_SCSI*) SIMOS_SCSI_ADDR; + sc->sc_pending = 0; + + devq = cam_simq_alloc(/*maxopenings*/1); + if (devq == NULL) + return; + + sc->sc_sim = cam_sim_alloc(simos_action, simos_poll, "simos", sc, unit, + /*untagged*/1, /*tagged*/0, devq); + if (sc->sc_sim == NULL) { + cam_simq_free(devq); + return; + } + + if (xpt_bus_register(sc->sc_sim, /*bus*/0) != CAM_SUCCESS) { + cam_sim_free(sc->sc_sim, /*free_devq*/TRUE); + return; + } + + if (xpt_create_path(&sc->sc_path, /*periph*/NULL, + cam_sim_path(sc->sc_sim), CAM_TARGET_WILDCARD, + CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + xpt_bus_deregister(cam_sim_path(sc->sc_sim)); + cam_sim_free(sc->sc_sim, /*free_devq*/TRUE); + return; + } + + alpha_register_pci_scsi(config_id->bus, config_id->slot, sc->sc_sim); + + return; +} + +static void +simos_start(struct simos_softc* sc, struct ccb_scsiio *csio) +{ + struct scsi_generic *cmd; + int cmdlen; + caddr_t data; + int datalen; + int s; + u_int8_t* p; + int i, count, target; + vm_offset_t va; + vm_size_t size; + + cmd = (struct scsi_generic *) &csio->cdb_io.cdb_bytes; + cmdlen = csio->cdb_len; + data = csio->data_ptr; + datalen = csio->dxfer_len; + + /* + * Simos doesn't understand some commands + */ + if (cmd->opcode == START_STOP || cmd->opcode == PREVENT_ALLOW + || cmd->opcode == SYNCHRONIZE_CACHE) { + csio->ccb_h.status = CAM_REQ_CMP; + xpt_done((union ccb *) csio); + return; + } + + if (sc->sc_pending) { + /* + * Don't think this can happen. + */ + printf("simos_start: can't start command while one is pending\n"); + csio->ccb_h.status = CAM_BUSY; + xpt_done((union ccb *) csio); + return; + } + + s = splcam(); + + csio->ccb_h.status |= CAM_SIM_QUEUED; + sc->sc_pending = csio; + + target = csio->ccb_h.target_id; + + /* + * Copy the command into SimOS' buffer + */ + p = (u_int8_t*) cmd; + count = cmdlen; + for (i = 0; i < count; i++) + sc->sc_regs->cmd[i] = *p++; + sc->sc_regs->length = count; + sc->sc_regs->target = target; + sc->sc_regs->lun = csio->ccb_h.target_lun; + + /* + * Setup the segment descriptors. + */ + va = (vm_offset_t) data; + size = datalen; + i = 0; + while (size > 0) { + vm_size_t len = PAGE_SIZE - (va & PAGE_MASK); + if (len > size) + len = size; + sc->sc_regs->sgMap[i].pAddr = vtophys(va); + sc->sc_regs->sgMap[i].len = len; + size -= len; + va += len; + i++; + } + sc->sc_regs->sgLen = i; + + /* + * Start the i/o. + */ + alpha_wmb(); + sc->sc_regs->startIO = 1; + alpha_wmb(); + + splx(s); +} + +static void +simos_done(struct simos_softc* sc) +{ + struct ccb_scsiio* csio = sc->sc_pending; + int s, done; + + /* + * Spurious interrupt caused by my bogus interrupt broadcasting. + */ + if (!csio) + return; + + sc->sc_pending = 0; + + done = sc->sc_regs->done[csio->ccb_h.target_id]; + if (!done) + return; + + s = splcam(); + + if (done >> 16) + /* Error detected */ + csio->ccb_h.status = CAM_CMD_TIMEOUT; + else + csio->ccb_h.status = CAM_REQ_CMP; + + /* + * Ack the interrupt to clear it. + */ + sc->sc_regs->done[csio->ccb_h.target_id] = 1; + alpha_wmb(); + + xpt_done((union ccb *) csio); + + splx(s); +} + +static void +simos_action(struct cam_sim *sim, union ccb *ccb) +{ + struct simos_softc* sc = (struct simos_softc *)sim->softc; + + switch (ccb->ccb_h.func_code) { + case XPT_SCSI_IO: + { + struct ccb_scsiio *csio; + + csio = &ccb->csio; + simos_start(sc, csio); + break; + } + + case XPT_CALC_GEOMETRY: + { + struct ccb_calc_geometry *ccg; + u_int32_t size_mb; + u_int32_t secs_per_cylinder; + + ccg = &ccb->ccg; + size_mb = ccg->volume_size + / ((1024L * 1024L) / ccg->block_size); + + ccg->heads = 64; + ccg->secs_per_track = 32; + + secs_per_cylinder = ccg->heads * ccg->secs_per_track; + ccg->cylinders = ccg->volume_size / secs_per_cylinder; + + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } + + case XPT_RESET_BUS: + { + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } + + case XPT_PATH_INQ: + { + struct ccb_pathinq *cpi = &ccb->cpi; + + cpi->version_num = 1; /* XXX??? */ + cpi->max_target = 2; + cpi->max_lun = 0; + cpi->initiator_id = 7; + cpi->bus_id = sim->bus_id; + cpi->base_transfer_speed = 3300; + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, "SimOS", HBA_IDLEN); + strncpy(cpi->dev_name, sim->sim_name, DEV_IDLEN); + cpi->unit_number = sim->unit_number; + + cpi->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } + + default: + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } +} + +static void +simos_poll(struct cam_sim *sim) +{ + simos_done(cam_sim_softc(sim)); +} + +void +simos_intr(int unit) +{ + /* XXX bogus */ + struct simos_softc* sc = simosp[unit]; + + simos_done(sc); +} diff --git a/sys/pci/simos.h b/sys/pci/simos.h new file mode 100644 index 0000000..e703074 --- /dev/null +++ b/sys/pci/simos.h @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 1998 Doug Rabson + * 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$ + */ + +/* + * Copyright (C) 1998 by the Board of Trustees + * of Leland Stanford Junior University. + * Copyright (C) 1998 Digital Equipment Corporation + * + * This file is part of the SimOS distribution. + * See LICENSE file for terms of the license. + * + */ + + + + +#ifndef _SIMOS_SCSI_H +#define _SIMOS_SCSI_H + +#define SIMOS_SCSI_ADDR 0xfffffcc500000000 +#define SIMOS_SCSI_ADDR_32 0xffffffffa5000000 +#define SIMOS_SCSI_MAXDMA_LEN 128 +#define SIMOS_SCSI_MAXTARG 16 +#define SIMOS_SCSI_MAXLUN 16 + +#define SIMOS_SCSI_REGS ((struct SimOS_SCSI *)SIMOS_SCSI_ADDR) +#define SIMOS_SCSI_REGS_32 ((struct SimOS_SCSI *)SIMOS_SCSI_ADDR_32) + +typedef unsigned long SCSIReg; + + +typedef struct SimOS_SCSI { + SCSIReg startIO; /* write-only */ + SCSIReg done[SIMOS_SCSI_MAXTARG]; /* read-write (write=ack) */ + + SCSIReg target; /* data fields */ + SCSIReg lun; + SCSIReg cmd[12]; + SCSIReg length; + SCSIReg sgLen; + struct { + SCSIReg pAddr; + SCSIReg len; + } sgMap[SIMOS_SCSI_MAXDMA_LEN]; + + +} SimOS_SCSI; + +#endif diff --git a/sys/pci/ti_fw.h b/sys/pci/ti_fw.h new file mode 100644 index 0000000..89f5c9f --- /dev/null +++ b/sys/pci/ti_fw.h @@ -0,0 +1,4593 @@ +/* + * Firmware for Alteon Tigon 1 chip. + * Generated by genfw.c + * + * $FreeBSD$ + */ +static int tigonFwReleaseMajor = 0xc; +static int tigonFwReleaseMinor = 0x4; +static int tigonFwReleaseFix = 0xb; +static u_int32_t tigonFwStartAddr = 0x00004000; +static u_int32_t tigonFwTextAddr = 0x00004000; +static int tigonFwTextLen = 0x11140; +static u_int32_t tigonFwRodataAddr = 0x00015140; +static int tigonFwRodataLen = 0xac0; +static u_int32_t tigonFwDataAddr = 0x00015c20; +static int tigonFwDataLen = 0x170; +static u_int32_t tigonFwSbssAddr = 0x00015d90; +static int tigonFwSbssLen = 0x38; +static u_int32_t tigonFwBssAddr = 0x00015dd0; +static int tigonFwBssLen = 0x2080; +static u_int32_t tigonFwText[] = { +0x10000003, +0x0, 0xd, 0xd, 0x3c1d0001, +0x8fbd5c54, 0x3a0f021, 0x3c100000, 0x26104000, +0xc00100c, 0x0, 0xd, 0x27bdffd8, +0x3c1cc000, 0x3c1b0013, 0x377bd800, 0xd021, +0x3c170013, 0x36f75418, 0x2e02021, 0x340583e8, +0xafbf0024, 0xc002488, 0xafb00020, 0xc0023e8, +0x0, 0x3c040001, 0x248451a4, 0x24050001, +0x2e03021, 0x3821, 0x3c100001, 0x26107e50, +0xafb00010, 0xc002403, 0xafbb0014, 0x3c02000f, +0x3442ffff, 0x2021024, 0x362102b, 0x10400009, +0x24050003, 0x3c040001, 0x248451b0, 0x2003021, +0x3603821, 0x3c020010, 0xafa20010, 0xc002403, +0xafa00014, 0x2021, 0x3405c000, 0x3c010001, +0x370821, 0xa02083b0, 0x3c010001, 0x370821, +0xa02083b2, 0x3c010001, 0x370821, 0xa02083b3, +0x3c010001, 0x370821, 0xac2083b4, 0xa2e004d8, +0x418c0, 0x24840001, 0x771021, 0xac40727c, +0x771021, 0xac407280, 0x2e31021, 0xa445727c, +0x2c820020, 0x1440fff7, 0x418c0, 0x2021, +0x3405c000, 0x418c0, 0x24840001, 0x771021, +0xac40737c, 0x771021, 0xac407380, 0x2e31021, +0xa445737c, 0x2c820080, 0x5440fff7, 0x418c0, +0xaf800054, 0xaf80011c, 0x8f820044, 0x34420040, +0xaf820044, 0x8f820044, 0x34420020, 0xaf820044, +0x8f420218, 0x30420002, 0x10400009, 0x0, +0x8f420220, 0x3c030002, 0x34630004, 0x431025, +0xaee204c4, 0x8f42021c, 0x8001074, 0x34420004, +0x8f420220, 0x3c030002, 0x34630006, 0x431025, +0xaee204c4, 0x8f42021c, 0x34420006, 0xaee204cc, +0x8f420218, 0x30420010, 0x1040000a, 0x0, +0x8f42021c, 0x34420004, 0xaee204c8, 0x8f420220, +0x3c03000a, 0x34630004, 0x431025, 0x800108a, +0xaee204c0, 0x8f420220, 0x3c03000a, 0x34630006, +0x431025, 0xaee204c0, 0x8f42021c, 0x34420006, +0xaee204c8, 0x8f420218, 0x30420200, 0x10400003, +0x24020001, 0x8001091, 0xa2e27248, 0xa2e07248, +0x24020001, 0xaf8200a0, 0xaf8200b0, 0x8f830054, +0x8f820054, 0x8001099, 0x24630064, 0x8f820054, +0x621023, 0x2c420065, 0x1440fffc, 0x0, +0xaf800044, 0x8f420208, 0x8f43020c, 0xaee20010, +0xaee30014, 0x8ee40010, 0x8ee50014, 0x26e20030, +0xaee20028, 0x24020490, 0xaee20018, 0xaf840090, +0xaf850094, 0x8ee20028, 0xaf8200b4, 0x96e2001a, +0xaf82009c, 0x8f8200b0, 0x8ee304cc, 0x431025, +0xaf8200b0, 0x8f8200b0, 0x30420004, 0x1440fffd, +0x0, 0x8ee20450, 0x8ee30454, 0xaee304fc, +0x8ee204fc, 0x2442e000, 0x2c422001, 0x1440000d, +0x26e40030, 0x8ee20450, 0x8ee30454, 0x3c040001, +0x248451bc, 0x3c050001, 0xafa00010, 0xafa00014, +0x8ee704fc, 0x34a5f000, 0xc002403, 0x603021, +0x26e40030, 0xc002488, 0x24050400, 0x27440080, +0xc002488, 0x24050080, 0x26e4777c, 0xc002488, +0x24050400, 0x8f42025c, 0x26e40094, 0xaee20060, +0x8f420260, 0x27450200, 0x24060008, 0xaee20068, +0x24020006, 0xc00249a, 0xaee20064, 0x3c023b9a, +0x3442ca00, 0x2021, 0x24030002, 0xaee30074, +0xaee30070, 0xaee2006c, 0x240203e8, 0xaee20104, +0x24020001, 0xaee30100, 0xaee2010c, 0x3c030001, +0x641821, 0x90635c20, 0x2e41021, 0x24840001, +0xa043009c, 0x2c82000f, 0x1440fff8, 0x0, +0x8f820040, 0x2e41821, 0x24840001, 0x21702, +0x24420030, 0xa062009c, 0x2e41021, 0xa040009c, +0x96e2046a, 0x30420003, 0x14400009, 0x0, +0x96e2047a, 0x30420003, 0x50400131, 0x3c030800, +0x96e2046a, 0x30420003, 0x1040002a, 0x3c020700, +0x96e2047a, 0x30420003, 0x10400026, 0x3c020700, +0x96e3047a, 0x96e2046a, 0x14620022, 0x3c020700, +0x8ee204c0, 0x24030001, 0xa2e34e20, 0x34420e00, +0xaee204c0, 0x8f420218, 0x30420100, 0x10400005, +0x0, 0x3c020001, 0x2442e168, 0x800111d, +0x21100, 0x3c020001, 0x2442d35c, 0x21100, +0x21182, 0x3c030800, 0x431025, 0x3c010001, +0xac221238, 0x3c020001, 0x2442f680, 0x21100, +0x21182, 0x3c030800, 0x431025, 0x3c010001, +0xac221278, 0x8ee20000, 0x34424000, 0x8001238, +0xaee20000, 0x34423000, 0xafa20018, 0x8ee20608, +0x8f430228, 0x24420001, 0x304900ff, 0x512300e2, +0xafa00010, 0x8ee20608, 0x210c0, 0x571021, +0x8fa30018, 0x8fa4001c, 0xac43060c, 0xac440610, +0x8f870120, 0x27623800, 0x24e80020, 0x102102b, +0x50400001, 0x27683000, 0x8f820128, 0x11020004, +0x0, 0x8f820124, 0x15020007, 0x1021, +0x8ee201a4, 0x3021, 0x24420001, 0xaee201a4, +0x80011a0, 0x8ee201a4, 0x8ee40608, 0x420c0, +0x801821, 0x8ee40430, 0x8ee50434, 0xa32821, +0xa3302b, 0x822021, 0x862021, 0xace40000, +0xace50004, 0x8ee30608, 0x24020008, 0xa4e2000e, +0x2402000d, 0xace20018, 0xace9001c, 0x318c0, +0x2463060c, 0x2e31021, 0xace20008, 0x8ee204c4, +0xace20010, 0xaf880120, 0x92e24e20, 0x14400037, +0x24060001, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0x8c830000, 0x24020007, 0x1462001f, +0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, +0x24030040, 0x8c820004, 0x24420001, 0xac820004, +0x8ee24e34, 0x8ee54e30, 0x24420001, 0x10430007, +0x0, 0x8ee24e34, 0x24420001, 0x10a20005, +0x0, 0x800118a, 0x0, 0x14a00005, +0x0, 0x8f820128, 0x24420020, 0xaf820128, +0x8f820128, 0x8c820004, 0x2c420011, 0x50400013, +0xac800000, 0x80011a0, 0x0, 0x8ee24e30, +0x24030040, 0x24420001, 0x50430003, 0x1021, +0x8ee24e30, 0x24420001, 0xaee24e30, 0x8ee24e30, +0x210c0, 0x24425038, 0x2e22021, 0x24020007, +0xac820000, 0x24020001, 0xac820004, 0x54c0000c, +0xaee90608, 0x3c040001, 0x248451c8, 0xafa00010, +0xafa00014, 0x8ee60608, 0x8f470228, 0x3c050009, +0xc002403, 0x34a5f000, 0x8001223, 0x0, +0x8f830120, 0x27623800, 0x24660020, 0xc2102b, +0x50400001, 0x27663000, 0x8f820128, 0x10c20004, +0x0, 0x8f820124, 0x14c20007, 0x0, +0x8ee201a4, 0x3021, 0x24420001, 0xaee201a4, +0x8001207, 0x8ee201a4, 0x8ee20608, 0xac62001c, +0x8ee404a0, 0x8ee504a4, 0x2462001c, 0xac620008, +0x24020008, 0xa462000e, 0x24020011, 0xac620018, +0xac640000, 0xac650004, 0x8ee204c4, 0xac620010, +0xaf860120, 0x92e24e20, 0x14400037, 0x24060001, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x8c830000, 0x24020012, 0x1462001f, 0x0, +0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x24030040, +0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, +0x8ee54e30, 0x24420001, 0x10430007, 0x0, +0x8ee24e34, 0x24420001, 0x10a20005, 0x0, +0x80011f1, 0x0, 0x14a00005, 0x0, +0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, +0x8c820004, 0x2c420011, 0x50400013, 0xac800000, +0x8001207, 0x0, 0x8ee24e30, 0x24030040, +0x24420001, 0x50430003, 0x1021, 0x8ee24e30, +0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x24020012, 0xac820000, +0x24020001, 0xac820004, 0x14c0001b, 0x0, +0x3c040001, 0x248451d0, 0xafa00010, 0xafa00014, +0x8ee60608, 0x8f470228, 0x3c050009, 0xc002403, +0x34a5f001, 0x8ee201b0, 0x24420001, 0xaee201b0, +0x8001223, 0x8ee201b0, 0x3c040001, 0x248451dc, +0xafa00014, 0x8ee60608, 0x8f470228, 0x3c050009, +0xc002403, 0x34a5f005, 0x8ee201ac, 0x24420001, +0xaee201ac, 0x8ee201ac, 0x8ee20160, 0x3c040001, +0x248451e8, 0x3405f001, 0x24420001, 0xaee20160, +0x8ee20160, 0x3021, 0x3821, 0xafa00010, +0xc002403, 0xafa00014, 0x8001238, 0x0, +0x3c020001, 0x2442f5a8, 0x21100, 0x21182, +0x431025, 0x3c010001, 0xac221278, 0x96e2045a, +0x30420003, 0x10400025, 0x3c050fff, 0x8ee204c8, +0x34a5ffff, 0x34420a00, 0xaee204c8, 0x8ee304c8, +0x3c040001, 0x248451f4, 0x24020001, 0xa2e204ec, +0xa2e204ed, 0x3c020002, 0x621825, 0x3c020001, +0x2442a390, 0x451024, 0x21082, 0xaee304c8, +0x3c030800, 0x431025, 0x3c010001, 0xac221220, +0x3c020001, 0x2442add4, 0x451024, 0x21082, +0x431025, 0x3c010001, 0xac221280, 0x96e6045a, +0x3821, 0x24050011, 0xafa00010, 0xc002403, +0xafa00014, 0x8001268, 0x0, 0x3c020001, +0x2442a9d4, 0x21100, 0x21182, 0x3c030800, +0x431025, 0x3c010001, 0xac221280, 0x96e2046a, +0x30420010, 0x14400009, 0x0, 0x96e2047a, +0x30420010, 0x10400112, 0x0, 0x96e2046a, +0x30420010, 0x10400005, 0x3c020700, 0x96e2047a, +0x30420010, 0x14400102, 0x3c020700, 0x34423000, +0xafa20018, 0x8ee20608, 0x8f430228, 0x24420001, +0x304900ff, 0x512300e2, 0xafa00010, 0x8ee20608, +0x210c0, 0x571021, 0x8fa30018, 0x8fa4001c, +0xac43060c, 0xac440610, 0x8f870120, 0x27623800, +0x24e80020, 0x102102b, 0x50400001, 0x27683000, +0x8f820128, 0x11020004, 0x0, 0x8f820124, +0x15020007, 0x1021, 0x8ee201a4, 0x3021, +0x24420001, 0xaee201a4, 0x80012ea, 0x8ee201a4, +0x8ee40608, 0x420c0, 0x801821, 0x8ee40430, +0x8ee50434, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xace40000, 0xace50004, 0x8ee30608, +0x24020008, 0xa4e2000e, 0x2402000d, 0xace20018, +0xace9001c, 0x318c0, 0x2463060c, 0x2e31021, +0xace20008, 0x8ee204c4, 0xace20010, 0xaf880120, +0x92e24e20, 0x14400037, 0x24060001, 0x8ee24e30, +0x210c0, 0x24425038, 0x2e22021, 0x8c830000, +0x24020007, 0x1462001f, 0x0, 0x8ee34e30, +0x8ee24e34, 0x1062001b, 0x24030040, 0x8c820004, +0x24420001, 0xac820004, 0x8ee24e34, 0x8ee54e30, +0x24420001, 0x10430007, 0x0, 0x8ee24e34, +0x24420001, 0x10a20005, 0x0, 0x80012d4, +0x0, 0x14a00005, 0x0, 0x8f820128, +0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, +0x2c420011, 0x50400013, 0xac800000, 0x80012ea, +0x0, 0x8ee24e30, 0x24030040, 0x24420001, +0x50430003, 0x1021, 0x8ee24e30, 0x24420001, +0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0x24020007, 0xac820000, 0x24020001, +0xac820004, 0x54c0000c, 0xaee90608, 0x3c040001, +0x248451c8, 0xafa00010, 0xafa00014, 0x8ee60608, +0x8f470228, 0x3c050009, 0xc002403, 0x34a5f000, +0x800136d, 0x0, 0x8f830120, 0x27623800, +0x24660020, 0xc2102b, 0x50400001, 0x27663000, +0x8f820128, 0x10c20004, 0x0, 0x8f820124, +0x14c20007, 0x0, 0x8ee201a4, 0x3021, +0x24420001, 0xaee201a4, 0x8001351, 0x8ee201a4, +0x8ee20608, 0xac62001c, 0x8ee404a0, 0x8ee504a4, +0x2462001c, 0xac620008, 0x24020008, 0xa462000e, +0x24020011, 0xac620018, 0xac640000, 0xac650004, +0x8ee204c4, 0xac620010, 0xaf860120, 0x92e24e20, +0x14400037, 0x24060001, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x8c830000, 0x24020012, +0x1462001f, 0x0, 0x8ee34e30, 0x8ee24e34, +0x1062001b, 0x24030040, 0x8c820004, 0x24420001, +0xac820004, 0x8ee24e34, 0x8ee54e30, 0x24420001, +0x10430007, 0x0, 0x8ee24e34, 0x24420001, +0x10a20005, 0x0, 0x800133b, 0x0, +0x14a00005, 0x0, 0x8f820128, 0x24420020, +0xaf820128, 0x8f820128, 0x8c820004, 0x2c420011, +0x50400013, 0xac800000, 0x8001351, 0x0, +0x8ee24e30, 0x24030040, 0x24420001, 0x50430003, +0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x24020012, 0xac820000, 0x24020001, 0xac820004, +0x14c0001b, 0x0, 0x3c040001, 0x248451d0, +0xafa00010, 0xafa00014, 0x8ee60608, 0x8f470228, +0x3c050009, 0xc002403, 0x34a5f001, 0x8ee201b0, +0x24420001, 0xaee201b0, 0x800136d, 0x8ee201b0, +0x3c040001, 0x248451dc, 0xafa00014, 0x8ee60608, +0x8f470228, 0x3c050009, 0xc002403, 0x34a5f005, +0x8ee201ac, 0x24420001, 0xaee201ac, 0x8ee201ac, +0x8ee20160, 0x3c040001, 0x248451e8, 0x3405f002, +0x24420001, 0xaee20160, 0x8ee20160, 0x3021, +0x3821, 0xafa00010, 0xc002403, 0xafa00014, +0x96e6047a, 0x96e7046a, 0x3c040001, 0x24845200, +0x24050012, 0xafa00010, 0xc002403, 0xafa00014, +0xc004500, 0x0, 0xc002318, 0x0, +0x3c060001, 0x34c63800, 0xaee00608, 0xaf400228, +0xaf40022c, 0x96e30458, 0x8ee40000, 0x3c0512d8, +0x34a5c358, 0x27623800, 0xaee27258, 0x27623800, +0xaee27260, 0x27623800, 0xaee27264, 0x3661021, +0xaee27270, 0x2402ffff, 0xaee004d4, 0xaee004e0, +0xaee004e4, 0xaee004f0, 0xa2e004f4, 0xaee00e0c, +0xaee00e18, 0xaee00e10, 0xaee00e14, 0xaee00e1c, +0xaee0724c, 0xaee05244, 0xaee05240, 0xaee0523c, +0xaee07250, 0xaee07254, 0xaee0725c, 0xaee07268, +0xaee004d0, 0x2463ffff, 0x852025, 0xaee304f8, +0xaee40000, 0xaf800060, 0xaf820064, 0x3c020100, +0xafa20018, 0x8ee20608, 0x8f430228, 0x24420001, +0x304900ff, 0x512300e2, 0xafa00010, 0x8ee20608, +0x210c0, 0x571021, 0x8fa30018, 0x8fa4001c, +0xac43060c, 0xac440610, 0x8f870120, 0x27623800, +0x24e80020, 0x102102b, 0x50400001, 0x27683000, +0x8f820128, 0x11020004, 0x0, 0x8f820124, +0x15020007, 0x1021, 0x8ee201a4, 0x3021, +0x24420001, 0xaee201a4, 0x8001422, 0x8ee201a4, +0x8ee40608, 0x420c0, 0x801821, 0x8ee40430, +0x8ee50434, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xace40000, 0xace50004, 0x8ee30608, +0x24020008, 0xa4e2000e, 0x2402000d, 0xace20018, +0xace9001c, 0x318c0, 0x2463060c, 0x2e31021, +0xace20008, 0x8ee204c4, 0xace20010, 0xaf880120, +0x92e24e20, 0x14400037, 0x24060001, 0x8ee24e30, +0x210c0, 0x24425038, 0x2e22021, 0x8c830000, +0x24020007, 0x1462001f, 0x0, 0x8ee34e30, +0x8ee24e34, 0x1062001b, 0x24030040, 0x8c820004, +0x24420001, 0xac820004, 0x8ee24e34, 0x8ee54e30, +0x24420001, 0x10430007, 0x0, 0x8ee24e34, +0x24420001, 0x10a20005, 0x0, 0x800140c, +0x0, 0x14a00005, 0x0, 0x8f820128, +0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, +0x2c420011, 0x50400013, 0xac800000, 0x8001422, +0x0, 0x8ee24e30, 0x24030040, 0x24420001, +0x50430003, 0x1021, 0x8ee24e30, 0x24420001, +0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0x24020007, 0xac820000, 0x24020001, +0xac820004, 0x54c0000c, 0xaee90608, 0x3c040001, +0x248451c8, 0xafa00010, 0xafa00014, 0x8ee60608, +0x8f470228, 0x3c050009, 0xc002403, 0x34a5f000, +0x80014a5, 0x0, 0x8f830120, 0x27623800, +0x24660020, 0xc2102b, 0x50400001, 0x27663000, +0x8f820128, 0x10c20004, 0x0, 0x8f820124, +0x14c20007, 0x0, 0x8ee201a4, 0x3021, +0x24420001, 0xaee201a4, 0x8001489, 0x8ee201a4, +0x8ee20608, 0xac62001c, 0x8ee404a0, 0x8ee504a4, +0x2462001c, 0xac620008, 0x24020008, 0xa462000e, +0x24020011, 0xac620018, 0xac640000, 0xac650004, +0x8ee204c4, 0xac620010, 0xaf860120, 0x92e24e20, +0x14400037, 0x24060001, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x8c830000, 0x24020012, +0x1462001f, 0x0, 0x8ee34e30, 0x8ee24e34, +0x1062001b, 0x24030040, 0x8c820004, 0x24420001, +0xac820004, 0x8ee24e34, 0x8ee54e30, 0x24420001, +0x10430007, 0x0, 0x8ee24e34, 0x24420001, +0x10a20005, 0x0, 0x8001473, 0x0, +0x14a00005, 0x0, 0x8f820128, 0x24420020, +0xaf820128, 0x8f820128, 0x8c820004, 0x2c420011, +0x50400013, 0xac800000, 0x8001489, 0x0, +0x8ee24e30, 0x24030040, 0x24420001, 0x50430003, +0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x24020012, 0xac820000, 0x24020001, 0xac820004, +0x14c0001b, 0x0, 0x3c040001, 0x248451d0, +0xafa00010, 0xafa00014, 0x8ee60608, 0x8f470228, +0x3c050009, 0xc002403, 0x34a5f001, 0x8ee201b0, +0x24420001, 0xaee201b0, 0x80014a5, 0x8ee201b0, +0x3c040001, 0x248451dc, 0xafa00014, 0x8ee60608, +0x8f470228, 0x3c050009, 0xc002403, 0x34a5f005, +0x8ee201ac, 0x24420001, 0xaee201ac, 0x8ee201ac, +0x8ee20154, 0x24420001, 0xaee20154, 0xc0014dc, +0x8ee20154, 0x8f8200a0, 0x30420004, 0x1440fffd, +0x0, 0x8f820040, 0x30420001, 0x14400008, +0x0, 0x8f430104, 0x24020001, 0x10620004, +0x0, 0x8f420264, 0x10400006, 0x0, +0x8ee2017c, 0x24420001, 0xaee2017c, 0x80014c5, +0x8ee2017c, 0x8f820044, 0x34420004, 0xaf820044, +0x8ee20178, 0x24420001, 0xaee20178, 0x8ee20178, +0x8f8200d8, 0x8f8300d4, 0x431023, 0xaee2726c, +0x8ee2726c, 0x1c400003, 0x3c030001, 0x431021, +0xaee2726c, 0xc004064, 0x0, 0xc004440, +0xaf800228, 0x8fbf0024, 0x8fb00020, 0x3e00008, +0x27bd0028, 0x3e00008, 0x0, 0x3e00008, +0x0, 0x0, 0x0, 0x2402002c, +0xaf820050, 0xaee07274, 0x8f420238, 0xaee27278, +0x8f820054, 0x24420067, 0xaf820058, 0xaee07b88, +0xaee07b8c, 0xaee07b84, 0x3c010001, 0x370821, +0xac2083bc, 0x3c010001, 0x370821, 0x3e00008, +0xa02083b9, 0x27bdffd8, 0xafbf0024, 0xafb00020, +0x8f820054, 0x3c030001, 0x8c635cd8, 0x24420067, +0x1060000d, 0xaf820058, 0x3c020001, 0x571021, +0x904283b8, 0x10400005, 0x3c030200, 0x3c010001, +0x370821, 0x8001503, 0xa02083b8, 0x8ee20000, +0x431025, 0xaee20000, 0x8f420218, 0x30420100, +0x104000c6, 0x0, 0x8f8200b0, 0x30420004, +0x104000c2, 0x0, 0x3c030001, 0x771821, +0x8c6383d0, 0x8f820104, 0x146200b4, 0x0, +0x3c030001, 0x771821, 0x8c6383d4, 0x8f8200b4, +0x146200ae, 0x0, 0x8f8200b0, 0x3c030080, +0x431024, 0x1040000d, 0x0, 0x8f82011c, +0x34420002, 0xaf82011c, 0x8f8200b0, 0x2403fffb, +0x431024, 0xaf8200b0, 0x8f82011c, 0x2403fffd, +0x431024, 0x80015cc, 0xaf82011c, 0x3c030001, +0x771821, 0x8c6383d0, 0x8f820104, 0x14620082, +0x0, 0x3c030001, 0x771821, 0x8c6383d4, +0x8f8200b4, 0x1462007c, 0x0, 0x3c070001, +0xf73821, 0x8ce783d0, 0x8f8200b0, 0x3c040001, +0x24845270, 0xafa00014, 0xafa20010, 0x8f8600b0, +0x3c050005, 0xc002403, 0x34a50900, 0x8f82011c, +0x34420002, 0xaf82011c, 0x8f830104, 0x8f8200b0, +0x34420001, 0xaf8200b0, 0xaf830104, 0x8f830120, +0x27623800, 0x24660020, 0xc2102b, 0x50400001, +0x27663000, 0x8f820128, 0x10c20004, 0x0, +0x8f820124, 0x14c20006, 0x0, 0x8ee201a4, +0x24420001, 0xaee201a4, 0x80015a0, 0x8ee201a4, +0x8f440208, 0x8f45020c, 0x26e20030, 0xac620008, +0x24020400, 0xa462000e, 0x2402000f, 0xac620018, +0xac60001c, 0xac640000, 0xac650004, 0x8ee204c4, +0xac620010, 0xaf860120, 0x92e24e20, 0x14400037, +0x0, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0x8c830000, 0x24020007, 0x1462001f, +0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, +0x24030040, 0x8c820004, 0x24420001, 0xac820004, +0x8ee24e34, 0x8ee54e30, 0x24420001, 0x10430007, +0x0, 0x8ee24e34, 0x24420001, 0x10a20005, +0x0, 0x800158a, 0x0, 0x14a00005, +0x0, 0x8f820128, 0x24420020, 0xaf820128, +0x8f820128, 0x8c820004, 0x2c420011, 0x50400013, +0xac800000, 0x80015a0, 0x0, 0x8ee24e30, +0x24030040, 0x24420001, 0x50430003, 0x1021, +0x8ee24e30, 0x24420001, 0xaee24e30, 0x8ee24e30, +0x210c0, 0x24425038, 0x2e22021, 0x24020007, +0xac820000, 0x24020001, 0xac820004, 0x8f82011c, +0x2403fffd, 0x431024, 0xaf82011c, 0x8ee201e4, +0x3c070001, 0xf73821, 0x8ce783d0, 0x24420001, +0xaee201e4, 0x8ee201e4, 0x3c040001, 0x2484527c, +0x80015bd, 0xafa00010, 0x8f820104, 0x3c010001, +0x370821, 0xac2283d0, 0x8f8200b4, 0x3c070001, +0xf73821, 0x8ce783d0, 0x3c040001, 0x24845284, +0x3c010001, 0x370821, 0xac2283d4, 0xafa00010, +0xafa00014, 0x8f8600b0, 0x3c050005, 0xc002403, +0x34a50900, 0x80015cc, 0x0, 0x8f820104, +0x3c010001, 0x370821, 0xac2283d0, 0x8f8200b4, +0x3c010001, 0x370821, 0xac2283d4, 0x8ee27274, +0x92e304f4, 0x24420067, 0x14600006, 0xaee27274, +0x8ee27274, 0x8f430234, 0x43102b, 0x1440007b, +0x0, 0x8ee304e4, 0x8ee204f8, 0x14620004, +0x0, 0x92e204f4, 0x50400074, 0xa2e004f4, +0x8f830120, 0x27623800, 0x24660020, 0xc2102b, +0x50400001, 0x27663000, 0x8f820128, 0x10c20004, +0x0, 0x8f820124, 0x14c20007, 0x0, +0x8ee201a4, 0x8021, 0x24420001, 0xaee201a4, +0x8001637, 0x8ee201a4, 0x8ee204e4, 0xac62001c, +0x8ee404b0, 0x8ee504b4, 0x2462001c, 0xac620008, +0x24020008, 0xa462000e, 0x24020011, 0xac620018, +0xac640000, 0xac650004, 0x8ee204c4, 0xac620010, +0xaf860120, 0x92e24e20, 0x14400037, 0x24100001, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x8c830000, 0x24020012, 0x1462001f, 0x0, +0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x24030040, +0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, +0x8ee54e30, 0x24420001, 0x10430007, 0x0, +0x8ee24e34, 0x24420001, 0x10a20005, 0x0, +0x8001621, 0x0, 0x14a00005, 0x0, +0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, +0x8c820004, 0x2c420011, 0x50400013, 0xac800000, +0x8001637, 0x0, 0x8ee24e30, 0x24030040, +0x24420001, 0x50430003, 0x1021, 0x8ee24e30, +0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x24020012, 0xac820000, +0x24020001, 0xac820004, 0x5600000b, 0x24100001, +0x8ee204e4, 0x3c040001, 0x2484528c, 0xafa00014, +0xafa20010, 0x8ee60608, 0x8f470228, 0x3c050009, +0xc002403, 0x34a5f006, 0x16000003, 0x24020001, +0x8001650, 0xa2e204f4, 0x8ee20170, 0x24420001, +0xaee20170, 0x8ee20170, 0x8ee204e4, 0xa2e004f4, +0xaee004f0, 0xaee07274, 0xaee204f8, 0x8ee20e1c, +0x1040006d, 0x0, 0x8f830120, 0x27623800, +0x24660020, 0xc2102b, 0x50400001, 0x27663000, +0x8f820128, 0x10c20004, 0x0, 0x8f820124, +0x14c20007, 0x0, 0x8ee201a4, 0x8021, +0x24420001, 0xaee201a4, 0x80016ad, 0x8ee201a4, +0x8ee2724c, 0xac62001c, 0x8ee404a8, 0x8ee504ac, +0x2462001c, 0xac620008, 0x24020008, 0xa462000e, +0x24020011, 0xac620018, 0xac640000, 0xac650004, +0x8ee204c4, 0xac620010, 0xaf860120, 0x92e24e20, +0x14400037, 0x24100001, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x8c830000, 0x24020012, +0x1462001f, 0x0, 0x8ee34e30, 0x8ee24e34, +0x1062001b, 0x24030040, 0x8c820004, 0x24420001, +0xac820004, 0x8ee24e34, 0x8ee54e30, 0x24420001, +0x10430007, 0x0, 0x8ee24e34, 0x24420001, +0x10a20005, 0x0, 0x8001697, 0x0, +0x14a00005, 0x0, 0x8f820128, 0x24420020, +0xaf820128, 0x8f820128, 0x8c820004, 0x2c420011, +0x50400013, 0xac800000, 0x80016ad, 0x0, +0x8ee24e30, 0x24030040, 0x24420001, 0x50430003, +0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x24020012, 0xac820000, 0x24020001, 0xac820004, +0x5600000b, 0x24100001, 0x8ee2724c, 0x3c040001, +0x24845298, 0xafa00014, 0xafa20010, 0x8ee6724c, +0x8f470280, 0x3c050009, 0xc002403, 0x34a5f008, +0x56000001, 0xaee00e1c, 0x8ee20174, 0x24420001, +0xaee20174, 0x8ee20174, 0x8ee24e24, 0x10400019, +0x0, 0xaee04e24, 0x8f820040, 0x30420001, +0x14400008, 0x0, 0x8f430104, 0x24020001, +0x10620004, 0x0, 0x8f420264, 0x10400006, +0x0, 0x8ee2017c, 0x24420001, 0xaee2017c, +0x80016da, 0x8ee2017c, 0x8f820044, 0x34420004, +0xaf820044, 0x8ee20178, 0x24420001, 0xaee20178, +0x8ee20178, 0x8ee27278, 0x2442ff99, 0xaee27278, +0x8ee27278, 0x1c4002ad, 0x0, 0x8f420238, +0x104002aa, 0x0, 0x3c020001, 0x571021, +0x904283e0, 0x144002a5, 0x0, 0x8f420080, +0xaee2004c, 0x8f4200c0, 0xaee20048, 0x8f420084, +0xaee20038, 0x8f420084, 0xaee20244, 0x8f420088, +0xaee20248, 0x8f42008c, 0xaee2024c, 0x8f420090, +0xaee20250, 0x8f420094, 0xaee20254, 0x8f420098, +0xaee20258, 0x8f42009c, 0xaee2025c, 0x8f4200a0, +0xaee20260, 0x8f4200a4, 0xaee20264, 0x8f4200a8, +0xaee20268, 0x8f4200ac, 0xaee2026c, 0x8f4200b0, +0xaee20270, 0x8f4200b4, 0xaee20274, 0x8f4200b8, +0xaee20278, 0x8f4200bc, 0x24040001, 0xaee2027c, +0xaee0003c, 0x41080, 0x571021, 0x8ee3003c, +0x8c420244, 0x24840001, 0x621821, 0x2c82000f, +0xaee3003c, 0x1440fff8, 0x41080, 0x8f4200cc, +0xaee20050, 0x8f4200d0, 0xaee20054, 0x8f830120, +0x27623800, 0x24660020, 0xc2102b, 0x50400001, +0x27663000, 0x8f820128, 0x10c20004, 0x0, +0x8f820124, 0x14c20007, 0x0, 0x8ee201a4, +0x8021, 0x24420001, 0xaee201a4, 0x8001775, +0x8ee201a4, 0x8f440208, 0x8f45020c, 0x26e20030, +0xac620008, 0x24020400, 0xa462000e, 0x2402000f, +0xac620018, 0xac60001c, 0xac640000, 0xac650004, +0x8ee204c4, 0xac620010, 0xaf860120, 0x92e24e20, +0x14400037, 0x24100001, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x8c830000, 0x24020007, +0x1462001f, 0x0, 0x8ee34e30, 0x8ee24e34, +0x1062001b, 0x24030040, 0x8c820004, 0x24420001, +0xac820004, 0x8ee24e34, 0x8ee54e30, 0x24420001, +0x10430007, 0x0, 0x8ee24e34, 0x24420001, +0x10a20005, 0x0, 0x800175f, 0x0, +0x14a00005, 0x0, 0x8f820128, 0x24420020, +0xaf820128, 0x8f820128, 0x8c820004, 0x2c420011, +0x50400013, 0xac800000, 0x8001775, 0x0, +0x8ee24e30, 0x24030040, 0x24420001, 0x50430003, +0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x24020007, 0xac820000, 0x24020001, 0xac820004, +0x12000212, 0x3c020400, 0xafa20018, 0x3c020001, +0x571021, 0x904283b0, 0x1040010b, 0x0, +0x8ee20608, 0x8f430228, 0x24420001, 0x304a00ff, +0x514300fd, 0xafa00010, 0x8ee20608, 0x210c0, +0x571021, 0x8fa30018, 0x8fa4001c, 0xac43060c, +0xac440610, 0x8f830054, 0x8f820054, 0x24690032, +0x1221023, 0x2c420033, 0x1040006a, 0x5821, +0x24180008, 0x240f000d, 0x240d0007, 0x240c0040, +0x240e0001, 0x8f870120, 0x27623800, 0x24e80020, +0x102102b, 0x50400001, 0x27683000, 0x8f820128, +0x11020004, 0x0, 0x8f820124, 0x15020007, +0x1021, 0x8ee201a4, 0x8021, 0x24420001, +0xaee201a4, 0x80017f3, 0x8ee201a4, 0x8ee40608, +0x420c0, 0x801821, 0x8ee40430, 0x8ee50434, +0xa32821, 0xa3302b, 0x822021, 0x862021, +0xace40000, 0xace50004, 0x8ee20608, 0xa4f8000e, +0xacef0018, 0xacea001c, 0x210c0, 0x2442060c, +0x2e21021, 0xace20008, 0x8ee204c4, 0xace20010, +0xaf880120, 0x92e24e20, 0x14400033, 0x24100001, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x8c820000, 0x144d001f, 0x0, 0x8ee34e30, +0x8ee24e34, 0x1062001b, 0x0, 0x8c820004, +0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, +0x24420001, 0x104c0007, 0x0, 0x8ee24e34, +0x24420001, 0x10620005, 0x0, 0x80017e0, +0x0, 0x14600005, 0x0, 0x8f820128, +0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, +0x2c420011, 0x50400010, 0xac800000, 0x80017f3, +0x0, 0x8ee24e30, 0x24420001, 0x504c0003, +0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0xac8d0000, 0xac8e0004, 0x56000006, 0x240b0001, +0x8f820054, 0x1221023, 0x2c420033, 0x1440ff9d, +0x0, 0x316300ff, 0x24020001, 0x14620077, +0x3c050009, 0xaeea0608, 0x8f830054, 0x8f820054, +0x24690032, 0x1221023, 0x2c420033, 0x10400061, +0x5821, 0x240d0008, 0x240c0011, 0x24080012, +0x24070040, 0x240a0001, 0x8f830120, 0x27623800, +0x24660020, 0xc2102b, 0x50400001, 0x27663000, +0x8f820128, 0x10c20004, 0x0, 0x8f820124, +0x14c20007, 0x0, 0x8ee201a4, 0x8021, +0x24420001, 0xaee201a4, 0x800185f, 0x8ee201a4, +0x8ee20608, 0xac62001c, 0x8ee404a0, 0x8ee504a4, +0x2462001c, 0xac620008, 0xa46d000e, 0xac6c0018, +0xac640000, 0xac650004, 0x8ee204c4, 0xac620010, +0xaf860120, 0x92e24e20, 0x14400033, 0x24100001, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x8c820000, 0x1448001f, 0x0, 0x8ee34e30, +0x8ee24e34, 0x1062001b, 0x0, 0x8c820004, +0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, +0x24420001, 0x10470007, 0x0, 0x8ee24e34, +0x24420001, 0x10620005, 0x0, 0x800184c, +0x0, 0x14600005, 0x0, 0x8f820128, +0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, +0x2c420011, 0x50400010, 0xac800000, 0x800185f, +0x0, 0x8ee24e30, 0x24420001, 0x50470003, +0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0xac880000, 0xac8a0004, 0x56000006, 0x240b0001, +0x8f820054, 0x1221023, 0x2c420033, 0x1440ffa6, +0x0, 0x316300ff, 0x24020001, 0x14620003, +0x3c050009, 0x800197c, 0x24100001, 0x3c040001, +0x248452a4, 0xafa00010, 0xafa00014, 0x8f860120, +0x8f870124, 0x800187b, 0x34a5f011, 0x3c040001, +0x248452b0, 0xafa00010, 0xafa00014, 0x8f860120, +0x8f870124, 0x34a5f010, 0xc002403, 0x8021, +0x800197c, 0x0, 0x3c040001, 0x248452bc, +0xafa00014, 0x8ee60608, 0x8f470228, 0x3c050009, +0x8001975, 0x34a5f00f, 0x8ee20608, 0x8f430228, +0x24420001, 0x304900ff, 0x512300e2, 0xafa00010, +0x8ee20608, 0x210c0, 0x571021, 0x8fa30018, +0x8fa4001c, 0xac43060c, 0xac440610, 0x8f870120, +0x27623800, 0x24e80020, 0x102102b, 0x50400001, +0x27683000, 0x8f820128, 0x11020004, 0x0, +0x8f820124, 0x15020007, 0x1021, 0x8ee201a4, +0x8021, 0x24420001, 0xaee201a4, 0x80018f7, +0x8ee201a4, 0x8ee40608, 0x420c0, 0x801821, +0x8ee40430, 0x8ee50434, 0xa32821, 0xa3302b, +0x822021, 0x862021, 0xace40000, 0xace50004, +0x8ee30608, 0x24020008, 0xa4e2000e, 0x2402000d, +0xace20018, 0xace9001c, 0x318c0, 0x2463060c, +0x2e31021, 0xace20008, 0x8ee204c4, 0xace20010, +0xaf880120, 0x92e24e20, 0x14400037, 0x24100001, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x8c830000, 0x24020007, 0x1462001f, 0x0, +0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x24030040, +0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, +0x8ee54e30, 0x24420001, 0x10430007, 0x0, +0x8ee24e34, 0x24420001, 0x10a20005, 0x0, +0x80018e1, 0x0, 0x14a00005, 0x0, +0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, +0x8c820004, 0x2c420011, 0x50400013, 0xac800000, +0x80018f7, 0x0, 0x8ee24e30, 0x24030040, +0x24420001, 0x50430003, 0x1021, 0x8ee24e30, +0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x24020007, 0xac820000, +0x24020001, 0xac820004, 0x5600000c, 0xaee90608, +0x3c040001, 0x248452c8, 0xafa00010, 0xafa00014, +0x8ee60608, 0x8f470228, 0x3c050009, 0xc002403, +0x34a5f000, 0x800197c, 0x0, 0x8f830120, +0x27623800, 0x24660020, 0xc2102b, 0x50400001, +0x27663000, 0x8f820128, 0x10c20004, 0x0, +0x8f820124, 0x14c20007, 0x0, 0x8ee201a4, +0x8021, 0x24420001, 0xaee201a4, 0x800195e, +0x8ee201a4, 0x8ee20608, 0xac62001c, 0x8ee404a0, +0x8ee504a4, 0x2462001c, 0xac620008, 0x24020008, +0xa462000e, 0x24020011, 0xac620018, 0xac640000, +0xac650004, 0x8ee204c4, 0xac620010, 0xaf860120, +0x92e24e20, 0x14400037, 0x24100001, 0x8ee24e30, +0x210c0, 0x24425038, 0x2e22021, 0x8c830000, +0x24020012, 0x1462001f, 0x0, 0x8ee34e30, +0x8ee24e34, 0x1062001b, 0x24030040, 0x8c820004, +0x24420001, 0xac820004, 0x8ee24e34, 0x8ee54e30, +0x24420001, 0x10430007, 0x0, 0x8ee24e34, +0x24420001, 0x10a20005, 0x0, 0x8001948, +0x0, 0x14a00005, 0x0, 0x8f820128, +0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, +0x2c420011, 0x50400013, 0xac800000, 0x800195e, +0x0, 0x8ee24e30, 0x24030040, 0x24420001, +0x50430003, 0x1021, 0x8ee24e30, 0x24420001, +0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0x24020012, 0xac820000, 0x24020001, +0xac820004, 0x5600001d, 0x24100001, 0x3c040001, +0x248452d0, 0xafa00010, 0xafa00014, 0x8ee60608, +0x8f470228, 0x3c050009, 0xc002403, 0x34a5f001, +0x8ee201b0, 0x24420001, 0xaee201b0, 0x800197c, +0x8ee201b0, 0x3c040001, 0x248452dc, 0xafa00014, +0x8ee60608, 0x8f470228, 0x3c050009, 0x34a5f005, +0xc002403, 0x0, 0x8ee201ac, 0x8021, +0x24420001, 0xaee201ac, 0x8ee201ac, 0x1200000c, +0x24020001, 0x3c010001, 0x370821, 0xa02083b0, +0x8f420238, 0x8ee30158, 0x24630001, 0xaee30158, +0x8ee30158, 0x800198c, 0xaee27278, 0x24020001, +0x3c010001, 0x370821, 0xa02283b0, 0x3c020001, +0x8c425cd8, 0x10400187, 0x0, 0x8ee27b84, +0x24430001, 0x284200c9, 0x144001a4, 0xaee37b84, +0x8ee204d4, 0x30420002, 0x14400119, 0xaee07b84, +0x8ee204d4, 0x3c030600, 0x34631000, 0x34420002, +0xaee204d4, 0xafa30018, 0x8ee20608, 0x8f430228, +0x24420001, 0x304a00ff, 0x514300fd, 0xafa00010, +0x8ee20608, 0x210c0, 0x571021, 0x8fa30018, +0x8fa4001c, 0xac43060c, 0xac440610, 0x8f830054, +0x8f820054, 0x24690032, 0x1221023, 0x2c420033, +0x1040006a, 0x5821, 0x24180008, 0x240f000d, +0x240d0007, 0x240c0040, 0x240e0001, 0x8f870120, +0x27623800, 0x24e80020, 0x102102b, 0x50400001, +0x27683000, 0x8f820128, 0x11020004, 0x0, +0x8f820124, 0x15020007, 0x1021, 0x8ee201a4, +0x8021, 0x24420001, 0xaee201a4, 0x8001a15, +0x8ee201a4, 0x8ee40608, 0x420c0, 0x801821, +0x8ee40430, 0x8ee50434, 0xa32821, 0xa3302b, +0x822021, 0x862021, 0xace40000, 0xace50004, +0x8ee20608, 0xa4f8000e, 0xacef0018, 0xacea001c, +0x210c0, 0x2442060c, 0x2e21021, 0xace20008, +0x8ee204c4, 0xace20010, 0xaf880120, 0x92e24e20, +0x14400033, 0x24100001, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x8c820000, 0x144d001f, +0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, +0x0, 0x8c820004, 0x24420001, 0xac820004, +0x8ee24e34, 0x8ee34e30, 0x24420001, 0x104c0007, +0x0, 0x8ee24e34, 0x24420001, 0x10620005, +0x0, 0x8001a02, 0x0, 0x14600005, +0x0, 0x8f820128, 0x24420020, 0xaf820128, +0x8f820128, 0x8c820004, 0x2c420011, 0x50400010, +0xac800000, 0x8001a15, 0x0, 0x8ee24e30, +0x24420001, 0x504c0003, 0x1021, 0x8ee24e30, +0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0xac8d0000, 0xac8e0004, +0x56000006, 0x240b0001, 0x8f820054, 0x1221023, +0x2c420033, 0x1440ff9d, 0x0, 0x316300ff, +0x24020001, 0x54620078, 0xafa00010, 0xaeea0608, +0x8f830054, 0x8f820054, 0x24690032, 0x1221023, +0x2c420033, 0x10400061, 0x5821, 0x240d0008, +0x240c0011, 0x24080012, 0x24070040, 0x240a0001, +0x8f830120, 0x27623800, 0x24660020, 0xc2102b, +0x50400001, 0x27663000, 0x8f820128, 0x10c20004, +0x0, 0x8f820124, 0x14c20007, 0x0, +0x8ee201a4, 0x8021, 0x24420001, 0xaee201a4, +0x8001a81, 0x8ee201a4, 0x8ee20608, 0xac62001c, +0x8ee404a0, 0x8ee504a4, 0x2462001c, 0xac620008, +0xa46d000e, 0xac6c0018, 0xac640000, 0xac650004, +0x8ee204c4, 0xac620010, 0xaf860120, 0x92e24e20, +0x14400033, 0x24100001, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x8c820000, 0x1448001f, +0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, +0x0, 0x8c820004, 0x24420001, 0xac820004, +0x8ee24e34, 0x8ee34e30, 0x24420001, 0x10470007, +0x0, 0x8ee24e34, 0x24420001, 0x10620005, +0x0, 0x8001a6e, 0x0, 0x14600005, +0x0, 0x8f820128, 0x24420020, 0xaf820128, +0x8f820128, 0x8c820004, 0x2c420011, 0x50400010, +0xac800000, 0x8001a81, 0x0, 0x8ee24e30, +0x24420001, 0x50470003, 0x1021, 0x8ee24e30, +0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0xac880000, 0xac8a0004, +0x56000006, 0x240b0001, 0x8f820054, 0x1221023, +0x2c420033, 0x1440ffa6, 0x0, 0x316300ff, +0x24020001, 0x10620022, 0x0, 0x3c040001, +0x248452a4, 0xafa00010, 0xafa00014, 0x8f860120, +0x8f870124, 0x3c050009, 0xc002403, 0x34a5f011, +0x8001aad, 0x0, 0x3c040001, 0x248452b0, +0xafa00014, 0x8f860120, 0x8f870124, 0x3c050009, +0xc002403, 0x34a5f010, 0x8001aad, 0x0, +0x3c040001, 0x248452bc, 0xafa00014, 0x8ee60608, +0x8f470228, 0x3c050009, 0xc002403, 0x34a5f00f, +0x8ee201ac, 0x24420001, 0xaee201ac, 0x8ee201ac, +0x8ee2015c, 0x24420001, 0xaee2015c, 0x8ee2015c, +0x8ee204d4, 0x30420001, 0x10400055, 0x0, +0x8f420218, 0x30420080, 0x10400029, 0x0, +0x8f820044, 0x34420040, 0xaf820044, 0x8ee27b7c, +0x402821, 0x8ee200c0, 0x8ee300c4, 0x24060000, +0x2407ffff, 0x2021, 0x461024, 0x1444000d, +0x671824, 0x1465000b, 0x0, 0x8ee27b80, +0x402821, 0x8ee200e0, 0x8ee300e4, 0x2021, +0x461024, 0x14440003, 0x671824, 0x1065000b, +0x0, 0x8ee200c0, 0x8ee300c4, 0x8ee400e0, +0x8ee500e4, 0xaee37b7c, 0xaee57b80, 0x8f820044, +0x38420020, 0x8001b38, 0xaf820044, 0x8f820044, +0x2403ffdf, 0x431024, 0x8001b38, 0xaf820044, +0x8f820044, 0x2403ffdf, 0x431024, 0xaf820044, +0x8ee27b7c, 0x402821, 0x8ee200c0, 0x8ee300c4, +0x24060000, 0x2407ffff, 0x2021, 0x461024, +0x1444000d, 0x671824, 0x1465000b, 0x0, +0x8ee27b80, 0x402821, 0x8ee200e0, 0x8ee300e4, +0x2021, 0x461024, 0x14440003, 0x671824, +0x1065000b, 0x0, 0x8ee200c0, 0x8ee300c4, +0x8ee400e0, 0x8ee500e4, 0xaee37b7c, 0xaee57b80, +0x8f820044, 0x38420040, 0x8001b38, 0xaf820044, +0x8f820044, 0x34420040, 0x8001b38, 0xaf820044, +0x8f820044, 0x34420040, 0xaf820044, 0x8ee27b8c, +0x24430001, 0x28420015, 0x14400028, 0xaee37b8c, +0x8f820044, 0x38420020, 0xaf820044, 0x8001b38, +0xaee07b8c, 0x8ee204d4, 0x30420001, 0x10400011, +0x0, 0x8f420218, 0x30420080, 0x10400009, +0x0, 0x8f820044, 0x34420020, 0xaf820044, +0x8f820044, 0x2403ffbf, 0x431024, 0x8001b36, +0xaf820044, 0x8f820044, 0x34420060, 0x8001b36, +0xaf820044, 0x8f820044, 0x34420040, 0xaf820044, +0x8ee27b88, 0x24430001, 0x28421389, 0x14400005, +0xaee37b88, 0x8f820044, 0x38420020, 0xaf820044, +0xaee07b88, 0xc004603, 0x0, 0x8fbf0024, +0x8fb00020, 0x3e00008, 0x27bd0028, 0x27bdffb8, +0xafbf0044, 0xafb60040, 0xafb5003c, 0xafb40038, +0xafb30034, 0xafb20030, 0xafb1002c, 0xafb00028, +0x8f960064, 0x32c20004, 0x1040000c, 0x24020004, +0xaf820064, 0x8f420114, 0xaee204e0, 0x8f820060, +0x34420008, 0xaf820060, 0x8ee2016c, 0x24420001, +0xaee2016c, 0x80022f4, 0x8ee2016c, 0x32c20001, +0x10400004, 0x24020001, 0xaf820064, 0x80022f4, +0x0, 0x32c20002, 0x1440000c, 0x3c050003, +0x3c040001, 0x24845354, 0x34a50001, 0x2c03021, +0x3821, 0xafa00010, 0xc002403, 0xafa00014, +0x2402fff8, 0x80022f4, 0xaf820064, 0x8f43022c, +0x8f42010c, 0x5062000c, 0xafa00010, 0x8f42022c, +0x21080, 0x5a1021, 0x8c420300, 0xafa20020, +0x8f42022c, 0x24070001, 0x24420001, 0x3042003f, +0x8001b80, 0xaf42022c, 0x3c040001, 0x24845360, +0xafa00014, 0x8f46022c, 0x8f47010c, 0x3c050003, +0xc002403, 0x34a5f01f, 0x3821, 0x14e00003, +0x0, 0x80022ed, 0xaf960064, 0x93a20020, +0x2443ffff, 0x2c620011, 0x10400658, 0x31080, +0x3c010001, 0x220821, 0x8c225418, 0x400008, +0x0, 0x8fa20020, 0x30420fff, 0xaee20e0c, +0x8f820060, 0x34420200, 0xaf820060, 0x8ee20118, +0x24420001, 0xaee20118, 0x80022e8, 0x8ee20118, +0x8fa20020, 0x24030001, 0x3c010001, 0x370821, +0xa02383b1, 0x30420fff, 0xaee25238, 0x8f820060, +0x34420100, 0xaf820060, 0x8ee20144, 0x24420001, +0xaee20144, 0x80022e8, 0x8ee20144, 0x8fa20020, +0x21200, 0x22502, 0x24020001, 0x10820005, +0x24020002, 0x10820009, 0x2402fffe, 0x8001bc9, +0xafa00010, 0x8ee204d4, 0xaee40070, 0xaee40074, +0x34420001, 0x8001bbd, 0xaee204d4, 0x8ee304d4, +0xaee40070, 0xaee40074, 0x621824, 0xaee304d4, +0x8f840054, 0x41442, 0x41c82, 0x431021, +0x41cc2, 0x431023, 0x41d02, 0x431021, +0x41d42, 0x431023, 0x8001bd0, 0xaee20078, +0x3c040001, 0x2484536c, 0xafa00014, 0x8fa60020, +0x3c050003, 0xc002403, 0x34a50004, 0x8ee20110, +0x24420001, 0xaee20110, 0x80022e8, 0x8ee20110, +0x27440212, 0xc0022fe, 0x24050006, 0x3049001f, +0x920c0, 0x2e41021, 0x9442727c, 0x30424000, +0x1040000a, 0x971021, 0x97430212, 0xa443727e, +0x8f430214, 0x971021, 0xac437280, 0x2e41821, +0x34028000, 0x8001c79, 0xa462727c, 0x9443727e, +0x97420212, 0x14620006, 0x2e41021, 0x971021, +0x8c437280, 0x8f420214, 0x1062009f, 0x2e41021, +0x9442727c, 0x30428000, 0x1040002a, 0x2406ffff, +0x2021, 0x410c0, 0x2e21021, 0x9442737c, +0x30424000, 0x54400005, 0x803021, 0x24840001, +0x2c820080, 0x1440fff8, 0x410c0, 0x4c10010, +0x618c0, 0x610c0, 0x571821, 0x8c63737c, +0x571021, 0xafa30010, 0x8c427380, 0x3c040001, +0x24845378, 0xafa20014, 0x8f470214, 0x3c050003, +0xc002403, 0x34a50013, 0x8001c90, 0x3c020800, +0x97440212, 0x771021, 0xa444737e, 0x8f440214, +0x771021, 0x2e31821, 0xac447380, 0x34028000, +0xa462737c, 0x910c0, 0x2e21021, 0x8001c79, +0xa446727c, 0x2e41021, 0x9445727c, 0x8001c2e, +0x510c0, 0x9443737e, 0x97420212, 0x14620006, +0x510c0, 0x971021, 0x8c437380, 0x8f420214, +0x10620065, 0x510c0, 0x2e21021, 0x9445737c, +0x510c0, 0x2e21021, 0x9442737c, 0x30428000, +0x1040fff0, 0x971021, 0x520c0, 0x971021, +0x9443737e, 0x97420212, 0x14620006, 0x2406ffff, +0x971021, 0x8c437380, 0x8f420214, 0x10620053, +0x3c020800, 0x2021, 0x410c0, 0x2e21021, +0x9442737c, 0x30424000, 0x54400005, 0x803021, +0x24840001, 0x2c820080, 0x1440fff8, 0x410c0, +0x4c10023, 0x618c0, 0x910c0, 0x571821, +0x8c63727c, 0x571021, 0xafa30010, 0x8c427280, +0x3c040001, 0x24845384, 0xafa20014, 0x8f470214, +0x3c050003, 0xc002403, 0x34a5f017, 0x8001c90, +0x3c020800, 0x8f430210, 0xb71021, 0xac43777c, +0x8f430214, 0xb71021, 0xac437780, 0x3c020001, +0x571021, 0x8c4283b4, 0x24420001, 0x3c010001, +0x370821, 0xac2283b4, 0x3c030001, 0x771821, +0x8c6383b4, 0x2e51021, 0x8001c82, 0xa443777c, +0x97440212, 0x771021, 0xa444737e, 0x8f440214, +0x771021, 0x2e31821, 0xac447380, 0x34028000, +0xa462737c, 0x510c0, 0x2e21021, 0xa446737c, +0x2021, 0x428c0, 0x2e51021, 0x9442777c, +0x1040ffdc, 0x24840001, 0x2c820080, 0x5440fffa, +0x428c0, 0x92e204d8, 0x10400006, 0x24020001, +0x8ee304dc, 0x1221004, 0x621825, 0x8001c8f, +0xaee304dc, 0x8f830228, 0x24020001, 0x1221004, +0x621825, 0xaf830228, 0x3c020800, 0x34421000, +0xafa20018, 0x8ee20608, 0x8f430228, 0x24420001, +0x304a00ff, 0x514300fd, 0xafa00010, 0x8ee20608, +0x210c0, 0x571021, 0x8fa30018, 0x8fa4001c, +0xac43060c, 0xac440610, 0x8f830054, 0x8f820054, +0x24690032, 0x1221023, 0x2c420033, 0x1040006a, +0x5821, 0x24100008, 0x240f000d, 0x240d0007, +0x240c0040, 0x240e0001, 0x8f870120, 0x27623800, +0x24e80020, 0x102102b, 0x50400001, 0x27683000, +0x8f820128, 0x11020004, 0x0, 0x8f820124, +0x15020007, 0x1021, 0x8ee201a4, 0x3821, +0x24420001, 0xaee201a4, 0x8001d08, 0x8ee201a4, +0x8ee40608, 0x420c0, 0x801821, 0x8ee40430, +0x8ee50434, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xace40000, 0xace50004, 0x8ee20608, +0xa4f0000e, 0xacef0018, 0xacea001c, 0x210c0, +0x2442060c, 0x2e21021, 0xace20008, 0x8ee204c4, +0xace20010, 0xaf880120, 0x92e24e20, 0x14400033, +0x24070001, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0x8c820000, 0x144d001f, 0x0, +0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x0, +0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, +0x8ee34e30, 0x24420001, 0x104c0007, 0x0, +0x8ee24e34, 0x24420001, 0x10620005, 0x0, +0x8001cf5, 0x0, 0x14600005, 0x0, +0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, +0x8c820004, 0x2c420011, 0x50400010, 0xac800000, +0x8001d08, 0x0, 0x8ee24e30, 0x24420001, +0x504c0003, 0x1021, 0x8ee24e30, 0x24420001, +0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0xac8d0000, 0xac8e0004, 0x54e00006, +0x240b0001, 0x8f820054, 0x1221023, 0x2c420033, +0x1440ff9d, 0x0, 0x316300ff, 0x24020001, +0x54620078, 0xafa00010, 0xaeea0608, 0x8f830054, +0x8f820054, 0x24690032, 0x1221023, 0x2c420033, +0x10400061, 0x5821, 0x240e0008, 0x240d0011, +0x240a0012, 0x24080040, 0x240c0001, 0x8f830120, +0x27623800, 0x24660020, 0xc2102b, 0x50400001, +0x27663000, 0x8f820128, 0x10c20004, 0x0, +0x8f820124, 0x14c20007, 0x0, 0x8ee201a4, +0x3821, 0x24420001, 0xaee201a4, 0x8001d74, +0x8ee201a4, 0x8ee20608, 0xac62001c, 0x8ee404a0, +0x8ee504a4, 0x2462001c, 0xac620008, 0xa46e000e, +0xac6d0018, 0xac640000, 0xac650004, 0x8ee204c4, +0xac620010, 0xaf860120, 0x92e24e20, 0x14400033, +0x24070001, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0x8c820000, 0x144a001f, 0x0, +0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x0, +0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, +0x8ee34e30, 0x24420001, 0x10480007, 0x0, +0x8ee24e34, 0x24420001, 0x10620005, 0x0, +0x8001d61, 0x0, 0x14600005, 0x0, +0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, +0x8c820004, 0x2c420011, 0x50400010, 0xac800000, +0x8001d74, 0x0, 0x8ee24e30, 0x24420001, +0x50480003, 0x1021, 0x8ee24e30, 0x24420001, +0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0xac8a0000, 0xac8c0004, 0x54e00006, +0x240b0001, 0x8f820054, 0x1221023, 0x2c420033, +0x1440ffa6, 0x0, 0x316300ff, 0x24020001, +0x10620022, 0x0, 0x3c040001, 0x24845390, +0xafa00010, 0xafa00014, 0x8f860120, 0x8f870124, +0x3c050009, 0xc002403, 0x34a5f011, 0x8001da0, +0x0, 0x3c040001, 0x2484539c, 0xafa00014, +0x8f860120, 0x8f870124, 0x3c050009, 0xc002403, +0x34a5f010, 0x8001da0, 0x0, 0x3c040001, +0x248453a8, 0xafa00014, 0x8ee60608, 0x8f470228, +0x3c050009, 0xc002403, 0x34a5f00f, 0x8ee201ac, +0x24420001, 0xaee201ac, 0x8ee201ac, 0x8ee20124, +0x24420001, 0xaee20124, 0x8001f97, 0x8ee20124, +0x27440212, 0xc0022fe, 0x24050006, 0x3049001f, +0x928c0, 0x2e51021, 0x9442727c, 0x30428000, +0x1040002f, 0x2e51021, 0x9442727c, 0x30424000, +0x1440001c, 0xb71021, 0x9443727e, 0x97420212, +0x14620018, 0xb71021, 0x8c437280, 0x8f420214, +0x54620016, 0xafa20010, 0x92e204d8, 0x10400007, +0x24020001, 0x8ee304dc, 0x1221004, 0x21027, +0x621824, 0x8001dc9, 0xaee304dc, 0x8f830228, +0x1221004, 0x21027, 0x621824, 0xaf830228, +0x910c0, 0x2e21821, 0x3402c000, 0x8001e4e, +0xa462727c, 0x8f420214, 0xafa20010, 0x910c0, +0x571021, 0x8c42727c, 0x3c040001, 0x248453b4, +0x3c050003, 0xafa20014, 0x8f470210, 0x34a5f01c, +0xc002403, 0x1203021, 0x8001e83, 0x3c020800, +0xb71021, 0x9443727e, 0x97420212, 0x14620019, +0x918c0, 0xb71021, 0x8c437280, 0x8f420214, +0x14620014, 0x918c0, 0x2e51021, 0x9447727c, +0x720c0, 0x971021, 0x9443737e, 0xb71021, +0xa443727e, 0x971021, 0x8c437380, 0xb71021, +0xac437280, 0x2e41021, 0x9443737c, 0x2e51021, +0xa443727c, 0x2e41821, 0x3402c000, 0x8001e4e, +0xa462737c, 0x2e31021, 0x9447727c, 0x3021, +0x720c0, 0x2e41021, 0x9442737c, 0x4021, +0x30428000, 0x14400025, 0xe02821, 0x605021, +0x340bc000, 0x971021, 0x9443737e, 0x97420212, +0x54620015, 0xe02821, 0x971021, 0x8c437380, +0x8f420214, 0x54620010, 0xe02821, 0x11000006, +0x2e41021, 0x9443737c, 0x510c0, 0x2e21021, +0x8001e1a, 0xa443737c, 0x9443737c, 0x2ea1021, +0xa443727c, 0x710c0, 0x2e21021, 0xa44b737c, +0x8001e28, 0x24060001, 0x510c0, 0x2e21021, +0x9447737c, 0x720c0, 0x2e41021, 0x9442737c, +0x30428000, 0x1040ffdf, 0x25080001, 0x30c200ff, +0x14400025, 0x2021, 0x720c0, 0x971021, +0x9443737e, 0x97420212, 0x1462000f, 0x910c0, +0x971021, 0x8c437380, 0x8f420214, 0x1462000a, +0x910c0, 0x2e41821, 0x3402c000, 0x15000015, +0xa462737c, 0x910c0, 0x2e21821, 0x34028000, +0x8001e4e, 0xa462727c, 0x571021, 0x8c42727c, +0x3c040001, 0x248453c0, 0x3c050003, 0xafa20010, +0x710c0, 0x571021, 0x8c42737c, 0x34a5001e, +0x1203021, 0xc002403, 0xafa20014, 0x8001e83, +0x3c020800, 0x2021, 0x428c0, 0xb71021, +0x9443777e, 0x97420212, 0x5462002b, 0x24840001, +0xb71021, 0x8c437780, 0x8f420214, 0x54620026, +0x24840001, 0x3c020001, 0x571021, 0x8c4283b4, +0x2442ffff, 0x3c010001, 0x370821, 0xac2283b4, +0x3c020001, 0x571021, 0x8c4283b4, 0x809021, +0x242102b, 0x1040000e, 0x24b1777c, 0x24b07784, +0x2f02021, 0x2f12821, 0xc002490, 0x24060008, +0x26310008, 0x3c020001, 0x571021, 0x8c4283b4, +0x26520001, 0x242102b, 0x1440fff5, 0x26100008, +0x3c040001, 0x972021, 0x8c8483b4, 0x24050008, +0x420c0, 0x2484777c, 0xc002488, 0x2e42021, +0x8001e83, 0x3c020800, 0x2c820080, 0x1440ffcf, +0x428c0, 0x3c020800, 0x34422000, 0xafa20018, +0x8ee20608, 0x8f430228, 0x24420001, 0x304a00ff, +0x514300fd, 0xafa00010, 0x8ee20608, 0x210c0, +0x571021, 0x8fa30018, 0x8fa4001c, 0xac43060c, +0xac440610, 0x8f830054, 0x8f820054, 0x24690032, +0x1221023, 0x2c420033, 0x1040006a, 0x5821, +0x24100008, 0x240f000d, 0x240d0007, 0x240c0040, +0x240e0001, 0x8f870120, 0x27623800, 0x24e80020, +0x102102b, 0x50400001, 0x27683000, 0x8f820128, +0x11020004, 0x0, 0x8f820124, 0x15020007, +0x1021, 0x8ee201a4, 0x3821, 0x24420001, +0xaee201a4, 0x8001efb, 0x8ee201a4, 0x8ee40608, +0x420c0, 0x801821, 0x8ee40430, 0x8ee50434, +0xa32821, 0xa3302b, 0x822021, 0x862021, +0xace40000, 0xace50004, 0x8ee20608, 0xa4f0000e, +0xacef0018, 0xacea001c, 0x210c0, 0x2442060c, +0x2e21021, 0xace20008, 0x8ee204c4, 0xace20010, +0xaf880120, 0x92e24e20, 0x14400033, 0x24070001, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x8c820000, 0x144d001f, 0x0, 0x8ee34e30, +0x8ee24e34, 0x1062001b, 0x0, 0x8c820004, +0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, +0x24420001, 0x104c0007, 0x0, 0x8ee24e34, +0x24420001, 0x10620005, 0x0, 0x8001ee8, +0x0, 0x14600005, 0x0, 0x8f820128, +0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, +0x2c420011, 0x50400010, 0xac800000, 0x8001efb, +0x0, 0x8ee24e30, 0x24420001, 0x504c0003, +0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0xac8d0000, 0xac8e0004, 0x54e00006, 0x240b0001, +0x8f820054, 0x1221023, 0x2c420033, 0x1440ff9d, +0x0, 0x316300ff, 0x24020001, 0x54620078, +0xafa00010, 0xaeea0608, 0x8f830054, 0x8f820054, +0x24690032, 0x1221023, 0x2c420033, 0x10400061, +0x5821, 0x240e0008, 0x240d0011, 0x240a0012, +0x24080040, 0x240c0001, 0x8f830120, 0x27623800, +0x24660020, 0xc2102b, 0x50400001, 0x27663000, +0x8f820128, 0x10c20004, 0x0, 0x8f820124, +0x14c20007, 0x0, 0x8ee201a4, 0x3821, +0x24420001, 0xaee201a4, 0x8001f67, 0x8ee201a4, +0x8ee20608, 0xac62001c, 0x8ee404a0, 0x8ee504a4, +0x2462001c, 0xac620008, 0xa46e000e, 0xac6d0018, +0xac640000, 0xac650004, 0x8ee204c4, 0xac620010, +0xaf860120, 0x92e24e20, 0x14400033, 0x24070001, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x8c820000, 0x144a001f, 0x0, 0x8ee34e30, +0x8ee24e34, 0x1062001b, 0x0, 0x8c820004, +0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, +0x24420001, 0x10480007, 0x0, 0x8ee24e34, +0x24420001, 0x10620005, 0x0, 0x8001f54, +0x0, 0x14600005, 0x0, 0x8f820128, +0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, +0x2c420011, 0x50400010, 0xac800000, 0x8001f67, +0x0, 0x8ee24e30, 0x24420001, 0x50480003, +0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0xac8a0000, 0xac8c0004, 0x54e00006, 0x240b0001, +0x8f820054, 0x1221023, 0x2c420033, 0x1440ffa6, +0x0, 0x316300ff, 0x24020001, 0x10620022, +0x0, 0x3c040001, 0x24845390, 0xafa00010, +0xafa00014, 0x8f860120, 0x8f870124, 0x3c050009, +0xc002403, 0x34a5f011, 0x8001f93, 0x0, +0x3c040001, 0x2484539c, 0xafa00014, 0x8f860120, +0x8f870124, 0x3c050009, 0xc002403, 0x34a5f010, +0x8001f93, 0x0, 0x3c040001, 0x248453a8, +0xafa00014, 0x8ee60608, 0x8f470228, 0x3c050009, +0xc002403, 0x34a5f00f, 0x8ee201ac, 0x24420001, +0xaee201ac, 0x8ee201ac, 0x8ee20128, 0x24420001, +0xaee20128, 0x8ee20128, 0x8ee20164, 0x24420001, +0xaee20164, 0x80022e8, 0x8ee20164, 0x8fa20020, +0x21200, 0x21d02, 0x24020001, 0x10620005, +0x24020002, 0x1062000d, 0x0, 0x8001fb7, +0xafa00010, 0x92e204d8, 0x14400006, 0x24020001, +0x8f820228, 0xaee204dc, 0x2402ffff, 0xaf820228, +0x24020001, 0x8001fbe, 0xa2e204d8, 0x92e204d8, +0x5040000c, 0xa2e004d8, 0x8ee204dc, 0xaf820228, +0x8001fbe, 0xa2e004d8, 0x3c040001, 0x248453c8, +0xafa00014, 0x8fa60020, 0x3c050003, 0xc002403, +0x34a5f009, 0x8ee2013c, 0x24420001, 0xaee2013c, +0x80022e8, 0x8ee2013c, 0x8fa20020, 0x21200, +0x22502, 0x24020001, 0x10820005, 0x24020002, +0x1082000f, 0x0, 0x8001fe3, 0xafa00010, +0x8f820220, 0x3c0308ff, 0x3463ffff, 0x431024, +0x34420008, 0xaf820220, 0x24020001, 0x3c010001, +0x370821, 0xa02283b2, 0x8001fea, 0xaee40108, +0x8f820220, 0x3c0308ff, 0x3463fff7, 0x431024, +0xaf820220, 0x3c010001, 0x370821, 0xa02083b2, +0x8001fea, 0xaee40108, 0x3c040001, 0x248453d4, +0xafa00014, 0x8fa60020, 0x3c050003, 0xc002403, +0x34a5f00a, 0x8ee2012c, 0x24420001, 0xaee2012c, +0x80022e8, 0x8ee2012c, 0x8fa20020, 0x21200, +0x21d02, 0x24020001, 0x10620005, 0x24020002, +0x1062000e, 0x0, 0x8002011, 0xafa00010, +0x8f820220, 0x3c0308ff, 0x3463ffff, 0x431024, +0x34420008, 0xaf820220, 0x24020001, 0x3c010001, +0x370821, 0x8002018, 0xa02283b3, 0x3c020001, +0x571021, 0x904283b2, 0x3c010001, 0x370821, +0x1440000e, 0xa02083b3, 0x8f820220, 0x3c0308ff, +0x3463fff7, 0x431024, 0x8002018, 0xaf820220, +0x3c040001, 0x248453e0, 0xafa00014, 0x8fa60020, +0x3c050003, 0xc002403, 0x34a5f00b, 0x8ee20114, +0x24420001, 0xaee20114, 0x80022e8, 0x8ee20114, +0x27840208, 0x27450200, 0xc00249a, 0x24060008, +0x26e40094, 0x27450200, 0xc00249a, 0x24060008, +0x8ee20134, 0x24420001, 0xaee20134, 0x80022e8, +0x8ee20134, 0x8f460248, 0x2021, 0xc005108, +0x24050004, 0x8ee20130, 0x24420001, 0xaee20130, +0x80022e8, 0x8ee20130, 0x8ef301cc, 0x8ef401d0, +0x8ef501d8, 0x8ee20140, 0x26e40030, 0x24420001, +0xaee20140, 0x8ef00140, 0x8ef10074, 0x8ef20070, +0xc002488, 0x24050400, 0xaef301cc, 0xaef401d0, +0xaef501d8, 0xaef00140, 0xaef10074, 0xaef20070, +0x8f42025c, 0x26e40094, 0xaee20060, 0x8f420260, +0x27450200, 0x24060008, 0xaee20068, 0x24020006, +0xc00249a, 0xaee20064, 0x3c023b9a, 0x3442ca00, +0xaee2006c, 0x240203e8, 0x24040002, 0x24030001, +0xaee20104, 0xaee40100, 0xaee3010c, 0x8f820220, +0x30420008, 0x10400004, 0x0, 0xaee30108, +0x8002061, 0x2021, 0xaee40108, 0x2021, +0x3c030001, 0x641821, 0x90635c30, 0x2e41021, +0x24840001, 0xa043009c, 0x2c82000f, 0x1440fff8, +0x0, 0x8f820040, 0x2e41821, 0x24840001, +0x21702, 0x24420030, 0xa062009c, 0x2e41021, +0x80022e8, 0xa040009c, 0x24020001, 0x3c010001, +0x370821, 0xa02283e0, 0x240b0400, 0x24080014, +0x240a0040, 0x24090001, 0x8f830100, 0x27623000, +0x24660020, 0xc2102b, 0x50400001, 0x27662800, +0x8f820108, 0x10c20004, 0x0, 0x8f820104, +0x14c20007, 0x26e20030, 0x8ee201a8, 0x3821, +0x24420001, 0xaee201a8, 0x80020a8, 0x8ee201a8, +0x8ee404b8, 0x8ee504bc, 0xac620008, 0xa46b000e, +0xac680018, 0xac60001c, 0xac640000, 0xac650004, +0x8ee204cc, 0xac620010, 0xaf860100, 0x92e204ec, +0x1440000e, 0x24070001, 0x8ee24e28, 0x24420001, +0x504a0003, 0x1021, 0x8ee24e28, 0x24420001, +0xaee24e28, 0x8ee24e28, 0x210c0, 0x24424e38, +0x2e21021, 0xac480000, 0xac490004, 0x10e0ffd2, +0x0, 0x80022e8, 0x0, 0x3c020900, +0xaee05238, 0xaee0523c, 0xaee05240, 0xaee05244, +0xaee001d0, 0x3c010001, 0x370821, 0xa02083b1, +0xafa20018, 0x8ee20608, 0x8f430228, 0x24420001, +0x304a00ff, 0x514300fd, 0xafa00010, 0x8ee20608, +0x210c0, 0x571021, 0x8fa30018, 0x8fa4001c, +0xac43060c, 0xac440610, 0x8f830054, 0x8f820054, +0x24690032, 0x1221023, 0x2c420033, 0x1040006a, +0x5821, 0x24100008, 0x240f000d, 0x240d0007, +0x240c0040, 0x240e0001, 0x8f870120, 0x27623800, +0x24e80020, 0x102102b, 0x50400001, 0x27683000, +0x8f820128, 0x11020004, 0x0, 0x8f820124, +0x15020007, 0x1021, 0x8ee201a4, 0x3821, +0x24420001, 0xaee201a4, 0x800212c, 0x8ee201a4, +0x8ee40608, 0x420c0, 0x801821, 0x8ee40430, +0x8ee50434, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xace40000, 0xace50004, 0x8ee20608, +0xa4f0000e, 0xacef0018, 0xacea001c, 0x210c0, +0x2442060c, 0x2e21021, 0xace20008, 0x8ee204c4, +0xace20010, 0xaf880120, 0x92e24e20, 0x14400033, +0x24070001, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0x8c820000, 0x144d001f, 0x0, +0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x0, +0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, +0x8ee34e30, 0x24420001, 0x104c0007, 0x0, +0x8ee24e34, 0x24420001, 0x10620005, 0x0, +0x8002119, 0x0, 0x14600005, 0x0, +0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, +0x8c820004, 0x2c420011, 0x50400010, 0xac800000, +0x800212c, 0x0, 0x8ee24e30, 0x24420001, +0x504c0003, 0x1021, 0x8ee24e30, 0x24420001, +0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0xac8d0000, 0xac8e0004, 0x54e00006, +0x240b0001, 0x8f820054, 0x1221023, 0x2c420033, +0x1440ff9d, 0x0, 0x316300ff, 0x24020001, +0x54620078, 0xafa00010, 0xaeea0608, 0x8f830054, +0x8f820054, 0x24690032, 0x1221023, 0x2c420033, +0x10400061, 0x5821, 0x240e0008, 0x240d0011, +0x240a0012, 0x24080040, 0x240c0001, 0x8f830120, +0x27623800, 0x24660020, 0xc2102b, 0x50400001, +0x27663000, 0x8f820128, 0x10c20004, 0x0, +0x8f820124, 0x14c20007, 0x0, 0x8ee201a4, +0x3821, 0x24420001, 0xaee201a4, 0x8002198, +0x8ee201a4, 0x8ee20608, 0xac62001c, 0x8ee404a0, +0x8ee504a4, 0x2462001c, 0xac620008, 0xa46e000e, +0xac6d0018, 0xac640000, 0xac650004, 0x8ee204c4, +0xac620010, 0xaf860120, 0x92e24e20, 0x14400033, +0x24070001, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0x8c820000, 0x144a001f, 0x0, +0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x0, +0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, +0x8ee34e30, 0x24420001, 0x10480007, 0x0, +0x8ee24e34, 0x24420001, 0x10620005, 0x0, +0x8002185, 0x0, 0x14600005, 0x0, +0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, +0x8c820004, 0x2c420011, 0x50400010, 0xac800000, +0x8002198, 0x0, 0x8ee24e30, 0x24420001, +0x50480003, 0x1021, 0x8ee24e30, 0x24420001, +0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0xac8a0000, 0xac8c0004, 0x54e00006, +0x240b0001, 0x8f820054, 0x1221023, 0x2c420033, +0x1440ffa6, 0x0, 0x316300ff, 0x24020001, +0x10620022, 0x0, 0x3c040001, 0x24845390, +0xafa00010, 0xafa00014, 0x8f860120, 0x8f870124, +0x3c050009, 0xc002403, 0x34a5f011, 0x80021c4, +0x0, 0x3c040001, 0x2484539c, 0xafa00014, +0x8f860120, 0x8f870124, 0x3c050009, 0xc002403, +0x34a5f010, 0x80021c4, 0x0, 0x3c040001, +0x248453a8, 0xafa00014, 0x8ee60608, 0x8f470228, +0x3c050009, 0xc002403, 0x34a5f00f, 0x8ee201ac, +0x24420001, 0xaee201ac, 0x8ee201ac, 0x8ee20120, +0x24420001, 0xaee20120, 0x8ee20120, 0x8ee20168, +0x24420001, 0xaee20168, 0x80022e8, 0x8ee20168, +0x8f42025c, 0x26e40094, 0xaee20060, 0x8f420260, +0x27450200, 0x24060008, 0xc00249a, 0xaee20068, +0x8f820220, 0x30420008, 0x14400002, 0x24020001, +0x24020002, 0xaee20108, 0x8ee2011c, 0x24420001, +0xaee2011c, 0x80022e8, 0x8ee2011c, 0x3c040001, +0x248453ec, 0xafa00010, 0xafa00014, 0x8fa60020, +0x3c050003, 0xc002403, 0x34a5f00f, 0x93a20020, +0x3c030700, 0x34631000, 0x431025, 0xafa20018, +0x8ee20608, 0x8f430228, 0x24420001, 0x304900ff, +0x512300e2, 0xafa00010, 0x8ee20608, 0x210c0, +0x571021, 0x8fa30018, 0x8fa4001c, 0xac43060c, +0xac440610, 0x8f870120, 0x27623800, 0x24e80020, +0x102102b, 0x50400001, 0x27683000, 0x8f820128, +0x11020004, 0x0, 0x8f820124, 0x15020007, +0x1021, 0x8ee201a4, 0x3821, 0x24420001, +0xaee201a4, 0x800225d, 0x8ee201a4, 0x8ee40608, +0x420c0, 0x801821, 0x8ee40430, 0x8ee50434, +0xa32821, 0xa3302b, 0x822021, 0x862021, +0xace40000, 0xace50004, 0x8ee30608, 0x24020008, +0xa4e2000e, 0x2402000d, 0xace20018, 0xace9001c, +0x318c0, 0x2463060c, 0x2e31021, 0xace20008, +0x8ee204c4, 0xace20010, 0xaf880120, 0x92e24e20, +0x14400037, 0x24070001, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x8c830000, 0x24020007, +0x1462001f, 0x0, 0x8ee34e30, 0x8ee24e34, +0x1062001b, 0x24030040, 0x8c820004, 0x24420001, +0xac820004, 0x8ee24e34, 0x8ee54e30, 0x24420001, +0x10430007, 0x0, 0x8ee24e34, 0x24420001, +0x10a20005, 0x0, 0x8002247, 0x0, +0x14a00005, 0x0, 0x8f820128, 0x24420020, +0xaf820128, 0x8f820128, 0x8c820004, 0x2c420011, +0x50400013, 0xac800000, 0x800225d, 0x0, +0x8ee24e30, 0x24030040, 0x24420001, 0x50430003, +0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x24020007, 0xac820000, 0x24020001, 0xac820004, +0x54e0000c, 0xaee90608, 0x3c040001, 0x248453f4, +0xafa00010, 0xafa00014, 0x8ee60608, 0x8f470228, +0x3c050009, 0xc002403, 0x34a5f000, 0x80022e0, +0x0, 0x8f830120, 0x27623800, 0x24660020, +0xc2102b, 0x50400001, 0x27663000, 0x8f820128, +0x10c20004, 0x0, 0x8f820124, 0x14c20007, +0x0, 0x8ee201a4, 0x3821, 0x24420001, +0xaee201a4, 0x80022c4, 0x8ee201a4, 0x8ee20608, +0xac62001c, 0x8ee404a0, 0x8ee504a4, 0x2462001c, +0xac620008, 0x24020008, 0xa462000e, 0x24020011, +0xac620018, 0xac640000, 0xac650004, 0x8ee204c4, +0xac620010, 0xaf860120, 0x92e24e20, 0x14400037, +0x24070001, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0x8c830000, 0x24020012, 0x1462001f, +0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, +0x24030040, 0x8c820004, 0x24420001, 0xac820004, +0x8ee24e34, 0x8ee54e30, 0x24420001, 0x10430007, +0x0, 0x8ee24e34, 0x24420001, 0x10a20005, +0x0, 0x80022ae, 0x0, 0x14a00005, +0x0, 0x8f820128, 0x24420020, 0xaf820128, +0x8f820128, 0x8c820004, 0x2c420011, 0x50400013, +0xac800000, 0x80022c4, 0x0, 0x8ee24e30, +0x24030040, 0x24420001, 0x50430003, 0x1021, +0x8ee24e30, 0x24420001, 0xaee24e30, 0x8ee24e30, +0x210c0, 0x24425038, 0x2e22021, 0x24020012, +0xac820000, 0x24020001, 0xac820004, 0x14e0001b, +0x0, 0x3c040001, 0x248453fc, 0xafa00010, +0xafa00014, 0x8ee60608, 0x8f470228, 0x3c050009, +0xc002403, 0x34a5f001, 0x8ee201b0, 0x24420001, +0xaee201b0, 0x80022e0, 0x8ee201b0, 0x3c040001, +0x24845408, 0xafa00014, 0x8ee60608, 0x8f470228, +0x3c050009, 0xc002403, 0x34a5f005, 0x8ee201ac, +0x24420001, 0xaee201ac, 0x8ee201ac, 0x8ee20150, +0x24420001, 0xaee20150, 0x8ee20150, 0x8ee20160, +0x24420001, 0xaee20160, 0x8ee20160, 0x8f43022c, +0x8f42010c, 0x14620009, 0x24020002, 0xaf820064, +0x8f820064, 0x14400005, 0x0, 0x8f43022c, +0x8f42010c, 0x1462f875, 0x0, 0x8fbf0044, +0x8fb60040, 0x8fb5003c, 0x8fb40038, 0x8fb30034, +0x8fb20030, 0x8fb1002c, 0x8fb00028, 0x3e00008, +0x27bd0048, 0x27bdfff8, 0x2408ffff, 0x10a00014, +0x4821, 0x3c0aedb8, 0x354a8320, 0x90870000, +0x24840001, 0x3021, 0x1071026, 0x30420001, +0x10400002, 0x81842, 0x6a1826, 0x604021, +0x24c60001, 0x2cc20008, 0x1440fff7, 0x73842, +0x25290001, 0x125102b, 0x1440fff0, 0x0, +0x1001021, 0x3e00008, 0x27bd0008, 0x27bdffe8, +0x27642800, 0xafbf0010, 0xc002488, 0x24051000, +0x24020021, 0xaf800100, 0xaf800104, 0xaf800108, +0xaf800110, 0xaf800114, 0xaf800118, 0xaf800120, +0xaf800124, 0xaf800128, 0xaf800130, 0xaf800134, +0xaf800138, 0xaee04e28, 0xaee04e2c, 0xaee04e30, +0xaee04e34, 0xaf82011c, 0x8f420218, 0x30420040, +0x10400004, 0x0, 0x8f82011c, 0x34420004, +0xaf82011c, 0x8fbf0010, 0x3e00008, 0x27bd0018, +0x27bdffe0, 0xafbf0018, 0x8f820104, 0xafa20010, +0x8f820100, 0x3c050002, 0xafa20014, 0x8f8600b0, +0x8f87011c, 0x3c040001, 0x248454c0, 0xc002403, +0x34a5f000, 0x8f8300b0, 0x3c027f00, 0x621824, +0x3c020400, 0x10620029, 0x43102b, 0x14400008, +0x3c022000, 0x3c020100, 0x10620024, 0x3c020200, +0x10620011, 0x0, 0x8002374, 0x0, +0x10620008, 0x3c024000, 0x1462001c, 0x0, +0x8ee20190, 0x24420001, 0xaee20190, 0x8002374, +0x8ee20190, 0x8ee2018c, 0x24420001, 0xaee2018c, +0x8002374, 0x8ee2018c, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f830104, 0x8f8200b0, 0x34420001, +0xaf8200b0, 0xaf830104, 0x8f82011c, 0x2403fffd, +0x431024, 0xaf82011c, 0x8ee201a0, 0x24420001, +0xaee201a0, 0x8002377, 0x8ee201a0, 0x8f8200b0, +0x34420001, 0xaf8200b0, 0x8fbf0018, 0x3e00008, +0x27bd0020, 0x27bdffe0, 0xafbf001c, 0xafb00018, +0x8f820120, 0xafa20010, 0x8f820124, 0x3c050001, +0xafa20014, 0x8f8600a0, 0x8f87011c, 0x3c040001, +0x248454cc, 0xc002403, 0x34a5f000, 0x8f8300a0, +0x3c027f00, 0x621824, 0x3c020400, 0x10620053, +0x8021, 0x43102b, 0x14400008, 0x3c042000, +0x3c020100, 0x1062004d, 0x3c020200, 0x1062003a, +0x0, 0x80023e0, 0x0, 0x10640003, +0x3c024000, 0x14620045, 0x0, 0x8f8200a0, +0x441024, 0x10400006, 0x0, 0x8ee20194, +0x24420001, 0xaee20194, 0x80023a9, 0x8ee20194, +0x8ee20198, 0x24420001, 0xaee20198, 0x8ee20198, +0x8f82011c, 0x34420002, 0xaf82011c, 0x8f82011c, +0x30420200, 0x1040001b, 0x0, 0x8f8300a0, +0x8f840124, 0x8f8200ac, 0x14400007, 0x24020001, +0x3c020001, 0x3442f000, 0x621024, 0x50400001, +0x24100001, 0x24020001, 0x1200000d, 0xaf8200a0, +0x8f820124, 0x2442ffe0, 0xaf820124, 0x8f820124, +0x8f820124, 0x27633000, 0x43102b, 0x10400005, +0x276237e0, 0xaf820124, 0x80023ca, 0x0, +0xaf840124, 0x8f82011c, 0x2403fffd, 0x431024, +0x80023e3, 0xaf82011c, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f830124, 0x8f8200a0, 0x34420001, +0xaf8200a0, 0xaf830124, 0x8f82011c, 0x2403fffd, +0x431024, 0xaf82011c, 0x8ee2019c, 0x24420001, +0xaee2019c, 0x80023e3, 0x8ee2019c, 0x8f8200a0, +0x34420001, 0xaf8200a0, 0x8fbf001c, 0x8fb00018, +0x3e00008, 0x27bd0020, 0x0, 0x3c020001, +0x8c425c58, 0x27bdffe8, 0xafbf0014, 0x14400012, +0xafb00010, 0x3c100001, 0x26105dd0, 0x2002021, +0xc002488, 0x24052000, 0x26021fe0, 0x3c010001, +0xac225d94, 0x3c010001, 0xac225d90, 0xaf420250, +0x24022000, 0xaf500254, 0xaf420258, 0x24020001, +0x3c010001, 0xac225c58, 0x8fbf0014, 0x8fb00010, +0x3e00008, 0x27bd0018, 0x3c030001, 0x8c635d94, +0x8c820000, 0x8fa80010, 0x8fa90014, 0xac620000, +0x3c020001, 0x8c425d94, 0x8c830004, 0xac430004, +0xac450008, 0x8f840054, 0x2443ffe0, 0xac460010, +0xac470014, 0xac480018, 0xac49001c, 0x3c010001, +0xac235d94, 0xac44000c, 0x3c020001, 0x24425dd0, +0x62182b, 0x10600005, 0x0, 0x3c020001, +0x8c425d90, 0x3c010001, 0xac225d94, 0x3c030001, +0x8c635d94, 0x3c020001, 0x8c425c40, 0xac620000, +0x3c030001, 0x8c635d94, 0x3c020001, 0x8c425c40, +0xac620004, 0x3e00008, 0xaf430250, 0x3c030001, +0x8c635d94, 0x3c020001, 0x8c425c40, 0x27bdffd0, +0xafb40020, 0x8fb40040, 0xafb00010, 0x808021, +0xafb50024, 0x8fb50044, 0x8fa40048, 0xafb10014, +0xa08821, 0xafbf0028, 0xafb3001c, 0xafb20018, +0xac620000, 0x3c050001, 0x8ca55d94, 0x3c020001, +0x8c425c40, 0xc09021, 0xe09821, 0x10800006, +0xaca20004, 0x24a50008, 0xc002490, 0x24060018, +0x800244e, 0x0, 0x24a40008, 0xc002488, +0x24050018, 0x3c020001, 0x8c425d94, 0x3c050001, +0x24a55dd0, 0x2442ffe0, 0x3c010001, 0xac225d94, +0x45102b, 0x10400005, 0x0, 0x3c020001, +0x8c425d90, 0x3c010001, 0xac225d94, 0x3c030001, +0x8c635d94, 0x8e020000, 0xac620000, 0x3c030001, +0x8c635d94, 0x8e020004, 0xac620004, 0xac710008, +0x8f840054, 0x2462ffe0, 0x3c010001, 0xac225d94, +0x45102b, 0xac720010, 0xac730014, 0xac740018, +0xac75001c, 0x10400005, 0xac64000c, 0x3c020001, +0x8c425d90, 0x3c010001, 0xac225d94, 0x3c030001, +0x8c635d94, 0x3c020001, 0x8c425c40, 0xac620000, +0x3c030001, 0x8c635d94, 0x3c020001, 0x8c425c40, +0xac620004, 0xaf430250, 0x8fbf0028, 0x8fb50024, +0x8fb40020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, +0x8fb00010, 0x3e00008, 0x27bd0030, 0x10a00005, +0x0, 0xac800000, 0x24a5fffc, 0x14a0fffd, +0x24840004, 0x3e00008, 0x0, 0x10c00007, +0x0, 0x8c820000, 0x24840004, 0x24c6fffc, +0xaca20000, 0x14c0fffb, 0x24a50004, 0x3e00008, +0x0, 0x10c00007, 0x0, 0x8ca20000, +0x24a50004, 0x24c6fffc, 0xac820000, 0x14c0fffb, +0x24840004, 0x3e00008, 0x0, 0x3e00008, +0x0, 0x27bdffd8, 0xafbf0020, 0x8ee304e4, +0x8ee204e0, 0x10620436, 0x0, 0x8ee204e4, +0x8ee304fc, 0x21100, 0x626021, 0x95870008, +0x8d8a0000, 0x8d8b0004, 0x958d000a, 0x8ee2725c, +0x8ee3726c, 0x30e4ffff, 0x441021, 0x62182b, +0x10600015, 0x31a20004, 0x8f8200d8, 0x8ee37258, +0x431023, 0xaee2726c, 0x8ee2726c, 0x1c400003, +0x3c030001, 0x431021, 0xaee2726c, 0x8ee2725c, +0x8ee3726c, 0x441021, 0x62182b, 0x10600006, +0x31a20004, 0x8ee201b8, 0x24420001, 0xaee201b8, +0x80028e1, 0x8ee201b8, 0x10400240, 0x31a20200, +0x1040014d, 0x4821, 0x96e2045a, 0x30420010, +0x10400149, 0x0, 0x8f840100, 0x27623000, +0x24850020, 0xa2102b, 0x50400001, 0x27652800, +0x8f820108, 0x10a20004, 0x0, 0x8f820104, +0x14a20006, 0x2402000c, 0x8ee201a8, 0x24420001, +0xaee201a8, 0x800252c, 0x8ee201a8, 0xac8a0000, +0xac8b0004, 0x8ee37264, 0x24060005, 0xa482000e, +0xac860018, 0xac830008, 0x8ee204e4, 0xac82001c, +0x8ee204c8, 0xac820010, 0xaf850100, 0x92e204ec, +0x14400036, 0x24090001, 0x8ee24e28, 0x210c0, +0x24424e38, 0x2e22021, 0x8c820000, 0x1446001f, +0x0, 0x8ee34e28, 0x8ee24e2c, 0x1062001b, +0x24030040, 0x8c820004, 0x24420001, 0xac820004, +0x8ee24e2c, 0x8ee54e28, 0x24420001, 0x10430007, +0x0, 0x8ee24e2c, 0x24420001, 0x10a20005, +0x0, 0x8002516, 0x0, 0x14a00005, +0x0, 0x8f820108, 0x24420020, 0xaf820108, +0x8f820108, 0x8c820004, 0x2c420011, 0x50400013, +0xac800000, 0x800252c, 0x0, 0x8ee24e28, +0x24030040, 0x24420001, 0x50430003, 0x1021, +0x8ee24e28, 0x24420001, 0xaee24e28, 0x8ee24e28, +0x210c0, 0x24424e38, 0x2e22021, 0x24020005, +0xac820000, 0x24020001, 0xac820004, 0x1520000a, +0x3c040001, 0xafab0010, 0x8ee27264, 0x3c040001, +0x24845730, 0x3c050004, 0xafa20014, 0x8ee604e4, +0x80028be, 0x34a5f114, 0x8ee27264, 0x34843800, +0x3641821, 0x24420010, 0x43102b, 0x14400073, +0x0, 0x8ee27264, 0x24480010, 0x3641021, +0x102102b, 0x14400002, 0x3c02ffff, 0x1024021, +0x8f850100, 0x27623000, 0x24a60020, 0xc2102b, +0x50400001, 0x27662800, 0x8f820108, 0x10c20004, +0x0, 0x8f820104, 0x14c20007, 0x2563000c, +0x8ee201a8, 0x4821, 0x24420001, 0xaee201a8, +0x80025a0, 0x8ee201a8, 0x2c64000c, 0x1441021, +0xaca20000, 0xaca30004, 0x24e2fff4, 0xa4a2000e, +0x24020006, 0xaca80008, 0xaca20018, 0x8ee204e4, +0xaca2001c, 0x8ee204c8, 0x3c030002, 0x431025, +0xaca20010, 0xaf860100, 0x92e204ec, 0x14400037, +0x24090001, 0x8ee24e28, 0x210c0, 0x24424e38, +0x2e22021, 0x8c830000, 0x24020005, 0x1462001f, +0x0, 0x8ee34e28, 0x8ee24e2c, 0x1062001b, +0x24030040, 0x8c820004, 0x24420001, 0xac820004, +0x8ee24e2c, 0x8ee54e28, 0x24420001, 0x10430007, +0x0, 0x8ee24e2c, 0x24420001, 0x10a20005, +0x0, 0x800258a, 0x0, 0x14a00005, +0x0, 0x8f820108, 0x24420020, 0xaf820108, +0x8f820108, 0x8c820004, 0x2c420011, 0x50400013, +0xac800000, 0x80025a0, 0x0, 0x8ee24e28, +0x24030040, 0x24420001, 0x50430003, 0x1021, +0x8ee24e28, 0x24420001, 0xaee24e28, 0x8ee24e28, +0x210c0, 0x24424e38, 0x2e22021, 0x24020005, +0xac820000, 0x24020001, 0xac820004, 0x1520000a, +0x2508fffc, 0xafab0010, 0x8ee27264, 0x3c040001, +0x24845730, 0x3c050004, 0xafa20014, 0x8ee604e4, +0x80028be, 0x34a5f125, 0x34028100, 0xa5020000, +0x9582000e, 0x800261d, 0xa5020002, 0x8f850100, +0x27623000, 0x24a60020, 0xc2102b, 0x50400001, +0x27662800, 0x8f820108, 0x10c20004, 0x0, +0x8f820104, 0x14c20007, 0x2563000c, 0x8ee201a8, +0x4821, 0x24420001, 0xaee201a8, 0x800260d, +0x8ee201a8, 0x2c64000c, 0x1441021, 0xaca20000, +0xaca30004, 0x8ee37264, 0x24e2fff4, 0xa4a2000e, +0x24020006, 0xaca20018, 0x24630010, 0xaca30008, +0x8ee204e4, 0xaca2001c, 0x8ee204c8, 0x3c030002, +0x431025, 0xaca20010, 0xaf860100, 0x92e204ec, +0x14400037, 0x24090001, 0x8ee24e28, 0x210c0, +0x24424e38, 0x2e22021, 0x8c830000, 0x24020005, +0x1462001f, 0x0, 0x8ee34e28, 0x8ee24e2c, +0x1062001b, 0x24030040, 0x8c820004, 0x24420001, +0xac820004, 0x8ee24e2c, 0x8ee54e28, 0x24420001, +0x10430007, 0x0, 0x8ee24e2c, 0x24420001, +0x10a20005, 0x0, 0x80025f7, 0x0, +0x14a00005, 0x0, 0x8f820108, 0x24420020, +0xaf820108, 0x8f820108, 0x8c820004, 0x2c420011, +0x50400013, 0xac800000, 0x800260d, 0x0, +0x8ee24e28, 0x24030040, 0x24420001, 0x50430003, +0x1021, 0x8ee24e28, 0x24420001, 0xaee24e28, +0x8ee24e28, 0x210c0, 0x24424e38, 0x2e22021, +0x24020005, 0xac820000, 0x24020001, 0xac820004, +0x1520000a, 0x34028100, 0xafab0010, 0x8ee27264, +0x3c040001, 0x24845730, 0x3c050004, 0xafa20014, +0x8ee604e4, 0x80028be, 0x34a5f015, 0x8ee37264, +0xa462000c, 0x8ee37264, 0x9582000e, 0xa462000e, +0x8002681, 0x24e70004, 0x8f840100, 0x27623000, +0x24850020, 0xa2102b, 0x50400001, 0x27652800, +0x8f820108, 0x10a20004, 0x0, 0x8f820104, +0x14a20007, 0x24020006, 0x8ee201a8, 0x4821, +0x24420001, 0xaee201a8, 0x8002677, 0x8ee201a8, +0xac8a0000, 0xac8b0004, 0x8ee37264, 0xa487000e, +0xac820018, 0xac830008, 0x8ee204e4, 0xac82001c, +0x8ee204c8, 0x3c030002, 0x431025, 0xac820010, +0xaf850100, 0x92e204ec, 0x14400037, 0x24090001, +0x8ee24e28, 0x210c0, 0x24424e38, 0x2e22021, +0x8c830000, 0x24020005, 0x1462001f, 0x0, +0x8ee34e28, 0x8ee24e2c, 0x1062001b, 0x24030040, +0x8c820004, 0x24420001, 0xac820004, 0x8ee24e2c, +0x8ee54e28, 0x24420001, 0x10430007, 0x0, +0x8ee24e2c, 0x24420001, 0x10a20005, 0x0, +0x8002661, 0x0, 0x14a00005, 0x0, +0x8f820108, 0x24420020, 0xaf820108, 0x8f820108, +0x8c820004, 0x2c420011, 0x50400013, 0xac800000, +0x8002677, 0x0, 0x8ee24e28, 0x24030040, +0x24420001, 0x50430003, 0x1021, 0x8ee24e28, +0x24420001, 0xaee24e28, 0x8ee24e28, 0x210c0, +0x24424e38, 0x2e22021, 0x24020005, 0xac820000, +0x24020001, 0xac820004, 0x15200009, 0x3c050004, +0xafab0010, 0x8ee27264, 0x3c040001, 0x24845730, +0xafa20014, 0x8ee604e4, 0x80028be, 0x34a5f004, +0x8ee2725c, 0x30e7ffff, 0x471021, 0xaee2725c, +0x8ee204e4, 0x8ee304fc, 0x8ee47258, 0x21100, +0x431021, 0xac44000c, 0x8ee27258, 0xafa20018, +0x8ee3725c, 0xafa3001c, 0x8ee2725c, 0x2c42003c, +0x10400004, 0x24620001, 0x2403fffe, 0x431024, +0xafa2001c, 0x8ee27264, 0x3c060001, 0x34c63800, +0x8ee3725c, 0x2405fff8, 0x471021, 0x24420007, +0x451024, 0x24630007, 0xaee27258, 0x8ee2726c, +0x8ee47258, 0x651824, 0x431023, 0xaee2726c, +0x3661021, 0x82202b, 0x14800004, 0x3c03ffff, +0x8ee27258, 0x431021, 0xaee27258, 0x8ee27258, +0xaee27264, 0x8f8200f0, 0x24470008, 0x27621800, +0xe2102b, 0x50400001, 0x27671000, 0x8f8200f4, +0x14e20007, 0x0, 0x8ee201b4, 0x4821, +0x24420001, 0xaee201b4, 0x80026c4, 0x8ee201b4, +0x8f8200f0, 0x24090001, 0x8fa30018, 0x8fa4001c, +0xac430000, 0xac440004, 0xaf8700f0, 0x15200012, +0xd1142, 0x8f8200f0, 0xafa20010, 0x8f8200f4, +0x3c040001, 0x2484573c, 0xafa20014, 0x8fa60018, +0x8fa7001c, 0x3c050004, 0xc002403, 0x34a5f005, +0x8ee20088, 0x24420001, 0xaee20088, 0x8ee20088, +0x80028d3, 0xaee0725c, 0x30430003, 0x24020002, +0x10620016, 0x28620003, 0x10400005, 0x24020001, +0x10620008, 0x0, 0x8002703, 0x0, +0x24020003, 0x10620017, 0x0, 0x8002703, +0x0, 0x8ee200e8, 0x8ee300ec, 0x24630001, +0x2c640001, 0x441021, 0xaee200e8, 0xaee300ec, +0x8ee200e8, 0x8002703, 0x8ee300ec, 0x8ee200f0, +0x8ee300f4, 0x24630001, 0x2c640001, 0x441021, +0xaee200f0, 0xaee300f4, 0x8ee200f0, 0x8002703, +0x8ee300f4, 0x8ee200f8, 0x8ee300fc, 0x24630001, +0x2c640001, 0x441021, 0xaee200f8, 0xaee300fc, +0x8ee200f8, 0x8ee300fc, 0x8ee2725c, 0x8ee400e0, +0x8ee500e4, 0x401821, 0x1021, 0xa32821, +0xa3302b, 0x822021, 0x862021, 0xaee400e0, +0xaee500e4, 0x80028d3, 0xaee0725c, 0x30e2ffff, +0x104001c1, 0x31a20200, 0x1040014d, 0x4821, +0x96e2045a, 0x30420010, 0x10400149, 0x0, +0x8f840100, 0x27623000, 0x24850020, 0xa2102b, +0x50400001, 0x27652800, 0x8f820108, 0x10a20004, +0x0, 0x8f820104, 0x14a20006, 0x2402000c, +0x8ee201a8, 0x24420001, 0xaee201a8, 0x800276e, +0x8ee201a8, 0xac8a0000, 0xac8b0004, 0x8ee37264, +0x24060005, 0xa482000e, 0xac860018, 0xac830008, +0x8ee204e4, 0xac82001c, 0x8ee204c8, 0xac820010, +0xaf850100, 0x92e204ec, 0x14400036, 0x24090001, +0x8ee24e28, 0x210c0, 0x24424e38, 0x2e22021, +0x8c820000, 0x1446001f, 0x0, 0x8ee34e28, +0x8ee24e2c, 0x1062001b, 0x24030040, 0x8c820004, +0x24420001, 0xac820004, 0x8ee24e2c, 0x8ee54e28, +0x24420001, 0x10430007, 0x0, 0x8ee24e2c, +0x24420001, 0x10a20005, 0x0, 0x8002758, +0x0, 0x14a00005, 0x0, 0x8f820108, +0x24420020, 0xaf820108, 0x8f820108, 0x8c820004, +0x2c420011, 0x50400013, 0xac800000, 0x800276e, +0x0, 0x8ee24e28, 0x24030040, 0x24420001, +0x50430003, 0x1021, 0x8ee24e28, 0x24420001, +0xaee24e28, 0x8ee24e28, 0x210c0, 0x24424e38, +0x2e22021, 0x24020005, 0xac820000, 0x24020001, +0xac820004, 0x1520000a, 0x3c040001, 0xafab0010, +0x8ee27264, 0x3c040001, 0x24845730, 0x3c050004, +0xafa20014, 0x8ee604e4, 0x80028be, 0x34a5f014, +0x8ee27264, 0x34843800, 0x3641821, 0x24420010, +0x43102b, 0x14400073, 0x0, 0x8ee27264, +0x24480010, 0x3641021, 0x102102b, 0x14400002, +0x3c02ffff, 0x1024021, 0x8f850100, 0x27623000, +0x24a60020, 0xc2102b, 0x50400001, 0x27662800, +0x8f820108, 0x10c20004, 0x0, 0x8f820104, +0x14c20007, 0x2563000c, 0x8ee201a8, 0x4821, +0x24420001, 0xaee201a8, 0x80027e2, 0x8ee201a8, +0x2c64000c, 0x1441021, 0xaca20000, 0xaca30004, +0x24e2fff4, 0xa4a2000e, 0x24020006, 0xaca80008, +0xaca20018, 0x8ee204e4, 0xaca2001c, 0x8ee204c8, +0x3c030002, 0x431025, 0xaca20010, 0xaf860100, +0x92e204ec, 0x14400037, 0x24090001, 0x8ee24e28, +0x210c0, 0x24424e38, 0x2e22021, 0x8c830000, +0x24020005, 0x1462001f, 0x0, 0x8ee34e28, +0x8ee24e2c, 0x1062001b, 0x24030040, 0x8c820004, +0x24420001, 0xac820004, 0x8ee24e2c, 0x8ee54e28, +0x24420001, 0x10430007, 0x0, 0x8ee24e2c, +0x24420001, 0x10a20005, 0x0, 0x80027cc, +0x0, 0x14a00005, 0x0, 0x8f820108, +0x24420020, 0xaf820108, 0x8f820108, 0x8c820004, +0x2c420011, 0x50400013, 0xac800000, 0x80027e2, +0x0, 0x8ee24e28, 0x24030040, 0x24420001, +0x50430003, 0x1021, 0x8ee24e28, 0x24420001, +0xaee24e28, 0x8ee24e28, 0x210c0, 0x24424e38, +0x2e22021, 0x24020005, 0xac820000, 0x24020001, +0xac820004, 0x1520000a, 0x2508fffc, 0xafab0010, +0x8ee27264, 0x3c040001, 0x24845730, 0x3c050004, +0xafa20014, 0x8ee604e4, 0x80028be, 0x34a5f015, +0x34028100, 0xa5020000, 0x9582000e, 0x800285f, +0xa5020002, 0x8f850100, 0x27623000, 0x24a60020, +0xc2102b, 0x50400001, 0x27662800, 0x8f820108, +0x10c20004, 0x0, 0x8f820104, 0x14c20007, +0x2563000c, 0x8ee201a8, 0x4821, 0x24420001, +0xaee201a8, 0x800284f, 0x8ee201a8, 0x2c64000c, +0x1441021, 0xaca20000, 0xaca30004, 0x8ee37264, +0x24e2fff4, 0xa4a2000e, 0x24020006, 0xaca20018, +0x24630010, 0xaca30008, 0x8ee204e4, 0xaca2001c, +0x8ee204c8, 0x3c030002, 0x431025, 0xaca20010, +0xaf860100, 0x92e204ec, 0x14400037, 0x24090001, +0x8ee24e28, 0x210c0, 0x24424e38, 0x2e22021, +0x8c830000, 0x24020005, 0x1462001f, 0x0, +0x8ee34e28, 0x8ee24e2c, 0x1062001b, 0x24030040, +0x8c820004, 0x24420001, 0xac820004, 0x8ee24e2c, +0x8ee54e28, 0x24420001, 0x10430007, 0x0, +0x8ee24e2c, 0x24420001, 0x10a20005, 0x0, +0x8002839, 0x0, 0x14a00005, 0x0, +0x8f820108, 0x24420020, 0xaf820108, 0x8f820108, +0x8c820004, 0x2c420011, 0x50400013, 0xac800000, +0x800284f, 0x0, 0x8ee24e28, 0x24030040, +0x24420001, 0x50430003, 0x1021, 0x8ee24e28, +0x24420001, 0xaee24e28, 0x8ee24e28, 0x210c0, +0x24424e38, 0x2e22021, 0x24020005, 0xac820000, +0x24020001, 0xac820004, 0x1520000a, 0x34028100, +0xafab0010, 0x8ee27264, 0x3c040001, 0x24845730, +0x3c050004, 0xafa20014, 0x8ee604e4, 0x80028be, +0x34a5f016, 0x8ee37264, 0xa462000c, 0x8ee37264, +0x9582000e, 0xa462000e, 0x80028c2, 0x24e70004, +0x8f830100, 0x27623000, 0x24640020, 0x82102b, +0x50400001, 0x27642800, 0x8f820108, 0x10820004, +0x0, 0x8f820104, 0x14820007, 0x24050005, +0x8ee201a8, 0x4821, 0x24420001, 0xaee201a8, +0x80028b6, 0x8ee201a8, 0xac6a0000, 0xac6b0004, +0x8ee27264, 0xa467000e, 0xac650018, 0xac620008, +0x8ee204e4, 0xac62001c, 0x8ee204c8, 0xac620010, +0xaf840100, 0x92e204ec, 0x14400036, 0x24090001, +0x8ee24e28, 0x210c0, 0x24424e38, 0x2e22021, +0x8c820000, 0x1445001f, 0x0, 0x8ee34e28, +0x8ee24e2c, 0x1062001b, 0x24030040, 0x8c820004, +0x24420001, 0xac820004, 0x8ee24e2c, 0x8ee54e28, +0x24420001, 0x10430007, 0x0, 0x8ee24e2c, +0x24420001, 0x10a20005, 0x0, 0x80028a0, +0x0, 0x14a00005, 0x0, 0x8f820108, +0x24420020, 0xaf820108, 0x8f820108, 0x8c820004, +0x2c420011, 0x50400013, 0xac800000, 0x80028b6, +0x0, 0x8ee24e28, 0x24030040, 0x24420001, +0x50430003, 0x1021, 0x8ee24e28, 0x24420001, +0xaee24e28, 0x8ee24e28, 0x210c0, 0x24424e38, +0x2e22021, 0x24020005, 0xac820000, 0x24020001, +0xac820004, 0x1520000b, 0x3c050004, 0x3c040001, +0x24845748, 0xafab0010, 0xafa00014, 0x8ee604e4, +0x34a5f017, 0xc002403, 0x30e7ffff, 0x80028e1, +0x0, 0x8ee27264, 0x3c050001, 0x30e4ffff, +0x441021, 0xaee27264, 0x8ee2725c, 0x8ee37264, +0x34a53800, 0x441021, 0xaee2725c, 0x3651021, +0x62182b, 0x14600004, 0x3c03ffff, 0x8ee27264, +0x431021, 0xaee27264, 0x8ee304e4, 0x96e20458, +0x24630001, 0x2442ffff, 0x621824, 0xaee304e4, +0x8ee304e4, 0x8ee204e0, 0x14620005, 0x0, +0x8f820060, 0x2403fff7, 0x431024, 0xaf820060, +0x8fbf0020, 0x3e00008, 0x27bd0028, 0x27bdffe0, +0xafbf0018, 0x8ee304e8, 0x8ee204e0, 0x10620189, +0x0, 0x8ee204e8, 0x8ee304fc, 0x21100, +0x621821, 0x94670008, 0x92e204ed, 0x8c680000, +0x8c690004, 0x10400023, 0x946a000a, 0x8ee204c8, +0x34460400, 0x31420200, 0x1040001f, 0x0, +0x96e2045a, 0x30420010, 0x1040001b, 0x3c028000, +0x3c010001, 0x370821, 0xac2283d8, 0x8ee27264, +0x9464000e, 0x3c050001, 0x34a53800, 0x24420004, +0xaee27264, 0x8ee37264, 0x42400, 0x3651021, +0x3c010001, 0x370821, 0xac2483dc, 0x62182b, +0x14600005, 0x24e70004, 0x8ee27264, 0x3c03ffff, +0x431021, 0xaee27264, 0x8ee27264, 0x8002917, +0xaee27258, 0x8ee604c8, 0x8ee2726c, 0x30e4ffff, +0x44102a, 0x10400015, 0x0, 0x8f8200d8, +0x8ee37258, 0x431023, 0xaee2726c, 0x8ee2726c, +0x1c400007, 0x44102a, 0x8ee2726c, 0x3c030001, +0x431021, 0xaee2726c, 0x8ee2726c, 0x44102a, +0x10400006, 0x0, 0x8ee201b8, 0x24420001, +0xaee201b8, 0x8002a72, 0x8ee201b8, 0x3c020001, +0x571021, 0x8c4283d8, 0x54400001, 0x24e7fffc, +0x31420004, 0x104000b9, 0x30e2ffff, 0x3c020001, +0x571021, 0x8c4283d8, 0x1040002f, 0x5021, +0x8f840100, 0x27623000, 0x24850020, 0xa2102b, +0x50400001, 0x27652800, 0x8f820108, 0x10a20032, +0x0, 0x8f820104, 0x10a2002f, 0x24020015, +0xac880000, 0xac890004, 0x8ee37264, 0xa487000e, +0xac820018, 0xac830008, 0x8ee204e8, 0x3c030001, +0x771821, 0x8c6383dc, 0xac860010, 0x431025, +0xac82001c, 0xaf850100, 0x92e204ec, 0x14400066, +0x240a0001, 0x8ee24e28, 0x24030040, 0x24420001, +0x50430003, 0x1021, 0x8ee24e28, 0x24420001, +0xaee24e28, 0x8ee24e28, 0x210c0, 0x24424e38, +0x2e21821, 0x24020015, 0xac620000, 0x24020001, +0x80029bf, 0xac620004, 0x8f840100, 0x27623000, +0x24850020, 0xa2102b, 0x50400001, 0x27652800, +0x8f820108, 0x10a20004, 0x0, 0x8f820104, +0x14a20006, 0x24020006, 0x8ee201a8, 0x24420001, +0xaee201a8, 0x80029bf, 0x8ee201a8, 0xac880000, +0xac890004, 0x8ee37264, 0xa487000e, 0xac820018, +0xac830008, 0x8ee204e8, 0xac860010, 0xac82001c, +0xaf850100, 0x92e204ec, 0x14400037, 0x240a0001, +0x8ee24e28, 0x210c0, 0x24424e38, 0x2e22021, +0x8c830000, 0x24020005, 0x1462001f, 0x0, +0x8ee34e28, 0x8ee24e2c, 0x1062001b, 0x24030040, +0x8c820004, 0x24420001, 0xac820004, 0x8ee24e2c, +0x8ee54e28, 0x24420001, 0x10430007, 0x0, +0x8ee24e2c, 0x24420001, 0x10a20005, 0x0, +0x80029a9, 0x0, 0x14a00005, 0x0, +0x8f820108, 0x24420020, 0xaf820108, 0x8f820108, +0x8c820004, 0x2c420011, 0x50400013, 0xac800000, +0x80029bf, 0x0, 0x8ee24e28, 0x24030040, +0x24420001, 0x50430003, 0x1021, 0x8ee24e28, +0x24420001, 0xaee24e28, 0x8ee24e28, 0x210c0, +0x24424e38, 0x2e22021, 0x24020005, 0xac820000, +0x24020001, 0xac820004, 0x1540000a, 0x24020001, +0xafa90010, 0x8ee27264, 0x3c040001, 0x24845730, +0x3c050004, 0xafa20014, 0x8ee604e4, 0x8002a4f, +0x34a5f204, 0xa2e204ed, 0x8ee204e8, 0x8ee304fc, +0x8ee47258, 0x3c060001, 0x34c63800, 0x3c010001, +0x370821, 0xac2083d8, 0x3c010001, 0x370821, +0xac2083dc, 0x21100, 0x431021, 0xac44000c, +0x8ee27264, 0x2405fff8, 0x30e3ffff, 0x431021, +0x24420007, 0x451024, 0x24630007, 0xaee27258, +0x8ee2726c, 0x8ee47258, 0x651824, 0x431023, +0xaee2726c, 0x3661021, 0x82202b, 0x14800004, +0x3c03ffff, 0x8ee27258, 0x431021, 0xaee27258, +0x8ee27258, 0x8002a64, 0xaee27264, 0x10400073, +0x0, 0x8f830100, 0x27623000, 0x24640020, +0x82102b, 0x14400002, 0x5021, 0x27642800, +0x8f820108, 0x10820004, 0x0, 0x8f820104, +0x14820006, 0x24050005, 0x8ee201a8, 0x24420001, +0xaee201a8, 0x8002a46, 0x8ee201a8, 0xac680000, +0xac690004, 0x8ee27264, 0xa467000e, 0xac650018, +0xac620008, 0x8ee204e8, 0xac660010, 0xac62001c, +0xaf840100, 0x92e204ec, 0x14400036, 0x240a0001, +0x8ee24e28, 0x210c0, 0x24424e38, 0x2e22021, +0x8c820000, 0x1445001f, 0x0, 0x8ee34e28, +0x8ee24e2c, 0x1062001b, 0x24030040, 0x8c820004, +0x24420001, 0xac820004, 0x8ee24e2c, 0x8ee54e28, +0x24420001, 0x10430007, 0x0, 0x8ee24e2c, +0x24420001, 0x10a20005, 0x0, 0x8002a30, +0x0, 0x14a00005, 0x0, 0x8f820108, +0x24420020, 0xaf820108, 0x8f820108, 0x8c820004, +0x2c420011, 0x50400013, 0xac800000, 0x8002a46, +0x0, 0x8ee24e28, 0x24030040, 0x24420001, +0x50430003, 0x1021, 0x8ee24e28, 0x24420001, +0xaee24e28, 0x8ee24e28, 0x210c0, 0x24424e38, +0x2e22021, 0x24020005, 0xac820000, 0x24020001, +0xac820004, 0x1540000c, 0x30e5ffff, 0x3c040001, +0x24845748, 0x3c050004, 0xafa90010, 0xafa00014, +0x8ee604e4, 0x34a5f237, 0xc002403, 0x30e7ffff, +0x8002a72, 0x0, 0x8ee27264, 0x451021, +0xaee27264, 0x8ee2726c, 0x8ee37264, 0x3c040001, +0x34843800, 0xa2e004ed, 0x451023, 0xaee2726c, +0x3641021, 0x62182b, 0x14600004, 0x3c03ffff, +0x8ee27264, 0x431021, 0xaee27264, 0x8ee304e8, +0x96e20458, 0x24630001, 0x2442ffff, 0x621824, +0xaee304e8, 0x8ee304e8, 0x8ee204e0, 0x14620005, +0x0, 0x8f820060, 0x2403fff7, 0x431024, +0xaf820060, 0x8fbf0018, 0x3e00008, 0x27bd0020, +0x27bdffe0, 0xafbf001c, 0xafb00018, 0x8f820100, +0x8ee34e2c, 0x8f820104, 0x8f850108, 0x24020040, +0x24630001, 0x50620003, 0x1021, 0x8ee24e2c, +0x24420001, 0xaee24e2c, 0x8ee24e2c, 0x8ee34e2c, +0x210c0, 0x24424e38, 0x2e22021, 0x8ee24e28, +0x8c870004, 0x14620007, 0xa03021, 0x8f820108, +0x24420020, 0xaf820108, 0x8f820108, 0x8002aa2, +0xac800000, 0x8ee24e2c, 0x24030040, 0x24420001, +0x50430003, 0x1021, 0x8ee24e2c, 0x24420001, +0x210c0, 0x24424e38, 0x2e22021, 0x8c820004, +0x8f830108, 0x21140, 0x621821, 0xaf830108, +0xac800000, 0x8cc20018, 0x2443fffe, 0x2c620013, +0x104000c1, 0x31080, 0x3c010001, 0x220821, +0x8c225770, 0x400008, 0x0, 0x8ee204f0, +0x471021, 0xaee204f0, 0x8ee204f0, 0x8f43023c, +0x43102b, 0x144000be, 0x0, 0x8ee304e4, +0x8ee204f8, 0x506200ba, 0xa2e004f4, 0x8f830120, +0x27623800, 0x24660020, 0xc2102b, 0x50400001, +0x27663000, 0x8f820128, 0x10c20004, 0x0, +0x8f820124, 0x14c20007, 0x0, 0x8ee201a4, +0x8021, 0x24420001, 0xaee201a4, 0x8002b12, +0x8ee201a4, 0x8ee204e4, 0xac62001c, 0x8ee404b0, +0x8ee504b4, 0x2462001c, 0xac620008, 0x24020008, +0xa462000e, 0x24020011, 0xac620018, 0xac640000, +0xac650004, 0x8ee204c4, 0xac620010, 0xaf860120, +0x92e24e20, 0x14400037, 0x24100001, 0x8ee24e30, +0x210c0, 0x24425038, 0x2e22021, 0x8c830000, +0x24020012, 0x1462001f, 0x0, 0x8ee34e30, +0x8ee24e34, 0x1062001b, 0x24030040, 0x8c820004, +0x24420001, 0xac820004, 0x8ee24e34, 0x8ee54e30, +0x24420001, 0x10430007, 0x0, 0x8ee24e34, +0x24420001, 0x10a20005, 0x0, 0x8002afc, +0x0, 0x14a00005, 0x0, 0x8f820128, +0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, +0x2c420011, 0x50400013, 0xac800000, 0x8002b12, +0x0, 0x8ee24e30, 0x24030040, 0x24420001, +0x50430003, 0x1021, 0x8ee24e30, 0x24420001, +0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0x24020012, 0xac820000, 0x24020001, +0xac820004, 0x5600000b, 0x24100001, 0x8ee204e4, +0x3c040001, 0x24845754, 0xafa00014, 0xafa20010, +0x8ee60608, 0x8f470228, 0x3c050009, 0xc002403, +0x34a5f006, 0x16000003, 0x24020001, 0x8002b71, +0xa2e204f4, 0x8ee20170, 0x24420001, 0xaee20170, +0x8ee20170, 0x8ee204e4, 0xa2e004f4, 0xaee004f0, +0xaee204f8, 0x8f42023c, 0x50400045, 0xaee07274, +0x8ee20184, 0x24420001, 0xaee20184, 0x8ee20184, +0x8002b71, 0xaee07274, 0x8ee20504, 0x24030040, +0x24420001, 0x50430003, 0x1021, 0x8ee20504, +0x24420001, 0xaee20504, 0x8ee20504, 0x8cc30018, +0x21080, 0x571021, 0x8c440508, 0x24020003, +0x1462000f, 0x0, 0x3c020001, 0x571021, +0x904283b1, 0x10400014, 0x0, 0x8ee201d0, +0x8ee35240, 0x441021, 0xaee201d0, 0x8ee201d8, +0x641821, 0x306300ff, 0x8002b59, 0xaee35240, +0x8ee201cc, 0x8ee30e10, 0x441021, 0xaee201cc, +0x8ee201d8, 0x641821, 0x306301ff, 0xaee30e10, +0x441021, 0xaee201d8, 0x8ee20000, 0x34420040, +0x8002b71, 0xaee20000, 0x8ee2014c, 0x3c010001, +0x370821, 0xa02083e0, 0x24420001, 0xaee2014c, +0x8002b71, 0x8ee2014c, 0x94c7000e, 0x8cc2001c, +0x3c040001, 0x24845760, 0xafa60014, 0xafa20010, +0x8cc60018, 0x3c050008, 0xc002403, 0x34a50910, +0x8fbf001c, 0x8fb00018, 0x3e00008, 0x27bd0020, +0x27bdff98, 0xafbf0060, 0xafbe005c, 0xafb60058, +0xafb50054, 0xafb40050, 0xafb3004c, 0xafb20048, +0xafb10044, 0xafb00040, 0x8f830108, 0x8f820104, +0xafa00024, 0x106203e7, 0xafa0002c, 0x3c1e0001, +0x37de3800, 0x3c0bffff, 0x8f930108, 0x8e620018, +0x8f830104, 0x2443fffe, 0x2c620014, 0x104003cf, +0x31080, 0x3c010001, 0x220821, 0x8c2257c0, +0x400008, 0x0, 0x9663000e, 0x8ee2725c, +0x8ee404f0, 0x431021, 0xaee2725c, 0x8e63001c, +0x96e20458, 0x24840001, 0xaee404f0, 0x24630001, +0x2442ffff, 0x621824, 0xaee304e4, 0x8f42023c, +0x82202b, 0x148003b9, 0x0, 0x8f830120, +0x27623800, 0x24660020, 0xc2102b, 0x50400001, +0x27663000, 0x8f820128, 0x10c20004, 0x0, +0x8f820124, 0x14c20007, 0x0, 0x8ee201a4, +0x8021, 0x24420001, 0xaee201a4, 0x8002bfe, +0x8ee201a4, 0x8ee204e4, 0xac62001c, 0x8ee404b0, +0x8ee504b4, 0x2462001c, 0xac620008, 0x24020008, +0xa462000e, 0x24020011, 0xac620018, 0xac640000, +0xac650004, 0x8ee204c4, 0xac620010, 0xaf860120, +0x92e24e20, 0x14400037, 0x24100001, 0x8ee24e30, +0x210c0, 0x24425038, 0x2e22021, 0x8c830000, +0x24020012, 0x1462001f, 0x0, 0x8ee34e30, +0x8ee24e34, 0x1062001b, 0x240c0040, 0x8c820004, +0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, +0x24420001, 0x104c0007, 0x0, 0x8ee24e34, +0x24420001, 0x10620005, 0x0, 0x8002be8, +0x0, 0x14600005, 0x0, 0x8f820128, +0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, +0x2c420011, 0x50400013, 0xac800000, 0x8002bfe, +0x0, 0x8ee24e30, 0x240c0040, 0x24420001, +0x504c0003, 0x1021, 0x8ee24e30, 0x24420001, +0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0x24020012, 0x240c0001, 0xac820000, +0xac8c0004, 0x5600000d, 0x24100001, 0x8ee204e4, +0x3c040001, 0x24845754, 0xafa00014, 0xafa20010, +0x8ee60608, 0x8f470228, 0x3c050009, 0x34a5f006, +0xc002403, 0xafab0038, 0x8fab0038, 0x1200030a, +0x240c0001, 0x8002f19, 0x0, 0x966c001c, +0xafac002c, 0x9662001e, 0x3c0c8000, 0xafac0024, +0xae62001c, 0x8e75001c, 0x8ee204fc, 0x8ee404fc, +0x151900, 0x621021, 0x8c52000c, 0x92e27b98, +0x641821, 0x9476000a, 0x14400003, 0x32c20002, +0xaef27ba4, 0xaef57b9c, 0x1040004b, 0x8021, +0x96e2045a, 0x30420002, 0x10400047, 0x0, +0x8e63001c, 0x8ee204fc, 0x32100, 0x821021, +0x8c42000c, 0x37e1821, 0x24420022, 0x43102b, +0x1440000a, 0x24050014, 0x8ee204fc, 0x821021, +0x8c44000c, 0xafab0038, 0xc002f75, 0x2484000e, +0x8fab0038, 0x8002c52, 0x3050ffff, 0x8ee204fc, +0x821021, 0x8c42000c, 0x9450000e, 0x94430010, +0x94440012, 0x94450014, 0x2038021, 0x2048021, +0x2058021, 0x94430016, 0x94440018, 0x9445001a, +0x2038021, 0x2048021, 0x2058021, 0x9443001c, +0x9444001e, 0x94420020, 0x2038021, 0x2048021, +0x2028021, 0x101c02, 0x3202ffff, 0x628021, +0x8e63001c, 0x8ee204fc, 0x102402, 0x32900, +0xa21021, 0x8c43000c, 0x3202ffff, 0x828021, +0x37e1021, 0x24630018, 0x62182b, 0x14600009, +0x0, 0x8ee204fc, 0xa21021, 0x8c43000c, +0x101027, 0x3c01ffff, 0x230821, 0x8002c6f, +0xa4220018, 0x8ee204fc, 0xa21021, 0x8c43000c, +0x101027, 0xa4620018, 0x96e2045a, 0x8821, +0x30420008, 0x14400063, 0xa021, 0x8e63001c, +0x8ee204fc, 0x33100, 0xc21021, 0x8c42000c, +0x37e1821, 0x24420022, 0x43102b, 0x14400035, +0x0, 0x8ee204fc, 0xc21021, 0x8c42000c, +0x24470010, 0x37e1021, 0xe2102b, 0x50400001, +0xeb3821, 0x8ee204fc, 0x94f10000, 0xc21021, +0x8c42000c, 0x24470016, 0x37e1021, 0xe2102b, +0x14400002, 0x2634ffec, 0xeb3821, 0x8ee204fc, +0x90e30001, 0xc21021, 0x8c42000c, 0x2447001a, +0x37e1021, 0xe2102b, 0x14400002, 0x2838821, +0xeb3821, 0x94e20000, 0x24e70002, 0x2228821, +0x37e1021, 0xe2102b, 0x50400001, 0xeb3821, +0x94e20000, 0x24e70002, 0x2228821, 0x37e1021, +0xe2102b, 0x50400001, 0xeb3821, 0x94e20000, +0x24e70002, 0x2228821, 0x37e1021, 0xe2102b, +0x50400001, 0xeb3821, 0x94e20000, 0x8002cd0, +0x2228821, 0x8ee204fc, 0xc21021, 0x8c43000c, +0x8ee204fc, 0x94710010, 0x8ee304fc, 0xc21021, +0x8c44000c, 0xc31821, 0x8c62000c, 0x2634ffec, +0x90840017, 0x8ee304fc, 0x9442001a, 0x2848821, +0xc31821, 0x8c65000c, 0x8ee304fc, 0x2228821, +0x8ee204fc, 0xc31821, 0xc21021, 0x8c44000c, +0x8c62000c, 0x94a3001c, 0x9484001e, 0x94420020, +0x2238821, 0x2248821, 0x2228821, 0x111c02, +0x3222ffff, 0x628821, 0x111c02, 0x3222ffff, +0x628821, 0x32c20001, 0x104000b2, 0x0, +0x96e2045a, 0x30420001, 0x104000ae, 0x32c20080, +0x10400008, 0x0, 0x92e27b98, 0x14400005, +0x0, 0x240c0001, 0xa2ec7b98, 0xaef57b9c, +0xaef27ba4, 0x8ee304fc, 0x151100, 0x431021, +0x8c47000c, 0x37e1821, 0x24e2000e, 0x43102b, +0x14400008, 0xe02021, 0x2405000e, 0xc002f75, +0xafab0038, 0x3042ffff, 0x8fab0038, 0x8002d09, +0x2028021, 0x94e60000, 0x24e70002, 0x94e50000, +0x24e70002, 0x94e30000, 0x24e70002, 0x94e20000, +0x24e70002, 0x94e40000, 0x24e70002, 0x2068021, +0x2058021, 0x2038021, 0x2028021, 0x94e20000, +0x94e30002, 0x2048021, 0x2028021, 0x2038021, +0x101c02, 0x3202ffff, 0x628021, 0x101c02, +0x3202ffff, 0x8ee47b9c, 0x628021, 0x14950004, +0x3205ffff, 0x96620016, 0x8002d17, 0x512021, +0x96620016, 0x542021, 0x41402, 0x3083ffff, +0x432021, 0x852023, 0x41402, 0x822021, +0x3084ffff, 0x50800001, 0x3404ffff, 0x8ee27ba4, +0x24430017, 0x37e1021, 0x62102b, 0x50400001, +0x6b1821, 0x90630000, 0x24020011, 0x14620031, +0x24020006, 0x8ee27ba4, 0x37e1821, 0x24420028, +0x43102b, 0x14400018, 0x0, 0x8ee27b9c, +0x12a2000a, 0x32c20100, 0x8ee27ba4, 0x3c01ffff, +0x220821, 0x94220028, 0x822021, 0x41c02, +0x3082ffff, 0x622021, 0x32c20100, 0x14400004, +0x41027, 0x92e27b98, 0x14400002, 0x41027, +0x3044ffff, 0x8ee27ba4, 0x3c01ffff, 0x220821, +0x8002d8a, 0xa4240028, 0x8ee27b9c, 0x12a20008, +0x32c20100, 0x8ee27ba4, 0x94420028, 0x822021, +0x41c02, 0x3082ffff, 0x622021, 0x32c20100, +0x14400004, 0x41027, 0x92e27b98, 0x14400002, +0x41027, 0x3044ffff, 0x8ee27ba4, 0x8002d8a, +0xa4440028, 0x1462002f, 0x37e1821, 0x8ee27ba4, +0x24420032, 0x43102b, 0x14400018, 0x0, +0x8ee27b9c, 0x12a2000a, 0x32c20100, 0x8ee27ba4, +0x3c01ffff, 0x220821, 0x94220032, 0x822021, +0x41c02, 0x3082ffff, 0x622021, 0x32c20100, +0x14400004, 0x41027, 0x92e27b98, 0x14400002, +0x41027, 0x3044ffff, 0x8ee27ba4, 0x3c01ffff, +0x220821, 0x8002d8a, 0xa4240032, 0x8ee27b9c, +0x12a20008, 0x32c20100, 0x8ee27ba4, 0x94420032, +0x822021, 0x41c02, 0x3082ffff, 0x622021, +0x32c20100, 0x14400004, 0x41027, 0x92e27b98, +0x14400002, 0x41027, 0x3044ffff, 0x8ee27ba4, +0xa4440032, 0x8fac0024, 0x1180002c, 0x37e1821, +0x8e420000, 0xae42fffc, 0x2642000a, 0x43102b, +0x1440001b, 0x34038100, 0x26430004, 0x37e1021, +0x62102b, 0x14400003, 0x602021, 0x6b1821, +0x602021, 0x8c620000, 0x24630004, 0xae420000, +0x37e1021, 0x62102b, 0x50400001, 0x6b1821, +0x8c620000, 0xac820000, 0x34028100, 0xa4620000, +0x24630002, 0x37e1021, 0x62102b, 0x50400001, +0x6b1821, 0x97ac002e, 0x8002db4, 0xa46c0000, +0x8e420004, 0x8e440008, 0xa6430008, 0x97ac002e, +0xa64c000a, 0xae420000, 0xae440004, 0x9662000e, +0x2652fffc, 0x24420004, 0xa662000e, 0x9662000e, +0x8ee3725c, 0x621821, 0xaee3725c, 0xafb20018, +0x8ee3725c, 0xafa3001c, 0x8ee2725c, 0x2c42003c, +0x10400004, 0x24620001, 0x2403fffe, 0x431024, +0xafa2001c, 0x32c20080, 0x1040000c, 0x32c20100, +0x8ee27ba8, 0x24430001, 0x210c0, 0x571021, +0xaee37ba8, 0x8fa30018, 0x8fa4001c, 0xac437bac, +0xac447bb0, 0x8002ea0, 0xaee0725c, 0x10400072, +0x0, 0x8ee27ba8, 0x24430001, 0x210c0, +0x571021, 0xaee37ba8, 0x8fa30018, 0x8fa4001c, +0xac437bac, 0xac447bb0, 0x8ee27ba8, 0x10400063, +0x4821, 0x5021, 0x8f8200f0, 0x24480008, +0x27621800, 0x102102b, 0x50400001, 0x27681000, +0x8f8200f4, 0x15020007, 0x0, 0x8ee201b4, +0x8021, 0x24420001, 0xaee201b4, 0x8002dfa, +0x8ee201b4, 0x8f8300f0, 0x24100001, 0x1571021, +0x8c447bac, 0x8c457bb0, 0xac640000, 0xac650004, +0xaf8800f0, 0x16000006, 0x2ea1021, 0x8ee20088, +0x24420001, 0xaee20088, 0x8002e3f, 0x8ee20088, +0x8c427bb0, 0x8ee400e0, 0x8ee500e4, 0x8ee67b9c, +0x401821, 0x1021, 0xa32821, 0xa3382b, +0x822021, 0x872021, 0x8ee204fc, 0xc93021, +0x63100, 0xaee400e0, 0xaee500e4, 0xc23021, +0x94c2000a, 0x240c0002, 0x21142, 0x30430003, +0x106c0016, 0x28620003, 0x10400005, 0x240c0001, +0x106c0008, 0x0, 0x8002e3f, 0x0, +0x240c0003, 0x106c0017, 0x0, 0x8002e3f, +0x0, 0x8ee200e8, 0x8ee300ec, 0x24630001, +0x2c640001, 0x441021, 0xaee200e8, 0xaee300ec, +0x8ee200e8, 0x8002e3f, 0x8ee300ec, 0x8ee200f0, +0x8ee300f4, 0x24630001, 0x2c640001, 0x441021, +0xaee200f0, 0xaee300f4, 0x8ee200f0, 0x8002e3f, +0x8ee300f4, 0x8ee200f8, 0x8ee300fc, 0x24630001, +0x2c640001, 0x441021, 0xaee200f8, 0xaee300fc, +0x8ee200f8, 0x8ee300fc, 0x8ee27ba8, 0x25290001, +0x122102b, 0x1440ffa0, 0x254a0008, 0xa2e07b98, +0x8002e9f, 0xaee07ba8, 0x8f8200f0, 0x24470008, +0x27621800, 0xe2102b, 0x50400001, 0x27671000, +0x8f8200f4, 0x14e20007, 0x0, 0x8ee201b4, +0x8021, 0x24420001, 0xaee201b4, 0x8002e5d, +0x8ee201b4, 0x8f8200f0, 0x24100001, 0x8fa30018, +0x8fa4001c, 0xac430000, 0xac440004, 0xaf8700f0, +0x16000007, 0x0, 0x8ee20088, 0x24420001, +0xaee20088, 0x8ee20088, 0x8002ea0, 0xaee0725c, +0x8ee2725c, 0x8ee400e0, 0x8ee500e4, 0x240c0002, +0x401821, 0x1021, 0xa32821, 0xa3302b, +0x822021, 0x862021, 0x161142, 0x30430003, +0xaee400e0, 0xaee500e4, 0x106c0017, 0x2c620003, +0x10400005, 0x240c0001, 0x106c0008, 0x0, +0x8002ea0, 0xaee0725c, 0x240c0003, 0x106c0019, +0x0, 0x8002ea0, 0xaee0725c, 0x8ee200e8, +0x8ee300ec, 0x24630001, 0x2c640001, 0x441021, +0xaee200e8, 0xaee300ec, 0x8ee200e8, 0x8ee300ec, +0x8002ea0, 0xaee0725c, 0x8ee200f0, 0x8ee300f4, +0x24630001, 0x2c640001, 0x441021, 0xaee200f0, +0xaee300f4, 0x8ee200f0, 0x8ee300f4, 0x8002ea0, +0xaee0725c, 0x8ee200f8, 0x8ee300fc, 0x24630001, +0x2c640001, 0x441021, 0xaee200f8, 0xaee300fc, +0x8ee200f8, 0x8ee300fc, 0xaee0725c, 0x8e62001c, +0x96e30458, 0x8ee404f0, 0x24420001, 0x2463ffff, +0x431024, 0x24840001, 0xaee204e4, 0xaee404f0, +0x8f42023c, 0x82202b, 0x148000b0, 0x0, +0x8f830120, 0x27623800, 0x24660020, 0xc2102b, +0x50400001, 0x27663000, 0x8f820128, 0x10c20004, +0x0, 0x8f820124, 0x14c20007, 0x0, +0x8ee201a4, 0x8021, 0x24420001, 0xaee201a4, +0x8002f07, 0x8ee201a4, 0x8ee204e4, 0xac62001c, +0x8ee404b0, 0x8ee504b4, 0x2462001c, 0xac620008, +0x24020008, 0xa462000e, 0x24020011, 0xac620018, +0xac640000, 0xac650004, 0x8ee204c4, 0xac620010, +0xaf860120, 0x92e24e20, 0x14400037, 0x24100001, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x8c830000, 0x24020012, 0x1462001f, 0x0, +0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x240c0040, +0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, +0x8ee34e30, 0x24420001, 0x104c0007, 0x0, +0x8ee24e34, 0x24420001, 0x10620005, 0x0, +0x8002ef1, 0x0, 0x14600005, 0x0, +0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, +0x8c820004, 0x2c420011, 0x50400013, 0xac800000, +0x8002f07, 0x0, 0x8ee24e30, 0x240c0040, +0x24420001, 0x504c0003, 0x1021, 0x8ee24e30, +0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x24020012, 0x240c0001, +0xac820000, 0xac8c0004, 0x5600000d, 0x24100001, +0x8ee204e4, 0x3c040001, 0x24845754, 0xafa00014, +0xafa20010, 0x8ee60608, 0x8f470228, 0x3c050009, +0x34a5f006, 0xc002403, 0xafab0038, 0x8fab0038, +0x16000003, 0x240c0001, 0x8002f5c, 0xa2ec04f4, +0x8ee20170, 0x24420001, 0xaee20170, 0x8ee20170, +0x8ee204e4, 0xa2e004f4, 0xaee004f0, 0xaee07274, +0xaee204f8, 0x8f42023c, 0x10400038, 0x0, +0x8ee20184, 0x24420001, 0xaee20184, 0x8002f5c, +0x8ee20184, 0x8ee20504, 0x240c0040, 0x24420001, +0x504c0003, 0x1021, 0x8ee20504, 0x24420001, +0xaee20504, 0x8ee20504, 0x8e630018, 0x240c0003, +0x21080, 0x571021, 0x146c000f, 0x8c440508, +0x3c020001, 0x571021, 0x904283b1, 0x10400014, +0x0, 0x8ee201d0, 0x8ee35240, 0x441021, +0xaee201d0, 0x8ee201d8, 0x641821, 0x306300ff, +0x8002f4f, 0xaee35240, 0x8ee201cc, 0x8ee30e10, +0x441021, 0xaee201cc, 0x8ee201d8, 0x641821, +0x306301ff, 0xaee30e10, 0x441021, 0xaee201d8, +0x8ee20000, 0x34420040, 0x8002f5c, 0xaee20000, +0x8ee2014c, 0x3c010001, 0x370821, 0xa02083e0, +0x24420001, 0xaee2014c, 0x8ee2014c, 0x8f820108, +0x24420020, 0xaf820108, 0x8f820108, 0x8f820108, +0x27633000, 0x43102b, 0x14400002, 0x27622800, +0xaf820108, 0x8f830108, 0x8f820104, 0x1462fc1e, +0x0, 0x8fbf0060, 0x8fbe005c, 0x8fb60058, +0x8fb50054, 0x8fb40050, 0x8fb3004c, 0x8fb20048, +0x8fb10044, 0x8fb00040, 0x3e00008, 0x27bd0068, +0x52843, 0x10a0000d, 0x3021, 0x3c030001, +0x34633800, 0x3c07ffff, 0x3631021, 0x82102b, +0x50400001, 0x872021, 0x94820000, 0x24840002, +0x24a5ffff, 0x14a0fff8, 0xc23021, 0x61c02, +0x30c2ffff, 0x623021, 0x61c02, 0x30c2ffff, +0x623021, 0x3e00008, 0x30c2ffff, 0x27bdff88, +0x240f0001, 0xafbf0070, 0xafbe006c, 0xafb60068, +0xafb50064, 0xafb40060, 0xafb3005c, 0xafb20058, +0xafb10054, 0xafb00050, 0xa3a00027, 0xafaf002c, +0x8ee204d4, 0x8021, 0x30420001, 0x1440002a, +0xa3a00037, 0x8f8700e0, 0x8f8800c4, 0x8f8200e8, +0xe22023, 0x2c821000, 0x50400001, 0x24841000, +0x420c2, 0x801821, 0x8ee400c8, 0x8ee500cc, +0x1021, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xaee400c8, 0xaee500cc, 0x8f8300c8, +0x3c02000a, 0x3442efff, 0x1032023, 0x44102b, +0x10400003, 0x3c02000a, 0x3442f000, 0x822021, +0x801821, 0x8ee400c0, 0x8ee500c4, 0x1021, +0xa32821, 0xa3302b, 0x822021, 0x862021, +0xaee400c0, 0xaee500c4, 0xaf8800c8, 0xaf8700e4, +0x80034cc, 0xaf8700e8, 0x3c020001, 0x571021, +0x904283c0, 0x1040000b, 0x0, 0x3c140001, +0x297a021, 0x8e9483c4, 0x3c130001, 0x2779821, +0x8e7383c8, 0x3c120001, 0x2579021, 0x8003193, +0x8e5283cc, 0x8f8300e0, 0x8f8200e4, 0x10430007, +0x8821, 0x8f8200e4, 0x24110001, 0x8c430000, +0x8c440004, 0xafa30018, 0xafa4001c, 0x1620000e, +0x3c02ffff, 0x8f8200c4, 0xafa20010, 0x8f8200c8, +0x3c040001, 0x24845870, 0xafa20014, 0x8f8600e0, +0x8f8700e4, 0x3c050006, 0xc002403, 0x34a5f000, +0x80034cc, 0x0, 0x8fa3001c, 0x8fb20018, +0x3074ffff, 0x2694fffc, 0x621024, 0x10400058, +0x2409821, 0x3c020080, 0x621024, 0x1040000a, +0x3c040040, 0x8ee2007c, 0x24420001, 0xaee2007c, +0x8ee2007c, 0x8ee201fc, 0x24420001, 0xaee201fc, +0x80034c6, 0x8ee201fc, 0x3c060004, 0x3c0b0001, +0x3c0a0002, 0x3c050010, 0x3c090008, 0x8ee20080, +0x3c080020, 0x34078000, 0x24420001, 0xaee20080, +0x8ee20080, 0x8fa2001c, 0x441824, 0x10660021, +0xc3102b, 0x14400007, 0x0, 0x106b0011, +0x0, 0x106a0015, 0x0, 0x8003049, +0x42042, 0x10650023, 0xa3102b, 0x14400005, +0x0, 0x10690019, 0x0, 0x8003049, +0x42042, 0x10680021, 0x0, 0x8003049, +0x42042, 0x8ee20034, 0x24420001, 0xaee20034, +0x8ee20034, 0x8003049, 0x42042, 0x8ee201ec, +0x24420001, 0xaee201ec, 0x8ee201ec, 0x8003049, +0x42042, 0x8ee201f0, 0x24420001, 0xaee201f0, +0x8ee201f0, 0x8003049, 0x42042, 0x8ee201f4, +0x24420001, 0xaee201f4, 0x8ee201f4, 0x8003049, +0x42042, 0x8ee20030, 0x24420001, 0xaee20030, +0x8ee20030, 0x8003049, 0x42042, 0x8ee201f8, +0x24420001, 0xaee201f8, 0x8ee201f8, 0x42042, +0x1087047c, 0x0, 0x800300e, 0x0, +0x3c020001, 0x571021, 0x904283b2, 0x14400084, +0x24020001, 0x3c030001, 0x771821, 0x906383b3, +0x1462007f, 0x3c020100, 0x8e430000, 0x621024, +0x1040006f, 0x2402ffff, 0x14620005, 0x24100001, +0x96430004, 0x3402ffff, 0x10620075, 0x0, +0x92e204d8, 0x14400072, 0x0, 0x3c020001, +0x571021, 0x8c4283b4, 0x28420005, 0x10400020, +0x3821, 0x3c020001, 0x571021, 0x8c4283b4, +0x18400016, 0x2821, 0x96660000, 0x520c0, +0x971021, 0x9442777e, 0x14460009, 0x971021, +0x94437780, 0x96620002, 0x14620005, 0x971021, +0x94437782, 0x96620004, 0x50620008, 0x24070001, +0x3c020001, 0x571021, 0x8c4283b4, 0x24a50001, +0xa2102a, 0x5440ffee, 0x520c0, 0x30e200ff, +0x10400440, 0x0, 0x80030d5, 0x0, +0x2402021, 0xc0022fe, 0x24050006, 0x3044001f, +0x428c0, 0x2e51021, 0x9442727c, 0x30424000, +0x14400434, 0xb71021, 0x9443727e, 0x96620000, +0x1462000b, 0x418c0, 0xb71021, 0x94437280, +0x96620002, 0x14620006, 0x418c0, 0xb71021, +0x94437282, 0x96620004, 0x10620035, 0x418c0, +0x2e31021, 0x9442727c, 0x30428000, 0x14400421, +0x2e31021, 0x944b727c, 0x96670000, 0xb28c0, +0xb71021, 0x9442737e, 0x80030b7, 0x3021, +0x420c0, 0x2e41021, 0x9443737c, 0x2e41021, +0x944b737c, 0x30638000, 0x14600010, 0xb28c0, +0xb71021, 0x9442737e, 0x1447fff5, 0x1602021, +0xb71021, 0x94437380, 0x96620002, 0x5462fff1, +0x420c0, 0xb71021, 0x94437382, 0x96620004, +0x5462ffec, 0x420c0, 0x24060001, 0x30c200ff, +0x10400400, 0x0, 0x80030d5, 0x0, +0x97430202, 0x96420000, 0x146203fa, 0x0, +0x97430204, 0x96420002, 0x146203f6, 0x0, +0x97430206, 0x96420004, 0x146203f2, 0x0, +0x92420000, 0x3a030001, 0x30420001, 0x431024, +0x10400074, 0x2402ffff, 0x8e630000, 0x14620004, +0x3402ffff, 0x96630004, 0x1062006f, 0x240f0002, +0x3c020001, 0x571021, 0x904283b2, 0x1440006a, +0x240f0003, 0x92e204d8, 0x54400068, 0xafaf002c, +0x3c020001, 0x571021, 0x8c4283b4, 0x28420005, +0x10400020, 0x3821, 0x3c020001, 0x571021, +0x8c4283b4, 0x18400016, 0x2821, 0x96660000, +0x520c0, 0x971021, 0x9442777e, 0x14460009, +0x971021, 0x94437780, 0x96620002, 0x14620005, +0x971021, 0x94437782, 0x96620004, 0x50620008, +0x24070001, 0x3c020001, 0x571021, 0x8c4283b4, +0x24a50001, 0xa2102a, 0x5440ffee, 0x520c0, +0x30e200ff, 0x14400044, 0x240f0003, 0x80034c6, +0x0, 0x2402021, 0xc0022fe, 0x24050006, +0x3044001f, 0x428c0, 0x2e51021, 0x9442727c, +0x30424000, 0x144003af, 0xb71021, 0x9443727e, +0x96620000, 0x1462000b, 0x418c0, 0xb71021, +0x94437280, 0x96620002, 0x14620006, 0x418c0, +0xb71021, 0x94437282, 0x96620004, 0x10620027, +0x418c0, 0x2e31021, 0x9442727c, 0x30428000, +0x1440039c, 0x2e31021, 0x944b727c, 0x96670000, +0xb28c0, 0xb71021, 0x9442737e, 0x800313c, +0x3021, 0x420c0, 0x2e41021, 0x9443737c, +0x2e41021, 0x944b737c, 0x30638000, 0x14600010, +0xb28c0, 0xb71021, 0x9442737e, 0x1447fff5, +0x1602021, 0xb71021, 0x94437380, 0x96620002, +0x5462fff1, 0x420c0, 0xb71021, 0x94437382, +0x96620004, 0x5462ffec, 0x420c0, 0x24060001, +0x30c200ff, 0x1040037b, 0x0, 0x800314f, +0x240f0003, 0x240f0001, 0xafaf002c, 0x8f420260, +0x54102b, 0x1040003a, 0x0, 0x8f8300e4, +0x8f8200e0, 0x10620003, 0x24630008, 0xaf8300e4, +0xaf8300e8, 0x8ee400c0, 0x8ee500c4, 0x2801821, +0x1021, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xaee400c0, 0xaee500c4, 0x8ee20058, +0x24420001, 0xaee20058, 0x8ee20058, 0x8ee2007c, +0x24420001, 0xaee2007c, 0x8ee2007c, 0x8f8200e0, +0xafa20010, 0x8f8200e4, 0x3c040001, 0x24845878, +0xafa20014, 0x8fa60018, 0x8fa7001c, 0x3c050006, +0xc002403, 0x34a5f003, 0x80034cc, 0x0, +0x8ee25240, 0xafa20010, 0x8ee25244, 0x3c040001, +0x24845884, 0xafa20014, 0x8ee60e10, 0x8ee70e18, +0x3c050006, 0xc002403, 0x34a5f002, 0x8ee201c0, +0x24420001, 0xaee201c0, 0x8ee20000, 0x8ee301c0, +0x2403ffbf, 0x431024, 0x8003470, 0xaee20000, +0x96e20468, 0x54102b, 0x10400003, 0x0, +0x240f0001, 0xa3af0027, 0x12800301, 0x24160007, +0x24150040, 0x241e0001, 0x240e0012, 0x8ee2724c, +0x8f430280, 0x24420001, 0x304207ff, 0x106202d3, +0x0, 0x93a20027, 0x10400014, 0x0, +0x8ee35240, 0x8ee25244, 0x10620009, 0x26ed5244, +0x8ee65244, 0x8ee35244, 0x21140, 0x24425248, +0x2e28021, 0x24630001, 0x80031bf, 0x306b00ff, +0x92e27248, 0x1440ffca, 0x0, 0x8ee201e0, +0x24420001, 0xaee201e0, 0x8ee201e0, 0x8ee30e10, +0x8ee20e18, 0x1062ffc2, 0x26ed0e18, 0x8ee60e18, +0x8ee30e18, 0x21140, 0x24420e20, 0x2e28021, +0x24630001, 0x306b01ff, 0x96e2046a, 0x30420010, +0x10400019, 0x0, 0x9642000c, 0x340f8100, +0x144f0015, 0x0, 0x3c020001, 0x571021, +0x904283c0, 0x14400010, 0x0, 0x9642000e, +0xa6020016, 0x8e420008, 0x8e430004, 0x8e440000, +0x2694fffc, 0xae42000c, 0xae430008, 0xae440004, +0x9602000e, 0x26730004, 0x240f0001, 0xa3af0037, +0x34420200, 0xa602000e, 0x8e020000, 0x8e030004, +0x3c040001, 0x34843800, 0x306a0007, 0x26a9823, +0x3641021, 0x262102b, 0x10400005, 0x28aa021, +0x2641023, 0x3621823, 0x3c020020, 0x439823, +0x26820007, 0x2404fff8, 0x9603000a, 0x446024, +0x6a1821, 0x6c102b, 0x10400002, 0x1803821, +0x603821, 0xae130018, 0x8f880120, 0x24e20007, +0x443824, 0x27623800, 0x25090020, 0x122102b, +0x50400001, 0x27693000, 0x8f820128, 0x11220004, +0x0, 0x8f820124, 0x15220007, 0x1401821, +0x8ee201a4, 0x8821, 0x24420001, 0xaee201a4, +0x800324c, 0x8ee201a4, 0x8e040000, 0x8e050004, +0x1021, 0xad130008, 0xa507000e, 0xad160018, +0xad06001c, 0xa3302b, 0xa32823, 0x822023, +0x862023, 0xad040000, 0xad050004, 0x8ee204c0, +0xad020010, 0xaf890120, 0x92e24e20, 0x14400033, +0x24110001, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0x8c820000, 0x1456001f, 0x0, +0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x0, +0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, +0x8ee34e30, 0x24420001, 0x10550007, 0x0, +0x8ee24e34, 0x24420001, 0x10620005, 0x0, +0x8003239, 0x0, 0x14600005, 0x0, +0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, +0x8c820004, 0x2c420011, 0x50400010, 0xac800000, +0x800324c, 0x0, 0x8ee24e30, 0x24420001, +0x50550003, 0x1021, 0x8ee24e30, 0x24420001, +0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0xac960000, 0xac9e0004, 0x16200018, +0x3c050006, 0x8e020018, 0x3c040001, 0x24845890, +0xafa20010, 0x8e020000, 0x8e030004, 0x34a5f009, +0x2003021, 0xc002403, 0xafa30014, 0x93a20037, +0x10400216, 0x340f8100, 0x8e420004, 0x8e430008, +0x8e44000c, 0xa64f000c, 0xae420000, 0xae430004, +0xae440008, 0x96020016, 0x8003470, 0xa642000e, +0x14ec0168, 0x28a1823, 0x960c000a, 0x9603000e, +0x28a1023, 0xa602000a, 0x34620004, 0xa602000e, +0x8f880120, 0x27623800, 0x25090020, 0x122102b, +0x14400002, 0x306affff, 0x27693000, 0x8f820128, +0x11220004, 0x0, 0x8f820124, 0x15220007, +0x24040020, 0x8ee201a4, 0x8821, 0x24420001, +0xaee201a4, 0x80032ca, 0x8ee201a4, 0x8ee5724c, +0x8ee60490, 0x8ee70494, 0xa504000e, 0x24040004, +0xad100008, 0xad040018, 0x52940, 0xa01821, +0x1021, 0xe33821, 0xe3202b, 0xc23021, +0xc43021, 0xad060000, 0xad070004, 0x8ee2724c, +0xad02001c, 0x8ee204c4, 0xad020010, 0xaf890120, +0x92e24e20, 0x14400033, 0x24110001, 0x8ee24e30, +0x210c0, 0x24425038, 0x2e22021, 0x8c820000, +0x1456001f, 0x0, 0x8ee34e30, 0x8ee24e34, +0x1062001b, 0x0, 0x8c820004, 0x24420001, +0xac820004, 0x8ee24e34, 0x8ee34e30, 0x24420001, +0x10550007, 0x0, 0x8ee24e34, 0x24420001, +0x10620005, 0x0, 0x80032b7, 0x0, +0x14600005, 0x0, 0x8f820128, 0x24420020, +0xaf820128, 0x8f820128, 0x8c820004, 0x2c420011, +0x50400010, 0xac800000, 0x80032ca, 0x0, +0x8ee24e30, 0x24420001, 0x50550003, 0x1021, +0x8ee24e30, 0x24420001, 0xaee24e30, 0x8ee24e30, +0x210c0, 0x24425038, 0x2e22021, 0xac960000, +0xac9e0004, 0x1620000d, 0x0, 0xa60c000a, +0xa60a000e, 0x8f820100, 0xafa20010, 0x8f820104, +0x3c040001, 0x2484589c, 0x3c050006, 0xafa20014, +0x8ee6724c, 0x800343b, 0x34a5f00b, 0x3c010001, +0x370821, 0xa02083c0, 0xadab0000, 0x8ee201d8, +0x8ee3724c, 0x2442ffff, 0xaee201d8, 0x8ee201d8, +0x24630001, 0x306307ff, 0x26e25244, 0x15a20006, +0xaee3724c, 0x8ee201d0, 0x2442ffff, 0xaee201d0, +0x80032ef, 0x8ee201d0, 0x8ee201cc, 0x2442ffff, +0xaee201cc, 0x8ee201cc, 0x8f420240, 0x10400073, +0x0, 0x8ee20e1c, 0x24420001, 0xaee20e1c, +0x8f430240, 0x43102b, 0x14400176, 0xa021, +0x8f830120, 0x27623800, 0x24660020, 0xc2102b, +0x50400001, 0x27663000, 0x8f820128, 0x10c20004, +0x0, 0x8f820124, 0x14c20007, 0x0, +0x8ee201a4, 0x8821, 0x24420001, 0xaee201a4, +0x800334f, 0x8ee201a4, 0x8ee2724c, 0xac62001c, +0x8ee404a8, 0x8ee504ac, 0x2462001c, 0xac620008, +0x24020008, 0xa462000e, 0x24020011, 0xac620018, +0xac640000, 0xac650004, 0x8ee204c4, 0xac620010, +0xaf860120, 0x92e24e20, 0x14400033, 0x24110001, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x8c820000, 0x144e001f, 0x0, 0x8ee34e30, +0x8ee24e34, 0x1062001b, 0x0, 0x8c820004, +0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, +0x24420001, 0x10550007, 0x0, 0x8ee24e34, +0x24420001, 0x10620005, 0x0, 0x800333c, +0x0, 0x14600005, 0x0, 0x8f820128, +0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, +0x2c420011, 0x50400010, 0xac800000, 0x800334f, +0x0, 0x8ee24e30, 0x24420001, 0x50550003, +0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0xac8e0000, 0xac9e0004, 0x5620000d, 0x24110001, +0x8ee2724c, 0x3c040001, 0x248458a8, 0xafa00014, +0xafa20010, 0x8ee6724c, 0x8f470280, 0x3c050009, +0x34a5f008, 0xc002403, 0xafae0048, 0x8fae0048, +0x56200001, 0xaee00e1c, 0x8ee20188, 0x24420001, +0xaee20188, 0x80033c8, 0x8ee20188, 0x8f830120, +0x27623800, 0x24660020, 0xc2102b, 0x50400001, +0x27663000, 0x8f820128, 0x10c20004, 0x0, +0x8f820124, 0x14c20007, 0x0, 0x8ee201a4, +0x8821, 0x24420001, 0xaee201a4, 0x80033ba, +0x8ee201a4, 0x8ee2724c, 0xac62001c, 0x8ee404a8, +0x8ee504ac, 0x2462001c, 0xac620008, 0x24020008, +0xa462000e, 0x24020011, 0xac620018, 0xac640000, +0xac650004, 0x8ee204c4, 0xac620010, 0xaf860120, +0x92e24e20, 0x14400033, 0x24110001, 0x8ee24e30, +0x210c0, 0x24425038, 0x2e22021, 0x8c820000, +0x144e001f, 0x0, 0x8ee34e30, 0x8ee24e34, +0x1062001b, 0x0, 0x8c820004, 0x24420001, +0xac820004, 0x8ee24e34, 0x8ee34e30, 0x24420001, +0x10550007, 0x0, 0x8ee24e34, 0x24420001, +0x10620005, 0x0, 0x80033a7, 0x0, +0x14600005, 0x0, 0x8f820128, 0x24420020, +0xaf820128, 0x8f820128, 0x8c820004, 0x2c420011, +0x50400010, 0xac800000, 0x80033ba, 0x0, +0x8ee24e30, 0x24420001, 0x50550003, 0x1021, +0x8ee24e30, 0x24420001, 0xaee24e30, 0x8ee24e30, +0x210c0, 0x24425038, 0x2e22021, 0xac8e0000, +0xac9e0004, 0x1620000d, 0x0, 0x8ee2724c, +0x3c040001, 0x248458a8, 0xafa00014, 0xafa20010, +0x8ee6724c, 0x8f470280, 0x3c050009, 0x34a5f008, +0xc002403, 0xafae0048, 0x8fae0048, 0x8ee20174, +0x24420001, 0xaee20174, 0x8ee20174, 0x800346e, +0xa021, 0x960c000a, 0x183102b, 0x54400001, +0x1801821, 0xa603000a, 0x8f880120, 0x27623800, +0x25090020, 0x122102b, 0x50400001, 0x27693000, +0x8f820128, 0x11220004, 0x0, 0x8f820124, +0x15220007, 0x24040020, 0x8ee201a4, 0x8821, +0x24420001, 0xaee201a4, 0x800342f, 0x8ee201a4, +0x8ee5724c, 0x8ee60490, 0x8ee70494, 0xa504000e, +0x24040004, 0xad100008, 0xad040018, 0x52940, +0xa01821, 0x1021, 0xe33821, 0xe3202b, +0xc23021, 0xc43021, 0xad060000, 0xad070004, +0x8ee2724c, 0xad02001c, 0x8ee204c4, 0xad020010, +0xaf890120, 0x92e24e20, 0x14400033, 0x24110001, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x8c820000, 0x1456001f, 0x0, 0x8ee34e30, +0x8ee24e34, 0x1062001b, 0x0, 0x8c820004, +0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, +0x24420001, 0x10550007, 0x0, 0x8ee24e34, +0x24420001, 0x10620005, 0x0, 0x800341c, +0x0, 0x14600005, 0x0, 0x8f820128, +0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, +0x2c420011, 0x50400010, 0xac800000, 0x800342f, +0x0, 0x8ee24e30, 0x24420001, 0x50550003, +0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0xac960000, 0xac9e0004, 0x1620001d, 0x0, +0xa60c000a, 0x8f820100, 0xafa20010, 0x8f820104, +0x3c040001, 0x2484589c, 0x3c050006, 0xafa20014, +0x8ee6724c, 0x34a5f00d, 0xc002403, 0x2003821, +0x93a20037, 0x10400031, 0x340f8100, 0x8e420004, +0x8e430008, 0x8e44000c, 0xa64f000c, 0xae420000, +0xae430004, 0xae440008, 0x96020016, 0xa642000e, +0x9602000e, 0x3042fdff, 0x8003470, 0xa602000e, +0x8ee201d8, 0x2442ffff, 0xaee201d8, 0x8ee201d8, +0x8ee201cc, 0x3c04001f, 0x3c010001, 0x370821, +0xa03e83c0, 0x2442ffff, 0xaee201cc, 0x9603000a, +0x3484ffff, 0x8ee201cc, 0x6a1821, 0x2639821, +0x93202b, 0x10800003, 0x3c02fff5, 0x34421000, +0x2629821, 0xadab0000, 0x8ee2724c, 0x24420001, +0x304207ff, 0xaee2724c, 0x8f420240, 0x10400004, +0x283a023, 0x8ee20e1c, 0x24420001, 0xaee20e1c, +0xa3a00027, 0x1680fd29, 0x0, 0x12800024, +0x0, 0x3c010001, 0x370821, 0xac3483c4, +0x3c010001, 0x370821, 0xac3383c8, 0x3c010001, +0x370821, 0xac3283cc, 0x93a20037, 0x10400008, +0x0, 0x3c020001, 0x571021, 0x8c4283cc, +0x24420004, 0x3c010001, 0x370821, 0xac2283cc, +0x8ee2724c, 0x8f430280, 0x24420001, 0x304207ff, +0x14620006, 0x0, 0x8ee201c4, 0x24420001, +0xaee201c4, 0x80034cc, 0x8ee201c4, 0x8ee201bc, +0x24420001, 0xaee201bc, 0x80034cc, 0x8ee201bc, +0x97a4001e, 0x2484fffc, 0x801821, 0x8ee400c0, +0x8ee500c4, 0x1021, 0xa32821, 0xa3302b, +0x822021, 0x862021, 0xaee400c0, 0xaee500c4, +0x8faf002c, 0x24020002, 0x11e2000f, 0x29e20003, +0x14400017, 0x24020003, 0x15e20015, 0x0, +0x8ee200d0, 0x8ee300d4, 0x24630001, 0x2c640001, +0x441021, 0xaee200d0, 0xaee300d4, 0x8ee200d0, +0x80034c6, 0x8ee300d4, 0x8ee200d8, 0x8ee300dc, +0x24630001, 0x2c640001, 0x441021, 0xaee200d8, +0xaee300dc, 0x8ee200d8, 0x80034c6, 0x8ee300dc, +0x8ee200c8, 0x8ee300cc, 0x24630001, 0x2c640001, +0x441021, 0xaee200c8, 0xaee300cc, 0x8ee200c8, +0x8ee300cc, 0x8f8300e4, 0x8f8200e0, 0x10620003, +0x24630008, 0xaf8300e4, 0xaf8300e8, 0x8fbf0070, +0x8fbe006c, 0x8fb60068, 0x8fb50064, 0x8fb40060, +0x8fb3005c, 0x8fb20058, 0x8fb10054, 0x8fb00050, +0x3e00008, 0x27bd0078, 0x27bdffb0, 0xafb50044, +0xa821, 0xafb00030, 0x8021, 0xafbf004c, +0xafb60048, 0xafb40040, 0xafb3003c, 0xafb20038, +0xafb10034, 0x8ee204d4, 0x24140001, 0x30420001, +0x1440002a, 0xb021, 0x8f8700e0, 0x8f8800c4, +0x8f8200e8, 0xe22023, 0x2c821000, 0x50400001, +0x24841000, 0x420c2, 0x801821, 0x8ee400c8, +0x8ee500cc, 0x1021, 0xa32821, 0xa3302b, +0x822021, 0x862021, 0xaee400c8, 0xaee500cc, +0x8f8300c8, 0x3c02000a, 0x3442efff, 0x1032023, +0x44102b, 0x10400003, 0x3c02000a, 0x3442f000, +0x822021, 0x801821, 0x8ee400c0, 0x8ee500c4, +0x1021, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xaee400c0, 0xaee500c4, 0xaf8800c8, +0xaf8700e4, 0x8003850, 0xaf8700e8, 0x3c020001, +0x571021, 0x904283c0, 0x1040000b, 0x0, +0x3c130001, 0x2779821, 0x8e7383c4, 0x3c110001, +0x2378821, 0x8e3183c8, 0x3c120001, 0x2579021, +0x80036e8, 0x8e5283cc, 0x8f8300e0, 0x8f8200e4, +0x10430007, 0x4821, 0x8f8200e4, 0x24090001, +0x8c430000, 0x8c440004, 0xafa30018, 0xafa4001c, +0x1520000e, 0x3c02ffff, 0x8f8200c4, 0xafa20010, +0x8f8200c8, 0x3c040001, 0x24845870, 0xafa20014, +0x8f8600e0, 0x8f8700e4, 0x3c050006, 0xc002403, +0x34a5f000, 0x8003850, 0x0, 0x8fa3001c, +0x8fb20018, 0x3073ffff, 0x2673fffc, 0x621024, +0x10400058, 0x2408821, 0x3c020080, 0x621024, +0x1040000a, 0x3c040040, 0x8ee2007c, 0x24420001, +0xaee2007c, 0x8ee2007c, 0x8ee201fc, 0x24420001, +0xaee201fc, 0x800384a, 0x8ee201fc, 0x3c060004, +0x3c0b0001, 0x3c0a0002, 0x3c050010, 0x3c090008, +0x8ee20080, 0x3c080020, 0x34078000, 0x24420001, +0xaee20080, 0x8ee20080, 0x8fa2001c, 0x441824, +0x10660021, 0xc3102b, 0x14400007, 0x0, +0x106b0011, 0x0, 0x106a0015, 0x0, +0x8003592, 0x42042, 0x10650023, 0xa3102b, +0x14400005, 0x0, 0x10690019, 0x0, +0x8003592, 0x42042, 0x10680021, 0x0, +0x8003592, 0x42042, 0x8ee20034, 0x24420001, +0xaee20034, 0x8ee20034, 0x8003592, 0x42042, +0x8ee201ec, 0x24420001, 0xaee201ec, 0x8ee201ec, +0x8003592, 0x42042, 0x8ee201f0, 0x24420001, +0xaee201f0, 0x8ee201f0, 0x8003592, 0x42042, +0x8ee201f4, 0x24420001, 0xaee201f4, 0x8ee201f4, +0x8003592, 0x42042, 0x8ee20030, 0x24420001, +0xaee20030, 0x8ee20030, 0x8003592, 0x42042, +0x8ee201f8, 0x24420001, 0xaee201f8, 0x8ee201f8, +0x42042, 0x108702b7, 0x0, 0x8003557, +0x0, 0x3c020001, 0x571021, 0x904283b2, +0x14400084, 0x24020001, 0x3c030001, 0x771821, +0x906383b3, 0x1462007f, 0x3c020100, 0x8e430000, +0x621024, 0x1040006f, 0x2402ffff, 0x14620005, +0x24100001, 0x96430004, 0x3402ffff, 0x10620075, +0x0, 0x92e204d8, 0x14400072, 0x0, +0x3c020001, 0x571021, 0x8c4283b4, 0x28420005, +0x10400020, 0x3821, 0x3c020001, 0x571021, +0x8c4283b4, 0x18400016, 0x2821, 0x96260000, +0x520c0, 0x971021, 0x9442777e, 0x14460009, +0x971021, 0x94437780, 0x96220002, 0x14620005, +0x971021, 0x94437782, 0x96220004, 0x50620008, +0x24070001, 0x3c020001, 0x571021, 0x8c4283b4, +0x24a50001, 0xa2102a, 0x5440ffee, 0x520c0, +0x30e200ff, 0x1040027b, 0x0, 0x800361e, +0x0, 0x2402021, 0xc0022fe, 0x24050006, +0x3044001f, 0x428c0, 0x2e51021, 0x9442727c, +0x30424000, 0x1440026f, 0xb71021, 0x9443727e, +0x96220000, 0x1462000b, 0x418c0, 0xb71021, +0x94437280, 0x96220002, 0x14620006, 0x418c0, +0xb71021, 0x94437282, 0x96220004, 0x10620035, +0x418c0, 0x2e31021, 0x9442727c, 0x30428000, +0x1440025c, 0x2e31021, 0x9448727c, 0x96270000, +0x828c0, 0xb71021, 0x9442737e, 0x8003600, +0x3021, 0x420c0, 0x2e41021, 0x9443737c, +0x2e41021, 0x9448737c, 0x30638000, 0x14600010, +0x828c0, 0xb71021, 0x9442737e, 0x1447fff5, +0x1002021, 0xb71021, 0x94437380, 0x96220002, +0x5462fff1, 0x420c0, 0xb71021, 0x94437382, +0x96220004, 0x5462ffec, 0x420c0, 0x24060001, +0x30c200ff, 0x1040023b, 0x0, 0x800361e, +0x0, 0x97430202, 0x96420000, 0x14620235, +0x0, 0x97430204, 0x96420002, 0x14620231, +0x0, 0x97430206, 0x96420004, 0x1462022d, +0x0, 0x92420000, 0x3a030001, 0x30420001, +0x431024, 0x10400074, 0x2402ffff, 0x8e230000, +0x14620004, 0x3402ffff, 0x96230004, 0x1062006f, +0x24140002, 0x3c020001, 0x571021, 0x904283b2, +0x1440006a, 0x24140003, 0x92e204d8, 0x14400067, +0x0, 0x3c020001, 0x571021, 0x8c4283b4, +0x28420005, 0x10400020, 0x3821, 0x3c020001, +0x571021, 0x8c4283b4, 0x18400016, 0x2821, +0x96260000, 0x520c0, 0x971021, 0x9442777e, +0x14460009, 0x971021, 0x94437780, 0x96220002, +0x14620005, 0x971021, 0x94437782, 0x96220004, +0x50620008, 0x24070001, 0x3c020001, 0x571021, +0x8c4283b4, 0x24a50001, 0xa2102a, 0x5440ffee, +0x520c0, 0x30e200ff, 0x14400044, 0x24140003, +0x800384a, 0x0, 0x2402021, 0xc0022fe, +0x24050006, 0x3044001f, 0x428c0, 0x2e51021, +0x9442727c, 0x30424000, 0x144001ea, 0xb71021, +0x9443727e, 0x96220000, 0x1462000b, 0x418c0, +0xb71021, 0x94437280, 0x96220002, 0x14620006, +0x418c0, 0xb71021, 0x94437282, 0x96220004, +0x10620027, 0x418c0, 0x2e31021, 0x9442727c, +0x30428000, 0x144001d7, 0x2e31021, 0x9448727c, +0x96270000, 0x828c0, 0xb71021, 0x9442737e, +0x8003685, 0x3021, 0x420c0, 0x2e41021, +0x9443737c, 0x2e41021, 0x9448737c, 0x30638000, +0x14600010, 0x828c0, 0xb71021, 0x9442737e, +0x1447fff5, 0x1002021, 0xb71021, 0x94437380, +0x96220002, 0x5462fff1, 0x420c0, 0xb71021, +0x94437382, 0x96220004, 0x5462ffec, 0x420c0, +0x24060001, 0x30c200ff, 0x104001b6, 0x0, +0x8003698, 0x24140003, 0x24140001, 0x8f420260, +0x53102b, 0x10400049, 0x0, 0x8f8300e4, +0x8f8200e0, 0x10620003, 0x24630008, 0xaf8300e4, +0xaf8300e8, 0x8ee400c0, 0x8ee500c4, 0x2601821, +0x1021, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xaee400c0, 0xaee500c4, 0x8ee20058, +0x24420001, 0xaee20058, 0x8ee20058, 0x8ee2007c, +0x24420001, 0xaee2007c, 0x8ee2007c, 0x8f8200e0, +0xafa20010, 0x8f8200e4, 0x3c040001, 0x24845878, +0xafa20014, 0x8fa60018, 0x8fa7001c, 0x3c050006, +0xc002403, 0x34a5f003, 0x8003850, 0x0, +0x8ee25240, 0xafa20010, 0x8ee25244, 0x3c040001, +0x24845884, 0xafa20014, 0x8ee60e10, 0x8ee70e18, +0xc002403, 0x34a5f002, 0x8ee201c0, 0x24420001, +0xaee201c0, 0x8ee20000, 0x8ee301c0, 0x2403ffbf, +0x431024, 0x80037f8, 0xaee20000, 0x8ee25240, +0xafa20010, 0x8ee25244, 0x3c040001, 0x24845884, +0xafa20014, 0x8ee60e10, 0x8ee70e18, 0x3c050006, +0xc002403, 0x34a5f002, 0x8ee201c0, 0x24420001, +0xaee201c0, 0x80037f8, 0x8ee201c0, 0x96e20468, +0x53102b, 0x54400001, 0x3c158000, 0x12600131, +0x3c0c001f, 0x358cffff, 0x8ee2724c, 0x8f430280, +0x24420001, 0x304207ff, 0x10620108, 0x0, +0x12a00014, 0x0, 0x8ee35240, 0x8ee25244, +0x10620009, 0x26ee5244, 0x8eeb5244, 0x8ee35244, +0x21140, 0x24425248, 0x2e28021, 0x24630001, +0x8003712, 0x306800ff, 0x92e27248, 0x1440ffc0, +0x3c050006, 0x8ee201e0, 0x24420001, 0xaee201e0, +0x8ee201e0, 0x8ee30e10, 0x8ee20e18, 0x1062ffcb, +0x26ee0e18, 0x8eeb0e18, 0xa821, 0x8ee30e18, +0x21140, 0x24420e20, 0x2e28021, 0x24630001, +0x306801ff, 0x96e2046a, 0x30420010, 0x10400017, +0x34028100, 0x9643000c, 0x14620014, 0x0, +0x3c020001, 0x571021, 0x904283c0, 0x1440000f, +0x0, 0x9642000e, 0xa6020016, 0x8e420008, +0x8e430004, 0x8e440000, 0x2673fffc, 0xae42000c, +0xae430008, 0xae440004, 0x9602000e, 0x26310004, +0x24160001, 0x34420200, 0xa602000e, 0x9603000a, +0x2605021, 0x73102b, 0x10400002, 0x2606821, +0x605021, 0x2d42003d, 0x1040002a, 0x3821, +0x9623000c, 0x24020800, 0x54620027, 0xae110018, +0x3c020001, 0x571021, 0x904283c0, 0x54400022, +0xae110018, 0x26220017, 0x182102b, 0x10400013, +0x0, 0x3c02fff5, 0x511021, 0x90421017, +0x38430006, 0x2c630001, 0x38420011, 0x2c420001, +0x621825, 0x10600013, 0x26220010, 0x182102b, +0x1040000e, 0x0, 0x3c07fff5, 0xf13821, +0x94e71010, 0x800375e, 0x24e7000e, 0x92220017, +0x38430006, 0x2c630001, 0x38420011, 0x2c420001, +0x621825, 0x50600004, 0xae110018, 0x96270010, +0x24e7000e, 0xae110018, 0x3c020001, 0x571021, +0x904283c0, 0x2102b, 0x14e00002, 0x24ec0, +0x1403821, 0x8f830120, 0x27623800, 0x24660020, +0xc2102b, 0x50400001, 0x27663000, 0x8f820128, +0x10c20004, 0x0, 0x8f820124, 0x14c20007, +0x2402000b, 0x8ee201a4, 0x4821, 0x24420001, +0xaee201a4, 0x80037bf, 0x8ee201a4, 0x8e040000, +0x8e050004, 0xac620018, 0x1751025, 0x491025, +0xac710008, 0xa467000e, 0xac62001c, 0xac640000, +0xac650004, 0x8ee204c0, 0xac620010, 0xaf860120, +0x92e24e20, 0x14400038, 0x24090001, 0x8ee24e30, +0x210c0, 0x24425038, 0x2e22021, 0x8c830000, +0x24020007, 0x14620020, 0x0, 0x8ee34e30, +0x8ee24e34, 0x1062001c, 0x0, 0x8c820004, +0x24420001, 0xac820004, 0x8ee34e34, 0x8ee54e30, +0x24020040, 0x24630001, 0x10620007, 0x0, +0x8ee24e34, 0x24420001, 0x10a20005, 0x0, +0x80037a9, 0x0, 0x14a00005, 0x0, +0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, +0x8c820004, 0x2c420011, 0x50400013, 0xac800000, +0x80037bf, 0x0, 0x8ee24e30, 0x24030040, +0x24420001, 0x50430003, 0x1021, 0x8ee24e30, +0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x24020007, 0xac820000, +0x24020001, 0xac820004, 0x15200018, 0x3c050006, +0x8e020018, 0x3c040001, 0x24845890, 0xafa20010, +0x8e020000, 0x8e030004, 0x34a5f009, 0x2003021, +0xc002403, 0xafa30014, 0x32c200ff, 0x1040002b, +0x34028100, 0x8e430004, 0x8e440008, 0x8e45000c, +0xa642000c, 0xae430000, 0xae440004, 0xae450008, +0x96020016, 0x80037f8, 0xa642000e, 0x154d000a, +0x0, 0x9602000e, 0xa613000a, 0x34420004, +0xa602000e, 0x3c010001, 0x370821, 0xa02083c0, +0x80037f6, 0x9821, 0x9604000a, 0x93102b, +0x10400002, 0x2601821, 0x801821, 0x24020001, +0xa603000a, 0x3c010001, 0x370821, 0xa02283c0, +0x9604000a, 0x2248821, 0x191102b, 0x10400003, +0x3c02fff5, 0x34421000, 0x2228821, 0x2649823, +0xa821, 0x1660fef4, 0xadc80000, 0x12600021, +0x32c200ff, 0x3c010001, 0x370821, 0xac3383c4, +0x3c010001, 0x370821, 0xac3183c8, 0x3c010001, +0x370821, 0x10400008, 0xac3283cc, 0x3c020001, +0x571021, 0x8c4283cc, 0x24420004, 0x3c010001, +0x370821, 0xac2283cc, 0x8ee2724c, 0x8f430280, +0x24420001, 0x14620006, 0x0, 0x8ee201c4, +0x24420001, 0xaee201c4, 0x8003850, 0x8ee201c4, +0x8ee201bc, 0x24420001, 0xaee201bc, 0x8003850, +0x8ee201bc, 0x97a4001e, 0x2484fffc, 0x801821, +0x8ee400c0, 0x8ee500c4, 0x1021, 0xa32821, +0xa3302b, 0x822021, 0x862021, 0x24020002, +0xaee400c0, 0xaee500c4, 0x1282000f, 0x2a820003, +0x14400017, 0x24020003, 0x16820015, 0x0, +0x8ee200d0, 0x8ee300d4, 0x24630001, 0x2c640001, +0x441021, 0xaee200d0, 0xaee300d4, 0x8ee200d0, +0x800384a, 0x8ee300d4, 0x8ee200d8, 0x8ee300dc, +0x24630001, 0x2c640001, 0x441021, 0xaee200d8, +0xaee300dc, 0x8ee200d8, 0x800384a, 0x8ee300dc, +0x8ee200c8, 0x8ee300cc, 0x24630001, 0x2c640001, +0x441021, 0xaee200c8, 0xaee300cc, 0x8ee200c8, +0x8ee300cc, 0x8f8300e4, 0x8f8200e0, 0x10620003, +0x24630008, 0xaf8300e4, 0xaf8300e8, 0x8fbf004c, +0x8fb60048, 0x8fb50044, 0x8fb40040, 0x8fb3003c, +0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, +0x27bd0050, 0x27bdff90, 0xafb60060, 0xb021, +0xafbf0068, 0xafbe0064, 0xafb5005c, 0xafb40058, +0xafb30054, 0xafb20050, 0xafb1004c, 0xafb00048, +0x8ee204d4, 0x8821, 0x24150001, 0x30420001, +0x1440002a, 0xa3a0002f, 0x8f8700e0, 0x8f8800c4, +0x8f8200e8, 0xe22023, 0x2c821000, 0x50400001, +0x24841000, 0x420c2, 0x801821, 0x8ee400c8, +0x8ee500cc, 0x1021, 0xa32821, 0xa3302b, +0x822021, 0x862021, 0xaee400c8, 0xaee500cc, +0x8f8300c8, 0x3c02000a, 0x3442efff, 0x1032023, +0x44102b, 0x10400003, 0x3c02000a, 0x3442f000, +0x822021, 0x801821, 0x8ee400c0, 0x8ee500c4, +0x1021, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xaee400c0, 0xaee500c4, 0xaf8800c8, +0xaf8700e4, 0x8003c5b, 0xaf8700e8, 0x3c020001, +0x571021, 0x904283c0, 0x1040000b, 0x0, +0x3c130001, 0x2779821, 0x8e7383c4, 0x3c100001, +0x2178021, 0x8e1083c8, 0x3c120001, 0x2579021, +0x8003a59, 0x8e5283cc, 0x8f8300e0, 0x8f8200e4, +0x10430007, 0x3821, 0x8f8200e4, 0x24070001, +0x8c430000, 0x8c440004, 0xafa30018, 0xafa4001c, +0x14e0000e, 0x3c02ffff, 0x8f8200c4, 0xafa20010, +0x8f8200c8, 0x3c040001, 0x248458b4, 0xafa20014, +0x8f8600e0, 0x8f8700e4, 0x3c050006, 0xc002403, +0x34a5f200, 0x8003c5b, 0x0, 0x8fa3001c, +0x8fb20018, 0x3073ffff, 0x2673fffc, 0x621024, +0x10400058, 0x2408021, 0x3c020080, 0x621024, +0x1040000a, 0x3c040040, 0x8ee2007c, 0x24420001, +0xaee2007c, 0x8ee2007c, 0x8ee201fc, 0x24420001, +0xaee201fc, 0x8003c55, 0x8ee201fc, 0x3c060004, +0x3c0b0001, 0x3c0a0002, 0x3c050010, 0x3c090008, +0x8ee20080, 0x3c080020, 0x34078000, 0x24420001, +0xaee20080, 0x8ee20080, 0x8fa2001c, 0x441824, +0x10660021, 0xc3102b, 0x14400007, 0x0, +0x106b0011, 0x0, 0x106a0015, 0x0, +0x8003916, 0x42042, 0x10650023, 0xa3102b, +0x14400005, 0x0, 0x10690019, 0x0, +0x8003916, 0x42042, 0x10680021, 0x0, +0x8003916, 0x42042, 0x8ee20034, 0x24420001, +0xaee20034, 0x8ee20034, 0x8003916, 0x42042, +0x8ee201ec, 0x24420001, 0xaee201ec, 0x8ee201ec, +0x8003916, 0x42042, 0x8ee201f0, 0x24420001, +0xaee201f0, 0x8ee201f0, 0x8003916, 0x42042, +0x8ee201f4, 0x24420001, 0xaee201f4, 0x8ee201f4, +0x8003916, 0x42042, 0x8ee20030, 0x24420001, +0xaee20030, 0x8ee20030, 0x8003916, 0x42042, +0x8ee201f8, 0x24420001, 0xaee201f8, 0x8ee201f8, +0x42042, 0x1087033e, 0x0, 0x80038db, +0x0, 0x3c020001, 0x571021, 0x904283b2, +0x14400084, 0x24020001, 0x3c030001, 0x771821, +0x906383b3, 0x1462007f, 0x3c020100, 0x8e430000, +0x621024, 0x1040006f, 0x2402ffff, 0x14620005, +0x24110001, 0x96430004, 0x3402ffff, 0x10620075, +0x0, 0x92e204d8, 0x14400072, 0x0, +0x3c020001, 0x571021, 0x8c4283b4, 0x28420005, +0x10400020, 0x3821, 0x3c020001, 0x571021, +0x8c4283b4, 0x18400016, 0x2821, 0x96060000, +0x520c0, 0x971021, 0x9442777e, 0x14460009, +0x971021, 0x94437780, 0x96020002, 0x14620005, +0x971021, 0x94437782, 0x96020004, 0x50620008, +0x24070001, 0x3c020001, 0x571021, 0x8c4283b4, +0x24a50001, 0xa2102a, 0x5440ffee, 0x520c0, +0x30e200ff, 0x10400302, 0x0, 0x80039a2, +0x0, 0x2402021, 0xc0022fe, 0x24050006, +0x3044001f, 0x428c0, 0x2e51021, 0x9442727c, +0x30424000, 0x144002f6, 0xb71021, 0x9443727e, +0x96020000, 0x1462000b, 0x418c0, 0xb71021, +0x94437280, 0x96020002, 0x14620006, 0x418c0, +0xb71021, 0x94437282, 0x96020004, 0x10620035, +0x418c0, 0x2e31021, 0x9442727c, 0x30428000, +0x144002e3, 0x2e31021, 0x944d727c, 0x96070000, +0xd28c0, 0xb71021, 0x9442737e, 0x8003984, +0x3021, 0x420c0, 0x2e41021, 0x9443737c, +0x2e41021, 0x944d737c, 0x30638000, 0x14600010, +0xd28c0, 0xb71021, 0x9442737e, 0x1447fff5, +0x1a02021, 0xb71021, 0x94437380, 0x96020002, +0x5462fff1, 0x420c0, 0xb71021, 0x94437382, +0x96020004, 0x5462ffec, 0x420c0, 0x24060001, +0x30c200ff, 0x104002c2, 0x0, 0x80039a2, +0x0, 0x97430202, 0x96420000, 0x146202bc, +0x0, 0x97430204, 0x96420002, 0x146202b8, +0x0, 0x97430206, 0x96420004, 0x146202b4, +0x0, 0x92420000, 0x3a230001, 0x30420001, +0x431024, 0x10400074, 0x2402ffff, 0x8e030000, +0x14620004, 0x3402ffff, 0x96030004, 0x1062006f, +0x24150002, 0x3c020001, 0x571021, 0x904283b2, +0x1440006a, 0x24150003, 0x92e204d8, 0x14400067, +0x0, 0x3c020001, 0x571021, 0x8c4283b4, +0x28420005, 0x10400020, 0x3821, 0x3c020001, +0x571021, 0x8c4283b4, 0x18400016, 0x2821, +0x96060000, 0x520c0, 0x971021, 0x9442777e, +0x14460009, 0x971021, 0x94437780, 0x96020002, +0x14620005, 0x971021, 0x94437782, 0x96020004, +0x50620008, 0x24070001, 0x3c020001, 0x571021, +0x8c4283b4, 0x24a50001, 0xa2102a, 0x5440ffee, +0x520c0, 0x30e200ff, 0x14400044, 0x24150003, +0x8003c55, 0x0, 0x2402021, 0xc0022fe, +0x24050006, 0x3044001f, 0x428c0, 0x2e51021, +0x9442727c, 0x30424000, 0x14400271, 0xb71021, +0x9443727e, 0x96020000, 0x1462000b, 0x418c0, +0xb71021, 0x94437280, 0x96020002, 0x14620006, +0x418c0, 0xb71021, 0x94437282, 0x96020004, +0x10620027, 0x418c0, 0x2e31021, 0x9442727c, +0x30428000, 0x1440025e, 0x2e31021, 0x944d727c, +0x96070000, 0xd28c0, 0xb71021, 0x9442737e, +0x8003a09, 0x3021, 0x420c0, 0x2e41021, +0x9443737c, 0x2e41021, 0x944d737c, 0x30638000, +0x14600010, 0xd28c0, 0xb71021, 0x9442737e, +0x1447fff5, 0x1a02021, 0xb71021, 0x94437380, +0x96020002, 0x5462fff1, 0x420c0, 0xb71021, +0x94437382, 0x96020004, 0x5462ffec, 0x420c0, +0x24060001, 0x30c200ff, 0x1040023d, 0x0, +0x8003a1c, 0x24150003, 0x24150001, 0x8f420260, +0x53102b, 0x10400036, 0x0, 0x8f8300e4, +0x8f8200e0, 0x10620003, 0x24630008, 0xaf8300e4, +0xaf8300e8, 0x8ee400c0, 0x8ee500c4, 0x2601821, +0x1021, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xaee400c0, 0xaee500c4, 0x8ee20058, +0x24420001, 0xaee20058, 0x8ee20058, 0x8ee2007c, +0x24420001, 0xaee2007c, 0x8ee2007c, 0x8f8200e0, +0xafa20010, 0x8f8200e4, 0x3c040001, 0x248458c0, +0xafa20014, 0x8fa60018, 0x8fa7001c, 0x3c050006, +0xc002403, 0x34a5f203, 0x8003c5b, 0x0, +0x8ee25240, 0xafa20010, 0x8ee25244, 0x3c040001, +0x248458cc, 0xafa20014, 0x8ee60e10, 0x8ee70e18, +0x3c050006, 0xc002403, 0x34a5f202, 0x8ee201c0, +0x24420001, 0xaee201c0, 0x8003c02, 0x8ee201c0, +0x96e20468, 0x53102b, 0x54400001, 0x3c168000, +0x126001cb, 0x3c0e001f, 0x35ceffff, 0x3c0ffff5, +0x35ef1000, 0x241e0040, 0x8ee2724c, 0x8f430280, +0x24420001, 0x304207ff, 0x1062019e, 0x0, +0x12c00012, 0x0, 0x8ee35240, 0x8ee25244, +0x1062000a, 0x26f85244, 0x8ef45244, 0xafb80024, +0x8ee35244, 0x21140, 0x24425248, 0x2e28821, +0x24630001, 0x8003a85, 0x306d00ff, 0x8ee201e0, +0x24420001, 0xaee201e0, 0x8ee201e0, 0x8ee30e10, +0x8ee20e18, 0x1062ffca, 0x26f80e18, 0x8ef40e18, +0xb021, 0xafb80024, 0x8ee30e18, 0x21140, +0x24420e20, 0x2e28821, 0x24630001, 0x306d01ff, +0x96e2046a, 0x30420010, 0x10400018, 0x34028100, +0x9643000c, 0x14620015, 0x0, 0x3c020001, +0x571021, 0x904283c0, 0x14400010, 0x0, +0x9642000e, 0xa6220016, 0x8e420008, 0x8e430004, +0x8e440000, 0x2673fffc, 0xae42000c, 0xae430008, +0xae440004, 0x9622000e, 0x26100004, 0x24180001, +0xa3b8002f, 0x34420200, 0xa622000e, 0x8e220000, +0x8e230004, 0x3c040001, 0x34843800, 0x2003021, +0x306a0007, 0x20a8023, 0x3641021, 0x202102b, +0x10400005, 0x26a9821, 0x2041023, 0x3621823, +0x3c020020, 0x438023, 0x26620007, 0x9623000a, +0x2418fff8, 0x58c824, 0x6a1821, 0x79102b, +0x10400002, 0x3206021, 0x606021, 0x1801821, +0x24620007, 0x2418fff8, 0x586024, 0x26c102b, +0x14400004, 0x1932823, 0x1832823, 0x8003ac3, +0xc31021, 0xd31021, 0x4a2023, 0x1c4102b, +0x54400001, 0x8f2021, 0x25420040, 0x4c102b, +0x14400035, 0x5821, 0x94c3000c, 0x24020800, +0x54620032, 0xae260018, 0x3c020001, 0x571021, +0x904283c0, 0x5440002d, 0xae260018, 0x24c20017, +0x1c2102b, 0x10400013, 0x0, 0x3c02fff5, +0x461021, 0x90421017, 0x38430006, 0x2c630001, +0x38420011, 0x2c420001, 0x621825, 0x10600014, +0x24c20010, 0x1c2102b, 0x1040000e, 0x0, +0x3c0bfff5, 0x1665821, 0x956b1010, 0x8003af4, +0x2562000e, 0x90c20017, 0x38430006, 0x2c630001, +0x38420011, 0x2c420001, 0x621825, 0x10600005, +0x1601821, 0x94cb0010, 0x2562000e, 0x4a5821, +0x1601821, 0x24620007, 0x2418fff8, 0x585824, +0xc31021, 0x4a2023, 0x1c4102b, 0x10400002, +0x1632823, 0x8f2021, 0xae260018, 0x3c020001, +0x571021, 0x904283c0, 0x2102b, 0x216c0, +0x15600002, 0xafa20044, 0x1805821, 0x30820001, +0x10400007, 0x4021, 0x90880000, 0x24840001, +0x1c4102b, 0x10400002, 0x24a5ffff, 0x8f2021, +0x50a00012, 0x81c02, 0x2ca20002, 0x54400009, +0x24a5ffff, 0x94820000, 0x24840002, 0x1024021, +0x1c4102b, 0x10400006, 0x24a5fffe, 0x8003b21, +0x8f2021, 0x90820000, 0x21200, 0x1024021, +0x14a0fff2, 0x2ca20002, 0x81c02, 0x3102ffff, +0x624021, 0x3108ffff, 0x1402821, 0x11400011, +0x2002021, 0x2ca20002, 0x54400009, 0x24a5ffff, +0x94820000, 0x24840002, 0x1024021, 0x1c4102b, +0x10400006, 0x24a5fffe, 0x8003b38, 0x8f2021, +0x90820000, 0x21200, 0x1024021, 0x14a0fff2, +0x2ca20002, 0x81c02, 0x3102ffff, 0x624021, +0x81c02, 0x3102ffff, 0x8f890120, 0x624021, +0x27623800, 0x25230020, 0x62102b, 0x14400002, +0x3108ffff, 0x27633000, 0x8f820128, 0x10620004, +0x0, 0x8f820124, 0x14620007, 0x1402821, +0x8ee201a4, 0x3821, 0x24420001, 0xaee201a4, +0x8003bc9, 0x8ee201a4, 0x8e260000, 0x8e270004, +0x81400, 0x3448000b, 0xad300008, 0xa52b000e, +0xad280018, 0x8fb80044, 0x2021, 0x2961025, +0x581025, 0xad22001c, 0xe5102b, 0xe53823, +0xc43023, 0xc23023, 0xad260000, 0xad270004, +0x8ee204c0, 0xad220010, 0xaf830120, 0x92e24e20, +0x1440005f, 0x24070001, 0x2502ffee, 0x2c420002, +0x14400003, 0x24020011, 0x15020024, 0x0, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x8c830000, 0x24020012, 0x1462000f, 0x0, +0x8ee34e30, 0x8ee24e34, 0x1062000b, 0x0, +0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, +0x8ee34e30, 0x24420001, 0x105e002a, 0x0, +0x8003ba8, 0x0, 0x8ee24e30, 0x24420001, +0x505e0003, 0x1021, 0x8ee24e30, 0x24420001, +0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0x8003bc6, 0x24020012, 0x8ee24e30, +0x210c0, 0x24425038, 0x2e22021, 0x8c830000, +0x24020007, 0x1462001f, 0x0, 0x8ee34e30, +0x8ee24e34, 0x1062001b, 0x0, 0x8c820004, +0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, +0x24420001, 0x105e0007, 0x0, 0x8ee24e34, +0x24420001, 0x10620005, 0x0, 0x8003bb4, +0x0, 0x14600005, 0x0, 0x8f820128, +0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, +0x2c420011, 0x50400012, 0xac800000, 0x8003bc9, +0x0, 0x8ee24e30, 0x24420001, 0x505e0003, +0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x24020007, 0xac820000, 0x24020001, 0xac820004, +0x14e00019, 0x3c050006, 0x3c040001, 0x24845890, +0x8e220018, 0x34a5f209, 0xafa20010, 0x8e220000, +0x8e230004, 0x2203021, 0x1603821, 0xc002403, +0xafa30014, 0x93a2002f, 0x1040002a, 0x34028100, +0x8e430004, 0x8e440008, 0x8e45000c, 0xa642000c, +0xae430000, 0xae440004, 0xae450008, 0x96220016, +0x8003c02, 0xa642000e, 0x1599000a, 0x26a1823, +0x9622000e, 0xa623000a, 0x34420004, 0xa622000e, +0x3c010001, 0x370821, 0xa02083c0, 0x8003bff, +0x9821, 0x9624000a, 0x83102b, 0x54400001, +0x801821, 0x24020001, 0xa623000a, 0x3c010001, +0x370821, 0xa02283c0, 0x9622000a, 0x4a1821, +0x2038021, 0x1d0102b, 0x54400001, 0x20f8021, +0x2639823, 0xb021, 0x8fb80024, 0x1660fe5e, +0xaf0d0000, 0x12600022, 0x0, 0x3c010001, +0x370821, 0xac3383c4, 0x3c010001, 0x370821, +0xac3083c8, 0x3c010001, 0x370821, 0xac3283cc, +0x93a2002f, 0x10400008, 0x0, 0x3c020001, +0x571021, 0x8c4283cc, 0x24420004, 0x3c010001, +0x370821, 0xac2283cc, 0x8f430280, 0x8ee2724c, +0x14620006, 0x0, 0x8ee201c4, 0x24420001, +0xaee201c4, 0x8003c5b, 0x8ee201c4, 0x8ee201bc, +0x24420001, 0xaee201bc, 0x8003c5b, 0x8ee201bc, +0x97a4001e, 0x2484fffc, 0x801821, 0x8ee400c0, +0x8ee500c4, 0x1021, 0xa32821, 0xa3302b, +0x822021, 0x862021, 0x24020002, 0xaee400c0, +0xaee500c4, 0x12a2000f, 0x2aa20003, 0x14400017, +0x24020003, 0x16a20015, 0x0, 0x8ee200d0, +0x8ee300d4, 0x24630001, 0x2c640001, 0x441021, +0xaee200d0, 0xaee300d4, 0x8ee200d0, 0x8003c55, +0x8ee300d4, 0x8ee200d8, 0x8ee300dc, 0x24630001, +0x2c640001, 0x441021, 0xaee200d8, 0xaee300dc, +0x8ee200d8, 0x8003c55, 0x8ee300dc, 0x8ee200c8, +0x8ee300cc, 0x24630001, 0x2c640001, 0x441021, +0xaee200c8, 0xaee300cc, 0x8ee200c8, 0x8ee300cc, +0x8f8300e4, 0x8f8200e0, 0x10620003, 0x24630008, +0xaf8300e4, 0xaf8300e8, 0x8fbf0068, 0x8fbe0064, +0x8fb60060, 0x8fb5005c, 0x8fb40058, 0x8fb30054, +0x8fb20050, 0x8fb1004c, 0x8fb00048, 0x3e00008, +0x27bd0070, 0x27bdffe0, 0xafbf0018, 0x8ee30e14, +0x8ee20e0c, 0x10620074, 0x0, 0x8ee30e0c, +0x8ee20e14, 0x622023, 0x4820001, 0x24840200, +0x8ee30e18, 0x8ee20e14, 0x43102b, 0x14400004, +0x24020200, 0x8ee30e14, 0x8003c7d, 0x431823, +0x8ee20e18, 0x8ee30e14, 0x431023, 0x2443ffff, +0x804821, 0x69102a, 0x54400001, 0x604821, +0x8f870100, 0x27623000, 0x24e80020, 0x102102b, +0x50400001, 0x27682800, 0x8f820108, 0x11020004, +0x0, 0x8f820104, 0x15020007, 0x1021, +0x8ee201a8, 0x2021, 0x24420001, 0xaee201a8, +0x8003cbf, 0x8ee201a8, 0x8ee40e14, 0x42140, +0x801821, 0x8ee40460, 0x8ee50464, 0xa32821, +0xa3302b, 0x822021, 0x862021, 0xace40000, +0xace50004, 0x8ee30e14, 0x91140, 0xa4e2000e, +0x24020002, 0xace20018, 0x31940, 0x24630e20, +0x2e31021, 0xace20008, 0x8ee20e14, 0xace2001c, +0x8ee204cc, 0xace20010, 0xaf880100, 0x92e204ec, +0x14400011, 0x24040001, 0x8ee24e28, 0x24030040, +0x24420001, 0x50430003, 0x1021, 0x8ee24e28, +0x24420001, 0xaee24e28, 0x8ee24e28, 0x210c0, +0x24424e38, 0x2e21821, 0x24020002, 0xac620000, +0x24020001, 0xac620004, 0x1480000e, 0x24030040, +0x8ee20e14, 0xafa20010, 0x8ee20e18, 0x3c050007, +0xafa20014, 0x8ee60e0c, 0x8ee70e10, 0x3c040001, +0x248458d4, 0xc002403, 0x34a5f001, 0x8003cdd, +0x0, 0x8ee20500, 0x24420001, 0x50430003, +0x1021, 0x8ee20500, 0x24420001, 0xaee20500, +0x8ee20500, 0x21080, 0x571021, 0xac490508, +0x8ee20e14, 0x491021, 0x304201ff, 0xaee20e14, +0x8ee30e14, 0x8ee20e0c, 0x14620005, 0x0, +0x8f820060, 0x2403fdff, 0x431024, 0xaf820060, +0x8fbf0018, 0x3e00008, 0x27bd0020, 0x27bdffe0, +0xafbf0018, 0x8ee3523c, 0x8ee25238, 0x10620074, +0x0, 0x8ee35238, 0x8ee2523c, 0x622023, +0x4820001, 0x24840100, 0x8ee35244, 0x8ee2523c, +0x43102b, 0x14400004, 0x24020100, 0x8ee3523c, +0x8003cff, 0x431823, 0x8ee25244, 0x8ee3523c, +0x431023, 0x2443ffff, 0x804821, 0x69102a, +0x54400001, 0x604821, 0x8f870100, 0x27623000, +0x24e80020, 0x102102b, 0x50400001, 0x27682800, +0x8f820108, 0x11020004, 0x0, 0x8f820104, +0x15020007, 0x1021, 0x8ee201a8, 0x2021, +0x24420001, 0xaee201a8, 0x8003d41, 0x8ee201a8, +0x8ee4523c, 0x42140, 0x801821, 0x8ee40470, +0x8ee50474, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xace40000, 0xace50004, 0x8ee3523c, +0x91140, 0xa4e2000e, 0x24020003, 0xace20018, +0x31940, 0x24635248, 0x2e31021, 0xace20008, +0x8ee2523c, 0xace2001c, 0x8ee204cc, 0xace20010, +0xaf880100, 0x92e204ec, 0x14400011, 0x24040001, +0x8ee24e28, 0x24030040, 0x24420001, 0x50430003, +0x1021, 0x8ee24e28, 0x24420001, 0xaee24e28, +0x8ee24e28, 0x210c0, 0x24424e38, 0x2e21821, +0x24020003, 0xac620000, 0x24020001, 0xac620004, +0x1480000e, 0x24030040, 0x8ee2523c, 0xafa20010, +0x8ee25244, 0x3c050007, 0xafa20014, 0x8ee65238, +0x8ee75240, 0x3c040001, 0x248458e0, 0xc002403, +0x34a5f010, 0x8003d5f, 0x0, 0x8ee20500, +0x24420001, 0x50430003, 0x1021, 0x8ee20500, +0x24420001, 0xaee20500, 0x8ee20500, 0x21080, +0x571021, 0xac490508, 0x8ee2523c, 0x491021, +0x304200ff, 0xaee2523c, 0x8ee3523c, 0x8ee25238, +0x14620005, 0x0, 0x8f820060, 0x2403feff, +0x431024, 0xaf820060, 0x8fbf0018, 0x3e00008, +0x27bd0020, 0x8f820120, 0x8ee34e34, 0x8f820124, +0x8f860128, 0x24020040, 0x24630001, 0x50620003, +0x1021, 0x8ee24e34, 0x24420001, 0xaee24e34, +0x8ee24e34, 0x8ee44e34, 0x8ee34e30, 0x210c0, +0x24425038, 0x14830007, 0x2e22821, 0x8f820128, +0x24420020, 0xaf820128, 0x8f820128, 0x8003d92, +0xaca00000, 0x8ee24e34, 0x24030040, 0x24420001, +0x50430003, 0x1021, 0x8ee24e34, 0x24420001, +0x210c0, 0x24425038, 0x2e22821, 0x8ca20004, +0x8f830128, 0x21140, 0x621821, 0xaf830128, +0xaca00000, 0x8cc20018, 0x2443fffe, 0x2c620012, +0x10400008, 0x31080, 0x3c010001, 0x220821, +0x8c2258f0, 0x400008, 0x0, 0x24020001, +0xaee24e24, 0x3e00008, 0x0, 0x27bdffc8, +0xafbf0030, 0xafb5002c, 0xafb40028, 0xafb30024, +0xafb20020, 0xafb1001c, 0xafb00018, 0x8f830128, +0x8f820124, 0x106202b0, 0x9821, 0x3c11001f, +0x3631ffff, 0x3c12fff5, 0x36521000, 0x24150012, +0x24140040, 0x8f8c0128, 0x8f820128, 0x24420020, +0xaf820128, 0x9182001b, 0x8f830128, 0x2443fffe, +0x2c620012, 0x1040029c, 0x31080, 0x3c010001, +0x220821, 0x8c225948, 0x400008, 0x0, +0x8f420218, 0x30420100, 0x10400007, 0x0, +0x95830016, 0x95820018, 0x621823, 0x31402, +0x431021, 0xa5820016, 0x8d82001c, 0x3c038000, +0x3044ffff, 0x436824, 0x3c030800, 0x431824, +0x11a00004, 0xad84001c, 0x41140, 0x8003dd8, +0x24425248, 0x41140, 0x24420e20, 0x2e25821, +0x9562000e, 0x3042fffc, 0x10600004, 0xa562000e, +0x95840016, 0x8003ec0, 0x0, 0x8d690018, +0x4021, 0x952a0000, 0x25290002, 0x95270000, +0x25290002, 0x95260000, 0x25290002, 0x95250000, +0x25290002, 0x95240000, 0x25290002, 0x95230000, +0x25290002, 0x95220000, 0x25290002, 0x1475021, +0x1465021, 0x1455021, 0x1445021, 0x1435021, +0x1425021, 0xa1c02, 0x3142ffff, 0x625021, +0xa1c02, 0x3142ffff, 0x625021, 0x96e2046a, +0x314effff, 0x30420002, 0x10400044, 0x5021, +0x25220014, 0x222102b, 0x10400014, 0x1201821, +0x2405000a, 0x2021, 0x223102b, 0x54400001, +0x721821, 0x94620000, 0x24630002, 0x24a5ffff, +0x14a0fff9, 0x822021, 0x41c02, 0x3082ffff, +0x622021, 0x41402, 0x3083ffff, 0x431021, +0x3042ffff, 0x8003e33, 0x1425021, 0x952a0000, +0x25290002, 0x95280000, 0x25290002, 0x95270000, +0x25290002, 0x95260000, 0x25290002, 0x95250000, +0x25290002, 0x95230000, 0x25290002, 0x95220000, +0x25290002, 0x95240000, 0x25290002, 0x1485021, +0x1475021, 0x1465021, 0x1455021, 0x1435021, +0x1425021, 0x95220000, 0x95230002, 0x1445021, +0x1425021, 0x1435021, 0xa1c02, 0x3142ffff, +0x625021, 0xa1c02, 0x3142ffff, 0x625021, +0x3148ffff, 0x51000001, 0x3408ffff, 0x8d620018, +0x9443000c, 0x24020800, 0x54620005, 0xa5680010, +0x9562000e, 0x34420002, 0xa562000e, 0xa5680010, +0x96e2046a, 0x2821, 0x30420008, 0x14400056, +0x3021, 0x8d630018, 0x24620024, 0x222102b, +0x10400034, 0x24690010, 0x229102b, 0x54400001, +0x1324821, 0x95250000, 0x24690014, 0x229102b, +0x10400002, 0x24a5ffec, 0x1324821, 0x95220000, +0x30420fff, 0x14400003, 0x25290002, 0x8003e60, +0x24130001, 0x9821, 0xa03021, 0x229102b, +0x54400001, 0x1324821, 0x91220001, 0x25290002, +0xa22821, 0x229102b, 0x54400001, 0x1324821, +0x25290002, 0x229102b, 0x54400001, 0x1324821, +0x95220000, 0x25290002, 0xa22821, 0x229102b, +0x54400001, 0x1324821, 0x95220000, 0x25290002, +0xa22821, 0x229102b, 0x54400001, 0x1324821, +0x95220000, 0x25290002, 0xa22821, 0x229102b, +0x54400001, 0x1324821, 0x95220000, 0x8003e99, +0xa22821, 0x94650010, 0x94620014, 0x24690016, +0x30420fff, 0x14400003, 0x24a5ffec, 0x8003e8c, +0x24130001, 0x9821, 0xa03021, 0x91230001, +0x25290004, 0x95220000, 0x25290002, 0x95240000, +0x25290002, 0xa32821, 0xa22821, 0x95220000, +0x95230002, 0xa42821, 0xa22821, 0xa32821, +0x51c02, 0x30a2ffff, 0x622821, 0x51c02, +0x30a2ffff, 0x622821, 0x96e2046a, 0x30420001, +0x1040001e, 0x2021, 0x95820016, 0x4e2023, +0x41402, 0x822021, 0x326200ff, 0x50400002, +0x862021, 0x852021, 0x41402, 0x822021, +0x3084ffff, 0x50800001, 0x3404ffff, 0x8d620018, +0x24430017, 0x223102b, 0x54400001, 0x721821, +0x90620000, 0x38430011, 0x2c630001, 0x38420006, +0x2c420001, 0x621825, 0x10600004, 0x0, +0x9562000e, 0x34420001, 0xa562000e, 0x9562000e, +0x240a0002, 0x30420004, 0x10400002, 0xa5640012, +0x240a0004, 0x8f880120, 0x27623800, 0x25090020, +0x122102b, 0x50400001, 0x27693000, 0x8f820128, +0x11220004, 0x0, 0x8f820124, 0x15220007, +0x24040020, 0x8ee201a4, 0x8021, 0x24420001, +0xaee201a4, 0x8003f4f, 0x8ee201a4, 0x8ee5724c, +0x8ee60490, 0x8ee70494, 0xad0b0008, 0xa504000e, +0xad0a0018, 0x52940, 0xa01821, 0x1021, +0xe33821, 0xe3202b, 0xc23021, 0xc43021, +0xad060000, 0xad070004, 0x8ee2724c, 0x4d1025, +0xad02001c, 0x8ee204c4, 0xad020010, 0xaf890120, +0x92e24e20, 0x14400060, 0x24100001, 0x2543ffee, +0x2c630002, 0x39420011, 0x2c420001, 0x621825, +0x10600024, 0x0, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x8c820000, 0x1455000f, +0x0, 0x8ee34e30, 0x8ee24e34, 0x1062000b, +0x0, 0x8c820004, 0x24420001, 0xac820004, +0x8ee24e34, 0x8ee34e30, 0x24420001, 0x1054002b, +0x0, 0x8003f2e, 0x0, 0x8ee24e30, +0x24420001, 0x50540003, 0x1021, 0x8ee24e30, +0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x24020001, 0x8003f4e, +0xac950000, 0x8ee24e30, 0x210c0, 0x24425038, +0x2e22021, 0x8c830000, 0x24020007, 0x1462001f, +0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, +0x0, 0x8c820004, 0x24420001, 0xac820004, +0x8ee24e34, 0x8ee34e30, 0x24420001, 0x10540007, +0x0, 0x8ee24e34, 0x24420001, 0x10620005, +0x0, 0x8003f3a, 0x0, 0x14600005, +0x0, 0x8f820128, 0x24420020, 0xaf820128, +0x8f820128, 0x8c820004, 0x2c420011, 0x50400012, +0xac800000, 0x8003f4f, 0x0, 0x8ee24e30, +0x24420001, 0x50540003, 0x1021, 0x8ee24e30, +0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x24020007, 0xac820000, +0x24020001, 0xac820004, 0x1600000d, 0x0, +0x8f820120, 0x3c040001, 0x24845938, 0xafa00014, +0xafa20010, 0x8d86001c, 0x8f870124, 0x3c050008, +0xc002403, 0x34a50001, 0x8004057, 0x0, +0x8ee2724c, 0x24420001, 0x304207ff, 0x11a00006, +0xaee2724c, 0x8ee201d0, 0x2442ffff, 0xaee201d0, +0x8003f6b, 0x8ee201d0, 0x8ee201cc, 0x2442ffff, +0xaee201cc, 0x8ee201cc, 0x8ee201d8, 0x2442ffff, +0xaee201d8, 0x8004057, 0x8ee201d8, 0x8f420240, +0x104000e5, 0x0, 0x8ee20e1c, 0x24420001, +0x8004057, 0xaee20e1c, 0x9582001e, 0xad82001c, +0x8f420240, 0x10400072, 0x0, 0x8ee20e1c, +0x24420001, 0xaee20e1c, 0x8f430240, 0x43102b, +0x144000d5, 0x0, 0x8f830120, 0x27623800, +0x24660020, 0xc2102b, 0x50400001, 0x27663000, +0x8f820128, 0x10c20004, 0x0, 0x8f820124, +0x14c20007, 0x0, 0x8ee201a4, 0x8021, +0x24420001, 0xaee201a4, 0x8003fda, 0x8ee201a4, +0x8ee2724c, 0xac62001c, 0x8ee404a8, 0x8ee504ac, +0x2462001c, 0xac620008, 0x24020008, 0xa462000e, +0x24020011, 0xac620018, 0xac640000, 0xac650004, +0x8ee204c4, 0xac620010, 0xaf860120, 0x92e24e20, +0x14400034, 0x24100001, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x8c820000, 0x1455001f, +0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, +0x0, 0x8c820004, 0x24420001, 0xac820004, +0x8ee24e34, 0x8ee34e30, 0x24420001, 0x10540007, +0x0, 0x8ee24e34, 0x24420001, 0x10620005, +0x0, 0x8003fc6, 0x0, 0x14600005, +0x0, 0x8f820128, 0x24420020, 0xaf820128, +0x8f820128, 0x8c820004, 0x2c420011, 0x50400011, +0xac800000, 0x8003fda, 0x0, 0x8ee24e30, +0x24420001, 0x50540003, 0x1021, 0x8ee24e30, +0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x24020001, 0xac950000, +0xac820004, 0x5600000b, 0x24100001, 0x8ee2724c, +0x3c040001, 0x248458a8, 0xafa00014, 0xafa20010, +0x8ee6724c, 0x8f470280, 0x3c050009, 0xc002403, +0x34a5f008, 0x56000001, 0xaee00e1c, 0x8ee20188, +0x24420001, 0xaee20188, 0x8004050, 0x8ee20188, +0x8f830120, 0x27623800, 0x24660020, 0xc2102b, +0x50400001, 0x27663000, 0x8f820128, 0x10c20004, +0x0, 0x8f820124, 0x14c20007, 0x0, +0x8ee201a4, 0x8021, 0x24420001, 0xaee201a4, +0x8004044, 0x8ee201a4, 0x8ee2724c, 0xac62001c, +0x8ee404a8, 0x8ee504ac, 0x2462001c, 0xac620008, +0x24020008, 0xa462000e, 0x24020011, 0xac620018, +0xac640000, 0xac650004, 0x8ee204c4, 0xac620010, +0xaf860120, 0x92e24e20, 0x14400034, 0x24100001, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x8c820000, 0x1455001f, 0x0, 0x8ee34e30, +0x8ee24e34, 0x1062001b, 0x0, 0x8c820004, +0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, +0x24420001, 0x10540007, 0x0, 0x8ee24e34, +0x24420001, 0x10620005, 0x0, 0x8004030, +0x0, 0x14600005, 0x0, 0x8f820128, +0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, +0x2c420011, 0x50400011, 0xac800000, 0x8004044, +0x0, 0x8ee24e30, 0x24420001, 0x50540003, +0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, +0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, +0x24020001, 0xac950000, 0xac820004, 0x1600000b, +0x0, 0x8ee2724c, 0x3c040001, 0x248458a8, +0xafa00014, 0xafa20010, 0x8ee6724c, 0x8f470280, +0x3c050009, 0xc002403, 0x34a5f008, 0x8ee20174, +0x24420001, 0xaee20174, 0x8004057, 0x8ee20174, +0x24020001, 0xaee24e24, 0x8f830128, 0x8f820124, +0x1462fd58, 0x0, 0x8fbf0030, 0x8fb5002c, +0x8fb40028, 0x8fb30024, 0x8fb20020, 0x8fb1001c, +0x8fb00018, 0x3e00008, 0x27bd0038, 0x27bdffe8, +0x27840208, 0x27450200, 0x24060008, 0xafbf0014, +0xc00249a, 0xafb00010, 0x2021, 0x24100001, +0x2402241f, 0xaf900210, 0xaf900200, 0xaf800204, +0xaf820214, 0x8f460248, 0x24030004, 0x3c020040, +0x3c010001, 0xac235cc4, 0x3c010001, 0xac235cc8, +0x3c010001, 0xac205d9c, 0x3c010001, 0xac225cc0, +0x3c010001, 0xac235cc8, 0xc005108, 0x24050004, +0xc004822, 0x0, 0x8ee20000, 0x3c03feff, +0x3463fffd, 0x431024, 0xaee20000, 0x3c023c00, +0xaf82021c, 0x3c010001, 0x370821, 0xac3083ac, +0x8fbf0014, 0x8fb00010, 0x3e00008, 0x27bd0018, +0x27bdffe0, 0x3c050008, 0x34a50400, 0xafbf0018, +0xafa00010, 0xafa00014, 0x8f860200, 0x3c040001, +0x248459f0, 0xc002403, 0x3821, 0x8ee20280, +0x24420001, 0xaee20280, 0x8ee20280, 0x8f830200, +0x3c023f00, 0x621824, 0x8fbf0018, 0x3c020400, +0x3e00008, 0x27bd0020, 0x27bdffd8, 0xafbf0020, +0xafb1001c, 0xafb00018, 0x8f900220, 0x8ee20214, +0x3821, 0x24420001, 0xaee20214, 0x8ee20214, +0x3c020300, 0x2021024, 0x10400027, 0x3c110400, +0xc00429b, 0x0, 0x3c020100, 0x2021024, +0x10400007, 0x0, 0x8ee20218, 0x24420001, +0xaee20218, 0x8ee20218, 0x80040c6, 0x3c03fdff, +0x8ee2021c, 0x24420001, 0xaee2021c, 0x8ee2021c, +0x3c03fdff, 0x3463ffff, 0x3c0808ff, 0x3508ffff, +0x8ee20000, 0x3c040001, 0x248459fc, 0x3c050008, +0x2003021, 0x431024, 0xaee20000, 0x8f820220, +0x3821, 0x3c030300, 0x481024, 0x431025, +0xaf820220, 0xafa00010, 0xc002403, 0xafa00014, +0x8004296, 0x0, 0x2111024, 0x1040001f, +0x3c024000, 0x8f830224, 0x24021402, 0x1462000b, +0x3c03fdff, 0x3c040001, 0x24845a08, 0x3c050008, +0xafa00010, 0xafa00014, 0x8f860224, 0x34a5ffff, +0xc002403, 0x3821, 0x3c03fdff, 0x8ee20000, +0x3463ffff, 0x2002021, 0x431024, 0xc004e54, +0xaee20000, 0x8ee20220, 0x24420001, 0xaee20220, +0x8ee20220, 0x8f820220, 0x3c0308ff, 0x3463ffff, +0x431024, 0x8004295, 0x511025, 0x2021024, +0x10400142, 0x0, 0x8ee2022c, 0x24420001, +0xaee2022c, 0x8ee2022c, 0x8f820220, 0x3c0308ff, +0x3463ffff, 0x431024, 0x34420004, 0xaf820220, +0x8f830054, 0x8f820054, 0x800410e, 0x24630002, +0x8f820054, 0x621023, 0x2c420003, 0x1440fffc, +0x0, 0x8f8600e0, 0x8f8400e4, 0x30c20007, +0x10400012, 0x0, 0x8f8300e4, 0x2402fff8, +0xc21024, 0x1043000d, 0x0, 0x8f820054, +0x8f8300e0, 0x14c30009, 0x24440050, 0x8f820054, +0x821023, 0x2c420051, 0x10400004, 0x0, +0x8f8200e0, 0x10c2fff9, 0x0, 0x8f820220, +0x3c0308ff, 0x3463fffd, 0x431024, 0xaf820220, +0x8f8600e0, 0x30c20007, 0x10400003, 0x2402fff8, +0xc23024, 0xaf8600e0, 0x8f8300c4, 0x3c02001f, +0x3442ffff, 0x24680008, 0x48102b, 0x10400003, +0x3c02fff5, 0x34421000, 0x1024021, 0x8f8b00c8, +0x8f850120, 0x8f840124, 0x8004145, 0x6021, +0x27623800, 0x82102b, 0x50400001, 0x27643000, +0x10a40010, 0x318200ff, 0x8c820018, 0x38430007, +0x2c630001, 0x3842000b, 0x2c420001, 0x621825, +0x5060fff3, 0x24840020, 0x8ee20240, 0x240c0001, +0x24420001, 0xaee20240, 0x8ee20240, 0x8c8b0008, +0x318200ff, 0x14400065, 0x0, 0x3c020001, +0x571021, 0x904283c0, 0x14400060, 0x0, +0x8f8400e4, 0xc41023, 0x218c3, 0x4620001, +0x24630200, 0x8f8900c4, 0x10600005, 0x24020001, +0x10620009, 0x0, 0x8004187, 0x0, +0x8ee20230, 0x1205821, 0x24420001, 0xaee20230, +0x80041bc, 0x8ee20230, 0x8ee20234, 0x3c05000a, +0x24420001, 0xaee20234, 0x8c8b0000, 0x34a5f000, +0x8ee20234, 0x12b1823, 0xa3102b, 0x54400001, +0x651821, 0x2c62233f, 0x14400040, 0x0, +0x8f8200e8, 0x24420008, 0xaf8200e8, 0x8f8200e8, +0x8f8200e4, 0x1205821, 0x24420008, 0xaf8200e4, +0x80041bc, 0x8f8200e4, 0x8ee20238, 0x3c03000a, +0x24420001, 0xaee20238, 0x8c840000, 0x3463f000, +0x8ee20238, 0x883823, 0x67102b, 0x54400001, +0xe33821, 0x3c020003, 0x34420d40, 0x47102b, +0x10400003, 0x0, 0x80041bc, 0x805821, +0x8f8200e4, 0x24440008, 0xaf8400e4, 0x8f8400e4, +0x10860018, 0x3c05000a, 0x34a5f000, 0x3c0a0003, +0x354a0d40, 0x8ee2007c, 0x24420001, 0xaee2007c, +0x8c830000, 0x8ee2007c, 0x683823, 0xa7102b, +0x54400001, 0xe53821, 0x147102b, 0x54400007, +0x605821, 0x8f8200e4, 0x24440008, 0xaf8400e4, +0x8f8400e4, 0x1486ffef, 0x0, 0x14860005, +0x0, 0x1205821, 0xaf8600e4, 0x80041bc, +0xaf8600e8, 0xaf8400e4, 0xaf8400e8, 0x8f8200c8, +0x3c03000a, 0x3463f000, 0x483823, 0x67102b, +0x54400001, 0xe33821, 0x3c020003, 0x34420d3f, +0x47102b, 0x54400007, 0x6021, 0x1683823, +0x67102b, 0x54400003, 0xe33821, 0x80041cf, +0x3c020003, 0x3c020003, 0x34420d3f, 0x47102b, +0x14400016, 0x318200ff, 0x14400006, 0x0, +0x3c020001, 0x571021, 0x904283c0, 0x1040000f, +0x0, 0x8ee2023c, 0x3c04fdff, 0x8ee30000, +0x3484ffff, 0x24420001, 0xaee2023c, 0x8ee2023c, +0x24020001, 0x641824, 0x3c010001, 0x370821, +0xa02283b8, 0x800422c, 0xaee30000, 0xaf8b00c8, +0x8f8300c8, 0x8f8200c4, 0x3c04000a, 0x3484f000, +0x623823, 0x87102b, 0x54400001, 0xe43821, +0x3c020003, 0x34420d40, 0x47102b, 0x2ce30001, +0x431025, 0x10400008, 0x0, 0x8f820220, +0x3c0308ff, 0x3463ffff, 0x431024, 0x3c034000, +0x431025, 0xaf820220, 0x8f8600e0, 0x8f8400e4, +0x10c4002a, 0x0, 0x8ee2007c, 0x24420001, +0xaee2007c, 0x8ee2007c, 0x24c2fff8, 0xaf8200e0, +0x3c020001, 0x8c427e30, 0x3c030008, 0x8f8600e0, +0x431024, 0x1040001d, 0x0, 0x10c4001b, +0x240dfff8, 0x3c0a000a, 0x354af000, 0x3c0c0080, +0x24850008, 0x27622800, 0x50a20001, 0x27651800, +0x8c880004, 0x8c820000, 0x8ca90000, 0x3103ffff, +0x431021, 0x4d1024, 0x24430010, 0x6b102b, +0x54400001, 0x6a1821, 0x12b102b, 0x54400001, +0x12a4821, 0x10690002, 0x10c1025, 0xac820004, +0xa02021, 0x14c4ffeb, 0x24850008, 0x8f820220, +0x3c0308ff, 0x3463ffff, 0x431024, 0x34420002, +0xaf820220, 0x8f830054, 0x8f820054, 0x8004237, +0x24630001, 0x8f820054, 0x621023, 0x2c420002, +0x1440fffc, 0x0, 0x8f820220, 0x3c0308ff, +0x3463fffb, 0x431024, 0xaf820220, 0x6010055, +0x0, 0x8ee20228, 0x24420001, 0xaee20228, +0x8ee20228, 0x8f820220, 0x3c0308ff, 0x3463ffff, +0x431024, 0x34420004, 0xaf820220, 0x8f830054, +0x8f820054, 0x8004251, 0x24630002, 0x8f820054, +0x621023, 0x2c420003, 0x1440fffc, 0x0, +0x8f8600e0, 0x30c20007, 0x10400012, 0x0, +0x8f8300e4, 0x2402fff8, 0xc21024, 0x1043000d, +0x0, 0x8f820054, 0x8f8300e0, 0x14c30009, +0x24440032, 0x8f820054, 0x821023, 0x2c420033, +0x10400004, 0x0, 0x8f8200e0, 0x10c2fff9, +0x0, 0x8f820220, 0x3c0308ff, 0x3463fffd, +0x431024, 0xaf820220, 0x8f8600e0, 0x30c20007, +0x10400003, 0x2402fff8, 0xc23024, 0xaf8600e0, +0x240301f5, 0x8f8200e8, 0x673823, 0x718c0, +0x431021, 0xaf8200e8, 0x8f8200e8, 0xaf8200e4, +0x8ee2007c, 0x3c0408ff, 0x3484ffff, 0x471021, +0xaee2007c, 0x8f820220, 0x3c038000, 0x34630002, +0x441024, 0x431025, 0xaf820220, 0x8f830054, +0x8f820054, 0x800428d, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x8f820220, 0x3c0308ff, 0x3463fffb, 0x431024, +0xaf820220, 0x8fbf0020, 0x8fb1001c, 0x8fb00018, +0x3e00008, 0x27bd0028, 0x3c020001, 0x8c425cd8, +0x27bdffd8, 0x10400012, 0xafbf0020, 0x3c040001, +0x24845a14, 0x3c050008, 0x24020001, 0x3c010001, +0x370821, 0xac2283ac, 0xafa00010, 0xafa00014, +0x8f860220, 0x34a50498, 0x3c010001, 0xac205cd8, +0x3c010001, 0xac225ccc, 0xc002403, 0x3821, +0x8f420268, 0x3c037fff, 0x3463ffff, 0x431024, +0xaf420268, 0x8ee204d0, 0x8ee404d4, 0x2403fffe, +0x431024, 0x30840002, 0x1080011e, 0xaee204d0, +0x8ee204d4, 0x2403fffd, 0x431024, 0xaee204d4, +0x8f820044, 0x3c030600, 0x34632000, 0x34420020, +0xaf820044, 0xafa30018, 0x8ee20608, 0x8f430228, +0x24420001, 0x304a00ff, 0x514300fe, 0xafa00010, +0x8ee20608, 0x210c0, 0x571021, 0x8fa30018, +0x8fa4001c, 0xac43060c, 0xac440610, 0x8f830054, +0x8f820054, 0x24690032, 0x1221023, 0x2c420033, +0x1040006a, 0x5821, 0x24180008, 0x240f000d, +0x240d0007, 0x240c0040, 0x240e0001, 0x8f870120, +0x27623800, 0x24e80020, 0x102102b, 0x50400001, +0x27683000, 0x8f820128, 0x11020004, 0x0, +0x8f820124, 0x15020007, 0x1021, 0x8ee201a4, +0x2821, 0x24420001, 0xaee201a4, 0x800433d, +0x8ee201a4, 0x8ee40608, 0x420c0, 0x801821, +0x8ee40430, 0x8ee50434, 0xa32821, 0xa3302b, +0x822021, 0x862021, 0xace40000, 0xace50004, +0x8ee20608, 0xa4f8000e, 0xacef0018, 0xacea001c, +0x210c0, 0x2442060c, 0x2e21021, 0xace20008, +0x8ee204c4, 0xace20010, 0xaf880120, 0x92e24e20, +0x14400033, 0x24050001, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x8c820000, 0x144d001f, +0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, +0x0, 0x8c820004, 0x24420001, 0xac820004, +0x8ee24e34, 0x8ee34e30, 0x24420001, 0x104c0007, +0x0, 0x8ee24e34, 0x24420001, 0x10620005, +0x0, 0x800432a, 0x0, 0x14600005, +0x0, 0x8f820128, 0x24420020, 0xaf820128, +0x8f820128, 0x8c820004, 0x2c420011, 0x50400010, +0xac800000, 0x800433d, 0x0, 0x8ee24e30, +0x24420001, 0x504c0003, 0x1021, 0x8ee24e30, +0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0xac8d0000, 0xac8e0004, +0x54a00006, 0x240b0001, 0x8f820054, 0x1221023, +0x2c420033, 0x1440ff9d, 0x0, 0x316300ff, +0x24020001, 0x54620079, 0xafa00010, 0xaeea0608, +0x8f830054, 0x8f820054, 0x24690032, 0x1221023, +0x2c420033, 0x10400061, 0x5821, 0x240d0008, +0x240c0011, 0x24080012, 0x24070040, 0x240a0001, +0x8f830120, 0x27623800, 0x24660020, 0xc2102b, +0x50400001, 0x27663000, 0x8f820128, 0x10c20004, +0x0, 0x8f820124, 0x14c20007, 0x0, +0x8ee201a4, 0x2821, 0x24420001, 0xaee201a4, +0x80043a9, 0x8ee201a4, 0x8ee20608, 0xac62001c, +0x8ee404a0, 0x8ee504a4, 0x2462001c, 0xac620008, +0xa46d000e, 0xac6c0018, 0xac640000, 0xac650004, +0x8ee204c4, 0xac620010, 0xaf860120, 0x92e24e20, +0x14400033, 0x24050001, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0x8c820000, 0x1448001f, +0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, +0x0, 0x8c820004, 0x24420001, 0xac820004, +0x8ee24e34, 0x8ee34e30, 0x24420001, 0x10470007, +0x0, 0x8ee24e34, 0x24420001, 0x10620005, +0x0, 0x8004396, 0x0, 0x14600005, +0x0, 0x8f820128, 0x24420020, 0xaf820128, +0x8f820128, 0x8c820004, 0x2c420011, 0x50400010, +0xac800000, 0x80043a9, 0x0, 0x8ee24e30, +0x24420001, 0x50470003, 0x1021, 0x8ee24e30, +0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, +0x24425038, 0x2e22021, 0xac880000, 0xac8a0004, +0x54a00006, 0x240b0001, 0x8f820054, 0x1221023, +0x2c420033, 0x1440ffa6, 0x0, 0x316300ff, +0x24020001, 0x54620003, 0xafa00010, 0x80043d6, +0x0, 0x3c040001, 0x24845a20, 0xafa00014, +0x8f860120, 0x8f870124, 0x3c050009, 0xc002403, +0x34a5f011, 0x80043d6, 0x0, 0x3c040001, +0x24845a2c, 0xafa00014, 0x8f860120, 0x8f870124, +0x3c050009, 0xc002403, 0x34a5f010, 0x80043d6, +0x0, 0x3c040001, 0x24845a38, 0xafa00014, +0x8ee60608, 0x8f470228, 0x3c050009, 0xc002403, +0x34a5f00f, 0x8ee201ac, 0x24420001, 0xaee201ac, +0x8ee201ac, 0x8ee2015c, 0x24420001, 0xaee2015c, +0x8ee2015c, 0x8fbf0020, 0x3e00008, 0x27bd0028, +0x3c020001, 0x8c425cd8, 0x27bdffe0, 0x1440000d, +0xafbf0018, 0x3c040001, 0x24845a44, 0x3c050008, +0xafa00010, 0xafa00014, 0x8f860220, 0x34a50499, +0x24020001, 0x3c010001, 0xac225cd8, 0xc002403, +0x3821, 0x8ee204d0, 0x3c030001, 0x771821, +0x946383b2, 0x34420001, 0x10600007, 0xaee204d0, +0x8f820220, 0x3c0308ff, 0x3463ffff, 0x431024, +0x34420008, 0xaf820220, 0x2021, 0xc0052a2, +0x24050004, 0xaf420268, 0x8fbf0018, 0x3e00008, +0x27bd0020, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x3c120001, +0x26521200, 0x3c140001, 0x8e945c50, 0x3c100001, +0x26101120, 0x3c15c000, 0x36b50060, 0x8e8a0000, +0x8eb30000, 0x26a400b, 0x248000a, 0x200f821, +0x0, 0xd, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x80014d6, +0x0, 0x80014d8, 0x3c0a0001, 0x80014d8, +0x3c0a0002, 0x80014d8, 0x0, 0x80024a6, +0x0, 0x80014d8, 0x3c0a0003, 0x80014d8, +0x3c0a0004, 0x8002f8c, 0x0, 0x80014d8, +0x3c0a0005, 0x8003ce8, 0x0, 0x8003c66, +0x0, 0x80014d8, 0x3c0a0006, 0x80014d8, +0x3c0a0007, 0x80014d8, 0x0, 0x80014d8, +0x0, 0x80014d8, 0x0, 0x8002a75, +0x0, 0x80014d8, 0x3c0a000b, 0x80014d8, +0x3c0a000c, 0x80014d8, 0x3c0a000d, 0x800237a, +0x0, 0x8002339, 0x0, 0x80014d8, +0x3c0a000e, 0x8001b3c, 0x0, 0x80024a4, +0x0, 0x80014d8, 0x3c0a000f, 0x80040a7, +0x0, 0x8004091, 0x0, 0x80014d8, +0x3c0a0010, 0x80014ee, 0x0, 0x80014d8, +0x3c0a0011, 0x80014d8, 0x3c0a0012, 0x80014d8, +0x3c0a0013, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x3c030001, +0x34633800, 0x24050080, 0x2404001f, 0x2406ffff, +0x24020001, 0xaf80021c, 0xaf820200, 0xaf820220, +0x3631021, 0xaf8200c0, 0x3631021, 0xaf8200c4, +0x3631021, 0xaf8200c8, 0x27623800, 0xaf8200d0, +0x27623800, 0xaf8200d4, 0x27623800, 0xaf8200d8, +0x27621800, 0xaf8200e0, 0x27621800, 0xaf8200e4, +0x27621800, 0xaf8200e8, 0x27621000, 0xaf8200f0, +0x27621000, 0xaf8200f4, 0x27621000, 0xaf8200f8, +0xaca00000, 0x2484ffff, 0x1486fffd, 0x24a50004, +0x8f830040, 0x3c02f000, 0x621824, 0x3c025000, +0x1062000c, 0x43102b, 0x14400006, 0x3c026000, +0x3c024000, 0x10620008, 0x24020800, 0x8004539, +0x0, 0x10620004, 0x24020800, 0x8004539, +0x0, 0x24020700, 0x3c010001, 0xac225cdc, +0x3e00008, 0x0, 0x27bdffd8, 0xafbf0024, +0xafb00020, 0x8f830054, 0x8f820054, 0x3c010001, +0xac205cc4, 0x8004545, 0x24630064, 0x8f820054, +0x621023, 0x2c420065, 0x1440fffc, 0x0, +0xc004d71, 0x0, 0x24040001, 0x2821, +0x27a60018, 0x34028000, 0xc00498e, 0xa7a20018, +0x8f830054, 0x8f820054, 0x8004556, 0x24630064, +0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, +0x24040001, 0x24050001, 0xc00494c, 0x27a60018, +0x8f830054, 0x8f820054, 0x8004562, 0x24630064, +0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, +0x24040001, 0x24050001, 0xc00494c, 0x27a60018, +0x8f830054, 0x8f820054, 0x800456e, 0x24630064, +0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, +0x24040001, 0x3c060001, 0x24c65da0, 0xc00494c, +0x24050002, 0x8f830054, 0x8f820054, 0x800457b, +0x24630064, 0x8f820054, 0x621023, 0x2c420065, +0x1440fffc, 0x24040001, 0x24050003, 0x3c100001, +0x26105da2, 0xc00494c, 0x2003021, 0x97a60018, +0x3c070001, 0x94e75da0, 0x3c040001, 0x24845ab0, +0xafa00014, 0x96020000, 0x3c05000d, 0x34a50100, +0xc002403, 0xafa20010, 0x97a20018, 0x1040004c, +0x24036040, 0x96020000, 0x3042fff0, 0x1443000a, +0x24020020, 0x3c030001, 0x94635da0, 0x54620009, +0x24027830, 0x24020003, 0x3c010001, 0xac225cc4, +0x80045ac, 0x24020005, 0x3c030001, 0x94635da0, +0x24027830, 0x1462000f, 0x24030010, 0x3c020001, +0x94425da2, 0x3042fff0, 0x1443000a, 0x24020003, +0x3c010001, 0xac225cc4, 0x24020006, 0x3c010001, +0xac225db0, 0x3c010001, 0xac225dbc, 0x80045e6, +0x3c09fff0, 0x3c020001, 0x8c425cc4, 0x3c030001, +0x94635da0, 0x34420001, 0x3c010001, 0xac225cc4, +0x24020015, 0x1462000f, 0x0, 0x3c020001, +0x94425da2, 0x3042fff0, 0x3843f420, 0x2c630001, +0x3842f430, 0x2c420001, 0x621825, 0x10600005, +0x24020003, 0x3c010001, 0xac225dbc, 0x80045e6, +0x3c09fff0, 0x3c030001, 0x94635da0, 0x24027810, +0x1462000b, 0x24020002, 0x3c020001, 0x94425da2, +0x3042fff0, 0x14400006, 0x24020002, 0x24020004, +0x3c010001, 0xac225dbc, 0x80045e6, 0x3c09fff0, +0x3c010001, 0xac225dbc, 0x80045e6, 0x3c09fff0, +0x3c020001, 0x8c425cc4, 0x24030001, 0x3c010001, +0xac235dbc, 0x34420004, 0x3c010001, 0xac225cc4, +0x3c09fff0, 0x3529bdc0, 0x3c060001, 0x8cc65cc4, +0x3c040001, 0x24845ab0, 0x24020001, 0x3c010001, +0xac225ccc, 0x8f820054, 0x3c070001, 0x8ce75dbc, +0x3c030001, 0x94635da0, 0x3c080001, 0x95085da2, +0x3c05000d, 0x34a50100, 0x3c010001, 0xac205cc8, +0x491021, 0x3c010001, 0xac225dac, 0xafa30010, +0xc002403, 0xafa80014, 0x8fbf0024, 0x8fb00020, +0x3e00008, 0x27bd0028, 0x27bdffe8, 0x3c050001, +0x8ca55cc8, 0x24060004, 0x24020001, 0x14a20014, +0xafbf0010, 0x3c020001, 0x8c427e3c, 0x30428000, +0x10400005, 0x3c04000f, 0x3c030001, 0x8c635dbc, +0x8004617, 0x34844240, 0x3c040004, 0x3c030001, +0x8c635dbc, 0x348493e0, 0x24020005, 0x14620016, +0x0, 0x3c04003d, 0x800462f, 0x34840900, +0x3c020001, 0x8c427e38, 0x30428000, 0x10400005, +0x3c04001e, 0x3c030001, 0x8c635dbc, 0x800462a, +0x34848480, 0x3c04000f, 0x3c030001, 0x8c635dbc, +0x34844240, 0x24020005, 0x14620003, 0x0, +0x3c04007a, 0x34841200, 0x3c020001, 0x8c425dac, +0x8f830054, 0x441021, 0x431023, 0x44102b, +0x14400037, 0x0, 0x3c020001, 0x8c425cd0, +0x14400033, 0x0, 0x3c010001, 0x10c00025, +0xac205ce0, 0x3c090001, 0x8d295cc4, 0x24070001, +0x3c044000, 0x3c080001, 0x25087e3c, 0x250afffc, +0x52842, 0x14a00002, 0x24c6ffff, 0x24050008, +0xa91024, 0x10400010, 0x0, 0x14a70008, +0x0, 0x8d020000, 0x441024, 0x1040000a, +0x0, 0x3c010001, 0x800465b, 0xac255ce0, +0x8d420000, 0x441024, 0x10400003, 0x0, +0x3c010001, 0xac275ce0, 0x3c020001, 0x8c425ce0, +0x6182b, 0x2c420001, 0x431024, 0x5440ffe5, +0x52842, 0x8f820054, 0x3c030001, 0x8c635ce0, +0x3c010001, 0xac225dac, 0x1060002a, 0x24020001, +0x3c010001, 0xac255cc8, 0x3c010001, 0xac225ccc, +0x3c020001, 0x8c425ce0, 0x10400022, 0x0, +0x3c020001, 0x8c425ccc, 0x1040000a, 0x24020001, +0x3c010001, 0xac205ccc, 0x3c010001, 0x370821, +0xac2283ac, 0x3c010001, 0xac205d4c, 0x3c010001, +0xac225d04, 0x3c030001, 0x771821, 0x8c6383ac, +0x24020008, 0x10620005, 0x24020001, 0xc004695, +0x0, 0x8004692, 0x0, 0x3c030001, +0x8c635cc8, 0x10620007, 0x2402000e, 0x3c030001, +0x8c637dd0, 0x10620003, 0x0, 0xc004e54, +0x8f840220, 0x8fbf0010, 0x3e00008, 0x27bd0018, +0x27bdffe0, 0x3c02fdff, 0xafbf0018, 0x8ee30000, +0x3c050001, 0x8ca55cc8, 0x3c040001, 0x8c845cf0, +0x3442ffff, 0x621824, 0x14a40008, 0xaee30000, +0x3c030001, 0x771821, 0x8c6383ac, 0x3c020001, +0x8c425cf4, 0x10620008, 0x0, 0x3c020001, +0x571021, 0x8c4283ac, 0x3c010001, 0xac255cf0, +0x3c010001, 0xac225cf4, 0x3c030001, 0x8c635cc8, +0x24020002, 0x10620169, 0x2c620003, 0x10400005, +0x24020001, 0x10620008, 0x0, 0x800481c, +0x0, 0x24020004, 0x106200b1, 0x24020001, +0x800481d, 0x0, 0x3c020001, 0x571021, +0x8c4283ac, 0x2443ffff, 0x2c620008, 0x1040015a, +0x31080, 0x3c010001, 0x220821, 0x8c225ac8, +0x400008, 0x0, 0x3c030001, 0x8c635dbc, +0x24020005, 0x14620014, 0x0, 0x3c020001, +0x8c425cd4, 0x1040000a, 0x24020003, 0xc004822, +0x0, 0x24020002, 0x3c010001, 0x370821, +0xac2283ac, 0x3c010001, 0x80046e0, 0xac205cd4, +0x3c010001, 0x370821, 0xac2283ac, 0x3c010001, +0x800481f, 0xac205c60, 0xc004822, 0x0, +0x3c020001, 0x8c425cd4, 0x3c010001, 0xac205c60, +0x104000dd, 0x24020002, 0x3c010001, 0x370821, +0xac2283ac, 0x3c010001, 0x800481f, 0xac205cd4, +0x3c030001, 0x8c635dbc, 0x24020005, 0x14620003, +0x24020001, 0x3c010001, 0xac225d00, 0xc0049cf, +0x0, 0x3c030001, 0x8c635d00, 0x800478e, +0x24020011, 0x3c050001, 0x8ca55cc8, 0x3c060001, +0x8cc67e3c, 0xc005108, 0x2021, 0x24020005, +0x3c010001, 0xac205cd4, 0x3c010001, 0x370821, +0x800481f, 0xac2283ac, 0x3c040001, 0x24845abc, +0x3c05000f, 0x34a50100, 0x3021, 0x3821, +0xafa00010, 0xc002403, 0xafa00014, 0x800481f, +0x0, 0x8f820220, 0x3c03f700, 0x431025, +0x80047b7, 0xaf820220, 0x8f820220, 0x3c030004, +0x431024, 0x144000a9, 0x24020007, 0x8f830054, +0x3c020001, 0x8c425da4, 0x2463d8f0, 0x431023, +0x2c422710, 0x144000f8, 0x24020001, 0x800481d, +0x0, 0x3c050001, 0x8ca55cc8, 0xc0052a2, +0x2021, 0xc005386, 0x2021, 0x3c030001, +0x8c637e34, 0x46100ea, 0x24020001, 0x3c020008, +0x621024, 0x10400006, 0x0, 0x8f820214, +0x3c03ffff, 0x431024, 0x8004741, 0x3442251f, +0x8f820214, 0x3c03ffff, 0x431024, 0x3442241f, +0xaf820214, 0x8ee20000, 0x3c030200, 0x431025, +0xaee20000, 0x8f820220, 0x2403fffb, 0x431024, +0xaf820220, 0x8f820220, 0x34420002, 0xaf820220, +0x24020008, 0x3c010001, 0x370821, 0xac2283ac, +0x8f820220, 0x3c030004, 0x431024, 0x14400005, +0x0, 0x8f820220, 0x3c03f700, 0x431025, +0xaf820220, 0x3c030001, 0x8c635dbc, 0x24020005, +0x1462000a, 0x0, 0x3c020001, 0x94425da2, +0x24429fbc, 0x2c420004, 0x10400004, 0x24040018, +0x24050002, 0xc004d93, 0x24060020, 0xc0043dd, +0x0, 0x3c010001, 0x800481f, 0xac205d50, +0x3c020001, 0x571021, 0x8c4283ac, 0x2443ffff, +0x2c620008, 0x104000ac, 0x31080, 0x3c010001, +0x220821, 0x8c225ae8, 0x400008, 0x0, +0xc00429b, 0x0, 0x3c010001, 0xac205ccc, +0xaf800204, 0x3c010001, 0xc004822, 0xac207e20, +0x24020001, 0x3c010001, 0xac225ce4, 0x24020002, +0x3c010001, 0x370821, 0x800481f, 0xac2283ac, +0xc00489f, 0x0, 0x3c030001, 0x8c635ce4, +0x24020009, 0x14620090, 0x24020003, 0x3c010001, +0x370821, 0x800481f, 0xac2283ac, 0x3c020001, +0x8c427e38, 0x30424000, 0x10400005, 0x0, +0x8f820044, 0x3c03ffff, 0x800479f, 0x34637fff, +0x8f820044, 0x2403ff7f, 0x431024, 0xaf820044, +0x8f830054, 0x80047b9, 0x24020004, 0x8f830054, +0x3c020001, 0x8c425da4, 0x2463d8f0, 0x431023, +0x2c422710, 0x14400074, 0x24020005, 0x3c010001, +0x370821, 0x800481f, 0xac2283ac, 0x8f820220, +0x3c03f700, 0x431025, 0xaf820220, 0xaf800204, +0x3c010001, 0xac207e20, 0x8f830054, 0x24020006, +0x3c010001, 0x370821, 0xac2283ac, 0x3c010001, +0x800481f, 0xac235da4, 0x8f830054, 0x3c020001, +0x8c425da4, 0x2463fff6, 0x431023, 0x2c42000a, +0x14400059, 0x0, 0x24020007, 0x3c010001, +0x370821, 0x800481f, 0xac2283ac, 0x8f820220, +0x3c04f700, 0x441025, 0xaf820220, 0x8f820220, +0x3c030300, 0x431024, 0x14400005, 0x1821, +0x8f820220, 0x24030001, 0x441025, 0xaf820220, +0x10600043, 0x24020001, 0x8f820214, 0x3c03ffff, +0x3c040001, 0x8c845d98, 0x431024, 0x3442251f, +0xaf820214, 0x24020008, 0x3c010001, 0x370821, +0x1080000b, 0xac2283ac, 0x3c020001, 0x8c425d74, +0x14400007, 0x24020001, 0x3c010001, 0xac227dd0, +0xc004e54, 0x8f840220, 0x800480c, 0x0, +0x8f820220, 0x3c030008, 0x431024, 0x14400017, +0x2402000e, 0x3c010001, 0xac227dd0, 0x8ee20000, +0x2021, 0x3c030200, 0x431025, 0xc005386, +0xaee20000, 0x8f820220, 0x2403fffb, 0x431024, +0xaf820220, 0x8f820220, 0x34420002, 0xc0043dd, +0xaf820220, 0x3c050001, 0x8ca55cc8, 0xc0052a2, +0x2021, 0x800481f, 0x0, 0x3c020001, +0x8c425d74, 0x10400010, 0x0, 0x3c020001, +0x8c425d70, 0x2442ffff, 0x3c010001, 0xac225d70, +0x14400009, 0x24020002, 0x3c010001, 0xac205d74, +0x3c010001, 0x800481f, 0xac225d70, 0x24020001, +0x3c010001, 0xac225ccc, 0x8fbf0018, 0x3e00008, +0x27bd0020, 0x8f820200, 0x8f820220, 0x8f820220, +0x34420004, 0xaf820220, 0x8f820200, 0x3c060001, +0x8cc65cc8, 0x34420004, 0xaf820200, 0x24020002, +0x10c2003a, 0x2cc20003, 0x10400005, 0x24020001, +0x10c20008, 0x0, 0x8004868, 0x0, +0x24020004, 0x10c20013, 0x24020001, 0x8004868, +0x0, 0x3c030001, 0x8c635cb8, 0x3c020001, +0x8c425cc0, 0x3c040001, 0x8c845cdc, 0x3c050001, +0x8ca55cbc, 0xaf860200, 0xaf860220, 0x34630022, +0x441025, 0x451025, 0x34420002, 0x8004867, +0xaf830200, 0x3c030001, 0x8c635d98, 0xaf820200, +0x10600009, 0xaf820220, 0x3c020001, 0x8c425d74, +0x14400005, 0x3c033f00, 0x3c020001, 0x8c425cb0, +0x800485b, 0x346300e0, 0x3c020001, 0x8c425cb0, +0x3c033f00, 0x346300e2, 0x431025, 0xaf820200, +0x3c030001, 0x8c635cb4, 0x3c04f700, 0x3c020001, +0x8c425cc0, 0x3c050001, 0x8ca55cdc, 0x641825, +0x431025, 0x451025, 0xaf820220, 0x3e00008, +0x0, 0x8f820220, 0x3c030001, 0x8c635cc8, +0x34420004, 0xaf820220, 0x24020001, 0x1062000f, +0x0, 0x8f830054, 0x8f820054, 0x24630002, +0x621023, 0x2c420003, 0x10400011, 0x0, +0x8f820054, 0x621023, 0x2c420003, 0x1040000c, +0x0, 0x8004879, 0x0, 0x8f830054, +0x8f820054, 0x8004885, 0x24630007, 0x8f820054, +0x621023, 0x2c420008, 0x1440fffc, 0x0, +0x8f8400e0, 0x30820007, 0x1040000d, 0x0, +0x8f820054, 0x8f8300e0, 0x14830009, 0x24450032, +0x8f820054, 0xa21023, 0x2c420033, 0x10400004, +0x0, 0x8f8200e0, 0x1082fff9, 0x0, +0x8f820220, 0x2403fffd, 0x431024, 0xaf820220, +0x3e00008, 0x0, 0x3c030001, 0x8c635ce4, +0x3c020001, 0x8c425ce8, 0x50620004, 0x2463ffff, +0x3c010001, 0xac235ce8, 0x2463ffff, 0x2c620009, +0x1040009d, 0x31080, 0x3c010001, 0x220821, +0x8c225b08, 0x400008, 0x0, 0x8f820044, +0x34428080, 0xaf820044, 0x8f830054, 0x8004938, +0x24020002, 0x8f830054, 0x3c020001, 0x8c425da8, +0x2463d8f0, 0x431023, 0x2c422710, 0x1440008a, +0x24020003, 0x8004945, 0x0, 0x8f820044, +0x3c03ffff, 0x34637fff, 0x431024, 0xaf820044, +0x8f830054, 0x8004938, 0x24020004, 0x8f830054, +0x3c020001, 0x8c425da8, 0x2463fff6, 0x431023, +0x2c42000a, 0x14400078, 0x24020005, 0x8004945, +0x0, 0x8f820220, 0x3c03f700, 0x431025, +0xaf820220, 0x8f820220, 0x2403fffb, 0x431024, +0xaf820220, 0x8f820220, 0x34420002, 0xaf820220, +0x3c023f00, 0x344200e0, 0xaf820200, 0x8f820200, +0x2403fffd, 0x431024, 0xaf820200, 0x24040001, +0x3405ffff, 0xaf840204, 0x8f830054, 0x8f820054, +0x80048ec, 0x24630001, 0x8f820054, 0x621023, +0x2c420002, 0x1440fffc, 0x0, 0x8f820224, +0x42040, 0xa4102b, 0x1040fff2, 0x0, +0x8f820220, 0x3c03f700, 0x431025, 0xaf820220, +0x8f820214, 0x3c03ffff, 0x431024, 0x3442251f, +0xaf820214, 0x8f820220, 0x2403fffb, 0x431024, +0xaf820220, 0x8f820220, 0x3c04f700, 0x34840008, +0x34420002, 0xaf820220, 0x8f820220, 0x3c033f00, +0x346300e2, 0x441025, 0xaf820220, 0xaf830200, +0x8f8400f0, 0x276217f8, 0x14820002, 0x24850008, +0x27651000, 0x8f8200f4, 0x10a20007, 0x3c038000, +0x34630040, 0x3c020001, 0x24425c70, 0xac820000, +0xac830004, 0xaf8500f0, 0x8f830054, 0x8004938, +0x24020006, 0x8f830054, 0x3c020001, 0x8c425da8, +0x2463fff6, 0x431023, 0x2c42000a, 0x14400022, +0x24020007, 0x8004945, 0x0, 0x8f8200e0, +0xaf8200e4, 0x8f8200e0, 0xaf8200e8, 0x8f820220, +0x34420004, 0xaf820220, 0x8f820220, 0x2403fff7, +0x431024, 0xaf820220, 0x8f820044, 0x34428080, +0xaf820044, 0x8f830054, 0x24020008, 0x3c010001, +0xac225ce4, 0x3c010001, 0x8004947, 0xac235da8, +0x8f830054, 0x3c020001, 0x8c425da8, 0x2463d8f0, +0x431023, 0x2c422710, 0x14400003, 0x24020009, +0x3c010001, 0xac225ce4, 0x3e00008, 0x0, +0x0, 0x0, 0x0, 0x27bdffd8, +0xafb20018, 0x809021, 0xafb3001c, 0xa09821, +0xafb10014, 0xc08821, 0xafb00010, 0x8021, +0xafbf0020, 0xa6200000, 0xc004d4b, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d4b, 0x2021, 0xc004d4b, 0x24040001, +0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, +0x24100010, 0x2501024, 0x10400002, 0x2021, +0x24040001, 0xc004d4b, 0x108042, 0x1600fffa, +0x2501024, 0x24100010, 0x2701024, 0x10400002, +0x2021, 0x24040001, 0xc004d4b, 0x108042, +0x1600fffa, 0x2701024, 0xc004d71, 0x34108000, +0xc004d71, 0x0, 0xc004d2b, 0x0, +0x50400005, 0x108042, 0x96220000, 0x501025, +0xa6220000, 0x108042, 0x1600fff7, 0x0, +0xc004d71, 0x0, 0x8fbf0020, 0x8fb3001c, +0x8fb20018, 0x8fb10014, 0x8fb00010, 0x3e00008, +0x27bd0028, 0x27bdffd8, 0xafb10014, 0x808821, +0xafb20018, 0xa09021, 0xafb3001c, 0xc09821, +0xafb00010, 0x8021, 0xafbf0020, 0xc004d4b, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d4b, 0x2021, 0xc004d4b, +0x24040001, 0xc004d4b, 0x2021, 0xc004d4b, +0x24040001, 0x24100010, 0x2301024, 0x10400002, +0x2021, 0x24040001, 0xc004d4b, 0x108042, +0x1600fffa, 0x2301024, 0x24100010, 0x2501024, +0x10400002, 0x2021, 0x24040001, 0xc004d4b, +0x108042, 0x1600fffa, 0x2501024, 0xc004d4b, +0x24040001, 0xc004d4b, 0x2021, 0x34108000, +0x96620000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d4b, 0x108042, 0x1600fff8, +0x0, 0xc004d71, 0x0, 0x8fbf0020, +0x8fb3001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, +0x3e00008, 0x27bd0028, 0x3c030001, 0x8c635d00, +0x3c020001, 0x8c425d48, 0x27bdffd8, 0xafbf0020, +0xafb1001c, 0x10620003, 0xafb00018, 0x3c010001, +0xac235d48, 0x2463ffff, 0x2c620013, 0x10400349, +0x31080, 0x3c010001, 0x220821, 0x8c225b30, +0x400008, 0x0, 0xc004d71, 0x8021, +0x34028000, 0xa7a20010, 0x27b10010, 0xc004d4b, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d4b, 0x2021, 0xc004d4b, +0x24040001, 0xc004d4b, 0x2021, 0xc004d4b, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d4b, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0xc004d4b, +0x2021, 0x108042, 0x1600fffc, 0x0, +0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d4b, 0x108042, +0x1600fff8, 0x0, 0xc004d71, 0x0, +0x8004d24, 0x24020002, 0x27b10010, 0xa7a00010, +0x8021, 0xc004d4b, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc004d4b, +0x2021, 0xc004d4b, 0x24040001, 0xc004d4b, +0x24040001, 0xc004d4b, 0x2021, 0x24100010, +0x32020001, 0x10400002, 0x2021, 0x24040001, +0xc004d4b, 0x108042, 0x1600fffa, 0x32020001, +0x24100010, 0xc004d4b, 0x2021, 0x108042, +0x1600fffc, 0x0, 0xc004d71, 0x34108000, +0xc004d71, 0x0, 0xc004d2b, 0x0, +0x50400005, 0x108042, 0x96220000, 0x501025, +0xa6220000, 0x108042, 0x1600fff7, 0x0, +0xc004d71, 0x0, 0x97a20010, 0x30428000, +0x144002dc, 0x24020003, 0x8004d24, 0x0, +0x24021200, 0xa7a20010, 0x27b10010, 0x8021, +0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d4b, 0x2021, +0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, +0xc004d4b, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d4b, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0xc004d4b, 0x2021, 0x108042, 0x1600fffc, +0x0, 0xc004d4b, 0x24040001, 0xc004d4b, +0x2021, 0x34108000, 0x96220000, 0x501024, +0x10400002, 0x2021, 0x24040001, 0xc004d4b, +0x108042, 0x1600fff8, 0x0, 0xc004d71, +0x0, 0x8f830054, 0x8004d16, 0x24020004, +0x8f830054, 0x3c020001, 0x8c425db8, 0x2463ff9c, +0x431023, 0x2c420064, 0x1440029e, 0x24020002, +0x3c030001, 0x8c635dbc, 0x10620297, 0x2c620003, +0x14400296, 0x24020011, 0x24020003, 0x10620005, +0x24020004, 0x10620291, 0x2402000f, 0x8004d24, +0x24020011, 0x8004d24, 0x24020005, 0x24020014, +0xa7a20010, 0x27b10010, 0x8021, 0xc004d4b, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d4b, 0x2021, 0xc004d4b, +0x24040001, 0xc004d4b, 0x2021, 0xc004d4b, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d4b, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x32020012, +0x10400002, 0x2021, 0x24040001, 0xc004d4b, +0x108042, 0x1600fffa, 0x32020012, 0xc004d4b, +0x24040001, 0xc004d4b, 0x2021, 0x34108000, +0x96220000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d4b, 0x108042, 0x1600fff8, +0x0, 0xc004d71, 0x0, 0x8f830054, +0x8004d16, 0x24020006, 0x8f830054, 0x3c020001, +0x8c425db8, 0x2463ff9c, 0x431023, 0x2c420064, +0x14400250, 0x24020007, 0x8004d24, 0x0, +0x24020006, 0xa7a20010, 0x27b10010, 0x8021, +0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d4b, 0x2021, +0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, +0xc004d4b, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d4b, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020013, 0x10400002, 0x2021, 0x24040001, +0xc004d4b, 0x108042, 0x1600fffa, 0x32020013, +0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d4b, 0x108042, +0x1600fff8, 0x0, 0xc004d71, 0x0, +0x8f830054, 0x8004d16, 0x24020008, 0x8f830054, +0x3c020001, 0x8c425db8, 0x2463ff9c, 0x431023, +0x2c420064, 0x1440020f, 0x24020009, 0x8004d24, +0x0, 0x27b10010, 0xa7a00010, 0x8021, +0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d4b, 0x2021, +0xc004d4b, 0x24040001, 0xc004d4b, 0x24040001, +0xc004d4b, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d4b, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d4b, 0x108042, 0x1600fffa, 0x32020018, +0xc004d71, 0x34108000, 0xc004d71, 0x0, +0xc004d2b, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004d71, 0x8021, +0x97a20010, 0x27b10010, 0x34420001, 0xa7a20010, +0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d4b, 0x2021, +0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, +0xc004d4b, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d4b, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d4b, 0x108042, 0x1600fffa, 0x32020018, +0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d4b, 0x108042, +0x1600fff8, 0x0, 0xc004d71, 0x0, +0x8f830054, 0x8004d16, 0x2402000a, 0x8f830054, +0x3c020001, 0x8c425db8, 0x2463ff9c, 0x431023, +0x2c420064, 0x1440019b, 0x2402000b, 0x8004d24, +0x0, 0x27b10010, 0xa7a00010, 0x8021, +0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d4b, 0x2021, +0xc004d4b, 0x24040001, 0xc004d4b, 0x24040001, +0xc004d4b, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d4b, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020017, 0x10400002, 0x2021, 0x24040001, +0xc004d4b, 0x108042, 0x1600fffa, 0x32020017, +0xc004d71, 0x34108000, 0xc004d71, 0x0, +0xc004d2b, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004d71, 0x8021, +0x97a20010, 0x27b10010, 0x34420700, 0xa7a20010, +0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d4b, 0x2021, +0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, +0xc004d4b, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d4b, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020017, 0x10400002, 0x2021, 0x24040001, +0xc004d4b, 0x108042, 0x1600fffa, 0x32020017, +0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d4b, 0x108042, +0x1600fff8, 0x0, 0xc004d71, 0x0, +0x8f830054, 0x8004d16, 0x2402000c, 0x8f830054, +0x3c020001, 0x8c425db8, 0x2463ff9c, 0x431023, +0x2c420064, 0x14400127, 0x24020012, 0x8004d24, +0x0, 0x27b10010, 0xa7a00010, 0x8021, +0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d4b, 0x2021, +0xc004d4b, 0x24040001, 0xc004d4b, 0x24040001, +0xc004d4b, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d4b, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020014, 0x10400002, 0x2021, 0x24040001, +0xc004d4b, 0x108042, 0x1600fffa, 0x32020014, +0xc004d71, 0x34108000, 0xc004d71, 0x0, +0xc004d2b, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004d71, 0x8021, +0x97a20010, 0x27b10010, 0x34420010, 0xa7a20010, +0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d4b, 0x2021, +0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, +0xc004d4b, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d4b, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020014, 0x10400002, 0x2021, 0x24040001, +0xc004d4b, 0x108042, 0x1600fffa, 0x32020014, +0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d4b, 0x108042, +0x1600fff8, 0x0, 0xc004d71, 0x0, +0x8f830054, 0x8004d16, 0x24020013, 0x8f830054, +0x3c020001, 0x8c425db8, 0x2463ff9c, 0x431023, +0x2c420064, 0x144000b3, 0x2402000d, 0x8004d24, +0x0, 0x27b10010, 0xa7a00010, 0x8021, +0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d4b, 0x2021, +0xc004d4b, 0x24040001, 0xc004d4b, 0x24040001, +0xc004d4b, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d4b, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d4b, 0x108042, 0x1600fffa, 0x32020018, +0xc004d71, 0x34108000, 0xc004d71, 0x0, +0xc004d2b, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004d71, 0x8021, +0x97a20010, 0x27b10010, 0x3042fffe, 0xa7a20010, +0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d4b, 0x2021, +0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, +0xc004d4b, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d4b, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d4b, 0x108042, 0x1600fffa, 0x32020018, +0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d4b, 0x108042, +0x1600fff8, 0x0, 0xc004d71, 0x0, +0x8f830054, 0x8004d16, 0x2402000e, 0x24020840, +0xa7a20010, 0x27b10010, 0x8021, 0xc004d4b, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d4b, 0x2021, 0xc004d4b, +0x24040001, 0xc004d4b, 0x2021, 0xc004d4b, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d4b, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x32020013, +0x10400002, 0x2021, 0x24040001, 0xc004d4b, +0x108042, 0x1600fffa, 0x32020013, 0xc004d4b, +0x24040001, 0xc004d4b, 0x2021, 0x34108000, +0x96220000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d4b, 0x108042, 0x1600fff8, +0x0, 0xc004d71, 0x0, 0x8f830054, +0x24020010, 0x3c010001, 0xac225d00, 0x3c010001, +0x8004d26, 0xac235db8, 0x8f830054, 0x3c020001, +0x8c425db8, 0x2463ff9c, 0x431023, 0x2c420064, +0x14400004, 0x0, 0x24020011, 0x3c010001, +0xac225d00, 0x8fbf0020, 0x8fb1001c, 0x8fb00018, +0x3e00008, 0x27bd0028, 0x8f850044, 0x8f820044, +0x3c030001, 0x431025, 0x3c030008, 0xaf820044, +0x8f840054, 0x8f820054, 0xa32824, 0x8004d37, +0x24840001, 0x8f820054, 0x821023, 0x2c420002, +0x1440fffc, 0x0, 0x8f820044, 0x3c03fffe, +0x3463ffff, 0x431024, 0xaf820044, 0x8f830054, +0x8f820054, 0x8004d45, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x3e00008, 0xa01021, 0x8f830044, 0x3c02fff0, +0x3442ffff, 0x42480, 0x621824, 0x3c020002, +0x822025, 0x641825, 0xaf830044, 0x8f820044, +0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820044, +0x8f830054, 0x8f820054, 0x8004d5e, 0x24630001, +0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, +0x0, 0x8f820044, 0x3c030001, 0x431025, +0xaf820044, 0x8f830054, 0x8f820054, 0x8004d6b, +0x24630001, 0x8f820054, 0x621023, 0x2c420002, +0x1440fffc, 0x0, 0x3e00008, 0x0, +0x8f820044, 0x3c03fff0, 0x3463ffff, 0x431024, +0xaf820044, 0x8f820044, 0x3c030001, 0x431025, +0xaf820044, 0x8f830054, 0x8f820054, 0x8004d7f, +0x24630001, 0x8f820054, 0x621023, 0x2c420002, +0x1440fffc, 0x0, 0x8f820044, 0x3c03fffe, +0x3463ffff, 0x431024, 0xaf820044, 0x8f830054, +0x8f820054, 0x8004d8d, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x3e00008, 0x0, 0x27bdffc8, 0xafb30024, +0x809821, 0xafb5002c, 0xa0a821, 0xafb20020, +0xc09021, 0x32a2ffff, 0xafbf0030, 0xafb40028, +0xafb1001c, 0xafb00018, 0x14400034, 0xa7b20010, +0x3271ffff, 0x27b20010, 0x8021, 0xc004d4b, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d4b, 0x2021, 0xc004d4b, +0x24040001, 0xc004d4b, 0x2021, 0xc004d4b, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d4b, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x2301024, +0x10400002, 0x2021, 0x24040001, 0xc004d4b, +0x108042, 0x1600fffa, 0x2301024, 0xc004d4b, +0x24040001, 0xc004d4b, 0x2021, 0x34108000, +0x96420000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d4b, 0x108042, 0x12000075, +0x0, 0x8004dc9, 0x0, 0x3274ffff, +0x27b10010, 0xa7a00010, 0x8021, 0xc004d4b, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d4b, 0x2021, 0xc004d4b, +0x24040001, 0xc004d4b, 0x24040001, 0xc004d4b, +0x2021, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d4b, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x2901024, +0x10400002, 0x2021, 0x24040001, 0xc004d4b, +0x108042, 0x1600fffa, 0x2901024, 0xc004d71, +0x34108000, 0xc004d71, 0x0, 0xc004d2b, +0x0, 0x50400005, 0x108042, 0x96220000, +0x501025, 0xa6220000, 0x108042, 0x1600fff7, +0x0, 0xc004d71, 0x0, 0x32a5ffff, +0x24020001, 0x54a20004, 0x24020002, 0x97a20010, +0x8004e14, 0x521025, 0x14a20006, 0x3271ffff, +0x97a20010, 0x121827, 0x431024, 0xa7a20010, +0x3271ffff, 0x27b20010, 0x8021, 0xc004d4b, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d4b, 0x2021, 0xc004d4b, +0x24040001, 0xc004d4b, 0x2021, 0xc004d4b, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d4b, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x2301024, +0x10400002, 0x2021, 0x24040001, 0xc004d4b, +0x108042, 0x1600fffa, 0x2301024, 0xc004d4b, +0x24040001, 0xc004d4b, 0x2021, 0x34108000, +0x96420000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d4b, 0x108042, 0x1600fff8, +0x0, 0xc004d71, 0x0, 0x8fbf0030, +0x8fb5002c, 0x8fb40028, 0x8fb30024, 0x8fb20020, +0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0038, +0x0, 0x0, 0x0, 0x27bdffe8, +0xafbf0010, 0x3c030001, 0x771821, 0x8c6383ac, +0x24020008, 0x1462022c, 0x803021, 0x3c020001, +0x8c425d98, 0x14400033, 0x0, 0x8f850224, +0x38a30020, 0x2c630001, 0x38a20010, 0x2c420001, +0x621825, 0x1460000d, 0x38a30030, 0x2c630001, +0x38a20400, 0x2c420001, 0x621825, 0x14600007, +0x38a30402, 0x2c630001, 0x38a20404, 0x2c420001, +0x621825, 0x10600005, 0x0, 0xc00429b, +0x0, 0x8004e8d, 0x2402000e, 0xc0043dd, +0x0, 0x3c050001, 0x8ca55cc8, 0xc0052a2, +0x2021, 0x3c030001, 0x8c635cc8, 0x24020004, +0x14620005, 0x2403fffb, 0x3c020001, 0x8c425cc4, +0x8004e89, 0x2403fff7, 0x3c020001, 0x8c425cc4, +0x431024, 0x3c010001, 0xac225cc4, 0x2402000e, +0x3c010001, 0xc00429b, 0xac227dd0, 0x8005087, +0x0, 0x8f820220, 0x3c030400, 0x431024, +0x10400027, 0x2403ffbf, 0x8f850224, 0x3c020001, +0x8c427ddc, 0xa32024, 0x431024, 0x1482000c, +0x0, 0x3c020001, 0x8c427de0, 0x24420001, +0x3c010001, 0xac227de0, 0x2c420002, 0x14400008, +0x24020001, 0x3c010001, 0x8004ead, 0xac227e00, +0x3c010001, 0xac207de0, 0x3c010001, 0xac207e00, +0x3c020001, 0x8c427e00, 0x10400006, 0x30a20040, +0x10400004, 0x24020001, 0x3c010001, 0x8004eb8, +0xac227e04, 0x3c010001, 0xac207e04, 0x3c010001, +0xac257ddc, 0x3c010001, 0x8004ec8, 0xac207e10, +0x24020001, 0x3c010001, 0xac227e10, 0x3c010001, +0xac207e00, 0x3c010001, 0xac207de0, 0x3c010001, +0xac207e04, 0x3c010001, 0xac207ddc, 0x3c030001, +0x8c637dd0, 0x3c020001, 0x8c427dd4, 0x10620003, +0x3c020200, 0x3c010001, 0xac237dd4, 0xc21024, +0x10400007, 0x2463ffff, 0x8f820220, 0x24030001, +0x3c010001, 0xac235ccc, 0x8005085, 0x3c03f700, +0x2c62000e, 0x104001a8, 0x31080, 0x3c010001, +0x220821, 0x8c225b80, 0x400008, 0x0, +0x3c010001, 0xac207e00, 0x3c010001, 0xac207de0, +0x3c010001, 0xac207ddc, 0x3c010001, 0xac207e04, +0x3c010001, 0xac207df8, 0x3c010001, 0xac207df0, +0xc00486a, 0xaf800224, 0x24020002, 0x3c010001, +0xac227dd0, 0x3c020001, 0x8c427e10, 0x14400056, +0x3c03fdff, 0x8ee20000, 0x3463ffff, 0x431024, +0xc00429b, 0xaee20000, 0xaf800204, 0x8f820200, +0x2403fffd, 0x431024, 0xaf820200, 0x3c010001, +0xac207e20, 0x8f830054, 0x3c020001, 0x8c427df8, +0x24040001, 0x3c010001, 0xac247e0c, 0x24420001, +0x3c010001, 0xac227df8, 0x2c420004, 0x3c010001, +0xac237df4, 0x14400006, 0x24020003, 0x3c010001, +0xac245ccc, 0x3c010001, 0x8005083, 0xac207df8, +0x3c010001, 0x8005083, 0xac227dd0, 0x8f830054, +0x3c020001, 0x8c427df4, 0x2463d8f0, 0x431023, +0x2c422710, 0x14400003, 0x24020004, 0x3c010001, +0xac227dd0, 0x3c020001, 0x8c427e10, 0x14400026, +0x3c03fdff, 0x8ee20000, 0x3463ffff, 0x431024, +0x8005083, 0xaee20000, 0x3c040001, 0x8c845d9c, +0x3c010001, 0xc00508a, 0xac207de8, 0x3c020001, +0x8c427e1c, 0xaf820204, 0x3c020001, 0x8c427e10, +0x14400015, 0x3c03fdff, 0x8ee20000, 0x3463ffff, +0x431024, 0xaee20000, 0x8f820204, 0x30420030, +0x1440013c, 0x24020002, 0x3c030001, 0x8c637e1c, +0x24020005, 0x3c010001, 0xac227dd0, 0x3c010001, +0x8005083, 0xac237e20, 0x3c020001, 0x8c427e10, +0x10400010, 0x3c03fdff, 0x3c020001, 0x8c425d6c, +0x24420001, 0x3c010001, 0xac225d6c, 0x2c420002, +0x14400131, 0x24020001, 0x3c010001, 0xac225d74, +0x3c010001, 0xac205d6c, 0x3c010001, 0x8005083, +0xac225ccc, 0x8ee20000, 0x3463ffff, 0x431024, +0xaee20000, 0x3c020001, 0x8c427e00, 0x10400122, +0x0, 0x3c020001, 0x8c427ddc, 0x1040011e, +0x0, 0x3c010001, 0xac227e08, 0x24020003, +0x3c010001, 0xac227de0, 0x8005024, 0x24020006, +0x3c010001, 0xac207de8, 0x8f820204, 0x34420040, +0xaf820204, 0x3c020001, 0x8c427e20, 0x24030007, +0x3c010001, 0xac237dd0, 0x34420040, 0x3c010001, +0xac227e20, 0x3c020001, 0x8c427e00, 0x10400005, +0x0, 0x3c020001, 0x8c427ddc, 0x104000f9, +0x24020002, 0x3c050001, 0x24a57de0, 0x8ca20000, +0x2c424e21, 0x104000f3, 0x24020002, 0x3c020001, +0x8c427e04, 0x104000f8, 0x2404ffbf, 0x3c020001, +0x8c427ddc, 0x3c030001, 0x8c637e08, 0x441024, +0x641824, 0x10430004, 0x24020001, 0x3c010001, +0x8005083, 0xac227dd0, 0x24020003, 0xaca20000, +0x24020008, 0x3c010001, 0xac227dd0, 0x3c020001, +0x8c427e0c, 0x1040000c, 0x24020001, 0x3c040001, +0xc005097, 0x8c847ddc, 0x3c020001, 0x8c427e28, +0x14400005, 0x24020001, 0x3c020001, 0x8c427e24, +0x10400006, 0x24020001, 0x3c010001, 0xac225ccc, +0x3c010001, 0x8005083, 0xac207df8, 0x3c020001, +0x8c427df0, 0x3c030001, 0x8c637ddc, 0x2c420001, +0x210c0, 0x30630008, 0x3c010001, 0xac227df0, +0x3c010001, 0xac237dec, 0x8f830054, 0x24020009, +0x3c010001, 0xac227dd0, 0x3c010001, 0x8005083, +0xac237df4, 0x8f830054, 0x3c020001, 0x8c427df4, +0x2463d8f0, 0x431023, 0x2c422710, 0x144000a8, +0x0, 0x3c020001, 0x8c427e00, 0x10400005, +0x0, 0x3c020001, 0x8c427ddc, 0x104000a9, +0x24020002, 0x3c030001, 0x24637de0, 0x8c620000, +0x2c424e21, 0x104000a3, 0x24020002, 0x3c020001, +0x8c427e0c, 0x1040000e, 0x0, 0x3c020001, +0x8c427ddc, 0x3c010001, 0xac207e0c, 0x30420080, +0x1040002f, 0x2402000c, 0x8f820204, 0x30420080, +0x1440000c, 0x24020003, 0x8005011, 0x2402000c, +0x3c020001, 0x8c427ddc, 0x30420080, 0x14400005, +0x24020003, 0x8f820204, 0x30420080, 0x1040001f, +0x24020003, 0xac620000, 0x2402000a, 0x3c010001, +0xac227dd0, 0x3c040001, 0x24847e18, 0x8c820000, +0x3c030001, 0x8c637df0, 0x431025, 0xaf820204, +0x8c830000, 0x3c040001, 0x8c847df0, 0x2402000b, +0x3c010001, 0xac227dd0, 0x641825, 0x3c010001, +0xac237e20, 0x3c050001, 0x24a57de0, 0x8ca20000, +0x2c424e21, 0x1040006f, 0x24020002, 0x3c020001, +0x8c427e10, 0x10400005, 0x0, 0x2402000c, +0x3c010001, 0x8005083, 0xac227dd0, 0x3c020001, +0x8c427e00, 0x1040006c, 0x0, 0x3c040001, +0x8c847ddc, 0x1080005e, 0x30820008, 0x3c030001, +0x8c637dec, 0x10620064, 0x24020003, 0x3c010001, +0xac247e08, 0xaca20000, 0x24020006, 0x3c010001, +0x8005083, 0xac227dd0, 0x8f820200, 0x34420002, +0xaf820200, 0x8f830054, 0x2402000d, 0x3c010001, +0xac227dd0, 0x3c010001, 0xac237df4, 0x8f830054, +0x3c020001, 0x8c427df4, 0x2463d8f0, 0x431023, +0x2c422710, 0x1440003a, 0x0, 0x3c020001, +0x8c427e10, 0x10400029, 0x2402000e, 0x3c030001, +0x8c637e24, 0x3c010001, 0x14600015, 0xac227dd0, +0xc0043dd, 0x0, 0x3c050001, 0x8ca55cc8, +0xc0052a2, 0x2021, 0x3c030001, 0x8c635cc8, +0x24020004, 0x14620005, 0x2403fffb, 0x3c020001, +0x8c425cc4, 0x8005052, 0x2403fff7, 0x3c020001, +0x8c425cc4, 0x431024, 0x3c010001, 0xac225cc4, +0x8ee20000, 0x3c030200, 0x431025, 0xaee20000, +0x8f820224, 0x3c010001, 0xac227e2c, 0x8f820220, +0x2403fffb, 0x431024, 0xaf820220, 0x8f820220, +0x34420002, 0x8005083, 0xaf820220, 0x3c020001, +0x8c427e00, 0x10400005, 0x0, 0x3c020001, +0x8c427ddc, 0x1040000f, 0x24020002, 0x3c020001, +0x8c427de0, 0x2c424e21, 0x1040000a, 0x24020002, +0x3c020001, 0x8c427e00, 0x1040000f, 0x0, +0x3c020001, 0x8c427ddc, 0x1440000b, 0x0, +0x24020002, 0x3c010001, 0x8005083, 0xac227dd0, +0x3c020001, 0x8c427e00, 0x10400003, 0x0, +0xc00429b, 0x0, 0x8f820220, 0x3c03f700, +0x431025, 0xaf820220, 0x8fbf0010, 0x3e00008, +0x27bd0018, 0x3c030001, 0x24637e28, 0x8c620000, +0x10400005, 0x34422000, 0x3c010001, 0xac227e1c, +0x8005095, 0xac600000, 0x3c010001, 0xac247e1c, +0x3e00008, 0x0, 0x27bdffe0, 0x30820030, +0xafbf0018, 0x3c010001, 0xac227e24, 0x14400067, +0x3c02ffff, 0x34421f0e, 0x821024, 0x14400061, +0x24020030, 0x30822000, 0x1040005d, 0x30838000, +0x31a02, 0x30820001, 0x21200, 0x3c040001, +0x8c845d9c, 0x621825, 0x331c2, 0x3c030001, +0x24635d78, 0x30828000, 0x21202, 0x30840001, +0x42200, 0x441025, 0x239c2, 0x61080, +0x431021, 0x471021, 0x90430000, 0x24020001, +0x10620025, 0x0, 0x10600007, 0x24020002, +0x10620013, 0x24020003, 0x1062002c, 0x3c05000f, +0x80050f9, 0x0, 0x8f820200, 0x2403feff, +0x431024, 0xaf820200, 0x8f820220, 0x3c03fffe, +0x3463ffff, 0x431024, 0xaf820220, 0x3c010001, +0xac207e44, 0x3c010001, 0x8005104, 0xac207e4c, +0x8f820200, 0x34420100, 0xaf820200, 0x8f820220, +0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820220, +0x24020100, 0x3c010001, 0xac227e44, 0x3c010001, +0x8005104, 0xac207e4c, 0x8f820200, 0x2403feff, +0x431024, 0xaf820200, 0x8f820220, 0x3c030001, +0x431025, 0xaf820220, 0x3c010001, 0xac207e44, +0x3c010001, 0x8005104, 0xac237e4c, 0x8f820200, +0x34420100, 0xaf820200, 0x8f820220, 0x3c030001, +0x431025, 0xaf820220, 0x24020100, 0x3c010001, +0xac227e44, 0x3c010001, 0x8005104, 0xac237e4c, +0x34a5ffff, 0x3c040001, 0x24845bb8, 0xafa30010, +0xc002403, 0xafa00014, 0x8005104, 0x0, +0x24020030, 0x3c010001, 0xac227e28, 0x8fbf0018, +0x3e00008, 0x27bd0020, 0x0, 0x27bdffc8, +0xafb20028, 0x809021, 0xafb3002c, 0xa09821, +0xafb00020, 0xc08021, 0x3c040001, 0x24845bd0, +0x3c050009, 0x3c020001, 0x8c425cc8, 0x34a59001, +0x2403021, 0x2603821, 0xafbf0030, 0xafb10024, +0xa7a0001a, 0xafb00014, 0xc002403, 0xafa20010, +0x24020002, 0x12620083, 0x2e620003, 0x10400005, +0x24020001, 0x1262000a, 0x0, 0x800529b, +0x0, 0x24020004, 0x126200fa, 0x24020008, +0x126200f9, 0x3c02ffec, 0x800529b, 0x0, +0x3c020001, 0x8c425cc4, 0x30420002, 0x14400004, +0x128940, 0x3c02fffb, 0x3442ffff, 0x2028024, +0x3c010001, 0x310821, 0xac307e3c, 0x3c024000, +0x2021024, 0x1040004e, 0x1023c2, 0x30840030, +0x101382, 0x3042001c, 0x3c030001, 0x24635d08, +0x431021, 0x823821, 0x3c020020, 0x2021024, +0x10400006, 0x24020100, 0x3c010001, 0x310821, +0xac227e40, 0x8005150, 0x3c020080, 0x3c010001, +0x310821, 0xac207e40, 0x3c020080, 0x2021024, +0x10400006, 0x121940, 0x3c020001, 0x3c010001, +0x230821, 0x800515c, 0xac227e48, 0x121140, +0x3c010001, 0x220821, 0xac207e48, 0x94e40000, +0x3c030001, 0x8c635dbc, 0x24020005, 0x10620010, +0xa7a40018, 0x32024000, 0x10400002, 0x34824000, +0xa7a20018, 0x24040001, 0x94e20002, 0x24050004, +0x24e60002, 0x34420001, 0xc00498e, 0xa4e20002, +0x24040001, 0x2821, 0xc00498e, 0x27a60018, +0x3c020001, 0x8c425cc8, 0x24110001, 0x3c010001, +0xac315cd4, 0x14530004, 0x32028000, 0xc00429b, +0x0, 0x32028000, 0x1040011f, 0x0, +0xc00429b, 0x0, 0x3c030001, 0x8c635dbc, +0x24020005, 0x10620118, 0x24020002, 0x3c010001, +0xac315ccc, 0x3c010001, 0x800529b, 0xac225cc8, +0x24040001, 0x24050004, 0x27b0001a, 0xc00498e, +0x2003021, 0x24040001, 0x2821, 0xc00498e, +0x2003021, 0x3c020001, 0x511021, 0x8c427e34, +0x3c040001, 0x8c845cc8, 0x3c03bfff, 0x3463ffff, +0x3c010001, 0xac335cd4, 0x431024, 0x3c010001, +0x310821, 0x109300fa, 0xac227e34, 0x800529b, +0x0, 0x3c022000, 0x2021024, 0x10400005, +0x24020001, 0x3c010001, 0xac225d98, 0x80051ad, +0x128940, 0x3c010001, 0xac205d98, 0x128940, +0x3c010001, 0x310821, 0xac307e38, 0x3c024000, +0x2021024, 0x14400016, 0x0, 0x3c020001, +0x8c425d98, 0x10400008, 0x24040004, 0x24050001, +0xc004d93, 0x24062000, 0x24020001, 0x3c010001, +0x370821, 0xac2283ac, 0x3c020001, 0x511021, +0x8c427e30, 0x3c03bfff, 0x3463ffff, 0x431024, +0x3c010001, 0x310821, 0x8005299, 0xac227e30, +0x3c020001, 0x8c425d98, 0x10400028, 0x3c0300a0, +0x2031024, 0x5443000d, 0x3c020020, 0x3c020001, +0x8c425d9c, 0x24030100, 0x3c010001, 0x310821, +0xac237e44, 0x3c030001, 0x3c010001, 0x310821, +0xac237e4c, 0x80051f0, 0x34420400, 0x2021024, +0x10400008, 0x24030100, 0x3c020001, 0x8c425d9c, +0x3c010001, 0x310821, 0xac237e44, 0x80051f0, +0x34420800, 0x3c020080, 0x2021024, 0x1040002e, +0x3c030001, 0x3c020001, 0x8c425d9c, 0x3c010001, +0x310821, 0xac237e4c, 0x34420c00, 0x3c010001, +0xac225d9c, 0x8005218, 0x24040001, 0x3c020020, +0x2021024, 0x10400006, 0x24020100, 0x3c010001, +0x310821, 0xac227e44, 0x8005201, 0x3c020080, +0x3c010001, 0x310821, 0xac207e44, 0x3c020080, +0x2021024, 0x10400007, 0x121940, 0x3c020001, +0x3c010001, 0x230821, 0xac227e4c, 0x800520f, +0x24040001, 0x121140, 0x3c010001, 0x220821, +0xac207e4c, 0x24040001, 0x2821, 0x27b0001e, +0xc00494c, 0x2003021, 0x24040001, 0x2821, +0xc00494c, 0x2003021, 0x24040001, 0x24050001, +0x27b0001c, 0xc00494c, 0x2003021, 0x24040001, +0x24050001, 0xc00494c, 0x2003021, 0x8005299, +0x0, 0x3c02ffec, 0x3442ffff, 0x2028024, +0x3c020008, 0x2028025, 0x121140, 0x3c010001, +0x220821, 0xac307e38, 0x3c022000, 0x2021024, +0x10400009, 0x0, 0x3c020001, 0x8c425d74, +0x14400005, 0x24020001, 0x3c010001, 0xac225d98, +0x800523a, 0x3c024000, 0x3c010001, 0xac205d98, +0x3c024000, 0x2021024, 0x1440001e, 0x0, +0x3c020001, 0x8c425d98, 0x3c010001, 0xac205ce0, +0x10400007, 0x24022020, 0x3c010001, 0xac225d9c, +0x24020001, 0x3c010001, 0x370821, 0xac2283ac, +0x3c04bfff, 0x121940, 0x3c020001, 0x431021, +0x8c427e30, 0x3c050001, 0x8ca55cc8, 0x3484ffff, +0x441024, 0x3c010001, 0x230821, 0xac227e30, +0x24020001, 0x10a20044, 0x0, 0x8005299, +0x0, 0x3c020001, 0x8c425d98, 0x1040001c, +0x24022000, 0x3c010001, 0xac225d9c, 0x3c0300a0, +0x2031024, 0x14430005, 0x121140, 0x3402a000, +0x3c010001, 0x8005294, 0xac225d9c, 0x3c030001, +0x621821, 0x8c637e38, 0x3c020020, 0x621024, +0x10400004, 0x24022001, 0x3c010001, 0x8005294, +0xac225d9c, 0x3c020080, 0x621024, 0x1040001f, +0x3402a001, 0x3c010001, 0x8005294, 0xac225d9c, +0x3c020020, 0x2021024, 0x10400007, 0x121940, +0x24020100, 0x3c010001, 0x230821, 0xac227e44, +0x8005288, 0x3c020080, 0x121140, 0x3c010001, +0x220821, 0xac207e44, 0x3c020080, 0x2021024, +0x10400006, 0x121940, 0x3c020001, 0x3c010001, +0x230821, 0x8005294, 0xac227e4c, 0x121140, +0x3c010001, 0x220821, 0xac207e4c, 0x3c030001, +0x8c635cc8, 0x24020001, 0x10620003, 0x0, +0xc00429b, 0x0, 0x8fbf0030, 0x8fb3002c, +0x8fb20028, 0x8fb10024, 0x8fb00020, 0x3e00008, +0x27bd0038, 0x27bdffd8, 0xafb20020, 0x809021, +0xafb1001c, 0x8821, 0x24020002, 0xafbf0024, +0xafb00018, 0xa7a00012, 0x10a200d3, 0xa7a00010, +0x2ca20003, 0x10400005, 0x24020001, 0x10a2000a, +0x128140, 0x8005380, 0x2201021, 0x24020004, +0x10a2007d, 0x24020008, 0x10a2007c, 0x122940, +0x8005380, 0x2201021, 0x3c030001, 0x701821, +0x8c637e3c, 0x3c024000, 0x621024, 0x14400009, +0x24040001, 0x3c027fff, 0x3442ffff, 0x628824, +0x3c010001, 0x300821, 0xac317e34, 0x8005380, +0x2201021, 0x24050001, 0xc00494c, 0x27a60010, +0x24040001, 0x24050001, 0xc00494c, 0x27a60010, +0x97a20010, 0x30420004, 0x10400034, 0x3c114000, +0x3c020001, 0x8c425dbc, 0x2443ffff, 0x2c620006, +0x10400034, 0x31080, 0x3c010001, 0x220821, +0x8c225be0, 0x400008, 0x0, 0x24040001, +0x24050011, 0x27b00012, 0xc00494c, 0x2003021, +0x24040001, 0x24050011, 0xc00494c, 0x2003021, +0x97a50012, 0x30a24000, 0x10400002, 0x3c040010, +0x3c040008, 0x3c030001, 0x8005301, 0x30a28000, +0x24040001, 0x24050014, 0x27b00012, 0xc00494c, +0x2003021, 0x24040001, 0x24050014, 0xc00494c, +0x2003021, 0x97a50012, 0x30a21000, 0x10400002, +0x3c040010, 0x3c040008, 0x3c030001, 0x30a20800, +0x54400001, 0x3c030002, 0x3c028000, 0x2221025, +0x641825, 0x800530e, 0x438825, 0x3c110001, +0x2308821, 0x8e317e3c, 0x3c027fff, 0x3442ffff, +0x2228824, 0x3c020001, 0x8c425cd8, 0x1040001d, +0x121140, 0x3c020001, 0x8c425d98, 0x10400002, +0x3c022000, 0x2228825, 0x121140, 0x3c010001, +0x220821, 0x8c227e40, 0x10400003, 0x3c020020, +0x8005322, 0x2228825, 0x3c02ffdf, 0x3442ffff, +0x2228824, 0x121140, 0x3c010001, 0x220821, +0x8c227e48, 0x10400003, 0x3c020080, 0x800532d, +0x2228825, 0x3c02ff7f, 0x3442ffff, 0x2228824, +0x121140, 0x3c010001, 0x220821, 0xac317e34, +0x8005380, 0x2201021, 0x122940, 0x3c030001, +0x651821, 0x8c637e38, 0x3c024000, 0x621024, +0x14400008, 0x3c027fff, 0x3442ffff, 0x628824, +0x3c010001, 0x250821, 0xac317e30, 0x8005380, +0x2201021, 0x3c020001, 0x8c425cd8, 0x10400033, +0x3c11c00c, 0x3c020001, 0x8c425d74, 0x3c04c00c, +0x34842000, 0x3c030001, 0x8c635d98, 0x2102b, +0x21023, 0x441024, 0x10600003, 0x518825, +0x3c022000, 0x2228825, 0x3c020001, 0x451021, +0x8c427e44, 0x10400003, 0x3c020020, 0x800535d, +0x2228825, 0x3c02ffdf, 0x3442ffff, 0x2228824, +0x121140, 0x3c010001, 0x220821, 0x8c227e4c, +0x10400003, 0x3c020080, 0x8005368, 0x2228825, +0x3c02ff7f, 0x3442ffff, 0x2228824, 0x3c020001, +0x8c425d60, 0x10400002, 0x3c020800, 0x2228825, +0x3c020001, 0x8c425d64, 0x10400002, 0x3c020400, +0x2228825, 0x3c020001, 0x8c425d68, 0x10400006, +0x3c020100, 0x800537b, 0x2228825, 0x3c027fff, +0x3442ffff, 0x628824, 0x121140, 0x3c010001, +0x220821, 0xac317e30, 0x2201021, 0x8fbf0024, +0x8fb20020, 0x8fb1001c, 0x8fb00018, 0x3e00008, +0x27bd0028, 0x27bdffd8, 0xafb40020, 0x80a021, +0xafbf0024, 0xafb3001c, 0xafb20018, 0xafb10014, +0xafb00010, 0x8f900200, 0x3c030001, 0x8c635cc8, +0x8f930220, 0x24020002, 0x10620063, 0x2c620003, +0x10400005, 0x24020001, 0x1062000a, 0x141940, +0x8005448, 0x0, 0x24020004, 0x1062005a, +0x24020008, 0x10620059, 0x149140, 0x8005448, +0x0, 0x3c040001, 0x832021, 0x8c847e3c, +0x3c110001, 0x2238821, 0x8e317e34, 0x3c024000, +0x821024, 0x1040003e, 0x3c020008, 0x2221024, +0x10400020, 0x36100002, 0x3c020001, 0x431021, +0x8c427e40, 0x10400005, 0x36100020, 0x36100100, +0x3c020020, 0x80053bd, 0x2228825, 0x2402feff, +0x2028024, 0x3c02ffdf, 0x3442ffff, 0x2228824, +0x141140, 0x3c010001, 0x220821, 0x8c227e48, +0x10400005, 0x3c020001, 0x2629825, 0x3c020080, +0x80053dc, 0x2228825, 0x3c02fffe, 0x3442ffff, +0x2629824, 0x3c02ff7f, 0x3442ffff, 0x80053dc, +0x2228824, 0x2402fedf, 0x2028024, 0x3c02fffe, +0x3442ffff, 0x2629824, 0x3c02ff5f, 0x3442ffff, +0x2228824, 0x3c010001, 0x230821, 0xac207e40, +0x3c010001, 0x230821, 0xac207e48, 0xc00486a, +0x0, 0xaf900200, 0xaf930220, 0x8f820220, +0x2403fffb, 0x431024, 0xaf820220, 0x8f820220, +0x34420002, 0xaf820220, 0x80053f3, 0x141140, +0x8f820200, 0x2403fffd, 0x431024, 0xc00486a, +0xaf820200, 0x3c02bfff, 0x3442ffff, 0xc00429b, +0x2228824, 0x141140, 0x3c010001, 0x220821, +0x8005448, 0xac317e34, 0x149140, 0x3c040001, +0x922021, 0x8c847e38, 0x3c110001, 0x2328821, +0x8e317e30, 0x3c024000, 0x821024, 0x14400011, +0x0, 0x3c020001, 0x8c425d98, 0x14400006, +0x3c02bfff, 0x8f820200, 0x34420002, 0xc00486a, +0xaf820200, 0x3c02bfff, 0x3442ffff, 0xc00429b, +0x2228824, 0x3c010001, 0x320821, 0x8005448, +0xac317e30, 0x3c020001, 0x8c425d98, 0x10400005, +0x3c020020, 0x3c020001, 0x8c425d74, 0x1040002b, +0x3c020020, 0x821024, 0x10400007, 0x36100020, +0x24020100, 0x3c010001, 0x320821, 0xac227e44, +0x8005428, 0x36100100, 0x3c010001, 0x320821, +0xac207e44, 0x2402feff, 0x2028024, 0x3c020080, +0x821024, 0x10400007, 0x141940, 0x3c020001, +0x3c010001, 0x230821, 0xac227e4c, 0x8005439, +0x2629825, 0x141140, 0x3c010001, 0x220821, +0xac207e4c, 0x3c02fffe, 0x3442ffff, 0x2629824, +0xc00486a, 0x0, 0xaf900200, 0xaf930220, +0x8f820220, 0x2403fffb, 0x431024, 0xaf820220, +0x8f820220, 0x34420002, 0xaf820220, 0x141140, +0x3c010001, 0x220821, 0xac317e30, 0x8fbf0024, +0x8fb40020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, +0x8fb00010, 0x3e00008, 0x27bd0028, 0x0 }; +static u_int32_t tigonFwRodata[] = { +0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, +0x2f66776d, 0x61696e2e, 0x632c7620, 0x312e312e, +0x322e3131, 0x20313939, 0x382f3034, 0x2f323720, +0x32323a31, 0x333a3432, 0x20736875, 0x616e6720, +0x45787020, 0x24000000, 0x7468655f, 0x4441574e, +0x0, 0x53544143, 0x4b5f3120, 0x0, +0x42616453, 0x6e64526e, 0x67000000, 0x3f456e71, +0x45767400, 0x3f6e6f51, 0x64457650, 0x0, +0x6576526e, 0x6746756c, 0x6c000000, 0x496c6c43, +0x6f6e6652, 0x78000000, 0x53656e64, 0x436b5375, +0x6d000000, 0x52656376, 0x566c616e, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, +0x2f74696d, 0x65722e63, 0x2c762031, 0x2e312e32, +0x2e382031, 0x3939382f, 0x30372f33, 0x31203137, +0x3a35383a, 0x34352073, 0x6875616e, 0x67204578, +0x70202400, 0x542d446d, 0x61526431, 0x0, +0x542d446d, 0x61424200, 0x542d446d, 0x61320000, +0x3f6e6f51, 0x64547845, 0x0, 0x3f6e6f51, +0x64527845, 0x0, 0x656e714d, 0x45765046, +0x61696c00, 0x656e714d, 0x45764661, 0x696c0000, +0x6661696c, 0x456e454d, 0x0, 0x3f456e71, +0x45767400, 0x3f6e6f51, 0x64457650, 0x0, +0x6576526e, 0x6746756c, 0x6c000000, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, +0x2f636f6d, 0x6d616e64, 0x2e632c76, 0x20312e31, +0x2e322e31, 0x30203139, 0x39382f31, 0x312f3138, +0x2031373a, 0x31313a31, 0x38207368, 0x75616e67, +0x20457870, 0x20240000, 0x3f4d626f, 0x78457674, +0x0, 0x4e4f636f, 0x6d616e64, 0x0, +0x68737465, 0x5f455252, 0x0, 0x412d4572, +0x72427563, 0x0, 0x4552524f, 0x522d4164, +0x64000000, 0x656e714d, 0x45765046, 0x61696c00, +0x656e714d, 0x45764661, 0x696c0000, 0x6661696c, +0x456e454d, 0x0, 0x442d4572, 0x724c6173, +0x74000000, 0x442d4572, 0x72320000, 0x6d437374, +0x4d644552, 0x52000000, 0x70726f6d, 0x4d644552, +0x52000000, 0x46696c74, 0x4d644552, 0x52000000, +0x636d645f, 0x45525200, 0x3f456e71, 0x45767400, +0x3f6e6f51, 0x64457650, 0x0, 0x6576526e, +0x6746756c, 0x6c000000, 0x0, 0x6ea0, +0x7fbc, 0x6e38, 0x8734, 0x82b0, +0x8780, 0x8780, 0x6f54, 0x7694, +0x7f0c, 0x80a8, 0x8074, 0x8780, +0x7e70, 0x80cc, 0x6e64, 0x81cc, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, +0x2f646d61, 0x2e632c76, 0x20312e31, 0x2e322e33, +0x20313939, 0x382f3034, 0x2f323720, 0x32323a31, +0x333a3431, 0x20736875, 0x616e6720, 0x45787020, +0x24000000, 0x646d6172, 0x6441544e, 0x0, +0x646d6177, 0x7241544e, 0x0, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, +0x2f747261, 0x63652e63, 0x2c762031, 0x2e312e32, +0x2e322031, 0x3939382f, 0x30342f32, 0x37203232, +0x3a31333a, 0x35302073, 0x6875616e, 0x67204578, +0x70202400, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, +0x2f646174, 0x612e632c, 0x7620312e, 0x312e322e, +0x32203139, 0x39382f30, 0x342f3237, 0x2032323a, +0x31333a34, 0x30207368, 0x75616e67, 0x20457870, +0x20240000, 0x46575f56, 0x45525349, 0x4f4e3a20, +0x23312046, 0x72692041, 0x70722037, 0x2031373a, +0x35353a34, 0x38205044, 0x54203230, 0x30300000, +0x46575f43, 0x4f4d5049, 0x4c455f54, 0x494d453a, +0x2031373a, 0x35353a34, 0x38000000, 0x46575f43, +0x4f4d5049, 0x4c455f42, 0x593a2064, 0x65767263, +0x73000000, 0x46575f43, 0x4f4d5049, 0x4c455f48, +0x4f53543a, 0x20636f6d, 0x70757465, 0x0, +0x46575f43, 0x4f4d5049, 0x4c455f44, 0x4f4d4149, +0x4e3a2065, 0x6e672e61, 0x6374656f, 0x6e2e636f, +0x6d000000, 0x46575f43, 0x4f4d5049, 0x4c45523a, +0x20676363, 0x20766572, 0x73696f6e, 0x20322e37, +0x2e320000, 0x0, 0x0, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, +0x2f6d656d, 0x2e632c76, 0x20312e31, 0x2e322e32, +0x20313939, 0x382f3034, 0x2f323720, 0x32323a31, +0x333a3434, 0x20736875, 0x616e6720, 0x45787020, +0x24000000, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, +0x2f73656e, 0x642e632c, 0x7620312e, 0x312e322e, +0x31312031, 0x3939382f, 0x31322f32, 0x32203137, +0x3a31373a, 0x35352073, 0x6875616e, 0x67204578, +0x70202400, 0x736e6464, 0x654e6f51, 0x20000000, +0x6e6f454e, 0x515f5458, 0x0, 0x736e6464, +0x744e6f51, 0x20000000, 0x3f6e6f51, 0x64547845, +0x0, 0x756e6b72, 0x64747970, 0x65000000, +0x0, 0xaccc, 0xaccc, 0xad9c, +0xaab0, 0xaab0, 0xad9c, 0xad9c, +0xad9c, 0xad9c, 0xad9c, 0xad9c, +0xad9c, 0xad9c, 0xad9c, 0xad9c, +0xad9c, 0xad9c, 0xad9c, 0xad7c, +0x0, 0xbca8, 0xbca8, 0xbd70, +0xae4c, 0xb058, 0xbd70, 0xbd70, +0xbd70, 0xbd70, 0xbd70, 0xbd70, +0xbd70, 0xbd70, 0xbd70, 0xbd70, +0xbd70, 0xbd70, 0xbd70, 0xbd54, +0xb040, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, +0x2f726563, 0x762e632c, 0x7620312e, 0x312e322e, +0x31392031, 0x3939382f, 0x30372f32, 0x34203231, +0x3a33303a, 0x30352073, 0x6875616e, 0x67204578, +0x70202400, 0x706b5278, 0x45525200, 0x66726d32, +0x4c617267, 0x65000000, 0x72784e6f, 0x52784264, +0x0, 0x72785144, 0x6d614446, 0x0, +0x72785144, 0x6d614246, 0x0, 0x3f6e6f51, +0x64527845, 0x0, 0x706b5278, 0x45525273, +0x0, 0x66726d32, 0x4c726753, 0x0, +0x72784e6f, 0x42645300, 0x3f724264, 0x446d6146, +0x0, 0x3f724a42, 0x64446d46, 0x0, +0x0, 0xf678, 0xf678, 0xf678, +0xf678, 0xf678, 0xf678, 0xf678, +0xf678, 0xf678, 0xf678, 0xf678, +0xf678, 0xf678, 0xf678, 0xf678, +0xf670, 0xf670, 0xf670, 0x572d444d, +0x41456e46, 0x0, 0x0, 0xfdc0, +0x1015c, 0xfddc, 0x1015c, 0x1015c, +0x1015c, 0x1015c, 0x1015c, 0x1015c, +0xf704, 0x1015c, 0x1015c, 0x1015c, +0x1015c, 0x1015c, 0x10154, 0x10154, +0x10154, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, +0x2f6d6163, 0x2e632c76, 0x20312e31, 0x2e322e31, +0x32203139, 0x39382f30, 0x342f3237, 0x2032323a, +0x31333a34, 0x32207368, 0x75616e67, 0x20457870, +0x20240000, 0x6d616374, 0x7841544e, 0x0, +0x4e745379, 0x6e264c6b, 0x0, 0x72656d61, +0x73737274, 0x0, 0x6c696e6b, 0x444f574e, +0x0, 0x656e714d, 0x45765046, 0x61696c00, +0x656e714d, 0x45764661, 0x696c0000, 0x6661696c, +0x456e454d, 0x0, 0x6c696e6b, 0x55500000, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, +0x2f636b73, 0x756d2e63, 0x2c762031, 0x2e312e32, +0x2e322031, 0x3939382f, 0x30342f32, 0x37203232, +0x3a31333a, 0x33392073, 0x6875616e, 0x67204578, +0x70202400, 0x50726f62, 0x65506879, 0x0, +0x6c6e6b41, 0x53535254, 0x0, 0x11b2c, +0x11bc4, 0x11bf8, 0x11c2c, 0x11c58, +0x11c6c, 0x11ca8, 0x1207c, 0x11de4, +0x11e24, 0x11e50, 0x11e90, 0x11ec0, +0x11efc, 0x11f30, 0x1207c, 0x122c0, +0x122d8, 0x12300, 0x12320, 0x12348, +0x12478, 0x124a0, 0x124f4, 0x1251c, +0x0, 0x1278c, 0x1285c, 0x12934, +0x12a04, 0x12a60, 0x12b3c, 0x12b64, +0x12c40, 0x12c68, 0x12e10, 0x12e38, +0x12fe0, 0x131d8, 0x1346c, 0x13380, +0x1346c, 0x13498, 0x13008, 0x131b0, +0x0, 0x13b84, 0x13bc8, 0x13c60, +0x13cac, 0x13d1c, 0x13db4, 0x13de8, +0x13e70, 0x13f08, 0x13fd8, 0x14018, +0x1409c, 0x140c0, 0x141f4, 0x646f4261, +0x73655067, 0x0, 0x0, 0x0, +0x0, 0x73746d61, 0x634c4e4b, 0x0, +0x0, 0x14c38, 0x14c38, 0x14b80, +0x14bc4, 0x14c38, 0x14c38, 0x0, +0x0, 0x0 }; +static u_int32_t tigonFwData[] = { +0x416c7465, +0x6f6e2041, 0x63654e49, 0x43205600, 0x416c7465, +0x6f6e2041, 0x63654e49, 0x43205600, 0x42424242, +0x0, 0x0, 0x0, 0x135418, +0x13e7fc, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x60cf00, +0x60, 0xcf000000, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x3, 0x0, +0x1, 0x0, 0x0, 0x0, +0x1, 0x0, 0x1, 0x0, +0x0, 0x0, 0x0, 0x1, +0x1, 0x0, 0x0, 0x0, +0x0, 0x0, 0x1000000, 0x21000000, +0x12000140, 0x0, 0x0, 0x20000000, +0x120000a0, 0x0, 0x12000060, 0x12000180, +0x120001e0, 0x0, 0x0, 0x0, +0x1, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x2, +0x0, 0x0, 0x30001, 0x1, +0x30201, 0x0, 0x0, 0x0 }; diff --git a/sys/pci/ti_fw2.h b/sys/pci/ti_fw2.h new file mode 100644 index 0000000..e54c920 --- /dev/null +++ b/sys/pci/ti_fw2.h @@ -0,0 +1,5232 @@ +/* + * Generated by Ken's special genfw.c + * Built on Wed Aug 2 17:21:09 MDT 2000 by ken@roadwarrior.plutotech.com + * OS: FreeBSD 5.0-CURRENT + * $FreeBSD$ + */ +static int tigon2FwReleaseMajor = 0xc; +static int tigon2FwReleaseMinor = 0x4; +static int tigon2FwReleaseFix = 0xb; +static u_int32_t tigon2FwStartAddr = 0x00004000; +static u_int32_t tigon2FwTextAddr = 0x00004000; +int tigon2FwTextLen = 0x132f8; +static u_int32_t tigon2FwRodataAddr = 0x000172f8; +int tigon2FwRodataLen = 0x10da; +static u_int32_t tigon2FwDataAddr = 0x000185c0; +int tigon2FwDataLen = 0x17c; +static u_int32_t tigon2FwSbssAddr = 0x0001873c; +int tigon2FwSbssLen = 0xcc; +static u_int32_t tigon2FwBssAddr = 0x00018810; +int tigon2FwBssLen = 0x20c0; +static u_int32_t tigon2FwText[] = { +0x0, +0x10000003, 0x0, 0xd, 0xd, +0x3c1d0002, 0x8fbd8600, 0x3a0f021, 0x3c100000, +0x26104000, 0xc001082, 0x0, 0xd, +0x3c1d0002, 0x8fbd8604, 0x3a0f021, 0x3c100000, +0x26104000, 0xc0018cc, 0x0, 0xd, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x2000008, +0x0, 0x80017d9, 0x3c0a0001, 0x80017d9, +0x3c0a0002, 0x80017d9, 0x0, 0x8002ec4, +0x0, 0x8002e4e, 0x0, 0x80017d9, +0x3c0a0004, 0x80035a3, 0x0, 0x8001b5a, +0x0, 0x8003df3, 0x0, 0x8003d81, +0x0, 0x80017d9, 0x3c0a0006, 0x8003e7a, +0x3c0a0007, 0x80017d9, 0x3c0a0008, 0x80017d9, +0x3c0a0009, 0x8003eeb, 0x0, 0x80030d9, +0x0, 0x80017d9, 0x3c0a000b, 0x80017d9, +0x3c0a000c, 0x80017d9, 0x3c0a000d, 0x8002af6, +0x0, 0x8002a8a, 0x0, 0x80017d9, +0x3c0a000e, 0x800219b, 0x0, 0x8001a69, +0x0, 0x8001b0b, 0x0, 0x80041cb, +0x0, 0x80041b9, 0x0, 0x80017d9, +0x0, 0x8001a1f, 0x0, 0x80017d9, +0x0, 0x80017d9, 0x3c0a0013, 0x80017d9, +0x3c0a0014, 0x27bdffe0, 0x3c1cc000, 0xafbf001c, +0xafb00018, 0x8f820140, 0x24030003, 0xaf8300ec, +0x34420004, 0xaf820140, 0xc002d20, 0x0, +0x3c0100c0, 0xac203ffc, 0xc00184f, 0x0, +0x401821, 0x3c020010, 0x3c010002, 0xac238758, +0x10620025, 0x43102b, 0x14400002, 0x3c020020, +0x3c020008, 0x10620020, 0x24050100, 0x3c040001, +0x248473ac, 0x3c060002, 0x8cc68758, 0x3821, +0xafa00010, 0xc002d3b, 0xafa00014, 0x3c040001, +0x248473b8, 0x24020256, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e773c0, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f830140, +0x3c020020, 0x3c010002, 0xac228758, 0x3c020001, +0x621825, 0xaf830140, 0x24020008, 0x3c010002, +0xac228770, 0x2402001f, 0x3c010002, 0xac228780, +0x24020016, 0x3c010002, 0xac228754, 0x3c05fffe, +0x34a56f08, 0x3c020002, 0x8c428758, 0x3c030002, +0x2463a8d0, 0x3c040002, 0x8c8485c4, 0x431023, +0x14800002, 0x458021, 0x2610fa38, 0x2402f000, +0x2028024, 0xc001871, 0x2002021, 0x2022823, +0x3c040020, 0x821823, 0x651823, 0x247bb000, +0x3c03fffe, 0x3463bf08, 0x363b821, 0x3c0600bf, +0x34c6f000, 0x3c070002, 0x8ce785c0, 0x3c0300bf, +0x3463e000, 0x852023, 0x3c010002, 0xac248764, +0x822023, 0x3c010002, 0xac25874c, 0x52842, +0x3c010002, 0xac228740, 0x27620ffc, 0x3c010002, +0xac228600, 0x27621ffc, 0xdb3023, 0x7b1823, +0x3c010002, 0xac248744, 0x3c010002, 0xac258768, +0x3c010002, 0xac228604, 0xaf860150, 0xaf830250, +0x10e00027, 0x33620fff, 0x10400014, 0x2402028b, +0x3c040001, 0x248473b8, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e773c0, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x3c1d0002, +0x8fbd85cc, 0x3a0f021, 0xc001807, 0x0, +0x3c020002, 0x8c4285d0, 0x3c030002, 0x8c6385d4, +0x2442fe00, 0x24630200, 0x3c010002, 0xac2285d0, +0x3c010002, 0xac2385d4, 0x10000004, 0x0, +0x3c1d0002, 0x8fbd8600, 0x3a0f021, 0x3c020002, +0x8c4285c4, 0x1040000d, 0x26fafa38, 0x3c020002, +0x8c4285d0, 0x3c030002, 0x8c6385d4, 0x3c1a0002, +0x8f5a85d4, 0x2442fa38, 0x246305c8, 0x3c010002, +0xac2285d0, 0x3c010002, 0xac2385d4, 0x3c020002, +0x8c4285c8, 0x14400003, 0x0, 0x3c010002, +0xac2085d0, 0xc001140, 0x0, 0x8fbf001c, +0x8fb00018, 0x3e00008, 0x27bd0020, 0x3c020002, +0x8c4285d0, 0x3c030002, 0x8c6385d4, 0x27bdff98, +0xafb00048, 0x3c100001, 0x8e107d5c, 0xafb20050, +0x3c120000, 0x26524100, 0xafbf0060, 0xafbe005c, +0xafb50058, 0xafb30054, 0xafb1004c, 0xafa20034, +0xafa30030, 0xafa00010, 0xafa00014, 0x8f860040, +0x3c040001, 0x248473cc, 0x24050200, 0x3c010002, +0xac32873c, 0xc002d3b, 0x2003821, 0x8f830040, +0x3c02f000, 0x621824, 0x3c026000, 0x1062001f, +0xa3a0003f, 0x3c040001, 0x248473d4, 0xafa00010, +0xafa00014, 0x8f860040, 0x24050300, 0xc002d3b, +0x2003821, 0x3c040001, 0x248473b8, 0x240202e1, +0xafa20010, 0xafa00014, 0x8f860144, 0x3c070001, +0x24e773c0, 0xc002d3b, 0x3405dead, 0x8f82011c, +0x34420002, 0xaf82011c, 0x8f820220, 0x34420004, +0xaf820220, 0x8f820140, 0x240e0001, 0x3c030001, +0xa3ae003f, 0x431025, 0xaf820140, 0x8f820240, +0x3c030001, 0x431025, 0xaf820240, 0xaf800048, +0x8f820048, 0x14400005, 0x0, 0xaf800048, +0x8f820048, 0x10400004, 0x0, 0xaf800048, +0x10000003, 0x2e02021, 0xaf80004c, 0x2e02021, +0x3c050001, 0xc002da8, 0x34a540f8, 0x3402021, +0xc002da8, 0x240505c8, 0x3c020002, 0x8c428764, +0x3c0d0002, 0x8dad8744, 0x3c030002, 0x8c638740, +0x3c080002, 0x8d08874c, 0x3c090002, 0x8d298768, +0x3c0a0002, 0x8d4a8770, 0x3c0b0002, 0x8d6b8780, +0x3c0c0002, 0x8d8c8754, 0x3c040001, 0x248473e0, +0x24050400, 0xaf42013c, 0x8f42013c, 0x24060001, +0x24070001, 0xaf400000, 0xaf4d0138, 0xaf430144, +0xaf480148, 0xaf49014c, 0xaf4a0150, 0xaf4b0154, +0xaf4c0158, 0x2442ff80, 0xaf420140, 0x24020001, +0xafa20010, 0xc002d3b, 0xafa00014, 0x8f420138, +0xafa20010, 0x8f42013c, 0xafa20014, 0x8f460144, +0x8f470148, 0x3c040001, 0x248473ec, 0xc002d3b, +0x24050500, 0xafb70010, 0xafba0014, 0x8f46014c, +0x8f470150, 0x3c040001, 0x248473f8, 0xc002d3b, +0x24050600, 0x3c020002, 0x8c428758, 0x3603821, +0x3c060002, 0x24c6a8d0, 0x2448ffff, 0x1061824, +0xe81024, 0x43102b, 0x1040001a, 0x24050900, +0x3c040001, 0x24847404, 0xafa80010, 0xc002d3b, +0xafa00014, 0x3c040001, 0x248473b8, 0x2402033a, +0xafa20010, 0xafa00014, 0x8f860144, 0x3c070001, +0x24e773c0, 0xc002d3b, 0x3405dead, 0x8f82011c, +0x34420002, 0xaf82011c, 0x8f820220, 0x34420004, +0xaf820220, 0x8f820140, 0x3c030001, 0x431025, +0xaf820140, 0x8f82000c, 0xafa20010, 0x8f82003c, +0xafa20014, 0x8f860000, 0x8f870004, 0x3c040001, +0x24847410, 0xc002d3b, 0x24051000, 0x8c020220, +0x8c030224, 0x8c060218, 0x8c07021c, 0x3c040001, +0x24847418, 0x24051100, 0xafa20010, 0xc002d3b, +0xafa30014, 0xaf800054, 0xaf80011c, 0x8c020218, +0x30420002, 0x10400009, 0x0, 0x8c020220, +0x3c030002, 0x34630004, 0x431025, 0xaf42000c, +0x8c02021c, 0x10000008, 0x34420004, 0x8c020220, +0x3c030002, 0x34630006, 0x431025, 0xaf42000c, +0x8c02021c, 0x34420006, 0xaf420014, 0x8c020218, +0x30420010, 0x1040000a, 0x0, 0x8c02021c, +0x34420004, 0xaf420010, 0x8c020220, 0x3c03000a, +0x34630004, 0x431025, 0x10000009, 0xaf420008, +0x8c020220, 0x3c03000a, 0x34630006, 0x431025, +0xaf420008, 0x8c02021c, 0x34420006, 0xaf420010, +0x24020001, 0xaf8200a0, 0xaf8200b0, 0x8f830054, +0x8f820054, 0xaf8000d0, 0xaf8000c0, 0x10000002, +0x24630064, 0x8f820054, 0x621023, 0x2c420065, +0x1440fffc, 0x0, 0x8c040208, 0x8c05020c, +0x26e20028, 0xaee20020, 0x24020490, 0xaee20010, +0xaee40008, 0xaee5000c, 0x26e40008, 0x8c820000, +0x8c830004, 0xaf820090, 0xaf830094, 0x8c820018, +0xaf8200b4, 0x9482000a, 0xaf82009c, 0x8f420014, +0xaf8200b0, 0x8f8200b0, 0x30420004, 0x1440fffd, +0x0, 0x8f8200b0, 0x3c03ef00, 0x431024, +0x10400021, 0x0, 0x8f8200b4, 0xafa20010, +0x8f820090, 0x8f830094, 0x3c040001, 0x24847420, +0xafa30014, 0x8f8600b0, 0x8f87009c, 0x3c050001, +0xc002d3b, 0x34a5200d, 0x3c040001, 0x248473b8, +0x240203c4, 0xafa20010, 0xafa00014, 0x8f860144, +0x3c070001, 0x24e773c0, 0xc002d3b, 0x3405dead, +0x8f82011c, 0x34420002, 0xaf82011c, 0x8f820220, +0x34420004, 0xaf820220, 0x8f820140, 0x3c030001, +0x431025, 0xaf820140, 0x96e20472, 0x96e60452, +0x96e70462, 0xafa20010, 0x96e20482, 0x3c040001, +0x2484742c, 0x24051200, 0xc002d3b, 0xafa20014, +0x96f00452, 0x32020001, 0x10400002, 0xb021, +0x24160001, 0x32020002, 0x54400001, 0x36d60002, +0x32020008, 0x54400001, 0x36d60004, 0x32020010, +0x54400001, 0x36d60008, 0x32020020, 0x54400001, +0x36d60010, 0x32020040, 0x54400001, 0x36d60020, +0x32020080, 0x54400001, 0x36d60040, 0x96e60482, +0x30c20200, 0x54400001, 0x36d64000, 0x96e30472, +0x30620200, 0x10400003, 0x30620100, 0x10000003, +0x36d62000, 0x54400001, 0x36d61000, 0x96f00462, +0x32c24000, 0x14400004, 0x3207009b, 0x30c2009b, +0x14e20007, 0x0, 0x32c22000, 0x14400022, +0x32020001, 0x3062009b, 0x10e2001f, 0x32020001, +0x3c040001, 0x24847438, 0x24051300, 0x2003821, +0xafa30010, 0xc002d3b, 0xafa00014, 0x3c040001, +0x248473b8, 0x24020400, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e773c0, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x240e0001, 0x3c030001, 0xa3ae003f, 0x431025, +0xaf820140, 0x32020001, 0x54400001, 0x36d60080, +0x32020002, 0x54400001, 0x36d60100, 0x32020008, +0x54400001, 0x36d60200, 0x32020010, 0x54400001, +0x36d60400, 0x32020080, 0x54400001, 0x36d60800, +0x8c020218, 0x30420200, 0x10400002, 0x3c020008, +0x2c2b025, 0x8c020218, 0x30428000, 0x10400002, +0x3c021000, 0x2c2b025, 0x8c020218, 0x30420800, +0x10400002, 0x3c020080, 0x2c2b025, 0x8c020218, +0x30420400, 0x10400002, 0x3c020100, 0x2c2b025, +0x8c020218, 0x30420100, 0x10400002, 0x3c020200, +0x2c2b025, 0x8c020218, 0x30420080, 0x10400002, +0x3c020400, 0x2c2b025, 0x8c020218, 0x30422000, +0x10400002, 0x3c020010, 0x2c2b025, 0x8c020218, +0x30424000, 0x10400002, 0x3c020020, 0x2c2b025, +0x8c020218, 0x30421000, 0x10400002, 0x3c020040, +0x2c2b025, 0x8ee20498, 0x8ee3049c, 0xaf420160, +0xaf430164, 0x8ee204a0, 0x8ee304a4, 0xaf420168, +0xaf43016c, 0x8ee204a8, 0x8ee304ac, 0xaf420170, +0xaf430174, 0x8ee20428, 0x8ee3042c, 0xaf420178, +0xaf43017c, 0x8ee20448, 0x8ee3044c, 0xaf420180, +0xaf430184, 0x8ee20458, 0x8ee3045c, 0xaf420188, +0xaf43018c, 0x8ee20468, 0x8ee3046c, 0xaf420190, +0xaf430194, 0x8ee20478, 0x8ee3047c, 0xaf420198, +0xaf43019c, 0x8ee20488, 0x8ee3048c, 0xaf4201a0, +0xaf4301a4, 0x8ee204b0, 0x8ee304b4, 0x24040080, +0xaf4201a8, 0xaf4301ac, 0xc002da8, 0x24050080, +0x8c02025c, 0x27440224, 0xaf4201f0, 0x8c020260, +0x24050200, 0x24060008, 0xaf4201f8, 0xc002dbf, +0x0, 0x3c043b9a, 0x3484ca00, 0x3821, +0x24020006, 0x24030002, 0xaf4201f4, 0x240203e8, +0xaf430204, 0xaf430200, 0xaf4401fc, 0xaf420294, +0x24020001, 0xaf430290, 0xaf42029c, 0x3c030002, +0x671821, 0x906385d8, 0x3471021, 0x24e70001, +0xa043022c, 0x2ce2000f, 0x1440fff8, 0x3471821, +0x24e70001, 0x3c080001, 0x350840f8, 0x8f820040, +0x3c040001, 0x24847444, 0x24051400, 0x21702, +0x24420030, 0xa062022c, 0x3471021, 0xa040022c, +0x8c070218, 0x2c03021, 0x240205c8, 0xafa20010, +0xc002d3b, 0xafa80014, 0x3c040001, 0x24847450, +0x3c050000, 0x24a55f28, 0x24060010, 0x27b10030, +0x2203821, 0x27b30034, 0xc00188f, 0xafb30010, +0x3c030002, 0x8c6385c8, 0x1060000a, 0x408021, +0x8fa30030, 0x2405ff00, 0x8fa20034, 0x246400ff, +0x852024, 0x831823, 0x431023, 0xafa20034, +0xafa40030, 0x3c040001, 0x2484745c, 0x3c050000, +0x24a54100, 0x24060108, 0x2203821, 0xc00188f, +0xafb30010, 0x409021, 0x32c20003, 0x3c010002, +0xac32873c, 0x10400059, 0x2203821, 0x8f820050, +0x3c030010, 0x431024, 0x1040002a, 0x0, +0x8c020218, 0x30420040, 0x10400023, 0x24020001, +0x8f820050, 0x8c030218, 0x3c040001, 0x24847468, +0xafa20010, 0xafa30014, 0x8f870040, 0x24051500, +0xc002d3b, 0x2c03021, 0x3c040001, 0x248473b8, +0x24020474, 0xafa20010, 0xafa00014, 0x8f860144, +0x3c070001, 0x24e773c0, 0xc002d3b, 0x3405dead, +0x8f82011c, 0x34420002, 0xaf82011c, 0x8f820220, +0x34420004, 0xaf820220, 0x8f820140, 0x240e0001, +0x3c030001, 0xa3ae003f, 0x431025, 0xaf820140, +0x10000004, 0x0, 0x3c010001, 0x370821, +0xa02240f4, 0x3c040001, 0x24847474, 0x3c050001, +0x24a5a8fc, 0x3c060001, 0x24c6aa20, 0xc53023, +0x8f420010, 0x27b30030, 0x2603821, 0x27b10034, +0x34420a00, 0xaf420010, 0xc00188f, 0xafb10010, +0x3c040001, 0x24847488, 0x3c050001, 0x24a5bfd4, +0x3c060001, 0x24c6c35c, 0xc53023, 0x2603821, +0xaf420108, 0xc00188f, 0xafb10010, 0x3c040001, +0x248474a4, 0x3c050001, 0x24a5c7fc, 0x3c060001, +0x24c6d53c, 0xc53023, 0x2603821, 0x3c010002, +0xac2287b0, 0xc00188f, 0xafb10010, 0x3c040001, +0x248474bc, 0x10000024, 0x24051600, 0x3c040001, +0x248474c4, 0x3c050001, 0x24a5a744, 0x3c060001, +0x24c6a8f4, 0xc53023, 0xc00188f, 0xafb30010, +0x3c040001, 0x248474d4, 0x3c050001, 0x24a5bb10, +0x3c060001, 0x24c6bfcc, 0xc53023, 0x2203821, +0xaf420108, 0xc00188f, 0xafb30010, 0x3c040001, +0x248474e8, 0x3c050001, 0x24a5c364, 0x3c060001, +0x24c6c7f4, 0xc53023, 0x2203821, 0x3c010002, +0xac2287b0, 0xc00188f, 0xafb30010, 0x3c040001, +0x248474fc, 0x24051650, 0x2c03021, 0x3821, +0x3c010002, 0xac2287b4, 0xafa00010, 0xc002d3b, +0xafa00014, 0x32c20020, 0x10400021, 0x27a70030, +0x3c040001, 0x24847508, 0x3c050001, 0x24a5b938, +0x3c060001, 0x24c6bb08, 0xc53023, 0x24022000, +0xaf42001c, 0x27a20034, 0xc00188f, 0xafa20010, +0x21900, 0x31982, 0x3c040800, 0x641825, +0xae430028, 0x24030010, 0xaf43003c, 0x96e30450, +0xaf430040, 0x8f430040, 0x3c040001, 0x2484751c, +0xafa00014, 0xafa30010, 0x8f47001c, 0x24051660, +0x3c010002, 0xac2287ac, 0x10000039, 0x32c60020, +0x8ee20448, 0x8ee3044c, 0xaf43001c, 0x8f42001c, +0x2442e000, 0x2c422001, 0x1440001e, 0x24051700, +0x3c040001, 0x24847528, 0xafa00010, 0xafa00014, +0x8f46001c, 0xc002d3b, 0x3821, 0x3c040001, +0x248473b8, 0x240204dd, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e773c0, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x240e0001, 0x3c030001, 0xa3ae003f, 0x431025, +0xaf820140, 0x3c020000, 0x24425f64, 0x21100, +0x21182, 0x3c030800, 0x431025, 0xae420028, +0x24020008, 0xaf42003c, 0x96e20450, 0xaf420040, +0x8f420040, 0x3c040001, 0x24847534, 0xafa00014, +0xafa20010, 0x8f47001c, 0x24051800, 0x32c60020, +0xc002d3b, 0x0, 0x3c050fff, 0x3c030002, +0x8c6387b0, 0x34a5ffff, 0x2403021, 0x3c020002, +0x8c4287b4, 0x3c040800, 0x651824, 0x31882, +0x641825, 0x451024, 0x21082, 0x441025, +0xacc20080, 0x32c20180, 0x1040007e, 0xacc30020, +0x8f82005c, 0x3c030080, 0x431024, 0x10400021, +0x0, 0x8f820050, 0xafa20010, 0x8f82005c, +0x3c040001, 0x24847540, 0xafa20014, 0x8f870040, +0x24051900, 0xc002d3b, 0x2c03021, 0x3c040001, +0x248473b8, 0x240204fe, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e773c0, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x240e0001, 0x3c030001, 0xa3ae003f, 0x431025, +0xaf820140, 0x8f820050, 0x3c030010, 0x431024, +0x1040002a, 0x0, 0x8c020218, 0x30420040, +0x10400023, 0x24020001, 0x8f820050, 0x8c030218, +0x3c040001, 0x24847468, 0xafa20010, 0xafa30014, +0x8f870040, 0x24052000, 0xc002d3b, 0x2c03021, +0x3c040001, 0x248473b8, 0x2402050c, 0xafa20010, +0xafa00014, 0x8f860144, 0x3c070001, 0x24e773c0, +0xc002d3b, 0x3405dead, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, +0x8f820140, 0x240e0001, 0x3c030001, 0xa3ae003f, +0x431025, 0xaf820140, 0x10000004, 0x0, +0x3c010001, 0x370821, 0xa02240f4, 0x3c040001, +0x2484754c, 0x3c050001, 0x24a5a60c, 0x3c060001, +0x24c6a73c, 0xc53023, 0x8f420008, 0x27b30030, +0x2603821, 0x27b10034, 0x34420e00, 0xaf420008, +0xc00188f, 0xafb10010, 0x3c040001, 0x24847564, +0x3c050001, 0x24a5e844, 0x3c060001, 0x24c6f5fc, +0xc53023, 0x2603821, 0xaf42010c, 0xc00188f, +0xafb10010, 0x3c040001, 0x2484757c, 0x3c050001, +0x24a5fde8, 0x3c060001, 0x24c60588, 0xc53023, +0x2603821, 0x3c010002, 0xac2287c0, 0xc00188f, +0xafb10010, 0x3c040001, 0x24847594, 0x10000027, +0x24052100, 0x3c040001, 0x2484759c, 0x3c050001, +0x24a5a444, 0x3c060001, 0x24c6a604, 0xc53023, +0x27b10030, 0x2203821, 0x27b30034, 0xc00188f, +0xafb30010, 0x3c040001, 0x248475ac, 0x3c050001, +0x24a5d738, 0x3c060001, 0x24c6e83c, 0xc53023, +0x2203821, 0xaf42010c, 0xc00188f, 0xafb30010, +0x3c040001, 0x248475bc, 0x3c050001, 0x24a5fbac, +0x3c060001, 0x24c6fde0, 0xc53023, 0x2203821, +0x3c010002, 0xac2287c0, 0xc00188f, 0xafb30010, +0x3c040001, 0x248475d0, 0x24052150, 0x2c03021, +0x3821, 0x3c010002, 0xac2287cc, 0xafa00010, +0xc002d3b, 0xafa00014, 0x3c110fff, 0x3c030002, +0x8c6387c0, 0x3631ffff, 0x2409821, 0x3c020002, +0x8c4287cc, 0x3c0e0800, 0x711824, 0x31882, +0x6e1825, 0x511024, 0x21082, 0x4e1025, +0xae630038, 0xae620078, 0x8c020218, 0x30420040, +0x14400004, 0x24020001, 0x3c010001, 0x370821, +0xa02240f4, 0x3c040001, 0x248475dc, 0x3c050001, +0x24a5f604, 0x3c060001, 0x24c6f7c4, 0xc53023, +0x27be0030, 0x3c03821, 0x27b50034, 0xc00188f, +0xafb50010, 0x3c010002, 0xac2287b8, 0x511024, +0x21082, 0x3c0e0800, 0x4e1025, 0xae620050, +0x32c22000, 0x10400006, 0x3c03821, 0x3c020000, +0x24425f64, 0x2221024, 0x1000000f, 0x21082, +0x3c040001, 0x248475f0, 0x3c050001, 0x24a5f7cc, +0x3c060001, 0x24c6f9e0, 0xc53023, 0xc00188f, +0xafb50010, 0x3c010002, 0xac2287d0, 0x511024, +0x21082, 0x3c0e0800, 0x4e1025, 0xae620048, +0x32c24000, 0x10400005, 0x27a70030, 0x3c020000, +0x24425f64, 0x1000000e, 0x21100, 0x3c040001, +0x24847608, 0x3c050001, 0x24a5f9e8, 0x3c060001, +0x24c6fba4, 0xc53023, 0x27a20034, 0xc00188f, +0xafa20010, 0x3c010002, 0xac2287c4, 0x21100, +0x21182, 0x3c030800, 0x431025, 0xae420060, +0x3c040001, 0x24847620, 0x3c050001, 0x24a5866c, +0x3c060001, 0x24c68aac, 0xc53023, 0x27b10030, +0x2203821, 0x27b30034, 0xc00188f, 0xafb30010, +0x3c0e0fff, 0x35ceffff, 0x3c040001, 0x2484762c, +0x3c050000, 0x24a5687c, 0x3c060000, 0x24c6699c, +0xc53023, 0x2203821, 0x240f021, 0x3c010002, +0xac228798, 0x4e1024, 0x21082, 0x3c150800, +0x551025, 0xafae0044, 0xafc200b8, 0xc00188f, +0xafb30010, 0x3c040001, 0x24847638, 0x3c050000, +0x24a569a4, 0x3c060000, 0x24c66c24, 0x8fae0044, +0xc53023, 0x2203821, 0x3c010002, 0xac22878c, +0x4e1024, 0x21082, 0x551025, 0xafc200e8, +0xc00188f, 0xafb30010, 0x3c040001, 0x24847650, +0x3c050000, 0x24a56c2c, 0x3c060000, 0x24c66d60, +0x8fae0044, 0xc53023, 0x2203821, 0x3c010002, +0xac228784, 0x4e1024, 0x21082, 0x551025, +0xafc200c0, 0xc00188f, 0xafb30010, 0x3c040001, +0x24847668, 0x3c050001, 0x24a51034, 0x3c060001, +0x24c6110c, 0x8fae0044, 0xc53023, 0x2203821, +0x3c010002, 0xac228790, 0x4e1024, 0x21082, +0x551025, 0xafc200c8, 0xc00188f, 0xafb30010, +0x3c040001, 0x24847674, 0x3c050001, 0x24a5d570, +0x3c060001, 0x24c6d654, 0xc53023, 0x2203821, +0xaf420110, 0xc00188f, 0xafb30010, 0x3c040001, +0x24847684, 0x3c050001, 0x24a5d544, 0x3c060001, +0x24c6d568, 0xc53023, 0x2203821, 0xaf420124, +0xc00188f, 0xafb30010, 0x3c040001, 0x24847694, +0x3c050001, 0x24a5d65c, 0x3c060001, 0x24c6d684, +0xc53023, 0x2203821, 0xaf420120, 0xaf420114, +0xc00188f, 0xafb30010, 0x3c040001, 0x248476a0, +0x3c050001, 0x24a5072c, 0x3c060001, 0x24c60c24, +0xc53023, 0x2203821, 0xaf420118, 0xc00188f, +0xafb30010, 0x8fae0044, 0x3c010002, 0xac2287d4, +0x4e1024, 0x21082, 0x551025, 0xc00451b, +0xafc200d0, 0xc004164, 0x0, 0xc0028c7, +0x0, 0xac000228, 0xac00022c, 0x96e20450, +0x2442ffff, 0xaf420038, 0x96e20460, 0xaf420080, +0x32c24000, 0x14400003, 0x0, 0x96e20480, +0xaf420084, 0x96e70490, 0x50e00001, 0x24070800, +0x24e2ffff, 0xaf420088, 0xaf42007c, 0x24020800, +0x10e20023, 0x32c24000, 0x10400003, 0x24020400, +0x10e2001f, 0x0, 0x3c040001, 0x248476b0, +0x96e60490, 0x24052170, 0x2c03821, 0xafa00010, +0xc002d3b, 0xafa00014, 0x3c040001, 0x248473b8, +0x240205f1, 0xafa20010, 0xafa00014, 0x8f860144, +0x3c070001, 0x24e773c0, 0xc002d3b, 0x3405dead, +0x8f82011c, 0x34420002, 0xaf82011c, 0x8f820220, +0x34420004, 0xaf820220, 0x8f820140, 0x240e0001, +0x3c030001, 0xa3ae003f, 0x431025, 0xaf820140, +0x8f430138, 0x8f440138, 0x24020001, 0xa34205c2, +0xaf430094, 0xaf440098, 0xafa00010, 0xafa00014, +0x8f460080, 0x8f470084, 0x3c040001, 0x248476bc, +0xc002d3b, 0x24052200, 0xc0025c6, 0x3c110800, +0x3c1433d8, 0x3694cb58, 0x3c020800, 0x34420080, +0x3c040001, 0x248476c8, 0x3c050000, 0x24a55ff8, +0x3c060000, 0x24c66014, 0xc53023, 0x27a70030, +0xaf820060, 0x2402ffff, 0xaf820064, 0x27a20034, +0xc00188f, 0xafa20010, 0x3c010002, 0xac228774, +0x21100, 0x21182, 0x511025, 0xc0019e8, +0xae420000, 0x8f820240, 0x3c030001, 0x431025, +0xaf820240, 0x3c020000, 0x24424034, 0xaf820244, +0xaf800240, 0x8f820060, 0x511024, 0x14400005, +0x3c030800, 0x8f820060, 0x431024, 0x1040fffd, +0x0, 0xc004171, 0x8821, 0x3c020100, +0xafa20020, 0x8f530018, 0x240200ff, 0x56620001, +0x26710001, 0x8c020228, 0x1622000e, 0x1330c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x2484735c, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60020, 0x1000003f, +0x34a50100, 0xd71021, 0x8fa30020, 0x8fa40024, +0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, +0x8f45017c, 0x1021, 0x24070004, 0xafa70010, +0xafb10014, 0x8f48000c, 0x24c604c0, 0x2e63021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x24847364, 0x3c050009, +0xafa20014, 0x8fa60020, 0x1000001c, 0x34a50200, +0x8f440160, 0x8f450164, 0x8f43000c, 0xaf510018, +0x8f860120, 0x24020010, 0xafa20010, 0xafb10014, +0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400010, 0x0, 0x8f420340, 0x24420001, +0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x2484736c, 0x3c050009, +0xafa20014, 0x8fa60020, 0x34a50300, 0xc002d3b, +0x2603821, 0x8f4202e4, 0x24420001, 0xaf4202e4, +0x8f4202e4, 0x93a2003f, 0x1040007d, 0x3c020700, +0x34423000, 0xafa20028, 0x8f530018, 0x240200ff, +0x12620002, 0x8821, 0x26710001, 0x8c020228, +0x1622000e, 0x1330c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x2484735c, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60028, 0x1000003f, 0x34a50100, 0xd71021, +0x8fa30028, 0x8fa4002c, 0xac4304c0, 0xac4404c4, +0xc01821, 0x8f440178, 0x8f45017c, 0x1021, +0x24070004, 0xafa70010, 0xafb10014, 0x8f48000c, +0x24c604c0, 0x2e63021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x1440000b, 0x24070008, +0x8f820120, 0xafa20010, 0x8f820124, 0x3c040001, +0x24847364, 0x3c050009, 0xafa20014, 0x8fa60028, +0x1000001c, 0x34a50200, 0x8f440160, 0x8f450164, +0x8f43000c, 0xaf510018, 0x8f860120, 0x24020010, +0xafa20010, 0xafb10014, 0xafa30018, 0x8f42010c, +0x40f809, 0x24c6001c, 0x14400010, 0x0, +0x8f420340, 0x24420001, 0xaf420340, 0x8f420340, +0x8f820120, 0xafa20010, 0x8f820124, 0x3c040001, +0x2484736c, 0x3c050009, 0xafa20014, 0x8fa60028, +0x34a50300, 0xc002d3b, 0x2603821, 0x8f4202f0, +0x24420001, 0xaf4202f0, 0x8f4202f0, 0x3c040001, +0x248476d8, 0xafa00010, 0xafa00014, 0x8fa60028, +0x24052300, 0xc002d3b, 0x3821, 0x3c040001, +0x248473b8, 0x24020656, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e773c0, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x10000004, +0x0, 0x8c020264, 0x10400005, 0x0, +0x8f8200a0, 0x30420004, 0x1440fffa, 0x0, +0x8f820044, 0x34420004, 0xaf820044, 0x8f420308, +0x24420001, 0xaf420308, 0x8f420308, 0x8f8200d8, +0x8f8300d4, 0x431023, 0x2442ff80, 0xaf420090, +0x8f420090, 0x2842ff81, 0x10400006, 0x24020001, +0x8f420090, 0x8f430144, 0x431021, 0xaf420090, +0x24020001, 0xaf42008c, 0x32c20008, 0x10400006, +0x0, 0x8f820214, 0x3c038100, 0x3042ffff, +0x431025, 0xaf820214, 0x3c030002, 0x8c638668, +0x30620002, 0x10400009, 0x30620001, 0x3c040001, +0x248476e4, 0x3c050000, 0x24a57174, 0x3c060000, +0x24c675f8, 0x10000012, 0xc53023, 0x10400009, +0x0, 0x3c040001, 0x248476f4, 0x3c050000, +0x24a57600, 0x3c060000, 0x24c67aa8, 0x10000008, +0xc53023, 0x3c040001, 0x24847704, 0x3c050000, +0x24a56d68, 0x3c060000, 0x24c6716c, 0xc53023, +0x27a70030, 0x27a20034, 0xc00188f, 0xafa20010, +0x3c010002, 0xac228788, 0x3c020002, 0x8c428788, +0x3c030800, 0x21100, 0x21182, 0x431025, +0xae420040, 0x8f8200a0, 0xafa20010, 0x8f8200b0, +0xafa20014, 0x8f86005c, 0x8f87011c, 0x3c040001, +0x24847714, 0x3c010002, 0xac368760, 0x3c010002, +0xac208750, 0x3c010002, 0xac3c8748, 0x3c010002, +0xac3b8778, 0x3c010002, 0xac37877c, 0x3c010002, +0xac3a875c, 0xc002d3b, 0x24052400, 0x8f820200, +0xafa20010, 0x8f820220, 0xafa20014, 0x8f860044, +0x8f870050, 0x3c040001, 0x24847720, 0xc002d3b, +0x24052500, 0x8f830060, 0x74100b, 0x242000a, +0x200f821, 0x0, 0xd, 0x8fbf0060, +0x8fbe005c, 0x8fb50058, 0x8fb30054, 0x8fb20050, +0x8fb1004c, 0x8fb00048, 0x3e00008, 0x27bd0068, +0x27bdffe0, 0x3c040001, 0x2484772c, 0x24052600, +0x3021, 0x3821, 0xafbf0018, 0xafa00010, +0xc002d3b, 0xafa00014, 0x3c040001, 0x248473b8, +0x240206bb, 0xafa20010, 0xafa00014, 0x8f860144, +0x3c070001, 0x24e773c0, 0xc002d3b, 0x3405dead, +0x8f82011c, 0x34420002, 0xaf82011c, 0x8f820220, +0x34420004, 0xaf820220, 0x8f820140, 0x3c030001, +0x431025, 0xaf820140, 0x8fbf0018, 0x3e00008, +0x27bd0020, 0x3e00008, 0x0, 0x3e00008, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x3e00008, 0x0, +0x3e00008, 0x0, 0x27bdfde0, 0xafb00218, +0x27b00018, 0x3c0200bf, 0x3442ffff, 0x50102b, +0x10400015, 0xafbf021c, 0x3c040001, 0x248473b8, +0x240206df, 0xafa20010, 0xafa00014, 0x8f860144, +0x3c070001, 0x24e773c0, 0xc002d3b, 0x3405dead, +0x8f82011c, 0x34420002, 0xaf82011c, 0x8f820220, +0x34420004, 0xaf820220, 0x8f820140, 0x3c030001, +0x431025, 0xaf820140, 0x3c04dead, 0x3484beef, +0x8f820150, 0x3c03001f, 0x3463ffff, 0xafa40018, +0x2028023, 0x2038024, 0x8e020000, 0x1044001e, +0x0, 0xafb00010, 0x8e020000, 0xafa20014, +0x8f860150, 0x8f870250, 0x3c040001, 0x24847734, +0xc002d3b, 0x24052700, 0x3c040001, 0x248473b8, +0x240206ed, 0xafa20010, 0xafa00014, 0x8f860144, +0x3c070001, 0x24e773c0, 0xc002d3b, 0x3405dead, +0x8f82011c, 0x34420002, 0xaf82011c, 0x8f820220, +0x34420004, 0xaf820220, 0x8f820140, 0x3c030001, +0x431025, 0xaf820140, 0x8fbf021c, 0x8fb00218, +0x3e00008, 0x27bd0220, 0x27bdffe0, 0x3c06abba, +0x34c6babe, 0xafb00018, 0x3c100004, 0x3c07007f, +0x34e7ffff, 0xafbf001c, 0x102840, 0x8e040000, +0x8ca30000, 0xaca00000, 0xae060000, 0x8ca20000, +0xaca30000, 0x10460005, 0xae040000, 0xa08021, +0xf0102b, 0x1040fff5, 0x102840, 0x3c040001, +0x24847740, 0x24052800, 0x2003021, 0x3821, +0xafa00010, 0xc002d3b, 0xafa00014, 0x2001021, +0x8fbf001c, 0x8fb00018, 0x3e00008, 0x27bd0020, +0x8c020224, 0x3047003f, 0x10e00010, 0x803021, +0x2821, 0x24030020, 0xe31024, 0x10400002, +0x63042, 0xa62821, 0x31842, 0x1460fffb, +0xe31024, 0x2402f000, 0xa22824, 0x3402ffff, +0x45102b, 0x14400003, 0x3c020001, 0x10000008, +0x3c020001, 0x3442ffff, 0x851823, 0x43102b, +0x14400003, 0xa01021, 0x3c02fffe, 0x821021, +0x3e00008, 0x0, 0x27bdffd0, 0xafb50028, +0x8fb50040, 0xafb20020, 0xa09021, 0xafb1001c, +0x24c60003, 0xafbf002c, 0xafb30024, 0xafb00018, +0x8ea20000, 0x2403fffc, 0xc38024, 0x50102b, +0x1440001b, 0xe08821, 0x8e330000, 0xafb00010, +0x8ea20000, 0xafa20014, 0x8e270000, 0x24053000, +0xc002d3b, 0x2403021, 0x8e230000, 0x702021, +0x64102b, 0x10400007, 0x2402821, 0x8ca20000, +0xac620000, 0x24630004, 0x64102b, 0x1440fffb, +0x24a50004, 0x8ea20000, 0x501023, 0xaea20000, +0x8e220000, 0x501021, 0x1000000b, 0xae220000, +0x2402002d, 0xa0820000, 0xafb00010, 0x8ea20000, +0x2409821, 0xafa20014, 0x8e270000, 0x24053100, +0xc002d3b, 0x2603021, 0x2601021, 0x8fbf002c, +0x8fb50028, 0x8fb30024, 0x8fb20020, 0x8fb1001c, +0x8fb00018, 0x3e00008, 0x27bd0030, 0x27bdffe8, +0x3c1cc000, 0x3c05fffe, 0x3c030002, 0x8c638740, +0x3c040002, 0x8c84874c, 0x34a5bf08, 0x24021ffc, +0x3c010002, 0xac2285d0, 0x3c0200c0, 0x3c010002, +0xac2285d4, 0x3c020020, 0xafbf0010, 0x3c0100c0, +0xac201ffc, 0x431023, 0x441023, 0x245bb000, +0x365b821, 0x3c1d0002, 0x8fbd85cc, 0x3a0f021, +0x3c0400c0, 0x34840200, 0x3c1a00c0, 0x3c0300c0, +0x346307c8, 0x24021dfc, 0x3c010002, 0xac2285d0, +0x24021834, 0x3c010002, 0xac2485d4, 0x3c010002, +0xac2285d0, 0x3c010002, 0xac2385d4, 0xc0018f9, +0x375a0200, 0x8fbf0010, 0x3e00008, 0x27bd0018, +0x27bdffc8, 0x3c040001, 0x2484774c, 0x24053200, +0x3c020002, 0x8c4285d0, 0x3c030002, 0x8c6385d4, +0x3021, 0x3603821, 0xafbf0030, 0xafb3002c, +0xafb20028, 0xafb10024, 0xafb00020, 0xafa2001c, +0xafa30018, 0xafb70010, 0xc002d3b, 0xafba0014, +0xc001a1b, 0x0, 0x8f820240, 0x34420004, +0xaf820240, 0x24020001, 0xaf420000, 0x3c020001, +0x571021, 0x904240f4, 0x10400093, 0x2403fffc, +0x3c100001, 0x2610b47b, 0x3c120001, 0x2652b044, +0x2121023, 0x438024, 0x8fa3001c, 0x3c040001, +0x24847758, 0x70102b, 0x1440001a, 0x27b30018, +0x8fb10018, 0x24053000, 0x2403021, 0xafb00010, +0xafa30014, 0xc002d3b, 0x2203821, 0x8fa30018, +0x702021, 0x64102b, 0x10400007, 0x2403021, +0x8cc20000, 0xac620000, 0x24630004, 0x64102b, +0x1440fffb, 0x24c60004, 0x8fa2001c, 0x501023, +0xafa2001c, 0x8e620000, 0x501021, 0x1000000a, +0xae620000, 0x2408821, 0x24053100, 0xafb00010, +0xafa30014, 0x8fa70018, 0x2203021, 0x2402002d, +0xc002d3b, 0xa0820000, 0x24070020, 0x8fa3001c, +0x3c040001, 0x24847774, 0x24120020, 0x3c010002, +0xac31876c, 0x2c620020, 0x1440001d, 0x27b10018, +0x8fb00018, 0x24053000, 0x3c060002, 0x24c68810, +0xafa70010, 0xafa30014, 0xc002d3b, 0x2003821, +0x8fa30018, 0x3c040002, 0x24848810, 0x24650020, +0x65102b, 0x10400007, 0x0, 0x8c820000, +0xac620000, 0x24630004, 0x65102b, 0x1440fffb, +0x24840004, 0x8fa2001c, 0x521023, 0xafa2001c, +0x8e220000, 0x521021, 0x1000000b, 0xae220000, +0x3c100002, 0x26108810, 0x24053100, 0xafa70010, +0xafa30014, 0x8fa70018, 0x2003021, 0x2402002d, +0xc002d3b, 0xa0820000, 0x24070020, 0x3c040001, +0x24847788, 0x8fa3001c, 0x24120020, 0x3c010002, +0xac3087a0, 0x2c620020, 0x1440001d, 0x27b10018, +0x8fb00018, 0x24053000, 0x3c060002, 0x24c68830, +0xafa70010, 0xafa30014, 0xc002d3b, 0x2003821, +0x8fa30018, 0x3c040002, 0x24848830, 0x24650020, +0x65102b, 0x10400007, 0x0, 0x8c820000, +0xac620000, 0x24630004, 0x65102b, 0x1440fffb, +0x24840004, 0x8fa2001c, 0x521023, 0xafa2001c, +0x8e220000, 0x521021, 0x1000000b, 0xae220000, +0x3c100002, 0x26108830, 0x24053100, 0xafa70010, +0xafa30014, 0x8fa70018, 0x2003021, 0x2402002d, +0xc002d3b, 0xa0820000, 0x3c010002, 0xac30879c, +0x10000031, 0x0, 0x3c100001, 0x26108667, +0x3c120001, 0x265284d8, 0x2121023, 0x438024, +0x8fa3001c, 0x3c040001, 0x2484779c, 0x70102b, +0x1440001a, 0x27b30018, 0x8fb10018, 0x24053000, +0x2403021, 0xafb00010, 0xafa30014, 0xc002d3b, +0x2203821, 0x8fa30018, 0x702021, 0x64102b, +0x10400007, 0x2403021, 0x8cc20000, 0xac620000, +0x24630004, 0x64102b, 0x1440fffb, 0x24c60004, +0x8fa2001c, 0x501023, 0xafa2001c, 0x8e620000, +0x501021, 0x1000000a, 0xae620000, 0x2408821, +0x24053100, 0xafb00010, 0xafa30014, 0x8fa70018, +0x2203021, 0x2402002d, 0xc002d3b, 0xa0820000, +0x3c010002, 0xac31876c, 0x3c030002, 0x8c63876c, +0x24020400, 0xaf820070, 0x60f809, 0x0, +0x8fbf0030, 0x8fb3002c, 0x8fb20028, 0x8fb10024, +0x8fb00020, 0x3e00008, 0x27bd0038, 0x27bdffe0, +0xafbf0018, 0x8f820040, 0x3c03f000, 0x431024, +0x3c036000, 0x14430008, 0x240201f9, 0x8f820050, +0x2403ff80, 0x431024, 0x34420055, 0xaf820050, +0x10000014, 0x0, 0x3c040001, 0x2484785c, +0xafa20010, 0xafa00014, 0x8f860144, 0x3c070001, +0x24e7786c, 0xc002d3b, 0x3405dead, 0x8f82011c, +0x34420002, 0xaf82011c, 0x8f820220, 0x34420004, +0xaf820220, 0x8f820140, 0x3c030001, 0x431025, +0xaf820140, 0x8f820054, 0x244203e8, 0xaf820058, +0x240201f4, 0xaf4200e0, 0x24020004, 0xaf4200e8, +0x24020002, 0xaf4001b0, 0xaf4000e4, 0xaf4200dc, +0xaf4000d8, 0xaf4000d4, 0x8fbf0018, 0xaf4000d0, +0x3e00008, 0x27bd0020, 0x8f820054, 0x24420005, +0x3e00008, 0xaf820078, 0x27bdffe8, 0xafbf0010, +0x8f820054, 0x244203e8, 0xaf820058, 0x3c020800, +0x2c21024, 0x10400004, 0x3c02f7ff, 0x3442ffff, +0x2c2b024, 0x36940040, 0x3c020002, 0x8c42867c, +0x10400017, 0x3c020200, 0x3c030002, 0x8c6387d8, +0x10600016, 0x282a025, 0x3c020002, 0x8c428708, +0x14400012, 0x3c020200, 0x3c020002, 0x8c428668, +0x30420003, 0x1440000d, 0x3c020200, 0x8f830224, +0x3c020002, 0x8c42a8ac, 0x10620008, 0x3c020200, +0xc00430b, 0x0, 0x10000004, 0x3c020200, +0xc00470e, 0x0, 0x3c020200, 0x2c21024, +0x10400003, 0x0, 0xc002058, 0x0, +0x8f4200d8, 0x8f4300dc, 0x24420001, 0xaf4200d8, +0x43102b, 0x14400003, 0x0, 0xaf4000d8, +0x36940080, 0x8c030238, 0x1060000c, 0x0, +0x8f4201b0, 0x244203e8, 0xaf4201b0, 0x43102b, +0x14400006, 0x0, 0x934205c5, 0x14400003, +0x0, 0xc001eac, 0x0, 0x8fbf0010, +0x3e00008, 0x27bd0018, 0x3e00008, 0x0, +0x27bdffd8, 0xafbf0020, 0x8f43002c, 0x8f420038, +0x10620059, 0x0, 0x3c020001, 0x571021, +0x904240f0, 0x10400026, 0x24070008, 0x8f440170, +0x8f450174, 0x8f48000c, 0x8f860120, 0x24020020, +0xafa20010, 0xafa30014, 0xafa80018, 0x8f42010c, +0x40f809, 0x24c6001c, 0x14400011, 0x24020001, +0x3c010001, 0x370821, 0xa02240f0, 0x8f820124, +0xafa20010, 0x8f820128, 0x3c040001, 0x24847840, +0xafa20014, 0x8f46002c, 0x8f870120, 0x3c050009, +0xc002d3b, 0x34a50900, 0x1000005d, 0x0, +0x8f420300, 0x24420001, 0xaf420300, 0x8f420300, +0x8f42002c, 0xa34005c1, 0x10000027, 0xaf420038, +0x8f440170, 0x8f450174, 0x8f43002c, 0x8f48000c, +0x8f860120, 0x24020080, 0xafa20010, 0xafa30014, +0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400011, 0x24020001, 0x3c010001, 0x370821, +0xa02240f1, 0x8f820124, 0xafa20010, 0x8f820128, +0x3c040001, 0x2484784c, 0xafa20014, 0x8f46002c, +0x8f870120, 0x3c050009, 0xc002d3b, 0x34a51100, +0x10000037, 0x0, 0x8f420300, 0x8f43002c, +0x24420001, 0xaf420300, 0x8f420300, 0x24020001, +0xa34205c1, 0xaf430038, 0x3c010001, 0x370821, +0xa02040f1, 0x3c010001, 0x370821, 0xa02040f0, +0x10000027, 0xaf400034, 0x934205c1, 0x1040001e, +0x0, 0xa34005c1, 0x8f820040, 0x30420001, +0x14400008, 0x2021, 0x8c030104, 0x24020001, +0x50620005, 0x24040001, 0x8c020264, 0x10400003, +0x801021, 0x24040001, 0x801021, 0x10400007, +0x0, 0x8f42030c, 0x24420001, 0xaf42030c, +0x8f42030c, 0x10000008, 0x0, 0x8f820044, +0x34420004, 0xaf820044, 0x8f420308, 0x24420001, +0xaf420308, 0x8f420308, 0x3c010001, 0x370821, +0xa02040f0, 0x3c010001, 0x370821, 0xa02040f1, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x3c03ff7f, 0x3463ffff, +0x431024, 0xaf820060, 0x8f420000, 0x10400004, +0x0, 0xaf80004c, 0x10000002, 0x0, +0xaf800048, 0x8fbf0020, 0x3e00008, 0x27bd0028, +0x3e00008, 0x0, 0x27bdffd8, 0xafbf0020, +0x8f430044, 0x8f42007c, 0x10620029, 0x24070008, +0x8f440168, 0x8f45016c, 0x8f48000c, 0x8f860120, +0x24020040, 0xafa20010, 0xafa30014, 0xafa80018, +0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, +0x24020001, 0x3c010001, 0x370821, 0xa02240f2, +0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, +0x24847854, 0xafa20014, 0x8f460044, 0x8f870120, +0x3c050009, 0xc002d3b, 0x34a51300, 0x1000000f, +0x0, 0x8f420304, 0x24420001, 0xaf420304, +0x8f420304, 0x8f420044, 0xaf42007c, 0x3c010001, +0x370821, 0xa02040f2, 0x10000004, 0xaf400078, +0x3c010001, 0x370821, 0xa02040f2, 0x8f420000, +0x10400007, 0x0, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x3c03feff, 0x3463ffff, 0x431024, +0xaf820060, 0x8f420000, 0x10400004, 0x0, +0xaf80004c, 0x10000002, 0x0, 0xaf800048, +0x8fbf0020, 0x3e00008, 0x27bd0028, 0x3e00008, +0x0, 0x3c020002, 0x8c42867c, 0x27bdffa8, +0xafbf0050, 0xafbe004c, 0xafb50048, 0xafb30044, +0xafb20040, 0xafb1003c, 0xafb00038, 0x8f900044, +0x104000d5, 0x0, 0x8f4200d0, 0x24430001, +0x2842000b, 0x144000e4, 0xaf4300d0, 0x8f420004, +0x30420002, 0x1440009c, 0xaf4000d0, 0x8f420004, +0x3c030002, 0x8c63866c, 0x34420002, 0xaf420004, +0x24020001, 0x14620003, 0x3c020600, 0x10000002, +0x34423000, 0x34421000, 0xafa20020, 0x8f4a0018, +0xafaa0034, 0x27aa0020, 0xafaa002c, 0x8faa0034, +0x240200ff, 0x11420002, 0x1821, 0x25430001, +0x8c020228, 0x609821, 0x1662000e, 0x3c050009, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x8fa70034, 0x3c040001, 0x24847824, +0xafa00014, 0xafa20010, 0x8fa60020, 0x10000070, +0x34a50500, 0x8faa0034, 0xa38c0, 0xf71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0x8f830054, 0x8f820054, 0x247103e8, 0x2221023, +0x2c4203e9, 0x1040001b, 0xa821, 0xe09021, +0x265e04c0, 0x8f440178, 0x8f45017c, 0x2401821, +0x240a0004, 0xafaa0010, 0xafb30014, 0x8f48000c, +0x1021, 0x2fe3021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x54400006, 0x24150001, +0x8f820054, 0x2221023, 0x2c4203e9, 0x1440ffe9, +0x0, 0x32a200ff, 0x54400018, 0xaf530018, +0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, +0x8f820120, 0x8faa002c, 0x8fa70034, 0xafa20010, +0x8f820124, 0x3c040001, 0x24847830, 0xafa20014, +0x8d460000, 0x3c050009, 0x10000035, 0x34a50600, +0x8f420308, 0x24150001, 0x24420001, 0xaf420308, +0x8f420308, 0x1000001e, 0x32a200ff, 0x8f830054, +0x8f820054, 0x247103e8, 0x2221023, 0x2c4203e9, +0x10400016, 0xa821, 0x3c1e0020, 0x24120010, +0x8f42000c, 0x8f440160, 0x8f450164, 0x8f860120, +0xafb20010, 0xafb30014, 0x5e1025, 0xafa20018, +0x8f42010c, 0x24070008, 0x40f809, 0x24c6001c, +0x1440ffe3, 0x0, 0x8f820054, 0x2221023, +0x2c4203e9, 0x1440ffee, 0x0, 0x32a200ff, +0x14400011, 0x3c050009, 0x8f420378, 0x24420001, +0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, +0x8fa70034, 0xafa20010, 0x8f820124, 0x3c040001, +0x24847838, 0xafa20014, 0x8d460000, 0x34a50700, +0xc002d3b, 0x0, 0x8f4202ec, 0x24420001, +0xaf4202ec, 0x8f4202ec, 0x8f420004, 0x30420001, +0x50400029, 0x36100040, 0x3c020400, 0x2c21024, +0x10400013, 0x2404ffdf, 0x8f420250, 0x8f430254, +0x8f4401b4, 0x14640006, 0x36100040, 0x8f420270, +0x8f430274, 0x8f4401b8, 0x10640007, 0x2402ffdf, +0x8f420250, 0x8f430254, 0x8f440270, 0x8f450274, +0x10000012, 0x3a100020, 0x1000002b, 0x2028024, +0x8f420250, 0x8f430254, 0x8f4501b4, 0x14650006, +0x2048024, 0x8f420270, 0x8f430274, 0x8f4401b8, +0x50640021, 0x36100040, 0x8f420250, 0x8f430254, +0x8f440270, 0x8f450274, 0x3a100040, 0xaf4301b4, +0x10000019, 0xaf4501b8, 0x8f4200d4, 0x24430001, +0x10000011, 0x28420033, 0x8f420004, 0x30420001, +0x10400009, 0x3c020400, 0x2c21024, 0x10400004, +0x2402ffdf, 0x2028024, 0x1000000b, 0x36100040, +0x10000009, 0x36100060, 0x8f4200d4, 0x36100040, +0x24430001, 0x284201f5, 0x14400003, 0xaf4300d4, +0xaf4000d4, 0x3a100020, 0xaf900044, 0x2402ff7f, +0x282a024, 0x8fbf0050, 0x8fbe004c, 0x8fb50048, +0x8fb30044, 0x8fb20040, 0x8fb1003c, 0x8fb00038, +0x3e00008, 0x27bd0058, 0x3e00008, 0x0, +0x3c020002, 0x8c42867c, 0x27bdffb0, 0xafbf0048, +0xafbe0044, 0xafb50040, 0xafb3003c, 0xafb20038, +0xafb10034, 0x104000c9, 0xafb00030, 0x8f4200d0, +0x24430001, 0x2842000b, 0x144000dd, 0xaf4300d0, +0x8f420004, 0x30420002, 0x14400097, 0xaf4000d0, +0x8f420004, 0x3c030002, 0x8c63866c, 0x34420002, +0xaf420004, 0x24020001, 0x14620003, 0x3c020600, +0x10000002, 0x34423000, 0x34421000, 0xafa20020, +0x1821, 0x8f5e0018, 0x27aa0020, 0x240200ff, +0x13c20002, 0xafaa002c, 0x27c30001, 0x8c020228, +0x609021, 0x1642000e, 0x1e38c0, 0x8f42033c, +0x24420001, 0xaf42033c, 0x8f42033c, 0x8c020228, +0x3c040001, 0x24847824, 0x3c050009, 0xafa00014, +0xafa20010, 0x8fa60020, 0x1000006d, 0x34a50500, +0xf71021, 0x8fa30020, 0x8fa40024, 0xac4304c0, +0xac4404c4, 0x8f830054, 0x8f820054, 0x247003e8, +0x2021023, 0x2c4203e9, 0x1040001b, 0x9821, +0xe08821, 0x263504c0, 0x8f440178, 0x8f45017c, +0x2201821, 0x240a0004, 0xafaa0010, 0xafb20014, +0x8f48000c, 0x1021, 0x2f53021, 0xafa80018, +0x8f48010c, 0x24070008, 0xa32821, 0xa3482b, +0x822021, 0x100f809, 0x892021, 0x54400006, +0x24130001, 0x8f820054, 0x2021023, 0x2c4203e9, +0x1440ffe9, 0x0, 0x326200ff, 0x54400017, +0xaf520018, 0x8f420378, 0x24420001, 0xaf420378, +0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, +0x8f820124, 0x3c040001, 0x24847830, 0x3c050009, +0xafa20014, 0x8d460000, 0x10000035, 0x34a50600, +0x8f420308, 0x24130001, 0x24420001, 0xaf420308, +0x8f420308, 0x1000001e, 0x326200ff, 0x8f830054, +0x8f820054, 0x247003e8, 0x2021023, 0x2c4203e9, +0x10400016, 0x9821, 0x3c150020, 0x24110010, +0x8f42000c, 0x8f440160, 0x8f450164, 0x8f860120, +0xafb10010, 0xafb20014, 0x551025, 0xafa20018, +0x8f42010c, 0x24070008, 0x40f809, 0x24c6001c, +0x1440ffe3, 0x0, 0x8f820054, 0x2021023, +0x2c4203e9, 0x1440ffee, 0x0, 0x326200ff, +0x14400011, 0x0, 0x8f420378, 0x24420001, +0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, +0xafa20010, 0x8f820124, 0x3c040001, 0x24847838, +0x3c050009, 0xafa20014, 0x8d460000, 0x34a50700, +0xc002d3b, 0x3c03821, 0x8f4202ec, 0x24420001, +0xaf4202ec, 0x8f4202ec, 0x8f420004, 0x30420001, +0x1040001a, 0x24040001, 0x8f420250, 0x8f430254, +0x8f4501b4, 0x3c010002, 0xa02485e9, 0x14650006, +0x0, 0x8f420270, 0x8f430274, 0x8f4401b8, +0x10640022, 0x0, 0x8f420250, 0x8f430254, +0x3c040002, 0x908485e8, 0x8f460270, 0x8f470274, +0x38840001, 0xaf4301b4, 0xaf4701b8, 0x3c010002, +0xa02485e8, 0x10000026, 0x0, 0x8f4200d4, +0x3c010002, 0xa02085e8, 0x24430001, 0x28420033, +0x1440001f, 0xaf4300d4, 0x3c020002, 0x904285e9, +0xaf4000d4, 0x10000018, 0x38420001, 0x8f420004, +0x30420001, 0x10400009, 0x0, 0xc005c9f, +0x2021, 0x3c010002, 0xa02085e9, 0x3c010002, +0xa02085e8, 0x1000000e, 0x0, 0x8f4200d4, +0x3c010002, 0xa02085e8, 0x24430001, 0x284201f5, +0x14400007, 0xaf4300d4, 0x3c020002, 0x904285e9, +0xaf4000d4, 0x421026, 0x3c010002, 0xa02285e9, +0x3c030002, 0x8c63866c, 0x24020002, 0x1462000c, +0x3c030002, 0x3c030002, 0x906385e9, 0x24020001, +0x5462001f, 0x2021, 0x3c020002, 0x904285e8, +0x1443001b, 0x24040005, 0x10000019, 0x24040006, +0x3c020002, 0x8c42a8b4, 0x431024, 0x1040000b, +0x24020001, 0x3c030002, 0x906385e9, 0x54620010, +0x2021, 0x3c020002, 0x904285e8, 0x1443000c, +0x24040003, 0x1000000a, 0x24040004, 0x3c030002, +0x906385e9, 0x14620006, 0x2021, 0x3c020002, +0x904285e8, 0x24040001, 0x50440001, 0x24040002, +0xc005c9f, 0x0, 0x2402ff7f, 0x282a024, +0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, +0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, +0x27bd0050, 0x3e00008, 0x0, 0x3c020002, +0x8c42867c, 0x27bdffb0, 0xafbf0048, 0xafbe0044, +0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, +0x104000de, 0xafb00030, 0x8f4200d0, 0x3c040002, +0x8c84866c, 0x24430001, 0x2842000b, 0xaf4400e8, +0x144000fe, 0xaf4300d0, 0x8f420004, 0x30420002, +0x14400095, 0xaf4000d0, 0x8f420004, 0x34420002, +0xaf420004, 0x24020001, 0x14820003, 0x3c020600, +0x10000002, 0x34423000, 0x34421000, 0xafa20020, +0x1821, 0x8f5e0018, 0x27aa0020, 0x240200ff, +0x13c20002, 0xafaa002c, 0x27c30001, 0x8c020228, +0x609021, 0x1642000e, 0x1e38c0, 0x8f42033c, +0x24420001, 0xaf42033c, 0x8f42033c, 0x8c020228, +0x3c040001, 0x24847824, 0x3c050009, 0xafa00014, +0xafa20010, 0x8fa60020, 0x1000006d, 0x34a50500, +0xf71021, 0x8fa30020, 0x8fa40024, 0xac4304c0, +0xac4404c4, 0x8f830054, 0x8f820054, 0x247003e8, +0x2021023, 0x2c4203e9, 0x1040001b, 0x9821, +0xe08821, 0x263504c0, 0x8f440178, 0x8f45017c, +0x2201821, 0x240a0004, 0xafaa0010, 0xafb20014, +0x8f48000c, 0x1021, 0x2f53021, 0xafa80018, +0x8f48010c, 0x24070008, 0xa32821, 0xa3482b, +0x822021, 0x100f809, 0x892021, 0x54400006, +0x24130001, 0x8f820054, 0x2021023, 0x2c4203e9, +0x1440ffe9, 0x0, 0x326200ff, 0x54400017, +0xaf520018, 0x8f420378, 0x24420001, 0xaf420378, +0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, +0x8f820124, 0x3c040001, 0x24847830, 0x3c050009, +0xafa20014, 0x8d460000, 0x10000035, 0x34a50600, +0x8f420308, 0x24130001, 0x24420001, 0xaf420308, +0x8f420308, 0x1000001e, 0x326200ff, 0x8f830054, +0x8f820054, 0x247003e8, 0x2021023, 0x2c4203e9, +0x10400016, 0x9821, 0x3c150020, 0x24110010, +0x8f42000c, 0x8f440160, 0x8f450164, 0x8f860120, +0xafb10010, 0xafb20014, 0x551025, 0xafa20018, +0x8f42010c, 0x24070008, 0x40f809, 0x24c6001c, +0x1440ffe3, 0x0, 0x8f820054, 0x2021023, +0x2c4203e9, 0x1440ffee, 0x0, 0x326200ff, +0x14400011, 0x0, 0x8f420378, 0x24420001, +0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, +0xafa20010, 0x8f820124, 0x3c040001, 0x24847838, +0x3c050009, 0xafa20014, 0x8d460000, 0x34a50700, +0xc002d3b, 0x3c03821, 0x8f4202ec, 0x24420001, +0xaf4202ec, 0x8f4202ec, 0x8f420004, 0x30420001, +0x10400033, 0x3c020400, 0x2c21024, 0x10400017, +0x0, 0x934205c0, 0x8f440250, 0x8f450254, +0x8f4301b4, 0x34420020, 0x14a30006, 0xa34205c0, +0x8f420270, 0x8f430274, 0x8f4401b8, 0x10640008, +0x0, 0x8f420250, 0x8f430254, 0x934405c0, +0x8f460270, 0x8f470274, 0x10000016, 0x38840040, +0x934205c0, 0x10000048, 0x304200bf, 0x934205c0, +0x8f440250, 0x8f450254, 0x8f4301b4, 0x304200bf, +0x14a30006, 0xa34205c0, 0x8f420270, 0x8f430274, +0x8f4401b8, 0x1064000b, 0x0, 0x8f420250, +0x8f430254, 0x934405c0, 0x8f460270, 0x8f470274, +0x38840020, 0xaf4301b4, 0xaf4701b8, 0x10000033, +0xa34405c0, 0x934205c0, 0x1000002f, 0x34420020, +0x934205c0, 0x8f4300d4, 0x34420020, 0xa34205c0, +0x24620001, 0x10000023, 0x28630033, 0x8f4200e4, +0x8f4300e0, 0x24420001, 0xaf4200e4, 0x43102a, +0x14400006, 0x24030001, 0x8f4200e8, 0x14430002, +0xaf4000e4, 0x24030004, 0xaf4300e8, 0x8f420004, +0x30420001, 0x1040000d, 0x3c020400, 0x2c21024, +0x10400007, 0x0, 0x934205c0, 0x34420040, +0xa34205c0, 0x934205c0, 0x1000000f, 0x304200df, +0x934205c0, 0x1000000c, 0x34420060, 0x934205c0, +0x8f4300d4, 0x34420020, 0xa34205c0, 0x24620001, +0x286300fb, 0x14600005, 0xaf4200d4, 0x934205c0, +0xaf4000d4, 0x38420040, 0xa34205c0, 0x934205c0, +0x8f4300e8, 0x3042007f, 0xa34205c0, 0x24020001, +0x14620005, 0x0, 0x934405c0, 0x42102, +0x10000003, 0x348400f0, 0x934405c0, 0x3484000f, +0xc005c85, 0x0, 0x2402ff7f, 0x282a024, +0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, +0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, +0x27bd0050, 0x3e00008, 0x0, 0x27bdffb0, +0x274401c0, 0x26e30028, 0x24650400, 0x65102b, +0xafbf0048, 0xafbe0044, 0xafb50040, 0xafb3003c, +0xafb20038, 0xafb10034, 0x10400007, 0xafb00030, +0x8c820000, 0xac620000, 0x24630004, 0x65102b, +0x1440fffb, 0x24840004, 0x8c020080, 0xaee20044, +0x8c0200c0, 0xaee20040, 0x8c020084, 0xaee20030, +0x8c020084, 0xaee2023c, 0x8c020088, 0xaee20240, +0x8c02008c, 0xaee20244, 0x8c020090, 0xaee20248, +0x8c020094, 0xaee2024c, 0x8c020098, 0xaee20250, +0x8c02009c, 0xaee20254, 0x8c0200a0, 0xaee20258, +0x8c0200a4, 0xaee2025c, 0x8c0200a8, 0xaee20260, +0x8c0200ac, 0xaee20264, 0x8c0200b0, 0xaee20268, +0x8c0200b4, 0xaee2026c, 0x8c0200b8, 0xaee20270, +0x8c0200bc, 0x24040001, 0xaee20274, 0xaee00034, +0x41080, 0x571021, 0x8ee30034, 0x8c42023c, +0x24840001, 0x621821, 0x2c82000f, 0xaee30034, +0x1440fff8, 0x41080, 0x8c0200cc, 0xaee20048, +0x8c0200d0, 0xaee2004c, 0x8c0200e0, 0xaee201f8, +0x8c0200e4, 0xaee201fc, 0x8c0200e8, 0xaee20200, +0x8c0200ec, 0xaee20204, 0x8c0200f0, 0xaee20208, +0x8ee400c0, 0x8ee500c4, 0x8c0200fc, 0x45102b, +0x1040000b, 0x0, 0x8ee200c0, 0x8ee300c4, +0x24040001, 0x24050000, 0x651821, 0x65302b, +0x441021, 0x461021, 0xaee200c0, 0xaee300c4, +0x8c0200fc, 0x8ee400c0, 0x8ee500c4, 0x2408ffff, +0x24090000, 0x401821, 0x1021, 0x882024, +0xa92824, 0x822025, 0xa32825, 0xaee400c0, +0xaee500c4, 0x8ee400d0, 0x8ee500d4, 0x8c0200f4, +0x45102b, 0x1040000b, 0x0, 0x8ee200d0, +0x8ee300d4, 0x24040001, 0x24050000, 0x651821, +0x65302b, 0x441021, 0x461021, 0xaee200d0, +0xaee300d4, 0x8c0200f4, 0x8ee400d0, 0x8ee500d4, +0x401821, 0x1021, 0x882024, 0xa92824, +0x822025, 0xa32825, 0xaee400d0, 0xaee500d4, +0x8ee400c8, 0x8ee500cc, 0x8c0200f8, 0x45102b, +0x1040000b, 0x0, 0x8ee200c8, 0x8ee300cc, +0x24040001, 0x24050000, 0x651821, 0x65302b, +0x441021, 0x461021, 0xaee200c8, 0xaee300cc, +0x8c0200f8, 0x8ee400c8, 0x8ee500cc, 0x401821, +0x1021, 0x882024, 0xa92824, 0x822025, +0xa32825, 0x24020008, 0xaee400c8, 0xaee500cc, +0xafa20010, 0xafa00014, 0x8f42000c, 0x8c040208, +0x8c05020c, 0xafa20018, 0x8f42010c, 0x26e60028, +0x40f809, 0x24070400, 0x104000f1, 0x3c020400, +0xafa20020, 0x934205c6, 0x10400089, 0x1821, +0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, +0xafaa002c, 0x27c30001, 0x8c020228, 0x609021, +0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x24847824, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, +0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, +0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, +0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x54400006, 0x24130001, +0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, +0x0, 0x326200ff, 0x54400017, 0xaf520018, +0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, +0x8f820120, 0x8faa002c, 0xafa20010, 0x8f820124, +0x3c040001, 0x24847830, 0x3c050009, 0xafa20014, +0x8d460000, 0x10000033, 0x34a50600, 0x8f420308, +0x24130001, 0x24420001, 0xaf420308, 0x8f420308, +0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, +0x9821, 0x24110010, 0x8f42000c, 0x8f440160, +0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, +0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, +0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, +0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, +0x326200ff, 0x54400012, 0x24020001, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8faa002c, 0xafa20010, 0x8f820124, 0x3c040001, +0x24847838, 0x3c050009, 0xafa20014, 0x8d460000, +0x34a50700, 0xc002d3b, 0x3c03821, 0x1021, +0x1440005b, 0x24020001, 0x10000066, 0x0, +0x8f510018, 0x240200ff, 0x12220002, 0x8021, +0x26300001, 0x8c020228, 0x1602000e, 0x1130c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x2484780c, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60020, 0x1000003f, +0x34a50100, 0xd71021, 0x8fa30020, 0x8fa40024, +0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, +0x8f45017c, 0x1021, 0x24070004, 0xafa70010, +0xafb00014, 0x8f48000c, 0x24c604c0, 0x2e63021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x24847814, 0x3c050009, +0xafa20014, 0x8fa60020, 0x1000001c, 0x34a50200, +0x8f440160, 0x8f450164, 0x8f43000c, 0xaf500018, +0x8f860120, 0x24020010, 0xafa20010, 0xafb00014, +0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x54400011, 0x24020001, 0x8f420340, 0x24420001, +0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x2484781c, 0x3c050009, +0xafa20014, 0x8fa60020, 0x34a50300, 0xc002d3b, +0x2203821, 0x1021, 0x1040000e, 0x24020001, +0x8f4202e8, 0xa34005c6, 0xaf4001b0, 0x24420001, +0xaf4202e8, 0x8f4202e8, 0x8ee20150, 0x24420001, +0xaee20150, 0x8ee20150, 0x10000003, 0x0, +0x24020001, 0xa34205c6, 0x8fbf0048, 0x8fbe0044, +0x8fb50040, 0x8fb3003c, 0x8fb20038, 0x8fb10034, +0x8fb00030, 0x3e00008, 0x27bd0050, 0x27bdffd8, +0xafbf0020, 0x8f8200b0, 0x30420004, 0x10400069, +0x0, 0x8f430128, 0x8f820104, 0x14620005, +0x0, 0x8f430130, 0x8f8200b4, 0x10620006, +0x0, 0x8f820104, 0xaf420128, 0x8f8200b4, +0x1000005c, 0xaf420130, 0x8f8200b0, 0x3c030080, +0x431024, 0x1040000e, 0x0, 0x8f82011c, +0x34420002, 0xaf82011c, 0x8f8200b0, 0x2403fffb, +0x431024, 0xaf8200b0, 0x8f82011c, 0x2403fffd, +0x431024, 0xaf82011c, 0x1000004a, 0x0, +0x8f430128, 0x8f820104, 0x14620005, 0x0, +0x8f430130, 0x8f8200b4, 0x10620010, 0x0, +0x8f820104, 0xaf420128, 0x8f8200b4, 0x8f430128, +0xaf420130, 0xafa30010, 0x8f420130, 0x3c040001, +0x24847874, 0xafa20014, 0x8f86011c, 0x8f8700b0, +0x3c050005, 0x10000031, 0x34a50900, 0x8f420128, +0xafa20010, 0x8f420130, 0x3c040001, 0x24847880, +0xafa20014, 0x8f86011c, 0x8f8700b0, 0x3c050005, +0xc002d3b, 0x34a51000, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f830104, 0x8f8200b0, 0x34420001, +0xaf8200b0, 0x24020008, 0xaf830104, 0xafa20010, +0xafa00014, 0x8f42000c, 0x8c040208, 0x8c05020c, +0xafa20018, 0x8f42010c, 0x26e60028, 0x40f809, +0x24070400, 0x8f82011c, 0x2403fffd, 0x431024, +0xaf82011c, 0x8ee201dc, 0x24420001, 0xaee201dc, +0x8ee201dc, 0x8f420128, 0xafa20010, 0x8f420130, +0x3c040001, 0x2484788c, 0xafa20014, 0x8f86011c, +0x8f8700b0, 0x3c050005, 0x34a51100, 0xc002d3b, +0x0, 0x8f8200a0, 0x30420004, 0x1040006a, +0x0, 0x8f43012c, 0x8f820124, 0x14620005, +0x0, 0x8f430134, 0x8f8200a4, 0x10620006, +0x0, 0x8f820124, 0xaf42012c, 0x8f8200a4, +0x1000005d, 0xaf420134, 0x8f8200a0, 0x3c030080, +0x431024, 0x1040000e, 0x0, 0x8f82011c, +0x34420002, 0xaf82011c, 0x8f8200a0, 0x2403fffb, +0x431024, 0xaf8200a0, 0x8f82011c, 0x2403fffd, +0x431024, 0xaf82011c, 0x1000004b, 0x0, +0x8f43012c, 0x8f820124, 0x14620005, 0x0, +0x8f430134, 0x8f8200a4, 0x10620010, 0x0, +0x8f820124, 0xaf42012c, 0x8f8200a4, 0x8f43012c, +0xaf420134, 0xafa30010, 0x8f420134, 0x3c040001, +0x24847898, 0xafa20014, 0x8f86011c, 0x8f8700a0, +0x3c050005, 0x10000032, 0x34a51200, 0x8f42012c, +0xafa20010, 0x8f420134, 0x3c040001, 0x248478a4, +0xafa20014, 0x8f86011c, 0x8f8700a0, 0x3c050005, +0xc002d3b, 0x34a51300, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f830124, 0x8f8200a0, 0x34420001, +0xaf8200a0, 0x24020080, 0xaf830124, 0xafa20010, +0xafa00014, 0x8f420014, 0x8c040208, 0x8c05020c, +0xafa20018, 0x8f420108, 0x3c060002, 0x24c68794, +0x40f809, 0x24070004, 0x8f82011c, 0x2403fffd, +0x431024, 0xaf82011c, 0x8ee201dc, 0x24420001, +0xaee201dc, 0x8ee201dc, 0x8f42012c, 0xafa20010, +0x8f420134, 0x3c040001, 0x248478b0, 0xafa20014, +0x8f86011c, 0x8f8700a0, 0x3c050005, 0x34a51400, +0xc002d3b, 0x0, 0x8fbf0020, 0x3e00008, +0x27bd0028, 0x3c081000, 0x24070001, 0x3c060080, +0x3c050100, 0x8f820070, 0x481024, 0x1040fffd, +0x0, 0x8f820054, 0x24420005, 0xaf820078, +0x8c040234, 0x10800017, 0x1821, 0x3c020001, +0x571021, 0x8c4240e8, 0x24420005, 0x3c010001, +0x370821, 0xac2240e8, 0x3c020001, 0x571021, +0x8c4240e8, 0x44102b, 0x1440000a, 0x0, +0x3c030080, 0x3c010001, 0x370821, 0xac2040e8, +0x3c010001, 0x370821, 0xa02740f0, 0x1000000b, +0x0, 0x3c020001, 0x571021, 0x904240f0, +0x54400006, 0x661825, 0x3c020001, 0x571021, +0x904240f1, 0x54400001, 0x661825, 0x8c040230, +0x10800013, 0x0, 0x3c020001, 0x571021, +0x8c4240ec, 0x24420005, 0x3c010001, 0x370821, +0xac2240ec, 0x3c020001, 0x571021, 0x8c4240ec, +0x44102b, 0x14400006, 0x0, 0x3c010001, +0x370821, 0xac2040ec, 0x10000006, 0x651825, +0x3c020001, 0x571021, 0x904240f2, 0x54400001, +0x651825, 0x1060ffbb, 0x0, 0x8f420000, +0x10400007, 0x0, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x431025, 0xaf820060, 0x8f420000, +0x10400004, 0x0, 0xaf80004c, 0x1000ffa5, +0x0, 0xaf800048, 0x1000ffa2, 0x0, +0x3e00008, 0x0, 0x27bdffe0, 0xafbf0018, +0x8f860064, 0x30c20004, 0x10400026, 0x24040004, +0x8c020114, 0xaf420020, 0xaf840064, 0x8f4202fc, +0x24420001, 0xaf4202fc, 0x8f4202fc, 0x8f820064, +0x30420004, 0x14400005, 0x0, 0x8c030114, +0x8f420020, 0x1462fff2, 0x0, 0x8f420000, +0x8f43003c, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x431025, 0xaf820060, +0x8f420000, 0x10400074, 0x0, 0x1000006f, +0x0, 0x30c20008, 0x10400020, 0x24040008, +0x8c02011c, 0xaf420048, 0xaf840064, 0x8f4202a8, +0x24420001, 0xaf4202a8, 0x8f4202a8, 0x8f820064, +0x30420008, 0x14400005, 0x0, 0x8c03011c, +0x8f420048, 0x1462fff2, 0x0, 0x8f420000, +0x10400007, 0x0, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x1000ffd9, 0x34420200, 0x30c20020, +0x10400023, 0x24040020, 0x8c02012c, 0xaf420068, +0xaf840064, 0x8f4202d8, 0x24420001, 0xaf4202d8, +0x8f4202d8, 0x8f820064, 0x30420020, 0x14400005, +0x32c24000, 0x8c03012c, 0x8f420068, 0x1462fff2, +0x32c24000, 0x14400002, 0x3c020001, 0x2c2b025, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x1000ffb4, 0x34420800, +0x30c20010, 0x1040002b, 0x24040010, 0x8c020124, +0xaf420058, 0xaf840064, 0x8f4202d4, 0x24420001, +0xaf4202d4, 0x8f4202d4, 0x8f820064, 0x30420010, +0x14400005, 0x32c22000, 0x8c030124, 0x8f420058, +0x1462fff2, 0x32c22000, 0x50400001, 0x36d68000, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x34420100, 0xaf820060, +0x8f420000, 0x10400004, 0x0, 0xaf80004c, +0x10000072, 0x0, 0xaf800048, 0x1000006f, +0x0, 0x30c20001, 0x10400004, 0x24020001, +0xaf820064, 0x10000069, 0x0, 0x30c20002, +0x1440000c, 0x3c050003, 0x3c040001, 0x24847974, +0x34a50500, 0x3821, 0xafa00010, 0xc002d3b, +0xafa00014, 0x2402ffc0, 0xaf820064, 0x1000005b, +0x0, 0x8c05022c, 0x8c02010c, 0x10a2004c, +0x51080, 0x8c460300, 0x24a20001, 0x3045003f, +0x24020003, 0xac05022c, 0x61e02, 0x10620005, +0x24020010, 0x1062001e, 0x30c20fff, 0x1000003d, +0x0, 0x8f4302a8, 0x8f440000, 0x30c20fff, +0xaf420048, 0x24630001, 0xaf4302a8, 0x8f4202a8, +0x10800007, 0x0, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x34420200, 0xaf820060, 0x8f420000, +0x10400021, 0x0, 0x1000001c, 0x0, +0xaf420058, 0x32c22000, 0x50400001, 0x36d68000, +0x8f4202d4, 0x8f430000, 0x24420001, 0xaf4202d4, +0x8f4202d4, 0x10600007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x34420100, 0xaf820060, +0x8f420000, 0x10400004, 0x0, 0xaf80004c, +0x10000007, 0x0, 0xaf800048, 0x10000004, +0x0, 0xc0022ad, 0xc02021, 0x402821, +0x8c02010c, 0x14a20002, 0x24020002, 0xaf820064, +0x8f820064, 0x30420002, 0x14400004, 0x0, +0x8c02010c, 0x14a2ffa8, 0x0, 0x8fbf0018, +0x3e00008, 0x27bd0020, 0x3e00008, 0x0, +0x27bdffa0, 0xafb00040, 0x808021, 0x101602, +0x2442ffff, 0x304300ff, 0x2c620013, 0xafbf0058, +0xafbe0054, 0xafb50050, 0xafb3004c, 0xafb20048, +0xafb10044, 0x104001fe, 0xafa50034, 0x31080, +0x3c010001, 0x220821, 0x8c2279b8, 0x400008, +0x0, 0x101302, 0x30440fff, 0x24020001, +0x10820005, 0x24020002, 0x1082000c, 0x2402fffe, +0x10000025, 0x3c050003, 0x8f430004, 0x3c020002, +0x8c4287c0, 0xaf440200, 0xaf440204, 0x3c040002, +0x8c84873c, 0x10000009, 0x34630001, 0x8f430004, +0xaf440200, 0xaf440204, 0x3c040002, 0x8c84873c, +0x621824, 0x3c020001, 0x2442d68c, 0x21100, +0x21182, 0xaf430004, 0x3c030800, 0x431025, +0xac820038, 0x8f840054, 0x41442, 0x41c82, +0x431021, 0x41cc2, 0x431023, 0x41d02, +0x431021, 0x41d42, 0x431023, 0xaf420208, +0x10000009, 0x0, 0x3c040001, 0x24847980, +0x34a51000, 0x2003021, 0x3821, 0xafa00010, +0xc002d3b, 0xafa00014, 0x8f4202a0, 0x24420001, +0xaf4202a0, 0x8f4202a0, 0x10000228, 0x0, +0x27b00028, 0x2002021, 0x24050210, 0xc002dbf, +0x24060008, 0xc00263a, 0x2002021, 0x1000021f, +0x0, 0x8faa0034, 0x27a40028, 0xa1880, +0x25420001, 0x3042003f, 0xafa20034, 0x8c650300, +0x8faa0034, 0x21080, 0x8c430300, 0x25420001, +0x3042003f, 0xafa20034, 0xac02022c, 0xafa50028, +0xc00263a, 0xafa3002c, 0x1000020c, 0x0, +0x27b00028, 0x2002021, 0x24050210, 0xc002dbf, +0x24060008, 0xc002779, 0x2002021, 0x10000203, +0x0, 0x8faa0034, 0x27a40028, 0xa1880, +0x25420001, 0x3042003f, 0xafa20034, 0x8c650300, +0x8faa0034, 0x21080, 0x8c430300, 0x25420001, +0x3042003f, 0xafa20034, 0xac02022c, 0xafa50028, +0xc002779, 0xafa3002c, 0x100001f0, 0x0, +0x101302, 0x30430fff, 0x24020001, 0x10620005, +0x24020002, 0x1062001e, 0x3c020002, 0x10000033, +0x3c050003, 0x3c030002, 0x2c31024, 0x54400037, +0x2c3b025, 0x8f820228, 0x3c010001, 0x370821, +0xac2238d8, 0x8f82022c, 0x3c010001, 0x370821, +0xac2238dc, 0x8f820230, 0x3c010001, 0x370821, +0xac2238e0, 0x8f820234, 0x3c010001, 0x370821, +0xac2238e4, 0x2402ffff, 0xaf820228, 0xaf82022c, +0xaf820230, 0xaf820234, 0x10000020, 0x2c3b025, +0x2c21024, 0x10400012, 0x3c02fffd, 0x3c020001, +0x571021, 0x8c4238d8, 0xaf820228, 0x3c020001, +0x571021, 0x8c4238dc, 0xaf82022c, 0x3c020001, +0x571021, 0x8c4238e0, 0xaf820230, 0x3c020001, +0x571021, 0x8c4238e4, 0xaf820234, 0x3c02fffd, +0x3442ffff, 0x10000009, 0x2c2b024, 0x3c040001, +0x2484798c, 0x34a51100, 0x2003021, 0x3821, +0xafa00010, 0xc002d3b, 0xafa00014, 0x8f4202cc, +0x24420001, 0xaf4202cc, 0x8f4202cc, 0x100001a7, +0x0, 0x101302, 0x30450fff, 0x24020001, +0x10a20005, 0x24020002, 0x10a2000e, 0x3c0408ff, +0x10000016, 0x3c050003, 0x3c0208ff, 0x3442ffff, +0x8f830220, 0x3c040004, 0x2c4b025, 0x621824, +0x34630008, 0xaf830220, 0xaf450298, 0x10000013, +0x0, 0x3484fff7, 0x3c03fffb, 0x8f820220, +0x3463ffff, 0x2c3b024, 0x441024, 0xaf820220, +0xaf450298, 0x10000009, 0x0, 0x3c040001, +0x24847998, 0x34a51200, 0x2003021, 0x3821, +0xafa00010, 0xc002d3b, 0xafa00014, 0x8f4202bc, +0x24420001, 0xaf4202bc, 0x8f4202bc, 0x1000017b, +0x0, 0x27840208, 0x24050200, 0xc002dbf, +0x24060008, 0x27440224, 0x24050200, 0xc002dbf, +0x24060008, 0x8f4202c4, 0x24420001, 0xaf4202c4, +0x8f4202c4, 0x1000016d, 0x0, 0x101302, +0x30430fff, 0x24020001, 0x10620011, 0x28620002, +0x50400005, 0x24020002, 0x10600007, 0x0, +0x10000017, 0x0, 0x1062000f, 0x0, +0x10000013, 0x0, 0x8c060248, 0x2021, +0xc005738, 0x24050004, 0x10000007, 0x0, +0x8c060248, 0x2021, 0xc005738, 0x24050004, +0x10000010, 0x0, 0x8c06024c, 0x2021, +0xc005738, 0x24050001, 0x1000000a, 0x0, +0x3c040001, 0x248479a4, 0x3c050003, 0x34a51300, +0x2003021, 0x3821, 0xafa00010, 0xc002d3b, +0xafa00014, 0x8f4202c0, 0x24420001, 0xaf4202c0, +0x8f4202c0, 0x1000013d, 0x0, 0xc002548, +0x0, 0x10000139, 0x0, 0x24020001, +0xa34205c5, 0x24100100, 0x8f4401a8, 0x8f4501ac, +0xafb00010, 0xafa00014, 0x8f420014, 0xafa20018, +0x8f420108, 0x26e60028, 0x40f809, 0x24070400, +0x1040fff5, 0x0, 0x10000128, 0x0, +0x3c03ffff, 0x34637fff, 0x8f420368, 0x8f440360, +0x2c3b024, 0x1821, 0xaf400058, 0xaf40005c, +0xaf400060, 0xaf400064, 0x441023, 0xaf420368, +0x3c020900, 0xaf400360, 0xafa20020, 0x8f5e0018, +0x27aa0020, 0x240200ff, 0x13c20002, 0xafaa003c, +0x27c30001, 0x8c020228, 0x609021, 0x1642000e, +0x1e38c0, 0x8f42033c, 0x24420001, 0xaf42033c, +0x8f42033c, 0x8c020228, 0x3c040001, 0x2484793c, +0x3c050009, 0xafa00014, 0xafa20010, 0x8fa60020, +0x1000006b, 0x34a50500, 0xf71021, 0x8fa30020, +0x8fa40024, 0xac4304c0, 0xac4404c4, 0x8f830054, +0x8f820054, 0x247003e8, 0x2021023, 0x2c4203e9, +0x1040001b, 0x9821, 0xe08821, 0x263504c0, +0x8f440178, 0x8f45017c, 0x2201821, 0x240a0004, +0xafaa0010, 0xafb20014, 0x8f48000c, 0x1021, +0x2f53021, 0xafa80018, 0x8f48010c, 0x24070008, +0xa32821, 0xa3482b, 0x822021, 0x100f809, +0x892021, 0x54400006, 0x24130001, 0x8f820054, +0x2021023, 0x2c4203e9, 0x1440ffe9, 0x0, +0x326200ff, 0x54400017, 0xaf520018, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8faa003c, 0xafa20010, 0x8f820124, 0x3c040001, +0x24847948, 0x3c050009, 0xafa20014, 0x8d460000, +0x10000033, 0x34a50600, 0x8f420308, 0x24130001, +0x24420001, 0xaf420308, 0x8f420308, 0x1000001c, +0x326200ff, 0x8f830054, 0x8f820054, 0x247003e8, +0x2021023, 0x2c4203e9, 0x10400014, 0x9821, +0x24110010, 0x8f42000c, 0x8f440160, 0x8f450164, +0x8f860120, 0xafb10010, 0xafb20014, 0xafa20018, +0x8f42010c, 0x24070008, 0x40f809, 0x24c6001c, +0x1440ffe5, 0x0, 0x8f820054, 0x2021023, +0x2c4203e9, 0x1440ffef, 0x0, 0x326200ff, +0x14400011, 0x0, 0x8f420378, 0x24420001, +0xaf420378, 0x8f420378, 0x8f820120, 0x8faa003c, +0xafa20010, 0x8f820124, 0x3c040001, 0x24847950, +0x3c050009, 0xafa20014, 0x8d460000, 0x34a50700, +0xc002d3b, 0x3c03821, 0x8f4202b0, 0x24420001, +0xaf4202b0, 0x8f4202b0, 0x8f4202f8, 0x24420001, +0xaf4202f8, 0x8f4202f8, 0x1000008c, 0x0, +0x8c02025c, 0x27440224, 0xaf4201f0, 0x8c020260, +0x24050200, 0x24060008, 0xaf4201f8, 0xc002dbf, +0x0, 0x8f820220, 0x30420008, 0x14400002, +0x24020001, 0x24020002, 0xaf420298, 0x8f4202ac, +0x24420001, 0xaf4202ac, 0x8f4202ac, 0x10000077, +0x0, 0x3c0200ff, 0x3442ffff, 0x2021824, +0x32c20180, 0x14400006, 0x3402fffb, 0x43102b, +0x14400003, 0x0, 0x1000006c, 0xaf4300bc, +0x3c040001, 0x248479b0, 0x3c050003, 0x34a51500, +0x2003021, 0x3821, 0xafa00010, 0xc002d3b, +0xafa00014, 0x3c020700, 0x34421000, 0x101e02, +0x621825, 0xafa30020, 0x8f510018, 0x240200ff, +0x12220002, 0x8021, 0x26300001, 0x8c020228, +0x1602000e, 0x1130c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x24847924, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000003f, 0x34a50100, 0xd71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0xc01821, 0x8f440178, 0x8f45017c, 0x1021, +0x24070004, 0xafa70010, 0xafb00014, 0x8f48000c, +0x24c604c0, 0x2e63021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x1440000b, 0x24070008, +0x8f820120, 0xafa20010, 0x8f820124, 0x3c040001, +0x2484792c, 0x3c050009, 0xafa20014, 0x8fa60020, +0x1000001c, 0x34a50200, 0x8f440160, 0x8f450164, +0x8f43000c, 0xaf500018, 0x8f860120, 0x24020010, +0xafa20010, 0xafb00014, 0xafa30018, 0x8f42010c, +0x40f809, 0x24c6001c, 0x14400010, 0x0, +0x8f420340, 0x24420001, 0xaf420340, 0x8f420340, +0x8f820120, 0xafa20010, 0x8f820124, 0x3c040001, +0x24847934, 0x3c050009, 0xafa20014, 0x8fa60020, +0x34a50300, 0xc002d3b, 0x2203821, 0x8f4202e0, +0x24420001, 0xaf4202e0, 0x8f4202e0, 0x8f4202f0, +0x24420001, 0xaf4202f0, 0x8f4202f0, 0x8fa20034, +0x8fbf0058, 0x8fbe0054, 0x8fb50050, 0x8fb3004c, +0x8fb20048, 0x8fb10044, 0x8fb00040, 0x3e00008, +0x27bd0060, 0x27bdfff8, 0x2408ffff, 0x10a00014, +0x4821, 0x3c0aedb8, 0x354a8320, 0x90870000, +0x24840001, 0x3021, 0x1071026, 0x30420001, +0x10400002, 0x81842, 0x6a1826, 0x604021, +0x24c60001, 0x2cc20008, 0x1440fff7, 0x73842, +0x25290001, 0x125102b, 0x1440fff0, 0x0, +0x1001021, 0x3e00008, 0x27bd0008, 0x27bdffb0, +0xafbf0048, 0xafbe0044, 0xafb50040, 0xafb3003c, +0xafb20038, 0xafb10034, 0xafb00030, 0x8f870220, +0xafa70024, 0x8f870200, 0xafa7002c, 0x8f820220, +0x3c0308ff, 0x3463ffff, 0x431024, 0x34420004, +0xaf820220, 0x8f820200, 0x3c03c0ff, 0x3463ffff, +0x431024, 0x34420004, 0xaf820200, 0x8f530358, +0x8f55035c, 0x8f5e0360, 0x8f470364, 0xafa70014, +0x8f470368, 0xafa7001c, 0x8f4202d0, 0x274401c0, +0x24420001, 0xaf4202d0, 0x8f5002d0, 0x8f510204, +0x8f520200, 0xc002da8, 0x24050400, 0xaf530358, +0xaf55035c, 0xaf5e0360, 0x8fa70014, 0xaf470364, +0x8fa7001c, 0xaf470368, 0xaf5002d0, 0xaf510204, +0xaf520200, 0x8c02025c, 0x27440224, 0xaf4201f0, +0x8c020260, 0x24050200, 0x24060008, 0xaf4201f8, +0x24020006, 0xaf4201f4, 0xc002dbf, 0x0, +0x3c023b9a, 0x3442ca00, 0xaf4201fc, 0x240203e8, +0x24040002, 0x24030001, 0xaf420294, 0xaf440290, +0xaf43029c, 0x8f820220, 0x30420008, 0x10400004, +0x0, 0xaf430298, 0x10000003, 0x3021, +0xaf440298, 0x3021, 0x3c030002, 0x661821, +0x906385ec, 0x3461021, 0x24c60001, 0xa043022c, +0x2cc2000f, 0x1440fff8, 0x3461821, 0x24c60001, +0x8f820040, 0x24040080, 0x24050080, 0x21702, +0x24420030, 0xa062022c, 0x3461021, 0xa040022c, +0xc002da8, 0x0, 0x8fa70024, 0x30e20004, +0x14400006, 0x0, 0x8f820220, 0x3c0308ff, +0x3463fffb, 0x431024, 0xaf820220, 0x8fa7002c, +0x30e20004, 0x14400006, 0x0, 0x8f820200, +0x3c03c0ff, 0x3463fffb, 0x431024, 0xaf820200, +0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, +0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, +0x27bd0050, 0xaf400104, 0x24040001, 0x410c0, +0x2e21821, 0x24820001, 0x3c010001, 0x230821, +0xa42234d0, 0x402021, 0x2c820080, 0x1440fff8, +0x410c0, 0x24020001, 0x3c010001, 0x370821, +0xa42038d0, 0xaf420100, 0xaf800228, 0xaf80022c, +0xaf800230, 0xaf800234, 0x3e00008, 0x0, +0x27bdffe8, 0xafbf0014, 0xafb00010, 0x8f420104, +0x28420005, 0x10400026, 0x808021, 0x3c020001, +0x8f430104, 0x344230d0, 0x2e23021, 0x318c0, +0x621821, 0x2e33821, 0xc7102b, 0x10400015, +0x1021, 0x96080000, 0x24c40006, 0x9482fffc, +0x14480009, 0x2821, 0x9483fffe, 0x96020002, +0x14620006, 0xa01021, 0x94820000, 0x96030004, +0x431026, 0x2c450001, 0xa01021, 0x1440000a, +0x24c60008, 0xc7102b, 0x1440fff0, 0x24840008, +0x1021, 0x304200ff, 0x14400030, 0x24020001, +0x1000002e, 0x1021, 0x1000fffa, 0x24020001, +0x2002021, 0xc00252e, 0x24050006, 0x3042007f, +0x218c0, 0x2e31021, 0x3c010001, 0x220821, +0x942230d0, 0x1040fff2, 0x2e31021, 0x3c060001, +0xc23021, 0x94c630d0, 0x10c0ffed, 0x3c080001, +0x350834d2, 0x96070000, 0x610c0, 0x572021, +0x882021, 0x94820000, 0x14470009, 0x2821, +0x94830002, 0x96020002, 0x14620006, 0xa01021, +0x94820004, 0x96030004, 0x431026, 0x2c450001, +0xa01021, 0x14400007, 0x610c0, 0x2e21021, +0x3c060001, 0xc23021, 0x94c634d0, 0x14c0ffeb, +0x610c0, 0x10c0ffd2, 0x24020001, 0x8fbf0014, +0x8fb00010, 0x3e00008, 0x27bd0018, 0x3e00008, +0x0, 0x27bdffb0, 0x801021, 0xafb00030, +0x24500002, 0x2002021, 0x24050006, 0xafb10034, +0x408821, 0xafbf0048, 0xafbe0044, 0xafb50040, +0xafb3003c, 0xc00252e, 0xafb20038, 0x3047007f, +0x710c0, 0x2e21021, 0x3c050001, 0xa22821, +0x94a530d0, 0x50a0001c, 0xa03021, 0x3c090001, +0x352934d2, 0x96280002, 0x510c0, 0x572021, +0x892021, 0x94820000, 0x14480009, 0x3021, +0x94830002, 0x96020002, 0x14620006, 0xc01021, +0x94820004, 0x96030004, 0x431026, 0x2c460001, +0xc01021, 0x14400007, 0x510c0, 0x2e21021, +0x3c050001, 0xa22821, 0x94a534d0, 0x14a0ffeb, +0x510c0, 0xa03021, 0x10c00014, 0x610c0, +0x571821, 0x3c010001, 0x230821, 0x8c2334d0, +0x571021, 0xafa30010, 0x3c010001, 0x220821, +0x8c2234d4, 0x3c040001, 0x24847ab8, 0xafa20014, +0x8e260000, 0x8e270004, 0x3c050004, 0xc002d3b, +0x34a50400, 0x10000063, 0x3c020800, 0x8f450100, +0x10a00006, 0x510c0, 0x2e21021, 0x3c010001, +0x220821, 0x942234d0, 0xaf420100, 0xa03021, +0x14c00011, 0x628c0, 0x710c0, 0x2e21021, +0xafa70010, 0x3c010001, 0x220821, 0x942230d0, +0x3c040001, 0x24847ac4, 0xafa20014, 0x8e260000, +0x8e270004, 0x3c050004, 0xc002d3b, 0x34a50500, +0x10000048, 0x3c020800, 0xb71821, 0x3c020001, +0x96040000, 0x344234d2, 0x621821, 0xa4640000, +0x8e020002, 0x720c0, 0xac620002, 0x2e41021, +0x3c030001, 0x621821, 0x946330d0, 0x2e51021, +0x3c010001, 0x220821, 0xa42334d0, 0x2e41021, +0x3c010001, 0x220821, 0xa42630d0, 0x8f420104, +0x24420001, 0x28420080, 0x1040000f, 0x3c020002, +0x8f420104, 0x3c040001, 0x348430d2, 0x96030000, +0x210c0, 0x571021, 0x441021, 0xa4430000, +0x8e030002, 0xac430002, 0x8f420104, 0x24420001, +0xaf420104, 0x3c020002, 0x2c21024, 0x10400011, +0x72142, 0x3c030001, 0x346338d8, 0x24020003, +0x441023, 0x21080, 0x572021, 0x832021, +0x571021, 0x431021, 0x30e5001f, 0x8c430000, +0x24020001, 0xa21004, 0x621825, 0x1000000c, +0xac830000, 0x24020003, 0x441023, 0x21080, +0x5c2821, 0x5c1021, 0x30e4001f, 0x8c430228, +0x24020001, 0x821004, 0x621825, 0xaca30228, +0x3c020800, 0x34421000, 0x1821, 0xafa20020, +0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, +0xafaa002c, 0x27c30001, 0x8c020228, 0x609021, +0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x24847a80, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, +0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, +0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, +0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x54400006, 0x24130001, +0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, +0x0, 0x326200ff, 0x54400017, 0xaf520018, +0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, +0x8f820120, 0x8faa002c, 0xafa20010, 0x8f820124, +0x3c040001, 0x24847a8c, 0x3c050009, 0xafa20014, +0x8d460000, 0x10000033, 0x34a50600, 0x8f420308, +0x24130001, 0x24420001, 0xaf420308, 0x8f420308, +0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, +0x9821, 0x24110010, 0x8f42000c, 0x8f440160, +0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, +0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, +0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, +0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, +0x326200ff, 0x14400011, 0x0, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8faa002c, 0xafa20010, 0x8f820124, 0x3c040001, +0x24847a94, 0x3c050009, 0xafa20014, 0x8d460000, +0x34a50700, 0xc002d3b, 0x3c03821, 0x8f4202b4, +0x24420001, 0xaf4202b4, 0x8f4202b4, 0x8f4202f4, +0x24420001, 0xaf4202f4, 0x8f4202f4, 0x8fbf0048, +0x8fbe0044, 0x8fb50040, 0x8fb3003c, 0x8fb20038, +0x8fb10034, 0x8fb00030, 0x3e00008, 0x27bd0050, +0x27bdffa0, 0x801021, 0xafb00040, 0x24500002, +0x2002021, 0x24050006, 0xafb10044, 0x408821, +0xafbf0058, 0xafbe0054, 0xafb50050, 0xafb3004c, +0xc00252e, 0xafb20048, 0x3048007f, 0x810c0, +0x2e21021, 0x3c060001, 0xc23021, 0x94c630d0, +0x10c0001c, 0x3821, 0x3c0a0001, 0x354a34d2, +0x96290002, 0x610c0, 0x572021, 0x8a2021, +0x94820000, 0x14490009, 0x2821, 0x94830002, +0x96020002, 0x14620006, 0xa01021, 0x94820004, +0x96030004, 0x431026, 0x2c450001, 0xa01021, +0x14400008, 0x610c0, 0xc03821, 0x2e21021, +0x3c060001, 0xc23021, 0x94c634d0, 0x14c0ffea, +0x610c0, 0x14c00011, 0xafa70028, 0x810c0, +0x2e21021, 0xafa80010, 0x3c010001, 0x220821, +0x942230d0, 0x3c040001, 0x24847ad0, 0xafa20014, +0x8e260000, 0x8e270004, 0x3c050004, 0xc002d3b, +0x34a50900, 0x10000075, 0x3c020800, 0x10e0000c, +0x610c0, 0x2e21021, 0x3c030001, 0x621821, +0x946334d0, 0x710c0, 0x2e21021, 0x3c010001, +0x220821, 0xa42334d0, 0x1000000b, 0x3c040001, +0x2e21021, 0x3c030001, 0x621821, 0x946334d0, +0x810c0, 0x2e21021, 0x3c010001, 0x220821, +0xa42330d0, 0x3c040001, 0x348430d0, 0x8f430100, +0x610c0, 0x2e21021, 0x3c010001, 0x220821, +0xa42334d0, 0x8f420104, 0x2e43821, 0x2821, +0x18400029, 0xaf460100, 0x24e60006, 0x94c3fffc, +0x96020000, 0x14620009, 0x2021, 0x94c3fffe, +0x96020002, 0x14620006, 0x801021, 0x94c20000, +0x96030004, 0x431026, 0x2c440001, 0x801021, +0x50400014, 0x24a50001, 0x8f420104, 0x2442ffff, +0xa2102a, 0x1040000b, 0x24e40004, 0x94820006, +0x8c830008, 0xa482fffe, 0xac830000, 0x8f420104, +0x24a50001, 0x2442ffff, 0xa2102a, 0x1440fff7, +0x24840008, 0x8f420104, 0x2442ffff, 0x10000006, +0xaf420104, 0x8f420104, 0x24c60008, 0xa2102a, +0x1440ffda, 0x24e70008, 0x810c0, 0x2e21021, +0x3c010001, 0x220821, 0x942230d0, 0x14400023, +0x3c020800, 0x3c020002, 0x2c21024, 0x10400012, +0x82142, 0x3c030001, 0x346338d8, 0x24020003, +0x441023, 0x21080, 0x572021, 0x832021, +0x571021, 0x431021, 0x3105001f, 0x24030001, +0x8c420000, 0xa31804, 0x31827, 0x431024, +0x1000000d, 0xac820000, 0x24020003, 0x441023, +0x21080, 0x5c2821, 0x5c1021, 0x3104001f, +0x24030001, 0x8c420228, 0x831804, 0x31827, +0x431024, 0xaca20228, 0x3c020800, 0x34422000, +0x1821, 0xafa20020, 0x8f5e0018, 0x27ab0020, +0x240200ff, 0x13c20002, 0xafab0034, 0x27c30001, +0x8c020228, 0x609021, 0x1642000e, 0x1e38c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x24847a80, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60020, 0x1000006b, +0x34a50500, 0xf71021, 0x8fa30020, 0x8fa40024, +0xac4304c0, 0xac4404c4, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x1040001b, +0x9821, 0xe08821, 0x263504c0, 0x8f440178, +0x8f45017c, 0x2201821, 0x240b0004, 0xafab0010, +0xafb20014, 0x8f48000c, 0x1021, 0x2f53021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x54400006, 0x24130001, 0x8f820054, 0x2021023, +0x2c4203e9, 0x1440ffe9, 0x0, 0x326200ff, +0x54400017, 0xaf520018, 0x8f420378, 0x24420001, +0xaf420378, 0x8f420378, 0x8f820120, 0x8fab0034, +0xafa20010, 0x8f820124, 0x3c040001, 0x24847a8c, +0x3c050009, 0xafa20014, 0x8d660000, 0x10000033, +0x34a50600, 0x8f420308, 0x24130001, 0x24420001, +0xaf420308, 0x8f420308, 0x1000001c, 0x326200ff, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x10400014, 0x9821, 0x24110010, +0x8f42000c, 0x8f440160, 0x8f450164, 0x8f860120, +0xafb10010, 0xafb20014, 0xafa20018, 0x8f42010c, +0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe5, +0x0, 0x8f820054, 0x2021023, 0x2c4203e9, +0x1440ffef, 0x0, 0x326200ff, 0x14400011, +0x0, 0x8f420378, 0x24420001, 0xaf420378, +0x8f420378, 0x8f820120, 0x8fab0034, 0xafa20010, +0x8f820124, 0x3c040001, 0x24847a94, 0x3c050009, +0xafa20014, 0x8d660000, 0x34a50700, 0xc002d3b, +0x3c03821, 0x8f4202b8, 0x24420001, 0xaf4202b8, +0x8f4202b8, 0x8f4202f4, 0x24420001, 0xaf4202f4, +0x8f4202f4, 0x8fbf0058, 0x8fbe0054, 0x8fb50050, +0x8fb3004c, 0x8fb20048, 0x8fb10044, 0x8fb00040, +0x3e00008, 0x27bd0060, 0x27bdffe0, 0x27644000, +0xafbf0018, 0xc002da8, 0x24051000, 0x3c030001, +0x34632cc0, 0x3c040001, 0x34842ec8, 0x24020020, +0xaf82011c, 0x2e31021, 0xaf800100, 0xaf800104, +0xaf800108, 0xaf800110, 0xaf800114, 0xaf800118, +0xaf800120, 0xaf800124, 0xaf800128, 0xaf800130, +0xaf800134, 0xaf800138, 0xaf4200ec, 0x2e31021, +0xaf4200f0, 0x2e41021, 0xaf4200f4, 0x2e41021, +0xaf4200f8, 0x3c020001, 0x571021, 0x904240f4, +0x1440001c, 0x3c050001, 0x8f82011c, 0x3c040001, +0x24847b8c, 0x3c050001, 0x34420001, 0xaf82011c, +0xafa00010, 0xafa00014, 0x8f86011c, 0x34a50100, +0xc002d3b, 0x3821, 0x8c020218, 0x30420040, +0x10400014, 0x0, 0x8f82011c, 0x3c040001, +0x24847b98, 0x3c050001, 0x34420004, 0xaf82011c, +0xafa00010, 0xafa00014, 0x8f86011c, 0x10000007, +0x34a50200, 0x3c040001, 0x24847ba0, 0xafa00010, +0xafa00014, 0x8f86011c, 0x34a50300, 0xc002d3b, +0x3821, 0x8fbf0018, 0x3e00008, 0x27bd0020, +0x27bdffd8, 0xafb1001c, 0x8fb10038, 0xafbf0020, +0xafb00018, 0x8f83012c, 0x8fa9003c, 0x8faa0040, +0x1060000a, 0x27624fe0, 0x14620002, 0x24680020, +0x27684800, 0x8f820128, 0x11020004, 0x0, +0x8f820124, 0x15020008, 0x0, 0x8f430334, +0x1021, 0x24630001, 0xaf430334, 0x8f430334, +0x10000052, 0x0, 0xac640000, 0xac650004, +0xac660008, 0xa467000e, 0xac710018, 0xac69001c, +0xac6a0010, 0xac620014, 0xaf880120, 0x8f4200fc, +0x8f5000f4, 0x2442ffff, 0xaf4200fc, 0x8e020000, +0x10510005, 0x3042ff8f, 0x10400019, 0x3222ff8f, +0x10400018, 0x3c020001, 0x8e030004, 0x2c620010, +0x10400013, 0x3c020001, 0x24630001, 0xae030004, +0x8f4300f8, 0x344230c8, 0x2e21021, 0x54620004, +0x24620008, 0x3c020001, 0x34422ec8, 0x2e21021, +0x1450002e, 0x24020001, 0x8f820128, 0x24420020, +0xaf820128, 0x8f820128, 0x10000028, 0x24020001, +0x3c020001, 0x344230c8, 0x2e21021, 0x16020004, +0x26030008, 0x3c020001, 0x34422ec8, 0x2e21821, +0x8f4200f8, 0x608021, 0x12020004, 0xaf5000f4, +0x8e020000, 0x10400016, 0x24020001, 0x24020170, +0x3c040001, 0x24847ba8, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e77bb0, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x24020001, +0xae110000, 0xae020004, 0x24020001, 0x8fbf0020, +0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0028, +0x3e00008, 0x0, 0x27bdffc8, 0xafb30024, +0xafb20020, 0x809021, 0xa09821, 0xafb50028, +0xc0a821, 0xafbf0030, 0xafbe002c, 0xafb1001c, +0xafb00018, 0x8f900120, 0x27624fe0, 0x16020003, +0xe0f021, 0x10000002, 0x27714800, 0x26110020, +0x8f820128, 0x16220008, 0x0, 0x8f430334, +0x1021, 0x24630001, 0xaf430334, 0x8f430334, +0x10000028, 0x0, 0x8f820124, 0x16220014, +0x240201ab, 0x3c040001, 0x24847ba8, 0xafa20010, +0xafa00014, 0x8f860144, 0x3c070001, 0x24e77bb0, +0xc002d3b, 0x3405dead, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, +0x8f820140, 0x3c030001, 0x431025, 0xaf820140, +0xae120000, 0xae130004, 0xae150008, 0xa61e000e, +0x8fa80048, 0xae080018, 0x8fa8004c, 0x26020016, +0xae08001c, 0xae020014, 0x8fa80050, 0xae080010, +0xaf910120, 0x8f4300fc, 0x24020001, 0x2463ffff, +0xaf4300fc, 0x8fbf0030, 0x8fbe002c, 0x8fb50028, +0x8fb30024, 0x8fb20020, 0x8fb1001c, 0x8fb00018, +0x3e00008, 0x27bd0038, 0x3e00008, 0x0, +0x27bdffd8, 0xafb1001c, 0x8fb10038, 0xafbf0020, +0xafb00018, 0x8f83010c, 0x8fa9003c, 0x8faa0040, +0x1060000a, 0x276247e0, 0x14620002, 0x24680020, +0x27684000, 0x8f820108, 0x11020004, 0x0, +0x8f820104, 0x15020008, 0x0, 0x8f430338, +0x1021, 0x24630001, 0xaf430338, 0x8f430338, +0x1000004e, 0x0, 0xac640000, 0xac650004, +0xac660008, 0xa467000e, 0xac710018, 0xac69001c, +0xac6a0010, 0xac620014, 0xaf880100, 0x8f5000ec, +0x8e020000, 0x30420006, 0x10400019, 0x32220006, +0x10400018, 0x3c020001, 0x8e030004, 0x2c620010, +0x10400013, 0x3c020001, 0x24630001, 0xae030004, +0x8f4300f0, 0x34422ec0, 0x2e21021, 0x54620004, +0x24620008, 0x3c020001, 0x34422cc0, 0x2e21021, +0x1450002e, 0x24020001, 0x8f820108, 0x24420020, +0xaf820108, 0x8f820108, 0x10000028, 0x24020001, +0x3c020001, 0x34422ec0, 0x2e21021, 0x16020004, +0x26030008, 0x3c020001, 0x34422cc0, 0x2e21821, +0x8f4200f0, 0x608021, 0x12020004, 0xaf5000ec, +0x8e020000, 0x10400016, 0x24020001, 0x24020212, +0x3c040001, 0x24847ba8, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e77bb0, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x24020001, +0xae110000, 0xae020004, 0x24020001, 0x8fbf0020, +0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0028, +0x3e00008, 0x0, 0x27bdffc8, 0xafb30024, +0xafb20020, 0x809021, 0xa09821, 0xafb50028, +0xc0a821, 0xafbf0030, 0xafbe002c, 0xafb1001c, +0xafb00018, 0x8f900100, 0x276247e0, 0x16020003, +0xe0f021, 0x10000002, 0x27714000, 0x26110020, +0x8f820108, 0x16220008, 0x0, 0x8f430338, +0x1021, 0x24630001, 0xaf430338, 0x8f430338, +0x10000025, 0x0, 0x8f820104, 0x16220014, +0x2402024d, 0x3c040001, 0x24847ba8, 0xafa20010, +0xafa00014, 0x8f860144, 0x3c070001, 0x24e77bb0, +0xc002d3b, 0x3405dead, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, +0x8f820140, 0x3c030001, 0x431025, 0xaf820140, +0xae120000, 0xae130004, 0xae150008, 0xa61e000e, +0x8fa80048, 0xae080018, 0x8fa8004c, 0x26030016, +0xae08001c, 0xae030014, 0x8fa80050, 0x24020001, +0xae080010, 0xaf910100, 0x8fbf0030, 0x8fbe002c, +0x8fb50028, 0x8fb30024, 0x8fb20020, 0x8fb1001c, +0x8fb00018, 0x3e00008, 0x27bd0038, 0x3e00008, +0x0, 0x27bdffd8, 0x3c040001, 0x24847bb8, +0x3c050001, 0xafbf0024, 0xafb20020, 0xafb1001c, +0xafb00018, 0x8f900104, 0x8f9100b0, 0x8f92011c, +0x34a52500, 0x8f820100, 0x2403021, 0x2203821, +0xafa20010, 0xc002d3b, 0xafb00014, 0x8e020008, +0xafa20010, 0x8e02000c, 0x3c040001, 0x24847bc4, +0xafa20014, 0x8e060000, 0x8e070004, 0x3c050001, +0xc002d3b, 0x34a52510, 0x8e020018, 0xafa20010, +0x8e02001c, 0x3c040001, 0x24847bd0, 0xafa20014, +0x8e060010, 0x8e070014, 0x3c050001, 0xc002d3b, +0x34a52520, 0x3c027f00, 0x2221024, 0x3c030800, +0x54430016, 0x3c030200, 0x8f82009c, 0x3042ffff, +0x14400012, 0x3c030200, 0x3c040001, 0x24847bdc, +0x3c050002, 0x34a5f030, 0x3021, 0x3821, +0x36420002, 0xaf82011c, 0x36220001, 0xaf8200b0, +0xaf900104, 0xaf92011c, 0xafa00010, 0xc002d3b, +0xafa00014, 0x10000025, 0x0, 0x2c31024, +0x1040000e, 0x2231024, 0x1040000c, 0x36420002, +0xaf82011c, 0x36220001, 0xaf8200b0, 0xaf900104, +0xaf92011c, 0x8f420330, 0x24420001, 0xaf420330, +0x8f420330, 0x10000015, 0x0, 0x3c040001, +0x24847ba8, 0x240202a9, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e77bb0, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x8fbf0024, +0x8fb20020, 0x8fb1001c, 0x8fb00018, 0x3e00008, +0x27bd0028, 0x27bdffd8, 0x3c040001, 0x24847be4, +0x3c050001, 0xafbf0024, 0xafb20020, 0xafb1001c, +0xafb00018, 0x8f900124, 0x8f9100a0, 0x8f92011c, +0x34a52600, 0x8f820120, 0x2403021, 0x2203821, +0xafa20010, 0xc002d3b, 0xafb00014, 0x8e020008, +0xafa20010, 0x8e02000c, 0x3c040001, 0x24847bf0, +0xafa20014, 0x8e060000, 0x8e070004, 0x3c050001, +0xc002d3b, 0x34a52610, 0x8e020018, 0xafa20010, +0x8e02001c, 0x3c040001, 0x24847bfc, 0xafa20014, +0x8e060010, 0x8e070014, 0x3c050001, 0xc002d3b, +0x34a52620, 0x3c027f00, 0x2221024, 0x3c030800, +0x54430016, 0x3c030200, 0x8f8200ac, 0x3042ffff, +0x14400012, 0x3c030200, 0x3c040001, 0x24847c08, +0x3c050001, 0x34a5f030, 0x3021, 0x3821, +0x36420002, 0xaf82011c, 0x36220001, 0xaf8200a0, +0xaf900124, 0xaf92011c, 0xafa00010, 0xc002d3b, +0xafa00014, 0x10000025, 0x0, 0x2c31024, +0x1040000e, 0x2231024, 0x1040000c, 0x36420002, +0xaf82011c, 0x36220001, 0xaf8200a0, 0xaf900124, +0xaf92011c, 0x8f42032c, 0x24420001, 0xaf42032c, +0x8f42032c, 0x10000015, 0x0, 0x3c040001, +0x24847ba8, 0x240202e2, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e77bb0, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x8fbf0024, +0x8fb20020, 0x8fb1001c, 0x8fb00018, 0x3e00008, +0x27bd0028, 0x6021, 0x5021, 0x3021, +0x2821, 0x6821, 0x4821, 0x7821, +0x7021, 0x8f880124, 0x8f870104, 0x8f8b011c, +0x1580002e, 0x0, 0x11a00014, 0x31620800, +0x8f820120, 0x10460029, 0x0, 0x3c040002, +0x8c8487a0, 0x8cc20000, 0x8cc30004, 0xac820000, +0xac830004, 0x8cc20008, 0xac820008, 0x94c2000e, +0xa482000e, 0x8cc20010, 0x240c0001, 0xac820010, +0x8cc20014, 0x10000012, 0x24c60020, 0x10400017, +0x0, 0x3c040002, 0x8c8487a0, 0x8d020000, +0x8d030004, 0xac820000, 0xac830004, 0x8d020008, +0xac820008, 0x9502000e, 0xa482000e, 0x8d020010, +0x25060020, 0xac820010, 0x8d020014, 0x240c0001, +0xc01821, 0xac820014, 0x27624fe0, 0x43102b, +0x54400001, 0x27634800, 0x603021, 0x1540002f, +0x31620100, 0x11200014, 0x31628000, 0x8f820100, +0x1045002a, 0x31620100, 0x3c040002, 0x8c84879c, +0x8ca20000, 0x8ca30004, 0xac820000, 0xac830004, +0x8ca20008, 0xac820008, 0x94a2000e, 0xa482000e, +0x8ca20010, 0x240a0001, 0xac820010, 0x8ca20014, +0x10000012, 0x24a50020, 0x10400018, 0x31620100, +0x3c040002, 0x8c84879c, 0x8ce20000, 0x8ce30004, +0xac820000, 0xac830004, 0x8ce20008, 0xac820008, +0x94e2000e, 0xa482000e, 0x8ce20010, 0x24e50020, +0xac820010, 0x8ce20014, 0x240a0001, 0xa01821, +0xac820014, 0x276247e0, 0x43102b, 0x54400001, +0x27634000, 0x602821, 0x31620100, 0x5440001d, +0x31621000, 0x11a00009, 0x31a20800, 0x10400004, +0x25020020, 0x8f8200a8, 0xa5e20000, 0x25020020, +0xaf820124, 0x8f880124, 0x6821, 0x11800011, +0x31621000, 0x3c040002, 0x8c8487a0, 0x8c820000, +0x8c830004, 0xaf820080, 0xaf830084, 0x8c820008, +0xaf8200a4, 0x9482000e, 0xaf8200ac, 0x8c820010, +0x6021, 0xaf8200a0, 0x8c8d0010, 0x8c8f0014, +0x31621000, 0x1440ff81, 0x0, 0x1120000f, +0x31220800, 0x10400004, 0x3c020002, 0x8f8200b8, +0xa5c20000, 0x3c020002, 0x1221024, 0x10400004, +0x24e20020, 0x8f8200b4, 0xaf8200d4, 0x24e20020, +0xaf820104, 0x8f870104, 0x4821, 0x1140ff6f, +0x0, 0x3c040002, 0x8c84879c, 0x8c820000, +0x8c830004, 0xaf820090, 0xaf830094, 0x8c820008, +0xaf8200b4, 0x9482000e, 0xaf82009c, 0x8c820010, +0x5021, 0xaf8200b0, 0x8c890010, 0x8c8e0014, +0x1000ff5e, 0x0, 0x3e00008, 0x0, +0x6021, 0x5821, 0x3021, 0x2821, +0x6821, 0x5021, 0x7821, 0x7021, +0x8f880124, 0x8f870104, 0x3c180100, 0x8f89011c, +0x1580002e, 0x0, 0x11a00014, 0x31220800, +0x8f820120, 0x10460029, 0x0, 0x3c040002, +0x8c8487a0, 0x8cc20000, 0x8cc30004, 0xac820000, +0xac830004, 0x8cc20008, 0xac820008, 0x94c2000e, +0xa482000e, 0x8cc20010, 0x240c0001, 0xac820010, +0x8cc20014, 0x10000012, 0x24c60020, 0x10400017, +0x0, 0x3c040002, 0x8c8487a0, 0x8d020000, +0x8d030004, 0xac820000, 0xac830004, 0x8d020008, +0xac820008, 0x9502000e, 0xa482000e, 0x8d020010, +0x25060020, 0xac820010, 0x8d020014, 0x240c0001, +0xc01821, 0xac820014, 0x27624fe0, 0x43102b, +0x54400001, 0x27634800, 0x603021, 0x1560002f, +0x31220100, 0x11400014, 0x31228000, 0x8f820100, +0x1045002a, 0x31220100, 0x3c040002, 0x8c84879c, +0x8ca20000, 0x8ca30004, 0xac820000, 0xac830004, +0x8ca20008, 0xac820008, 0x94a2000e, 0xa482000e, +0x8ca20010, 0x240b0001, 0xac820010, 0x8ca20014, +0x10000012, 0x24a50020, 0x10400018, 0x31220100, +0x3c040002, 0x8c84879c, 0x8ce20000, 0x8ce30004, +0xac820000, 0xac830004, 0x8ce20008, 0xac820008, +0x94e2000e, 0xa482000e, 0x8ce20010, 0x24e50020, +0xac820010, 0x8ce20014, 0x240b0001, 0xa01821, +0xac820014, 0x276247e0, 0x43102b, 0x54400001, +0x27634000, 0x602821, 0x31220100, 0x5440001d, +0x31221000, 0x11a00009, 0x31a20800, 0x10400004, +0x25020020, 0x8f8200a8, 0xa5e20000, 0x25020020, +0xaf820124, 0x8f880124, 0x6821, 0x11800011, +0x31221000, 0x3c040002, 0x8c8487a0, 0x8c820000, +0x8c830004, 0xaf820080, 0xaf830084, 0x8c820008, +0xaf8200a4, 0x9482000e, 0xaf8200ac, 0x8c820010, +0x6021, 0xaf8200a0, 0x8c8d0010, 0x8c8f0014, +0x31221000, 0x14400022, 0x0, 0x1140000f, +0x31420800, 0x10400004, 0x3c020002, 0x8f8200b8, +0xa5c20000, 0x3c020002, 0x1421024, 0x10400004, +0x24e20020, 0x8f8200b4, 0xaf8200d4, 0x24e20020, +0xaf820104, 0x8f870104, 0x5021, 0x11600010, +0x0, 0x3c040002, 0x8c84879c, 0x8c820000, +0x8c830004, 0xaf820090, 0xaf830094, 0x8c820008, +0xaf8200b4, 0x9482000e, 0xaf82009c, 0x8c820010, +0x5821, 0xaf8200b0, 0x8c8a0010, 0x8c8e0014, +0x8f820070, 0x3c031000, 0x431024, 0x1040ff5b, +0x0, 0x8f820054, 0x24420005, 0xaf820078, +0x8c040234, 0x10800017, 0x1821, 0x3c020001, +0x571021, 0x8c4240e8, 0x24420005, 0x3c010001, +0x370821, 0xac2240e8, 0x3c020001, 0x571021, +0x8c4240e8, 0x44102b, 0x1440000a, 0x24020001, +0x3c030080, 0x3c010001, 0x370821, 0xac2040e8, +0x3c010001, 0x370821, 0xa02240f0, 0x1000000c, +0x0, 0x3c020001, 0x571021, 0x904240f0, +0x14400006, 0x3c020080, 0x3c020001, 0x571021, +0x904240f1, 0x10400002, 0x3c020080, 0x621825, +0x8c040230, 0x10800013, 0x0, 0x3c020001, +0x571021, 0x8c4240ec, 0x24420005, 0x3c010001, +0x370821, 0xac2240ec, 0x3c020001, 0x571021, +0x8c4240ec, 0x44102b, 0x14400006, 0x0, +0x3c010001, 0x370821, 0xac2040ec, 0x10000006, +0x781825, 0x3c020001, 0x571021, 0x904240f2, +0x54400001, 0x781825, 0x1060ff18, 0x0, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x431025, 0xaf820060, +0x8f420000, 0x10400004, 0x0, 0xaf80004c, +0x1000ff02, 0x0, 0xaf800048, 0x1000feff, +0x0, 0x3e00008, 0x0, 0x3c020002, +0x8c428608, 0x27bdffe8, 0xafbf0014, 0x14400012, +0xafb00010, 0x3c100002, 0x26108850, 0x2002021, +0xc002da8, 0x24052000, 0x26021fe0, 0x3c010002, +0xac2287a8, 0x3c010002, 0xac2287a4, 0xac020250, +0x24022000, 0xac100254, 0xac020258, 0x24020001, +0x3c010002, 0xac228608, 0x8fbf0014, 0x8fb00010, +0x3e00008, 0x27bd0018, 0x3c090002, 0x8d2987a8, +0x8c820000, 0x8fa30010, 0x8fa80014, 0xad220000, +0x8c820004, 0xad250008, 0xad220004, 0x8f820054, +0xad260010, 0xad270014, 0xad230018, 0xad28001c, +0xad22000c, 0x2529ffe0, 0x3c020002, 0x24428850, +0x122102b, 0x10400003, 0x0, 0x3c090002, +0x8d2987a4, 0x3c020002, 0x8c4285fc, 0xad220000, +0x3c020002, 0x8c4285fc, 0x3c010002, 0xac2987a8, +0xad220004, 0xac090250, 0x3e00008, 0x0, +0x27bdffd0, 0xafb00010, 0x3c100002, 0x8e1087a8, +0x3c020002, 0x8c4285fc, 0xafb10014, 0x808821, +0xafbe0024, 0x8fbe0040, 0x8fa40048, 0xafb20018, +0xa09021, 0xafbf0028, 0xafb50020, 0xafb3001c, +0xae020000, 0x3c020002, 0x8c4285fc, 0xc09821, +0xe0a821, 0x10800006, 0xae020004, 0x26050008, +0xc002db3, 0x24060018, 0x10000005, 0x2610ffe0, +0x26040008, 0xc002da8, 0x24050018, 0x2610ffe0, +0x3c030002, 0x24638850, 0x203102b, 0x10400003, +0x0, 0x3c100002, 0x8e1087a4, 0x8e220000, +0xae020000, 0x8e220004, 0xae120008, 0xae020004, +0x8f820054, 0xae130010, 0xae150014, 0xae1e0018, +0x8fa80044, 0xae08001c, 0xae02000c, 0x2610ffe0, +0x203102b, 0x10400003, 0x0, 0x3c100002, +0x8e1087a4, 0x3c020002, 0x8c4285fc, 0xae020000, +0x3c020002, 0x8c4285fc, 0x3c010002, 0xac3087a8, +0xae020004, 0xac100250, 0x8fbf0028, 0x8fbe0024, +0x8fb50020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, +0x8fb00010, 0x3e00008, 0x27bd0030, 0x851821, +0x83102b, 0x10400006, 0x0, 0xac800000, +0x24840004, 0x83102b, 0x5440fffd, 0xac800000, +0x3e00008, 0x0, 0xa61821, 0xa3102b, +0x10400007, 0x0, 0x8c820000, 0xaca20000, +0x24a50004, 0xa3102b, 0x1440fffb, 0x24840004, +0x3e00008, 0x0, 0x861821, 0x83102b, +0x10400007, 0x0, 0x8ca20000, 0xac820000, +0x24840004, 0x83102b, 0x1440fffb, 0x24a50004, +0x3e00008, 0x0, 0x63080, 0x861821, +0x83102b, 0x10400006, 0x0, 0xac850000, +0x24840004, 0x83102b, 0x5440fffd, 0xac850000, +0x3e00008, 0x0, 0x26e50028, 0xa03021, +0x274301c0, 0x8f4d0358, 0x8f47035c, 0x8f480360, +0x8f490364, 0x8f4a0368, 0x8f4b0204, 0x8f4c0200, +0x24640400, 0x64102b, 0x10400008, 0x3c0208ff, +0x8cc20000, 0xac620000, 0x24630004, 0x64102b, +0x1440fffb, 0x24c60004, 0x3c0208ff, 0x3442ffff, +0x3c03c0ff, 0xaf4d0358, 0xaf47035c, 0xaf480360, +0xaf490364, 0xaf4a0368, 0xaf4b0204, 0xaf4c0200, +0x8f840220, 0x3463ffff, 0x8f860200, 0x821024, +0x34420004, 0xc31824, 0x34630004, 0xaf820220, +0xaf830200, 0x8ca20214, 0xac020084, 0x8ca20218, +0xac020088, 0x8ca2021c, 0xac02008c, 0x8ca20220, +0xac020090, 0x8ca20224, 0xac020094, 0x8ca20228, +0xac020098, 0x8ca2022c, 0xac02009c, 0x8ca20230, +0xac0200a0, 0x8ca20234, 0xac0200a4, 0x8ca20238, +0xac0200a8, 0x8ca2023c, 0xac0200ac, 0x8ca20240, +0xac0200b0, 0x8ca20244, 0xac0200b4, 0x8ca20248, +0xac0200b8, 0x8ca2024c, 0xac0200bc, 0x8ca2001c, +0xac020080, 0x8ca20018, 0xac0200c0, 0x8ca20020, +0xac0200cc, 0x8ca20024, 0xac0200d0, 0x8ca201d0, +0xac0200e0, 0x8ca201d4, 0xac0200e4, 0x8ca201d8, +0xac0200e8, 0x8ca201dc, 0xac0200ec, 0x8ca201e0, +0xac0200f0, 0x8ca20098, 0x8ca3009c, 0xac0300fc, +0x8ca200a8, 0x8ca300ac, 0xac0300f4, 0x8ca200a0, +0x8ca300a4, 0x30840004, 0xac0300f8, 0x14800007, +0x30c20004, 0x8f820220, 0x3c0308ff, 0x3463fffb, +0x431024, 0xaf820220, 0x30c20004, 0x14400006, +0x0, 0x8f820200, 0x3c03c0ff, 0x3463fffb, +0x431024, 0xaf820200, 0x8f4202dc, 0xa34005c5, +0x24420001, 0xaf4202dc, 0x8f4202dc, 0x3e00008, +0x0, 0x27bdffd0, 0xafbf0028, 0xafb10024, +0xafb00020, 0x8f430024, 0x8f420020, 0x1062004e, +0x0, 0x8f430020, 0x8f420024, 0x628823, +0x6210003, 0x0, 0x8f420040, 0x2228821, +0x8f430030, 0x8f420024, 0x43102b, 0x14400005, +0x0, 0x8f430040, 0x8f420024, 0x10000005, +0x628023, 0x8f420030, 0x8f430024, 0x431023, +0x2450ffff, 0x16000016, 0x2006821, 0x3c040001, +0x24847e74, 0x240202aa, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e77e84, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x2006821, +0x22d102a, 0x54400001, 0x2206821, 0x8f4b0024, +0x8f4a0040, 0x8f490024, 0x8f440180, 0x8f450184, +0x8f460024, 0x8f4c001c, 0xd3900, 0x24080001, +0xafa80010, 0x94900, 0x1201821, 0x16d5821, +0x254affff, 0x16a8024, 0xafb00014, 0x8f480014, +0x1021, 0xa32821, 0xa3482b, 0x822021, +0x892021, 0xafa80018, 0x8f420108, 0x63100, +0x40f809, 0x1863021, 0x54400001, 0xaf500024, +0x8f430024, 0x8f420020, 0x14620019, 0x0, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x2403ffef, 0x431024, +0xaf820060, 0x8f420000, 0x10400004, 0x0, +0xaf80004c, 0x10000002, 0x0, 0xaf800048, +0x8fbf0028, 0x8fb10024, 0x8fb00020, 0x3e00008, +0x27bd0030, 0x3e00008, 0x0, 0x27bdffc0, +0x32c20020, 0xafbf0038, 0xafb30034, 0xafb20030, +0xafb1002c, 0x10400004, 0xafb00028, 0x8f530028, +0x10000002, 0x0, 0x8f530020, 0x8f420030, +0x10530102, 0x21100, 0x8f43001c, 0x628021, +0x8e040000, 0x8e050004, 0x96120008, 0x8f420090, +0x9611000a, 0x3246ffff, 0x46102a, 0x10400018, +0x0, 0x8f8200d8, 0x8f430098, 0x431023, +0x2442dcbe, 0xaf420090, 0x8f420090, 0x2842dcbf, +0x10400005, 0x0, 0x8f420090, 0x8f430144, +0x431021, 0xaf420090, 0x8f420090, 0x46102a, +0x10400007, 0x0, 0x8f420348, 0x24420001, +0xaf420348, 0x8f420348, 0x100000f8, 0x0, +0x8f8200fc, 0x14400007, 0x0, 0x8f420344, +0x24420001, 0xaf420344, 0x8f420344, 0x100000ef, +0x0, 0x934205c2, 0x1040000b, 0x32c20008, +0x10400008, 0x32220200, 0x10400006, 0x3c034000, +0x9602000e, 0xaf4300ac, 0x21400, 0x10000002, +0xaf4200b0, 0xaf4000ac, 0x32220004, 0x10400094, +0x32220800, 0x10400003, 0x3247ffff, 0x10000002, +0x24020020, 0x24020004, 0xafa20010, 0x8f420030, +0xafa20014, 0x8f420010, 0x3c030002, 0x431025, +0xafa20018, 0x8f460098, 0x8f420108, 0x40f809, +0x0, 0x104000cd, 0x0, 0x8f42009c, +0x8f430094, 0x2421021, 0xaf42009c, 0xae03000c, +0x8f4200ac, 0x10400008, 0x3c034000, 0x8f420094, +0x431025, 0xafa20020, 0x8f42009c, 0x8f4300b0, +0x10000004, 0x431025, 0x8f420094, 0xafa20020, +0x8f42009c, 0xafa20024, 0x8f9000fc, 0x16000014, +0x240200e1, 0x3c040001, 0x24847e74, 0xafa20010, +0xafa00014, 0x8f860144, 0x3c070001, 0x24e77e7c, +0xc002d3b, 0x3405dead, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, +0x8f820140, 0x3c030001, 0x431025, 0xaf820140, +0x8fa20020, 0x8fa30024, 0xae020000, 0xae030004, +0x26020008, 0xaf8200f0, 0x8f42009c, 0x8f440270, +0x8f450274, 0x401821, 0x1021, 0xa32821, +0xa3302b, 0x822021, 0x862021, 0x32230060, +0x24020040, 0xaf440270, 0xaf450274, 0x10620017, +0x2c620041, 0x10400005, 0x24020020, 0x10620008, +0x24020001, 0x10000026, 0x0, 0x24020060, +0x10620019, 0x24020001, 0x10000021, 0x0, +0x8f420278, 0x8f43027c, 0x24630001, 0x2c640001, +0x441021, 0xaf420278, 0xaf43027c, 0x8f420278, +0x8f43027c, 0x10000016, 0x24020001, 0x8f420280, +0x8f430284, 0x24630001, 0x2c640001, 0x441021, +0xaf420280, 0xaf430284, 0x8f420280, 0x8f430284, +0x1000000b, 0x24020001, 0x8f420288, 0x8f43028c, +0x24630001, 0x2c640001, 0x441021, 0xaf420288, +0xaf43028c, 0x8f420288, 0x8f43028c, 0x24020001, +0xa34205c2, 0x8f420098, 0x3244ffff, 0x2406fff8, +0x8f45013c, 0x441021, 0x24420007, 0x461024, +0x24840007, 0xaf420094, 0x8f420090, 0x8f430094, +0x862024, 0x441023, 0x65182b, 0x14600005, +0xaf420090, 0x8f420094, 0x8f430144, 0x431023, +0xaf420094, 0x8f420094, 0x10000023, 0xaf40009c, +0x3247ffff, 0x50e00022, 0x32c20020, 0x14400002, +0x24020010, 0x24020002, 0xafa20010, 0x8f420030, +0xafa20014, 0x8f420010, 0xafa20018, 0x8f460098, +0x8f420108, 0x40f809, 0x0, 0x1040003b, +0x3245ffff, 0x8f420098, 0x8f430090, 0x8f46013c, +0x451021, 0xaf420098, 0x8f42009c, 0x8f440098, +0xa34005c2, 0x651823, 0xaf430090, 0x451021, +0x86202b, 0x14800005, 0xaf42009c, 0x8f420098, +0x8f430144, 0x431023, 0xaf420098, 0x32c20020, +0x10400005, 0x0, 0x8f420358, 0x2442ffff, +0xaf420358, 0x8f420358, 0x8f420030, 0x8f430040, +0x24420001, 0x2463ffff, 0x431024, 0xaf420030, +0x8f420030, 0x14530019, 0x0, 0x8f420000, +0x10400007, 0x0, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x2403fff7, 0x431024, 0xaf820060, +0x8f420000, 0x10400004, 0x0, 0xaf80004c, +0x10000002, 0x0, 0xaf800048, 0x8fbf0038, +0x8fb30034, 0x8fb20030, 0x8fb1002c, 0x8fb00028, +0x3e00008, 0x27bd0040, 0x3e00008, 0x0, +0x27bdffd0, 0x32c20020, 0xafbf002c, 0xafb20028, +0xafb10024, 0x10400004, 0xafb00020, 0x8f520028, +0x10000002, 0x0, 0x8f520020, 0x8f420030, +0x105200b7, 0x21100, 0x8f43001c, 0x628021, +0x8e040000, 0x8e050004, 0x96110008, 0x8f420090, +0x9607000a, 0x3226ffff, 0x46102a, 0x10400018, +0x0, 0x8f8200d8, 0x8f430098, 0x431023, +0x2442dc46, 0xaf420090, 0x8f420090, 0x2842dc47, +0x10400005, 0x0, 0x8f420090, 0x8f430144, +0x431021, 0xaf420090, 0x8f420090, 0x46102a, +0x10400007, 0x0, 0x8f420348, 0x24420001, +0xaf420348, 0x8f420348, 0x100000ad, 0x0, +0x8f8600fc, 0x10c0000c, 0x0, 0x8f8200f4, +0x2403fff8, 0x431024, 0x461023, 0x218c3, +0x58600001, 0x24630100, 0x8f42008c, 0x43102b, +0x14400007, 0x712c2, 0x8f420344, 0x24420001, +0xaf420344, 0x8f420344, 0x10000099, 0x0, +0x934305c2, 0x1060000f, 0x30460001, 0x8f420010, +0x34480400, 0x32c20008, 0x10400008, 0x30e20200, +0x10400006, 0x3c034000, 0x9602000e, 0xaf4300ac, +0x21400, 0x10000004, 0xaf4200b0, 0x10000002, +0xaf4000ac, 0x8f480010, 0x30e20004, 0x10400045, +0x3227ffff, 0x8f4900ac, 0x11200005, 0x30c200ff, +0x14400006, 0x24020040, 0x10000004, 0x24020008, +0x14400002, 0x24020020, 0x24020004, 0xafa20010, +0x8f430030, 0x11200004, 0xafa30014, 0x8f4200b0, +0x621025, 0xafa20014, 0x3c020002, 0x1021025, +0xafa20018, 0x8f460098, 0x8f420108, 0x40f809, +0x0, 0x1040006a, 0x3224ffff, 0x8f42008c, +0x8f430094, 0x24420001, 0xaf42008c, 0x24020001, +0xae03000c, 0xa34205c2, 0x8f420098, 0x2406fff8, +0x8f45013c, 0x441021, 0x24420007, 0x461024, +0x24840007, 0xaf420094, 0x8f420090, 0x8f430094, +0x862024, 0x441023, 0x65182b, 0x14600005, +0xaf420090, 0x8f420094, 0x8f430144, 0x431023, +0xaf420094, 0x8f430094, 0x8f420140, 0x43102b, +0x10400009, 0x0, 0x8f43013c, 0x8f440094, +0x8f420090, 0x8f450138, 0x641823, 0x431023, +0xaf420090, 0xaf450094, 0x8f420094, 0x1000001f, +0xaf420098, 0x10e0001d, 0x30c200ff, 0x14400002, +0x24020010, 0x24020002, 0xafa20010, 0x8f420030, +0xafa80018, 0xafa20014, 0x8f460098, 0x8f420108, +0x40f809, 0x0, 0x10400031, 0x3225ffff, +0x8f420098, 0x8f44013c, 0x451021, 0xaf420098, +0x8f420090, 0x8f430098, 0xa34005c2, 0x451023, +0x64182b, 0x14600005, 0xaf420090, 0x8f420098, +0x8f430144, 0x431023, 0xaf420098, 0x8f420030, +0x8f430040, 0x24420001, 0x2463ffff, 0x431024, +0xaf420030, 0x8f420030, 0x14520019, 0x0, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x2403fff7, 0x431024, +0xaf820060, 0x8f420000, 0x10400004, 0x0, +0xaf80004c, 0x10000002, 0x0, 0xaf800048, +0x8fbf002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, +0x3e00008, 0x27bd0030, 0x3e00008, 0x0, +0x27bdffd8, 0xafbf0024, 0xafb00020, 0x8f4300f0, +0x8f4200ec, 0x8f900108, 0x14620017, 0x3c020001, +0x3c040001, 0x24847e74, 0x240204ea, 0xafa20010, +0xafa00014, 0x8f860144, 0x3c070001, 0x24e77e84, +0xc002d3b, 0x3405dead, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, +0x8f820140, 0x3c030001, 0x431025, 0xaf820140, +0x3c020001, 0x8f4300f0, 0x34422ec0, 0x2e21021, +0x54620004, 0x24620008, 0x3c020001, 0x34422cc0, +0x2e21021, 0x401821, 0xaf4300f0, 0xac600000, +0x8f4200ec, 0x8c660004, 0x14620005, 0x3c020001, +0x26020020, 0xaf820108, 0x1000000f, 0x0, +0x8f4300f0, 0x34422ec0, 0x2e21021, 0x54620004, +0x24620008, 0x3c020001, 0x34422cc0, 0x2e21021, +0x401821, 0x8c620004, 0x21140, 0x2021021, +0xaf820108, 0xac600000, 0x8e050018, 0x30a20036, +0x1040006d, 0x30a20001, 0x8e02001c, 0x8f430040, +0x8f440034, 0x24420001, 0x2463ffff, 0x431024, +0x862021, 0xaf42002c, 0x30a20030, 0x14400006, +0xaf440034, 0x8f420034, 0x8c03023c, 0x43102b, +0x144000cf, 0x0, 0x32c20010, 0x10400028, +0x24070008, 0x8f440170, 0x8f450174, 0x8f43002c, +0x8f48000c, 0x8f860120, 0x24020080, 0xafa20010, +0xafa30014, 0xafa80018, 0x8f42010c, 0x40f809, +0x24c6001c, 0x14400011, 0x24020001, 0x3c010001, +0x370821, 0xa02240f1, 0x8f820124, 0xafa20010, +0x8f820128, 0x3c040001, 0x24847e64, 0xafa20014, +0x8f46002c, 0x8f870120, 0x3c050009, 0xc002d3b, +0x34a51100, 0x10000036, 0x0, 0x8f420300, +0x8f43002c, 0x24420001, 0xaf420300, 0x8f420300, +0x24020001, 0xa34205c1, 0x10000026, 0xaf430038, +0x8f440170, 0x8f450174, 0x8f43002c, 0x8f48000c, +0x8f860120, 0x24020020, 0xafa20010, 0xafa30014, +0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400011, 0x24020001, 0x3c010001, 0x370821, +0xa02240f0, 0x8f820124, 0xafa20010, 0x8f820128, +0x3c040001, 0x24847e58, 0xafa20014, 0x8f46002c, +0x8f870120, 0x3c050009, 0xc002d3b, 0x34a50900, +0x1000000f, 0x0, 0x8f420300, 0x24420001, +0xaf420300, 0x8f420300, 0x8f42002c, 0xa34005c1, +0xaf420038, 0x3c010001, 0x370821, 0xa02040f1, +0x3c010001, 0x370821, 0xa02040f0, 0xaf400034, +0x8f420314, 0x24420001, 0xaf420314, 0x8f420314, +0x10000073, 0x0, 0x10400025, 0x30a27000, +0x8e05001c, 0x8f420028, 0xa22023, 0x4810003, +0x0, 0x8f420040, 0x822021, 0x8f420358, +0x8f430000, 0xaf450028, 0x441021, 0xaf420358, +0x10600007, 0x0, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x34420008, 0xaf820060, 0x8f420000, +0x10400004, 0x0, 0xaf80004c, 0x10000050, +0x0, 0xaf800048, 0x1000004d, 0x0, +0x1040002f, 0x30a21000, 0x1040000c, 0x30a24000, +0x8e03001c, 0x8f420050, 0x622023, 0x4820001, +0x24840200, 0x8f42035c, 0x441021, 0xaf42035c, +0x8f420368, 0x1000001a, 0xaf430050, 0x1040000c, +0x32c28000, 0x8e03001c, 0x8f420070, 0x622023, +0x4820001, 0x24840400, 0x8f420364, 0x441021, +0xaf420364, 0x8f420368, 0x1000000d, 0xaf430070, +0x1040000e, 0x3c020800, 0x8e03001c, 0x8f420060, +0x622023, 0x4820001, 0x24840100, 0x8f420360, +0x441021, 0xaf420360, 0x8f420368, 0xaf430060, +0x441021, 0xaf420368, 0x3c020800, 0x2c21024, +0x5040001f, 0x36940040, 0x1000001d, 0x0, +0x30a20100, 0x10400005, 0x30a20080, 0xc002dd7, +0x0, 0x10000016, 0x0, 0x14400014, +0x240205dd, 0x3c040001, 0x24847e74, 0xafa20010, +0xafa00014, 0x8f860144, 0x3c070001, 0x24e77e84, +0xc002d3b, 0x3405dead, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, +0x8f820140, 0x3c030001, 0x431025, 0xaf820140, +0x8fbf0024, 0x8fb00020, 0x3e00008, 0x27bd0028, +0x3e00008, 0x0, 0x27bdff98, 0xafbf0060, +0xafbe005c, 0xafb50058, 0xafb30054, 0xafb20050, +0xafb1004c, 0xafb00048, 0x8f920108, 0x8f820104, +0x16420016, 0x26420020, 0x3c040001, 0x24847e74, +0x240205f8, 0xafa20010, 0xafa00014, 0x8f860144, +0x3c070001, 0x24e77e84, 0xc002d3b, 0x3405dead, +0x8f82011c, 0x34420002, 0xaf82011c, 0x8f820220, +0x34420004, 0xaf820220, 0x8f820140, 0x3c030001, +0x431025, 0xaf820140, 0x26420020, 0xaf820108, +0x8e530018, 0xa3a0003f, 0x32620024, 0x1040022c, +0xafa00034, 0x8e50001c, 0x8f42001c, 0x101900, +0x431021, 0x8c51000c, 0x8f430140, 0x965e0016, +0x9455000a, 0x71182b, 0x10600014, 0x24020634, +0x3c040001, 0x24847e74, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e77e84, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x9624000c, +0x2c8305dd, 0x38828870, 0x2c420001, 0x621825, +0x10600015, 0x2821, 0x32c20040, 0x10400015, +0x24020800, 0x96230014, 0x14620012, 0x3402aaaa, +0x9623000e, 0x14620007, 0x2021, 0x96230010, +0x24020300, 0x14620004, 0x801021, 0x96220012, +0x2c440001, 0x801021, 0x54400006, 0x24050016, +0x10000004, 0x0, 0x24020800, 0x50820001, +0x2405000e, 0x934205c3, 0x14400008, 0x5021, +0x240a0001, 0x32a20180, 0xaf4500a8, 0xaf5100a0, +0x10400002, 0xaf5000a4, 0xa34a05c3, 0x10a00086, +0x2253821, 0x90e20000, 0x3021, 0x3042000f, +0x24880, 0x32c20002, 0x10400012, 0xe91821, +0x32a20002, 0x10400010, 0x32c20001, 0xe02021, +0x94820000, 0x24840002, 0xc23021, 0x83102b, +0x1440fffb, 0x30c2ffff, 0x61c02, 0x623021, +0x61c02, 0x30c2ffff, 0x623021, 0x61027, +0xa4e2000a, 0x32c20001, 0x1040006b, 0x32a20001, +0x10400069, 0x0, 0x8f4200a8, 0x10400066, +0x0, 0x8f4200a0, 0x8f4300a8, 0x431021, +0x904b0009, 0x316800ff, 0x39030006, 0x3182b, +0x39020011, 0x2102b, 0x621824, 0x1060000d, +0x3c050006, 0x3c040001, 0x24847e8c, 0x8f4200a4, +0x34a54600, 0xafa20010, 0x8f4200a0, 0x2003021, +0x1003821, 0xc002d3b, 0xafa20014, 0x1000004e, +0x0, 0x32c20004, 0x14400013, 0x2821, +0x314200ff, 0x14400004, 0x0, 0x94e20002, +0x1000000d, 0x492823, 0x94e5000c, 0x94e2000e, +0x94e30010, 0xa22821, 0xa32821, 0x94e30012, +0x90e40009, 0x94e20002, 0xa32821, 0xa42821, +0x491023, 0xa22821, 0x2202021, 0x94820000, +0x24840002, 0xc23021, 0x87102b, 0x1440fffb, +0x61c02, 0x30c2ffff, 0x623021, 0x61c02, +0x30c2ffff, 0x623021, 0x3c52821, 0x51c02, +0x30a2ffff, 0x622821, 0x51c02, 0x30a2ffff, +0x622821, 0xa62823, 0x51402, 0xa22821, +0x30a5ffff, 0x50a00001, 0x3405ffff, 0x314200ff, +0x14400008, 0x316300ff, 0x8f4300a0, 0x8f4200a8, +0x623821, 0x90e20000, 0x3042000f, 0x24880, +0x316300ff, 0x24020006, 0x14620003, 0xe91021, +0x10000002, 0x24440010, 0x24440006, 0x314200ff, +0x14400006, 0x0, 0x94820000, 0xa22821, +0x51c02, 0x30a2ffff, 0x622821, 0x934205c3, +0x10400003, 0x32a20100, 0x50400003, 0xa4850000, +0x52827, 0xa4850000, 0x9642000e, 0x8f43009c, +0x621821, 0xaf43009c, 0x93a2003f, 0x10400007, +0x3c024000, 0x2221025, 0xafa20020, 0x8f42009c, +0x8fac0034, 0x10000003, 0x4c1025, 0xafb10020, +0x8f42009c, 0xafa20024, 0x32a20080, 0x10400027, +0x32a20100, 0x8f4200b4, 0x2c420100, 0x14400014, +0x2402076a, 0x3c040001, 0x24847e74, 0xafa20010, +0xafa00014, 0x8f860144, 0x3c070001, 0x24e77e84, +0xc002d3b, 0x3405dead, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, +0x8f820140, 0x3c030001, 0x431025, 0xaf820140, +0x8f4200b4, 0x24430001, 0x210c0, 0x571021, +0xaf4300b4, 0x8fa30020, 0x8fa40024, 0x3c010001, +0x220821, 0xac2338e8, 0x3c010001, 0x220821, +0xac2438ec, 0x100000e7, 0x32c20020, 0x10400091, +0x0, 0x8f4200b4, 0x2c420100, 0x14400014, +0x24020778, 0x3c040001, 0x24847e74, 0xafa20010, +0xafa00014, 0x8f860144, 0x3c070001, 0x24e77e84, +0xc002d3b, 0x3405dead, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, +0x8f820140, 0x3c030001, 0x431025, 0xaf820140, +0x8f4200b4, 0x24430001, 0x210c0, 0x571021, +0xaf4300b4, 0x8fa30020, 0x8fa40024, 0x3c010001, +0x220821, 0xac2338e8, 0x3c010001, 0x220821, +0xac2438ec, 0x8f4200b4, 0x10400067, 0x8821, +0x1110c0, 0x571021, 0x3c030001, 0x621821, +0x8c6338e8, 0x3c040001, 0x822021, 0x8c8438ec, +0xafa30028, 0xafa4002c, 0x8f9000fc, 0x16000014, +0x240200f4, 0x3c040001, 0x24847e74, 0xafa20010, +0xafa00014, 0x8f860144, 0x3c070001, 0x24e77e7c, +0xc002d3b, 0x3405dead, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, +0x8f820140, 0x3c030001, 0x431025, 0xaf820140, +0x8fa20028, 0x8fa3002c, 0xae020000, 0xae030004, +0x26020008, 0xaf8200f0, 0x8f42008c, 0x2442ffff, +0xaf42008c, 0x97a2002e, 0x8f440270, 0x8f450274, +0x401821, 0x1021, 0xa32821, 0xa3302b, +0x822021, 0x862021, 0xaf440270, 0xaf450274, +0x8fa30028, 0x3c02001f, 0x3442ffff, 0x622024, +0x90820000, 0x30420001, 0x1440000c, 0x2402ffff, +0x8f420278, 0x8f43027c, 0x24630001, 0x2c640001, +0x441021, 0xaf420278, 0xaf43027c, 0x8f420278, +0x8f43027c, 0x1000001b, 0x0, 0x8c830000, +0x1462000f, 0x3402ffff, 0x94830004, 0x1462000c, +0x0, 0x8f420288, 0x8f43028c, 0x24630001, +0x2c640001, 0x441021, 0xaf420288, 0xaf43028c, +0x8f420288, 0x8f43028c, 0x1000000a, 0x0, +0x8f420280, 0x8f430284, 0x24630001, 0x2c640001, +0x441021, 0xaf420280, 0xaf430284, 0x8f420280, +0x8f430284, 0x8f4200b4, 0x26310001, 0x222102b, +0x1440ff9c, 0x1110c0, 0xa34005c3, 0x10000054, +0xaf4000b4, 0x8f9000fc, 0x16000014, 0x240200f4, +0x3c040001, 0x24847e74, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e77e7c, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x8fa20020, +0x8fa30024, 0xae020000, 0xae030004, 0x26020008, +0xaf8200f0, 0x8f42009c, 0x8f46008c, 0x8f440270, +0x8f450274, 0x401821, 0x1021, 0x24c6ffff, +0xaf46008c, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xaf440270, 0xaf450274, 0x92220000, +0x30420001, 0x1440000c, 0x2402ffff, 0x8f420278, +0x8f43027c, 0x24630001, 0x2c640001, 0x441021, +0xaf420278, 0xaf43027c, 0x8f420278, 0x8f43027c, +0x1000001c, 0x32c20020, 0x8e230000, 0x1462000f, +0x3402ffff, 0x96230004, 0x1462000c, 0x0, +0x8f420288, 0x8f43028c, 0x24630001, 0x2c640001, +0x441021, 0xaf420288, 0xaf43028c, 0x8f420288, +0x8f43028c, 0x1000000b, 0x32c20020, 0x8f420280, +0x8f430284, 0x24630001, 0x2c640001, 0x441021, +0xaf420280, 0xaf430284, 0x8f420280, 0x8f430284, +0x32c20020, 0x10400005, 0xaf40009c, 0x8f420358, +0x2442ffff, 0xaf420358, 0x8f420358, 0x8e42001c, +0x8f430040, 0x24420001, 0x2463ffff, 0x431024, +0xaf42002c, 0x32620060, 0x14400008, 0x32c20010, +0x8f420034, 0x24420001, 0xaf420034, 0x8c03023c, +0x43102b, 0x1440011f, 0x32c20010, 0x10400018, +0x24070008, 0x8f440170, 0x8f450174, 0x8f43002c, +0x8f48000c, 0x8f860120, 0x24020080, 0xafa20010, +0xafa30014, 0xafa80018, 0x8f42010c, 0x40f809, +0x24c6001c, 0x10400047, 0x24020001, 0x8f420300, +0x8f43002c, 0x24420001, 0xaf420300, 0x8f420300, +0x24020001, 0xa34205c1, 0x1000007c, 0xaf430038, +0x8f440170, 0x8f450174, 0x8f43002c, 0x8f48000c, +0x8f860120, 0x24020020, 0xafa20010, 0xafa30014, +0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x10400057, 0x24020001, 0x10000065, 0x0, +0x32620012, 0x10400076, 0x32620001, 0x9642000e, +0x8f43009c, 0x621821, 0x32c20020, 0x10400005, +0xaf43009c, 0x8f420358, 0x2442ffff, 0xaf420358, +0x8f420358, 0x8e42001c, 0x8f430040, 0x24420001, +0x2463ffff, 0x431024, 0xaf42002c, 0x32620010, +0x14400008, 0x32c20010, 0x8f420034, 0x24420001, +0xaf420034, 0x8c03023c, 0x43102b, 0x144000d9, +0x32c20010, 0x10400028, 0x24070008, 0x8f440170, +0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, +0x24020080, 0xafa20010, 0xafa30014, 0xafa80018, +0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, +0x24020001, 0x3c010001, 0x370821, 0xa02240f1, +0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, +0x24847e64, 0xafa20014, 0x8f46002c, 0x8f870120, +0x3c050009, 0xc002d3b, 0x34a51100, 0x10000036, +0x0, 0x8f420300, 0x8f43002c, 0x24420001, +0xaf420300, 0x8f420300, 0x24020001, 0xa34205c1, +0x10000026, 0xaf430038, 0x8f440170, 0x8f450174, +0x8f43002c, 0x8f48000c, 0x8f860120, 0x24020020, +0xafa20010, 0xafa30014, 0xafa80018, 0x8f42010c, +0x40f809, 0x24c6001c, 0x14400011, 0x24020001, +0x3c010001, 0x370821, 0xa02240f0, 0x8f820124, +0xafa20010, 0x8f820128, 0x3c040001, 0x24847e58, +0xafa20014, 0x8f46002c, 0x8f870120, 0x3c050009, +0xc002d3b, 0x34a50900, 0x1000000f, 0x0, +0x8f420300, 0x24420001, 0xaf420300, 0x8f420300, +0x8f42002c, 0xa34005c1, 0xaf420038, 0x3c010001, +0x370821, 0xa02040f1, 0x3c010001, 0x370821, +0xa02040f0, 0xaf400034, 0x8f420314, 0x24420001, +0xaf420314, 0x8f420314, 0x1000007e, 0x0, +0x10400025, 0x32627000, 0x8e45001c, 0x8f420028, +0xa22023, 0x4810003, 0x0, 0x8f420040, +0x822021, 0x8f420358, 0x8f430000, 0xaf450028, +0x441021, 0xaf420358, 0x10600007, 0x0, +0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, +0x10000005, 0x0, 0xaf800048, 0x8f820048, +0x1040fffd, 0x0, 0x8f820060, 0x34420008, +0xaf820060, 0x8f420000, 0x10400004, 0x0, +0xaf80004c, 0x1000005b, 0x0, 0xaf800048, +0x10000058, 0x0, 0x1040002f, 0x32621000, +0x1040000c, 0x32624000, 0x8e43001c, 0x8f420050, +0x622023, 0x4820001, 0x24840200, 0x8f42035c, +0x441021, 0xaf42035c, 0x8f420368, 0x1000001a, +0xaf430050, 0x1040000c, 0x32c28000, 0x8e43001c, +0x8f420070, 0x622023, 0x4820001, 0x24840400, +0x8f420364, 0x441021, 0xaf420364, 0x8f420368, +0x1000000d, 0xaf430070, 0x1040000e, 0x3c020800, +0x8e43001c, 0x8f420060, 0x622023, 0x4820001, +0x24840100, 0x8f420360, 0x441021, 0xaf420360, +0x8f420368, 0xaf430060, 0x441021, 0xaf420368, +0x3c020800, 0x2c21024, 0x5040002a, 0x36940040, +0x10000028, 0x0, 0x32620048, 0x10400009, +0x240c0001, 0x8e42001c, 0x3c03ffff, 0xa3ac003f, +0x431824, 0x3042ffff, 0xafa30034, 0x1000fcfd, +0xae42001c, 0x32620100, 0x10400005, 0x32620080, +0xc002dd7, 0x0, 0x10000016, 0x0, +0x14400014, 0x24020896, 0x3c040001, 0x24847e74, +0xafa20010, 0xafa00014, 0x8f860144, 0x3c070001, +0x24e77e84, 0xc002d3b, 0x3405dead, 0x8f82011c, +0x34420002, 0xaf82011c, 0x8f820220, 0x34420004, +0xaf820220, 0x8f820140, 0x3c030001, 0x431025, +0xaf820140, 0x8fbf0060, 0x8fbe005c, 0x8fb50058, +0x8fb30054, 0x8fb20050, 0x8fb1004c, 0x8fb00048, +0x3e00008, 0x27bd0068, 0x3e00008, 0x0, +0x8f8300e4, 0x8f8200e0, 0x2404fff8, 0x441024, +0x621026, 0x2102b, 0x21023, 0x3e00008, +0x621024, 0x3e00008, 0x0, 0x27bdffe0, +0xafbf001c, 0xafb00018, 0x8f8600c4, 0x8f8400e0, +0x8f8500e4, 0x2402fff8, 0x821824, 0x10a30009, +0x27623ff8, 0x14a20002, 0x24a20008, 0x27623000, +0x408021, 0x16030005, 0x30820004, 0x10400004, +0xc02021, 0x10000022, 0x1021, 0x8e040000, +0x8f42011c, 0x14a20003, 0x0, 0x8f420120, +0xaf420114, 0x8ca30000, 0x8f420148, 0x831823, +0x43102b, 0x10400003, 0x0, 0x8f420148, +0x621821, 0x94a20006, 0x24420050, 0x62102b, +0x1440000f, 0xa01021, 0xafa40010, 0xafa30014, +0x8ca60000, 0x8ca70004, 0x3c040001, 0xc002d3b, +0x24847f5c, 0x8f42020c, 0x24420001, 0xaf42020c, +0x8f42020c, 0x1021, 0xaf9000e8, 0xaf9000e4, +0x8fbf001c, 0x8fb00018, 0x3e00008, 0x27bd0020, +0x3e00008, 0x0, 0x8f8300e4, 0x27623ff8, +0x14620002, 0x24620008, 0x27623000, 0x401821, +0xaf8300e8, 0xaf8300e4, 0x3e00008, 0x0, +0x3e00008, 0x0, 0x8f8400e0, 0x8f8800c4, +0x8f8300e8, 0x2402fff8, 0x823824, 0xe32023, +0x2c821000, 0x50400001, 0x24841000, 0x420c2, +0x801821, 0x8f440258, 0x8f45025c, 0x1021, +0xa32821, 0xa3302b, 0x822021, 0x862021, +0xaf440258, 0xaf45025c, 0x8f8300c8, 0x8f420148, +0x1032023, 0x82102b, 0x14400004, 0x801821, +0x8f420148, 0x822021, 0x801821, 0x8f440250, +0x8f450254, 0x1021, 0xa32821, 0xa3302b, +0x822021, 0x862021, 0xaf440250, 0xaf450254, +0xaf8800c8, 0xaf8700e4, 0xaf8700e8, 0x3e00008, +0x0, 0x27bdff28, 0x240a0001, 0xafbf00d0, +0xafbe00cc, 0xafb500c8, 0xafb300c4, 0xafb200c0, +0xafb100bc, 0xafb000b8, 0xa3a0009f, 0xafa0004c, +0xafaa0064, 0xa7a00096, 0xafa00040, 0x934205c4, +0x8821, 0x1040000a, 0xa7a0008e, 0x8f4b00c4, +0xafab006c, 0x8f4a00c0, 0xafaa0074, 0x8f4b00cc, +0xafab007c, 0x8f4a00c8, 0x1000019f, 0xafaa0084, +0x8f420114, 0x40f809, 0x0, 0x403021, +0x10c00418, 0x0, 0x8cc20000, 0x8cc30004, +0xafa20020, 0xafa30024, 0x8fab0024, 0x8faa0020, +0x3162ffff, 0x2442fffc, 0xafa20074, 0x3c020006, +0x2c21024, 0xafab0084, 0x14400015, 0xafaa006c, +0x91420000, 0x30420001, 0x10400011, 0x2402ffff, +0x8d430000, 0x14620004, 0x3402ffff, 0x95430004, +0x1062000b, 0x0, 0xc0025dd, 0x8fa4006c, +0x304200ff, 0x14400006, 0x0, 0x8f420118, +0x40f809, 0x0, 0x100003f6, 0x0, +0x8fa20024, 0x3c03ffbf, 0x3463ffff, 0x431024, +0x3c03ffff, 0x431824, 0x14600003, 0xafa20024, +0x10000040, 0x1821, 0x3c020080, 0x621024, +0x10400007, 0x0, 0x8f42038c, 0x24420001, +0xaf42038c, 0x8f42038c, 0x10000036, 0x24030001, +0x8f420210, 0x24420001, 0xaf420210, 0x8f420210, +0x3c020001, 0x621024, 0x10400006, 0x3c020002, +0x8f4201c4, 0x24420001, 0xaf4201c4, 0x8f4201c4, +0x3c020002, 0x621024, 0x10400006, 0x3c020004, +0x8f42037c, 0x24420001, 0xaf42037c, 0x8f42037c, +0x3c020004, 0x621024, 0x10400006, 0x3c020008, +0x8f420380, 0x24420001, 0xaf420380, 0x8f420380, +0x3c020008, 0x621024, 0x10400006, 0x3c020010, +0x8f420384, 0x24420001, 0xaf420384, 0x8f420384, +0x3c020010, 0x621024, 0x10400006, 0x3c020020, +0x8f4201c0, 0x24420001, 0xaf4201c0, 0x8f4201c0, +0x3c020020, 0x621024, 0x10400006, 0x24030001, +0x8f420388, 0x24420001, 0xaf420388, 0x8f420388, +0x24030001, 0x8c020260, 0x8fab0074, 0x4b102b, +0x10400014, 0x307000ff, 0x8f4201e8, 0x24420001, +0xaf4201e8, 0x8f4201e8, 0x8faa0084, 0x8f8200e0, +0x354a0100, 0xafaa0084, 0xafa20010, 0x8f8200e4, +0x24100001, 0x3c040001, 0x24847f68, 0xafa20014, +0x8fa60020, 0x8fa70024, 0x3c050007, 0xc002d3b, +0x34a50800, 0x12000011, 0x3c020080, 0x2c21024, +0x1440000f, 0x32c20400, 0x8fab0084, 0x3c020080, +0x34420100, 0x1621024, 0x10400005, 0x0, +0x8f42020c, 0x24420001, 0xaf42020c, 0x8f42020c, +0x8fa30074, 0x10000377, 0x0, 0x32c20400, +0x10400015, 0x34028100, 0x8faa006c, 0x9543000c, +0x14620012, 0x3c020100, 0x240b0200, 0xa7ab0096, +0x9542000e, 0x8d430008, 0x8d440004, 0x8d450000, +0x8faa0074, 0x8fab006c, 0x254afffc, 0xafaa0074, +0xa7a2008e, 0xad63000c, 0xad640008, 0xad650004, +0x256b0004, 0xafab006c, 0x3c020100, 0x2c21024, +0x10400004, 0x0, 0x8faa0074, 0x254a0004, +0xafaa0074, 0x16000005, 0x24020800, 0x8fab006c, +0x9563000c, 0x50620001, 0x2411000e, 0x8f4200bc, +0x5040000a, 0xafa0007c, 0x8faa0074, 0x4a102b, +0x50400006, 0xafa0007c, 0x8f4200bc, 0x1421023, +0xafa2007c, 0x8f4b00bc, 0xafab0074, 0x8f420080, +0x8faa0074, 0x4a102b, 0x104000c5, 0x32c28000, +0x104000cd, 0x32c21000, 0x10400058, 0x240b0004, +0x3c021000, 0x2c21024, 0x104000c7, 0xafab0064, +0x122000c5, 0x0, 0x8faa006c, 0x8fab0074, +0x1711023, 0x2c420014, 0x144000c0, 0x1512021, +0x24830006, 0x90820000, 0x3c05001f, 0x34a5ffff, +0x3042000f, 0x23080, 0xa3102b, 0x10400003, +0x0, 0x8f420148, 0x621823, 0x94620000, +0x30421fff, 0x10400003, 0x2261021, 0x100000ae, +0xafa20040, 0x24830009, 0xa3102b, 0x10400003, +0x0, 0x8f420148, 0x621823, 0x90630000, +0x24020006, 0x14620017, 0x24020011, 0x94820002, +0x2c420028, 0x144000a0, 0x861821, 0xa3102b, +0x50400004, 0x2463000c, 0x8f420148, 0x621823, +0x2463000c, 0xa3102b, 0x10400003, 0x0, +0x8f420148, 0x621823, 0x90630000, 0x306200f0, +0x21882, 0x2261021, 0x431021, 0x1000008e, +0xafa20040, 0x1462008c, 0x0, 0x94820002, +0x2c42001c, 0x14400088, 0x2263821, 0x94830002, +0x24e20008, 0x2c63009c, 0x14600083, 0xafa20040, +0x861821, 0xa3102b, 0x10400003, 0x0, +0x8f420148, 0x621823, 0x94620000, 0x24040801, +0x10440004, 0x24e20088, 0x94620002, 0x14440076, +0x24e20088, 0x10000074, 0xafa20040, 0x10000071, +0x240a0003, 0x8f420350, 0x2403ffbf, 0x283a024, +0x24420001, 0xaf420350, 0x8f420350, 0x100002b8, +0x0, 0x2c2b025, 0x2402ffbf, 0x282a024, +0x8f830128, 0x3c040001, 0x24847fa0, 0x26620001, +0xafa20014, 0xafa30010, 0x8f860120, 0x8f870124, +0x3c050007, 0xc002d3b, 0x34a52250, 0x100002a8, +0x0, 0x2c2b025, 0x2402ffbf, 0x282a024, +0x8f830128, 0x3c040001, 0x24847fa0, 0x24020002, +0xafa20014, 0xafa30010, 0x8f860120, 0x8f870124, +0x3c050007, 0xc002d3b, 0x34a52450, 0x10000298, +0x0, 0x8ea20000, 0x8ea30004, 0x3c040001, +0x24847fb8, 0xafb00010, 0xafbe0014, 0x8ea70018, +0x34a52800, 0xc002d3b, 0x603021, 0x3c040001, +0x24847f4c, 0x1000001f, 0x2402058d, 0xa6b1000a, +0x8f820124, 0x3c040001, 0x24847fc0, 0xafbe0014, +0xafa20010, 0x8f460044, 0x8f870120, 0x3c050007, +0xc002d3b, 0x34a53000, 0x3c040001, 0x24847f4c, +0x10000010, 0x240205c9, 0xa6b1000a, 0xa6b2000e, +0x8f820124, 0x3c040001, 0x24847fcc, 0xafbe0014, +0xafa20010, 0x8f460044, 0x8f870120, 0x3c050007, +0xc002d3b, 0x34a53200, 0x3c040001, 0x24847f4c, +0x240205fe, 0xafa20010, 0xafa00014, 0x8f860144, +0x3c070001, 0x24e77f98, 0xc002d3b, 0x3405dead, +0x8f82011c, 0x34420002, 0xaf82011c, 0x8f820220, +0x34420004, 0xaf820220, 0x8f820140, 0x3c030001, +0x431025, 0xaf820140, 0x10000259, 0x0, +0x8f420084, 0x8fab0074, 0x4b102b, 0x14400007, +0x3c020001, 0x2c21024, 0x10400004, 0x0, +0x240a0002, 0xafaa0064, 0x8fab0074, 0x1160026d, +0x27aa0020, 0xafaa00ac, 0x3c0b001f, 0x356bffff, +0xafab00a4, 0x8faa0064, 0x240b0001, 0x154b0022, +0x24020002, 0x8f430054, 0x8f420050, 0x1062000b, +0x274a0054, 0x8f5e0054, 0x3403ecc0, 0xafaa0054, +0x27c20001, 0x304201ff, 0xafa2005c, 0x1e1140, +0x431021, 0x10000077, 0x2e2a821, 0x8f420044, +0x8fab0074, 0x3c040001, 0x24847f74, 0xafab0014, +0xafa20010, 0x8f460054, 0x8f470050, 0x3c050007, +0xc002d3b, 0x34a51300, 0x8f430350, 0x2402ffbf, +0x282a024, 0x24630001, 0xaf430350, 0x8f420350, +0x10000223, 0x0, 0x1542001d, 0x0, +0x8f430074, 0x8f420070, 0x1062000a, 0x274b0074, +0x8f5e0074, 0xafab0054, 0x27c20001, 0x304203ff, +0xafa2005c, 0x1e1140, 0x24426cc0, 0x10000055, +0x2e2a821, 0x8f420044, 0x8faa0074, 0x3c040001, +0x24847f80, 0x3c050007, 0xafaa0014, 0xafa20010, +0x8f460074, 0x8f470070, 0x34a51500, 0x240b0001, +0xc002d3b, 0xafab0064, 0x1000ffc2, 0x0, +0x8f430064, 0x8f420060, 0x1062002b, 0x274a0064, +0x8f5e0064, 0x8fab0064, 0xafaa0054, 0x27c20001, +0x304200ff, 0xafa2005c, 0x24020004, 0x1562001f, +0x1e1140, 0x1e1180, 0x24420cc0, 0x2e21021, +0xafa2004c, 0x24550020, 0x3c021000, 0x2c21024, +0x1040000e, 0x0, 0x8faa004c, 0x8fab0074, +0x9542002a, 0x4b102b, 0x54400006, 0x240a0001, +0x8fa20040, 0x10400027, 0x0, 0x104b0025, +0x240a0001, 0x10000023, 0xa3aa009f, 0x8fab004c, +0x8faa0074, 0x9562002a, 0x4a102b, 0x1040001d, +0x240b0001, 0x1000001b, 0xa3ab009f, 0x24424cc0, +0x10000018, 0x2e2a821, 0x8f420044, 0x8faa0074, +0x3c040001, 0x24847f8c, 0xafaa0014, 0xafa20010, +0x8f460064, 0x8f470060, 0x3c050007, 0xc002d3b, +0x34a51800, 0x3c020008, 0x2c21024, 0x1440ff09, +0x0, 0x8f420370, 0x240b0001, 0xafab0064, +0x24420001, 0xaf420370, 0x8f420370, 0x1000ff7d, +0x0, 0x8faa006c, 0xaeaa0018, 0x93a2009f, +0x104000ae, 0x24050002, 0x8fab004c, 0x8fa40040, +0x25620020, 0xafa20028, 0x25620008, 0xafa20030, +0x25620010, 0xafab002c, 0x1080000e, 0xafa20034, +0x9563002a, 0x83102b, 0x54400001, 0x801821, +0x1000000b, 0xa7a30038, 0x27a30036, 0x131040, +0x621821, 0x94620000, 0x441021, 0x10000016, +0xa4620000, 0x8faa004c, 0x9542002a, 0xa7a20038, +0x8fab004c, 0x8fa40074, 0x8fa300ac, 0x95620018, +0xa7a2003a, 0x9562001a, 0xa7a2003c, 0x9562001c, +0x9821, 0xa7a2003e, 0x94620018, 0x24630002, +0x822023, 0x1880ffe8, 0x26730001, 0x2e620004, +0x1440fff9, 0x0, 0x18800014, 0x2402052b, +0x3c040001, 0x24847f4c, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e77f98, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x8f4200fc, +0x26650001, 0xa2102a, 0x1440003f, 0x24030001, +0x8f83012c, 0x10600037, 0x0, 0x8f820124, +0x431023, 0x22143, 0x58800001, 0x24840040, +0x8f820128, 0x431023, 0x21943, 0x58600001, +0x24630040, 0x64102a, 0x54400001, 0x602021, +0xaf4400fc, 0x8f4200fc, 0xa2102a, 0x10400025, +0x24030001, 0x10000029, 0x306200ff, 0x8faa006c, +0x96070018, 0xafaa0010, 0x8e220008, 0x3c040001, +0x24847fac, 0x8c430004, 0x8c420000, 0x34a52400, +0x2403021, 0xc002d3b, 0xafa30014, 0x3c040001, +0x24847f4c, 0x2402054f, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e77f98, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x1000002b, +0x0, 0x8f420334, 0x1821, 0x24420001, +0xaf420334, 0x8f420334, 0x306200ff, 0x5040fe7d, +0x3c020800, 0x12600021, 0x9021, 0x8fb100ac, +0x2208021, 0x8e220008, 0x96070018, 0x8fa6006c, +0x8c440000, 0x8c450004, 0x240b0001, 0xafab0010, +0xafbe0014, 0x8f420008, 0xafa20018, 0x8f42010c, +0x40f809, 0x0, 0x1040ffc4, 0x3c050007, +0x96020018, 0x8faa006c, 0x8fab00a4, 0x1425021, +0x16a102b, 0x10400004, 0xafaa006c, 0x8f420148, +0x1425023, 0xafaa006c, 0x26100002, 0x26520001, +0x253102b, 0x1440ffe3, 0x26310004, 0x8fb00074, +0x97b10038, 0x10000035, 0x0, 0x8f4200fc, +0xa2102a, 0x1440001b, 0x24030001, 0x8f83012c, +0x10600013, 0x0, 0x8f820124, 0x431023, +0x22143, 0x58800001, 0x24840040, 0x8f820128, +0x431023, 0x21943, 0x58600001, 0x24630040, +0x64102a, 0x54400001, 0x602021, 0xaf4400fc, +0x8f4200fc, 0xa2102a, 0x14400006, 0x24030001, +0x8f420334, 0x1821, 0x24420001, 0xaf420334, +0x8f420334, 0x306200ff, 0x1040fe46, 0x3c020800, +0x96b1000a, 0x8fb00074, 0x3223ffff, 0x70102b, +0x54400001, 0x608021, 0x8ea40000, 0x8ea50004, +0x240a0001, 0xafaa0010, 0xafbe0014, 0x8f420008, +0x8fa6006c, 0xafa20018, 0x8f42010c, 0x40f809, +0x2003821, 0x1040fe43, 0x3c050007, 0x96a4000e, +0x97ab0096, 0x11600007, 0x809021, 0x934205c4, +0x14400004, 0x0, 0x97aa008e, 0x8b2025, +0xa6aa0016, 0x8fab0084, 0x3c02ffff, 0x1621024, +0x10400003, 0xb1402, 0x34840400, 0xa6a20014, +0x3c021000, 0x2c21024, 0x10400006, 0x0, +0x8fa20040, 0x401821, 0x1021, 0xaea20000, +0xaea30004, 0x8faa0074, 0x560a0073, 0xa6a4000e, +0x34820004, 0xa6a2000e, 0x8fab007c, 0x14b1021, +0xa6a2000a, 0x8f430044, 0x8f4401a0, 0x8f4501a4, +0x34028000, 0xafa20010, 0x8f420044, 0x2a03021, +0x24070020, 0xafa20014, 0x8f42000c, 0x31940, +0x604821, 0xafa20018, 0x8f42010c, 0x4021, +0xa92821, 0xa9182b, 0x882021, 0x40f809, +0x832021, 0x5040fe19, 0xa6b2000e, 0x8f420368, +0xafa00074, 0xa34005c4, 0x2442ffff, 0xaf420368, +0x8faa0064, 0x240b0001, 0x8f420368, 0x154b0006, +0x24020002, 0x8f42035c, 0x2442ffff, 0xaf42035c, +0x1000000c, 0x8f42035c, 0x15420006, 0x0, +0x8f420364, 0x2442ffff, 0xaf420364, 0x10000005, +0x8f420364, 0x8f420360, 0x2442ffff, 0xaf420360, +0x8f420360, 0x8faa005c, 0x8fab0054, 0xad6a0000, +0x8f420044, 0x8f440088, 0x8f430078, 0x24420001, +0x441024, 0x24630001, 0xaf420044, 0xaf430078, +0x8c020240, 0x62182b, 0x14600076, 0x24070008, +0x8f440168, 0x8f45016c, 0x8f430044, 0x8f48000c, +0x8f860120, 0x24020040, 0xafa20010, 0xafa30014, +0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400011, 0x240b0001, 0x3c010001, 0x370821, +0xa02b40f2, 0x8f820124, 0xafa20010, 0x8f820128, +0x3c040001, 0x24847f44, 0xafa20014, 0x8f460044, +0x8f870120, 0x3c050009, 0xc002d3b, 0x34a51300, +0x1000000b, 0x0, 0x8f420304, 0x24420001, +0xaf420304, 0x8f420304, 0x8f420044, 0xaf42007c, +0x3c010001, 0x370821, 0xa02040f2, 0xaf400078, +0x8f420318, 0x24420001, 0xaf420318, 0x8f420318, +0x10000048, 0x0, 0xa6b0000a, 0x8f430044, +0x8f4401a0, 0x8f4501a4, 0x34028000, 0xafa20010, +0x8f420044, 0x2a03021, 0x24070020, 0xafa20014, +0x8f42000c, 0x31940, 0x604821, 0xafa20018, +0x8f42010c, 0x4021, 0xa92821, 0xa9182b, +0x882021, 0x40f809, 0x832021, 0x1040fdba, +0x240a0001, 0xa34a05c4, 0x8fab0074, 0x8faa006c, +0x1705823, 0xafab0074, 0x8fab00a4, 0x1505021, +0x16a102b, 0x10400004, 0xafaa006c, 0x8f420148, +0x1425023, 0xafaa006c, 0x8f420368, 0x2442ffff, +0xaf420368, 0x8faa0064, 0x240b0001, 0x8f420368, +0x154b0006, 0x24020002, 0x8f42035c, 0x2442ffff, +0xaf42035c, 0x1000000c, 0x8f42035c, 0x11420006, +0x0, 0x8f420360, 0x2442ffff, 0xaf420360, +0x10000005, 0x8f420360, 0x8f420364, 0x2442ffff, +0xaf420364, 0x8f420364, 0x8faa005c, 0x8fab0054, +0xad6a0000, 0x8f420044, 0x8f440088, 0x8f430078, +0x24420001, 0x441024, 0x24630001, 0xaf420044, +0xaf430078, 0x8fab0074, 0x1560fdba, 0x0, +0x8faa0074, 0x1140001f, 0x0, 0x934205c4, +0x10400009, 0x0, 0x8fab006c, 0xaf4b00c4, +0xaf4a00c0, 0x8faa0084, 0xaf4a00c8, 0x8fab007c, +0x1000000e, 0xaf4b00cc, 0x97aa0096, 0x1140000b, +0x34038100, 0x8fa20020, 0x8c46000c, 0xa443000c, +0x97ab008e, 0x8c440004, 0x8c450008, 0xa44b000e, +0xac440000, 0xac450004, 0xac460008, 0x8f42034c, +0x24420001, 0xaf42034c, 0x8f42034c, 0x10000011, +0x0, 0x8faa0084, 0x3144ffff, 0x2484fffc, +0x801821, 0x8f440250, 0x8f450254, 0x8f460118, +0x1021, 0xa32821, 0xa3382b, 0x822021, +0x872021, 0xaf440250, 0xaf450254, 0xc0f809, +0x0, 0x8fbf00d0, 0x8fbe00cc, 0x8fb500c8, +0x8fb300c4, 0x8fb200c0, 0x8fb100bc, 0x8fb000b8, +0x3e00008, 0x27bd00d8, 0x3e00008, 0x0, +0x27bdff30, 0x240b0001, 0xafbf00c8, 0xafbe00c4, +0xafb500c0, 0xafb300bc, 0xafb200b8, 0xafb100b4, +0xafb000b0, 0xa3a0008f, 0xafa0004c, 0xafab0064, +0xa7a0007e, 0xafa00040, 0x934205c4, 0x8821, +0x10400007, 0xa7a00086, 0x8f4c00c0, 0xafac006c, +0x8f4b00c8, 0x8f5e00c4, 0x10000184, 0xafab0074, +0x8f420114, 0x40f809, 0x0, 0x403021, +0x10c00348, 0x0, 0x8cc20000, 0x8cc30004, +0xafa20020, 0xafa30024, 0x8fac0024, 0x8fbe0020, +0x3182ffff, 0x2442fffc, 0xafa2006c, 0x3c020006, +0x2c21024, 0x14400015, 0xafac0074, 0x93c20000, +0x30420001, 0x10400011, 0x2402ffff, 0x8fc30000, +0x14620004, 0x3402ffff, 0x97c30004, 0x1062000b, +0x0, 0xc0025dd, 0x3c02021, 0x304200ff, +0x14400006, 0x0, 0x8f420118, 0x40f809, +0x0, 0x10000327, 0x0, 0x8fa20024, +0x3c03ffbf, 0x3463ffff, 0x431024, 0x3c03ffff, +0x431824, 0x14600003, 0xafa20024, 0x10000040, +0x8021, 0x3c020080, 0x621024, 0x10400007, +0x0, 0x8f42038c, 0x24420001, 0xaf42038c, +0x8f42038c, 0x10000036, 0x24100001, 0x8f420210, +0x24420001, 0xaf420210, 0x8f420210, 0x3c020001, +0x621024, 0x10400006, 0x3c020002, 0x8f4201c4, +0x24420001, 0xaf4201c4, 0x8f4201c4, 0x3c020002, +0x621024, 0x10400006, 0x3c020004, 0x8f42037c, +0x24420001, 0xaf42037c, 0x8f42037c, 0x3c020004, +0x621024, 0x10400006, 0x3c020008, 0x8f420380, +0x24420001, 0xaf420380, 0x8f420380, 0x3c020008, +0x621024, 0x10400006, 0x3c020010, 0x8f420384, +0x24420001, 0xaf420384, 0x8f420384, 0x3c020010, +0x621024, 0x10400006, 0x3c020020, 0x8f4201c0, +0x24420001, 0xaf4201c0, 0x8f4201c0, 0x3c020020, +0x621024, 0x10400006, 0x24100001, 0x8f420388, +0x24420001, 0xaf420388, 0x8f420388, 0x24100001, +0x8c020260, 0x8fab006c, 0x4b102b, 0x10400015, +0x320200ff, 0x8f4201e8, 0x24420001, 0xaf4201e8, +0x8f4201e8, 0x8fac0074, 0x8f8200e0, 0x358c0100, +0xafac0074, 0xafa20010, 0x8f8200e4, 0x24100001, +0x3c040001, 0x24847f68, 0xafa20014, 0x8fa60020, +0x8fa70024, 0x3c050007, 0xc002d3b, 0x34a53600, +0x320200ff, 0x10400011, 0x3c020080, 0x2c21024, +0x1440000f, 0x32c20400, 0x8fab0074, 0x3c020080, +0x34420100, 0x1621024, 0x10400005, 0x0, +0x8f42020c, 0x24420001, 0xaf42020c, 0x8f42020c, +0x8fa3006c, 0x100002a7, 0x0, 0x32c20400, +0x10400012, 0x34028100, 0x97c3000c, 0x1462000f, +0x0, 0x240c0200, 0xa7ac007e, 0x97c2000e, +0x8fc30008, 0x8fc40004, 0x8fab006c, 0x8fc50000, +0x256bfffc, 0xafab006c, 0xa7a20086, 0xafc3000c, +0xafc40008, 0xafc50004, 0x27de0004, 0x8fa7006c, +0x320200ff, 0x14400033, 0x3c020100, 0x97c4000c, +0x2c8305dd, 0x38828870, 0x2c420001, 0x621825, +0x10600014, 0x32c20800, 0x10400015, 0x24020800, +0x97c30014, 0x14620012, 0x3402aaaa, 0x97c3000e, +0x14620007, 0x2021, 0x97c30010, 0x24020300, +0x14620004, 0x801021, 0x97c20012, 0x2c440001, +0x801021, 0x54400006, 0x24110016, 0x10000004, +0x0, 0x24020800, 0x50820001, 0x2411000e, +0x12200013, 0x3d12021, 0x24830009, 0x3c02001f, +0x3442ffff, 0x43102b, 0x10400003, 0x0, +0x8f420148, 0x621823, 0x90620000, 0x38430006, +0x2c630001, 0x38420011, 0x2c420001, 0x621825, +0x10600004, 0x3c020100, 0x94820002, 0x513821, +0x3c020100, 0x2c21024, 0x5040000e, 0xafa7006c, +0x8fac006c, 0x10ec0008, 0x3c050007, 0x3c040001, +0x24847fd8, 0x8fa6006c, 0x34a54000, 0xafa00010, +0xc002d3b, 0xafa00014, 0x8fab006c, 0x256b0004, +0xafab006c, 0x8f420080, 0x8fac006c, 0x4c102b, +0x10400080, 0x32c28000, 0x10400088, 0x32c21000, +0x10400055, 0x240b0004, 0x3c021000, 0x2c21024, +0x10400082, 0xafab0064, 0x12200080, 0x1911023, +0x2c420014, 0x1440007d, 0x3d12021, 0x24830006, +0x90820000, 0x3c05001f, 0x34a5ffff, 0x3042000f, +0x23080, 0xa3102b, 0x10400003, 0x0, +0x8f420148, 0x621823, 0x94620000, 0x30421fff, +0x10400003, 0x2261021, 0x1000006c, 0xafa20040, +0x24830009, 0xa3102b, 0x10400003, 0x0, +0x8f420148, 0x621823, 0x90630000, 0x24020006, +0x14620017, 0x24020011, 0x94820002, 0x2c420028, +0x1440005e, 0x861821, 0xa3102b, 0x50400004, +0x2463000c, 0x8f420148, 0x621823, 0x2463000c, +0xa3102b, 0x10400003, 0x0, 0x8f420148, +0x621823, 0x90630000, 0x306200f0, 0x21882, +0x2261021, 0x431021, 0x1000004c, 0xafa20040, +0x1462004a, 0x0, 0x94820002, 0x2c42001c, +0x14400046, 0x2263821, 0x94830002, 0x24e20008, +0x2c63009c, 0x14600041, 0xafa20040, 0x861821, +0xa3102b, 0x10400003, 0x0, 0x8f420148, +0x621823, 0x94620000, 0x24040801, 0x10440004, +0x24e20088, 0x94620002, 0x14440034, 0x24e20088, +0x10000032, 0xafa20040, 0x1000002f, 0x240c0003, +0x8f420350, 0x2403ffbf, 0x283a024, 0x24420001, +0xaf420350, 0x8f420350, 0x100001c4, 0x0, +0x3c020800, 0x2c2b025, 0x2402ffbf, 0x282a024, +0x8f830128, 0x3c040001, 0x24847fa0, 0x26620001, +0xafa20014, 0xafa30010, 0x8f860120, 0x8f870124, +0x3c050007, 0xc002d3b, 0x34a55300, 0x100001b3, +0x0, 0x8ea20000, 0x8ea30004, 0x3c040001, +0x24847fb8, 0xafb00010, 0xafb10014, 0x8ea70018, +0x34a55900, 0xc002d3b, 0x603021, 0x100001a7, +0x0, 0x8f420084, 0x8fab006c, 0x4b102b, +0x14400007, 0x3c020001, 0x2c21024, 0x10400004, +0x0, 0x240c0002, 0xafac0064, 0x8fab006c, +0x116001b8, 0x27ac0020, 0xafac0094, 0x8fab0064, +0x240c0001, 0x556c0022, 0x240c0002, 0x8f430054, +0x8f420050, 0x1062000b, 0x274b0054, 0x8f510054, +0x3403ecc0, 0xafab0054, 0x26220001, 0x304201ff, +0xafa2005c, 0x111140, 0x431021, 0x10000077, +0x2e2a821, 0x8f420044, 0x8fac006c, 0x3c040001, +0x24847f74, 0xafac0014, 0xafa20010, 0x8f460054, +0x8f470050, 0x3c050007, 0xc002d3b, 0x34a54300, +0x8f430350, 0x2402ffbf, 0x282a024, 0x24630001, +0xaf430350, 0x8f420350, 0x10000174, 0x0, +0x156c001d, 0x0, 0x8f430074, 0x8f420070, +0x1062000a, 0x274b0074, 0x8f510074, 0xafab0054, +0x26220001, 0x304203ff, 0xafa2005c, 0x111140, +0x24426cc0, 0x10000055, 0x2e2a821, 0x8f420044, +0x8fac006c, 0x3c040001, 0x24847f80, 0x3c050007, +0xafac0014, 0xafa20010, 0x8f460074, 0x8f470070, +0x34a54500, 0x240b0001, 0xc002d3b, 0xafab0064, +0x1000ffc2, 0x0, 0x8f430064, 0x8f420060, +0x1062002b, 0x274c0064, 0x8f510064, 0x8fab0064, +0xafac0054, 0x26220001, 0x304200ff, 0xafa2005c, +0x24020004, 0x1562001f, 0x111140, 0x111180, +0x24420cc0, 0x2e21021, 0xafa2004c, 0x24550020, +0x3c021000, 0x2c21024, 0x1040000e, 0x0, +0x8fac004c, 0x8fab006c, 0x9582002a, 0x4b102b, +0x54400006, 0x240c0001, 0x8fa20040, 0x50400028, +0xaebe0018, 0x104b0025, 0x240c0001, 0x10000023, +0xa3ac008f, 0x8fab004c, 0x8fac006c, 0x9562002a, +0x4c102b, 0x1040001d, 0x240b0001, 0x1000001b, +0xa3ab008f, 0x24424cc0, 0x10000018, 0x2e2a821, +0x8f420044, 0x8fac006c, 0x3c040001, 0x24847f8c, +0xafac0014, 0xafa20010, 0x8f460064, 0x8f470060, +0x3c050007, 0xc002d3b, 0x34a54800, 0x3c020008, +0x2c21024, 0x1440ff4e, 0x0, 0x8f420370, +0x240b0001, 0xafab0064, 0x24420001, 0xaf420370, +0x8f420370, 0x1000ff7d, 0x0, 0xaebe0018, +0x93a2008f, 0x104000bf, 0x0, 0x8fac004c, +0x8fa40040, 0x25820020, 0xafa20028, 0x25820008, +0xafa20030, 0x25820010, 0xafac002c, 0x1080000e, +0xafa20034, 0x9583002a, 0x83102b, 0x54400001, +0x801821, 0x1000000b, 0xa7a30038, 0x27a30036, +0x131040, 0x621821, 0x94620000, 0x441021, +0x10000016, 0xa4620000, 0x8fab004c, 0x9562002a, +0xa7a20038, 0x8fac004c, 0x8fa4006c, 0x8fa30094, +0x95820018, 0xa7a2003a, 0x9582001a, 0xa7a2003c, +0x9582001c, 0x9821, 0xa7a2003e, 0x94620018, +0x24630002, 0x822023, 0x1880ffe8, 0x26730001, +0x2e620004, 0x1440fff9, 0x0, 0x18800014, +0x240207c2, 0x3c040001, 0x24847f4c, 0xafa20010, +0xafa00014, 0x8f860144, 0x3c070001, 0x24e77f98, +0xc002d3b, 0x3405dead, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, +0x8f820140, 0x3c030001, 0x431025, 0xaf820140, +0x8f4200fc, 0x262102a, 0x14400044, 0x24030001, +0x8f83012c, 0x1060003c, 0x0, 0x8f820124, +0x431023, 0x22143, 0x58800001, 0x24840040, +0x8f820128, 0x431023, 0x21943, 0x58600001, +0x24630040, 0x64102a, 0x54400001, 0x602021, +0xaf4400fc, 0x8f4200fc, 0x262102a, 0x1040002a, +0x24030001, 0x1000002e, 0x306200ff, 0x8fab0094, +0x101040, 0x4b1021, 0x94470018, 0x101080, +0x4b1021, 0xafbe0010, 0x8c420008, 0x3c040001, +0x24847fac, 0x3c050007, 0x8c430004, 0x8c420000, +0x34a55500, 0x2003021, 0xc002d3b, 0xafa30014, +0x3c040001, 0x24847f4c, 0x240207f4, 0xafa20010, +0xafa00014, 0x8f860144, 0x3c070001, 0x24e77f98, +0xc002d3b, 0x3405dead, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, +0x8f820140, 0x3c030001, 0x431025, 0xaf820140, +0x10000039, 0x0, 0x8f420334, 0x1821, +0x24420001, 0xaf420334, 0x8f420334, 0x306200ff, +0x1040febf, 0x8021, 0x8f430008, 0x2402fbff, +0x1260002d, 0x625024, 0x3c0c4000, 0x22c4025, +0x8fb10094, 0x2669ffff, 0x2209021, 0x8e420008, +0x96270018, 0x8c440000, 0x8c450004, 0x56090004, +0x240c0001, 0x240b0002, 0x10000002, 0xafab0010, +0xafac0010, 0x16000004, 0xafa80014, 0x8f420008, +0x10000002, 0xafa20018, 0xafaa0018, 0x8f42010c, +0x3c03021, 0xafa800a0, 0xafa900a4, 0x40f809, +0xafaa00a8, 0x8fa800a0, 0x8fa900a4, 0x8faa00a8, +0x1040ffae, 0x3c02001f, 0x96230018, 0x3442ffff, +0x3c3f021, 0x5e102b, 0x10400003, 0x26310002, +0x8f420148, 0x3c2f023, 0x26100001, 0x213102b, +0x1440ffda, 0x26520004, 0x8fb0006c, 0x1000001a, +0x0, 0x96a3000a, 0x8fb0006c, 0x70102b, +0x54400001, 0x608021, 0x8ea40000, 0x8ea50004, +0x8fac0064, 0x240b0002, 0xafab0010, 0x934305c4, +0xc1700, 0x10600003, 0x2223025, 0x3c020800, +0xc23025, 0xafa60014, 0x8f420008, 0xafa20018, +0x8f42010c, 0x3c03021, 0x40f809, 0x2003821, +0x1040fe84, 0x3c050007, 0x97ab007e, 0x96a3000e, +0x11600007, 0x0, 0x934205c4, 0x14400004, +0x0, 0x97ac0086, 0x6b1825, 0xa6ac0016, +0x8fab0074, 0x3c02ffff, 0x1621024, 0x10400003, +0xb1402, 0x34630400, 0xa6a20014, 0xa6b0000a, +0x8fac006c, 0x560c0006, 0x3d0f021, 0x34620004, +0xafa0006c, 0xa6a2000e, 0x1000000d, 0xa34005c4, +0x8fab006c, 0x3c02001f, 0x3442ffff, 0x5e102b, +0x1705823, 0xafab006c, 0xa6a3000e, 0x240c0001, +0x10400003, 0xa34c05c4, 0x8f420148, 0x3c2f023, +0x3c021000, 0x2c21024, 0x10400006, 0x0, +0x8fa20040, 0x401821, 0x1021, 0xaea20000, +0xaea30004, 0x8fac005c, 0x8fab0054, 0xad6c0000, +0x8fab006c, 0x1560fe69, 0x0, 0x8fac006c, +0x1180001c, 0x0, 0x934205c4, 0x10400006, +0x0, 0xaf5e00c4, 0xaf4c00c0, 0x8fab0074, +0x1000000e, 0xaf4b00c8, 0x97ac007e, 0x1180000b, +0x34038100, 0x8fa20020, 0x8c46000c, 0xa443000c, +0x97ab0086, 0x8c440004, 0x8c450008, 0xa44b000e, +0xac440000, 0xac450004, 0xac460008, 0x8f42034c, +0x24420001, 0xaf42034c, 0x8f42034c, 0x10000011, +0x0, 0x8fac0074, 0x3184ffff, 0x2484fffc, +0x801821, 0x8f440250, 0x8f450254, 0x8f460118, +0x1021, 0xa32821, 0xa3382b, 0x822021, +0x872021, 0xaf440250, 0xaf450254, 0xc0f809, +0x0, 0x8fbf00c8, 0x8fbe00c4, 0x8fb500c0, +0x8fb300bc, 0x8fb200b8, 0x8fb100b4, 0x8fb000b0, +0x3e00008, 0x27bd00d0, 0x3e00008, 0x0, +0x27bdffd0, 0xafbf0028, 0xafb10024, 0xafb00020, +0x8f43004c, 0x8f420048, 0x1062004a, 0x0, +0x8f430048, 0x8f42004c, 0x628823, 0x6220001, +0x26310200, 0x8f430054, 0x8f42004c, 0x43102b, +0x14400004, 0x24020200, 0x8f43004c, 0x10000005, +0x438023, 0x8f420054, 0x8f43004c, 0x431023, +0x2450ffff, 0x16000016, 0x2005821, 0x3c040001, +0x24847f4c, 0x24020888, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e77f98, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x2005821, +0x22b102a, 0x54400001, 0x2205821, 0x8f4a004c, +0x8f49004c, 0x8f440188, 0x8f45018c, 0x8f46004c, +0xb3940, 0x24081000, 0xafa80010, 0x94940, +0x1201821, 0x1021, 0x14b5021, 0x315001ff, +0xafb00014, 0x8f480014, 0xa32821, 0xa3482b, +0x822021, 0x892021, 0x63140, 0x3402ecc0, +0xafa80018, 0x8f430108, 0xc23021, 0x60f809, +0x2e63021, 0x54400001, 0xaf50004c, 0x8f43004c, +0x8f420048, 0x14620019, 0x0, 0x8f420000, +0x10400007, 0x0, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x2403fdff, 0x431024, 0xaf820060, +0x8f420000, 0x10400004, 0x0, 0xaf80004c, +0x10000002, 0x0, 0xaf800048, 0x8fbf0028, +0x8fb10024, 0x8fb00020, 0x3e00008, 0x27bd0030, +0x3e00008, 0x0, 0x27bdffd0, 0xafbf0028, +0xafb10024, 0xafb00020, 0x8f43005c, 0x8f420058, +0x1062005f, 0x0, 0x8f430058, 0x8f42005c, +0x628823, 0x6220001, 0x26310100, 0x8f430064, +0x8f42005c, 0x43102b, 0x14400004, 0x24020100, +0x8f43005c, 0x10000005, 0x438023, 0x8f420064, +0x8f43005c, 0x431023, 0x2450ffff, 0x16000016, +0x2003821, 0x3c040001, 0x24847f4c, 0x240208e2, +0xafa20010, 0xafa00014, 0x8f860144, 0x3c070001, +0x24e77f98, 0xc002d3b, 0x3405dead, 0x8f82011c, +0x34420002, 0xaf82011c, 0x8f820220, 0x34420004, +0xaf820220, 0x8f820140, 0x3c030001, 0x431025, +0xaf820140, 0x2003821, 0x227102a, 0x54400001, +0x2203821, 0x8f42005c, 0x471021, 0x305000ff, +0x32c21000, 0x10400015, 0x24082000, 0x8f49005c, +0x8f440190, 0x8f450194, 0x8f46005c, 0x73980, +0xafa80010, 0xafb00014, 0x8f480014, 0x94980, +0x1201821, 0x1021, 0xa32821, 0xa3482b, +0x822021, 0x892021, 0x63180, 0xafa80018, +0x8f420108, 0x10000014, 0x24c60cc0, 0x8f49005c, +0x8f440190, 0x8f450194, 0x8f46005c, 0x73940, +0xafa80010, 0xafb00014, 0x8f480014, 0x94940, +0x1201821, 0x1021, 0xa32821, 0xa3482b, +0x822021, 0x892021, 0x63140, 0xafa80018, +0x8f420108, 0x24c64cc0, 0x40f809, 0x2e63021, +0x54400001, 0xaf50005c, 0x8f43005c, 0x8f420058, +0x14620019, 0x0, 0x8f420000, 0x10400007, +0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, +0x0, 0x10000005, 0x0, 0xaf800048, +0x8f820048, 0x1040fffd, 0x0, 0x8f820060, +0x2403feff, 0x431024, 0xaf820060, 0x8f420000, +0x10400004, 0x0, 0xaf80004c, 0x10000002, +0x0, 0xaf800048, 0x8fbf0028, 0x8fb10024, +0x8fb00020, 0x3e00008, 0x27bd0030, 0x3e00008, +0x0, 0x27bdffd0, 0xafbf0028, 0xafb10024, +0xafb00020, 0x8f43006c, 0x8f420068, 0x10620049, +0x0, 0x8f430068, 0x8f42006c, 0x628823, +0x6220001, 0x26310400, 0x8f430074, 0x8f42006c, +0x43102b, 0x14400004, 0x24020400, 0x8f43006c, +0x10000005, 0x438023, 0x8f420074, 0x8f43006c, +0x431023, 0x2450ffff, 0x16000016, 0x2005821, +0x3c040001, 0x24847f4c, 0x2402094b, 0xafa20010, +0xafa00014, 0x8f860144, 0x3c070001, 0x24e77f98, +0xc002d3b, 0x3405dead, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, +0x8f820140, 0x3c030001, 0x431025, 0xaf820140, +0x2005821, 0x22b102a, 0x54400001, 0x2205821, +0x8f4a006c, 0x8f49006c, 0x8f440198, 0x8f45019c, +0x8f46006c, 0xb3940, 0x24084000, 0xafa80010, +0x94940, 0x1201821, 0x1021, 0x14b5021, +0x315003ff, 0xafb00014, 0x8f480014, 0xa32821, +0xa3482b, 0x822021, 0x892021, 0x63140, +0xafa80018, 0x8f420108, 0x24c66cc0, 0x40f809, +0x2e63021, 0x54400001, 0xaf50006c, 0x8f43006c, +0x8f420068, 0x14620019, 0x0, 0x8f420000, +0x10400007, 0x0, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x2403f7ff, 0x431024, 0xaf820060, +0x8f420000, 0x10400004, 0x0, 0xaf80004c, +0x10000002, 0x0, 0xaf800048, 0x8fbf0028, +0x8fb10024, 0x8fb00020, 0x3e00008, 0x27bd0030, +0x3e00008, 0x0, 0x27bdffe0, 0xafbf001c, +0xafb00018, 0x8f4200fc, 0x8f4400f8, 0x8f4300f4, +0x24420001, 0xaf4200fc, 0x8f900128, 0x14830016, +0x3c020001, 0x3c040001, 0x24847f4c, 0x240209b3, +0xafa20010, 0xafa00014, 0x8f860144, 0x3c070001, +0x24e77f98, 0xc002d3b, 0x3405dead, 0x8f82011c, +0x34420002, 0xaf82011c, 0x8f820220, 0x34420004, +0xaf820220, 0x8f820140, 0x3c030001, 0x431025, +0xaf820140, 0x3c020001, 0x8f4300f8, 0x344230c8, +0x2e21021, 0x54620004, 0x24620008, 0x3c020001, +0x34422ec8, 0x2e21021, 0x401821, 0xaf4300f8, +0xac600000, 0x8f4200f4, 0x14620005, 0x3c020001, +0x26020020, 0xaf820128, 0x1000000f, 0x0, +0x8f4300f8, 0x344230c8, 0x2e21021, 0x54620004, +0x24620008, 0x3c020001, 0x34422ec8, 0x2e21021, +0x401821, 0x8c620004, 0x21140, 0x2021021, +0xaf820128, 0xac600000, 0x8e030018, 0x30620070, +0x10400030, 0x30620020, 0x10400004, 0x3c020010, +0x2c21024, 0x1040000d, 0x0, 0x30620040, +0x10400004, 0x3c020020, 0x2c21024, 0x10400007, +0x0, 0x30620010, 0x10400038, 0x3c020040, +0x2c21024, 0x14400035, 0x0, 0x8f820040, +0x30420001, 0x14400008, 0x2021, 0x8c030104, +0x24020001, 0x50620005, 0x24040001, 0x8c020264, +0x10400003, 0x801021, 0x24040001, 0x801021, +0x10400007, 0x0, 0x8f42030c, 0x24420001, +0xaf42030c, 0x8f42030c, 0x10000020, 0x0, +0x8f820044, 0x34420004, 0xaf820044, 0x8f420308, +0x24420001, 0xaf420308, 0x8f420308, 0x10000017, +0x0, 0x3062b08f, 0x14400014, 0x240209e4, +0x3c040001, 0x24847f4c, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e77f98, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x8fbf001c, +0x8fb00018, 0x3e00008, 0x27bd0020, 0x3e00008, +0x0, 0x27bdff98, 0xafbf0060, 0xafbe005c, +0xafb50058, 0xafb30054, 0xafb20050, 0xafb1004c, +0xafb00048, 0x8f4200fc, 0x24420001, 0xaf4200fc, +0x8f880128, 0x25020020, 0xaf820128, 0x8d030018, +0x30620070, 0x10400030, 0x30620020, 0x10400004, +0x3c020010, 0x2c21024, 0x1040000d, 0x0, +0x30620040, 0x10400004, 0x3c020020, 0x2c21024, +0x10400007, 0x0, 0x30620010, 0x104001c0, +0x3c020040, 0x2c21024, 0x144001bd, 0x0, +0x8f820040, 0x30420001, 0x14400008, 0x2021, +0x8c030104, 0x24020001, 0x50620005, 0x24040001, +0x8c020264, 0x10400003, 0x801021, 0x24040001, +0x801021, 0x10400007, 0x0, 0x8f42030c, +0x24420001, 0xaf42030c, 0x8f42030c, 0x100001a8, +0x0, 0x8f820044, 0x34420004, 0xaf820044, +0x8f420308, 0x24420001, 0xaf420308, 0x8f420308, +0x1000019f, 0x0, 0x30620002, 0x10400160, +0x3c020800, 0x8d1e001c, 0x1e5702, 0xafaa0034, +0x950a0016, 0x3c22024, 0xafaa0024, 0x8faa0034, +0x24020001, 0x15420006, 0x33deffff, 0x1e1140, +0x3403ecc0, 0x431021, 0x10000010, 0x2e2a821, +0x24020002, 0x15420005, 0x24020003, 0x1e1140, +0x24426cc0, 0x10000009, 0x2e2a821, 0x15420005, +0x1e1180, 0x1e1140, 0x24424cc0, 0x10000003, +0x2e2a821, 0x571021, 0x24550ce0, 0x96a2000e, +0x304afffc, 0x30420400, 0x10400003, 0xafaa002c, +0x100000e1, 0x8821, 0x10800004, 0x8821, +0x97b10026, 0x100000dd, 0xa6b10012, 0x8eb30018, +0x966a000c, 0xa7aa003e, 0x97a5003e, 0x2ca305dd, +0x38a28870, 0x2c420001, 0x621825, 0x10600015, +0x2021, 0x32c20800, 0x10400015, 0x24020800, +0x96630014, 0x14620012, 0x3402aaaa, 0x9663000e, +0x14620007, 0x2821, 0x96630010, 0x24020300, +0x14620004, 0xa01021, 0x96620012, 0x2c450001, +0xa01021, 0x54400006, 0x24040016, 0x10000004, +0x0, 0x24020800, 0x50a20001, 0x2404000e, +0x108000b9, 0x2649021, 0x92420000, 0x3042000f, +0x28080, 0x32c20100, 0x10400020, 0x2501821, +0x3c020020, 0x43102b, 0x1440000e, 0x2402021, +0x2821, 0x94820000, 0x24840002, 0xa22821, +0x83102b, 0x1440fffb, 0x30a2ffff, 0x51c02, +0x622821, 0x51c02, 0x30a2ffff, 0x10000009, +0x622821, 0x8f470148, 0x8f420110, 0x102842, +0x3c060020, 0x40f809, 0xafa80040, 0x3045ffff, +0x8fa80040, 0x50a00001, 0x3405ffff, 0x8faa002c, +0x354a0002, 0x10000002, 0xafaa002c, 0x2821, +0x32c20080, 0x10400090, 0xa6a50010, 0x26430009, +0x3c02001f, 0x3442ffff, 0x43102b, 0x10400003, +0x0, 0x8f420148, 0x621823, 0x90660000, +0x30c200ff, 0x38430006, 0x2c630001, 0x38420011, +0x2c420001, 0x621825, 0x1060007f, 0x24020800, +0x8821, 0x97a3003e, 0x1462000f, 0x2602021, +0x96710000, 0x96620002, 0x96630004, 0x96640006, +0x2228821, 0x2238821, 0x2248821, 0x96620008, +0x9663000a, 0x9664000c, 0x2228821, 0x2238821, +0x10000007, 0x2248821, 0x94820000, 0x24840002, +0x2228821, 0x92102b, 0x1440fffb, 0x0, +0x111c02, 0x3222ffff, 0x628821, 0x111c02, +0x3222ffff, 0x628821, 0x32c20200, 0x10400003, +0x26440006, 0x1000003e, 0x8021, 0x3c05001f, +0x34a5ffff, 0xa4102b, 0x10400003, 0x0, +0x8f420148, 0x822023, 0x94820000, 0x30421fff, +0x10400004, 0x2644000c, 0x96420002, 0x10000030, +0x508023, 0x96420002, 0x26430014, 0x508023, +0x3c020020, 0x43102b, 0x1440000a, 0xd08021, +0x9642000c, 0x2028021, 0x9642000e, 0x96430010, +0x96440012, 0x2028021, 0x2038021, 0x10000020, +0x2048021, 0xa4102b, 0x10400003, 0x0, +0x8f420148, 0x822023, 0x94820000, 0x24840002, +0x2028021, 0xa4102b, 0x10400003, 0x0, +0x8f420148, 0x822023, 0x94820000, 0x24840002, +0x2028021, 0xa4102b, 0x10400003, 0x0, +0x8f420148, 0x822023, 0x94820000, 0x24840002, +0x2028021, 0xa4102b, 0x10400003, 0x0, +0x8f420148, 0x822023, 0x94820000, 0x2028021, +0x3c020100, 0x2c21024, 0x1040000e, 0x0, +0x8faa002c, 0x31420004, 0x1040000a, 0x0, +0x9504000e, 0x2642021, 0xc004445, 0x2484fffc, +0x3042ffff, 0x2228821, 0x111c02, 0x3222ffff, +0x628821, 0x8faa0024, 0x1518823, 0x111402, +0x2228821, 0x2308821, 0x111402, 0x2228821, +0x3231ffff, 0x52200001, 0x3411ffff, 0x8faa002c, +0x354a0001, 0xafaa002c, 0xa6b10012, 0x97aa002e, +0xa6aa000e, 0x8faa002c, 0x31420004, 0x10400002, +0x24091000, 0x34098000, 0x8f480044, 0x8f4401a0, +0x8f4501a4, 0xafa90010, 0x8f490044, 0x84140, +0x1001821, 0xafa90014, 0x8f48000c, 0x2a03021, +0x24070020, 0xafa80018, 0x8f48010c, 0x1021, +0xa32821, 0xa3482b, 0x822021, 0x100f809, +0x892021, 0x1440001f, 0x0, 0x8f820128, +0x3c040001, 0x24847fe4, 0xafbe0014, 0xafa20010, +0x8f860124, 0x8f870120, 0x3c050007, 0xc002d3b, +0x34a59920, 0x3c040001, 0x24847f4c, 0x24020bfa, +0xafa20010, 0xafa00014, 0x8f860144, 0x3c070001, +0x24e77f98, 0xc002d3b, 0x3405dead, 0x8f82011c, +0x34420002, 0xaf82011c, 0x8f820220, 0x34420004, +0xaf820220, 0x8f820140, 0x3c030001, 0x431025, +0xaf820140, 0x8f420368, 0x2442ffff, 0xaf420368, +0x8f420044, 0x8f430088, 0x24420001, 0x431024, +0xaf420044, 0x8faa0034, 0x8f440368, 0x24020001, +0x15420006, 0x24020002, 0x8f42035c, 0x2442ffff, +0xaf42035c, 0x1000004a, 0x8f42035c, 0x15420006, +0x0, 0x8f420364, 0x2442ffff, 0xaf420364, +0x10000043, 0x8f420364, 0x8f420360, 0x2442ffff, +0xaf420360, 0x8f420360, 0x1000003d, 0x0, +0x30621000, 0x10400005, 0x30628000, 0x8f420078, +0x24420001, 0x10000036, 0xaf420078, 0x10400034, +0x0, 0x8f420078, 0x24420001, 0xaf420078, +0x8c030240, 0x43102b, 0x1440002d, 0x24070008, +0x8f440168, 0x8f45016c, 0x8f430044, 0x8f48000c, +0x8f860120, 0x24020040, 0xafa20010, 0xafa30014, +0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400011, 0x24020001, 0x3c010001, 0x370821, +0xa02240f2, 0x8f820124, 0xafa20010, 0x8f820128, +0x3c040001, 0x24847f44, 0xafa20014, 0x8f460044, +0x8f870120, 0x3c050009, 0xc002d3b, 0x34a51300, +0x1000000b, 0x0, 0x8f420304, 0x24420001, +0xaf420304, 0x8f420304, 0x8f420044, 0xaf42007c, +0x3c010001, 0x370821, 0xa02040f2, 0xaf400078, +0x8f420318, 0x24420001, 0xaf420318, 0x8f420318, +0x8fbf0060, 0x8fbe005c, 0x8fb50058, 0x8fb30054, +0x8fb20050, 0x8fb1004c, 0x8fb00048, 0x3e00008, +0x27bd0068, 0x3e00008, 0x0, 0x8f42013c, +0xaf8200c0, 0x8f42013c, 0xaf8200c4, 0x8f42013c, +0xaf8200c8, 0x8f420138, 0xaf8200d0, 0x8f420138, +0xaf8200d4, 0x8f420138, 0x3e00008, 0xaf8200d8, +0x27bdffe0, 0x27840208, 0x24050200, 0xafbf0018, +0xc002dbf, 0x24060008, 0x8c020204, 0xaf820210, +0xc004586, 0x0, 0x3c020002, 0x8c428668, +0x30420002, 0x1040000e, 0x2021, 0x8c060248, +0x24020002, 0x3c010002, 0xac22866c, 0xc005738, +0x24050002, 0x2021, 0x8c060248, 0x24020001, +0x3c010002, 0xac22866c, 0x10000011, 0x24050001, +0x8c060248, 0x24020004, 0x3c010002, 0xac22866c, +0xc005738, 0x24050004, 0x3c020002, 0x8c428668, +0x30420001, 0x10400008, 0x24020001, 0x3c010002, +0xac22866c, 0x2021, 0x24050001, 0x3c06601b, +0xc005738, 0x0, 0x3c040002, 0x248480b0, +0x8f420150, 0x8f430154, 0x3c050008, 0x8f460158, +0x21640, 0x31940, 0x34630403, 0x431025, +0x633c0, 0x461025, 0xaf82021c, 0xafa00010, +0xafa00014, 0x8f86021c, 0x34a50200, 0xc002d3b, +0x3821, 0x3c010002, 0xac208664, 0x3c010002, +0xac20867c, 0x8fbf0018, 0x3e00008, 0x27bd0020, +0x27bdffe0, 0x3c050008, 0x34a50300, 0xafbf0018, +0xafa00010, 0xafa00014, 0x8f860200, 0x3c040002, +0x248480bc, 0xc002d3b, 0x3821, 0x8f420410, +0x24420001, 0xaf420410, 0x8f420410, 0x8fbf0018, +0x3e00008, 0x27bd0020, 0x27bdffd8, 0xafbf0020, +0xafb1001c, 0xafb00018, 0x8f4203a4, 0x24420001, +0xaf4203a4, 0x8f4203a4, 0x8f900220, 0x8f8200e0, +0xafa20010, 0x8f8200e4, 0xafa20014, 0x8f8600c4, +0x8f8700c8, 0x3c040002, 0x248480c8, 0xc002d3b, +0x2002821, 0x3c044000, 0x2041024, 0x504000bc, +0x3c040100, 0x8f4203bc, 0x24420001, 0xaf4203bc, +0x8f4203bc, 0x8f8700c4, 0x8f8300c8, 0x8f420148, +0x671823, 0x43102b, 0x10400003, 0x0, +0x8f420148, 0x621821, 0x10600005, 0x0, +0x8f42014c, 0x43102b, 0x1040000d, 0x0, +0x8f8200e0, 0x8f430124, 0xaf42011c, 0xaf430114, +0x8f820220, 0x3c0308ff, 0x3463fffb, 0x431024, +0x441025, 0xaf820220, 0x10000104, 0x0, +0x8f820220, 0x3c0308ff, 0x3463ffff, 0x431024, +0x34420004, 0xaf820220, 0x8f8200e0, 0x8f430124, +0xaf42011c, 0xaf430114, 0x8f8600c8, 0x8f840120, +0x8f830124, 0x10000005, 0x2821, 0x14620002, +0x24620020, 0x27624800, 0x401821, 0x1064000c, +0x30a200ff, 0x8c620018, 0x30420003, 0x1040fff7, +0x27624fe0, 0x8f4203d0, 0x24050001, 0x24420001, +0xaf4203d0, 0x8f4203d0, 0x8c660008, 0x30a200ff, +0x1440005b, 0x0, 0x934205c4, 0x14400058, +0x0, 0x8f8700c4, 0x8f8800e0, 0x8f8400e4, +0x2402fff8, 0x1024024, 0x1041023, 0x218c3, +0x4620001, 0x24630200, 0x10600005, 0x24020001, +0x1062000a, 0x0, 0x10000021, 0x0, +0x8f4203c0, 0xe03021, 0x24420001, 0xaf4203c0, +0x8f4203c0, 0x10000042, 0x0, 0x8f4203c4, +0x24420001, 0xaf4203c4, 0x8c860000, 0x8f420148, +0x8f4303c4, 0xe61823, 0x43102b, 0x10400004, +0x2c62233f, 0x8f420148, 0x621821, 0x2c62233f, +0x14400033, 0x0, 0x8f42020c, 0x24420001, +0xaf42020c, 0x8f42020c, 0xe03021, 0x24820008, +0xaf8200e4, 0xaf8200e8, 0x10000029, 0x0, +0x8f4203c8, 0x24420001, 0xaf4203c8, 0x8f4203c8, +0x8c850000, 0x8f420148, 0xa71823, 0x43102b, +0x10400003, 0x0, 0x8f420148, 0x621821, +0x8f42014c, 0x43102b, 0x5440000b, 0xa03021, +0x8f42020c, 0x24420001, 0xaf42020c, 0x8f42020c, +0x24820008, 0xaf8200e4, 0x8f8400e4, 0xaf8400e8, +0x1488ffeb, 0x0, 0x1488000d, 0x27623000, +0x14820002, 0x2482fff8, 0x27623ff8, 0x94430006, +0x3c02001f, 0x3442ffff, 0xc33021, 0x46102b, +0x10400003, 0x0, 0x8f420148, 0xc23023, +0xaf8600c8, 0x8f8300c4, 0x8f420148, 0xc31823, +0x43102b, 0x10400003, 0x0, 0x8f420148, +0x621821, 0x10600005, 0x0, 0x8f42014c, +0x43102b, 0x1040000a, 0x3c02fdff, 0x8f820220, +0x3c0308ff, 0x3463fffb, 0x431024, 0x3c034000, +0x431025, 0xaf820220, 0x10000070, 0x0, +0x8f4303cc, 0x3442ffff, 0x282a024, 0x24630001, +0xaf4303cc, 0x8f4203cc, 0x10000068, 0x0, +0x2041024, 0x1040000f, 0x3c110200, 0x8f4203a8, +0x24420001, 0xaf4203a8, 0x8f4203a8, 0x8f820220, +0x3c0308ff, 0x3463ffff, 0x431024, 0x441025, +0xaf820220, 0xc00430b, 0x0, 0x10000057, +0x0, 0x2111024, 0x50400009, 0x3c110400, +0x8f4203ac, 0x24420001, 0xaf4203ac, 0x8f4203ac, +0xc00430b, 0x0, 0x1000002e, 0x0, +0x2111024, 0x10400033, 0x3c02b800, 0x8f830224, +0x24021402, 0x1462001d, 0x3c050008, 0x3c040002, +0x248480d4, 0xafa00010, 0xafa00014, 0x8f860224, +0x34a50500, 0xc002d3b, 0x3821, 0x3c040002, +0x248480a0, 0x240203b3, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070002, 0x24e780e0, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x8f4203b0, +0x24420001, 0xaf4203b0, 0x8f4203b0, 0x8f820220, +0x2002021, 0x34420002, 0xaf820220, 0xc0054be, +0x0, 0x8f820220, 0x3c0308ff, 0x3463ffff, +0x431024, 0x511025, 0xaf820220, 0x10000017, +0x0, 0x2021024, 0x10400014, 0x240203c7, +0x3c040002, 0x248480a0, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070002, 0x24e780e0, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x8fbf0020, +0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0028, +0x3e00008, 0x0, 0x3c020002, 0x8c42867c, +0x27bdffb0, 0xafbf0048, 0xafbe0044, 0xafb50040, +0xafb3003c, 0xafb20038, 0xafb10034, 0x1040000f, +0xafb00030, 0x3c040002, 0x248480e8, 0x3c050008, +0xafa00010, 0xafa00014, 0x8f860220, 0x34a50600, +0x24020001, 0x3c010002, 0xac20867c, 0x3c010002, +0xac228670, 0xc002d3b, 0x3821, 0x3c037fff, +0x8c020268, 0x3463ffff, 0x3c04fdff, 0x431024, +0xac020268, 0x8f420004, 0x3484ffff, 0x30420002, +0x10400092, 0x284a024, 0x3c040600, 0x34842000, +0x8f420004, 0x2821, 0x2403fffd, 0x431024, +0xaf420004, 0xafa40020, 0x8f5e0018, 0x27aa0020, +0x240200ff, 0x13c20002, 0xafaa002c, 0x27c50001, +0x8c020228, 0xa09021, 0x1642000e, 0x1e38c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040002, 0x24848068, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60020, 0x1000006d, +0x34a50500, 0xf71021, 0x8fa30020, 0x8fa40024, +0xac4304c0, 0xac4404c4, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x1040001b, +0x9821, 0xe08821, 0x263504c0, 0x8f440178, +0x8f45017c, 0x2201821, 0x240a0004, 0xafaa0010, +0xafb20014, 0x8f48000c, 0x1021, 0x2f53021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x54400006, 0x24130001, 0x8f820054, 0x2021023, +0x2c4203e9, 0x1440ffe9, 0x0, 0x326200ff, +0x54400017, 0xaf520018, 0x8f420378, 0x24420001, +0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, +0xafa20010, 0x8f820124, 0x3c040002, 0x24848074, +0x3c050009, 0xafa20014, 0x8d460000, 0x10000035, +0x34a50600, 0x8f420308, 0x24130001, 0x24420001, +0xaf420308, 0x8f420308, 0x1000001e, 0x326200ff, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x10400016, 0x9821, 0x3c150020, +0x24110010, 0x8f42000c, 0x8f440160, 0x8f450164, +0x8f860120, 0xafb10010, 0xafb20014, 0x551025, +0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, +0x24c6001c, 0x1440ffe3, 0x0, 0x8f820054, +0x2021023, 0x2c4203e9, 0x1440ffee, 0x0, +0x326200ff, 0x14400011, 0x0, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8faa002c, 0xafa20010, 0x8f820124, 0x3c040002, +0x2484807c, 0x3c050009, 0xafa20014, 0x8d460000, +0x34a50700, 0xc002d3b, 0x3c03821, 0x8f4202ec, +0x24420001, 0xaf4202ec, 0x8f4202ec, 0x8fbf0048, +0x8fbe0044, 0x8fb50040, 0x8fb3003c, 0x8fb20038, +0x8fb10034, 0x8fb00030, 0x3e00008, 0x27bd0050, +0x3c020002, 0x8c42867c, 0x27bdffe0, 0x1440000d, +0xafbf0018, 0x3c040002, 0x248480f4, 0x3c050008, +0xafa00010, 0xafa00014, 0x8f860220, 0x34a50700, +0x24020001, 0x3c010002, 0xac22867c, 0xc002d3b, +0x3821, 0x3c020004, 0x2c21024, 0x10400007, +0x0, 0x8f820220, 0x3c0308ff, 0x3463ffff, +0x431024, 0x34420008, 0xaf820220, 0x3c050002, +0x8ca5866c, 0x24020001, 0x14a20007, 0x2021, +0xc0058d7, 0x24050001, 0xac02026c, 0x8c03026c, +0x10000006, 0x3c020007, 0xc0058d7, 0x2021, +0xac020268, 0x8c030268, 0x3c020007, 0x621824, +0x3c020002, 0x5062000d, 0x3c0205f5, 0x43102b, +0x14400006, 0x3c020004, 0x3c020001, 0x10620009, +0x3c020098, 0x1000000b, 0x0, 0x14620009, +0x3c023b9a, 0x10000004, 0x3442ca00, 0x10000002, +0x3442e100, 0x34429680, 0xaf4201fc, 0x8f4201fc, +0xaee20064, 0x8fbf0018, 0x3e00008, 0x27bd0020, +0x86102b, 0x50400001, 0x872023, 0xc41023, +0x24843, 0x125102b, 0x1040001b, 0x91040, +0x824021, 0x88102b, 0x10400007, 0x1821, +0x94820000, 0x24840002, 0x621821, 0x88102b, +0x1440fffb, 0x0, 0x602021, 0xc73023, +0xa91023, 0x21040, 0xc22821, 0xc5102b, +0x10400007, 0x1821, 0x94c20000, 0x24c60002, +0x621821, 0xc5102b, 0x1440fffb, 0x0, +0x1000000d, 0x832021, 0x51040, 0x822821, +0x85102b, 0x10400007, 0x1821, 0x94820000, +0x24840002, 0x621821, 0x85102b, 0x1440fffb, +0x0, 0x602021, 0x41c02, 0x3082ffff, +0x622021, 0x41c02, 0x3082ffff, 0x622021, +0x3e00008, 0x3082ffff, 0x3e00008, 0x0, +0x802821, 0x30a20001, 0x1040002b, 0x3c03001f, +0x3463ffff, 0x24a20004, 0x62102b, 0x54400007, +0x65102b, 0x90a20001, 0x90a40003, 0x90a30000, +0x90a50002, 0x1000002a, 0x441021, 0x10400003, +0x0, 0x8f420148, 0xa22823, 0x90a40000, +0x24a50001, 0x65102b, 0x10400003, 0x0, +0x8f420148, 0xa22823, 0x90a20000, 0x24a50001, +0x21200, 0x822021, 0x65102b, 0x10400003, +0x0, 0x8f420148, 0xa22823, 0x90a20000, +0x24a50001, 0x822021, 0x65102b, 0x10400003, +0x0, 0x8f420148, 0xa22823, 0x90a20000, +0x1000002d, 0x21200, 0x3463ffff, 0x24a20004, +0x62102b, 0x5440000a, 0x65102b, 0x90a20000, +0x90a40002, 0x90a30001, 0x90a50003, 0x441021, +0x21200, 0x651821, 0x10000020, 0x432021, +0x10400003, 0x0, 0x8f420148, 0xa22823, +0x90a20000, 0x24a50001, 0x22200, 0x65102b, +0x10400003, 0x0, 0x8f420148, 0xa22823, +0x90a20000, 0x24a50001, 0x822021, 0x65102b, +0x10400003, 0x0, 0x8f420148, 0xa22823, +0x90a20000, 0x24a50001, 0x21200, 0x822021, +0x65102b, 0x10400003, 0x0, 0x8f420148, +0xa22823, 0x90a20000, 0x822021, 0x41c02, +0x3082ffff, 0x622021, 0x41c02, 0x3082ffff, +0x622021, 0x3e00008, 0x3082ffff, 0x8f820220, +0x34420002, 0xaf820220, 0x3c020002, 0x8c42a8b8, +0x30424000, 0x10400054, 0x24040001, 0x8f820200, +0x24067fff, 0x8f830200, 0x30450002, 0x2402fffd, +0x621824, 0xaf830200, 0xaf840204, 0x8f830054, +0x8f820054, 0x10000002, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x8f820224, 0x1444004d, 0x42040, 0xc4102b, +0x1040fff1, 0x0, 0x8f820200, 0x451025, +0xaf820200, 0x8f820220, 0x34428000, 0xaf820220, +0x8f830054, 0x8f820054, 0x10000002, 0x24630001, +0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, +0x0, 0x8f820220, 0x3c030004, 0x431024, +0x1440000f, 0x0, 0x8f820220, 0x3c03ffff, +0x34637fff, 0x431024, 0xaf820220, 0x8f830054, +0x8f820054, 0x10000002, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x8f820220, 0x3c030004, 0x431024, 0x1440000d, +0x0, 0x8f820220, 0x34428000, 0xaf820220, +0x8f830054, 0x8f820054, 0x10000002, 0x24630001, +0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, +0x0, 0x8f820220, 0x3c030004, 0x431024, +0x1040001b, 0x1021, 0x8f830220, 0x24020001, +0x10000015, 0x3c04f700, 0x8f820220, 0x3c04f700, +0x441025, 0xaf820220, 0x8f820220, 0x2403fffd, +0x431024, 0xaf820220, 0x8f820220, 0x3c030300, +0x431024, 0x14400003, 0x0, 0x10000008, +0x1021, 0x8f820220, 0x34420002, 0xaf820220, +0x8f830220, 0x24020001, 0x641825, 0xaf830220, +0x3e00008, 0x0, 0x27bdffe0, 0x2021, +0x3c050100, 0x24020001, 0xafbf0018, 0xaf80021c, +0xaf820200, 0xaf820220, 0x27625000, 0xaf8200c0, +0x27625000, 0xaf8200c4, 0x27625000, 0xaf8200c8, +0x27625000, 0xaf8200d0, 0x27625000, 0xaf8200d4, +0x27625000, 0xaf8200d8, 0x27623000, 0xaf8200e0, +0x27623000, 0xaf8200e4, 0x27623000, 0xaf8200e8, +0x27622800, 0xaf8200f0, 0x27622800, 0xaf8200f4, +0x27622800, 0xaf8200f8, 0x418c0, 0x24840001, +0x3631021, 0xac453004, 0x3631021, 0xac403000, +0x28820200, 0x1440fff9, 0x418c0, 0x2021, +0x418c0, 0x24840001, 0x3631021, 0xac402804, +0x3631021, 0xac402800, 0x28820100, 0x1440fff9, +0x418c0, 0xaf80023c, 0x24030080, 0x24040100, +0xac600000, 0x24630004, 0x64102b, 0x5440fffd, +0xac600000, 0x8f830040, 0x3c02f000, 0x621824, +0x3c025000, 0x1062000c, 0x43102b, 0x14400006, +0x3c026000, 0x3c024000, 0x1062000c, 0x24020800, +0x1000000e, 0x240202a9, 0x10620008, 0x24020800, +0x1000000a, 0x240202a9, 0x24020700, 0x3c010002, +0xac228680, 0x10000018, 0x0, 0x3c010002, +0xac228680, 0x10000014, 0x0, 0x3c040002, +0x248481c0, 0xafa20010, 0xafa00014, 0x8f860144, +0x3c070002, 0x24e781d8, 0xc002d3b, 0x3405dead, +0x8f82011c, 0x34420002, 0xaf82011c, 0x8f820220, +0x34420004, 0xaf820220, 0x8f820140, 0x3c030001, +0x431025, 0xaf820140, 0x8fbf0018, 0x3e00008, +0x27bd0020, 0x3c020002, 0x8c428690, 0x27bdffd0, +0xafbf002c, 0xafb20028, 0xafb10024, 0xafb00020, +0x3c010002, 0xac208668, 0x10400005, 0x0, +0xc0053c3, 0x0, 0x3c010002, 0xac208690, +0x8f830054, 0x8f820054, 0x10000002, 0x24630064, +0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, +0x0, 0xc0053de, 0x0, 0x24040001, +0x2821, 0x27a60018, 0x34028000, 0xc004bdc, +0xa7a20018, 0x8f830054, 0x8f820054, 0x10000002, +0x24630064, 0x8f820054, 0x621023, 0x2c420065, +0x1440fffc, 0x24040001, 0x24050001, 0xc004b9a, +0x27a60018, 0x8f830054, 0x8f820054, 0x10000002, +0x24630064, 0x8f820054, 0x621023, 0x2c420065, +0x1440fffc, 0x24040001, 0x24050001, 0xc004b9a, +0x27a60018, 0x8f830054, 0x8f820054, 0x10000002, +0x24630064, 0x8f820054, 0x621023, 0x2c420065, +0x1440fffc, 0x24040001, 0x3c060002, 0x24c687e0, +0xc004b9a, 0x24050002, 0x8f830054, 0x8f820054, +0x10000002, 0x24630064, 0x8f820054, 0x621023, +0x2c420065, 0x1440fffc, 0x24040001, 0x24050003, +0x3c100002, 0x261087e2, 0xc004b9a, 0x2003021, +0x97a60018, 0x3c070002, 0x94e787e0, 0x3c040002, +0x248481f0, 0xafa00014, 0x96020000, 0x3c05000d, +0x34a50100, 0xc002d3b, 0xafa20010, 0x97a20018, +0x10400050, 0x24036040, 0x96020000, 0x3042fff0, +0x1443000d, 0x24020020, 0x3c030002, 0x946387e0, +0x1462000c, 0x24027830, 0x24020003, 0x3c010002, +0xac228668, 0x24020005, 0x3c010002, 0xac2287f0, +0x10000041, 0x0, 0x3c030002, 0x946387e0, +0x24027830, 0x1462000d, 0x24030010, 0x3c020002, +0x944287e2, 0x3042fff0, 0x14430008, 0x24020003, +0x3c010002, 0xac228668, 0x24020006, 0x3c010002, +0xac2287f0, 0x10000030, 0x0, 0x3c020002, +0x8c428668, 0x3c030002, 0x946387e0, 0x34420001, +0x3c010002, 0xac228668, 0x24020015, 0x1462000b, +0x0, 0x3c020002, 0x944287e2, 0x3042fff0, +0x3843f420, 0x2c630001, 0x3842f430, 0x2c420001, +0x621825, 0x1460001c, 0x24020003, 0x3c030002, +0x946387e0, 0x24027810, 0x14620017, 0x24020002, +0x3c020002, 0x944287e2, 0x3042fff0, 0x14400012, +0x24020002, 0x10000010, 0x24020004, 0x3c020002, +0x8c428668, 0x34420008, 0x3c010002, 0xac228668, +0x1000005f, 0x24020004, 0x3c020002, 0x8c428668, +0x34420004, 0x3c010002, 0xac228668, 0x100000af, +0x0, 0x24020001, 0x3c010002, 0xac2287fc, +0x3c020002, 0x8c428668, 0x30420002, 0x144000b2, +0x3c09fff0, 0x24020e00, 0xaf820238, 0x8f840054, +0x8f820054, 0x24030008, 0x3c010002, 0xac23866c, +0x10000002, 0x248401f4, 0x8f820054, 0x821023, +0x2c4201f5, 0x1440fffc, 0x3c0200c8, 0x344201fb, +0xaf820238, 0x8f830054, 0x8f820054, 0x10000002, +0x246301f4, 0x8f820054, 0x621023, 0x2c4201f5, +0x1440fffc, 0x8021, 0x24120001, 0x24110009, +0xc004a53, 0x0, 0x3c010002, 0xac328688, +0xc004b1d, 0x0, 0x3c020002, 0x8c428688, +0x1451fffb, 0x3c0200c8, 0x344201f6, 0xaf820238, +0x8f830054, 0x8f820054, 0x10000002, 0x2463000a, +0x8f820054, 0x621023, 0x2c42000b, 0x1440fffc, +0x0, 0x8f820220, 0x24040001, 0x34420002, +0xaf820220, 0x8f830200, 0x24057fff, 0x2402fffd, +0x621824, 0xaf830200, 0xaf840204, 0x8f830054, +0x8f820054, 0x10000002, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x8f820224, 0x14440005, 0x34028000, 0x42040, +0xa4102b, 0x1040fff0, 0x34028000, 0x1082ff9f, +0x26100001, 0x2e020014, 0x1440ffcd, 0x24020004, +0x3c010002, 0xac22866c, 0x8021, 0x24120009, +0x3c11ffff, 0x36313f7f, 0xc004a53, 0x0, +0x24020001, 0x3c010002, 0xac228688, 0xc004b1d, +0x0, 0x3c020002, 0x8c428688, 0x1452fffb, +0x0, 0x8f820044, 0x511024, 0x34425080, +0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, +0x2463000a, 0x8f820054, 0x621023, 0x2c42000b, +0x1440fffc, 0x0, 0x8f820044, 0x511024, +0x3442f080, 0xaf820044, 0x8f830054, 0x8f820054, +0x10000002, 0x2463000a, 0x8f820054, 0x621023, +0x2c42000b, 0x1440fffc, 0x0, 0x8f820220, +0x3c03f700, 0x431025, 0xaf820220, 0x8f830054, +0x8f820054, 0x10000002, 0x24630064, 0x8f820054, +0x621023, 0x2c420065, 0x1440fffc, 0x0, +0x8f820220, 0x24040001, 0x34420002, 0xaf820220, +0x8f830200, 0x24057fff, 0x2402fffd, 0x621824, +0xaf830200, 0xaf840204, 0x8f830054, 0x8f820054, +0x10000002, 0x24630001, 0x8f820054, 0x621023, +0x2c420002, 0x1440fffc, 0x0, 0x8f820224, +0x14440005, 0x34028000, 0x42040, 0xa4102b, +0x1040fff0, 0x34028000, 0x1082ff4f, 0x26100001, +0x2e020064, 0x1440ffb0, 0x0, 0x3c020002, +0x8c428668, 0x30420004, 0x14400007, 0x3c09fff0, +0x8f820044, 0x3c03ffff, 0x34633f7f, 0x431024, +0xaf820044, 0x3c09fff0, 0x3529bdc0, 0x3c060002, +0x8cc68668, 0x3c040002, 0x248481f0, 0x24020001, +0x3c010002, 0xac228670, 0x8f820054, 0x3c070002, +0x8ce787fc, 0x3c030002, 0x946387e0, 0x3c080002, +0x950887e2, 0x3c05000d, 0x34a50100, 0x3c010002, +0xac20866c, 0x491021, 0x3c010002, 0xac2287ec, +0xafa30010, 0xc002d3b, 0xafa80014, 0x8fbf002c, +0x8fb20028, 0x8fb10024, 0x8fb00020, 0x3e00008, +0x27bd0030, 0x27bdffe8, 0x3c050002, 0x8ca5866c, +0x24060004, 0x24020001, 0x14a20014, 0xafbf0010, +0x3c020002, 0x8c42a8bc, 0x30428000, 0x10400005, +0x3c04000f, 0x3c030002, 0x8c6387fc, 0x10000005, +0x34844240, 0x3c040004, 0x3c030002, 0x8c6387fc, +0x348493e0, 0x24020005, 0x14620016, 0x0, +0x3c04003d, 0x10000013, 0x34840900, 0x3c020002, +0x8c42a8b8, 0x30428000, 0x10400005, 0x3c04001e, +0x3c030002, 0x8c6387fc, 0x10000005, 0x34848480, +0x3c04000f, 0x3c030002, 0x8c6387fc, 0x34844240, +0x24020005, 0x14620003, 0x0, 0x3c04007a, +0x34841200, 0x3c020002, 0x8c4287ec, 0x8f830054, +0x441021, 0x431023, 0x44102b, 0x1440004e, +0x0, 0x3c020002, 0x8c428674, 0x1440004a, +0x0, 0x3c010002, 0xac208684, 0x10c00026, +0x0, 0x3c090002, 0x8d298668, 0x24070001, +0x3c044000, 0x3c080002, 0x2508a8bc, 0x250afffc, +0x52842, 0x14a00002, 0x24c6ffff, 0x24050008, +0xa91024, 0x10400011, 0x0, 0x14a70009, +0x0, 0x8d020000, 0x441024, 0x1040000b, +0x0, 0x3c010002, 0xac258684, 0x10000007, +0x0, 0x8d420000, 0x441024, 0x10400003, +0x0, 0x3c010002, 0xac278684, 0x3c020002, +0x8c428684, 0x6182b, 0x2c420001, 0x431024, +0x5440ffe4, 0x52842, 0x8f820054, 0x3c030002, +0x8c638684, 0x3c010002, 0xac2287ec, 0x1060003b, +0x24020005, 0x3c030002, 0x8c6387fc, 0x3c010002, +0xac25866c, 0x14620012, 0x24020001, 0x3c020002, +0x8c42a8b8, 0x3c032000, 0x34635000, 0x431024, +0x14400006, 0x24020001, 0x3c010002, 0xac2087d8, +0x3c010002, 0xac22866c, 0x24020001, 0x3c010002, +0xac2286f0, 0x3c010002, 0xac228678, 0x24020001, +0x3c010002, 0xac228670, 0x3c020002, 0x8c428684, +0x1040001e, 0x0, 0x3c020002, 0x8c428670, +0x10400008, 0x24020001, 0x3c010002, 0xac208670, +0xaee204b8, 0x3c010002, 0xac2086e8, 0x3c010002, +0xac2286a0, 0x8ee304b8, 0x24020008, 0x10620005, +0x24020001, 0xc0047b3, 0x0, 0x1000000b, +0x0, 0x3c030002, 0x8c63866c, 0x10620007, +0x2402000e, 0x3c030002, 0x8c63a850, 0x10620003, +0x0, 0xc0054be, 0x8f840220, 0x8fbf0010, +0x3e00008, 0x27bd0018, 0x27bdffd8, 0x3c03fdff, +0x3c040002, 0x8c84866c, 0x3c020002, 0x8c428694, +0x3463ffff, 0x283a024, 0x14820006, 0xafbf0020, +0x8ee304b8, 0x3c020002, 0x8c428698, 0x10620006, +0x0, 0x8ee204b8, 0x3c010002, 0xac248694, +0x3c010002, 0xac228698, 0x3c030002, 0x8c63866c, +0x24020002, 0x106201cc, 0x2c620003, 0x10400005, +0x24020001, 0x1062000a, 0x0, 0x1000027d, +0x0, 0x24020004, 0x106200eb, 0x24020008, +0x1062014e, 0x24020001, 0x10000276, 0x0, +0x8ee204b8, 0x2443ffff, 0x2c620008, 0x10400273, +0x31080, 0x3c010002, 0x220821, 0x8c228210, +0x400008, 0x0, 0x3c030002, 0x8c6387fc, +0x24020005, 0x14620012, 0x0, 0x3c020002, +0x8c428678, 0x10400009, 0x24020003, 0xc004a53, +0x0, 0x24020002, 0xaee204b8, 0x3c010002, +0xac208678, 0x10000002, 0x0, 0xaee204b8, +0x3c010002, 0xac20860c, 0x10000258, 0x0, +0xc004a53, 0x0, 0x3c020002, 0x8c428678, +0x3c010002, 0xac20860c, 0x144001a8, 0x24020002, +0x100001f1, 0x24020007, 0x3c030002, 0x8c6387fc, +0x24020005, 0x14620003, 0x24020001, 0x3c010002, +0xac22869c, 0xc004c1d, 0x0, 0x3c030002, +0x8c63869c, 0x100001a3, 0x24020011, 0x3c050002, +0x8ca5866c, 0x3c060002, 0x8cc6a8bc, 0xc005738, +0x2021, 0x24020005, 0x3c010002, 0xac208678, +0x10000236, 0xaee204b8, 0x3c040002, 0x248481fc, +0x3c05000f, 0x34a50100, 0x3021, 0x3821, +0xafa00010, 0xc002d3b, 0xafa00014, 0x3c040002, +0x248481c0, 0x240204b0, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070002, 0x24e781d8, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x10000217, +0x0, 0x24040001, 0x2405001a, 0x8f820220, +0x27a60018, 0x3c03f700, 0x431025, 0xaf820220, +0xc004b9a, 0x0, 0x97a60018, 0x30c20200, +0x104001a1, 0x3c05000c, 0x3c040002, 0x24848208, +0x34a50111, 0x3c020002, 0x8c42866c, 0x3c030002, +0x8c6387d8, 0x3821, 0xafa20010, 0xc002d3b, +0xafa30014, 0x2021, 0x2821, 0xc005400, +0x24064040, 0x1000018e, 0x24020002, 0x8f820220, +0x3c030004, 0x431024, 0x14400197, 0x24020007, +0x8f830054, 0x3c020002, 0x8c4287e4, 0x2463d8f0, +0x431023, 0x2c422710, 0x14400003, 0x24020001, +0x3c010002, 0xac228670, 0x3c020002, 0x8c42a8bc, +0x30425000, 0x104001e5, 0x0, 0x8f820220, +0x30428000, 0x1040019f, 0x0, 0x10000197, +0x0, 0x3c050002, 0x8ca5866c, 0xc0058d7, +0x2021, 0xc005b59, 0x2021, 0x3c030002, +0x8c63a8b4, 0x46101d3, 0x24020001, 0x3c020008, +0x621024, 0x10400006, 0x0, 0x8f820214, +0x3c03ffff, 0x431024, 0x10000005, 0x3442251f, +0x8f820214, 0x3c03ffff, 0x431024, 0x3442241f, +0xaf820214, 0x8f820220, 0x3c030200, 0x34420002, +0xaf820220, 0x24020008, 0xaee204b8, 0x8f820220, +0x283a025, 0x3c030004, 0x431024, 0x14400016, +0x0, 0x3c020002, 0x8c42a8bc, 0x30425000, +0x1040000d, 0x0, 0x8f820220, 0x30428000, +0x10400006, 0x0, 0x8f820220, 0x3c03ffff, +0x34637fff, 0x10000003, 0x431024, 0x8f820220, +0x34428000, 0xaf820220, 0x8f820220, 0x3c03f700, +0x431025, 0xaf820220, 0x3c030002, 0x8c6387fc, +0x24020005, 0x1462000a, 0x0, 0x3c020002, +0x944287e2, 0x24429fbc, 0x2c420004, 0x10400004, +0x24040018, 0x24050002, 0xc005400, 0x24060020, +0xc0043c9, 0x0, 0x3c010002, 0xac2086ec, +0x10000192, 0x0, 0x8ee204b8, 0x2443ffff, +0x2c620008, 0x1040018d, 0x31080, 0x3c010002, +0x220821, 0x8c228230, 0x400008, 0x0, +0xc00430b, 0x0, 0x3c010002, 0xac208670, +0xaf800204, 0x3c010002, 0xac20a8a0, 0xc004a53, +0x0, 0x24020001, 0x3c010002, 0xac228688, +0x1000010f, 0x24020002, 0xc004b1d, 0x0, +0x3c030002, 0x8c638688, 0x100000d6, 0x24020009, +0x3c020002, 0x8c42a8b8, 0x30424000, 0x10400004, +0x0, 0x8f820044, 0x10000006, 0x3442f080, +0x8f820044, 0x3c03ffff, 0x34633f7f, 0x431024, +0x3442a080, 0xaf820044, 0x8f830054, 0x100000fc, +0x24020004, 0x8f830054, 0x3c020002, 0x8c4287e4, +0x2463d8f0, 0x431023, 0x2c422710, 0x1440015b, +0x24020005, 0x100000c6, 0x0, 0x8f820220, +0x3c03f700, 0x431025, 0xaf820220, 0xaf800204, +0x3c010002, 0xac20a8a0, 0x100000e7, 0x0, +0x8f830054, 0x3c020002, 0x8c4287e4, 0x2463fff6, +0x431023, 0x2c42000a, 0x14400148, 0x24020007, +0x100000e9, 0x0, 0xc0044a8, 0x0, +0x10400140, 0x24020001, 0x8f820214, 0x3c03ffff, +0x3c040002, 0x8c8487d8, 0x431024, 0x3442241f, +0xaf820214, 0x24020008, 0x10800005, 0xaee204b8, +0x3c020002, 0x8c428708, 0x1040004e, 0x24020001, +0x8f820220, 0x3c030008, 0x431024, 0x10400054, +0x3c020200, 0x10000063, 0x0, 0x8ee204b8, +0x2443ffff, 0x2c620007, 0x10400128, 0x31080, +0x3c010002, 0x220821, 0x8c228250, 0x400008, +0x0, 0xc004b1d, 0x0, 0x3c030002, +0x8c638688, 0x1000007f, 0x24020009, 0x3c020002, +0x8c42a8b8, 0x30424000, 0x10400003, 0x3c0200c8, +0x10000002, 0x344201f6, 0x344201fe, 0xaf820238, +0x8f830054, 0x100000aa, 0x24020004, 0x8f830054, +0x3c020002, 0x8c4287e4, 0x2463d8f0, 0x431023, +0x2c422710, 0x14400109, 0x24020005, 0x10000074, +0x0, 0x8f830054, 0x3c020002, 0x8c4287e4, +0x2463fff6, 0x431023, 0x2c42000a, 0x144000ff, +0x24020007, 0x100000a0, 0x0, 0xc0044a8, +0x0, 0x104000f7, 0x24020001, 0x8f820214, +0x3c03ffff, 0x3c040002, 0x8c8487d8, 0x431024, +0x3442241f, 0xaf820214, 0x24020008, 0x1080000f, +0xaee204b8, 0x3c020002, 0x8c428708, 0x1440000b, +0x0, 0x8f820220, 0x34420002, 0xaf820220, +0x24020001, 0x3c010002, 0xac22a850, 0xc0054be, +0x8f840220, 0x10000017, 0x0, 0x8f820220, +0x3c030008, 0x431024, 0x14400012, 0x3c020200, +0x282a025, 0x2402000e, 0x3c010002, 0xac22a850, +0xc005b59, 0x2021, 0x8f820220, 0x34420002, +0xaf820220, 0xc0043c9, 0x0, 0x3c050002, +0x8ca5866c, 0xc0058d7, 0x2021, 0x100000cb, +0x0, 0x3c020002, 0x8c428708, 0x104000c7, +0x0, 0x3c020002, 0x8c428704, 0x2442ffff, +0x3c010002, 0xac228704, 0x144000c0, 0x24020002, +0x3c010002, 0xac208708, 0x3c010002, 0xac228704, +0x100000ba, 0x0, 0x8ee204b8, 0x2443ffff, +0x2c620007, 0x104000b5, 0x31080, 0x3c010002, +0x220821, 0x8c228270, 0x400008, 0x0, +0x3c020002, 0x8c428678, 0x10400019, 0x24020005, +0xc004a53, 0x0, 0x24020002, 0xaee204b8, +0x3c010002, 0xac208678, 0x100000a4, 0x0, +0xc004f82, 0x0, 0x3c030002, 0x8c6386a0, +0x24020006, 0x1462009d, 0x24020003, 0x1000009b, +0xaee204b8, 0x3c050002, 0x8ca5866c, 0x3c060002, +0x8cc6a8b8, 0xc005738, 0x2021, 0x24020005, +0x10000092, 0xaee204b8, 0x24040001, 0x2405001a, +0x8f820220, 0x27a60018, 0x3c03f700, 0x431025, +0xaf820220, 0xc004b9a, 0x0, 0x97a60018, +0x30c20200, 0x1040001c, 0x3c05000c, 0x3c040002, +0x24848208, 0x34a50112, 0x3c020002, 0x8c42866c, +0x3c030002, 0x8c6387d8, 0x3821, 0xafa20010, +0xc002d3b, 0xafa30014, 0x2021, 0x2821, +0xc005400, 0x24064040, 0x3c020002, 0x8c4287d8, +0x10400006, 0x2021, 0x2821, 0xc005400, +0x24061000, 0x1000006d, 0x0, 0x24020002, +0x1000006a, 0xaee204b8, 0x8f830054, 0x24020006, +0xaee204b8, 0x3c010002, 0xac2387e4, 0x10000063, +0x0, 0x8f820220, 0x3c030004, 0x431024, +0x10400003, 0x24020007, 0x1000005c, 0xaee204b8, +0x8f830054, 0x3c020002, 0x8c4287e4, 0x2463d8f0, +0x431023, 0x2c422710, 0x14400003, 0x24020001, +0x3c010002, 0xac228670, 0x3c020002, 0x8c42a8b8, +0x30425000, 0x1040004d, 0x0, 0x8f820220, +0x30428000, 0x10400007, 0x0, 0x8f820220, +0x3c03ffff, 0x34637fff, 0x431024, 0x10000043, +0xaf820220, 0x8f820220, 0x34428000, 0xaf820220, +0x1000003e, 0x0, 0x3c050002, 0x8ca5866c, +0xc0058d7, 0x2021, 0xc005b59, 0x2021, +0x3c020002, 0x8c42a8b0, 0x4410032, 0x24020001, +0x8f820214, 0x3c03ffff, 0x431024, 0x3442241f, +0xaf820214, 0x24020008, 0xaee204b8, 0x8f820220, +0x34420002, 0xaf820220, 0x8f820220, 0x3c030004, +0x431024, 0x14400016, 0x0, 0x3c020002, +0x8c42a8b8, 0x30425000, 0x1040000d, 0x0, +0x8f820220, 0x30428000, 0x10400006, 0x0, +0x8f820220, 0x3c03ffff, 0x34637fff, 0x10000003, +0x431024, 0x8f820220, 0x34428000, 0xaf820220, +0x8f820220, 0x3c03f700, 0x431025, 0xaf820220, +0x3c020002, 0x944287e2, 0x24429fbc, 0x2c420004, +0x10400004, 0x24040018, 0x24050002, 0xc005400, +0x24060020, 0xc0043c9, 0x0, 0x10000003, +0x0, 0x3c010002, 0xac228670, 0x8fbf0020, +0x3e00008, 0x27bd0028, 0x8f820200, 0x8f820220, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820200, +0x3c050002, 0x8ca5866c, 0x34420004, 0xaf820200, +0x24020002, 0x10a2004d, 0x2ca20003, 0x10400005, +0x24020001, 0x10a2000a, 0x0, 0x100000b6, +0x0, 0x24020004, 0x10a20075, 0x24020008, +0x10a20089, 0x3c02f0ff, 0x100000af, 0x0, +0x8f830050, 0x3c02f0ff, 0x3442ffff, 0x3c040002, +0x8c8487fc, 0x621824, 0x3c020700, 0x621825, +0x24020e00, 0x2484fffb, 0x2c840002, 0xaf830050, +0xaf850200, 0xaf850220, 0xaf820238, 0x14800006, +0x0, 0x8f820044, 0x3c03ffff, 0x34633f7f, +0x431024, 0xaf820044, 0x3c030002, 0x8c6387fc, +0x24020005, 0x14620004, 0x0, 0x8f820044, +0x34425000, 0xaf820044, 0x3c020002, 0x8c42865c, +0x3c030002, 0x8c6387fc, 0x34420022, 0x2463fffc, +0x2c630002, 0xaf820200, 0x1460000c, 0x0, +0x3c020002, 0x8c428680, 0x3c030002, 0x8c638664, +0x3c040002, 0x8c848660, 0x34428000, 0x621825, +0x641825, 0x1000000a, 0x34620002, 0x3c020002, +0x8c428664, 0x3c030002, 0x8c638680, 0x3c040002, +0x8c848660, 0x431025, 0x441025, 0x34420002, +0xaf820220, 0x1000002f, 0x24020001, 0x24020e01, +0xaf820238, 0x8f830050, 0x3c02f0ff, 0x3442ffff, +0x3c040002, 0x8c8487d8, 0x621824, 0x3c020d00, +0x621825, 0x24020001, 0xaf830050, 0xaf820200, +0xaf820220, 0x10800005, 0x3c033f00, 0x3c020002, +0x8c428654, 0x10000004, 0x34630070, 0x3c020002, +0x8c428654, 0x34630072, 0x431025, 0xaf820200, +0x3c030002, 0x8c638658, 0x3c02f700, 0x621825, +0x3c020002, 0x8c428664, 0x3c040002, 0x8c848680, +0x3c050002, 0x8ca587fc, 0x431025, 0x441025, +0xaf820220, 0x24020005, 0x14a20006, 0x24020001, +0x8f820044, 0x2403afff, 0x431024, 0xaf820044, +0x24020001, 0xaf820238, 0x1000003f, 0x0, +0x8f830050, 0x3c02f0ff, 0x3442ffff, 0x3c040002, +0x8c8487d8, 0x621824, 0x3c020a00, 0x621825, +0x24020001, 0xaf830050, 0xaf820200, 0xaf820220, +0x1080001f, 0x0, 0x3c020002, 0x8c428708, +0x1440001b, 0x3c033f00, 0x3c020002, 0x8c428654, +0x1000001b, 0x346300e0, 0x8f830050, 0x3c040002, +0x8c8487d8, 0x3442ffff, 0x621824, 0xaf830050, +0x1080000f, 0x0, 0x3c020002, 0x8c428708, +0x1440000b, 0x3c043f00, 0x3c030002, 0x8c638654, +0x348400e0, 0x24020001, 0xaf820200, 0xaf820220, +0x641825, 0xaf830200, 0x10000008, 0x3c05f700, +0x3c020002, 0x8c428654, 0x3c033f00, 0x346300e2, +0x431025, 0xaf820200, 0x3c05f700, 0x34a58000, +0x3c030002, 0x8c638658, 0x3c020002, 0x8c428664, +0x3c040002, 0x8c848680, 0x651825, 0x431025, +0x441025, 0xaf820220, 0x3e00008, 0x0, +0x3c030002, 0x8c638688, 0x3c020002, 0x8c42868c, +0x27bdffe0, 0x10620003, 0xafbf0018, 0x3c010002, +0xac23868c, 0x24020002, 0x1062003c, 0x2c620003, +0x10400005, 0x24020001, 0x10620008, 0x24020004, +0x10000055, 0x0, 0x24020009, 0x1062003f, +0x240209e3, 0x10000050, 0x0, 0x3c030002, +0x8c63866c, 0x10620008, 0x24020008, 0x14620010, +0x240209ca, 0x3c0200c8, 0x344201fb, 0xaf820238, +0x1000001e, 0x0, 0x24020e01, 0xaf820238, +0x8f820044, 0x3c03ffff, 0x34633f7f, 0x431024, +0x34420080, 0xaf820044, 0x10000014, 0x0, +0x3c040002, 0x248481c0, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070002, 0x24e781d8, 0xc002d3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x8f830054, +0x24020002, 0x3c010002, 0xac228688, 0x3c010002, +0xac2387e8, 0x10000034, 0x0, 0x8f830054, +0x3c020002, 0x8c4287e8, 0x2463d8f0, 0x431023, +0x2c422710, 0x1440002c, 0x24020009, 0x3c010002, +0xac228688, 0x10000028, 0x0, 0x3c040002, +0x248481c0, 0xafa20010, 0xafa00014, 0x8f860144, +0x3c070002, 0x24e781d8, 0xc002d3b, 0x3405dead, +0x8f82011c, 0x34420002, 0xaf82011c, 0x8f820220, +0x34420004, 0xaf820220, 0x8f820140, 0x3c030001, +0x431025, 0xaf820140, 0x3c040002, 0x248481c0, +0x240209e9, 0xafa20010, 0xafa00014, 0x8f860144, +0x3c070002, 0x24e781d8, 0xc002d3b, 0x3405dead, +0x8f82011c, 0x34420002, 0xaf82011c, 0x8f820220, +0x34420004, 0xaf820220, 0x8f820140, 0x3c030001, +0x431025, 0xaf820140, 0x8fbf0018, 0x3e00008, +0x27bd0020, 0x27bdffd8, 0xafb20018, 0x809021, +0xafb3001c, 0xa09821, 0xafb10014, 0xc08821, +0xafb00010, 0x8021, 0xafbf0020, 0xa6200000, +0xc00539d, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc00539d, 0x2021, +0xc00539d, 0x24040001, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0x24100010, 0x2501024, +0x10400002, 0x2021, 0x24040001, 0xc00539d, +0x108042, 0x1600fffa, 0x2501024, 0x24100010, +0x2701024, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fffa, 0x2701024, +0xc0053de, 0x34108000, 0xc0053de, 0x0, +0xc00537d, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc0053de, 0x0, +0x8fbf0020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, +0x8fb00010, 0x3e00008, 0x27bd0028, 0x27bdffd8, +0xafb10014, 0x808821, 0xafb20018, 0xa09021, +0xafb3001c, 0xc09821, 0xafb00010, 0x8021, +0xafbf0020, 0xc00539d, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc00539d, +0x2021, 0xc00539d, 0x24040001, 0xc00539d, +0x2021, 0xc00539d, 0x24040001, 0x24100010, +0x2301024, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fffa, 0x2301024, +0x24100010, 0x2501024, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fffa, +0x2501024, 0xc00539d, 0x24040001, 0xc00539d, +0x2021, 0x34108000, 0x96620000, 0x501024, +0x10400002, 0x2021, 0x24040001, 0xc00539d, +0x108042, 0x1600fff8, 0x0, 0xc0053de, +0x0, 0x8fbf0020, 0x8fb3001c, 0x8fb20018, +0x8fb10014, 0x8fb00010, 0x3e00008, 0x27bd0028, +0x3c040002, 0x8c84869c, 0x3c020002, 0x8c4286e4, +0x27bdffd8, 0xafbf0020, 0xafb1001c, 0x10820003, +0xafb00018, 0x3c010002, 0xac2486e4, 0x3c030002, +0x8c6387fc, 0x24020005, 0x14620005, 0x2483ffff, +0xc004f82, 0x0, 0x1000034d, 0x0, +0x2c620013, 0x1040034a, 0x31080, 0x3c010002, +0x220821, 0x8c2282a8, 0x400008, 0x0, +0xc0053de, 0x8021, 0x34028000, 0xa7a20010, +0x27b10010, 0xc00539d, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc00539d, +0x2021, 0xc00539d, 0x24040001, 0xc00539d, +0x2021, 0xc00539d, 0x24040001, 0x24100010, +0x32020001, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fffa, 0x32020001, +0x24100010, 0xc00539d, 0x2021, 0x108042, +0x1600fffc, 0x0, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0x34108000, 0x96220000, +0x501024, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fff8, 0x0, +0xc0053de, 0x0, 0x1000030f, 0x24020002, +0x27b10010, 0xa7a00010, 0x8021, 0xc00539d, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc00539d, 0x2021, 0xc00539d, +0x24040001, 0xc00539d, 0x24040001, 0xc00539d, +0x2021, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc00539d, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0xc00539d, +0x2021, 0x108042, 0x1600fffc, 0x0, +0xc0053de, 0x34108000, 0xc0053de, 0x0, +0xc00537d, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc0053de, 0x0, +0x97a20010, 0x30428000, 0x144002dd, 0x24020003, +0x100002d9, 0x0, 0x24021200, 0xa7a20010, +0x27b10010, 0x8021, 0xc00539d, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0xc00539d, 0x2021, +0x108042, 0x1600fffc, 0x0, 0xc00539d, +0x24040001, 0xc00539d, 0x2021, 0x34108000, +0x96220000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fff8, +0x0, 0xc0053de, 0x0, 0x8f830054, +0x10000296, 0x24020004, 0x8f830054, 0x3c020002, +0x8c4287f8, 0x2463ff9c, 0x431023, 0x2c420064, +0x1440029f, 0x24020002, 0x3c030002, 0x8c6387fc, +0x10620298, 0x2c620003, 0x14400297, 0x24020011, +0x24020003, 0x10620005, 0x24020004, 0x10620292, +0x2402000f, 0x10000290, 0x24020011, 0x1000028e, +0x24020005, 0x24020014, 0xa7a20010, 0x27b10010, +0x8021, 0xc00539d, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc00539d, +0x2021, 0xc00539d, 0x24040001, 0xc00539d, +0x2021, 0xc00539d, 0x24040001, 0x24100010, +0x32020001, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fffa, 0x32020001, +0x24100010, 0x32020012, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fffa, +0x32020012, 0xc00539d, 0x24040001, 0xc00539d, +0x2021, 0x34108000, 0x96220000, 0x501024, +0x10400002, 0x2021, 0x24040001, 0xc00539d, +0x108042, 0x1600fff8, 0x0, 0xc0053de, +0x0, 0x8f830054, 0x10000248, 0x24020006, +0x8f830054, 0x3c020002, 0x8c4287f8, 0x2463ff9c, +0x431023, 0x2c420064, 0x14400251, 0x24020007, +0x1000024d, 0x0, 0x24020006, 0xa7a20010, +0x27b10010, 0x8021, 0xc00539d, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0x32020013, 0x10400002, +0x2021, 0x24040001, 0xc00539d, 0x108042, +0x1600fffa, 0x32020013, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0x34108000, 0x96220000, +0x501024, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fff8, 0x0, +0xc0053de, 0x0, 0x8f830054, 0x10000207, +0x24020008, 0x8f830054, 0x3c020002, 0x8c4287f8, +0x2463ff9c, 0x431023, 0x2c420064, 0x14400210, +0x24020009, 0x1000020c, 0x0, 0x27b10010, +0xa7a00010, 0x8021, 0xc00539d, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0xc00539d, 0x24040001, 0xc00539d, 0x2021, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0x32020018, 0x10400002, +0x2021, 0x24040001, 0xc00539d, 0x108042, +0x1600fffa, 0x32020018, 0xc0053de, 0x34108000, +0xc0053de, 0x0, 0xc00537d, 0x0, +0x50400005, 0x108042, 0x96220000, 0x501025, +0xa6220000, 0x108042, 0x1600fff7, 0x0, +0xc0053de, 0x8021, 0x97a20010, 0x27b10010, +0x34420001, 0xa7a20010, 0xc00539d, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0x32020018, 0x10400002, +0x2021, 0x24040001, 0xc00539d, 0x108042, +0x1600fffa, 0x32020018, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0x34108000, 0x96220000, +0x501024, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fff8, 0x0, +0xc0053de, 0x0, 0x8f830054, 0x10000193, +0x2402000a, 0x8f830054, 0x3c020002, 0x8c4287f8, +0x2463ff9c, 0x431023, 0x2c420064, 0x1440019c, +0x2402000b, 0x10000198, 0x0, 0x27b10010, +0xa7a00010, 0x8021, 0xc00539d, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0xc00539d, 0x24040001, 0xc00539d, 0x2021, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0x32020017, 0x10400002, +0x2021, 0x24040001, 0xc00539d, 0x108042, +0x1600fffa, 0x32020017, 0xc0053de, 0x34108000, +0xc0053de, 0x0, 0xc00537d, 0x0, +0x50400005, 0x108042, 0x96220000, 0x501025, +0xa6220000, 0x108042, 0x1600fff7, 0x0, +0xc0053de, 0x8021, 0x97a20010, 0x27b10010, +0x34420700, 0xa7a20010, 0xc00539d, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0x32020017, 0x10400002, +0x2021, 0x24040001, 0xc00539d, 0x108042, +0x1600fffa, 0x32020017, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0x34108000, 0x96220000, +0x501024, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fff8, 0x0, +0xc0053de, 0x0, 0x8f830054, 0x1000011f, +0x2402000c, 0x8f830054, 0x3c020002, 0x8c4287f8, +0x2463ff9c, 0x431023, 0x2c420064, 0x14400128, +0x24020012, 0x10000124, 0x0, 0x27b10010, +0xa7a00010, 0x8021, 0xc00539d, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0xc00539d, 0x24040001, 0xc00539d, 0x2021, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0x32020014, 0x10400002, +0x2021, 0x24040001, 0xc00539d, 0x108042, +0x1600fffa, 0x32020014, 0xc0053de, 0x34108000, +0xc0053de, 0x0, 0xc00537d, 0x0, +0x50400005, 0x108042, 0x96220000, 0x501025, +0xa6220000, 0x108042, 0x1600fff7, 0x0, +0xc0053de, 0x8021, 0x97a20010, 0x27b10010, +0x34420010, 0xa7a20010, 0xc00539d, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0x32020014, 0x10400002, +0x2021, 0x24040001, 0xc00539d, 0x108042, +0x1600fffa, 0x32020014, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0x34108000, 0x96220000, +0x501024, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fff8, 0x0, +0xc0053de, 0x0, 0x8f830054, 0x100000ab, +0x24020013, 0x8f830054, 0x3c020002, 0x8c4287f8, +0x2463ff9c, 0x431023, 0x2c420064, 0x144000b4, +0x2402000d, 0x100000b0, 0x0, 0x27b10010, +0xa7a00010, 0x8021, 0xc00539d, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0xc00539d, 0x24040001, 0xc00539d, 0x2021, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0x32020018, 0x10400002, +0x2021, 0x24040001, 0xc00539d, 0x108042, +0x1600fffa, 0x32020018, 0xc0053de, 0x34108000, +0xc0053de, 0x0, 0xc00537d, 0x0, +0x50400005, 0x108042, 0x96220000, 0x501025, +0xa6220000, 0x108042, 0x1600fff7, 0x0, +0xc0053de, 0x8021, 0x97a20010, 0x27b10010, +0x3042fffe, 0xa7a20010, 0xc00539d, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0x32020018, 0x10400002, +0x2021, 0x24040001, 0xc00539d, 0x108042, +0x1600fffa, 0x32020018, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0x34108000, 0x96220000, +0x501024, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fff8, 0x0, +0xc0053de, 0x0, 0x8f830054, 0x10000037, +0x2402000e, 0x24020840, 0xa7a20010, 0x27b10010, +0x8021, 0xc00539d, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc00539d, +0x2021, 0xc00539d, 0x24040001, 0xc00539d, +0x2021, 0xc00539d, 0x24040001, 0x24100010, +0x32020001, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fffa, 0x32020001, +0x24100010, 0x32020013, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fffa, +0x32020013, 0xc00539d, 0x24040001, 0xc00539d, +0x2021, 0x34108000, 0x96220000, 0x501024, +0x10400002, 0x2021, 0x24040001, 0xc00539d, +0x108042, 0x1600fff8, 0x0, 0xc0053de, +0x0, 0x8f830054, 0x24020010, 0x3c010002, +0xac22869c, 0x3c010002, 0xac2387f8, 0x1000000c, +0x0, 0x8f830054, 0x3c020002, 0x8c4287f8, +0x2463ff9c, 0x431023, 0x2c420064, 0x14400004, +0x0, 0x24020011, 0x3c010002, 0xac22869c, +0x8fbf0020, 0x8fb1001c, 0x8fb00018, 0x3e00008, +0x27bd0028, 0x3c030002, 0x8c63866c, 0x27bdffc8, +0x24020002, 0xafbf0034, 0xafb20030, 0xafb1002c, +0x14620005, 0xafb00028, 0x3c120002, 0x8e52a8b8, +0x10000003, 0x0, 0x3c120002, 0x8e52a8bc, +0x3c030002, 0x8c6386a0, 0x3c020002, 0x8c4286e8, +0x50620004, 0x2463ffff, 0x3c010002, 0xac2386e8, +0x2463ffff, 0x2c620006, 0x1040037c, 0x31080, +0x3c010002, 0x220821, 0x8c228300, 0x400008, +0x0, 0x2021, 0x2821, 0xc005400, +0x34068000, 0x24040010, 0x24050002, 0x24060002, +0x24020002, 0xc005400, 0xa7a20018, 0x24020002, +0x3c010002, 0xac2286a0, 0x10000368, 0x0, +0x27b10018, 0xa7a00018, 0x8021, 0xc00539d, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc00539d, 0x2021, 0xc00539d, +0x24040001, 0xc00539d, 0x24040001, 0xc00539d, +0x2021, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc00539d, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0xc00539d, +0x2021, 0x108042, 0x1600fffc, 0x0, +0xc0053de, 0x34108000, 0xc0053de, 0x0, +0xc00537d, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc0053de, 0x0, +0x97a20018, 0x30428000, 0x14400004, 0x24020003, +0x3c010002, 0xac2286a0, 0x24020003, 0x3c010002, +0xac2286a0, 0x1000032d, 0x0, 0x24040010, +0x24050002, 0x24060002, 0x24020002, 0xc005400, +0xa7a20018, 0x3c030002, 0x8c6386ec, 0x24020001, +0x146201e1, 0x8021, 0x27b10018, 0xa7a00018, +0xc00539d, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc00539d, 0x2021, +0xc00539d, 0x24040001, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc00539d, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fffa, 0x32020018, +0xc0053de, 0x34108000, 0xc0053de, 0x0, +0xc00537d, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc0053de, 0x8021, +0x27b10018, 0xa7a00018, 0xc00539d, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0xc00539d, 0x24040001, 0xc00539d, 0x2021, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0x32020018, 0x10400002, +0x2021, 0x24040001, 0xc00539d, 0x108042, +0x1600fffa, 0x32020018, 0xc0053de, 0x34108000, +0xc0053de, 0x0, 0xc00537d, 0x0, +0x50400005, 0x108042, 0x96220000, 0x501025, +0xa6220000, 0x108042, 0x1600fff7, 0x0, +0xc0053de, 0x8021, 0x24040018, 0x2821, +0xc005400, 0x24060404, 0xa7a0001a, 0xc00539d, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc00539d, 0x2021, 0xc00539d, +0x24040001, 0xc00539d, 0x24040001, 0xc00539d, +0x2021, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc00539d, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x32020018, +0x10400002, 0x2021, 0x24040001, 0xc00539d, +0x108042, 0x1600fffa, 0x32020018, 0xc0053de, +0x34108000, 0xc0053de, 0x0, 0xc00537d, +0x0, 0x50400005, 0x108042, 0x97a2001a, +0x501025, 0xa7a2001a, 0x108042, 0x1600fff7, +0x0, 0xc0053de, 0x8021, 0xa7a0001a, +0xc00539d, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc00539d, 0x2021, +0xc00539d, 0x24040001, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc00539d, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fffa, 0x32020018, +0xc0053de, 0x34108000, 0xc0053de, 0x0, +0xc00537d, 0x0, 0x50400005, 0x108042, +0x97a2001a, 0x501025, 0xa7a2001a, 0x108042, +0x1600fff7, 0x0, 0xc0053de, 0x8021, +0xa7a0001c, 0xc00539d, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc00539d, +0x2021, 0xc00539d, 0x24040001, 0xc00539d, +0x24040001, 0xc00539d, 0x2021, 0x24100010, +0xc00539d, 0x2021, 0x108042, 0x1600fffc, +0x0, 0x24100010, 0x3202001e, 0x10400002, +0x2021, 0x24040001, 0xc00539d, 0x108042, +0x1600fffa, 0x3202001e, 0xc0053de, 0x34108000, +0xc0053de, 0x0, 0xc00537d, 0x0, +0x50400005, 0x108042, 0x97a2001c, 0x501025, +0xa7a2001c, 0x108042, 0x1600fff7, 0x0, +0xc0053de, 0x8021, 0xa7a0001c, 0xc00539d, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc00539d, 0x2021, 0xc00539d, +0x24040001, 0xc00539d, 0x24040001, 0xc00539d, +0x2021, 0x24100010, 0xc00539d, 0x2021, +0x108042, 0x1600fffc, 0x0, 0x24100010, +0x3202001e, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fffa, 0x3202001e, +0xc0053de, 0x34108000, 0xc0053de, 0x0, +0xc00537d, 0x0, 0x50400005, 0x108042, +0x97a2001c, 0x501025, 0xa7a2001c, 0x108042, +0x1600fff7, 0x0, 0xc0053de, 0x8021, +0x24020002, 0xa7a2001e, 0xc00539d, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0x24100010, 0xc00539d, 0x2021, 0x108042, +0x1600fffc, 0x0, 0x24100010, 0x3202001e, +0x10400002, 0x2021, 0x24040001, 0xc00539d, +0x108042, 0x1600fffa, 0x3202001e, 0xc00539d, +0x24040001, 0xc00539d, 0x2021, 0x34108000, +0x97a2001e, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fff8, +0x0, 0xc0053de, 0x8021, 0xa7a00020, +0xc00539d, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc00539d, 0x2021, +0xc00539d, 0x24040001, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0x24100010, 0xc00539d, +0x2021, 0x108042, 0x1600fffc, 0x0, +0x24100010, 0x3202001e, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fffa, +0x3202001e, 0xc0053de, 0x34108000, 0xc0053de, +0x0, 0xc00537d, 0x0, 0x50400005, +0x108042, 0x97a20020, 0x501025, 0xa7a20020, +0x108042, 0x1600fff7, 0x0, 0xc0053de, +0x8021, 0xa7a00020, 0xc00539d, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0xc00539d, 0x24040001, 0xc00539d, 0x2021, +0x24100010, 0xc00539d, 0x2021, 0x108042, +0x1600fffc, 0x0, 0x24100010, 0x3202001e, +0x10400002, 0x2021, 0x24040001, 0xc00539d, +0x108042, 0x1600fffa, 0x3202001e, 0xc0053de, +0x34108000, 0xc0053de, 0x0, 0xc00537d, +0x0, 0x50400005, 0x108042, 0x97a20020, +0x501025, 0xa7a20020, 0x108042, 0x1600fff7, +0x0, 0xc0053de, 0x8021, 0xa7a00022, +0xc00539d, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc00539d, 0x2021, +0xc00539d, 0x24040001, 0xc00539d, 0x2021, +0xc00539d, 0x24040001, 0x24100010, 0xc00539d, +0x2021, 0x108042, 0x1600fffc, 0x0, +0x24100010, 0xc00539d, 0x2021, 0x108042, +0x1600fffc, 0x0, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0x34108000, 0x97a20022, +0x501024, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fff8, 0x0, +0xc0053de, 0x0, 0x24040018, 0x24050002, +0xc005400, 0x24060004, 0x3c100002, 0x8e1086f0, +0x24020001, 0x1602011e, 0x0, 0x3c020002, +0x944287e2, 0x3c010002, 0xac2086f0, 0x24429fbc, +0x2c420004, 0x1040000c, 0x24040009, 0x24050001, +0xc005400, 0x24060400, 0x24040018, 0x24050001, +0xc005400, 0x24060020, 0x24040018, 0x24050001, +0xc005400, 0x24062000, 0x3c024000, 0x2421024, +0x10400126, 0x3c022000, 0x2421024, 0x10400005, +0x0, 0x3c010002, 0xac3087d8, 0x10000003, +0x0, 0x3c010002, 0xac2087d8, 0x3c030002, +0x8c6387f0, 0x24020005, 0x146200f9, 0x0, +0x3c020002, 0x8c4287d8, 0x10400067, 0x3c020004, +0x2421024, 0x10400011, 0xa7a00018, 0x3c020008, +0x2421024, 0x10400002, 0x24020200, 0xa7a20018, +0x3c020010, 0x2421024, 0x10400004, 0x0, +0x97a20018, 0x34420100, 0xa7a20018, 0x97a60018, +0x24040009, 0x10000004, 0x2821, 0x24040009, +0x2821, 0x3021, 0xc005400, 0x0, +0x24020001, 0xa7a2001a, 0x3c020008, 0x2421024, +0x1040000c, 0x3c020002, 0x2421024, 0x10400002, +0x24020101, 0xa7a2001a, 0x3c020001, 0x2421024, +0x10400005, 0x3c020010, 0x97a2001a, 0x34420040, +0xa7a2001a, 0x3c020010, 0x2421024, 0x1040000e, +0x3c020002, 0x2421024, 0x10400005, 0x3c020001, +0x97a2001a, 0x34420080, 0xa7a2001a, 0x3c020001, +0x2421024, 0x10400005, 0x3c0300a0, 0x97a2001a, +0x34420020, 0xa7a2001a, 0x3c0300a0, 0x2431024, +0x54430004, 0x3c020020, 0x97a2001a, 0x1000000c, +0x34420400, 0x2421024, 0x50400004, 0x3c020080, +0x97a2001a, 0x10000006, 0x34420800, 0x2421024, +0x10400004, 0x0, 0x97a2001a, 0x34420c00, +0xa7a2001a, 0x97a6001a, 0x24040004, 0xc005400, +0x2821, 0x3c020004, 0x2421024, 0x10400004, +0xa7a0001c, 0x32425000, 0x14400004, 0x0, +0x32424000, 0x10400005, 0x2021, 0xc00531e, +0x2402021, 0x10000096, 0x0, 0x97a6001c, +0x2821, 0x34c61200, 0xc005400, 0xa7a6001c, +0x1000008f, 0x0, 0x2421024, 0x10400004, +0xa7a00018, 0x32425000, 0x14400004, 0x0, +0x32424000, 0x10400005, 0x3c020010, 0xc00531e, +0x2402021, 0x10000019, 0xa7a0001a, 0x2421024, +0x10400004, 0x0, 0x97a20018, 0x10000004, +0xa7a20018, 0x97a20018, 0x34420100, 0xa7a20018, +0x3c020001, 0x2421024, 0x10400004, 0x0, +0x97a20018, 0x10000004, 0xa7a20018, 0x97a20018, +0x34422000, 0xa7a20018, 0x97a60018, 0x2021, +0xc005400, 0x2821, 0xa7a0001a, 0x8021, +0xc00539d, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc00539d, 0x2021, +0xc00539d, 0x24040001, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc00539d, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0xc00539d, 0x2021, 0x108042, 0x1600fffc, +0x0, 0xc0053de, 0x34108000, 0xc0053de, +0x0, 0xc00537d, 0x0, 0x50400005, +0x108042, 0x97a2001a, 0x501025, 0xa7a2001a, +0x108042, 0x1600fff7, 0x0, 0xc0053de, +0x8021, 0xa7a0001a, 0xc00539d, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc00539d, 0x2021, 0xc00539d, 0x24040001, +0xc00539d, 0x24040001, 0xc00539d, 0x2021, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc00539d, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0xc00539d, 0x2021, +0x108042, 0x1600fffc, 0x0, 0xc0053de, +0x34108000, 0xc0053de, 0x0, 0xc00537d, +0x0, 0x50400005, 0x108042, 0x97a2001a, +0x501025, 0xa7a2001a, 0x108042, 0x1600fff7, +0x0, 0xc0053de, 0x0, 0x3c040002, +0x248482f4, 0x97a60018, 0x97a7001a, 0x3c020002, +0x8c42866c, 0x3c030002, 0x8c6387d8, 0x3c05000d, +0x34a50205, 0xafa20010, 0xc002d3b, 0xafa30014, +0x8f830054, 0x24020004, 0x3c010002, 0xac2286a0, +0x3c010002, 0xac2387f4, 0x10000018, 0x0, +0x8f830054, 0x3c020002, 0x8c4287f4, 0x2463ff9c, +0x431023, 0x2c420064, 0x14400010, 0x0, +0x8f820220, 0x24030005, 0x3c010002, 0xac2386a0, +0x3c03f700, 0x431025, 0xaf820220, 0x10000007, +0x0, 0x24020006, 0x3c010002, 0xac2286a0, +0x24020011, 0x3c010002, 0xac22869c, 0x8fbf0034, +0x8fb20030, 0x8fb1002c, 0x8fb00028, 0x3e00008, +0x27bd0038, 0x27bdffd8, 0xafb00018, 0x808021, +0xafb1001c, 0x8821, 0x32024000, 0x10400013, +0xafbf0020, 0x3c020010, 0x2021024, 0x2c420001, +0x21023, 0x30434100, 0x3c020001, 0x2021024, +0x14400006, 0x34714000, 0x3c020002, 0x2021024, +0x14400002, 0x34716000, 0x34714040, 0x2021, +0x2821, 0x10000036, 0x2203021, 0x32021000, +0x10400035, 0x2021, 0x2821, 0xc005400, +0x24060040, 0x24040018, 0x2821, 0xc005400, +0x24060c00, 0x24040017, 0x2821, 0xc005400, +0x24060400, 0x24040016, 0x2821, 0xc005400, +0x24060006, 0x24040017, 0x2821, 0xc005400, +0x24062500, 0x24040016, 0x2821, 0xc005400, +0x24060006, 0x24040017, 0x2821, 0xc005400, +0x24064600, 0x24040016, 0x2821, 0xc005400, +0x24060006, 0x24040017, 0x2821, 0xc005400, +0x24066700, 0x24040016, 0x2821, 0xc005400, +0x24060006, 0x2404001f, 0x2821, 0xc005400, +0x24060010, 0x24040009, 0x2821, 0xc005400, +0x24061500, 0x24040009, 0x2821, 0x24061d00, +0xc005400, 0x0, 0x3c040002, 0x24848318, +0x3c05000e, 0x34a50100, 0x2003021, 0x2203821, +0xafa00010, 0xc002d3b, 0xafa00014, 0x8fbf0020, +0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0028, +0x8f850044, 0x8f820044, 0x3c030001, 0x431025, +0x3c030008, 0xaf820044, 0x8f840054, 0x8f820054, +0xa32824, 0x10000002, 0x24840001, 0x8f820054, +0x821023, 0x2c420002, 0x1440fffc, 0x0, +0x8f820044, 0x3c03fffe, 0x3463ffff, 0x431024, +0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, +0x24630001, 0x8f820054, 0x621023, 0x2c420002, +0x1440fffc, 0x0, 0x3e00008, 0xa01021, +0x8f830044, 0x3c02fff0, 0x3442ffff, 0x42480, +0x621824, 0x3c020002, 0x822025, 0x641825, +0xaf830044, 0x8f820044, 0x3c03fffe, 0x3463ffff, +0x431024, 0xaf820044, 0x8f830054, 0x8f820054, +0x10000002, 0x24630001, 0x8f820054, 0x621023, +0x2c420002, 0x1440fffc, 0x0, 0x8f820044, +0x3c030001, 0x431025, 0xaf820044, 0x8f830054, +0x8f820054, 0x10000002, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x3e00008, 0x0, 0x8f820044, 0x2403ff7f, +0x431024, 0xaf820044, 0x8f830054, 0x8f820054, +0x10000002, 0x24630001, 0x8f820054, 0x621023, +0x2c420002, 0x1440fffc, 0x0, 0x8f820044, +0x34420080, 0xaf820044, 0x8f830054, 0x8f820054, +0x10000002, 0x24630001, 0x8f820054, 0x621023, +0x2c420002, 0x1440fffc, 0x0, 0x3e00008, +0x0, 0x8f820044, 0x3c03fff0, 0x3463ffff, +0x431024, 0xaf820044, 0x8f820044, 0x3c030001, +0x431025, 0xaf820044, 0x8f830054, 0x8f820054, +0x10000002, 0x24630001, 0x8f820054, 0x621023, +0x2c420002, 0x1440fffc, 0x0, 0x8f820044, +0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820044, +0x8f830054, 0x8f820054, 0x10000002, 0x24630001, +0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, +0x0, 0x3e00008, 0x0, 0x27bdffc8, +0xafb30024, 0x809821, 0xafbe002c, 0xa0f021, +0xafb20020, 0xc09021, 0x33c2ffff, 0xafbf0030, +0xafb50028, 0xafb1001c, 0xafb00018, 0x14400034, +0xa7b20010, 0x3271ffff, 0x27b20010, 0x8021, +0xc00539d, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc00539d, 0x2021, +0xc00539d, 0x24040001, 0xc00539d, 0x2021, +0xc00539d, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc00539d, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x2301024, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fffa, 0x2301024, +0xc00539d, 0x24040001, 0xc00539d, 0x2021, +0x34108000, 0x96420000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc00539d, 0x108042, +0x12000075, 0x0, 0x1000fff6, 0x0, +0x3275ffff, 0x27b10010, 0xa7a00010, 0x8021, +0xc00539d, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc00539d, 0x2021, +0xc00539d, 0x24040001, 0xc00539d, 0x24040001, +0xc00539d, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc00539d, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x2b01024, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fffa, 0x2b01024, +0xc0053de, 0x34108000, 0xc0053de, 0x0, +0xc00537d, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc0053de, 0x0, +0x33c5ffff, 0x24020001, 0x54a20004, 0x24020002, +0x97a20010, 0x10000006, 0x521025, 0x14a20006, +0x3271ffff, 0x97a20010, 0x121827, 0x431024, +0xa7a20010, 0x3271ffff, 0x27b20010, 0x8021, +0xc00539d, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc00539d, 0x2021, +0xc00539d, 0x24040001, 0xc00539d, 0x2021, +0xc00539d, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc00539d, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x2301024, 0x10400002, 0x2021, 0x24040001, +0xc00539d, 0x108042, 0x1600fffa, 0x2301024, +0xc00539d, 0x24040001, 0xc00539d, 0x2021, +0x34108000, 0x96420000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc00539d, 0x108042, +0x1600fff8, 0x0, 0xc0053de, 0x0, +0x8fbf0030, 0x8fbe002c, 0x8fb50028, 0x8fb30024, +0x8fb20020, 0x8fb1001c, 0x8fb00018, 0x3e00008, +0x27bd0038, 0x27bdffe8, 0xafbf0010, 0x8ee304b8, +0x24020008, 0x146201f1, 0x0, 0x3c020002, +0x8c4287d8, 0x14400006, 0x0, 0x8f840224, +0xc00430b, 0x0, 0x100001e8, 0x0, +0x8f820220, 0x3c030008, 0x431024, 0x10400029, +0x24020001, 0x8f840224, 0x8f820220, 0x3c030400, +0x431024, 0x10400007, 0x0, 0x3c010002, +0xac20a860, 0x3c010002, 0xac20a880, 0x1000000b, +0x0, 0x3c030002, 0x2463a860, 0x8c620000, +0x24420001, 0xac620000, 0x2c420002, 0x14400003, +0x24020001, 0x3c010002, 0xac22a880, 0x3c020002, +0x8c42a880, 0x10400007, 0x30820040, 0x10400005, +0x24020001, 0x3c010002, 0xac22a884, 0x10000003, +0x0, 0x3c010002, 0xac20a884, 0x3c010002, +0xac24a85c, 0x3c010002, 0xac20a890, 0x1000000b, +0x0, 0x3c010002, 0xac22a890, 0x3c010002, +0xac20a880, 0x3c010002, 0xac20a860, 0x3c010002, +0xac20a884, 0x3c010002, 0xac20a85c, 0x3c030002, +0x8c63a850, 0x3c020002, 0x8c42a854, 0x50620004, +0x2463ffff, 0x3c010002, 0xac23a854, 0x2463ffff, +0x2c62000e, 0x104001a1, 0x31080, 0x3c010002, +0x220821, 0x8c228338, 0x400008, 0x0, +0x24020002, 0x3c010002, 0xac20a880, 0x3c010002, +0xac20a860, 0x3c010002, 0xac20a85c, 0x3c010002, +0xac20a884, 0x3c010002, 0xac20a878, 0x3c010002, +0xac20a870, 0xaf800224, 0x3c010002, 0xac22a850, +0x3c020002, 0x8c42a890, 0x14400053, 0x3c02fdff, +0x3442ffff, 0xc00430b, 0x282a024, 0xaf800204, +0x8f820200, 0x2403fffd, 0x431024, 0xaf820200, +0x3c010002, 0xac20a8a0, 0x8f830054, 0x3c020002, +0x8c42a878, 0x24040001, 0x3c010002, 0xac24a88c, +0x24420001, 0x3c010002, 0xac22a878, 0x2c420004, +0x3c010002, 0xac23a874, 0x14400007, 0x24020003, +0x3c010002, 0xac248670, 0x3c010002, 0xac20a878, +0x1000016a, 0x0, 0x3c010002, 0xac22a850, +0x10000166, 0x0, 0x8f830054, 0x3c020002, +0x8c42a874, 0x2463d8f0, 0x431023, 0x2c422710, +0x14400003, 0x24020004, 0x3c010002, 0xac22a850, +0x3c020002, 0x8c42a890, 0x14400023, 0x3c02fdff, +0x3442ffff, 0x10000155, 0x282a024, 0x3c040002, +0x8c8487dc, 0x3c010002, 0xac20a868, 0xc0056b7, +0x0, 0x3c020002, 0x8c42a89c, 0xaf820204, +0x3c020002, 0x8c42a890, 0x14400013, 0x3c03fdff, +0x8f820204, 0x3463ffff, 0x30420030, 0x14400138, +0x283a024, 0x3c030002, 0x8c63a89c, 0x24020005, +0x3c010002, 0xac22a850, 0x3c010002, 0xac23a8a0, +0x1000013a, 0x0, 0x3c020002, 0x8c42a890, +0x10400011, 0x3c02fdff, 0x3c020002, 0x8c428700, +0x24420001, 0x3c010002, 0xac228700, 0x2c420002, +0x1440012e, 0x24020001, 0x3c010002, 0xac228708, +0x3c010002, 0xac208700, 0x3c010002, 0xac228670, +0x10000126, 0x0, 0x3c030002, 0x8c63a880, +0x3442ffff, 0x10600121, 0x282a024, 0x3c020002, +0x8c42a85c, 0x1040011d, 0x0, 0x3c010002, +0xac22a888, 0x24020003, 0x3c010002, 0xac22a860, +0x100000bd, 0x24020006, 0x3c010002, 0xac20a868, +0x8f820204, 0x34420040, 0xaf820204, 0x3c020002, +0x8c42a8a0, 0x24030007, 0x3c010002, 0xac23a850, +0x34420040, 0x3c010002, 0xac22a8a0, 0x3c020002, +0x8c42a880, 0x10400005, 0x0, 0x3c020002, +0x8c42a85c, 0x104000f7, 0x24020002, 0x3c050002, +0x24a5a860, 0x8ca20000, 0x2c424e21, 0x104000f1, +0x24020002, 0x3c020002, 0x8c42a884, 0x104000f7, +0x2404ffbf, 0x3c020002, 0x8c42a85c, 0x3c030002, +0x8c63a888, 0x441024, 0x641824, 0x10430005, +0x24020001, 0x3c010002, 0xac22a850, 0x100000eb, +0x0, 0x24020003, 0xaca20000, 0x24020008, +0x3c010002, 0xac22a850, 0x3c020002, 0x8c42a88c, +0x1040000d, 0x24020001, 0x3c040002, 0x8c84a85c, +0xc0056c4, 0x0, 0x3c020002, 0x8c42a8a8, +0x14400005, 0x24020001, 0x3c020002, 0x8c42a8a4, +0x10400007, 0x24020001, 0x3c010002, 0xac228670, +0x3c010002, 0xac20a878, 0x100000d0, 0x0, +0x3c020002, 0x8c42a870, 0x3c030002, 0x8c63a85c, +0x2c420001, 0x210c0, 0x30630008, 0x3c010002, +0xac22a870, 0x3c010002, 0xac23a86c, 0x8f830054, +0x24020009, 0x3c010002, 0xac22a850, 0x3c010002, +0xac23a874, 0x100000bd, 0x0, 0x8f830054, +0x3c020002, 0x8c42a874, 0x2463d8f0, 0x431023, +0x2c422710, 0x144000a2, 0x0, 0x3c020002, +0x8c42a880, 0x10400005, 0x0, 0x3c020002, +0x8c42a85c, 0x104000a3, 0x24020002, 0x3c030002, +0x2463a860, 0x8c620000, 0x2c424e21, 0x1040009d, +0x24020002, 0x3c020002, 0x8c42a88c, 0x1040000e, +0x0, 0x3c020002, 0x8c42a85c, 0x3c010002, +0xac20a88c, 0x30420080, 0x1040002f, 0x2402000c, +0x8f820204, 0x30420080, 0x1440000c, 0x24020003, +0x10000029, 0x2402000c, 0x3c020002, 0x8c42a85c, +0x30420080, 0x14400005, 0x24020003, 0x8f820204, +0x30420080, 0x1040001f, 0x24020003, 0xac620000, +0x2402000a, 0x3c010002, 0xac22a850, 0x3c040002, +0x2484a898, 0x8c820000, 0x3c030002, 0x8c63a870, +0x431025, 0xaf820204, 0x8c830000, 0x3c040002, +0x8c84a870, 0x2402000b, 0x3c010002, 0xac22a850, +0x641825, 0x3c010002, 0xac23a8a0, 0x3c050002, +0x24a5a860, 0x8ca20000, 0x2c424e21, 0x10400069, +0x24020002, 0x3c020002, 0x8c42a890, 0x10400006, +0x0, 0x2402000c, 0x3c010002, 0xac22a850, +0x1000006a, 0x0, 0x3c020002, 0x8c42a880, +0x10400066, 0x0, 0x3c040002, 0x8c84a85c, +0x10800057, 0x30820008, 0x3c030002, 0x8c63a86c, +0x1062005e, 0x24020003, 0x3c010002, 0xac24a888, +0xaca20000, 0x24020006, 0x3c010002, 0xac22a850, +0x10000056, 0x0, 0x8f820200, 0x34420002, +0xaf820200, 0x8f830054, 0x2402000d, 0x3c010002, +0xac22a850, 0x3c010002, 0xac23a874, 0x8f830054, +0x3c020002, 0x8c42a874, 0x2463d8f0, 0x431023, +0x2c422710, 0x14400032, 0x0, 0x3c020002, +0x8c42a890, 0x10400021, 0x2402000e, 0x3c030002, +0x8c63a8a4, 0x3c010002, 0xac22a850, 0x14600015, +0x0, 0xc0043c9, 0x0, 0x3c050002, +0x8ca5866c, 0xc0058d7, 0x2021, 0x3c030002, +0x8c63866c, 0x24020004, 0x14620005, 0x2403fffb, +0x3c020002, 0x8c428668, 0x10000003, 0x2403fff7, +0x3c020002, 0x8c428668, 0x431024, 0x3c010002, +0xac228668, 0x8f830224, 0x3c020200, 0x3c010002, +0xac23a8ac, 0x10000021, 0x282a025, 0x3c020002, +0x8c42a880, 0x10400005, 0x0, 0x3c020002, +0x8c42a85c, 0x1040000f, 0x24020002, 0x3c020002, +0x8c42a860, 0x2c424e21, 0x1040000a, 0x24020002, +0x3c020002, 0x8c42a880, 0x10400010, 0x0, +0x3c020002, 0x8c42a85c, 0x1440000c, 0x0, +0x24020002, 0x3c010002, 0xac22a850, 0x10000007, +0x0, 0x3c020002, 0x8c42a880, 0x10400003, +0x0, 0xc00430b, 0x0, 0x8f820220, +0x3c03f700, 0x431025, 0xaf820220, 0x8fbf0010, +0x3e00008, 0x27bd0018, 0x3c030002, 0x2463a8a8, +0x8c620000, 0x10400005, 0x34422000, 0x3c010002, +0xac22a89c, 0x10000003, 0xac600000, 0x3c010002, +0xac24a89c, 0x3e00008, 0x0, 0x27bdffe0, +0x30820030, 0xafbf0018, 0x3c010002, 0xac22a8a4, +0x1440006b, 0x3c02ffff, 0x34421f0e, 0x821024, +0x14400065, 0x24020030, 0x30822000, 0x10400061, +0x30838000, 0x31a02, 0x30820001, 0x21200, +0x3c040002, 0x8c8487dc, 0x621825, 0x331c2, +0x3c030002, 0x2463870c, 0x30828000, 0x21202, +0x30840001, 0x42200, 0x441025, 0x239c2, +0x61080, 0x431021, 0x471021, 0x90430000, +0x24020001, 0x10620027, 0x0, 0x10600007, +0x24020002, 0x10620014, 0x24020003, 0x1062002f, +0x3c05000f, 0x1000003b, 0x0, 0x8f820200, +0x2403feff, 0x431024, 0xaf820200, 0x8f820220, +0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820220, +0x3c010002, 0xac20a8c4, 0x3c010002, 0xac20a8cc, +0x10000037, 0x0, 0x8f820200, 0x34420100, +0xaf820200, 0x8f820220, 0x3c03fffe, 0x3463ffff, +0x431024, 0xaf820220, 0x24020100, 0x3c010002, +0xac22a8c4, 0x3c010002, 0xac20a8cc, 0x10000028, +0x0, 0x8f820200, 0x2403feff, 0x431024, +0xaf820200, 0x8f820220, 0x3c030001, 0x431025, +0xaf820220, 0x3c010002, 0xac20a8c4, 0x3c010002, +0xac23a8cc, 0x1000001a, 0x0, 0x8f820200, +0x34420100, 0xaf820200, 0x8f820220, 0x3c030001, +0x431025, 0xaf820220, 0x24020100, 0x3c010002, +0xac22a8c4, 0x3c010002, 0xac23a8cc, 0x1000000c, +0x0, 0x34a5ffff, 0x3c040002, 0x24848370, +0xafa30010, 0xc002d3b, 0xafa00014, 0x10000004, +0x0, 0x24020030, 0x3c010002, 0xac22a8a8, +0x8fbf0018, 0x3e00008, 0x27bd0020, 0x27bdffc8, +0xafb20028, 0x809021, 0xafb3002c, 0xa09821, +0xafb00020, 0xc08021, 0x3c040002, 0x24848398, +0x3c050009, 0x3c020002, 0x8c42866c, 0x34a59001, +0x2403021, 0x2603821, 0xafbf0030, 0xafb10024, +0xa7a0001a, 0xafb00014, 0xc002d3b, 0xafa20010, +0x24020002, 0x12620086, 0x2e620003, 0x10400005, +0x24020001, 0x1262000a, 0x0, 0x1000017b, +0x0, 0x24020004, 0x126200fc, 0x24020008, +0x126200fb, 0x3c02ffec, 0x10000174, 0x0, +0x3c020002, 0x8c428668, 0x30420002, 0x14400004, +0x128940, 0x3c02fffb, 0x3442ffff, 0x2028024, +0x3c010002, 0x310821, 0xac30a8bc, 0x3c024000, +0x2021024, 0x10400050, 0x1023c2, 0x30840030, +0x101382, 0x3042001c, 0x3c030002, 0x246386a4, +0x431021, 0x823821, 0x3c020020, 0x2021024, +0x10400006, 0x24020100, 0x3c010002, 0x310821, +0xac22a8c0, 0x10000005, 0x3c020080, 0x3c010002, +0x310821, 0xac20a8c0, 0x3c020080, 0x2021024, +0x10400007, 0x121940, 0x3c020001, 0x3c010002, +0x230821, 0xac22a8c8, 0x10000005, 0x0, +0x121140, 0x3c010002, 0x220821, 0xac20a8c8, +0x94e40000, 0x3c030002, 0x8c6387fc, 0x24020005, +0x10620010, 0xa7a40018, 0x32024000, 0x10400002, +0x34824000, 0xa7a20018, 0x24040001, 0x94e20002, +0x24050004, 0x24e60002, 0x34420001, 0xc004bdc, +0xa4e20002, 0x24040001, 0x2821, 0xc004bdc, +0x27a60018, 0x3c020002, 0x8c42866c, 0x24110001, +0x3c010002, 0xac318678, 0x14530004, 0x32028000, +0xc00430b, 0x0, 0x32028000, 0x10400123, +0x0, 0xc00430b, 0x0, 0x3c030002, +0x8c6387fc, 0x24020005, 0x1062011c, 0x24020002, +0x3c010002, 0xac318670, 0x3c010002, 0xac22866c, +0x10000116, 0x0, 0x24040001, 0x24050004, +0x27b0001a, 0xc004bdc, 0x2003021, 0x24040001, +0x2821, 0xc004bdc, 0x2003021, 0x3c020002, +0x511021, 0x8c42a8b4, 0x3c040002, 0x8c84866c, +0x3c03bfff, 0x3463ffff, 0x3c010002, 0xac338678, +0x431024, 0x3c010002, 0x310821, 0xac22a8b4, +0x109300fc, 0x0, 0x100000fc, 0x0, +0x3c022000, 0x2021024, 0x10400005, 0x24020001, +0x3c010002, 0xac2287d8, 0x10000004, 0x128940, +0x3c010002, 0xac2087d8, 0x128940, 0x3c010002, +0x310821, 0xac30a8b8, 0x3c024000, 0x2021024, +0x14400015, 0x0, 0x3c020002, 0x8c4287d8, +0x10400006, 0x24040004, 0x24050001, 0xc005400, +0x24062000, 0x24020001, 0xaee204b8, 0x3c020002, +0x511021, 0x8c42a8b0, 0x3c03bfff, 0x3463ffff, +0x431024, 0x3c010002, 0x310821, 0xac22a8b0, +0x100000d4, 0x0, 0x3c020002, 0x8c4287d8, +0x10400028, 0x3c0300a0, 0x2031024, 0x5443000d, +0x3c020020, 0x3c020002, 0x8c4287dc, 0x24030100, +0x3c010002, 0x310821, 0xac23a8c4, 0x3c030001, +0x3c010002, 0x310821, 0xac23a8cc, 0x10000015, +0x34420400, 0x2021024, 0x10400008, 0x24030100, +0x3c020002, 0x8c4287dc, 0x3c010002, 0x310821, +0xac23a8c4, 0x1000000b, 0x34420800, 0x3c020080, +0x2021024, 0x1040002e, 0x3c030001, 0x3c020002, +0x8c4287dc, 0x3c010002, 0x310821, 0xac23a8cc, +0x34420c00, 0x3c010002, 0xac2287dc, 0x10000025, +0x24040001, 0x3c020020, 0x2021024, 0x10400006, +0x24020100, 0x3c010002, 0x310821, 0xac22a8c4, +0x10000005, 0x3c020080, 0x3c010002, 0x310821, +0xac20a8c4, 0x3c020080, 0x2021024, 0x10400007, +0x121940, 0x3c020001, 0x3c010002, 0x230821, +0xac22a8cc, 0x10000006, 0x24040001, 0x121140, +0x3c010002, 0x220821, 0xac20a8cc, 0x24040001, +0x2821, 0x27b0001e, 0xc004b9a, 0x2003021, +0x24040001, 0x2821, 0xc004b9a, 0x2003021, +0x24040001, 0x24050001, 0x27b0001c, 0xc004b9a, +0x2003021, 0x24040001, 0x24050001, 0xc004b9a, +0x2003021, 0x1000007b, 0x0, 0x3c02ffec, +0x3442ffff, 0x2028024, 0x3c020008, 0x2028025, +0x121140, 0x3c010002, 0x220821, 0xac30a8b8, +0x3c022000, 0x2021024, 0x10400009, 0x0, +0x3c020002, 0x8c428708, 0x14400005, 0x24020001, +0x3c010002, 0xac2287d8, 0x10000004, 0x3c024000, +0x3c010002, 0xac2087d8, 0x3c024000, 0x2021024, +0x1440001d, 0x24020e01, 0x3c030002, 0x8c6387d8, +0xaf820238, 0x3c010002, 0xac208684, 0x10600005, +0x24022020, 0x3c010002, 0xac2287dc, 0x24020001, +0xaee204b8, 0x3c04bfff, 0x121940, 0x3c020002, +0x431021, 0x8c42a8b0, 0x3c050002, 0x8ca5866c, +0x3484ffff, 0x441024, 0x3c010002, 0x230821, +0xac22a8b0, 0x24020001, 0x10a20048, 0x0, +0x10000044, 0x0, 0x3c020002, 0x8c4287d8, +0x1040001f, 0x24022000, 0x3c010002, 0xac2287dc, +0x3c0300a0, 0x2031024, 0x14430006, 0x121140, +0x3402a000, 0x3c010002, 0xac2287dc, 0x10000030, +0x0, 0x3c030002, 0x621821, 0x8c63a8b8, +0x3c020020, 0x621024, 0x10400005, 0x24022001, +0x3c010002, 0xac2287dc, 0x10000025, 0x0, +0x3c020080, 0x621024, 0x10400021, 0x3402a001, +0x3c010002, 0xac2287dc, 0x1000001d, 0x0, +0x3c020020, 0x2021024, 0x10400007, 0x121940, +0x24020100, 0x3c010002, 0x230821, 0xac22a8c4, +0x10000006, 0x3c020080, 0x121140, 0x3c010002, +0x220821, 0xac20a8c4, 0x3c020080, 0x2021024, +0x10400007, 0x121940, 0x3c020001, 0x3c010002, +0x230821, 0xac22a8cc, 0x10000005, 0x0, +0x121140, 0x3c010002, 0x220821, 0xac20a8cc, +0x3c030002, 0x8c63866c, 0x24020001, 0x10620003, +0x0, 0xc00430b, 0x0, 0x8fbf0030, +0x8fb3002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, +0x3e00008, 0x27bd0038, 0x27bdffb0, 0xafb3003c, +0x9821, 0xafb50040, 0xa821, 0xafb10034, +0x8821, 0x24020002, 0xafbf0048, 0xafbe0044, +0xafb20038, 0xafb00030, 0xafa4002c, 0xa7a0001a, +0xa7a00018, 0xa7a00020, 0xa7a0001e, 0xa7a00022, +0x10a20131, 0xa7a0001c, 0x2ca20003, 0x10400005, +0x24020001, 0x10a2000a, 0x3c024000, 0x1000025f, +0x2201021, 0x24020004, 0x10a2020c, 0x24020008, +0x10a2020a, 0x2201021, 0x10000258, 0x0, +0x8fa8002c, 0x88140, 0x3c030002, 0x701821, +0x8c63a8bc, 0x621024, 0x14400009, 0x24040001, +0x3c027fff, 0x3442ffff, 0x628824, 0x3c010002, +0x300821, 0xac31a8b4, 0x10000248, 0x2201021, +0x24050001, 0xc004b9a, 0x27a60018, 0x24040001, +0x24050001, 0xc004b9a, 0x27a60018, 0x97a20018, +0x30420004, 0x104000da, 0x3c114000, 0x3c020002, +0x8c4287fc, 0x2443ffff, 0x2c620006, 0x104000da, +0x31080, 0x3c010002, 0x220821, 0x8c2283b0, +0x400008, 0x0, 0x24040001, 0x24050011, +0x27b0001a, 0xc004b9a, 0x2003021, 0x24040001, +0x24050011, 0xc004b9a, 0x2003021, 0x97a3001a, +0x30624000, 0x10400002, 0x3c150010, 0x3c150008, +0x30628000, 0x104000ab, 0x3c130001, 0x100000a9, +0x3c130002, 0x24040001, 0x24050014, 0x27b0001a, +0xc004b9a, 0x2003021, 0x24040001, 0x24050014, +0xc004b9a, 0x2003021, 0x97a3001a, 0x30621000, +0x10400002, 0x3c150010, 0x3c150008, 0x30620800, +0x10400098, 0x3c130001, 0x10000096, 0x3c130002, +0x24040001, 0x24050019, 0x27b0001c, 0xc004b9a, +0x2003021, 0x24040001, 0x24050019, 0xc004b9a, +0x2003021, 0x97a2001c, 0x30430700, 0x24020400, +0x10620027, 0x28620401, 0x1040000e, 0x24020200, +0x1062001f, 0x28620201, 0x10400005, 0x24020100, +0x5062001e, 0x3c130001, 0x1000001e, 0x24040001, +0x24020300, 0x50620019, 0x3c130002, 0x10000019, +0x24040001, 0x24020600, 0x1062000d, 0x28620601, +0x10400005, 0x24020500, 0x5062000b, 0x3c130002, +0x10000010, 0x24040001, 0x24020700, 0x1462000d, +0x24040001, 0x3c130004, 0x1000000a, 0x3c150008, +0x10000006, 0x3c130004, 0x10000005, 0x3c150008, +0x3c130001, 0x10000002, 0x3c150008, 0x3c150010, +0x24040001, 0x24050018, 0x27b0001e, 0xc004b9a, +0x2003021, 0x24040001, 0x24050018, 0xc004b9a, +0x2003021, 0x8fa8002c, 0x97a7001e, 0x81140, +0x3c060002, 0xc23021, 0x8cc6a8b4, 0x97a20022, +0x3c100002, 0x261083a4, 0x2002021, 0xafa20010, +0x97a2001c, 0x3c05000c, 0x34a50303, 0xc002d3b, +0xafa20014, 0x3c020004, 0x16620010, 0x3c020001, +0x8f840054, 0x24030001, 0x24020002, 0x3c010002, +0xac238670, 0x3c010002, 0xac22866c, 0x3c010002, +0xac238678, 0x3c010002, 0xac2386f0, 0x3c010002, +0xac2487ec, 0x10000050, 0x2b38825, 0x1662003a, +0x3c028000, 0x3c020002, 0x8c4286ec, 0x1440001f, +0x24040018, 0x2021, 0x2821, 0xc005400, +0x34068000, 0x8f830054, 0x8f820054, 0x2b38825, +0x10000002, 0x24630032, 0x8f820054, 0x621023, +0x2c420033, 0x1440fffc, 0x0, 0x8f830054, +0x24020001, 0x3c010002, 0xac2286ec, 0x3c010002, +0xac228670, 0x3c010002, 0xac22866c, 0x3c010002, +0xac228678, 0x3c010002, 0xac2286f0, 0x3c010002, +0xac2387ec, 0x1000002c, 0x0, 0x2821, +0xc005400, 0x24060404, 0x2021, 0x2405001e, +0x27a60018, 0x24020002, 0xc004bdc, 0xa7a20018, +0x2021, 0x2821, 0x27a60018, 0xc004bdc, +0xa7a00018, 0x24040018, 0x24050002, 0xc005400, +0x24060004, 0x3c028000, 0x2221025, 0x2b31825, +0x10000015, 0x438825, 0x2221025, 0x2751825, +0x438825, 0x2002021, 0x97a6001c, 0x3c070002, +0x8ce7866c, 0x3c05000c, 0x34a50326, 0xafb30010, +0xc002d3b, 0xafb10014, 0x10000007, 0x0, +0x3c110002, 0x2308821, 0x8e31a8bc, 0x3c027fff, +0x3442ffff, 0x2228824, 0x3c020002, 0x8c42867c, +0x1040001e, 0x0, 0x3c020002, 0x8c4287d8, +0x10400002, 0x3c022000, 0x2228825, 0x8fa8002c, +0x81140, 0x3c010002, 0x220821, 0x8c22a8c0, +0x10400003, 0x3c020020, 0x10000005, 0x2228825, +0x3c02ffdf, 0x3442ffff, 0x2228824, 0x8fa8002c, +0x81140, 0x3c010002, 0x220821, 0x8c22a8c8, +0x10400003, 0x3c020080, 0x10000004, 0x2228825, +0x3c02ff7f, 0x3442ffff, 0x2228824, 0x8fa8002c, +0x81140, 0x3c010002, 0x220821, 0xac31a8b4, +0x10000136, 0x2201021, 0x8fa8002c, 0x8f140, +0x3c030002, 0x7e1821, 0x8c63a8b8, 0x3c024000, +0x621024, 0x14400009, 0x24040001, 0x3c027fff, +0x3442ffff, 0x628824, 0x3c010002, 0x3e0821, +0xac31a8b0, 0x10000125, 0x2201021, 0x2821, +0xc004b9a, 0x27a60018, 0x24040001, 0x2821, +0xc004b9a, 0x27a60018, 0x24040001, 0x24050001, +0x27b20020, 0xc004b9a, 0x2403021, 0x24040001, +0x24050001, 0xc004b9a, 0x2403021, 0x24040001, +0x24050004, 0x27b1001e, 0xc004b9a, 0x2203021, +0x24040001, 0x24050004, 0xc004b9a, 0x2203021, +0x24040001, 0x24050005, 0x27b00022, 0xc004b9a, +0x2003021, 0x24040001, 0x24050005, 0xc004b9a, +0x2003021, 0x24040001, 0x24050010, 0xc004b9a, +0x27a60018, 0x24040001, 0x24050010, 0xc004b9a, +0x27a60018, 0x24040001, 0x2405000a, 0xc004b9a, +0x2403021, 0x24040001, 0x2405000a, 0xc004b9a, +0x2403021, 0x24040001, 0x24050018, 0xc004b9a, +0x2203021, 0x24040001, 0x24050018, 0xc004b9a, +0x2203021, 0x24040001, 0x24050001, 0xc004b9a, +0x27a60018, 0x24040001, 0x24050001, 0xc004b9a, +0x27a60018, 0x97a20018, 0x30420004, 0x10400067, +0x3c114000, 0x3c030002, 0x8c6387f0, 0x24020005, +0x14620068, 0x24040001, 0x24050019, 0x27b0001c, +0xc004b9a, 0x2003021, 0x24040001, 0x24050019, +0xc004b9a, 0x2003021, 0x97a2001c, 0x30430700, +0x24020400, 0x10620027, 0x28620401, 0x1040000e, +0x24020200, 0x1062001f, 0x28620201, 0x10400005, +0x24020100, 0x5062001e, 0x3c130001, 0x1000001e, +0x3c020004, 0x24020300, 0x50620019, 0x3c130002, +0x10000019, 0x3c020004, 0x24020600, 0x1062000d, +0x28620601, 0x10400005, 0x24020500, 0x5062000b, +0x3c130002, 0x10000010, 0x3c020004, 0x24020700, +0x1462000d, 0x3c020004, 0x3c130004, 0x1000000a, +0x3c150008, 0x10000006, 0x3c130004, 0x10000005, +0x3c150008, 0x3c130001, 0x10000002, 0x3c150008, +0x3c150010, 0x3c020004, 0x12620018, 0x3c028000, +0x8f820054, 0x24100001, 0x3c010002, 0xac308670, +0x3c010002, 0xac30866c, 0x3c010002, 0xac308678, +0x3c010002, 0xac3086f0, 0x3c010002, 0xac2287ec, +0x3c020001, 0x16620023, 0x2758825, 0x2021, +0x2821, 0xc005400, 0x34068000, 0x3c010002, +0xac3086ec, 0x1000001b, 0x0, 0x2221025, +0x2b31825, 0x438825, 0x97a6001c, 0x3c020002, +0x8c4287d8, 0x3c070002, 0x8ce7866c, 0x3c040002, +0x248483a4, 0xafa20010, 0x97a2001e, 0x3c05000c, +0x34a50323, 0x3c010002, 0xac2086ec, 0xc002d3b, +0xafa20014, 0x10000007, 0x0, 0x3c110002, +0x23e8821, 0x8e31a8b0, 0x3c027fff, 0x3442ffff, +0x2228824, 0x3c020002, 0x8c42867c, 0x10400069, +0x0, 0x3c020002, 0x8c4287d8, 0x10400002, +0x3c022000, 0x2228825, 0x8fa8002c, 0x81140, +0x3c010002, 0x220821, 0x8c22a8c4, 0x10400003, +0x3c020020, 0x10000005, 0x2228825, 0x3c02ffdf, +0x3442ffff, 0x2228824, 0x8fa8002c, 0x81140, +0x3c010002, 0x220821, 0x8c22a8cc, 0x10400003, +0x3c020080, 0x1000004f, 0x2228825, 0x3c02ff7f, +0x3442ffff, 0x1000004b, 0x2228824, 0x8fa8002c, +0x82940, 0x3c030002, 0x651821, 0x8c63a8b8, +0x3c024000, 0x621024, 0x14400008, 0x3c027fff, +0x3442ffff, 0x628824, 0x3c010002, 0x250821, +0xac31a8b0, 0x10000041, 0x2201021, 0x3c020002, +0x8c42867c, 0x10400034, 0x3c11c00c, 0x3c020002, +0x8c428708, 0x3c04c00c, 0x34842000, 0x3c030002, +0x8c6387d8, 0x2102b, 0x21023, 0x441024, +0x10600003, 0x518825, 0x3c022000, 0x2228825, +0x3c020002, 0x451021, 0x8c42a8c4, 0x10400003, +0x3c020020, 0x10000004, 0x2228825, 0x3c02ffdf, +0x3442ffff, 0x2228824, 0x8fa8002c, 0x81140, +0x3c010002, 0x220821, 0x8c22a8cc, 0x10400003, +0x3c020080, 0x10000004, 0x2228825, 0x3c02ff7f, +0x3442ffff, 0x2228824, 0x3c020002, 0x8c4286f4, +0x10400002, 0x3c020800, 0x2228825, 0x3c020002, +0x8c4286f8, 0x10400002, 0x3c020400, 0x2228825, +0x3c020002, 0x8c4286fc, 0x10400006, 0x3c020100, +0x10000004, 0x2228825, 0x3c027fff, 0x3442ffff, +0x628824, 0x8fa8002c, 0x81140, 0x3c010002, +0x220821, 0xac31a8b0, 0x2201021, 0x8fbf0048, +0x8fbe0044, 0x8fb50040, 0x8fb3003c, 0x8fb20038, +0x8fb10034, 0x8fb00030, 0x3e00008, 0x27bd0050, +0x27bdffd0, 0xafb20028, 0x809021, 0xafbf002c, +0xafb10024, 0xafb00020, 0x8f840200, 0x3c100002, +0x8e10866c, 0x8f860220, 0x24020002, 0x1202005e, +0x2e020003, 0x10400005, 0x24020001, 0x1202000a, +0x121940, 0x10000114, 0x0, 0x24020004, +0x120200c6, 0x24020008, 0x120200c5, 0x128940, +0x1000010d, 0x0, 0x3c050002, 0xa32821, +0x8ca5a8bc, 0x3c100002, 0x2038021, 0x8e10a8b4, +0x3c024000, 0xa21024, 0x10400038, 0x3c020008, +0x2021024, 0x10400020, 0x34840002, 0x3c020002, +0x431021, 0x8c42a8c0, 0x10400005, 0x34840020, +0x34840100, 0x3c020020, 0x10000006, 0x2028025, +0x2402feff, 0x822024, 0x3c02ffdf, 0x3442ffff, +0x2028024, 0x121140, 0x3c010002, 0x220821, +0x8c22a8c8, 0x10400005, 0x3c020001, 0xc23025, +0x3c020080, 0x10000016, 0x2028025, 0x3c02fffe, +0x3442ffff, 0xc23024, 0x3c02ff7f, 0x3442ffff, +0x1000000f, 0x2028024, 0x2402fedf, 0x822024, +0x3c02fffe, 0x3442ffff, 0xc23024, 0x3c02ff5f, +0x3442ffff, 0x2028024, 0x3c010002, 0x230821, +0xac20a8c0, 0x3c010002, 0x230821, 0xac20a8c8, +0xaf840200, 0xaf860220, 0x8f820220, 0x34420002, +0xaf820220, 0x1000000b, 0x121140, 0x3c02bfff, +0x3442ffff, 0x8f830200, 0x2028024, 0x2402fffd, +0x621824, 0xaf830200, 0xc00430b, 0x0, +0x121140, 0x3c010002, 0x220821, 0xac30a8b4, +0x100000bd, 0x0, 0x3c020002, 0x8c4287d8, +0x1040006e, 0x24050004, 0x24040001, 0xc004b9a, +0x27a60018, 0x24040001, 0x24050005, 0xc004b9a, +0x27a6001a, 0x97a30018, 0x97a2001a, 0x3c040002, +0x2484870c, 0x30630c00, 0x31a82, 0x30420c00, +0x21282, 0xa7a2001a, 0x21080, 0x441021, +0x431021, 0xa7a30018, 0x90480000, 0x24020001, +0x3103ffff, 0x1062002b, 0x28620002, 0x10400005, +0x0, 0x10600009, 0x0, 0x10000041, +0x0, 0x10700014, 0x24020003, 0x1062002f, +0x0, 0x1000003b, 0x0, 0x8f820200, +0x2403feff, 0x431024, 0xaf820200, 0x8f820220, +0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820220, +0x3c010002, 0xac20a8c4, 0x3c010002, 0xac20a8cc, +0x10000035, 0x0, 0x8f820200, 0x34420100, +0xaf820200, 0x8f820220, 0x3c03fffe, 0x3463ffff, +0x431024, 0xaf820220, 0x24020100, 0x3c010002, +0xac22a8c4, 0x3c010002, 0xac20a8cc, 0x10000026, +0x0, 0x8f820200, 0x2403feff, 0x431024, +0xaf820200, 0x8f820220, 0x3c030001, 0x431025, +0xaf820220, 0x3c010002, 0xac20a8c4, 0x3c010002, +0xac23a8cc, 0x10000018, 0x0, 0x8f820200, +0x34420100, 0xaf820200, 0x8f820220, 0x3c030001, +0x431025, 0xaf820220, 0x24020100, 0x3c010002, +0xac22a8c4, 0x3c010002, 0xac23a8cc, 0x1000000a, +0x0, 0x3c040002, 0x248483c8, 0x97a6001a, +0x97a70018, 0x3c050001, 0x34a5ffff, 0xafa80010, +0xc002d3b, 0xafa00014, 0x8f820200, 0x34420002, +0xaf820200, 0x1000004c, 0x0, 0x128940, +0x3c050002, 0xb12821, 0x8ca5a8b8, 0x3c100002, +0x2118021, 0x8e10a8b0, 0x3c024000, 0xa21024, +0x14400011, 0x0, 0x3c020002, 0x8c4287d8, +0x14400005, 0x3c02bfff, 0x8f820200, 0x34420002, +0xaf820200, 0x3c02bfff, 0x3442ffff, 0xc00430b, +0x2028024, 0x3c010002, 0x310821, 0xac30a8b0, +0x10000031, 0x0, 0x3c020002, 0x8c4287d8, +0x10400005, 0x3c020020, 0x3c020002, 0x8c428708, +0x10400025, 0x3c020020, 0xa21024, 0x10400007, +0x34840020, 0x24020100, 0x3c010002, 0x310821, +0xac22a8c4, 0x10000006, 0x34840100, 0x3c010002, +0x310821, 0xac20a8c4, 0x2402feff, 0x822024, +0x3c020080, 0xa21024, 0x10400007, 0x121940, +0x3c020001, 0x3c010002, 0x230821, 0xac22a8cc, +0x10000008, 0xc23025, 0x121140, 0x3c010002, +0x220821, 0xac20a8cc, 0x3c02fffe, 0x3442ffff, +0xc23024, 0xaf840200, 0xaf860220, 0x8f820220, +0x34420002, 0xaf820220, 0x121140, 0x3c010002, +0x220821, 0xac30a8b0, 0x8fbf002c, 0x8fb20028, +0x8fb10024, 0x8fb00020, 0x3e00008, 0x27bd0030, +0x1821, 0x308400ff, 0x2405ffdf, 0x2406ffbf, +0x641007, 0x30420001, 0x10400004, 0x0, +0x8f820044, 0x10000003, 0x34420040, 0x8f820044, +0x461024, 0xaf820044, 0x8f820044, 0x34420020, +0xaf820044, 0x8f820044, 0x451024, 0xaf820044, +0x24630001, 0x28620008, 0x5440ffee, 0x641007, +0x3e00008, 0x0, 0x2c820008, 0x1040001b, +0x0, 0x2405ffdf, 0x2406ffbf, 0x41880, +0x3c020002, 0x2442871c, 0x621821, 0x24640004, +0x90620000, 0x10400004, 0x0, 0x8f820044, +0x10000003, 0x34420040, 0x8f820044, 0x461024, +0xaf820044, 0x8f820044, 0x34420020, 0xaf820044, +0x8f820044, 0x451024, 0xaf820044, 0x24630001, +0x64102b, 0x1440ffee, 0x0, 0x3e00008, +0x0, 0x0 }; +static u_int32_t tigon2FwRodata[] = { +0x24486561, +0x6465723a, 0x202f7072, 0x6f6a6563, 0x74732f72, +0x63732f73, 0x772f6765, 0x2f2e2f6e, 0x69632f66, +0x77322f63, 0x6f6d6d6f, 0x6e2f6677, 0x6d61696e, +0x2e632c76, 0x20312e31, 0x2e322e34, 0x35203139, +0x39392f30, 0x312f3234, 0x2030303a, 0x31303a35, +0x35207368, 0x75616e67, 0x20457870, 0x20240000, +0x65767452, 0x6e674600, 0x51657674, 0x46000000, +0x51657674, 0x505f4600, 0x4d657674, 0x526e6746, +0x0, 0x4d516576, 0x74460000, 0x4d516576, +0x505f4600, 0x5173436f, 0x6e495f46, 0x0, +0x5173436f, 0x6e734600, 0x51725072, 0x6f644600, +0x6261644d, 0x656d537a, 0x0, 0x2a50414e, +0x49432a00, 0x66776d61, 0x696e2e63, 0x0, +0x68775665, 0x72000000, 0x62616448, 0x77566572, +0x0, 0x2a2a4441, 0x574e5f41, 0x0, +0x74785278, 0x4266537a, 0x0, 0x62664174, +0x6e4d726b, 0x0, 0x7265645a, 0x6f6e6531, +0x0, 0x70636943, 0x6f6e6600, 0x67656e43, +0x6f6e6600, 0x2a646d61, 0x5244666c, 0x0, +0x72636246, 0x6c616773, 0x0, 0x62616452, +0x78526362, 0x0, 0x676c6f62, 0x466c6773, +0x0, 0x2b5f6469, 0x73705f6c, 0x6f6f7000, +0x2b65765f, 0x68616e64, 0x6c657200, 0x63616e74, +0x31446d61, 0x0, 0x2b715f64, 0x6d615f74, +0x6f5f6e69, 0x635f636b, 0x73756d00, 0x2b685f73, +0x656e645f, 0x64617461, 0x5f726561, 0x64795f63, +0x6b73756d, 0x0, 0x2b685f64, 0x6d615f72, +0x645f6173, 0x73697374, 0x5f636b73, 0x756d0000, +0x74436b73, 0x6d4f6e00, 0x2b715f64, 0x6d615f74, +0x6f5f6e69, 0x63000000, 0x2b685f73, 0x656e645f, +0x64617461, 0x5f726561, 0x64790000, 0x2b685f64, +0x6d615f72, 0x645f6173, 0x73697374, 0x0, +0x74436b73, 0x6d4f6666, 0x0, 0x2b685f73, +0x656e645f, 0x62645f72, 0x65616479, 0x0, +0x68737453, 0x52696e67, 0x0, 0x62616453, +0x52696e67, 0x0, 0x6e696353, 0x52696e67, +0x0, 0x77446d61, 0x416c6c41, 0x0, +0x2b715f64, 0x6d615f74, 0x6f5f686f, 0x73745f63, +0x6b73756d, 0x0, 0x2b685f6d, 0x61635f72, +0x785f636f, 0x6d705f63, 0x6b73756d, 0x0, +0x2b685f64, 0x6d615f77, 0x725f6173, 0x73697374, +0x5f636b73, 0x756d0000, 0x72436b73, 0x6d4f6e00, +0x2b715f64, 0x6d615f74, 0x6f5f686f, 0x73740000, +0x2b685f6d, 0x61635f72, 0x785f636f, 0x6d700000, +0x2b685f64, 0x6d615f77, 0x725f6173, 0x73697374, +0x0, 0x72436b73, 0x6d4f6666, 0x0, +0x2b685f72, 0x6563765f, 0x62645f72, 0x65616479, +0x0, 0x2b685f72, 0x6563765f, 0x6a756d62, +0x6f5f6264, 0x5f726561, 0x64790000, 0x2b685f72, +0x6563765f, 0x6d696e69, 0x5f62645f, 0x72656164, +0x79000000, 0x2b6d685f, 0x636f6d6d, 0x616e6400, +0x2b685f74, 0x696d6572, 0x0, 0x2b685f64, +0x6f5f7570, 0x64617465, 0x5f74785f, 0x636f6e73, +0x0, 0x2b685f64, 0x6f5f7570, 0x64617465, +0x5f72785f, 0x70726f64, 0x0, 0x2b636b73, +0x756d3136, 0x0, 0x2b706565, 0x6b5f6d61, +0x635f7278, 0x5f776100, 0x2b706565, 0x6b5f6d61, +0x635f7278, 0x0, 0x2b646571, 0x5f6d6163, +0x5f727800, 0x2b685f6d, 0x61635f72, 0x785f6174, +0x746e0000, 0x62616452, 0x6574537a, 0x0, +0x72784264, 0x4266537a, 0x0, 0x2b6e756c, +0x6c5f6861, 0x6e646c65, 0x72000000, 0x66774f70, +0x4661696c, 0x0, 0x2b685f75, 0x70646174, +0x655f6c65, 0x64340000, 0x2b685f75, 0x70646174, +0x655f6c65, 0x64360000, 0x2b685f75, 0x70646174, +0x655f6c65, 0x64320000, 0x696e7453, 0x74617465, +0x0, 0x2a2a696e, 0x69744370, 0x0, +0x23736372, 0x65616d00, 0x69537461, 0x636b4572, +0x0, 0x70726f62, 0x654d656d, 0x0, +0x2a2a4441, 0x574e5f42, 0x0, 0x2b73775f, +0x646d615f, 0x61737369, 0x73745f70, 0x6c75735f, +0x74696d65, 0x72000000, 0x2b267072, 0x656c6f61, +0x645f7772, 0x5f646573, 0x63720000, 0x2b267072, +0x656c6f61, 0x645f7264, 0x5f646573, 0x63720000, +0x2b685f68, 0x665f7469, 0x6d657200, 0x24486561, +0x6465723a, 0x202f7072, 0x6f6a6563, 0x74732f72, +0x63732f73, 0x772f6765, 0x2f2e2f6e, 0x69632f66, +0x77322f63, 0x6f6d6d6f, 0x6e2f7469, 0x6d65722e, +0x632c7620, 0x312e312e, 0x322e3335, 0x20313939, +0x392f3031, 0x2f323720, 0x31393a30, 0x393a3530, +0x20686179, 0x65732045, 0x78702024, 0x0, +0x65767452, 0x6e674600, 0x51657674, 0x46000000, +0x51657674, 0x505f4600, 0x4d657674, 0x526e6746, +0x0, 0x4d516576, 0x74460000, 0x4d516576, +0x505f4600, 0x5173436f, 0x6e495f46, 0x0, +0x5173436f, 0x6e734600, 0x51725072, 0x6f644600, +0x2a50414e, 0x49432a00, 0x6d61632e, 0x68000000, +0x74696d65, 0x722e6300, 0x542d446d, 0x61526432, +0x0, 0x542d446d, 0x61526431, 0x0, +0x542d446d, 0x61526442, 0x0, 0x542d446d, +0x61577232, 0x0, 0x542d446d, 0x61577231, +0x0, 0x542d446d, 0x61577242, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f636f, 0x6d6d616e, 0x642e632c, 0x7620312e, +0x312e322e, 0x32382031, 0x3939392f, 0x30312f32, +0x30203139, 0x3a34393a, 0x34392073, 0x6875616e, +0x67204578, 0x70202400, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x3f48636d, 0x644d6278, +0x0, 0x3f636d64, 0x48737453, 0x0, +0x3f636d64, 0x4d634d64, 0x0, 0x3f636d64, +0x50726f6d, 0x0, 0x3f636d64, 0x4c696e6b, +0x0, 0x3f636d64, 0x45727200, 0x8b08, +0x92e4, 0x92e4, 0x9264, 0x8ff4, +0x92b8, 0x92e4, 0x8bf4, 0x8c64, +0x8df8, 0x8ee0, 0x8ea8, 0x92e4, +0x8cd4, 0x8fa0, 0x92e4, 0x8fb0, +0x8c18, 0x8c88, 0x24486561, 0x6465723a, +0x202f7072, 0x6f6a6563, 0x74732f72, 0x63732f73, +0x772f6765, 0x2f2e2f6e, 0x69632f66, 0x77322f63, +0x6f6d6d6f, 0x6e2f6d63, 0x6173742e, 0x632c7620, +0x312e312e, 0x322e3820, 0x31393938, 0x2f31322f, +0x30382030, 0x323a3336, 0x3a333620, 0x73687561, +0x6e672045, 0x78702024, 0x0, 0x65767452, +0x6e674600, 0x51657674, 0x46000000, 0x51657674, +0x505f4600, 0x4d657674, 0x526e6746, 0x0, +0x4d516576, 0x74460000, 0x4d516576, 0x505f4600, +0x5173436f, 0x6e495f46, 0x0, 0x5173436f, +0x6e734600, 0x51725072, 0x6f644600, 0x6164644d, +0x63447570, 0x0, 0x6164644d, 0x6346756c, +0x0, 0x64656c4d, 0x634e6f45, 0x0, +0x24486561, 0x6465723a, 0x202f7072, 0x6f6a6563, +0x74732f72, 0x63732f73, 0x772f6765, 0x2f2e2f6e, +0x69632f66, 0x77322f63, 0x6f6d6d6f, 0x6e2f646d, +0x612e632c, 0x7620312e, 0x312e322e, 0x32342031, +0x3939382f, 0x31322f32, 0x31203030, 0x3a33333a, +0x30392073, 0x6875616e, 0x67204578, 0x70202400, +0x65767452, 0x6e674600, 0x51657674, 0x46000000, +0x51657674, 0x505f4600, 0x4d657674, 0x526e6746, +0x0, 0x4d516576, 0x74460000, 0x4d516576, +0x505f4600, 0x5173436f, 0x6e495f46, 0x0, +0x5173436f, 0x6e734600, 0x51725072, 0x6f644600, +0x7377446d, 0x614f6666, 0x0, 0x31446d61, +0x4f6e0000, 0x7377446d, 0x614f6e00, 0x2a50414e, +0x49432a00, 0x646d612e, 0x63000000, 0x2372446d, +0x6141544e, 0x0, 0x72446d61, 0x41544e30, +0x0, 0x72446d61, 0x41544e31, 0x0, +0x72446d61, 0x34476200, 0x2377446d, 0x6141544e, +0x0, 0x77446d61, 0x41544e30, 0x0, +0x77446d61, 0x41544e31, 0x0, 0x77446d61, +0x34476200, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f7472, 0x6163652e, 0x632c7620, 0x312e312e, +0x322e3520, 0x31393938, 0x2f30392f, 0x33302031, +0x383a3530, 0x3a323820, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x24486561, 0x6465723a, +0x202f7072, 0x6f6a6563, 0x74732f72, 0x63732f73, +0x772f6765, 0x2f2e2f6e, 0x69632f66, 0x77322f63, +0x6f6d6d6f, 0x6e2f6461, 0x74612e63, 0x2c762031, +0x2e312e32, 0x2e313220, 0x31393939, 0x2f30312f, +0x32302031, 0x393a3439, 0x3a353120, 0x73687561, +0x6e672045, 0x78702024, 0x0, 0x46575f56, +0x45525349, 0x4f4e3a20, 0x58585800, 0x46575f43, +0x4f4d5049, 0x4c455f54, 0x494d453a, 0x20585858, +0x0, 0x46575f43, 0x4f4d5049, 0x4c455f42, +0x593a2058, 0x58580000, 0x46575f43, 0x4f4d5049, +0x4c455f48, 0x4f53543a, 0x20585858, 0x0, +0x46575f43, 0x4f4d5049, 0x4c455f44, 0x4f4d4149, +0x4e3a2058, 0x58580000, 0x46575f43, 0x4f4d5049, +0x4c45523a, 0x20585858, 0x0, 0x0, +0x12041100, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f6d65, 0x6d2e632c, 0x7620312e, 0x312e322e, +0x35203139, 0x39382f30, 0x392f3330, 0x2031383a, +0x35303a30, 0x38207368, 0x75616e67, 0x20457870, +0x20240000, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f7365, 0x6e642e63, 0x2c762031, 0x2e312e32, +0x2e343420, 0x31393938, 0x2f31322f, 0x32312030, +0x303a3333, 0x3a313820, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x2a50414e, 0x49432a00, +0x6d61632e, 0x68000000, 0x73656e64, 0x2e630000, +0x69736e74, 0x54637055, 0x0, 0x24486561, +0x6465723a, 0x202f7072, 0x6f6a6563, 0x74732f72, +0x63732f73, 0x772f6765, 0x2f2e2f6e, 0x69632f66, +0x77322f63, 0x6f6d6d6f, 0x6e2f7265, 0x63762e63, +0x2c762031, 0x2e312e32, 0x2e353320, 0x31393939, +0x2f30312f, 0x31362030, 0x323a3535, 0x3a343320, +0x73687561, 0x6e672045, 0x78702024, 0x0, +0x65767452, 0x6e674600, 0x51657674, 0x46000000, +0x51657674, 0x505f4600, 0x4d657674, 0x526e6746, +0x0, 0x4d516576, 0x74460000, 0x4d516576, +0x505f4600, 0x5173436f, 0x6e495f46, 0x0, +0x5173436f, 0x6e734600, 0x51725072, 0x6f644600, +0x2a50414e, 0x49432a00, 0x6d61632e, 0x68000000, +0x724d6163, 0x43686b30, 0x0, 0x72784672, +0x6d324c67, 0x0, 0x72784e6f, 0x53744264, +0x0, 0x72784e6f, 0x4d694264, 0x0, +0x72784e6f, 0x4a6d4264, 0x0, 0x72656376, +0x2e630000, 0x7278436b, 0x446d6146, 0x0, +0x72785144, 0x6d457846, 0x0, 0x72785144, +0x6d614600, 0x72785144, 0x4c426446, 0x0, +0x72785144, 0x6d426446, 0x0, 0x72784372, +0x63506164, 0x0, 0x72536d51, 0x446d6146, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f6d61, 0x632e632c, 0x7620312e, 0x312e322e, +0x32322031, 0x3939382f, 0x31322f30, 0x38203032, +0x3a33363a, 0x33302073, 0x6875616e, 0x67204578, +0x70202400, 0x65767452, 0x6e674600, 0x51657674, +0x46000000, 0x51657674, 0x505f4600, 0x4d657674, +0x526e6746, 0x0, 0x4d516576, 0x74460000, +0x4d516576, 0x505f4600, 0x5173436f, 0x6e495f46, +0x0, 0x5173436f, 0x6e734600, 0x51725072, +0x6f644600, 0x2a50414e, 0x49432a00, 0x6d61632e, +0x68000000, 0x6d616354, 0x68726573, 0x0, +0x23744d61, 0x6341544e, 0x0, 0x23724d61, +0x6341544e, 0x0, 0x72656d41, 0x73737274, +0x0, 0x6d61632e, 0x63000000, 0x6c696e6b, +0x444f574e, 0x0, 0x6c696e6b, 0x55500000, +0x24486561, 0x6465723a, 0x202f7072, 0x6f6a6563, +0x74732f72, 0x63732f73, 0x772f6765, 0x2f2e2f6e, +0x69632f66, 0x77322f63, 0x6f6d6d6f, 0x6e2f636b, +0x73756d2e, 0x632c7620, 0x312e312e, 0x322e3920, +0x31393939, 0x2f30312f, 0x31342030, 0x303a3033, +0x3a343820, 0x73687561, 0x6e672045, 0x78702024, +0x0, 0x65767452, 0x6e674600, 0x51657674, +0x46000000, 0x51657674, 0x505f4600, 0x4d657674, +0x526e6746, 0x0, 0x4d516576, 0x74460000, +0x4d516576, 0x505f4600, 0x5173436f, 0x6e495f46, +0x0, 0x5173436f, 0x6e734600, 0x51725072, +0x6f644600, 0x2a50414e, 0x49432a00, 0x6d61632e, +0x68000000, 0x2a50414e, 0x49432a00, 0x2e2e2f63, +0x6f6d6d6f, 0x6e2f6d61, 0x632e6800, 0x2e2e2f2e, +0x2e2f2e2e, 0x2f636f6d, 0x6d6f6e2f, 0x6c696e6b, +0x2e630000, 0x50726f62, 0x65506879, 0x0, +0x6c6e6b41, 0x53535254, 0x0, 0x6e6f4863, +0x644c6b00, 0x11f8c, 0x1200c, 0x12040, +0x1206c, 0x120e8, 0x12160, 0x121c8, +0x12940, 0x12324, 0x1235c, 0x12374, +0x123b8, 0x123e0, 0x12404, 0x1242c, +0x12940, 0x12324, 0x124b8, 0x124d0, +0x12500, 0x123e0, 0x12528, 0x12550, +0x0, 0x12684, 0x126b4, 0x126d8, +0x12940, 0x126fc, 0x127b8, 0x1284c, +0x0, 0x2a50414e, 0x49432a00, 0x2e2e2f63, +0x6f6d6d6f, 0x6e2f6d61, 0x632e6800, 0x130e4, +0x131b4, 0x1328c, 0x1335c, 0x133b8, +0x13494, 0x134bc, 0x13598, 0x135c0, +0x13768, 0x13790, 0x13938, 0x13b30, +0x13dc8, 0x13cd8, 0x13dc8, 0x13df4, +0x13960, 0x13b08, 0x7273745f, 0x676d6969, +0x0, 0x13e88, 0x13ec4, 0x13fb0, +0x14c04, 0x14c48, 0x14c60, 0x7365746c, +0x6f6f7000, 0x2a50414e, 0x49432a00, 0x2e2e2f63, +0x6f6d6d6f, 0x6e2f6d61, 0x632e6800, 0x15454, +0x15494, 0x1552c, 0x15570, 0x155dc, +0x1566c, 0x156a0, 0x1572c, 0x157d0, +0x158a0, 0x158e0, 0x1596c, 0x15990, +0x15aa8, 0x646f4261, 0x73655067, 0x0, +0x0, 0x2a50414e, 0x49432a00, 0x2e2e2f63, +0x6f6d6d6f, 0x6e2f6d61, 0x632e6800, 0x73746d61, +0x634c4e4b, 0x0, 0x6765746d, 0x636c6e6b, +0x0, 0x167cc, 0x167cc, 0x1647c, +0x164c8, 0x16514, 0x167cc, 0x7365746d, +0x61636163, 0x74000000, 0x0 }; +static u_int32_t tigon2FwData[] = { +0x1, +0x1, 0x1, 0xc001fc, 0x3ffc, +0xc00000, 0x416c7465, 0x6f6e2041, 0x63654e49, +0x43205600, 0x0, 0x416c7465, 0x6f6e2041, +0x63654e49, 0x43205600, 0x42424242, 0x1ffffc, +0x1fff7c, 0x0, 0x0, 0x0, +0x60cf00, 0x60, 0xcf000000, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x3, +0x0, 0x1, 0x0, 0x0, +0x0, 0x1, 0x0, 0x1, +0x0, 0x0, 0x1, 0x1, +0x0, 0x0, 0x0, 0x0, +0x0, 0x1000000, 0x21000000, 0x12000140, +0x0, 0x0, 0x20000000, 0x120000a0, +0x0, 0x12000060, 0x12000180, 0x120001e0, +0x0, 0x0, 0x0, 0x1, +0x0, 0x0, 0x0, 0x0, +0x2, 0x0, 0x0, 0x30001, +0x1, 0x30201, 0x1010101, 0x1010100, +0x10100, 0x1010001, 0x10001, 0x1000101, +0x101, 0x0, 0x0 }; diff --git a/sys/pci/uhci_pci.c b/sys/pci/uhci_pci.c new file mode 100644 index 0000000..db3d5c6 --- /dev/null +++ b/sys/pci/uhci_pci.c @@ -0,0 +1,407 @@ +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (augustss@carlstedt.se) at + * Carlstedt Research & Technology. + * + * 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 acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + * + * $FreeBSD$ + */ + +/* Universal Host Controller Interface + * + * UHCI spec: http://www.intel.com/ + */ + +/* The low level controller code for UHCI has been split into + * PCI probes and UHCI specific code. This was done to facilitate the + * sharing of code between *BSD's + */ + +#include "opt_bus.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/queue.h> +#if defined(__FreeBSD__) +#include <sys/bus.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> +#endif + +#include <pci/pcivar.h> +#include <pci/pcireg.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usb_mem.h> + +#include <dev/usb/uhcireg.h> +#include <dev/usb/uhcivar.h> + +#define PCI_UHCI_VENDORID_INTEL 0x8086 +#define PCI_UHCI_VENDORID_VIA 0x1106 + +#define PCI_UHCI_DEVICEID_PIIX3 0x70208086 +static const char *uhci_device_piix3 = "Intel 82371SB (PIIX3) USB controller"; + +#define PCI_UHCI_DEVICEID_PIIX4 0x71128086 +#define PCI_UHCI_DEVICEID_PIIX4E 0x71128086 /* no separate stepping */ +static const char *uhci_device_piix4 = "Intel 82371AB/EB (PIIX4) USB controller"; + +#define PCI_UHCI_DEVICEID_ICH 0x24128086 +static const char *uhci_device_ich = "Intel 82801AA (ICH) USB controller"; + +#define PCI_UHCI_DEVICEID_ICH0 0x24228086 +static const char *uhci_device_ich0 = "Intel 82801AB (ICH0) USB controller"; + +#define PCI_UHCI_DEVICEID_ICH2_A 0x24428086 +static const char *uhci_device_ich2_a = "Intel 82801BA/BAM (ICH2) USB controller USB-A"; + +#define PCI_UHCI_DEVICEID_ICH2_B 0x24448086 +static const char *uhci_device_ich2_b = "Intel 82801BA/BAM (ICH2) USB controller USB-B"; + +#define PCI_UHCI_DEVICEID_ICH3_A 0x24828086 +static const char *uhci_device_ich3_a = "Intel 82801CA/CAM (ICH3) USB controller USB-A"; + +#define PCI_UHCI_DEVICEID_ICH3_B 0x24848086 +static const char *uhci_device_ich3_b = "Intel 82801CA/CAM (ICH3) USB controller USB-B"; + +#define PCI_UHCI_DEVICEID_440MX 0x719a8086 +static const char *uhci_device_440mx = "Intel 82443MX USB controller"; + +#define PCI_UHCI_DEVICEID_460GX 0x76028086 +static const char *uhci_device_460gx = "Intel 82372FB/82468GX USB controller"; + +#define PCI_UHCI_DEVICEID_VT83C572 0x30381106 +static const char *uhci_device_vt83c572 = "VIA 83C572 USB controller"; + +static const char *uhci_device_generic = "UHCI (generic) USB controller"; + +#define PCI_UHCI_BASE_REG 0x20 + + +static int uhci_pci_attach(device_t self); +static int uhci_pci_detach(device_t self); +static int uhci_pci_suspend(device_t self); +static int uhci_pci_resume(device_t self); + + +static int +uhci_pci_suspend(device_t self) +{ + uhci_softc_t *sc = device_get_softc(self); + int err; + + err = bus_generic_suspend(self); + if (err) + return err; + uhci_power(PWR_SUSPEND, sc); + + return 0; +} + +static int +uhci_pci_resume(device_t self) +{ + uhci_softc_t *sc = device_get_softc(self); + + uhci_power(PWR_RESUME, sc); + bus_generic_resume(self); + + return 0; +} + +static const char * +uhci_pci_match(device_t self) +{ + u_int32_t device_id = pci_get_devid(self); + + if (device_id == PCI_UHCI_DEVICEID_PIIX3) { + return (uhci_device_piix3); + } else if (device_id == PCI_UHCI_DEVICEID_PIIX4) { + return (uhci_device_piix4); + } else if (device_id == PCI_UHCI_DEVICEID_ICH) { + return (uhci_device_ich); + } else if (device_id == PCI_UHCI_DEVICEID_ICH0) { + return (uhci_device_ich0); + } else if (device_id == PCI_UHCI_DEVICEID_ICH2_A) { + return (uhci_device_ich2_a); + } else if (device_id == PCI_UHCI_DEVICEID_ICH2_B) { + return (uhci_device_ich2_b); + } else if (device_id == PCI_UHCI_DEVICEID_ICH3_A) { + return (uhci_device_ich3_a); + } else if (device_id == PCI_UHCI_DEVICEID_ICH3_B) { + return (uhci_device_ich3_b); + } else if (device_id == PCI_UHCI_DEVICEID_440MX) { + return (uhci_device_440mx); + } else if (device_id == PCI_UHCI_DEVICEID_460GX) { + return (uhci_device_460gx); + } else if (device_id == PCI_UHCI_DEVICEID_VT83C572) { + return (uhci_device_vt83c572); + } else { + if (pci_get_class(self) == PCIC_SERIALBUS + && pci_get_subclass(self) == PCIS_SERIALBUS_USB + && pci_get_progif(self) == PCI_INTERFACE_UHCI) { + return (uhci_device_generic); + } + } + + return NULL; /* dunno... */ +} + +static int +uhci_pci_probe(device_t self) +{ + const char *desc = uhci_pci_match(self); + + if (desc) { + device_set_desc(self, desc); + return 0; + } else { + return ENXIO; + } +} + +static int +uhci_pci_attach(device_t self) +{ + uhci_softc_t *sc = device_get_softc(self); + int rid; + int err; + + rid = PCI_UHCI_BASE_REG; + sc->io_res = bus_alloc_resource(self, SYS_RES_IOPORT, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!sc->io_res) { + device_printf(self, "Could not map ports\n"); + return ENXIO; + } + sc->iot = rman_get_bustag(sc->io_res); + sc->ioh = rman_get_bushandle(sc->io_res); + + /* disable interrupts */ + bus_space_write_2(sc->iot, sc->ioh, UHCI_INTR, 0); + + rid = 0; + sc->irq_res = bus_alloc_resource(self, SYS_RES_IRQ, &rid, + 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(self, "Could not allocate irq\n"); + uhci_pci_detach(self); + return ENXIO; + } + sc->sc_bus.bdev = device_add_child(self, "usb", -1); + if (!sc->sc_bus.bdev) { + device_printf(self, "Could not add USB device\n"); + uhci_pci_detach(self); + return ENOMEM; + } + device_set_ivars(sc->sc_bus.bdev, sc); + + switch (pci_get_devid(self)) { + case PCI_UHCI_DEVICEID_PIIX3: + device_set_desc(sc->sc_bus.bdev, uhci_device_piix3); + sprintf(sc->sc_vendor, "Intel"); + break; + case PCI_UHCI_DEVICEID_PIIX4: + device_set_desc(sc->sc_bus.bdev, uhci_device_piix4); + sprintf(sc->sc_vendor, "Intel"); + break; + case PCI_UHCI_DEVICEID_ICH: + device_set_desc(sc->sc_bus.bdev, uhci_device_ich); + sprintf(sc->sc_vendor, "Intel"); + break; + case PCI_UHCI_DEVICEID_ICH0: + device_set_desc(sc->sc_bus.bdev, uhci_device_ich0); + sprintf(sc->sc_vendor, "Intel"); + break; + case PCI_UHCI_DEVICEID_ICH2_A: + device_set_desc(sc->sc_bus.bdev, uhci_device_ich2_a); + sprintf(sc->sc_vendor, "Intel"); + break; + case PCI_UHCI_DEVICEID_ICH2_B: + device_set_desc(sc->sc_bus.bdev, uhci_device_ich2_b); + sprintf(sc->sc_vendor, "Intel"); + break; + case PCI_UHCI_DEVICEID_ICH3_A: + device_set_desc(sc->sc_bus.bdev, uhci_device_ich3_a); + sprintf(sc->sc_vendor, "Intel"); + break; + case PCI_UHCI_DEVICEID_ICH3_B: + device_set_desc(sc->sc_bus.bdev, uhci_device_ich3_b); + sprintf(sc->sc_vendor, "Intel"); + break; + case PCI_UHCI_DEVICEID_440MX: + device_set_desc(sc->sc_bus.bdev, uhci_device_440mx); + sprintf(sc->sc_vendor, "Intel"); + break; + case PCI_UHCI_DEVICEID_460GX: + device_set_desc(sc->sc_bus.bdev, uhci_device_460gx); + sprintf(sc->sc_vendor, "Intel"); + break; + case PCI_UHCI_DEVICEID_VT83C572: + device_set_desc(sc->sc_bus.bdev, uhci_device_vt83c572); + sprintf(sc->sc_vendor, "VIA"); + break; + default: + device_printf(self, "(New UHCI DeviceId=0x%08x)\n", + pci_get_devid(self)); + device_set_desc(sc->sc_bus.bdev, uhci_device_generic); + sprintf(sc->sc_vendor, "(0x%08x)", pci_get_devid(self)); + } + + switch (pci_read_config(self, PCI_USBREV, 4) & PCI_USBREV_MASK) { + case PCI_USBREV_PRE_1_0: + sc->sc_bus.usbrev = USBREV_PRE_1_0; + break; + case PCI_USBREV_1_0: + sc->sc_bus.usbrev = USBREV_1_0; + break; + default: + sc->sc_bus.usbrev = USBREV_UNKNOWN; + break; + } + + err = bus_setup_intr(self, sc->irq_res, INTR_TYPE_BIO, + (driver_intr_t *) uhci_intr, sc, &sc->ih); + if (err) { + device_printf(self, "Could not setup irq, %d\n", err); + sc->ih = NULL; + uhci_pci_detach(self); + return ENXIO; + } + /* + * Set the PIRQD enable bit and switch off all the others. We don't + * want legacy support to interfere with us XXX Does this also mean + * that the BIOS won't touch the keyboard anymore if it is connected + * to the ports of the root hub? + */ +#ifdef UHCI_DEBUG + if (pci_read_config(self, PCI_LEGSUP, 4) != PCI_LEGSUP_USBPIRQDEN) + device_printf(self, "LegSup = 0x%08x\n", + pci_read_config(self, PCI_LEGSUP, 4)); +#endif + pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 4); + + err = uhci_init(sc); + if (!err) + err = device_probe_and_attach(sc->sc_bus.bdev); + + if (err) { + device_printf(self, "USB init failed\n"); + uhci_pci_detach(self); + return EIO; + } + return 0; /* success */ +} + +int +uhci_pci_detach(device_t self) +{ + uhci_softc_t *sc = device_get_softc(self); + + /* + * XXX This function is not yet complete and should not be added + * method list. + */ +#if 0 + if uhci_init + was successful + we should call something like uhci_deinit +#endif + + /* + * disable interrupts that might have been switched on in + * uhci_init. + */ + if (sc->iot && sc->ioh) + bus_space_write_2(sc->iot, sc->ioh, UHCI_INTR, 0); + + if (sc->irq_res && sc->ih) { + int err = bus_teardown_intr(self, sc->irq_res, sc->ih); + + if (err) + /* XXX or should we panic? */ + device_printf(self, "Could not tear down irq, %d\n", + err); + sc->ih = NULL; + } + if (sc->sc_bus.bdev) { + device_delete_child(self, sc->sc_bus.bdev); + sc->sc_bus.bdev = NULL; + } + if (sc->irq_res) { + bus_release_resource(self, SYS_RES_IRQ, 0, sc->irq_res); + sc->irq_res = NULL; + } + if (sc->io_res) { + bus_release_resource(self, SYS_RES_IOPORT, PCI_UHCI_BASE_REG, + sc->io_res); + sc->io_res = NULL; + sc->iot = 0; + sc->ioh = 0; + } + return 0; +} + + +static device_method_t uhci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uhci_pci_probe), + DEVMETHOD(device_attach, uhci_pci_attach), + DEVMETHOD(device_suspend, uhci_pci_suspend), + DEVMETHOD(device_resume, uhci_pci_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + {0, 0} +}; + +static driver_t uhci_driver = { + "uhci", + uhci_methods, + sizeof(uhci_softc_t), +}; + +static devclass_t uhci_devclass; + +DRIVER_MODULE(uhci, pci, uhci_driver, uhci_devclass, 0, 0); diff --git a/sys/pci/viapm.c b/sys/pci/viapm.c new file mode 100644 index 0000000..d18f64d --- /dev/null +++ b/sys/pci/viapm.c @@ -0,0 +1,924 @@ +/*- + * Copyright (c) 2001 Alcove - Nicolas Souchu + * 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$ + * + */ +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/uio.h> + +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/clock.h> /* for DELAY */ +#include <machine/resource.h> +#include <sys/rman.h> + +#include <pci/pcivar.h> +#include <pci/pcireg.h> + +#include <dev/iicbus/iiconf.h> +#include <dev/iicbus/iicbus.h> + +#include <dev/smbus/smbconf.h> +#include <dev/smbus/smbus.h> + +#include "iicbb_if.h" +#include "smbus_if.h" + +#define VIAPM_DEBUG(x) if (viapm_debug) (x) + +#ifdef DEBUG +static int viapm_debug = 1; +#else +static int viapm_debug = 0; +#endif + +#define VIA_586B_PMU_ID 0x30401106 +#define VIA_596A_PMU_ID 0x30501106 +#define VIA_596B_PMU_ID 0x30511106 +#define VIA_686A_PMU_ID 0x30571106 +#define VIA_8233_PMU_ID 0x30741106 + +#define VIAPM_INB(port) \ + ((u_char)bus_space_read_1(viapm->st, viapm->sh, port)) +#define VIAPM_OUTB(port,val) \ + (bus_space_write_1(viapm->st, viapm->sh, port, (u_char)val)) + +#define VIAPM_TYP_UNKNOWN 0 +#define VIAPM_TYP_586B_3040E 1 +#define VIAPM_TYP_586B_3040F 2 +#define VIAPM_TYP_596B 3 +#define VIAPM_TYP_686A 4 +#define VIAPM_TYP_8233 5 + +struct viapm_softc { + int type; + u_int32_t base; + bus_space_tag_t st; + bus_space_handle_t sh; + int iorid; + int irqrid; + struct resource *iores; + struct resource *irqres; + void *irqih; + + device_t iicbb; + device_t smbus; +}; + +static devclass_t viapm_devclass; +static devclass_t viapropm_devclass; + +/* + * VT82C586B definitions + */ + +#define VIAPM_586B_REVID 0x08 + +#define VIAPM_586B_3040E_BASE 0x20 +#define VIAPM_586B_3040E_ACTIV 0x4 /* 16 bits */ + +#define VIAPM_586B_3040F_BASE 0x48 +#define VIAPM_586B_3040F_ACTIV 0x41 /* 8 bits */ + +#define VIAPM_586B_OEM_REV_E 0x00 +#define VIAPM_586B_OEM_REV_F 0x01 +#define VIAPM_586B_PROD_REV_A 0x10 + +#define VIAPM_586B_BA_MASK 0x0000ff00 + +#define GPIO_DIR 0x40 +#define GPIO_VAL 0x42 +#define EXTSMI_VAL 0x44 + +#define VIAPM_SCL 0x02 /* GPIO1_VAL */ +#define VIAPM_SDA 0x04 /* GPIO2_VAL */ + +/* + * VIAPRO common definitions + */ + +#define VIAPM_PRO_BA_MASK 0x0000fff0 +#define VIAPM_PRO_SMBCTRL 0xd2 +#define VIAPM_PRO_REVID 0xd6 + +/* + * VT82C686A definitions + */ + +#define VIAPM_PRO_BASE 0x90 + +#define SMBHST 0x0 +#define SMBHSL 0x1 +#define SMBHCTRL 0x2 +#define SMBHCMD 0x3 +#define SMBHADDR 0x4 +#define SMBHDATA0 0x5 +#define SMBHDATA1 0x6 +#define SMBHBLOCK 0x7 + +#define SMBSST 0x1 +#define SMBSCTRL 0x8 +#define SMBSSDWCMD 0x9 +#define SMBSEVENT 0xa +#define SMBSDATA 0xc + +#define SMBHST_RESERVED 0xef /* reserved bits */ +#define SMBHST_FAILED 0x10 /* failed bus transaction */ +#define SMBHST_COLLID 0x08 /* bus collision */ +#define SMBHST_ERROR 0x04 /* device error */ +#define SMBHST_INTR 0x02 /* command completed */ +#define SMBHST_BUSY 0x01 /* host busy */ + +#define SMBHCTRL_START 0x40 /* start command */ +#define SMBHCTRL_PROTO 0x1c /* command protocol mask */ +#define SMBHCTRL_QUICK 0x00 +#define SMBHCTRL_SENDRECV 0x04 +#define SMBHCTRL_BYTE 0x08 +#define SMBHCTRL_WORD 0x0c +#define SMBHCTRL_BLOCK 0x14 +#define SMBHCTRL_KILL 0x02 /* stop the current transaction */ +#define SMBHCTRL_ENABLE 0x01 /* enable interrupts */ + +#define SMBSCTRL_ENABLE 0x01 /* enable slave */ + + +/* + * VIA8233 definitions + */ + +#define VIAPM_8233_BASE 0xD0 + +static int +viapm_586b_probe(device_t dev) +{ + struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev); + u_int32_t l; + u_int16_t s; + u_int8_t c; + + switch (pci_get_devid(dev)) { + case VIA_586B_PMU_ID: + + bzero(viapm, sizeof(struct viapm_softc)); + + l = pci_read_config(dev, VIAPM_586B_REVID, 1); + switch (l) { + case VIAPM_586B_OEM_REV_E: + viapm->type = VIAPM_TYP_586B_3040E; + viapm->iorid = VIAPM_586B_3040E_BASE; + + /* Activate IO block access */ + s = pci_read_config(dev, VIAPM_586B_3040E_ACTIV, 2); + pci_write_config(dev, VIAPM_586B_3040E_ACTIV, s | 0x1, 2); + break; + + case VIAPM_586B_OEM_REV_F: + case VIAPM_586B_PROD_REV_A: + default: + viapm->type = VIAPM_TYP_586B_3040F; + viapm->iorid = VIAPM_586B_3040F_BASE; + + /* Activate IO block access */ + c = pci_read_config(dev, VIAPM_586B_3040F_ACTIV, 1); + pci_write_config(dev, VIAPM_586B_3040F_ACTIV, c | 0x80, 1); + break; + } + + viapm->base = pci_read_config(dev, viapm->iorid, 4) & + VIAPM_586B_BA_MASK; + + /* + * We have to set the I/O resources by hand because it is + * described outside the viapmope of the traditional maps + */ + if (bus_set_resource(dev, SYS_RES_IOPORT, viapm->iorid, + viapm->base, 256)) { + device_printf(dev, "could not set bus resource\n"); + return ENXIO; + } + device_set_desc(dev, "VIA VT82C586B Power Management Unit"); + return 0; + + default: + break; + } + + return ENXIO; +} + + +static int +viapm_pro_probe(device_t dev) +{ + struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev); +#ifdef VIAPM_BASE_ADDR + u_int32_t l; +#endif + u_int32_t base_cfgreg; + char *desc; + + switch (pci_get_devid(dev)) { + case VIA_596A_PMU_ID: + desc = "VIA VT82C596A Power Management Unit"; + viapm->type = VIAPM_TYP_596B; + base_cfgreg = VIAPM_PRO_BASE; + goto viapro; + + case VIA_596B_PMU_ID: + desc = "VIA VT82C596B Power Management Unit"; + viapm->type = VIAPM_TYP_596B; + base_cfgreg = VIAPM_PRO_BASE; + goto viapro; + + case VIA_686A_PMU_ID: + desc = "VIA VT82C686A Power Management Unit"; + viapm->type = VIAPM_TYP_686A; + base_cfgreg = VIAPM_PRO_BASE; + goto viapro; + + case VIA_8233_PMU_ID: + desc = "VIA VT8233 Power Management Unit"; + viapm->type = VIAPM_TYP_UNKNOWN; + base_cfgreg = VIAPM_8233_BASE; + goto viapro; + + viapro: + +#ifdef VIAPM_BASE_ADDR + /* force VIAPM I/O base address */ + + /* enable the SMBus controller function */ + l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1); + pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 1, 1); + + /* write the base address */ + pci_write_config(dev, base_cfgreg, + VIAPM_BASE_ADDR & VIAPM_PRO_BA_MASK, 4); +#endif + + viapm->base = pci_read_config(dev, base_cfgreg, 4) & VIAPM_PRO_BA_MASK; + + /* + * We have to set the I/O resources by hand because it is + * described outside the viapmope of the traditional maps + */ + viapm->iorid = base_cfgreg; + if (bus_set_resource(dev, SYS_RES_IOPORT, viapm->iorid, + viapm->base, 16)) { + device_printf(dev, "could not set bus resource 0x%x\n", + viapm->base); + return ENXIO; + } + + if (1 || bootverbose) { + device_printf(dev, "SMBus I/O base at 0x%x\n", viapm->base); + } + + device_set_desc(dev, desc); + return 0; + + default: + break; + } + + return ENXIO; +} + +static int +viapm_pro_attach(device_t dev) +{ + struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev); + u_int32_t l; + + if (!(viapm->iores = bus_alloc_resource(dev, SYS_RES_IOPORT, + &viapm->iorid, 0l, ~0l, 1, RF_ACTIVE))) { + device_printf(dev, "could not allocate bus space\n"); + goto error; + } + viapm->st = rman_get_bustag(viapm->iores); + viapm->sh = rman_get_bushandle(viapm->iores); + +#if notyet + /* force irq 9 */ + l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1); + pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 0x80, 1); + + viapm->irqrid = 0; + if (!(viapm->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, + &viapm->irqrid, 9, 9, 1, + RF_SHAREABLE | RF_ACTIVE))) { + device_printf(dev, "could not allocate irq\n"); + goto error; + } + + if (bus_setup_intr(dev, viapm->irqres, INTR_TYPE_MISC, + (driver_intr_t *) viasmb_intr, viapm, &viapm->irqih)) { + device_printf(dev, "could not setup irq\n"); + goto error; + } +#endif + + if (1 | bootverbose) { + l = pci_read_config(dev, VIAPM_PRO_REVID, 1); + device_printf(dev, "SMBus revision code 0x%x\n", l); + } + + viapm->smbus = device_add_child(dev, "smbus", -1); + + /* probe and attach the smbus */ + bus_generic_attach(dev); + + /* disable slave function */ + VIAPM_OUTB(SMBSCTRL, VIAPM_INB(SMBSCTRL) & ~SMBSCTRL_ENABLE); + + /* enable the SMBus controller function */ + l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1); + pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 1, 1); + +#if notyet + /* enable interrupts */ + VIAPM_OUTB(SMBHCTRL, VIAPM_INB(SMBHCTRL) | SMBHCTRL_ENABLE); +#endif + + return 0; + +error: + if (viapm->iores) + bus_release_resource(dev, SYS_RES_IOPORT, viapm->iorid, viapm->iores); +#if notyet + if (viapm->irqres) + bus_release_resource(dev, SYS_RES_IRQ, viapm->irqrid, viapm->irqres); +#endif + + return ENXIO; +} + +static int +viapm_586b_attach(device_t dev) +{ + struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev); + + if (!(viapm->iores = bus_alloc_resource(dev, SYS_RES_IOPORT, + &viapm->iorid, 0ul, ~0ul, 1, RF_ACTIVE | RF_SHAREABLE))) { + device_printf(dev, "could not allocate bus resource\n"); + return ENXIO; + } + viapm->st = rman_get_bustag(viapm->iores); + viapm->sh = rman_get_bushandle(viapm->iores); + + VIAPM_OUTB(GPIO_DIR, VIAPM_INB(GPIO_DIR) | VIAPM_SCL | VIAPM_SDA); + + /* add generic bit-banging code */ + if (!(viapm->iicbb = device_add_child(dev, "iicbb", -1))) + goto error; + + bus_generic_attach(dev); + + return 0; + +error: + if (viapm->iores) + bus_release_resource(dev, SYS_RES_IOPORT, + viapm->iorid, viapm->iores); + return ENXIO; +} + +static int +viapm_586b_detach(device_t dev) +{ + struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev); + int error; + + bus_generic_detach(dev); + if (viapm->iicbb) { + device_delete_child(dev, viapm->iicbb); + } + + if (viapm->iores && (error = bus_release_resource(dev, SYS_RES_IOPORT, + viapm->iorid, viapm->iores))) + return (error); + + return 0; +} + +static int +viapm_pro_detach(device_t dev) +{ + struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev); + int error; + + bus_generic_detach(dev); + if (viapm->smbus) { + device_delete_child(dev, viapm->smbus); + } + + if ((error = bus_release_resource(dev, SYS_RES_IOPORT, + viapm->iorid, viapm->iores))) + return (error); + +#if notyet + if ((error = bus_release_resource(dev, SYS_RES_IRQ, + viapm->irqrid, viapm->irqres)) + return (error); +#endif + + return 0; +} + +static int +viabb_callback(device_t dev, int index, caddr_t *data) +{ + return 0; +} + +static void +viabb_setscl(device_t dev, int ctrl) +{ + struct viapm_softc *viapm = device_get_softc(dev); + u_char val; + + val = VIAPM_INB(GPIO_VAL); + + if (ctrl) + val |= VIAPM_SCL; + else + val &= ~VIAPM_SCL; + + VIAPM_OUTB(GPIO_VAL, val); + + return; +} + +static void +viabb_setsda(device_t dev, int data) +{ + struct viapm_softc *viapm = device_get_softc(dev); + u_char val; + + val = VIAPM_INB(GPIO_VAL); + + if (data) + val |= VIAPM_SDA; + else + val &= ~VIAPM_SDA; + + VIAPM_OUTB(GPIO_VAL, val); + + return; +} + +static int +viabb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) +{ + /* reset bus */ + viabb_setsda(dev, 1); + viabb_setscl(dev, 1); + + return (IIC_ENOADDR); +} + +static int +viabb_getscl(device_t dev) +{ + struct viapm_softc *viapm = device_get_softc(dev); + + return ((VIAPM_INB(EXTSMI_VAL) & VIAPM_SCL) != 0); +} + +static int +viabb_getsda(device_t dev) +{ + struct viapm_softc *viapm = device_get_softc(dev); + + return ((VIAPM_INB(EXTSMI_VAL) & VIAPM_SDA) != 0); +} + +static int +viapm_abort(struct viapm_softc *viapm) +{ + VIAPM_OUTB(SMBHCTRL, SMBHCTRL_KILL); + DELAY(10); + + return (0); +} + +static int +viapm_clear(struct viapm_softc *viapm) +{ + VIAPM_OUTB(SMBHST, SMBHST_FAILED | SMBHST_COLLID | + SMBHST_ERROR | SMBHST_INTR); + DELAY(10); + + return (0); +} + +static int +viapm_busy(struct viapm_softc *viapm) +{ + u_char sts; + + sts = VIAPM_INB(SMBHST); + + VIAPM_DEBUG(printf("viapm: idle? STS=0x%x\n", sts)); + + return (sts & SMBHST_BUSY); +} + +/* + * Poll the SMBus controller + */ +static int +viapm_wait(struct viapm_softc *viapm) +{ + int count = 10000; + u_char sts = 0; + int error; + + /* wait for command to complete and SMBus controller is idle */ + while(count--) { + DELAY(10); + sts = VIAPM_INB(SMBHST); + + /* check if the controller is processing a command */ + if (!(sts & SMBHST_BUSY) && (sts & SMBHST_INTR)) + break; + } + + VIAPM_DEBUG(printf("viapm: SMBHST=0x%x\n", sts)); + + error = SMB_ENOERR; + + if (!count) + error |= SMB_ETIMEOUT; + + if (sts & SMBHST_FAILED) + error |= SMB_EABORT; + + if (sts & SMBHST_COLLID) + error |= SMB_ENOACK; + + if (sts & SMBHST_ERROR) + error |= SMB_EBUSERR; + + if (error != SMB_ENOERR) + viapm_abort(viapm); + + viapm_clear(viapm); + + return (error); +} + +static int +viasmb_callback(device_t dev, int index, caddr_t *data) +{ + int error = 0; + + switch (index) { + case SMB_REQUEST_BUS: + case SMB_RELEASE_BUS: + /* ok, bus allocation accepted */ + break; + default: + error = EINVAL; + } + + return (error); +} + +static int +viasmb_quick(device_t dev, u_char slave, int how) +{ + struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev); + int error; + + viapm_clear(viapm); + if (viapm_busy(viapm)) + return (EBUSY); + + switch (how) { + case SMB_QWRITE: + VIAPM_DEBUG(printf("viapm: QWRITE to 0x%x", slave)); + VIAPM_OUTB(SMBHADDR, slave & ~LSB); + break; + case SMB_QREAD: + VIAPM_DEBUG(printf("viapm: QREAD to 0x%x", slave)); + VIAPM_OUTB(SMBHADDR, slave | LSB); + break; + default: + panic("%s: unknown QUICK command (%x)!", __FUNCTION__, + how); + } + + VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_QUICK); + + error = viapm_wait(viapm); + + return (error); +} + +static int +viasmb_sendb(device_t dev, u_char slave, char byte) +{ + struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev); + int error; + + viapm_clear(viapm); + if (viapm_busy(viapm)) + return (EBUSY); + + VIAPM_OUTB(SMBHADDR, slave & ~ LSB); + VIAPM_OUTB(SMBHCMD, byte); + + VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_SENDRECV); + + error = viapm_wait(viapm); + + VIAPM_DEBUG(printf("viapm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error)); + + return (error); +} + +static int +viasmb_recvb(device_t dev, u_char slave, char *byte) +{ + struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev); + int error; + + viapm_clear(viapm); + if (viapm_busy(viapm)) + return (EBUSY); + + VIAPM_OUTB(SMBHADDR, slave | LSB); + + VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_SENDRECV); + + if ((error = viapm_wait(viapm)) == SMB_ENOERR) + *byte = VIAPM_INB(SMBHDATA0); + + VIAPM_DEBUG(printf("viapm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error)); + + return (error); +} + +static int +viasmb_writeb(device_t dev, u_char slave, char cmd, char byte) +{ + struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev); + int error; + + viapm_clear(viapm); + if (viapm_busy(viapm)) + return (EBUSY); + + VIAPM_OUTB(SMBHADDR, slave & ~ LSB); + VIAPM_OUTB(SMBHCMD, cmd); + VIAPM_OUTB(SMBHDATA0, byte); + + VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BYTE); + + error = viapm_wait(viapm); + + VIAPM_DEBUG(printf("viapm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error)); + + return (error); +} + +static int +viasmb_readb(device_t dev, u_char slave, char cmd, char *byte) +{ + struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev); + int error; + + viapm_clear(viapm); + if (viapm_busy(viapm)) + return (EBUSY); + + VIAPM_OUTB(SMBHADDR, slave | LSB); + VIAPM_OUTB(SMBHCMD, cmd); + + VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BYTE); + + if ((error = viapm_wait(viapm)) == SMB_ENOERR) + *byte = VIAPM_INB(SMBHDATA0); + + VIAPM_DEBUG(printf("viapm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error)); + + return (error); +} + +static int +viasmb_writew(device_t dev, u_char slave, char cmd, short word) +{ + struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev); + int error; + + viapm_clear(viapm); + if (viapm_busy(viapm)) + return (EBUSY); + + VIAPM_OUTB(SMBHADDR, slave & ~ LSB); + VIAPM_OUTB(SMBHCMD, cmd); + VIAPM_OUTB(SMBHDATA0, word & 0x00ff); + VIAPM_OUTB(SMBHDATA1, (word & 0xff00) >> 8); + + VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_WORD); + + error = viapm_wait(viapm); + + VIAPM_DEBUG(printf("viapm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error)); + + return (error); +} + +static int +viasmb_readw(device_t dev, u_char slave, char cmd, short *word) +{ + struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev); + int error; + u_char high, low; + + viapm_clear(viapm); + if (viapm_busy(viapm)) + return (EBUSY); + + VIAPM_OUTB(SMBHADDR, slave | LSB); + VIAPM_OUTB(SMBHCMD, cmd); + + VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_WORD); + + if ((error = viapm_wait(viapm)) == SMB_ENOERR) { + low = VIAPM_INB(SMBHDATA0); + high = VIAPM_INB(SMBHDATA1); + + *word = ((high & 0xff) << 8) | (low & 0xff); + } + + VIAPM_DEBUG(printf("viapm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error)); + + return (error); +} + +static int +viasmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) +{ + struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev); + u_char remain, len, i; + int error = SMB_ENOERR; + + viapm_clear(viapm); + if (viapm_busy(viapm)) + return (EBUSY); + + remain = count; + while (remain) { + len = min(remain, 32); + + VIAPM_OUTB(SMBHADDR, slave & ~LSB); + VIAPM_OUTB(SMBHCMD, cmd); + VIAPM_OUTB(SMBHDATA0, len); + i = VIAPM_INB(SMBHCTRL); + + /* fill the 32-byte internal buffer */ + for (i=0; i<len; i++) { + VIAPM_OUTB(SMBHBLOCK, buf[count-remain+i]); + DELAY(2); + } + VIAPM_OUTB(SMBHCMD, cmd); + VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BLOCK); + + if ((error = viapm_wait(viapm)) != SMB_ENOERR) + goto error; + + remain -= len; + } + +error: + VIAPM_DEBUG(printf("viapm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error)); + + return (error); + +} + +static int +viasmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf) +{ + struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev); + u_char remain, len, i; + int error = SMB_ENOERR; + + viapm_clear(viapm); + if (viapm_busy(viapm)) + return (EBUSY); + + remain = count; + while (remain) { + VIAPM_OUTB(SMBHADDR, slave | LSB); + VIAPM_OUTB(SMBHCMD, cmd); + VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BLOCK); + + if ((error = viapm_wait(viapm)) != SMB_ENOERR) + goto error; + + len = VIAPM_INB(SMBHDATA0); + i = VIAPM_INB(SMBHCTRL); /* reset counter */ + + len = min(len, remain); + + /* read the 32-byte internal buffer */ + for (i=0; i<len; i++) { + buf[count-remain+i] = VIAPM_INB(SMBHBLOCK); + DELAY(2); + } + + remain -= len; + } +error: + VIAPM_DEBUG(printf("viapm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error)); + + return (error); +} + +static device_method_t viapm_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, viapm_586b_probe), + DEVMETHOD(device_attach, viapm_586b_attach), + DEVMETHOD(device_detach, viapm_586b_detach), + + /* iicbb interface */ + DEVMETHOD(iicbb_callback, viabb_callback), + DEVMETHOD(iicbb_setscl, viabb_setscl), + DEVMETHOD(iicbb_setsda, viabb_setsda), + DEVMETHOD(iicbb_getscl, viabb_getscl), + DEVMETHOD(iicbb_getsda, viabb_getsda), + DEVMETHOD(iicbb_reset, viabb_reset), + + { 0, 0 } +}; + +static driver_t viapm_driver = { + "viapm", + viapm_methods, + sizeof(struct viapm_softc), +}; + +static device_method_t viapropm_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, viapm_pro_probe), + DEVMETHOD(device_attach, viapm_pro_attach), + DEVMETHOD(device_detach, viapm_pro_detach), + + /* smbus interface */ + DEVMETHOD(smbus_callback, viasmb_callback), + DEVMETHOD(smbus_quick, viasmb_quick), + DEVMETHOD(smbus_sendb, viasmb_sendb), + DEVMETHOD(smbus_recvb, viasmb_recvb), + DEVMETHOD(smbus_writeb, viasmb_writeb), + DEVMETHOD(smbus_readb, viasmb_readb), + DEVMETHOD(smbus_writew, viasmb_writew), + DEVMETHOD(smbus_readw, viasmb_readw), + DEVMETHOD(smbus_bwrite, viasmb_bwrite), + DEVMETHOD(smbus_bread, viasmb_bread), + + { 0, 0 } +}; + +static driver_t viapropm_driver = { + "viapropm", + viapropm_methods, + sizeof(struct viapm_softc), +}; + +DRIVER_MODULE(viapm, pci, viapm_driver, viapm_devclass, 0, 0); +DRIVER_MODULE(viapropm, pci, viapropm_driver, viapropm_devclass, 0, 0); + +MODULE_DEPEND(viapm, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER); +MODULE_DEPEND(viapropm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); +MODULE_VERSION(viapm, 1); diff --git a/sys/pci/xmaciireg.h b/sys/pci/xmaciireg.h new file mode 100644 index 0000000..137f47ee --- /dev/null +++ b/sys/pci/xmaciireg.h @@ -0,0 +1,403 @@ +/* + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul <wpaul@ctr.columbia.edu>. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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$ + */ + +/* + * Registers and data structures for the XaQti Corporation XMAC II + * Gigabit Ethernet MAC. Datasheet is available from http://www.xaqti.com. + * The XMAC can be programmed for 16-bit or 32-bit register access modes. + * The SysKonnect gigabit ethernet adapters use 16-bit mode, so that's + * how the registers are laid out here. + */ + +#define XM_DEVICEID 0x00E0AE20 +#define XM_XAQTI_OUI 0x00E0AE + +#define XM_XMAC_REV(x) (((x) & 0x000000E0) >> 5) + +#define XM_XMAC_REV_B2 0x0 +#define XM_XMAC_REV_C1 0x1 + +#define XM_MMUCMD 0x0000 +#define XM_POFF 0x0008 +#define XM_BURST 0x000C +#define XM_VLAN_TAGLEV1 0x0010 +#define XM_VLAN_TAGLEV2 0x0014 +#define XM_TXCMD 0x0020 +#define XM_TX_RETRYLIMIT 0x0024 +#define XM_TX_SLOTTIME 0x0028 +#define XM_TX_IPG 0x003C +#define XM_RXCMD 0x0030 +#define XM_PHY_ADDR 0x0034 +#define XM_PHY_DATA 0x0038 +#define XM_GPIO 0x0040 +#define XM_IMR 0x0044 +#define XM_ISR 0x0048 +#define XM_HWCFG 0x004C +#define XM_TX_LOWAT 0x0060 +#define XM_TX_HIWAT 0x0062 +#define XM_TX_REQTHRESH_LO 0x0064 +#define XM_TX_REQTHRESH_HI 0x0066 +#define XM_TX_REQTHRESH XM_TX_REQTHRESH_LO +#define XM_PAUSEDST0 0x0068 +#define XM_PAUSEDST1 0x006A +#define XM_PAUSEDST2 0x006C +#define XM_CTLPARM_LO 0x0070 +#define XM_CTLPARM_HI 0x0072 +#define XM_CTLPARM XM_CTLPARM_LO +#define XM_OPCODE_PAUSE_TIMER 0x0074 +#define XM_TXSTAT_LIFO 0x0078 + +/* + * Perfect filter registers. The XMAC has a table of 16 perfect + * filter entries, spaced 8 bytes apart. This is in addition to + * the station address registers, which appear below. + */ +#define XM_RXFILT_BASE 0x0080 +#define XM_RXFILT_END 0x0107 +#define XM_RXFILT_MAX 16 +#define XM_RXFILT_ENTRY(ent) (XM_RXFILT_BASE + ((ent * 8))) + +/* Primary station address. */ +#define XM_PAR0 0x0108 +#define XM_PAR1 0x010A +#define XM_PAR2 0x010C + +/* 64-bit multicast hash table registers */ +#define XM_MAR0 0x0110 +#define XM_MAR1 0x0112 +#define XM_MAR2 0x0114 +#define XM_MAR3 0x0116 +#define XM_RX_LOWAT 0x0118 +#define XM_RX_HIWAT 0x011A +#define XM_RX_REQTHRESH_LO 0x011C +#define XM_RX_REQTHRESH_HI 0x011E +#define XM_RX_REQTHRESH XM_RX_REQTHRESH_LO +#define XM_DEVID_LO 0x0120 +#define XM_DEVID_HI 0x0122 +#define XM_DEVID XM_DEVID_LO +#define XM_MODE_LO 0x0124 +#define XM_MODE_HI 0x0126 +#define XM_MODE XM_MODE_LO +#define XM_LASTSRC0 0x0128 +#define XM_LASTSRC1 0x012A +#define XM_LASTSRC2 0x012C +#define XM_TSTAMP_READ 0x0130 +#define XM_TSTAMP_LOAD 0x0134 +#define XM_STATS_CMD 0x0200 +#define XM_RXCNT_EVENT_LO 0x0204 +#define XM_RXCNT_EVENT_HI 0x0206 +#define XM_RXCNT_EVENT XM_RXCNT_EVENT_LO +#define XM_TXCNT_EVENT_LO 0x0208 +#define XM_TXCNT_EVENT_HI 0x020A +#define XM_TXCNT_EVENT XM_TXCNT_EVENT_LO +#define XM_RXCNT_EVMASK_LO 0x020C +#define XM_RXCNT_EVMASK_HI 0x020E +#define XM_RXCNT_EVMASK XM_RXCNT_EVMASK_LO +#define XM_TXCNT_EVMASK_LO 0x0210 +#define XM_TXCNT_EVMASK_HI 0x0212 +#define XM_TXCNT_EVMASK XM_TXCNT_EVMASK_LO + +/* Statistics command register */ +#define XM_STATCMD_CLR_TX 0x0001 +#define XM_STATCMD_CLR_RX 0x0002 +#define XM_STATCMD_COPY_TX 0x0004 +#define XM_STATCMD_COPY_RX 0x0008 +#define XM_STATCMD_SNAP_TX 0x0010 +#define XM_STATCMD_SNAP_RX 0x0020 + +/* TX statistics registers */ +#define XM_TXSTATS_PKTSOK 0x280 +#define XM_TXSTATS_BYTESOK_HI 0x284 +#define XM_TXSTATS_BYTESOK_LO 0x288 +#define XM_TXSTATS_BCASTSOK 0x28C +#define XM_TXSTATS_MCASTSOK 0x290 +#define XM_TXSTATS_UCASTSOK 0x294 +#define XM_TXSTATS_GIANTS 0x298 +#define XM_TXSTATS_BURSTCNT 0x29C +#define XM_TXSTATS_PAUSEPKTS 0x2A0 +#define XM_TXSTATS_MACCTLPKTS 0x2A4 +#define XM_TXSTATS_SINGLECOLS 0x2A8 +#define XM_TXSTATS_MULTICOLS 0x2AC +#define XM_TXSTATS_EXCESSCOLS 0x2B0 +#define XM_TXSTATS_LATECOLS 0x2B4 +#define XM_TXSTATS_DEFER 0x2B8 +#define XM_TXSTATS_EXCESSDEFER 0x2BC +#define XM_TXSTATS_UNDERRUN 0x2C0 +#define XM_TXSTATS_CARRIERSENSE 0x2C4 +#define XM_TXSTATS_UTILIZATION 0x2C8 +#define XM_TXSTATS_64 0x2D0 +#define XM_TXSTATS_65_127 0x2D4 +#define XM_TXSTATS_128_255 0x2D8 +#define XM_TXSTATS_256_511 0x2DC +#define XM_TXSTATS_512_1023 0x2E0 +#define XM_TXSTATS_1024_MAX 0x2E4 + +/* RX statistics registers */ +#define XM_RXSTATS_PKTSOK 0x300 +#define XM_RXSTATS_BYTESOK_HI 0x304 +#define XM_RXSTATS_BYTESOK_LO 0x308 +#define XM_RXSTATS_BCASTSOK 0x30C +#define XM_RXSTATS_MCASTSOK 0x310 +#define XM_RXSTATS_UCASTSOK 0x314 +#define XM_RXSTATS_PAUSEPKTS 0x318 +#define XM_RXSTATS_MACCTLPKTS 0x31C +#define XM_RXSTATS_BADPAUSEPKTS 0x320 +#define XM_RXSTATS_BADMACCTLPKTS 0x324 +#define XM_RXSTATS_BURSTCNT 0x328 +#define XM_RXSTATS_MISSEDPKTS 0x32C +#define XM_RXSTATS_FRAMEERRS 0x330 +#define XM_RXSTATS_OVERRUN 0x334 +#define XM_RXSTATS_JABBER 0x338 +#define XM_RXSTATS_CARRLOSS 0x33C +#define XM_RXSTATS_INRNGLENERR 0x340 +#define XM_RXSTATS_SYMERR 0x344 +#define XM_RXSTATS_SHORTEVENT 0x348 +#define XM_RXSTATS_RUNTS 0x34C +#define XM_RXSTATS_GIANTS 0x350 +#define XM_RXSTATS_CRCERRS 0x354 +#define XM_RXSTATS_CEXTERRS 0x35C +#define XM_RXSTATS_UTILIZATION 0x360 +#define XM_RXSTATS_64 0x368 +#define XM_RXSTATS_65_127 0x36C +#define XM_RXSTATS_128_255 0x370 +#define XM_RXSTATS_256_511 0x374 +#define XM_RXSTATS_512_1023 0x378 +#define XM_RXSTATS_1024_MAX 0x37C + +#define XM_MMUCMD_TX_ENB 0x0001 +#define XM_MMUCMD_RX_ENB 0x0002 +#define XM_MMUCMD_GMIILOOP 0x0004 +#define XM_MMUCMD_RATECTL 0x0008 +#define XM_MMUCMD_GMIIFDX 0x0010 +#define XM_MMUCMD_NO_MGMT_PRMB 0x0020 +#define XM_MMUCMD_SIMCOL 0x0040 +#define XM_MMUCMD_FORCETX 0x0080 +#define XM_MMUCMD_LOOPENB 0x0200 +#define XM_MMUCMD_IGNPAUSE 0x0400 +#define XM_MMUCMD_PHYBUSY 0x0800 +#define XM_MMUCMD_PHYDATARDY 0x1000 + +#define XM_TXCMD_AUTOPAD 0x0001 +#define XM_TXCMD_NOCRC 0x0002 +#define XM_TXCMD_NOPREAMBLE 0x0004 +#define XM_TXCMD_NOGIGAMODE 0x0008 +#define XM_TXCMD_SAMPLELINE 0x0010 +#define XM_TXCMD_ENCBYPASS 0x0020 +#define XM_TXCMD_XMITBK2BK 0x0040 +#define XM_TXCMD_FAIRSHARE 0x0080 + +#define XM_RXCMD_DISABLE_CEXT 0x0001 +#define XM_RXCMD_STRIPPAD 0x0002 +#define XM_RXCMD_SAMPLELINE 0x0004 +#define XM_RXCMD_SELFRX 0x0008 +#define XM_RXCMD_STRIPFCS 0x0010 +#define XM_RXCMD_TRANSPARENT 0x0020 +#define XM_RXCMD_IPGCAPTURE 0x0040 +#define XM_RXCMD_BIGPKTOK 0x0080 +#define XM_RXCMD_LENERROK 0x0100 + +#define XM_GPIO_GP0_SET 0x0001 +#define XM_GPIO_RESETSTATS 0x0004 +#define XM_GPIO_RESETMAC 0x0008 +#define XM_GPIO_FORCEINT 0x0020 +#define XM_GPIO_ANEGINPROG 0x0040 + +#define XM_IMR_RX_EOF 0x0001 +#define XM_IMR_TX_EOF 0x0002 +#define XM_IMR_TX_UNDERRUN 0x0004 +#define XM_IMR_RX_OVERRUN 0x0008 +#define XM_IMR_TX_STATS_OFLOW 0x0010 +#define XM_IMR_RX_STATS_OFLOW 0x0020 +#define XM_IMR_TSTAMP_OFLOW 0x0040 +#define XM_IMR_AUTONEG_DONE 0x0080 +#define XM_IMR_NEXTPAGE_RDY 0x0100 +#define XM_IMR_PAGE_RECEIVED 0x0200 +#define XM_IMR_LP_REQCFG 0x0400 +#define XM_IMR_GP0_SET 0x0800 +#define XM_IMR_FORCEINTR 0x1000 +#define XM_IMR_TX_ABORT 0x2000 +#define XM_IMR_LINKEVENT 0x4000 + +#define XM_INTRS \ + (~(XM_IMR_GP0_SET|XM_IMR_AUTONEG_DONE|XM_IMR_TX_UNDERRUN)) + +#define XM_ISR_RX_EOF 0x0001 +#define XM_ISR_TX_EOF 0x0002 +#define XM_ISR_TX_UNDERRUN 0x0004 +#define XM_ISR_RX_OVERRUN 0x0008 +#define XM_ISR_TX_STATS_OFLOW 0x0010 +#define XM_ISR_RX_STATS_OFLOW 0x0020 +#define XM_ISR_TSTAMP_OFLOW 0x0040 +#define XM_ISR_AUTONEG_DONE 0x0080 +#define XM_ISR_NEXTPAGE_RDY 0x0100 +#define XM_ISR_PAGE_RECEIVED 0x0200 +#define XM_ISR_LP_REQCFG 0x0400 +#define XM_ISR_GP0_SET 0x0800 +#define XM_ISR_FORCEINTR 0x1000 +#define XM_ISR_TX_ABORT 0x2000 +#define XM_ISR_LINKEVENT 0x4000 + +#define XM_HWCFG_GENEOP 0x0008 +#define XM_HWCFG_SIGSTATCKH 0x0004 +#define XM_HWCFG_GMIIMODE 0x0001 + +#define XM_MODE_FLUSH_RXFIFO 0x00000001 +#define XM_MODE_FLUSH_TXFIFO 0x00000002 +#define XM_MODE_BIGENDIAN 0x00000004 +#define XM_MODE_RX_PROMISC 0x00000008 +#define XM_MODE_RX_NOBROAD 0x00000010 +#define XM_MODE_RX_NOMULTI 0x00000020 +#define XM_MODE_RX_NOUNI 0x00000040 +#define XM_MODE_RX_BADFRAMES 0x00000080 +#define XM_MODE_RX_CRCERRS 0x00000100 +#define XM_MODE_RX_GIANTS 0x00000200 +#define XM_MODE_RX_INRANGELEN 0x00000400 +#define XM_MODE_RX_RUNTS 0x00000800 +#define XM_MODE_RX_MACCTL 0x00001000 +#define XM_MODE_RX_USE_PERFECT 0x00002000 +#define XM_MODE_RX_USE_STATION 0x00004000 +#define XM_MODE_RX_USE_HASH 0x00008000 +#define XM_MODE_RX_ADDRPAIR 0x00010000 +#define XM_MODE_PAUSEONHI 0x00020000 +#define XM_MODE_PAUSEONLO 0x00040000 +#define XM_MODE_TIMESTAMP 0x00080000 +#define XM_MODE_SENDPAUSE 0x00100000 +#define XM_MODE_SENDCONTINUOUS 0x00200000 +#define XM_MODE_LE_STATUSWORD 0x00400000 +#define XM_MODE_AUTOFIFOPAUSE 0x00800000 +#define XM_MODE_EXPAUSEGEN 0x02000000 +#define XM_MODE_RX_INVERSE 0x04000000 + +#define XM_RXSTAT_MACCTL 0x00000001 +#define XM_RXSTAT_ERRFRAME 0x00000002 +#define XM_RXSTAT_CRCERR 0x00000004 +#define XM_RXSTAT_GIANT 0x00000008 +#define XM_RXSTAT_RUNT 0x00000010 +#define XM_RXSTAT_FRAMEERR 0x00000020 +#define XM_RXSTAT_INRANGEERR 0x00000040 +#define XM_RXSTAT_CARRIERERR 0x00000080 +#define XM_RXSTAT_COLLERR 0x00000100 +#define XM_RXSTAT_802_3 0x00000200 +#define XM_RXSTAT_CARREXTERR 0x00000400 +#define XM_RXSTAT_BURSTMODE 0x00000800 +#define XM_RXSTAT_UNICAST 0x00002000 +#define XM_RXSTAT_MULTICAST 0x00004000 +#define XM_RXSTAT_BROADCAST 0x00008000 +#define XM_RXSTAT_VLAN_LEV1 0x00010000 +#define XM_RXSTAT_VLAN_LEV2 0x00020000 +#define XM_RXSTAT_LEN 0xFFFC0000 + +/* + * XMAC PHY registers, indirectly accessed through + * XM_PHY_ADDR and XM_PHY_REG. + */ + +#define XM_PHY_BMCR 0x0000 /* control */ +#define XM_PHY_BMSR 0x0001 /* status */ +#define XM_PHY_VENID 0x0002 /* vendor id */ +#define XM_PHY_DEVID 0x0003 /* device id */ +#define XM_PHY_ANAR 0x0004 /* autoneg advertisenemt */ +#define XM_PHY_LPAR 0x0005 /* link partner ability */ +#define XM_PHY_ANEXP 0x0006 /* autoneg expansion */ +#define XM_PHY_NEXTP 0x0007 /* nextpage */ +#define XM_PHY_LPNEXTP 0x0008 /* link partner's nextpage */ +#define XM_PHY_EXTSTS 0x000F /* extented status */ +#define XM_PHY_RESAB 0x0010 /* resolved ability */ + +#define XM_BMCR_DUPLEX 0x0100 +#define XM_BMCR_RENEGOTIATE 0x0200 +#define XM_BMCR_AUTONEGENBL 0x1000 +#define XM_BMCR_LOOPBACK 0x4000 +#define XM_BMCR_RESET 0x8000 + +#define XM_BMSR_EXTCAP 0x0001 +#define XM_BMSR_LINKSTAT 0x0004 +#define XM_BMSR_AUTONEGABLE 0x0008 +#define XM_BMSR_REMFAULT 0x0010 +#define XM_BMSR_AUTONEGDONE 0x0020 +#define XM_BMSR_EXTSTAT 0x0100 + +#define XM_VENID_XAQTI 0xD14C +#define XM_DEVID_XMAC 0x0002 + +#define XM_ANAR_FULLDUPLEX 0x0020 +#define XM_ANAR_HALFDUPLEX 0x0040 +#define XM_ANAR_PAUSEBITS 0x0180 +#define XM_ANAR_REMFAULTBITS 0x1800 +#define XM_ANAR_ACK 0x4000 +#define XM_ANAR_NEXTPAGE 0x8000 + +#define XM_LPAR_FULLDUPLEX 0x0020 +#define XM_LPAR_HALFDUPLEX 0x0040 +#define XM_LPAR_PAUSEBITS 0x0180 +#define XM_LPAR_REMFAULTBITS 0x1800 +#define XM_LPAR_ACK 0x4000 +#define XM_LPAR_NEXTPAGE 0x8000 + +#define XM_PAUSE_NOPAUSE 0x0000 +#define XM_PAUSE_SYMPAUSE 0x0080 +#define XM_PAUSE_ASYMPAUSE 0x0100 +#define XM_PAUSE_BOTH 0x0180 + +#define XM_REMFAULT_LINKOK 0x0000 +#define XM_REMFAULT_LINKFAIL 0x0800 +#define XM_REMFAULT_OFFLINE 0x1000 +#define XM_REMFAULT_ANEGERR 0x1800 + +#define XM_ANEXP_GOTPAGE 0x0002 +#define XM_ANEXP_NEXTPAGE_SELF 0x0004 +#define XM_ANEXP_NEXTPAGE_LP 0x0008 + +#define XM_NEXTP_MESSAGE 0x07FF +#define XM_NEXTP_TOGGLE 0x0800 +#define XM_NEXTP_ACK2 0x1000 +#define XM_NEXTP_MPAGE 0x2000 +#define XM_NEXTP_ACK1 0x4000 +#define XM_NEXTP_NPAGE 0x8000 + +#define XM_LPNEXTP_MESSAGE 0x07FF +#define XM_LPNEXTP_TOGGLE 0x0800 +#define XM_LPNEXTP_ACK2 0x1000 +#define XM_LPNEXTP_MPAGE 0x2000 +#define XM_LPNEXTP_ACK1 0x4000 +#define XM_LPNEXTP_NPAGE 0x8000 + +#define XM_EXTSTS_HALFDUPLEX 0x4000 +#define XM_EXTSTS_FULLDUPLEX 0x8000 + +#define XM_RESAB_PAUSEMISMATCH 0x0008 +#define XM_RESAB_ABLMISMATCH 0x0010 +#define XM_RESAB_FDMODESEL 0x0020 +#define XM_RESAB_HDMODESEL 0x0040 +#define XM_RESAB_PAUSEBITS 0x0180 diff --git a/sys/pci/xrpu.c b/sys/pci/xrpu.c new file mode 100644 index 0000000..dc21896 --- /dev/null +++ b/sys/pci/xrpu.c @@ -0,0 +1,271 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + * + * $FreeBSD$ + * + * A very simple device driver for PCI cards based on Xilinx 6200 series + * FPGA/RPU devices. Current Functionality is to allow you to open and + * mmap the entire thing into your program. + * + * Hardware currently supported: + * www.vcc.com HotWorks 1 6216 based card. + * + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/timetc.h> +#include <sys/timepps.h> +#include <sys/xrpuio.h> +#include <sys/bus.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> +#include <pci/pcireg.h> +#include <pci/pcivar.h> +#include "pci_if.h" + +/* + * Device driver initialization stuff + */ + +static d_open_t xrpu_open; +static d_close_t xrpu_close; +static d_ioctl_t xrpu_ioctl; +static d_mmap_t xrpu_mmap; + +#define CDEV_MAJOR 100 +static struct cdevsw xrpu_cdevsw = { + /* open */ xrpu_open, + /* close */ xrpu_close, + /* read */ noread, + /* write */ nowrite, + /* ioctl */ xrpu_ioctl, + /* poll */ nopoll, + /* mmap */ xrpu_mmap, + /* strategy */ nostrategy, + /* name */ "xrpu", + /* maj */ CDEV_MAJOR, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ 0, +}; + +static MALLOC_DEFINE(M_XRPU, "xrpu", "XRPU related"); + +static devclass_t xrpu_devclass; + +#define dev2unit(devt) (minor(devt) & 0xff) +#define dev2pps(devt) ((minor(devt) >> 16)-1) + +struct softc { + enum { NORMAL, TIMECOUNTER } mode; + vm_offset_t virbase, physbase; + u_int *virbase62; + struct timecounter tc; + u_int *trigger, *latch, dummy; + struct pps_state pps[XRPU_MAX_PPS]; + u_int *assert[XRPU_MAX_PPS], *clear[XRPU_MAX_PPS]; +}; + +static unsigned +xrpu_get_timecount(struct timecounter *tc) +{ + struct softc *sc = tc->tc_priv; + + sc->dummy += *sc->trigger; + return (*sc->latch & tc->tc_counter_mask); +} + +static void +xrpu_poll_pps(struct timecounter *tc) +{ + struct softc *sc = tc->tc_priv; + int i, j; + unsigned count1, ppscount; + + for (i = 0; i < XRPU_MAX_PPS; i++) { + if (sc->assert[i]) { + pps_capture(&sc->pps[i]); + ppscount = *(sc->assert[i]) & tc->tc_counter_mask; + j = 0; + do { + count1 = ppscount; + ppscount = *(sc->assert[i]) & tc->tc_counter_mask; + } while (ppscount != count1 && ++j < 5); + sc->pps[i].capcount = ppscount; + pps_event(&sc->pps[i], PPS_CAPTUREASSERT); + } + if (sc->clear[i]) { + pps_capture(&sc->pps[i]); + j = 0; + ppscount = *(sc->clear[i]) & tc->tc_counter_mask; + do { + count1 = ppscount; + ppscount = *(sc->clear[i]) & tc->tc_counter_mask; + } while (ppscount != count1 && ++j < 5); + sc->pps[i].capcount = ppscount; + pps_event(&sc->pps[i], PPS_CAPTURECLEAR); + } + } +} + +static int +xrpu_open(dev_t dev, int flag, int mode, struct thread *td) +{ + struct softc *sc = devclass_get_softc(xrpu_devclass, dev2unit(dev)); + + if (!sc) + return (ENXIO); + dev->si_drv1 = sc; + return (0); +} + +static int +xrpu_close(dev_t dev, int flag, int mode, struct thread *td) +{ + return (0); +} + +static int +xrpu_mmap(dev_t dev, vm_offset_t offset, int nprot) +{ + struct softc *sc = dev->si_drv1; + if (offset >= 0x1000000) + return (-1); + return (i386_btop(sc->physbase + offset)); +} + +static int +xrpu_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct thread *tdr) +{ + struct softc *sc = dev->si_drv1; + int i, error; + + if (sc->mode == TIMECOUNTER) { + i = dev2pps(dev); + if (i < 0 || i >= XRPU_MAX_PPS) + return ENODEV; + error = pps_ioctl(cmd, arg, &sc->pps[i]); + return (error); + } + + if (cmd == XRPU_IOC_TIMECOUNTING) { + struct xrpu_timecounting *xt = (struct xrpu_timecounting *)arg; + + /* Name SHALL be zero terminated */ + xt->xt_name[sizeof xt->xt_name - 1] = '\0'; + i = strlen(xt->xt_name); + sc->tc.tc_name = (char *)malloc(i + 1, M_XRPU, M_WAITOK); + strcpy(sc->tc.tc_name, xt->xt_name); + sc->tc.tc_frequency = xt->xt_frequency; + sc->tc.tc_get_timecount = xrpu_get_timecount; + sc->tc.tc_poll_pps = xrpu_poll_pps; + sc->tc.tc_priv = sc; + sc->tc.tc_counter_mask = xt->xt_mask; + sc->trigger = sc->virbase62 + xt->xt_addr_trigger; + sc->latch = sc->virbase62 + xt->xt_addr_latch; + + for (i = 0; i < XRPU_MAX_PPS; i++) { + if (xt->xt_pps[i].xt_addr_assert == 0 + && xt->xt_pps[i].xt_addr_clear == 0) + continue; + make_dev(&xrpu_cdevsw, (i+1)<<16, + UID_ROOT, GID_WHEEL, 0600, "xpps%d", i); + sc->pps[i].ppscap = 0; + if (xt->xt_pps[i].xt_addr_assert) { + sc->assert[i] = sc->virbase62 + xt->xt_pps[i].xt_addr_assert; + sc->pps[i].ppscap |= PPS_CAPTUREASSERT; + } + if (xt->xt_pps[i].xt_addr_clear) { + sc->clear[i] = sc->virbase62 + xt->xt_pps[i].xt_addr_clear; + sc->pps[i].ppscap |= PPS_CAPTURECLEAR; + } + pps_init(&sc->pps[i]); + } + sc->mode = TIMECOUNTER; + tc_init(&sc->tc); + return (0); + } + error = ENOTTY; + return (error); +} + +/* + * PCI initialization stuff + */ + +static int +xrpu_probe(device_t self) +{ + char *desc; + + desc = NULL; + switch (pci_get_devid(self)) { + case 0x6216133e: + desc = "VCC Hotworks-I xc6216"; + break; + } + if (desc == NULL) + return ENXIO; + + device_set_desc(self, desc); + return 0; +} + +static int +xrpu_attach(device_t self) +{ + struct softc *sc; + struct resource *res; + int rid, unit; + + unit = device_get_unit(self); + sc = device_get_softc(self); + sc->mode = NORMAL; + rid = PCIR_MAPS; + res = bus_alloc_resource(self, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (res == NULL) { + device_printf(self, "Could not map memory\n"); + return ENXIO; + } + sc->virbase = (vm_offset_t)rman_get_virtual(res); + sc->physbase = rman_get_start(res); + sc->virbase62 = (u_int *)(sc->virbase + 0x800000); + + if (bootverbose) + printf("Mapped physbase %#lx to virbase %#lx\n", + (u_long)sc->physbase, (u_long)sc->virbase); + + make_dev(&xrpu_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "xrpu%d", unit); + return 0; +} + +static device_method_t xrpu_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, xrpu_probe), + DEVMETHOD(device_attach, xrpu_attach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + {0, 0} +}; + +static driver_t xrpu_driver = { + "xrpu", + xrpu_methods, + sizeof(struct softc) +}; + + +DRIVER_MODULE(xrpu, pci, xrpu_driver, xrpu_devclass, 0, 0); |