Files
OpenBMC/meta-luxshare/meta-bhs/recipes-kernel/linux/linux-aspeed/0001-Add-sw-workaround-to-let-all-dimm-spd-devices-to-rec.patch
2026-04-23 17:07:55 +08:00

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