All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1 0/1] drm/amdgpu: Show IP discovery in sysfs
@ 2022-02-09 22:51 Luben Tuikov
  2022-02-09 22:51 ` [PATCH v1 1/1] " Luben Tuikov
  0 siblings, 1 reply; 4+ messages in thread
From: Luben Tuikov @ 2022-02-09 22:51 UTC (permalink / raw)
  To: amd-gfx; +Cc: Alex Deucher, Tom StDenis, Luben Tuikov

Version 1, this version, adds sysfs tear-down on rmmod.

Show IP discovery in sysfs. See the commit message for the layout
format.

For instance, on a Sienna Cichlid, the layout looks like this:

$tree /sys/class/drm/card0/device/ip_discovery/
/sys/class/drm/card0/device/ip_discovery/
└── die
    └── 0
        ├── 1
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 108
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 11
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 12
        │   ├── 0
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   └── 1
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 128
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 150
        │   ├── 0
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   ├── 1
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   ├── 2
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   ├── 3
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   ├── 4
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   ├── 5
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   ├── 6
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   └── 7
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 170
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 200
        │   ├── 0
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   └── 1
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 255
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 271
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 3
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 34
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 35
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 36
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 37
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 4
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 40
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 41
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 42
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 43
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 46
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 47
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 49
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 5
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 6
        │   ├── 0
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   ├── 1
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   ├── 2
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   ├── 3
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   ├── 4
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   ├── 5
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   ├── 6
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   ├── 7
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   ├── 8
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   └── 9
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 66
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 68
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 69
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 70
        │   └── 0
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── 80
        │   ├── 0
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   ├── 1
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   ├── 2
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   ├── 3
        │   │   ├── base_addr
        │   │   ├── hw_id
        │   │   ├── major
        │   │   ├── minor
        │   │   ├── num_base_addresses
        │   │   ├── num_instance
        │   │   └── revision
        │   └── 4
        │       ├── base_addr
        │       ├── hw_id
        │       ├── major
        │       ├── minor
        │       ├── num_base_addresses
        │       ├── num_instance
        │       └── revision
        ├── ATHUB -> 35
        ├── CLKA -> 6
        ├── CLKB -> 47
        ├── DBGU_NBIO -> 36
        ├── DF -> 46
        ├── DFX -> 37
        ├── DFX_DAP -> 49
        ├── DMU -> 271
        ├── FUSE -> 5
        ├── GC -> 11
        ├── HDP -> 41
        ├── MMHUB -> 34
        ├── MP0 -> 255
        ├── MP1 -> 1
        ├── NBIF -> 108
        ├── num_ips
        ├── OSSSYS -> 40
        ├── PCIE -> 70
        ├── PCS -> 80
        ├── SDMA0 -> 42
        ├── SDMA1 -> 43
        ├── SDMA2 -> 68
        ├── SDMA3 -> 69
        ├── SMUIO -> 4
        ├── SYSTEMHUB -> 128
        ├── THM -> 3
        ├── UMC -> 150
        ├── USB -> 170
        ├── UVD -> 12
        ├── WAFLC -> 66
        └── XGMI -> 200

114 directories, 365 files
$_

"num_ips" shows the total number of IPs, as this count exists by the
same name in the binary data.

Typycal contents of IP attributes is,

$head /sys/class/drm/card0/device/ip_discovery/die/0/GC/0/*
==> /sys/class/drm/card0/device/ip_discovery/die/0/GC/0/base_addr <==
0x00001260
0x0000A000
0x0001C000
0x02402C00

==> /sys/class/drm/card0/device/ip_discovery/die/0/GC/0/hw_id <==
11

==> /sys/class/drm/card0/device/ip_discovery/die/0/GC/0/major <==
10

==> /sys/class/drm/card0/device/ip_discovery/die/0/GC/0/minor <==
3

==> /sys/class/drm/card0/device/ip_discovery/die/0/GC/0/num_base_addresses <==
4

==> /sys/class/drm/card0/device/ip_discovery/die/0/GC/0/num_instance <==
0

==> /sys/class/drm/card0/device/ip_discovery/die/0/GC/0/revision <==
0
$_

Luben Tuikov (1):
  drm/amdgpu: Show IP discovery in sysfs

Cc: Alex Deucher <Alexander.Deucher@amd.com>
Cc: Tom StDenis <tom.stdenis@amd.com>

 drivers/gpu/drm/amd/amdgpu/amdgpu.h           |   4 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 486 ++++++++++++++++++
 2 files changed, 490 insertions(+)

base-commit: ff7951de1a24615706ca2533e27170961ce11f27
-- 
2.35.0.3.gb23dac905b


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v1 1/1] drm/amdgpu: Show IP discovery in sysfs
  2022-02-09 22:51 [PATCH v1 0/1] drm/amdgpu: Show IP discovery in sysfs Luben Tuikov
@ 2022-02-09 22:51 ` Luben Tuikov
  2022-02-10  3:43   ` Wang, Yang(Kevin)
  2022-02-10  4:48   ` Lang Yu
  0 siblings, 2 replies; 4+ messages in thread
From: Luben Tuikov @ 2022-02-09 22:51 UTC (permalink / raw)
  To: amd-gfx; +Cc: Alex Deucher, Tom StDenis, Luben Tuikov

Add IP discovery data in sysfs. The format is:
/sys/class/drm/cardX/device/ip_discovery/die/D/B/I/<attrs>
where,
X is the card ID, an integer,
D is the die ID, an integer,
B is the IP HW ID, an integer, aka block type,
I is the IP HW ID instance, an integer.
<attrs> are the attributes of the block instance. At the moment these
include HW ID, instance number, major, minor, revision, number of base
addresses, and the base addresses themselves.

A symbolic link of the acronym HW ID is also created, under D/, if you
prefer to browse by something humanly accessible.

Cc: Alex Deucher <Alexander.Deucher@amd.com>
Cc: Tom StDenis <tom.stdenis@amd.com>
Signed-off-by: Luben Tuikov <luben.tuikov@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu.h           |   4 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 486 ++++++++++++++++++
 2 files changed, 490 insertions(+)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index e4eb812ade2fa4..3a126dce8a2fe9 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -772,6 +772,8 @@ struct amd_powerplay {
 	const struct amd_pm_funcs *pp_funcs;
 };
 
+struct ip_discovery_top;
+
 /* polaris10 kickers */
 #define ASICID_IS_P20(did, rid)		(((did == 0x67DF) && \
 					 ((rid == 0xE3) || \
@@ -1097,6 +1099,8 @@ struct amdgpu_device {
 	bool				ram_is_direct_mapped;
 
 	struct list_head                ras_list;
+
+	struct ip_discovery_top         *ip_top;
 };
 
 static inline struct amdgpu_device *drm_to_adev(struct drm_device *ddev)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
index 07623634fdc2f1..46e6e1352574f6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
@@ -360,8 +360,11 @@ static int amdgpu_discovery_init(struct amdgpu_device *adev)
 	return r;
 }
 
+static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev);
+
 void amdgpu_discovery_fini(struct amdgpu_device *adev)
 {
+	amdgpu_discovery_sysfs_fini(adev);
 	kfree(adev->mman.discovery_bin);
 	adev->mman.discovery_bin = NULL;
 }
@@ -382,6 +385,487 @@ static int amdgpu_discovery_validate_ip(const struct ip *ip)
 	return 0;
 }
 
+/* ================================================== */
+
+struct ip_hw_instance {
+	struct kobject kobj; /* ip_discovery/die/#die/#hw_id/#instance/<attrs...> */
+
+	int hw_id;
+	u8  num_instance;
+	u8  major, minor, revision;
+
+	int num_base_addresses;
+	u32 base_addr[0];
+};
+
+struct ip_hw_id {
+	struct kset hw_id_kset;  /* ip_discovery/die/#die/#hw_id/, contains ip_hw_instance */
+	int hw_id;
+};
+
+struct ip_die_entry {
+	struct kset ip_kset;     /* ip_discovery/die/#die/, contains ip_hw_id  */
+	u16 num_ips;
+};
+
+/* -------------------------------------------------- */
+
+struct ip_hw_instance_attr {
+	struct attribute attr;
+	ssize_t (*show)(struct ip_hw_instance *ip_hw_instance, char *buf);
+};
+
+static ssize_t hw_id_show(struct ip_hw_instance *ip_hw_instance, char *buf)
+{
+	return sprintf(buf, "%d\n", ip_hw_instance->hw_id);
+}
+
+static ssize_t num_instance_show(struct ip_hw_instance *ip_hw_instance, char *buf)
+{
+	return sprintf(buf, "%d\n", ip_hw_instance->num_instance);
+}
+
+static ssize_t major_show(struct ip_hw_instance *ip_hw_instance, char *buf)
+{
+	return sprintf(buf, "%d\n", ip_hw_instance->major);
+}
+
+static ssize_t minor_show(struct ip_hw_instance *ip_hw_instance, char *buf)
+{
+	return sprintf(buf, "%d\n", ip_hw_instance->minor);
+}
+
+static ssize_t revision_show(struct ip_hw_instance *ip_hw_instance, char *buf)
+{
+	return sprintf(buf, "%d\n", ip_hw_instance->revision);
+}
+
+static ssize_t num_base_addresses_show(struct ip_hw_instance *ip_hw_instance, char *buf)
+{
+	return sprintf(buf, "%d\n", ip_hw_instance->num_base_addresses);
+}
+
+static ssize_t base_addr_show(struct ip_hw_instance *ip_hw_instance, char *buf)
+{
+	ssize_t res = 0;
+	int ii;
+
+	for (ii = 0; ii < ip_hw_instance->num_base_addresses; ii++) {
+		if (res + 12 >= PAGE_SIZE)
+			break;
+		res += sprintf(buf + res, "0x%08X\n", ip_hw_instance->base_addr[ii]);
+	}
+
+	return res;
+}
+
+static struct ip_hw_instance_attr ip_hw_attr[] = {
+	__ATTR_RO(hw_id),
+	__ATTR_RO(num_instance),
+	__ATTR_RO(major),
+	__ATTR_RO(minor),
+	__ATTR_RO(revision),
+	__ATTR_RO(num_base_addresses),
+	__ATTR_RO(base_addr),
+};
+
+static struct attribute *ip_hw_instance_attrs[] = {
+	&ip_hw_attr[0].attr,
+	&ip_hw_attr[1].attr,
+	&ip_hw_attr[2].attr,
+	&ip_hw_attr[3].attr,
+	&ip_hw_attr[4].attr,
+	&ip_hw_attr[5].attr,
+	&ip_hw_attr[6].attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(ip_hw_instance);
+
+#define to_ip_hw_instance(x) container_of(x, struct ip_hw_instance, kobj)
+#define to_ip_hw_instance_attr(x) container_of(x, struct ip_hw_instance_attr, attr)
+
+static ssize_t ip_hw_instance_attr_show(struct kobject *kobj,
+					struct attribute *attr,
+					char *buf)
+{
+	struct ip_hw_instance *ip_hw_instance = to_ip_hw_instance(kobj);
+	struct ip_hw_instance_attr *ip_hw_attr = to_ip_hw_instance_attr(attr);
+
+	if (!ip_hw_attr->show)
+		return -EIO;
+
+	return ip_hw_attr->show(ip_hw_instance, buf);
+}
+
+static const struct sysfs_ops ip_hw_instance_sysfs_ops = {
+	.show = ip_hw_instance_attr_show,
+};
+
+static void ip_hw_instance_release(struct kobject *kobj)
+{
+	struct ip_hw_instance *ip_hw_instance = to_ip_hw_instance(kobj);
+
+	kfree(ip_hw_instance);
+}
+
+static struct kobj_type ip_hw_instance_ktype = {
+	.release = ip_hw_instance_release,
+	.sysfs_ops = &ip_hw_instance_sysfs_ops,
+	.default_groups = ip_hw_instance_groups,
+};
+
+/* -------------------------------------------------- */
+
+#define to_ip_hw_id(x)  container_of(to_kset(x), struct ip_hw_id, hw_id_kset)
+
+static void ip_hw_id_release(struct kobject *kobj)
+{
+	struct ip_hw_id *ip_hw_id = to_ip_hw_id(kobj);
+
+	if (!list_empty(&ip_hw_id->hw_id_kset.list))
+		DRM_ERROR("ip_hw_id->hw_id_kset is not empty");
+	kfree(ip_hw_id);
+}
+
+static struct kobj_type ip_hw_id_ktype = {
+	.release = ip_hw_id_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+};
+
+/* -------------------------------------------------- */
+
+static void die_kobj_release(struct kobject *kobj);
+static void ip_disc_release(struct kobject *kobj);
+
+struct ip_die_entry_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct ip_die_entry *ip_die_entry, char *buf);
+};
+
+#define to_ip_die_entry_attr(x)  container_of(x, struct ip_die_entry_attribute, attr)
+
+static ssize_t num_ips_show(struct ip_die_entry *ip_die_entry, char *buf)
+{
+	return sprintf(buf, "%d\n", ip_die_entry->num_ips);
+}
+
+/* If there are more ip_die_entry attrs, other than the number of IPs,
+ * we can make this intro an array of attrs, and then initialize
+ * ip_die_entry_attrs in a loop.
+ */
+static struct ip_die_entry_attribute num_ips_attr =
+	__ATTR_RO(num_ips);
+
+static struct attribute *ip_die_entry_attrs[] = {
+	&num_ips_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(ip_die_entry); /* ip_die_entry_groups */
+
+#define to_ip_die_entry(x) container_of(to_kset(x), struct ip_die_entry, ip_kset)
+
+static ssize_t ip_die_entry_attr_show(struct kobject *kobj,
+				      struct attribute *attr,
+				      char *buf)
+{
+	struct ip_die_entry_attribute *ip_die_entry_attr = to_ip_die_entry_attr(attr);
+	struct ip_die_entry *ip_die_entry = to_ip_die_entry(kobj);
+
+	if (!ip_die_entry_attr->show)
+		return -EIO;
+
+	return ip_die_entry_attr->show(ip_die_entry, buf);
+}
+
+static void ip_die_entry_release(struct kobject *kobj)
+{
+	struct ip_die_entry *ip_die_entry = to_ip_die_entry(kobj);
+
+	if (!list_empty(&ip_die_entry->ip_kset.list))
+		DRM_ERROR("ip_die_entry->ip_kset is not empty");
+	kfree(ip_die_entry);
+}
+
+static const struct sysfs_ops ip_die_entry_sysfs_ops = {
+	.show = ip_die_entry_attr_show,
+};
+
+static struct kobj_type ip_die_entry_ktype = {
+	.release = ip_die_entry_release,
+	.sysfs_ops = &ip_die_entry_sysfs_ops,
+	.default_groups = ip_die_entry_groups,
+};
+
+static struct kobj_type die_kobj_ktype = {
+	.release = die_kobj_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+};
+
+static struct kobj_type ip_discovery_ktype = {
+	.release = ip_disc_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+};
+
+struct ip_discovery_top {
+	struct kobject kobj;    /* ip_discovery/ */
+	struct kset die_kset;   /* ip_discovery/die/, contains ip_die_entry */
+	struct amdgpu_device *adev;
+};
+
+static void die_kobj_release(struct kobject *kobj)
+{
+	struct ip_discovery_top *ip_top = container_of(to_kset(kobj),
+						       struct ip_discovery_top,
+						       die_kset);
+	if (!list_empty(&ip_top->die_kset.list))
+		DRM_ERROR("ip_top->die_kset is not empty");
+}
+
+static void ip_disc_release(struct kobject *kobj)
+{
+	struct ip_discovery_top *ip_top = container_of(kobj, struct ip_discovery_top,
+						       kobj);
+	struct amdgpu_device *adev = ip_top->adev;
+
+	adev->ip_top = NULL;
+	kfree(ip_top);
+}
+
+static int amdgpu_discovery_sysfs_ips(struct amdgpu_device *adev,
+				      struct ip_die_entry *ip_die_entry,
+				      const size_t _ip_offset, const int num_ips)
+{
+	int ii, jj, kk, res;
+
+	DRM_DEBUG("num_ips:%d", num_ips);
+
+	/* Find all IPs of a given HW ID, and add their instance to
+	 * #die/#hw_id/#instance/<attributes>
+	 */
+	for (ii = 0; ii < HW_ID_MAX; ii++) {
+		struct ip_hw_id *ip_hw_id = NULL;
+		size_t ip_offset = _ip_offset;
+
+		for (jj = 0; jj < num_ips; jj++) {
+			struct ip *ip;
+			struct ip_hw_instance *ip_hw_instance;
+
+			ip = (struct ip *)(adev->mman.discovery_bin + ip_offset);
+			if (amdgpu_discovery_validate_ip(ip) ||
+			    le16_to_cpu(ip->hw_id) != ii)
+				goto next_ip;
+
+			DRM_DEBUG("match:%d @ ip_offset:%ld", ii, ip_offset);
+
+			/* We have a hw_id match; register the hw
+			 * block if not yet registered.
+			 */
+			if (!ip_hw_id) {
+				ip_hw_id = kzalloc(sizeof(*ip_hw_id), GFP_KERNEL);
+				if (!ip_hw_id)
+					return -ENOMEM;
+				ip_hw_id->hw_id = ii;
+
+				kobject_set_name(&ip_hw_id->hw_id_kset.kobj, "%d", ii);
+				ip_hw_id->hw_id_kset.kobj.kset = &ip_die_entry->ip_kset;
+				ip_hw_id->hw_id_kset.kobj.ktype = &ip_hw_id_ktype;
+				res = kset_register(&ip_hw_id->hw_id_kset);
+				if (res) {
+					DRM_ERROR("Couldn't register ip_hw_id kset");
+					kfree(ip_hw_id);
+					return res;
+				}
+				if (hw_id_names[ii]) {
+					res = sysfs_create_link(&ip_die_entry->ip_kset.kobj,
+								&ip_hw_id->hw_id_kset.kobj,
+								hw_id_names[ii]);
+					if (res) {
+						DRM_ERROR("Couldn't create IP link %s in IP Die:%s\n",
+							  hw_id_names[ii],
+							  kobject_name(&ip_die_entry->ip_kset.kobj));
+					}
+				}
+			}
+
+			/* Now register its instance.
+			 */
+			ip_hw_instance = kzalloc(sizeof(*ip_hw_instance) +
+						 sizeof(u32) * ip->num_base_address,
+						 GFP_KERNEL);
+			if (!ip_hw_instance) {
+				DRM_ERROR("no memory for ip_hw_instance");
+				return -ENOMEM;
+			}
+			ip_hw_instance->hw_id = le16_to_cpu(ip->hw_id); /* == ii */
+			ip_hw_instance->num_instance = ip->number_instance;
+			ip_hw_instance->major = ip->major;
+			ip_hw_instance->minor = ip->minor;
+			ip_hw_instance->revision = ip->revision;
+			ip_hw_instance->num_base_addresses = ip->num_base_address;
+
+			for (kk = 0; kk < ip_hw_instance->num_base_addresses; kk++)
+				ip_hw_instance->base_addr[kk] = ip->base_address[kk];
+
+			kobject_init(&ip_hw_instance->kobj, &ip_hw_instance_ktype);
+			ip_hw_instance->kobj.kset = &ip_hw_id->hw_id_kset;
+			res = kobject_add(&ip_hw_instance->kobj, NULL,
+					  "%d", ip_hw_instance->num_instance);
+next_ip:
+			ip_offset += sizeof(*ip) + 4 * (ip->num_base_address - 1);
+		}
+	}
+
+	return 0;
+}
+
+static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev)
+{
+	struct binary_header *bhdr;
+	struct ip_discovery_header *ihdr;
+	struct die_header *dhdr;
+	struct kset *die_kset = &adev->ip_top->die_kset;
+	u16 num_dies, die_offset, num_ips;
+	size_t ip_offset;
+	int ii, res;
+
+	bhdr = (struct binary_header *)adev->mman.discovery_bin;
+	ihdr = (struct ip_discovery_header *)(adev->mman.discovery_bin +
+					      le16_to_cpu(bhdr->table_list[IP_DISCOVERY].offset));
+	num_dies = le16_to_cpu(ihdr->num_dies);
+
+	DRM_DEBUG("number of dies: %d\n", num_dies);
+
+	for (ii = 0; ii < num_dies; ii++) {
+		struct ip_die_entry *ip_die_entry;
+
+		die_offset = le16_to_cpu(ihdr->die_info[ii].die_offset);
+		dhdr = (struct die_header *)(adev->mman.discovery_bin + die_offset);
+		num_ips = le16_to_cpu(dhdr->num_ips);
+		ip_offset = die_offset + sizeof(*dhdr);
+
+		/* Add the die to the kset.
+		 *
+		 * dhdr->die_id == ii, which was checked in
+		 * amdgpu_discovery_reg_base_init().
+		 */
+
+		ip_die_entry = kzalloc(sizeof(*ip_die_entry), GFP_KERNEL);
+		if (!ip_die_entry)
+			return -ENOMEM;
+
+		ip_die_entry->num_ips = num_ips;
+
+		kobject_set_name(&ip_die_entry->ip_kset.kobj, "%d", le16_to_cpu(dhdr->die_id));
+		ip_die_entry->ip_kset.kobj.kset = die_kset;
+		ip_die_entry->ip_kset.kobj.ktype = &ip_die_entry_ktype;
+		res = kset_register(&ip_die_entry->ip_kset);
+		if (res) {
+			DRM_ERROR("Couldn't register ip_die_entry kset");
+			kfree(ip_die_entry);
+			return res;
+		}
+
+		amdgpu_discovery_sysfs_ips(adev, ip_die_entry, ip_offset, num_ips);
+	}
+
+	return 0;
+}
+
+static int amdgpu_discovery_sysfs_init(struct amdgpu_device *adev)
+{
+	struct kset *die_kset;
+	int res;
+
+	adev->ip_top = kzalloc(sizeof(*adev->ip_top), GFP_KERNEL);
+	if (!adev->ip_top)
+		return -ENOMEM;
+
+	adev->ip_top->adev = adev;
+
+	res = kobject_init_and_add(&adev->ip_top->kobj, &ip_discovery_ktype,
+				   &adev->dev->kobj, "ip_discovery");
+	if (res) {
+		DRM_ERROR("Couldn't init and add ip_discovery/");
+		goto Err;
+	}
+
+	die_kset = &adev->ip_top->die_kset;
+	kobject_set_name(&die_kset->kobj, "%s", "die");
+	die_kset->kobj.parent = &adev->ip_top->kobj;
+	die_kset->kobj.ktype = &die_kobj_ktype;
+	res = kset_register(&adev->ip_top->die_kset);
+	if (res) {
+		DRM_ERROR("Couldn't register die_kset");
+		goto Err;
+	}
+
+	res = amdgpu_discovery_sysfs_recurse(adev);
+
+	return res;
+Err:
+	kobject_put(&adev->ip_top->kobj);
+	return res;
+}
+
+/* -------------------------------------------------- */
+
+#define list_to_kobj(el) container_of(el, struct kobject, entry)
+
+static void amdgpu_discovery_sysfs_ip_hw_free(struct ip_hw_id *ip_hw_id)
+{
+	struct list_head *el, *tmp;
+	struct kset *hw_id_kset;
+
+	hw_id_kset = &ip_hw_id->hw_id_kset;
+	spin_lock(&hw_id_kset->list_lock);
+	list_for_each_prev_safe(el, tmp, &hw_id_kset->list) {
+		list_del_init(el);
+		spin_unlock(&hw_id_kset->list_lock);
+		/* kobject is embedded in ip_hw_instance */
+		kobject_put(list_to_kobj(el));
+		spin_lock(&hw_id_kset->list_lock);
+	}
+	spin_unlock(&hw_id_kset->list_lock);
+	kobject_put(&ip_hw_id->hw_id_kset.kobj);
+}
+
+static void amdgpu_discovery_sysfs_die_free(struct ip_die_entry *ip_die_entry)
+{
+	struct list_head *el, *tmp;
+	struct kset *ip_kset;
+
+	ip_kset = &ip_die_entry->ip_kset;
+	spin_lock(&ip_kset->list_lock);
+	list_for_each_prev_safe(el, tmp, &ip_kset->list) {
+		list_del_init(el);
+		spin_unlock(&ip_kset->list_lock);
+		amdgpu_discovery_sysfs_ip_hw_free(to_ip_hw_id(list_to_kobj(el)));
+		spin_lock(&ip_kset->list_lock);
+	}
+	spin_unlock(&ip_kset->list_lock);
+	kobject_put(&ip_die_entry->ip_kset.kobj);
+}
+
+static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev)
+{
+	struct list_head *el, *tmp;
+	struct kset *die_kset;
+
+	die_kset = &adev->ip_top->die_kset;
+	spin_lock(&die_kset->list_lock);
+	list_for_each_prev_safe(el, tmp, &die_kset->list) {
+		list_del_init(el);
+		spin_unlock(&die_kset->list_lock);
+		amdgpu_discovery_sysfs_die_free(to_ip_die_entry(list_to_kobj(el)));
+		spin_lock(&die_kset->list_lock);
+	}
+	spin_unlock(&die_kset->list_lock);
+	kobject_put(&adev->ip_top->die_kset.kobj);
+	kobject_put(&adev->ip_top->kobj);
+}
+
+/* ================================================== */
+
 int amdgpu_discovery_reg_base_init(struct amdgpu_device *adev)
 {
 	struct binary_header *bhdr;
@@ -492,6 +976,8 @@ int amdgpu_discovery_reg_base_init(struct amdgpu_device *adev)
 		}
 	}
 
+	amdgpu_discovery_sysfs_init(adev);
+
 	return 0;
 }
 
-- 
2.35.0.3.gb23dac905b


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v1 1/1] drm/amdgpu: Show IP discovery in sysfs
  2022-02-09 22:51 ` [PATCH v1 1/1] " Luben Tuikov
@ 2022-02-10  3:43   ` Wang, Yang(Kevin)
  2022-02-10  4:48   ` Lang Yu
  1 sibling, 0 replies; 4+ messages in thread
From: Wang, Yang(Kevin) @ 2022-02-10  3:43 UTC (permalink / raw)
  To: Tuikov, Luben, amd-gfx; +Cc: Deucher, Alexander, StDenis, Tom

[-- Attachment #1: Type: text/plain, Size: 21402 bytes --]

[AMD Official Use Only]



________________________________
From: amd-gfx <amd-gfx-bounces@lists.freedesktop.org> on behalf of Luben Tuikov <luben.tuikov@amd.com>
Sent: Thursday, February 10, 2022 6:51 AM
To: amd-gfx@lists.freedesktop.org <amd-gfx@lists.freedesktop.org>
Cc: Deucher, Alexander <Alexander.Deucher@amd.com>; StDenis, Tom <Tom.StDenis@amd.com>; Tuikov, Luben <Luben.Tuikov@amd.com>
Subject: [PATCH v1 1/1] drm/amdgpu: Show IP discovery in sysfs

Add IP discovery data in sysfs. The format is:
/sys/class/drm/cardX/device/ip_discovery/die/D/B/I/<attrs>
where,
X is the card ID, an integer,
D is the die ID, an integer,
B is the IP HW ID, an integer, aka block type,
I is the IP HW ID instance, an integer.
<attrs> are the attributes of the block instance. At the moment these
include HW ID, instance number, major, minor, revision, number of base
addresses, and the base addresses themselves.

A symbolic link of the acronym HW ID is also created, under D/, if you
prefer to browse by something humanly accessible.

Cc: Alex Deucher <Alexander.Deucher@amd.com>
Cc: Tom StDenis <tom.stdenis@amd.com>
Signed-off-by: Luben Tuikov <luben.tuikov@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu.h           |   4 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 486 ++++++++++++++++++
 2 files changed, 490 insertions(+)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index e4eb812ade2fa4..3a126dce8a2fe9 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -772,6 +772,8 @@ struct amd_powerplay {
         const struct amd_pm_funcs *pp_funcs;
 };

+struct ip_discovery_top;
+
 /* polaris10 kickers */
 #define ASICID_IS_P20(did, rid)         (((did == 0x67DF) && \
                                          ((rid == 0xE3) || \
@@ -1097,6 +1099,8 @@ struct amdgpu_device {
         bool                            ram_is_direct_mapped;

         struct list_head                ras_list;
+
+       struct ip_discovery_top         *ip_top;
 };

 static inline struct amdgpu_device *drm_to_adev(struct drm_device *ddev)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
index 07623634fdc2f1..46e6e1352574f6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
@@ -360,8 +360,11 @@ static int amdgpu_discovery_init(struct amdgpu_device *adev)
         return r;
 }

+static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev);
+
 void amdgpu_discovery_fini(struct amdgpu_device *adev)
 {
+       amdgpu_discovery_sysfs_fini(adev);
         kfree(adev->mman.discovery_bin);
         adev->mman.discovery_bin = NULL;
 }
@@ -382,6 +385,487 @@ static int amdgpu_discovery_validate_ip(const struct ip *ip)
         return 0;
 }

+/* ================================================== */
+
+struct ip_hw_instance {
+       struct kobject kobj; /* ip_discovery/die/#die/#hw_id/#instance/<attrs...> */
+
+       int hw_id;
+       u8  num_instance;
+       u8  major, minor, revision;
+
+       int num_base_addresses;
+       u32 base_addr[0];
+};

[kevin]:

  1.  use flex-array instead of zero length array is better to match kernel coding style and avoid some compiler warning. eg: "u32 base_addr[];"
  2.  please use following macro helper to handle array size.
// include/linux/overflow.h
- struct_size() - Calculate size of structure with trailing array.
  3.  - flex_array_size() - Calculate size of a flexible array member within an enclosing structure.

On the other hand, do we really need variable length arrays?  why not fix size to 4?
+
+struct ip_hw_id {
+       struct kset hw_id_kset;  /* ip_discovery/die/#die/#hw_id/, contains ip_hw_instance */
+       int hw_id;
+};
+
+struct ip_die_entry {
+       struct kset ip_kset;     /* ip_discovery/die/#die/, contains ip_hw_id  */
+       u16 num_ips;
+};
+
+/* -------------------------------------------------- */
+
+struct ip_hw_instance_attr {
+       struct attribute attr;
+       ssize_t (*show)(struct ip_hw_instance *ip_hw_instance, char *buf);
+};
+
+static ssize_t hw_id_show(struct ip_hw_instance *ip_hw_instance, char *buf)
+{
+       return sprintf(buf, "%d\n", ip_hw_instance->hw_id);
+}
+
+static ssize_t num_instance_show(struct ip_hw_instance *ip_hw_instance, char *buf)
+{
+       return sprintf(buf, "%d\n", ip_hw_instance->num_instance);
+}
+
+static ssize_t major_show(struct ip_hw_instance *ip_hw_instance, char *buf)
+{
+       return sprintf(buf, "%d\n", ip_hw_instance->major);
+}
+
+static ssize_t minor_show(struct ip_hw_instance *ip_hw_instance, char *buf)
+{
+       return sprintf(buf, "%d\n", ip_hw_instance->minor);
+}
+
+static ssize_t revision_show(struct ip_hw_instance *ip_hw_instance, char *buf)
+{
+       return sprintf(buf, "%d\n", ip_hw_instance->revision);
+}
+
+static ssize_t num_base_addresses_show(struct ip_hw_instance *ip_hw_instance, char *buf)
+{
+       return sprintf(buf, "%d\n", ip_hw_instance->num_base_addresses);
+}
+
+static ssize_t base_addr_show(struct ip_hw_instance *ip_hw_instance, char *buf)
+{
+       ssize_t res = 0;
+       int ii;
+
+       for (ii = 0; ii < ip_hw_instance->num_base_addresses; ii++) {
+               if (res + 12 >= PAGE_SIZE)
+                       break;
+               res += sprintf(buf + res, "0x%08X\n", ip_hw_instance->base_addr[ii]);
+       }
+
+       return res;
+}
+
+static struct ip_hw_instance_attr ip_hw_attr[] = {
+       __ATTR_RO(hw_id),
+       __ATTR_RO(num_instance),
+       __ATTR_RO(major),
+       __ATTR_RO(minor),
+       __ATTR_RO(revision),
+       __ATTR_RO(num_base_addresses),
+       __ATTR_RO(base_addr),
+};
+
+static struct attribute *ip_hw_instance_attrs[] = {
+       &ip_hw_attr[0].attr,
+       &ip_hw_attr[1].attr,
+       &ip_hw_attr[2].attr,
+       &ip_hw_attr[3].attr,
+       &ip_hw_attr[4].attr,
+       &ip_hw_attr[5].attr,
+       &ip_hw_attr[6].attr,
+       NULL,
+};
+ATTRIBUTE_GROUPS(ip_hw_instance);
+
+#define to_ip_hw_instance(x) container_of(x, struct ip_hw_instance, kobj)
+#define to_ip_hw_instance_attr(x) container_of(x, struct ip_hw_instance_attr, attr)
+
+static ssize_t ip_hw_instance_attr_show(struct kobject *kobj,
+                                       struct attribute *attr,
+                                       char *buf)
+{
+       struct ip_hw_instance *ip_hw_instance = to_ip_hw_instance(kobj);
+       struct ip_hw_instance_attr *ip_hw_attr = to_ip_hw_instance_attr(attr);
+
+       if (!ip_hw_attr->show)
+               return -EIO;
+
+       return ip_hw_attr->show(ip_hw_instance, buf);
+}
+
+static const struct sysfs_ops ip_hw_instance_sysfs_ops = {
+       .show = ip_hw_instance_attr_show,
+};
+
+static void ip_hw_instance_release(struct kobject *kobj)
+{
+       struct ip_hw_instance *ip_hw_instance = to_ip_hw_instance(kobj);
+
+       kfree(ip_hw_instance);
+}
+
+static struct kobj_type ip_hw_instance_ktype = {
+       .release = ip_hw_instance_release,
+       .sysfs_ops = &ip_hw_instance_sysfs_ops,
+       .default_groups = ip_hw_instance_groups,
+};
+
+/* -------------------------------------------------- */
+
+#define to_ip_hw_id(x)  container_of(to_kset(x), struct ip_hw_id, hw_id_kset)
+
+static void ip_hw_id_release(struct kobject *kobj)
+{
+       struct ip_hw_id *ip_hw_id = to_ip_hw_id(kobj);
+
+       if (!list_empty(&ip_hw_id->hw_id_kset.list))
+               DRM_ERROR("ip_hw_id->hw_id_kset is not empty");
+       kfree(ip_hw_id);
+}
+
+static struct kobj_type ip_hw_id_ktype = {
+       .release = ip_hw_id_release,
+       .sysfs_ops = &kobj_sysfs_ops,
+};
+
+/* -------------------------------------------------- */
+
+static void die_kobj_release(struct kobject *kobj);
+static void ip_disc_release(struct kobject *kobj);
+
+struct ip_die_entry_attribute {
+       struct attribute attr;
+       ssize_t (*show)(struct ip_die_entry *ip_die_entry, char *buf);
+};
+
+#define to_ip_die_entry_attr(x)  container_of(x, struct ip_die_entry_attribute, attr)
+
+static ssize_t num_ips_show(struct ip_die_entry *ip_die_entry, char *buf)
+{
+       return sprintf(buf, "%d\n", ip_die_entry->num_ips);
+}
+
+/* If there are more ip_die_entry attrs, other than the number of IPs,
+ * we can make this intro an array of attrs, and then initialize
+ * ip_die_entry_attrs in a loop.
+ */
+static struct ip_die_entry_attribute num_ips_attr =
+       __ATTR_RO(num_ips);
+
+static struct attribute *ip_die_entry_attrs[] = {
+       &num_ips_attr.attr,
+       NULL,
+};
+ATTRIBUTE_GROUPS(ip_die_entry); /* ip_die_entry_groups */
+
+#define to_ip_die_entry(x) container_of(to_kset(x), struct ip_die_entry, ip_kset)
+
+static ssize_t ip_die_entry_attr_show(struct kobject *kobj,
+                                     struct attribute *attr,
+                                     char *buf)
+{
+       struct ip_die_entry_attribute *ip_die_entry_attr = to_ip_die_entry_attr(attr);
+       struct ip_die_entry *ip_die_entry = to_ip_die_entry(kobj);
+
+       if (!ip_die_entry_attr->show)
+               return -EIO;
+
+       return ip_die_entry_attr->show(ip_die_entry, buf);
+}
+
+static void ip_die_entry_release(struct kobject *kobj)
+{
+       struct ip_die_entry *ip_die_entry = to_ip_die_entry(kobj);
+
+       if (!list_empty(&ip_die_entry->ip_kset.list))
+               DRM_ERROR("ip_die_entry->ip_kset is not empty");
+       kfree(ip_die_entry);
+}
+
+static const struct sysfs_ops ip_die_entry_sysfs_ops = {
+       .show = ip_die_entry_attr_show,
+};
+
+static struct kobj_type ip_die_entry_ktype = {
+       .release = ip_die_entry_release,
+       .sysfs_ops = &ip_die_entry_sysfs_ops,
+       .default_groups = ip_die_entry_groups,
+};
+
+static struct kobj_type die_kobj_ktype = {
+       .release = die_kobj_release,
+       .sysfs_ops = &kobj_sysfs_ops,
+};
+
+static struct kobj_type ip_discovery_ktype = {
+       .release = ip_disc_release,
+       .sysfs_ops = &kobj_sysfs_ops,
+};
+
+struct ip_discovery_top {
+       struct kobject kobj;    /* ip_discovery/ */
+       struct kset die_kset;   /* ip_discovery/die/, contains ip_die_entry */
+       struct amdgpu_device *adev;
+};
+
+static void die_kobj_release(struct kobject *kobj)
+{
+       struct ip_discovery_top *ip_top = container_of(to_kset(kobj),
+                                                      struct ip_discovery_top,
+                                                      die_kset);
+       if (!list_empty(&ip_top->die_kset.list))
+               DRM_ERROR("ip_top->die_kset is not empty");
+}
+
+static void ip_disc_release(struct kobject *kobj)
+{
+       struct ip_discovery_top *ip_top = container_of(kobj, struct ip_discovery_top,
+                                                      kobj);
+       struct amdgpu_device *adev = ip_top->adev;
+
+       adev->ip_top = NULL;
+       kfree(ip_top);
+}
+
+static int amdgpu_discovery_sysfs_ips(struct amdgpu_device *adev,
+                                     struct ip_die_entry *ip_die_entry,
+                                     const size_t _ip_offset, const int num_ips)
+{
+       int ii, jj, kk, res;
+
+       DRM_DEBUG("num_ips:%d", num_ips);
+
+       /* Find all IPs of a given HW ID, and add their instance to
+        * #die/#hw_id/#instance/<attributes>
+        */
+       for (ii = 0; ii < HW_ID_MAX; ii++) {
+               struct ip_hw_id *ip_hw_id = NULL;
+               size_t ip_offset = _ip_offset;
+
+               for (jj = 0; jj < num_ips; jj++) {
+                       struct ip *ip;
+                       struct ip_hw_instance *ip_hw_instance;
+
+                       ip = (struct ip *)(adev->mman.discovery_bin + ip_offset);
+                       if (amdgpu_discovery_validate_ip(ip) ||
+                           le16_to_cpu(ip->hw_id) != ii)
+                               goto next_ip;
+
+                       DRM_DEBUG("match:%d @ ip_offset:%ld", ii, ip_offset);
+
+                       /* We have a hw_id match; register the hw
+                        * block if not yet registered.
+                        */
+                       if (!ip_hw_id) {
+                               ip_hw_id = kzalloc(sizeof(*ip_hw_id), GFP_KERNEL);
+                               if (!ip_hw_id)
+                                       return -ENOMEM;
+                               ip_hw_id->hw_id = ii;
+
+                               kobject_set_name(&ip_hw_id->hw_id_kset.kobj, "%d", ii);
+                               ip_hw_id->hw_id_kset.kobj.kset = &ip_die_entry->ip_kset;
+                               ip_hw_id->hw_id_kset.kobj.ktype = &ip_hw_id_ktype;
+                               res = kset_register(&ip_hw_id->hw_id_kset);
+                               if (res) {
+                                       DRM_ERROR("Couldn't register ip_hw_id kset");
+                                       kfree(ip_hw_id);
+                                       return res;
+                               }
+                               if (hw_id_names[ii]) {
+                                       res = sysfs_create_link(&ip_die_entry->ip_kset.kobj,
+                                                               &ip_hw_id->hw_id_kset.kobj,
+                                                               hw_id_names[ii]);
+                                       if (res) {
+                                               DRM_ERROR("Couldn't create IP link %s in IP Die:%s\n",
+                                                         hw_id_names[ii],
+                                                         kobject_name(&ip_die_entry->ip_kset.kobj));
+                                       }
+                               }
+                       }
+
+                       /* Now register its instance.
+                        */
+                       ip_hw_instance = kzalloc(sizeof(*ip_hw_instance) +
+                                                sizeof(u32) * ip->num_base_address,
+                                                GFP_KERNEL);
+                       if (!ip_hw_instance) {
+                               DRM_ERROR("no memory for ip_hw_instance");
+                               return -ENOMEM;
+                       }
+                       ip_hw_instance->hw_id = le16_to_cpu(ip->hw_id); /* == ii */
+                       ip_hw_instance->num_instance = ip->number_instance;
+                       ip_hw_instance->major = ip->major;
+                       ip_hw_instance->minor = ip->minor;
+                       ip_hw_instance->revision = ip->revision;
+                       ip_hw_instance->num_base_addresses = ip->num_base_address;
+
+                       for (kk = 0; kk < ip_hw_instance->num_base_addresses; kk++)
+                               ip_hw_instance->base_addr[kk] = ip->base_address[kk];
+
+                       kobject_init(&ip_hw_instance->kobj, &ip_hw_instance_ktype);
+                       ip_hw_instance->kobj.kset = &ip_hw_id->hw_id_kset;
+                       res = kobject_add(&ip_hw_instance->kobj, NULL,
+                                         "%d", ip_hw_instance->num_instance);
+next_ip:
+                       ip_offset += sizeof(*ip) + 4 * (ip->num_base_address - 1);
+               }
+       }
+
+       return 0;
+}
+
+static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev)
+{
+       struct binary_header *bhdr;
+       struct ip_discovery_header *ihdr;
+       struct die_header *dhdr;
+       struct kset *die_kset = &adev->ip_top->die_kset;
+       u16 num_dies, die_offset, num_ips;
+       size_t ip_offset;
+       int ii, res;
+
+       bhdr = (struct binary_header *)adev->mman.discovery_bin;
+       ihdr = (struct ip_discovery_header *)(adev->mman.discovery_bin +
+                                             le16_to_cpu(bhdr->table_list[IP_DISCOVERY].offset));
+       num_dies = le16_to_cpu(ihdr->num_dies);
+
+       DRM_DEBUG("number of dies: %d\n", num_dies);
+
+       for (ii = 0; ii < num_dies; ii++) {
+               struct ip_die_entry *ip_die_entry;
+
+               die_offset = le16_to_cpu(ihdr->die_info[ii].die_offset);
+               dhdr = (struct die_header *)(adev->mman.discovery_bin + die_offset);
+               num_ips = le16_to_cpu(dhdr->num_ips);
+               ip_offset = die_offset + sizeof(*dhdr);
+
+               /* Add the die to the kset.
+                *
+                * dhdr->die_id == ii, which was checked in
+                * amdgpu_discovery_reg_base_init().
+                */
+
+               ip_die_entry = kzalloc(sizeof(*ip_die_entry), GFP_KERNEL);
+               if (!ip_die_entry)
+                       return -ENOMEM;
+
+               ip_die_entry->num_ips = num_ips;
+
+               kobject_set_name(&ip_die_entry->ip_kset.kobj, "%d", le16_to_cpu(dhdr->die_id));
+               ip_die_entry->ip_kset.kobj.kset = die_kset;
+               ip_die_entry->ip_kset.kobj.ktype = &ip_die_entry_ktype;
+               res = kset_register(&ip_die_entry->ip_kset);
+               if (res) {
+                       DRM_ERROR("Couldn't register ip_die_entry kset");
+                       kfree(ip_die_entry);
+                       return res;
+               }
+
+               amdgpu_discovery_sysfs_ips(adev, ip_die_entry, ip_offset, num_ips);
+       }
+
+       return 0;
+}
+
+static int amdgpu_discovery_sysfs_init(struct amdgpu_device *adev)
+{
+       struct kset *die_kset;
+       int res;
+
+       adev->ip_top = kzalloc(sizeof(*adev->ip_top), GFP_KERNEL);
+       if (!adev->ip_top)
+               return -ENOMEM;
+
+       adev->ip_top->adev = adev;
+
+       res = kobject_init_and_add(&adev->ip_top->kobj, &ip_discovery_ktype,
+                                  &adev->dev->kobj, "ip_discovery");
+       if (res) {
+               DRM_ERROR("Couldn't init and add ip_discovery/");
+               goto Err;
+       }
+
+       die_kset = &adev->ip_top->die_kset;
+       kobject_set_name(&die_kset->kobj, "%s", "die");
+       die_kset->kobj.parent = &adev->ip_top->kobj;
+       die_kset->kobj.ktype = &die_kobj_ktype;
+       res = kset_register(&adev->ip_top->die_kset);
+       if (res) {
+               DRM_ERROR("Couldn't register die_kset");
+               goto Err;
+       }
+
+       res = amdgpu_discovery_sysfs_recurse(adev);
+
+       return res;
+Err:
+       kobject_put(&adev->ip_top->kobj);
+       return res;
+}
+
+/* -------------------------------------------------- */
+
+#define list_to_kobj(el) container_of(el, struct kobject, entry)
+
+static void amdgpu_discovery_sysfs_ip_hw_free(struct ip_hw_id *ip_hw_id)
+{
+       struct list_head *el, *tmp;
+       struct kset *hw_id_kset;
+
+       hw_id_kset = &ip_hw_id->hw_id_kset;
+       spin_lock(&hw_id_kset->list_lock);
+       list_for_each_prev_safe(el, tmp, &hw_id_kset->list) {
+               list_del_init(el);
+               spin_unlock(&hw_id_kset->list_lock);
+               /* kobject is embedded in ip_hw_instance */
+               kobject_put(list_to_kobj(el));
+               spin_lock(&hw_id_kset->list_lock);
+       }
+       spin_unlock(&hw_id_kset->list_lock);
+       kobject_put(&ip_hw_id->hw_id_kset.kobj);
+}
+
+static void amdgpu_discovery_sysfs_die_free(struct ip_die_entry *ip_die_entry)
+{
+       struct list_head *el, *tmp;
+       struct kset *ip_kset;
+
+       ip_kset = &ip_die_entry->ip_kset;
+       spin_lock(&ip_kset->list_lock);
+       list_for_each_prev_safe(el, tmp, &ip_kset->list) {
+               list_del_init(el);
+               spin_unlock(&ip_kset->list_lock);
+               amdgpu_discovery_sysfs_ip_hw_free(to_ip_hw_id(list_to_kobj(el)));
+               spin_lock(&ip_kset->list_lock);
+       }
+       spin_unlock(&ip_kset->list_lock);
+       kobject_put(&ip_die_entry->ip_kset.kobj);
+}
+
+static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev)
+{
+       struct list_head *el, *tmp;
+       struct kset *die_kset;
+
+       die_kset = &adev->ip_top->die_kset;
+       spin_lock(&die_kset->list_lock);
+       list_for_each_prev_safe(el, tmp, &die_kset->list) {
+               list_del_init(el);
+               spin_unlock(&die_kset->list_lock);
+               amdgpu_discovery_sysfs_die_free(to_ip_die_entry(list_to_kobj(el)));
+               spin_lock(&die_kset->list_lock);
+       }
+       spin_unlock(&die_kset->list_lock);
+       kobject_put(&adev->ip_top->die_kset.kobj);
+       kobject_put(&adev->ip_top->kobj);
+}
+
+/* ================================================== */
+
 int amdgpu_discovery_reg_base_init(struct amdgpu_device *adev)
 {
         struct binary_header *bhdr;
@@ -492,6 +976,8 @@ int amdgpu_discovery_reg_base_init(struct amdgpu_device *adev)
                 }
         }

+       amdgpu_discovery_sysfs_init(adev);
+
         return 0;
 }

--
2.35.0.3.gb23dac905b


[-- Attachment #2: Type: text/html, Size: 50021 bytes --]

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v1 1/1] drm/amdgpu: Show IP discovery in sysfs
  2022-02-09 22:51 ` [PATCH v1 1/1] " Luben Tuikov
  2022-02-10  3:43   ` Wang, Yang(Kevin)
@ 2022-02-10  4:48   ` Lang Yu
  1 sibling, 0 replies; 4+ messages in thread
From: Lang Yu @ 2022-02-10  4:48 UTC (permalink / raw)
  To: Luben Tuikov; +Cc: Alex Deucher, Tom StDenis, amd-gfx

On 02/09/ , Luben Tuikov wrote:
> Add IP discovery data in sysfs. The format is:
> /sys/class/drm/cardX/device/ip_discovery/die/D/B/I/<attrs>
> where,
> X is the card ID, an integer,
> D is the die ID, an integer,
> B is the IP HW ID, an integer, aka block type,
> I is the IP HW ID instance, an integer.
> <attrs> are the attributes of the block instance. At the moment these
> include HW ID, instance number, major, minor, revision, number of base
> addresses, and the base addresses themselves.
> 
> A symbolic link of the acronym HW ID is also created, under D/, if you
> prefer to browse by something humanly accessible.
> 
> Cc: Alex Deucher <Alexander.Deucher@amd.com>
> Cc: Tom StDenis <tom.stdenis@amd.com>
> Signed-off-by: Luben Tuikov <luben.tuikov@amd.com>
> ---
>  drivers/gpu/drm/amd/amdgpu/amdgpu.h           |   4 +
>  drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 486 ++++++++++++++++++
>  2 files changed, 490 insertions(+)
> 
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
> index e4eb812ade2fa4..3a126dce8a2fe9 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
> @@ -772,6 +772,8 @@ struct amd_powerplay {
>  	const struct amd_pm_funcs *pp_funcs;
>  };
>  
> +struct ip_discovery_top;
> +
>  /* polaris10 kickers */
>  #define ASICID_IS_P20(did, rid)		(((did == 0x67DF) && \
>  					 ((rid == 0xE3) || \
> @@ -1097,6 +1099,8 @@ struct amdgpu_device {
>  	bool				ram_is_direct_mapped;
>  
>  	struct list_head                ras_list;
> +
> +	struct ip_discovery_top         *ip_top;
>  };
>  
>  static inline struct amdgpu_device *drm_to_adev(struct drm_device *ddev)
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
> index 07623634fdc2f1..46e6e1352574f6 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
> @@ -360,8 +360,11 @@ static int amdgpu_discovery_init(struct amdgpu_device *adev)
>  	return r;
>  }
>  
> +static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev);
> +
>  void amdgpu_discovery_fini(struct amdgpu_device *adev)
>  {
> +	amdgpu_discovery_sysfs_fini(adev);
>  	kfree(adev->mman.discovery_bin);
>  	adev->mman.discovery_bin = NULL;
>  }
> @@ -382,6 +385,487 @@ static int amdgpu_discovery_validate_ip(const struct ip *ip)
>  	return 0;
>  }
>  
> +/* ================================================== */
> +
> +struct ip_hw_instance {
> +	struct kobject kobj; /* ip_discovery/die/#die/#hw_id/#instance/<attrs...> */
> +
> +	int hw_id;
> +	u8  num_instance;
> +	u8  major, minor, revision;
> +
> +	int num_base_addresses;
> +	u32 base_addr[0];
> +};
> +
> +struct ip_hw_id {
> +	struct kset hw_id_kset;  /* ip_discovery/die/#die/#hw_id/, contains ip_hw_instance */
> +	int hw_id;
> +};
> +
> +struct ip_die_entry {
> +	struct kset ip_kset;     /* ip_discovery/die/#die/, contains ip_hw_id  */
> +	u16 num_ips;
> +};
> +
> +/* -------------------------------------------------- */
> +
> +struct ip_hw_instance_attr {
> +	struct attribute attr;
> +	ssize_t (*show)(struct ip_hw_instance *ip_hw_instance, char *buf);
> +};
> +
> +static ssize_t hw_id_show(struct ip_hw_instance *ip_hw_instance, char *buf)
> +{
> +	return sprintf(buf, "%d\n", ip_hw_instance->hw_id);
> +}

It's better to use sysfs_emit() or sysfs_emit_at() in show()
per Documentation/filesystems/sysfs.rst.

Regards,
Lang
> +static ssize_t num_instance_show(struct ip_hw_instance *ip_hw_instance, char *buf)
> +{
> +	return sprintf(buf, "%d\n", ip_hw_instance->num_instance);
> +}
> +
> +static ssize_t major_show(struct ip_hw_instance *ip_hw_instance, char *buf)
> +{
> +	return sprintf(buf, "%d\n", ip_hw_instance->major);
> +}
> +
> +static ssize_t minor_show(struct ip_hw_instance *ip_hw_instance, char *buf)
> +{
> +	return sprintf(buf, "%d\n", ip_hw_instance->minor);
> +}
> +
> +static ssize_t revision_show(struct ip_hw_instance *ip_hw_instance, char *buf)
> +{
> +	return sprintf(buf, "%d\n", ip_hw_instance->revision);
> +}
> +
> +static ssize_t num_base_addresses_show(struct ip_hw_instance *ip_hw_instance, char *buf)
> +{
> +	return sprintf(buf, "%d\n", ip_hw_instance->num_base_addresses);
> +}
> +
> +static ssize_t base_addr_show(struct ip_hw_instance *ip_hw_instance, char *buf)
> +{
> +	ssize_t res = 0;
> +	int ii;
> +
> +	for (ii = 0; ii < ip_hw_instance->num_base_addresses; ii++) {
> +		if (res + 12 >= PAGE_SIZE)
> +			break;
> +		res += sprintf(buf + res, "0x%08X\n", ip_hw_instance->base_addr[ii]);
> +	}
> +
> +	return res;
> +}
> +
> +static struct ip_hw_instance_attr ip_hw_attr[] = {
> +	__ATTR_RO(hw_id),
> +	__ATTR_RO(num_instance),
> +	__ATTR_RO(major),
> +	__ATTR_RO(minor),
> +	__ATTR_RO(revision),
> +	__ATTR_RO(num_base_addresses),
> +	__ATTR_RO(base_addr),
> +};
> +
> +static struct attribute *ip_hw_instance_attrs[] = {
> +	&ip_hw_attr[0].attr,
> +	&ip_hw_attr[1].attr,
> +	&ip_hw_attr[2].attr,
> +	&ip_hw_attr[3].attr,
> +	&ip_hw_attr[4].attr,
> +	&ip_hw_attr[5].attr,
> +	&ip_hw_attr[6].attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(ip_hw_instance);
> +
> +#define to_ip_hw_instance(x) container_of(x, struct ip_hw_instance, kobj)
> +#define to_ip_hw_instance_attr(x) container_of(x, struct ip_hw_instance_attr, attr)
> +
> +static ssize_t ip_hw_instance_attr_show(struct kobject *kobj,
> +					struct attribute *attr,
> +					char *buf)
> +{
> +	struct ip_hw_instance *ip_hw_instance = to_ip_hw_instance(kobj);
> +	struct ip_hw_instance_attr *ip_hw_attr = to_ip_hw_instance_attr(attr);
> +
> +	if (!ip_hw_attr->show)
> +		return -EIO;
> +
> +	return ip_hw_attr->show(ip_hw_instance, buf);
> +}
> +
> +static const struct sysfs_ops ip_hw_instance_sysfs_ops = {
> +	.show = ip_hw_instance_attr_show,
> +};
> +
> +static void ip_hw_instance_release(struct kobject *kobj)
> +{
> +	struct ip_hw_instance *ip_hw_instance = to_ip_hw_instance(kobj);
> +
> +	kfree(ip_hw_instance);
> +}
> +
> +static struct kobj_type ip_hw_instance_ktype = {
> +	.release = ip_hw_instance_release,
> +	.sysfs_ops = &ip_hw_instance_sysfs_ops,
> +	.default_groups = ip_hw_instance_groups,
> +};
> +
> +/* -------------------------------------------------- */
> +
> +#define to_ip_hw_id(x)  container_of(to_kset(x), struct ip_hw_id, hw_id_kset)
> +
> +static void ip_hw_id_release(struct kobject *kobj)
> +{
> +	struct ip_hw_id *ip_hw_id = to_ip_hw_id(kobj);
> +
> +	if (!list_empty(&ip_hw_id->hw_id_kset.list))
> +		DRM_ERROR("ip_hw_id->hw_id_kset is not empty");
> +	kfree(ip_hw_id);
> +}
> +
> +static struct kobj_type ip_hw_id_ktype = {
> +	.release = ip_hw_id_release,
> +	.sysfs_ops = &kobj_sysfs_ops,
> +};
> +
> +/* -------------------------------------------------- */
> +
> +static void die_kobj_release(struct kobject *kobj);
> +static void ip_disc_release(struct kobject *kobj);
> +
> +struct ip_die_entry_attribute {
> +	struct attribute attr;
> +	ssize_t (*show)(struct ip_die_entry *ip_die_entry, char *buf);
> +};
> +
> +#define to_ip_die_entry_attr(x)  container_of(x, struct ip_die_entry_attribute, attr)
> +
> +static ssize_t num_ips_show(struct ip_die_entry *ip_die_entry, char *buf)
> +{
> +	return sprintf(buf, "%d\n", ip_die_entry->num_ips);
> +}
> +
> +/* If there are more ip_die_entry attrs, other than the number of IPs,
> + * we can make this intro an array of attrs, and then initialize
> + * ip_die_entry_attrs in a loop.
> + */
> +static struct ip_die_entry_attribute num_ips_attr =
> +	__ATTR_RO(num_ips);
> +
> +static struct attribute *ip_die_entry_attrs[] = {
> +	&num_ips_attr.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(ip_die_entry); /* ip_die_entry_groups */
> +
> +#define to_ip_die_entry(x) container_of(to_kset(x), struct ip_die_entry, ip_kset)
> +
> +static ssize_t ip_die_entry_attr_show(struct kobject *kobj,
> +				      struct attribute *attr,
> +				      char *buf)
> +{
> +	struct ip_die_entry_attribute *ip_die_entry_attr = to_ip_die_entry_attr(attr);
> +	struct ip_die_entry *ip_die_entry = to_ip_die_entry(kobj);
> +
> +	if (!ip_die_entry_attr->show)
> +		return -EIO;
> +
> +	return ip_die_entry_attr->show(ip_die_entry, buf);
> +}
> +
> +static void ip_die_entry_release(struct kobject *kobj)
> +{
> +	struct ip_die_entry *ip_die_entry = to_ip_die_entry(kobj);
> +
> +	if (!list_empty(&ip_die_entry->ip_kset.list))
> +		DRM_ERROR("ip_die_entry->ip_kset is not empty");
> +	kfree(ip_die_entry);
> +}
> +
> +static const struct sysfs_ops ip_die_entry_sysfs_ops = {
> +	.show = ip_die_entry_attr_show,
> +};
> +
> +static struct kobj_type ip_die_entry_ktype = {
> +	.release = ip_die_entry_release,
> +	.sysfs_ops = &ip_die_entry_sysfs_ops,
> +	.default_groups = ip_die_entry_groups,
> +};
> +
> +static struct kobj_type die_kobj_ktype = {
> +	.release = die_kobj_release,
> +	.sysfs_ops = &kobj_sysfs_ops,
> +};
> +
> +static struct kobj_type ip_discovery_ktype = {
> +	.release = ip_disc_release,
> +	.sysfs_ops = &kobj_sysfs_ops,
> +};
> +
> +struct ip_discovery_top {
> +	struct kobject kobj;    /* ip_discovery/ */
> +	struct kset die_kset;   /* ip_discovery/die/, contains ip_die_entry */
> +	struct amdgpu_device *adev;
> +};
> +
> +static void die_kobj_release(struct kobject *kobj)
> +{
> +	struct ip_discovery_top *ip_top = container_of(to_kset(kobj),
> +						       struct ip_discovery_top,
> +						       die_kset);
> +	if (!list_empty(&ip_top->die_kset.list))
> +		DRM_ERROR("ip_top->die_kset is not empty");
> +}
> +
> +static void ip_disc_release(struct kobject *kobj)
> +{
> +	struct ip_discovery_top *ip_top = container_of(kobj, struct ip_discovery_top,
> +						       kobj);
> +	struct amdgpu_device *adev = ip_top->adev;
> +
> +	adev->ip_top = NULL;
> +	kfree(ip_top);
> +}
> +
> +static int amdgpu_discovery_sysfs_ips(struct amdgpu_device *adev,
> +				      struct ip_die_entry *ip_die_entry,
> +				      const size_t _ip_offset, const int num_ips)
> +{
> +	int ii, jj, kk, res;
> +
> +	DRM_DEBUG("num_ips:%d", num_ips);
> +
> +	/* Find all IPs of a given HW ID, and add their instance to
> +	 * #die/#hw_id/#instance/<attributes>
> +	 */
> +	for (ii = 0; ii < HW_ID_MAX; ii++) {
> +		struct ip_hw_id *ip_hw_id = NULL;
> +		size_t ip_offset = _ip_offset;
> +
> +		for (jj = 0; jj < num_ips; jj++) {
> +			struct ip *ip;
> +			struct ip_hw_instance *ip_hw_instance;
> +
> +			ip = (struct ip *)(adev->mman.discovery_bin + ip_offset);
> +			if (amdgpu_discovery_validate_ip(ip) ||
> +			    le16_to_cpu(ip->hw_id) != ii)
> +				goto next_ip;
> +
> +			DRM_DEBUG("match:%d @ ip_offset:%ld", ii, ip_offset);
> +
> +			/* We have a hw_id match; register the hw
> +			 * block if not yet registered.
> +			 */
> +			if (!ip_hw_id) {
> +				ip_hw_id = kzalloc(sizeof(*ip_hw_id), GFP_KERNEL);
> +				if (!ip_hw_id)
> +					return -ENOMEM;
> +				ip_hw_id->hw_id = ii;
> +
> +				kobject_set_name(&ip_hw_id->hw_id_kset.kobj, "%d", ii);
> +				ip_hw_id->hw_id_kset.kobj.kset = &ip_die_entry->ip_kset;
> +				ip_hw_id->hw_id_kset.kobj.ktype = &ip_hw_id_ktype;
> +				res = kset_register(&ip_hw_id->hw_id_kset);
> +				if (res) {
> +					DRM_ERROR("Couldn't register ip_hw_id kset");
> +					kfree(ip_hw_id);
> +					return res;
> +				}
> +				if (hw_id_names[ii]) {
> +					res = sysfs_create_link(&ip_die_entry->ip_kset.kobj,
> +								&ip_hw_id->hw_id_kset.kobj,
> +								hw_id_names[ii]);
> +					if (res) {
> +						DRM_ERROR("Couldn't create IP link %s in IP Die:%s\n",
> +							  hw_id_names[ii],
> +							  kobject_name(&ip_die_entry->ip_kset.kobj));
> +					}
> +				}
> +			}
> +
> +			/* Now register its instance.
> +			 */
> +			ip_hw_instance = kzalloc(sizeof(*ip_hw_instance) +
> +						 sizeof(u32) * ip->num_base_address,
> +						 GFP_KERNEL);
> +			if (!ip_hw_instance) {
> +				DRM_ERROR("no memory for ip_hw_instance");
> +				return -ENOMEM;
> +			}
> +			ip_hw_instance->hw_id = le16_to_cpu(ip->hw_id); /* == ii */
> +			ip_hw_instance->num_instance = ip->number_instance;
> +			ip_hw_instance->major = ip->major;
> +			ip_hw_instance->minor = ip->minor;
> +			ip_hw_instance->revision = ip->revision;
> +			ip_hw_instance->num_base_addresses = ip->num_base_address;
> +
> +			for (kk = 0; kk < ip_hw_instance->num_base_addresses; kk++)
> +				ip_hw_instance->base_addr[kk] = ip->base_address[kk];
> +
> +			kobject_init(&ip_hw_instance->kobj, &ip_hw_instance_ktype);
> +			ip_hw_instance->kobj.kset = &ip_hw_id->hw_id_kset;
> +			res = kobject_add(&ip_hw_instance->kobj, NULL,
> +					  "%d", ip_hw_instance->num_instance);
> +next_ip:
> +			ip_offset += sizeof(*ip) + 4 * (ip->num_base_address - 1);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev)
> +{
> +	struct binary_header *bhdr;
> +	struct ip_discovery_header *ihdr;
> +	struct die_header *dhdr;
> +	struct kset *die_kset = &adev->ip_top->die_kset;
> +	u16 num_dies, die_offset, num_ips;
> +	size_t ip_offset;
> +	int ii, res;
> +
> +	bhdr = (struct binary_header *)adev->mman.discovery_bin;
> +	ihdr = (struct ip_discovery_header *)(adev->mman.discovery_bin +
> +					      le16_to_cpu(bhdr->table_list[IP_DISCOVERY].offset));
> +	num_dies = le16_to_cpu(ihdr->num_dies);
> +
> +	DRM_DEBUG("number of dies: %d\n", num_dies);
> +
> +	for (ii = 0; ii < num_dies; ii++) {
> +		struct ip_die_entry *ip_die_entry;
> +
> +		die_offset = le16_to_cpu(ihdr->die_info[ii].die_offset);
> +		dhdr = (struct die_header *)(adev->mman.discovery_bin + die_offset);
> +		num_ips = le16_to_cpu(dhdr->num_ips);
> +		ip_offset = die_offset + sizeof(*dhdr);
> +
> +		/* Add the die to the kset.
> +		 *
> +		 * dhdr->die_id == ii, which was checked in
> +		 * amdgpu_discovery_reg_base_init().
> +		 */
> +
> +		ip_die_entry = kzalloc(sizeof(*ip_die_entry), GFP_KERNEL);
> +		if (!ip_die_entry)
> +			return -ENOMEM;
> +
> +		ip_die_entry->num_ips = num_ips;
> +
> +		kobject_set_name(&ip_die_entry->ip_kset.kobj, "%d", le16_to_cpu(dhdr->die_id));
> +		ip_die_entry->ip_kset.kobj.kset = die_kset;
> +		ip_die_entry->ip_kset.kobj.ktype = &ip_die_entry_ktype;
> +		res = kset_register(&ip_die_entry->ip_kset);
> +		if (res) {
> +			DRM_ERROR("Couldn't register ip_die_entry kset");
> +			kfree(ip_die_entry);
> +			return res;
> +		}
> +
> +		amdgpu_discovery_sysfs_ips(adev, ip_die_entry, ip_offset, num_ips);
> +	}
> +
> +	return 0;
> +}
> +
> +static int amdgpu_discovery_sysfs_init(struct amdgpu_device *adev)
> +{
> +	struct kset *die_kset;
> +	int res;
> +
> +	adev->ip_top = kzalloc(sizeof(*adev->ip_top), GFP_KERNEL);
> +	if (!adev->ip_top)
> +		return -ENOMEM;
> +
> +	adev->ip_top->adev = adev;
> +
> +	res = kobject_init_and_add(&adev->ip_top->kobj, &ip_discovery_ktype,
> +				   &adev->dev->kobj, "ip_discovery");
> +	if (res) {
> +		DRM_ERROR("Couldn't init and add ip_discovery/");
> +		goto Err;
> +	}
> +
> +	die_kset = &adev->ip_top->die_kset;
> +	kobject_set_name(&die_kset->kobj, "%s", "die");
> +	die_kset->kobj.parent = &adev->ip_top->kobj;
> +	die_kset->kobj.ktype = &die_kobj_ktype;
> +	res = kset_register(&adev->ip_top->die_kset);
> +	if (res) {
> +		DRM_ERROR("Couldn't register die_kset");
> +		goto Err;
> +	}
> +
> +	res = amdgpu_discovery_sysfs_recurse(adev);
> +
> +	return res;
> +Err:
> +	kobject_put(&adev->ip_top->kobj);
> +	return res;
> +}
> +
> +/* -------------------------------------------------- */
> +
> +#define list_to_kobj(el) container_of(el, struct kobject, entry)
> +
> +static void amdgpu_discovery_sysfs_ip_hw_free(struct ip_hw_id *ip_hw_id)
> +{
> +	struct list_head *el, *tmp;
> +	struct kset *hw_id_kset;
> +
> +	hw_id_kset = &ip_hw_id->hw_id_kset;
> +	spin_lock(&hw_id_kset->list_lock);
> +	list_for_each_prev_safe(el, tmp, &hw_id_kset->list) {
> +		list_del_init(el);
> +		spin_unlock(&hw_id_kset->list_lock);
> +		/* kobject is embedded in ip_hw_instance */
> +		kobject_put(list_to_kobj(el));
> +		spin_lock(&hw_id_kset->list_lock);
> +	}
> +	spin_unlock(&hw_id_kset->list_lock);
> +	kobject_put(&ip_hw_id->hw_id_kset.kobj);
> +}
> +
> +static void amdgpu_discovery_sysfs_die_free(struct ip_die_entry *ip_die_entry)
> +{
> +	struct list_head *el, *tmp;
> +	struct kset *ip_kset;
> +
> +	ip_kset = &ip_die_entry->ip_kset;
> +	spin_lock(&ip_kset->list_lock);
> +	list_for_each_prev_safe(el, tmp, &ip_kset->list) {
> +		list_del_init(el);
> +		spin_unlock(&ip_kset->list_lock);
> +		amdgpu_discovery_sysfs_ip_hw_free(to_ip_hw_id(list_to_kobj(el)));
> +		spin_lock(&ip_kset->list_lock);
> +	}
> +	spin_unlock(&ip_kset->list_lock);
> +	kobject_put(&ip_die_entry->ip_kset.kobj);
> +}
> +
> +static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev)
> +{
> +	struct list_head *el, *tmp;
> +	struct kset *die_kset;
> +
> +	die_kset = &adev->ip_top->die_kset;
> +	spin_lock(&die_kset->list_lock);
> +	list_for_each_prev_safe(el, tmp, &die_kset->list) {
> +		list_del_init(el);
> +		spin_unlock(&die_kset->list_lock);
> +		amdgpu_discovery_sysfs_die_free(to_ip_die_entry(list_to_kobj(el)));
> +		spin_lock(&die_kset->list_lock);
> +	}
> +	spin_unlock(&die_kset->list_lock);
> +	kobject_put(&adev->ip_top->die_kset.kobj);
> +	kobject_put(&adev->ip_top->kobj);
> +}
> +
> +/* ================================================== */
> +
>  int amdgpu_discovery_reg_base_init(struct amdgpu_device *adev)
>  {
>  	struct binary_header *bhdr;
> @@ -492,6 +976,8 @@ int amdgpu_discovery_reg_base_init(struct amdgpu_device *adev)
>  		}
>  	}
>  
> +	amdgpu_discovery_sysfs_init(adev);
> +
>  	return 0;
>  }
>  
> -- 
> 2.35.0.3.gb23dac905b
> 

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2022-02-10  4:48 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-09 22:51 [PATCH v1 0/1] drm/amdgpu: Show IP discovery in sysfs Luben Tuikov
2022-02-09 22:51 ` [PATCH v1 1/1] " Luben Tuikov
2022-02-10  3:43   ` Wang, Yang(Kevin)
2022-02-10  4:48   ` Lang Yu

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.