From 136ea6342d8de89ab6166dfce0ba20f67aabc0d3 Mon Sep 17 00:00:00 2001 From: wangjue 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 --- 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 #include #include +#include +#include #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