@@ -94,39 +94,12 @@ static bool is_fru_eeprom_supported(struct amdgpu_device *adev, u32 *fru_addr)
94
94
}
95
95
}
96
96
97
- static int amdgpu_fru_read_eeprom (struct amdgpu_device * adev , uint32_t addrptr ,
98
- unsigned char * buf , size_t buf_size )
99
- {
100
- int ret ;
101
- u8 size ;
102
-
103
- ret = amdgpu_eeprom_read (adev -> pm .fru_eeprom_i2c_bus , addrptr , buf , 1 );
104
- if (ret < 1 ) {
105
- DRM_WARN ("FRU: Failed to get size field" );
106
- return ret ;
107
- }
108
-
109
- /* The size returned by the i2c requires subtraction of 0xC0 since the
110
- * size apparently always reports as 0xC0+actual size.
111
- */
112
- size = buf [0 ] & 0x3F ;
113
- size = min_t (size_t , size , buf_size );
114
-
115
- ret = amdgpu_eeprom_read (adev -> pm .fru_eeprom_i2c_bus , addrptr + 1 ,
116
- buf , size );
117
- if (ret < 1 ) {
118
- DRM_WARN ("FRU: Failed to get data field" );
119
- return ret ;
120
- }
121
-
122
- return size ;
123
- }
124
-
125
97
int amdgpu_fru_get_product_info (struct amdgpu_device * adev )
126
98
{
127
- unsigned char buf [AMDGPU_PRODUCT_NAME_LEN ] ;
128
- u32 addrptr , fru_addr ;
99
+ unsigned char buf [8 ], * pia ;
100
+ u32 addr , fru_addr ;
129
101
int size , len ;
102
+ u8 csum ;
130
103
131
104
if (!is_fru_eeprom_supported (adev , & fru_addr ))
132
105
return 0 ;
@@ -137,88 +110,102 @@ int amdgpu_fru_get_product_info(struct amdgpu_device *adev)
137
110
return - ENODEV ;
138
111
}
139
112
140
- /* There's a lot of repetition here. This is due to the FRU having
141
- * variable-length fields. To get the information, we have to find the
142
- * size of each field, and then keep reading along and reading along
143
- * until we get all of the data that we want. We use addrptr to track
144
- * the address as we go
145
- */
146
-
147
- /* The first fields are all of size 1-byte, from 0-7 are offsets that
148
- * contain information that isn't useful to us.
149
- * Bytes 8-a are all 1-byte and refer to the size of the entire struct,
150
- * and the language field, so just start from 0xb, manufacturer size
151
- */
152
- addrptr = fru_addr + 0xb ;
153
- size = amdgpu_fru_read_eeprom (adev , addrptr , buf , sizeof (buf ));
154
- if (size < 1 ) {
155
- DRM_ERROR ("Failed to read FRU Manufacturer, ret:%d" , size );
156
- return - EINVAL ;
113
+ /* Read the IPMI Common header */
114
+ len = amdgpu_eeprom_read (adev -> pm .fru_eeprom_i2c_bus , fru_addr , buf ,
115
+ sizeof (buf ));
116
+ if (len != 8 ) {
117
+ DRM_ERROR ("Couldn't read the IPMI Common Header: %d" , len );
118
+ return len < 0 ? len : - EIO ;
157
119
}
158
120
159
- /* Increment the addrptr by the size of the field, and 1 due to the
160
- * size field being 1 byte. This pattern continues below.
161
- */
162
- addrptr += size + 1 ;
163
- size = amdgpu_fru_read_eeprom (adev , addrptr , buf , sizeof (buf ));
164
- if (size < 1 ) {
165
- DRM_ERROR ("Failed to read FRU product name, ret:%d" , size );
166
- return - EINVAL ;
121
+ if (buf [0 ] != 1 ) {
122
+ DRM_ERROR ("Bad IPMI Common Header version: 0x%02x" , buf [0 ]);
123
+ return - EIO ;
167
124
}
168
125
169
- len = size ;
170
- if (len >= AMDGPU_PRODUCT_NAME_LEN ) {
171
- DRM_WARN ("FRU Product Name is larger than %d characters. This is likely a mistake" ,
172
- AMDGPU_PRODUCT_NAME_LEN );
173
- len = AMDGPU_PRODUCT_NAME_LEN - 1 ;
174
- }
175
- memcpy (adev -> product_name , buf , len );
176
- adev -> product_name [len ] = '\0' ;
177
-
178
- addrptr += size + 1 ;
179
- size = amdgpu_fru_read_eeprom (adev , addrptr , buf , sizeof (buf ));
180
- if (size < 1 ) {
181
- DRM_ERROR ("Failed to read FRU product number, ret:%d" , size );
182
- return - EINVAL ;
126
+ for (csum = 0 ; len > 0 ; len -- )
127
+ csum += buf [len - 1 ];
128
+ if (csum ) {
129
+ DRM_ERROR ("Bad IPMI Common Header checksum: 0x%02x" , csum );
130
+ return - EIO ;
183
131
}
184
132
185
- len = size ;
186
- /* Product number should only be 16 characters. Any more,
187
- * and something could be wrong. Cap it at 16 to be safe
188
- */
189
- if (len >= sizeof (adev -> product_number )) {
190
- DRM_WARN ("FRU Product Number is larger than 16 characters. This is likely a mistake" );
191
- len = sizeof (adev -> product_number ) - 1 ;
192
- }
193
- memcpy (adev -> product_number , buf , len );
194
- adev -> product_number [len ] = '\0' ;
133
+ /* Get the offset to the Product Info Area (PIA). */
134
+ addr = buf [4 ] * 8 ;
135
+ if (!addr )
136
+ return 0 ;
195
137
196
- addrptr += size + 1 ;
197
- size = amdgpu_fru_read_eeprom ( adev , addrptr , buf , sizeof ( buf )) ;
138
+ /* Get the absolute address to the PIA. */
139
+ addr += fru_addr ;
198
140
199
- if (size < 1 ) {
200
- DRM_ERROR ("Failed to read FRU product version, ret:%d" , size );
201
- return - EINVAL ;
141
+ /* Read the header of the PIA. */
142
+ len = amdgpu_eeprom_read (adev -> pm .fru_eeprom_i2c_bus , addr , buf , 3 );
143
+ if (len != 3 ) {
144
+ DRM_ERROR ("Couldn't read the Product Info Area header: %d" , len );
145
+ return len < 0 ? len : - EIO ;
202
146
}
203
147
204
- addrptr += size + 1 ;
205
- size = amdgpu_fru_read_eeprom (adev , addrptr , buf , sizeof (buf ));
148
+ if (buf [0 ] != 1 ) {
149
+ DRM_ERROR ("Bad IPMI Product Info Area version: 0x%02x" , buf [0 ]);
150
+ return - EIO ;
151
+ }
206
152
207
- if (size < 1 ) {
208
- DRM_ERROR ("Failed to read FRU serial number, ret:%d" , size );
209
- return - EINVAL ;
153
+ size = buf [1 ] * 8 ;
154
+ pia = kzalloc (size , GFP_KERNEL );
155
+ if (!pia )
156
+ return - ENOMEM ;
157
+
158
+ /* Read the whole PIA. */
159
+ len = amdgpu_eeprom_read (adev -> pm .fru_eeprom_i2c_bus , addr , pia , size );
160
+ if (len != size ) {
161
+ kfree (pia );
162
+ DRM_ERROR ("Couldn't read the Product Info Area: %d" , len );
163
+ return len < 0 ? len : - EIO ;
210
164
}
211
165
212
- len = size ;
213
- /* Serial number should only be 16 characters. Any more,
214
- * and something could be wrong. Cap it at 16 to be safe
215
- */
216
- if (len >= sizeof (adev -> serial )) {
217
- DRM_WARN ("FRU Serial Number is larger than 16 characters. This is likely a mistake" );
218
- len = sizeof (adev -> serial ) - 1 ;
166
+ for (csum = 0 ; size > 0 ; size -- )
167
+ csum += pia [size - 1 ];
168
+ if (csum ) {
169
+ DRM_ERROR ("Bad Product Info Area checksum: 0x%02x" , csum );
170
+ return - EIO ;
219
171
}
220
- memcpy (adev -> serial , buf , len );
221
- adev -> serial [len ] = '\0' ;
222
172
173
+ /* Now extract useful information from the PIA.
174
+ *
175
+ * Skip the Manufacturer Name at [3] and go directly to
176
+ * the Product Name field.
177
+ */
178
+ addr = 3 + 1 + (pia [3 ] & 0x3F );
179
+ if (addr + 1 >= len )
180
+ goto Out ;
181
+ memcpy (adev -> product_name , pia + addr + 1 ,
182
+ min_t (size_t ,
183
+ sizeof (adev -> product_name ),
184
+ pia [addr ] & 0x3F ));
185
+ adev -> product_name [sizeof (adev -> product_name ) - 1 ] = '\0' ;
186
+
187
+ /* Go to the Product Part/Model Number field. */
188
+ addr += 1 + (pia [addr ] & 0x3F );
189
+ if (addr + 1 >= len )
190
+ goto Out ;
191
+ memcpy (adev -> product_number , pia + addr + 1 ,
192
+ min_t (size_t ,
193
+ sizeof (adev -> product_number ),
194
+ pia [addr ] & 0x3F ));
195
+ adev -> product_number [sizeof (adev -> product_number ) - 1 ] = '\0' ;
196
+
197
+ /* Go to the Product Version field. */
198
+ addr += 1 + (pia [addr ] & 0x3F );
199
+
200
+ /* Go to the Product Serial Number field. */
201
+ addr += 1 + (pia [addr ] & 0x3F );
202
+ if (addr + 1 >= len )
203
+ goto Out ;
204
+ memcpy (adev -> serial , pia + addr + 1 , min_t (size_t ,
205
+ sizeof (adev -> serial ),
206
+ pia [addr ] & 0x3F ));
207
+ adev -> serial [sizeof (adev -> serial ) - 1 ] = '\0' ;
208
+ Out :
209
+ kfree (pia );
223
210
return 0 ;
224
211
}
0 commit comments