LCOV - code coverage report
Current view: top level - drivers/gpu/drm/amd/amdgpu - amdgpu_fru_eeprom.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 67 0.0 %
Date: 2022-12-09 01:23:36 Functions: 0 3 0.0 %

          Line data    Source code
       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 <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        0x60000
      33             : 
      34           0 : static bool is_fru_eeprom_supported(struct amdgpu_device *adev)
      35             : {
      36             :         /* Only server cards have the FRU EEPROM
      37             :          * TODO: See if we can figure this out dynamically instead of
      38             :          * having to parse VBIOS versions.
      39             :          */
      40           0 :         struct atom_context *atom_ctx = adev->mode_info.atom_context;
      41             : 
      42             :         /* The i2c access is blocked on VF
      43             :          * TODO: Need other way to get the info
      44             :          */
      45           0 :         if (amdgpu_sriov_vf(adev))
      46             :                 return false;
      47             : 
      48             :         /* VBIOS is of the format ###-DXXXYYYY-##. For SKU identification,
      49             :          * we can use just the "DXXX" portion. If there were more models, we
      50             :          * could convert the 3 characters to a hex integer and use a switch
      51             :          * for ease/speed/readability. For now, 2 string comparisons are
      52             :          * reasonable and not too expensive
      53             :          */
      54           0 :         switch (adev->asic_type) {
      55             :         case CHIP_VEGA20:
      56             :                 /* D161 and D163 are the VG20 server SKUs */
      57           0 :                 if (strnstr(atom_ctx->vbios_version, "D161",
      58           0 :                             sizeof(atom_ctx->vbios_version)) ||
      59           0 :                     strnstr(atom_ctx->vbios_version, "D163",
      60             :                             sizeof(atom_ctx->vbios_version)))
      61             :                         return true;
      62             :                 else
      63             :                         return false;
      64             :         case CHIP_ALDEBARAN:
      65             :                 /* All Aldebaran SKUs have the FRU */
      66             :                 return true;
      67             :         case CHIP_SIENNA_CICHLID:
      68           0 :                 if (strnstr(atom_ctx->vbios_version, "D603",
      69             :                     sizeof(atom_ctx->vbios_version))) {
      70           0 :                         if (strnstr(atom_ctx->vbios_version, "D603GLXE",
      71             :                             sizeof(atom_ctx->vbios_version)))
      72             :                                 return false;
      73             :                         else
      74             :                                 return true;
      75             :                 } else {
      76             :                         return false;
      77             :                 }
      78             :         default:
      79             :                 return false;
      80             :         }
      81             : }
      82             : 
      83           0 : static int amdgpu_fru_read_eeprom(struct amdgpu_device *adev, uint32_t addrptr,
      84             :                                   unsigned char *buf, size_t buf_size)
      85             : {
      86             :         int ret;
      87             :         u8 size;
      88             : 
      89           0 :         ret = amdgpu_eeprom_read(adev->pm.fru_eeprom_i2c_bus, addrptr, buf, 1);
      90           0 :         if (ret < 1) {
      91           0 :                 DRM_WARN("FRU: Failed to get size field");
      92             :                 return ret;
      93             :         }
      94             : 
      95             :         /* The size returned by the i2c requires subtraction of 0xC0 since the
      96             :          * size apparently always reports as 0xC0+actual size.
      97             :          */
      98           0 :         size = buf[0] & 0x3F;
      99           0 :         size = min_t(size_t, size, buf_size);
     100             : 
     101           0 :         ret = amdgpu_eeprom_read(adev->pm.fru_eeprom_i2c_bus, addrptr + 1,
     102             :                                  buf, size);
     103           0 :         if (ret < 1) {
     104           0 :                 DRM_WARN("FRU: Failed to get data field");
     105             :                 return ret;
     106             :         }
     107             : 
     108             :         return size;
     109             : }
     110             : 
     111           0 : int amdgpu_fru_get_product_info(struct amdgpu_device *adev)
     112             : {
     113             :         unsigned char buf[AMDGPU_PRODUCT_NAME_LEN];
     114             :         u32 addrptr;
     115             :         int size, len;
     116             : 
     117           0 :         if (!is_fru_eeprom_supported(adev))
     118             :                 return 0;
     119             : 
     120             :         /* If algo exists, it means that the i2c_adapter's initialized */
     121           0 :         if (!adev->pm.fru_eeprom_i2c_bus || !adev->pm.fru_eeprom_i2c_bus->algo) {
     122           0 :                 DRM_WARN("Cannot access FRU, EEPROM accessor not initialized");
     123           0 :                 return -ENODEV;
     124             :         }
     125             : 
     126             :         /* There's a lot of repetition here. This is due to the FRU having
     127             :          * variable-length fields. To get the information, we have to find the
     128             :          * size of each field, and then keep reading along and reading along
     129             :          * until we get all of the data that we want. We use addrptr to track
     130             :          * the address as we go
     131             :          */
     132             : 
     133             :         /* The first fields are all of size 1-byte, from 0-7 are offsets that
     134             :          * contain information that isn't useful to us.
     135             :          * Bytes 8-a are all 1-byte and refer to the size of the entire struct,
     136             :          * and the language field, so just start from 0xb, manufacturer size
     137             :          */
     138           0 :         addrptr = FRU_EEPROM_MADDR + 0xb;
     139           0 :         size = amdgpu_fru_read_eeprom(adev, addrptr, buf, sizeof(buf));
     140           0 :         if (size < 1) {
     141           0 :                 DRM_ERROR("Failed to read FRU Manufacturer, ret:%d", size);
     142           0 :                 return -EINVAL;
     143             :         }
     144             : 
     145             :         /* Increment the addrptr by the size of the field, and 1 due to the
     146             :          * size field being 1 byte. This pattern continues below.
     147             :          */
     148           0 :         addrptr += size + 1;
     149           0 :         size = amdgpu_fru_read_eeprom(adev, addrptr, buf, sizeof(buf));
     150           0 :         if (size < 1) {
     151           0 :                 DRM_ERROR("Failed to read FRU product name, ret:%d", size);
     152           0 :                 return -EINVAL;
     153             :         }
     154             : 
     155           0 :         len = size;
     156           0 :         if (len >= AMDGPU_PRODUCT_NAME_LEN) {
     157           0 :                 DRM_WARN("FRU Product Name is larger than %d characters. This is likely a mistake",
     158             :                                 AMDGPU_PRODUCT_NAME_LEN);
     159           0 :                 len = AMDGPU_PRODUCT_NAME_LEN - 1;
     160             :         }
     161           0 :         memcpy(adev->product_name, buf, len);
     162           0 :         adev->product_name[len] = '\0';
     163             : 
     164           0 :         addrptr += size + 1;
     165           0 :         size = amdgpu_fru_read_eeprom(adev, addrptr, buf, sizeof(buf));
     166           0 :         if (size < 1) {
     167           0 :                 DRM_ERROR("Failed to read FRU product number, ret:%d", size);
     168           0 :                 return -EINVAL;
     169             :         }
     170             : 
     171           0 :         len = size;
     172             :         /* Product number should only be 16 characters. Any more,
     173             :          * and something could be wrong. Cap it at 16 to be safe
     174             :          */
     175           0 :         if (len >= sizeof(adev->product_number)) {
     176           0 :                 DRM_WARN("FRU Product Number is larger than 16 characters. This is likely a mistake");
     177           0 :                 len = sizeof(adev->product_number) - 1;
     178             :         }
     179           0 :         memcpy(adev->product_number, buf, len);
     180           0 :         adev->product_number[len] = '\0';
     181             : 
     182           0 :         addrptr += size + 1;
     183           0 :         size = amdgpu_fru_read_eeprom(adev, addrptr, buf, sizeof(buf));
     184             : 
     185           0 :         if (size < 1) {
     186           0 :                 DRM_ERROR("Failed to read FRU product version, ret:%d", size);
     187           0 :                 return -EINVAL;
     188             :         }
     189             : 
     190           0 :         addrptr += size + 1;
     191           0 :         size = amdgpu_fru_read_eeprom(adev, addrptr, buf, sizeof(buf));
     192             : 
     193           0 :         if (size < 1) {
     194           0 :                 DRM_ERROR("Failed to read FRU serial number, ret:%d", size);
     195           0 :                 return -EINVAL;
     196             :         }
     197             : 
     198           0 :         len = size;
     199             :         /* Serial number should only be 16 characters. Any more,
     200             :          * and something could be wrong. Cap it at 16 to be safe
     201             :          */
     202           0 :         if (len >= sizeof(adev->serial)) {
     203           0 :                 DRM_WARN("FRU Serial Number is larger than 16 characters. This is likely a mistake");
     204           0 :                 len = sizeof(adev->serial) - 1;
     205             :         }
     206           0 :         memcpy(adev->serial, buf, len);
     207           0 :         adev->serial[len] = '\0';
     208             : 
     209           0 :         return 0;
     210             : }

Generated by: LCOV version 1.14