|
@@ -28,6 +28,7 @@
|
|
#include <linux/of_device.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/module.h>
|
|
#include <linux/module.h>
|
|
#include <linux/reset.h>
|
|
#include <linux/reset.h>
|
|
|
|
+#include <linux/pm_runtime.h>
|
|
|
|
|
|
#include <asm/unaligned.h>
|
|
#include <asm/unaligned.h>
|
|
|
|
|
|
@@ -401,8 +402,9 @@ static void tegra_dvc_init(struct tegra_i2c_dev *i2c_dev)
|
|
dvc_writel(i2c_dev, val, DVC_CTRL_REG1);
|
|
dvc_writel(i2c_dev, val, DVC_CTRL_REG1);
|
|
}
|
|
}
|
|
|
|
|
|
-static inline int tegra_i2c_clock_enable(struct tegra_i2c_dev *i2c_dev)
|
|
|
|
|
|
+static int tegra_i2c_runtime_resume(struct device *dev)
|
|
{
|
|
{
|
|
|
|
+ struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
if (!i2c_dev->hw->has_single_clk_source) {
|
|
if (!i2c_dev->hw->has_single_clk_source) {
|
|
@@ -413,32 +415,39 @@ static inline int tegra_i2c_clock_enable(struct tegra_i2c_dev *i2c_dev)
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
ret = clk_enable(i2c_dev->div_clk);
|
|
ret = clk_enable(i2c_dev->div_clk);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
dev_err(i2c_dev->dev,
|
|
dev_err(i2c_dev->dev,
|
|
"Enabling div clk failed, err %d\n", ret);
|
|
"Enabling div clk failed, err %d\n", ret);
|
|
clk_disable(i2c_dev->fast_clk);
|
|
clk_disable(i2c_dev->fast_clk);
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
- return ret;
|
|
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static inline void tegra_i2c_clock_disable(struct tegra_i2c_dev *i2c_dev)
|
|
|
|
|
|
+static int tegra_i2c_runtime_suspend(struct device *dev)
|
|
{
|
|
{
|
|
|
|
+ struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
|
|
|
+
|
|
clk_disable(i2c_dev->div_clk);
|
|
clk_disable(i2c_dev->div_clk);
|
|
if (!i2c_dev->hw->has_single_clk_source)
|
|
if (!i2c_dev->hw->has_single_clk_source)
|
|
clk_disable(i2c_dev->fast_clk);
|
|
clk_disable(i2c_dev->fast_clk);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
|
|
static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
|
|
{
|
|
{
|
|
u32 val;
|
|
u32 val;
|
|
- int err = 0;
|
|
|
|
|
|
+ int err;
|
|
u32 clk_divisor;
|
|
u32 clk_divisor;
|
|
unsigned long timeout = jiffies + HZ;
|
|
unsigned long timeout = jiffies + HZ;
|
|
|
|
|
|
- err = tegra_i2c_clock_enable(i2c_dev);
|
|
|
|
|
|
+ err = pm_runtime_get_sync(i2c_dev->dev);
|
|
if (err < 0) {
|
|
if (err < 0) {
|
|
- dev_err(i2c_dev->dev, "Clock enable failed %d\n", err);
|
|
|
|
|
|
+ dev_err(i2c_dev->dev, "runtime resume failed %d\n", err);
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -477,8 +486,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
|
|
0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
|
|
0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
|
|
i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
|
|
i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
|
|
|
|
|
|
- if (tegra_i2c_flush_fifos(i2c_dev))
|
|
|
|
- err = -ETIMEDOUT;
|
|
|
|
|
|
+ err = tegra_i2c_flush_fifos(i2c_dev);
|
|
|
|
|
|
if (i2c_dev->is_multimaster_mode && i2c_dev->hw->has_slcg_override_reg)
|
|
if (i2c_dev->is_multimaster_mode && i2c_dev->hw->has_slcg_override_reg)
|
|
i2c_writel(i2c_dev, I2C_MST_CORE_CLKEN_OVR, I2C_CLKEN_OVERRIDE);
|
|
i2c_writel(i2c_dev, I2C_MST_CORE_CLKEN_OVR, I2C_CLKEN_OVERRIDE);
|
|
@@ -502,7 +510,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
|
|
}
|
|
}
|
|
|
|
|
|
err:
|
|
err:
|
|
- tegra_i2c_clock_disable(i2c_dev);
|
|
|
|
|
|
+ pm_runtime_put(i2c_dev->dev);
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -677,9 +685,9 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
|
|
if (i2c_dev->is_suspended)
|
|
if (i2c_dev->is_suspended)
|
|
return -EBUSY;
|
|
return -EBUSY;
|
|
|
|
|
|
- ret = tegra_i2c_clock_enable(i2c_dev);
|
|
|
|
|
|
+ ret = pm_runtime_get_sync(i2c_dev->dev);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
- dev_err(i2c_dev->dev, "Clock enable failed %d\n", ret);
|
|
|
|
|
|
+ dev_err(i2c_dev->dev, "runtime resume failed %d\n", ret);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -696,7 +704,9 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
|
|
if (ret)
|
|
if (ret)
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
- tegra_i2c_clock_disable(i2c_dev);
|
|
|
|
|
|
+
|
|
|
|
+ pm_runtime_put(i2c_dev->dev);
|
|
|
|
+
|
|
return ret ?: i;
|
|
return ret ?: i;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -902,12 +912,21 @@ static int tegra_i2c_probe(struct platform_device *pdev)
|
|
goto unprepare_fast_clk;
|
|
goto unprepare_fast_clk;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ pm_runtime_enable(&pdev->dev);
|
|
|
|
+ if (!pm_runtime_enabled(&pdev->dev)) {
|
|
|
|
+ ret = tegra_i2c_runtime_resume(&pdev->dev);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ dev_err(&pdev->dev, "runtime resume failed\n");
|
|
|
|
+ goto unprepare_div_clk;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
if (i2c_dev->is_multimaster_mode) {
|
|
if (i2c_dev->is_multimaster_mode) {
|
|
ret = clk_enable(i2c_dev->div_clk);
|
|
ret = clk_enable(i2c_dev->div_clk);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
dev_err(i2c_dev->dev, "div_clk enable failed %d\n",
|
|
dev_err(i2c_dev->dev, "div_clk enable failed %d\n",
|
|
ret);
|
|
ret);
|
|
- goto unprepare_div_clk;
|
|
|
|
|
|
+ goto disable_rpm;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -943,6 +962,11 @@ disable_div_clk:
|
|
if (i2c_dev->is_multimaster_mode)
|
|
if (i2c_dev->is_multimaster_mode)
|
|
clk_disable(i2c_dev->div_clk);
|
|
clk_disable(i2c_dev->div_clk);
|
|
|
|
|
|
|
|
+disable_rpm:
|
|
|
|
+ pm_runtime_disable(&pdev->dev);
|
|
|
|
+ if (!pm_runtime_status_suspended(&pdev->dev))
|
|
|
|
+ tegra_i2c_runtime_suspend(&pdev->dev);
|
|
|
|
+
|
|
unprepare_div_clk:
|
|
unprepare_div_clk:
|
|
clk_unprepare(i2c_dev->div_clk);
|
|
clk_unprepare(i2c_dev->div_clk);
|
|
|
|
|
|
@@ -962,6 +986,10 @@ static int tegra_i2c_remove(struct platform_device *pdev)
|
|
if (i2c_dev->is_multimaster_mode)
|
|
if (i2c_dev->is_multimaster_mode)
|
|
clk_disable(i2c_dev->div_clk);
|
|
clk_disable(i2c_dev->div_clk);
|
|
|
|
|
|
|
|
+ pm_runtime_disable(&pdev->dev);
|
|
|
|
+ if (!pm_runtime_status_suspended(&pdev->dev))
|
|
|
|
+ tegra_i2c_runtime_suspend(&pdev->dev);
|
|
|
|
+
|
|
clk_unprepare(i2c_dev->div_clk);
|
|
clk_unprepare(i2c_dev->div_clk);
|
|
if (!i2c_dev->hw->has_single_clk_source)
|
|
if (!i2c_dev->hw->has_single_clk_source)
|
|
clk_unprepare(i2c_dev->fast_clk);
|
|
clk_unprepare(i2c_dev->fast_clk);
|
|
@@ -997,7 +1025,11 @@ static int tegra_i2c_resume(struct device *dev)
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
-static SIMPLE_DEV_PM_OPS(tegra_i2c_pm, tegra_i2c_suspend, tegra_i2c_resume);
|
|
|
|
|
|
+static const struct dev_pm_ops tegra_i2c_pm = {
|
|
|
|
+ SET_RUNTIME_PM_OPS(tegra_i2c_runtime_suspend, tegra_i2c_runtime_resume,
|
|
|
|
+ NULL)
|
|
|
|
+ SET_SYSTEM_SLEEP_PM_OPS(tegra_i2c_suspend, tegra_i2c_resume)
|
|
|
|
+};
|
|
#define TEGRA_I2C_PM (&tegra_i2c_pm)
|
|
#define TEGRA_I2C_PM (&tegra_i2c_pm)
|
|
#else
|
|
#else
|
|
#define TEGRA_I2C_PM NULL
|
|
#define TEGRA_I2C_PM NULL
|