Skip to content

Commit

Permalink
Support for dynamic change of i2c bus_clk_rate
Browse files Browse the repository at this point in the history
  • Loading branch information
Arun-Prasad-V committed Nov 4, 2024
1 parent f740b0a commit ce601bc
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
From ce6faaedef68204e8b132bf44a0e4d2432dfb7ad Mon Sep 17 00:00:00 2001
From: Arun-Prasad-V <[email protected]>
Date: Mon, 4 Nov 2024 12:52:39 +0530
Subject: [PATCH] Support for dynamic change of i2c bus-clk-rate

Signed-off-by: Arun Prasad V <[email protected]>
---
drivers/i2c/busses/i2c-tegra.c | 42 +++++++++++++++++++++++++++++++
drivers/i2c/i2c-core-base.c | 45 ++++++++++++++++++++++++++++++++++
include/linux/i2c.h | 5 ++++
3 files changed, 92 insertions(+)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index cd50436e5734..4269343266c2 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -1484,6 +1484,37 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
return 0;
}

+static int tegra_i2c_change_clock_rate(struct tegra_i2c_dev *i2c_dev)
+{
+ u32 val;
+ int err;
+
+ val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN |
+ FIELD_PREP(I2C_CNFG_DEBOUNCE_CNT, 2);
+
+ if (i2c_dev->hw->has_multi_master_mode)
+ val |= I2C_CNFG_MULTI_MASTER_MODE;
+
+ i2c_writel(i2c_dev, val, I2C_CNFG);
+
+ if (i2c_dev->prod_list)
+ tegra_i2c_config_prod_settings(i2c_dev);
+ else
+ tegra_i2c_set_clk_params(i2c_dev);
+
+ err = tegra_i2c_set_div_clk(i2c_dev);
+ if (err) {
+ dev_err(i2c_dev->dev, "failed to set div-clk rate: %d\n", err);
+ return err;
+ }
+
+ err = tegra_i2c_wait_for_config_load(i2c_dev);
+ if (err)
+ return err;
+
+ return err;
+}
+
static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
int num)
{
@@ -1497,6 +1528,16 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
return ret;
}

+ if (adap->bus_clk_rate != i2c_dev->bus_clk_rate) {
+ i2c_dev->bus_clk_rate = adap->bus_clk_rate;
+ ret = tegra_i2c_change_clock_rate(i2c_dev);
+ if (ret) {
+ dev_err(i2c_dev->dev,
+ "failed changing clock rate: %d\n", ret);
+ return ret;
+ }
+ }
+
for (i = 0; i < num; i++) {
enum msg_end_type end_type = MSG_END_STOP;

@@ -1957,6 +1998,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
i2c_dev->adapter.class = I2C_CLASS_DEPRECATED;
i2c_dev->adapter.algo = &tegra_i2c_algo;
i2c_dev->adapter.nr = pdev->id;
+ i2c_dev->adapter.bus_clk_rate = i2c_dev->bus_clk_rate;

if (i2c_dev->hw->supports_bus_clear)
i2c_dev->adapter.bus_recovery_info = &tegra_i2c_recovery_info;
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index c232535ca8f4..2edd24973cca 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -1285,10 +1285,33 @@ delete_device_store(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, S_IWUSR, NULL,
delete_device_store);

+static ssize_t show_bus_clk_rate(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_adapter *adap = to_i2c_adapter(dev);
+
+ return sprintf(buf, "%ld\n", adap->bus_clk_rate);
+}
+
+static ssize_t set_bus_clk_rate(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_adapter *adap = to_i2c_adapter(dev);
+ char *p = (char *)buf;
+ int bus_clk_rate;
+
+ bus_clk_rate = memparse(p, &p);
+ dev_info(dev, "Setting clock rate %d on next transfer\n", bus_clk_rate);
+ adap->bus_clk_rate = bus_clk_rate;
+ return count;
+}
+static DEVICE_ATTR(bus_clk_rate, 0644, show_bus_clk_rate, set_bus_clk_rate);
+
static struct attribute *i2c_adapter_attrs[] = {
&dev_attr_name.attr,
&dev_attr_new_device.attr,
&dev_attr_delete_device.attr,
+ &dev_attr_bus_clk_rate.attr,
NULL
};
ATTRIBUTE_GROUPS(i2c_adapter);
@@ -1401,6 +1424,28 @@ static int i2c_setup_host_notify_irq_domain(struct i2c_adapter *adap)
return 0;
}

+int i2c_set_adapter_bus_clk_rate(struct i2c_adapter *adap, int bus_rate)
+{
+ i2c_lock_bus(adap, I2C_LOCK_ROOT_ADAPTER);
+ adap->bus_clk_rate = bus_rate;
+ i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(i2c_set_adapter_bus_clk_rate);
+
+int i2c_get_adapter_bus_clk_rate(struct i2c_adapter *adap)
+{
+ int bus_clk_rate;
+
+ i2c_lock_bus(adap, I2C_LOCK_ROOT_ADAPTER);
+ bus_clk_rate = adap->bus_clk_rate;
+ i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER);
+
+ return bus_clk_rate;
+}
+EXPORT_SYMBOL_GPL(i2c_get_adapter_bus_clk_rate);
+
/**
* i2c_handle_smbus_host_notify - Forward a Host Notify event to the correct
* I2C client.
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 2ce3efbe9198..9d876ad6373c 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -131,6 +131,10 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
/* Unlocked flavor */
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);

+/* Change bus clock rate for i2c adapter */
+extern int i2c_set_adapter_bus_clk_rate(struct i2c_adapter *adap, int bus_rate);
+extern int i2c_get_adapter_bus_clk_rate(struct i2c_adapter *adap);
+
/* This is the very generalized SMBus access routine. You probably do not
want to use this, though; one of the functions below may be much easier,
and probably just as fast.
@@ -737,6 +741,7 @@ struct i2c_adapter {
const struct i2c_adapter_quirks *quirks;

struct irq_domain *host_notify_domain;
+ unsigned long bus_clk_rate;
struct regulator *bus_regulator;
};
#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
--
2.34.1

8 changes: 0 additions & 8 deletions kernel/realsense/d4xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -4962,10 +4962,8 @@ static int ds5_dfu_device_open(struct inode *inode, struct file *file)
struct ds5 *state = container_of(inode->i_cdev, struct ds5,
dfu_dev.ds5_cdev);
#ifdef CONFIG_TEGRA_CAMERA_PLATFORM
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10)
struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(
state->client->adapter);
#endif
#endif
mutex_lock(&state->lock);
if (state->dfu_dev.device_open_count) {
Expand All @@ -4983,7 +4981,6 @@ static int ds5_dfu_device_open(struct inode *inode, struct file *file)
}
file->private_data = state;
#ifdef CONFIG_TEGRA_CAMERA_PLATFORM
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10)
/* get i2c controller and set dfu bus clock rate */
while (parent && i2c_parent_is_i2c_adapter(parent))
parent = i2c_parent_is_i2c_adapter(state->client->adapter);
Expand All @@ -5000,7 +4997,6 @@ static int ds5_dfu_device_open(struct inode *inode, struct file *file)

state->dfu_dev.bus_clk_rate = i2c_get_adapter_bus_clk_rate(parent);
i2c_set_adapter_bus_clk_rate(parent, DFU_I2C_BUS_CLK_RATE);
#endif
#endif
mutex_unlock(&state->lock);
return 0;
Expand Down Expand Up @@ -5060,10 +5056,8 @@ static int ds5_dfu_device_release(struct inode *inode, struct file *file)
{
struct ds5 *state = container_of(inode->i_cdev, struct ds5, dfu_dev.ds5_cdev);
#ifdef CONFIG_TEGRA_CAMERA_PLATFORM
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10)
struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(
state->client->adapter);
#endif
#endif
int ret = 0, retry = 10;
mutex_lock(&state->lock);
Expand All @@ -5080,7 +5074,6 @@ static int ds5_dfu_device_release(struct inode *inode, struct file *file)
devm_kfree(&state->client->dev, state->dfu_dev.dfu_msg);
state->dfu_dev.dfu_msg = NULL;
#ifdef CONFIG_TEGRA_CAMERA_PLATFORM
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10)
/* get i2c controller and restore bus clock rate */
while (parent && i2c_parent_is_i2c_adapter(parent))
parent = i2c_parent_is_i2c_adapter(state->client->adapter);
Expand All @@ -5094,7 +5087,6 @@ static int ds5_dfu_device_release(struct inode *inode, struct file *file)
state->dfu_dev.bus_clk_rate);

i2c_set_adapter_bus_clk_rate(parent, state->dfu_dev.bus_clk_rate);
#endif
#endif
/* Verify communication */
do {
Expand Down

0 comments on commit ce601bc

Please sign in to comment.