diff options
Diffstat (limited to 'src')
-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; } |