392 lines · c
1/*2 * Copyright 2019 Advanced Micro Devices, Inc.3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice shall be included in12 * all copies or substantial portions of the Software.13 *14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR18 * 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 OR20 * OTHER DEALINGS IN THE SOFTWARE.21 *22 */23#include <linux/pci.h>24 25#include "amdgpu.h"26#include "amdgpu_i2c.h"27#include "smu_v11_0_i2c.h"28#include "atom.h"29#include "amdgpu_fru_eeprom.h"30#include "amdgpu_eeprom.h"31 32#define FRU_EEPROM_MADDR_6 0x6000033#define FRU_EEPROM_MADDR_8 0x8000034 35static bool is_fru_eeprom_supported(struct amdgpu_device *adev, u32 *fru_addr)36{37 /* Only server cards have the FRU EEPROM38 * TODO: See if we can figure this out dynamically instead of39 * having to parse VBIOS versions.40 */41 struct atom_context *atom_ctx = adev->mode_info.atom_context;42 43 /* The i2c access is blocked on VF44 * TODO: Need other way to get the info45 * Also, FRU not valid for APU devices.46 */47 if (amdgpu_sriov_vf(adev) || (adev->flags & AMD_IS_APU))48 return false;49 50 /* The default I2C EEPROM address of the FRU.51 */52 if (fru_addr)53 *fru_addr = FRU_EEPROM_MADDR_8;54 55 /* VBIOS is of the format ###-DXXXYYYY-##. For SKU identification,56 * we can use just the "DXXX" portion. If there were more models, we57 * could convert the 3 characters to a hex integer and use a switch58 * for ease/speed/readability. For now, 2 string comparisons are59 * reasonable and not too expensive60 */61 switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) {62 case IP_VERSION(11, 0, 2):63 switch (adev->asic_type) {64 case CHIP_VEGA20:65 /* D161 and D163 are the VG20 server SKUs */66 if (strnstr(atom_ctx->vbios_pn, "D161",67 sizeof(atom_ctx->vbios_pn)) ||68 strnstr(atom_ctx->vbios_pn, "D163",69 sizeof(atom_ctx->vbios_pn))) {70 if (fru_addr)71 *fru_addr = FRU_EEPROM_MADDR_6;72 return true;73 } else {74 return false;75 }76 case CHIP_ARCTURUS:77 default:78 return false;79 }80 case IP_VERSION(11, 0, 7):81 if (strnstr(atom_ctx->vbios_pn, "D603",82 sizeof(atom_ctx->vbios_pn))) {83 if (strnstr(atom_ctx->vbios_pn, "D603GLXE",84 sizeof(atom_ctx->vbios_pn))) {85 return false;86 }87 88 if (fru_addr)89 *fru_addr = FRU_EEPROM_MADDR_6;90 return true;91 92 } else {93 return false;94 }95 case IP_VERSION(13, 0, 2):96 /* All Aldebaran SKUs have an FRU */97 if (!strnstr(atom_ctx->vbios_pn, "D673",98 sizeof(atom_ctx->vbios_pn)))99 if (fru_addr)100 *fru_addr = FRU_EEPROM_MADDR_6;101 return true;102 case IP_VERSION(13, 0, 6):103 case IP_VERSION(13, 0, 14):104 if (fru_addr)105 *fru_addr = FRU_EEPROM_MADDR_8;106 return true;107 default:108 return false;109 }110}111 112int amdgpu_fru_get_product_info(struct amdgpu_device *adev)113{114 struct amdgpu_fru_info *fru_info;115 unsigned char buf[8], *pia;116 u32 addr, fru_addr;117 int size, len;118 u8 csum;119 120 if (!is_fru_eeprom_supported(adev, &fru_addr))121 return 0;122 123 if (!adev->fru_info) {124 adev->fru_info = kzalloc(sizeof(*adev->fru_info), GFP_KERNEL);125 if (!adev->fru_info)126 return -ENOMEM;127 }128 129 fru_info = adev->fru_info;130 /* For Arcturus-and-later, default value of serial_number is unique_id131 * so convert it to a 16-digit HEX string for convenience and132 * backwards-compatibility.133 */134 sprintf(fru_info->serial, "%llx", adev->unique_id);135 136 /* If algo exists, it means that the i2c_adapter's initialized */137 if (!adev->pm.fru_eeprom_i2c_bus || !adev->pm.fru_eeprom_i2c_bus->algo) {138 DRM_WARN("Cannot access FRU, EEPROM accessor not initialized");139 return -ENODEV;140 }141 142 /* Read the IPMI Common header */143 len = amdgpu_eeprom_read(adev->pm.fru_eeprom_i2c_bus, fru_addr, buf,144 sizeof(buf));145 if (len != 8) {146 DRM_ERROR("Couldn't read the IPMI Common Header: %d", len);147 return len < 0 ? len : -EIO;148 }149 150 if (buf[0] != 1) {151 DRM_ERROR("Bad IPMI Common Header version: 0x%02x", buf[0]);152 return -EIO;153 }154 155 for (csum = 0; len > 0; len--)156 csum += buf[len - 1];157 if (csum) {158 DRM_ERROR("Bad IPMI Common Header checksum: 0x%02x", csum);159 return -EIO;160 }161 162 /* Get the offset to the Product Info Area (PIA). */163 addr = buf[4] * 8;164 if (!addr)165 return 0;166 167 /* Get the absolute address to the PIA. */168 addr += fru_addr;169 170 /* Read the header of the PIA. */171 len = amdgpu_eeprom_read(adev->pm.fru_eeprom_i2c_bus, addr, buf, 3);172 if (len != 3) {173 DRM_ERROR("Couldn't read the Product Info Area header: %d", len);174 return len < 0 ? len : -EIO;175 }176 177 if (buf[0] != 1) {178 DRM_ERROR("Bad IPMI Product Info Area version: 0x%02x", buf[0]);179 return -EIO;180 }181 182 size = buf[1] * 8;183 pia = kzalloc(size, GFP_KERNEL);184 if (!pia)185 return -ENOMEM;186 187 /* Read the whole PIA. */188 len = amdgpu_eeprom_read(adev->pm.fru_eeprom_i2c_bus, addr, pia, size);189 if (len != size) {190 kfree(pia);191 DRM_ERROR("Couldn't read the Product Info Area: %d", len);192 return len < 0 ? len : -EIO;193 }194 195 for (csum = 0; size > 0; size--)196 csum += pia[size - 1];197 if (csum) {198 DRM_ERROR("Bad Product Info Area checksum: 0x%02x", csum);199 kfree(pia);200 return -EIO;201 }202 203 /* Now extract useful information from the PIA.204 *205 * Read Manufacturer Name field whose length is [3].206 */207 addr = 3;208 if (addr + 1 >= len)209 goto Out;210 memcpy(fru_info->manufacturer_name, pia + addr + 1,211 min_t(size_t, sizeof(fru_info->manufacturer_name),212 pia[addr] & 0x3F));213 fru_info->manufacturer_name[sizeof(fru_info->manufacturer_name) - 1] =214 '\0';215 216 /* Read Product Name field. */217 addr += 1 + (pia[addr] & 0x3F);218 if (addr + 1 >= len)219 goto Out;220 memcpy(fru_info->product_name, pia + addr + 1,221 min_t(size_t, sizeof(fru_info->product_name), pia[addr] & 0x3F));222 fru_info->product_name[sizeof(fru_info->product_name) - 1] = '\0';223 224 /* Go to the Product Part/Model Number field. */225 addr += 1 + (pia[addr] & 0x3F);226 if (addr + 1 >= len)227 goto Out;228 memcpy(fru_info->product_number, pia + addr + 1,229 min_t(size_t, sizeof(fru_info->product_number),230 pia[addr] & 0x3F));231 fru_info->product_number[sizeof(fru_info->product_number) - 1] = '\0';232 233 /* Go to the Product Version field. */234 addr += 1 + (pia[addr] & 0x3F);235 236 /* Go to the Product Serial Number field. */237 addr += 1 + (pia[addr] & 0x3F);238 if (addr + 1 >= len)239 goto Out;240 memcpy(fru_info->serial, pia + addr + 1,241 min_t(size_t, sizeof(fru_info->serial), pia[addr] & 0x3F));242 fru_info->serial[sizeof(fru_info->serial) - 1] = '\0';243 244 /* Asset Tag field */245 addr += 1 + (pia[addr] & 0x3F);246 247 /* FRU File Id field. This could be 'null'. */248 addr += 1 + (pia[addr] & 0x3F);249 if ((addr + 1 >= len) || !(pia[addr] & 0x3F))250 goto Out;251 memcpy(fru_info->fru_id, pia + addr + 1,252 min_t(size_t, sizeof(fru_info->fru_id), pia[addr] & 0x3F));253 fru_info->fru_id[sizeof(fru_info->fru_id) - 1] = '\0';254 255Out:256 kfree(pia);257 return 0;258}259 260/**261 * DOC: product_name262 *263 * The amdgpu driver provides a sysfs API for reporting the product name264 * for the device265 * The file product_name is used for this and returns the product name266 * as returned from the FRU.267 * NOTE: This is only available for certain server cards268 */269 270static ssize_t amdgpu_fru_product_name_show(struct device *dev,271 struct device_attribute *attr,272 char *buf)273{274 struct drm_device *ddev = dev_get_drvdata(dev);275 struct amdgpu_device *adev = drm_to_adev(ddev);276 277 return sysfs_emit(buf, "%s\n", adev->fru_info->product_name);278}279 280static DEVICE_ATTR(product_name, 0444, amdgpu_fru_product_name_show, NULL);281 282/**283 * DOC: product_number284 *285 * The amdgpu driver provides a sysfs API for reporting the part number286 * for the device287 * The file product_number is used for this and returns the part number288 * as returned from the FRU.289 * NOTE: This is only available for certain server cards290 */291 292static ssize_t amdgpu_fru_product_number_show(struct device *dev,293 struct device_attribute *attr,294 char *buf)295{296 struct drm_device *ddev = dev_get_drvdata(dev);297 struct amdgpu_device *adev = drm_to_adev(ddev);298 299 return sysfs_emit(buf, "%s\n", adev->fru_info->product_number);300}301 302static DEVICE_ATTR(product_number, 0444, amdgpu_fru_product_number_show, NULL);303 304/**305 * DOC: serial_number306 *307 * The amdgpu driver provides a sysfs API for reporting the serial number308 * for the device309 * The file serial_number is used for this and returns the serial number310 * as returned from the FRU.311 * NOTE: This is only available for certain server cards312 */313 314static ssize_t amdgpu_fru_serial_number_show(struct device *dev,315 struct device_attribute *attr,316 char *buf)317{318 struct drm_device *ddev = dev_get_drvdata(dev);319 struct amdgpu_device *adev = drm_to_adev(ddev);320 321 return sysfs_emit(buf, "%s\n", adev->fru_info->serial);322}323 324static DEVICE_ATTR(serial_number, 0444, amdgpu_fru_serial_number_show, NULL);325 326/**327 * DOC: fru_id328 *329 * The amdgpu driver provides a sysfs API for reporting FRU File Id330 * for the device.331 * The file fru_id is used for this and returns the File Id value332 * as returned from the FRU.333 * NOTE: This is only available for certain server cards334 */335 336static ssize_t amdgpu_fru_id_show(struct device *dev,337 struct device_attribute *attr, char *buf)338{339 struct drm_device *ddev = dev_get_drvdata(dev);340 struct amdgpu_device *adev = drm_to_adev(ddev);341 342 return sysfs_emit(buf, "%s\n", adev->fru_info->fru_id);343}344 345static DEVICE_ATTR(fru_id, 0444, amdgpu_fru_id_show, NULL);346 347/**348 * DOC: manufacturer349 *350 * The amdgpu driver provides a sysfs API for reporting manufacturer name from351 * FRU information.352 * The file manufacturer returns the value as returned from the FRU.353 * NOTE: This is only available for certain server cards354 */355 356static ssize_t amdgpu_fru_manufacturer_name_show(struct device *dev,357 struct device_attribute *attr,358 char *buf)359{360 struct drm_device *ddev = dev_get_drvdata(dev);361 struct amdgpu_device *adev = drm_to_adev(ddev);362 363 return sysfs_emit(buf, "%s\n", adev->fru_info->manufacturer_name);364}365 366static DEVICE_ATTR(manufacturer, 0444, amdgpu_fru_manufacturer_name_show, NULL);367 368static const struct attribute *amdgpu_fru_attributes[] = {369 &dev_attr_product_name.attr,370 &dev_attr_product_number.attr,371 &dev_attr_serial_number.attr,372 &dev_attr_fru_id.attr,373 &dev_attr_manufacturer.attr,374 NULL375};376 377int amdgpu_fru_sysfs_init(struct amdgpu_device *adev)378{379 if (!is_fru_eeprom_supported(adev, NULL) || !adev->fru_info)380 return 0;381 382 return sysfs_create_files(&adev->dev->kobj, amdgpu_fru_attributes);383}384 385void amdgpu_fru_sysfs_fini(struct amdgpu_device *adev)386{387 if (!is_fru_eeprom_supported(adev, NULL) || !adev->fru_info)388 return;389 390 sysfs_remove_files(&adev->dev->kobj, amdgpu_fru_attributes);391}392