mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-07-06 17:35:20 +02:00
i2c: i2c-stm32f7: add PM Runtime support
Use PM Runtime API to enable/disable clock Signed-off-by: Pierre-Yves MORDRET <pierre-yves.mordret@st.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
This commit is contained in:
parent
5eb316e636
commit
4e7bca6fc0
|
@ -28,6 +28,8 @@
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/reset.h>
|
#include <linux/reset.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -165,6 +167,8 @@
|
||||||
#define STM32F7_SCLH_MAX BIT(8)
|
#define STM32F7_SCLH_MAX BIT(8)
|
||||||
#define STM32F7_SCLL_MAX BIT(8)
|
#define STM32F7_SCLL_MAX BIT(8)
|
||||||
|
|
||||||
|
#define STM32F7_AUTOSUSPEND_DELAY (HZ / 100)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct stm32f7_i2c_spec - private i2c specification timing
|
* struct stm32f7_i2c_spec - private i2c specification timing
|
||||||
* @rate: I2C bus speed (Hz)
|
* @rate: I2C bus speed (Hz)
|
||||||
|
@ -1549,15 +1553,13 @@ static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap,
|
||||||
i2c_dev->msg_id = 0;
|
i2c_dev->msg_id = 0;
|
||||||
f7_msg->smbus = false;
|
f7_msg->smbus = false;
|
||||||
|
|
||||||
ret = clk_enable(i2c_dev->clk);
|
ret = pm_runtime_get_sync(i2c_dev->dev);
|
||||||
if (ret) {
|
if (ret < 0)
|
||||||
dev_err(i2c_dev->dev, "Failed to enable clock\n");
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
ret = stm32f7_i2c_wait_free_bus(i2c_dev);
|
ret = stm32f7_i2c_wait_free_bus(i2c_dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto clk_free;
|
goto pm_free;
|
||||||
|
|
||||||
stm32f7_i2c_xfer_msg(i2c_dev, msgs);
|
stm32f7_i2c_xfer_msg(i2c_dev, msgs);
|
||||||
|
|
||||||
|
@ -1573,8 +1575,9 @@ static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap,
|
||||||
ret = -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
clk_free:
|
pm_free:
|
||||||
clk_disable(i2c_dev->clk);
|
pm_runtime_mark_last_busy(i2c_dev->dev);
|
||||||
|
pm_runtime_put_autosuspend(i2c_dev->dev);
|
||||||
|
|
||||||
return (ret < 0) ? ret : num;
|
return (ret < 0) ? ret : num;
|
||||||
}
|
}
|
||||||
|
@ -1596,39 +1599,37 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
|
||||||
f7_msg->read_write = read_write;
|
f7_msg->read_write = read_write;
|
||||||
f7_msg->smbus = true;
|
f7_msg->smbus = true;
|
||||||
|
|
||||||
ret = clk_enable(i2c_dev->clk);
|
ret = pm_runtime_get_sync(dev);
|
||||||
if (ret) {
|
if (ret < 0)
|
||||||
dev_err(i2c_dev->dev, "Failed to enable clock\n");
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
ret = stm32f7_i2c_wait_free_bus(i2c_dev);
|
ret = stm32f7_i2c_wait_free_bus(i2c_dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto clk_free;
|
goto pm_free;
|
||||||
|
|
||||||
ret = stm32f7_i2c_smbus_xfer_msg(i2c_dev, flags, command, data);
|
ret = stm32f7_i2c_smbus_xfer_msg(i2c_dev, flags, command, data);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto clk_free;
|
goto pm_free;
|
||||||
|
|
||||||
timeout = wait_for_completion_timeout(&i2c_dev->complete,
|
timeout = wait_for_completion_timeout(&i2c_dev->complete,
|
||||||
i2c_dev->adap.timeout);
|
i2c_dev->adap.timeout);
|
||||||
ret = f7_msg->result;
|
ret = f7_msg->result;
|
||||||
if (ret)
|
if (ret)
|
||||||
goto clk_free;
|
goto pm_free;
|
||||||
|
|
||||||
if (!timeout) {
|
if (!timeout) {
|
||||||
dev_dbg(dev, "Access to slave 0x%x timed out\n", f7_msg->addr);
|
dev_dbg(dev, "Access to slave 0x%x timed out\n", f7_msg->addr);
|
||||||
if (i2c_dev->use_dma)
|
if (i2c_dev->use_dma)
|
||||||
dmaengine_terminate_all(dma->chan_using);
|
dmaengine_terminate_all(dma->chan_using);
|
||||||
ret = -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
goto clk_free;
|
goto pm_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check PEC */
|
/* Check PEC */
|
||||||
if ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && read_write) {
|
if ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && read_write) {
|
||||||
ret = stm32f7_i2c_smbus_check_pec(i2c_dev);
|
ret = stm32f7_i2c_smbus_check_pec(i2c_dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto clk_free;
|
goto pm_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read_write && size != I2C_SMBUS_QUICK) {
|
if (read_write && size != I2C_SMBUS_QUICK) {
|
||||||
|
@ -1653,8 +1654,9 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clk_free:
|
pm_free:
|
||||||
clk_disable(i2c_dev->clk);
|
pm_runtime_mark_last_busy(dev);
|
||||||
|
pm_runtime_put_autosuspend(dev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1680,13 +1682,9 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (!(stm32f7_i2c_is_slave_registered(i2c_dev))) {
|
ret = pm_runtime_get_sync(dev);
|
||||||
ret = clk_enable(i2c_dev->clk);
|
if (ret < 0)
|
||||||
if (ret) {
|
return ret;
|
||||||
dev_err(dev, "Failed to enable clock\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id == 0) {
|
if (id == 0) {
|
||||||
/* Configure Own Address 1 */
|
/* Configure Own Address 1 */
|
||||||
|
@ -1707,7 +1705,7 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
|
||||||
oar2 &= ~STM32F7_I2C_OAR2_MASK;
|
oar2 &= ~STM32F7_I2C_OAR2_MASK;
|
||||||
if (slave->flags & I2C_CLIENT_TEN) {
|
if (slave->flags & I2C_CLIENT_TEN) {
|
||||||
ret = -EOPNOTSUPP;
|
ret = -EOPNOTSUPP;
|
||||||
goto exit;
|
goto pm_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
oar2 |= STM32F7_I2C_OAR2_OA2_7(slave->addr);
|
oar2 |= STM32F7_I2C_OAR2_OA2_7(slave->addr);
|
||||||
|
@ -1716,7 +1714,7 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
|
||||||
writel_relaxed(oar2, i2c_dev->base + STM32F7_I2C_OAR2);
|
writel_relaxed(oar2, i2c_dev->base + STM32F7_I2C_OAR2);
|
||||||
} else {
|
} else {
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto exit;
|
goto pm_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enable ACK */
|
/* Enable ACK */
|
||||||
|
@ -1727,11 +1725,10 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
|
||||||
STM32F7_I2C_CR1_PE;
|
STM32F7_I2C_CR1_PE;
|
||||||
stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, mask);
|
stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, mask);
|
||||||
|
|
||||||
return 0;
|
ret = 0;
|
||||||
|
pm_free:
|
||||||
exit:
|
pm_runtime_mark_last_busy(dev);
|
||||||
if (!(stm32f7_i2c_is_slave_registered(i2c_dev)))
|
pm_runtime_put_autosuspend(dev);
|
||||||
clk_disable(i2c_dev->clk);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1749,6 +1746,10 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave)
|
||||||
|
|
||||||
WARN_ON(!i2c_dev->slave[id]);
|
WARN_ON(!i2c_dev->slave[id]);
|
||||||
|
|
||||||
|
ret = pm_runtime_get_sync(i2c_dev->dev);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (id == 0) {
|
if (id == 0) {
|
||||||
mask = STM32F7_I2C_OAR1_OA1EN;
|
mask = STM32F7_I2C_OAR1_OA1EN;
|
||||||
stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR1, mask);
|
stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR1, mask);
|
||||||
|
@ -1759,10 +1760,11 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave)
|
||||||
|
|
||||||
i2c_dev->slave[id] = NULL;
|
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);
|
stm32f7_i2c_disable_irq(i2c_dev, STM32F7_I2C_ALL_IRQ_MASK);
|
||||||
clk_disable(i2c_dev->clk);
|
|
||||||
}
|
pm_runtime_mark_last_busy(i2c_dev->dev);
|
||||||
|
pm_runtime_put_autosuspend(i2c_dev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1847,6 +1849,7 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
|
||||||
dev_err(&pdev->dev, "Error: Missing controller clock\n");
|
dev_err(&pdev->dev, "Error: Missing controller clock\n");
|
||||||
return PTR_ERR(i2c_dev->clk);
|
return PTR_ERR(i2c_dev->clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = clk_prepare_enable(i2c_dev->clk);
|
ret = clk_prepare_enable(i2c_dev->clk);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "Failed to prepare_enable clock\n");
|
dev_err(&pdev->dev, "Failed to prepare_enable clock\n");
|
||||||
|
@ -1920,8 +1923,6 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto clk_free;
|
goto clk_free;
|
||||||
|
|
||||||
stm32f7_i2c_hw_config(i2c_dev);
|
|
||||||
|
|
||||||
adap = &i2c_dev->adap;
|
adap = &i2c_dev->adap;
|
||||||
i2c_set_adapdata(adap, i2c_dev);
|
i2c_set_adapdata(adap, i2c_dev);
|
||||||
snprintf(adap->name, sizeof(adap->name), "STM32F7 I2C(%pa)",
|
snprintf(adap->name, sizeof(adap->name), "STM32F7 I2C(%pa)",
|
||||||
|
@ -1940,18 +1941,35 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
|
||||||
STM32F7_I2C_TXDR,
|
STM32F7_I2C_TXDR,
|
||||||
STM32F7_I2C_RXDR);
|
STM32F7_I2C_RXDR);
|
||||||
|
|
||||||
ret = i2c_add_adapter(adap);
|
|
||||||
if (ret)
|
|
||||||
goto clk_free;
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, i2c_dev);
|
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);
|
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;
|
return 0;
|
||||||
|
|
||||||
|
pm_disable:
|
||||||
|
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_free:
|
||||||
clk_disable_unprepare(i2c_dev->clk);
|
clk_disable_unprepare(i2c_dev->clk);
|
||||||
|
|
||||||
|
@ -1968,12 +1986,51 @@ static int stm32f7_i2c_remove(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
i2c_del_adapter(&i2c_dev->adap);
|
i2c_del_adapter(&i2c_dev->adap);
|
||||||
|
pm_runtime_get_sync(i2c_dev->dev);
|
||||||
|
|
||||||
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;
|
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
|
||||||
|
|
||||||
|
static const struct dev_pm_ops stm32f7_i2c_pm_ops = {
|
||||||
|
SET_RUNTIME_PM_OPS(stm32f7_i2c_runtime_suspend,
|
||||||
|
stm32f7_i2c_runtime_resume, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id stm32f7_i2c_match[] = {
|
static const struct of_device_id stm32f7_i2c_match[] = {
|
||||||
{ .compatible = "st,stm32f7-i2c", .data = &stm32f7_setup},
|
{ .compatible = "st,stm32f7-i2c", .data = &stm32f7_setup},
|
||||||
{},
|
{},
|
||||||
|
@ -1984,6 +2041,7 @@ static struct platform_driver stm32f7_i2c_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "stm32f7-i2c",
|
.name = "stm32f7-i2c",
|
||||||
.of_match_table = stm32f7_i2c_match,
|
.of_match_table = stm32f7_i2c_match,
|
||||||
|
.pm = &stm32f7_i2c_pm_ops,
|
||||||
},
|
},
|
||||||
.probe = stm32f7_i2c_probe,
|
.probe = stm32f7_i2c_probe,
|
||||||
.remove = stm32f7_i2c_remove,
|
.remove = stm32f7_i2c_remove,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user