From 656c7df3520f4422b871a4e69c68e6a9daf7ba61 Mon Sep 17 00:00:00 2001 From: Romuald JEANNE Date: Tue, 13 Nov 2018 12:18:06 +0100 Subject: [PATCH 04/52] ARM: stm32mp1-r0-rc1: I2C --- drivers/i2c/busses/i2c-stm32f7.c | 369 +++++++++++++++++++++++++++++++++------ 1 file changed, 320 insertions(+), 49 deletions(-) diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 62d023e..c1cbf93 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -21,12 +21,17 @@ #include #include #include +#include #include #include #include #include #include #include +#include +#include +#include +#include #include #include @@ -46,6 +51,7 @@ /* STM32F7 I2C control 1 */ #define STM32F7_I2C_CR1_PECEN BIT(23) +#define STM32F7_I2C_CR1_WUPEN BIT(18) #define STM32F7_I2C_CR1_SBC BIT(16) #define STM32F7_I2C_CR1_RXDMAEN BIT(15) #define STM32F7_I2C_CR1_TXDMAEN BIT(14) @@ -163,6 +169,26 @@ #define STM32F7_SCLH_MAX BIT(8) #define STM32F7_SCLL_MAX BIT(8) +#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 + * @timingr: 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) @@ -259,6 +285,8 @@ struct stm32f7_i2c_msg { * struct stm32f7_i2c_dev - private data of the controller * @adap: I2C adapter for this controller * @dev: device for this controller + * @irq_event: interrupt event line for the controller + * @irq_wakeup: interrupt wakeup line for the controller * @base: virtual memory area * @complete: completion of I2C message * @clk: hw i2c clock @@ -276,11 +304,14 @@ struct stm32f7_i2c_msg { * slave) * @dma: dma data * @use_dma: boolean to know if dma is used in the current transfer + * @regmap: holds SYSCFG phandle for Fast Mode Plus bits */ struct stm32f7_i2c_dev { struct i2c_adapter adap; struct device *dev; void __iomem *base; + int irq_event; + int irq_wakeup; struct completion complete; struct clk *clk; int speed; @@ -292,10 +323,12 @@ 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 regs; u32 slave_dir; bool master_mode; struct stm32_i2c_dma *dma; bool use_dma; + struct regmap *regmap; }; /** @@ -1545,15 +1578,13 @@ static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap, i2c_dev->msg_id = 0; f7_msg->smbus = false; - ret = clk_enable(i2c_dev->clk); - if (ret) { - dev_err(i2c_dev->dev, "Failed to enable clock\n"); + ret = pm_runtime_get_sync(i2c_dev->dev); + if (ret < 0) return ret; - } ret = stm32f7_i2c_wait_free_bus(i2c_dev); if (ret) - goto clk_free; + goto pm_free; stm32f7_i2c_xfer_msg(i2c_dev, msgs); @@ -1569,8 +1600,9 @@ static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap, ret = -ETIMEDOUT; } -clk_free: - clk_disable(i2c_dev->clk); +pm_free: + pm_runtime_mark_last_busy(i2c_dev->dev); + pm_runtime_put_autosuspend(i2c_dev->dev); return (ret < 0) ? ret : num; } @@ -1592,39 +1624,37 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, f7_msg->read_write = read_write; f7_msg->smbus = true; - ret = clk_enable(i2c_dev->clk); - if (ret) { - dev_err(i2c_dev->dev, "Failed to enable clock\n"); + ret = pm_runtime_get_sync(dev); + if (ret < 0) return ret; - } ret = stm32f7_i2c_wait_free_bus(i2c_dev); if (ret) - goto clk_free; + goto pm_free; ret = stm32f7_i2c_smbus_xfer_msg(i2c_dev, flags, command, data); if (ret) - goto clk_free; + goto pm_free; timeout = wait_for_completion_timeout(&i2c_dev->complete, i2c_dev->adap.timeout); ret = f7_msg->result; if (ret) - goto clk_free; + goto pm_free; if (!timeout) { dev_dbg(dev, "Access to slave 0x%x timed out\n", f7_msg->addr); if (i2c_dev->use_dma) dmaengine_terminate_all(dma->chan_using); ret = -ETIMEDOUT; - goto clk_free; + goto pm_free; } /* Check PEC */ if ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && read_write) { ret = stm32f7_i2c_smbus_check_pec(i2c_dev); if (ret) - goto clk_free; + goto pm_free; } if (read_write && size != I2C_SMBUS_QUICK) { @@ -1649,11 +1679,15 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, } } -clk_free: - clk_disable(i2c_dev->clk); +pm_free: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); return ret; } +static void stm32f7_i2c_enable_wakeup(struct stm32f7_i2c_dev *i2c_dev, + bool enable); + static int stm32f7_i2c_reg_slave(struct i2c_client *slave) { struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(slave->adapter); @@ -1676,13 +1710,12 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave) if (ret) return ret; - if (!(stm32f7_i2c_is_slave_registered(i2c_dev))) { - ret = clk_enable(i2c_dev->clk); - if (ret) { - dev_err(dev, "Failed to enable clock\n"); - return ret; - } - } + ret = pm_runtime_get_sync(dev); + if (ret < 0) + return ret; + + if (!stm32f7_i2c_is_slave_registered(i2c_dev)) + stm32f7_i2c_enable_wakeup(i2c_dev, true); if (id == 0) { /* Configure Own Address 1 */ @@ -1703,7 +1736,7 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave) oar2 &= ~STM32F7_I2C_OAR2_MASK; if (slave->flags & I2C_CLIENT_TEN) { ret = -EOPNOTSUPP; - goto exit; + goto pm_free; } oar2 |= STM32F7_I2C_OAR2_OA2_7(slave->addr); @@ -1712,7 +1745,7 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave) writel_relaxed(oar2, i2c_dev->base + STM32F7_I2C_OAR2); } else { ret = -ENODEV; - goto exit; + goto pm_free; } /* Enable ACK */ @@ -1723,11 +1756,13 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave) STM32F7_I2C_CR1_PE; stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, mask); - return 0; + ret = 0; +pm_free: + if (!stm32f7_i2c_is_slave_registered(i2c_dev)) + stm32f7_i2c_enable_wakeup(i2c_dev, false); -exit: - if (!(stm32f7_i2c_is_slave_registered(i2c_dev))) - clk_disable(i2c_dev->clk); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); return ret; } @@ -1745,6 +1780,10 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave) WARN_ON(!i2c_dev->slave[id]); + ret = pm_runtime_get_sync(i2c_dev->dev); + if (ret < 0) + return ret; + if (id == 0) { mask = STM32F7_I2C_OAR1_OA1EN; stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR1, mask); @@ -1755,14 +1794,56 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave) i2c_dev->slave[id] = NULL; - if (!(stm32f7_i2c_is_slave_registered(i2c_dev))) { + if (!stm32f7_i2c_is_slave_registered(i2c_dev)) { stm32f7_i2c_disable_irq(i2c_dev, STM32F7_I2C_ALL_IRQ_MASK); - clk_disable(i2c_dev->clk); + stm32f7_i2c_enable_wakeup(i2c_dev, false); } + pm_runtime_mark_last_busy(i2c_dev->dev); + pm_runtime_put_autosuspend(i2c_dev->dev); + return 0; } +static int stm32f7_i2c_setup_wakeup(struct stm32f7_i2c_dev *i2c_dev) +{ + int ret; + + device_init_wakeup(i2c_dev->dev, true); + ret = dev_pm_set_dedicated_wake_irq(i2c_dev->dev, i2c_dev->irq_wakeup); + if (ret) { + device_init_wakeup(i2c_dev->dev, false); + dev_warn(i2c_dev->dev, "failed to set up wakeup irq"); + return ret; + } + + return device_set_wakeup_enable(i2c_dev->dev, false); +} + +static int stm32f7_i2c_setup_fm_plus_bits(struct platform_device *pdev, + struct stm32f7_i2c_dev *i2c_dev) +{ + struct device_node *np = pdev->dev.of_node; + int ret; + u32 reg, mask; + + i2c_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg-fmp"); + if (IS_ERR(i2c_dev->regmap)) { + /* Optional */ + return 0; + } + + ret = of_property_read_u32_index(np, "st,syscfg-fmp", 1, ®); + if (ret) + return ret; + + ret = of_property_read_u32_index(np, "st,syscfg-fmp", 2, &mask); + if (ret) + return ret; + + return regmap_update_bits(i2c_dev->regmap, reg, mask, mask); +} + static u32 stm32f7_i2c_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SLAVE | @@ -1786,7 +1867,7 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) struct stm32f7_i2c_dev *i2c_dev; const struct stm32f7_i2c_setup *setup; struct resource *res; - u32 irq_error, irq_event, clk_rate, rise_time, fall_time; + u32 irq_error, clk_rate, rise_time, fall_time; struct i2c_adapter *adap; struct reset_control *rst; dma_addr_t phy_addr; @@ -1802,13 +1883,13 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) return PTR_ERR(i2c_dev->base); phy_addr = (dma_addr_t)res->start; - irq_event = irq_of_parse_and_map(np, 0); - if (!irq_event) { + i2c_dev->irq_event = of_irq_get_byname(np, "event"); + if (!i2c_dev->irq_event) { dev_err(&pdev->dev, "IRQ event missing or invalid\n"); return -EINVAL; } - irq_error = irq_of_parse_and_map(np, 1); + irq_error = of_irq_get_byname(np, "error"); if (!irq_error) { dev_err(&pdev->dev, "IRQ error missing or invalid\n"); return -EINVAL; @@ -1819,6 +1900,7 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Error: Missing controller clock\n"); return PTR_ERR(i2c_dev->clk); } + ret = clk_prepare_enable(i2c_dev->clk); if (ret) { dev_err(&pdev->dev, "Failed to prepare_enable clock\n"); @@ -1828,12 +1910,16 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) i2c_dev->speed = STM32_I2C_SPEED_STANDARD; ret = device_property_read_u32(&pdev->dev, "clock-frequency", &clk_rate); - if (!ret && clk_rate >= 1000000) + if (!ret && clk_rate >= 1000000) { i2c_dev->speed = STM32_I2C_SPEED_FAST_PLUS; - else if (!ret && clk_rate >= 400000) + ret = stm32f7_i2c_setup_fm_plus_bits(pdev, i2c_dev); + if (ret) + goto clk_free; + } else if (!ret && clk_rate >= 400000) { i2c_dev->speed = STM32_I2C_SPEED_FAST; - else if (!ret && clk_rate >= 100000) + } else if (!ret && clk_rate >= 100000) { i2c_dev->speed = STM32_I2C_SPEED_STANDARD; + } rst = devm_reset_control_get(&pdev->dev, NULL); if (IS_ERR(rst)) { @@ -1847,14 +1933,14 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) i2c_dev->dev = &pdev->dev; - ret = devm_request_threaded_irq(&pdev->dev, irq_event, + ret = devm_request_threaded_irq(&pdev->dev, i2c_dev->irq_event, stm32f7_i2c_isr_event, stm32f7_i2c_isr_event_thread, IRQF_ONESHOT, pdev->name, i2c_dev); if (ret) { dev_err(&pdev->dev, "Failed to request irq event %i\n", - irq_event); + i2c_dev->irq_event); goto clk_free; } @@ -1888,8 +1974,6 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) if (ret) goto clk_free; - stm32f7_i2c_hw_config(i2c_dev); - adap = &i2c_dev->adap; i2c_set_adapdata(adap, i2c_dev); snprintf(adap->name, sizeof(adap->name), "STM32F7 I2C(%pa)", @@ -1908,18 +1992,45 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) STM32F7_I2C_TXDR, STM32F7_I2C_RXDR); - ret = i2c_add_adapter(adap); - if (ret) - goto clk_free; + i2c_dev->irq_wakeup = of_irq_get_byname(np, "wakeup"); + if (i2c_dev->irq_wakeup > 0) { + ret = stm32f7_i2c_setup_wakeup(i2c_dev); + if (ret) + goto clk_free; + } platform_set_drvdata(pdev, i2c_dev); - clk_disable(i2c_dev->clk); + pm_runtime_set_autosuspend_delay(i2c_dev->dev, + STM32F7_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(i2c_dev->dev); + pm_runtime_set_active(i2c_dev->dev); + pm_runtime_enable(i2c_dev->dev); + + pm_runtime_get_noresume(&pdev->dev); + + stm32f7_i2c_hw_config(i2c_dev); + + ret = i2c_add_adapter(adap); + if (ret) + goto pm_disable; dev_info(i2c_dev->dev, "STM32F7 I2C-%d bus adapter\n", adap->nr); + pm_runtime_mark_last_busy(i2c_dev->dev); + pm_runtime_put_autosuspend(i2c_dev->dev); + return 0; +pm_disable: + dev_pm_clear_wake_irq(i2c_dev->dev); + device_init_wakeup(i2c_dev->dev, false); + + pm_runtime_put_noidle(i2c_dev->dev); + pm_runtime_disable(i2c_dev->dev); + pm_runtime_set_suspended(i2c_dev->dev); + pm_runtime_dont_use_autosuspend(i2c_dev->dev); + clk_free: clk_disable_unprepare(i2c_dev->clk); @@ -1936,11 +2047,170 @@ static int stm32f7_i2c_remove(struct platform_device *pdev) } i2c_del_adapter(&i2c_dev->adap); + pm_runtime_get_sync(i2c_dev->dev); + + dev_pm_clear_wake_irq(i2c_dev->dev); + device_init_wakeup(i2c_dev->dev, false); - clk_unprepare(i2c_dev->clk); + clk_disable_unprepare(i2c_dev->clk); + + pm_runtime_put_noidle(i2c_dev->dev); + pm_runtime_disable(i2c_dev->dev); + pm_runtime_set_suspended(i2c_dev->dev); + pm_runtime_dont_use_autosuspend(i2c_dev->dev); + + return 0; +} + +#ifdef CONFIG_PM +static int stm32f7_i2c_runtime_suspend(struct device *dev) +{ + struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev); + + if (!stm32f7_i2c_is_slave_registered(i2c_dev)) + clk_disable_unprepare(i2c_dev->clk); + + return 0; +} + +static int stm32f7_i2c_runtime_resume(struct device *dev) +{ + struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev); + int ret; + + if (!stm32f7_i2c_is_slave_registered(i2c_dev)) { + ret = clk_prepare_enable(i2c_dev->clk); + if (ret) { + dev_err(dev, "failed to prepare_enable clock\n"); + return ret; + } + } return 0; } +#endif + +#ifdef CONFIG_PM_SLEEP +static int stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev) +{ + int ret; + + ret = pm_runtime_get_sync(i2c_dev->dev); + if (ret < 0) + return ret; + + i2c_dev->regs.cr1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR1); + i2c_dev->regs.cr2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR2); + i2c_dev->regs.oar1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR1); + i2c_dev->regs.oar2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR2); + i2c_dev->regs.pecr = readl_relaxed(i2c_dev->base + STM32F7_I2C_PECR); + i2c_dev->regs.tmgr = readl_relaxed(i2c_dev->base + STM32F7_I2C_TIMINGR); + + pm_runtime_put_sync(i2c_dev->dev); + + return ret; +} + +static int stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev) +{ + u32 cr1; + int ret; + + 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(i2c_dev->regs.tmgr, i2c_dev->base + STM32F7_I2C_TIMINGR); + writel_relaxed(i2c_dev->regs.cr1 & ~STM32F7_I2C_CR1_PE, + i2c_dev->base + STM32F7_I2C_CR1); + if (i2c_dev->regs.cr1 & STM32F7_I2C_CR1_PE) + stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1, + STM32F7_I2C_CR1_PE); + writel_relaxed(i2c_dev->regs.cr2, i2c_dev->base + STM32F7_I2C_CR2); + writel_relaxed(i2c_dev->regs.oar1, i2c_dev->base + STM32F7_I2C_OAR1); + writel_relaxed(i2c_dev->regs.oar2, i2c_dev->base + STM32F7_I2C_OAR2); + writel_relaxed(i2c_dev->regs.pecr, i2c_dev->base + STM32F7_I2C_PECR); + + pm_runtime_put_sync(i2c_dev->dev); + + return ret; +} + +static void stm32f7_i2c_enable_wakeup(struct stm32f7_i2c_dev *i2c_dev, + bool enable) +{ + void __iomem *base = i2c_dev->base; + u32 mask = STM32F7_I2C_CR1_WUPEN; + + if (i2c_dev->irq_wakeup <= 0) + return; + + if (enable) { + device_set_wakeup_enable(i2c_dev->dev, true); + enable_irq_wake(i2c_dev->irq_wakeup); + enable_irq_wake(i2c_dev->irq_event); + stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, mask); + readl_relaxed(i2c_dev->base + STM32F7_I2C_CR1); + } else { + disable_irq_wake(i2c_dev->irq_wakeup); + disable_irq_wake(i2c_dev->irq_event); + device_set_wakeup_enable(i2c_dev->dev, false); + stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR1, mask); + } +} + +static int stm32f7_i2c_suspend(struct device *dev) +{ + struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev); + int ret; + + ret = stm32f7_i2c_regs_backup(i2c_dev); + if (ret < 0) + return ret; + + if (!stm32f7_i2c_is_slave_registered(i2c_dev)) { + pinctrl_pm_select_sleep_state(dev); + pm_runtime_force_suspend(dev); + } + + return 0; +} + +static int stm32f7_i2c_resume(struct device *dev) +{ + struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev); + int ret; + + if (!stm32f7_i2c_is_slave_registered(i2c_dev)) { + 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; + + return 0; +} +#else +static void stm32f7_i2c_enable_wakeup(struct stm32f7_i2c_dev *i2c_dev, + bool enable) +{ +} +#endif + +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[] = { { .compatible = "st,stm32f7-i2c", .data = &stm32f7_setup}, @@ -1952,6 +2222,7 @@ static struct platform_driver stm32f7_i2c_driver = { .driver = { .name = "stm32f7-i2c", .of_match_table = stm32f7_i2c_match, + .pm = &stm32f7_i2c_pm_ops, }, .probe = stm32f7_i2c_probe, .remove = stm32f7_i2c_remove, -- 2.7.4