All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/4] Add RAS EEPROM table and I2C driver implementation.
@ 2019-08-21 20:01 Andrey Grodzovsky
       [not found] ` <1566417719-1528-1-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
  0 siblings, 1 reply; 18+ messages in thread
From: Andrey Grodzovsky @ 2019-08-21 20:01 UTC (permalink / raw)
  To: amd-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: David.Panariti-5C7GfCeVMHo, Lijo.Lazar-5C7GfCeVMHo,
	Andrey Grodzovsky, Xinhui.Pan-5C7GfCeVMHo,
	Luben.Tuikov-5C7GfCeVMHo, Kent.Russell-5C7GfCeVMHo,
	Tao.Zhou1-5C7GfCeVMHo, Alexander.Deucher-5C7GfCeVMHo,
	evan.quan-5C7GfCeVMHo, Hawking.Zhang-5C7GfCeVMHo

This patch set introduces EEPROM table to store RAS errors which rise
during run time so on next driver load those errors can be retrieved
and action taken on them (e.g. Reserve bad memory pages to disallow
their usage by the GPU).

First patch is HW independent EEPROM table manager while the next 3
are Vega 20 specific I2C controller implementation to do the actual
EEPROM read/write over I2C.

Integration of this code into RAS will follow soon.

v2:
Add improved I2C controller TX buffer filling to avoid the slave
interpreting idle data line (SDA) as valid data.

v3:
Fix records store address calculation bug in EEPROM manager.
Fix bugs in I2C driver and revet the optimization from V2 as it's superfluous.
Remove the hack from SMU interface as the SMU found and fxied their bug.

v4:
Fix CRC calcualtion bug and address some style comments  from prevoius
review.

Andrey Grodzovsky (4):
  drm/amdgpu: Add RAS EEPROM table.
  drm/amd: Import smuio_11_0 headres for EEPROM access on Vega20
  drm/amd/powerplay: Add interface to lock SMU HW I2C.
  drm/amdgpu: Vega20 SMU I2C HW engine controller.

 drivers/gpu/drm/amd/amdgpu/Makefile                |   5 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h            |   3 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c     | 483 ++++++++++++++
 drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h     |  90 +++
 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c         | 710 +++++++++++++++++++++
 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h         |  34 +
 .../include/asic_reg/smuio/smuio_11_0_0_offset.h   |  92 +++
 .../include/asic_reg/smuio/smuio_11_0_0_sh_mask.h  | 231 +++++++
 drivers/gpu/drm/amd/include/kgd_pp_interface.h     |   1 +
 drivers/gpu/drm/amd/powerplay/amd_powerplay.c      |  16 +
 drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c |  19 +
 drivers/gpu/drm/amd/powerplay/inc/hwmgr.h          |   1 +
 .../gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c   |   2 +-
 .../gpu/drm/amd/powerplay/smumgr/vega20_smumgr.h   |   2 +
 14 files changed, 1687 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
 create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
 create mode 100644 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
 create mode 100644 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h

-- 
2.7.4

_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v4 1/4] drm/amdgpu: Add RAS EEPROM table.
       [not found] ` <1566417719-1528-1-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
@ 2019-08-21 20:01   ` Andrey Grodzovsky
       [not found]     ` <1566417719-1528-2-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
  2019-08-21 20:01   ` [PATCH v4 2/4] drm/amd: Import smuio_11_0 headres for EEPROM access on Vega20 Andrey Grodzovsky
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 18+ messages in thread
From: Andrey Grodzovsky @ 2019-08-21 20:01 UTC (permalink / raw)
  To: amd-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: David.Panariti-5C7GfCeVMHo, Lijo.Lazar-5C7GfCeVMHo,
	Andrey Grodzovsky, Xinhui.Pan-5C7GfCeVMHo,
	Luben.Tuikov-5C7GfCeVMHo, Kent.Russell-5C7GfCeVMHo,
	Tao.Zhou1-5C7GfCeVMHo, Alexander.Deucher-5C7GfCeVMHo,
	evan.quan-5C7GfCeVMHo, Hawking.Zhang-5C7GfCeVMHo

Add RAS EEPROM table manager to eanble RAS errors to be stored
upon appearance and retrived on driver load.

v2: Fix some prints.

v3:
Fix checksum calculation.
Make table record and header structs packed to do correct byte value sum.
Fix record crossing EEPROM page boundry.

v4:
Fix byte sum val calculation for record - look at sizeof(record).
Fix some style comments.

Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/Makefile            |   2 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h        |   3 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c | 482 +++++++++++++++++++++++++
 drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h |  90 +++++
 4 files changed, 576 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
 create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h

diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
index 28d76bd..f016cf1 100644
--- a/drivers/gpu/drm/amd/amdgpu/Makefile
+++ b/drivers/gpu/drm/amd/amdgpu/Makefile
@@ -54,7 +54,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
 	amdgpu_gtt_mgr.o amdgpu_vram_mgr.o amdgpu_virt.o amdgpu_atomfirmware.o \
 	amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o amdgpu_ids.o \
 	amdgpu_gmc.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o amdgpu_vm_cpu.o \
-	amdgpu_vm_sdma.o amdgpu_discovery.o
+	amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o amdgpu_ras_eeprom.o
 
 amdgpu-$(CONFIG_PERF_EVENTS) += amdgpu_pmu.o
 
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
index 2765f2d..8d5bcd8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
@@ -29,6 +29,7 @@
 #include "amdgpu.h"
 #include "amdgpu_psp.h"
 #include "ta_ras_if.h"
+#include "amdgpu_ras_eeprom.h"
 
 enum amdgpu_ras_block {
 	AMDGPU_RAS_BLOCK__UMC = 0,
@@ -333,6 +334,8 @@ struct amdgpu_ras {
 	struct mutex recovery_lock;
 
 	uint32_t flags;
+
+	struct amdgpu_ras_eeprom_control eeprom_control;
 };
 
 struct ras_fs_data {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
new file mode 100644
index 0000000..bf07515
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
@@ -0,0 +1,482 @@
+/*
+ * 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>
+
+#define EEPROM_I2C_TARGET_ADDR 0xA0
+
+#define EEPROM_TABLE_HEADER_SIZE 20
+#define EEPROM_TABLE_RECORD_SIZE 24
+#define EEPROM_ADDRESS_SIZE 0x2
+
+/* Table hdr is 'AMDR' */
+#define EEPROM_TABLE_HDR_VAL 0x414d4452
+#define EEPROM_TABLE_VER 0x00010000
+
+/* Assume 2 Mbit size */
+#define EEPROM_SIZE_BYTES 256000
+#define EEPROM_PAGE__SIZE_BYTES 256
+#define EEPROM_HDR_START 0
+#define EEPROM_RECORD_START (EEPROM_HDR_START + EEPROM_TABLE_HEADER_SIZE)
+#define EEPROM_MAX_RECORD_NUM ((EEPROM_SIZE_BYTES - EEPROM_TABLE_HEADER_SIZE) / EEPROM_TABLE_RECORD_SIZE)
+#define EEPROM_ADDR_MSB_MASK GENMASK(17, 8)
+
+#define to_amdgpu_device(x) (container_of(x, struct amdgpu_ras, eeprom_control))->adev
+
+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;
+
+	hdr->header 	      = le32_to_cpu(pp[0]);
+	hdr->version 	      = le32_to_cpu(pp[1]);
+	hdr->first_rec_offset = le32_to_cpu(pp[2]);
+	hdr->tbl_size 	      = le32_to_cpu(pp[3]);
+	hdr->checksum 	      = le32_to_cpu(pp[4]);
+}
+
+static int __update_table_header(struct amdgpu_ras_eeprom_control *control,
+				 unsigned char *buff)
+{
+	int ret = 0;
+	struct i2c_msg msg = {
+			.addr	= EEPROM_I2C_TARGET_ADDR,
+			.flags	= 0,
+			.len	= EEPROM_ADDRESS_SIZE + EEPROM_TABLE_HEADER_SIZE,
+			.buf	= buff,
+	};
+
+
+	*(uint16_t *)buff = EEPROM_HDR_START;
+	__encode_table_header_to_buff(&control->tbl_hdr, buff + EEPROM_ADDRESS_SIZE);
+
+	ret = i2c_transfer(&control->eeprom_accessor, &msg, 1);
+	if (ret < 1)
+		DRM_ERROR("Failed to write EEPROM table header, ret:%d", ret);
+
+	return ret;
+}
+
+static uint32_t  __calc_hdr_byte_sum(struct amdgpu_ras_eeprom_control *control);
+
+int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
+{
+	int ret = 0;
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+	unsigned char buff[EEPROM_ADDRESS_SIZE + EEPROM_TABLE_HEADER_SIZE] = { 0 };
+	struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr;
+	struct i2c_msg msg = {
+			.addr	= EEPROM_I2C_TARGET_ADDR,
+			.flags	= I2C_M_RD,
+			.len	= EEPROM_ADDRESS_SIZE + EEPROM_TABLE_HEADER_SIZE,
+			.buf	= buff,
+	};
+
+	mutex_init(&control->tbl_mutex);
+
+	switch (adev->asic_type) {
+	case CHIP_VEGA20:
+	/*TODO Add MI-60 */
+		break;
+
+	default:
+		return 0;
+	}
+
+	if (ret) {
+		DRM_ERROR("Failed to init I2C controller, ret:%d", ret);
+		return ret;
+	}
+
+	/* Read/Create table header from EEPROM address 0 */
+	ret = i2c_transfer(&control->eeprom_accessor, &msg, 1);
+	if (ret < 1) {
+		DRM_ERROR("Failed to read EEPROM table header, ret:%d", ret);
+		return ret;
+	}
+
+	__decode_table_header_from_buff(hdr, &buff[2]);
+
+	if (hdr->header == EEPROM_TABLE_HDR_VAL) {
+		control->num_recs = (hdr->tbl_size - EEPROM_TABLE_HEADER_SIZE) /
+				    EEPROM_TABLE_RECORD_SIZE;
+		DRM_DEBUG_DRIVER("Found existing EEPROM table with %d records",
+				 control->num_recs);
+
+	} else {
+		DRM_INFO("Creating new EEPROM table");
+
+		hdr->header = EEPROM_TABLE_HDR_VAL;
+		hdr->version = EEPROM_TABLE_VER;
+		hdr->first_rec_offset = EEPROM_RECORD_START;
+		hdr->tbl_size = EEPROM_TABLE_HEADER_SIZE;
+
+		adev->psp.ras.ras->eeprom_control.tbl_byte_sum =
+				__calc_hdr_byte_sum(&adev->psp.ras.ras->eeprom_control);
+		ret = __update_table_header(control, buff);
+	}
+
+	/* Start inserting records from here */
+	adev->psp.ras.ras->eeprom_control.next_addr = EEPROM_RECORD_START;
+
+	return ret == 1 ? 0 : -EIO;
+}
+
+void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control *control)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+
+	switch (adev->asic_type) {
+	case CHIP_VEGA20:
+		/*TODO Add MI-60 */
+		break;
+
+	default:
+		return;
+	}
+}
+
+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;
+
+	buff[i++] = record->mem_channel;
+	buff[i++] = record->mcumc_id;
+
+	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
+ * When next record access will go beyond EEPROM page boundary modify bits A17/A8
+ * in I2C selector to go to next page
+ */
+static uint32_t __correct_eeprom_dest_address(uint32_t curr_address)
+{
+	uint32_t next_address = curr_address + EEPROM_TABLE_RECORD_SIZE;
+
+	/* When all EEPROM memory used jump back to 0 address */
+	if (next_address > EEPROM_SIZE_BYTES) {
+		DRM_INFO("Reached end of EEPROM memory, jumping to 0 "
+			 "and overriding old record");
+		return EEPROM_RECORD_START;
+	}
+
+	/*
+	 * To check if we overflow page boundary  compare next address with
+	 * current and see if bits 17/8 of the EEPROM address will change
+	 * If they do start from the next 256b page
+	 *
+	 * https://www.st.com/resource/en/datasheet/m24m02-dr.pdf sec. 5.1.2
+	 */
+	if ((curr_address & EEPROM_ADDR_MSB_MASK) != (next_address & EEPROM_ADDR_MSB_MASK)) {
+		DRM_DEBUG_DRIVER("Reached end of EEPROM memory page, jumpimng to next: %lx",
+				(next_address & EEPROM_ADDR_MSB_MASK));
+
+		return  (next_address & EEPROM_ADDR_MSB_MASK);
+	}
+
+	return curr_address;
+}
+
+
+static uint32_t  __calc_hdr_byte_sum(struct amdgpu_ras_eeprom_control *control)
+{
+	int i;
+	uint32_t tbl_sum = 0;
+
+	/* Header checksum, skip checksum field in the calculation */
+	for (i = 0; i < sizeof(control->tbl_hdr) - sizeof(control->tbl_hdr.checksum); i++)
+		tbl_sum += *(((unsigned char *)&control->tbl_hdr) + i);
+
+	return tbl_sum;
+}
+
+static uint32_t  __calc_recs_byte_sum(struct eeprom_table_record *records,
+				      int num)
+{
+	int i, j;
+	uint32_t tbl_sum = 0;
+
+	/* Records checksum */
+	for (i = 0; i < num; i++) {
+		struct eeprom_table_record *record = &records[i];
+
+		for (j = 0; j < sizeof(*record); j++) {
+			tbl_sum += *(((unsigned char *)record) + j);
+		}
+	}
+
+	return tbl_sum;
+}
+
+static inline uint32_t  __calc_tbl_byte_sum(struct amdgpu_ras_eeprom_control *control,
+				  struct eeprom_table_record *records, int num)
+{
+	return __calc_hdr_byte_sum(control) + __calc_recs_byte_sum(records, num);
+}
+
+/* Checksum = 256 -((sum of all table entries) mod 256) */
+static void __update_tbl_checksum(struct amdgpu_ras_eeprom_control *control,
+				  struct eeprom_table_record *records, int num,
+				  uint32_t old_hdr_byte_sum)
+{
+	/*
+	 * This will update the table sum with new records.
+	 *
+	 * TODO: What happens when the EEPROM table is to be wrapped around
+	 * and old records from start will get overridden.
+	 */
+
+	/* need to recalculate updated header byte sum */
+	control->tbl_byte_sum -= old_hdr_byte_sum;
+	control->tbl_byte_sum += __calc_tbl_byte_sum(control, records, num);
+
+	control->tbl_hdr.checksum = 256 - (control->tbl_byte_sum % 256);
+}
+
+/* table sum mod 256 + checksum must equals 256 */
+static bool __validate_tbl_checksum(struct amdgpu_ras_eeprom_control *control,
+			    struct eeprom_table_record *records, int num)
+{
+	control->tbl_byte_sum = __calc_tbl_byte_sum(control, records, num);
+
+	if (control->tbl_hdr.checksum + (control->tbl_byte_sum % 256) != 256) {
+		DRM_WARN("Checksum mismatch, checksum: %u ", control->tbl_hdr.checksum);
+		return false;
+	}
+
+	return true;
+}
+
+int amdgpu_ras_eeprom_process_recods(struct amdgpu_ras_eeprom_control *control,
+					    struct eeprom_table_record *records,
+					    bool write,
+					    int num)
+{
+	int i, ret = 0;
+	struct i2c_msg *msgs;
+	unsigned char *buffs;
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+
+	if (adev->asic_type != CHIP_VEGA20)
+		return 0;
+
+	buffs = kcalloc(num, EEPROM_ADDRESS_SIZE + EEPROM_TABLE_RECORD_SIZE,
+			 GFP_KERNEL);
+	if (!buffs)
+		return -ENOMEM;
+
+	mutex_lock(&control->tbl_mutex);
+
+	msgs = kcalloc(num, sizeof(*msgs), GFP_KERNEL);
+	if (!msgs) {
+		ret = -ENOMEM;
+		goto free_buff;
+	}
+
+	/* In case of overflow just start from beginning to not lose newest records */
+	if (write && (control->next_addr + EEPROM_TABLE_RECORD_SIZE * num > EEPROM_SIZE_BYTES))
+		control->next_addr = EEPROM_RECORD_START;
+
+
+	/*
+	 * 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++) {
+		unsigned char *buff = &buffs[i*(EEPROM_ADDRESS_SIZE + EEPROM_TABLE_RECORD_SIZE)];
+		struct eeprom_table_record *record = &records[i];
+		struct i2c_msg *msg = &msgs[i];
+
+		control->next_addr = __correct_eeprom_dest_address(control->next_addr);
+
+		/*
+		 * Update bits 16,17 of EEPROM address in I2C address by setting them
+		 * to bits 1,2 of Device address byte
+		 */
+		msg->addr = EEPROM_I2C_TARGET_ADDR |
+			       ((control->next_addr & EEPROM_ADDR_MSB_MASK) >> 15);
+		msg->flags	= write ? 0 : I2C_M_RD;
+		msg->len	= EEPROM_ADDRESS_SIZE + EEPROM_TABLE_RECORD_SIZE;
+		msg->buf	= buff;
+
+		/* Insert the EEPROM dest addess, bits 0-15 */
+		buff[0] = ((control->next_addr >> 8) & 0xff);
+		buff[1] = (control->next_addr & 0xff);
+
+		/* EEPROM table content is stored in LE format */
+		if (write)
+			__encode_table_record_to_buff(control, record, buff + EEPROM_ADDRESS_SIZE);
+
+		/*
+		 * The destination EEPROM address might need to be corrected to account
+		 * for page or entire memory wrapping
+		 */
+		control->next_addr += EEPROM_TABLE_RECORD_SIZE;
+	}
+
+	ret = i2c_transfer(&control->eeprom_accessor, msgs, num);
+	if (ret < 1) {
+		DRM_ERROR("Failed to process EEPROM table records, ret:%d", ret);
+
+		/* TODO Restore prev next EEPROM address ? */
+		goto free_msgs;
+	}
+
+
+	if (!write) {
+		for (i = 0; i < num; i++) {
+			unsigned char *buff = &buffs[i*(EEPROM_ADDRESS_SIZE + EEPROM_TABLE_RECORD_SIZE)];
+			struct eeprom_table_record *record = &records[i];
+
+			__decode_table_record_from_buff(control, record, buff + EEPROM_ADDRESS_SIZE);
+		}
+	}
+
+	if (write) {
+		uint32_t old_hdr_byte_sum = __calc_hdr_byte_sum(control);
+
+		/*
+		 * 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;
+		control->num_recs %= EEPROM_MAX_RECORD_NUM;
+		control->tbl_hdr.tbl_size += EEPROM_TABLE_RECORD_SIZE * num;
+		if (control->tbl_hdr.tbl_size > EEPROM_SIZE_BYTES)
+			control->tbl_hdr.tbl_size = EEPROM_TABLE_HEADER_SIZE +
+			control->num_recs * EEPROM_TABLE_RECORD_SIZE;
+
+		__update_tbl_checksum(control, records, num, old_hdr_byte_sum);
+
+		__update_table_header(control, buffs);
+	} else if (!__validate_tbl_checksum(control, records, num)) {
+		DRM_WARN("EEPROM Table checksum mismatch!");
+		/* TODO Uncomment when EEPROM read/write is relliable */
+		/* ret = -EIO; */
+	}
+
+free_msgs:
+	kfree(msgs);
+
+free_buff:
+	kfree(buffs);
+
+	mutex_unlock(&control->tbl_mutex);
+
+	return ret == num ? 0 : -EIO;
+}
+
+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;
+	}
+
+	if (!amdgpu_ras_eeprom_process_recods(control, recs, true, 1)) {
+
+		memset(recs, 0, sizeof(*recs) * 1);
+
+		control->next_addr = EEPROM_RECORD_START;
+
+		if (!amdgpu_ras_eeprom_process_recods(control, recs, false, 1)) {
+			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");
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
new file mode 100644
index 0000000..41f3fcb
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef _AMDGPU_RAS_EEPROM_H
+#define _AMDGPU_RAS_EEPROM_H
+
+#include <linux/i2c.h>
+
+struct amdgpu_device;
+
+enum amdgpu_ras_eeprom_err_type{
+	AMDGPU_RAS_EEPROM_ERR_PLACE_HOLDER,
+	AMDGPU_RAS_EEPROM_ERR_RECOVERABLE,
+	AMDGPU_RAS_EEPROM_ERR_NON_RECOVERABLE
+};
+
+struct amdgpu_ras_eeprom_table_header {
+	uint32_t header;
+	uint32_t version;
+	uint32_t first_rec_offset;
+	uint32_t tbl_size;
+	uint32_t checksum;
+}__attribute__((__packed__));
+
+struct amdgpu_ras_eeprom_control {
+	struct amdgpu_ras_eeprom_table_header tbl_hdr;
+	struct i2c_adapter eeprom_accessor;
+	uint32_t next_addr;
+	unsigned int num_recs;
+	struct mutex tbl_mutex;
+	bool bus_locked;
+	uint32_t tbl_byte_sum;
+};
+
+/*
+ * Represents single table record. Packed to be easily serialized into byte
+ * stream.
+ */
+struct eeprom_table_record {
+
+	union {
+		uint64_t address;
+		uint64_t offset;
+	};
+
+	uint64_t retired_page;
+	uint64_t ts;
+
+	enum amdgpu_ras_eeprom_err_type err_type;
+
+	union {
+		unsigned char bank;
+		unsigned char cu;
+	};
+
+	unsigned char mem_channel;
+	unsigned char mcumc_id;
+}__attribute__((__packed__));
+
+int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control);
+void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control *control);
+
+int amdgpu_ras_eeprom_process_recods(struct amdgpu_ras_eeprom_control *control,
+					    struct eeprom_table_record *records,
+					    bool write,
+					    int num);
+
+void amdgpu_ras_eeprom_test(struct amdgpu_ras_eeprom_control *control);
+
+#endif // _AMDGPU_RAS_EEPROM_H
-- 
2.7.4

_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v4 2/4] drm/amd: Import smuio_11_0 headres for EEPROM access on Vega20
       [not found] ` <1566417719-1528-1-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
  2019-08-21 20:01   ` [PATCH v4 1/4] drm/amdgpu: Add RAS EEPROM table Andrey Grodzovsky
@ 2019-08-21 20:01   ` Andrey Grodzovsky
       [not found]     ` <1566417719-1528-3-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
  2019-08-21 20:01   ` [PATCH v4 3/4] drm/amd/powerplay: Add interface to lock SMU HW I2C Andrey Grodzovsky
  2019-08-21 20:01   ` [PATCH v4 4/4] drm/amdgpu: Vega20 SMU I2C HW engine controller Andrey Grodzovsky
  3 siblings, 1 reply; 18+ messages in thread
From: Andrey Grodzovsky @ 2019-08-21 20:01 UTC (permalink / raw)
  To: amd-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: David.Panariti-5C7GfCeVMHo, Lijo.Lazar-5C7GfCeVMHo,
	Andrey Grodzovsky, Xinhui.Pan-5C7GfCeVMHo,
	Luben.Tuikov-5C7GfCeVMHo, Kent.Russell-5C7GfCeVMHo,
	Tao.Zhou1-5C7GfCeVMHo, Alexander.Deucher-5C7GfCeVMHo,
	evan.quan-5C7GfCeVMHo, Hawking.Zhang-5C7GfCeVMHo

v3: Merge CKSVII2C_IC regs into exsisting headers.

Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@amd.com>
---
 .../include/asic_reg/smuio/smuio_11_0_0_offset.h   |  92 ++++++++
 .../include/asic_reg/smuio/smuio_11_0_0_sh_mask.h  | 231 +++++++++++++++++++++
 2 files changed, 323 insertions(+)

diff --git a/drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_11_0_0_offset.h b/drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_11_0_0_offset.h
index 5df7048..d387605 100644
--- a/drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_11_0_0_offset.h
+++ b/drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_11_0_0_offset.h
@@ -29,6 +29,98 @@
 #define mmSMUSVI0_TEL_PLANE0_BASE_IDX                                                                  0
 #define mmSMUIO_MCM_CONFIG                                                                             0x0024
 #define mmSMUIO_MCM_CONFIG_BASE_IDX                                                                    0
+#define mmCKSVII2C_IC_CON                                                                              0x0040
+#define mmCKSVII2C_IC_CON_BASE_IDX                                                                     0
+#define mmCKSVII2C_IC_TAR                                                                              0x0041
+#define mmCKSVII2C_IC_TAR_BASE_IDX                                                                     0
+#define mmCKSVII2C_IC_SAR                                                                              0x0042
+#define mmCKSVII2C_IC_SAR_BASE_IDX                                                                     0
+#define mmCKSVII2C_IC_HS_MADDR                                                                         0x0043
+#define mmCKSVII2C_IC_HS_MADDR_BASE_IDX                                                                0
+#define mmCKSVII2C_IC_DATA_CMD                                                                         0x0044
+#define mmCKSVII2C_IC_DATA_CMD_BASE_IDX                                                                0
+#define mmCKSVII2C_IC_SS_SCL_HCNT                                                                      0x0045
+#define mmCKSVII2C_IC_SS_SCL_HCNT_BASE_IDX                                                             0
+#define mmCKSVII2C_IC_SS_SCL_LCNT                                                                      0x0046
+#define mmCKSVII2C_IC_SS_SCL_LCNT_BASE_IDX                                                             0
+#define mmCKSVII2C_IC_FS_SCL_HCNT                                                                      0x0047
+#define mmCKSVII2C_IC_FS_SCL_HCNT_BASE_IDX                                                             0
+#define mmCKSVII2C_IC_FS_SCL_LCNT                                                                      0x0048
+#define mmCKSVII2C_IC_FS_SCL_LCNT_BASE_IDX                                                             0
+#define mmCKSVII2C_IC_HS_SCL_HCNT                                                                      0x0049
+#define mmCKSVII2C_IC_HS_SCL_HCNT_BASE_IDX                                                             0
+#define mmCKSVII2C_IC_HS_SCL_LCNT                                                                      0x004a
+#define mmCKSVII2C_IC_HS_SCL_LCNT_BASE_IDX                                                             0
+#define mmCKSVII2C_IC_INTR_STAT                                                                        0x004b
+#define mmCKSVII2C_IC_INTR_STAT_BASE_IDX                                                               0
+#define mmCKSVII2C_IC_INTR_MASK                                                                        0x004c
+#define mmCKSVII2C_IC_INTR_MASK_BASE_IDX                                                               0
+#define mmCKSVII2C_IC_RAW_INTR_STAT                                                                    0x004d
+#define mmCKSVII2C_IC_RAW_INTR_STAT_BASE_IDX                                                           0
+#define mmCKSVII2C_IC_RX_TL                                                                            0x004e
+#define mmCKSVII2C_IC_RX_TL_BASE_IDX                                                                   0
+#define mmCKSVII2C_IC_TX_TL                                                                            0x004f
+#define mmCKSVII2C_IC_TX_TL_BASE_IDX                                                                   0
+#define mmCKSVII2C_IC_CLR_INTR                                                                         0x0050
+#define mmCKSVII2C_IC_CLR_INTR_BASE_IDX                                                                0
+#define mmCKSVII2C_IC_CLR_RX_UNDER                                                                     0x0051
+#define mmCKSVII2C_IC_CLR_RX_UNDER_BASE_IDX                                                            0
+#define mmCKSVII2C_IC_CLR_RX_OVER                                                                      0x0052
+#define mmCKSVII2C_IC_CLR_RX_OVER_BASE_IDX                                                             0
+#define mmCKSVII2C_IC_CLR_TX_OVER                                                                      0x0053
+#define mmCKSVII2C_IC_CLR_TX_OVER_BASE_IDX                                                             0
+#define mmCKSVII2C_IC_CLR_RD_REQ                                                                       0x0054
+#define mmCKSVII2C_IC_CLR_RD_REQ_BASE_IDX                                                              0
+#define mmCKSVII2C_IC_CLR_TX_ABRT                                                                      0x0055
+#define mmCKSVII2C_IC_CLR_TX_ABRT_BASE_IDX                                                             0
+#define mmCKSVII2C_IC_CLR_RX_DONE                                                                      0x0056
+#define mmCKSVII2C_IC_CLR_RX_DONE_BASE_IDX                                                             0
+#define mmCKSVII2C_IC_CLR_ACTIVITY                                                                     0x0057
+#define mmCKSVII2C_IC_CLR_ACTIVITY_BASE_IDX                                                            0
+#define mmCKSVII2C_IC_CLR_STOP_DET                                                                     0x0058
+#define mmCKSVII2C_IC_CLR_STOP_DET_BASE_IDX                                                            0
+#define mmCKSVII2C_IC_CLR_START_DET                                                                    0x0059
+#define mmCKSVII2C_IC_CLR_START_DET_BASE_IDX                                                           0
+#define mmCKSVII2C_IC_CLR_GEN_CALL                                                                     0x005a
+#define mmCKSVII2C_IC_CLR_GEN_CALL_BASE_IDX                                                            0
+#define mmCKSVII2C_IC_ENABLE                                                                           0x005b
+#define mmCKSVII2C_IC_ENABLE_BASE_IDX                                                                  0
+#define mmCKSVII2C_IC_STATUS                                                                           0x005c
+#define mmCKSVII2C_IC_STATUS_BASE_IDX                                                                  0
+#define mmCKSVII2C_IC_TXFLR                                                                            0x005d
+#define mmCKSVII2C_IC_TXFLR_BASE_IDX                                                                   0
+#define mmCKSVII2C_IC_RXFLR                                                                            0x005e
+#define mmCKSVII2C_IC_RXFLR_BASE_IDX                                                                   0
+#define mmCKSVII2C_IC_SDA_HOLD                                                                         0x005f
+#define mmCKSVII2C_IC_SDA_HOLD_BASE_IDX                                                                0
+#define mmCKSVII2C_IC_TX_ABRT_SOURCE                                                                   0x0060
+#define mmCKSVII2C_IC_TX_ABRT_SOURCE_BASE_IDX                                                          0
+#define mmCKSVII2C_IC_SLV_DATA_NACK_ONLY                                                               0x0061
+#define mmCKSVII2C_IC_SLV_DATA_NACK_ONLY_BASE_IDX                                                      0
+#define mmCKSVII2C_IC_DMA_CR                                                                           0x0062
+#define mmCKSVII2C_IC_DMA_CR_BASE_IDX                                                                  0
+#define mmCKSVII2C_IC_DMA_TDLR                                                                         0x0063
+#define mmCKSVII2C_IC_DMA_TDLR_BASE_IDX                                                                0
+#define mmCKSVII2C_IC_DMA_RDLR                                                                         0x0064
+#define mmCKSVII2C_IC_DMA_RDLR_BASE_IDX                                                                0
+#define mmCKSVII2C_IC_SDA_SETUP                                                                        0x0065
+#define mmCKSVII2C_IC_SDA_SETUP_BASE_IDX                                                               0
+#define mmCKSVII2C_IC_ACK_GENERAL_CALL                                                                 0x0066
+#define mmCKSVII2C_IC_ACK_GENERAL_CALL_BASE_IDX                                                        0
+#define mmCKSVII2C_IC_ENABLE_STATUS                                                                    0x0067
+#define mmCKSVII2C_IC_ENABLE_STATUS_BASE_IDX                                                           0
+#define mmCKSVII2C_IC_FS_SPKLEN                                                                        0x0068
+#define mmCKSVII2C_IC_FS_SPKLEN_BASE_IDX                                                               0
+#define mmCKSVII2C_IC_HS_SPKLEN                                                                        0x0069
+#define mmCKSVII2C_IC_HS_SPKLEN_BASE_IDX                                                               0
+#define mmCKSVII2C_IC_CLR_RESTART_DET                                                                  0x006a
+#define mmCKSVII2C_IC_CLR_RESTART_DET_BASE_IDX                                                         0
+#define mmCKSVII2C_IC_COMP_PARAM_1                                                                     0x006b
+#define mmCKSVII2C_IC_COMP_PARAM_1_BASE_IDX                                                            0
+#define mmCKSVII2C_IC_COMP_VERSION                                                                     0x006c
+#define mmCKSVII2C_IC_COMP_VERSION_BASE_IDX                                                            0
+#define mmCKSVII2C_IC_COMP_TYPE                                                                        0x006d
+#define mmCKSVII2C_IC_COMP_TYPE_BASE_IDX                                                               0
 #define mmSMUIO_MP_RESET_INTR                                                                          0x00c1
 #define mmSMUIO_MP_RESET_INTR_BASE_IDX                                                                 0
 #define mmSMUIO_SOC_HALT                                                                               0x00c2
diff --git a/drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_11_0_0_sh_mask.h b/drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_11_0_0_sh_mask.h
index 2379615..f8afa351 100644
--- a/drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_11_0_0_sh_mask.h
+++ b/drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_11_0_0_sh_mask.h
@@ -37,6 +37,237 @@
 #define SMUIO_MCM_CONFIG__PKG_TYPE_MASK                                                                       0x0000001CL
 #define SMUIO_MCM_CONFIG__SOCKET_ID_MASK                                                                      0x00000020L
 #define SMUIO_MCM_CONFIG__PKG_SUBTYPE_MASK                                                                    0x000000C0L
+//CKSVII2C_IC_CON
+#define CKSVII2C_IC_CON__IC_MASTER_MODE__SHIFT                                                                0x0
+#define CKSVII2C_IC_CON__IC_MAX_SPEED_MODE__SHIFT                                                             0x1
+#define CKSVII2C_IC_CON__IC_10BITADDR_SLAVE__SHIFT                                                            0x3
+#define CKSVII2C_IC_CON__IC_10BITADDR_MASTER__SHIFT                                                           0x4
+#define CKSVII2C_IC_CON__IC_RESTART_EN__SHIFT                                                                 0x5
+#define CKSVII2C_IC_CON__IC_SLAVE_DISABLE__SHIFT                                                              0x6
+#define CKSVII2C_IC_CON__STOP_DET_IFADDRESSED__SHIFT                                                          0x7
+#define CKSVII2C_IC_CON__TX_EMPTY_CTRL__SHIFT                                                                 0x8
+#define CKSVII2C_IC_CON__RX_FIFO_FULL_HLD_CTRL__SHIFT                                                         0x9
+#define CKSVII2C_IC_CON__IC_MASTER_MODE_MASK                                                                  0x00000001L
+#define CKSVII2C_IC_CON__IC_MAX_SPEED_MODE_MASK                                                               0x00000006L
+#define CKSVII2C_IC_CON__IC_10BITADDR_SLAVE_MASK                                                              0x00000008L
+#define CKSVII2C_IC_CON__IC_10BITADDR_MASTER_MASK                                                             0x00000010L
+#define CKSVII2C_IC_CON__IC_RESTART_EN_MASK                                                                   0x00000020L
+#define CKSVII2C_IC_CON__IC_SLAVE_DISABLE_MASK                                                                0x00000040L
+#define CKSVII2C_IC_CON__STOP_DET_IFADDRESSED_MASK                                                            0x00000080L
+#define CKSVII2C_IC_CON__TX_EMPTY_CTRL_MASK                                                                   0x00000100L
+#define CKSVII2C_IC_CON__RX_FIFO_FULL_HLD_CTRL_MASK                                                           0x00000200L
+//CKSVII2C_IC_TAR
+#define CKSVII2C_IC_TAR__IC_TAR__SHIFT                                                                        0x0
+#define CKSVII2C_IC_TAR__GC_OR_START__SHIFT                                                                   0xa
+#define CKSVII2C_IC_TAR__SPECIAL__SHIFT                                                                       0xb
+#define CKSVII2C_IC_TAR__IC_10BITADDR_MASTER__SHIFT                                                           0xc
+#define CKSVII2C_IC_TAR__IC_TAR_MASK                                                                          0x000003FFL
+#define CKSVII2C_IC_TAR__GC_OR_START_MASK                                                                     0x00000400L
+#define CKSVII2C_IC_TAR__SPECIAL_MASK                                                                         0x00000800L
+#define CKSVII2C_IC_TAR__IC_10BITADDR_MASTER_MASK                                                             0x00001000L
+//CKSVII2C_IC_SAR
+#define CKSVII2C_IC_SAR__IC_SAR__SHIFT                                                                        0x0
+#define CKSVII2C_IC_SAR__IC_SAR_MASK                                                                          0x000003FFL
+//CKSVII2C_IC_HS_MADDR
+#define CKSVII2C_IC_HS_MADDR__IC_HS_MADDR__SHIFT                                                              0x0
+#define CKSVII2C_IC_HS_MADDR__IC_HS_MADDR_MASK                                                                0x00000007L
+//CKSVII2C_IC_DATA_CMD
+#define CKSVII2C_IC_DATA_CMD__DAT__SHIFT                                                                      0x0
+#define CKSVII2C_IC_DATA_CMD__CMD__SHIFT                                                                      0x8
+#define CKSVII2C_IC_DATA_CMD__STOP__SHIFT                                                                     0x9
+#define CKSVII2C_IC_DATA_CMD__RESTART__SHIFT                                                                  0xa
+#define CKSVII2C_IC_DATA_CMD__DAT_MASK                                                                        0x000000FFL
+#define CKSVII2C_IC_DATA_CMD__CMD_MASK                                                                        0x00000100L
+#define CKSVII2C_IC_DATA_CMD__STOP_MASK                                                                       0x00000200L
+#define CKSVII2C_IC_DATA_CMD__RESTART_MASK                                                                    0x00000400L
+//CKSVII2C_IC_SS_SCL_HCNT
+#define CKSVII2C_IC_SS_SCL_HCNT__IC_SS_SCL_HCNT__SHIFT                                                        0x0
+#define CKSVII2C_IC_SS_SCL_HCNT__IC_SS_SCL_HCNT_MASK                                                          0x0000FFFFL
+//CKSVII2C_IC_SS_SCL_LCNT
+#define CKSVII2C_IC_SS_SCL_LCNT__IC_SS_SCL_LCNT__SHIFT                                                        0x0
+#define CKSVII2C_IC_SS_SCL_LCNT__IC_SS_SCL_LCNT_MASK                                                          0x0000FFFFL
+//CKSVII2C_IC_FS_SCL_HCNT
+#define CKSVII2C_IC_FS_SCL_HCNT__IC_FS_SCL_HCNT__SHIFT                                                        0x0
+#define CKSVII2C_IC_FS_SCL_HCNT__IC_FS_SCL_HCNT_MASK                                                          0x0000FFFFL
+//CKSVII2C_IC_FS_SCL_LCNT
+#define CKSVII2C_IC_FS_SCL_LCNT__IC_FS_SCL_LCNT__SHIFT                                                        0x0
+#define CKSVII2C_IC_FS_SCL_LCNT__IC_FS_SCL_LCNT_MASK                                                          0x0000FFFFL
+//CKSVII2C_IC_HS_SCL_HCNT
+#define CKSVII2C_IC_HS_SCL_HCNT__IC_HS_SCL_HCNT__SHIFT                                                        0x0
+#define CKSVII2C_IC_HS_SCL_HCNT__IC_HS_SCL_HCNT_MASK                                                          0x0000FFFFL
+//CKSVII2C_IC_HS_SCL_LCNT
+#define CKSVII2C_IC_HS_SCL_LCNT__IC_HS_SCL_LCNT__SHIFT                                                        0x0
+#define CKSVII2C_IC_HS_SCL_LCNT__IC_HS_SCL_LCNT_MASK                                                          0x0000FFFFL
+//CKSVII2C_IC_INTR_STAT
+#define CKSVII2C_IC_INTR_STAT__R_RX_UNDER__SHIFT                                                              0x0
+#define CKSVII2C_IC_INTR_STAT__R_RX_OVER__SHIFT                                                               0x1
+#define CKSVII2C_IC_INTR_STAT__R_RX_FULL__SHIFT                                                               0x2
+#define CKSVII2C_IC_INTR_STAT__R_TX_OVER__SHIFT                                                               0x3
+#define CKSVII2C_IC_INTR_STAT__R_TX_EMPTY__SHIFT                                                              0x4
+#define CKSVII2C_IC_INTR_STAT__R_RD_REQ__SHIFT                                                                0x5
+#define CKSVII2C_IC_INTR_STAT__R_TX_ABRT__SHIFT                                                               0x6
+#define CKSVII2C_IC_INTR_STAT__R_RX_DONE__SHIFT                                                               0x7
+#define CKSVII2C_IC_INTR_STAT__R_ACTIVITY__SHIFT                                                              0x8
+#define CKSVII2C_IC_INTR_STAT__R_STOP_DET__SHIFT                                                              0x9
+#define CKSVII2C_IC_INTR_STAT__R_START_DET__SHIFT                                                             0xa
+#define CKSVII2C_IC_INTR_STAT__R_GEN_CALL__SHIFT                                                              0xb
+#define CKSVII2C_IC_INTR_STAT__R_RESTART_DET__SHIFT                                                           0xc
+#define CKSVII2C_IC_INTR_STAT__R_MST_ON_HOLD__SHIFT                                                           0xd
+#define CKSVII2C_IC_INTR_STAT__R_RX_UNDER_MASK                                                                0x00000001L
+#define CKSVII2C_IC_INTR_STAT__R_RX_OVER_MASK                                                                 0x00000002L
+#define CKSVII2C_IC_INTR_STAT__R_RX_FULL_MASK                                                                 0x00000004L
+#define CKSVII2C_IC_INTR_STAT__R_TX_OVER_MASK                                                                 0x00000008L
+#define CKSVII2C_IC_INTR_STAT__R_TX_EMPTY_MASK                                                                0x00000010L
+#define CKSVII2C_IC_INTR_STAT__R_RD_REQ_MASK                                                                  0x00000020L
+#define CKSVII2C_IC_INTR_STAT__R_TX_ABRT_MASK                                                                 0x00000040L
+#define CKSVII2C_IC_INTR_STAT__R_RX_DONE_MASK                                                                 0x00000080L
+#define CKSVII2C_IC_INTR_STAT__R_ACTIVITY_MASK                                                                0x00000100L
+#define CKSVII2C_IC_INTR_STAT__R_STOP_DET_MASK                                                                0x00000200L
+#define CKSVII2C_IC_INTR_STAT__R_START_DET_MASK                                                               0x00000400L
+#define CKSVII2C_IC_INTR_STAT__R_GEN_CALL_MASK                                                                0x00000800L
+#define CKSVII2C_IC_INTR_STAT__R_RESTART_DET_MASK                                                             0x00001000L
+#define CKSVII2C_IC_INTR_STAT__R_MST_ON_HOLD_MASK                                                             0x00002000L
+//CKSVII2C_IC_INTR_MASK
+#define CKSVII2C_IC_INTR_MASK__M_RX_UNDER__SHIFT                                                              0x0
+#define CKSVII2C_IC_INTR_MASK__M_RX_OVER__SHIFT                                                               0x1
+#define CKSVII2C_IC_INTR_MASK__M_RX_FULL__SHIFT                                                               0x2
+#define CKSVII2C_IC_INTR_MASK__M_TX_OVER__SHIFT                                                               0x3
+#define CKSVII2C_IC_INTR_MASK__M_TX_EMPTY__SHIFT                                                              0x4
+#define CKSVII2C_IC_INTR_MASK__M_RD_REQ__SHIFT                                                                0x5
+#define CKSVII2C_IC_INTR_MASK__M_TX_ABRT__SHIFT                                                               0x6
+#define CKSVII2C_IC_INTR_MASK__M_RX_DONE__SHIFT                                                               0x7
+#define CKSVII2C_IC_INTR_MASK__M_ACTIVITY__SHIFT                                                              0x8
+#define CKSVII2C_IC_INTR_MASK__M_STOP_DET__SHIFT                                                              0x9
+#define CKSVII2C_IC_INTR_MASK__M_START_DET__SHIFT                                                             0xa
+#define CKSVII2C_IC_INTR_MASK__M_GEN_CALL__SHIFT                                                              0xb
+#define CKSVII2C_IC_INTR_MASK__M_RESTART_DET__SHIFT                                                           0xc
+#define CKSVII2C_IC_INTR_MASK__M_MST_ON_HOLD__SHIFT                                                           0xd
+#define CKSVII2C_IC_INTR_MASK__M_RX_UNDER_MASK                                                                0x00000001L
+#define CKSVII2C_IC_INTR_MASK__M_RX_OVER_MASK                                                                 0x00000002L
+#define CKSVII2C_IC_INTR_MASK__M_RX_FULL_MASK                                                                 0x00000004L
+#define CKSVII2C_IC_INTR_MASK__M_TX_OVER_MASK                                                                 0x00000008L
+#define CKSVII2C_IC_INTR_MASK__M_TX_EMPTY_MASK                                                                0x00000010L
+#define CKSVII2C_IC_INTR_MASK__M_RD_REQ_MASK                                                                  0x00000020L
+#define CKSVII2C_IC_INTR_MASK__M_TX_ABRT_MASK                                                                 0x00000040L
+#define CKSVII2C_IC_INTR_MASK__M_RX_DONE_MASK                                                                 0x00000080L
+#define CKSVII2C_IC_INTR_MASK__M_ACTIVITY_MASK                                                                0x00000100L
+#define CKSVII2C_IC_INTR_MASK__M_STOP_DET_MASK                                                                0x00000200L
+#define CKSVII2C_IC_INTR_MASK__M_START_DET_MASK                                                               0x00000400L
+#define CKSVII2C_IC_INTR_MASK__M_GEN_CALL_MASK                                                                0x00000800L
+#define CKSVII2C_IC_INTR_MASK__M_RESTART_DET_MASK                                                             0x00001000L
+#define CKSVII2C_IC_INTR_MASK__M_MST_ON_HOLD_MASK                                                             0x00002000L
+//CKSVII2C_IC_RAW_INTR_STAT
+#define CKSVII2C_IC_RAW_INTR_STAT__R_RX_UNDER__SHIFT                                                              0x0
+#define CKSVII2C_IC_RAW_INTR_STAT__R_RX_OVER__SHIFT                                                               0x1
+#define CKSVII2C_IC_RAW_INTR_STAT__R_RX_FULL__SHIFT                                                               0x2
+#define CKSVII2C_IC_RAW_INTR_STAT__R_TX_OVER__SHIFT                                                               0x3
+#define CKSVII2C_IC__RAW_INTR_STAT__R_TX_EMPTY__SHIFT                                                              0x4
+#define CKSVII2C_IC_RAW_INTR_STAT__R_RD_REQ__SHIFT                                                                0x5
+#define CKSVII2C_IC_RAW_INTR_STAT__R_TX_ABRT__SHIFT                                                               0x6
+#define CKSVII2C_IC_RAW_INTR_STAT__R_RX_DONE__SHIFT                                                               0x7
+#define CKSVII2C_IC_RAW_INTR_STAT__R_ACTIVITY__SHIFT                                                              0x8
+#define CKSVII2C_IC_RAW_INTR_STAT__R_STOP_DET__SHIFT                                                              0x9
+#define CKSVII2C_IC_RAW_INTR_STAT__R_START_DET__SHIFT                                                             0xa
+#define CKSVII2C_IC_RAW_INTR_STAT__R_GEN_CALL__SHIFT                                                              0xb
+#define CKSVII2C_IC_RAW_INTR_STAT__R_RESTART_DET__SHIFT                                                           0xc
+#define CKSVII2C_IC_RAW_INTR_STAT__R_MST_ON_HOLD__SHIFT                                                           0xd
+#define CKSVII2C_IC_RAW_INTR_STAT__R_RX_UNDER_MASK                                                                0x00000001L
+#define CKSVII2C_IC_RAW_INTR_STAT__R_RX_OVER_MASK                                                                 0x00000002L
+#define CKSVII2C_IC_RAW_INTR_STAT__R_RX_FULL_MASK                                                                 0x00000004L
+#define CKSVII2C_IC_RAW_INTR_STAT__R_TX_OVER_MASK                                                                 0x00000008L
+#define CKSVII2C_IC_RAW_INTR_STAT__R_TX_EMPTY_MASK                                                                0x00000010L
+#define CKSVII2C_IC_RAW_INTR_STAT__R_RD_REQ_MASK                                                                  0x00000020L
+#define CKSVII2C_IC_RAW_INTR_STAT__R_TX_ABRT_MASK                                                                 0x00000040L
+#define CKSVII2C_IC_RAW_INTR_STAT__R_RX_DONE_MASK                                                                 0x00000080L
+#define CKSVII2C_IC_RAW_INTR_STAT__R_ACTIVITY_MASK                                                                0x00000100L
+#define CKSVII2C_IC_RAW_INTR_STAT__R_STOP_DET_MASK                                                                0x00000200L
+#define CKSVII2C_IC_RAW_INTR_STAT__R_START_DET_MASK                                                               0x00000400L
+#define CKSVII2C_IC_RAW_INTR_STAT__R_GEN_CALL_MASK                                                                0x00000800L
+#define CKSVII2C_IC_RAW_INTR_STAT__R_RESTART_DET_MASK                                                             0x00001000L
+#define CKSVII2C_IC_RAW_INTR_STAT__R_MST_ON_HOLD_MASK                                                             0x00002000L
+//CKSVII2C_IC_RX_TL
+//CKSVII2C_IC_TX_TL
+//CKSVII2C_IC_CLR_INTR
+//CKSVII2C_IC_CLR_RX_UNDER
+//CKSVII2C_IC_CLR_RX_OVER
+//CKSVII2C_IC_CLR_TX_OVER
+//CKSVII2C_IC_CLR_RD_REQ
+//CKSVII2C_IC_CLR_TX_ABRT
+//CKSVII2C_IC_CLR_RX_DONE
+//CKSVII2C_IC_CLR_ACTIVITY
+#define CKSVII2C_IC_CLR_ACTIVITY__CLR_ACTIVITY__SHIFT                                                         0x0
+#define CKSVII2C_IC_CLR_ACTIVITY__CLR_ACTIVITY_MASK                                                           0x00000001L
+//CKSVII2C_IC_CLR_STOP_DET
+//CKSVII2C_IC_CLR_START_DET
+//CKSVII2C_IC_CLR_GEN_CALL
+//CKSVII2C_IC_ENABLE
+#define CKSVII2C_IC_ENABLE__ENABLE__SHIFT                                                                     0x0
+#define CKSVII2C_IC_ENABLE__ABORT__SHIFT                                                                      0x1
+#define CKSVII2C_IC_ENABLE__ENABLE_MASK                                                                       0x00000001L
+#define CKSVII2C_IC_ENABLE__ABORT_MASK                                                                        0x00000002L
+//CKSVII2C_IC_STATUS
+#define CKSVII2C_IC_STATUS__ACTIVITY__SHIFT                                                                   0x0
+#define CKSVII2C_IC_STATUS__TFNF__SHIFT                                                                       0x1
+#define CKSVII2C_IC_STATUS__TFE__SHIFT                                                                        0x2
+#define CKSVII2C_IC_STATUS__RFNE__SHIFT                                                                       0x3
+#define CKSVII2C_IC_STATUS__RFF__SHIFT                                                                        0x4
+#define CKSVII2C_IC_STATUS__MST_ACTIVITY__SHIFT                                                               0x5
+#define CKSVII2C_IC_STATUS__SLV_ACTIVITY__SHIFT                                                               0x6
+#define CKSVII2C_IC_STATUS__ACTIVITY_MASK                                                                     0x00000001L
+#define CKSVII2C_IC_STATUS__TFNF_MASK                                                                         0x00000002L
+#define CKSVII2C_IC_STATUS__TFE_MASK                                                                          0x00000004L
+#define CKSVII2C_IC_STATUS__RFNE_MASK                                                                         0x00000008L
+#define CKSVII2C_IC_STATUS__RFF_MASK                                                                          0x00000010L
+#define CKSVII2C_IC_STATUS__MST_ACTIVITY_MASK                                                                 0x00000020L
+#define CKSVII2C_IC_STATUS__SLV_ACTIVITY_MASK                                                                 0x00000040L
+//CKSVII2C_IC_TXFLR
+//CKSVII2C_IC_RXFLR
+//CKSVII2C_IC_SDA_HOLD
+#define CKSVII2C_IC_SDA_HOLD__IC_SDA_HOLD__SHIFT                                                              0x0
+#define CKSVII2C_IC_SDA_HOLD__IC_SDA_HOLD_MASK                                                                0x00FFFFFFL
+//CKSVII2C_IC_TX_ABRT_SOURCE
+
+#define CKSVII2C_IC_TX_ABRT_SOURCE__ABRT_7B_ADDR_NOACK__SHIFT                                                  0x0
+#define CKSVII2C_IC_TX_ABRT_SOURCE__ABRT_10ADDR1_NOACK__SHIFT                                                  0x1
+#define CKSVII2C_IC_TX_ABRT_SOURCE__ABRT_10ADDR2_NOACK__SHIFT                                                  0x2
+#define CKSVII2C_IC_TX_ABRT_SOURCE__ABRT_TXDATA_NOACK__SHIFT                                                   0x3
+#define CKSVII2C_IC_TX_ABRT_SOURCE__ABRT_7B_ADDR_NOACK_MASK                                                   0x00000001L
+#define CKSVII2C_IC_TX_ABRT_SOURCE__ABRT_10ADDR1_NOACK_MASK                                                   0x00000002L
+#define CKSVII2C_IC_TX_ABRT_SOURCE__ABRT_10ADDR2_NOACK_MASK                                                   0x00000004L
+#define CKSVII2C_IC_TX_ABRT_SOURCE__ABRT_TXDATA_NOACK_MASK                                                    0x00000008L
+//CKSVII2C_IC_SLV_DATA_NACK_ONLY
+//CKSVII2C_IC_DMA_CR
+//CKSVII2C_IC_DMA_TDLR
+//CKSVII2C_IC_DMA_RDLR
+//CKSVII2C_IC_SDA_SETUP
+#define CKSVII2C_IC_SDA_SETUP__SDA_SETUP__SHIFT                                                               0x0
+#define CKSVII2C_IC_SDA_SETUP__SDA_SETUP_MASK                                                                 0x000000FFL
+//CKSVII2C_IC_ACK_GENERAL_CALL
+#define CKSVII2C_IC_ACK_GENERAL_CALL__ACK_GENERAL_CALL__SHIFT                                                 0x0
+#define CKSVII2C_IC_ACK_GENERAL_CALL__ACK_GENERAL_CALL_MASK                                                   0x00000001L
+//CKSVII2C_IC_ENABLE_STATUS
+#define CKSVII2C_IC_ENABLE_STATUS__IC_EN__SHIFT                                                               0x0
+#define CKSVII2C_IC_ENABLE_STATUS__SLV_RX_ABORTED__SHIFT                                                      0x1
+#define CKSVII2C_IC_ENABLE_STATUS__SLV_FIFO_FILLED_AND_FLUSHED__SHIFT                                         0x2
+#define CKSVII2C_IC_ENABLE_STATUS__IC_EN_MASK                                                                 0x00000001L
+#define CKSVII2C_IC_ENABLE_STATUS__SLV_RX_ABORTED_MASK                                                        0x00000002L
+#define CKSVII2C_IC_ENABLE_STATUS__SLV_FIFO_FILLED_AND_FLUSHED_MASK                                           0x00000004L
+//CKSVII2C_IC_FS_SPKLEN
+#define CKSVII2C_IC_FS_SPKLEN__FS_SPKLEN__SHIFT                                                               0x0
+#define CKSVII2C_IC_FS_SPKLEN__FS_SPKLEN_MASK                                                                 0x000000FFL
+//CKSVII2C_IC_HS_SPKLEN
+#define CKSVII2C_IC_HS_SPKLEN__HS_SPKLEN__SHIFT                                                               0x0
+#define CKSVII2C_IC_HS_SPKLEN__HS_SPKLEN_MASK                                                                 0x000000FFL
+//CKSVII2C_IC_CLR_RESTART_DET
+//CKSVII2C_IC_COMP_PARAM_1
+#define CKSVII2C_IC_COMP_PARAM_1__COMP_PARAM_1__SHIFT                                                         0x0
+#define CKSVII2C_IC_COMP_PARAM_1__COMP_PARAM_1_MASK                                                           0xFFFFFFFFL
+//CKSVII2C_IC_COMP_VERSION
+#define CKSVII2C_IC_COMP_VERSION__COMP_VERSION__SHIFT                                                         0x0
+#define CKSVII2C_IC_COMP_VERSION__COMP_VERSION_MASK                                                           0xFFFFFFFFL
+//CKSVII2C_IC_COMP_TYPE
+#define CKSVII2C_IC_COMP_TYPE__COMP_TYPE__SHIFT                                                               0x0
+#define CKSVII2C_IC_COMP_TYPE__COMP_TYPE_MASK                                                                 0xFFFFFFFFL
 //SMUIO_MP_RESET_INTR
 #define SMUIO_MP_RESET_INTR__SMUIO_MP_RESET_INTR__SHIFT                                                       0x0
 #define SMUIO_MP_RESET_INTR__SMUIO_MP_RESET_INTR_MASK                                                         0x00000001L
-- 
2.7.4

_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v4 3/4] drm/amd/powerplay: Add interface to lock SMU HW I2C.
       [not found] ` <1566417719-1528-1-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
  2019-08-21 20:01   ` [PATCH v4 1/4] drm/amdgpu: Add RAS EEPROM table Andrey Grodzovsky
  2019-08-21 20:01   ` [PATCH v4 2/4] drm/amd: Import smuio_11_0 headres for EEPROM access on Vega20 Andrey Grodzovsky
@ 2019-08-21 20:01   ` Andrey Grodzovsky
       [not found]     ` <1566417719-1528-4-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
  2019-08-21 20:01   ` [PATCH v4 4/4] drm/amdgpu: Vega20 SMU I2C HW engine controller Andrey Grodzovsky
  3 siblings, 1 reply; 18+ messages in thread
From: Andrey Grodzovsky @ 2019-08-21 20:01 UTC (permalink / raw)
  To: amd-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: David.Panariti-5C7GfCeVMHo, Lijo.Lazar-5C7GfCeVMHo,
	Andrey Grodzovsky, Xinhui.Pan-5C7GfCeVMHo,
	Luben.Tuikov-5C7GfCeVMHo, Kent.Russell-5C7GfCeVMHo,
	Tao.Zhou1-5C7GfCeVMHo, Alexander.Deucher-5C7GfCeVMHo,
	evan.quan-5C7GfCeVMHo, Hawking.Zhang-5C7GfCeVMHo

v2:
PPSMC_MSG_RequestI2CBus seems not to work and so to avoid conflict
over I2C bus and engine disable thermal control access to
force SMU stop using the I2C bus until the issue is reslolved.

Expose and call vega20_is_smc_ram_running to skip locking when SMU
FW is not yet loaded.

v3:
Remove the prevoius hack as the SMU found the bug.

Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@amd.com>
---
 drivers/gpu/drm/amd/include/kgd_pp_interface.h       |  1 +
 drivers/gpu/drm/amd/powerplay/amd_powerplay.c        | 16 ++++++++++++++++
 drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c   | 19 +++++++++++++++++++
 drivers/gpu/drm/amd/powerplay/inc/hwmgr.h            |  1 +
 drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c |  2 +-
 drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.h |  2 ++
 6 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
index 0de4e37..021ce466 100644
--- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h
+++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
@@ -275,6 +275,7 @@ struct amd_pm_funcs {
 	int (*set_power_profile_mode)(void *handle, long *input, uint32_t size);
 	int (*odn_edit_dpm_table)(void *handle, uint32_t type, long *input, uint32_t size);
 	int (*set_mp1_state)(void *handle, enum pp_mp1_state mp1_state);
+	int (*smu_i2c_bus_access)(void *handle, bool aquire);
 /* export to DC */
 	u32 (*get_sclk)(void *handle, bool low);
 	u32 (*get_mclk)(void *handle, bool low);
diff --git a/drivers/gpu/drm/amd/powerplay/amd_powerplay.c b/drivers/gpu/drm/amd/powerplay/amd_powerplay.c
index 7ef2027..bef5cd1 100644
--- a/drivers/gpu/drm/amd/powerplay/amd_powerplay.c
+++ b/drivers/gpu/drm/amd/powerplay/amd_powerplay.c
@@ -1528,6 +1528,21 @@ static int pp_asic_reset_mode_2(void *handle)
 	return ret;
 }
 
+static int pp_smu_i2c_bus_access(void *handle, bool aquire)
+{
+	struct pp_hwmgr *hwmgr = handle;
+
+	if (!hwmgr || !hwmgr->pm_en)
+		return -EINVAL;
+
+	if (hwmgr->hwmgr_func->smu_i2c_bus_access == NULL) {
+		pr_info_ratelimited("%s was not implemented.\n", __func__);
+		return -EINVAL;
+	}
+
+	return hwmgr->hwmgr_func->smu_i2c_bus_access(hwmgr, aquire);
+}
+
 static const struct amd_pm_funcs pp_dpm_funcs = {
 	.load_firmware = pp_dpm_load_fw,
 	.wait_for_fw_loading_complete = pp_dpm_fw_loading_complete,
@@ -1585,4 +1600,5 @@ static const struct amd_pm_funcs pp_dpm_funcs = {
 	.get_ppfeature_status = pp_get_ppfeature_status,
 	.set_ppfeature_status = pp_set_ppfeature_status,
 	.asic_reset_mode_2 = pp_asic_reset_mode_2,
+	.smu_i2c_bus_access = pp_smu_i2c_bus_access,
 };
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c
index 0516c29..f26b14f 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c
@@ -4085,6 +4085,24 @@ static int vega20_get_thermal_temperature_range(struct pp_hwmgr *hwmgr,
 	return 0;
 }
 
+static int vega20_smu_i2c_bus_access(struct pp_hwmgr *hwmgr, bool aquire)
+{
+	int res;
+
+	/* I2C bus access can happen very early, when SMU not loaded yet */
+	if (!vega20_is_smc_ram_running(hwmgr))
+		return 0;
+
+	res = smum_send_msg_to_smc_with_parameter(hwmgr,
+						  (aquire ?
+						  PPSMC_MSG_RequestI2CBus :
+						  PPSMC_MSG_ReleaseI2CBus),
+						  0);
+
+	PP_ASSERT_WITH_CODE(!res, "[SmuI2CAccessBus] Failed to access bus!", return res);
+	return res;
+}
+
 static const struct pp_hwmgr_func vega20_hwmgr_funcs = {
 	/* init/fini related */
 	.backend_init = vega20_hwmgr_backend_init,
@@ -4152,6 +4170,7 @@ static const struct pp_hwmgr_func vega20_hwmgr_funcs = {
 	.get_asic_baco_state = vega20_baco_get_state,
 	.set_asic_baco_state = vega20_baco_set_state,
 	.set_mp1_state = vega20_set_mp1_state,
+	.smu_i2c_bus_access = vega20_smu_i2c_bus_access,
 };
 
 int vega20_hwmgr_init(struct pp_hwmgr *hwmgr)
diff --git a/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h
index abeff15..7bf9a14 100644
--- a/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h
+++ b/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h
@@ -354,6 +354,7 @@ struct pp_hwmgr_func {
 	int (*set_ppfeature_status)(struct pp_hwmgr *hwmgr, uint64_t ppfeature_masks);
 	int (*set_mp1_state)(struct pp_hwmgr *hwmgr, enum pp_mp1_state mp1_state);
 	int (*asic_reset)(struct pp_hwmgr *hwmgr, enum SMU_ASIC_RESET_MODE mode);
+	int (*smu_i2c_bus_access)(struct pp_hwmgr *hwmgr, bool aquire);
 };
 
 struct pp_table_func {
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c
index 3e97b83..b9089c6 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c
@@ -44,7 +44,7 @@
 #define smnMP0_FW_INTF			0x30101c0
 #define smnMP1_PUB_CTRL			0x3010b14
 
-static bool vega20_is_smc_ram_running(struct pp_hwmgr *hwmgr)
+bool vega20_is_smc_ram_running(struct pp_hwmgr *hwmgr)
 {
 	struct amdgpu_device *adev = hwmgr->adev;
 	uint32_t mp1_fw_flags;
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.h b/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.h
index ec953ab..62ebbfd 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.h
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.h
@@ -57,5 +57,7 @@ int vega20_get_activity_monitor_coeff(struct pp_hwmgr *hwmgr,
 		uint8_t *table, uint16_t workload_type);
 int vega20_set_pptable_driver_address(struct pp_hwmgr *hwmgr);
 
+bool vega20_is_smc_ram_running(struct pp_hwmgr *hwmgr);
+
 #endif
 
-- 
2.7.4

_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v4 4/4] drm/amdgpu: Vega20 SMU I2C HW engine controller.
       [not found] ` <1566417719-1528-1-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
                     ` (2 preceding siblings ...)
  2019-08-21 20:01   ` [PATCH v4 3/4] drm/amd/powerplay: Add interface to lock SMU HW I2C Andrey Grodzovsky
@ 2019-08-21 20:01   ` Andrey Grodzovsky
       [not found]     ` <1566417719-1528-5-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
  3 siblings, 1 reply; 18+ messages in thread
From: Andrey Grodzovsky @ 2019-08-21 20:01 UTC (permalink / raw)
  To: amd-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: David.Panariti-5C7GfCeVMHo, Lijo.Lazar-5C7GfCeVMHo,
	Andrey Grodzovsky, Xinhui.Pan-5C7GfCeVMHo,
	Luben.Tuikov-5C7GfCeVMHo, Kent.Russell-5C7GfCeVMHo,
	Tao.Zhou1-5C7GfCeVMHo, Alexander.Deucher-5C7GfCeVMHo,
	evan.quan-5C7GfCeVMHo, Hawking.Zhang-5C7GfCeVMHo

Implement HW I2C enigne controller to be used by the RAS EEPROM
table manager. This is based on code from ATITOOLs.

v2:
Rename the file and all function prefixes to smu_v11_0_i2c

By Luben's observation always fill the TX fifo to full so
we don't have garbadge interpreted by the slave as valid data.

v3:
Remove preemption disable as the HW I2C controller will not
stop the clock on empty TX fifo and so it's not critical to
keep not empty queue.
Switch to fast mode 400 khz SCL clock for faster read and write.

Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/Makefile            |   5 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c |   5 +-
 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c     | 710 +++++++++++++++++++++++++
 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h     |  34 ++
 4 files changed, 751 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
 create mode 100644 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h

diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
index f016cf1..14733ff 100644
--- a/drivers/gpu/drm/amd/amdgpu/Makefile
+++ b/drivers/gpu/drm/amd/amdgpu/Makefile
@@ -38,6 +38,9 @@ ccflags-y := -I$(FULL_AMD_PATH)/include/asic_reg \
 	-I$(FULL_AMD_DISPLAY_PATH)/amdgpu_dm \
 	-I$(FULL_AMD_PATH)/amdkfd
 
+
+
+
 amdgpu-y := amdgpu_drv.o
 
 # add KMS driver
@@ -54,7 +57,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
 	amdgpu_gtt_mgr.o amdgpu_vram_mgr.o amdgpu_virt.o amdgpu_atomfirmware.o \
 	amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o amdgpu_ids.o \
 	amdgpu_gmc.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o amdgpu_vm_cpu.o \
-	amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o amdgpu_ras_eeprom.o
+	amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o amdgpu_ras_eeprom.o smu_v11_0_i2c.o
 
 amdgpu-$(CONFIG_PERF_EVENTS) += amdgpu_pmu.o
 
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
index bf07515..e6b2e17 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
@@ -25,6 +25,7 @@
 #include "amdgpu.h"
 #include "amdgpu_ras.h"
 #include <linux/bits.h>
+#include "smu_v11_0_i2c.h"
 
 #define EEPROM_I2C_TARGET_ADDR 0xA0
 
@@ -111,7 +112,7 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
 
 	switch (adev->asic_type) {
 	case CHIP_VEGA20:
-	/*TODO Add MI-60 */
+		ret = smu_v11_0_i2c_eeprom_control_init(&control->eeprom_accessor);
 		break;
 
 	default:
@@ -163,7 +164,7 @@ void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control *control)
 
 	switch (adev->asic_type) {
 	case CHIP_VEGA20:
-		/*TODO Add MI-60 */
+		smu_v11_0_i2c_eeprom_control_fini(&control->eeprom_accessor);
 		break;
 
 	default:
diff --git a/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
new file mode 100644
index 0000000..24405fa
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
@@ -0,0 +1,710 @@
+/*
+ * 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 "smuio/smuio_11_0_0_offset.h"
+#include "smuio/smuio_11_0_0_sh_mask.h"
+
+#include "smu_v11_0_i2c.h"
+#include "amdgpu.h"
+#include "soc15_common.h"
+#include <drm/drm_fixed.h>
+#include "amdgpu_amdkfd.h"
+#include <linux/i2c.h>
+#include "amdgpu_ras.h"
+
+/* error codes */
+#define I2C_OK				0
+#define I2C_NAK_7B_ADDR_NOACK		1
+#define I2C_NAK_TXDATA_NOACK		2
+#define I2C_TIMEOUT			4
+#define I2C_SW_TIMEOUT			8
+#define I2C_ABORT			0x10
+
+/* I2C transaction flags */
+#define I2C_NO_STOP	1
+#define I2C_RESTART	2
+
+#define to_amdgpu_device(x) (container_of(x, struct amdgpu_ras, eeprom_control.eeprom_accessor))->adev
+#define to_eeprom_control(x) container_of(x, struct amdgpu_ras_eeprom_control, eeprom_accessor)
+
+static void smu_v11_0_i2c_dis_clock_gating(struct i2c_adapter *control)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+	uint32_t reg;
+
+	reg = RREG32_SOC15(SMUIO, 0, mmSMUIO_PWRMGT);
+	reg = REG_SET_FIELD(reg, SMUIO_PWRMGT, i2c_clk_gate_en, 0);
+	WREG32_SOC15(SMUIO, 0, mmSMUIO_PWRMGT, reg);
+}
+
+
+static void smu_v11_0_i2c_enable(struct i2c_adapter *control, bool enable)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+
+	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, enable ? 1 : 0);
+}
+
+static void smu_v11_0_i2c_clear_status(struct i2c_adapter *control)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+	/* do */
+	{
+		RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CLR_INTR);
+
+	} /* while (reg_CKSVII2C_ic_clr_intr == 0) */
+}
+
+static void smu_v11_0_i2c_configure(struct i2c_adapter *control)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+	uint32_t reg = 0;
+
+	reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_SLAVE_DISABLE, 1);
+	reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_RESTART_EN, 1);
+	reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_10BITADDR_MASTER, 0);
+	reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_10BITADDR_SLAVE, 0);
+	/* Standard mode */
+	reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_MAX_SPEED_MODE, 2);
+	reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_MASTER_MODE, 1);
+
+	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CON, reg);
+}
+
+static void smu_v11_0_i2c_set_clock(struct i2c_adapter *control)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+
+	/*
+	 * Standard mode speed, These values are taken from SMUIO MAS,
+	 * but are different from what is given is
+	 * Synopsys spec. The values here are based on assumption
+	 * that refclock is 100MHz
+	 *
+	 * Configuration for standard mode; Speed = 100kbps
+	 * Scale linearly, for now only support standard speed clock
+	 * This will work only with 100M ref clock
+	 *
+	 * TBD:Change the calculation to take into account ref clock values also.
+	 */
+
+	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_FS_SPKLEN, 2);
+	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SS_SCL_HCNT, 120);
+	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SS_SCL_LCNT, 130);
+	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SDA_HOLD, 20);
+}
+
+static void smu_v11_0_i2c_set_address(struct i2c_adapter *control, uint8_t address)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+
+	/* Convert fromr 8-bit to 7-bit address */
+	address >>= 1;
+	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TAR, (address & 0xFF));
+}
+
+static uint32_t smu_v11_0_i2c_poll_tx_status(struct i2c_adapter *control)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+	uint32_t ret = I2C_OK;
+	uint32_t reg, reg_c_tx_abrt_source;
+
+	/*Check if transmission is completed */
+	unsigned long  timeout_counter = jiffies + msecs_to_jiffies(20);
+
+	do {
+		reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
+
+		if (time_after(jiffies, timeout_counter)) {
+			ret |= I2C_SW_TIMEOUT;
+			break;
+		}
+	} while (REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFE) == 0);
+
+	if (ret != I2C_OK)
+		return ret;
+
+	/* This only checks if NAK is received and transaction got aborted */
+	reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_INTR_STAT);
+
+	if (REG_GET_FIELD(reg, CKSVII2C_IC_INTR_STAT, R_TX_ABRT) == 1) {
+		reg_c_tx_abrt_source = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TX_ABRT_SOURCE);
+		DRM_INFO("TX was terminated, IC_TX_ABRT_SOURCE val is:%x", reg_c_tx_abrt_source);
+
+		/* Check for stop due to NACK */
+		if (REG_GET_FIELD(reg_c_tx_abrt_source,
+				  CKSVII2C_IC_TX_ABRT_SOURCE,
+				  ABRT_TXDATA_NOACK) == 1) {
+
+			ret |= I2C_NAK_TXDATA_NOACK;
+
+		} else if (REG_GET_FIELD(reg_c_tx_abrt_source,
+					 CKSVII2C_IC_TX_ABRT_SOURCE,
+					 ABRT_7B_ADDR_NOACK) == 1) {
+
+			ret |= I2C_NAK_7B_ADDR_NOACK;
+		} else {
+			ret |= I2C_ABORT;
+		}
+
+		smu_v11_0_i2c_clear_status(control);
+	}
+
+	return ret;
+}
+
+static uint32_t smu_v11_0_i2c_poll_rx_status(struct i2c_adapter *control)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+	uint32_t ret = I2C_OK;
+	uint32_t reg_ic_status, reg_c_tx_abrt_source;
+
+	reg_c_tx_abrt_source = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TX_ABRT_SOURCE);
+
+	/* If slave is not present */
+	if (REG_GET_FIELD(reg_c_tx_abrt_source,
+			  CKSVII2C_IC_TX_ABRT_SOURCE,
+			  ABRT_7B_ADDR_NOACK) == 1) {
+		ret |= I2C_NAK_7B_ADDR_NOACK;
+
+		smu_v11_0_i2c_clear_status(control);
+	} else {  /* wait till some data is there in RXFIFO */
+		/* Poll for some byte in RXFIFO */
+		unsigned long  timeout_counter = jiffies + msecs_to_jiffies(20);
+
+		do {
+			reg_ic_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
+
+			if (time_after(jiffies, timeout_counter)) {
+				ret |= I2C_SW_TIMEOUT;
+				break;
+			}
+		} while (REG_GET_FIELD(reg_ic_status, CKSVII2C_IC_STATUS, RFNE) == 0);
+	}
+
+	return ret;
+}
+
+
+
+
+/**
+ * smu_v11_0_i2c_transmit - Send a block of data over the I2C bus to a slave device.
+ *
+ * @address: The I2C address of the slave device.
+ * @data: The data to transmit over the bus.
+ * @numbytes: The amount of data to transmit.
+ * @i2c_flag: Flags for transmission
+ *
+ * Returns 0 on success or error.
+ */
+static uint32_t smu_v11_0_i2c_transmit(struct i2c_adapter *control,
+				  uint8_t address, uint8_t *data,
+				  uint32_t numbytes, uint32_t i2c_flag)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+	uint32_t bytes_sent, reg, ret = 0;
+	unsigned long  timeout_counter;
+
+	bytes_sent = 0;
+
+	DRM_DEBUG_DRIVER("I2C_Transmit(), address = %x, bytes = %d , data: ",
+		 (uint16_t)address, numbytes);
+
+	if (drm_debug & DRM_UT_DRIVER) {
+		print_hex_dump(KERN_INFO, "data: ", DUMP_PREFIX_NONE,
+			       16, 1, data, numbytes, false);
+	}
+
+	/* Set the I2C slave address */
+	smu_v11_0_i2c_set_address(control, address);
+	/* Enable I2C */
+	smu_v11_0_i2c_enable(control, true);
+
+	/* Clear status bits */
+	smu_v11_0_i2c_clear_status(control);
+
+
+	timeout_counter = jiffies + msecs_to_jiffies(20);
+
+	while (numbytes > 0) {
+		reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
+		if (REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFNF)) {
+			do {
+				reg = 0;
+				/*
+				 * Prepare transaction, no need to set RESTART. I2C engine will send
+				 * START as soon as it sees data in TXFIFO
+				 */
+				if (bytes_sent == 0)
+					reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, RESTART,
+							    (i2c_flag & I2C_RESTART) ? 1 : 0);
+				reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, data[bytes_sent]);
+
+				/* determine if we need to send STOP bit or not */
+				if (numbytes == 1)
+					/* Final transaction, so send stop unless I2C_NO_STOP */
+					reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, STOP,
+							    (i2c_flag & I2C_NO_STOP) ? 0 : 1);
+				/* Write */
+				reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 0);
+				WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg);
+
+				/* Record that the bytes were transmitted */
+				bytes_sent++;
+				numbytes--;
+
+				reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
+
+			} while (numbytes &&  REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFNF));
+		}
+
+
+		if (time_after(jiffies, timeout_counter)) {
+			ret |= I2C_SW_TIMEOUT;
+			goto err;
+		}
+	}
+
+	ret = smu_v11_0_i2c_poll_tx_status(control);
+
+err:
+	/* Any error, no point in proceeding */
+	if (ret != I2C_OK) {
+		if (ret & I2C_SW_TIMEOUT)
+			DRM_ERROR("TIMEOUT ERROR !!!");
+
+		if (ret & I2C_NAK_7B_ADDR_NOACK)
+			DRM_ERROR("Received I2C_NAK_7B_ADDR_NOACK !!!");
+
+
+		if (ret & I2C_NAK_TXDATA_NOACK)
+			DRM_ERROR("Received I2C_NAK_TXDATA_NOACK !!!");
+	}
+
+	return ret;
+}
+
+
+/**
+ * smu_v11_0_i2c_receive - Receive a block of data over the I2C bus from a slave device.
+ *
+ * @address: The I2C address of the slave device.
+ * @numbytes: The amount of data to transmit.
+ * @i2c_flag: Flags for transmission
+ *
+ * Returns 0 on success or error.
+ */
+static uint32_t smu_v11_0_i2c_receive(struct i2c_adapter *control,
+				 uint8_t address, uint8_t *data,
+				 uint32_t numbytes, uint8_t i2c_flag)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+	uint32_t bytes_received, ret = I2C_OK;
+
+	bytes_received = 0;
+
+	/* Set the I2C slave address */
+	smu_v11_0_i2c_set_address(control, address);
+
+	/* Enable I2C */
+	smu_v11_0_i2c_enable(control, true);
+
+	while (numbytes > 0) {
+		uint32_t reg = 0;
+
+		smu_v11_0_i2c_clear_status(control);
+
+
+		/* Prepare transaction */
+
+		/* Each time we disable I2C, so this is not a restart */
+		if (bytes_received == 0)
+			reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, RESTART,
+					    (i2c_flag & I2C_RESTART) ? 1 : 0);
+
+		reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, 0);
+		/* Read */
+		reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 1);
+
+		/* Transmitting last byte */
+		if (numbytes == 1)
+			/* Final transaction, so send stop if requested */
+			reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, STOP,
+					    (i2c_flag & I2C_NO_STOP) ? 0 : 1);
+
+		WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg);
+
+		ret = smu_v11_0_i2c_poll_rx_status(control);
+
+		/* Any error, no point in proceeding */
+		if (ret != I2C_OK) {
+			if (ret & I2C_SW_TIMEOUT)
+				DRM_ERROR("TIMEOUT ERROR !!!");
+
+			if (ret & I2C_NAK_7B_ADDR_NOACK)
+				DRM_ERROR("Received I2C_NAK_7B_ADDR_NOACK !!!");
+
+			if (ret & I2C_NAK_TXDATA_NOACK)
+				DRM_ERROR("Received I2C_NAK_TXDATA_NOACK !!!");
+
+			break;
+		}
+
+		reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD);
+		data[bytes_received] = REG_GET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT);
+
+		/* Record that the bytes were received */
+		bytes_received++;
+		numbytes--;
+	}
+
+	DRM_DEBUG_DRIVER("I2C_Receive(), address = %x, bytes = %d, data :",
+		  (uint16_t)address, bytes_received);
+
+	if (drm_debug & DRM_UT_DRIVER) {
+		print_hex_dump(KERN_INFO, "data: ", DUMP_PREFIX_NONE,
+			       16, 1, data, bytes_received, false);
+	}
+
+	return ret;
+}
+
+static void smu_v11_0_i2c_abort(struct i2c_adapter *control)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+	uint32_t reg = 0;
+
+	/* Enable I2C engine; */
+	reg = REG_SET_FIELD(reg, CKSVII2C_IC_ENABLE, ENABLE, 1);
+	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, reg);
+
+	/* Abort previous transaction */
+	reg = REG_SET_FIELD(reg, CKSVII2C_IC_ENABLE, ABORT, 1);
+	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, reg);
+
+	DRM_DEBUG_DRIVER("I2C_Abort() Done.");
+}
+
+
+static bool smu_v11_0_i2c_activity_done(struct i2c_adapter *control)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+
+	const uint32_t IDLE_TIMEOUT = 1024;
+	uint32_t timeoutCount = 0;
+	uint32_t reg_ic_enable, reg_ic_enable_status, reg_ic_clr_activity;
+
+	reg_ic_enable_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE_STATUS);
+	reg_ic_enable = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE);
+
+
+	if ((REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) &&
+	    (REG_GET_FIELD(reg_ic_enable_status, CKSVII2C_IC_ENABLE_STATUS, IC_EN) == 1)) {
+		/*
+		 * Nobody is using I2C engine, but engine remains active because
+		 * someone missed to send STOP
+		 */
+		smu_v11_0_i2c_abort(control);
+	} else if (REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) {
+		/* Nobody is using I2C engine */
+		return true;
+	}
+
+	/* Keep reading activity bit until it's cleared */
+	do {
+		reg_ic_clr_activity = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CLR_ACTIVITY);
+
+		if (REG_GET_FIELD(reg_ic_clr_activity,
+		    CKSVII2C_IC_CLR_ACTIVITY, CLR_ACTIVITY) == 0)
+			return true;
+
+		++timeoutCount;
+
+	} while (timeoutCount < IDLE_TIMEOUT);
+
+	return false;
+}
+
+static void smu_v11_0_i2c_init(struct i2c_adapter *control)
+{
+	/* Disable clock gating */
+	smu_v11_0_i2c_dis_clock_gating(control);
+
+	if (!smu_v11_0_i2c_activity_done(control))
+		DRM_WARN("I2C busy !");
+
+	/* Disable I2C */
+	smu_v11_0_i2c_enable(control, false);
+
+	/* Configure I2C to operate as master and in standard mode */
+	smu_v11_0_i2c_configure(control);
+
+	/* Initialize the clock to 50 kHz default */
+	smu_v11_0_i2c_set_clock(control);
+
+}
+
+static void smu_v11_0_i2c_fini(struct i2c_adapter *control)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+	uint32_t reg_ic_enable_status, reg_ic_enable;
+
+	smu_v11_0_i2c_enable(control, false);
+
+	/* Double check if disabled, else force abort */
+	reg_ic_enable_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE_STATUS);
+	reg_ic_enable = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE);
+
+	if ((REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) &&
+	    (REG_GET_FIELD(reg_ic_enable_status,
+			   CKSVII2C_IC_ENABLE_STATUS, IC_EN) == 1)) {
+		/*
+		 * Nobody is using I2C engine, but engine remains active because
+		 * someone missed to send STOP
+		 */
+		smu_v11_0_i2c_abort(control);
+	}
+
+}
+
+static bool smu_v11_0_i2c_bus_lock(struct i2c_adapter *control)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+
+	/* Send  PPSMC_MSG_RequestI2CBus */
+	if (!adev->powerplay.pp_funcs->smu_i2c_bus_access)
+		goto fail;
+
+
+	if (!adev->powerplay.pp_funcs->smu_i2c_bus_access(adev->powerplay.pp_handle, true))
+		return true;
+
+fail:
+	return false;
+}
+
+static bool smu_v11_0_i2c_bus_unlock(struct i2c_adapter *control)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+
+	/* Send  PPSMC_MSG_RequestI2CBus */
+	if (!adev->powerplay.pp_funcs->smu_i2c_bus_access)
+		goto fail;
+
+	/* Send  PPSMC_MSG_ReleaseI2CBus */
+	if (!adev->powerplay.pp_funcs->smu_i2c_bus_access(adev->powerplay.pp_handle,
+							     false))
+		return true;
+
+fail:
+	return false;
+}
+
+/***************************** EEPROM I2C GLUE ****************************/
+
+static uint32_t smu_v11_0_i2c_eeprom_read_data(struct i2c_adapter *control,
+					       uint8_t address,
+					       uint8_t *data,
+					       uint32_t numbytes)
+{
+	uint32_t  ret = 0;
+
+	/* First 2 bytes are dummy write to set EEPROM address */
+	ret = smu_v11_0_i2c_transmit(control, address, data, 2, I2C_NO_STOP);
+	if (ret != I2C_OK)
+		goto fail;
+
+	/* Now read data starting with that address */
+	ret = smu_v11_0_i2c_receive(control, address, data + 2, numbytes - 2,
+				    I2C_RESTART);
+
+fail:
+	if (ret != I2C_OK)
+		DRM_ERROR("ReadData() - I2C error occurred :%x", ret);
+
+	return ret;
+}
+
+static uint32_t smu_v11_0_i2c_eeprom_write_data(struct i2c_adapter *control,
+						uint8_t address,
+						uint8_t *data,
+						uint32_t numbytes)
+{
+	uint32_t  ret;
+
+	ret = smu_v11_0_i2c_transmit(control, address, data, numbytes, 0);
+
+	if (ret != I2C_OK)
+		DRM_ERROR("WriteI2CData() - I2C error occurred :%x", ret);
+	else
+		/*
+		 * According to EEPROM spec there is a MAX of 10 ms required for
+		 * EEPROM to flush internal RX buffer after STOP was issued at the
+		 * end of write transaction. During this time the EEPROM will not be
+		 * responsive to any more commands - so wait a bit more.
+		 *
+		 * TODO Improve to wait for first ACK for slave address after
+		 * internal write cycle done.
+		 */
+		msleep(10);
+
+	return ret;
+
+}
+
+static void lock_bus(struct i2c_adapter *i2c, unsigned int flags)
+{
+	struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c);
+
+	if (!smu_v11_0_i2c_bus_lock(i2c)) {
+		DRM_ERROR("Failed to lock the bus from SMU");
+		return;
+	}
+
+	control->bus_locked = true;
+}
+
+static int trylock_bus(struct i2c_adapter *i2c, unsigned int flags)
+{
+	WARN_ONCE(1, "This operation not supposed to run in atomic context!");
+	return false;
+}
+
+static void unlock_bus(struct i2c_adapter *i2c, unsigned int flags)
+{
+	struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c);
+
+	if (!smu_v11_0_i2c_bus_unlock(i2c)) {
+		DRM_ERROR("Failed to unlock the bus from SMU");
+		return;
+	}
+
+	control->bus_locked = false;
+}
+
+static const struct i2c_lock_operations smu_v11_0_i2c_i2c_lock_ops = {
+	.lock_bus = lock_bus,
+	.trylock_bus = trylock_bus,
+	.unlock_bus = unlock_bus,
+};
+
+static int smu_v11_0_i2c_eeprom_i2c_xfer(struct i2c_adapter *i2c_adap,
+			      struct i2c_msg *msgs, int num)
+{
+	int i, ret;
+	struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c_adap);
+
+	if (!control->bus_locked) {
+		DRM_ERROR("I2C bus unlocked, stopping transaction!");
+		return -EIO;
+	}
+
+	smu_v11_0_i2c_init(i2c_adap);
+
+	for (i = 0; i < num; i++) {
+		if (msgs[i].flags & I2C_M_RD)
+			ret = smu_v11_0_i2c_eeprom_read_data(i2c_adap,
+							(uint8_t)msgs[i].addr,
+							msgs[i].buf, msgs[i].len);
+		else
+			ret = smu_v11_0_i2c_eeprom_write_data(i2c_adap,
+							 (uint8_t)msgs[i].addr,
+							 msgs[i].buf, msgs[i].len);
+
+		if (ret != I2C_OK) {
+			num = -EIO;
+			break;
+		}
+	}
+
+	smu_v11_0_i2c_fini(i2c_adap);
+	return num;
+}
+
+static u32 smu_v11_0_i2c_eeprom_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+
+static const struct i2c_algorithm smu_v11_0_i2c_eeprom_i2c_algo = {
+	.master_xfer = smu_v11_0_i2c_eeprom_i2c_xfer,
+	.functionality = smu_v11_0_i2c_eeprom_i2c_func,
+};
+
+int smu_v11_0_i2c_eeprom_control_init(struct i2c_adapter *control)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+	int res;
+
+	control->owner = THIS_MODULE;
+	control->class = I2C_CLASS_SPD;
+	control->dev.parent = &adev->pdev->dev;
+	control->algo = &smu_v11_0_i2c_eeprom_i2c_algo;
+	snprintf(control->name, sizeof(control->name), "RAS EEPROM");
+	control->lock_ops = &smu_v11_0_i2c_i2c_lock_ops;
+
+	res = i2c_add_adapter(control);
+	if (res)
+		DRM_ERROR("Failed to register hw i2c, err: %d\n", res);
+
+	return res;
+}
+
+void smu_v11_0_i2c_eeprom_control_fini(struct i2c_adapter *control)
+{
+	i2c_del_adapter(control);
+}
+
+#define I2C_TARGET_ADDR 0xA0
+
+bool smu_v11_0_i2c_test_bus(struct i2c_adapter *control)
+{
+
+	uint32_t ret = I2C_OK;
+	uint8_t data[6] = {0xf, 0, 0xde, 0xad, 0xbe, 0xef};
+
+
+	DRM_INFO("Begin");
+
+	if (!smu_v11_0_i2c_bus_lock(control)) {
+		DRM_ERROR("Failed to lock the bus!.");
+		return false;
+	}
+
+	smu_v11_0_i2c_init(control);
+
+	/* Write 0xde to address 0x0000 on the EEPROM */
+	ret = smu_v11_0_i2c_eeprom_write_data(control, I2C_TARGET_ADDR, data, 6);
+
+	ret = smu_v11_0_i2c_eeprom_read_data(control, I2C_TARGET_ADDR, data, 6);
+
+	smu_v11_0_i2c_fini(control);
+
+	smu_v11_0_i2c_bus_unlock(control);
+
+
+	DRM_INFO("End");
+	return true;
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
new file mode 100644
index 0000000..973f28d
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef SMU_V11_I2C_CONTROL_H
+#define SMU_V11_I2C_CONTROL_H
+
+#include <linux/types.h>
+
+struct i2c_adapter;
+
+int smu_v11_0_i2c_eeprom_control_init(struct i2c_adapter *control);
+void smu_v11_0_i2c_eeprom_control_fini(struct i2c_adapter *control);
+
+#endif
-- 
2.7.4

_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 3/4] drm/amd/powerplay: Add interface to lock SMU HW I2C.
       [not found]     ` <1566417719-1528-4-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
@ 2019-08-22  2:17       ` Alex Deucher
  0 siblings, 0 replies; 18+ messages in thread
From: Alex Deucher @ 2019-08-22  2:17 UTC (permalink / raw)
  To: Andrey Grodzovsky
  Cc: David Panariti, xinhui pan, Lijo.Lazar-5C7GfCeVMHo, amd-gfx list,
	Deucher, Alexander, Luben.Tuikov-5C7GfCeVMHo, Russell, Kent,
	Tao Zhou, Quan, Evan, Hawking Zhang

On Wed, Aug 21, 2019 at 4:02 PM Andrey Grodzovsky
<andrey.grodzovsky@amd.com> wrote:
>
> v2:
> PPSMC_MSG_RequestI2CBus seems not to work and so to avoid conflict
> over I2C bus and engine disable thermal control access to
> force SMU stop using the I2C bus until the issue is reslolved.
>
> Expose and call vega20_is_smc_ram_running to skip locking when SMU
> FW is not yet loaded.
>
> v3:
> Remove the prevoius hack as the SMU found the bug.
>
> Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@amd.com>

Minor spelling typo noted below.  With that fixed:
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>

> ---
>  drivers/gpu/drm/amd/include/kgd_pp_interface.h       |  1 +
>  drivers/gpu/drm/amd/powerplay/amd_powerplay.c        | 16 ++++++++++++++++
>  drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c   | 19 +++++++++++++++++++
>  drivers/gpu/drm/amd/powerplay/inc/hwmgr.h            |  1 +
>  drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c |  2 +-
>  drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.h |  2 ++
>  6 files changed, 40 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
> index 0de4e37..021ce466 100644
> --- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h
> +++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
> @@ -275,6 +275,7 @@ struct amd_pm_funcs {
>         int (*set_power_profile_mode)(void *handle, long *input, uint32_t size);
>         int (*odn_edit_dpm_table)(void *handle, uint32_t type, long *input, uint32_t size);
>         int (*set_mp1_state)(void *handle, enum pp_mp1_state mp1_state);
> +       int (*smu_i2c_bus_access)(void *handle, bool aquire);

typo: aquire -> acquire

>  /* export to DC */
>         u32 (*get_sclk)(void *handle, bool low);
>         u32 (*get_mclk)(void *handle, bool low);
> diff --git a/drivers/gpu/drm/amd/powerplay/amd_powerplay.c b/drivers/gpu/drm/amd/powerplay/amd_powerplay.c
> index 7ef2027..bef5cd1 100644
> --- a/drivers/gpu/drm/amd/powerplay/amd_powerplay.c
> +++ b/drivers/gpu/drm/amd/powerplay/amd_powerplay.c
> @@ -1528,6 +1528,21 @@ static int pp_asic_reset_mode_2(void *handle)
>         return ret;
>  }
>
> +static int pp_smu_i2c_bus_access(void *handle, bool aquire)

and here.

> +{
> +       struct pp_hwmgr *hwmgr = handle;
> +
> +       if (!hwmgr || !hwmgr->pm_en)
> +               return -EINVAL;
> +
> +       if (hwmgr->hwmgr_func->smu_i2c_bus_access == NULL) {
> +               pr_info_ratelimited("%s was not implemented.\n", __func__);
> +               return -EINVAL;
> +       }
> +
> +       return hwmgr->hwmgr_func->smu_i2c_bus_access(hwmgr, aquire);
> +}
> +
>  static const struct amd_pm_funcs pp_dpm_funcs = {
>         .load_firmware = pp_dpm_load_fw,
>         .wait_for_fw_loading_complete = pp_dpm_fw_loading_complete,
> @@ -1585,4 +1600,5 @@ static const struct amd_pm_funcs pp_dpm_funcs = {
>         .get_ppfeature_status = pp_get_ppfeature_status,
>         .set_ppfeature_status = pp_set_ppfeature_status,
>         .asic_reset_mode_2 = pp_asic_reset_mode_2,
> +       .smu_i2c_bus_access = pp_smu_i2c_bus_access,
>  };
> diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c
> index 0516c29..f26b14f 100644
> --- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c
> +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c
> @@ -4085,6 +4085,24 @@ static int vega20_get_thermal_temperature_range(struct pp_hwmgr *hwmgr,
>         return 0;
>  }
>
> +static int vega20_smu_i2c_bus_access(struct pp_hwmgr *hwmgr, bool aquire)

Same here.

> +{
> +       int res;
> +
> +       /* I2C bus access can happen very early, when SMU not loaded yet */
> +       if (!vega20_is_smc_ram_running(hwmgr))
> +               return 0;
> +
> +       res = smum_send_msg_to_smc_with_parameter(hwmgr,
> +                                                 (aquire ?
> +                                                 PPSMC_MSG_RequestI2CBus :
> +                                                 PPSMC_MSG_ReleaseI2CBus),
> +                                                 0);
> +
> +       PP_ASSERT_WITH_CODE(!res, "[SmuI2CAccessBus] Failed to access bus!", return res);
> +       return res;
> +}
> +
>  static const struct pp_hwmgr_func vega20_hwmgr_funcs = {
>         /* init/fini related */
>         .backend_init = vega20_hwmgr_backend_init,
> @@ -4152,6 +4170,7 @@ static const struct pp_hwmgr_func vega20_hwmgr_funcs = {
>         .get_asic_baco_state = vega20_baco_get_state,
>         .set_asic_baco_state = vega20_baco_set_state,
>         .set_mp1_state = vega20_set_mp1_state,
> +       .smu_i2c_bus_access = vega20_smu_i2c_bus_access,
>  };
>
>  int vega20_hwmgr_init(struct pp_hwmgr *hwmgr)
> diff --git a/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h
> index abeff15..7bf9a14 100644
> --- a/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h
> +++ b/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h
> @@ -354,6 +354,7 @@ struct pp_hwmgr_func {
>         int (*set_ppfeature_status)(struct pp_hwmgr *hwmgr, uint64_t ppfeature_masks);
>         int (*set_mp1_state)(struct pp_hwmgr *hwmgr, enum pp_mp1_state mp1_state);
>         int (*asic_reset)(struct pp_hwmgr *hwmgr, enum SMU_ASIC_RESET_MODE mode);
> +       int (*smu_i2c_bus_access)(struct pp_hwmgr *hwmgr, bool aquire);
>  };
>
>  struct pp_table_func {
> diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c
> index 3e97b83..b9089c6 100644
> --- a/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c
> +++ b/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c
> @@ -44,7 +44,7 @@
>  #define smnMP0_FW_INTF                 0x30101c0
>  #define smnMP1_PUB_CTRL                        0x3010b14
>
> -static bool vega20_is_smc_ram_running(struct pp_hwmgr *hwmgr)
> +bool vega20_is_smc_ram_running(struct pp_hwmgr *hwmgr)
>  {
>         struct amdgpu_device *adev = hwmgr->adev;
>         uint32_t mp1_fw_flags;
> diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.h b/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.h
> index ec953ab..62ebbfd 100644
> --- a/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.h
> +++ b/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.h
> @@ -57,5 +57,7 @@ int vega20_get_activity_monitor_coeff(struct pp_hwmgr *hwmgr,
>                 uint8_t *table, uint16_t workload_type);
>  int vega20_set_pptable_driver_address(struct pp_hwmgr *hwmgr);
>
> +bool vega20_is_smc_ram_running(struct pp_hwmgr *hwmgr);
> +
>  #endif
>
> --
> 2.7.4
>
> _______________________________________________
> amd-gfx mailing list
> amd-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/amd-gfx
_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 4/4] drm/amdgpu: Vega20 SMU I2C HW engine controller.
       [not found]     ` <1566417719-1528-5-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
@ 2019-08-22  2:32       ` Alex Deucher
       [not found]         ` <CADnq5_M9gSnLWaQLh3aUtrV0=Tj4USQt3rk-GUjHni6KSMxGbg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  2019-08-23  3:50       ` Tuikov, Luben
  1 sibling, 1 reply; 18+ messages in thread
From: Alex Deucher @ 2019-08-22  2:32 UTC (permalink / raw)
  To: Andrey Grodzovsky
  Cc: David Panariti, xinhui pan, Lijo.Lazar-5C7GfCeVMHo, amd-gfx list,
	Deucher, Alexander, Luben.Tuikov-5C7GfCeVMHo, Russell, Kent,
	Tao Zhou, Quan, Evan, Hawking Zhang

On Wed, Aug 21, 2019 at 4:02 PM Andrey Grodzovsky
<andrey.grodzovsky@amd.com> wrote:
>
> Implement HW I2C enigne controller to be used by the RAS EEPROM
> table manager. This is based on code from ATITOOLs.
>
> v2:
> Rename the file and all function prefixes to smu_v11_0_i2c
>
> By Luben's observation always fill the TX fifo to full so
> we don't have garbadge interpreted by the slave as valid data.
>
> v3:
> Remove preemption disable as the HW I2C controller will not
> stop the clock on empty TX fifo and so it's not critical to
> keep not empty queue.
> Switch to fast mode 400 khz SCL clock for faster read and write.
>
> Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@amd.com>
> ---
>  drivers/gpu/drm/amd/amdgpu/Makefile            |   5 +-
>  drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c |   5 +-
>  drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c     | 710 +++++++++++++++++++++++++
>  drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h     |  34 ++
>  4 files changed, 751 insertions(+), 3 deletions(-)
>  create mode 100644 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
>  create mode 100644 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
> index f016cf1..14733ff 100644
> --- a/drivers/gpu/drm/amd/amdgpu/Makefile
> +++ b/drivers/gpu/drm/amd/amdgpu/Makefile
> @@ -38,6 +38,9 @@ ccflags-y := -I$(FULL_AMD_PATH)/include/asic_reg \
>         -I$(FULL_AMD_DISPLAY_PATH)/amdgpu_dm \
>         -I$(FULL_AMD_PATH)/amdkfd
>
> +
> +
> +

Drop this random whitespace change.

>  amdgpu-y := amdgpu_drv.o
>
>  # add KMS driver
> @@ -54,7 +57,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
>         amdgpu_gtt_mgr.o amdgpu_vram_mgr.o amdgpu_virt.o amdgpu_atomfirmware.o \
>         amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o amdgpu_ids.o \
>         amdgpu_gmc.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o amdgpu_vm_cpu.o \
> -       amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o amdgpu_ras_eeprom.o
> +       amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o amdgpu_ras_eeprom.o smu_v11_0_i2c.o
>
>  amdgpu-$(CONFIG_PERF_EVENTS) += amdgpu_pmu.o
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
> index bf07515..e6b2e17 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
> @@ -25,6 +25,7 @@
>  #include "amdgpu.h"
>  #include "amdgpu_ras.h"
>  #include <linux/bits.h>
> +#include "smu_v11_0_i2c.h"
>
>  #define EEPROM_I2C_TARGET_ADDR 0xA0
>
> @@ -111,7 +112,7 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
>
>         switch (adev->asic_type) {
>         case CHIP_VEGA20:
> -       /*TODO Add MI-60 */
> +               ret = smu_v11_0_i2c_eeprom_control_init(&control->eeprom_accessor);
>                 break;
>
>         default:
> @@ -163,7 +164,7 @@ void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control *control)
>
>         switch (adev->asic_type) {
>         case CHIP_VEGA20:
> -               /*TODO Add MI-60 */
> +               smu_v11_0_i2c_eeprom_control_fini(&control->eeprom_accessor);
>                 break;
>
>         default:
> diff --git a/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
> new file mode 100644
> index 0000000..24405fa
> --- /dev/null
> +++ b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
> @@ -0,0 +1,710 @@
> +/*
> + * 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 "smuio/smuio_11_0_0_offset.h"
> +#include "smuio/smuio_11_0_0_sh_mask.h"
> +
> +#include "smu_v11_0_i2c.h"
> +#include "amdgpu.h"
> +#include "soc15_common.h"
> +#include <drm/drm_fixed.h>
> +#include "amdgpu_amdkfd.h"
> +#include <linux/i2c.h>
> +#include "amdgpu_ras.h"
> +
> +/* error codes */
> +#define I2C_OK                         0
> +#define I2C_NAK_7B_ADDR_NOACK          1
> +#define I2C_NAK_TXDATA_NOACK           2
> +#define I2C_TIMEOUT                    4
> +#define I2C_SW_TIMEOUT                 8
> +#define I2C_ABORT                      0x10
> +
> +/* I2C transaction flags */
> +#define I2C_NO_STOP    1
> +#define I2C_RESTART    2
> +
> +#define to_amdgpu_device(x) (container_of(x, struct amdgpu_ras, eeprom_control.eeprom_accessor))->adev
> +#define to_eeprom_control(x) container_of(x, struct amdgpu_ras_eeprom_control, eeprom_accessor)
> +
> +static void smu_v11_0_i2c_dis_clock_gating(struct i2c_adapter *control)
> +{
> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> +       uint32_t reg;
> +
> +       reg = RREG32_SOC15(SMUIO, 0, mmSMUIO_PWRMGT);
> +       reg = REG_SET_FIELD(reg, SMUIO_PWRMGT, i2c_clk_gate_en, 0);
> +       WREG32_SOC15(SMUIO, 0, mmSMUIO_PWRMGT, reg);
> +}

Do we need to worry about re-enabling clock gating when we are done?


> +
> +
> +static void smu_v11_0_i2c_enable(struct i2c_adapter *control, bool enable)
> +{
> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> +
> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, enable ? 1 : 0);
> +}
> +
> +static void smu_v11_0_i2c_clear_status(struct i2c_adapter *control)
> +{
> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> +       /* do */
> +       {
> +               RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CLR_INTR);
> +
> +       } /* while (reg_CKSVII2C_ic_clr_intr == 0) */
> +}
> +
> +static void smu_v11_0_i2c_configure(struct i2c_adapter *control)
> +{
> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> +       uint32_t reg = 0;
> +
> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_SLAVE_DISABLE, 1);
> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_RESTART_EN, 1);
> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_10BITADDR_MASTER, 0);
> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_10BITADDR_SLAVE, 0);
> +       /* Standard mode */
> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_MAX_SPEED_MODE, 2);
> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_MASTER_MODE, 1);
> +
> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CON, reg);
> +}
> +
> +static void smu_v11_0_i2c_set_clock(struct i2c_adapter *control)
> +{
> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> +
> +       /*
> +        * Standard mode speed, These values are taken from SMUIO MAS,
> +        * but are different from what is given is
> +        * Synopsys spec. The values here are based on assumption
> +        * that refclock is 100MHz
> +        *
> +        * Configuration for standard mode; Speed = 100kbps
> +        * Scale linearly, for now only support standard speed clock
> +        * This will work only with 100M ref clock
> +        *
> +        * TBD:Change the calculation to take into account ref clock values also.
> +        */
> +
> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_FS_SPKLEN, 2);
> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SS_SCL_HCNT, 120);
> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SS_SCL_LCNT, 130);
> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SDA_HOLD, 20);
> +}
> +
> +static void smu_v11_0_i2c_set_address(struct i2c_adapter *control, uint8_t address)
> +{
> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> +
> +       /* Convert fromr 8-bit to 7-bit address */
> +       address >>= 1;
> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TAR, (address & 0xFF));
> +}
> +
> +static uint32_t smu_v11_0_i2c_poll_tx_status(struct i2c_adapter *control)
> +{
> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> +       uint32_t ret = I2C_OK;
> +       uint32_t reg, reg_c_tx_abrt_source;
> +
> +       /*Check if transmission is completed */
> +       unsigned long  timeout_counter = jiffies + msecs_to_jiffies(20);
> +
> +       do {
> +               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
> +
> +               if (time_after(jiffies, timeout_counter)) {
> +                       ret |= I2C_SW_TIMEOUT;
> +                       break;
> +               }
> +       } while (REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFE) == 0);
> +
> +       if (ret != I2C_OK)
> +               return ret;
> +
> +       /* This only checks if NAK is received and transaction got aborted */
> +       reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_INTR_STAT);
> +
> +       if (REG_GET_FIELD(reg, CKSVII2C_IC_INTR_STAT, R_TX_ABRT) == 1) {
> +               reg_c_tx_abrt_source = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TX_ABRT_SOURCE);
> +               DRM_INFO("TX was terminated, IC_TX_ABRT_SOURCE val is:%x", reg_c_tx_abrt_source);
> +
> +               /* Check for stop due to NACK */
> +               if (REG_GET_FIELD(reg_c_tx_abrt_source,
> +                                 CKSVII2C_IC_TX_ABRT_SOURCE,
> +                                 ABRT_TXDATA_NOACK) == 1) {
> +
> +                       ret |= I2C_NAK_TXDATA_NOACK;
> +
> +               } else if (REG_GET_FIELD(reg_c_tx_abrt_source,
> +                                        CKSVII2C_IC_TX_ABRT_SOURCE,
> +                                        ABRT_7B_ADDR_NOACK) == 1) {
> +
> +                       ret |= I2C_NAK_7B_ADDR_NOACK;
> +               } else {
> +                       ret |= I2C_ABORT;
> +               }
> +
> +               smu_v11_0_i2c_clear_status(control);
> +       }
> +
> +       return ret;
> +}
> +
> +static uint32_t smu_v11_0_i2c_poll_rx_status(struct i2c_adapter *control)
> +{
> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> +       uint32_t ret = I2C_OK;
> +       uint32_t reg_ic_status, reg_c_tx_abrt_source;
> +
> +       reg_c_tx_abrt_source = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TX_ABRT_SOURCE);
> +
> +       /* If slave is not present */
> +       if (REG_GET_FIELD(reg_c_tx_abrt_source,
> +                         CKSVII2C_IC_TX_ABRT_SOURCE,
> +                         ABRT_7B_ADDR_NOACK) == 1) {
> +               ret |= I2C_NAK_7B_ADDR_NOACK;
> +
> +               smu_v11_0_i2c_clear_status(control);
> +       } else {  /* wait till some data is there in RXFIFO */
> +               /* Poll for some byte in RXFIFO */
> +               unsigned long  timeout_counter = jiffies + msecs_to_jiffies(20);
> +
> +               do {
> +                       reg_ic_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
> +
> +                       if (time_after(jiffies, timeout_counter)) {
> +                               ret |= I2C_SW_TIMEOUT;
> +                               break;
> +                       }
> +               } while (REG_GET_FIELD(reg_ic_status, CKSVII2C_IC_STATUS, RFNE) == 0);
> +       }
> +
> +       return ret;
> +}
> +
> +
> +
> +
> +/**
> + * smu_v11_0_i2c_transmit - Send a block of data over the I2C bus to a slave device.
> + *
> + * @address: The I2C address of the slave device.
> + * @data: The data to transmit over the bus.
> + * @numbytes: The amount of data to transmit.
> + * @i2c_flag: Flags for transmission
> + *
> + * Returns 0 on success or error.
> + */
> +static uint32_t smu_v11_0_i2c_transmit(struct i2c_adapter *control,
> +                                 uint8_t address, uint8_t *data,
> +                                 uint32_t numbytes, uint32_t i2c_flag)
> +{
> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> +       uint32_t bytes_sent, reg, ret = 0;
> +       unsigned long  timeout_counter;
> +
> +       bytes_sent = 0;
> +
> +       DRM_DEBUG_DRIVER("I2C_Transmit(), address = %x, bytes = %d , data: ",
> +                (uint16_t)address, numbytes);
> +
> +       if (drm_debug & DRM_UT_DRIVER) {
> +               print_hex_dump(KERN_INFO, "data: ", DUMP_PREFIX_NONE,
> +                              16, 1, data, numbytes, false);
> +       }
> +
> +       /* Set the I2C slave address */
> +       smu_v11_0_i2c_set_address(control, address);
> +       /* Enable I2C */
> +       smu_v11_0_i2c_enable(control, true);
> +
> +       /* Clear status bits */
> +       smu_v11_0_i2c_clear_status(control);
> +
> +
> +       timeout_counter = jiffies + msecs_to_jiffies(20);
> +
> +       while (numbytes > 0) {
> +               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
> +               if (REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFNF)) {
> +                       do {
> +                               reg = 0;
> +                               /*
> +                                * Prepare transaction, no need to set RESTART. I2C engine will send
> +                                * START as soon as it sees data in TXFIFO
> +                                */
> +                               if (bytes_sent == 0)
> +                                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, RESTART,
> +                                                           (i2c_flag & I2C_RESTART) ? 1 : 0);
> +                               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, data[bytes_sent]);
> +
> +                               /* determine if we need to send STOP bit or not */
> +                               if (numbytes == 1)
> +                                       /* Final transaction, so send stop unless I2C_NO_STOP */
> +                                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, STOP,
> +                                                           (i2c_flag & I2C_NO_STOP) ? 0 : 1);
> +                               /* Write */
> +                               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 0);
> +                               WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg);
> +
> +                               /* Record that the bytes were transmitted */
> +                               bytes_sent++;
> +                               numbytes--;
> +
> +                               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
> +
> +                       } while (numbytes &&  REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFNF));
> +               }
> +
> +
> +               if (time_after(jiffies, timeout_counter)) {
> +                       ret |= I2C_SW_TIMEOUT;
> +                       goto err;
> +               }
> +       }
> +
> +       ret = smu_v11_0_i2c_poll_tx_status(control);
> +
> +err:
> +       /* Any error, no point in proceeding */
> +       if (ret != I2C_OK) {
> +               if (ret & I2C_SW_TIMEOUT)
> +                       DRM_ERROR("TIMEOUT ERROR !!!");
> +
> +               if (ret & I2C_NAK_7B_ADDR_NOACK)
> +                       DRM_ERROR("Received I2C_NAK_7B_ADDR_NOACK !!!");
> +
> +
> +               if (ret & I2C_NAK_TXDATA_NOACK)
> +                       DRM_ERROR("Received I2C_NAK_TXDATA_NOACK !!!");
> +       }
> +
> +       return ret;
> +}
> +
> +
> +/**
> + * smu_v11_0_i2c_receive - Receive a block of data over the I2C bus from a slave device.
> + *
> + * @address: The I2C address of the slave device.
> + * @numbytes: The amount of data to transmit.
> + * @i2c_flag: Flags for transmission
> + *
> + * Returns 0 on success or error.
> + */
> +static uint32_t smu_v11_0_i2c_receive(struct i2c_adapter *control,
> +                                uint8_t address, uint8_t *data,
> +                                uint32_t numbytes, uint8_t i2c_flag)
> +{
> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> +       uint32_t bytes_received, ret = I2C_OK;
> +
> +       bytes_received = 0;
> +
> +       /* Set the I2C slave address */
> +       smu_v11_0_i2c_set_address(control, address);
> +
> +       /* Enable I2C */
> +       smu_v11_0_i2c_enable(control, true);
> +
> +       while (numbytes > 0) {
> +               uint32_t reg = 0;
> +
> +               smu_v11_0_i2c_clear_status(control);
> +
> +
> +               /* Prepare transaction */
> +
> +               /* Each time we disable I2C, so this is not a restart */
> +               if (bytes_received == 0)
> +                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, RESTART,
> +                                           (i2c_flag & I2C_RESTART) ? 1 : 0);
> +
> +               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, 0);
> +               /* Read */
> +               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 1);
> +
> +               /* Transmitting last byte */
> +               if (numbytes == 1)
> +                       /* Final transaction, so send stop if requested */
> +                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, STOP,
> +                                           (i2c_flag & I2C_NO_STOP) ? 0 : 1);
> +
> +               WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg);
> +
> +               ret = smu_v11_0_i2c_poll_rx_status(control);
> +
> +               /* Any error, no point in proceeding */
> +               if (ret != I2C_OK) {
> +                       if (ret & I2C_SW_TIMEOUT)
> +                               DRM_ERROR("TIMEOUT ERROR !!!");
> +
> +                       if (ret & I2C_NAK_7B_ADDR_NOACK)
> +                               DRM_ERROR("Received I2C_NAK_7B_ADDR_NOACK !!!");
> +
> +                       if (ret & I2C_NAK_TXDATA_NOACK)
> +                               DRM_ERROR("Received I2C_NAK_TXDATA_NOACK !!!");
> +
> +                       break;
> +               }
> +
> +               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD);
> +               data[bytes_received] = REG_GET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT);
> +
> +               /* Record that the bytes were received */
> +               bytes_received++;
> +               numbytes--;
> +       }
> +
> +       DRM_DEBUG_DRIVER("I2C_Receive(), address = %x, bytes = %d, data :",
> +                 (uint16_t)address, bytes_received);
> +
> +       if (drm_debug & DRM_UT_DRIVER) {
> +               print_hex_dump(KERN_INFO, "data: ", DUMP_PREFIX_NONE,
> +                              16, 1, data, bytes_received, false);
> +       }
> +
> +       return ret;
> +}
> +
> +static void smu_v11_0_i2c_abort(struct i2c_adapter *control)
> +{
> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> +       uint32_t reg = 0;
> +
> +       /* Enable I2C engine; */
> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_ENABLE, ENABLE, 1);
> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, reg);
> +
> +       /* Abort previous transaction */
> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_ENABLE, ABORT, 1);
> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, reg);
> +
> +       DRM_DEBUG_DRIVER("I2C_Abort() Done.");
> +}
> +
> +
> +static bool smu_v11_0_i2c_activity_done(struct i2c_adapter *control)
> +{
> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> +
> +       const uint32_t IDLE_TIMEOUT = 1024;
> +       uint32_t timeoutCount = 0;

kernel coding style prefers non CamelCase.  E.g., timeout_count.

> +       uint32_t reg_ic_enable, reg_ic_enable_status, reg_ic_clr_activity;
> +
> +       reg_ic_enable_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE_STATUS);
> +       reg_ic_enable = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE);
> +
> +
> +       if ((REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) &&
> +           (REG_GET_FIELD(reg_ic_enable_status, CKSVII2C_IC_ENABLE_STATUS, IC_EN) == 1)) {
> +               /*
> +                * Nobody is using I2C engine, but engine remains active because
> +                * someone missed to send STOP
> +                */
> +               smu_v11_0_i2c_abort(control);
> +       } else if (REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) {
> +               /* Nobody is using I2C engine */
> +               return true;
> +       }
> +
> +       /* Keep reading activity bit until it's cleared */
> +       do {
> +               reg_ic_clr_activity = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CLR_ACTIVITY);
> +
> +               if (REG_GET_FIELD(reg_ic_clr_activity,
> +                   CKSVII2C_IC_CLR_ACTIVITY, CLR_ACTIVITY) == 0)
> +                       return true;
> +
> +               ++timeoutCount;
> +
> +       } while (timeoutCount < IDLE_TIMEOUT);
> +
> +       return false;
> +}
> +
> +static void smu_v11_0_i2c_init(struct i2c_adapter *control)
> +{
> +       /* Disable clock gating */
> +       smu_v11_0_i2c_dis_clock_gating(control);
> +
> +       if (!smu_v11_0_i2c_activity_done(control))
> +               DRM_WARN("I2C busy !");
> +
> +       /* Disable I2C */
> +       smu_v11_0_i2c_enable(control, false);
> +
> +       /* Configure I2C to operate as master and in standard mode */
> +       smu_v11_0_i2c_configure(control);
> +
> +       /* Initialize the clock to 50 kHz default */
> +       smu_v11_0_i2c_set_clock(control);
> +
> +}
> +
> +static void smu_v11_0_i2c_fini(struct i2c_adapter *control)
> +{
> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> +       uint32_t reg_ic_enable_status, reg_ic_enable;
> +
> +       smu_v11_0_i2c_enable(control, false);
> +
> +       /* Double check if disabled, else force abort */
> +       reg_ic_enable_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE_STATUS);
> +       reg_ic_enable = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE);
> +
> +       if ((REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) &&
> +           (REG_GET_FIELD(reg_ic_enable_status,
> +                          CKSVII2C_IC_ENABLE_STATUS, IC_EN) == 1)) {
> +               /*
> +                * Nobody is using I2C engine, but engine remains active because
> +                * someone missed to send STOP
> +                */
> +               smu_v11_0_i2c_abort(control);
> +       }
> +
> +}
> +
> +static bool smu_v11_0_i2c_bus_lock(struct i2c_adapter *control)
> +{
> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> +
> +       /* Send  PPSMC_MSG_RequestI2CBus */
> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access)
> +               goto fail;
> +
> +
> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access(adev->powerplay.pp_handle, true))
> +               return true;
> +
> +fail:
> +       return false;
> +}
> +
> +static bool smu_v11_0_i2c_bus_unlock(struct i2c_adapter *control)
> +{
> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> +
> +       /* Send  PPSMC_MSG_RequestI2CBus */
> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access)
> +               goto fail;
> +
> +       /* Send  PPSMC_MSG_ReleaseI2CBus */
> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access(adev->powerplay.pp_handle,
> +                                                            false))
> +               return true;
> +
> +fail:
> +       return false;
> +}
> +
> +/***************************** EEPROM I2C GLUE ****************************/
> +
> +static uint32_t smu_v11_0_i2c_eeprom_read_data(struct i2c_adapter *control,
> +                                              uint8_t address,
> +                                              uint8_t *data,
> +                                              uint32_t numbytes)
> +{
> +       uint32_t  ret = 0;
> +
> +       /* First 2 bytes are dummy write to set EEPROM address */
> +       ret = smu_v11_0_i2c_transmit(control, address, data, 2, I2C_NO_STOP);
> +       if (ret != I2C_OK)
> +               goto fail;
> +
> +       /* Now read data starting with that address */
> +       ret = smu_v11_0_i2c_receive(control, address, data + 2, numbytes - 2,
> +                                   I2C_RESTART);
> +
> +fail:
> +       if (ret != I2C_OK)
> +               DRM_ERROR("ReadData() - I2C error occurred :%x", ret);
> +
> +       return ret;
> +}
> +
> +static uint32_t smu_v11_0_i2c_eeprom_write_data(struct i2c_adapter *control,
> +                                               uint8_t address,
> +                                               uint8_t *data,
> +                                               uint32_t numbytes)
> +{
> +       uint32_t  ret;
> +
> +       ret = smu_v11_0_i2c_transmit(control, address, data, numbytes, 0);
> +
> +       if (ret != I2C_OK)
> +               DRM_ERROR("WriteI2CData() - I2C error occurred :%x", ret);
> +       else
> +               /*
> +                * According to EEPROM spec there is a MAX of 10 ms required for
> +                * EEPROM to flush internal RX buffer after STOP was issued at the
> +                * end of write transaction. During this time the EEPROM will not be
> +                * responsive to any more commands - so wait a bit more.
> +                *
> +                * TODO Improve to wait for first ACK for slave address after
> +                * internal write cycle done.
> +                */
> +               msleep(10);
> +
> +       return ret;
> +
> +}
> +
> +static void lock_bus(struct i2c_adapter *i2c, unsigned int flags)
> +{
> +       struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c);
> +
> +       if (!smu_v11_0_i2c_bus_lock(i2c)) {
> +               DRM_ERROR("Failed to lock the bus from SMU");
> +               return;
> +       }
> +
> +       control->bus_locked = true;
> +}
> +
> +static int trylock_bus(struct i2c_adapter *i2c, unsigned int flags)
> +{
> +       WARN_ONCE(1, "This operation not supposed to run in atomic context!");
> +       return false;
> +}
> +
> +static void unlock_bus(struct i2c_adapter *i2c, unsigned int flags)
> +{
> +       struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c);
> +
> +       if (!smu_v11_0_i2c_bus_unlock(i2c)) {
> +               DRM_ERROR("Failed to unlock the bus from SMU");
> +               return;
> +       }
> +
> +       control->bus_locked = false;
> +}
> +
> +static const struct i2c_lock_operations smu_v11_0_i2c_i2c_lock_ops = {
> +       .lock_bus = lock_bus,
> +       .trylock_bus = trylock_bus,
> +       .unlock_bus = unlock_bus,
> +};
> +
> +static int smu_v11_0_i2c_eeprom_i2c_xfer(struct i2c_adapter *i2c_adap,
> +                             struct i2c_msg *msgs, int num)
> +{
> +       int i, ret;
> +       struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c_adap);
> +
> +       if (!control->bus_locked) {
> +               DRM_ERROR("I2C bus unlocked, stopping transaction!");
> +               return -EIO;
> +       }
> +
> +       smu_v11_0_i2c_init(i2c_adap);
> +
> +       for (i = 0; i < num; i++) {
> +               if (msgs[i].flags & I2C_M_RD)
> +                       ret = smu_v11_0_i2c_eeprom_read_data(i2c_adap,
> +                                                       (uint8_t)msgs[i].addr,
> +                                                       msgs[i].buf, msgs[i].len);
> +               else
> +                       ret = smu_v11_0_i2c_eeprom_write_data(i2c_adap,
> +                                                        (uint8_t)msgs[i].addr,
> +                                                        msgs[i].buf, msgs[i].len);
> +
> +               if (ret != I2C_OK) {
> +                       num = -EIO;
> +                       break;
> +               }
> +       }
> +
> +       smu_v11_0_i2c_fini(i2c_adap);
> +       return num;
> +}
> +
> +static u32 smu_v11_0_i2c_eeprom_i2c_func(struct i2c_adapter *adap)
> +{
> +       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +
> +static const struct i2c_algorithm smu_v11_0_i2c_eeprom_i2c_algo = {
> +       .master_xfer = smu_v11_0_i2c_eeprom_i2c_xfer,
> +       .functionality = smu_v11_0_i2c_eeprom_i2c_func,
> +};
> +
> +int smu_v11_0_i2c_eeprom_control_init(struct i2c_adapter *control)
> +{
> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> +       int res;
> +
> +       control->owner = THIS_MODULE;
> +       control->class = I2C_CLASS_SPD;
> +       control->dev.parent = &adev->pdev->dev;
> +       control->algo = &smu_v11_0_i2c_eeprom_i2c_algo;
> +       snprintf(control->name, sizeof(control->name), "RAS EEPROM");
> +       control->lock_ops = &smu_v11_0_i2c_i2c_lock_ops;
> +
> +       res = i2c_add_adapter(control);
> +       if (res)
> +               DRM_ERROR("Failed to register hw i2c, err: %d\n", res);
> +
> +       return res;
> +}
> +
> +void smu_v11_0_i2c_eeprom_control_fini(struct i2c_adapter *control)
> +{
> +       i2c_del_adapter(control);
> +}
> +
> +#define I2C_TARGET_ADDR 0xA0
> +
> +bool smu_v11_0_i2c_test_bus(struct i2c_adapter *control)
> +{
> +
> +       uint32_t ret = I2C_OK;
> +       uint8_t data[6] = {0xf, 0, 0xde, 0xad, 0xbe, 0xef};
> +
> +
> +       DRM_INFO("Begin");
> +
> +       if (!smu_v11_0_i2c_bus_lock(control)) {
> +               DRM_ERROR("Failed to lock the bus!.");
> +               return false;
> +       }
> +
> +       smu_v11_0_i2c_init(control);
> +
> +       /* Write 0xde to address 0x0000 on the EEPROM */
> +       ret = smu_v11_0_i2c_eeprom_write_data(control, I2C_TARGET_ADDR, data, 6);
> +
> +       ret = smu_v11_0_i2c_eeprom_read_data(control, I2C_TARGET_ADDR, data, 6);
> +
> +       smu_v11_0_i2c_fini(control);
> +
> +       smu_v11_0_i2c_bus_unlock(control);
> +
> +
> +       DRM_INFO("End");
> +       return true;
> +}

Leftover debugging function?

> diff --git a/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
> new file mode 100644
> index 0000000..973f28d
> --- /dev/null
> +++ b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
> @@ -0,0 +1,34 @@
> +/*
> + * 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.
> + *
> + */
> +
> +#ifndef SMU_V11_I2C_CONTROL_H
> +#define SMU_V11_I2C_CONTROL_H
> +
> +#include <linux/types.h>
> +
> +struct i2c_adapter;
> +
> +int smu_v11_0_i2c_eeprom_control_init(struct i2c_adapter *control);
> +void smu_v11_0_i2c_eeprom_control_fini(struct i2c_adapter *control);
> +
> +#endif
> --
> 2.7.4
>
> _______________________________________________
> amd-gfx mailing list
> amd-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/amd-gfx
_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 2/4] drm/amd: Import smuio_11_0 headres for EEPROM access on Vega20
       [not found]     ` <1566417719-1528-3-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
@ 2019-08-22  2:33       ` Alex Deucher
  0 siblings, 0 replies; 18+ messages in thread
From: Alex Deucher @ 2019-08-22  2:33 UTC (permalink / raw)
  To: Andrey Grodzovsky
  Cc: David Panariti, xinhui pan, Lijo.Lazar-5C7GfCeVMHo, amd-gfx list,
	Deucher, Alexander, Luben.Tuikov-5C7GfCeVMHo, Russell, Kent,
	Tao Zhou, Quan, Evan, Hawking Zhang

On Wed, Aug 21, 2019 at 4:02 PM Andrey Grodzovsky
<andrey.grodzovsky@amd.com> wrote:
>
> v3: Merge CKSVII2C_IC regs into exsisting headers.
>
> Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@amd.com>

Reviewed-by: Alex Deucher <alexander.deucher@amd.com>

> ---
>  .../include/asic_reg/smuio/smuio_11_0_0_offset.h   |  92 ++++++++
>  .../include/asic_reg/smuio/smuio_11_0_0_sh_mask.h  | 231 +++++++++++++++++++++
>  2 files changed, 323 insertions(+)
>
> diff --git a/drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_11_0_0_offset.h b/drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_11_0_0_offset.h
> index 5df7048..d387605 100644
> --- a/drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_11_0_0_offset.h
> +++ b/drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_11_0_0_offset.h
> @@ -29,6 +29,98 @@
>  #define mmSMUSVI0_TEL_PLANE0_BASE_IDX                                                                  0
>  #define mmSMUIO_MCM_CONFIG                                                                             0x0024
>  #define mmSMUIO_MCM_CONFIG_BASE_IDX                                                                    0
> +#define mmCKSVII2C_IC_CON                                                                              0x0040
> +#define mmCKSVII2C_IC_CON_BASE_IDX                                                                     0
> +#define mmCKSVII2C_IC_TAR                                                                              0x0041
> +#define mmCKSVII2C_IC_TAR_BASE_IDX                                                                     0
> +#define mmCKSVII2C_IC_SAR                                                                              0x0042
> +#define mmCKSVII2C_IC_SAR_BASE_IDX                                                                     0
> +#define mmCKSVII2C_IC_HS_MADDR                                                                         0x0043
> +#define mmCKSVII2C_IC_HS_MADDR_BASE_IDX                                                                0
> +#define mmCKSVII2C_IC_DATA_CMD                                                                         0x0044
> +#define mmCKSVII2C_IC_DATA_CMD_BASE_IDX                                                                0
> +#define mmCKSVII2C_IC_SS_SCL_HCNT                                                                      0x0045
> +#define mmCKSVII2C_IC_SS_SCL_HCNT_BASE_IDX                                                             0
> +#define mmCKSVII2C_IC_SS_SCL_LCNT                                                                      0x0046
> +#define mmCKSVII2C_IC_SS_SCL_LCNT_BASE_IDX                                                             0
> +#define mmCKSVII2C_IC_FS_SCL_HCNT                                                                      0x0047
> +#define mmCKSVII2C_IC_FS_SCL_HCNT_BASE_IDX                                                             0
> +#define mmCKSVII2C_IC_FS_SCL_LCNT                                                                      0x0048
> +#define mmCKSVII2C_IC_FS_SCL_LCNT_BASE_IDX                                                             0
> +#define mmCKSVII2C_IC_HS_SCL_HCNT                                                                      0x0049
> +#define mmCKSVII2C_IC_HS_SCL_HCNT_BASE_IDX                                                             0
> +#define mmCKSVII2C_IC_HS_SCL_LCNT                                                                      0x004a
> +#define mmCKSVII2C_IC_HS_SCL_LCNT_BASE_IDX                                                             0
> +#define mmCKSVII2C_IC_INTR_STAT                                                                        0x004b
> +#define mmCKSVII2C_IC_INTR_STAT_BASE_IDX                                                               0
> +#define mmCKSVII2C_IC_INTR_MASK                                                                        0x004c
> +#define mmCKSVII2C_IC_INTR_MASK_BASE_IDX                                                               0
> +#define mmCKSVII2C_IC_RAW_INTR_STAT                                                                    0x004d
> +#define mmCKSVII2C_IC_RAW_INTR_STAT_BASE_IDX                                                           0
> +#define mmCKSVII2C_IC_RX_TL                                                                            0x004e
> +#define mmCKSVII2C_IC_RX_TL_BASE_IDX                                                                   0
> +#define mmCKSVII2C_IC_TX_TL                                                                            0x004f
> +#define mmCKSVII2C_IC_TX_TL_BASE_IDX                                                                   0
> +#define mmCKSVII2C_IC_CLR_INTR                                                                         0x0050
> +#define mmCKSVII2C_IC_CLR_INTR_BASE_IDX                                                                0
> +#define mmCKSVII2C_IC_CLR_RX_UNDER                                                                     0x0051
> +#define mmCKSVII2C_IC_CLR_RX_UNDER_BASE_IDX                                                            0
> +#define mmCKSVII2C_IC_CLR_RX_OVER                                                                      0x0052
> +#define mmCKSVII2C_IC_CLR_RX_OVER_BASE_IDX                                                             0
> +#define mmCKSVII2C_IC_CLR_TX_OVER                                                                      0x0053
> +#define mmCKSVII2C_IC_CLR_TX_OVER_BASE_IDX                                                             0
> +#define mmCKSVII2C_IC_CLR_RD_REQ                                                                       0x0054
> +#define mmCKSVII2C_IC_CLR_RD_REQ_BASE_IDX                                                              0
> +#define mmCKSVII2C_IC_CLR_TX_ABRT                                                                      0x0055
> +#define mmCKSVII2C_IC_CLR_TX_ABRT_BASE_IDX                                                             0
> +#define mmCKSVII2C_IC_CLR_RX_DONE                                                                      0x0056
> +#define mmCKSVII2C_IC_CLR_RX_DONE_BASE_IDX                                                             0
> +#define mmCKSVII2C_IC_CLR_ACTIVITY                                                                     0x0057
> +#define mmCKSVII2C_IC_CLR_ACTIVITY_BASE_IDX                                                            0
> +#define mmCKSVII2C_IC_CLR_STOP_DET                                                                     0x0058
> +#define mmCKSVII2C_IC_CLR_STOP_DET_BASE_IDX                                                            0
> +#define mmCKSVII2C_IC_CLR_START_DET                                                                    0x0059
> +#define mmCKSVII2C_IC_CLR_START_DET_BASE_IDX                                                           0
> +#define mmCKSVII2C_IC_CLR_GEN_CALL                                                                     0x005a
> +#define mmCKSVII2C_IC_CLR_GEN_CALL_BASE_IDX                                                            0
> +#define mmCKSVII2C_IC_ENABLE                                                                           0x005b
> +#define mmCKSVII2C_IC_ENABLE_BASE_IDX                                                                  0
> +#define mmCKSVII2C_IC_STATUS                                                                           0x005c
> +#define mmCKSVII2C_IC_STATUS_BASE_IDX                                                                  0
> +#define mmCKSVII2C_IC_TXFLR                                                                            0x005d
> +#define mmCKSVII2C_IC_TXFLR_BASE_IDX                                                                   0
> +#define mmCKSVII2C_IC_RXFLR                                                                            0x005e
> +#define mmCKSVII2C_IC_RXFLR_BASE_IDX                                                                   0
> +#define mmCKSVII2C_IC_SDA_HOLD                                                                         0x005f
> +#define mmCKSVII2C_IC_SDA_HOLD_BASE_IDX                                                                0
> +#define mmCKSVII2C_IC_TX_ABRT_SOURCE                                                                   0x0060
> +#define mmCKSVII2C_IC_TX_ABRT_SOURCE_BASE_IDX                                                          0
> +#define mmCKSVII2C_IC_SLV_DATA_NACK_ONLY                                                               0x0061
> +#define mmCKSVII2C_IC_SLV_DATA_NACK_ONLY_BASE_IDX                                                      0
> +#define mmCKSVII2C_IC_DMA_CR                                                                           0x0062
> +#define mmCKSVII2C_IC_DMA_CR_BASE_IDX                                                                  0
> +#define mmCKSVII2C_IC_DMA_TDLR                                                                         0x0063
> +#define mmCKSVII2C_IC_DMA_TDLR_BASE_IDX                                                                0
> +#define mmCKSVII2C_IC_DMA_RDLR                                                                         0x0064
> +#define mmCKSVII2C_IC_DMA_RDLR_BASE_IDX                                                                0
> +#define mmCKSVII2C_IC_SDA_SETUP                                                                        0x0065
> +#define mmCKSVII2C_IC_SDA_SETUP_BASE_IDX                                                               0
> +#define mmCKSVII2C_IC_ACK_GENERAL_CALL                                                                 0x0066
> +#define mmCKSVII2C_IC_ACK_GENERAL_CALL_BASE_IDX                                                        0
> +#define mmCKSVII2C_IC_ENABLE_STATUS                                                                    0x0067
> +#define mmCKSVII2C_IC_ENABLE_STATUS_BASE_IDX                                                           0
> +#define mmCKSVII2C_IC_FS_SPKLEN                                                                        0x0068
> +#define mmCKSVII2C_IC_FS_SPKLEN_BASE_IDX                                                               0
> +#define mmCKSVII2C_IC_HS_SPKLEN                                                                        0x0069
> +#define mmCKSVII2C_IC_HS_SPKLEN_BASE_IDX                                                               0
> +#define mmCKSVII2C_IC_CLR_RESTART_DET                                                                  0x006a
> +#define mmCKSVII2C_IC_CLR_RESTART_DET_BASE_IDX                                                         0
> +#define mmCKSVII2C_IC_COMP_PARAM_1                                                                     0x006b
> +#define mmCKSVII2C_IC_COMP_PARAM_1_BASE_IDX                                                            0
> +#define mmCKSVII2C_IC_COMP_VERSION                                                                     0x006c
> +#define mmCKSVII2C_IC_COMP_VERSION_BASE_IDX                                                            0
> +#define mmCKSVII2C_IC_COMP_TYPE                                                                        0x006d
> +#define mmCKSVII2C_IC_COMP_TYPE_BASE_IDX                                                               0
>  #define mmSMUIO_MP_RESET_INTR                                                                          0x00c1
>  #define mmSMUIO_MP_RESET_INTR_BASE_IDX                                                                 0
>  #define mmSMUIO_SOC_HALT                                                                               0x00c2
> diff --git a/drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_11_0_0_sh_mask.h b/drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_11_0_0_sh_mask.h
> index 2379615..f8afa351 100644
> --- a/drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_11_0_0_sh_mask.h
> +++ b/drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_11_0_0_sh_mask.h
> @@ -37,6 +37,237 @@
>  #define SMUIO_MCM_CONFIG__PKG_TYPE_MASK                                                                       0x0000001CL
>  #define SMUIO_MCM_CONFIG__SOCKET_ID_MASK                                                                      0x00000020L
>  #define SMUIO_MCM_CONFIG__PKG_SUBTYPE_MASK                                                                    0x000000C0L
> +//CKSVII2C_IC_CON
> +#define CKSVII2C_IC_CON__IC_MASTER_MODE__SHIFT                                                                0x0
> +#define CKSVII2C_IC_CON__IC_MAX_SPEED_MODE__SHIFT                                                             0x1
> +#define CKSVII2C_IC_CON__IC_10BITADDR_SLAVE__SHIFT                                                            0x3
> +#define CKSVII2C_IC_CON__IC_10BITADDR_MASTER__SHIFT                                                           0x4
> +#define CKSVII2C_IC_CON__IC_RESTART_EN__SHIFT                                                                 0x5
> +#define CKSVII2C_IC_CON__IC_SLAVE_DISABLE__SHIFT                                                              0x6
> +#define CKSVII2C_IC_CON__STOP_DET_IFADDRESSED__SHIFT                                                          0x7
> +#define CKSVII2C_IC_CON__TX_EMPTY_CTRL__SHIFT                                                                 0x8
> +#define CKSVII2C_IC_CON__RX_FIFO_FULL_HLD_CTRL__SHIFT                                                         0x9
> +#define CKSVII2C_IC_CON__IC_MASTER_MODE_MASK                                                                  0x00000001L
> +#define CKSVII2C_IC_CON__IC_MAX_SPEED_MODE_MASK                                                               0x00000006L
> +#define CKSVII2C_IC_CON__IC_10BITADDR_SLAVE_MASK                                                              0x00000008L
> +#define CKSVII2C_IC_CON__IC_10BITADDR_MASTER_MASK                                                             0x00000010L
> +#define CKSVII2C_IC_CON__IC_RESTART_EN_MASK                                                                   0x00000020L
> +#define CKSVII2C_IC_CON__IC_SLAVE_DISABLE_MASK                                                                0x00000040L
> +#define CKSVII2C_IC_CON__STOP_DET_IFADDRESSED_MASK                                                            0x00000080L
> +#define CKSVII2C_IC_CON__TX_EMPTY_CTRL_MASK                                                                   0x00000100L
> +#define CKSVII2C_IC_CON__RX_FIFO_FULL_HLD_CTRL_MASK                                                           0x00000200L
> +//CKSVII2C_IC_TAR
> +#define CKSVII2C_IC_TAR__IC_TAR__SHIFT                                                                        0x0
> +#define CKSVII2C_IC_TAR__GC_OR_START__SHIFT                                                                   0xa
> +#define CKSVII2C_IC_TAR__SPECIAL__SHIFT                                                                       0xb
> +#define CKSVII2C_IC_TAR__IC_10BITADDR_MASTER__SHIFT                                                           0xc
> +#define CKSVII2C_IC_TAR__IC_TAR_MASK                                                                          0x000003FFL
> +#define CKSVII2C_IC_TAR__GC_OR_START_MASK                                                                     0x00000400L
> +#define CKSVII2C_IC_TAR__SPECIAL_MASK                                                                         0x00000800L
> +#define CKSVII2C_IC_TAR__IC_10BITADDR_MASTER_MASK                                                             0x00001000L
> +//CKSVII2C_IC_SAR
> +#define CKSVII2C_IC_SAR__IC_SAR__SHIFT                                                                        0x0
> +#define CKSVII2C_IC_SAR__IC_SAR_MASK                                                                          0x000003FFL
> +//CKSVII2C_IC_HS_MADDR
> +#define CKSVII2C_IC_HS_MADDR__IC_HS_MADDR__SHIFT                                                              0x0
> +#define CKSVII2C_IC_HS_MADDR__IC_HS_MADDR_MASK                                                                0x00000007L
> +//CKSVII2C_IC_DATA_CMD
> +#define CKSVII2C_IC_DATA_CMD__DAT__SHIFT                                                                      0x0
> +#define CKSVII2C_IC_DATA_CMD__CMD__SHIFT                                                                      0x8
> +#define CKSVII2C_IC_DATA_CMD__STOP__SHIFT                                                                     0x9
> +#define CKSVII2C_IC_DATA_CMD__RESTART__SHIFT                                                                  0xa
> +#define CKSVII2C_IC_DATA_CMD__DAT_MASK                                                                        0x000000FFL
> +#define CKSVII2C_IC_DATA_CMD__CMD_MASK                                                                        0x00000100L
> +#define CKSVII2C_IC_DATA_CMD__STOP_MASK                                                                       0x00000200L
> +#define CKSVII2C_IC_DATA_CMD__RESTART_MASK                                                                    0x00000400L
> +//CKSVII2C_IC_SS_SCL_HCNT
> +#define CKSVII2C_IC_SS_SCL_HCNT__IC_SS_SCL_HCNT__SHIFT                                                        0x0
> +#define CKSVII2C_IC_SS_SCL_HCNT__IC_SS_SCL_HCNT_MASK                                                          0x0000FFFFL
> +//CKSVII2C_IC_SS_SCL_LCNT
> +#define CKSVII2C_IC_SS_SCL_LCNT__IC_SS_SCL_LCNT__SHIFT                                                        0x0
> +#define CKSVII2C_IC_SS_SCL_LCNT__IC_SS_SCL_LCNT_MASK                                                          0x0000FFFFL
> +//CKSVII2C_IC_FS_SCL_HCNT
> +#define CKSVII2C_IC_FS_SCL_HCNT__IC_FS_SCL_HCNT__SHIFT                                                        0x0
> +#define CKSVII2C_IC_FS_SCL_HCNT__IC_FS_SCL_HCNT_MASK                                                          0x0000FFFFL
> +//CKSVII2C_IC_FS_SCL_LCNT
> +#define CKSVII2C_IC_FS_SCL_LCNT__IC_FS_SCL_LCNT__SHIFT                                                        0x0
> +#define CKSVII2C_IC_FS_SCL_LCNT__IC_FS_SCL_LCNT_MASK                                                          0x0000FFFFL
> +//CKSVII2C_IC_HS_SCL_HCNT
> +#define CKSVII2C_IC_HS_SCL_HCNT__IC_HS_SCL_HCNT__SHIFT                                                        0x0
> +#define CKSVII2C_IC_HS_SCL_HCNT__IC_HS_SCL_HCNT_MASK                                                          0x0000FFFFL
> +//CKSVII2C_IC_HS_SCL_LCNT
> +#define CKSVII2C_IC_HS_SCL_LCNT__IC_HS_SCL_LCNT__SHIFT                                                        0x0
> +#define CKSVII2C_IC_HS_SCL_LCNT__IC_HS_SCL_LCNT_MASK                                                          0x0000FFFFL
> +//CKSVII2C_IC_INTR_STAT
> +#define CKSVII2C_IC_INTR_STAT__R_RX_UNDER__SHIFT                                                              0x0
> +#define CKSVII2C_IC_INTR_STAT__R_RX_OVER__SHIFT                                                               0x1
> +#define CKSVII2C_IC_INTR_STAT__R_RX_FULL__SHIFT                                                               0x2
> +#define CKSVII2C_IC_INTR_STAT__R_TX_OVER__SHIFT                                                               0x3
> +#define CKSVII2C_IC_INTR_STAT__R_TX_EMPTY__SHIFT                                                              0x4
> +#define CKSVII2C_IC_INTR_STAT__R_RD_REQ__SHIFT                                                                0x5
> +#define CKSVII2C_IC_INTR_STAT__R_TX_ABRT__SHIFT                                                               0x6
> +#define CKSVII2C_IC_INTR_STAT__R_RX_DONE__SHIFT                                                               0x7
> +#define CKSVII2C_IC_INTR_STAT__R_ACTIVITY__SHIFT                                                              0x8
> +#define CKSVII2C_IC_INTR_STAT__R_STOP_DET__SHIFT                                                              0x9
> +#define CKSVII2C_IC_INTR_STAT__R_START_DET__SHIFT                                                             0xa
> +#define CKSVII2C_IC_INTR_STAT__R_GEN_CALL__SHIFT                                                              0xb
> +#define CKSVII2C_IC_INTR_STAT__R_RESTART_DET__SHIFT                                                           0xc
> +#define CKSVII2C_IC_INTR_STAT__R_MST_ON_HOLD__SHIFT                                                           0xd
> +#define CKSVII2C_IC_INTR_STAT__R_RX_UNDER_MASK                                                                0x00000001L
> +#define CKSVII2C_IC_INTR_STAT__R_RX_OVER_MASK                                                                 0x00000002L
> +#define CKSVII2C_IC_INTR_STAT__R_RX_FULL_MASK                                                                 0x00000004L
> +#define CKSVII2C_IC_INTR_STAT__R_TX_OVER_MASK                                                                 0x00000008L
> +#define CKSVII2C_IC_INTR_STAT__R_TX_EMPTY_MASK                                                                0x00000010L
> +#define CKSVII2C_IC_INTR_STAT__R_RD_REQ_MASK                                                                  0x00000020L
> +#define CKSVII2C_IC_INTR_STAT__R_TX_ABRT_MASK                                                                 0x00000040L
> +#define CKSVII2C_IC_INTR_STAT__R_RX_DONE_MASK                                                                 0x00000080L
> +#define CKSVII2C_IC_INTR_STAT__R_ACTIVITY_MASK                                                                0x00000100L
> +#define CKSVII2C_IC_INTR_STAT__R_STOP_DET_MASK                                                                0x00000200L
> +#define CKSVII2C_IC_INTR_STAT__R_START_DET_MASK                                                               0x00000400L
> +#define CKSVII2C_IC_INTR_STAT__R_GEN_CALL_MASK                                                                0x00000800L
> +#define CKSVII2C_IC_INTR_STAT__R_RESTART_DET_MASK                                                             0x00001000L
> +#define CKSVII2C_IC_INTR_STAT__R_MST_ON_HOLD_MASK                                                             0x00002000L
> +//CKSVII2C_IC_INTR_MASK
> +#define CKSVII2C_IC_INTR_MASK__M_RX_UNDER__SHIFT                                                              0x0
> +#define CKSVII2C_IC_INTR_MASK__M_RX_OVER__SHIFT                                                               0x1
> +#define CKSVII2C_IC_INTR_MASK__M_RX_FULL__SHIFT                                                               0x2
> +#define CKSVII2C_IC_INTR_MASK__M_TX_OVER__SHIFT                                                               0x3
> +#define CKSVII2C_IC_INTR_MASK__M_TX_EMPTY__SHIFT                                                              0x4
> +#define CKSVII2C_IC_INTR_MASK__M_RD_REQ__SHIFT                                                                0x5
> +#define CKSVII2C_IC_INTR_MASK__M_TX_ABRT__SHIFT                                                               0x6
> +#define CKSVII2C_IC_INTR_MASK__M_RX_DONE__SHIFT                                                               0x7
> +#define CKSVII2C_IC_INTR_MASK__M_ACTIVITY__SHIFT                                                              0x8
> +#define CKSVII2C_IC_INTR_MASK__M_STOP_DET__SHIFT                                                              0x9
> +#define CKSVII2C_IC_INTR_MASK__M_START_DET__SHIFT                                                             0xa
> +#define CKSVII2C_IC_INTR_MASK__M_GEN_CALL__SHIFT                                                              0xb
> +#define CKSVII2C_IC_INTR_MASK__M_RESTART_DET__SHIFT                                                           0xc
> +#define CKSVII2C_IC_INTR_MASK__M_MST_ON_HOLD__SHIFT                                                           0xd
> +#define CKSVII2C_IC_INTR_MASK__M_RX_UNDER_MASK                                                                0x00000001L
> +#define CKSVII2C_IC_INTR_MASK__M_RX_OVER_MASK                                                                 0x00000002L
> +#define CKSVII2C_IC_INTR_MASK__M_RX_FULL_MASK                                                                 0x00000004L
> +#define CKSVII2C_IC_INTR_MASK__M_TX_OVER_MASK                                                                 0x00000008L
> +#define CKSVII2C_IC_INTR_MASK__M_TX_EMPTY_MASK                                                                0x00000010L
> +#define CKSVII2C_IC_INTR_MASK__M_RD_REQ_MASK                                                                  0x00000020L
> +#define CKSVII2C_IC_INTR_MASK__M_TX_ABRT_MASK                                                                 0x00000040L
> +#define CKSVII2C_IC_INTR_MASK__M_RX_DONE_MASK                                                                 0x00000080L
> +#define CKSVII2C_IC_INTR_MASK__M_ACTIVITY_MASK                                                                0x00000100L
> +#define CKSVII2C_IC_INTR_MASK__M_STOP_DET_MASK                                                                0x00000200L
> +#define CKSVII2C_IC_INTR_MASK__M_START_DET_MASK                                                               0x00000400L
> +#define CKSVII2C_IC_INTR_MASK__M_GEN_CALL_MASK                                                                0x00000800L
> +#define CKSVII2C_IC_INTR_MASK__M_RESTART_DET_MASK                                                             0x00001000L
> +#define CKSVII2C_IC_INTR_MASK__M_MST_ON_HOLD_MASK                                                             0x00002000L
> +//CKSVII2C_IC_RAW_INTR_STAT
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_RX_UNDER__SHIFT                                                              0x0
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_RX_OVER__SHIFT                                                               0x1
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_RX_FULL__SHIFT                                                               0x2
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_TX_OVER__SHIFT                                                               0x3
> +#define CKSVII2C_IC__RAW_INTR_STAT__R_TX_EMPTY__SHIFT                                                              0x4
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_RD_REQ__SHIFT                                                                0x5
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_TX_ABRT__SHIFT                                                               0x6
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_RX_DONE__SHIFT                                                               0x7
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_ACTIVITY__SHIFT                                                              0x8
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_STOP_DET__SHIFT                                                              0x9
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_START_DET__SHIFT                                                             0xa
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_GEN_CALL__SHIFT                                                              0xb
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_RESTART_DET__SHIFT                                                           0xc
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_MST_ON_HOLD__SHIFT                                                           0xd
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_RX_UNDER_MASK                                                                0x00000001L
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_RX_OVER_MASK                                                                 0x00000002L
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_RX_FULL_MASK                                                                 0x00000004L
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_TX_OVER_MASK                                                                 0x00000008L
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_TX_EMPTY_MASK                                                                0x00000010L
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_RD_REQ_MASK                                                                  0x00000020L
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_TX_ABRT_MASK                                                                 0x00000040L
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_RX_DONE_MASK                                                                 0x00000080L
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_ACTIVITY_MASK                                                                0x00000100L
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_STOP_DET_MASK                                                                0x00000200L
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_START_DET_MASK                                                               0x00000400L
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_GEN_CALL_MASK                                                                0x00000800L
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_RESTART_DET_MASK                                                             0x00001000L
> +#define CKSVII2C_IC_RAW_INTR_STAT__R_MST_ON_HOLD_MASK                                                             0x00002000L
> +//CKSVII2C_IC_RX_TL
> +//CKSVII2C_IC_TX_TL
> +//CKSVII2C_IC_CLR_INTR
> +//CKSVII2C_IC_CLR_RX_UNDER
> +//CKSVII2C_IC_CLR_RX_OVER
> +//CKSVII2C_IC_CLR_TX_OVER
> +//CKSVII2C_IC_CLR_RD_REQ
> +//CKSVII2C_IC_CLR_TX_ABRT
> +//CKSVII2C_IC_CLR_RX_DONE
> +//CKSVII2C_IC_CLR_ACTIVITY
> +#define CKSVII2C_IC_CLR_ACTIVITY__CLR_ACTIVITY__SHIFT                                                         0x0
> +#define CKSVII2C_IC_CLR_ACTIVITY__CLR_ACTIVITY_MASK                                                           0x00000001L
> +//CKSVII2C_IC_CLR_STOP_DET
> +//CKSVII2C_IC_CLR_START_DET
> +//CKSVII2C_IC_CLR_GEN_CALL
> +//CKSVII2C_IC_ENABLE
> +#define CKSVII2C_IC_ENABLE__ENABLE__SHIFT                                                                     0x0
> +#define CKSVII2C_IC_ENABLE__ABORT__SHIFT                                                                      0x1
> +#define CKSVII2C_IC_ENABLE__ENABLE_MASK                                                                       0x00000001L
> +#define CKSVII2C_IC_ENABLE__ABORT_MASK                                                                        0x00000002L
> +//CKSVII2C_IC_STATUS
> +#define CKSVII2C_IC_STATUS__ACTIVITY__SHIFT                                                                   0x0
> +#define CKSVII2C_IC_STATUS__TFNF__SHIFT                                                                       0x1
> +#define CKSVII2C_IC_STATUS__TFE__SHIFT                                                                        0x2
> +#define CKSVII2C_IC_STATUS__RFNE__SHIFT                                                                       0x3
> +#define CKSVII2C_IC_STATUS__RFF__SHIFT                                                                        0x4
> +#define CKSVII2C_IC_STATUS__MST_ACTIVITY__SHIFT                                                               0x5
> +#define CKSVII2C_IC_STATUS__SLV_ACTIVITY__SHIFT                                                               0x6
> +#define CKSVII2C_IC_STATUS__ACTIVITY_MASK                                                                     0x00000001L
> +#define CKSVII2C_IC_STATUS__TFNF_MASK                                                                         0x00000002L
> +#define CKSVII2C_IC_STATUS__TFE_MASK                                                                          0x00000004L
> +#define CKSVII2C_IC_STATUS__RFNE_MASK                                                                         0x00000008L
> +#define CKSVII2C_IC_STATUS__RFF_MASK                                                                          0x00000010L
> +#define CKSVII2C_IC_STATUS__MST_ACTIVITY_MASK                                                                 0x00000020L
> +#define CKSVII2C_IC_STATUS__SLV_ACTIVITY_MASK                                                                 0x00000040L
> +//CKSVII2C_IC_TXFLR
> +//CKSVII2C_IC_RXFLR
> +//CKSVII2C_IC_SDA_HOLD
> +#define CKSVII2C_IC_SDA_HOLD__IC_SDA_HOLD__SHIFT                                                              0x0
> +#define CKSVII2C_IC_SDA_HOLD__IC_SDA_HOLD_MASK                                                                0x00FFFFFFL
> +//CKSVII2C_IC_TX_ABRT_SOURCE
> +
> +#define CKSVII2C_IC_TX_ABRT_SOURCE__ABRT_7B_ADDR_NOACK__SHIFT                                                  0x0
> +#define CKSVII2C_IC_TX_ABRT_SOURCE__ABRT_10ADDR1_NOACK__SHIFT                                                  0x1
> +#define CKSVII2C_IC_TX_ABRT_SOURCE__ABRT_10ADDR2_NOACK__SHIFT                                                  0x2
> +#define CKSVII2C_IC_TX_ABRT_SOURCE__ABRT_TXDATA_NOACK__SHIFT                                                   0x3
> +#define CKSVII2C_IC_TX_ABRT_SOURCE__ABRT_7B_ADDR_NOACK_MASK                                                   0x00000001L
> +#define CKSVII2C_IC_TX_ABRT_SOURCE__ABRT_10ADDR1_NOACK_MASK                                                   0x00000002L
> +#define CKSVII2C_IC_TX_ABRT_SOURCE__ABRT_10ADDR2_NOACK_MASK                                                   0x00000004L
> +#define CKSVII2C_IC_TX_ABRT_SOURCE__ABRT_TXDATA_NOACK_MASK                                                    0x00000008L
> +//CKSVII2C_IC_SLV_DATA_NACK_ONLY
> +//CKSVII2C_IC_DMA_CR
> +//CKSVII2C_IC_DMA_TDLR
> +//CKSVII2C_IC_DMA_RDLR
> +//CKSVII2C_IC_SDA_SETUP
> +#define CKSVII2C_IC_SDA_SETUP__SDA_SETUP__SHIFT                                                               0x0
> +#define CKSVII2C_IC_SDA_SETUP__SDA_SETUP_MASK                                                                 0x000000FFL
> +//CKSVII2C_IC_ACK_GENERAL_CALL
> +#define CKSVII2C_IC_ACK_GENERAL_CALL__ACK_GENERAL_CALL__SHIFT                                                 0x0
> +#define CKSVII2C_IC_ACK_GENERAL_CALL__ACK_GENERAL_CALL_MASK                                                   0x00000001L
> +//CKSVII2C_IC_ENABLE_STATUS
> +#define CKSVII2C_IC_ENABLE_STATUS__IC_EN__SHIFT                                                               0x0
> +#define CKSVII2C_IC_ENABLE_STATUS__SLV_RX_ABORTED__SHIFT                                                      0x1
> +#define CKSVII2C_IC_ENABLE_STATUS__SLV_FIFO_FILLED_AND_FLUSHED__SHIFT                                         0x2
> +#define CKSVII2C_IC_ENABLE_STATUS__IC_EN_MASK                                                                 0x00000001L
> +#define CKSVII2C_IC_ENABLE_STATUS__SLV_RX_ABORTED_MASK                                                        0x00000002L
> +#define CKSVII2C_IC_ENABLE_STATUS__SLV_FIFO_FILLED_AND_FLUSHED_MASK                                           0x00000004L
> +//CKSVII2C_IC_FS_SPKLEN
> +#define CKSVII2C_IC_FS_SPKLEN__FS_SPKLEN__SHIFT                                                               0x0
> +#define CKSVII2C_IC_FS_SPKLEN__FS_SPKLEN_MASK                                                                 0x000000FFL
> +//CKSVII2C_IC_HS_SPKLEN
> +#define CKSVII2C_IC_HS_SPKLEN__HS_SPKLEN__SHIFT                                                               0x0
> +#define CKSVII2C_IC_HS_SPKLEN__HS_SPKLEN_MASK                                                                 0x000000FFL
> +//CKSVII2C_IC_CLR_RESTART_DET
> +//CKSVII2C_IC_COMP_PARAM_1
> +#define CKSVII2C_IC_COMP_PARAM_1__COMP_PARAM_1__SHIFT                                                         0x0
> +#define CKSVII2C_IC_COMP_PARAM_1__COMP_PARAM_1_MASK                                                           0xFFFFFFFFL
> +//CKSVII2C_IC_COMP_VERSION
> +#define CKSVII2C_IC_COMP_VERSION__COMP_VERSION__SHIFT                                                         0x0
> +#define CKSVII2C_IC_COMP_VERSION__COMP_VERSION_MASK                                                           0xFFFFFFFFL
> +//CKSVII2C_IC_COMP_TYPE
> +#define CKSVII2C_IC_COMP_TYPE__COMP_TYPE__SHIFT                                                               0x0
> +#define CKSVII2C_IC_COMP_TYPE__COMP_TYPE_MASK                                                                 0xFFFFFFFFL
>  //SMUIO_MP_RESET_INTR
>  #define SMUIO_MP_RESET_INTR__SMUIO_MP_RESET_INTR__SHIFT                                                       0x0
>  #define SMUIO_MP_RESET_INTR__SMUIO_MP_RESET_INTR_MASK                                                         0x00000001L
> --
> 2.7.4
>
> _______________________________________________
> amd-gfx mailing list
> amd-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/amd-gfx
_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply	[flat|nested] 18+ messages in thread

* RE: [PATCH v4 1/4] drm/amdgpu: Add RAS EEPROM table.
       [not found]     ` <1566417719-1528-2-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
@ 2019-08-22  4:13       ` Zhou1, Tao
       [not found]         ` <MN2PR12MB3054832C9FC9C73A7F9AD539B0A50-rweVpJHSKTqnT25eLM+iUQdYzm3356FpvxpqHgZTriW3zl9H0oFU5g@public.gmane.org>
  2019-08-23  3:48       ` Tuikov, Luben
  1 sibling, 1 reply; 18+ messages in thread
From: Zhou1, Tao @ 2019-08-22  4:13 UTC (permalink / raw)
  To: amd-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: Panariti, David, Lazar, Lijo, Grodzovsky, Andrey, Pan, Xinhui,
	Tuikov, Luben, Russell, Kent, Deucher, Alexander, Quan, Evan,
	Zhang, Hawking



> -----Original Message-----
> From: Andrey Grodzovsky <andrey.grodzovsky@amd.com>
> Sent: 2019年8月22日 4:02
> To: amd-gfx@lists.freedesktop.org
> Cc: Deucher, Alexander <Alexander.Deucher@amd.com>; Pan, Xinhui
> <Xinhui.Pan@amd.com>; Zhang, Hawking <Hawking.Zhang@amd.com>;
> Tuikov, Luben <Luben.Tuikov@amd.com>; Lazar, Lijo <Lijo.Lazar@amd.com>;
> Quan, Evan <Evan.Quan@amd.com>; Panariti, David
> <David.Panariti@amd.com>; Russell, Kent <Kent.Russell@amd.com>; Zhou1,
> Tao <Tao.Zhou1@amd.com>; Grodzovsky, Andrey
> <Andrey.Grodzovsky@amd.com>
> Subject: [PATCH v4 1/4] drm/amdgpu: Add RAS EEPROM table.
> 
> Add RAS EEPROM table manager to eanble RAS errors to be stored upon
> appearance and retrived on driver load.
> 
> v2: Fix some prints.
> 
> v3:
> Fix checksum calculation.
> Make table record and header structs packed to do correct byte value sum.
> Fix record crossing EEPROM page boundry.
> 
> v4:
> Fix byte sum val calculation for record - look at sizeof(record).
> Fix some style comments.
> 
> Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@amd.com>
> ---
>  drivers/gpu/drm/amd/amdgpu/Makefile            |   2 +-
>  drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h        |   3 +
>  drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c | 482
> +++++++++++++++++++++++++
> drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h |  90 +++++
>  4 files changed, 576 insertions(+), 1 deletion(-)  create mode 100644
> drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
>  create mode 100644
> drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
> 
> diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile
> b/drivers/gpu/drm/amd/amdgpu/Makefile
> index 28d76bd..f016cf1 100644
> --- a/drivers/gpu/drm/amd/amdgpu/Makefile
> +++ b/drivers/gpu/drm/amd/amdgpu/Makefile
> @@ -54,7 +54,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
>  	amdgpu_gtt_mgr.o amdgpu_vram_mgr.o amdgpu_virt.o
> amdgpu_atomfirmware.o \
>  	amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o
> amdgpu_ids.o \
>  	amdgpu_gmc.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o
> amdgpu_vm_cpu.o \
> -	amdgpu_vm_sdma.o amdgpu_discovery.o
> +	amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o
> amdgpu_ras_eeprom.o
> 
>  amdgpu-$(CONFIG_PERF_EVENTS) += amdgpu_pmu.o
> 
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
> b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
> index 2765f2d..8d5bcd8 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
> @@ -29,6 +29,7 @@
>  #include "amdgpu.h"
>  #include "amdgpu_psp.h"
>  #include "ta_ras_if.h"
> +#include "amdgpu_ras_eeprom.h"
> 
>  enum amdgpu_ras_block {
>  	AMDGPU_RAS_BLOCK__UMC = 0,
> @@ -333,6 +334,8 @@ struct amdgpu_ras {
>  	struct mutex recovery_lock;
> 
>  	uint32_t flags;
> +
> +	struct amdgpu_ras_eeprom_control eeprom_control;
>  };
> 
>  struct ras_fs_data {
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
> b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
> new file mode 100644
> index 0000000..bf07515
> --- /dev/null
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
> @@ -0,0 +1,482 @@
> +/*
> + * 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>
> +
> +#define EEPROM_I2C_TARGET_ADDR 0xA0
> +
> +#define EEPROM_TABLE_HEADER_SIZE 20
> +#define EEPROM_TABLE_RECORD_SIZE 24
[Tao] should we replace fixed value with sizeof for the two macros?

> +#define EEPROM_ADDRESS_SIZE 0x2
> +
> +/* Table hdr is 'AMDR' */
> +#define EEPROM_TABLE_HDR_VAL 0x414d4452 #define
> EEPROM_TABLE_VER
> +0x00010000
> +
> +/* Assume 2 Mbit size */
> +#define EEPROM_SIZE_BYTES 256000
> +#define EEPROM_PAGE__SIZE_BYTES 256
> +#define EEPROM_HDR_START 0
> +#define EEPROM_RECORD_START (EEPROM_HDR_START +
> +EEPROM_TABLE_HEADER_SIZE) #define EEPROM_MAX_RECORD_NUM
> +((EEPROM_SIZE_BYTES - EEPROM_TABLE_HEADER_SIZE) /
> +EEPROM_TABLE_RECORD_SIZE) #define EEPROM_ADDR_MSB_MASK
> GENMASK(17, 8)
> +
> +#define to_amdgpu_device(x) (container_of(x, struct amdgpu_ras,
> +eeprom_control))->adev
> +
> +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;
> +
> +	hdr->header 	      = le32_to_cpu(pp[0]);
> +	hdr->version 	      = le32_to_cpu(pp[1]);
> +	hdr->first_rec_offset = le32_to_cpu(pp[2]);
> +	hdr->tbl_size 	      = le32_to_cpu(pp[3]);
> +	hdr->checksum 	      = le32_to_cpu(pp[4]);
> +}
> +
> +static int __update_table_header(struct amdgpu_ras_eeprom_control
> *control,
> +				 unsigned char *buff)
> +{
> +	int ret = 0;
> +	struct i2c_msg msg = {
> +			.addr	= EEPROM_I2C_TARGET_ADDR,
> +			.flags	= 0,
> +			.len	= EEPROM_ADDRESS_SIZE +
> EEPROM_TABLE_HEADER_SIZE,
> +			.buf	= buff,
> +	};
> +
> +
> +	*(uint16_t *)buff = EEPROM_HDR_START;
> +	__encode_table_header_to_buff(&control->tbl_hdr, buff +
> +EEPROM_ADDRESS_SIZE);
> +
> +	ret = i2c_transfer(&control->eeprom_accessor, &msg, 1);
> +	if (ret < 1)
> +		DRM_ERROR("Failed to write EEPROM table header, ret:%d",
> ret);
> +
> +	return ret;
> +}
> +
> +static uint32_t  __calc_hdr_byte_sum(struct amdgpu_ras_eeprom_control
> +*control);
> +
> +int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
> {
> +	int ret = 0;
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +	unsigned char buff[EEPROM_ADDRESS_SIZE +
> EEPROM_TABLE_HEADER_SIZE] = { 0 };
> +	struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr;
> +	struct i2c_msg msg = {
> +			.addr	= EEPROM_I2C_TARGET_ADDR,
> +			.flags	= I2C_M_RD,
> +			.len	= EEPROM_ADDRESS_SIZE +
> EEPROM_TABLE_HEADER_SIZE,
> +			.buf	= buff,
> +	};
> +
> +	mutex_init(&control->tbl_mutex);
> +
> +	switch (adev->asic_type) {
> +	case CHIP_VEGA20:
> +	/*TODO Add MI-60 */
> +		break;
> +
> +	default:
> +		return 0;
> +	}
> +
> +	if (ret) {
> +		DRM_ERROR("Failed to init I2C controller, ret:%d", ret);
> +		return ret;
> +	}
> +
> +	/* Read/Create table header from EEPROM address 0 */
> +	ret = i2c_transfer(&control->eeprom_accessor, &msg, 1);
> +	if (ret < 1) {
> +		DRM_ERROR("Failed to read EEPROM table header, ret:%d",
> ret);
> +		return ret;
> +	}
> +
> +	__decode_table_header_from_buff(hdr, &buff[2]);
> +
> +	if (hdr->header == EEPROM_TABLE_HDR_VAL) {
> +		control->num_recs = (hdr->tbl_size -
> EEPROM_TABLE_HEADER_SIZE) /
> +				    EEPROM_TABLE_RECORD_SIZE;
> +		DRM_DEBUG_DRIVER("Found existing EEPROM table with %d
> records",
> +				 control->num_recs);
> +
> +	} else {
> +		DRM_INFO("Creating new EEPROM table");
> +
> +		hdr->header = EEPROM_TABLE_HDR_VAL;
> +		hdr->version = EEPROM_TABLE_VER;
> +		hdr->first_rec_offset = EEPROM_RECORD_START;
> +		hdr->tbl_size = EEPROM_TABLE_HEADER_SIZE;
> +
> +		adev->psp.ras.ras->eeprom_control.tbl_byte_sum =
> +				__calc_hdr_byte_sum(&adev->psp.ras.ras-
> >eeprom_control);
> +		ret = __update_table_header(control, buff);
> +	}
> +
> +	/* Start inserting records from here */
> +	adev->psp.ras.ras->eeprom_control.next_addr =
> EEPROM_RECORD_START;
> +
> +	return ret == 1 ? 0 : -EIO;
> +}
> +
> +void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control
> *control)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +
> +	switch (adev->asic_type) {
> +	case CHIP_VEGA20:
> +		/*TODO Add MI-60 */
> +		break;
> +
> +	default:
> +		return;
> +	}
> +}
> +
> +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;
[Tao] I think sizeof(record->ts) is better

> +
> +	tmp = cpu_to_le64((record->offset & 0xffffffffffff));
[Tao] (0x1ULL << 49 - 1) is more readable than & 0xffffffffffff, or a macro can be defined, but either way is OK.

> +	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;
> +
> +	buff[i++] = record->mem_channel;
> +	buff[i++] = record->mcumc_id;
> +
> +	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
> + * When next record access will go beyond EEPROM page boundary modify
> +bits A17/A8
> + * in I2C selector to go to next page
> + */
> +static uint32_t __correct_eeprom_dest_address(uint32_t curr_address) {
> +	uint32_t next_address = curr_address +
> EEPROM_TABLE_RECORD_SIZE;
> +
> +	/* When all EEPROM memory used jump back to 0 address */
> +	if (next_address > EEPROM_SIZE_BYTES) {
> +		DRM_INFO("Reached end of EEPROM memory, jumping to 0
> "
> +			 "and overriding old record");
> +		return EEPROM_RECORD_START;
> +	}
> +
> +	/*
> +	 * To check if we overflow page boundary  compare next address
> with
> +	 * current and see if bits 17/8 of the EEPROM address will change
> +	 * If they do start from the next 256b page
> +	 *
> +	 * https://www.st.com/resource/en/datasheet/m24m02-dr.pdf sec.
> 5.1.2
> +	 */
> +	if ((curr_address & EEPROM_ADDR_MSB_MASK) != (next_address &
> EEPROM_ADDR_MSB_MASK)) {
> +		DRM_DEBUG_DRIVER("Reached end of EEPROM memory
> page, jumpimng to next: %lx",
> +				(next_address &
> EEPROM_ADDR_MSB_MASK));
> +
> +		return  (next_address & EEPROM_ADDR_MSB_MASK);
> +	}
> +
> +	return curr_address;
> +}
> +
> +
> +static uint32_t  __calc_hdr_byte_sum(struct amdgpu_ras_eeprom_control
> +*control) {
> +	int i;
> +	uint32_t tbl_sum = 0;
> +
> +	/* Header checksum, skip checksum field in the calculation */
> +	for (i = 0; i < sizeof(control->tbl_hdr) - sizeof(control-
> >tbl_hdr.checksum); i++)
> +		tbl_sum += *(((unsigned char *)&control->tbl_hdr) + i);
> +
> +	return tbl_sum;
> +}
> +
> +static uint32_t  __calc_recs_byte_sum(struct eeprom_table_record *records,
> +				      int num)
> +{
> +	int i, j;
> +	uint32_t tbl_sum = 0;
> +
> +	/* Records checksum */
> +	for (i = 0; i < num; i++) {
> +		struct eeprom_table_record *record = &records[i];
> +
> +		for (j = 0; j < sizeof(*record); j++) {
> +			tbl_sum += *(((unsigned char *)record) + j);
> +		}
> +	}
> +
> +	return tbl_sum;
> +}
> +
> +static inline uint32_t  __calc_tbl_byte_sum(struct
> amdgpu_ras_eeprom_control *control,
> +				  struct eeprom_table_record *records, int
> num) {
> +	return __calc_hdr_byte_sum(control) +
> __calc_recs_byte_sum(records,
> +num); }
> +
> +/* Checksum = 256 -((sum of all table entries) mod 256) */ static void
> +__update_tbl_checksum(struct amdgpu_ras_eeprom_control *control,
> +				  struct eeprom_table_record *records, int
> num,
> +				  uint32_t old_hdr_byte_sum)
> +{
> +	/*
> +	 * This will update the table sum with new records.
> +	 *
> +	 * TODO: What happens when the EEPROM table is to be wrapped
> around
> +	 * and old records from start will get overridden.
> +	 */
> +
> +	/* need to recalculate updated header byte sum */
> +	control->tbl_byte_sum -= old_hdr_byte_sum;
> +	control->tbl_byte_sum += __calc_tbl_byte_sum(control, records,
> num);
> +
> +	control->tbl_hdr.checksum = 256 - (control->tbl_byte_sum % 256); }
[Tao] we can change 256 to EEPROM_PAGE__SIZE_BYTES

> +
> +/* table sum mod 256 + checksum must equals 256 */ static bool
> +__validate_tbl_checksum(struct amdgpu_ras_eeprom_control *control,
> +			    struct eeprom_table_record *records, int num) {
> +	control->tbl_byte_sum = __calc_tbl_byte_sum(control, records, num);
> +
> +	if (control->tbl_hdr.checksum + (control->tbl_byte_sum % 256) != 256)
> {
> +		DRM_WARN("Checksum mismatch, checksum: %u ", control-
> >tbl_hdr.checksum);
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +int amdgpu_ras_eeprom_process_recods(struct
> amdgpu_ras_eeprom_control *control,
> +					    struct eeprom_table_record
> *records,
> +					    bool write,
> +					    int num)
> +{
> +	int i, ret = 0;
> +	struct i2c_msg *msgs;
> +	unsigned char *buffs;
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +
> +	if (adev->asic_type != CHIP_VEGA20)
> +		return 0;
> +
> +	buffs = kcalloc(num, EEPROM_ADDRESS_SIZE +
> EEPROM_TABLE_RECORD_SIZE,
> +			 GFP_KERNEL);
> +	if (!buffs)
> +		return -ENOMEM;
> +
> +	mutex_lock(&control->tbl_mutex);
> +
> +	msgs = kcalloc(num, sizeof(*msgs), GFP_KERNEL);
> +	if (!msgs) {
> +		ret = -ENOMEM;
> +		goto free_buff;
> +	}
> +
> +	/* In case of overflow just start from beginning to not lose newest
> records */
> +	if (write && (control->next_addr + EEPROM_TABLE_RECORD_SIZE *
> num > EEPROM_SIZE_BYTES))
> +		control->next_addr = EEPROM_RECORD_START;
> +
> +
> +	/*
> +	 * 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++) {
> +		unsigned char *buff = &buffs[i*(EEPROM_ADDRESS_SIZE +
> EEPROM_TABLE_RECORD_SIZE)];
> +		struct eeprom_table_record *record = &records[i];
> +		struct i2c_msg *msg = &msgs[i];
> +
> +		control->next_addr =
> +__correct_eeprom_dest_address(control->next_addr);
> +
> +		/*
> +		 * Update bits 16,17 of EEPROM address in I2C address by
> setting them
> +		 * to bits 1,2 of Device address byte
> +		 */
> +		msg->addr = EEPROM_I2C_TARGET_ADDR |
> +			       ((control->next_addr &
> EEPROM_ADDR_MSB_MASK) >> 15);
> +		msg->flags	= write ? 0 : I2C_M_RD;
> +		msg->len	= EEPROM_ADDRESS_SIZE +
> EEPROM_TABLE_RECORD_SIZE;
> +		msg->buf	= buff;
> +
> +		/* Insert the EEPROM dest addess, bits 0-15 */
> +		buff[0] = ((control->next_addr >> 8) & 0xff);
> +		buff[1] = (control->next_addr & 0xff);
> +
> +		/* EEPROM table content is stored in LE format */
> +		if (write)
> +			__encode_table_record_to_buff(control, record, buff
> +
> +EEPROM_ADDRESS_SIZE);
> +
> +		/*
> +		 * The destination EEPROM address might need to be
> corrected to account
> +		 * for page or entire memory wrapping
> +		 */
> +		control->next_addr += EEPROM_TABLE_RECORD_SIZE;
> +	}
> +
> +	ret = i2c_transfer(&control->eeprom_accessor, msgs, num);
> +	if (ret < 1) {
> +		DRM_ERROR("Failed to process EEPROM table records,
> ret:%d", ret);
> +
> +		/* TODO Restore prev next EEPROM address ? */
> +		goto free_msgs;
> +	}
> +
> +
> +	if (!write) {
> +		for (i = 0; i < num; i++) {
> +			unsigned char *buff =
> &buffs[i*(EEPROM_ADDRESS_SIZE + EEPROM_TABLE_RECORD_SIZE)];
[Tao] space is needed before and after "*"

> +			struct eeprom_table_record *record = &records[i];
[Tao] add a space after "*"

> +
> +			__decode_table_record_from_buff(control, record,
> buff + EEPROM_ADDRESS_SIZE);
> +		}
> +	}
> +
> +	if (write) {
> +		uint32_t old_hdr_byte_sum = __calc_hdr_byte_sum(control);
> +
> +		/*
> +		 * 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;
> +		control->num_recs %= EEPROM_MAX_RECORD_NUM;
> +		control->tbl_hdr.tbl_size += EEPROM_TABLE_RECORD_SIZE *
> num;
> +		if (control->tbl_hdr.tbl_size > EEPROM_SIZE_BYTES)
> +			control->tbl_hdr.tbl_size =
> EEPROM_TABLE_HEADER_SIZE +
> +			control->num_recs * EEPROM_TABLE_RECORD_SIZE;
> +
> +		__update_tbl_checksum(control, records, num,
> old_hdr_byte_sum);
> +
> +		__update_table_header(control, buffs);
> +	} else if (!__validate_tbl_checksum(control, records, num)) {
> +		DRM_WARN("EEPROM Table checksum mismatch!");
> +		/* TODO Uncomment when EEPROM read/write is relliable
> */
> +		/* ret = -EIO; */
> +	}
> +
> +free_msgs:
> +	kfree(msgs);
> +
> +free_buff:
> +	kfree(buffs);
> +
> +	mutex_unlock(&control->tbl_mutex);
> +
> +	return ret == num ? 0 : -EIO;
> +}
> +
> +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;
> +	}
> +
> +	if (!amdgpu_ras_eeprom_process_recods(control, recs, true, 1)) {
> +
> +		memset(recs, 0, sizeof(*recs) * 1);
> +
> +		control->next_addr = EEPROM_RECORD_START;
> +
> +		if (!amdgpu_ras_eeprom_process_recods(control, recs, false,
> 1)) {
> +			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"); }
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
> b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
> new file mode 100644
> index 0000000..41f3fcb
> --- /dev/null
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
> @@ -0,0 +1,90 @@
> +/*
> + * 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.
> + *
> + */
> +
> +#ifndef _AMDGPU_RAS_EEPROM_H
> +#define _AMDGPU_RAS_EEPROM_H
> +
> +#include <linux/i2c.h>
> +
> +struct amdgpu_device;
> +
> +enum amdgpu_ras_eeprom_err_type{
> +	AMDGPU_RAS_EEPROM_ERR_PLACE_HOLDER,
> +	AMDGPU_RAS_EEPROM_ERR_RECOVERABLE,
> +	AMDGPU_RAS_EEPROM_ERR_NON_RECOVERABLE
> +};
> +
> +struct amdgpu_ras_eeprom_table_header {
> +	uint32_t header;
> +	uint32_t version;
> +	uint32_t first_rec_offset;
> +	uint32_t tbl_size;
> +	uint32_t checksum;
> +}__attribute__((__packed__));
> +
> +struct amdgpu_ras_eeprom_control {
> +	struct amdgpu_ras_eeprom_table_header tbl_hdr;
> +	struct i2c_adapter eeprom_accessor;
> +	uint32_t next_addr;
> +	unsigned int num_recs;
> +	struct mutex tbl_mutex;
> +	bool bus_locked;
> +	uint32_t tbl_byte_sum;
> +};
> +
> +/*
> + * Represents single table record. Packed to be easily serialized into
> +byte
> + * stream.
> + */
> +struct eeprom_table_record {
> +
> +	union {
> +		uint64_t address;
> +		uint64_t offset;
> +	};
> +
> +	uint64_t retired_page;
> +	uint64_t ts;
> +
> +	enum amdgpu_ras_eeprom_err_type err_type;
> +
> +	union {
> +		unsigned char bank;
> +		unsigned char cu;
> +	};
> +
> +	unsigned char mem_channel;
> +	unsigned char mcumc_id;
> +}__attribute__((__packed__));
> +
> +int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control);
> +void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control
> *control);
> +
> +int amdgpu_ras_eeprom_process_recods(struct
> amdgpu_ras_eeprom_control *control,
> +					    struct eeprom_table_record
> *records,
> +					    bool write,
> +					    int num);
> +
> +void amdgpu_ras_eeprom_test(struct amdgpu_ras_eeprom_control
> *control);
> +
> +#endif // _AMDGPU_RAS_EEPROM_H
> --
> 2.7.4

_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 1/4] drm/amdgpu: Add RAS EEPROM table.
       [not found]         ` <MN2PR12MB3054832C9FC9C73A7F9AD539B0A50-rweVpJHSKTqnT25eLM+iUQdYzm3356FpvxpqHgZTriW3zl9H0oFU5g@public.gmane.org>
@ 2019-08-22 15:07           ` Grodzovsky, Andrey
       [not found]             ` <ce52910d-0d1f-402c-7cd0-a8a283a2cda8-5C7GfCeVMHo@public.gmane.org>
  0 siblings, 1 reply; 18+ messages in thread
From: Grodzovsky, Andrey @ 2019-08-22 15:07 UTC (permalink / raw)
  To: Zhou1, Tao, amd-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: Panariti, David, Lazar, Lijo, Pan, Xinhui, Tuikov, Luben,
	Russell, Kent, Deucher, Alexander, Quan, Evan, Zhang, Hawking


[-- Attachment #1.1: Type: text/plain, Size: 26168 bytes --]


On 8/22/19 12:13 AM, Zhou1, Tao wrote:





-----Original Message-----
From: Andrey Grodzovsky <andrey.grodzovsky@amd.com><mailto:andrey.grodzovsky@amd.com>
Sent: 2019年8月22日 4:02
To: amd-gfx@lists.freedesktop.org<mailto:amd-gfx@lists.freedesktop.org>
Cc: Deucher, Alexander <Alexander.Deucher@amd.com><mailto:Alexander.Deucher@amd.com>; Pan, Xinhui
<Xinhui.Pan@amd.com><mailto:Xinhui.Pan@amd.com>; Zhang, Hawking <Hawking.Zhang@amd.com><mailto:Hawking.Zhang@amd.com>;
Tuikov, Luben <Luben.Tuikov@amd.com><mailto:Luben.Tuikov@amd.com>; Lazar, Lijo <Lijo.Lazar@amd.com><mailto:Lijo.Lazar@amd.com>;
Quan, Evan <Evan.Quan@amd.com><mailto:Evan.Quan@amd.com>; Panariti, David
<David.Panariti@amd.com><mailto:David.Panariti@amd.com>; Russell, Kent <Kent.Russell@amd.com><mailto:Kent.Russell@amd.com>; Zhou1,
Tao <Tao.Zhou1@amd.com><mailto:Tao.Zhou1@amd.com>; Grodzovsky, Andrey
<Andrey.Grodzovsky@amd.com><mailto:Andrey.Grodzovsky@amd.com>
Subject: [PATCH v4 1/4] drm/amdgpu: Add RAS EEPROM table.

Add RAS EEPROM table manager to eanble RAS errors to be stored upon
appearance and retrived on driver load.

v2: Fix some prints.

v3:
Fix checksum calculation.
Make table record and header structs packed to do correct byte value sum.
Fix record crossing EEPROM page boundry.

v4:
Fix byte sum val calculation for record - look at sizeof(record).
Fix some style comments.

Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@amd.com><mailto:andrey.grodzovsky@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/Makefile            |   2 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h        |   3 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c | 482
+++++++++++++++++++++++++
drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h |  90 +++++
 4 files changed, 576 insertions(+), 1 deletion(-)  create mode 100644
drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
 create mode 100644
drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h

diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile
b/drivers/gpu/drm/amd/amdgpu/Makefile
index 28d76bd..f016cf1 100644
--- a/drivers/gpu/drm/amd/amdgpu/Makefile
+++ b/drivers/gpu/drm/amd/amdgpu/Makefile
@@ -54,7 +54,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
        amdgpu_gtt_mgr.o amdgpu_vram_mgr.o amdgpu_virt.o
amdgpu_atomfirmware.o \
        amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o
amdgpu_ids.o \
        amdgpu_gmc.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o
amdgpu_vm_cpu.o \
-       amdgpu_vm_sdma.o amdgpu_discovery.o
+       amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o
amdgpu_ras_eeprom.o

 amdgpu-$(CONFIG_PERF_EVENTS) += amdgpu_pmu.o

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
index 2765f2d..8d5bcd8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
@@ -29,6 +29,7 @@
 #include "amdgpu.h"
 #include "amdgpu_psp.h"
 #include "ta_ras_if.h"
+#include "amdgpu_ras_eeprom.h"

 enum amdgpu_ras_block {
        AMDGPU_RAS_BLOCK__UMC = 0,
@@ -333,6 +334,8 @@ struct amdgpu_ras {
        struct mutex recovery_lock;

        uint32_t flags;
+
+       struct amdgpu_ras_eeprom_control eeprom_control;
 };

 struct ras_fs_data {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
new file mode 100644
index 0000000..bf07515
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
@@ -0,0 +1,482 @@
+/*
+ * 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>
+
+#define EEPROM_I2C_TARGET_ADDR 0xA0
+
+#define EEPROM_TABLE_HEADER_SIZE 20
+#define EEPROM_TABLE_RECORD_SIZE 24


[Tao] should we replace fixed value with sizeof for the two macros?


No, as I already explained before the EEPROM_TABLE_HEADER/RECORD_SIZE represent the actual size in bytes that those entities occupy in the EEPROM memory and as defined in the EEPROM table description while struct eeprom_table_record would be larger because for example to store 6b of RetiedPage I have to use uint64 which is 8b. I could of course use some kind of bitvector (bitset) to store them compactly but it seems to me as an overkill. I was told we are not expecting to much records for any given system anyway during it's life time so the dynamic memory saved would be small anyway.






+#define EEPROM_ADDRESS_SIZE 0x2
+
+/* Table hdr is 'AMDR' */
+#define EEPROM_TABLE_HDR_VAL 0x414d4452 #define
EEPROM_TABLE_VER
+0x00010000
+
+/* Assume 2 Mbit size */
+#define EEPROM_SIZE_BYTES 256000
+#define EEPROM_PAGE__SIZE_BYTES 256
+#define EEPROM_HDR_START 0
+#define EEPROM_RECORD_START (EEPROM_HDR_START +
+EEPROM_TABLE_HEADER_SIZE) #define EEPROM_MAX_RECORD_NUM
+((EEPROM_SIZE_BYTES - EEPROM_TABLE_HEADER_SIZE) /
+EEPROM_TABLE_RECORD_SIZE) #define EEPROM_ADDR_MSB_MASK
GENMASK(17, 8)
+
+#define to_amdgpu_device(x) (container_of(x, struct amdgpu_ras,
+eeprom_control))->adev
+
+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;
+
+       hdr->header           = le32_to_cpu(pp[0]);
+       hdr->version          = le32_to_cpu(pp[1]);
+       hdr->first_rec_offset = le32_to_cpu(pp[2]);
+       hdr->tbl_size         = le32_to_cpu(pp[3]);
+       hdr->checksum         = le32_to_cpu(pp[4]);
+}
+
+static int __update_table_header(struct amdgpu_ras_eeprom_control
*control,
+                                unsigned char *buff)
+{
+       int ret = 0;
+       struct i2c_msg msg = {
+                       .addr   = EEPROM_I2C_TARGET_ADDR,
+                       .flags  = 0,
+                       .len    = EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_HEADER_SIZE,
+                       .buf    = buff,
+       };
+
+
+       *(uint16_t *)buff = EEPROM_HDR_START;
+       __encode_table_header_to_buff(&control->tbl_hdr, buff +
+EEPROM_ADDRESS_SIZE);
+
+       ret = i2c_transfer(&control->eeprom_accessor, &msg, 1);
+       if (ret < 1)
+               DRM_ERROR("Failed to write EEPROM table header, ret:%d",
ret);
+
+       return ret;
+}
+
+static uint32_t  __calc_hdr_byte_sum(struct amdgpu_ras_eeprom_control
+*control);
+
+int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
{
+       int ret = 0;
+       struct amdgpu_device *adev = to_amdgpu_device(control);
+       unsigned char buff[EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_HEADER_SIZE] = { 0 };
+       struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr;
+       struct i2c_msg msg = {
+                       .addr   = EEPROM_I2C_TARGET_ADDR,
+                       .flags  = I2C_M_RD,
+                       .len    = EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_HEADER_SIZE,
+                       .buf    = buff,
+       };
+
+       mutex_init(&control->tbl_mutex);
+
+       switch (adev->asic_type) {
+       case CHIP_VEGA20:
+       /*TODO Add MI-60 */
+               break;
+
+       default:
+               return 0;
+       }
+
+       if (ret) {
+               DRM_ERROR("Failed to init I2C controller, ret:%d", ret);
+               return ret;
+       }
+
+       /* Read/Create table header from EEPROM address 0 */
+       ret = i2c_transfer(&control->eeprom_accessor, &msg, 1);
+       if (ret < 1) {
+               DRM_ERROR("Failed to read EEPROM table header, ret:%d",
ret);
+               return ret;
+       }
+
+       __decode_table_header_from_buff(hdr, &buff[2]);
+
+       if (hdr->header == EEPROM_TABLE_HDR_VAL) {
+               control->num_recs = (hdr->tbl_size -
EEPROM_TABLE_HEADER_SIZE) /
+                                   EEPROM_TABLE_RECORD_SIZE;
+               DRM_DEBUG_DRIVER("Found existing EEPROM table with %d
records",
+                                control->num_recs);
+
+       } else {
+               DRM_INFO("Creating new EEPROM table");
+
+               hdr->header = EEPROM_TABLE_HDR_VAL;
+               hdr->version = EEPROM_TABLE_VER;
+               hdr->first_rec_offset = EEPROM_RECORD_START;
+               hdr->tbl_size = EEPROM_TABLE_HEADER_SIZE;
+
+               adev->psp.ras.ras->eeprom_control.tbl_byte_sum =
+                               __calc_hdr_byte_sum(&adev->psp.ras.ras-


eeprom_control);


+               ret = __update_table_header(control, buff);
+       }
+
+       /* Start inserting records from here */
+       adev->psp.ras.ras->eeprom_control.next_addr =
EEPROM_RECORD_START;
+
+       return ret == 1 ? 0 : -EIO;
+}
+
+void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control
*control)
+{
+       struct amdgpu_device *adev = to_amdgpu_device(control);
+
+       switch (adev->asic_type) {
+       case CHIP_VEGA20:
+               /*TODO Add MI-60 */
+               break;
+
+       default:
+               return;
+       }
+}
+
+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;


[Tao] I think sizeof(record->ts) is better



+
+       tmp = cpu_to_le64((record->offset & 0xffffffffffff));


[Tao] (0x1ULL << 49 - 1) is more readable than & 0xffffffffffff, or a macro can be defined, but either way is OK.



+       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;
+
+       buff[i++] = record->mem_channel;
+       buff[i++] = record->mcumc_id;
+
+       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
+ * When next record access will go beyond EEPROM page boundary modify
+bits A17/A8
+ * in I2C selector to go to next page
+ */
+static uint32_t __correct_eeprom_dest_address(uint32_t curr_address) {
+       uint32_t next_address = curr_address +
EEPROM_TABLE_RECORD_SIZE;
+
+       /* When all EEPROM memory used jump back to 0 address */
+       if (next_address > EEPROM_SIZE_BYTES) {
+               DRM_INFO("Reached end of EEPROM memory, jumping to 0
"
+                        "and overriding old record");
+               return EEPROM_RECORD_START;
+       }
+
+       /*
+        * To check if we overflow page boundary  compare next address
with
+        * current and see if bits 17/8 of the EEPROM address will change
+        * If they do start from the next 256b page
+        *
+        * https://www.st.com/resource/en/datasheet/m24m02-dr.pdf sec.
5.1.2
+        */
+       if ((curr_address & EEPROM_ADDR_MSB_MASK) != (next_address &
EEPROM_ADDR_MSB_MASK)) {
+               DRM_DEBUG_DRIVER("Reached end of EEPROM memory
page, jumpimng to next: %lx",
+                               (next_address &
EEPROM_ADDR_MSB_MASK));
+
+               return  (next_address & EEPROM_ADDR_MSB_MASK);
+       }
+
+       return curr_address;
+}
+
+
+static uint32_t  __calc_hdr_byte_sum(struct amdgpu_ras_eeprom_control
+*control) {
+       int i;
+       uint32_t tbl_sum = 0;
+
+       /* Header checksum, skip checksum field in the calculation */
+       for (i = 0; i < sizeof(control->tbl_hdr) - sizeof(control-


tbl_hdr.checksum); i++)


+               tbl_sum += *(((unsigned char *)&control->tbl_hdr) + i);
+
+       return tbl_sum;
+}
+
+static uint32_t  __calc_recs_byte_sum(struct eeprom_table_record *records,
+                                     int num)
+{
+       int i, j;
+       uint32_t tbl_sum = 0;
+
+       /* Records checksum */
+       for (i = 0; i < num; i++) {
+               struct eeprom_table_record *record = &records[i];
+
+               for (j = 0; j < sizeof(*record); j++) {
+                       tbl_sum += *(((unsigned char *)record) + j);
+               }
+       }
+
+       return tbl_sum;
+}
+
+static inline uint32_t  __calc_tbl_byte_sum(struct
amdgpu_ras_eeprom_control *control,
+                                 struct eeprom_table_record *records, int
num) {
+       return __calc_hdr_byte_sum(control) +
__calc_recs_byte_sum(records,
+num); }
+
+/* Checksum = 256 -((sum of all table entries) mod 256) */ static void
+__update_tbl_checksum(struct amdgpu_ras_eeprom_control *control,
+                                 struct eeprom_table_record *records, int
num,
+                                 uint32_t old_hdr_byte_sum)
+{
+       /*
+        * This will update the table sum with new records.
+        *
+        * TODO: What happens when the EEPROM table is to be wrapped
around
+        * and old records from start will get overridden.
+        */
+
+       /* need to recalculate updated header byte sum */
+       control->tbl_byte_sum -= old_hdr_byte_sum;
+       control->tbl_byte_sum += __calc_tbl_byte_sum(control, records,
num);
+
+       control->tbl_hdr.checksum = 256 - (control->tbl_byte_sum % 256); }


[Tao] we can change 256 to EEPROM_PAGE__SIZE_BYTES


This 256 just accidentally has same value as  EEPROM_PAGE__SIZE_BYTES but doesn't relate in any way, it's part of byte value checksum algorithm I was given.

Andrey






+
+/* table sum mod 256 + checksum must equals 256 */ static bool
+__validate_tbl_checksum(struct amdgpu_ras_eeprom_control *control,
+                           struct eeprom_table_record *records, int num) {
+       control->tbl_byte_sum = __calc_tbl_byte_sum(control, records, num);
+
+       if (control->tbl_hdr.checksum + (control->tbl_byte_sum % 256) != 256)
{
+               DRM_WARN("Checksum mismatch, checksum: %u ", control-


tbl_hdr.checksum);


+               return false;
+       }
+
+       return true;
+}
+
+int amdgpu_ras_eeprom_process_recods(struct
amdgpu_ras_eeprom_control *control,
+                                           struct eeprom_table_record
*records,
+                                           bool write,
+                                           int num)
+{
+       int i, ret = 0;
+       struct i2c_msg *msgs;
+       unsigned char *buffs;
+       struct amdgpu_device *adev = to_amdgpu_device(control);
+
+       if (adev->asic_type != CHIP_VEGA20)
+               return 0;
+
+       buffs = kcalloc(num, EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_RECORD_SIZE,
+                        GFP_KERNEL);
+       if (!buffs)
+               return -ENOMEM;
+
+       mutex_lock(&control->tbl_mutex);
+
+       msgs = kcalloc(num, sizeof(*msgs), GFP_KERNEL);
+       if (!msgs) {
+               ret = -ENOMEM;
+               goto free_buff;
+       }
+
+       /* In case of overflow just start from beginning to not lose newest
records */
+       if (write && (control->next_addr + EEPROM_TABLE_RECORD_SIZE *
num > EEPROM_SIZE_BYTES))
+               control->next_addr = EEPROM_RECORD_START;
+
+
+       /*
+        * 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++) {
+               unsigned char *buff = &buffs[i*(EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_RECORD_SIZE)];
+               struct eeprom_table_record *record = &records[i];
+               struct i2c_msg *msg = &msgs[i];
+
+               control->next_addr =
+__correct_eeprom_dest_address(control->next_addr);
+
+               /*
+                * Update bits 16,17 of EEPROM address in I2C address by
setting them
+                * to bits 1,2 of Device address byte
+                */
+               msg->addr = EEPROM_I2C_TARGET_ADDR |
+                              ((control->next_addr &
EEPROM_ADDR_MSB_MASK) >> 15);
+               msg->flags      = write ? 0 : I2C_M_RD;
+               msg->len        = EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_RECORD_SIZE;
+               msg->buf        = buff;
+
+               /* Insert the EEPROM dest addess, bits 0-15 */
+               buff[0] = ((control->next_addr >> 8) & 0xff);
+               buff[1] = (control->next_addr & 0xff);
+
+               /* EEPROM table content is stored in LE format */
+               if (write)
+                       __encode_table_record_to_buff(control, record, buff
+
+EEPROM_ADDRESS_SIZE);
+
+               /*
+                * The destination EEPROM address might need to be
corrected to account
+                * for page or entire memory wrapping
+                */
+               control->next_addr += EEPROM_TABLE_RECORD_SIZE;
+       }
+
+       ret = i2c_transfer(&control->eeprom_accessor, msgs, num);
+       if (ret < 1) {
+               DRM_ERROR("Failed to process EEPROM table records,
ret:%d", ret);
+
+               /* TODO Restore prev next EEPROM address ? */
+               goto free_msgs;
+       }
+
+
+       if (!write) {
+               for (i = 0; i < num; i++) {
+                       unsigned char *buff =
&buffs[i*(EEPROM_ADDRESS_SIZE + EEPROM_TABLE_RECORD_SIZE)];


[Tao] space is needed before and after "*"



+                       struct eeprom_table_record *record = &records[i];


[Tao] add a space after "*"



+
+                       __decode_table_record_from_buff(control, record,
buff + EEPROM_ADDRESS_SIZE);
+               }
+       }
+
+       if (write) {
+               uint32_t old_hdr_byte_sum = __calc_hdr_byte_sum(control);
+
+               /*
+                * 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;
+               control->num_recs %= EEPROM_MAX_RECORD_NUM;
+               control->tbl_hdr.tbl_size += EEPROM_TABLE_RECORD_SIZE *
num;
+               if (control->tbl_hdr.tbl_size > EEPROM_SIZE_BYTES)
+                       control->tbl_hdr.tbl_size =
EEPROM_TABLE_HEADER_SIZE +
+                       control->num_recs * EEPROM_TABLE_RECORD_SIZE;
+
+               __update_tbl_checksum(control, records, num,
old_hdr_byte_sum);
+
+               __update_table_header(control, buffs);
+       } else if (!__validate_tbl_checksum(control, records, num)) {
+               DRM_WARN("EEPROM Table checksum mismatch!");
+               /* TODO Uncomment when EEPROM read/write is relliable
*/
+               /* ret = -EIO; */
+       }
+
+free_msgs:
+       kfree(msgs);
+
+free_buff:
+       kfree(buffs);
+
+       mutex_unlock(&control->tbl_mutex);
+
+       return ret == num ? 0 : -EIO;
+}
+
+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;
+       }
+
+       if (!amdgpu_ras_eeprom_process_recods(control, recs, true, 1)) {
+
+               memset(recs, 0, sizeof(*recs) * 1);
+
+               control->next_addr = EEPROM_RECORD_START;
+
+               if (!amdgpu_ras_eeprom_process_recods(control, recs, false,
1)) {
+                       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"); }
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
new file mode 100644
index 0000000..41f3fcb
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef _AMDGPU_RAS_EEPROM_H
+#define _AMDGPU_RAS_EEPROM_H
+
+#include <linux/i2c.h>
+
+struct amdgpu_device;
+
+enum amdgpu_ras_eeprom_err_type{
+       AMDGPU_RAS_EEPROM_ERR_PLACE_HOLDER,
+       AMDGPU_RAS_EEPROM_ERR_RECOVERABLE,
+       AMDGPU_RAS_EEPROM_ERR_NON_RECOVERABLE
+};
+
+struct amdgpu_ras_eeprom_table_header {
+       uint32_t header;
+       uint32_t version;
+       uint32_t first_rec_offset;
+       uint32_t tbl_size;
+       uint32_t checksum;
+}__attribute__((__packed__));
+
+struct amdgpu_ras_eeprom_control {
+       struct amdgpu_ras_eeprom_table_header tbl_hdr;
+       struct i2c_adapter eeprom_accessor;
+       uint32_t next_addr;
+       unsigned int num_recs;
+       struct mutex tbl_mutex;
+       bool bus_locked;
+       uint32_t tbl_byte_sum;
+};
+
+/*
+ * Represents single table record. Packed to be easily serialized into
+byte
+ * stream.
+ */
+struct eeprom_table_record {
+
+       union {
+               uint64_t address;
+               uint64_t offset;
+       };
+
+       uint64_t retired_page;
+       uint64_t ts;
+
+       enum amdgpu_ras_eeprom_err_type err_type;
+
+       union {
+               unsigned char bank;
+               unsigned char cu;
+       };
+
+       unsigned char mem_channel;
+       unsigned char mcumc_id;
+}__attribute__((__packed__));
+
+int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control);
+void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control
*control);
+
+int amdgpu_ras_eeprom_process_recods(struct
amdgpu_ras_eeprom_control *control,
+                                           struct eeprom_table_record
*records,
+                                           bool write,
+                                           int num);
+
+void amdgpu_ras_eeprom_test(struct amdgpu_ras_eeprom_control
*control);
+
+#endif // _AMDGPU_RAS_EEPROM_H
--
2.7.4





[-- Attachment #1.2: Type: text/html, Size: 30171 bytes --]

[-- Attachment #2: Type: text/plain, Size: 153 bytes --]

_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 4/4] drm/amdgpu: Vega20 SMU I2C HW engine controller.
       [not found]         ` <CADnq5_M9gSnLWaQLh3aUtrV0=Tj4USQt3rk-GUjHni6KSMxGbg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2019-08-22 15:35           ` Grodzovsky, Andrey
       [not found]             ` <fe5e0eff-e4bd-4722-6755-ebdbb9c05e03-5C7GfCeVMHo@public.gmane.org>
  0 siblings, 1 reply; 18+ messages in thread
From: Grodzovsky, Andrey @ 2019-08-22 15:35 UTC (permalink / raw)
  To: Alex Deucher
  Cc: Panariti, David, Pan, Xinhui, Lazar, Lijo, amd-gfx list, Deucher,
	Alexander, Tuikov, Luben, Russell, Kent, Zhou1, Tao, Quan, Evan,
	Zhang, Hawking


On 8/21/19 10:32 PM, Alex Deucher wrote:
> On Wed, Aug 21, 2019 at 4:02 PM Andrey Grodzovsky
> <andrey.grodzovsky@amd.com> wrote:
>> Implement HW I2C enigne controller to be used by the RAS EEPROM
>> table manager. This is based on code from ATITOOLs.
>>
>> v2:
>> Rename the file and all function prefixes to smu_v11_0_i2c
>>
>> By Luben's observation always fill the TX fifo to full so
>> we don't have garbadge interpreted by the slave as valid data.
>>
>> v3:
>> Remove preemption disable as the HW I2C controller will not
>> stop the clock on empty TX fifo and so it's not critical to
>> keep not empty queue.
>> Switch to fast mode 400 khz SCL clock for faster read and write.
>>
>> Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@amd.com>
>> ---
>>   drivers/gpu/drm/amd/amdgpu/Makefile            |   5 +-
>>   drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c |   5 +-
>>   drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c     | 710 +++++++++++++++++++++++++
>>   drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h     |  34 ++
>>   4 files changed, 751 insertions(+), 3 deletions(-)
>>   create mode 100644 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
>>   create mode 100644 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
>>
>> diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
>> index f016cf1..14733ff 100644
>> --- a/drivers/gpu/drm/amd/amdgpu/Makefile
>> +++ b/drivers/gpu/drm/amd/amdgpu/Makefile
>> @@ -38,6 +38,9 @@ ccflags-y := -I$(FULL_AMD_PATH)/include/asic_reg \
>>          -I$(FULL_AMD_DISPLAY_PATH)/amdgpu_dm \
>>          -I$(FULL_AMD_PATH)/amdkfd
>>
>> +
>> +
>> +
> Drop this random whitespace change.
>
>>   amdgpu-y := amdgpu_drv.o
>>
>>   # add KMS driver
>> @@ -54,7 +57,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
>>          amdgpu_gtt_mgr.o amdgpu_vram_mgr.o amdgpu_virt.o amdgpu_atomfirmware.o \
>>          amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o amdgpu_ids.o \
>>          amdgpu_gmc.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o amdgpu_vm_cpu.o \
>> -       amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o amdgpu_ras_eeprom.o
>> +       amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o amdgpu_ras_eeprom.o smu_v11_0_i2c.o
>>
>>   amdgpu-$(CONFIG_PERF_EVENTS) += amdgpu_pmu.o
>>
>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
>> index bf07515..e6b2e17 100644
>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
>> @@ -25,6 +25,7 @@
>>   #include "amdgpu.h"
>>   #include "amdgpu_ras.h"
>>   #include <linux/bits.h>
>> +#include "smu_v11_0_i2c.h"
>>
>>   #define EEPROM_I2C_TARGET_ADDR 0xA0
>>
>> @@ -111,7 +112,7 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
>>
>>          switch (adev->asic_type) {
>>          case CHIP_VEGA20:
>> -       /*TODO Add MI-60 */
>> +               ret = smu_v11_0_i2c_eeprom_control_init(&control->eeprom_accessor);
>>                  break;
>>
>>          default:
>> @@ -163,7 +164,7 @@ void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control *control)
>>
>>          switch (adev->asic_type) {
>>          case CHIP_VEGA20:
>> -               /*TODO Add MI-60 */
>> +               smu_v11_0_i2c_eeprom_control_fini(&control->eeprom_accessor);
>>                  break;
>>
>>          default:
>> diff --git a/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
>> new file mode 100644
>> index 0000000..24405fa
>> --- /dev/null
>> +++ b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
>> @@ -0,0 +1,710 @@
>> +/*
>> + * 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 "smuio/smuio_11_0_0_offset.h"
>> +#include "smuio/smuio_11_0_0_sh_mask.h"
>> +
>> +#include "smu_v11_0_i2c.h"
>> +#include "amdgpu.h"
>> +#include "soc15_common.h"
>> +#include <drm/drm_fixed.h>
>> +#include "amdgpu_amdkfd.h"
>> +#include <linux/i2c.h>
>> +#include "amdgpu_ras.h"
>> +
>> +/* error codes */
>> +#define I2C_OK                         0
>> +#define I2C_NAK_7B_ADDR_NOACK          1
>> +#define I2C_NAK_TXDATA_NOACK           2
>> +#define I2C_TIMEOUT                    4
>> +#define I2C_SW_TIMEOUT                 8
>> +#define I2C_ABORT                      0x10
>> +
>> +/* I2C transaction flags */
>> +#define I2C_NO_STOP    1
>> +#define I2C_RESTART    2
>> +
>> +#define to_amdgpu_device(x) (container_of(x, struct amdgpu_ras, eeprom_control.eeprom_accessor))->adev
>> +#define to_eeprom_control(x) container_of(x, struct amdgpu_ras_eeprom_control, eeprom_accessor)
>> +
>> +static void smu_v11_0_i2c_dis_clock_gating(struct i2c_adapter *control)
>> +{
>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>> +       uint32_t reg;
>> +
>> +       reg = RREG32_SOC15(SMUIO, 0, mmSMUIO_PWRMGT);
>> +       reg = REG_SET_FIELD(reg, SMUIO_PWRMGT, i2c_clk_gate_en, 0);
>> +       WREG32_SOC15(SMUIO, 0, mmSMUIO_PWRMGT, reg);
>> +}
> Do we need to worry about re-enabling clock gating when we are done?


In smu_11_smuio_mas doc this step is missing for I2C0 (sec. 18.4.1.1) 
and hence probably also was missing from the original code this was 
ported so I am not sure. The side effect of this staying disabled is 
less  power saving I think.

Andrey


>
>
>> +
>> +
>> +static void smu_v11_0_i2c_enable(struct i2c_adapter *control, bool enable)
>> +{
>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>> +
>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, enable ? 1 : 0);
>> +}
>> +
>> +static void smu_v11_0_i2c_clear_status(struct i2c_adapter *control)
>> +{
>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>> +       /* do */
>> +       {
>> +               RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CLR_INTR);
>> +
>> +       } /* while (reg_CKSVII2C_ic_clr_intr == 0) */
>> +}
>> +
>> +static void smu_v11_0_i2c_configure(struct i2c_adapter *control)
>> +{
>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>> +       uint32_t reg = 0;
>> +
>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_SLAVE_DISABLE, 1);
>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_RESTART_EN, 1);
>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_10BITADDR_MASTER, 0);
>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_10BITADDR_SLAVE, 0);
>> +       /* Standard mode */
>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_MAX_SPEED_MODE, 2);
>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_MASTER_MODE, 1);
>> +
>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CON, reg);
>> +}
>> +
>> +static void smu_v11_0_i2c_set_clock(struct i2c_adapter *control)
>> +{
>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>> +
>> +       /*
>> +        * Standard mode speed, These values are taken from SMUIO MAS,
>> +        * but are different from what is given is
>> +        * Synopsys spec. The values here are based on assumption
>> +        * that refclock is 100MHz
>> +        *
>> +        * Configuration for standard mode; Speed = 100kbps
>> +        * Scale linearly, for now only support standard speed clock
>> +        * This will work only with 100M ref clock
>> +        *
>> +        * TBD:Change the calculation to take into account ref clock values also.
>> +        */
>> +
>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_FS_SPKLEN, 2);
>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SS_SCL_HCNT, 120);
>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SS_SCL_LCNT, 130);
>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SDA_HOLD, 20);
>> +}
>> +
>> +static void smu_v11_0_i2c_set_address(struct i2c_adapter *control, uint8_t address)
>> +{
>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>> +
>> +       /* Convert fromr 8-bit to 7-bit address */
>> +       address >>= 1;
>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TAR, (address & 0xFF));
>> +}
>> +
>> +static uint32_t smu_v11_0_i2c_poll_tx_status(struct i2c_adapter *control)
>> +{
>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>> +       uint32_t ret = I2C_OK;
>> +       uint32_t reg, reg_c_tx_abrt_source;
>> +
>> +       /*Check if transmission is completed */
>> +       unsigned long  timeout_counter = jiffies + msecs_to_jiffies(20);
>> +
>> +       do {
>> +               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
>> +
>> +               if (time_after(jiffies, timeout_counter)) {
>> +                       ret |= I2C_SW_TIMEOUT;
>> +                       break;
>> +               }
>> +       } while (REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFE) == 0);
>> +
>> +       if (ret != I2C_OK)
>> +               return ret;
>> +
>> +       /* This only checks if NAK is received and transaction got aborted */
>> +       reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_INTR_STAT);
>> +
>> +       if (REG_GET_FIELD(reg, CKSVII2C_IC_INTR_STAT, R_TX_ABRT) == 1) {
>> +               reg_c_tx_abrt_source = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TX_ABRT_SOURCE);
>> +               DRM_INFO("TX was terminated, IC_TX_ABRT_SOURCE val is:%x", reg_c_tx_abrt_source);
>> +
>> +               /* Check for stop due to NACK */
>> +               if (REG_GET_FIELD(reg_c_tx_abrt_source,
>> +                                 CKSVII2C_IC_TX_ABRT_SOURCE,
>> +                                 ABRT_TXDATA_NOACK) == 1) {
>> +
>> +                       ret |= I2C_NAK_TXDATA_NOACK;
>> +
>> +               } else if (REG_GET_FIELD(reg_c_tx_abrt_source,
>> +                                        CKSVII2C_IC_TX_ABRT_SOURCE,
>> +                                        ABRT_7B_ADDR_NOACK) == 1) {
>> +
>> +                       ret |= I2C_NAK_7B_ADDR_NOACK;
>> +               } else {
>> +                       ret |= I2C_ABORT;
>> +               }
>> +
>> +               smu_v11_0_i2c_clear_status(control);
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +static uint32_t smu_v11_0_i2c_poll_rx_status(struct i2c_adapter *control)
>> +{
>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>> +       uint32_t ret = I2C_OK;
>> +       uint32_t reg_ic_status, reg_c_tx_abrt_source;
>> +
>> +       reg_c_tx_abrt_source = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TX_ABRT_SOURCE);
>> +
>> +       /* If slave is not present */
>> +       if (REG_GET_FIELD(reg_c_tx_abrt_source,
>> +                         CKSVII2C_IC_TX_ABRT_SOURCE,
>> +                         ABRT_7B_ADDR_NOACK) == 1) {
>> +               ret |= I2C_NAK_7B_ADDR_NOACK;
>> +
>> +               smu_v11_0_i2c_clear_status(control);
>> +       } else {  /* wait till some data is there in RXFIFO */
>> +               /* Poll for some byte in RXFIFO */
>> +               unsigned long  timeout_counter = jiffies + msecs_to_jiffies(20);
>> +
>> +               do {
>> +                       reg_ic_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
>> +
>> +                       if (time_after(jiffies, timeout_counter)) {
>> +                               ret |= I2C_SW_TIMEOUT;
>> +                               break;
>> +                       }
>> +               } while (REG_GET_FIELD(reg_ic_status, CKSVII2C_IC_STATUS, RFNE) == 0);
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +
>> +
>> +
>> +/**
>> + * smu_v11_0_i2c_transmit - Send a block of data over the I2C bus to a slave device.
>> + *
>> + * @address: The I2C address of the slave device.
>> + * @data: The data to transmit over the bus.
>> + * @numbytes: The amount of data to transmit.
>> + * @i2c_flag: Flags for transmission
>> + *
>> + * Returns 0 on success or error.
>> + */
>> +static uint32_t smu_v11_0_i2c_transmit(struct i2c_adapter *control,
>> +                                 uint8_t address, uint8_t *data,
>> +                                 uint32_t numbytes, uint32_t i2c_flag)
>> +{
>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>> +       uint32_t bytes_sent, reg, ret = 0;
>> +       unsigned long  timeout_counter;
>> +
>> +       bytes_sent = 0;
>> +
>> +       DRM_DEBUG_DRIVER("I2C_Transmit(), address = %x, bytes = %d , data: ",
>> +                (uint16_t)address, numbytes);
>> +
>> +       if (drm_debug & DRM_UT_DRIVER) {
>> +               print_hex_dump(KERN_INFO, "data: ", DUMP_PREFIX_NONE,
>> +                              16, 1, data, numbytes, false);
>> +       }
>> +
>> +       /* Set the I2C slave address */
>> +       smu_v11_0_i2c_set_address(control, address);
>> +       /* Enable I2C */
>> +       smu_v11_0_i2c_enable(control, true);
>> +
>> +       /* Clear status bits */
>> +       smu_v11_0_i2c_clear_status(control);
>> +
>> +
>> +       timeout_counter = jiffies + msecs_to_jiffies(20);
>> +
>> +       while (numbytes > 0) {
>> +               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
>> +               if (REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFNF)) {
>> +                       do {
>> +                               reg = 0;
>> +                               /*
>> +                                * Prepare transaction, no need to set RESTART. I2C engine will send
>> +                                * START as soon as it sees data in TXFIFO
>> +                                */
>> +                               if (bytes_sent == 0)
>> +                                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, RESTART,
>> +                                                           (i2c_flag & I2C_RESTART) ? 1 : 0);
>> +                               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, data[bytes_sent]);
>> +
>> +                               /* determine if we need to send STOP bit or not */
>> +                               if (numbytes == 1)
>> +                                       /* Final transaction, so send stop unless I2C_NO_STOP */
>> +                                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, STOP,
>> +                                                           (i2c_flag & I2C_NO_STOP) ? 0 : 1);
>> +                               /* Write */
>> +                               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 0);
>> +                               WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg);
>> +
>> +                               /* Record that the bytes were transmitted */
>> +                               bytes_sent++;
>> +                               numbytes--;
>> +
>> +                               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
>> +
>> +                       } while (numbytes &&  REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFNF));
>> +               }
>> +
>> +
>> +               if (time_after(jiffies, timeout_counter)) {
>> +                       ret |= I2C_SW_TIMEOUT;
>> +                       goto err;
>> +               }
>> +       }
>> +
>> +       ret = smu_v11_0_i2c_poll_tx_status(control);
>> +
>> +err:
>> +       /* Any error, no point in proceeding */
>> +       if (ret != I2C_OK) {
>> +               if (ret & I2C_SW_TIMEOUT)
>> +                       DRM_ERROR("TIMEOUT ERROR !!!");
>> +
>> +               if (ret & I2C_NAK_7B_ADDR_NOACK)
>> +                       DRM_ERROR("Received I2C_NAK_7B_ADDR_NOACK !!!");
>> +
>> +
>> +               if (ret & I2C_NAK_TXDATA_NOACK)
>> +                       DRM_ERROR("Received I2C_NAK_TXDATA_NOACK !!!");
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +
>> +/**
>> + * smu_v11_0_i2c_receive - Receive a block of data over the I2C bus from a slave device.
>> + *
>> + * @address: The I2C address of the slave device.
>> + * @numbytes: The amount of data to transmit.
>> + * @i2c_flag: Flags for transmission
>> + *
>> + * Returns 0 on success or error.
>> + */
>> +static uint32_t smu_v11_0_i2c_receive(struct i2c_adapter *control,
>> +                                uint8_t address, uint8_t *data,
>> +                                uint32_t numbytes, uint8_t i2c_flag)
>> +{
>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>> +       uint32_t bytes_received, ret = I2C_OK;
>> +
>> +       bytes_received = 0;
>> +
>> +       /* Set the I2C slave address */
>> +       smu_v11_0_i2c_set_address(control, address);
>> +
>> +       /* Enable I2C */
>> +       smu_v11_0_i2c_enable(control, true);
>> +
>> +       while (numbytes > 0) {
>> +               uint32_t reg = 0;
>> +
>> +               smu_v11_0_i2c_clear_status(control);
>> +
>> +
>> +               /* Prepare transaction */
>> +
>> +               /* Each time we disable I2C, so this is not a restart */
>> +               if (bytes_received == 0)
>> +                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, RESTART,
>> +                                           (i2c_flag & I2C_RESTART) ? 1 : 0);
>> +
>> +               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, 0);
>> +               /* Read */
>> +               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 1);
>> +
>> +               /* Transmitting last byte */
>> +               if (numbytes == 1)
>> +                       /* Final transaction, so send stop if requested */
>> +                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, STOP,
>> +                                           (i2c_flag & I2C_NO_STOP) ? 0 : 1);
>> +
>> +               WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg);
>> +
>> +               ret = smu_v11_0_i2c_poll_rx_status(control);
>> +
>> +               /* Any error, no point in proceeding */
>> +               if (ret != I2C_OK) {
>> +                       if (ret & I2C_SW_TIMEOUT)
>> +                               DRM_ERROR("TIMEOUT ERROR !!!");
>> +
>> +                       if (ret & I2C_NAK_7B_ADDR_NOACK)
>> +                               DRM_ERROR("Received I2C_NAK_7B_ADDR_NOACK !!!");
>> +
>> +                       if (ret & I2C_NAK_TXDATA_NOACK)
>> +                               DRM_ERROR("Received I2C_NAK_TXDATA_NOACK !!!");
>> +
>> +                       break;
>> +               }
>> +
>> +               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD);
>> +               data[bytes_received] = REG_GET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT);
>> +
>> +               /* Record that the bytes were received */
>> +               bytes_received++;
>> +               numbytes--;
>> +       }
>> +
>> +       DRM_DEBUG_DRIVER("I2C_Receive(), address = %x, bytes = %d, data :",
>> +                 (uint16_t)address, bytes_received);
>> +
>> +       if (drm_debug & DRM_UT_DRIVER) {
>> +               print_hex_dump(KERN_INFO, "data: ", DUMP_PREFIX_NONE,
>> +                              16, 1, data, bytes_received, false);
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +static void smu_v11_0_i2c_abort(struct i2c_adapter *control)
>> +{
>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>> +       uint32_t reg = 0;
>> +
>> +       /* Enable I2C engine; */
>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_ENABLE, ENABLE, 1);
>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, reg);
>> +
>> +       /* Abort previous transaction */
>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_ENABLE, ABORT, 1);
>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, reg);
>> +
>> +       DRM_DEBUG_DRIVER("I2C_Abort() Done.");
>> +}
>> +
>> +
>> +static bool smu_v11_0_i2c_activity_done(struct i2c_adapter *control)
>> +{
>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>> +
>> +       const uint32_t IDLE_TIMEOUT = 1024;
>> +       uint32_t timeoutCount = 0;
> kernel coding style prefers non CamelCase.  E.g., timeout_count.
>
>> +       uint32_t reg_ic_enable, reg_ic_enable_status, reg_ic_clr_activity;
>> +
>> +       reg_ic_enable_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE_STATUS);
>> +       reg_ic_enable = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE);
>> +
>> +
>> +       if ((REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) &&
>> +           (REG_GET_FIELD(reg_ic_enable_status, CKSVII2C_IC_ENABLE_STATUS, IC_EN) == 1)) {
>> +               /*
>> +                * Nobody is using I2C engine, but engine remains active because
>> +                * someone missed to send STOP
>> +                */
>> +               smu_v11_0_i2c_abort(control);
>> +       } else if (REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) {
>> +               /* Nobody is using I2C engine */
>> +               return true;
>> +       }
>> +
>> +       /* Keep reading activity bit until it's cleared */
>> +       do {
>> +               reg_ic_clr_activity = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CLR_ACTIVITY);
>> +
>> +               if (REG_GET_FIELD(reg_ic_clr_activity,
>> +                   CKSVII2C_IC_CLR_ACTIVITY, CLR_ACTIVITY) == 0)
>> +                       return true;
>> +
>> +               ++timeoutCount;
>> +
>> +       } while (timeoutCount < IDLE_TIMEOUT);
>> +
>> +       return false;
>> +}
>> +
>> +static void smu_v11_0_i2c_init(struct i2c_adapter *control)
>> +{
>> +       /* Disable clock gating */
>> +       smu_v11_0_i2c_dis_clock_gating(control);
>> +
>> +       if (!smu_v11_0_i2c_activity_done(control))
>> +               DRM_WARN("I2C busy !");
>> +
>> +       /* Disable I2C */
>> +       smu_v11_0_i2c_enable(control, false);
>> +
>> +       /* Configure I2C to operate as master and in standard mode */
>> +       smu_v11_0_i2c_configure(control);
>> +
>> +       /* Initialize the clock to 50 kHz default */
>> +       smu_v11_0_i2c_set_clock(control);
>> +
>> +}
>> +
>> +static void smu_v11_0_i2c_fini(struct i2c_adapter *control)
>> +{
>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>> +       uint32_t reg_ic_enable_status, reg_ic_enable;
>> +
>> +       smu_v11_0_i2c_enable(control, false);
>> +
>> +       /* Double check if disabled, else force abort */
>> +       reg_ic_enable_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE_STATUS);
>> +       reg_ic_enable = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE);
>> +
>> +       if ((REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) &&
>> +           (REG_GET_FIELD(reg_ic_enable_status,
>> +                          CKSVII2C_IC_ENABLE_STATUS, IC_EN) == 1)) {
>> +               /*
>> +                * Nobody is using I2C engine, but engine remains active because
>> +                * someone missed to send STOP
>> +                */
>> +               smu_v11_0_i2c_abort(control);
>> +       }
>> +
>> +}
>> +
>> +static bool smu_v11_0_i2c_bus_lock(struct i2c_adapter *control)
>> +{
>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>> +
>> +       /* Send  PPSMC_MSG_RequestI2CBus */
>> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access)
>> +               goto fail;
>> +
>> +
>> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access(adev->powerplay.pp_handle, true))
>> +               return true;
>> +
>> +fail:
>> +       return false;
>> +}
>> +
>> +static bool smu_v11_0_i2c_bus_unlock(struct i2c_adapter *control)
>> +{
>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>> +
>> +       /* Send  PPSMC_MSG_RequestI2CBus */
>> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access)
>> +               goto fail;
>> +
>> +       /* Send  PPSMC_MSG_ReleaseI2CBus */
>> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access(adev->powerplay.pp_handle,
>> +                                                            false))
>> +               return true;
>> +
>> +fail:
>> +       return false;
>> +}
>> +
>> +/***************************** EEPROM I2C GLUE ****************************/
>> +
>> +static uint32_t smu_v11_0_i2c_eeprom_read_data(struct i2c_adapter *control,
>> +                                              uint8_t address,
>> +                                              uint8_t *data,
>> +                                              uint32_t numbytes)
>> +{
>> +       uint32_t  ret = 0;
>> +
>> +       /* First 2 bytes are dummy write to set EEPROM address */
>> +       ret = smu_v11_0_i2c_transmit(control, address, data, 2, I2C_NO_STOP);
>> +       if (ret != I2C_OK)
>> +               goto fail;
>> +
>> +       /* Now read data starting with that address */
>> +       ret = smu_v11_0_i2c_receive(control, address, data + 2, numbytes - 2,
>> +                                   I2C_RESTART);
>> +
>> +fail:
>> +       if (ret != I2C_OK)
>> +               DRM_ERROR("ReadData() - I2C error occurred :%x", ret);
>> +
>> +       return ret;
>> +}
>> +
>> +static uint32_t smu_v11_0_i2c_eeprom_write_data(struct i2c_adapter *control,
>> +                                               uint8_t address,
>> +                                               uint8_t *data,
>> +                                               uint32_t numbytes)
>> +{
>> +       uint32_t  ret;
>> +
>> +       ret = smu_v11_0_i2c_transmit(control, address, data, numbytes, 0);
>> +
>> +       if (ret != I2C_OK)
>> +               DRM_ERROR("WriteI2CData() - I2C error occurred :%x", ret);
>> +       else
>> +               /*
>> +                * According to EEPROM spec there is a MAX of 10 ms required for
>> +                * EEPROM to flush internal RX buffer after STOP was issued at the
>> +                * end of write transaction. During this time the EEPROM will not be
>> +                * responsive to any more commands - so wait a bit more.
>> +                *
>> +                * TODO Improve to wait for first ACK for slave address after
>> +                * internal write cycle done.
>> +                */
>> +               msleep(10);
>> +
>> +       return ret;
>> +
>> +}
>> +
>> +static void lock_bus(struct i2c_adapter *i2c, unsigned int flags)
>> +{
>> +       struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c);
>> +
>> +       if (!smu_v11_0_i2c_bus_lock(i2c)) {
>> +               DRM_ERROR("Failed to lock the bus from SMU");
>> +               return;
>> +       }
>> +
>> +       control->bus_locked = true;
>> +}
>> +
>> +static int trylock_bus(struct i2c_adapter *i2c, unsigned int flags)
>> +{
>> +       WARN_ONCE(1, "This operation not supposed to run in atomic context!");
>> +       return false;
>> +}
>> +
>> +static void unlock_bus(struct i2c_adapter *i2c, unsigned int flags)
>> +{
>> +       struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c);
>> +
>> +       if (!smu_v11_0_i2c_bus_unlock(i2c)) {
>> +               DRM_ERROR("Failed to unlock the bus from SMU");
>> +               return;
>> +       }
>> +
>> +       control->bus_locked = false;
>> +}
>> +
>> +static const struct i2c_lock_operations smu_v11_0_i2c_i2c_lock_ops = {
>> +       .lock_bus = lock_bus,
>> +       .trylock_bus = trylock_bus,
>> +       .unlock_bus = unlock_bus,
>> +};
>> +
>> +static int smu_v11_0_i2c_eeprom_i2c_xfer(struct i2c_adapter *i2c_adap,
>> +                             struct i2c_msg *msgs, int num)
>> +{
>> +       int i, ret;
>> +       struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c_adap);
>> +
>> +       if (!control->bus_locked) {
>> +               DRM_ERROR("I2C bus unlocked, stopping transaction!");
>> +               return -EIO;
>> +       }
>> +
>> +       smu_v11_0_i2c_init(i2c_adap);
>> +
>> +       for (i = 0; i < num; i++) {
>> +               if (msgs[i].flags & I2C_M_RD)
>> +                       ret = smu_v11_0_i2c_eeprom_read_data(i2c_adap,
>> +                                                       (uint8_t)msgs[i].addr,
>> +                                                       msgs[i].buf, msgs[i].len);
>> +               else
>> +                       ret = smu_v11_0_i2c_eeprom_write_data(i2c_adap,
>> +                                                        (uint8_t)msgs[i].addr,
>> +                                                        msgs[i].buf, msgs[i].len);
>> +
>> +               if (ret != I2C_OK) {
>> +                       num = -EIO;
>> +                       break;
>> +               }
>> +       }
>> +
>> +       smu_v11_0_i2c_fini(i2c_adap);
>> +       return num;
>> +}
>> +
>> +static u32 smu_v11_0_i2c_eeprom_i2c_func(struct i2c_adapter *adap)
>> +{
>> +       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
>> +}
>> +
>> +
>> +static const struct i2c_algorithm smu_v11_0_i2c_eeprom_i2c_algo = {
>> +       .master_xfer = smu_v11_0_i2c_eeprom_i2c_xfer,
>> +       .functionality = smu_v11_0_i2c_eeprom_i2c_func,
>> +};
>> +
>> +int smu_v11_0_i2c_eeprom_control_init(struct i2c_adapter *control)
>> +{
>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>> +       int res;
>> +
>> +       control->owner = THIS_MODULE;
>> +       control->class = I2C_CLASS_SPD;
>> +       control->dev.parent = &adev->pdev->dev;
>> +       control->algo = &smu_v11_0_i2c_eeprom_i2c_algo;
>> +       snprintf(control->name, sizeof(control->name), "RAS EEPROM");
>> +       control->lock_ops = &smu_v11_0_i2c_i2c_lock_ops;
>> +
>> +       res = i2c_add_adapter(control);
>> +       if (res)
>> +               DRM_ERROR("Failed to register hw i2c, err: %d\n", res);
>> +
>> +       return res;
>> +}
>> +
>> +void smu_v11_0_i2c_eeprom_control_fini(struct i2c_adapter *control)
>> +{
>> +       i2c_del_adapter(control);
>> +}
>> +
>> +#define I2C_TARGET_ADDR 0xA0
>> +
>> +bool smu_v11_0_i2c_test_bus(struct i2c_adapter *control)
>> +{
>> +
>> +       uint32_t ret = I2C_OK;
>> +       uint8_t data[6] = {0xf, 0, 0xde, 0xad, 0xbe, 0xef};
>> +
>> +
>> +       DRM_INFO("Begin");
>> +
>> +       if (!smu_v11_0_i2c_bus_lock(control)) {
>> +               DRM_ERROR("Failed to lock the bus!.");
>> +               return false;
>> +       }
>> +
>> +       smu_v11_0_i2c_init(control);
>> +
>> +       /* Write 0xde to address 0x0000 on the EEPROM */
>> +       ret = smu_v11_0_i2c_eeprom_write_data(control, I2C_TARGET_ADDR, data, 6);
>> +
>> +       ret = smu_v11_0_i2c_eeprom_read_data(control, I2C_TARGET_ADDR, data, 6);
>> +
>> +       smu_v11_0_i2c_fini(control);
>> +
>> +       smu_v11_0_i2c_bus_unlock(control);
>> +
>> +
>> +       DRM_INFO("End");
>> +       return true;
>> +}
> Leftover debugging function?
>
>> diff --git a/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
>> new file mode 100644
>> index 0000000..973f28d
>> --- /dev/null
>> +++ b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
>> @@ -0,0 +1,34 @@
>> +/*
>> + * 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.
>> + *
>> + */
>> +
>> +#ifndef SMU_V11_I2C_CONTROL_H
>> +#define SMU_V11_I2C_CONTROL_H
>> +
>> +#include <linux/types.h>
>> +
>> +struct i2c_adapter;
>> +
>> +int smu_v11_0_i2c_eeprom_control_init(struct i2c_adapter *control);
>> +void smu_v11_0_i2c_eeprom_control_fini(struct i2c_adapter *control);
>> +
>> +#endif
>> --
>> 2.7.4
>>
>> _______________________________________________
>> amd-gfx mailing list
>> amd-gfx@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/amd-gfx
_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 4/4] drm/amdgpu: Vega20 SMU I2C HW engine controller.
       [not found]             ` <fe5e0eff-e4bd-4722-6755-ebdbb9c05e03-5C7GfCeVMHo@public.gmane.org>
@ 2019-08-22 15:59               ` Alex Deucher
       [not found]                 ` <CADnq5_NSOATAdC_z9R5BoVbM=eSLQbLYVvdPDOqZYa2Gyz_V6A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  0 siblings, 1 reply; 18+ messages in thread
From: Alex Deucher @ 2019-08-22 15:59 UTC (permalink / raw)
  To: Grodzovsky, Andrey
  Cc: Panariti, David, Pan, Xinhui, Lazar, Lijo, amd-gfx list, Deucher,
	Alexander, Tuikov, Luben, Russell, Kent, Zhou1, Tao, Quan, Evan,
	Zhang, Hawking

On Thu, Aug 22, 2019 at 11:35 AM Grodzovsky, Andrey
<Andrey.Grodzovsky@amd.com> wrote:
>
>
> On 8/21/19 10:32 PM, Alex Deucher wrote:
> > On Wed, Aug 21, 2019 at 4:02 PM Andrey Grodzovsky
> > <andrey.grodzovsky@amd.com> wrote:
> >> Implement HW I2C enigne controller to be used by the RAS EEPROM
> >> table manager. This is based on code from ATITOOLs.
> >>
> >> v2:
> >> Rename the file and all function prefixes to smu_v11_0_i2c
> >>
> >> By Luben's observation always fill the TX fifo to full so
> >> we don't have garbadge interpreted by the slave as valid data.
> >>
> >> v3:
> >> Remove preemption disable as the HW I2C controller will not
> >> stop the clock on empty TX fifo and so it's not critical to
> >> keep not empty queue.
> >> Switch to fast mode 400 khz SCL clock for faster read and write.
> >>
> >> Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@amd.com>
> >> ---
> >>   drivers/gpu/drm/amd/amdgpu/Makefile            |   5 +-
> >>   drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c |   5 +-
> >>   drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c     | 710 +++++++++++++++++++++++++
> >>   drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h     |  34 ++
> >>   4 files changed, 751 insertions(+), 3 deletions(-)
> >>   create mode 100644 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
> >>   create mode 100644 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
> >>
> >> diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
> >> index f016cf1..14733ff 100644
> >> --- a/drivers/gpu/drm/amd/amdgpu/Makefile
> >> +++ b/drivers/gpu/drm/amd/amdgpu/Makefile
> >> @@ -38,6 +38,9 @@ ccflags-y := -I$(FULL_AMD_PATH)/include/asic_reg \
> >>          -I$(FULL_AMD_DISPLAY_PATH)/amdgpu_dm \
> >>          -I$(FULL_AMD_PATH)/amdkfd
> >>
> >> +
> >> +
> >> +
> > Drop this random whitespace change.
> >
> >>   amdgpu-y := amdgpu_drv.o
> >>
> >>   # add KMS driver
> >> @@ -54,7 +57,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
> >>          amdgpu_gtt_mgr.o amdgpu_vram_mgr.o amdgpu_virt.o amdgpu_atomfirmware.o \
> >>          amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o amdgpu_ids.o \
> >>          amdgpu_gmc.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o amdgpu_vm_cpu.o \
> >> -       amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o amdgpu_ras_eeprom.o
> >> +       amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o amdgpu_ras_eeprom.o smu_v11_0_i2c.o
> >>
> >>   amdgpu-$(CONFIG_PERF_EVENTS) += amdgpu_pmu.o
> >>
> >> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
> >> index bf07515..e6b2e17 100644
> >> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
> >> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
> >> @@ -25,6 +25,7 @@
> >>   #include "amdgpu.h"
> >>   #include "amdgpu_ras.h"
> >>   #include <linux/bits.h>
> >> +#include "smu_v11_0_i2c.h"
> >>
> >>   #define EEPROM_I2C_TARGET_ADDR 0xA0
> >>
> >> @@ -111,7 +112,7 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
> >>
> >>          switch (adev->asic_type) {
> >>          case CHIP_VEGA20:
> >> -       /*TODO Add MI-60 */
> >> +               ret = smu_v11_0_i2c_eeprom_control_init(&control->eeprom_accessor);
> >>                  break;
> >>
> >>          default:
> >> @@ -163,7 +164,7 @@ void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control *control)
> >>
> >>          switch (adev->asic_type) {
> >>          case CHIP_VEGA20:
> >> -               /*TODO Add MI-60 */
> >> +               smu_v11_0_i2c_eeprom_control_fini(&control->eeprom_accessor);
> >>                  break;
> >>
> >>          default:
> >> diff --git a/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
> >> new file mode 100644
> >> index 0000000..24405fa
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
> >> @@ -0,0 +1,710 @@
> >> +/*
> >> + * 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 "smuio/smuio_11_0_0_offset.h"
> >> +#include "smuio/smuio_11_0_0_sh_mask.h"
> >> +
> >> +#include "smu_v11_0_i2c.h"
> >> +#include "amdgpu.h"
> >> +#include "soc15_common.h"
> >> +#include <drm/drm_fixed.h>
> >> +#include "amdgpu_amdkfd.h"
> >> +#include <linux/i2c.h>
> >> +#include "amdgpu_ras.h"
> >> +
> >> +/* error codes */
> >> +#define I2C_OK                         0
> >> +#define I2C_NAK_7B_ADDR_NOACK          1
> >> +#define I2C_NAK_TXDATA_NOACK           2
> >> +#define I2C_TIMEOUT                    4
> >> +#define I2C_SW_TIMEOUT                 8
> >> +#define I2C_ABORT                      0x10
> >> +
> >> +/* I2C transaction flags */
> >> +#define I2C_NO_STOP    1
> >> +#define I2C_RESTART    2
> >> +
> >> +#define to_amdgpu_device(x) (container_of(x, struct amdgpu_ras, eeprom_control.eeprom_accessor))->adev
> >> +#define to_eeprom_control(x) container_of(x, struct amdgpu_ras_eeprom_control, eeprom_accessor)
> >> +
> >> +static void smu_v11_0_i2c_dis_clock_gating(struct i2c_adapter *control)
> >> +{
> >> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >> +       uint32_t reg;
> >> +
> >> +       reg = RREG32_SOC15(SMUIO, 0, mmSMUIO_PWRMGT);
> >> +       reg = REG_SET_FIELD(reg, SMUIO_PWRMGT, i2c_clk_gate_en, 0);
> >> +       WREG32_SOC15(SMUIO, 0, mmSMUIO_PWRMGT, reg);
> >> +}
> > Do we need to worry about re-enabling clock gating when we are done?
>
>
> In smu_11_smuio_mas doc this step is missing for I2C0 (sec. 18.4.1.1)
> and hence probably also was missing from the original code this was
> ported so I am not sure. The side effect of this staying disabled is
> less  power saving I think.

Can we check the state of SMUIO_PWRMGT.i2c_clk_gate_en at init time,
and then, if it's enabled, we can disable it in lock_bus and enable it
in unlock_bus.

Alex

>
> Andrey
>
>
> >
> >
> >> +
> >> +
> >> +static void smu_v11_0_i2c_enable(struct i2c_adapter *control, bool enable)
> >> +{
> >> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >> +
> >> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, enable ? 1 : 0);
> >> +}
> >> +
> >> +static void smu_v11_0_i2c_clear_status(struct i2c_adapter *control)
> >> +{
> >> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >> +       /* do */
> >> +       {
> >> +               RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CLR_INTR);
> >> +
> >> +       } /* while (reg_CKSVII2C_ic_clr_intr == 0) */
> >> +}
> >> +
> >> +static void smu_v11_0_i2c_configure(struct i2c_adapter *control)
> >> +{
> >> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >> +       uint32_t reg = 0;
> >> +
> >> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_SLAVE_DISABLE, 1);
> >> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_RESTART_EN, 1);
> >> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_10BITADDR_MASTER, 0);
> >> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_10BITADDR_SLAVE, 0);
> >> +       /* Standard mode */
> >> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_MAX_SPEED_MODE, 2);
> >> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_MASTER_MODE, 1);
> >> +
> >> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CON, reg);
> >> +}
> >> +
> >> +static void smu_v11_0_i2c_set_clock(struct i2c_adapter *control)
> >> +{
> >> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >> +
> >> +       /*
> >> +        * Standard mode speed, These values are taken from SMUIO MAS,
> >> +        * but are different from what is given is
> >> +        * Synopsys spec. The values here are based on assumption
> >> +        * that refclock is 100MHz
> >> +        *
> >> +        * Configuration for standard mode; Speed = 100kbps
> >> +        * Scale linearly, for now only support standard speed clock
> >> +        * This will work only with 100M ref clock
> >> +        *
> >> +        * TBD:Change the calculation to take into account ref clock values also.
> >> +        */
> >> +
> >> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_FS_SPKLEN, 2);
> >> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SS_SCL_HCNT, 120);
> >> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SS_SCL_LCNT, 130);
> >> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SDA_HOLD, 20);
> >> +}
> >> +
> >> +static void smu_v11_0_i2c_set_address(struct i2c_adapter *control, uint8_t address)
> >> +{
> >> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >> +
> >> +       /* Convert fromr 8-bit to 7-bit address */
> >> +       address >>= 1;
> >> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TAR, (address & 0xFF));
> >> +}
> >> +
> >> +static uint32_t smu_v11_0_i2c_poll_tx_status(struct i2c_adapter *control)
> >> +{
> >> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >> +       uint32_t ret = I2C_OK;
> >> +       uint32_t reg, reg_c_tx_abrt_source;
> >> +
> >> +       /*Check if transmission is completed */
> >> +       unsigned long  timeout_counter = jiffies + msecs_to_jiffies(20);
> >> +
> >> +       do {
> >> +               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
> >> +
> >> +               if (time_after(jiffies, timeout_counter)) {
> >> +                       ret |= I2C_SW_TIMEOUT;
> >> +                       break;
> >> +               }
> >> +       } while (REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFE) == 0);
> >> +
> >> +       if (ret != I2C_OK)
> >> +               return ret;
> >> +
> >> +       /* This only checks if NAK is received and transaction got aborted */
> >> +       reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_INTR_STAT);
> >> +
> >> +       if (REG_GET_FIELD(reg, CKSVII2C_IC_INTR_STAT, R_TX_ABRT) == 1) {
> >> +               reg_c_tx_abrt_source = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TX_ABRT_SOURCE);
> >> +               DRM_INFO("TX was terminated, IC_TX_ABRT_SOURCE val is:%x", reg_c_tx_abrt_source);
> >> +
> >> +               /* Check for stop due to NACK */
> >> +               if (REG_GET_FIELD(reg_c_tx_abrt_source,
> >> +                                 CKSVII2C_IC_TX_ABRT_SOURCE,
> >> +                                 ABRT_TXDATA_NOACK) == 1) {
> >> +
> >> +                       ret |= I2C_NAK_TXDATA_NOACK;
> >> +
> >> +               } else if (REG_GET_FIELD(reg_c_tx_abrt_source,
> >> +                                        CKSVII2C_IC_TX_ABRT_SOURCE,
> >> +                                        ABRT_7B_ADDR_NOACK) == 1) {
> >> +
> >> +                       ret |= I2C_NAK_7B_ADDR_NOACK;
> >> +               } else {
> >> +                       ret |= I2C_ABORT;
> >> +               }
> >> +
> >> +               smu_v11_0_i2c_clear_status(control);
> >> +       }
> >> +
> >> +       return ret;
> >> +}
> >> +
> >> +static uint32_t smu_v11_0_i2c_poll_rx_status(struct i2c_adapter *control)
> >> +{
> >> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >> +       uint32_t ret = I2C_OK;
> >> +       uint32_t reg_ic_status, reg_c_tx_abrt_source;
> >> +
> >> +       reg_c_tx_abrt_source = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TX_ABRT_SOURCE);
> >> +
> >> +       /* If slave is not present */
> >> +       if (REG_GET_FIELD(reg_c_tx_abrt_source,
> >> +                         CKSVII2C_IC_TX_ABRT_SOURCE,
> >> +                         ABRT_7B_ADDR_NOACK) == 1) {
> >> +               ret |= I2C_NAK_7B_ADDR_NOACK;
> >> +
> >> +               smu_v11_0_i2c_clear_status(control);
> >> +       } else {  /* wait till some data is there in RXFIFO */
> >> +               /* Poll for some byte in RXFIFO */
> >> +               unsigned long  timeout_counter = jiffies + msecs_to_jiffies(20);
> >> +
> >> +               do {
> >> +                       reg_ic_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
> >> +
> >> +                       if (time_after(jiffies, timeout_counter)) {
> >> +                               ret |= I2C_SW_TIMEOUT;
> >> +                               break;
> >> +                       }
> >> +               } while (REG_GET_FIELD(reg_ic_status, CKSVII2C_IC_STATUS, RFNE) == 0);
> >> +       }
> >> +
> >> +       return ret;
> >> +}
> >> +
> >> +
> >> +
> >> +
> >> +/**
> >> + * smu_v11_0_i2c_transmit - Send a block of data over the I2C bus to a slave device.
> >> + *
> >> + * @address: The I2C address of the slave device.
> >> + * @data: The data to transmit over the bus.
> >> + * @numbytes: The amount of data to transmit.
> >> + * @i2c_flag: Flags for transmission
> >> + *
> >> + * Returns 0 on success or error.
> >> + */
> >> +static uint32_t smu_v11_0_i2c_transmit(struct i2c_adapter *control,
> >> +                                 uint8_t address, uint8_t *data,
> >> +                                 uint32_t numbytes, uint32_t i2c_flag)
> >> +{
> >> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >> +       uint32_t bytes_sent, reg, ret = 0;
> >> +       unsigned long  timeout_counter;
> >> +
> >> +       bytes_sent = 0;
> >> +
> >> +       DRM_DEBUG_DRIVER("I2C_Transmit(), address = %x, bytes = %d , data: ",
> >> +                (uint16_t)address, numbytes);
> >> +
> >> +       if (drm_debug & DRM_UT_DRIVER) {
> >> +               print_hex_dump(KERN_INFO, "data: ", DUMP_PREFIX_NONE,
> >> +                              16, 1, data, numbytes, false);
> >> +       }
> >> +
> >> +       /* Set the I2C slave address */
> >> +       smu_v11_0_i2c_set_address(control, address);
> >> +       /* Enable I2C */
> >> +       smu_v11_0_i2c_enable(control, true);
> >> +
> >> +       /* Clear status bits */
> >> +       smu_v11_0_i2c_clear_status(control);
> >> +
> >> +
> >> +       timeout_counter = jiffies + msecs_to_jiffies(20);
> >> +
> >> +       while (numbytes > 0) {
> >> +               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
> >> +               if (REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFNF)) {
> >> +                       do {
> >> +                               reg = 0;
> >> +                               /*
> >> +                                * Prepare transaction, no need to set RESTART. I2C engine will send
> >> +                                * START as soon as it sees data in TXFIFO
> >> +                                */
> >> +                               if (bytes_sent == 0)
> >> +                                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, RESTART,
> >> +                                                           (i2c_flag & I2C_RESTART) ? 1 : 0);
> >> +                               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, data[bytes_sent]);
> >> +
> >> +                               /* determine if we need to send STOP bit or not */
> >> +                               if (numbytes == 1)
> >> +                                       /* Final transaction, so send stop unless I2C_NO_STOP */
> >> +                                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, STOP,
> >> +                                                           (i2c_flag & I2C_NO_STOP) ? 0 : 1);
> >> +                               /* Write */
> >> +                               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 0);
> >> +                               WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg);
> >> +
> >> +                               /* Record that the bytes were transmitted */
> >> +                               bytes_sent++;
> >> +                               numbytes--;
> >> +
> >> +                               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
> >> +
> >> +                       } while (numbytes &&  REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFNF));
> >> +               }
> >> +
> >> +
> >> +               if (time_after(jiffies, timeout_counter)) {
> >> +                       ret |= I2C_SW_TIMEOUT;
> >> +                       goto err;
> >> +               }
> >> +       }
> >> +
> >> +       ret = smu_v11_0_i2c_poll_tx_status(control);
> >> +
> >> +err:
> >> +       /* Any error, no point in proceeding */
> >> +       if (ret != I2C_OK) {
> >> +               if (ret & I2C_SW_TIMEOUT)
> >> +                       DRM_ERROR("TIMEOUT ERROR !!!");
> >> +
> >> +               if (ret & I2C_NAK_7B_ADDR_NOACK)
> >> +                       DRM_ERROR("Received I2C_NAK_7B_ADDR_NOACK !!!");
> >> +
> >> +
> >> +               if (ret & I2C_NAK_TXDATA_NOACK)
> >> +                       DRM_ERROR("Received I2C_NAK_TXDATA_NOACK !!!");
> >> +       }
> >> +
> >> +       return ret;
> >> +}
> >> +
> >> +
> >> +/**
> >> + * smu_v11_0_i2c_receive - Receive a block of data over the I2C bus from a slave device.
> >> + *
> >> + * @address: The I2C address of the slave device.
> >> + * @numbytes: The amount of data to transmit.
> >> + * @i2c_flag: Flags for transmission
> >> + *
> >> + * Returns 0 on success or error.
> >> + */
> >> +static uint32_t smu_v11_0_i2c_receive(struct i2c_adapter *control,
> >> +                                uint8_t address, uint8_t *data,
> >> +                                uint32_t numbytes, uint8_t i2c_flag)
> >> +{
> >> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >> +       uint32_t bytes_received, ret = I2C_OK;
> >> +
> >> +       bytes_received = 0;
> >> +
> >> +       /* Set the I2C slave address */
> >> +       smu_v11_0_i2c_set_address(control, address);
> >> +
> >> +       /* Enable I2C */
> >> +       smu_v11_0_i2c_enable(control, true);
> >> +
> >> +       while (numbytes > 0) {
> >> +               uint32_t reg = 0;
> >> +
> >> +               smu_v11_0_i2c_clear_status(control);
> >> +
> >> +
> >> +               /* Prepare transaction */
> >> +
> >> +               /* Each time we disable I2C, so this is not a restart */
> >> +               if (bytes_received == 0)
> >> +                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, RESTART,
> >> +                                           (i2c_flag & I2C_RESTART) ? 1 : 0);
> >> +
> >> +               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, 0);
> >> +               /* Read */
> >> +               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 1);
> >> +
> >> +               /* Transmitting last byte */
> >> +               if (numbytes == 1)
> >> +                       /* Final transaction, so send stop if requested */
> >> +                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, STOP,
> >> +                                           (i2c_flag & I2C_NO_STOP) ? 0 : 1);
> >> +
> >> +               WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg);
> >> +
> >> +               ret = smu_v11_0_i2c_poll_rx_status(control);
> >> +
> >> +               /* Any error, no point in proceeding */
> >> +               if (ret != I2C_OK) {
> >> +                       if (ret & I2C_SW_TIMEOUT)
> >> +                               DRM_ERROR("TIMEOUT ERROR !!!");
> >> +
> >> +                       if (ret & I2C_NAK_7B_ADDR_NOACK)
> >> +                               DRM_ERROR("Received I2C_NAK_7B_ADDR_NOACK !!!");
> >> +
> >> +                       if (ret & I2C_NAK_TXDATA_NOACK)
> >> +                               DRM_ERROR("Received I2C_NAK_TXDATA_NOACK !!!");
> >> +
> >> +                       break;
> >> +               }
> >> +
> >> +               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD);
> >> +               data[bytes_received] = REG_GET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT);
> >> +
> >> +               /* Record that the bytes were received */
> >> +               bytes_received++;
> >> +               numbytes--;
> >> +       }
> >> +
> >> +       DRM_DEBUG_DRIVER("I2C_Receive(), address = %x, bytes = %d, data :",
> >> +                 (uint16_t)address, bytes_received);
> >> +
> >> +       if (drm_debug & DRM_UT_DRIVER) {
> >> +               print_hex_dump(KERN_INFO, "data: ", DUMP_PREFIX_NONE,
> >> +                              16, 1, data, bytes_received, false);
> >> +       }
> >> +
> >> +       return ret;
> >> +}
> >> +
> >> +static void smu_v11_0_i2c_abort(struct i2c_adapter *control)
> >> +{
> >> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >> +       uint32_t reg = 0;
> >> +
> >> +       /* Enable I2C engine; */
> >> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_ENABLE, ENABLE, 1);
> >> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, reg);
> >> +
> >> +       /* Abort previous transaction */
> >> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_ENABLE, ABORT, 1);
> >> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, reg);
> >> +
> >> +       DRM_DEBUG_DRIVER("I2C_Abort() Done.");
> >> +}
> >> +
> >> +
> >> +static bool smu_v11_0_i2c_activity_done(struct i2c_adapter *control)
> >> +{
> >> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >> +
> >> +       const uint32_t IDLE_TIMEOUT = 1024;
> >> +       uint32_t timeoutCount = 0;
> > kernel coding style prefers non CamelCase.  E.g., timeout_count.
> >
> >> +       uint32_t reg_ic_enable, reg_ic_enable_status, reg_ic_clr_activity;
> >> +
> >> +       reg_ic_enable_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE_STATUS);
> >> +       reg_ic_enable = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE);
> >> +
> >> +
> >> +       if ((REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) &&
> >> +           (REG_GET_FIELD(reg_ic_enable_status, CKSVII2C_IC_ENABLE_STATUS, IC_EN) == 1)) {
> >> +               /*
> >> +                * Nobody is using I2C engine, but engine remains active because
> >> +                * someone missed to send STOP
> >> +                */
> >> +               smu_v11_0_i2c_abort(control);
> >> +       } else if (REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) {
> >> +               /* Nobody is using I2C engine */
> >> +               return true;
> >> +       }
> >> +
> >> +       /* Keep reading activity bit until it's cleared */
> >> +       do {
> >> +               reg_ic_clr_activity = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CLR_ACTIVITY);
> >> +
> >> +               if (REG_GET_FIELD(reg_ic_clr_activity,
> >> +                   CKSVII2C_IC_CLR_ACTIVITY, CLR_ACTIVITY) == 0)
> >> +                       return true;
> >> +
> >> +               ++timeoutCount;
> >> +
> >> +       } while (timeoutCount < IDLE_TIMEOUT);
> >> +
> >> +       return false;
> >> +}
> >> +
> >> +static void smu_v11_0_i2c_init(struct i2c_adapter *control)
> >> +{
> >> +       /* Disable clock gating */
> >> +       smu_v11_0_i2c_dis_clock_gating(control);
> >> +
> >> +       if (!smu_v11_0_i2c_activity_done(control))
> >> +               DRM_WARN("I2C busy !");
> >> +
> >> +       /* Disable I2C */
> >> +       smu_v11_0_i2c_enable(control, false);
> >> +
> >> +       /* Configure I2C to operate as master and in standard mode */
> >> +       smu_v11_0_i2c_configure(control);
> >> +
> >> +       /* Initialize the clock to 50 kHz default */
> >> +       smu_v11_0_i2c_set_clock(control);
> >> +
> >> +}
> >> +
> >> +static void smu_v11_0_i2c_fini(struct i2c_adapter *control)
> >> +{
> >> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >> +       uint32_t reg_ic_enable_status, reg_ic_enable;
> >> +
> >> +       smu_v11_0_i2c_enable(control, false);
> >> +
> >> +       /* Double check if disabled, else force abort */
> >> +       reg_ic_enable_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE_STATUS);
> >> +       reg_ic_enable = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE);
> >> +
> >> +       if ((REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) &&
> >> +           (REG_GET_FIELD(reg_ic_enable_status,
> >> +                          CKSVII2C_IC_ENABLE_STATUS, IC_EN) == 1)) {
> >> +               /*
> >> +                * Nobody is using I2C engine, but engine remains active because
> >> +                * someone missed to send STOP
> >> +                */
> >> +               smu_v11_0_i2c_abort(control);
> >> +       }
> >> +
> >> +}
> >> +
> >> +static bool smu_v11_0_i2c_bus_lock(struct i2c_adapter *control)
> >> +{
> >> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >> +
> >> +       /* Send  PPSMC_MSG_RequestI2CBus */
> >> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access)
> >> +               goto fail;
> >> +
> >> +
> >> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access(adev->powerplay.pp_handle, true))
> >> +               return true;
> >> +
> >> +fail:
> >> +       return false;
> >> +}
> >> +
> >> +static bool smu_v11_0_i2c_bus_unlock(struct i2c_adapter *control)
> >> +{
> >> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >> +
> >> +       /* Send  PPSMC_MSG_RequestI2CBus */
> >> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access)
> >> +               goto fail;
> >> +
> >> +       /* Send  PPSMC_MSG_ReleaseI2CBus */
> >> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access(adev->powerplay.pp_handle,
> >> +                                                            false))
> >> +               return true;
> >> +
> >> +fail:
> >> +       return false;
> >> +}
> >> +
> >> +/***************************** EEPROM I2C GLUE ****************************/
> >> +
> >> +static uint32_t smu_v11_0_i2c_eeprom_read_data(struct i2c_adapter *control,
> >> +                                              uint8_t address,
> >> +                                              uint8_t *data,
> >> +                                              uint32_t numbytes)
> >> +{
> >> +       uint32_t  ret = 0;
> >> +
> >> +       /* First 2 bytes are dummy write to set EEPROM address */
> >> +       ret = smu_v11_0_i2c_transmit(control, address, data, 2, I2C_NO_STOP);
> >> +       if (ret != I2C_OK)
> >> +               goto fail;
> >> +
> >> +       /* Now read data starting with that address */
> >> +       ret = smu_v11_0_i2c_receive(control, address, data + 2, numbytes - 2,
> >> +                                   I2C_RESTART);
> >> +
> >> +fail:
> >> +       if (ret != I2C_OK)
> >> +               DRM_ERROR("ReadData() - I2C error occurred :%x", ret);
> >> +
> >> +       return ret;
> >> +}
> >> +
> >> +static uint32_t smu_v11_0_i2c_eeprom_write_data(struct i2c_adapter *control,
> >> +                                               uint8_t address,
> >> +                                               uint8_t *data,
> >> +                                               uint32_t numbytes)
> >> +{
> >> +       uint32_t  ret;
> >> +
> >> +       ret = smu_v11_0_i2c_transmit(control, address, data, numbytes, 0);
> >> +
> >> +       if (ret != I2C_OK)
> >> +               DRM_ERROR("WriteI2CData() - I2C error occurred :%x", ret);
> >> +       else
> >> +               /*
> >> +                * According to EEPROM spec there is a MAX of 10 ms required for
> >> +                * EEPROM to flush internal RX buffer after STOP was issued at the
> >> +                * end of write transaction. During this time the EEPROM will not be
> >> +                * responsive to any more commands - so wait a bit more.
> >> +                *
> >> +                * TODO Improve to wait for first ACK for slave address after
> >> +                * internal write cycle done.
> >> +                */
> >> +               msleep(10);
> >> +
> >> +       return ret;
> >> +
> >> +}
> >> +
> >> +static void lock_bus(struct i2c_adapter *i2c, unsigned int flags)
> >> +{
> >> +       struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c);
> >> +
> >> +       if (!smu_v11_0_i2c_bus_lock(i2c)) {
> >> +               DRM_ERROR("Failed to lock the bus from SMU");
> >> +               return;
> >> +       }
> >> +
> >> +       control->bus_locked = true;
> >> +}
> >> +
> >> +static int trylock_bus(struct i2c_adapter *i2c, unsigned int flags)
> >> +{
> >> +       WARN_ONCE(1, "This operation not supposed to run in atomic context!");
> >> +       return false;
> >> +}
> >> +
> >> +static void unlock_bus(struct i2c_adapter *i2c, unsigned int flags)
> >> +{
> >> +       struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c);
> >> +
> >> +       if (!smu_v11_0_i2c_bus_unlock(i2c)) {
> >> +               DRM_ERROR("Failed to unlock the bus from SMU");
> >> +               return;
> >> +       }
> >> +
> >> +       control->bus_locked = false;
> >> +}
> >> +
> >> +static const struct i2c_lock_operations smu_v11_0_i2c_i2c_lock_ops = {
> >> +       .lock_bus = lock_bus,
> >> +       .trylock_bus = trylock_bus,
> >> +       .unlock_bus = unlock_bus,
> >> +};
> >> +
> >> +static int smu_v11_0_i2c_eeprom_i2c_xfer(struct i2c_adapter *i2c_adap,
> >> +                             struct i2c_msg *msgs, int num)
> >> +{
> >> +       int i, ret;
> >> +       struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c_adap);
> >> +
> >> +       if (!control->bus_locked) {
> >> +               DRM_ERROR("I2C bus unlocked, stopping transaction!");
> >> +               return -EIO;
> >> +       }
> >> +
> >> +       smu_v11_0_i2c_init(i2c_adap);
> >> +
> >> +       for (i = 0; i < num; i++) {
> >> +               if (msgs[i].flags & I2C_M_RD)
> >> +                       ret = smu_v11_0_i2c_eeprom_read_data(i2c_adap,
> >> +                                                       (uint8_t)msgs[i].addr,
> >> +                                                       msgs[i].buf, msgs[i].len);
> >> +               else
> >> +                       ret = smu_v11_0_i2c_eeprom_write_data(i2c_adap,
> >> +                                                        (uint8_t)msgs[i].addr,
> >> +                                                        msgs[i].buf, msgs[i].len);
> >> +
> >> +               if (ret != I2C_OK) {
> >> +                       num = -EIO;
> >> +                       break;
> >> +               }
> >> +       }
> >> +
> >> +       smu_v11_0_i2c_fini(i2c_adap);
> >> +       return num;
> >> +}
> >> +
> >> +static u32 smu_v11_0_i2c_eeprom_i2c_func(struct i2c_adapter *adap)
> >> +{
> >> +       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> >> +}
> >> +
> >> +
> >> +static const struct i2c_algorithm smu_v11_0_i2c_eeprom_i2c_algo = {
> >> +       .master_xfer = smu_v11_0_i2c_eeprom_i2c_xfer,
> >> +       .functionality = smu_v11_0_i2c_eeprom_i2c_func,
> >> +};
> >> +
> >> +int smu_v11_0_i2c_eeprom_control_init(struct i2c_adapter *control)
> >> +{
> >> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >> +       int res;
> >> +
> >> +       control->owner = THIS_MODULE;
> >> +       control->class = I2C_CLASS_SPD;
> >> +       control->dev.parent = &adev->pdev->dev;
> >> +       control->algo = &smu_v11_0_i2c_eeprom_i2c_algo;
> >> +       snprintf(control->name, sizeof(control->name), "RAS EEPROM");
> >> +       control->lock_ops = &smu_v11_0_i2c_i2c_lock_ops;
> >> +
> >> +       res = i2c_add_adapter(control);
> >> +       if (res)
> >> +               DRM_ERROR("Failed to register hw i2c, err: %d\n", res);
> >> +
> >> +       return res;
> >> +}
> >> +
> >> +void smu_v11_0_i2c_eeprom_control_fini(struct i2c_adapter *control)
> >> +{
> >> +       i2c_del_adapter(control);
> >> +}
> >> +
> >> +#define I2C_TARGET_ADDR 0xA0
> >> +
> >> +bool smu_v11_0_i2c_test_bus(struct i2c_adapter *control)
> >> +{
> >> +
> >> +       uint32_t ret = I2C_OK;
> >> +       uint8_t data[6] = {0xf, 0, 0xde, 0xad, 0xbe, 0xef};
> >> +
> >> +
> >> +       DRM_INFO("Begin");
> >> +
> >> +       if (!smu_v11_0_i2c_bus_lock(control)) {
> >> +               DRM_ERROR("Failed to lock the bus!.");
> >> +               return false;
> >> +       }
> >> +
> >> +       smu_v11_0_i2c_init(control);
> >> +
> >> +       /* Write 0xde to address 0x0000 on the EEPROM */
> >> +       ret = smu_v11_0_i2c_eeprom_write_data(control, I2C_TARGET_ADDR, data, 6);
> >> +
> >> +       ret = smu_v11_0_i2c_eeprom_read_data(control, I2C_TARGET_ADDR, data, 6);
> >> +
> >> +       smu_v11_0_i2c_fini(control);
> >> +
> >> +       smu_v11_0_i2c_bus_unlock(control);
> >> +
> >> +
> >> +       DRM_INFO("End");
> >> +       return true;
> >> +}
> > Leftover debugging function?
> >
> >> diff --git a/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
> >> new file mode 100644
> >> index 0000000..973f28d
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
> >> @@ -0,0 +1,34 @@
> >> +/*
> >> + * 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.
> >> + *
> >> + */
> >> +
> >> +#ifndef SMU_V11_I2C_CONTROL_H
> >> +#define SMU_V11_I2C_CONTROL_H
> >> +
> >> +#include <linux/types.h>
> >> +
> >> +struct i2c_adapter;
> >> +
> >> +int smu_v11_0_i2c_eeprom_control_init(struct i2c_adapter *control);
> >> +void smu_v11_0_i2c_eeprom_control_fini(struct i2c_adapter *control);
> >> +
> >> +#endif
> >> --
> >> 2.7.4
> >>
> >> _______________________________________________
> >> amd-gfx mailing list
> >> amd-gfx@lists.freedesktop.org
> >> https://lists.freedesktop.org/mailman/listinfo/amd-gfx
_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 4/4] drm/amdgpu: Vega20 SMU I2C HW engine controller.
       [not found]                 ` <CADnq5_NSOATAdC_z9R5BoVbM=eSLQbLYVvdPDOqZYa2Gyz_V6A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2019-08-22 16:45                   ` Grodzovsky, Andrey
       [not found]                     ` <a29f8c1c-3b0f-0412-ab66-441fae96ed8d-5C7GfCeVMHo@public.gmane.org>
  0 siblings, 1 reply; 18+ messages in thread
From: Grodzovsky, Andrey @ 2019-08-22 16:45 UTC (permalink / raw)
  To: Alex Deucher
  Cc: Panariti, David, Pan, Xinhui, Lazar, Lijo, amd-gfx list, Deucher,
	Alexander, Tuikov, Luben, Russell, Kent, Zhou1, Tao, Quan, Evan,
	Zhang, Hawking


On 8/22/19 11:59 AM, Alex Deucher wrote:
> On Thu, Aug 22, 2019 at 11:35 AM Grodzovsky, Andrey
> <Andrey.Grodzovsky@amd.com> wrote:
>>
>> On 8/21/19 10:32 PM, Alex Deucher wrote:
>>> On Wed, Aug 21, 2019 at 4:02 PM Andrey Grodzovsky
>>> <andrey.grodzovsky@amd.com> wrote:
>>>> Implement HW I2C enigne controller to be used by the RAS EEPROM
>>>> table manager. This is based on code from ATITOOLs.
>>>>
>>>> v2:
>>>> Rename the file and all function prefixes to smu_v11_0_i2c
>>>>
>>>> By Luben's observation always fill the TX fifo to full so
>>>> we don't have garbadge interpreted by the slave as valid data.
>>>>
>>>> v3:
>>>> Remove preemption disable as the HW I2C controller will not
>>>> stop the clock on empty TX fifo and so it's not critical to
>>>> keep not empty queue.
>>>> Switch to fast mode 400 khz SCL clock for faster read and write.
>>>>
>>>> Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@amd.com>
>>>> ---
>>>>    drivers/gpu/drm/amd/amdgpu/Makefile            |   5 +-
>>>>    drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c |   5 +-
>>>>    drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c     | 710 +++++++++++++++++++++++++
>>>>    drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h     |  34 ++
>>>>    4 files changed, 751 insertions(+), 3 deletions(-)
>>>>    create mode 100644 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
>>>>    create mode 100644 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
>>>>
>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
>>>> index f016cf1..14733ff 100644
>>>> --- a/drivers/gpu/drm/amd/amdgpu/Makefile
>>>> +++ b/drivers/gpu/drm/amd/amdgpu/Makefile
>>>> @@ -38,6 +38,9 @@ ccflags-y := -I$(FULL_AMD_PATH)/include/asic_reg \
>>>>           -I$(FULL_AMD_DISPLAY_PATH)/amdgpu_dm \
>>>>           -I$(FULL_AMD_PATH)/amdkfd
>>>>
>>>> +
>>>> +
>>>> +
>>> Drop this random whitespace change.
>>>
>>>>    amdgpu-y := amdgpu_drv.o
>>>>
>>>>    # add KMS driver
>>>> @@ -54,7 +57,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
>>>>           amdgpu_gtt_mgr.o amdgpu_vram_mgr.o amdgpu_virt.o amdgpu_atomfirmware.o \
>>>>           amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o amdgpu_ids.o \
>>>>           amdgpu_gmc.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o amdgpu_vm_cpu.o \
>>>> -       amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o amdgpu_ras_eeprom.o
>>>> +       amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o amdgpu_ras_eeprom.o smu_v11_0_i2c.o
>>>>
>>>>    amdgpu-$(CONFIG_PERF_EVENTS) += amdgpu_pmu.o
>>>>
>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
>>>> index bf07515..e6b2e17 100644
>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
>>>> @@ -25,6 +25,7 @@
>>>>    #include "amdgpu.h"
>>>>    #include "amdgpu_ras.h"
>>>>    #include <linux/bits.h>
>>>> +#include "smu_v11_0_i2c.h"
>>>>
>>>>    #define EEPROM_I2C_TARGET_ADDR 0xA0
>>>>
>>>> @@ -111,7 +112,7 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
>>>>
>>>>           switch (adev->asic_type) {
>>>>           case CHIP_VEGA20:
>>>> -       /*TODO Add MI-60 */
>>>> +               ret = smu_v11_0_i2c_eeprom_control_init(&control->eeprom_accessor);
>>>>                   break;
>>>>
>>>>           default:
>>>> @@ -163,7 +164,7 @@ void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control *control)
>>>>
>>>>           switch (adev->asic_type) {
>>>>           case CHIP_VEGA20:
>>>> -               /*TODO Add MI-60 */
>>>> +               smu_v11_0_i2c_eeprom_control_fini(&control->eeprom_accessor);
>>>>                   break;
>>>>
>>>>           default:
>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
>>>> new file mode 100644
>>>> index 0000000..24405fa
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
>>>> @@ -0,0 +1,710 @@
>>>> +/*
>>>> + * 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 "smuio/smuio_11_0_0_offset.h"
>>>> +#include "smuio/smuio_11_0_0_sh_mask.h"
>>>> +
>>>> +#include "smu_v11_0_i2c.h"
>>>> +#include "amdgpu.h"
>>>> +#include "soc15_common.h"
>>>> +#include <drm/drm_fixed.h>
>>>> +#include "amdgpu_amdkfd.h"
>>>> +#include <linux/i2c.h>
>>>> +#include "amdgpu_ras.h"
>>>> +
>>>> +/* error codes */
>>>> +#define I2C_OK                         0
>>>> +#define I2C_NAK_7B_ADDR_NOACK          1
>>>> +#define I2C_NAK_TXDATA_NOACK           2
>>>> +#define I2C_TIMEOUT                    4
>>>> +#define I2C_SW_TIMEOUT                 8
>>>> +#define I2C_ABORT                      0x10
>>>> +
>>>> +/* I2C transaction flags */
>>>> +#define I2C_NO_STOP    1
>>>> +#define I2C_RESTART    2
>>>> +
>>>> +#define to_amdgpu_device(x) (container_of(x, struct amdgpu_ras, eeprom_control.eeprom_accessor))->adev
>>>> +#define to_eeprom_control(x) container_of(x, struct amdgpu_ras_eeprom_control, eeprom_accessor)
>>>> +
>>>> +static void smu_v11_0_i2c_dis_clock_gating(struct i2c_adapter *control)
>>>> +{
>>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>>>> +       uint32_t reg;
>>>> +
>>>> +       reg = RREG32_SOC15(SMUIO, 0, mmSMUIO_PWRMGT);
>>>> +       reg = REG_SET_FIELD(reg, SMUIO_PWRMGT, i2c_clk_gate_en, 0);
>>>> +       WREG32_SOC15(SMUIO, 0, mmSMUIO_PWRMGT, reg);
>>>> +}
>>> Do we need to worry about re-enabling clock gating when we are done?
>>
>> In smu_11_smuio_mas doc this step is missing for I2C0 (sec. 18.4.1.1)
>> and hence probably also was missing from the original code this was
>> ported so I am not sure. The side effect of this staying disabled is
>> less  power saving I think.
> Can we check the state of SMUIO_PWRMGT.i2c_clk_gate_en at init time,
> and then, if it's enabled, we can disable it in lock_bus and enable it
> in unlock_bus.
>
> Alex


smu_v11_0_i2c_init is called after the lock_bus. I think we should just 
do what you suggest in  smu_v11_0_i2c_init and smu_v11_0_i2c_fini 
respectively.


>
>> Andrey
>>
>>
>>>
>>>> +
>>>> +
>>>> +static void smu_v11_0_i2c_enable(struct i2c_adapter *control, bool enable)
>>>> +{
>>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>>>> +
>>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, enable ? 1 : 0);
>>>> +}
>>>> +
>>>> +static void smu_v11_0_i2c_clear_status(struct i2c_adapter *control)
>>>> +{
>>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>>>> +       /* do */
>>>> +       {
>>>> +               RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CLR_INTR);
>>>> +
>>>> +       } /* while (reg_CKSVII2C_ic_clr_intr == 0) */
>>>> +}
>>>> +
>>>> +static void smu_v11_0_i2c_configure(struct i2c_adapter *control)
>>>> +{
>>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>>>> +       uint32_t reg = 0;
>>>> +
>>>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_SLAVE_DISABLE, 1);
>>>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_RESTART_EN, 1);
>>>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_10BITADDR_MASTER, 0);
>>>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_10BITADDR_SLAVE, 0);
>>>> +       /* Standard mode */
>>>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_MAX_SPEED_MODE, 2);
>>>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_MASTER_MODE, 1);
>>>> +
>>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CON, reg);
>>>> +}
>>>> +
>>>> +static void smu_v11_0_i2c_set_clock(struct i2c_adapter *control)
>>>> +{
>>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>>>> +
>>>> +       /*
>>>> +        * Standard mode speed, These values are taken from SMUIO MAS,
>>>> +        * but are different from what is given is
>>>> +        * Synopsys spec. The values here are based on assumption
>>>> +        * that refclock is 100MHz
>>>> +        *
>>>> +        * Configuration for standard mode; Speed = 100kbps
>>>> +        * Scale linearly, for now only support standard speed clock
>>>> +        * This will work only with 100M ref clock
>>>> +        *
>>>> +        * TBD:Change the calculation to take into account ref clock values also.
>>>> +        */
>>>> +
>>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_FS_SPKLEN, 2);
>>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SS_SCL_HCNT, 120);
>>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SS_SCL_LCNT, 130);
>>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SDA_HOLD, 20);
>>>> +}
>>>> +
>>>> +static void smu_v11_0_i2c_set_address(struct i2c_adapter *control, uint8_t address)
>>>> +{
>>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>>>> +
>>>> +       /* Convert fromr 8-bit to 7-bit address */
>>>> +       address >>= 1;
>>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TAR, (address & 0xFF));
>>>> +}
>>>> +
>>>> +static uint32_t smu_v11_0_i2c_poll_tx_status(struct i2c_adapter *control)
>>>> +{
>>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>>>> +       uint32_t ret = I2C_OK;
>>>> +       uint32_t reg, reg_c_tx_abrt_source;
>>>> +
>>>> +       /*Check if transmission is completed */
>>>> +       unsigned long  timeout_counter = jiffies + msecs_to_jiffies(20);
>>>> +
>>>> +       do {
>>>> +               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
>>>> +
>>>> +               if (time_after(jiffies, timeout_counter)) {
>>>> +                       ret |= I2C_SW_TIMEOUT;
>>>> +                       break;
>>>> +               }
>>>> +       } while (REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFE) == 0);
>>>> +
>>>> +       if (ret != I2C_OK)
>>>> +               return ret;
>>>> +
>>>> +       /* This only checks if NAK is received and transaction got aborted */
>>>> +       reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_INTR_STAT);
>>>> +
>>>> +       if (REG_GET_FIELD(reg, CKSVII2C_IC_INTR_STAT, R_TX_ABRT) == 1) {
>>>> +               reg_c_tx_abrt_source = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TX_ABRT_SOURCE);
>>>> +               DRM_INFO("TX was terminated, IC_TX_ABRT_SOURCE val is:%x", reg_c_tx_abrt_source);
>>>> +
>>>> +               /* Check for stop due to NACK */
>>>> +               if (REG_GET_FIELD(reg_c_tx_abrt_source,
>>>> +                                 CKSVII2C_IC_TX_ABRT_SOURCE,
>>>> +                                 ABRT_TXDATA_NOACK) == 1) {
>>>> +
>>>> +                       ret |= I2C_NAK_TXDATA_NOACK;
>>>> +
>>>> +               } else if (REG_GET_FIELD(reg_c_tx_abrt_source,
>>>> +                                        CKSVII2C_IC_TX_ABRT_SOURCE,
>>>> +                                        ABRT_7B_ADDR_NOACK) == 1) {
>>>> +
>>>> +                       ret |= I2C_NAK_7B_ADDR_NOACK;
>>>> +               } else {
>>>> +                       ret |= I2C_ABORT;
>>>> +               }
>>>> +
>>>> +               smu_v11_0_i2c_clear_status(control);
>>>> +       }
>>>> +
>>>> +       return ret;
>>>> +}
>>>> +
>>>> +static uint32_t smu_v11_0_i2c_poll_rx_status(struct i2c_adapter *control)
>>>> +{
>>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>>>> +       uint32_t ret = I2C_OK;
>>>> +       uint32_t reg_ic_status, reg_c_tx_abrt_source;
>>>> +
>>>> +       reg_c_tx_abrt_source = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TX_ABRT_SOURCE);
>>>> +
>>>> +       /* If slave is not present */
>>>> +       if (REG_GET_FIELD(reg_c_tx_abrt_source,
>>>> +                         CKSVII2C_IC_TX_ABRT_SOURCE,
>>>> +                         ABRT_7B_ADDR_NOACK) == 1) {
>>>> +               ret |= I2C_NAK_7B_ADDR_NOACK;
>>>> +
>>>> +               smu_v11_0_i2c_clear_status(control);
>>>> +       } else {  /* wait till some data is there in RXFIFO */
>>>> +               /* Poll for some byte in RXFIFO */
>>>> +               unsigned long  timeout_counter = jiffies + msecs_to_jiffies(20);
>>>> +
>>>> +               do {
>>>> +                       reg_ic_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
>>>> +
>>>> +                       if (time_after(jiffies, timeout_counter)) {
>>>> +                               ret |= I2C_SW_TIMEOUT;
>>>> +                               break;
>>>> +                       }
>>>> +               } while (REG_GET_FIELD(reg_ic_status, CKSVII2C_IC_STATUS, RFNE) == 0);
>>>> +       }
>>>> +
>>>> +       return ret;
>>>> +}
>>>> +
>>>> +
>>>> +
>>>> +
>>>> +/**
>>>> + * smu_v11_0_i2c_transmit - Send a block of data over the I2C bus to a slave device.
>>>> + *
>>>> + * @address: The I2C address of the slave device.
>>>> + * @data: The data to transmit over the bus.
>>>> + * @numbytes: The amount of data to transmit.
>>>> + * @i2c_flag: Flags for transmission
>>>> + *
>>>> + * Returns 0 on success or error.
>>>> + */
>>>> +static uint32_t smu_v11_0_i2c_transmit(struct i2c_adapter *control,
>>>> +                                 uint8_t address, uint8_t *data,
>>>> +                                 uint32_t numbytes, uint32_t i2c_flag)
>>>> +{
>>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>>>> +       uint32_t bytes_sent, reg, ret = 0;
>>>> +       unsigned long  timeout_counter;
>>>> +
>>>> +       bytes_sent = 0;
>>>> +
>>>> +       DRM_DEBUG_DRIVER("I2C_Transmit(), address = %x, bytes = %d , data: ",
>>>> +                (uint16_t)address, numbytes);
>>>> +
>>>> +       if (drm_debug & DRM_UT_DRIVER) {
>>>> +               print_hex_dump(KERN_INFO, "data: ", DUMP_PREFIX_NONE,
>>>> +                              16, 1, data, numbytes, false);
>>>> +       }
>>>> +
>>>> +       /* Set the I2C slave address */
>>>> +       smu_v11_0_i2c_set_address(control, address);
>>>> +       /* Enable I2C */
>>>> +       smu_v11_0_i2c_enable(control, true);
>>>> +
>>>> +       /* Clear status bits */
>>>> +       smu_v11_0_i2c_clear_status(control);
>>>> +
>>>> +
>>>> +       timeout_counter = jiffies + msecs_to_jiffies(20);
>>>> +
>>>> +       while (numbytes > 0) {
>>>> +               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
>>>> +               if (REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFNF)) {
>>>> +                       do {
>>>> +                               reg = 0;
>>>> +                               /*
>>>> +                                * Prepare transaction, no need to set RESTART. I2C engine will send
>>>> +                                * START as soon as it sees data in TXFIFO
>>>> +                                */
>>>> +                               if (bytes_sent == 0)
>>>> +                                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, RESTART,
>>>> +                                                           (i2c_flag & I2C_RESTART) ? 1 : 0);
>>>> +                               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, data[bytes_sent]);
>>>> +
>>>> +                               /* determine if we need to send STOP bit or not */
>>>> +                               if (numbytes == 1)
>>>> +                                       /* Final transaction, so send stop unless I2C_NO_STOP */
>>>> +                                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, STOP,
>>>> +                                                           (i2c_flag & I2C_NO_STOP) ? 0 : 1);
>>>> +                               /* Write */
>>>> +                               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 0);
>>>> +                               WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg);
>>>> +
>>>> +                               /* Record that the bytes were transmitted */
>>>> +                               bytes_sent++;
>>>> +                               numbytes--;
>>>> +
>>>> +                               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
>>>> +
>>>> +                       } while (numbytes &&  REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFNF));
>>>> +               }
>>>> +
>>>> +
>>>> +               if (time_after(jiffies, timeout_counter)) {
>>>> +                       ret |= I2C_SW_TIMEOUT;
>>>> +                       goto err;
>>>> +               }
>>>> +       }
>>>> +
>>>> +       ret = smu_v11_0_i2c_poll_tx_status(control);
>>>> +
>>>> +err:
>>>> +       /* Any error, no point in proceeding */
>>>> +       if (ret != I2C_OK) {
>>>> +               if (ret & I2C_SW_TIMEOUT)
>>>> +                       DRM_ERROR("TIMEOUT ERROR !!!");
>>>> +
>>>> +               if (ret & I2C_NAK_7B_ADDR_NOACK)
>>>> +                       DRM_ERROR("Received I2C_NAK_7B_ADDR_NOACK !!!");
>>>> +
>>>> +
>>>> +               if (ret & I2C_NAK_TXDATA_NOACK)
>>>> +                       DRM_ERROR("Received I2C_NAK_TXDATA_NOACK !!!");
>>>> +       }
>>>> +
>>>> +       return ret;
>>>> +}
>>>> +
>>>> +
>>>> +/**
>>>> + * smu_v11_0_i2c_receive - Receive a block of data over the I2C bus from a slave device.
>>>> + *
>>>> + * @address: The I2C address of the slave device.
>>>> + * @numbytes: The amount of data to transmit.
>>>> + * @i2c_flag: Flags for transmission
>>>> + *
>>>> + * Returns 0 on success or error.
>>>> + */
>>>> +static uint32_t smu_v11_0_i2c_receive(struct i2c_adapter *control,
>>>> +                                uint8_t address, uint8_t *data,
>>>> +                                uint32_t numbytes, uint8_t i2c_flag)
>>>> +{
>>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>>>> +       uint32_t bytes_received, ret = I2C_OK;
>>>> +
>>>> +       bytes_received = 0;
>>>> +
>>>> +       /* Set the I2C slave address */
>>>> +       smu_v11_0_i2c_set_address(control, address);
>>>> +
>>>> +       /* Enable I2C */
>>>> +       smu_v11_0_i2c_enable(control, true);
>>>> +
>>>> +       while (numbytes > 0) {
>>>> +               uint32_t reg = 0;
>>>> +
>>>> +               smu_v11_0_i2c_clear_status(control);
>>>> +
>>>> +
>>>> +               /* Prepare transaction */
>>>> +
>>>> +               /* Each time we disable I2C, so this is not a restart */
>>>> +               if (bytes_received == 0)
>>>> +                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, RESTART,
>>>> +                                           (i2c_flag & I2C_RESTART) ? 1 : 0);
>>>> +
>>>> +               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, 0);
>>>> +               /* Read */
>>>> +               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 1);
>>>> +
>>>> +               /* Transmitting last byte */
>>>> +               if (numbytes == 1)
>>>> +                       /* Final transaction, so send stop if requested */
>>>> +                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, STOP,
>>>> +                                           (i2c_flag & I2C_NO_STOP) ? 0 : 1);
>>>> +
>>>> +               WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg);
>>>> +
>>>> +               ret = smu_v11_0_i2c_poll_rx_status(control);
>>>> +
>>>> +               /* Any error, no point in proceeding */
>>>> +               if (ret != I2C_OK) {
>>>> +                       if (ret & I2C_SW_TIMEOUT)
>>>> +                               DRM_ERROR("TIMEOUT ERROR !!!");
>>>> +
>>>> +                       if (ret & I2C_NAK_7B_ADDR_NOACK)
>>>> +                               DRM_ERROR("Received I2C_NAK_7B_ADDR_NOACK !!!");
>>>> +
>>>> +                       if (ret & I2C_NAK_TXDATA_NOACK)
>>>> +                               DRM_ERROR("Received I2C_NAK_TXDATA_NOACK !!!");
>>>> +
>>>> +                       break;
>>>> +               }
>>>> +
>>>> +               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD);
>>>> +               data[bytes_received] = REG_GET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT);
>>>> +
>>>> +               /* Record that the bytes were received */
>>>> +               bytes_received++;
>>>> +               numbytes--;
>>>> +       }
>>>> +
>>>> +       DRM_DEBUG_DRIVER("I2C_Receive(), address = %x, bytes = %d, data :",
>>>> +                 (uint16_t)address, bytes_received);
>>>> +
>>>> +       if (drm_debug & DRM_UT_DRIVER) {
>>>> +               print_hex_dump(KERN_INFO, "data: ", DUMP_PREFIX_NONE,
>>>> +                              16, 1, data, bytes_received, false);
>>>> +       }
>>>> +
>>>> +       return ret;
>>>> +}
>>>> +
>>>> +static void smu_v11_0_i2c_abort(struct i2c_adapter *control)
>>>> +{
>>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>>>> +       uint32_t reg = 0;
>>>> +
>>>> +       /* Enable I2C engine; */
>>>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_ENABLE, ENABLE, 1);
>>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, reg);
>>>> +
>>>> +       /* Abort previous transaction */
>>>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_ENABLE, ABORT, 1);
>>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, reg);
>>>> +
>>>> +       DRM_DEBUG_DRIVER("I2C_Abort() Done.");
>>>> +}
>>>> +
>>>> +
>>>> +static bool smu_v11_0_i2c_activity_done(struct i2c_adapter *control)
>>>> +{
>>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>>>> +
>>>> +       const uint32_t IDLE_TIMEOUT = 1024;
>>>> +       uint32_t timeoutCount = 0;
>>> kernel coding style prefers non CamelCase.  E.g., timeout_count.
>>>
>>>> +       uint32_t reg_ic_enable, reg_ic_enable_status, reg_ic_clr_activity;
>>>> +
>>>> +       reg_ic_enable_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE_STATUS);
>>>> +       reg_ic_enable = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE);
>>>> +
>>>> +
>>>> +       if ((REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) &&
>>>> +           (REG_GET_FIELD(reg_ic_enable_status, CKSVII2C_IC_ENABLE_STATUS, IC_EN) == 1)) {
>>>> +               /*
>>>> +                * Nobody is using I2C engine, but engine remains active because
>>>> +                * someone missed to send STOP
>>>> +                */
>>>> +               smu_v11_0_i2c_abort(control);
>>>> +       } else if (REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) {
>>>> +               /* Nobody is using I2C engine */
>>>> +               return true;
>>>> +       }
>>>> +
>>>> +       /* Keep reading activity bit until it's cleared */
>>>> +       do {
>>>> +               reg_ic_clr_activity = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CLR_ACTIVITY);
>>>> +
>>>> +               if (REG_GET_FIELD(reg_ic_clr_activity,
>>>> +                   CKSVII2C_IC_CLR_ACTIVITY, CLR_ACTIVITY) == 0)
>>>> +                       return true;
>>>> +
>>>> +               ++timeoutCount;
>>>> +
>>>> +       } while (timeoutCount < IDLE_TIMEOUT);
>>>> +
>>>> +       return false;
>>>> +}
>>>> +
>>>> +static void smu_v11_0_i2c_init(struct i2c_adapter *control)
>>>> +{
>>>> +       /* Disable clock gating */
>>>> +       smu_v11_0_i2c_dis_clock_gating(control);
>>>> +
>>>> +       if (!smu_v11_0_i2c_activity_done(control))
>>>> +               DRM_WARN("I2C busy !");
>>>> +
>>>> +       /* Disable I2C */
>>>> +       smu_v11_0_i2c_enable(control, false);
>>>> +
>>>> +       /* Configure I2C to operate as master and in standard mode */
>>>> +       smu_v11_0_i2c_configure(control);
>>>> +
>>>> +       /* Initialize the clock to 50 kHz default */
>>>> +       smu_v11_0_i2c_set_clock(control);
>>>> +
>>>> +}
>>>> +
>>>> +static void smu_v11_0_i2c_fini(struct i2c_adapter *control)
>>>> +{
>>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>>>> +       uint32_t reg_ic_enable_status, reg_ic_enable;
>>>> +
>>>> +       smu_v11_0_i2c_enable(control, false);
>>>> +
>>>> +       /* Double check if disabled, else force abort */
>>>> +       reg_ic_enable_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE_STATUS);
>>>> +       reg_ic_enable = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE);
>>>> +
>>>> +       if ((REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) &&
>>>> +           (REG_GET_FIELD(reg_ic_enable_status,
>>>> +                          CKSVII2C_IC_ENABLE_STATUS, IC_EN) == 1)) {
>>>> +               /*
>>>> +                * Nobody is using I2C engine, but engine remains active because
>>>> +                * someone missed to send STOP
>>>> +                */
>>>> +               smu_v11_0_i2c_abort(control);
>>>> +       }
>>>> +
>>>> +}
>>>> +
>>>> +static bool smu_v11_0_i2c_bus_lock(struct i2c_adapter *control)
>>>> +{
>>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>>>> +
>>>> +       /* Send  PPSMC_MSG_RequestI2CBus */
>>>> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access)
>>>> +               goto fail;
>>>> +
>>>> +
>>>> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access(adev->powerplay.pp_handle, true))
>>>> +               return true;
>>>> +
>>>> +fail:
>>>> +       return false;
>>>> +}
>>>> +
>>>> +static bool smu_v11_0_i2c_bus_unlock(struct i2c_adapter *control)
>>>> +{
>>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>>>> +
>>>> +       /* Send  PPSMC_MSG_RequestI2CBus */
>>>> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access)
>>>> +               goto fail;
>>>> +
>>>> +       /* Send  PPSMC_MSG_ReleaseI2CBus */
>>>> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access(adev->powerplay.pp_handle,
>>>> +                                                            false))
>>>> +               return true;
>>>> +
>>>> +fail:
>>>> +       return false;
>>>> +}
>>>> +
>>>> +/***************************** EEPROM I2C GLUE ****************************/
>>>> +
>>>> +static uint32_t smu_v11_0_i2c_eeprom_read_data(struct i2c_adapter *control,
>>>> +                                              uint8_t address,
>>>> +                                              uint8_t *data,
>>>> +                                              uint32_t numbytes)
>>>> +{
>>>> +       uint32_t  ret = 0;
>>>> +
>>>> +       /* First 2 bytes are dummy write to set EEPROM address */
>>>> +       ret = smu_v11_0_i2c_transmit(control, address, data, 2, I2C_NO_STOP);
>>>> +       if (ret != I2C_OK)
>>>> +               goto fail;
>>>> +
>>>> +       /* Now read data starting with that address */
>>>> +       ret = smu_v11_0_i2c_receive(control, address, data + 2, numbytes - 2,
>>>> +                                   I2C_RESTART);
>>>> +
>>>> +fail:
>>>> +       if (ret != I2C_OK)
>>>> +               DRM_ERROR("ReadData() - I2C error occurred :%x", ret);
>>>> +
>>>> +       return ret;
>>>> +}
>>>> +
>>>> +static uint32_t smu_v11_0_i2c_eeprom_write_data(struct i2c_adapter *control,
>>>> +                                               uint8_t address,
>>>> +                                               uint8_t *data,
>>>> +                                               uint32_t numbytes)
>>>> +{
>>>> +       uint32_t  ret;
>>>> +
>>>> +       ret = smu_v11_0_i2c_transmit(control, address, data, numbytes, 0);
>>>> +
>>>> +       if (ret != I2C_OK)
>>>> +               DRM_ERROR("WriteI2CData() - I2C error occurred :%x", ret);
>>>> +       else
>>>> +               /*
>>>> +                * According to EEPROM spec there is a MAX of 10 ms required for
>>>> +                * EEPROM to flush internal RX buffer after STOP was issued at the
>>>> +                * end of write transaction. During this time the EEPROM will not be
>>>> +                * responsive to any more commands - so wait a bit more.
>>>> +                *
>>>> +                * TODO Improve to wait for first ACK for slave address after
>>>> +                * internal write cycle done.
>>>> +                */
>>>> +               msleep(10);
>>>> +
>>>> +       return ret;
>>>> +
>>>> +}
>>>> +
>>>> +static void lock_bus(struct i2c_adapter *i2c, unsigned int flags)
>>>> +{
>>>> +       struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c);
>>>> +
>>>> +       if (!smu_v11_0_i2c_bus_lock(i2c)) {
>>>> +               DRM_ERROR("Failed to lock the bus from SMU");
>>>> +               return;
>>>> +       }
>>>> +
>>>> +       control->bus_locked = true;
>>>> +}
>>>> +
>>>> +static int trylock_bus(struct i2c_adapter *i2c, unsigned int flags)
>>>> +{
>>>> +       WARN_ONCE(1, "This operation not supposed to run in atomic context!");
>>>> +       return false;
>>>> +}
>>>> +
>>>> +static void unlock_bus(struct i2c_adapter *i2c, unsigned int flags)
>>>> +{
>>>> +       struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c);
>>>> +
>>>> +       if (!smu_v11_0_i2c_bus_unlock(i2c)) {
>>>> +               DRM_ERROR("Failed to unlock the bus from SMU");
>>>> +               return;
>>>> +       }
>>>> +
>>>> +       control->bus_locked = false;
>>>> +}
>>>> +
>>>> +static const struct i2c_lock_operations smu_v11_0_i2c_i2c_lock_ops = {
>>>> +       .lock_bus = lock_bus,
>>>> +       .trylock_bus = trylock_bus,
>>>> +       .unlock_bus = unlock_bus,
>>>> +};
>>>> +
>>>> +static int smu_v11_0_i2c_eeprom_i2c_xfer(struct i2c_adapter *i2c_adap,
>>>> +                             struct i2c_msg *msgs, int num)
>>>> +{
>>>> +       int i, ret;
>>>> +       struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c_adap);
>>>> +
>>>> +       if (!control->bus_locked) {
>>>> +               DRM_ERROR("I2C bus unlocked, stopping transaction!");
>>>> +               return -EIO;
>>>> +       }
>>>> +
>>>> +       smu_v11_0_i2c_init(i2c_adap);
>>>> +
>>>> +       for (i = 0; i < num; i++) {
>>>> +               if (msgs[i].flags & I2C_M_RD)
>>>> +                       ret = smu_v11_0_i2c_eeprom_read_data(i2c_adap,
>>>> +                                                       (uint8_t)msgs[i].addr,
>>>> +                                                       msgs[i].buf, msgs[i].len);
>>>> +               else
>>>> +                       ret = smu_v11_0_i2c_eeprom_write_data(i2c_adap,
>>>> +                                                        (uint8_t)msgs[i].addr,
>>>> +                                                        msgs[i].buf, msgs[i].len);
>>>> +
>>>> +               if (ret != I2C_OK) {
>>>> +                       num = -EIO;
>>>> +                       break;
>>>> +               }
>>>> +       }
>>>> +
>>>> +       smu_v11_0_i2c_fini(i2c_adap);
>>>> +       return num;
>>>> +}
>>>> +
>>>> +static u32 smu_v11_0_i2c_eeprom_i2c_func(struct i2c_adapter *adap)
>>>> +{
>>>> +       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
>>>> +}
>>>> +
>>>> +
>>>> +static const struct i2c_algorithm smu_v11_0_i2c_eeprom_i2c_algo = {
>>>> +       .master_xfer = smu_v11_0_i2c_eeprom_i2c_xfer,
>>>> +       .functionality = smu_v11_0_i2c_eeprom_i2c_func,
>>>> +};
>>>> +
>>>> +int smu_v11_0_i2c_eeprom_control_init(struct i2c_adapter *control)
>>>> +{
>>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
>>>> +       int res;
>>>> +
>>>> +       control->owner = THIS_MODULE;
>>>> +       control->class = I2C_CLASS_SPD;
>>>> +       control->dev.parent = &adev->pdev->dev;
>>>> +       control->algo = &smu_v11_0_i2c_eeprom_i2c_algo;
>>>> +       snprintf(control->name, sizeof(control->name), "RAS EEPROM");
>>>> +       control->lock_ops = &smu_v11_0_i2c_i2c_lock_ops;
>>>> +
>>>> +       res = i2c_add_adapter(control);
>>>> +       if (res)
>>>> +               DRM_ERROR("Failed to register hw i2c, err: %d\n", res);
>>>> +
>>>> +       return res;
>>>> +}
>>>> +
>>>> +void smu_v11_0_i2c_eeprom_control_fini(struct i2c_adapter *control)
>>>> +{
>>>> +       i2c_del_adapter(control);
>>>> +}
>>>> +
>>>> +#define I2C_TARGET_ADDR 0xA0
>>>> +
>>>> +bool smu_v11_0_i2c_test_bus(struct i2c_adapter *control)
>>>> +{
>>>> +
>>>> +       uint32_t ret = I2C_OK;
>>>> +       uint8_t data[6] = {0xf, 0, 0xde, 0xad, 0xbe, 0xef};
>>>> +
>>>> +
>>>> +       DRM_INFO("Begin");
>>>> +
>>>> +       if (!smu_v11_0_i2c_bus_lock(control)) {
>>>> +               DRM_ERROR("Failed to lock the bus!.");
>>>> +               return false;
>>>> +       }
>>>> +
>>>> +       smu_v11_0_i2c_init(control);
>>>> +
>>>> +       /* Write 0xde to address 0x0000 on the EEPROM */
>>>> +       ret = smu_v11_0_i2c_eeprom_write_data(control, I2C_TARGET_ADDR, data, 6);
>>>> +
>>>> +       ret = smu_v11_0_i2c_eeprom_read_data(control, I2C_TARGET_ADDR, data, 6);
>>>> +
>>>> +       smu_v11_0_i2c_fini(control);
>>>> +
>>>> +       smu_v11_0_i2c_bus_unlock(control);
>>>> +
>>>> +
>>>> +       DRM_INFO("End");
>>>> +       return true;
>>>> +}
>>> Leftover debugging function?


Missed that one, yes. Should I remove it ? Could be useful at some point 
for future bugs debugging...

Andrey


>>>
>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
>>>> new file mode 100644
>>>> index 0000000..973f28d
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
>>>> @@ -0,0 +1,34 @@
>>>> +/*
>>>> + * 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.
>>>> + *
>>>> + */
>>>> +
>>>> +#ifndef SMU_V11_I2C_CONTROL_H
>>>> +#define SMU_V11_I2C_CONTROL_H
>>>> +
>>>> +#include <linux/types.h>
>>>> +
>>>> +struct i2c_adapter;
>>>> +
>>>> +int smu_v11_0_i2c_eeprom_control_init(struct i2c_adapter *control);
>>>> +void smu_v11_0_i2c_eeprom_control_fini(struct i2c_adapter *control);
>>>> +
>>>> +#endif
>>>> --
>>>> 2.7.4
>>>>
>>>> _______________________________________________
>>>> amd-gfx mailing list
>>>> amd-gfx@lists.freedesktop.org
>>>> https://lists.freedesktop.org/mailman/listinfo/amd-gfx
_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 4/4] drm/amdgpu: Vega20 SMU I2C HW engine controller.
       [not found]                     ` <a29f8c1c-3b0f-0412-ab66-441fae96ed8d-5C7GfCeVMHo@public.gmane.org>
@ 2019-08-22 19:13                       ` Alex Deucher
  0 siblings, 0 replies; 18+ messages in thread
From: Alex Deucher @ 2019-08-22 19:13 UTC (permalink / raw)
  To: Grodzovsky, Andrey
  Cc: Panariti, David, Pan, Xinhui, Lazar, Lijo, amd-gfx list, Deucher,
	Alexander, Tuikov, Luben, Russell, Kent, Zhou1, Tao, Quan, Evan,
	Zhang, Hawking

On Thu, Aug 22, 2019 at 12:45 PM Grodzovsky, Andrey
<Andrey.Grodzovsky@amd.com> wrote:
>
>
> On 8/22/19 11:59 AM, Alex Deucher wrote:
> > On Thu, Aug 22, 2019 at 11:35 AM Grodzovsky, Andrey
> > <Andrey.Grodzovsky@amd.com> wrote:
> >>
> >> On 8/21/19 10:32 PM, Alex Deucher wrote:
> >>> On Wed, Aug 21, 2019 at 4:02 PM Andrey Grodzovsky
> >>> <andrey.grodzovsky@amd.com> wrote:
> >>>> Implement HW I2C enigne controller to be used by the RAS EEPROM
> >>>> table manager. This is based on code from ATITOOLs.
> >>>>
> >>>> v2:
> >>>> Rename the file and all function prefixes to smu_v11_0_i2c
> >>>>
> >>>> By Luben's observation always fill the TX fifo to full so
> >>>> we don't have garbadge interpreted by the slave as valid data.
> >>>>
> >>>> v3:
> >>>> Remove preemption disable as the HW I2C controller will not
> >>>> stop the clock on empty TX fifo and so it's not critical to
> >>>> keep not empty queue.
> >>>> Switch to fast mode 400 khz SCL clock for faster read and write.
> >>>>
> >>>> Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@amd.com>
> >>>> ---
> >>>>    drivers/gpu/drm/amd/amdgpu/Makefile            |   5 +-
> >>>>    drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c |   5 +-
> >>>>    drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c     | 710 +++++++++++++++++++++++++
> >>>>    drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h     |  34 ++
> >>>>    4 files changed, 751 insertions(+), 3 deletions(-)
> >>>>    create mode 100644 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
> >>>>    create mode 100644 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
> >>>>
> >>>> diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
> >>>> index f016cf1..14733ff 100644
> >>>> --- a/drivers/gpu/drm/amd/amdgpu/Makefile
> >>>> +++ b/drivers/gpu/drm/amd/amdgpu/Makefile
> >>>> @@ -38,6 +38,9 @@ ccflags-y := -I$(FULL_AMD_PATH)/include/asic_reg \
> >>>>           -I$(FULL_AMD_DISPLAY_PATH)/amdgpu_dm \
> >>>>           -I$(FULL_AMD_PATH)/amdkfd
> >>>>
> >>>> +
> >>>> +
> >>>> +
> >>> Drop this random whitespace change.
> >>>
> >>>>    amdgpu-y := amdgpu_drv.o
> >>>>
> >>>>    # add KMS driver
> >>>> @@ -54,7 +57,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
> >>>>           amdgpu_gtt_mgr.o amdgpu_vram_mgr.o amdgpu_virt.o amdgpu_atomfirmware.o \
> >>>>           amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o amdgpu_ids.o \
> >>>>           amdgpu_gmc.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o amdgpu_vm_cpu.o \
> >>>> -       amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o amdgpu_ras_eeprom.o
> >>>> +       amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o amdgpu_ras_eeprom.o smu_v11_0_i2c.o
> >>>>
> >>>>    amdgpu-$(CONFIG_PERF_EVENTS) += amdgpu_pmu.o
> >>>>
> >>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
> >>>> index bf07515..e6b2e17 100644
> >>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
> >>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
> >>>> @@ -25,6 +25,7 @@
> >>>>    #include "amdgpu.h"
> >>>>    #include "amdgpu_ras.h"
> >>>>    #include <linux/bits.h>
> >>>> +#include "smu_v11_0_i2c.h"
> >>>>
> >>>>    #define EEPROM_I2C_TARGET_ADDR 0xA0
> >>>>
> >>>> @@ -111,7 +112,7 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
> >>>>
> >>>>           switch (adev->asic_type) {
> >>>>           case CHIP_VEGA20:
> >>>> -       /*TODO Add MI-60 */
> >>>> +               ret = smu_v11_0_i2c_eeprom_control_init(&control->eeprom_accessor);
> >>>>                   break;
> >>>>
> >>>>           default:
> >>>> @@ -163,7 +164,7 @@ void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control *control)
> >>>>
> >>>>           switch (adev->asic_type) {
> >>>>           case CHIP_VEGA20:
> >>>> -               /*TODO Add MI-60 */
> >>>> +               smu_v11_0_i2c_eeprom_control_fini(&control->eeprom_accessor);
> >>>>                   break;
> >>>>
> >>>>           default:
> >>>> diff --git a/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
> >>>> new file mode 100644
> >>>> index 0000000..24405fa
> >>>> --- /dev/null
> >>>> +++ b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
> >>>> @@ -0,0 +1,710 @@
> >>>> +/*
> >>>> + * 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 "smuio/smuio_11_0_0_offset.h"
> >>>> +#include "smuio/smuio_11_0_0_sh_mask.h"
> >>>> +
> >>>> +#include "smu_v11_0_i2c.h"
> >>>> +#include "amdgpu.h"
> >>>> +#include "soc15_common.h"
> >>>> +#include <drm/drm_fixed.h>
> >>>> +#include "amdgpu_amdkfd.h"
> >>>> +#include <linux/i2c.h>
> >>>> +#include "amdgpu_ras.h"
> >>>> +
> >>>> +/* error codes */
> >>>> +#define I2C_OK                         0
> >>>> +#define I2C_NAK_7B_ADDR_NOACK          1
> >>>> +#define I2C_NAK_TXDATA_NOACK           2
> >>>> +#define I2C_TIMEOUT                    4
> >>>> +#define I2C_SW_TIMEOUT                 8
> >>>> +#define I2C_ABORT                      0x10
> >>>> +
> >>>> +/* I2C transaction flags */
> >>>> +#define I2C_NO_STOP    1
> >>>> +#define I2C_RESTART    2
> >>>> +
> >>>> +#define to_amdgpu_device(x) (container_of(x, struct amdgpu_ras, eeprom_control.eeprom_accessor))->adev
> >>>> +#define to_eeprom_control(x) container_of(x, struct amdgpu_ras_eeprom_control, eeprom_accessor)
> >>>> +
> >>>> +static void smu_v11_0_i2c_dis_clock_gating(struct i2c_adapter *control)
> >>>> +{
> >>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >>>> +       uint32_t reg;
> >>>> +
> >>>> +       reg = RREG32_SOC15(SMUIO, 0, mmSMUIO_PWRMGT);
> >>>> +       reg = REG_SET_FIELD(reg, SMUIO_PWRMGT, i2c_clk_gate_en, 0);
> >>>> +       WREG32_SOC15(SMUIO, 0, mmSMUIO_PWRMGT, reg);
> >>>> +}
> >>> Do we need to worry about re-enabling clock gating when we are done?
> >>
> >> In smu_11_smuio_mas doc this step is missing for I2C0 (sec. 18.4.1.1)
> >> and hence probably also was missing from the original code this was
> >> ported so I am not sure. The side effect of this staying disabled is
> >> less  power saving I think.
> > Can we check the state of SMUIO_PWRMGT.i2c_clk_gate_en at init time,
> > and then, if it's enabled, we can disable it in lock_bus and enable it
> > in unlock_bus.
> >
> > Alex
>
>
> smu_v11_0_i2c_init is called after the lock_bus. I think we should just
> do what you suggest in  smu_v11_0_i2c_init and smu_v11_0_i2c_fini
> respectively.

sounds good.

>
>
> >
> >> Andrey
> >>
> >>
> >>>
> >>>> +
> >>>> +
> >>>> +static void smu_v11_0_i2c_enable(struct i2c_adapter *control, bool enable)
> >>>> +{
> >>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >>>> +
> >>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, enable ? 1 : 0);
> >>>> +}
> >>>> +
> >>>> +static void smu_v11_0_i2c_clear_status(struct i2c_adapter *control)
> >>>> +{
> >>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >>>> +       /* do */
> >>>> +       {
> >>>> +               RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CLR_INTR);
> >>>> +
> >>>> +       } /* while (reg_CKSVII2C_ic_clr_intr == 0) */
> >>>> +}
> >>>> +
> >>>> +static void smu_v11_0_i2c_configure(struct i2c_adapter *control)
> >>>> +{
> >>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >>>> +       uint32_t reg = 0;
> >>>> +
> >>>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_SLAVE_DISABLE, 1);
> >>>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_RESTART_EN, 1);
> >>>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_10BITADDR_MASTER, 0);
> >>>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_10BITADDR_SLAVE, 0);
> >>>> +       /* Standard mode */
> >>>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_MAX_SPEED_MODE, 2);
> >>>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_MASTER_MODE, 1);
> >>>> +
> >>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CON, reg);
> >>>> +}
> >>>> +
> >>>> +static void smu_v11_0_i2c_set_clock(struct i2c_adapter *control)
> >>>> +{
> >>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >>>> +
> >>>> +       /*
> >>>> +        * Standard mode speed, These values are taken from SMUIO MAS,
> >>>> +        * but are different from what is given is
> >>>> +        * Synopsys spec. The values here are based on assumption
> >>>> +        * that refclock is 100MHz
> >>>> +        *
> >>>> +        * Configuration for standard mode; Speed = 100kbps
> >>>> +        * Scale linearly, for now only support standard speed clock
> >>>> +        * This will work only with 100M ref clock
> >>>> +        *
> >>>> +        * TBD:Change the calculation to take into account ref clock values also.
> >>>> +        */
> >>>> +
> >>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_FS_SPKLEN, 2);
> >>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SS_SCL_HCNT, 120);
> >>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SS_SCL_LCNT, 130);
> >>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SDA_HOLD, 20);
> >>>> +}
> >>>> +
> >>>> +static void smu_v11_0_i2c_set_address(struct i2c_adapter *control, uint8_t address)
> >>>> +{
> >>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >>>> +
> >>>> +       /* Convert fromr 8-bit to 7-bit address */
> >>>> +       address >>= 1;
> >>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TAR, (address & 0xFF));
> >>>> +}
> >>>> +
> >>>> +static uint32_t smu_v11_0_i2c_poll_tx_status(struct i2c_adapter *control)
> >>>> +{
> >>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >>>> +       uint32_t ret = I2C_OK;
> >>>> +       uint32_t reg, reg_c_tx_abrt_source;
> >>>> +
> >>>> +       /*Check if transmission is completed */
> >>>> +       unsigned long  timeout_counter = jiffies + msecs_to_jiffies(20);
> >>>> +
> >>>> +       do {
> >>>> +               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
> >>>> +
> >>>> +               if (time_after(jiffies, timeout_counter)) {
> >>>> +                       ret |= I2C_SW_TIMEOUT;
> >>>> +                       break;
> >>>> +               }
> >>>> +       } while (REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFE) == 0);
> >>>> +
> >>>> +       if (ret != I2C_OK)
> >>>> +               return ret;
> >>>> +
> >>>> +       /* This only checks if NAK is received and transaction got aborted */
> >>>> +       reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_INTR_STAT);
> >>>> +
> >>>> +       if (REG_GET_FIELD(reg, CKSVII2C_IC_INTR_STAT, R_TX_ABRT) == 1) {
> >>>> +               reg_c_tx_abrt_source = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TX_ABRT_SOURCE);
> >>>> +               DRM_INFO("TX was terminated, IC_TX_ABRT_SOURCE val is:%x", reg_c_tx_abrt_source);
> >>>> +
> >>>> +               /* Check for stop due to NACK */
> >>>> +               if (REG_GET_FIELD(reg_c_tx_abrt_source,
> >>>> +                                 CKSVII2C_IC_TX_ABRT_SOURCE,
> >>>> +                                 ABRT_TXDATA_NOACK) == 1) {
> >>>> +
> >>>> +                       ret |= I2C_NAK_TXDATA_NOACK;
> >>>> +
> >>>> +               } else if (REG_GET_FIELD(reg_c_tx_abrt_source,
> >>>> +                                        CKSVII2C_IC_TX_ABRT_SOURCE,
> >>>> +                                        ABRT_7B_ADDR_NOACK) == 1) {
> >>>> +
> >>>> +                       ret |= I2C_NAK_7B_ADDR_NOACK;
> >>>> +               } else {
> >>>> +                       ret |= I2C_ABORT;
> >>>> +               }
> >>>> +
> >>>> +               smu_v11_0_i2c_clear_status(control);
> >>>> +       }
> >>>> +
> >>>> +       return ret;
> >>>> +}
> >>>> +
> >>>> +static uint32_t smu_v11_0_i2c_poll_rx_status(struct i2c_adapter *control)
> >>>> +{
> >>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >>>> +       uint32_t ret = I2C_OK;
> >>>> +       uint32_t reg_ic_status, reg_c_tx_abrt_source;
> >>>> +
> >>>> +       reg_c_tx_abrt_source = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TX_ABRT_SOURCE);
> >>>> +
> >>>> +       /* If slave is not present */
> >>>> +       if (REG_GET_FIELD(reg_c_tx_abrt_source,
> >>>> +                         CKSVII2C_IC_TX_ABRT_SOURCE,
> >>>> +                         ABRT_7B_ADDR_NOACK) == 1) {
> >>>> +               ret |= I2C_NAK_7B_ADDR_NOACK;
> >>>> +
> >>>> +               smu_v11_0_i2c_clear_status(control);
> >>>> +       } else {  /* wait till some data is there in RXFIFO */
> >>>> +               /* Poll for some byte in RXFIFO */
> >>>> +               unsigned long  timeout_counter = jiffies + msecs_to_jiffies(20);
> >>>> +
> >>>> +               do {
> >>>> +                       reg_ic_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
> >>>> +
> >>>> +                       if (time_after(jiffies, timeout_counter)) {
> >>>> +                               ret |= I2C_SW_TIMEOUT;
> >>>> +                               break;
> >>>> +                       }
> >>>> +               } while (REG_GET_FIELD(reg_ic_status, CKSVII2C_IC_STATUS, RFNE) == 0);
> >>>> +       }
> >>>> +
> >>>> +       return ret;
> >>>> +}
> >>>> +
> >>>> +
> >>>> +
> >>>> +
> >>>> +/**
> >>>> + * smu_v11_0_i2c_transmit - Send a block of data over the I2C bus to a slave device.
> >>>> + *
> >>>> + * @address: The I2C address of the slave device.
> >>>> + * @data: The data to transmit over the bus.
> >>>> + * @numbytes: The amount of data to transmit.
> >>>> + * @i2c_flag: Flags for transmission
> >>>> + *
> >>>> + * Returns 0 on success or error.
> >>>> + */
> >>>> +static uint32_t smu_v11_0_i2c_transmit(struct i2c_adapter *control,
> >>>> +                                 uint8_t address, uint8_t *data,
> >>>> +                                 uint32_t numbytes, uint32_t i2c_flag)
> >>>> +{
> >>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >>>> +       uint32_t bytes_sent, reg, ret = 0;
> >>>> +       unsigned long  timeout_counter;
> >>>> +
> >>>> +       bytes_sent = 0;
> >>>> +
> >>>> +       DRM_DEBUG_DRIVER("I2C_Transmit(), address = %x, bytes = %d , data: ",
> >>>> +                (uint16_t)address, numbytes);
> >>>> +
> >>>> +       if (drm_debug & DRM_UT_DRIVER) {
> >>>> +               print_hex_dump(KERN_INFO, "data: ", DUMP_PREFIX_NONE,
> >>>> +                              16, 1, data, numbytes, false);
> >>>> +       }
> >>>> +
> >>>> +       /* Set the I2C slave address */
> >>>> +       smu_v11_0_i2c_set_address(control, address);
> >>>> +       /* Enable I2C */
> >>>> +       smu_v11_0_i2c_enable(control, true);
> >>>> +
> >>>> +       /* Clear status bits */
> >>>> +       smu_v11_0_i2c_clear_status(control);
> >>>> +
> >>>> +
> >>>> +       timeout_counter = jiffies + msecs_to_jiffies(20);
> >>>> +
> >>>> +       while (numbytes > 0) {
> >>>> +               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
> >>>> +               if (REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFNF)) {
> >>>> +                       do {
> >>>> +                               reg = 0;
> >>>> +                               /*
> >>>> +                                * Prepare transaction, no need to set RESTART. I2C engine will send
> >>>> +                                * START as soon as it sees data in TXFIFO
> >>>> +                                */
> >>>> +                               if (bytes_sent == 0)
> >>>> +                                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, RESTART,
> >>>> +                                                           (i2c_flag & I2C_RESTART) ? 1 : 0);
> >>>> +                               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, data[bytes_sent]);
> >>>> +
> >>>> +                               /* determine if we need to send STOP bit or not */
> >>>> +                               if (numbytes == 1)
> >>>> +                                       /* Final transaction, so send stop unless I2C_NO_STOP */
> >>>> +                                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, STOP,
> >>>> +                                                           (i2c_flag & I2C_NO_STOP) ? 0 : 1);
> >>>> +                               /* Write */
> >>>> +                               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 0);
> >>>> +                               WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg);
> >>>> +
> >>>> +                               /* Record that the bytes were transmitted */
> >>>> +                               bytes_sent++;
> >>>> +                               numbytes--;
> >>>> +
> >>>> +                               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
> >>>> +
> >>>> +                       } while (numbytes &&  REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFNF));
> >>>> +               }
> >>>> +
> >>>> +
> >>>> +               if (time_after(jiffies, timeout_counter)) {
> >>>> +                       ret |= I2C_SW_TIMEOUT;
> >>>> +                       goto err;
> >>>> +               }
> >>>> +       }
> >>>> +
> >>>> +       ret = smu_v11_0_i2c_poll_tx_status(control);
> >>>> +
> >>>> +err:
> >>>> +       /* Any error, no point in proceeding */
> >>>> +       if (ret != I2C_OK) {
> >>>> +               if (ret & I2C_SW_TIMEOUT)
> >>>> +                       DRM_ERROR("TIMEOUT ERROR !!!");
> >>>> +
> >>>> +               if (ret & I2C_NAK_7B_ADDR_NOACK)
> >>>> +                       DRM_ERROR("Received I2C_NAK_7B_ADDR_NOACK !!!");
> >>>> +
> >>>> +
> >>>> +               if (ret & I2C_NAK_TXDATA_NOACK)
> >>>> +                       DRM_ERROR("Received I2C_NAK_TXDATA_NOACK !!!");
> >>>> +       }
> >>>> +
> >>>> +       return ret;
> >>>> +}
> >>>> +
> >>>> +
> >>>> +/**
> >>>> + * smu_v11_0_i2c_receive - Receive a block of data over the I2C bus from a slave device.
> >>>> + *
> >>>> + * @address: The I2C address of the slave device.
> >>>> + * @numbytes: The amount of data to transmit.
> >>>> + * @i2c_flag: Flags for transmission
> >>>> + *
> >>>> + * Returns 0 on success or error.
> >>>> + */
> >>>> +static uint32_t smu_v11_0_i2c_receive(struct i2c_adapter *control,
> >>>> +                                uint8_t address, uint8_t *data,
> >>>> +                                uint32_t numbytes, uint8_t i2c_flag)
> >>>> +{
> >>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >>>> +       uint32_t bytes_received, ret = I2C_OK;
> >>>> +
> >>>> +       bytes_received = 0;
> >>>> +
> >>>> +       /* Set the I2C slave address */
> >>>> +       smu_v11_0_i2c_set_address(control, address);
> >>>> +
> >>>> +       /* Enable I2C */
> >>>> +       smu_v11_0_i2c_enable(control, true);
> >>>> +
> >>>> +       while (numbytes > 0) {
> >>>> +               uint32_t reg = 0;
> >>>> +
> >>>> +               smu_v11_0_i2c_clear_status(control);
> >>>> +
> >>>> +
> >>>> +               /* Prepare transaction */
> >>>> +
> >>>> +               /* Each time we disable I2C, so this is not a restart */
> >>>> +               if (bytes_received == 0)
> >>>> +                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, RESTART,
> >>>> +                                           (i2c_flag & I2C_RESTART) ? 1 : 0);
> >>>> +
> >>>> +               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, 0);
> >>>> +               /* Read */
> >>>> +               reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 1);
> >>>> +
> >>>> +               /* Transmitting last byte */
> >>>> +               if (numbytes == 1)
> >>>> +                       /* Final transaction, so send stop if requested */
> >>>> +                       reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, STOP,
> >>>> +                                           (i2c_flag & I2C_NO_STOP) ? 0 : 1);
> >>>> +
> >>>> +               WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg);
> >>>> +
> >>>> +               ret = smu_v11_0_i2c_poll_rx_status(control);
> >>>> +
> >>>> +               /* Any error, no point in proceeding */
> >>>> +               if (ret != I2C_OK) {
> >>>> +                       if (ret & I2C_SW_TIMEOUT)
> >>>> +                               DRM_ERROR("TIMEOUT ERROR !!!");
> >>>> +
> >>>> +                       if (ret & I2C_NAK_7B_ADDR_NOACK)
> >>>> +                               DRM_ERROR("Received I2C_NAK_7B_ADDR_NOACK !!!");
> >>>> +
> >>>> +                       if (ret & I2C_NAK_TXDATA_NOACK)
> >>>> +                               DRM_ERROR("Received I2C_NAK_TXDATA_NOACK !!!");
> >>>> +
> >>>> +                       break;
> >>>> +               }
> >>>> +
> >>>> +               reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD);
> >>>> +               data[bytes_received] = REG_GET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT);
> >>>> +
> >>>> +               /* Record that the bytes were received */
> >>>> +               bytes_received++;
> >>>> +               numbytes--;
> >>>> +       }
> >>>> +
> >>>> +       DRM_DEBUG_DRIVER("I2C_Receive(), address = %x, bytes = %d, data :",
> >>>> +                 (uint16_t)address, bytes_received);
> >>>> +
> >>>> +       if (drm_debug & DRM_UT_DRIVER) {
> >>>> +               print_hex_dump(KERN_INFO, "data: ", DUMP_PREFIX_NONE,
> >>>> +                              16, 1, data, bytes_received, false);
> >>>> +       }
> >>>> +
> >>>> +       return ret;
> >>>> +}
> >>>> +
> >>>> +static void smu_v11_0_i2c_abort(struct i2c_adapter *control)
> >>>> +{
> >>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >>>> +       uint32_t reg = 0;
> >>>> +
> >>>> +       /* Enable I2C engine; */
> >>>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_ENABLE, ENABLE, 1);
> >>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, reg);
> >>>> +
> >>>> +       /* Abort previous transaction */
> >>>> +       reg = REG_SET_FIELD(reg, CKSVII2C_IC_ENABLE, ABORT, 1);
> >>>> +       WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, reg);
> >>>> +
> >>>> +       DRM_DEBUG_DRIVER("I2C_Abort() Done.");
> >>>> +}
> >>>> +
> >>>> +
> >>>> +static bool smu_v11_0_i2c_activity_done(struct i2c_adapter *control)
> >>>> +{
> >>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >>>> +
> >>>> +       const uint32_t IDLE_TIMEOUT = 1024;
> >>>> +       uint32_t timeoutCount = 0;
> >>> kernel coding style prefers non CamelCase.  E.g., timeout_count.
> >>>
> >>>> +       uint32_t reg_ic_enable, reg_ic_enable_status, reg_ic_clr_activity;
> >>>> +
> >>>> +       reg_ic_enable_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE_STATUS);
> >>>> +       reg_ic_enable = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE);
> >>>> +
> >>>> +
> >>>> +       if ((REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) &&
> >>>> +           (REG_GET_FIELD(reg_ic_enable_status, CKSVII2C_IC_ENABLE_STATUS, IC_EN) == 1)) {
> >>>> +               /*
> >>>> +                * Nobody is using I2C engine, but engine remains active because
> >>>> +                * someone missed to send STOP
> >>>> +                */
> >>>> +               smu_v11_0_i2c_abort(control);
> >>>> +       } else if (REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) {
> >>>> +               /* Nobody is using I2C engine */
> >>>> +               return true;
> >>>> +       }
> >>>> +
> >>>> +       /* Keep reading activity bit until it's cleared */
> >>>> +       do {
> >>>> +               reg_ic_clr_activity = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CLR_ACTIVITY);
> >>>> +
> >>>> +               if (REG_GET_FIELD(reg_ic_clr_activity,
> >>>> +                   CKSVII2C_IC_CLR_ACTIVITY, CLR_ACTIVITY) == 0)
> >>>> +                       return true;
> >>>> +
> >>>> +               ++timeoutCount;
> >>>> +
> >>>> +       } while (timeoutCount < IDLE_TIMEOUT);
> >>>> +
> >>>> +       return false;
> >>>> +}
> >>>> +
> >>>> +static void smu_v11_0_i2c_init(struct i2c_adapter *control)
> >>>> +{
> >>>> +       /* Disable clock gating */
> >>>> +       smu_v11_0_i2c_dis_clock_gating(control);
> >>>> +
> >>>> +       if (!smu_v11_0_i2c_activity_done(control))
> >>>> +               DRM_WARN("I2C busy !");
> >>>> +
> >>>> +       /* Disable I2C */
> >>>> +       smu_v11_0_i2c_enable(control, false);
> >>>> +
> >>>> +       /* Configure I2C to operate as master and in standard mode */
> >>>> +       smu_v11_0_i2c_configure(control);
> >>>> +
> >>>> +       /* Initialize the clock to 50 kHz default */
> >>>> +       smu_v11_0_i2c_set_clock(control);
> >>>> +
> >>>> +}
> >>>> +
> >>>> +static void smu_v11_0_i2c_fini(struct i2c_adapter *control)
> >>>> +{
> >>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >>>> +       uint32_t reg_ic_enable_status, reg_ic_enable;
> >>>> +
> >>>> +       smu_v11_0_i2c_enable(control, false);
> >>>> +
> >>>> +       /* Double check if disabled, else force abort */
> >>>> +       reg_ic_enable_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE_STATUS);
> >>>> +       reg_ic_enable = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE);
> >>>> +
> >>>> +       if ((REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) &&
> >>>> +           (REG_GET_FIELD(reg_ic_enable_status,
> >>>> +                          CKSVII2C_IC_ENABLE_STATUS, IC_EN) == 1)) {
> >>>> +               /*
> >>>> +                * Nobody is using I2C engine, but engine remains active because
> >>>> +                * someone missed to send STOP
> >>>> +                */
> >>>> +               smu_v11_0_i2c_abort(control);
> >>>> +       }
> >>>> +
> >>>> +}
> >>>> +
> >>>> +static bool smu_v11_0_i2c_bus_lock(struct i2c_adapter *control)
> >>>> +{
> >>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >>>> +
> >>>> +       /* Send  PPSMC_MSG_RequestI2CBus */
> >>>> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access)
> >>>> +               goto fail;
> >>>> +
> >>>> +
> >>>> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access(adev->powerplay.pp_handle, true))
> >>>> +               return true;
> >>>> +
> >>>> +fail:
> >>>> +       return false;
> >>>> +}
> >>>> +
> >>>> +static bool smu_v11_0_i2c_bus_unlock(struct i2c_adapter *control)
> >>>> +{
> >>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >>>> +
> >>>> +       /* Send  PPSMC_MSG_RequestI2CBus */
> >>>> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access)
> >>>> +               goto fail;
> >>>> +
> >>>> +       /* Send  PPSMC_MSG_ReleaseI2CBus */
> >>>> +       if (!adev->powerplay.pp_funcs->smu_i2c_bus_access(adev->powerplay.pp_handle,
> >>>> +                                                            false))
> >>>> +               return true;
> >>>> +
> >>>> +fail:
> >>>> +       return false;
> >>>> +}
> >>>> +
> >>>> +/***************************** EEPROM I2C GLUE ****************************/
> >>>> +
> >>>> +static uint32_t smu_v11_0_i2c_eeprom_read_data(struct i2c_adapter *control,
> >>>> +                                              uint8_t address,
> >>>> +                                              uint8_t *data,
> >>>> +                                              uint32_t numbytes)
> >>>> +{
> >>>> +       uint32_t  ret = 0;
> >>>> +
> >>>> +       /* First 2 bytes are dummy write to set EEPROM address */
> >>>> +       ret = smu_v11_0_i2c_transmit(control, address, data, 2, I2C_NO_STOP);
> >>>> +       if (ret != I2C_OK)
> >>>> +               goto fail;
> >>>> +
> >>>> +       /* Now read data starting with that address */
> >>>> +       ret = smu_v11_0_i2c_receive(control, address, data + 2, numbytes - 2,
> >>>> +                                   I2C_RESTART);
> >>>> +
> >>>> +fail:
> >>>> +       if (ret != I2C_OK)
> >>>> +               DRM_ERROR("ReadData() - I2C error occurred :%x", ret);
> >>>> +
> >>>> +       return ret;
> >>>> +}
> >>>> +
> >>>> +static uint32_t smu_v11_0_i2c_eeprom_write_data(struct i2c_adapter *control,
> >>>> +                                               uint8_t address,
> >>>> +                                               uint8_t *data,
> >>>> +                                               uint32_t numbytes)
> >>>> +{
> >>>> +       uint32_t  ret;
> >>>> +
> >>>> +       ret = smu_v11_0_i2c_transmit(control, address, data, numbytes, 0);
> >>>> +
> >>>> +       if (ret != I2C_OK)
> >>>> +               DRM_ERROR("WriteI2CData() - I2C error occurred :%x", ret);
> >>>> +       else
> >>>> +               /*
> >>>> +                * According to EEPROM spec there is a MAX of 10 ms required for
> >>>> +                * EEPROM to flush internal RX buffer after STOP was issued at the
> >>>> +                * end of write transaction. During this time the EEPROM will not be
> >>>> +                * responsive to any more commands - so wait a bit more.
> >>>> +                *
> >>>> +                * TODO Improve to wait for first ACK for slave address after
> >>>> +                * internal write cycle done.
> >>>> +                */
> >>>> +               msleep(10);
> >>>> +
> >>>> +       return ret;
> >>>> +
> >>>> +}
> >>>> +
> >>>> +static void lock_bus(struct i2c_adapter *i2c, unsigned int flags)
> >>>> +{
> >>>> +       struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c);
> >>>> +
> >>>> +       if (!smu_v11_0_i2c_bus_lock(i2c)) {
> >>>> +               DRM_ERROR("Failed to lock the bus from SMU");
> >>>> +               return;
> >>>> +       }
> >>>> +
> >>>> +       control->bus_locked = true;
> >>>> +}
> >>>> +
> >>>> +static int trylock_bus(struct i2c_adapter *i2c, unsigned int flags)
> >>>> +{
> >>>> +       WARN_ONCE(1, "This operation not supposed to run in atomic context!");
> >>>> +       return false;
> >>>> +}
> >>>> +
> >>>> +static void unlock_bus(struct i2c_adapter *i2c, unsigned int flags)
> >>>> +{
> >>>> +       struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c);
> >>>> +
> >>>> +       if (!smu_v11_0_i2c_bus_unlock(i2c)) {
> >>>> +               DRM_ERROR("Failed to unlock the bus from SMU");
> >>>> +               return;
> >>>> +       }
> >>>> +
> >>>> +       control->bus_locked = false;
> >>>> +}
> >>>> +
> >>>> +static const struct i2c_lock_operations smu_v11_0_i2c_i2c_lock_ops = {
> >>>> +       .lock_bus = lock_bus,
> >>>> +       .trylock_bus = trylock_bus,
> >>>> +       .unlock_bus = unlock_bus,
> >>>> +};
> >>>> +
> >>>> +static int smu_v11_0_i2c_eeprom_i2c_xfer(struct i2c_adapter *i2c_adap,
> >>>> +                             struct i2c_msg *msgs, int num)
> >>>> +{
> >>>> +       int i, ret;
> >>>> +       struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c_adap);
> >>>> +
> >>>> +       if (!control->bus_locked) {
> >>>> +               DRM_ERROR("I2C bus unlocked, stopping transaction!");
> >>>> +               return -EIO;
> >>>> +       }
> >>>> +
> >>>> +       smu_v11_0_i2c_init(i2c_adap);
> >>>> +
> >>>> +       for (i = 0; i < num; i++) {
> >>>> +               if (msgs[i].flags & I2C_M_RD)
> >>>> +                       ret = smu_v11_0_i2c_eeprom_read_data(i2c_adap,
> >>>> +                                                       (uint8_t)msgs[i].addr,
> >>>> +                                                       msgs[i].buf, msgs[i].len);
> >>>> +               else
> >>>> +                       ret = smu_v11_0_i2c_eeprom_write_data(i2c_adap,
> >>>> +                                                        (uint8_t)msgs[i].addr,
> >>>> +                                                        msgs[i].buf, msgs[i].len);
> >>>> +
> >>>> +               if (ret != I2C_OK) {
> >>>> +                       num = -EIO;
> >>>> +                       break;
> >>>> +               }
> >>>> +       }
> >>>> +
> >>>> +       smu_v11_0_i2c_fini(i2c_adap);
> >>>> +       return num;
> >>>> +}
> >>>> +
> >>>> +static u32 smu_v11_0_i2c_eeprom_i2c_func(struct i2c_adapter *adap)
> >>>> +{
> >>>> +       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> >>>> +}
> >>>> +
> >>>> +
> >>>> +static const struct i2c_algorithm smu_v11_0_i2c_eeprom_i2c_algo = {
> >>>> +       .master_xfer = smu_v11_0_i2c_eeprom_i2c_xfer,
> >>>> +       .functionality = smu_v11_0_i2c_eeprom_i2c_func,
> >>>> +};
> >>>> +
> >>>> +int smu_v11_0_i2c_eeprom_control_init(struct i2c_adapter *control)
> >>>> +{
> >>>> +       struct amdgpu_device *adev = to_amdgpu_device(control);
> >>>> +       int res;
> >>>> +
> >>>> +       control->owner = THIS_MODULE;
> >>>> +       control->class = I2C_CLASS_SPD;
> >>>> +       control->dev.parent = &adev->pdev->dev;
> >>>> +       control->algo = &smu_v11_0_i2c_eeprom_i2c_algo;
> >>>> +       snprintf(control->name, sizeof(control->name), "RAS EEPROM");
> >>>> +       control->lock_ops = &smu_v11_0_i2c_i2c_lock_ops;
> >>>> +
> >>>> +       res = i2c_add_adapter(control);
> >>>> +       if (res)
> >>>> +               DRM_ERROR("Failed to register hw i2c, err: %d\n", res);
> >>>> +
> >>>> +       return res;
> >>>> +}
> >>>> +
> >>>> +void smu_v11_0_i2c_eeprom_control_fini(struct i2c_adapter *control)
> >>>> +{
> >>>> +       i2c_del_adapter(control);
> >>>> +}
> >>>> +
> >>>> +#define I2C_TARGET_ADDR 0xA0
> >>>> +
> >>>> +bool smu_v11_0_i2c_test_bus(struct i2c_adapter *control)
> >>>> +{
> >>>> +
> >>>> +       uint32_t ret = I2C_OK;
> >>>> +       uint8_t data[6] = {0xf, 0, 0xde, 0xad, 0xbe, 0xef};
> >>>> +
> >>>> +
> >>>> +       DRM_INFO("Begin");
> >>>> +
> >>>> +       if (!smu_v11_0_i2c_bus_lock(control)) {
> >>>> +               DRM_ERROR("Failed to lock the bus!.");
> >>>> +               return false;
> >>>> +       }
> >>>> +
> >>>> +       smu_v11_0_i2c_init(control);
> >>>> +
> >>>> +       /* Write 0xde to address 0x0000 on the EEPROM */
> >>>> +       ret = smu_v11_0_i2c_eeprom_write_data(control, I2C_TARGET_ADDR, data, 6);
> >>>> +
> >>>> +       ret = smu_v11_0_i2c_eeprom_read_data(control, I2C_TARGET_ADDR, data, 6);
> >>>> +
> >>>> +       smu_v11_0_i2c_fini(control);
> >>>> +
> >>>> +       smu_v11_0_i2c_bus_unlock(control);
> >>>> +
> >>>> +
> >>>> +       DRM_INFO("End");
> >>>> +       return true;
> >>>> +}
> >>> Leftover debugging function?
>
>
> Missed that one, yes. Should I remove it ? Could be useful at some point
> for future bugs debugging...

Probably.  If you think it's useful, you can comment it out.

Alex

>
> Andrey
>
>
> >>>
> >>>> diff --git a/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
> >>>> new file mode 100644
> >>>> index 0000000..973f28d
> >>>> --- /dev/null
> >>>> +++ b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
> >>>> @@ -0,0 +1,34 @@
> >>>> +/*
> >>>> + * 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.
> >>>> + *
> >>>> + */
> >>>> +
> >>>> +#ifndef SMU_V11_I2C_CONTROL_H
> >>>> +#define SMU_V11_I2C_CONTROL_H
> >>>> +
> >>>> +#include <linux/types.h>
> >>>> +
> >>>> +struct i2c_adapter;
> >>>> +
> >>>> +int smu_v11_0_i2c_eeprom_control_init(struct i2c_adapter *control);
> >>>> +void smu_v11_0_i2c_eeprom_control_fini(struct i2c_adapter *control);
> >>>> +
> >>>> +#endif
> >>>> --
> >>>> 2.7.4
> >>>>
> >>>> _______________________________________________
> >>>> amd-gfx mailing list
> >>>> amd-gfx@lists.freedesktop.org
> >>>> https://lists.freedesktop.org/mailman/listinfo/amd-gfx
_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply	[flat|nested] 18+ messages in thread

* RE: [PATCH v4 1/4] drm/amdgpu: Add RAS EEPROM table.
       [not found]             ` <ce52910d-0d1f-402c-7cd0-a8a283a2cda8-5C7GfCeVMHo@public.gmane.org>
@ 2019-08-23  2:16               ` Zhou1, Tao
       [not found]                 ` <MN2PR12MB305437611047FDE19ACC251AB0A40-rweVpJHSKTqnT25eLM+iUQdYzm3356FpvxpqHgZTriW3zl9H0oFU5g@public.gmane.org>
  0 siblings, 1 reply; 18+ messages in thread
From: Zhou1, Tao @ 2019-08-23  2:16 UTC (permalink / raw)
  To: Grodzovsky, Andrey, amd-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: Panariti, David, Lazar, Lijo, Pan, Xinhui, Tuikov, Luben,
	Russell, Kent, Deucher, Alexander, Quan, Evan, Zhang, Hawking



From: Grodzovsky, Andrey <Andrey.Grodzovsky@amd.com> 
Sent: 2019年8月22日 23:07
To: Zhou1, Tao <Tao.Zhou1@amd.com>; amd-gfx@lists.freedesktop.org
Cc: Deucher, Alexander <Alexander.Deucher@amd.com>; Pan, Xinhui <Xinhui.Pan@amd.com>; Zhang, Hawking <Hawking.Zhang@amd.com>; Tuikov, Luben <Luben.Tuikov@amd.com>; Lazar, Lijo <Lijo.Lazar@amd.com>; Quan, Evan <Evan.Quan@amd.com>; Panariti, David <David.Panariti@amd.com>; Russell, Kent <Kent.Russell@amd.com>
Subject: Re: [PATCH v4 1/4] drm/amdgpu: Add RAS EEPROM table.


On 8/22/19 12:13 AM, Zhou1, Tao wrote:


-----Original Message-----
From: Andrey Grodzovsky mailto:andrey.grodzovsky@amd.com
Sent: 2019年8月22日 4:02
To: mailto:amd-gfx@lists.freedesktop.org
Cc: Deucher, Alexander mailto:Alexander.Deucher@amd.com; Pan, Xinhui
mailto:Xinhui.Pan@amd.com; Zhang, Hawking mailto:Hawking.Zhang@amd.com;
Tuikov, Luben mailto:Luben.Tuikov@amd.com; Lazar, Lijo mailto:Lijo.Lazar@amd.com;
Quan, Evan mailto:Evan.Quan@amd.com; Panariti, David
mailto:David.Panariti@amd.com; Russell, Kent mailto:Kent.Russell@amd.com; Zhou1,
Tao mailto:Tao.Zhou1@amd.com; Grodzovsky, Andrey
mailto:Andrey.Grodzovsky@amd.com
Subject: [PATCH v4 1/4] drm/amdgpu: Add RAS EEPROM table.

Add RAS EEPROM table manager to eanble RAS errors to be stored upon
appearance and retrived on driver load.

v2: Fix some prints.

v3:
Fix checksum calculation.
Make table record and header structs packed to do correct byte value sum.
Fix record crossing EEPROM page boundry.

v4:
Fix byte sum val calculation for record - look at sizeof(record).
Fix some style comments.

Signed-off-by: Andrey Grodzovsky mailto:andrey.grodzovsky@amd.com
---
 drivers/gpu/drm/amd/amdgpu/Makefile            |   2 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h        |   3 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c | 482
+++++++++++++++++++++++++
drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h |  90 +++++
 4 files changed, 576 insertions(+), 1 deletion(-)  create mode 100644
drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
 create mode 100644
drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h

diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile
b/drivers/gpu/drm/amd/amdgpu/Makefile
index 28d76bd..f016cf1 100644
--- a/drivers/gpu/drm/amd/amdgpu/Makefile
+++ b/drivers/gpu/drm/amd/amdgpu/Makefile
@@ -54,7 +54,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
 	amdgpu_gtt_mgr.o amdgpu_vram_mgr.o amdgpu_virt.o
amdgpu_atomfirmware.o \
 	amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o
amdgpu_ids.o \
 	amdgpu_gmc.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o
amdgpu_vm_cpu.o \
-	amdgpu_vm_sdma.o amdgpu_discovery.o
+	amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o
amdgpu_ras_eeprom.o

 amdgpu-$(CONFIG_PERF_EVENTS) += amdgpu_pmu.o

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
index 2765f2d..8d5bcd8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
@@ -29,6 +29,7 @@
 #include "amdgpu.h"
 #include "amdgpu_psp.h"
 #include "ta_ras_if.h"
+#include "amdgpu_ras_eeprom.h"

 enum amdgpu_ras_block {
 	AMDGPU_RAS_BLOCK__UMC = 0,
@@ -333,6 +334,8 @@ struct amdgpu_ras {
 	struct mutex recovery_lock;

 	uint32_t flags;
+
+	struct amdgpu_ras_eeprom_control eeprom_control;
 };

 struct ras_fs_data {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
new file mode 100644
index 0000000..bf07515
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
@@ -0,0 +1,482 @@
+/*
+ * 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>
+
+#define EEPROM_I2C_TARGET_ADDR 0xA0
+
+#define EEPROM_TABLE_HEADER_SIZE 20
+#define EEPROM_TABLE_RECORD_SIZE 24
[Tao] should we replace fixed value with sizeof for the two macros?

No, as I already explained before the EEPROM_TABLE_HEADER/RECORD_SIZE represent the actual size in bytes that those entities occupy in the EEPROM memory and as defined in the EEPROM table description while struct eeprom_table_record would be larger because for example to store 6b of RetiedPage I have to use uint64 which is 8b. I could of course use some kind of bitvector (bitset) to store them compactly but it seems to me as an overkill. I was told we are not expecting to much records for any given system anyway during it's life time so the dynamic memory saved would be small anyway. 

[Tao] OK, could you add a comment in the code to clarify the two types of SIZE?



+#define EEPROM_ADDRESS_SIZE 0x2
+
+/* Table hdr is 'AMDR' */
+#define EEPROM_TABLE_HDR_VAL 0x414d4452 #define
EEPROM_TABLE_VER
+0x00010000
+
+/* Assume 2 Mbit size */
+#define EEPROM_SIZE_BYTES 256000
+#define EEPROM_PAGE__SIZE_BYTES 256
+#define EEPROM_HDR_START 0
+#define EEPROM_RECORD_START (EEPROM_HDR_START +
+EEPROM_TABLE_HEADER_SIZE) #define EEPROM_MAX_RECORD_NUM
+((EEPROM_SIZE_BYTES - EEPROM_TABLE_HEADER_SIZE) /
+EEPROM_TABLE_RECORD_SIZE) #define EEPROM_ADDR_MSB_MASK
GENMASK(17, 8)
+
+#define to_amdgpu_device(x) (container_of(x, struct amdgpu_ras,
+eeprom_control))->adev
+
+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;
+
+	hdr->header 	      = le32_to_cpu(pp[0]);
+	hdr->version 	      = le32_to_cpu(pp[1]);
+	hdr->first_rec_offset = le32_to_cpu(pp[2]);
+	hdr->tbl_size 	      = le32_to_cpu(pp[3]);
+	hdr->checksum 	      = le32_to_cpu(pp[4]);
+}
+
+static int __update_table_header(struct amdgpu_ras_eeprom_control
*control,
+				 unsigned char *buff)
+{
+	int ret = 0;
+	struct i2c_msg msg = {
+			.addr	= EEPROM_I2C_TARGET_ADDR,
+			.flags	= 0,
+			.len	= EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_HEADER_SIZE,
+			.buf	= buff,
+	};
+
+
+	*(uint16_t *)buff = EEPROM_HDR_START;
+	__encode_table_header_to_buff(&control->tbl_hdr, buff +
+EEPROM_ADDRESS_SIZE);
+
+	ret = i2c_transfer(&control->eeprom_accessor, &msg, 1);
+	if (ret < 1)
+		DRM_ERROR("Failed to write EEPROM table header, ret:%d",
ret);
+
+	return ret;
+}
+
+static uint32_t  __calc_hdr_byte_sum(struct amdgpu_ras_eeprom_control
+*control);
+
+int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
{
+	int ret = 0;
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+	unsigned char buff[EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_HEADER_SIZE] = { 0 };
+	struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr;
+	struct i2c_msg msg = {
+			.addr	= EEPROM_I2C_TARGET_ADDR,
+			.flags	= I2C_M_RD,
+			.len	= EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_HEADER_SIZE,
+			.buf	= buff,
+	};
+
+	mutex_init(&control->tbl_mutex);
+
+	switch (adev->asic_type) {
+	case CHIP_VEGA20:
+	/*TODO Add MI-60 */
+		break;
+
+	default:
+		return 0;
+	}
+
+	if (ret) {
+		DRM_ERROR("Failed to init I2C controller, ret:%d", ret);
+		return ret;
+	}
+
+	/* Read/Create table header from EEPROM address 0 */
+	ret = i2c_transfer(&control->eeprom_accessor, &msg, 1);
+	if (ret < 1) {
+		DRM_ERROR("Failed to read EEPROM table header, ret:%d",
ret);
+		return ret;
+	}
+
+	__decode_table_header_from_buff(hdr, &buff[2]);
+
+	if (hdr->header == EEPROM_TABLE_HDR_VAL) {
+		control->num_recs = (hdr->tbl_size -
EEPROM_TABLE_HEADER_SIZE) /
+				    EEPROM_TABLE_RECORD_SIZE;
+		DRM_DEBUG_DRIVER("Found existing EEPROM table with %d
records",
+				 control->num_recs);
+
+	} else {
+		DRM_INFO("Creating new EEPROM table");
+
+		hdr->header = EEPROM_TABLE_HDR_VAL;
+		hdr->version = EEPROM_TABLE_VER;
+		hdr->first_rec_offset = EEPROM_RECORD_START;
+		hdr->tbl_size = EEPROM_TABLE_HEADER_SIZE;
+
+		adev->psp.ras.ras->eeprom_control.tbl_byte_sum =
+				__calc_hdr_byte_sum(&adev->psp.ras.ras-
eeprom_control);
+		ret = __update_table_header(control, buff);
+	}
+
+	/* Start inserting records from here */
+	adev->psp.ras.ras->eeprom_control.next_addr =
EEPROM_RECORD_START;
+
+	return ret == 1 ? 0 : -EIO;
+}
+
+void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control
*control)
+{
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+
+	switch (adev->asic_type) {
+	case CHIP_VEGA20:
+		/*TODO Add MI-60 */
+		break;
+
+	default:
+		return;
+	}
+}
+
+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;
[Tao] I think sizeof(record->ts) is better

+
+	tmp = cpu_to_le64((record->offset & 0xffffffffffff));
[Tao] (0x1ULL << 49 - 1) is more readable than & 0xffffffffffff, or a macro can be defined, but either way is OK.

+	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;
+
+	buff[i++] = record->mem_channel;
+	buff[i++] = record->mcumc_id;
+
+	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
+ * When next record access will go beyond EEPROM page boundary modify
+bits A17/A8
+ * in I2C selector to go to next page
+ */
+static uint32_t __correct_eeprom_dest_address(uint32_t curr_address) {
+	uint32_t next_address = curr_address +
EEPROM_TABLE_RECORD_SIZE;
+
+	/* When all EEPROM memory used jump back to 0 address */
+	if (next_address > EEPROM_SIZE_BYTES) {
+		DRM_INFO("Reached end of EEPROM memory, jumping to 0
"
+			 "and overriding old record");
+		return EEPROM_RECORD_START;
+	}
+
+	/*
+	 * To check if we overflow page boundary  compare next address
with
+	 * current and see if bits 17/8 of the EEPROM address will change
+	 * If they do start from the next 256b page
+	 *
+	 * https://www.st.com/resource/en/datasheet/m24m02-dr.pdf sec.
5.1.2
+	 */
+	if ((curr_address & EEPROM_ADDR_MSB_MASK) != (next_address &
EEPROM_ADDR_MSB_MASK)) {
+		DRM_DEBUG_DRIVER("Reached end of EEPROM memory
page, jumpimng to next: %lx",
+				(next_address &
EEPROM_ADDR_MSB_MASK));
+
+		return  (next_address & EEPROM_ADDR_MSB_MASK);
+	}
+
+	return curr_address;
+}
+
+
+static uint32_t  __calc_hdr_byte_sum(struct amdgpu_ras_eeprom_control
+*control) {
+	int i;
+	uint32_t tbl_sum = 0;
+
+	/* Header checksum, skip checksum field in the calculation */
+	for (i = 0; i < sizeof(control->tbl_hdr) - sizeof(control-
tbl_hdr.checksum); i++)
+		tbl_sum += *(((unsigned char *)&control->tbl_hdr) + i);
+
+	return tbl_sum;
+}
+
+static uint32_t  __calc_recs_byte_sum(struct eeprom_table_record *records,
+				      int num)
+{
+	int i, j;
+	uint32_t tbl_sum = 0;
+
+	/* Records checksum */
+	for (i = 0; i < num; i++) {
+		struct eeprom_table_record *record = &records[i];
+
+		for (j = 0; j < sizeof(*record); j++) {
+			tbl_sum += *(((unsigned char *)record) + j);
+		}
+	}
+
+	return tbl_sum;
+}
+
+static inline uint32_t  __calc_tbl_byte_sum(struct
amdgpu_ras_eeprom_control *control,
+				  struct eeprom_table_record *records, int
num) {
+	return __calc_hdr_byte_sum(control) +
__calc_recs_byte_sum(records,
+num); }
+
+/* Checksum = 256 -((sum of all table entries) mod 256) */ static void
+__update_tbl_checksum(struct amdgpu_ras_eeprom_control *control,
+				  struct eeprom_table_record *records, int
num,
+				  uint32_t old_hdr_byte_sum)
+{
+	/*
+	 * This will update the table sum with new records.
+	 *
+	 * TODO: What happens when the EEPROM table is to be wrapped
around
+	 * and old records from start will get overridden.
+	 */
+
+	/* need to recalculate updated header byte sum */
+	control->tbl_byte_sum -= old_hdr_byte_sum;
+	control->tbl_byte_sum += __calc_tbl_byte_sum(control, records,
num);
+
+	control->tbl_hdr.checksum = 256 - (control->tbl_byte_sum % 256); }
[Tao] we can change 256 to EEPROM_PAGE__SIZE_BYTES

This 256 just accidentally has same value as  EEPROM_PAGE__SIZE_BYTES but doesn't relate in any way, it's part of byte value checksum algorithm I was given.
Andrey



+
+/* table sum mod 256 + checksum must equals 256 */ static bool
+__validate_tbl_checksum(struct amdgpu_ras_eeprom_control *control,
+			    struct eeprom_table_record *records, int num) {
+	control->tbl_byte_sum = __calc_tbl_byte_sum(control, records, num);
+
+	if (control->tbl_hdr.checksum + (control->tbl_byte_sum % 256) != 256)
{
+		DRM_WARN("Checksum mismatch, checksum: %u ", control-
tbl_hdr.checksum);
+		return false;
+	}
+
+	return true;
+}
+
+int amdgpu_ras_eeprom_process_recods(struct
amdgpu_ras_eeprom_control *control,
+					    struct eeprom_table_record
*records,
+					    bool write,
+					    int num)
+{
+	int i, ret = 0;
+	struct i2c_msg *msgs;
+	unsigned char *buffs;
+	struct amdgpu_device *adev = to_amdgpu_device(control);
+
+	if (adev->asic_type != CHIP_VEGA20)
+		return 0;
+
+	buffs = kcalloc(num, EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_RECORD_SIZE,
+			 GFP_KERNEL);
+	if (!buffs)
+		return -ENOMEM;
+
+	mutex_lock(&control->tbl_mutex);
+
+	msgs = kcalloc(num, sizeof(*msgs), GFP_KERNEL);
+	if (!msgs) {
+		ret = -ENOMEM;
+		goto free_buff;
+	}
+
+	/* In case of overflow just start from beginning to not lose newest
records */
+	if (write && (control->next_addr + EEPROM_TABLE_RECORD_SIZE *
num > EEPROM_SIZE_BYTES))
+		control->next_addr = EEPROM_RECORD_START;
+
+
+	/*
+	 * 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++) {
+		unsigned char *buff = &buffs[i*(EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_RECORD_SIZE)];
+		struct eeprom_table_record *record = &records[i];
+		struct i2c_msg *msg = &msgs[i];
+
+		control->next_addr =
+__correct_eeprom_dest_address(control->next_addr);
+
+		/*
+		 * Update bits 16,17 of EEPROM address in I2C address by
setting them
+		 * to bits 1,2 of Device address byte
+		 */
+		msg->addr = EEPROM_I2C_TARGET_ADDR |
+			       ((control->next_addr &
EEPROM_ADDR_MSB_MASK) >> 15);
+		msg->flags	= write ? 0 : I2C_M_RD;
+		msg->len	= EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_RECORD_SIZE;
+		msg->buf	= buff;
+
+		/* Insert the EEPROM dest addess, bits 0-15 */
+		buff[0] = ((control->next_addr >> 8) & 0xff);
+		buff[1] = (control->next_addr & 0xff);
+
+		/* EEPROM table content is stored in LE format */
+		if (write)
+			__encode_table_record_to_buff(control, record, buff
+
+EEPROM_ADDRESS_SIZE);
+
+		/*
+		 * The destination EEPROM address might need to be
corrected to account
+		 * for page or entire memory wrapping
+		 */
+		control->next_addr += EEPROM_TABLE_RECORD_SIZE;
+	}
+
+	ret = i2c_transfer(&control->eeprom_accessor, msgs, num);
+	if (ret < 1) {
+		DRM_ERROR("Failed to process EEPROM table records,
ret:%d", ret);
+
+		/* TODO Restore prev next EEPROM address ? */
+		goto free_msgs;
+	}
+
+
+	if (!write) {
+		for (i = 0; i < num; i++) {
+			unsigned char *buff =
&buffs[i*(EEPROM_ADDRESS_SIZE + EEPROM_TABLE_RECORD_SIZE)];
[Tao] space is needed before and after "*"

+			struct eeprom_table_record *record = &records[i];
[Tao] add a space after "*"

+
+			__decode_table_record_from_buff(control, record,
buff + EEPROM_ADDRESS_SIZE);
+		}
+	}
+
+	if (write) {
+		uint32_t old_hdr_byte_sum = __calc_hdr_byte_sum(control);
+
+		/*
+		 * 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;
+		control->num_recs %= EEPROM_MAX_RECORD_NUM;
+		control->tbl_hdr.tbl_size += EEPROM_TABLE_RECORD_SIZE *
num;
+		if (control->tbl_hdr.tbl_size > EEPROM_SIZE_BYTES)
+			control->tbl_hdr.tbl_size =
EEPROM_TABLE_HEADER_SIZE +
+			control->num_recs * EEPROM_TABLE_RECORD_SIZE;
+
+		__update_tbl_checksum(control, records, num,
old_hdr_byte_sum);
+
+		__update_table_header(control, buffs);
+	} else if (!__validate_tbl_checksum(control, records, num)) {
+		DRM_WARN("EEPROM Table checksum mismatch!");
+		/* TODO Uncomment when EEPROM read/write is relliable
*/
+		/* ret = -EIO; */
+	}
+
+free_msgs:
+	kfree(msgs);
+
+free_buff:
+	kfree(buffs);
+
+	mutex_unlock(&control->tbl_mutex);
+
+	return ret == num ? 0 : -EIO;
+}
+
+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;
+	}
+
+	if (!amdgpu_ras_eeprom_process_recods(control, recs, true, 1)) {
+
+		memset(recs, 0, sizeof(*recs) * 1);
+
+		control->next_addr = EEPROM_RECORD_START;
+
+		if (!amdgpu_ras_eeprom_process_recods(control, recs, false,
1)) {
+			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"); }
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
new file mode 100644
index 0000000..41f3fcb
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef _AMDGPU_RAS_EEPROM_H
+#define _AMDGPU_RAS_EEPROM_H
+
+#include <linux/i2c.h>
+
+struct amdgpu_device;
+
+enum amdgpu_ras_eeprom_err_type{
+	AMDGPU_RAS_EEPROM_ERR_PLACE_HOLDER,
+	AMDGPU_RAS_EEPROM_ERR_RECOVERABLE,
+	AMDGPU_RAS_EEPROM_ERR_NON_RECOVERABLE
+};
+
+struct amdgpu_ras_eeprom_table_header {
+	uint32_t header;
+	uint32_t version;
+	uint32_t first_rec_offset;
+	uint32_t tbl_size;
+	uint32_t checksum;
+}__attribute__((__packed__));
+
+struct amdgpu_ras_eeprom_control {
+	struct amdgpu_ras_eeprom_table_header tbl_hdr;
+	struct i2c_adapter eeprom_accessor;
+	uint32_t next_addr;
+	unsigned int num_recs;
+	struct mutex tbl_mutex;
+	bool bus_locked;
+	uint32_t tbl_byte_sum;
+};
+
+/*
+ * Represents single table record. Packed to be easily serialized into
+byte
+ * stream.
+ */
+struct eeprom_table_record {
+
+	union {
+		uint64_t address;
+		uint64_t offset;
+	};
+
+	uint64_t retired_page;
+	uint64_t ts;
+
+	enum amdgpu_ras_eeprom_err_type err_type;
+
+	union {
+		unsigned char bank;
+		unsigned char cu;
+	};
+
+	unsigned char mem_channel;
+	unsigned char mcumc_id;
+}__attribute__((__packed__));
+
+int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control);
+void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control
*control);
+
+int amdgpu_ras_eeprom_process_recods(struct
amdgpu_ras_eeprom_control *control,
+					    struct eeprom_table_record
*records,
+					    bool write,
+					    int num);
+
+void amdgpu_ras_eeprom_test(struct amdgpu_ras_eeprom_control
*control);
+
+#endif // _AMDGPU_RAS_EEPROM_H
--
2.7.4

_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 1/4] drm/amdgpu: Add RAS EEPROM table.
       [not found]                 ` <MN2PR12MB305437611047FDE19ACC251AB0A40-rweVpJHSKTqnT25eLM+iUQdYzm3356FpvxpqHgZTriW3zl9H0oFU5g@public.gmane.org>
@ 2019-08-23  2:47                   ` Grodzovsky, Andrey
  0 siblings, 0 replies; 18+ messages in thread
From: Grodzovsky, Andrey @ 2019-08-23  2:47 UTC (permalink / raw)
  To: Zhou1, Tao, amd-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: Panariti, David, Lazar, Lijo, Pan, Xinhui, Tuikov, Luben,
	Russell, Kent, Deucher, Alexander, Quan, Evan, Zhang, Hawking

Sure, will add clarification comment.

Andrey

________________________________________
From: Zhou1, Tao <Tao.Zhou1@amd.com>
Sent: 22 August 2019 22:16:56
To: Grodzovsky, Andrey; amd-gfx@lists.freedesktop.org
Cc: Deucher, Alexander; Pan, Xinhui; Zhang, Hawking; Tuikov, Luben; Lazar, Lijo; Quan, Evan; Panariti, David; Russell, Kent
Subject: RE: [PATCH v4 1/4] drm/amdgpu: Add RAS EEPROM table.



From: Grodzovsky, Andrey <Andrey.Grodzovsky@amd.com>
Sent: 2019年8月22日 23:07
To: Zhou1, Tao <Tao.Zhou1@amd.com>; amd-gfx@lists.freedesktop.org
Cc: Deucher, Alexander <Alexander.Deucher@amd.com>; Pan, Xinhui <Xinhui.Pan@amd.com>; Zhang, Hawking <Hawking.Zhang@amd.com>; Tuikov, Luben <Luben.Tuikov@amd.com>; Lazar, Lijo <Lijo.Lazar@amd.com>; Quan, Evan <Evan.Quan@amd.com>; Panariti, David <David.Panariti@amd.com>; Russell, Kent <Kent.Russell@amd.com>
Subject: Re: [PATCH v4 1/4] drm/amdgpu: Add RAS EEPROM table.


On 8/22/19 12:13 AM, Zhou1, Tao wrote:


-----Original Message-----
From: Andrey Grodzovsky mailto:andrey.grodzovsky@amd.com
Sent: 2019年8月22日 4:02
To: mailto:amd-gfx@lists.freedesktop.org
Cc: Deucher, Alexander mailto:Alexander.Deucher@amd.com; Pan, Xinhui
mailto:Xinhui.Pan@amd.com; Zhang, Hawking mailto:Hawking.Zhang@amd.com;
Tuikov, Luben mailto:Luben.Tuikov@amd.com; Lazar, Lijo mailto:Lijo.Lazar@amd.com;
Quan, Evan mailto:Evan.Quan@amd.com; Panariti, David
mailto:David.Panariti@amd.com; Russell, Kent mailto:Kent.Russell@amd.com; Zhou1,
Tao mailto:Tao.Zhou1@amd.com; Grodzovsky, Andrey
mailto:Andrey.Grodzovsky@amd.com
Subject: [PATCH v4 1/4] drm/amdgpu: Add RAS EEPROM table.

Add RAS EEPROM table manager to eanble RAS errors to be stored upon
appearance and retrived on driver load.

v2: Fix some prints.

v3:
Fix checksum calculation.
Make table record and header structs packed to do correct byte value sum.
Fix record crossing EEPROM page boundry.

v4:
Fix byte sum val calculation for record - look at sizeof(record).
Fix some style comments.

Signed-off-by: Andrey Grodzovsky mailto:andrey.grodzovsky@amd.com
---
 drivers/gpu/drm/amd/amdgpu/Makefile            |   2 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h        |   3 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c | 482
+++++++++++++++++++++++++
drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h |  90 +++++
 4 files changed, 576 insertions(+), 1 deletion(-)  create mode 100644
drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
 create mode 100644
drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h

diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile
b/drivers/gpu/drm/amd/amdgpu/Makefile
index 28d76bd..f016cf1 100644
--- a/drivers/gpu/drm/amd/amdgpu/Makefile
+++ b/drivers/gpu/drm/amd/amdgpu/Makefile
@@ -54,7 +54,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
        amdgpu_gtt_mgr.o amdgpu_vram_mgr.o amdgpu_virt.o
amdgpu_atomfirmware.o \
        amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o
amdgpu_ids.o \
        amdgpu_gmc.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o
amdgpu_vm_cpu.o \
-       amdgpu_vm_sdma.o amdgpu_discovery.o
+       amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o
amdgpu_ras_eeprom.o

 amdgpu-$(CONFIG_PERF_EVENTS) += amdgpu_pmu.o

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
index 2765f2d..8d5bcd8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
@@ -29,6 +29,7 @@
 #include "amdgpu.h"
 #include "amdgpu_psp.h"
 #include "ta_ras_if.h"
+#include "amdgpu_ras_eeprom.h"

 enum amdgpu_ras_block {
        AMDGPU_RAS_BLOCK__UMC = 0,
@@ -333,6 +334,8 @@ struct amdgpu_ras {
        struct mutex recovery_lock;

        uint32_t flags;
+
+       struct amdgpu_ras_eeprom_control eeprom_control;
 };

 struct ras_fs_data {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
new file mode 100644
index 0000000..bf07515
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
@@ -0,0 +1,482 @@
+/*
+ * 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>
+
+#define EEPROM_I2C_TARGET_ADDR 0xA0
+
+#define EEPROM_TABLE_HEADER_SIZE 20
+#define EEPROM_TABLE_RECORD_SIZE 24
[Tao] should we replace fixed value with sizeof for the two macros?

No, as I already explained before the EEPROM_TABLE_HEADER/RECORD_SIZE represent the actual size in bytes that those entities occupy in the EEPROM memory and as defined in the EEPROM table description while struct eeprom_table_record would be larger because for example to store 6b of RetiedPage I have to use uint64 which is 8b. I could of course use some kind of bitvector (bitset) to store them compactly but it seems to me as an overkill. I was told we are not expecting to much records for any given system anyway during it's life time so the dynamic memory saved would be small anyway.

[Tao] OK, could you add a comment in the code to clarify the two types of SIZE?



+#define EEPROM_ADDRESS_SIZE 0x2
+
+/* Table hdr is 'AMDR' */
+#define EEPROM_TABLE_HDR_VAL 0x414d4452 #define
EEPROM_TABLE_VER
+0x00010000
+
+/* Assume 2 Mbit size */
+#define EEPROM_SIZE_BYTES 256000
+#define EEPROM_PAGE__SIZE_BYTES 256
+#define EEPROM_HDR_START 0
+#define EEPROM_RECORD_START (EEPROM_HDR_START +
+EEPROM_TABLE_HEADER_SIZE) #define EEPROM_MAX_RECORD_NUM
+((EEPROM_SIZE_BYTES - EEPROM_TABLE_HEADER_SIZE) /
+EEPROM_TABLE_RECORD_SIZE) #define EEPROM_ADDR_MSB_MASK
GENMASK(17, 8)
+
+#define to_amdgpu_device(x) (container_of(x, struct amdgpu_ras,
+eeprom_control))->adev
+
+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;
+
+       hdr->header           = le32_to_cpu(pp[0]);
+       hdr->version          = le32_to_cpu(pp[1]);
+       hdr->first_rec_offset = le32_to_cpu(pp[2]);
+       hdr->tbl_size         = le32_to_cpu(pp[3]);
+       hdr->checksum         = le32_to_cpu(pp[4]);
+}
+
+static int __update_table_header(struct amdgpu_ras_eeprom_control
*control,
+                                unsigned char *buff)
+{
+       int ret = 0;
+       struct i2c_msg msg = {
+                       .addr   = EEPROM_I2C_TARGET_ADDR,
+                       .flags  = 0,
+                       .len    = EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_HEADER_SIZE,
+                       .buf    = buff,
+       };
+
+
+       *(uint16_t *)buff = EEPROM_HDR_START;
+       __encode_table_header_to_buff(&control->tbl_hdr, buff +
+EEPROM_ADDRESS_SIZE);
+
+       ret = i2c_transfer(&control->eeprom_accessor, &msg, 1);
+       if (ret < 1)
+               DRM_ERROR("Failed to write EEPROM table header, ret:%d",
ret);
+
+       return ret;
+}
+
+static uint32_t  __calc_hdr_byte_sum(struct amdgpu_ras_eeprom_control
+*control);
+
+int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
{
+       int ret = 0;
+       struct amdgpu_device *adev = to_amdgpu_device(control);
+       unsigned char buff[EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_HEADER_SIZE] = { 0 };
+       struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr;
+       struct i2c_msg msg = {
+                       .addr   = EEPROM_I2C_TARGET_ADDR,
+                       .flags  = I2C_M_RD,
+                       .len    = EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_HEADER_SIZE,
+                       .buf    = buff,
+       };
+
+       mutex_init(&control->tbl_mutex);
+
+       switch (adev->asic_type) {
+       case CHIP_VEGA20:
+       /*TODO Add MI-60 */
+               break;
+
+       default:
+               return 0;
+       }
+
+       if (ret) {
+               DRM_ERROR("Failed to init I2C controller, ret:%d", ret);
+               return ret;
+       }
+
+       /* Read/Create table header from EEPROM address 0 */
+       ret = i2c_transfer(&control->eeprom_accessor, &msg, 1);
+       if (ret < 1) {
+               DRM_ERROR("Failed to read EEPROM table header, ret:%d",
ret);
+               return ret;
+       }
+
+       __decode_table_header_from_buff(hdr, &buff[2]);
+
+       if (hdr->header == EEPROM_TABLE_HDR_VAL) {
+               control->num_recs = (hdr->tbl_size -
EEPROM_TABLE_HEADER_SIZE) /
+                                   EEPROM_TABLE_RECORD_SIZE;
+               DRM_DEBUG_DRIVER("Found existing EEPROM table with %d
records",
+                                control->num_recs);
+
+       } else {
+               DRM_INFO("Creating new EEPROM table");
+
+               hdr->header = EEPROM_TABLE_HDR_VAL;
+               hdr->version = EEPROM_TABLE_VER;
+               hdr->first_rec_offset = EEPROM_RECORD_START;
+               hdr->tbl_size = EEPROM_TABLE_HEADER_SIZE;
+
+               adev->psp.ras.ras->eeprom_control.tbl_byte_sum =
+                               __calc_hdr_byte_sum(&adev->psp.ras.ras-
eeprom_control);
+               ret = __update_table_header(control, buff);
+       }
+
+       /* Start inserting records from here */
+       adev->psp.ras.ras->eeprom_control.next_addr =
EEPROM_RECORD_START;
+
+       return ret == 1 ? 0 : -EIO;
+}
+
+void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control
*control)
+{
+       struct amdgpu_device *adev = to_amdgpu_device(control);
+
+       switch (adev->asic_type) {
+       case CHIP_VEGA20:
+               /*TODO Add MI-60 */
+               break;
+
+       default:
+               return;
+       }
+}
+
+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;
[Tao] I think sizeof(record->ts) is better

+
+       tmp = cpu_to_le64((record->offset & 0xffffffffffff));
[Tao] (0x1ULL << 49 - 1) is more readable than & 0xffffffffffff, or a macro can be defined, but either way is OK.

+       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;
+
+       buff[i++] = record->mem_channel;
+       buff[i++] = record->mcumc_id;
+
+       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
+ * When next record access will go beyond EEPROM page boundary modify
+bits A17/A8
+ * in I2C selector to go to next page
+ */
+static uint32_t __correct_eeprom_dest_address(uint32_t curr_address) {
+       uint32_t next_address = curr_address +
EEPROM_TABLE_RECORD_SIZE;
+
+       /* When all EEPROM memory used jump back to 0 address */
+       if (next_address > EEPROM_SIZE_BYTES) {
+               DRM_INFO("Reached end of EEPROM memory, jumping to 0
"
+                        "and overriding old record");
+               return EEPROM_RECORD_START;
+       }
+
+       /*
+        * To check if we overflow page boundary  compare next address
with
+        * current and see if bits 17/8 of the EEPROM address will change
+        * If they do start from the next 256b page
+        *
+        * https://www.st.com/resource/en/datasheet/m24m02-dr.pdf sec.
5.1.2
+        */
+       if ((curr_address & EEPROM_ADDR_MSB_MASK) != (next_address &
EEPROM_ADDR_MSB_MASK)) {
+               DRM_DEBUG_DRIVER("Reached end of EEPROM memory
page, jumpimng to next: %lx",
+                               (next_address &
EEPROM_ADDR_MSB_MASK));
+
+               return  (next_address & EEPROM_ADDR_MSB_MASK);
+       }
+
+       return curr_address;
+}
+
+
+static uint32_t  __calc_hdr_byte_sum(struct amdgpu_ras_eeprom_control
+*control) {
+       int i;
+       uint32_t tbl_sum = 0;
+
+       /* Header checksum, skip checksum field in the calculation */
+       for (i = 0; i < sizeof(control->tbl_hdr) - sizeof(control-
tbl_hdr.checksum); i++)
+               tbl_sum += *(((unsigned char *)&control->tbl_hdr) + i);
+
+       return tbl_sum;
+}
+
+static uint32_t  __calc_recs_byte_sum(struct eeprom_table_record *records,
+                                     int num)
+{
+       int i, j;
+       uint32_t tbl_sum = 0;
+
+       /* Records checksum */
+       for (i = 0; i < num; i++) {
+               struct eeprom_table_record *record = &records[i];
+
+               for (j = 0; j < sizeof(*record); j++) {
+                       tbl_sum += *(((unsigned char *)record) + j);
+               }
+       }
+
+       return tbl_sum;
+}
+
+static inline uint32_t  __calc_tbl_byte_sum(struct
amdgpu_ras_eeprom_control *control,
+                                 struct eeprom_table_record *records, int
num) {
+       return __calc_hdr_byte_sum(control) +
__calc_recs_byte_sum(records,
+num); }
+
+/* Checksum = 256 -((sum of all table entries) mod 256) */ static void
+__update_tbl_checksum(struct amdgpu_ras_eeprom_control *control,
+                                 struct eeprom_table_record *records, int
num,
+                                 uint32_t old_hdr_byte_sum)
+{
+       /*
+        * This will update the table sum with new records.
+        *
+        * TODO: What happens when the EEPROM table is to be wrapped
around
+        * and old records from start will get overridden.
+        */
+
+       /* need to recalculate updated header byte sum */
+       control->tbl_byte_sum -= old_hdr_byte_sum;
+       control->tbl_byte_sum += __calc_tbl_byte_sum(control, records,
num);
+
+       control->tbl_hdr.checksum = 256 - (control->tbl_byte_sum % 256); }
[Tao] we can change 256 to EEPROM_PAGE__SIZE_BYTES

This 256 just accidentally has same value as  EEPROM_PAGE__SIZE_BYTES but doesn't relate in any way, it's part of byte value checksum algorithm I was given.
Andrey



+
+/* table sum mod 256 + checksum must equals 256 */ static bool
+__validate_tbl_checksum(struct amdgpu_ras_eeprom_control *control,
+                           struct eeprom_table_record *records, int num) {
+       control->tbl_byte_sum = __calc_tbl_byte_sum(control, records, num);
+
+       if (control->tbl_hdr.checksum + (control->tbl_byte_sum % 256) != 256)
{
+               DRM_WARN("Checksum mismatch, checksum: %u ", control-
tbl_hdr.checksum);
+               return false;
+       }
+
+       return true;
+}
+
+int amdgpu_ras_eeprom_process_recods(struct
amdgpu_ras_eeprom_control *control,
+                                           struct eeprom_table_record
*records,
+                                           bool write,
+                                           int num)
+{
+       int i, ret = 0;
+       struct i2c_msg *msgs;
+       unsigned char *buffs;
+       struct amdgpu_device *adev = to_amdgpu_device(control);
+
+       if (adev->asic_type != CHIP_VEGA20)
+               return 0;
+
+       buffs = kcalloc(num, EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_RECORD_SIZE,
+                        GFP_KERNEL);
+       if (!buffs)
+               return -ENOMEM;
+
+       mutex_lock(&control->tbl_mutex);
+
+       msgs = kcalloc(num, sizeof(*msgs), GFP_KERNEL);
+       if (!msgs) {
+               ret = -ENOMEM;
+               goto free_buff;
+       }
+
+       /* In case of overflow just start from beginning to not lose newest
records */
+       if (write && (control->next_addr + EEPROM_TABLE_RECORD_SIZE *
num > EEPROM_SIZE_BYTES))
+               control->next_addr = EEPROM_RECORD_START;
+
+
+       /*
+        * 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++) {
+               unsigned char *buff = &buffs[i*(EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_RECORD_SIZE)];
+               struct eeprom_table_record *record = &records[i];
+               struct i2c_msg *msg = &msgs[i];
+
+               control->next_addr =
+__correct_eeprom_dest_address(control->next_addr);
+
+               /*
+                * Update bits 16,17 of EEPROM address in I2C address by
setting them
+                * to bits 1,2 of Device address byte
+                */
+               msg->addr = EEPROM_I2C_TARGET_ADDR |
+                              ((control->next_addr &
EEPROM_ADDR_MSB_MASK) >> 15);
+               msg->flags      = write ? 0 : I2C_M_RD;
+               msg->len        = EEPROM_ADDRESS_SIZE +
EEPROM_TABLE_RECORD_SIZE;
+               msg->buf        = buff;
+
+               /* Insert the EEPROM dest addess, bits 0-15 */
+               buff[0] = ((control->next_addr >> 8) & 0xff);
+               buff[1] = (control->next_addr & 0xff);
+
+               /* EEPROM table content is stored in LE format */
+               if (write)
+                       __encode_table_record_to_buff(control, record, buff
+
+EEPROM_ADDRESS_SIZE);
+
+               /*
+                * The destination EEPROM address might need to be
corrected to account
+                * for page or entire memory wrapping
+                */
+               control->next_addr += EEPROM_TABLE_RECORD_SIZE;
+       }
+
+       ret = i2c_transfer(&control->eeprom_accessor, msgs, num);
+       if (ret < 1) {
+               DRM_ERROR("Failed to process EEPROM table records,
ret:%d", ret);
+
+               /* TODO Restore prev next EEPROM address ? */
+               goto free_msgs;
+       }
+
+
+       if (!write) {
+               for (i = 0; i < num; i++) {
+                       unsigned char *buff =
&buffs[i*(EEPROM_ADDRESS_SIZE + EEPROM_TABLE_RECORD_SIZE)];
[Tao] space is needed before and after "*"

+                       struct eeprom_table_record *record = &records[i];
[Tao] add a space after "*"

+
+                       __decode_table_record_from_buff(control, record,
buff + EEPROM_ADDRESS_SIZE);
+               }
+       }
+
+       if (write) {
+               uint32_t old_hdr_byte_sum = __calc_hdr_byte_sum(control);
+
+               /*
+                * 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;
+               control->num_recs %= EEPROM_MAX_RECORD_NUM;
+               control->tbl_hdr.tbl_size += EEPROM_TABLE_RECORD_SIZE *
num;
+               if (control->tbl_hdr.tbl_size > EEPROM_SIZE_BYTES)
+                       control->tbl_hdr.tbl_size =
EEPROM_TABLE_HEADER_SIZE +
+                       control->num_recs * EEPROM_TABLE_RECORD_SIZE;
+
+               __update_tbl_checksum(control, records, num,
old_hdr_byte_sum);
+
+               __update_table_header(control, buffs);
+       } else if (!__validate_tbl_checksum(control, records, num)) {
+               DRM_WARN("EEPROM Table checksum mismatch!");
+               /* TODO Uncomment when EEPROM read/write is relliable
*/
+               /* ret = -EIO; */
+       }
+
+free_msgs:
+       kfree(msgs);
+
+free_buff:
+       kfree(buffs);
+
+       mutex_unlock(&control->tbl_mutex);
+
+       return ret == num ? 0 : -EIO;
+}
+
+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;
+       }
+
+       if (!amdgpu_ras_eeprom_process_recods(control, recs, true, 1)) {
+
+               memset(recs, 0, sizeof(*recs) * 1);
+
+               control->next_addr = EEPROM_RECORD_START;
+
+               if (!amdgpu_ras_eeprom_process_recods(control, recs, false,
1)) {
+                       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"); }
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
new file mode 100644
index 0000000..41f3fcb
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef _AMDGPU_RAS_EEPROM_H
+#define _AMDGPU_RAS_EEPROM_H
+
+#include <linux/i2c.h>
+
+struct amdgpu_device;
+
+enum amdgpu_ras_eeprom_err_type{
+       AMDGPU_RAS_EEPROM_ERR_PLACE_HOLDER,
+       AMDGPU_RAS_EEPROM_ERR_RECOVERABLE,
+       AMDGPU_RAS_EEPROM_ERR_NON_RECOVERABLE
+};
+
+struct amdgpu_ras_eeprom_table_header {
+       uint32_t header;
+       uint32_t version;
+       uint32_t first_rec_offset;
+       uint32_t tbl_size;
+       uint32_t checksum;
+}__attribute__((__packed__));
+
+struct amdgpu_ras_eeprom_control {
+       struct amdgpu_ras_eeprom_table_header tbl_hdr;
+       struct i2c_adapter eeprom_accessor;
+       uint32_t next_addr;
+       unsigned int num_recs;
+       struct mutex tbl_mutex;
+       bool bus_locked;
+       uint32_t tbl_byte_sum;
+};
+
+/*
+ * Represents single table record. Packed to be easily serialized into
+byte
+ * stream.
+ */
+struct eeprom_table_record {
+
+       union {
+               uint64_t address;
+               uint64_t offset;
+       };
+
+       uint64_t retired_page;
+       uint64_t ts;
+
+       enum amdgpu_ras_eeprom_err_type err_type;
+
+       union {
+               unsigned char bank;
+               unsigned char cu;
+       };
+
+       unsigned char mem_channel;
+       unsigned char mcumc_id;
+}__attribute__((__packed__));
+
+int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control);
+void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control
*control);
+
+int amdgpu_ras_eeprom_process_recods(struct
amdgpu_ras_eeprom_control *control,
+                                           struct eeprom_table_record
*records,
+                                           bool write,
+                                           int num);
+
+void amdgpu_ras_eeprom_test(struct amdgpu_ras_eeprom_control
*control);
+
+#endif // _AMDGPU_RAS_EEPROM_H
--
2.7.4

_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 1/4] drm/amdgpu: Add RAS EEPROM table.
       [not found]     ` <1566417719-1528-2-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
  2019-08-22  4:13       ` Zhou1, Tao
@ 2019-08-23  3:48       ` Tuikov, Luben
  1 sibling, 0 replies; 18+ messages in thread
From: Tuikov, Luben @ 2019-08-23  3:48 UTC (permalink / raw)
  To: Grodzovsky, Andrey, amd-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: Panariti, David, Pan, Xinhui, Lazar, Lijo, Russell, Kent, Zhou1,
	Tao, Deucher, Alexander, Quan, Evan, Zhang, Hawking

Hi Andrey,

Looks good--thanks for addressing my comments dated 2019-08-07.

Regards,
Luben

On 2019-08-21 16:01, Andrey Grodzovsky wrote:
> Add RAS EEPROM table manager to eanble RAS errors to be stored
> upon appearance and retrived on driver load.
> 
> v2: Fix some prints.
> 
> v3:
> Fix checksum calculation.
> Make table record and header structs packed to do correct byte value sum.
> Fix record crossing EEPROM page boundry.
> 
> v4:
> Fix byte sum val calculation for record - look at sizeof(record).
> Fix some style comments.
> 
> Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@amd.com>
> ---
>  drivers/gpu/drm/amd/amdgpu/Makefile            |   2 +-
>  drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h        |   3 +
>  drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c | 482 +++++++++++++++++++++++++
>  drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h |  90 +++++
>  4 files changed, 576 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
>  create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
> 
> diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
> index 28d76bd..f016cf1 100644
> --- a/drivers/gpu/drm/amd/amdgpu/Makefile
> +++ b/drivers/gpu/drm/amd/amdgpu/Makefile
> @@ -54,7 +54,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
>  	amdgpu_gtt_mgr.o amdgpu_vram_mgr.o amdgpu_virt.o amdgpu_atomfirmware.o \
>  	amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o amdgpu_ids.o \
>  	amdgpu_gmc.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o amdgpu_vm_cpu.o \
> -	amdgpu_vm_sdma.o amdgpu_discovery.o
> +	amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o amdgpu_ras_eeprom.o
>  
>  amdgpu-$(CONFIG_PERF_EVENTS) += amdgpu_pmu.o
>  
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
> index 2765f2d..8d5bcd8 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
> @@ -29,6 +29,7 @@
>  #include "amdgpu.h"
>  #include "amdgpu_psp.h"
>  #include "ta_ras_if.h"
> +#include "amdgpu_ras_eeprom.h"
>  
>  enum amdgpu_ras_block {
>  	AMDGPU_RAS_BLOCK__UMC = 0,
> @@ -333,6 +334,8 @@ struct amdgpu_ras {
>  	struct mutex recovery_lock;
>  
>  	uint32_t flags;
> +
> +	struct amdgpu_ras_eeprom_control eeprom_control;
>  };
>  
>  struct ras_fs_data {
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
> new file mode 100644
> index 0000000..bf07515
> --- /dev/null
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
> @@ -0,0 +1,482 @@
> +/*
> + * 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>
> +
> +#define EEPROM_I2C_TARGET_ADDR 0xA0
> +
> +#define EEPROM_TABLE_HEADER_SIZE 20
> +#define EEPROM_TABLE_RECORD_SIZE 24
> +#define EEPROM_ADDRESS_SIZE 0x2
> +
> +/* Table hdr is 'AMDR' */
> +#define EEPROM_TABLE_HDR_VAL 0x414d4452
> +#define EEPROM_TABLE_VER 0x00010000
> +
> +/* Assume 2 Mbit size */
> +#define EEPROM_SIZE_BYTES 256000
> +#define EEPROM_PAGE__SIZE_BYTES 256
> +#define EEPROM_HDR_START 0
> +#define EEPROM_RECORD_START (EEPROM_HDR_START + EEPROM_TABLE_HEADER_SIZE)
> +#define EEPROM_MAX_RECORD_NUM ((EEPROM_SIZE_BYTES - EEPROM_TABLE_HEADER_SIZE) / EEPROM_TABLE_RECORD_SIZE)
> +#define EEPROM_ADDR_MSB_MASK GENMASK(17, 8)
> +
> +#define to_amdgpu_device(x) (container_of(x, struct amdgpu_ras, eeprom_control))->adev
> +
> +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;
> +
> +	hdr->header 	      = le32_to_cpu(pp[0]);
> +	hdr->version 	      = le32_to_cpu(pp[1]);
> +	hdr->first_rec_offset = le32_to_cpu(pp[2]);
> +	hdr->tbl_size 	      = le32_to_cpu(pp[3]);
> +	hdr->checksum 	      = le32_to_cpu(pp[4]);
> +}
> +
> +static int __update_table_header(struct amdgpu_ras_eeprom_control *control,
> +				 unsigned char *buff)
> +{
> +	int ret = 0;
> +	struct i2c_msg msg = {
> +			.addr	= EEPROM_I2C_TARGET_ADDR,
> +			.flags	= 0,
> +			.len	= EEPROM_ADDRESS_SIZE + EEPROM_TABLE_HEADER_SIZE,
> +			.buf	= buff,
> +	};
> +
> +
> +	*(uint16_t *)buff = EEPROM_HDR_START;
> +	__encode_table_header_to_buff(&control->tbl_hdr, buff + EEPROM_ADDRESS_SIZE);
> +
> +	ret = i2c_transfer(&control->eeprom_accessor, &msg, 1);
> +	if (ret < 1)
> +		DRM_ERROR("Failed to write EEPROM table header, ret:%d", ret);
> +
> +	return ret;
> +}
> +
> +static uint32_t  __calc_hdr_byte_sum(struct amdgpu_ras_eeprom_control *control);
> +
> +int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
> +{
> +	int ret = 0;
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +	unsigned char buff[EEPROM_ADDRESS_SIZE + EEPROM_TABLE_HEADER_SIZE] = { 0 };
> +	struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr;
> +	struct i2c_msg msg = {
> +			.addr	= EEPROM_I2C_TARGET_ADDR,
> +			.flags	= I2C_M_RD,
> +			.len	= EEPROM_ADDRESS_SIZE + EEPROM_TABLE_HEADER_SIZE,
> +			.buf	= buff,
> +	};
> +
> +	mutex_init(&control->tbl_mutex);
> +
> +	switch (adev->asic_type) {
> +	case CHIP_VEGA20:
> +	/*TODO Add MI-60 */
> +		break;
> +
> +	default:
> +		return 0;
> +	}
> +
> +	if (ret) {
> +		DRM_ERROR("Failed to init I2C controller, ret:%d", ret);
> +		return ret;
> +	}
> +
> +	/* Read/Create table header from EEPROM address 0 */
> +	ret = i2c_transfer(&control->eeprom_accessor, &msg, 1);
> +	if (ret < 1) {
> +		DRM_ERROR("Failed to read EEPROM table header, ret:%d", ret);
> +		return ret;
> +	}
> +
> +	__decode_table_header_from_buff(hdr, &buff[2]);
> +
> +	if (hdr->header == EEPROM_TABLE_HDR_VAL) {
> +		control->num_recs = (hdr->tbl_size - EEPROM_TABLE_HEADER_SIZE) /
> +				    EEPROM_TABLE_RECORD_SIZE;
> +		DRM_DEBUG_DRIVER("Found existing EEPROM table with %d records",
> +				 control->num_recs);
> +
> +	} else {
> +		DRM_INFO("Creating new EEPROM table");
> +
> +		hdr->header = EEPROM_TABLE_HDR_VAL;
> +		hdr->version = EEPROM_TABLE_VER;
> +		hdr->first_rec_offset = EEPROM_RECORD_START;
> +		hdr->tbl_size = EEPROM_TABLE_HEADER_SIZE;
> +
> +		adev->psp.ras.ras->eeprom_control.tbl_byte_sum =
> +				__calc_hdr_byte_sum(&adev->psp.ras.ras->eeprom_control);
> +		ret = __update_table_header(control, buff);
> +	}
> +
> +	/* Start inserting records from here */
> +	adev->psp.ras.ras->eeprom_control.next_addr = EEPROM_RECORD_START;
> +
> +	return ret == 1 ? 0 : -EIO;
> +}
> +
> +void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control *control)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +
> +	switch (adev->asic_type) {
> +	case CHIP_VEGA20:
> +		/*TODO Add MI-60 */
> +		break;
> +
> +	default:
> +		return;
> +	}
> +}
> +
> +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;
> +
> +	buff[i++] = record->mem_channel;
> +	buff[i++] = record->mcumc_id;
> +
> +	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
> + * When next record access will go beyond EEPROM page boundary modify bits A17/A8
> + * in I2C selector to go to next page
> + */
> +static uint32_t __correct_eeprom_dest_address(uint32_t curr_address)
> +{
> +	uint32_t next_address = curr_address + EEPROM_TABLE_RECORD_SIZE;
> +
> +	/* When all EEPROM memory used jump back to 0 address */
> +	if (next_address > EEPROM_SIZE_BYTES) {
> +		DRM_INFO("Reached end of EEPROM memory, jumping to 0 "
> +			 "and overriding old record");
> +		return EEPROM_RECORD_START;
> +	}
> +
> +	/*
> +	 * To check if we overflow page boundary  compare next address with
> +	 * current and see if bits 17/8 of the EEPROM address will change
> +	 * If they do start from the next 256b page
> +	 *
> +	 * https://www.st.com/resource/en/datasheet/m24m02-dr.pdf sec. 5.1.2
> +	 */
> +	if ((curr_address & EEPROM_ADDR_MSB_MASK) != (next_address & EEPROM_ADDR_MSB_MASK)) {
> +		DRM_DEBUG_DRIVER("Reached end of EEPROM memory page, jumpimng to next: %lx",
> +				(next_address & EEPROM_ADDR_MSB_MASK));
> +
> +		return  (next_address & EEPROM_ADDR_MSB_MASK);
> +	}
> +
> +	return curr_address;
> +}
> +
> +
> +static uint32_t  __calc_hdr_byte_sum(struct amdgpu_ras_eeprom_control *control)
> +{
> +	int i;
> +	uint32_t tbl_sum = 0;
> +
> +	/* Header checksum, skip checksum field in the calculation */
> +	for (i = 0; i < sizeof(control->tbl_hdr) - sizeof(control->tbl_hdr.checksum); i++)
> +		tbl_sum += *(((unsigned char *)&control->tbl_hdr) + i);
> +
> +	return tbl_sum;
> +}
> +
> +static uint32_t  __calc_recs_byte_sum(struct eeprom_table_record *records,
> +				      int num)
> +{
> +	int i, j;
> +	uint32_t tbl_sum = 0;
> +
> +	/* Records checksum */
> +	for (i = 0; i < num; i++) {
> +		struct eeprom_table_record *record = &records[i];
> +
> +		for (j = 0; j < sizeof(*record); j++) {
> +			tbl_sum += *(((unsigned char *)record) + j);
> +		}
> +	}
> +
> +	return tbl_sum;
> +}
> +
> +static inline uint32_t  __calc_tbl_byte_sum(struct amdgpu_ras_eeprom_control *control,
> +				  struct eeprom_table_record *records, int num)
> +{
> +	return __calc_hdr_byte_sum(control) + __calc_recs_byte_sum(records, num);
> +}
> +
> +/* Checksum = 256 -((sum of all table entries) mod 256) */
> +static void __update_tbl_checksum(struct amdgpu_ras_eeprom_control *control,
> +				  struct eeprom_table_record *records, int num,
> +				  uint32_t old_hdr_byte_sum)
> +{
> +	/*
> +	 * This will update the table sum with new records.
> +	 *
> +	 * TODO: What happens when the EEPROM table is to be wrapped around
> +	 * and old records from start will get overridden.
> +	 */
> +
> +	/* need to recalculate updated header byte sum */
> +	control->tbl_byte_sum -= old_hdr_byte_sum;
> +	control->tbl_byte_sum += __calc_tbl_byte_sum(control, records, num);
> +
> +	control->tbl_hdr.checksum = 256 - (control->tbl_byte_sum % 256);
> +}
> +
> +/* table sum mod 256 + checksum must equals 256 */
> +static bool __validate_tbl_checksum(struct amdgpu_ras_eeprom_control *control,
> +			    struct eeprom_table_record *records, int num)
> +{
> +	control->tbl_byte_sum = __calc_tbl_byte_sum(control, records, num);
> +
> +	if (control->tbl_hdr.checksum + (control->tbl_byte_sum % 256) != 256) {
> +		DRM_WARN("Checksum mismatch, checksum: %u ", control->tbl_hdr.checksum);
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +int amdgpu_ras_eeprom_process_recods(struct amdgpu_ras_eeprom_control *control,
> +					    struct eeprom_table_record *records,
> +					    bool write,
> +					    int num)
> +{
> +	int i, ret = 0;
> +	struct i2c_msg *msgs;
> +	unsigned char *buffs;
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +
> +	if (adev->asic_type != CHIP_VEGA20)
> +		return 0;
> +
> +	buffs = kcalloc(num, EEPROM_ADDRESS_SIZE + EEPROM_TABLE_RECORD_SIZE,
> +			 GFP_KERNEL);
> +	if (!buffs)
> +		return -ENOMEM;
> +
> +	mutex_lock(&control->tbl_mutex);
> +
> +	msgs = kcalloc(num, sizeof(*msgs), GFP_KERNEL);
> +	if (!msgs) {
> +		ret = -ENOMEM;
> +		goto free_buff;
> +	}
> +
> +	/* In case of overflow just start from beginning to not lose newest records */
> +	if (write && (control->next_addr + EEPROM_TABLE_RECORD_SIZE * num > EEPROM_SIZE_BYTES))
> +		control->next_addr = EEPROM_RECORD_START;
> +
> +
> +	/*
> +	 * 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++) {
> +		unsigned char *buff = &buffs[i*(EEPROM_ADDRESS_SIZE + EEPROM_TABLE_RECORD_SIZE)];
> +		struct eeprom_table_record *record = &records[i];
> +		struct i2c_msg *msg = &msgs[i];
> +
> +		control->next_addr = __correct_eeprom_dest_address(control->next_addr);
> +
> +		/*
> +		 * Update bits 16,17 of EEPROM address in I2C address by setting them
> +		 * to bits 1,2 of Device address byte
> +		 */
> +		msg->addr = EEPROM_I2C_TARGET_ADDR |
> +			       ((control->next_addr & EEPROM_ADDR_MSB_MASK) >> 15);
> +		msg->flags	= write ? 0 : I2C_M_RD;
> +		msg->len	= EEPROM_ADDRESS_SIZE + EEPROM_TABLE_RECORD_SIZE;
> +		msg->buf	= buff;
> +
> +		/* Insert the EEPROM dest addess, bits 0-15 */
> +		buff[0] = ((control->next_addr >> 8) & 0xff);
> +		buff[1] = (control->next_addr & 0xff);
> +
> +		/* EEPROM table content is stored in LE format */
> +		if (write)
> +			__encode_table_record_to_buff(control, record, buff + EEPROM_ADDRESS_SIZE);
> +
> +		/*
> +		 * The destination EEPROM address might need to be corrected to account
> +		 * for page or entire memory wrapping
> +		 */
> +		control->next_addr += EEPROM_TABLE_RECORD_SIZE;
> +	}
> +
> +	ret = i2c_transfer(&control->eeprom_accessor, msgs, num);
> +	if (ret < 1) {
> +		DRM_ERROR("Failed to process EEPROM table records, ret:%d", ret);
> +
> +		/* TODO Restore prev next EEPROM address ? */
> +		goto free_msgs;
> +	}
> +
> +
> +	if (!write) {
> +		for (i = 0; i < num; i++) {
> +			unsigned char *buff = &buffs[i*(EEPROM_ADDRESS_SIZE + EEPROM_TABLE_RECORD_SIZE)];
> +			struct eeprom_table_record *record = &records[i];
> +
> +			__decode_table_record_from_buff(control, record, buff + EEPROM_ADDRESS_SIZE);
> +		}
> +	}
> +
> +	if (write) {
> +		uint32_t old_hdr_byte_sum = __calc_hdr_byte_sum(control);
> +
> +		/*
> +		 * 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;
> +		control->num_recs %= EEPROM_MAX_RECORD_NUM;
> +		control->tbl_hdr.tbl_size += EEPROM_TABLE_RECORD_SIZE * num;
> +		if (control->tbl_hdr.tbl_size > EEPROM_SIZE_BYTES)
> +			control->tbl_hdr.tbl_size = EEPROM_TABLE_HEADER_SIZE +
> +			control->num_recs * EEPROM_TABLE_RECORD_SIZE;
> +
> +		__update_tbl_checksum(control, records, num, old_hdr_byte_sum);
> +
> +		__update_table_header(control, buffs);
> +	} else if (!__validate_tbl_checksum(control, records, num)) {
> +		DRM_WARN("EEPROM Table checksum mismatch!");
> +		/* TODO Uncomment when EEPROM read/write is relliable */
> +		/* ret = -EIO; */
> +	}
> +
> +free_msgs:
> +	kfree(msgs);
> +
> +free_buff:
> +	kfree(buffs);
> +
> +	mutex_unlock(&control->tbl_mutex);
> +
> +	return ret == num ? 0 : -EIO;
> +}
> +
> +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;
> +	}
> +
> +	if (!amdgpu_ras_eeprom_process_recods(control, recs, true, 1)) {
> +
> +		memset(recs, 0, sizeof(*recs) * 1);
> +
> +		control->next_addr = EEPROM_RECORD_START;
> +
> +		if (!amdgpu_ras_eeprom_process_recods(control, recs, false, 1)) {
> +			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");
> +}
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
> new file mode 100644
> index 0000000..41f3fcb
> --- /dev/null
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
> @@ -0,0 +1,90 @@
> +/*
> + * 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.
> + *
> + */
> +
> +#ifndef _AMDGPU_RAS_EEPROM_H
> +#define _AMDGPU_RAS_EEPROM_H
> +
> +#include <linux/i2c.h>
> +
> +struct amdgpu_device;
> +
> +enum amdgpu_ras_eeprom_err_type{
> +	AMDGPU_RAS_EEPROM_ERR_PLACE_HOLDER,
> +	AMDGPU_RAS_EEPROM_ERR_RECOVERABLE,
> +	AMDGPU_RAS_EEPROM_ERR_NON_RECOVERABLE
> +};
> +
> +struct amdgpu_ras_eeprom_table_header {
> +	uint32_t header;
> +	uint32_t version;
> +	uint32_t first_rec_offset;
> +	uint32_t tbl_size;
> +	uint32_t checksum;
> +}__attribute__((__packed__));
> +
> +struct amdgpu_ras_eeprom_control {
> +	struct amdgpu_ras_eeprom_table_header tbl_hdr;
> +	struct i2c_adapter eeprom_accessor;
> +	uint32_t next_addr;
> +	unsigned int num_recs;
> +	struct mutex tbl_mutex;
> +	bool bus_locked;
> +	uint32_t tbl_byte_sum;
> +};
> +
> +/*
> + * Represents single table record. Packed to be easily serialized into byte
> + * stream.
> + */
> +struct eeprom_table_record {
> +
> +	union {
> +		uint64_t address;
> +		uint64_t offset;
> +	};
> +
> +	uint64_t retired_page;
> +	uint64_t ts;
> +
> +	enum amdgpu_ras_eeprom_err_type err_type;
> +
> +	union {
> +		unsigned char bank;
> +		unsigned char cu;
> +	};
> +
> +	unsigned char mem_channel;
> +	unsigned char mcumc_id;
> +}__attribute__((__packed__));
> +
> +int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control);
> +void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control *control);
> +
> +int amdgpu_ras_eeprom_process_recods(struct amdgpu_ras_eeprom_control *control,
> +					    struct eeprom_table_record *records,
> +					    bool write,
> +					    int num);
> +
> +void amdgpu_ras_eeprom_test(struct amdgpu_ras_eeprom_control *control);
> +
> +#endif // _AMDGPU_RAS_EEPROM_H
> 


Regards,
-- 
Luben
_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 4/4] drm/amdgpu: Vega20 SMU I2C HW engine controller.
       [not found]     ` <1566417719-1528-5-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
  2019-08-22  2:32       ` Alex Deucher
@ 2019-08-23  3:50       ` Tuikov, Luben
  1 sibling, 0 replies; 18+ messages in thread
From: Tuikov, Luben @ 2019-08-23  3:50 UTC (permalink / raw)
  To: Grodzovsky, Andrey, amd-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: Panariti, David, Lazar, Lijo, Pan, Xinhui, Zhou1, Tao, Russell,
	Kent, Deucher, Alexander, Quan, Evan, Zhang, Hawking

Hi Andrey,

I've the exact same comments about this patch as the ones
I posted on 2019-08-07.

Regards,
Luben

On 2019-08-21 16:01, Andrey Grodzovsky wrote:
> Implement HW I2C enigne controller to be used by the RAS EEPROM
> table manager. This is based on code from ATITOOLs.
> 
> v2:
> Rename the file and all function prefixes to smu_v11_0_i2c
> 
> By Luben's observation always fill the TX fifo to full so
> we don't have garbadge interpreted by the slave as valid data.
> 
> v3:
> Remove preemption disable as the HW I2C controller will not
> stop the clock on empty TX fifo and so it's not critical to
> keep not empty queue.
> Switch to fast mode 400 khz SCL clock for faster read and write.
> 
> Signed-off-by: Andrey Grodzovsky <andrey.grodzovsky@amd.com>
> ---
>  drivers/gpu/drm/amd/amdgpu/Makefile            |   5 +-
>  drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c |   5 +-
>  drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c     | 710 +++++++++++++++++++++++++
>  drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h     |  34 ++
>  4 files changed, 751 insertions(+), 3 deletions(-)
>  create mode 100644 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
>  create mode 100644 drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
> 
> diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
> index f016cf1..14733ff 100644
> --- a/drivers/gpu/drm/amd/amdgpu/Makefile
> +++ b/drivers/gpu/drm/amd/amdgpu/Makefile
> @@ -38,6 +38,9 @@ ccflags-y := -I$(FULL_AMD_PATH)/include/asic_reg \
>  	-I$(FULL_AMD_DISPLAY_PATH)/amdgpu_dm \
>  	-I$(FULL_AMD_PATH)/amdkfd
>  
> +
> +
> +
>  amdgpu-y := amdgpu_drv.o
>  
>  # add KMS driver
> @@ -54,7 +57,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
>  	amdgpu_gtt_mgr.o amdgpu_vram_mgr.o amdgpu_virt.o amdgpu_atomfirmware.o \
>  	amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o amdgpu_ids.o \
>  	amdgpu_gmc.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o amdgpu_vm_cpu.o \
> -	amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o amdgpu_ras_eeprom.o
> +	amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o amdgpu_ras_eeprom.o smu_v11_0_i2c.o
>  
>  amdgpu-$(CONFIG_PERF_EVENTS) += amdgpu_pmu.o
>  
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
> index bf07515..e6b2e17 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
> @@ -25,6 +25,7 @@
>  #include "amdgpu.h"
>  #include "amdgpu_ras.h"
>  #include <linux/bits.h>
> +#include "smu_v11_0_i2c.h"
>  
>  #define EEPROM_I2C_TARGET_ADDR 0xA0
>  
> @@ -111,7 +112,7 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
>  
>  	switch (adev->asic_type) {
>  	case CHIP_VEGA20:
> -	/*TODO Add MI-60 */
> +		ret = smu_v11_0_i2c_eeprom_control_init(&control->eeprom_accessor);
>  		break;
>  
>  	default:
> @@ -163,7 +164,7 @@ void amdgpu_ras_eeprom_fini(struct amdgpu_ras_eeprom_control *control)
>  
>  	switch (adev->asic_type) {
>  	case CHIP_VEGA20:
> -		/*TODO Add MI-60 */
> +		smu_v11_0_i2c_eeprom_control_fini(&control->eeprom_accessor);
>  		break;
>  
>  	default:
> diff --git a/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
> new file mode 100644
> index 0000000..24405fa
> --- /dev/null
> +++ b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c
> @@ -0,0 +1,710 @@
> +/*
> + * 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 "smuio/smuio_11_0_0_offset.h"
> +#include "smuio/smuio_11_0_0_sh_mask.h"
> +
> +#include "smu_v11_0_i2c.h"
> +#include "amdgpu.h"
> +#include "soc15_common.h"
> +#include <drm/drm_fixed.h>
> +#include "amdgpu_amdkfd.h"
> +#include <linux/i2c.h>
> +#include "amdgpu_ras.h"
> +
> +/* error codes */
> +#define I2C_OK				0
> +#define I2C_NAK_7B_ADDR_NOACK		1
> +#define I2C_NAK_TXDATA_NOACK		2
> +#define I2C_TIMEOUT			4
> +#define I2C_SW_TIMEOUT			8
> +#define I2C_ABORT			0x10
> +
> +/* I2C transaction flags */
> +#define I2C_NO_STOP	1
> +#define I2C_RESTART	2
> +
> +#define to_amdgpu_device(x) (container_of(x, struct amdgpu_ras, eeprom_control.eeprom_accessor))->adev
> +#define to_eeprom_control(x) container_of(x, struct amdgpu_ras_eeprom_control, eeprom_accessor)
> +
> +static void smu_v11_0_i2c_dis_clock_gating(struct i2c_adapter *control)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +	uint32_t reg;
> +
> +	reg = RREG32_SOC15(SMUIO, 0, mmSMUIO_PWRMGT);
> +	reg = REG_SET_FIELD(reg, SMUIO_PWRMGT, i2c_clk_gate_en, 0);
> +	WREG32_SOC15(SMUIO, 0, mmSMUIO_PWRMGT, reg);
> +}
> +
> +
> +static void smu_v11_0_i2c_enable(struct i2c_adapter *control, bool enable)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +
> +	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, enable ? 1 : 0);
> +}
> +
> +static void smu_v11_0_i2c_clear_status(struct i2c_adapter *control)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +	/* do */
> +	{
> +		RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CLR_INTR);
> +
> +	} /* while (reg_CKSVII2C_ic_clr_intr == 0) */
> +}
> +
> +static void smu_v11_0_i2c_configure(struct i2c_adapter *control)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +	uint32_t reg = 0;
> +
> +	reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_SLAVE_DISABLE, 1);
> +	reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_RESTART_EN, 1);
> +	reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_10BITADDR_MASTER, 0);
> +	reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_10BITADDR_SLAVE, 0);
> +	/* Standard mode */
> +	reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_MAX_SPEED_MODE, 2);
> +	reg = REG_SET_FIELD(reg, CKSVII2C_IC_CON, IC_MASTER_MODE, 1);
> +
> +	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CON, reg);
> +}
> +
> +static void smu_v11_0_i2c_set_clock(struct i2c_adapter *control)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +
> +	/*
> +	 * Standard mode speed, These values are taken from SMUIO MAS,
> +	 * but are different from what is given is
> +	 * Synopsys spec. The values here are based on assumption
> +	 * that refclock is 100MHz
> +	 *
> +	 * Configuration for standard mode; Speed = 100kbps
> +	 * Scale linearly, for now only support standard speed clock
> +	 * This will work only with 100M ref clock
> +	 *
> +	 * TBD:Change the calculation to take into account ref clock values also.
> +	 */
> +
> +	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_FS_SPKLEN, 2);
> +	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SS_SCL_HCNT, 120);
> +	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SS_SCL_LCNT, 130);
> +	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_SDA_HOLD, 20);
> +}
> +
> +static void smu_v11_0_i2c_set_address(struct i2c_adapter *control, uint8_t address)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +
> +	/* Convert fromr 8-bit to 7-bit address */
> +	address >>= 1;
> +	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TAR, (address & 0xFF));
> +}
> +
> +static uint32_t smu_v11_0_i2c_poll_tx_status(struct i2c_adapter *control)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +	uint32_t ret = I2C_OK;
> +	uint32_t reg, reg_c_tx_abrt_source;
> +
> +	/*Check if transmission is completed */
> +	unsigned long  timeout_counter = jiffies + msecs_to_jiffies(20);
> +
> +	do {
> +		reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
> +
> +		if (time_after(jiffies, timeout_counter)) {
> +			ret |= I2C_SW_TIMEOUT;
> +			break;
> +		}
> +	} while (REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFE) == 0);
> +
> +	if (ret != I2C_OK)
> +		return ret;
> +
> +	/* This only checks if NAK is received and transaction got aborted */
> +	reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_INTR_STAT);
> +
> +	if (REG_GET_FIELD(reg, CKSVII2C_IC_INTR_STAT, R_TX_ABRT) == 1) {
> +		reg_c_tx_abrt_source = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TX_ABRT_SOURCE);
> +		DRM_INFO("TX was terminated, IC_TX_ABRT_SOURCE val is:%x", reg_c_tx_abrt_source);
> +
> +		/* Check for stop due to NACK */
> +		if (REG_GET_FIELD(reg_c_tx_abrt_source,
> +				  CKSVII2C_IC_TX_ABRT_SOURCE,
> +				  ABRT_TXDATA_NOACK) == 1) {
> +
> +			ret |= I2C_NAK_TXDATA_NOACK;
> +
> +		} else if (REG_GET_FIELD(reg_c_tx_abrt_source,
> +					 CKSVII2C_IC_TX_ABRT_SOURCE,
> +					 ABRT_7B_ADDR_NOACK) == 1) {
> +
> +			ret |= I2C_NAK_7B_ADDR_NOACK;
> +		} else {
> +			ret |= I2C_ABORT;
> +		}
> +
> +		smu_v11_0_i2c_clear_status(control);
> +	}
> +
> +	return ret;
> +}
> +
> +static uint32_t smu_v11_0_i2c_poll_rx_status(struct i2c_adapter *control)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +	uint32_t ret = I2C_OK;
> +	uint32_t reg_ic_status, reg_c_tx_abrt_source;
> +
> +	reg_c_tx_abrt_source = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_TX_ABRT_SOURCE);
> +
> +	/* If slave is not present */
> +	if (REG_GET_FIELD(reg_c_tx_abrt_source,
> +			  CKSVII2C_IC_TX_ABRT_SOURCE,
> +			  ABRT_7B_ADDR_NOACK) == 1) {
> +		ret |= I2C_NAK_7B_ADDR_NOACK;
> +
> +		smu_v11_0_i2c_clear_status(control);
> +	} else {  /* wait till some data is there in RXFIFO */
> +		/* Poll for some byte in RXFIFO */
> +		unsigned long  timeout_counter = jiffies + msecs_to_jiffies(20);
> +
> +		do {
> +			reg_ic_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
> +
> +			if (time_after(jiffies, timeout_counter)) {
> +				ret |= I2C_SW_TIMEOUT;
> +				break;
> +			}
> +		} while (REG_GET_FIELD(reg_ic_status, CKSVII2C_IC_STATUS, RFNE) == 0);
> +	}
> +
> +	return ret;
> +}
> +
> +
> +
> +
> +/**
> + * smu_v11_0_i2c_transmit - Send a block of data over the I2C bus to a slave device.
> + *
> + * @address: The I2C address of the slave device.
> + * @data: The data to transmit over the bus.
> + * @numbytes: The amount of data to transmit.
> + * @i2c_flag: Flags for transmission
> + *
> + * Returns 0 on success or error.
> + */
> +static uint32_t smu_v11_0_i2c_transmit(struct i2c_adapter *control,
> +				  uint8_t address, uint8_t *data,
> +				  uint32_t numbytes, uint32_t i2c_flag)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +	uint32_t bytes_sent, reg, ret = 0;
> +	unsigned long  timeout_counter;
> +
> +	bytes_sent = 0;
> +
> +	DRM_DEBUG_DRIVER("I2C_Transmit(), address = %x, bytes = %d , data: ",
> +		 (uint16_t)address, numbytes);
> +
> +	if (drm_debug & DRM_UT_DRIVER) {
> +		print_hex_dump(KERN_INFO, "data: ", DUMP_PREFIX_NONE,
> +			       16, 1, data, numbytes, false);
> +	}
> +
> +	/* Set the I2C slave address */
> +	smu_v11_0_i2c_set_address(control, address);
> +	/* Enable I2C */
> +	smu_v11_0_i2c_enable(control, true);
> +
> +	/* Clear status bits */
> +	smu_v11_0_i2c_clear_status(control);
> +
> +
> +	timeout_counter = jiffies + msecs_to_jiffies(20);
> +
> +	while (numbytes > 0) {
> +		reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
> +		if (REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFNF)) {
> +			do {
> +				reg = 0;
> +				/*
> +				 * Prepare transaction, no need to set RESTART. I2C engine will send
> +				 * START as soon as it sees data in TXFIFO
> +				 */
> +				if (bytes_sent == 0)
> +					reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, RESTART,
> +							    (i2c_flag & I2C_RESTART) ? 1 : 0);
> +				reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, data[bytes_sent]);
> +
> +				/* determine if we need to send STOP bit or not */
> +				if (numbytes == 1)
> +					/* Final transaction, so send stop unless I2C_NO_STOP */
> +					reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, STOP,
> +							    (i2c_flag & I2C_NO_STOP) ? 0 : 1);
> +				/* Write */
> +				reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 0);
> +				WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg);
> +
> +				/* Record that the bytes were transmitted */
> +				bytes_sent++;
> +				numbytes--;
> +
> +				reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_STATUS);
> +
> +			} while (numbytes &&  REG_GET_FIELD(reg, CKSVII2C_IC_STATUS, TFNF));
> +		}
> +
> +
> +		if (time_after(jiffies, timeout_counter)) {
> +			ret |= I2C_SW_TIMEOUT;
> +			goto err;
> +		}
> +	}
> +
> +	ret = smu_v11_0_i2c_poll_tx_status(control);
> +
> +err:
> +	/* Any error, no point in proceeding */
> +	if (ret != I2C_OK) {
> +		if (ret & I2C_SW_TIMEOUT)
> +			DRM_ERROR("TIMEOUT ERROR !!!");
> +
> +		if (ret & I2C_NAK_7B_ADDR_NOACK)
> +			DRM_ERROR("Received I2C_NAK_7B_ADDR_NOACK !!!");
> +
> +
> +		if (ret & I2C_NAK_TXDATA_NOACK)
> +			DRM_ERROR("Received I2C_NAK_TXDATA_NOACK !!!");
> +	}
> +
> +	return ret;
> +}
> +
> +
> +/**
> + * smu_v11_0_i2c_receive - Receive a block of data over the I2C bus from a slave device.
> + *
> + * @address: The I2C address of the slave device.
> + * @numbytes: The amount of data to transmit.
> + * @i2c_flag: Flags for transmission
> + *
> + * Returns 0 on success or error.
> + */
> +static uint32_t smu_v11_0_i2c_receive(struct i2c_adapter *control,
> +				 uint8_t address, uint8_t *data,
> +				 uint32_t numbytes, uint8_t i2c_flag)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +	uint32_t bytes_received, ret = I2C_OK;
> +
> +	bytes_received = 0;
> +
> +	/* Set the I2C slave address */
> +	smu_v11_0_i2c_set_address(control, address);
> +
> +	/* Enable I2C */
> +	smu_v11_0_i2c_enable(control, true);
> +
> +	while (numbytes > 0) {
> +		uint32_t reg = 0;
> +
> +		smu_v11_0_i2c_clear_status(control);
> +
> +
> +		/* Prepare transaction */
> +
> +		/* Each time we disable I2C, so this is not a restart */
> +		if (bytes_received == 0)
> +			reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, RESTART,
> +					    (i2c_flag & I2C_RESTART) ? 1 : 0);
> +
> +		reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT, 0);
> +		/* Read */
> +		reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, CMD, 1);
> +
> +		/* Transmitting last byte */
> +		if (numbytes == 1)
> +			/* Final transaction, so send stop if requested */
> +			reg = REG_SET_FIELD(reg, CKSVII2C_IC_DATA_CMD, STOP,
> +					    (i2c_flag & I2C_NO_STOP) ? 0 : 1);
> +
> +		WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD, reg);
> +
> +		ret = smu_v11_0_i2c_poll_rx_status(control);
> +
> +		/* Any error, no point in proceeding */
> +		if (ret != I2C_OK) {
> +			if (ret & I2C_SW_TIMEOUT)
> +				DRM_ERROR("TIMEOUT ERROR !!!");
> +
> +			if (ret & I2C_NAK_7B_ADDR_NOACK)
> +				DRM_ERROR("Received I2C_NAK_7B_ADDR_NOACK !!!");
> +
> +			if (ret & I2C_NAK_TXDATA_NOACK)
> +				DRM_ERROR("Received I2C_NAK_TXDATA_NOACK !!!");
> +
> +			break;
> +		}
> +
> +		reg = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_DATA_CMD);
> +		data[bytes_received] = REG_GET_FIELD(reg, CKSVII2C_IC_DATA_CMD, DAT);
> +
> +		/* Record that the bytes were received */
> +		bytes_received++;
> +		numbytes--;
> +	}
> +
> +	DRM_DEBUG_DRIVER("I2C_Receive(), address = %x, bytes = %d, data :",
> +		  (uint16_t)address, bytes_received);
> +
> +	if (drm_debug & DRM_UT_DRIVER) {
> +		print_hex_dump(KERN_INFO, "data: ", DUMP_PREFIX_NONE,
> +			       16, 1, data, bytes_received, false);
> +	}
> +
> +	return ret;
> +}
> +
> +static void smu_v11_0_i2c_abort(struct i2c_adapter *control)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +	uint32_t reg = 0;
> +
> +	/* Enable I2C engine; */
> +	reg = REG_SET_FIELD(reg, CKSVII2C_IC_ENABLE, ENABLE, 1);
> +	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, reg);
> +
> +	/* Abort previous transaction */
> +	reg = REG_SET_FIELD(reg, CKSVII2C_IC_ENABLE, ABORT, 1);
> +	WREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE, reg);
> +
> +	DRM_DEBUG_DRIVER("I2C_Abort() Done.");
> +}
> +
> +
> +static bool smu_v11_0_i2c_activity_done(struct i2c_adapter *control)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +
> +	const uint32_t IDLE_TIMEOUT = 1024;
> +	uint32_t timeoutCount = 0;
> +	uint32_t reg_ic_enable, reg_ic_enable_status, reg_ic_clr_activity;
> +
> +	reg_ic_enable_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE_STATUS);
> +	reg_ic_enable = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE);
> +
> +
> +	if ((REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) &&
> +	    (REG_GET_FIELD(reg_ic_enable_status, CKSVII2C_IC_ENABLE_STATUS, IC_EN) == 1)) {
> +		/*
> +		 * Nobody is using I2C engine, but engine remains active because
> +		 * someone missed to send STOP
> +		 */
> +		smu_v11_0_i2c_abort(control);
> +	} else if (REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) {
> +		/* Nobody is using I2C engine */
> +		return true;
> +	}
> +
> +	/* Keep reading activity bit until it's cleared */
> +	do {
> +		reg_ic_clr_activity = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_CLR_ACTIVITY);
> +
> +		if (REG_GET_FIELD(reg_ic_clr_activity,
> +		    CKSVII2C_IC_CLR_ACTIVITY, CLR_ACTIVITY) == 0)
> +			return true;
> +
> +		++timeoutCount;
> +
> +	} while (timeoutCount < IDLE_TIMEOUT);
> +
> +	return false;
> +}
> +
> +static void smu_v11_0_i2c_init(struct i2c_adapter *control)
> +{
> +	/* Disable clock gating */
> +	smu_v11_0_i2c_dis_clock_gating(control);
> +
> +	if (!smu_v11_0_i2c_activity_done(control))
> +		DRM_WARN("I2C busy !");
> +
> +	/* Disable I2C */
> +	smu_v11_0_i2c_enable(control, false);
> +
> +	/* Configure I2C to operate as master and in standard mode */
> +	smu_v11_0_i2c_configure(control);
> +
> +	/* Initialize the clock to 50 kHz default */
> +	smu_v11_0_i2c_set_clock(control);
> +
> +}
> +
> +static void smu_v11_0_i2c_fini(struct i2c_adapter *control)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +	uint32_t reg_ic_enable_status, reg_ic_enable;
> +
> +	smu_v11_0_i2c_enable(control, false);
> +
> +	/* Double check if disabled, else force abort */
> +	reg_ic_enable_status = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE_STATUS);
> +	reg_ic_enable = RREG32_SOC15(SMUIO, 0, mmCKSVII2C_IC_ENABLE);
> +
> +	if ((REG_GET_FIELD(reg_ic_enable, CKSVII2C_IC_ENABLE, ENABLE) == 0) &&
> +	    (REG_GET_FIELD(reg_ic_enable_status,
> +			   CKSVII2C_IC_ENABLE_STATUS, IC_EN) == 1)) {
> +		/*
> +		 * Nobody is using I2C engine, but engine remains active because
> +		 * someone missed to send STOP
> +		 */
> +		smu_v11_0_i2c_abort(control);
> +	}
> +
> +}
> +
> +static bool smu_v11_0_i2c_bus_lock(struct i2c_adapter *control)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +
> +	/* Send  PPSMC_MSG_RequestI2CBus */
> +	if (!adev->powerplay.pp_funcs->smu_i2c_bus_access)
> +		goto fail;
> +
> +
> +	if (!adev->powerplay.pp_funcs->smu_i2c_bus_access(adev->powerplay.pp_handle, true))
> +		return true;
> +
> +fail:
> +	return false;
> +}
> +
> +static bool smu_v11_0_i2c_bus_unlock(struct i2c_adapter *control)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +
> +	/* Send  PPSMC_MSG_RequestI2CBus */
> +	if (!adev->powerplay.pp_funcs->smu_i2c_bus_access)
> +		goto fail;
> +
> +	/* Send  PPSMC_MSG_ReleaseI2CBus */
> +	if (!adev->powerplay.pp_funcs->smu_i2c_bus_access(adev->powerplay.pp_handle,
> +							     false))
> +		return true;
> +
> +fail:
> +	return false;
> +}
> +
> +/***************************** EEPROM I2C GLUE ****************************/
> +
> +static uint32_t smu_v11_0_i2c_eeprom_read_data(struct i2c_adapter *control,
> +					       uint8_t address,
> +					       uint8_t *data,
> +					       uint32_t numbytes)
> +{
> +	uint32_t  ret = 0;
> +
> +	/* First 2 bytes are dummy write to set EEPROM address */
> +	ret = smu_v11_0_i2c_transmit(control, address, data, 2, I2C_NO_STOP);
> +	if (ret != I2C_OK)
> +		goto fail;
> +
> +	/* Now read data starting with that address */
> +	ret = smu_v11_0_i2c_receive(control, address, data + 2, numbytes - 2,
> +				    I2C_RESTART);
> +
> +fail:
> +	if (ret != I2C_OK)
> +		DRM_ERROR("ReadData() - I2C error occurred :%x", ret);
> +
> +	return ret;
> +}
> +
> +static uint32_t smu_v11_0_i2c_eeprom_write_data(struct i2c_adapter *control,
> +						uint8_t address,
> +						uint8_t *data,
> +						uint32_t numbytes)
> +{
> +	uint32_t  ret;
> +
> +	ret = smu_v11_0_i2c_transmit(control, address, data, numbytes, 0);
> +
> +	if (ret != I2C_OK)
> +		DRM_ERROR("WriteI2CData() - I2C error occurred :%x", ret);
> +	else
> +		/*
> +		 * According to EEPROM spec there is a MAX of 10 ms required for
> +		 * EEPROM to flush internal RX buffer after STOP was issued at the
> +		 * end of write transaction. During this time the EEPROM will not be
> +		 * responsive to any more commands - so wait a bit more.
> +		 *
> +		 * TODO Improve to wait for first ACK for slave address after
> +		 * internal write cycle done.
> +		 */
> +		msleep(10);
> +
> +	return ret;
> +
> +}
> +
> +static void lock_bus(struct i2c_adapter *i2c, unsigned int flags)
> +{
> +	struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c);
> +
> +	if (!smu_v11_0_i2c_bus_lock(i2c)) {
> +		DRM_ERROR("Failed to lock the bus from SMU");
> +		return;
> +	}
> +
> +	control->bus_locked = true;
> +}
> +
> +static int trylock_bus(struct i2c_adapter *i2c, unsigned int flags)
> +{
> +	WARN_ONCE(1, "This operation not supposed to run in atomic context!");
> +	return false;
> +}
> +
> +static void unlock_bus(struct i2c_adapter *i2c, unsigned int flags)
> +{
> +	struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c);
> +
> +	if (!smu_v11_0_i2c_bus_unlock(i2c)) {
> +		DRM_ERROR("Failed to unlock the bus from SMU");
> +		return;
> +	}
> +
> +	control->bus_locked = false;
> +}
> +
> +static const struct i2c_lock_operations smu_v11_0_i2c_i2c_lock_ops = {
> +	.lock_bus = lock_bus,
> +	.trylock_bus = trylock_bus,
> +	.unlock_bus = unlock_bus,
> +};
> +
> +static int smu_v11_0_i2c_eeprom_i2c_xfer(struct i2c_adapter *i2c_adap,
> +			      struct i2c_msg *msgs, int num)
> +{
> +	int i, ret;
> +	struct amdgpu_ras_eeprom_control *control = to_eeprom_control(i2c_adap);
> +
> +	if (!control->bus_locked) {
> +		DRM_ERROR("I2C bus unlocked, stopping transaction!");
> +		return -EIO;
> +	}
> +
> +	smu_v11_0_i2c_init(i2c_adap);
> +
> +	for (i = 0; i < num; i++) {
> +		if (msgs[i].flags & I2C_M_RD)
> +			ret = smu_v11_0_i2c_eeprom_read_data(i2c_adap,
> +							(uint8_t)msgs[i].addr,
> +							msgs[i].buf, msgs[i].len);
> +		else
> +			ret = smu_v11_0_i2c_eeprom_write_data(i2c_adap,
> +							 (uint8_t)msgs[i].addr,
> +							 msgs[i].buf, msgs[i].len);
> +
> +		if (ret != I2C_OK) {
> +			num = -EIO;
> +			break;
> +		}
> +	}
> +
> +	smu_v11_0_i2c_fini(i2c_adap);
> +	return num;
> +}
> +
> +static u32 smu_v11_0_i2c_eeprom_i2c_func(struct i2c_adapter *adap)
> +{
> +	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +
> +static const struct i2c_algorithm smu_v11_0_i2c_eeprom_i2c_algo = {
> +	.master_xfer = smu_v11_0_i2c_eeprom_i2c_xfer,
> +	.functionality = smu_v11_0_i2c_eeprom_i2c_func,
> +};
> +
> +int smu_v11_0_i2c_eeprom_control_init(struct i2c_adapter *control)
> +{
> +	struct amdgpu_device *adev = to_amdgpu_device(control);
> +	int res;
> +
> +	control->owner = THIS_MODULE;
> +	control->class = I2C_CLASS_SPD;
> +	control->dev.parent = &adev->pdev->dev;
> +	control->algo = &smu_v11_0_i2c_eeprom_i2c_algo;
> +	snprintf(control->name, sizeof(control->name), "RAS EEPROM");
> +	control->lock_ops = &smu_v11_0_i2c_i2c_lock_ops;
> +
> +	res = i2c_add_adapter(control);
> +	if (res)
> +		DRM_ERROR("Failed to register hw i2c, err: %d\n", res);
> +
> +	return res;
> +}
> +
> +void smu_v11_0_i2c_eeprom_control_fini(struct i2c_adapter *control)
> +{
> +	i2c_del_adapter(control);
> +}
> +
> +#define I2C_TARGET_ADDR 0xA0
> +
> +bool smu_v11_0_i2c_test_bus(struct i2c_adapter *control)
> +{
> +
> +	uint32_t ret = I2C_OK;
> +	uint8_t data[6] = {0xf, 0, 0xde, 0xad, 0xbe, 0xef};
> +
> +
> +	DRM_INFO("Begin");
> +
> +	if (!smu_v11_0_i2c_bus_lock(control)) {
> +		DRM_ERROR("Failed to lock the bus!.");
> +		return false;
> +	}
> +
> +	smu_v11_0_i2c_init(control);
> +
> +	/* Write 0xde to address 0x0000 on the EEPROM */
> +	ret = smu_v11_0_i2c_eeprom_write_data(control, I2C_TARGET_ADDR, data, 6);
> +
> +	ret = smu_v11_0_i2c_eeprom_read_data(control, I2C_TARGET_ADDR, data, 6);
> +
> +	smu_v11_0_i2c_fini(control);
> +
> +	smu_v11_0_i2c_bus_unlock(control);
> +
> +
> +	DRM_INFO("End");
> +	return true;
> +}
> diff --git a/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
> new file mode 100644
> index 0000000..973f28d
> --- /dev/null
> +++ b/drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.h
> @@ -0,0 +1,34 @@
> +/*
> + * 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.
> + *
> + */
> +
> +#ifndef SMU_V11_I2C_CONTROL_H
> +#define SMU_V11_I2C_CONTROL_H
> +
> +#include <linux/types.h>
> +
> +struct i2c_adapter;
> +
> +int smu_v11_0_i2c_eeprom_control_init(struct i2c_adapter *control);
> +void smu_v11_0_i2c_eeprom_control_fini(struct i2c_adapter *control);
> +
> +#endif
> 


Regards,
-- 
Luben
_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2019-08-23  3:50 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-21 20:01 [PATCH v4 0/4] Add RAS EEPROM table and I2C driver implementation Andrey Grodzovsky
     [not found] ` <1566417719-1528-1-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
2019-08-21 20:01   ` [PATCH v4 1/4] drm/amdgpu: Add RAS EEPROM table Andrey Grodzovsky
     [not found]     ` <1566417719-1528-2-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
2019-08-22  4:13       ` Zhou1, Tao
     [not found]         ` <MN2PR12MB3054832C9FC9C73A7F9AD539B0A50-rweVpJHSKTqnT25eLM+iUQdYzm3356FpvxpqHgZTriW3zl9H0oFU5g@public.gmane.org>
2019-08-22 15:07           ` Grodzovsky, Andrey
     [not found]             ` <ce52910d-0d1f-402c-7cd0-a8a283a2cda8-5C7GfCeVMHo@public.gmane.org>
2019-08-23  2:16               ` Zhou1, Tao
     [not found]                 ` <MN2PR12MB305437611047FDE19ACC251AB0A40-rweVpJHSKTqnT25eLM+iUQdYzm3356FpvxpqHgZTriW3zl9H0oFU5g@public.gmane.org>
2019-08-23  2:47                   ` Grodzovsky, Andrey
2019-08-23  3:48       ` Tuikov, Luben
2019-08-21 20:01   ` [PATCH v4 2/4] drm/amd: Import smuio_11_0 headres for EEPROM access on Vega20 Andrey Grodzovsky
     [not found]     ` <1566417719-1528-3-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
2019-08-22  2:33       ` Alex Deucher
2019-08-21 20:01   ` [PATCH v4 3/4] drm/amd/powerplay: Add interface to lock SMU HW I2C Andrey Grodzovsky
     [not found]     ` <1566417719-1528-4-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
2019-08-22  2:17       ` Alex Deucher
2019-08-21 20:01   ` [PATCH v4 4/4] drm/amdgpu: Vega20 SMU I2C HW engine controller Andrey Grodzovsky
     [not found]     ` <1566417719-1528-5-git-send-email-andrey.grodzovsky-5C7GfCeVMHo@public.gmane.org>
2019-08-22  2:32       ` Alex Deucher
     [not found]         ` <CADnq5_M9gSnLWaQLh3aUtrV0=Tj4USQt3rk-GUjHni6KSMxGbg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2019-08-22 15:35           ` Grodzovsky, Andrey
     [not found]             ` <fe5e0eff-e4bd-4722-6755-ebdbb9c05e03-5C7GfCeVMHo@public.gmane.org>
2019-08-22 15:59               ` Alex Deucher
     [not found]                 ` <CADnq5_NSOATAdC_z9R5BoVbM=eSLQbLYVvdPDOqZYa2Gyz_V6A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2019-08-22 16:45                   ` Grodzovsky, Andrey
     [not found]                     ` <a29f8c1c-3b0f-0412-ab66-441fae96ed8d-5C7GfCeVMHo@public.gmane.org>
2019-08-22 19:13                       ` Alex Deucher
2019-08-23  3:50       ` Tuikov, Luben

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.