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)
55c28aa44dSLuben Tuikov #define RAS_HDR_START           0
56c28aa44dSLuben Tuikov #define RAS_RECORD_START        (RAS_HDR_START + RAS_TABLE_HEADER_SIZE)
57c28aa44dSLuben Tuikov #define RAS_MAX_RECORD_NUM      ((RAS_TBL_SIZE_BYTES - RAS_TABLE_HEADER_SIZE) \
58c28aa44dSLuben Tuikov 				 / RAS_TABLE_RECORD_SIZE)
5964f55e62SAndrey Grodzovsky 
6064f55e62SAndrey Grodzovsky #define to_amdgpu_device(x) (container_of(x, struct amdgpu_ras, eeprom_control))->adev
6164f55e62SAndrey Grodzovsky 
624bfb7428SJohn Clements static bool __is_ras_eeprom_supported(struct amdgpu_device *adev)
634bfb7428SJohn Clements {
644bfb7428SJohn Clements 	if ((adev->asic_type == CHIP_VEGA20) ||
653e7bc83eSJohn Clements 	    (adev->asic_type == CHIP_ARCTURUS) ||
6652a9df81SJohn Clements 	    (adev->asic_type == CHIP_SIENNA_CICHLID) ||
6752a9df81SJohn Clements 	    (adev->asic_type == CHIP_ALDEBARAN))
684bfb7428SJohn Clements 		return true;
694bfb7428SJohn Clements 
704bfb7428SJohn Clements 	return false;
714bfb7428SJohn Clements }
724bfb7428SJohn Clements 
73ef1caf48SJohn Clements static bool __get_eeprom_i2c_addr_arct(struct amdgpu_device *adev,
74ccdfbfecSLuben Tuikov 				       struct amdgpu_ras_eeprom_control *control)
75ef1caf48SJohn Clements {
76ef1caf48SJohn Clements 	struct atom_context *atom_ctx = adev->mode_info.atom_context;
77ef1caf48SJohn Clements 
78ccdfbfecSLuben Tuikov 	if (!control || !atom_ctx)
79ef1caf48SJohn Clements 		return false;
80ef1caf48SJohn Clements 
81ef1caf48SJohn Clements 	if (strnstr(atom_ctx->vbios_version,
82ef1caf48SJohn Clements 	            "D342",
83ef1caf48SJohn Clements 		    sizeof(atom_ctx->vbios_version)))
84ccdfbfecSLuben Tuikov 		control->i2c_address = EEPROM_I2C_MADDR_ARCTURUS_D342;
85ef1caf48SJohn Clements 	else
86ccdfbfecSLuben Tuikov 		control->i2c_address = EEPROM_I2C_MADDR_ARCTURUS;
87ef1caf48SJohn Clements 
88ef1caf48SJohn Clements 	return true;
89ef1caf48SJohn Clements }
90ef1caf48SJohn Clements 
91ef1caf48SJohn Clements static bool __get_eeprom_i2c_addr(struct amdgpu_device *adev,
92ccdfbfecSLuben Tuikov 				  struct amdgpu_ras_eeprom_control *control)
93ef1caf48SJohn Clements {
94ccdfbfecSLuben Tuikov 	if (!control)
95ef1caf48SJohn Clements 		return false;
96ef1caf48SJohn Clements 
97ef1caf48SJohn Clements 	switch (adev->asic_type) {
98ef1caf48SJohn Clements 	case CHIP_VEGA20:
99ccdfbfecSLuben Tuikov 		control->i2c_address = EEPROM_I2C_MADDR_VEGA20;
100ef1caf48SJohn Clements 		break;
101ef1caf48SJohn Clements 
102ef1caf48SJohn Clements 	case CHIP_ARCTURUS:
103ccdfbfecSLuben Tuikov 		return __get_eeprom_i2c_addr_arct(adev, control);
104ef1caf48SJohn Clements 
1053e7bc83eSJohn Clements 	case CHIP_SIENNA_CICHLID:
106ccdfbfecSLuben Tuikov 		control->i2c_address = EEPROM_I2C_MADDR_SIENNA_CICHLID;
1073e7bc83eSJohn Clements 		break;
1083e7bc83eSJohn Clements 
10952a9df81SJohn Clements 	case CHIP_ALDEBARAN:
110ccdfbfecSLuben Tuikov 		control->i2c_address = EEPROM_I2C_MADDR_ALDEBARAN;
11152a9df81SJohn Clements 		break;
11252a9df81SJohn Clements 
113ef1caf48SJohn Clements 	default:
114ef1caf48SJohn Clements 		return false;
115ef1caf48SJohn Clements 	}
116ef1caf48SJohn Clements 
117ef1caf48SJohn Clements 	return true;
118ef1caf48SJohn Clements }
119ef1caf48SJohn Clements 
12064f55e62SAndrey Grodzovsky static void __encode_table_header_to_buff(struct amdgpu_ras_eeprom_table_header *hdr,
12164f55e62SAndrey Grodzovsky 					  unsigned char *buff)
12264f55e62SAndrey Grodzovsky {
12364f55e62SAndrey Grodzovsky 	uint32_t *pp = (uint32_t *) buff;
12464f55e62SAndrey Grodzovsky 
12564f55e62SAndrey Grodzovsky 	pp[0] = cpu_to_le32(hdr->header);
12664f55e62SAndrey Grodzovsky 	pp[1] = cpu_to_le32(hdr->version);
12764f55e62SAndrey Grodzovsky 	pp[2] = cpu_to_le32(hdr->first_rec_offset);
12864f55e62SAndrey Grodzovsky 	pp[3] = cpu_to_le32(hdr->tbl_size);
12964f55e62SAndrey Grodzovsky 	pp[4] = cpu_to_le32(hdr->checksum);
13064f55e62SAndrey Grodzovsky }
13164f55e62SAndrey Grodzovsky 
13264f55e62SAndrey Grodzovsky static void __decode_table_header_from_buff(struct amdgpu_ras_eeprom_table_header *hdr,
13364f55e62SAndrey Grodzovsky 					  unsigned char *buff)
13464f55e62SAndrey Grodzovsky {
13564f55e62SAndrey Grodzovsky 	uint32_t *pp = (uint32_t *)buff;
13664f55e62SAndrey Grodzovsky 
13764f55e62SAndrey Grodzovsky 	hdr->header	      = le32_to_cpu(pp[0]);
13864f55e62SAndrey Grodzovsky 	hdr->version	      = le32_to_cpu(pp[1]);
13964f55e62SAndrey Grodzovsky 	hdr->first_rec_offset = le32_to_cpu(pp[2]);
14064f55e62SAndrey Grodzovsky 	hdr->tbl_size	      = le32_to_cpu(pp[3]);
14164f55e62SAndrey Grodzovsky 	hdr->checksum	      = le32_to_cpu(pp[4]);
14264f55e62SAndrey Grodzovsky }
14364f55e62SAndrey Grodzovsky 
14464f55e62SAndrey Grodzovsky static int __update_table_header(struct amdgpu_ras_eeprom_control *control,
14564f55e62SAndrey Grodzovsky 				 unsigned char *buff)
14664f55e62SAndrey Grodzovsky {
14764f55e62SAndrey Grodzovsky 	int ret = 0;
1489015d60cSAndrey Grodzovsky 	struct amdgpu_device *adev = to_amdgpu_device(control);
14964f55e62SAndrey Grodzovsky 
15024f55c05SAlex Deucher 	__encode_table_header_to_buff(&control->tbl_hdr, buff);
1515985ebbeSJohn Clements 
15240e7ed97SDennis Li 	/* i2c may be unstable in gpu reset */
15340e7ed97SDennis Li 	down_read(&adev->reset_sem);
15416ef7977SLuben Tuikov 	ret = amdgpu_eeprom_write(&adev->pm.smu_i2c,
155c28aa44dSLuben Tuikov 				  control->i2c_address + RAS_HDR_START,
15616ef7977SLuben Tuikov 				  buff, RAS_TABLE_HEADER_SIZE);
15740e7ed97SDennis Li 	up_read(&adev->reset_sem);
15840e7ed97SDennis Li 
15964f55e62SAndrey Grodzovsky 	if (ret < 1)
16064f55e62SAndrey Grodzovsky 		DRM_ERROR("Failed to write EEPROM table header, ret:%d", ret);
16164f55e62SAndrey Grodzovsky 
16264f55e62SAndrey Grodzovsky 	return ret;
16364f55e62SAndrey Grodzovsky }
16464f55e62SAndrey Grodzovsky 
165db338e16SAndrey Grodzovsky static uint32_t  __calc_hdr_byte_sum(struct amdgpu_ras_eeprom_control *control)
166db338e16SAndrey Grodzovsky {
167db338e16SAndrey Grodzovsky 	int i;
168db338e16SAndrey Grodzovsky 	uint32_t tbl_sum = 0;
169db338e16SAndrey Grodzovsky 
170db338e16SAndrey Grodzovsky 	/* Header checksum, skip checksum field in the calculation */
171db338e16SAndrey Grodzovsky 	for (i = 0; i < sizeof(control->tbl_hdr) - sizeof(control->tbl_hdr.checksum); i++)
172db338e16SAndrey Grodzovsky 		tbl_sum += *(((unsigned char *)&control->tbl_hdr) + i);
173db338e16SAndrey Grodzovsky 
174db338e16SAndrey Grodzovsky 	return tbl_sum;
175db338e16SAndrey Grodzovsky }
176db338e16SAndrey Grodzovsky 
177db338e16SAndrey Grodzovsky static uint32_t  __calc_recs_byte_sum(struct eeprom_table_record *records,
178db338e16SAndrey Grodzovsky 				      int num)
179db338e16SAndrey Grodzovsky {
180db338e16SAndrey Grodzovsky 	int i, j;
181db338e16SAndrey Grodzovsky 	uint32_t tbl_sum = 0;
182db338e16SAndrey Grodzovsky 
183db338e16SAndrey Grodzovsky 	/* Records checksum */
184db338e16SAndrey Grodzovsky 	for (i = 0; i < num; i++) {
185db338e16SAndrey Grodzovsky 		struct eeprom_table_record *record = &records[i];
186db338e16SAndrey Grodzovsky 
187db338e16SAndrey Grodzovsky 		for (j = 0; j < sizeof(*record); j++) {
188db338e16SAndrey Grodzovsky 			tbl_sum += *(((unsigned char *)record) + j);
189db338e16SAndrey Grodzovsky 		}
190db338e16SAndrey Grodzovsky 	}
191db338e16SAndrey Grodzovsky 
192db338e16SAndrey Grodzovsky 	return tbl_sum;
193db338e16SAndrey Grodzovsky }
194db338e16SAndrey Grodzovsky 
195db338e16SAndrey Grodzovsky static inline uint32_t  __calc_tbl_byte_sum(struct amdgpu_ras_eeprom_control *control,
196db338e16SAndrey Grodzovsky 				  struct eeprom_table_record *records, int num)
197db338e16SAndrey Grodzovsky {
198db338e16SAndrey Grodzovsky 	return __calc_hdr_byte_sum(control) + __calc_recs_byte_sum(records, num);
199db338e16SAndrey Grodzovsky }
200db338e16SAndrey Grodzovsky 
201db338e16SAndrey Grodzovsky /* Checksum = 256 -((sum of all table entries) mod 256) */
202db338e16SAndrey Grodzovsky static void __update_tbl_checksum(struct amdgpu_ras_eeprom_control *control,
203db338e16SAndrey Grodzovsky 				  struct eeprom_table_record *records, int num,
204db338e16SAndrey Grodzovsky 				  uint32_t old_hdr_byte_sum)
205db338e16SAndrey Grodzovsky {
206db338e16SAndrey Grodzovsky 	/*
207db338e16SAndrey Grodzovsky 	 * This will update the table sum with new records.
208db338e16SAndrey Grodzovsky 	 *
209db338e16SAndrey Grodzovsky 	 * TODO: What happens when the EEPROM table is to be wrapped around
210db338e16SAndrey Grodzovsky 	 * and old records from start will get overridden.
211db338e16SAndrey Grodzovsky 	 */
212db338e16SAndrey Grodzovsky 
213db338e16SAndrey Grodzovsky 	/* need to recalculate updated header byte sum */
214db338e16SAndrey Grodzovsky 	control->tbl_byte_sum -= old_hdr_byte_sum;
215db338e16SAndrey Grodzovsky 	control->tbl_byte_sum += __calc_tbl_byte_sum(control, records, num);
216db338e16SAndrey Grodzovsky 
217db338e16SAndrey Grodzovsky 	control->tbl_hdr.checksum = 256 - (control->tbl_byte_sum % 256);
218db338e16SAndrey Grodzovsky }
219db338e16SAndrey Grodzovsky 
220db338e16SAndrey Grodzovsky /* table sum mod 256 + checksum must equals 256 */
221db338e16SAndrey Grodzovsky static bool __validate_tbl_checksum(struct amdgpu_ras_eeprom_control *control,
222db338e16SAndrey Grodzovsky 			    struct eeprom_table_record *records, int num)
223db338e16SAndrey Grodzovsky {
224db338e16SAndrey Grodzovsky 	control->tbl_byte_sum = __calc_tbl_byte_sum(control, records, num);
225db338e16SAndrey Grodzovsky 
226db338e16SAndrey Grodzovsky 	if (control->tbl_hdr.checksum + (control->tbl_byte_sum % 256) != 256) {
227db338e16SAndrey Grodzovsky 		DRM_WARN("Checksum mismatch, checksum: %u ", control->tbl_hdr.checksum);
228db338e16SAndrey Grodzovsky 		return false;
229db338e16SAndrey Grodzovsky 	}
230db338e16SAndrey Grodzovsky 
231db338e16SAndrey Grodzovsky 	return true;
232db338e16SAndrey Grodzovsky }
23364f55e62SAndrey Grodzovsky 
2349b856defSGuchun Chen static int amdgpu_ras_eeprom_correct_header_tag(
2359b856defSGuchun Chen 				struct amdgpu_ras_eeprom_control *control,
2369b856defSGuchun Chen 				uint32_t header)
2379b856defSGuchun Chen {
238c28aa44dSLuben Tuikov 	unsigned char buff[RAS_TABLE_HEADER_SIZE];
2399b856defSGuchun Chen 	struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr;
2409b856defSGuchun Chen 	int ret = 0;
2419b856defSGuchun Chen 
242c28aa44dSLuben Tuikov 	memset(buff, 0, RAS_TABLE_HEADER_SIZE);
2439b856defSGuchun Chen 
2449b856defSGuchun Chen 	mutex_lock(&control->tbl_mutex);
2459b856defSGuchun Chen 	hdr->header = header;
2469b856defSGuchun Chen 	ret = __update_table_header(control, buff);
2479b856defSGuchun Chen 	mutex_unlock(&control->tbl_mutex);
2489b856defSGuchun Chen 
2499b856defSGuchun Chen 	return ret;
2509b856defSGuchun Chen }
2519b856defSGuchun Chen 
252d01b400bSAndrey Grodzovsky int amdgpu_ras_eeprom_reset_table(struct amdgpu_ras_eeprom_control *control)
253d01b400bSAndrey Grodzovsky {
254c28aa44dSLuben Tuikov 	unsigned char buff[RAS_TABLE_HEADER_SIZE] = { 0 };
255d01b400bSAndrey Grodzovsky 	struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr;
256db338e16SAndrey Grodzovsky 	int ret = 0;
257db338e16SAndrey Grodzovsky 
258db338e16SAndrey Grodzovsky 	mutex_lock(&control->tbl_mutex);
259d01b400bSAndrey Grodzovsky 
260c28aa44dSLuben Tuikov 	hdr->header = RAS_TABLE_HDR_VAL;
261c28aa44dSLuben Tuikov 	hdr->version = RAS_TABLE_VER;
262c28aa44dSLuben Tuikov 	hdr->first_rec_offset = RAS_RECORD_START;
263c28aa44dSLuben Tuikov 	hdr->tbl_size = RAS_TABLE_HEADER_SIZE;
264d01b400bSAndrey Grodzovsky 
265db338e16SAndrey Grodzovsky 	control->tbl_byte_sum = 0;
266db338e16SAndrey Grodzovsky 	__update_tbl_checksum(control, NULL, 0, 0);
267c28aa44dSLuben Tuikov 	control->next_addr = RAS_RECORD_START;
268db338e16SAndrey Grodzovsky 
269db338e16SAndrey Grodzovsky 	ret = __update_table_header(control, buff);
270db338e16SAndrey Grodzovsky 
271db338e16SAndrey Grodzovsky 	mutex_unlock(&control->tbl_mutex);
272db338e16SAndrey Grodzovsky 
273db338e16SAndrey Grodzovsky 	return ret;
274db338e16SAndrey Grodzovsky 
275d01b400bSAndrey Grodzovsky }
276d01b400bSAndrey Grodzovsky 
277b82e65a9SGuchun Chen int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control,
278b82e65a9SGuchun Chen 			   bool *exceed_err_limit)
27964f55e62SAndrey Grodzovsky {
28064f55e62SAndrey Grodzovsky 	int ret = 0;
28164f55e62SAndrey Grodzovsky 	struct amdgpu_device *adev = to_amdgpu_device(control);
282c28aa44dSLuben Tuikov 	unsigned char buff[RAS_TABLE_HEADER_SIZE] = { 0 };
28364f55e62SAndrey Grodzovsky 	struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr;
2849b856defSGuchun Chen 	struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
28564f55e62SAndrey Grodzovsky 
286b82e65a9SGuchun Chen 	*exceed_err_limit = false;
287b82e65a9SGuchun Chen 
2884bfb7428SJohn Clements 	if (!__is_ras_eeprom_supported(adev))
2894bfb7428SJohn Clements 		return 0;
2904bfb7428SJohn Clements 
2919015d60cSAndrey Grodzovsky 	/* Verify i2c adapter is initialized */
2929015d60cSAndrey Grodzovsky 	if (!adev->pm.smu_i2c.algo)
2939015d60cSAndrey Grodzovsky 		return -ENOENT;
2949015d60cSAndrey Grodzovsky 
295ccdfbfecSLuben Tuikov 	if (!__get_eeprom_i2c_addr(adev, control))
296ef1caf48SJohn Clements 		return -EINVAL;
297ef1caf48SJohn Clements 
29864f55e62SAndrey Grodzovsky 	mutex_init(&control->tbl_mutex);
29964f55e62SAndrey Grodzovsky 
30064f55e62SAndrey Grodzovsky 	/* Read/Create table header from EEPROM address 0 */
30116ef7977SLuben Tuikov 	ret = amdgpu_eeprom_read(&adev->pm.smu_i2c,
302c28aa44dSLuben Tuikov 				 control->i2c_address + RAS_HDR_START,
30316ef7977SLuben Tuikov 				 buff, RAS_TABLE_HEADER_SIZE);
30464f55e62SAndrey Grodzovsky 	if (ret < 1) {
30564f55e62SAndrey Grodzovsky 		DRM_ERROR("Failed to read EEPROM table header, ret:%d", ret);
30664f55e62SAndrey Grodzovsky 		return ret;
30764f55e62SAndrey Grodzovsky 	}
30864f55e62SAndrey Grodzovsky 
30964f55e62SAndrey Grodzovsky 	__decode_table_header_from_buff(hdr, &buff[2]);
31064f55e62SAndrey Grodzovsky 
311c28aa44dSLuben Tuikov 	if (hdr->header == RAS_TABLE_HDR_VAL) {
312c28aa44dSLuben Tuikov 		control->num_recs = (hdr->tbl_size - RAS_TABLE_HEADER_SIZE) /
313c28aa44dSLuben Tuikov 				    RAS_TABLE_RECORD_SIZE;
314db338e16SAndrey Grodzovsky 		control->tbl_byte_sum = __calc_hdr_byte_sum(control);
315c28aa44dSLuben Tuikov 		control->next_addr = RAS_RECORD_START;
316db338e16SAndrey Grodzovsky 
31764f55e62SAndrey Grodzovsky 		DRM_DEBUG_DRIVER("Found existing EEPROM table with %d records",
31864f55e62SAndrey Grodzovsky 				 control->num_recs);
31964f55e62SAndrey Grodzovsky 
320c28aa44dSLuben Tuikov 	} else if ((hdr->header == RAS_TABLE_HDR_BAD) &&
321b82e65a9SGuchun Chen 			(amdgpu_bad_page_threshold != 0)) {
3229b856defSGuchun Chen 		if (ras->bad_page_cnt_threshold > control->num_recs) {
3239b856defSGuchun Chen 			dev_info(adev->dev, "Using one valid bigger bad page "
3249b856defSGuchun Chen 				"threshold and correcting eeprom header tag.\n");
3259b856defSGuchun Chen 			ret = amdgpu_ras_eeprom_correct_header_tag(control,
326c28aa44dSLuben Tuikov 							RAS_TABLE_HDR_VAL);
3279b856defSGuchun Chen 		} else {
328b82e65a9SGuchun Chen 			*exceed_err_limit = true;
329b82e65a9SGuchun Chen 			dev_err(adev->dev, "Exceeding the bad_page_threshold parameter, "
330b82e65a9SGuchun Chen 				"disabling the GPU.\n");
3319b856defSGuchun Chen 		}
33264f55e62SAndrey Grodzovsky 	} else {
33364f55e62SAndrey Grodzovsky 		DRM_INFO("Creating new EEPROM table");
33464f55e62SAndrey Grodzovsky 
335d01b400bSAndrey Grodzovsky 		ret = amdgpu_ras_eeprom_reset_table(control);
33664f55e62SAndrey Grodzovsky 	}
33764f55e62SAndrey Grodzovsky 
338*cf696091SLuben Tuikov 	return ret > 0 ? 0 : -EIO;
33964f55e62SAndrey Grodzovsky }
34064f55e62SAndrey Grodzovsky 
34164f55e62SAndrey Grodzovsky static void __encode_table_record_to_buff(struct amdgpu_ras_eeprom_control *control,
34264f55e62SAndrey Grodzovsky 					  struct eeprom_table_record *record,
34364f55e62SAndrey Grodzovsky 					  unsigned char *buff)
34464f55e62SAndrey Grodzovsky {
34564f55e62SAndrey Grodzovsky 	__le64 tmp = 0;
34664f55e62SAndrey Grodzovsky 	int i = 0;
34764f55e62SAndrey Grodzovsky 
34864f55e62SAndrey Grodzovsky 	/* Next are all record fields according to EEPROM page spec in LE foramt */
34964f55e62SAndrey Grodzovsky 	buff[i++] = record->err_type;
35064f55e62SAndrey Grodzovsky 
35164f55e62SAndrey Grodzovsky 	buff[i++] = record->bank;
35264f55e62SAndrey Grodzovsky 
35364f55e62SAndrey Grodzovsky 	tmp = cpu_to_le64(record->ts);
35464f55e62SAndrey Grodzovsky 	memcpy(buff + i, &tmp, 8);
35564f55e62SAndrey Grodzovsky 	i += 8;
35664f55e62SAndrey Grodzovsky 
35764f55e62SAndrey Grodzovsky 	tmp = cpu_to_le64((record->offset & 0xffffffffffff));
35864f55e62SAndrey Grodzovsky 	memcpy(buff + i, &tmp, 6);
35964f55e62SAndrey Grodzovsky 	i += 6;
36064f55e62SAndrey Grodzovsky 
36164f55e62SAndrey Grodzovsky 	buff[i++] = record->mem_channel;
36264f55e62SAndrey Grodzovsky 	buff[i++] = record->mcumc_id;
36364f55e62SAndrey Grodzovsky 
36464f55e62SAndrey Grodzovsky 	tmp = cpu_to_le64((record->retired_page & 0xffffffffffff));
36564f55e62SAndrey Grodzovsky 	memcpy(buff + i, &tmp, 6);
36664f55e62SAndrey Grodzovsky }
36764f55e62SAndrey Grodzovsky 
36864f55e62SAndrey Grodzovsky static void __decode_table_record_from_buff(struct amdgpu_ras_eeprom_control *control,
36964f55e62SAndrey Grodzovsky 					    struct eeprom_table_record *record,
37064f55e62SAndrey Grodzovsky 					    unsigned char *buff)
37164f55e62SAndrey Grodzovsky {
37264f55e62SAndrey Grodzovsky 	__le64 tmp = 0;
37364f55e62SAndrey Grodzovsky 	int i =  0;
37464f55e62SAndrey Grodzovsky 
37564f55e62SAndrey Grodzovsky 	/* Next are all record fields according to EEPROM page spec in LE foramt */
37664f55e62SAndrey Grodzovsky 	record->err_type = buff[i++];
37764f55e62SAndrey Grodzovsky 
37864f55e62SAndrey Grodzovsky 	record->bank = buff[i++];
37964f55e62SAndrey Grodzovsky 
38064f55e62SAndrey Grodzovsky 	memcpy(&tmp, buff + i, 8);
38164f55e62SAndrey Grodzovsky 	record->ts = le64_to_cpu(tmp);
38264f55e62SAndrey Grodzovsky 	i += 8;
38364f55e62SAndrey Grodzovsky 
38464f55e62SAndrey Grodzovsky 	memcpy(&tmp, buff + i, 6);
38564f55e62SAndrey Grodzovsky 	record->offset = (le64_to_cpu(tmp) & 0xffffffffffff);
38664f55e62SAndrey Grodzovsky 	i += 6;
38764f55e62SAndrey Grodzovsky 
3884bc22340SAndrey Grodzovsky 	record->mem_channel = buff[i++];
3894bc22340SAndrey Grodzovsky 	record->mcumc_id = buff[i++];
39064f55e62SAndrey Grodzovsky 
39164f55e62SAndrey Grodzovsky 	memcpy(&tmp, buff + i,  6);
39264f55e62SAndrey Grodzovsky 	record->retired_page = (le64_to_cpu(tmp) & 0xffffffffffff);
39364f55e62SAndrey Grodzovsky }
39464f55e62SAndrey Grodzovsky 
39564f55e62SAndrey Grodzovsky /*
39664f55e62SAndrey Grodzovsky  * When reaching end of EEPROM memory jump back to 0 record address
39764f55e62SAndrey Grodzovsky  */
39864f55e62SAndrey Grodzovsky static uint32_t __correct_eeprom_dest_address(uint32_t curr_address)
39964f55e62SAndrey Grodzovsky {
400c28aa44dSLuben Tuikov 	u32 next_address = curr_address + RAS_TABLE_RECORD_SIZE;
40164f55e62SAndrey Grodzovsky 
40264f55e62SAndrey Grodzovsky 	/* When all EEPROM memory used jump back to 0 address */
403c28aa44dSLuben Tuikov 	if (next_address >= RAS_TBL_SIZE_BYTES) {
404edb63a53SLuben Tuikov 		DRM_INFO("Reached end of EEPROM memory, wrap around to 0.");
405c28aa44dSLuben Tuikov 		return RAS_RECORD_START;
40664f55e62SAndrey Grodzovsky 	}
40764f55e62SAndrey Grodzovsky 
40864f55e62SAndrey Grodzovsky 	return curr_address;
40964f55e62SAndrey Grodzovsky }
41064f55e62SAndrey Grodzovsky 
41111003c68SDennis Li bool amdgpu_ras_eeprom_check_err_threshold(struct amdgpu_device *adev)
412e8fbaf03SGuchun Chen {
41311003c68SDennis Li 	struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
414e8fbaf03SGuchun Chen 
4154bfb7428SJohn Clements 	if (!__is_ras_eeprom_supported(adev))
41611003c68SDennis Li 		return false;
4174bfb7428SJohn Clements 
418970fd197SStanley.Yang 	/* skip check eeprom table for VEGA20 Gaming */
419970fd197SStanley.Yang 	if (!con)
420970fd197SStanley.Yang 		return false;
421970fd197SStanley.Yang 	else
422970fd197SStanley.Yang 		if (!(con->features & BIT(AMDGPU_RAS_BLOCK__UMC)))
423970fd197SStanley.Yang 			return false;
424970fd197SStanley.Yang 
425c28aa44dSLuben Tuikov 	if (con->eeprom_control.tbl_hdr.header == RAS_TABLE_HDR_BAD) {
426e8fbaf03SGuchun Chen 		dev_warn(adev->dev, "This GPU is in BAD status.");
427e8fbaf03SGuchun Chen 		dev_warn(adev->dev, "Please retire it or setting one bigger "
428e8fbaf03SGuchun Chen 				"threshold value when reloading driver.\n");
42911003c68SDennis Li 		return true;
430e8fbaf03SGuchun Chen 	}
431e8fbaf03SGuchun Chen 
43211003c68SDennis Li 	return false;
433e8fbaf03SGuchun Chen }
434e8fbaf03SGuchun Chen 
4351fab841fSLuben Tuikov static int amdgpu_ras_eeprom_xfer(struct amdgpu_ras_eeprom_control *control,
43664f55e62SAndrey Grodzovsky 				  struct eeprom_table_record *records,
437a4399657SLuben Tuikov 				  const u32 num, bool write)
43864f55e62SAndrey Grodzovsky {
43964f55e62SAndrey Grodzovsky 	int i, ret = 0;
4405222d261SGuchun Chen 	unsigned char *buffs, *buff;
4415222d261SGuchun Chen 	struct eeprom_table_record *record;
44264f55e62SAndrey Grodzovsky 	struct amdgpu_device *adev = to_amdgpu_device(control);
4439c06f91fSGuchun Chen 	struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
44464f55e62SAndrey Grodzovsky 
4454bfb7428SJohn Clements 	if (!__is_ras_eeprom_supported(adev))
44664f55e62SAndrey Grodzovsky 		return 0;
44764f55e62SAndrey Grodzovsky 
448c28aa44dSLuben Tuikov 	buffs = kcalloc(num, RAS_TABLE_RECORD_SIZE, GFP_KERNEL);
44964f55e62SAndrey Grodzovsky 	if (!buffs)
45064f55e62SAndrey Grodzovsky 		return -ENOMEM;
45164f55e62SAndrey Grodzovsky 
45264f55e62SAndrey Grodzovsky 	mutex_lock(&control->tbl_mutex);
45364f55e62SAndrey Grodzovsky 
4549c06f91fSGuchun Chen 	/*
4559c06f91fSGuchun Chen 	 * If saved bad pages number exceeds the bad page threshold for
4569c06f91fSGuchun Chen 	 * the whole VRAM, update table header to mark the BAD GPU tag
4579c06f91fSGuchun Chen 	 * and schedule one ras recovery after eeprom write is done,
4589c06f91fSGuchun Chen 	 * this can avoid the missing for latest records.
4599c06f91fSGuchun Chen 	 *
4609c06f91fSGuchun Chen 	 * This new header will be picked up and checked in the bootup
4619c06f91fSGuchun Chen 	 * by ras recovery, which may break bootup process to notify
4629c06f91fSGuchun Chen 	 * user this GPU is in bad state and to retire such GPU for
4639c06f91fSGuchun Chen 	 * further check.
4649c06f91fSGuchun Chen 	 */
4659c06f91fSGuchun Chen 	if (write && (amdgpu_bad_page_threshold != 0) &&
4669c06f91fSGuchun Chen 		((control->num_recs + num) >= ras->bad_page_cnt_threshold)) {
4679c06f91fSGuchun Chen 		dev_warn(adev->dev,
4689c06f91fSGuchun Chen 			"Saved bad pages(%d) reaches threshold value(%d).\n",
4699c06f91fSGuchun Chen 			control->num_recs + num, ras->bad_page_cnt_threshold);
470c28aa44dSLuben Tuikov 		control->tbl_hdr.header = RAS_TABLE_HDR_BAD;
4719c06f91fSGuchun Chen 	}
4729c06f91fSGuchun Chen 
47364f55e62SAndrey Grodzovsky 	/* In case of overflow just start from beginning to not lose newest records */
474edb63a53SLuben Tuikov 	if (write &&
475edb63a53SLuben Tuikov 	    (control->next_addr +
476c28aa44dSLuben Tuikov 	     RAS_TABLE_RECORD_SIZE * num >= RAS_TBL_SIZE_BYTES))
477c28aa44dSLuben Tuikov 		control->next_addr = RAS_RECORD_START;
47864f55e62SAndrey Grodzovsky 
47964f55e62SAndrey Grodzovsky 	/*
48064f55e62SAndrey Grodzovsky 	 * TODO Currently makes EEPROM writes for each record, this creates
48164f55e62SAndrey Grodzovsky 	 * internal fragmentation. Optimized the code to do full page write of
48264f55e62SAndrey Grodzovsky 	 * 256b
48364f55e62SAndrey Grodzovsky 	 */
48464f55e62SAndrey Grodzovsky 	for (i = 0; i < num; i++) {
485c28aa44dSLuben Tuikov 		buff = &buffs[i * RAS_TABLE_RECORD_SIZE];
4865222d261SGuchun Chen 		record = &records[i];
48764f55e62SAndrey Grodzovsky 
48864f55e62SAndrey Grodzovsky 		control->next_addr = __correct_eeprom_dest_address(control->next_addr);
48964f55e62SAndrey Grodzovsky 
49064f55e62SAndrey Grodzovsky 		/* EEPROM table content is stored in LE format */
49164f55e62SAndrey Grodzovsky 		if (write)
49224f55c05SAlex Deucher 			__encode_table_record_to_buff(control, record, buff);
49364f55e62SAndrey Grodzovsky 
49424f55c05SAlex Deucher 		/* i2c may be unstable in gpu reset */
49524f55c05SAlex Deucher 		down_read(&adev->reset_sem);
496ccdfbfecSLuben Tuikov 		ret = amdgpu_eeprom_xfer(&adev->pm.smu_i2c,
497ccdfbfecSLuben Tuikov 					 control->i2c_address + control->next_addr,
498c28aa44dSLuben Tuikov 					 buff, RAS_TABLE_RECORD_SIZE, !write);
49924f55c05SAlex Deucher 		up_read(&adev->reset_sem);
50024f55c05SAlex Deucher 
50124f55c05SAlex Deucher 		if (ret < 1) {
50224f55c05SAlex Deucher 			DRM_ERROR("Failed to process EEPROM table records, ret:%d", ret);
50324f55c05SAlex Deucher 
50424f55c05SAlex Deucher 			/* TODO Restore prev next EEPROM address ? */
50524f55c05SAlex Deucher 			goto free_buff;
50624f55c05SAlex Deucher 		}
50764f55e62SAndrey Grodzovsky 		/*
50864f55e62SAndrey Grodzovsky 		 * The destination EEPROM address might need to be corrected to account
50964f55e62SAndrey Grodzovsky 		 * for page or entire memory wrapping
51064f55e62SAndrey Grodzovsky 		 */
511c28aa44dSLuben Tuikov 		control->next_addr += RAS_TABLE_RECORD_SIZE;
51264f55e62SAndrey Grodzovsky 	}
51364f55e62SAndrey Grodzovsky 
51464f55e62SAndrey Grodzovsky 	if (!write) {
51564f55e62SAndrey Grodzovsky 		for (i = 0; i < num; i++) {
516c28aa44dSLuben Tuikov 			buff = &buffs[i * RAS_TABLE_RECORD_SIZE];
5175222d261SGuchun Chen 			record = &records[i];
51864f55e62SAndrey Grodzovsky 
51924f55c05SAlex Deucher 			__decode_table_record_from_buff(control, record, buff);
52064f55e62SAndrey Grodzovsky 		}
52164f55e62SAndrey Grodzovsky 	}
52264f55e62SAndrey Grodzovsky 
52364f55e62SAndrey Grodzovsky 	if (write) {
52464f55e62SAndrey Grodzovsky 		uint32_t old_hdr_byte_sum = __calc_hdr_byte_sum(control);
52564f55e62SAndrey Grodzovsky 
52664f55e62SAndrey Grodzovsky 		/*
52764f55e62SAndrey Grodzovsky 		 * Update table header with size and CRC and account for table
52864f55e62SAndrey Grodzovsky 		 * wrap around where the assumption is that we treat it as empty
52964f55e62SAndrey Grodzovsky 		 * table
53064f55e62SAndrey Grodzovsky 		 *
53164f55e62SAndrey Grodzovsky 		 * TODO - Check the assumption is correct
53264f55e62SAndrey Grodzovsky 		 */
53364f55e62SAndrey Grodzovsky 		control->num_recs += num;
534c28aa44dSLuben Tuikov 		control->num_recs %= RAS_MAX_RECORD_NUM;
535c28aa44dSLuben Tuikov 		control->tbl_hdr.tbl_size += RAS_TABLE_RECORD_SIZE * num;
536c28aa44dSLuben Tuikov 		if (control->tbl_hdr.tbl_size > RAS_TBL_SIZE_BYTES)
537c28aa44dSLuben Tuikov 			control->tbl_hdr.tbl_size = RAS_TABLE_HEADER_SIZE +
538c28aa44dSLuben Tuikov 			control->num_recs * RAS_TABLE_RECORD_SIZE;
53964f55e62SAndrey Grodzovsky 
54064f55e62SAndrey Grodzovsky 		__update_tbl_checksum(control, records, num, old_hdr_byte_sum);
54164f55e62SAndrey Grodzovsky 
54264f55e62SAndrey Grodzovsky 		__update_table_header(control, buffs);
54364f55e62SAndrey Grodzovsky 	} else if (!__validate_tbl_checksum(control, records, num)) {
54464f55e62SAndrey Grodzovsky 		DRM_WARN("EEPROM Table checksum mismatch!");
54564f55e62SAndrey Grodzovsky 		/* TODO Uncomment when EEPROM read/write is relliable */
54664f55e62SAndrey Grodzovsky 		/* ret = -EIO; */
54764f55e62SAndrey Grodzovsky 	}
54864f55e62SAndrey Grodzovsky 
54964f55e62SAndrey Grodzovsky free_buff:
55064f55e62SAndrey Grodzovsky 	kfree(buffs);
55164f55e62SAndrey Grodzovsky 
55264f55e62SAndrey Grodzovsky 	mutex_unlock(&control->tbl_mutex);
55364f55e62SAndrey Grodzovsky 
55464f55e62SAndrey Grodzovsky 	return ret == num ? 0 : -EIO;
55564f55e62SAndrey Grodzovsky }
55664f55e62SAndrey Grodzovsky 
5571fab841fSLuben Tuikov int amdgpu_ras_eeprom_read(struct amdgpu_ras_eeprom_control *control,
5581fab841fSLuben Tuikov 			   struct eeprom_table_record *records,
5591fab841fSLuben Tuikov 			   const u32 num)
5601fab841fSLuben Tuikov {
5611fab841fSLuben Tuikov 	return amdgpu_ras_eeprom_xfer(control, records, num, false);
5621fab841fSLuben Tuikov }
5631fab841fSLuben Tuikov 
5641fab841fSLuben Tuikov int amdgpu_ras_eeprom_write(struct amdgpu_ras_eeprom_control *control,
5651fab841fSLuben Tuikov 			    struct eeprom_table_record *records,
5661fab841fSLuben Tuikov 			    const u32 num)
5671fab841fSLuben Tuikov {
5681fab841fSLuben Tuikov 	return amdgpu_ras_eeprom_xfer(control, records, num, true);
5691fab841fSLuben Tuikov }
5701fab841fSLuben Tuikov 
571c84d4670SGuchun Chen inline uint32_t amdgpu_ras_eeprom_get_record_max_length(void)
572c84d4670SGuchun Chen {
573c28aa44dSLuben Tuikov 	return RAS_MAX_RECORD_NUM;
574c84d4670SGuchun Chen }
575c84d4670SGuchun Chen 
57664f55e62SAndrey Grodzovsky /* Used for testing if bugs encountered */
57764f55e62SAndrey Grodzovsky #if 0
57864f55e62SAndrey Grodzovsky void amdgpu_ras_eeprom_test(struct amdgpu_ras_eeprom_control *control)
57964f55e62SAndrey Grodzovsky {
58064f55e62SAndrey Grodzovsky 	int i;
58164f55e62SAndrey Grodzovsky 	struct eeprom_table_record *recs = kcalloc(1, sizeof(*recs), GFP_KERNEL);
58264f55e62SAndrey Grodzovsky 
58364f55e62SAndrey Grodzovsky 	if (!recs)
58464f55e62SAndrey Grodzovsky 		return;
58564f55e62SAndrey Grodzovsky 
58664f55e62SAndrey Grodzovsky 	for (i = 0; i < 1 ; i++) {
58764f55e62SAndrey Grodzovsky 		recs[i].address = 0xdeadbeef;
58864f55e62SAndrey Grodzovsky 		recs[i].retired_page = i;
58964f55e62SAndrey Grodzovsky 	}
59064f55e62SAndrey Grodzovsky 
5911fab841fSLuben Tuikov 	if (!amdgpu_ras_eeprom_write(control, recs, 1)) {
59264f55e62SAndrey Grodzovsky 
59364f55e62SAndrey Grodzovsky 		memset(recs, 0, sizeof(*recs) * 1);
59464f55e62SAndrey Grodzovsky 
595c28aa44dSLuben Tuikov 		control->next_addr = RAS_RECORD_START;
59664f55e62SAndrey Grodzovsky 
5971fab841fSLuben Tuikov 		if (!amdgpu_ras_eeprom_read(control, recs)) {
59864f55e62SAndrey Grodzovsky 			for (i = 0; i < 1; i++)
59964f55e62SAndrey Grodzovsky 				DRM_INFO("rec.address :0x%llx, rec.retired_page :%llu",
60064f55e62SAndrey Grodzovsky 					 recs[i].address, recs[i].retired_page);
60164f55e62SAndrey Grodzovsky 		} else
60264f55e62SAndrey Grodzovsky 			DRM_ERROR("Failed in reading from table");
60364f55e62SAndrey Grodzovsky 
60464f55e62SAndrey Grodzovsky 	} else
60564f55e62SAndrey Grodzovsky 		DRM_ERROR("Failed in writing to table");
60664f55e62SAndrey Grodzovsky }
60764f55e62SAndrey Grodzovsky #endif
608