summaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-omap.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-omap.c')
-rw-r--r--drivers/i2c/busses/i2c-omap.c124
1 files changed, 78 insertions, 46 deletions
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 58fd6fa3edf1..d9f590f0c384 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -24,12 +24,13 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/mux/consumer.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/platform_data/i2c-omap.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/property.h>
/* I2C controller revisions */
#define OMAP_I2C_OMAP1_REV_2 0x20
@@ -211,6 +212,7 @@ struct omap_i2c_dev {
u16 syscstate;
u16 westate;
u16 errata;
+ struct mux_state *mux_state;
};
static const u8 reg_map_ip_v1[] = {
@@ -660,7 +662,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
struct i2c_msg *msg, int stop, bool polling)
{
struct omap_i2c_dev *omap = i2c_get_adapdata(adap);
- unsigned long timeout;
+ unsigned long time_left;
u16 w;
int ret;
@@ -740,19 +742,18 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
* into arbitration and we're currently unable to recover from it.
*/
if (!polling) {
- timeout = wait_for_completion_timeout(&omap->cmd_complete,
- OMAP_I2C_TIMEOUT);
+ time_left = wait_for_completion_timeout(&omap->cmd_complete,
+ OMAP_I2C_TIMEOUT);
} else {
do {
omap_i2c_wait(omap);
ret = omap_i2c_xfer_data(omap);
} while (ret == -EAGAIN);
- timeout = !ret;
+ time_left = !ret;
}
- if (timeout == 0) {
- dev_err(omap->dev, "controller timed out\n");
+ if (time_left == 0) {
omap_i2c_reset(omap);
__omap_i2c_init(omap);
return -ETIMEDOUT;
@@ -827,7 +828,6 @@ omap_i2c_xfer_common(struct i2c_adapter *adap, struct i2c_msg msgs[], int num,
omap->set_mpu_wkup_lat(omap->dev, -1);
out:
- pm_runtime_mark_last_busy(omap->dev);
pm_runtime_put_autosuspend(omap->dev);
return r;
}
@@ -1049,23 +1049,6 @@ static int omap_i2c_transmit_data(struct omap_i2c_dev *omap, u8 num_bytes,
return 0;
}
-static irqreturn_t
-omap_i2c_isr(int irq, void *dev_id)
-{
- struct omap_i2c_dev *omap = dev_id;
- irqreturn_t ret = IRQ_HANDLED;
- u16 mask;
- u16 stat;
-
- stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG);
- mask = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG) & ~OMAP_I2C_STAT_NACK;
-
- if (stat & mask)
- ret = IRQ_WAKE_THREAD;
-
- return ret;
-}
-
static int omap_i2c_xfer_data(struct omap_i2c_dev *omap)
{
u16 bits;
@@ -1096,8 +1079,13 @@ static int omap_i2c_xfer_data(struct omap_i2c_dev *omap)
}
if (stat & OMAP_I2C_STAT_NACK) {
- err |= OMAP_I2C_STAT_NACK;
+ omap->cmd_err |= OMAP_I2C_STAT_NACK;
omap_i2c_ack_stat(omap, OMAP_I2C_STAT_NACK);
+
+ if (!(stat & ~OMAP_I2C_STAT_NACK)) {
+ err = -EAGAIN;
+ break;
+ }
}
if (stat & OMAP_I2C_STAT_AL) {
@@ -1212,9 +1200,9 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
}
static const struct i2c_algorithm omap_i2c_algo = {
- .master_xfer = omap_i2c_xfer_irq,
- .master_xfer_atomic = omap_i2c_xfer_polling,
- .functionality = omap_i2c_func,
+ .xfer = omap_i2c_xfer_irq,
+ .xfer_atomic = omap_i2c_xfer_polling,
+ .functionality = omap_i2c_func,
};
static const struct i2c_adapter_quirks omap_i2c_quirks = {
@@ -1262,7 +1250,7 @@ static const struct of_device_id omap_i2c_of_match[] = {
.compatible = "ti,omap2420-i2c",
.data = &omap2420_pdata,
},
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, omap_i2c_of_match);
#endif
@@ -1358,7 +1346,6 @@ omap_i2c_probe(struct platform_device *pdev)
const struct omap_i2c_bus_platform_data *pdata =
dev_get_platdata(&pdev->dev);
struct device_node *node = pdev->dev.of_node;
- const struct of_device_id *match;
int irq;
int r;
u32 rev;
@@ -1376,11 +1363,10 @@ omap_i2c_probe(struct platform_device *pdev)
if (IS_ERR(omap->base))
return PTR_ERR(omap->base);
- match = of_match_device(of_match_ptr(omap_i2c_of_match), &pdev->dev);
- if (match) {
+ if (pdev->dev.of_node) {
u32 freq = I2C_MAX_STANDARD_MODE_FREQ;
- pdata = match->data;
+ pdata = device_get_match_data(&pdev->dev);
omap->flags = pdata->flags;
of_property_read_u32(node, "clock-frequency", &freq);
@@ -1467,15 +1453,34 @@ omap_i2c_probe(struct platform_device *pdev)
(1000 * omap->speed / 8);
}
+ if (of_property_present(node, "mux-states")) {
+ struct mux_state *mux_state;
+
+ mux_state = devm_mux_state_get(&pdev->dev, NULL);
+ if (IS_ERR(mux_state)) {
+ r = PTR_ERR(mux_state);
+ dev_dbg(&pdev->dev, "failed to get I2C mux: %d\n", r);
+ goto err_put_pm;
+ }
+ omap->mux_state = mux_state;
+ r = mux_state_select(omap->mux_state);
+ if (r) {
+ dev_err(&pdev->dev, "failed to select I2C mux: %d\n", r);
+ goto err_put_pm;
+ }
+ }
+
/* reset ASAP, clearing any IRQs */
- omap_i2c_init(omap);
+ r = omap_i2c_init(omap);
+ if (r)
+ goto err_mux_state_deselect;
if (omap->rev < OMAP_I2C_OMAP1_REV_2)
r = devm_request_irq(&pdev->dev, omap->irq, omap_i2c_omap1_isr,
IRQF_NO_SUSPEND, pdev->name, omap);
else
r = devm_request_threaded_irq(&pdev->dev, omap->irq,
- omap_i2c_isr, omap_i2c_isr_thread,
+ NULL, omap_i2c_isr_thread,
IRQF_NO_SUSPEND | IRQF_ONESHOT,
pdev->name, omap);
@@ -1504,16 +1509,19 @@ omap_i2c_probe(struct platform_device *pdev)
dev_info(omap->dev, "bus %d rev%d.%d at %d kHz\n", adap->nr,
major, minor, omap->speed);
- pm_runtime_mark_last_busy(omap->dev);
pm_runtime_put_autosuspend(omap->dev);
return 0;
err_unuse_clocks:
omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0);
- pm_runtime_dont_use_autosuspend(omap->dev);
+err_mux_state_deselect:
+ if (omap->mux_state)
+ mux_state_deselect(omap->mux_state);
+err_put_pm:
pm_runtime_put_sync(omap->dev);
err_disable_pm:
+ pm_runtime_dont_use_autosuspend(omap->dev);
pm_runtime_disable(&pdev->dev);
return r;
@@ -1526,6 +1534,9 @@ static void omap_i2c_remove(struct platform_device *pdev)
i2c_del_adapter(&omap->adapter);
+ if (omap->mux_state)
+ mux_state_deselect(omap->mux_state);
+
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0)
dev_err(omap->dev, "Failed to resume hardware, skip disable\n");
@@ -1537,7 +1548,7 @@ static void omap_i2c_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
-static int __maybe_unused omap_i2c_runtime_suspend(struct device *dev)
+static int omap_i2c_runtime_suspend(struct device *dev)
{
struct omap_i2c_dev *omap = dev_get_drvdata(dev);
@@ -1563,7 +1574,7 @@ static int __maybe_unused omap_i2c_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused omap_i2c_runtime_resume(struct device *dev)
+static int omap_i2c_runtime_resume(struct device *dev)
{
struct omap_i2c_dev *omap = dev_get_drvdata(dev);
@@ -1577,19 +1588,40 @@ static int __maybe_unused omap_i2c_runtime_resume(struct device *dev)
return 0;
}
+static int omap_i2c_suspend(struct device *dev)
+{
+ /*
+ * If the controller is autosuspended, there is no way to wakeup it once
+ * runtime pm is disabled (in suspend_late()).
+ * But a device may need the controller up during suspend_noirq() or
+ * resume_noirq().
+ * Wakeup the controller while runtime pm is enabled, so it is available
+ * until its suspend_noirq(), and from resume_noirq().
+ */
+ return pm_runtime_resume_and_get(dev);
+}
+
+static int omap_i2c_resume(struct device *dev)
+{
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
static const struct dev_pm_ops omap_i2c_pm_ops = {
- SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(omap_i2c_runtime_suspend,
- omap_i2c_runtime_resume, NULL)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SYSTEM_SLEEP_PM_OPS(omap_i2c_suspend, omap_i2c_resume)
+ RUNTIME_PM_OPS(omap_i2c_runtime_suspend,
+ omap_i2c_runtime_resume, NULL)
};
static struct platform_driver omap_i2c_driver = {
.probe = omap_i2c_probe,
- .remove_new = omap_i2c_remove,
+ .remove = omap_i2c_remove,
.driver = {
.name = "omap_i2c",
- .pm = &omap_i2c_pm_ops,
+ .pm = pm_ptr(&omap_i2c_pm_ops),
.of_match_table = of_match_ptr(omap_i2c_of_match),
},
};