<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<style type="text/css" style="display:none;"> P {margin-top:0;margin-bottom:0;} </style>
</head>
<body dir="ltr">
<p style="font-family:Arial;font-size:10pt;color:#0000FF;margin:5pt;" align="Left">
[AMD Official Use Only]<br>
</p>
<br>
<div>
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div id="appendonsend"></div>
<div style="font-family:Calibri,Arial,Helvetica,sans-serif; font-size:12pt; color:rgb(0,0,0)">
<br>
</div>
<hr tabindex="-1" style="display:inline-block; width:98%">
<div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" color="#000000" style="font-size:11pt"><b>From:</b> amd-gfx <amd-gfx-bounces@lists.freedesktop.org> on behalf of Luben Tuikov <luben.tuikov@amd.com><br>
<b>Sent:</b> Thursday, February 10, 2022 6:51 AM<br>
<b>To:</b> amd-gfx@lists.freedesktop.org <amd-gfx@lists.freedesktop.org><br>
<b>Cc:</b> Deucher, Alexander <Alexander.Deucher@amd.com>; StDenis, Tom <Tom.StDenis@amd.com>; Tuikov, Luben <Luben.Tuikov@amd.com><br>
<b>Subject:</b> [PATCH v1 1/1] drm/amdgpu: Show IP discovery in sysfs</font>
<div> </div>
</div>
<div class="BodyFragment"><font size="2"><span style="font-size:11pt">
<div class="PlainText">Add IP discovery data in sysfs. The format is:<br>
/sys/class/drm/cardX/device/ip_discovery/die/D/B/I/<attrs><br>
where,<br>
X is the card ID, an integer,<br>
D is the die ID, an integer,<br>
B is the IP HW ID, an integer, aka block type,<br>
I is the IP HW ID instance, an integer.<br>
<attrs> are the attributes of the block instance. At the moment these<br>
include HW ID, instance number, major, minor, revision, number of base<br>
addresses, and the base addresses themselves.<br>
<br>
A symbolic link of the acronym HW ID is also created, under D/, if you<br>
prefer to browse by something humanly accessible.<br>
<br>
Cc: Alex Deucher <Alexander.Deucher@amd.com><br>
Cc: Tom StDenis <tom.stdenis@amd.com><br>
Signed-off-by: Luben Tuikov <luben.tuikov@amd.com><br>
---<br>
 drivers/gpu/drm/amd/amdgpu/amdgpu.h           |   4 +<br>
 drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 486 ++++++++++++++++++<br>
 2 files changed, 490 insertions(+)<br>
<br>
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h<br>
index e4eb812ade2fa4..3a126dce8a2fe9 100644<br>
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h<br>
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h<br>
@@ -772,6 +772,8 @@ struct amd_powerplay {<br>
         const struct amd_pm_funcs *pp_funcs;<br>
 };<br>
 <br>
+struct ip_discovery_top;<br>
+<br>
 /* polaris10 kickers */<br>
 #define ASICID_IS_P20(did, rid)         (((did == 0x67DF) && \<br>
                                          ((rid == 0xE3) || \<br>
@@ -1097,6 +1099,8 @@ struct amdgpu_device {<br>
         bool                            ram_is_direct_mapped;<br>
 <br>
         struct list_head                ras_list;<br>
+<br>
+       struct ip_discovery_top         *ip_top;<br>
 };<br>
 <br>
 static inline struct amdgpu_device *drm_to_adev(struct drm_device *ddev)<br>
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c<br>
index 07623634fdc2f1..46e6e1352574f6 100644<br>
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c<br>
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c<br>
@@ -360,8 +360,11 @@ static int amdgpu_discovery_init(struct amdgpu_device *adev)<br>
         return r;<br>
 }<br>
 <br>
+static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev);<br>
+<br>
 void amdgpu_discovery_fini(struct amdgpu_device *adev)<br>
 {<br>
+       amdgpu_discovery_sysfs_fini(adev);<br>
         kfree(adev->mman.discovery_bin);<br>
         adev->mman.discovery_bin = NULL;<br>
 }<br>
@@ -382,6 +385,487 @@ static int amdgpu_discovery_validate_ip(const struct ip *ip)<br>
         return 0;<br>
 }<br>
 <br>
+/* ================================================== */<br>
+<br>
+struct ip_hw_instance {<br>
+       struct kobject kobj; /* ip_discovery/die/#die/#hw_id/#instance/<attrs...> */<br>
+<br>
+       int hw_id;<br>
+       u8  num_instance;<br>
+       u8  major, minor, revision;<br>
+<br>
+       int num_base_addresses;<br>
+       u32 base_addr[0];<br>
+};</div>
<div class="PlainText"><br>
</div>
<div class="PlainText"><span style="font-family: "Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif; font-size: 11pt; color: rgb(0, 0, 0); background-color: rgba(0, 0, 0, 0);">[kevin]:</span></div>
<div class="PlainText">
<ol>
<li><span style="font-family: "Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif; font-size: 11pt; color: rgb(0, 0, 0); background-color: rgba(0, 0, 0, 0);">use flex-array instead
 of zero length array is better to match kernel coding style and avoid some compiler warning. eg: "</span><span><span style="text-align: start; background-color: rgba(0, 0, 0, 0); display: inline !important; font-family: "Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif; font-size: 11pt; color: rgb(0, 0, 0);">u32
 base_addr[];"</span><br>
</span></li><li><span><span style="text-align: start; background-color: rgba(0, 0, 0, 0); display: inline !important; font-family: "Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif; font-size: 11pt; color: rgb(0, 0, 0);">please
 use following macro helper to handle array size.</span><span style="text-align:start;background-color:rgb(255, 255, 255);display:inline !important"><br>
</span><span style="text-align: start; background-color: rgba(0, 0, 0, 0); display: inline !important; font-family: "Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif; font-size: 11pt; color: rgb(0, 0, 0);">// </span><span style="text-align: start; background-color: rgba(0, 0, 0, 0); display: inline !important; font-family: "Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif; font-size: 11pt; color: rgb(0, 0, 0);">include/linux/overflow.h</span><span style="text-align:start;background-color:rgb(255, 255, 255);display:inline !important"><br>
</span><span style="text-align: start; background-color: rgba(0, 0, 0, 0); display: inline !important; font-family: "Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif; font-size: 11pt; color: rgb(0, 0, 0);">-
 struct_size() - Calculate size of structure with trailing array.</span><span style="text-align:start;background-color:rgb(255, 255, 255);display:inline !important"><br>
</span></span></li><li style="display:block;font-family:"Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif;font-size:11pt;color:rgb(0, 0, 0)">
<span><span style="text-align: start; background-color: rgba(0, 0, 0, 0); display: inline !important; font-family: "Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif; font-size: 11pt; color: rgb(0, 0, 0);">- flex_array_size()
 - Calculate size of a flexible array member within an enclosing structure.</span></span></li></ol>
<div><span><span style="text-align: start; background-color: rgba(0, 0, 0, 0); display: inline !important; font-family: "Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif; font-size: 11pt; color: rgb(0, 0, 0);">On
 the other hand, do we really need variable length arrays?  why not fix size to 4?</span><span style="text-align:start;background-color:rgb(255, 255, 255);display:inline !important"><br>
<span></span></span></span></div>
</div>
<div class="PlainText">+<br>
+struct ip_hw_id {<br>
+       struct kset hw_id_kset;  /* ip_discovery/die/#die/#hw_id/, contains ip_hw_instance */<br>
+       int hw_id;<br>
+};<br>
+<br>
+struct ip_die_entry {<br>
+       struct kset ip_kset;     /* ip_discovery/die/#die/, contains ip_hw_id  */<br>
+       u16 num_ips;<br>
+};<br>
+<br>
+/* -------------------------------------------------- */<br>
+<br>
+struct ip_hw_instance_attr {<br>
+       struct attribute attr;<br>
+       ssize_t (*show)(struct ip_hw_instance *ip_hw_instance, char *buf);<br>
+};<br>
+<br>
+static ssize_t hw_id_show(struct ip_hw_instance *ip_hw_instance, char *buf)<br>
+{<br>
+       return sprintf(buf, "%d\n", ip_hw_instance->hw_id);<br>
+}<br>
+<br>
+static ssize_t num_instance_show(struct ip_hw_instance *ip_hw_instance, char *buf)<br>
+{<br>
+       return sprintf(buf, "%d\n", ip_hw_instance->num_instance);<br>
+}<br>
+<br>
+static ssize_t major_show(struct ip_hw_instance *ip_hw_instance, char *buf)<br>
+{<br>
+       return sprintf(buf, "%d\n", ip_hw_instance->major);<br>
+}<br>
+<br>
+static ssize_t minor_show(struct ip_hw_instance *ip_hw_instance, char *buf)<br>
+{<br>
+       return sprintf(buf, "%d\n", ip_hw_instance->minor);<br>
+}<br>
+<br>
+static ssize_t revision_show(struct ip_hw_instance *ip_hw_instance, char *buf)<br>
+{<br>
+       return sprintf(buf, "%d\n", ip_hw_instance->revision);<br>
+}<br>
+<br>
+static ssize_t num_base_addresses_show(struct ip_hw_instance *ip_hw_instance, char *buf)<br>
+{<br>
+       return sprintf(buf, "%d\n", ip_hw_instance->num_base_addresses);<br>
+}<br>
+<br>
+static ssize_t base_addr_show(struct ip_hw_instance *ip_hw_instance, char *buf)<br>
+{<br>
+       ssize_t res = 0;<br>
+       int ii;<br>
+<br>
+       for (ii = 0; ii < ip_hw_instance->num_base_addresses; ii++) {<br>
+               if (res + 12 >= PAGE_SIZE)<br>
+                       break;<br>
+               res += sprintf(buf + res, "0x%08X\n", ip_hw_instance->base_addr[ii]);<br>
+       }<br>
+<br>
+       return res;<br>
+}<br>
+<br>
+static struct ip_hw_instance_attr ip_hw_attr[] = {<br>
+       __ATTR_RO(hw_id),<br>
+       __ATTR_RO(num_instance),<br>
+       __ATTR_RO(major),<br>
+       __ATTR_RO(minor),<br>
+       __ATTR_RO(revision),<br>
+       __ATTR_RO(num_base_addresses),<br>
+       __ATTR_RO(base_addr),<br>
+};<br>
+<br>
+static struct attribute *ip_hw_instance_attrs[] = {<br>
+       &ip_hw_attr[0].attr,<br>
+       &ip_hw_attr[1].attr,<br>
+       &ip_hw_attr[2].attr,<br>
+       &ip_hw_attr[3].attr,<br>
+       &ip_hw_attr[4].attr,<br>
+       &ip_hw_attr[5].attr,<br>
+       &ip_hw_attr[6].attr,<br>
+       NULL,<br>
+};<br>
+ATTRIBUTE_GROUPS(ip_hw_instance);<br>
+<br>
+#define to_ip_hw_instance(x) container_of(x, struct ip_hw_instance, kobj)<br>
+#define to_ip_hw_instance_attr(x) container_of(x, struct ip_hw_instance_attr, attr)<br>
+<br>
+static ssize_t ip_hw_instance_attr_show(struct kobject *kobj,<br>
+                                       struct attribute *attr,<br>
+                                       char *buf)<br>
+{<br>
+       struct ip_hw_instance *ip_hw_instance = to_ip_hw_instance(kobj);<br>
+       struct ip_hw_instance_attr *ip_hw_attr = to_ip_hw_instance_attr(attr);<br>
+<br>
+       if (!ip_hw_attr->show)<br>
+               return -EIO;<br>
+<br>
+       return ip_hw_attr->show(ip_hw_instance, buf);<br>
+}<br>
+<br>
+static const struct sysfs_ops ip_hw_instance_sysfs_ops = {<br>
+       .show = ip_hw_instance_attr_show,<br>
+};<br>
+<br>
+static void ip_hw_instance_release(struct kobject *kobj)<br>
+{<br>
+       struct ip_hw_instance *ip_hw_instance = to_ip_hw_instance(kobj);<br>
+<br>
+       kfree(ip_hw_instance);<br>
+}<br>
+<br>
+static struct kobj_type ip_hw_instance_ktype = {<br>
+       .release = ip_hw_instance_release,<br>
+       .sysfs_ops = &ip_hw_instance_sysfs_ops,<br>
+       .default_groups = ip_hw_instance_groups,<br>
+};<br>
+<br>
+/* -------------------------------------------------- */<br>
+<br>
+#define to_ip_hw_id(x)  container_of(to_kset(x), struct ip_hw_id, hw_id_kset)<br>
+<br>
+static void ip_hw_id_release(struct kobject *kobj)<br>
+{<br>
+       struct ip_hw_id *ip_hw_id = to_ip_hw_id(kobj);<br>
+<br>
+       if (!list_empty(&ip_hw_id->hw_id_kset.list))<br>
+               DRM_ERROR("ip_hw_id->hw_id_kset is not empty");<br>
+       kfree(ip_hw_id);<br>
+}<br>
+<br>
+static struct kobj_type ip_hw_id_ktype = {<br>
+       .release = ip_hw_id_release,<br>
+       .sysfs_ops = &kobj_sysfs_ops,<br>
+};<br>
+<br>
+/* -------------------------------------------------- */<br>
+<br>
+static void die_kobj_release(struct kobject *kobj);<br>
+static void ip_disc_release(struct kobject *kobj);<br>
+<br>
+struct ip_die_entry_attribute {<br>
+       struct attribute attr;<br>
+       ssize_t (*show)(struct ip_die_entry *ip_die_entry, char *buf);<br>
+};<br>
+<br>
+#define to_ip_die_entry_attr(x)  container_of(x, struct ip_die_entry_attribute, attr)<br>
+<br>
+static ssize_t num_ips_show(struct ip_die_entry *ip_die_entry, char *buf)<br>
+{<br>
+       return sprintf(buf, "%d\n", ip_die_entry->num_ips);<br>
+}<br>
+<br>
+/* If there are more ip_die_entry attrs, other than the number of IPs,<br>
+ * we can make this intro an array of attrs, and then initialize<br>
+ * ip_die_entry_attrs in a loop.<br>
+ */<br>
+static struct ip_die_entry_attribute num_ips_attr =<br>
+       __ATTR_RO(num_ips);<br>
+<br>
+static struct attribute *ip_die_entry_attrs[] = {<br>
+       &num_ips_attr.attr,<br>
+       NULL,<br>
+};<br>
+ATTRIBUTE_GROUPS(ip_die_entry); /* ip_die_entry_groups */<br>
+<br>
+#define to_ip_die_entry(x) container_of(to_kset(x), struct ip_die_entry, ip_kset)<br>
+<br>
+static ssize_t ip_die_entry_attr_show(struct kobject *kobj,<br>
+                                     struct attribute *attr,<br>
+                                     char *buf)<br>
+{<br>
+       struct ip_die_entry_attribute *ip_die_entry_attr = to_ip_die_entry_attr(attr);<br>
+       struct ip_die_entry *ip_die_entry = to_ip_die_entry(kobj);<br>
+<br>
+       if (!ip_die_entry_attr->show)<br>
+               return -EIO;<br>
+<br>
+       return ip_die_entry_attr->show(ip_die_entry, buf);<br>
+}<br>
+<br>
+static void ip_die_entry_release(struct kobject *kobj)<br>
+{<br>
+       struct ip_die_entry *ip_die_entry = to_ip_die_entry(kobj);<br>
+<br>
+       if (!list_empty(&ip_die_entry->ip_kset.list))<br>
+               DRM_ERROR("ip_die_entry->ip_kset is not empty");<br>
+       kfree(ip_die_entry);<br>
+}<br>
+<br>
+static const struct sysfs_ops ip_die_entry_sysfs_ops = {<br>
+       .show = ip_die_entry_attr_show,<br>
+};<br>
+<br>
+static struct kobj_type ip_die_entry_ktype = {<br>
+       .release = ip_die_entry_release,<br>
+       .sysfs_ops = &ip_die_entry_sysfs_ops,<br>
+       .default_groups = ip_die_entry_groups,<br>
+};<br>
+<br>
+static struct kobj_type die_kobj_ktype = {<br>
+       .release = die_kobj_release,<br>
+       .sysfs_ops = &kobj_sysfs_ops,<br>
+};<br>
+<br>
+static struct kobj_type ip_discovery_ktype = {<br>
+       .release = ip_disc_release,<br>
+       .sysfs_ops = &kobj_sysfs_ops,<br>
+};<br>
+<br>
+struct ip_discovery_top {<br>
+       struct kobject kobj;    /* ip_discovery/ */<br>
+       struct kset die_kset;   /* ip_discovery/die/, contains ip_die_entry */<br>
+       struct amdgpu_device *adev;<br>
+};<br>
+<br>
+static void die_kobj_release(struct kobject *kobj)<br>
+{<br>
+       struct ip_discovery_top *ip_top = container_of(to_kset(kobj),<br>
+                                                      struct ip_discovery_top,<br>
+                                                      die_kset);<br>
+       if (!list_empty(&ip_top->die_kset.list))<br>
+               DRM_ERROR("ip_top->die_kset is not empty");<br>
+}<br>
+<br>
+static void ip_disc_release(struct kobject *kobj)<br>
+{<br>
+       struct ip_discovery_top *ip_top = container_of(kobj, struct ip_discovery_top,<br>
+                                                      kobj);<br>
+       struct amdgpu_device *adev = ip_top->adev;<br>
+<br>
+       adev->ip_top = NULL;<br>
+       kfree(ip_top);<br>
+}<br>
+<br>
+static int amdgpu_discovery_sysfs_ips(struct amdgpu_device *adev,<br>
+                                     struct ip_die_entry *ip_die_entry,<br>
+                                     const size_t _ip_offset, const int num_ips)<br>
+{<br>
+       int ii, jj, kk, res;<br>
+<br>
+       DRM_DEBUG("num_ips:%d", num_ips);<br>
+<br>
+       /* Find all IPs of a given HW ID, and add their instance to<br>
+        * #die/#hw_id/#instance/<attributes><br>
+        */<br>
+       for (ii = 0; ii < HW_ID_MAX; ii++) {<br>
+               struct ip_hw_id *ip_hw_id = NULL;<br>
+               size_t ip_offset = _ip_offset;<br>
+<br>
+               for (jj = 0; jj < num_ips; jj++) {<br>
+                       struct ip *ip;<br>
+                       struct ip_hw_instance *ip_hw_instance;<br>
+<br>
+                       ip = (struct ip *)(adev->mman.discovery_bin + ip_offset);<br>
+                       if (amdgpu_discovery_validate_ip(ip) ||<br>
+                           le16_to_cpu(ip->hw_id) != ii)<br>
+                               goto next_ip;<br>
+<br>
+                       DRM_DEBUG("match:%d @ ip_offset:%ld", ii, ip_offset);<br>
+<br>
+                       /* We have a hw_id match; register the hw<br>
+                        * block if not yet registered.<br>
+                        */<br>
+                       if (!ip_hw_id) {<br>
+                               ip_hw_id = kzalloc(sizeof(*ip_hw_id), GFP_KERNEL);<br>
+                               if (!ip_hw_id)<br>
+                                       return -ENOMEM;<br>
+                               ip_hw_id->hw_id = ii;<br>
+<br>
+                               kobject_set_name(&ip_hw_id->hw_id_kset.kobj, "%d", ii);<br>
+                               ip_hw_id->hw_id_kset.kobj.kset = &ip_die_entry->ip_kset;<br>
+                               ip_hw_id->hw_id_kset.kobj.ktype = &ip_hw_id_ktype;<br>
+                               res = kset_register(&ip_hw_id->hw_id_kset);<br>
+                               if (res) {<br>
+                                       DRM_ERROR("Couldn't register ip_hw_id kset");<br>
+                                       kfree(ip_hw_id);<br>
+                                       return res;<br>
+                               }<br>
+                               if (hw_id_names[ii]) {<br>
+                                       res = sysfs_create_link(&ip_die_entry->ip_kset.kobj,<br>
+                                                               &ip_hw_id->hw_id_kset.kobj,<br>
+                                                               hw_id_names[ii]);<br>
+                                       if (res) {<br>
+                                               DRM_ERROR("Couldn't create IP link %s in IP Die:%s\n",<br>
+                                                         hw_id_names[ii],<br>
+                                                         kobject_name(&ip_die_entry->ip_kset.kobj));<br>
+                                       }<br>
+                               }<br>
+                       }<br>
+<br>
+                       /* Now register its instance.<br>
+                        */<br>
+                       ip_hw_instance = kzalloc(sizeof(*ip_hw_instance) +<br>
+                                                sizeof(u32) * ip->num_base_address,<br>
+                                                GFP_KERNEL);<br>
+                       if (!ip_hw_instance) {<br>
+                               DRM_ERROR("no memory for ip_hw_instance");<br>
+                               return -ENOMEM;<br>
+                       }<br>
+                       ip_hw_instance->hw_id = le16_to_cpu(ip->hw_id); /* == ii */<br>
+                       ip_hw_instance->num_instance = ip->number_instance;<br>
+                       ip_hw_instance->major = ip->major;<br>
+                       ip_hw_instance->minor = ip->minor;<br>
+                       ip_hw_instance->revision = ip->revision;<br>
+                       ip_hw_instance->num_base_addresses = ip->num_base_address;<br>
+<br>
+                       for (kk = 0; kk < ip_hw_instance->num_base_addresses; kk++)<br>
+                               ip_hw_instance->base_addr[kk] = ip->base_address[kk];<br>
+<br>
+                       kobject_init(&ip_hw_instance->kobj, &ip_hw_instance_ktype);<br>
+                       ip_hw_instance->kobj.kset = &ip_hw_id->hw_id_kset;<br>
+                       res = kobject_add(&ip_hw_instance->kobj, NULL,<br>
+                                         "%d", ip_hw_instance->num_instance);<br>
+next_ip:<br>
+                       ip_offset += sizeof(*ip) + 4 * (ip->num_base_address - 1);<br>
+               }<br>
+       }<br>
+<br>
+       return 0;<br>
+}<br>
+<br>
+static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev)<br>
+{<br>
+       struct binary_header *bhdr;<br>
+       struct ip_discovery_header *ihdr;<br>
+       struct die_header *dhdr;<br>
+       struct kset *die_kset = &adev->ip_top->die_kset;<br>
+       u16 num_dies, die_offset, num_ips;<br>
+       size_t ip_offset;<br>
+       int ii, res;<br>
+<br>
+       bhdr = (struct binary_header *)adev->mman.discovery_bin;<br>
+       ihdr = (struct ip_discovery_header *)(adev->mman.discovery_bin +<br>
+                                             le16_to_cpu(bhdr->table_list[IP_DISCOVERY].offset));<br>
+       num_dies = le16_to_cpu(ihdr->num_dies);<br>
+<br>
+       DRM_DEBUG("number of dies: %d\n", num_dies);<br>
+<br>
+       for (ii = 0; ii < num_dies; ii++) {<br>
+               struct ip_die_entry *ip_die_entry;<br>
+<br>
+               die_offset = le16_to_cpu(ihdr->die_info[ii].die_offset);<br>
+               dhdr = (struct die_header *)(adev->mman.discovery_bin + die_offset);<br>
+               num_ips = le16_to_cpu(dhdr->num_ips);<br>
+               ip_offset = die_offset + sizeof(*dhdr);<br>
+<br>
+               /* Add the die to the kset.<br>
+                *<br>
+                * dhdr->die_id == ii, which was checked in<br>
+                * amdgpu_discovery_reg_base_init().<br>
+                */<br>
+<br>
+               ip_die_entry = kzalloc(sizeof(*ip_die_entry), GFP_KERNEL);<br>
+               if (!ip_die_entry)<br>
+                       return -ENOMEM;<br>
+<br>
+               ip_die_entry->num_ips = num_ips;<br>
+<br>
+               kobject_set_name(&ip_die_entry->ip_kset.kobj, "%d", le16_to_cpu(dhdr->die_id));<br>
+               ip_die_entry->ip_kset.kobj.kset = die_kset;<br>
+               ip_die_entry->ip_kset.kobj.ktype = &ip_die_entry_ktype;<br>
+               res = kset_register(&ip_die_entry->ip_kset);<br>
+               if (res) {<br>
+                       DRM_ERROR("Couldn't register ip_die_entry kset");<br>
+                       kfree(ip_die_entry);<br>
+                       return res;<br>
+               }<br>
+<br>
+               amdgpu_discovery_sysfs_ips(adev, ip_die_entry, ip_offset, num_ips);<br>
+       }<br>
+<br>
+       return 0;<br>
+}<br>
+<br>
+static int amdgpu_discovery_sysfs_init(struct amdgpu_device *adev)<br>
+{<br>
+       struct kset *die_kset;<br>
+       int res;<br>
+<br>
+       adev->ip_top = kzalloc(sizeof(*adev->ip_top), GFP_KERNEL);<br>
+       if (!adev->ip_top)<br>
+               return -ENOMEM;<br>
+<br>
+       adev->ip_top->adev = adev;<br>
+<br>
+       res = kobject_init_and_add(&adev->ip_top->kobj, &ip_discovery_ktype,<br>
+                                  &adev->dev->kobj, "ip_discovery");<br>
+       if (res) {<br>
+               DRM_ERROR("Couldn't init and add ip_discovery/");<br>
+               goto Err;<br>
+       }<br>
+<br>
+       die_kset = &adev->ip_top->die_kset;<br>
+       kobject_set_name(&die_kset->kobj, "%s", "die");<br>
+       die_kset->kobj.parent = &adev->ip_top->kobj;<br>
+       die_kset->kobj.ktype = &die_kobj_ktype;<br>
+       res = kset_register(&adev->ip_top->die_kset);<br>
+       if (res) {<br>
+               DRM_ERROR("Couldn't register die_kset");<br>
+               goto Err;<br>
+       }<br>
+<br>
+       res = amdgpu_discovery_sysfs_recurse(adev);<br>
+<br>
+       return res;<br>
+Err:<br>
+       kobject_put(&adev->ip_top->kobj);<br>
+       return res;<br>
+}<br>
+<br>
+/* -------------------------------------------------- */<br>
+<br>
+#define list_to_kobj(el) container_of(el, struct kobject, entry)<br>
+<br>
+static void amdgpu_discovery_sysfs_ip_hw_free(struct ip_hw_id *ip_hw_id)<br>
+{<br>
+       struct list_head *el, *tmp;<br>
+       struct kset *hw_id_kset;<br>
+<br>
+       hw_id_kset = &ip_hw_id->hw_id_kset;<br>
+       spin_lock(&hw_id_kset->list_lock);<br>
+       list_for_each_prev_safe(el, tmp, &hw_id_kset->list) {<br>
+               list_del_init(el);<br>
+               spin_unlock(&hw_id_kset->list_lock);<br>
+               /* kobject is embedded in ip_hw_instance */<br>
+               kobject_put(list_to_kobj(el));<br>
+               spin_lock(&hw_id_kset->list_lock);<br>
+       }<br>
+       spin_unlock(&hw_id_kset->list_lock);<br>
+       kobject_put(&ip_hw_id->hw_id_kset.kobj);<br>
+}<br>
+<br>
+static void amdgpu_discovery_sysfs_die_free(struct ip_die_entry *ip_die_entry)<br>
+{<br>
+       struct list_head *el, *tmp;<br>
+       struct kset *ip_kset;<br>
+<br>
+       ip_kset = &ip_die_entry->ip_kset;<br>
+       spin_lock(&ip_kset->list_lock);<br>
+       list_for_each_prev_safe(el, tmp, &ip_kset->list) {<br>
+               list_del_init(el);<br>
+               spin_unlock(&ip_kset->list_lock);<br>
+               amdgpu_discovery_sysfs_ip_hw_free(to_ip_hw_id(list_to_kobj(el)));<br>
+               spin_lock(&ip_kset->list_lock);<br>
+       }<br>
+       spin_unlock(&ip_kset->list_lock);<br>
+       kobject_put(&ip_die_entry->ip_kset.kobj);<br>
+}<br>
+<br>
+static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev)<br>
+{<br>
+       struct list_head *el, *tmp;<br>
+       struct kset *die_kset;<br>
+<br>
+       die_kset = &adev->ip_top->die_kset;<br>
+       spin_lock(&die_kset->list_lock);<br>
+       list_for_each_prev_safe(el, tmp, &die_kset->list) {<br>
+               list_del_init(el);<br>
+               spin_unlock(&die_kset->list_lock);<br>
+               amdgpu_discovery_sysfs_die_free(to_ip_die_entry(list_to_kobj(el)));<br>
+               spin_lock(&die_kset->list_lock);<br>
+       }<br>
+       spin_unlock(&die_kset->list_lock);<br>
+       kobject_put(&adev->ip_top->die_kset.kobj);<br>
+       kobject_put(&adev->ip_top->kobj);<br>
+}<br>
+<br>
+/* ================================================== */<br>
+<br>
 int amdgpu_discovery_reg_base_init(struct amdgpu_device *adev)<br>
 {<br>
         struct binary_header *bhdr;<br>
@@ -492,6 +976,8 @@ int amdgpu_discovery_reg_base_init(struct amdgpu_device *adev)<br>
                 }<br>
         }<br>
 <br>
+       amdgpu_discovery_sysfs_init(adev);<br>
+<br>
         return 0;<br>
 }<br>
 <br>
-- <br>
2.35.0.3.gb23dac905b<br>
<br>
</div>
</span></font></div>
</div>
</body>
</html>