|
| 1 | +From ce6faaedef68204e8b132bf44a0e4d2432dfb7ad Mon Sep 17 00:00:00 2001 |
| 2 | +From: Arun-Prasad-V < [email protected]> |
| 3 | +Date: Mon, 4 Nov 2024 12:52:39 +0530 |
| 4 | +Subject: [PATCH] Support for dynamic change of i2c bus-clk-rate |
| 5 | + |
| 6 | +Signed-off-by: Arun Prasad V < [email protected]> |
| 7 | +--- |
| 8 | + drivers/i2c/busses/i2c-tegra.c | 42 +++++++++++++++++++++++++++++++ |
| 9 | + drivers/i2c/i2c-core-base.c | 45 ++++++++++++++++++++++++++++++++++ |
| 10 | + include/linux/i2c.h | 5 ++++ |
| 11 | + 3 files changed, 92 insertions(+) |
| 12 | + |
| 13 | +diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c |
| 14 | +index cd50436e5734..4269343266c2 100644 |
| 15 | +--- a/drivers/i2c/busses/i2c-tegra.c |
| 16 | ++++ b/drivers/i2c/busses/i2c-tegra.c |
| 17 | +@@ -1484,6 +1484,37 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, |
| 18 | + return 0; |
| 19 | + } |
| 20 | + |
| 21 | ++static int tegra_i2c_change_clock_rate(struct tegra_i2c_dev *i2c_dev) |
| 22 | ++{ |
| 23 | ++ u32 val; |
| 24 | ++ int err; |
| 25 | ++ |
| 26 | ++ val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN | |
| 27 | ++ FIELD_PREP(I2C_CNFG_DEBOUNCE_CNT, 2); |
| 28 | ++ |
| 29 | ++ if (i2c_dev->hw->has_multi_master_mode) |
| 30 | ++ val |= I2C_CNFG_MULTI_MASTER_MODE; |
| 31 | ++ |
| 32 | ++ i2c_writel(i2c_dev, val, I2C_CNFG); |
| 33 | ++ |
| 34 | ++ if (i2c_dev->prod_list) |
| 35 | ++ tegra_i2c_config_prod_settings(i2c_dev); |
| 36 | ++ else |
| 37 | ++ tegra_i2c_set_clk_params(i2c_dev); |
| 38 | ++ |
| 39 | ++ err = tegra_i2c_set_div_clk(i2c_dev); |
| 40 | ++ if (err) { |
| 41 | ++ dev_err(i2c_dev->dev, "failed to set div-clk rate: %d\n", err); |
| 42 | ++ return err; |
| 43 | ++ } |
| 44 | ++ |
| 45 | ++ err = tegra_i2c_wait_for_config_load(i2c_dev); |
| 46 | ++ if (err) |
| 47 | ++ return err; |
| 48 | ++ |
| 49 | ++ return err; |
| 50 | ++} |
| 51 | ++ |
| 52 | + static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], |
| 53 | + int num) |
| 54 | + { |
| 55 | +@@ -1497,6 +1528,16 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], |
| 56 | + return ret; |
| 57 | + } |
| 58 | + |
| 59 | ++ if (adap->bus_clk_rate != i2c_dev->bus_clk_rate) { |
| 60 | ++ i2c_dev->bus_clk_rate = adap->bus_clk_rate; |
| 61 | ++ ret = tegra_i2c_change_clock_rate(i2c_dev); |
| 62 | ++ if (ret) { |
| 63 | ++ dev_err(i2c_dev->dev, |
| 64 | ++ "failed changing clock rate: %d\n", ret); |
| 65 | ++ return ret; |
| 66 | ++ } |
| 67 | ++ } |
| 68 | ++ |
| 69 | + for (i = 0; i < num; i++) { |
| 70 | + enum msg_end_type end_type = MSG_END_STOP; |
| 71 | + |
| 72 | +@@ -1957,6 +1998,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) |
| 73 | + i2c_dev->adapter.class = I2C_CLASS_DEPRECATED; |
| 74 | + i2c_dev->adapter.algo = &tegra_i2c_algo; |
| 75 | + i2c_dev->adapter.nr = pdev->id; |
| 76 | ++ i2c_dev->adapter.bus_clk_rate = i2c_dev->bus_clk_rate; |
| 77 | + |
| 78 | + if (i2c_dev->hw->supports_bus_clear) |
| 79 | + i2c_dev->adapter.bus_recovery_info = &tegra_i2c_recovery_info; |
| 80 | +diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c |
| 81 | +index c232535ca8f4..2edd24973cca 100644 |
| 82 | +--- a/drivers/i2c/i2c-core-base.c |
| 83 | ++++ b/drivers/i2c/i2c-core-base.c |
| 84 | +@@ -1285,10 +1285,33 @@ delete_device_store(struct device *dev, struct device_attribute *attr, |
| 85 | + static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, S_IWUSR, NULL, |
| 86 | + delete_device_store); |
| 87 | + |
| 88 | ++static ssize_t show_bus_clk_rate(struct device *dev, |
| 89 | ++ struct device_attribute *attr, char *buf) |
| 90 | ++{ |
| 91 | ++ struct i2c_adapter *adap = to_i2c_adapter(dev); |
| 92 | ++ |
| 93 | ++ return sprintf(buf, "%ld\n", adap->bus_clk_rate); |
| 94 | ++} |
| 95 | ++ |
| 96 | ++static ssize_t set_bus_clk_rate(struct device *dev, |
| 97 | ++ struct device_attribute *attr, const char *buf, size_t count) |
| 98 | ++{ |
| 99 | ++ struct i2c_adapter *adap = to_i2c_adapter(dev); |
| 100 | ++ char *p = (char *)buf; |
| 101 | ++ int bus_clk_rate; |
| 102 | ++ |
| 103 | ++ bus_clk_rate = memparse(p, &p); |
| 104 | ++ dev_info(dev, "Setting clock rate %d on next transfer\n", bus_clk_rate); |
| 105 | ++ adap->bus_clk_rate = bus_clk_rate; |
| 106 | ++ return count; |
| 107 | ++} |
| 108 | ++static DEVICE_ATTR(bus_clk_rate, 0644, show_bus_clk_rate, set_bus_clk_rate); |
| 109 | ++ |
| 110 | + static struct attribute *i2c_adapter_attrs[] = { |
| 111 | + &dev_attr_name.attr, |
| 112 | + &dev_attr_new_device.attr, |
| 113 | + &dev_attr_delete_device.attr, |
| 114 | ++ &dev_attr_bus_clk_rate.attr, |
| 115 | + NULL |
| 116 | + }; |
| 117 | + ATTRIBUTE_GROUPS(i2c_adapter); |
| 118 | +@@ -1401,6 +1424,28 @@ static int i2c_setup_host_notify_irq_domain(struct i2c_adapter *adap) |
| 119 | + return 0; |
| 120 | + } |
| 121 | + |
| 122 | ++int i2c_set_adapter_bus_clk_rate(struct i2c_adapter *adap, int bus_rate) |
| 123 | ++{ |
| 124 | ++ i2c_lock_bus(adap, I2C_LOCK_ROOT_ADAPTER); |
| 125 | ++ adap->bus_clk_rate = bus_rate; |
| 126 | ++ i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER); |
| 127 | ++ |
| 128 | ++ return 0; |
| 129 | ++} |
| 130 | ++EXPORT_SYMBOL_GPL(i2c_set_adapter_bus_clk_rate); |
| 131 | ++ |
| 132 | ++int i2c_get_adapter_bus_clk_rate(struct i2c_adapter *adap) |
| 133 | ++{ |
| 134 | ++ int bus_clk_rate; |
| 135 | ++ |
| 136 | ++ i2c_lock_bus(adap, I2C_LOCK_ROOT_ADAPTER); |
| 137 | ++ bus_clk_rate = adap->bus_clk_rate; |
| 138 | ++ i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER); |
| 139 | ++ |
| 140 | ++ return bus_clk_rate; |
| 141 | ++} |
| 142 | ++EXPORT_SYMBOL_GPL(i2c_get_adapter_bus_clk_rate); |
| 143 | ++ |
| 144 | + /** |
| 145 | + * i2c_handle_smbus_host_notify - Forward a Host Notify event to the correct |
| 146 | + * I2C client. |
| 147 | +diff --git a/include/linux/i2c.h b/include/linux/i2c.h |
| 148 | +index 2ce3efbe9198..9d876ad6373c 100644 |
| 149 | +--- a/include/linux/i2c.h |
| 150 | ++++ b/include/linux/i2c.h |
| 151 | +@@ -131,6 +131,10 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); |
| 152 | + /* Unlocked flavor */ |
| 153 | + int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); |
| 154 | + |
| 155 | ++/* Change bus clock rate for i2c adapter */ |
| 156 | ++extern int i2c_set_adapter_bus_clk_rate(struct i2c_adapter *adap, int bus_rate); |
| 157 | ++extern int i2c_get_adapter_bus_clk_rate(struct i2c_adapter *adap); |
| 158 | ++ |
| 159 | + /* This is the very generalized SMBus access routine. You probably do not |
| 160 | + want to use this, though; one of the functions below may be much easier, |
| 161 | + and probably just as fast. |
| 162 | +@@ -737,6 +741,7 @@ struct i2c_adapter { |
| 163 | + const struct i2c_adapter_quirks *quirks; |
| 164 | + |
| 165 | + struct irq_domain *host_notify_domain; |
| 166 | ++ unsigned long bus_clk_rate; |
| 167 | + struct regulator *bus_regulator; |
| 168 | + }; |
| 169 | + #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) |
| 170 | +-- |
| 171 | +2.34.1 |
| 172 | + |
0 commit comments