// SPDX-License-Identifier: GPL-2.0-or-later
/*
* This file supports the /sys/firmware/sgi_uv topology tree on HPE UV.
*
* Copyright (c) 2020 Hewlett Packard Enterprise. All Rights Reserved.
* Copyright (c) Justin Ernst
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/kobject.h>
#include <asm/uv/bios.h>
#include <asm/uv/uv.h>
#include <asm/uv/uv_hub.h>
#include <asm/uv/uv_geo.h>
#define INVALID_CNODE -1
struct kobject *sgi_uv_kobj;
static struct kset *uv_pcibus_kset;
static struct kset *uv_hubs_kset;
static struct uv_bios_hub_info *hub_buf;
static struct uv_bios_port_info **port_buf;
static struct uv_hub **uv_hubs;
static struct uv_pci_top_obj **uv_pci_objs;
static int num_pci_lines;
static int num_cnodes;
static int *prev_obj_to_cnode;
static int uv_bios_obj_cnt;
static signed short uv_master_nasid = -1;
static void *uv_biosheap;
static const char *uv_type_string(void)
{
if (is_uv5_hub())
return "9.0";
else if (is_uv4a_hub())
return "7.1";
else if (is_uv4_hub())
return "7.0";
else if (is_uv3_hub())
return "5.0";
else if (is_uv2_hub())
return "3.0";
else if (uv_get_hubless_system())
return "0.1";
else
return "unknown";
}
static int ordinal_to_nasid(int ordinal)
{
if (ordinal < num_cnodes && ordinal >= 0)
return UV_PNODE_TO_NASID(uv_blade_to_pnode(ordinal));
else
return -1;
}
static union geoid_u cnode_to_geoid(int cnode)
{
union geoid_u geoid;
uv_bios_get_geoinfo(ordinal_to_nasid(cnode), (u64)sizeof(union geoid_u), (u64 *)&geoid);
return geoid;
}
static int location_to_bpos(char *location, int *rack, int *slot, int *blade)
{
char type, r, b, h;
int idb, idh;
if (sscanf(location, "%c%03d%c%02d%c%2d%c%d",
&r, rack, &type, slot, &b, &idb, &h, &idh) != 8)
return -1;
*blade = idb * 2 + idh;
return 0;
}
static int cache_obj_to_cnode(struct uv_bios_hub_info *obj)
{
int cnode;
union geoid_u geoid;
int obj_rack, obj_slot, obj_blade;
int rack, slot, blade;
if (!obj->f.fields.this_part && !obj->f.fields.is_shared)
return 0;
if (location_to_bpos(obj->location, &obj_rack, &obj_slot, &obj_blade))
return -1;
for (cnode = 0; cnode < num_cnodes; cnode++) {
geoid = cnode_to_geoid(cnode);
rack = geo_rack(geoid);
slot = geo_slot(geoid);
blade = geo_blade(geoid);
if (obj_rack == rack && obj_slot == slot && obj_blade == blade)
prev_obj_to_cnode[obj->id] = cnode;
}
return 0;
}
static int get_obj_to_cnode(int obj_id)
{
return prev_obj_to_cnode[obj_id];
}
struct uv_hub {
struct kobject kobj;
struct uv_bios_hub_info *hub_info;
struct uv_port **ports;
};
#define to_uv_hub(kobj_ptr) container_of(kobj_ptr, struct uv_hub, kobj)
static ssize_t hub_name_show(struct uv_bios_hub_info *hub_info, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%s\n", hub_info->name);
}
static ssize_t hub_location_show(struct uv_bios_hub_info *hub_info, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%s\n", hub_info->location);
}
static ssize_t hub_partition_show(struct uv_bios_hub_info *hub_info, char *buf)
{
return sprintf(buf, "%d\n", hub_info->f.fields.this_part);
}
static ssize_t hub_shared_show(struct uv_bios_hub_info *hub_info, char *buf)
{
return sprintf(buf, "%d\n", hub_info->f.fields.is_shared);
}
static ssize_t hub_nasid_show(struct uv_bios_hub_info *hub_info, char *buf)
{
int cnode = get_obj_to_cnode(hub_info->id);
return sprintf(buf, "%d\n", ordinal_to_nasid(