From 8983cfbf278ba9c211d639409c4373eeb08f41e9 Mon Sep 17 00:00:00 2001 From: Mike Smith Date: Wed, 13 Dec 2000 01:25:11 +0000 Subject: [PATCH] Next round of PCI subsystem updates: - Break out the /dev/pci driver into a separate file. - Kill the COMPAT_OLDPCI support. - Make the EISA bridge attach a bit more like the old code; explicitly check for the existence of eisa0/isa0 and only attach if they don't already exist. Only make one bus_generic_attach() pass over the bridge, once both busses are attached. Note that the stupid Intel bridge's class is entirely unpredictable. - Add prototypes and re-layout the core PCI modules in line with current coding standards (not a major whitespace change, just moving the module data to the top of the file). - Remove redundant type-2 bridge support from the core PCI code; the PCI-CardBus code does this itself internally. Remove the now entirely redundant header-class-specific support, as well as the secondary and subordinate bus number fields. These are bridge attributes now. - Add support for PCI Extended Capabilities. - Add support for PCI Power Management. The interface currently allows a driver to query and set the power state of a device. - Add helper functions to allow drivers to enable/disable busmastering and the decoding of I/O and memory ranges. - Use PCI_SLOTMAX and PCI_FUNCMAX rather than magic numbers in some places. - Make the PCI-PCI bridge code a little more paranoid about valid I/O and memory decodes. - Add some more PCI register definitions for the command and status registers. Correct another bogus definition for type-1 bridges. --- sys/conf/files | 3 +- sys/conf/options | 1 - sys/dev/pci/eisa_pci.c | 19 +- sys/dev/pci/pci.c | 807 ++++++++++++++--------------------------- sys/dev/pci/pci_pci.c | 49 +-- sys/dev/pci/pci_user.c | 487 +++++++++++++++++++++++++ sys/dev/pci/pcireg.h | 97 +++-- sys/dev/pci/pcivar.h | 165 ++++----- sys/pci/pci_compat.c | 336 ----------------- 9 files changed, 931 insertions(+), 1033 deletions(-) create mode 100644 sys/dev/pci/pci_user.c delete mode 100644 sys/pci/pci_compat.c diff --git a/sys/conf/files b/sys/conf/files index b43b3531bb7..9ff4e4d1a25 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -375,6 +375,7 @@ dev/pci/isa_pci.c optional pci dev/pci/pci.c count pci dev/pci/pci_if.m optional pci dev/pci/pci_pci.c optional pci +dev/pci/pci_user.c optional pci dev/pci/pcib_if.m optional pci dev/pci/vga_pci.c optional pci dev/pcic/i82365.c optional pcic pccard @@ -1098,8 +1099,6 @@ pci/meteor.c count meteor pci pci/ncr.c optional ncr pci/ohci_pci.c optional ohci dev/pccbb/pccbb.c optional pccbb -pci/pci_compat.c optional pci compat_oldpci \ - warning "Old PCI driver compatability shims present." pci/pcic_p.c optional pcic pci card pci/simos.c optional simos pci/uhci_pci.c optional uhci diff --git a/sys/conf/options b/sys/conf/options index eb53814b571..098289eb043 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -381,7 +381,6 @@ MSGBUF_SIZE opt_msgbuf.h # PCI related options PCI_QUIET opt_pci.h -COMPAT_OLDPCI # NFS options NFS_MINATTRTIMO opt_nfs.h diff --git a/sys/dev/pci/eisa_pci.c b/sys/dev/pci/eisa_pci.c index b44f57217b5..6572801859d 100644 --- a/sys/dev/pci/eisa_pci.c +++ b/sys/dev/pci/eisa_pci.c @@ -90,7 +90,7 @@ eisab_probe(device_t dev) * Some bridges don't correctly report their class. */ switch (pci_get_devid(dev)) { - case 0x04828086: /* reports PCI-HOST class (!) */ + case 0x04828086: /* may show up as PCI-HOST or 0:0 */ matched = 1; break; default: @@ -107,21 +107,20 @@ eisab_probe(device_t dev) static int eisab_attach(device_t dev) { - device_t child; - /* * Attach an EISA bus. Note that we can only have one EISA bus. */ - child = device_add_child(dev, "eisa", 0); - if (child != NULL) - bus_generic_attach(dev); + if (!devclass_get_device(devclass_find("eisa"), 0)) + device_add_child(dev, "eisa", -1); /* - * Attach an ISA bus as well (should this be a child of EISA?) + * Attach an ISA bus as well, since the EISA bus may have ISA + * cards installed, and we may have no EISA support in the system. */ - child = device_add_child(dev, "isa", 0); - if (child != NULL) - bus_generic_attach(dev); + if (!devclass_get_device(devclass_find("isa"), 0)) + device_add_child(dev, "isa", -1); + + bus_generic_attach(dev); return(0); } diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index a609e66030e..49eed85f967 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -1,5 +1,7 @@ /* * Copyright (c) 1997, Stefan Esser + * Copyright (c) 2000, Michael Smith + * Copyright (c) 2000, BSDi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -54,12 +56,98 @@ #include #include "pcib_if.h" +#include "pci_if.h" + +static u_int32_t pci_mapbase(unsigned mapreg); +static int pci_maptype(unsigned mapreg); +static int pci_mapsize(unsigned testval); +static int pci_maprange(unsigned mapreg); +static void pci_fixancient(pcicfgregs *cfg); +static void *pci_readpcb(device_t pcib, int b, int s, int f); +static void pci_hdrtypedata(device_t pcib, int b, int s, int f, + pcicfgregs *cfg); +static struct pci_devinfo *pci_read_device(device_t pcib, int b, int s, int f); +static void pci_read_extcap(device_t pcib, pcicfgregs *cfg); + +static void pci_print_verbose(struct pci_devinfo *dinfo); +static int pci_porten(device_t pcib, int b, int s, int f); +static int pci_memen(device_t pcib, int b, int s, int f); +static int pci_add_map(device_t pcib, int b, int s, int f, int reg, + struct resource_list *rl); +static void pci_add_resources(device_t pcib, int b, int s, int f, + device_t dev); +static void pci_add_children(device_t dev, int busno); +static int pci_probe(device_t dev); +static int pci_print_resources(struct resource_list *rl, + const char *name, int type, + const char *format); +static int pci_print_child(device_t dev, device_t child); +static void pci_probe_nomatch(device_t dev, device_t child); +static int pci_describe_parse_line(char **ptr, int *vendor, + int *device, char **desc); +static char *pci_describe_device(device_t dev); +static int pci_read_ivar(device_t dev, device_t child, int which, + uintptr_t *result); +static int pci_write_ivar(device_t dev, device_t child, int which, + uintptr_t value); +static struct resource *pci_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 void pci_delete_resource(device_t dev, device_t child, + int type, int rid); +static struct resource_list *pci_get_resource_list (device_t dev, device_t child); +static u_int32_t pci_read_config_method(device_t dev, device_t child, + int reg, int width); +static void pci_write_config_method(device_t dev, device_t child, + int reg, u_int32_t val, int width); +static int pci_modevent(module_t mod, int what, void *arg); + +static device_method_t pci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pci_probe), + DEVMETHOD(device_attach, bus_generic_attach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* Bus interface */ + DEVMETHOD(bus_print_child, pci_print_child), + DEVMETHOD(bus_probe_nomatch, pci_probe_nomatch), + DEVMETHOD(bus_read_ivar, pci_read_ivar), + DEVMETHOD(bus_write_ivar, pci_write_ivar), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + DEVMETHOD(bus_get_resource_list,pci_get_resource_list), + DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), + DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), + DEVMETHOD(bus_delete_resource, pci_delete_resource), + DEVMETHOD(bus_alloc_resource, pci_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + + /* PCI interface */ + DEVMETHOD(pci_read_config, pci_read_config_method), + DEVMETHOD(pci_write_config, pci_write_config_method), + + { 0, 0 } +}; + +static driver_t pci_driver = { + "pci", + pci_methods, + 0, /* no softc */ +}; + +static devclass_t pci_devclass; +DRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, 0); +DRIVER_MODULE(pci, acpi_pcib, pci_driver, pci_devclass, pci_modevent, 0); static char *pci_vendordata; static size_t pci_vendordata_size; -static char *pci_describe_device(device_t dev); -static devclass_t pci_devclass; struct pci_quirk { u_int32_t devid; /* Vendor/device of the card */ @@ -83,9 +171,7 @@ struct pci_quirk pci_quirks[] = { #define PCI_MAPMEMP 0x02 /* prefetchable memory map */ #define PCI_MAPPORT 0x04 /* port map */ -static STAILQ_HEAD(devlist, pci_devinfo) pci_devq; u_int32_t pci_numdevs = 0; -static u_int32_t pci_generation = 0; /* return base address of memory or port map */ @@ -171,36 +257,6 @@ pci_fixancient(pcicfgregs *cfg) cfg->hdrtype = 1; } -/* read config data specific to header type 2 device (PCI to CardBus bridge) */ - -static void * -pci_readpcb(device_t pcib, int b, int s, int f) -{ - pcih2cfgregs *p; - - p = malloc(sizeof (pcih2cfgregs), M_DEVBUF, M_WAITOK | M_ZERO); - if (p == NULL) - return (NULL); - - p->secstat = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_SECSTAT_2, 2); - p->bridgectl = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_BRIDGECTL_2, 2); - - p->seclat = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_SECLAT_2, 1); - - p->membase0 = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_MEMBASE0_2, 4); - p->memlimit0 = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_MEMLIMIT0_2, 4); - p->membase1 = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_MEMBASE1_2, 4); - p->memlimit1 = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_MEMLIMIT1_2, 4); - - p->iobase0 = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_IOBASE0_2, 4); - p->iolimit0 = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_IOLIMIT0_2, 4); - p->iobase1 = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_IOBASE1_2, 4); - p->iolimit1 = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_IOLIMIT1_2, 4); - - p->pccardif = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_PCCARDIF_2, 4); - return p; -} - /* extract header type specific config data */ static void @@ -221,16 +277,13 @@ pci_hdrtypedata(device_t pcib, int b, int s, int f, pcicfgregs *cfg) case 2: cfg->subvendor = REG(PCIR_SUBVEND_2, 2); cfg->subdevice = REG(PCIR_SUBDEV_2, 2); - cfg->secondarybus = REG(PCIR_SECBUS_2, 1); - cfg->subordinatebus = REG(PCIR_SUBBUS_2, 1); cfg->nummaps = PCI_MAXMAPS_2; - cfg->hdrspec = pci_readpcb(pcib, b, s, f); break; } #undef REG } -/* read configuration header into pcicfgrect structure */ +/* read configuration header into pcicfgregs structure */ static struct pci_devinfo * pci_read_device(device_t pcib, int b, int s, int f) @@ -278,6 +331,9 @@ pci_read_device(device_t pcib, int b, int s, int f) pci_fixancient(cfg); pci_hdrtypedata(pcib, b, s, f, cfg); + if (REG(PCIR_STATUS, 2) & PCIM_STATUS_CAPPRESENT) + pci_read_extcap(pcib, cfg); + STAILQ_INSERT_TAIL(devlist_head, devlist_entry, pci_links); devlist_entry->conf.pc_sel.pc_bus = cfg->bus; @@ -302,6 +358,50 @@ pci_read_device(device_t pcib, int b, int s, int f) #undef REG } +static void +pci_read_extcap(device_t pcib, pcicfgregs *cfg) +{ +#define REG(n, w) PCIB_READ_CONFIG(pcib, 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) { + /* 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 +} + #if 0 /* free pcicfgregs structure and all depending data structures */ @@ -329,477 +429,147 @@ pci_freecfg(struct pci_devinfo *dinfo) } #endif - /* - * This is the user interface to PCI configuration space. + * PCI power manangement */ - -static int -pci_open(dev_t dev, int oflags, int devtype, struct proc *p) +int +pci_set_powerstate(device_t dev, int state) { - if ((oflags & FWRITE) && securelevel > 0) { - return EPERM; - } - return 0; -} - -static int -pci_close(dev_t dev, int flag, int devtype, struct proc *p) -{ - return 0; -} - -/* - * Match a single pci_conf structure against an array of pci_match_conf - * structures. The first argument, 'matches', is an array of num_matches - * pci_match_conf structures. match_buf is a pointer to the pci_conf - * structure that will be compared to every entry in the matches array. - * This function returns 1 on failure, 0 on success. - */ -static int -pci_conf_match(struct pci_match_conf *matches, int num_matches, - struct pci_conf *match_buf) -{ - int i; - - if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) - return(1); - - for (i = 0; i < num_matches; i++) { - /* - * I'm not sure why someone would do this...but... - */ - if (matches[i].flags == PCI_GETCONF_NO_MATCH) - continue; - - /* - * Look at each of the match flags. If it's set, do the - * comparison. If the comparison fails, we don't have a - * match, go on to the next item if there is one. - */ - if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) - && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) - continue; - - if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) - && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) - continue; - - if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) - && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) - continue; - - if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) - && (match_buf->pc_vendor != matches[i].pc_vendor)) - continue; - - if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) - && (match_buf->pc_device != matches[i].pc_device)) - continue; - - if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) - && (match_buf->pc_class != matches[i].pc_class)) - continue; - - if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) - && (match_buf->pd_unit != matches[i].pd_unit)) - continue; - - if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) - && (strncmp(matches[i].pd_name, match_buf->pd_name, - sizeof(match_buf->pd_name)) != 0)) - continue; - - return(0); - } - - return(1); -} - -/* - * Locate the parent of a PCI device by scanning the PCI devlist - * and return the entry for the parent. - * For devices on PCI Bus 0 (the host bus), this is the PCI Host. - * For devices on secondary PCI busses, this is that bus' PCI-PCI Bridge. - */ - -pcicfgregs * -pci_devlist_get_parent(pcicfgregs *cfg) -{ - struct devlist *devlist_head; - struct pci_devinfo *dinfo; - pcicfgregs *bridge_cfg; - int i; - - dinfo = STAILQ_FIRST(devlist_head = &pci_devq); - - /* If the device is on PCI bus 0, look for the host */ - if (cfg->bus == 0) { - for (i = 0; (dinfo != NULL) && (i < pci_numdevs); - dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { - bridge_cfg = &dinfo->cfg; - if (bridge_cfg->baseclass == PCIC_BRIDGE - && bridge_cfg->subclass == PCIS_BRIDGE_HOST - && bridge_cfg->bus == cfg->bus) { - return bridge_cfg; + struct pci_devinfo *dinfo = device_get_ivars(dev); + pcicfgregs *cfg = &dinfo->cfg; + u_int16_t status; + int result; + + if (cfg->pp_cap != 0) { + status = pci_read_config(dev, 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; } - } - } - - /* If the device is not on PCI bus 0, look for the PCI-PCI bridge */ - if (cfg->bus > 0) { - for (i = 0; (dinfo != NULL) && (i < pci_numdevs); - dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { - bridge_cfg = &dinfo->cfg; - if (bridge_cfg->baseclass == PCIC_BRIDGE - && bridge_cfg->subclass == PCIS_BRIDGE_PCI - && bridge_cfg->secondarybus == cfg->bus) { - return bridge_cfg; + 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(dev, cfg->pp_status, status, 2); + } else { + result = ENXIO; } - - return NULL; + return(result); } -static int -pci_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +int +pci_get_powerstate(device_t dev) { - device_t pci, pcib; - struct pci_io *io; - const char *name; - int error; - - if (!(flag & FWRITE)) - return EPERM; - - - switch(cmd) { - case PCIOCGETCONF: - { - struct pci_devinfo *dinfo; - struct pci_conf_io *cio; - struct devlist *devlist_head; - struct pci_match_conf *pattern_buf; - int num_patterns; - size_t iolen; - int ionum, i; - - cio = (struct pci_conf_io *)data; - - num_patterns = 0; - dinfo = NULL; - - /* - * Hopefully the user won't pass in a null pointer, but it - * can't hurt to check. - */ - if (cio == NULL) { - error = EINVAL; + struct pci_devinfo *dinfo = device_get_ivars(dev); + pcicfgregs *cfg = &dinfo->cfg; + u_int16_t status; + int result; + + if (cfg->pp_cap != 0) { + status = pci_read_config(dev, cfg->pp_status, 2); + switch (status & PCIM_PSTAT_DMASK) { + case PCIM_PSTAT_D0: + result = PCI_POWERSTATE_D0; break; - } - - /* - * If the user specified an offset into the device list, - * but the list has changed since they last called this - * ioctl, tell them that the list has changed. They will - * have to get the list from the beginning. - */ - if ((cio->offset != 0) - && (cio->generation != pci_generation)){ - cio->num_matches = 0; - cio->status = PCI_GETCONF_LIST_CHANGED; - error = 0; + case PCIM_PSTAT_D1: + result = PCI_POWERSTATE_D1; break; - } - - /* - * Check to see whether the user has asked for an offset - * past the end of our list. - */ - if (cio->offset >= pci_numdevs) { - cio->num_matches = 0; - cio->status = PCI_GETCONF_LAST_DEVICE; - error = 0; + case PCIM_PSTAT_D2: + result = PCI_POWERSTATE_D2; break; - } - - /* get the head of the device queue */ - devlist_head = &pci_devq; - - /* - * Determine how much room we have for pci_conf structures. - * Round the user's buffer size down to the nearest - * multiple of sizeof(struct pci_conf) in case the user - * didn't specify a multiple of that size. - */ - iolen = min(cio->match_buf_len - - (cio->match_buf_len % sizeof(struct pci_conf)), - pci_numdevs * sizeof(struct pci_conf)); - - /* - * Since we know that iolen is a multiple of the size of - * the pciconf union, it's okay to do this. - */ - ionum = iolen / sizeof(struct pci_conf); - - /* - * If this test is true, the user wants the pci_conf - * structures returned to match the supplied entries. - */ - if ((cio->num_patterns > 0) - && (cio->pat_buf_len > 0)) { - /* - * pat_buf_len needs to be: - * num_patterns * sizeof(struct pci_match_conf) - * While it is certainly possible the user just - * allocated a large buffer, but set the number of - * matches correctly, it is far more likely that - * their kernel doesn't match the userland utility - * they're using. It's also possible that the user - * forgot to initialize some variables. Yes, this - * may be overly picky, but I hazard to guess that - * it's far more likely to just catch folks that - * updated their kernel but not their userland. - */ - if ((cio->num_patterns * - sizeof(struct pci_match_conf)) != cio->pat_buf_len){ - /* The user made a mistake, return an error*/ - cio->status = PCI_GETCONF_ERROR; - printf("pci_ioctl: pat_buf_len %d != " - "num_patterns (%d) * sizeof(struct " - "pci_match_conf) (%d)\npci_ioctl: " - "pat_buf_len should be = %d\n", - cio->pat_buf_len, cio->num_patterns, - (int)sizeof(struct pci_match_conf), - (int)sizeof(struct pci_match_conf) * - cio->num_patterns); - printf("pci_ioctl: do your headers match your " - "kernel?\n"); - cio->num_matches = 0; - error = EINVAL; - break; - } - - /* - * Check the user's buffer to make sure it's readable. - */ - if (!useracc((caddr_t)cio->patterns, - cio->pat_buf_len, VM_PROT_READ)) { - printf("pci_ioctl: pattern buffer %p, " - "length %u isn't user accessible for" - " READ\n", cio->patterns, - cio->pat_buf_len); - error = EACCES; - break; - } - /* - * Allocate a buffer to hold the patterns. - */ - pattern_buf = malloc(cio->pat_buf_len, M_TEMP, - M_WAITOK); - error = copyin(cio->patterns, pattern_buf, - cio->pat_buf_len); - if (error != 0) - break; - num_patterns = cio->num_patterns; - - } else if ((cio->num_patterns > 0) - || (cio->pat_buf_len > 0)) { - /* - * The user made a mistake, spit out an error. - */ - cio->status = PCI_GETCONF_ERROR; - cio->num_matches = 0; - printf("pci_ioctl: invalid GETCONF arguments\n"); - error = EINVAL; + case PCIM_PSTAT_D3: + result = PCI_POWERSTATE_D3; break; - } else - pattern_buf = NULL; - - /* - * Make sure we can write to the match buffer. - */ - if (!useracc((caddr_t)cio->matches, - cio->match_buf_len, VM_PROT_WRITE)) { - printf("pci_ioctl: match buffer %p, length %u " - "isn't user accessible for WRITE\n", - cio->matches, cio->match_buf_len); - error = EACCES; + default: + result = PCI_POWERSTATE_UNKNOWN; break; } + } else { + /* No support, device is always at D0 */ + result = PCI_POWERSTATE_D0; + } + return(result); +} - /* - * Go through the list of devices and copy out the devices - * that match the user's criteria. - */ - for (cio->num_matches = 0, error = 0, i = 0, - dinfo = STAILQ_FIRST(devlist_head); - (dinfo != NULL) && (cio->num_matches < ionum) - && (error == 0) && (i < pci_numdevs); - dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { - - if (i < cio->offset) - continue; - - /* Populate pd_name and pd_unit */ - name = NULL; - if (dinfo->cfg.dev && dinfo->conf.pd_name[0] == '\0') - name = device_get_name(dinfo->cfg.dev); - if (name) { - strncpy(dinfo->conf.pd_name, name, - sizeof(dinfo->conf.pd_name)); - dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0; - dinfo->conf.pd_unit = - device_get_unit(dinfo->cfg.dev); - } - - if ((pattern_buf == NULL) || - (pci_conf_match(pattern_buf, num_patterns, - &dinfo->conf) == 0)) { - - /* - * If we've filled up the user's buffer, - * break out at this point. Since we've - * got a match here, we'll pick right back - * up at the matching entry. We can also - * tell the user that there are more matches - * left. - */ - if (cio->num_matches >= ionum) - break; - - error = copyout(&dinfo->conf, - &cio->matches[cio->num_matches], - sizeof(struct pci_conf)); - cio->num_matches++; - } - } - - /* - * Set the pointer into the list, so if the user is getting - * n records at a time, where n < pci_numdevs, - */ - cio->offset = i; +/* + * Some convenience functions for PCI device drivers. + */ - /* - * Set the generation, the user will need this if they make - * another ioctl call with offset != 0. - */ - cio->generation = pci_generation; - - /* - * If this is the last device, inform the user so he won't - * bother asking for more devices. If dinfo isn't NULL, we - * know that there are more matches in the list because of - * the way the traversal is done. - */ - if (dinfo == NULL) - cio->status = PCI_GETCONF_LAST_DEVICE; - else - cio->status = PCI_GETCONF_MORE_DEVS; +static __inline void +pci_set_command_bit(device_t dev, u_int16_t bit) +{ + u_int16_t command; - if (pattern_buf != NULL) - free(pattern_buf, M_TEMP); + command = pci_read_config(dev, PCIR_COMMAND, 2); + command |= bit; + pci_write_config(dev, PCIR_COMMAND, command, 2); +} - break; - } - case PCIOCREAD: - io = (struct pci_io *)data; - switch(io->pi_width) { - case 4: - case 2: - case 1: - /* - * Assume that the user-level bus number is - * actually the pciN instance number. We map - * from that to the real pcib+bus combination. - */ - pci = devclass_get_device(pci_devclass, - io->pi_sel.pc_bus); - if (pci) { - int b = pcib_get_bus(pci); - pcib = device_get_parent(pci); - io->pi_data = - PCIB_READ_CONFIG(pcib, - b, - io->pi_sel.pc_dev, - io->pi_sel.pc_func, - io->pi_reg, - io->pi_width); - error = 0; - } else { - error = ENODEV; - } - break; - default: - error = ENODEV; - break; - } - break; +static __inline void +pci_clear_command_bit(device_t dev, u_int16_t bit) +{ + u_int16_t command; - case PCIOCWRITE: - io = (struct pci_io *)data; - switch(io->pi_width) { - case 4: - case 2: - case 1: - /* - * Assume that the user-level bus number is - * actually the pciN instance number. We map - * from that to the real pcib+bus combination. - */ - pci = devclass_get_device(pci_devclass, - io->pi_sel.pc_bus); - if (pci) { - int b = pcib_get_bus(pci); - pcib = device_get_parent(pci); - PCIB_WRITE_CONFIG(pcib, - b, - io->pi_sel.pc_dev, - io->pi_sel.pc_func, - io->pi_reg, - io->pi_data, - io->pi_width); - error = 0; - } else { - error = ENODEV; - } - break; - default: - error = ENODEV; - break; - } - break; + command = pci_read_config(dev, PCIR_COMMAND, 2); + command &= ~bit; + pci_write_config(dev, PCIR_COMMAND, command, 2); +} - default: - error = ENOTTY; - break; - } +void +pci_enable_busmaster(device_t dev) +{ + pci_set_command_bit(dev, PCIM_CMD_BUSMASTEREN); +} - return (error); +void +pci_disable_busmaster(device_t dev) +{ + pci_clear_command_bit(dev, PCIM_CMD_BUSMASTEREN); } -#define PCI_CDEV 78 - -static struct cdevsw pcicdev = { - /* open */ pci_open, - /* close */ pci_close, - /* read */ noread, - /* write */ nowrite, - /* ioctl */ pci_ioctl, - /* poll */ nopoll, - /* mmap */ nommap, - /* strategy */ nostrategy, - /* name */ "pci", - /* maj */ PCI_CDEV, - /* dump */ nodump, - /* psize */ nopsize, - /* flags */ 0, - /* bmaj */ -1 -}; +void +pci_enable_io(device_t dev, int space) +{ + switch(space) { + case SYS_RES_IOPORT: + pci_set_command_bit(dev, PCIM_CMD_PORTEN); + break; + case SYS_RES_MEMORY: + pci_set_command_bit(dev, PCIM_CMD_MEMEN); + break; + } +} -#include "pci_if.h" +void +pci_disable_io(device_t dev, int space) +{ + switch(space) { + case SYS_RES_IOPORT: + pci_clear_command_bit(dev, PCIM_CMD_PORTEN); + break; + case SYS_RES_MEMORY: + pci_clear_command_bit(dev, PCIM_CMD_MEMEN); + break; + } +} /* * New style pci driver. Parent device is either a pci-host-bridge or a @@ -819,8 +589,6 @@ pci_print_verbose(struct pci_devinfo *dinfo) printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n", cfg->baseclass, cfg->subclass, cfg->progif, cfg->hdrtype, cfg->mfdev); - printf("\tsubordinatebus=%x \tsecondarybus=%x\n", - cfg->subordinatebus, cfg->secondarybus); #ifdef PCI_DEBUG printf("\tcmdreg=0x%04x, statreg=0x%04x, cachelnsz=%d (dwords)\n", cfg->cmdreg, cfg->statreg, cfg->cachelnsz); @@ -830,6 +598,16 @@ pci_print_verbose(struct pci_devinfo *dinfo) #endif /* PCI_DEBUG */ if (cfg->intpin > 0) printf("\tintpin=%c, irq=%d\n", cfg->intpin +'a' -1, cfg->intline); + if (cfg->pp_cap) { + u_int16_t status; + + status = pci_read_config(cfg->dev, cfg->pp_status, 2); + printf("\tpowerspec %d supports D0%s%s D3 current D%d\n", + cfg->pp_cap & PCIM_PCAP_SPEC, + cfg->pp_cap & PCIM_PCAP_D1SUPP ? " D1" : "", + cfg->pp_cap & PCIM_PCAP_D2SUPP ? " D2" : "", + status & PCIM_PSTAT_DMASK); + } } } @@ -970,13 +748,13 @@ pci_add_children(device_t dev, int busno) pci_read_device(pcib, busno, s, f); if (dinfo != NULL) { if (dinfo->cfg.mfdev) - pcifunchigh = 7; + pcifunchigh = PCI_FUNCMAX; - pci_print_verbose(dinfo); dinfo->cfg.dev = device_add_child(dev, NULL, -1); device_set_ivars(dinfo->cfg.dev, dinfo); pci_add_resources(pcib, busno, s, f, dinfo->cfg.dev); + pci_print_verbose(dinfo); } } } @@ -1301,7 +1079,6 @@ pci_describe_device(device_t dev) return(desc); } - static int pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { @@ -1354,12 +1131,6 @@ pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) case PCI_IVAR_FUNCTION: *result = cfg->func; break; - case PCI_IVAR_SECONDARYBUS: - *result = cfg->secondarybus; - break; - case PCI_IVAR_SUBORDINATEBUS: - *result = cfg->subordinatebus; - break; default: return ENOENT; } @@ -1392,12 +1163,6 @@ pci_write_ivar(device_t dev, device_t child, int which, uintptr_t value) case PCI_IVAR_FUNCTION: return EINVAL; /* disallow for now */ - case PCI_IVAR_SECONDARYBUS: - cfg->secondarybus = value; - break; - case PCI_IVAR_SUBORDINATEBUS: - cfg->subordinatebus = value; - break; default: return ENOENT; } @@ -1484,6 +1249,7 @@ pci_modevent(module_t mod, int what, void *arg) switch (what) { case MOD_LOAD: STAILQ_INIT(&pci_devq); + pci_generation = 0; break; case MOD_UNLOAD: @@ -1492,44 +1258,3 @@ pci_modevent(module_t mod, int what, void *arg) return 0; } - -static device_method_t pci_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, pci_probe), - DEVMETHOD(device_attach, bus_generic_attach), - DEVMETHOD(device_shutdown, bus_generic_shutdown), - DEVMETHOD(device_suspend, bus_generic_suspend), - DEVMETHOD(device_resume, bus_generic_resume), - - /* Bus interface */ - DEVMETHOD(bus_print_child, pci_print_child), - DEVMETHOD(bus_probe_nomatch, pci_probe_nomatch), - DEVMETHOD(bus_read_ivar, pci_read_ivar), - DEVMETHOD(bus_write_ivar, pci_write_ivar), - DEVMETHOD(bus_driver_added, bus_generic_driver_added), - DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), - DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), - - DEVMETHOD(bus_get_resource_list,pci_get_resource_list), - DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), - DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), - DEVMETHOD(bus_delete_resource, pci_delete_resource), - DEVMETHOD(bus_alloc_resource, pci_alloc_resource), - DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), - DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), - DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), - - /* PCI interface */ - DEVMETHOD(pci_read_config, pci_read_config_method), - DEVMETHOD(pci_write_config, pci_write_config_method), - - { 0, 0 } -}; - -static driver_t pci_driver = { - "pci", - pci_methods, - 0, /* no softc */ -}; -DRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, 0); -DRIVER_MODULE(pci, acpi_pcib, pci_driver, pci_devclass, pci_modevent, 0); diff --git a/sys/dev/pci/pci_pci.c b/sys/dev/pci/pci_pci.c index 6bcb15a3963..3980158f40e 100644 --- a/sys/dev/pci/pci_pci.c +++ b/sys/dev/pci/pci_pci.c @@ -52,12 +52,13 @@ struct pcib_softc { device_t dev; + u_int16_t command; /* command register */ u_int8_t secbus; /* secondary bus number */ u_int8_t subbus; /* subordinate bus number */ pci_addr_t pmembase; /* base address of prefetchable memory */ pci_addr_t pmemlimit; /* topmost address of prefetchable memory */ - u_int32_t membase; /* base address of memory window */ - u_int32_t memlimit; /* topmost address of memory window */ + pci_addr_t membase; /* base address of memory window */ + pci_addr_t memlimit; /* topmost address of memory window */ u_int32_t iobase; /* base address of port window */ u_int32_t iolimit; /* topmost address of port window */ u_int16_t secstat; /* secondary bus status register */ @@ -141,6 +142,7 @@ pcib_attach(device_t dev) /* * Get current bridge configuration. */ + sc->command = pci_read_config(dev, PCIR_COMMAND, 1); sc->secbus = pci_read_config(dev, PCIR_SECBUS_1, 1); sc->subbus = pci_read_config(dev, PCIR_SUBBUS_1, 1); sc->secstat = pci_read_config(dev, PCIR_SECSTAT_1, 2); @@ -150,31 +152,35 @@ pcib_attach(device_t dev) /* * Determine current I/O decode. */ - iolow = pci_read_config(dev, PCIR_IOBASEL_1, 1); - if ((iolow & PCIM_BRIO_MASK) == PCIM_BRIO_32) { - sc->iobase = PCI_PPBIOBASE(pci_read_config(dev, PCIR_IOBASEH_1, 2), - pci_read_config(dev, PCIR_IOBASEL_1, 1)); - } else { - sc->iobase = PCI_PPBIOBASE(0, pci_read_config(dev, PCIR_IOBASEL_1, 1)); - } + if (sc->command & PCIM_CMD_PORTEN) { + iolow = pci_read_config(dev, PCIR_IOBASEL_1, 1); + if ((iolow & PCIM_BRIO_MASK) == PCIM_BRIO_32) { + sc->iobase = PCI_PPBIOBASE(pci_read_config(dev, PCIR_IOBASEH_1, 2), + pci_read_config(dev, PCIR_IOBASEL_1, 1)); + } else { + sc->iobase = PCI_PPBIOBASE(0, pci_read_config(dev, PCIR_IOBASEL_1, 1)); + } - iolow = pci_read_config(dev, PCIR_IOLIMITL_1, 1); - if ((iolow & PCIM_BRIO_MASK) == PCIM_BRIO_32) { - sc->iolimit = PCI_PPBIOLIMIT(pci_read_config(dev, PCIR_IOLIMITH_1, 2), - pci_read_config(dev, PCIR_IOLIMITL_1, 1)); - } else { - sc->iolimit = PCI_PPBIOLIMIT(0, pci_read_config(dev, PCIR_IOLIMITL_1, 1)); + iolow = pci_read_config(dev, PCIR_IOLIMITL_1, 1); + if ((iolow & PCIM_BRIO_MASK) == PCIM_BRIO_32) { + sc->iolimit = PCI_PPBIOLIMIT(pci_read_config(dev, PCIR_IOLIMITH_1, 2), + pci_read_config(dev, PCIR_IOLIMITL_1, 1)); + } else { + sc->iolimit = PCI_PPBIOLIMIT(0, pci_read_config(dev, PCIR_IOLIMITL_1, 1)); + } } /* * Determine current memory decode. */ - sc->membase = PCI_PPBMEMBASE(0, pci_read_config(dev, PCIR_MEMBASE_1, 2)); - sc->memlimit = PCI_PPBMEMLIMIT(0, pci_read_config(dev, PCIR_MEMLIMIT_1, 2)); - sc->pmembase = PCI_PPBMEMBASE((pci_addr_t)pci_read_config(dev, PCIR_PMBASEH_1, 4), - pci_read_config(dev, PCIR_PMBASEL_1, 2)); - sc->pmemlimit = PCI_PPBMEMLIMIT((pci_addr_t)pci_read_config(dev, PCIR_PMLIMITH_1, 4), - pci_read_config(dev, PCIR_PMLIMITL_1, 2)); + if (sc->command & PCIM_CMD_MEMEN) { + sc->membase = PCI_PPBMEMBASE(0, pci_read_config(dev, PCIR_MEMBASE_1, 2)); + sc->memlimit = PCI_PPBMEMLIMIT(0, pci_read_config(dev, PCIR_MEMLIMIT_1, 2)); + sc->pmembase = PCI_PPBMEMBASE((pci_addr_t)pci_read_config(dev, PCIR_PMBASEH_1, 4), + pci_read_config(dev, PCIR_PMBASEL_1, 2)); + sc->pmemlimit = PCI_PPBMEMLIMIT((pci_addr_t)pci_read_config(dev, PCIR_PMLIMITH_1, 4), + pci_read_config(dev, PCIR_PMLIMITL_1, 2)); + } /* * Quirk handling. @@ -193,7 +199,6 @@ pcib_attach(device_t dev) break; } - if (bootverbose) { device_printf(dev, " secondary bus %d\n", sc->secbus); device_printf(dev, " subordinate bus %d\n", sc->subbus); diff --git a/sys/dev/pci/pci_user.c b/sys/dev/pci/pci_user.c new file mode 100644 index 00000000000..16990291538 --- /dev/null +++ b/sys/dev/pci/pci_user.c @@ -0,0 +1,487 @@ +/* + * Copyright (c) 1997, Stefan Esser + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#include "opt_bus.h" /* XXX trim includes */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "pcib_if.h" +#include "pci_if.h" + +/* + * This is the user interface to PCI configuration space. + */ + +static int pci_open(dev_t dev, int oflags, int devtype, struct proc *p); +static int pci_close(dev_t dev, int flag, int devtype, struct proc *p); +static int pci_conf_match(struct pci_match_conf *matches, int num_matches, + struct pci_conf *match_buf); +static int pci_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p); + +#define PCI_CDEV 78 + +struct cdevsw pcicdev = { + /* open */ pci_open, + /* close */ pci_close, + /* read */ noread, + /* write */ nowrite, + /* ioctl */ pci_ioctl, + /* poll */ nopoll, + /* mmap */ nommap, + /* strategy */ nostrategy, + /* name */ "pci", + /* maj */ PCI_CDEV, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ 0, + /* bmaj */ -1 +}; + +static int +pci_open(dev_t dev, int oflags, int devtype, struct proc *p) +{ + if ((oflags & FWRITE) && securelevel > 0) { + return EPERM; + } + return 0; +} + +static int +pci_close(dev_t dev, int flag, int devtype, struct proc *p) +{ + return 0; +} + +/* + * Match a single pci_conf structure against an array of pci_match_conf + * structures. The first argument, 'matches', is an array of num_matches + * pci_match_conf structures. match_buf is a pointer to the pci_conf + * structure that will be compared to every entry in the matches array. + * This function returns 1 on failure, 0 on success. + */ +static int +pci_conf_match(struct pci_match_conf *matches, int num_matches, + struct pci_conf *match_buf) +{ + int i; + + if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) + return(1); + + for (i = 0; i < num_matches; i++) { + /* + * I'm not sure why someone would do this...but... + */ + if (matches[i].flags == PCI_GETCONF_NO_MATCH) + continue; + + /* + * Look at each of the match flags. If it's set, do the + * comparison. If the comparison fails, we don't have a + * match, go on to the next item if there is one. + */ + if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) + && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) + && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) + && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) + && (match_buf->pc_vendor != matches[i].pc_vendor)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) + && (match_buf->pc_device != matches[i].pc_device)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) + && (match_buf->pc_class != matches[i].pc_class)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) + && (match_buf->pd_unit != matches[i].pd_unit)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) + && (strncmp(matches[i].pd_name, match_buf->pd_name, + sizeof(match_buf->pd_name)) != 0)) + continue; + + return(0); + } + + return(1); +} + +static int +pci_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + device_t pci, pcib; + struct pci_io *io; + const char *name; + int error; + + if (!(flag & FWRITE)) + return EPERM; + + + switch(cmd) { + case PCIOCGETCONF: + { + struct pci_devinfo *dinfo; + struct pci_conf_io *cio; + struct devlist *devlist_head; + struct pci_match_conf *pattern_buf; + int num_patterns; + size_t iolen; + int ionum, i; + + cio = (struct pci_conf_io *)data; + + num_patterns = 0; + dinfo = NULL; + + /* + * Hopefully the user won't pass in a null pointer, but it + * can't hurt to check. + */ + if (cio == NULL) { + error = EINVAL; + break; + } + + /* + * If the user specified an offset into the device list, + * but the list has changed since they last called this + * ioctl, tell them that the list has changed. They will + * have to get the list from the beginning. + */ + if ((cio->offset != 0) + && (cio->generation != pci_generation)){ + cio->num_matches = 0; + cio->status = PCI_GETCONF_LIST_CHANGED; + error = 0; + break; + } + + /* + * Check to see whether the user has asked for an offset + * past the end of our list. + */ + if (cio->offset >= pci_numdevs) { + cio->num_matches = 0; + cio->status = PCI_GETCONF_LAST_DEVICE; + error = 0; + break; + } + + /* get the head of the device queue */ + devlist_head = &pci_devq; + + /* + * Determine how much room we have for pci_conf structures. + * Round the user's buffer size down to the nearest + * multiple of sizeof(struct pci_conf) in case the user + * didn't specify a multiple of that size. + */ + iolen = min(cio->match_buf_len - + (cio->match_buf_len % sizeof(struct pci_conf)), + pci_numdevs * sizeof(struct pci_conf)); + + /* + * Since we know that iolen is a multiple of the size of + * the pciconf union, it's okay to do this. + */ + ionum = iolen / sizeof(struct pci_conf); + + /* + * If this test is true, the user wants the pci_conf + * structures returned to match the supplied entries. + */ + if ((cio->num_patterns > 0) + && (cio->pat_buf_len > 0)) { + /* + * pat_buf_len needs to be: + * num_patterns * sizeof(struct pci_match_conf) + * While it is certainly possible the user just + * allocated a large buffer, but set the number of + * matches correctly, it is far more likely that + * their kernel doesn't match the userland utility + * they're using. It's also possible that the user + * forgot to initialize some variables. Yes, this + * may be overly picky, but I hazard to guess that + * it's far more likely to just catch folks that + * updated their kernel but not their userland. + */ + if ((cio->num_patterns * + sizeof(struct pci_match_conf)) != cio->pat_buf_len){ + /* The user made a mistake, return an error*/ + cio->status = PCI_GETCONF_ERROR; + printf("pci_ioctl: pat_buf_len %d != " + "num_patterns (%d) * sizeof(struct " + "pci_match_conf) (%d)\npci_ioctl: " + "pat_buf_len should be = %d\n", + cio->pat_buf_len, cio->num_patterns, + (int)sizeof(struct pci_match_conf), + (int)sizeof(struct pci_match_conf) * + cio->num_patterns); + printf("pci_ioctl: do your headers match your " + "kernel?\n"); + cio->num_matches = 0; + error = EINVAL; + break; + } + + /* + * Check the user's buffer to make sure it's readable. + */ + if (!useracc((caddr_t)cio->patterns, + cio->pat_buf_len, VM_PROT_READ)) { + printf("pci_ioctl: pattern buffer %p, " + "length %u isn't user accessible for" + " READ\n", cio->patterns, + cio->pat_buf_len); + error = EACCES; + break; + } + /* + * Allocate a buffer to hold the patterns. + */ + pattern_buf = malloc(cio->pat_buf_len, M_TEMP, + M_WAITOK); + error = copyin(cio->patterns, pattern_buf, + cio->pat_buf_len); + if (error != 0) + break; + num_patterns = cio->num_patterns; + + } else if ((cio->num_patterns > 0) + || (cio->pat_buf_len > 0)) { + /* + * The user made a mistake, spit out an error. + */ + cio->status = PCI_GETCONF_ERROR; + cio->num_matches = 0; + printf("pci_ioctl: invalid GETCONF arguments\n"); + error = EINVAL; + break; + } else + pattern_buf = NULL; + + /* + * Make sure we can write to the match buffer. + */ + if (!useracc((caddr_t)cio->matches, + cio->match_buf_len, VM_PROT_WRITE)) { + printf("pci_ioctl: match buffer %p, length %u " + "isn't user accessible for WRITE\n", + cio->matches, cio->match_buf_len); + error = EACCES; + break; + } + + /* + * Go through the list of devices and copy out the devices + * that match the user's criteria. + */ + for (cio->num_matches = 0, error = 0, i = 0, + dinfo = STAILQ_FIRST(devlist_head); + (dinfo != NULL) && (cio->num_matches < ionum) + && (error == 0) && (i < pci_numdevs); + dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { + + if (i < cio->offset) + continue; + + /* Populate pd_name and pd_unit */ + name = NULL; + if (dinfo->cfg.dev && dinfo->conf.pd_name[0] == '\0') + name = device_get_name(dinfo->cfg.dev); + if (name) { + strncpy(dinfo->conf.pd_name, name, + sizeof(dinfo->conf.pd_name)); + dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0; + dinfo->conf.pd_unit = + device_get_unit(dinfo->cfg.dev); + } + + if ((pattern_buf == NULL) || + (pci_conf_match(pattern_buf, num_patterns, + &dinfo->conf) == 0)) { + + /* + * If we've filled up the user's buffer, + * break out at this point. Since we've + * got a match here, we'll pick right back + * up at the matching entry. We can also + * tell the user that there are more matches + * left. + */ + if (cio->num_matches >= ionum) + break; + + error = copyout(&dinfo->conf, + &cio->matches[cio->num_matches], + sizeof(struct pci_conf)); + cio->num_matches++; + } + } + + /* + * Set the pointer into the list, so if the user is getting + * n records at a time, where n < pci_numdevs, + */ + cio->offset = i; + + /* + * Set the generation, the user will need this if they make + * another ioctl call with offset != 0. + */ + cio->generation = pci_generation; + + /* + * If this is the last device, inform the user so he won't + * bother asking for more devices. If dinfo isn't NULL, we + * know that there are more matches in the list because of + * the way the traversal is done. + */ + if (dinfo == NULL) + cio->status = PCI_GETCONF_LAST_DEVICE; + else + cio->status = PCI_GETCONF_MORE_DEVS; + + if (pattern_buf != NULL) + free(pattern_buf, M_TEMP); + + break; + } + case PCIOCREAD: + io = (struct pci_io *)data; + switch(io->pi_width) { + case 4: + case 2: + case 1: + /* + * Assume that the user-level bus number is + * actually the pciN instance number. We map + * from that to the real pcib+bus combination. + */ + pci = devclass_get_device(devclass_find("pci"), + io->pi_sel.pc_bus); + if (pci) { + int b = pcib_get_bus(pci); + pcib = device_get_parent(pci); + io->pi_data = + PCIB_READ_CONFIG(pcib, + b, + io->pi_sel.pc_dev, + io->pi_sel.pc_func, + io->pi_reg, + io->pi_width); + error = 0; + } else { + error = ENODEV; + } + break; + default: + error = ENODEV; + break; + } + break; + + case PCIOCWRITE: + io = (struct pci_io *)data; + switch(io->pi_width) { + case 4: + case 2: + case 1: + /* + * Assume that the user-level bus number is + * actually the pciN instance number. We map + * from that to the real pcib+bus combination. + */ + pci = devclass_get_device(devclass_find("pci"), + io->pi_sel.pc_bus); + if (pci) { + int b = pcib_get_bus(pci); + pcib = device_get_parent(pci); + PCIB_WRITE_CONFIG(pcib, + b, + io->pi_sel.pc_dev, + io->pi_sel.pc_func, + io->pi_reg, + io->pi_data, + io->pi_width); + error = 0; + } else { + error = ENODEV; + } + break; + default: + error = ENODEV; + break; + } + break; + + default: + error = ENOTTY; + break; + } + + return (error); +} + diff --git a/sys/dev/pci/pcireg.h b/sys/dev/pci/pcireg.h index 35297e4eb9d..1e98f72065d 100644 --- a/sys/dev/pci/pcireg.h +++ b/sys/dev/pci/pcireg.h @@ -52,10 +52,26 @@ #define PCIR_COMMAND 0x04 #define PCIM_CMD_PORTEN 0x0001 #define PCIM_CMD_MEMEN 0x0002 -#define PCIM_CMD_MWRICEN 0x0010 #define PCIM_CMD_BUSMASTEREN 0x0004 +#define PCIM_CMD_SPECIALEN 0x0008 +#define PCIM_CMD_MWRICEN 0x0010 #define PCIM_CMD_PERRESPEN 0x0040 +#define PCIM_CMD_SERREN 0x0100 +#define PCIM_CMD_BACKTOBACK 0x0200 #define PCIR_STATUS 0x06 +#define PCIM_STATUS_CAPPRESENT 0x0010 +#define PCIM_STATUS_66CAPABLE 0x0020 +#define PCIM_STATUS_BACKTOBACK 0x0080 +#define PCIM_STATUS_PERRREPORT 0x0100 +#define PCIM_STATUS_SEL_FAST 0x0000 +#define PCIM_STATUS_SEL_MEDIMUM 0x0200 +#define PCIM_STATUS_SEL_SLOW 0x0400 +#define PCIM_STATUS_SEL_MASK 0x0600 +#define PCIM_STATUS_STABORT 0x0800 +#define PCIM_STATUS_RTABORT 0x1000 +#define PCIM_STATUS_RMABORT 0x2000 +#define PCIM_STATUS_SERR 0x4000 +#define PCIM_STATUS_PERR 0x8000 #define PCIR_REVID 0x08 #define PCIR_PROGIF 0x09 #define PCIR_SUBCLASS 0x0a @@ -81,7 +97,7 @@ /* config registers for header type 1 devices */ -#define PCIR_SECSTAT_1 0 /**/ +#define PCIR_SECSTAT_1 0x1e #define PCIR_PRIBUS_1 0x18 #define PCIR_SECBUS_1 0x19 @@ -227,38 +243,59 @@ #define PCIC_OTHER 0xff +/* PCI power manangement */ + +#define PCIR_POWER_CAP 0x2 +#define PCIM_PCAP_SPEC 0x0007 +#define PCIM_PCAP_PMEREQCLK 0x0008 +#define PCIM_PCAP_PMEREQPWR 0x0010 +#define PCIM_PCAP_DEVSPECINIT 0x0020 +#define PCIM_PCAP_DYNCLOCK 0x0040 +#define PCIM_PCAP_SECCLOCK 0x00c0 +#define PCIM_PCAP_CLOCKMASK 0x00c0 +#define PCIM_PCAP_REQFULLCLOCK 0x0100 +#define PCIM_PCAP_D1SUPP 0x0200 +#define PCIM_PCAP_D2SUPP 0x0400 +#define PCIM_PCAP_D0PME 0x1000 +#define PCIM_PCAP_D1PME 0x2000 +#define PCIM_PCAP_D2PME 0x4000 + +#define PCIR_POWER_STATUS 0x4 +#define PCIM_PSTAT_D0 0x0000 +#define PCIM_PSTAT_D1 0x0001 +#define PCIM_PSTAT_D2 0x0002 +#define PCIM_PSTAT_D3 0x0003 +#define PCIM_PSTAT_DMASK 0x0003 +#define PCIM_PSTAT_REPENABLE 0x0010 +#define PCIM_PSTAT_PMEENABLE 0x0100 +#define PCIM_PSTAT_D0POWER 0x0000 +#define PCIM_PSTAT_D1POWER 0x0200 +#define PCIM_PSTAT_D2POWER 0x0400 +#define PCIM_PSTAT_D3POWER 0x0600 +#define PCIM_PSTAT_D0HEAT 0x0800 +#define PCIM_PSTAT_D1HEAT 0x1000 +#define PCIM_PSTAT_D2HEAT 0x1200 +#define PCIM_PSTAT_D3HEAT 0x1400 +#define PCIM_PSTAT_DATAUNKN 0x0000 +#define PCIM_PSTAT_DATADIV10 0x2000 +#define PCIM_PSTAT_DATADIV100 0x4000 +#define PCIM_PSTAT_DATADIV1000 0x6000 +#define PCIM_PSTAT_DATADIVMASK 0x6000 +#define PCIM_PSTAT_PME 0x8000 + +#define PCIR_POWER_PMCSR 0x6 +#define PCIM_PMCSR_DCLOCK 0x10 +#define PCIM_PMCSR_B2SUPP 0x20 +#define PCIM_BMCSR_B3SUPP 0x40 +#define PCIM_BMCSR_BPCE 0x80 + +#define PCIR_POWER_DATA 0x7 + +#if 0 /* some PCI vendor definitions (only used to identify ancient devices !!! */ #define PCIV_INTEL 0x8086 #define PCID_INTEL_SATURN 0x0483 #define PCID_INTEL_ORION 0x84c4 - -/* for compatibility to FreeBSD-2.2 and 3.x versions of PCI code */ - -#if defined(_KERNEL) && !defined(KLD_MODULE) -#include "opt_compat_oldpci.h" #endif - -#ifdef COMPAT_OLDPCI - -#define PCI_ID_REG 0x00 -#define PCI_COMMAND_STATUS_REG 0x04 -#define PCI_COMMAND_IO_ENABLE 0x00000001 -#define PCI_COMMAND_MEM_ENABLE 0x00000002 -#define PCI_CLASS_REG 0x08 -#define PCI_CLASS_MASK 0xff000000 -#define PCI_SUBCLASS_MASK 0x00ff0000 -#define PCI_REVISION_MASK 0x000000ff -#define PCI_CLASS_PREHISTORIC 0x00000000 -#define PCI_SUBCLASS_PREHISTORIC_VGA 0x00010000 -#define PCI_CLASS_MASS_STORAGE 0x01000000 -#define PCI_CLASS_DISPLAY 0x03000000 -#define PCI_SUBCLASS_DISPLAY_VGA 0x00000000 -#define PCI_CLASS_BRIDGE 0x06000000 -#define PCI_MAP_REG_START 0x10 -#define PCI_MAP_REG_END 0x28 -#define PCI_MAP_IO 0x00000001 -#define PCI_INTERRUPT_REG 0x3c - -#endif /* COMPAT_OLDPCI */ diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h index 4ac4dd13832..5ab5d13fc09 100644 --- a/sys/dev/pci/pcivar.h +++ b/sys/dev/pci/pcivar.h @@ -55,7 +55,6 @@ typedef u_int32_t pci_addr_t; /* u_int64_t for system with 64bit addresses */ typedef struct pcicfg { struct device *dev; /* device which owns this */ - void *hdrspec; /* pointer to header type specific data */ u_int16_t subvendor; /* card vendor ID */ u_int16_t subdevice; /* card device ID, assigned by card vendor */ @@ -86,8 +85,11 @@ typedef struct pcicfg { u_int8_t slot; /* config space slot address */ u_int8_t func; /* config space function number */ - u_int8_t secondarybus; /* bus on secondary side of bridge, if any */ - u_int8_t subordinatebus; /* topmost bus number behind bridge, if any */ + u_int16_t pp_cap; /* PCI power management capabilities */ + u_int8_t pp_status; /* config space address of PCI power status reg */ + u_int8_t pp_pmcsr; /* config space address of PMCSR reg */ + u_int8_t pp_data; /* config space address of PCI power data reg */ + } pcicfgregs; /* additional type 1 device config header information (PCI to PCI bridge) */ @@ -144,19 +146,11 @@ struct pci_devinfo { }; #endif -/* low level PCI config register functions provided by pcibus.c */ - -int pci_cfgread (pcicfgregs *cfg, int reg, int bytes); -void pci_cfgwrite (pcicfgregs *cfg, int reg, int data, int bytes); - #ifdef __alpha__ vm_offset_t pci_cvt_to_dense (vm_offset_t); vm_offset_t pci_cvt_to_bwx (vm_offset_t); #endif /* __alpha__ */ -/* low level devlist operations for the 2.2 compatibility code in pci.c */ -pcicfgregs * pci_devlist_get_parent(pcicfgregs *cfg); - #ifdef _SYS_BUS_H_ #include "pci_if.h" @@ -169,22 +163,20 @@ pcicfgregs * pci_devlist_get_parent(pcicfgregs *cfg); #define PCI_RF_BWX 0x20000 enum pci_device_ivars { - PCI_IVAR_SUBVENDOR, - PCI_IVAR_SUBDEVICE, - PCI_IVAR_VENDOR, - PCI_IVAR_DEVICE, - PCI_IVAR_DEVID, - PCI_IVAR_CLASS, - PCI_IVAR_SUBCLASS, - PCI_IVAR_PROGIF, - PCI_IVAR_REVID, - PCI_IVAR_INTPIN, - PCI_IVAR_IRQ, - PCI_IVAR_BUS, - PCI_IVAR_SLOT, - PCI_IVAR_FUNCTION, - PCI_IVAR_SECONDARYBUS, - PCI_IVAR_SUBORDINATEBUS, + PCI_IVAR_SUBVENDOR, + PCI_IVAR_SUBDEVICE, + PCI_IVAR_VENDOR, + PCI_IVAR_DEVICE, + PCI_IVAR_DEVID, + PCI_IVAR_CLASS, + PCI_IVAR_SUBCLASS, + PCI_IVAR_PROGIF, + PCI_IVAR_REVID, + PCI_IVAR_INTPIN, + PCI_IVAR_IRQ, + PCI_IVAR_BUS, + PCI_IVAR_SLOT, + PCI_IVAR_FUNCTION, }; /* @@ -194,15 +186,15 @@ enum pci_device_ivars { \ static __inline T pci_get_ ## A(device_t dev) \ { \ - uintptr_t v; \ - BUS_READ_IVAR(device_get_parent(dev), dev, PCI_IVAR_ ## B, &v); \ - return (T) v; \ + uintptr_t v; \ + BUS_READ_IVAR(device_get_parent(dev), dev, PCI_IVAR_ ## B, &v); \ + return (T) v; \ } \ \ static __inline void pci_set_ ## A(device_t dev, T t) \ { \ - uintptr_t v = (uintptr_t) t; \ - BUS_WRITE_IVAR(device_get_parent(dev), dev, PCI_IVAR_ ## B, v); \ + uintptr_t v = (uintptr_t) t; \ + BUS_WRITE_IVAR(device_get_parent(dev), dev, PCI_IVAR_ ## B, v); \ } PCI_ACCESSOR(subvendor, SUBVENDOR, u_int16_t) @@ -219,11 +211,12 @@ PCI_ACCESSOR(irq, IRQ, u_int8_t) PCI_ACCESSOR(bus, BUS, u_int8_t) PCI_ACCESSOR(slot, SLOT, u_int8_t) PCI_ACCESSOR(function, FUNCTION, u_int8_t) -PCI_ACCESSOR(secondarybus, SECONDARYBUS, u_int8_t) -PCI_ACCESSOR(subordinatebus, SUBORDINATEBUS, u_int8_t) #undef PCI_ACCESSOR +/* + * Operations on configuration space. + */ static __inline u_int32_t pci_read_config(device_t dev, int reg, int width) { @@ -249,77 +242,67 @@ enum pcib_device_ivars { \ static __inline T pcib_get_ ## A(device_t dev) \ { \ - uintptr_t v; \ - BUS_READ_IVAR(device_get_parent(dev), dev, PCIB_IVAR_ ## B, &v); \ - return (T) v; \ + uintptr_t v; \ + BUS_READ_IVAR(device_get_parent(dev), dev, PCIB_IVAR_ ## B, &v); \ + return (T) v; \ } \ \ static __inline void pcib_set_ ## A(device_t dev, T t) \ { \ - uintptr_t v = (uintptr_t) t; \ - BUS_WRITE_IVAR(device_get_parent(dev), dev, PCIB_IVAR_ ## B, v); \ + uintptr_t v = (uintptr_t) t; \ + BUS_WRITE_IVAR(device_get_parent(dev), dev, PCIB_IVAR_ ## B, v); \ } PCIB_ACCESSOR(bus, BUS, u_int32_t) #undef PCIB_ACCESSOR -#endif - -/* for compatibility to FreeBSD-2.2 and 3.x versions of PCI code */ - -#if defined(_KERNEL) && !defined(KLD_MODULE) -#include "opt_compat_oldpci.h" -#endif - -#ifdef COMPAT_OLDPCI - -/* all this is going some day */ - -typedef pcicfgregs *pcici_t; -typedef unsigned pcidi_t; -typedef void pci_inthand_t(void *arg); +/* + * Convenience functions. + * + * These should be used in preference to manually manipulating + * configuration space. + */ +extern void pci_enable_busmaster(device_t dev); +extern void pci_disable_busmaster(device_t dev); +extern void pci_enable_io(device_t dev, int space); +extern void pci_disable_io(device_t dev, int space); -#define pci_max_burst_len (3) +/* + * PCI power states are as defined by ACPI: + * + * D0 State in which device is on and running. It is receiving full + * power from the system and delivering full functionality to the user. + * D1 Class-specific low-power state in which device context may or may not + * be lost. Buses in D1 cannot do anything to the bus that would force + * devices on that bus to loose context. + * D2 Class-specific low-power state in which device context may or may + * not be lost. Attains greater power savings than D1. Buses in D2 + * can cause devices on that bus to loose some context. Devices in D2 + * must be prepared for the bus to be in D2 or higher. + * D3 State in which the device is off and not running. Device context is + * lost. Power can be removed from the device. + */ +#define PCI_POWERSTATE_D0 0 +#define PCI_POWERSTATE_D1 1 +#define PCI_POWERSTATE_D2 2 +#define PCI_POWERSTATE_D3 3 +#define PCI_POWERSTATE_UNKNOWN -1 -/* just copied from old PCI code for now ... */ +extern int pci_set_powerstate(device_t dev, int state); +extern int pci_get_powerstate(device_t dev); -struct pci_device { - char* pd_name; - const char* (*pd_probe ) (pcici_t tag, pcidi_t type); - void (*pd_attach) (pcici_t tag, int unit); - u_long *pd_count; - int (*pd_shutdown) (int, int); -}; +#endif /* _SYS_BUS_H_ */ -#ifdef __i386__ -typedef u_short pci_port_t; -#else -typedef u_int pci_port_t; -#endif +/* + * cdev switch for control device, initialised in generic PCI code + */ +extern struct cdevsw pcicdev; -u_long pci_conf_read (pcici_t tag, u_long reg); -void pci_conf_write (pcici_t tag, u_long reg, u_long data); -int pci_map_port (pcici_t tag, u_long reg, pci_port_t* pa); -int pci_map_mem (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa); -int pci_map_int (pcici_t tag, pci_inthand_t *handler, void *arg, - intrmask_t *maskptr); -int pci_map_int_right(pcici_t cfg, pci_inthand_t *handler, void *arg, - intrmask_t *maskptr, u_int flags); -int pci_unmap_int (pcici_t tag); - -pcici_t pci_get_parent_from_tag(pcici_t tag); -int pci_get_bus_from_tag(pcici_t tag); - -struct module; -int compat_pci_handler (struct module *, int, void *); -#define COMPAT_PCI_DRIVER(name, pcidata) \ -static moduledata_t name##_mod = { \ - #name, \ - compat_pci_handler, \ - &pcidata \ -}; \ -DECLARE_MODULE(name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_ANY) -#endif /* COMPAT_OLDPCI */ +/* + * List of all PCI devices, generation count for the list. + */ +STAILQ_HEAD(devlist, pci_devinfo) pci_devq; +u_int32_t pci_generation; #endif /* _PCIVAR_H_ */ diff --git a/sys/pci/pci_compat.c b/sys/pci/pci_compat.c deleted file mode 100644 index 401680db22b..00000000000 --- a/sys/pci/pci_compat.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (c) 1997, Stefan Esser - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice unmodified, this list of conditions, and the following - * disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ - * - */ - -#include "opt_bus.h" - -/* for compatibility to FreeBSD-2.2 and 3.x versions of PCI code */ - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#ifdef APIC_IO -#include -#endif - -/* ------------------------------------------------------------------------- */ - -u_long -pci_conf_read(pcici_t cfg, u_long reg) -{ - return (pci_read_config(cfg->dev, reg, 4)); -} - -void -pci_conf_write(pcici_t cfg, u_long reg, u_long data) -{ - pci_write_config(cfg->dev, reg, data, 4); -} - -int -pci_cfgread (pcicfgregs *cfg, int reg, int bytes) -{ - return (pci_read_config(cfg->dev, reg, bytes)); -} - -void -pci_cfgwrite (pcicfgregs *cfg, int reg, int data, int bytes) -{ - pci_write_config(cfg->dev, reg, data, bytes); -} - -int -pci_map_port(pcici_t cfg, u_long reg, pci_port_t* pa) -{ - int rid; - struct resource *res; - - rid = reg; - res = bus_alloc_resource(cfg->dev, SYS_RES_IOPORT, &rid, - 0, ~0, 1, RF_ACTIVE); - if (res) { - *pa = rman_get_start(res); - return (1); - } - return (0); -} - -int -pci_map_mem(pcici_t cfg, u_long reg, vm_offset_t* va, vm_offset_t* pa) -{ - int rid; - struct resource *res; - - rid = reg; - res = bus_alloc_resource(cfg->dev, SYS_RES_MEMORY, &rid, - 0, ~0, 1, RF_ACTIVE); - if (res) { - *pa = rman_get_start(res); - *va = (vm_offset_t) rman_get_virtual(res); - return (1); - } - return (0); -} - -int -pci_map_int(pcici_t cfg, pci_inthand_t *handler, void *arg, intrmask_t *maskptr) -{ - return (pci_map_int_right(cfg, handler, arg, maskptr, 0)); -} - -int -pci_map_int_right(pcici_t cfg, pci_inthand_t *handler, void *arg, - intrmask_t *maskptr, u_int intflags) -{ - int error; -#ifdef APIC_IO - int nextpin, muxcnt; -#endif - if (cfg->intpin != 0) { - int irq = cfg->intline; - int rid = 0; - struct resource *res; - int flags = 0; - int resflags = RF_SHAREABLE|RF_ACTIVE; - void *ih; - -#ifdef INTR_FAST - if (intflags & INTR_FAST) - flags |= INTR_FAST; - if (intflags & INTR_EXCL) - resflags &= ~RF_SHAREABLE; -#endif - - res = bus_alloc_resource(cfg->dev, SYS_RES_IRQ, &rid, - irq, irq, 1, resflags); - if (!res) { - printf("pci_map_int: can't allocate interrupt\n"); - return 0; - } - - /* - * This is ugly. Translate the mask into an interrupt type. - */ - if (maskptr == &tty_imask) - flags |= INTR_TYPE_TTY; - else if (maskptr == &bio_imask) - flags |= INTR_TYPE_BIO; - else if (maskptr == &net_imask) - flags |= INTR_TYPE_NET; - else if (maskptr == &cam_imask) - flags |= INTR_TYPE_CAM; - - error = BUS_SETUP_INTR(device_get_parent(cfg->dev), cfg->dev, - res, flags, handler, arg, &ih); - if (error != 0) - return 0; - -#ifdef NEW_BUS_PCI - /* - * XXX this apic stuff looks totally busted. It should - * move to the nexus code which actually registers the - * interrupt. - */ -#endif - -#ifdef APIC_IO - nextpin = next_apic_irq(irq); - - if (nextpin < 0) - return 1; - - /* - * Attempt handling of some broken mp tables. - * - * It's OK to yell (since the mp tables are broken). - * - * Hanging in the boot is not OK - */ - - muxcnt = 2; - nextpin = next_apic_irq(nextpin); - while (muxcnt < 5 && nextpin >= 0) { - muxcnt++; - nextpin = next_apic_irq(nextpin); - } - if (muxcnt >= 5) { - printf("bogus MP table, more than 4 IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n"); - return 0; - } - - printf("bogus MP table, %d IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n", muxcnt); - - nextpin = next_apic_irq(irq); - while (nextpin >= 0) { - rid = 0; - res = bus_alloc_resource(cfg->dev, SYS_RES_IRQ, &rid, - nextpin, nextpin, 1, - resflags); - if (!res) { - printf("pci_map_int: can't allocate extra interrupt\n"); - return 0; - } - error = BUS_SETUP_INTR(device_get_parent(cfg->dev), - cfg->dev, res, flags, - handler, arg, &ih); - if (error != 0) { - printf("pci_map_int: BUS_SETUP_INTR failed\n"); - return 0; - } - printf("Registered extra interrupt handler for int %d (in addition to int %d)\n", nextpin, irq); - nextpin = next_apic_irq(nextpin); - } -#endif - } - return (1); -} - -int -pci_unmap_int(pcici_t cfg) -{ - return (0); /* not supported, yet, since cfg doesn't know about idesc */ -} - -pcici_t -pci_get_parent_from_tag(pcici_t tag) -{ - return (pcici_t)pci_devlist_get_parent(tag); -} - -int -pci_get_bus_from_tag(pcici_t tag) -{ - return tag->bus; -} - -/* - * A simple driver to wrap the old pci driver mechanism for back-compat. - */ - -static int -pci_compat_probe(device_t dev) -{ - struct pci_device *dvp; - struct pci_devinfo *dinfo; - pcicfgregs *cfg; - const char *name; - int error; - - dinfo = device_get_ivars(dev); - cfg = &dinfo->cfg; - dvp = device_get_driver(dev)->priv; - - /* - * Do the wrapped probe. - */ - error = ENXIO; - if (dvp && dvp->pd_probe) { - name = dvp->pd_probe(cfg, (cfg->device << 16) + cfg->vendor); - if (name) { - device_set_desc_copy(dev, name); - /* Allow newbus drivers to match "better" */ - error = -200; - } - } - - return error; -} - -static int -pci_compat_attach(device_t dev) -{ - struct pci_device *dvp; - struct pci_devinfo *dinfo; - pcicfgregs *cfg; - int unit; - - dinfo = device_get_ivars(dev); - cfg = &dinfo->cfg; - dvp = device_get_driver(dev)->priv; - - unit = device_get_unit(dev); - if (unit > *dvp->pd_count) - *dvp->pd_count = unit; - if (dvp->pd_attach) - dvp->pd_attach(cfg, unit); - device_printf(dev, "driver is using old-style compatability shims\n"); - return 0; -} - -static device_method_t pci_compat_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, pci_compat_probe), - DEVMETHOD(device_attach, pci_compat_attach), - - { 0, 0 } -}; - -/* - * Create a new style driver around each old pci driver. - */ -int -compat_pci_handler(module_t mod, int type, void *data) -{ - struct pci_device *dvp = (struct pci_device *)data; - driver_t *driver; - devclass_t pci_devclass = devclass_find("pci"); - - switch (type) { - case MOD_LOAD: - driver = malloc(sizeof(driver_t), M_DEVBUF, M_NOWAIT | M_ZERO); - if (!driver) - return ENOMEM; - driver->name = dvp->pd_name; - driver->methods = pci_compat_methods; - driver->size = sizeof(struct pci_devinfo *); - driver->priv = dvp; - devclass_add_driver(pci_devclass, driver); - break; - case MOD_UNLOAD: - printf("%s: module unload not supported!\n", dvp->pd_name); - return EOPNOTSUPP; - default: - break; - } - return 0; -} -- 2.45.2