Merge tag 'acpi-4.20-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 30 Oct 2018 16:15:31 +0000 (09:15 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 30 Oct 2018 16:15:31 +0000 (09:15 -0700)
Pull more ACPI updates from Rafael Wysocki:
 "Rework the handling of the P-unit semaphore on Intel Baytrail and
  Cherrytrail systems to avoid race conditions and excessive overhead
  related to it (Hans de Goede)"

* tag 'acpi-4.20-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  ACPI / PMIC: xpower: Add depends on IOSF_MBI to Kconfig entry
  i2c: designware: Cleanup bus lock handling
  ACPI / PMIC: xpower: Block P-Unit I2C access during read-modify-write
  x86: baytrail/cherrytrail: Rework and move P-Unit PMIC bus semaphore code

1  2 
drivers/i2c/busses/i2c-designware-baytrail.c
drivers/i2c/busses/i2c-designware-common.c
drivers/i2c/busses/i2c-designware-core.h
drivers/i2c/busses/i2c-designware-platdrv.c

index 9ca1feaba98f8afa0ee9fc4e9eb3bc85caab5357,971b5cde7a93a1d42caf579878548a08c030c9e7..33da07d6449475317385bcc3b72eede8fd52d561
   * Intel BayTrail PMIC I2C bus semaphore implementaion
   * Copyright (c) 2014, Intel Corporation.
   */
- #include <linux/delay.h>
  #include <linux/device.h>
  #include <linux/acpi.h>
  #include <linux/i2c.h>
  #include <linux/interrupt.h>
- #include <linux/pm_qos.h>
  
  #include <asm/iosf_mbi.h>
  
  #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;
- }
- 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));
- }
  int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev)
  {
        acpi_status status;
                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;
 +      dev->shared_with_punit = 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);
- }
index 36271cd7534283c847e5116701c14fd2847f32e1,7d50f230cd37c44dd0ab73b8e0e0a70850f8a98b..a4730111d290317b246e29b410a82f2ec20630d4
@@@ -201,8 -201,6 +201,8 @@@ int i2c_dw_set_sda_hold(struct dw_i2c_d
                dev_dbg(dev->dev, "SDA Hold Time TX:RX = %d:%d\n",
                        dev->sda_hold_time & ~(u32)DW_IC_SDA_HOLD_RX_MASK,
                        dev->sda_hold_time >> DW_IC_SDA_HOLD_RX_SHIFT);
 +      } else if (dev->set_sda_hold_time) {
 +              dev->set_sda_hold_time(dev);
        } else if (dev->sda_hold_time) {
                dev_warn(dev->dev,
                        "Hardware too old to adjust SDA hold time.\n");
@@@ -269,7 -267,7 +269,7 @@@ int i2c_dw_acquire_lock(struct dw_i2c_d
        if (!dev->acquire_lock)
                return 0;
  
-       ret = dev->acquire_lock(dev);
+       ret = dev->acquire_lock();
        if (!ret)
                return 0;
  
  void i2c_dw_release_lock(struct dw_i2c_dev *dev)
  {
        if (dev->release_lock)
-               dev->release_lock(dev);
+               dev->release_lock();
  }
  
  /*
index 9ec8394f47878e9058dc7b467ba0e044baef6394,152bf56d84044d4e4b28c8ca66a43c6ff18db6fe..b4a0b2b99a7821da40a21c396442436a41fedc07
@@@ -10,7 -10,6 +10,6 @@@
   */
  
  #include <linux/i2c.h>
- #include <linux/pm_qos.h>
  
  #define DW_IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C |                   \
                                        I2C_FUNC_SMBUS_BYTE |           \
   * @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
 + * @shared_with_punit: true if this bus is shared with the SoCs PUNIT
   * @disable: function to disable the controller
   * @disable_int: function to disable all interrupts
   * @init: function to initialize the I2C hardware
  struct dw_i2c_dev {
        struct device           *dev;
        void __iomem            *base;
 +      void __iomem            *ext;
        struct completion       cmd_complete;
        struct clk              *clk;
        struct reset_control    *rst;
        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;
 +      bool                    shared_with_punit;
        void                    (*disable)(struct dw_i2c_dev *dev);
        void                    (*disable_int)(struct dw_i2c_dev *dev);
        int                     (*init)(struct dw_i2c_dev *dev);
 +      int                     (*set_sda_hold_time)(struct dw_i2c_dev *dev);
        int                     mode;
        struct i2c_bus_recovery_info rinfo;
  };
  #define ACCESS_SWAP           0x00000001
  #define ACCESS_16BIT          0x00000002
  #define ACCESS_INTR_MASK      0x00000004
 +#define ACCESS_NO_IRQ_SUSPEND 0x00000008
  
  #define MODEL_CHERRYTRAIL     0x00000100
 +#define MODEL_MSCC_OCELOT     0x00000200
 +#define MODEL_MASK            0x00000f00
  
  u32 dw_readl(struct dw_i2c_dev *dev, int offset);
  void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset);
@@@ -322,8 -314,6 +319,6 @@@ static inline int i2c_dw_probe_slave(st
  
  #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
index 997bbb3d925f51da78f240d258468340e1d8ca50,a14fb5f933ac2efb37e62dd3adc573d95371e8ab..9eaac3be1f63da4e4db4e7759042bd87de86ba2e
@@@ -85,6 -85,10 +85,6 @@@ static int dw_i2c_acpi_configure(struc
        struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
        struct i2c_timings *t = &dev->timings;
        u32 ss_ht = 0, fp_ht = 0, hs_ht = 0, fs_ht = 0;
 -      acpi_handle handle = ACPI_HANDLE(&pdev->dev);
 -      const struct acpi_device_id *id;
 -      struct acpi_device *adev;
 -      const char *uid;
  
        dev->adapter.nr = -1;
        dev->tx_fifo_depth = 32;
                break;
        }
  
 -      id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
 -      if (id && id->driver_data)
 -              dev->flags |= (u32)id->driver_data;
 -
 -      if (acpi_bus_get_device(handle, &adev))
 -              return -ENODEV;
 -
 -      /*
 -       * Cherrytrail I2C7 gets used for the PMIC which gets accessed
 -       * through ACPI opregions during late suspend / early resume
 -       * disable pm for it.
 -       */
 -      uid = adev->pnp.unique_id;
 -      if ((dev->flags & MODEL_CHERRYTRAIL) && !strcmp(uid, "7"))
 -              dev->pm_disabled = true;
 -
        return 0;
  }
  
@@@ -123,8 -143,8 +123,8 @@@ static const struct acpi_device_id dw_i
        { "INT33C3", 0 },
        { "INT3432", 0 },
        { "INT3433", 0 },
 -      { "80860F41", 0 },
 -      { "808622C1", MODEL_CHERRYTRAIL },
 +      { "80860F41", ACCESS_NO_IRQ_SUSPEND },
 +      { "808622C1", ACCESS_NO_IRQ_SUSPEND | MODEL_CHERRYTRAIL },
        { "AMD0010", ACCESS_INTR_MASK },
        { "AMDI0010", ACCESS_INTR_MASK },
        { "AMDI0510", 0 },
@@@ -141,51 -161,6 +141,51 @@@ static inline int dw_i2c_acpi_configure
  }
  #endif
  
 +#ifdef CONFIG_OF
 +#define MSCC_ICPU_CFG_TWI_DELAY               0x0
 +#define MSCC_ICPU_CFG_TWI_DELAY_ENABLE        BIT(0)
 +#define MSCC_ICPU_CFG_TWI_SPIKE_FILTER        0x4
 +
 +static int mscc_twi_set_sda_hold_time(struct dw_i2c_dev *dev)
 +{
 +      writel((dev->sda_hold_time << 1) | MSCC_ICPU_CFG_TWI_DELAY_ENABLE,
 +             dev->ext + MSCC_ICPU_CFG_TWI_DELAY);
 +
 +      return 0;
 +}
 +
 +static int dw_i2c_of_configure(struct platform_device *pdev)
 +{
 +      struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
 +      struct resource *mem;
 +
 +      switch (dev->flags & MODEL_MASK) {
 +      case MODEL_MSCC_OCELOT:
 +              mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 +              dev->ext = devm_ioremap_resource(&pdev->dev, mem);
 +              if (!IS_ERR(dev->ext))
 +                      dev->set_sda_hold_time = mscc_twi_set_sda_hold_time;
 +              break;
 +      default:
 +              break;
 +      }
 +
 +      return 0;
 +}
 +
 +static const struct of_device_id dw_i2c_of_match[] = {
 +      { .compatible = "snps,designware-i2c", },
 +      { .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT },
 +      {},
 +};
 +MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
 +#else
 +static inline int dw_i2c_of_configure(struct platform_device *pdev)
 +{
 +      return -ENODEV;
 +}
 +#endif
 +
  static void i2c_dw_configure_master(struct dw_i2c_dev *dev)
  {
        struct i2c_timings *t = &dev->timings;
@@@ -246,7 -221,7 +246,7 @@@ static void dw_i2c_plat_pm_cleanup(stru
  {
        pm_runtime_disable(dev->dev);
  
 -      if (dev->pm_disabled)
 +      if (dev->shared_with_punit)
                pm_runtime_put_noidle(dev->dev);
  }
  
@@@ -316,11 -291,6 +316,11 @@@ static int dw_i2c_plat_probe(struct pla
        else
                t->bus_freq_hz = 400000;
  
 +      dev->flags |= (uintptr_t)device_get_match_data(&pdev->dev);
 +
 +      if (pdev->dev.of_node)
 +              dw_i2c_of_configure(pdev);
 +
        if (has_acpi_companion(&pdev->dev))
                dw_i2c_acpi_configure(pdev);
  
        pm_runtime_use_autosuspend(&pdev->dev);
        pm_runtime_set_active(&pdev->dev);
  
 -      if (dev->pm_disabled)
 +      if (dev->shared_with_punit)
                pm_runtime_get_noresume(&pdev->dev);
  
        pm_runtime_enable(&pdev->dev);
@@@ -418,11 -388,17 +418,9 @@@ static int dw_i2c_plat_remove(struct pl
        if (!IS_ERR_OR_NULL(dev->rst))
                reset_control_assert(dev->rst);
  
-       i2c_dw_remove_lock_support(dev);
        return 0;
  }
  
 -#ifdef CONFIG_OF
 -static const struct of_device_id dw_i2c_of_match[] = {
 -      { .compatible = "snps,designware-i2c", },
 -      {},
 -};
 -MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
 -#endif
 -
  #ifdef CONFIG_PM_SLEEP
  static int dw_i2c_plat_prepare(struct device *dev)
  {
@@@ -456,7 -432,7 +454,7 @@@ static int dw_i2c_plat_suspend(struct d
  {
        struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
  
 -      if (i_dev->pm_disabled)
 +      if (i_dev->shared_with_punit)
                return 0;
  
        i_dev->disable(i_dev);
@@@ -469,7 -445,7 +467,7 @@@ static int dw_i2c_plat_resume(struct de
  {
        struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
  
 -      if (!i_dev->pm_disabled)
 +      if (!i_dev->shared_with_punit)
                i2c_dw_prepare_clk(i_dev, true);
  
        i_dev->init(i_dev);