summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c285
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c2
3 files changed, 257 insertions, 35 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
index be5069642a90..b844f4a9f0c6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
@@ -22,6 +22,7 @@
*/
#include <linux/firmware.h>
+#include <linux/kernfs.h>
#include "amdgpu.h"
#include "amdgpu_discovery.h"
@@ -148,6 +149,26 @@ MODULE_FIRMWARE("amdgpu/aldebaran_ip_discovery.bin");
#define mmDRIVER_SCRATCH_1 0x95
#define mmDRIVER_SCRATCH_2 0x96
+struct ip_discovery_top {
+ struct kobject kobj;
+ struct kset die_kset;
+ struct pci_dev *pdev;
+ struct amdgpu_device *adev;
+ uint8_t *discovery_bin;
+ uint32_t bin_size;
+ bool standalone_mode;
+};
+
+/* List to track early-initialized ip_discovery_top entries */
+struct early_ip_discovery {
+ struct list_head list;
+ struct pci_dev *pdev;
+ struct ip_discovery_top *ip_top;
+};
+
+static LIST_HEAD(early_ip_discovery_list);
+static DEFINE_MUTEX(early_ip_discovery_mutex);
+
static const char *hw_id_names[HW_ID_MAX] = {
[MP1_HWID] = "MP1",
[MP2_HWID] = "MP2",
@@ -542,25 +563,37 @@ static const char *amdgpu_discovery_get_fw_name(struct amdgpu_device *adev)
}
}
-static int amdgpu_discovery_get_table_info(struct amdgpu_device *adev,
- struct table_info **info,
- uint16_t table_id)
+static struct table_info *
+amdgpu_discovery_get_table_info_from_bin(uint8_t *discovery_bin,
+ uint16_t table_id)
{
- struct binary_header *bhdr =
- (struct binary_header *)adev->discovery.bin;
+ struct binary_header *bhdr = (struct binary_header *)discovery_bin;
struct binary_header_v2 *bhdrv2;
switch (bhdr->version_major) {
case 2:
- bhdrv2 = (struct binary_header_v2 *)adev->discovery.bin;
- *info = &bhdrv2->table_list[table_id];
- break;
+ bhdrv2 = (struct binary_header_v2 *)discovery_bin;
+ return &bhdrv2->table_list[table_id];
case 1:
case 0:
- *info = &bhdr->table_list[table_id];
- break;
+ return &bhdr->table_list[table_id];
default:
- dev_err(adev->dev, "Invalid ip discovery table version %d\n",bhdr->version_major);
+ return NULL;
+ }
+}
+
+static int amdgpu_discovery_get_table_info(struct amdgpu_device *adev,
+ struct table_info **info,
+ uint16_t table_id)
+{
+ struct binary_header *bhdr =
+ (struct binary_header *)adev->discovery.bin;
+
+ *info = amdgpu_discovery_get_table_info_from_bin(adev->discovery.bin,
+ table_id);
+ if (!*info) {
+ dev_err(adev->dev, "Invalid ip discovery table version %d\n",
+ bhdr->version_major);
return -EINVAL;
}
@@ -728,7 +761,9 @@ static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev);
void amdgpu_discovery_fini(struct amdgpu_device *adev)
{
- amdgpu_discovery_sysfs_fini(adev);
+ if (adev->discovery.ip_top && !adev->discovery.ip_top->standalone_mode)
+ amdgpu_discovery_sysfs_fini(adev);
+
kfree(adev->discovery.bin);
adev->discovery.bin = NULL;
}
@@ -737,15 +772,17 @@ static int amdgpu_discovery_validate_ip(struct amdgpu_device *adev,
uint8_t instance, uint16_t hw_id)
{
if (instance >= HWIP_MAX_INSTANCE) {
- dev_err(adev->dev,
- "Unexpected instance_number (%d) from ip discovery blob\n",
- instance);
+ if (adev)
+ dev_err(adev->dev,
+ "Unexpected instance_number (%d) from ip discovery blob\n",
+ instance);
return -EINVAL;
}
if (hw_id >= HW_ID_MAX) {
- dev_err(adev->dev,
- "Unexpected hw_id (%d) from ip discovery blob\n",
- hw_id);
+ if (adev)
+ dev_err(adev->dev,
+ "Unexpected hw_id (%d) from ip discovery blob\n",
+ hw_id);
return -EINVAL;
}
@@ -1111,12 +1148,6 @@ static const struct kobj_type ip_discovery_ktype = {
.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),
@@ -1132,8 +1163,14 @@ static void ip_disc_release(struct kobject *kobj)
kobj);
struct amdgpu_device *adev = ip_top->adev;
+ /* In standalone mode, discovery_bin is managed by devm and will be
+ * freed automatically when the PCI device is removed. Do not manually
+ * free it here to avoid double-free.
+ */
+
kfree(ip_top);
- adev->discovery.ip_top = NULL;
+ if (adev)
+ adev->discovery.ip_top = NULL;
}
static uint8_t amdgpu_discovery_get_harvest_info(struct amdgpu_device *adev,
@@ -1141,6 +1178,10 @@ static uint8_t amdgpu_discovery_get_harvest_info(struct amdgpu_device *adev,
{
uint8_t harvest = 0;
+ /* In early init mode (adev == NULL), harvest info is not available */
+ if (!adev)
+ return 0;
+
/* Until a uniform way is figured, get mask based on hwid */
switch (hw_id) {
case VCN_HWID:
@@ -1169,11 +1210,14 @@ static uint8_t amdgpu_discovery_get_harvest_info(struct amdgpu_device *adev,
}
static int amdgpu_discovery_sysfs_ips(struct amdgpu_device *adev,
+ struct ip_discovery_top *ip_top,
struct ip_die_entry *ip_die_entry,
const size_t _ip_offset, const int num_ips,
bool reg_base_64)
{
- uint8_t *discovery_bin = adev->discovery.bin;
+ uint8_t *discovery_bin = ip_top->standalone_mode ?
+ ip_top->discovery_bin :
+ adev->discovery.bin;
int ii, jj, kk, res;
uint16_t hw_id;
uint8_t inst;
@@ -1270,10 +1314,12 @@ next_ip:
return 0;
}
-static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev)
+static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev,
+ struct ip_discovery_top *ip_top)
{
- struct ip_discovery_top *ip_top = adev->discovery.ip_top;
- uint8_t *discovery_bin = adev->discovery.bin;
+ uint8_t *discovery_bin = ip_top->standalone_mode ?
+ ip_top->discovery_bin :
+ adev->discovery.bin;
struct table_info *info;
struct ip_discovery_header *ihdr;
struct die_header *dhdr;
@@ -1282,9 +1328,10 @@ static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev)
size_t ip_offset;
int ii, res;
- res = amdgpu_discovery_get_table_info(adev, &info, IP_DISCOVERY);
- if (res)
- return res;
+ info = amdgpu_discovery_get_table_info_from_bin(discovery_bin,
+ IP_DISCOVERY);
+ if (!info)
+ return -EINVAL;
ihdr = (struct ip_discovery_header
*)(discovery_bin +
le16_to_cpu(info->offset));
@@ -1322,7 +1369,8 @@ static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev)
return res;
}
- amdgpu_discovery_sysfs_ips(adev, ip_die_entry, ip_offset, num_ips, !!ihdr->base_addr_64_bit);
+ amdgpu_discovery_sysfs_ips(adev, ip_top, ip_die_entry, ip_offset,
+ num_ips, !!ihdr->base_addr_64_bit);
}
return 0;
@@ -1338,12 +1386,30 @@ static int amdgpu_discovery_sysfs_init(struct amdgpu_device *adev)
if (!discovery_bin)
return -EINVAL;
+ /* If early init already created sysfs in standalone mode, skip normal init */
+ if (adev->discovery.ip_top && adev->discovery.ip_top->standalone_mode)
+ return 0;
+
ip_top = kzalloc_obj(*ip_top);
if (!ip_top)
return -ENOMEM;
ip_top->adev = adev;
- adev->discovery.ip_top = ip_top;
+
+ /* Check if ip_discovery already exists before creating.
+ * This shouldn't normally happen but handle it gracefully.
+ */
+ if (adev->dev->kobj.sd) {
+ struct kernfs_node *existing;
+
+ existing = kernfs_find_and_get(adev->dev->kobj.sd, "ip_discovery");
+ if (existing) {
+ kernfs_put(existing);
+ kfree(ip_top);
+ return 0;
+ }
+ }
+
res = kobject_init_and_add(&ip_top->kobj, &ip_discovery_ktype,
&adev->dev->kobj, "ip_discovery");
if (res) {
@@ -1351,6 +1417,8 @@ static int amdgpu_discovery_sysfs_init(struct amdgpu_device *adev)
goto Err;
}
+ adev->discovery.ip_top = ip_top;
+
die_kset = &ip_top->die_kset;
kobject_set_name(&die_kset->kobj, "%s", "die");
die_kset->kobj.parent = &ip_top->kobj;
@@ -1365,7 +1433,7 @@ static int amdgpu_discovery_sysfs_init(struct amdgpu_device *adev)
ip_hw_instance_attrs[ii] = &ip_hw_attr[ii].attr;
ip_hw_instance_attrs[ii] = NULL;
- res = amdgpu_discovery_sysfs_recurse(adev);
+ res = amdgpu_discovery_sysfs_recurse(adev, ip_top);
return res;
Err:
@@ -1479,6 +1547,150 @@ void amdgpu_discovery_dump(struct amdgpu_device *adev, struct drm_printer *p)
spin_unlock(&die_kset->list_lock);
}
+int amdgpu_discovery_sysfs_early_init(struct amdgpu_device *adev, struct pci_dev *pdev)
+{
+ struct ip_discovery_top *ip_top;
+ struct early_ip_discovery *early_entry, *tmp;
+ struct kset *die_kset;
+ uint8_t *discovery_bin;
+ int res, ii;
+
+ if (!adev || !adev->discovery.bin)
+ return -EINVAL;
+
+ if (adev->discovery.ip_top)
+ return 0;
+
+ mutex_lock(&early_ip_discovery_mutex);
+ list_for_each_entry_safe(early_entry, tmp, &early_ip_discovery_list, list) {
+ if (early_entry->pdev == pdev) {
+ adev->discovery.ip_top = early_entry->ip_top;
+ early_entry->ip_top->adev = adev;
+ mutex_unlock(&early_ip_discovery_mutex);
+ return 0;
+ }
+ }
+ mutex_unlock(&early_ip_discovery_mutex);
+
+ discovery_bin = adev->discovery.bin;
+
+ early_entry = kzalloc(sizeof(*early_entry), GFP_KERNEL);
+ if (!early_entry)
+ return -ENOMEM;
+
+ ip_top = kzalloc(sizeof(*ip_top), GFP_KERNEL);
+ if (!ip_top) {
+ kfree(early_entry);
+ return -ENOMEM;
+ }
+
+ ip_top->discovery_bin = devm_kmemdup(&pdev->dev, discovery_bin,
+ DISCOVERY_TMR_SIZE, GFP_KERNEL);
+ if (!ip_top->discovery_bin) {
+ kfree(ip_top);
+ kfree(early_entry);
+ return -ENOMEM;
+ }
+
+ ip_top->bin_size = DISCOVERY_TMR_SIZE;
+ ip_top->pdev = pdev;
+ ip_top->adev = adev;
+ ip_top->standalone_mode = true;
+
+ /* Check if ip_discovery already exists (from previous probe attempt).
+ * This can happen if the module was unloaded and reloaded but the
+ * sysfs persisted (tied to PCI device lifetime).
+ */
+ if (pdev->dev.kobj.sd) {
+ struct kernfs_node *existing;
+
+ existing = kernfs_find_and_get(pdev->dev.kobj.sd, "ip_discovery");
+ if (existing) {
+ kernfs_put(existing);
+ kfree(ip_top);
+ kfree(early_entry);
+ return 0;
+ }
+ }
+
+ res = kobject_init_and_add(&ip_top->kobj, &ip_discovery_ktype,
+ &pdev->dev.kobj, "ip_discovery");
+ if (res)
+ goto err_put_kobj;
+
+ adev->discovery.ip_top = ip_top;
+
+ die_kset = &ip_top->die_kset;
+ kobject_set_name(&die_kset->kobj, "%s", "die");
+ die_kset->kobj.parent = &ip_top->kobj;
+ die_kset->kobj.ktype = &die_kobj_ktype;
+ res = kset_register(&ip_top->die_kset);
+ if (res)
+ goto err_put_die_kset;
+
+ for (ii = 0; ii < ARRAY_SIZE(ip_hw_attr); ii++)
+ ip_hw_instance_attrs[ii] = &ip_hw_attr[ii].attr;
+ ip_hw_instance_attrs[ii] = NULL;
+
+ res = amdgpu_discovery_sysfs_recurse(NULL, ip_top);
+ if (res)
+ goto err_put_die_kset;
+
+ early_entry->pdev = pdev;
+ early_entry->ip_top = ip_top;
+ mutex_lock(&early_ip_discovery_mutex);
+ list_add(&early_entry->list, &early_ip_discovery_list);
+ mutex_unlock(&early_ip_discovery_mutex);
+
+ return 0;
+
+err_put_die_kset:
+ kobject_put(&ip_top->die_kset.kobj);
+err_put_kobj:
+ kobject_put(&ip_top->kobj);
+ kfree(early_entry);
+ adev->discovery.ip_top = NULL;
+ return res;
+}
+
+void amdgpu_discovery_sysfs_early_fini(struct pci_dev *pdev)
+{
+ struct early_ip_discovery *entry, *tmp_entry;
+ struct ip_discovery_top *ip_top = NULL;
+ struct list_head *el, *tmp;
+ struct kset *die_kset;
+
+ /* Find the entry in our tracking list */
+ mutex_lock(&early_ip_discovery_mutex);
+ list_for_each_entry_safe(entry, tmp_entry, &early_ip_discovery_list, list) {
+ if (entry->pdev == pdev) {
+ ip_top = entry->ip_top;
+ list_del(&entry->list);
+ kfree(entry);
+ break;
+ }
+ }
+ mutex_unlock(&early_ip_discovery_mutex);
+
+ if (!ip_top)
+ return;
+
+ /* Clean up sysfs hierarchy */
+ die_kset = &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(&ip_top->die_kset.kobj);
+ kobject_put(&ip_top->kobj);
+ /* ip_top itself will be freed by kobject_put via ip_disc_release */
+}
/* ================================================== */
@@ -1504,6 +1716,9 @@ static int amdgpu_discovery_reg_base_init(struct amdgpu_device *adev)
r = amdgpu_discovery_init(adev);
if (r)
return r;
+
+ amdgpu_discovery_sysfs_early_init(adev, adev->pdev);
+
discovery_bin = adev->discovery.bin;
wafl_ver = 0;
adev->gfx.xcc_mask = 0;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h
index e0010f6a3eda..edc78184e0f3 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h
@@ -53,4 +53,9 @@ int amdgpu_discovery_get_gc_major_minor_version(struct amdgpu_device *adev,
void amdgpu_discovery_dump(struct amdgpu_device *adev, struct drm_printer *p);
+/* Early sysfs functions for persistent ip_discovery export */
+int amdgpu_discovery_sysfs_early_init(struct amdgpu_device *adev,
+ struct pci_dev *pdev);
+void amdgpu_discovery_sysfs_early_fini(struct pci_dev *pdev);
+
#endif /* __AMDGPU_DISCOVERY__ */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index b4120207bfa0..65f2de86fdd2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -2557,6 +2557,8 @@ amdgpu_pci_remove(struct pci_dev *pdev)
amdgpu_driver_unload_kms(dev);
+ amdgpu_discovery_sysfs_early_fini(pdev);
+
/*
* Flush any in flight DMA operations from device.
* Clear the Bus Master Enable bit and then wait on the PCIe Device