From 56a50667cbcfaf95eea9128d5676af94e54b51a8 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 1 Nov 2024 23:09:51 +0100 Subject: i2c: Replace list-based mechanism for handling auto-detected clients So far a list is used to track auto-detected clients per driver. The same functionality can be achieved much simpler by flagging auto-detected clients. Two notes regarding the usage of driver_for_each_device: In our case it can't fail, however the function is annotated __must_check. So a little workaround is needed to avoid a compiler warning. Then we may remove nodes from the list over which we iterate. This is safe, see the explanation at the beginning of lib/klist.c. Signed-off-by: Heiner Kallweit [wsa: fixed description of the new flag] Signed-off-by: Wolfram Sang --- include/linux/i2c.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 388ce71a29a9..072a62244e14 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -244,7 +244,6 @@ enum i2c_driver_flags { * @id_table: List of I2C devices supported by this driver * @detect: Callback for device detection * @address_list: The I2C addresses to probe (for detect) - * @clients: List of detected clients we created (for i2c-core use only) * @flags: A bitmask of flags defined in &enum i2c_driver_flags * * The driver.owner field should be set to the module owner of this driver. @@ -299,7 +298,6 @@ struct i2c_driver { /* Device detection callback for automatic device creation */ int (*detect)(struct i2c_client *client, struct i2c_board_info *info); const unsigned short *address_list; - struct list_head clients; u32 flags; }; @@ -334,6 +332,7 @@ struct i2c_client { #define I2C_CLIENT_SLAVE 0x20 /* we are the slave */ #define I2C_CLIENT_HOST_NOTIFY 0x40 /* We want to use I2C host notify */ #define I2C_CLIENT_WAKE 0x80 /* for board_info; true iff can wake */ +#define I2C_CLIENT_AUTO 0x100 /* client was auto-detected */ #define I2C_CLIENT_SCCB 0x9000 /* Use Omnivision SCCB protocol */ /* Must match I2C_M_STOP|IGNORE_NAK */ -- cgit v1.2.3 From 3cfe39b3a845593a485ab1c716615979004ef9f6 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 1 Nov 2024 23:11:39 +0100 Subject: i2c: Replace list-based mechanism for handling userspace-created clients Similar to the list of auto-detected clients, we can also replace the list of userspace-created clients with flagging such client devices. Signed-off-by: Heiner Kallweit [wsa: fixed description of the new flag; reordered new code in 'device_store' to have single exit point; fixed whitespace errors; folded cleanup patch into this one] Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-base.c | 61 ++++++++++++++++----------------------------- include/linux/i2c.h | 7 +----- 2 files changed, 23 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 42c62839de0c..bd90a6084fc0 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1293,14 +1293,12 @@ new_device_store(struct device *dev, struct device_attribute *attr, info.flags |= I2C_CLIENT_SLAVE; } + info.flags |= I2C_CLIENT_USER; + client = i2c_new_client_device(adap, &info); if (IS_ERR(client)) return PTR_ERR(client); - /* Keep track of the added device */ - mutex_lock(&adap->userspace_clients_lock); - list_add_tail(&client->detected, &adap->userspace_clients); - mutex_unlock(&adap->userspace_clients_lock); dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device", info.type, info.addr); @@ -1308,6 +1306,15 @@ new_device_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_WO(new_device); +static int __i2c_find_user_addr(struct device *dev, void *addrp) +{ + struct i2c_client *client = i2c_verify_client(dev); + unsigned short addr = *(unsigned short *)addrp; + + return client && client->flags & I2C_CLIENT_USER && + i2c_encode_flags_to_addr(client) == addr; +} + /* * And of course let the users delete the devices they instantiated, if * they got it wrong. This interface can only be used to delete devices @@ -1322,7 +1329,7 @@ delete_device_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_adapter *adap = to_i2c_adapter(dev); - struct i2c_client *client, *next; + struct device *child_dev; unsigned short addr; char end; int res; @@ -1338,28 +1345,19 @@ delete_device_store(struct device *dev, struct device_attribute *attr, return -EINVAL; } + mutex_lock(&core_lock); /* Make sure the device was added through sysfs */ - res = -ENOENT; - mutex_lock_nested(&adap->userspace_clients_lock, - i2c_adapter_depth(adap)); - list_for_each_entry_safe(client, next, &adap->userspace_clients, - detected) { - if (i2c_encode_flags_to_addr(client) == addr) { - dev_info(dev, "%s: Deleting device %s at 0x%02hx\n", - "delete_device", client->name, client->addr); - - list_del(&client->detected); - i2c_unregister_device(client); - res = count; - break; - } + child_dev = device_find_child(&adap->dev, &addr, __i2c_find_user_addr); + if (child_dev) { + i2c_unregister_device(i2c_verify_client(child_dev)); + put_device(child_dev); + } else { + dev_err(dev, "Can't find userspace-created device at %#x\n", addr); + count = -ENOENT; } - mutex_unlock(&adap->userspace_clients_lock); + mutex_unlock(&core_lock); - if (res < 0) - dev_err(dev, "%s: Can't find device in list\n", - "delete_device"); - return res; + return count; } static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, S_IWUSR, NULL, delete_device_store); @@ -1530,8 +1528,6 @@ static int i2c_register_adapter(struct i2c_adapter *adap) adap->locked_flags = 0; rt_mutex_init(&adap->bus_lock); rt_mutex_init(&adap->mux_lock); - mutex_init(&adap->userspace_clients_lock); - INIT_LIST_HEAD(&adap->userspace_clients); /* Set default timeout to 1 second if not already set */ if (adap->timeout == 0) @@ -1722,7 +1718,6 @@ static int __unregister_dummy(struct device *dev, void *dummy) void i2c_del_adapter(struct i2c_adapter *adap) { struct i2c_adapter *found; - struct i2c_client *client, *next; /* First make sure that this adapter was ever added */ mutex_lock(&core_lock); @@ -1735,18 +1730,6 @@ void i2c_del_adapter(struct i2c_adapter *adap) i2c_acpi_remove_space_handler(adap); - /* Remove devices instantiated from sysfs */ - mutex_lock_nested(&adap->userspace_clients_lock, - i2c_adapter_depth(adap)); - list_for_each_entry_safe(client, next, &adap->userspace_clients, - detected) { - dev_dbg(&adap->dev, "Removing %s at 0x%x\n", client->name, - client->addr); - list_del(&client->detected); - i2c_unregister_device(client); - } - mutex_unlock(&adap->userspace_clients_lock); - /* Detach any active clients. This can't fail, thus we do not * check the returned value. This is a two-pass process, because * we can't remove the dummy devices during the first pass: they diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 072a62244e14..66fb3d6cf686 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -313,8 +313,6 @@ struct i2c_driver { * @dev: Driver model device node for the slave. * @init_irq: IRQ that was set at initialization * @irq: indicates the IRQ generated by this device (if any) - * @detected: member of an i2c_driver.clients list or i2c-core's - * userspace_devices list * @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter * calls it to pass on slave events to the slave driver. * @devres_group_id: id of the devres group that will be created for resources @@ -333,6 +331,7 @@ struct i2c_client { #define I2C_CLIENT_HOST_NOTIFY 0x40 /* We want to use I2C host notify */ #define I2C_CLIENT_WAKE 0x80 /* for board_info; true iff can wake */ #define I2C_CLIENT_AUTO 0x100 /* client was auto-detected */ +#define I2C_CLIENT_USER 0x200 /* client was userspace-created */ #define I2C_CLIENT_SCCB 0x9000 /* Use Omnivision SCCB protocol */ /* Must match I2C_M_STOP|IGNORE_NAK */ @@ -344,7 +343,6 @@ struct i2c_client { struct device dev; /* the device structure */ int init_irq; /* irq set at initialization */ int irq; /* irq issued by device */ - struct list_head detected; #if IS_ENABLED(CONFIG_I2C_SLAVE) i2c_slave_cb_t slave_cb; /* callback for slave mode */ #endif @@ -750,9 +748,6 @@ struct i2c_adapter { char name[48]; struct completion dev_released; - struct mutex userspace_clients_lock; - struct list_head userspace_clients; - struct i2c_bus_recovery_info *bus_recovery_info; const struct i2c_adapter_quirks *quirks; -- cgit v1.2.3 From b04ce63859793e3439b394976b8d29e785d4d69a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 11 Dec 2024 11:23:35 +0100 Subject: i2c: davinci: kill platform data There are no more board file users of this driver. The platform data structure is only used internally. Two of the four fields it stores are not used at all anymore. Pull the remainder into the driver data struct and shrink code by removing parts that are now dead code. Signed-off-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20241211102337.37956-1-brgl@bgdev.pl Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-davinci.c | 84 +++++++------------------------ include/linux/platform_data/i2c-davinci.h | 26 ---------- 2 files changed, 19 insertions(+), 91 deletions(-) delete mode 100644 include/linux/platform_data/i2c-davinci.h (limited to 'include') diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 71dc0a6688b7..4b499931fdfd 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -26,7 +26,6 @@ #include #include #include -#include #include /* ----- global defines ----------------------------------------------- */ @@ -117,6 +116,8 @@ /* timeout for pm runtime autosuspend */ #define DAVINCI_I2C_PM_TIMEOUT 1000 /* ms */ +#define DAVINCI_I2C_DEFAULT_BUS_FREQ 100 + struct davinci_i2c_dev { struct device *dev; void __iomem *base; @@ -132,13 +133,10 @@ struct davinci_i2c_dev { #ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition; #endif - struct davinci_i2c_platform_data *pdata; -}; - -/* default platform data to use if not supplied in the platform_device */ -static struct davinci_i2c_platform_data davinci_i2c_platform_data_default = { - .bus_freq = 100, - .bus_delay = 0, + /* standard bus frequency (kHz) */ + unsigned int bus_freq; + /* Chip has a ICPFUNC register */ + bool has_pfunc; }; static inline void davinci_i2c_write_reg(struct davinci_i2c_dev *i2c_dev, @@ -168,7 +166,6 @@ static inline void davinci_i2c_reset_ctrl(struct davinci_i2c_dev *i2c_dev, static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev) { - struct davinci_i2c_platform_data *pdata = dev->pdata; u16 psc; u32 clk; u32 d; @@ -212,16 +209,16 @@ static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev) if (of_node && of_device_is_compatible(of_node, "ti,keystone-i2c")) d = 6; - clk = ((input_clock / (psc + 1)) / (pdata->bus_freq * 1000)); + clk = ((input_clock / (psc + 1)) / (dev->bus_freq * 1000)); /* Avoid driving the bus too fast because of rounding errors above */ - if (input_clock / (psc + 1) / clk > pdata->bus_freq * 1000) + if (input_clock / (psc + 1) / clk > dev->bus_freq * 1000) clk++; /* * According to I2C-BUS Spec 2.1, in FAST-MODE LOW period should be at * least 1.3uS, which is not the case with 50% duty cycle. Driving HIGH * to LOW ratio as 1 to 2 is more safe. */ - if (pdata->bus_freq > 100) + if (dev->bus_freq > 100) clkl = (clk << 1) / 3; else clkl = (clk >> 1); @@ -255,8 +252,6 @@ static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev) */ static int i2c_davinci_init(struct davinci_i2c_dev *dev) { - struct davinci_i2c_platform_data *pdata = dev->pdata; - /* put I2C into reset */ davinci_i2c_reset_ctrl(dev, 0); @@ -274,8 +269,7 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev) davinci_i2c_read_reg(dev, DAVINCI_I2C_CLKL_REG)); dev_dbg(dev->dev, "CLKH = %d\n", davinci_i2c_read_reg(dev, DAVINCI_I2C_CLKH_REG)); - dev_dbg(dev->dev, "bus_freq = %dkHz, bus_delay = %d\n", - pdata->bus_freq, pdata->bus_delay); + dev_dbg(dev->dev, "bus_freq = %dkHz\n", dev->bus_freq); /* Take the I2C module out of reset: */ @@ -309,12 +303,6 @@ static void davinci_i2c_unprepare_recovery(struct i2c_adapter *adap) i2c_davinci_init(dev); } -static struct i2c_bus_recovery_info davinci_i2c_gpio_recovery_info = { - .recover_bus = i2c_generic_scl_recovery, - .prepare_recovery = davinci_i2c_prepare_recovery, - .unprepare_recovery = davinci_i2c_unprepare_recovery, -}; - static void davinci_i2c_set_scl(struct i2c_adapter *adap, int val) { struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); @@ -414,7 +402,6 @@ static int i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) { struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); - struct davinci_i2c_platform_data *pdata = dev->pdata; u32 flag; u16 w; unsigned long time_left; @@ -424,10 +411,6 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) return -EADDRNOTAVAIL; } - /* Introduce a delay, required for some boards (e.g Davinci EVM) */ - if (pdata->bus_delay) - udelay(pdata->bus_delay); - /* set the target address */ davinci_i2c_write_reg(dev, DAVINCI_I2C_SAR_REG, msg->addr); @@ -758,8 +741,8 @@ static int davinci_i2c_probe(struct platform_device *pdev) { struct davinci_i2c_dev *dev; struct i2c_adapter *adap; - struct i2c_bus_recovery_info *rinfo; int r, irq; + u32 prop; irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -773,29 +756,15 @@ static int davinci_i2c_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->irq = irq; - dev->pdata = dev_get_platdata(&pdev->dev); platform_set_drvdata(pdev, dev); - if (!dev->pdata && pdev->dev.of_node) { - u32 prop; - - dev->pdata = devm_kzalloc(&pdev->dev, - sizeof(struct davinci_i2c_platform_data), GFP_KERNEL); - if (!dev->pdata) - return -ENOMEM; - - memcpy(dev->pdata, &davinci_i2c_platform_data_default, - sizeof(struct davinci_i2c_platform_data)); - if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency", - &prop)) - dev->pdata->bus_freq = prop / 1000; - - dev->pdata->has_pfunc = - of_property_read_bool(pdev->dev.of_node, - "ti,has-pfunc"); - } else if (!dev->pdata) { - dev->pdata = &davinci_i2c_platform_data_default; - } + r = device_property_read_u32(&pdev->dev, "clock-frequency", &prop); + if (r) + prop = DAVINCI_I2C_DEFAULT_BUS_FREQ; + + dev->bus_freq = prop / 1000; + + dev->has_pfunc = device_property_present(&pdev->dev, "ti,has-pfunc"); dev->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) @@ -843,23 +812,8 @@ static int davinci_i2c_probe(struct platform_device *pdev) adap->timeout = DAVINCI_I2C_TIMEOUT; adap->dev.of_node = pdev->dev.of_node; - if (dev->pdata->has_pfunc) + if (dev->has_pfunc) adap->bus_recovery_info = &davinci_i2c_scl_recovery_info; - else if (dev->pdata->gpio_recovery) { - rinfo = &davinci_i2c_gpio_recovery_info; - adap->bus_recovery_info = rinfo; - rinfo->scl_gpiod = devm_gpiod_get(&pdev->dev, "scl", - GPIOD_OUT_HIGH_OPEN_DRAIN); - if (IS_ERR(rinfo->scl_gpiod)) { - r = PTR_ERR(rinfo->scl_gpiod); - goto err_unuse_clocks; - } - rinfo->sda_gpiod = devm_gpiod_get(&pdev->dev, "sda", GPIOD_IN); - if (IS_ERR(rinfo->sda_gpiod)) { - r = PTR_ERR(rinfo->sda_gpiod); - goto err_unuse_clocks; - } - } adap->nr = pdev->id; r = i2c_add_numbered_adapter(adap); diff --git a/include/linux/platform_data/i2c-davinci.h b/include/linux/platform_data/i2c-davinci.h deleted file mode 100644 index 98967df07468..000000000000 --- a/include/linux/platform_data/i2c-davinci.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * DaVinci I2C controller platform_device info - * - * Author: Vladimir Barinov, MontaVista Software, Inc. - * - * 2007 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. -*/ - -#ifndef __ASM_ARCH_I2C_H -#define __ASM_ARCH_I2C_H - -/* All frequencies are expressed in kHz */ -struct davinci_i2c_platform_data { - unsigned int bus_freq; /* standard bus frequency (kHz) */ - unsigned int bus_delay; /* post-transaction delay (usec) */ - bool gpio_recovery; /* Use GPIO recovery method */ - bool has_pfunc; /* Chip has a ICPFUNC register */ -}; - -/* for board setup code */ -void davinci_init_i2c(struct davinci_i2c_platform_data *); - -#endif /* __ASM_ARCH_I2C_H */ -- cgit v1.2.3 From d06905d686107c8343ff71aa4f3c881cc0a9a7b9 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 9 Jan 2025 13:21:10 +0100 Subject: i2c: add core-managed per-client directory in debugfs More and more I2C client drivers use debugfs entries and currently they need to manage a subdir for their files on their own. This means inconsistent naming for these subdirs and they are scattered all over the debugfs-tree as well. Not to mention the duplicated code. Let the I2C core provide and maintain a proper directory per client. Note: It was considered to save the additional pointer in 'struct i2c_client' and only provide a subdir when requested via a helper function. When sketching this approach, more and more corner cases appeared, though, so the current solution with its simple and unabiguous code was chosen. Signed-off-by: Wolfram Sang Reviewed-by: Guenter Roeck --- drivers/i2c/i2c-core-base.c | 4 ++++ include/linux/i2c.h | 1 + 2 files changed, 5 insertions(+) (limited to 'include') diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index b072030a9105..00f171ebc01f 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1015,6 +1015,8 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf if (status) goto out_remove_swnode; + client->debugfs = debugfs_create_dir(dev_name(&client->dev), adap->debugfs); + dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n", client->name, dev_name(&client->dev)); @@ -1058,6 +1060,8 @@ void i2c_unregister_device(struct i2c_client *client) if (ACPI_COMPANION(&client->dev)) acpi_device_clear_enumerated(ACPI_COMPANION(&client->dev)); + + debugfs_remove_recursive(client->debugfs); device_remove_software_node(&client->dev); device_unregister(&client->dev); } diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 66fb3d6cf686..36de788dc7fe 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -347,6 +347,7 @@ struct i2c_client { i2c_slave_cb_t slave_cb; /* callback for slave mode */ #endif void *devres_group_id; /* ID of probe devres group */ + struct dentry *debugfs; /* per-client debugfs dir */ }; #define to_i2c_client(d) container_of(d, struct i2c_client, dev) -- cgit v1.2.3 From 27c3f0e61f19d2306527406cad233d5f5915ca1e Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 15 Jan 2025 08:39:18 +0100 Subject: i2c: add kdoc for the new debugfs entry of clients When adding the new debugfs entry, its kdoc equivalent was forgotten. Add it now. Fixes: d06905d68610 ("i2c: add core-managed per-client directory in debugfs") Reported-by: Stephen Rothwell Closes: https://lore.kernel.org/r/20250115163146.6c48f066@canb.auug.org.au Signed-off-by: Wolfram Sang --- include/linux/i2c.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 36de788dc7fe..c31fd1dba3bd 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -317,6 +317,8 @@ struct i2c_driver { * calls it to pass on slave events to the slave driver. * @devres_group_id: id of the devres group that will be created for resources * acquired when probing this device. + * @debugfs: pointer to the debugfs subdirectory which the I2C core created + * for this client. * * An i2c_client identifies a single device (i.e. chip) connected to an * i2c bus. The behaviour exposed to Linux is defined by the driver -- cgit v1.2.3