diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-qup.c')
| -rw-r--r-- | drivers/i2c/busses/i2c-qup.c | 80 |
1 files changed, 53 insertions, 27 deletions
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index ae90170023b0..a0e076fc5f36 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -14,12 +14,13 @@ #include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/i2c.h> +#include <linux/interconnect.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/property.h> #include <linux/scatterlist.h> /* QUP Registers */ @@ -150,6 +151,8 @@ /* TAG length for DATA READ in RX FIFO */ #define READ_RX_TAGS_LEN 2 +#define QUP_BUS_WIDTH 8 + static unsigned int scl_freq; module_param_named(scl_freq, scl_freq, uint, 0444); MODULE_PARM_DESC(scl_freq, "SCL frequency override"); @@ -227,6 +230,7 @@ struct qup_i2c_dev { int irq; struct clk *clk; struct clk *pclk; + struct icc_path *icc_path; struct i2c_adapter adap; int clk_ctl; @@ -255,6 +259,10 @@ struct qup_i2c_dev { /* To configure when bus is in run state */ u32 config_run; + /* bandwidth votes */ + u32 src_clk_freq; + u32 cur_bw_clk_freq; + /* dma parameters */ bool is_dma; /* To check if the current transfer is using DMA */ @@ -444,8 +452,10 @@ static int qup_i2c_bus_active(struct qup_i2c_dev *qup, int len) if (!(status & I2C_STATUS_BUS_ACTIVE)) break; - if (time_after(jiffies, timeout)) + if (time_after(jiffies, timeout)) { ret = -ETIMEDOUT; + break; + } usleep_range(len, len * 2); } @@ -453,6 +463,23 @@ static int qup_i2c_bus_active(struct qup_i2c_dev *qup, int len) return ret; } +static int qup_i2c_vote_bw(struct qup_i2c_dev *qup, u32 clk_freq) +{ + u32 needed_peak_bw; + int ret; + + if (qup->cur_bw_clk_freq == clk_freq) + return 0; + + needed_peak_bw = Bps_to_icc(clk_freq * QUP_BUS_WIDTH); + ret = icc_set_bw(qup->icc_path, 0, needed_peak_bw); + if (ret) + return ret; + + qup->cur_bw_clk_freq = clk_freq; + return 0; +} + static void qup_i2c_write_tx_fifo_v1(struct qup_i2c_dev *qup) { struct qup_i2c_block *blk = &qup->blk; @@ -793,10 +820,8 @@ static int qup_i2c_bam_schedule_desc(struct qup_i2c_dev *qup) dma_async_issue_pending(qup->brx.dma); } - if (!wait_for_completion_timeout(&qup->xfer, qup->xfer_timeout)) { - dev_err(qup->dev, "normal trans timed out\n"); + if (!wait_for_completion_timeout(&qup->xfer, qup->xfer_timeout)) ret = -ETIMEDOUT; - } if (ret || qup->bus_err || qup->qup_err) { reinit_completion(&qup->xfer); @@ -840,6 +865,10 @@ static int qup_i2c_bam_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int ret = 0; int idx = 0; + ret = qup_i2c_vote_bw(qup, qup->src_clk_freq); + if (ret) + return ret; + enable_irq(qup->irq); ret = qup_i2c_req_dma(qup); @@ -1110,7 +1139,6 @@ static int qup_i2c_xfer(struct i2c_adapter *adap, ret = num; out: - pm_runtime_mark_last_busy(qup->dev); pm_runtime_put_autosuspend(qup->dev); return ret; @@ -1595,7 +1623,6 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap, if (ret == 0) ret = num; out: - pm_runtime_mark_last_busy(qup->dev); pm_runtime_put_autosuspend(qup->dev); return ret; @@ -1607,13 +1634,13 @@ static u32 qup_i2c_func(struct i2c_adapter *adap) } static const struct i2c_algorithm qup_i2c_algo = { - .master_xfer = qup_i2c_xfer, - .functionality = qup_i2c_func, + .xfer = qup_i2c_xfer, + .functionality = qup_i2c_func, }; static const struct i2c_algorithm qup_i2c_algo_v2 = { - .master_xfer = qup_i2c_xfer_v2, - .functionality = qup_i2c_func, + .xfer = qup_i2c_xfer_v2, + .functionality = qup_i2c_func, }; /* @@ -1645,12 +1672,13 @@ static void qup_i2c_disable_clocks(struct qup_i2c_dev *qup) config = readl(qup->base + QUP_CONFIG); config |= QUP_CLOCK_AUTO_GATE; writel(config, qup->base + QUP_CONFIG); + qup_i2c_vote_bw(qup, 0); clk_disable_unprepare(qup->pclk); } static const struct acpi_device_id qup_i2c_acpi_match[] = { { "QCOM8010"}, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, qup_i2c_acpi_match); @@ -1685,7 +1713,7 @@ static int qup_i2c_probe(struct platform_device *pdev) } } - if (of_device_is_compatible(pdev->dev.of_node, "qcom,i2c-qup-v1.1.1")) { + if (device_is_compatible(&pdev->dev, "qcom,i2c-qup-v1.1.1")) { qup->adap.algo = &qup_i2c_algo; qup->adap.quirks = &qup_i2c_quirks; is_qup_v1 = true; @@ -1745,6 +1773,11 @@ static int qup_i2c_probe(struct platform_device *pdev) goto fail_dma; } qup->is_dma = true; + + qup->icc_path = devm_of_icc_get(&pdev->dev, NULL); + if (IS_ERR(qup->icc_path)) + return dev_err_probe(&pdev->dev, PTR_ERR(qup->icc_path), + "failed to get interconnect path\n"); } nodma: @@ -1793,6 +1826,7 @@ nodma: qup_i2c_enable_clocks(qup); src_clk_freq = clk_get_rate(qup->clk); } + qup->src_clk_freq = src_clk_freq; /* * Bootloaders might leave a pending interrupt on certain QUP's, @@ -1927,7 +1961,6 @@ static void qup_i2c_remove(struct platform_device *pdev) pm_runtime_set_suspended(qup->dev); } -#ifdef CONFIG_PM static int qup_i2c_pm_suspend_runtime(struct device *device) { struct qup_i2c_dev *qup = dev_get_drvdata(device); @@ -1945,9 +1978,7 @@ static int qup_i2c_pm_resume_runtime(struct device *device) qup_i2c_enable_clocks(qup); return 0; } -#endif -#ifdef CONFIG_PM_SLEEP static int qup_i2c_suspend(struct device *device) { if (!pm_runtime_suspended(device)) @@ -1958,20 +1989,14 @@ static int qup_i2c_suspend(struct device *device) static int qup_i2c_resume(struct device *device) { qup_i2c_pm_resume_runtime(device); - pm_runtime_mark_last_busy(device); pm_request_autosuspend(device); return 0; } -#endif static const struct dev_pm_ops qup_i2c_qup_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS( - qup_i2c_suspend, - qup_i2c_resume) - SET_RUNTIME_PM_OPS( - qup_i2c_pm_suspend_runtime, - qup_i2c_pm_resume_runtime, - NULL) + SYSTEM_SLEEP_PM_OPS(qup_i2c_suspend, qup_i2c_resume) + RUNTIME_PM_OPS(qup_i2c_pm_suspend_runtime, + qup_i2c_pm_resume_runtime, NULL) }; static const struct of_device_id qup_i2c_dt_match[] = { @@ -1984,10 +2009,10 @@ MODULE_DEVICE_TABLE(of, qup_i2c_dt_match); static struct platform_driver qup_i2c_driver = { .probe = qup_i2c_probe, - .remove_new = qup_i2c_remove, + .remove = qup_i2c_remove, .driver = { .name = "i2c_qup", - .pm = &qup_i2c_qup_pm_ops, + .pm = pm_ptr(&qup_i2c_qup_pm_ops), .of_match_table = qup_i2c_dt_match, .acpi_match_table = ACPI_PTR(qup_i2c_acpi_match), }, @@ -1995,5 +2020,6 @@ static struct platform_driver qup_i2c_driver = { module_platform_driver(qup_i2c_driver); +MODULE_DESCRIPTION("Qualcomm QUP based I2C controller"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:i2c_qup"); |
