diff options
author | Henry Zhao <henry.zhao@oracle.com> | 2011-11-14 18:53:21 -0800 |
---|---|---|
committer | Alan Coopersmith <alan.coopersmith@oracle.com> | 2011-11-17 19:12:01 -0800 |
commit | cfae4096835fe569edb03cd12d4580fc912a0e61 (patch) | |
tree | 9376adc2003611f9598d93c2c45b548b5d56aa6b | |
parent | 78eed07d599ff9e30c075aa7c8d1795e125ffc4b (diff) | |
download | libpciaccess-cfae4096835fe569edb03cd12d4580fc912a0e61.tar.gz libpciaccess-cfae4096835fe569edb03cd12d4580fc912a0e61.tar.bz2 libpciaccess-cfae4096835fe569edb03cd12d4580fc912a0e61.zip |
Solaris: improve support for sparc platform
(1) added prom property retrieval on sparc
(2) added multiple domain support on sparc
(3) use kernel device as mapping device
(4) performance improvements by removing redundant ioctl
Signed-off-by: Henry Zhao <henry.zhao@oracle.com>
Reviewed-by: Alan Coopersmith <alan.coopersmith@oracle.com>
Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>
-rw-r--r-- | src/solx_devfs.c | 265 |
1 files changed, 217 insertions, 48 deletions
diff --git a/src/solx_devfs.c b/src/solx_devfs.c index ea91479..b668318 100644 --- a/src/solx_devfs.c +++ b/src/solx_devfs.c @@ -66,6 +66,11 @@ typedef struct nexus { char *path; /* for errors/debugging; fd is all we need */ char *dev_path; struct nexus *next; +#ifdef __sparc + struct pci_device **devlist; + volatile size_t num_allocated_elems; + volatile size_t num_devices; +#endif } nexus_t; typedef struct probe_info { @@ -75,7 +80,13 @@ typedef struct probe_info { } probe_info_t; static nexus_t *nexus_list = NULL; +#if !defined(__sparc) static int xsvc_fd = -1; +#endif + +#ifdef __sparc +static di_prom_handle_t di_phdl; +#endif /* * Read config space in native processor endianness. Endian-neutral @@ -91,6 +102,10 @@ static int xsvc_fd = -1; # error "ISA is neither __sparc nor __x86" #endif +#ifdef __sparc +#define MAPPING_DEV_PATH(dev) (((struct pci_device_private *) dev)->device_string) +#endif + /* * Identify problematic southbridges. These have device id 0x5249 and * vendor id 0x10b9. Check for revision ID 0 and class code 060400 as well. @@ -153,6 +168,22 @@ static const struct pci_system_methods solx_devfs_methods = { .fill_capabilities = pci_fill_capabilities_generic }; +#ifdef __sparc +static nexus_t * +find_nexus_for_dev(struct pci_device *dev) +{ + nexus_t *nexus; + int i; + + for (nexus = nexus_list ; nexus != NULL ; nexus = nexus->next) { + for (i = 0; i < nexus->num_devices; i++) { + if (nexus->devlist[i] == dev) + return nexus; + } + } + return NULL; +} +#else static nexus_t * find_nexus_for_bus( int domain, int bus ) { @@ -166,6 +197,7 @@ find_nexus_for_bus( int domain, int bus ) } return NULL; } +#endif #define GET_CONFIG_VAL_8(offset) (config_hdr.bytes[offset]) #define GET_CONFIG_VAL_16(offset) \ @@ -195,14 +227,32 @@ pci_system_solx_devfs_destroy( void ) close(nexus->fd); free(nexus->path); free(nexus->dev_path); +#ifdef __sparc + { + struct pci_device *dev; + int i; + + for (i = 0; i < nexus->num_devices; i++) { + dev = nexus->devlist[i]; + if (MAPPING_DEV_PATH(dev)) + di_devfs_path_free((char *) MAPPING_DEV_PATH(dev)); + } + } + free(nexus->devlist); +#endif free(nexus); } nexus_list = NULL; +#ifdef __sparc + if (di_phdl != DI_PROM_HANDLE_NIL) + (void) di_prom_fini(di_phdl); +#else if (xsvc_fd >= 0) { close(xsvc_fd); xsvc_fd = -1; } +#endif } /* @@ -235,10 +285,16 @@ pci_system_solx_devfs_create( void ) return (err); } +#ifdef __sparc + if ((di_phdl = di_prom_init()) == DI_PROM_HANDLE_NIL) + (void) fprintf(stderr, "di_prom_init failed: %s\n", strerror(errno)); +#endif + pinfo.num_allocated_elems = INITIAL_NUM_DEVICES; pinfo.num_devices = 0; pinfo.devices = devices; (void) di_walk_minor(di_node, DDI_NT_REGACC, 0, &pinfo, probe_nexus_node); + di_fini(di_node); if ((pci_sys = calloc(1, sizeof (struct pci_system))) == NULL) { @@ -246,6 +302,7 @@ pci_system_solx_devfs_create( void ) free(devices); return (err); } + pci_sys->methods = &solx_devfs_methods; pci_sys->devices = pinfo.devices; pci_sys->num_devices = pinfo.num_devices; @@ -369,6 +426,10 @@ probe_dev(nexus_t *nexus, pcitool_reg_t *prg_p, probe_info_t *pinfo) else if (((errno != EFAULT) || (prg_p->status != PCITOOL_INVALID_ADDRESS)) && (prg_p->data != 0xffffffff)) { +#ifdef __sparc +/* on sparc, devices can be enumerated discontiguously. Do not quit */ + rval = 0; +#endif break; } @@ -435,6 +496,7 @@ probe_dev(nexus_t *nexus, pcitool_reg_t *prg_p, probe_info_t *pinfo) pci_base->device_id = GET_CONFIG_VAL_16(PCI_CONF_DEVID); pci_base->subvendor_id = GET_CONFIG_VAL_16(PCI_CONF_SUBVENID); pci_base->subdevice_id = GET_CONFIG_VAL_16(PCI_CONF_SUBSYSID); + pci_base->irq = GET_CONFIG_VAL_8(PCI_CONF_ILINE); pinfo->devices[pinfo->num_devices].header_type = GET_CONFIG_VAL_8(PCI_CONF_HEADER); @@ -466,6 +528,25 @@ probe_dev(nexus_t *nexus, pcitool_reg_t *prg_p, probe_info_t *pinfo) pinfo->devices = new_devs; } +#ifdef __sparc + nexus->devlist[nexus->num_devices++] = pci_base; + + if (nexus->num_devices == nexus->num_allocated_elems) { + struct pci_device **new_devs; + size_t new_num_elems = nexus->num_allocated_elems * 2; + + new_devs = realloc(nexus->devlist, + new_num_elems * sizeof (struct pci_device *)); + if (new_devs == NULL) + return (rval); + (void) memset(&new_devs[nexus->num_devices], 0, + nexus->num_allocated_elems * + sizeof (struct pci_device *)); + nexus->num_allocated_elems = new_num_elems; + nexus->devlist = new_devs; + } +#endif + /* * Accommodate devices which state their * multi-functionality only in their function 0 config @@ -500,6 +581,12 @@ probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg) int pci_node = 0; int first_bus = 0, last_bus = PCI_REG_BUS_G(PCI_REG_BUS_M); int domain = 0; +#ifdef __sparc + int bus_range_found = 0; + int device_type_found = 0; + di_prom_prop_t prom_prop; +#endif + #ifdef DEBUG nexus_name = di_devfs_minor_path(minor); @@ -517,11 +604,17 @@ probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg) if (strcmp(prop_name, "device_type") == 0) { numval = di_prop_strings(prop, &strings); - if (numval != 1 || strncmp(strings, "pci", 3) != 0) { - /* not a PCI node, bail */ - return (DI_WALK_CONTINUE); + if (numval == 1) { + if (strncmp(strings, "pci", 3) != 0) + /* not a PCI node, bail */ + return (DI_WALK_CONTINUE); + else { + pci_node = 1; +#ifdef __sparc + device_type_found = 1; +#endif + } } - pci_node = 1; } else if (strcmp(prop_name, "class-code") == 0) { /* not a root bus node, bail */ @@ -532,6 +625,9 @@ probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg) if (numval == 2) { first_bus = ints[0]; last_bus = ints[1]; +#ifdef __sparc + bus_range_found = 1; +#endif } } else if (strcmp(prop_name, "pciseg") == 0) { @@ -542,10 +638,30 @@ probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg) } } -#ifdef __x86 /* sparc pci nodes don't have the device_type set */ +#ifdef __sparc + if ((!device_type_found) && di_phdl) { + numval = di_prom_prop_lookup_strings(di_phdl, di_node, + "device_type", &strings); + if (numval == 1) { + if (strncmp(strings, "pci", 3) != 0) + return (DI_WALK_CONTINUE); + else + pci_node = 1; + } + } + + if ((!bus_range_found) && di_phdl) { + numval = di_prom_prop_lookup_ints(di_phdl, di_node, + "bus-range", &ints); + if (numval == 2) { + first_bus = ints[0]; + last_bus = ints[1]; + } + } +#endif + if (pci_node != 1) return (DI_WALK_CONTINUE); -#endif /* we have a PCI root bus node. */ nexus = calloc(1, sizeof(nexus_t)); @@ -558,6 +674,18 @@ probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg) nexus->last_bus = last_bus; nexus->domain = domain; +#ifdef __sparc + if ((nexus->devlist = calloc(INITIAL_NUM_DEVICES, + sizeof (struct pci_device *))) == NULL) { + (void) fprintf(stderr, "Error allocating memory for nexus devlist: %s\n", + strerror(errno)); + free (nexus); + return (DI_WALK_TERMINATE); + } + nexus->num_allocated_elems = INITIAL_NUM_DEVICES; + nexus->num_devices = 0; +#endif + nexus_name = di_devfs_minor_path(minor); if (nexus_name == NULL) { (void) fprintf(stderr, "Error getting nexus path: %s\n", @@ -690,6 +818,11 @@ find_target_node(di_node_t node, void *arg) len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", ®buf); +#ifdef __sparc + if ((len <= 0) && di_phdl) + len = di_prom_prop_lookup_ints(di_phdl, node, "reg", ®buf); +#endif + if (len <= 0) { #ifdef DEBUG fprintf(stderr, "error = %x\n", errno); @@ -719,59 +852,50 @@ find_target_node(di_node_t node, void *arg) static int pci_device_solx_devfs_probe( struct pci_device * dev ) { - uint8_t config[256]; - int err; + int err = 0; di_node_t rnode = DI_NODE_NIL; i_devnode_t args = { 0, 0, 0, DI_NODE_NIL }; int *regbuf; pci_regspec_t *reg; int i; - pciaddr_t bytes; int len = 0; uint ent = 0; - - err = pci_device_solx_devfs_read( dev, config, 0, 256, & bytes ); - - if ( bytes >= 64 ) { - struct pci_device_private *priv = - (struct pci_device_private *) dev; nexus_t *nexus; +#ifdef __sparc + if ( (nexus = find_nexus_for_dev(dev)) == NULL ) +#else if ( (nexus = find_nexus_for_bus(dev->domain, dev->bus)) == NULL ) +#endif return ENODEV; - dev->vendor_id = (uint16_t)config[0] + ((uint16_t)config[1] << 8); - dev->device_id = (uint16_t)config[2] + ((uint16_t)config[3] << 8); - dev->device_class = (uint32_t)config[9] + - ((uint32_t)config[10] << 8) + - ((uint16_t)config[11] << 16); - - /* - * device class code is already there. - * see probe_dev function. - */ - dev->revision = config[8]; - dev->subvendor_id = (uint16_t)config[44] + ((uint16_t)config[45] << 8); - dev->subdevice_id = (uint16_t)config[46] + ((uint16_t)config[47] << 8); - dev->irq = config[60]; - - priv->header_type = config[14]; - /* - * starting to find if it is MEM/MEM64/IO - * using libdevinfo - */ - if ((rnode = di_init(nexus->dev_path, DINFOCPYALL)) == DI_NODE_NIL) { - err = errno; - (void) fprintf(stderr, "di_init failed: %s\n", strerror(errno)); - } else { - args.bus = dev->bus; - args.dev = dev->dev; - args.func = dev->func; - (void) di_walk_node(rnode, DI_WALK_CLDFIRST, - (void *)&args, find_target_node); - } + /* + * starting to find if it is MEM/MEM64/IO + * using libdevinfo + */ + if ((rnode = di_init(nexus->dev_path, DINFOCPYALL)) == DI_NODE_NIL) { + err = errno; + (void) fprintf(stderr, "di_init failed: %s\n", strerror(errno)); + } else { + args.bus = dev->bus; + args.dev = dev->dev; + args.func = dev->func; + (void) di_walk_node(rnode, DI_WALK_CLDFIRST, + (void *)&args, find_target_node); } + if (args.node != DI_NODE_NIL) { +#ifdef __sparc + di_minor_t minor; +#endif + +#ifdef __sparc + if (minor = di_minor_next(args.node, DI_MINOR_NIL)) + MAPPING_DEV_PATH(dev) = di_devfs_minor_path (minor); + else + MAPPING_DEV_PATH(dev) = NULL; +#endif + /* * It will succeed for sure, because it was * successfully called in find_target_node @@ -780,6 +904,12 @@ pci_device_solx_devfs_probe( struct pci_device * dev ) "assigned-addresses", ®buf); +#ifdef __sparc + if ((len <= 0) && di_phdl) { + len = di_prom_prop_lookup_ints(di_phdl, args.node, + "assigned-addresses", ®buf); + } +#endif } if (len <= 0) @@ -907,7 +1037,13 @@ pci_device_solx_devfs_read( struct pci_device * dev, void * data, pcitool_reg_t cfg_prg; int err = 0; int i = 0; - nexus_t *nexus = find_nexus_for_bus(dev->domain, dev->bus); + nexus_t *nexus; + +#ifdef __sparc + nexus = find_nexus_for_dev(dev); +#else + nexus = find_nexus_for_bus(dev->domain, dev->bus); +#endif *bytes_read = 0; @@ -959,7 +1095,13 @@ pci_device_solx_devfs_write( struct pci_device * dev, const void * data, pcitool_reg_t cfg_prg; int err = 0; int cmd; - nexus_t *nexus = find_nexus_for_bus(dev->domain, dev->bus); + nexus_t *nexus; + +#ifdef __sparc + nexus = find_nexus_for_dev(dev); +#else + nexus = find_nexus_for_bus(dev->domain, dev->bus); +#endif if ( bytes_written != NULL ) { *bytes_written = 0; @@ -973,15 +1115,19 @@ pci_device_solx_devfs_write( struct pci_device * dev, const void * data, switch (size) { case 1: cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 + NATIVE_ENDIAN; + cfg_prg.data = *((uint8_t *)data); break; case 2: cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_2 + NATIVE_ENDIAN; + cfg_prg.data = *((uint16_t *)data); break; case 4: cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + NATIVE_ENDIAN; + cfg_prg.data = *((uint32_t *)data); break; case 8: cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 + NATIVE_ENDIAN; + cfg_prg.data = *((uint64_t *)data); break; default: return EINVAL; @@ -991,7 +1137,6 @@ pci_device_solx_devfs_write( struct pci_device * dev, const void * data, cfg_prg.func_no = dev->func; cfg_prg.barnum = 0; cfg_prg.user_version = PCITOOL_USER_VERSION; - cfg_prg.data = *((uint64_t *)data); /* * Check if this device is bridge device. @@ -1028,6 +1173,24 @@ pci_device_solx_devfs_map_range(struct pci_device *dev, ? (PROT_READ | PROT_WRITE) : PROT_READ; int err = 0; +#ifdef __sparc + char map_dev[128]; + int map_fd; + + if (MAPPING_DEV_PATH(dev)) + snprintf(map_dev, sizeof (map_dev), "%s%s", "/devices", MAPPING_DEV_PATH(dev)); + else + strcpy (map_dev, "/dev/fb0"); + + if ((map_fd = open(map_dev, O_RDWR)) < 0) { + err = errno; + (void) fprintf(stderr, "can not open %s: %s\n", map_dev, + strerror(errno)); + return err; + } + + map->memory = mmap(NULL, map->size, prot, MAP_SHARED, map_fd, map->base); +#else /* * Still used xsvc to do the user space mapping */ @@ -1041,6 +1204,8 @@ pci_device_solx_devfs_map_range(struct pci_device *dev, } map->memory = mmap(NULL, map->size, prot, MAP_SHARED, xsvc_fd, map->base); +#endif + if (map->memory == MAP_FAILED) { err = errno; @@ -1048,5 +1213,9 @@ pci_device_solx_devfs_map_range(struct pci_device *dev, map->base, strerror(errno)); } +#ifdef __sparc + close (map_fd); +#endif + return err; } |