diff options
author | Hans de Goede <hdegoede@redhat.com> | 2017-02-10 13:27:56 +0300 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2017-03-02 17:46:33 +0300 |
commit | 086cb4afef45262806ee5bf26c34244e5867712c (patch) | |
tree | cf0a18a2be7cce0af7fe6a3f52adaeb0a55ba28d /drivers/i2c/busses/i2c-designware-baytrail.c | |
parent | e234ed2f06fad95680710976bcddae91d7fb7af9 (diff) | |
download | linux-086cb4afef45262806ee5bf26c34244e5867712c.tar.xz |
i2c: designware-baytrail: Disallow the CPU to enter C6 or C7 while holding the punit semaphore
On my cherrytrail tablet with axp288 pmic, just doing a bunch of repeated
reads from the pmic, e.g. "i2cdump -y 14 0x34" would lookup the tablet in
1 - 3 runs guaranteed.
This seems to be causes by the cpu trying to enter C6 or C7 while we hold
the punit bus semaphore, at which point everything just hangs.
Avoid this by disallowing the CPU to enter C6 or C7 before acquiring the
punit bus semaphore.
BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=109051
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Tested-by: Takashi Iwai <tiwai@suse.de>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Acked-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Acked-by: Wolfram Sang <wsa@the-dreams.de>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/20170210102802.20898-7-hdegoede@redhat.com
Diffstat (limited to 'drivers/i2c/busses/i2c-designware-baytrail.c')
-rw-r--r-- | drivers/i2c/busses/i2c-designware-baytrail.c | 24 |
1 files changed, 22 insertions, 2 deletions
diff --git a/drivers/i2c/busses/i2c-designware-baytrail.c b/drivers/i2c/busses/i2c-designware-baytrail.c index cf022226b693..650a700f215e 100644 --- a/drivers/i2c/busses/i2c-designware-baytrail.c +++ b/drivers/i2c/busses/i2c-designware-baytrail.c @@ -16,6 +16,7 @@ #include <linux/acpi.h> #include <linux/i2c.h> #include <linux/interrupt.h> +#include <linux/pm_qos.h> #include <asm/iosf_mbi.h> @@ -56,6 +57,8 @@ static void reset_semaphore(struct dw_i2c_dev *dev) data &= ~PUNIT_SEMAPHORE_BIT; if (iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE, PUNIT_SEMAPHORE, data)) dev_err(dev->dev, "iosf failed to reset punit semaphore during write\n"); + + pm_qos_update_request(&dev->pm_qos, PM_QOS_DEFAULT_VALUE); } static int baytrail_i2c_acquire(struct dw_i2c_dev *dev) @@ -72,11 +75,18 @@ static int baytrail_i2c_acquire(struct dw_i2c_dev *dev) if (!dev->release_lock) return 0; + /* + * 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); + /* host driver writes to side band semaphore register */ ret = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE, PUNIT_SEMAPHORE, sem); if (ret) { dev_err(dev->dev, "iosf punit semaphore request failed\n"); - return ret; + goto out; } /* host driver waits for bit 0 to be set in semaphore register */ @@ -95,6 +105,7 @@ static int baytrail_i2c_acquire(struct dw_i2c_dev *dev) } 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, PUNIT_SEMAPHORE, &sem); @@ -121,7 +132,7 @@ static void baytrail_i2c_release(struct dw_i2c_dev *dev) jiffies_to_msecs(jiffies - acquired)); } -int i2c_dw_eval_lock_support(struct dw_i2c_dev *dev) +int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) { acpi_status status; unsigned long long shared_host = 0; @@ -149,5 +160,14 @@ int i2c_dw_eval_lock_support(struct dw_i2c_dev *dev) dev->release_lock = baytrail_i2c_release; dev->pm_runtime_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); +} |