Skip to content

Commit 05883f1

Browse files
committed
Enable reading FRU chip via I2C
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. Change-Id: Id506dc3e29d58f2d23bc13df4fec5fa1da778e31 Signed-off-by: Kent Russell <kent.russell@amd.com>
1 parent 9825ef3 commit 05883f1

File tree

3 files changed

+244
-0
lines changed

3 files changed

+244
-0
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,6 +1024,10 @@ struct amdgpu_device {
10241024

10251025
bool pm_sysfs_en;
10261026
bool ucode_sysfs_en;
1027+
1028+
char product_name[32];
1029+
char product_number[16];
1030+
char serial[16];
10271031
};
10281032

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

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

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,72 @@ static DEVICE_ATTR(pcie_replay_count, S_IRUGO,
138138

139139
static void amdgpu_device_get_pcie_info(struct amdgpu_device *adev);
140140

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+
141207
/**
142208
* amdgpu_device_supports_boco - Is the device a dGPU with HG/PX power control
143209
*
@@ -3189,6 +3255,24 @@ int amdgpu_device_init(struct amdgpu_device *adev,
31893255
return r;
31903256
}
31913257

3258+
r = device_create_file(adev->dev, &dev_attr_product_name);
3259+
if (r) {
3260+
dev_err(adev->dev, "Could not create product_name");
3261+
return r;
3262+
}
3263+
3264+
r = device_create_file(adev->dev, &dev_attr_product_number);
3265+
if (r) {
3266+
dev_err(adev->dev, "Could not create product_number");
3267+
return r;
3268+
}
3269+
3270+
r = device_create_file(adev->dev, &dev_attr_serial_number);
3271+
if (r) {
3272+
dev_err(adev->dev, "Could not create serial_number");
3273+
return r;
3274+
}
3275+
31923276
if (IS_ENABLED(CONFIG_PERF_EVENTS))
31933277
r = amdgpu_pmu_init(adev);
31943278
if (r)
@@ -3268,6 +3352,9 @@ void amdgpu_device_fini(struct amdgpu_device *adev)
32683352
device_remove_file(adev->dev, &dev_attr_pcie_replay_count);
32693353
if (adev->ucode_sysfs_en)
32703354
amdgpu_ucode_sysfs_fini(adev);
3355+
device_remove_file(adev->dev, &dev_attr_product_name);
3356+
device_remove_file(adev->dev, &dev_attr_product_number);
3357+
device_remove_file(adev->dev, &dev_attr_serial_number);
32713358
if (IS_ENABLED(CONFIG_PERF_EVENTS))
32723359
amdgpu_pmu_fini(adev);
32733360
amdgpu_debugfs_preempt_cleanup(adev);

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

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929

3030
#define EEPROM_I2C_TARGET_ADDR_ARCTURUS 0xA8
3131
#define EEPROM_I2C_TARGET_ADDR_VEGA20 0xA0
32+
#define EEPROM_I2C_PI_ADDR 0xAC
33+
#define I2C_PRODUCT_INFO_OFFSET 0xC0
3234

3335
/*
3436
* The 2 macros bellow represent the actual size in bytes that
@@ -79,6 +81,155 @@ static void __decode_table_header_from_buff(struct amdgpu_ras_eeprom_table_heade
7981
hdr->checksum = le32_to_cpu(pp[4]);
8082
}
8183

84+
static int amdgpu_ras_eeprom_get_product_info(struct amdgpu_ras_eeprom_control *control)
85+
{
86+
int ret = 0, i, addrptr = 0, size = 0;
87+
struct amdgpu_device *adev = to_amdgpu_device(control);
88+
unsigned char buff[34], sizebuff[3];
89+
/* size_msg will be the message to get the size of a field,
90+
* since it's always 1 for field length
91+
*/
92+
struct i2c_msg size_msg = {
93+
.addr = EEPROM_I2C_PI_ADDR,
94+
.flags = I2C_M_RD,
95+
.len = EEPROM_ADDRESS_SIZE + 1,
96+
.buf = sizebuff,
97+
};
98+
99+
/* Add the size obtained above to .len as required */
100+
struct i2c_msg msg = {
101+
.addr = EEPROM_I2C_PI_ADDR,
102+
.flags = I2C_M_RD,
103+
.len = EEPROM_ADDRESS_SIZE,
104+
.buf = buff,
105+
};
106+
107+
/* Not supported before VG20 */
108+
if (adev->asic_type < CHIP_VEGA20)
109+
return 0;
110+
111+
/* There's a lot of repetition here. This is due to the FRU having
112+
* variable-length fields. To get the information, we have to find the
113+
* size of each field, and then keep reading along and reading along
114+
* until we get all of the data that we want. We use addrptr to track
115+
* the address as we go, while we create 2 i2c messages, one to obtain
116+
* size (since that's always 1-byte, and one to get the data.
117+
*
118+
* The size returned by the i2c requires subtraction of 0xC0 since the
119+
* size apparently always reports as 0xC0+actual size.
120+
*
121+
* NOTE: We don't lock the mutex since this data is ummutable
122+
*/
123+
124+
/* The first fields are all of size 1-byte, from 0-7 are offsets that
125+
* aren't entirely useful. Bytes 8-e are all 1-byte and refer to the
126+
* size of the entire struct, and the language field, so just start
127+
* from 0xf, which is the size of the manufacturer
128+
*/
129+
addrptr = 0xf;
130+
131+
/* The i2c message reads the address from the buffer passed in */
132+
sizebuff[0] = 0;
133+
sizebuff[1] = 0xf;
134+
135+
ret = i2c_transfer(&control->eeprom_accessor, &size_msg, 1);
136+
137+
if (ret < 1) {
138+
DRM_ERROR("Failed to read EEPROM manufacturer size, ret:%d", ret);
139+
return ret;
140+
}
141+
142+
size = sizebuff[2] - I2C_PRODUCT_INFO_OFFSET;
143+
msg.len = EEPROM_ADDRESS_SIZE + size;
144+
/* Add 1 since address field was 1 byte */
145+
addrptr += 1;
146+
/* Now fill in the actual buffer with the desired address */
147+
buff[0] = 0;
148+
buff[1] = addrptr;
149+
ret = i2c_transfer(&control->eeprom_accessor, &msg, 1);
150+
151+
if (ret < 1) {
152+
DRM_ERROR("Failed to read EEPROM product name, ret:%d", ret);
153+
return ret;
154+
}
155+
memcpy(adev->product_name, &buff[2], size);
156+
adev->product_name[size] = '\0';
157+
158+
/* Increase the addrptr by the appropriate size */
159+
addrptr += size;
160+
161+
/* Get the next field, product name, starting with size */
162+
sizebuff[0] = 0;
163+
sizebuff[1] = addrptr;
164+
165+
ret = i2c_transfer(&control->eeprom_accessor, &size_msg, 1);
166+
167+
if (ret < 1) {
168+
DRM_ERROR("Failed to read EEPROM product number size, ret:%d", ret);
169+
return ret;
170+
}
171+
172+
size = sizebuff[2] - I2C_PRODUCT_INFO_OFFSET;
173+
msg.len = EEPROM_ADDRESS_SIZE + size;
174+
/* Add 1 since address field was 1 byte */
175+
addrptr += 1;
176+
buff[0] = 0;
177+
buff[1] = addrptr;
178+
179+
ret = i2c_transfer(&control->eeprom_accessor, &msg, 1);
180+
181+
if (ret < 1) {
182+
DRM_ERROR("Failed to read EEPROM product number, ret:%d", ret);
183+
return ret;
184+
}
185+
memcpy(adev->product_number, &buff[2], size);
186+
adev->product_number[size] = '\0';
187+
188+
/* Increase the addrptr by the appropriate size */
189+
addrptr += size;
190+
191+
/* Skip over the Product Version, it's not very useful */
192+
sizebuff[0] = 0;
193+
sizebuff[1] = addrptr;
194+
195+
ret = i2c_transfer(&control->eeprom_accessor, &size_msg, 1);
196+
197+
if (ret < 1) {
198+
DRM_ERROR("Failed to read EEPROM product version size, ret:%d", ret);
199+
return ret;
200+
}
201+
size = sizebuff[2] - I2C_PRODUCT_INFO_OFFSET;
202+
/* Add 1 since address field was 1 byte, plus the size of the version */
203+
addrptr += size + 1;
204+
205+
/* Get the last field, serial number */
206+
sizebuff[0] = 0;
207+
sizebuff[1] = addrptr;
208+
209+
ret = i2c_transfer(&control->eeprom_accessor, &size_msg, 1);
210+
211+
if (ret < 1) {
212+
DRM_ERROR("Failed to read EEPROM serial number size, ret:%d", ret);
213+
return ret;
214+
}
215+
size = sizebuff[2] - I2C_PRODUCT_INFO_OFFSET;
216+
msg.len = EEPROM_ADDRESS_SIZE + size;
217+
/* Add 1 since address field was 1 byte */
218+
addrptr += 1;
219+
buff[0] = 0;
220+
buff[1] = addrptr;
221+
ret = i2c_transfer(&control->eeprom_accessor, &msg, 1);
222+
223+
if (ret < 1) {
224+
DRM_ERROR("Failed to read EEPROM serial info, ret:%d", ret);
225+
return ret;
226+
}
227+
memcpy(adev->serial, &buff[2], size);
228+
adev->serial[size] = '\0';
229+
230+
return 0;
231+
}
232+
82233
static int __update_table_header(struct amdgpu_ras_eeprom_control *control,
83234
unsigned char *buff)
84235
{
@@ -260,6 +411,8 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
260411
ret = amdgpu_ras_eeprom_reset_table(control);
261412
}
262413

414+
amdgpu_ras_eeprom_get_product_info(control);
415+
263416
return ret == 1 ? 0 : -EIO;
264417
}
265418

0 commit comments

Comments
 (0)