From 78d5e9e299e31bc2deaaa94a45bf8ea024f27e8c Mon Sep 17 00:00:00 2001 From: Jan Dabros Date: Tue, 8 Feb 2022 15:12:18 +0100 Subject: i2c: designware: Add AMD PSP I2C bus support Implement an I2C controller sharing mechanism between the host (kernel) and PSP co-processor on some platforms equipped with AMD Cezanne SoC. On these platforms we need to implement "software" i2c arbitration. Default arbitration owner is PSP and kernel asks for acquire as well as inform about release of the i2c bus via mailbox mechanism. +---------+ <- ACQUIRE | | +---------| CPU |\ | | | \ +----------+ SDA | +---------+ \ | |------- MAILBOX +--> | I2C-DW | SCL | +---------+ | |------- | | | +----------+ +---------| PSP | <- ACK | | +---------+ +---------+ <- RELEASE | | +---------| CPU | | | | +----------+ SDA | +---------+ | |------- MAILBOX +--> | I2C-DW | SCL | +---------+ / | |------- | | | / +----------+ +---------| PSP |/ <- ACK | | +---------+ The solution is similar to i2c-designware-baytrail.c implementation, where we are using a generic i2c-designware-* driver with a small "wrapper". In contrary to baytrail semaphore implementation, beside internal acquire_lock() and release_lock() methods we are also applying quirks to lock_bus() and unlock_bus() global adapter methods. With this in place all i2c clients drivers may lock i2c bus for a desired number of i2c transactions (e.g. write-wait-read) without being aware of that such bus is shared with another entity. Modify i2c_dw_probe_lock_support() to select correct semaphore implementation at runtime, since now we have more than one available. Configure new matching ACPI ID "AMDI0019" and register ARBITRATION_SEMAPHORE flag in order to distinguish setup with PSP arbitration. Add myself as a reviewer for I2C DesignWare in order to help with reviewing and testing possible changes touching new i2c-designware-amdpsp.c module. Signed-off-by: Jan Dabros Reviewed-by: Andy Shevchenko Acked-by: Jarkko Nikula Tested-by: Jarkko Nikula [wsa: removed unneeded blank line and curly braces] Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-platdrv.c | 60 +++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'drivers/i2c/busses/i2c-designware-platdrv.c') diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 2bd81abc86f6..9973ac894a51 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -50,6 +50,7 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = { { "808622C1", ACCESS_NO_IRQ_SUSPEND }, { "AMD0010", ACCESS_INTR_MASK }, { "AMDI0010", ACCESS_INTR_MASK }, + { "AMDI0019", ACCESS_INTR_MASK | ARBITRATION_SEMAPHORE }, { "AMDI0510", 0 }, { "APMC0D0F", 0 }, { "HISI02A1", 0 }, @@ -204,6 +205,63 @@ static const struct dmi_system_id dw_i2c_hwmon_class_dmi[] = { { } /* terminate list */ }; +static const struct i2c_dw_semaphore_callbacks i2c_dw_semaphore_cb_table[] = { +#ifdef CONFIG_I2C_DESIGNWARE_BAYTRAIL + { + .probe = i2c_dw_baytrail_probe_lock_support, + }, +#endif +#ifdef CONFIG_I2C_DESIGNWARE_AMDPSP + { + .probe = i2c_dw_amdpsp_probe_lock_support, + .remove = i2c_dw_amdpsp_remove_lock_support, + }, +#endif + {} +}; + +static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) +{ + const struct i2c_dw_semaphore_callbacks *ptr; + int i = 0; + int ret; + + ptr = i2c_dw_semaphore_cb_table; + + dev->semaphore_idx = -1; + + while (ptr->probe) { + ret = ptr->probe(dev); + if (ret) { + /* + * If there is no semaphore device attached to this + * controller, we shouldn't abort general i2c_controller + * probe. + */ + if (ret != -ENODEV) + return ret; + + i++; + ptr++; + continue; + } + + dev->semaphore_idx = i; + break; + } + + return 0; +} + +static void i2c_dw_remove_lock_support(struct dw_i2c_dev *dev) +{ + if (dev->semaphore_idx < 0) + return; + + if (i2c_dw_semaphore_cb_table[dev->semaphore_idx].remove) + i2c_dw_semaphore_cb_table[dev->semaphore_idx].remove(dev); +} + static int dw_i2c_plat_probe(struct platform_device *pdev) { struct i2c_adapter *adap; @@ -334,6 +392,8 @@ static int dw_i2c_plat_remove(struct platform_device *pdev) pm_runtime_put_sync(&pdev->dev); dw_i2c_plat_pm_cleanup(dev); + i2c_dw_remove_lock_support(dev); + reset_control_assert(dev->rst); return 0; -- cgit From c57813b8b288dcb5e158993cfdff31a60bc24955 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 23 Feb 2022 14:48:38 +0100 Subject: i2c: designware: Lock the adapter while setting the suspended flag Lock the adapter while setting the suspended flag, to ensure that other locked code always sees the change immediately, rather then possibly using a stale value. This involves splitting the suspend/resume callbacks into separate runtime and normal suspend/resume calls. This is necessary because i2c_dw_xfer() will get called by the i2c-core with the adapter locked and it in turn calls the runtime-resume callback through pm_runtime_get_sync(). So the runtime versions of the suspend/resume callbacks cannot take the adapter-lock. Note this patch simply makes the runtime suspend/resume callbacks not deal with the suspended flag at all. During runtime the pm_runtime_get_sync() from i2c_dw_xfer() will always ensure that the adapter is resumed when necessary. The suspended flag check is only necessary to check proper suspend/resume ordering during normal suspend/resume which makes the pm_runtime_get_sync() call a no-op. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Acked-by: Jarkko Nikula Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-platdrv.c | 31 ++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) (limited to 'drivers/i2c/busses/i2c-designware-platdrv.c') diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 9973ac894a51..f0703286d93b 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -428,12 +428,10 @@ static void dw_i2c_plat_complete(struct device *dev) #endif #ifdef CONFIG_PM -static int dw_i2c_plat_suspend(struct device *dev) +static int dw_i2c_plat_runtime_suspend(struct device *dev) { struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); - i_dev->suspended = true; - if (i_dev->shared_with_punit) return 0; @@ -443,7 +441,18 @@ static int dw_i2c_plat_suspend(struct device *dev) return 0; } -static int dw_i2c_plat_resume(struct device *dev) +static int dw_i2c_plat_suspend(struct device *dev) +{ + struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); + + i2c_lock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); + i_dev->suspended = true; + i2c_unlock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); + + return dw_i2c_plat_runtime_suspend(dev); +} + +static int dw_i2c_plat_runtime_resume(struct device *dev) { struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); @@ -451,7 +460,19 @@ static int dw_i2c_plat_resume(struct device *dev) i2c_dw_prepare_clk(i_dev, true); i_dev->init(i_dev); + + return 0; +} + +static int dw_i2c_plat_resume(struct device *dev) +{ + struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); + + dw_i2c_plat_runtime_resume(dev); + + i2c_lock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); i_dev->suspended = false; + i2c_unlock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); return 0; } @@ -460,7 +481,7 @@ static const struct dev_pm_ops dw_i2c_dev_pm_ops = { .prepare = dw_i2c_plat_prepare, .complete = dw_i2c_plat_complete, SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume) - SET_RUNTIME_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume, NULL) + SET_RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend, dw_i2c_plat_runtime_resume, NULL) }; #define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops) -- cgit From 80704a84a9f8276337a83c0c3ce641af3b27c79c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 23 Feb 2022 14:48:39 +0100 Subject: i2c: designware: Use the i2c_mark_adapter_suspended/resumed() helpers Use the i2c_mark_adapter_suspended/resumed() i2c-core helpers and rely on the i2c-core's suspended checking instead of using DIY code. Signed-off-by: Hans de Goede Acked-by: Jarkko Nikula Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-platdrv.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers/i2c/busses/i2c-designware-platdrv.c') diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index f0703286d93b..116a297d1f6b 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -445,9 +445,7 @@ static int dw_i2c_plat_suspend(struct device *dev) { struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); - i2c_lock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); - i_dev->suspended = true; - i2c_unlock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); + i2c_mark_adapter_suspended(&i_dev->adapter); return dw_i2c_plat_runtime_suspend(dev); } @@ -469,10 +467,7 @@ static int dw_i2c_plat_resume(struct device *dev) struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); dw_i2c_plat_runtime_resume(dev); - - i2c_lock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); - i_dev->suspended = false; - i2c_unlock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); + i2c_mark_adapter_resumed(&i_dev->adapter); return 0; } -- cgit From d0583229bcf5090495d0621892d5da93d9fb6e3c Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Thu, 3 Mar 2022 12:17:13 -0700 Subject: i2c: designware: Mark dw_i2c_plat_{suspend,resume}() as __maybe_unused When CONFIG_PM is set but CONFIG_PM_SLEEP is not, two compiler warnings appear: drivers/i2c/busses/i2c-designware-platdrv.c:444:12: error: unused function 'dw_i2c_plat_suspend' [-Werror,-Wunused-function] static int dw_i2c_plat_suspend(struct device *dev) ^ drivers/i2c/busses/i2c-designware-platdrv.c:465:12: error: unused function 'dw_i2c_plat_resume' [-Werror,-Wunused-function] static int dw_i2c_plat_resume(struct device *dev) ^ 2 errors generated. These functions are only used in SET_LATE_SYSTEM_SLEEP_PM_OPS(), which is defined as empty when CONFIG_PM_SLEEP is not defined. Mark the functions as __maybe_unused to make it clear that these functions might be unused in this configuration. Fixes: c57813b8b288 ("i2c: designware: Lock the adapter while setting the suspended flag") Signed-off-by: Nathan Chancellor Reviewed-by: Hans de Goede Reviewed-by: Andy Shevchenko Acked-by: Jarkko Nikula Acked-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-platdrv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/i2c/busses/i2c-designware-platdrv.c') diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 116a297d1f6b..70ade5306e45 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -441,7 +441,7 @@ static int dw_i2c_plat_runtime_suspend(struct device *dev) return 0; } -static int dw_i2c_plat_suspend(struct device *dev) +static int __maybe_unused dw_i2c_plat_suspend(struct device *dev) { struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); @@ -462,7 +462,7 @@ static int dw_i2c_plat_runtime_resume(struct device *dev) return 0; } -static int dw_i2c_plat_resume(struct device *dev) +static int __maybe_unused dw_i2c_plat_resume(struct device *dev) { struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); -- cgit