162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file supports the /sys/firmware/sgi_uv topology tree on HPE UV. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2020 Hewlett Packard Enterprise. All Rights Reserved. 662306a36Sopenharmony_ci * Copyright (c) Justin Ernst 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/kobject.h> 1462306a36Sopenharmony_ci#include <asm/uv/bios.h> 1562306a36Sopenharmony_ci#include <asm/uv/uv.h> 1662306a36Sopenharmony_ci#include <asm/uv/uv_hub.h> 1762306a36Sopenharmony_ci#include <asm/uv/uv_geo.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define INVALID_CNODE -1 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct kobject *sgi_uv_kobj; 2262306a36Sopenharmony_cistatic struct kset *uv_pcibus_kset; 2362306a36Sopenharmony_cistatic struct kset *uv_hubs_kset; 2462306a36Sopenharmony_cistatic struct uv_bios_hub_info *hub_buf; 2562306a36Sopenharmony_cistatic struct uv_bios_port_info **port_buf; 2662306a36Sopenharmony_cistatic struct uv_hub **uv_hubs; 2762306a36Sopenharmony_cistatic struct uv_pci_top_obj **uv_pci_objs; 2862306a36Sopenharmony_cistatic int num_pci_lines; 2962306a36Sopenharmony_cistatic int num_cnodes; 3062306a36Sopenharmony_cistatic int *prev_obj_to_cnode; 3162306a36Sopenharmony_cistatic int uv_bios_obj_cnt; 3262306a36Sopenharmony_cistatic signed short uv_master_nasid = -1; 3362306a36Sopenharmony_cistatic void *uv_biosheap; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic const char *uv_type_string(void) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci if (is_uv5_hub()) 3862306a36Sopenharmony_ci return "9.0"; 3962306a36Sopenharmony_ci else if (is_uv4a_hub()) 4062306a36Sopenharmony_ci return "7.1"; 4162306a36Sopenharmony_ci else if (is_uv4_hub()) 4262306a36Sopenharmony_ci return "7.0"; 4362306a36Sopenharmony_ci else if (is_uv3_hub()) 4462306a36Sopenharmony_ci return "5.0"; 4562306a36Sopenharmony_ci else if (is_uv2_hub()) 4662306a36Sopenharmony_ci return "3.0"; 4762306a36Sopenharmony_ci else if (uv_get_hubless_system()) 4862306a36Sopenharmony_ci return "0.1"; 4962306a36Sopenharmony_ci else 5062306a36Sopenharmony_ci return "unknown"; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic int ordinal_to_nasid(int ordinal) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci if (ordinal < num_cnodes && ordinal >= 0) 5662306a36Sopenharmony_ci return UV_PNODE_TO_NASID(uv_blade_to_pnode(ordinal)); 5762306a36Sopenharmony_ci else 5862306a36Sopenharmony_ci return -1; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic union geoid_u cnode_to_geoid(int cnode) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci union geoid_u geoid; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci uv_bios_get_geoinfo(ordinal_to_nasid(cnode), (u64)sizeof(union geoid_u), (u64 *)&geoid); 6662306a36Sopenharmony_ci return geoid; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int location_to_bpos(char *location, int *rack, int *slot, int *blade) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci char type, r, b, h; 7262306a36Sopenharmony_ci int idb, idh; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (sscanf(location, "%c%03d%c%02d%c%2d%c%d", 7562306a36Sopenharmony_ci &r, rack, &type, slot, &b, &idb, &h, &idh) != 8) 7662306a36Sopenharmony_ci return -1; 7762306a36Sopenharmony_ci *blade = idb * 2 + idh; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int cache_obj_to_cnode(struct uv_bios_hub_info *obj) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci int cnode; 8562306a36Sopenharmony_ci union geoid_u geoid; 8662306a36Sopenharmony_ci int obj_rack, obj_slot, obj_blade; 8762306a36Sopenharmony_ci int rack, slot, blade; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (!obj->f.fields.this_part && !obj->f.fields.is_shared) 9062306a36Sopenharmony_ci return 0; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (location_to_bpos(obj->location, &obj_rack, &obj_slot, &obj_blade)) 9362306a36Sopenharmony_ci return -1; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci for (cnode = 0; cnode < num_cnodes; cnode++) { 9662306a36Sopenharmony_ci geoid = cnode_to_geoid(cnode); 9762306a36Sopenharmony_ci rack = geo_rack(geoid); 9862306a36Sopenharmony_ci slot = geo_slot(geoid); 9962306a36Sopenharmony_ci blade = geo_blade(geoid); 10062306a36Sopenharmony_ci if (obj_rack == rack && obj_slot == slot && obj_blade == blade) 10162306a36Sopenharmony_ci prev_obj_to_cnode[obj->id] = cnode; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int get_obj_to_cnode(int obj_id) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci return prev_obj_to_cnode[obj_id]; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistruct uv_hub { 11362306a36Sopenharmony_ci struct kobject kobj; 11462306a36Sopenharmony_ci struct uv_bios_hub_info *hub_info; 11562306a36Sopenharmony_ci struct uv_port **ports; 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define to_uv_hub(kobj_ptr) container_of(kobj_ptr, struct uv_hub, kobj) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic ssize_t hub_name_show(struct uv_bios_hub_info *hub_info, char *buf) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", hub_info->name); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic ssize_t hub_location_show(struct uv_bios_hub_info *hub_info, char *buf) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", hub_info->location); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic ssize_t hub_partition_show(struct uv_bios_hub_info *hub_info, char *buf) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci return sprintf(buf, "%d\n", hub_info->f.fields.this_part); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic ssize_t hub_shared_show(struct uv_bios_hub_info *hub_info, char *buf) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci return sprintf(buf, "%d\n", hub_info->f.fields.is_shared); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_cistatic ssize_t hub_nasid_show(struct uv_bios_hub_info *hub_info, char *buf) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci int cnode = get_obj_to_cnode(hub_info->id); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return sprintf(buf, "%d\n", ordinal_to_nasid(cnode)); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_cistatic ssize_t hub_cnode_show(struct uv_bios_hub_info *hub_info, char *buf) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci return sprintf(buf, "%d\n", get_obj_to_cnode(hub_info->id)); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistruct hub_sysfs_entry { 15162306a36Sopenharmony_ci struct attribute attr; 15262306a36Sopenharmony_ci ssize_t (*show)(struct uv_bios_hub_info *hub_info, char *buf); 15362306a36Sopenharmony_ci ssize_t (*store)(struct uv_bios_hub_info *hub_info, const char *buf, size_t sz); 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic struct hub_sysfs_entry name_attribute = 15762306a36Sopenharmony_ci __ATTR(name, 0444, hub_name_show, NULL); 15862306a36Sopenharmony_cistatic struct hub_sysfs_entry location_attribute = 15962306a36Sopenharmony_ci __ATTR(location, 0444, hub_location_show, NULL); 16062306a36Sopenharmony_cistatic struct hub_sysfs_entry partition_attribute = 16162306a36Sopenharmony_ci __ATTR(this_partition, 0444, hub_partition_show, NULL); 16262306a36Sopenharmony_cistatic struct hub_sysfs_entry shared_attribute = 16362306a36Sopenharmony_ci __ATTR(shared, 0444, hub_shared_show, NULL); 16462306a36Sopenharmony_cistatic struct hub_sysfs_entry nasid_attribute = 16562306a36Sopenharmony_ci __ATTR(nasid, 0444, hub_nasid_show, NULL); 16662306a36Sopenharmony_cistatic struct hub_sysfs_entry cnode_attribute = 16762306a36Sopenharmony_ci __ATTR(cnode, 0444, hub_cnode_show, NULL); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic struct attribute *uv_hub_attrs[] = { 17062306a36Sopenharmony_ci &name_attribute.attr, 17162306a36Sopenharmony_ci &location_attribute.attr, 17262306a36Sopenharmony_ci &partition_attribute.attr, 17362306a36Sopenharmony_ci &shared_attribute.attr, 17462306a36Sopenharmony_ci &nasid_attribute.attr, 17562306a36Sopenharmony_ci &cnode_attribute.attr, 17662306a36Sopenharmony_ci NULL, 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ciATTRIBUTE_GROUPS(uv_hub); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void hub_release(struct kobject *kobj) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct uv_hub *hub = to_uv_hub(kobj); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci kfree(hub); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic ssize_t hub_type_show(struct kobject *kobj, struct attribute *attr, 18862306a36Sopenharmony_ci char *buf) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct uv_hub *hub = to_uv_hub(kobj); 19162306a36Sopenharmony_ci struct uv_bios_hub_info *bios_hub_info = hub->hub_info; 19262306a36Sopenharmony_ci struct hub_sysfs_entry *entry; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci entry = container_of(attr, struct hub_sysfs_entry, attr); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (!entry->show) 19762306a36Sopenharmony_ci return -EIO; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return entry->show(bios_hub_info, buf); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic const struct sysfs_ops hub_sysfs_ops = { 20362306a36Sopenharmony_ci .show = hub_type_show, 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic const struct kobj_type hub_attr_type = { 20762306a36Sopenharmony_ci .release = hub_release, 20862306a36Sopenharmony_ci .sysfs_ops = &hub_sysfs_ops, 20962306a36Sopenharmony_ci .default_groups = uv_hub_groups, 21062306a36Sopenharmony_ci}; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic int uv_hubs_init(void) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci s64 biosr; 21562306a36Sopenharmony_ci u64 sz; 21662306a36Sopenharmony_ci int i, ret; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci prev_obj_to_cnode = kmalloc_array(uv_bios_obj_cnt, sizeof(*prev_obj_to_cnode), 21962306a36Sopenharmony_ci GFP_KERNEL); 22062306a36Sopenharmony_ci if (!prev_obj_to_cnode) 22162306a36Sopenharmony_ci return -ENOMEM; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci for (i = 0; i < uv_bios_obj_cnt; i++) 22462306a36Sopenharmony_ci prev_obj_to_cnode[i] = INVALID_CNODE; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci uv_hubs_kset = kset_create_and_add("hubs", NULL, sgi_uv_kobj); 22762306a36Sopenharmony_ci if (!uv_hubs_kset) { 22862306a36Sopenharmony_ci ret = -ENOMEM; 22962306a36Sopenharmony_ci goto err_hubs_kset; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci sz = uv_bios_obj_cnt * sizeof(*hub_buf); 23262306a36Sopenharmony_ci hub_buf = kzalloc(sz, GFP_KERNEL); 23362306a36Sopenharmony_ci if (!hub_buf) { 23462306a36Sopenharmony_ci ret = -ENOMEM; 23562306a36Sopenharmony_ci goto err_hub_buf; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci biosr = uv_bios_enum_objs((u64)uv_master_nasid, sz, (u64 *)hub_buf); 23962306a36Sopenharmony_ci if (biosr) { 24062306a36Sopenharmony_ci ret = -EINVAL; 24162306a36Sopenharmony_ci goto err_enum_objs; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci uv_hubs = kcalloc(uv_bios_obj_cnt, sizeof(*uv_hubs), GFP_KERNEL); 24562306a36Sopenharmony_ci if (!uv_hubs) { 24662306a36Sopenharmony_ci ret = -ENOMEM; 24762306a36Sopenharmony_ci goto err_enum_objs; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci for (i = 0; i < uv_bios_obj_cnt; i++) { 25162306a36Sopenharmony_ci uv_hubs[i] = kzalloc(sizeof(*uv_hubs[i]), GFP_KERNEL); 25262306a36Sopenharmony_ci if (!uv_hubs[i]) { 25362306a36Sopenharmony_ci i--; 25462306a36Sopenharmony_ci ret = -ENOMEM; 25562306a36Sopenharmony_ci goto err_hubs; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci uv_hubs[i]->hub_info = &hub_buf[i]; 25962306a36Sopenharmony_ci cache_obj_to_cnode(uv_hubs[i]->hub_info); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci uv_hubs[i]->kobj.kset = uv_hubs_kset; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci ret = kobject_init_and_add(&uv_hubs[i]->kobj, &hub_attr_type, 26462306a36Sopenharmony_ci NULL, "hub_%u", hub_buf[i].id); 26562306a36Sopenharmony_ci if (ret) 26662306a36Sopenharmony_ci goto err_hubs; 26762306a36Sopenharmony_ci kobject_uevent(&uv_hubs[i]->kobj, KOBJ_ADD); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cierr_hubs: 27262306a36Sopenharmony_ci for (; i >= 0; i--) 27362306a36Sopenharmony_ci kobject_put(&uv_hubs[i]->kobj); 27462306a36Sopenharmony_ci kfree(uv_hubs); 27562306a36Sopenharmony_cierr_enum_objs: 27662306a36Sopenharmony_ci kfree(hub_buf); 27762306a36Sopenharmony_cierr_hub_buf: 27862306a36Sopenharmony_ci kset_unregister(uv_hubs_kset); 27962306a36Sopenharmony_cierr_hubs_kset: 28062306a36Sopenharmony_ci kfree(prev_obj_to_cnode); 28162306a36Sopenharmony_ci return ret; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic void uv_hubs_exit(void) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci int i; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci for (i = 0; i < uv_bios_obj_cnt; i++) 29062306a36Sopenharmony_ci kobject_put(&uv_hubs[i]->kobj); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci kfree(uv_hubs); 29362306a36Sopenharmony_ci kfree(hub_buf); 29462306a36Sopenharmony_ci kset_unregister(uv_hubs_kset); 29562306a36Sopenharmony_ci kfree(prev_obj_to_cnode); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistruct uv_port { 29962306a36Sopenharmony_ci struct kobject kobj; 30062306a36Sopenharmony_ci struct uv_bios_port_info *port_info; 30162306a36Sopenharmony_ci}; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci#define to_uv_port(kobj_ptr) container_of(kobj_ptr, struct uv_port, kobj) 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic ssize_t uv_port_conn_hub_show(struct uv_bios_port_info *port, char *buf) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci return sprintf(buf, "%d\n", port->conn_id); 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic ssize_t uv_port_conn_port_show(struct uv_bios_port_info *port, char *buf) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci return sprintf(buf, "%d\n", port->conn_port); 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistruct uv_port_sysfs_entry { 31662306a36Sopenharmony_ci struct attribute attr; 31762306a36Sopenharmony_ci ssize_t (*show)(struct uv_bios_port_info *port_info, char *buf); 31862306a36Sopenharmony_ci ssize_t (*store)(struct uv_bios_port_info *port_info, const char *buf, size_t size); 31962306a36Sopenharmony_ci}; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic struct uv_port_sysfs_entry uv_port_conn_hub_attribute = 32262306a36Sopenharmony_ci __ATTR(conn_hub, 0444, uv_port_conn_hub_show, NULL); 32362306a36Sopenharmony_cistatic struct uv_port_sysfs_entry uv_port_conn_port_attribute = 32462306a36Sopenharmony_ci __ATTR(conn_port, 0444, uv_port_conn_port_show, NULL); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic struct attribute *uv_port_attrs[] = { 32762306a36Sopenharmony_ci &uv_port_conn_hub_attribute.attr, 32862306a36Sopenharmony_ci &uv_port_conn_port_attribute.attr, 32962306a36Sopenharmony_ci NULL, 33062306a36Sopenharmony_ci}; 33162306a36Sopenharmony_ciATTRIBUTE_GROUPS(uv_port); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic void uv_port_release(struct kobject *kobj) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct uv_port *port = to_uv_port(kobj); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci kfree(port); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic ssize_t uv_port_type_show(struct kobject *kobj, struct attribute *attr, 34162306a36Sopenharmony_ci char *buf) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct uv_port *port = to_uv_port(kobj); 34462306a36Sopenharmony_ci struct uv_bios_port_info *port_info = port->port_info; 34562306a36Sopenharmony_ci struct uv_port_sysfs_entry *entry; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci entry = container_of(attr, struct uv_port_sysfs_entry, attr); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (!entry->show) 35062306a36Sopenharmony_ci return -EIO; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return entry->show(port_info, buf); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic const struct sysfs_ops uv_port_sysfs_ops = { 35662306a36Sopenharmony_ci .show = uv_port_type_show, 35762306a36Sopenharmony_ci}; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic const struct kobj_type uv_port_attr_type = { 36062306a36Sopenharmony_ci .release = uv_port_release, 36162306a36Sopenharmony_ci .sysfs_ops = &uv_port_sysfs_ops, 36262306a36Sopenharmony_ci .default_groups = uv_port_groups, 36362306a36Sopenharmony_ci}; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic int uv_ports_init(void) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci s64 biosr; 36862306a36Sopenharmony_ci int j = 0, k = 0, ret, sz; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci port_buf = kcalloc(uv_bios_obj_cnt, sizeof(*port_buf), GFP_KERNEL); 37162306a36Sopenharmony_ci if (!port_buf) 37262306a36Sopenharmony_ci return -ENOMEM; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci for (j = 0; j < uv_bios_obj_cnt; j++) { 37562306a36Sopenharmony_ci sz = hub_buf[j].ports * sizeof(*port_buf[j]); 37662306a36Sopenharmony_ci port_buf[j] = kzalloc(sz, GFP_KERNEL); 37762306a36Sopenharmony_ci if (!port_buf[j]) { 37862306a36Sopenharmony_ci ret = -ENOMEM; 37962306a36Sopenharmony_ci j--; 38062306a36Sopenharmony_ci goto err_port_info; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci biosr = uv_bios_enum_ports((u64)uv_master_nasid, (u64)hub_buf[j].id, sz, 38362306a36Sopenharmony_ci (u64 *)port_buf[j]); 38462306a36Sopenharmony_ci if (biosr) { 38562306a36Sopenharmony_ci ret = -EINVAL; 38662306a36Sopenharmony_ci goto err_port_info; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci for (j = 0; j < uv_bios_obj_cnt; j++) { 39062306a36Sopenharmony_ci uv_hubs[j]->ports = kcalloc(hub_buf[j].ports, 39162306a36Sopenharmony_ci sizeof(*uv_hubs[j]->ports), GFP_KERNEL); 39262306a36Sopenharmony_ci if (!uv_hubs[j]->ports) { 39362306a36Sopenharmony_ci ret = -ENOMEM; 39462306a36Sopenharmony_ci j--; 39562306a36Sopenharmony_ci goto err_ports; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci for (j = 0; j < uv_bios_obj_cnt; j++) { 39962306a36Sopenharmony_ci for (k = 0; k < hub_buf[j].ports; k++) { 40062306a36Sopenharmony_ci uv_hubs[j]->ports[k] = kzalloc(sizeof(*uv_hubs[j]->ports[k]), GFP_KERNEL); 40162306a36Sopenharmony_ci if (!uv_hubs[j]->ports[k]) { 40262306a36Sopenharmony_ci ret = -ENOMEM; 40362306a36Sopenharmony_ci k--; 40462306a36Sopenharmony_ci goto err_kobj_ports; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci uv_hubs[j]->ports[k]->port_info = &port_buf[j][k]; 40762306a36Sopenharmony_ci ret = kobject_init_and_add(&uv_hubs[j]->ports[k]->kobj, &uv_port_attr_type, 40862306a36Sopenharmony_ci &uv_hubs[j]->kobj, "port_%d", port_buf[j][k].port); 40962306a36Sopenharmony_ci if (ret) 41062306a36Sopenharmony_ci goto err_kobj_ports; 41162306a36Sopenharmony_ci kobject_uevent(&uv_hubs[j]->ports[k]->kobj, KOBJ_ADD); 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci return 0; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cierr_kobj_ports: 41762306a36Sopenharmony_ci for (; j >= 0; j--) { 41862306a36Sopenharmony_ci for (; k >= 0; k--) 41962306a36Sopenharmony_ci kobject_put(&uv_hubs[j]->ports[k]->kobj); 42062306a36Sopenharmony_ci if (j > 0) 42162306a36Sopenharmony_ci k = hub_buf[j-1].ports - 1; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci j = uv_bios_obj_cnt - 1; 42462306a36Sopenharmony_cierr_ports: 42562306a36Sopenharmony_ci for (; j >= 0; j--) 42662306a36Sopenharmony_ci kfree(uv_hubs[j]->ports); 42762306a36Sopenharmony_ci j = uv_bios_obj_cnt - 1; 42862306a36Sopenharmony_cierr_port_info: 42962306a36Sopenharmony_ci for (; j >= 0; j--) 43062306a36Sopenharmony_ci kfree(port_buf[j]); 43162306a36Sopenharmony_ci kfree(port_buf); 43262306a36Sopenharmony_ci return ret; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic void uv_ports_exit(void) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci int j, k; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci for (j = 0; j < uv_bios_obj_cnt; j++) { 44062306a36Sopenharmony_ci for (k = hub_buf[j].ports - 1; k >= 0; k--) 44162306a36Sopenharmony_ci kobject_put(&uv_hubs[j]->ports[k]->kobj); 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci for (j = 0; j < uv_bios_obj_cnt; j++) { 44462306a36Sopenharmony_ci kfree(uv_hubs[j]->ports); 44562306a36Sopenharmony_ci kfree(port_buf[j]); 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci kfree(port_buf); 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistruct uv_pci_top_obj { 45162306a36Sopenharmony_ci struct kobject kobj; 45262306a36Sopenharmony_ci char *type; 45362306a36Sopenharmony_ci char *location; 45462306a36Sopenharmony_ci int iio_stack; 45562306a36Sopenharmony_ci char *ppb_addr; 45662306a36Sopenharmony_ci int slot; 45762306a36Sopenharmony_ci}; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci#define to_uv_pci_top_obj(kobj_ptr) container_of(kobj_ptr, struct uv_pci_top_obj, kobj) 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic ssize_t uv_pci_type_show(struct uv_pci_top_obj *top_obj, char *buf) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", top_obj->type); 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic ssize_t uv_pci_location_show(struct uv_pci_top_obj *top_obj, char *buf) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", top_obj->location); 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic ssize_t uv_pci_iio_stack_show(struct uv_pci_top_obj *top_obj, char *buf) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci return sprintf(buf, "%d\n", top_obj->iio_stack); 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic ssize_t uv_pci_ppb_addr_show(struct uv_pci_top_obj *top_obj, char *buf) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", top_obj->ppb_addr); 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic ssize_t uv_pci_slot_show(struct uv_pci_top_obj *top_obj, char *buf) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci return sprintf(buf, "%d\n", top_obj->slot); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistruct uv_pci_top_sysfs_entry { 48762306a36Sopenharmony_ci struct attribute attr; 48862306a36Sopenharmony_ci ssize_t (*show)(struct uv_pci_top_obj *top_obj, char *buf); 48962306a36Sopenharmony_ci ssize_t (*store)(struct uv_pci_top_obj *top_obj, const char *buf, size_t size); 49062306a36Sopenharmony_ci}; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic struct uv_pci_top_sysfs_entry uv_pci_type_attribute = 49362306a36Sopenharmony_ci __ATTR(type, 0444, uv_pci_type_show, NULL); 49462306a36Sopenharmony_cistatic struct uv_pci_top_sysfs_entry uv_pci_location_attribute = 49562306a36Sopenharmony_ci __ATTR(location, 0444, uv_pci_location_show, NULL); 49662306a36Sopenharmony_cistatic struct uv_pci_top_sysfs_entry uv_pci_iio_stack_attribute = 49762306a36Sopenharmony_ci __ATTR(iio_stack, 0444, uv_pci_iio_stack_show, NULL); 49862306a36Sopenharmony_cistatic struct uv_pci_top_sysfs_entry uv_pci_ppb_addr_attribute = 49962306a36Sopenharmony_ci __ATTR(ppb_addr, 0444, uv_pci_ppb_addr_show, NULL); 50062306a36Sopenharmony_cistatic struct uv_pci_top_sysfs_entry uv_pci_slot_attribute = 50162306a36Sopenharmony_ci __ATTR(slot, 0444, uv_pci_slot_show, NULL); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic void uv_pci_top_release(struct kobject *kobj) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct uv_pci_top_obj *top_obj = to_uv_pci_top_obj(kobj); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci kfree(top_obj->type); 50862306a36Sopenharmony_ci kfree(top_obj->location); 50962306a36Sopenharmony_ci kfree(top_obj->ppb_addr); 51062306a36Sopenharmony_ci kfree(top_obj); 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic ssize_t pci_top_type_show(struct kobject *kobj, 51462306a36Sopenharmony_ci struct attribute *attr, char *buf) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct uv_pci_top_obj *top_obj = to_uv_pci_top_obj(kobj); 51762306a36Sopenharmony_ci struct uv_pci_top_sysfs_entry *entry; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci entry = container_of(attr, struct uv_pci_top_sysfs_entry, attr); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (!entry->show) 52262306a36Sopenharmony_ci return -EIO; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return entry->show(top_obj, buf); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic const struct sysfs_ops uv_pci_top_sysfs_ops = { 52862306a36Sopenharmony_ci .show = pci_top_type_show, 52962306a36Sopenharmony_ci}; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic const struct kobj_type uv_pci_top_attr_type = { 53262306a36Sopenharmony_ci .release = uv_pci_top_release, 53362306a36Sopenharmony_ci .sysfs_ops = &uv_pci_top_sysfs_ops, 53462306a36Sopenharmony_ci}; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic int init_pci_top_obj(struct uv_pci_top_obj *top_obj, char *line) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci char *start; 53962306a36Sopenharmony_ci char type[11], location[14], ppb_addr[15]; 54062306a36Sopenharmony_ci int str_cnt, ret; 54162306a36Sopenharmony_ci unsigned int tmp_match[2]; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci // Minimum line length 54462306a36Sopenharmony_ci if (strlen(line) < 36) 54562306a36Sopenharmony_ci return -EINVAL; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci //Line must match format "pcibus %4x:%2x" to be valid 54862306a36Sopenharmony_ci str_cnt = sscanf(line, "pcibus %4x:%2x", &tmp_match[0], &tmp_match[1]); 54962306a36Sopenharmony_ci if (str_cnt < 2) 55062306a36Sopenharmony_ci return -EINVAL; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* Connect pcibus to segment:bus number with '_' 55362306a36Sopenharmony_ci * to concatenate name tokens. 55462306a36Sopenharmony_ci * pcibus 0000:00 ... -> pcibus_0000:00 ... 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ci line[6] = '_'; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* Null terminate after the concatencated name tokens 55962306a36Sopenharmony_ci * to produce kobj name string. 56062306a36Sopenharmony_ci */ 56162306a36Sopenharmony_ci line[14] = '\0'; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci // Use start to index after name tokens string for remainder of line info. 56462306a36Sopenharmony_ci start = &line[15]; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci top_obj->iio_stack = -1; 56762306a36Sopenharmony_ci top_obj->slot = -1; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* r001i01b00h0 BASE IO (IIO Stack 0) 57062306a36Sopenharmony_ci * r001i01b00h1 PCIe IO (IIO Stack 1) 57162306a36Sopenharmony_ci * r001i01b03h1 PCIe SLOT 57262306a36Sopenharmony_ci * r001i01b00h0 NODE IO 57362306a36Sopenharmony_ci * r001i01b00h0 Riser 57462306a36Sopenharmony_ci * (IIO Stack #) may not be present. 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_ci if (start[0] == 'r') { 57762306a36Sopenharmony_ci str_cnt = sscanf(start, "%13s %10[^(] %*s %*s %d)", 57862306a36Sopenharmony_ci location, type, &top_obj->iio_stack); 57962306a36Sopenharmony_ci if (str_cnt < 2) 58062306a36Sopenharmony_ci return -EINVAL; 58162306a36Sopenharmony_ci top_obj->type = kstrdup(type, GFP_KERNEL); 58262306a36Sopenharmony_ci if (!top_obj->type) 58362306a36Sopenharmony_ci return -ENOMEM; 58462306a36Sopenharmony_ci top_obj->location = kstrdup(location, GFP_KERNEL); 58562306a36Sopenharmony_ci if (!top_obj->location) { 58662306a36Sopenharmony_ci kfree(top_obj->type); 58762306a36Sopenharmony_ci return -ENOMEM; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci /* PPB at 0000:80:00.00 (slot 3) 59162306a36Sopenharmony_ci * (slot #) may not be present. 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_ci else if (start[0] == 'P') { 59462306a36Sopenharmony_ci str_cnt = sscanf(start, "%10s %*s %14s %*s %d)", 59562306a36Sopenharmony_ci type, ppb_addr, &top_obj->slot); 59662306a36Sopenharmony_ci if (str_cnt < 2) 59762306a36Sopenharmony_ci return -EINVAL; 59862306a36Sopenharmony_ci top_obj->type = kstrdup(type, GFP_KERNEL); 59962306a36Sopenharmony_ci if (!top_obj->type) 60062306a36Sopenharmony_ci return -ENOMEM; 60162306a36Sopenharmony_ci top_obj->ppb_addr = kstrdup(ppb_addr, GFP_KERNEL); 60262306a36Sopenharmony_ci if (!top_obj->ppb_addr) { 60362306a36Sopenharmony_ci kfree(top_obj->type); 60462306a36Sopenharmony_ci return -ENOMEM; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci } else 60762306a36Sopenharmony_ci return -EINVAL; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci top_obj->kobj.kset = uv_pcibus_kset; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci ret = kobject_init_and_add(&top_obj->kobj, &uv_pci_top_attr_type, NULL, "%s", line); 61262306a36Sopenharmony_ci if (ret) 61362306a36Sopenharmony_ci goto err_add_sysfs; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (top_obj->type) { 61662306a36Sopenharmony_ci ret = sysfs_create_file(&top_obj->kobj, &uv_pci_type_attribute.attr); 61762306a36Sopenharmony_ci if (ret) 61862306a36Sopenharmony_ci goto err_add_sysfs; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci if (top_obj->location) { 62162306a36Sopenharmony_ci ret = sysfs_create_file(&top_obj->kobj, &uv_pci_location_attribute.attr); 62262306a36Sopenharmony_ci if (ret) 62362306a36Sopenharmony_ci goto err_add_sysfs; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci if (top_obj->iio_stack >= 0) { 62662306a36Sopenharmony_ci ret = sysfs_create_file(&top_obj->kobj, &uv_pci_iio_stack_attribute.attr); 62762306a36Sopenharmony_ci if (ret) 62862306a36Sopenharmony_ci goto err_add_sysfs; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci if (top_obj->ppb_addr) { 63162306a36Sopenharmony_ci ret = sysfs_create_file(&top_obj->kobj, &uv_pci_ppb_addr_attribute.attr); 63262306a36Sopenharmony_ci if (ret) 63362306a36Sopenharmony_ci goto err_add_sysfs; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci if (top_obj->slot >= 0) { 63662306a36Sopenharmony_ci ret = sysfs_create_file(&top_obj->kobj, &uv_pci_slot_attribute.attr); 63762306a36Sopenharmony_ci if (ret) 63862306a36Sopenharmony_ci goto err_add_sysfs; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci kobject_uevent(&top_obj->kobj, KOBJ_ADD); 64262306a36Sopenharmony_ci return 0; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cierr_add_sysfs: 64562306a36Sopenharmony_ci kobject_put(&top_obj->kobj); 64662306a36Sopenharmony_ci return ret; 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic int pci_topology_init(void) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci char *pci_top_str, *start, *found, *count; 65262306a36Sopenharmony_ci size_t sz; 65362306a36Sopenharmony_ci s64 biosr; 65462306a36Sopenharmony_ci int l = 0, k = 0; 65562306a36Sopenharmony_ci int len, ret; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci uv_pcibus_kset = kset_create_and_add("pcibuses", NULL, sgi_uv_kobj); 65862306a36Sopenharmony_ci if (!uv_pcibus_kset) 65962306a36Sopenharmony_ci return -ENOMEM; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci for (sz = PAGE_SIZE; sz < 16 * PAGE_SIZE; sz += PAGE_SIZE) { 66262306a36Sopenharmony_ci pci_top_str = kmalloc(sz, GFP_KERNEL); 66362306a36Sopenharmony_ci if (!pci_top_str) { 66462306a36Sopenharmony_ci ret = -ENOMEM; 66562306a36Sopenharmony_ci goto err_pci_top_str; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci biosr = uv_bios_get_pci_topology((u64)sz, (u64 *)pci_top_str); 66862306a36Sopenharmony_ci if (biosr == BIOS_STATUS_SUCCESS) { 66962306a36Sopenharmony_ci len = strnlen(pci_top_str, sz); 67062306a36Sopenharmony_ci for (count = pci_top_str; count < pci_top_str + len; count++) { 67162306a36Sopenharmony_ci if (*count == '\n') 67262306a36Sopenharmony_ci l++; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci num_pci_lines = l; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci uv_pci_objs = kcalloc(num_pci_lines, 67762306a36Sopenharmony_ci sizeof(*uv_pci_objs), GFP_KERNEL); 67862306a36Sopenharmony_ci if (!uv_pci_objs) { 67962306a36Sopenharmony_ci kfree(pci_top_str); 68062306a36Sopenharmony_ci ret = -ENOMEM; 68162306a36Sopenharmony_ci goto err_pci_top_str; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci start = pci_top_str; 68462306a36Sopenharmony_ci while ((found = strsep(&start, "\n")) != NULL) { 68562306a36Sopenharmony_ci uv_pci_objs[k] = kzalloc(sizeof(*uv_pci_objs[k]), GFP_KERNEL); 68662306a36Sopenharmony_ci if (!uv_pci_objs[k]) { 68762306a36Sopenharmony_ci ret = -ENOMEM; 68862306a36Sopenharmony_ci goto err_pci_obj; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci ret = init_pci_top_obj(uv_pci_objs[k], found); 69162306a36Sopenharmony_ci if (ret) 69262306a36Sopenharmony_ci goto err_pci_obj; 69362306a36Sopenharmony_ci k++; 69462306a36Sopenharmony_ci if (k == num_pci_lines) 69562306a36Sopenharmony_ci break; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci kfree(pci_top_str); 69962306a36Sopenharmony_ci if (biosr == BIOS_STATUS_SUCCESS || biosr == BIOS_STATUS_UNIMPLEMENTED) 70062306a36Sopenharmony_ci break; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci return 0; 70462306a36Sopenharmony_cierr_pci_obj: 70562306a36Sopenharmony_ci k--; 70662306a36Sopenharmony_ci for (; k >= 0; k--) 70762306a36Sopenharmony_ci kobject_put(&uv_pci_objs[k]->kobj); 70862306a36Sopenharmony_ci kfree(uv_pci_objs); 70962306a36Sopenharmony_ci kfree(pci_top_str); 71062306a36Sopenharmony_cierr_pci_top_str: 71162306a36Sopenharmony_ci kset_unregister(uv_pcibus_kset); 71262306a36Sopenharmony_ci return ret; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic void pci_topology_exit(void) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci int k; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci for (k = 0; k < num_pci_lines; k++) 72062306a36Sopenharmony_ci kobject_put(&uv_pci_objs[k]->kobj); 72162306a36Sopenharmony_ci kset_unregister(uv_pcibus_kset); 72262306a36Sopenharmony_ci kfree(uv_pci_objs); 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic ssize_t partition_id_show(struct kobject *kobj, 72662306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci return sprintf(buf, "%ld\n", sn_partition_id); 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic ssize_t coherence_id_show(struct kobject *kobj, 73262306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci return sprintf(buf, "%ld\n", sn_coherency_id); 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic ssize_t uv_type_show(struct kobject *kobj, 73862306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", uv_type_string()); 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic ssize_t uv_archtype_show(struct kobject *kobj, 74462306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci return uv_get_archtype(buf, PAGE_SIZE); 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic ssize_t uv_hub_type_show(struct kobject *kobj, 75062306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci return sysfs_emit(buf, "0x%x\n", uv_hub_type()); 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic ssize_t uv_hubless_show(struct kobject *kobj, 75662306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci return sysfs_emit(buf, "0x%x\n", uv_get_hubless_system()); 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic struct kobj_attribute partition_id_attr = 76262306a36Sopenharmony_ci __ATTR(partition_id, 0444, partition_id_show, NULL); 76362306a36Sopenharmony_cistatic struct kobj_attribute coherence_id_attr = 76462306a36Sopenharmony_ci __ATTR(coherence_id, 0444, coherence_id_show, NULL); 76562306a36Sopenharmony_cistatic struct kobj_attribute uv_type_attr = 76662306a36Sopenharmony_ci __ATTR(uv_type, 0444, uv_type_show, NULL); 76762306a36Sopenharmony_cistatic struct kobj_attribute uv_archtype_attr = 76862306a36Sopenharmony_ci __ATTR(archtype, 0444, uv_archtype_show, NULL); 76962306a36Sopenharmony_cistatic struct kobj_attribute uv_hub_type_attr = 77062306a36Sopenharmony_ci __ATTR(hub_type, 0444, uv_hub_type_show, NULL); 77162306a36Sopenharmony_cistatic struct kobj_attribute uv_hubless_attr = 77262306a36Sopenharmony_ci __ATTR(hubless, 0444, uv_hubless_show, NULL); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic struct attribute *base_attrs[] = { 77562306a36Sopenharmony_ci &partition_id_attr.attr, 77662306a36Sopenharmony_ci &coherence_id_attr.attr, 77762306a36Sopenharmony_ci &uv_type_attr.attr, 77862306a36Sopenharmony_ci &uv_archtype_attr.attr, 77962306a36Sopenharmony_ci &uv_hub_type_attr.attr, 78062306a36Sopenharmony_ci NULL, 78162306a36Sopenharmony_ci}; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic const struct attribute_group base_attr_group = { 78462306a36Sopenharmony_ci .attrs = base_attrs 78562306a36Sopenharmony_ci}; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic int initial_bios_setup(void) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci u64 v; 79062306a36Sopenharmony_ci s64 biosr; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci biosr = uv_bios_get_master_nasid((u64)sizeof(uv_master_nasid), (u64 *)&uv_master_nasid); 79362306a36Sopenharmony_ci if (biosr) 79462306a36Sopenharmony_ci return -EINVAL; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci biosr = uv_bios_get_heapsize((u64)uv_master_nasid, (u64)sizeof(u64), &v); 79762306a36Sopenharmony_ci if (biosr) 79862306a36Sopenharmony_ci return -EINVAL; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci uv_biosheap = vmalloc(v); 80162306a36Sopenharmony_ci if (!uv_biosheap) 80262306a36Sopenharmony_ci return -ENOMEM; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci biosr = uv_bios_install_heap((u64)uv_master_nasid, v, (u64 *)uv_biosheap); 80562306a36Sopenharmony_ci if (biosr) { 80662306a36Sopenharmony_ci vfree(uv_biosheap); 80762306a36Sopenharmony_ci return -EINVAL; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci biosr = uv_bios_obj_count((u64)uv_master_nasid, sizeof(u64), &v); 81162306a36Sopenharmony_ci if (biosr) { 81262306a36Sopenharmony_ci vfree(uv_biosheap); 81362306a36Sopenharmony_ci return -EINVAL; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci uv_bios_obj_cnt = (int)v; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci return 0; 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic struct attribute *hubless_base_attrs[] = { 82162306a36Sopenharmony_ci &partition_id_attr.attr, 82262306a36Sopenharmony_ci &uv_type_attr.attr, 82362306a36Sopenharmony_ci &uv_archtype_attr.attr, 82462306a36Sopenharmony_ci &uv_hubless_attr.attr, 82562306a36Sopenharmony_ci NULL, 82662306a36Sopenharmony_ci}; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic const struct attribute_group hubless_base_attr_group = { 82962306a36Sopenharmony_ci .attrs = hubless_base_attrs 83062306a36Sopenharmony_ci}; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cistatic int __init uv_sysfs_hubless_init(void) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci int ret; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci ret = sysfs_create_group(sgi_uv_kobj, &hubless_base_attr_group); 83862306a36Sopenharmony_ci if (ret) { 83962306a36Sopenharmony_ci pr_warn("sysfs_create_group hubless_base_attr_group failed\n"); 84062306a36Sopenharmony_ci kobject_put(sgi_uv_kobj); 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci return ret; 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cistatic int __init uv_sysfs_init(void) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci int ret = 0; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (!is_uv_system() && !uv_get_hubless_system()) 85062306a36Sopenharmony_ci return -ENODEV; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci num_cnodes = uv_num_possible_blades(); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (!sgi_uv_kobj) 85562306a36Sopenharmony_ci sgi_uv_kobj = kobject_create_and_add("sgi_uv", firmware_kobj); 85662306a36Sopenharmony_ci if (!sgi_uv_kobj) { 85762306a36Sopenharmony_ci pr_warn("kobject_create_and_add sgi_uv failed\n"); 85862306a36Sopenharmony_ci return -EINVAL; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (uv_get_hubless_system()) 86262306a36Sopenharmony_ci return uv_sysfs_hubless_init(); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci ret = sysfs_create_group(sgi_uv_kobj, &base_attr_group); 86562306a36Sopenharmony_ci if (ret) { 86662306a36Sopenharmony_ci pr_warn("sysfs_create_group base_attr_group failed\n"); 86762306a36Sopenharmony_ci goto err_create_group; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci ret = initial_bios_setup(); 87162306a36Sopenharmony_ci if (ret) 87262306a36Sopenharmony_ci goto err_bios_setup; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci ret = uv_hubs_init(); 87562306a36Sopenharmony_ci if (ret) 87662306a36Sopenharmony_ci goto err_hubs_init; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci ret = uv_ports_init(); 87962306a36Sopenharmony_ci if (ret) 88062306a36Sopenharmony_ci goto err_ports_init; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci ret = pci_topology_init(); 88362306a36Sopenharmony_ci if (ret) 88462306a36Sopenharmony_ci goto err_pci_init; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci return 0; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_cierr_pci_init: 88962306a36Sopenharmony_ci uv_ports_exit(); 89062306a36Sopenharmony_cierr_ports_init: 89162306a36Sopenharmony_ci uv_hubs_exit(); 89262306a36Sopenharmony_cierr_hubs_init: 89362306a36Sopenharmony_ci vfree(uv_biosheap); 89462306a36Sopenharmony_cierr_bios_setup: 89562306a36Sopenharmony_ci sysfs_remove_group(sgi_uv_kobj, &base_attr_group); 89662306a36Sopenharmony_cierr_create_group: 89762306a36Sopenharmony_ci kobject_put(sgi_uv_kobj); 89862306a36Sopenharmony_ci return ret; 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_cistatic void __exit uv_sysfs_hubless_exit(void) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci sysfs_remove_group(sgi_uv_kobj, &hubless_base_attr_group); 90462306a36Sopenharmony_ci kobject_put(sgi_uv_kobj); 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic void __exit uv_sysfs_exit(void) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci if (!is_uv_system()) { 91062306a36Sopenharmony_ci if (uv_get_hubless_system()) 91162306a36Sopenharmony_ci uv_sysfs_hubless_exit(); 91262306a36Sopenharmony_ci return; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci pci_topology_exit(); 91662306a36Sopenharmony_ci uv_ports_exit(); 91762306a36Sopenharmony_ci uv_hubs_exit(); 91862306a36Sopenharmony_ci vfree(uv_biosheap); 91962306a36Sopenharmony_ci sysfs_remove_group(sgi_uv_kobj, &base_attr_group); 92062306a36Sopenharmony_ci kobject_put(sgi_uv_kobj); 92162306a36Sopenharmony_ci} 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci#ifndef MODULE 92462306a36Sopenharmony_cidevice_initcall(uv_sysfs_init); 92562306a36Sopenharmony_ci#else 92662306a36Sopenharmony_cimodule_init(uv_sysfs_init); 92762306a36Sopenharmony_ci#endif 92862306a36Sopenharmony_cimodule_exit(uv_sysfs_exit); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ciMODULE_AUTHOR("Hewlett Packard Enterprise"); 93162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 932