AMD-GFX Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Enable reading FRU chip via I2C v3
@ 2020-03-19 14:16 Kent Russell
  2020-03-19 14:45 ` Andrey Grodzovsky
  0 siblings, 1 reply; 2+ messages in thread
From: Kent Russell @ 2020-03-19 14:16 UTC (permalink / raw)
  To: amd-gfx; +Cc: Kent Russell

Allow for reading of information like manufacturer, product number
and serial number from the FRU chip. Report the serial number as
the new sysfs file serial_number. Note that this only works on
server cards, as consumer cards do not feature the FRU chip, which
contains this information.

v2: Add documentation to amdgpu.rst, add helper functions,
    rename functions for consistency, fix bad starting offset
v3: Remove testing definitions

Signed-off-by: Kent Russell <kent.russell@amd.com>
---
 Documentation/gpu/amdgpu.rst                  |  24 +++
 drivers/gpu/drm/amd/amdgpu/Makefile           |   2 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu.h           |   5 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_device.c    |  90 +++++++++++
 .../gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c    | 143 ++++++++++++++++++
 .../gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.h    |  29 ++++
 6 files changed, 292 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c
 create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.h

diff --git a/Documentation/gpu/amdgpu.rst b/Documentation/gpu/amdgpu.rst
index 0efede580039..d9ea09ec8e24 100644
--- a/Documentation/gpu/amdgpu.rst
+++ b/Documentation/gpu/amdgpu.rst
@@ -202,3 +202,27 @@ busy_percent
 
 .. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c
    :doc: busy_percent
+
+GPU Product Information
+=======================
+
+Information about the GPU can be obtained on certain cards
+via sysfs
+
+product_name
+------------
+
+.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+   :doc: product_name
+
+product_number
+--------------
+
+.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+   :doc: product_name
+
+serial_number
+-------------
+
+.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+   :doc: serial_number
diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
index c2bbcdd9c875..210d57a4afc8 100644
--- a/drivers/gpu/drm/amd/amdgpu/Makefile
+++ b/drivers/gpu/drm/amd/amdgpu/Makefile
@@ -55,7 +55,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
 	amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o amdgpu_ids.o \
 	amdgpu_gmc.o amdgpu_mmhub.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o amdgpu_vm_cpu.o \
 	amdgpu_vm_sdma.o amdgpu_discovery.o amdgpu_ras_eeprom.o amdgpu_nbio.o \
-	amdgpu_umc.o smu_v11_0_i2c.o
+	amdgpu_umc.o smu_v11_0_i2c.o amdgpu_fru_eeprom.o
 
 amdgpu-$(CONFIG_PERF_EVENTS) += amdgpu_pmu.o
 
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 87c2523076af..7dd74253e7b6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -979,6 +979,11 @@ struct amdgpu_device {
 
 	bool                            pm_sysfs_en;
 	bool                            ucode_sysfs_en;
+
+	/* Chip product information */
+	char				product_number[16];
+	char				product_name[32];
+	char				serial[16];
 };
 
 static inline struct amdgpu_device *amdgpu_ttm_adev(struct ttm_bo_device *bdev)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 729565f79cfe..80a654326190 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -64,6 +64,7 @@
 #include "amdgpu_xgmi.h"
 #include "amdgpu_ras.h"
 #include "amdgpu_pmu.h"
+#include "amdgpu_fru_eeprom.h"
 
 #include <linux/suspend.h>
 #include <drm/task_barrier.h>
@@ -137,6 +138,72 @@ static DEVICE_ATTR(pcie_replay_count, S_IRUGO,
 
 static void amdgpu_device_get_pcie_info(struct amdgpu_device *adev);
 
+/**
+ * DOC: product_name
+ *
+ * The amdgpu driver provides a sysfs API for reporting the product name
+ * for the device
+ * The file serial_number is used for this and returns the product name
+ * as returned from the FRU.
+ * NOTE: This is only available for certain server cards
+ */
+
+static ssize_t amdgpu_device_get_product_name(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct drm_device *ddev = dev_get_drvdata(dev);
+	struct amdgpu_device *adev = ddev->dev_private;
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", adev->product_name);
+}
+
+static DEVICE_ATTR(product_name, S_IRUGO,
+		amdgpu_device_get_product_name, NULL);
+
+/**
+ * DOC: product_number
+ *
+ * The amdgpu driver provides a sysfs API for reporting the part number
+ * for the device
+ * The file serial_number is used for this and returns the part number
+ * as returned from the FRU.
+ * NOTE: This is only available for certain server cards
+ */
+
+static ssize_t amdgpu_device_get_product_number(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct drm_device *ddev = dev_get_drvdata(dev);
+	struct amdgpu_device *adev = ddev->dev_private;
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", adev->product_number);
+}
+
+static DEVICE_ATTR(product_number, S_IRUGO,
+		amdgpu_device_get_product_number, NULL);
+
+/**
+ * DOC: serial_number
+ *
+ * The amdgpu driver provides a sysfs API for reporting the serial number
+ * for the device
+ * The file serial_number is used for this and returns the serial number
+ * as returned from the FRU.
+ * NOTE: This is only available for certain server cards
+ */
+
+static ssize_t amdgpu_device_get_serial_number(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct drm_device *ddev = dev_get_drvdata(dev);
+	struct amdgpu_device *adev = ddev->dev_private;
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", adev->serial);
+}
+
+static DEVICE_ATTR(serial_number, S_IRUGO,
+		amdgpu_device_get_serial_number, NULL);
+
 /**
  * amdgpu_device_supports_boco - Is the device a dGPU with HG/PX power control
  *
@@ -1977,6 +2044,8 @@ static int amdgpu_device_ip_init(struct amdgpu_device *adev)
 		amdgpu_xgmi_add_device(adev);
 	amdgpu_amdkfd_device_init(adev);
 
+	amdgpu_fru_get_product_info(adev);
+
 init_failed:
 	if (amdgpu_sriov_vf(adev))
 		amdgpu_virt_release_full_gpu(adev, true);
@@ -3188,6 +3257,24 @@ int amdgpu_device_init(struct amdgpu_device *adev,
 		return r;
 	}
 
+	r = device_create_file(adev->dev, &dev_attr_product_name);
+	if (r) {
+		dev_err(adev->dev, "Could not create product_name");
+		return r;
+	}
+
+	r = device_create_file(adev->dev, &dev_attr_product_number);
+	if (r) {
+		dev_err(adev->dev, "Could not create product_number");
+		return r;
+	}
+
+	r = device_create_file(adev->dev, &dev_attr_serial_number);
+	if (r) {
+		dev_err(adev->dev, "Could not create serial_number");
+		return r;
+	}
+
 	if (IS_ENABLED(CONFIG_PERF_EVENTS))
 		r = amdgpu_pmu_init(adev);
 	if (r)
@@ -3270,6 +3357,9 @@ void amdgpu_device_fini(struct amdgpu_device *adev)
 	device_remove_file(adev->dev, &dev_attr_pcie_replay_count);
 	if (adev->ucode_sysfs_en)
 		amdgpu_ucode_sysfs_fini(adev);
+	device_remove_file(adev->dev, &dev_attr_product_name);
+	device_remove_file(adev->dev, &dev_attr_product_number);
+	device_remove_file(adev->dev, &dev_attr_serial_number);
 	if (IS_ENABLED(CONFIG_PERF_EVENTS))
 		amdgpu_pmu_fini(adev);
 	if (amdgpu_discovery && adev->asic_type >= CHIP_NAVI10)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c
new file mode 100644
index 000000000000..990dee6e22d5
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "amdgpu.h"
+#include "amdgpu_i2c.h"
+#include "smu_v11_0_i2c.h"
+#include "atom.h"
+
+#define I2C_PRODUCT_INFO_ADDR		0xAC
+#define I2C_PRODUCT_INFO_ADDR_SIZE	0x2
+#define I2C_PRODUCT_INFO_OFFSET		0xC0
+
+int amdgpu_fru_read_eeprom(struct amdgpu_device *adev, uint32_t addrptr,
+			   unsigned char *buff)
+{
+	int ret, size;
+	struct i2c_msg msg = {
+			.addr   = I2C_PRODUCT_INFO_ADDR,
+			.flags  = I2C_M_RD,
+			.buf    = buff,
+	};
+	buff[0] = 0;
+	buff[1] = addrptr;
+	msg.len = I2C_PRODUCT_INFO_ADDR_SIZE + 1;
+	ret = i2c_transfer(&adev->pm.smu_i2c, &msg, 1);
+
+	if (ret < 1) {
+		DRM_WARN("FRU: Failed to get size field");
+		return ret;
+	}
+
+	/* The size returned by the i2c requires subtraction of 0xC0 since the
+	 * size apparently always reports as 0xC0+actual size.
+	 */
+	size = buff[2] - I2C_PRODUCT_INFO_OFFSET;
+	/* Add 1 since address field was 1 byte */
+	buff[1] = addrptr + 1;
+
+	msg.len = I2C_PRODUCT_INFO_ADDR_SIZE + size;
+	ret = i2c_transfer(&adev->pm.smu_i2c, &msg, 1);
+
+	if (ret < 1) {
+		DRM_WARN("FRU: Failed to get data field");
+		return ret;
+	}
+
+	return size;
+}
+
+int amdgpu_fru_get_product_info(struct amdgpu_device *adev)
+{
+	unsigned char buff[32];
+	int addrptr = 0, size = 0;
+
+	/* If algo exists, it means that the i2c_adapter's initialized */
+	if (!adev->pm.smu_i2c.algo) {
+		DRM_WARN("Cannot access FRU, EEPROM accessor not initialized");
+		return 0;
+	}
+
+	/* There's a lot of repetition here. This is due to the FRU having
+	 * variable-length fields. To get the information, we have to find the
+	 * size of each field, and then keep reading along and reading along
+	 * until we get all of the data that we want. We use addrptr to track
+	 * the address as we go
+	 */
+
+	/* The first fields are all of size 1-byte, from 0-7 are offsets that
+	 * contain information that isn't useful to us.
+	 * Bytes 8-a are all 1-byte and refer to the size of the entire struct,
+	 * and the language field, so just start from 0xb, manufacturer size
+	 */
+	addrptr = 0xb;
+	size = amdgpu_fru_read_eeprom(adev, addrptr, buff);
+	if (size < 1) {
+		DRM_ERROR("Failed to read FRU Manufacturer, ret:%d", size);
+		return size;
+	}
+
+	/* Increment the addrptr by the size of the field, and 1 due to the
+	 * size field being 1 byte. This pattern continues below.
+	 */
+	addrptr += size + 1;
+	size = amdgpu_fru_read_eeprom(adev, addrptr, buff);
+	if (size < 1) {
+		DRM_ERROR("Failed to read FRU product name, ret:%d", size);
+		return size;
+	}
+
+	/* Start at 2 due to buff using fields 0 and 1 for the address */
+	memcpy(adev->product_name, &buff[2], size);
+	adev->product_name[size] = '\0';
+
+	addrptr += size + 1;
+	size = amdgpu_fru_read_eeprom(adev, addrptr, buff);
+	if (size < 1) {
+		DRM_ERROR("Failed to read FRU product number, ret:%d", size);
+		return size;
+	}
+
+	memcpy(adev->product_number, &buff[2], size);
+	adev->product_number[size] = '\0';
+
+	addrptr += size + 1;
+	size = amdgpu_fru_read_eeprom(adev, addrptr, buff);
+
+	if (size < 1) {
+		DRM_ERROR("Failed to read FRU product version, ret:%d", size);
+		return size;
+	}
+
+	addrptr += size + 1;
+	size = amdgpu_fru_read_eeprom(adev, addrptr, buff);
+
+	if (size < 1) {
+		DRM_ERROR("Failed to read FRU serial number, ret:%d", size);
+		return size;
+	}
+
+	memcpy(adev->serial, &buff[2], size);
+	adev->serial[size] = '\0';
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.h
new file mode 100644
index 000000000000..968115c97e33
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __AMDGPU_PRODINFO_H__
+#define __AMDGPU_PRODINFO_H__
+
+int amdgpu_fru_get_product_info(struct amdgpu_device *adev);
+
+#endif  // __AMDGPU_PRODINFO_H__
-- 
2.17.1

_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

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

* Re: [PATCH] Enable reading FRU chip via I2C v3
  2020-03-19 14:16 [PATCH] Enable reading FRU chip via I2C v3 Kent Russell
@ 2020-03-19 14:45 ` Andrey Grodzovsky
  0 siblings, 0 replies; 2+ messages in thread
From: Andrey Grodzovsky @ 2020-03-19 14:45 UTC (permalink / raw)
  To: Kent Russell, amd-gfx

Looks good to me.

Reviewed-by: Andrey Grodzovsky <andrey.grodzovsky@amd.com>

Andrey

On 3/19/20 10:16 AM, Kent Russell wrote:
> Allow for reading of information like manufacturer, product number
> and serial number from the FRU chip. Report the serial number as
> the new sysfs file serial_number. Note that this only works on
> server cards, as consumer cards do not feature the FRU chip, which
> contains this information.
>
> v2: Add documentation to amdgpu.rst, add helper functions,
>      rename functions for consistency, fix bad starting offset
> v3: Remove testing definitions
>
> Signed-off-by: Kent Russell <kent.russell@amd.com>
> ---
>   Documentation/gpu/amdgpu.rst                  |  24 +++
>   drivers/gpu/drm/amd/amdgpu/Makefile           |   2 +-
>   drivers/gpu/drm/amd/amdgpu/amdgpu.h           |   5 +
>   drivers/gpu/drm/amd/amdgpu/amdgpu_device.c    |  90 +++++++++++
>   .../gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c    | 143 ++++++++++++++++++
>   .../gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.h    |  29 ++++
>   6 files changed, 292 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c
>   create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.h
>
> diff --git a/Documentation/gpu/amdgpu.rst b/Documentation/gpu/amdgpu.rst
> index 0efede580039..d9ea09ec8e24 100644
> --- a/Documentation/gpu/amdgpu.rst
> +++ b/Documentation/gpu/amdgpu.rst
> @@ -202,3 +202,27 @@ busy_percent
>   
>   .. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c
>      :doc: busy_percent
> +
> +GPU Product Information
> +=======================
> +
> +Information about the GPU can be obtained on certain cards
> +via sysfs
> +
> +product_name
> +------------
> +
> +.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> +   :doc: product_name
> +
> +product_number
> +--------------
> +
> +.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> +   :doc: product_name
> +
> +serial_number
> +-------------
> +
> +.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> +   :doc: serial_number
> diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
> index c2bbcdd9c875..210d57a4afc8 100644
> --- a/drivers/gpu/drm/amd/amdgpu/Makefile
> +++ b/drivers/gpu/drm/amd/amdgpu/Makefile
> @@ -55,7 +55,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
>   	amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o amdgpu_ids.o \
>   	amdgpu_gmc.o amdgpu_mmhub.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o amdgpu_vm_cpu.o \
>   	amdgpu_vm_sdma.o amdgpu_discovery.o amdgpu_ras_eeprom.o amdgpu_nbio.o \
> -	amdgpu_umc.o smu_v11_0_i2c.o
> +	amdgpu_umc.o smu_v11_0_i2c.o amdgpu_fru_eeprom.o
>   
>   amdgpu-$(CONFIG_PERF_EVENTS) += amdgpu_pmu.o
>   
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
> index 87c2523076af..7dd74253e7b6 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
> @@ -979,6 +979,11 @@ struct amdgpu_device {
>   
>   	bool                            pm_sysfs_en;
>   	bool                            ucode_sysfs_en;
> +
> +	/* Chip product information */
> +	char				product_number[16];
> +	char				product_name[32];
> +	char				serial[16];
>   };
>   
>   static inline struct amdgpu_device *amdgpu_ttm_adev(struct ttm_bo_device *bdev)
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> index 729565f79cfe..80a654326190 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> @@ -64,6 +64,7 @@
>   #include "amdgpu_xgmi.h"
>   #include "amdgpu_ras.h"
>   #include "amdgpu_pmu.h"
> +#include "amdgpu_fru_eeprom.h"
>   
>   #include <linux/suspend.h>
>   #include <drm/task_barrier.h>
> @@ -137,6 +138,72 @@ static DEVICE_ATTR(pcie_replay_count, S_IRUGO,
>   
>   static void amdgpu_device_get_pcie_info(struct amdgpu_device *adev);
>   
> +/**
> + * DOC: product_name
> + *
> + * The amdgpu driver provides a sysfs API for reporting the product name
> + * for the device
> + * The file serial_number is used for this and returns the product name
> + * as returned from the FRU.
> + * NOTE: This is only available for certain server cards
> + */
> +
> +static ssize_t amdgpu_device_get_product_name(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct drm_device *ddev = dev_get_drvdata(dev);
> +	struct amdgpu_device *adev = ddev->dev_private;
> +
> +	return snprintf(buf, PAGE_SIZE, "%s\n", adev->product_name);
> +}
> +
> +static DEVICE_ATTR(product_name, S_IRUGO,
> +		amdgpu_device_get_product_name, NULL);
> +
> +/**
> + * DOC: product_number
> + *
> + * The amdgpu driver provides a sysfs API for reporting the part number
> + * for the device
> + * The file serial_number is used for this and returns the part number
> + * as returned from the FRU.
> + * NOTE: This is only available for certain server cards
> + */
> +
> +static ssize_t amdgpu_device_get_product_number(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct drm_device *ddev = dev_get_drvdata(dev);
> +	struct amdgpu_device *adev = ddev->dev_private;
> +
> +	return snprintf(buf, PAGE_SIZE, "%s\n", adev->product_number);
> +}
> +
> +static DEVICE_ATTR(product_number, S_IRUGO,
> +		amdgpu_device_get_product_number, NULL);
> +
> +/**
> + * DOC: serial_number
> + *
> + * The amdgpu driver provides a sysfs API for reporting the serial number
> + * for the device
> + * The file serial_number is used for this and returns the serial number
> + * as returned from the FRU.
> + * NOTE: This is only available for certain server cards
> + */
> +
> +static ssize_t amdgpu_device_get_serial_number(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct drm_device *ddev = dev_get_drvdata(dev);
> +	struct amdgpu_device *adev = ddev->dev_private;
> +
> +	return snprintf(buf, PAGE_SIZE, "%s\n", adev->serial);
> +}
> +
> +static DEVICE_ATTR(serial_number, S_IRUGO,
> +		amdgpu_device_get_serial_number, NULL);
> +
>   /**
>    * amdgpu_device_supports_boco - Is the device a dGPU with HG/PX power control
>    *
> @@ -1977,6 +2044,8 @@ static int amdgpu_device_ip_init(struct amdgpu_device *adev)
>   		amdgpu_xgmi_add_device(adev);
>   	amdgpu_amdkfd_device_init(adev);
>   
> +	amdgpu_fru_get_product_info(adev);
> +
>   init_failed:
>   	if (amdgpu_sriov_vf(adev))
>   		amdgpu_virt_release_full_gpu(adev, true);
> @@ -3188,6 +3257,24 @@ int amdgpu_device_init(struct amdgpu_device *adev,
>   		return r;
>   	}
>   
> +	r = device_create_file(adev->dev, &dev_attr_product_name);
> +	if (r) {
> +		dev_err(adev->dev, "Could not create product_name");
> +		return r;
> +	}
> +
> +	r = device_create_file(adev->dev, &dev_attr_product_number);
> +	if (r) {
> +		dev_err(adev->dev, "Could not create product_number");
> +		return r;
> +	}
> +
> +	r = device_create_file(adev->dev, &dev_attr_serial_number);
> +	if (r) {
> +		dev_err(adev->dev, "Could not create serial_number");
> +		return r;
> +	}
> +
>   	if (IS_ENABLED(CONFIG_PERF_EVENTS))
>   		r = amdgpu_pmu_init(adev);
>   	if (r)
> @@ -3270,6 +3357,9 @@ void amdgpu_device_fini(struct amdgpu_device *adev)
>   	device_remove_file(adev->dev, &dev_attr_pcie_replay_count);
>   	if (adev->ucode_sysfs_en)
>   		amdgpu_ucode_sysfs_fini(adev);
> +	device_remove_file(adev->dev, &dev_attr_product_name);
> +	device_remove_file(adev->dev, &dev_attr_product_number);
> +	device_remove_file(adev->dev, &dev_attr_serial_number);
>   	if (IS_ENABLED(CONFIG_PERF_EVENTS))
>   		amdgpu_pmu_fini(adev);
>   	if (amdgpu_discovery && adev->asic_type >= CHIP_NAVI10)
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c
> new file mode 100644
> index 000000000000..990dee6e22d5
> --- /dev/null
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c
> @@ -0,0 +1,143 @@
> +/*
> + * Copyright 2019 Advanced Micro Devices, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + */
> +#include "amdgpu.h"
> +#include "amdgpu_i2c.h"
> +#include "smu_v11_0_i2c.h"
> +#include "atom.h"
> +
> +#define I2C_PRODUCT_INFO_ADDR		0xAC
> +#define I2C_PRODUCT_INFO_ADDR_SIZE	0x2
> +#define I2C_PRODUCT_INFO_OFFSET		0xC0
> +
> +int amdgpu_fru_read_eeprom(struct amdgpu_device *adev, uint32_t addrptr,
> +			   unsigned char *buff)
> +{
> +	int ret, size;
> +	struct i2c_msg msg = {
> +			.addr   = I2C_PRODUCT_INFO_ADDR,
> +			.flags  = I2C_M_RD,
> +			.buf    = buff,
> +	};
> +	buff[0] = 0;
> +	buff[1] = addrptr;
> +	msg.len = I2C_PRODUCT_INFO_ADDR_SIZE + 1;
> +	ret = i2c_transfer(&adev->pm.smu_i2c, &msg, 1);
> +
> +	if (ret < 1) {
> +		DRM_WARN("FRU: Failed to get size field");
> +		return ret;
> +	}
> +
> +	/* The size returned by the i2c requires subtraction of 0xC0 since the
> +	 * size apparently always reports as 0xC0+actual size.
> +	 */
> +	size = buff[2] - I2C_PRODUCT_INFO_OFFSET;
> +	/* Add 1 since address field was 1 byte */
> +	buff[1] = addrptr + 1;
> +
> +	msg.len = I2C_PRODUCT_INFO_ADDR_SIZE + size;
> +	ret = i2c_transfer(&adev->pm.smu_i2c, &msg, 1);
> +
> +	if (ret < 1) {
> +		DRM_WARN("FRU: Failed to get data field");
> +		return ret;
> +	}
> +
> +	return size;
> +}
> +
> +int amdgpu_fru_get_product_info(struct amdgpu_device *adev)
> +{
> +	unsigned char buff[32];
> +	int addrptr = 0, size = 0;
> +
> +	/* If algo exists, it means that the i2c_adapter's initialized */
> +	if (!adev->pm.smu_i2c.algo) {
> +		DRM_WARN("Cannot access FRU, EEPROM accessor not initialized");
> +		return 0;
> +	}
> +
> +	/* There's a lot of repetition here. This is due to the FRU having
> +	 * variable-length fields. To get the information, we have to find the
> +	 * size of each field, and then keep reading along and reading along
> +	 * until we get all of the data that we want. We use addrptr to track
> +	 * the address as we go
> +	 */
> +
> +	/* The first fields are all of size 1-byte, from 0-7 are offsets that
> +	 * contain information that isn't useful to us.
> +	 * Bytes 8-a are all 1-byte and refer to the size of the entire struct,
> +	 * and the language field, so just start from 0xb, manufacturer size
> +	 */
> +	addrptr = 0xb;
> +	size = amdgpu_fru_read_eeprom(adev, addrptr, buff);
> +	if (size < 1) {
> +		DRM_ERROR("Failed to read FRU Manufacturer, ret:%d", size);
> +		return size;
> +	}
> +
> +	/* Increment the addrptr by the size of the field, and 1 due to the
> +	 * size field being 1 byte. This pattern continues below.
> +	 */
> +	addrptr += size + 1;
> +	size = amdgpu_fru_read_eeprom(adev, addrptr, buff);
> +	if (size < 1) {
> +		DRM_ERROR("Failed to read FRU product name, ret:%d", size);
> +		return size;
> +	}
> +
> +	/* Start at 2 due to buff using fields 0 and 1 for the address */
> +	memcpy(adev->product_name, &buff[2], size);
> +	adev->product_name[size] = '\0';
> +
> +	addrptr += size + 1;
> +	size = amdgpu_fru_read_eeprom(adev, addrptr, buff);
> +	if (size < 1) {
> +		DRM_ERROR("Failed to read FRU product number, ret:%d", size);
> +		return size;
> +	}
> +
> +	memcpy(adev->product_number, &buff[2], size);
> +	adev->product_number[size] = '\0';
> +
> +	addrptr += size + 1;
> +	size = amdgpu_fru_read_eeprom(adev, addrptr, buff);
> +
> +	if (size < 1) {
> +		DRM_ERROR("Failed to read FRU product version, ret:%d", size);
> +		return size;
> +	}
> +
> +	addrptr += size + 1;
> +	size = amdgpu_fru_read_eeprom(adev, addrptr, buff);
> +
> +	if (size < 1) {
> +		DRM_ERROR("Failed to read FRU serial number, ret:%d", size);
> +		return size;
> +	}
> +
> +	memcpy(adev->serial, &buff[2], size);
> +	adev->serial[size] = '\0';
> +
> +	return 0;
> +}
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.h
> new file mode 100644
> index 000000000000..968115c97e33
> --- /dev/null
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.h
> @@ -0,0 +1,29 @@
> +/*
> + * Copyright 2020 Advanced Micro Devices, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + */
> +
> +#ifndef __AMDGPU_PRODINFO_H__
> +#define __AMDGPU_PRODINFO_H__
> +
> +int amdgpu_fru_get_product_info(struct amdgpu_device *adev);
> +
> +#endif  // __AMDGPU_PRODINFO_H__
_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

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

end of thread, other threads:[~2020-03-19 14:45 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-03-19 14:16 [PATCH] Enable reading FRU chip via I2C v3 Kent Russell
2020-03-19 14:45 ` Andrey Grodzovsky

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox