diff options
author | jon <jon@FreeBSD.org> | 2001-08-27 00:09:42 +0000 |
---|---|---|
committer | jon <jon@FreeBSD.org> | 2001-08-27 00:09:42 +0000 |
commit | c07859c916909808f7cff6785361d52752a25bf6 (patch) | |
tree | eb8e2e5b668b53a10937bcc1b415f47806f840fe /sys/dev/cardbus | |
parent | 673b6e2dbf0d796b5f8b2f3c96b3bd885632602a (diff) | |
download | FreeBSD-src-c07859c916909808f7cff6785361d52752a25bf6.zip FreeBSD-src-c07859c916909808f7cff6785361d52752a25bf6.tar.gz |
Part two of this NEWCARD update:
Briefly, the significant changes include:
* Way better resource management in pccbb, pccard and cardbus.
* pccard hot-removal now appears to work.
* support pre-fetchable memory in cardbus.
* update cardbus to support new pci bus interface functions.
* Fix CIS reading to no longer use rman_get_virtual().
What's not there, but in the works:
* pccard needs to do interrupt properly and not read the ISR on single
function cards.
* real resource management for pccard
* a complete implementation of CIS parsing
* need to look into how to correctly use mutex in pccbb
Diffstat (limited to 'sys/dev/cardbus')
-rw-r--r-- | sys/dev/cardbus/cardbus.c | 759 | ||||
-rw-r--r-- | sys/dev/cardbus/cardbus_cis.c | 629 | ||||
-rw-r--r-- | sys/dev/cardbus/cardbus_cis.h | 4 | ||||
-rw-r--r-- | sys/dev/cardbus/cardbusvar.h | 12 |
4 files changed, 1037 insertions, 367 deletions
diff --git a/sys/dev/cardbus/cardbus.c b/sys/dev/cardbus/cardbus.c index 02150b6..fc478b5 100644 --- a/sys/dev/cardbus/cardbus.c +++ b/sys/dev/cardbus/cardbus.c @@ -49,8 +49,8 @@ #include <sys/rman.h> #include <machine/resource.h> -#include <pci/pcivar.h> -#include <pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> #include <sys/pciio.h> #include <dev/cardbus/cardbusreg.h> @@ -74,18 +74,6 @@ 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); @@ -94,14 +82,15 @@ static void device_setup_regs(device_t brdev, int b, int s, int f, 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, @@ -112,16 +101,18 @@ 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); @@ -130,10 +121,23 @@ 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 */ @@ -227,13 +231,12 @@ cardbus_attach_card(device_t cbdev) 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++; } @@ -271,15 +274,16 @@ cardbus_detach_card(device_t cbdev, int flags) 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) @@ -291,9 +295,9 @@ cardbus_detach_card(device_t cbdev, int flags) 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); @@ -304,26 +308,27 @@ cardbus_driver_added(device_t cbdev, driver_t *driver) 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); } @@ -333,6 +338,82 @@ cardbus_driver_added(device_t cbdev, driver_t *driver) /* 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) { @@ -340,7 +421,7 @@ cardbus_read_device(device_t brdev, int b, int s, int f) 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) @@ -364,37 +445,6 @@ cardbus_read_device(device_t brdev, int b, int s, int f) 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); @@ -404,6 +454,9 @@ cardbus_read_device(device_t brdev, int b, int s, int f) 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; @@ -423,32 +476,6 @@ cardbus_read_device(device_t brdev, int b, int s, int f) #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 @@ -462,7 +489,10 @@ cardbus_freecfg(struct cardbus_devinfo *dinfo) 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", @@ -492,11 +522,45 @@ 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) + 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; @@ -506,9 +570,15 @@ 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; @@ -522,14 +592,29 @@ cardbus_get_resource(device_t cbdev, device_t child, int type, int rid, 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) @@ -541,7 +626,7 @@ 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, @@ -568,162 +653,86 @@ cardbus_delete_resource_method(device_t cbdev, device_t child, 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; - - types = 0; - for (i = 0; i < cfg->nummaps; i++) { - type = cardbus_add_map(cbdev, dev, cfg, PCIR_MAPS + i*4); - types |= 0x1 << type; - } - 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; - } + struct cardbus_intrlist *ile; + + /* 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); } - 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; -} - -static void -cardbus_release_all_resources(device_t cbdev, struct resource_list *rl) -{ - struct resource_list_entry *rle; - - SLIST_FOREACH(rle, rl, link) { + /* Free all allocated resources */ + SLIST_FOREACH(rle, &dinfo->resources, link) { if (rle->res) { - bus_generic_release_resource(device_get_parent(cbdev), - cbdev, rle->type, rle->rid, - 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); } } + resource_list_free(&dinfo->resources); } -static struct resource* +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) + 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; + struct cardbus_devinfo *dinfo; + struct resource_list_entry *rle = 0; + int passthrough = (device_get_parent(child) != cbdev); - 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 (passthrough) { + return (BUS_ALLOC_RESOURCE(device_get_parent(cbdev), child, + type, rid, start, end, count, flags)); + } - 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 */ + dinfo = device_get_ivars(child); + rle = resource_list_find(&dinfo->resources, type, *rid); + + if (!rle) + return NULL; /* no resource of that type/rid */ + + 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); } } @@ -731,6 +740,18 @@ 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 @@ -745,9 +766,100 @@ cardbus_release_resource(device_t cbdev, device_t child, int type, int rid, 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 */ /************************************************************************/ @@ -920,6 +1032,88 @@ cardbus_write_ivar(device_t cbdev, device_t child, int which, uintptr_t value) /* 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) { @@ -941,6 +1135,64 @@ cardbus_write_config_method(device_t cbdev, device_t child, int reg, 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), @@ -960,9 +1212,8 @@ static device_method_t cardbus_methods[] = { 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), @@ -977,6 +1228,12 @@ static device_method_t cardbus_methods[] = { /* 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} }; diff --git a/sys/dev/cardbus/cardbus_cis.c b/sys/dev/cardbus/cardbus_cis.c index 883beb8..f0b4274 100644 --- a/sys/dev/cardbus/cardbus_cis.c +++ b/sys/dev/cardbus/cardbus_cis.c @@ -44,9 +44,12 @@ #include <machine/resource.h> #include <sys/rman.h> -#include <pci/pcivar.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <sys/pciio.h> #include <dev/cardbus/cardbusreg.h> +#include <dev/cardbus/cardbusvar.h> #include <dev/cardbus/cardbus_cis.h> #include "card_if.h" @@ -66,7 +69,7 @@ static const char rcsid[] = #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 { @@ -88,22 +91,28 @@ 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, - 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, +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(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 } @@ -120,6 +129,18 @@ static char *funcnames[] = { "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; @@ -258,11 +279,10 @@ DECODE_PROTOTYPE(bar) 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); @@ -279,13 +299,26 @@ DECODE_PROTOTYPE(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; } @@ -307,14 +340,14 @@ DECODE_PROTOTYPE(end) */ 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--) @@ -336,120 +369,159 @@ cardbus_read_tuple_conf(device_t cbdev, device_t child, u_int32_t *start, } 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; - } - - 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; + return cardbus_read_tuple_mem(cbdev, res, start, off, + tupleid, len, tupledata); } +} - 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; +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); } - 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; } /* @@ -458,7 +530,7 @@ cardbus_read_tuple(device_t cbdev, device_t child, u_int32_t *start, 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; @@ -486,29 +558,348 @@ cardbus_parse_cis(device_t cbdev, device_t child, 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) @@ -571,7 +962,7 @@ cardbus_cis_read(device_t cbdev, device_t child, u_int8_t id, } 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++) @@ -583,6 +974,7 @@ cardbus_cis_free(device_t cbdev, struct cis_tupleinfo *buff, int* nret) int cardbus_do_cis(device_t cbdev, device_t child) { + int ret; struct tuple_callbacks init_callbacks[] = { MAKETUPLE(NULL, generic), MAKETUPLE(DEVICE, generic), @@ -624,5 +1016,10 @@ cardbus_do_cis(device_t cbdev, device_t child) 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); } diff --git a/sys/dev/cardbus/cardbus_cis.h b/sys/dev/cardbus/cardbus_cis.h index ca21090..36df335 100644 --- a/sys/dev/cardbus/cardbus_cis.h +++ b/sys/dev/cardbus/cardbus_cis.h @@ -88,6 +88,10 @@ void cardbus_cis_free(device_t, struct cis_tupleinfo*, int*); /* 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 */ diff --git a/sys/dev/cardbus/cardbusvar.h b/sys/dev/cardbus/cardbusvar.h index 7e0e3c5..814af16 100644 --- a/sys/dev/cardbus/cardbusvar.h +++ b/sys/dev/cardbus/cardbusvar.h @@ -32,8 +32,20 @@ * 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; }; |