Index: head/sys/dev/cardbus/cardbus.c =================================================================== --- head/sys/dev/cardbus/cardbus.c (revision 82377) +++ head/sys/dev/cardbus/cardbus.c (revision 82378) @@ -1,995 +1,1252 @@ /* * Copyright (c) 2000,2001 Jonathan Chen. * 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, * without modification, immediately at the beginning of the file. * 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$ */ /* * Cardbus Bus Driver * * much of the bus code was stolen directly from sys/pci/pci.c * (Copyright (c) 1997, Stefan Esser ) * * Written by Jonathan Chen */ #define CARDBUS_DEBUG #include #include #include #include #include #include #include #include -#include -#include +#include +#include #include #include #include #include #include "power_if.h" #include "card_if.h" #include "pcib_if.h" #if defined CARDBUS_DEBUG #define DPRINTF(a) printf a #define DEVPRINTF(x) device_printf x #else #define DPRINTF(a) #define DEVPRINTF(x) #endif #if !defined(lint) static const char rcsid[] = "$FreeBSD$"; #endif -struct cardbus_quirk { - u_int32_t devid; /* Vendor/device of the card */ - int type; -#define CARDBUS_QUIRK_MAP_REG 1 /* PCI map register in weird place */ - int arg1; - int arg2; -}; - -struct cardbus_quirk cardbus_quirks[] = { - { 0 } -}; - static int cardbus_probe(device_t cbdev); static int cardbus_attach(device_t cbdev); static int cardbus_detach(device_t cbdev); static void device_setup_regs(device_t brdev, int b, int s, int f, pcicfgregs *cfg); static int cardbus_attach_card(device_t cbdev); static int cardbus_detach_card(device_t cbdev, int flags); static void cardbus_driver_added(device_t cbdev, driver_t *driver); -static struct cardbus_devinfo * cardbus_read_device(device_t brdev, int b, - int s, int f); +static void cardbus_read_extcap(device_t cbdev, pcicfgregs *cfg); static void cardbus_hdrtypedata(device_t brdev, int b, int s, int f, pcicfgregs *cfg); +static struct cardbus_devinfo *cardbus_read_device(device_t brdev, int b, + int s, int f); static int cardbus_freecfg(struct cardbus_devinfo *dinfo); static void cardbus_print_verbose(struct cardbus_devinfo *dinfo); static int cardbus_set_resource(device_t cbdev, device_t child, int type, - int rid, u_long start, u_long count); + int rid, u_long start, u_long count, struct resource *res); static int cardbus_get_resource(device_t cbdev, device_t child, int type, int rid, u_long *startp, u_long *countp); static void cardbus_delete_resource(device_t cbdev, device_t child, int type, int rid); static int cardbus_set_resource_method(device_t cbdev, device_t child, int type, int rid, u_long start, u_long count); static int cardbus_get_resource_method(device_t cbdev, device_t child, int type, int rid, u_long *startp, u_long *countp); static void cardbus_delete_resource_method(device_t cbdev, device_t child, int type, int rid); -static int cardbus_add_map(device_t cbdev, device_t dev, pcicfgregs *cfg, - int reg); -static void cardbus_add_resources(device_t dev, pcicfgregs* cfg); static void cardbus_release_all_resources(device_t cbdev, - struct resource_list *rl); -static struct resource* cardbus_alloc_resource(device_t cbdev, device_t child, - int type, int* rid, u_long start, u_long end, u_long count, + struct cardbus_devinfo *dinfo); +static struct resource *cardbus_alloc_resource(device_t cbdev, device_t child, + int type, int *rid, u_long start, u_long end, u_long count, u_int flags); static int cardbus_release_resource(device_t cbdev, device_t child, int type, int rid, struct resource *r); +static int cardbus_setup_intr(device_t cbdev, device_t child, + struct resource *irq, int flags, driver_intr_t *intr, + void *arg, void **cookiep); +static int cardbus_teardown_intr(device_t cbdev, device_t child, + struct resource *irq, void *cookie); static int cardbus_print_resources(struct resource_list *rl, const char *name, int type, const char *format); static int cardbus_print_child(device_t cbdev, device_t child); static void cardbus_probe_nomatch(device_t cbdev, device_t child); static int cardbus_read_ivar(device_t cbdev, device_t child, int which, u_long *result); static int cardbus_write_ivar(device_t cbdev, device_t child, int which, uintptr_t value); -static u_int32_t cardbus_read_config_method(device_t cbdev, +static int cardbus_set_powerstate_method(device_t cbdev, device_t child, + int state); +static int cardbus_get_powerstate_method(device_t cbdev, device_t child); +static u_int32_t cardbus_read_config_method(device_t cbdev, device_t child, int reg, int width); static void cardbus_write_config_method(device_t cbdev, device_t child, int reg, u_int32_t val, int width); +static __inline void cardbus_set_command_bit(device_t cbdev, device_t child, + u_int16_t bit); +static __inline void cardbus_clear_command_bit(device_t cbdev, device_t child, + u_int16_t bit); +static void cardbus_enable_busmaster_method(device_t cbdev, device_t child); +static void cardbus_disable_busmaster_method(device_t cbdev, device_t child); +static void cardbus_enable_io_method(device_t cbdev, device_t child, + int space); +static void cardbus_disable_io_method(device_t cbdev, device_t child, + int space); /************************************************************************/ /* Probe/Attach */ /************************************************************************/ static int cardbus_probe(device_t cbdev) { device_set_desc(cbdev, "Cardbus bus (newcard)"); return 0; } static int cardbus_attach(device_t cbdev) { return 0; } static int cardbus_detach(device_t cbdev) { cardbus_detach_card(cbdev, DETACH_FORCE); return 0; } /************************************************************************/ /* Attach/Detach card */ /************************************************************************/ static void device_setup_regs(device_t brdev, int b, int s, int f, pcicfgregs *cfg) { PCIB_WRITE_CONFIG(brdev, b, s, f, PCIR_INTLINE, pci_get_irq(device_get_parent(brdev)), 1); cfg->intline = PCIB_READ_CONFIG(brdev, b, s, f, PCIR_INTLINE, 1); PCIB_WRITE_CONFIG(brdev, b, s, f, PCIR_CACHELNSZ, 0x08, 1); cfg->cachelnsz = PCIB_READ_CONFIG(brdev, b, s, f, PCIR_CACHELNSZ, 1); PCIB_WRITE_CONFIG(brdev, b, s, f, PCIR_LATTIMER, 0xa8, 1); cfg->lattimer = PCIB_READ_CONFIG(brdev, b, s, f, PCIR_LATTIMER, 1); PCIB_WRITE_CONFIG(brdev, b, s, f, PCIR_MINGNT, 0x14, 1); cfg->mingnt = PCIB_READ_CONFIG(brdev, b, s, f, PCIR_MINGNT, 1); PCIB_WRITE_CONFIG(brdev, b, s, f, PCIR_MAXLAT, 0x14, 1); cfg->maxlat = PCIB_READ_CONFIG(brdev, b, s, f, PCIR_MAXLAT, 1); } static int cardbus_attach_card(device_t cbdev) { device_t brdev = device_get_parent(cbdev); int cardattached = 0; static int curr_bus_number = 2; /* XXX EVILE BAD (see below) */ int bus, slot, func; cardbus_detach_card(cbdev, DETACH_NOWARN); /* detach existing cards */ POWER_ENABLE_SOCKET(brdev, cbdev); bus = pcib_get_bus(cbdev); if (bus == 0) { /* * XXX EVILE BAD XXX * Not all BIOSes initialize the secondary bus number properly, * so if the default is bad, we just put one in and hope it * works. */ bus = curr_bus_number; pci_write_config(brdev, PCIR_SECBUS_2, curr_bus_number, 1); pci_write_config(brdev, PCIR_SUBBUS_2, curr_bus_number + 2, 1); curr_bus_number += 3; } /* For each function, set it up and try to attach a driver to it */ for (slot = 0; slot <= CARDBUS_SLOTMAX; slot++) { int cardbusfunchigh = 0; for (func = 0; func <= cardbusfunchigh; func++) { struct cardbus_devinfo *dinfo = cardbus_read_device(brdev, bus, slot, func); if (dinfo == NULL) continue; if (dinfo->cfg.mfdev) cardbusfunchigh = CARDBUS_FUNCMAX; device_setup_regs(brdev, bus, slot, func, &dinfo->cfg); cardbus_print_verbose(dinfo); dinfo->cfg.dev = device_add_child(cbdev, NULL, -1); if (!dinfo->cfg.dev) { DEVPRINTF((cbdev, "Cannot add child!\n")); cardbus_freecfg(dinfo); continue; } resource_list_init(&dinfo->resources); + SLIST_INIT(&dinfo->intrlist); device_set_ivars(dinfo->cfg.dev, dinfo); - cardbus_add_resources(dinfo->cfg.dev, &dinfo->cfg); cardbus_do_cis(cbdev, dinfo->cfg.dev); if (device_probe_and_attach(dinfo->cfg.dev) != 0) { /* when fail, release all resources */ - cardbus_release_all_resources(dinfo->cfg.dev, - &dinfo->resources); + cardbus_release_all_resources(cbdev, dinfo); } else cardattached++; } } if (cardattached > 0) return 0; POWER_DISABLE_SOCKET(brdev, cbdev); return ENOENT; } static int cardbus_detach_card(device_t cbdev, int flags) { int numdevs; device_t *devlist; int tmp; int err = 0; device_get_children(cbdev, &devlist, &numdevs); if (numdevs == 0) { if (!(flags & DETACH_NOWARN)) { DEVPRINTF((cbdev, "detach_card: no card to detach!\n")); POWER_DISABLE_SOCKET(device_get_parent(cbdev), cbdev); } free(devlist, M_TEMP); return ENOENT; } for (tmp = 0; tmp < numdevs; tmp++) { struct cardbus_devinfo *dinfo = device_get_ivars(devlist[tmp]); int status = device_get_state(devlist[tmp]); if (status == DS_ATTACHED || status == DS_BUSY) { if (device_detach(dinfo->cfg.dev) == 0 || flags & DETACH_FORCE) { - cardbus_release_all_resources(dinfo->cfg.dev, - &dinfo->resources); + cardbus_release_all_resources(cbdev, dinfo); device_delete_child(cbdev, devlist[tmp]); } else { err++; } cardbus_freecfg(dinfo); } else { + cardbus_release_all_resources(cbdev, dinfo); device_delete_child(cbdev, devlist[tmp]); + cardbus_freecfg(dinfo); } } if (err == 0) POWER_DISABLE_SOCKET(device_get_parent(cbdev), cbdev); free(devlist, M_TEMP); return err; } static void cardbus_driver_added(device_t cbdev, driver_t *driver) { + /* XXX check if 16-bit or cardbus! */ int numdevs; device_t *devlist; - device_t brdev = device_get_parent(cbdev); int tmp, cardattached; device_get_children(cbdev, &devlist, &numdevs); cardattached = 0; for (tmp = 0; tmp < numdevs; tmp++) { if (device_get_state(devlist[tmp]) != DS_NOTPRESENT) cardattached++; } - if (cardattached == 0) - POWER_ENABLE_SOCKET(brdev, cbdev); + if (cardattached == 0) { + free(devlist, M_TEMP); + CARD_REPROBE_CARD(device_get_parent(cbdev), cbdev); + return; + } + DEVICE_IDENTIFY(driver, cbdev); for (tmp = 0; tmp < numdevs; tmp++) { if (device_get_state(devlist[tmp]) == DS_NOTPRESENT) { struct cardbus_devinfo *dinfo; dinfo = device_get_ivars(devlist[tmp]); + cardbus_release_all_resources(cbdev, dinfo); resource_list_init(&dinfo->resources); - cardbus_add_resources(dinfo->cfg.dev, &dinfo->cfg); cardbus_do_cis(cbdev, dinfo->cfg.dev); if (device_probe_and_attach(dinfo->cfg.dev) != 0) { - cardbus_release_all_resources(dinfo->cfg.dev, - &dinfo->resources); + cardbus_release_all_resources(cbdev, dinfo); } else cardattached++; } } - if (cardattached == 0) - POWER_DISABLE_SOCKET(brdev, cbdev); free(devlist, M_TEMP); } /************************************************************************/ /* PCI-Like config reading (copied from pci.c */ /************************************************************************/ /* read configuration header into pcicfgrect structure */ +static void +cardbus_read_extcap(device_t cbdev, pcicfgregs *cfg) +{ +#define REG(n, w) PCIB_READ_CONFIG(cbdev, cfg->bus, cfg->slot, cfg->func, n, w) + int ptr, nextptr, ptrptr; + + switch (cfg->hdrtype) { + case 0: + ptrptr = 0x34; + break; + case 2: + ptrptr = 0x14; + break; + default: + return; /* no extended capabilities support */ + } + nextptr = REG(ptrptr, 1); /* sanity check? */ + + /* + * Read capability entries. + */ + while (nextptr != 0) { + /* Sanity check */ + if (nextptr > 255) { + printf("illegal PCI extended capability offset %d\n", + nextptr); + return; + } + /* Find the next entry */ + ptr = nextptr; + nextptr = REG(ptr + 1, 1); + + /* Process this entry */ + switch (REG(ptr, 1)) { + case 0x01: /* PCI power management */ + if (cfg->pp_cap == 0) { + cfg->pp_cap = REG(ptr + PCIR_POWER_CAP, 2); + cfg->pp_status = ptr + PCIR_POWER_STATUS; + cfg->pp_pmcsr = ptr + PCIR_POWER_PMCSR; + if ((nextptr - ptr) > PCIR_POWER_DATA) + cfg->pp_data = ptr + PCIR_POWER_DATA; + } + break; + default: + break; + } + } +#undef REG +} + +/* extract header type specific config data */ + +static void +cardbus_hdrtypedata(device_t brdev, int b, int s, int f, pcicfgregs *cfg) +{ +#define REG(n, w) PCIB_READ_CONFIG(brdev, b, s, f, n, w) + switch (cfg->hdrtype) { + case 0: + cfg->subvendor = REG(PCIR_SUBVEND_0, 2); + cfg->subdevice = REG(PCIR_SUBDEV_0, 2); + cfg->nummaps = PCI_MAXMAPS_0; + break; + case 1: + cfg->subvendor = REG(PCIR_SUBVEND_1, 2); + cfg->subdevice = REG(PCIR_SUBDEV_1, 2); + cfg->nummaps = PCI_MAXMAPS_1; + break; + case 2: + cfg->subvendor = REG(PCIR_SUBVEND_2, 2); + cfg->subdevice = REG(PCIR_SUBDEV_2, 2); + cfg->nummaps = PCI_MAXMAPS_2; + break; + } +#undef REG +} + static struct cardbus_devinfo * cardbus_read_device(device_t brdev, int b, int s, int f) { #define REG(n, w) PCIB_READ_CONFIG(brdev, b, s, f, n, w) pcicfgregs *cfg = NULL; struct cardbus_devinfo *devlist_entry = NULL; - if (PCIB_READ_CONFIG(brdev, b, s, f, PCIR_DEVVENDOR, 4) != -1) { + if (REG(PCIR_DEVVENDOR, 4) != -1) { devlist_entry = malloc(sizeof(struct cardbus_devinfo), M_DEVBUF, M_WAITOK | M_ZERO); if (devlist_entry == NULL) return (NULL); cfg = &devlist_entry->cfg; cfg->bus = b; cfg->slot = s; cfg->func = f; cfg->vendor = REG(PCIR_VENDOR, 2); cfg->device = REG(PCIR_DEVICE, 2); cfg->cmdreg = REG(PCIR_COMMAND, 2); cfg->statreg = REG(PCIR_STATUS, 2); cfg->baseclass = REG(PCIR_CLASS, 1); cfg->subclass = REG(PCIR_SUBCLASS, 1); cfg->progif = REG(PCIR_PROGIF, 1); cfg->revid = REG(PCIR_REVID, 1); cfg->hdrtype = REG(PCIR_HEADERTYPE, 1); cfg->cachelnsz = REG(PCIR_CACHELNSZ, 1); cfg->lattimer = REG(PCIR_LATTIMER, 1); cfg->intpin = REG(PCIR_INTPIN, 1); cfg->intline = REG(PCIR_INTLINE, 1); -#ifdef __alpha__ - alpha_platform_assign_pciintr(cfg); -#endif -#ifdef APIC_IO - if (cfg->intpin != 0) { - int airq; - - airq = pci_apic_irq(cfg->bus, cfg->slot, cfg->intpin); - if (airq >= 0) { - /* PCI specific entry found in MP table */ - if (airq != cfg->intline) { - undirect_pci_irq(cfg->intline); - cfg->intline = airq; - } - } else { - /* - * PCI interrupts might be redirected to the - * ISA bus according to some MP tables. Use the - * same methods as used by the ISA devices - * devices to find the proper IOAPIC int pin. - */ - airq = isa_apic_irq(cfg->intline); - if ((airq >= 0) && (airq != cfg->intline)) { - /* XXX: undirect_pci_irq() ? */ - undirect_isa_irq(cfg->intline); - cfg->intline = airq; - } - } - } -#endif /* APIC_IO */ - cfg->mingnt = REG(PCIR_MINGNT, 1); cfg->maxlat = REG(PCIR_MAXLAT, 1); cfg->mfdev = (cfg->hdrtype & PCIM_MFDEV) != 0; cfg->hdrtype &= ~PCIM_MFDEV; cardbus_hdrtypedata(brdev, b, s, f, cfg); + if (REG(PCIR_STATUS, 2) & PCIM_STATUS_CAPPRESENT) + cardbus_read_extcap(brdev, cfg); + devlist_entry->conf.pc_sel.pc_bus = cfg->bus; devlist_entry->conf.pc_sel.pc_dev = cfg->slot; devlist_entry->conf.pc_sel.pc_func = cfg->func; devlist_entry->conf.pc_hdr = cfg->hdrtype; devlist_entry->conf.pc_subvendor = cfg->subvendor; devlist_entry->conf.pc_subdevice = cfg->subdevice; devlist_entry->conf.pc_vendor = cfg->vendor; devlist_entry->conf.pc_device = cfg->device; devlist_entry->conf.pc_class = cfg->baseclass; devlist_entry->conf.pc_subclass = cfg->subclass; devlist_entry->conf.pc_progif = cfg->progif; devlist_entry->conf.pc_revid = cfg->revid; } return (devlist_entry); #undef REG } -/* extract header type specific config data */ - -static void -cardbus_hdrtypedata(device_t brdev, int b, int s, int f, pcicfgregs *cfg) -{ -#define REG(n, w) PCIB_READ_CONFIG(brdev, b, s, f, n, w) - switch (cfg->hdrtype) { - case 0: - cfg->subvendor = REG(PCIR_SUBVEND_0, 2); - cfg->subdevice = REG(PCIR_SUBDEV_0, 2); - cfg->nummaps = PCI_MAXMAPS_0; - break; - case 1: - cfg->subvendor = REG(PCIR_SUBVEND_1, 2); - cfg->subdevice = REG(PCIR_SUBDEV_1, 2); - cfg->nummaps = PCI_MAXMAPS_1; - break; - case 2: - cfg->subvendor = REG(PCIR_SUBVEND_2, 2); - cfg->subdevice = REG(PCIR_SUBDEV_2, 2); - cfg->nummaps = PCI_MAXMAPS_2; - break; - } -#undef REG -} - /* free pcicfgregs structure and all depending data structures */ static int cardbus_freecfg(struct cardbus_devinfo *dinfo) { free(dinfo, M_DEVBUF); return (0); } static void cardbus_print_verbose(struct cardbus_devinfo *dinfo) { - if (bootverbose) { +#ifndef CARDBUS_DEBUG + if (bootverbose) +#endif /* CARDBUS_DEBUG */ + { pcicfgregs *cfg = &dinfo->cfg; printf("found->\tvendor=0x%04x, dev=0x%04x, revid=0x%02x\n", cfg->vendor, cfg->device, cfg->revid); printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n", cfg->baseclass, cfg->subclass, cfg->progif, cfg->hdrtype, cfg->mfdev); #ifdef CARDBUS_DEBUG printf("\tcmdreg=0x%04x, statreg=0x%04x, " "cachelnsz=%d (dwords)\n", cfg->cmdreg, cfg->statreg, cfg->cachelnsz); printf("\tlattimer=0x%02x (%d ns), mingnt=0x%02x (%d ns), " "maxlat=0x%02x (%d ns)\n", cfg->lattimer, cfg->lattimer * 30, cfg->mingnt, cfg->mingnt * 250, cfg->maxlat, cfg->maxlat * 250); #endif /* CARDBUS_DEBUG */ if (cfg->intpin > 0) printf("\tintpin=%c, irq=%d\n", cfg->intpin + 'a' - 1, cfg->intline); } } /************************************************************************/ /* Resources */ /************************************************************************/ static int cardbus_set_resource(device_t cbdev, device_t child, int type, int rid, - u_long start, u_long count) + u_long start, u_long count, struct resource *res) { - struct cardbus_devinfo *dinfo = device_get_ivars(child); - struct resource_list *rl = &dinfo->resources; - resource_list_add(rl, type, rid, start, start + count - 1, count); + struct cardbus_devinfo *dinfo; + struct resource_list *rl; + struct resource_list_entry *rle; + + if (device_get_parent(child) != cbdev) + return ENOENT; + + dinfo = device_get_ivars(child); + rl = &dinfo->resources; + rle = resource_list_find(rl, type, rid); + if (rle == NULL) { + resource_list_add(rl, type, rid, start, start + count - 1, + count); + if (res != NULL) { + rle = resource_list_find(rl, type, rid); + rle->res = res; + } + } else { + if (rle->res == NULL) { + } else if (rle->res->r_dev == cbdev && + (!(rman_get_flags(rle->res) & RF_ACTIVE))) { + int f; + f = rman_get_flags(rle->res); + bus_release_resource(cbdev, type, rid, res); + rle->res = bus_alloc_resource(cbdev, type, &rid, + start, start + count - 1, + count, f); + } else { + device_printf(cbdev, "set_resource: resource busy\n"); + return EBUSY; + } + rle->start = start; + rle->end = start + count - 1; + rle->count = count; + if (res != NULL) + rle->res = res; + } if (device_get_parent(child) == cbdev) pci_write_config(child, rid, start, 4); return 0; } static int cardbus_get_resource(device_t cbdev, device_t child, int type, int rid, u_long *startp, u_long *countp) { - struct cardbus_devinfo *dinfo = device_get_ivars(child); - struct resource_list *rl = &dinfo->resources; + struct cardbus_devinfo *dinfo; + struct resource_list *rl; struct resource_list_entry *rle; + + if (device_get_parent(child) != cbdev) + return ENOENT; + + dinfo = device_get_ivars(child); + rl = &dinfo->resources; rle = resource_list_find(rl, type, rid); if (!rle) return ENOENT; if (startp) *startp = rle->start; if (countp) *countp = rle->count; return 0; } static void cardbus_delete_resource(device_t cbdev, device_t child, int type, int rid) { - struct cardbus_devinfo *dinfo = device_get_ivars(child); - struct resource_list *rl = &dinfo->resources; + struct cardbus_devinfo *dinfo; + struct resource_list *rl; struct resource_list_entry *rle; + + if (device_get_parent(child) != cbdev) + return; + + dinfo = device_get_ivars(child); + rl = &dinfo->resources; rle = resource_list_find(rl, type, rid); if (rle) { - if (rle->res) - bus_generic_release_resource(cbdev, child, type, rid, - rle->res); + if (rle->res) { + if (rle->res->r_dev != cbdev || + rman_get_flags(rle->res) & RF_ACTIVE) { + device_printf(cbdev, "delete_resource: " + "Resource still owned by child, oops. " + "(type=%d, rid=%d, addr=%lx)\n", + rle->type, rle->rid, + rman_get_start(rle->res)); + return; + } + bus_release_resource(cbdev, type, rid, rle->res); + } resource_list_delete(rl, type, rid); } if (device_get_parent(child) == cbdev) pci_write_config(child, rid, 0, 4); } static int cardbus_set_resource_method(device_t cbdev, device_t child, int type, int rid, u_long start, u_long count) { int ret; - ret = cardbus_set_resource(cbdev, child, type, rid, start, count); + ret = cardbus_set_resource(cbdev, child, type, rid, start, count, NULL); if (ret != 0) return ret; return BUS_SET_RESOURCE(device_get_parent(cbdev), child, type, rid, start, count); } static int cardbus_get_resource_method(device_t cbdev, device_t child, int type, int rid, u_long *startp, u_long *countp) { int ret; ret = cardbus_get_resource(cbdev, child, type, rid, startp, countp); if (ret != 0) return ret; return BUS_GET_RESOURCE(device_get_parent(cbdev), child, type, rid, startp, countp); } static void cardbus_delete_resource_method(device_t cbdev, device_t child, int type, int rid) { cardbus_delete_resource(cbdev, child, type, rid); BUS_DELETE_RESOURCE(device_get_parent(cbdev), child, type, rid); } -static int -cardbus_add_map(device_t cbdev, device_t dev, pcicfgregs *cfg, int reg) -{ - struct cardbus_devinfo *dinfo = device_get_ivars(dev); - struct resource_list *rl = &dinfo->resources; - struct resource_list_entry *rle; - struct resource *res; - device_t bdev = device_get_parent(cbdev); - u_int32_t size; - u_int32_t testval; - int type; - - if (reg == CARDBUS_ROM_REG) - testval = CARDBUS_ROM_ADDRMASK; - else - testval = ~0; - - PCIB_WRITE_CONFIG(bdev, cfg->bus, cfg->slot, cfg->func, - reg, testval, 4); - - testval = PCIB_READ_CONFIG(bdev, cfg->bus, cfg->slot, cfg->func, - reg, 4); - if (testval == ~0 || testval == 0) - return 0; - - if ((testval&1) == 0) - type = SYS_RES_MEMORY; - else - type = SYS_RES_IOPORT; - - size = CARDBUS_MAPREG_MEM_SIZE(testval); - res = bus_generic_alloc_resource(cbdev, dev, type, ®, 0, ~0, size, - rman_make_alignment_flags(size)); - if (res) { - u_int32_t start = rman_get_start(res); - u_int32_t end = rman_get_end(res); - cardbus_set_resource(cbdev, dev, type, reg, start,end-start+1); - rle = resource_list_find(rl, type, reg); - rle->res = res; - } else { - device_printf(dev, "Unable to add map %02x\n", reg); - type = 0; - } - return type; -} - static void -cardbus_add_resources(device_t dev, pcicfgregs* cfg) +cardbus_release_all_resources(device_t cbdev, struct cardbus_devinfo *dinfo) { - device_t cbdev = device_get_parent(dev); - device_t bdev = device_get_parent(cbdev); - struct cardbus_devinfo *dinfo = device_get_ivars(dev); - struct resource_list *rl = &dinfo->resources; - struct cardbus_quirk *q; struct resource_list_entry *rle; - struct resource *res; - int rid; - u_int command; - int type; - int types; - int i; + struct cardbus_intrlist *ile; - types = 0; - for (i = 0; i < cfg->nummaps; i++) { - type = cardbus_add_map(cbdev, dev, cfg, PCIR_MAPS + i*4); - types |= 0x1 << type; + /* Remove any interrupt handlers */ + while (NULL != (ile = SLIST_FIRST(&dinfo->intrlist))) { + device_printf(cbdev, "release_all_resource: " + "intr handler still active, removing.\n"); + bus_teardown_intr(ile->dev, ile->irq, ile->cookie); + SLIST_REMOVE_HEAD(&dinfo->intrlist, link); + free(ile, M_DEVBUF); } - type = cardbus_add_map(cbdev, dev, cfg, CARDBUS_ROM_REG); - types |= 0x1 << type; - for (q = &cardbus_quirks[0]; q->devid; q++) { - if (q->devid == ((cfg->device << 16) | cfg->vendor) - && q->type == CARDBUS_QUIRK_MAP_REG) { - type = cardbus_add_map(cbdev, dev, cfg, q->arg1); - types |= 0x1 << type; + /* Free all allocated resources */ + SLIST_FOREACH(rle, &dinfo->resources, link) { + if (rle->res) { + if (rle->res->r_dev != cbdev) + device_printf(cbdev, "release_all_resource: " + "Resource still owned by child, oops. " + "(type=%d, rid=%d, addr=%lx)\n", + rle->type, rle->rid, + rman_get_start(rle->res)); + BUS_RELEASE_RESOURCE(device_get_parent(cbdev), + rle->res->r_dev, + rle->type, rle->rid, + rle->res); + rle->res = NULL; + /* + * zero out config so the card won't acknowledge + * access to the space anymore + */ + pci_write_config(dinfo->cfg.dev, rle->rid, 0, 4); } } - - command = PCIB_READ_CONFIG(bdev, cfg->bus, cfg->slot, - cfg->func, PCIR_COMMAND, 2); - if ((types & (0x1 << SYS_RES_MEMORY)) != 0) - command |= PCIM_CMD_MEMEN; - if ((types & (0x1 << SYS_RES_IOPORT)) != 0) - command |= PCIM_CMD_PORTEN; - command |= PCIM_CMD_BUSMASTEREN; - PCIB_WRITE_CONFIG(bdev, cfg->bus, cfg->slot, cfg->func, - PCIR_COMMAND, command, 2); - - rid = 0; - res = bus_generic_alloc_resource(cbdev, dev, SYS_RES_IRQ, - &rid, 0, ~0, 1, RF_SHAREABLE); - - if (res == NULL) - panic("Cannot allocate IRQ for card\n"); - - resource_list_add(rl, SYS_RES_IRQ, rid, - rman_get_start(res), rman_get_start(res), 1); - rle = resource_list_find(rl, SYS_RES_IRQ, rid); - rle->res = res; + resource_list_free(&dinfo->resources); } -static void -cardbus_release_all_resources(device_t cbdev, struct resource_list *rl) +static struct resource * +cardbus_alloc_resource(device_t cbdev, device_t child, int type, + int *rid, u_long start, u_long end, u_long count, u_int flags) { - struct resource_list_entry *rle; + struct cardbus_devinfo *dinfo; + struct resource_list_entry *rle = 0; + int passthrough = (device_get_parent(child) != cbdev); - SLIST_FOREACH(rle, rl, link) { - if (rle->res) { - bus_generic_release_resource(device_get_parent(cbdev), - cbdev, rle->type, rle->rid, - rle->res); - } + if (passthrough) { + return (BUS_ALLOC_RESOURCE(device_get_parent(cbdev), child, + type, rid, start, end, count, flags)); } -} -static struct resource* -cardbus_alloc_resource(device_t cbdev, device_t child, int type, - int* rid, u_long start, u_long end, u_long count, u_int flags) -{ - struct cardbus_devinfo *dinfo = device_get_ivars(child); - struct resource_list *rl = &dinfo->resources; - struct resource_list_entry *rle = NULL; - struct resource *res; + dinfo = device_get_ivars(child); + rle = resource_list_find(&dinfo->resources, type, *rid); - if (device_get_parent(child) == cbdev || child == cbdev) - rle = resource_list_find(rl, type, *rid); - if (rle) { - if (flags & RF_ACTIVE) { - if (bus_activate_resource(child, rle->type, *rid, - rle->res)) { - return NULL; - } - if (*rid == CARDBUS_ROM_REG) { - uint32_t rom_reg; + if (!rle) + return NULL; /* no resource of that type/rid */ - rom_reg = pci_read_config(child, *rid, 4); - rom_reg |= CARDBUS_ROM_ENABLE; - pci_write_config(child, *rid, rom_reg, 4); - } - } - return rle->res; /* XXX: check if range within start/end */ + if (!rle->res) { + device_printf(cbdev, "WARNING: Resource not reserved by bus\n"); + return NULL; } else { - res = bus_generic_alloc_resource(cbdev, child, type, rid, - start, end, count, flags); - if (res) { - start = rman_get_start(res); - end = rman_get_end(res); - cardbus_set_resource(cbdev, child, type, *rid, start, - end-start+1); - rle = resource_list_find(rl, type, *rid); - rle->res = res; - return res; - } else { - device_printf(cbdev, "Resource Allocation Failed!\n"); + /* Release the cardbus hold on the resource */ + if (rle->res->r_dev != cbdev) return NULL; + bus_release_resource(cbdev, type, *rid, rle->res); + rle->res = NULL; + switch (type) { + case SYS_RES_IOPORT: + case SYS_RES_MEMORY: + if (!(flags & RF_ALIGNMENT_MASK)) + flags |= rman_make_alignment_flags(rle->count); + break; + case SYS_RES_IRQ: + flags |= RF_SHAREABLE; + break; } + /* Allocate the resource to the child */ + return resource_list_alloc(&dinfo->resources, cbdev, child, + type, rid, rle->start, rle->end, rle->count, flags); } } static int cardbus_release_resource(device_t cbdev, device_t child, int type, int rid, struct resource *r) { + struct cardbus_devinfo *dinfo; + int passthrough = (device_get_parent(child) != cbdev); + struct resource_list_entry *rle = 0; + int flags; + int ret; + + if (passthrough) { + return BUS_RELEASE_RESOURCE(device_get_parent(cbdev), child, + type, rid, r); + } + + dinfo = device_get_ivars(child); /* * According to the PCI 2.2 spec, devices may share an address * decoder between memory mapped ROM access and memory * mapped register access. To be safe, disable ROM access * whenever it is released. */ if (rid == CARDBUS_ROM_REG) { uint32_t rom_reg; rom_reg = pci_read_config(child, rid, 4); rom_reg &= ~CARDBUS_ROM_ENABLE; pci_write_config(child, rid, rom_reg, 4); } - return bus_deactivate_resource(child, type, rid, r); + rle = resource_list_find(&dinfo->resources, type, rid); + + if (!rle) { + device_printf(cbdev, "Allocated resource not found\n"); + return ENOENT; + } + if (!rle->res) { + device_printf(cbdev, "Allocated resource not recorded\n"); + return ENOENT; + } + + ret = BUS_RELEASE_RESOURCE(device_get_parent(cbdev), child, + type, rid, r); + switch (type) { + case SYS_RES_IOPORT: + case SYS_RES_MEMORY: + flags = rman_make_alignment_flags(rle->count); + break; + case SYS_RES_IRQ: + flags = RF_SHAREABLE; + break; + default: + flags = 0; + } + /* Restore cardbus hold on the resource */ + rle->res = bus_alloc_resource(cbdev, type, &rid, + rle->start, rle->end, rle->count, flags); + if (rle->res == NULL) + device_printf(cbdev, "release_resource: " + "unable to reacquire resource\n"); + return ret; } +static int +cardbus_setup_intr(device_t cbdev, device_t child, struct resource *irq, + int flags, driver_intr_t *intr, void *arg, void **cookiep) +{ + int ret; + struct cardbus_intrlist *ile; + device_t cdev; + struct cardbus_devinfo *dinfo; + + ret = bus_generic_setup_intr(cbdev, child, irq, flags, intr, arg, + cookiep); + if (ret != 0) + return ret; + + for (cdev = child; cbdev != device_get_parent(cdev); + cdev = device_get_parent(cdev)) + /* NOTHING */; + dinfo = device_get_ivars(cdev); + + /* record interrupt handler */ + ile = malloc(sizeof(struct cardbus_intrlist), M_DEVBUF, M_NOWAIT); + ile->dev = child; + ile->irq = irq; + ile->cookie = *cookiep; + + SLIST_INSERT_HEAD(&dinfo->intrlist, ile, link); + return 0; +} + +static int +cardbus_teardown_intr(device_t cbdev, device_t child, struct resource *irq, + void *cookie) +{ + int ret; + struct cardbus_intrlist *ile; + device_t cdev; + struct cardbus_devinfo *dinfo; + + ret = bus_generic_teardown_intr(cbdev, child, irq, cookie); + if (ret != 0) + return ret; + + for (cdev = child; cbdev != device_get_parent(cdev); + cdev = device_get_parent(cdev)) + /* NOTHING */; + dinfo = device_get_ivars(cdev); + + /* remove interrupt handler from record */ + SLIST_FOREACH(ile, &dinfo->intrlist, link) { + if (ile->irq == irq && ile->cookie == cookie) { + SLIST_REMOVE(&dinfo->intrlist, ile, cardbus_intrlist, + link); + free(ile, M_DEVBUF); + return 0; + } + } + device_printf(cbdev, "teardown_intr: intr handler not recorded.\n"); + return ENOENT; +} + + /************************************************************************/ /* Other Bus Methods */ /************************************************************************/ static int cardbus_print_resources(struct resource_list *rl, const char *name, int type, const char *format) { struct resource_list_entry *rle; int printed, retval; printed = 0; retval = 0; /* Yes, this is kinda cheating */ SLIST_FOREACH(rle, rl, link) { if (rle->type == type) { if (printed == 0) retval += printf(" %s ", name); else if (printed > 0) retval += printf(","); printed++; retval += printf(format, rle->start); if (rle->count > 1) { retval += printf("-"); retval += printf(format, rle->start + rle->count - 1); } } } return retval; } static int cardbus_print_child(device_t cbdev, device_t child) { struct cardbus_devinfo *dinfo; struct resource_list *rl; pcicfgregs *cfg; int retval = 0; dinfo = device_get_ivars(child); cfg = &dinfo->cfg; rl = &dinfo->resources; retval += bus_print_child_header(cbdev, child); retval += cardbus_print_resources(rl, "port", SYS_RES_IOPORT, "%#lx"); retval += cardbus_print_resources(rl, "mem", SYS_RES_MEMORY, "%#lx"); retval += cardbus_print_resources(rl, "irq", SYS_RES_IRQ, "%ld"); if (device_get_flags(cbdev)) retval += printf(" flags %#x", device_get_flags(cbdev)); retval += printf(" at device %d.%d", pci_get_slot(child), pci_get_function(child)); retval += bus_print_child_footer(cbdev, child); return (retval); } static void cardbus_probe_nomatch(device_t cbdev, device_t child) { struct cardbus_devinfo *dinfo; pcicfgregs *cfg; dinfo = device_get_ivars(child); cfg = &dinfo->cfg; device_printf(cbdev, ""); printf(" (vendor=0x%04x, dev=0x%04x)", cfg->vendor, cfg->device); printf(" at %d.%d", pci_get_slot(child), pci_get_function(child)); if (cfg->intpin > 0 && cfg->intline != 255) { printf(" irq %d", cfg->intline); } printf("\n"); return; } static int cardbus_read_ivar(device_t cbdev, device_t child, int which, u_long *result) { struct cardbus_devinfo *dinfo; pcicfgregs *cfg; dinfo = device_get_ivars(child); cfg = &dinfo->cfg; switch (which) { case PCI_IVAR_SUBVENDOR: *result = cfg->subvendor; break; case PCI_IVAR_SUBDEVICE: *result = cfg->subdevice; break; case PCI_IVAR_VENDOR: *result = cfg->vendor; break; case PCI_IVAR_DEVICE: *result = cfg->device; break; case PCI_IVAR_DEVID: *result = (cfg->device << 16) | cfg->vendor; break; case PCI_IVAR_CLASS: *result = cfg->baseclass; break; case PCI_IVAR_SUBCLASS: *result = cfg->subclass; break; case PCI_IVAR_PROGIF: *result = cfg->progif; break; case PCI_IVAR_REVID: *result = cfg->revid; break; case PCI_IVAR_INTPIN: *result = cfg->intpin; break; case PCI_IVAR_IRQ: *result = cfg->intline; break; case PCI_IVAR_BUS: *result = cfg->bus; break; case PCI_IVAR_SLOT: *result = cfg->slot; break; case PCI_IVAR_FUNCTION: *result = cfg->func; break; default: return ENOENT; } return 0; } static int cardbus_write_ivar(device_t cbdev, device_t child, int which, uintptr_t value) { struct cardbus_devinfo *dinfo; pcicfgregs *cfg; dinfo = device_get_ivars(child); cfg = &dinfo->cfg; switch (which) { case PCI_IVAR_SUBVENDOR: case PCI_IVAR_SUBDEVICE: case PCI_IVAR_VENDOR: case PCI_IVAR_DEVICE: case PCI_IVAR_DEVID: case PCI_IVAR_CLASS: case PCI_IVAR_SUBCLASS: case PCI_IVAR_PROGIF: case PCI_IVAR_REVID: case PCI_IVAR_INTPIN: case PCI_IVAR_IRQ: case PCI_IVAR_BUS: case PCI_IVAR_SLOT: case PCI_IVAR_FUNCTION: return EINVAL; /* disallow for now */ default: return ENOENT; } return 0; } /************************************************************************/ /* Compatibility with PCI bus (XXX: Do we need this?) */ /************************************************************************/ +/* + * PCI power manangement + */ +static int +cardbus_set_powerstate_method(device_t cbdev, device_t child, int state) +{ + struct cardbus_devinfo *dinfo = device_get_ivars(child); + pcicfgregs *cfg = &dinfo->cfg; + u_int16_t status; + int result; + + if (cfg->pp_cap != 0) { + status = PCI_READ_CONFIG(cbdev, child, cfg->pp_status, 2) + & ~PCIM_PSTAT_DMASK; + result = 0; + switch (state) { + case PCI_POWERSTATE_D0: + status |= PCIM_PSTAT_D0; + break; + case PCI_POWERSTATE_D1: + if (cfg->pp_cap & PCIM_PCAP_D1SUPP) { + status |= PCIM_PSTAT_D1; + } else { + result = EOPNOTSUPP; + } + break; + case PCI_POWERSTATE_D2: + if (cfg->pp_cap & PCIM_PCAP_D2SUPP) { + status |= PCIM_PSTAT_D2; + } else { + result = EOPNOTSUPP; + } + break; + case PCI_POWERSTATE_D3: + status |= PCIM_PSTAT_D3; + break; + default: + result = EINVAL; + } + if (result == 0) + PCI_WRITE_CONFIG(cbdev, child, cfg->pp_status, + status, 2); + } else { + result = ENXIO; + } + return (result); +} + +static int +cardbus_get_powerstate_method(device_t cbdev, device_t child) +{ + struct cardbus_devinfo *dinfo = device_get_ivars(child); + pcicfgregs *cfg = &dinfo->cfg; + u_int16_t status; + int result; + + if (cfg->pp_cap != 0) { + status = PCI_READ_CONFIG(cbdev, child, cfg->pp_status, 2); + switch (status & PCIM_PSTAT_DMASK) { + case PCIM_PSTAT_D0: + result = PCI_POWERSTATE_D0; + break; + case PCIM_PSTAT_D1: + result = PCI_POWERSTATE_D1; + break; + case PCIM_PSTAT_D2: + result = PCI_POWERSTATE_D2; + break; + case PCIM_PSTAT_D3: + result = PCI_POWERSTATE_D3; + break; + default: + result = PCI_POWERSTATE_UNKNOWN; + break; + } + } else { + /* No support, device is always at D0 */ + result = PCI_POWERSTATE_D0; + } + return (result); +} + static u_int32_t cardbus_read_config_method(device_t cbdev, device_t child, int reg, int width) { struct cardbus_devinfo *dinfo = device_get_ivars(child); pcicfgregs *cfg = &dinfo->cfg; return PCIB_READ_CONFIG(device_get_parent(cbdev), cfg->bus, cfg->slot, cfg->func, reg, width); } static void cardbus_write_config_method(device_t cbdev, device_t child, int reg, u_int32_t val, int width) { struct cardbus_devinfo *dinfo = device_get_ivars(child); pcicfgregs *cfg = &dinfo->cfg; PCIB_WRITE_CONFIG(device_get_parent(cbdev), cfg->bus, cfg->slot, cfg->func, reg, val, width); } +static __inline void +cardbus_set_command_bit(device_t cbdev, device_t child, u_int16_t bit) +{ + u_int16_t command; + + command = PCI_READ_CONFIG(cbdev, child, PCIR_COMMAND, 2); + command |= bit; + PCI_WRITE_CONFIG(cbdev, child, PCIR_COMMAND, command, 2); +} + +static __inline void +cardbus_clear_command_bit(device_t cbdev, device_t child, u_int16_t bit) +{ + u_int16_t command; + + command = PCI_READ_CONFIG(cbdev, child, PCIR_COMMAND, 2); + command &= ~bit; + PCI_WRITE_CONFIG(cbdev, child, PCIR_COMMAND, command, 2); +} + +static void +cardbus_enable_busmaster_method(device_t cbdev, device_t child) +{ + cardbus_set_command_bit(cbdev, child, PCIM_CMD_BUSMASTEREN); +} + +static void +cardbus_disable_busmaster_method(device_t cbdev, device_t child) +{ + cardbus_clear_command_bit(cbdev, child, PCIM_CMD_BUSMASTEREN); +} + +static void +cardbus_enable_io_method(device_t cbdev, device_t child, int space) +{ + switch (space) { + case SYS_RES_IOPORT: + cardbus_set_command_bit(cbdev, child, PCIM_CMD_PORTEN); + break; + case SYS_RES_MEMORY: + cardbus_set_command_bit(cbdev, child, PCIM_CMD_MEMEN); + break; + } +} + +static void +cardbus_disable_io_method(device_t cbdev, device_t child, int space) +{ + switch (space) { + case SYS_RES_IOPORT: + cardbus_clear_command_bit(cbdev, child, PCIM_CMD_PORTEN); + break; + case SYS_RES_MEMORY: + cardbus_clear_command_bit(cbdev, child, PCIM_CMD_MEMEN); + break; + } +} + static device_method_t cardbus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cardbus_probe), DEVMETHOD(device_attach, cardbus_attach), DEVMETHOD(device_detach, cardbus_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, cardbus_print_child), DEVMETHOD(bus_probe_nomatch, cardbus_probe_nomatch), DEVMETHOD(bus_read_ivar, cardbus_read_ivar), DEVMETHOD(bus_write_ivar, cardbus_write_ivar), DEVMETHOD(bus_driver_added, cardbus_driver_added), DEVMETHOD(bus_alloc_resource, cardbus_alloc_resource), DEVMETHOD(bus_release_resource, cardbus_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), - DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), - DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), - DEVMETHOD(bus_driver_added, bus_generic_driver_added), + DEVMETHOD(bus_setup_intr, cardbus_setup_intr), + DEVMETHOD(bus_teardown_intr, cardbus_teardown_intr), DEVMETHOD(bus_set_resource, cardbus_set_resource_method), DEVMETHOD(bus_get_resource, cardbus_get_resource_method), DEVMETHOD(bus_delete_resource, cardbus_delete_resource_method), /* Card Interface */ DEVMETHOD(card_attach_card, cardbus_attach_card), DEVMETHOD(card_detach_card, cardbus_detach_card), DEVMETHOD(card_cis_read, cardbus_cis_read), DEVMETHOD(card_cis_free, cardbus_cis_free), /* Cardbus/PCI interface */ DEVMETHOD(pci_read_config, cardbus_read_config_method), DEVMETHOD(pci_write_config, cardbus_write_config_method), + DEVMETHOD(pci_enable_busmaster, cardbus_enable_busmaster_method), + DEVMETHOD(pci_disable_busmaster, cardbus_disable_busmaster_method), + DEVMETHOD(pci_enable_io, cardbus_enable_io_method), + DEVMETHOD(pci_disable_io, cardbus_disable_io_method), + DEVMETHOD(pci_get_powerstate, cardbus_get_powerstate_method), + DEVMETHOD(pci_set_powerstate, cardbus_set_powerstate_method), {0,0} }; static driver_t cardbus_driver = { "cardbus", cardbus_methods, 0 /* no softc */ }; static devclass_t cardbus_devclass; DRIVER_MODULE(cardbus, pccbb, cardbus_driver, cardbus_devclass, 0, 0); /* MODULE_DEPEND(cardbus, pccbb, 1, 1, 1); */ Index: head/sys/dev/cardbus/cardbus_cis.c =================================================================== --- head/sys/dev/cardbus/cardbus_cis.c (revision 82377) +++ head/sys/dev/cardbus/cardbus_cis.c (revision 82378) @@ -1,628 +1,1025 @@ /* * Copyright (c) 2000,2001 Jonathan Chen. * 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, * without modification, immediately at the beginning of the file. * 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$ */ /* * CIS Handling for the Cardbus Bus */ #define CARDBUS_DEBUG #include #include #include #include #include #include #include #include -#include +#include +#include +#include #include +#include #include #include "card_if.h" #if defined CARDBUS_DEBUG #define DPRINTF(a) printf a #define DEVPRINTF(x) device_printf x #else #define DPRINTF(a) #define DEVPRINTF(x) #endif #if !defined(lint) static const char rcsid[] = "$FreeBSD$"; #endif #define DECODE_PARAMS \ (device_t cbdev, device_t child, int id, int len, \ - u_int8_t *tupledata, u_int32_t *start, u_int32_t *off, \ + u_int8_t *tupledata, u_int32_t start, u_int32_t *off, \ struct tuple_callbacks *info) struct tuple_callbacks { int id; char *name; int (*func) DECODE_PARAMS; }; #define DECODE_PROTOTYPE(NAME) static int decode_tuple_ ## NAME DECODE_PARAMS DECODE_PROTOTYPE(generic); DECODE_PROTOTYPE(nothing); DECODE_PROTOTYPE(copy); DECODE_PROTOTYPE(linktarget); DECODE_PROTOTYPE(vers_1); DECODE_PROTOTYPE(funcid); DECODE_PROTOTYPE(manfid); DECODE_PROTOTYPE(funce); DECODE_PROTOTYPE(bar); DECODE_PROTOTYPE(unhandled); DECODE_PROTOTYPE(end); static int cardbus_read_tuple_conf(device_t cbdev, device_t child, - u_int32_t *start, u_int32_t *off, int *tupleid, int *len, + u_int32_t start, u_int32_t *off, int *tupleid, int *len, u_int8_t *tupledata); -static int cardbus_read_tuple_exrom(device_t cbdev, struct resource *mem, - u_int32_t *start, u_int32_t *off, int *tupleid, int *len, +static int cardbus_read_tuple_mem(device_t cbdev, struct resource *res, + u_int32_t start, u_int32_t *off, int *tupleid, int *len, u_int8_t *tupledata); -static int cardbus_read_tuple_mem(device_t cbdev, device_t child, - u_int32_t *start, u_int32_t *off, int *tupleid, int *len, - u_int8_t *tupledata); static int cardbus_read_tuple(device_t cbdev, device_t child, - u_int32_t *start, u_int32_t *off, int *tupleid, int *len, - u_int8_t *tupledata); + struct resource *res, u_int32_t start, u_int32_t *off, + int *tupleid, int *len, u_int8_t *tupledata); +static void cardbus_read_tuple_finish(device_t cbdev, device_t child, + int rid, struct resource *res); +static struct resource *cardbus_read_tuple_init(device_t cbdev, device_t child, + u_int32_t *start, int *rid); static int decode_tuple(device_t cbdev, device_t child, int tupleid, - int len, u_int8_t *tupledata, u_int32_t *start, + int len, u_int8_t *tupledata, u_int32_t start, u_int32_t *off, struct tuple_callbacks *callbacks); static int cardbus_parse_cis(device_t cbdev, device_t child, struct tuple_callbacks *callbacks); +static int barsort(const void *a, const void *b); +static int cardbus_alloc_resources(device_t cbdev, device_t child); +static void cardbus_add_map(device_t cbdev, device_t child, int reg); +static void cardbus_pickup_maps(device_t cbdev, device_t child); + #define MAKETUPLE(NAME,FUNC) { CISTPL_ ## NAME, #NAME, decode_tuple_ ## FUNC } static char *funcnames[] = { "Multi-Functioned", "Memory", "Serial Port", "Parallel Port", "Fixed Disk", "Video Adaptor", "Network Adaptor", "AIMS", "SCSI", "Security" }; +struct cardbus_quirk { + u_int32_t devid; /* Vendor/device of the card */ + int type; +#define CARDBUS_QUIRK_MAP_REG 1 /* PCI map register in weird place */ + int arg1; + int arg2; +}; + +struct cardbus_quirk cardbus_quirks[] = { + { 0 } +}; + static struct cis_tupleinfo *cisread_buf; static int ncisread_buf; /* * Handler functions for various CIS tuples */ DECODE_PROTOTYPE(generic) { #ifdef CARDBUS_DEBUG int i; if (info) printf("TUPLE: %s [%d]:", info->name, len); else printf("TUPLE: Unknown(0x%02x) [%d]:", id, len); for (i = 0; i < len; i++) { if (i % 0x10 == 0 && len > 0x10) printf("\n 0x%02x:", i); printf(" %02x", tupledata[i]); } printf("\n"); #endif return 0; } DECODE_PROTOTYPE(nothing) { return 0; } DECODE_PROTOTYPE(copy) { struct cis_tupleinfo *tmpbuf; tmpbuf = malloc(sizeof(struct cis_tupleinfo) * (ncisread_buf+1), M_DEVBUF, M_WAITOK); if (ncisread_buf > 0) { memcpy(tmpbuf, cisread_buf, sizeof(struct cis_tupleinfo) * ncisread_buf); free(cisread_buf, M_DEVBUF); } cisread_buf = tmpbuf; cisread_buf[ncisread_buf].id = id; cisread_buf[ncisread_buf].len = len; cisread_buf[ncisread_buf].data = malloc(len, M_DEVBUF, M_WAITOK); memcpy(cisread_buf[ncisread_buf].data, tupledata, len); ncisread_buf++; return 0; } DECODE_PROTOTYPE(linktarget) { #ifdef CARDBUS_DEBUG int i; printf("TUPLE: %s [%d]:", info->name, len); for (i = 0; i < len; i++) { if (i % 0x10 == 0 && len > 0x10) printf("\n 0x%02x:", i); printf(" %02x", tupledata[i]); } printf("\n"); #endif if (len != 3 || tupledata[0] != 'C' || tupledata[1] != 'I' || tupledata[2] != 'S') { printf("Invalid data for CIS Link Target!\n"); decode_tuple_generic(cbdev, child, id, len, tupledata, start, off, info); return EINVAL; } return 0; } DECODE_PROTOTYPE(vers_1) { int i; printf("Product version: %d.%d\n", tupledata[0], tupledata[1]); printf("Product name: "); for (i = 2; i < len; i++) { if (tupledata[i] == '\0') printf(" | "); else if (tupledata[i] == 0xff) break; else printf("%c", tupledata[i]); } printf("\n"); return 0; } DECODE_PROTOTYPE(funcid) { int i; int numnames = sizeof(funcnames) / sizeof(funcnames[0]); printf("Functions: "); for (i = 0; i < len; i++) { if (tupledata[i] < numnames) printf("%s", funcnames[tupledata[i]]); else printf("Unknown(%d)", tupledata[i]); if (i < len-1) printf(", "); } printf("\n"); return 0; } DECODE_PROTOTYPE(manfid) { int i; printf("Manufacturer ID: "); for (i = 0; i < len; i++) printf("%02x", tupledata[i]); printf("\n"); return 0; } DECODE_PROTOTYPE(funce) { int i; printf("Function Extension: "); for (i = 0; i < len; i++) printf("%02x", tupledata[i]); printf("\n"); return 0; } DECODE_PROTOTYPE(bar) { if (len != 6) { printf("*** ERROR *** BAR length not 6 (%d)\n", len); return EINVAL; } else { + struct cardbus_devinfo *dinfo = device_get_ivars(child); int type; int reg; u_int32_t bar; - u_int32_t len; - struct resource *res; reg = *(u_int16_t*)tupledata; len = *(u_int32_t*)(tupledata + 2); if (reg & TPL_BAR_REG_AS) { type = SYS_RES_IOPORT; } else { type = SYS_RES_MEMORY; } bar = (reg & TPL_BAR_REG_ASI_MASK) - 1; if (bar < 0 || bar > 5 || (type == SYS_RES_IOPORT && bar == 5)) { device_printf(cbdev, "Invalid BAR number: %02x(%02x)\n", reg, bar); return 0; } bar = CARDBUS_BASE0_REG + bar * 4; - DEVPRINTF((cbdev, "Opening BAR: type=%s, bar=%02x, len=%04x\n", - (type==SYS_RES_MEMORY)?"MEM":"IO", bar, len)); - res = bus_generic_alloc_resource(child, child, type, &bar, 0, - ~0, len, rman_make_alignment_flags(len) | RF_ACTIVE); - if (res == NULL) { - device_printf(cbdev, "Cannot allocate BAR %02x\n", bar); + if (type == SYS_RES_MEMORY) { + if (bar & TPL_BAR_REG_PREFETCHABLE) + dinfo->mprefetchable |= BARBIT(bar); + if (bar & TPL_BAR_REG_BELOW1MB) + dinfo->mbelow1mb |= BARBIT(bar); + } else if (type == SYS_RES_IOPORT) { + if (bar & TPL_BAR_REG_BELOW1MB) + dinfo->ibelow1mb |= BARBIT(bar); } + DEVPRINTF((cbdev, "Opening BAR: type=%s, bar=%02x, " + "len=%04x%s%s\n", + (type==SYS_RES_MEMORY)?"MEM":"IO", bar, len, + (type==SYS_RES_MEMORY&&dinfo->mprefetchable&BARBIT(bar))? + " (Prefetchable)":"", + type==SYS_RES_MEMORY? + ((dinfo->mbelow1mb&BARBIT(bar))?" (Below 1Mb)":"") + :(dinfo->ibelow1mb&BARBIT(bar))?" (Below 1Mb)":"" + )); + + resource_list_add(&dinfo->resources, type, bar, 0UL, ~0UL, len); } return 0; } DECODE_PROTOTYPE(unhandled) { printf("TUPLE: %s [%d] is unhandled! Bailing...", info->name, len); return -1; } DECODE_PROTOTYPE(end) { printf("CIS reading done\n"); return 0; } /* * Functions to read the a tuple from the card */ static int -cardbus_read_tuple_conf(device_t cbdev, device_t child, u_int32_t *start, +cardbus_read_tuple_conf(device_t cbdev, device_t child, u_int32_t start, u_int32_t *off, int *tupleid, int *len, u_int8_t *tupledata) { int i, j; u_int32_t e; u_int32_t loc; - loc = CARDBUS_CIS_ADDR(*start) + *off; + loc = start + *off; e = pci_read_config(child, loc - loc % 4, 4); for (j = loc % 4; j > 0; j--) e >>= 8; *len = 0; for (i = loc, j = -2; j < *len; j++, i++) { if (i % 4 == 0) e = pci_read_config(child, i, 4); if (j == -2) *tupleid = 0xff & e; else if (j == -1) *len = 0xff & e; else tupledata[j] = 0xff & e; e >>= 8; } *off += *len + 2; return 0; } static int -cardbus_read_tuple_exrom(device_t cbdev, struct resource *mem, u_int32_t *start, +cardbus_read_tuple_mem(device_t cbdev, struct resource *res, u_int32_t start, u_int32_t *off, int *tupleid, int *len, u_int8_t *tupledata) { -#define READROM(rom, type, offset) \ - (*((u_int ## type ##_t *)(((unsigned char*)rom) + offset))) + bus_space_tag_t bt; + bus_space_handle_t bh; + int ret; - int romnum = 0; - unsigned char *data; - u_int32_t imagesize; - unsigned char *image; - int imagenum; + bt = rman_get_bustag(res); + bh = rman_get_bushandle(res); - image = (unsigned char*)rman_get_virtual(mem); - imagenum = CARDBUS_CIS_ASI_ROM_IMAGE(*start); - do { - if (READROM(image, 16, CARDBUS_EXROM_SIGNATURE) != 0xaa55) { - device_printf(cbdev, "Bad header in rom %d: %04x\n", - romnum, *(u_int16_t*)(image + - CARDBUS_EXROM_SIGNATURE)); - return ENXIO; - } - data = image + READROM(image, 16, CARDBUS_EXROM_DATA_PTR); - imagesize = READROM(data, 16, CARDBUS_EXROM_DATA_IMAGE_LENGTH); - - if (imagesize == 0) { - /* - * XXX some ROMs seem to have this as zero, - * can we assume this means 1 block? - */ - imagesize = 1; - } - imagesize <<= 9; - - if (imagenum == romnum) { - image += CARDBUS_CIS_ADDR(*start) + *off; - *tupleid = image[0]; - *len = image[1]; - memcpy(tupledata, image+2, *len); - *off += *len+2; - return 0; - } - image += imagesize; - romnum++; - } while ((READROM(data, 8, CARDBUS_EXROM_DATA_INDICATOR) & 0x80) == 0); - device_printf(cbdev, "Cannot read CIS: Not enough images of rom\n"); - return ENOENT; -#undef READROM + *tupleid = bus_space_read_1(bt, bh, start + *off); + *len = bus_space_read_1(bt, bh, start + *off + 1); + bus_space_read_region_1(bt, bh, *off + start + 2, tupledata, *len); + ret = 0; + *off += *len + 2; + return ret; } static int -cardbus_read_tuple_mem(device_t cbdev, device_t child, u_int32_t *start, - u_int32_t *off, int *tupleid, int *len, u_int8_t *tupledata) +cardbus_read_tuple(device_t cbdev, device_t child, struct resource *res, + u_int32_t start, u_int32_t *off, int *tupleid, int *len, + u_int8_t *tupledata) { - struct resource *mem; - bus_space_tag_t bt; - bus_space_handle_t bh; - int rid; - int ret; - - if (CARDBUS_CIS_SPACE(*start) == CARDBUS_CIS_ASI_ROM) { - rid = CARDBUS_ROM_REG; + if (res == (struct resource*)~0UL) { + return cardbus_read_tuple_conf(cbdev, child, start, off, + tupleid, len, tupledata); } else { - rid = CARDBUS_BASE0_REG + (CARDBUS_CIS_SPACE(*start) - 1) * 4; + return cardbus_read_tuple_mem(cbdev, res, start, off, + tupleid, len, tupledata); } +} - mem = bus_alloc_resource(child, SYS_RES_MEMORY, &rid, 0, ~0, - 1, RF_ACTIVE); - bt = rman_get_bustag(mem); - bh = rman_get_bushandle(mem); - if (mem == NULL) { - device_printf(cbdev, "Failed to get memory for CIS reading\n"); - return ENOMEM; +static void +cardbus_read_tuple_finish(device_t cbdev, device_t child, int rid, + struct resource *res) +{ + if (res != (struct resource*)~0UL) { + bus_release_resource(cbdev, SYS_RES_MEMORY, rid, res); + pci_write_config(child, rid, 0, 4); + PCI_DISABLE_IO(cbdev, child, SYS_RES_MEMORY); } - - if(CARDBUS_CIS_SPACE(*start) == CARDBUS_CIS_ASI_ROM) { - ret = cardbus_read_tuple_exrom(cbdev, mem, start, off, tupleid, - len, tupledata); - } else { - *tupleid = bus_space_read_1(bt, bh, - CARDBUS_CIS_ADDR(*start) + *off); - *len = bus_space_read_1(bt, bh, - CARDBUS_CIS_ADDR(*start) + *off + 1); - bus_space_read_multi_1(rman_get_bustag(mem), - rman_get_bushandle(mem), - *off + CARDBUS_CIS_ADDR(*start), tupledata, *len); - ret = 0; - *off += *len+2; - } - bus_release_resource(child, SYS_RES_MEMORY, rid, mem); - return ret; } -static int -cardbus_read_tuple(device_t cbdev, device_t child, u_int32_t *start, - u_int32_t *off, int *tupleid, int *len, u_int8_t *tupledata) +static struct resource * +cardbus_read_tuple_init(device_t cbdev, device_t child, u_int32_t *start, + int *rid) { - switch(CARDBUS_CIS_SPACE(*start)) { + u_int32_t testval; + u_int32_t size; + struct resource *res; + + switch (CARDBUS_CIS_SPACE(*start)) { case CARDBUS_CIS_ASI_TUPLE: - return cardbus_read_tuple_conf(cbdev, child, start, off, - tupleid, len, tupledata); + /* CIS in tuple space need no initialization */ + return (struct resource*)~0UL; case CARDBUS_CIS_ASI_BAR0: case CARDBUS_CIS_ASI_BAR1: case CARDBUS_CIS_ASI_BAR2: case CARDBUS_CIS_ASI_BAR3: case CARDBUS_CIS_ASI_BAR4: case CARDBUS_CIS_ASI_BAR5: + *rid = CARDBUS_BASE0_REG + (CARDBUS_CIS_SPACE(*start) - 1) * 4; + pci_write_config(child, *rid, ~0UL, 4); + break; case CARDBUS_CIS_ASI_ROM: - return cardbus_read_tuple_mem(cbdev, child, start, off, - tupleid, len, tupledata); + *rid = CARDBUS_ROM_REG; + pci_write_config(child, *rid, CARDBUS_ROM_ADDRMASK, 4); + break; default: device_printf(cbdev, "Unable to read CIS: Unknown space: %d\n", CARDBUS_CIS_SPACE(*start)); - return EINVAL; + return NULL; } + + /* figure out how much space we need */ + testval = pci_read_config(child, *rid, 4); + if (testval & 1) { + device_printf(cbdev, "CIS Space is IO, expecting memory.\n"); + return NULL; + } + size = CARDBUS_MAPREG_MEM_SIZE(testval); + if (size < 4096) + size = 4096; + /* allocate the memory space to read CIS */ + res = bus_alloc_resource(cbdev, SYS_RES_MEMORY, rid, 0, ~0, size, + rman_make_alignment_flags(size) | RF_ACTIVE); + if (res == NULL) { + device_printf(cbdev, "Unable to allocate resource " + "to read CIS.\n"); + return NULL; + } + pci_write_config(child, *rid, + rman_get_start(res) | ((*rid == CARDBUS_ROM_REG)? + CARDBUS_ROM_ENABLE : 0), + 4); + PCI_ENABLE_IO(cbdev, child, SYS_RES_MEMORY); + + /* Flip to the right ROM image if CIS is in ROM */ + if (CARDBUS_CIS_SPACE(*start) == CARDBUS_CIS_ASI_ROM) { + bus_space_tag_t bt; + bus_space_handle_t bh; + int imagenum; + u_int32_t imagesize; + int mystart = 0; + int romnum = 0; + int dataptr; + + bt = rman_get_bustag(res); + bh = rman_get_bushandle(res); + + imagenum = CARDBUS_CIS_ASI_ROM_IMAGE(*start); + for (romnum = 0;; romnum++) { + if (bus_space_read_2(bt, bh, + mystart+CARDBUS_EXROM_SIGNATURE) != 0xaa55) { + device_printf(cbdev, "Bad header in rom %d: " + "[%x] %04x\n", romnum, mystart + + CARDBUS_EXROM_SIGNATURE, + bus_space_read_2(bt, bh, + mystart+CARDBUS_EXROM_SIGNATURE)); + bus_release_resource(cbdev, SYS_RES_MEMORY, + *rid, res); + *rid = 0; + return NULL; + } + dataptr = mystart + bus_space_read_2(bt, bh, + mystart + CARDBUS_EXROM_DATA_PTR); + imagesize = bus_space_read_2(bt, bh, + dataptr + CARDBUS_EXROM_DATA_IMAGE_LENGTH); + + if (imagesize == 0) { + /* + * XXX some ROMs seem to have this as zero, + * can we assume this means 1 block? + */ + imagesize = 1; + } + imagesize <<= 9; + + if (romnum == imagenum) + break; + if ((bus_space_read_1(bt, bh, mystart + + CARDBUS_EXROM_DATA_INDICATOR) & 0x80) == 0) { + device_printf(cbdev, "Cannot read CIS: " + "Not enough images of rom\n"); + return NULL; + } + mystart += imagesize; + } + *start = mystart + CARDBUS_CIS_ADDR(*start); + } else { + *start = CARDBUS_CIS_SPACE(*start); + } + return res; } /* * Dispatch the right handler function per tuple */ static int decode_tuple(device_t cbdev, device_t child, int tupleid, int len, - u_int8_t *tupledata, u_int32_t *start, u_int32_t *off, + u_int8_t *tupledata, u_int32_t start, u_int32_t *off, struct tuple_callbacks *callbacks) { int i; for (i = 0; callbacks[i].id != CISTPL_GENERIC; i++) { if (tupleid == callbacks[i].id) return callbacks[i].func(cbdev, child, tupleid, len, tupledata, start, off, &callbacks[i]); } if (tupleid < CISTPL_CUSTOMSTART) { device_printf(cbdev, "Undefined tuple encountered, " "CIS parsing terminated\n"); return EINVAL; } return callbacks[i].func(cbdev, child, tupleid, len, tupledata, start, off, NULL); } static int cardbus_parse_cis(device_t cbdev, device_t child, struct tuple_callbacks *callbacks) { u_int8_t tupledata[MAXTUPLESIZE]; int tupleid; int len; int expect_linktarget; u_int32_t start, off; + struct resource *res; + int rid; bzero(tupledata, MAXTUPLESIZE); expect_linktarget = TRUE; start = pci_read_config(child, CARDBUS_CIS_REG, 4); off = 0; + res = cardbus_read_tuple_init(cbdev, child, &start, &rid); + if (res == NULL) + return ENXIO; do { - cardbus_read_tuple(cbdev, child, &start, &off, &tupleid, &len, - tupledata); + if (0 != cardbus_read_tuple(cbdev, child, res, start, &off, + &tupleid, &len, tupledata)) { + device_printf(cbdev, "Failed to read CIS.\n"); + cardbus_read_tuple_finish(cbdev, child, rid, res); + return ENXIO; + } if (expect_linktarget && tupleid != CISTPL_LINKTARGET) { device_printf(cbdev, "Expecting link target, got 0x%x\n", tupleid); + cardbus_read_tuple_finish(cbdev, child, rid, res); return EINVAL; } expect_linktarget = decode_tuple(cbdev, child, tupleid, len, - tupledata, &start, &off, - callbacks); - if (expect_linktarget != 0) + tupledata, start, &off, callbacks); + if (expect_linktarget != 0) { + cardbus_read_tuple_finish(cbdev, child, rid, res); return expect_linktarget; + } } while (tupleid != CISTPL_END); + cardbus_read_tuple_finish(cbdev, child, rid, res); return 0; } +static int +barsort(const void *a, const void *b) +{ + return (*(const struct resource_list_entry **)b)->count - + (*(const struct resource_list_entry **)a)->count; +} + +static int +cardbus_alloc_resources(device_t cbdev, device_t child) +{ + struct cardbus_devinfo *dinfo = device_get_ivars(child); + int count; + struct resource_list_entry *rle; + struct resource_list_entry **barlist; + int tmp; + u_int32_t mem_psize = 0, mem_nsize = 0, io_size = 0; + struct resource *res; + u_int32_t start,end; + int rid, flags; + + count = 0; + SLIST_FOREACH(rle, &dinfo->resources, link) { + count++; + } + if (count == 0) + return 0; + barlist = malloc(sizeof(struct resource_list_entry*) * count, M_DEVBUF, + M_WAITOK); + count = 0; + SLIST_FOREACH(rle, &dinfo->resources, link) { + barlist[count] = rle; + if (rle->type == SYS_RES_IOPORT) { + io_size += rle->count; + } else if (rle->type == SYS_RES_MEMORY) { + if (dinfo->mprefetchable & BARBIT(rle->rid)) + mem_psize += rle->count; + else + mem_nsize += rle->count; + } + count++; + } + + /* + * We want to allocate the largest resource first, so that our + * allocated memory is packed. + */ + qsort(barlist, count, sizeof(struct resource_list_entry*), barsort); + + /* Allocate prefetchable memory */ + flags = 0; + for (tmp = 0; tmp < count; tmp++) { + if (barlist[tmp]->res == NULL && + barlist[tmp]->type == SYS_RES_MEMORY && + dinfo->mprefetchable & BARBIT(barlist[tmp]->rid)) { + flags = rman_make_alignment_flags(barlist[tmp]->count); + break; + } + } + if (flags > 0) { /* If any prefetchable memory is requested... */ + /* + * First we allocate one big space for all resources of this + * type. We do this because our parent, pccbb, needs to open + * a window to forward all addresses within the window, and + * it would be best if nobody else has resources allocated + * within the window. + * (XXX: Perhaps there might be a better way to do this?) + */ + rid = 0; + res = bus_alloc_resource(cbdev, SYS_RES_MEMORY, &rid, 0, + (dinfo->mprefetchable & dinfo->mbelow1mb)?0xFFFFF:~0UL, + mem_psize, flags); + start = rman_get_start(res); + end = rman_get_end(res); + DEVPRINTF((cbdev, "Prefetchable memory at %x-%x\n", start, end)); + /* + * Now that we know the region is free, release it and hand it + * out piece by piece. + */ + bus_release_resource(cbdev, SYS_RES_MEMORY, rid, res); + for (tmp = 0; tmp < count; tmp++) { + if (barlist[tmp]->res == NULL && + barlist[tmp]->type == SYS_RES_MEMORY && + dinfo->mprefetchable & BARBIT(barlist[tmp]->rid)) { + barlist[tmp]->res = bus_alloc_resource(cbdev, + barlist[tmp]->type, + &barlist[tmp]->rid, start, end, + barlist[tmp]->count, + rman_make_alignment_flags( + barlist[tmp]->count)); + if (barlist[tmp]->res == NULL) { + mem_nsize += barlist[tmp]->count; + dinfo->mprefetchable &= + ~BARBIT(barlist[tmp]->rid); + DEVPRINTF((cbdev, "Cannot pre-allocate " + "prefetchable memory, will try as " + "non-prefetchable.\n")); + } else { + barlist[tmp]->start = + rman_get_start(barlist[tmp]->res); + barlist[tmp]->end = + rman_get_end(barlist[tmp]->res); + pci_write_config(child, + barlist[tmp]->rid, + barlist[tmp]->start, 4); + DEVPRINTF((cbdev, "Prefetchable memory " + "rid=%x at %lx-%lx\n", + barlist[tmp]->rid, + barlist[tmp]->start, + barlist[tmp]->end)); + } + } + } + } + + /* Allocate non-prefetchable memory */ + flags = 0; + for (tmp = 0; tmp < count; tmp++) { + if (barlist[tmp]->res == NULL && + barlist[tmp]->type == SYS_RES_MEMORY) { + flags = rman_make_alignment_flags(barlist[tmp]->count); + break; + } + } + if (flags > 0) { /* If any non-prefetchable memory is requested... */ + /* + * First we allocate one big space for all resources of this + * type. We do this because our parent, pccbb, needs to open + * a window to forward all addresses within the window, and + * it would be best if nobody else has resources allocated + * within the window. + * (XXX: Perhaps there might be a better way to do this?) + */ + rid = 0; + res = bus_alloc_resource(cbdev, SYS_RES_MEMORY, &rid, 0, + ((~dinfo->mprefetchable) & dinfo->mbelow1mb)?0xFFFFF:~0UL, + mem_nsize, flags); + start = rman_get_start(res); + end = rman_get_end(res); + DEVPRINTF((cbdev, "Non-prefetchable memory at %x-%x\n", + start, end)); + /* + * Now that we know the region is free, release it and hand it + * out piece by piece. + */ + bus_release_resource(cbdev, SYS_RES_MEMORY, rid, res); + for (tmp = 0; tmp < count; tmp++) { + if (barlist[tmp]->res == NULL && + barlist[tmp]->type == SYS_RES_MEMORY) { + barlist[tmp]->res = bus_alloc_resource(cbdev, + barlist[tmp]->type, &barlist[tmp]->rid, + start, end, barlist[tmp]->count, + rman_make_alignment_flags( + barlist[tmp]->count)); + if (barlist[tmp]->res == NULL) { + DEVPRINTF((cbdev, "Cannot pre-allocate " + "memory for cardbus device\n")); + return ENOMEM; + } + barlist[tmp]->start = + rman_get_start(barlist[tmp]->res); + barlist[tmp]->end = rman_get_end( + barlist[tmp]->res); + pci_write_config(child, barlist[tmp]->rid, + barlist[tmp]->start, 4); + DEVPRINTF((cbdev, "Non-prefetchable memory " + "rid=%x at %lx-%lx (%lx)\n", + barlist[tmp]->rid, barlist[tmp]->start, + barlist[tmp]->end, barlist[tmp]->count)); + } + } + } + + /* Allocate IO ports */ + flags = 0; + for (tmp = 0; tmp < count; tmp++) { + if (barlist[tmp]->res == NULL && + barlist[tmp]->type == SYS_RES_IOPORT) { + flags = rman_make_alignment_flags(barlist[tmp]->count); + break; + } + } + if (flags > 0) { /* If any IO port is requested... */ + /* + * First we allocate one big space for all resources of this + * type. We do this because our parent, pccbb, needs to open + * a window to forward all addresses within the window, and + * it would be best if nobody else has resources allocated + * within the window. + * (XXX: Perhaps there might be a better way to do this?) + */ + rid = 0; + res = bus_alloc_resource(cbdev, SYS_RES_IOPORT, &rid, 0, + (dinfo->ibelow1mb)?0xFFFFF:~0UL, io_size, flags); + start = rman_get_start(res); + end = rman_get_end(res); + DEVPRINTF((cbdev, "IO port at %x-%x\n", start, end)); + /* + * Now that we know the region is free, release it and hand it + * out piece by piece. + */ + bus_release_resource(cbdev, SYS_RES_IOPORT, rid, res); + for (tmp = 0; tmp < count; tmp++) { + if (barlist[tmp]->res == NULL && + barlist[tmp]->type == SYS_RES_IOPORT) { + barlist[tmp]->res = bus_alloc_resource(cbdev, + barlist[tmp]->type, &barlist[tmp]->rid, + start, end, barlist[tmp]->count, + rman_make_alignment_flags( + barlist[tmp]->count)); + if (barlist[tmp]->res == NULL) { + DEVPRINTF((cbdev, "Cannot pre-allocate " + "IO port for cardbus device\n")); + return ENOMEM; + } + barlist[tmp]->start = + rman_get_start(barlist[tmp]->res); + barlist[tmp]->end = + rman_get_end(barlist[tmp]->res); + pci_write_config(child, barlist[tmp]->rid, + barlist[tmp]->start, 4); + DEVPRINTF((cbdev, "IO port rid=%x at %lx-%lx\n", + barlist[tmp]->rid, barlist[tmp]->start, + barlist[tmp]->end)); + } + } + } + + /* Allocate IRQ */ + /* XXX: Search CIS for IRQ description */ + rid = 0; + res = bus_alloc_resource(cbdev, SYS_RES_IRQ, &rid, 0, ~0UL, 1, + RF_SHAREABLE); + resource_list_add(&dinfo->resources, SYS_RES_IRQ, rid, + rman_get_start(res), rman_get_end(res), 1); + rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, rid); + rle->res = res; + dinfo->cfg.intline = rman_get_start(res); + pci_write_config(child, PCIR_INTLINE, rman_get_start(res), 1); + + return 0; +} + +/* + * Adding a memory/io resource (sans CIS) + */ + +static void +cardbus_add_map(device_t cbdev, device_t child, int reg) +{ + struct cardbus_devinfo *dinfo = device_get_ivars(child); + struct resource_list_entry *rle; + u_int32_t size; + u_int32_t testval; + int type; + + SLIST_FOREACH(rle, &dinfo->resources, link) { + if (rle->rid == reg) + return; + } + + if (reg == CARDBUS_ROM_REG) + testval = CARDBUS_ROM_ADDRMASK; + else + testval = ~0; + + pci_write_config(child, reg, testval, 4); + testval = pci_read_config(child, reg, 4); + + if (testval == ~0 || testval == 0) + return; + + if ((testval & 1) == 0) + type = SYS_RES_MEMORY; + else + type = SYS_RES_IOPORT; + + size = CARDBUS_MAPREG_MEM_SIZE(testval); + device_printf(cbdev, "Resource not specified in CIS: id=%x, size=%x\n", + reg, size); + resource_list_add(&dinfo->resources, type, reg, 0UL, ~0UL, size); +} + +static void +cardbus_pickup_maps(device_t cbdev, device_t child) +{ + struct cardbus_devinfo *dinfo = device_get_ivars(child); + struct cardbus_quirk *q; + int reg; + + /* + * Try to pick up any resources that was not specified in CIS. + * Some devices (eg, 3c656) does not list all resources required by + * the driver in its CIS. + * XXX: should we do this or use quirks? + */ + for (reg = 0; reg < dinfo->cfg.nummaps; reg++) { + cardbus_add_map(cbdev, child, PCIR_MAPS + reg * 4); + } + + for (q = &cardbus_quirks[0]; q->devid; q++) { + if (q->devid == ((dinfo->cfg.device << 16) | dinfo->cfg.vendor) + && q->type == CARDBUS_QUIRK_MAP_REG) { + cardbus_add_map(cbdev, child, q->arg1); + } + } +} + int cardbus_cis_read(device_t cbdev, device_t child, u_int8_t id, struct cis_tupleinfo **buff, int *nret) { struct tuple_callbacks cisread_callbacks[] = { MAKETUPLE(NULL, nothing), /* first entry will be overwritten */ MAKETUPLE(NULL, nothing), MAKETUPLE(DEVICE, nothing), MAKETUPLE(LONG_LINK_CB, unhandled), MAKETUPLE(INDIRECT, unhandled), MAKETUPLE(CONFIG_CB, nothing), MAKETUPLE(CFTABLE_ENTRY_CB, nothing), MAKETUPLE(LONGLINK_MFC, unhandled), MAKETUPLE(BAR, nothing), MAKETUPLE(PWR_MGMNT, nothing), MAKETUPLE(EXTDEVICE, nothing), MAKETUPLE(CHECKSUM, nothing), MAKETUPLE(LONGLINK_A, unhandled), MAKETUPLE(LONGLINK_C, unhandled), MAKETUPLE(LINKTARGET, nothing), MAKETUPLE(NO_LINK, nothing), MAKETUPLE(VERS_1, nothing), MAKETUPLE(ALTSTR, nothing), MAKETUPLE(DEVICE_A, nothing), MAKETUPLE(JEDEC_C, nothing), MAKETUPLE(JEDEC_A, nothing), MAKETUPLE(CONFIG, nothing), MAKETUPLE(CFTABLE_ENTRY, nothing), MAKETUPLE(DEVICE_OC, nothing), MAKETUPLE(DEVICE_OA, nothing), MAKETUPLE(DEVICE_GEO, nothing), MAKETUPLE(DEVICE_GEO_A, nothing), MAKETUPLE(MANFID, nothing), MAKETUPLE(FUNCID, nothing), MAKETUPLE(FUNCE, nothing), MAKETUPLE(SWIL, nothing), MAKETUPLE(VERS_2, nothing), MAKETUPLE(FORMAT, nothing), MAKETUPLE(GEOMETRY, nothing), MAKETUPLE(BYTEORDER, nothing), MAKETUPLE(DATE, nothing), MAKETUPLE(BATTERY, nothing), MAKETUPLE(ORG, nothing), MAKETUPLE(END, end), MAKETUPLE(GENERIC, nothing), }; int ret; cisread_callbacks[0].id = id; cisread_callbacks[0].name = "COPY"; cisread_callbacks[0].func = decode_tuple_copy; ncisread_buf = 0; cisread_buf = NULL; ret = cardbus_parse_cis(cbdev, child, cisread_callbacks); *buff = cisread_buf; *nret = ncisread_buf; return ret; } void -cardbus_cis_free(device_t cbdev, struct cis_tupleinfo *buff, int* nret) +cardbus_cis_free(device_t cbdev, struct cis_tupleinfo *buff, int *nret) { int i; for (i = 0; i < *nret; i++) free(buff[i].data, M_DEVBUF); if (*nret > 0) free(buff, M_DEVBUF); } int cardbus_do_cis(device_t cbdev, device_t child) { + int ret; struct tuple_callbacks init_callbacks[] = { MAKETUPLE(NULL, generic), MAKETUPLE(DEVICE, generic), MAKETUPLE(LONG_LINK_CB, unhandled), MAKETUPLE(INDIRECT, unhandled), MAKETUPLE(CONFIG_CB, generic), MAKETUPLE(CFTABLE_ENTRY_CB, generic), MAKETUPLE(LONGLINK_MFC, unhandled), MAKETUPLE(BAR, bar), MAKETUPLE(PWR_MGMNT, generic), MAKETUPLE(EXTDEVICE, generic), MAKETUPLE(CHECKSUM, generic), MAKETUPLE(LONGLINK_A, unhandled), MAKETUPLE(LONGLINK_C, unhandled), MAKETUPLE(LINKTARGET, linktarget), MAKETUPLE(NO_LINK, generic), MAKETUPLE(VERS_1, vers_1), MAKETUPLE(ALTSTR, generic), MAKETUPLE(DEVICE_A, generic), MAKETUPLE(JEDEC_C, generic), MAKETUPLE(JEDEC_A, generic), MAKETUPLE(CONFIG, generic), MAKETUPLE(CFTABLE_ENTRY, generic), MAKETUPLE(DEVICE_OC, generic), MAKETUPLE(DEVICE_OA, generic), MAKETUPLE(DEVICE_GEO, generic), MAKETUPLE(DEVICE_GEO_A, generic), MAKETUPLE(MANFID, manfid), MAKETUPLE(FUNCID, funcid), MAKETUPLE(FUNCE, funce), MAKETUPLE(SWIL, generic), MAKETUPLE(VERS_2, generic), MAKETUPLE(FORMAT, generic), MAKETUPLE(GEOMETRY, generic), MAKETUPLE(BYTEORDER, generic), MAKETUPLE(DATE, generic), MAKETUPLE(BATTERY, generic), MAKETUPLE(ORG, generic), MAKETUPLE(END, end), MAKETUPLE(GENERIC, generic), }; - return cardbus_parse_cis(cbdev, child, init_callbacks); + + ret = cardbus_parse_cis(cbdev, child, init_callbacks); + if (ret < 0) + return ret; + cardbus_pickup_maps(cbdev, child); + return cardbus_alloc_resources(cbdev, child); } Index: head/sys/dev/cardbus/cardbus_cis.h =================================================================== --- head/sys/dev/cardbus/cardbus_cis.h (revision 82377) +++ head/sys/dev/cardbus/cardbus_cis.h (revision 82378) @@ -1,107 +1,111 @@ /* * Copyright (c) 2000,2001 Jonathan Chen. * 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, * without modification, immediately at the beginning of the file. * 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$ */ /* * Cardbus CIS definitions */ struct cis_tupleinfo; int cardbus_do_cis(device_t, device_t); int cardbus_cis_read(device_t, device_t, u_int8_t, struct cis_tupleinfo**, int*); void cardbus_cis_free(device_t, struct cis_tupleinfo*, int*); #define MAXTUPLESIZE 0x400 /* CIS TUPLES */ #define CISTPL_NULL 0x00 #define CISTPL_DEVICE 0x01 #define CISTPL_LONG_LINK_CB 0x02 #define CISTPL_INDIRECT 0x03 #define CISTPL_CONFIG_CB 0x04 #define CISTPL_CFTABLE_ENTRY_CB 0x05 #define CISTPL_LONGLINK_MFC 0x06 #define CISTPL_BAR 0x07 #define CISTPL_PWR_MGMNT 0x08 #define CISTPL_EXTDEVICE 0x09 #define CISTPL_CHECKSUM 0x10 #define CISTPL_LONGLINK_A 0x11 #define CISTPL_LONGLINK_C 0x12 #define CISTPL_LINKTARGET 0x13 #define CISTPL_NO_LINK 0x14 #define CISTPL_VERS_1 0x15 #define CISTPL_ALTSTR 0x16 #define CISTPL_DEVICE_A 0x17 #define CISTPL_JEDEC_C 0x18 #define CISTPL_JEDEC_A 0x19 #define CISTPL_CONFIG 0x1A #define CISTPL_CFTABLE_ENTRY 0x1B #define CISTPL_DEVICE_OC 0x1C #define CISTPL_DEVICE_OA 0x1D #define CISTPL_DEVICE_GEO 0x1E #define CISTPL_DEVICE_GEO_A 0x1F #define CISTPL_MANFID 0x20 #define CISTPL_FUNCID 0x21 #define CISTPL_FUNCE 0x22 #define CISTPL_SWIL 0x23 #define CISTPL_VERS_2 0x40 #define CISTPL_FORMAT 0x41 #define CISTPL_GEOMETRY 0x42 #define CISTPL_BYTEORDER 0x43 #define CISTPL_DATE 0x44 #define CISTPL_BATTERY 0x45 #define CISTPL_ORG 0x46 #define CISTPL_CUSTOMSTART 0x80 #define CISTPL_END 0xFF #define CISTPL_GENERIC -1 /* catchall */ /* BAR */ #define TPL_BAR_REG_ASI_MASK 0x07 #define TPL_BAR_REG_AS 0x10 +#define TPL_BAR_REG_PREFETCHABLE_ONLY 0x20 +#define TPL_BAR_REG_PREFETCHABLE_CACHEABLE 0x40 +#define TPL_BAR_REG_PREFETCHABLE 0x60 +#define TPL_BAR_REG_BELOW1MB 0x80 /* CISTPL_FUNC */ #define TPL_FUNC_MF 0 /* multi function tuple */ #define TPL_FUNC_MEM 1 /* memory */ #define TPL_FUNC_SERIAL 2 /* serial, including modem and fax */ #define TPL_FUNC_PARALLEL 3 /* parallel, including printer and SCSI */ #define TPL_FUNC_DISK 4 /* Disk */ #define TPL_FUNC_VIDEO 5 /* Video Adaptor */ #define TPL_FUNC_LAN 6 /* LAN Adaptor */ #define TPL_FUNC_AIMS 7 /* Auto Inclement Mass Strages */ /* TPL_FUNC_LAN */ #define TPL_FUNCE_LAN_TECH 1 /* technology */ #define TPL_FUNCE_LAN_SPEED 2 /* speed */ #define TPL_FUNCE_LAN_MEDIA 2 /* which media do you use? */ #define TPL_FUNCE_LAN_NID 4 /* node id (address) */ #define TPL_FUNCE_LAN_CONN 5 /* connector type (shape) */ Index: head/sys/dev/cardbus/cardbusvar.h =================================================================== --- head/sys/dev/cardbus/cardbusvar.h (revision 82377) +++ head/sys/dev/cardbus/cardbusvar.h (revision 82378) @@ -1,39 +1,51 @@ /* * Copyright (c) 2000,2001 Jonathan Chen. * 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, * without modification, immediately at the beginning of the file. * 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$ */ /* * Structure definitions for the Cardbus Bus driver */ +struct cardbus_intrlist { + SLIST_ENTRY(cardbus_intrlist) link; + device_t dev; + struct resource *irq; + void *cookie; +}; + struct cardbus_devinfo { struct resource_list resources; pcicfgregs cfg; struct pci_conf conf; + u_int8_t mprefetchable; /* bit mask of prefetchable BARs */ + u_int8_t mbelow1mb; /* bit mask of BARs which require below 1Mb */ + u_int8_t ibelow1mb; /* bit mask of BARs which require below 1Mb */ +#define BARBIT(RID) (1<<((RID)-CARDBUS_BASE0_REG)/4) + SLIST_HEAD(, cardbus_intrlist) intrlist; }; Index: head/sys/dev/pccard/card_if.m =================================================================== --- head/sys/dev/pccard/card_if.m (revision 82377) +++ head/sys/dev/pccard/card_if.m (revision 82378) @@ -1,248 +1,257 @@ # # Copyright (c) 1999 M. Warner Losh. # 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 INTERFACE card; # WARNING: THIS FILE IS USED BY BOTH OLDCARD AND NEWCARD. MAKE SURE # YOU TEST BOTH KERNELS IF CHANGING THIS FILE. # # Companion interface for pccard. We need to set attributes for memory # and i/o port mappings (as well as other types of attributes) that have # a well defined meaning inside the pccard/cardbus system. The bus # methods are inadequate for this because this must be done at the time the # resources are set for the device, which predates their activation. Also, # the driver activating the resources doesn't necessarily know or need to know # these attributes. # METHOD int set_res_flags { device_t dev; device_t child; int restype; int rid; u_long value; }; METHOD int get_res_flags { device_t dev; device_t child; int restype; int rid; u_long *value; }; # # Sets the memory offset of the pccard bridge's window into attribute # or common memory space. # METHOD int set_memory_offset { device_t dev; device_t child; int rid; u_int32_t cardaddr; u_int32_t *deltap; } METHOD int get_memory_offset { device_t dev; device_t child; int rid; u_int32_t *offset; } # # pccard bridges call this method to initate the attachment of a card # METHOD int attach_card { device_t dev; } # # pccard bridges call this to detach a card. # METHOD int detach_card { device_t dev; int flags; } +# +# pccard/cardbus buses call this to request a reprobe of the bus. +# reprobe only initiated if the child bus is the same type the card inserted. +# +METHOD int reprobe_card { + device_t dev; + device_t child; +} + HEADER { #define DETACH_FORCE 0x01 #define DETACH_NOWARN 0x02 } # # Returns the type of card this is. Maybe we don't need this. # METHOD int get_type { device_t dev; int *type; } # # Returns the function number for this device. # METHOD int get_function { device_t dev; device_t child; int *func; } # # Activates (and powers up if necessary) the card's nth function # since each function gets its own device, there is no need to # to specify a function number # METHOD int activate_function { device_t dev; device_t child; } METHOD int deactivate_function { device_t dev; device_t child; } # # Compatibility methods for OLDCARD drivers. We use these routines to make # it possible to call the OLDCARD driver's probe routine in the context that # it expects. For OLDCARD these are implemented as pass throughs to the # device_{probe,attach} routines. For NEWCARD they are implemented such # such that probe becomes strictly a matching routine and attach does both # the old probe and old attach. # # compat devices should use the following: # # /* Device interface */ # DEVMETHOD(device_probe), pccard_compat_probe), # DEVMETHOD(device_attach), pccard_compat_attach), # /* Card interface */ # DEVMETHOD(card_compat_match, foo_match), /* newly written */ # DEVMETHOD(card_compat_probe, foo_probe), /* old probe */ # DEVMETHOD(card_compat_attach, foo_attach), /* old attach */ # # This will allow a single driver binary image to be used for both # OLDCARD and NEWCARD. # # Drivers wishing to not retain OLDCARD compatibility needn't do this. # # The compat_do_* versions are so that we can make the pccard_compat_probe # and _attach static lines and have the bus system pick the right version # to use so we don't enshrine pccard_* symbols in the driver's module. # METHOD int compat_probe { device_t dev; } METHOD int compat_attach { device_t dev; } CODE { static int null_do_probe(device_t bus, device_t dev) __unused; static int null_do_probe(device_t bus, device_t dev) { return (CARD_COMPAT_DO_PROBE(device_get_parent(bus), dev)); } static int null_do_attach(device_t bus, device_t dev) { return (CARD_COMPAT_DO_ATTACH(device_get_parent(bus), dev)); } } METHOD int compat_do_probe { device_t bus; device_t dev; } DEFAULT null_do_attach; METHOD int compat_do_attach { device_t bus; device_t dev; } DEFAULT null_do_attach; # # Helper method for the above. When a compatibility driver is converted, # one must write a match routine. This routine is unused on OLDCARD but # is used as a discriminator for NEWCARD. # METHOD int compat_match { device_t dev; } # # Method for devices to ask its CIS-enabled parent bus for CIS info. # Device driver requests all tuples if type 'id', the routine places # 'nret' number of tuples in 'buff'. Returns 0 if all tuples processed, # or an error code if processing was aborted. # Users of this method will be responsible for freeing the memory allocated # by calling the cis_free method. # HEADER { struct cis_tupleinfo { u_int8_t id; int len; char *data; }; }; CODE { static int null_cis_read(device_t dev, device_t child, u_int8_t id, struct cis_tupleinfo **buff, int *nret) { *nret = 0; *buff = NULL; return ENXIO; } static void null_cis_free(device_t dev, struct cis_tupleinfo *buff, int *nret) { return; } }; METHOD int cis_read { device_t dev; device_t child; u_int8_t id; struct cis_tupleinfo **buff; int *nret; } DEFAULT null_cis_read; METHOD int cis_free { device_t dev; struct cis_tupleinfo *buff; int nret; } DEFAULT null_cis_free; Index: head/sys/dev/pccard/pccard.c =================================================================== --- head/sys/dev/pccard/pccard.c (revision 82377) +++ head/sys/dev/pccard/pccard.c (revision 82378) @@ -1,1135 +1,1215 @@ /* $NetBSD: pcmcia.c,v 1.23 2000/07/28 19:17:02 drochner Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1997 Marc Horowitz. 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 Marc Horowitz. * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "power_if.h" #include "card_if.h" #define PCCARDDEBUG #ifdef PCCARDDEBUG int pccard_debug = 1; #define DPRINTF(arg) if (pccard_debug) printf arg #define DEVPRINTF(arg) if (pccard_debug) device_printf arg #define PRVERBOSE(arg) printf arg #define DEVPRVERBOSE(arg) device_printf arg #else #define DPRINTF(arg) #define DEVPRINTF(arg) #define PRVERBOSE(arg) if (bootverbose) printf arg #define DEVPRVERBOSE(arg) if (bootverbose) device_printf arg #endif #ifdef PCCARDVERBOSE int pccard_verbose = 1; #else int pccard_verbose = 0; #endif -int pccard_print(void *, const char *); +static int pccard_ccr_read(struct pccard_function *pf, int ccr); +static void pccard_ccr_write(struct pccard_function *pf, int ccr, int val); +static int pccard_attach_card(device_t dev); +static int pccard_detach_card(device_t dev, int flags); +static const struct pccard_product *pccard_product_lookup(device_t dev, + const struct pccard_product *tab, size_t ent_size, + pccard_product_match_fn matchfn); +static int pccard_card_gettype(device_t dev, int *type); +static void pccard_function_init(struct pccard_function *pf); +static void pccard_function_free(struct pccard_function *pf); +static int pccard_function_enable(struct pccard_function *pf); +static void pccard_function_disable(struct pccard_function *pf); +static int pccard_compat_do_probe(device_t bus, device_t dev); +static int pccard_compat_do_attach(device_t bus, device_t dev); +static int pccard_add_children(device_t dev, int busno); +static int pccard_probe(device_t dev); +static int pccard_attach(device_t dev); +static int pccard_detach(device_t dev); +static void pccard_print_resources(struct resource_list *rl, + const char *name, int type, int count, const char *format); +static int pccard_print_child(device_t dev, device_t child); +static int pccard_set_resource(device_t dev, device_t child, int type, + int rid, u_long start, u_long count); +static int pccard_get_resource(device_t dev, device_t child, int type, + int rid, u_long *startp, u_long *countp); +static void pccard_delete_resource(device_t dev, device_t child, int type, + int rid); +static int pccard_set_res_flags(device_t dev, device_t child, int type, + int rid, u_int32_t flags); +static int pccard_set_memory_offset(device_t dev, device_t child, int rid, + u_int32_t offset, u_int32_t *deltap); +static int pccard_read_ivar(device_t bus, device_t child, int which, + u_char *result); +static void pccard_driver_added(device_t dev, driver_t *driver); +static struct resource *pccard_alloc_resource(device_t dev, + device_t child, int type, int *rid, u_long start, + u_long end, u_long count, u_int flags); +static int pccard_release_resource(device_t dev, device_t child, int type, + int rid, struct resource *r); +static void pccard_child_detached(device_t parent, device_t dev); +static void pccard_intr(void *arg); +static int pccard_setup_intr(device_t dev, device_t child, + struct resource *irq, int flags, driver_intr_t *intr, + void *arg, void **cookiep); +static int pccard_teardown_intr(device_t dev, device_t child, + struct resource *r, void *cookie); -int +static int pccard_ccr_read(struct pccard_function *pf, int ccr) { return (bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh, pf->pf_ccr_offset + ccr)); } -void +static void pccard_ccr_write(struct pccard_function *pf, int ccr, int val) { if ((pf->ccr_mask) & (1 << (ccr / 2))) { bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh, pf->pf_ccr_offset + ccr, val); } } static int pccard_attach_card(device_t dev) { struct pccard_softc *sc = PCCARD_SOFTC(dev); struct pccard_function *pf; struct pccard_ivar *ivar; device_t child; - int attached; - sc->intr_handler_count = 0; /* * this is here so that when socket_enable calls gettype, trt happens */ STAILQ_INIT(&sc->card.pf_head); DEVPRINTF((dev, "chip_socket_enable\n")); POWER_ENABLE_SOCKET(device_get_parent(dev), dev); DEVPRINTF((dev, "read_cis\n")); pccard_read_cis(sc); DEVPRINTF((dev, "check_cis_quirks\n")); pccard_check_cis_quirks(dev); /* * bail now if the card has no functions, or if there was an error in * the cis. */ if (sc->card.error) { device_printf (dev, "CARD ERROR!\n"); return (1); } if (STAILQ_EMPTY(&sc->card.pf_head)) { device_printf (dev, "Card has no functions!\n"); return (1); } if (1) pccard_print_cis(dev); - attached = 0; - DEVPRINTF((dev, "functions scanning\n")); STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { if (STAILQ_EMPTY(&pf->cfe_head)) continue; pf->sc = sc; pf->cfe = NULL; pf->dev = NULL; } -#if 0 - DEVPRINTF((dev, "chip_socket_disable\n")); - POWER_DISABLE_SOCKET(device_get_parent(dev), dev); -#endif + STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { if (STAILQ_EMPTY(&pf->cfe_head)) continue; /* * In NetBSD, the drivers are responsible for activating * each function of a card. I think that in FreeBSD we * want to activate them enough for the usual bus_*_resource * routines will do the right thing. This many mean a * departure from the current NetBSD model. * * This could get really ugly for multifunction cards. But * it might also just fall out of the FreeBSD resource model. * */ ivar = malloc(sizeof(struct pccard_ivar), M_DEVBUF, M_WAITOK | M_ZERO); child = device_add_child(dev, NULL, -1); device_set_ivars(child, ivar); ivar->fcn = pf; pf->dev = child; /* * XXX We might want to move the next two lines into * XXX the pccard interface layer. For the moment, this * XXX is OK, but some drivers want to pick the config * XXX entry to use as well as some address tweaks (mostly * XXX due to bugs in decode logic that makes some * XXX addresses illegal or broken). */ pccard_function_init(pf); + if (sc->sc_enabled_count == 0) + POWER_ENABLE_SOCKET(device_get_parent(dev), dev); if (pccard_function_enable(pf) == 0 && device_probe_and_attach(child) == 0) { - attached++; - DEVPRINTF((sc->dev, "function %d CCR at %d " "offset %x: %x %x %x %x, %x %x %x %x, %x\n", pf->number, pf->pf_ccr_window, pf->pf_ccr_offset, pccard_ccr_read(pf, 0x00), pccard_ccr_read(pf, 0x02), pccard_ccr_read(pf, 0x04), pccard_ccr_read(pf, 0x06), pccard_ccr_read(pf, 0x0A), pccard_ccr_read(pf, 0x0C), pccard_ccr_read(pf, 0x0E), pccard_ccr_read(pf, 0x10), pccard_ccr_read(pf, 0x12))); } else { - device_delete_child(dev, child); + pccard_function_disable(pf); } } + if (sc->sc_enabled_count == 0) + pccard_detach_card(dev, 0); return (0); } static int pccard_detach_card(device_t dev, int flags) { struct pccard_softc *sc = PCCARD_SOFTC(dev); struct pccard_function *pf; + struct pccard_config_entry *cfe; /* * We are running on either the PCCARD socket's event thread * or in user context detaching a device by user request. */ STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { - if (STAILQ_FIRST(&pf->cfe_head) == NULL) - continue; + int state = device_get_state(pf->dev); + if (state == DS_ATTACHED || state == DS_BUSY) + device_detach(pf->dev); pccard_function_disable(pf); - /* - * XXX must also actually delete resources created by - * pccard_function_init(). If pccard_function_init - * keeps things allocated it is a bug. - */ + pccard_function_free(pf); if (pf->dev != NULL) device_delete_child(dev, pf->dev); } + if (sc->sc_enabled_count == 0) + POWER_DISABLE_SOCKET(device_get_parent(dev), dev); + + while (NULL != (pf = STAILQ_FIRST(&sc->card.pf_head))) { + while (NULL != (cfe = STAILQ_FIRST(&pf->cfe_head))) { + STAILQ_REMOVE_HEAD(&pf->cfe_head, cfe_list); + free(cfe, M_DEVBUF); + } + STAILQ_REMOVE_HEAD(&sc->card.pf_head, pf_list); + free(pf, M_DEVBUF); + } return (0); } -const struct pccard_product * +static const struct pccard_product * pccard_product_lookup(device_t dev, const struct pccard_product *tab, size_t ent_size, pccard_product_match_fn matchfn) { const struct pccard_product *ent; int matches; u_int32_t fcn; u_int32_t vendor; u_int32_t prod; char *vendorstr; char *prodstr; #ifdef DIAGNOSTIC if (sizeof *ent > ent_size) panic("pccard_product_lookup: bogus ent_size %ld", (long) ent_size); #endif if (pccard_get_vendor(dev, &vendor)) return (NULL); if (pccard_get_product(dev, &prod)) return (NULL); if (pccard_get_function_number(dev, &fcn)) return (NULL); if (pccard_get_vendor_str(dev, &vendorstr)) return (NULL); if (pccard_get_product_str(dev, &prodstr)) return (NULL); - for (ent = tab; ent->pp_name != NULL; - ent = (const struct pccard_product *) - ((const char *) ent + ent_size)) { + for (ent = tab; ent->pp_name != NULL; ent = + (const struct pccard_product *) ((const char *) ent + ent_size)) { matches = 1; if (matches && ent->pp_vendor != PCCARD_VENDOR_ANY && vendor != ent->pp_vendor) matches = 0; if (matches && ent->pp_product != PCCARD_PRODUCT_ANY && prod != ent->pp_product) matches = 0; if (matches && fcn != ent->pp_expfunc) matches = 0; if (matches && ent->pp_cis[0] && strcmp(ent->pp_cis[0], vendorstr) != 0) matches = 0; if (matches && ent->pp_cis[1] && strcmp(ent->pp_cis[1], prodstr) != 0) matches = 0; /* XXX need to match cis[2] and cis[3] also XXX */ if (matchfn != NULL) matches = (*matchfn)(dev, ent, matches); if (matches) return (ent); } return (NULL); } static int pccard_card_gettype(device_t dev, int *type) { struct pccard_softc *sc = PCCARD_SOFTC(dev); struct pccard_function *pf; /* * set the iftype to memory if this card has no functions (not yet * probed), or only one function, and that is not initialized yet or * that is memory. */ pf = STAILQ_FIRST(&sc->card.pf_head); if (pf == NULL || (STAILQ_NEXT(pf, pf_list) == NULL && (pf->cfe == NULL || pf->cfe->iftype == PCCARD_IFTYPE_MEMORY))) *type = PCCARD_IFTYPE_MEMORY; else *type = PCCARD_IFTYPE_IO; return (0); } /* * Initialize a PCCARD function. May be called as long as the function is * disabled. */ -void +static void pccard_function_init(struct pccard_function *pf) { struct pccard_config_entry *cfe; int i; struct pccard_ivar *devi = PCCARD_IVAR(pf->dev); struct resource_list *rl = &devi->resources; struct resource_list_entry *rle; struct resource *r = 0; device_t bus; int start; int end; if (pf->pf_flags & PFF_ENABLED) { printf("pccard_function_init: function is enabled"); return; } bus = device_get_parent(pf->dev); /* Remember which configuration entry we are using. */ STAILQ_FOREACH(cfe, &pf->cfe_head, cfe_list) { for (i = 0; i < cfe->num_iospace; i++) cfe->iores[i] = NULL; cfe->irqres = NULL; for (i = 0; i < cfe->num_iospace; i++) { start = cfe->iospace[i].start; if (start) end = start + cfe->iospace[i].length - 1; else end = ~0; cfe->iorid[i] = i; r = cfe->iores[i] = bus_alloc_resource(bus, SYS_RES_IOPORT, &cfe->iorid[i], start, end, cfe->iospace[i].length, rman_make_alignment_flags(cfe->iospace[i].length)); if (cfe->iores[i] == NULL) goto not_this_one; resource_list_add(rl, SYS_RES_IOPORT, cfe->iorid[i], rman_get_start(r), rman_get_end(r), cfe->iospace[i].length); rle = resource_list_find(rl, SYS_RES_IOPORT, cfe->iorid[i]); rle->res = r; } if (cfe->num_memspace > 0) { goto not_this_one; } if (cfe->irqmask) { cfe->irqrid = 0; r = cfe->irqres = bus_alloc_resource(bus, SYS_RES_IRQ, &cfe->irqrid, 0, ~0, 1, 0); if (cfe->irqres == NULL) goto not_this_one; resource_list_add(rl, SYS_RES_IRQ, cfe->irqrid, rman_get_start(r), rman_get_end(r), 1); rle = resource_list_find(rl, SYS_RES_IRQ, cfe->irqrid); rle->res = r; } /* If we get to here, we've allocated all we need */ pf->cfe = cfe; break; not_this_one:; DEVPRVERBOSE((bus, "Allocation failed for cfe %d\n", cfe->number)); /* * Release resources that we partially allocated * from this config entry. */ for (i = 0; i < cfe->num_iospace; i++) { if (cfe->iores[i] != NULL) { bus_release_resource(bus, SYS_RES_IOPORT, cfe->iorid[i], cfe->iores[i]); - rle = resource_list_find(rl, SYS_RES_IOPORT, + rle = resource_list_find(rl, SYS_RES_IOPORT, cfe->iorid[i]); rle->res = NULL; resource_list_delete(rl, SYS_RES_IOPORT, cfe->iorid[i]); } cfe->iores[i] = NULL; } if (cfe->irqmask && cfe->irqres != NULL) { bus_release_resource(bus, SYS_RES_IRQ, cfe->irqrid, cfe->irqres); rle = resource_list_find(rl, SYS_RES_IRQ, cfe->irqrid); rle->res = NULL; resource_list_delete(rl, SYS_RES_IRQ, cfe->irqrid); cfe->irqres = NULL; } } } +/* + * Free resources allocated by pccard_function_init(), May be called as long + * as the function is disabled. + */ +static void +pccard_function_free(struct pccard_function *pf) +{ + struct pccard_ivar *devi = PCCARD_IVAR(pf->dev); + struct resource_list_entry *rle; + + if (pf->pf_flags & PFF_ENABLED) { + printf("pccard_function_init: function is enabled"); + return; + } + + SLIST_FOREACH(rle, &devi->resources, link) { + if (rle->res) { + if (rle->res->r_dev != pf->sc->dev) + device_printf(pf->sc->dev, + "function_free: Resource still owned by " + "child, oops. " + "(type=%d, rid=%d, addr=%lx)\n", + rle->type, rle->rid, + rman_get_start(rle->res)); + BUS_RELEASE_RESOURCE(device_get_parent(pf->sc->dev), + rle->res->r_dev, rle->type, rle->rid, rle->res); + rle->res = NULL; + } + } + resource_list_free(&devi->resources); +} + /* Enable a PCCARD function */ -int +static int pccard_function_enable(struct pccard_function *pf) { struct pccard_function *tmp; int reg; device_t dev = pf->sc->dev; if (pf->cfe == NULL) { DEVPRVERBOSE((dev, "No config entry could be allocated.\n")); return (ENOMEM); } /* * Increase the reference count on the socket, enabling power, if * necessary. */ - if (pf->sc->sc_enabled_count++ == 0) - POWER_ENABLE_SOCKET(device_get_parent(dev), dev); - DEVPRINTF((dev, "++enabled_count = %d\n", pf->sc->sc_enabled_count)); + pf->sc->sc_enabled_count++; if (pf->pf_flags & PFF_ENABLED) { /* * Don't do anything if we're already enabled. */ return (0); } /* * it's possible for different functions' CCRs to be in the same * underlying page. Check for that. */ STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { if ((tmp->pf_flags & PFF_ENABLED) && (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) && ((pf->ccr_base + PCCARD_CCR_SIZE) <= - (tmp->ccr_base - tmp->pf_ccr_offset + - tmp->pf_ccr_realsize))) { + (tmp->ccr_base - tmp->pf_ccr_offset + + tmp->pf_ccr_realsize))) { pf->pf_ccrt = tmp->pf_ccrt; pf->pf_ccrh = tmp->pf_ccrh; pf->pf_ccr_realsize = tmp->pf_ccr_realsize; /* * pf->pf_ccr_offset = (tmp->pf_ccr_offset - * tmp->ccr_base) + pf->ccr_base; */ /* pf->pf_ccr_offset = (tmp->pf_ccr_offset + pf->ccr_base) - tmp->ccr_base; */ pf->pf_ccr_window = tmp->pf_ccr_window; break; } } if (tmp == NULL) { pf->ccr_rid = 0; pf->ccr_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &pf->ccr_rid, 0, ~0, 1 << 10, RF_ACTIVE); if (!pf->ccr_res) goto bad; DEVPRINTF((dev, "ccr_res == %lx-%lx, base=%lx\n", rman_get_start(pf->ccr_res), rman_get_end(pf->ccr_res), pf->ccr_base)); CARD_SET_RES_FLAGS(device_get_parent(dev), dev, SYS_RES_MEMORY, pf->ccr_rid, PCCARD_A_MEM_ATTR); CARD_SET_MEMORY_OFFSET(device_get_parent(dev), dev, pf->ccr_rid, pf->ccr_base, &pf->pf_ccr_offset); pf->pf_ccrt = rman_get_bustag(pf->ccr_res); pf->pf_ccrh = rman_get_bushandle(pf->ccr_res); pf->pf_ccr_realsize = 1; } reg = (pf->cfe->number & PCCARD_CCR_OPTION_CFINDEX); reg |= PCCARD_CCR_OPTION_LEVIREQ; if (pccard_mfc(pf->sc)) { reg |= (PCCARD_CCR_OPTION_FUNC_ENABLE | PCCARD_CCR_OPTION_ADDR_DECODE); /* * XXX Need to enable PCCARD_CCR_OPTION_IRQ_ENABLE if * XXX we have an interrupt handler, but we don't know that * XXX at this point. */ /* reg |= PCCARD_CCR_OPTION_IREQ_ENABLE;*/ } pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); reg = 0; if ((pf->cfe->flags & PCCARD_CFE_IO16) == 0) reg |= PCCARD_CCR_STATUS_IOIS8; if (pf->cfe->flags & PCCARD_CFE_AUDIO) reg |= PCCARD_CCR_STATUS_AUDIO; pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg); pccard_ccr_write(pf, PCCARD_CCR_SOCKETCOPY, 0); if (pccard_mfc(pf->sc)) { long tmp, iosize; tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase; /* round up to nearest (2^n)-1 */ for (iosize = 1; iosize < tmp; iosize <<= 1) ; iosize--; pccard_ccr_write(pf, PCCARD_CCR_IOBASE0, pf->pf_mfc_iobase & 0xff); pccard_ccr_write(pf, PCCARD_CCR_IOBASE1, (pf->pf_mfc_iobase >> 8) & 0xff); pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0); pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0); pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize); } #ifdef PCCARDDEBUG if (pccard_debug) { STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { device_printf(tmp->sc->dev, "function %d CCR at %d offset %x: " "%x %x %x %x, %x %x %x %x, %x\n", tmp->number, tmp->pf_ccr_window, tmp->pf_ccr_offset, pccard_ccr_read(tmp, 0x00), pccard_ccr_read(tmp, 0x02), pccard_ccr_read(tmp, 0x04), pccard_ccr_read(tmp, 0x06), pccard_ccr_read(tmp, 0x0A), pccard_ccr_read(tmp, 0x0C), pccard_ccr_read(tmp, 0x0E), pccard_ccr_read(tmp, 0x10), pccard_ccr_read(tmp, 0x12)); } } #endif pf->pf_flags |= PFF_ENABLED; return (0); bad: /* * Decrement the reference count, and power down the socket, if * necessary. */ - if (--pf->sc->sc_enabled_count == 0) - POWER_DISABLE_SOCKET(device_get_parent(dev), dev); + pf->sc->sc_enabled_count--; DEVPRINTF((dev, "bad --enabled_count = %d\n", pf->sc->sc_enabled_count)); return (1); } /* Disable PCCARD function. */ -void +static void pccard_function_disable(struct pccard_function *pf) { struct pccard_function *tmp; device_t dev = pf->sc->dev; if (pf->cfe == NULL) panic("pccard_function_disable: function not initialized"); if ((pf->pf_flags & PFF_ENABLED) == 0) { /* * Don't do anything if we're already disabled. */ return; } if (pf->intr_handler != NULL) { - pf->intr_handler = NULL; - pf->intr_handler_arg = NULL; - pf->intr_handler_cookie = NULL; - pccard_ccr_write(pf, PCCARD_CCR_OPTION, - pccard_ccr_read(pf, PCCARD_CCR_OPTION) & - ~PCCARD_CCR_OPTION_IREQ_ENABLE); - - if (pf->sc->intr_handler_count == 1) { - struct pccard_ivar *ivar = PCCARD_IVAR(pf->dev); - struct resource_list_entry *rle = NULL; - - pf->sc->intr_handler_count--; - rle = resource_list_find(&ivar->resources, SYS_RES_IRQ, - 0); - if (rle == NULL) - panic("No IRQ for pccard?"); - - bus_teardown_intr(dev, rle->res, - &pf->sc->intr_handler_count); - } + struct pccard_ivar *devi = PCCARD_IVAR(pf->dev); + struct resource_list_entry *rle = + resource_list_find(&devi->resources, SYS_RES_IRQ, 0); + BUS_TEARDOWN_INTR(dev, pf->dev, rle->res, + pf->intr_handler_cookie); } /* * it's possible for different functions' CCRs to be in the same * underlying page. Check for that. Note we mark us as disabled * first to avoid matching ourself. */ pf->pf_flags &= ~PFF_ENABLED; STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { if ((tmp->pf_flags & PFF_ENABLED) && (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) && ((pf->ccr_base + PCCARD_CCR_SIZE) <= - (tmp->ccr_base - tmp->pf_ccr_offset + tmp->pf_ccr_realsize))) + (tmp->ccr_base - tmp->pf_ccr_offset + + tmp->pf_ccr_realsize))) break; } /* Not used by anyone else; unmap the CCR. */ if (tmp == NULL) { bus_release_resource(dev, SYS_RES_MEMORY, pf->ccr_rid, pf->ccr_res); pf->ccr_res = NULL; } /* * Decrement the reference count, and power down the socket, if * necessary. */ - if (--pf->sc->sc_enabled_count == 0) - POWER_DISABLE_SOCKET(device_get_parent(dev), dev); - DEVPRINTF((dev, "--enabled_count = %d\n", pf->sc->sc_enabled_count)); + pf->sc->sc_enabled_count--; } #if 0 /* XXX These functions are needed, but not like this XXX */ int pccard_io_map(struct pccard_function *pf, int width, bus_addr_t offset, bus_size_t size, struct pccard_io_handle *pcihp, int *windowp) { int reg; if (pccard_chip_io_map(pf->sc->pct, pf->sc->pch, width, offset, size, pcihp, windowp)) return (1); /* * XXX in the multifunction multi-iospace-per-function case, this * needs to cooperate with io_alloc to make sure that the spaces * don't overlap, and that the ccr's are set correctly */ if (pccard_mfc(pf->sc)) { long tmp, iosize; if (pf->pf_mfc_iomax == 0) { pf->pf_mfc_iobase = pcihp->addr + offset; pf->pf_mfc_iomax = pf->pf_mfc_iobase + size; } else { /* this makes the assumption that nothing overlaps */ if (pf->pf_mfc_iobase > pcihp->addr + offset) pf->pf_mfc_iobase = pcihp->addr + offset; if (pf->pf_mfc_iomax < pcihp->addr + offset + size) pf->pf_mfc_iomax = pcihp->addr + offset + size; } tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase; /* round up to nearest (2^n)-1 */ for (iosize = 1; iosize >= tmp; iosize <<= 1) ; iosize--; pccard_ccr_write(pf, PCCARD_CCR_IOBASE0, pf->pf_mfc_iobase & 0xff); pccard_ccr_write(pf, PCCARD_CCR_IOBASE1, (pf->pf_mfc_iobase >> 8) & 0xff); pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0); pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0); pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize); reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION); reg |= PCCARD_CCR_OPTION_ADDR_DECODE; pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); } return (0); } void pccard_io_unmap(struct pccard_function *pf, int window) { pccard_chip_io_unmap(pf->sc->pct, pf->sc->pch, window); /* XXX Anything for multi-function cards? */ } #endif /* * simulate the old "probe" routine. In the new world order, the driver * needs to grab devices while in the old they were assigned to the device by * the pccardd process. These symbols are exported to the upper layers. */ static int pccard_compat_do_probe(device_t bus, device_t dev) { return (CARD_COMPAT_MATCH(dev)); } static int pccard_compat_do_attach(device_t bus, device_t dev) { int err; err = CARD_COMPAT_PROBE(dev); if (err == 0) err = CARD_COMPAT_ATTACH(dev); return (err); } #define PCCARD_NPORT 2 #define PCCARD_NMEM 5 #define PCCARD_NIRQ 1 #define PCCARD_NDRQ 0 static int pccard_add_children(device_t dev, int busno) { /* Call parent to scan for any current children */ return (0); } static int pccard_probe(device_t dev) { device_set_desc(dev, "16-bit PCCard bus"); return (pccard_add_children(dev, device_get_unit(dev))); } static int pccard_attach(device_t dev) { struct pccard_softc *sc = PCCARD_SOFTC(dev); sc->dev = dev; sc->sc_enabled_count = 0; return (bus_generic_attach(dev)); } +static int +pccard_detach(device_t dev) +{ + pccard_detach_card(dev, 0); + return 0; +} + static void pccard_print_resources(struct resource_list *rl, const char *name, int type, int count, const char *format) { struct resource_list_entry *rle; int printed; int i; printed = 0; for (i = 0; i < count; i++) { rle = resource_list_find(rl, type, i); if (rle != NULL) { if (printed == 0) printf(" %s ", name); else if (printed > 0) printf(","); printed++; printf(format, rle->start); if (rle->count > 1) { printf("-"); printf(format, rle->start + rle->count - 1); } } else if (i > 3) { /* check the first few regardless */ break; } } } static int pccard_print_child(device_t dev, device_t child) { struct pccard_ivar *devi = PCCARD_IVAR(child); struct resource_list *rl = &devi->resources; int retval = 0; retval += bus_print_child_header(dev, child); retval += printf(" at"); if (devi != NULL) { pccard_print_resources(rl, "port", SYS_RES_IOPORT, PCCARD_NPORT, "%#lx"); pccard_print_resources(rl, "iomem", SYS_RES_MEMORY, PCCARD_NMEM, "%#lx"); pccard_print_resources(rl, "irq", SYS_RES_IRQ, PCCARD_NIRQ, "%ld"); pccard_print_resources(rl, "drq", SYS_RES_DRQ, PCCARD_NDRQ, "%ld"); retval += printf(" function %d config %d", devi->fcn->number, devi->fcn->cfe->number); } retval += bus_print_child_footer(dev, child); return (retval); } static int pccard_set_resource(device_t dev, device_t child, int type, int rid, u_long start, u_long count) { struct pccard_ivar *devi = PCCARD_IVAR(child); struct resource_list *rl = &devi->resources; if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY && type != SYS_RES_IRQ && type != SYS_RES_DRQ) return (EINVAL); if (rid < 0) return (EINVAL); if (type == SYS_RES_IOPORT && rid >= PCCARD_NPORT) return (EINVAL); if (type == SYS_RES_MEMORY && rid >= PCCARD_NMEM) return (EINVAL); if (type == SYS_RES_IRQ && rid >= PCCARD_NIRQ) return (EINVAL); if (type == SYS_RES_DRQ && rid >= PCCARD_NDRQ) return (EINVAL); resource_list_add(rl, type, rid, start, start + count - 1, count); - - return (0); + if (NULL != resource_list_alloc(rl, device_get_parent(dev), dev, + type, &rid, start, start + count - 1, count, 0)) + return 0; + else + return ENOMEM; } static int pccard_get_resource(device_t dev, device_t child, int type, int rid, u_long *startp, u_long *countp) { struct pccard_ivar *devi = PCCARD_IVAR(child); struct resource_list *rl = &devi->resources; struct resource_list_entry *rle; rle = resource_list_find(rl, type, rid); if (rle == NULL) return (ENOENT); if (startp != NULL) *startp = rle->start; if (countp != NULL) *countp = rle->count; return (0); } static void pccard_delete_resource(device_t dev, device_t child, int type, int rid) { struct pccard_ivar *devi = PCCARD_IVAR(child); struct resource_list *rl = &devi->resources; resource_list_delete(rl, type, rid); } static int pccard_set_res_flags(device_t dev, device_t child, int type, int rid, u_int32_t flags) { return (CARD_SET_RES_FLAGS(device_get_parent(dev), child, type, rid, flags)); } static int pccard_set_memory_offset(device_t dev, device_t child, int rid, - u_int32_t offset, u_int32_t *deltap) + u_int32_t offset, u_int32_t *deltap) { return (CARD_SET_MEMORY_OFFSET(device_get_parent(dev), child, rid, offset, deltap)); } static int pccard_read_ivar(device_t bus, device_t child, int which, u_char *result) { struct pccard_ivar *devi = PCCARD_IVAR(child); struct pccard_function *func = devi->fcn; struct pccard_softc *sc = PCCARD_SOFTC(bus); /* PCCARD_IVAR_ETHADDR unhandled from oldcard */ switch (which) { default: case PCCARD_IVAR_ETHADDR: return (ENOENT); break; case PCCARD_IVAR_VENDOR: *(u_int32_t *) result = sc->card.manufacturer; break; case PCCARD_IVAR_PRODUCT: *(u_int32_t *) result = sc->card.product; break; case PCCARD_IVAR_FUNCTION: *(u_int32_t *) result = func->function; break; case PCCARD_IVAR_FUNCTION_NUMBER: if (!func) { device_printf(bus, "No function number, bug!\n"); return (ENOENT); } *(u_int32_t *) result = func->number; break; case PCCARD_IVAR_VENDOR_STR: *(char **) result = sc->card.cis1_info[0]; break; case PCCARD_IVAR_PRODUCT_STR: *(char **) result = sc->card.cis1_info[1]; break; case PCCARD_IVAR_CIS3_STR: *(char **) result = sc->card.cis1_info[2]; break; case PCCARD_IVAR_CIS4_STR: *(char **) result = sc->card.cis1_info[2]; break; } return (0); } static void pccard_driver_added(device_t dev, driver_t *driver) { - /* - * XXX eventually we need to attach stuff when we know we - * XXX have kids. For now we do nothing because we normally - * XXX add children ourselves. We don't want to necessarily - * XXX force a reprobe. - */ + struct pccard_softc *sc = PCCARD_SOFTC(dev); + struct pccard_function *pf; + device_t child; + + if (sc->sc_enabled_count == 0) { + CARD_REPROBE_CARD(device_get_parent(dev), dev); + return; + } + + STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { + if (STAILQ_EMPTY(&pf->cfe_head)) + continue; + child = pf->dev; + if (device_get_state(child) != DS_NOTPRESENT) + continue; + if (pccard_function_enable(pf) == 0 && + device_probe_and_attach(child) == 0) { + DEVPRINTF((sc->dev, "function %d CCR at %d " + "offset %x: %x %x %x %x, %x %x %x %x, %x\n", + pf->number, pf->pf_ccr_window, pf->pf_ccr_offset, + pccard_ccr_read(pf, 0x00), + pccard_ccr_read(pf, 0x02), pccard_ccr_read(pf, 0x04), + pccard_ccr_read(pf, 0x06), pccard_ccr_read(pf, 0x0A), + pccard_ccr_read(pf, 0x0C), pccard_ccr_read(pf, 0x0E), + pccard_ccr_read(pf, 0x10), pccard_ccr_read(pf, 0x12))); + } else { + pccard_function_disable(pf); + } + } + return; } static struct resource * pccard_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { - struct resource_list_entry *rle = NULL; + struct pccard_ivar *dinfo; + struct resource_list_entry *rle = 0; + int passthrough = (device_get_parent(child) != dev); - /* XXX: This is an ugly way to fudge the resources. */ + if (passthrough) { + return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child, + type, rid, start, end, count, flags)); + } - if (device_get_parent(child) == dev) { - struct pccard_ivar *devi = PCCARD_IVAR(child); - struct resource_list *rl = &devi->resources; + dinfo = device_get_ivars(child); + rle = resource_list_find(&dinfo->resources, type, *rid); - rle = resource_list_find(rl, type, *rid); - } + if (!rle) + return NULL; /* no resource of that type/rid */ - if (rle != NULL) { - if (flags & RF_ACTIVE) - bus_activate_resource(dev, type, rle->rid, rle->res); - return (rle->res); + if (!rle->res) { + device_printf(dev, "WARNING: Resource not reserved by pccard bus\n"); + return NULL; + } else { + if (rle->res->r_dev != dev) return NULL; + bus_release_resource(dev, type, *rid, rle->res); + rle->res = NULL; + switch(type) { + case SYS_RES_IOPORT: + case SYS_RES_MEMORY: + if (!(flags & RF_ALIGNMENT_MASK)) + flags |= rman_make_alignment_flags(rle->count); + break; + case SYS_RES_IRQ: + flags |= RF_SHAREABLE; + break; + } + return resource_list_alloc(&dinfo->resources, dev, child, type, + rid, rle->start, rle->end, rle->count, flags); } - return (bus_generic_alloc_resource(dev, child, type, rid, start, - end, count, flags)); } static int pccard_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { - struct resource_list_entry *rle = NULL; + struct pccard_ivar *dinfo; + int passthrough = (device_get_parent(child) != dev); + struct resource_list_entry *rle = 0; + int ret; + int flags; - if (device_get_parent(child) == dev) { - struct pccard_ivar *devi = PCCARD_IVAR(child); - struct resource_list *rl = &devi->resources; + if (passthrough) + return BUS_RELEASE_RESOURCE(device_get_parent(dev), child, + type, rid, r); - rle = resource_list_find(rl, type, rid); - } + dinfo = device_get_ivars(child); - if (rle != NULL) { - return (bus_deactivate_resource(dev, type, rle->rid, rle->res)); - } + rle = resource_list_find(&dinfo->resources, type, rid); - return (bus_generic_release_resource(dev, child, type, rid, r)); -} - -static int -pccard_activate_resource(device_t dev, device_t child, int type, int rid, - struct resource *r) -{ - /* XXX need to write to the COR to activate this for mf cards */ - struct resource_list_entry *rle = NULL; - - if (device_get_parent(child) == dev) { - struct pccard_ivar *devi = PCCARD_IVAR(child); - struct resource_list *rl = &devi->resources; - - rle = resource_list_find(rl, type, rid); + if (!rle) { + device_printf(dev, "Allocated resource not found, " + "%d %x %lx %lx\n", + type, rid, rman_get_start(r), rman_get_size(r)); + return ENOENT; } - - if (rle != NULL) { - return (bus_activate_resource(dev, type, rle->rid, rle->res)); + if (!rle->res) { + device_printf(dev, "Allocated resource not recorded\n"); + return ENOENT; } - return (bus_generic_activate_resource(dev, child, type, rid, r)); -} - -static int -pccard_deactivate_resource(device_t dev, device_t child, int type, int rid, - struct resource *r) -{ - /* XXX need to write to the COR to deactivate this for mf cards */ - struct resource_list_entry *rle = NULL; - - if (device_get_parent(child) == dev) { - struct pccard_ivar *devi = PCCARD_IVAR(child); - struct resource_list *rl = &devi->resources; - - rle = resource_list_find(rl, type, rid); + ret = BUS_RELEASE_RESOURCE(device_get_parent(dev), child, + type, rid, r); + switch(type) { + case SYS_RES_IOPORT: + case SYS_RES_MEMORY: + flags = rman_make_alignment_flags(rle->count); + break; + case SYS_RES_IRQ: + flags = RF_SHAREABLE; + break; + default: + flags = 0; } - - if (rle != NULL) { - return (bus_deactivate_resource(dev, type, rle->rid, rle->res)); - } - - return (bus_generic_deactivate_resource(dev, child, type, rid, r)); + rle->res = bus_alloc_resource(dev, type, &rid, + rle->start, rle->end, rle->count, flags); + if (rle->res == NULL) + device_printf(dev, "release_resource: " + "unable to reaquire resource\n"); + return ret; } static void pccard_child_detached(device_t parent, device_t dev) { struct pccard_ivar *ivar = PCCARD_IVAR(dev); + struct pccard_function *pf = ivar->fcn; - if (parent == device_get_parent(dev)) - free(ivar, M_DEVBUF); + pccard_function_disable(pf); } static void pccard_intr(void *arg) { - struct pccard_softc *sc = (struct pccard_softc *) arg; - struct pccard_function *pf; - STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { - if (pf->intr_handler != NULL) { - int reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); - if (reg & PCCARD_CCR_STATUS_INTR) { - pccard_ccr_write(pf, PCCARD_CCR_STATUS, - reg & ~PCCARD_CCR_STATUS_INTR); - pf->intr_handler(pf->intr_handler_arg); - } - } + struct pccard_function *pf = (struct pccard_function*) arg; + int reg; + + if (pf->intr_handler == NULL) + panic("Null interrupt handler?\n"); + + reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); + if (reg & PCCARD_CCR_STATUS_INTR) { + pccard_ccr_write(pf, PCCARD_CCR_STATUS, + reg & ~PCCARD_CCR_STATUS_INTR); + pf->intr_handler(pf->intr_handler_arg); } } static int pccard_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep) { struct pccard_ivar *ivar = PCCARD_IVAR(child); struct pccard_function *func = ivar->fcn; - struct resource_list_entry *rle = NULL; - struct pccard_softc *sc = device_get_softc(dev); if (func->intr_handler != NULL) panic("Only one interrupt handler per function allowed\n"); - rle = resource_list_find(&ivar->resources, SYS_RES_IRQ, 0); - if (rle == NULL || rle->res != irq) - panic("irq in setup_intr does not match allocated irq\n"); - func->intr_handler = intr; func->intr_handler_arg = arg; - func->intr_handler_cookie = *cookiep = func; + func->intr_handler_cookie = *cookiep; pccard_ccr_write(func, PCCARD_CCR_OPTION, - pccard_ccr_read(func, PCCARD_CCR_OPTION) | + pccard_ccr_read(func, PCCARD_CCR_OPTION) | PCCARD_CCR_OPTION_IREQ_ENABLE); - if (sc->intr_handler_count++ == 0) { - rle = resource_list_find(&ivar->resources, SYS_RES_IRQ, 0); - if (rle == NULL) - panic("No IRQ for pccard?"); - - bus_setup_intr(dev, rle->res, INTR_TYPE_TTY/* | INTR_FAST*/, - pccard_intr, sc, (void*)&sc->intr_handler_count); - } + bus_setup_intr(dev, irq, INTR_TYPE_TTY/* | INTR_FAST*/, + pccard_intr, func, cookiep); return (0); } static int pccard_teardown_intr(device_t dev, device_t child, struct resource *r, void *cookie) { struct pccard_ivar *ivar = PCCARD_IVAR(child); struct pccard_function *func = ivar->fcn; - struct pccard_softc *sc = device_get_softc(dev); + int ret; - if (func->intr_handler_cookie != cookie) - panic("pccard teardown of unknown interrupt handler\n"); - - func->intr_handler = NULL; - func->intr_handler_arg = NULL; - func->intr_handler_cookie = NULL; pccard_ccr_write(func, PCCARD_CCR_OPTION, pccard_ccr_read(func, PCCARD_CCR_OPTION) & ~PCCARD_CCR_OPTION_IREQ_ENABLE); - if (--sc->intr_handler_count == 0) { - struct resource_list_entry *rle = NULL; - - rle = resource_list_find(&ivar->resources, SYS_RES_IRQ, 0); - if (rle == NULL) - panic("No IRQ for pccard?"); - - bus_teardown_intr(dev, rle->res, &sc->intr_handler_count); + ret = bus_teardown_intr(dev, r, cookie); + if (ret == 0) { + func->intr_handler = NULL; + func->intr_handler_arg = NULL; + func->intr_handler_cookie = NULL; } - return (0); + + return (ret); } static device_method_t pccard_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pccard_probe), DEVMETHOD(device_attach, pccard_attach), - DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_detach, pccard_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, pccard_print_child), DEVMETHOD(bus_driver_added, pccard_driver_added), DEVMETHOD(bus_child_detached, pccard_child_detached), DEVMETHOD(bus_alloc_resource, pccard_alloc_resource), DEVMETHOD(bus_release_resource, pccard_release_resource), - DEVMETHOD(bus_activate_resource, pccard_activate_resource), - DEVMETHOD(bus_deactivate_resource, pccard_deactivate_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, pccard_setup_intr), DEVMETHOD(bus_teardown_intr, pccard_teardown_intr), DEVMETHOD(bus_set_resource, pccard_set_resource), DEVMETHOD(bus_get_resource, pccard_get_resource), DEVMETHOD(bus_delete_resource, pccard_delete_resource), DEVMETHOD(bus_read_ivar, pccard_read_ivar), /* Card Interface */ DEVMETHOD(card_set_res_flags, pccard_set_res_flags), DEVMETHOD(card_set_memory_offset, pccard_set_memory_offset), DEVMETHOD(card_get_type, pccard_card_gettype), DEVMETHOD(card_attach_card, pccard_attach_card), DEVMETHOD(card_detach_card, pccard_detach_card), DEVMETHOD(card_compat_do_probe, pccard_compat_do_probe), DEVMETHOD(card_compat_do_attach, pccard_compat_do_attach), { 0, 0 } }; static driver_t pccard_driver = { "pccard", pccard_methods, sizeof(struct pccard_softc) }; devclass_t pccard_devclass; DRIVER_MODULE(pccard, pcic, pccard_driver, pccard_devclass, 0, 0); DRIVER_MODULE(pccard, pc98pcic, pccard_driver, pccard_devclass, 0, 0); DRIVER_MODULE(pccard, pccbb, pccard_driver, pccard_devclass, 0, 0); DRIVER_MODULE(pccard, tcic, pccard_driver, pccard_devclass, 0, 0); MODULE_VERSION(pccard, 1); /*MODULE_DEPEND(pccard, pcic, 1, 1, 1);*/ Index: head/sys/dev/pccard/pccardvar.h =================================================================== --- head/sys/dev/pccard/pccardvar.h (revision 82377) +++ head/sys/dev/pccard/pccardvar.h (revision 82378) @@ -1,349 +1,339 @@ /* $NetBSD: pcmciavar.h,v 1.12 2000/02/08 12:51:31 enami Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1997 Marc Horowitz. 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 Marc Horowitz. * 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. */ #include #include #include #include "card_if.h" extern int pccard_verbose; /* * Contains information about mapped/allocated i/o spaces. */ struct pccard_io_handle { bus_space_tag_t iot; /* bus space tag (from chipset) */ bus_space_handle_t ioh; /* mapped space handle */ bus_addr_t addr; /* resulting address in bus space */ bus_size_t size; /* size of i/o space */ int flags; /* misc. information */ int width; }; #define PCCARD_IO_ALLOCATED 0x01 /* i/o space was allocated */ /* * Contains information about allocated memory space. */ struct pccard_mem_handle { bus_space_tag_t memt; /* bus space tag (from chipset) */ bus_space_handle_t memh; /* mapped space handle */ bus_addr_t addr; /* resulting address in bus space */ bus_size_t size; /* size of mem space */ bus_size_t realsize; /* how much we really allocated */ long offset; int kind; }; /* pccard itself */ #define PCCARD_CFE_MWAIT_REQUIRED 0x0001 #define PCCARD_CFE_RDYBSY_ACTIVE 0x0002 #define PCCARD_CFE_WP_ACTIVE 0x0004 #define PCCARD_CFE_BVD_ACTIVE 0x0008 #define PCCARD_CFE_IO8 0x0010 #define PCCARD_CFE_IO16 0x0020 #define PCCARD_CFE_IRQSHARE 0x0040 #define PCCARD_CFE_IRQPULSE 0x0080 #define PCCARD_CFE_IRQLEVEL 0x0100 #define PCCARD_CFE_POWERDOWN 0x0200 #define PCCARD_CFE_READONLY 0x0400 #define PCCARD_CFE_AUDIO 0x0800 struct pccard_config_entry { int number; u_int32_t flags; int iftype; int num_iospace; /* * The card will only decode this mask in any case, so we can * do dynamic allocation with this in mind, in case the suggestions * below are no good. */ u_long iomask; struct { u_long length; u_long start; } iospace[4]; /* XXX this could be as high as 16 */ u_int16_t irqmask; int num_memspace; struct { u_long length; u_long cardaddr; u_long hostaddr; } memspace[2]; /* XXX this could be as high as 8 */ int maxtwins; struct resource *iores[4]; int iorid[4]; struct resource *irqres; int irqrid; struct resource *memres[2]; int memrid[2]; STAILQ_ENTRY(pccard_config_entry) cfe_list; }; struct pccard_function { /* read off the card */ int number; int function; int last_config_index; u_long ccr_base; u_long ccr_mask; struct resource *ccr_res; int ccr_rid; STAILQ_HEAD(, pccard_config_entry) cfe_head; STAILQ_ENTRY(pccard_function) pf_list; /* run-time state */ struct pccard_softc *sc; struct pccard_config_entry *cfe; struct pccard_mem_handle pf_pcmh; device_t dev; #define pf_ccrt pf_pcmh.memt #define pf_ccrh pf_pcmh.memh #define pf_ccr_realsize pf_pcmh.realsize bus_addr_t pf_ccr_offset; int pf_ccr_window; long pf_mfc_iobase; long pf_mfc_iomax; int pf_flags; driver_intr_t *intr_handler; void *intr_handler_arg; void *intr_handler_cookie; }; /* pf_flags */ #define PFF_ENABLED 0x0001 /* function is enabled */ struct pccard_card { int cis1_major; int cis1_minor; /* XXX waste of space? */ char cis1_info_buf[256]; char *cis1_info[4]; /* * Use int32_t for manufacturer and product so that they can * hold the id value found in card CIS and special value that * indicates no id was found. */ int32_t manufacturer; #define PCCARD_VENDOR_INVALID -1 int32_t product; #define PCCARD_PRODUCT_INVALID -1 u_int16_t error; #define PCCARD_CIS_INVALID { NULL, NULL, NULL, NULL } STAILQ_HEAD(, pccard_function) pf_head; }; #define PCCARD_MEM_ATTR 1 #define PCCARD_MEM_COMMON 2 #define PCCARD_WIDTH_AUTO 0 #define PCCARD_WIDTH_IO8 1 #define PCCARD_WIDTH_IO16 2 /* More later? */ struct pccard_ivar { struct resource_list resources; struct pccard_function *fcn; }; struct pccard_softc { device_t dev; /* this stuff is for the socket */ /* this stuff is for the card */ struct pccard_card card; int sc_enabled_count; /* num functions enabled */ - int intr_handler_count; }; void pccardbus_if_setup(struct pccard_softc*); struct pccard_cis_quirk { int32_t manufacturer; int32_t product; char *cis1_info[4]; struct pccard_function *pf; struct pccard_config_entry *cfe; }; struct pccard_tuple { unsigned int code; unsigned int length; u_long mult; bus_addr_t ptr; bus_space_tag_t memt; bus_space_handle_t memh; }; struct pccard_product { const char *pp_name; /* NULL if end of table */ #define PCCARD_VENDOR_ANY ((u_int32_t) -1) u_int32_t pp_vendor; #define PCCARD_PRODUCT_ANY ((u_int32_t) -1) u_int32_t pp_product; int pp_expfunc; const char *pp_cis[4]; }; typedef int (*pccard_product_match_fn) (device_t dev, const struct pccard_product *ent, int vpfmatch); const struct pccard_product *pccard_product_lookup(device_t dev, const struct pccard_product *tab, size_t ent_size, pccard_product_match_fn matchfn); void pccard_read_cis(struct pccard_softc *); void pccard_check_cis_quirks(device_t); void pccard_print_cis(device_t); int pccard_scan_cis(device_t, int (*) (struct pccard_tuple *, void *), void *); #define pccard_cis_read_1(tuple, idx0) \ (bus_space_read_1((tuple)->memt, (tuple)->memh, (tuple)->mult*(idx0))) #define pccard_tuple_read_1(tuple, idx1) \ (pccard_cis_read_1((tuple), ((tuple)->ptr+(2+(idx1))))) #define pccard_tuple_read_2(tuple, idx2) \ - (pccard_tuple_read_1((tuple), (idx2)) | \ + (pccard_tuple_read_1((tuple), (idx2)) | \ (pccard_tuple_read_1((tuple), (idx2)+1)<<8)) #define pccard_tuple_read_3(tuple, idx3) \ (pccard_tuple_read_1((tuple), (idx3)) | \ (pccard_tuple_read_1((tuple), (idx3)+1)<<8) | \ (pccard_tuple_read_1((tuple), (idx3)+2)<<16)) #define pccard_tuple_read_4(tuple, idx4) \ (pccard_tuple_read_1((tuple), (idx4)) | \ (pccard_tuple_read_1((tuple), (idx4)+1)<<8) | \ (pccard_tuple_read_1((tuple), (idx4)+2)<<16) | \ (pccard_tuple_read_1((tuple), (idx4)+3)<<24)) #define pccard_tuple_read_n(tuple, n, idxn) \ (((n)==1)?pccard_tuple_read_1((tuple), (idxn)) : \ (((n)==2)?pccard_tuple_read_2((tuple), (idxn)) : \ (((n)==3)?pccard_tuple_read_3((tuple), (idxn)) : \ /* n == 4 */ pccard_tuple_read_4((tuple), (idxn))))) #define PCCARD_SPACE_MEMORY 1 #define PCCARD_SPACE_IO 2 -int pccard_ccr_read(struct pccard_function *, int); -void pccard_ccr_write(struct pccard_function *, int, int); - #define pccard_mfc(sc) (STAILQ_FIRST(&(sc)->card.pf_head) && \ STAILQ_NEXT(STAILQ_FIRST(&(sc)->card.pf_head),pf_list)) -/* The following is the vestages of the NetBSD driver api */ - -void pccard_function_init(struct pccard_function *); -int pccard_function_enable(struct pccard_function *); -void pccard_function_disable(struct pccard_function *); - #define pccard_io_alloc(pf, start, size, align, pciop) \ (pccard_chip_io_alloc((pf)->sc->pct, pf->sc->pch, (start), \ (size), (align), (pciop))) #define pccard_io_free(pf, pciohp) \ (pccard_chip_io_free((pf)->sc->pct, (pf)->sc->pch, (pciohp))) int pccard_io_map(struct pccard_function *, int, bus_addr_t, bus_size_t, struct pccard_io_handle *, int *); void pccard_io_unmap(struct pccard_function *, int); #define pccard_mem_alloc(pf, size, pcmhp) \ (pccard_chip_mem_alloc((pf)->sc->pct, (pf)->sc->pch, (size), (pcmhp))) #define pccard_mem_free(pf, pcmhp) \ (pccard_chip_mem_free((pf)->sc->pct, (pf)->sc->pch, (pcmhp))) #define pccard_mem_map(pf, kind, card_addr, size, pcmhp, offsetp, windowp) \ (pccard_chip_mem_map((pf)->sc->pct, (pf)->sc->pch, (kind), \ (card_addr), (size), (pcmhp), (offsetp), (windowp))) #define pccard_mem_unmap(pf, window) \ (pccard_chip_mem_unmap((pf)->sc->pct, (pf)->sc->pch, (window))) /* compat layer */ static __inline int pccard_compat_probe(device_t dev) { return (CARD_COMPAT_DO_PROBE(device_get_parent(dev), dev)); } static __inline int pccard_compat_attach(device_t dev) { return (CARD_COMPAT_DO_ATTACH(device_get_parent(dev), dev)); } /* ivar interface */ enum { PCCARD_IVAR_ETHADDR, /* read ethernet address from CIS tupple */ PCCARD_IVAR_VENDOR, PCCARD_IVAR_PRODUCT, PCCARD_IVAR_FUNCTION_NUMBER, PCCARD_IVAR_VENDOR_STR, /* CIS string for "Manufacturer" */ PCCARD_IVAR_PRODUCT_STR,/* CIS strnig for "Product" */ PCCARD_IVAR_CIS3_STR, PCCARD_IVAR_CIS4_STR, PCCARD_IVAR_FUNCTION }; #define PCCARD_ACCESSOR(A, B, T) \ __inline static int \ pccard_get_ ## A(device_t dev, T *t) \ { \ - return BUS_READ_IVAR(device_get_parent(dev), dev, \ + return BUS_READ_IVAR(device_get_parent(dev), dev, \ PCCARD_IVAR_ ## B, (uintptr_t *) t); \ } PCCARD_ACCESSOR(ether, ETHADDR, u_int8_t) PCCARD_ACCESSOR(vendor, VENDOR, u_int32_t) PCCARD_ACCESSOR(product, PRODUCT, u_int32_t) PCCARD_ACCESSOR(function_number,FUNCTION_NUMBER, u_int32_t) PCCARD_ACCESSOR(function, FUNCTION, u_int32_t) PCCARD_ACCESSOR(vendor_str, VENDOR_STR, char *) PCCARD_ACCESSOR(product_str, PRODUCT_STR, char *) PCCARD_ACCESSOR(cis3_str, CIS3_STR, char *) /* shared memory flags */ enum { PCCARD_A_MEM_COM, /* common */ PCCARD_A_MEM_ATTR, /* attribute */ PCCARD_A_MEM_8BIT, /* 8 bit */ PCCARD_A_MEM_16BIT /* 16 bit */ }; #define PCCARD_SOFTC(d) (struct pccard_softc *) device_get_softc(d) #define PCCARD_IVAR(d) (struct pccard_ivar *) device_get_ivars(d) Index: head/sys/dev/pccbb/pccbb.c =================================================================== --- head/sys/dev/pccbb/pccbb.c (revision 82377) +++ head/sys/dev/pccbb/pccbb.c (revision 82378) @@ -1,1942 +1,2123 @@ /* * Copyright (c) 2000,2001 Jonathan Chen. * 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, * without modification, immediately at the beginning of the file. * 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$ */ /* * Driver for PCI to Cardbus Bridge chips * * References: * TI Datasheets: * http://www-s.ti.com/cgi-bin/sc/generic2.cgi?family=PCI+CARDBUS+CONTROLLERS * Much of the 16-bit PC Card compatibility code stolen from dev/pcic/i82365.c * XXX and should be cleaned up to share as much as possible. * * Written by Jonathan Chen * The author would like to acknowledge: * * HAYAKAWA Koichi: Author of the NetBSD code for the same thing * * Warner Losh: Newbus/newcard guru and author of the pccard side of things * * YAMAMOTO Shigeru: Author of another FreeBSD cardbus driver * * David Cross: Author of the initial ugly hack for a specific cardbus card */ #define CBB_DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "power_if.h" #include "card_if.h" #include "pcib_if.h" #if defined CBB_DEBUG #define DPRINTF(x) printf x #define DEVPRINTF(x) device_printf x #else #define DPRINTF(x) #define DEVPRINTF(x) #endif -#define PCI_MASK_CONFIG(DEV,REG,MASK,SIZE) \ +#define PCI_MASK_CONFIG(DEV,REG,MASK,SIZE) \ pci_write_config(DEV, REG, pci_read_config(DEV, REG, SIZE) MASK, SIZE) #define PCI_MASK2_CONFIG(DEV,REG,MASK1,MASK2,SIZE) \ pci_write_config(DEV, REG, ( \ pci_read_config(DEV, REG, SIZE) MASK1) MASK2, SIZE) /* * XXX all the pcic code really doesn't belong here and needs to be * XXX migrated to its own file, shared with the 16-bit code */ #define PCIC_READ(SC,REG) \ (((u_int8_t*)((SC)->sc_socketreg))[0x800+(REG)]) #define PCIC_WRITE(SC,REG,val) \ (((u_int8_t*)((SC)->sc_socketreg))[0x800+(REG)]) = (val) #define PCIC_MASK(SC,REG,MASK) \ PCIC_WRITE(SC,REG,PCIC_READ(SC,REG) MASK) #define PCIC_MASK2(SC,REG,MASK,MASK2) \ PCIC_WRITE(SC,REG,(PCIC_READ(SC,REG) MASK) MASK2) struct pccbb_sclist { struct pccbb_softc *sc; STAILQ_ENTRY(pccbb_sclist) entries; }; static STAILQ_HEAD(, pccbb_sclist) softcs; static int softcs_init = 0; struct yenta_chipinfo { u_int32_t yc_id; const char *yc_name; int yc_chiptype; int yc_flags; } yc_chipsets[] = { /* Texas Instruments chips */ {PCI_DEVICE_ID_PCIC_TI1130, "TI1130 PCI-CardBus Bridge", CB_TI113X, PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_PCIC_TI1131, "TI1131 PCI-CardBus Bridge", CB_TI113X, PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_PCIC_TI1211, "TI1211 PCI-CardBus Bridge", CB_TI12XX, PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_PCIC_TI1220, "TI1220 PCI-CardBus Bridge", CB_TI12XX, PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_PCIC_TI1221, "TI1221 PCI-CardBus Bridge", CB_TI12XX, PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_PCIC_TI1225, "TI1225 PCI-CardBus Bridge", CB_TI12XX, PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_PCIC_TI1250, "TI1250 PCI-CardBus Bridge", CB_TI12XX, PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_PCIC_TI1251, "TI1251 PCI-CardBus Bridge", CB_TI12XX, PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_PCIC_TI1251B,"TI1251B PCI-CardBus Bridge",CB_TI12XX, PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_PCIC_TI1410, "TI1410 PCI-CardBus Bridge", CB_TI12XX, PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_PCIC_TI1420, "TI1420 PCI-CardBus Bridge", CB_TI12XX, PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_PCIC_TI1450, "TI1450 PCI-CardBus Bridge", CB_TI12XX, PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_PCIC_TI1451, "TI1451 PCI-CardBus Bridge", CB_TI12XX, PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_PCIC_TI4451, "TI4451 PCI-CardBus Bridge", CB_TI12XX, PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, /* Ricoh chips */ {PCI_DEVICE_ID_RICOH_RL5C465, "RF5C465 PCI-CardBus Bridge", CB_RF5C46X, PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_RICOH_RL5C466, "RF5C466 PCI-CardBus Bridge", CB_RF5C46X, PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_RICOH_RL5C475, "RF5C475 PCI-CardBus Bridge", CB_RF5C47X, PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_RICOH_RL5C476, "RF5C476 PCI-CardBus Bridge", CB_RF5C47X, PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_RICOH_RL5C478, "RF5C478 PCI-CardBus Bridge", CB_RF5C47X, PCCBB_PCIC_MEM_32}, /* Toshiba products */ {PCI_DEVICE_ID_TOSHIBA_TOPIC95, "ToPIC95 PCI-CardBus Bridge", CB_TOPIC95, PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_TOSHIBA_TOPIC95B, "ToPIC95B PCI-CardBus Bridge", CB_TOPIC95B, PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_TOSHIBA_TOPIC97, "ToPIC97 PCI-CardBus Bridge", CB_TOPIC97, PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_TOSHIBA_TOPIC100, "ToPIC100 PCI-CardBus Bridge", CB_TOPIC97, PCCBB_PCIC_MEM_32}, /* Cirrus Logic */ {PCI_DEVICE_ID_PCIC_CLPD6832, "CLPD6832 PCI-CardBus Bridge", CB_CIRRUS, PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_PCIC_CLPD6833, "CLPD6833 PCI-CardBus Bridge", CB_CIRRUS, PCCBB_PCIC_MEM_32}, /* 02Micro */ {PCI_DEVICE_ID_PCIC_OZ6832, "O2Mirco OZ6832/6833 PCI-CardBus Bridge", CB_CIRRUS, PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_PCIC_OZ6860, "O2Mirco OZ6836/6860 PCI-CardBus Bridge", CB_CIRRUS, PCCBB_PCIC_MEM_32}, {PCI_DEVICE_ID_PCIC_OZ6872, "O2Mirco OZ6812/6872 PCI-CardBus Bridge", CB_CIRRUS, PCCBB_PCIC_MEM_32}, /* sentinel */ {0 /* null id */, "unknown", CB_UNKNOWN, 0}, }; static int cb_chipset(u_int32_t pci_id, const char **namep, int *flagp); static int pccbb_probe(device_t brdev); static void pccbb_chipinit(struct pccbb_softc *sc); static int pccbb_attach(device_t brdev); static int pccbb_detach(device_t brdev); +static int pccbb_shutdown(device_t brdev); static void pccbb_driver_added(device_t brdev, driver_t *driver); static void pccbb_child_detached(device_t brdev, device_t child); +static int pccbb_card_reprobe(device_t brdev, device_t busdev); static void pccbb_event_thread(void *arg); -static void pccbb_create_event_thread (struct pccbb_softc *sc); +static void pccbb_create_event_thread(struct pccbb_softc *sc); static void pccbb_start_threads(void *arg); static void pccbb_insert(struct pccbb_softc *sc); static void pccbb_removal(struct pccbb_softc *sc); static void pccbb_intr(void *arg); static int pccbb_detect_voltage(device_t brdev); static int pccbb_power(device_t brdev, int volts); static void pccbb_cardbus_reset(device_t brdev); static int pccbb_cardbus_power_enable_socket(device_t brdev, device_t child); static void pccbb_cardbus_power_disable_socket(device_t brdev, device_t child); static int pccbb_cardbus_io_open(device_t brdev, int win, u_int32_t start, u_int32_t end); static int pccbb_cardbus_mem_open(device_t brdev, int win, u_int32_t start, u_int32_t end); static void pccbb_cardbus_auto_open(struct pccbb_softc *sc, int type); static int pccbb_cardbus_activate_resource(device_t brdev, device_t child, - int type, int rid, struct resource *r); + int type, int rid, struct resource *res); static int pccbb_cardbus_deactivate_resource(device_t brdev, - device_t child, int type, int rid, struct resource *r); -static struct resource * pccbb_cardbus_alloc_resource(device_t brdev, + device_t child, int type, int rid, struct resource *res); +static struct resource *pccbb_cardbus_alloc_resource(device_t brdev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); static int pccbb_cardbus_release_resource(device_t brdev, device_t child, - int type, int rid, struct resource *r); + int type, int rid, struct resource *res); static int pccbb_pcic_power_enable_socket(device_t brdev, device_t child); static void pccbb_pcic_power_disable_socket(device_t brdev, device_t child); static void pccbb_pcic_wait_ready(struct pccbb_softc *sc); static void pccbb_pcic_do_mem_map(struct pccbb_softc *sc, int win); static int pccbb_pcic_mem_map(struct pccbb_softc *sc, int kind, - struct resource *r, bus_addr_t card_addr, int *win); + struct resource *res); static void pccbb_pcic_mem_unmap(struct pccbb_softc *sc, int window); +static int pccbb_pcic_mem_findmap(struct pccbb_softc *sc, + struct resource *res); static void pccbb_pcic_do_io_map(struct pccbb_softc *sc, int win); static int pccbb_pcic_io_map(struct pccbb_softc *sc, int width, - struct resource *r, bus_addr_t card_addr, int *win); + struct resource *r); static void pccbb_pcic_io_unmap(struct pccbb_softc *sc, int window); +static int pccbb_pcic_io_findmap(struct pccbb_softc *sc, + struct resource *res); static int pccbb_pcic_activate_resource(device_t brdev, device_t child, - int type, int rid, struct resource *r); + int type, int rid, struct resource *res); static int pccbb_pcic_deactivate_resource(device_t brdev, device_t child, - int type, int rid, struct resource *r); -static struct resource * pccbb_pcic_alloc_resource(device_t brdev, + int type, int rid, struct resource *res); +static struct resource *pccbb_pcic_alloc_resource(device_t brdev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); static int pccbb_pcic_release_resource(device_t brdev, device_t child, int type, int rid, struct resource *res); static int pccbb_pcic_set_res_flags(device_t brdev, device_t child, int type, int rid, u_int32_t flags); static int pccbb_pcic_set_memory_offset(device_t brdev, device_t child, int rid, u_int32_t cardaddr, u_int32_t *deltap); static int pccbb_power_enable_socket(device_t brdev, device_t child); static void pccbb_power_disable_socket(device_t brdev, device_t child); static int pccbb_activate_resource(device_t brdev, device_t child, int type, int rid, struct resource *r); static int pccbb_deactivate_resource(device_t brdev, device_t child, int type, int rid, struct resource *r); -static struct resource * pccbb_alloc_resource(device_t brdev, - device_t child, int type, int *rid, u_long start, - u_long end, u_long count, u_int flags); +static struct resource *pccbb_alloc_resource(device_t brdev, device_t child, + int type, int *rid, u_long start, u_long end, u_long count, + u_int flags); static int pccbb_release_resource(device_t brdev, device_t child, int type, int rid, struct resource *r); static int pccbb_read_ivar(device_t brdev, device_t child, int which, uintptr_t *result); static int pccbb_write_ivar(device_t brdev, device_t child, int which, uintptr_t value); static int pccbb_maxslots(device_t brdev); -static u_int32_t pccbb_read_config(device_t brdev, int b, int s, int f, +static u_int32_t pccbb_read_config(device_t brdev, int b, int s, int f, int reg, int width); static void pccbb_write_config(device_t brdev, int b, int s, int f, int reg, u_int32_t val, int width); /************************************************************************/ /* Probe/Attach */ /************************************************************************/ static int cb_chipset(u_int32_t pci_id, const char **namep, int *flagp) { int loopend = sizeof(yc_chipsets)/sizeof(yc_chipsets[0]); struct yenta_chipinfo *ycp, *ycend; ycend = yc_chipsets + loopend; for (ycp = yc_chipsets; ycp < ycend && pci_id != ycp->yc_id; ++ycp); if (ycp == ycend) { /* not found */ ycp = yc_chipsets + loopend - 1; /* to point the sentinel */ } if (namep != NULL) { *namep = ycp->yc_name; } if (flagp != NULL) { *flagp = ycp->yc_flags; } return ycp->yc_chiptype; } static int pccbb_probe(device_t brdev) { const char *name; if (cb_chipset(pci_get_devid(brdev), &name, NULL) == CB_UNKNOWN) return ENXIO; device_set_desc(brdev, name); return 0; } static void pccbb_chipinit(struct pccbb_softc *sc) { /* Set CardBus latency timer */ if (pci_read_config(sc->sc_dev, PCIR_SECLAT_1, 1) < 0x20) pci_write_config(sc->sc_dev, PCIR_SECLAT_1, 0x20, 1); /* Set PCI latency timer */ if (pci_read_config(sc->sc_dev, PCIR_LATTIMER, 1) < 0x20) pci_write_config(sc->sc_dev, PCIR_LATTIMER, 0x20, 1); /* Enable memory access */ PCI_MASK_CONFIG(sc->sc_dev, PCIR_COMMAND, | PCIM_CMD_MEMEN | PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN, 2); /* disable Legacy IO */ switch (sc->sc_chipset) { case CB_RF5C46X: case CB_RF5C47X: PCI_MASK_CONFIG(sc->sc_dev, PCCBBR_BRIDGECTRL, & ~(PCCBBM_BRIDGECTRL_RL_3E0_EN | PCCBBM_BRIDGECTRL_RL_3E2_EN), 2); break; default: pci_write_config(sc->sc_dev, PCCBBR_LEGACY, 0x0, 4); break; } /* Use PCI interrupt for interrupt routing */ PCI_MASK2_CONFIG(sc->sc_dev, PCCBBR_BRIDGECTRL, & ~(PCCBBM_BRIDGECTRL_MASTER_ABORT | PCCBBM_BRIDGECTRL_INTR_IREQ_EN), | PCCBBM_BRIDGECTRL_WRITE_POST_EN, 2); switch (sc->sc_chipset) { case CB_TI113X: PCI_MASK2_CONFIG(sc->sc_dev, PCCBBR_CBCTRL, & ~PCCBBM_CBCTRL_113X_PCI_INTR, | PCCBBM_CBCTRL_113X_PCI_CSC | PCCBBM_CBCTRL_113X_PCI_IRQ_EN, 1); PCI_MASK_CONFIG(sc->sc_dev, PCCBBR_DEVCTRL, & ~(PCCBBM_DEVCTRL_INT_SERIAL | PCCBBM_DEVCTRL_INT_PCI), 1); PCIC_WRITE(sc, PCIC_INTR, PCIC_INTR_ENABLE); PCIC_WRITE(sc, PCIC_CSC_INTR, 0); break; case CB_TI12XX: PCIC_WRITE(sc, PCIC_INTR, PCIC_INTR_ENABLE); PCIC_WRITE(sc, PCIC_CSC_INTR, 0); break; case CB_TOPIC95B: PCI_MASK_CONFIG(sc->sc_dev, PCCBBR_TOPIC_SOCKETCTRL, | PCCBBM_TOPIC_SOCKETCTRL_SCR_IRQSEL, 4); PCI_MASK2_CONFIG(sc->sc_dev, PCCBBR_TOPIC_SLOTCTRL, | PCCBBM_TOPIC_SLOTCTRL_SLOTON | PCCBBM_TOPIC_SLOTCTRL_SLOTEN | PCCBBM_TOPIC_SLOTCTRL_ID_LOCK | PCCBBM_TOPIC_SLOTCTRL_CARDBUS, & ~PCCBBM_TOPIC_SLOTCTRL_SWDETECT, 4); break; } /* close all memory and io windows */ pci_write_config(sc->sc_dev, PCCBBR_MEMBASE0, 0xffffffff, 4); pci_write_config(sc->sc_dev, PCCBBR_MEMLIMIT0, 0, 4); pci_write_config(sc->sc_dev, PCCBBR_MEMBASE1, 0xffffffff, 4); pci_write_config(sc->sc_dev, PCCBBR_MEMLIMIT1, 0, 4); pci_write_config(sc->sc_dev, PCCBBR_IOBASE0, 0xffffffff, 4); pci_write_config(sc->sc_dev, PCCBBR_IOLIMIT0, 0, 4); pci_write_config(sc->sc_dev, PCCBBR_IOBASE1, 0xffffffff, 4); pci_write_config(sc->sc_dev, PCCBBR_IOLIMIT1, 0, 4); } static int pccbb_attach(device_t brdev) { struct pccbb_softc *sc = (struct pccbb_softc *)device_get_softc(brdev); int rid; u_int32_t tmp; if (!softcs_init) { softcs_init = 1; STAILQ_INIT(&softcs); } mtx_init(&sc->sc_mtx, device_get_nameunit(brdev), MTX_DEF); sc->sc_chipset = cb_chipset(pci_get_devid(brdev), NULL, &sc->sc_flags); sc->sc_dev = brdev; sc->sc_cbdev = NULL; sc->sc_pccarddev = NULL; sc->sc_secbus = pci_read_config(brdev, PCIR_SECBUS_2, 1); sc->sc_subbus = pci_read_config(brdev, PCIR_SUBBUS_2, 1); sc->memalloc = 0; sc->ioalloc = 0; SLIST_INIT(&sc->rl); /* Ths PCI bus should have given me memory... right? */ rid=PCCBBR_SOCKBASE; sc->sc_base_res=bus_alloc_resource(brdev, SYS_RES_MEMORY, &rid, 0,~0,1, RF_ACTIVE); if (!sc->sc_base_res) { /* * XXX EVILE HACK BAD THING! XXX * The pci bus device should do this for us. * Some BIOSes doesn't assign a memory space properly. * So we try to manually put one in... */ u_int32_t sockbase; sockbase = pci_read_config(brdev, rid, 4); if (sockbase < 0x100000 || sockbase >= 0xfffffff0) { pci_write_config(brdev, rid, 0xffffffff, 4); sockbase = pci_read_config(brdev, rid, 4); sockbase = (sockbase & 0xfffffff0) & -(sockbase & 0xfffffff0); sc->sc_base_res = bus_generic_alloc_resource( device_get_parent(brdev), brdev, SYS_RES_MEMORY, - &rid, CARDBUS_SYS_RES_MEMORY_START, - CARDBUS_SYS_RES_MEMORY_END, sockbase, + &rid, 0, ~0, sockbase, RF_ACTIVE|rman_make_alignment_flags(sockbase)); if (!sc->sc_base_res){ device_printf(brdev, "Could not grab register memory\n"); mtx_destroy(&sc->sc_mtx); return ENOMEM; } pci_write_config(brdev, PCCBBR_SOCKBASE, rman_get_start(sc->sc_base_res), 4); DEVPRINTF((brdev, "PCI Memory allocated: %08lx\n", rman_get_start(sc->sc_base_res))); } else { device_printf(brdev, "Could not map register memory\n"); mtx_destroy(&sc->sc_mtx); return ENOMEM; } } sc->sc_socketreg = (struct pccbb_socketreg *)rman_get_virtual(sc->sc_base_res); pccbb_chipinit(sc); /* CSC Interrupt: Card detect interrupt on */ sc->sc_socketreg->socket_mask |= PCCBB_SOCKET_MASK_CD; /* reset interrupt */ tmp = sc->sc_socketreg->socket_event; sc->sc_socketreg->socket_event = tmp; /* Map and establish the interrupt. */ rid=0; sc->sc_irq_res=bus_alloc_resource(brdev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); if (sc->sc_irq_res == NULL) { printf("pccbb: Unable to map IRQ...\n"); bus_release_resource(brdev, SYS_RES_MEMORY, PCCBBR_SOCKBASE, sc->sc_base_res); mtx_destroy(&sc->sc_mtx); return ENOMEM; } if (bus_setup_intr(brdev, sc->sc_irq_res, INTR_TYPE_BIO, pccbb_intr, sc, &(sc->sc_intrhand))) { device_printf(brdev, "couldn't establish interrupt"); bus_release_resource(brdev, SYS_RES_IRQ, 0, sc->sc_irq_res); bus_release_resource(brdev, SYS_RES_MEMORY, PCCBBR_SOCKBASE, sc->sc_base_res); mtx_destroy(&sc->sc_mtx); return ENOMEM; } /* attach children */ sc->sc_cbdev = device_add_child(brdev, "cardbus", -1); if (sc->sc_cbdev == NULL) DEVPRINTF((brdev, "WARNING: cannot add cardbus bus.\n")); else if (device_probe_and_attach(sc->sc_cbdev) != 0) { DEVPRINTF((brdev, "WARNING: cannot attach cardbus bus!\n")); sc->sc_cbdev = NULL; } sc->sc_pccarddev = device_add_child(brdev, "pccard", -1); if (sc->sc_pccarddev == NULL) DEVPRINTF((brdev, "WARNING: cannot add pccard bus.\n")); else if (device_probe_and_attach(sc->sc_pccarddev) != 0) { DEVPRINTF((brdev, "WARNING: cannot attach pccard bus.\n")); sc->sc_pccarddev = NULL; } #ifndef KLD_MODULE if (sc->sc_cbdev == NULL && sc->sc_pccarddev == NULL) { device_printf(brdev, "ERROR: Failed to attach cardbus/pccard bus!\n"); bus_teardown_intr(brdev, sc->sc_irq_res, sc->sc_intrhand); bus_release_resource(brdev, SYS_RES_IRQ, 0, sc->sc_irq_res); bus_release_resource(brdev, SYS_RES_MEMORY, PCCBBR_SOCKBASE, sc->sc_base_res); mtx_destroy(&sc->sc_mtx); return ENOMEM; } #endif { struct pccbb_sclist *sclist; sclist = malloc(sizeof(struct pccbb_sclist), M_DEVBUF, M_WAITOK); sclist->sc = sc; STAILQ_INSERT_TAIL(&softcs, sclist, entries); } return 0; } static int pccbb_detach(device_t brdev) { struct pccbb_softc *sc = device_get_softc(brdev); int numdevs; device_t *devlist; int tmp; int error; device_get_children(brdev, &devlist, &numdevs); error = 0; for (tmp = 0; tmp < numdevs; tmp++) { if (device_detach(devlist[tmp]) == 0) device_delete_child(brdev, devlist[tmp]); else error++; } free(devlist, M_TEMP); if (error > 0) return ENXIO; mtx_lock(&sc->sc_mtx); bus_teardown_intr(brdev, sc->sc_irq_res, sc->sc_intrhand); sc->sc_flags |= PCCBB_KTHREAD_DONE; if (sc->sc_flags & PCCBB_KTHREAD_RUNNING) { wakeup(sc); mtx_unlock(&sc->sc_mtx); DEVPRINTF((brdev, "waiting for kthread exit...")); error = tsleep(sc, PWAIT, "pccbb-detach-wait", 60 * hz); if (error) DPRINTF(("timeout\n")); else DPRINTF(("done\n")); } else mtx_unlock(&sc->sc_mtx); bus_release_resource(brdev, SYS_RES_IRQ, 0, sc->sc_irq_res); bus_release_resource(brdev, SYS_RES_MEMORY, PCCBBR_SOCKBASE, sc->sc_base_res); mtx_destroy(&sc->sc_mtx); return 0; } +static int +pccbb_shutdown(device_t brdev) +{ + struct pccbb_softc *sc = (struct pccbb_softc *)device_get_softc(brdev); + /* properly reset everything at shutdown */ + + PCI_MASK_CONFIG(brdev, PCCBBR_BRIDGECTRL, |PCCBBM_BRIDGECTRL_RESET, 2); + PCIC_MASK(sc, PCIC_INTR, & ~PCIC_INTR_RESET); + + sc->sc_socketreg->socket_mask = 0; + + pccbb_power(brdev, CARD_VCC_0V | CARD_VPP_0V); + + PCIC_WRITE(sc, PCIC_ADDRWIN_ENABLE, 0); + pci_write_config(brdev, PCCBBR_MEMBASE0, 0, 4); + pci_write_config(brdev, PCCBBR_MEMLIMIT0, 0, 4); + pci_write_config(brdev, PCCBBR_MEMBASE1, 0, 4); + pci_write_config(brdev, PCCBBR_MEMLIMIT1, 0, 4); + pci_write_config(brdev, PCCBBR_IOBASE0, 0, 4); + pci_write_config(brdev, PCCBBR_IOLIMIT0, 0, 4); + pci_write_config(brdev, PCCBBR_IOBASE1, 0, 4); + pci_write_config(brdev, PCCBBR_IOLIMIT1, 0, 4); + pci_write_config(brdev, PCIR_COMMAND, 0, 2); + return 0; +} + static void pccbb_driver_added(device_t brdev, driver_t *driver) { struct pccbb_softc *sc = device_get_softc(brdev); device_t *devlist; int tmp; int numdevs; + int wake; + u_int32_t sockstate; DEVICE_IDENTIFY(driver, brdev); device_get_children(brdev, &devlist, &numdevs); + wake = 0; + sockstate = sc->sc_socketreg->socket_state; for (tmp = 0; tmp < numdevs; tmp++) { if (device_get_state(devlist[tmp]) == DS_NOTPRESENT && device_probe_and_attach(devlist[tmp]) == 0) { if (devlist[tmp] == NULL) /* NOTHING */; else if (strcmp(driver->name, "cardbus") == 0) { sc->sc_cbdev = devlist[tmp]; - if ((sc->sc_socketreg->socket_state - & PCCBB_SOCKET_STAT_CD) == 0) { - mtx_lock(&sc->sc_mtx); - wakeup(sc); - mtx_unlock(&sc->sc_mtx); - } + if (((sockstate & PCCBB_SOCKET_STAT_CD) == 0) && + (sockstate & PCCBB_SOCKET_STAT_CB)) + wake++; } else if (strcmp(driver->name, "pccard") == 0) { sc->sc_pccarddev = devlist[tmp]; - if ((sc->sc_socketreg->socket_state - & PCCBB_SOCKET_STAT_CD) == 0) { - mtx_lock(&sc->sc_mtx); - wakeup(sc); - mtx_unlock(&sc->sc_mtx); - } + if (((sockstate & PCCBB_SOCKET_STAT_CD) == 0) && + (sockstate & PCCBB_SOCKET_STAT_16BIT)) + wake++; } else device_printf(brdev, "Unsupported child bus: %s\n", driver->name); } } free(devlist, M_TEMP); + + if (wake > 0) { + if ((sc->sc_socketreg->socket_state & PCCBB_SOCKET_STAT_CD) == + 0) { + mtx_lock(&sc->sc_mtx); + wakeup(sc); + mtx_unlock(&sc->sc_mtx); + } + } } static void pccbb_child_detached(device_t brdev, device_t child) { struct pccbb_softc *sc = device_get_softc(brdev); if (child == sc->sc_cbdev) sc->sc_cbdev = NULL; else if (child == sc->sc_pccarddev) sc->sc_pccarddev = NULL; else device_printf(brdev, "Unknown child detached: %s %p/%p\n", device_get_nameunit(child), sc->sc_cbdev, sc->sc_pccarddev); } +static int +pccbb_card_reprobe(device_t brdev, device_t busdev) { + struct pccbb_softc *sc = device_get_softc(brdev); + int wake = 0; + u_int32_t sockstate; + + sockstate = sc->sc_socketreg->socket_state; + + if ((sockstate & PCCBB_SOCKET_STAT_CD) == 0) { + if (busdev == sc->sc_cbdev && + (sockstate & PCCBB_SOCKET_STAT_CB)) + wake++; + else if (busdev == sc->sc_pccarddev && + (sockstate & PCCBB_SOCKET_STAT_16BIT)) + wake++; + + if (wake > 0) { + mtx_lock(&sc->sc_mtx); + wakeup(sc); + mtx_unlock(&sc->sc_mtx); + return 0; + } + return EBUSY; + } + return ENOENT; +} + /************************************************************************/ /* Kthreads */ /************************************************************************/ static void pccbb_event_thread(void *arg) { struct pccbb_softc *sc = arg; u_int32_t status; mtx_lock(&Giant); for(;;) { if (!(sc->sc_flags & PCCBB_KTHREAD_RUNNING)) sc->sc_flags |= PCCBB_KTHREAD_RUNNING; else { tsleep (sc, PWAIT, "pccbbev", 0); /* * Delay 1 second, make sure the user is done with * whatever he is doing. We tsleep on sc->sc_flags, * which should never be woken up. */ tsleep (&sc->sc_flags, PWAIT, "pccbbev", 1*hz); } mtx_lock(&sc->sc_mtx); if (sc->sc_flags & PCCBB_KTHREAD_DONE) break; status = sc->sc_socketreg->socket_state; if ((status & PCCBB_SOCKET_STAT_CD) == 0) { pccbb_insert(sc); } else { pccbb_removal(sc); } mtx_unlock(&sc->sc_mtx); } mtx_unlock(&sc->sc_mtx); sc->sc_flags &= ~PCCBB_KTHREAD_RUNNING; wakeup(sc); kthread_exit(0); } static void -pccbb_create_event_thread (struct pccbb_softc *sc) +pccbb_create_event_thread(struct pccbb_softc *sc) { if (kthread_create(pccbb_event_thread, sc, &sc->event_thread, 0, "%s%d", device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev))) { device_printf (sc->sc_dev, "unable to create event thread.\n"); panic ("pccbb_create_event_thread"); } } static void pccbb_start_threads(void *arg) { struct pccbb_sclist *sclist; STAILQ_FOREACH(sclist, &softcs, entries) { pccbb_create_event_thread(sclist->sc); } } /************************************************************************/ /* Insert/removal */ /************************************************************************/ static void pccbb_insert(struct pccbb_softc *sc) { u_int32_t sockevent, sockstate; int timeout = 30; do { sockevent = sc->sc_socketreg->socket_event; sockstate = sc->sc_socketreg->socket_state; } while (sockstate & PCCBB_SOCKET_STAT_CD && --timeout > 0); if (timeout < 0) { device_printf (sc->sc_dev, "insert timeout"); return; } DEVPRINTF((sc->sc_dev, "card inserted: event=0x%08x, state=%08x\n", sockevent, sockstate)); if (sockstate & PCCBB_SOCKET_STAT_16BIT && sc->sc_pccarddev != NULL) { sc->sc_flags |= PCCBB_16BIT_CARD; if (CARD_ATTACH_CARD(sc->sc_pccarddev) != 0) device_printf(sc->sc_dev, "card activation failed\n"); } else if (sockstate & PCCBB_SOCKET_STAT_CB && sc->sc_cbdev != NULL) { sc->sc_flags &= ~PCCBB_16BIT_CARD; if (CARD_ATTACH_CARD(sc->sc_cbdev) != 0) device_printf(sc->sc_dev, "card activation failed\n"); } else { device_printf (sc->sc_dev, "Unsupported card type detected\n"); } } static void pccbb_removal(struct pccbb_softc *sc) { - u_int32_t sockstate; struct pccbb_reslist *rle; - sockstate = sc->sc_socketreg->socket_state; - - if (sockstate & PCCBB_16BIT_CARD && sc->sc_pccarddev != NULL) + if (sc->sc_flags & PCCBB_16BIT_CARD && sc->sc_pccarddev != NULL) CARD_DETACH_CARD(sc->sc_pccarddev, DETACH_FORCE); - else if ((!(sockstate & PCCBB_16BIT_CARD)) && sc->sc_cbdev != NULL) + else if ((!(sc->sc_flags & PCCBB_16BIT_CARD)) && sc->sc_cbdev != NULL) CARD_DETACH_CARD(sc->sc_cbdev, DETACH_FORCE); - while (NULL != (rle = SLIST_FIRST(&sc->rl))) { + while ((rle = SLIST_FIRST(&sc->rl)) != NULL) { device_printf(sc->sc_dev, "Danger Will Robinson: Resource " "left allocated! This is a bug... " - "(rid=%x, type=%d, addr=%x)\n", rle->rid, rle->type, - rle->start); - SLIST_REMOVE_HEAD(&sc->rl, entries); + "(rid=%x, type=%d, addr=%lx)\n", rle->rid, rle->type, + rman_get_start(rle->res)); + SLIST_REMOVE_HEAD(&sc->rl, link); + free(rle, M_DEVBUF); } } /************************************************************************/ /* Interrupt Handler */ /************************************************************************/ static void pccbb_intr(void *arg) { struct pccbb_softc *sc = arg; u_int32_t sockevent; if (!(sockevent = sc->sc_socketreg->socket_event)) { /* not for me. */ return; } /* reset bit */ sc->sc_socketreg->socket_event = sockevent | 0x01; if (sockevent & PCCBB_SOCKET_EVENT_CD) { mtx_lock(&sc->sc_mtx); wakeup(sc); mtx_unlock(&sc->sc_mtx); } else { if (sockevent & PCCBB_SOCKET_EVENT_CSTS) { DPRINTF((" cstsevent occures, 0x%08x\n", sc->sc_socketreg->socket_state)); } if (sockevent & PCCBB_SOCKET_EVENT_POWER) { DPRINTF((" pwrevent occures, 0x%08x\n", sc->sc_socketreg->socket_state)); } } return; } /************************************************************************/ /* Generic Power functions */ /************************************************************************/ static int pccbb_detect_voltage(device_t brdev) { struct pccbb_softc *sc = device_get_softc(brdev); u_int32_t psr; int vol = CARD_UKN_CARD; psr = sc->sc_socketreg->socket_state; if (psr & PCCBB_SOCKET_STAT_5VCARD) { vol |= CARD_5V_CARD; } if (psr & PCCBB_SOCKET_STAT_3VCARD) { vol |= CARD_3V_CARD; } if (psr & PCCBB_SOCKET_STAT_XVCARD) { vol |= CARD_XV_CARD; } if (psr & PCCBB_SOCKET_STAT_YVCARD) { vol |= CARD_YV_CARD; } return vol; } static int pccbb_power(device_t brdev, int volts) { u_int32_t status, sock_ctrl; struct pccbb_softc *sc = device_get_softc(brdev); DEVPRINTF((sc->sc_dev, "pccbb_power: %s and %s [%x]\n", (volts & CARD_VCCMASK) == CARD_VCC_UC ? "CARD_VCC_UC" : (volts & CARD_VCCMASK) == CARD_VCC_5V ? "CARD_VCC_5V" : (volts & CARD_VCCMASK) == CARD_VCC_3V ? "CARD_VCC_3V" : (volts & CARD_VCCMASK) == CARD_VCC_XV ? "CARD_VCC_XV" : (volts & CARD_VCCMASK) == CARD_VCC_YV ? "CARD_VCC_YV" : (volts & CARD_VCCMASK) == CARD_VCC_0V ? "CARD_VCC_0V" : "VCC-UNKNOWN", (volts & CARD_VPPMASK) == CARD_VPP_UC ? "CARD_VPP_UC" : (volts & CARD_VPPMASK) == CARD_VPP_12V ? "CARD_VPP_12V" : (volts & CARD_VPPMASK) == CARD_VPP_VCC ? "CARD_VPP_VCC" : (volts & CARD_VPPMASK) == CARD_VPP_0V ? "CARD_VPP_0V" : "VPP-UNKNOWN", volts)); status = sc->sc_socketreg->socket_state; sock_ctrl = sc->sc_socketreg->socket_control; switch (volts & CARD_VCCMASK) { case CARD_VCC_UC: break; case CARD_VCC_5V: if (PCCBB_SOCKET_STAT_5VCARD & status) { /* check 5 V card */ sock_ctrl &= ~PCCBB_SOCKET_CTRL_VCCMASK; sock_ctrl |= PCCBB_SOCKET_CTRL_VCC_5V; } else { device_printf(sc->sc_dev, "BAD voltage request: no 5 V card\n"); } break; case CARD_VCC_3V: if (PCCBB_SOCKET_STAT_3VCARD & status) { sock_ctrl &= ~PCCBB_SOCKET_CTRL_VCCMASK; sock_ctrl |= PCCBB_SOCKET_CTRL_VCC_3V; } else { device_printf(sc->sc_dev, "BAD voltage request: no 3.3 V card\n"); } break; case CARD_VCC_0V: sock_ctrl &= ~PCCBB_SOCKET_CTRL_VCCMASK; break; default: return 0; /* power NEVER changed */ break; } switch (volts & CARD_VPPMASK) { case CARD_VPP_UC: break; case CARD_VPP_0V: sock_ctrl &= ~PCCBB_SOCKET_CTRL_VPPMASK; break; case CARD_VPP_VCC: sock_ctrl &= ~PCCBB_SOCKET_CTRL_VPPMASK; sock_ctrl |= ((sock_ctrl >> 4) & 0x07); break; case CARD_VPP_12V: sock_ctrl &= ~PCCBB_SOCKET_CTRL_VPPMASK; sock_ctrl |= PCCBB_SOCKET_CTRL_VPP_12V; break; } if (sc->sc_socketreg->socket_control == sock_ctrl) return 1; /* no change necessary */ sc->sc_socketreg->socket_control = sock_ctrl; status = sc->sc_socketreg->socket_state; { int timeout = 20; u_int32_t sockevent; do { DELAY(20*1000); sockevent = sc->sc_socketreg->socket_event; } while (!(sockevent & PCCBB_SOCKET_EVENT_POWER) && --timeout > 0); /* reset event status */ sc->sc_socketreg->socket_event = sockevent; if (timeout < 0) { printf ("VCC supply failed.\n"); return 0; } } /* XXX * delay 400 ms: thgough the standard defines that the Vcc set-up time * is 20 ms, some PC-Card bridge requires longer duration. */ DELAY(400*1000); if (status & PCCBB_SOCKET_STAT_BADVCC) { device_printf(sc->sc_dev, "bad Vcc request. ctrl=0x%x, status=0x%x\n", sock_ctrl ,status); printf("pccbb_power: %s and %s [%x]\n", (volts & CARD_VCCMASK) == CARD_VCC_UC ? "CARD_VCC_UC" : (volts & CARD_VCCMASK) == CARD_VCC_5V ? "CARD_VCC_5V" : (volts & CARD_VCCMASK) == CARD_VCC_3V ? "CARD_VCC_3V" : (volts & CARD_VCCMASK) == CARD_VCC_XV ? "CARD_VCC_XV" : (volts & CARD_VCCMASK) == CARD_VCC_YV ? "CARD_VCC_YV" : (volts & CARD_VCCMASK) == CARD_VCC_0V ? "CARD_VCC_0V" : "VCC-UNKNOWN", (volts & CARD_VPPMASK) == CARD_VPP_UC ? "CARD_VPP_UC" : (volts & CARD_VPPMASK) == CARD_VPP_12V ? "CARD_VPP_12V": (volts & CARD_VPPMASK) == CARD_VPP_VCC ? "CARD_VPP_VCC": (volts & CARD_VPPMASK) == CARD_VPP_0V ? "CARD_VPP_0V" : "VPP-UNKNOWN", volts); return 0; } return 1; /* power changed correctly */ } /************************************************************************/ /* Cardbus power functions */ /************************************************************************/ static void pccbb_cardbus_reset(device_t brdev) { struct pccbb_softc *sc = device_get_softc(brdev); int delay_us; delay_us = sc->sc_chipset == CB_RF5C47X ? 400*1000 : 20*1000; PCI_MASK_CONFIG(brdev, PCCBBR_BRIDGECTRL, |PCCBBM_BRIDGECTRL_RESET, 2); DELAY(delay_us); /* If a card exists, unreset it! */ if ((sc->sc_socketreg->socket_state & PCCBB_SOCKET_STAT_CD) == 0) { PCI_MASK_CONFIG(brdev, PCCBBR_BRIDGECTRL, &~PCCBBM_BRIDGECTRL_RESET, 2); DELAY(delay_us); } } static int pccbb_cardbus_power_enable_socket(device_t brdev, device_t child) { struct pccbb_softc *sc = device_get_softc(brdev); int voltage; if ((sc->sc_socketreg->socket_state & PCCBB_SOCKET_STAT_CD) == PCCBB_SOCKET_STAT_CD) return ENODEV; voltage = pccbb_detect_voltage(brdev); pccbb_power(brdev, CARD_VCC_0V | CARD_VPP_0V); if (voltage & CARD_5V_CARD) pccbb_power(brdev, CARD_VCC_5V | CARD_VPP_VCC); else if (voltage & CARD_3V_CARD) pccbb_power(brdev, CARD_VCC_3V | CARD_VPP_VCC); else { device_printf(brdev, "Unknown card voltage\n"); return ENXIO; } pccbb_cardbus_reset(brdev); return 0; } static void pccbb_cardbus_power_disable_socket(device_t brdev, device_t child) { pccbb_power(brdev, CARD_VCC_0V | CARD_VPP_0V); pccbb_cardbus_reset(brdev); } /************************************************************************/ /* Cardbus Resource */ /************************************************************************/ static int pccbb_cardbus_io_open(device_t brdev, int win, u_int32_t start, u_int32_t end) { int basereg; int limitreg; if ((win < 0) || (win > 1)) { DEVPRINTF((brdev, "pccbb_cardbus_io_open: window out of range %d\n", win)); return EINVAL; } basereg = win*8 + PCCBBR_IOBASE0; limitreg = win*8 + PCCBBR_IOLIMIT0; pci_write_config(brdev, basereg, start, 4); pci_write_config(brdev, limitreg, end, 4); return 0; } static int pccbb_cardbus_mem_open(device_t brdev, int win, u_int32_t start, u_int32_t end) { int basereg; int limitreg; if ((win < 0) || (win > 1)) { DEVPRINTF((brdev, "pccbb_cardbus_mem_open: window out of range %d\n", win)); return EINVAL; } basereg = win*8 + PCCBBR_MEMBASE0; limitreg = win*8 + PCCBBR_MEMLIMIT0; pci_write_config(brdev, basereg, start, 4); pci_write_config(brdev, limitreg, end, 4); return 0; } static void pccbb_cardbus_auto_open(struct pccbb_softc *sc, int type) { u_int32_t starts[2]; u_int32_t ends[2]; struct pccbb_reslist *rle; int align; + int prefetchable[2]; starts[0] = starts[1] = 0xffffffff; ends[0] = ends[1] = 0; - SLIST_FOREACH(rle, &sc->rl, entries) { + if (type == SYS_RES_MEMORY) + align = PCCBB_MEMALIGN; + else if (type == SYS_RES_IOPORT) + align = PCCBB_IOALIGN; + else + align = 1; + + SLIST_FOREACH(rle, &sc->rl, link) { if (rle->type != type) ; - else if (starts[0] == 0xffffffff) { - starts[0] = rle->start; - ends[0] = rle->end; - rle->win = 0; - } else if (rle->end > ends[0] && - rle->start - ends[0] < PCCBB_AUTO_OPEN_SMALLHOLE) { - ends[0] = rle->end; - rle->win = 0; - } else if (rle->start < starts[0] && - starts[0] - rle->end < PCCBB_AUTO_OPEN_SMALLHOLE) { - starts[0] = rle->start; - rle->win = 0; + else if (rle->res == NULL) { + device_printf(sc->sc_dev, "WARNING: Resource not reserved? " + "(type=%d, addr=%lx)\n", + rle->type, rman_get_start(rle->res)); + } else if (!(rman_get_flags(rle->res) & RF_ACTIVE)) { + } else if (starts[0] == 0xffffffff) { + starts[0] = rman_get_start(rle->res); + ends[0] = rman_get_end(rle->res); + prefetchable[0] = + rman_get_flags(rle->res) & RF_PREFETCHABLE; + } else if (rman_get_end(rle->res) > ends[0] && + rman_get_start(rle->res) - ends[0] < + PCCBB_AUTO_OPEN_SMALLHOLE && prefetchable[0] == + (rman_get_flags(rle->res) & RF_PREFETCHABLE)) { + ends[0] = rman_get_end(rle->res); + } else if (rman_get_start(rle->res) < starts[0] && + starts[0] - rman_get_end(rle->res) < + PCCBB_AUTO_OPEN_SMALLHOLE && prefetchable[0] == + (rman_get_flags(rle->res) & RF_PREFETCHABLE)) { + starts[0] = rman_get_start(rle->res); } else if (starts[1] == 0xffffffff) { - starts[1] = rle->start; - ends[1] = rle->end; - rle->win = 1; - } else if (rle->end > ends[1] && - rle->start - ends[1] < PCCBB_AUTO_OPEN_SMALLHOLE) { - ends[1] = rle->end; - rle->win = 1; - } else if (rle->start < starts[1] && - starts[1] - rle->end < PCCBB_AUTO_OPEN_SMALLHOLE) { - starts[1] = rle->start; - rle->win = 1; + starts[1] = rman_get_start(rle->res); + ends[1] = rman_get_end(rle->res); + prefetchable[1] = + rman_get_flags(rle->res) & RF_PREFETCHABLE; + } else if (rman_get_end(rle->res) > ends[1] && + rman_get_start(rle->res) - ends[1] < + PCCBB_AUTO_OPEN_SMALLHOLE && prefetchable[1] == + (rman_get_flags(rle->res) & RF_PREFETCHABLE)) { + ends[1] = rman_get_end(rle->res); + } else if (rman_get_start(rle->res) < starts[1] && + starts[1] - rman_get_end(rle->res) < + PCCBB_AUTO_OPEN_SMALLHOLE && prefetchable[1] == + (rman_get_flags(rle->res) & RF_PREFETCHABLE)) { + starts[1] = rman_get_start(rle->res); } else { u_int32_t diffs[2]; + int win; diffs[0] = diffs[1] = 0xffffffff; - if (rle->start > ends[0]) - diffs[0] = rle->start - ends[0]; - else if (rle->end < starts[0]) - diffs[0] = starts[0] - rle->end; - if (rle->start > ends[1]) - diffs[1] = rle->start - ends[1]; - else if (rle->end < starts[1]) - diffs[1] = starts[1] - rle->end; + if (rman_get_start(rle->res) > ends[0]) + diffs[0] = rman_get_start(rle->res) - ends[0]; + else if (rman_get_end(rle->res) < starts[0]) + diffs[0] = starts[0] - rman_get_end(rle->res); + if (rman_get_start(rle->res) > ends[1]) + diffs[1] = rman_get_start(rle->res) - ends[1]; + else if (rman_get_end(rle->res) < starts[1]) + diffs[1] = starts[1] - rman_get_end(rle->res); - rle->win = (diffs[0] <= diffs[1])?0:1; - if (rle->start > ends[rle->win]) - ends[rle->win] = rle->end; - else if (rle->end < starts[rle->win]) - starts[rle->win] = rle->start; + win = (diffs[0] <= diffs[1])?0:1; + if (rman_get_start(rle->res) > ends[win]) + ends[win] = rman_get_end(rle->res); + else if (rman_get_end(rle->res) < starts[win]) + starts[win] = rman_get_start(rle->res); + if (!(rman_get_flags(rle->res) & RF_PREFETCHABLE)) + prefetchable[win] = 0; } + + if (starts[0] != 0xffffffff) + starts[0] -= starts[0] % align; + if (starts[1] != 0xffffffff) + starts[1] -= starts[1] % align; + if (ends[0] % align != 0) + ends[0] += align - ends[0]%align - 1; + if (ends[1] % align != 0) + ends[1] += align - ends[1]%align - 1; } - if (type == SYS_RES_MEMORY) - align = PCCBB_MEMALIGN; - else if (type == SYS_RES_IOPORT) - align = PCCBB_IOALIGN; - else - align = 1; - if (starts[0] != 0xffffffff) - starts[0] -= starts[0] % align; - if (starts[1] != 0xffffffff) - starts[1] -= starts[1] % align; - if (ends[0] % align != 0) - ends[0] += align - ends[0]%align - 1; - if (ends[1] % align != 0) - ends[1] += align - ends[1]%align - 1; - if (type == SYS_RES_MEMORY) { + u_int32_t reg; + pccbb_cardbus_mem_open(sc->sc_dev, 0, starts[0], ends[0]); pccbb_cardbus_mem_open(sc->sc_dev, 1, starts[1], ends[1]); + reg = pci_read_config(sc->sc_dev, PCCBBR_BRIDGECTRL, 2); + reg &= ~(PCCBBM_BRIDGECTRL_PREFETCH_0| + PCCBBM_BRIDGECTRL_PREFETCH_1); + reg |= (prefetchable[0]?PCCBBM_BRIDGECTRL_PREFETCH_0:0)| + (prefetchable[1]?PCCBBM_BRIDGECTRL_PREFETCH_1:0); + pci_write_config(sc->sc_dev, PCCBBR_BRIDGECTRL, reg, 2); } else if (type == SYS_RES_IOPORT) { pccbb_cardbus_io_open(sc->sc_dev, 0, starts[0], ends[0]); pccbb_cardbus_io_open(sc->sc_dev, 1, starts[1], ends[1]); } } static int pccbb_cardbus_activate_resource(device_t brdev, device_t child, int type, - int rid, struct resource *r) + int rid, struct resource *res) { - struct pccbb_softc *sc = device_get_softc(brdev); - struct pccbb_reslist *rle; + int ret; - if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { - SLIST_FOREACH(rle, &sc->rl, entries) { - if (type == rle->type && rid == rle->rid && - child == rle->odev) - return bus_generic_activate_resource( - brdev, child, type, rid, r); - } - rle = malloc(sizeof(struct pccbb_reslist), M_DEVBUF, M_WAITOK); - rle->type = type; - rle->rid = rid; - rle->start = rman_get_start(r); - rle->end = rman_get_end(r); - rle->odev = child; - rle->win = -1; - SLIST_INSERT_HEAD(&sc->rl, rle, entries); - - pccbb_cardbus_auto_open(sc, type); - } - return bus_generic_activate_resource(brdev, child, type, rid, r); + ret = BUS_ACTIVATE_RESOURCE(device_get_parent(brdev), child, + type, rid, res); + if (ret != 0) return ret; + pccbb_cardbus_auto_open(device_get_softc(brdev), type); + return 0; } static int pccbb_cardbus_deactivate_resource(device_t brdev, device_t child, int type, - int rid, struct resource *r) + int rid, struct resource *res) { - struct pccbb_softc *sc = device_get_softc(brdev); - struct pccbb_reslist *rle; + int ret; - SLIST_FOREACH(rle, &sc->rl, entries) { - if (type == rle->type && rid == rle->rid && - child == rle->odev) { - SLIST_REMOVE(&sc->rl, rle, pccbb_reslist, entries); - if (type == SYS_RES_IOPORT || - type == SYS_RES_MEMORY) - pccbb_cardbus_auto_open(sc, type); - free(rle, M_DEVBUF); - break; - } - } - return bus_generic_deactivate_resource(brdev, child, type, rid, r); + ret = BUS_DEACTIVATE_RESOURCE(device_get_parent(brdev), child, + type, rid, res); + if (ret != 0) return ret; + pccbb_cardbus_auto_open(device_get_softc(brdev), type); + return 0; } static struct resource * pccbb_cardbus_alloc_resource(device_t brdev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { - if (type == SYS_RES_IRQ) { - struct pccbb_softc *sc = device_get_softc(brdev); - if (start == 0) { - start = end = rman_get_start(sc->sc_irq_res); + struct pccbb_softc *sc = device_get_softc(brdev); + struct pccbb_reslist *rle; + int tmp; + struct resource *res; + + switch (type) { + case SYS_RES_IRQ: + tmp = rman_get_start(sc->sc_irq_res); + if (start > tmp || end < tmp || count != 1) { + device_printf(child, "requested interrupt %ld-%ld," + "count = %ld not supported by pccbb\n", + start, end, count); + return NULL; } - return bus_generic_alloc_resource(brdev, child, type, rid, - start, end, count, flags); - } else { - if (type == SYS_RES_MEMORY && start == 0 && end == ~0) { - start = CARDBUS_SYS_RES_MEMORY_START; - end = CARDBUS_SYS_RES_MEMORY_END; - } else if (type == SYS_RES_IOPORT && start == 0 && end == ~0) { - start = CARDBUS_SYS_RES_IOPORT_START; - end = CARDBUS_SYS_RES_IOPORT_END; - } - return bus_generic_alloc_resource(brdev, child, type, rid, - start, end, count, flags); + start = end = tmp; + break; + case SYS_RES_IOPORT: + if (start <= 0x1000) + start = 0x1000; + if (end < start) + end = start; + break; + case SYS_RES_MEMORY: + if (start <= 0x44000000) + start = 0x44000000; + if (end < start) + end = start; + break; } + + res = BUS_ALLOC_RESOURCE(device_get_parent(brdev), child, type, rid, + start, end, count, flags & ~RF_ACTIVE); + if (res == NULL) { + printf("pccbb alloc res fail\n"); + return NULL; + } + + /* + * Need to record allocated resource so we can iterate through + * it later. + */ + rle = malloc(sizeof(struct pccbb_reslist), M_DEVBUF, M_NOWAIT); + if (!res) + panic("pccbb_cardbus_alloc_resource: can't record entry!"); + rle->res = res; + rle->type = type; + rle->rid = *rid; + rle->cardaddr = 0; + SLIST_INSERT_HEAD(&sc->rl, rle, link); + + if (flags & RF_ACTIVE) + if (bus_activate_resource(child, type, *rid, res) != 0) { + bus_release_resource(child, type, *rid, res); + return NULL; + } + + return res; } static int pccbb_cardbus_release_resource(device_t brdev, device_t child, int type, - int rid, struct resource *r) + int rid, struct resource *res) { - return bus_generic_release_resource(brdev, child, type, rid, r); + struct pccbb_softc *sc = device_get_softc(brdev); + struct pccbb_reslist *rle; + + if (rman_get_flags(res) & RF_ACTIVE) { + int error; + error = bus_deactivate_resource(child, type, rid, res); + if (error != 0) + return error; + } + + SLIST_FOREACH(rle, &sc->rl, link) { + if (rle->res == res) { + SLIST_REMOVE(&sc->rl, rle, pccbb_reslist, link); + free(rle, M_DEVBUF); + break; + } + } + + return BUS_RELEASE_RESOURCE(device_get_parent(brdev), child, + type, rid, res); } /************************************************************************/ /* PC Card Power Functions */ /************************************************************************/ static int pccbb_pcic_power_enable_socket(device_t brdev, device_t child) { struct pccbb_softc *sc = device_get_softc(brdev); DPRINTF(("pccbb_pcic_socket_enable:\n")); /* power down/up the socket to reset */ { int voltage = pccbb_detect_voltage(brdev); pccbb_power(brdev, CARD_VCC_0V | CARD_VPP_0V); if (voltage & CARD_5V_CARD) pccbb_power(brdev, CARD_VCC_5V | CARD_VPP_VCC); else if (voltage & CARD_3V_CARD) pccbb_power(brdev, CARD_VCC_3V | CARD_VPP_VCC); else { device_printf(brdev, "Unknown card voltage\n"); return ENXIO; } } /* enable socket i/o */ PCIC_MASK(sc, PCIC_PWRCTL, | PCIC_PWRCTL_OE); PCIC_WRITE(sc, PCIC_INTR, PCIC_INTR_ENABLE); /* hold reset for 30ms */ DELAY(30*1000); /* clear the reset flag */ PCIC_MASK(sc, PCIC_INTR, | PCIC_INTR_RESET); /* wait 20ms as per pc card standard (r2.01) section 4.3.6 */ DELAY(20*1000); pccbb_pcic_wait_ready(sc); /* disable all address windows */ PCIC_WRITE(sc, PCIC_ADDRWIN_ENABLE, 0); { int cardtype; CARD_GET_TYPE(child, &cardtype); PCIC_MASK(sc, PCIC_INTR, | ((cardtype == PCCARD_IFTYPE_IO) ? PCIC_INTR_CARDTYPE_IO : PCIC_INTR_CARDTYPE_MEM)); DEVPRINTF((sc->sc_dev, "card type is %s\n", (cardtype == PCCARD_IFTYPE_IO) ? "io" : "mem")); } /* reinstall all the memory and io mappings */ { int win; for (win = 0; win < PCIC_MEM_WINS; ++win) { if (sc->memalloc & (1 << win)) { pccbb_pcic_do_mem_map(sc, win); } } for (win = 0; win < PCIC_IO_WINS; ++win) { if (sc->ioalloc & (1 << win)) { pccbb_pcic_do_io_map(sc, win); } } } return 0; } static void pccbb_pcic_power_disable_socket(device_t brdev, device_t child) { struct pccbb_softc *sc = device_get_softc(brdev); DPRINTF(("pccbb_pcic_socket_disable\n")); /* reset signal asserting... */ PCIC_MASK(sc, PCIC_INTR, & ~PCIC_INTR_RESET); DELAY(2*1000); /* power down the socket */ - PCIC_MASK(sc, PCIC_PWRCTL, &~PCIC_PWRCTL_OE); pccbb_power(brdev, CARD_VCC_0V | CARD_VPP_0V); + PCIC_MASK(sc, PCIC_PWRCTL, &~PCIC_PWRCTL_OE); /* wait 300ms until power fails (Tpf). */ DELAY(300 * 1000); } /************************************************************************/ /* PC Card Resource Functions */ /************************************************************************/ static void pccbb_pcic_wait_ready(struct pccbb_softc *sc) { int i; DEVPRINTF((sc->sc_dev, "pccbb_pcic_wait_ready: status 0x%02x\n", PCIC_READ(sc, PCIC_IF_STATUS))); for (i = 0; i < 10000; i++) { if (PCIC_READ(sc, PCIC_IF_STATUS) & PCIC_IF_STATUS_READY) { return; } DELAY(500); } device_printf(sc->sc_dev, "ready never happened, status = %02x\n", PCIC_READ(sc, PCIC_IF_STATUS)); } #define PCIC_MEMINFO(NUM) { \ PCIC_SYSMEM_ADDR ## NUM ## _START_LSB, \ PCIC_SYSMEM_ADDR ## NUM ## _START_MSB, \ PCIC_SYSMEM_ADDR ## NUM ## _STOP_LSB, \ PCIC_SYSMEM_ADDR ## NUM ## _STOP_MSB, \ PCIC_SYSMEM_ADDR ## NUM ## _WIN, \ PCIC_CARDMEM_ADDR ## NUM ## _LSB, \ PCIC_CARDMEM_ADDR ## NUM ## _MSB, \ PCIC_ADDRWIN_ENABLE_MEM ## NUM ##, \ } static struct mem_map_index_st { int sysmem_start_lsb; int sysmem_start_msb; int sysmem_stop_lsb; int sysmem_stop_msb; int sysmem_win; int cardmem_lsb; int cardmem_msb; int memenable; } mem_map_index[] = { PCIC_MEMINFO(0), PCIC_MEMINFO(1), PCIC_MEMINFO(2), PCIC_MEMINFO(3), PCIC_MEMINFO(4), }; #undef PCIC_MEMINFO static void pccbb_pcic_do_mem_map(struct pccbb_softc *sc, int win) { PCIC_WRITE(sc, mem_map_index[win].sysmem_start_lsb, (sc->mem[win].addr >> PCIC_SYSMEM_ADDRX_SHIFT) & 0xff); PCIC_WRITE(sc, mem_map_index[win].sysmem_start_msb, ((sc->mem[win].addr >> (PCIC_SYSMEM_ADDRX_SHIFT + 8)) & PCIC_SYSMEM_ADDRX_START_MSB_ADDR_MASK) | 0x80); PCIC_WRITE(sc, mem_map_index[win].sysmem_stop_lsb, ((sc->mem[win].addr + sc->mem[win].realsize - 1) >> PCIC_SYSMEM_ADDRX_SHIFT) & 0xff); PCIC_WRITE(sc, mem_map_index[win].sysmem_stop_msb, (((sc->mem[win].addr + sc->mem[win].realsize - 1) >> (PCIC_SYSMEM_ADDRX_SHIFT + 8)) & PCIC_SYSMEM_ADDRX_STOP_MSB_ADDR_MASK) | PCIC_SYSMEM_ADDRX_STOP_MSB_WAIT2); PCIC_WRITE(sc, mem_map_index[win].sysmem_win, (sc->mem[win].addr >> PCIC_MEMREG_WIN_SHIFT) & 0xff); PCIC_WRITE(sc, mem_map_index[win].cardmem_lsb, (sc->mem[win].offset >> PCIC_CARDMEM_ADDRX_SHIFT) & 0xff); PCIC_WRITE(sc, mem_map_index[win].cardmem_msb, ((sc->mem[win].offset >> (PCIC_CARDMEM_ADDRX_SHIFT + 8)) & PCIC_CARDMEM_ADDRX_MSB_ADDR_MASK) | ((sc->mem[win].kind == PCCARD_MEM_ATTR) ? PCIC_CARDMEM_ADDRX_MSB_REGACTIVE_ATTR : 0)); PCIC_MASK(sc, PCIC_ADDRWIN_ENABLE, | PCIC_ADDRWIN_ENABLE_MEMCS16 | mem_map_index[win].memenable); DELAY(100); #ifdef CBB_DEBUG { int r1, r2, r3, r4, r5, r6, r7; r1 = PCIC_READ(sc, mem_map_index[win].sysmem_start_msb); r2 = PCIC_READ(sc, mem_map_index[win].sysmem_start_lsb); r3 = PCIC_READ(sc, mem_map_index[win].sysmem_stop_msb); r4 = PCIC_READ(sc, mem_map_index[win].sysmem_stop_lsb); r5 = PCIC_READ(sc, mem_map_index[win].cardmem_msb); r6 = PCIC_READ(sc, mem_map_index[win].cardmem_lsb); r7 = PCIC_READ(sc, mem_map_index[win].sysmem_win); DPRINTF(("pccbb_pcic_do_mem_map window %d: %02x%02x %02x%02x " "%02x%02x %02x (%08x+%08x.%08x*%08lx)\n", win, r1, r2, r3, r4, r5, r6, r7, sc->mem[win].addr, sc->mem[win].size, sc->mem[win].realsize, sc->mem[win].offset)); } #endif } static int -pccbb_pcic_mem_map(struct pccbb_softc *sc, int kind, - struct resource *r, bus_addr_t card_addr, int *win) +pccbb_pcic_mem_map(struct pccbb_softc *sc, int kind, struct resource *res) { - int i; + int win; + struct pccbb_reslist *rle; + bus_addr_t card_addr; - *win = -1; - for (i = 0; i < PCIC_MEM_WINS; i++) { - if ((sc->memalloc & (1 << i)) == 0) { - *win = i; - sc->memalloc |= (1 << i); + for (win = 0; win < PCIC_MEM_WINS; win++) { + if ((sc->memalloc & (1 << win)) == 0) { + sc->memalloc |= (1 << win); break; } } - if (*win == -1) + if (win >= PCIC_MEM_WINS) return (1); - card_addr = card_addr - card_addr % PCIC_MEM_PAGESIZE; - sc->mem[*win].memt = rman_get_bustag(r); - sc->mem[*win].memh = rman_get_bushandle(r); - sc->mem[*win].addr = rman_get_start(r); - sc->mem[*win].size = rman_get_end(r) - sc->mem[*win].addr + 1; - sc->mem[*win].realsize = sc->mem[*win].size + PCIC_MEM_PAGESIZE - 1; - sc->mem[*win].realsize = sc->mem[*win].realsize - - (sc->mem[*win].realsize % PCIC_MEM_PAGESIZE); - sc->mem[*win].offset = ((long)card_addr) - - ((long)(sc->mem[*win].addr)); - sc->mem[*win].kind = kind; + SLIST_FOREACH(rle, &sc->rl, link) { + if (rle->res == res) + break; + } + if (!rle) { + device_printf(sc->sc_dev, + "pcic_map_mem: Memory resource not found\n"); + return ENXIO; + } + card_addr = rle->cardaddr - rle->cardaddr % PCIC_MEM_PAGESIZE; + sc->mem[win].memt = rman_get_bustag(res); + sc->mem[win].memh = rman_get_bushandle(res); + sc->mem[win].addr = rman_get_start(res); + sc->mem[win].size = rman_get_end(res) - sc->mem[win].addr + 1; + sc->mem[win].realsize = sc->mem[win].size + PCIC_MEM_PAGESIZE - 1; + sc->mem[win].realsize = sc->mem[win].realsize - + (sc->mem[win].realsize % PCIC_MEM_PAGESIZE); + sc->mem[win].offset = ((long)card_addr) - + ((long)(sc->mem[win].addr)); + sc->mem[win].kind = kind; DPRINTF(("pccbb_pcic_mem_map window %d bus %x+%x+%lx card addr %x\n", - *win, sc->mem[*win].addr, sc->mem[*win].size, - sc->mem[*win].offset, card_addr)); + win, sc->mem[win].addr, sc->mem[win].size, + sc->mem[win].offset, card_addr)); - pccbb_pcic_do_mem_map(sc, *win); + pccbb_pcic_do_mem_map(sc, win); return (0); } static void pccbb_pcic_mem_unmap(struct pccbb_softc *sc, int window) { if (window >= PCIC_MEM_WINS) panic("pccbb_pcic_mem_unmap: window out of range"); PCIC_MASK(sc, PCIC_ADDRWIN_ENABLE, & ~mem_map_index[window].memenable); sc->memalloc &= ~(1 << window); } +static int +pccbb_pcic_mem_findmap(struct pccbb_softc *sc, struct resource *res) +{ + int win; + + for (win = 0; win < PCIC_MEM_WINS; win++) { + if (sc->mem[win].memt == rman_get_bustag(res) && + sc->mem[win].addr == rman_get_start(res) && + sc->mem[win].size == rman_get_size(res)) + return win; + } + device_printf(sc->sc_dev, "Memory map not found!\n"); + return -1; +} + #define PCIC_IOINFO(NUM) { \ PCIC_IOADDR ## NUM ## _START_LSB, \ PCIC_IOADDR ## NUM ## _START_MSB, \ PCIC_IOADDR ## NUM ## _STOP_LSB, \ PCIC_IOADDR ## NUM ## _STOP_MSB, \ PCIC_ADDRWIN_ENABLE_IO ## NUM ##, \ PCIC_IOCTL_IO ## NUM ## _WAITSTATE \ | PCIC_IOCTL_IO ## NUM ## _ZEROWAIT \ | PCIC_IOCTL_IO ## NUM ## _IOCS16SRC_MASK \ | PCIC_IOCTL_IO ## NUM ## _DATASIZE_MASK, \ { \ PCIC_IOCTL_IO ## NUM ## _IOCS16SRC_CARD, \ PCIC_IOCTL_IO ## NUM ## _IOCS16SRC_DATASIZE \ | PCIC_IOCTL_IO ## NUM ## _DATASIZE_8BIT, \ PCIC_IOCTL_IO ## NUM ## _IOCS16SRC_DATASIZE \ | PCIC_IOCTL_IO ## NUM ## _DATASIZE_16BIT, \ } \ } static struct io_map_index_st { int start_lsb; int start_msb; int stop_lsb; int stop_msb; int ioenable; int ioctlmask; int ioctlbits[3]; /* indexed by PCCARD_WIDTH_* */ } io_map_index[] = { PCIC_IOINFO(0), PCIC_IOINFO(1), }; #undef PCIC_IOINFO static void pccbb_pcic_do_io_map(struct pccbb_softc *sc, int win) { PCIC_WRITE(sc, io_map_index[win].start_lsb, sc->io[win].addr & 0xff); PCIC_WRITE(sc, io_map_index[win].start_msb, (sc->io[win].addr >> 8) & 0xff); PCIC_WRITE(sc, io_map_index[win].stop_lsb, (sc->io[win].addr + sc->io[win].size - 1) & 0xff); PCIC_WRITE(sc, io_map_index[win].stop_msb, ((sc->io[win].addr + sc->io[win].size - 1) >> 8) & 0xff); PCIC_MASK2(sc, PCIC_IOCTL, & ~io_map_index[win].ioctlmask, | io_map_index[win].ioctlbits[sc->io[win].width]); PCIC_MASK(sc, PCIC_ADDRWIN_ENABLE, | io_map_index[win].ioenable); #ifdef CBB_DEBUG { int r1, r2, r3, r4; r1 = PCIC_READ(sc, io_map_index[win].start_msb); r2 = PCIC_READ(sc, io_map_index[win].start_lsb); r3 = PCIC_READ(sc, io_map_index[win].stop_msb); r4 = PCIC_READ(sc, io_map_index[win].stop_lsb); DPRINTF(("pccbb_pcic_do_io_map window %d: %02x%02x %02x%02x " "(%08x+%08x)\n", win, r1, r2, r3, r4, sc->io[win].addr, sc->io[win].size)); } #endif } static int -pccbb_pcic_io_map(struct pccbb_softc *sc, int width, - struct resource *r, bus_addr_t card_addr, int *win) +pccbb_pcic_io_map(struct pccbb_softc *sc, int width, struct resource *r) { - int i; + int win; #ifdef CBB_DEBUG static char *width_names[] = { "auto", "io8", "io16"}; #endif - *win = -1; - for (i=0; i < PCIC_IO_WINS; i++) { - if ((sc->ioalloc & (1 << i)) == 0) { - *win = i; - sc->ioalloc |= (1 << i); + for (win=0; win < PCIC_IO_WINS; win++) { + if ((sc->ioalloc & (1 << win)) == 0) { + sc->ioalloc |= (1 << win); break; } } - if (*win == -1) + if (win >= PCIC_IO_WINS) return (1); - sc->io[*win].iot = rman_get_bustag(r); - sc->io[*win].ioh = rman_get_bushandle(r); - sc->io[*win].addr = rman_get_start(r); - sc->io[*win].size = rman_get_end(r) - sc->io[*win].addr + 1; - sc->io[*win].flags = 0; - sc->io[*win].width = width; + sc->io[win].iot = rman_get_bustag(r); + sc->io[win].ioh = rman_get_bushandle(r); + sc->io[win].addr = rman_get_start(r); + sc->io[win].size = rman_get_end(r) - sc->io[win].addr + 1; + sc->io[win].flags = 0; + sc->io[win].width = width; DPRINTF(("pccbb_pcic_io_map window %d %s port %x+%x\n", - *win, width_names[width], sc->io[*win].addr, - sc->io[*win].size)); + win, width_names[width], sc->io[win].addr, + sc->io[win].size)); - pccbb_pcic_do_io_map(sc, *win); + pccbb_pcic_do_io_map(sc, win); return (0); } static void pccbb_pcic_io_unmap(struct pccbb_softc *sc, int window) { if (window >= PCIC_IO_WINS) panic("pccbb_pcic_io_unmap: window out of range"); PCIC_MASK(sc, PCIC_ADDRWIN_ENABLE, & ~io_map_index[window].ioenable); sc->ioalloc &= ~(1 << window); sc->io[window].iot = 0; sc->io[window].ioh = 0; sc->io[window].addr = 0; sc->io[window].size = 0; sc->io[window].flags = 0; sc->io[window].width = 0; } static int -pccbb_pcic_activate_resource(device_t brdev, device_t child, int type, int rid, - struct resource *r) +pccbb_pcic_io_findmap(struct pccbb_softc *sc, struct resource *res) { - int err; int win; - struct pccbb_reslist *rle; - struct pccbb_softc *sc = device_get_softc(brdev); - if (rman_get_flags(r) & RF_ACTIVE) - return 0; + for (win = 0; win < PCIC_IO_WINS; win++) { + if (sc->io[win].iot == rman_get_bustag(res) && + sc->io[win].addr == rman_get_start(res) && + sc->io[win].size == rman_get_size(res)) + return win; + } + device_printf(sc->sc_dev, "IO map not found!\n"); + return -1; +} - switch (type) { - case SYS_RES_IOPORT: - err = pccbb_pcic_io_map(sc, 0, r, 0, &win); +static int +pccbb_pcic_activate_resource(device_t brdev, device_t child, int type, int rid, + struct resource *res) +{ + int err; + struct pccbb_softc *sc = device_get_softc(brdev); + if (!(rman_get_flags(res) & RF_ACTIVE)) { /* not already activated */ + switch (type) { + case SYS_RES_IOPORT: + err = pccbb_pcic_io_map(sc, 0, res); + break; + case SYS_RES_MEMORY: + err = pccbb_pcic_mem_map(sc, 0, res); + break; + default: + err = 0; + break; + } if (err) return err; - break; - case SYS_RES_MEMORY: - err = pccbb_pcic_mem_map(sc, 0, r, 0, &win); - if (err) - return err; - break; - default: - break; + } - SLIST_FOREACH(rle, &sc->rl, entries) { - if (type == rle->type && rid == rle->rid && - child == rle->odev) { - rle->win = win; - break; - } - } - err = bus_generic_activate_resource(brdev, child, type, rid, r); - return (err); + return BUS_ACTIVATE_RESOURCE(device_get_parent(brdev), child, + type, rid, res); } static int pccbb_pcic_deactivate_resource(device_t brdev, device_t child, int type, - int rid, struct resource *r) + int rid, struct resource *res) { struct pccbb_softc *sc = device_get_softc(brdev); int win; - struct pccbb_reslist *rle; - win = -1; - SLIST_FOREACH(rle, &sc->rl, entries) { - if (type == rle->type && rid == rle->rid && - child == rle->odev) { - win = rle->win; + + if (rman_get_flags(res) & RF_ACTIVE) { /* if activated */ + switch (type) { + case SYS_RES_IOPORT: + win = pccbb_pcic_io_findmap(sc, res); + if (win >= 0) + pccbb_pcic_io_unmap(sc, win); + else + return ENOENT; break; + case SYS_RES_MEMORY: + win = pccbb_pcic_mem_findmap(sc, res); + if (win >= 0) + pccbb_pcic_mem_unmap(sc, win); + else + return ENOENT; + break; } } - if (win == -1) { - panic("pccbb_pcic: deactivating bogus resoure"); - return 1; - } - - switch (type) { - case SYS_RES_IOPORT: - pccbb_pcic_io_unmap(sc, win); - break; - case SYS_RES_MEMORY: - pccbb_pcic_mem_unmap(sc, win); - break; - default: - break; - } - return bus_generic_deactivate_resource(brdev, child, type, rid, r); + return BUS_DEACTIVATE_RESOURCE(device_get_parent(brdev), child, + type, rid, res); } static struct resource * pccbb_pcic_alloc_resource(device_t brdev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { - struct resource *r = NULL; + struct resource *res = NULL; struct pccbb_softc *sc = device_get_softc(brdev); struct pccbb_reslist *rle; + int tmp; if ((sc->sc_flags & PCCBB_PCIC_MEM_32) == 0) { /* XXX: how do we do this? */ panic("PCCBB bridge cannot handle non MEM_32 bridges\n"); } switch (type) { case SYS_RES_MEMORY: - /* Nearly default */ - if (start == 0 && end == ~0 && count != 1) { - start = CARDBUS_SYS_RES_MEMORY_START; /* XXX -- should be tweakable*/ - end = CARDBUS_SYS_RES_MEMORY_END; - } + if (start < 0x10000000) + start = 0x10000000; /* XXX tweakable? */ + if (end < start) + end = start; flags = (flags & ~RF_ALIGNMENT_MASK) | rman_make_alignment_flags(PCCBB_MEMALIGN); break; case SYS_RES_IOPORT: if (start < 0x100) start = 0x100; /* XXX tweakable? */ if (end < start) end = start; break; case SYS_RES_IRQ: + tmp = rman_get_start(sc->sc_irq_res); + if (start > tmp || end < tmp || count != 1) { + device_printf(child, "requested interrupt %ld-%ld," + "count = %ld not supported by pccbb\n", + start, end, count); + return NULL; + } flags |= RF_SHAREABLE; start = end = rman_get_start(sc->sc_irq_res); break; } - r = bus_generic_alloc_resource(brdev, child, type, rid, start, end, - count, flags & ~RF_ACTIVE); - if (r == NULL) + res = BUS_ALLOC_RESOURCE(device_get_parent(brdev), child, type, rid, + start, end, count, flags & ~RF_ACTIVE); + if (res == NULL) return NULL; - rle = malloc(sizeof(struct pccbb_reslist), M_DEVBUF, M_WAITOK); + rle = malloc(sizeof(struct pccbb_reslist), M_DEVBUF, M_NOWAIT); + if (!rle) + panic("pccbb_pcic_alloc_resource: can't record entry!"); + rle->res = res; rle->type = type; rle->rid = *rid; - rle->start = rman_get_start(r); - rle->end = rman_get_end(r); - rle->odev = child; - rle->win = -1; - SLIST_INSERT_HEAD(&sc->rl, rle, entries); + rle->cardaddr = 0; + SLIST_INSERT_HEAD(&sc->rl, rle, link); if (flags & RF_ACTIVE) { - if (bus_activate_resource(child, type, *rid, r) != 0) { - BUS_RELEASE_RESOURCE(brdev, child, type, *rid, r); + if (bus_activate_resource(child, type, *rid, res) != 0) { + bus_release_resource(child, type, *rid, res); return NULL; } } - return r; + return res; } static int pccbb_pcic_release_resource(device_t brdev, device_t child, int type, int rid, struct resource *res) { struct pccbb_softc *sc = device_get_softc(brdev); struct pccbb_reslist *rle; - int count = 0; if (rman_get_flags(res) & RF_ACTIVE) { int error; error = bus_deactivate_resource(child, type, rid, res); if (error != 0) return error; } - SLIST_FOREACH(rle, &sc->rl, entries) { - if (type == rle->type && rid == rle->rid && - child == rle->odev) { - SLIST_REMOVE(&sc->rl, rle, pccbb_reslist, entries); + SLIST_FOREACH(rle, &sc->rl, link) { + if (rle->res == res) { + SLIST_REMOVE(&sc->rl, rle, pccbb_reslist, link); free(rle, M_DEVBUF); - count++; break; } } - if (count == 0) { - panic("pccbb_pcic: releasing bogus resource"); - } - return bus_generic_release_resource(brdev, child, type, rid, res); + return BUS_RELEASE_RESOURCE(device_get_parent(brdev), child, + type, rid, res); } /************************************************************************/ /* PC Card methods */ /************************************************************************/ static int pccbb_pcic_set_res_flags(device_t brdev, device_t child, int type, int rid, u_int32_t flags) { struct pccbb_softc *sc = device_get_softc(brdev); + struct resource *res; + struct pccbb_reslist *rle; + int win; + res = NULL; if (type != SYS_RES_MEMORY) return (EINVAL); - sc->mem[rid].kind = flags; - pccbb_pcic_do_mem_map(sc, rid); + SLIST_FOREACH(rle, &sc->rl, link) { + if (SYS_RES_MEMORY == rle->type && rid == rle->rid && + child == rle->res->r_dev) { + res = rle->res; + break; + } + } + + if (res == NULL) { + device_printf(brdev, + "set_res_flags: specified rid not found\n"); + return ENOENT; + } + win = pccbb_pcic_mem_findmap(sc, res); + if (win < 0) { + device_printf(brdev, + "set_res_flags: specified resource not active\n"); + return ENOENT; + } + + sc->mem[win].kind = flags; + pccbb_pcic_do_mem_map(sc, win); return 0; } static int pccbb_pcic_set_memory_offset(device_t brdev, device_t child, int rid, u_int32_t cardaddr, u_int32_t *deltap) { struct pccbb_softc *sc = device_get_softc(brdev); int win; struct pccbb_reslist *rle; + struct resource *res; u_int32_t delta; win = -1; - SLIST_FOREACH(rle, &sc->rl, entries) { + res = NULL; + SLIST_FOREACH(rle, &sc->rl, link) { if (SYS_RES_MEMORY == rle->type && rid == rle->rid && - child == rle->odev) { - win = rle->win; + child == rle->res->r_dev) { + res = rle->res; + rle->cardaddr = cardaddr; break; } } - if (win == -1) { - panic("pccbb_pcic: setting memory offset of bogus resource"); - return 1; + + if (res == NULL) { + device_printf(brdev, + "set_memory_offset: specified rid not found\n"); + return ENOENT; } + win = pccbb_pcic_mem_findmap(sc, res); + if (win < 0) { + device_printf(brdev, + "set_memory_offset: specified resource not active\n"); + return ENOENT; + } delta = cardaddr % PCIC_MEM_PAGESIZE; if (deltap) *deltap = delta; cardaddr -= delta; sc->mem[win].realsize = sc->mem[win].size + delta + PCIC_MEM_PAGESIZE - 1; sc->mem[win].realsize = sc->mem[win].realsize - (sc->mem[win].realsize % PCIC_MEM_PAGESIZE); sc->mem[win].offset = cardaddr - sc->mem[win].addr; pccbb_pcic_do_mem_map(sc, win); return 0; } /************************************************************************/ /* POWER methods */ /************************************************************************/ static int pccbb_power_enable_socket(device_t brdev, device_t child) { struct pccbb_softc *sc = device_get_softc(brdev); if (sc->sc_flags & PCCBB_16BIT_CARD) return pccbb_pcic_power_enable_socket(brdev, child); else return pccbb_cardbus_power_enable_socket(brdev, child); } static void pccbb_power_disable_socket(device_t brdev, device_t child) { struct pccbb_softc *sc = device_get_softc(brdev); if (sc->sc_flags & PCCBB_16BIT_CARD) pccbb_pcic_power_disable_socket(brdev, child); else pccbb_cardbus_power_disable_socket(brdev, child); } /************************************************************************/ /* BUS Methods */ /************************************************************************/ static int pccbb_activate_resource(device_t brdev, device_t child, int type, int rid, struct resource *r) { struct pccbb_softc *sc = device_get_softc(brdev); if (sc->sc_flags & PCCBB_16BIT_CARD) return pccbb_pcic_activate_resource(brdev, child, type, rid, r); else return pccbb_cardbus_activate_resource(brdev, child, type, rid, r); } static int pccbb_deactivate_resource(device_t brdev, device_t child, int type, int rid, struct resource *r) { struct pccbb_softc *sc = device_get_softc(brdev); if (sc->sc_flags & PCCBB_16BIT_CARD) return pccbb_pcic_deactivate_resource(brdev, child, type, rid, r); else return pccbb_cardbus_deactivate_resource(brdev, child, type, rid, r); } static struct resource * pccbb_alloc_resource(device_t brdev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct pccbb_softc *sc = device_get_softc(brdev); if (sc->sc_flags & PCCBB_16BIT_CARD) return pccbb_pcic_alloc_resource(brdev, child, type, rid, start, end, count, flags); else return pccbb_cardbus_alloc_resource(brdev, child, type, rid, start, end, count, flags); } static int pccbb_release_resource(device_t brdev, device_t child, int type, int rid, struct resource *r) { struct pccbb_softc *sc = device_get_softc(brdev); if (sc->sc_flags & PCCBB_16BIT_CARD) return pccbb_pcic_release_resource(brdev, child, type, rid, r); else return pccbb_cardbus_release_resource(brdev, child, type, rid, r); } static int pccbb_read_ivar(device_t brdev, device_t child, int which, uintptr_t *result) { struct pccbb_softc *sc = device_get_softc(brdev); switch (which) { case PCIB_IVAR_BUS: *result = sc->sc_secbus; return(0); } return(ENOENT); } static int pccbb_write_ivar(device_t brdev, device_t child, int which, uintptr_t value) { struct pccbb_softc *sc = device_get_softc(brdev); switch (which) { case PCIB_IVAR_BUS: sc->sc_secbus = value; break; } return(ENOENT); } /************************************************************************/ /* PCI compat methods */ /************************************************************************/ static int pccbb_maxslots(device_t brdev) { return 0; } static u_int32_t pccbb_read_config(device_t brdev, int b, int s, int f, int reg, int width) { /* * Pass through to the next ppb up the chain (i.e. our grandparent). */ return PCIB_READ_CONFIG(device_get_parent(device_get_parent(brdev)), b, s, f, reg, width); } static void pccbb_write_config(device_t brdev, int b, int s, int f, int reg, u_int32_t val, int width) { /* * Pass through to the next ppb up the chain (i.e. our grandparent). */ PCIB_WRITE_CONFIG(device_get_parent(device_get_parent(brdev)), b, s, f, reg, val, width); } static device_method_t pccbb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pccbb_probe), DEVMETHOD(device_attach, pccbb_attach), DEVMETHOD(device_detach, pccbb_detach), + DEVMETHOD(device_shutdown, pccbb_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* bus methods */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, pccbb_read_ivar), DEVMETHOD(bus_write_ivar, pccbb_write_ivar), DEVMETHOD(bus_alloc_resource, pccbb_alloc_resource), DEVMETHOD(bus_release_resource, pccbb_release_resource), DEVMETHOD(bus_activate_resource, pccbb_activate_resource), DEVMETHOD(bus_deactivate_resource, pccbb_deactivate_resource), DEVMETHOD(bus_driver_added, pccbb_driver_added), DEVMETHOD(bus_child_detached, pccbb_child_detached), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), - /* 16-bit card interface */ + /* 16-bit card interface */ DEVMETHOD(card_set_res_flags, pccbb_pcic_set_res_flags), DEVMETHOD(card_set_memory_offset, pccbb_pcic_set_memory_offset), + DEVMETHOD(card_reprobe_card, pccbb_card_reprobe), - /* power interface */ + /* power interface */ DEVMETHOD(power_enable_socket, pccbb_power_enable_socket), DEVMETHOD(power_disable_socket, pccbb_power_disable_socket), - /* pcib compatibility interface */ + /* pcib compatibility interface */ DEVMETHOD(pcib_maxslots, pccbb_maxslots), DEVMETHOD(pcib_read_config, pccbb_read_config), DEVMETHOD(pcib_write_config, pccbb_write_config), {0,0} }; static driver_t pccbb_driver = { "pccbb", pccbb_methods, sizeof(struct pccbb_softc) }; static devclass_t pccbb_devclass; DRIVER_MODULE(pccbb, pci, pccbb_driver, pccbb_devclass, 0, 0); SYSINIT(pccbb, SI_SUB_KTHREAD_IDLE, SI_ORDER_ANY, pccbb_start_threads, 0); Index: head/sys/dev/pccbb/pccbbvar.h =================================================================== --- head/sys/dev/pccbb/pccbbvar.h (revision 82377) +++ head/sys/dev/pccbb/pccbbvar.h (revision 82378) @@ -1,128 +1,123 @@ /* * Copyright (c) 2000,2001 Jonathan Chen. * 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, * without modification, immediately at the beginning of the file. * 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$ */ /* * Structure definitions for the Cardbus Bridge driver */ struct intrhand { void(*func)(void*arg); void *arg; STAILQ_ENTRY(intrhand) entries; }; struct pccbb_socketreg { u_int32_t socket_event; u_int32_t socket_mask; u_int32_t socket_state; u_int32_t socket_force; u_int32_t socket_control; u_int32_t socket_power; }; struct pccbb_reslist { - SLIST_ENTRY(pccbb_reslist) entries; + SLIST_ENTRY(pccbb_reslist) link; + struct resource *res; int type; int rid; - u_int32_t start; - u_int32_t end; - device_t odev; - int win; + /* note: unlike the regular resource list, there can be + * duplicate rid's in the same list. However, the + * combination of rid and res->r_dev should be unique. + */ + bus_addr_t cardaddr; /* for 16-bit pccard memory */ }; #define PCCBB_AUTO_OPEN_SMALLHOLE 0x100 struct pccbb_softc { device_t sc_dev; struct resource *sc_base_res; struct resource *sc_irq_res; void *sc_intrhand; struct pccbb_socketreg *sc_socketreg; u_int8_t sc_secbus; u_int8_t sc_subbus; struct mtx sc_mtx; u_int32_t sc_flags; #define PCCBB_PCIC_IO_RELOC 0x01 #define PCCBB_PCIC_MEM_32 0x02 #define PCCBB_16BIT_CARD 0x02000000 #define PCCBB_KTHREAD_RUNNING 0x04000000 #define PCCBB_KTHREAD_DONE 0x08000000 int sc_chipset; /* chipset id */ #define CB_UNKNOWN 0 /* NOT Cardbus-PCI bridge */ #define CB_TI113X 1 /* TI PCI1130/1131 */ #define CB_TI12XX 2 /* TI PCI1250/1220 */ #define CB_RF5C47X 3 /* RICOH RF5C475/476/477 */ #define CB_RF5C46X 4 /* RICOH RF5C465/466/467 */ #define CB_TOPIC95 5 /* Toshiba ToPIC95 */ #define CB_TOPIC95B 6 /* Toshiba ToPIC95B */ #define CB_TOPIC97 7 /* Toshiba ToPIC97/100 */ #define CB_CIRRUS 8 /* Cirrus Logic CLPD683x */ SLIST_HEAD(, pccbb_reslist) rl; device_t sc_cbdev; device_t sc_pccarddev; /* PC Card stuff */ int memalloc; struct pccard_mem_handle mem[PCIC_MEM_WINS]; int ioalloc; struct pccard_io_handle io[PCIC_IO_WINS]; /* kthread staff */ struct proc *event_thread; }; /* result of detect_card */ #define CARD_UKN_CARD 0x00 #define CARD_5V_CARD 0x01 #define CARD_3V_CARD 0x02 #define CARD_XV_CARD 0x04 #define CARD_YV_CARD 0x08 /* for power_socket */ #define CARD_VCC_UC 0x0000 #define CARD_VCC_3V 0x0001 #define CARD_VCC_XV 0x0002 #define CARD_VCC_YV 0x0003 #define CARD_VCC_0V 0x0004 #define CARD_VCC_5V 0x0005 #define CARD_VCCMASK 0x000f #define CARD_VPP_UC 0x0000 #define CARD_VPP_VCC 0x0010 #define CARD_VPP_12V 0x0030 #define CARD_VPP_0V 0x0040 #define CARD_VPPMASK 0x00f0 - -/* XXX: rman is dumb */ -#define CARDBUS_SYS_RES_MEMORY_START 0x44000000 -#define CARDBUS_SYS_RES_MEMORY_END 0xEFFFFFFF -#define CARDBUS_SYS_RES_IOPORT_START 0x3000 -#define CARDBUS_SYS_RES_IOPORT_END 0xEFFF - Index: head/sys/sys/rman.h =================================================================== --- head/sys/sys/rman.h (revision 82377) +++ head/sys/sys/rman.h (revision 82378) @@ -1,144 +1,145 @@ /* * Copyright 1998 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. 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 _SYS_RMAN_H_ #define _SYS_RMAN_H_ 1 #ifndef _KERNEL #include #endif #define RF_ALLOCATED 0x0001 /* resource has been reserved */ #define RF_ACTIVE 0x0002 /* resource allocation has been activated */ #define RF_SHAREABLE 0x0004 /* resource permits contemporaneous sharing */ #define RF_TIMESHARE 0x0008 /* resource permits time-division sharing */ #define RF_WANTED 0x0010 /* somebody is waiting for this resource */ #define RF_FIRSTSHARE 0x0020 /* first in sharing list */ +#define RF_PREFETCHABLE 0x0040 /* resource is prefetchable */ #define RF_ALIGNMENT_SHIFT 10 /* alignment size bit starts bit 10 */ #define RF_ALIGNMENT_MASK (0x003F << RF_ALIGNMENT_SHIFT) /* resource address alignemnt size bit mask */ #define RF_ALIGNMENT_LOG2(x) ((x) << RF_ALIGNMENT_SHIFT) #define RF_ALIGNMENT(x) (((x) & RF_ALIGNMENT_MASK) >> RF_ALIGNMENT_SHIFT) enum rman_type { RMAN_UNINIT = 0, RMAN_GAUGE, RMAN_ARRAY }; /* * String length exported to userspace for resource names, etc. */ #define RM_TEXTLEN 32 /* * Userspace-exported structures. */ struct u_resource { uintptr_t r_handle; /* resource uniquifier */ uintptr_t r_parent; /* parent rman */ uintptr_t r_device; /* device owning this resource */ char r_devname[RM_TEXTLEN]; /* device name XXX obsolete */ u_long r_start; /* offset in resource space */ u_long r_size; /* size in resource space */ u_int r_flags; /* RF_* flags */ }; struct u_rman { uintptr_t rm_handle; /* rman uniquifier */ char rm_descr[RM_TEXTLEN]; /* rman description */ u_long rm_start; /* base of managed region */ u_long rm_size; /* size of managed region */ enum rman_type rm_type; /* region type */ }; #ifdef _KERNEL /* * We use a linked list rather than a bitmap because we need to be able to * represent potentially huge objects (like all of a processor's physical * address space). That is also why the indices are defined to have type * `unsigned long' -- that being the largest integral type in ISO C (1990). * The 1999 version of C allows `long long'; we may need to switch to that * at some point in the future, particularly if we want to support 36-bit * addresses on IA32 hardware. */ TAILQ_HEAD(resource_head, resource); struct resource { TAILQ_ENTRY(resource) r_link; LIST_ENTRY(resource) r_sharelink; LIST_HEAD(, resource) *r_sharehead; u_long r_start; /* index of the first entry in this resource */ u_long r_end; /* index of the last entry (inclusive) */ u_int r_flags; void *r_virtual; /* virtual address of this resource */ bus_space_tag_t r_bustag; /* bus_space tag */ bus_space_handle_t r_bushandle; /* bus_space handle */ struct device *r_dev; /* device which has allocated this resource */ struct rman *r_rm; /* resource manager from whence this came */ }; struct rman { struct resource_head rm_list; struct mtx *rm_mtx; /* mutex used to protect rm_list */ TAILQ_ENTRY(rman) rm_link; /* link in list of all rmans */ u_long rm_start; /* index of globally first entry */ u_long rm_end; /* index of globally last entry */ enum rman_type rm_type; /* what type of resource this is */ const char *rm_descr; /* text descripion of this resource */ }; TAILQ_HEAD(rman_head, rman); int rman_activate_resource(struct resource *r); int rman_await_resource(struct resource *r, int pri, int timo); int rman_deactivate_resource(struct resource *r); int rman_fini(struct rman *rm); int rman_init(struct rman *rm); int rman_manage_region(struct rman *rm, u_long start, u_long end); int rman_release_resource(struct resource *r); struct resource *rman_reserve_resource(struct rman *rm, u_long start, u_long end, u_long count, u_int flags, struct device *dev); uint32_t rman_make_alignment_flags(uint32_t size); #define rman_get_start(r) ((r)->r_start) #define rman_get_end(r) ((r)->r_end) #define rman_get_size(r) (((r)->r_end - (r)->r_start) + 1) #define rman_get_flags(r) ((r)->r_flags) #define rman_set_virtual(r,v) ((r)->r_virtual = (v)) #define rman_get_virtual(r) ((r)->r_virtual) #define rman_set_bustag(r,t) ((r)->r_bustag = (t)) #define rman_get_bustag(r) ((r)->r_bustag) #define rman_set_bushandle(r,h) ((r)->r_bushandle = (h)) #define rman_get_bushandle(r) ((r)->r_bushandle) extern struct rman_head rman_head; #endif /* _KERNEL */ #endif /* !_SYS_RMAN_H_ */