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.deuc...@amd.com>
> Cc: Tom StDenis <tom.stde...@amd.com>
> Signed-off-by: Luben Tuikov <luben.tui...@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
> 

Reply via email to