From e09db3d241f8d594381b6e5f2ca0f72ffcce478a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 11 Oct 2018 16:29:09 +0200 Subject: x86: baytrail/cherrytrail: Rework and move P-Unit PMIC bus semaphore code On some BYT/CHT systems the SoC's P-Unit shares the I2C bus with the kernel. The P-Unit has a semaphore for the PMIC bus which we can take to block it from accessing the shared bus while the kernel wants to access it. Currently we have the I2C-controller driver acquiring and releasing the semaphore around each I2C transfer. There are 2 problems with this: 1) PMIC accesses often come in the form of a read-modify-write on one of the PMIC registers, we currently release the P-Unit's PMIC bus semaphore between the read and the write. If the P-Unit modifies the register during this window?, then we end up overwriting the P-Unit's changes. I believe that this is mostly an academic problem, but I'm not sure. 2) To safely access the shared I2C bus, we need to do 3 things: a) Notify the GPU driver that we are starting a window in which it may not access the P-Unit, since the P-Unit seems to ignore the semaphore for explicit power-level requests made by the GPU driver b) Make a pm_qos request to force all CPU cores out of C6/C7 since entering C6/C7 while we hold the semaphore hangs the SoC c) Finally take the P-Unit's PMIC bus semaphore All 3 these steps together are somewhat expensive, so ideally if we have a bunch of i2c transfers grouped together we only do this once for the entire group. Taking the read-modify-write on a PMIC register as example then ideally we would only do all 3 steps once at the beginning and undo all 3 steps once at the end. For this we need to be able to take the semaphore from within e.g. the PMIC opregion driver, yet we do not want to remove the taking of the semaphore from the I2C-controller driver, as that is still necessary to protect many other code-paths leading to accessing the shared I2C bus. This means that we first have the PMIC driver acquire the semaphore and then have the I2C controller driver trying to acquire it again. To make this possible this commit does the following: 1) Move the semaphore code from being private to the I2C controller driver into the generic iosf_mbi code, which already has other code to deal with the shared bus so that it can be accessed outside of the I2C bus driver. 2) Rework the code so that it can be called multiple times nested, while still blocking I2C accesses while e.g. the GPU driver has indicated the P-Unit needs the bus through a iosf_mbi_punit_acquire() call. Signed-off-by: Hans de Goede Acked-by: Jarkko Nikula Tested-by: Jarkko Nikula Acked-by: Wolfram Sang Signed-off-by: Rafael J. Wysocki --- drivers/i2c/busses/i2c-designware-baytrail.c | 125 +-------------------------- 1 file changed, 2 insertions(+), 123 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-designware-baytrail.c b/drivers/i2c/busses/i2c-designware-baytrail.c index a2a275cfc1f6..b2ba4de4e204 100644 --- a/drivers/i2c/busses/i2c-designware-baytrail.c +++ b/drivers/i2c/busses/i2c-designware-baytrail.c @@ -3,139 +3,23 @@ * Intel BayTrail PMIC I2C bus semaphore implementaion * Copyright (c) 2014, Intel Corporation. */ -#include #include #include #include #include -#include #include #include "i2c-designware-core.h" -#define SEMAPHORE_TIMEOUT 500 -#define PUNIT_SEMAPHORE 0x7 -#define PUNIT_SEMAPHORE_CHT 0x10e -#define PUNIT_SEMAPHORE_BIT BIT(0) -#define PUNIT_SEMAPHORE_ACQUIRE BIT(1) - -static unsigned long acquired; - -static u32 get_sem_addr(struct dw_i2c_dev *dev) -{ - if (dev->flags & MODEL_CHERRYTRAIL) - return PUNIT_SEMAPHORE_CHT; - else - return PUNIT_SEMAPHORE; -} - -static int get_sem(struct dw_i2c_dev *dev, u32 *sem) -{ - u32 addr = get_sem_addr(dev); - u32 data; - int ret; - - ret = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, addr, &data); - if (ret) { - dev_err(dev->dev, "iosf failed to read punit semaphore\n"); - return ret; - } - - *sem = data & PUNIT_SEMAPHORE_BIT; - - return 0; -} - -static void reset_semaphore(struct dw_i2c_dev *dev) -{ - if (iosf_mbi_modify(BT_MBI_UNIT_PMC, MBI_REG_READ, get_sem_addr(dev), - 0, PUNIT_SEMAPHORE_BIT)) - dev_err(dev->dev, "iosf failed to reset punit semaphore during write\n"); - - pm_qos_update_request(&dev->pm_qos, PM_QOS_DEFAULT_VALUE); - - iosf_mbi_call_pmic_bus_access_notifier_chain(MBI_PMIC_BUS_ACCESS_END, - NULL); - iosf_mbi_punit_release(); -} - static int baytrail_i2c_acquire(struct dw_i2c_dev *dev) { - u32 addr; - u32 sem = PUNIT_SEMAPHORE_ACQUIRE; - int ret; - unsigned long start, end; - - might_sleep(); - - if (!dev || !dev->dev) - return -ENODEV; - - if (!dev->release_lock) - return 0; - - iosf_mbi_punit_acquire(); - iosf_mbi_call_pmic_bus_access_notifier_chain(MBI_PMIC_BUS_ACCESS_BEGIN, - NULL); - - /* - * Disallow the CPU to enter C6 or C7 state, entering these states - * requires the punit to talk to the pmic and if this happens while - * we're holding the semaphore, the SoC hangs. - */ - pm_qos_update_request(&dev->pm_qos, 0); - - addr = get_sem_addr(dev); - - /* host driver writes to side band semaphore register */ - ret = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE, addr, sem); - if (ret) { - dev_err(dev->dev, "iosf punit semaphore request failed\n"); - goto out; - } - - /* host driver waits for bit 0 to be set in semaphore register */ - start = jiffies; - end = start + msecs_to_jiffies(SEMAPHORE_TIMEOUT); - do { - ret = get_sem(dev, &sem); - if (!ret && sem) { - acquired = jiffies; - dev_dbg(dev->dev, "punit semaphore acquired after %ums\n", - jiffies_to_msecs(jiffies - start)); - return 0; - } - - usleep_range(1000, 2000); - } while (time_before(jiffies, end)); - - dev_err(dev->dev, "punit semaphore timed out, resetting\n"); -out: - reset_semaphore(dev); - - ret = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, addr, &sem); - if (ret) - dev_err(dev->dev, "iosf failed to read punit semaphore\n"); - else - dev_err(dev->dev, "PUNIT SEM: %d\n", sem); - - WARN_ON(1); - - return -ETIMEDOUT; + return iosf_mbi_block_punit_i2c_access(); } static void baytrail_i2c_release(struct dw_i2c_dev *dev) { - if (!dev || !dev->dev) - return; - - if (!dev->acquire_lock) - return; - - reset_semaphore(dev); - dev_dbg(dev->dev, "punit semaphore held for %ums\n", - jiffies_to_msecs(jiffies - acquired)); + iosf_mbi_unblock_punit_i2c_access(); } int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) @@ -166,14 +50,9 @@ int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) dev->release_lock = baytrail_i2c_release; dev->pm_disabled = true; - pm_qos_add_request(&dev->pm_qos, PM_QOS_CPU_DMA_LATENCY, - PM_QOS_DEFAULT_VALUE); - return 0; } void i2c_dw_remove_lock_support(struct dw_i2c_dev *dev) { - if (dev->acquire_lock) - pm_qos_remove_request(&dev->pm_qos); } -- cgit v1.2.3 From 8afb46804dfa88bb86d65e13f3209372f3d912e3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 11 Oct 2018 16:29:11 +0200 Subject: i2c: designware: Cleanup bus lock handling Now that most of the special Bay- / Cherry-Trail bus lock handling has been moved to the iosf_mbi code we can simplify the remaining code a bit. Signed-off-by: Hans de Goede Acked-by: Jarkko Nikula Tested-by: Jarkko Nikula Acked-by: Wolfram Sang Signed-off-by: Rafael J. Wysocki --- drivers/i2c/busses/i2c-designware-baytrail.c | 18 ++---------------- drivers/i2c/busses/i2c-designware-common.c | 4 ++-- drivers/i2c/busses/i2c-designware-core.h | 9 ++------- drivers/i2c/busses/i2c-designware-platdrv.c | 2 -- 4 files changed, 6 insertions(+), 27 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-designware-baytrail.c b/drivers/i2c/busses/i2c-designware-baytrail.c index b2ba4de4e204..971b5cde7a93 100644 --- a/drivers/i2c/busses/i2c-designware-baytrail.c +++ b/drivers/i2c/busses/i2c-designware-baytrail.c @@ -12,16 +12,6 @@ #include "i2c-designware-core.h" -static int baytrail_i2c_acquire(struct dw_i2c_dev *dev) -{ - return iosf_mbi_block_punit_i2c_access(); -} - -static void baytrail_i2c_release(struct dw_i2c_dev *dev) -{ - iosf_mbi_unblock_punit_i2c_access(); -} - int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) { acpi_status status; @@ -46,13 +36,9 @@ int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) return -EPROBE_DEFER; dev_info(dev->dev, "I2C bus managed by PUNIT\n"); - dev->acquire_lock = baytrail_i2c_acquire; - dev->release_lock = baytrail_i2c_release; + dev->acquire_lock = iosf_mbi_block_punit_i2c_access; + dev->release_lock = iosf_mbi_unblock_punit_i2c_access; dev->pm_disabled = true; return 0; } - -void i2c_dw_remove_lock_support(struct dw_i2c_dev *dev) -{ -} diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index 69ec4a791f23..7d50f230cd37 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -267,7 +267,7 @@ int i2c_dw_acquire_lock(struct dw_i2c_dev *dev) if (!dev->acquire_lock) return 0; - ret = dev->acquire_lock(dev); + ret = dev->acquire_lock(); if (!ret) return 0; @@ -279,7 +279,7 @@ int i2c_dw_acquire_lock(struct dw_i2c_dev *dev) void i2c_dw_release_lock(struct dw_i2c_dev *dev) { if (dev->release_lock) - dev->release_lock(dev); + dev->release_lock(); } /* diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index e367b1af4ab2..152bf56d8404 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -10,7 +10,6 @@ */ #include -#include #define DW_IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \ I2C_FUNC_SMBUS_BYTE | \ @@ -209,7 +208,6 @@ * @fp_lcnt: fast plus LCNT value * @hs_hcnt: high speed HCNT value * @hs_lcnt: high speed LCNT value - * @pm_qos: pm_qos_request used while holding a hardware lock on the bus * @acquire_lock: function to acquire a hardware lock on the bus * @release_lock: function to release a hardware lock on the bus * @pm_disabled: true if power-management should be disabled for this i2c-bus @@ -262,9 +260,8 @@ struct dw_i2c_dev { u16 fp_lcnt; u16 hs_hcnt; u16 hs_lcnt; - struct pm_qos_request pm_qos; - int (*acquire_lock)(struct dw_i2c_dev *dev); - void (*release_lock)(struct dw_i2c_dev *dev); + int (*acquire_lock)(void); + void (*release_lock)(void); bool pm_disabled; void (*disable)(struct dw_i2c_dev *dev); void (*disable_int)(struct dw_i2c_dev *dev); @@ -317,8 +314,6 @@ static inline int i2c_dw_probe_slave(struct dw_i2c_dev *dev) { return -EINVAL; } #if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL) extern int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev); -extern void i2c_dw_remove_lock_support(struct dw_i2c_dev *dev); #else static inline int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) { return 0; } -static inline void i2c_dw_remove_lock_support(struct dw_i2c_dev *dev) {} #endif diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index b5750fd85125..a14fb5f933ac 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -388,8 +388,6 @@ static int dw_i2c_plat_remove(struct platform_device *pdev) if (!IS_ERR_OR_NULL(dev->rst)) reset_control_assert(dev->rst); - i2c_dw_remove_lock_support(dev); - return 0; } -- cgit v1.2.3