2019-05-30 11:01:26 -04:00
|
|
|
|
/*
|
|
|
|
|
* Copyright 2019 Advanced Micro Devices, Inc.
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
|
|
|
* to deal in the Software without restriction, including without limitation
|
|
|
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
|
|
|
*
|
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
|
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
|
|
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
|
|
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
|
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "amdgpu_ras_eeprom.h"
|
|
|
|
|
#include "amdgpu.h"
|
|
|
|
|
#include "amdgpu_ras.h"
|
|
|
|
|
#include <linux/bits.h>
|
2020-02-25 20:25:17 +08:00
|
|
|
|
#include "atom.h"
|
2021-01-21 15:13:05 -05:00
|
|
|
|
#include "amdgpu_eeprom.h"
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
2021-02-02 11:26:13 -05:00
|
|
|
|
#define EEPROM_I2C_MADDR_VEGA20 0x0
|
|
|
|
|
#define EEPROM_I2C_MADDR_ARCTURUS 0x40000
|
|
|
|
|
#define EEPROM_I2C_MADDR_ARCTURUS_D342 0x0
|
|
|
|
|
#define EEPROM_I2C_MADDR_SIENNA_CICHLID 0x0
|
|
|
|
|
#define EEPROM_I2C_MADDR_ALDEBARAN 0x0
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The 2 macros bellow represent the actual size in bytes that
|
|
|
|
|
* those entities occupy in the EEPROM memory.
|
2021-02-04 14:15:01 -05:00
|
|
|
|
* RAS_TABLE_RECORD_SIZE is different than sizeof(eeprom_table_record) which
|
2019-05-30 11:01:26 -04:00
|
|
|
|
* uses uint64 to store 6b fields such as retired_page.
|
|
|
|
|
*/
|
2021-02-04 14:15:01 -05:00
|
|
|
|
#define RAS_TABLE_HEADER_SIZE 20
|
|
|
|
|
#define RAS_TABLE_RECORD_SIZE 24
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
|
|
|
|
/* Table hdr is 'AMDR' */
|
2021-02-04 14:15:01 -05:00
|
|
|
|
#define RAS_TABLE_HDR_VAL 0x414d4452
|
|
|
|
|
#define RAS_TABLE_VER 0x00010000
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
2020-07-23 15:35:53 +08:00
|
|
|
|
/* Bad GPU tag ‘BADG’ */
|
2021-02-04 14:15:01 -05:00
|
|
|
|
#define RAS_TABLE_HDR_BAD 0x42414447
|
2020-07-23 15:35:53 +08:00
|
|
|
|
|
2021-02-04 14:15:01 -05:00
|
|
|
|
/* Assume 2-Mbit size EEPROM and take up the whole space. */
|
|
|
|
|
#define RAS_TBL_SIZE_BYTES (256 * 1024)
|
|
|
|
|
#define RAS_HDR_START 0
|
|
|
|
|
#define RAS_RECORD_START (RAS_HDR_START + RAS_TABLE_HEADER_SIZE)
|
drm/amdgpu: Use explicit cardinality for clarity
RAS_MAX_RECORD_NUM may mean the maximum record
number, as in the maximum house number on your
street, or it may mean the maximum number of
records, as in the count of records, which is also
a number. To make this distinction whether the
number is ordinal (index) or cardinal (count),
rename this macro to RAS_MAX_RECORD_COUNT.
This makes it easy to understand what it refers
to, especially when we compute quantities such as,
how many records do we have left in the table,
especially when there are so many other numbers,
quantities and numerical macros around.
Also rename the long,
amdgpu_ras_eeprom_get_record_max_length() to the
more succinct and clear,
amdgpu_ras_eeprom_max_record_count().
When computing the threshold, which also deals
with counts, i.e. "how many", use cardinal
"max_eeprom_records_count", than the quantitative
"max_eeprom_records_len".
Simplify the logic here and there, as well.
Cc: Guchun Chen <guchun.chen@amd.com>
Cc: John Clements <john.clements@amd.com>
Cc: Hawking Zhang <Hawking.Zhang@amd.com>
Cc: Alexander Deucher <Alexander.Deucher@amd.com>
Signed-off-by: Luben Tuikov <luben.tuikov@amd.com>
Acked-by: Alexander Deucher <Alexander.Deucher@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
2021-03-27 03:57:49 -04:00
|
|
|
|
#define RAS_MAX_RECORD_COUNT ((RAS_TBL_SIZE_BYTES - RAS_TABLE_HEADER_SIZE) \
|
2021-02-04 14:15:01 -05:00
|
|
|
|
/ RAS_TABLE_RECORD_SIZE)
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
|
|
|
|
#define to_amdgpu_device(x) (container_of(x, struct amdgpu_ras, eeprom_control))->adev
|
|
|
|
|
|
2020-08-03 15:52:52 +08:00
|
|
|
|
static bool __is_ras_eeprom_supported(struct amdgpu_device *adev)
|
|
|
|
|
{
|
|
|
|
|
if ((adev->asic_type == CHIP_VEGA20) ||
|
2021-01-05 14:53:14 +08:00
|
|
|
|
(adev->asic_type == CHIP_ARCTURUS) ||
|
2021-04-08 17:59:18 +08:00
|
|
|
|
(adev->asic_type == CHIP_SIENNA_CICHLID) ||
|
|
|
|
|
(adev->asic_type == CHIP_ALDEBARAN))
|
2020-08-03 15:52:52 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-25 20:25:17 +08:00
|
|
|
|
static bool __get_eeprom_i2c_addr_arct(struct amdgpu_device *adev,
|
2021-02-02 11:26:13 -05:00
|
|
|
|
struct amdgpu_ras_eeprom_control *control)
|
2020-02-25 20:25:17 +08:00
|
|
|
|
{
|
|
|
|
|
struct atom_context *atom_ctx = adev->mode_info.atom_context;
|
|
|
|
|
|
2021-02-02 11:26:13 -05:00
|
|
|
|
if (!control || !atom_ctx)
|
2020-02-25 20:25:17 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (strnstr(atom_ctx->vbios_version,
|
|
|
|
|
"D342",
|
|
|
|
|
sizeof(atom_ctx->vbios_version)))
|
2021-02-02 11:26:13 -05:00
|
|
|
|
control->i2c_address = EEPROM_I2C_MADDR_ARCTURUS_D342;
|
2020-02-25 20:25:17 +08:00
|
|
|
|
else
|
2021-02-02 11:26:13 -05:00
|
|
|
|
control->i2c_address = EEPROM_I2C_MADDR_ARCTURUS;
|
2020-02-25 20:25:17 +08:00
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool __get_eeprom_i2c_addr(struct amdgpu_device *adev,
|
2021-02-02 11:26:13 -05:00
|
|
|
|
struct amdgpu_ras_eeprom_control *control)
|
2020-02-25 20:25:17 +08:00
|
|
|
|
{
|
2021-02-02 11:26:13 -05:00
|
|
|
|
if (!control)
|
2020-02-25 20:25:17 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
switch (adev->asic_type) {
|
|
|
|
|
case CHIP_VEGA20:
|
2021-02-02 11:26:13 -05:00
|
|
|
|
control->i2c_address = EEPROM_I2C_MADDR_VEGA20;
|
2020-02-25 20:25:17 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CHIP_ARCTURUS:
|
2021-02-02 11:26:13 -05:00
|
|
|
|
return __get_eeprom_i2c_addr_arct(adev, control);
|
2020-02-25 20:25:17 +08:00
|
|
|
|
|
2021-01-05 14:53:14 +08:00
|
|
|
|
case CHIP_SIENNA_CICHLID:
|
2021-02-02 11:26:13 -05:00
|
|
|
|
control->i2c_address = EEPROM_I2C_MADDR_SIENNA_CICHLID;
|
2021-01-05 14:53:14 +08:00
|
|
|
|
break;
|
|
|
|
|
|
2021-04-08 17:59:18 +08:00
|
|
|
|
case CHIP_ALDEBARAN:
|
2021-02-02 11:26:13 -05:00
|
|
|
|
control->i2c_address = EEPROM_I2C_MADDR_ALDEBARAN;
|
2021-04-08 17:59:18 +08:00
|
|
|
|
break;
|
|
|
|
|
|
2020-02-25 20:25:17 +08:00
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 11:01:26 -04:00
|
|
|
|
static void __encode_table_header_to_buff(struct amdgpu_ras_eeprom_table_header *hdr,
|
|
|
|
|
unsigned char *buff)
|
|
|
|
|
{
|
|
|
|
|
uint32_t *pp = (uint32_t *) buff;
|
|
|
|
|
|
|
|
|
|
pp[0] = cpu_to_le32(hdr->header);
|
|
|
|
|
pp[1] = cpu_to_le32(hdr->version);
|
|
|
|
|
pp[2] = cpu_to_le32(hdr->first_rec_offset);
|
|
|
|
|
pp[3] = cpu_to_le32(hdr->tbl_size);
|
|
|
|
|
pp[4] = cpu_to_le32(hdr->checksum);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __decode_table_header_from_buff(struct amdgpu_ras_eeprom_table_header *hdr,
|
|
|
|
|
unsigned char *buff)
|
|
|
|
|
{
|
|
|
|
|
uint32_t *pp = (uint32_t *)buff;
|
|
|
|
|
|
2020-11-02 22:50:19 +05:30
|
|
|
|
hdr->header = le32_to_cpu(pp[0]);
|
|
|
|
|
hdr->version = le32_to_cpu(pp[1]);
|
2019-05-30 11:01:26 -04:00
|
|
|
|
hdr->first_rec_offset = le32_to_cpu(pp[2]);
|
2020-11-02 22:50:19 +05:30
|
|
|
|
hdr->tbl_size = le32_to_cpu(pp[3]);
|
|
|
|
|
hdr->checksum = le32_to_cpu(pp[4]);
|
2019-05-30 11:01:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 20:13:10 -04:00
|
|
|
|
static int __write_table_header(struct amdgpu_ras_eeprom_control *control,
|
|
|
|
|
unsigned char *buff)
|
2019-05-30 11:01:26 -04:00
|
|
|
|
{
|
|
|
|
|
int ret = 0;
|
2020-03-12 17:11:32 -04:00
|
|
|
|
struct amdgpu_device *adev = to_amdgpu_device(control);
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
2021-01-21 15:13:05 -05:00
|
|
|
|
__encode_table_header_to_buff(&control->tbl_hdr, buff);
|
2019-11-25 18:24:17 +08:00
|
|
|
|
|
2020-10-14 17:11:41 +08:00
|
|
|
|
/* i2c may be unstable in gpu reset */
|
|
|
|
|
down_read(&adev->reset_sem);
|
2021-03-11 17:12:32 -05:00
|
|
|
|
ret = amdgpu_eeprom_write(&adev->pm.smu_i2c,
|
|
|
|
|
control->i2c_address + RAS_HDR_START,
|
|
|
|
|
buff, RAS_TABLE_HEADER_SIZE);
|
2020-10-14 17:11:41 +08:00
|
|
|
|
up_read(&adev->reset_sem);
|
|
|
|
|
|
2019-05-30 11:01:26 -04:00
|
|
|
|
if (ret < 1)
|
|
|
|
|
DRM_ERROR("Failed to write EEPROM table header, ret:%d", ret);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 20:13:10 -04:00
|
|
|
|
static u8 __calc_hdr_byte_sum(const struct amdgpu_ras_eeprom_control *control)
|
2019-09-12 17:16:32 -04:00
|
|
|
|
{
|
|
|
|
|
int i;
|
2021-03-26 20:13:10 -04:00
|
|
|
|
u8 hdr_sum = 0;
|
|
|
|
|
u8 *p;
|
|
|
|
|
size_t sz;
|
2019-09-12 17:16:32 -04:00
|
|
|
|
|
|
|
|
|
/* Header checksum, skip checksum field in the calculation */
|
2021-03-26 20:13:10 -04:00
|
|
|
|
sz = sizeof(control->tbl_hdr) - sizeof(control->tbl_hdr.checksum);
|
|
|
|
|
p = (u8 *) &control->tbl_hdr;
|
|
|
|
|
for (i = 0; i < sz; i++, p++)
|
|
|
|
|
hdr_sum += *p;
|
2019-09-12 17:16:32 -04:00
|
|
|
|
|
2021-03-26 20:13:10 -04:00
|
|
|
|
return hdr_sum;
|
2019-09-12 17:16:32 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 20:13:10 -04:00
|
|
|
|
static u8 __calc_recs_byte_sum(const struct eeprom_table_record *record,
|
|
|
|
|
const int num)
|
2019-09-12 17:16:32 -04:00
|
|
|
|
{
|
|
|
|
|
int i, j;
|
2021-03-26 20:13:10 -04:00
|
|
|
|
u8 tbl_sum = 0;
|
|
|
|
|
|
|
|
|
|
if (!record)
|
|
|
|
|
return 0;
|
2019-09-12 17:16:32 -04:00
|
|
|
|
|
|
|
|
|
/* Records checksum */
|
|
|
|
|
for (i = 0; i < num; i++) {
|
2021-03-26 20:13:10 -04:00
|
|
|
|
u8 *p = (u8 *) &record[i];
|
2019-09-12 17:16:32 -04:00
|
|
|
|
|
2021-03-26 20:13:10 -04:00
|
|
|
|
for (j = 0; j < sizeof(*record); j++, p++)
|
|
|
|
|
tbl_sum += *p;
|
2019-09-12 17:16:32 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tbl_sum;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 20:13:10 -04:00
|
|
|
|
static inline u8
|
|
|
|
|
__calc_tbl_byte_sum(struct amdgpu_ras_eeprom_control *control,
|
|
|
|
|
struct eeprom_table_record *records, int num)
|
2019-09-12 17:16:32 -04:00
|
|
|
|
{
|
2021-03-26 20:13:10 -04:00
|
|
|
|
return __calc_hdr_byte_sum(control) +
|
|
|
|
|
__calc_recs_byte_sum(records, num);
|
2019-09-12 17:16:32 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __update_tbl_checksum(struct amdgpu_ras_eeprom_control *control,
|
2021-03-26 20:13:10 -04:00
|
|
|
|
struct eeprom_table_record *records, int num)
|
2019-09-12 17:16:32 -04:00
|
|
|
|
{
|
2021-03-26 20:13:10 -04:00
|
|
|
|
u8 v;
|
2019-09-12 17:16:32 -04:00
|
|
|
|
|
2021-03-26 20:13:10 -04:00
|
|
|
|
control->tbl_byte_sum = __calc_tbl_byte_sum(control, records, num);
|
|
|
|
|
/* Avoid 32-bit sign extension. */
|
|
|
|
|
v = -control->tbl_byte_sum;
|
|
|
|
|
control->tbl_hdr.checksum = v;
|
2019-09-12 17:16:32 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 20:13:10 -04:00
|
|
|
|
static bool __verify_tbl_checksum(struct amdgpu_ras_eeprom_control *control,
|
|
|
|
|
struct eeprom_table_record *records,
|
|
|
|
|
int num)
|
2019-09-12 17:16:32 -04:00
|
|
|
|
{
|
2021-03-26 20:13:10 -04:00
|
|
|
|
u8 result;
|
|
|
|
|
|
2019-09-12 17:16:32 -04:00
|
|
|
|
control->tbl_byte_sum = __calc_tbl_byte_sum(control, records, num);
|
|
|
|
|
|
2021-03-26 20:13:10 -04:00
|
|
|
|
result = (u8)control->tbl_hdr.checksum + control->tbl_byte_sum;
|
|
|
|
|
if (result) {
|
|
|
|
|
DRM_WARN("RAS table checksum mismatch: stored:0x%02X wants:0x%02hhX",
|
|
|
|
|
control->tbl_hdr.checksum,
|
|
|
|
|
-control->tbl_byte_sum);
|
2019-09-12 17:16:32 -04:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
2020-07-27 15:51:05 +08:00
|
|
|
|
static int amdgpu_ras_eeprom_correct_header_tag(
|
2021-03-26 20:13:10 -04:00
|
|
|
|
struct amdgpu_ras_eeprom_control *control,
|
|
|
|
|
uint32_t header)
|
2020-07-27 15:51:05 +08:00
|
|
|
|
{
|
2021-02-04 14:15:01 -05:00
|
|
|
|
unsigned char buff[RAS_TABLE_HEADER_SIZE];
|
2020-07-27 15:51:05 +08:00
|
|
|
|
struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
2021-02-04 14:15:01 -05:00
|
|
|
|
memset(buff, 0, RAS_TABLE_HEADER_SIZE);
|
2020-07-27 15:51:05 +08:00
|
|
|
|
|
|
|
|
|
mutex_lock(&control->tbl_mutex);
|
|
|
|
|
hdr->header = header;
|
2021-03-26 20:13:10 -04:00
|
|
|
|
ret = __write_table_header(control, buff);
|
2020-07-27 15:51:05 +08:00
|
|
|
|
mutex_unlock(&control->tbl_mutex);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-09 15:59:45 -04:00
|
|
|
|
int amdgpu_ras_eeprom_reset_table(struct amdgpu_ras_eeprom_control *control)
|
|
|
|
|
{
|
2021-02-04 14:15:01 -05:00
|
|
|
|
unsigned char buff[RAS_TABLE_HEADER_SIZE] = { 0 };
|
2019-09-09 15:59:45 -04:00
|
|
|
|
struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr;
|
2019-09-12 17:16:32 -04:00
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
mutex_lock(&control->tbl_mutex);
|
2019-09-09 15:59:45 -04:00
|
|
|
|
|
2021-02-04 14:15:01 -05:00
|
|
|
|
hdr->header = RAS_TABLE_HDR_VAL;
|
|
|
|
|
hdr->version = RAS_TABLE_VER;
|
|
|
|
|
hdr->first_rec_offset = RAS_RECORD_START;
|
|
|
|
|
hdr->tbl_size = RAS_TABLE_HEADER_SIZE;
|
2019-09-09 15:59:45 -04:00
|
|
|
|
|
2021-03-26 20:13:10 -04:00
|
|
|
|
__update_tbl_checksum(control, NULL, 0);
|
2021-02-04 14:15:01 -05:00
|
|
|
|
control->next_addr = RAS_RECORD_START;
|
2021-03-26 20:13:10 -04:00
|
|
|
|
ret = __write_table_header(control, buff);
|
2019-09-12 17:16:32 -04:00
|
|
|
|
|
|
|
|
|
mutex_unlock(&control->tbl_mutex);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
2019-09-09 15:59:45 -04:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-23 15:42:19 +08:00
|
|
|
|
int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control,
|
2021-02-02 11:26:13 -05:00
|
|
|
|
bool *exceed_err_limit)
|
2019-05-30 11:01:26 -04:00
|
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
struct amdgpu_device *adev = to_amdgpu_device(control);
|
2021-02-04 14:15:01 -05:00
|
|
|
|
unsigned char buff[RAS_TABLE_HEADER_SIZE] = { 0 };
|
2019-05-30 11:01:26 -04:00
|
|
|
|
struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr;
|
2020-07-27 15:51:05 +08:00
|
|
|
|
struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
2020-07-23 15:42:19 +08:00
|
|
|
|
*exceed_err_limit = false;
|
|
|
|
|
|
2020-08-03 15:52:52 +08:00
|
|
|
|
if (!__is_ras_eeprom_supported(adev))
|
|
|
|
|
return 0;
|
|
|
|
|
|
2020-03-12 17:11:32 -04:00
|
|
|
|
/* Verify i2c adapter is initialized */
|
|
|
|
|
if (!adev->pm.smu_i2c.algo)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
2021-02-02 11:26:13 -05:00
|
|
|
|
if (!__get_eeprom_i2c_addr(adev, control))
|
2020-02-25 20:25:17 +08:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
2019-05-30 11:01:26 -04:00
|
|
|
|
mutex_init(&control->tbl_mutex);
|
|
|
|
|
|
|
|
|
|
/* Read/Create table header from EEPROM address 0 */
|
2021-03-11 17:12:32 -05:00
|
|
|
|
ret = amdgpu_eeprom_read(&adev->pm.smu_i2c,
|
2021-02-04 14:15:01 -05:00
|
|
|
|
control->i2c_address + RAS_HDR_START,
|
2021-03-11 17:12:32 -05:00
|
|
|
|
buff, RAS_TABLE_HEADER_SIZE);
|
2019-05-30 11:01:26 -04:00
|
|
|
|
if (ret < 1) {
|
|
|
|
|
DRM_ERROR("Failed to read EEPROM table header, ret:%d", ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 16:40:22 -04:00
|
|
|
|
__decode_table_header_from_buff(hdr, buff);
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
2021-02-04 14:15:01 -05:00
|
|
|
|
if (hdr->header == RAS_TABLE_HDR_VAL) {
|
|
|
|
|
control->num_recs = (hdr->tbl_size - RAS_TABLE_HEADER_SIZE) /
|
|
|
|
|
RAS_TABLE_RECORD_SIZE;
|
2019-09-12 17:16:32 -04:00
|
|
|
|
control->tbl_byte_sum = __calc_hdr_byte_sum(control);
|
2021-02-04 14:15:01 -05:00
|
|
|
|
control->next_addr = RAS_RECORD_START;
|
2019-09-12 17:16:32 -04:00
|
|
|
|
|
2019-05-30 11:01:26 -04:00
|
|
|
|
DRM_DEBUG_DRIVER("Found existing EEPROM table with %d records",
|
|
|
|
|
control->num_recs);
|
|
|
|
|
|
2021-02-04 14:15:01 -05:00
|
|
|
|
} else if ((hdr->header == RAS_TABLE_HDR_BAD) &&
|
2020-07-23 15:42:19 +08:00
|
|
|
|
(amdgpu_bad_page_threshold != 0)) {
|
2020-07-27 15:51:05 +08:00
|
|
|
|
if (ras->bad_page_cnt_threshold > control->num_recs) {
|
|
|
|
|
dev_info(adev->dev, "Using one valid bigger bad page "
|
|
|
|
|
"threshold and correcting eeprom header tag.\n");
|
|
|
|
|
ret = amdgpu_ras_eeprom_correct_header_tag(control,
|
2021-02-04 14:15:01 -05:00
|
|
|
|
RAS_TABLE_HDR_VAL);
|
2020-07-27 15:51:05 +08:00
|
|
|
|
} else {
|
|
|
|
|
*exceed_err_limit = true;
|
|
|
|
|
dev_err(adev->dev, "Exceeding the bad_page_threshold parameter, "
|
2020-07-23 15:42:19 +08:00
|
|
|
|
"disabling the GPU.\n");
|
2020-07-27 15:51:05 +08:00
|
|
|
|
}
|
2019-05-30 11:01:26 -04:00
|
|
|
|
} else {
|
|
|
|
|
DRM_INFO("Creating new EEPROM table");
|
|
|
|
|
|
2019-09-09 15:59:45 -04:00
|
|
|
|
ret = amdgpu_ras_eeprom_reset_table(control);
|
2019-05-30 11:01:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-11 10:34:28 -05:00
|
|
|
|
return ret > 0 ? 0 : -EIO;
|
2019-05-30 11:01:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __encode_table_record_to_buff(struct amdgpu_ras_eeprom_control *control,
|
|
|
|
|
struct eeprom_table_record *record,
|
|
|
|
|
unsigned char *buff)
|
|
|
|
|
{
|
|
|
|
|
__le64 tmp = 0;
|
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
|
|
/* Next are all record fields according to EEPROM page spec in LE foramt */
|
|
|
|
|
buff[i++] = record->err_type;
|
|
|
|
|
|
|
|
|
|
buff[i++] = record->bank;
|
|
|
|
|
|
|
|
|
|
tmp = cpu_to_le64(record->ts);
|
|
|
|
|
memcpy(buff + i, &tmp, 8);
|
|
|
|
|
i += 8;
|
|
|
|
|
|
|
|
|
|
tmp = cpu_to_le64((record->offset & 0xffffffffffff));
|
|
|
|
|
memcpy(buff + i, &tmp, 6);
|
|
|
|
|
i += 6;
|
|
|
|
|
|
|
|
|
|
buff[i++] = record->mem_channel;
|
|
|
|
|
buff[i++] = record->mcumc_id;
|
|
|
|
|
|
|
|
|
|
tmp = cpu_to_le64((record->retired_page & 0xffffffffffff));
|
|
|
|
|
memcpy(buff + i, &tmp, 6);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __decode_table_record_from_buff(struct amdgpu_ras_eeprom_control *control,
|
|
|
|
|
struct eeprom_table_record *record,
|
|
|
|
|
unsigned char *buff)
|
|
|
|
|
{
|
|
|
|
|
__le64 tmp = 0;
|
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
|
|
/* Next are all record fields according to EEPROM page spec in LE foramt */
|
|
|
|
|
record->err_type = buff[i++];
|
|
|
|
|
|
|
|
|
|
record->bank = buff[i++];
|
|
|
|
|
|
|
|
|
|
memcpy(&tmp, buff + i, 8);
|
|
|
|
|
record->ts = le64_to_cpu(tmp);
|
|
|
|
|
i += 8;
|
|
|
|
|
|
|
|
|
|
memcpy(&tmp, buff + i, 6);
|
|
|
|
|
record->offset = (le64_to_cpu(tmp) & 0xffffffffffff);
|
|
|
|
|
i += 6;
|
|
|
|
|
|
2019-09-04 22:45:20 -04:00
|
|
|
|
record->mem_channel = buff[i++];
|
|
|
|
|
record->mcumc_id = buff[i++];
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
|
|
|
|
memcpy(&tmp, buff + i, 6);
|
|
|
|
|
record->retired_page = (le64_to_cpu(tmp) & 0xffffffffffff);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* When reaching end of EEPROM memory jump back to 0 record address
|
|
|
|
|
*/
|
|
|
|
|
static uint32_t __correct_eeprom_dest_address(uint32_t curr_address)
|
|
|
|
|
{
|
2021-02-04 14:15:01 -05:00
|
|
|
|
u32 next_address = curr_address + RAS_TABLE_RECORD_SIZE;
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
|
|
|
|
/* When all EEPROM memory used jump back to 0 address */
|
2021-02-04 14:15:01 -05:00
|
|
|
|
if (next_address >= RAS_TBL_SIZE_BYTES) {
|
2021-02-02 18:36:25 -05:00
|
|
|
|
DRM_INFO("Reached end of EEPROM memory, wrap around to 0.");
|
2021-02-04 14:15:01 -05:00
|
|
|
|
return RAS_RECORD_START;
|
2019-05-30 11:01:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return curr_address;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-26 09:17:10 +08:00
|
|
|
|
bool amdgpu_ras_eeprom_check_err_threshold(struct amdgpu_device *adev)
|
2020-07-23 16:20:02 +08:00
|
|
|
|
{
|
2021-02-26 09:17:10 +08:00
|
|
|
|
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
|
2020-07-23 16:20:02 +08:00
|
|
|
|
|
2020-08-03 15:52:52 +08:00
|
|
|
|
if (!__is_ras_eeprom_supported(adev))
|
2021-02-26 09:17:10 +08:00
|
|
|
|
return false;
|
2020-07-23 16:20:02 +08:00
|
|
|
|
|
2021-03-10 19:10:11 +08:00
|
|
|
|
/* skip check eeprom table for VEGA20 Gaming */
|
|
|
|
|
if (!con)
|
|
|
|
|
return false;
|
|
|
|
|
else
|
|
|
|
|
if (!(con->features & BIT(AMDGPU_RAS_BLOCK__UMC)))
|
|
|
|
|
return false;
|
|
|
|
|
|
2021-02-04 14:15:01 -05:00
|
|
|
|
if (con->eeprom_control.tbl_hdr.header == RAS_TABLE_HDR_BAD) {
|
2020-07-23 16:20:02 +08:00
|
|
|
|
dev_warn(adev->dev, "This GPU is in BAD status.");
|
|
|
|
|
dev_warn(adev->dev, "Please retire it or setting one bigger "
|
|
|
|
|
"threshold value when reloading driver.\n");
|
2021-02-26 09:17:10 +08:00
|
|
|
|
return true;
|
2020-07-23 16:20:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-26 09:17:10 +08:00
|
|
|
|
return false;
|
2020-07-23 16:20:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-11 11:20:15 -05:00
|
|
|
|
static int amdgpu_ras_eeprom_xfer(struct amdgpu_ras_eeprom_control *control,
|
|
|
|
|
struct eeprom_table_record *records,
|
|
|
|
|
const u32 num, bool write)
|
2019-05-30 11:01:26 -04:00
|
|
|
|
{
|
|
|
|
|
int i, ret = 0;
|
2019-09-18 11:33:19 +08:00
|
|
|
|
unsigned char *buffs, *buff;
|
|
|
|
|
struct eeprom_table_record *record;
|
2019-05-30 11:01:26 -04:00
|
|
|
|
struct amdgpu_device *adev = to_amdgpu_device(control);
|
2020-07-23 16:05:00 +08:00
|
|
|
|
struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
2020-08-03 15:52:52 +08:00
|
|
|
|
if (!__is_ras_eeprom_supported(adev))
|
2019-05-30 11:01:26 -04:00
|
|
|
|
return 0;
|
|
|
|
|
|
2021-02-04 14:15:01 -05:00
|
|
|
|
buffs = kcalloc(num, RAS_TABLE_RECORD_SIZE, GFP_KERNEL);
|
2019-05-30 11:01:26 -04:00
|
|
|
|
if (!buffs)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
mutex_lock(&control->tbl_mutex);
|
|
|
|
|
|
2020-07-23 16:05:00 +08:00
|
|
|
|
/*
|
|
|
|
|
* If saved bad pages number exceeds the bad page threshold for
|
|
|
|
|
* the whole VRAM, update table header to mark the BAD GPU tag
|
|
|
|
|
* and schedule one ras recovery after eeprom write is done,
|
|
|
|
|
* this can avoid the missing for latest records.
|
|
|
|
|
*
|
|
|
|
|
* This new header will be picked up and checked in the bootup
|
|
|
|
|
* by ras recovery, which may break bootup process to notify
|
|
|
|
|
* user this GPU is in bad state and to retire such GPU for
|
|
|
|
|
* further check.
|
|
|
|
|
*/
|
|
|
|
|
if (write && (amdgpu_bad_page_threshold != 0) &&
|
|
|
|
|
((control->num_recs + num) >= ras->bad_page_cnt_threshold)) {
|
|
|
|
|
dev_warn(adev->dev,
|
|
|
|
|
"Saved bad pages(%d) reaches threshold value(%d).\n",
|
|
|
|
|
control->num_recs + num, ras->bad_page_cnt_threshold);
|
2021-02-04 14:15:01 -05:00
|
|
|
|
control->tbl_hdr.header = RAS_TABLE_HDR_BAD;
|
2020-07-23 16:05:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 11:01:26 -04:00
|
|
|
|
/* In case of overflow just start from beginning to not lose newest records */
|
2021-02-02 18:36:25 -05:00
|
|
|
|
if (write &&
|
|
|
|
|
(control->next_addr +
|
2021-02-04 14:15:01 -05:00
|
|
|
|
RAS_TABLE_RECORD_SIZE * num >= RAS_TBL_SIZE_BYTES))
|
|
|
|
|
control->next_addr = RAS_RECORD_START;
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* TODO Currently makes EEPROM writes for each record, this creates
|
|
|
|
|
* internal fragmentation. Optimized the code to do full page write of
|
|
|
|
|
* 256b
|
|
|
|
|
*/
|
|
|
|
|
for (i = 0; i < num; i++) {
|
2021-02-04 14:15:01 -05:00
|
|
|
|
buff = &buffs[i * RAS_TABLE_RECORD_SIZE];
|
2019-09-18 11:33:19 +08:00
|
|
|
|
record = &records[i];
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
|
|
|
|
control->next_addr = __correct_eeprom_dest_address(control->next_addr);
|
|
|
|
|
|
|
|
|
|
/* EEPROM table content is stored in LE format */
|
|
|
|
|
if (write)
|
2021-01-21 15:13:05 -05:00
|
|
|
|
__encode_table_record_to_buff(control, record, buff);
|
|
|
|
|
|
|
|
|
|
/* i2c may be unstable in gpu reset */
|
|
|
|
|
down_read(&adev->reset_sem);
|
2021-02-02 11:26:13 -05:00
|
|
|
|
ret = amdgpu_eeprom_xfer(&adev->pm.smu_i2c,
|
|
|
|
|
control->i2c_address + control->next_addr,
|
2021-02-04 14:15:01 -05:00
|
|
|
|
buff, RAS_TABLE_RECORD_SIZE, !write);
|
2021-01-21 15:13:05 -05:00
|
|
|
|
up_read(&adev->reset_sem);
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
2021-01-21 15:13:05 -05:00
|
|
|
|
if (ret < 1) {
|
|
|
|
|
DRM_ERROR("Failed to process EEPROM table records, ret:%d", ret);
|
|
|
|
|
|
|
|
|
|
/* TODO Restore prev next EEPROM address ? */
|
|
|
|
|
goto free_buff;
|
|
|
|
|
}
|
2019-05-30 11:01:26 -04:00
|
|
|
|
/*
|
|
|
|
|
* The destination EEPROM address might need to be corrected to account
|
|
|
|
|
* for page or entire memory wrapping
|
|
|
|
|
*/
|
2021-02-04 14:15:01 -05:00
|
|
|
|
control->next_addr += RAS_TABLE_RECORD_SIZE;
|
2019-05-30 11:01:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!write) {
|
|
|
|
|
for (i = 0; i < num; i++) {
|
2021-02-04 14:15:01 -05:00
|
|
|
|
buff = &buffs[i * RAS_TABLE_RECORD_SIZE];
|
2019-09-18 11:33:19 +08:00
|
|
|
|
record = &records[i];
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
2021-01-21 15:13:05 -05:00
|
|
|
|
__decode_table_record_from_buff(control, record, buff);
|
2019-05-30 11:01:26 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (write) {
|
|
|
|
|
/*
|
|
|
|
|
* Update table header with size and CRC and account for table
|
|
|
|
|
* wrap around where the assumption is that we treat it as empty
|
|
|
|
|
* table
|
|
|
|
|
*
|
|
|
|
|
* TODO - Check the assumption is correct
|
|
|
|
|
*/
|
|
|
|
|
control->num_recs += num;
|
drm/amdgpu: Use explicit cardinality for clarity
RAS_MAX_RECORD_NUM may mean the maximum record
number, as in the maximum house number on your
street, or it may mean the maximum number of
records, as in the count of records, which is also
a number. To make this distinction whether the
number is ordinal (index) or cardinal (count),
rename this macro to RAS_MAX_RECORD_COUNT.
This makes it easy to understand what it refers
to, especially when we compute quantities such as,
how many records do we have left in the table,
especially when there are so many other numbers,
quantities and numerical macros around.
Also rename the long,
amdgpu_ras_eeprom_get_record_max_length() to the
more succinct and clear,
amdgpu_ras_eeprom_max_record_count().
When computing the threshold, which also deals
with counts, i.e. "how many", use cardinal
"max_eeprom_records_count", than the quantitative
"max_eeprom_records_len".
Simplify the logic here and there, as well.
Cc: Guchun Chen <guchun.chen@amd.com>
Cc: John Clements <john.clements@amd.com>
Cc: Hawking Zhang <Hawking.Zhang@amd.com>
Cc: Alexander Deucher <Alexander.Deucher@amd.com>
Signed-off-by: Luben Tuikov <luben.tuikov@amd.com>
Acked-by: Alexander Deucher <Alexander.Deucher@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
2021-03-27 03:57:49 -04:00
|
|
|
|
control->num_recs %= RAS_MAX_RECORD_COUNT;
|
2021-02-04 14:15:01 -05:00
|
|
|
|
control->tbl_hdr.tbl_size += RAS_TABLE_RECORD_SIZE * num;
|
|
|
|
|
if (control->tbl_hdr.tbl_size > RAS_TBL_SIZE_BYTES)
|
|
|
|
|
control->tbl_hdr.tbl_size = RAS_TABLE_HEADER_SIZE +
|
|
|
|
|
control->num_recs * RAS_TABLE_RECORD_SIZE;
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
2021-03-26 20:13:10 -04:00
|
|
|
|
__update_tbl_checksum(control, records, num);
|
|
|
|
|
__write_table_header(control, buffs);
|
|
|
|
|
} else if (!__verify_tbl_checksum(control, records, num)) {
|
2019-05-30 11:01:26 -04:00
|
|
|
|
DRM_WARN("EEPROM Table checksum mismatch!");
|
|
|
|
|
/* TODO Uncomment when EEPROM read/write is relliable */
|
|
|
|
|
/* ret = -EIO; */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free_buff:
|
|
|
|
|
kfree(buffs);
|
|
|
|
|
|
|
|
|
|
mutex_unlock(&control->tbl_mutex);
|
|
|
|
|
|
|
|
|
|
return ret == num ? 0 : -EIO;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-11 11:20:15 -05:00
|
|
|
|
int amdgpu_ras_eeprom_read(struct amdgpu_ras_eeprom_control *control,
|
|
|
|
|
struct eeprom_table_record *records,
|
|
|
|
|
const u32 num)
|
|
|
|
|
{
|
|
|
|
|
return amdgpu_ras_eeprom_xfer(control, records, num, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int amdgpu_ras_eeprom_write(struct amdgpu_ras_eeprom_control *control,
|
|
|
|
|
struct eeprom_table_record *records,
|
|
|
|
|
const u32 num)
|
|
|
|
|
{
|
|
|
|
|
return amdgpu_ras_eeprom_xfer(control, records, num, true);
|
|
|
|
|
}
|
|
|
|
|
|
drm/amdgpu: Use explicit cardinality for clarity
RAS_MAX_RECORD_NUM may mean the maximum record
number, as in the maximum house number on your
street, or it may mean the maximum number of
records, as in the count of records, which is also
a number. To make this distinction whether the
number is ordinal (index) or cardinal (count),
rename this macro to RAS_MAX_RECORD_COUNT.
This makes it easy to understand what it refers
to, especially when we compute quantities such as,
how many records do we have left in the table,
especially when there are so many other numbers,
quantities and numerical macros around.
Also rename the long,
amdgpu_ras_eeprom_get_record_max_length() to the
more succinct and clear,
amdgpu_ras_eeprom_max_record_count().
When computing the threshold, which also deals
with counts, i.e. "how many", use cardinal
"max_eeprom_records_count", than the quantitative
"max_eeprom_records_len".
Simplify the logic here and there, as well.
Cc: Guchun Chen <guchun.chen@amd.com>
Cc: John Clements <john.clements@amd.com>
Cc: Hawking Zhang <Hawking.Zhang@amd.com>
Cc: Alexander Deucher <Alexander.Deucher@amd.com>
Signed-off-by: Luben Tuikov <luben.tuikov@amd.com>
Acked-by: Alexander Deucher <Alexander.Deucher@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
2021-03-27 03:57:49 -04:00
|
|
|
|
inline uint32_t amdgpu_ras_eeprom_max_record_count(void)
|
2020-07-22 10:00:27 +08:00
|
|
|
|
{
|
drm/amdgpu: Use explicit cardinality for clarity
RAS_MAX_RECORD_NUM may mean the maximum record
number, as in the maximum house number on your
street, or it may mean the maximum number of
records, as in the count of records, which is also
a number. To make this distinction whether the
number is ordinal (index) or cardinal (count),
rename this macro to RAS_MAX_RECORD_COUNT.
This makes it easy to understand what it refers
to, especially when we compute quantities such as,
how many records do we have left in the table,
especially when there are so many other numbers,
quantities and numerical macros around.
Also rename the long,
amdgpu_ras_eeprom_get_record_max_length() to the
more succinct and clear,
amdgpu_ras_eeprom_max_record_count().
When computing the threshold, which also deals
with counts, i.e. "how many", use cardinal
"max_eeprom_records_count", than the quantitative
"max_eeprom_records_len".
Simplify the logic here and there, as well.
Cc: Guchun Chen <guchun.chen@amd.com>
Cc: John Clements <john.clements@amd.com>
Cc: Hawking Zhang <Hawking.Zhang@amd.com>
Cc: Alexander Deucher <Alexander.Deucher@amd.com>
Signed-off-by: Luben Tuikov <luben.tuikov@amd.com>
Acked-by: Alexander Deucher <Alexander.Deucher@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
2021-03-27 03:57:49 -04:00
|
|
|
|
return RAS_MAX_RECORD_COUNT;
|
2020-07-22 10:00:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 11:01:26 -04:00
|
|
|
|
/* Used for testing if bugs encountered */
|
|
|
|
|
#if 0
|
|
|
|
|
void amdgpu_ras_eeprom_test(struct amdgpu_ras_eeprom_control *control)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
struct eeprom_table_record *recs = kcalloc(1, sizeof(*recs), GFP_KERNEL);
|
|
|
|
|
|
|
|
|
|
if (!recs)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 1 ; i++) {
|
|
|
|
|
recs[i].address = 0xdeadbeef;
|
|
|
|
|
recs[i].retired_page = i;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-11 11:20:15 -05:00
|
|
|
|
if (!amdgpu_ras_eeprom_write(control, recs, 1)) {
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
|
|
|
|
memset(recs, 0, sizeof(*recs) * 1);
|
|
|
|
|
|
2021-02-04 14:15:01 -05:00
|
|
|
|
control->next_addr = RAS_RECORD_START;
|
2019-05-30 11:01:26 -04:00
|
|
|
|
|
2021-03-11 11:20:15 -05:00
|
|
|
|
if (!amdgpu_ras_eeprom_read(control, recs)) {
|
2019-05-30 11:01:26 -04:00
|
|
|
|
for (i = 0; i < 1; i++)
|
|
|
|
|
DRM_INFO("rec.address :0x%llx, rec.retired_page :%llu",
|
|
|
|
|
recs[i].address, recs[i].retired_page);
|
|
|
|
|
} else
|
|
|
|
|
DRM_ERROR("Failed in reading from table");
|
|
|
|
|
|
|
|
|
|
} else
|
|
|
|
|
DRM_ERROR("Failed in writing to table");
|
|
|
|
|
}
|
|
|
|
|
#endif
|