Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

i2c: qup: Vote for interconnect bandwidth to DRAM

When the I2C QUP controller is used together with a DMA engine it needs
to vote for the interconnect path to the DRAM. Otherwise it may be
unable to access the memory quickly enough.

The requested peak bandwidth is dependent on the I2C core clock.

To avoid sending votes too often the bandwidth is always requested when
a DMA transfer starts, but dropped only on runtime suspend. Runtime
suspend should only happen if no transfer is active. After resumption we
can defer the next vote until the first DMA transfer actually happens.

The implementation is largely identical to the one introduced for
spi-qup in commit ecdaa9473019 ("spi: qup: Vote for interconnect
bandwidth to DRAM") since both drivers represent the same hardware
block.

Signed-off-by: Stephan Gerhold <stephan.gerhold@kernkonzept.com>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
Link: https://lore.kernel.org/r/20231128-i2c-qup-dvfs-v1-3-59a0e3039111@kernkonzept.com

authored by

Stephan Gerhold and committed by
Andi Shyti
d4f35233 d1597144

+36
+36
drivers/i2c/busses/i2c-qup.c
··· 14 14 #include <linux/dma-mapping.h> 15 15 #include <linux/err.h> 16 16 #include <linux/i2c.h> 17 + #include <linux/interconnect.h> 17 18 #include <linux/interrupt.h> 18 19 #include <linux/io.h> 19 20 #include <linux/module.h> ··· 151 150 /* TAG length for DATA READ in RX FIFO */ 152 151 #define READ_RX_TAGS_LEN 2 153 152 153 + #define QUP_BUS_WIDTH 8 154 + 154 155 static unsigned int scl_freq; 155 156 module_param_named(scl_freq, scl_freq, uint, 0444); 156 157 MODULE_PARM_DESC(scl_freq, "SCL frequency override"); ··· 230 227 int irq; 231 228 struct clk *clk; 232 229 struct clk *pclk; 230 + struct icc_path *icc_path; 233 231 struct i2c_adapter adap; 234 232 235 233 int clk_ctl; ··· 258 254 259 255 /* To configure when bus is in run state */ 260 256 u32 config_run; 257 + 258 + /* bandwidth votes */ 259 + u32 src_clk_freq; 260 + u32 cur_bw_clk_freq; 261 261 262 262 /* dma parameters */ 263 263 bool is_dma; ··· 459 451 } 460 452 461 453 return ret; 454 + } 455 + 456 + static int qup_i2c_vote_bw(struct qup_i2c_dev *qup, u32 clk_freq) 457 + { 458 + u32 needed_peak_bw; 459 + int ret; 460 + 461 + if (qup->cur_bw_clk_freq == clk_freq) 462 + return 0; 463 + 464 + needed_peak_bw = Bps_to_icc(clk_freq * QUP_BUS_WIDTH); 465 + ret = icc_set_bw(qup->icc_path, 0, needed_peak_bw); 466 + if (ret) 467 + return ret; 468 + 469 + qup->cur_bw_clk_freq = clk_freq; 470 + return 0; 462 471 } 463 472 464 473 static void qup_i2c_write_tx_fifo_v1(struct qup_i2c_dev *qup) ··· 862 837 struct qup_i2c_dev *qup = i2c_get_adapdata(adap); 863 838 int ret = 0; 864 839 int idx = 0; 840 + 841 + ret = qup_i2c_vote_bw(qup, qup->src_clk_freq); 842 + if (ret) 843 + return ret; 865 844 866 845 enable_irq(qup->irq); 867 846 ret = qup_i2c_req_dma(qup); ··· 1672 1643 config = readl(qup->base + QUP_CONFIG); 1673 1644 config |= QUP_CLOCK_AUTO_GATE; 1674 1645 writel(config, qup->base + QUP_CONFIG); 1646 + qup_i2c_vote_bw(qup, 0); 1675 1647 clk_disable_unprepare(qup->pclk); 1676 1648 } 1677 1649 ··· 1773 1743 goto fail_dma; 1774 1744 } 1775 1745 qup->is_dma = true; 1746 + 1747 + qup->icc_path = devm_of_icc_get(&pdev->dev, NULL); 1748 + if (IS_ERR(qup->icc_path)) 1749 + return dev_err_probe(&pdev->dev, PTR_ERR(qup->icc_path), 1750 + "failed to get interconnect path\n"); 1776 1751 } 1777 1752 1778 1753 nodma: ··· 1826 1791 qup_i2c_enable_clocks(qup); 1827 1792 src_clk_freq = clk_get_rate(qup->clk); 1828 1793 } 1794 + qup->src_clk_freq = src_clk_freq; 1829 1795 1830 1796 /* 1831 1797 * Bootloaders might leave a pending interrupt on certain QUP's,