]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
i2c: stm32f7: add PM_SLEEP suspend/resume support
authorAlain Volmat <alain.volmat@st.com>
Mon, 6 Jan 2020 13:28:32 +0000 (14:28 +0100)
committerWolfram Sang <wsa@the-dreams.de>
Thu, 30 Jan 2020 08:34:26 +0000 (09:34 +0100)
Backup/restore I2C registers as part of the suspend/resume
handlers. The device is marked as suspended to ensure that
transfers are rejected during the suspended period.

Signed-off-by: Pierre-Yves MORDRET <pierre-yves.mordret@st.com>
Signed-off-by: Alain Volmat <alain.volmat@st.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
drivers/i2c/busses/i2c-stm32f7.c

index b2634afe066d33d6f18eae3a1fa5e25a122977f7..5c3e8ac6ad928d5503afbf5fb93c76b2fd290213 100644 (file)
 
 #define STM32F7_AUTOSUSPEND_DELAY              (HZ / 100)
 
+/**
+ * struct stm32f7_i2c_regs - i2c f7 registers backup
+ * @cr1: Control register 1
+ * @cr2: Control register 2
+ * @oar1: Own address 1 register
+ * @oar2: Own address 2 register
+ * @pecr: PEC register
+ * @tmgr: Timing register
+ */
+struct stm32f7_i2c_regs {
+       u32 cr1;
+       u32 cr2;
+       u32 oar1;
+       u32 oar2;
+       u32 pecr;
+       u32 tmgr;
+};
+
 /**
  * struct stm32f7_i2c_spec - private i2c specification timing
  * @rate: I2C bus speed (Hz)
@@ -276,6 +294,7 @@ struct stm32f7_i2c_msg {
  * @timing: I2C computed timings
  * @slave: list of slave devices registered on the I2C bus
  * @slave_running: slave device currently used
+ * @backup_regs: backup of i2c controller registers (for suspend/resume)
  * @slave_dir: transfer direction for the current slave device
  * @master_mode: boolean to know in which mode the I2C is running (master or
  * slave)
@@ -298,6 +317,7 @@ struct stm32f7_i2c_dev {
        struct stm32f7_i2c_timings timing;
        struct i2c_client *slave[STM32F7_I2C_MAX_SLAVE];
        struct i2c_client *slave_running;
+       struct stm32f7_i2c_regs backup_regs;
        u32 slave_dir;
        bool master_mode;
        struct stm32_i2c_dma *dma;
@@ -2027,8 +2047,7 @@ static int stm32f7_i2c_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int stm32f7_i2c_runtime_suspend(struct device *dev)
+static int __maybe_unused stm32f7_i2c_runtime_suspend(struct device *dev)
 {
        struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
 
@@ -2038,7 +2057,7 @@ static int stm32f7_i2c_runtime_suspend(struct device *dev)
        return 0;
 }
 
-static int stm32f7_i2c_runtime_resume(struct device *dev)
+static int __maybe_unused stm32f7_i2c_runtime_resume(struct device *dev)
 {
        struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
        int ret;
@@ -2053,11 +2072,101 @@ static int stm32f7_i2c_runtime_resume(struct device *dev)
 
        return 0;
 }
-#endif
+
+static int __maybe_unused
+stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev)
+{
+       int ret;
+       struct stm32f7_i2c_regs *backup_regs = &i2c_dev->backup_regs;
+
+       ret = pm_runtime_get_sync(i2c_dev->dev);
+       if (ret < 0)
+               return ret;
+
+       backup_regs->cr1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR1);
+       backup_regs->cr2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR2);
+       backup_regs->oar1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR1);
+       backup_regs->oar2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR2);
+       backup_regs->pecr = readl_relaxed(i2c_dev->base + STM32F7_I2C_PECR);
+       backup_regs->tmgr = readl_relaxed(i2c_dev->base + STM32F7_I2C_TIMINGR);
+
+       pm_runtime_put_sync(i2c_dev->dev);
+
+       return ret;
+}
+
+static int __maybe_unused
+stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev)
+{
+       u32 cr1;
+       int ret;
+       struct stm32f7_i2c_regs *backup_regs = &i2c_dev->backup_regs;
+
+       ret = pm_runtime_get_sync(i2c_dev->dev);
+       if (ret < 0)
+               return ret;
+
+       cr1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR1);
+       if (cr1 & STM32F7_I2C_CR1_PE)
+               stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
+                                    STM32F7_I2C_CR1_PE);
+
+       writel_relaxed(backup_regs->tmgr, i2c_dev->base + STM32F7_I2C_TIMINGR);
+       writel_relaxed(backup_regs->cr1 & ~STM32F7_I2C_CR1_PE,
+                      i2c_dev->base + STM32F7_I2C_CR1);
+       if (backup_regs->cr1 & STM32F7_I2C_CR1_PE)
+               stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
+                                    STM32F7_I2C_CR1_PE);
+       writel_relaxed(backup_regs->cr2, i2c_dev->base + STM32F7_I2C_CR2);
+       writel_relaxed(backup_regs->oar1, i2c_dev->base + STM32F7_I2C_OAR1);
+       writel_relaxed(backup_regs->oar2, i2c_dev->base + STM32F7_I2C_OAR2);
+       writel_relaxed(backup_regs->pecr, i2c_dev->base + STM32F7_I2C_PECR);
+
+       pm_runtime_put_sync(i2c_dev->dev);
+
+       return ret;
+}
+
+static int __maybe_unused stm32f7_i2c_suspend(struct device *dev)
+{
+       struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
+       int ret;
+
+       i2c_mark_adapter_suspended(&i2c_dev->adap);
+       ret = stm32f7_i2c_regs_backup(i2c_dev);
+       if (ret < 0) {
+               i2c_mark_adapter_resumed(&i2c_dev->adap);
+               return ret;
+       }
+
+       pinctrl_pm_select_sleep_state(dev);
+       pm_runtime_force_suspend(dev);
+
+       return 0;
+}
+
+static int __maybe_unused stm32f7_i2c_resume(struct device *dev)
+{
+       struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
+       int ret;
+
+       ret = pm_runtime_force_resume(dev);
+       if (ret < 0)
+               return ret;
+       pinctrl_pm_select_default_state(dev);
+
+       ret = stm32f7_i2c_regs_restore(i2c_dev);
+       if (ret < 0)
+               return ret;
+       i2c_mark_adapter_resumed(&i2c_dev->adap);
+
+       return 0;
+}
 
 static const struct dev_pm_ops stm32f7_i2c_pm_ops = {
        SET_RUNTIME_PM_OPS(stm32f7_i2c_runtime_suspend,
                           stm32f7_i2c_runtime_resume, NULL)
+       SET_SYSTEM_SLEEP_PM_OPS(stm32f7_i2c_suspend, stm32f7_i2c_resume)
 };
 
 static const struct of_device_id stm32f7_i2c_match[] = {