diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-imx.c')
-rw-r--r-- | drivers/i2c/busses/i2c-imx.c | 103 |
1 files changed, 84 insertions, 19 deletions
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 5c9a8dfbc4a0..9e5d454d8318 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -397,17 +397,16 @@ static void i2c_imx_reset_regs(struct imx_i2c_struct *i2c_imx) } /* Functions for DMA support */ -static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, - dma_addr_t phy_addr) +static int i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, dma_addr_t phy_addr) { struct imx_i2c_dma *dma; struct dma_slave_config dma_sconfig; - struct device *dev = &i2c_imx->adapter.dev; + struct device *dev = i2c_imx->adapter.dev.parent; int ret; dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); if (!dma) - return; + return -ENOMEM; dma->chan_tx = dma_request_chan(dev, "tx"); if (IS_ERR(dma->chan_tx)) { @@ -452,7 +451,7 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, dev_info(dev, "using %s (tx) and %s (rx) for DMA transfers\n", dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx)); - return; + return 0; fail_rx: dma_release_channel(dma->chan_rx); @@ -460,6 +459,8 @@ fail_tx: dma_release_channel(dma->chan_tx); fail_al: devm_kfree(dev, dma); + + return ret; } static void i2c_imx_dma_callback(void *arg) @@ -621,8 +622,8 @@ static int i2c_imx_acked(struct imx_i2c_struct *i2c_imx) return 0; } -static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx, - unsigned int i2c_clk_rate) +static int i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx, + unsigned int i2c_clk_rate) { struct imx_i2c_clk_pair *i2c_clk_div = i2c_imx->hwdata->clk_div; unsigned int div; @@ -637,7 +638,11 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx, /* Divider value calculation */ if (i2c_imx->cur_clk == i2c_clk_rate) - return; + return 0; + + /* Keep the denominator of the following program always NOT equal to 0. */ + if (!(i2c_clk_rate / 2)) + return -EINVAL; i2c_imx->cur_clk = i2c_clk_rate; @@ -668,6 +673,8 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx, dev_dbg(&i2c_imx->adapter.dev, "IFDR[IC]=0x%x, REAL DIV=%d\n", i2c_clk_div[i].val, i2c_clk_div[i].div); #endif + + return 0; } static int i2c_imx_clk_notifier_call(struct notifier_block *nb, @@ -677,11 +684,12 @@ static int i2c_imx_clk_notifier_call(struct notifier_block *nb, struct imx_i2c_struct *i2c_imx = container_of(nb, struct imx_i2c_struct, clk_change_nb); + int ret = 0; if (action & POST_RATE_CHANGE) - i2c_imx_set_clk(i2c_imx, ndata->new_rate); + ret = i2c_imx_set_clk(i2c_imx, ndata->new_rate); - return NOTIFY_OK; + return notifier_from_errno(ret); } static int i2c_imx_start(struct imx_i2c_struct *i2c_imx, bool atomic) @@ -1715,8 +1723,8 @@ static int i2c_imx_probe(struct platform_device *pdev) return -ENOMEM; spin_lock_init(&i2c_imx->slave_lock); - hrtimer_init(&i2c_imx->slave_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); - i2c_imx->slave_timer.function = i2c_imx_slave_timeout; + hrtimer_setup(&i2c_imx->slave_timer, i2c_imx_slave_timeout, CLOCK_MONOTONIC, + HRTIMER_MODE_ABS); match = device_get_match_data(&pdev->dev); if (match) @@ -1760,7 +1768,8 @@ static int i2c_imx_probe(struct platform_device *pdev) goto rpm_disable; /* Request IRQ */ - ret = request_irq(irq, i2c_imx_isr, IRQF_SHARED, pdev->name, i2c_imx); + ret = request_irq(irq, i2c_imx_isr, IRQF_SHARED | IRQF_NO_SUSPEND, + pdev->name, i2c_imx); if (ret) { dev_err(&pdev->dev, "can't claim irq %d\n", irq); goto rpm_disable; @@ -1780,7 +1789,11 @@ static int i2c_imx_probe(struct platform_device *pdev) i2c_imx->bitrate = pdata->bitrate; i2c_imx->clk_change_nb.notifier_call = i2c_imx_clk_notifier_call; clk_notifier_register(i2c_imx->clk, &i2c_imx->clk_change_nb); - i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk)); + ret = i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk)); + if (ret < 0) { + dev_err(&pdev->dev, "can't get I2C clock\n"); + goto clk_notifier_unregister; + } i2c_imx_reset_regs(i2c_imx); @@ -1790,6 +1803,22 @@ static int i2c_imx_probe(struct platform_device *pdev) if (ret == -EPROBE_DEFER) goto clk_notifier_unregister; + /* + * DMA mode should be optional for I2C, when encountering DMA errors, + * no need to exit I2C probe. Only print warning to show DMA error and + * use PIO mode directly to ensure I2C bus available as much as possible. + */ + ret = i2c_imx_dma_request(i2c_imx, phy_addr); + if (ret) { + if (ret == -EPROBE_DEFER) + goto clk_notifier_unregister; + else if (ret == -ENODEV) + dev_dbg(&pdev->dev, "Only use PIO mode\n"); + else + dev_warn(&pdev->dev, "Failed to setup DMA (%pe), only use PIO mode\n", + ERR_PTR(ret)); + } + /* Add I2C adapter */ ret = i2c_add_numbered_adapter(&i2c_imx->adapter); if (ret < 0) @@ -1804,9 +1833,6 @@ static int i2c_imx_probe(struct platform_device *pdev) i2c_imx->adapter.name); dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n"); - /* Init DMA config if supported */ - i2c_imx_dma_request(i2c_imx, phy_addr); - return 0; /* Return OK */ clk_notifier_unregister: @@ -1858,8 +1884,7 @@ static int i2c_imx_runtime_suspend(struct device *dev) struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev); clk_disable(i2c_imx->clk); - - return 0; + return pinctrl_pm_select_sleep_state(dev); } static int i2c_imx_runtime_resume(struct device *dev) @@ -1867,6 +1892,10 @@ static int i2c_imx_runtime_resume(struct device *dev) struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev); int ret; + ret = pinctrl_pm_select_default_state(dev); + if (ret) + return ret; + ret = clk_enable(i2c_imx->clk); if (ret) dev_err(dev, "can't enable I2C clock, ret=%d\n", ret); @@ -1874,7 +1903,43 @@ static int i2c_imx_runtime_resume(struct device *dev) return ret; } +static int i2c_imx_suspend(struct device *dev) +{ + /* + * Some I2C devices may need the I2C controller to remain active + * during resume_noirq() or suspend_noirq(). If the controller is + * autosuspended, there is no way to wake it up once runtime PM is + * disabled (in suspend_late()). + * + * During system resume, the I2C controller will be available only + * after runtime PM is re-enabled (in resume_early()). However, this + * may be too late for some devices. + * + * Wake up the controller in the suspend() callback while runtime PM + * is still enabled. The I2C controller will remain available until + * the suspend_noirq() callback (pm_runtime_force_suspend()) is + * called. During resume, the I2C controller can be restored by the + * resume_noirq() callback (pm_runtime_force_resume()). + * + * Finally, the resume() callback re-enables autosuspend, ensuring + * the I2C controller remains available until the system enters + * suspend_noirq() and from resume_noirq(). + */ + return pm_runtime_resume_and_get(dev); +} + +static int i2c_imx_resume(struct device *dev) +{ + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; +} + static const struct dev_pm_ops i2c_imx_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend, i2c_imx_resume) RUNTIME_PM_OPS(i2c_imx_runtime_suspend, i2c_imx_runtime_resume, NULL) }; |