From b9189b85b1c66898761783b716c5a4017c5b95e5 Mon Sep 17 00:00:00 2001 From: Mk16kawai <1041324852hzq@sina.com> Date: Mon, 9 Dec 2024 18:12:24 +0800 Subject: [PATCH] [i2c5@gpio] supports dynamic rate modification --- linux_5.10/drivers/i2c/busses/i2c-gpio.c | 90 ++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/linux_5.10/drivers/i2c/busses/i2c-gpio.c b/linux_5.10/drivers/i2c/busses/i2c-gpio.c index a4a6825c8..965c58160 100644 --- a/linux_5.10/drivers/i2c/busses/i2c-gpio.c +++ b/linux_5.10/drivers/i2c/busses/i2c-gpio.c @@ -18,6 +18,20 @@ #include #include +#include +#include +#include +#include +#include + +#define DEBUG_LOG_TAG "[i2c gpio]" +#define DEBUG_LOG(fmt, ...) printk(KERN_DEBUG DEBUG_LOG_TAG " " fmt, ##__VA_ARGS__) + +static int delay_us = -1; +volatile int udelay_value = 0; +struct kobject *udelay_v; +struct i2c_algo_bit_data *udelay_data = NULL; + struct i2c_gpio_private_data { struct gpio_desc *sda; struct gpio_desc *scl; @@ -32,6 +46,49 @@ struct i2c_gpio_private_data { #endif }; +static void show_freq(void) +{ + switch(udelay_data->udelay) { + case 0: + DEBUG_LOG("Freq max ~ 750KHz for MaixCAM"); + break; + case 1: + DEBUG_LOG("Freq 500KHz ~ 308KHz for MaixCAM"); + break; + case 2: + DEBUG_LOG("Freq 250KHz"); + break; + case 5: + DEBUG_LOG("Freq 100KHz"); + break; + default: + DEBUG_LOG("Freq - Unknown"); + break; + } +} + +static ssize_t __udelay_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + // DEBUG_LOG("Read udelay value: %d\n", udelay_value); + show_freq(); + udelay_value = udelay_data->udelay; + return sprintf(buf, "%d", udelay_value); +} + +static ssize_t __udelay_store(struct kobject *kobj, + struct kobj_attribute *attr,const char *buf, size_t count) +{ + // int old_value = udelay_value; + sscanf(buf,"%d",&udelay_value); + udelay_data->udelay = udelay_value; + // DEBUG_LOG("Write udelay value: %d --> %d\n", old_value, udelay_value); + // show_freq(); + return count; +} + +struct kobj_attribute sysfs_test_attr = __ATTR(udelay_v, 0664, __udelay_show, __udelay_store); + /* * Toggle SDA by changing the output value of the pin. This is only * valid for pins configured as open drain (i.e. setting the value @@ -364,6 +421,7 @@ static int i2c_gpio_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; enum gpiod_flags gflags; int ret; + struct device *curr_dev; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -420,6 +478,9 @@ static int i2c_gpio_probe(struct platform_device *pdev) bit_data->getscl = i2c_gpio_getscl; bit_data->getsda = i2c_gpio_getsda; + udelay_data = bit_data; + + /* Get udelay from device tree */ if (pdata->udelay) bit_data->udelay = pdata->udelay; else if (pdata->scl_is_output_only) @@ -432,6 +493,17 @@ static int i2c_gpio_probe(struct platform_device *pdev) else bit_data->timeout = HZ / 10; /* 100 ms */ + /* If `insmod ko udelay_value=x` (x>=0) */ + if (delay_us >= 0) { + DEBUG_LOG("Get parameters from insmod: %d\n", delay_us); + bit_data->udelay = delay_us; + } else { + DEBUG_LOG("Get parameters from device tree: %d\n", bit_data->udelay); + } + + DEBUG_LOG("delay us: %d\n", bit_data->udelay); + show_freq(); + bit_data->data = priv; adap->owner = THIS_MODULE; @@ -464,6 +536,17 @@ static int i2c_gpio_probe(struct platform_device *pdev) i2c_gpio_fault_injector_init(pdev); + /* Add udelay kobj */ + curr_dev = &pdev->dev; + udelay_v = kobject_create_and_add("udelay_value", &curr_dev->kobj); + if(sysfs_create_file(udelay_v, &sysfs_test_attr.attr)){ + DEBUG_LOG("Create sysfs file failed!\n"); + kobject_put(udelay_v); + sysfs_remove_file(kernel_kobj, &sysfs_test_attr.attr); + } + + udelay_value = udelay_data->udelay; + return 0; } @@ -479,6 +562,10 @@ static int i2c_gpio_remove(struct platform_device *pdev) i2c_del_adapter(adap); + /* Remove udelay kobj */ + kobject_put(udelay_v); + sysfs_remove_file(kernel_kobj, &sysfs_test_attr.attr); + return 0; } @@ -500,6 +587,9 @@ static struct platform_driver i2c_gpio_driver = { .remove = i2c_gpio_remove, }; +module_param(delay_us, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(delay_us, "An integer parameter for the i2c udelay"); + static int __init i2c_gpio_init(void) { int ret;