400 lines
12 KiB
Diff
Executable File
400 lines
12 KiB
Diff
Executable File
From 136ea6342d8de89ab6166dfce0ba20f67aabc0d3 Mon Sep 17 00:00:00 2001
|
|
From: wangjue <jue.wang2@luxshare-ict.com>
|
|
Date: Thu, 9 Oct 2025 10:49:32 +0800
|
|
Subject: [PATCH] Add sw workaround to let all dimm spd devices to receive ccc
|
|
message
|
|
|
|
Signed-off-by: wangjue <jue.wang2@luxshare-ict.com>
|
|
---
|
|
drivers/i3c/master.c | 251 ++++++++++++++++++++++++++++++-------
|
|
include/linux/i3c/master.h | 11 ++
|
|
2 files changed, 214 insertions(+), 48 deletions(-)
|
|
|
|
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
|
|
index 534db4f05deb..9cd566d3928f 100644
|
|
--- a/drivers/i3c/master.c
|
|
+++ b/drivers/i3c/master.c
|
|
@@ -16,12 +16,117 @@
|
|
#include <linux/slab.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/workqueue.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/delay.h>
|
|
|
|
#include "internals.h"
|
|
|
|
static DEFINE_IDR(i3c_bus_idr);
|
|
static DEFINE_MUTEX(i3c_core_lock);
|
|
|
|
+static void i3c_gpio_switch_init(struct i3c_master_controller *master)
|
|
+{
|
|
+ if (gpio_request(I3C_SPD_BMC_MUX0_SEL, "dw-i3c-master")) {
|
|
+ dev_err(&master->dev, "unable to allocate I3C_SPD_BMC_MUX0_SEL\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (gpio_request(I3C_SPD_BMC_MUX0_EN, "dw-i3c-master")) {
|
|
+ dev_err(&master->dev, "unable to allocate I3C_SPD_BMC_MUX0_EN\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (gpio_request(I3C_SPD_BMC_MUX1_SEL, "dw-i3c-master")) {
|
|
+ dev_err(&master->dev, "unable to allocate I3C_SPD_BMC_MUX1_SEL\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (gpio_request(I3C_SPD_BMC_MUX1_EN, "dw-i3c-master")) {
|
|
+ dev_err(&master->dev, "unable to allocate I3C_SPD_BMC_MUX1_EN\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (gpio_request(I3C_SPD_BMC_MUX2_SEL, "dw-i3c-master")) {
|
|
+ dev_err(&master->dev, "unable to allocate I3C_SPD_BMC_MUX2_SEL\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (gpio_request(I3C_SPD_BMC_MUX2_EN, "dw-i3c-master")) {
|
|
+ dev_err(&master->dev, "unable to allocate I3C_SPD_BMC_MUX2_EN\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (gpio_request(I3C_SPD_BMC_MUX3_SEL, "dw-i3c-master")) {
|
|
+ dev_err(&master->dev, "unable to allocate I3C_SPD_BMC_MUX3_SEL\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (gpio_request(I3C_SPD_BMC_MUX3_EN, "dw-i3c-master")) {
|
|
+ dev_err(&master->dev, "unable to allocate I3C_SPD_BMC_MUX3_EN\n");
|
|
+ return;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void i3c_gpio_switch_free(void)
|
|
+{
|
|
+ gpio_free(I3C_SPD_BMC_MUX0_SEL);
|
|
+ gpio_free(I3C_SPD_BMC_MUX0_EN);
|
|
+ gpio_free(I3C_SPD_BMC_MUX1_SEL);
|
|
+ gpio_free(I3C_SPD_BMC_MUX1_EN);
|
|
+ gpio_free(I3C_SPD_BMC_MUX2_SEL);
|
|
+ gpio_free(I3C_SPD_BMC_MUX2_EN);
|
|
+ gpio_free(I3C_SPD_BMC_MUX3_SEL);
|
|
+ gpio_free(I3C_SPD_BMC_MUX3_EN);
|
|
+}
|
|
+
|
|
+static void i3c_gpio_switch_dimm_group(int gNum)
|
|
+{
|
|
+ if(gNum == 0)
|
|
+ {
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX0_SEL, 0);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX0_EN, 0);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX1_SEL, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX1_EN, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX2_SEL, 0);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX2_EN, 0);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX3_SEL, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX3_EN, 1);
|
|
+ }
|
|
+ if(gNum == 1)
|
|
+ {
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX0_SEL, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX0_EN, 0);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX1_SEL, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX1_EN, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX2_SEL, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX2_EN, 0);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX3_SEL, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX3_EN, 1);
|
|
+ }
|
|
+ if(gNum == 2)
|
|
+ {
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX0_SEL, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX0_EN, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX1_SEL, 0);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX1_EN, 0);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX2_SEL, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX2_EN, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX3_SEL, 0);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX3_EN, 0);
|
|
+ }
|
|
+ if(gNum == 3)
|
|
+ {
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX0_SEL, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX0_EN, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX1_SEL, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX1_EN, 0);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX2_SEL, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX2_EN, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX3_SEL, 1);
|
|
+ gpio_direction_output(I3C_SPD_BMC_MUX3_EN, 0);
|
|
+ }
|
|
+}
|
|
+
|
|
/**
|
|
* i3c_bus_maintenance_lock - Lock the bus for a maintenance operation
|
|
* @bus: I3C bus to take the lock on
|
|
@@ -593,39 +698,51 @@ static ssize_t rescan_store(struct device *dev, struct device_attribute *attr,
|
|
if (!res)
|
|
return count;
|
|
|
|
+ i3c_gpio_switch_init(master);
|
|
+
|
|
i3c_device_publish_event(master, i3c_event_prepare_for_rescan);
|
|
|
|
- i3c_bus_maintenance_lock(bus);
|
|
+ for(int i=0; i<4; i++) {
|
|
+ i3c_gpio_switch_dimm_group(i);
|
|
+ usleep_range(2000, 2100);
|
|
|
|
- ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
|
|
- I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
|
|
- I3C_CCC_EVENT_HJ);
|
|
- if (ret && ret != I3C_ERROR_M2) {
|
|
- dev_dbg(&master->dev,
|
|
- "Failed to run broadcast DISEC for rescan, ret=%d\n", ret);
|
|
- i3c_bus_maintenance_unlock(bus);
|
|
- return ret;
|
|
- }
|
|
+ i3c_bus_maintenance_lock(bus);
|
|
+
|
|
+ ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
|
|
+ I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
|
|
+ I3C_CCC_EVENT_HJ);
|
|
+ if (ret && ret != I3C_ERROR_M2) {
|
|
+ dev_dbg(&master->dev,
|
|
+ "Failed to run broadcast DISEC for rescan, ret=%d\n", ret);
|
|
+ i3c_bus_maintenance_unlock(bus);
|
|
+ i3c_gpio_switch_free();
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
|
|
+ if (ret && ret != I3C_ERROR_M2) {
|
|
+ dev_dbg(&master->dev,
|
|
+ "Failed to run RSTDAA for rescan, ret=%d\n", ret);
|
|
+ i3c_bus_maintenance_unlock(bus);
|
|
+ i3c_gpio_switch_free();
|
|
+ return ret;
|
|
+ }
|
|
|
|
- ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
|
|
- if (ret && ret != I3C_ERROR_M2) {
|
|
- dev_dbg(&master->dev,
|
|
- "Failed to run RSTDAA for rescan, ret=%d\n", ret);
|
|
i3c_bus_maintenance_unlock(bus);
|
|
- return ret;
|
|
+ usleep_range(2000, 2100);
|
|
}
|
|
|
|
- i3c_bus_maintenance_unlock(bus);
|
|
-
|
|
ret = i3c_master_do_daa(master);
|
|
if (ret) {
|
|
dev_dbg(&master->dev, "Failed to run DAA for rescan, ret=%d\n",
|
|
ret);
|
|
+ i3c_gpio_switch_free();
|
|
return ret;
|
|
}
|
|
|
|
i3c_device_publish_event(master, i3c_event_rescan_done);
|
|
|
|
+ i3c_gpio_switch_free();
|
|
return count;
|
|
}
|
|
static DEVICE_ATTR_WO(rescan);
|
|
@@ -1863,11 +1980,17 @@ int i3c_master_do_daa(struct i3c_master_controller *master)
|
|
{
|
|
int ret = 0;
|
|
|
|
+ i3c_gpio_switch_init(master);
|
|
mutex_lock(&master->daa_lock);
|
|
i3c_bus_maintenance_lock(&master->bus);
|
|
if (master->jdec_spd) {
|
|
- ret = i3c_master_sethid_locked(master);
|
|
- ret = i3c_master_setaasa_locked(master);
|
|
+ for(int i=0; i<4; i++) {
|
|
+ i3c_gpio_switch_dimm_group(i);
|
|
+ usleep_range(2000, 2100);
|
|
+ ret = i3c_master_sethid_locked(master);
|
|
+ ret = i3c_master_setaasa_locked(master);
|
|
+ usleep_range(2000, 2100);
|
|
+ }
|
|
} else {
|
|
ret = master->ops->do_daa(master);
|
|
}
|
|
@@ -1880,6 +2003,7 @@ int i3c_master_do_daa(struct i3c_master_controller *master)
|
|
mutex_unlock:
|
|
mutex_unlock(&master->daa_lock);
|
|
|
|
+ i3c_gpio_switch_free();
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(i3c_master_do_daa);
|
|
@@ -2011,6 +2135,7 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
|
|
struct i2c_dev_desc *i2cdev;
|
|
int ret, n_i3cdev = 0;
|
|
|
|
+ i3c_gpio_switch_init(master);
|
|
/*
|
|
* First attach all devices with static definitions provided by the
|
|
* FW.
|
|
@@ -2061,21 +2186,31 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
|
|
goto err_bus_cleanup;
|
|
}
|
|
|
|
- /* Disable all slave events before starting DAA. */
|
|
- ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
|
|
- I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
|
|
- I3C_CCC_EVENT_HJ);
|
|
- if (ret && ret != I3C_ERROR_M2)
|
|
- goto err_bus_cleanup;
|
|
+ for(int i=0; i<4; i++) {
|
|
+ i3c_gpio_switch_dimm_group(i);
|
|
+ usleep_range(2000, 2100);
|
|
+ /* Disable all slave events before starting DAA. */
|
|
+ ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
|
|
+ I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
|
|
+ I3C_CCC_EVENT_HJ);
|
|
+ if (ret && ret != I3C_ERROR_M2)
|
|
+ goto err_bus_cleanup;
|
|
+ usleep_range(2000, 2100);
|
|
+ }
|
|
|
|
- /*
|
|
- * Reset all dynamic address that may have been assigned before
|
|
- * (assigned by the bootloader for example).
|
|
- */
|
|
- ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
|
|
- if (ret && ret != I3C_ERROR_M2)
|
|
- goto err_bus_cleanup;
|
|
|
|
+ for(int i=0; i<4; i++) {
|
|
+ i3c_gpio_switch_dimm_group(i);
|
|
+ usleep_range(2000, 2100);
|
|
+ /*
|
|
+ * Reset all dynamic address that may have been assigned before
|
|
+ * (assigned by the bootloader for example).
|
|
+ */
|
|
+ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
|
|
+ if (ret && ret != I3C_ERROR_M2)
|
|
+ goto err_bus_cleanup;
|
|
+ usleep_range(2000, 2100);
|
|
+ }
|
|
/*
|
|
* Reserve init_dyn_addr first, and then try to pre-assign dynamic
|
|
* address and retrieve device information if needed.
|
|
@@ -2117,12 +2252,16 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
|
|
n_i3cdev++;
|
|
}
|
|
|
|
+ i3c_gpio_switch_free();
|
|
+
|
|
/*
|
|
* Since SPD devices are all with static address. Don't do DAA if we
|
|
* know it is a pure I2C bus.
|
|
*/
|
|
if (master->jdec_spd && n_i3cdev == 0)
|
|
+ {
|
|
return 0;
|
|
+ }
|
|
|
|
ret = i3c_master_do_daa(master);
|
|
if (ret)
|
|
@@ -2131,7 +2270,12 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
|
|
return 0;
|
|
|
|
err_rstdaa:
|
|
- i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
|
|
+ for(int i=0; i<4; i++) {
|
|
+ i3c_gpio_switch_dimm_group(i);
|
|
+ usleep_range(2000, 2100);
|
|
+ i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
|
|
+ usleep_range(2000, 2100);
|
|
+ }
|
|
|
|
err_bus_cleanup:
|
|
if (master->ops->bus_cleanup)
|
|
@@ -2140,6 +2284,7 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
|
|
err_detach_devs:
|
|
i3c_master_detach_free_devs(master);
|
|
|
|
+ i3c_gpio_switch_free();
|
|
return ret;
|
|
}
|
|
|
|
@@ -2147,27 +2292,37 @@ static void i3c_master_bus_cleanup(struct i3c_master_controller *master)
|
|
{
|
|
int ret;
|
|
|
|
- i3c_bus_maintenance_lock(&master->bus);
|
|
- /* Disable all slave events before starting DAA. */
|
|
- ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
|
|
- I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
|
|
- I3C_CCC_EVENT_HJ);
|
|
- if (ret && ret != I3C_ERROR_M2)
|
|
- dev_dbg(&master->dev, "failed to send DISEC, ret=%i\n", ret);
|
|
+ i3c_gpio_switch_init(master);
|
|
|
|
- /*
|
|
- * Reset all dynamic address that may have been assigned before
|
|
- * (assigned by the bootloader for example).
|
|
- */
|
|
- ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
|
|
- if (ret && ret != I3C_ERROR_M2)
|
|
- dev_dbg(&master->dev, "failed to send RSTDAA, ret=%i\n", ret);
|
|
- i3c_bus_maintenance_unlock(&master->bus);
|
|
+ for(int i=0; i<4; i++) {
|
|
+ i3c_gpio_switch_dimm_group(i);
|
|
+ usleep_range(2000, 2100);
|
|
+ i3c_bus_maintenance_lock(&master->bus);
|
|
+ /* Disable all slave events before starting DAA. */
|
|
+ ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
|
|
+ I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
|
|
+ I3C_CCC_EVENT_HJ);
|
|
+ if (ret && ret != I3C_ERROR_M2)
|
|
+ dev_dbg(&master->dev, "failed to send DISEC, ret=%i\n", ret);
|
|
+
|
|
+ /*
|
|
+ * Reset all dynamic address that may have been assigned before
|
|
+ * (assigned by the bootloader for example).
|
|
+ */
|
|
+ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
|
|
+ if (ret && ret != I3C_ERROR_M2)
|
|
+ dev_dbg(&master->dev, "failed to send RSTDAA, ret=%i\n", ret);
|
|
+
|
|
+ i3c_bus_maintenance_unlock(&master->bus);
|
|
+ usleep_range(2000, 2100);
|
|
+ }
|
|
|
|
if (master->ops->bus_cleanup)
|
|
master->ops->bus_cleanup(master);
|
|
|
|
i3c_master_detach_free_devs(master);
|
|
+
|
|
+ i3c_gpio_switch_free();
|
|
}
|
|
|
|
static void i3c_master_attach_boardinfo(struct i3c_dev_desc *i3cdev)
|
|
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
|
|
index 22bcf638e5b1..6bf882d21a1e 100644
|
|
--- a/include/linux/i3c/master.h
|
|
+++ b/include/linux/i3c/master.h
|
|
@@ -690,4 +690,15 @@ void i3c_master_queue_ibi(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot);
|
|
|
|
struct i3c_ibi_slot *i3c_master_get_free_ibi_slot(struct i3c_dev_desc *dev);
|
|
|
|
+#define SEL_CPU0_I3C_DDR_GPIO 921
|
|
+#define SEL_CPU1_I3C_DDR_GPIO 922
|
|
+#define I3C_SPD_BMC_MUX0_SEL 1004
|
|
+#define I3C_SPD_BMC_MUX0_EN 852
|
|
+#define I3C_SPD_BMC_MUX1_SEL 1005
|
|
+#define I3C_SPD_BMC_MUX1_EN 853
|
|
+#define I3C_SPD_BMC_MUX2_SEL 1006
|
|
+#define I3C_SPD_BMC_MUX2_EN 854
|
|
+#define I3C_SPD_BMC_MUX3_SEL 1007
|
|
+#define I3C_SPD_BMC_MUX3_EN 909
|
|
+
|
|
#endif /* I3C_MASTER_H */
|
|
--
|
|
2.34.1
|
|
|