Skip to content

Commit bd60716

Browse files
kentrussellalexdeucher
authored andcommitted
drm/amdgpu: Enable reading FRU chip via I2C v3
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> Reviewed-by: Andrey Grodzovsky <andrey.grodzovsky@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
1 parent 3148a6a commit bd60716

File tree

6 files changed

+292
-1
lines changed

6 files changed

+292
-1
lines changed

Documentation/gpu/amdgpu.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,27 @@ busy_percent
202202

203203
.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c
204204
:doc: busy_percent
205+
206+
GPU Product Information
207+
=======================
208+
209+
Information about the GPU can be obtained on certain cards
210+
via sysfs
211+
212+
product_name
213+
------------
214+
215+
.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
216+
:doc: product_name
217+
218+
product_number
219+
--------------
220+
221+
.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
222+
:doc: product_name
223+
224+
serial_number
225+
-------------
226+
227+
.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
228+
:doc: serial_number

drivers/gpu/drm/amd/amdgpu/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
5555
amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o amdgpu_ids.o \
5656
amdgpu_gmc.o amdgpu_mmhub.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o amdgpu_vm_cpu.o \
5757
amdgpu_vm_sdma.o amdgpu_discovery.o amdgpu_ras_eeprom.o amdgpu_nbio.o \
58-
amdgpu_umc.o smu_v11_0_i2c.o
58+
amdgpu_umc.o smu_v11_0_i2c.o amdgpu_fru_eeprom.o
5959

6060
amdgpu-$(CONFIG_PERF_EVENTS) += amdgpu_pmu.o
6161

drivers/gpu/drm/amd/amdgpu/amdgpu.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,11 @@ struct amdgpu_device {
974974

975975
bool pm_sysfs_en;
976976
bool ucode_sysfs_en;
977+
978+
/* Chip product information */
979+
char product_number[16];
980+
char product_name[32];
981+
char serial[16];
977982
};
978983

979984
static inline struct amdgpu_device *amdgpu_ttm_adev(struct ttm_bo_device *bdev)

drivers/gpu/drm/amd/amdgpu/amdgpu_device.c

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#include "amdgpu_xgmi.h"
6565
#include "amdgpu_ras.h"
6666
#include "amdgpu_pmu.h"
67+
#include "amdgpu_fru_eeprom.h"
6768

6869
#include <linux/suspend.h>
6970
#include <drm/task_barrier.h>
@@ -137,6 +138,72 @@ static DEVICE_ATTR(pcie_replay_count, S_IRUGO,
137138

138139
static void amdgpu_device_get_pcie_info(struct amdgpu_device *adev);
139140

141+
/**
142+
* DOC: product_name
143+
*
144+
* The amdgpu driver provides a sysfs API for reporting the product name
145+
* for the device
146+
* The file serial_number is used for this and returns the product name
147+
* as returned from the FRU.
148+
* NOTE: This is only available for certain server cards
149+
*/
150+
151+
static ssize_t amdgpu_device_get_product_name(struct device *dev,
152+
struct device_attribute *attr, char *buf)
153+
{
154+
struct drm_device *ddev = dev_get_drvdata(dev);
155+
struct amdgpu_device *adev = ddev->dev_private;
156+
157+
return snprintf(buf, PAGE_SIZE, "%s\n", adev->product_name);
158+
}
159+
160+
static DEVICE_ATTR(product_name, S_IRUGO,
161+
amdgpu_device_get_product_name, NULL);
162+
163+
/**
164+
* DOC: product_number
165+
*
166+
* The amdgpu driver provides a sysfs API for reporting the part number
167+
* for the device
168+
* The file serial_number is used for this and returns the part number
169+
* as returned from the FRU.
170+
* NOTE: This is only available for certain server cards
171+
*/
172+
173+
static ssize_t amdgpu_device_get_product_number(struct device *dev,
174+
struct device_attribute *attr, char *buf)
175+
{
176+
struct drm_device *ddev = dev_get_drvdata(dev);
177+
struct amdgpu_device *adev = ddev->dev_private;
178+
179+
return snprintf(buf, PAGE_SIZE, "%s\n", adev->product_number);
180+
}
181+
182+
static DEVICE_ATTR(product_number, S_IRUGO,
183+
amdgpu_device_get_product_number, NULL);
184+
185+
/**
186+
* DOC: serial_number
187+
*
188+
* The amdgpu driver provides a sysfs API for reporting the serial number
189+
* for the device
190+
* The file serial_number is used for this and returns the serial number
191+
* as returned from the FRU.
192+
* NOTE: This is only available for certain server cards
193+
*/
194+
195+
static ssize_t amdgpu_device_get_serial_number(struct device *dev,
196+
struct device_attribute *attr, char *buf)
197+
{
198+
struct drm_device *ddev = dev_get_drvdata(dev);
199+
struct amdgpu_device *adev = ddev->dev_private;
200+
201+
return snprintf(buf, PAGE_SIZE, "%s\n", adev->serial);
202+
}
203+
204+
static DEVICE_ATTR(serial_number, S_IRUGO,
205+
amdgpu_device_get_serial_number, NULL);
206+
140207
/**
141208
* amdgpu_device_supports_boco - Is the device a dGPU with HG/PX power control
142209
*
@@ -1975,6 +2042,8 @@ static int amdgpu_device_ip_init(struct amdgpu_device *adev)
19752042
amdgpu_xgmi_add_device(adev);
19762043
amdgpu_amdkfd_device_init(adev);
19772044

2045+
amdgpu_fru_get_product_info(adev);
2046+
19782047
init_failed:
19792048
if (amdgpu_sriov_vf(adev))
19802049
amdgpu_virt_release_full_gpu(adev, true);
@@ -3189,6 +3258,24 @@ int amdgpu_device_init(struct amdgpu_device *adev,
31893258
return r;
31903259
}
31913260

3261+
r = device_create_file(adev->dev, &dev_attr_product_name);
3262+
if (r) {
3263+
dev_err(adev->dev, "Could not create product_name");
3264+
return r;
3265+
}
3266+
3267+
r = device_create_file(adev->dev, &dev_attr_product_number);
3268+
if (r) {
3269+
dev_err(adev->dev, "Could not create product_number");
3270+
return r;
3271+
}
3272+
3273+
r = device_create_file(adev->dev, &dev_attr_serial_number);
3274+
if (r) {
3275+
dev_err(adev->dev, "Could not create serial_number");
3276+
return r;
3277+
}
3278+
31923279
if (IS_ENABLED(CONFIG_PERF_EVENTS))
31933280
r = amdgpu_pmu_init(adev);
31943281
if (r)
@@ -3271,6 +3358,9 @@ void amdgpu_device_fini(struct amdgpu_device *adev)
32713358
device_remove_file(adev->dev, &dev_attr_pcie_replay_count);
32723359
if (adev->ucode_sysfs_en)
32733360
amdgpu_ucode_sysfs_fini(adev);
3361+
device_remove_file(adev->dev, &dev_attr_product_name);
3362+
device_remove_file(adev->dev, &dev_attr_product_number);
3363+
device_remove_file(adev->dev, &dev_attr_serial_number);
32743364
if (IS_ENABLED(CONFIG_PERF_EVENTS))
32753365
amdgpu_pmu_fini(adev);
32763366
if (amdgpu_discovery && adev->asic_type >= CHIP_NAVI10)
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*
2+
* Copyright 2019 Advanced Micro Devices, Inc.
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a
5+
* copy of this software and associated documentation files (the "Software"),
6+
* to deal in the Software without restriction, including without limitation
7+
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
8+
* and/or sell copies of the Software, and to permit persons to whom the
9+
* Software is furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17+
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18+
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19+
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20+
* OTHER DEALINGS IN THE SOFTWARE.
21+
*
22+
*/
23+
#include "amdgpu.h"
24+
#include "amdgpu_i2c.h"
25+
#include "smu_v11_0_i2c.h"
26+
#include "atom.h"
27+
28+
#define I2C_PRODUCT_INFO_ADDR 0xAC
29+
#define I2C_PRODUCT_INFO_ADDR_SIZE 0x2
30+
#define I2C_PRODUCT_INFO_OFFSET 0xC0
31+
32+
int amdgpu_fru_read_eeprom(struct amdgpu_device *adev, uint32_t addrptr,
33+
unsigned char *buff)
34+
{
35+
int ret, size;
36+
struct i2c_msg msg = {
37+
.addr = I2C_PRODUCT_INFO_ADDR,
38+
.flags = I2C_M_RD,
39+
.buf = buff,
40+
};
41+
buff[0] = 0;
42+
buff[1] = addrptr;
43+
msg.len = I2C_PRODUCT_INFO_ADDR_SIZE + 1;
44+
ret = i2c_transfer(&adev->pm.smu_i2c, &msg, 1);
45+
46+
if (ret < 1) {
47+
DRM_WARN("FRU: Failed to get size field");
48+
return ret;
49+
}
50+
51+
/* The size returned by the i2c requires subtraction of 0xC0 since the
52+
* size apparently always reports as 0xC0+actual size.
53+
*/
54+
size = buff[2] - I2C_PRODUCT_INFO_OFFSET;
55+
/* Add 1 since address field was 1 byte */
56+
buff[1] = addrptr + 1;
57+
58+
msg.len = I2C_PRODUCT_INFO_ADDR_SIZE + size;
59+
ret = i2c_transfer(&adev->pm.smu_i2c, &msg, 1);
60+
61+
if (ret < 1) {
62+
DRM_WARN("FRU: Failed to get data field");
63+
return ret;
64+
}
65+
66+
return size;
67+
}
68+
69+
int amdgpu_fru_get_product_info(struct amdgpu_device *adev)
70+
{
71+
unsigned char buff[32];
72+
int addrptr = 0, size = 0;
73+
74+
/* If algo exists, it means that the i2c_adapter's initialized */
75+
if (!adev->pm.smu_i2c.algo) {
76+
DRM_WARN("Cannot access FRU, EEPROM accessor not initialized");
77+
return 0;
78+
}
79+
80+
/* There's a lot of repetition here. This is due to the FRU having
81+
* variable-length fields. To get the information, we have to find the
82+
* size of each field, and then keep reading along and reading along
83+
* until we get all of the data that we want. We use addrptr to track
84+
* the address as we go
85+
*/
86+
87+
/* The first fields are all of size 1-byte, from 0-7 are offsets that
88+
* contain information that isn't useful to us.
89+
* Bytes 8-a are all 1-byte and refer to the size of the entire struct,
90+
* and the language field, so just start from 0xb, manufacturer size
91+
*/
92+
addrptr = 0xb;
93+
size = amdgpu_fru_read_eeprom(adev, addrptr, buff);
94+
if (size < 1) {
95+
DRM_ERROR("Failed to read FRU Manufacturer, ret:%d", size);
96+
return size;
97+
}
98+
99+
/* Increment the addrptr by the size of the field, and 1 due to the
100+
* size field being 1 byte. This pattern continues below.
101+
*/
102+
addrptr += size + 1;
103+
size = amdgpu_fru_read_eeprom(adev, addrptr, buff);
104+
if (size < 1) {
105+
DRM_ERROR("Failed to read FRU product name, ret:%d", size);
106+
return size;
107+
}
108+
109+
/* Start at 2 due to buff using fields 0 and 1 for the address */
110+
memcpy(adev->product_name, &buff[2], size);
111+
adev->product_name[size] = '\0';
112+
113+
addrptr += size + 1;
114+
size = amdgpu_fru_read_eeprom(adev, addrptr, buff);
115+
if (size < 1) {
116+
DRM_ERROR("Failed to read FRU product number, ret:%d", size);
117+
return size;
118+
}
119+
120+
memcpy(adev->product_number, &buff[2], size);
121+
adev->product_number[size] = '\0';
122+
123+
addrptr += size + 1;
124+
size = amdgpu_fru_read_eeprom(adev, addrptr, buff);
125+
126+
if (size < 1) {
127+
DRM_ERROR("Failed to read FRU product version, ret:%d", size);
128+
return size;
129+
}
130+
131+
addrptr += size + 1;
132+
size = amdgpu_fru_read_eeprom(adev, addrptr, buff);
133+
134+
if (size < 1) {
135+
DRM_ERROR("Failed to read FRU serial number, ret:%d", size);
136+
return size;
137+
}
138+
139+
memcpy(adev->serial, &buff[2], size);
140+
adev->serial[size] = '\0';
141+
142+
return 0;
143+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2020 Advanced Micro Devices, Inc.
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a
5+
* copy of this software and associated documentation files (the "Software"),
6+
* to deal in the Software without restriction, including without limitation
7+
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
8+
* and/or sell copies of the Software, and to permit persons to whom the
9+
* Software is furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17+
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18+
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19+
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20+
* OTHER DEALINGS IN THE SOFTWARE.
21+
*
22+
*/
23+
24+
#ifndef __AMDGPU_PRODINFO_H__
25+
#define __AMDGPU_PRODINFO_H__
26+
27+
int amdgpu_fru_get_product_info(struct amdgpu_device *adev);
28+
29+
#endif // __AMDGPU_PRODINFO_H__

0 commit comments

Comments
 (0)