164f55e62SAndrey Grodzovsky /* 264f55e62SAndrey Grodzovsky * Copyright 2019 Advanced Micro Devices, Inc. 364f55e62SAndrey Grodzovsky * 464f55e62SAndrey Grodzovsky * Permission is hereby granted, free of charge, to any person obtaining a 564f55e62SAndrey Grodzovsky * copy of this software and associated documentation files (the "Software"), 664f55e62SAndrey Grodzovsky * to deal in the Software without restriction, including without limitation 764f55e62SAndrey Grodzovsky * the rights to use, copy, modify, merge, publish, distribute, sublicense, 864f55e62SAndrey Grodzovsky * and/or sell copies of the Software, and to permit persons to whom the 964f55e62SAndrey Grodzovsky * Software is furnished to do so, subject to the following conditions: 1064f55e62SAndrey Grodzovsky * 1164f55e62SAndrey Grodzovsky * The above copyright notice and this permission notice shall be included in 1264f55e62SAndrey Grodzovsky * all copies or substantial portions of the Software. 1364f55e62SAndrey Grodzovsky * 1464f55e62SAndrey Grodzovsky * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1564f55e62SAndrey Grodzovsky * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1664f55e62SAndrey Grodzovsky * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1764f55e62SAndrey Grodzovsky * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 1864f55e62SAndrey Grodzovsky * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 1964f55e62SAndrey Grodzovsky * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2064f55e62SAndrey Grodzovsky * OTHER DEALINGS IN THE SOFTWARE. 2164f55e62SAndrey Grodzovsky * 2264f55e62SAndrey Grodzovsky */ 2364f55e62SAndrey Grodzovsky 2464f55e62SAndrey Grodzovsky #include "amdgpu_ras_eeprom.h" 2564f55e62SAndrey Grodzovsky #include "amdgpu.h" 2664f55e62SAndrey Grodzovsky #include "amdgpu_ras.h" 2764f55e62SAndrey Grodzovsky #include <linux/bits.h> 28ef1caf48SJohn Clements #include "atom.h" 2924f55c05SAlex Deucher #include "amdgpu_eeprom.h" 3064f55e62SAndrey Grodzovsky 31ccdfbfecSLuben Tuikov #define EEPROM_I2C_MADDR_VEGA20 0x0 32ccdfbfecSLuben Tuikov #define EEPROM_I2C_MADDR_ARCTURUS 0x40000 33ccdfbfecSLuben Tuikov #define EEPROM_I2C_MADDR_ARCTURUS_D342 0x0 34ccdfbfecSLuben Tuikov #define EEPROM_I2C_MADDR_SIENNA_CICHLID 0x0 35ccdfbfecSLuben Tuikov #define EEPROM_I2C_MADDR_ALDEBARAN 0x0 3664f55e62SAndrey Grodzovsky 3764f55e62SAndrey Grodzovsky /* 3864f55e62SAndrey Grodzovsky * The 2 macros bellow represent the actual size in bytes that 3964f55e62SAndrey Grodzovsky * those entities occupy in the EEPROM memory. 40c28aa44dSLuben Tuikov * RAS_TABLE_RECORD_SIZE is different than sizeof(eeprom_table_record) which 4164f55e62SAndrey Grodzovsky * uses uint64 to store 6b fields such as retired_page. 4264f55e62SAndrey Grodzovsky */ 43c28aa44dSLuben Tuikov #define RAS_TABLE_HEADER_SIZE 20 44c28aa44dSLuben Tuikov #define RAS_TABLE_RECORD_SIZE 24 4564f55e62SAndrey Grodzovsky 4664f55e62SAndrey Grodzovsky /* Table hdr is 'AMDR' */ 47c28aa44dSLuben Tuikov #define RAS_TABLE_HDR_VAL 0x414d4452 48c28aa44dSLuben Tuikov #define RAS_TABLE_VER 0x00010000 4964f55e62SAndrey Grodzovsky 501d6a9d12SGuchun Chen /* Bad GPU tag ‘BADG’ */ 51c28aa44dSLuben Tuikov #define RAS_TABLE_HDR_BAD 0x42414447 521d6a9d12SGuchun Chen 53c28aa44dSLuben Tuikov /* Assume 2-Mbit size EEPROM and take up the whole space. */ 54c28aa44dSLuben Tuikov #define RAS_TBL_SIZE_BYTES (256 * 1024) 55*63d4c081SLuben Tuikov #define RAS_TABLE_START 0 56*63d4c081SLuben Tuikov #define RAS_HDR_START RAS_TABLE_START 57c28aa44dSLuben Tuikov #define RAS_RECORD_START (RAS_HDR_START + RAS_TABLE_HEADER_SIZE) 58e4e6a589SLuben Tuikov #define RAS_MAX_RECORD_COUNT ((RAS_TBL_SIZE_BYTES - RAS_TABLE_HEADER_SIZE) \ 59c28aa44dSLuben Tuikov / RAS_TABLE_RECORD_SIZE) 6064f55e62SAndrey Grodzovsky 61*63d4c081SLuben Tuikov /* Given a zero-based index of an EEPROM RAS record, yields the EEPROM 62*63d4c081SLuben Tuikov * offset off of RAS_TABLE_START. That is, this is something you can 63*63d4c081SLuben Tuikov * add to control->i2c_address, and then tell I2C layer to read 64*63d4c081SLuben Tuikov * from/write to there. _N is the so called absolute index, 65*63d4c081SLuben Tuikov * because it starts right after the table header. 66*63d4c081SLuben Tuikov */ 67*63d4c081SLuben Tuikov #define RAS_INDEX_TO_OFFSET(_C, _N) ((_C)->ras_record_offset + \ 68*63d4c081SLuben Tuikov (_N) * RAS_TABLE_RECORD_SIZE) 69*63d4c081SLuben Tuikov 70*63d4c081SLuben Tuikov #define RAS_OFFSET_TO_INDEX(_C, _O) (((_O) - \ 71*63d4c081SLuben Tuikov (_C)->ras_record_offset) / RAS_TABLE_RECORD_SIZE) 72*63d4c081SLuben Tuikov 73*63d4c081SLuben Tuikov #define RAS_NUM_RECS(_tbl_hdr) (((_tbl_hdr)->tbl_size - \ 74*63d4c081SLuben Tuikov RAS_TABLE_HEADER_SIZE) / RAS_TABLE_RECORD_SIZE) 75*63d4c081SLuben Tuikov 7664f55e62SAndrey Grodzovsky #define to_amdgpu_device(x) (container_of(x, struct amdgpu_ras, eeprom_control))->adev 7764f55e62SAndrey Grodzovsky 784bfb7428SJohn Clements static bool __is_ras_eeprom_supported(struct amdgpu_device *adev) 794bfb7428SJohn Clements { 804bfb7428SJohn Clements if ((adev->asic_type == CHIP_VEGA20) || 813e7bc83eSJohn Clements (adev->asic_type == CHIP_ARCTURUS) || 8252a9df81SJohn Clements (adev->asic_type == CHIP_SIENNA_CICHLID) || 8352a9df81SJohn Clements (adev->asic_type == CHIP_ALDEBARAN)) 844bfb7428SJohn Clements return true; 854bfb7428SJohn Clements 864bfb7428SJohn Clements return false; 874bfb7428SJohn Clements } 884bfb7428SJohn Clements 89ef1caf48SJohn Clements static bool __get_eeprom_i2c_addr_arct(struct amdgpu_device *adev, 90ccdfbfecSLuben Tuikov struct amdgpu_ras_eeprom_control *control) 91ef1caf48SJohn Clements { 92ef1caf48SJohn Clements struct atom_context *atom_ctx = adev->mode_info.atom_context; 93ef1caf48SJohn Clements 94ccdfbfecSLuben Tuikov if (!control || !atom_ctx) 95ef1caf48SJohn Clements return false; 96ef1caf48SJohn Clements 97ef1caf48SJohn Clements if (strnstr(atom_ctx->vbios_version, 98ef1caf48SJohn Clements "D342", 99ef1caf48SJohn Clements sizeof(atom_ctx->vbios_version))) 100ccdfbfecSLuben Tuikov control->i2c_address = EEPROM_I2C_MADDR_ARCTURUS_D342; 101ef1caf48SJohn Clements else 102ccdfbfecSLuben Tuikov control->i2c_address = EEPROM_I2C_MADDR_ARCTURUS; 103ef1caf48SJohn Clements 104ef1caf48SJohn Clements return true; 105ef1caf48SJohn Clements } 106ef1caf48SJohn Clements 107ef1caf48SJohn Clements static bool __get_eeprom_i2c_addr(struct amdgpu_device *adev, 108ccdfbfecSLuben Tuikov struct amdgpu_ras_eeprom_control *control) 109ef1caf48SJohn Clements { 110ccdfbfecSLuben Tuikov if (!control) 111ef1caf48SJohn Clements return false; 112ef1caf48SJohn Clements 113ef1caf48SJohn Clements switch (adev->asic_type) { 114ef1caf48SJohn Clements case CHIP_VEGA20: 115ccdfbfecSLuben Tuikov control->i2c_address = EEPROM_I2C_MADDR_VEGA20; 116ef1caf48SJohn Clements break; 117ef1caf48SJohn Clements 118ef1caf48SJohn Clements case CHIP_ARCTURUS: 119ccdfbfecSLuben Tuikov return __get_eeprom_i2c_addr_arct(adev, control); 120ef1caf48SJohn Clements 1213e7bc83eSJohn Clements case CHIP_SIENNA_CICHLID: 122ccdfbfecSLuben Tuikov control->i2c_address = EEPROM_I2C_MADDR_SIENNA_CICHLID; 1233e7bc83eSJohn Clements break; 1243e7bc83eSJohn Clements 12552a9df81SJohn Clements case CHIP_ALDEBARAN: 126ccdfbfecSLuben Tuikov control->i2c_address = EEPROM_I2C_MADDR_ALDEBARAN; 12752a9df81SJohn Clements break; 12852a9df81SJohn Clements 129ef1caf48SJohn Clements default: 130ef1caf48SJohn Clements return false; 131ef1caf48SJohn Clements } 132ef1caf48SJohn Clements 133ef1caf48SJohn Clements return true; 134ef1caf48SJohn Clements } 135ef1caf48SJohn Clements 136*63d4c081SLuben Tuikov static void 137*63d4c081SLuben Tuikov __encode_table_header_to_buf(struct amdgpu_ras_eeprom_table_header *hdr, 138d7edde3dSLuben Tuikov unsigned char *buf) 13964f55e62SAndrey Grodzovsky { 140*63d4c081SLuben Tuikov u32 *pp = (uint32_t *)buf; 14164f55e62SAndrey Grodzovsky 14264f55e62SAndrey Grodzovsky pp[0] = cpu_to_le32(hdr->header); 14364f55e62SAndrey Grodzovsky pp[1] = cpu_to_le32(hdr->version); 14464f55e62SAndrey Grodzovsky pp[2] = cpu_to_le32(hdr->first_rec_offset); 14564f55e62SAndrey Grodzovsky pp[3] = cpu_to_le32(hdr->tbl_size); 14664f55e62SAndrey Grodzovsky pp[4] = cpu_to_le32(hdr->checksum); 14764f55e62SAndrey Grodzovsky } 14864f55e62SAndrey Grodzovsky 149*63d4c081SLuben Tuikov static void 150*63d4c081SLuben Tuikov __decode_table_header_from_buf(struct amdgpu_ras_eeprom_table_header *hdr, 151d7edde3dSLuben Tuikov unsigned char *buf) 15264f55e62SAndrey Grodzovsky { 153d7edde3dSLuben Tuikov u32 *pp = (uint32_t *)buf; 15464f55e62SAndrey Grodzovsky 15564f55e62SAndrey Grodzovsky hdr->header = le32_to_cpu(pp[0]); 15664f55e62SAndrey Grodzovsky hdr->version = le32_to_cpu(pp[1]); 15764f55e62SAndrey Grodzovsky hdr->first_rec_offset = le32_to_cpu(pp[2]); 15864f55e62SAndrey Grodzovsky hdr->tbl_size = le32_to_cpu(pp[3]); 15964f55e62SAndrey Grodzovsky hdr->checksum = le32_to_cpu(pp[4]); 16064f55e62SAndrey Grodzovsky } 16164f55e62SAndrey Grodzovsky 162*63d4c081SLuben Tuikov static int __write_table_header(struct amdgpu_ras_eeprom_control *control) 16364f55e62SAndrey Grodzovsky { 164*63d4c081SLuben Tuikov u8 buf[RAS_TABLE_HEADER_SIZE]; 1659015d60cSAndrey Grodzovsky struct amdgpu_device *adev = to_amdgpu_device(control); 166*63d4c081SLuben Tuikov int res; 16764f55e62SAndrey Grodzovsky 168*63d4c081SLuben Tuikov memset(buf, 0, sizeof(buf)); 169d7edde3dSLuben Tuikov __encode_table_header_to_buf(&control->tbl_hdr, buf); 1705985ebbeSJohn Clements 17140e7ed97SDennis Li /* i2c may be unstable in gpu reset */ 17240e7ed97SDennis Li down_read(&adev->reset_sem); 173*63d4c081SLuben Tuikov res = amdgpu_eeprom_write(&adev->pm.smu_i2c, 174*63d4c081SLuben Tuikov control->i2c_address + 175*63d4c081SLuben Tuikov control->ras_header_offset, 176d7edde3dSLuben Tuikov buf, RAS_TABLE_HEADER_SIZE); 17740e7ed97SDennis Li up_read(&adev->reset_sem); 17840e7ed97SDennis Li 179*63d4c081SLuben Tuikov if (res < 0) { 180*63d4c081SLuben Tuikov DRM_ERROR("Failed to write EEPROM table header:%d", res); 181*63d4c081SLuben Tuikov } else if (res < RAS_TABLE_HEADER_SIZE) { 182*63d4c081SLuben Tuikov DRM_ERROR("Short write:%d out of %d\n", 183*63d4c081SLuben Tuikov res, RAS_TABLE_HEADER_SIZE); 184*63d4c081SLuben Tuikov res = -EIO; 185*63d4c081SLuben Tuikov } else { 186*63d4c081SLuben Tuikov res = 0; 187*63d4c081SLuben Tuikov } 18864f55e62SAndrey Grodzovsky 189*63d4c081SLuben Tuikov return res; 19064f55e62SAndrey Grodzovsky } 19164f55e62SAndrey Grodzovsky 192803c6ebdSLuben Tuikov static u8 __calc_hdr_byte_sum(const struct amdgpu_ras_eeprom_control *control) 193db338e16SAndrey Grodzovsky { 194*63d4c081SLuben Tuikov int ii; 195*63d4c081SLuben Tuikov u8 *pp, csum; 196803c6ebdSLuben Tuikov size_t sz; 197db338e16SAndrey Grodzovsky 198db338e16SAndrey Grodzovsky /* Header checksum, skip checksum field in the calculation */ 199803c6ebdSLuben Tuikov sz = sizeof(control->tbl_hdr) - sizeof(control->tbl_hdr.checksum); 200*63d4c081SLuben Tuikov pp = (u8 *) &control->tbl_hdr; 201*63d4c081SLuben Tuikov csum = 0; 202*63d4c081SLuben Tuikov for (ii = 0; ii < sz; ii++, pp++) 203*63d4c081SLuben Tuikov csum += *pp; 204db338e16SAndrey Grodzovsky 205*63d4c081SLuben Tuikov return csum; 206db338e16SAndrey Grodzovsky } 20764f55e62SAndrey Grodzovsky 2089b856defSGuchun Chen static int amdgpu_ras_eeprom_correct_header_tag( 2099b856defSGuchun Chen struct amdgpu_ras_eeprom_control *control, 2109b856defSGuchun Chen uint32_t header) 2119b856defSGuchun Chen { 2129b856defSGuchun Chen struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr; 213*63d4c081SLuben Tuikov u8 *hh; 214*63d4c081SLuben Tuikov int res; 215*63d4c081SLuben Tuikov u8 csum; 2169b856defSGuchun Chen 217*63d4c081SLuben Tuikov csum = -hdr->checksum; 2189b856defSGuchun Chen 219*63d4c081SLuben Tuikov hh = (void *) &hdr->header; 220*63d4c081SLuben Tuikov csum -= (hh[0] + hh[1] + hh[2] + hh[3]); 221*63d4c081SLuben Tuikov hh = (void *) &header; 222*63d4c081SLuben Tuikov csum += hh[0] + hh[1] + hh[2] + hh[3]; 223*63d4c081SLuben Tuikov csum = -csum; 2240686627bSLuben Tuikov mutex_lock(&control->ras_tbl_mutex); 2259b856defSGuchun Chen hdr->header = header; 226*63d4c081SLuben Tuikov hdr->checksum = csum; 227*63d4c081SLuben Tuikov res = __write_table_header(control); 2280686627bSLuben Tuikov mutex_unlock(&control->ras_tbl_mutex); 2299b856defSGuchun Chen 230*63d4c081SLuben Tuikov return res; 2319b856defSGuchun Chen } 2329b856defSGuchun Chen 233*63d4c081SLuben Tuikov /** 234*63d4c081SLuben Tuikov * amdgpu_ras_eeprom_reset_table -- Reset the RAS EEPROM table 235*63d4c081SLuben Tuikov * @control: pointer to control structure 236*63d4c081SLuben Tuikov * 237*63d4c081SLuben Tuikov * Reset the contents of the header of the RAS EEPROM table. 238*63d4c081SLuben Tuikov * Return 0 on success, -errno on error. 239*63d4c081SLuben Tuikov */ 240d01b400bSAndrey Grodzovsky int amdgpu_ras_eeprom_reset_table(struct amdgpu_ras_eeprom_control *control) 241d01b400bSAndrey Grodzovsky { 242d01b400bSAndrey Grodzovsky struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr; 243*63d4c081SLuben Tuikov u8 csum; 244*63d4c081SLuben Tuikov int res; 245db338e16SAndrey Grodzovsky 2460686627bSLuben Tuikov mutex_lock(&control->ras_tbl_mutex); 247d01b400bSAndrey Grodzovsky 248c28aa44dSLuben Tuikov hdr->header = RAS_TABLE_HDR_VAL; 249c28aa44dSLuben Tuikov hdr->version = RAS_TABLE_VER; 250c28aa44dSLuben Tuikov hdr->first_rec_offset = RAS_RECORD_START; 251c28aa44dSLuben Tuikov hdr->tbl_size = RAS_TABLE_HEADER_SIZE; 252d01b400bSAndrey Grodzovsky 253*63d4c081SLuben Tuikov csum = __calc_hdr_byte_sum(control); 254*63d4c081SLuben Tuikov csum = -csum; 255*63d4c081SLuben Tuikov hdr->checksum = csum; 256*63d4c081SLuben Tuikov res = __write_table_header(control); 257*63d4c081SLuben Tuikov 258*63d4c081SLuben Tuikov control->ras_num_recs = 0; 259*63d4c081SLuben Tuikov control->ras_fri = 0; 260db338e16SAndrey Grodzovsky 2610686627bSLuben Tuikov mutex_unlock(&control->ras_tbl_mutex); 262db338e16SAndrey Grodzovsky 263*63d4c081SLuben Tuikov return res; 264d01b400bSAndrey Grodzovsky } 265d01b400bSAndrey Grodzovsky 266*63d4c081SLuben Tuikov static void 267*63d4c081SLuben Tuikov __encode_table_record_to_buf(struct amdgpu_ras_eeprom_control *control, 26864f55e62SAndrey Grodzovsky struct eeprom_table_record *record, 269d7edde3dSLuben Tuikov unsigned char *buf) 27064f55e62SAndrey Grodzovsky { 27164f55e62SAndrey Grodzovsky __le64 tmp = 0; 27264f55e62SAndrey Grodzovsky int i = 0; 27364f55e62SAndrey Grodzovsky 27464f55e62SAndrey Grodzovsky /* Next are all record fields according to EEPROM page spec in LE foramt */ 275d7edde3dSLuben Tuikov buf[i++] = record->err_type; 27664f55e62SAndrey Grodzovsky 277d7edde3dSLuben Tuikov buf[i++] = record->bank; 27864f55e62SAndrey Grodzovsky 27964f55e62SAndrey Grodzovsky tmp = cpu_to_le64(record->ts); 280d7edde3dSLuben Tuikov memcpy(buf + i, &tmp, 8); 28164f55e62SAndrey Grodzovsky i += 8; 28264f55e62SAndrey Grodzovsky 28364f55e62SAndrey Grodzovsky tmp = cpu_to_le64((record->offset & 0xffffffffffff)); 284d7edde3dSLuben Tuikov memcpy(buf + i, &tmp, 6); 28564f55e62SAndrey Grodzovsky i += 6; 28664f55e62SAndrey Grodzovsky 287d7edde3dSLuben Tuikov buf[i++] = record->mem_channel; 288d7edde3dSLuben Tuikov buf[i++] = record->mcumc_id; 28964f55e62SAndrey Grodzovsky 29064f55e62SAndrey Grodzovsky tmp = cpu_to_le64((record->retired_page & 0xffffffffffff)); 291d7edde3dSLuben Tuikov memcpy(buf + i, &tmp, 6); 29264f55e62SAndrey Grodzovsky } 29364f55e62SAndrey Grodzovsky 294*63d4c081SLuben Tuikov static void 295*63d4c081SLuben Tuikov __decode_table_record_from_buf(struct amdgpu_ras_eeprom_control *control, 29664f55e62SAndrey Grodzovsky struct eeprom_table_record *record, 297d7edde3dSLuben Tuikov unsigned char *buf) 29864f55e62SAndrey Grodzovsky { 29964f55e62SAndrey Grodzovsky __le64 tmp = 0; 30064f55e62SAndrey Grodzovsky int i = 0; 30164f55e62SAndrey Grodzovsky 30264f55e62SAndrey Grodzovsky /* Next are all record fields according to EEPROM page spec in LE foramt */ 303d7edde3dSLuben Tuikov record->err_type = buf[i++]; 30464f55e62SAndrey Grodzovsky 305d7edde3dSLuben Tuikov record->bank = buf[i++]; 30664f55e62SAndrey Grodzovsky 307d7edde3dSLuben Tuikov memcpy(&tmp, buf + i, 8); 30864f55e62SAndrey Grodzovsky record->ts = le64_to_cpu(tmp); 30964f55e62SAndrey Grodzovsky i += 8; 31064f55e62SAndrey Grodzovsky 311d7edde3dSLuben Tuikov memcpy(&tmp, buf + i, 6); 31264f55e62SAndrey Grodzovsky record->offset = (le64_to_cpu(tmp) & 0xffffffffffff); 31364f55e62SAndrey Grodzovsky i += 6; 31464f55e62SAndrey Grodzovsky 315d7edde3dSLuben Tuikov record->mem_channel = buf[i++]; 316d7edde3dSLuben Tuikov record->mcumc_id = buf[i++]; 31764f55e62SAndrey Grodzovsky 318d7edde3dSLuben Tuikov memcpy(&tmp, buf + i, 6); 31964f55e62SAndrey Grodzovsky record->retired_page = (le64_to_cpu(tmp) & 0xffffffffffff); 32064f55e62SAndrey Grodzovsky } 32164f55e62SAndrey Grodzovsky 32211003c68SDennis Li bool amdgpu_ras_eeprom_check_err_threshold(struct amdgpu_device *adev) 323e8fbaf03SGuchun Chen { 32411003c68SDennis Li struct amdgpu_ras *con = amdgpu_ras_get_context(adev); 325e8fbaf03SGuchun Chen 3264bfb7428SJohn Clements if (!__is_ras_eeprom_supported(adev)) 32711003c68SDennis Li return false; 3284bfb7428SJohn Clements 329970fd197SStanley.Yang /* skip check eeprom table for VEGA20 Gaming */ 330970fd197SStanley.Yang if (!con) 331970fd197SStanley.Yang return false; 332970fd197SStanley.Yang else 333970fd197SStanley.Yang if (!(con->features & BIT(AMDGPU_RAS_BLOCK__UMC))) 334970fd197SStanley.Yang return false; 335970fd197SStanley.Yang 336c28aa44dSLuben Tuikov if (con->eeprom_control.tbl_hdr.header == RAS_TABLE_HDR_BAD) { 337e8fbaf03SGuchun Chen dev_warn(adev->dev, "This GPU is in BAD status."); 338*63d4c081SLuben Tuikov dev_warn(adev->dev, "Please retire it or set a larger " 339e8fbaf03SGuchun Chen "threshold value when reloading driver.\n"); 34011003c68SDennis Li return true; 341e8fbaf03SGuchun Chen } 342e8fbaf03SGuchun Chen 34311003c68SDennis Li return false; 344e8fbaf03SGuchun Chen } 345e8fbaf03SGuchun Chen 346*63d4c081SLuben Tuikov /** 347*63d4c081SLuben Tuikov * __amdgpu_ras_eeprom_write -- write indexed from buffer to EEPROM 348*63d4c081SLuben Tuikov * @control: pointer to control structure 349*63d4c081SLuben Tuikov * @buf: pointer to buffer containing data to write 350*63d4c081SLuben Tuikov * @fri: start writing at this index 351*63d4c081SLuben Tuikov * @num: number of records to write 352*63d4c081SLuben Tuikov * 353*63d4c081SLuben Tuikov * The caller must hold the table mutex in @control. 354*63d4c081SLuben Tuikov * Return 0 on success, -errno otherwise. 355*63d4c081SLuben Tuikov */ 356*63d4c081SLuben Tuikov static int __amdgpu_ras_eeprom_write(struct amdgpu_ras_eeprom_control *control, 357*63d4c081SLuben Tuikov u8 *buf, const u32 fri, const u32 num) 35864f55e62SAndrey Grodzovsky { 359*63d4c081SLuben Tuikov struct amdgpu_device *adev = to_amdgpu_device(control); 360*63d4c081SLuben Tuikov u32 buf_size; 361*63d4c081SLuben Tuikov int res; 362*63d4c081SLuben Tuikov 363*63d4c081SLuben Tuikov /* i2c may be unstable in gpu reset */ 364*63d4c081SLuben Tuikov down_read(&adev->reset_sem); 365*63d4c081SLuben Tuikov buf_size = num * RAS_TABLE_RECORD_SIZE; 366*63d4c081SLuben Tuikov res = amdgpu_eeprom_write(&adev->pm.smu_i2c, 367*63d4c081SLuben Tuikov control->i2c_address + 368*63d4c081SLuben Tuikov RAS_INDEX_TO_OFFSET(control, fri), 369*63d4c081SLuben Tuikov buf, buf_size); 370*63d4c081SLuben Tuikov up_read(&adev->reset_sem); 371*63d4c081SLuben Tuikov if (res < 0) { 372*63d4c081SLuben Tuikov DRM_ERROR("Writing %d EEPROM table records error:%d", 373*63d4c081SLuben Tuikov num, res); 374*63d4c081SLuben Tuikov } else if (res < buf_size) { 375*63d4c081SLuben Tuikov /* Short write, return error. 376*63d4c081SLuben Tuikov */ 377*63d4c081SLuben Tuikov DRM_ERROR("Wrote %d records out of %d", 378*63d4c081SLuben Tuikov res / RAS_TABLE_RECORD_SIZE, num); 379*63d4c081SLuben Tuikov res = -EIO; 380*63d4c081SLuben Tuikov } else { 381*63d4c081SLuben Tuikov res = 0; 382*63d4c081SLuben Tuikov } 383*63d4c081SLuben Tuikov 384*63d4c081SLuben Tuikov return res; 385*63d4c081SLuben Tuikov } 386*63d4c081SLuben Tuikov 387*63d4c081SLuben Tuikov static int 388*63d4c081SLuben Tuikov amdgpu_ras_eeprom_append_table(struct amdgpu_ras_eeprom_control *control, 389*63d4c081SLuben Tuikov struct eeprom_table_record *record, 390*63d4c081SLuben Tuikov const u32 num) 391*63d4c081SLuben Tuikov { 392*63d4c081SLuben Tuikov u32 a, b, i; 393*63d4c081SLuben Tuikov u8 *buf, *pp; 394*63d4c081SLuben Tuikov int res; 395*63d4c081SLuben Tuikov 396*63d4c081SLuben Tuikov buf = kcalloc(num, RAS_TABLE_RECORD_SIZE, GFP_KERNEL); 397*63d4c081SLuben Tuikov if (!buf) 398*63d4c081SLuben Tuikov return -ENOMEM; 399*63d4c081SLuben Tuikov 400*63d4c081SLuben Tuikov /* Encode all of them in one go. 401*63d4c081SLuben Tuikov */ 402*63d4c081SLuben Tuikov pp = buf; 403*63d4c081SLuben Tuikov for (i = 0; i < num; i++, pp += RAS_TABLE_RECORD_SIZE) 404*63d4c081SLuben Tuikov __encode_table_record_to_buf(control, &record[i], pp); 405*63d4c081SLuben Tuikov 406*63d4c081SLuben Tuikov /* a, first record index to write into. 407*63d4c081SLuben Tuikov * b, last record index to write into. 408*63d4c081SLuben Tuikov * a = first index to read (fri) + number of records in the table, 409*63d4c081SLuben Tuikov * b = a + @num - 1. 410*63d4c081SLuben Tuikov * Let N = control->ras_max_num_record_count, then we have, 411*63d4c081SLuben Tuikov * case 0: 0 <= a <= b < N, 412*63d4c081SLuben Tuikov * just append @num records starting at a; 413*63d4c081SLuben Tuikov * case 1: 0 <= a < N <= b, 414*63d4c081SLuben Tuikov * append (N - a) records starting at a, and 415*63d4c081SLuben Tuikov * append the remainder, b % N + 1, starting at 0. 416*63d4c081SLuben Tuikov * case 2: 0 <= fri < N <= a <= b, then modulo N we get two subcases, 417*63d4c081SLuben Tuikov * case 2a: 0 <= a <= b < N 418*63d4c081SLuben Tuikov * append num records starting at a; and fix fri if b overwrote it, 419*63d4c081SLuben Tuikov * and since a <= b, if b overwrote it then a must've also, 420*63d4c081SLuben Tuikov * and if b didn't overwrite it, then a didn't also. 421*63d4c081SLuben Tuikov * case 2b: 0 <= b < a < N 422*63d4c081SLuben Tuikov * write num records starting at a, which wraps around 0=N 423*63d4c081SLuben Tuikov * and overwrite fri unconditionally. Now from case 2a, 424*63d4c081SLuben Tuikov * this means that b eclipsed fri to overwrite it and wrap 425*63d4c081SLuben Tuikov * around 0 again, i.e. b = 2N+r pre modulo N, so we unconditionally 426*63d4c081SLuben Tuikov * set fri = b + 1 (mod N). 427*63d4c081SLuben Tuikov * Now, since fri is updated in every case, except the trivial case 0, 428*63d4c081SLuben Tuikov * the number of records present in the table after writing, is, 429*63d4c081SLuben Tuikov * num_recs - 1 = b - fri (mod N), and we take the positive value, 430*63d4c081SLuben Tuikov * by adding an arbitrary multiple of N before taking the modulo N 431*63d4c081SLuben Tuikov * as shown below. 432*63d4c081SLuben Tuikov */ 433*63d4c081SLuben Tuikov a = control->ras_fri + control->ras_num_recs; 434*63d4c081SLuben Tuikov b = a + num - 1; 435*63d4c081SLuben Tuikov if (b < control->ras_max_record_count) { 436*63d4c081SLuben Tuikov res = __amdgpu_ras_eeprom_write(control, buf, a, num); 437*63d4c081SLuben Tuikov } else if (a < control->ras_max_record_count) { 438*63d4c081SLuben Tuikov u32 g0, g1; 439*63d4c081SLuben Tuikov 440*63d4c081SLuben Tuikov g0 = control->ras_max_record_count - a; 441*63d4c081SLuben Tuikov g1 = b % control->ras_max_record_count + 1; 442*63d4c081SLuben Tuikov res = __amdgpu_ras_eeprom_write(control, buf, a, g0); 443*63d4c081SLuben Tuikov if (res) 444*63d4c081SLuben Tuikov goto Out; 445*63d4c081SLuben Tuikov res = __amdgpu_ras_eeprom_write(control, 446*63d4c081SLuben Tuikov buf + g0 * RAS_TABLE_RECORD_SIZE, 447*63d4c081SLuben Tuikov 0, g1); 448*63d4c081SLuben Tuikov if (res) 449*63d4c081SLuben Tuikov goto Out; 450*63d4c081SLuben Tuikov if (g1 > control->ras_fri) 451*63d4c081SLuben Tuikov control->ras_fri = g1 % control->ras_max_record_count; 452*63d4c081SLuben Tuikov } else { 453*63d4c081SLuben Tuikov a %= control->ras_max_record_count; 454*63d4c081SLuben Tuikov b %= control->ras_max_record_count; 455*63d4c081SLuben Tuikov 456*63d4c081SLuben Tuikov if (a <= b) { 457*63d4c081SLuben Tuikov /* Note that, b - a + 1 = num. */ 458*63d4c081SLuben Tuikov res = __amdgpu_ras_eeprom_write(control, buf, a, num); 459*63d4c081SLuben Tuikov if (res) 460*63d4c081SLuben Tuikov goto Out; 461*63d4c081SLuben Tuikov if (b >= control->ras_fri) 462*63d4c081SLuben Tuikov control->ras_fri = (b + 1) % control->ras_max_record_count; 463*63d4c081SLuben Tuikov } else { 464*63d4c081SLuben Tuikov u32 g0, g1; 465*63d4c081SLuben Tuikov 466*63d4c081SLuben Tuikov /* b < a, which means, we write from 467*63d4c081SLuben Tuikov * a to the end of the table, and from 468*63d4c081SLuben Tuikov * the start of the table to b. 469*63d4c081SLuben Tuikov */ 470*63d4c081SLuben Tuikov g0 = control->ras_max_record_count - a; 471*63d4c081SLuben Tuikov g1 = b + 1; 472*63d4c081SLuben Tuikov res = __amdgpu_ras_eeprom_write(control, buf, a, g0); 473*63d4c081SLuben Tuikov if (res) 474*63d4c081SLuben Tuikov goto Out; 475*63d4c081SLuben Tuikov res = __amdgpu_ras_eeprom_write(control, 476*63d4c081SLuben Tuikov buf + g0 * RAS_TABLE_RECORD_SIZE, 477*63d4c081SLuben Tuikov 0, g1); 478*63d4c081SLuben Tuikov if (res) 479*63d4c081SLuben Tuikov goto Out; 480*63d4c081SLuben Tuikov control->ras_fri = g1 % control->ras_max_record_count; 481*63d4c081SLuben Tuikov } 482*63d4c081SLuben Tuikov } 483*63d4c081SLuben Tuikov control->ras_num_recs = 1 + (control->ras_max_record_count + b 484*63d4c081SLuben Tuikov - control->ras_fri) 485*63d4c081SLuben Tuikov % control->ras_max_record_count; 486*63d4c081SLuben Tuikov Out: 487*63d4c081SLuben Tuikov kfree(buf); 488*63d4c081SLuben Tuikov return res; 489*63d4c081SLuben Tuikov } 490*63d4c081SLuben Tuikov 491*63d4c081SLuben Tuikov static int 492*63d4c081SLuben Tuikov amdgpu_ras_eeprom_update_header(struct amdgpu_ras_eeprom_control *control) 493*63d4c081SLuben Tuikov { 49464f55e62SAndrey Grodzovsky struct amdgpu_device *adev = to_amdgpu_device(control); 4959c06f91fSGuchun Chen struct amdgpu_ras *ras = amdgpu_ras_get_context(adev); 496*63d4c081SLuben Tuikov u8 *buf, *pp, csum; 497*63d4c081SLuben Tuikov u32 buf_size; 498*63d4c081SLuben Tuikov int res; 499*63d4c081SLuben Tuikov 500*63d4c081SLuben Tuikov /* Modify the header if it exceeds. 501*63d4c081SLuben Tuikov */ 502*63d4c081SLuben Tuikov if (amdgpu_bad_page_threshold != 0 && 503*63d4c081SLuben Tuikov control->ras_num_recs >= ras->bad_page_cnt_threshold) { 504*63d4c081SLuben Tuikov dev_warn(adev->dev, 505*63d4c081SLuben Tuikov "Saved bad pages %d reaches threshold value %d\n", 506*63d4c081SLuben Tuikov control->ras_num_recs, ras->bad_page_cnt_threshold); 507*63d4c081SLuben Tuikov control->tbl_hdr.header = RAS_TABLE_HDR_BAD; 508*63d4c081SLuben Tuikov } 509*63d4c081SLuben Tuikov 510*63d4c081SLuben Tuikov control->tbl_hdr.version = RAS_TABLE_VER; 511*63d4c081SLuben Tuikov control->tbl_hdr.first_rec_offset = RAS_INDEX_TO_OFFSET(control, control->ras_fri); 512*63d4c081SLuben Tuikov control->tbl_hdr.tbl_size = RAS_TABLE_HEADER_SIZE + control->ras_num_recs * RAS_TABLE_RECORD_SIZE; 513*63d4c081SLuben Tuikov control->tbl_hdr.checksum = 0; 514*63d4c081SLuben Tuikov 515*63d4c081SLuben Tuikov buf_size = control->ras_num_recs * RAS_TABLE_RECORD_SIZE; 516*63d4c081SLuben Tuikov buf = kcalloc(control->ras_num_recs, RAS_TABLE_RECORD_SIZE, GFP_KERNEL); 517*63d4c081SLuben Tuikov if (!buf) { 518*63d4c081SLuben Tuikov DRM_ERROR("allocating memory for table of size %d bytes failed\n", 519*63d4c081SLuben Tuikov control->tbl_hdr.tbl_size); 520*63d4c081SLuben Tuikov res = -ENOMEM; 521*63d4c081SLuben Tuikov goto Out; 522*63d4c081SLuben Tuikov } 523*63d4c081SLuben Tuikov 524*63d4c081SLuben Tuikov down_read(&adev->reset_sem); 525*63d4c081SLuben Tuikov res = amdgpu_eeprom_read(&adev->pm.smu_i2c, 526*63d4c081SLuben Tuikov control->i2c_address + 527*63d4c081SLuben Tuikov control->ras_record_offset, 528*63d4c081SLuben Tuikov buf, buf_size); 529*63d4c081SLuben Tuikov up_read(&adev->reset_sem); 530*63d4c081SLuben Tuikov if (res < 0) { 531*63d4c081SLuben Tuikov DRM_ERROR("EEPROM failed reading records:%d\n", 532*63d4c081SLuben Tuikov res); 533*63d4c081SLuben Tuikov goto Out; 534*63d4c081SLuben Tuikov } else if (res < buf_size) { 535*63d4c081SLuben Tuikov DRM_ERROR("EEPROM read %d out of %d bytes\n", 536*63d4c081SLuben Tuikov res, buf_size); 537*63d4c081SLuben Tuikov res = -EIO; 538*63d4c081SLuben Tuikov goto Out; 539*63d4c081SLuben Tuikov } 540*63d4c081SLuben Tuikov 541*63d4c081SLuben Tuikov /* Recalc the checksum. 542*63d4c081SLuben Tuikov */ 543*63d4c081SLuben Tuikov csum = 0; 544*63d4c081SLuben Tuikov for (pp = buf; pp < buf + buf_size; pp++) 545*63d4c081SLuben Tuikov csum += *pp; 546*63d4c081SLuben Tuikov 547*63d4c081SLuben Tuikov csum += __calc_hdr_byte_sum(control); 548*63d4c081SLuben Tuikov /* avoid sign extension when assigning to "checksum" */ 549*63d4c081SLuben Tuikov csum = -csum; 550*63d4c081SLuben Tuikov control->tbl_hdr.checksum = csum; 551*63d4c081SLuben Tuikov res = __write_table_header(control); 552*63d4c081SLuben Tuikov Out: 553*63d4c081SLuben Tuikov kfree(buf); 554*63d4c081SLuben Tuikov return res; 555*63d4c081SLuben Tuikov } 556*63d4c081SLuben Tuikov 557*63d4c081SLuben Tuikov /** 558*63d4c081SLuben Tuikov * amdgpu_ras_eeprom_append -- append records to the EEPROM RAS table 559*63d4c081SLuben Tuikov * @control: pointer to control structure 560*63d4c081SLuben Tuikov * @record: array of records to append 561*63d4c081SLuben Tuikov * @num: number of records in @record array 562*63d4c081SLuben Tuikov * 563*63d4c081SLuben Tuikov * Append @num records to the table, calculate the checksum and write 564*63d4c081SLuben Tuikov * the table back to EEPROM. The maximum number of records that 565*63d4c081SLuben Tuikov * can be appended is between 1 and control->ras_max_record_count, 566*63d4c081SLuben Tuikov * regardless of how many records are already stored in the table. 567*63d4c081SLuben Tuikov * 568*63d4c081SLuben Tuikov * Return 0 on success or if EEPROM is not supported, -errno on error. 569*63d4c081SLuben Tuikov */ 570*63d4c081SLuben Tuikov int amdgpu_ras_eeprom_append(struct amdgpu_ras_eeprom_control *control, 571*63d4c081SLuben Tuikov struct eeprom_table_record *record, 572*63d4c081SLuben Tuikov const u32 num) 573*63d4c081SLuben Tuikov { 574*63d4c081SLuben Tuikov struct amdgpu_device *adev = to_amdgpu_device(control); 575*63d4c081SLuben Tuikov int res; 57664f55e62SAndrey Grodzovsky 5774bfb7428SJohn Clements if (!__is_ras_eeprom_supported(adev)) 57864f55e62SAndrey Grodzovsky return 0; 57964f55e62SAndrey Grodzovsky 580*63d4c081SLuben Tuikov if (num == 0) { 581*63d4c081SLuben Tuikov DRM_ERROR("will not append 0 records\n"); 582*63d4c081SLuben Tuikov return -EINVAL; 583*63d4c081SLuben Tuikov } else if (num > control->ras_max_record_count) { 584*63d4c081SLuben Tuikov DRM_ERROR("cannot append %d records than the size of table %d\n", 585*63d4c081SLuben Tuikov num, control->ras_max_record_count); 586*63d4c081SLuben Tuikov return -EINVAL; 587*63d4c081SLuben Tuikov } 58864f55e62SAndrey Grodzovsky 5890686627bSLuben Tuikov mutex_lock(&control->ras_tbl_mutex); 59064f55e62SAndrey Grodzovsky 591*63d4c081SLuben Tuikov res = amdgpu_ras_eeprom_append_table(control, record, num); 592*63d4c081SLuben Tuikov if (!res) 593*63d4c081SLuben Tuikov res = amdgpu_ras_eeprom_update_header(control); 594*63d4c081SLuben Tuikov 595*63d4c081SLuben Tuikov mutex_unlock(&control->ras_tbl_mutex); 596*63d4c081SLuben Tuikov return res; 5979c06f91fSGuchun Chen } 5989c06f91fSGuchun Chen 599*63d4c081SLuben Tuikov /** 600*63d4c081SLuben Tuikov * __amdgpu_ras_eeprom_read -- read indexed from EEPROM into buffer 601*63d4c081SLuben Tuikov * @control: pointer to control structure 602*63d4c081SLuben Tuikov * @buf: pointer to buffer to read into 603*63d4c081SLuben Tuikov * @fri: first record index, start reading at this index, absolute index 604*63d4c081SLuben Tuikov * @num: number of records to read 605*63d4c081SLuben Tuikov * 606*63d4c081SLuben Tuikov * The caller must hold the table mutex in @control. 607*63d4c081SLuben Tuikov * Return 0 on success, -errno otherwise. 60864f55e62SAndrey Grodzovsky */ 609*63d4c081SLuben Tuikov static int __amdgpu_ras_eeprom_read(struct amdgpu_ras_eeprom_control *control, 610*63d4c081SLuben Tuikov u8 *buf, const u32 fri, const u32 num) 611*63d4c081SLuben Tuikov { 612*63d4c081SLuben Tuikov struct amdgpu_device *adev = to_amdgpu_device(control); 613*63d4c081SLuben Tuikov u32 buf_size; 614*63d4c081SLuben Tuikov int res; 61564f55e62SAndrey Grodzovsky 61624f55c05SAlex Deucher /* i2c may be unstable in gpu reset */ 61724f55c05SAlex Deucher down_read(&adev->reset_sem); 618*63d4c081SLuben Tuikov buf_size = num * RAS_TABLE_RECORD_SIZE; 619*63d4c081SLuben Tuikov res = amdgpu_eeprom_read(&adev->pm.smu_i2c, 620*63d4c081SLuben Tuikov control->i2c_address + 621*63d4c081SLuben Tuikov RAS_INDEX_TO_OFFSET(control, fri), 622*63d4c081SLuben Tuikov buf, buf_size); 62324f55c05SAlex Deucher up_read(&adev->reset_sem); 624*63d4c081SLuben Tuikov if (res < 0) { 625*63d4c081SLuben Tuikov DRM_ERROR("Reading %d EEPROM table records error:%d", 626*63d4c081SLuben Tuikov num, res); 627*63d4c081SLuben Tuikov } else if (res < buf_size) { 628*63d4c081SLuben Tuikov /* Short read, return error. 62964f55e62SAndrey Grodzovsky */ 630*63d4c081SLuben Tuikov DRM_ERROR("Read %d records out of %d", 631*63d4c081SLuben Tuikov res / RAS_TABLE_RECORD_SIZE, num); 632*63d4c081SLuben Tuikov res = -EIO; 633*63d4c081SLuben Tuikov } else { 634*63d4c081SLuben Tuikov res = 0; 63564f55e62SAndrey Grodzovsky } 63664f55e62SAndrey Grodzovsky 637*63d4c081SLuben Tuikov return res; 63864f55e62SAndrey Grodzovsky } 63964f55e62SAndrey Grodzovsky 640*63d4c081SLuben Tuikov /** 641*63d4c081SLuben Tuikov * amdgpu_ras_eeprom_read -- read EEPROM 642*63d4c081SLuben Tuikov * @control: pointer to control structure 643*63d4c081SLuben Tuikov * @record: array of records to read into 644*63d4c081SLuben Tuikov * @num: number of records in @record 64564f55e62SAndrey Grodzovsky * 646*63d4c081SLuben Tuikov * Reads num records from the RAS table in EEPROM and 647*63d4c081SLuben Tuikov * writes the data into @record array. 648*63d4c081SLuben Tuikov * 649*63d4c081SLuben Tuikov * Returns 0 on success, -errno on error. 65064f55e62SAndrey Grodzovsky */ 651*63d4c081SLuben Tuikov int amdgpu_ras_eeprom_read(struct amdgpu_ras_eeprom_control *control, 652*63d4c081SLuben Tuikov struct eeprom_table_record *record, 653*63d4c081SLuben Tuikov const u32 num) 654*63d4c081SLuben Tuikov { 655*63d4c081SLuben Tuikov struct amdgpu_device *adev = to_amdgpu_device(control); 656*63d4c081SLuben Tuikov int i, res; 657*63d4c081SLuben Tuikov u8 *buf, *pp; 658*63d4c081SLuben Tuikov u32 g0, g1; 65964f55e62SAndrey Grodzovsky 660*63d4c081SLuben Tuikov if (!__is_ras_eeprom_supported(adev)) 661*63d4c081SLuben Tuikov return 0; 662*63d4c081SLuben Tuikov 663*63d4c081SLuben Tuikov if (num == 0) { 664*63d4c081SLuben Tuikov DRM_ERROR("will not read 0 records\n"); 665*63d4c081SLuben Tuikov return -EINVAL; 666*63d4c081SLuben Tuikov } else if (num > control->ras_num_recs) { 667*63d4c081SLuben Tuikov DRM_ERROR("too many records to read:%d available:%d\n", 668*63d4c081SLuben Tuikov num, control->ras_num_recs); 669*63d4c081SLuben Tuikov return -EINVAL; 67064f55e62SAndrey Grodzovsky } 67164f55e62SAndrey Grodzovsky 672*63d4c081SLuben Tuikov buf = kcalloc(num, RAS_TABLE_RECORD_SIZE, GFP_KERNEL); 673*63d4c081SLuben Tuikov if (!buf) 674*63d4c081SLuben Tuikov return -ENOMEM; 67564f55e62SAndrey Grodzovsky 676*63d4c081SLuben Tuikov /* Determine how many records to read, from the first record 677*63d4c081SLuben Tuikov * index, fri, to the end of the table, and from the beginning 678*63d4c081SLuben Tuikov * of the table, such that the total number of records is 679*63d4c081SLuben Tuikov * @num, and we handle wrap around when fri > 0 and 680*63d4c081SLuben Tuikov * fri + num > RAS_MAX_RECORD_COUNT. 681*63d4c081SLuben Tuikov * 682*63d4c081SLuben Tuikov * First we compute the index of the last element 683*63d4c081SLuben Tuikov * which would be fetched from each region, 684*63d4c081SLuben Tuikov * g0 is in [fri, fri + num - 1], and 685*63d4c081SLuben Tuikov * g1 is in [0, RAS_MAX_RECORD_COUNT - 1]. 686*63d4c081SLuben Tuikov * Then, if g0 < RAS_MAX_RECORD_COUNT, the index of 687*63d4c081SLuben Tuikov * the last element to fetch, we set g0 to _the number_ 688*63d4c081SLuben Tuikov * of elements to fetch, @num, since we know that the last 689*63d4c081SLuben Tuikov * indexed to be fetched does not exceed the table. 690*63d4c081SLuben Tuikov * 691*63d4c081SLuben Tuikov * If, however, g0 >= RAS_MAX_RECORD_COUNT, then 692*63d4c081SLuben Tuikov * we set g0 to the number of elements to read 693*63d4c081SLuben Tuikov * until the end of the table, and g1 to the number of 694*63d4c081SLuben Tuikov * elements to read from the beginning of the table. 695*63d4c081SLuben Tuikov */ 696*63d4c081SLuben Tuikov g0 = control->ras_fri + num - 1; 697*63d4c081SLuben Tuikov g1 = g0 % control->ras_max_record_count; 698*63d4c081SLuben Tuikov if (g0 < control->ras_max_record_count) { 699*63d4c081SLuben Tuikov g0 = num; 700*63d4c081SLuben Tuikov g1 = 0; 701*63d4c081SLuben Tuikov } else { 702*63d4c081SLuben Tuikov g0 = control->ras_max_record_count - control->ras_fri; 703*63d4c081SLuben Tuikov g1 += 1; 704*63d4c081SLuben Tuikov } 705*63d4c081SLuben Tuikov 706*63d4c081SLuben Tuikov mutex_lock(&control->ras_tbl_mutex); 707*63d4c081SLuben Tuikov res = __amdgpu_ras_eeprom_read(control, buf, control->ras_fri, g0); 708*63d4c081SLuben Tuikov if (res) 709*63d4c081SLuben Tuikov goto Out; 710*63d4c081SLuben Tuikov if (g1) { 711*63d4c081SLuben Tuikov res = __amdgpu_ras_eeprom_read(control, 712*63d4c081SLuben Tuikov buf + g0 * RAS_TABLE_RECORD_SIZE, 713*63d4c081SLuben Tuikov 0, g1); 714*63d4c081SLuben Tuikov if (res) 715*63d4c081SLuben Tuikov goto Out; 716*63d4c081SLuben Tuikov } 717*63d4c081SLuben Tuikov 718*63d4c081SLuben Tuikov res = 0; 719*63d4c081SLuben Tuikov 720*63d4c081SLuben Tuikov /* Read up everything? Then transform. 721*63d4c081SLuben Tuikov */ 722*63d4c081SLuben Tuikov pp = buf; 723*63d4c081SLuben Tuikov for (i = 0; i < num; i++, pp += RAS_TABLE_RECORD_SIZE) 724*63d4c081SLuben Tuikov __decode_table_record_from_buf(control, &record[i], pp); 725*63d4c081SLuben Tuikov Out: 726*63d4c081SLuben Tuikov kfree(buf); 7270686627bSLuben Tuikov mutex_unlock(&control->ras_tbl_mutex); 72864f55e62SAndrey Grodzovsky 729*63d4c081SLuben Tuikov return res; 7301fab841fSLuben Tuikov } 7311fab841fSLuben Tuikov 732e4e6a589SLuben Tuikov inline uint32_t amdgpu_ras_eeprom_max_record_count(void) 733c84d4670SGuchun Chen { 734e4e6a589SLuben Tuikov return RAS_MAX_RECORD_COUNT; 735c84d4670SGuchun Chen } 736*63d4c081SLuben Tuikov 737*63d4c081SLuben Tuikov /** 738*63d4c081SLuben Tuikov * __verify_ras_table_checksum -- verify the RAS EEPROM table checksum 739*63d4c081SLuben Tuikov * @control: pointer to control structure 740*63d4c081SLuben Tuikov * 741*63d4c081SLuben Tuikov * Check the checksum of the stored in EEPROM RAS table. 742*63d4c081SLuben Tuikov * 743*63d4c081SLuben Tuikov * Return 0 if the checksum is correct, 744*63d4c081SLuben Tuikov * positive if it is not correct, and 745*63d4c081SLuben Tuikov * -errno on I/O error. 746*63d4c081SLuben Tuikov */ 747*63d4c081SLuben Tuikov static int __verify_ras_table_checksum(struct amdgpu_ras_eeprom_control *control) 748*63d4c081SLuben Tuikov { 749*63d4c081SLuben Tuikov struct amdgpu_device *adev = to_amdgpu_device(control); 750*63d4c081SLuben Tuikov int res; 751*63d4c081SLuben Tuikov u8 csum, *buf, *pp; 752*63d4c081SLuben Tuikov u32 buf_size; 753*63d4c081SLuben Tuikov 754*63d4c081SLuben Tuikov buf_size = RAS_TABLE_HEADER_SIZE + 755*63d4c081SLuben Tuikov control->ras_num_recs * RAS_TABLE_RECORD_SIZE; 756*63d4c081SLuben Tuikov buf = kzalloc(buf_size, GFP_KERNEL); 757*63d4c081SLuben Tuikov if (!buf) { 758*63d4c081SLuben Tuikov DRM_ERROR("Out of memory checking RAS table checksum.\n"); 759*63d4c081SLuben Tuikov return -ENOMEM; 760*63d4c081SLuben Tuikov } 761*63d4c081SLuben Tuikov 762*63d4c081SLuben Tuikov res = amdgpu_eeprom_read(&adev->pm.smu_i2c, 763*63d4c081SLuben Tuikov control->i2c_address + 764*63d4c081SLuben Tuikov control->ras_header_offset, 765*63d4c081SLuben Tuikov buf, buf_size); 766*63d4c081SLuben Tuikov if (res < buf_size) { 767*63d4c081SLuben Tuikov DRM_ERROR("Partial read for checksum, res:%d\n", res); 768*63d4c081SLuben Tuikov /* On partial reads, return -EIO. 769*63d4c081SLuben Tuikov */ 770*63d4c081SLuben Tuikov if (res >= 0) 771*63d4c081SLuben Tuikov res = -EIO; 772*63d4c081SLuben Tuikov goto Out; 773*63d4c081SLuben Tuikov } 774*63d4c081SLuben Tuikov 775*63d4c081SLuben Tuikov csum = 0; 776*63d4c081SLuben Tuikov for (pp = buf; pp < buf + buf_size; pp++) 777*63d4c081SLuben Tuikov csum += *pp; 778*63d4c081SLuben Tuikov Out: 779*63d4c081SLuben Tuikov kfree(buf); 780*63d4c081SLuben Tuikov return res < 0 ? res : csum; 781*63d4c081SLuben Tuikov } 782*63d4c081SLuben Tuikov 783*63d4c081SLuben Tuikov int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control, 784*63d4c081SLuben Tuikov bool *exceed_err_limit) 785*63d4c081SLuben Tuikov { 786*63d4c081SLuben Tuikov struct amdgpu_device *adev = to_amdgpu_device(control); 787*63d4c081SLuben Tuikov unsigned char buf[RAS_TABLE_HEADER_SIZE] = { 0 }; 788*63d4c081SLuben Tuikov struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr; 789*63d4c081SLuben Tuikov struct amdgpu_ras *ras = amdgpu_ras_get_context(adev); 790*63d4c081SLuben Tuikov int res; 791*63d4c081SLuben Tuikov 792*63d4c081SLuben Tuikov *exceed_err_limit = false; 793*63d4c081SLuben Tuikov 794*63d4c081SLuben Tuikov if (!__is_ras_eeprom_supported(adev)) 795*63d4c081SLuben Tuikov return 0; 796*63d4c081SLuben Tuikov 797*63d4c081SLuben Tuikov /* Verify i2c adapter is initialized */ 798*63d4c081SLuben Tuikov if (!adev->pm.smu_i2c.algo) 799*63d4c081SLuben Tuikov return -ENOENT; 800*63d4c081SLuben Tuikov 801*63d4c081SLuben Tuikov if (!__get_eeprom_i2c_addr(adev, control)) 802*63d4c081SLuben Tuikov return -EINVAL; 803*63d4c081SLuben Tuikov 804*63d4c081SLuben Tuikov control->ras_header_offset = RAS_HDR_START; 805*63d4c081SLuben Tuikov control->ras_record_offset = RAS_RECORD_START; 806*63d4c081SLuben Tuikov control->ras_max_record_count = RAS_MAX_RECORD_COUNT; 807*63d4c081SLuben Tuikov mutex_init(&control->ras_tbl_mutex); 808*63d4c081SLuben Tuikov 809*63d4c081SLuben Tuikov /* Read the table header from EEPROM address */ 810*63d4c081SLuben Tuikov res = amdgpu_eeprom_read(&adev->pm.smu_i2c, 811*63d4c081SLuben Tuikov control->i2c_address + control->ras_header_offset, 812*63d4c081SLuben Tuikov buf, RAS_TABLE_HEADER_SIZE); 813*63d4c081SLuben Tuikov if (res < RAS_TABLE_HEADER_SIZE) { 814*63d4c081SLuben Tuikov DRM_ERROR("Failed to read EEPROM table header, res:%d", res); 815*63d4c081SLuben Tuikov return res >= 0 ? -EIO : res; 816*63d4c081SLuben Tuikov } 817*63d4c081SLuben Tuikov 818*63d4c081SLuben Tuikov __decode_table_header_from_buf(hdr, buf); 819*63d4c081SLuben Tuikov 820*63d4c081SLuben Tuikov control->ras_num_recs = RAS_NUM_RECS(hdr); 821*63d4c081SLuben Tuikov control->ras_fri = RAS_OFFSET_TO_INDEX(control, hdr->first_rec_offset); 822*63d4c081SLuben Tuikov 823*63d4c081SLuben Tuikov if (hdr->header == RAS_TABLE_HDR_VAL) { 824*63d4c081SLuben Tuikov DRM_DEBUG_DRIVER("Found existing EEPROM table with %d records", 825*63d4c081SLuben Tuikov control->ras_num_recs); 826*63d4c081SLuben Tuikov res = __verify_ras_table_checksum(control); 827*63d4c081SLuben Tuikov if (res) 828*63d4c081SLuben Tuikov DRM_ERROR("RAS table incorrect checksum or error:%d\n", 829*63d4c081SLuben Tuikov res); 830*63d4c081SLuben Tuikov } else if (hdr->header == RAS_TABLE_HDR_BAD && 831*63d4c081SLuben Tuikov amdgpu_bad_page_threshold != 0) { 832*63d4c081SLuben Tuikov res = __verify_ras_table_checksum(control); 833*63d4c081SLuben Tuikov if (res) 834*63d4c081SLuben Tuikov DRM_ERROR("RAS Table incorrect checksum or error:%d\n", 835*63d4c081SLuben Tuikov res); 836*63d4c081SLuben Tuikov if (ras->bad_page_cnt_threshold > control->ras_num_recs) { 837*63d4c081SLuben Tuikov /* This means that, the threshold was increased since 838*63d4c081SLuben Tuikov * the last time the system was booted, and now, 839*63d4c081SLuben Tuikov * ras->bad_page_cnt_threshold - control->num_recs > 0, 840*63d4c081SLuben Tuikov * so that at least one more record can be saved, 841*63d4c081SLuben Tuikov * before the page count threshold is reached. 842*63d4c081SLuben Tuikov */ 843*63d4c081SLuben Tuikov dev_info(adev->dev, 844*63d4c081SLuben Tuikov "records:%d threshold:%d, resetting " 845*63d4c081SLuben Tuikov "RAS table header signature", 846*63d4c081SLuben Tuikov control->ras_num_recs, 847*63d4c081SLuben Tuikov ras->bad_page_cnt_threshold); 848*63d4c081SLuben Tuikov res = amdgpu_ras_eeprom_correct_header_tag(control, 849*63d4c081SLuben Tuikov RAS_TABLE_HDR_VAL); 850*63d4c081SLuben Tuikov } else { 851*63d4c081SLuben Tuikov *exceed_err_limit = true; 852*63d4c081SLuben Tuikov dev_err(adev->dev, 853*63d4c081SLuben Tuikov "RAS records:%d exceed threshold:%d, " 854*63d4c081SLuben Tuikov "maybe retire this GPU?", 855*63d4c081SLuben Tuikov control->ras_num_recs, ras->bad_page_cnt_threshold); 856*63d4c081SLuben Tuikov } 857*63d4c081SLuben Tuikov } else { 858*63d4c081SLuben Tuikov DRM_INFO("Creating a new EEPROM table"); 859*63d4c081SLuben Tuikov 860*63d4c081SLuben Tuikov res = amdgpu_ras_eeprom_reset_table(control); 861*63d4c081SLuben Tuikov } 862*63d4c081SLuben Tuikov 863*63d4c081SLuben Tuikov return res < 0 ? res : 0; 864*63d4c081SLuben Tuikov } 865