From 88440208c6074e639a7ccc038c6a7ed4b6f8bb99 Mon Sep 17 00:00:00 2001 From: Tomas Melin Date: Tue, 10 Feb 2026 10:53:34 +0000 Subject: [PATCH 001/405] iio: industrialio-backend: support backend capabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not all backends support the full set of capabilities provided by the industrialio-backend framework. Capability bits can be used in frontends and backends for checking for a certain feature set, or if using related functions can be expected to fail. Capability bits should be set by a compatible backend and provided when registering the backend. Reviewed-by: Nuno Sá Reviewed-by: David Lechner Signed-off-by: Tomas Melin Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-backend.c | 16 ++++++++++++++++ include/linux/iio/backend.h | 24 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 447b694d6d5f..1afd00763da9 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -56,6 +56,7 @@ struct iio_backend { void *priv; const char *name; unsigned int cached_reg_addr; + u32 caps; /* * This index is relative to the frontend. Meaning that for * frontends with multiple backends, this will be the index of this @@ -774,6 +775,20 @@ int iio_backend_extend_chan_spec(struct iio_backend *back, } EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, "IIO_BACKEND"); +/** + * iio_backend_has_caps - Check if backend has specific capabilities + * @back: Backend device + * @caps: Capabilities to check + * + * RETURNS: + * True if backend has all the requested capabilities, false otherwise. + */ +bool iio_backend_has_caps(struct iio_backend *back, u32 caps) +{ + return (back->caps & caps) == caps; +} +EXPORT_SYMBOL_NS_GPL(iio_backend_has_caps, "IIO_BACKEND"); + static void iio_backend_release(void *arg) { struct iio_backend *back = arg; @@ -1114,6 +1129,7 @@ int devm_iio_backend_register(struct device *dev, back->ops = info->ops; back->name = info->name; + back->caps = info->caps; back->owner = dev->driver->owner; back->dev = dev; back->priv = priv; diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h index 7f815f3fed6a..4d15c2a9802c 100644 --- a/include/linux/iio/backend.h +++ b/include/linux/iio/backend.h @@ -84,6 +84,27 @@ enum iio_backend_filter_type { IIO_BACKEND_FILTER_TYPE_MAX }; +/** + * enum iio_backend_capabilities - Backend capabilities + * Backend capabilities can be used by frontends to check if a given + * functionality is supported by the backend. This is useful for frontend + * devices which are expected to work with alternative backend + * implementations. Capabilities are loosely coupled with operations, + * meaning that a capability requires certain operations to be implemented + * by the backend. A capability might be mapped to a single operation or + * multiple operations. + * + * @IIO_BACKEND_CAP_CALIBRATION: Backend supports digital interface + * calibration. Calibration procedure is device specific. + * @IIO_BACKEND_CAP_BUFFER: Support for IIO buffer interface. + * @IIO_BACKEND_CAP_ENABLE: Backend can be explicitly enabled/disabled. + */ +enum iio_backend_capabilities { + IIO_BACKEND_CAP_CALIBRATION = BIT(0), + IIO_BACKEND_CAP_BUFFER = BIT(1), + IIO_BACKEND_CAP_ENABLE = BIT(2), +}; + /** * struct iio_backend_ops - operations structure for an iio_backend * @enable: Enable backend. @@ -179,10 +200,12 @@ struct iio_backend_ops { * struct iio_backend_info - info structure for an iio_backend * @name: Backend name. * @ops: Backend operations. + * @caps: Backend capabilities. (bitmask of enum iio_backend_capabilities). */ struct iio_backend_info { const char *name; const struct iio_backend_ops *ops; + u32 caps; }; int iio_backend_chan_enable(struct iio_backend *back, unsigned int chan); @@ -235,6 +258,7 @@ int iio_backend_read_raw(struct iio_backend *back, long mask); int iio_backend_extend_chan_spec(struct iio_backend *back, struct iio_chan_spec *chan); +bool iio_backend_has_caps(struct iio_backend *back, u32 caps); void *iio_backend_get_priv(const struct iio_backend *conv); struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name); struct iio_backend *devm_iio_backend_fwnode_get(struct device *dev, From ed3be723b0ed3e8e35ee1b9ab14a94cc5630ff02 Mon Sep 17 00:00:00 2001 From: Tomas Melin Date: Tue, 10 Feb 2026 10:53:35 +0000 Subject: [PATCH 002/405] iio: adc: adi-axi-adc: define supported iio-backend capabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit axi-adc and axi-ad485x backend variants provide calibration support, whereas the axi-ad408x does not. Set accordingly. Reviewed-by: Nuno Sá Reviewed-by: David Lechner Signed-off-by: Tomas Melin Signed-off-by: Jonathan Cameron --- drivers/iio/adc/adi-axi-adc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index 5f445e0de9ea..ced0a2321ecf 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -621,6 +621,8 @@ static const struct iio_backend_ops adi_axi_adc_ops = { static const struct iio_backend_info adi_axi_adc_generic = { .name = "axi-adc", .ops = &adi_axi_adc_ops, + .caps = IIO_BACKEND_CAP_CALIBRATION | IIO_BACKEND_CAP_BUFFER | + IIO_BACKEND_CAP_ENABLE, }; static const struct iio_backend_ops adi_ad485x_ops = { @@ -645,6 +647,8 @@ static const struct iio_backend_ops adi_ad485x_ops = { static const struct iio_backend_info axi_ad485x = { .name = "axi-ad485x", .ops = &adi_ad485x_ops, + .caps = IIO_BACKEND_CAP_CALIBRATION | IIO_BACKEND_CAP_BUFFER | + IIO_BACKEND_CAP_ENABLE, }; static const struct iio_backend_ops adi_ad408x_ops = { @@ -665,6 +669,7 @@ static const struct iio_backend_ops adi_ad408x_ops = { static const struct iio_backend_info axi_ad408x = { .name = "axi-ad408x", .ops = &adi_ad408x_ops, + .caps = IIO_BACKEND_CAP_BUFFER | IIO_BACKEND_CAP_ENABLE, }; static int adi_axi_adc_probe(struct platform_device *pdev) From 9db3e8ddca3d84cc46e12344c38cf17f4f8744e8 Mon Sep 17 00:00:00 2001 From: Tomas Melin Date: Tue, 10 Feb 2026 10:53:36 +0000 Subject: [PATCH 003/405] iio: dac: adi-axi-dac: define supported iio-backend capabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backends support the buffer/enable capabilities so advertise it while registering in case a frontend makes checks for it. Reviewed-by: Nuno Sá Reviewed-by: David Lechner Signed-off-by: Tomas Melin Signed-off-by: Jonathan Cameron --- drivers/iio/dac/adi-axi-dac.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c index 9cc895bbe51a..33b07de1bfcb 100644 --- a/drivers/iio/dac/adi-axi-dac.c +++ b/drivers/iio/dac/adi-axi-dac.c @@ -869,11 +869,13 @@ static const struct iio_backend_ops axi_ad3552r_ops = { static const struct iio_backend_info axi_dac_generic = { .name = "axi-dac", .ops = &axi_dac_generic_ops, + .caps = IIO_BACKEND_CAP_BUFFER | IIO_BACKEND_CAP_ENABLE, }; static const struct iio_backend_info axi_ad3552r = { .name = "axi-ad3552r", .ops = &axi_ad3552r_ops, + .caps = IIO_BACKEND_CAP_BUFFER | IIO_BACKEND_CAP_ENABLE, }; static const struct regmap_config axi_dac_regmap_config = { From 519ecb5f100193dd79de2760dd7296688b2484b4 Mon Sep 17 00:00:00 2001 From: Tomas Melin Date: Tue, 10 Feb 2026 10:53:37 +0000 Subject: [PATCH 004/405] iio: adc: sd_adc_modulator: define supported iio-backend capabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This backend supports the added CAP_ENABLE capability. Reviewed-by: Nuno Sá Reviewed-by: David Lechner Signed-off-by: Tomas Melin Signed-off-by: Jonathan Cameron --- drivers/iio/adc/sd_adc_modulator.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/sd_adc_modulator.c b/drivers/iio/adc/sd_adc_modulator.c index 9f7a75168aac..218117c45ec8 100644 --- a/drivers/iio/adc/sd_adc_modulator.c +++ b/drivers/iio/adc/sd_adc_modulator.c @@ -77,6 +77,7 @@ static const struct iio_backend_ops sd_backend_ops = { static const struct iio_backend_info sd_backend_info = { .name = "sd-modulator", .ops = &sd_backend_ops, + .caps = IIO_BACKEND_CAP_ENABLE, }; static int iio_sd_mod_register(struct platform_device *pdev) From 89443dc7843c22411f7bdeffa513e4dc709c7ba2 Mon Sep 17 00:00:00 2001 From: Tomas Melin Date: Tue, 10 Feb 2026 10:53:38 +0000 Subject: [PATCH 005/405] iio: adc: ad9467: simplify device pointer in probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create alias for the device pointer to simplify referencing and keeping syntax and column width shorter. Suggested-by: Andy Shevchenko Signed-off-by: Tomas Melin Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad9467.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c index 022888545580..ee0190e711af 100644 --- a/drivers/iio/adc/ad9467.c +++ b/drivers/iio/adc/ad9467.c @@ -1300,12 +1300,13 @@ static void ad9467_debugfs_init(struct iio_dev *indio_dev) static int ad9467_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; struct iio_dev *indio_dev; struct ad9467_state *st; unsigned int id; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -1320,16 +1321,15 @@ static int ad9467_probe(struct spi_device *spi) if (AD9467_CAN_INVERT(st)) st->calib_map_size *= 2; - st->clk = devm_clk_get_enabled(&spi->dev, "adc-clk"); + st->clk = devm_clk_get_enabled(dev, "adc-clk"); if (IS_ERR(st->clk)) return PTR_ERR(st->clk); - st->pwrdown_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown", - GPIOD_OUT_LOW); + st->pwrdown_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_LOW); if (IS_ERR(st->pwrdown_gpio)) return PTR_ERR(st->pwrdown_gpio); - ret = ad9467_reset(&spi->dev); + ret = ad9467_reset(dev); if (ret) return ret; @@ -1339,7 +1339,7 @@ static int ad9467_probe(struct spi_device *spi) id = ad9467_spi_read(st, AN877_ADC_REG_CHIP_ID); if (id != st->info->id) { - dev_err(&spi->dev, "Mismatch CHIP_ID, got 0x%X, expected 0x%X\n", + dev_err(dev, "Mismatch CHIP_ID, got 0x%X, expected 0x%X\n", id, st->info->id); return -ENODEV; } @@ -1356,11 +1356,11 @@ static int ad9467_probe(struct spi_device *spi) if (ret) return ret; - ret = devm_iio_backend_request_buffer(&spi->dev, st->back, indio_dev); + ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); if (ret) return ret; - ret = devm_iio_backend_enable(&spi->dev, st->back); + ret = devm_iio_backend_enable(dev, st->back); if (ret) return ret; @@ -1368,7 +1368,7 @@ static int ad9467_probe(struct spi_device *spi) if (ret) return ret; - ret = devm_iio_device_register(&spi->dev, indio_dev); + ret = devm_iio_device_register(dev, indio_dev); if (ret) return ret; From 2e47110413d94cde2282f46112f3035237ace249 Mon Sep 17 00:00:00 2001 From: Tomas Melin Date: Tue, 10 Feb 2026 10:53:39 +0000 Subject: [PATCH 006/405] iio: adc: ad9467: check for backend capabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add capability checks for operation with backends that do not necessarily support full set of features, but are otherwise compatible with the device. This ensures a fully functional device, but with limited capabilities. Reviewed-by: Nuno Sá Reviewed-by: David Lechner Signed-off-by: Tomas Melin Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad9467.c | 80 ++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c index ee0190e711af..d611ca813c0c 100644 --- a/drivers/iio/adc/ad9467.c +++ b/drivers/iio/adc/ad9467.c @@ -925,7 +925,11 @@ static int __ad9467_update_clock(struct ad9467_state *st, long r_clk) return ret; guard(mutex)(&st->lock); - return ad9467_calibrate(st); + + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) + return ad9467_calibrate(st); + + return 0; } static int ad9467_write_raw(struct iio_dev *indio_dev, @@ -1131,12 +1135,15 @@ static ssize_t ad9467_chan_test_mode_read(struct file *file, len = scnprintf(buf, sizeof(buf), "Running \"%s\" Test:\n\t", ad9467_test_modes[chan->mode]); - ret = iio_backend_debugfs_print_chan_status(st->back, chan->idx, - buf + len, - sizeof(buf) - len); - if (ret < 0) - return ret; - len += ret; + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) { + ret = iio_backend_debugfs_print_chan_status(st->back, + chan->idx, + buf + len, + sizeof(buf) - len); + if (ret < 0) + return ret; + len += ret; + } } else if (chan->mode == AN877_ADC_TESTMODE_OFF) { len = scnprintf(buf, sizeof(buf), "No test Running...\n"); } else { @@ -1175,11 +1182,13 @@ static ssize_t ad9467_chan_test_mode_write(struct file *file, if (mode == AN877_ADC_TESTMODE_OFF) { unsigned int out_mode; - if (chan->mode == AN877_ADC_TESTMODE_PN9_SEQ || - chan->mode == AN877_ADC_TESTMODE_PN23_SEQ) { - ret = ad9467_backend_testmode_off(st, chan->idx); - if (ret) - return ret; + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) { + if (chan->mode == AN877_ADC_TESTMODE_PN9_SEQ || + chan->mode == AN877_ADC_TESTMODE_PN23_SEQ) { + ret = ad9467_backend_testmode_off(st, chan->idx); + if (ret) + return ret; + } } ret = ad9467_testmode_set(st, chan->idx, mode); @@ -1205,16 +1214,18 @@ static ssize_t ad9467_chan_test_mode_write(struct file *file, return ret; /* some patterns have a backend matching monitoring block */ - if (mode == AN877_ADC_TESTMODE_PN9_SEQ) { - ret = ad9467_backend_testmode_on(st, chan->idx, + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) { + if (mode == AN877_ADC_TESTMODE_PN9_SEQ) { + ret = ad9467_backend_testmode_on(st, chan->idx, IIO_BACKEND_ADI_PRBS_9A); - if (ret) - return ret; - } else if (mode == AN877_ADC_TESTMODE_PN23_SEQ) { - ret = ad9467_backend_testmode_on(st, chan->idx, + if (ret) + return ret; + } else if (mode == AN877_ADC_TESTMODE_PN23_SEQ) { + ret = ad9467_backend_testmode_on(st, chan->idx, IIO_BACKEND_ADI_PRBS_23A); - if (ret) - return ret; + if (ret) + return ret; + } } } @@ -1280,8 +1291,9 @@ static void ad9467_debugfs_init(struct iio_dev *indio_dev) if (!st->chan_test) return; - debugfs_create_file("calibration_table_dump", 0400, d, st, - &ad9467_calib_table_fops); + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) + debugfs_create_file("calibration_table_dump", 0400, d, st, + &ad9467_calib_table_fops); for (chan = 0; chan < st->info->num_channels; chan++) { snprintf(attr_name, sizeof(attr_name), "in_voltage%u_test_mode", @@ -1356,17 +1368,23 @@ static int ad9467_probe(struct spi_device *spi) if (ret) return ret; - ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); - if (ret) - return ret; + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_BUFFER)) { + ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); + if (ret) + return ret; + } - ret = devm_iio_backend_enable(dev, st->back); - if (ret) - return ret; + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_ENABLE)) { + ret = devm_iio_backend_enable(dev, st->back); + if (ret) + return ret; + } - ret = ad9467_calibrate(st); - if (ret) - return ret; + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) { + ret = ad9467_calibrate(st); + if (ret) + return ret; + } ret = devm_iio_device_register(dev, indio_dev); if (ret) From 577fe2fdd42fb16cacb1178cb94a42dd7c595914 Mon Sep 17 00:00:00 2001 From: Yasin Lee Date: Fri, 13 Feb 2026 23:14:45 +0800 Subject: [PATCH 007/405] dt-bindings: iio: proximity: hx9023s: support firmware-name property The hx9023s requires a firmware blob containing board-specific configuration data used to initialize its internal sensing engine. Although the silicon is identical across platforms, different products may use different electrode layouts, PCB routing, cover materials and mechanical stack-ups. These physical differences require distinct calibration parameters and register configuration tables in order for the sensor to operate correctly. The driver has always required firmware and historically assumed a single default firmware file name suitable for the reference design. However, this assumption does not hold for boards with different physical sensor layouts. The default firmware file name remains unchanged and continues to be used for existing platforms. Allowing the firmware file name to be specified via device tree enables selecting the appropriate hardware-specific configuration when the board design differs. This property does not change the existing ABI and is optional. Signed-off-by: Yasin Lee Acked-by: Krzysztof Kozlowski Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/proximity/tyhx,hx9023s.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/proximity/tyhx,hx9023s.yaml b/Documentation/devicetree/bindings/iio/proximity/tyhx,hx9023s.yaml index 64ce8bc8bd36..cc5b5284c267 100644 --- a/Documentation/devicetree/bindings/iio/proximity/tyhx,hx9023s.yaml +++ b/Documentation/devicetree/bindings/iio/proximity/tyhx,hx9023s.yaml @@ -28,6 +28,9 @@ properties: vdd-supply: true + firmware-name: + maxItems: 1 + "#address-cells": const: 1 @@ -65,6 +68,7 @@ examples: interrupt-parent = <&pio>; interrupts = <16 IRQ_TYPE_EDGE_FALLING>; vdd-supply = <&pp1800_prox>; + firmware-name = "hx9023s.bin"; #address-cells = <1>; #size-cells = <0>; From 8e901c49ffc8d59adabae4020724f96770d35db7 Mon Sep 17 00:00:00 2001 From: Yasin Lee Date: Fri, 13 Feb 2026 23:14:46 +0800 Subject: [PATCH 008/405] iio: proximity: hx9023s: support firmware-name property Add an optional firmware-name property to specify the firmware file. If not provided, the driver falls back to the default firmware name. Reviewed-by: Andy Shevchenko Signed-off-by: Yasin Lee Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/hx9023s.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/iio/proximity/hx9023s.c b/drivers/iio/proximity/hx9023s.c index 2918dfc0df54..5fa3f4b179dd 100644 --- a/drivers/iio/proximity/hx9023s.c +++ b/drivers/iio/proximity/hx9023s.c @@ -1086,6 +1086,7 @@ static int hx9023s_probe(struct i2c_client *client) struct device *dev = &client->dev; struct iio_dev *indio_dev; struct hx9023s_data *data; + const char *fw_name; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); @@ -1123,7 +1124,9 @@ static int hx9023s_probe(struct i2c_client *client) if (ret) return dev_err_probe(dev, ret, "channel config failed\n"); - ret = request_firmware_nowait(THIS_MODULE, true, "hx9023s.bin", dev, + fw_name = "hx9023s.bin"; + device_property_read_string(dev, "firmware-name", &fw_name); + ret = request_firmware_nowait(THIS_MODULE, true, fw_name, dev, GFP_KERNEL, data, hx9023s_cfg_update); if (ret) return dev_err_probe(dev, ret, "reg config failed\n"); From 25ac1dea217f328ea71f8f09bfb290534de3cbce Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Wed, 11 Feb 2026 19:10:05 +0200 Subject: [PATCH 009/405] iio: adc: pac1934: Return -ENOMEM on memory allocation failure devm_kzalloc() returns NULL on allocation failure. The appropriate error code for this condition is -ENOMEM, not -EINVAL. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/pac1934.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/pac1934.c b/drivers/iio/adc/pac1934.c index 712b5e9caba6..23055405a6e0 100644 --- a/drivers/iio/adc/pac1934.c +++ b/drivers/iio/adc/pac1934.c @@ -1351,7 +1351,7 @@ static int pac1934_prep_iio_channels(struct pac1934_chip_info *info, struct iio_ dyn_ch_struct = devm_kzalloc(dev, channel_size, GFP_KERNEL); if (!dyn_ch_struct) - return -EINVAL; + return -ENOMEM; tmp_data = dyn_ch_struct; From bc2cb23607eb0ff99a67e260ec33d01c6e6f1290 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Wed, 11 Feb 2026 19:10:06 +0200 Subject: [PATCH 010/405] iio: frequency: adf4350: Return -ENOMEM on memory allocation failure adf4350_parse_dt() returns NULL only when devm_kzalloc() fails. The caller should return -ENOMEM in this case, not -EINVAL. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adf4350.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index ed1741165f55..3883b63dcc3c 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -607,7 +607,7 @@ static int adf4350_probe(struct spi_device *spi) if (dev_fwnode(&spi->dev)) { pdata = adf4350_parse_dt(&spi->dev); if (pdata == NULL) - return -EINVAL; + return -ENOMEM; } else { pdata = dev_get_platdata(&spi->dev); } From 5cf5654b1975d76dbd2e5696a8a8e81c601e0744 Mon Sep 17 00:00:00 2001 From: Ethan Tidmore Date: Fri, 13 Feb 2026 18:53:32 -0600 Subject: [PATCH 011/405] iio: adc: ad4062: Add missing IS_ERR() check In the function ad4062_sizeof_storagebits() iio_get_current_scan_type() is called which can return an error pointer and is not checked for it. Also the function ad4062_sizeof_storagebits() returns type size_t but, is only used once and the variable assigned from it is type u8. Add check for error pointer in ad4062_sizeof_storagebits() and change return type to int so the error code can be properly propagated and then checked. Fixes: 23cc92280302d ("iio: adc: ad4062: Add IIO Trigger support") Reported-by: kernel test robot Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/202512280539.AholFF7m-lkp@intel.com/ Signed-off-by: Ethan Tidmore Reviewed-by: Jorge Marques Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4062.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ad4062.c b/drivers/iio/adc/ad4062.c index dd4ad32aa6f5..c864de3b46ba 100644 --- a/drivers/iio/adc/ad4062.c +++ b/drivers/iio/adc/ad4062.c @@ -1199,11 +1199,14 @@ static int ad4062_write_event_value(struct iio_dev *indio_dev, * The AD4062 in burst averaging mode increases realbits from 16-bits to * 20-bits, increasing the storagebits from 16-bits to 32-bits. */ -static inline size_t ad4062_sizeof_storagebits(struct ad4062_state *st) +static inline int ad4062_sizeof_storagebits(struct ad4062_state *st) { const struct iio_scan_type *scan_type = iio_get_current_scan_type(st->indio_dev, st->chip->channels); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + return BITS_TO_BYTES(scan_type->storagebits); } @@ -1233,7 +1236,12 @@ static int pm_ad4062_triggered_buffer_postenable(struct ad4062_state *st) if (ret) return ret; - st->conv_sizeof = ad4062_sizeof_storagebits(st); + ret = ad4062_sizeof_storagebits(st); + if (ret < 0) + return ret; + + st->conv_sizeof = ret; + st->conv_addr = ad4062_get_conv_addr(st, st->conv_sizeof); /* CONV_READ requires read to trigger first sample. */ struct i3c_xfer xfer_sample[2] = { From 63670c90f02b3732c49c2b1fee8f5dffd2dc8eb7 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 23 Jan 2026 14:37:31 -0600 Subject: [PATCH 012/405] dt-bindings: iio: adc: adi,ad7380: add spi-rx-bus-width property Add spi-rx-bus-width property to describe how many SDO lines are wired up on the ADC. These chips are simultaneous sampling ADCs and have one SDO line per channel, either 2 or 4 total depending on the part number. Reviewed-by: Rob Herring (Arm) Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/adi,ad7380.yaml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml index b91bfb16ed6b..396e1a1aa805 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml @@ -62,6 +62,11 @@ properties: spi-cpol: true spi-cpha: true + spi-rx-bus-width: + maxItems: 4 + items: + maximum: 1 + vcc-supply: description: A 3V to 3.6V supply that powers the chip. @@ -160,6 +165,23 @@ patternProperties: unevaluatedProperties: false allOf: + # 2-channel chips only have two SDO lines + - if: + properties: + compatible: + enum: + - adi,ad7380 + - adi,ad7381 + - adi,ad7383 + - adi,ad7384 + - adi,ad7386 + - adi,ad7387 + - adi,ad7388 + then: + properties: + spi-rx-bus-width: + maxItems: 2 + # pseudo-differential chips require common mode voltage supplies, # true differential chips don't use them - if: @@ -284,6 +306,7 @@ examples: spi-cpol; spi-cpha; spi-max-frequency = <80000000>; + spi-rx-bus-width = <1>, <1>, <1>, <1>; interrupts = <27 IRQ_TYPE_EDGE_FALLING>; interrupt-parent = <&gpio0>; From 196c9df178652732ceab85b743997a4ab9e8cc82 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 23 Jan 2026 14:37:32 -0600 Subject: [PATCH 013/405] iio: adc: ad7380: add support for multiple SPI lanes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for multiple SPI lanes to increase throughput. The AD7380 family of ADCs have multiple SDO lines on the chip that can be used to read each channel on a separate SPI lane. If wired up to a SPI controller that supports it, the driver will now take advantage of this feature. This allows reaching the maximum sample rate advertised in the datasheet when combined with SPI offloading. Reviewed-by: Nuno Sá Reviewed-by: Marcelo Schmitt Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7380.c | 51 ++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c index bfd908deefc0..ca411371816f 100644 --- a/drivers/iio/adc/ad7380.c +++ b/drivers/iio/adc/ad7380.c @@ -77,8 +77,7 @@ #define AD7380_CONFIG1_REFSEL BIT(1) #define AD7380_CONFIG1_PMODE BIT(0) -#define AD7380_CONFIG2_SDO2 GENMASK(9, 8) -#define AD7380_CONFIG2_SDO BIT(8) +#define AD7380_CONFIG2_SDO GENMASK(9, 8) #define AD7380_CONFIG2_RESET GENMASK(7, 0) #define AD7380_CONFIG2_RESET_SOFT 0x3C @@ -92,11 +91,6 @@ #define T_CONVERT_X_NS 500 /* xth conversion start time (oversampling) */ #define T_POWERUP_US 5000 /* Power up */ -/* - * AD738x support several SDO lines to increase throughput, but driver currently - * supports only 1 SDO line (standard SPI transaction) - */ -#define AD7380_NUM_SDO_LINES 1 #define AD7380_DEFAULT_GAIN_MILLI 1000 /* @@ -888,6 +882,8 @@ struct ad7380_state { bool resolution_boost_enabled; unsigned int ch; bool seq; + /* How many SDO lines are wired up. */ + u8 num_sdo_lines; unsigned int vref_mv; unsigned int vcm_mv[MAX_NUM_CHANNELS]; unsigned int gain_milli[MAX_NUM_CHANNELS]; @@ -1084,7 +1080,7 @@ static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch) if (oversampling_ratio > 1) xfer.delay.value = T_CONVERT_0_NS + T_CONVERT_X_NS * (oversampling_ratio - 1) * - st->chip_info->num_simult_channels / AD7380_NUM_SDO_LINES; + st->chip_info->num_simult_channels / st->num_sdo_lines; return spi_sync_transfer(st->spi, &xfer, 1); } @@ -1113,7 +1109,7 @@ static int ad7380_update_xfers(struct ad7380_state *st, if (oversampling_ratio > 1) t_convert = T_CONVERT_0_NS + T_CONVERT_X_NS * (oversampling_ratio - 1) * - st->chip_info->num_simult_channels / AD7380_NUM_SDO_LINES; + st->chip_info->num_simult_channels / st->num_sdo_lines; if (st->seq) { xfer[0].delay.value = xfer[1].delay.value = t_convert; @@ -1198,6 +1194,8 @@ static int ad7380_init_offload_msg(struct ad7380_state *st, xfer->bits_per_word = scan_type->realbits; xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; xfer->len = AD7380_SPI_BYTES(scan_type) * st->chip_info->num_simult_channels; + if (st->num_sdo_lines > 1) + xfer->multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE; spi_message_init_with_transfers(&st->offload_msg, xfer, 1); st->offload_msg.offload = st->offload; @@ -1793,6 +1791,7 @@ static const struct iio_info ad7380_info = { static int ad7380_init(struct ad7380_state *st, bool external_ref_en) { + u32 sdo; int ret; /* perform hard reset */ @@ -1815,11 +1814,24 @@ static int ad7380_init(struct ad7380_state *st, bool external_ref_en) st->ch = 0; st->seq = false; - /* SPI 1-wire mode */ + /* SDO field has an irregular mapping. */ + switch (st->num_sdo_lines) { + case 1: + sdo = 1; + break; + case 2: + sdo = 0; + break; + case 4: + sdo = 2; + break; + default: + return -EINVAL; + } + return regmap_update_bits(st->regmap, AD7380_REG_ADDR_CONFIG2, AD7380_CONFIG2_SDO, - FIELD_PREP(AD7380_CONFIG2_SDO, - AD7380_NUM_SDO_LINES)); + FIELD_PREP(AD7380_CONFIG2_SDO, sdo)); } static int ad7380_probe_spi_offload(struct iio_dev *indio_dev, @@ -1842,7 +1854,7 @@ static int ad7380_probe_spi_offload(struct iio_dev *indio_dev, "failed to get offload trigger\n"); sample_rate = st->chip_info->max_conversion_rate_hz * - AD7380_NUM_SDO_LINES / st->chip_info->num_simult_channels; + st->num_sdo_lines / st->chip_info->num_simult_channels; st->sample_freq_range[0] = 1; /* min */ st->sample_freq_range[1] = 1; /* step */ @@ -1887,6 +1899,13 @@ static int ad7380_probe(struct spi_device *spi) if (!st->chip_info) return dev_err_probe(dev, -EINVAL, "missing match data\n"); + st->num_sdo_lines = spi->num_rx_lanes; + + if (st->num_sdo_lines < 1 || st->num_sdo_lines > st->chip_info->num_simult_channels) + return dev_err_probe(dev, -EINVAL, + "invalid number of SDO lines (%d)\n", + st->num_sdo_lines); + ret = devm_regulator_bulk_get_enable(dev, st->chip_info->num_supplies, st->chip_info->supplies); @@ -2010,6 +2029,8 @@ static int ad7380_probe(struct spi_device *spi) st->normal_xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns; st->normal_xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; st->normal_xfer[1].rx_buf = st->scan_data; + if (st->num_sdo_lines > 1) + st->normal_xfer[1].multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE; spi_message_init_with_transfers(&st->normal_msg, st->normal_xfer, ARRAY_SIZE(st->normal_xfer)); @@ -2031,6 +2052,10 @@ static int ad7380_probe(struct spi_device *spi) st->seq_xfer[2].cs_change = 1; st->seq_xfer[2].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns; st->seq_xfer[2].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + if (st->num_sdo_lines > 1) { + st->seq_xfer[2].multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE; + st->seq_xfer[3].multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE; + } spi_message_init_with_transfers(&st->seq_msg, st->seq_xfer, ARRAY_SIZE(st->seq_xfer)); From f48bfc823c13eb2a8f0e32229d3ca32d849ff876 Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Tue, 17 Feb 2026 14:13:15 +0200 Subject: [PATCH 014/405] dt-bindings: iio: light: vcnl4000: add Capella CM36686 and CM36672P Capella CM36686 is an ambient light and proximity sensor developed by Capella Microsystems, now a subsidiary of Vishay Intertechnology Inc. It has an I2C address of 0x60 and is fully compatible with an existing driver for VCNL4040. Capella CM36672P is a proximity-only sensor that is partially compatible with CM36686 - they share the same register fields for proximity sensing, but ambient light sensor register fields in CM36672P are reserved. Add compatibles for cm36672p and cm36686, with a fallback for cm36686 of vcnl4040. Signed-off-by: Erikas Bitovtas Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- .../bindings/iio/light/vishay,vcnl4000.yaml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/light/vishay,vcnl4000.yaml b/Documentation/devicetree/bindings/iio/light/vishay,vcnl4000.yaml index 4d1a225e8868..2ba4d5de4ec4 100644 --- a/Documentation/devicetree/bindings/iio/light/vishay,vcnl4000.yaml +++ b/Documentation/devicetree/bindings/iio/light/vishay,vcnl4000.yaml @@ -18,12 +18,17 @@ allOf: properties: compatible: - enum: - - vishay,vcnl4000 - - vishay,vcnl4010 - - vishay,vcnl4020 - - vishay,vcnl4040 - - vishay,vcnl4200 + oneOf: + - enum: + - capella,cm36672p + - vishay,vcnl4000 + - vishay,vcnl4010 + - vishay,vcnl4020 + - vishay,vcnl4040 + - vishay,vcnl4200 + - items: + - const: capella,cm36686 + - const: vishay,vcnl4040 interrupts: maxItems: 1 From e3310a32e0573ade05ae09521b17c1463753e872 Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Tue, 17 Feb 2026 14:13:16 +0200 Subject: [PATCH 015/405] iio: light: vcnl4000: add support for Capella CM36686 and CM36672P Add support for Capella's CM36686 and CM36672P sensors. Capella CM36686 is an ambient light and proximity sensor that is fully compatible with VCNL4040 and can be used as is. CM36672P is partially compatible with VCNL4040 - it uses the same register fields for proximity sensing, but the ambient light registers are reserved. For CM36672P, we reuse vcnl4040_channels, but remove the IIO_LIGHT channel and ambient light integration time. Reviewed-by: Andy Shevchenko Signed-off-by: Erikas Bitovtas Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 41 ++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index a36c23813679..5e03c3d8874b 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -185,6 +185,7 @@ static const int vcnl4040_ps_oversampling_ratio[] = {1, 2, 4, 8}; #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ enum vcnl4000_device_ids { + CM36672P, VCNL4000, VCNL4010, VCNL4040, @@ -235,6 +236,8 @@ struct vcnl4000_chip_spec { }; static const struct i2c_device_id vcnl4000_id[] = { + { "cm36672p", CM36672P }, + { "cm36686", VCNL4040 }, { "vcnl4000", VCNL4000 }, { "vcnl4010", VCNL4010 }, { "vcnl4020", VCNL4010 }, @@ -1842,6 +1845,22 @@ static const struct iio_chan_spec vcnl4040_channels[] = { } }; +static const struct iio_chan_spec cm36672p_channels[] = { + { + .type = IIO_PROXIMITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .ext_info = vcnl4000_ext_info, + .event_spec = vcnl4040_event_spec, + .num_event_specs = ARRAY_SIZE(vcnl4040_event_spec), + }, +}; + static const struct iio_info vcnl4000_info = { .read_raw = vcnl4000_read_raw, }; @@ -1867,6 +1886,19 @@ static const struct iio_info vcnl4040_info = { }; static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { + [CM36672P] = { + .prod = "CM36672P", + .init = vcnl4200_init, + .measure_proximity = vcnl4200_measure_proximity, + .set_power_state = vcnl4200_set_power_state, + .channels = cm36672p_channels, + .num_channels = ARRAY_SIZE(cm36672p_channels), + .info = &vcnl4040_info, + .irq_thread = vcnl4040_irq_thread, + .int_reg = VCNL4040_INT_FLAGS, + .ps_it_times = &vcnl4040_ps_it_times, + .num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times), + }, [VCNL4000] = { .prod = "VCNL4000", .init = vcnl4000_init, @@ -2033,6 +2065,15 @@ static int vcnl4000_probe(struct i2c_client *client) } static const struct of_device_id vcnl_4000_of_match[] = { + { + .compatible = "capella,cm36672p", + .data = (void *)CM36672P, + }, + /* Capella CM36686 is fully compatible with Vishay VCNL4040 */ + { + .compatible = "capella,cm36686", + .data = (void *)VCNL4040, + }, { .compatible = "vishay,vcnl4000", .data = (void *)VCNL4000, From d1fa7b316b9fc8f11f8158b1beda9150165a377c Mon Sep 17 00:00:00 2001 From: Neel Bullywon Date: Sun, 15 Feb 2026 20:54:53 -0500 Subject: [PATCH 016/405] iio: magnetometer: bmc150_magn: replace msleep with fsleep Replace msleep(5) with fsleep(5 * USEC_PER_MSEC) to allow the kernel to select the most appropriate delay mechanism based on duration. Using USEC_PER_MSEC makes the unit conversion explicit. Signed-off-by: Neel Bullywon Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/bmc150_magn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index a022e1805dff..4a0582d624d4 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -695,7 +695,7 @@ static int bmc150_magn_init(struct bmc150_magn_data *data) * 3ms power-on time according to datasheet, let's better * be safe than sorry and set this delay to 5ms. */ - msleep(5); + fsleep(5 * USEC_PER_MSEC); ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, false); From 094d5d37d385ed99461883da013995ac2586dae0 Mon Sep 17 00:00:00 2001 From: Neel Bullywon Date: Sun, 15 Feb 2026 20:54:54 -0500 Subject: [PATCH 017/405] iio: magnetometer: bmc150_magn: minor formatting cleanup Improve initializer list style for bmc150_magn_samp_freq_table by moving the opening brace to its own line and using one entry per line with proper indentation and spaces inside braces. Add spaces inside braces for initializer lists in the preset table for consistency. Fix indentation of bmc150_magn_scan_masks array. No functional changes. Signed-off-by: Neel Bullywon Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/bmc150_magn.c | 31 ++++++++++++++------------ 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index 4a0582d624d4..04c4619dfc24 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -148,14 +148,16 @@ struct bmc150_magn_data { static const struct { int freq; u8 reg_val; -} bmc150_magn_samp_freq_table[] = { {2, 0x01}, - {6, 0x02}, - {8, 0x03}, - {10, 0x00}, - {15, 0x04}, - {20, 0x05}, - {25, 0x06}, - {30, 0x07} }; +} bmc150_magn_samp_freq_table[] = { + { 2, 0x01 }, + { 6, 0x02 }, + { 8, 0x03 }, + { 10, 0x00 }, + { 15, 0x04 }, + { 20, 0x05 }, + { 25, 0x06 }, + { 30, 0x07 }, +}; enum bmc150_magn_presets { LOW_POWER_PRESET, @@ -169,10 +171,10 @@ static const struct bmc150_magn_preset { u8 rep_z; u8 odr; } bmc150_magn_presets_table[] = { - [LOW_POWER_PRESET] = {3, 3, 10}, - [REGULAR_PRESET] = {9, 15, 10}, - [ENHANCED_REGULAR_PRESET] = {15, 27, 10}, - [HIGH_ACCURACY_PRESET] = {47, 83, 20}, + [LOW_POWER_PRESET] = { 3, 3, 10 }, + [REGULAR_PRESET] = { 9, 15, 10 }, + [ENHANCED_REGULAR_PRESET] = { 15, 27, 10 }, + [HIGH_ACCURACY_PRESET] = { 47, 83, 20 }, }; #define BMC150_MAGN_DEFAULT_PRESET REGULAR_PRESET @@ -655,8 +657,9 @@ static const struct iio_info bmc150_magn_info = { }; static const unsigned long bmc150_magn_scan_masks[] = { - BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z), - 0}; + BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z), + 0 +}; static irqreturn_t bmc150_magn_trigger_handler(int irq, void *p) { From d6ae9f202f616527f0d9a3a3fe980d36ec332707 Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Mon, 16 Feb 2026 02:24:48 -0800 Subject: [PATCH 018/405] iio: sca3000: simplify with spi_get_device_match_data() Refactor each sca3000 variant with it's own chip_info struct, update the sca3000_probe() to use spi_get_device_match_data(). Suggested-by: David Lechner Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Harshit Mogalapalli Signed-off-by: Jonathan Cameron --- drivers/iio/accel/sca3000.c | 127 +++++++++++++++++------------------- 1 file changed, 59 insertions(+), 68 deletions(-) diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 4a827be439a2..096f9726ae1f 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -171,6 +171,7 @@ struct sca3000_state { /** * struct sca3000_chip_info - model dependent parameters + * @name: name of the chip * @scale: scale * 10^-6 * @temp_output: some devices have temperature sensors. * @measurement_mode_freq: normal mode sampling frequency @@ -193,6 +194,7 @@ struct sca3000_state { * sca3000 variant. **/ struct sca3000_chip_info { + const char *name; unsigned int scale; bool temp_output; int measurement_mode_freq; @@ -207,69 +209,59 @@ struct sca3000_chip_info { int mot_det_mult_y[7]; }; -enum sca3000_variant { - d01, - e02, - e04, - e05, +static const struct sca3000_chip_info sca3000_chip_info_d01 = { + .name = "sca3000_d01", + .scale = 7357, + .temp_output = true, + .measurement_mode_freq = 250, + .measurement_mode_3db_freq = 45, + .option_mode_1 = SCA3000_OP_MODE_BYPASS, + .option_mode_1_freq = 250, + .option_mode_1_3db_freq = 70, + .mot_det_mult_xz = { 50, 100, 200, 350, 650, 1300 }, + .mot_det_mult_y = { 50, 100, 150, 250, 450, 850, 1750 }, }; -/* - * Note where option modes are not defined, the chip simply does not - * support any. - * Other chips in the sca3000 series use i2c and are not included here. - * - * Some of these devices are only listed in the family data sheet and - * do not actually appear to be available. - */ -static const struct sca3000_chip_info sca3000_spi_chip_info_tbl[] = { - [d01] = { - .scale = 7357, - .temp_output = true, - .measurement_mode_freq = 250, - .measurement_mode_3db_freq = 45, - .option_mode_1 = SCA3000_OP_MODE_BYPASS, - .option_mode_1_freq = 250, - .option_mode_1_3db_freq = 70, - .mot_det_mult_xz = {50, 100, 200, 350, 650, 1300}, - .mot_det_mult_y = {50, 100, 150, 250, 450, 850, 1750}, - }, - [e02] = { - .scale = 9810, - .measurement_mode_freq = 125, - .measurement_mode_3db_freq = 40, - .option_mode_1 = SCA3000_OP_MODE_NARROW, - .option_mode_1_freq = 63, - .option_mode_1_3db_freq = 11, - .mot_det_mult_xz = {100, 150, 300, 550, 1050, 2050}, - .mot_det_mult_y = {50, 100, 200, 350, 700, 1350, 2700}, - }, - [e04] = { - .scale = 19620, - .measurement_mode_freq = 100, - .measurement_mode_3db_freq = 38, - .option_mode_1 = SCA3000_OP_MODE_NARROW, - .option_mode_1_freq = 50, - .option_mode_1_3db_freq = 9, - .option_mode_2 = SCA3000_OP_MODE_WIDE, - .option_mode_2_freq = 400, - .option_mode_2_3db_freq = 70, - .mot_det_mult_xz = {200, 300, 600, 1100, 2100, 4100}, - .mot_det_mult_y = {100, 200, 400, 7000, 1400, 2700, 54000}, - }, - [e05] = { - .scale = 61313, - .measurement_mode_freq = 200, - .measurement_mode_3db_freq = 60, - .option_mode_1 = SCA3000_OP_MODE_NARROW, - .option_mode_1_freq = 50, - .option_mode_1_3db_freq = 9, - .option_mode_2 = SCA3000_OP_MODE_WIDE, - .option_mode_2_freq = 400, - .option_mode_2_3db_freq = 75, - .mot_det_mult_xz = {600, 900, 1700, 3200, 6100, 11900}, - .mot_det_mult_y = {300, 600, 1200, 2000, 4100, 7800, 15600}, - }, +static const struct sca3000_chip_info sca3000_chip_info_e02 = { + .name = "sca3000_e02", + .scale = 9810, + .measurement_mode_freq = 125, + .measurement_mode_3db_freq = 40, + .option_mode_1 = SCA3000_OP_MODE_NARROW, + .option_mode_1_freq = 63, + .option_mode_1_3db_freq = 11, + .mot_det_mult_xz = { 100, 150, 300, 550, 1050, 2050 }, + .mot_det_mult_y = { 50, 100, 200, 350, 700, 1350, 2700 }, +}; + +static const struct sca3000_chip_info sca3000_chip_info_e04 = { + .name = "sca3000_e04", + .scale = 19620, + .measurement_mode_freq = 100, + .measurement_mode_3db_freq = 38, + .option_mode_1 = SCA3000_OP_MODE_NARROW, + .option_mode_1_freq = 50, + .option_mode_1_3db_freq = 9, + .option_mode_2 = SCA3000_OP_MODE_WIDE, + .option_mode_2_freq = 400, + .option_mode_2_3db_freq = 70, + .mot_det_mult_xz = { 200, 300, 600, 1100, 2100, 4100 }, + .mot_det_mult_y = { 100, 200, 400, 7000, 1400, 2700, 54000 }, +}; + +static const struct sca3000_chip_info sca3000_chip_info_e05 = { + .name = "sca3000_e05", + .scale = 61313, + .measurement_mode_freq = 200, + .measurement_mode_3db_freq = 60, + .option_mode_1 = SCA3000_OP_MODE_NARROW, + .option_mode_1_freq = 50, + .option_mode_1_3db_freq = 9, + .option_mode_2 = SCA3000_OP_MODE_WIDE, + .option_mode_2_freq = 400, + .option_mode_2_3db_freq = 75, + .mot_det_mult_xz = { 600, 900, 1700, 3200, 6100, 11900 }, + .mot_det_mult_y = { 300, 600, 1200, 2000, 4100, 7800, 15600 }, }; static int sca3000_write_reg(struct sca3000_state *st, u8 address, u8 val) @@ -1449,10 +1441,9 @@ static int sca3000_probe(struct spi_device *spi) spi_set_drvdata(spi, indio_dev); st->us = spi; mutex_init(&st->lock); - st->info = &sca3000_spi_chip_info_tbl[spi_get_device_id(spi) - ->driver_data]; + st->info = spi_get_device_match_data(spi); - indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->name = st->info->name; indio_dev->info = &sca3000_info; if (st->info->temp_output) { indio_dev->channels = sca3000_channels_with_temp; @@ -1532,10 +1523,10 @@ static void sca3000_remove(struct spi_device *spi) } static const struct spi_device_id sca3000_id[] = { - {"sca3000_d01", d01}, - {"sca3000_e02", e02}, - {"sca3000_e04", e04}, - {"sca3000_e05", e05}, + { "sca3000_d01", (kernel_ulong_t)&sca3000_chip_info_d01 }, + { "sca3000_e02", (kernel_ulong_t)&sca3000_chip_info_e02 }, + { "sca3000_e04", (kernel_ulong_t)&sca3000_chip_info_e04 }, + { "sca3000_e05", (kernel_ulong_t)&sca3000_chip_info_e05 }, { } }; MODULE_DEVICE_TABLE(spi, sca3000_id); From 0309e66abea141f3ec339b064b8f8b8003d2a634 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 17 Feb 2026 09:05:14 +0100 Subject: [PATCH 019/405] iio: adc: ad7192: Revert "properly check spi_get_device_match_data()" This reverts commit b7f99fa1b64af2f696b13cec581cb4cd7d3982b8. The added code is currently a dead code. Moreover, the driver is not designed to have any defaults effectively making driver data a mandatory information to work with. Taking all together, revert unneeded change. Discussion at #1 concluded in agreeing a new policy on this for IIO. Link: https://lore.kernel.org/linux-iio/20260217080514.1288115-1-andriy.shevchenko@linux.intel.com/ #1 Reported-by: Harshit Mogalapalli Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7192.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index 530e1d307860..8b1664f6b102 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -1402,9 +1402,6 @@ static int ad7192_probe(struct spi_device *spi) st->int_vref_mv = ret == -ENODEV ? avdd_mv : ret / MILLI; st->chip_info = spi_get_device_match_data(spi); - if (!st->chip_info) - return -ENODEV; - indio_dev->name = st->chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = st->chip_info->info; From 93f60f67215e09d281fb0b22d29cb33ff033db61 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 16 Feb 2026 15:17:04 +0200 Subject: [PATCH 020/405] iio: magnetometer: si7210: simplify probe with devm_regulator_get_enable_read_voltage() Simplify probe by using devm_regulator_get_enable_read_voltage() to get, enable and read the regulator voltage in a single call, caching the value at probe time. This is a functional change as VDD voltage is now read once at probe rather than dynamically on each temperature measurement. However, in real deployments it is very rare for VDD to change after initial probe. Reviewed-by: Andy Shevchenko Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/si7210.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/iio/magnetometer/si7210.c b/drivers/iio/magnetometer/si7210.c index 27e3feba7a0f..2a36abd1c99d 100644 --- a/drivers/iio/magnetometer/si7210.c +++ b/drivers/iio/magnetometer/si7210.c @@ -128,8 +128,8 @@ static const struct regmap_config si7210_regmap_conf = { struct si7210_data { struct regmap *regmap; struct i2c_client *client; - struct regulator *vdd; struct mutex fetch_lock; /* lock for a single measurement fetch */ + unsigned int vdd_uV; s8 temp_offset; s8 temp_gain; s8 scale_20_a[A_REGS_COUNT]; @@ -221,12 +221,8 @@ static int si7210_read_raw(struct iio_dev *indio_dev, temp *= (1 + (data->temp_gain / 2048)); temp += (int)(MICRO / 16) * data->temp_offset; - ret = regulator_get_voltage(data->vdd); - if (ret < 0) - return ret; - /* temp -= 0.222 * VDD */ - temp -= 222 * div_s64(ret, MILLI); + temp -= 222 * (data->vdd_uV / MILLI); *val = div_s64(temp, MILLI); @@ -396,14 +392,11 @@ static int si7210_probe(struct i2c_client *client) return dev_err_probe(&client->dev, PTR_ERR(data->regmap), "failed to register regmap\n"); - data->vdd = devm_regulator_get(&client->dev, "vdd"); - if (IS_ERR(data->vdd)) - return dev_err_probe(&client->dev, PTR_ERR(data->vdd), - "failed to get VDD regulator\n"); - - ret = regulator_enable(data->vdd); - if (ret) - return ret; + ret = devm_regulator_get_enable_read_voltage(&client->dev, "vdd"); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to get vdd regulator\n"); + data->vdd_uV = ret; indio_dev->name = dev_name(&client->dev); indio_dev->modes = INDIO_DIRECT_MODE; From 65d887ff21057dede1684c176e910991700a8bd0 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 16 Feb 2026 15:17:05 +0200 Subject: [PATCH 021/405] iio: dac: max5522: simplify probe with devm_regulator_get_enable_read_voltage() Simplify probe by using devm_regulator_get_enable_read_voltage() to get, enable and read the regulator voltage in a single call, caching the value at probe time. The reference voltage for this DAC is a fixed hardware configuration that is not expected to change at runtime, so reading it once during probe and caching the millivolt value is sufficient. Reviewed-by: Andy Shevchenko Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/dac/max5522.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/iio/dac/max5522.c b/drivers/iio/dac/max5522.c index 1b8fe6b8d26e..b52a9cc1da79 100644 --- a/drivers/iio/dac/max5522.c +++ b/drivers/iio/dac/max5522.c @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -34,7 +35,7 @@ struct max5522_state { struct regmap *regmap; const struct max5522_chip_info *chip_info; unsigned short dac_cache[2]; - struct regulator *vrefin_reg; + int vref_mV; }; #define MAX5522_CHANNEL(chan) { \ @@ -79,17 +80,13 @@ static int max5522_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long info) { struct max5522_state *state = iio_priv(indio_dev); - int ret; switch (info) { case IIO_CHAN_INFO_RAW: *val = state->dac_cache[chan->channel]; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - ret = regulator_get_voltage(state->vrefin_reg); - if (ret < 0) - return -EINVAL; - *val = ret / 1000; + *val = state->vref_mV; *val2 = 10; return IIO_VAL_FRACTIONAL_LOG2; default: @@ -147,16 +144,11 @@ static int max5522_spi_probe(struct spi_device *spi) if (!state->chip_info) return -EINVAL; - state->vrefin_reg = devm_regulator_get(&spi->dev, "vrefin"); - if (IS_ERR(state->vrefin_reg)) - return dev_err_probe(&spi->dev, PTR_ERR(state->vrefin_reg), - "Vrefin regulator not specified\n"); - - ret = regulator_enable(state->vrefin_reg); - if (ret) { + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vrefin"); + if (ret < 0) return dev_err_probe(&spi->dev, ret, - "Failed to enable vref regulators\n"); - } + "Failed to get vrefin regulator\n"); + state->vref_mV = ret / (MICRO / MILLI); state->regmap = devm_regmap_init_spi(spi, &max5522_regmap_config); From 34773346827bcaa685be325946fd7bdf6f32bda2 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Mon, 16 Feb 2026 11:59:10 -0300 Subject: [PATCH 022/405] dt-bindings: iio: adc: adi,ad4030: Reference spi-peripheral-props AD4030 and similar devices all connect to the system as SPI peripherals. Reference spi-peripheral-props so common SPI peripheral can be used from ad4030 dt-binding. Acked-by: Conor Dooley Signed-off-by: Marcelo Schmitt Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml index e22d518135f2..29e266865805 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml @@ -20,6 +20,8 @@ description: | * https://www.analog.com/media/en/technical-documentation/data-sheets/ad4630-24_ad4632-24.pdf * https://www.analog.com/media/en/technical-documentation/data-sheets/ad4630-16-4632-16.pdf +$ref: /schemas/spi/spi-peripheral-props.yaml# + properties: compatible: enum: From a10f6dd4eef2e64fe1f366613bed45aa308494de Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Mon, 16 Feb 2026 12:00:22 -0300 Subject: [PATCH 023/405] iio: adc: ad4030: Use BIT macro to improve code readability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use BIT macro to make the list of average modes more readable. Suggested-by: Andy Shevchenko Reviewed-by: Nuno Sá Acked-by: Andy Shevchenko Signed-off-by: Marcelo Schmitt Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4030.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c index 68446db9bef1..def3e1d01ceb 100644 --- a/drivers/iio/adc/ad4030.c +++ b/drivers/iio/adc/ad4030.c @@ -232,10 +232,16 @@ struct ad4030_state { .num_ext_scan_type = ARRAY_SIZE(_scan_type), \ } +/* + * AD4030 can average over 2^N samples, where N = 1, 2, 3, ..., 16. + * We use N = 0 to mean no sample averaging. + */ static const int ad4030_average_modes[] = { - 1, 2, 4, 8, 16, 32, 64, 128, - 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, - 65536, + BIT(0), /* No sampling average */ + BIT(1), BIT(2), BIT(3), BIT(4), + BIT(5), BIT(6), BIT(7), BIT(8), + BIT(9), BIT(10), BIT(11), BIT(12), + BIT(13), BIT(14), BIT(15), BIT(16), }; static int ad4030_enter_config_mode(struct ad4030_state *st) From 6cfb965afed9b63b4614897ef9aac92ce1419c29 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 16 Feb 2026 17:11:10 +0200 Subject: [PATCH 024/405] dt-bindings: iio: adc: adi,ad4080: add support for AD4082, AD4085 and AD4088 Add device tree binding support for AD4082, AD4085 and AD4088 SAR ADCs. Add adi,ad4082, adi,ad4085 and adi,ad4088 to the compatible enum. A fallback compatible string is not appropriate for these devices as they differ in LVDS CNV clock count maximum from their base variants: - AD4082 (20-bit) vs AD4080: lvds_cnv_clk_cnt_max 8 vs 7 - AD4085 (16-bit) vs AD4084: lvds_cnv_clk_cnt_max 8 vs 2 - AD4088 (14-bit) vs AD4087: lvds_cnv_clk_cnt_max 8 vs 1 Acked-by: Conor Dooley Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml index ccd6a0ac1539..79df2696ef24 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml @@ -27,10 +27,13 @@ properties: enum: - adi,ad4080 - adi,ad4081 + - adi,ad4082 - adi,ad4083 - adi,ad4084 + - adi,ad4085 - adi,ad4086 - adi,ad4087 + - adi,ad4088 reg: maxItems: 1 From 419a96f82474e7b1657c6086ef692379315bffc5 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 16 Feb 2026 17:11:11 +0200 Subject: [PATCH 025/405] iio: adc: ad4080: add support for AD4082, AD4085 and AD4088 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the AD4082, AD4085 and AD4088 SAR ADCs. These devices share the same resolution as their base variants but differ in LVDS CNV clock count maximum: - AD4082 (20-bit, like AD4080): lvds_cnv_clk_cnt_max 8 vs 7 - AD4085 (16-bit, like AD4084): lvds_cnv_clk_cnt_max 8 vs 2 - AD4088 (14-bit, like AD4087): lvds_cnv_clk_cnt_max 8 vs 1 Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4080.c | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c index 7cf3b6ed7940..fc261d3d7687 100644 --- a/drivers/iio/adc/ad4080.c +++ b/drivers/iio/adc/ad4080.c @@ -127,10 +127,13 @@ #define AD4080_SPI_READ BIT(7) #define AD4080_CHIP_ID 0x0050 #define AD4081_CHIP_ID 0x0051 +#define AD4082_CHIP_ID 0x0052 #define AD4083_CHIP_ID 0x0053 #define AD4084_CHIP_ID 0x0054 +#define AD4085_CHIP_ID 0x0055 #define AD4086_CHIP_ID 0x0056 #define AD4087_CHIP_ID 0x0057 +#define AD4088_CHIP_ID 0x0058 #define AD4080_LVDS_CNV_CLK_CNT_MAX 7 @@ -442,14 +445,20 @@ static const struct iio_chan_spec ad4080_channel = AD4080_CHANNEL_DEFINE(20, 32) static const struct iio_chan_spec ad4081_channel = AD4080_CHANNEL_DEFINE(20, 32); +static const struct iio_chan_spec ad4082_channel = AD4080_CHANNEL_DEFINE(20, 32); + static const struct iio_chan_spec ad4083_channel = AD4080_CHANNEL_DEFINE(16, 16); static const struct iio_chan_spec ad4084_channel = AD4080_CHANNEL_DEFINE(16, 16); +static const struct iio_chan_spec ad4085_channel = AD4080_CHANNEL_DEFINE(16, 16); + static const struct iio_chan_spec ad4086_channel = AD4080_CHANNEL_DEFINE(14, 16); static const struct iio_chan_spec ad4087_channel = AD4080_CHANNEL_DEFINE(14, 16); +static const struct iio_chan_spec ad4088_channel = AD4080_CHANNEL_DEFINE(14, 16); + static const struct ad4080_chip_info ad4080_chip_info = { .name = "ad4080", .product_id = AD4080_CHIP_ID, @@ -470,6 +479,16 @@ static const struct ad4080_chip_info ad4081_chip_info = { .lvds_cnv_clk_cnt_max = 2, }; +static const struct ad4080_chip_info ad4082_chip_info = { + .name = "ad4082", + .product_id = AD4082_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 1, + .channels = &ad4082_channel, + .lvds_cnv_clk_cnt_max = 8, +}; + static const struct ad4080_chip_info ad4083_chip_info = { .name = "ad4083", .product_id = AD4083_CHIP_ID, @@ -490,6 +509,16 @@ static const struct ad4080_chip_info ad4084_chip_info = { .lvds_cnv_clk_cnt_max = 2, }; +static const struct ad4080_chip_info ad4085_chip_info = { + .name = "ad4085", + .product_id = AD4085_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 1, + .channels = &ad4085_channel, + .lvds_cnv_clk_cnt_max = 8, +}; + static const struct ad4080_chip_info ad4086_chip_info = { .name = "ad4086", .product_id = AD4086_CHIP_ID, @@ -510,6 +539,16 @@ static const struct ad4080_chip_info ad4087_chip_info = { .lvds_cnv_clk_cnt_max = 1, }; +static const struct ad4080_chip_info ad4088_chip_info = { + .name = "ad4088", + .product_id = AD4088_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 1, + .channels = &ad4088_channel, + .lvds_cnv_clk_cnt_max = 8, +}; + static int ad4080_setup(struct iio_dev *indio_dev) { struct ad4080_state *st = iio_priv(indio_dev); @@ -666,10 +705,13 @@ static int ad4080_probe(struct spi_device *spi) static const struct spi_device_id ad4080_id[] = { { "ad4080", (kernel_ulong_t)&ad4080_chip_info }, { "ad4081", (kernel_ulong_t)&ad4081_chip_info }, + { "ad4082", (kernel_ulong_t)&ad4082_chip_info }, { "ad4083", (kernel_ulong_t)&ad4083_chip_info }, { "ad4084", (kernel_ulong_t)&ad4084_chip_info }, + { "ad4085", (kernel_ulong_t)&ad4085_chip_info }, { "ad4086", (kernel_ulong_t)&ad4086_chip_info }, { "ad4087", (kernel_ulong_t)&ad4087_chip_info }, + { "ad4088", (kernel_ulong_t)&ad4088_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4080_id); @@ -677,10 +719,13 @@ MODULE_DEVICE_TABLE(spi, ad4080_id); static const struct of_device_id ad4080_of_match[] = { { .compatible = "adi,ad4080", &ad4080_chip_info }, { .compatible = "adi,ad4081", &ad4081_chip_info }, + { .compatible = "adi,ad4082", &ad4082_chip_info }, { .compatible = "adi,ad4083", &ad4083_chip_info }, { .compatible = "adi,ad4084", &ad4084_chip_info }, + { .compatible = "adi,ad4085", &ad4085_chip_info }, { .compatible = "adi,ad4086", &ad4086_chip_info }, { .compatible = "adi,ad4087", &ad4087_chip_info }, + { .compatible = "adi,ad4088", &ad4088_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad4080_of_match); From 1261aea929a95097d05c9fc4fa9f9419720c64a8 Mon Sep 17 00:00:00 2001 From: Pranav Kharche Date: Wed, 18 Feb 2026 09:25:49 +0530 Subject: [PATCH 026/405] dt-bindings: iio: dac: Fix typo in ti,dac7612.yaml Fix a typo in the description where "Is is" should be "It is". Signed-off-by: Pranav Kharche Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/dac/ti,dac7612.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/dac/ti,dac7612.yaml b/Documentation/devicetree/bindings/iio/dac/ti,dac7612.yaml index 20dd1370660d..624c640be4c8 100644 --- a/Documentation/devicetree/bindings/iio/dac/ti,dac7612.yaml +++ b/Documentation/devicetree/bindings/iio/dac/ti,dac7612.yaml @@ -9,7 +9,7 @@ title: Texas Instruments DAC7612 family of DACs description: The DAC7612 is a dual, 12-bit digital-to-analog converter (DAC) with guaranteed 12-bit monotonicity performance over the industrial temperature - range. Is is programmable through an SPI interface. + range. It is programmable through an SPI interface. maintainers: - Ricardo Ribalda Delgado From f808d21ef7d5a93f14c3465a3268c46474cf04b8 Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Tue, 17 Feb 2026 11:31:56 -0300 Subject: [PATCH 027/405] iio: light: zopt2201: Reorder header includes Reorder the header includes to follow the usual kernel ordering conventions and improve readability. Signed-off-by: Gabriel Almeida Signed-off-by: Jonathan Cameron --- drivers/iio/light/zopt2201.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/iio/light/zopt2201.c b/drivers/iio/light/zopt2201.c index 1dba1b949cc3..b7dd20fc20d7 100644 --- a/drivers/iio/light/zopt2201.c +++ b/drivers/iio/light/zopt2201.c @@ -10,17 +10,16 @@ * TODO: interrupt support, ALS/UVB raw mode */ -#include -#include -#include -#include #include +#include +#include +#include +#include +#include #include #include -#include - #define ZOPT2201_DRV_NAME "zopt2201" /* Registers */ From 08eea726f4a30dc7e3ee4bf76a1497c54948ff51 Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Tue, 17 Feb 2026 11:31:57 -0300 Subject: [PATCH 028/405] iio: light: zopt2201: use lock guards Use guard() and scoped_guard() to handle the mutex lock instead of manually locking and unlocking it. Signed-off-by: Gabriel Almeida Signed-off-by: Jonathan Cameron --- drivers/iio/light/zopt2201.c | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/drivers/iio/light/zopt2201.c b/drivers/iio/light/zopt2201.c index b7dd20fc20d7..0990e4d266eb 100644 --- a/drivers/iio/light/zopt2201.c +++ b/drivers/iio/light/zopt2201.c @@ -10,6 +10,7 @@ * TODO: interrupt support, ALS/UVB raw mode */ +#include #include #include #include @@ -185,10 +186,10 @@ static int zopt2201_read(struct zopt2201_data *data, u8 reg) u8 buf[3]; int ret; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); ret = zopt2201_enable_mode(data, reg == ZOPT2201_UVB_DATA); if (ret < 0) - goto fail; + return ret; while (tries--) { unsigned long t = zopt2201_resolution[data->res].us; @@ -199,30 +200,25 @@ static int zopt2201_read(struct zopt2201_data *data, u8 reg) msleep(t / 1000); ret = i2c_smbus_read_byte_data(client, ZOPT2201_MAIN_STATUS); if (ret < 0) - goto fail; + return ret; if (ret & ZOPT2201_MAIN_STATUS_DRDY) break; } if (tries < 0) { ret = -ETIMEDOUT; - goto fail; + return ret; } ret = i2c_smbus_read_i2c_block_data(client, reg, sizeof(buf), buf); if (ret < 0) - goto fail; + return ret; ret = i2c_smbus_write_byte_data(client, ZOPT2201_MAIN_CTRL, 0x00); if (ret < 0) - goto fail; - mutex_unlock(&data->lock); + return ret; return get_unaligned_le24(&buf[0]); - -fail: - mutex_unlock(&data->lock); - return ret; } static const struct iio_chan_spec zopt2201_channels[] = { @@ -316,17 +312,15 @@ static int zopt2201_set_resolution(struct zopt2201_data *data, u8 res) static int zopt2201_write_resolution(struct zopt2201_data *data, int val, int val2) { - int i, ret; + int i; if (val != 0) return -EINVAL; for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++) if (val2 == zopt2201_resolution[i].us) { - mutex_lock(&data->lock); - ret = zopt2201_set_resolution(data, i); - mutex_unlock(&data->lock); - return ret; + guard(mutex)(&data->lock); + return zopt2201_set_resolution(data, i); } return -EINVAL; @@ -350,16 +344,12 @@ static int zopt2201_write_scale_by_idx(struct zopt2201_data *data, int idx, { int ret; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); ret = zopt2201_set_resolution(data, zopt2201_scale_array[idx].res); if (ret < 0) - goto unlock; + return ret; - ret = zopt2201_set_gain(data, zopt2201_scale_array[idx].gain); - -unlock: - mutex_unlock(&data->lock); - return ret; + return zopt2201_set_gain(data, zopt2201_scale_array[idx].gain); } static int zopt2201_write_scale_als(struct zopt2201_data *data, From 9f3a352e9ff0c2ba89cc81555b2de0a4a5ed4221 Mon Sep 17 00:00:00 2001 From: Jishnu Prakash Date: Fri, 30 Jan 2026 17:24:18 +0530 Subject: [PATCH 029/405] dt-bindings: iio: adc: Split out QCOM VADC channel properties Split out the common channel properties for QCOM VADC devices into a separate file so that it can be included as a reference for devices using them. This will be needed for the upcoming ADC5 Gen3 binding support patch, as ADC5 Gen3 also uses all of these common properties. Reviewed-by: Krzysztof Kozlowski Acked-by: Jonathan Cameron Signed-off-by: Jishnu Prakash Signed-off-by: Jonathan Cameron --- .../iio/adc/qcom,spmi-vadc-common.yaml | 84 +++++++++++++++++++ .../bindings/iio/adc/qcom,spmi-vadc.yaml | 76 +---------------- 2 files changed, 86 insertions(+), 74 deletions(-) create mode 100644 Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc-common.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc-common.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc-common.yaml new file mode 100644 index 000000000000..3ae252c17b91 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc-common.yaml @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/qcom,spmi-vadc-common.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies, Inc. SPMI PMIC ADC channels + +maintainers: + - Jishnu Prakash + +description: + This defines the common properties used to define Qualcomm VADC channels. + +properties: + reg: + description: + ADC channel number (PMIC-specific for versions after PMIC5 ADC). + maxItems: 1 + + label: + description: + ADC input of the platform as seen in the schematics. + For thermistor inputs connected to generic AMUX or GPIO inputs + these can vary across platform for the same pins. Hence select + the platform schematics name for this channel. + + qcom,decimation: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + This parameter is used to decrease ADC sampling rate. + Quicker measurements can be made by reducing decimation ratio. + + qcom,pre-scaling: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + Used for scaling the channel input signal before the signal is + fed to VADC. The configuration for this node is to know the + pre-determined ratio and use it for post scaling. It is a pair of + integers, denoting the numerator and denominator of the fraction by which + input signal is multiplied. For example, <1 3> indicates the signal is scaled + down to 1/3 of its value before ADC measurement. + If property is not found default value depending on chip will be used. + oneOf: + - items: + - const: 1 + - enum: [ 1, 3, 4, 6, 20, 8, 10, 16 ] + - items: + - const: 10 + - const: 81 + + qcom,ratiometric: + type: boolean + description: | + Channel calibration type. + - For compatible property "qcom,spmi-vadc", if this property is + specified VADC will use the VDD reference (1.8V) and GND for + channel calibration. If property is not found, channel will be + calibrated with 0.625V and 1.25V reference channels, also + known as absolute calibration. + - For other compatible properties, if this property is specified + VADC will use the VDD reference (1.875V) and GND for channel + calibration. If property is not found, channel will be calibrated + with 0V and 1.25V reference channels, also known as absolute calibration. + + qcom,hw-settle-time: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Time between AMUX getting configured and the ADC starting + conversion. The 'hw_settle_time' is an index used from valid values + and programmed in hardware to achieve the hardware settling delay. + + qcom,avg-samples: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Number of samples to be used for measurement. + Averaging provides the option to obtain a single measurement + from the ADC that is an average of multiple samples. The value + selected is 2^(value). + +required: + - reg + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml index b9dc04b0d307..16c80709a3ee 100644 --- a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml @@ -56,7 +56,7 @@ required: patternProperties: "^channel@[0-9a-f]+$": type: object - additionalProperties: false + unevaluatedProperties: false description: | Represents the external channels which are connected to the ADC. For compatible property "qcom,spmi-vadc" following channels, also known as @@ -64,79 +64,7 @@ patternProperties: configuration nodes should be defined: VADC_REF_625MV and/or VADC_SPARE1(based on PMIC version) VADC_REF_1250MV, VADC_GND_REF and VADC_VDD_VADC. - - properties: - reg: - maxItems: 1 - description: | - ADC channel number. - See include/dt-bindings/iio/qcom,spmi-vadc.h - For PMIC7 ADC, the channel numbers are specified separately per PMIC - in the PMIC-specific files in include/dt-bindings/iio/. - - label: - description: | - ADC input of the platform as seen in the schematics. - For thermistor inputs connected to generic AMUX or GPIO inputs - these can vary across platform for the same pins. Hence select - the platform schematics name for this channel. - - qcom,decimation: - $ref: /schemas/types.yaml#/definitions/uint32 - description: | - This parameter is used to decrease ADC sampling rate. - Quicker measurements can be made by reducing decimation ratio. - - qcom,pre-scaling: - description: | - Used for scaling the channel input signal before the signal is - fed to VADC. The configuration for this node is to know the - pre-determined ratio and use it for post scaling. It is a pair of - integers, denoting the numerator and denominator of the fraction by which - input signal is multiplied. For example, <1 3> indicates the signal is scaled - down to 1/3 of its value before ADC measurement. - If property is not found default value depending on chip will be used. - $ref: /schemas/types.yaml#/definitions/uint32-array - oneOf: - - items: - - const: 1 - - enum: [ 1, 3, 4, 6, 20, 8, 10, 16 ] - - items: - - const: 10 - - const: 81 - - qcom,ratiometric: - description: | - Channel calibration type. - - For compatible property "qcom,spmi-vadc", if this property is - specified VADC will use the VDD reference (1.8V) and GND for - channel calibration. If property is not found, channel will be - calibrated with 0.625V and 1.25V reference channels, also - known as absolute calibration. - - For compatible property "qcom,spmi-adc5", "qcom,spmi-adc7" and - "qcom,spmi-adc-rev2", if this property is specified VADC will use - the VDD reference (1.875V) and GND for channel calibration. If - property is not found, channel will be calibrated with 0V and 1.25V - reference channels, also known as absolute calibration. - type: boolean - - qcom,hw-settle-time: - $ref: /schemas/types.yaml#/definitions/uint32 - description: | - Time between AMUX getting configured and the ADC starting - conversion. The 'hw_settle_time' is an index used from valid values - and programmed in hardware to achieve the hardware settling delay. - - qcom,avg-samples: - $ref: /schemas/types.yaml#/definitions/uint32 - description: | - Number of samples to be used for measurement. - Averaging provides the option to obtain a single measurement - from the ADC that is an average of multiple samples. The value - selected is 2^(value). - - required: - - reg + $ref: /schemas/iio/adc/qcom,spmi-vadc-common.yaml allOf: - if: From 1c1b853eefcdbf4670020a917e25a6ac3548069d Mon Sep 17 00:00:00 2001 From: Jishnu Prakash Date: Fri, 30 Jan 2026 17:24:19 +0530 Subject: [PATCH 030/405] dt-bindings: iio: adc: Add support for QCOM PMIC5 Gen3 ADC For the PMIC5-Gen3 type PMICs, ADC peripheral is present in HW for the following PMICs: PMK8550, PM8550, PM8550B and PM8550VX PMICs. It is similar to PMIC5-Gen2, with SW communication to ADCs on all PMICs going through PBS(Programmable Boot Sequence) firmware through a single register interface. This interface is implemented on SDAM (Shared Direct Access Memory) peripherals on the master PMIC PMK8550 rather than a dedicated ADC peripheral. Add documentation for PMIC5 Gen3 ADC and update SPMI PMIC bindings to allow ADC5 Gen3 as adc@ subnode. Acked-by: Jonathan Cameron Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jishnu Prakash Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/qcom,spmi-adc5-gen3.yaml | 151 ++++++++++++++++++ .../bindings/iio/adc/qcom,spmi-vadc.yaml | 2 + .../bindings/mfd/qcom,spmi-pmic.yaml | 1 + 3 files changed, 154 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/qcom,spmi-adc5-gen3.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-adc5-gen3.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-adc5-gen3.yaml new file mode 100644 index 000000000000..149f4af8f4b8 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-adc5-gen3.yaml @@ -0,0 +1,151 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/qcom,spmi-adc5-gen3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm's SPMI PMIC ADC5 Gen3 + +maintainers: + - Jishnu Prakash + +description: | + SPMI PMIC5 Gen3 voltage ADC (ADC) provides interface to clients to read + voltage. It is a 16-bit sigma-delta ADC. It also performs the same thermal + monitoring function as the existing ADC_TM devices. + + The interface is implemented on SDAM (Shared Direct Access Memory) peripherals + on the master PMIC rather than a dedicated ADC peripheral. The number of PMIC + SDAM peripherals allocated for ADC is not correlated with the PMIC used, it is + programmed in FW (PBS) and is fixed per SOC, based on the SOC requirements. + All boards using a particular (SOC + master PMIC) combination will have the + same number of ADC SDAMs supported on that PMIC. + +properties: + compatible: + const: qcom,spmi-adc5-gen3 + + reg: + items: + - description: SDAM0 base address in the SPMI PMIC register map + - description: SDAM1 base address + minItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + "#io-channel-cells": + const: 1 + + "#thermal-sensor-cells": + const: 1 + + interrupts: + items: + - description: SDAM0 end of conversion (EOC) interrupt + - description: SDAM1 EOC interrupt + minItems: 1 + +patternProperties: + "^channel@[0-9a-f]+$": + type: object + unevaluatedProperties: false + $ref: /schemas/iio/adc/qcom,spmi-vadc-common.yaml + description: + Represents the external channels which are connected to the ADC. + + properties: + qcom,decimation: + enum: [ 85, 340, 1360 ] + default: 1360 + + qcom,hw-settle-time: + enum: [ 15, 100, 200, 300, 400, 500, 600, 700, + 1000, 2000, 4000, 8000, 16000, 32000, 64000, 128000 ] + default: 15 + + qcom,avg-samples: + enum: [ 1, 2, 4, 8, 16 ] + default: 1 + + qcom,adc-tm: + description: + ADC_TM is a threshold monitoring feature in HW which can be enabled + on any ADC channel, to trigger an IRQ for threshold violation. In + earlier ADC generations, it was implemented in a separate device + (documented in Documentation/devicetree/bindings/thermal/qcom-spmi-adc-tm5.yaml.) + In Gen3, this feature can be enabled in the same ADC device for any + channel and threshold monitoring and IRQ triggering are handled in FW + (PBS) instead of another dedicated HW block. + This property indicates ADC_TM monitoring is done on this channel. + type: boolean + +required: + - compatible + - reg + - "#address-cells" + - "#size-cells" + - "#io-channel-cells" + - interrupts + +additionalProperties: false + +examples: + - | + #include + + pmic { + #address-cells = <1>; + #size-cells = <0>; + + adc@9000 { + compatible = "qcom,spmi-adc5-gen3"; + reg = <0x9000>, <0x9100>; + interrupts = <0x0 0x90 0x1 IRQ_TYPE_EDGE_RISING>, + <0x0 0x91 0x1 IRQ_TYPE_EDGE_RISING>; + #address-cells = <1>; + #size-cells = <0>; + #io-channel-cells = <1>; + #thermal-sensor-cells = <1>; + + /* PMK8550 Channel nodes */ + channel@3 { + reg = <0x3>; + label = "pmk8550_die_temp"; + qcom,pre-scaling = <1 1>; + }; + + channel@44 { + reg = <0x44>; + label = "pmk8550_xo_therm"; + qcom,pre-scaling = <1 1>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + qcom,adc-tm; + }; + + /* PM8550 Channel nodes */ + channel@103 { + reg = <0x103>; + label = "pm8550_die_temp"; + qcom,pre-scaling = <1 1>; + }; + + /* PM8550B Channel nodes */ + channel@78f { + reg = <0x78f>; + label = "pm8550b_vbat_sns_qbg"; + qcom,pre-scaling = <1 3>; + }; + + /* PM8550VS_C Channel nodes */ + channel@203 { + reg = <0x203>; + label = "pm8550vs_c_die_temp"; + qcom,pre-scaling = <1 1>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml index 16c80709a3ee..72188041e8b5 100644 --- a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml @@ -15,6 +15,8 @@ description: | voltage. The VADC is a 15-bit sigma-delta ADC. SPMI PMIC5/PMIC7 voltage ADC (ADC) provides interface to clients to read voltage. The VADC is a 16-bit sigma-delta ADC. + Note that PMIC7 ADC is the generation between PMIC5 and PMIC5 Gen3 ADC, + it can be considered like PMIC5 Gen2. properties: compatible: diff --git a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml index e5931d18d998..644c42b5e2e5 100644 --- a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml +++ b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml @@ -135,6 +135,7 @@ patternProperties: "^adc@[0-9a-f]+$": type: object oneOf: + - $ref: /schemas/iio/adc/qcom,spmi-adc5-gen3.yaml# - $ref: /schemas/iio/adc/qcom,spmi-iadc.yaml# - $ref: /schemas/iio/adc/qcom,spmi-rradc.yaml# - $ref: /schemas/iio/adc/qcom,spmi-vadc.yaml# From baff45179e90276a14acb9dffce17ff517708453 Mon Sep 17 00:00:00 2001 From: Jishnu Prakash Date: Fri, 30 Jan 2026 17:24:20 +0530 Subject: [PATCH 031/405] iio: adc: Add support for QCOM PMIC5 Gen3 ADC The ADC architecture on PMIC5 Gen3 is similar to that on PMIC5 Gen2, with all SW communication to ADC going through PMK8550 which communicates with other PMICs through PBS. One major difference is that the register interface used here is that of an SDAM (Shared Direct Access Memory) peripheral present on PMK8550. There may be more than one SDAM used for ADC5 Gen3 and each has eight channels, which may be used for either immediate reads (same functionality as previous PMIC5 and PMIC5 Gen2 ADC peripherals) or recurring measurements (same as ADC_TM functionality). By convention, we reserve the first channel of the first SDAM for all immediate reads and use the remaining channels across all SDAMs for ADC_TM monitoring functionality. Add support for PMIC5 Gen3 ADC driver for immediate read functionality. ADC_TM is implemented as an auxiliary thermal driver under this ADC driver. Signed-off-by: Jishnu Prakash Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 26 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/qcom-spmi-adc5-gen3.c | 860 ++++++++++++++++++ include/linux/iio/adc/qcom-adc5-gen3-common.h | 211 +++++ 4 files changed, 1098 insertions(+) create mode 100644 drivers/iio/adc/qcom-spmi-adc5-gen3.c create mode 100644 include/linux/iio/adc/qcom-adc5-gen3-common.h diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 60038ae8dfc4..1f5915dd192d 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1366,6 +1366,32 @@ config QCOM_SPMI_ADC5 To compile this driver as a module, choose M here: the module will be called qcom-spmi-adc5. +config QCOM_SPMI_ADC5_GEN3 + tristate "Qualcomm Technologies Inc. SPMI PMIC5 GEN3 ADC" + depends on SPMI && THERMAL + select REGMAP_SPMI + select QCOM_VADC_COMMON + select AUXILIARY_BUS + help + IIO Voltage PMIC5 Gen3 ADC driver for Qualcomm Technologies Inc. + + The driver supports reading multiple channels. The ADC is a 16-bit + sigma-delta ADC. The hardware supports calibrated results for + conversion requests and clients include reading phone power supply + voltage, on board system thermistors connected to the PMIC ADC, + PMIC die temperature, charger temperature, battery current, USB + voltage input and voltage signals connected to supported PMIC GPIO + pins. The hardware supports internal pull-up for thermistors and can + choose between a 30k, 100k or 400k ohm pull up using the ADC channels. + + In addition, the same driver supports ADC thermal monitoring devices + too. They appear as thermal zones with multiple trip points. A thermal + client sets threshold temperature for both warm and cool trips and + gets updated when a threshold is reached. + + To compile this driver as a module, choose M here: the module will + be called qcom-spmi-adc5-gen3. + config RCAR_GYRO_ADC tristate "Renesas R-Car GyroADC driver" depends on ARCH_RCAR_GEN2 || COMPILE_TEST diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index c76550415ff1..097357d146ba 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -116,6 +116,7 @@ obj-$(CONFIG_PAC1934) += pac1934.o obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o +obj-$(CONFIG_QCOM_SPMI_ADC5_GEN3) += qcom-spmi-adc5-gen3.o obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o obj-$(CONFIG_QCOM_SPMI_RRADC) += qcom-spmi-rradc.o obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o diff --git a/drivers/iio/adc/qcom-spmi-adc5-gen3.c b/drivers/iio/adc/qcom-spmi-adc5-gen3.c new file mode 100644 index 000000000000..f8168a14b907 --- /dev/null +++ b/drivers/iio/adc/qcom-spmi-adc5-gen3.c @@ -0,0 +1,860 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADC5_GEN3_VADC_SDAM 0x0 + +struct adc5_chip; + +/** + * struct adc5_channel_prop - ADC channel structure + * @common_props: structure with ADC channel properties (common to TM usage). + * @adc_tm: indicates TM type if the channel is used for TM measurements. + * @chip: pointer to top-level ADC device structure. + */ +struct adc5_channel_prop { + struct adc5_channel_common_prop common_props; + int adc_tm; + struct adc5_chip *chip; +}; + +/** + * struct adc5_chip - ADC private structure. + * @dev: SPMI ADC5 Gen3 device. + * @dev_data: Top-level ADC device data. + * @nchannels: number of ADC channels. + * @chan_props: array of ADC channel properties. + * @iio_chans: array of IIO channels specification. + * @complete: ADC result notification after interrupt is received. + * @lock: ADC lock for access to the peripheral, to prevent concurrent + * requests from multiple clients. + * @data: software configuration data. + * @n_tm_channels: number of ADC channels used for TM measurements. + * @handler: TM callback to be called for threshold violation interrupt + * on first SDAM. + * @tm_aux: pointer to auxiliary TM device. + */ +struct adc5_chip { + struct device *dev; + struct adc5_device_data dev_data; + unsigned int nchannels; + struct adc5_channel_prop *chan_props; + struct iio_chan_spec *iio_chans; + struct completion complete; + struct mutex lock; + const struct adc5_data *data; + unsigned int n_tm_channels; + void (*handler)(struct auxiliary_device *tm_aux); + struct auxiliary_device *tm_aux; +}; + +int adc5_gen3_read(struct adc5_device_data *adc, unsigned int sdam_index, + u16 offset, u8 *data, int len) +{ + return regmap_bulk_read(adc->regmap, + adc->base[sdam_index].base_addr + offset, + data, len); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_read, "QCOM_SPMI_ADC5_GEN3"); + +int adc5_gen3_write(struct adc5_device_data *adc, unsigned int sdam_index, + u16 offset, u8 *data, int len) +{ + return regmap_bulk_write(adc->regmap, + adc->base[sdam_index].base_addr + offset, + data, len); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_write, "QCOM_SPMI_ADC5_GEN3"); + +static int adc5_gen3_read_voltage_data(struct adc5_chip *adc, u16 *data) +{ + u8 rslt[2]; + int ret; + + ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_CH_DATA0(0), rslt, sizeof(rslt)); + if (ret) + return ret; + + *data = get_unaligned_le16(rslt); + + if (*data == ADC5_USR_DATA_CHECK) { + dev_err(adc->dev, "Invalid data:%#x\n", *data); + return -EINVAL; + } + + dev_dbg(adc->dev, "voltage raw code:%#x\n", *data); + + return 0; +} + +void adc5_gen3_update_dig_param(struct adc5_channel_common_prop *prop, u8 *data) +{ + /* Update calibration select and decimation ratio select */ + *data &= ~(ADC5_GEN3_DIG_PARAM_CAL_SEL_MASK | ADC5_GEN3_DIG_PARAM_DEC_RATIO_SEL_MASK); + *data |= FIELD_PREP(ADC5_GEN3_DIG_PARAM_CAL_SEL_MASK, prop->cal_method); + *data |= FIELD_PREP(ADC5_GEN3_DIG_PARAM_DEC_RATIO_SEL_MASK, prop->decimation); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_update_dig_param, "QCOM_SPMI_ADC5_GEN3"); + +#define ADC5_GEN3_READ_CONFIG_REGS 7 + +static int adc5_gen3_configure(struct adc5_chip *adc, + struct adc5_channel_common_prop *prop) +{ + u8 buf[ADC5_GEN3_READ_CONFIG_REGS]; + u8 conv_req = 0; + int ret; + + ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, ADC5_GEN3_SID, + buf, sizeof(buf)); + if (ret) + return ret; + + /* Write SID */ + buf[0] = FIELD_PREP(ADC5_GEN3_SID_MASK, prop->sid); + + /* + * Use channel 0 by default for immediate conversion and to indicate + * there is an actual conversion request + */ + buf[1] = ADC5_GEN3_CHAN_CONV_REQ | 0; + + buf[2] = ADC5_GEN3_TIME_IMMEDIATE; + + /* Digital param selection */ + adc5_gen3_update_dig_param(prop, &buf[3]); + + /* Update fast average sample value */ + buf[4] = FIELD_PREP(ADC5_GEN3_FAST_AVG_CTL_SAMPLES_MASK, + prop->avg_samples) | ADC5_GEN3_FAST_AVG_CTL_EN; + + /* Select ADC channel */ + buf[5] = prop->channel; + + /* Select HW settle delay for channel */ + buf[6] = FIELD_PREP(ADC5_GEN3_HW_SETTLE_DELAY_MASK, + prop->hw_settle_time_us); + + reinit_completion(&adc->complete); + + ret = adc5_gen3_write(&adc->dev_data, ADC5_GEN3_VADC_SDAM, ADC5_GEN3_SID, + buf, sizeof(buf)); + if (ret) + return ret; + + conv_req = ADC5_GEN3_CONV_REQ_REQ; + return adc5_gen3_write(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_CONV_REQ, &conv_req, sizeof(conv_req)); +} + +/* + * Worst case delay from PBS in readying handshake bit can be up to 15ms, when + * PBS is busy running other simultaneous transactions, while in the best case, + * it is already ready at this point. Assigning polling delay and retry count + * accordingly. + */ + +#define ADC5_GEN3_HS_DELAY_US 100 +#define ADC5_GEN3_HS_RETRY_COUNT 150 + +int adc5_gen3_poll_wait_hs(struct adc5_device_data *adc, + unsigned int sdam_index) +{ + u8 conv_req = ADC5_GEN3_CONV_REQ_REQ; + int ret, count; + u8 status = 0; + + for (count = 0; count < ADC5_GEN3_HS_RETRY_COUNT; count++) { + ret = adc5_gen3_read(adc, sdam_index, ADC5_GEN3_HS, &status, sizeof(status)); + if (ret) + return ret; + + if (status == ADC5_GEN3_HS_READY) { + ret = adc5_gen3_read(adc, sdam_index, ADC5_GEN3_CONV_REQ, + &conv_req, sizeof(conv_req)); + if (ret) + return ret; + + if (!conv_req) + return 0; + } + + fsleep(ADC5_GEN3_HS_DELAY_US); + } + + pr_err("Setting HS ready bit timed out, sdam_index:%d, status:%#x\n", + sdam_index, status); + return -ETIMEDOUT; +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_poll_wait_hs, "QCOM_SPMI_ADC5_GEN3"); + +int adc5_gen3_status_clear(struct adc5_device_data *adc, + int sdam_index, u16 offset, u8 *val, int len) +{ + u8 value; + int ret; + + ret = adc5_gen3_write(adc, sdam_index, offset, val, len); + if (ret) + return ret; + + /* To indicate conversion request is only to clear a status */ + value = 0; + ret = adc5_gen3_write(adc, sdam_index, ADC5_GEN3_PERPH_CH, &value, + sizeof(value)); + if (ret) + return ret; + + value = ADC5_GEN3_CONV_REQ_REQ; + return adc5_gen3_write(adc, sdam_index, ADC5_GEN3_CONV_REQ, &value, + sizeof(value)); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_status_clear, "QCOM_SPMI_ADC5_GEN3"); + +/* + * Worst case delay from PBS for conversion time can be up to 500ms, when PBS + * has timed out twice, once for the initial attempt and once for a retry of + * the same transaction. + */ + +#define ADC5_GEN3_CONV_TIMEOUT_MS 501 + +static int adc5_gen3_do_conversion(struct adc5_chip *adc, + struct adc5_channel_common_prop *prop, + u16 *data_volt) +{ + unsigned long rc; + int ret; + u8 val; + + guard(mutex)(&adc->lock); + ret = adc5_gen3_poll_wait_hs(&adc->dev_data, ADC5_GEN3_VADC_SDAM); + if (ret) + return ret; + + ret = adc5_gen3_configure(adc, prop); + if (ret) { + dev_err(adc->dev, "ADC configure failed with %d\n", ret); + return ret; + } + + /* No support for polling mode at present */ + rc = wait_for_completion_timeout(&adc->complete, + msecs_to_jiffies(ADC5_GEN3_CONV_TIMEOUT_MS)); + if (!rc) { + dev_err(adc->dev, "Reading ADC channel %s timed out\n", + prop->label); + return -ETIMEDOUT; + } + + ret = adc5_gen3_read_voltage_data(adc, data_volt); + if (ret) + return ret; + + val = BIT(0); + return adc5_gen3_status_clear(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_EOC_CLR, &val, 1); +} + +static irqreturn_t adc5_gen3_isr(int irq, void *dev_id) +{ + struct adc5_chip *adc = dev_id; + struct device *dev = adc->dev; + struct auxiliary_device *adev; + u8 status, eoc_status, val; + u8 tm_status[2]; + int ret; + + ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_STATUS1, &status, sizeof(status)); + if (ret) { + dev_err(dev, "adc read status1 failed with %d\n", ret); + return IRQ_HANDLED; + } + + ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_EOC_STS, &eoc_status, sizeof(eoc_status)); + if (ret) { + dev_err(dev, "adc read eoc status failed with %d\n", ret); + return IRQ_HANDLED; + } + + if (status & ADC5_GEN3_STATUS1_CONV_FAULT) { + dev_err_ratelimited(dev, + "Unexpected conversion fault, status:%#x, eoc_status:%#x\n", + status, eoc_status); + val = ADC5_GEN3_CONV_ERR_CLR_REQ; + adc5_gen3_status_clear(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_CONV_ERR_CLR, &val, 1); + return IRQ_HANDLED; + } + + /* CHAN0 is the preconfigured channel for immediate conversion */ + if (eoc_status & ADC5_GEN3_EOC_CHAN_0) + complete(&adc->complete); + + ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_TM_HIGH_STS, tm_status, sizeof(tm_status)); + if (ret) { + dev_err(dev, "adc read TM status failed with %d\n", ret); + return IRQ_HANDLED; + } + + dev_dbg(dev, "Interrupt status:%#x, EOC status:%#x, high:%#x, low:%#x\n", + status, eoc_status, tm_status[0], tm_status[1]); + + if (tm_status[0] || tm_status[1]) { + adev = adc->tm_aux; + if (!adev || !adev->dev.driver) { + dev_err(dev, "adc_tm auxiliary device not initialized\n"); + return IRQ_HANDLED; + } + + adc->handler(adev); + } + + return IRQ_HANDLED; +} + +static int adc5_gen3_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) +{ + struct adc5_chip *adc = iio_priv(indio_dev); + int i, v_channel; + + for (i = 0; i < adc->nchannels; i++) { + v_channel = ADC5_GEN3_V_CHAN(adc->chan_props[i].common_props); + if (v_channel == iiospec->args[0]) + return i; + } + + return -ENOENT; +} + +static int adc5_gen3_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct adc5_chip *adc = iio_priv(indio_dev); + struct adc5_channel_common_prop *prop; + u16 adc_code_volt; + int ret; + + prop = &adc->chan_props[chan->address].common_props; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + ret = adc5_gen3_do_conversion(adc, prop, &adc_code_volt); + if (ret) + return ret; + + ret = qcom_adc5_hw_scale(prop->scale_fn_type, prop->prescale, + adc->data, adc_code_volt, val); + if (ret) + return ret; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int adc5_gen3_read_label(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, char *label) +{ + struct adc5_chip *adc = iio_priv(indio_dev); + struct adc5_channel_prop *prop; + + prop = &adc->chan_props[chan->address]; + return sprintf(label, "%s\n", prop->common_props.label); +} + +static const struct iio_info adc5_gen3_info = { + .read_raw = adc5_gen3_read_raw, + .read_label = adc5_gen3_read_label, + .fwnode_xlate = adc5_gen3_fwnode_xlate, +}; + +struct adc5_channels { + unsigned int prescale_index; + enum iio_chan_type type; + long info_mask; + enum vadc_scale_fn_type scale_fn_type; +}; + +/* In these definitions, _pre refers to an index into adc5_prescale_ratios. */ +#define ADC5_CHAN(_type, _mask, _pre, _scale) \ + { \ + .prescale_index = _pre, \ + .type = _type, \ + .info_mask = _mask, \ + .scale_fn_type = _scale, \ + }, \ + +#define ADC5_CHAN_TEMP(_pre, _scale) \ + ADC5_CHAN(IIO_TEMP, BIT(IIO_CHAN_INFO_PROCESSED), _pre, _scale) \ + +#define ADC5_CHAN_VOLT(_pre, _scale) \ + ADC5_CHAN(IIO_VOLTAGE, BIT(IIO_CHAN_INFO_PROCESSED), _pre, _scale) \ + +#define ADC5_CHAN_CUR(_pre, _scale) \ + ADC5_CHAN(IIO_CURRENT, BIT(IIO_CHAN_INFO_PROCESSED), _pre, _scale) \ + +static const struct adc5_channels adc5_gen3_chans_pmic[ADC5_MAX_CHANNEL] = { + [ADC5_GEN3_REF_GND] = ADC5_CHAN_VOLT(0, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_1P25VREF] = ADC5_CHAN_VOLT(0, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_VPH_PWR] = ADC5_CHAN_VOLT(1, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_VBAT_SNS_QBG] = ADC5_CHAN_VOLT(1, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_USB_SNS_V_16] = ADC5_CHAN_TEMP(8, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_VIN_DIV16_MUX] = ADC5_CHAN_TEMP(8, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_DIE_TEMP] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_PMIC_THERM_PM7) + [ADC5_GEN3_AMUX1_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX2_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX3_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX4_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX5_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX6_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX1_GPIO_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX2_GPIO_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX3_GPIO_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX4_GPIO_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) +}; + +static int adc5_gen3_get_fw_channel_data(struct adc5_chip *adc, + struct adc5_channel_prop *prop, + struct fwnode_handle *fwnode) +{ + const char *name = fwnode_get_name(fwnode); + const struct adc5_data *data = adc->data; + struct device *dev = adc->dev; + const char *channel_name; + u32 chan, value, sid; + u32 varr[2]; + int ret; + + ret = fwnode_property_read_u32(fwnode, "reg", &chan); + if (ret < 0) + return dev_err_probe(dev, ret, "invalid channel number %s\n", + name); + + /* + * Value read from "reg" is virtual channel number + * virtual channel number = sid << 8 | channel number + */ + sid = FIELD_GET(ADC5_GEN3_VIRTUAL_SID_MASK, chan); + chan = FIELD_GET(ADC5_GEN3_CHANNEL_MASK, chan); + + if (chan > ADC5_MAX_CHANNEL) + return dev_err_probe(dev, -EINVAL, + "%s invalid channel number %d\n", + name, chan); + + prop->common_props.channel = chan; + prop->common_props.sid = sid; + + if (!adc->data->adc_chans[chan].info_mask) + return dev_err_probe(dev, -EINVAL, "Channel %#x not supported\n", chan); + + channel_name = name; + fwnode_property_read_string(fwnode, "label", &channel_name); + prop->common_props.label = channel_name; + + value = data->decimation[ADC5_DECIMATION_DEFAULT]; + fwnode_property_read_u32(fwnode, "qcom,decimation", &value); + ret = qcom_adc5_decimation_from_dt(value, data->decimation); + if (ret < 0) + return dev_err_probe(dev, ret, "%#x invalid decimation %d\n", + chan, value); + prop->common_props.decimation = ret; + + prop->common_props.prescale = adc->data->adc_chans[chan].prescale_index; + ret = fwnode_property_read_u32_array(fwnode, "qcom,pre-scaling", varr, 2); + if (!ret) { + ret = qcom_adc5_prescaling_from_dt(varr[0], varr[1]); + if (ret < 0) + return dev_err_probe(dev, ret, + "%#x invalid pre-scaling <%d %d>\n", + chan, varr[0], varr[1]); + prop->common_props.prescale = ret; + } + + value = data->hw_settle_1[VADC_DEF_HW_SETTLE_TIME]; + fwnode_property_read_u32(fwnode, "qcom,hw-settle-time", &value); + ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_1); + if (ret < 0) + return dev_err_probe(dev, ret, + "%#x invalid hw-settle-time %d us\n", + chan, value); + prop->common_props.hw_settle_time_us = ret; + + value = BIT(VADC_DEF_AVG_SAMPLES); + fwnode_property_read_u32(fwnode, "qcom,avg-samples", &value); + ret = qcom_adc5_avg_samples_from_dt(value); + if (ret < 0) + return dev_err_probe(dev, ret, "%#x invalid avg-samples %d\n", + chan, value); + prop->common_props.avg_samples = ret; + + if (fwnode_property_read_bool(fwnode, "qcom,ratiometric")) + prop->common_props.cal_method = ADC5_RATIOMETRIC_CAL; + else + prop->common_props.cal_method = ADC5_ABSOLUTE_CAL; + + prop->adc_tm = fwnode_property_read_bool(fwnode, "qcom,adc-tm"); + if (prop->adc_tm) { + adc->n_tm_channels++; + if (adc->n_tm_channels > (adc->dev_data.num_sdams * 8 - 1)) + return dev_err_probe(dev, -EINVAL, + "Number of TM nodes %u greater than channels supported:%u\n", + adc->n_tm_channels, + adc->dev_data.num_sdams * 8 - 1); + } + + return 0; +} + +static const struct adc5_data adc5_gen3_data_pmic = { + .full_scale_code_volt = 0x70e4, + .adc_chans = adc5_gen3_chans_pmic, + .info = &adc5_gen3_info, + .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX]) + { 85, 340, 1360 }, + .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX]) + { 15, 100, 200, 300, + 400, 500, 600, 700, + 1000, 2000, 4000, 8000, + 16000, 32000, 64000, 128000 }, +}; + +static const struct of_device_id adc5_match_table[] = { + { + .compatible = "qcom,spmi-adc5-gen3", + .data = &adc5_gen3_data_pmic, + }, + { } +}; +MODULE_DEVICE_TABLE(of, adc5_match_table); + +static int adc5_get_fw_data(struct adc5_chip *adc) +{ + const struct adc5_channels *adc_chan; + struct adc5_channel_prop *chan_props; + struct iio_chan_spec *iio_chan; + struct device *dev = adc->dev; + unsigned int index = 0; + int ret; + + adc->nchannels = device_get_child_node_count(dev); + if (!adc->nchannels) + return dev_err_probe(dev, -EINVAL, "No ADC channels found\n"); + + adc->iio_chans = devm_kcalloc(dev, adc->nchannels, + sizeof(*adc->iio_chans), GFP_KERNEL); + if (!adc->iio_chans) + return -ENOMEM; + + adc->chan_props = devm_kcalloc(dev, adc->nchannels, + sizeof(*adc->chan_props), GFP_KERNEL); + if (!adc->chan_props) + return -ENOMEM; + + chan_props = adc->chan_props; + adc->n_tm_channels = 0; + iio_chan = adc->iio_chans; + adc->data = device_get_match_data(dev); + + device_for_each_child_node_scoped(dev, child) { + ret = adc5_gen3_get_fw_channel_data(adc, chan_props, child); + if (ret) + return ret; + + chan_props->chip = adc; + adc_chan = &adc->data->adc_chans[chan_props->common_props.channel]; + chan_props->common_props.scale_fn_type = adc_chan->scale_fn_type; + + iio_chan->channel = ADC5_GEN3_V_CHAN(chan_props->common_props); + iio_chan->info_mask_separate = adc_chan->info_mask; + iio_chan->type = adc_chan->type; + iio_chan->address = index; + iio_chan->indexed = 1; + iio_chan++; + chan_props++; + index++; + } + + return 0; +} + +static void adc5_gen3_uninit_aux(void *data) +{ + auxiliary_device_uninit(data); +} + +static void adc5_gen3_delete_aux(void *data) +{ + auxiliary_device_delete(data); +} + +static void adc5_gen3_aux_device_release(struct device *dev) {} + +static int adc5_gen3_add_aux_tm_device(struct adc5_chip *adc) +{ + struct tm5_aux_dev_wrapper *aux_device; + int i, ret, i_tm = 0; + + aux_device = devm_kzalloc(adc->dev, sizeof(*aux_device), GFP_KERNEL); + if (!aux_device) + return -ENOMEM; + + aux_device->aux_dev.name = "adc5_tm_gen3"; + aux_device->aux_dev.dev.parent = adc->dev; + aux_device->aux_dev.dev.release = adc5_gen3_aux_device_release; + + aux_device->tm_props = devm_kcalloc(adc->dev, adc->n_tm_channels, + sizeof(*aux_device->tm_props), + GFP_KERNEL); + if (!aux_device->tm_props) + return -ENOMEM; + + aux_device->dev_data = &adc->dev_data; + + for (i = 0; i < adc->nchannels; i++) { + if (!adc->chan_props[i].adc_tm) + continue; + aux_device->tm_props[i_tm] = adc->chan_props[i].common_props; + i_tm++; + } + + device_set_of_node_from_dev(&aux_device->aux_dev.dev, adc->dev); + + aux_device->n_tm_channels = adc->n_tm_channels; + + ret = auxiliary_device_init(&aux_device->aux_dev); + if (ret) + return ret; + + ret = devm_add_action_or_reset(adc->dev, adc5_gen3_uninit_aux, + &aux_device->aux_dev); + if (ret) + return ret; + + ret = auxiliary_device_add(&aux_device->aux_dev); + if (ret) + return ret; + ret = devm_add_action_or_reset(adc->dev, adc5_gen3_delete_aux, + &aux_device->aux_dev); + if (ret) + return ret; + + adc->tm_aux = &aux_device->aux_dev; + + return 0; +} + +void adc5_gen3_mutex_lock(struct device *dev) + __acquires(&adc->lock) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); + struct adc5_chip *adc = iio_priv(indio_dev); + + mutex_lock(&adc->lock); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_mutex_lock, "QCOM_SPMI_ADC5_GEN3"); + +void adc5_gen3_mutex_unlock(struct device *dev) + __releases(&adc->lock) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); + struct adc5_chip *adc = iio_priv(indio_dev); + + mutex_unlock(&adc->lock); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_mutex_unlock, "QCOM_SPMI_ADC5_GEN3"); + +int adc5_gen3_get_scaled_reading(struct device *dev, + struct adc5_channel_common_prop *common_props, + int *val) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); + struct adc5_chip *adc = iio_priv(indio_dev); + u16 adc_code_volt; + int ret; + + ret = adc5_gen3_do_conversion(adc, common_props, &adc_code_volt); + if (ret) + return ret; + + return qcom_adc5_hw_scale(common_props->scale_fn_type, + common_props->prescale, + adc->data, adc_code_volt, val); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_get_scaled_reading, "QCOM_SPMI_ADC5_GEN3"); + +int adc5_gen3_therm_code_to_temp(struct device *dev, + struct adc5_channel_common_prop *common_props, + u16 code, int *val) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); + struct adc5_chip *adc = iio_priv(indio_dev); + + return qcom_adc5_hw_scale(common_props->scale_fn_type, + common_props->prescale, + adc->data, code, val); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_therm_code_to_temp, "QCOM_SPMI_ADC5_GEN3"); + +void adc5_gen3_register_tm_event_notifier(struct device *dev, + void (*handler)(struct auxiliary_device *)) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); + struct adc5_chip *adc = iio_priv(indio_dev); + + adc->handler = handler; +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_register_tm_event_notifier, "QCOM_SPMI_ADC5_GEN3"); + +static int adc5_gen3_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct adc5_chip *adc; + struct regmap *regmap; + int ret, i; + u32 *reg; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->dev_data.regmap = regmap; + adc->dev = dev; + + ret = device_property_count_u32(dev, "reg"); + if (ret < 0) + return ret; + + adc->dev_data.num_sdams = ret; + + reg = devm_kcalloc(dev, adc->dev_data.num_sdams, sizeof(u32), + GFP_KERNEL); + if (!reg) + return -ENOMEM; + + ret = device_property_read_u32_array(dev, "reg", reg, + adc->dev_data.num_sdams); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read reg property\n"); + + adc->dev_data.base = devm_kcalloc(dev, adc->dev_data.num_sdams, + sizeof(*adc->dev_data.base), + GFP_KERNEL); + if (!adc->dev_data.base) + return -ENOMEM; + + platform_set_drvdata(pdev, indio_dev); + init_completion(&adc->complete); + ret = devm_mutex_init(dev, &adc->lock); + if (ret) + return ret; + + for (i = 0; i < adc->dev_data.num_sdams; i++) { + adc->dev_data.base[i].base_addr = reg[i]; + + ret = platform_get_irq(pdev, i); + if (ret < 0) + return dev_err_probe(dev, ret, + "Getting IRQ %d failed\n", i); + + adc->dev_data.base[i].irq = ret; + + adc->dev_data.base[i].irq_name = devm_kasprintf(dev, GFP_KERNEL, + "sdam%d", i); + if (!adc->dev_data.base[i].irq_name) + return -ENOMEM; + } + + ret = devm_request_irq(dev, adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq, + adc5_gen3_isr, 0, + adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq_name, + adc); + if (ret) + return dev_err_probe(dev, ret, + "Failed to request SDAM%d irq\n", + ADC5_GEN3_VADC_SDAM); + + ret = adc5_get_fw_data(adc); + if (ret) + return ret; + + if (adc->n_tm_channels > 0) { + ret = adc5_gen3_add_aux_tm_device(adc); + if (ret) + dev_err_probe(dev, ret, + "Failed to add auxiliary TM device\n"); + } + + indio_dev->name = "spmi-adc5-gen3"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &adc5_gen3_info; + indio_dev->channels = adc->iio_chans; + indio_dev->num_channels = adc->nchannels; + + return devm_iio_device_register(dev, indio_dev); +} + +static struct platform_driver adc5_gen3_driver = { + .driver = { + .name = "qcom-spmi-adc5-gen3", + .of_match_table = adc5_match_table, + }, + .probe = adc5_gen3_probe, +}; +module_platform_driver(adc5_gen3_driver); + +MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC5 Gen3 ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("QCOM_SPMI_ADC5_GEN3"); diff --git a/include/linux/iio/adc/qcom-adc5-gen3-common.h b/include/linux/iio/adc/qcom-adc5-gen3-common.h new file mode 100644 index 000000000000..6303eaa6640b --- /dev/null +++ b/include/linux/iio/adc/qcom-adc5-gen3-common.h @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * Code used in the main and auxiliary Qualcomm PMIC voltage ADCs + * of type ADC5 Gen3. + */ + +#ifndef QCOM_ADC5_GEN3_COMMON_H +#define QCOM_ADC5_GEN3_COMMON_H + +#include +#include +#include +#include +#include +#include +#include + +#define ADC5_GEN3_HS 0x45 +#define ADC5_GEN3_HS_BUSY BIT(7) +#define ADC5_GEN3_HS_READY BIT(0) + +#define ADC5_GEN3_STATUS1 0x46 +#define ADC5_GEN3_STATUS1_CONV_FAULT BIT(7) +#define ADC5_GEN3_STATUS1_THR_CROSS BIT(6) +#define ADC5_GEN3_STATUS1_EOC BIT(0) + +#define ADC5_GEN3_TM_EN_STS 0x47 +#define ADC5_GEN3_TM_HIGH_STS 0x48 +#define ADC5_GEN3_TM_LOW_STS 0x49 + +#define ADC5_GEN3_EOC_STS 0x4a +#define ADC5_GEN3_EOC_CHAN_0 BIT(0) + +#define ADC5_GEN3_EOC_CLR 0x4b +#define ADC5_GEN3_TM_HIGH_STS_CLR 0x4c +#define ADC5_GEN3_TM_LOW_STS_CLR 0x4d +#define ADC5_GEN3_CONV_ERR_CLR 0x4e +#define ADC5_GEN3_CONV_ERR_CLR_REQ BIT(0) + +#define ADC5_GEN3_SID 0x4f +#define ADC5_GEN3_SID_MASK GENMASK(3, 0) + +#define ADC5_GEN3_PERPH_CH 0x50 +#define ADC5_GEN3_CHAN_CONV_REQ BIT(7) + +#define ADC5_GEN3_TIMER_SEL 0x51 +#define ADC5_GEN3_TIME_IMMEDIATE 0x1 + +#define ADC5_GEN3_DIG_PARAM 0x52 +#define ADC5_GEN3_DIG_PARAM_CAL_SEL_MASK GENMASK(5, 4) +#define ADC5_GEN3_DIG_PARAM_DEC_RATIO_SEL_MASK GENMASK(3, 2) + +#define ADC5_GEN3_FAST_AVG 0x53 +#define ADC5_GEN3_FAST_AVG_CTL_EN BIT(7) +#define ADC5_GEN3_FAST_AVG_CTL_SAMPLES_MASK GENMASK(2, 0) + +#define ADC5_GEN3_ADC_CH_SEL_CTL 0x54 +#define ADC5_GEN3_DELAY_CTL 0x55 +#define ADC5_GEN3_HW_SETTLE_DELAY_MASK GENMASK(3, 0) + +#define ADC5_GEN3_CH_EN 0x56 +#define ADC5_GEN3_HIGH_THR_INT_EN BIT(1) +#define ADC5_GEN3_LOW_THR_INT_EN BIT(0) + +#define ADC5_GEN3_LOW_THR0 0x57 +#define ADC5_GEN3_LOW_THR1 0x58 +#define ADC5_GEN3_HIGH_THR0 0x59 +#define ADC5_GEN3_HIGH_THR1 0x5a + +#define ADC5_GEN3_CH_DATA0(channel) (0x5c + (channel) * 2) +#define ADC5_GEN3_CH_DATA1(channel) (0x5d + (channel) * 2) + +#define ADC5_GEN3_CONV_REQ 0xe5 +#define ADC5_GEN3_CONV_REQ_REQ BIT(0) + +#define ADC5_GEN3_VIRTUAL_SID_MASK GENMASK(15, 8) +#define ADC5_GEN3_CHANNEL_MASK GENMASK(7, 0) +#define ADC5_GEN3_V_CHAN(x) \ + (FIELD_PREP(ADC5_GEN3_VIRTUAL_SID_MASK, (x).sid) | (x).channel) + +/* ADC channels for PMIC5 Gen3 */ +#define ADC5_GEN3_REF_GND 0x00 +#define ADC5_GEN3_1P25VREF 0x01 +#define ADC5_GEN3_DIE_TEMP 0x03 +#define ADC5_GEN3_USB_SNS_V_16 0x11 +#define ADC5_GEN3_VIN_DIV16_MUX 0x12 +#define ADC5_GEN3_VPH_PWR 0x8e +#define ADC5_GEN3_VBAT_SNS_QBG 0x8f +/* 100k pull-up channels */ +#define ADC5_GEN3_AMUX1_THM_100K_PU 0x44 +#define ADC5_GEN3_AMUX2_THM_100K_PU 0x45 +#define ADC5_GEN3_AMUX3_THM_100K_PU 0x46 +#define ADC5_GEN3_AMUX4_THM_100K_PU 0x47 +#define ADC5_GEN3_AMUX5_THM_100K_PU 0x48 +#define ADC5_GEN3_AMUX6_THM_100K_PU 0x49 +#define ADC5_GEN3_AMUX1_GPIO_100K_PU 0x4a +#define ADC5_GEN3_AMUX2_GPIO_100K_PU 0x4b +#define ADC5_GEN3_AMUX3_GPIO_100K_PU 0x4c +#define ADC5_GEN3_AMUX4_GPIO_100K_PU 0x4d + +#define ADC5_MAX_CHANNEL 0xc0 + +enum adc5_cal_method { + ADC5_NO_CAL = 0, + ADC5_RATIOMETRIC_CAL, + ADC5_ABSOLUTE_CAL, +}; + +enum adc5_time_select { + MEAS_INT_DISABLE = 0, + MEAS_INT_IMMEDIATE, + MEAS_INT_50MS, + MEAS_INT_100MS, + MEAS_INT_1S, + MEAS_INT_NONE, +}; + +/** + * struct adc5_sdam_data - data per SDAM allocated for adc usage + * @base_addr: base address for the ADC SDAM peripheral. + * @irq_name: ADC IRQ name. + * @irq: ADC IRQ number. + */ +struct adc5_sdam_data { + u16 base_addr; + const char *irq_name; + int irq; +}; + +/** + * struct adc5_device_data - Top-level ADC device data + * @regmap: ADC peripheral register map field. + * @base: array of SDAM data. + * @num_sdams: number of ADC SDAM peripherals. + */ +struct adc5_device_data { + struct regmap *regmap; + struct adc5_sdam_data *base; + int num_sdams; +}; + +/** + * struct adc5_channel_common_prop - ADC channel properties (common to ADC and TM). + * @channel: channel number, refer to the channel list. + * @cal_method: calibration method. + * @decimation: sampling rate supported for the channel. + * @sid: ID of PMIC owning the channel. + * @label: Channel name used in device tree. + * @prescale: channel scaling performed on the input signal. + * @hw_settle_time_us: the time between AMUX being configured and the + * start of conversion in uS. + * @avg_samples: ability to provide single result from the ADC + * that is an average of multiple measurements. + * @scale_fn_type: Represents the scaling function to convert voltage + * physical units desired by the client for the channel. + */ +struct adc5_channel_common_prop { + unsigned int channel; + enum adc5_cal_method cal_method; + unsigned int decimation; + unsigned int sid; + const char *label; + unsigned int prescale; + unsigned int hw_settle_time_us; + unsigned int avg_samples; + enum vadc_scale_fn_type scale_fn_type; +}; + +/** + * struct tm5_aux_dev_wrapper - wrapper structure around TM auxiliary device + * @aux_dev: TM auxiliary device structure. + * @dev_data: Top-level ADC device data. + * @tm_props: Array of common ADC channel properties for TM channels. + * @n_tm_channels: number of TM channels. + */ +struct tm5_aux_dev_wrapper { + struct auxiliary_device aux_dev; + struct adc5_device_data *dev_data; + struct adc5_channel_common_prop *tm_props; + unsigned int n_tm_channels; +}; + +int adc5_gen3_read(struct adc5_device_data *adc, unsigned int sdam_index, + u16 offset, u8 *data, int len); + +int adc5_gen3_write(struct adc5_device_data *adc, unsigned int sdam_index, + u16 offset, u8 *data, int len); + +int adc5_gen3_poll_wait_hs(struct adc5_device_data *adc, + unsigned int sdam_index); + +void adc5_gen3_update_dig_param(struct adc5_channel_common_prop *prop, + u8 *data); + +int adc5_gen3_status_clear(struct adc5_device_data *adc, + int sdam_index, u16 offset, u8 *val, int len); + +void adc5_gen3_mutex_lock(struct device *dev); +void adc5_gen3_mutex_unlock(struct device *dev); +int adc5_gen3_get_scaled_reading(struct device *dev, + struct adc5_channel_common_prop *common_props, + int *val); +int adc5_gen3_therm_code_to_temp(struct device *dev, + struct adc5_channel_common_prop *common_props, + u16 code, int *val); +void adc5_gen3_register_tm_event_notifier(struct device *dev, + void (*handler)(struct auxiliary_device *)); + +#endif /* QCOM_ADC5_GEN3_COMMON_H */ From 0e2af0fc30eca0ae92abcf1c8fc7be94a8cc95c3 Mon Sep 17 00:00:00 2001 From: Archit Anant Date: Wed, 18 Feb 2026 18:23:27 +0530 Subject: [PATCH 032/405] staging: iio: impedance-analyzer: ad5933: use div64_ul() instead of do_div() Replace do_div() with div64_ul() since the remainder is not used. div64_ul() is the preferred API for 64-bit by 32-bit division when only the quotient is needed. Also replace explicit casting and shifting with the BIT_ULL(27) macro for clarity. Note: A mathematical simplification to `(freq * BIT_ULL(29)) / mclk` was suggested during review to improve precision. However, as confirmed by maintainers, the original driver's truncation via `(mclk / 4)` might be intentional or relied upon by userspace. Since hardware is not available for verification, this patch preserves the original logic to avoid regression risk in the absence of testing. Issue identified by coccicheck using do_div.cocci. Signed-off-by: Archit Anant Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/staging/iio/impedance-analyzer/ad5933.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index 85a4223295cd..f6d3d98b6c6a 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -5,6 +5,7 @@ * Copyright 2011 Analog Devices Inc. */ +#include #include #include #include @@ -194,8 +195,7 @@ static int ad5933_set_freq(struct ad5933_state *st, u8 d8[4]; } dat; - freqreg = (u64)freq * (u64)(1 << 27); - do_div(freqreg, st->mclk_hz / 4); + freqreg = div64_ul(BIT_ULL(27) * freq, st->mclk_hz / 4); switch (reg) { case AD5933_REG_FREQ_START: From d1e13ac7c2641a8ec815a9fe10835726eaf05302 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sun, 22 Feb 2026 02:09:08 +0800 Subject: [PATCH 033/405] iio: adc: nxp-sar-adc: Remove unnecessary type casting The readl_poll_timeout() macro returns a signed integer error code. In nxp_sar_adc_calibration_wait(), the return value is casted to u32 before being returned as int, which is unnecessary. Signed-off-by: Felix Gu Signed-off-by: Jonathan Cameron --- drivers/iio/adc/nxp-sar-adc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/nxp-sar-adc.c b/drivers/iio/adc/nxp-sar-adc.c index 9efa883c277d..a6e4888a8464 100644 --- a/drivers/iio/adc/nxp-sar-adc.c +++ b/drivers/iio/adc/nxp-sar-adc.c @@ -247,7 +247,8 @@ static inline void nxp_sar_adc_calibration_start(void __iomem *base) static inline int nxp_sar_adc_calibration_wait(void __iomem *base) { - u32 msr, ret; + u32 msr; + int ret; ret = readl_poll_timeout(NXP_SAR_ADC_MSR(base), msr, !FIELD_GET(NXP_SAR_ADC_MSR_CALBUSY, msr), From 12b393486c707dc005540da58f6c7a60776941ac Mon Sep 17 00:00:00 2001 From: Salah Triki Date: Sat, 21 Feb 2026 08:32:42 +0100 Subject: [PATCH 034/405] iio: core: Clean up device correctly on viio_trigger_alloc() failure Move device_initialize() after all error paths in viio_trigger_alloc(). Previously, put_device() should have been called on all error paths after device_initialize(), but that was not done. Rather than adding put_device(), move device_initialize() to avoid needing to unwind it on error. In addition move trig->dev initialization to just before device_initialize() to related code together. Signed-off-by: Salah Triki Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-trigger.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 7f34fe7bad07..9776a185864e 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -561,10 +561,6 @@ struct iio_trigger *viio_trigger_alloc(struct device *parent, if (!trig) return NULL; - trig->dev.parent = parent; - trig->dev.type = &iio_trig_type; - trig->dev.bus = &iio_bus_type; - device_initialize(&trig->dev); INIT_WORK(&trig->reenable_work, iio_reenable_work_fn); mutex_init(&trig->pool_lock); @@ -592,6 +588,11 @@ struct iio_trigger *viio_trigger_alloc(struct device *parent, IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE); } + trig->dev.parent = parent; + trig->dev.type = &iio_trig_type; + trig->dev.bus = &iio_bus_type; + device_initialize(&trig->dev); + return trig; free_descs: From 3d8fedcc62b663f6c04b1d172e7298c71bbddb8f Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 20 Feb 2026 15:33:34 +0200 Subject: [PATCH 035/405] iio: filter: admv8818: remove redundant else after return The else in admv8818_init() is unnecessary since the if block already returns after calling admv8818_rfin_band_select() when clkin is present. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/filter/admv8818.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/filter/admv8818.c b/drivers/iio/filter/admv8818.c index 19f823446cda..e494fd33911b 100644 --- a/drivers/iio/filter/admv8818.c +++ b/drivers/iio/filter/admv8818.c @@ -695,8 +695,8 @@ static int admv8818_init(struct admv8818_state *st) if (st->clkin) return admv8818_rfin_band_select(st); - else - return 0; + + return 0; } static int admv8818_clk_setup(struct admv8818_state *st) From b40be056eefe458698d75ff2b78ed853efdf5e8a Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 20 Feb 2026 15:18:46 +0200 Subject: [PATCH 036/405] iio: adc: ad7266: simplify error return Return PTR_ERR() directly instead of assigning it to an intermediate variable first. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7266.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index 3364ac6c4631..0ef36c249ab8 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -409,10 +409,8 @@ static int ad7266_probe(struct spi_device *spi) st->gpios[i] = devm_gpiod_get(&spi->dev, ad7266_gpio_labels[i], GPIOD_OUT_LOW); - if (IS_ERR(st->gpios[i])) { - ret = PTR_ERR(st->gpios[i]); - return ret; - } + if (IS_ERR(st->gpios[i])) + return PTR_ERR(st->gpios[i]); } } } else { From 4d9fccb3e98712df571936ec3d31338e3b906690 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 20 Feb 2026 14:25:19 +0100 Subject: [PATCH 037/405] iio: core: Simplify IIO core managed APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use devm_add_action_or_reset() instead of devres_alloc() and devres_add(), which works the same. This will simplify the code. There is no functional changes. While at it, inline devm_iio_kfifo_allocate() into its only user. Reviewed-by: Nuno Sá Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/kfifo_buf.c | 41 ++++++++---------------------- drivers/iio/industrialio-trigger.c | 24 ++++++++--------- 2 files changed, 21 insertions(+), 44 deletions(-) diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c index a6ff9085b8a2..b67ea6468226 100644 --- a/drivers/iio/buffer/kfifo_buf.c +++ b/drivers/iio/buffer/kfifo_buf.c @@ -224,35 +224,9 @@ void iio_kfifo_free(struct iio_buffer *r) } EXPORT_SYMBOL(iio_kfifo_free); -static void devm_iio_kfifo_release(struct device *dev, void *res) +static void devm_iio_kfifo_release(void *buffer) { - iio_kfifo_free(*(struct iio_buffer **)res); -} - -/** - * devm_iio_kfifo_allocate - Resource-managed iio_kfifo_allocate() - * @dev: Device to allocate kfifo buffer for - * - * RETURNS: - * Pointer to allocated iio_buffer on success, NULL on failure. - */ -static struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev) -{ - struct iio_buffer **ptr, *r; - - ptr = devres_alloc(devm_iio_kfifo_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return NULL; - - r = iio_kfifo_allocate(); - if (r) { - *ptr = r; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } - - return r; + iio_kfifo_free(buffer); } /** @@ -262,10 +236,12 @@ static struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev) * @setup_ops: The setup_ops required to configure the HW part of the buffer (optional) * @buffer_attrs: Extra sysfs buffer attributes for this IIO buffer * - * This function allocates a kfifo buffer via devm_iio_kfifo_allocate() and + * This function allocates a kfifo buffer via iio_kfifo_allocate() and * attaches it to the IIO device via iio_device_attach_buffer(). * This is meant to be a bit of a short-hand/helper function as there are a few * drivers that seem to do this. + * + * Return: 0 on success, negative error code on failure. */ int devm_iio_kfifo_buffer_setup_ext(struct device *dev, struct iio_dev *indio_dev, @@ -273,11 +249,16 @@ int devm_iio_kfifo_buffer_setup_ext(struct device *dev, const struct iio_dev_attr **buffer_attrs) { struct iio_buffer *buffer; + int ret; - buffer = devm_iio_kfifo_allocate(dev); + buffer = iio_kfifo_allocate(); if (!buffer) return -ENOMEM; + ret = devm_add_action_or_reset(dev, devm_iio_kfifo_release, buffer); + if (ret) + return ret; + indio_dev->modes |= INDIO_BUFFER_SOFTWARE; indio_dev->setup_ops = setup_ops; diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 9776a185864e..17781c12bc85 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -635,9 +635,9 @@ void iio_trigger_free(struct iio_trigger *trig) } EXPORT_SYMBOL(iio_trigger_free); -static void devm_iio_trigger_release(struct device *dev, void *res) +static void devm_iio_trigger_release(void *trig) { - iio_trigger_free(*(struct iio_trigger **)res); + iio_trigger_free(trig); } /** @@ -659,24 +659,20 @@ struct iio_trigger *__devm_iio_trigger_alloc(struct device *parent, struct module *this_mod, const char *fmt, ...) { - struct iio_trigger **ptr, *trig; + struct iio_trigger *trig; va_list vargs; - - ptr = devres_alloc(devm_iio_trigger_release, sizeof(*ptr), - GFP_KERNEL); - if (!ptr) - return NULL; + int ret; /* use raw alloc_dr for kmalloc caller tracing */ va_start(vargs, fmt); trig = viio_trigger_alloc(parent, this_mod, fmt, vargs); va_end(vargs); - if (trig) { - *ptr = trig; - devres_add(parent, ptr); - } else { - devres_free(ptr); - } + if (!trig) + return NULL; + + ret = devm_add_action_or_reset(parent, devm_iio_trigger_release, trig); + if (ret) + return NULL; return trig; } From 4b0d26cb9a79d38fcf61fdcf821e70b335718610 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 20 Feb 2026 15:11:43 +0200 Subject: [PATCH 038/405] iio: adc: ade9000: use dev_err_probe() in probe path Replace dev_err() + return with dev_err_probe() in ade9000_reset(), which is called during probe. This simplifies error handling. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ade9000.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index db085dc5e526..5dcc26a08970 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -1589,10 +1589,9 @@ static int ade9000_reset(struct ade9000_state *st) /* Only wait for completion if IRQ1 is available to signal reset done */ if (fwnode_irq_get_byname(dev_fwnode(dev), "irq1") >= 0) { if (!wait_for_completion_timeout(&st->reset_completion, - msecs_to_jiffies(1000))) { - dev_err(dev, "Reset timeout after 1s\n"); - return -ETIMEDOUT; - } + msecs_to_jiffies(1000))) + return dev_err_probe(dev, -ETIMEDOUT, + "Reset timeout after 1s\n"); } /* If no IRQ available, reset is already complete after the 50ms delay above */ From 2ee227268c3673031335c10be0dfeaa5222f87f6 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Mon, 16 Feb 2026 17:10:45 +0000 Subject: [PATCH 039/405] MAINTAINERS: Add missing maintainer entry for AD8366 driver Add maintainers entry for drivers/iio/amplifiers/ad8366.c Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 55af015174a5..5ae0f8a7a1dd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1594,6 +1594,14 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml F: drivers/iio/adc/ad7780.c +ANALOG DEVICES INC AD8366 DRIVER +M: Michael Hennerich +M: Rodrigo Alencar +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: drivers/iio/amplifiers/ad8366.c + ANALOG DEVICES INC AD9467 DRIVER M: Michael Hennerich M: Nuno Sa From 3cfbb50d1414a1e40c03a595eae56b6b3ffe2691 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Mon, 16 Feb 2026 17:10:46 +0000 Subject: [PATCH 040/405] dt-bindings: iio: amplifiers: Add AD8366 support Add device tree binding documentation for amplifiers and digital attenuators. This covers different device variants with similar SPI control. Each device has its own gain range and step, hence no fallback compatibles are used. Reviewed-by: Conor Dooley Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- .../bindings/iio/amplifiers/adi,ad8366.yaml | 97 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 98 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/amplifiers/adi,ad8366.yaml diff --git a/Documentation/devicetree/bindings/iio/amplifiers/adi,ad8366.yaml b/Documentation/devicetree/bindings/iio/amplifiers/adi,ad8366.yaml new file mode 100644 index 000000000000..2719de1166a1 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/amplifiers/adi,ad8366.yaml @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/amplifiers/adi,ad8366.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AD8366 and similar Gain Amplifiers and Digital Attenuators + +maintainers: + - Michael Hennerich + - Rodrigo Alencar + +description: + Digital Variable Gain Amplifiers (VGAs) and Digital Attenuators with + SPI interface. + +properties: + compatible: + enum: + - adi,ad8366 + - adi,ada4961 + - adi,adl5240 + - adi,adrf5720 + - adi,adrf5730 + - adi,adrf5731 + - adi,hmc271a + - adi,hmc792a + - adi,hmc1018a + - adi,hmc1019a + - adi,hmc1119 + + reg: + maxItems: 1 + + vcc-supply: + description: Regulator that provides power to the device. + + reset-gpios: + maxItems: 1 + + enable-gpios: + maxItems: 1 + description: Power-up or Serial Mode Enable GPIO. + +required: + - compatible + - reg + - vcc-supply + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + - if: + not: + properties: + compatible: + contains: + const: adi,hmc271a + then: + properties: + reset-gpios: false + - if: + not: + properties: + compatible: + contains: + anyOf: + - const: adi,ad8366 + - const: adi,ada4961 + - const: adi,adrf5720 + - const: adi,adrf5730 + - const: adi,adrf5731 + - const: adi,hmc792a + - const: adi,hmc1018a + - const: adi,hmc1019a + - const: adi,hmc1119 + then: + properties: + enable-gpios: false + +unevaluatedProperties: false + +examples: + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + + amplifier@0 { + compatible = "adi,ad8366"; + reg = <0>; + spi-max-frequency = <1000000>; + vcc-supply = <&vcc_3v3>; + enable-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 5ae0f8a7a1dd..1c75276404df 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1600,6 +1600,7 @@ M: Rodrigo Alencar L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/amplifiers/adi,ad8366.yaml F: drivers/iio/amplifiers/ad8366.c ANALOG DEVICES INC AD9467 DRIVER From 5593bddbf9dd5d94ba6547cc01cb894825c2fac5 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Mon, 16 Feb 2026 17:10:47 +0000 Subject: [PATCH 041/405] iio: amplifiers: ad8366: refactor include headers Apply IWYU principle, removing the following headers: - linux/device.h: no usage of devm_add_action_or_reset, device_attr... - linux/kernel.h: no usage of container_of, kasprintf, ... - linux/slab.h: memory management handled by iio - linux/sysfs.h: sysfs interaction is managed by iio - linux/iio/sysfs.h: not using iio device attributes in this driver Adding the following missing headers: + linux/array_size.h: for ARRAY_SIZE + linux/bits.h: for BIT + linux/dev_printk.h: for dev_err + linux/math.h: for abs + linux/mutex.h: for mutex_lock, mutex_unlock + linux/mod_devicetable.h: for spi_device_id + linux/types.h for NULL, __aligned Additionally, those include directives are alphabetically sorted. Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/ad8366.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index d06ac786501c..8dc639d30dbb 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -11,19 +11,22 @@ * Copyright 2012-2019 Analog Devices Inc. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include enum ad8366_type { ID_AD8366, From d9eece6f39c64b958e590686781e2aa16d7d6ae4 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Mon, 16 Feb 2026 17:10:48 +0000 Subject: [PATCH 042/405] iio: amplifiers: ad8366: add local dev pointer to the probe function Create local device pointer in the probe function to shorten lines, making the code easier to read. The local device pointer replaces &spi->dev and will be reused across other probe function places in later patches. Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/ad8366.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index 8dc639d30dbb..677d02f4f075 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -248,11 +248,12 @@ static const struct iio_chan_spec ada4961_channels[] = { static int ad8366_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; struct iio_dev *indio_dev; struct ad8366_state *st; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (indio_dev == NULL) return -ENOMEM; From 5603a07af9f171b8402e839537698f0ecff4796b Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Mon, 16 Feb 2026 17:10:49 +0000 Subject: [PATCH 043/405] iio: amplifiers: ad8366: use devm_mutex_init() and drop mutex_init() Adopt proper mutex lifecycle with devm_mutex_init(), replacing mutex_init(). Mutex init is moved up (before regulator init), so that goto statement in the error path is avoided (which will be cleaned up later). Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/ad8366.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index 677d02f4f075..6466f3eb6bbc 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -259,6 +259,10 @@ static int ad8366_probe(struct spi_device *spi) st = iio_priv(indio_dev); + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + st->reg = devm_regulator_get(&spi->dev, "vcc"); if (!IS_ERR(st->reg)) { ret = regulator_enable(st->reg); @@ -267,7 +271,6 @@ static int ad8366_probe(struct spi_device *spi) } spi_set_drvdata(spi, indio_dev); - mutex_init(&st->lock); st->spi = spi; st->type = spi_get_device_id(spi)->driver_data; From 5fdb9c833293c6c341f58dc8109a6fdd2556a034 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Mon, 16 Feb 2026 17:10:50 +0000 Subject: [PATCH 044/405] iio: amplifiers: ad8366: refactor device resource management Adhere modern device resource management with the following: - Voltage regulator managed and enabled internally; - IIO device registration handled with devm_iio_device_register(); - removal of goto's from the probe function; - ad8366_remove() removed as it is not needed anymore; With the drop of goto's dev_err_probe() is used to report probe errors. Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/ad8366.c | 50 +++++++-------------------------- 1 file changed, 10 insertions(+), 40 deletions(-) diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index 6466f3eb6bbc..e8c80551d524 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -43,7 +43,6 @@ struct ad8366_info { struct ad8366_state { struct spi_device *spi; - struct regulator *reg; struct mutex lock; /* protect sensor state */ struct gpio_desc *reset_gpio; unsigned char ch[2]; @@ -263,14 +262,10 @@ static int ad8366_probe(struct spi_device *spi) if (ret) return ret; - st->reg = devm_regulator_get(&spi->dev, "vcc"); - if (!IS_ERR(st->reg)) { - ret = regulator_enable(st->reg); - if (ret) - return ret; - } + ret = devm_regulator_get_enable(dev, "vcc"); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulator\n"); - spi_set_drvdata(spi, indio_dev); st->spi = spi; st->type = spi_get_device_id(spi)->driver_data; @@ -284,17 +279,15 @@ static int ad8366_probe(struct spi_device *spi) case ID_HMC792: case ID_HMC1119: st->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(st->reset_gpio)) { - ret = PTR_ERR(st->reset_gpio); - goto error_disable_reg; - } + if (IS_ERR(st->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(st->reset_gpio), + "Failed to get reset gpio\n"); + indio_dev->channels = ada4961_channels; indio_dev->num_channels = ARRAY_SIZE(ada4961_channels); break; default: - dev_err(&spi->dev, "Invalid device ID\n"); - ret = -EINVAL; - goto error_disable_reg; + return dev_err_probe(dev, -EINVAL, "Invalid device ID\n"); } st->info = &ad8366_infos[st->type]; @@ -304,31 +297,9 @@ static int ad8366_probe(struct spi_device *spi) ret = ad8366_write(indio_dev, 0, 0); if (ret < 0) - goto error_disable_reg; + return dev_err_probe(dev, ret, "failed to write initial gain\n"); - ret = iio_device_register(indio_dev); - if (ret) - goto error_disable_reg; - - return 0; - -error_disable_reg: - if (!IS_ERR(st->reg)) - regulator_disable(st->reg); - - return ret; -} - -static void ad8366_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad8366_state *st = iio_priv(indio_dev); - struct regulator *reg = st->reg; - - iio_device_unregister(indio_dev); - - if (!IS_ERR(reg)) - regulator_disable(reg); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id ad8366_id[] = { @@ -346,7 +317,6 @@ static struct spi_driver ad8366_driver = { .name = KBUILD_MODNAME, }, .probe = ad8366_probe, - .remove = ad8366_remove, .id_table = ad8366_id, }; From ee4f8c56e46abd4bfeab1cee96cd391c0b7a3e4a Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Mon, 16 Feb 2026 17:10:51 +0000 Subject: [PATCH 045/405] iio: amplifiers: ad8366: replace reset-gpio with reset controller Remove reset_gpio from the device state struct and use the reset_control interface instead, using a local variable, as it is not being used anywhere else. The reset controller init is moved out from the switch case and optionally initialized for every device variant. Although not all devices have a reset pin the code does not need to change if it is not wired. Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/ad8366.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index e8c80551d524..8b3d6825423e 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -44,7 +45,6 @@ struct ad8366_info { struct ad8366_state { struct spi_device *spi; struct mutex lock; /* protect sensor state */ - struct gpio_desc *reset_gpio; unsigned char ch[2]; enum ad8366_type type; const struct ad8366_info *info; @@ -248,6 +248,7 @@ static const struct iio_chan_spec ada4961_channels[] = { static int ad8366_probe(struct spi_device *spi) { struct device *dev = &spi->dev; + struct reset_control *rstc; struct iio_dev *indio_dev; struct ad8366_state *st; int ret; @@ -278,11 +279,6 @@ static int ad8366_probe(struct spi_device *spi) case ID_ADL5240: case ID_HMC792: case ID_HMC1119: - st->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(st->reset_gpio)) - return dev_err_probe(dev, PTR_ERR(st->reset_gpio), - "Failed to get reset gpio\n"); - indio_dev->channels = ada4961_channels; indio_dev->num_channels = ARRAY_SIZE(ada4961_channels); break; @@ -290,6 +286,11 @@ static int ad8366_probe(struct spi_device *spi) return dev_err_probe(dev, -EINVAL, "Invalid device ID\n"); } + rstc = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL); + if (IS_ERR(rstc)) + return dev_err_probe(dev, PTR_ERR(rstc), + "Failed to get reset controller\n"); + st->info = &ad8366_infos[st->type]; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->info = &ad8366_info; From 314b184b8202563b41317132c8aeb79fc10e14f7 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Mon, 16 Feb 2026 17:10:52 +0000 Subject: [PATCH 046/405] iio: amplifiers: ad8366: prepare for device-tree support Drop switch case on the enum ID in favor of extended chip info table, containing: - gain_step, indicating with sign the start of the code range; - num_channels, to indicate the number IIO channels; - pack_code() function to describe how SPI buffer is populated; Which allowed for a simplified read_raw() and write_raw() callbacks. The probe() function was adjusted accordingly. The linux/array_size.h include is removed as number of channels is provided by chip info table. Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/ad8366.c | 134 +++++++++++--------------------- 1 file changed, 44 insertions(+), 90 deletions(-) diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index 8b3d6825423e..22eb6c9bb0f6 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -11,7 +11,6 @@ * Copyright 2012-2019 Analog Devices Inc. */ -#include #include #include #include @@ -26,6 +25,7 @@ #include #include #include +#include #include @@ -40,13 +40,16 @@ enum ad8366_type { struct ad8366_info { int gain_min; int gain_max; + int gain_step; + size_t num_channels; + size_t (*pack_code)(const unsigned char *code, size_t num_channels, + unsigned char *data); }; struct ad8366_state { struct spi_device *spi; struct mutex lock; /* protect sensor state */ unsigned char ch[2]; - enum ad8366_type type; const struct ad8366_info *info; /* * DMA (thus cache coherency maintenance) may require the @@ -55,60 +58,61 @@ struct ad8366_state { unsigned char data[2] __aligned(IIO_DMA_MINALIGN); }; +static size_t ad8366_pack_code(const unsigned char *code, size_t num_channels, + unsigned char *data) +{ + u8 ch_a = bitrev8(code[0]) >> 2; + u8 ch_b = bitrev8(code[1]) >> 2; + + put_unaligned_be16((ch_b << 6) | ch_a, &data[0]); + return sizeof(__be16); +} + static const struct ad8366_info ad8366_infos[] = { [ID_AD8366] = { .gain_min = 4500, .gain_max = 20500, + .gain_step = 253, + .num_channels = 2, + .pack_code = ad8366_pack_code, }, [ID_ADA4961] = { .gain_min = -6000, .gain_max = 15000, + .gain_step = -1000, + .num_channels = 1, }, [ID_ADL5240] = { .gain_min = -11500, .gain_max = 20000, + .gain_step = 500, + .num_channels = 1, }, [ID_HMC792] = { .gain_min = -15750, .gain_max = 0, + .gain_step = 250, + .num_channels = 1, }, [ID_HMC1119] = { .gain_min = -31750, .gain_max = 0, + .gain_step = -250, + .num_channels = 1, }, }; -static int ad8366_write(struct iio_dev *indio_dev, - unsigned char ch_a, unsigned char ch_b) +static int ad8366_write_code(struct ad8366_state *st) { - struct ad8366_state *st = iio_priv(indio_dev); - int ret; + const struct ad8366_info *inf = st->info; + size_t len = 1; - switch (st->type) { - case ID_AD8366: - ch_a = bitrev8(ch_a & 0x3F); - ch_b = bitrev8(ch_b & 0x3F); + if (inf->pack_code) + len = inf->pack_code(st->ch, inf->num_channels, st->data); + else + st->data[0] = st->ch[0]; - st->data[0] = ch_b >> 4; - st->data[1] = (ch_b << 4) | (ch_a >> 2); - break; - case ID_ADA4961: - st->data[0] = ch_a & 0x1F; - break; - case ID_ADL5240: - st->data[0] = (ch_a & 0x3F); - break; - case ID_HMC792: - case ID_HMC1119: - st->data[0] = ch_a; - break; - } - - ret = spi_write(st->spi, st->data, indio_dev->num_channels); - if (ret < 0) - dev_err(&indio_dev->dev, "write failed (%d)", ret); - - return ret; + return spi_write(st->spi, st->data, len); } static int ad8366_read_raw(struct iio_dev *indio_dev, @@ -118,6 +122,7 @@ static int ad8366_read_raw(struct iio_dev *indio_dev, long m) { struct ad8366_state *st = iio_priv(indio_dev); + const struct ad8366_info *inf = st->info; int ret; int code, gain = 0; @@ -125,25 +130,8 @@ static int ad8366_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_HARDWAREGAIN: code = st->ch[chan->channel]; - - switch (st->type) { - case ID_AD8366: - gain = code * 253 + 4500; - break; - case ID_ADA4961: - gain = 15000 - code * 1000; - break; - case ID_ADL5240: - gain = 20000 - 31500 + code * 500; - break; - case ID_HMC792: - gain = -1 * code * 500; - break; - case ID_HMC1119: - gain = -1 * code * 250; - break; - } - + gain = inf->gain_step > 0 ? inf->gain_min : inf->gain_max; + gain += inf->gain_step * code; /* Values in dB */ *val = gain / 1000; *val2 = (gain % 1000) * 1000; @@ -178,29 +166,14 @@ static int ad8366_write_raw(struct iio_dev *indio_dev, if (gain > inf->gain_max || gain < inf->gain_min) return -EINVAL; - switch (st->type) { - case ID_AD8366: - code = (gain - 4500) / 253; - break; - case ID_ADA4961: - code = (15000 - gain) / 1000; - break; - case ID_ADL5240: - code = ((gain - 500 - 20000) / 500) & 0x3F; - break; - case ID_HMC792: - code = (abs(gain) / 500) & 0x3F; - break; - case ID_HMC1119: - code = (abs(gain) / 250) & 0x7F; - break; - } + gain -= inf->gain_step > 0 ? inf->gain_min : inf->gain_max; + code = DIV_ROUND_CLOSEST(gain, inf->gain_step); mutex_lock(&st->lock); switch (mask) { case IIO_CHAN_INFO_HARDWAREGAIN: st->ch[chan->channel] = code; - ret = ad8366_write(indio_dev, st->ch[0], st->ch[1]); + ret = ad8366_write_code(st); break; default: ret = -EINVAL; @@ -241,10 +214,6 @@ static const struct iio_chan_spec ad8366_channels[] = { AD8366_CHAN(1), }; -static const struct iio_chan_spec ada4961_channels[] = { - AD8366_CHAN(0), -}; - static int ad8366_probe(struct spi_device *spi) { struct device *dev = &spi->dev; @@ -268,35 +237,20 @@ static int ad8366_probe(struct spi_device *spi) return dev_err_probe(dev, ret, "Failed to get regulator\n"); st->spi = spi; - st->type = spi_get_device_id(spi)->driver_data; - - switch (st->type) { - case ID_AD8366: - indio_dev->channels = ad8366_channels; - indio_dev->num_channels = ARRAY_SIZE(ad8366_channels); - break; - case ID_ADA4961: - case ID_ADL5240: - case ID_HMC792: - case ID_HMC1119: - indio_dev->channels = ada4961_channels; - indio_dev->num_channels = ARRAY_SIZE(ada4961_channels); - break; - default: - return dev_err_probe(dev, -EINVAL, "Invalid device ID\n"); - } + st->info = &ad8366_infos[spi_get_device_id(spi)->driver_data]; rstc = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL); if (IS_ERR(rstc)) return dev_err_probe(dev, PTR_ERR(rstc), "Failed to get reset controller\n"); - st->info = &ad8366_infos[st->type]; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->info = &ad8366_info; indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad8366_channels; + indio_dev->num_channels = st->info->num_channels; - ret = ad8366_write(indio_dev, 0, 0); + ret = ad8366_write_code(st); if (ret < 0) return dev_err_probe(dev, ret, "failed to write initial gain\n"); From d5e02d0d00b99bb37f935f250181bf310d3d6e85 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Mon, 16 Feb 2026 17:10:53 +0000 Subject: [PATCH 047/405] iio: amplifiers: ad8366: add device tree support Drop the enum ID, split chip info table into per-device structs and add of_match_table. Additionally, add 'name' field into the chip info struct, dropping the usage of spi_get_device_id(). Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/ad8366.c | 107 ++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 48 deletions(-) diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index 22eb6c9bb0f6..fb787a512bff 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -29,15 +29,8 @@ #include -enum ad8366_type { - ID_AD8366, - ID_ADA4961, - ID_ADL5240, - ID_HMC792, - ID_HMC1119, -}; - struct ad8366_info { + const char *name; int gain_min; int gain_max; int gain_step; @@ -68,38 +61,45 @@ static size_t ad8366_pack_code(const unsigned char *code, size_t num_channels, return sizeof(__be16); } -static const struct ad8366_info ad8366_infos[] = { - [ID_AD8366] = { - .gain_min = 4500, - .gain_max = 20500, - .gain_step = 253, - .num_channels = 2, - .pack_code = ad8366_pack_code, - }, - [ID_ADA4961] = { - .gain_min = -6000, - .gain_max = 15000, - .gain_step = -1000, - .num_channels = 1, - }, - [ID_ADL5240] = { - .gain_min = -11500, - .gain_max = 20000, - .gain_step = 500, - .num_channels = 1, - }, - [ID_HMC792] = { - .gain_min = -15750, - .gain_max = 0, - .gain_step = 250, - .num_channels = 1, - }, - [ID_HMC1119] = { - .gain_min = -31750, - .gain_max = 0, - .gain_step = -250, - .num_channels = 1, - }, +static const struct ad8366_info ad8366_chip_info = { + .name = "ad8366", + .gain_min = 4500, + .gain_max = 20500, + .gain_step = 253, + .num_channels = 2, + .pack_code = ad8366_pack_code, +}; + +static const struct ad8366_info ada4961_chip_info = { + .name = "ada4961", + .gain_min = -6000, + .gain_max = 15000, + .gain_step = -1000, + .num_channels = 1, +}; + +static const struct ad8366_info adl5240_chip_info = { + .name = "adl5240", + .gain_min = -11500, + .gain_max = 20000, + .gain_step = 500, + .num_channels = 1, +}; + +static const struct ad8366_info hmc792_chip_info = { + .name = "hmc792a", + .gain_min = -15750, + .gain_max = 0, + .gain_step = 250, + .num_channels = 1, +}; + +static const struct ad8366_info hmc1119_chip_info = { + .name = "hmc1119", + .gain_min = -31750, + .gain_max = 0, + .gain_step = -250, + .num_channels = 1, }; static int ad8366_write_code(struct ad8366_state *st) @@ -237,14 +237,14 @@ static int ad8366_probe(struct spi_device *spi) return dev_err_probe(dev, ret, "Failed to get regulator\n"); st->spi = spi; - st->info = &ad8366_infos[spi_get_device_id(spi)->driver_data]; + st->info = spi_get_device_match_data(spi); rstc = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL); if (IS_ERR(rstc)) return dev_err_probe(dev, PTR_ERR(rstc), "Failed to get reset controller\n"); - indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->name = st->info->name; indio_dev->info = &ad8366_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = ad8366_channels; @@ -258,18 +258,29 @@ static int ad8366_probe(struct spi_device *spi) } static const struct spi_device_id ad8366_id[] = { - {"ad8366", ID_AD8366}, - {"ada4961", ID_ADA4961}, - {"adl5240", ID_ADL5240}, - {"hmc792a", ID_HMC792}, - {"hmc1119", ID_HMC1119}, + { "ad8366", (kernel_ulong_t)&ad8366_chip_info }, + { "ada4961", (kernel_ulong_t)&ada4961_chip_info }, + { "adl5240", (kernel_ulong_t)&adl5240_chip_info }, + { "hmc792a", (kernel_ulong_t)&hmc792_chip_info }, + { "hmc1119", (kernel_ulong_t)&hmc1119_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad8366_id); +static const struct of_device_id ad8366_of_match[] = { + { .compatible = "adi,ad8366", .data = &ad8366_chip_info }, + { .compatible = "adi,ada4961", .data = &ada4961_chip_info }, + { .compatible = "adi,adl5240", .data = &adl5240_chip_info }, + { .compatible = "adi,hmc792a", .data = &hmc792_chip_info }, + { .compatible = "adi,hmc1119", .data = &hmc1119_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad8366_of_match); + static struct spi_driver ad8366_driver = { .driver = { - .name = KBUILD_MODNAME, + .name = KBUILD_MODNAME, + .of_match_table = ad8366_of_match, }, .probe = ad8366_probe, .id_table = ad8366_id, From d99a03d6dda43226fd3138e5cd89ddba739a11cf Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Mon, 16 Feb 2026 17:10:54 +0000 Subject: [PATCH 048/405] iio: amplifiers: ad8366: consume enable gpio Some parts may consume enable GPIO to enable serial mode (HMC1119's and HMC792A P/S pin) or powerup the device (e.g. ADA4961's PWUP pin). Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/ad8366.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index fb787a512bff..d4499af0518a 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -217,6 +217,7 @@ static const struct iio_chan_spec ad8366_channels[] = { static int ad8366_probe(struct spi_device *spi) { struct device *dev = &spi->dev; + struct gpio_desc *enable_gpio; struct reset_control *rstc; struct iio_dev *indio_dev; struct ad8366_state *st; @@ -239,6 +240,11 @@ static int ad8366_probe(struct spi_device *spi) st->spi = spi; st->info = spi_get_device_match_data(spi); + enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(enable_gpio)) + return dev_err_probe(dev, PTR_ERR(enable_gpio), + "Failed to get enable GPIO\n"); + rstc = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL); if (IS_ERR(rstc)) return dev_err_probe(dev, PTR_ERR(rstc), From 76878a3820b52ef463c2f63f107d37a8c6fe7bc6 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Mon, 16 Feb 2026 17:10:55 +0000 Subject: [PATCH 049/405] iio: amplifiers: ad8366: update device support Add support for the following digital step attenuators: - HMC271A: 1dB LSB 5-Bit Digital Attenuator SMT, 0.7 - 3.7 GHz - ADRF5720: 0.5 dB LSB, 6-Bit, Digital Attenuator, 9 kHz to 40 GHz - ADRF5730: 0.5 dB LSB, 6-Bit, Digital Attenuator, 100 MHz to 40 GHz - ADRF5731: 2 dB LSB, 4-Bit, Digital Attenuator, 100 MHz to 40 GHz - HMC1018A: 1.0 dB LSB GaAs MMIC 5-BIT DIGITAL ATTENUATOR, 0.1 - 30 GHz - HMC1019A: 0.5 dB LSB GaAs MMIC 5-BIT DIGITAL ATTENUATOR, 0.1 - 30 GHz Additionally, copyright notice was updated with current year. Co-developed-by: Alexandru Ardelean Signed-off-by: Alexandru Ardelean Co-developed-by: Michael Hennerich Signed-off-by: Michael Hennerich Reviewed-by: Andy Shevchenko Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/Kconfig | 6 +++ drivers/iio/amplifiers/ad8366.c | 84 ++++++++++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/drivers/iio/amplifiers/Kconfig b/drivers/iio/amplifiers/Kconfig index a8a604863eed..39d280d4d437 100644 --- a/drivers/iio/amplifiers/Kconfig +++ b/drivers/iio/amplifiers/Kconfig @@ -18,7 +18,13 @@ config AD8366 AD8366 Dual-Digital Variable Gain Amplifier (VGA) ADA4961 BiCMOS RF Digital Gain Amplifier (DGA) ADL5240 Digitally controlled variable gain amplifier (VGA) + ADRF5720: 0.5 dB LSB, 6-Bit, Silicon Digital Attenuator + ADRF5730: 0.5 dB LSB, 6-Bit, Silicon Digital Attenuator + ADRF5731: 2 dB LSB, 4-Bit, Silicon Digital Attenuator + HMC271A: 1dB LSB 5-Bit Digital Attenuator SMT HMC792A 0.25 dB LSB GaAs MMIC 6-Bit Digital Attenuator + HMC1018A: 1.0 dB LSB GaAs MMIC 5-BIT Digital Attenuator + HMC1019A: 0.5 dB LSB GaAs MMIC 5-BIT Digital Attenuator HMC1119 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator To compile this driver as a module, choose M here: the diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index d4499af0518a..334ca91c0f59 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -5,10 +5,16 @@ * AD8366 Dual-Digital Variable Gain Amplifier (VGA) * ADA4961 BiCMOS RF Digital Gain Amplifier (DGA) * ADL5240 Digitally controlled variable gain amplifier (VGA) + * ADRF5720: 0.5 dB LSB, 6-Bit, Silicon Digital Attenuator, 9 kHz to 40 GHz + * ADRF5730: 0.5 dB LSB, 6-Bit, Silicon Digital Attenuator, 100 MHz to 40 GHz + * ADRF5731: 2 dB LSB, 4-Bit, Silicon Digital Attenuator, 100 MHz to 40 GHz + * HMC271A: 1dB LSB 5-Bit Digital Attenuator SMT, 0.7 - 3.7 GHz * HMC792A 0.25 dB LSB GaAs MMIC 6-Bit Digital Attenuator + * HMC1018A: 1.0 dB LSB GaAs MMIC 5-BIT DIGITAL ATTENUATOR, 0.1 - 30 GHz + * HMC1019A: 0.5 dB LSB GaAs MMIC 5-BIT DIGITAL ATTENUATOR, 0.1 - 30 GHz * HMC1119 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator * - * Copyright 2012-2019 Analog Devices Inc. + * Copyright 2012-2026 Analog Devices Inc. */ #include @@ -61,6 +67,20 @@ static size_t ad8366_pack_code(const unsigned char *code, size_t num_channels, return sizeof(__be16); } +static size_t adrf5731_pack_code(const unsigned char *code, size_t num_channels, + unsigned char *data) +{ + data[0] = code[0] << 2; + return 1; +} + +static size_t hmc271_pack_code(const unsigned char *code, size_t num_channels, + unsigned char *data) +{ + data[0] = bitrev8(code[0]) >> 3; + return 1; +} + static const struct ad8366_info ad8366_chip_info = { .name = "ad8366", .gain_min = 4500, @@ -86,6 +106,40 @@ static const struct ad8366_info adl5240_chip_info = { .num_channels = 1, }; +static const struct ad8366_info adrf5720_chip_info = { + .name = "adrf5720", + .gain_min = -31500, + .gain_max = 0, + .gain_step = -500, + .num_channels = 1, +}; + +static const struct ad8366_info adrf5730_chip_info = { + .name = "adrf5730", + .gain_min = -31500, + .gain_max = 0, + .gain_step = -500, + .num_channels = 1, +}; + +static const struct ad8366_info adrf5731_chip_info = { + .name = "adrf5731", + .gain_min = -30000, + .gain_max = 0, + .gain_step = -2000, + .num_channels = 1, + .pack_code = adrf5731_pack_code, +}; + +static const struct ad8366_info hmc271_chip_info = { + .name = "hmc271a", + .gain_min = -31000, + .gain_max = 0, + .gain_step = 1000, + .num_channels = 1, + .pack_code = hmc271_pack_code, +}; + static const struct ad8366_info hmc792_chip_info = { .name = "hmc792a", .gain_min = -15750, @@ -94,6 +148,22 @@ static const struct ad8366_info hmc792_chip_info = { .num_channels = 1, }; +static const struct ad8366_info hmc1018_chip_info = { + .name = "hmc1018a", + .gain_min = -31000, + .gain_max = 0, + .gain_step = 1000, + .num_channels = 1, +}; + +static const struct ad8366_info hmc1019_chip_info = { + .name = "hmc1019a", + .gain_min = -15500, + .gain_max = 0, + .gain_step = 500, + .num_channels = 1, +}; + static const struct ad8366_info hmc1119_chip_info = { .name = "hmc1119", .gain_min = -31750, @@ -267,7 +337,13 @@ static const struct spi_device_id ad8366_id[] = { { "ad8366", (kernel_ulong_t)&ad8366_chip_info }, { "ada4961", (kernel_ulong_t)&ada4961_chip_info }, { "adl5240", (kernel_ulong_t)&adl5240_chip_info }, + { "adrf5720", (kernel_ulong_t)&adrf5720_chip_info }, + { "adrf5730", (kernel_ulong_t)&adrf5730_chip_info }, + { "adrf5731", (kernel_ulong_t)&adrf5731_chip_info }, + { "hmc271a", (kernel_ulong_t)&hmc271_chip_info }, { "hmc792a", (kernel_ulong_t)&hmc792_chip_info }, + { "hmc1018a", (kernel_ulong_t)&hmc1018_chip_info }, + { "hmc1019a", (kernel_ulong_t)&hmc1019_chip_info }, { "hmc1119", (kernel_ulong_t)&hmc1119_chip_info }, { } }; @@ -277,7 +353,13 @@ static const struct of_device_id ad8366_of_match[] = { { .compatible = "adi,ad8366", .data = &ad8366_chip_info }, { .compatible = "adi,ada4961", .data = &ada4961_chip_info }, { .compatible = "adi,adl5240", .data = &adl5240_chip_info }, + { .compatible = "adi,adrf5720", .data = &adrf5720_chip_info }, + { .compatible = "adi,adrf5730", .data = &adrf5730_chip_info }, + { .compatible = "adi,adrf5731", .data = &adrf5731_chip_info }, + { .compatible = "adi,hmc271a", .data = &hmc271_chip_info }, { .compatible = "adi,hmc792a", .data = &hmc792_chip_info }, + { .compatible = "adi,hmc1018a", .data = &hmc1018_chip_info }, + { .compatible = "adi,hmc1019a", .data = &hmc1019_chip_info }, { .compatible = "adi,hmc1119", .data = &hmc1119_chip_info }, { } }; From 70a9ae59c5b1f2f5501e78e2d85bfeefd153f854 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 2 Feb 2026 13:57:30 +0200 Subject: [PATCH 050/405] iio: adc: at91_adc: change at91_ts_sample to return void The return value of at91_ts_sample() is never checked by its caller. Change the return type to void to make this explicit. The error conditions are already logged via dev_err() which provides sufficient visibility into hardware issues. Signed-off-by: Antoniu Miclaus Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91_adc.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 920dd9ffd27a..8942d15b3978 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -304,7 +304,7 @@ static void handle_adc_eoc_trigger(int irq, struct iio_dev *idev) } } -static int at91_ts_sample(struct iio_dev *idev) +static void at91_ts_sample(struct iio_dev *idev) { struct at91_adc_state *st = iio_priv(idev); unsigned int xscale, yscale, reg, z1, z2; @@ -323,7 +323,7 @@ static int at91_ts_sample(struct iio_dev *idev) xscale = (reg >> 16) & xyz_mask; if (xscale == 0) { dev_err(&idev->dev, "Error: xscale == 0!\n"); - return -1; + return; } x /= xscale; @@ -334,7 +334,7 @@ static int at91_ts_sample(struct iio_dev *idev) yscale = (reg >> 16) & xyz_mask; if (yscale == 0) { dev_err(&idev->dev, "Error: yscale == 0!\n"); - return -1; + return; } y /= yscale; @@ -363,8 +363,6 @@ static int at91_ts_sample(struct iio_dev *idev) } else { dev_dbg(&idev->dev, "pressure too low: not reporting\n"); } - - return 0; } static irqreturn_t at91_adc_rl_interrupt(int irq, void *private) From 34be156c88080ce1a58d44e409e3ac41ced572c1 Mon Sep 17 00:00:00 2001 From: Ethan Tidmore Date: Sun, 22 Feb 2026 21:40:11 -0600 Subject: [PATCH 051/405] iio: light: gp2ap020a00f: simplify locking with guard() Use the guard() cleanup handler to manage the device lock. This simplifies the code by removing the need for manual unlocking and goto error handling paths. Suggested-by: Jonathan Cameron Signed-off-by: Ethan Tidmore Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap020a00f.c | 110 ++++++++++--------------------- 1 file changed, 35 insertions(+), 75 deletions(-) diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index c7df4b258e2c..7cee15db3a1a 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -31,6 +31,7 @@ * the other one. */ +#include #include #include #include @@ -1024,17 +1025,13 @@ static int gp2ap020a00f_write_event_val(struct iio_dev *indio_dev, bool event_en = false; u8 thresh_val_id; u8 thresh_reg_l; - int err = 0; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); thresh_reg_l = gp2ap020a00f_get_thresh_reg(chan, dir); thresh_val_id = GP2AP020A00F_THRESH_VAL_ID(thresh_reg_l); - - if (thresh_val_id > GP2AP020A00F_THRESH_PH) { - err = -EINVAL; - goto error_unlock; - } + if (thresh_val_id > GP2AP020A00F_THRESH_PH) + return -EINVAL; switch (thresh_reg_l) { case GP2AP020A00F_TH_L_REG: @@ -1046,30 +1043,23 @@ static int gp2ap020a00f_write_event_val(struct iio_dev *indio_dev, &data->flags); break; case GP2AP020A00F_PH_L_REG: - if (val == 0) { - err = -EINVAL; - goto error_unlock; - } + if (val == 0) + return -EINVAL; + event_en = test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags); break; case GP2AP020A00F_PL_L_REG: - if (val == 0) { - err = -EINVAL; - goto error_unlock; - } + if (val == 0) + return -EINVAL; + event_en = test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags); break; } data->thresh_val[thresh_val_id] = val; - err = gp2ap020a00f_write_event_threshold(data, thresh_val_id, - event_en); -error_unlock: - mutex_unlock(&data->lock); - - return err; + return gp2ap020a00f_write_event_threshold(data, thresh_val_id, event_en); } static int gp2ap020a00f_read_event_val(struct iio_dev *indio_dev, @@ -1081,23 +1071,16 @@ static int gp2ap020a00f_read_event_val(struct iio_dev *indio_dev, { struct gp2ap020a00f_data *data = iio_priv(indio_dev); u8 thresh_reg_l; - int err = IIO_VAL_INT; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); thresh_reg_l = gp2ap020a00f_get_thresh_reg(chan, dir); - - if (thresh_reg_l > GP2AP020A00F_PH_L_REG) { - err = -EINVAL; - goto error_unlock; - } + if (thresh_reg_l > GP2AP020A00F_PH_L_REG) + return -EINVAL; *val = data->thresh_val[GP2AP020A00F_THRESH_VAL_ID(thresh_reg_l)]; -error_unlock: - mutex_unlock(&data->lock); - - return err; + return IIO_VAL_INT; } static int gp2ap020a00f_write_prox_event_config(struct iio_dev *indio_dev, @@ -1163,32 +1146,25 @@ static int gp2ap020a00f_write_event_config(struct iio_dev *indio_dev, { struct gp2ap020a00f_data *data = iio_priv(indio_dev); enum gp2ap020a00f_cmd cmd; - int err; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); switch (chan->type) { case IIO_PROXIMITY: - err = gp2ap020a00f_write_prox_event_config(indio_dev, state); - break; + return gp2ap020a00f_write_prox_event_config(indio_dev, state); case IIO_LIGHT: if (dir == IIO_EV_DIR_RISING) { cmd = state ? GP2AP020A00F_CMD_ALS_HIGH_EV_EN : GP2AP020A00F_CMD_ALS_HIGH_EV_DIS; - err = gp2ap020a00f_exec_cmd(data, cmd); + return gp2ap020a00f_exec_cmd(data, cmd); } else { cmd = state ? GP2AP020A00F_CMD_ALS_LOW_EV_EN : GP2AP020A00F_CMD_ALS_LOW_EV_DIS; - err = gp2ap020a00f_exec_cmd(data, cmd); + return gp2ap020a00f_exec_cmd(data, cmd); } - break; default: - err = -EINVAL; + return -EINVAL; } - - mutex_unlock(&data->lock); - - return err; } static int gp2ap020a00f_read_event_config(struct iio_dev *indio_dev, @@ -1197,35 +1173,23 @@ static int gp2ap020a00f_read_event_config(struct iio_dev *indio_dev, enum iio_event_direction dir) { struct gp2ap020a00f_data *data = iio_priv(indio_dev); - int event_en = 0; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); switch (chan->type) { case IIO_PROXIMITY: if (dir == IIO_EV_DIR_RISING) - event_en = test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, - &data->flags); + return test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags); else - event_en = test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, - &data->flags); - break; + return test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags); case IIO_LIGHT: if (dir == IIO_EV_DIR_RISING) - event_en = test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, - &data->flags); + return test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags); else - event_en = test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, - &data->flags); - break; + return test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags); default: - event_en = -EINVAL; - break; + return -EINVAL; } - - mutex_unlock(&data->lock); - - return event_en; } static int gp2ap020a00f_read_channel(struct gp2ap020a00f_data *data, @@ -1385,7 +1349,7 @@ static int gp2ap020a00f_buffer_postenable(struct iio_dev *indio_dev) struct gp2ap020a00f_data *data = iio_priv(indio_dev); int i, err = 0; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); /* * Enable triggers according to the scan_mask. Enabling either @@ -1413,16 +1377,13 @@ static int gp2ap020a00f_buffer_postenable(struct iio_dev *indio_dev) } if (err < 0) - goto error_unlock; + return err; data->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); if (!data->buffer) - err = -ENOMEM; + return -ENOMEM; -error_unlock: - mutex_unlock(&data->lock); - - return err; + return 0; } static int gp2ap020a00f_buffer_predisable(struct iio_dev *indio_dev) @@ -1430,7 +1391,7 @@ static int gp2ap020a00f_buffer_predisable(struct iio_dev *indio_dev) struct gp2ap020a00f_data *data = iio_priv(indio_dev); int i, err = 0; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); iio_for_each_active_channel(indio_dev, i) { switch (i) { @@ -1449,12 +1410,11 @@ static int gp2ap020a00f_buffer_predisable(struct iio_dev *indio_dev) } } - if (err == 0) - kfree(data->buffer); + if (err) + return err; - mutex_unlock(&data->lock); - - return err; + kfree(data->buffer); + return 0; } static const struct iio_buffer_setup_ops gp2ap020a00f_buffer_setup_ops = { From b969e76585e9cc3ce0df52e8290841f132a5ea6f Mon Sep 17 00:00:00 2001 From: Ethan Tidmore Date: Sun, 22 Feb 2026 21:40:12 -0600 Subject: [PATCH 052/405] iio: light: gp2ap020a00f: correct return type to int The function gp2ap020a00f_get_thresh_reg() can return -EINVAL in its error path. Yet, the function has return type of u8. Added error checking for gp2ap020a00f_get_thresh_reg() return value. Detected by Smatch: drivers/iio/light/gp2ap020a00f.c:1013 gp2ap020a00f_get_thresh_reg() warn: signedness bug returning '(-22)' Reviewed-by: Andy Shevchenko Signed-off-by: Ethan Tidmore Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap020a00f.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 7cee15db3a1a..6fe4486101b8 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -993,8 +993,8 @@ static irqreturn_t gp2ap020a00f_trigger_handler(int irq, void *data) return IRQ_HANDLED; } -static u8 gp2ap020a00f_get_thresh_reg(const struct iio_chan_spec *chan, - enum iio_event_direction event_dir) +static int gp2ap020a00f_get_thresh_reg(const struct iio_chan_spec *chan, + enum iio_event_direction event_dir) { switch (chan->type) { case IIO_PROXIMITY: @@ -1024,11 +1024,14 @@ static int gp2ap020a00f_write_event_val(struct iio_dev *indio_dev, struct gp2ap020a00f_data *data = iio_priv(indio_dev); bool event_en = false; u8 thresh_val_id; - u8 thresh_reg_l; + int thresh_reg_l; guard(mutex)(&data->lock); thresh_reg_l = gp2ap020a00f_get_thresh_reg(chan, dir); + if (thresh_reg_l < 0) + return thresh_reg_l; + thresh_val_id = GP2AP020A00F_THRESH_VAL_ID(thresh_reg_l); if (thresh_val_id > GP2AP020A00F_THRESH_PH) return -EINVAL; @@ -1070,11 +1073,13 @@ static int gp2ap020a00f_read_event_val(struct iio_dev *indio_dev, int *val, int *val2) { struct gp2ap020a00f_data *data = iio_priv(indio_dev); - u8 thresh_reg_l; + int thresh_reg_l; guard(mutex)(&data->lock); thresh_reg_l = gp2ap020a00f_get_thresh_reg(chan, dir); + if (thresh_reg_l < 0) + return thresh_reg_l; if (thresh_reg_l > GP2AP020A00F_PH_L_REG) return -EINVAL; From c579a6a7a7454b2de83a1f3841a60fd20ec9ef5c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 22 Feb 2026 21:40:13 -0600 Subject: [PATCH 053/405] iio: light: gp2ap020a00f: Use correct types for 16-bit LE data Instead of using byte arrays and then explicit castings, change the types of byte arrays to be __le16 and update the endianness conversions accordingly. Signed-off-by: Andy Shevchenko Signed-off-by: Ethan Tidmore Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap020a00f.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 6fe4486101b8..8558c4c3ef7a 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -462,7 +462,7 @@ static int gp2ap020a00f_write_event_threshold(struct gp2ap020a00f_data *data, return regmap_bulk_write(data->regmap, GP2AP020A00F_THRESH_REG(th_val_id), - (u8 *)&thresh_buf, 2); + &thresh_buf, sizeof(thresh_buf)); } static int gp2ap020a00f_alter_opmode(struct gp2ap020a00f_data *data, @@ -698,18 +698,18 @@ static int wait_conversion_complete_irq(struct gp2ap020a00f_data *data) static int gp2ap020a00f_read_output(struct gp2ap020a00f_data *data, unsigned int output_reg, int *val) { - u8 reg_buf[2]; + __le16 reg_buf; int err; err = wait_conversion_complete_irq(data); if (err < 0) dev_dbg(&data->client->dev, "data ready timeout\n"); - err = regmap_bulk_read(data->regmap, output_reg, reg_buf, 2); + err = regmap_bulk_read(data->regmap, output_reg, ®_buf, sizeof(reg_buf)); if (err < 0) return err; - *val = le16_to_cpup((__le16 *)reg_buf); + *val = le16_to_cpu(reg_buf); return err; } @@ -867,8 +867,9 @@ static irqreturn_t gp2ap020a00f_thresh_event_handler(int irq, void *data) { struct iio_dev *indio_dev = data; struct gp2ap020a00f_data *priv = iio_priv(indio_dev); - u8 op_reg_flags, d0_reg_buf[2]; unsigned int output_val, op_reg_val; + __le16 d0_reg_buf; + u8 op_reg_flags; int thresh_val_id, ret; /* Read interrupt flags */ @@ -896,11 +897,11 @@ static irqreturn_t gp2ap020a00f_thresh_event_handler(int irq, void *data) * transition is required. */ ret = regmap_bulk_read(priv->regmap, GP2AP020A00F_D0_L_REG, - d0_reg_buf, 2); + &d0_reg_buf, sizeof(d0_reg_buf)); if (ret < 0) goto done; - output_val = le16_to_cpup((__le16 *)d0_reg_buf); + output_val = le16_to_cpu(d0_reg_buf); if (gp2ap020a00f_adjust_lux_mode(priv, output_val)) goto done; @@ -967,17 +968,15 @@ static irqreturn_t gp2ap020a00f_trigger_handler(int irq, void *data) int i, out_val, ret; iio_for_each_active_channel(indio_dev, i) { - ret = regmap_bulk_read(priv->regmap, - GP2AP020A00F_DATA_REG(i), - &priv->buffer[d_size], 2); + ret = regmap_bulk_read(priv->regmap, GP2AP020A00F_DATA_REG(i), + &priv->buffer[d_size], 2); if (ret < 0) goto done; if (i == GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR || i == GP2AP020A00F_SCAN_MODE_LIGHT_IR) { - out_val = le16_to_cpup((__le16 *)&priv->buffer[d_size]); + out_val = get_unaligned_le16(&priv->buffer[d_size]); gp2ap020a00f_output_to_lux(priv, &out_val); - put_unaligned_le32(out_val, &priv->buffer[d_size]); d_size += 4; } else { From f6d460ec01556d8c5871a0abbb5baa31f8b0d630 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 22 Feb 2026 21:40:14 -0600 Subject: [PATCH 054/405] iio: light: gp2ap020a00f: Return directly from the switch cases Return directly from the switch cases which makes code easier to follow. In some cases convert pieces to the standard pattern which also unifies it with the accepted kernel practices. Signed-off-by: Andy Shevchenko Signed-off-by: Ethan Tidmore Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap020a00f.c | 60 +++++++++++++------------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 8558c4c3ef7a..6b283b52698f 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -494,27 +494,24 @@ static int gp2ap020a00f_alter_opmode(struct gp2ap020a00f_data *data, static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, enum gp2ap020a00f_cmd cmd) { - int err = 0; + int err; switch (cmd) { case GP2AP020A00F_CMD_READ_RAW_CLEAR: if (data->cur_opmode != GP2AP020A00F_OPMODE_SHUTDOWN) return -EBUSY; - err = gp2ap020a00f_set_operation_mode(data, + return gp2ap020a00f_set_operation_mode(data, GP2AP020A00F_OPMODE_READ_RAW_CLEAR); - break; case GP2AP020A00F_CMD_READ_RAW_IR: if (data->cur_opmode != GP2AP020A00F_OPMODE_SHUTDOWN) return -EBUSY; - err = gp2ap020a00f_set_operation_mode(data, + return gp2ap020a00f_set_operation_mode(data, GP2AP020A00F_OPMODE_READ_RAW_IR); - break; case GP2AP020A00F_CMD_READ_RAW_PROXIMITY: if (data->cur_opmode != GP2AP020A00F_OPMODE_SHUTDOWN) return -EBUSY; - err = gp2ap020a00f_set_operation_mode(data, + return gp2ap020a00f_set_operation_mode(data, GP2AP020A00F_OPMODE_READ_RAW_PROXIMITY); - break; case GP2AP020A00F_CMD_TRIGGER_CLEAR_EN: if (data->cur_opmode == GP2AP020A00F_OPMODE_PROX_DETECT) return -EBUSY; @@ -522,16 +519,17 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, err = gp2ap020a00f_alter_opmode(data, GP2AP020A00F_OPMODE_ALS, GP2AP020A00F_ADD_MODE); + else + err = 0; set_bit(GP2AP020A00F_FLAG_ALS_CLEAR_TRIGGER, &data->flags); - break; + return err; case GP2AP020A00F_CMD_TRIGGER_CLEAR_DIS: clear_bit(GP2AP020A00F_FLAG_ALS_CLEAR_TRIGGER, &data->flags); if (gp2ap020a00f_als_enabled(data)) break; - err = gp2ap020a00f_alter_opmode(data, + return gp2ap020a00f_alter_opmode(data, GP2AP020A00F_OPMODE_ALS, GP2AP020A00F_SUBTRACT_MODE); - break; case GP2AP020A00F_CMD_TRIGGER_IR_EN: if (data->cur_opmode == GP2AP020A00F_OPMODE_PROX_DETECT) return -EBUSY; @@ -539,16 +537,17 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, err = gp2ap020a00f_alter_opmode(data, GP2AP020A00F_OPMODE_ALS, GP2AP020A00F_ADD_MODE); + else + err = 0; set_bit(GP2AP020A00F_FLAG_ALS_IR_TRIGGER, &data->flags); - break; + return err; case GP2AP020A00F_CMD_TRIGGER_IR_DIS: clear_bit(GP2AP020A00F_FLAG_ALS_IR_TRIGGER, &data->flags); if (gp2ap020a00f_als_enabled(data)) break; - err = gp2ap020a00f_alter_opmode(data, + return gp2ap020a00f_alter_opmode(data, GP2AP020A00F_OPMODE_ALS, GP2AP020A00F_SUBTRACT_MODE); - break; case GP2AP020A00F_CMD_TRIGGER_PROX_EN: if (data->cur_opmode == GP2AP020A00F_OPMODE_PROX_DETECT) return -EBUSY; @@ -556,13 +555,12 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, GP2AP020A00F_OPMODE_PS, GP2AP020A00F_ADD_MODE); set_bit(GP2AP020A00F_FLAG_PROX_TRIGGER, &data->flags); - break; + return err; case GP2AP020A00F_CMD_TRIGGER_PROX_DIS: clear_bit(GP2AP020A00F_FLAG_PROX_TRIGGER, &data->flags); - err = gp2ap020a00f_alter_opmode(data, + return gp2ap020a00f_alter_opmode(data, GP2AP020A00F_OPMODE_PS, GP2AP020A00F_SUBTRACT_MODE); - break; case GP2AP020A00F_CMD_ALS_HIGH_EV_EN: if (test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags)) return 0; @@ -576,9 +574,8 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, return err; } set_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags); - err = gp2ap020a00f_write_event_threshold(data, + return gp2ap020a00f_write_event_threshold(data, GP2AP020A00F_THRESH_TH, true); - break; case GP2AP020A00F_CMD_ALS_HIGH_EV_DIS: if (!test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags)) return 0; @@ -590,9 +587,8 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, if (err < 0) return err; } - err = gp2ap020a00f_write_event_threshold(data, + return gp2ap020a00f_write_event_threshold(data, GP2AP020A00F_THRESH_TH, false); - break; case GP2AP020A00F_CMD_ALS_LOW_EV_EN: if (test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags)) return 0; @@ -606,9 +602,8 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, return err; } set_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags); - err = gp2ap020a00f_write_event_threshold(data, + return gp2ap020a00f_write_event_threshold(data, GP2AP020A00F_THRESH_TL, true); - break; case GP2AP020A00F_CMD_ALS_LOW_EV_DIS: if (!test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags)) return 0; @@ -620,9 +615,8 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, if (err < 0) return err; } - err = gp2ap020a00f_write_event_threshold(data, + return gp2ap020a00f_write_event_threshold(data, GP2AP020A00F_THRESH_TL, false); - break; case GP2AP020A00F_CMD_PROX_HIGH_EV_EN: if (test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags)) return 0; @@ -636,9 +630,8 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, return err; } set_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags); - err = gp2ap020a00f_write_event_threshold(data, + return gp2ap020a00f_write_event_threshold(data, GP2AP020A00F_THRESH_PH, true); - break; case GP2AP020A00F_CMD_PROX_HIGH_EV_DIS: if (!test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags)) return 0; @@ -647,9 +640,8 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, GP2AP020A00F_OPMODE_SHUTDOWN); if (err < 0) return err; - err = gp2ap020a00f_write_event_threshold(data, + return gp2ap020a00f_write_event_threshold(data, GP2AP020A00F_THRESH_PH, false); - break; case GP2AP020A00F_CMD_PROX_LOW_EV_EN: if (test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags)) return 0; @@ -663,9 +655,8 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, return err; } set_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags); - err = gp2ap020a00f_write_event_threshold(data, + return gp2ap020a00f_write_event_threshold(data, GP2AP020A00F_THRESH_PL, true); - break; case GP2AP020A00F_CMD_PROX_LOW_EV_DIS: if (!test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags)) return 0; @@ -674,12 +665,11 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, GP2AP020A00F_OPMODE_SHUTDOWN); if (err < 0) return err; - err = gp2ap020a00f_write_event_threshold(data, + return gp2ap020a00f_write_event_threshold(data, GP2AP020A00F_THRESH_PL, false); - break; } - return err; + return 0; } static int wait_conversion_complete_irq(struct gp2ap020a00f_data *data) @@ -1007,10 +997,8 @@ static int gp2ap020a00f_get_thresh_reg(const struct iio_chan_spec *chan, else return GP2AP020A00F_TL_L_REG; default: - break; + return -EINVAL; } - - return -EINVAL; } static int gp2ap020a00f_write_event_val(struct iio_dev *indio_dev, From 550f5c010465b6bf735264cd11d27e1f258508b1 Mon Sep 17 00:00:00 2001 From: Ethan Tidmore Date: Sun, 22 Feb 2026 21:40:15 -0600 Subject: [PATCH 055/405] iio: light: gp2ap020a00f: Fix possible error swallow Move error check into for loop in gp2ap020a00f_buffer_postenable() and gp2ap020a00f_buffer_predisable(), this fixes a possible error swallow. Suggested-by: Andy Shevchenko Signed-off-by: Ethan Tidmore Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap020a00f.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 6b283b52698f..64be9b8ca5e3 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -1339,7 +1339,7 @@ static const struct iio_info gp2ap020a00f_info = { static int gp2ap020a00f_buffer_postenable(struct iio_dev *indio_dev) { struct gp2ap020a00f_data *data = iio_priv(indio_dev); - int i, err = 0; + int i, err; guard(mutex)(&data->lock); @@ -1365,12 +1365,14 @@ static int gp2ap020a00f_buffer_postenable(struct iio_dev *indio_dev) err = gp2ap020a00f_exec_cmd(data, GP2AP020A00F_CMD_TRIGGER_PROX_EN); break; + default: + err = -EINVAL; + break; } + if (err) + return err; } - if (err < 0) - return err; - data->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); if (!data->buffer) return -ENOMEM; @@ -1381,7 +1383,7 @@ static int gp2ap020a00f_buffer_postenable(struct iio_dev *indio_dev) static int gp2ap020a00f_buffer_predisable(struct iio_dev *indio_dev) { struct gp2ap020a00f_data *data = iio_priv(indio_dev); - int i, err = 0; + int i, err; guard(mutex)(&data->lock); @@ -1399,12 +1401,14 @@ static int gp2ap020a00f_buffer_predisable(struct iio_dev *indio_dev) err = gp2ap020a00f_exec_cmd(data, GP2AP020A00F_CMD_TRIGGER_PROX_DIS); break; + default: + err = -EINVAL; + break; } + if (err) + return err; } - if (err) - return err; - kfree(data->buffer); return 0; } From 7b9e5e513915c99818780b2bb77c24da9d71a904 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 22 Feb 2026 21:40:16 -0600 Subject: [PATCH 056/405] iio: light: gp2ap020a00f: Replace custom implementation of min() Replace custom implementation of min() to save a few lines of code. Signed-off-by: Andy Shevchenko Signed-off-by: Ethan Tidmore Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap020a00f.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 64be9b8ca5e3..d41b3135b177 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,7 @@ #include #include #include + #include #include #include @@ -454,9 +456,7 @@ static int gp2ap020a00f_write_event_threshold(struct gp2ap020a00f_data *data, */ thresh_reg_val = data->thresh_val[th_val_id] / 16; else - thresh_reg_val = data->thresh_val[th_val_id] > 16000 ? - 16000 : - data->thresh_val[th_val_id]; + thresh_reg_val = min(data->thresh_val[th_val_id], 16000U); thresh_buf = cpu_to_le16(thresh_reg_val); From 7b53652c8c4df0b0f48f1f54be896c959730b11a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 22 Feb 2026 21:40:17 -0600 Subject: [PATCH 057/405] iio: light: gp2ap020a00f: Use temporary variable for struct device Use temporary variable for struct device to make code neater. Signed-off-by: Andy Shevchenko Signed-off-by: Ethan Tidmore Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap020a00f.c | 34 +++++++++++++++----------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index d41b3135b177..2b24771e87bb 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -1187,6 +1187,7 @@ static int gp2ap020a00f_read_event_config(struct iio_dev *indio_dev, static int gp2ap020a00f_read_channel(struct gp2ap020a00f_data *data, struct iio_chan_spec const *chan, int *val) { + struct device *dev = &data->client->dev; enum gp2ap020a00f_cmd cmd; int err; @@ -1206,27 +1207,23 @@ static int gp2ap020a00f_read_channel(struct gp2ap020a00f_data *data, err = gp2ap020a00f_exec_cmd(data, cmd); if (err < 0) { - dev_err(&data->client->dev, - "gp2ap020a00f_exec_cmd failed\n"); - goto error_ret; + dev_err(dev, "gp2ap020a00f_exec_cmd failed\n"); + return err; } err = gp2ap020a00f_read_output(data, chan->address, val); if (err < 0) - dev_err(&data->client->dev, - "gp2ap020a00f_read_output failed\n"); + dev_err(dev, "gp2ap020a00f_read_output failed\n"); err = gp2ap020a00f_set_operation_mode(data, GP2AP020A00F_OPMODE_SHUTDOWN); if (err < 0) - dev_err(&data->client->dev, - "Failed to shut down the device.\n"); + dev_err(dev, "Failed to shut down the device.\n"); if (cmd == GP2AP020A00F_CMD_READ_RAW_CLEAR || cmd == GP2AP020A00F_CMD_READ_RAW_IR) gp2ap020a00f_output_to_lux(data, val); -error_ret: return err; } @@ -1421,18 +1418,19 @@ static const struct iio_buffer_setup_ops gp2ap020a00f_buffer_setup_ops = { static int gp2ap020a00f_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct device *dev = &client->dev; struct gp2ap020a00f_data *data; struct iio_dev *indio_dev; struct regmap *regmap; int err; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; data = iio_priv(indio_dev); - data->vled_reg = devm_regulator_get(&client->dev, "vled"); + data->vled_reg = devm_regulator_get(dev, "vled"); if (IS_ERR(data->vled_reg)) return PTR_ERR(data->vled_reg); @@ -1442,7 +1440,7 @@ static int gp2ap020a00f_probe(struct i2c_client *client) regmap = devm_regmap_init_i2c(client, &gp2ap020a00f_regmap_config); if (IS_ERR(regmap)) { - dev_err(&client->dev, "Regmap initialization failed.\n"); + dev_err(dev, "Regmap initialization failed.\n"); err = PTR_ERR(regmap); goto error_regulator_disable; } @@ -1453,7 +1451,7 @@ static int gp2ap020a00f_probe(struct i2c_client *client) ARRAY_SIZE(gp2ap020a00f_reg_init_tab)); if (err < 0) { - dev_err(&client->dev, "Device initialization failed.\n"); + dev_err(dev, "Device initialization failed.\n"); goto error_regulator_disable; } @@ -1478,11 +1476,10 @@ static int gp2ap020a00f_probe(struct i2c_client *client) goto error_regulator_disable; /* Allocate trigger */ - data->trig = devm_iio_trigger_alloc(&client->dev, "%s-trigger", - indio_dev->name); + data->trig = devm_iio_trigger_alloc(dev, "%s-trigger", indio_dev->name); if (data->trig == NULL) { err = -ENOMEM; - dev_err(&indio_dev->dev, "Failed to allocate iio trigger.\n"); + dev_err(dev, "Failed to allocate iio trigger.\n"); goto error_uninit_buffer; } @@ -1494,7 +1491,7 @@ static int gp2ap020a00f_probe(struct i2c_client *client) "gp2ap020a00f_als_event", indio_dev); if (err < 0) { - dev_err(&client->dev, "Irq request failed.\n"); + dev_err(dev, "Irq request failed.\n"); goto error_uninit_buffer; } @@ -1502,7 +1499,7 @@ static int gp2ap020a00f_probe(struct i2c_client *client) err = iio_trigger_register(data->trig); if (err < 0) { - dev_err(&client->dev, "Failed to register iio trigger.\n"); + dev_err(dev, "Failed to register iio trigger.\n"); goto error_free_irq; } @@ -1528,12 +1525,13 @@ static void gp2ap020a00f_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); struct gp2ap020a00f_data *data = iio_priv(indio_dev); + struct device *dev = &client->dev; int err; err = gp2ap020a00f_set_operation_mode(data, GP2AP020A00F_OPMODE_SHUTDOWN); if (err < 0) - dev_err(&indio_dev->dev, "Failed to power off the device.\n"); + dev_err(dev, "Failed to power off the device.\n"); iio_device_unregister(indio_dev); iio_trigger_unregister(data->trig); From b047a2bc7a08247f603316060a1fce7b556207b9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 22 Feb 2026 21:40:18 -0600 Subject: [PATCH 058/405] iio: light: gp2ap020a00f: Explicitly use string literal for driver name The driver name should be easily greppable and clearly spelled. Replace a level of indirection and explicitly use string literal. While at it, remove useless blank lines before module_*() and MODULE_*() macros. Signed-off-by: Andy Shevchenko Signed-off-by: Ethan Tidmore Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap020a00f.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 2b24771e87bb..ad887b9547e4 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -55,8 +55,6 @@ #include #include -#define GP2A_I2C_NAME "gp2ap020a00f" - /* Registers */ #define GP2AP020A00F_OP_REG 0x00 /* Basic operations */ #define GP2AP020A00F_ALS_REG 0x01 /* ALS related settings */ @@ -1541,10 +1539,9 @@ static void gp2ap020a00f_remove(struct i2c_client *client) } static const struct i2c_device_id gp2ap020a00f_id[] = { - { GP2A_I2C_NAME }, + { "gp2ap020a00f" }, { } }; - MODULE_DEVICE_TABLE(i2c, gp2ap020a00f_id); static const struct of_device_id gp2ap020a00f_of_match[] = { @@ -1555,14 +1552,13 @@ MODULE_DEVICE_TABLE(of, gp2ap020a00f_of_match); static struct i2c_driver gp2ap020a00f_driver = { .driver = { - .name = GP2A_I2C_NAME, + .name = "gp2ap020a00f", .of_match_table = gp2ap020a00f_of_match, }, .probe = gp2ap020a00f_probe, .remove = gp2ap020a00f_remove, .id_table = gp2ap020a00f_id, }; - module_i2c_driver(gp2ap020a00f_driver); MODULE_AUTHOR("Jacek Anaszewski "); From 2eb3741b62200d430f1aba258d0653851e03ce46 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 22 Feb 2026 21:40:19 -0600 Subject: [PATCH 059/405] iio: light: gp2ap020a00f: Remove trailing comma in termination entry Termination entry by definition should be the last one, hence remove stray comma after it. Signed-off-by: Andy Shevchenko Signed-off-by: Ethan Tidmore Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap020a00f.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index ad887b9547e4..4ac7e30aec03 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -195,7 +195,7 @@ enum gp2ap020a00f_opmode { GP2AP020A00F_OPMODE_ALS_AND_PS, GP2AP020A00F_OPMODE_PROX_DETECT, GP2AP020A00F_OPMODE_SHUTDOWN, - GP2AP020A00F_NUM_OPMODES, + GP2AP020A00F_NUM_OPMODES }; enum gp2ap020a00f_cmd { From 06cdcd389ec464d42efa60fd096e91da6931773e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 22 Feb 2026 21:40:20 -0600 Subject: [PATCH 060/405] iio: light: gp2ap020a00f: Join some lines of code to be a single line In some cases the wrapped lines are harder to follow. Join them despite being longer than 80 characters in some cases. Signed-off-by: Andy Shevchenko Signed-off-by: Ethan Tidmore Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap020a00f.c | 39 +++++++++++--------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 4ac7e30aec03..76147b6d14e8 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -175,10 +175,8 @@ #define GP2AP020A00F_CHAN_TIMESTAMP 3 #define GP2AP020A00F_DATA_READY_TIMEOUT msecs_to_jiffies(1000) -#define GP2AP020A00F_DATA_REG(chan) (GP2AP020A00F_D0_L_REG + \ - (chan) * 2) -#define GP2AP020A00F_THRESH_REG(th_val_id) (GP2AP020A00F_TL_L_REG + \ - (th_val_id) * 2) +#define GP2AP020A00F_DATA_REG(chan) (GP2AP020A00F_D0_L_REG + (chan) * 2) +#define GP2AP020A00F_THRESH_REG(th_val_id) (GP2AP020A00F_TL_L_REG + (th_val_id) * 2) #define GP2AP020A00F_THRESH_VAL_ID(reg_addr) ((reg_addr - 4) / 2) #define GP2AP020A00F_SUBTRACT_MODE 0 @@ -390,20 +388,17 @@ static int gp2ap020a00f_set_operation_mode(struct gp2ap020a00f_data *data, } err = regmap_update_bits(data->regmap, GP2AP020A00F_ALS_REG, - GP2AP020A00F_PRST_MASK, opmode_regs_settings[op] - .als_reg); + GP2AP020A00F_PRST_MASK, opmode_regs_settings[op].als_reg); if (err < 0) return err; err = regmap_update_bits(data->regmap, GP2AP020A00F_PS_REG, - GP2AP020A00F_INTTYPE_MASK, opmode_regs_settings[op] - .ps_reg); + GP2AP020A00F_INTTYPE_MASK, opmode_regs_settings[op].ps_reg); if (err < 0) return err; err = regmap_update_bits(data->regmap, GP2AP020A00F_LED_REG, - GP2AP020A00F_PIN_MASK, opmode_regs_settings[op] - .led_reg); + GP2AP020A00F_PIN_MASK, opmode_regs_settings[op].led_reg); if (err < 0) return err; } @@ -861,8 +856,7 @@ static irqreturn_t gp2ap020a00f_thresh_event_handler(int irq, void *data) int thresh_val_id, ret; /* Read interrupt flags */ - ret = regmap_read(priv->regmap, GP2AP020A00F_OP_REG, - &op_reg_val); + ret = regmap_read(priv->regmap, GP2AP020A00F_OP_REG, &op_reg_val); if (ret < 0) goto done; @@ -874,8 +868,7 @@ static irqreturn_t gp2ap020a00f_thresh_event_handler(int irq, void *data) /* Clear interrupt flags (if not in INTTYPE_PULSE mode) */ if (priv->cur_opmode != GP2AP020A00F_OPMODE_PROX_DETECT) { - ret = regmap_write(priv->regmap, GP2AP020A00F_OP_REG, - op_reg_val); + ret = regmap_write(priv->regmap, GP2AP020A00F_OP_REG, op_reg_val); if (ret < 0) goto done; } @@ -972,8 +965,7 @@ static irqreturn_t gp2ap020a00f_trigger_handler(int irq, void *data) } } - iio_push_to_buffers_with_timestamp(indio_dev, priv->buffer, - pf->timestamp); + iio_push_to_buffers_with_timestamp(indio_dev, priv->buffer, pf->timestamp); done: iio_trigger_notify_done(indio_dev->trig); @@ -1023,26 +1015,22 @@ static int gp2ap020a00f_write_event_val(struct iio_dev *indio_dev, switch (thresh_reg_l) { case GP2AP020A00F_TH_L_REG: - event_en = test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, - &data->flags); + event_en = test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags); break; case GP2AP020A00F_TL_L_REG: - event_en = test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, - &data->flags); + event_en = test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags); break; case GP2AP020A00F_PH_L_REG: if (val == 0) return -EINVAL; - event_en = test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, - &data->flags); + event_en = test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags); break; case GP2AP020A00F_PL_L_REG: if (val == 0) return -EINVAL; - event_en = test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, - &data->flags); + event_en = test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags); break; } @@ -1526,8 +1514,7 @@ static void gp2ap020a00f_remove(struct i2c_client *client) struct device *dev = &client->dev; int err; - err = gp2ap020a00f_set_operation_mode(data, - GP2AP020A00F_OPMODE_SHUTDOWN); + err = gp2ap020a00f_set_operation_mode(data, GP2AP020A00F_OPMODE_SHUTDOWN); if (err < 0) dev_err(dev, "Failed to power off the device.\n"); From 049875cb16a02473f141968e1b3f29f226504dd9 Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Mon, 23 Feb 2026 12:24:00 +0400 Subject: [PATCH 061/405] iio: adc: ad7173: move opening brace to a separate line Place the opening brace of ad7173_calc_openwire_thrsh_raw() on its own line to comply with the kernel coding style for function definitions. Issue found by checkpatch. Signed-off-by: Giorgi Tchankvetadze Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7173.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index d36612352b44..f76a9e08f39e 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -1763,7 +1763,8 @@ static int ad7173_validate_openwire_ain_inputs(struct ad7173_state *st, static unsigned int ad7173_calc_openwire_thrsh_raw(struct ad7173_state *st, struct iio_chan_spec *chan, struct ad7173_channel *chan_st_priv, - unsigned int thrsh_mv) { + unsigned int thrsh_mv) +{ unsigned int thrsh_raw; thrsh_raw = From ba53939bbadd40aef1d9096b958dcf93dee10a8d Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 23 Feb 2026 12:14:40 +0200 Subject: [PATCH 062/405] iio: addac: ad74413r: simplify timeout return Return -ETIMEDOUT directly instead of assigning it to an intermediate variable first. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/addac/ad74413r.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index a20b4d48c5f7..fe930ce5ee30 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -839,12 +839,9 @@ static int _ad74413r_get_single_adc_result(struct ad74413r_state *st, if (ret) return ret; - ret = wait_for_completion_timeout(&st->adc_data_completion, - msecs_to_jiffies(1000)); - if (!ret) { - ret = -ETIMEDOUT; - return ret; - } + if (!wait_for_completion_timeout(&st->adc_data_completion, + msecs_to_jiffies(1000))) + return -ETIMEDOUT; ret = regmap_read(st->regmap, AD74413R_REG_ADC_RESULT_X(channel), &uval); From 55bf8be6f4a80d44c7f10e9d39b583e9645edf93 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Tue, 24 Feb 2026 10:56:14 +0000 Subject: [PATCH 063/405] coresight: cti: Move resource release to cti_remove() Currently, CTI driver releases resource by deferring cti_device_release() to the device unregistration: cti_remove() `> coresight_unregister() `> cti_remove_assoc_from_csdev() `> device_unregister() `> cti_device_release() `> mutex_lock(&ect_mutex) `> release CTI resource `> mutex_unlock(&ect_mutex) In the above flow, two different CTI release callbacks are involved: cti_remove_assoc_from_csdev() and cti_device_release(). The former is used by a CoreSight device to unbind its associated CTI helper device, while the latter releases resources for the CTI device itself. Since there is no dependency between them, it is unnecessary to defer the CTI resource release until device unregistration. This commit releases the resources directly in cti_remove() and remove the injected release callback. Signed-off-by: Leo Yan Reviewed-by: Mike Leach Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260224-arm_coresight_refactor_cti_resource_release-v1-1-ff1b2bca9176@arm.com --- .../hwtracing/coresight/coresight-cti-core.c | 24 +++---------------- drivers/hwtracing/coresight/coresight-cti.h | 2 -- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c b/drivers/hwtracing/coresight/coresight-cti-core.c index bfbc365bb2ef..7a8f1ef6b94e 100644 --- a/drivers/hwtracing/coresight/coresight-cti-core.c +++ b/drivers/hwtracing/coresight/coresight-cti-core.c @@ -823,16 +823,13 @@ static const struct coresight_ops cti_ops = { .helper_ops = &cti_ops_ect, }; -/* - * Free up CTI specific resources - * called by dev->release, need to call down to underlying csdev release. - */ -static void cti_device_release(struct device *dev) +static void cti_remove(struct amba_device *adev) { - struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_drvdata *drvdata = dev_get_drvdata(&adev->dev); struct cti_drvdata *ect_item, *ect_tmp; mutex_lock(&ect_mutex); + cti_remove_conn_xrefs(drvdata); cti_pm_release(drvdata); /* remove from the list */ @@ -844,17 +841,6 @@ static void cti_device_release(struct device *dev) } mutex_unlock(&ect_mutex); - if (drvdata->csdev_release) - drvdata->csdev_release(dev); -} -static void cti_remove(struct amba_device *adev) -{ - struct cti_drvdata *drvdata = dev_get_drvdata(&adev->dev); - - mutex_lock(&ect_mutex); - cti_remove_conn_xrefs(drvdata); - mutex_unlock(&ect_mutex); - coresight_unregister(drvdata->csdev); } @@ -947,10 +933,6 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) cti_update_conn_xrefs(drvdata); mutex_unlock(&ect_mutex); - /* set up release chain */ - drvdata->csdev_release = drvdata->csdev->dev.release; - drvdata->csdev->dev.release = cti_device_release; - /* all done - dec pm refcount */ pm_runtime_put(&adev->dev); dev_info(&drvdata->csdev->dev, "CTI initialized\n"); diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index 4f89091ee93f..daff9e32a6da 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -170,7 +170,6 @@ struct cti_config { * @spinlock: Control data access to one at a time. * @config: Configuration data for this CTI device. * @node: List entry of this device in the list of CTI devices. - * @csdev_release: release function for underlying coresight_device. */ struct cti_drvdata { void __iomem *base; @@ -179,7 +178,6 @@ struct cti_drvdata { raw_spinlock_t spinlock; struct cti_config config; struct list_head node; - void (*csdev_release)(struct device *dev); }; /* From 0289ada4a31661016a0611a41a4886bb958e9985 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Mon, 9 Feb 2026 12:44:33 +0000 Subject: [PATCH 064/405] coresight: Fix memory leak in coresight_alloc_device_name() The memory leak detector reports: echo clear > /sys/kernel/debug/kmemleak modprobe coresight_funnel rmmod coresight_funnel # Scan memory leak and report it echo scan > /sys/kernel/debug/kmemleak cat /sys/kernel/debug/kmemleak unreferenced object 0xffff0008020c7200 (size 64): comm "modprobe", pid 410, jiffies 4295333721 hex dump (first 32 bytes): d8 da fe 7e 09 00 ff ff e8 2e ff 7e 09 00 ff ff ...~.......~.... b0 6c ff 7e 09 00 ff ff 30 83 00 7f 09 00 ff ff .l.~....0....... backtrace (crc 4116a690): kmemleak_alloc+0xd8/0xf8 __kmalloc_node_track_caller_noprof+0x2c8/0x6f0 krealloc_node_align_noprof+0x13c/0x2c8 coresight_alloc_device_name+0xe4/0x158 [coresight] 0xffffd327ecef8394 0xffffd327ecef85ec amba_probe+0x118/0x1c8 really_probe+0xc8/0x3f0 __driver_probe_device+0x88/0x190 driver_probe_device+0x44/0x120 __driver_attach+0x100/0x238 bus_for_each_dev+0x84/0xf0 driver_attach+0x2c/0x40 bus_add_driver+0x128/0x258 driver_register+0x64/0x138 __amba_driver_register+0x2c/0x48 The memory leak is caused by not freeing the device list that maintains device indices. This device list preserves stable device indices across unbind and rebind device operations, so it does not share the same lifetime as a device instances and must only be freed when the module is unloaded. Some modules do not implement a module exit callback because they are registered using module_platform_driver(). As a result, the device list cannot be released during module exit for those modules. Fix this by moving the device list into the core layer. As a general solution, instead of maintaining a static list in each driver, drivers now allocate device lists via coresight_allocate_device_list() and device indices via coresight_allocate_device_idx(). The list is released only when the core module is unloaded by calling coresight_release_device_list(), avoiding the leak. Fixes: 0f5f9b6ba9e1 ("coresight: Use platform agnostic names") Reviewed-by: James Clark Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260209-arm_coresight_refactor_dev_register-v4-1-62d6042f76f7@arm.com --- drivers/hwtracing/coresight/coresight-catu.c | 4 +- drivers/hwtracing/coresight/coresight-core.c | 129 +++++++++++++----- .../hwtracing/coresight/coresight-ctcu-core.c | 4 +- .../hwtracing/coresight/coresight-cti-core.c | 19 ++- drivers/hwtracing/coresight/coresight-dummy.c | 7 +- drivers/hwtracing/coresight/coresight-etb10.c | 4 +- .../hwtracing/coresight/coresight-funnel.c | 4 +- .../coresight/coresight-replicator.c | 4 +- drivers/hwtracing/coresight/coresight-stm.c | 4 +- .../hwtracing/coresight/coresight-tmc-core.c | 12 +- drivers/hwtracing/coresight/coresight-tnoc.c | 4 +- drivers/hwtracing/coresight/coresight-tpda.c | 4 +- drivers/hwtracing/coresight/coresight-tpdm.c | 4 +- drivers/hwtracing/coresight/coresight-tpiu.c | 4 +- drivers/hwtracing/coresight/ultrasoc-smb.c | 4 +- include/linux/coresight.h | 14 +- 16 files changed, 121 insertions(+), 104 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c index dfd035852b12..ce71dcddfca2 100644 --- a/drivers/hwtracing/coresight/coresight-catu.c +++ b/drivers/hwtracing/coresight/coresight-catu.c @@ -30,8 +30,6 @@ #define catu_dbg(x, ...) do {} while (0) #endif -DEFINE_CORESIGHT_DEVLIST(catu_devs, "catu"); - struct catu_etr_buf { struct tmc_sg_table *catu_table; dma_addr_t sladdr; @@ -530,7 +528,7 @@ static int __catu_probe(struct device *dev, struct resource *res) if (ret) return ret; - catu_desc.name = coresight_alloc_device_name(&catu_devs, dev); + catu_desc.name = coresight_alloc_device_name("catu", dev); if (!catu_desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 80e26396ad0a..6881fdc5da92 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -53,6 +53,9 @@ struct coresight_node { const u32 coresight_barrier_pkt[4] = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff}; EXPORT_SYMBOL_GPL(coresight_barrier_pkt); +/* List maintains the device index */ +static LIST_HEAD(coresight_dev_idx_list); + static const struct cti_assoc_op *cti_assoc_ops; void coresight_set_cti_ops(const struct cti_assoc_op *cti_op) @@ -1438,22 +1441,55 @@ void coresight_unregister(struct coresight_device *csdev) } EXPORT_SYMBOL_GPL(coresight_unregister); - -/* - * coresight_search_device_idx - Search the fwnode handle of a device - * in the given dev_idx list. Must be called with the coresight_mutex held. - * - * Returns the index of the entry, when found. Otherwise, -ENOENT. - */ -static int coresight_search_device_idx(struct coresight_dev_list *dict, - struct fwnode_handle *fwnode) +static struct coresight_dev_list * +coresight_allocate_device_list(const char *prefix) { - int i; + struct coresight_dev_list *list; - for (i = 0; i < dict->nr_idx; i++) - if (dict->fwnode_list[i] == fwnode) - return i; - return -ENOENT; + /* Check if have already allocated */ + list_for_each_entry(list, &coresight_dev_idx_list, node) { + if (!strcmp(list->pfx, prefix)) + return list; + } + + list = kzalloc(sizeof(*list), GFP_KERNEL); + if (!list) + return NULL; + + list->pfx = kstrdup(prefix, GFP_KERNEL); + if (!list->pfx) { + kfree(list); + return NULL; + } + + list_add(&list->node, &coresight_dev_idx_list); + return list; +} + +static int coresight_allocate_device_idx(struct coresight_dev_list *list, + struct device *dev) +{ + struct fwnode_handle **fwnode_list; + struct fwnode_handle *fwnode = dev_fwnode(dev); + int idx; + + for (idx = 0; idx < list->nr_idx; idx++) + if (list->fwnode_list[idx] == fwnode) + return idx; + + /* Make space for the new entry */ + idx = list->nr_idx; + fwnode_list = krealloc_array(list->fwnode_list, + idx + 1, sizeof(*list->fwnode_list), + GFP_KERNEL); + if (!fwnode_list) + return -ENOMEM; + + fwnode_list[idx] = fwnode; + list->fwnode_list = fwnode_list; + list->nr_idx = idx + 1; + + return idx; } static bool coresight_compare_type(enum coresight_dev_type type_a, @@ -1527,45 +1563,63 @@ bool coresight_loses_context_with_cpu(struct device *dev) EXPORT_SYMBOL_GPL(coresight_loses_context_with_cpu); /* - * coresight_alloc_device_name - Get an index for a given device in the - * device index list specific to a driver. An index is allocated for a - * device and is tracked with the fwnode_handle to prevent allocating + * coresight_alloc_device_name - Get an index for a given device in the list + * specific to a driver (presented by the prefix string). An index is allocated + * for a device and is tracked with the fwnode_handle to prevent allocating * duplicate indices for the same device (e.g, if we defer probing of * a device due to dependencies), in case the index is requested again. */ -char *coresight_alloc_device_name(struct coresight_dev_list *dict, - struct device *dev) +char *coresight_alloc_device_name(const char *prefix, struct device *dev) { - int idx; + struct coresight_dev_list *list; char *name = NULL; - struct fwnode_handle **list; + int idx; mutex_lock(&coresight_mutex); - idx = coresight_search_device_idx(dict, dev_fwnode(dev)); - if (idx < 0) { - /* Make space for the new entry */ - idx = dict->nr_idx; - list = krealloc_array(dict->fwnode_list, - idx + 1, sizeof(*dict->fwnode_list), - GFP_KERNEL); - if (ZERO_OR_NULL_PTR(list)) { - idx = -ENOMEM; - goto done; - } + list = coresight_allocate_device_list(prefix); + if (!list) + goto done; - list[idx] = dev_fwnode(dev); - dict->fwnode_list = list; - dict->nr_idx = idx + 1; - } + idx = coresight_allocate_device_idx(list, dev); - name = devm_kasprintf(dev, GFP_KERNEL, "%s%d", dict->pfx, idx); + /* + * If index allocation fails, the device list is not released here; + * it is instead freed later by coresight_release_device_list() when + * the coresight_core module is unloaded. + */ + if (idx < 0) + goto done; + + name = devm_kasprintf(dev, GFP_KERNEL, "%s%d", list->pfx, idx); done: mutex_unlock(&coresight_mutex); return name; } EXPORT_SYMBOL_GPL(coresight_alloc_device_name); +static void coresight_release_device_list(void) +{ + struct coresight_dev_list *list, *next; + int i; + + /* + * Here is no need to take coresight_mutex; this is during core module + * unloading, no race condition with other modules. + */ + + list_for_each_entry_safe(list, next, &coresight_dev_idx_list, node) { + for (i = 0; i < list->nr_idx; i++) + list->fwnode_list[i] = NULL; + list->nr_idx = 0; + list_del(&list->node); + + kfree(list->pfx); + kfree(list->fwnode_list); + kfree(list); + } +} + const struct bus_type coresight_bustype = { .name = "coresight", }; @@ -1639,6 +1693,7 @@ static void __exit coresight_exit(void) &coresight_notifier); etm_perf_exit(); bus_unregister(&coresight_bustype); + coresight_release_device_list(); } module_init(coresight_init); diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/drivers/hwtracing/coresight/coresight-ctcu-core.c index abed15eb72b4..6813ae6e929b 100644 --- a/drivers/hwtracing/coresight/coresight-ctcu-core.c +++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c @@ -19,8 +19,6 @@ #include "coresight-ctcu.h" #include "coresight-priv.h" -DEFINE_CORESIGHT_DEVLIST(ctcu_devs, "ctcu"); - #define ctcu_writel(drvdata, val, offset) __raw_writel((val), drvdata->base + offset) #define ctcu_readl(drvdata, offset) __raw_readl(drvdata->base + offset) @@ -187,7 +185,7 @@ static int ctcu_probe(struct platform_device *pdev) void __iomem *base; int i, ret; - desc.name = coresight_alloc_device_name(&ctcu_devs, dev); + desc.name = coresight_alloc_device_name("ctcu", dev); if (!desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c b/drivers/hwtracing/coresight/coresight-cti-core.c index 7a8f1ef6b94e..fddc8f31b91d 100644 --- a/drivers/hwtracing/coresight/coresight-cti-core.c +++ b/drivers/hwtracing/coresight/coresight-cti-core.c @@ -48,15 +48,6 @@ static int nr_cti_cpu; /* quick lookup list for CPU bound CTIs when power handling */ static struct cti_drvdata *cti_cpu_drvdata[NR_CPUS]; -/* - * CTI naming. CTI bound to cores will have the name cti_cpu where - * N is the CPU ID. System CTIs will have the name cti_sys where I - * is an index allocated by order of discovery. - * - * CTI device name list - for CTI not bound to cores. - */ -DEFINE_CORESIGHT_DEVLIST(cti_sys_devs, "cti_sys"); - /* write set of regs to hardware - call with spinlock claimed */ void cti_write_all_hw_regs(struct cti_drvdata *drvdata) { @@ -889,12 +880,18 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) /* default to powered - could change on PM notifications */ drvdata->config.hw_powered = true; - /* set up device name - will depend if cpu bound or otherwise */ + /* + * Set up device name - will depend if cpu bound or otherwise. + * + * CTI bound to cores will have the name cti_cpu where N is th + * eCPU ID. System CTIs will have the name cti_sys where I is an + * index allocated by order of discovery. + */ if (drvdata->ctidev.cpu >= 0) cti_desc.name = devm_kasprintf(dev, GFP_KERNEL, "cti_cpu%d", drvdata->ctidev.cpu); else - cti_desc.name = coresight_alloc_device_name(&cti_sys_devs, dev); + cti_desc.name = coresight_alloc_device_name("cti_sys", dev); if (!cti_desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-dummy.c b/drivers/hwtracing/coresight/coresight-dummy.c index 14322c99e29d..c176a2f57300 100644 --- a/drivers/hwtracing/coresight/coresight-dummy.c +++ b/drivers/hwtracing/coresight/coresight-dummy.c @@ -19,9 +19,6 @@ struct dummy_drvdata { u8 traceid; }; -DEFINE_CORESIGHT_DEVLIST(source_devs, "dummy_source"); -DEFINE_CORESIGHT_DEVLIST(sink_devs, "dummy_sink"); - static int dummy_source_enable(struct coresight_device *csdev, struct perf_event *event, enum cs_mode mode, __maybe_unused struct coresight_path *path) @@ -126,7 +123,7 @@ static int dummy_probe(struct platform_device *pdev) if (of_device_is_compatible(node, "arm,coresight-dummy-source")) { - desc.name = coresight_alloc_device_name(&source_devs, dev); + desc.name = coresight_alloc_device_name("dummy_source", dev); if (!desc.name) return -ENOMEM; @@ -155,7 +152,7 @@ static int dummy_probe(struct platform_device *pdev) drvdata->traceid = (u8)trace_id; } else if (of_device_is_compatible(node, "arm,coresight-dummy-sink")) { - desc.name = coresight_alloc_device_name(&sink_devs, dev); + desc.name = coresight_alloc_device_name("dummy_sink", dev); if (!desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 6657602d8f2e..b952a1d47f12 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -63,8 +63,6 @@ #define ETB_FFSR_BIT 1 #define ETB_FRAME_SIZE_WORDS 4 -DEFINE_CORESIGHT_DEVLIST(etb_devs, "etb"); - /** * struct etb_drvdata - specifics associated to an ETB component * @base: memory mapped base address for this component. @@ -722,7 +720,7 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id) struct resource *res = &adev->res; struct coresight_desc desc = { 0 }; - desc.name = coresight_alloc_device_name(&etb_devs, dev); + desc.name = coresight_alloc_device_name("etb", dev); if (!desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 3b248e54471a..3f56ceccd8c9 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -30,8 +30,6 @@ #define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT) #define FUNNEL_ENSx_MASK 0xff -DEFINE_CORESIGHT_DEVLIST(funnel_devs, "funnel"); - /** * struct funnel_drvdata - specifics associated to a funnel component * @base: memory mapped base address for this component. @@ -223,7 +221,7 @@ static int funnel_probe(struct device *dev, struct resource *res) of_device_is_compatible(dev->of_node, "arm,coresight-funnel")) dev_warn_once(dev, "Uses OBSOLETE CoreSight funnel binding\n"); - desc.name = coresight_alloc_device_name(&funnel_devs, dev); + desc.name = coresight_alloc_device_name("funnel", dev); if (!desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index e6472658235d..07fc04f53b88 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -24,8 +24,6 @@ #define REPLICATOR_IDFILTER0 0x000 #define REPLICATOR_IDFILTER1 0x004 -DEFINE_CORESIGHT_DEVLIST(replicator_devs, "replicator"); - /** * struct replicator_drvdata - specifics associated to a replicator component * @base: memory mapped base address for this component. Also indicates @@ -230,7 +228,7 @@ static int replicator_probe(struct device *dev, struct resource *res) dev_warn_once(dev, "Uses OBSOLETE CoreSight replicator binding\n"); - desc.name = coresight_alloc_device_name(&replicator_devs, dev); + desc.name = coresight_alloc_device_name("replicator", dev); if (!desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index e68529bf89c9..aca6cec7885a 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -110,8 +110,6 @@ struct channel_space { unsigned long *guaranteed; }; -DEFINE_CORESIGHT_DEVLIST(stm_devs, "stm"); - /** * struct stm_drvdata - specifics associated to an STM component * @base: memory mapped base address for this component. @@ -834,7 +832,7 @@ static int __stm_probe(struct device *dev, struct resource *res) struct resource ch_res; struct coresight_desc desc = { 0 }; - desc.name = coresight_alloc_device_name(&stm_devs, dev); + desc.name = coresight_alloc_device_name("stm", dev); if (!desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index 36599c431be6..58b469ee73b4 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -32,10 +32,6 @@ #include "coresight-priv.h" #include "coresight-tmc.h" -DEFINE_CORESIGHT_DEVLIST(etb_devs, "tmc_etb"); -DEFINE_CORESIGHT_DEVLIST(etf_devs, "tmc_etf"); -DEFINE_CORESIGHT_DEVLIST(etr_devs, "tmc_etr"); - int tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) { struct coresight_device *csdev = drvdata->csdev; @@ -777,7 +773,7 @@ static int __tmc_probe(struct device *dev, struct resource *res) struct coresight_platform_data *pdata = NULL; struct tmc_drvdata *drvdata; struct coresight_desc desc = { 0 }; - struct coresight_dev_list *dev_list = NULL; + const char *dev_list = NULL; drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) @@ -827,7 +823,7 @@ static int __tmc_probe(struct device *dev, struct resource *res) desc.type = CORESIGHT_DEV_TYPE_SINK; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.ops = &tmc_etb_cs_ops; - dev_list = &etb_devs; + dev_list = "tmc_etb"; break; case TMC_CONFIG_TYPE_ETR: desc.groups = coresight_etr_groups; @@ -839,7 +835,7 @@ static int __tmc_probe(struct device *dev, struct resource *res) goto out; idr_init(&drvdata->idr); mutex_init(&drvdata->idr_mutex); - dev_list = &etr_devs; + dev_list = "tmc_etr"; break; case TMC_CONFIG_TYPE_ETF: desc.groups = coresight_etf_groups; @@ -847,7 +843,7 @@ static int __tmc_probe(struct device *dev, struct resource *res) desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO; desc.ops = &tmc_etf_cs_ops; - dev_list = &etf_devs; + dev_list = "tmc_etf"; break; default: pr_err("%s: Unsupported TMC config\n", desc.name); diff --git a/drivers/hwtracing/coresight/coresight-tnoc.c b/drivers/hwtracing/coresight/coresight-tnoc.c index 1128612e70a7..96a25877b824 100644 --- a/drivers/hwtracing/coresight/coresight-tnoc.c +++ b/drivers/hwtracing/coresight/coresight-tnoc.c @@ -47,8 +47,6 @@ struct trace_noc_drvdata { int atid; }; -DEFINE_CORESIGHT_DEVLIST(trace_noc_devs, "traceNoc"); - static void trace_noc_enable_hw(struct trace_noc_drvdata *drvdata) { u32 val; @@ -191,7 +189,7 @@ static int _tnoc_probe(struct device *dev, struct resource *res) struct coresight_desc desc = { 0 }; int ret; - desc.name = coresight_alloc_device_name(&trace_noc_devs, dev); + desc.name = coresight_alloc_device_name("traceNoc", dev); if (!desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-tpda.c b/drivers/hwtracing/coresight/coresight-tpda.c index 7055f8f13427..89c8f71f0aff 100644 --- a/drivers/hwtracing/coresight/coresight-tpda.c +++ b/drivers/hwtracing/coresight/coresight-tpda.c @@ -20,8 +20,6 @@ #include "coresight-trace-id.h" #include "coresight-tpdm.h" -DEFINE_CORESIGHT_DEVLIST(tpda_devs, "tpda"); - static void tpda_clear_element_size(struct coresight_device *csdev) { struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -585,7 +583,7 @@ static int tpda_probe(struct amba_device *adev, const struct amba_id *id) if (ret) return ret; - desc.name = coresight_alloc_device_name(&tpda_devs, dev); + desc.name = coresight_alloc_device_name("tpda", dev); if (!desc.name) return -ENOMEM; desc.type = CORESIGHT_DEV_TYPE_LINK; diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c index 06e0a905a67d..da77bdaad0a4 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.c +++ b/drivers/hwtracing/coresight/coresight-tpdm.c @@ -19,8 +19,6 @@ #include "coresight-priv.h" #include "coresight-tpdm.h" -DEFINE_CORESIGHT_DEVLIST(tpdm_devs, "tpdm"); - static bool tpdm_has_dsb_dataset(struct tpdm_drvdata *drvdata) { return (drvdata->datasets & TPDM_PIDR0_DS_DSB); @@ -1416,7 +1414,7 @@ static int tpdm_probe(struct device *dev, struct resource *res) } /* Set up coresight component description */ - desc.name = coresight_alloc_device_name(&tpdm_devs, dev); + desc.name = coresight_alloc_device_name("tpdm", dev); if (!desc.name) return -ENOMEM; desc.type = CORESIGHT_DEV_TYPE_SOURCE; diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index aaa44bc521c3..b8560b140e0f 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -49,8 +49,6 @@ #define FFCR_FON_MAN BIT(6) #define FFCR_STOP_FI BIT(12) -DEFINE_CORESIGHT_DEVLIST(tpiu_devs, "tpiu"); - /* * @base: memory mapped base address for this component. * @atclk: optional clock for the core parts of the TPIU. @@ -134,7 +132,7 @@ static int __tpiu_probe(struct device *dev, struct resource *res) struct coresight_desc desc = { 0 }; int ret; - desc.name = coresight_alloc_device_name(&tpiu_devs, dev); + desc.name = coresight_alloc_device_name("tpiu", dev); if (!desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c index 8f7922a5e534..5776f63468fa 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.c +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c @@ -17,8 +17,6 @@ #include "coresight-priv.h" #include "ultrasoc-smb.h" -DEFINE_CORESIGHT_DEVLIST(sink_devs, "ultra_smb"); - #define ULTRASOC_SMB_DSM_UUID "82ae1283-7f6a-4cbe-aa06-53e8fb24db18" static bool smb_buffer_not_empty(struct smb_drv_data *drvdata) @@ -478,7 +476,7 @@ static int smb_register_sink(struct platform_device *pdev, desc.pdata = pdata; desc.dev = &pdev->dev; desc.groups = smb_sink_groups; - desc.name = coresight_alloc_device_name(&sink_devs, &pdev->dev); + desc.name = coresight_alloc_device_name("ultra_smb", &pdev->dev); if (!desc.name) { dev_err(&pdev->dev, "Failed to alloc coresight device name"); return -ENOMEM; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 2b48be97fcd0..2131febebee9 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -306,24 +306,19 @@ struct coresight_device { * coresight_dev_list - Mapping for devices to "name" index for device * names. * + * @node: Node on the global device index list. * @nr_idx: Number of entries already allocated. * @pfx: Prefix pattern for device name. * @fwnode_list: Array of fwnode_handles associated with each allocated * index, upto nr_idx entries. */ struct coresight_dev_list { + struct list_head node; int nr_idx; - const char *pfx; + char *pfx; struct fwnode_handle **fwnode_list; }; -#define DEFINE_CORESIGHT_DEVLIST(var, dev_pfx) \ -static struct coresight_dev_list (var) = { \ - .pfx = dev_pfx, \ - .nr_idx = 0, \ - .fwnode_list = NULL, \ -} - #define to_coresight_device(d) container_of(d, struct coresight_device, dev) /** @@ -663,8 +658,7 @@ void coresight_clear_self_claim_tag(struct csdev_access *csa); void coresight_clear_self_claim_tag_unlocked(struct csdev_access *csa); void coresight_disclaim_device(struct coresight_device *csdev); void coresight_disclaim_device_unlocked(struct coresight_device *csdev); -char *coresight_alloc_device_name(struct coresight_dev_list *devs, - struct device *dev); +char *coresight_alloc_device_name(const char *prefix, struct device *dev); bool coresight_loses_context_with_cpu(struct device *dev); From 2f322f0392814a1b704e14927d282f4b2edb9e98 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Mon, 9 Feb 2026 12:44:34 +0000 Subject: [PATCH 065/405] coresight: Get parent device reference after sink ID map allocation The parent device's reference count is incremented before allocating the sink ID map. If the allocation fails, the reference count is not decremented, preventing proper cleanup. Fix this by incrementing the reference count only after the sink ID map is successfully allocated. Fixes: 5ad628a76176 ("coresight: Use per-sink trace ID maps for Perf sessions") Reviewed-by: James Clark Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260209-arm_coresight_refactor_dev_register-v4-2-62d6042f76f7@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 6881fdc5da92..e2c0f971eccf 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1349,12 +1349,6 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) csdev->dev.parent = desc->dev; csdev->dev.release = coresight_device_release; csdev->dev.bus = &coresight_bustype; - /* - * Hold the reference to our parent device. This will be - * dropped only in coresight_device_release(). - */ - csdev->dev.fwnode = fwnode_handle_get(dev_fwnode(desc->dev)); - dev_set_name(&csdev->dev, "%s", desc->name); if (csdev->type == CORESIGHT_DEV_TYPE_SINK || csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) { @@ -1366,6 +1360,14 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) goto err_out; } } + + /* + * Hold the reference to our parent device. This will be + * dropped only in coresight_device_release(). + */ + csdev->dev.fwnode = fwnode_handle_get(dev_fwnode(desc->dev)); + dev_set_name(&csdev->dev, "%s", desc->name); + /* * Make sure the device registration and the connection fixup * are synchronised, so that we don't see uninitialised devices From a6c4da4b95a3cfe4f6031bef2d913331dc186142 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Mon, 9 Feb 2026 12:44:35 +0000 Subject: [PATCH 066/405] coresight: Protect unregistration with mutex The device registration is protected by CoreSight mutex to ensure the atomic operations when adding a device onto bus. One the other hand, the locking is absent when unregister a device. Use mutex to ensure atomicity on device unregistration. During unregistration, unbinding the associated CTI device is not included in the locking region, as CTI has its own locking mechanism. Fixes: 8c1d3f79d9ca ("coresight: core: Fix coresight device probe failure issue") Reviewed-by: James Clark Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260209-arm_coresight_refactor_dev_register-v4-3-62d6042f76f7@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index e2c0f971eccf..c228c27e8517 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1432,14 +1432,17 @@ EXPORT_SYMBOL_GPL(coresight_register); void coresight_unregister(struct coresight_device *csdev) { - etm_perf_del_symlink_sink(csdev); /* Remove references of that device in the topology */ if (cti_assoc_ops && cti_assoc_ops->remove) cti_assoc_ops->remove(csdev); + + mutex_lock(&coresight_mutex); + etm_perf_del_symlink_sink(csdev); coresight_remove_conns(csdev); coresight_clear_default_sink(csdev); coresight_release_platform_data(csdev, csdev->dev.parent, csdev->pdata); device_unregister(&csdev->dev); + mutex_unlock(&coresight_mutex); } EXPORT_SYMBOL_GPL(coresight_unregister); From 32c225491ed8dc90c9ba4a8d07f064133d52a179 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Mon, 9 Feb 2026 12:44:36 +0000 Subject: [PATCH 067/405] coresight: Refactor output connection sysfs link cleanup To use a central place for releasing connections, move the output connection sysfs link cleanup into coresight_remove_conns(). Also update the comments accordingly. Reviewed-by: James Clark Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260209-arm_coresight_refactor_dev_register-v4-4-62d6042f76f7@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index c228c27e8517..2185a8f869ad 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1152,7 +1152,6 @@ static int coresight_clear_filter_source(struct device *dev, void *data) return 0; } -/* coresight_remove_conns - Remove other device's references to this device */ static void coresight_remove_conns(struct coresight_device *csdev) { int i, j; @@ -1162,10 +1161,6 @@ static void coresight_remove_conns(struct coresight_device *csdev) bus_for_each_dev(&coresight_bustype, NULL, csdev, coresight_clear_filter_source); - /* - * Remove the input connection references from the destination device - * for each output connection. - */ for (i = 0; i < csdev->pdata->nr_outconns; i++) { conn = csdev->pdata->out_conns[i]; if (conn->filter_src_fwnode) { @@ -1176,6 +1171,13 @@ static void coresight_remove_conns(struct coresight_device *csdev) if (!conn->dest_dev) continue; + /* Remove sysfs links for the output connection */ + coresight_remove_links(csdev, conn); + + /* + * Remove the input connection references from the destination + * device for each output connection. + */ for (j = 0; j < conn->dest_dev->pdata->nr_inconns; ++j) if (conn->dest_dev->pdata->in_conns[j] == conn) { conn->dest_dev->pdata->in_conns[j] = NULL; @@ -1306,9 +1308,6 @@ void coresight_release_platform_data(struct coresight_device *csdev, struct coresight_connection **conns = pdata->out_conns; for (i = 0; i < pdata->nr_outconns; i++) { - /* If we have made the links, remove them now */ - if (csdev && conns[i]->dest_dev) - coresight_remove_links(csdev, conns[i]); /* * Drop the refcount and clear the handle as this device * is going away @@ -1424,7 +1423,6 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) } err_out: - /* Cleanup the connection information */ coresight_release_platform_data(NULL, desc->dev, desc->pdata); return ERR_PTR(ret); } From babb987968d3610f8953f24b26154bf03c169811 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Mon, 9 Feb 2026 12:44:37 +0000 Subject: [PATCH 068/405] coresight: Refactor sysfs connection group cleanup Move the sysfs connection group cleanup into coresight_remove_conns(), so that the driver removes connections and related sysfs resources in one go. As side effect, the csdev argument to coresight_release_platform_data() is no longer needed; adjust the code for this. Reviewed-by: James Clark Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260209-arm_coresight_refactor_dev_register-v4-5-62d6042f76f7@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 11 +++++------ drivers/hwtracing/coresight/coresight-platform.c | 2 +- drivers/hwtracing/coresight/coresight-priv.h | 3 +-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 2185a8f869ad..34439ca98d8d 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1200,6 +1200,8 @@ static void coresight_remove_conns(struct coresight_device *csdev) coresight_remove_links(conn->src_dev, conn); conn->dest_dev = NULL; } + + coresight_remove_conns_sysfs_group(csdev); } /** @@ -1300,8 +1302,7 @@ void coresight_write64(struct coresight_device *csdev, u64 val, u32 offset) * coresight_release_platform_data: Release references to the devices connected * to the output port of this device. */ -void coresight_release_platform_data(struct coresight_device *csdev, - struct device *dev, +void coresight_release_platform_data(struct device *dev, struct coresight_platform_data *pdata) { int i; @@ -1319,8 +1320,6 @@ void coresight_release_platform_data(struct coresight_device *csdev, devm_kfree(dev, pdata->out_conns); devm_kfree(dev, pdata->in_conns); devm_kfree(dev, pdata); - if (csdev) - coresight_remove_conns_sysfs_group(csdev); } struct coresight_device *coresight_register(struct coresight_desc *desc) @@ -1423,7 +1422,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) } err_out: - coresight_release_platform_data(NULL, desc->dev, desc->pdata); + coresight_release_platform_data(desc->dev, desc->pdata); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(coresight_register); @@ -1438,7 +1437,7 @@ void coresight_unregister(struct coresight_device *csdev) etm_perf_del_symlink_sink(csdev); coresight_remove_conns(csdev); coresight_clear_default_sink(csdev); - coresight_release_platform_data(csdev, csdev->dev.parent, csdev->pdata); + coresight_release_platform_data(csdev->dev.parent, csdev->pdata); device_unregister(&csdev->dev); mutex_unlock(&coresight_mutex); } diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c index 0db64c5f4995..0ca3bd762454 100644 --- a/drivers/hwtracing/coresight/coresight-platform.c +++ b/drivers/hwtracing/coresight/coresight-platform.c @@ -849,7 +849,7 @@ coresight_get_platform_data(struct device *dev) error: if (!IS_ERR_OR_NULL(pdata)) /* Cleanup the connection information */ - coresight_release_platform_data(NULL, dev, pdata); + coresight_release_platform_data(dev, pdata); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(coresight_get_platform_data); diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index fd896ac07942..1ea882dffd70 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -239,8 +239,7 @@ static inline void *coresight_get_uci_data_from_amba(const struct amba_id *table return NULL; } -void coresight_release_platform_data(struct coresight_device *csdev, - struct device *dev, +void coresight_release_platform_data(struct device *dev, struct coresight_platform_data *pdata); struct coresight_device * coresight_find_csdev_by_fwnode(struct fwnode_handle *r_fwnode); From 6b1ffc542850e5dd1c71394f6205582a9c8ad9c8 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Mon, 9 Feb 2026 12:44:38 +0000 Subject: [PATCH 069/405] coresight: Move sink validation into etm_perf_add_symlink_sink() Move the sink device type checks into etm_perf_add_symlink_sink(), and return -EOPNOTSUPP for unsupported devices. This simplifies the registration flow to invoke etm_perf_add_symlink_sink() unconditionally. Reviewed-by: James Clark Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260209-arm_coresight_refactor_dev_register-v4-6-62d6042f76f7@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 23 ++++++++----------- .../hwtracing/coresight/coresight-etm-perf.c | 5 +++- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 34439ca98d8d..6ddbc773330e 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1383,21 +1383,16 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) goto out_unlock; } - if ((csdev->type == CORESIGHT_DEV_TYPE_SINK || - csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && - sink_ops(csdev)->alloc_buffer) { - ret = etm_perf_add_symlink_sink(csdev); + ret = etm_perf_add_symlink_sink(csdev); - if (ret) { - device_unregister(&csdev->dev); - /* - * As with the above, all resources are free'd - * explicitly via coresight_device_release() triggered - * from put_device(), which is in turn called from - * function device_unregister(). - */ - goto out_unlock; - } + /* + * As with the above, all resources are free'd explicitly via + * coresight_device_release() triggered from put_device(), which is in + * turn called from function device_unregister(). + */ + if (ret && ret != -EOPNOTSUPP) { + device_unregister(&csdev->dev); + goto out_unlock; } /* Device is now registered */ registered = true; diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 72017dcc3b7f..f85dedf89a3f 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -902,7 +902,10 @@ int etm_perf_add_symlink_sink(struct coresight_device *csdev) if (csdev->type != CORESIGHT_DEV_TYPE_SINK && csdev->type != CORESIGHT_DEV_TYPE_LINKSINK) - return -EINVAL; + return -EOPNOTSUPP; + + if (!sink_ops(csdev)->alloc_buffer) + return -EOPNOTSUPP; if (csdev->ea != NULL) return -EINVAL; From 8573756b235ddfa005837a958241caf204696a0a Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Mon, 9 Feb 2026 12:44:39 +0000 Subject: [PATCH 070/405] coresight: Do not mix success path with failure handling Separate the failure handling path from the successful case. Use the 'out_unlock' label only for failure handling. Reviewed-by: James Clark Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260209-arm_coresight_refactor_dev_register-v4-7-62d6042f76f7@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 21 ++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 6ddbc773330e..e04545295240 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1398,17 +1398,22 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) registered = true; ret = coresight_create_conns_sysfs_group(csdev); - if (!ret) - ret = coresight_fixup_orphan_conns(csdev); + if (ret) + goto out_unlock; + + ret = coresight_fixup_orphan_conns(csdev); + if (ret) + goto out_unlock; + + mutex_unlock(&coresight_mutex); + + if (cti_assoc_ops && cti_assoc_ops->add) + cti_assoc_ops->add(csdev); + + return csdev; out_unlock: mutex_unlock(&coresight_mutex); - /* Success */ - if (!ret) { - if (cti_assoc_ops && cti_assoc_ops->add) - cti_assoc_ops->add(csdev); - return csdev; - } /* Unregister the device if needed */ if (registered) { From eef33a7cce239783d0422526a4d786289a936f1b Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Mon, 9 Feb 2026 12:44:40 +0000 Subject: [PATCH 071/405] coresight: Unify bus unregistration via coresight_unregister() Once a device is successfully registered, set the "registered" flag to true. After that point, all failures jump to the out_unlock label to unwind the flow via coresight_unregister(). Since failure handling is unified, the comment about resource release for the etm_perf_add_symlink_sink() failure is no need, remove it. Signed-off-by: Leo Yan Reviewed-by: James Clark Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260209-arm_coresight_refactor_dev_register-v4-8-62d6042f76f7@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index e04545295240..46f247f73cf6 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1383,20 +1383,13 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) goto out_unlock; } - ret = etm_perf_add_symlink_sink(csdev); - - /* - * As with the above, all resources are free'd explicitly via - * coresight_device_release() triggered from put_device(), which is in - * turn called from function device_unregister(). - */ - if (ret && ret != -EOPNOTSUPP) { - device_unregister(&csdev->dev); - goto out_unlock; - } /* Device is now registered */ registered = true; + ret = etm_perf_add_symlink_sink(csdev); + if (ret && ret != -EOPNOTSUPP) + goto out_unlock; + ret = coresight_create_conns_sysfs_group(csdev); if (ret) goto out_unlock; From 36ffbbc2b387bc5320e9caa90c0313743b87ba51 Mon Sep 17 00:00:00 2001 From: Jie Gan Date: Thu, 19 Feb 2026 22:46:57 +0800 Subject: [PATCH 072/405] coresight: ctcu: fix the spin_bug Acquiring an uninitialized raw_spin_lock is invalid and may trigger unexpected behavior or spin_bug. Fixes: f78d206f3d73 ("Coresight: Add Coresight TMC Control Unit driver") Signed-off-by: Jie Gan Reviewed-by: James Clark Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260219-fix-spin-lock-issue-v1-1-557f7d513d7e@oss.qualcomm.com --- drivers/hwtracing/coresight/coresight-ctcu-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/drivers/hwtracing/coresight/coresight-ctcu-core.c index 6813ae6e929b..9043cad42f01 100644 --- a/drivers/hwtracing/coresight/coresight-ctcu-core.c +++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c @@ -226,6 +226,7 @@ static int ctcu_probe(struct platform_device *pdev) desc.dev = dev; desc.ops = &ctcu_ops; desc.access = CSDEV_ACCESS_IOMEM(base); + raw_spin_lock_init(&drvdata->spin_lock); drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) From 87c266bb30dc09979b7b7e8f962598367d626b57 Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Thu, 26 Feb 2026 14:13:53 +0000 Subject: [PATCH 073/405] MAINTAINERS: Change e-mail address for reviewer My e-mail address for linux work is changing to mike.leach@arm.com from 1st Jan 2026. Update MAINTAINERS file accordingly Updated .mailmap file accordingly. Signed-off-by: Mike Leach Signed-off-by: Mike Leach Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260226141354.638962-1-mike.leach@arm.com --- .mailmap | 1 + MAINTAINERS | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.mailmap b/.mailmap index e1cf6bb85d33..c27d1d5b49bd 100644 --- a/.mailmap +++ b/.mailmap @@ -565,6 +565,7 @@ Michel Lespinasse Michel Lespinasse Mickaël Salaün Miguel Ojeda +Mike Leach Mike Rapoport Mike Rapoport Mike Rapoport diff --git a/MAINTAINERS b/MAINTAINERS index 55af015174a5..9c5491001908 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2708,7 +2708,7 @@ N: digicolor ARM/CORESIGHT FRAMEWORK AND DRIVERS M: Suzuki K Poulose -R: Mike Leach +R: Mike Leach R: James Clark L: coresight@lists.linaro.org (moderated for non-subscribers) L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) @@ -20715,7 +20715,7 @@ PERFORMANCE EVENTS TOOLING ARM64 R: John Garry R: Will Deacon R: James Clark -R: Mike Leach +R: Mike Leach R: Leo Yan L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported From f3e0b76fc29c4e1ee542f5173a4a631803e69436 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 19 Feb 2026 11:27:00 +0000 Subject: [PATCH 074/405] rust_binder: avoid name mangling for get_work[_local] Currently ps -A shows processes waiting on schedule() in functions with names such as do_epoll_wait, wait_woken, and the impeccably named _RNvMs2_NtCs8QPsHWIn21X_16rust_binder_main6threadNtB5_6Thread8get_work. To improve how ps output looks, give explicit non-mangled names to the functions where Rust Binder calls schedule(), since these are the most likely places to show up on ps output. The name of rust_binder_waitlcl is truncated instead of using _local suffix because rust_binder_wait_local is sufficiently long that ps shows unaligned output. This is intended to be a temporary workaround until we find a better solution. Adding #[export_name] to every Rust function that calls schedule() is not a great long-term solution. Suggested-by: Matthew Maurer Signed-off-by: Alice Ryhl Acked-by: Gary Guo Link: https://patch.msgid.link/20260219-rust-binder-ps-v2-1-773eca09c125@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 3 +++ drivers/android/binder/thread.rs | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 41de5593197c..f62f626f928e 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -1442,6 +1442,9 @@ pub(crate) fn drop_outstanding_txn(&self) { } } + // #[export_name] is a temporary workaround so that ps output does not become unreadable from + // mangled symbol names. + #[export_name = "rust_binder_freeze"] pub(crate) fn ioctl_freeze(&self, info: &BinderFreezeInfo) -> Result { if info.enable == 0 { let msgs = self.prepare_freeze_messages()?; diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 0b62d24b2118..6f197be0fa75 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -513,6 +513,9 @@ pub(crate) fn has_current_transaction(&self) -> bool { /// Attempts to fetch a work item from the thread-local queue. The behaviour if the queue is /// empty depends on `wait`: if it is true, the function waits for some work to be queued (or a /// signal); otherwise it returns indicating that none is available. + // #[export_name] is a temporary workaround so that ps output does not become unreadable from + // mangled symbol names. + #[export_name = "rust_binder_waitlcl"] fn get_work_local(self: &Arc, wait: bool) -> Result>> { { let mut inner = self.inner.lock(); @@ -551,6 +554,9 @@ fn get_work_local(self: &Arc, wait: bool) -> Result, wait: bool) -> Result>> { // Try to get work from the thread's work queue, using only a local lock. { From 65b6721522892a4994472fbac41386c63c769511 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Fri, 13 Feb 2026 22:37:30 +0100 Subject: [PATCH 075/405] binder: use current_euid() for transaction sender identity Binder currently uses task_euid(proc->tsk) as the transaction sender EUID, where proc->tsk is the main thread of the process that opened /dev/binder. That's not clean; use the subjective EUID of the current task instead. Signed-off-by: Jann Horn Reviewed-by: Alice Ryhl Acked-by: Gary Guo Link: https://patch.msgid.link/20260213-binder-uid-v1-1-7b795ae05523@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 21f91d9f2fbc..9e6194224593 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3117,7 +3117,7 @@ static void binder_transaction(struct binder_proc *proc, t->start_time = t_start_time; t->from_pid = proc->pid; t->from_tid = thread->pid; - t->sender_euid = task_euid(proc->tsk); + t->sender_euid = current_euid(); t->code = tr->code; t->flags = tr->flags; t->priority = task_nice(current); From d31ed22a0678da8948439c3009b01c4806a677c9 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Fri, 13 Feb 2026 22:37:31 +0100 Subject: [PATCH 076/405] rust_binder: use current_euid() for transaction sender identity Binder currently uses from.process.task.euid() as the transaction sender EUID, where from.process.task is the main thread of the process that opened /dev/binder. That's not clean; use the subjective EUID of the current task instead. Signed-off-by: Jann Horn Reviewed-by: Alice Ryhl Acked-by: Gary Guo Link: https://patch.msgid.link/20260213-binder-uid-v1-2-7b795ae05523@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/transaction.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 75e6f5fbaaae..10af40527ca7 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -107,7 +107,7 @@ pub(crate) fn new( debug_id, target_node: Some(target_node), from_parent, - sender_euid: from.process.task.euid(), + sender_euid: Kuid::current_euid(), from: from.clone(), to, code: trd.code, @@ -147,7 +147,7 @@ pub(crate) fn new_reply( debug_id, target_node: None, from_parent: None, - sender_euid: from.process.task.euid(), + sender_euid: Kuid::current_euid(), from: from.clone(), to, code: trd.code, From 47ac2a4b5cd8f0cb826f6368c2fc0eeb97e5d55f Mon Sep 17 00:00:00 2001 From: Shivam Kalra Date: Mon, 16 Feb 2026 19:39:55 +0530 Subject: [PATCH 077/405] rust: kvec: implement shrink_to for KVVec Implement shrink_to method specifically for `KVVec` (i.e., `Vec`). `shrink_to` reduces the vector's capacity to a specified minimum. For kmalloc-backed allocations, the method delegates to realloc(), letting the allocator decide whether shrinking is worthwhile. For vmalloc-backed allocations (detected via is_vmalloc_addr), shrinking only occurs if at least one page of memory can be freed, using an explicit alloc+copy+free since vrealloc does not yet support in-place shrinking. A TODO note marks this for future replacement with a generic shrink_to for all allocators that uses A::realloc() once the underlying allocators properly support shrinking via realloc. Suggested-by: Alice Ryhl Suggested-by: Danilo Krummrich Reviewed-by: Alice Ryhl Acked-by: Danilo Krummrich Signed-off-by: Shivam Kalra Link: https://patch.msgid.link/20260216-binder-shrink-vec-v3-v6-1-ece8e8593e53@zohomail.in Signed-off-by: Greg Kroah-Hartman --- rust/kernel/alloc/kvec.rs | 114 +++++++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 1 deletion(-) diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs index ac8d6f763ae8..e7bc439538e4 100644 --- a/rust/kernel/alloc/kvec.rs +++ b/rust/kernel/alloc/kvec.rs @@ -9,7 +9,10 @@ }; use crate::{ fmt, - page::AsPageIter, // + page::{ + AsPageIter, + PAGE_SIZE, // + }, }; use core::{ borrow::{Borrow, BorrowMut}, @@ -734,6 +737,115 @@ pub fn retain(&mut self, mut f: impl FnMut(&mut T) -> bool) { self.truncate(num_kept); } } +// TODO: This is a temporary KVVec-specific implementation. It should be replaced with a generic +// `shrink_to()` for `impl Vec` that uses `A::realloc()` once the +// underlying allocators properly support shrinking via realloc. +impl Vec { + /// Shrinks the capacity of the vector with a lower bound. + /// + /// The capacity will remain at least as large as both the length and the supplied value. + /// If the current capacity is less than the lower limit, this is a no-op. + /// + /// For `kmalloc` allocations, this delegates to `realloc()`, which decides whether + /// shrinking is worthwhile. For `vmalloc` allocations, shrinking only occurs if the + /// operation would free at least one page of memory, and performs a deep copy since + /// `vrealloc` does not yet support in-place shrinking. + /// + /// # Examples + /// + /// ``` + /// // Allocate enough capacity to span multiple pages. + /// let elements_per_page = kernel::page::PAGE_SIZE / core::mem::size_of::(); + /// let mut v = KVVec::with_capacity(elements_per_page * 4, GFP_KERNEL)?; + /// v.push(1, GFP_KERNEL)?; + /// v.push(2, GFP_KERNEL)?; + /// + /// v.shrink_to(0, GFP_KERNEL)?; + /// # Ok::<(), Error>(()) + /// ``` + pub fn shrink_to(&mut self, min_capacity: usize, flags: Flags) -> Result<(), AllocError> { + let target_cap = core::cmp::max(self.len(), min_capacity); + + if self.capacity() <= target_cap { + return Ok(()); + } + + if Self::is_zst() { + return Ok(()); + } + + // For kmalloc allocations, delegate to realloc() and let the allocator decide + // whether shrinking is worthwhile. + // + // SAFETY: `self.ptr` points to a valid `KVmalloc` allocation. + if !unsafe { bindings::is_vmalloc_addr(self.ptr.as_ptr().cast()) } { + let new_layout = ArrayLayout::::new(target_cap).map_err(|_| AllocError)?; + + // SAFETY: + // - `self.ptr` is valid and was previously allocated with `KVmalloc`. + // - `self.layout` matches the `ArrayLayout` of the preceding allocation. + let ptr = unsafe { + KVmalloc::realloc( + Some(self.ptr.cast()), + new_layout.into(), + self.layout.into(), + flags, + NumaNode::NO_NODE, + )? + }; + + self.ptr = ptr.cast(); + self.layout = new_layout; + return Ok(()); + } + + // Only shrink if we would free at least one page. + let current_size = self.capacity() * core::mem::size_of::(); + let target_size = target_cap * core::mem::size_of::(); + let current_pages = current_size.div_ceil(PAGE_SIZE); + let target_pages = target_size.div_ceil(PAGE_SIZE); + + if current_pages <= target_pages { + return Ok(()); + } + + if target_cap == 0 { + if !self.layout.is_empty() { + // SAFETY: + // - `self.ptr` was previously allocated with `KVmalloc`. + // - `self.layout` matches the `ArrayLayout` of the preceding allocation. + unsafe { KVmalloc::free(self.ptr.cast(), self.layout.into()) }; + } + self.ptr = NonNull::dangling(); + self.layout = ArrayLayout::empty(); + return Ok(()); + } + + // SAFETY: `target_cap <= self.capacity()` and original capacity was valid. + let new_layout = unsafe { ArrayLayout::::new_unchecked(target_cap) }; + + let new_ptr = KVmalloc::alloc(new_layout.into(), flags, NumaNode::NO_NODE)?; + + // SAFETY: + // - `self.as_ptr()` is valid for reads of `self.len()` elements of `T`. + // - `new_ptr` is valid for writes of at least `target_cap >= self.len()` elements. + // - The two allocations do not overlap since `new_ptr` is freshly allocated. + // - Both pointers are properly aligned for `T`. + unsafe { + ptr::copy_nonoverlapping(self.as_ptr(), new_ptr.as_ptr().cast::(), self.len()) + }; + + // SAFETY: + // - `self.ptr` was previously allocated with `KVmalloc`. + // - `self.layout` matches the `ArrayLayout` of the preceding allocation. + unsafe { KVmalloc::free(self.ptr.cast(), self.layout.into()) }; + + self.ptr = new_ptr.cast::(); + self.layout = new_layout; + + Ok(()) + } +} impl Vec { /// Extend the vector by `n` clones of `value`. From fbfc0d615368ddf71899dbea2205e741c79b23e8 Mon Sep 17 00:00:00 2001 From: Shivam Kalra Date: Mon, 16 Feb 2026 19:39:56 +0530 Subject: [PATCH 078/405] rust: alloc: add KUnit tests for KVVec shrink_to Add comprehensive KUnit tests for the shrink_to method for KVVec. The tests verify: - Basic shrinking from multiple pages to fewer pages with data integrity preservation - Empty vector shrinking to zero capacity - No-op behavior when shrinking to a larger capacity than current - Respect for min_capacity parameter when larger than vector length These tests ensure that the shrinking logic correctly identifies when memory can be reclaimed (by freeing at least one page) and that data integrity is maintained throughout shrink operations. Reviewed-by: Alice Ryhl Acked-by: Danilo Krummrich Signed-off-by: Shivam Kalra Link: https://patch.msgid.link/20260216-binder-shrink-vec-v3-v6-2-ece8e8593e53@zohomail.in Signed-off-by: Greg Kroah-Hartman --- rust/kernel/alloc/kvec.rs | 102 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs index e7bc439538e4..6438385e4322 100644 --- a/rust/kernel/alloc/kvec.rs +++ b/rust/kernel/alloc/kvec.rs @@ -1510,4 +1510,106 @@ fn add(value: &mut [bool]) { func.push_within_capacity(false).unwrap(); } } + + #[test] + fn test_kvvec_shrink_to() { + use crate::page::PAGE_SIZE; + + // Create a vector with capacity spanning multiple pages. + let mut v = KVVec::::with_capacity(PAGE_SIZE * 4, GFP_KERNEL).unwrap(); + + // Add a few elements. + v.push(1, GFP_KERNEL).unwrap(); + v.push(2, GFP_KERNEL).unwrap(); + v.push(3, GFP_KERNEL).unwrap(); + + let initial_capacity = v.capacity(); + assert!(initial_capacity >= PAGE_SIZE * 4); + + // Shrink to a capacity that would free at least one page. + v.shrink_to(PAGE_SIZE, GFP_KERNEL).unwrap(); + + // Capacity should have been reduced. + assert!(v.capacity() < initial_capacity); + assert!(v.capacity() >= PAGE_SIZE); + + // Elements should be preserved. + assert_eq!(v.len(), 3); + assert_eq!(v[0], 1); + assert_eq!(v[1], 2); + assert_eq!(v[2], 3); + + // Shrink to zero (should shrink to len). + v.shrink_to(0, GFP_KERNEL).unwrap(); + + // Capacity should be at least the length. + assert!(v.capacity() >= v.len()); + + // Elements should still be preserved. + assert_eq!(v.len(), 3); + assert_eq!(v[0], 1); + assert_eq!(v[1], 2); + assert_eq!(v[2], 3); + } + + #[test] + fn test_kvvec_shrink_to_empty() { + use crate::page::PAGE_SIZE; + + // Create a vector with large capacity but no elements. + let mut v = KVVec::::with_capacity(PAGE_SIZE * 4, GFP_KERNEL).unwrap(); + + assert!(v.is_empty()); + + // Shrink empty vector to zero. + v.shrink_to(0, GFP_KERNEL).unwrap(); + + // Should have freed the allocation. + assert_eq!(v.capacity(), 0); + assert!(v.is_empty()); + } + + #[test] + fn test_kvvec_shrink_to_no_op() { + use crate::page::PAGE_SIZE; + + // Create a small vector. + let mut v = KVVec::::with_capacity(PAGE_SIZE, GFP_KERNEL).unwrap(); + v.push(1, GFP_KERNEL).unwrap(); + + let capacity_before = v.capacity(); + + // Try to shrink to a capacity larger than current - should be no-op. + v.shrink_to(capacity_before + 100, GFP_KERNEL).unwrap(); + + assert_eq!(v.capacity(), capacity_before); + assert_eq!(v.len(), 1); + assert_eq!(v[0], 1); + } + + #[test] + fn test_kvvec_shrink_to_respects_min_capacity() { + use crate::page::PAGE_SIZE; + + // Create a vector with large capacity. + let mut v = KVVec::::with_capacity(PAGE_SIZE * 4, GFP_KERNEL).unwrap(); + + // Add some elements. + for i in 0..10u8 { + v.push(i, GFP_KERNEL).unwrap(); + } + + // Shrink to a min_capacity larger than length. + let min_cap = PAGE_SIZE * 2; + v.shrink_to(min_cap, GFP_KERNEL).unwrap(); + + // Capacity should be at least min_capacity. + assert!(v.capacity() >= min_cap); + + // All elements preserved. + assert_eq!(v.len(), 10); + for i in 0..10u8 { + assert_eq!(v[i as usize], i); + } + } } From 34268365a9e9424e38083c8f318cc34b153dcb07 Mon Sep 17 00:00:00 2001 From: Shivam Kalra Date: Mon, 16 Feb 2026 19:39:57 +0530 Subject: [PATCH 079/405] rust_binder: shrink all_procs when deregistering processes When a process is deregistered from the binder context, the all_procs vector may have significant unused capacity. Add logic to shrink the vector using a conservative strategy that prevents shrink-then-regrow oscillation. The shrinking strategy triggers when length drops below 1/4 of capacity, and shrinks to twice the current length rather than to the exact length. This provides hysteresis to avoid repeated reallocations when the process count fluctuates. The shrink operation uses GFP_KERNEL and is allowed to fail gracefully since it is purely an optimization. The vector remains valid and functional even if shrinking fails. Suggested-by: Alice Ryhl Reviewed-by: Alice Ryhl Signed-off-by: Shivam Kalra Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260216-binder-shrink-vec-v3-v6-3-ece8e8593e53@zohomail.in Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/context.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/android/binder/context.rs b/drivers/android/binder/context.rs index 9cf437c025a2..ddddb66b3557 100644 --- a/drivers/android/binder/context.rs +++ b/drivers/android/binder/context.rs @@ -94,6 +94,17 @@ pub(crate) fn deregister_process(self: &Arc, proc: &Arc) { } let mut manager = self.manager.lock(); manager.all_procs.retain(|p| !Arc::ptr_eq(p, proc)); + + // Shrink the vector if it has significant unused capacity to avoid memory waste, + // but use a conservative strategy to prevent shrink-then-regrow oscillation. + // Only shrink when length drops below 1/4 of capacity, and shrink to twice the length. + let len = manager.all_procs.len(); + let cap = manager.all_procs.capacity(); + if len < cap / 4 { + // Shrink to twice the current length. Ignore allocation failures since this + // is just an optimization; the vector remains valid even if shrinking fails. + let _ = manager.all_procs.shrink_to(len * 2, GFP_KERNEL); + } } pub(crate) fn set_manager_node(&self, node_ref: NodeRef) -> Result { From 9047ea8defe2b91ea5ca88b52862e85bae0314f1 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 16:01:30 +0200 Subject: [PATCH 080/405] iio: frequency: adrf6780: add dev variable Introduce a local struct device pointer in functions that reference &spi->dev for device-managed resource calls and device property reads, improving code readability. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adrf6780.c | 33 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/drivers/iio/frequency/adrf6780.c b/drivers/iio/frequency/adrf6780.c index a7a21f929970..1899995f7b20 100644 --- a/drivers/iio/frequency/adrf6780.c +++ b/drivers/iio/frequency/adrf6780.c @@ -426,18 +426,18 @@ static int adrf6780_init(struct adrf6780_state *st) static void adrf6780_properties_parse(struct adrf6780_state *st) { - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; - st->vga_buff_en = device_property_read_bool(&spi->dev, "adi,vga-buff-en"); - st->lo_buff_en = device_property_read_bool(&spi->dev, "adi,lo-buff-en"); - st->if_mode_en = device_property_read_bool(&spi->dev, "adi,if-mode-en"); - st->iq_mode_en = device_property_read_bool(&spi->dev, "adi,iq-mode-en"); - st->lo_x2_en = device_property_read_bool(&spi->dev, "adi,lo-x2-en"); - st->lo_ppf_en = device_property_read_bool(&spi->dev, "adi,lo-ppf-en"); - st->lo_en = device_property_read_bool(&spi->dev, "adi,lo-en"); - st->uc_bias_en = device_property_read_bool(&spi->dev, "adi,uc-bias-en"); - st->lo_sideband = device_property_read_bool(&spi->dev, "adi,lo-sideband"); - st->vdet_out_en = device_property_read_bool(&spi->dev, "adi,vdet-out-en"); + st->vga_buff_en = device_property_read_bool(dev, "adi,vga-buff-en"); + st->lo_buff_en = device_property_read_bool(dev, "adi,lo-buff-en"); + st->if_mode_en = device_property_read_bool(dev, "adi,if-mode-en"); + st->iq_mode_en = device_property_read_bool(dev, "adi,iq-mode-en"); + st->lo_x2_en = device_property_read_bool(dev, "adi,lo-x2-en"); + st->lo_ppf_en = device_property_read_bool(dev, "adi,lo-ppf-en"); + st->lo_en = device_property_read_bool(dev, "adi,lo-en"); + st->uc_bias_en = device_property_read_bool(dev, "adi,uc-bias-en"); + st->lo_sideband = device_property_read_bool(dev, "adi,lo-sideband"); + st->vdet_out_en = device_property_read_bool(dev, "adi,vdet-out-en"); } static void adrf6780_powerdown(void *data) @@ -450,9 +450,10 @@ static int adrf6780_probe(struct spi_device *spi) { struct iio_dev *indio_dev; struct adrf6780_state *st; + struct device *dev = &spi->dev; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -467,9 +468,9 @@ static int adrf6780_probe(struct spi_device *spi) adrf6780_properties_parse(st); - st->clkin = devm_clk_get_enabled(&spi->dev, "lo_in"); + st->clkin = devm_clk_get_enabled(dev, "lo_in"); if (IS_ERR(st->clkin)) - return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), + return dev_err_probe(dev, PTR_ERR(st->clkin), "failed to get the LO input clock\n"); mutex_init(&st->lock); @@ -478,11 +479,11 @@ static int adrf6780_probe(struct spi_device *spi) if (ret) return ret; - ret = devm_add_action_or_reset(&spi->dev, adrf6780_powerdown, st); + ret = devm_add_action_or_reset(dev, adrf6780_powerdown, st); if (ret) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id adrf6780_id[] = { From 6faa419480ecc6647c29137e565f47c7e0d5ad23 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 16:01:31 +0200 Subject: [PATCH 081/405] iio: frequency: adrf6780: use dev_err_probe() Use dev_err_probe() consistently in the probe path to simplify error handling and ensure deferred probes are logged correctly. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adrf6780.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/iio/frequency/adrf6780.c b/drivers/iio/frequency/adrf6780.c index 1899995f7b20..257fd31a0b0e 100644 --- a/drivers/iio/frequency/adrf6780.c +++ b/drivers/iio/frequency/adrf6780.c @@ -346,23 +346,21 @@ static const struct iio_chan_spec adrf6780_channels[] = { static int adrf6780_reset(struct adrf6780_state *st) { int ret; - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_CONTROL, ADRF6780_SOFT_RESET_MSK, FIELD_PREP(ADRF6780_SOFT_RESET_MSK, 1)); - if (ret) { - dev_err(&spi->dev, "ADRF6780 SPI software reset failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "ADRF6780 SPI software reset failed.\n"); ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_CONTROL, ADRF6780_SOFT_RESET_MSK, FIELD_PREP(ADRF6780_SOFT_RESET_MSK, 0)); - if (ret) { - dev_err(&spi->dev, "ADRF6780 SPI software reset disable failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "ADRF6780 SPI software reset disable failed.\n"); return 0; } @@ -371,7 +369,7 @@ static int adrf6780_init(struct adrf6780_state *st) { int ret; unsigned int chip_id, enable_reg, enable_reg_msk; - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; /* Perform a software reset */ ret = adrf6780_reset(st); @@ -383,10 +381,9 @@ static int adrf6780_init(struct adrf6780_state *st) return ret; chip_id = FIELD_GET(ADRF6780_CHIP_ID_MSK, chip_id); - if (chip_id != ADRF6780_CHIP_ID) { - dev_err(&spi->dev, "ADRF6780 Invalid Chip ID.\n"); - return -EINVAL; - } + if (chip_id != ADRF6780_CHIP_ID) + return dev_err_probe(dev, -EINVAL, + "ADRF6780 Invalid Chip ID.\n"); enable_reg_msk = ADRF6780_VGA_BUFFER_EN_MSK | ADRF6780_DETECTOR_EN_MSK | From 4ced084be4cfbb8ee08a79a0b09e4db3ff1ed9fa Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 16:01:32 +0200 Subject: [PATCH 082/405] iio: frequency: admv1014: add dev variable Introduce a local struct device pointer in functions that reference &spi->dev for device-managed resource calls and device property reads, improving code readability. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/admv1014.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/iio/frequency/admv1014.c b/drivers/iio/frequency/admv1014.c index 7a8f92ec80a2..f829c4595df3 100644 --- a/drivers/iio/frequency/admv1014.c +++ b/drivers/iio/frequency/admv1014.c @@ -610,6 +610,7 @@ static int admv1014_init(struct admv1014_state *st) { unsigned int chip_id, enable_reg, enable_reg_msk; struct spi_device *spi = st->spi; + struct device *dev = &spi->dev; int ret; ret = regulator_bulk_enable(ADMV1014_NUM_REGULATORS, st->regulators); @@ -618,7 +619,7 @@ static int admv1014_init(struct admv1014_state *st) return ret; } - ret = devm_add_action_or_reset(&spi->dev, admv1014_reg_disable, st->regulators); + ret = devm_add_action_or_reset(dev, admv1014_reg_disable, st->regulators); if (ret) return ret; @@ -626,16 +627,16 @@ static int admv1014_init(struct admv1014_state *st) if (ret) return ret; - ret = devm_add_action_or_reset(&spi->dev, admv1014_clk_disable, st->clkin); + ret = devm_add_action_or_reset(dev, admv1014_clk_disable, st->clkin); if (ret) return ret; st->nb.notifier_call = admv1014_freq_change; - ret = devm_clk_notifier_register(&spi->dev, st->clkin, &st->nb); + ret = devm_clk_notifier_register(dev, st->clkin, &st->nb); if (ret) return ret; - ret = devm_add_action_or_reset(&spi->dev, admv1014_powerdown, st); + ret = devm_add_action_or_reset(dev, admv1014_powerdown, st); if (ret) return ret; @@ -712,13 +713,14 @@ static int admv1014_properties_parse(struct admv1014_state *st) { unsigned int i; struct spi_device *spi = st->spi; + struct device *dev = &spi->dev; int ret; - st->det_en = device_property_read_bool(&spi->dev, "adi,detector-enable"); + st->det_en = device_property_read_bool(dev, "adi,detector-enable"); - st->p1db_comp = device_property_read_bool(&spi->dev, "adi,p1db-compensation-enable"); + st->p1db_comp = device_property_read_bool(dev, "adi,p1db-compensation-enable"); - ret = device_property_match_property_string(&spi->dev, "adi,input-mode", + ret = device_property_match_property_string(dev, "adi,input-mode", input_mode_names, ARRAY_SIZE(input_mode_names)); if (ret >= 0) @@ -726,7 +728,7 @@ static int admv1014_properties_parse(struct admv1014_state *st) else st->input_mode = ADMV1014_IQ_MODE; - ret = device_property_match_property_string(&spi->dev, "adi,quad-se-mode", + ret = device_property_match_property_string(dev, "adi,quad-se-mode", quad_se_mode_names, ARRAY_SIZE(quad_se_mode_names)); if (ret >= 0) @@ -737,16 +739,16 @@ static int admv1014_properties_parse(struct admv1014_state *st) for (i = 0; i < ADMV1014_NUM_REGULATORS; ++i) st->regulators[i].supply = admv1014_reg_name[i]; - ret = devm_regulator_bulk_get(&st->spi->dev, ADMV1014_NUM_REGULATORS, + ret = devm_regulator_bulk_get(dev, ADMV1014_NUM_REGULATORS, st->regulators); if (ret) { dev_err(&spi->dev, "Failed to request regulators"); return ret; } - st->clkin = devm_clk_get(&spi->dev, "lo_in"); + st->clkin = devm_clk_get(dev, "lo_in"); if (IS_ERR(st->clkin)) - return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), + return dev_err_probe(dev, PTR_ERR(st->clkin), "failed to get the LO input clock\n"); return 0; @@ -756,9 +758,10 @@ static int admv1014_probe(struct spi_device *spi) { struct iio_dev *indio_dev; struct admv1014_state *st; + struct device *dev = &spi->dev; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -787,7 +790,7 @@ static int admv1014_probe(struct spi_device *spi) if (ret) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id admv1014_id[] = { From b343f010d55e7a1c5a43a8d2933db99cb84af48e Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 16:01:33 +0200 Subject: [PATCH 083/405] iio: frequency: admv1014: use dev_err_probe() Use dev_err_probe() consistently in the probe path to simplify error handling and ensure deferred probes are logged correctly. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/admv1014.c | 60 +++++++++++++------------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/drivers/iio/frequency/admv1014.c b/drivers/iio/frequency/admv1014.c index f829c4595df3..25e8cd8135ad 100644 --- a/drivers/iio/frequency/admv1014.c +++ b/drivers/iio/frequency/admv1014.c @@ -614,10 +614,8 @@ static int admv1014_init(struct admv1014_state *st) int ret; ret = regulator_bulk_enable(ADMV1014_NUM_REGULATORS, st->regulators); - if (ret) { - dev_err(&spi->dev, "Failed to enable regulators"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulators"); ret = devm_add_action_or_reset(dev, admv1014_reg_disable, st->regulators); if (ret) @@ -644,55 +642,47 @@ static int admv1014_init(struct admv1014_state *st) ret = __admv1014_spi_update_bits(st, ADMV1014_REG_SPI_CONTROL, ADMV1014_SPI_SOFT_RESET_MSK, FIELD_PREP(ADMV1014_SPI_SOFT_RESET_MSK, 1)); - if (ret) { - dev_err(&spi->dev, "ADMV1014 SPI software reset failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "ADMV1014 SPI software reset failed.\n"); ret = __admv1014_spi_update_bits(st, ADMV1014_REG_SPI_CONTROL, ADMV1014_SPI_SOFT_RESET_MSK, FIELD_PREP(ADMV1014_SPI_SOFT_RESET_MSK, 0)); - if (ret) { - dev_err(&spi->dev, "ADMV1014 SPI software reset disable failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "ADMV1014 SPI software reset disable failed.\n"); ret = __admv1014_spi_write(st, ADMV1014_REG_VVA_TEMP_COMP, 0x727C); - if (ret) { - dev_err(&spi->dev, "Writing default Temperature Compensation value failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "Writing default Temperature Compensation value failed.\n"); ret = __admv1014_spi_read(st, ADMV1014_REG_SPI_CONTROL, &chip_id); if (ret) return ret; chip_id = FIELD_GET(ADMV1014_CHIP_ID_MSK, chip_id); - if (chip_id != ADMV1014_CHIP_ID) { - dev_err(&spi->dev, "Invalid Chip ID.\n"); - return -EINVAL; - } + if (chip_id != ADMV1014_CHIP_ID) + return dev_err_probe(dev, -EINVAL, "Invalid Chip ID.\n"); ret = __admv1014_spi_update_bits(st, ADMV1014_REG_QUAD, ADMV1014_QUAD_SE_MODE_MSK, FIELD_PREP(ADMV1014_QUAD_SE_MODE_MSK, st->quad_se_mode)); - if (ret) { - dev_err(&spi->dev, "Writing Quad SE Mode failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "Writing Quad SE Mode failed.\n"); ret = admv1014_update_quad_filters(st); - if (ret) { - dev_err(&spi->dev, "Update Quad Filters failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "Update Quad Filters failed.\n"); ret = admv1014_update_vcm_settings(st); - if (ret) { - dev_err(&spi->dev, "Update VCM Settings failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "Update VCM Settings failed.\n"); enable_reg_msk = ADMV1014_P1DB_COMPENSATION_MSK | ADMV1014_IF_AMP_PD_MSK | @@ -741,10 +731,8 @@ static int admv1014_properties_parse(struct admv1014_state *st) ret = devm_regulator_bulk_get(dev, ADMV1014_NUM_REGULATORS, st->regulators); - if (ret) { - dev_err(&spi->dev, "Failed to request regulators"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to request regulators"); st->clkin = devm_clk_get(dev, "lo_in"); if (IS_ERR(st->clkin)) From e61b5bb0e91390adee41eaddc0a1a7d55d5652b2 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 16:01:34 +0200 Subject: [PATCH 084/405] iio: frequency: admv1013: add dev variable Introduce a local struct device pointer in functions that reference &spi->dev for device-managed resource calls and device property reads, improving code readability. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/admv1013.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c index d8e8d541990f..d29e288da011 100644 --- a/drivers/iio/frequency/admv1013.c +++ b/drivers/iio/frequency/admv1013.c @@ -518,11 +518,11 @@ static int admv1013_properties_parse(struct admv1013_state *st) { int ret; const char *str; - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; - st->det_en = device_property_read_bool(&spi->dev, "adi,detector-enable"); + st->det_en = device_property_read_bool(dev, "adi,detector-enable"); - ret = device_property_read_string(&spi->dev, "adi,input-mode", &str); + ret = device_property_read_string(dev, "adi,input-mode", &str); if (ret) st->input_mode = ADMV1013_IQ_MODE; @@ -533,7 +533,7 @@ static int admv1013_properties_parse(struct admv1013_state *st) else return -EINVAL; - ret = device_property_read_string(&spi->dev, "adi,quad-se-mode", &str); + ret = device_property_read_string(dev, "adi,quad-se-mode", &str); if (ret) st->quad_se_mode = ADMV1013_SE_MODE_DIFF; @@ -546,11 +546,11 @@ static int admv1013_properties_parse(struct admv1013_state *st) else return -EINVAL; - ret = devm_regulator_bulk_get_enable(&st->spi->dev, + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(admv1013_vcc_regs), admv1013_vcc_regs); if (ret) { - dev_err_probe(&spi->dev, ret, + dev_err_probe(dev, ret, "Failed to request VCC regulators\n"); return ret; } @@ -562,9 +562,10 @@ static int admv1013_probe(struct spi_device *spi) { struct iio_dev *indio_dev; struct admv1013_state *st; + struct device *dev = &spi->dev; int ret, vcm_uv; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -581,20 +582,20 @@ static int admv1013_probe(struct spi_device *spi) if (ret) return ret; - ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vcm"); + ret = devm_regulator_get_enable_read_voltage(dev, "vcm"); if (ret < 0) - return dev_err_probe(&spi->dev, ret, + return dev_err_probe(dev, ret, "failed to get the common-mode voltage\n"); vcm_uv = ret; - st->clkin = devm_clk_get_enabled(&spi->dev, "lo_in"); + st->clkin = devm_clk_get_enabled(dev, "lo_in"); if (IS_ERR(st->clkin)) - return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), + return dev_err_probe(dev, PTR_ERR(st->clkin), "failed to get the LO input clock\n"); st->nb.notifier_call = admv1013_freq_change; - ret = devm_clk_notifier_register(&spi->dev, st->clkin, &st->nb); + ret = devm_clk_notifier_register(dev, st->clkin, &st->nb); if (ret) return ret; @@ -606,11 +607,11 @@ static int admv1013_probe(struct spi_device *spi) return ret; } - ret = devm_add_action_or_reset(&spi->dev, admv1013_powerdown, st); + ret = devm_add_action_or_reset(dev, admv1013_powerdown, st); if (ret) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id admv1013_id[] = { From 9e9f38c44b2ed86c9a2a7583b9b88d8eec4b7793 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 16:01:35 +0200 Subject: [PATCH 085/405] iio: frequency: admv1013: use dev_err_probe() Use dev_err_probe() consistently in the probe path to simplify error handling and ensure deferred probes are logged correctly. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/admv1013.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c index d29e288da011..9202443ef445 100644 --- a/drivers/iio/frequency/admv1013.c +++ b/drivers/iio/frequency/admv1013.c @@ -441,7 +441,7 @@ static int admv1013_init(struct admv1013_state *st, int vcm_uv) { int ret; unsigned int data; - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; /* Perform a software reset */ ret = __admv1013_spi_update_bits(st, ADMV1013_REG_SPI_CONTROL, @@ -461,10 +461,8 @@ static int admv1013_init(struct admv1013_state *st, int vcm_uv) return ret; data = FIELD_GET(ADMV1013_CHIP_ID_MSK, data); - if (data != ADMV1013_CHIP_ID) { - dev_err(&spi->dev, "Invalid Chip ID.\n"); - return -EINVAL; - } + if (data != ADMV1013_CHIP_ID) + return dev_err_probe(dev, -EINVAL, "Invalid Chip ID.\n"); ret = __admv1013_spi_write(st, ADMV1013_REG_VVA_TEMP_COMP, 0xE700); if (ret) @@ -602,10 +600,8 @@ static int admv1013_probe(struct spi_device *spi) mutex_init(&st->lock); ret = admv1013_init(st, vcm_uv); - if (ret) { - dev_err(&spi->dev, "admv1013 init failed\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "admv1013 init failed\n"); ret = devm_add_action_or_reset(dev, admv1013_powerdown, st); if (ret) From b2d2a6ea12a1a47fbab515b2a1290384ecc7fef7 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 16:01:36 +0200 Subject: [PATCH 086/405] iio: frequency: adf4377: add dev variable Introduce a local struct device pointer in functions that reference &spi->dev for device-managed resource calls and device property reads, improving code readability. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adf4377.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/iio/frequency/adf4377.c b/drivers/iio/frequency/adf4377.c index fa686f785fa4..c2ae6f2012ce 100644 --- a/drivers/iio/frequency/adf4377.c +++ b/drivers/iio/frequency/adf4377.c @@ -882,35 +882,35 @@ static const struct iio_chan_spec adf4377_channels[] = { static int adf4377_properties_parse(struct adf4377_state *st) { - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; int ret; - st->clkin = devm_clk_get_enabled(&spi->dev, "ref_in"); + st->clkin = devm_clk_get_enabled(dev, "ref_in"); if (IS_ERR(st->clkin)) - return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), + return dev_err_probe(dev, PTR_ERR(st->clkin), "failed to get the reference input clock\n"); - st->gpio_ce = devm_gpiod_get_optional(&st->spi->dev, "chip-enable", + st->gpio_ce = devm_gpiod_get_optional(dev, "chip-enable", GPIOD_OUT_LOW); if (IS_ERR(st->gpio_ce)) - return dev_err_probe(&spi->dev, PTR_ERR(st->gpio_ce), + return dev_err_probe(dev, PTR_ERR(st->gpio_ce), "failed to get the CE GPIO\n"); - st->gpio_enclk1 = devm_gpiod_get_optional(&st->spi->dev, "clk1-enable", + st->gpio_enclk1 = devm_gpiod_get_optional(dev, "clk1-enable", GPIOD_OUT_LOW); if (IS_ERR(st->gpio_enclk1)) - return dev_err_probe(&spi->dev, PTR_ERR(st->gpio_enclk1), + return dev_err_probe(dev, PTR_ERR(st->gpio_enclk1), "failed to get the CE GPIO\n"); if (st->chip_info->has_gpio_enclk2) { - st->gpio_enclk2 = devm_gpiod_get_optional(&st->spi->dev, "clk2-enable", + st->gpio_enclk2 = devm_gpiod_get_optional(dev, "clk2-enable", GPIOD_OUT_LOW); if (IS_ERR(st->gpio_enclk2)) - return dev_err_probe(&spi->dev, PTR_ERR(st->gpio_enclk2), + return dev_err_probe(dev, PTR_ERR(st->gpio_enclk2), "failed to get the CE GPIO\n"); } - ret = device_property_match_property_string(&spi->dev, "adi,muxout-select", + ret = device_property_match_property_string(dev, "adi,muxout-select", adf4377_muxout_modes, ARRAY_SIZE(adf4377_muxout_modes)); if (ret >= 0) @@ -1055,9 +1055,10 @@ static int adf4377_probe(struct spi_device *spi) struct iio_dev *indio_dev; struct regmap *regmap; struct adf4377_state *st; + struct device *dev = &spi->dev; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -1080,7 +1081,7 @@ static int adf4377_probe(struct spi_device *spi) return ret; st->nb.notifier_call = adf4377_freq_change; - ret = devm_clk_notifier_register(&spi->dev, st->clkin, &st->nb); + ret = devm_clk_notifier_register(dev, st->clkin, &st->nb); if (ret) return ret; @@ -1097,7 +1098,7 @@ static int adf4377_probe(struct spi_device *spi) indio_dev->num_channels = ARRAY_SIZE(adf4377_channels); } - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id adf4377_id[] = { From 3e1c0b9501c227f2e567b20386774acc19f26c1c Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 16:01:37 +0200 Subject: [PATCH 087/405] iio: frequency: adf4377: use dev_err_probe() Use dev_err_probe() consistently in the probe path to simplify error handling and ensure deferred probes are logged correctly. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adf4377.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/drivers/iio/frequency/adf4377.c b/drivers/iio/frequency/adf4377.c index c2ae6f2012ce..ce3a396624c3 100644 --- a/drivers/iio/frequency/adf4377.c +++ b/drivers/iio/frequency/adf4377.c @@ -706,23 +706,20 @@ static void adf4377_gpio_init(struct adf4377_state *st) static int adf4377_init(struct adf4377_state *st) { - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; int ret; adf4377_gpio_init(st); ret = adf4377_soft_reset(st); - if (ret) { - dev_err(&spi->dev, "Failed to soft reset.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to soft reset.\n"); ret = regmap_multi_reg_write(st->regmap, adf4377_reg_defaults, ARRAY_SIZE(adf4377_reg_defaults)); - if (ret) { - dev_err(&spi->dev, "Failed to set default registers.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "Failed to set default registers.\n"); ret = regmap_update_bits(st->regmap, 0x00, ADF4377_0000_SDO_ACTIVE_MSK | ADF4377_0000_SDO_ACTIVE_R_MSK, @@ -730,10 +727,9 @@ static int adf4377_init(struct adf4377_state *st) ADF4377_0000_SDO_ACTIVE_SPI_4W) | FIELD_PREP(ADF4377_0000_SDO_ACTIVE_R_MSK, ADF4377_0000_SDO_ACTIVE_SPI_4W)); - if (ret) { - dev_err(&spi->dev, "Failed to set 4-Wire Operation.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "Failed to set 4-Wire Operation.\n"); st->clkin_freq = clk_get_rate(st->clkin); @@ -747,10 +743,9 @@ static int adf4377_init(struct adf4377_state *st) FIELD_PREP(ADF4377_001A_PD_PFDCP_MSK, 0) | FIELD_PREP(ADF4377_001A_PD_CLKOUT1_MSK, 0) | FIELD_PREP(ADF4377_001A_PD_CLKOUT2_MSK, 0)); - if (ret) { - dev_err(&spi->dev, "Failed to set power down registers.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "Failed to set power down registers.\n"); /* Set Mux Output */ ret = regmap_update_bits(st->regmap, 0x1D, From 70b9d4af16759ff606756fbd09bb5d167c4917fe Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 16:01:38 +0200 Subject: [PATCH 088/405] iio: dac: ad7293: add dev variable Introduce a local struct device pointer in functions that reference &spi->dev for device-managed resource calls and device property reads, improving code readability. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad7293.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/iio/dac/ad7293.c b/drivers/iio/dac/ad7293.c index c3797e40cdd9..c86c54d53df1 100644 --- a/drivers/iio/dac/ad7293.c +++ b/drivers/iio/dac/ad7293.c @@ -776,27 +776,27 @@ static int ad7293_reset(struct ad7293_state *st) static int ad7293_properties_parse(struct ad7293_state *st) { - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; int ret; - ret = devm_regulator_get_enable(&spi->dev, "avdd"); + ret = devm_regulator_get_enable(dev, "avdd"); if (ret) - return dev_err_probe(&spi->dev, ret, "failed to enable AVDD\n"); + return dev_err_probe(dev, ret, "failed to enable AVDD\n"); - ret = devm_regulator_get_enable(&spi->dev, "vdrive"); + ret = devm_regulator_get_enable(dev, "vdrive"); if (ret) - return dev_err_probe(&spi->dev, ret, "failed to enable VDRIVE\n"); + return dev_err_probe(dev, ret, "failed to enable VDRIVE\n"); - ret = devm_regulator_get_enable_optional(&spi->dev, "vrefin"); + ret = devm_regulator_get_enable_optional(dev, "vrefin"); if (ret < 0 && ret != -ENODEV) - return dev_err_probe(&spi->dev, ret, "failed to enable VREFIN\n"); + return dev_err_probe(dev, ret, "failed to enable VREFIN\n"); st->vrefin_en = ret != -ENODEV; - st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset", + st->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(st->gpio_reset)) - return dev_err_probe(&spi->dev, PTR_ERR(st->gpio_reset), + return dev_err_probe(dev, PTR_ERR(st->gpio_reset), "failed to get the reset GPIO\n"); return 0; @@ -845,9 +845,10 @@ static int ad7293_probe(struct spi_device *spi) { struct iio_dev *indio_dev; struct ad7293_state *st; + struct device *dev = &spi->dev; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -867,7 +868,7 @@ static int ad7293_probe(struct spi_device *spi) if (ret) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id ad7293_id[] = { From bbb8e1206716b6d4c46c931d741140098618f8fb Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 16:01:39 +0200 Subject: [PATCH 089/405] iio: dac: ad7293: use dev_err_probe() Use dev_err_probe() consistently in the probe path to simplify error handling and ensure deferred probes are logged correctly. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad7293.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/iio/dac/ad7293.c b/drivers/iio/dac/ad7293.c index c86c54d53df1..df6f126abf05 100644 --- a/drivers/iio/dac/ad7293.c +++ b/drivers/iio/dac/ad7293.c @@ -806,7 +806,7 @@ static int ad7293_init(struct ad7293_state *st) { int ret; u16 chip_id; - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; ret = ad7293_properties_parse(st); if (ret) @@ -821,10 +821,8 @@ static int ad7293_init(struct ad7293_state *st) if (ret) return ret; - if (chip_id != AD7293_CHIP_ID) { - dev_err(&spi->dev, "Invalid Chip ID.\n"); - return -EINVAL; - } + if (chip_id != AD7293_CHIP_ID) + return dev_err_probe(dev, -EINVAL, "Invalid Chip ID.\n"); if (!st->vrefin_en) return __ad7293_spi_update_bits(st, AD7293_REG_GENERAL, From 008120ca31a70a018b9fae7e887de8a32b1bd9a4 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 16:01:40 +0200 Subject: [PATCH 090/405] iio: filter: admv8818: add dev variable Introduce a local struct device pointer in functions that reference &spi->dev for device-managed resource calls and device property reads, improving code readability. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/filter/admv8818.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/iio/filter/admv8818.c b/drivers/iio/filter/admv8818.c index e494fd33911b..c83e729534e7 100644 --- a/drivers/iio/filter/admv8818.c +++ b/drivers/iio/filter/admv8818.c @@ -701,12 +701,12 @@ static int admv8818_init(struct admv8818_state *st) static int admv8818_clk_setup(struct admv8818_state *st) { - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; int ret; - st->clkin = devm_clk_get_optional(&spi->dev, "rf_in"); + st->clkin = devm_clk_get_optional(dev, "rf_in"); if (IS_ERR(st->clkin)) - return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), + return dev_err_probe(dev, PTR_ERR(st->clkin), "failed to get the input clock\n"); else if (!st->clkin) return 0; @@ -715,7 +715,7 @@ static int admv8818_clk_setup(struct admv8818_state *st) if (ret) return ret; - ret = devm_add_action_or_reset(&spi->dev, admv8818_clk_disable, st); + ret = devm_add_action_or_reset(dev, admv8818_clk_disable, st); if (ret) return ret; @@ -724,16 +724,16 @@ static int admv8818_clk_setup(struct admv8818_state *st) if (ret < 0) return ret; - return devm_add_action_or_reset(&spi->dev, admv8818_clk_notifier_unreg, st); + return devm_add_action_or_reset(dev, admv8818_clk_notifier_unreg, st); } static int admv8818_read_properties(struct admv8818_state *st) { - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; u32 mhz; int ret; - ret = device_property_read_u32(&spi->dev, "adi,lpf-margin-mhz", &mhz); + ret = device_property_read_u32(dev, "adi,lpf-margin-mhz", &mhz); if (ret == 0) st->lpf_margin_hz = (u64)mhz * HZ_PER_MHZ; else if (ret == -EINVAL) @@ -742,7 +742,7 @@ static int admv8818_read_properties(struct admv8818_state *st) return ret; - ret = device_property_read_u32(&spi->dev, "adi,hpf-margin-mhz", &mhz); + ret = device_property_read_u32(dev, "adi,hpf-margin-mhz", &mhz); if (ret == 0) st->hpf_margin_hz = (u64)mhz * HZ_PER_MHZ; else if (ret == -EINVAL) @@ -758,9 +758,10 @@ static int admv8818_probe(struct spi_device *spi) struct iio_dev *indio_dev; struct regmap *regmap; struct admv8818_state *st; + struct device *dev = &spi->dev; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -792,7 +793,7 @@ static int admv8818_probe(struct spi_device *spi) if (ret) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id admv8818_id[] = { From 82035b16c47e3f6378ddf6b3df8c2e220d3d2085 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 16:01:41 +0200 Subject: [PATCH 091/405] iio: filter: admv8818: use dev_err_probe() Use dev_err_probe() consistently in the probe path to simplify error handling and ensure deferred probes are logged correctly. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/filter/admv8818.c | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/drivers/iio/filter/admv8818.c b/drivers/iio/filter/admv8818.c index c83e729534e7..a4984b867248 100644 --- a/drivers/iio/filter/admv8818.c +++ b/drivers/iio/filter/admv8818.c @@ -657,41 +657,34 @@ static void admv8818_clk_disable(void *data) static int admv8818_init(struct admv8818_state *st) { int ret; - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; unsigned int chip_id; ret = regmap_write(st->regmap, ADMV8818_REG_SPI_CONFIG_A, ADMV8818_SOFTRESET_N_MSK | ADMV8818_SOFTRESET_MSK); - if (ret) { - dev_err(&spi->dev, "ADMV8818 Soft Reset failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "ADMV8818 Soft Reset failed.\n"); ret = regmap_write(st->regmap, ADMV8818_REG_SPI_CONFIG_A, ADMV8818_SDOACTIVE_N_MSK | ADMV8818_SDOACTIVE_MSK); - if (ret) { - dev_err(&spi->dev, "ADMV8818 SDO Enable failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "ADMV8818 SDO Enable failed.\n"); ret = regmap_read(st->regmap, ADMV8818_REG_CHIPTYPE, &chip_id); - if (ret) { - dev_err(&spi->dev, "ADMV8818 Chip ID read failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "ADMV8818 Chip ID read failed.\n"); - if (chip_id != 0x1) { - dev_err(&spi->dev, "ADMV8818 Invalid Chip ID.\n"); - return -EINVAL; - } + if (chip_id != 0x1) + return dev_err_probe(dev, -EINVAL, + "ADMV8818 Invalid Chip ID.\n"); ret = regmap_update_bits(st->regmap, ADMV8818_REG_SPI_CONFIG_B, ADMV8818_SINGLE_INSTRUCTION_MSK, FIELD_PREP(ADMV8818_SINGLE_INSTRUCTION_MSK, 1)); - if (ret) { - dev_err(&spi->dev, "ADMV8818 Single Instruction failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "ADMV8818 Single Instruction failed.\n"); if (st->clkin) return admv8818_rfin_band_select(st); From aac15061093d14b216179b66adfff4af53dd19ab Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Fri, 27 Feb 2026 12:58:13 +0400 Subject: [PATCH 092/405] iio: adc: ade9000: remove unused AD9000_CHANNELS_PER_PHASE macro Remove the AD9000_CHANNELS_PER_PHASE macro which is unused, has a misspelled prefix (AD9000 instead of ADE9000), and has an incorrect value (10 instead of 11, there are 33 total channels and 3 phases, so 11 per phase). Signed-off-by: Giorgi Tchankvetadze Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ade9000.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index 5dcc26a08970..4be8df34428d 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -283,7 +283,6 @@ enum ade9000_wfb_cfg { #define ADE9000_PHASE_C_POS_BIT BIT(6) #define ADE9000_MAX_PHASE_NR 3 -#define AD9000_CHANNELS_PER_PHASE 10 /* * Calculate register address for multi-phase device. From e830a8894ecbacd20bc8bcff9a726682ca5ef6f9 Mon Sep 17 00:00:00 2001 From: Bhargav Joshi Date: Fri, 27 Feb 2026 04:40:55 +0530 Subject: [PATCH 093/405] iio: frequency: ad9523: fix implicit variable macros The macros AD9523_CLK_DIST_DIV_PHASE_REV(x) and AD9523_CLK_DIST_DIV_REV(x) implicitly relied on the variable named 'ret' instead of using passed argument '(x)'. Update the macros to explicitly use the argument '(x)' for their operations. This also resolves the following checkpatch.pl warning: Argument '(x)' is not used in function-like macro. Signed-off-by: Bhargav Joshi Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/ad9523.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c index 63c485e9e44c..5725ab62e0fd 100644 --- a/drivers/iio/frequency/ad9523.c +++ b/drivers/iio/frequency/ad9523.c @@ -167,9 +167,9 @@ /* AD9523_CHANNEL_CLOCK_DIST */ #define AD9523_CLK_DIST_DIV_PHASE(x) (((x) & 0x3F) << 18) -#define AD9523_CLK_DIST_DIV_PHASE_REV(x) ((ret >> 18) & 0x3F) +#define AD9523_CLK_DIST_DIV_PHASE_REV(x) (((x) >> 18) & 0x3F) #define AD9523_CLK_DIST_DIV(x) ((((x) - 1) & 0x3FF) << 8) -#define AD9523_CLK_DIST_DIV_REV(x) (((ret >> 8) & 0x3FF) + 1) +#define AD9523_CLK_DIST_DIV_REV(x) ((((x) >> 8) & 0x3FF) + 1) #define AD9523_CLK_DIST_INV_DIV_OUTPUT_EN (1 << 7) #define AD9523_CLK_DIST_IGNORE_SYNC_EN (1 << 6) #define AD9523_CLK_DIST_PWR_DOWN_EN (1 << 5) From 787c9a9cdc5156d6465129caa9f9276911901f56 Mon Sep 17 00:00:00 2001 From: Bhargav Joshi Date: Fri, 27 Feb 2026 04:40:56 +0530 Subject: [PATCH 094/405] iio: frequency: ad9523: fix multi-line dereferences Platform data pointer dereferences for pll1_charge_pump_current_nA and pll2_charge_pump_current_nA were split across multiple lines. Bring the dereference chains onto a single line. This resolves the following checkpatch.pl warnings: WARNING: Avoid multiple line dereference - prefer 'pdata->pll1_charge_pump_current_nA' WARNING: Avoid multiple line dereference - prefer 'pdata->pll2_charge_pump_current_nA' Signed-off-by: Bhargav Joshi Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/ad9523.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c index 5725ab62e0fd..6daa2ea354a8 100644 --- a/drivers/iio/frequency/ad9523.c +++ b/drivers/iio/frequency/ad9523.c @@ -797,8 +797,7 @@ static int ad9523_setup(struct iio_dev *indio_dev) return ret; ret = ad9523_write(indio_dev, AD9523_PLL1_CHARGE_PUMP_CTRL, - AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(pdata-> - pll1_charge_pump_current_nA) | + AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(pdata->pll1_charge_pump_current_nA) | AD9523_PLL1_CHARGE_PUMP_MODE_NORMAL | AD9523_PLL1_BACKLASH_PW_MIN); if (ret < 0) @@ -842,8 +841,7 @@ static int ad9523_setup(struct iio_dev *indio_dev) */ ret = ad9523_write(indio_dev, AD9523_PLL2_CHARGE_PUMP, - AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(pdata-> - pll2_charge_pump_current_nA)); + AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(pdata->pll2_charge_pump_current_nA)); if (ret < 0) return ret; From dfe5e8fb17518e5af346a5e8139e704d63f82a11 Mon Sep 17 00:00:00 2001 From: Bhargav Joshi Date: Fri, 27 Feb 2026 04:40:57 +0530 Subject: [PATCH 095/405] iio: frequency: ad9523: use octal permissions The driver currently defines device attributes using symbolic permission flags (S_IRUGO and S_IWUSR). Update these to use octal permissions (0444 and 0200) to resolve checkpatch warnings. Signed-off-by: Bhargav Joshi Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/ad9523.c | 60 ++++++++++++---------------------- 1 file changed, 20 insertions(+), 40 deletions(-) diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c index 6daa2ea354a8..ad32eb66edca 100644 --- a/drivers/iio/frequency/ad9523.c +++ b/drivers/iio/frequency/ad9523.c @@ -558,55 +558,35 @@ static ssize_t ad9523_show(struct device *dev, return ret; } -static IIO_DEVICE_ATTR(pll1_locked, S_IRUGO, - ad9523_show, - NULL, - AD9523_STAT_PLL1_LD); +static IIO_DEVICE_ATTR(pll1_locked, 0444, ad9523_show, NULL, + AD9523_STAT_PLL1_LD); -static IIO_DEVICE_ATTR(pll2_locked, S_IRUGO, - ad9523_show, - NULL, - AD9523_STAT_PLL2_LD); +static IIO_DEVICE_ATTR(pll2_locked, 0444, ad9523_show, NULL, + AD9523_STAT_PLL2_LD); -static IIO_DEVICE_ATTR(pll1_reference_clk_a_present, S_IRUGO, - ad9523_show, - NULL, - AD9523_STAT_REFA); +static IIO_DEVICE_ATTR(pll1_reference_clk_a_present, 0444, ad9523_show, NULL, + AD9523_STAT_REFA); -static IIO_DEVICE_ATTR(pll1_reference_clk_b_present, S_IRUGO, - ad9523_show, - NULL, - AD9523_STAT_REFB); +static IIO_DEVICE_ATTR(pll1_reference_clk_b_present, 0444, ad9523_show, NULL, + AD9523_STAT_REFB); -static IIO_DEVICE_ATTR(pll1_reference_clk_test_present, S_IRUGO, - ad9523_show, - NULL, - AD9523_STAT_REF_TEST); +static IIO_DEVICE_ATTR(pll1_reference_clk_test_present, 0444, ad9523_show, NULL, + AD9523_STAT_REF_TEST); -static IIO_DEVICE_ATTR(vcxo_clk_present, S_IRUGO, - ad9523_show, - NULL, - AD9523_STAT_VCXO); +static IIO_DEVICE_ATTR(vcxo_clk_present, 0444, ad9523_show, NULL, + AD9523_STAT_VCXO); -static IIO_DEVICE_ATTR(pll2_feedback_clk_present, S_IRUGO, - ad9523_show, - NULL, - AD9523_STAT_PLL2_FB_CLK); +static IIO_DEVICE_ATTR(pll2_feedback_clk_present, 0444, ad9523_show, NULL, + AD9523_STAT_PLL2_FB_CLK); -static IIO_DEVICE_ATTR(pll2_reference_clk_present, S_IRUGO, - ad9523_show, - NULL, - AD9523_STAT_PLL2_REF_CLK); +static IIO_DEVICE_ATTR(pll2_reference_clk_present, 0444, ad9523_show, NULL, + AD9523_STAT_PLL2_REF_CLK); -static IIO_DEVICE_ATTR(sync_dividers, S_IWUSR, - NULL, - ad9523_store, - AD9523_SYNC); +static IIO_DEVICE_ATTR(sync_dividers, 0200, NULL, ad9523_store, + AD9523_SYNC); -static IIO_DEVICE_ATTR(store_eeprom, S_IWUSR, - NULL, - ad9523_store, - AD9523_EEPROM); +static IIO_DEVICE_ATTR(store_eeprom, 0200, NULL, ad9523_store, + AD9523_EEPROM); static struct attribute *ad9523_attributes[] = { &iio_dev_attr_sync_dividers.dev_attr.attr, From 8021729acf21f4bf3c43866b8919b68968028478 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 25 Feb 2026 21:12:57 -0800 Subject: [PATCH 096/405] iio: tsl2772: fix all kernel-doc warnings Use the correct kernel-doc notation for struct members to eliminate kernel-doc warnings: Warning: include/linux/platform_data/tsl2772.h:88 struct member 'prox_diode' not described in 'tsl2772_settings' Warning: include/linux/platform_data/tsl2772.h:88 struct member 'prox_power' not described in 'tsl2772_settings' Signed-off-by: Randy Dunlap Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- include/linux/platform_data/tsl2772.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/platform_data/tsl2772.h b/include/linux/platform_data/tsl2772.h index f8ade15a35e2..f042e82b39c3 100644 --- a/include/linux/platform_data/tsl2772.h +++ b/include/linux/platform_data/tsl2772.h @@ -61,9 +61,9 @@ struct tsl2772_lux { * @prox_pulse_count: Number if proximity emitter pulses. * @prox_max_samples_cal: The number of samples that are taken when performing * a proximity calibration. - * @prox_diode Which diode(s) to use for driving the external + * @prox_diode: Which diode(s) to use for driving the external * LED(s) for proximity sensing. - * @prox_power The amount of power to use for the external LED(s). + * @prox_power: The amount of power to use for the external LED(s). */ struct tsl2772_settings { int als_time; From a1fe33e07b612d099684b7e7af0863f80f019691 Mon Sep 17 00:00:00 2001 From: Taha Ed-Dafili <0rayn.dev@gmail.com> Date: Thu, 26 Feb 2026 15:11:02 +0000 Subject: [PATCH 097/405] docs: iio: adxl345: grammar and formatting cleanups Correct several grammatical errors, typos, and pluralization issues throughout the ADXL345 documentation. Key changes include: - Changing 'generic' to 'general-purpose' - Correcting 'axis' to 'axes' in multiple tables and descriptions - Improving phrasing in the device attributes section - Fixing 'latent' to 'latency' in usage examples - Sorting the existing event attribute table alphabetically for better readability and to establish a clean baseline for future additions. Suggested-by: David Lechner Signed-off-by: Taha Ed-Dafili <0rayn.dev@gmail.com> Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- Documentation/iio/adxl345.rst | 40 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Documentation/iio/adxl345.rst b/Documentation/iio/adxl345.rst index bb19d64f67c3..0e8977345e9d 100644 --- a/Documentation/iio/adxl345.rst +++ b/Documentation/iio/adxl345.rst @@ -12,16 +12,16 @@ This driver supports Analog Device's ADXL345/375 on SPI/I2C bus. * `ADXL345 `_ * `ADXL375 `_ -The ADXL345 is a generic purpose low power, 3-axis accelerometer with selectable +The ADXL345 is a general-purpose, low-power, 3-axis accelerometer with selectable measurement ranges. The ADXL345 supports the ±2 g, ±4 g, ±8 g, and ±16 g ranges. 2. Device Attributes ==================== -Each IIO device, has a device folder under ``/sys/bus/iio/devices/iio:deviceX``, +Each IIO device has a device folder under ``/sys/bus/iio/devices/iio:deviceX``, where X is the IIO index of the device. Under these folders reside a set of device files, depending on the characteristics and features of the hardware -device in questions. These files are consistently generalized and documented in +device in question. These files are consistently generalized and documented in the IIO ABI documentation. The following table shows the ADXL345 related device files, found in the @@ -42,7 +42,7 @@ specific device folder path ``/sys/bus/iio/devices/iio:deviceX``. +-------------------------------------------+----------------------------------------------------------+ | in_accel_x_raw | Raw X-axis accelerometer channel value. | +-------------------------------------------+----------------------------------------------------------+ -| in_accel_y_calibbias | y-axis acceleration offset correction | +| in_accel_y_calibbias | Y-axis acceleration offset correction | +-------------------------------------------+----------------------------------------------------------+ | in_accel_y_raw | Raw Y-axis accelerometer channel value. | +-------------------------------------------+----------------------------------------------------------+ @@ -68,7 +68,7 @@ present, simply assume its value is 0. +-------------------------------------+---------------------------+ | Channel type | Measurement unit | +-------------------------------------+---------------------------+ -| Acceleration on X, Y, and Z axis | Meters per second squared | +| Acceleration on X, Y, and Z axes | Meters per second squared | +-------------------------------------+---------------------------+ Sensor Events @@ -78,8 +78,8 @@ Specific IIO events are triggered by their corresponding interrupts. The sensor driver supports either none or a single active interrupt (INT) line, selectable from the two available options: INT1 or INT2. The active INT line should be specified in the device tree. If no INT line is configured, the sensor defaults -to FIFO bypass mode, where event detection is disabled and only X, Y, and Z axis -measurements are available. +to FIFO bypass mode, where event detection is disabled and only individual +X, Y, and Z axis measurements are available. The table below lists the ADXL345-related device files located in the device-specific path: ``/sys/bus/iio/devices/iio:deviceX/events``. @@ -90,38 +90,38 @@ listed. +---------------------------------------------+---------------------------------------------+ | Event handle | Description | +---------------------------------------------+---------------------------------------------+ -| in_accel_gesture_doubletap_en | Enable double tap detection on all axis | +| in_accel_gesture_doubletap_en | Enable double tap detection on all axes | +---------------------------------------------+---------------------------------------------+ | in_accel_gesture_doubletap_reset_timeout | Double tap window in [us] | +---------------------------------------------+---------------------------------------------+ -| in_accel_gesture_doubletap_tap2_min_delay | Double tap latent in [us] | +| in_accel_gesture_doubletap_tap2_min_delay | Double tap latency in [us] | +---------------------------------------------+---------------------------------------------+ | in_accel_gesture_singletap_timeout | Single tap duration in [us] | +---------------------------------------------+---------------------------------------------+ | in_accel_gesture_singletap_value | Single tap threshold value in 62.5/LSB | +---------------------------------------------+---------------------------------------------+ -| in_accel_mag_falling_period | Inactivity time in seconds | -+---------------------------------------------+---------------------------------------------+ -| in_accel_mag_falling_value | Inactivity threshold value in 62.5/LSB | -+---------------------------------------------+---------------------------------------------+ -| in_accel_mag_adaptive_rising_en | Enable AC coupled activity on X axis | -+---------------------------------------------+---------------------------------------------+ | in_accel_mag_adaptive_falling_period | AC coupled inactivity time in seconds | +---------------------------------------------+---------------------------------------------+ | in_accel_mag_adaptive_falling_value | AC coupled inactivity threshold in 62.5/LSB | +---------------------------------------------+---------------------------------------------+ +| in_accel_mag_adaptive_rising_en | Enable AC coupled activity on X axis | ++---------------------------------------------+---------------------------------------------+ | in_accel_mag_adaptive_rising_value | AC coupled activity threshold in 62.5/LSB | +---------------------------------------------+---------------------------------------------+ +| in_accel_mag_falling_period | Inactivity time in seconds | ++---------------------------------------------+---------------------------------------------+ +| in_accel_mag_falling_value | Inactivity threshold value in 62.5/LSB | ++---------------------------------------------+---------------------------------------------+ | in_accel_mag_rising_en | Enable activity detection on X axis | +---------------------------------------------+---------------------------------------------+ | in_accel_mag_rising_value | Activity threshold value in 62.5/LSB | +---------------------------------------------+---------------------------------------------+ +| in_accel_x&y&z_mag_adaptive_falling_en | Enable AC coupled inactivity on all axes | ++---------------------------------------------+---------------------------------------------+ +| in_accel_x&y&z_mag_falling_en | Enable inactivity detection on all axes | ++---------------------------------------------+---------------------------------------------+ | in_accel_x_gesture_singletap_en | Enable single tap detection on X axis | +---------------------------------------------+---------------------------------------------+ -| in_accel_x&y&z_mag_falling_en | Enable inactivity detection on all axis | -+---------------------------------------------+---------------------------------------------+ -| in_accel_x&y&z_mag_adaptive_falling_en | Enable AC coupled inactivity on all axis | -+---------------------------------------------+---------------------------------------------+ | in_accel_y_gesture_singletap_en | Enable single tap detection on Y axis | +---------------------------------------------+---------------------------------------------+ | in_accel_z_gesture_singletap_en | Enable single tap detection on Z axis | @@ -330,7 +330,7 @@ Configure one or several events: ## doubletap, window [us] root:/sys/bus/iio/devices/iio:device0> echo 0.025 > ./events/in_accel_gesture_doubletap_reset_timeout - ## doubletap, latent [us] + ## doubletap, latency [us] root:/sys/bus/iio/devices/iio:device0> echo 0.025 > ./events/in_accel_gesture_doubletap_tap2_min_delay ## AC coupled activity, enable From 2a76a626670b2ef391da37f457e8e51f168432a6 Mon Sep 17 00:00:00 2001 From: Taha Ed-Dafili <0rayn.dev@gmail.com> Date: Thu, 26 Feb 2026 15:11:03 +0000 Subject: [PATCH 098/405] iio: core: Add IIO_EV_INFO_SCALE to event info Implement support for IIO_EV_INFO_SCALE in the internal enum iio_event_info to allow proper ABI compliance. This allows drivers (like the ADXL345) to expose event scale attributes using the standard IIO ABI rather than manual device attributes. Signed-off-by: Taha Ed-Dafili <0rayn.dev@gmail.com> Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-event.c | 1 + include/linux/iio/types.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index 4149efcd5539..a0d6fcf2a9c9 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -256,6 +256,7 @@ static const char * const iio_ev_info_text[] = { [IIO_EV_INFO_TAP2_MIN_DELAY] = "tap2_min_delay", [IIO_EV_INFO_RUNNING_PERIOD] = "runningperiod", [IIO_EV_INFO_RUNNING_COUNT] = "runningcount", + [IIO_EV_INFO_SCALE] = "scale", }; static enum iio_event_direction iio_ev_attr_dir(struct iio_dev_attr *attr) diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 34eebad12d2c..4e3099defc1d 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -21,6 +21,7 @@ enum iio_event_info { IIO_EV_INFO_TAP2_MIN_DELAY, IIO_EV_INFO_RUNNING_PERIOD, IIO_EV_INFO_RUNNING_COUNT, + IIO_EV_INFO_SCALE, }; #define IIO_VAL_INT 1 From da29db0bcc95fb554ce9969ab57ba8f84c405be7 Mon Sep 17 00:00:00 2001 From: Taha Ed-Dafili <0rayn.dev@gmail.com> Date: Thu, 26 Feb 2026 15:11:04 +0000 Subject: [PATCH 099/405] iio: accel: adxl345: Expose IIO_EV_INFO_VALUE for double tap The ADXL345 uses a single hardware register (ADXL345_REG_THRESH_TAP) to store the threshold for both single tap and double tap events. Currently, the driver only exposes the IIO_EV_INFO_VALUE attribute for the single tap event. However, the IIO ABI dictates that if an event is supported, its associated configuration attributes should be exposed to userspace. This applies even if writing to one channel property alters the value of another due to shared underlying hardware state. Add IIO_EV_INFO_VALUE to the double tap event specification to ensure full ABI compliance. Suggested-by: Jonathan Cameron Signed-off-by: Taha Ed-Dafili <0rayn.dev@gmail.com> Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl345_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c index 78e3f799ecc1..96d1417d77c6 100644 --- a/drivers/iio/accel/adxl345_core.c +++ b/drivers/iio/accel/adxl345_core.c @@ -235,7 +235,9 @@ static const struct iio_event_spec adxl345_events[] = { /* double tap */ .type = IIO_EV_TYPE_GESTURE, .dir = IIO_EV_DIR_DOUBLETAP, - .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + .mask_shared_by_type = + BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_RESET_TIMEOUT) | BIT(IIO_EV_INFO_TAP2_MIN_DELAY), }, From 9fb007705c77b85bfad2d3d4818ebcd5fcfa9571 Mon Sep 17 00:00:00 2001 From: Taha Ed-Dafili <0rayn.dev@gmail.com> Date: Thu, 26 Feb 2026 15:11:05 +0000 Subject: [PATCH 100/405] iio: accel: adxl345: Implement event scaling for ABI compliance The ADXL345 uses a fixed threshold resolution of 62.5 mg/LSB for event-related registers. Previously, the driver reported raw values without a scale factor. Implement IIO_EV_INFO_SCALE for all event types to provide the conversion factor (0.612915 m/s^2) as required by the IIO ABI. Consequently, remove the obsolete comment in adxl345_read_event_value() which stated that the scale factor is not applied. Add explicit write rejection for IIO_EV_INFO_SCALE in adxl345_write_event_value() returning -EINVAL. Suggested-by: Jonathan Cameron Signed-off-by: Taha Ed-Dafili <0rayn.dev@gmail.com> Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl345_core.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c index 96d1417d77c6..6c9080d88c60 100644 --- a/drivers/iio/accel/adxl345_core.c +++ b/drivers/iio/accel/adxl345_core.c @@ -213,6 +213,7 @@ static const struct iio_event_spec adxl345_events[] = { .dir = IIO_EV_DIR_RISING, .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_SCALE) | BIT(IIO_EV_INFO_VALUE), }, { @@ -221,6 +222,7 @@ static const struct iio_event_spec adxl345_events[] = { .dir = IIO_EV_DIR_RISING, .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_SCALE) | BIT(IIO_EV_INFO_VALUE), }, { @@ -228,7 +230,9 @@ static const struct iio_event_spec adxl345_events[] = { .type = IIO_EV_TYPE_GESTURE, .dir = IIO_EV_DIR_SINGLETAP, .mask_separate = BIT(IIO_EV_INFO_ENABLE), - .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + .mask_shared_by_type = + BIT(IIO_EV_INFO_SCALE) | + BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_TIMEOUT), }, { @@ -237,6 +241,7 @@ static const struct iio_event_spec adxl345_events[] = { .dir = IIO_EV_DIR_DOUBLETAP, .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_SCALE) | BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_RESET_TIMEOUT) | BIT(IIO_EV_INFO_TAP2_MIN_DELAY), @@ -276,6 +281,7 @@ static const struct iio_event_spec adxl345_fake_chan_events[] = { .dir = IIO_EV_DIR_FALLING, .mask_separate = BIT(IIO_EV_INFO_ENABLE), .mask_shared_by_type = + BIT(IIO_EV_INFO_SCALE) | BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_PERIOD), }, @@ -285,6 +291,7 @@ static const struct iio_event_spec adxl345_fake_chan_events[] = { .dir = IIO_EV_DIR_FALLING, .mask_separate = BIT(IIO_EV_INFO_ENABLE), .mask_shared_by_type = + BIT(IIO_EV_INFO_SCALE) | BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_PERIOD), }, @@ -1343,6 +1350,16 @@ static int adxl345_read_event_value(struct iio_dev *indio_dev, unsigned int tap_threshold; int ret; + /* + * The event threshold LSB is fixed at 62.5 mg/LSB + * 0.0625 * 9.80665 = 0.612915625 m/s^2 + */ + if (info == IIO_EV_INFO_SCALE) { + *val = 0; + *val2 = 612915; + return IIO_VAL_INT_PLUS_MICRO; + } + switch (type) { case IIO_EV_TYPE_MAG: return adxl345_read_mag_value(st, dir, info, @@ -1357,12 +1374,6 @@ static int adxl345_read_event_value(struct iio_dev *indio_dev, case IIO_EV_TYPE_GESTURE: switch (info) { case IIO_EV_INFO_VALUE: - /* - * The scale factor would be 62.5mg/LSB (i.e. 0xFF = 16g) but - * not applied here. In context of this general purpose sensor, - * what imports is rather signal intensity than the absolute - * measured g value. - */ ret = regmap_read(st->regmap, ADXL345_REG_THRESH_TAP, &tap_threshold); if (ret) @@ -1403,6 +1414,9 @@ static int adxl345_write_event_value(struct iio_dev *indio_dev, if (ret) return ret; + if (info == IIO_EV_INFO_SCALE) + return -EINVAL; + switch (type) { case IIO_EV_TYPE_MAG: ret = adxl345_write_mag_value(st, dir, info, From d6bd0e2745e66106dea47e3781275fa305492f02 Mon Sep 17 00:00:00 2001 From: Taha Ed-Dafili <0rayn.dev@gmail.com> Date: Thu, 26 Feb 2026 15:11:06 +0000 Subject: [PATCH 101/405] docs: iio: adxl345: update event attributes and scaling math Update the documentation to reflect the recent driver additions for event scaling and the double tap threshold value, alongside correcting existing technical errors in scale calculations. Key changes: - Fix the 62.5 g/LSB typo to 62.5 mg/LSB and add SI unit conversion. - Correct decimal precision of in_accel_scale and in_accel_scale_available to match the actual SI unit (m/s^2) values reported by the driver. - Document the newly generated event scale attributes in the ABI table (e.g., in_accel_mag_rising_scale, in_accel_gesture_singletap_scale). - Document the newly exposed in_accel_gesture_doubletap_value attribute. - Add a sysfs example showing how to read and interpret the newly implemented event scale factor. Suggested-by: Jonathan Cameron Signed-off-by: Taha Ed-Dafili <0rayn.dev@gmail.com> Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- Documentation/iio/adxl345.rst | 51 +++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/Documentation/iio/adxl345.rst b/Documentation/iio/adxl345.rst index 0e8977345e9d..978f746a8198 100644 --- a/Documentation/iio/adxl345.rst +++ b/Documentation/iio/adxl345.rst @@ -13,7 +13,12 @@ This driver supports Analog Device's ADXL345/375 on SPI/I2C bus. * `ADXL375 `_ The ADXL345 is a general-purpose, low-power, 3-axis accelerometer with selectable -measurement ranges. The ADXL345 supports the ±2 g, ±4 g, ±8 g, and ±16 g ranges. +measurement ranges. The ADXL345 supports the following ranges: + +- ±2g (approx. ±19.61 m/s^2) +- ±4g (approx. ±39.23 m/s^2) +- ±8g (approx. ±78.45 m/s^2) +- ±16g (approx. ±156.91 m/s^2) 2. Device Attributes ==================== @@ -94,27 +99,41 @@ listed. +---------------------------------------------+---------------------------------------------+ | in_accel_gesture_doubletap_reset_timeout | Double tap window in [us] | +---------------------------------------------+---------------------------------------------+ +| in_accel_gesture_doubletap_scale | Double tap gesture threshold scale. | ++---------------------------------------------+---------------------------------------------+ | in_accel_gesture_doubletap_tap2_min_delay | Double tap latency in [us] | +---------------------------------------------+---------------------------------------------+ +| in_accel_gesture_doubletap_value | Double tap threshold value | ++---------------------------------------------+---------------------------------------------+ +| in_accel_gesture_singletap_scale | Single tap gesture threshold scale. | ++---------------------------------------------+---------------------------------------------+ | in_accel_gesture_singletap_timeout | Single tap duration in [us] | +---------------------------------------------+---------------------------------------------+ -| in_accel_gesture_singletap_value | Single tap threshold value in 62.5/LSB | +| in_accel_gesture_singletap_value | Single tap threshold value | +---------------------------------------------+---------------------------------------------+ | in_accel_mag_adaptive_falling_period | AC coupled inactivity time in seconds | +---------------------------------------------+---------------------------------------------+ -| in_accel_mag_adaptive_falling_value | AC coupled inactivity threshold in 62.5/LSB | +| in_accel_mag_adaptive_falling_scale | AC coupled inactivity threshold scale. | ++---------------------------------------------+---------------------------------------------+ +| in_accel_mag_adaptive_falling_value | AC coupled inactivity threshold | +---------------------------------------------+---------------------------------------------+ | in_accel_mag_adaptive_rising_en | Enable AC coupled activity on X axis | +---------------------------------------------+---------------------------------------------+ -| in_accel_mag_adaptive_rising_value | AC coupled activity threshold in 62.5/LSB | +| in_accel_mag_adaptive_rising_scale | AC coupled activity threshold scale. | ++---------------------------------------------+---------------------------------------------+ +| in_accel_mag_adaptive_rising_value | AC coupled activity threshold | +---------------------------------------------+---------------------------------------------+ | in_accel_mag_falling_period | Inactivity time in seconds | +---------------------------------------------+---------------------------------------------+ -| in_accel_mag_falling_value | Inactivity threshold value in 62.5/LSB | +| in_accel_mag_falling_scale | DC coupled inactivity threshold scale. | ++---------------------------------------------+---------------------------------------------+ +| in_accel_mag_falling_value | Inactivity threshold value | +---------------------------------------------+---------------------------------------------+ | in_accel_mag_rising_en | Enable activity detection on X axis | +---------------------------------------------+---------------------------------------------+ -| in_accel_mag_rising_value | Activity threshold value in 62.5/LSB | +| in_accel_mag_rising_scale | DC coupled activity threshold scale. | ++---------------------------------------------+---------------------------------------------+ +| in_accel_mag_rising_value | Activity threshold value | +---------------------------------------------+---------------------------------------------+ | in_accel_x&y&z_mag_adaptive_falling_en | Enable AC coupled inactivity on all axes | +---------------------------------------------+---------------------------------------------+ @@ -140,8 +159,8 @@ When changing the **g range** configuration, the driver attempts to estimate appropriate activity and inactivity thresholds by scaling the default values based on the ratio of the previous range to the new one. The resulting threshold will never be zero and will always fall between 1 and 255, corresponding to up -to 62.5 g/LSB as specified in the datasheet. However, you can override these -estimated thresholds by setting explicit values. +to 62.5 mg/LSB (0.612915 m/s^2/LSB) as specified in the datasheet. However, +you can override these estimated thresholds by setting explicit values. When **activity** and **inactivity** events are enabled, the driver automatically manages hysteresis behavior by setting the **link** and @@ -270,13 +289,13 @@ Scale range configuration: .. code-block:: bash root:/sys/bus/iio/devices/iio:device0> cat ./in_accel_scale - 0.478899 + 0.004789 root:/sys/bus/iio/devices/iio:device0> cat ./in_accel_scale_available - 0.478899 0.957798 1.915595 3.831190 + 0.004789 0.009578 0.019156 0.038312 - root:/sys/bus/iio/devices/iio:device0> echo 1.915595 > ./in_accel_scale + root:/sys/bus/iio/devices/iio:device0> echo 0.019156 > ./in_accel_scale root:/sys/bus/iio/devices/iio:device0> cat ./in_accel_scale - 1.915595 + 0.019156 Set output data rate (ODR): @@ -312,10 +331,14 @@ Configure one or several events: root:/sys/bus/iio/devices/iio:device0> echo 24 > ./buffer0/length - ## AC coupled activity, threshold [62.5/LSB] + ## Check the event scale factor (0.0625 * 9.80665) + root:/sys/bus/iio/devices/iio:device0> cat ./events/in_accel_gesture_doubletap_scale + 0.612915 + + ## AC coupled activity, threshold [0.612915 m/s^2/LSB] root:/sys/bus/iio/devices/iio:device0> echo 6 > ./events/in_accel_mag_adaptive_rising_value - ## AC coupled inactivity, threshold, [62.5/LSB] + ## AC coupled inactivity, threshold, [0.612915 m/s^2/LSB] root:/sys/bus/iio/devices/iio:device0> echo 4 > ./events/in_accel_mag_adaptive_falling_value ## AC coupled inactivity, time [s] From 1037352197476f5eee4804e13a64c242be297aa2 Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Wed, 25 Feb 2026 17:08:59 +0400 Subject: [PATCH 102/405] iio: adc: fix typos found by codespell Fix various spelling mistakes in comments and error messages across drivers/iio/adc/, found by running codespell. Signed-off-by: Giorgi Tchankvetadze Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4030.c | 2 +- drivers/iio/adc/ad4170-4.c | 6 +++--- drivers/iio/adc/ad7380.c | 2 +- drivers/iio/adc/ad7793.c | 2 +- drivers/iio/adc/ad7887.c | 2 +- drivers/iio/adc/ad7923.c | 4 ++-- drivers/iio/adc/ade9000.c | 2 +- drivers/iio/adc/at91-sama5d2_adc.c | 2 +- drivers/iio/adc/at91_adc.c | 4 ++-- drivers/iio/adc/fsl-imx25-gcq.c | 2 +- drivers/iio/adc/max1363.c | 2 +- drivers/iio/adc/mcp3564.c | 2 +- drivers/iio/adc/men_z188_adc.c | 2 +- drivers/iio/adc/nau7802.c | 2 +- drivers/iio/adc/npcm_adc.c | 2 +- drivers/iio/adc/pac1921.c | 2 +- drivers/iio/adc/palmas_gpadc.c | 2 +- drivers/iio/adc/rohm-bd79124.c | 4 ++-- drivers/iio/adc/spear_adc.c | 2 +- drivers/iio/adc/stm32-adc-core.c | 2 +- drivers/iio/adc/stm32-adc.c | 2 +- drivers/iio/adc/sun20i-gpadc-iio.c | 2 +- drivers/iio/adc/twl4030-madc.c | 2 +- drivers/iio/adc/twl6030-gpadc.c | 2 +- 24 files changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c index def3e1d01ceb..eebb6f5835ad 100644 --- a/drivers/iio/adc/ad4030.c +++ b/drivers/iio/adc/ad4030.c @@ -629,7 +629,7 @@ static int ad4030_conversion(struct iio_dev *indio_dev) /* Add one byte if we are using a differential + common byte mode */ bytes_to_read += (st->mode == AD4030_OUT_DATA_MD_24_DIFF_8_COM || st->mode == AD4030_OUT_DATA_MD_16_DIFF_8_COM) ? 1 : 0; - /* Mulitiply by the number of hardware channels */ + /* Multiply by the number of hardware channels */ bytes_to_read *= st->chip->num_voltage_inputs; for (i = 0; i < cnv_nb; i++) { diff --git a/drivers/iio/adc/ad4170-4.c b/drivers/iio/adc/ad4170-4.c index 82205bfae531..77af0e6b2c59 100644 --- a/drivers/iio/adc/ad4170-4.c +++ b/drivers/iio/adc/ad4170-4.c @@ -275,9 +275,9 @@ static const unsigned int ad4170_reg_size[] = { }; enum ad4170_ref_buf { - AD4170_REF_BUF_PRE, /* Pre-charge referrence buffer */ - AD4170_REF_BUF_FULL, /* Full referrence buffering */ - AD4170_REF_BUF_BYPASS, /* Bypass referrence buffering */ + AD4170_REF_BUF_PRE, /* Pre-charge reference buffer */ + AD4170_REF_BUF_FULL, /* Full reference buffering */ + AD4170_REF_BUF_BYPASS, /* Bypass reference buffering */ }; /* maps adi,positive/negative-reference-buffer property values to enum */ diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c index ca411371816f..9f77990a03f9 100644 --- a/drivers/iio/adc/ad7380.c +++ b/drivers/iio/adc/ad7380.c @@ -1862,7 +1862,7 @@ static int ad7380_probe_spi_offload(struct iio_dev *indio_dev, /* * Starting with a quite low frequency, to allow oversampling x32, - * user is then reponsible to adjust the frequency for the specific case. + * user is then responsible to adjust the frequency for the specific case. */ ret = ad7380_set_sample_freq(st, sample_rate / 32); if (ret) diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index ccf18ce48e34..b6d86c62f24a 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -805,7 +805,7 @@ static int ad7793_probe(struct spi_device *spi) vref_mv = ret / 1000; } else { - vref_mv = 1170; /* Build-in ref */ + vref_mv = 1170; /* Built-in ref */ } st->chip_info = diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index 87ff95643794..068171d54596 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -104,7 +104,7 @@ static int ad7887_ring_postdisable(struct iio_dev *indio_dev) { struct ad7887_state *st = iio_priv(indio_dev); - /* dummy read: restore default CH0 settin */ + /* dummy read: restore default CH0 settings */ return spi_sync(st->spi, &st->msg[AD7887_CH0]); } diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index 0369151c7db1..acc87d486aa4 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -30,7 +30,7 @@ #define AD7923_PM_MODE_AS (1) /* auto shutdown */ #define AD7923_PM_MODE_FS (2) /* full shutdown */ #define AD7923_PM_MODE_OPS (3) /* normal operation */ -#define AD7923_SEQUENCE_OFF (0) /* no sequence fonction */ +#define AD7923_SEQUENCE_OFF (0) /* no sequence function */ #define AD7923_SEQUENCE_PROTECT (2) /* no interrupt write cycle */ #define AD7923_SEQUENCE_ON (3) /* continuous sequence */ @@ -39,7 +39,7 @@ #define AD7923_CHANNEL_WRITE(channel) ((channel) << 6) /* write channel */ #define AD7923_SEQUENCE_WRITE(sequence) ((((sequence) & 1) << 3) \ + (((sequence) & 2) << 9)) - /* write sequence fonction */ + /* write sequence function */ /* left shift for CR : bit 11 transmit in first */ #define AD7923_SHIFT_REGISTER 4 diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index 4be8df34428d..b48728a759bc 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -1548,7 +1548,7 @@ static int ade9000_buffer_postdisable(struct iio_dev *indio_dev) ret = regmap_clear_bits(st->regmap, ADE9000_REG_MASK0, interrupts); if (ret) { - dev_err(dev, "Post-disable update maks0 fail\n"); + dev_err(dev, "Post-disable update mask0 fail\n"); return ret; } diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index aa4ba3f5a506..69bb49434f90 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -2507,7 +2507,7 @@ static int at91_adc_suspend(struct device *dev) at91_adc_buffer_postdisable(indio_dev); /* - * Do a sofware reset of the ADC before we go to suspend. + * Do a software reset of the ADC before we go to suspend. * this will ensure that all pins are free from being muxed by the ADC * and can be used by for other devices. * Otherwise, ADC will hog them and we can't go to suspend mode. diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 8942d15b3978..6e1930f7c65d 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -171,7 +171,7 @@ struct at91_adc_trigger { }; /** - * struct at91_adc_reg_desc - Various informations relative to registers + * struct at91_adc_reg_desc - Various information relative to registers * @channel_base: Base offset for the channel data registers * @drdy_mask: Mask of the DRDY field in the relevant registers * (Interruptions registers mostly) @@ -231,7 +231,7 @@ struct at91_adc_state { struct iio_trigger **trig; bool use_external; u32 vref_mv; - u32 res; /* resolution used for convertions */ + u32 res; /* resolution used for conversions */ wait_queue_head_t wq_data_avail; const struct at91_adc_caps *caps; diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c index f8c220f6a7b4..e6268f7ac400 100644 --- a/drivers/iio/adc/fsl-imx25-gcq.c +++ b/drivers/iio/adc/fsl-imx25-gcq.c @@ -47,7 +47,7 @@ struct mx25_gcq_priv { * of register writes, then a wait for a completion callback, * and finally a register read, during which userspace could issue * another read request. This lock protects a read access from - * ocurring before another one has finished. + * occurring before another one has finished. */ struct mutex lock; }; diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 9dd547e62b6c..d35f4487b2f9 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -121,7 +121,7 @@ enum max1363_modes { }; /** - * struct max1363_chip_info - chip specifc information + * struct max1363_chip_info - chip specific information * @info: iio core function callbacks structure * @channels: channel specification * @num_channels: number of channels diff --git a/drivers/iio/adc/mcp3564.c b/drivers/iio/adc/mcp3564.c index fcdf13f49c48..36675563829e 100644 --- a/drivers/iio/adc/mcp3564.c +++ b/drivers/iio/adc/mcp3564.c @@ -349,7 +349,7 @@ struct mcp3564_chip_info { * struct mcp3564_state - working data for a ADC device * @chip_info: chip specific data * @spi: SPI device structure - * @vref_mv: voltage reference value in miliVolts + * @vref_mv: voltage reference value in millivolts * @lock: synchronize access to driver's state members * @dev_addr: hardware device address * @oversampling: the index inside oversampling list of the ADC diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c index 90919d282e7b..5bd334ec5655 100644 --- a/drivers/iio/adc/men_z188_adc.c +++ b/drivers/iio/adc/men_z188_adc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * MEN 16z188 Analog to Digial Converter + * MEN 16z188 Analog to Digital Converter * * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) * Author: Johannes Thumshirn diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c index 458544cb8ee4..1a42c7962ec9 100644 --- a/drivers/iio/adc/nau7802.c +++ b/drivers/iio/adc/nau7802.c @@ -257,7 +257,7 @@ static int nau7802_read_poll(struct iio_dev *indio_dev, /* * Because there is actually only one ADC for both channels, we have to * wait for enough conversions to happen before getting a significant - * value when changing channels and the values are far appart. + * value when changing channels and the values are far apart. */ do { ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL); diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c index c8283873cdee..ddabb9600d46 100644 --- a/drivers/iio/adc/npcm_adc.c +++ b/drivers/iio/adc/npcm_adc.c @@ -38,7 +38,7 @@ struct npcm_adc { * read access from userspace. Reading a raw value requires a sequence * of register writes, then a wait for a event and finally a register * read, during which userspace could issue another read request. - * This lock protects a read access from ocurring before another one + * This lock protects a read access from occurring before another one * has finished. */ struct mutex lock; diff --git a/drivers/iio/adc/pac1921.c b/drivers/iio/adc/pac1921.c index a0227b57f238..bce7185953ec 100644 --- a/drivers/iio/adc/pac1921.c +++ b/drivers/iio/adc/pac1921.c @@ -856,7 +856,7 @@ static ssize_t pac1921_format_scale_avail(const int (*const scales_tbl)[2], /* * Read available scales for a specific channel * - * NOTE: using extended info insted of iio.read_avail() because access to + * NOTE: using extended info instead of iio.read_avail() because access to * current scales must be locked as they depend on shunt resistor which may * change runtime. Caller of iio.read_avail() would access the table unlocked * instead. diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index 3f433064618e..3aea12e9d4fb 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -105,7 +105,7 @@ struct palmas_gpadc_thresholds { * of register writes, then a wait for a completion callback, * and finally a register read, during which userspace could issue * another read request. This lock protects a read access from - * ocurring before another one has finished. + * occurring before another one has finished. * * This is the palmas_gpadc structure to store run-time information * and pointers for this driver instance. diff --git a/drivers/iio/adc/rohm-bd79124.c b/drivers/iio/adc/rohm-bd79124.c index fc0452749b79..40d00bd0cc9d 100644 --- a/drivers/iio/adc/rohm-bd79124.c +++ b/drivers/iio/adc/rohm-bd79124.c @@ -75,7 +75,7 @@ /* * The high limit, low limit and last measurement result are each stored in - * 2 consequtive registers. 4 bits are in the high bits of the first register + * 2 consecutive registers. 4 bits are in the high bits of the first register * and 8 bits in the next register. * * These macros return the address of the first reg for the given channel. @@ -962,7 +962,7 @@ static int bd79124_hw_init(struct bd79124_data *data) if (ret) return ret; - /* Enable writing the measured values to the regsters */ + /* Enable writing the measured values to the registers */ ret = regmap_set_bits(data->map, BD79124_REG_GEN_CFG, BD79124_MSK_STATS_EN); if (ret) diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c index 50b0a607baeb..91995489bb1c 100644 --- a/drivers/iio/adc/spear_adc.c +++ b/drivers/iio/adc/spear_adc.c @@ -82,7 +82,7 @@ struct spear_adc_state { * of register writes, then a wait for a completion callback, * and finally a register read, during which userspace could issue * another read request. This lock protects a read access from - * ocurring before another one has finished. + * occurring before another one has finished. */ struct mutex lock; u32 current_clk; diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index e39a4c0db25e..a42d82d61cb8 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -227,7 +227,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev, if (priv->aclk) { /* * Asynchronous clock modes (e.g. ckmode == 0) - * From spec: PLL output musn't exceed max rate + * From spec: PLL output mustn't exceed max rate */ rate = clk_get_rate(priv->aclk); if (!rate) { diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 2d7f88459c7c..46106200bb86 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -1662,7 +1662,7 @@ static irqreturn_t stm32_adc_threaded_isr(int irq, void *data) /* * Clear ovr bit to avoid subsequent calls to IRQ handler. * This requires to stop ADC first. OVR bit state in ISR, - * is propaged to CSR register by hardware. + * is propagated to CSR register by hardware. */ adc->cfg->stop_conv(indio_dev); stm32_adc_irq_clear(indio_dev, regs->isr_ovr.mask); diff --git a/drivers/iio/adc/sun20i-gpadc-iio.c b/drivers/iio/adc/sun20i-gpadc-iio.c index e4dfe76e6362..861c14da75ad 100644 --- a/drivers/iio/adc/sun20i-gpadc-iio.c +++ b/drivers/iio/adc/sun20i-gpadc-iio.c @@ -55,7 +55,7 @@ struct sun20i_gpadc_iio { * of register writes, then a wait for a completion callback, * and finally a register read, during which userspace could issue * another read request. This lock protects a read access from - * ocurring before another one has finished. + * occurring before another one has finished. */ struct mutex lock; }; diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c index fe3b31ec976e..f0274cd74973 100644 --- a/drivers/iio/adc/twl4030-madc.c +++ b/drivers/iio/adc/twl4030-madc.c @@ -252,7 +252,7 @@ static const struct s16_fract twl4030_divider_ratios[16] = { {5, 11}, /* CHANNEL 15 */ }; -/* Conversion table from -3 to 55 degrees Celcius */ +/* Conversion table from -3 to 55 degrees Celsius */ static int twl4030_therm_tbl[] = { 30800, 29500, 28300, 27100, 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c index 3ac774ebf678..7810d6b2b668 100644 --- a/drivers/iio/adc/twl6030-gpadc.c +++ b/drivers/iio/adc/twl6030-gpadc.c @@ -416,7 +416,7 @@ static u8 twl6032_channel_to_reg(int channel) { /* * for any prior chosen channel, when the conversion is ready - * the result is avalable in GPCH0_LSB, GPCH0_MSB. + * the result is available in GPCH0_LSB, GPCH0_MSB. */ return TWL6032_GPADC_GPCH0_LSB; From c4c1c5b773f7615fa7a9e3b421f0b788a94d0a09 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Mon, 23 Feb 2026 14:09:11 -0300 Subject: [PATCH 103/405] Docs: iio: ad4030: Add double PWM SPI offload doc Document double PWM setup SPI offload wiring schema. Reviewed-by: David Lechner Signed-off-by: Marcelo Schmitt Signed-off-by: Jonathan Cameron --- Documentation/iio/ad4030.rst | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Documentation/iio/ad4030.rst b/Documentation/iio/ad4030.rst index b57424b650a8..9caafa4148b0 100644 --- a/Documentation/iio/ad4030.rst +++ b/Documentation/iio/ad4030.rst @@ -92,6 +92,45 @@ Interleaved mode In this mode, both channels conversion results are bit interleaved one SDO line. As such the wiring is the same as `One lane mode`_. +SPI offload wiring +^^^^^^^^^^^^^^^^^^ + +.. code-block:: + + +-------------+ +-------------+ + | CNV |<-----+--| GPIO | + | | +--| PWM0 | + | | | | + | | +--| PWM1 | + | | | +-------------+ + | | +->| TRIGGER | + | CS |<--------| CS | + | | | | + | ADC | | SPI | + | | | | + | SDI |<--------| SDO | + | SDO |-------->| SDI | + | SCLK |<--------| SCLK | + +-------------+ +-------------+ + +In this mode, both the ``cnv-gpios`` and a ``pwms`` properties are required. +The ``pwms`` property specifies the PWM that is connected to the ADC CNV pin. +The SPI offload will have a ``trigger-sources`` property to indicate the SPI +offload (PWM) trigger source. For AD4030 and similar ADCs, there are two +possible data transfer zones for sample N. One of them (zone 1) starts after the +data conversion for sample N is complete while the other one (zone 2) starts 9.8 +nanoseconds after the rising edge of CNV for sample N + 1. + +The configuration depicted in the above diagram is intended to perform data +transfer in zone 2. To achieve high sample rates while meeting ADC timing +requirements, an offset is added between the rising edges of PWM0 and PWM1 to +delay the SPI transfer until 9.8 nanoseconds after CNV rising edge. This +requires a specialized PWM controller that can provide such an offset. +The `AD4630-FMC HDL project`_, for example, can be configured to sample AD4030 +data during zone 2 data read window. + +.. _AD4630-FMC HDL project: https://analogdevicesinc.github.io/hdl/projects/ad4630_fmc/index.html + SPI Clock mode -------------- From 5e0d71dc04e24bdcdc3468bf0081eb79dd016b9a Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Mon, 23 Feb 2026 14:09:31 -0300 Subject: [PATCH 104/405] dt-bindings: iio: adc: adi,ad4030: Add PWM In setups designed for high speed data rate capture, a PWM is used to generate the CNV signal that issues data captures from the ADC. Document the use of a PWM for AD4030 and similar devices. Reviewed-by: David Lechner Acked-by: Conor Dooley Signed-off-by: Marcelo Schmitt Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml index 29e266865805..a135c66142df 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml @@ -72,6 +72,10 @@ properties: The Reset Input (/RST). Used for asynchronous device reset. maxItems: 1 + pwms: + description: PWM signal connected to the CNV pin. + maxItems: 1 + interrupts: description: The BUSY pin is used to signal that the conversions results are available From a98edf7de54dffcacbd4bec8dd420f69ceeff7b1 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Mon, 23 Feb 2026 14:09:47 -0300 Subject: [PATCH 105/405] iio: adc: ad4030: Add SPI offload support AD4030 and similar ADCs can capture data at sample rates up to 2 mega samples per second (MSPS). Not all SPI controllers are able to achieve such high throughputs and even when the controller is fast enough to run transfers at the required speed, it may be costly to the CPU to handle transfer data at such high sample rates. Add SPI offload support for AD4030 and similar ADCs to enable data capture at maximum sample rates. Note that a pair of PWM devices are used for the supported setup. One of the PWM goes to the ADC CNV pin to initiate conversions while the other PWM is connected to the SPI offload trigger to signal when to fetch data from the peripheral. Note also that the PWMs must be somewhat synchronized such to make the controller run transfers only when ADC sample data is available. See Documentation/iio/ad4030.rst for details. Reviewed-by: David Lechner Co-developed-by: Trevor Gamblin Signed-off-by: Trevor Gamblin Co-developed-by: Axel Haslam Signed-off-by: Axel Haslam Signed-off-by: Marcelo Schmitt Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 5 + drivers/iio/adc/ad4030.c | 409 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 396 insertions(+), 18 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 1f5915dd192d..c54788ca8d8e 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -60,9 +60,14 @@ config AD4030 tristate "Analog Devices AD4030 ADC Driver" depends on SPI depends on GPIOLIB + depends on PWM select REGMAP select IIO_BUFFER + select IIO_BUFFER_DMA + select IIO_BUFFER_DMAENGINE select IIO_TRIGGERED_BUFFER + select SPI_OFFLOAD + select SPI_OFFLOAD_TRIGGER_PWM help Say yes here to build support for Analog Devices AD4030 and AD4630 high speed SPI analog to digital converters (ADC). diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c index eebb6f5835ad..42b8cd887382 100644 --- a/drivers/iio/adc/ad4030.c +++ b/drivers/iio/adc/ad4030.c @@ -14,15 +14,26 @@ */ #include +#include #include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include +#include #include #include #include +#include + +#include +#include +#include +#include #define AD4030_REG_INTERFACE_CONFIG_A 0x00 #define AD4030_REG_INTERFACE_CONFIG_A_SW_RESET (BIT(0) | BIT(7)) @@ -111,6 +122,8 @@ #define AD4632_TCYC_NS 2000 #define AD4632_TCYC_ADJUSTED_NS (AD4632_TCYC_NS - AD4030_TCNVL_NS) #define AD4030_TRESET_COM_DELAY_MS 750 +/* Datasheet says 9.8ns, so use the closest integer value */ +#define AD4030_TQUIET_CNV_DELAY_NS 10 enum ad4030_out_mode { AD4030_OUT_DATA_MD_DIFF, @@ -136,11 +149,13 @@ struct ad4030_chip_info { const char *name; const unsigned long *available_masks; const struct iio_chan_spec channels[AD4030_MAX_IIO_CHANNEL_NB]; + const struct iio_chan_spec offload_channels[AD4030_MAX_IIO_CHANNEL_NB]; u8 grade; u8 precision_bits; /* Number of hardware channels */ int num_voltage_inputs; unsigned int tcyc_ns; + unsigned int max_sample_rate_hz; }; struct ad4030_state { @@ -153,6 +168,14 @@ struct ad4030_state { int offset_avail[3]; unsigned int avg_log2; enum ad4030_out_mode mode; + /* Offload sampling */ + struct spi_transfer offload_xfer; + struct spi_message offload_msg; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + struct spi_offload_trigger_config offload_trigger_config; + struct pwm_device *cnv_trigger; + struct pwm_waveform cnv_wf; /* * DMA (thus cache coherency maintenance) requires the transfer buffers @@ -209,8 +232,9 @@ struct ad4030_state { * - voltage0-voltage1 * - voltage2-voltage3 */ -#define AD4030_CHAN_DIFF(_idx, _scan_type) { \ +#define __AD4030_CHAN_DIFF(_idx, _scan_type, _offload) { \ .info_mask_shared_by_all = \ + (_offload ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0) | \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .info_mask_shared_by_all_available = \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ @@ -232,6 +256,12 @@ struct ad4030_state { .num_ext_scan_type = ARRAY_SIZE(_scan_type), \ } +#define AD4030_CHAN_DIFF(_idx, _scan_type) \ + __AD4030_CHAN_DIFF(_idx, _scan_type, 0) + +#define AD4030_OFFLOAD_CHAN_DIFF(_idx, _scan_type) \ + __AD4030_CHAN_DIFF(_idx, _scan_type, 1) + /* * AD4030 can average over 2^N samples, where N = 1, 2, 3, ..., 16. * We use N = 0 to mean no sample averaging. @@ -244,6 +274,11 @@ static const int ad4030_average_modes[] = { BIT(13), BIT(14), BIT(15), BIT(16), }; +static const struct spi_offload_config ad4030_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + static int ad4030_enter_config_mode(struct ad4030_state *st) { st->tx_data[0] = AD4030_REG_ACCESS; @@ -457,6 +492,102 @@ static int ad4030_get_chan_calibbias(struct iio_dev *indio_dev, } } +static void ad4030_get_sampling_freq(struct ad4030_state *st, int *freq) +{ + struct spi_offload_trigger_config *config = &st->offload_trigger_config; + + /* + * Conversion data is fetched from the device when the offload transfer + * is triggered. Thus, provide the SPI offload trigger frequency as the + * sampling frequency. + */ + *freq = config->periodic.frequency_hz; +} + +static int ad4030_update_conversion_rate(struct ad4030_state *st, + unsigned int freq_hz, unsigned int avg_log2) +{ + struct spi_offload_trigger_config *config = &st->offload_trigger_config; + unsigned int offload_period_ns, cnv_rate_hz; + struct pwm_waveform cnv_wf = { }; + u64 target = AD4030_TCNVH_NS; + u64 offload_offset_ns; + int ret; + + /* + * When averaging/oversampling over N samples, we fire the offload + * trigger once at every N pulses of the CNV signal. Conversely, the CNV + * signal needs to be N times faster than the offload trigger. Take that + * into account to correctly re-evaluate both the PWM waveform connected + * to CNV and the SPI offload trigger. + */ + cnv_rate_hz = freq_hz << avg_log2; + + cnv_wf.period_length_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, cnv_rate_hz); + /* + * The datasheet lists a minimum time of 9.8 ns, but no maximum. If the + * rounded PWM's value is less than 10, increase the target value by 10 + * and attempt to round the waveform again, until the value is at least + * 10 ns. Use a separate variable to represent the target in case the + * rounding is severe enough to keep putting the first few results under + * the minimum 10ns condition checked by the while loop. + */ + do { + cnv_wf.duty_length_ns = target; + ret = pwm_round_waveform_might_sleep(st->cnv_trigger, &cnv_wf); + if (ret) + return ret; + target += AD4030_TCNVH_NS; + } while (cnv_wf.duty_length_ns < AD4030_TCNVH_NS); + + /* + * The CNV waveform period (period_length_ns) might get rounded down by + * pwm_round_waveform_might_sleep(). Check the resultant PWM period + * is not smaller than the minimum data conversion cycle time. + */ + if (!in_range(cnv_wf.period_length_ns, AD4030_TCYC_NS, INT_MAX)) + return -EINVAL; + + offload_period_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq_hz); + + config->periodic.frequency_hz = DIV_ROUND_UP(HZ_PER_GHZ, offload_period_ns); + + /* + * The hardware does the capture on zone 2 (when SPI trigger PWM + * is used). This means that the SPI trigger signal should happen at + * tsync + tquiet_con_delay being tsync the conversion signal period + * and tquiet_con_delay 9.8ns. Hence set the PWM phase accordingly. + * + * The PWM waveform API only supports nanosecond resolution right now, + * so round this setting to the closest available value. + */ + offload_offset_ns = AD4030_TQUIET_CNV_DELAY_NS; + do { + config->periodic.offset_ns = offload_offset_ns; + ret = spi_offload_trigger_validate(st->offload_trigger, config); + if (ret) + return ret; + offload_offset_ns += AD4030_TQUIET_CNV_DELAY_NS; + } while (config->periodic.offset_ns < AD4030_TQUIET_CNV_DELAY_NS); + + st->cnv_wf = cnv_wf; + + return 0; +} + +static int ad4030_set_sampling_freq(struct iio_dev *indio_dev, int freq_hz) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + if (freq_hz == 0) + return -EINVAL; + + if (!in_range(freq_hz, 0, st->chip->max_sample_rate_hz)) + return -ERANGE; + + return ad4030_update_conversion_rate(st, freq_hz, st->avg_log2); +} + static int ad4030_set_chan_calibscale(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int gain_int, @@ -516,11 +647,30 @@ static int ad4030_set_avg_frame_len(struct iio_dev *dev, int avg_val) struct ad4030_state *st = iio_priv(dev); unsigned int avg_log2 = ilog2(avg_val); unsigned int last_avg_idx = ARRAY_SIZE(ad4030_average_modes) - 1; + int freq_hz; int ret; if (avg_val < 0 || avg_val > ad4030_average_modes[last_avg_idx]) return -EINVAL; + if (st->offload_trigger) { + /* + * The sample averaging and sampling frequency configurations + * are mutually dependent on each other. That's because the + * effective data sample rate is fCNV / 2^N, where N is the + * number of samples being averaged. + * + * When SPI offload is supported and we have control over the + * sample rate, the conversion start signal (CNV) and the SPI + * offload trigger frequencies must be re-evaluated so data is + * fetched only after 'avg_val' conversions. + */ + ad4030_get_sampling_freq(st, &freq_hz); + ret = ad4030_update_conversion_rate(st, freq_hz, avg_log2); + if (ret) + return ret; + } + ret = regmap_write(st->regmap, AD4030_REG_AVG, AD4030_REG_AVG_MASK_AVG_SYNC | FIELD_PREP(AD4030_REG_AVG_MASK_AVG_VAL, avg_log2)); @@ -773,6 +923,10 @@ static int ad4030_read_raw_dispatch(struct iio_dev *indio_dev, *val = BIT(st->avg_log2); return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + ad4030_get_sampling_freq(st, val); + return IIO_VAL_INT; + default: return -EINVAL; } @@ -813,6 +967,9 @@ static int ad4030_write_raw_dispatch(struct iio_dev *indio_dev, case IIO_CHAN_INFO_OVERSAMPLING_RATIO: return ad4030_set_avg_frame_len(indio_dev, val); + case IIO_CHAN_INFO_SAMP_FREQ: + return ad4030_set_sampling_freq(indio_dev, val); + default: return -EINVAL; } @@ -902,6 +1059,86 @@ static const struct iio_buffer_setup_ops ad4030_buffer_setup_ops = { .validate_scan_mask = ad4030_validate_scan_mask, }; +static void ad4030_prepare_offload_msg(struct iio_dev *indio_dev) +{ + struct ad4030_state *st = iio_priv(indio_dev); + u8 offload_bpw; + + if (st->mode == AD4030_OUT_DATA_MD_30_AVERAGED_DIFF) + offload_bpw = 32; + else + offload_bpw = st->chip->precision_bits; + + st->offload_xfer.bits_per_word = offload_bpw; + st->offload_xfer.len = spi_bpw_to_bytes(offload_bpw); + st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1); +} + +static int ad4030_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4030_state *st = iio_priv(indio_dev); + unsigned int reg_modes; + int ret; + + /* + * When data from 2 analog input channels is output through a single + * bus line (interleaved mode (LANE_MD == 0b11)) and gets pushed through + * DMA, extra hardware is required to do the de-interleaving. While we + * don't support such hardware configurations, disallow interleaved mode + * when using SPI offload. + */ + ret = regmap_read(st->regmap, AD4030_REG_MODES, ®_modes); + if (ret) + return ret; + + if (st->chip->num_voltage_inputs > 1 && + FIELD_GET(AD4030_REG_MODES_MASK_LANE_MODE, reg_modes) == AD4030_LANE_MD_INTERLEAVED) + return -EINVAL; + + ad4030_prepare_offload_msg(indio_dev); + st->offload_msg.offload = st->offload; + ret = spi_optimize_message(st->spi, &st->offload_msg); + if (ret) + return ret; + + ret = pwm_set_waveform_might_sleep(st->cnv_trigger, &st->cnv_wf, false); + if (ret) + goto out_unoptimize; + + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, + &st->offload_trigger_config); + if (ret) + goto out_pwm_disable; + + return 0; + +out_pwm_disable: + pwm_disable(st->cnv_trigger); +out_unoptimize: + spi_unoptimize_message(&st->offload_msg); + + return ret; +} + +static int ad4030_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + + pwm_disable(st->cnv_trigger); + + spi_unoptimize_message(&st->offload_msg); + + return 0; +} + +static const struct iio_buffer_setup_ops ad4030_offload_buffer_setup_ops = { + .postenable = &ad4030_offload_buffer_postenable, + .predisable = &ad4030_offload_buffer_predisable, +}; + static int ad4030_regulators_get(struct ad4030_state *st) { struct device *dev = &st->spi->dev; @@ -971,6 +1208,24 @@ static int ad4030_detect_chip_info(const struct ad4030_state *st) return 0; } +static int ad4030_pwm_get(struct ad4030_state *st) +{ + struct device *dev = &st->spi->dev; + + st->cnv_trigger = devm_pwm_get(dev, NULL); + if (IS_ERR(st->cnv_trigger)) + return dev_err_probe(dev, PTR_ERR(st->cnv_trigger), + "Failed to get CNV PWM\n"); + + /* + * Preemptively disable the PWM, since we only want to enable it with + * the buffer. + */ + pwm_disable(st->cnv_trigger); + + return 0; +} + static int ad4030_config(struct ad4030_state *st) { int ret; @@ -998,6 +1253,31 @@ static int ad4030_config(struct ad4030_state *st) return 0; } +static int ad4030_spi_offload_setup(struct iio_dev *indio_dev, + struct ad4030_state *st) +{ + struct device *dev = &st->spi->dev; + struct dma_chan *rx_dma; + + indio_dev->setup_ops = &ad4030_offload_buffer_setup_ops; + + st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + + st->offload_trigger_config.type = SPI_OFFLOAD_TRIGGER_PERIODIC; + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "failed to get offload RX DMA\n"); + + return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma, + IIO_BUFFER_DIRECTION_IN); +} + static int ad4030_probe(struct spi_device *spi) { struct device *dev = &spi->dev; @@ -1049,24 +1329,58 @@ static int ad4030_probe(struct spi_device *spi) return dev_err_probe(dev, PTR_ERR(st->cnv_gpio), "Failed to get cnv gpio\n"); - /* - * One hardware channel is split in two software channels when using - * common byte mode. Add one more channel for the timestamp. - */ - indio_dev->num_channels = 2 * st->chip->num_voltage_inputs + 1; indio_dev->name = st->chip->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &ad4030_iio_info; - indio_dev->channels = st->chip->channels; indio_dev->available_scan_masks = st->chip->available_masks; - ret = devm_iio_triggered_buffer_setup(dev, indio_dev, - iio_pollfunc_store_time, - ad4030_trigger_handler, - &ad4030_buffer_setup_ops); - if (ret) - return dev_err_probe(dev, ret, - "Failed to setup triggered buffer\n"); + st->offload = devm_spi_offload_get(dev, spi, &ad4030_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + /* Fall back to low speed usage when no SPI offload is available. */ + if (ret == -ENODEV) { + /* + * One hardware channel is split in two software channels when + * using common byte mode. Add one more channel for the timestamp. + */ + indio_dev->num_channels = 2 * st->chip->num_voltage_inputs + 1; + indio_dev->channels = st->chip->channels; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ad4030_trigger_handler, + &ad4030_buffer_setup_ops); + if (ret) + return dev_err_probe(dev, ret, + "Failed to setup triggered buffer\n"); + } else if (ret) { + return dev_err_probe(dev, ret, "failed to get offload\n"); + } else { + /* + * Offloaded SPI transfers can't support software timestamp so + * no additional timestamp channel is added. + */ + indio_dev->num_channels = st->chip->num_voltage_inputs; + indio_dev->channels = st->chip->offload_channels; + ret = ad4030_spi_offload_setup(indio_dev, st); + if (ret) + return dev_err_probe(dev, ret, + "Failed to setup SPI offload\n"); + + ret = ad4030_pwm_get(st); + if (ret) + return dev_err_probe(dev, ret, "Failed to get PWM\n"); + + /* + * Start with a slower sampling rate so there is some room for + * adjusting the sample averaging and the sampling frequency + * without hitting the maximum conversion rate. + */ + ret = ad4030_update_conversion_rate(st, st->chip->max_sample_rate_hz >> 4, + st->avg_log2); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set offload samp freq\n"); + } return devm_iio_device_register(dev, indio_dev); } @@ -1104,11 +1418,28 @@ static const struct iio_scan_type ad4030_24_scan_types[] = { }, }; +static const struct iio_scan_type ad4030_24_offload_scan_types[] = { + [AD4030_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + .shift = 0, + .endianness = IIO_CPU, + }, + [AD4030_SCAN_TYPE_AVG] = { + .sign = 's', + .realbits = 30, + .storagebits = 32, + .shift = 2, + .endianness = IIO_CPU, + }, +}; + static const struct iio_scan_type ad4030_16_scan_types[] = { [AD4030_SCAN_TYPE_NORMAL] = { .sign = 's', - .storagebits = 32, .realbits = 16, + .storagebits = 32, .shift = 16, .endianness = IIO_BE, }, @@ -1121,6 +1452,23 @@ static const struct iio_scan_type ad4030_16_scan_types[] = { } }; +static const struct iio_scan_type ad4030_16_offload_scan_types[] = { + [AD4030_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 16, + .storagebits = 32, + .shift = 0, + .endianness = IIO_CPU, + }, + [AD4030_SCAN_TYPE_AVG] = { + .sign = 's', + .realbits = 30, + .storagebits = 32, + .shift = 2, + .endianness = IIO_CPU, + }, +}; + static const struct ad4030_chip_info ad4030_24_chip_info = { .name = "ad4030-24", .available_masks = ad4030_channel_masks, @@ -1129,10 +1477,14 @@ static const struct ad4030_chip_info ad4030_24_chip_info = { AD4030_CHAN_CMO(1, 0), IIO_CHAN_SOFT_TIMESTAMP(2), }, + .offload_channels = { + AD4030_OFFLOAD_CHAN_DIFF(0, ad4030_24_offload_scan_types), + }, .grade = AD4030_REG_CHIP_GRADE_AD4030_24_GRADE, .precision_bits = 24, .num_voltage_inputs = 1, .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 2 * HZ_PER_MHZ, }; static const struct ad4030_chip_info ad4630_16_chip_info = { @@ -1145,10 +1497,15 @@ static const struct ad4030_chip_info ad4630_16_chip_info = { AD4030_CHAN_CMO(3, 1), IIO_CHAN_SOFT_TIMESTAMP(4), }, + .offload_channels = { + AD4030_OFFLOAD_CHAN_DIFF(0, ad4030_16_offload_scan_types), + AD4030_OFFLOAD_CHAN_DIFF(1, ad4030_16_offload_scan_types), + }, .grade = AD4030_REG_CHIP_GRADE_AD4630_16_GRADE, .precision_bits = 16, .num_voltage_inputs = 2, .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 2 * HZ_PER_MHZ, }; static const struct ad4030_chip_info ad4630_24_chip_info = { @@ -1161,10 +1518,15 @@ static const struct ad4030_chip_info ad4630_24_chip_info = { AD4030_CHAN_CMO(3, 1), IIO_CHAN_SOFT_TIMESTAMP(4), }, + .offload_channels = { + AD4030_OFFLOAD_CHAN_DIFF(0, ad4030_24_offload_scan_types), + AD4030_OFFLOAD_CHAN_DIFF(1, ad4030_24_offload_scan_types), + }, .grade = AD4030_REG_CHIP_GRADE_AD4630_24_GRADE, .precision_bits = 24, .num_voltage_inputs = 2, .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 2 * HZ_PER_MHZ, }; static const struct ad4030_chip_info ad4632_16_chip_info = { @@ -1177,10 +1539,15 @@ static const struct ad4030_chip_info ad4632_16_chip_info = { AD4030_CHAN_CMO(3, 1), IIO_CHAN_SOFT_TIMESTAMP(4), }, + .offload_channels = { + AD4030_OFFLOAD_CHAN_DIFF(0, ad4030_16_offload_scan_types), + AD4030_OFFLOAD_CHAN_DIFF(1, ad4030_16_offload_scan_types), + }, .grade = AD4030_REG_CHIP_GRADE_AD4632_16_GRADE, .precision_bits = 16, .num_voltage_inputs = 2, .tcyc_ns = AD4632_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 500 * HZ_PER_KHZ, }; static const struct ad4030_chip_info ad4632_24_chip_info = { @@ -1193,10 +1560,15 @@ static const struct ad4030_chip_info ad4632_24_chip_info = { AD4030_CHAN_CMO(3, 1), IIO_CHAN_SOFT_TIMESTAMP(4), }, + .offload_channels = { + AD4030_OFFLOAD_CHAN_DIFF(0, ad4030_24_offload_scan_types), + AD4030_OFFLOAD_CHAN_DIFF(1, ad4030_24_offload_scan_types), + }, .grade = AD4030_REG_CHIP_GRADE_AD4632_24_GRADE, .precision_bits = 24, .num_voltage_inputs = 2, .tcyc_ns = AD4632_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 500 * HZ_PER_KHZ, }; static const struct spi_device_id ad4030_id_table[] = { @@ -1232,3 +1604,4 @@ module_spi_driver(ad4030_driver); MODULE_AUTHOR("Esteban Blanc "); MODULE_DESCRIPTION("Analog Devices AD4630 ADC family driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); From addb98c43b58609218e728f5cb578510d15e8737 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Mon, 23 Feb 2026 14:10:01 -0300 Subject: [PATCH 106/405] dt-bindings: iio: adc: adi,ad4030: Add ADAQ4216 and ADAQ4224 ADAQ4216 and ADAQ4224 are similar to AD4030 except that ADAQ devices have a PGA (programmable gain amplifier) that scales the input signal prior to it reaching the ADC inputs. The PGA is controlled through a couple of pins (A0 and A1) that set one of four possible signal gain configurations. Reviewed-by: Conor Dooley Signed-off-by: Marcelo Schmitt Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/adi,ad4030.yaml | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml index a135c66142df..08b1f9d75f89 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml @@ -19,6 +19,8 @@ description: | * https://www.analog.com/media/en/technical-documentation/data-sheets/ad4030-24-4032-24.pdf * https://www.analog.com/media/en/technical-documentation/data-sheets/ad4630-24_ad4632-24.pdf * https://www.analog.com/media/en/technical-documentation/data-sheets/ad4630-16-4632-16.pdf + * https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4216.pdf + * https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4224.pdf $ref: /schemas/spi/spi-peripheral-props.yaml# @@ -31,6 +33,8 @@ properties: - adi,ad4630-24 - adi,ad4632-16 - adi,ad4632-24 + - adi,adaq4216 + - adi,adaq4224 reg: maxItems: 1 @@ -62,6 +66,14 @@ properties: description: Internal buffered Reference. Used when ref-supply is not connected. + vddh-supply: + description: + PGIA Positive Power Supply. + + vdd-fda-supply: + description: + FDA Positive Power Supply. + cnv-gpios: description: The Convert Input (CNV). It initiates the sampling conversions. @@ -72,6 +84,13 @@ properties: The Reset Input (/RST). Used for asynchronous device reset. maxItems: 1 + pga-gpios: + description: + A0 and A1 pins for gain selection. For devices that have PGA configuration + input pins, pga-gpios should be defined. + minItems: 2 + maxItems: 2 + pwms: description: PWM signal connected to the CNV pin. maxItems: 1 @@ -113,6 +132,22 @@ allOf: properties: spi-rx-bus-width: maxItems: 1 + # ADAQ devices require a gain property to indicate how hardware PGA is set + - if: + properties: + compatible: + contains: + pattern: ^adi,adaq + then: + required: + - vddh-supply + - vdd-fda-supply + - pga-gpios + properties: + ref-supply: false + else: + properties: + pga-gpios: false examples: - | @@ -154,3 +189,26 @@ examples: reset-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; }; }; + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,adaq4216"; + reg = <0>; + spi-max-frequency = <80000000>; + vdd-5v-supply = <&supply_5V>; + vdd-1v8-supply = <&supply_1_8V>; + vio-supply = <&supply_1_8V>; + refin-supply = <&refin_sup>; + vddh-supply = <&vddh>; + vdd-fda-supply = <&vdd_fda>; + cnv-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; + pga-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>, + <&gpio0 3 GPIO_ACTIVE_HIGH>; + }; + }; From 185f7b6cee61a2c764e6a5026f5c0dc57e14277a Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Mon, 23 Feb 2026 14:10:23 -0300 Subject: [PATCH 107/405] iio: adc: ad4030: Add support for ADAQ4216 and ADAQ4224 ADAQ4216 and ADAQ4224 are similar to AD4030, but feature a PGA circuitry that scales the analog input signal prior to it reaching the ADC. The PGA is controlled through a pair of pins (A0 and A1) whose state define the gain that is applied to the input signal. Add support for ADAQ4216 and ADAQ4224. Provide a list of PGA options through the IIO device channel scale available interface and enable control of the PGA through the channel scale interface. Signed-off-by: Marcelo Schmitt Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4030.c | 201 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 198 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c index 42b8cd887382..9c5f19321e3b 100644 --- a/drivers/iio/adc/ad4030.c +++ b/drivers/iio/adc/ad4030.c @@ -48,6 +48,8 @@ #define AD4030_REG_CHIP_GRADE_AD4630_24_GRADE 0x00 #define AD4030_REG_CHIP_GRADE_AD4632_16_GRADE 0x05 #define AD4030_REG_CHIP_GRADE_AD4632_24_GRADE 0x02 +#define AD4030_REG_CHIP_GRADE_ADAQ4216_GRADE 0x1E +#define AD4030_REG_CHIP_GRADE_ADAQ4224_GRADE 0x1C #define AD4030_REG_CHIP_GRADE_MASK_CHIP_GRADE GENMASK(7, 3) #define AD4030_REG_SCRATCH_PAD 0x0A #define AD4030_REG_SPI_REVISION 0x0B @@ -125,6 +127,10 @@ /* Datasheet says 9.8ns, so use the closest integer value */ #define AD4030_TQUIET_CNV_DELAY_NS 10 +/* HARDWARE_GAIN */ +#define ADAQ4616_PGA_PINS 2 +#define ADAQ4616_PGA_GAIN_MAX_NANO (NANO * 2 / 3) + enum ad4030_out_mode { AD4030_OUT_DATA_MD_DIFF, AD4030_OUT_DATA_MD_16_DIFF_8_COM, @@ -145,6 +151,23 @@ enum { AD4030_SCAN_TYPE_AVG, }; +/* + * Gains computed as fractions of 1000 so they can be expressed by integers. + */ +static const int adaq4216_hw_gains_vpv[] = { + 1 * MILLI / 3, /* 0.333 */ + 5 * MILLI / 9, /* 0.555 */ + 20 * MILLI / 9, /* 0.2222 */ + 20 * MILLI / 3, /* 0.6666 */ +}; + +static const int adaq4216_hw_gains_frac[][2] = { + { 1, 3 }, /* 1/3 V/V gain */ + { 5, 9 }, /* 5/9 V/V gain */ + { 20, 9 }, /* 20/9 V/V gain */ + { 20, 3 }, /* 20/3 V/V gain */ +}; + struct ad4030_chip_info { const char *name; const unsigned long *available_masks; @@ -152,6 +175,7 @@ struct ad4030_chip_info { const struct iio_chan_spec offload_channels[AD4030_MAX_IIO_CHANNEL_NB]; u8 grade; u8 precision_bits; + bool has_pga; /* Number of hardware channels */ int num_voltage_inputs; unsigned int tcyc_ns; @@ -175,7 +199,11 @@ struct ad4030_state { struct spi_offload_trigger *offload_trigger; struct spi_offload_trigger_config offload_trigger_config; struct pwm_device *cnv_trigger; + size_t scale_avail_size; struct pwm_waveform cnv_wf; + unsigned int scale_avail[ARRAY_SIZE(adaq4216_hw_gains_vpv)][2]; + struct gpio_descs *pga_gpios; + unsigned int pga_index; /* * DMA (thus cache coherency maintenance) requires the transfer buffers @@ -232,7 +260,7 @@ struct ad4030_state { * - voltage0-voltage1 * - voltage2-voltage3 */ -#define __AD4030_CHAN_DIFF(_idx, _scan_type, _offload) { \ +#define __AD4030_CHAN_DIFF(_idx, _scan_type, _offload, _pga) { \ .info_mask_shared_by_all = \ (_offload ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0) | \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ @@ -243,6 +271,7 @@ struct ad4030_state { BIT(IIO_CHAN_INFO_CALIBBIAS) | \ BIT(IIO_CHAN_INFO_RAW), \ .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + (_pga ? BIT(IIO_CHAN_INFO_SCALE) : 0) | \ BIT(IIO_CHAN_INFO_CALIBSCALE), \ .type = IIO_VOLTAGE, \ .indexed = 1, \ @@ -257,10 +286,16 @@ struct ad4030_state { } #define AD4030_CHAN_DIFF(_idx, _scan_type) \ - __AD4030_CHAN_DIFF(_idx, _scan_type, 0) + __AD4030_CHAN_DIFF(_idx, _scan_type, 0, 0) #define AD4030_OFFLOAD_CHAN_DIFF(_idx, _scan_type) \ - __AD4030_CHAN_DIFF(_idx, _scan_type, 1) + __AD4030_CHAN_DIFF(_idx, _scan_type, 1, 0) + +#define ADAQ4216_CHAN_DIFF(_idx, _scan_type) \ + __AD4030_CHAN_DIFF(_idx, _scan_type, 0, 1) + +#define ADAQ4216_OFFLOAD_CHAN_DIFF(_idx, _scan_type) \ + __AD4030_CHAN_DIFF(_idx, _scan_type, 1, 1) /* * AD4030 can average over 2^N samples, where N = 1, 2, 3, ..., 16. @@ -418,6 +453,65 @@ static const struct regmap_config ad4030_regmap_config = { .max_register = AD4030_REG_DIG_ERR, }; +static void ad4030_fill_scale_avail(struct ad4030_state *st) +{ + unsigned int mag_bits, int_part, fract_part; + u64 range; + + /* + * The maximum precision of differential channels is retrieved from the + * chip properties. The output code of differential channels is in two's + * complement format (i.e. signed), so the MSB is the sign bit and only + * (precision_bits - 1) bits express voltage magnitude. + */ + mag_bits = st->chip->precision_bits - 1; + + for (unsigned int i = 0; i < ARRAY_SIZE(adaq4216_hw_gains_frac); i++) { + range = mult_frac(st->vref_uv, adaq4216_hw_gains_frac[i][1], + adaq4216_hw_gains_frac[i][0]); + /* + * If range were in mV, we would multiply it by NANO below. + * Though, range is in µV so multiply it by MICRO only so the + * result after right shift and division scales output codes to + * millivolts. + */ + int_part = div_u64_rem((range * MICRO) >> mag_bits, NANO, &fract_part); + st->scale_avail[i][0] = int_part; + st->scale_avail[i][1] = fract_part; + } +} + +static int ad4030_set_pga_gain(struct ad4030_state *st) +{ + DECLARE_BITMAP(bitmap, ADAQ4616_PGA_PINS) = { }; + + bitmap_write(bitmap, st->pga_index, 0, ADAQ4616_PGA_PINS); + + return gpiod_multi_set_value_cansleep(st->pga_gpios, bitmap); +} + +static int ad4030_set_pga(struct iio_dev *indio_dev, int gain_int, int gain_fract) +{ + struct ad4030_state *st = iio_priv(indio_dev); + unsigned int mag_bits = st->chip->precision_bits - 1; + unsigned int tmp; + u64 gain_nano; + + if (!st->pga_gpios) + return -EINVAL; + + gain_nano = gain_int * NANO + gain_fract; + if (!in_range(gain_nano, 1, ADAQ4616_PGA_GAIN_MAX_NANO)) + return -EINVAL; + + tmp = DIV_ROUND_CLOSEST_ULL(gain_nano << mag_bits, NANO); + gain_nano = DIV_ROUND_CLOSEST(st->vref_uv, tmp); + st->pga_index = find_closest(gain_nano, adaq4216_hw_gains_vpv, + ARRAY_SIZE(adaq4216_hw_gains_vpv)); + + return ad4030_set_pga_gain(st); +} + static int ad4030_get_chan_scale(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, @@ -430,6 +524,13 @@ static int ad4030_get_chan_scale(struct iio_dev *indio_dev, if (IS_ERR(scan_type)) return PTR_ERR(scan_type); + /* The LSB of the 8-bit common-mode data is always vref/256. */ + if (st->chip->has_pga && scan_type->realbits != 8) { + *val = st->scale_avail[st->pga_index][0]; + *val2 = st->scale_avail[st->pga_index][1]; + return IIO_VAL_INT_PLUS_NANO; + } + if (chan->differential) *val = (st->vref_uv * 2) / MILLI; else @@ -898,6 +999,15 @@ static int ad4030_read_avail(struct iio_dev *indio_dev, *length = ARRAY_SIZE(ad4030_average_modes); return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + if (st->scale_avail_size == 1) + *vals = (int *)st->scale_avail[st->pga_index]; + else + *vals = (int *)st->scale_avail; + *length = st->scale_avail_size * 2; /* print int and nano part */ + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_LIST; + default: return -EINVAL; } @@ -970,6 +1080,9 @@ static int ad4030_write_raw_dispatch(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SAMP_FREQ: return ad4030_set_sampling_freq(indio_dev, val); + case IIO_CHAN_INFO_SCALE: + return ad4030_set_pga(indio_dev, val, val2); + default: return -EINVAL; } @@ -991,6 +1104,17 @@ static int ad4030_write_raw(struct iio_dev *indio_dev, return ret; } +static int ad4030_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + static int ad4030_reg_access(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, unsigned int *readval) { @@ -1037,6 +1161,7 @@ static const struct iio_info ad4030_iio_info = { .read_avail = ad4030_read_avail, .read_raw = ad4030_read_raw, .write_raw = ad4030_write_raw, + .write_raw_get_fmt = &ad4030_write_raw_get_fmt, .debugfs_reg_access = ad4030_reg_access, .read_label = ad4030_read_label, .get_current_scan_type = ad4030_get_current_scan_type, @@ -1278,6 +1403,26 @@ static int ad4030_spi_offload_setup(struct iio_dev *indio_dev, IIO_BUFFER_DIRECTION_IN); } +static int ad4030_setup_pga(struct device *dev, struct iio_dev *indio_dev, + struct ad4030_state *st) +{ + /* Setup GPIOs for PGA control */ + st->pga_gpios = devm_gpiod_get_array(dev, "pga", GPIOD_OUT_LOW); + if (IS_ERR(st->pga_gpios)) + return dev_err_probe(dev, PTR_ERR(st->pga_gpios), + "Failed to get PGA gpios.\n"); + + if (st->pga_gpios->ndescs != ADAQ4616_PGA_PINS) + return dev_err_probe(dev, -EINVAL, + "Expected %d GPIOs for PGA control.\n", + ADAQ4616_PGA_PINS); + + st->scale_avail_size = ARRAY_SIZE(adaq4216_hw_gains_vpv); + st->pga_index = 0; + + return 0; +} + static int ad4030_probe(struct spi_device *spi) { struct device *dev = &spi->dev; @@ -1320,6 +1465,14 @@ static int ad4030_probe(struct spi_device *spi) if (ret) return ret; + if (st->chip->has_pga) { + ret = ad4030_setup_pga(dev, indio_dev, st); + if (ret) + return ret; + + ad4030_fill_scale_avail(st); + } + ret = ad4030_config(st); if (ret) return ret; @@ -1571,12 +1724,52 @@ static const struct ad4030_chip_info ad4632_24_chip_info = { .max_sample_rate_hz = 500 * HZ_PER_KHZ, }; +static const struct ad4030_chip_info adaq4216_chip_info = { + .name = "adaq4216", + .available_masks = ad4030_channel_masks, + .channels = { + ADAQ4216_CHAN_DIFF(0, ad4030_16_scan_types), + AD4030_CHAN_CMO(1, 0), + IIO_CHAN_SOFT_TIMESTAMP(2), + }, + .offload_channels = { + ADAQ4216_OFFLOAD_CHAN_DIFF(0, ad4030_16_offload_scan_types), + }, + .grade = AD4030_REG_CHIP_GRADE_ADAQ4216_GRADE, + .precision_bits = 16, + .has_pga = true, + .num_voltage_inputs = 1, + .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 2 * HZ_PER_MHZ, +}; + +static const struct ad4030_chip_info adaq4224_chip_info = { + .name = "adaq4224", + .available_masks = ad4030_channel_masks, + .channels = { + ADAQ4216_CHAN_DIFF(0, ad4030_24_scan_types), + AD4030_CHAN_CMO(1, 0), + IIO_CHAN_SOFT_TIMESTAMP(2), + }, + .offload_channels = { + ADAQ4216_OFFLOAD_CHAN_DIFF(0, ad4030_24_offload_scan_types), + }, + .grade = AD4030_REG_CHIP_GRADE_ADAQ4224_GRADE, + .precision_bits = 24, + .has_pga = true, + .num_voltage_inputs = 1, + .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 2 * HZ_PER_MHZ, +}; + static const struct spi_device_id ad4030_id_table[] = { { "ad4030-24", (kernel_ulong_t)&ad4030_24_chip_info }, { "ad4630-16", (kernel_ulong_t)&ad4630_16_chip_info }, { "ad4630-24", (kernel_ulong_t)&ad4630_24_chip_info }, { "ad4632-16", (kernel_ulong_t)&ad4632_16_chip_info }, { "ad4632-24", (kernel_ulong_t)&ad4632_24_chip_info }, + { "adaq4216", (kernel_ulong_t)&adaq4216_chip_info }, + { "adaq4224", (kernel_ulong_t)&adaq4224_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4030_id_table); @@ -1587,6 +1780,8 @@ static const struct of_device_id ad4030_of_match[] = { { .compatible = "adi,ad4630-24", .data = &ad4630_24_chip_info }, { .compatible = "adi,ad4632-16", .data = &ad4632_16_chip_info }, { .compatible = "adi,ad4632-24", .data = &ad4632_24_chip_info }, + { .compatible = "adi,adaq4216", .data = &adaq4216_chip_info }, + { .compatible = "adi,adaq4224", .data = &adaq4224_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad4030_of_match); From 8be19e233744961db6069da9c9ab63eb085a0447 Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Mon, 23 Feb 2026 08:59:26 -0300 Subject: [PATCH 108/405] iio: adc: ad7768-1: fix one-shot mode data acquisition According to the datasheet, one-shot mode requires a SYNC_IN pulse to trigger a new sample conversion. In the current implementation, No sync pulse was sent after switching to one-shot mode and reinit_completion() was called before mode switching, creating a race condition where spurious interrupts during mode change could trigger completion prematurely. Fix by sending a sync pulse after configuring one-shot mode and reinit_completion() to ensure it only waits for the actual conversion completion. Fixes: a5f8c7da3dbe ("iio: adc: Add AD7768-1 ADC basic support") Signed-off-by: Jonathan Santos Reviewed-by: David Lechner Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7768-1.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index fcd8aea7152e..4cb63ab4768a 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -463,12 +463,17 @@ static int ad7768_scan_direct(struct iio_dev *indio_dev) struct ad7768_state *st = iio_priv(indio_dev); int readval, ret; - reinit_completion(&st->completion); - ret = ad7768_set_mode(st, AD7768_ONE_SHOT); if (ret < 0) return ret; + reinit_completion(&st->completion); + + /* One-shot mode requires a SYNC pulse to generate a new sample */ + ret = ad7768_send_sync_pulse(st); + if (ret) + return ret; + ret = wait_for_completion_timeout(&st->completion, msecs_to_jiffies(1000)); if (!ret) From 81fdc3127d013a552465c3bf9829afbed5184406 Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Mon, 23 Feb 2026 08:59:35 -0300 Subject: [PATCH 109/405] iio: adc: ad7768-1: remove switch to one-shot mode wideband low ripple FIR Filter is not available in one-shot mode. In order to make direct reads work for all filter options, remove the switch for one-shot mode and guarantee device is always in continuous conversion mode. Fixes: fb1d3b24ebf5 ("iio: adc: ad7768-1: add filter type and oversampling ratio attributes") Signed-off-by: Jonathan Santos Reviewed-by: David Lechner Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7768-1.c | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 4cb63ab4768a..a927ae288fbb 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -463,17 +463,8 @@ static int ad7768_scan_direct(struct iio_dev *indio_dev) struct ad7768_state *st = iio_priv(indio_dev); int readval, ret; - ret = ad7768_set_mode(st, AD7768_ONE_SHOT); - if (ret < 0) - return ret; - reinit_completion(&st->completion); - /* One-shot mode requires a SYNC pulse to generate a new sample */ - ret = ad7768_send_sync_pulse(st); - if (ret) - return ret; - ret = wait_for_completion_timeout(&st->completion, msecs_to_jiffies(1000)); if (!ret) @@ -492,14 +483,6 @@ static int ad7768_scan_direct(struct iio_dev *indio_dev) if (st->oversampling_ratio == 8) readval >>= 8; - /* - * Any SPI configuration of the AD7768-1 can only be - * performed in continuous conversion mode. - */ - ret = ad7768_set_mode(st, AD7768_CONTINUOUS); - if (ret < 0) - return ret; - return readval; } @@ -1248,6 +1231,10 @@ static int ad7768_setup(struct iio_dev *indio_dev) return ret; } + ret = ad7768_set_mode(st, AD7768_CONTINUOUS); + if (ret) + return ret; + /* For backwards compatibility, try the adi,sync-in-gpios property */ st->gpio_sync_in = devm_gpiod_get_optional(&st->spi->dev, "adi,sync-in", GPIOD_OUT_LOW); From 68fe7c28faeac160c6474c5df93de6194af17a67 Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Mon, 23 Feb 2026 08:59:44 -0300 Subject: [PATCH 110/405] iio: adc: ad7768-1: disable IRQ autoenable The device continuously converts data while powered up, generating interrupts in the background. Configure the IRQ to be enabled and disabled manually as needed to avoid unnecessary CPU load. Signed-off-by: Jonathan Santos Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7768-1.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index a927ae288fbb..b77523393a18 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -464,9 +464,11 @@ static int ad7768_scan_direct(struct iio_dev *indio_dev) int readval, ret; reinit_completion(&st->completion); + enable_irq(st->spi->irq); ret = wait_for_completion_timeout(&st->completion, msecs_to_jiffies(1000)); + disable_irq(st->spi->irq); if (!ret) return -ETIMEDOUT; @@ -1339,8 +1341,22 @@ static const struct iio_buffer_setup_ops ad7768_buffer_ops = { .predisable = &ad7768_buffer_predisable, }; +static int ad7768_set_trigger_state(struct iio_trigger *trig, bool enable) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct ad7768_state *st = iio_priv(indio_dev); + + if (enable) + enable_irq(st->spi->irq); + else + disable_irq(st->spi->irq); + + return 0; +} + static const struct iio_trigger_ops ad7768_trigger_ops = { .validate_device = iio_trigger_validate_own_device, + .set_trigger_state = ad7768_set_trigger_state, }; static int ad7768_set_channel_label(struct iio_dev *indio_dev, @@ -1704,7 +1720,7 @@ static int ad7768_probe(struct spi_device *spi) return ret; ret = devm_request_irq(&spi->dev, spi->irq, &ad7768_interrupt, - IRQF_TRIGGER_RISING | IRQF_NO_THREAD, + IRQF_TRIGGER_RISING | IRQF_NO_THREAD | IRQF_NO_AUTOEN, indio_dev->name, indio_dev); if (ret) return ret; From 6ea592a31be5a45c1e695df8e3907c97f2c5213b Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Mon, 23 Feb 2026 08:59:53 -0300 Subject: [PATCH 111/405] iio: adc: ad7768-1: add support for SPI offload The AD7768-1 family supports sampling rates up to 1 MSPS, which exceeds the capabilities of conventional triggered buffer operations due to SPI transaction overhead and interrupt latency. Add SPI offload support to enable hardware-accelerated data acquisition that bypasses software SPI transactions using continuous data streaming. Signed-off-by: Jonathan Santos Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 2 + drivers/iio/adc/ad7768-1.c | 186 ++++++++++++++++++++++++++++++++++++- 2 files changed, 185 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index c54788ca8d8e..a9dedbb8eb46 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -418,8 +418,10 @@ config AD7768_1 select REGMAP_SPI select RATIONAL select IIO_BUFFER + select IIO_BUFFER_DMAENGINE select IIO_TRIGGER select IIO_TRIGGERED_BUFFER + select SPI_OFFLOAD help Say yes here to build support for Analog Devices AD7768-1 SPI simultaneously sampling sigma-delta analog to digital converter (ADC). diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index b77523393a18..917a3a0380a1 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -25,12 +25,15 @@ #include #include #include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -161,6 +164,8 @@ enum ad7768_filter_regval { enum ad7768_scan_type { AD7768_SCAN_TYPE_NORMAL, AD7768_SCAN_TYPE_HIGH_SPEED, + AD7768_SCAN_TYPE_OFFLOAD_NORMAL, + AD7768_SCAN_TYPE_OFFLOAD_HIGH_SPEED, }; enum { @@ -266,6 +271,18 @@ static const struct iio_scan_type ad7768_scan_type[] = { .storagebits = 16, .endianness = IIO_BE, }, + [AD7768_SCAN_TYPE_OFFLOAD_NORMAL] = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_CPU, + }, + [AD7768_SCAN_TYPE_OFFLOAD_HIGH_SPEED] = { + .sign = 's', + .realbits = 16, + .storagebits = 32, + .endianness = IIO_CPU, + }, }; struct ad7768_chip_info { @@ -283,6 +300,8 @@ struct ad7768_chip_info { struct ad7768_state { struct spi_device *spi; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; struct regmap *regmap; struct regmap *regmap24; int vref_uv; @@ -306,6 +325,8 @@ struct ad7768_state { struct gpio_desc *gpio_reset; const char *labels[AD7768_MAX_CHANNELS]; struct gpio_chip gpiochip; + struct spi_transfer offload_xfer; + struct spi_message offload_msg; const struct ad7768_chip_info *chip; bool en_spi_sync; struct mutex pga_lock; /* protect device internal state (PGA) */ @@ -1119,6 +1140,10 @@ static int ad7768_get_current_scan_type(const struct iio_dev *indio_dev, { struct ad7768_state *st = iio_priv(indio_dev); + if (st->offload) + return st->oversampling_ratio == 8 ? + AD7768_SCAN_TYPE_OFFLOAD_HIGH_SPEED : AD7768_SCAN_TYPE_OFFLOAD_NORMAL; + return st->oversampling_ratio == 8 ? AD7768_SCAN_TYPE_HIGH_SPEED : AD7768_SCAN_TYPE_NORMAL; } @@ -1341,6 +1366,78 @@ static const struct iio_buffer_setup_ops ad7768_buffer_ops = { .predisable = &ad7768_buffer_predisable, }; +static int ad7768_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_DATA_READY, + }; + const struct iio_scan_type *scan_type; + unsigned int unused; + int ret; + + scan_type = iio_get_current_scan_type(indio_dev, &indio_dev->channels[0]); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + st->offload_xfer.len = spi_bpw_to_bytes(scan_type->realbits); + st->offload_xfer.bits_per_word = scan_type->realbits; + st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + + spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1); + st->offload_msg.offload = st->offload; + + ret = spi_optimize_message(st->spi, &st->offload_msg); + if (ret) { + dev_err(&st->spi->dev, "failed to prepare offload, err: %d\n", ret); + return ret; + } + + /* + * Write a 1 to the LSB of the INTERFACE_FORMAT register to enter + * continuous read mode. Subsequent data reads do not require an + * initial 8-bit write to query the ADC_DATA register. + */ + ret = regmap_write(st->regmap, AD7768_REG_INTERFACE_FORMAT, 0x01); + if (ret) + goto err_unoptimize_message; + + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, + &config); + if (ret) + goto err_exit_continuous_read_mode; + + return 0; + +err_exit_continuous_read_mode: + regmap_read(st->regmap24, AD7768_REG24_ADC_DATA, &unused); + +err_unoptimize_message: + spi_unoptimize_message(&st->offload_msg); + + return ret; +} + +static int ad7768_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + unsigned int unused; + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + spi_unoptimize_message(&st->offload_msg); + + /* + * To exit continuous read mode, perform a single read of the ADC_DATA + * reg (0x2C), which allows further configuration of the device. + */ + return regmap_read(st->regmap24, AD7768_REG24_ADC_DATA, &unused); +} + +static const struct iio_buffer_setup_ops ad7768_offload_buffer_ops = { + .postenable = ad7768_offload_buffer_postenable, + .predisable = ad7768_offload_buffer_predisable, +}; + static int ad7768_set_trigger_state(struct iio_trigger *trig, bool enable) { struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); @@ -1589,6 +1686,36 @@ static int ad7768_parse_aaf_gain(struct device *dev, struct ad7768_state *st) return 0; } +static bool ad7768_offload_trigger_match(struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + if (type != SPI_OFFLOAD_TRIGGER_DATA_READY) + return false; + + /* Up to 2 args are allowed, but only 1 is used */ + if (nargs == 0 || nargs > 2 || args[0] != AD7768_TRIGGER_SOURCE_DRDY) + return false; + + return true; +} + +static int ad7768_offload_trigger_request(struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + /* Should already be validated by match, but just in case */ + if (nargs == 0 || nargs > 2) + return -EINVAL; + + return 0; +} + +static const struct spi_offload_trigger_ops ad7768_offload_trigger_ops = { + .match = ad7768_offload_trigger_match, + .request = ad7768_offload_trigger_request, +}; + static const struct ad7768_chip_info ad7768_chip_info = { .name = "ad7768-1", .channel_spec = ad7768_channels, @@ -1626,10 +1753,51 @@ static const struct ad7768_chip_info adaq7769_chip_info = { .has_variable_aaf = true, }; +static const struct spi_offload_config ad7768_spi_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +static int ad7768_spi_offload_probe(struct iio_dev *indio_dev, + struct ad7768_state *st) +{ + struct device *dev = &st->spi->dev; + struct spi_offload_trigger_info trigger_info = { + .fwnode = dev_fwnode(dev), + .ops = &ad7768_offload_trigger_ops, + .priv = st, + }; + struct dma_chan *rx_dma; + int ret; + + ret = devm_spi_offload_trigger_register(dev, &trigger_info); + if (ret) + return dev_err_probe(dev, ret, "failed to register offload trigger\n"); + + st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_DATA_READY); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), "failed to get offload RX DMA\n"); + + ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma, + IIO_BUFFER_DIRECTION_IN); + if (ret) + return dev_err_probe(dev, ret, "failed to setup offload RX DMA\n"); + + indio_dev->setup_ops = &ad7768_offload_buffer_ops; + + return 0; +} + static int ad7768_probe(struct spi_device *spi) { struct ad7768_state *st; struct iio_dev *indio_dev; + struct device *dev = &spi->dev; int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); @@ -1725,9 +1893,20 @@ static int ad7768_probe(struct spi_device *spi) if (ret) return ret; - ret = ad7768_triggered_buffer_alloc(indio_dev); - if (ret) - return ret; + st->offload = devm_spi_offload_get(dev, spi, &ad7768_spi_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + if (ret == -ENODEV) { + /* If not using SPI offload, fall back to low speed usage */ + ret = ad7768_triggered_buffer_alloc(indio_dev); + if (ret) + return ret; + } else if (ret) { + return dev_err_probe(dev, ret, "failed to get SPI offload\n"); + } else { + ret = ad7768_spi_offload_probe(indio_dev, st); + if (ret) + return ret; + } return devm_iio_device_register(&spi->dev, indio_dev); } @@ -1763,3 +1942,4 @@ module_spi_driver(ad7768_driver); MODULE_AUTHOR("Stefan Popa "); MODULE_DESCRIPTION("Analog Devices AD7768-1 ADC driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); From cdf89f225331cfa25451e139d16d748738546bb0 Mon Sep 17 00:00:00 2001 From: Bhargav Joshi Date: Sun, 1 Mar 2026 00:44:00 +0530 Subject: [PATCH 112/405] iio: hid-sensor-gyro-3d: fix typo in array name The array 'gryo_3d_sensitivity_addresses' has a clear spelling mistake in its prefix. Rename it to 'gyro_3d_sensitivity_addresses' to correctly match the naming convention. Signed-off-by: Bhargav Joshi Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/hid-sensor-gyro-3d.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index c43990c518f7..c340cc899a7c 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -42,7 +42,7 @@ static const u32 gyro_3d_addresses[GYRO_3D_CHANNEL_MAX] = { HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS }; -static const u32 gryo_3d_sensitivity_addresses[] = { +static const u32 gyro_3d_sensitivity_addresses[] = { HID_USAGE_SENSOR_DATA_ANGL_VELOCITY, }; @@ -297,8 +297,8 @@ static int hid_gyro_3d_probe(struct platform_device *pdev) ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_GYRO_3D, &gyro_state->common_attributes, - gryo_3d_sensitivity_addresses, - ARRAY_SIZE(gryo_3d_sensitivity_addresses)); + gyro_3d_sensitivity_addresses, + ARRAY_SIZE(gyro_3d_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; From 3712dd05dc77b2329bcc0a5ce81fbe9e6603a22c Mon Sep 17 00:00:00 2001 From: Jun Yan Date: Thu, 5 Feb 2026 23:07:28 +0800 Subject: [PATCH 113/405] dt-bindings: iio: accel: bosch,bma255: add bmx055 accel binding Add the device-tree binding for the Bosch BMX055 IMU (accelerometer part), which is compatible with bmc150_accel. Datasheet: https://cdn.sparkfun.com/assets/b/9/1/f/4/bst-bmx055-ds000_datasheet.pdf Signed-off-by: Jun Yan Reviewed-by: Linus Walleij Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- .../bindings/iio/accel/bosch,bma255.yaml | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/accel/bosch,bma255.yaml b/Documentation/devicetree/bindings/iio/accel/bosch,bma255.yaml index c1387e02eb82..7f9c5eec35dd 100644 --- a/Documentation/devicetree/bindings/iio/accel/bosch,bma255.yaml +++ b/Documentation/devicetree/bindings/iio/accel/bosch,bma255.yaml @@ -16,25 +16,27 @@ description: properties: compatible: - enum: - # bmc150-accel driver in Linux - - bosch,bma222 - - bosch,bma222e - - bosch,bma250e - - bosch,bma253 - - bosch,bma254 - - bosch,bma255 - - bosch,bma280 - - bosch,bmc150_accel - - bosch,bmc156_accel - - bosch,bmi055_accel + oneOf: + - enum: + - bosch,bma222 + - bosch,bma222e + - bosch,bma250e + - bosch,bma253 + - bosch,bma254 + - bosch,bma255 + - bosch,bma280 + - bosch,bmc150_accel + - bosch,bmc156_accel + - bosch,bmi055_accel - # bma180 driver in Linux - - bosch,bma023 - - bosch,bma150 - - bosch,bma180 - - bosch,bma250 - - bosch,smb380 + - bosch,bma023 + - bosch,bma150 + - bosch,bma180 + - bosch,bma250 + - bosch,smb380 + - items: + - const: bosch,bmx055-accel + - const: bosch,bmc150_accel reg: maxItems: 1 From e1d2445bb01eb113105d7dce598785a915391e66 Mon Sep 17 00:00:00 2001 From: Jun Yan Date: Thu, 5 Feb 2026 23:07:29 +0800 Subject: [PATCH 114/405] dt-bindings: iio: magnetometer: bosch,bmc150_magn: add bmx055 magnetometer binding Add the device-tree binding for the bosch BMX055 IMU (magnetometer part), which is compatible with bmc150_magn. Datasheet: https://cdn.sparkfun.com/assets/b/9/1/f/4/bst-bmx055-ds000_datasheet.pdf Signed-off-by: Jun Yan Reviewed-by: Linus Walleij Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- .../iio/magnetometer/bosch,bmc150_magn.yaml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/magnetometer/bosch,bmc150_magn.yaml b/Documentation/devicetree/bindings/iio/magnetometer/bosch,bmc150_magn.yaml index a3838ab0c524..c1a6892b0194 100644 --- a/Documentation/devicetree/bindings/iio/magnetometer/bosch,bmc150_magn.yaml +++ b/Documentation/devicetree/bindings/iio/magnetometer/bosch,bmc150_magn.yaml @@ -21,11 +21,15 @@ properties: description: Note the bmm150_magn is a deprecated compatible as this part contains only a magnetometer. - enum: - - bosch,bmc150_magn - - bosch,bmc156_magn - - bosch,bmm150 - - bosch,bmm150_magn + oneOf: + - enum: + - bosch,bmc150_magn + - bosch,bmc156_magn + - bosch,bmm150 + - bosch,bmm150_magn + - items: + - const: bosch,bmx055-magn + - const: bosch,bmc150_magn reg: maxItems: 1 From bfd61205aca379121b1913885d4d06c51857119b Mon Sep 17 00:00:00 2001 From: Jun Yan Date: Thu, 5 Feb 2026 23:07:30 +0800 Subject: [PATCH 115/405] dt-bindings: iio: gyroscope: bosch,bmg160: add bmx055 gyroscope binding Add the device-tree binding for the bosch BMX055 IMU (gyroscope part), which is compatible with bmg160. Datasheet: https://cdn.sparkfun.com/assets/b/9/1/f/4/bst-bmx055-ds000_datasheet.pdf Signed-off-by: Jun Yan Reviewed-by: Linus Walleij Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- .../bindings/iio/gyroscope/bosch,bmg160.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/gyroscope/bosch,bmg160.yaml b/Documentation/devicetree/bindings/iio/gyroscope/bosch,bmg160.yaml index 3c6fe74af0b8..fcbd4b430e48 100644 --- a/Documentation/devicetree/bindings/iio/gyroscope/bosch,bmg160.yaml +++ b/Documentation/devicetree/bindings/iio/gyroscope/bosch,bmg160.yaml @@ -11,10 +11,14 @@ maintainers: properties: compatible: - enum: - - bosch,bmg160 - - bosch,bmi055_gyro - - bosch,bmi088_gyro + oneOf: + - enum: + - bosch,bmg160 + - bosch,bmi055_gyro + - bosch,bmi088_gyro + - items: + - const: bosch,bmx055-gyro + - const: bosch,bmg160 reg: maxItems: 1 From 6a7084102bb9659f699005c420eb59eade6d3b4f Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Fri, 23 Jan 2026 17:16:17 +0530 Subject: [PATCH 116/405] bus: mhi: host: pci_generic: Add Qualcomm SDX35 modem Add support for sdx35 modem. Similar to SDX75, SDX35 can take longer to transition to ready during power up, so use modem_qcom_v2_mhiv_config configurations. 01:00.0 Unassigned class [ff00]: Qualcomm Device 011a Subsystem: Qualcomm Device 011a Signed-off-by: Krishna Chaitanya Chundru Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260123-mhi_sdx35-v1-1-79440abf0c92@oss.qualcomm.com --- drivers/bus/mhi/host/pci_generic.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 0884a384b77f..425362037830 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -407,6 +407,16 @@ static const struct mhi_pci_dev_info mhi_qcom_sdx55_info = { .sideband_wake = false, }; +static const struct mhi_pci_dev_info mhi_qcom_sdx35_info = { + .name = "qcom-sdx35m", + .config = &modem_qcom_v2_mhiv_config, + .bar_num = MHI_PCI_DEFAULT_BAR_NUM, + .dma_data_width = 32, + .mru_default = 32768, + .sideband_wake = false, + .edl_trigger = true, +}; + static const struct mhi_pci_dev_info mhi_qcom_sdx24_info = { .name = "qcom-sdx24", .edl = "qcom/prog_firehose_sdx24.mbn", @@ -909,6 +919,8 @@ static const struct pci_device_id mhi_pci_id_table[] = { /* Telit FN920C04 (sdx35) */ {PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x011a, 0x1c5d, 0x2020), .driver_data = (kernel_ulong_t) &mhi_telit_fn920c04_info }, + { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x011a), + .driver_data = (kernel_ulong_t) &mhi_qcom_sdx35_info }, { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0304), .driver_data = (kernel_ulong_t) &mhi_qcom_sdx24_info }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0306, PCI_VENDOR_ID_QCOM, 0x010c), From 061c39a17136376f368a6082d4948301641f7db1 Mon Sep 17 00:00:00 2001 From: Elsanti Date: Sat, 28 Feb 2026 18:23:44 -0400 Subject: [PATCH 117/405] drivers/hwtracing/coresight: remove unneeded variable in tmc_crashdata_release() The variable 'ret' is initialized to 0, never modified, and returned directly. Remove it and return 0 explicitly. Signed-off-by: Elsanti Reviewed-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260228222344.288639-1-santiagojoseleal27@gmail.com --- drivers/hwtracing/coresight/coresight-tmc-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index 58b469ee73b4..c89fe996af23 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -397,7 +397,6 @@ static ssize_t tmc_crashdata_read(struct file *file, char __user *data, static int tmc_crashdata_release(struct inode *inode, struct file *file) { - int ret = 0; unsigned long flags; struct tmc_resrv_buf *rbuf; struct tmc_drvdata *drvdata = container_of(file->private_data, @@ -410,7 +409,7 @@ static int tmc_crashdata_release(struct inode *inode, struct file *file) raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_dbg(&drvdata->csdev->dev, "%s: released\n", __func__); - return ret; + return 0; } static const struct file_operations tmc_crashdata_fops = { From 26e5e1be2e640a562618c1aa03ed15b62bfbf2d6 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Fri, 16 Jan 2026 22:36:26 -0600 Subject: [PATCH 118/405] fpga: bridge: Use sysfs_emit() instead of sprintf() According to Documentation/filesystems/sysfs.rst, show() functions should use sysfs_emit() when formatting the value to be returned to user space. This keeps consistent usage of sysfs_emit() across the same file. Signed-off-by: Dinh Nguyen [ Yilun: clarify the necessity of the change in changelog ] Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260117043626.2188219-1-dinguyen@kernel.org Signed-off-by: Xu Yilun --- drivers/fpga/fpga-bridge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c index ca68c38aa4a1..8c275bd48a0d 100644 --- a/drivers/fpga/fpga-bridge.c +++ b/drivers/fpga/fpga-bridge.c @@ -290,7 +290,7 @@ static ssize_t name_show(struct device *dev, { struct fpga_bridge *bridge = to_fpga_bridge(dev); - return sprintf(buf, "%s\n", bridge->name); + return sysfs_emit(buf, "%s\n", bridge->name); } static ssize_t state_show(struct device *dev, From dad46509b63b64f0a56f12caec0f60c8503e26a5 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 3 Mar 2026 01:07:29 +0800 Subject: [PATCH 119/405] iio: adc: ti-ads1119: Drop redundant error message devm_request_threaded_irq already prints an error message when failure, so the error message is redundant, drop it. Suggested-by: Andy Shevchenko Signed-off-by: Felix Gu Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1119.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index c9cedc59cdcd..1157e606bc64 100644 --- a/drivers/iio/adc/ti-ads1119.c +++ b/drivers/iio/adc/ti-ads1119.c @@ -740,8 +740,7 @@ static int ads1119_probe(struct i2c_client *client) NULL, IRQF_ONESHOT, "ads1119", indio_dev); if (ret) - return dev_err_probe(dev, ret, - "Failed to allocate irq\n"); + return ret; st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, From 5c3cf14b82f723ecaef7495fffcfe947e1c4d11e Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Mon, 2 Mar 2026 09:50:36 +0400 Subject: [PATCH 120/405] iio: adc: ade9000: remove unused ADE9000_ST_ERROR macro The ADE9000_ST_ERROR macro references ADE9000_ST1_ERROR0 through ADE9000_ST1_ERROR3, but the actual defined symbols use the BIT suffix. Since this macro is currently unused in the driver, remove it entirely rather than fixing the names. It can be reintroduced in a future patch when it is actually needed. The individual error bit definitions (bits 28-31 of the STATUS1 register at 0x403) are retained as they follow the convention of defining all register fields. Reference: ADE9000 datasheet (Rev. B, Page 61), STATUS1 register (0x403), bits 28-31 define ERROR0 through ERROR3 status flags. Suggested-by: Andy Shevchenko Signed-off-by: Giorgi Tchankvetadze Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ade9000.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index b48728a759bc..c9c21fa4a28b 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -218,9 +218,6 @@ #define ADE9000_ST1_ERROR1_BIT BIT(29) #define ADE9000_ST1_ERROR2_BIT BIT(30) #define ADE9000_ST1_ERROR3_BIT BIT(31) -#define ADE9000_ST_ERROR \ - (ADE9000_ST1_ERROR0 | ADE9000_ST1_ERROR1 | \ - ADE9000_ST1_ERROR2 | ADE9000_ST1_ERROR3) #define ADE9000_ST1_CROSSING_FIRST 6 #define ADE9000_ST1_CROSSING_DEPTH 25 From cd04646c0f3eefdde87538f6ff932420cedc9ba0 Mon Sep 17 00:00:00 2001 From: Bruno Martins Date: Sun, 1 Mar 2026 21:28:26 +0000 Subject: [PATCH 121/405] staging: iio: ad7816: Replace sprintf() with sysfs_emit() As stated in Documentation/filesystems/sysfs.rst: 'New implementations of show() methods should only use sysfs_emit() or sysfs_emit_at() when formatting the value to be returned to user space.' Replace sprintf with sysfs_emit in the following sysfs show functions: - ad7816_show_mode() - ad7816_show_available_modes() - ad7816_show_channel() - ad7816_show_value() - ad7816_show_oti() Signed-off-by: Bruno Martins Signed-off-by: Jonathan Cameron --- drivers/staging/iio/adc/ad7816.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/staging/iio/adc/ad7816.c b/drivers/staging/iio/adc/ad7816.c index 172acf135f3b..0e32a2295990 100644 --- a/drivers/staging/iio/adc/ad7816.c +++ b/drivers/staging/iio/adc/ad7816.c @@ -124,8 +124,8 @@ static ssize_t ad7816_show_mode(struct device *dev, struct ad7816_chip_info *chip = iio_priv(indio_dev); if (chip->mode) - return sprintf(buf, "power-save\n"); - return sprintf(buf, "full\n"); + return sysfs_emit(buf, "power-save\n"); + return sysfs_emit(buf, "full\n"); } static ssize_t ad7816_store_mode(struct device *dev, @@ -156,7 +156,7 @@ static ssize_t ad7816_show_available_modes(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "full\npower-save\n"); + return sysfs_emit(buf, "full\npower-save\n"); } static IIO_DEVICE_ATTR(available_modes, 0444, ad7816_show_available_modes, @@ -169,7 +169,7 @@ static ssize_t ad7816_show_channel(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad7816_chip_info *chip = iio_priv(indio_dev); - return sprintf(buf, "%d\n", chip->channel_id); + return sysfs_emit(buf, "%d\n", chip->channel_id); } static ssize_t ad7816_store_channel(struct device *dev, @@ -231,9 +231,9 @@ static ssize_t ad7816_show_value(struct device *dev, data &= AD7816_TEMP_FLOAT_MASK; if (value < 0) data = BIT(AD7816_TEMP_FLOAT_OFFSET) - data; - return sprintf(buf, "%d.%.2d\n", value, data * 25); + return sysfs_emit(buf, "%d.%.2d\n", value, data * 25); } - return sprintf(buf, "%u\n", data); + return sysfs_emit(buf, "%u\n", data); } static IIO_DEVICE_ATTR(value, 0444, ad7816_show_value, NULL, 0); @@ -281,9 +281,9 @@ static ssize_t ad7816_show_oti(struct device *dev, value = AD7816_BOUND_VALUE_MIN + (chip->oti_data[chip->channel_id] - AD7816_BOUND_VALUE_BASE); - return sprintf(buf, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } - return sprintf(buf, "%u\n", chip->oti_data[chip->channel_id]); + return sysfs_emit(buf, "%u\n", chip->oti_data[chip->channel_id]); } static inline ssize_t ad7816_set_oti(struct device *dev, From 7031ee94438469732754cfdb23ae097adfe9336e Mon Sep 17 00:00:00 2001 From: Neel Bullywon Date: Sat, 28 Feb 2026 12:23:20 -0500 Subject: [PATCH 122/405] iio: magnetometer: bmc150_magn: use automated cleanup for mutex Use guard() and scoped_guard() to replace manual mutex lock/unlock calls. This simplifies error handling and ensures RAII-style cleanup. guard() is used in read_raw, write_raw, trig_reen, and trigger_set_state. Case blocks using guard() in read_raw and write_raw are wrapped in braces at the case label level to ensure clear scope for the cleanup guards. A bmc150_magn_set_power_mode_locked() helper is added to deduplicate the lock-call-unlock pattern used by remove, runtime_suspend, suspend, and resume. The trigger_handler function is left unchanged as mixing guard() with goto error paths can be fragile. Signed-off-by: Neel Bullywon Acked-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/bmc150_magn.c | 112 ++++++++++--------------- 1 file changed, 44 insertions(+), 68 deletions(-) diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index 04c4619dfc24..bf2551988008 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -257,6 +258,13 @@ static int bmc150_magn_set_power_mode(struct bmc150_magn_data *data, return -EINVAL; } +static int bmc150_magn_set_power_mode_locked(struct bmc150_magn_data *data, + enum bmc150_magn_power_modes mode) +{ + guard(mutex)(&data->mutex); + return bmc150_magn_set_power_mode(data, mode, true); +} + static int bmc150_magn_set_power_state(struct bmc150_magn_data *data, bool on) { int ret = 0; @@ -455,33 +463,29 @@ static int bmc150_magn_read_raw(struct iio_dev *indio_dev, s32 values[AXIS_XYZ_MAX]; switch (mask) { - case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_RAW: { if (iio_buffer_enabled(indio_dev)) return -EBUSY; - mutex_lock(&data->mutex); + + guard(mutex)(&data->mutex); ret = bmc150_magn_set_power_state(data, true); - if (ret < 0) { - mutex_unlock(&data->mutex); + if (ret < 0) return ret; - } ret = bmc150_magn_read_xyz(data, values); if (ret < 0) { bmc150_magn_set_power_state(data, false); - mutex_unlock(&data->mutex); return ret; } *val = values[chan->scan_index]; ret = bmc150_magn_set_power_state(data, false); - if (ret < 0) { - mutex_unlock(&data->mutex); + if (ret < 0) return ret; - } - mutex_unlock(&data->mutex); return IIO_VAL_INT; + } case IIO_CHAN_INFO_SCALE: /* * The API/driver performs an off-chip temperature @@ -529,48 +533,39 @@ static int bmc150_magn_write_raw(struct iio_dev *indio_dev, int ret; switch (mask) { - case IIO_CHAN_INFO_SAMP_FREQ: + case IIO_CHAN_INFO_SAMP_FREQ: { if (val > data->max_odr) return -EINVAL; - mutex_lock(&data->mutex); - ret = bmc150_magn_set_odr(data, val); - mutex_unlock(&data->mutex); - return ret; + guard(mutex)(&data->mutex); + return bmc150_magn_set_odr(data, val); + } case IIO_CHAN_INFO_OVERSAMPLING_RATIO: switch (chan->channel2) { case IIO_MOD_X: - case IIO_MOD_Y: + case IIO_MOD_Y: { if (val < 1 || val > 511) return -EINVAL; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); ret = bmc150_magn_set_max_odr(data, val, 0, 0); - if (ret < 0) { - mutex_unlock(&data->mutex); + if (ret < 0) return ret; - } - ret = regmap_update_bits(data->regmap, + return regmap_update_bits(data->regmap, BMC150_MAGN_REG_REP_XY, BMC150_MAGN_REG_REP_DATAMASK, - BMC150_MAGN_REPXY_TO_REGVAL - (val)); - mutex_unlock(&data->mutex); - return ret; - case IIO_MOD_Z: + BMC150_MAGN_REPXY_TO_REGVAL(val)); + } + case IIO_MOD_Z: { if (val < 1 || val > 256) return -EINVAL; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); ret = bmc150_magn_set_max_odr(data, 0, val, 0); - if (ret < 0) { - mutex_unlock(&data->mutex); + if (ret < 0) return ret; - } - ret = regmap_update_bits(data->regmap, + return regmap_update_bits(data->regmap, BMC150_MAGN_REG_REP_Z, BMC150_MAGN_REG_REP_DATAMASK, - BMC150_MAGN_REPZ_TO_REGVAL - (val)); - mutex_unlock(&data->mutex); - return ret; + BMC150_MAGN_REPZ_TO_REGVAL(val)); + } default: return -EINVAL; } @@ -785,9 +780,8 @@ static void bmc150_magn_trig_reen(struct iio_trigger *trig) if (!data->dready_trigger_on) return; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); ret = bmc150_magn_reset_intr(data); - mutex_unlock(&data->mutex); if (ret) dev_err(data->dev, "Failed to reset interrupt\n"); } @@ -797,32 +791,28 @@ static int bmc150_magn_data_rdy_trigger_set_state(struct iio_trigger *trig, { struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); struct bmc150_magn_data *data = iio_priv(indio_dev); - int ret = 0; + int ret; + + guard(mutex)(&data->mutex); - mutex_lock(&data->mutex); if (state == data->dready_trigger_on) - goto err_unlock; + return 0; ret = regmap_update_bits(data->regmap, BMC150_MAGN_REG_INT_DRDY, BMC150_MAGN_MASK_DRDY_EN, state << BMC150_MAGN_SHIFT_DRDY_EN); if (ret < 0) - goto err_unlock; + return ret; data->dready_trigger_on = state; if (state) { ret = bmc150_magn_reset_intr(data); if (ret < 0) - goto err_unlock; + return ret; } - mutex_unlock(&data->mutex); return 0; - -err_unlock: - mutex_unlock(&data->mutex); - return ret; } static const struct iio_trigger_ops bmc150_magn_trigger_ops = { @@ -980,9 +970,7 @@ void bmc150_magn_remove(struct device *dev) if (data->dready_trig) iio_trigger_unregister(data->dready_trig); - mutex_lock(&data->mutex); - bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true); - mutex_unlock(&data->mutex); + bmc150_magn_set_power_mode_locked(data, BMC150_MAGN_POWER_MODE_SUSPEND); regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); } @@ -995,10 +983,8 @@ static int bmc150_magn_runtime_suspend(struct device *dev) struct bmc150_magn_data *data = iio_priv(indio_dev); int ret; - mutex_lock(&data->mutex); - ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SLEEP, - true); - mutex_unlock(&data->mutex); + ret = bmc150_magn_set_power_mode_locked(data, + BMC150_MAGN_POWER_MODE_SLEEP); if (ret < 0) { dev_err(dev, "powering off device failed\n"); return ret; @@ -1024,28 +1010,18 @@ static int bmc150_magn_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmc150_magn_data *data = iio_priv(indio_dev); - int ret; - mutex_lock(&data->mutex); - ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SLEEP, - true); - mutex_unlock(&data->mutex); - - return ret; + return bmc150_magn_set_power_mode_locked(data, + BMC150_MAGN_POWER_MODE_SLEEP); } static int bmc150_magn_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmc150_magn_data *data = iio_priv(indio_dev); - int ret; - mutex_lock(&data->mutex); - ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_NORMAL, - true); - mutex_unlock(&data->mutex); - - return ret; + return bmc150_magn_set_power_mode_locked(data, + BMC150_MAGN_POWER_MODE_NORMAL); } #endif From ef7d4aaf686785b8b0f70c9fe373fb5455d3e5b5 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Thu, 26 Feb 2026 09:23:49 +0000 Subject: [PATCH 123/405] coresight: cti: Make spinlock usage consistent The spinlock is acquired sometimes with IRQs disabled and sometimes without. This leads to inconsistent semantics: the lock can be either HARDIRQ-safe or HARDIRQ-unsafe, which may trigger lockdep complaints. Make spinlock usage consistent by acquiring it with disabling IRQs. It is possible for sysfs knobs to acquire the spinlock for accessing a CTI device, while at the same time a perf session sends an IPI to enable the same CTI device. In this case, the spinlock must be IRQ-safe, which is why all lock acquisitions are changed to disable IRQs. Use guard() and scoped_guard() for spinlock to tidy up the code. Fixes: 984f37efa385 ("coresight: cti: Write regsiters directly in cti_enable_hw()") Tested-by: James Clark Reviewed-by: Mike Leach Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260226-arm_coresight_cti_refactor_v1-v2-1-b30fada3cfec@arm.com --- .../hwtracing/coresight/coresight-cti-core.c | 74 ++++----- .../hwtracing/coresight/coresight-cti-sysfs.c | 145 +++++++++--------- 2 files changed, 103 insertions(+), 116 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c b/drivers/hwtracing/coresight/coresight-cti-core.c index fddc8f31b91d..e3c98d89c987 100644 --- a/drivers/hwtracing/coresight/coresight-cti-core.c +++ b/drivers/hwtracing/coresight/coresight-cti-core.c @@ -81,10 +81,9 @@ void cti_write_all_hw_regs(struct cti_drvdata *drvdata) static int cti_enable_hw(struct cti_drvdata *drvdata) { struct cti_config *config = &drvdata->config; - unsigned long flags; - int rc = 0; + int rc; - raw_spin_lock_irqsave(&drvdata->spinlock, flags); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); /* no need to do anything if enabled or unpowered*/ if (config->hw_enabled || !config->hw_powered) @@ -93,22 +92,15 @@ static int cti_enable_hw(struct cti_drvdata *drvdata) /* claim the device */ rc = coresight_claim_device(drvdata->csdev); if (rc) - goto cti_err_not_enabled; + return rc; cti_write_all_hw_regs(drvdata); config->hw_enabled = true; - drvdata->config.enable_req_count++; - raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); - return rc; cti_state_unchanged: drvdata->config.enable_req_count++; - - /* cannot enable due to error */ -cti_err_not_enabled: - raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); - return rc; + return 0; } /* re-enable CTI on CPU when using CPU hotplug */ @@ -116,25 +108,21 @@ static void cti_cpuhp_enable_hw(struct cti_drvdata *drvdata) { struct cti_config *config = &drvdata->config; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + config->hw_powered = true; /* no need to do anything if no enable request */ if (!drvdata->config.enable_req_count) - goto cti_hp_not_enabled; + return; /* try to claim the device */ if (coresight_claim_device(drvdata->csdev)) - goto cti_hp_not_enabled; + return; cti_write_all_hw_regs(drvdata); config->hw_enabled = true; - raw_spin_unlock(&drvdata->spinlock); return; - - /* did not re-enable due to no claim / no request */ -cti_hp_not_enabled: - raw_spin_unlock(&drvdata->spinlock); } /* disable hardware */ @@ -142,23 +130,20 @@ static int cti_disable_hw(struct cti_drvdata *drvdata) { struct cti_config *config = &drvdata->config; struct coresight_device *csdev = drvdata->csdev; - int ret = 0; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); /* don't allow negative refcounts, return an error */ - if (!drvdata->config.enable_req_count) { - ret = -EINVAL; - goto cti_not_disabled; - } + if (!drvdata->config.enable_req_count) + return -EINVAL; /* check refcount - disable on 0 */ if (--drvdata->config.enable_req_count > 0) - goto cti_not_disabled; + return 0; /* no need to do anything if disabled or cpu unpowered */ if (!config->hw_enabled || !config->hw_powered) - goto cti_not_disabled; + return 0; CS_UNLOCK(drvdata->base); @@ -168,13 +153,7 @@ static int cti_disable_hw(struct cti_drvdata *drvdata) coresight_disclaim_device_unlocked(csdev); CS_LOCK(drvdata->base); - raw_spin_unlock(&drvdata->spinlock); - return ret; - - /* not disabled this call */ -cti_not_disabled: - raw_spin_unlock(&drvdata->spinlock); - return ret; + return 0; } void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) @@ -189,11 +168,11 @@ void cti_write_intack(struct device *dev, u32 ackval) struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); struct cti_config *config = &drvdata->config; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + /* write if enabled */ if (cti_active(config)) cti_write_single_reg(drvdata, CTIINTACK, ackval); - raw_spin_unlock(&drvdata->spinlock); } /* @@ -360,7 +339,7 @@ int cti_channel_trig_op(struct device *dev, enum cti_chan_op op, reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) : CTIOUTEN(trigger_idx)); - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); /* read - modify write - the trigger / channel enable value */ reg_value = direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] : @@ -379,7 +358,7 @@ int cti_channel_trig_op(struct device *dev, enum cti_chan_op op, /* write through if enabled */ if (cti_active(config)) cti_write_single_reg(drvdata, reg_offset, reg_value); - raw_spin_unlock(&drvdata->spinlock); + return 0; } @@ -397,7 +376,8 @@ int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op, chan_bitmask = BIT(channel_idx); - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + reg_value = config->ctigate; switch (op) { case CTI_GATE_CHAN_ENABLE: @@ -417,7 +397,7 @@ int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op, if (cti_active(config)) cti_write_single_reg(drvdata, CTIGATE, reg_value); } - raw_spin_unlock(&drvdata->spinlock); + return err; } @@ -436,7 +416,8 @@ int cti_channel_setop(struct device *dev, enum cti_chan_set_op op, chan_bitmask = BIT(channel_idx); - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + reg_value = config->ctiappset; switch (op) { case CTI_CHAN_SET: @@ -464,7 +445,6 @@ int cti_channel_setop(struct device *dev, enum cti_chan_set_op op, if ((err == 0) && cti_active(config)) cti_write_single_reg(drvdata, reg_offset, reg_value); - raw_spin_unlock(&drvdata->spinlock); return err; } @@ -667,7 +647,7 @@ static int cti_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, if (WARN_ON_ONCE(drvdata->ctidev.cpu != cpu)) return NOTIFY_BAD; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); switch (cmd) { case CPU_PM_ENTER: @@ -707,7 +687,6 @@ static int cti_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, } cti_notify_exit: - raw_spin_unlock(&drvdata->spinlock); return notify_res; } @@ -734,11 +713,12 @@ static int cti_dying_cpu(unsigned int cpu) if (!drvdata) return 0; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + drvdata->config.hw_powered = false; if (drvdata->config.hw_enabled) coresight_disclaim_device(drvdata->csdev); - raw_spin_unlock(&drvdata->spinlock); + return 0; } diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 572b80ee96fb..455d08bcccd4 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -84,11 +84,11 @@ static ssize_t enable_show(struct device *dev, bool enabled, powered; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); - raw_spin_lock(&drvdata->spinlock); - enable_req = drvdata->config.enable_req_count; - powered = drvdata->config.hw_powered; - enabled = drvdata->config.hw_enabled; - raw_spin_unlock(&drvdata->spinlock); + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { + enable_req = drvdata->config.enable_req_count; + powered = drvdata->config.hw_powered; + enabled = drvdata->config.hw_enabled; + } if (powered) return sprintf(buf, "%d\n", enabled); @@ -134,9 +134,8 @@ static ssize_t powered_show(struct device *dev, bool powered; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); - raw_spin_lock(&drvdata->spinlock); - powered = drvdata->config.hw_powered; - raw_spin_unlock(&drvdata->spinlock); + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) + powered = drvdata->config.hw_powered; return sprintf(buf, "%d\n", powered); } @@ -181,10 +180,12 @@ static ssize_t coresight_cti_reg_show(struct device *dev, u32 val = 0; pm_runtime_get_sync(dev->parent); - raw_spin_lock(&drvdata->spinlock); - if (drvdata->config.hw_powered) - val = readl_relaxed(drvdata->base + cti_attr->off); - raw_spin_unlock(&drvdata->spinlock); + + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { + if (drvdata->config.hw_powered) + val = readl_relaxed(drvdata->base + cti_attr->off); + } + pm_runtime_put_sync(dev->parent); return sysfs_emit(buf, "0x%x\n", val); } @@ -202,10 +203,12 @@ static __maybe_unused ssize_t coresight_cti_reg_store(struct device *dev, return -EINVAL; pm_runtime_get_sync(dev->parent); - raw_spin_lock(&drvdata->spinlock); - if (drvdata->config.hw_powered) - cti_write_single_reg(drvdata, cti_attr->off, val); - raw_spin_unlock(&drvdata->spinlock); + + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { + if (drvdata->config.hw_powered) + cti_write_single_reg(drvdata, cti_attr->off, val); + } + pm_runtime_put_sync(dev->parent); return size; } @@ -264,17 +267,18 @@ static ssize_t cti_reg32_show(struct device *dev, char *buf, struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); struct cti_config *config = &drvdata->config; - raw_spin_lock(&drvdata->spinlock); - if ((reg_offset >= 0) && cti_active(config)) { - CS_UNLOCK(drvdata->base); - val = readl_relaxed(drvdata->base + reg_offset); - if (pcached_val) - *pcached_val = val; - CS_LOCK(drvdata->base); - } else if (pcached_val) { - val = *pcached_val; + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { + if ((reg_offset >= 0) && cti_active(config)) { + CS_UNLOCK(drvdata->base); + val = readl_relaxed(drvdata->base + reg_offset); + if (pcached_val) + *pcached_val = val; + CS_LOCK(drvdata->base); + } else if (pcached_val) { + val = *pcached_val; + } } - raw_spin_unlock(&drvdata->spinlock); + return sprintf(buf, "%#x\n", val); } @@ -293,15 +297,16 @@ static ssize_t cti_reg32_store(struct device *dev, const char *buf, if (kstrtoul(buf, 0, &val)) return -EINVAL; - raw_spin_lock(&drvdata->spinlock); - /* local store */ - if (pcached_val) - *pcached_val = (u32)val; + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { + /* local store */ + if (pcached_val) + *pcached_val = (u32)val; + + /* write through if offset and enabled */ + if ((reg_offset >= 0) && cti_active(config)) + cti_write_single_reg(drvdata, reg_offset, val); + } - /* write through if offset and enabled */ - if ((reg_offset >= 0) && cti_active(config)) - cti_write_single_reg(drvdata, reg_offset, val); - raw_spin_unlock(&drvdata->spinlock); return size; } @@ -349,9 +354,9 @@ static ssize_t inout_sel_store(struct device *dev, if (val > (CTIINOUTEN_MAX - 1)) return -EINVAL; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + drvdata->config.ctiinout_sel = val; - raw_spin_unlock(&drvdata->spinlock); return size; } static DEVICE_ATTR_RW(inout_sel); @@ -364,10 +369,11 @@ static ssize_t inen_show(struct device *dev, int index; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); - raw_spin_lock(&drvdata->spinlock); - index = drvdata->config.ctiinout_sel; - val = drvdata->config.ctiinen[index]; - raw_spin_unlock(&drvdata->spinlock); + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { + index = drvdata->config.ctiinout_sel; + val = drvdata->config.ctiinen[index]; + } + return sprintf(buf, "%#lx\n", val); } @@ -383,14 +389,15 @@ static ssize_t inen_store(struct device *dev, if (kstrtoul(buf, 0, &val)) return -EINVAL; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + index = config->ctiinout_sel; config->ctiinen[index] = val; /* write through if enabled */ if (cti_active(config)) cti_write_single_reg(drvdata, CTIINEN(index), val); - raw_spin_unlock(&drvdata->spinlock); + return size; } static DEVICE_ATTR_RW(inen); @@ -403,10 +410,11 @@ static ssize_t outen_show(struct device *dev, int index; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); - raw_spin_lock(&drvdata->spinlock); - index = drvdata->config.ctiinout_sel; - val = drvdata->config.ctiouten[index]; - raw_spin_unlock(&drvdata->spinlock); + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { + index = drvdata->config.ctiinout_sel; + val = drvdata->config.ctiouten[index]; + } + return sprintf(buf, "%#lx\n", val); } @@ -422,14 +430,15 @@ static ssize_t outen_store(struct device *dev, if (kstrtoul(buf, 0, &val)) return -EINVAL; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + index = config->ctiinout_sel; config->ctiouten[index] = val; /* write through if enabled */ if (cti_active(config)) cti_write_single_reg(drvdata, CTIOUTEN(index), val); - raw_spin_unlock(&drvdata->spinlock); + return size; } static DEVICE_ATTR_RW(outen); @@ -463,7 +472,7 @@ static ssize_t appclear_store(struct device *dev, if (kstrtoul(buf, 0, &val)) return -EINVAL; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); /* a 1'b1 in appclr clears down the same bit in appset*/ config->ctiappset &= ~val; @@ -471,7 +480,7 @@ static ssize_t appclear_store(struct device *dev, /* write through if enabled */ if (cti_active(config)) cti_write_single_reg(drvdata, CTIAPPCLEAR, val); - raw_spin_unlock(&drvdata->spinlock); + return size; } static DEVICE_ATTR_WO(appclear); @@ -487,12 +496,12 @@ static ssize_t apppulse_store(struct device *dev, if (kstrtoul(buf, 0, &val)) return -EINVAL; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); /* write through if enabled */ if (cti_active(config)) cti_write_single_reg(drvdata, CTIAPPPULSE, val); - raw_spin_unlock(&drvdata->spinlock); + return size; } static DEVICE_ATTR_WO(apppulse); @@ -681,9 +690,9 @@ static ssize_t trig_filter_enable_show(struct device *dev, u32 val; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); - raw_spin_lock(&drvdata->spinlock); - val = drvdata->config.trig_filter_enable; - raw_spin_unlock(&drvdata->spinlock); + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) + val = drvdata->config.trig_filter_enable; + return sprintf(buf, "%d\n", val); } @@ -697,9 +706,9 @@ static ssize_t trig_filter_enable_store(struct device *dev, if (kstrtoul(buf, 0, &val)) return -EINVAL; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + drvdata->config.trig_filter_enable = !!val; - raw_spin_unlock(&drvdata->spinlock); return size; } static DEVICE_ATTR_RW(trig_filter_enable); @@ -728,7 +737,7 @@ static ssize_t chan_xtrigs_reset_store(struct device *dev, struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); struct cti_config *config = &drvdata->config; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); /* clear the CTI trigger / channel programming registers */ for (i = 0; i < config->nr_trig_max; i++) { @@ -747,7 +756,6 @@ static ssize_t chan_xtrigs_reset_store(struct device *dev, if (cti_active(config)) cti_write_all_hw_regs(drvdata); - raw_spin_unlock(&drvdata->spinlock); return size; } static DEVICE_ATTR_WO(chan_xtrigs_reset); @@ -768,9 +776,9 @@ static ssize_t chan_xtrigs_sel_store(struct device *dev, if (val > (drvdata->config.nr_ctm_channels - 1)) return -EINVAL; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + drvdata->config.xtrig_rchan_sel = val; - raw_spin_unlock(&drvdata->spinlock); return size; } @@ -781,9 +789,8 @@ static ssize_t chan_xtrigs_sel_show(struct device *dev, unsigned long val; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); - raw_spin_lock(&drvdata->spinlock); - val = drvdata->config.xtrig_rchan_sel; - raw_spin_unlock(&drvdata->spinlock); + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) + val = drvdata->config.xtrig_rchan_sel; return sprintf(buf, "%ld\n", val); } @@ -838,12 +845,12 @@ static ssize_t print_chan_list(struct device *dev, unsigned long inuse_bits = 0, chan_mask; /* scan regs to get bitmap of channels in use. */ - raw_spin_lock(&drvdata->spinlock); - for (i = 0; i < config->nr_trig_max; i++) { - inuse_bits |= config->ctiinen[i]; - inuse_bits |= config->ctiouten[i]; + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { + for (i = 0; i < config->nr_trig_max; i++) { + inuse_bits |= config->ctiinen[i]; + inuse_bits |= config->ctiouten[i]; + } } - raw_spin_unlock(&drvdata->spinlock); /* inverse bits if printing free channels */ if (!inuse) From 6582fe69ac4b1b575e023f5110427c15fbf5ad36 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Thu, 26 Feb 2026 09:23:50 +0000 Subject: [PATCH 124/405] coresight: cti: Fix register reads Introduce cti_read_single_reg() as an interface for reading registers with unlocking the CS lock. Consolidate register read in sysfs interfaces using this new helper. Fixes: b5213376c240 ("coresight: cti: Add sysfs access to program function registers") Fixes: 1a556ca6cc24 ("coresight: cti: Add sysfs coresight mgmt register access") Reviewed-by: Mike Leach Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260226-arm_coresight_cti_refactor_v1-v2-2-b30fada3cfec@arm.com --- drivers/hwtracing/coresight/coresight-cti-core.c | 11 +++++++++++ drivers/hwtracing/coresight/coresight-cti-sysfs.c | 6 ++---- drivers/hwtracing/coresight/coresight-cti.h | 1 + 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c b/drivers/hwtracing/coresight/coresight-cti-core.c index e3c98d89c987..6a53c3ceebf4 100644 --- a/drivers/hwtracing/coresight/coresight-cti-core.c +++ b/drivers/hwtracing/coresight/coresight-cti-core.c @@ -156,6 +156,17 @@ static int cti_disable_hw(struct cti_drvdata *drvdata) return 0; } +u32 cti_read_single_reg(struct cti_drvdata *drvdata, int offset) +{ + int val; + + CS_UNLOCK(drvdata->base); + val = readl_relaxed(drvdata->base + offset); + CS_LOCK(drvdata->base); + + return val; +} + void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) { CS_UNLOCK(drvdata->base); diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 455d08bcccd4..9a997b2f0904 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -183,7 +183,7 @@ static ssize_t coresight_cti_reg_show(struct device *dev, scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { if (drvdata->config.hw_powered) - val = readl_relaxed(drvdata->base + cti_attr->off); + val = cti_read_single_reg(drvdata, cti_attr->off); } pm_runtime_put_sync(dev->parent); @@ -269,11 +269,9 @@ static ssize_t cti_reg32_show(struct device *dev, char *buf, scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { if ((reg_offset >= 0) && cti_active(config)) { - CS_UNLOCK(drvdata->base); - val = readl_relaxed(drvdata->base + reg_offset); + val = cti_read_single_reg(drvdata, reg_offset); if (pcached_val) *pcached_val = val; - CS_LOCK(drvdata->base); } else if (pcached_val) { val = *pcached_val; } diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index daff9e32a6da..45735526fe55 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -220,6 +220,7 @@ int cti_disable(struct coresight_device *csdev, struct coresight_path *path); void cti_write_all_hw_regs(struct cti_drvdata *drvdata); void cti_write_intack(struct device *dev, u32 ackval); void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value); +u32 cti_read_single_reg(struct cti_drvdata *drvdata, int offset); int cti_channel_trig_op(struct device *dev, enum cti_chan_op op, enum cti_trig_dir direction, u32 channel_idx, u32 trigger_idx); From b4d9ef475ec71e13e1ec395e9b9e6b165506a564 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Thu, 26 Feb 2026 09:23:51 +0000 Subject: [PATCH 125/405] coresight: cti: Access ASICCTL only when implemented According to the Arm ARM (DDI 0487 L.b), ASICCTL is implemented only when CTIDEVID.EXTMUXNUM is non-zero. Based on CTIDEVID.EXTMUXNUM, add a flag 'asicctl_impl' to indicate whether the register is implemented, and access ASICCTL conditionally based on the flag. Allow the sysfs node to be visible only when the register is implemented. Reviewed-by: Mike Leach Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260226-arm_coresight_cti_refactor_v1-v2-3-b30fada3cfec@arm.com --- drivers/hwtracing/coresight/coresight-cti-core.c | 5 ++++- drivers/hwtracing/coresight/coresight-cti-sysfs.c | 13 +++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 2 ++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c b/drivers/hwtracing/coresight/coresight-cti-core.c index 6a53c3ceebf4..726970d852de 100644 --- a/drivers/hwtracing/coresight/coresight-cti-core.c +++ b/drivers/hwtracing/coresight/coresight-cti-core.c @@ -68,7 +68,8 @@ void cti_write_all_hw_regs(struct cti_drvdata *drvdata) /* other regs */ writel_relaxed(config->ctigate, drvdata->base + CTIGATE); - writel_relaxed(config->asicctl, drvdata->base + ASICCTL); + if (config->asicctl_impl) + writel_relaxed(config->asicctl, drvdata->base + ASICCTL); writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET); /* re-enable CTI */ @@ -221,6 +222,8 @@ static void cti_set_default_config(struct device *dev, config->trig_filter_enable = true; config->ctigate = GENMASK(config->nr_ctm_channels - 1, 0); config->enable_req_count = 0; + + config->asicctl_impl = !!FIELD_GET(GENMASK(4, 0), devid); } /* diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 9a997b2f0904..c15a580f6e90 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -537,6 +537,18 @@ static struct attribute *coresight_cti_regs_attrs[] = { NULL, }; +static umode_t coresight_cti_regs_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (attr == &dev_attr_asicctl.attr && !drvdata->config.asicctl_impl) + return 0; + + return attr->mode; +} + /* CTI channel x-trigger programming */ static int cti_trig_op_parse(struct device *dev, enum cti_chan_op op, @@ -1174,6 +1186,7 @@ static const struct attribute_group coresight_cti_mgmt_group = { static const struct attribute_group coresight_cti_regs_group = { .attrs = coresight_cti_regs_attrs, + .is_visible = coresight_cti_regs_is_visible, .name = "regs", }; diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index 45735526fe55..a4b3968d8d3d 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -119,6 +119,7 @@ struct cti_device { * @nr_trig_max: Max number of trigger signals implemented on device. * (max of trig_in or trig_out) - from ID register. * @nr_ctm_channels: number of available CTM channels - from ID register. + * @asicctl_impl: true if asicctl is implemented. * @enable_req_count: CTI is enabled alongside >=1 associated devices. * @hw_enabled: true if hw is currently enabled. * @hw_powered: true if associated cpu powered on, or no cpu. @@ -140,6 +141,7 @@ struct cti_config { /* hardware description */ int nr_ctm_channels; int nr_trig_max; + bool asicctl_impl; /* cti enable control */ int enable_req_count; From 59213b4be5c109414f8240f6389b3fc3a7f48b4d Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Thu, 26 Feb 2026 09:23:52 +0000 Subject: [PATCH 126/405] coresight: cti: Remove CPU power management code According Arm ARM, the CTI ASICCTL register: "It is IMPLEMENTATION DEFINED whether ASICCTL is implemented in the Core power domain or in the Debug power domain." This is the only CTI register that may reside in the core power domain. However, it has been confirmed that Arm designed CTIs place ASICCTL in the debug power domain. Furthermore, ASICCTL is implemented only when CTIDEVID.EXTMUXNUM is non-zero, which is a rare case for CPU CTIs. For these reasons, it is safe to conclude that all CTI registers are not located in the CPU power domain. Therefore, the CTI driver does not need CPU power management. This commit removes the CPU power management from CTI driver. Reviewed-by: Mike Leach Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260226-arm_coresight_cti_refactor_v1-v2-4-b30fada3cfec@arm.com --- .../hwtracing/coresight/coresight-cti-core.c | 186 +----------------- 1 file changed, 3 insertions(+), 183 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c b/drivers/hwtracing/coresight/coresight-cti-core.c index 726970d852de..3aa081f28a18 100644 --- a/drivers/hwtracing/coresight/coresight-cti-core.c +++ b/drivers/hwtracing/coresight/coresight-cti-core.c @@ -42,12 +42,6 @@ static DEFINE_MUTEX(ect_mutex); #define csdev_to_cti_drvdata(csdev) \ dev_get_drvdata(csdev->dev.parent) -/* power management handling */ -static int nr_cti_cpu; - -/* quick lookup list for CPU bound CTIs when power handling */ -static struct cti_drvdata *cti_cpu_drvdata[NR_CPUS]; - /* write set of regs to hardware - call with spinlock claimed */ void cti_write_all_hw_regs(struct cti_drvdata *drvdata) { @@ -104,28 +98,6 @@ static int cti_enable_hw(struct cti_drvdata *drvdata) return 0; } -/* re-enable CTI on CPU when using CPU hotplug */ -static void cti_cpuhp_enable_hw(struct cti_drvdata *drvdata) -{ - struct cti_config *config = &drvdata->config; - - guard(raw_spinlock_irqsave)(&drvdata->spinlock); - - config->hw_powered = true; - - /* no need to do anything if no enable request */ - if (!drvdata->config.enable_req_count) - return; - - /* try to claim the device */ - if (coresight_claim_device(drvdata->csdev)) - return; - - cti_write_all_hw_regs(drvdata); - config->hw_enabled = true; - return; -} - /* disable hardware */ static int cti_disable_hw(struct cti_drvdata *drvdata) { @@ -643,146 +615,6 @@ static void cti_remove_conn_xrefs(struct cti_drvdata *drvdata) } } -/** cti PM callbacks **/ -static int cti_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, - void *v) -{ - struct cti_drvdata *drvdata; - struct coresight_device *csdev; - unsigned int cpu = smp_processor_id(); - int notify_res = NOTIFY_OK; - - if (!cti_cpu_drvdata[cpu]) - return NOTIFY_OK; - - drvdata = cti_cpu_drvdata[cpu]; - csdev = drvdata->csdev; - - if (WARN_ON_ONCE(drvdata->ctidev.cpu != cpu)) - return NOTIFY_BAD; - - guard(raw_spinlock_irqsave)(&drvdata->spinlock); - - switch (cmd) { - case CPU_PM_ENTER: - /* CTI regs all static - we have a copy & nothing to save */ - drvdata->config.hw_powered = false; - if (drvdata->config.hw_enabled) - coresight_disclaim_device(csdev); - break; - - case CPU_PM_ENTER_FAILED: - drvdata->config.hw_powered = true; - if (drvdata->config.hw_enabled) { - if (coresight_claim_device(csdev)) - drvdata->config.hw_enabled = false; - } - break; - - case CPU_PM_EXIT: - /* write hardware registers to re-enable. */ - drvdata->config.hw_powered = true; - drvdata->config.hw_enabled = false; - - /* check enable reference count to enable HW */ - if (drvdata->config.enable_req_count) { - /* check we can claim the device as we re-power */ - if (coresight_claim_device(csdev)) - goto cti_notify_exit; - - drvdata->config.hw_enabled = true; - cti_write_all_hw_regs(drvdata); - } - break; - - default: - notify_res = NOTIFY_DONE; - break; - } - -cti_notify_exit: - return notify_res; -} - -static struct notifier_block cti_cpu_pm_nb = { - .notifier_call = cti_cpu_pm_notify, -}; - -/* CPU HP handlers */ -static int cti_starting_cpu(unsigned int cpu) -{ - struct cti_drvdata *drvdata = cti_cpu_drvdata[cpu]; - - if (!drvdata) - return 0; - - cti_cpuhp_enable_hw(drvdata); - return 0; -} - -static int cti_dying_cpu(unsigned int cpu) -{ - struct cti_drvdata *drvdata = cti_cpu_drvdata[cpu]; - - if (!drvdata) - return 0; - - guard(raw_spinlock_irqsave)(&drvdata->spinlock); - - drvdata->config.hw_powered = false; - if (drvdata->config.hw_enabled) - coresight_disclaim_device(drvdata->csdev); - - return 0; -} - -static int cti_pm_setup(struct cti_drvdata *drvdata) -{ - int ret; - - if (drvdata->ctidev.cpu == -1) - return 0; - - if (nr_cti_cpu) - goto done; - - cpus_read_lock(); - ret = cpuhp_setup_state_nocalls_cpuslocked( - CPUHP_AP_ARM_CORESIGHT_CTI_STARTING, - "arm/coresight_cti:starting", - cti_starting_cpu, cti_dying_cpu); - if (ret) { - cpus_read_unlock(); - return ret; - } - - ret = cpu_pm_register_notifier(&cti_cpu_pm_nb); - cpus_read_unlock(); - if (ret) { - cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_CTI_STARTING); - return ret; - } - -done: - nr_cti_cpu++; - cti_cpu_drvdata[drvdata->ctidev.cpu] = drvdata; - - return 0; -} - -/* release PM registrations */ -static void cti_pm_release(struct cti_drvdata *drvdata) -{ - if (drvdata->ctidev.cpu == -1) - return; - - cti_cpu_drvdata[drvdata->ctidev.cpu] = NULL; - if (--nr_cti_cpu == 0) { - cpu_pm_unregister_notifier(&cti_cpu_pm_nb); - cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_CTI_STARTING); - } -} - /** cti ect operations **/ int cti_enable(struct coresight_device *csdev, enum cs_mode mode, struct coresight_path *path) @@ -815,7 +647,6 @@ static void cti_remove(struct amba_device *adev) mutex_lock(&ect_mutex); cti_remove_conn_xrefs(drvdata); - cti_pm_release(drvdata); /* remove from the list */ list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, node) { @@ -889,17 +720,12 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) if (!cti_desc.name) return -ENOMEM; - /* setup CPU power management handling for CPU bound CTI devices. */ - ret = cti_pm_setup(drvdata); - if (ret) - return ret; - /* create dynamic attributes for connections */ ret = cti_create_cons_sysfs(dev, drvdata); if (ret) { dev_err(dev, "%s: create dynamic sysfs entries failed\n", cti_desc.name); - goto pm_release; + return ret; } /* set up coresight component description */ @@ -912,10 +738,8 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) coresight_clear_self_claim_tag(&cti_desc.access); drvdata->csdev = coresight_register(&cti_desc); - if (IS_ERR(drvdata->csdev)) { - ret = PTR_ERR(drvdata->csdev); - goto pm_release; - } + if (IS_ERR(drvdata->csdev)) + return PTR_ERR(drvdata->csdev); /* add to list of CTI devices */ mutex_lock(&ect_mutex); @@ -928,10 +752,6 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) pm_runtime_put(&adev->dev); dev_info(&drvdata->csdev->dev, "CTI initialized\n"); return 0; - -pm_release: - cti_pm_release(drvdata); - return ret; } static struct amba_cs_uci_id uci_id_cti[] = { From b727e7bba3ff2b4da3e71628c85f2c2dd9ef471a Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Thu, 26 Feb 2026 09:23:53 +0000 Subject: [PATCH 127/405] coresight: cti: Rename cti_active() to cti_is_active() Rename cti_active() to cti_is_active() to clarify that it checks whether the CTI device is active. Reviewed-by: Mike Leach Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260226-arm_coresight_cti_refactor_v1-v2-5-b30fada3cfec@arm.com --- drivers/hwtracing/coresight/coresight-cti-core.c | 8 ++++---- drivers/hwtracing/coresight/coresight-cti-sysfs.c | 14 +++++++------- drivers/hwtracing/coresight/coresight-cti.h | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c b/drivers/hwtracing/coresight/coresight-cti-core.c index 3aa081f28a18..28f995263433 100644 --- a/drivers/hwtracing/coresight/coresight-cti-core.c +++ b/drivers/hwtracing/coresight/coresight-cti-core.c @@ -155,7 +155,7 @@ void cti_write_intack(struct device *dev, u32 ackval) guard(raw_spinlock_irqsave)(&drvdata->spinlock); /* write if enabled */ - if (cti_active(config)) + if (cti_is_active(config)) cti_write_single_reg(drvdata, CTIINTACK, ackval); } @@ -342,7 +342,7 @@ int cti_channel_trig_op(struct device *dev, enum cti_chan_op op, config->ctiouten[trigger_idx] = reg_value; /* write through if enabled */ - if (cti_active(config)) + if (cti_is_active(config)) cti_write_single_reg(drvdata, reg_offset, reg_value); return 0; @@ -380,7 +380,7 @@ int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op, } if (err == 0) { config->ctigate = reg_value; - if (cti_active(config)) + if (cti_is_active(config)) cti_write_single_reg(drvdata, CTIGATE, reg_value); } @@ -429,7 +429,7 @@ int cti_channel_setop(struct device *dev, enum cti_chan_set_op op, break; } - if ((err == 0) && cti_active(config)) + if ((err == 0) && cti_is_active(config)) cti_write_single_reg(drvdata, reg_offset, reg_value); return err; diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index c15a580f6e90..a22cc9a2bee2 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -268,7 +268,7 @@ static ssize_t cti_reg32_show(struct device *dev, char *buf, struct cti_config *config = &drvdata->config; scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { - if ((reg_offset >= 0) && cti_active(config)) { + if ((reg_offset >= 0) && cti_is_active(config)) { val = cti_read_single_reg(drvdata, reg_offset); if (pcached_val) *pcached_val = val; @@ -301,7 +301,7 @@ static ssize_t cti_reg32_store(struct device *dev, const char *buf, *pcached_val = (u32)val; /* write through if offset and enabled */ - if ((reg_offset >= 0) && cti_active(config)) + if ((reg_offset >= 0) && cti_is_active(config)) cti_write_single_reg(drvdata, reg_offset, val); } @@ -393,7 +393,7 @@ static ssize_t inen_store(struct device *dev, config->ctiinen[index] = val; /* write through if enabled */ - if (cti_active(config)) + if (cti_is_active(config)) cti_write_single_reg(drvdata, CTIINEN(index), val); return size; @@ -434,7 +434,7 @@ static ssize_t outen_store(struct device *dev, config->ctiouten[index] = val; /* write through if enabled */ - if (cti_active(config)) + if (cti_is_active(config)) cti_write_single_reg(drvdata, CTIOUTEN(index), val); return size; @@ -476,7 +476,7 @@ static ssize_t appclear_store(struct device *dev, config->ctiappset &= ~val; /* write through if enabled */ - if (cti_active(config)) + if (cti_is_active(config)) cti_write_single_reg(drvdata, CTIAPPCLEAR, val); return size; @@ -497,7 +497,7 @@ static ssize_t apppulse_store(struct device *dev, guard(raw_spinlock_irqsave)(&drvdata->spinlock); /* write through if enabled */ - if (cti_active(config)) + if (cti_is_active(config)) cti_write_single_reg(drvdata, CTIAPPPULSE, val); return size; @@ -763,7 +763,7 @@ static ssize_t chan_xtrigs_reset_store(struct device *dev, config->xtrig_rchan_sel = 0; /* if enabled then write through */ - if (cti_active(config)) + if (cti_is_active(config)) cti_write_all_hw_regs(drvdata); return size; diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index a4b3968d8d3d..2edc0d01812c 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -236,7 +236,7 @@ coresight_cti_get_platform_data(struct device *dev); const char *cti_plat_get_node_name(struct fwnode_handle *fwnode); /* cti powered and enabled */ -static inline bool cti_active(struct cti_config *cfg) +static inline bool cti_is_active(struct cti_config *cfg) { return cfg->hw_powered && cfg->hw_enabled; } From daedb30fd6ac68a100912407aba3a4fdba8a4080 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Thu, 26 Feb 2026 09:23:54 +0000 Subject: [PATCH 128/405] coresight: cti: Remove hw_powered flag Since the CPU PM code has been removed from the CTI driver and the device is enabled via runtime PM, pm_runtime_active() can be used to check whether the device is powered. As a result, the hw_powered flag is redundant, remove it. Reviewed-by: Mike Leach Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260226-arm_coresight_cti_refactor_v1-v2-6-b30fada3cfec@arm.com --- .../hwtracing/coresight/coresight-cti-core.c | 11 +++---- .../hwtracing/coresight/coresight-cti-sysfs.c | 29 +++++-------------- drivers/hwtracing/coresight/coresight-cti.h | 6 ++-- 3 files changed, 13 insertions(+), 33 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c b/drivers/hwtracing/coresight/coresight-cti-core.c index 28f995263433..5ac36f077618 100644 --- a/drivers/hwtracing/coresight/coresight-cti-core.c +++ b/drivers/hwtracing/coresight/coresight-cti-core.c @@ -80,8 +80,8 @@ static int cti_enable_hw(struct cti_drvdata *drvdata) guard(raw_spinlock_irqsave)(&drvdata->spinlock); - /* no need to do anything if enabled or unpowered*/ - if (config->hw_enabled || !config->hw_powered) + /* no need to do anything if enabled */ + if (config->hw_enabled) goto cti_state_unchanged; /* claim the device */ @@ -114,8 +114,8 @@ static int cti_disable_hw(struct cti_drvdata *drvdata) if (--drvdata->config.enable_req_count > 0) return 0; - /* no need to do anything if disabled or cpu unpowered */ - if (!config->hw_enabled || !config->hw_powered) + /* no need to do anything if disabled */ + if (!config->hw_enabled) return 0; CS_UNLOCK(drvdata->base); @@ -702,9 +702,6 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) return PTR_ERR(pdata); } - /* default to powered - could change on PM notifications */ - drvdata->config.hw_powered = true; - /* * Set up device name - will depend if cpu bound or otherwise. * diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index a22cc9a2bee2..9ab586a5c9a4 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -81,19 +81,12 @@ static ssize_t enable_show(struct device *dev, char *buf) { int enable_req; - bool enabled, powered; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); - scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) enable_req = drvdata->config.enable_req_count; - powered = drvdata->config.hw_powered; - enabled = drvdata->config.hw_enabled; - } - if (powered) - return sprintf(buf, "%d\n", enabled); - else - return sprintf(buf, "%d\n", !!enable_req); + return sprintf(buf, "%d\n", !!enable_req); } static ssize_t enable_store(struct device *dev, @@ -131,11 +124,7 @@ static ssize_t powered_show(struct device *dev, struct device_attribute *attr, char *buf) { - bool powered; - struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); - - scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) - powered = drvdata->config.hw_powered; + bool powered = pm_runtime_active(dev->parent); return sprintf(buf, "%d\n", powered); } @@ -181,10 +170,8 @@ static ssize_t coresight_cti_reg_show(struct device *dev, pm_runtime_get_sync(dev->parent); - scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { - if (drvdata->config.hw_powered) - val = cti_read_single_reg(drvdata, cti_attr->off); - } + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) + val = cti_read_single_reg(drvdata, cti_attr->off); pm_runtime_put_sync(dev->parent); return sysfs_emit(buf, "0x%x\n", val); @@ -204,10 +191,8 @@ static __maybe_unused ssize_t coresight_cti_reg_store(struct device *dev, pm_runtime_get_sync(dev->parent); - scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { - if (drvdata->config.hw_powered) - cti_write_single_reg(drvdata, cti_attr->off, val); - } + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) + cti_write_single_reg(drvdata, cti_attr->off, val); pm_runtime_put_sync(dev->parent); return size; diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index 2edc0d01812c..8754cb5def79 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -122,7 +122,6 @@ struct cti_device { * @asicctl_impl: true if asicctl is implemented. * @enable_req_count: CTI is enabled alongside >=1 associated devices. * @hw_enabled: true if hw is currently enabled. - * @hw_powered: true if associated cpu powered on, or no cpu. * @trig_in_use: bitfield of in triggers registered as in use. * @trig_out_use: bitfield of out triggers registered as in use. * @trig_out_filter: bitfield of out triggers that are blocked if filter @@ -146,7 +145,6 @@ struct cti_config { /* cti enable control */ int enable_req_count; bool hw_enabled; - bool hw_powered; /* registered triggers and filtering */ u32 trig_in_use; @@ -235,10 +233,10 @@ struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev); const char *cti_plat_get_node_name(struct fwnode_handle *fwnode); -/* cti powered and enabled */ +/* Check if a cti device is enabled */ static inline bool cti_is_active(struct cti_config *cfg) { - return cfg->hw_powered && cfg->hw_enabled; + return cfg->hw_enabled; } #endif /* _CORESIGHT_CORESIGHT_CTI_H */ From d5e57babdffb961ca8b51910d00c2c0ddb03c54a Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Thu, 26 Feb 2026 09:23:55 +0000 Subject: [PATCH 129/405] coresight: cti: Remove hw_enabled flag The enable_req_count field already tracks whether the CTI device is enabled. A non-zero value indicates that the device is active, the hw_enabled flag is redundant if so. Remove hw_enabled and update cti_is_active() to check enable_req_count. Replace open-coded enable_req_count checks with cti_is_active(). Reviewed-by: Mike Leach Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260226-arm_coresight_cti_refactor_v1-v2-7-b30fada3cfec@arm.com --- drivers/hwtracing/coresight/coresight-cti-core.c | 11 ++--------- drivers/hwtracing/coresight/coresight-cti-sysfs.c | 2 +- drivers/hwtracing/coresight/coresight-cti.h | 4 +--- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c b/drivers/hwtracing/coresight/coresight-cti-core.c index 5ac36f077618..2f4c9362709a 100644 --- a/drivers/hwtracing/coresight/coresight-cti-core.c +++ b/drivers/hwtracing/coresight/coresight-cti-core.c @@ -81,7 +81,7 @@ static int cti_enable_hw(struct cti_drvdata *drvdata) guard(raw_spinlock_irqsave)(&drvdata->spinlock); /* no need to do anything if enabled */ - if (config->hw_enabled) + if (cti_is_active(config)) goto cti_state_unchanged; /* claim the device */ @@ -91,8 +91,6 @@ static int cti_enable_hw(struct cti_drvdata *drvdata) cti_write_all_hw_regs(drvdata); - config->hw_enabled = true; - cti_state_unchanged: drvdata->config.enable_req_count++; return 0; @@ -107,22 +105,17 @@ static int cti_disable_hw(struct cti_drvdata *drvdata) guard(raw_spinlock_irqsave)(&drvdata->spinlock); /* don't allow negative refcounts, return an error */ - if (!drvdata->config.enable_req_count) + if (!cti_is_active(config)) return -EINVAL; /* check refcount - disable on 0 */ if (--drvdata->config.enable_req_count > 0) return 0; - /* no need to do anything if disabled */ - if (!config->hw_enabled) - return 0; - CS_UNLOCK(drvdata->base); /* disable CTI */ writel_relaxed(0, drvdata->base + CTICONTROL); - config->hw_enabled = false; coresight_disclaim_device_unlocked(csdev); CS_LOCK(drvdata->base); diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 9ab586a5c9a4..9ef44956ebdc 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -84,7 +84,7 @@ static ssize_t enable_show(struct device *dev, struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) - enable_req = drvdata->config.enable_req_count; + enable_req = cti_is_active(&drvdata->config); return sprintf(buf, "%d\n", !!enable_req); } diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index 8754cb5def79..c5f9e79fabc6 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -121,7 +121,6 @@ struct cti_device { * @nr_ctm_channels: number of available CTM channels - from ID register. * @asicctl_impl: true if asicctl is implemented. * @enable_req_count: CTI is enabled alongside >=1 associated devices. - * @hw_enabled: true if hw is currently enabled. * @trig_in_use: bitfield of in triggers registered as in use. * @trig_out_use: bitfield of out triggers registered as in use. * @trig_out_filter: bitfield of out triggers that are blocked if filter @@ -144,7 +143,6 @@ struct cti_config { /* cti enable control */ int enable_req_count; - bool hw_enabled; /* registered triggers and filtering */ u32 trig_in_use; @@ -236,7 +234,7 @@ const char *cti_plat_get_node_name(struct fwnode_handle *fwnode); /* Check if a cti device is enabled */ static inline bool cti_is_active(struct cti_config *cfg) { - return cfg->hw_enabled; + return !!cfg->enable_req_count; } #endif /* _CORESIGHT_CORESIGHT_CTI_H */ From 9c5ef7a30d9044f8706bd02bfdc4eff7266f3e25 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Thu, 26 Feb 2026 09:23:56 +0000 Subject: [PATCH 130/405] coresight: cti: Properly handle negative offsets in cti_reg32_{show|store}() Return an error when the offset is negative. Signed-off-by: Leo Yan Reviewed-by: Mike Leach Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260226-arm_coresight_cti_refactor_v1-v2-8-b30fada3cfec@arm.com --- drivers/hwtracing/coresight/coresight-cti-sysfs.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 9ef44956ebdc..4c0a60840efb 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -252,8 +252,11 @@ static ssize_t cti_reg32_show(struct device *dev, char *buf, struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); struct cti_config *config = &drvdata->config; + if (reg_offset < 0) + return -EINVAL; + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { - if ((reg_offset >= 0) && cti_is_active(config)) { + if (cti_is_active(config)) { val = cti_read_single_reg(drvdata, reg_offset); if (pcached_val) *pcached_val = val; @@ -280,13 +283,16 @@ static ssize_t cti_reg32_store(struct device *dev, const char *buf, if (kstrtoul(buf, 0, &val)) return -EINVAL; + if (reg_offset < 0) + return -EINVAL; + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { /* local store */ if (pcached_val) *pcached_val = (u32)val; /* write through if offset and enabled */ - if ((reg_offset >= 0) && cti_is_active(config)) + if (cti_is_active(config)) cti_write_single_reg(drvdata, reg_offset, val); } From d43795cb35b4b7c8db50c67a3459e3964829aca9 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 30 Jan 2026 15:50:05 +0200 Subject: [PATCH 131/405] iio: frequency: admv4420: return proper error code from admv4420_calc_parameters() Return -EINVAL instead of -1 when no valid PLL parameters solution is found. Using standard kernel error codes ensures consistency and proper error propagation through the call chain. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/admv4420.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/frequency/admv4420.c b/drivers/iio/frequency/admv4420.c index 3ae462b4f5c9..8748d9747639 100644 --- a/drivers/iio/frequency/admv4420.c +++ b/drivers/iio/frequency/admv4420.c @@ -243,7 +243,7 @@ static int admv4420_calc_parameters(struct admv4420_state *st) st->n_counter.n_counter = 1; } if (!sol_found) - return -1; + return -EINVAL; st->n_counter.int_val = div_u64_rem(st->n_counter.n_counter, 10, &st->n_counter.frac_val); st->n_counter.mod_val = 10; From f7c0ea2e782f5cf46f8c78859368106476e5f946 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 30 Jan 2026 14:23:08 +0200 Subject: [PATCH 132/405] MAINTAINERS: add entry for ADL8113 driver Add MAINTAINERS entry for the ADL8113 driver. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 1c75276404df..08d8ddf4ef68 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1711,6 +1711,14 @@ S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/imu/adi,adis16550.yaml +ANALOG DEVICES INC ADL8113 DRIVER +M: Antoniu Miclaus +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/amplifiers/adi,adl8113.yaml +F: drivers/iio/amplifiers/adl8113.c + ANALOG DEVICES INC ADM1177 DRIVER M: Michael Hennerich L: linux-hwmon@vger.kernel.org From f4e466aac34013904d81acfacf1f2453068578a2 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Thu, 29 Jan 2026 20:14:52 +0200 Subject: [PATCH 133/405] iio: pressure: hsc030pa: Improve i2c_transfer return value handling The i2c_transfer() function returns the number of messages successfully transferred. The function sends 1 message but checks for ret == 2, which can never be true. In practice this has no impact since the caller checks ret < 0, and the erroneous return value of 1 is not treated as an error. Improve the return value handling to properly distinguish between I2C errors and unexpected transfer counts. Signed-off-by: Antoniu Miclaus Tested-by: Petre Rodan Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/hsc030pa_i2c.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/iio/pressure/hsc030pa_i2c.c b/drivers/iio/pressure/hsc030pa_i2c.c index a34ef4653f34..3500bda03d75 100644 --- a/drivers/iio/pressure/hsc030pa_i2c.c +++ b/drivers/iio/pressure/hsc030pa_i2c.c @@ -34,8 +34,13 @@ static int hsc_i2c_recv(struct hsc_data *data) msg.buf = data->buffer; ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) + return ret; - return (ret == 2) ? 0 : ret; + if (ret != 1) + return -EIO; + + return 0; } static int hsc_i2c_probe(struct i2c_client *client) From 1bceffda64eeb7d24c4bfea9f0514b8775afb709 Mon Sep 17 00:00:00 2001 From: SAJJA EASWAR SAI Date: Fri, 30 Jan 2026 02:34:31 +0530 Subject: [PATCH 134/405] iio: light: apds9306: remove redundant explicit pointer cast C allows implicit conversion from void * to struct apds9306_data *, so the explicit cast on 'ptr' is unnecessary. Removing it improves readability. Signed-off-by: SAJJA EASWAR SAI Acked-by: Subhajit Ghosh Signed-off-by: Jonathan Cameron --- drivers/iio/light/apds9306.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c index 7e68cca0edfa..5d18eabd961e 100644 --- a/drivers/iio/light/apds9306.c +++ b/drivers/iio/light/apds9306.c @@ -1176,7 +1176,7 @@ static int apds9306_init_iio_gts(struct apds9306_data *data) static void apds9306_powerdown(void *ptr) { - struct apds9306_data *data = (struct apds9306_data *)ptr; + struct apds9306_data *data = ptr; struct apds9306_regfields *rf = &data->rf; int ret; From 6662283ac71552a3a5f66d747110186c9b997cf6 Mon Sep 17 00:00:00 2001 From: Menderes Sabaz Date: Thu, 29 Jan 2026 19:46:10 +0300 Subject: [PATCH 135/405] iio: dac: ad5360: converting to guard(mutex) Converting mutex_lock and mutex_unlock to guard(mutex) Signed-off-by: Menderes Sabaz Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5360.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/drivers/iio/dac/ad5360.c b/drivers/iio/dac/ad5360.c index bd32fa57b1d7..20316fd568e6 100644 --- a/drivers/iio/dac/ad5360.c +++ b/drivers/iio/dac/ad5360.c @@ -206,14 +206,10 @@ static int ad5360_write_unlocked(struct iio_dev *indio_dev, static int ad5360_write(struct iio_dev *indio_dev, unsigned int cmd, unsigned int addr, unsigned int val, unsigned int shift) { - int ret; struct ad5360_state *st = iio_priv(indio_dev); - mutex_lock(&st->lock); - ret = ad5360_write_unlocked(indio_dev, cmd, addr, val, shift); - mutex_unlock(&st->lock); - - return ret; + guard(mutex)(&st->lock); + return ad5360_write_unlocked(indio_dev, cmd, addr, val, shift); } static int ad5360_read(struct iio_dev *indio_dev, unsigned int type, @@ -232,7 +228,7 @@ static int ad5360_read(struct iio_dev *indio_dev, unsigned int type, }, }; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); st->data[0].d32 = cpu_to_be32(AD5360_CMD(AD5360_CMD_SPECIAL_FUNCTION) | AD5360_ADDR(AD5360_REG_SF_READBACK) | @@ -240,12 +236,10 @@ static int ad5360_read(struct iio_dev *indio_dev, unsigned int type, AD5360_READBACK_ADDR(addr)); ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t)); - if (ret >= 0) - ret = be32_to_cpu(st->data[1].d32) & 0xffff; + if (ret < 0) + return ret; - mutex_unlock(&st->lock); - - return ret; + return be32_to_cpu(st->data[1].d32) & 0xffff; } static ssize_t ad5360_read_dac_powerdown(struct device *dev, @@ -262,19 +256,14 @@ static int ad5360_update_ctrl(struct iio_dev *indio_dev, unsigned int set, unsigned int clr) { struct ad5360_state *st = iio_priv(indio_dev); - int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); st->ctrl |= set; st->ctrl &= ~clr; - ret = ad5360_write_unlocked(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, - AD5360_REG_SF_CTRL, st->ctrl, 0); - - mutex_unlock(&st->lock); - - return ret; + return ad5360_write_unlocked(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, + AD5360_REG_SF_CTRL, st->ctrl, 0); } static ssize_t ad5360_write_dac_powerdown(struct device *dev, From 7affc01b317819eb426f6c0aa2fd98be73080b75 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 9 Nov 2025 20:24:36 +0100 Subject: [PATCH 136/405] iio: imu: inv_icm42600: Convert to uXX and sXX integer types The driver has a some use of intXX_t and uintXX_t types which is not the pattern we use in the IIO subsystem. Switch the driver to use kernel internal types for that. No functional changes. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index 54760d8f92a2..9be5ade24501 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -1209,7 +1209,7 @@ int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev) ssize_t i, size; unsigned int no; const void *accel, *gyro, *timestamp; - const int8_t *temp; + const s8 *temp; unsigned int odr; int64_t ts_val; /* buffer is copied to userspace, zeroing it to avoid any data leak */ From 042d1244786ce713da25ea26cce71e7e90be7580 Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Thu, 5 Feb 2026 05:12:07 -0800 Subject: [PATCH 137/405] iio: sca3000: reuse device pointer for devm helpers Cache struct device *dev and feed it to the devm helpers to simplify the probe function. No functional changes. Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Harshit Mogalapalli Signed-off-by: Jonathan Cameron --- drivers/iio/accel/sca3000.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 096f9726ae1f..9b1b4c512199 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -1429,11 +1429,12 @@ static const struct iio_info sca3000_info = { static int sca3000_probe(struct spi_device *spi) { - int ret; + struct device *dev = &spi->dev; struct sca3000_state *st; struct iio_dev *indio_dev; + int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -1455,8 +1456,7 @@ static int sca3000_probe(struct spi_device *spi) } indio_dev->modes = INDIO_DIRECT_MODE; - ret = devm_iio_kfifo_buffer_setup(&spi->dev, indio_dev, - &sca3000_ring_setup_ops); + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, &sca3000_ring_setup_ops); if (ret) return ret; From 4390d4161a286646a0e180e8a6cb9fd58c511f68 Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Thu, 5 Feb 2026 05:12:08 -0800 Subject: [PATCH 138/405] iio: sca3000: switch IRQ handling to devm helpers Convert the threaded IRQ registration to devm_request_threaded_irq() so that the probe and remove paths can drop manual freeing of irqs. No functionality change. Suggested-by: Andy Shevchenko Reviewed-by: David Lechner Reviewed-by: Andy Shevchenko Signed-off-by: Harshit Mogalapalli Signed-off-by: Jonathan Cameron --- drivers/iio/accel/sca3000.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 9b1b4c512199..274e8f3fb763 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -1461,34 +1461,27 @@ static int sca3000_probe(struct spi_device *spi) return ret; if (spi->irq) { - ret = request_threaded_irq(spi->irq, - NULL, - &sca3000_event_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "sca3000", - indio_dev); + ret = devm_request_threaded_irq(dev, spi->irq, NULL, + &sca3000_event_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "sca3000", + indio_dev); if (ret) return ret; } ret = sca3000_clean_setup(st); if (ret) - goto error_free_irq; + return ret; ret = sca3000_print_rev(indio_dev); if (ret) - goto error_free_irq; + return ret; ret = iio_device_register(indio_dev); if (ret) - goto error_free_irq; + return ret; return 0; - -error_free_irq: - if (spi->irq) - free_irq(spi->irq, indio_dev); - - return ret; } static int sca3000_stop_all_interrupts(struct sca3000_state *st) @@ -1518,8 +1511,6 @@ static void sca3000_remove(struct spi_device *spi) /* Must ensure no interrupts can be generated after this! */ sca3000_stop_all_interrupts(st); - if (spi->irq) - free_irq(spi->irq, indio_dev); } static const struct spi_device_id sca3000_id[] = { From aa598c22157a18d19ef6071dd1dd9d2376a9c743 Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Thu, 5 Feb 2026 05:12:09 -0800 Subject: [PATCH 139/405] iio: sca3000: Move sca3000_stop_all_interrupts() above sca3000_probe() Move sca3000_stop_all_interrupts() above sca3000_probe() without altering its logic so the next set of patches are easier to review. No functional change. Suggested-by: Andy Shevchenko Signed-off-by: Harshit Mogalapalli Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/accel/sca3000.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 274e8f3fb763..156a9aa44c68 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -1427,6 +1427,24 @@ static const struct iio_info sca3000_info = { .write_event_config = &sca3000_write_event_config, }; +static int sca3000_stop_all_interrupts(struct sca3000_state *st) +{ + int ret; + + mutex_lock(&st->lock); + ret = sca3000_read_data_short(st, SCA3000_REG_INT_MASK_ADDR, 1); + if (ret) + goto error_ret; + ret = sca3000_write_reg(st, SCA3000_REG_INT_MASK_ADDR, + (st->rx[0] & + ~(SCA3000_REG_INT_MASK_RING_THREE_QUARTER | + SCA3000_REG_INT_MASK_RING_HALF | + SCA3000_REG_INT_MASK_ALL_INTS))); +error_ret: + mutex_unlock(&st->lock); + return ret; +} + static int sca3000_probe(struct spi_device *spi) { struct device *dev = &spi->dev; @@ -1484,24 +1502,6 @@ static int sca3000_probe(struct spi_device *spi) return 0; } -static int sca3000_stop_all_interrupts(struct sca3000_state *st) -{ - int ret; - - mutex_lock(&st->lock); - ret = sca3000_read_data_short(st, SCA3000_REG_INT_MASK_ADDR, 1); - if (ret) - goto error_ret; - ret = sca3000_write_reg(st, SCA3000_REG_INT_MASK_ADDR, - (st->rx[0] & - ~(SCA3000_REG_INT_MASK_RING_THREE_QUARTER | - SCA3000_REG_INT_MASK_RING_HALF | - SCA3000_REG_INT_MASK_ALL_INTS))); -error_ret: - mutex_unlock(&st->lock); - return ret; -} - static void sca3000_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); From 8358169ebb04a4e60c5730682ea5e8706bca485a Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Thu, 5 Feb 2026 05:12:10 -0800 Subject: [PATCH 140/405] iio: sca3000: make stop_all_interrupts() return void sca3000_stop_all_interrupts() is called only from the driver remove path and its return value is discarded, so convert the helper to return void. No functional change. Signed-off-by: Harshit Mogalapalli Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/accel/sca3000.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 156a9aa44c68..b08136ef6dcd 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -1427,7 +1427,7 @@ static const struct iio_info sca3000_info = { .write_event_config = &sca3000_write_event_config, }; -static int sca3000_stop_all_interrupts(struct sca3000_state *st) +static void sca3000_stop_all_interrupts(struct sca3000_state *st) { int ret; @@ -1435,14 +1435,13 @@ static int sca3000_stop_all_interrupts(struct sca3000_state *st) ret = sca3000_read_data_short(st, SCA3000_REG_INT_MASK_ADDR, 1); if (ret) goto error_ret; - ret = sca3000_write_reg(st, SCA3000_REG_INT_MASK_ADDR, - (st->rx[0] & - ~(SCA3000_REG_INT_MASK_RING_THREE_QUARTER | - SCA3000_REG_INT_MASK_RING_HALF | - SCA3000_REG_INT_MASK_ALL_INTS))); + sca3000_write_reg(st, SCA3000_REG_INT_MASK_ADDR, + (st->rx[0] & + ~(SCA3000_REG_INT_MASK_RING_THREE_QUARTER | + SCA3000_REG_INT_MASK_RING_HALF | + SCA3000_REG_INT_MASK_ALL_INTS))); error_ret: mutex_unlock(&st->lock); - return ret; } static int sca3000_probe(struct spi_device *spi) From 31ac64108d0e2484dc7fdeaf990cc88a42894639 Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Thu, 5 Feb 2026 05:12:11 -0800 Subject: [PATCH 141/405] iio: sca3000: use guard(mutex) to simplify return paths Switch sca3000_stop_all_interrupts() to use guard(mutex) to simplify the error paths without needing a goto. Suggested-by: Jonathan Cameron Signed-off-by: Harshit Mogalapalli Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/accel/sca3000.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index b08136ef6dcd..ad8925b227be 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -7,6 +7,7 @@ * See industrialio/accels/sca3000.h for comments. */ +#include #include #include #include @@ -1431,17 +1432,17 @@ static void sca3000_stop_all_interrupts(struct sca3000_state *st) { int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); + ret = sca3000_read_data_short(st, SCA3000_REG_INT_MASK_ADDR, 1); if (ret) - goto error_ret; + return; + sca3000_write_reg(st, SCA3000_REG_INT_MASK_ADDR, (st->rx[0] & ~(SCA3000_REG_INT_MASK_RING_THREE_QUARTER | SCA3000_REG_INT_MASK_RING_HALF | SCA3000_REG_INT_MASK_ALL_INTS))); -error_ret: - mutex_unlock(&st->lock); } static int sca3000_probe(struct spi_device *spi) From 71d0d6a6cae028e0713c1373bf14751764f0f3aa Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Thu, 5 Feb 2026 05:12:12 -0800 Subject: [PATCH 142/405] iio: sca3000: stop interrupts via devm_add_action_or_reset() Used devm_add_action_or_reset() for shutting down the interrupts. Make sca3000_stop_all_interrupts() return void now that it always hooks into devm cleanup. No functional change intended. Suggested-by: David Lechner Suggested-by: Andy Shevchenko Signed-off-by: Harshit Mogalapalli Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/accel/sca3000.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index ad8925b227be..9d6a31013909 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -1428,8 +1428,10 @@ static const struct iio_info sca3000_info = { .write_event_config = &sca3000_write_event_config, }; -static void sca3000_stop_all_interrupts(struct sca3000_state *st) +static void sca3000_stop_all_interrupts(void *data) { + struct iio_dev *indio_dev = data; + struct sca3000_state *st = iio_priv(indio_dev); int ret; guard(mutex)(&st->lock); @@ -1495,6 +1497,10 @@ static int sca3000_probe(struct spi_device *spi) if (ret) return ret; + ret = devm_add_action_or_reset(dev, sca3000_stop_all_interrupts, indio_dev); + if (ret) + return ret; + ret = iio_device_register(indio_dev); if (ret) return ret; @@ -1505,12 +1511,8 @@ static int sca3000_probe(struct spi_device *spi) static void sca3000_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct sca3000_state *st = iio_priv(indio_dev); iio_device_unregister(indio_dev); - - /* Must ensure no interrupts can be generated after this! */ - sca3000_stop_all_interrupts(st); } static const struct spi_device_id sca3000_id[] = { From 8b012728ed9fb0f05d05c464a7faa04800e087bf Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Thu, 5 Feb 2026 05:12:13 -0800 Subject: [PATCH 143/405] iio: sca3000: manage device registration with devm helper Convert the iio registration to use devm_* helpers so the probe no longer needs a separate cleanup path and remove callback can also drop the unregister. After this there is no need for having a remove callback, so remote it. No functional change intended. Suggested-by: Andy Shevchenko Reviewed-by: David Lechner Reviewed-by: Andy Shevchenko Signed-off-by: Harshit Mogalapalli Signed-off-by: Jonathan Cameron --- drivers/iio/accel/sca3000.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 9d6a31013909..573831199bba 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -1459,7 +1459,6 @@ static int sca3000_probe(struct spi_device *spi) return -ENOMEM; st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); st->us = spi; mutex_init(&st->lock); st->info = spi_get_device_match_data(spi); @@ -1501,18 +1500,7 @@ static int sca3000_probe(struct spi_device *spi) if (ret) return ret; - ret = iio_device_register(indio_dev); - if (ret) - return ret; - - return 0; -} - -static void sca3000_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - - iio_device_unregister(indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id sca3000_id[] = { @@ -1529,7 +1517,6 @@ static struct spi_driver sca3000_driver = { .name = "sca3000", }, .probe = sca3000_probe, - .remove = sca3000_remove, .id_table = sca3000_id, }; module_spi_driver(sca3000_driver); From 04bb8d0e5d1c8d5a9079b35b4e6f0868f734698b Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Wed, 4 Feb 2026 20:02:02 +0200 Subject: [PATCH 144/405] iio: ABI: fix current_trigger description Triggers exist under /sys/bus/iio/devices/, not under /sys/class/iio. /sys/class/iio does not even exist. Use the current path. Signed-off-by: Cosmin Tanislav Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 5f87dcee78f7..4fc9f6bd4281 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1428,7 +1428,7 @@ KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: The name of the trigger source being used, as per string given - in /sys/class/iio/triggerY/name. + in /sys/bus/iio/devices/triggerY/name. What: /sys/bus/iio/devices/iio:deviceX/bufferY/length KernelVersion: 5.11 From c1de86dab615b1b379ed856434c4fe9e71d32318 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 2 Feb 2026 13:25:51 +0200 Subject: [PATCH 145/405] iio: adc: ad4080: remove unused dec_rate field Remove unused dec_rate field from ad4080_state struct. The driver reads/writes decimation rate directly from hardware registers via ad4080_get_dec_rate() and ad4080_set_dec_rate() functions. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4080.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c index fc261d3d7687..204ad198342b 100644 --- a/drivers/iio/adc/ad4080.c +++ b/drivers/iio/adc/ad4080.c @@ -188,7 +188,6 @@ struct ad4080_state { */ struct mutex lock; unsigned int num_lanes; - unsigned int dec_rate; unsigned long clk_rate; enum ad4080_filter_type filter_type; bool lvds_cnv_en; From 1a993d5686ffe6f9b6addea22301ece733897765 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 2 Feb 2026 13:25:52 +0200 Subject: [PATCH 146/405] iio: adc: ad7768-1: remove unused mclk_div field Remove unused mclk_div field from ad7768_state struct. The field is declared but never accessed in the driver. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7768-1.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 917a3a0380a1..8131f1d90072 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -309,7 +309,6 @@ struct ad7768_state { unsigned int vcm_output_sel; struct clk *mclk; unsigned int mclk_freq; - unsigned int mclk_div; unsigned int oversampling_ratio; enum ad7768_filter_type filter_type; unsigned int samp_freq; From 1062f21ce1052eacee2714b9078e237470bcb973 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 2 Feb 2026 13:25:53 +0200 Subject: [PATCH 147/405] iio: adc: ad7793: remove unused int_vref_mv field Remove unused int_vref_mv field from ad7793_state struct. The field is declared but never accessed in the driver. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7793.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index b6d86c62f24a..8ff7b70d6632 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -152,7 +152,6 @@ struct ad7793_chip_info { struct ad7793_state { const struct ad7793_chip_info *chip_info; - u16 int_vref_mv; u16 mode; u16 conf; u32 scale_avail[8][2]; From 8c0af74e250734ab91276f2cc53058a0b394758a Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 2 Feb 2026 13:25:54 +0200 Subject: [PATCH 148/405] iio: adc: ad9467: remove unused output_mode field Remove unused output_mode field from ad9467_state struct. The field is declared but never accessed in the driver. Signed-off-by: Antoniu Miclaus Reviewed-by: Tomas Melin Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad9467.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c index d611ca813c0c..0bf67437508f 100644 --- a/drivers/iio/adc/ad9467.c +++ b/drivers/iio/adc/ad9467.c @@ -176,7 +176,6 @@ struct ad9467_state { struct clk *clk; /* used for debugfs */ struct ad9467_chan_test_mode *chan_test; - unsigned int output_mode; unsigned int (*scales)[2]; /* * Times 2 because we may also invert the signal polarity and run the From d41114a74e7312991ed05fe051d2c6fd04685b96 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 2 Feb 2026 13:25:55 +0200 Subject: [PATCH 149/405] iio: adc: max1363: remove unused requestedmask field Remove unused requestedmask field from max1363_state struct. The field is declared but never accessed in the driver. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1363.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index d35f4487b2f9..3ccc7b2d4f67 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -149,7 +149,6 @@ struct max1363_chip_info { * @configbyte: cache of current device config byte * @chip_info: chip model specific constants, available modes, etc. * @current_mode: the scan mode of this chip - * @requestedmask: a valid requested set of channels * @lock: lock to ensure state is consistent * @monitor_on: whether monitor mode is enabled * @monitor_speed: parameter corresponding to device monitor speed setting @@ -169,7 +168,6 @@ struct max1363_state { u8 configbyte; const struct max1363_chip_info *chip_info; const struct max1363_mode *current_mode; - u32 requestedmask; struct mutex lock; /* Using monitor modes and buffer at the same time is From 726c1035ba1e6a09d75367d900525c77d654db27 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 2 Feb 2026 13:25:56 +0200 Subject: [PATCH 150/405] iio: adc: nau7802: remove unused min_conversions field Remove unused min_conversions field from nau7802_state struct. The field is declared but never accessed in the driver. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/nau7802.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c index 1a42c7962ec9..970afb27b839 100644 --- a/drivers/iio/adc/nau7802.c +++ b/drivers/iio/adc/nau7802.c @@ -55,7 +55,6 @@ struct nau7802_state { struct mutex data_lock; u32 vref_mv; u32 conversion_count; - u32 min_conversions; u8 sample_rate; u32 scale_avail[8]; struct completion value_ok; From 0555e56f4c4b4b78191de7abf0ff6f8ab133a7c1 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 2 Feb 2026 13:25:57 +0200 Subject: [PATCH 151/405] iio: adc: ti-ads1015: remove unused enabled field Remove unused enabled field from ads1015_channel_data struct. The field is declared but never accessed in the driver. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1015.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index f2a93c63ca14..c7ffe47449e2 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -231,7 +231,6 @@ static const struct iio_event_spec ads1015_events[] = { } struct ads1015_channel_data { - bool enabled; unsigned int pga; unsigned int data_rate; }; From 3890d6a324960816df9e8cebd742b11ee7591186 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 2 Feb 2026 13:25:58 +0200 Subject: [PATCH 152/405] iio: dac: adi-axi-dac: remove unused int_tone field Remove unused int_tone field from axi_dac_state struct. The field is declared but never accessed in the driver. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/adi-axi-dac.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c index 33b07de1bfcb..451fad34e7ee 100644 --- a/drivers/iio/dac/adi-axi-dac.c +++ b/drivers/iio/dac/adi-axi-dac.c @@ -114,7 +114,6 @@ struct axi_dac_state { const struct axi_dac_info *info; u64 dac_clk; u32 reg_config; - bool int_tone; int dac_clk_rate; }; From 18c1d078efee67c0d65f1725bb07f5d3be7c8025 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 2 Feb 2026 13:25:59 +0200 Subject: [PATCH 153/405] iio: dac: ti-dac5571: remove unused id field Remove unused id field from dac5571_data struct. The field is declared but never accessed in the driver. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ti-dac5571.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c index bdc3f94aef98..455d61fc3f13 100644 --- a/drivers/iio/dac/ti-dac5571.c +++ b/drivers/iio/dac/ti-dac5571.c @@ -45,7 +45,6 @@ static const struct dac5571_spec dac5571_spec[] = { struct dac5571_data { struct i2c_client *client; - int id; struct mutex lock; struct regulator *vref; u16 val[4]; From 5c9ba5d863add3423e7b7ccdf44c7fd646171dd1 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 2 Feb 2026 13:26:00 +0200 Subject: [PATCH 154/405] iio: humidity: hdc2010: remove unused interrupt_config Remove unused interrupt_config field from hdc2010_data struct. The field is declared but never accessed in the driver. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hdc2010.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/humidity/hdc2010.c b/drivers/iio/humidity/hdc2010.c index 894a8b4ab193..1a0f18251381 100644 --- a/drivers/iio/humidity/hdc2010.c +++ b/drivers/iio/humidity/hdc2010.c @@ -44,7 +44,6 @@ struct hdc2010_data { struct i2c_client *client; struct mutex lock; u8 measurement_config; - u8 interrupt_config; u8 drdy_config; }; From c1f9dea72c9e0ee764a8d823696da32abcb00900 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 2 Feb 2026 13:26:01 +0200 Subject: [PATCH 155/405] iio: imu: bmi323: remove unused drdy_trigger_enabled Remove unused drdy_trigger_enabled field from bmi323_data struct. The field is declared but never accessed in the driver. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/imu/bmi323/bmi323_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/imu/bmi323/bmi323_core.c b/drivers/iio/imu/bmi323/bmi323_core.c index 6bcb9a436581..f3d499423399 100644 --- a/drivers/iio/imu/bmi323/bmi323_core.c +++ b/drivers/iio/imu/bmi323/bmi323_core.c @@ -156,7 +156,6 @@ struct bmi323_data { struct iio_mount_matrix orientation; enum bmi323_irq_pin irq_pin; struct iio_trigger *trig; - bool drdy_trigger_enabled; enum bmi323_state state; s64 fifo_tstamp, old_fifo_tstamp; u32 odrns[BMI323_SENSORS_CNT]; From 9c21a850f0c7a4bb9a50487b918a858014835b65 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 2 Feb 2026 13:26:02 +0200 Subject: [PATCH 156/405] iio: light: apds9306: remove unused nlux_per_count Remove unused nlux_per_count field from apds9306_data struct. The field is declared but never accessed in the driver. Acked-by: Subhajit Ghosh Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/apds9306.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c index 5d18eabd961e..5ca4c87524fe 100644 --- a/drivers/iio/light/apds9306.c +++ b/drivers/iio/light/apds9306.c @@ -168,7 +168,6 @@ struct apds9306_regfields { * respectively. * @regmap: Regmap structure pointer * @rf: Regmap register fields structure - * @nlux_per_count: Nano lux per ADC count for a particular model * @read_data_available: Flag set by IRQ handler for ADC data available */ struct apds9306_data { @@ -180,7 +179,6 @@ struct apds9306_data { struct regmap *regmap; struct apds9306_regfields rf; - int nlux_per_count; int read_data_available; }; From 2ac8cd2bab30509cfada82546eee96f0aca38c20 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 2 Feb 2026 13:26:03 +0200 Subject: [PATCH 157/405] iio: light: gp2ap020a00f: remove unused debug_reg_addr Remove unused debug_reg_addr field from gp2ap020a00f_data struct. The field is declared but never accessed in the driver. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap020a00f.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 76147b6d14e8..7e388319ee2e 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -245,7 +245,6 @@ struct gp2ap020a00f_data { struct iio_trigger *trig; struct regmap *regmap; unsigned int thresh_val[4]; - u8 debug_reg_addr; struct irq_work work; wait_queue_head_t data_ready_queue; }; From dd31b649ef002302331cbff137d8045885e49a11 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 6 Feb 2026 19:28:38 +0200 Subject: [PATCH 158/405] dt-bindings: iio: adc: cpcap-adc: document Mot ADC Add compatible for ADC used in Mot board. Separate compatible is required since ADC in the Mot board uses a unique set of configurations. Signed-off-by: Svyatoslav Ryhel Acked-by: Rob Herring (Arm) Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/motorola,cpcap-adc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/iio/adc/motorola,cpcap-adc.yaml b/Documentation/devicetree/bindings/iio/adc/motorola,cpcap-adc.yaml index 9ceb6f18c854..1f77da7f8e06 100644 --- a/Documentation/devicetree/bindings/iio/adc/motorola,cpcap-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/motorola,cpcap-adc.yaml @@ -19,6 +19,7 @@ properties: enum: - motorola,cpcap-adc - motorola,mapphone-cpcap-adc + - motorola,mot-cpcap-adc interrupts: maxItems: 1 From 18a1ae3e7350e1798ea9f492959d5000ae5d9bc4 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 6 Feb 2026 19:28:39 +0200 Subject: [PATCH 159/405] iio: adc: cpcap-adc: add support for Mot ADC Add support for ADC found in Motorola Mot board, used as a base for Atrix 4G and Droid X2 smartphones. Signed-off-by: Svyatoslav Ryhel Signed-off-by: Jonathan Cameron --- drivers/iio/adc/cpcap-adc.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/iio/adc/cpcap-adc.c b/drivers/iio/adc/cpcap-adc.c index d9ee2ea116a7..f6f72efcc6ed 100644 --- a/drivers/iio/adc/cpcap-adc.c +++ b/drivers/iio/adc/cpcap-adc.c @@ -934,6 +934,17 @@ static const struct cpcap_adc_ato mapphone_adc = { .atox_ps_factor_out = 0, }; +static const struct cpcap_adc_ato mot_adc = { + .ato_in = 0x0300, + .atox_in = 0, + .adc_ps_factor_in = 0x0200, + .atox_ps_factor_in = 0, + .ato_out = 0x0780, + .atox_out = 0, + .adc_ps_factor_out = 0x0600, + .atox_ps_factor_out = 0, +}; + static const struct of_device_id cpcap_adc_id_table[] = { { .compatible = "motorola,cpcap-adc", @@ -942,6 +953,10 @@ static const struct of_device_id cpcap_adc_id_table[] = { .compatible = "motorola,mapphone-cpcap-adc", .data = &mapphone_adc, }, + { + .compatible = "motorola,mot-cpcap-adc", + .data = &mot_adc, + }, { } }; MODULE_DEVICE_TABLE(of, cpcap_adc_id_table); From 1ff6d25d691d1b10c977b61219206c3400a81606 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 2 Feb 2026 14:07:12 +0200 Subject: [PATCH 160/405] iio: light: ltr501: return proper error code from ltr501_get_gain_index() Return -EINVAL instead of -1 when no matching gain value is found in the gain table. Update the callers to propagate this error directly rather than overwriting it with -EINVAL. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Reviewed-by: Waqar Hameed Signed-off-by: Jonathan Cameron --- drivers/iio/light/ltr501.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 022e0693983b..4d99ae336f61 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -754,7 +754,7 @@ static int ltr501_get_gain_index(const struct ltr501_gain *gain, int size, if (val == gain[i].scale && val2 == gain[i].uscale) return i; - return -1; + return -EINVAL; } static int __ltr501_write_raw(struct iio_dev *indio_dev, @@ -773,7 +773,7 @@ static int __ltr501_write_raw(struct iio_dev *indio_dev, info->als_gain_tbl_size, val, val2); if (i < 0) - return -EINVAL; + return i; data->als_contr &= ~info->als_gain_mask; data->als_contr |= i << info->als_gain_shift; @@ -785,7 +785,7 @@ static int __ltr501_write_raw(struct iio_dev *indio_dev, info->ps_gain_tbl_size, val, val2); if (i < 0) - return -EINVAL; + return i; data->ps_contr &= ~LTR501_CONTR_PS_GAIN_MASK; data->ps_contr |= i << LTR501_CONTR_PS_GAIN_SHIFT; From 8bd1254c92c92382114ff9b3b727d5cb81167df7 Mon Sep 17 00:00:00 2001 From: Vivek Pernamitta Date: Thu, 12 Feb 2026 16:30:23 +0530 Subject: [PATCH 161/405] bus: mhi: host: pci_generic: Enable IP_SW and IP_ETH channels for Qcom QDU100 device Enable IP_SW1 (ch:48/49), IP_ETH0 (ch:50,51) and IP_ETH1 (ch:52, 53) channels over MHI for M-plane, NETCONF and S-plane interface for Qualcomm 5G DU X100 Accelerator Card (QDU100). M-plane: Used to implement DU M-Plane software for non-real-time O-RAN management between O-DU and O-RU using NETCONF/YANG and O-RAN WG4 M-Plane YANG models. It provides capability exchange, configuration management, performance monitoring, and fault management per O-RAN.WG4.TS.MP.0-R004-v18.00 spec. Netconf: Used for configuration operations such as fetching, modifying, and deleting network device configurations. This interface is also used for IETF Netconf communication, with a Netconf server on the ORU to interact with a Netconf client running on the host. S-plane: To support frequency and time synchronization between O-DUs and O-RUs using Synchronous Ethernet and IEEE 1588. Assume PTP transport over L2 Ethernet (ITU-T G.8275.1) for full timing support and to allow PTP over UDP/IP (ITU-T G.8275.2) with reduced reliability, as per ORAN spec O-RAN.WG4.CUS.0-R003-v12.00. Signed-off-by: Vivek Pernamitta [mani: commit log] Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260212-eth_vdev_next-20260211-v8-2-0974b3a8d61b@qti.qualcomm.com --- drivers/bus/mhi/host/pci_generic.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 425362037830..314ded3da308 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -253,6 +253,13 @@ static const struct mhi_channel_config mhi_qcom_qdu100_channels[] = { MHI_CHANNEL_CONFIG_DL(41, "MHI_PHC", 32, 4), MHI_CHANNEL_CONFIG_UL(46, "IP_SW0", 256, 5), MHI_CHANNEL_CONFIG_DL(47, "IP_SW0", 256, 5), + MHI_CHANNEL_CONFIG_UL(48, "IP_SW1", 256, 6), + MHI_CHANNEL_CONFIG_DL(49, "IP_SW1", 256, 6), + MHI_CHANNEL_CONFIG_UL(50, "IP_ETH0", 256, 7), + MHI_CHANNEL_CONFIG_DL(51, "IP_ETH0", 256, 7), + MHI_CHANNEL_CONFIG_UL(52, "IP_ETH1", 256, 8), + MHI_CHANNEL_CONFIG_DL(53, "IP_ETH1", 256, 8), + }; static struct mhi_event_config mhi_qcom_qdu100_events[] = { @@ -268,6 +275,7 @@ static struct mhi_event_config mhi_qcom_qdu100_events[] = { MHI_EVENT_CONFIG_SW_DATA(5, 512), MHI_EVENT_CONFIG_SW_DATA(6, 512), MHI_EVENT_CONFIG_SW_DATA(7, 512), + MHI_EVENT_CONFIG_SW_DATA(8, 512), }; static const struct mhi_controller_config mhi_qcom_qdu100_config = { From 54b022f162a7f9b7c4f2b3902e4873d74f8d0875 Mon Sep 17 00:00:00 2001 From: Daniele Palmas Date: Thu, 5 Mar 2026 10:44:04 +0100 Subject: [PATCH 162/405] bus: mhi: host: pci_generic: Add NMEA channels to FN920C04 and FN990A Add NMEA channels to Telit FN920C04 and FN990A configuration. Signed-off-by: Daniele Palmas Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260305094404.1956028-1-dnlplm@gmail.com --- drivers/bus/mhi/host/pci_generic.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 314ded3da308..6609715f3431 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -806,6 +806,8 @@ static const struct mhi_channel_config mhi_telit_fn990_channels[] = { MHI_CHANNEL_CONFIG_DL(33, "DUN", 32, 0), MHI_CHANNEL_CONFIG_UL(92, "DUN2", 32, 1), MHI_CHANNEL_CONFIG_DL(93, "DUN2", 32, 1), + MHI_CHANNEL_CONFIG_UL(94, "NMEA", 32, 1), + MHI_CHANNEL_CONFIG_DL(95, "NMEA", 32, 1), MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0_MBIM", 128, 2), MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0_MBIM", 128, 3), }; @@ -857,6 +859,8 @@ static const struct mhi_channel_config mhi_telit_fn920c04_channels[] = { MHI_CHANNEL_CONFIG_DL_FP(35, "FIREHOSE", 32, 0), MHI_CHANNEL_CONFIG_UL(92, "DUN2", 32, 1), MHI_CHANNEL_CONFIG_DL(93, "DUN2", 32, 1), + MHI_CHANNEL_CONFIG_UL(94, "NMEA", 32, 1), + MHI_CHANNEL_CONFIG_DL(95, "NMEA", 32, 1), MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0", 128, 2), MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0", 128, 3), }; From cfdb41adf1c2822ad1b1791d4d11093edb5582b6 Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Tue, 3 Mar 2026 01:02:13 -0800 Subject: [PATCH 163/405] bus: mhi: host: pci_generic: Switch to async power up to avoid boot delays Some modem devices can take significant time (up to 20 secs for sdx75) to enter mission mode during initialization. Currently, mhi_sync_power_up() waits for this entire process to complete, blocking other driver probes and delaying system boot. Switch to mhi_async_power_up() so probe can return immediately while MHI initialization continues in the background. This eliminates lengthy boot delays and allows other drivers to probe in parallel, improving overall system boot performance. Fixes: 5571519009d0 ("bus: mhi: host: pci_generic: Add SDX75 based modem support") Signed-off-by: Qiang Yu Signed-off-by: Manivannan Sadhasivam Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260303-b4-async_power_on-v2-1-d3db81eb457d@oss.qualcomm.com --- drivers/bus/mhi/host/pci_generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 6609715f3431..b6b8ea3a11f3 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -1417,7 +1417,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_unregister; } - err = mhi_sync_power_up(mhi_cntrl); + err = mhi_async_power_up(mhi_cntrl); if (err) { dev_err(&pdev->dev, "failed to power up MHI controller\n"); goto err_unprepare; From f227b246307e0cf3091e13e7fbae3974aaf38eb9 Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Tue, 3 Mar 2026 01:02:14 -0800 Subject: [PATCH 164/405] bus: mhi: host: pci_generic: Add pm_runtime_forbid() in remove callback Add pm_runtime_forbid() to balance the pm_runtime_allow() call made during Mission Mode transition. Without this, the device remains in runtime PM allowed state even after driver removal. Fixes: 855a70c12021 ("bus: mhi: Add MHI PCI support for WWAN modems") Signed-off-by: Qiang Yu [mani: moved pm_runtime_forbid() to the start of remove()] Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260303-b4-async_power_on-v2-2-d3db81eb457d@oss.qualcomm.com --- drivers/bus/mhi/host/pci_generic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index b6b8ea3a11f3..391ab146f501 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -1452,6 +1452,7 @@ static void mhi_pci_remove(struct pci_dev *pdev) struct mhi_pci_device *mhi_pdev = pci_get_drvdata(pdev); struct mhi_controller *mhi_cntrl = &mhi_pdev->mhi_cntrl; + pm_runtime_forbid(&pdev->dev); pci_disable_sriov(pdev); if (pdev->is_physfn) From 43cb0a21a47577938735919a6effe9805414d40a Mon Sep 17 00:00:00 2001 From: Raviteja Laggyshetty Date: Mon, 9 Feb 2026 09:44:28 +0000 Subject: [PATCH 165/405] dt-bindings: interconnect: document the RPMh Network-On-Chip interconnect in Mahua SoC Document the RPMh Network-on-Chip (NoC) interconnect for the Qualcomm Mahua platform. Mahua is a derivative of the Glymur SoC. Many interconnect nodes are identical and continue to use Glymur fallback compatibles. Mahua introduces SoC-specific configurations and topologies for several NoC blocks, including CNOC, HSCNOC, PCIe West ANoC/Slave NoCs. This updates the existing Glymur yaml schema to include Mahua-specific compatible strings, using two-cell "fallback" compatibles wherever the hardware is identical with Glymur. Co-developed-by: Odelu Kukatla Signed-off-by: Odelu Kukatla Acked-by: Konrad Dybcio Signed-off-by: Raviteja Laggyshetty Reviewed-by: Krzysztof Kozlowski Link: https://msgid.link/20260209-mahua_icc-v3-1-c65f3dfd72c8@oss.qualcomm.com Signed-off-by: Georgi Djakov --- .../interconnect/qcom,glymur-rpmh.yaml | 136 ++++++++++++++---- 1 file changed, 111 insertions(+), 25 deletions(-) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,glymur-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,glymur-rpmh.yaml index d55a7bcf5591..f69b2facb658 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,glymur-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,glymur-rpmh.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/interconnect/qcom,glymur-rpmh.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Qualcomm RPMh Network-On-Chip Interconnect on GLYMUR +title: Qualcomm RPMh Network-On-Chip Interconnect on Glymur and Mahua SoCs maintainers: - Raviteja Laggyshetty @@ -21,28 +21,98 @@ description: | properties: compatible: - enum: - - qcom,glymur-aggre1-noc - - qcom,glymur-aggre2-noc - - qcom,glymur-aggre3-noc - - qcom,glymur-aggre4-noc - - qcom,glymur-clk-virt - - qcom,glymur-cnoc-cfg - - qcom,glymur-cnoc-main - - qcom,glymur-hscnoc - - qcom,glymur-lpass-ag-noc - - qcom,glymur-lpass-lpiaon-noc - - qcom,glymur-lpass-lpicx-noc - - qcom,glymur-mc-virt - - qcom,glymur-mmss-noc - - qcom,glymur-nsinoc - - qcom,glymur-nsp-noc - - qcom,glymur-oobm-ss-noc - - qcom,glymur-pcie-east-anoc - - qcom,glymur-pcie-east-slv-noc - - qcom,glymur-pcie-west-anoc - - qcom,glymur-pcie-west-slv-noc - - qcom,glymur-system-noc + oneOf: + - items: + - enum: + - qcom,mahua-aggre1-noc + - const: qcom,glymur-aggre1-noc + - items: + - enum: + - qcom,mahua-aggre2-noc + - const: qcom,glymur-aggre2-noc + - items: + - enum: + - qcom,mahua-aggre3-noc + - const: qcom,glymur-aggre3-noc + - items: + - enum: + - qcom,mahua-aggre4-noc + - const: qcom,glymur-aggre4-noc + - items: + - enum: + - qcom,mahua-clk-virt + - const: qcom,glymur-clk-virt + - items: + - enum: + - qcom,mahua-cnoc-main + - const: qcom,glymur-cnoc-main + - items: + - enum: + - qcom,mahua-lpass-ag-noc + - const: qcom,glymur-lpass-ag-noc + - items: + - enum: + - qcom,mahua-lpass-lpiaon-noc + - const: qcom,glymur-lpass-lpiaon-noc + - items: + - enum: + - qcom,mahua-lpass-lpicx-noc + - const: qcom,glymur-lpass-lpicx-noc + - items: + - enum: + - qcom,mahua-mmss-noc + - const: qcom,glymur-mmss-noc + - items: + - enum: + - qcom,mahua-nsinoc + - const: qcom,glymur-nsinoc + - items: + - enum: + - qcom,mahua-nsp-noc + - const: qcom,glymur-nsp-noc + - items: + - enum: + - qcom,mahua-oobm-ss-noc + - const: qcom,glymur-oobm-ss-noc + - items: + - enum: + - qcom,mahua-pcie-east-anoc + - const: qcom,glymur-pcie-east-anoc + - items: + - enum: + - qcom,mahua-pcie-east-slv-noc + - const: qcom,glymur-pcie-east-slv-noc + - items: + - enum: + - qcom,mahua-system-noc + - const: qcom,glymur-system-noc + - enum: + - qcom,glymur-aggre1-noc + - qcom,glymur-aggre2-noc + - qcom,glymur-aggre3-noc + - qcom,glymur-aggre4-noc + - qcom,glymur-clk-virt + - qcom,glymur-cnoc-cfg + - qcom,glymur-cnoc-main + - qcom,glymur-hscnoc + - qcom,glymur-lpass-ag-noc + - qcom,glymur-lpass-lpiaon-noc + - qcom,glymur-lpass-lpicx-noc + - qcom,glymur-mc-virt + - qcom,glymur-mmss-noc + - qcom,glymur-nsinoc + - qcom,glymur-nsp-noc + - qcom,glymur-oobm-ss-noc + - qcom,glymur-pcie-east-anoc + - qcom,glymur-pcie-east-slv-noc + - qcom,glymur-pcie-west-anoc + - qcom,glymur-pcie-west-slv-noc + - qcom,glymur-system-noc + - qcom,mahua-cnoc-cfg + - qcom,mahua-hscnoc + - qcom,mahua-mc-virt + - qcom,mahua-pcie-west-anoc + - qcom,mahua-pcie-west-slv-noc reg: maxItems: 1 @@ -63,6 +133,7 @@ allOf: enum: - qcom,glymur-clk-virt - qcom,glymur-mc-virt + - qcom,mahua-mc-virt then: properties: reg: false @@ -85,6 +156,20 @@ allOf: - description: aggre PCIE_4 WEST AXI clock - description: aggre PCIE_6 WEST AXI clock + - if: + properties: + compatible: + contains: + enum: + - qcom,mahua-pcie-west-anoc + then: + properties: + clocks: + items: + - description: aggre PCIE_3B WEST AXI clock + - description: aggre PCIE_4 WEST AXI clock + - description: aggre PCIE_6 WEST AXI clock + - if: properties: compatible: @@ -131,10 +216,11 @@ allOf: compatible: contains: enum: - - qcom,glymur-pcie-west-anoc - - qcom,glymur-pcie-east-anoc - qcom,glymur-aggre2-noc - qcom,glymur-aggre4-noc + - qcom,glymur-pcie-east-anoc + - qcom,glymur-pcie-west-anoc + - qcom,mahua-pcie-west-anoc then: required: - clocks From dfff14a4a44d8bbf33bdd08535b30981e76230f1 Mon Sep 17 00:00:00 2001 From: Raviteja Laggyshetty Date: Mon, 9 Feb 2026 09:44:29 +0000 Subject: [PATCH 166/405] interconnect: qcom: glymur: Add Mahua SoC support Mahua is a derivative of the Glymur SoC. Extend the Glymur driver to support Mahua by: 1. Adding new node definitions for interconnects that differ from Glymur (Config NoC, High-Speed Coherent NoC, PCIe West ANOC/Slave NoC). 2. Reusing existing Glymur definitions for identical NoCs. 3. Overriding the channel and buswidth, with Mahua specific values for the differing NoCs Co-developed-by: Odelu Kukatla Signed-off-by: Odelu Kukatla Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Signed-off-by: Raviteja Laggyshetty Link: https://msgid.link/20260209-mahua_icc-v3-2-c65f3dfd72c8@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/glymur.c | 38 ++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/drivers/interconnect/qcom/glymur.c b/drivers/interconnect/qcom/glymur.c index e5c07795a6c6..cfe061c1a75a 100644 --- a/drivers/interconnect/qcom/glymur.c +++ b/drivers/interconnect/qcom/glymur.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "bcm-voter.h" @@ -1985,7 +1986,7 @@ static struct qcom_icc_bcm * const cnoc_cfg_bcms[] = { &bcm_cn1, }; -static struct qcom_icc_node * const cnoc_cfg_nodes[] = { +static struct qcom_icc_node *cnoc_cfg_nodes[] = { [MASTER_CNOC_CFG] = &qsm_cfg, [SLAVE_AHB2PHY_SOUTH] = &qhs_ahb2phy0, [SLAVE_AHB2PHY_NORTH] = &qhs_ahb2phy1, @@ -2093,7 +2094,7 @@ static struct qcom_icc_bcm * const hscnoc_bcms[] = { &bcm_sh1, }; -static struct qcom_icc_node * const hscnoc_nodes[] = { +static struct qcom_icc_node *hscnoc_nodes[] = { [MASTER_GPU_TCU] = &alm_gpu_tcu, [MASTER_PCIE_TCU] = &alm_pcie_qtc, [MASTER_SYS_TCU] = &alm_sys_tcu, @@ -2377,7 +2378,7 @@ static struct qcom_icc_bcm * const pcie_west_anoc_bcms[] = { &bcm_sn6, }; -static struct qcom_icc_node * const pcie_west_anoc_nodes[] = { +static struct qcom_icc_node *pcie_west_anoc_nodes[] = { [MASTER_PCIE_WEST_ANOC_CFG] = &qsm_pcie_west_anoc_cfg, [MASTER_PCIE_2] = &xm_pcie_2, [MASTER_PCIE_3A] = &xm_pcie_3a, @@ -2409,7 +2410,7 @@ static struct qcom_icc_bcm * const pcie_west_slv_noc_bcms[] = { &bcm_sn6, }; -static struct qcom_icc_node * const pcie_west_slv_noc_nodes[] = { +static struct qcom_icc_node *pcie_west_slv_noc_nodes[] = { [MASTER_HSCNOC_PCIE_WEST] = &qnm_hscnoc_pcie_west, [MASTER_CNOC_PCIE_WEST_SLAVE_CFG] = &qsm_cnoc_pcie_west_slave_cfg, [SLAVE_HSCNOC_PCIE_WEST_MS_MPU_CFG] = &qhs_hscnoc_pcie_west_ms_mpu_cfg, @@ -2470,6 +2471,28 @@ static const struct qcom_icc_desc glymur_system_noc = { .num_bcms = ARRAY_SIZE(system_noc_bcms), }; +static int glymur_qnoc_probe(struct platform_device *pdev) +{ + if (device_is_compatible(&pdev->dev, "qcom,mahua-mc-virt")) { + llcc_mc.channels = 8; + ebi.channels = 8; + } else if (device_is_compatible(&pdev->dev, "qcom,mahua-hscnoc")) { + qns_llcc.channels = 8; + chm_apps.channels = 4; + qnm_pcie_west.buswidth = 32; + hscnoc_nodes[MASTER_WLAN_Q6] = NULL; + } else if (device_is_compatible(&pdev->dev, "qcom,mahua-pcie-west-anoc")) { + qns_pcie_west_mem_noc.buswidth = 32; + pcie_west_anoc_nodes[MASTER_PCIE_3A] = NULL; + } else if (device_is_compatible(&pdev->dev, "qcom,mahua-cnoc-cfg")) { + cnoc_cfg_nodes[SLAVE_PCIE_3A_CFG] = NULL; + } else if (device_is_compatible(&pdev->dev, "qcom,mahua-pcie-west-slv-noc")) { + pcie_west_slv_noc_nodes[SLAVE_PCIE_3A] = NULL; + } + + return qcom_icc_rpmh_probe(pdev); +} + static const struct of_device_id qnoc_of_match[] = { { .compatible = "qcom,glymur-aggre1-noc", .data = &glymur_aggre1_noc}, { .compatible = "qcom,glymur-aggre2-noc", .data = &glymur_aggre2_noc}, @@ -2477,12 +2500,15 @@ static const struct of_device_id qnoc_of_match[] = { { .compatible = "qcom,glymur-aggre4-noc", .data = &glymur_aggre4_noc}, { .compatible = "qcom,glymur-clk-virt", .data = &glymur_clk_virt}, { .compatible = "qcom,glymur-cnoc-cfg", .data = &glymur_cnoc_cfg}, + { .compatible = "qcom,mahua-cnoc-cfg", .data = &glymur_cnoc_cfg}, { .compatible = "qcom,glymur-cnoc-main", .data = &glymur_cnoc_main}, { .compatible = "qcom,glymur-hscnoc", .data = &glymur_hscnoc}, + { .compatible = "qcom,mahua-hscnoc", .data = &glymur_hscnoc}, { .compatible = "qcom,glymur-lpass-ag-noc", .data = &glymur_lpass_ag_noc}, { .compatible = "qcom,glymur-lpass-lpiaon-noc", .data = &glymur_lpass_lpiaon_noc}, { .compatible = "qcom,glymur-lpass-lpicx-noc", .data = &glymur_lpass_lpicx_noc}, { .compatible = "qcom,glymur-mc-virt", .data = &glymur_mc_virt}, + { .compatible = "qcom,mahua-mc-virt", .data = &glymur_mc_virt}, { .compatible = "qcom,glymur-mmss-noc", .data = &glymur_mmss_noc}, { .compatible = "qcom,glymur-nsinoc", .data = &glymur_nsinoc}, { .compatible = "qcom,glymur-nsp-noc", .data = &glymur_nsp_noc}, @@ -2490,14 +2516,16 @@ static const struct of_device_id qnoc_of_match[] = { { .compatible = "qcom,glymur-pcie-east-anoc", .data = &glymur_pcie_east_anoc}, { .compatible = "qcom,glymur-pcie-east-slv-noc", .data = &glymur_pcie_east_slv_noc}, { .compatible = "qcom,glymur-pcie-west-anoc", .data = &glymur_pcie_west_anoc}, + { .compatible = "qcom,mahua-pcie-west-anoc", .data = &glymur_pcie_west_anoc}, { .compatible = "qcom,glymur-pcie-west-slv-noc", .data = &glymur_pcie_west_slv_noc}, + { .compatible = "qcom,mahua-pcie-west-slv-noc", .data = &glymur_pcie_west_slv_noc}, { .compatible = "qcom,glymur-system-noc", .data = &glymur_system_noc}, { } }; MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { - .probe = qcom_icc_rpmh_probe, + .probe = glymur_qnoc_probe, .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-glymur", From 28a70e793977a606395550b3c0547c14b6441e98 Mon Sep 17 00:00:00 2001 From: Odelu Kukatla Date: Tue, 27 Jan 2026 14:31:14 +0530 Subject: [PATCH 167/405] dt-bindings: interconnect: qcom,qcs8300-rpmh: add clocks property to enable QoS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some QCS8300 interconnect nodes have QoS registers located inside a block whose interface is clock-gated. For those nodes, driver must enable the corresponding clock(s) before accessing the registers. Add the 'clocks' property so the driver can obtain and enable the required clock(s). Only interconnects that have clock‑gated QoS register interface use this property; it is not applicable to all interconnect nodes. Signed-off-by: Odelu Kukatla Reviewed-by: Krzysztof Kozlowski Link: https://msgid.link/20260127090116.1438780-2-odelu.kukatla@oss.qualcomm.com Signed-off-by: Georgi Djakov --- .../interconnect/qcom,qcs8300-rpmh.yaml | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,qcs8300-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,qcs8300-rpmh.yaml index e9f528d6d9a8..88fe17277110 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,qcs8300-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,qcs8300-rpmh.yaml @@ -35,6 +35,10 @@ properties: reg: maxItems: 1 + clocks: + minItems: 1 + maxItems: 4 + required: - compatible @@ -54,6 +58,64 @@ allOf: required: - reg + - if: + properties: + compatible: + contains: + enum: + - qcom,qcs8300-aggre1-noc + then: + properties: + clocks: + items: + - description: aggre UFS PHY AXI clock + - description: aggre QUP PRIM AXI clock + - description: aggre USB2 PRIM AXI clock + - description: aggre USB3 PRIM AXI clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,qcs8300-aggre2-noc + then: + properties: + clocks: + items: + - description: RPMH CC IPA clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,qcs8300-gem-noc + then: + properties: + clocks: + items: + - description: GCC DDRSS GPU AXI clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,qcs8300-clk-virt + - qcom,qcs8300-config-noc + - qcom,qcs8300-dc-noc + - qcom,qcs8300-gpdsp-anoc + - qcom,qcs8300-lpass-ag-noc + - qcom,qcs8300-mc-virt + - qcom,qcs8300-mmss-noc + - qcom,qcs8300-nspa-noc + - qcom,qcs8300-pcie-anoc + - qcom,qcs8300-system-noc + then: + properties: + clocks: false + unevaluatedProperties: false examples: @@ -63,6 +125,7 @@ examples: reg = <0x9100000 0xf7080>; #interconnect-cells = <2>; qcom,bcm-voters = <&apps_bcm_voter>; + clocks = <&gcc_ddrss_gpu_axi_clk>; }; clk_virt: interconnect-0 { From bc888ba1d493d5c352365cc24de64f093343a7b1 Mon Sep 17 00:00:00 2001 From: Odelu Kukatla Date: Tue, 27 Jan 2026 14:31:15 +0530 Subject: [PATCH 168/405] interconnect: qcom: qcs8300: enable QoS configuration Enable QoS configuration for master ports with predefined priority and urgency forwarding. Signed-off-by: Odelu Kukatla Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Link: https://msgid.link/20260127090116.1438780-3-odelu.kukatla@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/qcs8300.c | 375 ++++++++++++++++++++++++++++ 1 file changed, 375 insertions(+) diff --git a/drivers/interconnect/qcom/qcs8300.c b/drivers/interconnect/qcom/qcs8300.c index bc403a9bf68c..ebf167182572 100644 --- a/drivers/interconnect/qcom/qcs8300.c +++ b/drivers/interconnect/qcom/qcs8300.c @@ -186,6 +186,13 @@ static struct qcom_icc_node qxm_qup3 = { .name = "qxm_qup3", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x11000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -194,6 +201,13 @@ static struct qcom_icc_node xm_emac_0 = { .name = "xm_emac_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x12000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -202,6 +216,13 @@ static struct qcom_icc_node xm_sdc1 = { .name = "xm_sdc1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -210,6 +231,13 @@ static struct qcom_icc_node xm_ufs_mem = { .name = "xm_ufs_mem", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -218,6 +246,13 @@ static struct qcom_icc_node xm_usb2_2 = { .name = "xm_usb2_2", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -226,6 +261,13 @@ static struct qcom_icc_node xm_usb3_0 = { .name = "xm_usb3_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x17000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -234,6 +276,13 @@ static struct qcom_icc_node qhm_qdss_bam = { .name = "qhm_qdss_bam", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -242,6 +291,13 @@ static struct qcom_icc_node qhm_qup0 = { .name = "qhm_qup0", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x17000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -250,6 +306,13 @@ static struct qcom_icc_node qhm_qup1 = { .name = "qhm_qup1", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x12000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -258,6 +321,13 @@ static struct qcom_icc_node qnm_cnoc_datapath = { .name = "qnm_cnoc_datapath", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -266,6 +336,13 @@ static struct qcom_icc_node qxm_crypto_0 = { .name = "qxm_crypto_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x18000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -274,6 +351,13 @@ static struct qcom_icc_node qxm_crypto_1 = { .name = "qxm_crypto_1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1a000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -282,6 +366,13 @@ static struct qcom_icc_node qxm_ipa = { .name = "qxm_ipa", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x11000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -290,6 +381,13 @@ static struct qcom_icc_node xm_qdss_etr_0 = { .name = "xm_qdss_etr_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x13000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -298,6 +396,13 @@ static struct qcom_icc_node xm_qdss_etr_1 = { .name = "xm_qdss_etr_1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x19000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -390,6 +495,13 @@ static struct qcom_icc_node alm_gpu_tcu = { .name = "alm_gpu_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xaf000 }, + .prio_fwd_disable = 1, + .prio = 1, + .urg_fwd = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -398,6 +510,13 @@ static struct qcom_icc_node alm_pcie_tcu = { .name = "alm_pcie_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb0000 }, + .prio_fwd_disable = 1, + .prio = 3, + .urg_fwd = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -406,6 +525,13 @@ static struct qcom_icc_node alm_sys_tcu = { .name = "alm_sys_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb1000 }, + .prio_fwd_disable = 1, + .prio = 6, + .urg_fwd = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -423,6 +549,13 @@ static struct qcom_icc_node qnm_cmpnoc0 = { .name = "qnm_cmpnoc0", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0xf6000, 0xf7000 }, + .prio_fwd_disable = 1, + .prio = 0, + .urg_fwd = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -448,6 +581,13 @@ static struct qcom_icc_node qnm_gpu = { .name = "qnm_gpu", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0xf0000, 0xf1000 }, + .prio_fwd_disable = 1, + .prio = 0, + .urg_fwd = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -456,6 +596,13 @@ static struct qcom_icc_node qnm_mnoc_hf = { .name = "qnm_mnoc_hf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0xf2000, 0xf3000 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_llcc, &qns_pcie }, }; @@ -464,6 +611,13 @@ static struct qcom_icc_node qnm_mnoc_sf = { .name = "qnm_mnoc_sf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0xf4000, 0xf5000 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, &qns_pcie }, @@ -473,6 +627,13 @@ static struct qcom_icc_node qnm_pcie = { .name = "qnm_pcie", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb3000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -481,6 +642,13 @@ static struct qcom_icc_node qnm_snoc_gc = { .name = "qnm_snoc_gc", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb4000 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_llcc }, }; @@ -489,6 +657,13 @@ static struct qcom_icc_node qnm_snoc_sf = { .name = "qnm_snoc_sf", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb5000 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, &qns_pcie }, @@ -541,6 +716,13 @@ static struct qcom_icc_node qnm_camnoc_hf = { .name = "qnm_camnoc_hf", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa000 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -549,6 +731,13 @@ static struct qcom_icc_node qnm_camnoc_icp = { .name = "qnm_camnoc_icp", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2a000 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -557,6 +746,13 @@ static struct qcom_icc_node qnm_camnoc_sf = { .name = "qnm_camnoc_sf", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2a080 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -565,6 +761,13 @@ static struct qcom_icc_node qnm_mdp0_0 = { .name = "qnm_mdp0_0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa080 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -573,6 +776,13 @@ static struct qcom_icc_node qnm_mdp0_1 = { .name = "qnm_mdp0_1", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa180 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -597,6 +807,13 @@ static struct qcom_icc_node qnm_video0 = { .name = "qnm_video0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2a100 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -605,6 +822,13 @@ static struct qcom_icc_node qnm_video_cvp = { .name = "qnm_video_cvp", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2a200 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -613,6 +837,13 @@ static struct qcom_icc_node qnm_video_v_cpu = { .name = "qnm_video_v_cpu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2a280 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -637,6 +868,13 @@ static struct qcom_icc_node xm_pcie3_0 = { .name = "xm_pcie3_0", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_pcie_mem_noc }, }; @@ -645,6 +883,13 @@ static struct qcom_icc_node xm_pcie3_1 = { .name = "xm_pcie3_1", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_pcie_mem_noc }, }; @@ -653,6 +898,13 @@ static struct qcom_icc_node qhm_gic = { .name = "qhm_gic", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_sf }, }; @@ -677,6 +929,13 @@ static struct qcom_icc_node qnm_lpass_noc = { .name = "qnm_lpass_noc", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x12000 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_sf }, }; @@ -693,6 +952,13 @@ static struct qcom_icc_node qxm_pimem = { .name = "qxm_pimem", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x13000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_gc }, }; @@ -701,6 +967,13 @@ static struct qcom_icc_node xm_gic = { .name = "xm_gic", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_gc }, }; @@ -1599,11 +1872,21 @@ static struct qcom_icc_node * const aggre1_noc_nodes[] = { [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, }; +static const struct regmap_config qcs8300_aggre1_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x17080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_aggre1_noc = { + .config = &qcs8300_aggre1_noc_regmap_config, .nodes = aggre1_noc_nodes, .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), .bcms = aggre1_noc_bcms, .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const aggre2_noc_bcms[] = { @@ -1624,11 +1907,21 @@ static struct qcom_icc_node * const aggre2_noc_nodes[] = { [SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc, }; +static const struct regmap_config qcs8300_aggre2_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1a080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_aggre2_noc = { + .config = &qcs8300_aggre2_noc_regmap_config, .nodes = aggre2_noc_nodes, .num_nodes = ARRAY_SIZE(aggre2_noc_nodes), .bcms = aggre2_noc_bcms, .num_bcms = ARRAY_SIZE(aggre2_noc_bcms), + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const clk_virt_bcms[] = { @@ -1740,7 +2033,16 @@ static struct qcom_icc_node * const config_noc_nodes[] = { [SLAVE_TCU] = &xs_sys_tcu_cfg, }; +static const struct regmap_config qcs8300_config_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x13080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_config_noc = { + .config = &qcs8300_config_noc_regmap_config, .nodes = config_noc_nodes, .num_nodes = ARRAY_SIZE(config_noc_nodes), .bcms = config_noc_bcms, @@ -1753,7 +2055,16 @@ static struct qcom_icc_node * const dc_noc_nodes[] = { [SLAVE_GEM_NOC_CFG] = &qns_gemnoc, }; +static const struct regmap_config qcs8300_dc_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x5080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_dc_noc = { + .config = &qcs8300_dc_noc_regmap_config, .nodes = dc_noc_nodes, .num_nodes = ARRAY_SIZE(dc_noc_nodes), }; @@ -1786,11 +2097,21 @@ static struct qcom_icc_node * const gem_noc_nodes[] = { [SLAVE_SERVICE_GEM_NOC2] = &srvc_sys_gemnoc_2, }; +static const struct regmap_config qcs8300_gem_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xf7080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_gem_noc = { + .config = &qcs8300_gem_noc_regmap_config, .nodes = gem_noc_nodes, .num_nodes = ARRAY_SIZE(gem_noc_nodes), .bcms = gem_noc_bcms, .num_bcms = ARRAY_SIZE(gem_noc_bcms), + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const gpdsp_anoc_bcms[] = { @@ -1803,7 +2124,16 @@ static struct qcom_icc_node * const gpdsp_anoc_nodes[] = { [SLAVE_GP_DSP_SAIL_NOC] = &qns_gp_dsp_sail_noc, }; +static const struct regmap_config qcs8300_gpdsp_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xd080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_gpdsp_anoc = { + .config = &qcs8300_gpdsp_anoc_regmap_config, .nodes = gpdsp_anoc_nodes, .num_nodes = ARRAY_SIZE(gpdsp_anoc_nodes), .bcms = gpdsp_anoc_bcms, @@ -1826,7 +2156,16 @@ static struct qcom_icc_node * const lpass_ag_noc_nodes[] = { [SLAVE_SERVICE_LPASS_AG_NOC] = &srvc_niu_lpass_agnoc, }; +static const struct regmap_config qcs8300_lpass_ag_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x17200, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_lpass_ag_noc = { + .config = &qcs8300_lpass_ag_noc_regmap_config, .nodes = lpass_ag_noc_nodes, .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), .bcms = lpass_ag_noc_bcms, @@ -1872,7 +2211,16 @@ static struct qcom_icc_node * const mmss_noc_nodes[] = { [SLAVE_SERVICE_MNOC_SF] = &srvc_mnoc_sf, }; +static const struct regmap_config qcs8300_mmss_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x40000, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_mmss_noc = { + .config = &qcs8300_mmss_noc_regmap_config, .nodes = mmss_noc_nodes, .num_nodes = ARRAY_SIZE(mmss_noc_nodes), .bcms = mmss_noc_bcms, @@ -1892,7 +2240,16 @@ static struct qcom_icc_node * const nspa_noc_nodes[] = { [SLAVE_SERVICE_NSP_NOC] = &service_nsp_noc, }; +static const struct regmap_config qcs8300_nspa_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x16080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_nspa_noc = { + .config = &qcs8300_nspa_noc_regmap_config, .nodes = nspa_noc_nodes, .num_nodes = ARRAY_SIZE(nspa_noc_nodes), .bcms = nspa_noc_bcms, @@ -1909,7 +2266,16 @@ static struct qcom_icc_node * const pcie_anoc_nodes[] = { [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc, }; +static const struct regmap_config qcs8300_pcie_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xc080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_pcie_anoc = { + .config = &qcs8300_pcie_anoc_regmap_config, .nodes = pcie_anoc_nodes, .num_nodes = ARRAY_SIZE(pcie_anoc_nodes), .bcms = pcie_anoc_bcms, @@ -1937,7 +2303,16 @@ static struct qcom_icc_node * const system_noc_nodes[] = { [SLAVE_SERVICE_SNOC] = &srvc_snoc, }; +static const struct regmap_config qcs8300_system_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x15080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_system_noc = { + .config = &qcs8300_system_noc_regmap_config, .nodes = system_noc_nodes, .num_nodes = ARRAY_SIZE(system_noc_nodes), .bcms = system_noc_bcms, From fee48405a027516e0cde9c2b04a2714f035f7157 Mon Sep 17 00:00:00 2001 From: Aaron Kling Date: Thu, 19 Feb 2026 22:07:39 -0600 Subject: [PATCH 169/405] dt-bindings: interconnect: OSM L3: Document sm8550 OSM L3 compatible Document the OSM L3 found in the Qualcomm SM8550 platform. Acked-by: Krzysztof Kozlowski Signed-off-by: Aaron Kling Link: https://msgid.link/20260219-sm8550-ddr-bw-scaling-v3-1-75c19152e921@gmail.com Signed-off-by: Georgi Djakov --- Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml index 4b9b98fbe8f2..3cbe2c3701f7 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml @@ -34,6 +34,7 @@ properties: - qcom,sm6375-cpucp-l3 - qcom,sm8250-epss-l3 - qcom,sm8350-epss-l3 + - qcom,sm8550-epss-l3 - qcom,sm8650-epss-l3 - const: qcom,epss-l3 - items: From 7245b2ad0aee119f8edbb41b3f486af92e4f8baf Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 17 Feb 2026 14:00:36 +0100 Subject: [PATCH 170/405] dt-bindings: interconnect: qcom,glymur-rpmh: De-acronymize SoC name Glymur is a codename of Qualcomm SoC, not an acronym. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Reviewed-by: Taniya Das Link: https://msgid.link/20260217130035.281752-3-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Georgi Djakov --- .../devicetree/bindings/interconnect/qcom,glymur-rpmh.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,glymur-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,glymur-rpmh.yaml index d55a7bcf5591..65b0ff2b2c85 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,glymur-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,glymur-rpmh.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/interconnect/qcom,glymur-rpmh.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Qualcomm RPMh Network-On-Chip Interconnect on GLYMUR +title: Qualcomm RPMh Network-On-Chip Interconnect on Glymur SoC maintainers: - Raviteja Laggyshetty From 26078bbdad9704ba1567d6c79a8191f6184229bf Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 17 Feb 2026 14:00:37 +0100 Subject: [PATCH 171/405] interconnect: qcom: De-acronymize SoC names Glymur and Kaanapali are codenames of Qualcomm SoCs, not acronyms. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Link: https://msgid.link/20260217130035.281752-4-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/Kconfig | 4 ++-- drivers/interconnect/qcom/glymur.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index bb1cb8a640c1..425686f4ec50 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -9,7 +9,7 @@ config INTERCONNECT_QCOM_BCM_VOTER tristate config INTERCONNECT_QCOM_GLYMUR - tristate "Qualcomm GLYMUR interconnect driver" + tristate "Qualcomm Glymur interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER @@ -18,7 +18,7 @@ config INTERCONNECT_QCOM_GLYMUR platforms. config INTERCONNECT_QCOM_KAANAPALI - tristate "Qualcomm KAANAPALI interconnect driver" + tristate "Qualcomm Kaanapali interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER diff --git a/drivers/interconnect/qcom/glymur.c b/drivers/interconnect/qcom/glymur.c index e5c07795a6c6..4fa8be6375e7 100644 --- a/drivers/interconnect/qcom/glymur.c +++ b/drivers/interconnect/qcom/glymur.c @@ -2518,5 +2518,5 @@ static void __exit qnoc_driver_exit(void) } module_exit(qnoc_driver_exit); -MODULE_DESCRIPTION("GLYMUR NoC driver"); +MODULE_DESCRIPTION("Glymur NoC driver"); MODULE_LICENSE("GPL"); From a39efc80ff507d91cdaa4d2d143143300330f599 Mon Sep 17 00:00:00 2001 From: Odelu Kukatla Date: Tue, 24 Feb 2026 13:43:07 +0200 Subject: [PATCH 172/405] interconnect: qcom: Add Eliza interconnect provider driver Add driver for the Qualcomm interconnect buses found in Eliza based platforms. The topology consists of several NoCs that are controlled by a remote processor that collects the aggregated bandwidth for each master-slave pairs. Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Signed-off-by: Odelu Kukatla Signed-off-by: Abel Vesa Link: https://msgid.link/20260224-eliza-interconnect-v4-2-ad75855d5018@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/Kconfig | 9 + drivers/interconnect/qcom/Makefile | 2 + drivers/interconnect/qcom/eliza.c | 1585 ++++++++++++++++++++++++++++ 3 files changed, 1596 insertions(+) create mode 100644 drivers/interconnect/qcom/eliza.c diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index bb1cb8a640c1..d18afe4392f1 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -8,6 +8,15 @@ config INTERCONNECT_QCOM config INTERCONNECT_QCOM_BCM_VOTER tristate +config INTERCONNECT_QCOM_ELIZA + tristate "Qualcomm Eliza interconnect driver" + depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + select INTERCONNECT_QCOM_RPMH + select INTERCONNECT_QCOM_BCM_VOTER + help + This is a driver for the Qualcomm Network-on-Chip on Eliza-based + platforms. + config INTERCONNECT_QCOM_GLYMUR tristate "Qualcomm GLYMUR interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 6eedff043b41..cdf2c6c9fbf3 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM) += interconnect_qcom.o interconnect_qcom-y := icc-common.o icc-bcm-voter-objs := bcm-voter.o +qnoc-eliza-objs := eliza.o qnoc-glymur-objs := glymur.o qnoc-kaanapali-objs := kaanapali.o qnoc-milos-objs := milos.o @@ -48,6 +49,7 @@ qnoc-x1e80100-objs := x1e80100.o icc-smd-rpm-objs := smd-rpm.o icc-rpm.o icc-rpm-clocks.o obj-$(CONFIG_INTERCONNECT_QCOM_BCM_VOTER) += icc-bcm-voter.o +obj-$(CONFIG_INTERCONNECT_QCOM_ELIZA) += qnoc-eliza.o obj-$(CONFIG_INTERCONNECT_QCOM_GLYMUR) += qnoc-glymur.o obj-$(CONFIG_INTERCONNECT_QCOM_KAANAPALI) += qnoc-kaanapali.o obj-$(CONFIG_INTERCONNECT_QCOM_MILOS) += qnoc-milos.o diff --git a/drivers/interconnect/qcom/eliza.c b/drivers/interconnect/qcom/eliza.c new file mode 100644 index 000000000000..a4f7903f0524 --- /dev/null +++ b/drivers/interconnect/qcom/eliza.c @@ -0,0 +1,1585 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include + +#include "bcm-voter.h" +#include "icc-rpmh.h" + +static struct qcom_icc_node qup1_core_slave = { + .name = "qup1_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup2_core_slave = { + .name = "qup2_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ahb2phy0 = { + .name = "qhs_ahb2phy0", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ahb2phy1 = { + .name = "qhs_ahb2phy1", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_camera_cfg = { + .name = "qhs_camera_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_clk_ctl = { + .name = "qhs_clk_ctl", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_crypto0_cfg = { + .name = "qhs_crypto0_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_display_cfg = { + .name = "qhs_display_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_gpuss_cfg = { + .name = "qhs_gpuss_cfg", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qhs_i3c_ibi0_cfg = { + .name = "qhs_i3c_ibi0_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_i3c_ibi1_cfg = { + .name = "qhs_i3c_ibi1_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_imem_cfg = { + .name = "qhs_imem_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_mss_cfg = { + .name = "qhs_mss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_pcie_0_cfg = { + .name = "qhs_pcie_0_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_prng = { + .name = "qhs_prng", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qdss_cfg = { + .name = "qhs_qdss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qspi = { + .name = "qhs_qspi", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup1 = { + .name = "qhs_qup1", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup2 = { + .name = "qhs_qup2", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_sdc2 = { + .name = "qhs_sdc2", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_tcsr = { + .name = "qhs_tcsr", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_tlmm = { + .name = "qhs_tlmm", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ufs_mem_cfg = { + .name = "qhs_ufs_mem_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_usb3_0 = { + .name = "qhs_usb3_0", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_venus_cfg = { + .name = "qhs_venus_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_vsense_ctrl_cfg = { + .name = "qhs_vsense_ctrl_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node xs_qdss_stm = { + .name = "xs_qdss_stm", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node xs_sys_tcu_cfg = { + .name = "xs_sys_tcu_cfg", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qhs_aoss = { + .name = "qhs_aoss", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ipa = { + .name = "qhs_ipa", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ipc_router = { + .name = "qhs_ipc_router", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_soccp = { + .name = "qhs_soccp", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_tme_cfg = { + .name = "qhs_tme_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_apss = { + .name = "qss_apss", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_ddrss_cfg = { + .name = "qss_ddrss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qxs_boot_imem = { + .name = "qxs_boot_imem", + .channels = 1, + .buswidth = 16, +}; + +static struct qcom_icc_node qxs_imem = { + .name = "qxs_imem", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qxs_modem_boot_imem = { + .name = "qxs_modem_boot_imem", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node srvc_cnoc_main = { + .name = "srvc_cnoc_main", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node xs_pcie_0 = { + .name = "xs_pcie_0", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node xs_pcie_1 = { + .name = "xs_pcie_1", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node ebi = { + .name = "ebi", + .channels = 4, + .buswidth = 4, +}; + +static struct qcom_icc_node srvc_mnoc_sf = { + .name = "srvc_mnoc_sf", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node srvc_mnoc_hf = { + .name = "srvc_mnoc_hf", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node srvc_pcie_aggre_noc = { + .name = "srvc_pcie_aggre_noc", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup1_core_master = { + .name = "qup1_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_node qup2_core_master = { + .name = "qup2_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup2_core_slave }, +}; + +static struct qcom_icc_node qnm_gemnoc_pcie = { + .name = "qnm_gemnoc_pcie", + .channels = 1, + .buswidth = 16, + .num_links = 2, + .link_nodes = { &xs_pcie_0, &xs_pcie_1 }, +}; + +static struct qcom_icc_node llcc_mc = { + .name = "llcc_mc", + .channels = 4, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &ebi }, +}; + +static struct qcom_icc_node qsm_sf_mnoc_cfg = { + .name = "qsm_sf_mnoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &srvc_mnoc_sf }, +}; + +static struct qcom_icc_node qsm_hf_mnoc_cfg = { + .name = "qsm_hf_mnoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &srvc_mnoc_hf }, +}; + +static struct qcom_icc_node qsm_pcie_anoc_cfg = { + .name = "qsm_pcie_anoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &srvc_pcie_aggre_noc }, +}; + +static struct qcom_icc_node qss_mnoc_hf_cfg = { + .name = "qss_mnoc_hf_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_hf_mnoc_cfg }, +}; + +static struct qcom_icc_node qss_mnoc_sf_cfg = { + .name = "qss_mnoc_sf_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_sf_mnoc_cfg }, +}; + +static struct qcom_icc_node qss_pcie_anoc_cfg = { + .name = "qss_pcie_anoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_pcie_anoc_cfg }, +}; + +static struct qcom_icc_node qns_llcc = { + .name = "qns_llcc", + .channels = 2, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &llcc_mc }, +}; + +static struct qcom_icc_node qns_pcie = { + .name = "qns_pcie", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_gemnoc_pcie }, +}; + +static struct qcom_icc_node qsm_cfg = { + .name = "qsm_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 29, + .link_nodes = { &qhs_ahb2phy0, &qhs_ahb2phy1, + &qhs_camera_cfg, &qhs_clk_ctl, + &qhs_crypto0_cfg, &qhs_display_cfg, + &qhs_gpuss_cfg, &qhs_i3c_ibi0_cfg, + &qhs_i3c_ibi1_cfg, &qhs_imem_cfg, + &qhs_mss_cfg, &qhs_pcie_0_cfg, + &qhs_prng, &qhs_qdss_cfg, + &qhs_qspi, &qhs_qup1, + &qhs_qup2, &qhs_sdc2, + &qhs_tcsr, &qhs_tlmm, + &qhs_ufs_mem_cfg, &qhs_usb3_0, + &qhs_venus_cfg, &qhs_vsense_ctrl_cfg, + &qss_mnoc_hf_cfg, &qss_mnoc_sf_cfg, + &qss_pcie_anoc_cfg, &xs_qdss_stm, + &xs_sys_tcu_cfg }, +}; + +static struct qcom_icc_node xm_gic = { + .name = "xm_gic", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15d000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_llcc }, +}; + +static struct qcom_icc_node qss_cfg = { + .name = "qss_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg }, +}; + +static struct qcom_icc_node qnm_gemnoc_cnoc = { + .name = "qnm_gemnoc_cnoc", + .channels = 1, + .buswidth = 16, + .num_links = 12, + .link_nodes = { &qhs_aoss, &qhs_ipa, + &qhs_ipc_router, &qhs_soccp, + &qhs_tme_cfg, &qss_apss, + &qss_cfg, &qss_ddrss_cfg, + &qxs_boot_imem, &qxs_imem, + &qxs_modem_boot_imem, &srvc_cnoc_main }, +}; + +static struct qcom_icc_node qns_gem_noc_cnoc = { + .name = "qns_gem_noc_cnoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_gemnoc_cnoc }, +}; + +static struct qcom_icc_qosbox alm_gpu_tcu_qos = { + .num_ports = 1, + .port_offsets = { 0x155000 }, + .prio = 1, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node alm_gpu_tcu = { + .name = "alm_gpu_tcu", + .channels = 1, + .buswidth = 8, + .qosbox = &alm_gpu_tcu_qos, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_qosbox alm_sys_tcu_qos = { + .num_ports = 1, + .port_offsets = { 0x157000 }, + .prio = 6, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node alm_sys_tcu = { + .name = "alm_sys_tcu", + .channels = 1, + .buswidth = 8, + .qosbox = &alm_sys_tcu_qos, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node chm_apps = { + .name = "chm_apps", + .channels = 3, + .buswidth = 32, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_qosbox qnm_gpu_qos = { + .num_ports = 2, + .port_offsets = { 0x31000, 0xb1000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qnm_gpu = { + .name = "qnm_gpu", + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_gpu_qos, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_qosbox qnm_lpass_gemnoc_qos = { + .num_ports = 1, + .port_offsets = { 0x159000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_lpass_gemnoc = { + .name = "qnm_lpass_gemnoc", + .channels = 1, + .buswidth = 16, + .qosbox = &qnm_lpass_gemnoc_qos, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_mdsp = { + .name = "qnm_mdsp", + .channels = 1, + .buswidth = 16, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_qosbox qnm_mnoc_hf_qos = { + .num_ports = 2, + .port_offsets = { 0x33000, 0xb3000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_mnoc_hf = { + .name = "qnm_mnoc_hf", + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_mnoc_hf_qos, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_qosbox qnm_mnoc_sf_qos = { + .num_ports = 2, + .port_offsets = { 0x35000, 0xb5000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_mnoc_sf = { + .name = "qnm_mnoc_sf", + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_mnoc_sf_qos, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_qosbox qnm_nsp_gemnoc_qos = { + .num_ports = 2, + .port_offsets = { 0x37000, 0xb7000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qnm_nsp_gemnoc = { + .name = "qnm_nsp_gemnoc", + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_nsp_gemnoc_qos, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_qosbox qnm_pcie_qos = { + .num_ports = 1, + .port_offsets = { 0x15b000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_pcie = { + .name = "qnm_pcie", + .channels = 1, + .buswidth = 16, + .qosbox = &qnm_pcie_qos, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node qnm_snoc_sf = { + .name = "qnm_snoc_sf", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15f000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qxm_wlan_q6 = { + .name = "qxm_wlan_q6", + .channels = 1, + .buswidth = 8, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qns_lpass_ag_noc_gemnoc = { + .name = "qns_lpass_ag_noc_gemnoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_lpass_gemnoc }, +}; + +static struct qcom_icc_node qns_mem_noc_sf = { + .name = "qns_mem_noc_sf", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_mnoc_sf }, +}; + +static struct qcom_icc_node qns_mem_noc_hf = { + .name = "qns_mem_noc_hf", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_mnoc_hf }, +}; + +static struct qcom_icc_node qns_nsp_gemnoc = { + .name = "qns_nsp_gemnoc", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_nsp_gemnoc }, +}; + +static struct qcom_icc_node qns_pcie_mem_noc = { + .name = "qns_pcie_mem_noc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_pcie }, +}; + +static struct qcom_icc_node qns_gemnoc_sf = { + .name = "qns_gemnoc_sf", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_snoc_sf }, +}; + +static struct qcom_icc_node qnm_lpiaon_noc = { + .name = "qnm_lpiaon_noc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qns_lpass_ag_noc_gemnoc }, +}; + +static struct qcom_icc_qosbox qnm_camnoc_nrt_icp_sf_qos = { + .num_ports = 1, + .port_offsets = { 0x25000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qnm_camnoc_nrt_icp_sf = { + .name = "qnm_camnoc_nrt_icp_sf", + .channels = 1, + .buswidth = 8, + .qosbox = &qnm_camnoc_nrt_icp_sf_qos, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_camnoc_rt_cdm_sf = { + .name = "qnm_camnoc_rt_cdm_sf", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2c000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_camnoc_sf = { + .name = "qnm_camnoc_sf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x26000, 0x27000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_mvp = { + .name = "qnm_video_mvp", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x28000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_v_cpu = { + .name = "qnm_video_v_cpu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2b000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_camnoc_hf = { + .name = "qnm_camnoc_hf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x64000, 0x65000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_node qnm_mdp = { + .name = "qnm_mdp", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x66000, 0x67000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_node qxm_nsp = { + .name = "qxm_nsp", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_node xm_pcie3_0 = { + .name = "xm_pcie3_0", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 3, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_pcie_mem_noc }, +}; + +static struct qcom_icc_node xm_pcie3_1 = { + .name = "xm_pcie3_1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 3, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_pcie_mem_noc }, +}; + +static struct qcom_icc_node qnm_aggre1_noc = { + .name = "qnm_aggre1_noc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qnm_aggre2_noc = { + .name = "qnm_aggre2_noc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qnm_cnoc_data = { + .name = "qnm_cnoc_data", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1d000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qnm_nsinoc_snoc = { + .name = "qnm_nsinoc_snoc", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1c000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qns_a1noc_snoc = { + .name = "qns_a1noc_snoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_aggre1_noc }, +}; + +static struct qcom_icc_node qns_a2noc_snoc = { + .name = "qns_a2noc_snoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_aggre2_noc }, +}; + +static struct qcom_icc_node qns_lpass_aggnoc = { + .name = "qns_lpass_aggnoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_lpiaon_noc }, +}; + +static struct qcom_icc_node qhm_qspi = { + .name = "qhm_qspi", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup1 = { + .name = "qhm_qup1", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_ufs_mem = { + .name = "xm_ufs_mem", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_usb3_0 = { + .name = "xm_usb3_0", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x10000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup2 = { + .name = "qhm_qup2", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node qxm_crypto = { + .name = "qxm_crypto", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node qxm_ipa = { + .name = "qxm_ipa", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node qxm_soccp = { + .name = "qxm_soccp", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1a000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node xm_qdss_etr_0 = { + .name = "xm_qdss_etr_0", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x17000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node xm_qdss_etr_1 = { + .name = "xm_qdss_etr_1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x18000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node xm_sdc1 = { + .name = "xm_sdc1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x13000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node xm_sdc2 = { + .name = "xm_sdc2", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x19000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node qnm_lpass_lpinoc = { + .name = "qnm_lpass_lpinoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qns_lpass_aggnoc }, +}; + +static struct qcom_icc_node qns_lpi_aon_noc = { + .name = "qns_lpi_aon_noc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_lpass_lpinoc }, +}; + +static struct qcom_icc_node qxm_lpinoc_dsp_axim = { + .name = "qxm_lpinoc_dsp_axim", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qns_lpi_aon_noc }, +}; + +static struct qcom_icc_bcm bcm_ce0 = { + .name = "CE0", + .num_nodes = 1, + .nodes = { &qxm_crypto }, +}; + +static struct qcom_icc_bcm bcm_cn0 = { + .name = "CN0", + .enable_mask = BIT(0), + .keepalive = true, + .num_nodes = 43, + .nodes = { &qsm_cfg, &qhs_ahb2phy0, + &qhs_ahb2phy1, &qhs_camera_cfg, + &qhs_clk_ctl, &qhs_crypto0_cfg, + &qhs_gpuss_cfg, &qhs_i3c_ibi0_cfg, + &qhs_i3c_ibi1_cfg, &qhs_imem_cfg, + &qhs_mss_cfg, &qhs_pcie_0_cfg, + &qhs_prng, &qhs_qdss_cfg, + &qhs_qspi, &qhs_sdc2, + &qhs_tcsr, &qhs_tlmm, + &qhs_ufs_mem_cfg, &qhs_usb3_0, + &qhs_venus_cfg, &qhs_vsense_ctrl_cfg, + &qss_mnoc_hf_cfg, &qss_mnoc_sf_cfg, + &qss_pcie_anoc_cfg, &xs_qdss_stm, + &xs_sys_tcu_cfg, &qnm_gemnoc_cnoc, + &qnm_gemnoc_pcie, &qhs_aoss, + &qhs_ipa, &qhs_ipc_router, + &qhs_soccp, &qhs_tme_cfg, + &qss_apss, &qss_cfg, + &qss_ddrss_cfg, &qxs_boot_imem, + &qxs_imem, &qxs_modem_boot_imem, + &srvc_cnoc_main, &xs_pcie_0, + &xs_pcie_1 }, +}; + +static struct qcom_icc_bcm bcm_cn1 = { + .name = "CN1", + .num_nodes = 3, + .nodes = { &qhs_display_cfg, &qhs_qup1, + &qhs_qup2 }, +}; + +static struct qcom_icc_bcm bcm_co0 = { + .name = "CO0", + .enable_mask = BIT(0), + .num_nodes = 2, + .nodes = { &qxm_nsp, &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_bcm bcm_lp0 = { + .name = "LP0", + .num_nodes = 2, + .nodes = { &qnm_lpass_lpinoc, &qns_lpass_aggnoc }, +}; + +static struct qcom_icc_bcm bcm_mc0 = { + .name = "MC0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_mm0 = { + .name = "MM0", + .num_nodes = 1, + .nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_bcm bcm_mm1 = { + .name = "MM1", + .enable_mask = BIT(0), + .num_nodes = 7, + .nodes = { &qnm_camnoc_nrt_icp_sf, &qnm_camnoc_rt_cdm_sf, + &qnm_camnoc_sf, &qnm_video_mvp, + &qnm_video_v_cpu, &qnm_camnoc_hf, + &qns_mem_noc_sf }, +}; + +static struct qcom_icc_bcm bcm_qup1 = { + .name = "QUP1", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup2 = { + .name = "QUP2", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup2_core_slave }, +}; + +static struct qcom_icc_bcm bcm_sh0 = { + .name = "SH0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_llcc }, +}; + +static struct qcom_icc_bcm bcm_sh1 = { + .name = "SH1", + .enable_mask = BIT(0), + .num_nodes = 14, + .nodes = { &alm_gpu_tcu, &alm_sys_tcu, + &chm_apps, &qnm_gpu, + &qnm_mdsp, &qnm_mnoc_hf, + &qnm_mnoc_sf, &qnm_nsp_gemnoc, + &qnm_pcie, &qnm_snoc_sf, + &qxm_wlan_q6, &xm_gic, + &qns_gem_noc_cnoc, &qns_pcie }, +}; + +static struct qcom_icc_bcm bcm_sn0 = { + .name = "SN0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_bcm bcm_sn2 = { + .name = "SN2", + .num_nodes = 1, + .nodes = { &qnm_aggre1_noc }, +}; + +static struct qcom_icc_bcm bcm_sn3 = { + .name = "SN3", + .num_nodes = 1, + .nodes = { &qnm_aggre2_noc }, +}; + +static struct qcom_icc_bcm bcm_sn4 = { + .name = "SN4", + .num_nodes = 1, + .nodes = { &qns_pcie_mem_noc }, +}; + +static struct qcom_icc_node * const aggre1_noc_nodes[] = { + [MASTER_QSPI_0] = &qhm_qspi, + [MASTER_QUP_1] = &qhm_qup1, + [MASTER_UFS_MEM] = &xm_ufs_mem, + [MASTER_USB3_0] = &xm_usb3_0, + [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, +}; + +static const struct qcom_icc_desc eliza_aggre1_noc = { + .nodes = aggre1_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), + .qos_requires_clocks = true, +}; + +static struct qcom_icc_bcm * const aggre2_noc_bcms[] = { + &bcm_ce0, +}; + +static struct qcom_icc_node * const aggre2_noc_nodes[] = { + [MASTER_QUP_2] = &qhm_qup2, + [MASTER_CRYPTO] = &qxm_crypto, + [MASTER_IPA] = &qxm_ipa, + [MASTER_SOCCP_AGGR_NOC] = &qxm_soccp, + [MASTER_QDSS_ETR] = &xm_qdss_etr_0, + [MASTER_QDSS_ETR_1] = &xm_qdss_etr_1, + [MASTER_SDCC_1] = &xm_sdc1, + [MASTER_SDCC_2] = &xm_sdc2, + [SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc, +}; + +static const struct qcom_icc_desc eliza_aggre2_noc = { + .nodes = aggre2_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre2_noc_nodes), + .bcms = aggre2_noc_bcms, + .num_bcms = ARRAY_SIZE(aggre2_noc_bcms), + .qos_requires_clocks = true, +}; + +static struct qcom_icc_bcm * const clk_virt_bcms[] = { + &bcm_qup1, + &bcm_qup2, +}; + +static struct qcom_icc_node * const clk_virt_nodes[] = { + [MASTER_QUP_CORE_1] = &qup1_core_master, + [MASTER_QUP_CORE_2] = &qup2_core_master, + [SLAVE_QUP_CORE_1] = &qup1_core_slave, + [SLAVE_QUP_CORE_2] = &qup2_core_slave, +}; + +static const struct qcom_icc_desc eliza_clk_virt = { + .nodes = clk_virt_nodes, + .num_nodes = ARRAY_SIZE(clk_virt_nodes), + .bcms = clk_virt_bcms, + .num_bcms = ARRAY_SIZE(clk_virt_bcms), +}; + +static struct qcom_icc_bcm * const cnoc_cfg_bcms[] = { + &bcm_cn0, + &bcm_cn1, +}; + +static struct qcom_icc_node * const cnoc_cfg_nodes[] = { + [MASTER_CNOC_CFG] = &qsm_cfg, + [SLAVE_AHB2PHY_SOUTH] = &qhs_ahb2phy0, + [SLAVE_AHB2PHY_NORTH] = &qhs_ahb2phy1, + [SLAVE_CAMERA_CFG] = &qhs_camera_cfg, + [SLAVE_CLK_CTL] = &qhs_clk_ctl, + [SLAVE_CRYPTO_0_CFG] = &qhs_crypto0_cfg, + [SLAVE_DISPLAY_CFG] = &qhs_display_cfg, + [SLAVE_GFX3D_CFG] = &qhs_gpuss_cfg, + [SLAVE_I3C_IBI0_CFG] = &qhs_i3c_ibi0_cfg, + [SLAVE_I3C_IBI1_CFG] = &qhs_i3c_ibi1_cfg, + [SLAVE_IMEM_CFG] = &qhs_imem_cfg, + [SLAVE_CNOC_MSS] = &qhs_mss_cfg, + [SLAVE_PCIE_0_CFG] = &qhs_pcie_0_cfg, + [SLAVE_PRNG] = &qhs_prng, + [SLAVE_QDSS_CFG] = &qhs_qdss_cfg, + [SLAVE_QSPI_0] = &qhs_qspi, + [SLAVE_QUP_1] = &qhs_qup1, + [SLAVE_QUP_2] = &qhs_qup2, + [SLAVE_SDCC_2] = &qhs_sdc2, + [SLAVE_TCSR] = &qhs_tcsr, + [SLAVE_TLMM] = &qhs_tlmm, + [SLAVE_UFS_MEM_CFG] = &qhs_ufs_mem_cfg, + [SLAVE_USB3_0] = &qhs_usb3_0, + [SLAVE_VENUS_CFG] = &qhs_venus_cfg, + [SLAVE_VSENSE_CTRL_CFG] = &qhs_vsense_ctrl_cfg, + [SLAVE_CNOC_MNOC_HF_CFG] = &qss_mnoc_hf_cfg, + [SLAVE_CNOC_MNOC_SF_CFG] = &qss_mnoc_sf_cfg, + [SLAVE_PCIE_ANOC_CFG] = &qss_pcie_anoc_cfg, + [SLAVE_QDSS_STM] = &xs_qdss_stm, + [SLAVE_TCU] = &xs_sys_tcu_cfg, +}; + +static const struct qcom_icc_desc eliza_cnoc_cfg = { + .nodes = cnoc_cfg_nodes, + .num_nodes = ARRAY_SIZE(cnoc_cfg_nodes), + .bcms = cnoc_cfg_bcms, + .num_bcms = ARRAY_SIZE(cnoc_cfg_bcms), +}; + +static struct qcom_icc_bcm * const cnoc_main_bcms[] = { + &bcm_cn0, +}; + +static struct qcom_icc_node * const cnoc_main_nodes[] = { + [MASTER_GEM_NOC_CNOC] = &qnm_gemnoc_cnoc, + [MASTER_GEM_NOC_PCIE_SNOC] = &qnm_gemnoc_pcie, + [SLAVE_AOSS] = &qhs_aoss, + [SLAVE_IPA_CFG] = &qhs_ipa, + [SLAVE_IPC_ROUTER_CFG] = &qhs_ipc_router, + [SLAVE_SOCCP] = &qhs_soccp, + [SLAVE_TME_CFG] = &qhs_tme_cfg, + [SLAVE_APPSS] = &qss_apss, + [SLAVE_CNOC_CFG] = &qss_cfg, + [SLAVE_DDRSS_CFG] = &qss_ddrss_cfg, + [SLAVE_BOOT_IMEM] = &qxs_boot_imem, + [SLAVE_IMEM] = &qxs_imem, + [SLAVE_BOOT_IMEM_2] = &qxs_modem_boot_imem, + [SLAVE_SERVICE_CNOC] = &srvc_cnoc_main, + [SLAVE_PCIE_0] = &xs_pcie_0, + [SLAVE_PCIE_1] = &xs_pcie_1, +}; + +static const struct qcom_icc_desc eliza_cnoc_main = { + .nodes = cnoc_main_nodes, + .num_nodes = ARRAY_SIZE(cnoc_main_nodes), + .bcms = cnoc_main_bcms, + .num_bcms = ARRAY_SIZE(cnoc_main_bcms), +}; + +static struct qcom_icc_bcm * const gem_noc_bcms[] = { + &bcm_sh0, + &bcm_sh1, +}; + +static struct qcom_icc_node * const gem_noc_nodes[] = { + [MASTER_GPU_TCU] = &alm_gpu_tcu, + [MASTER_SYS_TCU] = &alm_sys_tcu, + [MASTER_APPSS_PROC] = &chm_apps, + [MASTER_GFX3D] = &qnm_gpu, + [MASTER_LPASS_GEM_NOC] = &qnm_lpass_gemnoc, + [MASTER_MSS_PROC] = &qnm_mdsp, + [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf, + [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf, + [MASTER_COMPUTE_NOC] = &qnm_nsp_gemnoc, + [MASTER_ANOC_PCIE_GEM_NOC] = &qnm_pcie, + [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, + [MASTER_WLAN_Q6] = &qxm_wlan_q6, + [MASTER_GIC] = &xm_gic, + [SLAVE_GEM_NOC_CNOC] = &qns_gem_noc_cnoc, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, +}; + +static const struct qcom_icc_desc eliza_gem_noc = { + .nodes = gem_noc_nodes, + .num_nodes = ARRAY_SIZE(gem_noc_nodes), + .bcms = gem_noc_bcms, + .num_bcms = ARRAY_SIZE(gem_noc_bcms), +}; + +static struct qcom_icc_node * const lpass_ag_noc_nodes[] = { + [MASTER_LPIAON_NOC] = &qnm_lpiaon_noc, + [SLAVE_LPASS_GEM_NOC] = &qns_lpass_ag_noc_gemnoc, +}; + +static const struct qcom_icc_desc eliza_lpass_ag_noc = { + .nodes = lpass_ag_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), +}; + +static struct qcom_icc_bcm * const lpass_lpiaon_noc_bcms[] = { + &bcm_lp0, +}; + +static struct qcom_icc_node * const lpass_lpiaon_noc_nodes[] = { + [MASTER_LPASS_LPINOC] = &qnm_lpass_lpinoc, + [SLAVE_LPIAON_NOC_LPASS_AG_NOC] = &qns_lpass_aggnoc, +}; + +static const struct qcom_icc_desc eliza_lpass_lpiaon_noc = { + .nodes = lpass_lpiaon_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_lpiaon_noc_nodes), + .bcms = lpass_lpiaon_noc_bcms, + .num_bcms = ARRAY_SIZE(lpass_lpiaon_noc_bcms), +}; + +static struct qcom_icc_node * const lpass_lpicx_noc_nodes[] = { + [MASTER_LPASS_PROC] = &qxm_lpinoc_dsp_axim, + [SLAVE_LPICX_NOC_LPIAON_NOC] = &qns_lpi_aon_noc, +}; + +static const struct qcom_icc_desc eliza_lpass_lpicx_noc = { + .nodes = lpass_lpicx_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_lpicx_noc_nodes), +}; + +static struct qcom_icc_bcm * const mc_virt_bcms[] = { + &bcm_mc0, +}; + +static struct qcom_icc_node * const mc_virt_nodes[] = { + [MASTER_LLCC] = &llcc_mc, + [SLAVE_EBI1] = &ebi, +}; + +static const struct qcom_icc_desc eliza_mc_virt = { + .nodes = mc_virt_nodes, + .num_nodes = ARRAY_SIZE(mc_virt_nodes), + .bcms = mc_virt_bcms, + .num_bcms = ARRAY_SIZE(mc_virt_bcms), +}; + +static struct qcom_icc_bcm * const mmss_noc_bcms[] = { + &bcm_mm0, + &bcm_mm1, +}; + +static struct qcom_icc_node * const mmss_noc_nodes[] = { + [MASTER_CAMNOC_NRT_ICP_SF] = &qnm_camnoc_nrt_icp_sf, + [MASTER_CAMNOC_RT_CDM_SF] = &qnm_camnoc_rt_cdm_sf, + [MASTER_CAMNOC_SF] = &qnm_camnoc_sf, + [MASTER_VIDEO_MVP] = &qnm_video_mvp, + [MASTER_VIDEO_V_PROC] = &qnm_video_v_cpu, + [MASTER_CNOC_MNOC_SF_CFG] = &qsm_sf_mnoc_cfg, + [MASTER_CAMNOC_HF] = &qnm_camnoc_hf, + [MASTER_MDP] = &qnm_mdp, + [MASTER_CNOC_MNOC_HF_CFG] = &qsm_hf_mnoc_cfg, + [SLAVE_MNOC_SF_MEM_NOC] = &qns_mem_noc_sf, + [SLAVE_SERVICE_MNOC_SF] = &srvc_mnoc_sf, + [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf, + [SLAVE_SERVICE_MNOC_HF] = &srvc_mnoc_hf, +}; + +static const struct qcom_icc_desc eliza_mmss_noc = { + .nodes = mmss_noc_nodes, + .num_nodes = ARRAY_SIZE(mmss_noc_nodes), + .bcms = mmss_noc_bcms, + .num_bcms = ARRAY_SIZE(mmss_noc_bcms), +}; + +static struct qcom_icc_bcm * const nsp_noc_bcms[] = { + &bcm_co0, +}; + +static struct qcom_icc_node * const nsp_noc_nodes[] = { + [MASTER_CDSP_PROC] = &qxm_nsp, + [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc, +}; + +static const struct qcom_icc_desc eliza_nsp_noc = { + .nodes = nsp_noc_nodes, + .num_nodes = ARRAY_SIZE(nsp_noc_nodes), + .bcms = nsp_noc_bcms, + .num_bcms = ARRAY_SIZE(nsp_noc_bcms), +}; + +static struct qcom_icc_bcm * const pcie_anoc_bcms[] = { + &bcm_sn4, +}; + +static struct qcom_icc_node * const pcie_anoc_nodes[] = { + [MASTER_PCIE_ANOC_CFG] = &qsm_pcie_anoc_cfg, + [MASTER_PCIE_0] = &xm_pcie3_0, + [MASTER_PCIE_1] = &xm_pcie3_1, + [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc, + [SLAVE_SERVICE_PCIE_ANOC] = &srvc_pcie_aggre_noc, +}; + +static const struct qcom_icc_desc eliza_pcie_anoc = { + .nodes = pcie_anoc_nodes, + .num_nodes = ARRAY_SIZE(pcie_anoc_nodes), + .bcms = pcie_anoc_bcms, + .num_bcms = ARRAY_SIZE(pcie_anoc_bcms), + .qos_requires_clocks = true, +}; + +static struct qcom_icc_bcm * const system_noc_bcms[] = { + &bcm_sn0, + &bcm_sn2, + &bcm_sn3, +}; + +static struct qcom_icc_node * const system_noc_nodes[] = { + [MASTER_A1NOC_SNOC] = &qnm_aggre1_noc, + [MASTER_A2NOC_SNOC] = &qnm_aggre2_noc, + [MASTER_CNOC_SNOC] = &qnm_cnoc_data, + [MASTER_NSINOC_SNOC] = &qnm_nsinoc_snoc, + [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf, +}; + +static const struct qcom_icc_desc eliza_system_noc = { + .nodes = system_noc_nodes, + .num_nodes = ARRAY_SIZE(system_noc_nodes), + .bcms = system_noc_bcms, + .num_bcms = ARRAY_SIZE(system_noc_bcms), +}; + +static const struct of_device_id qnoc_of_match[] = { + { .compatible = "qcom,eliza-aggre1-noc", .data = &eliza_aggre1_noc }, + { .compatible = "qcom,eliza-aggre2-noc", .data = &eliza_aggre2_noc }, + { .compatible = "qcom,eliza-clk-virt", .data = &eliza_clk_virt }, + { .compatible = "qcom,eliza-cnoc-cfg", .data = &eliza_cnoc_cfg }, + { .compatible = "qcom,eliza-cnoc-main", .data = &eliza_cnoc_main }, + { .compatible = "qcom,eliza-gem-noc", .data = &eliza_gem_noc }, + { .compatible = "qcom,eliza-lpass-ag-noc", .data = &eliza_lpass_ag_noc }, + { .compatible = "qcom,eliza-lpass-lpiaon-noc", .data = &eliza_lpass_lpiaon_noc }, + { .compatible = "qcom,eliza-lpass-lpicx-noc", .data = &eliza_lpass_lpicx_noc }, + { .compatible = "qcom,eliza-mc-virt", .data = &eliza_mc_virt }, + { .compatible = "qcom,eliza-mmss-noc", .data = &eliza_mmss_noc }, + { .compatible = "qcom,eliza-nsp-noc", .data = &eliza_nsp_noc }, + { .compatible = "qcom,eliza-pcie-anoc", .data = &eliza_pcie_anoc }, + { .compatible = "qcom,eliza-system-noc", .data = &eliza_system_noc }, + { } +}; +MODULE_DEVICE_TABLE(of, qnoc_of_match); + +static struct platform_driver qnoc_driver = { + .probe = qcom_icc_rpmh_probe, + .remove = qcom_icc_rpmh_remove, + .driver = { + .name = "qnoc-eliza", + .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, + }, +}; + +static int __init qnoc_driver_init(void) +{ + return platform_driver_register(&qnoc_driver); +} +core_initcall(qnoc_driver_init); + +static void __exit qnoc_driver_exit(void) +{ + platform_driver_unregister(&qnoc_driver); +} +module_exit(qnoc_driver_exit); + +MODULE_DESCRIPTION(" Qualcomm Eliza NoC driver"); +MODULE_LICENSE("GPL"); From a90863095f84f6d17c49716e4e2212d28a6b25b5 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Fri, 13 Mar 2026 10:21:33 +0000 Subject: [PATCH 173/405] MAINTAINERS: coresight: Add Leo Yan as Reviewer Leo has been an active contributor and reviewer for CoreSight subsystem for years. Add him as a Reviewer for CORESIGHT. Cc: Leo Yan Acked-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260313102133.2298330-1-suzuki.poulose@arm.com --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 9c5491001908..eaf928246aaf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2710,6 +2710,7 @@ ARM/CORESIGHT FRAMEWORK AND DRIVERS M: Suzuki K Poulose R: Mike Leach R: James Clark +R: Leo Yan L: coresight@lists.linaro.org (moderated for non-subscribers) L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained From af2f069b78950910ec7932a21544faad5ad8b13f Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Sat, 7 Mar 2026 11:15:56 +0400 Subject: [PATCH 174/405] iio: adc: max1363: Reformat enum and array initializers Reformat the device enum so each entry is on its own line and add a trailing comma to the final enumerator. Also reformat the nearby monitor speeds array for consistency. No functional change. Signed-off-by: Giorgi Tchankvetadze Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1363.c | 83 ++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 3ccc7b2d4f67..4d0b79cfeb27 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -635,48 +635,51 @@ static const enum max1363_modes max11644_mode_list[] = { static const struct iio_chan_spec max11646_channels[] = MAX1363_2X_CHANS(10); static const struct iio_chan_spec max11644_channels[] = MAX1363_2X_CHANS(12); -enum { max1361, - max1362, - max1363, - max1364, - max1036, - max1037, - max1038, - max1039, - max1136, - max1137, - max1138, - max1139, - max1236, - max1237, - max1238, - max1239, - max11600, - max11601, - max11602, - max11603, - max11604, - max11605, - max11606, - max11607, - max11608, - max11609, - max11610, - max11611, - max11612, - max11613, - max11614, - max11615, - max11616, - max11617, - max11644, - max11645, - max11646, - max11647 +enum { + max1361, + max1362, + max1363, + max1364, + max1036, + max1037, + max1038, + max1039, + max1136, + max1137, + max1138, + max1139, + max1236, + max1237, + max1238, + max1239, + max11600, + max11601, + max11602, + max11603, + max11604, + max11605, + max11606, + max11607, + max11608, + max11609, + max11610, + max11611, + max11612, + max11613, + max11614, + max11615, + max11616, + max11617, + max11644, + max11645, + max11646, + max11647, }; -static const int max1363_monitor_speeds[] = { 133000, 665000, 33300, 16600, - 8300, 4200, 2000, 1000 }; +static const int max1363_monitor_speeds[] = { + 133000, 665000, 33300, 16600, + 8300, 4200, 2000, 1000, +}; static ssize_t max1363_monitor_show_freq(struct device *dev, struct device_attribute *attr, From aac0a51b16700b403a55b67ba495de021db78763 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Thu, 5 Mar 2026 11:14:48 +0200 Subject: [PATCH 175/405] iio: frequency: admv1013: fix NULL pointer dereference on str MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When device_property_read_string() fails, str is left uninitialized but the code falls through to strcmp(str, ...), dereferencing a garbage pointer. Replace manual read/strcmp with device_property_match_property_string() and consolidate the SE mode enums into a single sequential enum, mapping to hardware register values via a switch consistent with other bitfields in the driver. Several cleanup patches have been applied to this driver recently so this will need a manual backport. Fixes: da35a7b526d9 ("iio: frequency: admv1013: add support for ADMV1013") Reviewed-by: Nuno Sá Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/admv1013.c | 65 ++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c index 9202443ef445..b852378b3f68 100644 --- a/drivers/iio/frequency/admv1013.c +++ b/drivers/iio/frequency/admv1013.c @@ -85,9 +85,9 @@ enum { }; enum { - ADMV1013_SE_MODE_POS = 6, - ADMV1013_SE_MODE_NEG = 9, - ADMV1013_SE_MODE_DIFF = 12 + ADMV1013_SE_MODE_POS, + ADMV1013_SE_MODE_NEG, + ADMV1013_SE_MODE_DIFF, }; struct admv1013_state { @@ -468,10 +468,23 @@ static int admv1013_init(struct admv1013_state *st, int vcm_uv) if (ret) return ret; - data = FIELD_PREP(ADMV1013_QUAD_SE_MODE_MSK, st->quad_se_mode); + switch (st->quad_se_mode) { + case ADMV1013_SE_MODE_POS: + data = 6; + break; + case ADMV1013_SE_MODE_NEG: + data = 9; + break; + case ADMV1013_SE_MODE_DIFF: + data = 12; + break; + default: + return -EINVAL; + } ret = __admv1013_spi_update_bits(st, ADMV1013_REG_QUAD, - ADMV1013_QUAD_SE_MODE_MSK, data); + ADMV1013_QUAD_SE_MODE_MSK, + FIELD_PREP(ADMV1013_QUAD_SE_MODE_MSK, data)); if (ret) return ret; @@ -512,37 +525,33 @@ static void admv1013_powerdown(void *data) admv1013_spi_update_bits(data, ADMV1013_REG_ENABLE, enable_reg_msk, enable_reg); } +static const char * const admv1013_input_modes[] = { + [ADMV1013_IQ_MODE] = "iq", + [ADMV1013_IF_MODE] = "if", +}; + +static const char * const admv1013_quad_se_modes[] = { + [ADMV1013_SE_MODE_POS] = "se-pos", + [ADMV1013_SE_MODE_NEG] = "se-neg", + [ADMV1013_SE_MODE_DIFF] = "diff", +}; + static int admv1013_properties_parse(struct admv1013_state *st) { int ret; - const char *str; struct device *dev = &st->spi->dev; st->det_en = device_property_read_bool(dev, "adi,detector-enable"); - ret = device_property_read_string(dev, "adi,input-mode", &str); - if (ret) - st->input_mode = ADMV1013_IQ_MODE; + ret = device_property_match_property_string(dev, "adi,input-mode", + admv1013_input_modes, + ARRAY_SIZE(admv1013_input_modes)); + st->input_mode = ret >= 0 ? ret : ADMV1013_IQ_MODE; - if (!strcmp(str, "iq")) - st->input_mode = ADMV1013_IQ_MODE; - else if (!strcmp(str, "if")) - st->input_mode = ADMV1013_IF_MODE; - else - return -EINVAL; - - ret = device_property_read_string(dev, "adi,quad-se-mode", &str); - if (ret) - st->quad_se_mode = ADMV1013_SE_MODE_DIFF; - - if (!strcmp(str, "diff")) - st->quad_se_mode = ADMV1013_SE_MODE_DIFF; - else if (!strcmp(str, "se-pos")) - st->quad_se_mode = ADMV1013_SE_MODE_POS; - else if (!strcmp(str, "se-neg")) - st->quad_se_mode = ADMV1013_SE_MODE_NEG; - else - return -EINVAL; + ret = device_property_match_property_string(dev, "adi,quad-se-mode", + admv1013_quad_se_modes, + ARRAY_SIZE(admv1013_quad_se_modes)); + st->quad_se_mode = ret >= 0 ? ret : ADMV1013_SE_MODE_DIFF; ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(admv1013_vcc_regs), From 1653e0897f1526acfb6d041d4920ebc6a3c3b028 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 4 Mar 2026 19:32:26 +0100 Subject: [PATCH 176/405] iio: light: acpi-als: Register ACPI notify handler directly To facilitate subsequent conversion of the driver to a platform one, make it install an ACPI notify handler directly instead of using a .notify() callback in struct acpi_driver. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/acpi-als.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index d5d1a8b9c035..2fe6b493c012 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -90,9 +90,9 @@ static int acpi_als_read_value(struct acpi_als *als, char *prop, s32 *val) return 0; } -static void acpi_als_notify(struct acpi_device *device, u32 event) +static void acpi_als_notify(acpi_handle handle, u32 event, void *data) { - struct iio_dev *indio_dev = acpi_driver_data(device); + struct iio_dev *indio_dev = data; struct acpi_als *als = iio_priv(indio_dev); if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev)) { @@ -102,7 +102,7 @@ static void acpi_als_notify(struct acpi_device *device, u32 event) break; default: /* Unhandled event */ - dev_dbg(&device->dev, + dev_dbg(&als->device->dev, "Unhandled ACPI ALS event (%08x)!\n", event); } @@ -218,7 +218,17 @@ static int acpi_als_add(struct acpi_device *device) if (ret) return ret; - return devm_iio_device_register(dev, indio_dev); + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ret; + + return acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_als_notify, indio_dev); +} + +static void acpi_als_remove(struct acpi_device *device) +{ + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, acpi_als_notify); } static const struct acpi_device_id acpi_als_device_ids[] = { @@ -234,7 +244,7 @@ static struct acpi_driver acpi_als_driver = { .ids = acpi_als_device_ids, .ops = { .add = acpi_als_add, - .notify = acpi_als_notify, + .remove = acpi_als_remove, }, }; From d4243cb08a272b6cc0f7c8293f9c7e128b51795c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 4 Mar 2026 19:33:14 +0100 Subject: [PATCH 177/405] iio: light: acpi-als: Convert ACPI driver to a platform one In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert the ACPI ambient light sensor driver to a platform one. After this change, the subordinate IIO device will be registered under the platform device used for driver binding instead of its ACPI companion. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/acpi-als.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index 2fe6b493c012..ab229318dce9 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -175,9 +176,10 @@ static irqreturn_t acpi_als_trigger_handler(int irq, void *p) return IRQ_HANDLED; } -static int acpi_als_add(struct acpi_device *device) +static int acpi_als_probe(struct platform_device *pdev) { - struct device *dev = &device->dev; + struct device *dev = &pdev->dev; + struct acpi_device *device = ACPI_COMPANION(dev); struct iio_dev *indio_dev; struct acpi_als *als; int ret; @@ -188,7 +190,6 @@ static int acpi_als_add(struct acpi_device *device) als = iio_priv(indio_dev); - device->driver_data = indio_dev; als->device = device; mutex_init(&als->lock); @@ -226,9 +227,10 @@ static int acpi_als_add(struct acpi_device *device) acpi_als_notify, indio_dev); } -static void acpi_als_remove(struct acpi_device *device) +static void acpi_als_remove(struct platform_device *pdev) { - acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, acpi_als_notify); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, acpi_als_notify); } static const struct acpi_device_id acpi_als_device_ids[] = { @@ -238,17 +240,15 @@ static const struct acpi_device_id acpi_als_device_ids[] = { MODULE_DEVICE_TABLE(acpi, acpi_als_device_ids); -static struct acpi_driver acpi_als_driver = { - .name = "acpi_als", - .class = ACPI_ALS_CLASS, - .ids = acpi_als_device_ids, - .ops = { - .add = acpi_als_add, - .remove = acpi_als_remove, +static struct platform_driver acpi_als_driver = { + .probe = acpi_als_probe, + .remove = acpi_als_remove, + .driver = { + .name = "acpi_als", + .acpi_match_table = acpi_als_device_ids, }, }; - -module_acpi_driver(acpi_als_driver); +module_platform_driver(acpi_als_driver); MODULE_AUTHOR("Zhang Rui "); MODULE_AUTHOR("Martin Liska "); From 8d8613036491f1057b1eeca4e4b2c41d8d465a63 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 3 Mar 2026 20:18:43 -0800 Subject: [PATCH 178/405] iio: adc: at91-sama5d2_adc: no devm for nvmem_cell_get MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is absolutely no reason to pospone cleanup of this post driver removal. Just do it immediately. Signed-off-by: Rosen Penev Reviewed-by: Nuno Sá Reviewed-by: Eugen Hristev Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 69bb49434f90..255970b2e747 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -2259,7 +2259,7 @@ static int at91_adc_temp_sensor_init(struct at91_adc_state *st, return 0; /* Get the calibration data from NVMEM. */ - temp_calib = devm_nvmem_cell_get(dev, "temperature_calib"); + temp_calib = nvmem_cell_get(dev, "temperature_calib"); if (IS_ERR(temp_calib)) { ret = PTR_ERR(temp_calib); if (ret != -ENOENT) @@ -2268,6 +2268,7 @@ static int at91_adc_temp_sensor_init(struct at91_adc_state *st, } buf = nvmem_cell_read(temp_calib, &len); + nvmem_cell_put(temp_calib); if (IS_ERR(buf)) { dev_err(dev, "Failed to read calibration data!\n"); return PTR_ERR(buf); From d6f2eac6440329d8c1d981b907b9ecd325ee3d2f Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 3 Mar 2026 20:17:16 -0800 Subject: [PATCH 179/405] iio: adc: meson: no devm for nvmem_cell_get There is no reason to extend the lifetime of this post removal of the driver when it's only needed in one spot. Moved tsc_regmap assignment to avoid two nvmem_cell_put calls. Signed-off-by: Rosen Penev Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/meson_saradc.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 47cd350498a0..ed91edf0e391 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -792,7 +792,7 @@ static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev) size_t read_len; int ret; - temperature_calib = devm_nvmem_cell_get(dev, "temperature_calib"); + temperature_calib = nvmem_cell_get(dev, "temperature_calib"); if (IS_ERR(temperature_calib)) { ret = PTR_ERR(temperature_calib); @@ -806,13 +806,9 @@ static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev) return dev_err_probe(dev, ret, "failed to get temperature_calib cell\n"); } - priv->tsc_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "amlogic,hhi-sysctrl"); - if (IS_ERR(priv->tsc_regmap)) - return dev_err_probe(dev, PTR_ERR(priv->tsc_regmap), - "failed to get amlogic,hhi-sysctrl regmap\n"); - read_len = MESON_SAR_ADC_EFUSE_BYTES; buf = nvmem_cell_read(temperature_calib, &read_len); + nvmem_cell_put(temperature_calib); if (IS_ERR(buf)) return dev_err_probe(dev, PTR_ERR(buf), "failed to read temperature_calib cell\n"); if (read_len != MESON_SAR_ADC_EFUSE_BYTES) { @@ -820,6 +816,11 @@ static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev) return dev_err_probe(dev, -EINVAL, "invalid read size of temperature_calib cell\n"); } + priv->tsc_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "amlogic,hhi-sysctrl"); + if (IS_ERR(priv->tsc_regmap)) + return dev_err_probe(dev, PTR_ERR(priv->tsc_regmap), + "failed to get amlogic,hhi-sysctrl regmap\n"); + trimming_bits = priv->param->temperature_trimming_bits; trimming_mask = BIT(trimming_bits) - 1; From 9a2e1233d38c460ad07f36901931f3674a32d1ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 3 Mar 2026 11:50:44 +0000 Subject: [PATCH 180/405] iio: buffer: hw-consumer: remove redundant scan_mask flexible array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scan_mask flexible array member in hw_consumer_buffer duplicates the scan_mask pointer already present in struct iio_buffer. Remove it and allocate the bitmap directly with bitmap_zalloc(), assigning it to buf->buffer.scan_mask. This simplifies the code and there's no need for the flex array allocation. Signed-off-by: Nuno Sá Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-hw-consumer.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c index cb771ef8eeb3..24d7df603760 100644 --- a/drivers/iio/buffer/industrialio-hw-consumer.c +++ b/drivers/iio/buffer/industrialio-hw-consumer.c @@ -28,7 +28,6 @@ struct hw_consumer_buffer { struct list_head head; struct iio_dev *indio_dev; struct iio_buffer buffer; - long scan_mask[]; }; static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer( @@ -52,7 +51,6 @@ static const struct iio_buffer_access_funcs iio_hw_buf_access = { static struct hw_consumer_buffer *iio_hw_consumer_get_buffer( struct iio_hw_consumer *hwc, struct iio_dev *indio_dev) { - unsigned int mask_longs = BITS_TO_LONGS(iio_get_masklength(indio_dev)); struct hw_consumer_buffer *buf; list_for_each_entry(buf, &hwc->buffers, head) { @@ -60,13 +58,18 @@ static struct hw_consumer_buffer *iio_hw_consumer_get_buffer( return buf; } - buf = kzalloc_flex(*buf, scan_mask, mask_longs); + buf = kzalloc_obj(*buf); if (!buf) return NULL; buf->buffer.access = &iio_hw_buf_access; buf->indio_dev = indio_dev; - buf->buffer.scan_mask = buf->scan_mask; + buf->buffer.scan_mask = bitmap_zalloc(iio_get_masklength(indio_dev), + GFP_KERNEL); + if (!buf->buffer.scan_mask) { + kfree(buf); + return NULL; + } iio_buffer_init(&buf->buffer); list_add_tail(&buf->head, &hwc->buffers); From c4e73728626e9930ecbb14b9cb2418d36feeefe4 Mon Sep 17 00:00:00 2001 From: Rajveer Chaudhari Date: Sat, 7 Mar 2026 17:19:11 +0530 Subject: [PATCH 181/405] iio: accel: adxl313: convert to guard(mutex) Replace manual mutex_lock/mutex_unlock pair with guard(mutex) in adxl313_read_axis(). This ensures the mutex is released on all return paths and allows returning directly without a goto label. Signed-off-by: Rajveer Chaudhari Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl313_core.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c index 9f5d4d2cb325..084037c89ad3 100644 --- a/drivers/iio/accel/adxl313_core.c +++ b/drivers/iio/accel/adxl313_core.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -356,19 +357,15 @@ static int adxl313_read_axis(struct adxl313_data *data, { int ret; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); ret = regmap_bulk_read(data->regmap, ADXL313_REG_DATA_AXIS(chan->address), &data->transf_buf, sizeof(data->transf_buf)); if (ret) - goto unlock_ret; + return ret; - ret = le16_to_cpu(data->transf_buf); - -unlock_ret: - mutex_unlock(&data->lock); - return ret; + return le16_to_cpu(data->transf_buf); } static int adxl313_read_freq_avail(struct iio_dev *indio_dev, From 594ca8ced1b380309f3a23e2a77d8846ec5fd025 Mon Sep 17 00:00:00 2001 From: Rajveer Chaudhari Date: Sat, 7 Mar 2026 17:19:12 +0530 Subject: [PATCH 182/405] iio: accel: adxl372: convert to guard(mutex) Replace manual mutex_lock/mutex_unlock pair with guard(mutex) in adxl372_write_threshold_value(). This ensures the mutex is released on all return paths and allows returning directly without a goto label. Signed-off-by: Rajveer Chaudhari Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl372.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 28a8793a53b6..6763f8ed9f7f 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -336,18 +337,14 @@ static ssize_t adxl372_write_threshold_value(struct iio_dev *indio_dev, unsigned struct adxl372_state *st = iio_priv(indio_dev); int ret; - mutex_lock(&st->threshold_m); + guard(mutex)(&st->threshold_m); + ret = regmap_write(st->regmap, addr, ADXL372_THRESH_VAL_H_SEL(threshold)); if (ret < 0) - goto unlock; + return ret; - ret = regmap_update_bits(st->regmap, addr + 1, GENMASK(7, 5), - ADXL372_THRESH_VAL_L_SEL(threshold) << 5); - -unlock: - mutex_unlock(&st->threshold_m); - - return ret; + return regmap_update_bits(st->regmap, addr + 1, GENMASK(7, 5), + ADXL372_THRESH_VAL_L_SEL(threshold) << 5); } static int adxl372_read_axis(struct adxl372_state *st, u8 addr) From c48012d519fe72fb82786d53930b2b907bf7c10c Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Sat, 7 Mar 2026 17:02:14 +0400 Subject: [PATCH 183/405] iio: adc: palmas_gpadc: Replace leading space indentation with tabs Fix lines starting with spaces instead of tabs to comply with the kernel coding style. No functional change. Signed-off-by: Giorgi Tchankvetadze Signed-off-by: Jonathan Cameron --- drivers/iio/adc/palmas_gpadc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index 3aea12e9d4fb..f777986a6aba 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -521,16 +521,16 @@ static int palmas_gpadc_get_low_threshold_raw(struct palmas_gpadc *adc, val = (val * 1000) / adc->adc_info[adc_chan].gain; - if (adc->adc_info[adc_chan].is_uncalibrated) { + if (adc->adc_info[adc_chan].is_uncalibrated) { /* 2% worse */ min_gain_error -= 20; min_offset_error = -36; - } else { + } else { val = (val * adc->adc_info[adc_chan].gain_error - adc->adc_info[adc_chan].offset) / 1000; min_offset_error = -2; - } + } return palmas_gpadc_threshold_with_tolerance(val, min_INL, From ff0843ceb1fb11a6b73e0e77b932ef7967aecd4b Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 7 Mar 2026 15:54:37 -0600 Subject: [PATCH 184/405] iio: adc: ti-ads7950: remove chip_info[] Remove the chip_info[] array and related enum used for looking up chip- specific information. Instead, use individual structs directly in the module device tables. Also update to use spi_get_device_match_data() in case the devicetree table is ever used instead of the SPI device ID table. Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7950.c | 172 +++++++++++++++++------------------ 1 file changed, 83 insertions(+), 89 deletions(-) diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index bbe1ce577789..fa3b446495ec 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -118,21 +118,6 @@ struct ti_ads7950_chip_info { unsigned int num_channels; }; -enum ti_ads7950_id { - TI_ADS7950, - TI_ADS7951, - TI_ADS7952, - TI_ADS7953, - TI_ADS7954, - TI_ADS7955, - TI_ADS7956, - TI_ADS7957, - TI_ADS7958, - TI_ADS7959, - TI_ADS7960, - TI_ADS7961, -}; - #define TI_ADS7950_V_CHAN(index, bits) \ { \ .type = IIO_VOLTAGE, \ @@ -225,55 +210,64 @@ static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7959, 8); static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7960, 8); static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7961, 8); -static const struct ti_ads7950_chip_info ti_ads7950_chip_info[] = { - [TI_ADS7950] = { - .channels = ti_ads7950_channels, - .num_channels = ARRAY_SIZE(ti_ads7950_channels), - }, - [TI_ADS7951] = { - .channels = ti_ads7951_channels, - .num_channels = ARRAY_SIZE(ti_ads7951_channels), - }, - [TI_ADS7952] = { - .channels = ti_ads7952_channels, - .num_channels = ARRAY_SIZE(ti_ads7952_channels), - }, - [TI_ADS7953] = { - .channels = ti_ads7953_channels, - .num_channels = ARRAY_SIZE(ti_ads7953_channels), - }, - [TI_ADS7954] = { - .channels = ti_ads7954_channels, - .num_channels = ARRAY_SIZE(ti_ads7954_channels), - }, - [TI_ADS7955] = { - .channels = ti_ads7955_channels, - .num_channels = ARRAY_SIZE(ti_ads7955_channels), - }, - [TI_ADS7956] = { - .channels = ti_ads7956_channels, - .num_channels = ARRAY_SIZE(ti_ads7956_channels), - }, - [TI_ADS7957] = { - .channels = ti_ads7957_channels, - .num_channels = ARRAY_SIZE(ti_ads7957_channels), - }, - [TI_ADS7958] = { - .channels = ti_ads7958_channels, - .num_channels = ARRAY_SIZE(ti_ads7958_channels), - }, - [TI_ADS7959] = { - .channels = ti_ads7959_channels, - .num_channels = ARRAY_SIZE(ti_ads7959_channels), - }, - [TI_ADS7960] = { - .channels = ti_ads7960_channels, - .num_channels = ARRAY_SIZE(ti_ads7960_channels), - }, - [TI_ADS7961] = { - .channels = ti_ads7961_channels, - .num_channels = ARRAY_SIZE(ti_ads7961_channels), - }, +static const struct ti_ads7950_chip_info ti_ads7950_chip_info = { + .channels = ti_ads7950_channels, + .num_channels = ARRAY_SIZE(ti_ads7950_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7951_chip_info = { + .channels = ti_ads7951_channels, + .num_channels = ARRAY_SIZE(ti_ads7951_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7952_chip_info = { + .channels = ti_ads7952_channels, + .num_channels = ARRAY_SIZE(ti_ads7952_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7953_chip_info = { + .channels = ti_ads7953_channels, + .num_channels = ARRAY_SIZE(ti_ads7953_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7954_chip_info = { + .channels = ti_ads7954_channels, + .num_channels = ARRAY_SIZE(ti_ads7954_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7955_chip_info = { + .channels = ti_ads7955_channels, + .num_channels = ARRAY_SIZE(ti_ads7955_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7956_chip_info = { + .channels = ti_ads7956_channels, + .num_channels = ARRAY_SIZE(ti_ads7956_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7957_chip_info = { + .channels = ti_ads7957_channels, + .num_channels = ARRAY_SIZE(ti_ads7957_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7958_chip_info = { + .channels = ti_ads7958_channels, + .num_channels = ARRAY_SIZE(ti_ads7958_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7959_chip_info = { + .channels = ti_ads7959_channels, + .num_channels = ARRAY_SIZE(ti_ads7959_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7960_chip_info = { + .channels = ti_ads7960_channels, + .num_channels = ARRAY_SIZE(ti_ads7960_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7961_chip_info = { + .channels = ti_ads7961_channels, + .num_channels = ARRAY_SIZE(ti_ads7961_channels), }; /* @@ -561,7 +555,7 @@ static int ti_ads7950_probe(struct spi_device *spi) st->spi = spi; - info = &ti_ads7950_chip_info[spi_get_device_id(spi)->driver_data]; + info = spi_get_device_match_data(spi); indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; @@ -683,35 +677,35 @@ static void ti_ads7950_remove(struct spi_device *spi) } static const struct spi_device_id ti_ads7950_id[] = { - { "ads7950", TI_ADS7950 }, - { "ads7951", TI_ADS7951 }, - { "ads7952", TI_ADS7952 }, - { "ads7953", TI_ADS7953 }, - { "ads7954", TI_ADS7954 }, - { "ads7955", TI_ADS7955 }, - { "ads7956", TI_ADS7956 }, - { "ads7957", TI_ADS7957 }, - { "ads7958", TI_ADS7958 }, - { "ads7959", TI_ADS7959 }, - { "ads7960", TI_ADS7960 }, - { "ads7961", TI_ADS7961 }, + { "ads7950", (kernel_ulong_t)&ti_ads7950_chip_info }, + { "ads7951", (kernel_ulong_t)&ti_ads7951_chip_info }, + { "ads7952", (kernel_ulong_t)&ti_ads7952_chip_info }, + { "ads7953", (kernel_ulong_t)&ti_ads7953_chip_info }, + { "ads7954", (kernel_ulong_t)&ti_ads7954_chip_info }, + { "ads7955", (kernel_ulong_t)&ti_ads7955_chip_info }, + { "ads7956", (kernel_ulong_t)&ti_ads7956_chip_info }, + { "ads7957", (kernel_ulong_t)&ti_ads7957_chip_info }, + { "ads7958", (kernel_ulong_t)&ti_ads7958_chip_info }, + { "ads7959", (kernel_ulong_t)&ti_ads7959_chip_info }, + { "ads7960", (kernel_ulong_t)&ti_ads7960_chip_info }, + { "ads7961", (kernel_ulong_t)&ti_ads7961_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ti_ads7950_id); static const struct of_device_id ads7950_of_table[] = { - { .compatible = "ti,ads7950", .data = &ti_ads7950_chip_info[TI_ADS7950] }, - { .compatible = "ti,ads7951", .data = &ti_ads7950_chip_info[TI_ADS7951] }, - { .compatible = "ti,ads7952", .data = &ti_ads7950_chip_info[TI_ADS7952] }, - { .compatible = "ti,ads7953", .data = &ti_ads7950_chip_info[TI_ADS7953] }, - { .compatible = "ti,ads7954", .data = &ti_ads7950_chip_info[TI_ADS7954] }, - { .compatible = "ti,ads7955", .data = &ti_ads7950_chip_info[TI_ADS7955] }, - { .compatible = "ti,ads7956", .data = &ti_ads7950_chip_info[TI_ADS7956] }, - { .compatible = "ti,ads7957", .data = &ti_ads7950_chip_info[TI_ADS7957] }, - { .compatible = "ti,ads7958", .data = &ti_ads7950_chip_info[TI_ADS7958] }, - { .compatible = "ti,ads7959", .data = &ti_ads7950_chip_info[TI_ADS7959] }, - { .compatible = "ti,ads7960", .data = &ti_ads7950_chip_info[TI_ADS7960] }, - { .compatible = "ti,ads7961", .data = &ti_ads7950_chip_info[TI_ADS7961] }, + { .compatible = "ti,ads7950", .data = &ti_ads7950_chip_info }, + { .compatible = "ti,ads7951", .data = &ti_ads7951_chip_info }, + { .compatible = "ti,ads7952", .data = &ti_ads7952_chip_info }, + { .compatible = "ti,ads7953", .data = &ti_ads7953_chip_info }, + { .compatible = "ti,ads7954", .data = &ti_ads7954_chip_info }, + { .compatible = "ti,ads7955", .data = &ti_ads7955_chip_info }, + { .compatible = "ti,ads7956", .data = &ti_ads7956_chip_info }, + { .compatible = "ti,ads7957", .data = &ti_ads7957_chip_info }, + { .compatible = "ti,ads7958", .data = &ti_ads7958_chip_info }, + { .compatible = "ti,ads7959", .data = &ti_ads7959_chip_info }, + { .compatible = "ti,ads7960", .data = &ti_ads7960_chip_info }, + { .compatible = "ti,ads7961", .data = &ti_ads7961_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ads7950_of_table); From 8c7440c686091a109802a720db25224dcc21485a Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Tue, 10 Mar 2026 18:38:10 +0400 Subject: [PATCH 185/405] iio: adc: mt6359-auxadc: Fix comma spacing Fix incorrect whitespace around comma on line 325 to comply with kernel coding style. This silences checkpatch errors "ERROR: space prohibited before that ','" and "ERROR: space required after that ','". No functional change. Signed-off-by: Giorgi Tchankvetadze Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mt6359-auxadc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/mt6359-auxadc.c b/drivers/iio/adc/mt6359-auxadc.c index f426a289e867..6b9ed9b1fde2 100644 --- a/drivers/iio/adc/mt6359-auxadc.c +++ b/drivers/iio/adc/mt6359-auxadc.c @@ -322,7 +322,7 @@ static const struct mtk_pmic_auxadc_chan mt6359_auxadc_ch_desc[] = { MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP1, 15, 128, 7, 2), MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, PMIC_AUXADC_IMP1, 15, 8, 5, 2), MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, PMIC_AUXADC_IMP1, 15, 8, 1, 1), - MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, PMIC_AUXADC_IMP1, 15 ,8, 1, 1), + MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, PMIC_AUXADC_IMP1, 15, 8, 1, 1), MTK_PMIC_ADC_CHAN(VDCXO, PMIC_AUXADC_RQST0, 6, PMIC_AUXADC_IMP1, 15, 8, 3, 2), MTK_PMIC_ADC_CHAN(TSX_TEMP, PMIC_AUXADC_RQST0, 7, PMIC_AUXADC_IMP1, 15, 128, 1, 1), MTK_PMIC_ADC_CHAN(HPOFS_CAL, PMIC_AUXADC_RQST0, 9, PMIC_AUXADC_IMP1, 15, 256, 1, 1), From bf3e24a9bff52303f5058c83cc6c7d7b2ef4958f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 5 Mar 2026 16:17:29 +0100 Subject: [PATCH 186/405] gpib: lpvo_usb: rename driver symbol prefix The LPVO driver apparently includes a more or less verbatim copy of the USB skeleton driver. Replace the "skel_" symbol prefix with "lpvo_" and rename the "usb_skel" struct "lpvo" to avoid symbol name clashes and make this a bit more palatable. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260305151729.10501-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c | 206 ++++++++++----------- 1 file changed, 101 insertions(+), 105 deletions(-) diff --git a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c index ee781d2f0b8e..389dde5a8481 100644 --- a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c +++ b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c @@ -51,10 +51,10 @@ MODULE_DESCRIPTION("GPIB driver for LPVO usb devices"); * */ -static const struct usb_device_id skel_table[] = { +static const struct usb_device_id lpvo_table[] = { { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE(usb, skel_table); +MODULE_DEVICE_TABLE(usb, lpvo_table); /* * *** Diagnostics and Debug *** @@ -182,15 +182,11 @@ static int usb_minors[MAX_DEV]; /* usb minors */ static int assigned_usb_minors; /* mask of filled slots */ static struct mutex minors_lock; /* operations on usb_minors are to be protected */ -/* - * usb-skeleton prototypes - */ - -struct usb_skel; -static ssize_t skel_do_write(struct usb_skel *, const char *, size_t); -static ssize_t skel_do_read(struct usb_skel *, char *, size_t); -static int skel_do_open(struct gpib_board *, int); -static int skel_do_release(struct gpib_board *); +struct lpvo; +static ssize_t lpvo_do_write(struct lpvo *, const char *, size_t); +static ssize_t lpvo_do_read(struct lpvo *, char *, size_t); +static int lpvo_do_open(struct gpib_board *, int); +static int lpvo_do_release(struct gpib_board *); /* * usec_diff : take difference in MICROsec between two 'timespec' @@ -218,7 +214,7 @@ static inline int usec_diff(struct timespec64 *a, struct timespec64 *b) static int write_loop(void *dev, char *msg, int leng) { - return skel_do_write(dev, msg, leng); + return lpvo_do_write(dev, msg, leng); } /** @@ -246,7 +242,7 @@ static int send_command(struct gpib_board *board, char *msg, int leng) if (retval < 0) return retval; - nchar = skel_do_read(GPIB_DEV, buffer, 64); + nchar = lpvo_do_read(GPIB_DEV, buffer, 64); if (nchar < 0) { dev_err(board->gpib_dev, " return from read: %d\n", nchar); @@ -310,7 +306,7 @@ static int one_char(struct gpib_board *board, struct char_buf *b) return b->inbuf[b->last - b->nchar--]; } ktime_get_real_ts64 (&before); - b->nchar = skel_do_read(GPIB_DEV, b->inbuf, INBUF_SIZE); + b->nchar = lpvo_do_read(GPIB_DEV, b->inbuf, INBUF_SIZE); b->last = b->nchar; ktime_get_real_ts64 (&after); @@ -445,12 +441,12 @@ static int usb_gpib_attach(struct gpib_board *board, const struct gpib_board_con if (!board->private_data) return -ENOMEM; - retval = skel_do_open(board, usb_minors[j]); + retval = lpvo_do_open(board, usb_minors[j]); - DIA_LOG(1, "Skel open: %d\n", retval); + DIA_LOG(1, "lpvo open: %d\n", retval); if (retval) { - dev_err(board->gpib_dev, "skel open failed.\n"); + dev_err(board->gpib_dev, "lpvo open failed.\n"); kfree(board->private_data); board->private_data = NULL; return -ENODEV; @@ -517,8 +513,8 @@ static void usb_gpib_detach(struct gpib_board *board) write_loop(GPIB_DEV, USB_GPIB_OFF, strlen(USB_GPIB_OFF)); msleep(100); DIA_LOG(1, "%s", "GPIB off\n"); - retval = skel_do_release(board); - DIA_LOG(1, "skel release -> %d\n", retval); + retval = lpvo_do_release(board); + DIA_LOG(1, "lpvo release -> %d\n", retval); } kfree(board->private_data); board->private_data = NULL; @@ -772,8 +768,8 @@ static int usb_gpib_read(struct gpib_board *board, if (retval < 0) return retval; - retval = skel_do_read(GPIB_DEV, inbuf, 1); - retval += skel_do_read(GPIB_DEV, inbuf + 1, 1); + retval = lpvo_do_read(GPIB_DEV, inbuf, 1); + retval += lpvo_do_read(GPIB_DEV, inbuf + 1, 1); ktime_get_real_ts64 (&after); @@ -1197,12 +1193,12 @@ static int write_latency_timer(struct usb_device *udev) * written by Greg Kroah-Hartman and available in the kernel tree. * * * * Functions skel_open() and skel_release() have been rewritten and named * - * skel_do_open() and skel_do_release() to process the attach and detach * + * lpvo_do_open() and lpvo_do_release() to process the attach and detach * * requests coming from gpib_config. * * * - * Functions skel_read() and skel_write() have been split into a * - * skel_do_read() and skel_do_write(), that cover the kernel stuff of read * - * and write operations, and the original skel_read() and skel_write(), * + * Functions lpvo_read() and lpvo_write() have been split into a * + * lpvo_do_read() and lpvo_do_write(), that cover the kernel stuff of read * + * and write operations, and the original lpvo_read() and lpvo_write(), * * that handle communication with user space and call their _do_ companion. * * * * Only the _do_ versions are used by the lpvo_usb_gpib driver; other ones * @@ -1230,7 +1226,7 @@ static int write_latency_timer(struct usb_device *udev) #include /* Get a minor range for your devices from the usb maintainer */ -#define USB_SKEL_MINOR_BASE 192 +#define USB_LPVO_MINOR_BASE 192 /* private defines */ @@ -1245,7 +1241,7 @@ static int write_latency_timer(struct usb_device *udev) #define USER_DEVICE 1 /* compile for device(s) in user space */ /* Structure to hold all of our device specific stuff */ -struct usb_skel { +struct lpvo { struct usb_device *udev; /* the usb device for this device */ struct usb_interface *interface; /* the interface for this device */ struct semaphore limit_sem; /* limiting the number of writes in progress */ @@ -1265,14 +1261,14 @@ struct usb_skel { wait_queue_head_t bulk_in_wait; /* to wait for an ongoing read */ }; -#define to_skel_dev(d) container_of(d, struct usb_skel, kref) +#define to_lpvo_dev(d) container_of(d, struct lpvo, kref) -static struct usb_driver skel_driver; -static void skel_draw_down(struct usb_skel *dev); +static struct usb_driver lpvo_driver; +static void lpvo_draw_down(struct lpvo *dev); -static void skel_delete(struct kref *kref) +static void lpvo_delete(struct kref *kref) { - struct usb_skel *dev = to_skel_dev(kref); + struct lpvo *dev = to_lpvo_dev(kref); usb_free_urb(dev->bulk_in_urb); usb_put_dev(dev->udev); @@ -1281,16 +1277,16 @@ static void skel_delete(struct kref *kref) } /* - * skel_do_open() - to be called by usb_gpib_attach + * lpvo_do_open() - to be called by usb_gpib_attach */ -static int skel_do_open(struct gpib_board *board, int subminor) +static int lpvo_do_open(struct gpib_board *board, int subminor) { - struct usb_skel *dev; + struct lpvo *dev; struct usb_interface *interface; int retval = 0; - interface = usb_find_interface(&skel_driver, subminor); + interface = usb_find_interface(&lpvo_driver, subminor); if (!interface) { dev_err(board->gpib_dev, "can't find device for minor %d\n", subminor); retval = -ENODEV; @@ -1318,12 +1314,12 @@ static int skel_do_open(struct gpib_board *board, int subminor) } /* - * skel_do_release() - to be called by usb_gpib_detach + * lpvo_do_release() - to be called by usb_gpib_detach */ -static int skel_do_release(struct gpib_board *board) +static int lpvo_do_release(struct gpib_board *board) { - struct usb_skel *dev; + struct lpvo *dev; dev = GPIB_DEV; if (!dev) @@ -1336,7 +1332,7 @@ static int skel_do_release(struct gpib_board *board) mutex_unlock(&dev->io_mutex); /* decrement the count on our device */ - kref_put(&dev->kref, skel_delete); + kref_put(&dev->kref, lpvo_delete); return 0; } @@ -1344,9 +1340,9 @@ static int skel_do_release(struct gpib_board *board) * read functions */ -static void skel_read_bulk_callback(struct urb *urb) +static void lpvo_read_bulk_callback(struct urb *urb) { - struct usb_skel *dev; + struct lpvo *dev; unsigned long flags; dev = urb->context; @@ -1370,7 +1366,7 @@ static void skel_read_bulk_callback(struct urb *urb) wake_up_interruptible(&dev->bulk_in_wait); } -static int skel_do_read_io(struct usb_skel *dev, size_t count) +static int lpvo_do_read_io(struct lpvo *dev, size_t count) { int rv; @@ -1381,7 +1377,7 @@ static int skel_do_read_io(struct usb_skel *dev, size_t count) dev->bulk_in_endpoint_addr), dev->bulk_in_buffer, min(dev->bulk_in_size, count), - skel_read_bulk_callback, + lpvo_read_bulk_callback, dev); /* tell everybody to leave the URB alone */ spin_lock_irq(&dev->err_lock); @@ -1406,10 +1402,10 @@ static int skel_do_read_io(struct usb_skel *dev, size_t count) } /* - * skel_do_read() - read operations from lpvo_usb_gpib + * lpvo_do_read() - read operations from lpvo_usb_gpib */ -static ssize_t skel_do_read(struct usb_skel *dev, char *buffer, size_t count) +static ssize_t lpvo_do_read(struct lpvo *dev, char *buffer, size_t count) { int rv; bool ongoing_io; @@ -1487,7 +1483,7 @@ static ssize_t skel_do_read(struct usb_skel *dev, char *buffer, size_t count) * it seems that requests for less than dev->bulk_in_size * are not accepted */ - rv = skel_do_read_io(dev, dev->bulk_in_size); + rv = lpvo_do_read_io(dev, dev->bulk_in_size); if (rv < 0) goto exit; else @@ -1534,10 +1530,10 @@ static ssize_t skel_do_read(struct usb_skel *dev, char *buffer, size_t count) * asked for by the lpvo_usb_gpib layer. */ // if (available < count) -// skel_do_read_io(dev, dev->bulk_in_size); +// lpvo_do_read_io(dev, dev->bulk_in_size); } else { /* no data in the buffer */ - rv = skel_do_read_io(dev, dev->bulk_in_size); + rv = lpvo_do_read_io(dev, dev->bulk_in_size); if (rv < 0) goto exit; else @@ -1557,9 +1553,9 @@ static ssize_t skel_do_read(struct usb_skel *dev, char *buffer, size_t count) * write functions */ -static void skel_write_bulk_callback(struct urb *urb) +static void lpvo_write_bulk_callback(struct urb *urb) { - struct usb_skel *dev; + struct lpvo *dev; unsigned long flags; dev = urb->context; @@ -1584,10 +1580,10 @@ static void skel_write_bulk_callback(struct urb *urb) } /* - * skel_do_write() - write operations from lpvo_usb_gpib + * lpvo_do_write() - write operations from lpvo_usb_gpib */ -static ssize_t skel_do_write(struct usb_skel *dev, const char *buffer, size_t count) +static ssize_t lpvo_do_write(struct lpvo *dev, const char *buffer, size_t count) { int retval = 0; struct urb *urb = NULL; @@ -1655,7 +1651,7 @@ static ssize_t skel_do_write(struct usb_skel *dev, const char *buffer, size_t co /* initialize the urb properly */ usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out_endpoint_addr), - buf, writesize, skel_write_bulk_callback, dev); + buf, writesize, lpvo_write_bulk_callback, dev); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; usb_anchor_urb(urb, &dev->submitted); @@ -1694,9 +1690,9 @@ static ssize_t skel_do_write(struct usb_skel *dev, const char *buffer, size_t co #if USER_DEVICE /* conditional compilation of user space device */ -static int skel_flush(struct file *file, fl_owner_t id) +static int lpvo_flush(struct file *file, fl_owner_t id) { - struct usb_skel *dev; + struct lpvo *dev; int res; dev = file->private_data; @@ -1705,7 +1701,7 @@ static int skel_flush(struct file *file, fl_owner_t id) /* wait for io to stop */ mutex_lock(&dev->io_mutex); - skel_draw_down(dev); + lpvo_draw_down(dev); /* read out errors, leave subsequent opens a clean slate */ spin_lock_irq(&dev->err_lock); @@ -1718,16 +1714,16 @@ static int skel_flush(struct file *file, fl_owner_t id) return res; } -static int skel_open(struct inode *inode, struct file *file) +static int lpvo_open(struct inode *inode, struct file *file) { - struct usb_skel *dev; + struct lpvo *dev; struct usb_interface *interface; int subminor; int retval = 0; subminor = iminor(inode); - interface = usb_find_interface(&skel_driver, subminor); + interface = usb_find_interface(&lpvo_driver, subminor); if (!interface) { pr_err("can't find device for minor %d\n", subminor); retval = -ENODEV; @@ -1754,9 +1750,9 @@ static int skel_open(struct inode *inode, struct file *file) return retval; } -static int skel_release(struct inode *inode, struct file *file) +static int lpvo_release(struct inode *inode, struct file *file) { - struct usb_skel *dev; + struct lpvo *dev; dev = file->private_data; if (!dev) @@ -1769,7 +1765,7 @@ static int skel_release(struct inode *inode, struct file *file) mutex_unlock(&dev->io_mutex); /* decrement the count on our device */ - kref_put(&dev->kref, skel_delete); + kref_put(&dev->kref, lpvo_delete); return 0; } @@ -1777,10 +1773,10 @@ static int skel_release(struct inode *inode, struct file *file) * user space access to read function */ -static ssize_t skel_read(struct file *file, char __user *buffer, size_t count, +static ssize_t lpvo_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { - struct usb_skel *dev; + struct lpvo *dev; char *buf; ssize_t rv; @@ -1790,7 +1786,7 @@ static ssize_t skel_read(struct file *file, char __user *buffer, size_t count, if (!buf) return -ENOMEM; - rv = skel_do_read(dev, buf, count); + rv = lpvo_do_read(dev, buf, count); if (rv > 0) { if (copy_to_user(buffer, buf, rv)) { @@ -1806,10 +1802,10 @@ static ssize_t skel_read(struct file *file, char __user *buffer, size_t count, * user space access to write function */ -static ssize_t skel_write(struct file *file, const char __user *user_buffer, +static ssize_t lpvo_write(struct file *file, const char __user *user_buffer, size_t count, loff_t *ppos) { - struct usb_skel *dev; + struct lpvo *dev; char *buf; ssize_t rv; @@ -1824,20 +1820,20 @@ static ssize_t skel_write(struct file *file, const char __user *user_buffer, return -EFAULT; } - rv = skel_do_write(dev, buf, count); + rv = lpvo_do_write(dev, buf, count); kfree(buf); return rv; } #endif -static const struct file_operations skel_fops = { +static const struct file_operations lpvo_fops = { .owner = THIS_MODULE, #if USER_DEVICE - .read = skel_read, - .write = skel_write, - .open = skel_open, - .release = skel_release, - .flush = skel_flush, + .read = lpvo_read, + .write = lpvo_write, + .open = lpvo_open, + .release = lpvo_release, + .flush = lpvo_flush, .llseek = noop_llseek, #endif }; @@ -1847,17 +1843,17 @@ static const struct file_operations skel_fops = { * and to have the device registered with the driver core */ #if USER_DEVICE -static struct usb_class_driver skel_class = { +static struct usb_class_driver lpvo_class = { .name = "lpvo_raw%d", - .fops = &skel_fops, - .minor_base = USB_SKEL_MINOR_BASE, + .fops = &lpvo_fops, + .minor_base = USB_LPVO_MINOR_BASE, }; #endif -static int skel_probe(struct usb_interface *interface, +static int lpvo_probe(struct usb_interface *interface, const struct usb_device_id *id) { - struct usb_skel *dev; + struct lpvo *dev; struct usb_endpoint_descriptor *bulk_in, *bulk_out; int retval; char *device_path; @@ -1916,7 +1912,7 @@ static int skel_probe(struct usb_interface *interface, #if USER_DEVICE /* we can register the device now, as it is ready */ - retval = usb_register_dev(interface, &skel_class); + retval = usb_register_dev(interface, &lpvo_class); if (retval) { /* something prevented us from registering this driver */ dev_err(&interface->dev, @@ -1934,14 +1930,14 @@ static int skel_probe(struct usb_interface *interface, error: /* this frees allocated memory */ - kref_put(&dev->kref, skel_delete); + kref_put(&dev->kref, lpvo_delete); return retval; } -static void skel_disconnect(struct usb_interface *interface) +static void lpvo_disconnect(struct usb_interface *interface) { - struct usb_skel *dev; + struct lpvo *dev; int minor = interface->minor; usb_gpib_exit_module(minor); /* first, disactivate the lpvo */ @@ -1951,7 +1947,7 @@ static void skel_disconnect(struct usb_interface *interface) #if USER_DEVICE /* give back our minor */ - usb_deregister_dev(interface, &skel_class); + usb_deregister_dev(interface, &lpvo_class); #endif /* prevent more I/O from starting */ @@ -1962,10 +1958,10 @@ static void skel_disconnect(struct usb_interface *interface) usb_kill_anchored_urbs(&dev->submitted); /* decrement our usage count */ - kref_put(&dev->kref, skel_delete); + kref_put(&dev->kref, lpvo_delete); } -static void skel_draw_down(struct usb_skel *dev) +static void lpvo_draw_down(struct lpvo *dev) { int time; @@ -1975,34 +1971,34 @@ static void skel_draw_down(struct usb_skel *dev) usb_kill_urb(dev->bulk_in_urb); } -static int skel_suspend(struct usb_interface *intf, pm_message_t message) +static int lpvo_suspend(struct usb_interface *intf, pm_message_t message) { - struct usb_skel *dev = usb_get_intfdata(intf); + struct lpvo *dev = usb_get_intfdata(intf); if (!dev) return 0; - skel_draw_down(dev); + lpvo_draw_down(dev); return 0; } -static int skel_resume(struct usb_interface *intf) +static int lpvo_resume(struct usb_interface *intf) { return 0; } -static int skel_pre_reset(struct usb_interface *intf) +static int lpvo_pre_reset(struct usb_interface *intf) { - struct usb_skel *dev = usb_get_intfdata(intf); + struct lpvo *dev = usb_get_intfdata(intf); mutex_lock(&dev->io_mutex); - skel_draw_down(dev); + lpvo_draw_down(dev); return 0; } -static int skel_post_reset(struct usb_interface *intf) +static int lpvo_post_reset(struct usb_interface *intf) { - struct usb_skel *dev = usb_get_intfdata(intf); + struct lpvo *dev = usb_get_intfdata(intf); /* we are sure no URBs are active - no locking needed */ dev->errors = -EPIPE; @@ -2011,16 +2007,16 @@ static int skel_post_reset(struct usb_interface *intf) return 0; } -static struct usb_driver skel_driver = { +static struct usb_driver lpvo_driver = { .name = NAME, - .probe = skel_probe, - .disconnect = skel_disconnect, - .suspend = skel_suspend, - .resume = skel_resume, - .pre_reset = skel_pre_reset, - .post_reset = skel_post_reset, - .id_table = skel_table, + .probe = lpvo_probe, + .disconnect = lpvo_disconnect, + .suspend = lpvo_suspend, + .resume = lpvo_resume, + .pre_reset = lpvo_pre_reset, + .post_reset = lpvo_post_reset, + .id_table = lpvo_table, .supports_autosuspend = 1, }; -module_usb_driver(skel_driver); +module_usb_driver(lpvo_driver); From 42df3a519ab6d355e2c43f1e30eb5414b60aeea5 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Fri, 30 Jan 2026 17:41:40 -0800 Subject: [PATCH 187/405] gpib: remove unnecessary module_init/exit functions Two GPIB drivers have unnecessary empty module_init and module_exit functions. Remove them. Note that if a module_init function exists, a module_exit function must also exist; otherwise, the module cannot be unloaded. Signed-off-by: Ethan Nelson-Moore Link: https://patch.msgid.link/20260131014152.35875-1-enelsonmoore@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/nec7210/nec7210.c | 12 ------------ drivers/gpib/tms9914/tms9914.c | 13 ------------- 2 files changed, 25 deletions(-) diff --git a/drivers/gpib/nec7210/nec7210.c b/drivers/gpib/nec7210/nec7210.c index bbf39367f5e4..f15d38dfa4cc 100644 --- a/drivers/gpib/nec7210/nec7210.c +++ b/drivers/gpib/nec7210/nec7210.c @@ -1107,15 +1107,3 @@ void nec7210_locking_iomem_write_byte(struct nec7210_priv *priv, u8 data, spin_unlock_irqrestore(&priv->register_page_lock, flags); } EXPORT_SYMBOL(nec7210_locking_iomem_write_byte); - -static int __init nec7210_init_module(void) -{ - return 0; -} - -static void __exit nec7210_exit_module(void) -{ -} - -module_init(nec7210_init_module); -module_exit(nec7210_exit_module); diff --git a/drivers/gpib/tms9914/tms9914.c b/drivers/gpib/tms9914/tms9914.c index 72a11596a35e..1411297e6217 100644 --- a/drivers/gpib/tms9914/tms9914.c +++ b/drivers/gpib/tms9914/tms9914.c @@ -899,16 +899,3 @@ void tms9914_iomem_write_byte(struct tms9914_priv *priv, u8 data, unsigned int r udelay(1); } EXPORT_SYMBOL_GPL(tms9914_iomem_write_byte); - -static int __init tms9914_init_module(void) -{ - return 0; -} - -static void __exit tms9914_exit_module(void) -{ -} - -module_init(tms9914_init_module); -module_exit(tms9914_exit_module); - From 04576813544d141b50e7e80e2f447b88235c61d7 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 3 Mar 2026 20:21:20 +0100 Subject: [PATCH 188/405] gpib: common: change gpib_class to a const struct The class_create() call has been deprecated in favor of class_register() as the driver core now allows for a struct class to be in read-only memory. Change gpib_class to be a const struct class and drop the class_create() call. Link: https://lore.kernel.org/all/2023040244-duffel-pushpin-f738@gregkh/ Suggested-by: Greg Kroah-Hartman Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260303192124.3855792-1-jkoolstra@xs4all.nl Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/common/gpib_os.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/gpib/common/gpib_os.c b/drivers/gpib/common/gpib_os.c index be757db993a5..0a8b981fc579 100644 --- a/drivers/gpib/common/gpib_os.c +++ b/drivers/gpib/common/gpib_os.c @@ -2169,10 +2169,13 @@ void init_gpib_status_queue(struct gpib_status_queue *device) device->dropped_byte = 0; } -static struct class *gpib_class; +static const struct class gpib_class = { + .name = "gpib_common", +}; static int __init gpib_common_init_module(void) { + int err; int i; pr_info("GPIB core driver\n"); @@ -2181,14 +2184,14 @@ static int __init gpib_common_init_module(void) pr_err("gpib: can't get major %d\n", GPIB_CODE); return -EIO; } - gpib_class = class_create("gpib_common"); - if (IS_ERR(gpib_class)) { + err = class_register(&gpib_class); + if (err) { pr_err("gpib: failed to create gpib class\n"); unregister_chrdev(GPIB_CODE, "gpib"); - return PTR_ERR(gpib_class); + return err; } for (i = 0; i < GPIB_MAX_NUM_BOARDS; ++i) - board_array[i].gpib_dev = device_create(gpib_class, NULL, + board_array[i].gpib_dev = device_create(&gpib_class, NULL, MKDEV(GPIB_CODE, i), NULL, "gpib%i", i); return 0; @@ -2199,9 +2202,9 @@ static void __exit gpib_common_exit_module(void) int i; for (i = 0; i < GPIB_MAX_NUM_BOARDS; ++i) - device_destroy(gpib_class, MKDEV(GPIB_CODE, i)); + device_destroy(&gpib_class, MKDEV(GPIB_CODE, i)); - class_destroy(gpib_class); + class_unregister(&gpib_class); unregister_chrdev(GPIB_CODE, "gpib"); } From d35da40ec364ff9e763e7fd3198ca14cf39d39bf Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 5 Mar 2026 11:27:44 +0100 Subject: [PATCH 189/405] gpib: agilent_82357a: drop redundant device reference Driver core holds a reference to the USB interface and its parent USB device while the interface is bound to a driver and there is no need to take additional references unless the structures are needed after disconnect. Drop the redundant device reference to reduce cargo culting, make it easier to spot drivers where an extra reference is needed, and reduce the risk of memory leaks when drivers fail to release it. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260305102745.12032-2-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/agilent_82357a/agilent_82357a.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/gpib/agilent_82357a/agilent_82357a.c b/drivers/gpib/agilent_82357a/agilent_82357a.c index e1349afbf933..770ba6eb40d1 100644 --- a/drivers/gpib/agilent_82357a/agilent_82357a.c +++ b/drivers/gpib/agilent_82357a/agilent_82357a.c @@ -1479,7 +1479,7 @@ static int agilent_82357a_driver_probe(struct usb_interface *interface, if (mutex_lock_interruptible(&agilent_82357a_hotplug_lock)) return -ERESTARTSYS; - usb_dev = usb_get_dev(interface_to_usbdev(interface)); + usb_dev = interface_to_usbdev(interface); for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) { if (!agilent_82357a_driver_interfaces[i]) { agilent_82357a_driver_interfaces[i] = interface; @@ -1490,14 +1490,12 @@ static int agilent_82357a_driver_probe(struct usb_interface *interface, } } if (i == MAX_NUM_82357A_INTERFACES) { - usb_put_dev(usb_dev); mutex_unlock(&agilent_82357a_hotplug_lock); dev_err(&usb_dev->dev, "out of space in agilent_82357a_driver_interfaces[]\n"); return -1; } path = kmalloc(path_length, GFP_KERNEL); if (!path) { - usb_put_dev(usb_dev); mutex_unlock(&agilent_82357a_hotplug_lock); return -ENOMEM; } @@ -1539,7 +1537,6 @@ static void agilent_82357a_driver_disconnect(struct usb_interface *interface) } if (i == MAX_NUM_82357A_INTERFACES) dev_err(&usb_dev->dev, "unable to find interface - bug?\n"); - usb_put_dev(usb_dev); mutex_unlock(&agilent_82357a_hotplug_lock); } From 678f946b40586a276421b2b4b6201665d6709119 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 5 Mar 2026 11:27:45 +0100 Subject: [PATCH 190/405] gpib: ni_usb: drop redundant device reference Driver core holds a reference to the USB interface and its parent USB device while the interface is bound to a driver and there is no need to take additional references unless the structures are needed after disconnect. Drop the redundant device reference to reduce cargo culting, make it easier to spot drivers where an extra reference is needed, and reduce the risk of memory leaks when drivers fail to release it. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260305102745.12032-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/ni_usb/ni_usb_gpib.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpib/ni_usb/ni_usb_gpib.c b/drivers/gpib/ni_usb/ni_usb_gpib.c index a24cd6521362..0bbc13ecebf9 100644 --- a/drivers/gpib/ni_usb/ni_usb_gpib.c +++ b/drivers/gpib/ni_usb/ni_usb_gpib.c @@ -2431,7 +2431,6 @@ static int ni_usb_driver_probe(struct usb_interface *interface, const struct usb static const int path_length = 1024; mutex_lock(&ni_usb_hotplug_lock); - usb_get_dev(usb_dev); for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) { if (!ni_usb_driver_interfaces[i]) { ni_usb_driver_interfaces[i] = interface; @@ -2440,14 +2439,12 @@ static int ni_usb_driver_probe(struct usb_interface *interface, const struct usb } } if (i == MAX_NUM_NI_USB_INTERFACES) { - usb_put_dev(usb_dev); mutex_unlock(&ni_usb_hotplug_lock); dev_err(&usb_dev->dev, "ni_usb_driver_interfaces[] full\n"); return -1; } path = kmalloc(path_length, GFP_KERNEL); if (!path) { - usb_put_dev(usb_dev); mutex_unlock(&ni_usb_hotplug_lock); return -ENOMEM; } @@ -2488,7 +2485,6 @@ static void ni_usb_driver_disconnect(struct usb_interface *interface) } if (i == MAX_NUM_NI_USB_INTERFACES) dev_err(&usb_dev->dev, "unable to find interface bug?\n"); - usb_put_dev(usb_dev); mutex_unlock(&ni_usb_hotplug_lock); } From 37a23d6f11938cd59927e3307b9b301624df8e8f Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 11 Mar 2026 21:59:21 -0700 Subject: [PATCH 191/405] bus: mhi: host: Use kzalloc_flex Change kzalloc + kzalloc to just kzalloc with a flexible array member. Add __counted_by for extra runtime analysis when requested. Move counting assignment immediately after allocation as required by __counted_by. Move mhi_buf definition as a complete definition as needed for flex arrays. It's not a pointer anymore. Signed-off-by: Rosen Penev [mani: squashed https://lore.kernel.org/mhi/20260317-mhi-invalid-free-mhi-buffers-v1-1-8418a3ad604f@oss.qualcomm.com] Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260312045921.7663-1-rosenp@gmail.com --- drivers/bus/mhi/host/boot.c | 22 +++------------------- include/linux/mhi.h | 34 +++++++++++++++++----------------- 2 files changed, 20 insertions(+), 36 deletions(-) diff --git a/drivers/bus/mhi/host/boot.c b/drivers/bus/mhi/host/boot.c index f16a1e67a667..19c84913cfb9 100644 --- a/drivers/bus/mhi/host/boot.c +++ b/drivers/bus/mhi/host/boot.c @@ -308,7 +308,6 @@ static void mhi_free_bhi_buffer(struct mhi_controller *mhi_cntrl, struct mhi_buf *mhi_buf = image_info->mhi_buf; dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len, mhi_buf->buf, mhi_buf->dma_addr); - kfree(image_info->mhi_buf); kfree(image_info); } @@ -322,7 +321,6 @@ void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl, dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len, mhi_buf->buf, mhi_buf->dma_addr); - kfree(image_info->mhi_buf); kfree(image_info); } @@ -333,15 +331,10 @@ static int mhi_alloc_bhi_buffer(struct mhi_controller *mhi_cntrl, struct image_info *img_info; struct mhi_buf *mhi_buf; - img_info = kzalloc_obj(*img_info); + img_info = kzalloc_flex(*img_info, mhi_buf, 1); if (!img_info) return -ENOMEM; - /* Allocate memory for entry */ - img_info->mhi_buf = kzalloc_obj(*img_info->mhi_buf); - if (!img_info->mhi_buf) - goto error_alloc_mhi_buf; - /* Allocate and populate vector table */ mhi_buf = img_info->mhi_buf; @@ -358,8 +351,6 @@ static int mhi_alloc_bhi_buffer(struct mhi_controller *mhi_cntrl, return 0; error_alloc_segment: - kfree(mhi_buf); -error_alloc_mhi_buf: kfree(img_info); return -ENOMEM; @@ -375,14 +366,11 @@ int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl, struct image_info *img_info; struct mhi_buf *mhi_buf; - img_info = kzalloc_obj(*img_info); + img_info = kzalloc_flex(*img_info, mhi_buf, segments); if (!img_info) return -ENOMEM; - /* Allocate memory for entries */ - img_info->mhi_buf = kzalloc_objs(*img_info->mhi_buf, segments); - if (!img_info->mhi_buf) - goto error_alloc_mhi_buf; + img_info->entries = segments; /* Allocate and populate vector table */ mhi_buf = img_info->mhi_buf; @@ -402,7 +390,6 @@ int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl, } img_info->bhi_vec = img_info->mhi_buf[segments - 1].buf; - img_info->entries = segments; *image_info = img_info; return 0; @@ -411,9 +398,6 @@ int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl, for (--i, --mhi_buf; i >= 0; i--, mhi_buf--) dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len, mhi_buf->buf, mhi_buf->dma_addr); - kfree(img_info->mhi_buf); - -error_alloc_mhi_buf: kfree(img_info); return -ENOMEM; diff --git a/include/linux/mhi.h b/include/linux/mhi.h index 88ccb3e14f48..fb3ba639f4f8 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -85,17 +85,33 @@ enum mhi_ch_type { MHI_CH_TYPE_INBOUND_COALESCED = 3, }; +/** + * struct mhi_buf - MHI Buffer description + * @buf: Virtual address of the buffer + * @name: Buffer label. For offload channel, configurations name must be: + * ECA - Event context array data + * CCA - Channel context array data + * @dma_addr: IOMMU address of the buffer + * @len: # of bytes + */ +struct mhi_buf { + void *buf; + const char *name; + dma_addr_t dma_addr; + size_t len; +}; + /** * struct image_info - Firmware and RDDM table * @mhi_buf: Buffer for firmware and RDDM table * @entries: # of entries in table */ struct image_info { - struct mhi_buf *mhi_buf; /* private: from internal.h */ struct bhi_vec_entry *bhi_vec; /* public: */ u32 entries; + struct mhi_buf mhi_buf[] __counted_by(entries); }; /** @@ -488,22 +504,6 @@ struct mhi_result { int transaction_status; }; -/** - * struct mhi_buf - MHI Buffer description - * @buf: Virtual address of the buffer - * @name: Buffer label. For offload channel, configurations name must be: - * ECA - Event context array data - * CCA - Channel context array data - * @dma_addr: IOMMU address of the buffer - * @len: # of bytes - */ -struct mhi_buf { - void *buf; - const char *name; - dma_addr_t dma_addr; - size_t len; -}; - /** * struct mhi_driver - Structure representing a MHI client driver * @probe: CB function for client driver probe function From f2d1643ddc0f3d0b847a6877ec37f1fabacfbfed Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 2 Mar 2026 11:20:21 +0530 Subject: [PATCH 192/405] bus: mhi: ep: Test for non-zero return value where applicable Instead of testing for negative error code, just test for non-zero return for cases where there is no positive return value. This helps to maintain code uniformity. No functional change. Reported-by: Bjorn Helgaas Closes: https://lore.kernel.org/linux-pci/20260227191510.GA3904799@bhelgaas Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260302055021.8616-1-manivannan.sadhasivam@oss.qualcomm.com --- drivers/bus/mhi/ep/main.c | 10 +++++----- drivers/bus/mhi/ep/ring.c | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index e3d0a3cbaf94..0277e1ab1198 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -367,7 +367,7 @@ static void mhi_ep_read_completion(struct mhi_ep_buf_info *buf_info) ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, MHI_TRE_DATA_GET_LEN(el), MHI_EV_CC_EOB); - if (ret < 0) { + if (ret) { dev_err(&mhi_chan->mhi_dev->dev, "Error sending transfer compl. event\n"); goto err_free_tre_buf; @@ -383,7 +383,7 @@ static void mhi_ep_read_completion(struct mhi_ep_buf_info *buf_info) ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, MHI_TRE_DATA_GET_LEN(el), MHI_EV_CC_EOT); - if (ret < 0) { + if (ret) { dev_err(&mhi_chan->mhi_dev->dev, "Error sending transfer compl. event\n"); goto err_free_tre_buf; @@ -449,7 +449,7 @@ static int mhi_ep_read_channel(struct mhi_ep_cntrl *mhi_cntrl, dev_dbg(dev, "Reading %zd bytes from channel (%u)\n", tr_len, ring->ch_id); ret = mhi_cntrl->read_async(mhi_cntrl, &buf_info); - if (ret < 0) { + if (ret) { dev_err(&mhi_chan->mhi_dev->dev, "Error reading from channel\n"); goto err_free_buf_addr; } @@ -494,7 +494,7 @@ static int mhi_ep_process_ch_ring(struct mhi_ep_ring *ring) } else { /* UL channel */ ret = mhi_ep_read_channel(mhi_cntrl, ring); - if (ret < 0) { + if (ret) { dev_err(&mhi_chan->mhi_dev->dev, "Failed to read channel\n"); return ret; } @@ -591,7 +591,7 @@ int mhi_ep_queue_skb(struct mhi_ep_device *mhi_dev, struct sk_buff *skb) dev_dbg(dev, "Writing %zd bytes to channel (%u)\n", tr_len, ring->ch_id); ret = mhi_cntrl->write_async(mhi_cntrl, &buf_info); - if (ret < 0) { + if (ret) { dev_err(dev, "Error writing to the channel\n"); goto err_exit; } diff --git a/drivers/bus/mhi/ep/ring.c b/drivers/bus/mhi/ep/ring.c index 9375b16ff2a5..405ce16c02a8 100644 --- a/drivers/bus/mhi/ep/ring.c +++ b/drivers/bus/mhi/ep/ring.c @@ -49,7 +49,7 @@ static int __mhi_ep_cache_ring(struct mhi_ep_ring *ring, size_t end) buf_info.dev_addr = &ring->ring_cache[start]; ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info); - if (ret < 0) + if (ret) return ret; } else { buf_info.size = (ring->ring_size - start) * sizeof(struct mhi_ring_element); @@ -57,7 +57,7 @@ static int __mhi_ep_cache_ring(struct mhi_ep_ring *ring, size_t end) buf_info.dev_addr = &ring->ring_cache[start]; ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info); - if (ret < 0) + if (ret) return ret; if (end) { @@ -66,7 +66,7 @@ static int __mhi_ep_cache_ring(struct mhi_ep_ring *ring, size_t end) buf_info.size = end * sizeof(struct mhi_ring_element); ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info); - if (ret < 0) + if (ret) return ret; } } From e07f3b8c9e1cc6aa5b48d63cf8270409ffe76068 Mon Sep 17 00:00:00 2001 From: Odelu Kukatla Date: Wed, 11 Mar 2026 16:05:46 +0530 Subject: [PATCH 193/405] dt-bindings: interconnect: qcom,qcs615-rpmh: add clocks property to enable QoS Aggre1-noc interconnect node on QCS615 has QoS registers located inside a block whose interface is clock-gated. Accessing these registers requires the corresponding clock(s) to be enabled. Update the bindings to include the 'clocks' property. Ensure that only aggre1-noc interconnect node uses this property by explicitly forbidding it for all other interconnect nodes. Signed-off-by: Odelu Kukatla Reviewed-by: Krzysztof Kozlowski Link: https://msgid.link/20260311103548.1823044-2-odelu.kukatla@oss.qualcomm.com Signed-off-by: Georgi Djakov --- .../interconnect/qcom,qcs615-rpmh.yaml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,qcs615-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,qcs615-rpmh.yaml index e06404828824..a9cd49bbe247 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,qcs615-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,qcs615-rpmh.yaml @@ -34,6 +34,13 @@ properties: reg: maxItems: 1 + clocks: + items: + - description: aggre UFS PHY AXI clock + - description: aggre USB2 SEC AXI clock + - description: aggre USB3 PRIM AXI clock + - description: RPMH CC IPA clock + required: - compatible @@ -53,6 +60,22 @@ allOf: required: - reg + - if: + properties: + compatible: + contains: + enum: + - qcom,qcs615-camnoc-virt + - qcom,qcs615-config-noc + - qcom,qcs615-dc-noc + - qcom,qcs615-gem-noc + - qcom,qcs615-mc-virt + - qcom,qcs615-mmss-noc + - qcom,qcs615-system-noc + then: + properties: + clocks: false + unevaluatedProperties: false examples: From 50a9b75d77611faa62e9a71ac66c345930b3d3f6 Mon Sep 17 00:00:00 2001 From: Odelu Kukatla Date: Wed, 11 Mar 2026 16:05:47 +0530 Subject: [PATCH 194/405] interconnect: qcom: qcs615: enable QoS configuration Enable QoS configuration for master ports with predefined priority and urgency forwarding. Signed-off-by: Odelu Kukatla Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Link: https://msgid.link/20260311103548.1823044-3-odelu.kukatla@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/qcs615.c | 247 +++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) diff --git a/drivers/interconnect/qcom/qcs615.c b/drivers/interconnect/qcom/qcs615.c index 797956eb6ff5..017a6017421f 100644 --- a/drivers/interconnect/qcom/qcs615.c +++ b/drivers/interconnect/qcom/qcs615.c @@ -142,6 +142,12 @@ static struct qcom_icc_node qhm_qdss_bam = { .name = "qhm_qdss_bam", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -150,6 +156,12 @@ static struct qcom_icc_node qhm_qspi = { .name = "qhm_qspi", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x17000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -158,6 +170,12 @@ static struct qcom_icc_node qhm_qup0 = { .name = "qhm_qup0", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x10000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -166,6 +184,12 @@ static struct qcom_icc_node qhm_qup1 = { .name = "qhm_qup1", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x12000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -174,6 +198,12 @@ static struct qcom_icc_node qnm_cnoc = { .name = "qnm_cnoc", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -182,6 +212,12 @@ static struct qcom_icc_node qxm_crypto = { .name = "qxm_crypto", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x5000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -190,6 +226,12 @@ static struct qcom_icc_node qxm_ipa = { .name = "qxm_ipa", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x6000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_lpass_snoc }, }; @@ -198,6 +240,12 @@ static struct qcom_icc_node xm_emac_avb = { .name = "xm_emac_avb", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -206,6 +254,12 @@ static struct qcom_icc_node xm_pcie = { .name = "xm_pcie", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x13000 }, + .prio = 0, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_pcie_snoc }, }; @@ -214,6 +268,12 @@ static struct qcom_icc_node xm_qdss_etr = { .name = "xm_qdss_etr", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -222,6 +282,12 @@ static struct qcom_icc_node xm_sdc1 = { .name = "xm_sdc1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -230,6 +296,12 @@ static struct qcom_icc_node xm_sdc2 = { .name = "xm_sdc2", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -238,6 +310,12 @@ static struct qcom_icc_node xm_ufs_mem = { .name = "xm_ufs_mem", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x11000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -246,6 +324,12 @@ static struct qcom_icc_node xm_usb2 = { .name = "xm_usb2", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -254,6 +338,12 @@ static struct qcom_icc_node xm_usb3_0 = { .name = "xm_usb3_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -356,6 +446,12 @@ static struct qcom_icc_node acm_apps = { .name = "acm_apps", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x2e000, 0x2e100 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_snoc, &qns_llcc, &qns_sys_pcie }, @@ -365,6 +461,12 @@ static struct qcom_icc_node acm_gpu_tcu = { .name = "acm_gpu_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x36000 }, + .prio = 6, + .urg_fwd = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_snoc, &qns_llcc }, }; @@ -373,6 +475,12 @@ static struct qcom_icc_node acm_sys_tcu = { .name = "acm_sys_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x37000 }, + .prio = 6, + .urg_fwd = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_snoc, &qns_llcc }, }; @@ -389,6 +497,12 @@ static struct qcom_icc_node qnm_gpu = { .name = "qnm_gpu", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x34000, 0x34080 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_snoc, &qns_llcc }, }; @@ -397,6 +511,12 @@ static struct qcom_icc_node qnm_mnoc_hf = { .name = "qnm_mnoc_hf", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2f000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_llcc }, }; @@ -405,6 +525,12 @@ static struct qcom_icc_node qnm_mnoc_sf = { .name = "qnm_mnoc_sf", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x35000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_snoc, &qns_llcc }, }; @@ -413,6 +539,12 @@ static struct qcom_icc_node qnm_snoc_gc = { .name = "qnm_snoc_gc", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x31000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_llcc }, }; @@ -421,6 +553,12 @@ static struct qcom_icc_node qnm_snoc_sf = { .name = "qnm_snoc_sf", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x30000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_llcc }, }; @@ -445,6 +583,12 @@ static struct qcom_icc_node qxm_camnoc_hf0 = { .name = "qxm_camnoc_hf0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -453,6 +597,12 @@ static struct qcom_icc_node qxm_camnoc_hf1 = { .name = "qxm_camnoc_hf1", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -461,6 +611,12 @@ static struct qcom_icc_node qxm_camnoc_sf = { .name = "qxm_camnoc_sf", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x9000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns2_mem_noc }, }; @@ -469,6 +625,12 @@ static struct qcom_icc_node qxm_mdp0 = { .name = "qxm_mdp0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -477,6 +639,12 @@ static struct qcom_icc_node qxm_rot = { .name = "qxm_rot", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns2_mem_noc }, }; @@ -485,6 +653,12 @@ static struct qcom_icc_node qxm_venus0 = { .name = "qxm_venus0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns2_mem_noc }, }; @@ -493,6 +667,12 @@ static struct qcom_icc_node qxm_venus_arm9 = { .name = "qxm_venus_arm9", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x11000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns2_mem_noc }, }; @@ -559,6 +739,12 @@ static struct qcom_icc_node qxm_pimem = { .name = "qxm_pimem", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_memnoc_gc, &qxs_imem }, }; @@ -567,6 +753,12 @@ static struct qcom_icc_node xm_gic = { .name = "xm_gic", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_memnoc_gc, &qxs_imem }, }; @@ -1213,11 +1405,21 @@ static struct qcom_icc_node * const aggre1_noc_nodes[] = { [SLAVE_SERVICE_A2NOC] = &srvc_aggre2_noc, }; +static const struct regmap_config qcs615_aggre1_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3f200, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs615_aggre1_noc = { + .config = &qcs615_aggre1_noc_regmap_config, .nodes = aggre1_noc_nodes, .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), .bcms = aggre1_noc_bcms, .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const camnoc_virt_bcms[] = { @@ -1289,7 +1491,16 @@ static struct qcom_icc_node * const config_noc_nodes[] = { [SLAVE_SERVICE_CNOC] = &srvc_cnoc, }; +static const struct regmap_config qcs615_config_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x5080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs615_config_noc = { + .config = &qcs615_config_noc_regmap_config, .nodes = config_noc_nodes, .num_nodes = ARRAY_SIZE(config_noc_nodes), .bcms = config_noc_bcms, @@ -1302,7 +1513,16 @@ static struct qcom_icc_node * const dc_noc_nodes[] = { [SLAVE_LLCC_CFG] = &qhs_llcc, }; +static const struct regmap_config qcs615_dc_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3200, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs615_dc_noc = { + .config = &qcs615_dc_noc_regmap_config, .nodes = dc_noc_nodes, .num_nodes = ARRAY_SIZE(dc_noc_nodes), }; @@ -1331,7 +1551,16 @@ static struct qcom_icc_node * const gem_noc_nodes[] = { [SLAVE_SERVICE_GEM_NOC] = &srvc_gemnoc, }; +static const struct regmap_config qcs615_gem_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3e200, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs615_gem_noc = { + .config = &qcs615_gem_noc_regmap_config, .nodes = gem_noc_nodes, .num_nodes = ARRAY_SIZE(gem_noc_nodes), .bcms = gem_noc_bcms, @@ -1376,7 +1605,16 @@ static struct qcom_icc_node * const mmss_noc_nodes[] = { [SLAVE_SERVICE_MNOC] = &srvc_mnoc, }; +static const struct regmap_config qcs615_mmss_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1c100, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs615_mmss_noc = { + .config = &qcs615_mmss_noc_regmap_config, .nodes = mmss_noc_nodes, .num_nodes = ARRAY_SIZE(mmss_noc_nodes), .bcms = mmss_noc_bcms, @@ -1418,7 +1656,16 @@ static struct qcom_icc_node * const system_noc_nodes[] = { [SLAVE_TCU] = &xs_sys_tcu_cfg, }; +static const struct regmap_config qcs615_system_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1f300, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs615_system_noc = { + .config = &qcs615_system_noc_regmap_config, .nodes = system_noc_nodes, .num_nodes = ARRAY_SIZE(system_noc_nodes), .bcms = system_noc_bcms, From 88cf8a9ad32f85012eaf4bd1fe70ab39635ce89c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 17 Mar 2026 16:20:30 +0100 Subject: [PATCH 195/405] mux: mmio: Zero the allocated memory Zero the allocated memory in probe() for fields and hardware states because: 1. The "hardware_states" array is not initialized in the probe, thus starting the device with uninitialized memory. This not a bug, because pointed memory will be assigned in suspend callback, however it is a discouraged coding practice. The "fields" array is initialized shortly further in the probe(). 2. Linux kernel convention for safer code encourages using zeroed allocations, as expressed in memory-allocation.rst document. Cc: Greg Kroah-Hartman Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260317152029.274829-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/mux/mmio.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/mux/mmio.c b/drivers/mux/mmio.c index 0611ef28bb69..b61e590f2ac9 100644 --- a/drivers/mux/mmio.c +++ b/drivers/mux/mmio.c @@ -100,12 +100,14 @@ static int mux_mmio_probe(struct platform_device *pdev) mux_mmio = mux_chip_priv(mux_chip); - mux_mmio->fields = devm_kmalloc(dev, num_fields * sizeof(*mux_mmio->fields), GFP_KERNEL); + mux_mmio->fields = devm_kcalloc(dev, num_fields, sizeof(*mux_mmio->fields), + GFP_KERNEL); if (!mux_mmio->fields) return -ENOMEM; - mux_mmio->hardware_states = devm_kmalloc(dev, num_fields * - sizeof(*mux_mmio->hardware_states), GFP_KERNEL); + mux_mmio->hardware_states = devm_kcalloc(dev, num_fields, + sizeof(*mux_mmio->hardware_states), + GFP_KERNEL); if (!mux_mmio->hardware_states) return -ENOMEM; From 4652fefcda3c604c83d1ae28ede94544e2142f06 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Sat, 15 Nov 2025 10:59:05 +0800 Subject: [PATCH 196/405] extcon: ptn5150: handle pending IRQ events during system resume When the system is suspended and ptn5150 wakeup interrupt is disabled, any changes on ptn5150 will only be record in interrupt status registers and won't fire an IRQ since its trigger type is falling edge. So the HW interrupt line will keep at low state and any further changes won't trigger IRQ anymore. To fix it, this will schedule a work to check whether any IRQ are pending and handle it accordingly. Fixes: 4ed754de2d66 ("extcon: Add support for ptn5150 extcon driver") Cc: stable@vger.kernel.org Reviewed-by: Krzysztof Kozlowski Acked-by: MyungJoo Ham Signed-off-by: Xu Yang Signed-off-by: Chanwoo Choi Link: https://lore.kernel.org/lkml/20251115025905.1395347-1-xu.yang_2@nxp.com/ --- drivers/extcon/extcon-ptn5150.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/extcon/extcon-ptn5150.c b/drivers/extcon/extcon-ptn5150.c index 78ad86c4a3be..31970fb34fcb 100644 --- a/drivers/extcon/extcon-ptn5150.c +++ b/drivers/extcon/extcon-ptn5150.c @@ -331,6 +331,19 @@ static int ptn5150_i2c_probe(struct i2c_client *i2c) return 0; } +static int ptn5150_resume(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct ptn5150_info *info = i2c_get_clientdata(i2c); + + /* Need to check possible pending interrupt events */ + schedule_work(&info->irq_work); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(ptn5150_pm_ops, NULL, ptn5150_resume); + static const struct of_device_id ptn5150_dt_match[] = { { .compatible = "nxp,ptn5150" }, { }, @@ -346,6 +359,7 @@ MODULE_DEVICE_TABLE(i2c, ptn5150_i2c_id); static struct i2c_driver ptn5150_i2c_driver = { .driver = { .name = "ptn5150", + .pm = pm_sleep_ptr(&ptn5150_pm_ops), .of_match_table = ptn5150_dt_match, }, .probe = ptn5150_i2c_probe, From 6a4d20fecc650d0aeeb668cd1be6aa704220e227 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Tue, 4 Nov 2025 12:01:05 +0100 Subject: [PATCH 197/405] extcon: int3496: replace use of system_wq with system_percpu_wq Currently if a user enqueue a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistentcy cannot be addressed without refactoring the API. This patch continues the effort to refactor worqueue APIs, which has begun with the change introducing new workqueues and a new alloc_workqueue flag: commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag") system_wq should be the per-cpu workqueue, yet in this name nothing makes that clear, so replace system_wq with system_percpu_wq. The old wq (system_wq) will be kept for a few release cycles. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Signed-off-by: Chanwoo Choi Link: https://lore.kernel.org/lkml/20251104110105.116858-1-marco.crivellari@suse.com/ --- drivers/extcon/extcon-intel-int3496.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/extcon/extcon-intel-int3496.c b/drivers/extcon/extcon-intel-int3496.c index ded1a85a5549..7d16d5b7d58f 100644 --- a/drivers/extcon/extcon-intel-int3496.c +++ b/drivers/extcon/extcon-intel-int3496.c @@ -106,7 +106,7 @@ static irqreturn_t int3496_thread_isr(int irq, void *priv) struct int3496_data *data = priv; /* Let the pin settle before processing it */ - mod_delayed_work(system_wq, &data->work, DEBOUNCE_TIME); + mod_delayed_work(system_percpu_wq, &data->work, DEBOUNCE_TIME); return IRQ_HANDLED; } @@ -181,7 +181,7 @@ static int int3496_probe(struct platform_device *pdev) } /* process id-pin so that we start with the right status */ - queue_delayed_work(system_wq, &data->work, 0); + queue_delayed_work(system_percpu_wq, &data->work, 0); flush_delayed_work(&data->work); platform_set_drvdata(pdev, data); From 8857f2495a2a37609ac925ab2fcd72104b190499 Mon Sep 17 00:00:00 2001 From: Michael Wu Date: Fri, 24 Oct 2025 10:49:46 +0800 Subject: [PATCH 198/405] extcon: Fixed sysfs duplicate filename issue With current extcon_dev_unregister() timing, ida_free is before device_unregister(), that may cause current id re-alloc to another device in extcon_dev_register() context but sysfs filename path not removal completed yet. The right timing shows below: on extcon_dev_register: ida_alloc() -> device_register() on extcon_dev_unregister: device_unregister() -> ida_free() stack information when an error occurs: sysfs: cannot create duplicate filename '/class/extcon/extcon1' Call trace: sysfs_warn_dup+0x68/0x88 sysfs_do_create_link_sd+0x94/0xdc sysfs_create_link+0x30/0x48 device_add_class_symlinks+0xb4/0x12c device_add+0x1e0/0x48c device_register+0x20/0x34 extcon_dev_register+0x3b8/0x5c4 Fixes: 7bba9e81a6fb ("extcon: Use unique number for the extcon device ID") Acked-by: MyungJoo Ham Reviewed-by: Andy Shevchenko Signed-off-by: Michael Wu Signed-off-by: Chanwoo Choi Link: https://lore.kernel.org/lkml/20251024024946.16618-1-michael@allwinnertech.com/ --- drivers/extcon/extcon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index d9e9815a5f96..98d85cc114a7 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -1366,10 +1366,10 @@ void extcon_dev_unregister(struct extcon_dev *edev) return; } - ida_free(&extcon_dev_ids, edev->id); - device_unregister(&edev->dev); + ida_free(&extcon_dev_ids, edev->id); + if (edev->mutually_exclusive && edev->max_supported) { for (index = 0; edev->mutually_exclusive[index]; index++) From 086d7f1e063b3f7bbc6c54bbbabf1fa842be289f Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Fri, 26 Sep 2025 10:53:07 +0800 Subject: [PATCH 199/405] dt-bindings: extcon: ptn5150: Allow "connector" node to present PTN5150 is usually used with a Type-C connector, so allow a "connector" node to be defined under it. Acked-by: Chanwoo Choi Acked-by: Rob Herring (Arm) Signed-off-by: Xu Yang Signed-off-by: Chanwoo Choi Link: https://lore.kernel.org/lkml/20250926025309.24267-1-xu.yang_2@nxp.com/ --- Documentation/devicetree/bindings/extcon/extcon-ptn5150.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/extcon/extcon-ptn5150.yaml b/Documentation/devicetree/bindings/extcon/extcon-ptn5150.yaml index 072b3c0c5fd0..79f88b5f4e5c 100644 --- a/Documentation/devicetree/bindings/extcon/extcon-ptn5150.yaml +++ b/Documentation/devicetree/bindings/extcon/extcon-ptn5150.yaml @@ -42,6 +42,9 @@ properties: description: A port node to link the usb controller for the dual role switch. + connector: + $ref: /schemas/connector/usb-connector.yaml# + required: - compatible - interrupts From 842546c56345eebc2396927df5b4e933d90de43a Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Fri, 26 Sep 2025 10:53:08 +0800 Subject: [PATCH 200/405] extcon: ptn5150: Add Type-C orientation switch support PTN5150 is able to detect CC polarity. The field[1:0] of CC status register (04H) will keep the result. 00: Cable Not Attached 01: CC1 is connected (normal orientation) 10: CC2 is connected (reversed orientation) 11: Reserved Add orientation switch support to correctly set orientation of multiplexer according to CC status. Acked-by: Chanwoo Choi Reviewed-by: Frank Li Signed-off-by: Xu Yang Signed-off-by: Chanwoo Choi Link: https://lore.kernel.org/lkml/20250926025309.24267-2-xu.yang_2@nxp.com/ --- drivers/extcon/Kconfig | 1 + drivers/extcon/extcon-ptn5150.c | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index aec46bf03302..68d9df7d2dae 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -158,6 +158,7 @@ config EXTCON_PTN5150 tristate "NXP PTN5150 CC LOGIC USB EXTCON support" depends on I2C && (GPIOLIB || COMPILE_TEST) depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH + depends on TYPEC || !TYPEC select REGMAP_I2C help Say Y here to enable support for USB peripheral and USB host diff --git a/drivers/extcon/extcon-ptn5150.c b/drivers/extcon/extcon-ptn5150.c index 31970fb34fcb..de753d00c4c2 100644 --- a/drivers/extcon/extcon-ptn5150.c +++ b/drivers/extcon/extcon-ptn5150.c @@ -18,6 +18,7 @@ #include #include #include +#include /* PTN5150 registers */ #define PTN5150_REG_DEVICE_ID 0x01 @@ -38,7 +39,11 @@ #define PTN5150_REG_DEVICE_ID_VERSION GENMASK(7, 3) #define PTN5150_REG_DEVICE_ID_VENDOR GENMASK(2, 0) +#define PTN5150_POLARITY_CC1 0x1 +#define PTN5150_POLARITY_CC2 0x2 + #define PTN5150_REG_CC_PORT_ATTACHMENT GENMASK(4, 2) +#define PTN5150_REG_CC_POLARITY GENMASK(1, 0) #define PTN5150_REG_CC_VBUS_DETECTION BIT(7) #define PTN5150_REG_INT_CABLE_ATTACH_MASK BIT(0) #define PTN5150_REG_INT_CABLE_DETACH_MASK BIT(1) @@ -53,6 +58,7 @@ struct ptn5150_info { int irq; struct work_struct irq_work; struct mutex mutex; + struct typec_switch *orient_sw; struct usb_role_switch *role_sw; }; @@ -71,6 +77,7 @@ static const struct regmap_config ptn5150_regmap_config = { static void ptn5150_check_state(struct ptn5150_info *info) { + enum typec_orientation orient = TYPEC_ORIENTATION_NONE; unsigned int port_status, reg_data, vbus; enum usb_role usb_role = USB_ROLE_NONE; int ret; @@ -81,6 +88,23 @@ static void ptn5150_check_state(struct ptn5150_info *info) return; } + orient = FIELD_GET(PTN5150_REG_CC_POLARITY, reg_data); + switch (orient) { + case PTN5150_POLARITY_CC1: + orient = TYPEC_ORIENTATION_NORMAL; + break; + case PTN5150_POLARITY_CC2: + orient = TYPEC_ORIENTATION_REVERSE; + break; + default: + orient = TYPEC_ORIENTATION_NONE; + break; + } + + ret = typec_switch_set(info->orient_sw, orient); + if (ret) + dev_err(info->dev, "failed to set orientation: %d\n", ret); + port_status = FIELD_GET(PTN5150_REG_CC_PORT_ATTACHMENT, reg_data); switch (port_status) { @@ -152,6 +176,12 @@ static void ptn5150_irq_work(struct work_struct *work) dev_err(info->dev, "failed to set none role: %d\n", ret); + + ret = typec_switch_set(info->orient_sw, + TYPEC_ORIENTATION_NONE); + if (ret) + dev_err(info->dev, + "failed to set orientation: %d\n", ret); } } @@ -219,12 +249,14 @@ static void ptn5150_work_sync_and_put(void *data) cancel_work_sync(&info->irq_work); usb_role_switch_put(info->role_sw); + typec_switch_put(info->orient_sw); } static int ptn5150_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct device_node *np = i2c->dev.of_node; + struct fwnode_handle *connector; struct ptn5150_info *info; int ret; @@ -311,6 +343,14 @@ static int ptn5150_i2c_probe(struct i2c_client *i2c) if (ret) return -EINVAL; + connector = device_get_named_child_node(dev, "connector"); + if (connector) { + info->orient_sw = fwnode_typec_switch_get(connector); + if (IS_ERR(info->orient_sw)) + return dev_err_probe(info->dev, PTR_ERR(info->orient_sw), + "failed to get orientation switch\n"); + } + info->role_sw = usb_role_switch_get(info->dev); if (IS_ERR(info->role_sw)) return dev_err_probe(info->dev, PTR_ERR(info->role_sw), From 9c98fdec70ec15c46610464366d414df1d6a0bee Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Fri, 26 Sep 2025 10:53:09 +0800 Subject: [PATCH 201/405] extcon: ptn5150: Support USB role switch via connector fwnode Since the PTN5150 is a Type-C chip, it's common to describe related properties under the connector node. To align with this, the port node will be located under the connector node in the future. To support this layout, retrieve the USB role switch using the connector's fwnode. For compatibility with existing device trees, keep the usb_role_switch_get() function. Acked-by: Chanwoo Choi Reviewed-by: Frank Li Signed-off-by: Xu Yang Signed-off-by: Chanwoo Choi Link: https://lore.kernel.org/lkml/20250926025309.24267-3-xu.yang_2@nxp.com/ --- drivers/extcon/extcon-ptn5150.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/extcon/extcon-ptn5150.c b/drivers/extcon/extcon-ptn5150.c index de753d00c4c2..eca1b140aeb0 100644 --- a/drivers/extcon/extcon-ptn5150.c +++ b/drivers/extcon/extcon-ptn5150.c @@ -352,6 +352,8 @@ static int ptn5150_i2c_probe(struct i2c_client *i2c) } info->role_sw = usb_role_switch_get(info->dev); + if (!info->role_sw && connector) + info->role_sw = fwnode_usb_role_switch_get(connector); if (IS_ERR(info->role_sw)) return dev_err_probe(info->dev, PTR_ERR(info->role_sw), "failed to get role switch\n"); From 1bf0ba46d9d2c784120fd9cb235c08add3a6e7be Mon Sep 17 00:00:00 2001 From: Yannis Bolliger Date: Fri, 17 Oct 2025 19:30:01 +0000 Subject: [PATCH 202/405] extcon: usbc-tusb320: Make typec-power-opmode optional The driver returned an error in the probe function when a usb c connector is configured in the DT without a "typec-power-opmode" property. This property is used to initialize the CURRENT_MODE_ADVERTISE register of the TUSB320, which is unused when operating as a UFP. Requiring this property causes unnecessary configuration overhead and inconsistency with the USB connector DT bindings, which do not specify it as required. This change makes typec-power-opmode optional. When the property is not present, the driver will skip programming the CURRENT_MODE_ADVERTISE register and rely on the hardware default. Signed-off-by: Yannis Bolliger Signed-off-by: Chanwoo Choi Link: https://lore.kernel.org/lkml/aPKZJ6WTZlhSOyST@yaene-desktop/ --- drivers/extcon/extcon-usbc-tusb320.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/extcon/extcon-usbc-tusb320.c b/drivers/extcon/extcon-usbc-tusb320.c index 2eab341de6b7..920b03421850 100644 --- a/drivers/extcon/extcon-usbc-tusb320.c +++ b/drivers/extcon/extcon-usbc-tusb320.c @@ -454,20 +454,18 @@ static int tusb320_typec_probe(struct i2c_client *client, priv->port_type = priv->cap.type; /* This goes into register 0x8 field CURRENT_MODE_ADVERTISE */ - ret = fwnode_property_read_string(connector, "typec-power-opmode", &cap_str); - if (ret) - goto err_put; + if (!fwnode_property_read_string(connector, "typec-power-opmode", + &cap_str)) { + ret = typec_find_pwr_opmode(cap_str); + if (ret < 0) + goto err_put; + priv->pwr_opmode = ret; - ret = typec_find_pwr_opmode(cap_str); - if (ret < 0) - goto err_put; - - priv->pwr_opmode = ret; - - /* Initialize the hardware with the devicetree settings. */ - ret = tusb320_set_adv_pwr_mode(priv); - if (ret) - goto err_put; + /* Initialize the hardware with the devicetree settings. */ + ret = tusb320_set_adv_pwr_mode(priv); + if (ret) + goto err_put; + } priv->cap.revision = USB_TYPEC_REV_1_1; priv->cap.accessory[0] = TYPEC_ACCESSORY_AUDIO; From 23d742859a2dd20b1e96ab0828fa26227c47f328 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Sat, 21 Mar 2026 12:04:56 +0200 Subject: [PATCH 203/405] iio: accel: adxl372: introduce chip_info structure Introduce a chip_info structure to parameterize device-specific properties such as ODR/bandwidth frequency tables, activity/inactivity timer scale factors, and the maximum ODR value. This refactors the driver to use chip_info lookups instead of hardcoded values, preparing the driver to support multiple device variants. The sampling_frequency and filter_low_pass_3db_frequency available attributes are switched from custom sysfs callbacks to read_avail() based handling via info_mask_shared_by_type_available. This enforces consistent formatting through the IIO framework and makes the values accessible to in-kernel consumers. The SPI/I2C probe functions are updated to pass a chip_info pointer instead of a device name string. No functional change intended. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl372.c | 151 ++++++++++++++++++-------------- drivers/iio/accel/adxl372.h | 16 +++- drivers/iio/accel/adxl372_i2c.c | 10 ++- drivers/iio/accel/adxl372_spi.c | 10 ++- 4 files changed, 112 insertions(+), 75 deletions(-) diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 6763f8ed9f7f..1592b25ecb2c 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -181,6 +181,7 @@ enum adxl372_odr { ADXL372_ODR_1600HZ, ADXL372_ODR_3200HZ, ADXL372_ODR_6400HZ, + ADXL372_ODR_NUM }; enum adxl372_bandwidth { @@ -215,14 +216,35 @@ enum adxl372_fifo_mode { ADXL372_FIFO_OLD_SAVED }; -static const int adxl372_samp_freq_tbl[5] = { - 400, 800, 1600, 3200, 6400, +static const int adxl372_samp_freq_tbl[ADXL372_ODR_NUM] = { + [ADXL372_ODR_400HZ] = 400, + [ADXL372_ODR_800HZ] = 800, + [ADXL372_ODR_1600HZ] = 1600, + [ADXL372_ODR_3200HZ] = 3200, + [ADXL372_ODR_6400HZ] = 6400, }; -static const int adxl372_bw_freq_tbl[5] = { - 200, 400, 800, 1600, 3200, +static const int adxl372_bw_freq_tbl[ADXL372_ODR_NUM] = { + [ADXL372_BW_200HZ] = 200, + [ADXL372_BW_400HZ] = 400, + [ADXL372_BW_800HZ] = 800, + [ADXL372_BW_1600HZ] = 1600, + [ADXL372_BW_3200HZ] = 3200, }; +const struct adxl372_chip_info adxl372_chip_info = { + .name = "adxl372", + .samp_freq_tbl = adxl372_samp_freq_tbl, + .bw_freq_tbl = adxl372_bw_freq_tbl, + .num_freqs = ARRAY_SIZE(adxl372_samp_freq_tbl), + .act_time_scale_us = 3300, + .act_time_scale_low_us = 6600, + .inact_time_scale_ms = 13, + .inact_time_scale_low_ms = 26, + .max_odr = ADXL372_ODR_6400HZ, +}; +EXPORT_SYMBOL_NS_GPL(adxl372_chip_info, "IIO_ADXL372"); + struct adxl372_axis_lookup { unsigned int bits; enum adxl372_fifo_format fifo_format; @@ -258,8 +280,12 @@ static const struct iio_event_spec adxl372_events[] = { .modified = 1, \ .channel2 = IIO_MOD_##axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + .info_mask_shared_by_type = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ .scan_index = index, \ .scan_type = { \ @@ -280,6 +306,7 @@ static const struct iio_chan_spec adxl372_channels[] = { }; struct adxl372_state { + const struct adxl372_chip_info *chip_info; int irq; struct device *dev; struct regmap *regmap; @@ -468,13 +495,14 @@ static int adxl372_set_activity_time_ms(struct adxl372_state *st, int ret; /* - * 3.3 ms per code is the scale factor of the TIME_ACT register for - * ODR = 6400 Hz. It is 6.6 ms per code for ODR = 3200 Hz and below. + * The scale factor of the TIME_ACT register depends on the ODR. + * A higher scale factor is used at the maximum ODR and a lower + * one at all other rates. */ - if (st->odr == ADXL372_ODR_6400HZ) - scale_factor = 3300; + if (st->odr == st->chip_info->max_odr) + scale_factor = st->chip_info->act_time_scale_us; else - scale_factor = 6600; + scale_factor = st->chip_info->act_time_scale_low_us; reg_val = DIV_ROUND_CLOSEST(act_time_ms * 1000, scale_factor); @@ -498,13 +526,14 @@ static int adxl372_set_inactivity_time_ms(struct adxl372_state *st, int ret; /* - * 13 ms per code is the scale factor of the TIME_INACT register for - * ODR = 6400 Hz. It is 26 ms per code for ODR = 3200 Hz and below. + * The scale factor of the TIME_INACT register depends on the ODR. + * A higher scale factor is used at the maximum ODR and a lower + * one at all other rates. */ - if (st->odr == ADXL372_ODR_6400HZ) - scale_factor = 13; + if (st->odr == st->chip_info->max_odr) + scale_factor = st->chip_info->inact_time_scale_ms; else - scale_factor = 26; + scale_factor = st->chip_info->inact_time_scale_low_ms; res = DIV_ROUND_CLOSEST(inact_time_ms, scale_factor); reg_val_h = (res >> 8) & 0xFF; @@ -714,7 +743,7 @@ static int adxl372_setup(struct adxl372_state *st) if (ret < 0) return ret; - ret = adxl372_set_odr(st, ADXL372_ODR_6400HZ); + ret = adxl372_set_odr(st, st->chip_info->max_odr); if (ret < 0) return ret; @@ -774,10 +803,10 @@ static int adxl372_read_raw(struct iio_dev *indio_dev, *val2 = ADXL372_USCALE; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SAMP_FREQ: - *val = adxl372_samp_freq_tbl[st->odr]; + *val = st->chip_info->samp_freq_tbl[st->odr]; return IIO_VAL_INT; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: - *val = adxl372_bw_freq_tbl[st->bw]; + *val = st->chip_info->bw_freq_tbl[st->bw]; return IIO_VAL_INT; } @@ -793,23 +822,17 @@ static int adxl372_write_raw(struct iio_dev *indio_dev, switch (info) { case IIO_CHAN_INFO_SAMP_FREQ: - odr_index = adxl372_find_closest_match(adxl372_samp_freq_tbl, - ARRAY_SIZE(adxl372_samp_freq_tbl), - val); + odr_index = adxl372_find_closest_match(st->chip_info->samp_freq_tbl, + st->chip_info->num_freqs, + val); ret = adxl372_set_odr(st, odr_index); if (ret < 0) return ret; - /* - * The timer period depends on the ODR selected. - * At 3200 Hz and below, it is 6.6 ms; at 6400 Hz, it is 3.3 ms - */ + /* Recalculate activity time as the timer period depends on ODR */ ret = adxl372_set_activity_time_ms(st, st->act_time_ms); if (ret < 0) return ret; - /* - * The timer period depends on the ODR selected. - * At 3200 Hz and below, it is 26 ms; at 6400 Hz, it is 13 ms - */ + /* Recalculate inactivity time as the timer period depends on ODR */ ret = adxl372_set_inactivity_time_ms(st, st->inact_time_ms); if (ret < 0) return ret; @@ -822,9 +845,9 @@ static int adxl372_write_raw(struct iio_dev *indio_dev, return ret; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: - bw_index = adxl372_find_closest_match(adxl372_bw_freq_tbl, - ARRAY_SIZE(adxl372_bw_freq_tbl), - val); + bw_index = adxl372_find_closest_match(st->chip_info->bw_freq_tbl, + st->chip_info->num_freqs, + val); return adxl372_set_bandwidth(st, bw_index); default: return -EINVAL; @@ -954,24 +977,6 @@ static int adxl372_write_event_config(struct iio_dev *indio_dev, const struct ii return adxl372_set_interrupts(st, st->int1_bitmask, 0); } -static ssize_t adxl372_show_filter_freq_avail(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct adxl372_state *st = iio_priv(indio_dev); - int i; - size_t len = 0; - - for (i = 0; i <= st->odr; i++) - len += scnprintf(buf + len, PAGE_SIZE - len, - "%d ", adxl372_bw_freq_tbl[i]); - - buf[len - 1] = '\n'; - - return len; -} - static ssize_t adxl372_get_fifo_enabled(struct device *dev, struct device_attribute *attr, char *buf) @@ -1139,25 +1144,38 @@ static const struct iio_trigger_ops adxl372_peak_data_trigger_ops = { .set_trigger_state = adxl372_peak_dready_trig_set_state, }; -static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("400 800 1600 3200 6400"); -static IIO_DEVICE_ATTR(in_accel_filter_low_pass_3db_frequency_available, - 0444, adxl372_show_filter_freq_avail, NULL, 0); +static int adxl372_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct adxl372_state *st = iio_priv(indio_dev); -static struct attribute *adxl372_attributes[] = { - &iio_const_attr_sampling_frequency_available.dev_attr.attr, - &iio_dev_attr_in_accel_filter_low_pass_3db_frequency_available.dev_attr.attr, - NULL, -}; - -static const struct attribute_group adxl372_attrs_group = { - .attrs = adxl372_attributes, -}; + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = st->chip_info->samp_freq_tbl; + *type = IIO_VAL_INT; + *length = st->chip_info->num_freqs; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *vals = st->chip_info->bw_freq_tbl; + *type = IIO_VAL_INT; + /* + * Bandwidth cannot exceed half the sampling frequency + * (Nyquist), so limit available values based on current ODR. + */ + *length = st->odr + 1; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} static const struct iio_info adxl372_info = { .validate_trigger = &adxl372_validate_trigger, - .attrs = &adxl372_attrs_group, .read_raw = adxl372_read_raw, .write_raw = adxl372_write_raw, + .read_avail = adxl372_read_avail, .read_event_config = adxl372_read_event_config, .write_event_config = adxl372_write_event_config, .read_event_value = adxl372_read_event_value, @@ -1173,7 +1191,7 @@ bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg) EXPORT_SYMBOL_NS_GPL(adxl372_readable_noinc_reg, "IIO_ADXL372"); int adxl372_probe(struct device *dev, struct regmap *regmap, - int irq, const char *name) + int irq, const struct adxl372_chip_info *chip_info) { struct iio_dev *indio_dev; struct adxl372_state *st; @@ -1189,13 +1207,14 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, st->dev = dev; st->regmap = regmap; st->irq = irq; + st->chip_info = chip_info; mutex_init(&st->threshold_m); indio_dev->channels = adxl372_channels; indio_dev->num_channels = ARRAY_SIZE(adxl372_channels); indio_dev->available_scan_masks = adxl372_channel_masks; - indio_dev->name = name; + indio_dev->name = chip_info->name; indio_dev->info = &adxl372_info; indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; diff --git a/drivers/iio/accel/adxl372.h b/drivers/iio/accel/adxl372.h index 80a0aa9714fc..3ce06609446c 100644 --- a/drivers/iio/accel/adxl372.h +++ b/drivers/iio/accel/adxl372.h @@ -10,8 +10,22 @@ #define ADXL372_REVID 0x03 +struct adxl372_chip_info { + const char *name; + const int *samp_freq_tbl; + const int *bw_freq_tbl; + unsigned int num_freqs; + unsigned int act_time_scale_us; + unsigned int act_time_scale_low_us; + unsigned int inact_time_scale_ms; + unsigned int inact_time_scale_low_ms; + unsigned int max_odr; +}; + +extern const struct adxl372_chip_info adxl372_chip_info; + int adxl372_probe(struct device *dev, struct regmap *regmap, - int irq, const char *name); + int irq, const struct adxl372_chip_info *chip_info); bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg); #endif /* _ADXL372_H_ */ diff --git a/drivers/iio/accel/adxl372_i2c.c b/drivers/iio/accel/adxl372_i2c.c index 186d4fe9a556..5d135b1150c8 100644 --- a/drivers/iio/accel/adxl372_i2c.c +++ b/drivers/iio/accel/adxl372_i2c.c @@ -20,11 +20,13 @@ static const struct regmap_config adxl372_regmap_config = { static int adxl372_i2c_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); + const struct adxl372_chip_info *chip_info; struct regmap *regmap; unsigned int regval; int ret; + chip_info = i2c_get_match_data(client); + regmap = devm_regmap_init_i2c(client, &adxl372_regmap_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); @@ -38,17 +40,17 @@ static int adxl372_i2c_probe(struct i2c_client *client) dev_warn(&client->dev, "I2C might not work properly with other devices on the bus"); - return adxl372_probe(&client->dev, regmap, client->irq, id->name); + return adxl372_probe(&client->dev, regmap, client->irq, chip_info); } static const struct i2c_device_id adxl372_i2c_id[] = { - { "adxl372" }, + { "adxl372", (kernel_ulong_t)&adxl372_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, adxl372_i2c_id); static const struct of_device_id adxl372_of_match[] = { - { .compatible = "adi,adxl372" }, + { .compatible = "adi,adxl372", .data = &adxl372_chip_info }, { } }; MODULE_DEVICE_TABLE(of, adxl372_of_match); diff --git a/drivers/iio/accel/adxl372_spi.c b/drivers/iio/accel/adxl372_spi.c index 39941b519c3b..e96a355bfe67 100644 --- a/drivers/iio/accel/adxl372_spi.c +++ b/drivers/iio/accel/adxl372_spi.c @@ -22,24 +22,26 @@ static const struct regmap_config adxl372_spi_regmap_config = { static int adxl372_spi_probe(struct spi_device *spi) { - const struct spi_device_id *id = spi_get_device_id(spi); + const struct adxl372_chip_info *chip_info; struct regmap *regmap; + chip_info = spi_get_device_match_data(spi); + regmap = devm_regmap_init_spi(spi, &adxl372_spi_regmap_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); - return adxl372_probe(&spi->dev, regmap, spi->irq, id->name); + return adxl372_probe(&spi->dev, regmap, spi->irq, chip_info); } static const struct spi_device_id adxl372_spi_id[] = { - { "adxl372", 0 }, + { "adxl372", (kernel_ulong_t)&adxl372_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adxl372_spi_id); static const struct of_device_id adxl372_of_match[] = { - { .compatible = "adi,adxl372" }, + { .compatible = "adi,adxl372", .data = &adxl372_chip_info }, { } }; MODULE_DEVICE_TABLE(of, adxl372_of_match); From 2643500bd2145a1c430d60273f3a6e9d28821834 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Sat, 21 Mar 2026 12:04:57 +0200 Subject: [PATCH 204/405] dt-bindings: iio: accel: adi,adxl372: add ADXL371 compatible Add the adi,adxl371 compatible string to the ADXL372 binding. The ADXL371 is a +-200g 3-axis MEMS accelerometer nearly identical to the ADXL372 in register layout, differing only in ODR/bandwidth values, timer scale factors, and a silicon anomaly affecting FIFO operation. Update the title and description to reflect both devices. Acked-by: Conor Dooley Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/accel/adi,adxl372.yaml | 9 ++++++--- MAINTAINERS | 5 ++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml index 0ba0df46c3a9..02e734946f44 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml @@ -4,20 +4,23 @@ $id: http://devicetree.org/schemas/iio/accel/adi,adxl372.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Analog Devices ADXL372 3-Axis, +/-(200g) Digital Accelerometer +title: Analog Devices ADXL371/ADXL372 3-Axis, +/-(200g) Digital Accelerometer maintainers: - Marcelo Schmitt - Nuno Sá + - Antoniu Miclaus description: | - Analog Devices ADXL372 3-Axis, +/-(200g) Digital Accelerometer that supports - both I2C & SPI interfaces + Analog Devices ADXL371/ADXL372 3-Axis, +/-(200g) Digital Accelerometer that + supports both I2C & SPI interfaces + https://www.analog.com/en/products/adxl371.html https://www.analog.com/en/products/adxl372.html properties: compatible: enum: + - adi,adxl371 - adi,adxl372 reg: diff --git a/MAINTAINERS b/MAINTAINERS index 08d8ddf4ef68..073b4f767d11 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -651,8 +651,11 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml F: drivers/iio/accel/adxl367* -ADXL372 THREE-AXIS DIGITAL ACCELEROMETER DRIVER +ADXL371/ADXL372 THREE-AXIS DIGITAL ACCELEROMETER DRIVER M: Michael Hennerich +M: Marcelo Schmitt +M: Nuno Sá +M: Antoniu Miclaus S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml From 39df6dbfad4ecc7ad667995e738fbe9b3b76f850 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Sat, 21 Mar 2026 12:04:58 +0200 Subject: [PATCH 205/405] iio: accel: adxl372: factor out buffer and trigger setup Extract the triggered buffer, trigger allocation, and IRQ request logic from adxl372_probe() into a dedicated adxl372_buffer_setup() helper. This reduces the probe function complexity and prepares for conditionally disabling buffer support on device variants with known FIFO issues. No functional change intended. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl372.c | 93 ++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 43 deletions(-) diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 1592b25ecb2c..bbd6dc9a1d4e 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -1190,6 +1190,55 @@ bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg) } EXPORT_SYMBOL_NS_GPL(adxl372_readable_noinc_reg, "IIO_ADXL372"); +static int adxl372_buffer_setup(struct iio_dev *indio_dev) +{ + struct adxl372_state *st = iio_priv(indio_dev); + struct device *dev = st->dev; + int ret; + + ret = devm_iio_triggered_buffer_setup_ext(dev, indio_dev, NULL, + adxl372_trigger_handler, + IIO_BUFFER_DIRECTION_IN, + &adxl372_buffer_ops, + adxl372_fifo_attributes); + if (ret) + return ret; + + if (!st->irq) + return 0; + + st->dready_trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->dready_trig) + return -ENOMEM; + + st->peak_datardy_trig = devm_iio_trigger_alloc(dev, "%s-dev%d-peak", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->peak_datardy_trig) + return -ENOMEM; + + st->dready_trig->ops = &adxl372_trigger_ops; + st->peak_datardy_trig->ops = &adxl372_peak_data_trigger_ops; + iio_trigger_set_drvdata(st->dready_trig, indio_dev); + iio_trigger_set_drvdata(st->peak_datardy_trig, indio_dev); + ret = devm_iio_trigger_register(dev, st->dready_trig); + if (ret) + return ret; + + ret = devm_iio_trigger_register(dev, st->peak_datardy_trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(st->dready_trig); + + return devm_request_irq(dev, st->irq, + iio_trigger_generic_data_rdy_poll, + IRQF_TRIGGER_RISING | IRQF_NO_THREAD, + indio_dev->name, st->dready_trig); +} + int adxl372_probe(struct device *dev, struct regmap *regmap, int irq, const struct adxl372_chip_info *chip_info) { @@ -1224,52 +1273,10 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, return ret; } - ret = devm_iio_triggered_buffer_setup_ext(dev, - indio_dev, NULL, - adxl372_trigger_handler, - IIO_BUFFER_DIRECTION_IN, - &adxl372_buffer_ops, - adxl372_fifo_attributes); + ret = adxl372_buffer_setup(indio_dev); if (ret < 0) return ret; - if (st->irq) { - st->dready_trig = devm_iio_trigger_alloc(dev, - "%s-dev%d", - indio_dev->name, - iio_device_id(indio_dev)); - if (st->dready_trig == NULL) - return -ENOMEM; - - st->peak_datardy_trig = devm_iio_trigger_alloc(dev, - "%s-dev%d-peak", - indio_dev->name, - iio_device_id(indio_dev)); - if (!st->peak_datardy_trig) - return -ENOMEM; - - st->dready_trig->ops = &adxl372_trigger_ops; - st->peak_datardy_trig->ops = &adxl372_peak_data_trigger_ops; - iio_trigger_set_drvdata(st->dready_trig, indio_dev); - iio_trigger_set_drvdata(st->peak_datardy_trig, indio_dev); - ret = devm_iio_trigger_register(dev, st->dready_trig); - if (ret < 0) - return ret; - - ret = devm_iio_trigger_register(dev, st->peak_datardy_trig); - if (ret < 0) - return ret; - - indio_dev->trig = iio_trigger_get(st->dready_trig); - - ret = devm_request_irq(dev, st->irq, - iio_trigger_generic_data_rdy_poll, - IRQF_TRIGGER_RISING | IRQF_NO_THREAD, - indio_dev->name, st->dready_trig); - if (ret < 0) - return ret; - } - return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(adxl372_probe, "IIO_ADXL372"); From e7ecdcbc16f049edfa32be01b34ec8dc8e3c51ce Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Sat, 21 Mar 2026 12:04:59 +0200 Subject: [PATCH 206/405] iio: accel: adxl372: add support for ADXL371 Add support for the Analog Devices ADXL371, a +-200g 3-axis MEMS accelerometer sharing the same register map as the ADXL372 but with different ODR values (320/640/1280/2560/5120 Hz vs 400/800/1600/3200/ 6400 Hz), different bandwidth values, and different timer scale factors for activity/inactivity detection. Due to a silicon anomaly (er001) causing FIFO data misalignment on all current ADXL371 silicon, FIFO and triggered buffer support is disabled for the ADXL371 - only direct mode reads are supported. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/accel/Kconfig | 12 +++---- drivers/iio/accel/adxl372.c | 63 +++++++++++++++++++++++++++++---- drivers/iio/accel/adxl372.h | 4 ++- drivers/iio/accel/adxl372_i2c.c | 7 ++-- drivers/iio/accel/adxl372_spi.c | 7 ++-- 5 files changed, 75 insertions(+), 18 deletions(-) diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 3d3f8d8673dd..4094299e2ed8 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -158,24 +158,24 @@ config ADXL372 select IIO_TRIGGERED_BUFFER config ADXL372_SPI - tristate "Analog Devices ADXL372 3-Axis Accelerometer SPI Driver" + tristate "Analog Devices ADXL371/ADXL372 3-Axis Accelerometer SPI Driver" depends on SPI select ADXL372 select REGMAP_SPI help - Say yes here to add support for the Analog Devices ADXL372 triaxial - acceleration sensor. + Say yes here to add support for the Analog Devices ADXL371/ADXL372 + triaxial acceleration sensor. To compile this driver as a module, choose M here: the module will be called adxl372_spi. config ADXL372_I2C - tristate "Analog Devices ADXL372 3-Axis Accelerometer I2C Driver" + tristate "Analog Devices ADXL371/ADXL372 3-Axis Accelerometer I2C Driver" depends on I2C select ADXL372 select REGMAP_I2C help - Say yes here to add support for the Analog Devices ADXL372 triaxial - acceleration sensor. + Say yes here to add support for the Analog Devices ADXL371/ADXL372 + triaxial acceleration sensor. To compile this driver as a module, choose M here: the module will be called adxl372_i2c. diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index bbd6dc9a1d4e..545a21e5a308 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * ADXL372 3-Axis Digital Accelerometer core driver + * ADXL371/ADXL372 3-Axis Digital Accelerometer core driver * * Copyright 2018 Analog Devices Inc. */ @@ -184,6 +184,15 @@ enum adxl372_odr { ADXL372_ODR_NUM }; +enum adxl371_odr { + ADXL371_ODR_320HZ, + ADXL371_ODR_640HZ, + ADXL371_ODR_1280HZ, + ADXL371_ODR_2560HZ, + ADXL371_ODR_5120HZ, + ADXL371_ODR_NUM +}; + enum adxl372_bandwidth { ADXL372_BW_200HZ, ADXL372_BW_400HZ, @@ -232,6 +241,37 @@ static const int adxl372_bw_freq_tbl[ADXL372_ODR_NUM] = { [ADXL372_BW_3200HZ] = 3200, }; +static const int adxl371_samp_freq_tbl[ADXL371_ODR_NUM] = { + [ADXL371_ODR_320HZ] = 320, + [ADXL371_ODR_640HZ] = 640, + [ADXL371_ODR_1280HZ] = 1280, + [ADXL371_ODR_2560HZ] = 2560, + [ADXL371_ODR_5120HZ] = 5120, +}; + +static const int adxl371_bw_freq_tbl[ADXL371_ODR_NUM] = { + [ADXL371_ODR_320HZ] = 160, + [ADXL371_ODR_640HZ] = 320, + [ADXL371_ODR_1280HZ] = 640, + [ADXL371_ODR_2560HZ] = 1280, + [ADXL371_ODR_5120HZ] = 2560, +}; + +const struct adxl372_chip_info adxl371_chip_info = { + .name = "adxl371", + .samp_freq_tbl = adxl371_samp_freq_tbl, + .bw_freq_tbl = adxl371_bw_freq_tbl, + .num_freqs = ARRAY_SIZE(adxl371_samp_freq_tbl), + .act_time_scale_us = 4125, + .act_time_scale_low_us = 8250, + .inact_time_scale_ms = 16, + .inact_time_scale_low_ms = 32, + .max_odr = ADXL371_ODR_5120HZ, + /* Silicon erratum (er001) causes FIFO data misalignment on ADXL371 */ + .fifo_supported = false, +}; +EXPORT_SYMBOL_NS_GPL(adxl371_chip_info, "IIO_ADXL372"); + const struct adxl372_chip_info adxl372_chip_info = { .name = "adxl372", .samp_freq_tbl = adxl372_samp_freq_tbl, @@ -242,6 +282,7 @@ const struct adxl372_chip_info adxl372_chip_info = { .inact_time_scale_ms = 13, .inact_time_scale_low_ms = 26, .max_odr = ADXL372_ODR_6400HZ, + .fifo_supported = true, }; EXPORT_SYMBOL_NS_GPL(adxl372_chip_info, "IIO_ADXL372"); @@ -1262,10 +1303,15 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, indio_dev->channels = adxl372_channels; indio_dev->num_channels = ARRAY_SIZE(adxl372_channels); - indio_dev->available_scan_masks = adxl372_channel_masks; indio_dev->name = chip_info->name; indio_dev->info = &adxl372_info; - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + + if (chip_info->fifo_supported) { + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->available_scan_masks = adxl372_channel_masks; + } else { + indio_dev->modes = INDIO_DIRECT_MODE; + } ret = adxl372_setup(st); if (ret < 0) { @@ -1273,14 +1319,17 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, return ret; } - ret = adxl372_buffer_setup(indio_dev); - if (ret < 0) - return ret; + if (chip_info->fifo_supported) { + ret = adxl372_buffer_setup(indio_dev); + if (ret < 0) + return ret; + } return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(adxl372_probe, "IIO_ADXL372"); MODULE_AUTHOR("Stefan Popa "); -MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer driver"); +MODULE_AUTHOR("Antoniu Miclaus "); +MODULE_DESCRIPTION("Analog Devices ADXL371/ADXL372 3-axis accelerometer driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/adxl372.h b/drivers/iio/accel/adxl372.h index 3ce06609446c..353a8b3a9d76 100644 --- a/drivers/iio/accel/adxl372.h +++ b/drivers/iio/accel/adxl372.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * ADXL372 3-Axis Digital Accelerometer + * ADXL371/ADXL372 3-Axis Digital Accelerometer * * Copyright 2018 Analog Devices Inc. */ @@ -20,8 +20,10 @@ struct adxl372_chip_info { unsigned int inact_time_scale_ms; unsigned int inact_time_scale_low_ms; unsigned int max_odr; + bool fifo_supported; }; +extern const struct adxl372_chip_info adxl371_chip_info; extern const struct adxl372_chip_info adxl372_chip_info; int adxl372_probe(struct device *dev, struct regmap *regmap, diff --git a/drivers/iio/accel/adxl372_i2c.c b/drivers/iio/accel/adxl372_i2c.c index 5d135b1150c8..ca2cabf24938 100644 --- a/drivers/iio/accel/adxl372_i2c.c +++ b/drivers/iio/accel/adxl372_i2c.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * ADXL372 3-Axis Digital Accelerometer I2C driver + * ADXL371/ADXL372 3-Axis Digital Accelerometer I2C driver * * Copyright 2018 Analog Devices Inc. */ @@ -44,12 +44,14 @@ static int adxl372_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id adxl372_i2c_id[] = { + { "adxl371", (kernel_ulong_t)&adxl371_chip_info }, { "adxl372", (kernel_ulong_t)&adxl372_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, adxl372_i2c_id); static const struct of_device_id adxl372_of_match[] = { + { .compatible = "adi,adxl371", .data = &adxl371_chip_info }, { .compatible = "adi,adxl372", .data = &adxl372_chip_info }, { } }; @@ -67,6 +69,7 @@ static struct i2c_driver adxl372_i2c_driver = { module_i2c_driver(adxl372_i2c_driver); MODULE_AUTHOR("Stefan Popa "); -MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer I2C driver"); +MODULE_AUTHOR("Antoniu Miclaus "); +MODULE_DESCRIPTION("Analog Devices ADXL371/ADXL372 3-axis accelerometer I2C driver"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("IIO_ADXL372"); diff --git a/drivers/iio/accel/adxl372_spi.c b/drivers/iio/accel/adxl372_spi.c index e96a355bfe67..1f9c1544e547 100644 --- a/drivers/iio/accel/adxl372_spi.c +++ b/drivers/iio/accel/adxl372_spi.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * ADXL372 3-Axis Digital Accelerometer SPI driver + * ADXL371/ADXL372 3-Axis Digital Accelerometer SPI driver * * Copyright 2018 Analog Devices Inc. */ @@ -35,12 +35,14 @@ static int adxl372_spi_probe(struct spi_device *spi) } static const struct spi_device_id adxl372_spi_id[] = { + { "adxl371", (kernel_ulong_t)&adxl371_chip_info }, { "adxl372", (kernel_ulong_t)&adxl372_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adxl372_spi_id); static const struct of_device_id adxl372_of_match[] = { + { .compatible = "adi,adxl371", .data = &adxl371_chip_info }, { .compatible = "adi,adxl372", .data = &adxl372_chip_info }, { } }; @@ -58,6 +60,7 @@ static struct spi_driver adxl372_spi_driver = { module_spi_driver(adxl372_spi_driver); MODULE_AUTHOR("Stefan Popa "); -MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer SPI driver"); +MODULE_AUTHOR("Antoniu Miclaus "); +MODULE_DESCRIPTION("Analog Devices ADXL371/ADXL372 3-axis accelerometer SPI driver"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("IIO_ADXL372"); From 0dc1147b4c9d4a77e8e4942b92ebf398aff1e91d Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Sat, 21 Mar 2026 12:01:51 +0200 Subject: [PATCH 207/405] iio: backend: use __free(fwnode_handle) for automatic cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert __devm_iio_backend_fwnode_get() to use the __free(fwnode_handle) cleanup attribute for the fwnode_back variable, removing the need for manual fwnode_handle_put() calls. Move the declaration closer to its first use, narrowing its scope. No functional change. Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-backend.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 1afd00763da9..10e689f49441 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -967,7 +967,6 @@ EXPORT_SYMBOL_NS_GPL(iio_backend_data_transfer_addr, "IIO_BACKEND"); static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, const char *name, struct fwnode_handle *fwnode) { - struct fwnode_handle *fwnode_back; struct iio_backend *back; unsigned int index; int ret; @@ -982,7 +981,8 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con index = 0; } - fwnode_back = fwnode_find_reference(fwnode, "io-backends", index); + struct fwnode_handle *fwnode_back __free(fwnode_handle) = + fwnode_find_reference(fwnode, "io-backends", index); if (IS_ERR(fwnode_back)) return dev_err_cast_probe(dev, fwnode_back, "Cannot get Firmware reference\n"); @@ -992,7 +992,6 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con if (!device_match_fwnode(back->dev, fwnode_back)) continue; - fwnode_handle_put(fwnode_back); ret = __devm_iio_backend_get(dev, back); if (ret) return ERR_PTR(ret); @@ -1003,7 +1002,6 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con return back; } - fwnode_handle_put(fwnode_back); return ERR_PTR(-EPROBE_DEFER); } From d674752564bfe294f7ac53a47b03846bc02c691f Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Fri, 20 Mar 2026 18:45:36 +0200 Subject: [PATCH 208/405] dt-bindings: iio: light: vcnl4000: add regulators These sensors can accept 2 supplies - one for the sensor and one for IR LED [1]. Add supply properties for the sensor - 2 for the sensors and one external, for their open drain interrupt line, to ensure the sensor is powered on before proceeding with setup. [1] https://www.vishay.com/docs/84274/vcnl4040.pdf Reviewed-by: David Lechner Signed-off-by: Erikas Bitovtas Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jonathan Cameron --- .../bindings/iio/light/vishay,vcnl4000.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/light/vishay,vcnl4000.yaml b/Documentation/devicetree/bindings/iio/light/vishay,vcnl4000.yaml index 2ba4d5de4ec4..516afef7a545 100644 --- a/Documentation/devicetree/bindings/iio/light/vishay,vcnl4000.yaml +++ b/Documentation/devicetree/bindings/iio/light/vishay,vcnl4000.yaml @@ -33,6 +33,17 @@ properties: interrupts: maxItems: 1 + vdd-supply: + description: Regulator providing power to the "VDD" pin. + + vio-supply: + description: Regulator providing power for pull-up of the I/O lines. + Does not connect to the sensor directly, but is needed for the + correct operation of the I2C and interrupt lines. + + vled-supply: + description: Regulator providing power to the IR anode pin. + reg: maxItems: 1 @@ -54,6 +65,9 @@ examples: compatible = "vishay,vcnl4200"; reg = <0x51>; proximity-near-level = <220>; + vdd-supply = <®_vdd>; + vio-supply = <®_vio>; + vled-supply = <®_vled>; }; }; ... From 7e1d6b37c8b560d7ecf3c6f63a8d64a585b8ec62 Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Fri, 20 Mar 2026 18:45:37 +0200 Subject: [PATCH 209/405] iio: light: vcnl4000: sort includes by their name Sort include headers by file name for better readability. Reviewed-by: David Lechner Signed-off-by: Erikas Bitovtas Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 5e03c3d8874b..939ff2d65105 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -18,12 +18,12 @@ */ #include -#include -#include -#include #include -#include +#include +#include #include +#include +#include #include #include From 1c9cb53572ee67d19c43191fe02eb937173ee9a7 Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Fri, 20 Mar 2026 18:45:38 +0200 Subject: [PATCH 210/405] iio: light: vcnl4000: move power enablement from init to probe Given both vcnl4000_init() and vcnl4200_init() end with dev->chip_spec->set_power_state(), they can be called once from the probe to enable the sensors. Move the set_power_state function from init and call it after init function in probe. Reviewed-by: David Lechner Signed-off-by: Erikas Bitovtas Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 939ff2d65105..287ccd89cfb2 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -280,7 +280,7 @@ static int vcnl4000_init(struct vcnl4000_data *data) data->rev = ret & 0xf; data->al_scale = 250000; - return data->chip_spec->set_power_state(data, true); + return 0; }; static ssize_t vcnl4000_write_als_enable(struct vcnl4000_data *data, bool en) @@ -425,10 +425,6 @@ static int vcnl4200_init(struct vcnl4000_data *data) if (ret < 0) return ret; - ret = data->chip_spec->set_power_state(data, true); - if (ret < 0) - return ret; - return 0; }; @@ -2003,6 +1999,10 @@ static int vcnl4000_probe(struct i2c_client *client) if (ret < 0) return ret; + ret = data->chip_spec->set_power_state(data, true); + if (ret) + return ret; + dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", data->chip_spec->prod, data->rev); From 177fa06d8e0b497fc9aec4ae2b233877ecd61bdb Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Fri, 20 Mar 2026 18:45:39 +0200 Subject: [PATCH 211/405] iio: light: vcnl4000: replace mutex_init() with devm_mutex_init() Replace mutex_init() used across the driver with its device-managed counterpart, so all assigned mutexes get destroyed. Reviewed-by: David Lechner Signed-off-by: Erikas Bitovtas Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 287ccd89cfb2..cd7e6ee42cc5 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -356,6 +356,8 @@ static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) static int vcnl4200_init(struct vcnl4000_data *data) { + struct i2c_client *client = data->client; + struct device *dev = &client->dev; int ret, id; u16 regval; @@ -400,8 +402,14 @@ static int vcnl4200_init(struct vcnl4000_data *data) } data->al_scale = data->chip_spec->ulux_step; data->ps_scale = 16; - mutex_init(&data->vcnl4200_al.lock); - mutex_init(&data->vcnl4200_ps.lock); + + ret = devm_mutex_init(dev, &data->vcnl4200_al.lock); + if (ret) + return ret; + + ret = devm_mutex_init(dev, &data->vcnl4200_ps.lock); + if (ret) + return ret; /* Use 16 bits proximity sensor readings */ ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); @@ -1979,6 +1987,7 @@ static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) static int vcnl4000_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct device *dev = &client->dev; struct vcnl4000_data *data; struct iio_dev *indio_dev; int ret; @@ -1993,7 +2002,9 @@ static int vcnl4000_probe(struct i2c_client *client) data->id = id->driver_data; data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; - mutex_init(&data->vcnl4000_lock); + ret = devm_mutex_init(dev, &data->vcnl4000_lock); + if (ret) + return ret; ret = data->chip_spec->init(data); if (ret < 0) From b10aecd9d59ae084bea58472010f92023efa4283 Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Fri, 20 Mar 2026 18:45:40 +0200 Subject: [PATCH 212/405] iio: light: vcnl4000: remove error messages for trigger and irq The error code is available in the log after return. In our case, attaching a triggered buffer can only fail if we are out of memory, as no other buffer is being attached. Remove duplicate error messages to reduce noise in dmesg. Reviewed-by: David Lechner Signed-off-by: Erikas Bitovtas Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index cd7e6ee42cc5..76aee16d479b 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -2033,11 +2033,8 @@ static int vcnl4000_probe(struct i2c_client *client) NULL, data->chip_spec->trig_buffer_func, data->chip_spec->buffer_setup_ops); - if (ret < 0) { - dev_err(&client->dev, - "unable to setup iio triggered buffer\n"); + if (ret < 0) return ret; - } } if (client->irq && data->chip_spec->irq_thread) { @@ -2047,10 +2044,8 @@ static int vcnl4000_probe(struct i2c_client *client) IRQF_ONESHOT, "vcnl4000_irq", indio_dev); - if (ret < 0) { - dev_err(&client->dev, "irq request failed\n"); + if (ret < 0) return ret; - } ret = vcnl4010_probe_trigger(indio_dev); if (ret < 0) From c4380f90752b8ad469e077658f378c9f454e429c Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Fri, 20 Mar 2026 18:45:41 +0200 Subject: [PATCH 213/405] iio: light: vcnl4000: use variables for I2C client and device instances After moving data->client and client->dev into variables of their own, replace all instances of data->client and client->dev being used in vcnl4200_init() and vcnl4000_probe() by the said variables to reduce clutter. Reviewed-by: David Lechner Signed-off-by: Erikas Bitovtas Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 76aee16d479b..34b52725aff6 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -361,14 +361,14 @@ static int vcnl4200_init(struct vcnl4000_data *data) int ret, id; u16 regval; - ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); + ret = i2c_smbus_read_word_data(client, VCNL4200_DEV_ID); if (ret < 0) return ret; id = ret & 0xff; if (id != VCNL4200_PROD_ID) { - ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); + ret = i2c_smbus_read_word_data(client, VCNL4040_DEV_ID); if (ret < 0) return ret; @@ -378,7 +378,7 @@ static int vcnl4200_init(struct vcnl4000_data *data) return -ENODEV; } - dev_dbg(&data->client->dev, "device id 0x%x", id); + dev_dbg(dev, "device id 0x%x", id); data->rev = (ret >> 8) & 0xf; data->ps_int = 0; @@ -412,24 +412,22 @@ static int vcnl4200_init(struct vcnl4000_data *data) return ret; /* Use 16 bits proximity sensor readings */ - ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); + ret = i2c_smbus_read_word_data(client, VCNL4200_PS_CONF1); if (ret < 0) return ret; regval = ret | VCNL4040_PS_CONF2_PS_HD; - ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, - regval); + ret = i2c_smbus_write_word_data(client, VCNL4200_PS_CONF1, regval); if (ret < 0) return ret; /* Align proximity sensor sample rate to 16 bits data width */ - ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); + ret = i2c_smbus_read_word_data(client, VCNL4200_PS_CONF3); if (ret < 0) return ret; regval = ret | VCNL4040_CONF3_PS_SAMPLE_16BITS; - ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3, - regval); + ret = i2c_smbus_write_word_data(client, VCNL4200_PS_CONF3, regval); if (ret < 0) return ret; @@ -1992,7 +1990,7 @@ static int vcnl4000_probe(struct i2c_client *client) struct iio_dev *indio_dev; int ret; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; @@ -2014,7 +2012,7 @@ static int vcnl4000_probe(struct i2c_client *client) if (ret) return ret; - dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", + dev_dbg(dev, "%s Ambient light/proximity sensor, Rev: %02x\n", data->chip_spec->prod, data->rev); if (device_property_read_u32(&client->dev, "proximity-near-level", @@ -2029,8 +2027,7 @@ static int vcnl4000_probe(struct i2c_client *client) if (data->chip_spec->trig_buffer_func && data->chip_spec->buffer_setup_ops) { - ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, - NULL, + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, data->chip_spec->trig_buffer_func, data->chip_spec->buffer_setup_ops); if (ret < 0) @@ -2038,8 +2035,8 @@ static int vcnl4000_probe(struct i2c_client *client) } if (client->irq && data->chip_spec->irq_thread) { - ret = devm_request_threaded_irq(&client->dev, client->irq, - NULL, data->chip_spec->irq_thread, + ret = devm_request_threaded_irq(dev, client->irq, NULL, + data->chip_spec->irq_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "vcnl4000_irq", @@ -2052,7 +2049,7 @@ static int vcnl4000_probe(struct i2c_client *client) return ret; } - ret = pm_runtime_set_active(&client->dev); + ret = pm_runtime_set_active(dev); if (ret < 0) goto fail_poweroff; @@ -2060,9 +2057,9 @@ static int vcnl4000_probe(struct i2c_client *client) if (ret < 0) goto fail_poweroff; - pm_runtime_enable(&client->dev); - pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); - pm_runtime_use_autosuspend(&client->dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, VCNL4000_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(dev); return 0; fail_poweroff: From 5ec96d77ca28c0560e8883a0709ab63667eac19f Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Fri, 20 Mar 2026 18:45:42 +0200 Subject: [PATCH 214/405] iio: light: vcnl4000: remove redundant check for proximity-near-level The data->near_level variable is already assigned 0 during devm_kzalloc(), therefore checking if the property is present and then assigning it 0 is redundant. Remove the check for device tree property and let it fail silently if it is missing or invalid. Reviewed-by: David Lechner Signed-off-by: Erikas Bitovtas Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 34b52725aff6..0a4d82679cfe 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -2015,9 +2015,7 @@ static int vcnl4000_probe(struct i2c_client *client) dev_dbg(dev, "%s Ambient light/proximity sensor, Rev: %02x\n", data->chip_spec->prod, data->rev); - if (device_property_read_u32(&client->dev, "proximity-near-level", - &data->near_level)) - data->near_level = 0; + device_property_read_u32(dev, "proximity-near-level", &data->near_level); indio_dev->info = data->chip_spec->info; indio_dev->channels = data->chip_spec->channels; From 773a5dc613ed1c9b8b2a9d614d42ac994e99aeb6 Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Fri, 20 Mar 2026 18:45:43 +0200 Subject: [PATCH 215/405] iio: light: vcnl4000: add support for regulators Add supply, I2C and cathode voltage regulators to the sensor and enable them. This keeps the sensor powered on even after its only supply shared by another device shuts down. Reported-by: Raymond Hackley Reviewed-by: David Lechner Signed-off-by: Erikas Bitovtas Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 0a4d82679cfe..9650dbc41f2b 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -1985,6 +1986,7 @@ static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) static int vcnl4000_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); + const char * const regulator_names[] = { "vdd", "vio", "vled" }; struct device *dev = &client->dev; struct vcnl4000_data *data; struct iio_dev *indio_dev; @@ -2000,6 +2002,11 @@ static int vcnl4000_probe(struct i2c_client *client) data->id = id->driver_data; data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulator_names), + regulator_names); + if (ret) + return ret; + ret = devm_mutex_init(dev, &data->vcnl4000_lock); if (ret) return ret; From f6192780807d5c2fc08f506948b19c6072e1bfe8 Mon Sep 17 00:00:00 2001 From: Gabriel Rondon Date: Fri, 20 Mar 2026 22:24:23 +0000 Subject: [PATCH 216/405] staging: iio: ad5933: use sysfs_emit() in show functions Replace sprintf() with sysfs_emit() in all sysfs attribute show functions. sysfs_emit() is the preferred API for sysfs callbacks as it is aware of the PAGE_SIZE buffer limit. Also remove the unnecessary (int) cast in ad5933_show_frequency() and use the correct format specifier %llu for the unsigned long long freqreg variable. Signed-off-by: Gabriel Rondon Signed-off-by: Jonathan Cameron --- .../staging/iio/impedance-analyzer/ad5933.c | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index f6d3d98b6c6a..dde2ec9d1f6a 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -285,7 +285,7 @@ static ssize_t ad5933_show_frequency(struct device *dev, freqreg = (u64)freqreg * (u64)(st->mclk_hz / 4); do_div(freqreg, BIT(27)); - return sprintf(buf, "%d\n", (int)freqreg); + return sysfs_emit(buf, "%llu\n", freqreg); } static ssize_t ad5933_store_frequency(struct device *dev, @@ -338,27 +338,27 @@ static ssize_t ad5933_show(struct device *dev, mutex_lock(&st->lock); switch ((u32)this_attr->address) { case AD5933_OUT_RANGE: - len = sprintf(buf, "%u\n", - st->range_avail[(st->ctrl_hb >> 1) & 0x3]); + len = sysfs_emit(buf, "%u\n", + st->range_avail[(st->ctrl_hb >> 1) & 0x3]); break; case AD5933_OUT_RANGE_AVAIL: - len = sprintf(buf, "%u %u %u %u\n", st->range_avail[0], - st->range_avail[3], st->range_avail[2], - st->range_avail[1]); + len = sysfs_emit(buf, "%u %u %u %u\n", st->range_avail[0], + st->range_avail[3], st->range_avail[2], + st->range_avail[1]); break; case AD5933_OUT_SETTLING_CYCLES: - len = sprintf(buf, "%d\n", st->settling_cycles); + len = sysfs_emit(buf, "%d\n", st->settling_cycles); break; case AD5933_IN_PGA_GAIN: - len = sprintf(buf, "%s\n", - (st->ctrl_hb & AD5933_CTRL_PGA_GAIN_1) ? - "1" : "0.2"); + len = sysfs_emit(buf, "%s\n", + (st->ctrl_hb & AD5933_CTRL_PGA_GAIN_1) ? + "1" : "0.2"); break; case AD5933_IN_PGA_GAIN_AVAIL: - len = sprintf(buf, "1 0.2\n"); + len = sysfs_emit(buf, "1 0.2\n"); break; case AD5933_FREQ_POINTS: - len = sprintf(buf, "%d\n", st->freq_points); + len = sysfs_emit(buf, "%d\n", st->freq_points); break; default: ret = -EINVAL; From 8a75ac8f0a7274085aa71aacfaa21ed72bf8435b Mon Sep 17 00:00:00 2001 From: Gabriel Rondon Date: Fri, 20 Mar 2026 22:24:24 +0000 Subject: [PATCH 217/405] staging: iio: ad9834: use sysfs_emit() and simplify show functions Replace sprintf() with sysfs_emit() in sysfs attribute show functions. sysfs_emit() is the preferred API for sysfs callbacks as it is aware of the PAGE_SIZE buffer limit. Also simplify the wavetype_available show functions by removing the intermediate string variable and returning directly from each branch. Signed-off-by: Gabriel Rondon Signed-off-by: Jonathan Cameron --- drivers/staging/iio/frequency/ad9834.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index d339d5e8e043..bdb2580e29bf 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -281,16 +281,12 @@ ssize_t ad9834_show_out0_wavetype_available(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad9834_state *st = iio_priv(indio_dev); - char *str; if (st->devid == ID_AD9833 || st->devid == ID_AD9837) - str = "sine triangle square"; - else if (st->control & AD9834_OPBITEN) - str = "sine"; - else - str = "sine triangle"; - - return sprintf(buf, "%s\n", str); + return sysfs_emit(buf, "sine triangle square\n"); + if (st->control & AD9834_OPBITEN) + return sysfs_emit(buf, "sine\n"); + return sysfs_emit(buf, "sine triangle\n"); } static IIO_DEVICE_ATTR(out_altvoltage0_out0_wavetype_available, 0444, @@ -303,14 +299,10 @@ ssize_t ad9834_show_out1_wavetype_available(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad9834_state *st = iio_priv(indio_dev); - char *str; if (st->control & AD9834_MODE) - str = ""; - else - str = "square"; - - return sprintf(buf, "%s\n", str); + return sysfs_emit(buf, "\n"); + return sysfs_emit(buf, "square\n"); } static IIO_DEVICE_ATTR(out_altvoltage0_out1_wavetype_available, 0444, From cbf8db23fc58db8d516ebc7aa57c4563f36532b9 Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Wed, 4 Feb 2026 20:00:32 +0200 Subject: [PATCH 218/405] counter: sysfs: remove double return in counter_sysfs_attr_add() sysfs attribute creation for counter extensions has been consolidated into a single function, counter_sysfs_exts_add(). Inside counter_sysfs_attr_add(), although the code was changed to return the result of counter_sysfs_exts_add(), an unreachable return 0; statement was left at the end of the function. Remove it. Fixes: bb4bbbec664f ("counter: Consolidate Counter extension sysfs attribute creation") Signed-off-by: Cosmin Tanislav Link: https://lore.kernel.org/r/20260204180032.514328-1-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: William Breathitt Gray --- drivers/counter/counter-sysfs.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c index 42c523343d32..ed85da907982 100644 --- a/drivers/counter/counter-sysfs.c +++ b/drivers/counter/counter-sysfs.c @@ -1101,8 +1101,6 @@ static int counter_sysfs_attr_add(struct counter_device *const counter, /* Add device extensions */ return counter_sysfs_exts_add(dev, cattr_group, counter->ext, counter->num_ext, scope, NULL); - - return 0; } /** From 19045d89e4f4754349b1256650729ae08bde14ed Mon Sep 17 00:00:00 2001 From: David Marinovic Date: Fri, 20 Mar 2026 16:09:46 +0100 Subject: [PATCH 219/405] iio: dac: ltc2632: drop enum and use individual chip_info objects Remove the ltc2632_chip_info_tbl[] array and related ltc2632_supported_device_ids enum used for looking up chip-specific information. Instead, use separate static const struct ltc2632_chip_info objects for each supported chip variant. Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: David Marinovic Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ltc2632.c | 327 +++++++++++++++++--------------------- 1 file changed, 142 insertions(+), 185 deletions(-) diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c index 105f939f7e54..ca0b88285ce5 100644 --- a/drivers/iio/dac/ltc2632.c +++ b/drivers/iio/dac/ltc2632.c @@ -48,27 +48,6 @@ struct ltc2632_state { int vref_mv; }; -enum ltc2632_supported_device_ids { - ID_LTC2632L12, - ID_LTC2632L10, - ID_LTC2632L8, - ID_LTC2632H12, - ID_LTC2632H10, - ID_LTC2632H8, - ID_LTC2634L12, - ID_LTC2634L10, - ID_LTC2634L8, - ID_LTC2634H12, - ID_LTC2634H10, - ID_LTC2634H8, - ID_LTC2636L12, - ID_LTC2636L10, - ID_LTC2636L8, - ID_LTC2636H12, - ID_LTC2636H10, - ID_LTC2636H8, -}; - static int ltc2632_spi_write(struct spi_device *spi, u8 cmd, u8 addr, u16 val, u8 shift) { @@ -210,97 +189,112 @@ static DECLARE_LTC2632_CHANNELS(ltc2632x12, 12); static DECLARE_LTC2632_CHANNELS(ltc2632x10, 10); static DECLARE_LTC2632_CHANNELS(ltc2632x8, 8); -static const struct ltc2632_chip_info ltc2632_chip_info_tbl[] = { - [ID_LTC2632L12] = { - .channels = ltc2632x12_channels, - .num_channels = 2, - .vref_mv = 2500, - }, - [ID_LTC2632L10] = { - .channels = ltc2632x10_channels, - .num_channels = 2, - .vref_mv = 2500, - }, - [ID_LTC2632L8] = { - .channels = ltc2632x8_channels, - .num_channels = 2, - .vref_mv = 2500, - }, - [ID_LTC2632H12] = { - .channels = ltc2632x12_channels, - .num_channels = 2, - .vref_mv = 4096, - }, - [ID_LTC2632H10] = { - .channels = ltc2632x10_channels, - .num_channels = 2, - .vref_mv = 4096, - }, - [ID_LTC2632H8] = { - .channels = ltc2632x8_channels, - .num_channels = 2, - .vref_mv = 4096, - }, - [ID_LTC2634L12] = { - .channels = ltc2632x12_channels, - .num_channels = 4, - .vref_mv = 2500, - }, - [ID_LTC2634L10] = { - .channels = ltc2632x10_channels, - .num_channels = 4, - .vref_mv = 2500, - }, - [ID_LTC2634L8] = { - .channels = ltc2632x8_channels, - .num_channels = 4, - .vref_mv = 2500, - }, - [ID_LTC2634H12] = { - .channels = ltc2632x12_channels, - .num_channels = 4, - .vref_mv = 4096, - }, - [ID_LTC2634H10] = { - .channels = ltc2632x10_channels, - .num_channels = 4, - .vref_mv = 4096, - }, - [ID_LTC2634H8] = { - .channels = ltc2632x8_channels, - .num_channels = 4, - .vref_mv = 4096, - }, - [ID_LTC2636L12] = { - .channels = ltc2632x12_channels, - .num_channels = 8, - .vref_mv = 2500, - }, - [ID_LTC2636L10] = { - .channels = ltc2632x10_channels, - .num_channels = 8, - .vref_mv = 2500, - }, - [ID_LTC2636L8] = { - .channels = ltc2632x8_channels, - .num_channels = 8, - .vref_mv = 2500, - }, - [ID_LTC2636H12] = { - .channels = ltc2632x12_channels, - .num_channels = 8, - .vref_mv = 4096, - }, - [ID_LTC2636H10] = { - .channels = ltc2632x10_channels, - .num_channels = 8, - .vref_mv = 4096, - }, - [ID_LTC2636H8] = { - .channels = ltc2632x8_channels, - .num_channels = 8, - .vref_mv = 4096, - }, +static const struct ltc2632_chip_info ltc2632l12_chip_info = { + .channels = ltc2632x12_channels, + .num_channels = 2, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2632l10_chip_info = { + .channels = ltc2632x10_channels, + .num_channels = 2, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2632l8_chip_info = { + .channels = ltc2632x8_channels, + .num_channels = 2, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2632h12_chip_info = { + .channels = ltc2632x12_channels, + .num_channels = 2, + .vref_mv = 4096, +}; + +static const struct ltc2632_chip_info ltc2632h10_chip_info = { + .channels = ltc2632x10_channels, + .num_channels = 2, + .vref_mv = 4096, +}; + +static const struct ltc2632_chip_info ltc2632h8_chip_info = { + .channels = ltc2632x8_channels, + .num_channels = 2, + .vref_mv = 4096, +}; + +static const struct ltc2632_chip_info ltc2634l12_chip_info = { + .channels = ltc2632x12_channels, + .num_channels = 4, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2634l10_chip_info = { + .channels = ltc2632x10_channels, + .num_channels = 4, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2634l8_chip_info = { + .channels = ltc2632x8_channels, + .num_channels = 4, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2634h12_chip_info = { + .channels = ltc2632x12_channels, + .num_channels = 4, + .vref_mv = 4096, +}; + +static const struct ltc2632_chip_info ltc2634h10_chip_info = { + .channels = ltc2632x10_channels, + .num_channels = 4, + .vref_mv = 4096, +}; + +static const struct ltc2632_chip_info ltc2634h8_chip_info = { + .channels = ltc2632x8_channels, + .num_channels = 4, + .vref_mv = 4096, +}; + +static const struct ltc2632_chip_info ltc2636l12_chip_info = { + .channels = ltc2632x12_channels, + .num_channels = 8, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2636l10_chip_info = { + .channels = ltc2632x10_channels, + .num_channels = 8, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2636l8_chip_info = { + .channels = ltc2632x8_channels, + .num_channels = 8, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2636h12_chip_info = { + .channels = ltc2632x12_channels, + .num_channels = 8, + .vref_mv = 4096, +}; + +static const struct ltc2632_chip_info ltc2636h10_chip_info = { + .channels = ltc2632x10_channels, + .num_channels = 8, + .vref_mv = 4096, +}; + +static const struct ltc2632_chip_info ltc2636h8_chip_info = { + .channels = ltc2632x8_channels, + .num_channels = 8, + .vref_mv = 4096, }; static int ltc2632_probe(struct spi_device *spi) @@ -354,84 +348,47 @@ static int ltc2632_probe(struct spi_device *spi) } static const struct spi_device_id ltc2632_id[] = { - { "ltc2632-l12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L12] }, - { "ltc2632-l10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L10] }, - { "ltc2632-l8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L8] }, - { "ltc2632-h12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H12] }, - { "ltc2632-h10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H10] }, - { "ltc2632-h8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H8] }, - { "ltc2634-l12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634L12] }, - { "ltc2634-l10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634L10] }, - { "ltc2634-l8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634L8] }, - { "ltc2634-h12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634H12] }, - { "ltc2634-h10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634H10] }, - { "ltc2634-h8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634H8] }, - { "ltc2636-l12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L12] }, - { "ltc2636-l10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L10] }, - { "ltc2636-l8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L8] }, - { "ltc2636-h12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H12] }, - { "ltc2636-h10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H10] }, - { "ltc2636-h8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H8] }, + { "ltc2632-l12", (kernel_ulong_t)<c2632l12_chip_info }, + { "ltc2632-l10", (kernel_ulong_t)<c2632l10_chip_info }, + { "ltc2632-l8", (kernel_ulong_t)<c2632l8_chip_info }, + { "ltc2632-h12", (kernel_ulong_t)<c2632h12_chip_info }, + { "ltc2632-h10", (kernel_ulong_t)<c2632h10_chip_info }, + { "ltc2632-h8", (kernel_ulong_t)<c2632h8_chip_info }, + { "ltc2634-l12", (kernel_ulong_t)<c2634l12_chip_info }, + { "ltc2634-l10", (kernel_ulong_t)<c2634l10_chip_info }, + { "ltc2634-l8", (kernel_ulong_t)<c2634l8_chip_info }, + { "ltc2634-h12", (kernel_ulong_t)<c2634h12_chip_info }, + { "ltc2634-h10", (kernel_ulong_t)<c2634h10_chip_info }, + { "ltc2634-h8", (kernel_ulong_t)<c2634h8_chip_info }, + { "ltc2636-l12", (kernel_ulong_t)<c2636l12_chip_info }, + { "ltc2636-l10", (kernel_ulong_t)<c2636l10_chip_info }, + { "ltc2636-l8", (kernel_ulong_t)<c2636l8_chip_info }, + { "ltc2636-h12", (kernel_ulong_t)<c2636h12_chip_info }, + { "ltc2636-h10", (kernel_ulong_t)<c2636h10_chip_info }, + { "ltc2636-h8", (kernel_ulong_t)<c2636h8_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ltc2632_id); static const struct of_device_id ltc2632_of_match[] = { - { - .compatible = "lltc,ltc2632-l12", - .data = <c2632_chip_info_tbl[ID_LTC2632L12] - }, { - .compatible = "lltc,ltc2632-l10", - .data = <c2632_chip_info_tbl[ID_LTC2632L10] - }, { - .compatible = "lltc,ltc2632-l8", - .data = <c2632_chip_info_tbl[ID_LTC2632L8] - }, { - .compatible = "lltc,ltc2632-h12", - .data = <c2632_chip_info_tbl[ID_LTC2632H12] - }, { - .compatible = "lltc,ltc2632-h10", - .data = <c2632_chip_info_tbl[ID_LTC2632H10] - }, { - .compatible = "lltc,ltc2632-h8", - .data = <c2632_chip_info_tbl[ID_LTC2632H8] - }, { - .compatible = "lltc,ltc2634-l12", - .data = <c2632_chip_info_tbl[ID_LTC2634L12] - }, { - .compatible = "lltc,ltc2634-l10", - .data = <c2632_chip_info_tbl[ID_LTC2634L10] - }, { - .compatible = "lltc,ltc2634-l8", - .data = <c2632_chip_info_tbl[ID_LTC2634L8] - }, { - .compatible = "lltc,ltc2634-h12", - .data = <c2632_chip_info_tbl[ID_LTC2634H12] - }, { - .compatible = "lltc,ltc2634-h10", - .data = <c2632_chip_info_tbl[ID_LTC2634H10] - }, { - .compatible = "lltc,ltc2634-h8", - .data = <c2632_chip_info_tbl[ID_LTC2634H8] - }, { - .compatible = "lltc,ltc2636-l12", - .data = <c2632_chip_info_tbl[ID_LTC2636L12] - }, { - .compatible = "lltc,ltc2636-l10", - .data = <c2632_chip_info_tbl[ID_LTC2636L10] - }, { - .compatible = "lltc,ltc2636-l8", - .data = <c2632_chip_info_tbl[ID_LTC2636L8] - }, { - .compatible = "lltc,ltc2636-h12", - .data = <c2632_chip_info_tbl[ID_LTC2636H12] - }, { - .compatible = "lltc,ltc2636-h10", - .data = <c2632_chip_info_tbl[ID_LTC2636H10] - }, { - .compatible = "lltc,ltc2636-h8", - .data = <c2632_chip_info_tbl[ID_LTC2636H8] - }, + { .compatible = "lltc,ltc2632-l12", .data = <c2632l12_chip_info }, + { .compatible = "lltc,ltc2632-l10", .data = <c2632l10_chip_info }, + { .compatible = "lltc,ltc2632-l8", .data = <c2632l8_chip_info }, + { .compatible = "lltc,ltc2632-h12", .data = <c2632h12_chip_info }, + { .compatible = "lltc,ltc2632-h10", .data = <c2632h10_chip_info }, + { .compatible = "lltc,ltc2632-h8", .data = <c2632h8_chip_info }, + { .compatible = "lltc,ltc2634-l12", .data = <c2634l12_chip_info }, + { .compatible = "lltc,ltc2634-l10", .data = <c2634l10_chip_info }, + { .compatible = "lltc,ltc2634-l8", .data = <c2634l8_chip_info }, + { .compatible = "lltc,ltc2634-h12", .data = <c2634h12_chip_info }, + { .compatible = "lltc,ltc2634-h10", .data = <c2634h10_chip_info }, + { .compatible = "lltc,ltc2634-h8", .data = <c2634h8_chip_info }, + { .compatible = "lltc,ltc2636-l12", .data = <c2636l12_chip_info }, + { .compatible = "lltc,ltc2636-l10", .data = <c2636l10_chip_info }, + { .compatible = "lltc,ltc2636-l8", .data = <c2636l8_chip_info }, + { .compatible = "lltc,ltc2636-h12", .data = <c2636h12_chip_info }, + { .compatible = "lltc,ltc2636-h10", .data = <c2636h10_chip_info }, + { .compatible = "lltc,ltc2636-h8", .data = <c2636h8_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ltc2632_of_match); From 9e5e2c58da138409c3a5f2a4bc430beb16cdd878 Mon Sep 17 00:00:00 2001 From: David Marinovic Date: Fri, 20 Mar 2026 16:09:47 +0100 Subject: [PATCH 220/405] dt-bindings: iio: dac: ltc2632: add LTC2654 compatible strings Add compatible strings for the LTC2654 quad-channel DAC family. The LTC2654 devices are 4-channel, 16-/12-bit DACs with an internal reference and SPI interface. They use the same 24-bit SPI command format as the LTC2632/2634/2636 family. The 16-bit variants (LTC2654-L16 and LTC2654-H16) require new compatible strings, as no existing compatibles support 16-bit resolution. The 12-bit variants (LTC2654-L12 and LTC2654-H12) are register- compatible with LTC2634-L12 and LTC2634-H12 respectively, and can use them as fallback compatibles. Signed-off-by: David Marinovic Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- .../bindings/iio/dac/lltc,ltc2632.yaml | 57 ++++++++++++------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/dac/lltc,ltc2632.yaml b/Documentation/devicetree/bindings/iio/dac/lltc,ltc2632.yaml index 733edc7d6d17..50a9cbb44e36 100644 --- a/Documentation/devicetree/bindings/iio/dac/lltc,ltc2632.yaml +++ b/Documentation/devicetree/bindings/iio/dac/lltc,ltc2632.yaml @@ -4,36 +4,49 @@ $id: http://devicetree.org/schemas/iio/dac/lltc,ltc2632.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Linear Technology LTC263x 12-/10-/8-Bit Rail-to-Rail DAC +title: Linear Technology LTC263x and LTC2654 Rail-to-Rail DAC maintainers: - Michael Hennerich description: | - Bindings for the Linear Technology LTC2632/2634/2636 DAC - Datasheet can be found here: https://www.analog.com/media/en/technical-documentation/data-sheets/LTC263[246].pdf + Bindings for the Linear Technology LTC2632/2634/2636/2654 DAC + Datasheet can be found here: + https://www.analog.com/media/en/technical-documentation/data-sheets/LTC263[246].pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/2654f.pdf properties: compatible: - enum: - - lltc,ltc2632-l12 - - lltc,ltc2632-l10 - - lltc,ltc2632-l8 - - lltc,ltc2632-h12 - - lltc,ltc2632-h10 - - lltc,ltc2632-h8 - - lltc,ltc2634-l12 - - lltc,ltc2634-l10 - - lltc,ltc2634-l8 - - lltc,ltc2634-h12 - - lltc,ltc2634-h10 - - lltc,ltc2634-h8 - - lltc,ltc2636-l12 - - lltc,ltc2636-l10 - - lltc,ltc2636-l8 - - lltc,ltc2636-h12 - - lltc,ltc2636-h10 - - lltc,ltc2636-h8 + oneOf: + - enum: + - lltc,ltc2632-l12 + - lltc,ltc2632-l10 + - lltc,ltc2632-l8 + - lltc,ltc2632-h12 + - lltc,ltc2632-h10 + - lltc,ltc2632-h8 + - lltc,ltc2634-l12 + - lltc,ltc2634-l10 + - lltc,ltc2634-l8 + - lltc,ltc2634-h12 + - lltc,ltc2634-h10 + - lltc,ltc2634-h8 + - lltc,ltc2636-l12 + - lltc,ltc2636-l10 + - lltc,ltc2636-l8 + - lltc,ltc2636-h12 + - lltc,ltc2636-h10 + - lltc,ltc2636-h8 + - lltc,ltc2654-l16 + - lltc,ltc2654-h16 + - items: + - enum: + - lltc,ltc2654-l12 + - const: lltc,ltc2634-l12 + - items: + - enum: + - lltc,ltc2654-h12 + - const: lltc,ltc2634-h12 reg: maxItems: 1 From e163b094917b122467b0498491c137c9e754e70f Mon Sep 17 00:00:00 2001 From: David Marinovic Date: Fri, 20 Mar 2026 16:09:48 +0100 Subject: [PATCH 221/405] iio: dac: ltc2632: add support for LTC2654 DAC family Add support for the Linear Technology LTC2654 quad DAC family. The LTC2654 is a 4-channel, 16-/12-bit DAC with SPI interface, sharing the same 24-bit SPI protocol as the existing LTC2632/ LTC2634/LTC2636 devices supported by this driver. The 12-bit variants of LTC2654 reuse existing LTC2634 chip_info structs as they are register-compatible. Add support for the following variants: - LTC2654L-16: 16-bit, 2.5V internal reference - LTC2654L-12: 12-bit, 2.5V internal reference - LTC2654H-16: 16-bit, 4.096V internal reference - LTC2654H-12: 12-bit, 4.096V internal reference Signed-off-by: David Marinovic Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ltc2632.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c index ca0b88285ce5..d6a3d290e7a8 100644 --- a/drivers/iio/dac/ltc2632.c +++ b/drivers/iio/dac/ltc2632.c @@ -58,8 +58,9 @@ static int ltc2632_spi_write(struct spi_device *spi, * The input shift register is 24 bits wide. * The next four are the command bits, C3 to C0, * followed by the 4-bit DAC address, A3 to A0, and then the - * 12-, 10-, 8-bit data-word. The data-word comprises the 12-, - * 10-, 8-bit input code followed by 4, 6, or 8 don't care bits. + * 16-, 12-, 10-, 8-bit data-word. The data-word comprises the + * 16-, 12-, 10-, 8-bit input code followed by 0, 4, 6, or 8 + * don't care bits. */ data = (cmd << 20) | (addr << 16) | (val << shift); put_unaligned_be24(data, &msg[0]); @@ -185,6 +186,7 @@ static const struct iio_chan_spec_ext_info ltc2632_ext_info[] = { LTC2632_CHANNEL(7, _bits), \ } +static DECLARE_LTC2632_CHANNELS(ltc2632x16, 16); static DECLARE_LTC2632_CHANNELS(ltc2632x12, 12); static DECLARE_LTC2632_CHANNELS(ltc2632x10, 10); static DECLARE_LTC2632_CHANNELS(ltc2632x8, 8); @@ -297,6 +299,18 @@ static const struct ltc2632_chip_info ltc2636h8_chip_info = { .vref_mv = 4096, }; +static const struct ltc2632_chip_info ltc2654l16_chip_info = { + .channels = ltc2632x16_channels, + .num_channels = 4, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2654h16_chip_info = { + .channels = ltc2632x16_channels, + .num_channels = 4, + .vref_mv = 4096, +}; + static int ltc2632_probe(struct spi_device *spi) { struct ltc2632_state *st; @@ -366,6 +380,10 @@ static const struct spi_device_id ltc2632_id[] = { { "ltc2636-h12", (kernel_ulong_t)<c2636h12_chip_info }, { "ltc2636-h10", (kernel_ulong_t)<c2636h10_chip_info }, { "ltc2636-h8", (kernel_ulong_t)<c2636h8_chip_info }, + { "ltc2654-l16", (kernel_ulong_t)<c2654l16_chip_info }, + { "ltc2654-l12", (kernel_ulong_t)<c2634l12_chip_info }, + { "ltc2654-h16", (kernel_ulong_t)<c2654h16_chip_info }, + { "ltc2654-h12", (kernel_ulong_t)<c2634h12_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ltc2632_id); @@ -389,6 +407,8 @@ static const struct of_device_id ltc2632_of_match[] = { { .compatible = "lltc,ltc2636-h12", .data = <c2636h12_chip_info }, { .compatible = "lltc,ltc2636-h10", .data = <c2636h10_chip_info }, { .compatible = "lltc,ltc2636-h8", .data = <c2636h8_chip_info }, + { .compatible = "lltc,ltc2654-l16", .data = <c2654l16_chip_info }, + { .compatible = "lltc,ltc2654-h16", .data = <c2654h16_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ltc2632_of_match); @@ -404,5 +424,5 @@ static struct spi_driver ltc2632_driver = { module_spi_driver(ltc2632_driver); MODULE_AUTHOR("Maxime Roussin-Belanger "); -MODULE_DESCRIPTION("LTC2632 DAC SPI driver"); +MODULE_DESCRIPTION("LTC2632 and similar DAC SPI driver"); MODULE_LICENSE("GPL v2"); From a28069be7d34597159840c4a0dfd3886787455db Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Fri, 20 Mar 2026 13:46:35 +0800 Subject: [PATCH 222/405] iio: adc: Add battery channel definition for ADC Defines a constant for the battery sensing channel, typically the last channel of the ADC. Clarifies channel usage and improves code readability. Signed-off-by: Billy Tsai Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 4be44c524b4d..8eebaa3dc534 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -75,6 +75,8 @@ #define ASPEED_ADC_INIT_POLLING_TIME 500 #define ASPEED_ADC_INIT_TIMEOUT 500000 +/* Battery sensing is typically on the last channel */ +#define ASPEED_ADC_BATTERY_CHANNEL 7 /* * When the sampling rate is too high, the ADC may not have enough charging * time, resulting in a low voltage value. Thus, the default uses a slow @@ -285,7 +287,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - if (data->battery_sensing && chan->channel == 7) { + if (data->battery_sensing && chan->channel == ASPEED_ADC_BATTERY_CHANNEL) { adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); writel(adc_engine_control_reg_val | @@ -309,7 +311,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: - if (data->battery_sensing && chan->channel == 7) + if (data->battery_sensing && chan->channel == ASPEED_ADC_BATTERY_CHANNEL) *val = (data->cv * data->battery_mode_gain.mult) / data->battery_mode_gain.div; else From 9ee1c3be164de16bab382197202ddef8ae020433 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Fri, 20 Mar 2026 13:46:36 +0800 Subject: [PATCH 223/405] iio: adc: Enable multiple consecutive channels based on model data Add helpers to generate channel masks and enable multiple ADC channels according to the device model's channel count. Signed-off-by: Billy Tsai Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 8eebaa3dc534..3ff24474f394 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -123,6 +123,24 @@ struct aspeed_adc_data { struct adc_gain battery_mode_gain; }; +/* + * Enable multiple consecutive channels starting from channel 0. + * This creates a bitmask for channels 0 to (num_channels - 1). + * For example: num_channels=3 creates mask 0x0007 (channels 0,1,2) + */ +static inline u32 aspeed_adc_channels_mask(unsigned int num_channels) +{ + if (num_channels > 16) + return GENMASK(15, 0); + + return BIT(num_channels) - 1; +} + +static inline unsigned int aspeed_adc_get_active_channels(const struct aspeed_adc_data *data) +{ + return data->model_data->num_channels; +} + #define ASPEED_CHAN(_idx, _data_reg_addr) { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ @@ -612,7 +630,9 @@ static int aspeed_adc_probe(struct platform_device *pdev) /* Start all channels in normal mode. */ adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); - adc_engine_control_reg_val |= ASPEED_ADC_CTRL_CHANNEL; + FIELD_MODIFY(ASPEED_ADC_CTRL_CHANNEL, &adc_engine_control_reg_val, + aspeed_adc_channels_mask(aspeed_adc_get_active_channels(data))); + writel(adc_engine_control_reg_val, data->base + ASPEED_REG_ENGINE_CONTROL); From 66ab53c286989634acaa2c4a5703c0f647cb9aa3 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Fri, 20 Mar 2026 13:46:37 +0800 Subject: [PATCH 224/405] iio: adc: aspeed: Replace mdelay() with fsleep() for ADC stabilization delay The ADC stabilization delays in compensation mode and battery sensing mode do not require atomic context. Using mdelay() here results in unnecessary busy waiting. Replace mdelay(1) with fsleep(1000) to allow the scheduler to run other tasks while waiting for the ADC to stabilize. Also fix a minor typo in the comment ("adc" -> "ADC"). Signed-off-by: Billy Tsai Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 3ff24474f394..a1a6296d3003 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -259,7 +259,7 @@ static int aspeed_adc_compensation(struct iio_dev *indio_dev) * After enable compensating sensing mode need to wait some time for ADC stable * Experiment result is 1ms. */ - mdelay(1); + fsleep(1000); for (index = 0; index < 16; index++) { /* @@ -314,10 +314,10 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, ASPEED_ADC_BAT_SENSING_ENABLE, data->base + ASPEED_REG_ENGINE_CONTROL); /* - * After enable battery sensing mode need to wait some time for adc stable + * After enable battery sensing mode need to wait some time for ADC stable * Experiment result is 1ms. */ - mdelay(1); + fsleep(1000); *val = readw(data->base + chan->address); *val = (*val * data->battery_mode_gain.mult) / data->battery_mode_gain.div; From 58b98c66e6b0e7bcd0799105dadad263a826d425 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Fri, 20 Mar 2026 13:46:38 +0800 Subject: [PATCH 225/405] iio: adc: aspeed: Reserve battery sensing channel for on-demand use For controllers with battery sensing capability (AST2600/AST2700), the last channel uses a different circuit design optimized for battery voltage measurement. This channel should not be enabled by default along with other channels to avoid potential interference and power efficiency issues. This ensures optimal power efficiency for normal ADC operations while maintaining full functionality when battery sensing is needed. Signed-off-by: Billy Tsai Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 38 +++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index a1a6296d3003..9b828a3c91da 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -138,6 +138,13 @@ static inline u32 aspeed_adc_channels_mask(unsigned int num_channels) static inline unsigned int aspeed_adc_get_active_channels(const struct aspeed_adc_data *data) { + /* + * For controllers with battery sensing capability, the last channel + * is reserved for battery sensing and should not be included in + * normal channel operations. + */ + if (data->model_data->bat_sense_sup) + return data->model_data->num_channels - 1; return data->model_data->num_channels; } @@ -256,8 +263,8 @@ static int aspeed_adc_compensation(struct iio_dev *indio_dev) ASPEED_ADC_CTRL_CHANNEL_ENABLE(0), data->base + ASPEED_REG_ENGINE_CONTROL); /* - * After enable compensating sensing mode need to wait some time for ADC stable - * Experiment result is 1ms. + * After enable compensating sensing mode need to wait some time for the + * ADC stablize. Experiment result is 1ms. */ fsleep(1000); @@ -305,9 +312,26 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: + adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); + /* + * For battery sensing capable controllers, we need to enable + * the specific channel before reading. This is required because + * the battery channel may not be enabled by default. + */ + if (data->model_data->bat_sense_sup && + chan->channel == ASPEED_ADC_BATTERY_CHANNEL) { + u32 ctrl_reg = adc_engine_control_reg_val & ~ASPEED_ADC_CTRL_CHANNEL; + + ctrl_reg |= ASPEED_ADC_CTRL_CHANNEL_ENABLE(chan->channel); + writel(ctrl_reg, data->base + ASPEED_REG_ENGINE_CONTROL); + /* + * After enable a new channel need to wait some time for ADC stable + * Experiment result is 1ms. + */ + fsleep(1000); + } + if (data->battery_sensing && chan->channel == ASPEED_ADC_BATTERY_CHANNEL) { - adc_engine_control_reg_val = - readl(data->base + ASPEED_REG_ENGINE_CONTROL); writel(adc_engine_control_reg_val | FIELD_PREP(ASPEED_ADC_CH7_MODE, ASPEED_ADC_CH7_BAT) | @@ -321,11 +345,11 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, *val = readw(data->base + chan->address); *val = (*val * data->battery_mode_gain.mult) / data->battery_mode_gain.div; - /* Restore control register value */ - writel(adc_engine_control_reg_val, - data->base + ASPEED_REG_ENGINE_CONTROL); } else *val = readw(data->base + chan->address); + /* Restore control register value */ + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: From db0da4b7f688a76951acc38870f71e673f8fcad0 Mon Sep 17 00:00:00 2001 From: Shi Hao Date: Mon, 16 Mar 2026 14:30:09 +0530 Subject: [PATCH 226/405] iio: accel: fix typo celcius to Celsius Fix incorrect spelling in comments - celcius -> Celsius Signed-off-by: Shi Hao Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adis16201.c | 2 +- drivers/iio/accel/adis16209.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c index 5127e58eebc7..ba0f97944c6d 100644 --- a/drivers/iio/accel/adis16201.c +++ b/drivers/iio/accel/adis16201.c @@ -147,7 +147,7 @@ static int adis16201_read_raw(struct iio_dev *indio_dev, /* * The raw ADC value is 1278 when the temperature * is 25 degrees and the scale factor per milli - * degree celcius is -470. + * degree Celsius is -470. */ *val = 25000 / -470 - 1278; return IIO_VAL_INT; diff --git a/drivers/iio/accel/adis16209.c b/drivers/iio/accel/adis16209.c index 41ffd92f27fd..04e169f221c4 100644 --- a/drivers/iio/accel/adis16209.c +++ b/drivers/iio/accel/adis16209.c @@ -186,7 +186,7 @@ static int adis16209_read_raw(struct iio_dev *indio_dev, /* * The raw ADC value is 0x4FE when the temperature * is 45 degrees and the scale factor per milli - * degree celcius is -470. + * degree Celsius is -470. */ *val = 25000 / -470 - 0x4FE; return IIO_VAL_INT; From 0dcaf3db244802283a268bb93b38a7683e476b2a Mon Sep 17 00:00:00 2001 From: Shi Hao Date: Mon, 16 Mar 2026 14:30:10 +0530 Subject: [PATCH 227/405] iio: light: fix several incorrect spellings Fix spelling mistakes reported by codespell. - sesnor -> sensor - substraction -> subtraction - simulataneous -> simultaneous - proccessed -> processed - coefficents -> coefficients Signed-off-by: Shi Hao Signed-off-by: Jonathan Cameron --- drivers/iio/light/Kconfig | 2 +- drivers/iio/light/apds9160.c | 2 +- drivers/iio/light/ltr390.c | 2 +- drivers/iio/light/opt3001.c | 2 +- drivers/iio/light/tsl2772.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index ac1408d374c9..eff33e456c70 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -359,7 +359,7 @@ config ROHM_BU27034 select IIO_KFIFO_BUF help Enable support for the ROHM BU27034 ambient light sensor. ROHM BU27034 - is an ambient light sesnor with 3 channels and 3 photo diodes capable + is an ambient light sensor with 3 channels and 3 photo diodes capable of detecting a very wide range of illuminance. Typical application is adjusting LCD and backlight power of TVs and mobile phones. diff --git a/drivers/iio/light/apds9160.c b/drivers/iio/light/apds9160.c index 9b8af11b7b67..3da0bdac04cf 100644 --- a/drivers/iio/light/apds9160.c +++ b/drivers/iio/light/apds9160.c @@ -620,7 +620,7 @@ static int apds9160_set_ps_gain(struct apds9160_chip *data, int val) /* * The PS intelligent cancellation level register allows - * for an on-chip substraction of the ADC count caused by + * for an on-chip subtraction of the ADC count caused by * unwanted reflected light from PS ADC output. */ static int apds9160_set_ps_cancellation_level(struct apds9160_chip *data, diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c index fc387426fa87..f1702aca582d 100644 --- a/drivers/iio/light/ltr390.c +++ b/drivers/iio/light/ltr390.c @@ -101,7 +101,7 @@ enum ltr390_meas_rate { struct ltr390_data { struct regmap *regmap; struct i2c_client *client; - /* Protects device from simulataneous reads */ + /* Protects device from simultaneous reads */ struct mutex lock; enum ltr390_mode mode; int gain; diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 393a3d2fbe1d..53bc455b7bad 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -91,7 +91,7 @@ struct opt3001_chip_info { */ int factor_integer; /* - * Factor used to align decimal part of proccessed value to six decimal + * Factor used to align decimal part of processed value to six decimal * places. */ int factor_decimal; diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c index 0b171106441a..c8f15ba95267 100644 --- a/drivers/iio/light/tsl2772.c +++ b/drivers/iio/light/tsl2772.c @@ -190,7 +190,7 @@ struct tsl2772_chip { }; /* - * Different devices require different coefficents, and these numbers were + * Different devices require different coefficients, and these numbers were * derived from the 'Lux Equation' section of the various device datasheets. * All of these coefficients assume a Glass Attenuation (GA) factor of 1. * The coefficients are multiplied by 1000 to avoid floating point operations. From a27bace4d5432bc5099a0f657119bebe60c5022a Mon Sep 17 00:00:00 2001 From: Shi Hao Date: Mon, 16 Mar 2026 14:30:11 +0530 Subject: [PATCH 228/405] iio: adc: add an article and use digitize instead of digitalize Use digitize instead of digitalize, which is the correct technical term, and add an article for clarity. Signed-off-by: Shi Hao Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti_am335x_adc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index a1a28584de93..1516dd332f90 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -113,10 +113,10 @@ static void tiadc_step_config(struct iio_dev *indio_dev) * There are 16 configurable steps and 8 analog input * lines available which are shared between Touchscreen and ADC. * - * Steps forwards i.e. from 0 towards 16 are used by ADC - * depending on number of input lines needed. + * Steps forward, i.e. from 0 towards 16, are used by ADC + * depending on the number of input lines needed. * Channel would represent which analog input - * needs to be given to ADC to digitalize data. + * needs to be given to ADC to digitize data. */ for (i = 0; i < adc_dev->channels; i++) { int chan; From d5036cd38aef10a59a08c10baf71a92efdd2d485 Mon Sep 17 00:00:00 2001 From: Shi Hao Date: Mon, 16 Mar 2026 14:30:12 +0530 Subject: [PATCH 229/405] iio: imu: fix typo from adjustement to adjustment Fix incorrect spelling in a comment. - adjustement -> adjustment Signed-off-by: Shi Hao Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c index 6aee6c989485..47394594d17a 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c @@ -125,7 +125,7 @@ static int inv_magn_init(struct inv_mpu6050_state *st) } /* - * Sensitivity adjustement and scale to Gauss + * Sensitivity adjustment and scale to Gauss * * Hadj = H * (((ASA - 128) * 0.5 / 128) + 1) * Factor simplification: From 2354338cc8136fd4e77b92807cf5e0ad89c82e3d Mon Sep 17 00:00:00 2001 From: Shi Hao Date: Mon, 16 Mar 2026 14:30:13 +0530 Subject: [PATCH 230/405] iio: magnetometer: fix various spelling mistakes Fix spelling mistakes in comments. - follwing -> following - atleast -> at least - occured -> occurred - measurment -> measurement - rougly -> roughly Signed-off-by: Shi Hao Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8974.c | 2 +- drivers/iio/magnetometer/ak8975.c | 6 +++--- drivers/iio/magnetometer/yamaha-yas530.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c index 68ece700c7ce..817b18257608 100644 --- a/drivers/iio/magnetometer/ak8974.c +++ b/drivers/iio/magnetometer/ak8974.c @@ -577,7 +577,7 @@ static int ak8974_measure_channel(struct ak8974 *ak8974, unsigned long address, /* * This explicit cast to (s16) is necessary as the measurement * is done in 2's complement with positive and negative values. - * The follwing assignment to *val will then convert the signed + * The following assignment to *val will then convert the signed * s16 value to a signed int value. */ *val = (s16)le16_to_cpu(hw_values[address]); diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index d30315ad85de..b648b0afa573 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -545,7 +545,7 @@ static int ak8975_set_mode(struct ak8975_data *data, enum ak_ctrl_mode mode) return ret; } data->cntl_cache = regval; - /* After mode change wait atleast 100us */ + /* After mode change wait at least 100us */ usleep_range(100, 500); return 0; @@ -697,7 +697,7 @@ static int wait_conversion_complete_polled(struct ak8975_data *data) return read_status; } -/* Returns 0 if the end of conversion interrupt occured or -ETIME otherwise */ +/* Returns 0 if the end of conversion interrupt occurred or -ETIME otherwise */ static int wait_conversion_complete_interrupt(struct ak8975_data *data) { int ret; @@ -759,7 +759,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) if (ret < 0) goto exit; - /* Read out ST2 for release lock on measurment data. */ + /* Read out ST2 for release lock on measurement data. */ ret = i2c_smbus_read_byte_data(client, data->def->ctrl_regs[ST2]); if (ret < 0) { dev_err(&client->dev, "Error in reading ST2\n"); diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index d49e37edcbed..140c422773f6 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -1223,7 +1223,7 @@ static int yas530_measure_offsets(struct yas5xx *yas5xx) * as the values for [x, y1, y2]. The value is +/-31 * but the effect on the raw values is much larger. * The effect of the offset is to bring the measure - * rougly to the center. + * roughly to the center. */ ox = 0; oy1 = 0; From 896b6508acdfa052dfdf91460ee1b2d565d23010 Mon Sep 17 00:00:00 2001 From: Shi Hao Date: Mon, 16 Mar 2026 14:30:14 +0530 Subject: [PATCH 231/405] iio: pressure: fix spelling mistakes in comments Fix several spelling mistakes in comments. - opertion -> operations - transfered -> transferred - usng -> using - externaly -> externally Signed-off-by: Shi Hao Reviewed-by: Matti Vaittinen Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-spi.c | 2 +- drivers/iio/pressure/hsc030pa.c | 2 +- drivers/iio/pressure/rohm-bm1390.c | 2 +- drivers/iio/pressure/zpa2326.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c index 3b90384f17d7..04bf2f5be5b1 100644 --- a/drivers/iio/pressure/bmp280-spi.c +++ b/drivers/iio/pressure/bmp280-spi.c @@ -47,7 +47,7 @@ static int bmp380_regmap_spi_read(void *context, const void *reg, return -EINVAL; /* - * According to the BMP3xx datasheets, for a basic SPI read opertion, + * According to the BMP3xx datasheets, for a basic SPI read operation, * the first byte needs to be dropped and the rest are the requested * data. */ diff --git a/drivers/iio/pressure/hsc030pa.c b/drivers/iio/pressure/hsc030pa.c index 2d00c0656259..d6b18a84f0ab 100644 --- a/drivers/iio/pressure/hsc030pa.c +++ b/drivers/iio/pressure/hsc030pa.c @@ -273,7 +273,7 @@ static const struct hsc_range_config hsc_range_config[HSC_VARIANTS_MAX] = { * @data: structure containing instantiated sensor data * Return: true only if both status bits are zero * - * the two MSB from the first transfered byte contain a status code + * The two MSB from the first transferred byte contain a status code * 00 - normal operation, valid data * 01 - device in factory programming mode * 10 - stale data diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c index dac27fd359ad..08146ca0f91d 100644 --- a/drivers/iio/pressure/rohm-bm1390.c +++ b/drivers/iio/pressure/rohm-bm1390.c @@ -440,7 +440,7 @@ static int bm1390_fifo_flush(struct iio_dev *idev, unsigned int samples) * the timestamps. If we are ran from IRQ, then the * IRQF_ONESHOT has us covered - but if we are ran by the * user-space read we need to disable the IRQ to be on a safe - * side. We do this usng synchronous disable so that if the + * side. We do this using synchronous disable so that if the * IRQ thread is being ran on other CPU we wait for it to be * finished. */ diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index 4923a558a26a..2c68fdf2744e 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -840,7 +840,7 @@ static irqreturn_t zpa2326_handle_threaded_irq(int irq, void *data) complete: /* - * Wake up direct or externaly triggered buffer mode waiters: see + * Wake up direct or externally triggered buffer mode waiters: see * zpa2326_sample_oneshot() and zpa2326_trigger_handler(). */ complete(&priv->data_ready); From 22dd6499c1a67fd3fdb73a7b2e57bc90a464987c Mon Sep 17 00:00:00 2001 From: Shi Hao Date: Mon, 16 Mar 2026 14:30:15 +0530 Subject: [PATCH 232/405] iio: proximity: fix typo from currenly to currently Fix incorrect spelling from currenly to currently. Signed-off-by: Shi Hao Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/sx9324.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c index c7b2d03c23bc..f61eff39751d 100644 --- a/drivers/iio/proximity/sx9324.c +++ b/drivers/iio/proximity/sx9324.c @@ -821,7 +821,7 @@ static const struct sx_common_reg_default sx9324_default_regs[] = { { SX9324_REG_ADV_CTRL10, 0x00, "adv_ctrl10" }, { SX9324_REG_ADV_CTRL11, 0x00, "adv_ctrl11" }, { SX9324_REG_ADV_CTRL12, 0x00, "adv_ctrl12" }, - /* TODO(gwendal): SAR currenly disabled */ + /* TODO(gwendal): SAR currently disabled */ { SX9324_REG_ADV_CTRL13, 0x00, "adv_ctrl13" }, { SX9324_REG_ADV_CTRL14, 0x00, "adv_ctrl14" }, { SX9324_REG_ADV_CTRL15, 0x00, "adv_ctrl15" }, From 761451473febd7777034720b85ba2bbb2c73ec89 Mon Sep 17 00:00:00 2001 From: Shi Hao Date: Mon, 16 Mar 2026 14:30:16 +0530 Subject: [PATCH 233/405] iio: resolver: fix typo from degredation to degradation Fix incorrect spelling from degredation to degradation and fixed up some missing spaces prior to */ Signed-off-by: Shi Hao Signed-off-by: Jonathan Cameron --- drivers/iio/resolver/ad2s1210.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c index 06d9c784f93e..774728c804c0 100644 --- a/drivers/iio/resolver/ad2s1210.c +++ b/drivers/iio/resolver/ad2s1210.c @@ -896,14 +896,14 @@ static const struct iio_event_spec ad2s1210_monitor_signal_event_spec[] = { .mask_separate = BIT(IIO_EV_INFO_VALUE), }, { - /* Sine/cosine DOS overrange fault.*/ + /* Sine/cosine DOS overrange fault. */ .type = IIO_EV_TYPE_THRESH, .dir = IIO_EV_DIR_RISING, - /* Degredation of signal overrange threshold. */ + /* Degradation of signal overrange threshold. */ .mask_separate = BIT(IIO_EV_INFO_VALUE), }, { - /* Sine/cosine DOS mismatch fault.*/ + /* Sine/cosine DOS mismatch fault. */ .type = IIO_EV_TYPE_MAG, .dir = IIO_EV_DIR_RISING, .mask_separate = BIT(IIO_EV_INFO_VALUE), From abf88d037b1bbdc76dec4248e09f7a3f71aae832 Mon Sep 17 00:00:00 2001 From: Shi Hao Date: Mon, 16 Mar 2026 14:30:17 +0530 Subject: [PATCH 234/405] iio: temp: fix spelling mistakes in comments Fix spelling mistakes in comments. - catched -> caught - chanel -> channel Signed-off-by: Shi Hao Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/ltc2983.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index 7dd40d69cce6..38e6f8dfd3b8 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -709,7 +709,7 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data ret = fwnode_property_read_u32(ref, "reg", &thermo->cold_junction_chan); if (ret) /* - * This would be catched later but we can just return + * This would be caught later but we can just return * the error right away. */ return dev_err_ptr_probe(&st->spi->dev, ret, @@ -798,7 +798,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, * For 4wire RTD with rotation, the channel selection cannot be * >=19 since the chann + 1 is used in this configuration. * For 4wire RTDs with kelvin rsense, the rsense channel cannot be - * <=1 since chanel - 1 and channel - 2 are used. + * <=1 since channel - 1 and channel - 2 are used. */ if (rtd->sensor_config & LTC2983_RTD_4_WIRE_MASK) { /* 4-wire */ From 5088fc744837eaba3f7a1c374cf9457a4ddbd87e Mon Sep 17 00:00:00 2001 From: Shi Hao Date: Mon, 16 Mar 2026 14:30:18 +0530 Subject: [PATCH 235/405] iio: test: fix typo from neeeds to needs in comment Fix incorrect spelling from neeeds to needs. Signed-off-by: Shi Hao Reviewed-by: Matti Vaittinen Signed-off-by: Jonathan Cameron --- drivers/iio/test/iio-test-gts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/test/iio-test-gts.c b/drivers/iio/test/iio-test-gts.c index 11250bc905c9..6ffff85ba853 100644 --- a/drivers/iio/test/iio-test-gts.c +++ b/drivers/iio/test/iio-test-gts.c @@ -20,7 +20,7 @@ * * If yes, then adding a test is probably a good idea but please stop for a * moment and consider the effort of changing all the tests when code gets - * refactored. Eventually it neeeds to be. + * refactored. Eventually it needs to be. */ #define TEST_TSEL_50 1 From 96f46405219d9bf62d9fab76e055e3b6059b0bd5 Mon Sep 17 00:00:00 2001 From: Shi Hao Date: Mon, 16 Mar 2026 14:30:19 +0530 Subject: [PATCH 236/405] iio: common: fix spelling mistakes in comments Fix spelling mistakes in comments. - exepects -> expects - fuction -> function - theoritical -> theoretical - appopriate -> appropriate - iio -> IIO Signed-off-by: Shi Hao Signed-off-by: Jonathan Cameron --- drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c | 2 +- drivers/iio/common/hid-sensors/hid-sensor-attributes.c | 2 +- drivers/iio/common/inv_sensors/inv_sensors_timestamp.c | 4 ++-- drivers/iio/common/ms_sensors/ms_sensors_i2c.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c index 82cef4a12442..f34e2bbba2d1 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c @@ -106,7 +106,7 @@ static int cros_ec_sensors_read(struct iio_dev *indio_dev, switch (st->core.type) { case MOTIONSENSE_TYPE_ACCEL: /* - * EC returns data in g, iio exepects m/s^2. + * EC returns data in g, IIO expects m/s^2. * Do not use IIO_G_TO_M_S_2 to avoid precision loss. */ *val = div_s64(val64 * 980665, 10); diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index a61428bfdce3..c115a72832b2 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -346,7 +346,7 @@ int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st, EXPORT_SYMBOL_NS(hid_sensor_write_raw_hyst_rel_value, "IIO_HID"); /* - * This fuction applies the unit exponent to the scale. + * This function applies the unit exponent to the scale. * For example: * 9.806650000 ->exp:2-> val0[980]val1[665000000] * 9.000806000 ->exp:2-> val0[900]val1[80600000] diff --git a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c index 97526ba87b93..e0b10366ed2b 100644 --- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c +++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c @@ -154,7 +154,7 @@ void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, valid = inv_update_chip_period(ts, period); } - /* no previous data, compute theoritical value from interrupt */ + /* no previous data, compute theoretical value from interrupt */ if (ts->timestamp == 0) { /* elapsed time: sensor period * sensor samples number */ interval = (int64_t)ts->period * (int64_t)sample_nb; @@ -185,7 +185,7 @@ void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts, /* * After ODR change the time interval with the previous sample is - * undertermined (depends when the change occures). So we compute the + * undertermined (depends when the change occurs). So we compute the * timestamp from the current interrupt using the new FIFO period, the * total number of samples and the current sample numero. */ diff --git a/drivers/iio/common/ms_sensors/ms_sensors_i2c.c b/drivers/iio/common/ms_sensors/ms_sensors_i2c.c index 588470863681..1960a2ce82a8 100644 --- a/drivers/iio/common/ms_sensors/ms_sensors_i2c.c +++ b/drivers/iio/common/ms_sensors/ms_sensors_i2c.c @@ -96,7 +96,7 @@ EXPORT_SYMBOL_NS(ms_sensors_read_prom_word, "IIO_MEAS_SPEC_SENSORS"); * * Generic ADC conversion & read function for Measurement Specialties * devices. - * The function will issue conversion command, sleep appopriate delay, and + * The function will issue conversion command, sleep appropriate delay, and * issue command to read ADC. * * Return: 0 on success, negative errno otherwise. From 1a18c847c89ce4b301a5870b0feafd931b076005 Mon Sep 17 00:00:00 2001 From: Shi Hao Date: Mon, 16 Mar 2026 14:30:20 +0530 Subject: [PATCH 237/405] iio: chemical: rephrase comment and fix a typo Rephrase the comment and fix a spelling mistake. - insuffient -> insufficient Signed-off-by: Shi Hao Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/bme680_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c index 70f81c4a96ba..b5b2c8587749 100644 --- a/drivers/iio/chemical/bme680_core.c +++ b/drivers/iio/chemical/bme680_core.c @@ -807,7 +807,7 @@ static int bme680_read_gas(struct bme680_data *data, int *comp_gas_res) adc_gas_res = FIELD_GET(BME680_ADC_GAS_RES, gas_regs_val); /* - * occurs if either the gas heating duration was insuffient + * This may occur if either the gas heating duration was insufficient * to reach the target heater temperature or the target * heater temperature was too high for the heater sink to * reach. From 1011a6bd86bc79dcadab04f3326868e62613484a Mon Sep 17 00:00:00 2001 From: Shi Hao Date: Mon, 16 Mar 2026 14:30:21 +0530 Subject: [PATCH 238/405] iio: cdc: fix spelling mistakes in comments Fix spelling mistakes in comments. - becaue -> because - reenable -> re-enable - irq's -> IRQs Signed-off-by: Shi Hao Signed-off-by: Jonathan Cameron --- drivers/iio/cdc/ad7150.c | 2 +- drivers/iio/cdc/ad7746.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/cdc/ad7150.c b/drivers/iio/cdc/ad7150.c index 427d32e398b3..8106a6a83561 100644 --- a/drivers/iio/cdc/ad7150.c +++ b/drivers/iio/cdc/ad7150.c @@ -306,7 +306,7 @@ static int ad7150_write_event_config(struct iio_dev *indio_dev, dir); if (ret) goto error_ret; - /* reenable any irq's we disabled whilst changing mode */ + /* re-enable any IRQs we disabled whilst changing mode */ enable_irq(chip->interrupts[0]); enable_irq(chip->interrupts[1]); } diff --git a/drivers/iio/cdc/ad7746.c b/drivers/iio/cdc/ad7746.c index 8a306d55c72a..cb97e3c978d8 100644 --- a/drivers/iio/cdc/ad7746.c +++ b/drivers/iio/cdc/ad7746.c @@ -606,7 +606,7 @@ static int ad7746_read_channel(struct iio_dev *indio_dev, return ret; /* - * Offset applied internally becaue the _offset userspace interface is + * Offset applied internally because the _offset userspace interface is * needed for the CAP DACs which apply a controllable offset. */ *val = get_unaligned_be24(data) - 0x800000; From 88d699da8ab6ead396809006c65d07b13b19907c Mon Sep 17 00:00:00 2001 From: Shi Hao Date: Mon, 16 Mar 2026 14:30:22 +0530 Subject: [PATCH 239/405] iio: amplifiers: fix typo from Curren to Current Fix incorrect spelling from Curren to Current. Signed-off-by: Shi Hao Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/ada4250.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/amplifiers/ada4250.c b/drivers/iio/amplifiers/ada4250.c index 40f396ea9069..71e361af2074 100644 --- a/drivers/iio/amplifiers/ada4250.c +++ b/drivers/iio/amplifiers/ada4250.c @@ -109,7 +109,7 @@ static int ada4250_set_offset_uv(struct iio_dev *indio_dev, /* * Compute Range and Voltage per LSB for the Sensor Offset Calibration - * Example of computation for Range 1 and Range 2 (Curren Bias Set = AVDD): + * Example of computation for Range 1 and Range 2 (Current Bias Set = AVDD): * Range 1 Range 2 * Gain | Max Vos(mV) | LSB(mV) | Max Vos(mV) | LSB(mV) | * 2 | X1*127 | X1=0.126(AVDD-1) | X1*3*127 | X1*3 | From 54dde4b1ed85a60ce1bcd10cc6783b9a33ea78e3 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 15 Mar 2026 14:21:51 -0500 Subject: [PATCH 240/405] iio: light: as73211: remove duplicate zero init of scan.chan[3] Remove setting scan.chan[3] to zero. Since commit 433b99e92294 ("iio: light: as73211: Ensure buffer holes are zeroed"), the entire scan struct is zeroed before being filled with data, so this is redundant. Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/as73211.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/iio/light/as73211.c b/drivers/iio/light/as73211.c index 32719f584c47..9fe830dac679 100644 --- a/drivers/iio/light/as73211.c +++ b/drivers/iio/light/as73211.c @@ -677,9 +677,6 @@ static irqreturn_t as73211_trigger_handler(int irq __always_unused, void *p) (char *)&scan.chan[0], 3 * sizeof(scan.chan[0])); if (ret < 0) goto done; - - /* Avoid pushing uninitialized data */ - scan.chan[3] = 0; } if (data_result) { From 733bcf18eab0cbcea7b1a85c967dc100945fffc3 Mon Sep 17 00:00:00 2001 From: Chuang Zhu Date: Mon, 16 Mar 2026 02:23:04 +0800 Subject: [PATCH 241/405] iio: adc: ina2xx: add INA236 support The calibration divisor is not directly specified in the datasheet, but can be calculated: I = Current_LSB * Current Current = ShuntVoltage * CAL / calibration_divisor CAL = 0.00512 / (Current_LSB * Rshunt) ShuntVoltage = Vshunt / ShuntVoltage_LSB => I = (0.00512 / (calibration_divisor*ShuntVoltage_LSB)) * (Vshunt / Rshunt) Ohm's law, I = Vshunt / Rshunt => 0.00512 / (calibration_divisor*ShuntVoltage_LSB) = 1 ShuntVoltage_LSB = 2.5 uV = 0.0000025 V => calibration_divisor = 2048 Signed-off-by: Chuang Zhu Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ina2xx-adc.c | 65 +++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 857e1b69d6cd..dd37109a008a 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -121,7 +121,7 @@ static const struct regmap_config ina2xx_regmap_config = { .volatile_reg = ina2xx_is_volatile_reg, }; -enum ina2xx_ids { ina219, ina226 }; +enum ina2xx_ids { ina219, ina226, ina236 }; struct ina2xx_config { const char *name; @@ -175,6 +175,16 @@ static const struct ina2xx_config ina2xx_config[] = { .power_lsb_factor = 25, .chip_id = ina226, }, + [ina236] = { + .name = "ina236", + .config_default = INA226_CONFIG_DEFAULT, + .calibration_value = 2048, + .shunt_voltage_lsb = 2500, + .bus_voltage_shift = 0, + .bus_voltage_lsb = 1600, + .power_lsb_factor = 32, + .chip_id = ina236, + }, }; static int ina2xx_read_raw(struct iio_dev *indio_dev, @@ -499,20 +509,26 @@ static int ina2xx_write_raw(struct iio_dev *indio_dev, break; case IIO_CHAN_INFO_INT_TIME: - if (chip->config->chip_id == ina226) { + switch (chip->config->chip_id) { + case ina226: + case ina236: if (chan->address == INA2XX_SHUNT_VOLTAGE) ret = ina226_set_int_time_vshunt(chip, val2, &tmp); else ret = ina226_set_int_time_vbus(chip, val2, &tmp); - } else { + break; + case ina219: if (chan->address == INA2XX_SHUNT_VOLTAGE) ret = ina219_set_int_time_vshunt(chip, val2, &tmp); else ret = ina219_set_int_time_vbus(chip, val2, &tmp); + break; + default: + ret = -EINVAL; } break; @@ -727,19 +743,27 @@ static int ina2xx_conversion_ready(struct iio_dev *indio_dev) * For now, we do an extra read of the MASK_ENABLE register (INA226) * resp. the BUS_VOLTAGE register (INA219). */ - if (chip->config->chip_id == ina226) { + switch (chip->config->chip_id) { + case ina226: + case ina236: ret = regmap_read(chip->regmap, INA226_MASK_ENABLE, &alert); + if (ret < 0) + return ret; + alert &= INA226_CVRF; - } else { + break; + case ina219: ret = regmap_read(chip->regmap, INA2XX_BUS_VOLTAGE, &alert); + if (ret < 0) + return ret; alert &= INA219_CNVR; + break; + default: + return -EINVAL; } - if (ret < 0) - return ret; - return !!alert; } @@ -998,16 +1022,22 @@ static int ina2xx_probe(struct i2c_client *client) /* Patch the current config register with default. */ val = chip->config->config_default; - if (type == ina226) { + switch (type) { + case ina226: + case ina236: ina226_set_average(chip, INA226_DEFAULT_AVG, &val); ina226_set_int_time_vbus(chip, INA226_DEFAULT_IT, &val); ina226_set_int_time_vshunt(chip, INA226_DEFAULT_IT, &val); - } else { + break; + case ina219: chip->avg = 1; ina219_set_int_time_vbus(chip, INA219_DEFAULT_IT, &val); ina219_set_int_time_vshunt(chip, INA219_DEFAULT_IT, &val); ina219_set_vbus_range_denom(chip, INA219_DEFAULT_BRNG, &val); ina219_set_vshunt_pga_gain(chip, INA219_DEFAULT_PGA, &val); + break; + default: + return -EINVAL; } ret = ina2xx_init(chip, val); @@ -1017,14 +1047,20 @@ static int ina2xx_probe(struct i2c_client *client) } indio_dev->modes = INDIO_DIRECT_MODE; - if (type == ina226) { + switch (type) { + case ina226: + case ina236: indio_dev->channels = ina226_channels; indio_dev->num_channels = ARRAY_SIZE(ina226_channels); indio_dev->info = &ina226_info; - } else { + break; + case ina219: indio_dev->channels = ina219_channels; indio_dev->num_channels = ARRAY_SIZE(ina219_channels); indio_dev->info = &ina219_info; + break; + default: + return -EINVAL; } indio_dev->name = id ? id->name : chip->config->name; @@ -1057,6 +1093,7 @@ static const struct i2c_device_id ina2xx_id[] = { { "ina226", ina226 }, { "ina230", ina226 }, { "ina231", ina226 }, + { "ina236", ina236 }, { } }; MODULE_DEVICE_TABLE(i2c, ina2xx_id); @@ -1082,6 +1119,10 @@ static const struct of_device_id ina2xx_of_match[] = { .compatible = "ti,ina231", .data = (void *)ina226 }, + { + .compatible = "ti,ina236", + .data = (void *)ina236 + }, { } }; MODULE_DEVICE_TABLE(of, ina2xx_of_match); From 1ac30f58f0336287203109872f71a81d4bb271db Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Sun, 15 Mar 2026 17:46:25 +0530 Subject: [PATCH 242/405] iio: st_sensors: drop temporary kmalloc buffer and reuse buffer_data Replace the per-call kmalloc() scratch buffer with the existing buffer_data[] field present in struct st_sensor_data. The existing buffer is DMA-aligned and sufficiently sized for all channel widths, so using it avoids unnecessary dynamic memory allocation on each read. This simplifies the code, removes redundant allocation and cleanup. No functional change intended. Signed-off-by: Sanjay Chitroda Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/common/st_sensors/st_sensors_core.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index dac593be5695..dbc5e16fbde4 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -501,14 +501,12 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev, byte_for_channel = DIV_ROUND_UP(ch->scan_type.realbits + ch->scan_type.shift, 8); - outdata = kmalloc(byte_for_channel, GFP_DMA | GFP_KERNEL); - if (!outdata) - return -ENOMEM; + outdata = sdata->buffer_data; err = regmap_bulk_read(sdata->regmap, ch->address, outdata, byte_for_channel); if (err < 0) - goto st_sensors_free_memory; + return err; if (byte_for_channel == 1) *data = (s8)*outdata; @@ -517,10 +515,7 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev, else if (byte_for_channel == 3) *data = (s32)sign_extend32(get_unaligned_le24(outdata), 23); -st_sensors_free_memory: - kfree(outdata); - - return err; + return 0; } int st_sensors_read_info_raw(struct iio_dev *indio_dev, From 7806c060cceb2d6895efbb6cff2f2f17cf1ec5de Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 14 Mar 2026 16:12:24 -0500 Subject: [PATCH 243/405] iio: adc: ti-ads7950: use iio_push_to_buffers_with_ts_unaligned() Use iio_push_to_buffers_with_ts_unaligned() to avoid unaligned access when writing the timestamp in the rx_buf. The previous implementation would have been fine on architectures that support 4-byte alignment of 64-bit integers but could cause issues on architectures that require 8-byte alignment. Fixes: 902c4b2446d4 ("iio: adc: New driver for TI ADS7950 chips") Signed-off-by: David Lechner Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7950.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index fa3b446495ec..4e9359b259fc 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -47,8 +47,6 @@ #define TI_ADS7950_MAX_CHAN 16 #define TI_ADS7950_NUM_GPIOS 4 -#define TI_ADS7950_TIMESTAMP_SIZE (sizeof(int64_t) / sizeof(__be16)) - /* val = value, dec = left shift, bits = number of bits of the mask */ #define TI_ADS7950_EXTRACT(val, dec, bits) \ (((val) >> (dec)) & ((1 << (bits)) - 1)) @@ -105,8 +103,7 @@ struct ti_ads7950_state { * DMA (thus cache coherency maintenance) may require the * transfer buffers to live in their own cache lines. */ - u16 rx_buf[TI_ADS7950_MAX_CHAN + 2 + TI_ADS7950_TIMESTAMP_SIZE] - __aligned(IIO_DMA_MINALIGN); + u16 rx_buf[TI_ADS7950_MAX_CHAN + 2] __aligned(IIO_DMA_MINALIGN); u16 tx_buf[TI_ADS7950_MAX_CHAN + 2]; u16 single_tx; u16 single_rx; @@ -307,8 +304,10 @@ static irqreturn_t ti_ads7950_trigger_handler(int irq, void *p) if (ret < 0) goto out; - iio_push_to_buffers_with_timestamp(indio_dev, &st->rx_buf[2], - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts_unaligned(indio_dev, &st->rx_buf[2], + sizeof(*st->rx_buf) * + TI_ADS7950_MAX_CHAN, + iio_get_time_ns(indio_dev)); out: mutex_unlock(&st->slock); From b37cce0bac85535d62ad0d1b235bd11a3c06ffdc Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Sat, 14 Mar 2026 15:12:53 +0400 Subject: [PATCH 244/405] iio: adc: ad_sigma_delta: Format block comments Format the multi-line comment in ad_sd_set_comm() according to the kernel multi-line comment style. Suggested-by: Andy Shevchenko Signed-off-by: Giorgi Tchankvetadze Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 7852884703b0..a955556f9ec8 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -51,8 +51,10 @@ */ void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, u8 comm) { - /* Some variants use the lower two bits of the communications register - * to select the channel */ + /* + * Some variants use the lower two bits of the communications register + * to select the channel. + */ sigma_delta->comm = comm & AD_SD_COMM_CHAN_MASK; } EXPORT_SYMBOL_NS_GPL(ad_sd_set_comm, "IIO_AD_SIGMA_DELTA"); From e81f3889c2e29351d67d779b6731bbbc602b5d5f Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 13 Mar 2026 13:57:41 +0200 Subject: [PATCH 245/405] iio: frequency: admv4420: add dev variable Introduce a local struct device variable in admv4420_probe() to simplify subsequent conversions and improve code readability. No functional change. Reviewed-by: Andy Shevchenko Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/admv4420.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/iio/frequency/admv4420.c b/drivers/iio/frequency/admv4420.c index 8748d9747639..6ebfe08fc211 100644 --- a/drivers/iio/frequency/admv4420.c +++ b/drivers/iio/frequency/admv4420.c @@ -344,18 +344,19 @@ static int admv4420_setup(struct iio_dev *indio_dev) static int admv4420_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; struct iio_dev *indio_dev; struct admv4420_state *st; struct regmap *regmap; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; regmap = devm_regmap_init_spi(spi, &admv4420_regmap_config); if (IS_ERR(regmap)) - return dev_err_probe(&spi->dev, PTR_ERR(regmap), + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to initializing spi regmap\n"); st = iio_priv(indio_dev); @@ -373,7 +374,7 @@ static int admv4420_probe(struct spi_device *spi) return ret; } - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct of_device_id admv4420_of_match[] = { From 7428168fe1615423685fa61e4d38bd2e20f14ebd Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 13 Mar 2026 13:57:42 +0200 Subject: [PATCH 246/405] iio: frequency: admv4420: use dev_err_probe Use dev_err_probe() instead of dev_err() in the probe path to ensure proper handling of deferred probing and to simplify error handling. Also fix the format specifier for vco_freq_hz from %lld to %llu since it is u64 (unsigned), and add missing newline to the error message. Reviewed-by: Andy Shevchenko Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/admv4420.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/drivers/iio/frequency/admv4420.c b/drivers/iio/frequency/admv4420.c index 6ebfe08fc211..618511eddfb1 100644 --- a/drivers/iio/frequency/admv4420.c +++ b/drivers/iio/frequency/admv4420.c @@ -279,10 +279,9 @@ static int admv4420_setup(struct iio_dev *indio_dev) if (ret) return ret; - if (val != ADMV4420_SCRATCH_PAD_VAL_1) { - dev_err(dev, "Failed ADMV4420 to read/write scratchpad %x ", val); - return -EIO; - } + if (val != ADMV4420_SCRATCH_PAD_VAL_1) + return dev_err_probe(dev, -EIO, + "Failed ADMV4420 to read/write scratchpad %x\n", val); ret = regmap_write(st->regmap, ADMV4420_SCRATCHPAD, @@ -294,10 +293,9 @@ static int admv4420_setup(struct iio_dev *indio_dev) if (ret) return ret; - if (val != ADMV4420_SCRATCH_PAD_VAL_2) { - dev_err(dev, "Failed to read/write scratchpad %x ", val); - return -EIO; - } + if (val != ADMV4420_SCRATCH_PAD_VAL_2) + return dev_err_probe(dev, -EIO, + "Failed to read/write scratchpad %x\n", val); st->mux_sel = ADMV4420_LOCK_DTCT; st->lo_freq_hz = ADMV4420_DEFAULT_LO_FREQ_HZ; @@ -305,10 +303,10 @@ static int admv4420_setup(struct iio_dev *indio_dev) admv4420_fw_parse(st); ret = admv4420_calc_parameters(st); - if (ret) { - dev_err(dev, "Failed calc parameters for %lld ", st->vco_freq_hz); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "Failed calc parameters for %llu\n", + st->vco_freq_hz); ret = regmap_write(st->regmap, ADMV4420_R_DIV_L, FIELD_GET(0xFF, st->ref_block.divider)); @@ -369,10 +367,8 @@ static int admv4420_probe(struct spi_device *spi) indio_dev->num_channels = ARRAY_SIZE(admv4420_channels); ret = admv4420_setup(indio_dev); - if (ret) { - dev_err(&spi->dev, "Setup ADMV4420 failed (%d)\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Setup ADMV4420 failed\n"); return devm_iio_device_register(dev, indio_dev); } From 9582a65eda4f566df9dc329e70f8be48260a9c9d Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 13 Mar 2026 13:57:43 +0200 Subject: [PATCH 247/405] iio: frequency: ad9523: add dev variable Introduce a local struct device variable in ad9523_probe() to simplify subsequent conversions and improve code readability. Split pdata declaration and assignment since the result is validated immediately after. No functional change. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/ad9523.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c index ad32eb66edca..f4e80ea0c6d2 100644 --- a/drivers/iio/frequency/ad9523.c +++ b/drivers/iio/frequency/ad9523.c @@ -948,17 +948,19 @@ static int ad9523_setup(struct iio_dev *indio_dev) static int ad9523_probe(struct spi_device *spi) { - struct ad9523_platform_data *pdata = dev_get_platdata(&spi->dev); + struct device *dev = &spi->dev; + struct ad9523_platform_data *pdata; struct iio_dev *indio_dev; struct ad9523_state *st; int ret; + pdata = dev_get_platdata(dev); if (!pdata) { dev_err(&spi->dev, "no platform data?\n"); return -EINVAL; } - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (indio_dev == NULL) return -ENOMEM; @@ -966,16 +968,16 @@ static int ad9523_probe(struct spi_device *spi) mutex_init(&st->lock); - ret = devm_regulator_get_enable(&spi->dev, "vcc"); + ret = devm_regulator_get_enable(dev, "vcc"); if (ret) return ret; - st->pwrdown_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown", + st->pwrdown_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); if (IS_ERR(st->pwrdown_gpio)) return PTR_ERR(st->pwrdown_gpio); - st->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", + st->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(st->reset_gpio)) return PTR_ERR(st->reset_gpio); @@ -985,7 +987,7 @@ static int ad9523_probe(struct spi_device *spi) gpiod_direction_output(st->reset_gpio, 1); } - st->sync_gpio = devm_gpiod_get_optional(&spi->dev, "sync", + st->sync_gpio = devm_gpiod_get_optional(dev, "sync", GPIOD_OUT_HIGH); if (IS_ERR(st->sync_gpio)) return PTR_ERR(st->sync_gpio); @@ -1005,7 +1007,7 @@ static int ad9523_probe(struct spi_device *spi) if (ret < 0) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id ad9523_id[] = { From 6849c6356bc314f40328d67f8a8fc8e207b28c29 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 13 Mar 2026 13:57:44 +0200 Subject: [PATCH 248/405] iio: frequency: ad9523: use dev_err_probe Use dev_err_probe() instead of dev_err() in the probe path to ensure proper handling of deferred probing and to simplify error handling. Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/ad9523.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c index f4e80ea0c6d2..ea4d2763564a 100644 --- a/drivers/iio/frequency/ad9523.c +++ b/drivers/iio/frequency/ad9523.c @@ -955,10 +955,8 @@ static int ad9523_probe(struct spi_device *spi) int ret; pdata = dev_get_platdata(dev); - if (!pdata) { - dev_err(&spi->dev, "no platform data?\n"); - return -EINVAL; - } + if (!pdata) + return dev_err_probe(dev, -EINVAL, "no platform data?\n"); indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (indio_dev == NULL) From e8b83499b4cbc8b989f7cd6aaa893b669326e93c Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 11 Mar 2026 22:13:45 -0700 Subject: [PATCH 249/405] iio: st_sensors: correct kernel-doc issues Use the proper kernel-doc format and struct member names to avoid kernel-doc warnings: Warning: include/linux/iio/common/st_sensors.h:184 struct member 'int1' not described in 'st_sensor_data_ready_irq' Warning: ../include/linux/iio/common/st_sensors.h:184 struct member 'int2' not described in 'st_sensor_data_ready_irq' Warning: ../include/linux/iio/common/st_sensors.h:184 struct member 'stat_drdy' not described in 'st_sensor_data_ready_irq' Warning: ../include/linux/iio/common/st_sensors.h:184 struct member 'ig1' not described in 'st_sensor_data_ready_irq' Warning: ../include/linux/iio/common/st_sensors.h:219 struct member 'num_ch' not described in 'st_sensor_settings' Warning: ../include/linux/iio/common/st_sensors.h:263 struct member 'num_data_channels' not described in 'st_sensor_data' Signed-off-by: Randy Dunlap Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- include/linux/iio/common/st_sensors.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index f9ae5cdd884f..1ba496f0fea5 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -160,12 +160,12 @@ struct st_sensor_int_drdy { /** * struct st_sensor_data_ready_irq - ST sensor device data-ready interrupt - * struct int1 - data-ready configuration register for INT1 pin. - * struct int2 - data-ready configuration register for INT2 pin. + * @int1: data-ready configuration register for INT1 pin. + * @int2: data-ready configuration register for INT2 pin. * @addr_ihl: address to enable/disable active low on the INT lines. * @mask_ihl: mask to enable/disable active low on the INT lines. - * struct stat_drdy - status register of DRDY (data ready) interrupt. - * struct ig1 - represents the Interrupt Generator 1 of sensors. + * @stat_drdy: status register of DRDY (data ready) interrupt. + * @ig1: represents the Interrupt Generator 1 of sensors. * @en_addr: address of the enable ig1 register. * @en_mask: mask to write the on/off value for enable. */ @@ -190,6 +190,7 @@ struct st_sensor_data_ready_irq { * @wai_addr: The address of WhoAmI register. * @sensors_supported: List of supported sensors by struct itself. * @ch: IIO channels for the sensor. + * @num_ch: Number of IIO channels in @ch * @odr: Output data rate register and ODR list available. * @pw: Power register of the sensor. * @enable_axis: Enable one or more axis of the sensor. @@ -228,7 +229,7 @@ struct st_sensor_settings { * @regmap: Pointer to specific sensor regmap configuration. * @enabled: Status of the sensor (false->off, true->on). * @odr: Output data rate of the sensor [Hz]. - * num_data_channels: Number of data channels used in buffer. + * @num_data_channels: Number of data channels used in buffer. * @drdy_int_pin: Redirect DRDY on pin 1 (1) or pin 2 (2). * @int_pin_open_drain: Set the interrupt/DRDY to open drain. * @irq: the IRQ number. From a718013647dcd5aca14c9a8856e1dab694e846ae Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 10 Feb 2026 14:50:59 +0100 Subject: [PATCH 250/405] iio: dac: ds4424: refactor raw access to use bitwise operations Refactor the raw access logic to use standard GENMASK() and BIT() macros. Use abs() for magnitude calculation to simplify the logic and make the data flow clearer. Signed-off-by: Oleksij Rempel Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ds4424.c | 55 +++++++++++++++------------------------- 1 file changed, 21 insertions(+), 34 deletions(-) diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c index c61868f2de31..c15eb7b5eb96 100644 --- a/drivers/iio/dac/ds4424.c +++ b/drivers/iio/dac/ds4424.c @@ -5,6 +5,7 @@ * Copyright (C) 2017 Maxim Integrated */ +#include #include #include #include @@ -18,9 +19,10 @@ #define DS4422_MAX_DAC_CHANNELS 2 #define DS4424_MAX_DAC_CHANNELS 4 +#define DS4424_DAC_MASK GENMASK(6, 0) +#define DS4424_DAC_SOURCE BIT(7) + #define DS4424_DAC_ADDR(chan) ((chan) + 0xf8) -#define DS4424_SOURCE_I 1 -#define DS4424_SINK_I 0 #define DS4424_CHANNEL(chan) { \ .type = IIO_CURRENT, \ @@ -30,22 +32,6 @@ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ } -/* - * DS4424 DAC control register 8 bits - * [7] 0: to sink; 1: to source - * [6:0] steps to sink/source - * bit[7] looks like a sign bit, but the value of the register is - * not a two's complement code considering the bit[6:0] is a absolute - * distance from the zero point. - */ -union ds4424_raw_data { - struct { - u8 dx:7; - u8 source_bit:1; - }; - u8 bits; -}; - enum ds4424_device_ids { ID_DS4422, ID_DS4424, @@ -107,21 +93,21 @@ static int ds4424_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { - union ds4424_raw_data raw; - int ret; + int ret, regval; switch (mask) { case IIO_CHAN_INFO_RAW: - ret = ds4424_get_value(indio_dev, val, chan->channel); + ret = ds4424_get_value(indio_dev, ®val, chan->channel); if (ret < 0) { pr_err("%s : ds4424_get_value returned %d\n", __func__, ret); return ret; } - raw.bits = *val; - *val = raw.dx; - if (raw.source_bit == DS4424_SINK_I) + + *val = regval & DS4424_DAC_MASK; + if (!(regval & DS4424_DAC_SOURCE)) *val = -*val; + return IIO_VAL_INT; default: @@ -133,25 +119,26 @@ static int ds4424_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { - union ds4424_raw_data raw; + unsigned int abs_val; if (val2 != 0) return -EINVAL; switch (mask) { case IIO_CHAN_INFO_RAW: - if (val <= S8_MIN || val > S8_MAX) + abs_val = abs(val); + if (abs_val > DS4424_DAC_MASK) return -EINVAL; - if (val > 0) { - raw.source_bit = DS4424_SOURCE_I; - raw.dx = val; - } else { - raw.source_bit = DS4424_SINK_I; - raw.dx = -val; - } + /* + * Currents exiting the IC (Source) are positive. 0 is a valid + * value for no current flow; the direction bit (Source vs Sink) + * is treated as don't-care by the hardware at 0. + */ + if (val > 0) + abs_val |= DS4424_DAC_SOURCE; - return ds4424_set_value(indio_dev, raw.bits, chan); + return ds4424_set_value(indio_dev, abs_val, chan); default: return -EINVAL; From c071adeb72739306bf2960a8f703206979c65196 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 10 Feb 2026 14:51:00 +0100 Subject: [PATCH 251/405] iio: dac: ds4424: ratelimit read errors and use device context Replace pr_err() with dev_err_ratelimited() in the RAW read path to avoid log spam on repeated I2C failures and to include the device context. Use %pe to print errno names for faster debugging. Use the parent device context to identify the physical hardware causing the error. Signed-off-by: Oleksij Rempel Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ds4424.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c index c15eb7b5eb96..9d33a810336f 100644 --- a/drivers/iio/dac/ds4424.c +++ b/drivers/iio/dac/ds4424.c @@ -99,8 +99,9 @@ static int ds4424_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_RAW: ret = ds4424_get_value(indio_dev, ®val, chan->channel); if (ret < 0) { - pr_err("%s : ds4424_get_value returned %d\n", - __func__, ret); + dev_err_ratelimited(indio_dev->dev.parent, + "Failed to read channel %d: %pe\n", + chan->channel, ERR_PTR(ret)); return ret; } From 809b578b99c57cd5cbcfe5e1ef40ae0da6a383ee Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 10 Feb 2026 14:51:01 +0100 Subject: [PATCH 252/405] iio: dac: ds4424: sort headers alphabetically Sort the header inclusions alphabetically. This improves readability and simplifies adding new includes in the future. Group subsystem-specific headers (linux/iio/*) separately at the end to clarify subsystem context. Signed-off-by: Oleksij Rempel Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ds4424.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c index 9d33a810336f..c35becc54985 100644 --- a/drivers/iio/dac/ds4424.c +++ b/drivers/iio/dac/ds4424.c @@ -6,14 +6,15 @@ */ #include +#include +#include +#include #include #include -#include #include -#include -#include -#include + #include +#include #include #define DS4422_MAX_DAC_CHANNELS 2 From d2d5a6cb288ad3d9fb327feb39f478dc40ec9f9c Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 10 Feb 2026 14:51:02 +0100 Subject: [PATCH 253/405] iio: dac: ds4424: rename iio_info struct to avoid ambiguity Rename the static `ds4424_info` structure to `ds4424_iio_info`. The previous name was generic and could be confused with chip-specific data structures (like the upcoming `ds4424_chip_info`). The new name explicitly indicates that this structure holds the IIO framework callbacks. Signed-off-by: Oleksij Rempel Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ds4424.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c index c35becc54985..3a923c539577 100644 --- a/drivers/iio/dac/ds4424.c +++ b/drivers/iio/dac/ds4424.c @@ -196,7 +196,7 @@ static int ds4424_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(ds4424_pm_ops, ds4424_suspend, ds4424_resume); -static const struct iio_info ds4424_info = { +static const struct iio_info ds4424_iio_info = { .read_raw = ds4424_read_raw, .write_raw = ds4424_write_raw, }; @@ -251,7 +251,7 @@ static int ds4424_probe(struct i2c_client *client) indio_dev->channels = ds4424_channels; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->info = &ds4424_info; + indio_dev->info = &ds4424_iio_info; ret = iio_device_register(indio_dev); if (ret < 0) { From 37840446078b47e49ea97dd2a6f20cb5ecd44483 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 10 Feb 2026 14:51:03 +0100 Subject: [PATCH 254/405] iio: dac: ds4424: use device match data for chip info Refactor the driver to use device match data instead of checking ID enums in a switch statement. Define a `ds4424_chip_info` structure to hold variant-specific attributes (currently just the channel count) and attach it directly to the I2C and OF device ID tables. This simplifies the probe function and makes it easier to add support for new variants like DS4402/DS4404. Signed-off-by: Oleksij Rempel Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ds4424.c | 47 ++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c index 3a923c539577..3e72a40d053c 100644 --- a/drivers/iio/dac/ds4424.c +++ b/drivers/iio/dac/ds4424.c @@ -33,9 +33,19 @@ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ } -enum ds4424_device_ids { - ID_DS4422, - ID_DS4424, +struct ds4424_chip_info { + const char *name; + u8 num_channels; +}; + +static const struct ds4424_chip_info ds4422_info = { + .name = "ds4422", + .num_channels = DS4422_MAX_DAC_CHANNELS, +}; + +static const struct ds4424_chip_info ds4424_info = { + .name = "ds4424", + .num_channels = DS4424_MAX_DAC_CHANNELS, }; struct ds4424_data { @@ -203,11 +213,15 @@ static const struct iio_info ds4424_iio_info = { static int ds4424_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); + const struct ds4424_chip_info *chip_info; struct ds4424_data *data; struct iio_dev *indio_dev; int ret; + chip_info = i2c_get_match_data(client); + if (!chip_info) + return -ENODEV; + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; @@ -215,7 +229,7 @@ static int ds4424_probe(struct i2c_client *client) data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); data->client = client; - indio_dev->name = id->name; + indio_dev->name = chip_info->name; data->vcc_reg = devm_regulator_get(&client->dev, "vcc"); if (IS_ERR(data->vcc_reg)) @@ -235,20 +249,7 @@ static int ds4424_probe(struct i2c_client *client) if (ret < 0) goto fail; - switch (id->driver_data) { - case ID_DS4422: - indio_dev->num_channels = DS4422_MAX_DAC_CHANNELS; - break; - case ID_DS4424: - indio_dev->num_channels = DS4424_MAX_DAC_CHANNELS; - break; - default: - dev_err(&client->dev, - "ds4424: Invalid chip id.\n"); - ret = -ENXIO; - goto fail; - } - + indio_dev->num_channels = chip_info->num_channels; indio_dev->channels = ds4424_channels; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &ds4424_iio_info; @@ -277,16 +278,16 @@ static void ds4424_remove(struct i2c_client *client) } static const struct i2c_device_id ds4424_id[] = { - { "ds4422", ID_DS4422 }, - { "ds4424", ID_DS4424 }, + { "ds4422", (kernel_ulong_t)&ds4422_info }, + { "ds4424", (kernel_ulong_t)&ds4424_info }, { } }; MODULE_DEVICE_TABLE(i2c, ds4424_id); static const struct of_device_id ds4424_of_match[] = { - { .compatible = "maxim,ds4422" }, - { .compatible = "maxim,ds4424" }, + { .compatible = "maxim,ds4422", .data = &ds4422_info }, + { .compatible = "maxim,ds4424", .data = &ds4424_info }, { } }; From 5ff37a60b39804c6e505fc2069a3d892b5d787f5 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 10 Feb 2026 14:51:04 +0100 Subject: [PATCH 255/405] iio: dac: ds4424: use fsleep() instead of usleep_range() The DS4422/DS4424 and DS4402/DS4404 datasheets do not specify a minimum delay between power-up (POR) and the availability of the I2C interface. The driver previously used `usleep_range(1000, 1200)` to enforce a ~1ms delay. Replace this with `fsleep(1000)` to allow the kernel to select the most efficient sleep mechanism while retaining the existing conservative delay to ensure device readiness. Signed-off-by: Oleksij Rempel Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ds4424.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c index 3e72a40d053c..f9cdc695032d 100644 --- a/drivers/iio/dac/ds4424.c +++ b/drivers/iio/dac/ds4424.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -244,7 +245,13 @@ static int ds4424_probe(struct i2c_client *client) return ret; } - usleep_range(1000, 1200); + /* + * The datasheet does not specify a power-up to I2C ready time. + * Maintain the existing conservative 1ms delay to ensure the + * device is ready for communication. + */ + fsleep(1 * USEC_PER_MSEC); + ret = ds4424_verify_chip(indio_dev); if (ret < 0) goto fail; From a7622a651d6ede223a25af432d6b29cb3c9337c9 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 10 Feb 2026 14:51:05 +0100 Subject: [PATCH 256/405] dt-bindings: iio: dac: maxim,ds4424: add ds4402/ds4404 Add compatible strings for Maxim DS4402 and DS4404 current DACs. These devices are 5-bit variants of the DS4422/DS4424 family. Signed-off-by: Oleksij Rempel Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/dac/maxim,ds4424.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/dac/maxim,ds4424.yaml b/Documentation/devicetree/bindings/iio/dac/maxim,ds4424.yaml index 264fa7c5fe3a..efe63e6cb55d 100644 --- a/Documentation/devicetree/bindings/iio/dac/maxim,ds4424.yaml +++ b/Documentation/devicetree/bindings/iio/dac/maxim,ds4424.yaml @@ -4,18 +4,21 @@ $id: http://devicetree.org/schemas/iio/dac/maxim,ds4424.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Maxim Integrated DS4422/DS4424 7-bit Sink/Source Current DAC +title: Maxim Integrated DS4402/DS4404 and DS4422/DS4424 Current DACs maintainers: - Ismail Kose description: | - Datasheet publicly available at: + Datasheets publicly available at: + https://datasheets.maximintegrated.com/en/ds/DS4402-DS4404.pdf https://datasheets.maximintegrated.com/en/ds/DS4422-DS4424.pdf properties: compatible: enum: + - maxim,ds4402 + - maxim,ds4404 - maxim,ds4422 - maxim,ds4424 From 1fa14dd130fae3feccdde112ed26f48042cf5d7b Mon Sep 17 00:00:00 2001 From: David Jander Date: Tue, 10 Feb 2026 14:51:06 +0100 Subject: [PATCH 257/405] iio: dac: ds4424: add DS4402/DS4404 device IDs Add I2C/OF IDs for DS4402 and DS4404 and set the correct channel count. Follow-up changes add per-variant scaling based on external Rfs. Co-developed-by: Oleksij Rempel Signed-off-by: Oleksij Rempel Signed-off-by: David Jander Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ds4424.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c index f9cdc695032d..f88e92927bf2 100644 --- a/drivers/iio/dac/ds4424.c +++ b/drivers/iio/dac/ds4424.c @@ -39,6 +39,16 @@ struct ds4424_chip_info { u8 num_channels; }; +static const struct ds4424_chip_info ds4402_info = { + .name = "ds4402", + .num_channels = DS4422_MAX_DAC_CHANNELS, +}; + +static const struct ds4424_chip_info ds4404_info = { + .name = "ds4404", + .num_channels = DS4424_MAX_DAC_CHANNELS, +}; + static const struct ds4424_chip_info ds4422_info = { .name = "ds4422", .num_channels = DS4422_MAX_DAC_CHANNELS, @@ -285,6 +295,8 @@ static void ds4424_remove(struct i2c_client *client) } static const struct i2c_device_id ds4424_id[] = { + { "ds4402", (kernel_ulong_t)&ds4402_info }, + { "ds4404", (kernel_ulong_t)&ds4404_info }, { "ds4422", (kernel_ulong_t)&ds4422_info }, { "ds4424", (kernel_ulong_t)&ds4424_info }, { } @@ -293,6 +305,8 @@ static const struct i2c_device_id ds4424_id[] = { MODULE_DEVICE_TABLE(i2c, ds4424_id); static const struct of_device_id ds4424_of_match[] = { + { .compatible = "maxim,ds4402", .data = &ds4402_info }, + { .compatible = "maxim,ds4404", .data = &ds4404_info }, { .compatible = "maxim,ds4422", .data = &ds4422_info }, { .compatible = "maxim,ds4424", .data = &ds4424_info }, { } From 8d68801a696ce101349db6896c10afb5f8d1c228 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 10 Feb 2026 14:51:07 +0100 Subject: [PATCH 258/405] iio: dac: ds4424: support per-variant output range limits The DS4402/DS4404 variants operate with a 5-bit resolution (31 steps), whereas the DS4422/DS4424 support 7-bit (127 steps). Previously, the driver enforced a hardcoded 7-bit mask (DS4424_DAC_MASK) for all variants. This allowed users to write values exceeding the 5-bit range to DS4402/DS4404 devices, resulting in silent truncation or undefined behavior. Add a `result_mask` field to the chip_info structure to define the valid data range for each variant. Use this mask to: 1. Correctly mask register values in read_raw(). 2. Return -EINVAL in write_raw() if the input value exceeds the variant's capabilities. Signed-off-by: Oleksij Rempel Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ds4424.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c index f88e92927bf2..4eff052c2b61 100644 --- a/drivers/iio/dac/ds4424.c +++ b/drivers/iio/dac/ds4424.c @@ -22,6 +22,7 @@ #define DS4424_MAX_DAC_CHANNELS 4 #define DS4424_DAC_MASK GENMASK(6, 0) +#define DS4404_DAC_MASK GENMASK(4, 0) #define DS4424_DAC_SOURCE BIT(7) #define DS4424_DAC_ADDR(chan) ((chan) + 0xf8) @@ -36,26 +37,31 @@ struct ds4424_chip_info { const char *name; + u8 result_mask; u8 num_channels; }; static const struct ds4424_chip_info ds4402_info = { .name = "ds4402", + .result_mask = DS4404_DAC_MASK, .num_channels = DS4422_MAX_DAC_CHANNELS, }; static const struct ds4424_chip_info ds4404_info = { .name = "ds4404", + .result_mask = DS4404_DAC_MASK, .num_channels = DS4424_MAX_DAC_CHANNELS, }; static const struct ds4424_chip_info ds4422_info = { .name = "ds4422", + .result_mask = DS4424_DAC_MASK, .num_channels = DS4422_MAX_DAC_CHANNELS, }; static const struct ds4424_chip_info ds4424_info = { .name = "ds4424", + .result_mask = DS4424_DAC_MASK, .num_channels = DS4424_MAX_DAC_CHANNELS, }; @@ -65,6 +71,7 @@ struct ds4424_data { uint8_t save[DS4424_MAX_DAC_CHANNELS]; struct regulator *vcc_reg; uint8_t raw[DS4424_MAX_DAC_CHANNELS]; + const struct ds4424_chip_info *chip_info; }; static const struct iio_chan_spec ds4424_channels[] = { @@ -115,6 +122,7 @@ static int ds4424_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { + struct ds4424_data *data = iio_priv(indio_dev); int ret, regval; switch (mask) { @@ -127,7 +135,7 @@ static int ds4424_read_raw(struct iio_dev *indio_dev, return ret; } - *val = regval & DS4424_DAC_MASK; + *val = regval & data->chip_info->result_mask; if (!(regval & DS4424_DAC_SOURCE)) *val = -*val; @@ -142,6 +150,7 @@ static int ds4424_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { + struct ds4424_data *data = iio_priv(indio_dev); unsigned int abs_val; if (val2 != 0) @@ -150,7 +159,7 @@ static int ds4424_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: abs_val = abs(val); - if (abs_val > DS4424_DAC_MASK) + if (abs_val > data->chip_info->result_mask) return -EINVAL; /* @@ -241,6 +250,7 @@ static int ds4424_probe(struct i2c_client *client) i2c_set_clientdata(client, indio_dev); data->client = client; indio_dev->name = chip_info->name; + data->chip_info = chip_info; data->vcc_reg = devm_regulator_get(&client->dev, "vcc"); if (IS_ERR(data->vcc_reg)) From cfeae3ce3e5a5755aeada4930a84cf4cbac3ea10 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 10 Feb 2026 14:51:08 +0100 Subject: [PATCH 259/405] iio: dac: ds4424: convert to regmap Refactor the driver to use the regmap API. Replace the driver-specific mutex and manual shadow buffers with the standard regmap infrastructure for locking and caching. This ensures the cache is populated from hardware at probe, preventing state desynchronization (e.g. across suspend/resume). Define access tables to validate the different register maps of DS44x2 and DS44x4. Signed-off-by: Oleksij Rempel Reviewed-by: Sander Vanheule Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 1 + drivers/iio/dac/ds4424.c | 167 +++++++++++++++++++++------------------ 2 files changed, 93 insertions(+), 75 deletions(-) diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index db9f5c711b3d..cd4870b65415 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -408,6 +408,7 @@ config DPOT_DAC config DS4424 tristate "Maxim Integrated DS4422/DS4424 DAC driver" depends on I2C + select REGMAP_I2C help If you say yes here you get support for Maxim chips DS4422, DS4424. diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c index 4eff052c2b61..ee53614301c6 100644 --- a/drivers/iio/dac/ds4424.c +++ b/drivers/iio/dac/ds4424.c @@ -5,14 +5,17 @@ * Copyright (C) 2017 Maxim Integrated */ +#include #include #include #include #include #include #include +#include #include #include +#include #include #include @@ -66,11 +69,8 @@ static const struct ds4424_chip_info ds4424_info = { }; struct ds4424_data { - struct i2c_client *client; - struct mutex lock; - uint8_t save[DS4424_MAX_DAC_CHANNELS]; + struct regmap *regmap; struct regulator *vcc_reg; - uint8_t raw[DS4424_MAX_DAC_CHANNELS]; const struct ds4424_chip_info *chip_info; }; @@ -81,41 +81,72 @@ static const struct iio_chan_spec ds4424_channels[] = { DS4424_CHANNEL(3), }; -static int ds4424_get_value(struct iio_dev *indio_dev, - int *val, int channel) +static const struct regmap_range ds44x2_ranges[] = { + regmap_reg_range(DS4424_DAC_ADDR(0), DS4424_DAC_ADDR(1)), +}; + +static const struct regmap_range ds44x4_ranges[] = { + regmap_reg_range(DS4424_DAC_ADDR(0), DS4424_DAC_ADDR(3)), +}; + +static const struct regmap_access_table ds44x2_table = { + .yes_ranges = ds44x2_ranges, + .n_yes_ranges = ARRAY_SIZE(ds44x2_ranges), +}; + +static const struct regmap_access_table ds44x4_table = { + .yes_ranges = ds44x4_ranges, + .n_yes_ranges = ARRAY_SIZE(ds44x4_ranges), +}; + +static const struct regmap_config ds44x2_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .max_register = DS4424_DAC_ADDR(1), + .rd_table = &ds44x2_table, + .wr_table = &ds44x2_table, +}; + +static const struct regmap_config ds44x4_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .max_register = DS4424_DAC_ADDR(3), + .rd_table = &ds44x4_table, + .wr_table = &ds44x4_table, +}; + +static int ds4424_init_regmap(struct i2c_client *client, + struct iio_dev *indio_dev) { struct ds4424_data *data = iio_priv(indio_dev); + const struct regmap_config *regmap_config; + u8 vals[DS4424_MAX_DAC_CHANNELS]; int ret; - mutex_lock(&data->lock); - ret = i2c_smbus_read_byte_data(data->client, DS4424_DAC_ADDR(channel)); - if (ret < 0) - goto fail; + if (indio_dev->num_channels == DS4424_MAX_DAC_CHANNELS) + regmap_config = &ds44x4_regmap_config; + else + regmap_config = &ds44x2_regmap_config; - *val = ret; + data->regmap = devm_regmap_init_i2c(client, regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(&client->dev, PTR_ERR(data->regmap), + "Failed to init regmap.\n"); -fail: - mutex_unlock(&data->lock); - return ret; -} + /* + * Prime the cache with the bootloader's configuration. + * regmap_bulk_read() will automatically populate the cache with + * the values read from the hardware. + */ + ret = regmap_bulk_read(data->regmap, DS4424_DAC_ADDR(0), vals, + indio_dev->num_channels); + if (ret) + return dev_err_probe(&client->dev, ret, + "Failed to read hardware values\n"); -static int ds4424_set_value(struct iio_dev *indio_dev, - int val, struct iio_chan_spec const *chan) -{ - struct ds4424_data *data = iio_priv(indio_dev); - int ret; - - mutex_lock(&data->lock); - ret = i2c_smbus_write_byte_data(data->client, - DS4424_DAC_ADDR(chan->channel), val); - if (ret < 0) - goto fail; - - data->raw[chan->channel] = val; - -fail: - mutex_unlock(&data->lock); - return ret; + return 0; } static int ds4424_read_raw(struct iio_dev *indio_dev, @@ -123,11 +154,13 @@ static int ds4424_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct ds4424_data *data = iio_priv(indio_dev); - int ret, regval; + unsigned int regval; + int ret; switch (mask) { case IIO_CHAN_INFO_RAW: - ret = ds4424_get_value(indio_dev, ®val, chan->channel); + ret = regmap_read(data->regmap, DS4424_DAC_ADDR(chan->channel), + ®val); if (ret < 0) { dev_err_ratelimited(indio_dev->dev.parent, "Failed to read channel %d: %pe\n", @@ -170,58 +203,44 @@ static int ds4424_write_raw(struct iio_dev *indio_dev, if (val > 0) abs_val |= DS4424_DAC_SOURCE; - return ds4424_set_value(indio_dev, abs_val, chan); + return regmap_write(data->regmap, DS4424_DAC_ADDR(chan->channel), + abs_val); default: return -EINVAL; } } -static int ds4424_verify_chip(struct iio_dev *indio_dev) -{ - int ret, val; - - ret = ds4424_get_value(indio_dev, &val, 0); - if (ret < 0) - dev_err(&indio_dev->dev, - "%s failed. ret: %d\n", __func__, ret); - - return ret; -} - static int ds4424_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ds4424_data *data = iio_priv(indio_dev); - int ret = 0; - int i; + u8 zero_buf[DS4424_MAX_DAC_CHANNELS] = { }; + int ret; - for (i = 0; i < indio_dev->num_channels; i++) { - data->save[i] = data->raw[i]; - ret = ds4424_set_value(indio_dev, 0, - &indio_dev->channels[i]); - if (ret < 0) - return ret; + /* Disable all outputs, bypass cache so the '0' isn't saved */ + regcache_cache_bypass(data->regmap, true); + ret = regmap_bulk_write(data->regmap, DS4424_DAC_ADDR(0), + zero_buf, indio_dev->num_channels); + regcache_cache_bypass(data->regmap, false); + if (ret) { + dev_err(dev, "Failed to zero outputs: %pe\n", ERR_PTR(ret)); + return ret; } - return ret; + + regcache_cache_only(data->regmap, true); + regcache_mark_dirty(data->regmap); + + return 0; } static int ds4424_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ds4424_data *data = iio_priv(indio_dev); - int ret = 0; - int i; - for (i = 0; i < indio_dev->num_channels; i++) { - ret = ds4424_set_value(indio_dev, data->save[i], - &indio_dev->channels[i]); - if (ret < 0) - return ret; - } - return ret; + regcache_cache_only(data->regmap, false); + return regcache_sync(data->regmap); } static DEFINE_SIMPLE_DEV_PM_OPS(ds4424_pm_ops, ds4424_suspend, ds4424_resume); @@ -248,7 +267,6 @@ static int ds4424_probe(struct i2c_client *client) data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); - data->client = client; indio_dev->name = chip_info->name; data->chip_info = chip_info; @@ -257,7 +275,6 @@ static int ds4424_probe(struct i2c_client *client) return dev_err_probe(&client->dev, PTR_ERR(data->vcc_reg), "Failed to get vcc-supply regulator.\n"); - mutex_init(&data->lock); ret = regulator_enable(data->vcc_reg); if (ret < 0) { dev_err(&client->dev, @@ -272,15 +289,15 @@ static int ds4424_probe(struct i2c_client *client) */ fsleep(1 * USEC_PER_MSEC); - ret = ds4424_verify_chip(indio_dev); - if (ret < 0) - goto fail; - indio_dev->num_channels = chip_info->num_channels; indio_dev->channels = ds4424_channels; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &ds4424_iio_info; + ret = ds4424_init_regmap(client, indio_dev); + if (ret) + goto fail; + ret = iio_device_register(indio_dev); if (ret < 0) { dev_err(&client->dev, From f789b8cc39f0dc46b1654aff84e420927bea4272 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 10 Feb 2026 14:51:09 +0100 Subject: [PATCH 260/405] dt-bindings: iio: dac: maxim,ds4424: add maxim,rfs-ohms property The Maxim DS4422/DS4424 and DS4402/DS4404 current DACs determine their full-scale output current via external resistors (Rfs) connected to the FSx pins. Without knowing these values, the full-scale range of the hardware is undefined. Add the 'maxim,rfs-ohms' property to describe these physical components. This property is required to provide a complete description of the hardware configuration. Signed-off-by: Oleksij Rempel Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- .../bindings/iio/dac/maxim,ds4424.yaml | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/dac/maxim,ds4424.yaml b/Documentation/devicetree/bindings/iio/dac/maxim,ds4424.yaml index efe63e6cb55d..4323df2036ac 100644 --- a/Documentation/devicetree/bindings/iio/dac/maxim,ds4424.yaml +++ b/Documentation/devicetree/bindings/iio/dac/maxim,ds4424.yaml @@ -27,9 +27,43 @@ properties: vcc-supply: true + maxim,rfs-ohms: + description: | + Array of resistance values in Ohms for the external Rfs resistors + connected to the FS pins. These values determine the full-scale + output current. The actual resistance depends on the chip variant + and specific hardware design requirements. + minItems: 2 + maxItems: 4 + required: - compatible - reg + - maxim,rfs-ohms + +allOf: + - if: + properties: + compatible: + contains: + enum: + - maxim,ds4402 + - maxim,ds4422 + then: + properties: + maxim,rfs-ohms: + maxItems: 2 + - if: + properties: + compatible: + contains: + enum: + - maxim,ds4404 + - maxim,ds4424 + then: + properties: + maxim,rfs-ohms: + minItems: 4 additionalProperties: false @@ -43,6 +77,7 @@ examples: compatible = "maxim,ds4424"; reg = <0x10>; /* When A0, A1 pins are ground */ vcc-supply = <&vcc_3v3>; + maxim,rfs-ohms = <40000>, <40000>, <40000>, <40000>; }; }; ... From af980a79bfed43c4a0be12cca786be46f1a0c5e8 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 10 Feb 2026 14:51:10 +0100 Subject: [PATCH 261/405] iio: dac: ds4424: add Rfs-based scale and per-variant limits Parse optional maxim,rfs-ohms values to derive the per-channel output current scale (mA per step) for the IIO current ABI. Behavior changes: - If maxim,rfs-ohms is present, IIO_CHAN_INFO_SCALE becomes available and reports mA/step derived from Rfs. - If maxim,rfs-ohms is missing, SCALE is not exposed to keep older DTs working without requiring updates. Signed-off-by: Oleksij Rempel Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ds4424.c | 81 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c index ee53614301c6..085f73de3f02 100644 --- a/drivers/iio/dac/ds4424.c +++ b/drivers/iio/dac/ds4424.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -38,32 +39,51 @@ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ } +#define DS4424_CHANNEL_WITH_SCALE(chan) { \ + .type = IIO_CURRENT, \ + .indexed = 1, \ + .output = 1, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ +} + struct ds4424_chip_info { const char *name; + int vref_mV; + int scale_denom; u8 result_mask; u8 num_channels; }; static const struct ds4424_chip_info ds4402_info = { .name = "ds4402", + .vref_mV = 1230, + .scale_denom = 4, .result_mask = DS4404_DAC_MASK, .num_channels = DS4422_MAX_DAC_CHANNELS, }; static const struct ds4424_chip_info ds4404_info = { .name = "ds4404", + .vref_mV = 1230, + .scale_denom = 4, .result_mask = DS4404_DAC_MASK, .num_channels = DS4424_MAX_DAC_CHANNELS, }; static const struct ds4424_chip_info ds4422_info = { .name = "ds4422", + .vref_mV = 976, + .scale_denom = 16, .result_mask = DS4424_DAC_MASK, .num_channels = DS4422_MAX_DAC_CHANNELS, }; static const struct ds4424_chip_info ds4424_info = { .name = "ds4424", + .vref_mV = 976, + .scale_denom = 16, .result_mask = DS4424_DAC_MASK, .num_channels = DS4424_MAX_DAC_CHANNELS, }; @@ -72,6 +92,8 @@ struct ds4424_data { struct regmap *regmap; struct regulator *vcc_reg; const struct ds4424_chip_info *chip_info; + u32 rfs_ohms[DS4424_MAX_DAC_CHANNELS]; + bool has_rfs; }; static const struct iio_chan_spec ds4424_channels[] = { @@ -81,6 +103,13 @@ static const struct iio_chan_spec ds4424_channels[] = { DS4424_CHANNEL(3), }; +static const struct iio_chan_spec ds4424_channels_with_scale[] = { + DS4424_CHANNEL_WITH_SCALE(0), + DS4424_CHANNEL_WITH_SCALE(1), + DS4424_CHANNEL_WITH_SCALE(2), + DS4424_CHANNEL_WITH_SCALE(3), +}; + static const struct regmap_range ds44x2_ranges[] = { regmap_reg_range(DS4424_DAC_ADDR(0), DS4424_DAC_ADDR(1)), }; @@ -173,6 +202,15 @@ static int ds4424_read_raw(struct iio_dev *indio_dev, *val = -*val; return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (!data->has_rfs) + return -EINVAL; + + /* SCALE is mA/step: mV / Ohm = mA. */ + *val = data->chip_info->vref_mV; + *val2 = data->rfs_ohms[chan->channel] * + data->chip_info->scale_denom; + return IIO_VAL_FRACTIONAL; default: return -EINVAL; @@ -211,6 +249,39 @@ static int ds4424_write_raw(struct iio_dev *indio_dev, } } +static int ds4424_parse_rfs(struct i2c_client *client, + struct ds4424_data *data, + struct iio_dev *indio_dev) +{ + struct device *dev = &client->dev; + int count, ret; + + if (!device_property_present(dev, "maxim,rfs-ohms")) + return 0; + + count = device_property_count_u32(dev, "maxim,rfs-ohms"); + if (count < 0) + return dev_err_probe(dev, count, "Failed to count maxim,rfs-ohms entries\n"); + if (count != indio_dev->num_channels) + return dev_err_probe(dev, -EINVAL, "maxim,rfs-ohms must have %u entries\n", + indio_dev->num_channels); + + ret = device_property_read_u32_array(dev, "maxim,rfs-ohms", + data->rfs_ohms, + indio_dev->num_channels); + if (ret) + return dev_err_probe(dev, ret, "Failed to read maxim,rfs-ohms property\n"); + + for (unsigned int i = 0; i < indio_dev->num_channels; i++) { + if (!data->rfs_ohms[i]) + return dev_err_probe(dev, -EINVAL, "maxim,rfs-ohms entry %u is zero\n", i); + } + + data->has_rfs = true; + + return 0; +} + static int ds4424_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); @@ -290,7 +361,6 @@ static int ds4424_probe(struct i2c_client *client) fsleep(1 * USEC_PER_MSEC); indio_dev->num_channels = chip_info->num_channels; - indio_dev->channels = ds4424_channels; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &ds4424_iio_info; @@ -298,6 +368,15 @@ static int ds4424_probe(struct i2c_client *client) if (ret) goto fail; + ret = ds4424_parse_rfs(client, data, indio_dev); + if (ret) + goto fail; + + if (data->has_rfs) + indio_dev->channels = ds4424_channels_with_scale; + else + indio_dev->channels = ds4424_channels; + ret = iio_device_register(indio_dev); if (ret < 0) { dev_err(&client->dev, From f63ddbcc3299047e7026b9324520aa826794f0c5 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Tue, 3 Mar 2026 15:08:41 -0500 Subject: [PATCH 262/405] fpga: m10bmc-sec: switch show_canceled_csk() to using sysfs_emit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch show_canceled_csk() to use the proper sysfs_emit("%*pbl"). Reviewed-by: Russ Weight Suggested-by: Thomas Weißschuh Signed-off-by: Yury Norov [ Yilun: Remove unnecessary header file ] Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260303200842.124996-6-ynorov@nvidia.com Signed-off-by: Xu Yilun --- drivers/fpga/intel-m10-bmc-sec-update.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/fpga/intel-m10-bmc-sec-update.c b/drivers/fpga/intel-m10-bmc-sec-update.c index 10f678b9ed36..b15dab6a39a3 100644 --- a/drivers/fpga/intel-m10-bmc-sec-update.c +++ b/drivers/fpga/intel-m10-bmc-sec-update.c @@ -183,7 +183,7 @@ show_canceled_csk(struct device *dev, u32 addr, char *buf) bitmap_from_arr32(csk_map, csk32, CSK_BIT_LEN); bitmap_complement(csk_map, csk_map, CSK_BIT_LEN); - return bitmap_print_to_pagebuf(1, buf, csk_map, CSK_BIT_LEN); + return sysfs_emit(buf, "%*pbl\n", CSK_BIT_LEN, csk_map); } #define DEVICE_ATTR_SEC_CSK_RO(_name) \ From 2f5bdca14ce88dd78998699ab25d129b9b3bc200 Mon Sep 17 00:00:00 2001 From: Nikhil Gautam Date: Wed, 25 Mar 2026 17:12:51 +0530 Subject: [PATCH 263/405] iio: accel: adxl380: fix typo in PART_ID register macro Fix a typo in the ADXL380_PART_ID_REG macro name where it was incorrectly defined as ADLX380_PART_ID_REG. Also update its usage in adxl380_setup(). Signed-off-by: Nikhil Gautam Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl380.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/accel/adxl380.c b/drivers/iio/accel/adxl380.c index 8fab2fdbe147..2fc838a234c9 100644 --- a/drivers/iio/accel/adxl380.c +++ b/drivers/iio/accel/adxl380.c @@ -31,7 +31,7 @@ #define ADXL319_ID_VAL 382 #define ADXL380_DEVID_AD_REG 0x00 -#define ADLX380_PART_ID_REG 0x02 +#define ADXL380_PART_ID_REG 0x02 #define ADXL380_X_DATA_H_REG 0x15 #define ADXL380_Y_DATA_H_REG 0x17 @@ -1878,7 +1878,7 @@ static int adxl380_setup(struct iio_dev *indio_dev) if (reg_val != ADXL380_DEVID_AD_VAL) dev_warn(st->dev, "Unknown chip id %x\n", reg_val); - ret = regmap_bulk_read(st->regmap, ADLX380_PART_ID_REG, + ret = regmap_bulk_read(st->regmap, ADXL380_PART_ID_REG, &st->transf_buf, 2); if (ret) return ret; From 732df35bbb94c06099cf8c64076233347b278516 Mon Sep 17 00:00:00 2001 From: Kyle Hsieh Date: Wed, 25 Mar 2026 10:24:20 +0800 Subject: [PATCH 264/405] dt-bindings: adc: ltc2497: add support for ltc2305 Add documentation for the 2-channel LTC2305 ADC in the existing ltc2497 binding. This enables automatic device tree matching for LTC2305 while using the LTC2309 driver (drivers/iio/adc/ltc2309.c), since both ADCs share the same I2C interface and 12-bit SAR architecture. The main difference is the number of channels (LTC2305: 2, LTC2309: 8). Reviewed-by: Rob Herring (Arm) Signed-off-by: Kyle Hsieh Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/lltc,ltc2497.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml b/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml index 5cc6a9684077..c884b6e03767 100644 --- a/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml +++ b/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml @@ -11,6 +11,12 @@ maintainers: - Liam Beguin description: | + LTC2305: + low noise, low power, 2-channel, 12-bit successive approximation ADC with an + I2C compatible serial interface. + + https://www.analog.com/media/en/technical-documentation/data-sheets/23015fb.pdf + LTC2309: low noise, low power, 8-channel, 12-bit successive approximation ADC with an I2C compatible serial interface. @@ -28,6 +34,7 @@ description: | properties: compatible: enum: + - lltc,ltc2305 - lltc,ltc2309 - lltc,ltc2497 - lltc,ltc2499 From 999ca38066302347c94fda49db7a33ce87def5b6 Mon Sep 17 00:00:00 2001 From: Kyle Hsieh Date: Wed, 25 Mar 2026 10:24:21 +0800 Subject: [PATCH 265/405] iio: adc: ltc2309: explicitly assign hex values to channel enums The current ltc2309_channels enum relies on implicit sequential assignment. While this works for the 8-channel LTC2309, it is not intuitive and makes it difficult to support other chips in the same family that might have different bit mappings. Explicitly assign hex values to the enum members based on the channel selection bits defined in the datasheet. This improves code readability and provides a consistent pattern for future chip support. Signed-off-by: Kyle Hsieh Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ltc2309.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/iio/adc/ltc2309.c b/drivers/iio/adc/ltc2309.c index 5f0d947d0615..3f27ffc66668 100644 --- a/drivers/iio/adc/ltc2309.c +++ b/drivers/iio/adc/ltc2309.c @@ -42,22 +42,22 @@ struct ltc2309 { /* Order matches expected channel address, See datasheet Table 1. */ enum ltc2309_channels { - LTC2309_CH0_CH1 = 0, - LTC2309_CH2_CH3, - LTC2309_CH4_CH5, - LTC2309_CH6_CH7, - LTC2309_CH1_CH0, - LTC2309_CH3_CH2, - LTC2309_CH5_CH4, - LTC2309_CH7_CH6, - LTC2309_CH0, - LTC2309_CH2, - LTC2309_CH4, - LTC2309_CH6, - LTC2309_CH1, - LTC2309_CH3, - LTC2309_CH5, - LTC2309_CH7, + LTC2309_CH0_CH1 = 0x0, + LTC2309_CH2_CH3 = 0x1, + LTC2309_CH4_CH5 = 0x2, + LTC2309_CH6_CH7 = 0x3, + LTC2309_CH1_CH0 = 0x4, + LTC2309_CH3_CH2 = 0x5, + LTC2309_CH5_CH4 = 0x6, + LTC2309_CH7_CH6 = 0x7, + LTC2309_CH0 = 0x8, + LTC2309_CH2 = 0x9, + LTC2309_CH4 = 0xa, + LTC2309_CH6 = 0xb, + LTC2309_CH1 = 0xc, + LTC2309_CH3 = 0xd, + LTC2309_CH5 = 0xe, + LTC2309_CH7 = 0xf, }; #define LTC2309_CHAN(_chan, _addr) { \ From 8625d418d24bc0ff463267b26b7cb2e7a612495f Mon Sep 17 00:00:00 2001 From: Kyle Hsieh Date: Wed, 25 Mar 2026 10:24:22 +0800 Subject: [PATCH 266/405] iio: adc: ltc2309: add support for ltc2305 Add support for the LTC2305 ADC to the LTC2309 driver. The LTC2305 is a 2-channel, 12-bit SAR ADC that is register-compatible with the LTC2309 but has a different channel selection mapping and count. To support multiple chips in this family, introduce ltc2309_chip_info struct to store chip-specific channel specifications and names. The probe function now uses i2c_get_match_data() to retrieve the correct configuration for the detected device. Specific channel addresses for LTC2305 (CH0, CH1, and differential pairs) are added based on the datasheet. Signed-off-by: Kyle Hsieh Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ltc2309.c | 49 +++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/ltc2309.c b/drivers/iio/adc/ltc2309.c index 3f27ffc66668..316256edf150 100644 --- a/drivers/iio/adc/ltc2309.c +++ b/drivers/iio/adc/ltc2309.c @@ -1,8 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 /* + * The LTC2305 is a 2-Channel, 12-Bit SAR ADC with an I2C Interface. * The LTC2309 is an 8-Channel, 12-Bit SAR ADC with an I2C Interface. * * Datasheet: + * https://www.analog.com/media/en/technical-documentation/data-sheets/23015fb.pdf * https://www.analog.com/media/en/technical-documentation/data-sheets/2309fd.pdf * * Copyright (c) 2023, Liam Beguin @@ -41,6 +43,13 @@ struct ltc2309 { }; /* Order matches expected channel address, See datasheet Table 1. */ +enum ltc2305_channels { + LTC2305_CH0_CH1 = 0x0, + LTC2305_CH1_CH0 = 0x4, + LTC2305_CH0 = 0x8, + LTC2305_CH1 = 0xc, +}; + enum ltc2309_channels { LTC2309_CH0_CH1 = 0x0, LTC2309_CH2_CH3 = 0x1, @@ -80,6 +89,13 @@ enum ltc2309_channels { .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ } +static const struct iio_chan_spec ltc2305_channels[] = { + LTC2309_CHAN(0, LTC2305_CH0), + LTC2309_CHAN(1, LTC2305_CH1), + LTC2309_DIFF_CHAN(0, 1, LTC2305_CH0_CH1), + LTC2309_DIFF_CHAN(1, 0, LTC2305_CH1_CH0), +}; + static const struct iio_chan_spec ltc2309_channels[] = { LTC2309_CHAN(0, LTC2309_CH0), LTC2309_CHAN(1, LTC2309_CH1), @@ -99,6 +115,24 @@ static const struct iio_chan_spec ltc2309_channels[] = { LTC2309_DIFF_CHAN(7, 6, LTC2309_CH7_CH6), }; +struct ltc2309_chip_info { + const char *name; + const struct iio_chan_spec *channels; + int num_channels; +}; + +static const struct ltc2309_chip_info ltc2305_chip_info = { + .name = "ltc2305", + .channels = ltc2305_channels, + .num_channels = ARRAY_SIZE(ltc2305_channels), +}; + +static const struct ltc2309_chip_info ltc2309_chip_info = { + .name = "ltc2309", + .channels = ltc2309_channels, + .num_channels = ARRAY_SIZE(ltc2309_channels), +}; + static int ltc2309_read_raw_channel(struct ltc2309 *ltc2309, unsigned long address, int *val) { @@ -158,6 +192,7 @@ static const struct iio_info ltc2309_info = { static int ltc2309_probe(struct i2c_client *client) { + const struct ltc2309_chip_info *chip_info; struct iio_dev *indio_dev; struct ltc2309 *ltc2309; int ret; @@ -167,13 +202,15 @@ static int ltc2309_probe(struct i2c_client *client) return -ENOMEM; ltc2309 = iio_priv(indio_dev); + chip_info = i2c_get_match_data(client); + ltc2309->dev = &indio_dev->dev; ltc2309->client = client; - indio_dev->name = "ltc2309"; + indio_dev->name = chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = ltc2309_channels; - indio_dev->num_channels = ARRAY_SIZE(ltc2309_channels); + indio_dev->channels = chip_info->channels; + indio_dev->num_channels = chip_info->num_channels; indio_dev->info = <c2309_info; ret = devm_regulator_get_enable_read_voltage(&client->dev, "vref"); @@ -189,13 +226,15 @@ static int ltc2309_probe(struct i2c_client *client) } static const struct of_device_id ltc2309_of_match[] = { - { .compatible = "lltc,ltc2309" }, + { .compatible = "lltc,ltc2305", .data = <c2305_chip_info }, + { .compatible = "lltc,ltc2309", .data = <c2309_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ltc2309_of_match); static const struct i2c_device_id ltc2309_id[] = { - { "ltc2309" }, + { "ltc2305", (kernel_ulong_t)<c2305_chip_info }, + { "ltc2309", (kernel_ulong_t)<c2309_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, ltc2309_id); From 8abf158b84ca34d5a37566b8d9a641f8a06f7eae Mon Sep 17 00:00:00 2001 From: Neel Bullywon Date: Mon, 23 Mar 2026 19:33:16 -0400 Subject: [PATCH 267/405] iio: frequency: adf4350: replace TODO with NOTE in adf4350_set_freq() Replace the TODO comment in adf4350_set_freq() with a NOTE explaining that a constant-time approach using fls_long() was attempted but deemed more complex without meaningful benefit for initialization code. Signed-off-by: Neel Bullywon Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adf4350.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index 3883b63dcc3c..6bbb6a8dd9d0 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -152,10 +152,10 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq) st->r4_rf_div_sel = 0; /* - * !\TODO: The below computation is making sure we get a power of 2 - * shift (st->r4_rf_div_sel) so that freq becomes higher or equal to - * ADF4350_MIN_VCO_FREQ. This might be simplified with fls()/fls_long() - * and friends. + * NOTE: This iteratively shifts freq by a power of 2 + * (st->r4_rf_div_sel) to meet or exceed ADF4350_MIN_VCO_FREQ. + * A constant-time approach using fls_long() was attempted but + * deemed more complex without meaningful benefit for init code. */ while (freq < ADF4350_MIN_VCO_FREQ) { freq <<= 1; From 7198b881fb00526f6e1125bba0a24e7dc8d95a90 Mon Sep 17 00:00:00 2001 From: Gabriel Rondon Date: Mon, 23 Mar 2026 21:56:19 +0000 Subject: [PATCH 268/405] iio: accel: bmc150-accel-core: use sysfs_emit() in show functions Replace sprintf() with sysfs_emit() in sysfs attribute show callbacks. sysfs_emit() is the preferred API as it is aware of the sysfs buffer page size limit. Signed-off-by: Gabriel Rondon Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bmc150-accel-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 42ccf0316ce5..2398eb7e12cd 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -851,7 +851,7 @@ static ssize_t bmc150_accel_get_fifo_watermark(struct device *dev, wm = data->watermark; mutex_unlock(&data->mutex); - return sprintf(buf, "%d\n", wm); + return sysfs_emit(buf, "%d\n", wm); } static ssize_t bmc150_accel_get_fifo_state(struct device *dev, @@ -866,7 +866,7 @@ static ssize_t bmc150_accel_get_fifo_state(struct device *dev, state = data->fifo_mode; mutex_unlock(&data->mutex); - return sprintf(buf, "%d\n", state); + return sysfs_emit(buf, "%d\n", state); } static const struct iio_mount_matrix * From 6b4cd7b76ee7ed4fb6c74d3876a73979b3669536 Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Mon, 23 Mar 2026 10:46:41 +0400 Subject: [PATCH 269/405] iio: adc: max11410: make vref register name arrays static const The vrefp_regs and vrefn_regs arrays are constant lookup tables and are not modified. Make them static const so they are not reinitialized on each probe call and are placed in read-only memory. Mark the pointer array as const as well to prevent unintended modification. Signed-off-by: Giorgi Tchankvetadze Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max11410.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c index 511b2f14dfaf..69351f4f10bb 100644 --- a/drivers/iio/adc/max11410.c +++ b/drivers/iio/adc/max11410.c @@ -912,8 +912,8 @@ static int max11410_self_calibrate(struct max11410_state *st) static int max11410_probe(struct spi_device *spi) { - const char *vrefp_regs[] = { "vref0p", "vref1p", "vref2p" }; - const char *vrefn_regs[] = { "vref0n", "vref1n", "vref2n" }; + static const char * const vrefp_regs[] = { "vref0p", "vref1p", "vref2p" }; + static const char * const vrefn_regs[] = { "vref0n", "vref1n", "vref2n" }; struct device *dev = &spi->dev; struct max11410_state *st; struct iio_dev *indio_dev; From 2c9225e8d2ca91f106c7b48cb97b0f745e7784a8 Mon Sep 17 00:00:00 2001 From: Siratul Islam Date: Thu, 26 Mar 2026 02:19:41 +0600 Subject: [PATCH 270/405] dt-bindings: iio: proximity: add ST VL53L1X ToF sensor Add device tree binding documentation for the STMicroelectronics VL53L1X Time-of-Flight ranging sensor connected via I2C. vdd-supply is not made globally required to maintain backwards compatibility with existing st,vl53l0x devicetrees that do not specify it. Signed-off-by: Siratul Islam Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jonathan Cameron --- .../bindings/iio/proximity/st,vl53l0x.yaml | 22 ++++++++++++++++--- MAINTAINERS | 6 +++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml b/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml index 322befc41de6..f7f8be1e379d 100644 --- a/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml +++ b/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml @@ -4,14 +4,17 @@ $id: http://devicetree.org/schemas/iio/proximity/st,vl53l0x.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: ST VL53L0X ToF ranging sensor +title: ST VL53L0X/VL53L1X ToF ranging sensor maintainers: - Song Qiang + - Siratul Islam properties: compatible: - const: st,vl53l0x + enum: + - st,vl53l0x + - st,vl53l1x reg: maxItems: 1 @@ -21,6 +24,8 @@ properties: reset-gpios: maxItems: 1 + description: + Phandle to the XSHUT GPIO. Used for hardware reset. vdd-supply: true @@ -28,6 +33,16 @@ required: - compatible - reg +allOf: + - if: + properties: + compatible: + contains: + const: st,vl53l1x + then: + required: + - vdd-supply + additionalProperties: false examples: @@ -38,8 +53,9 @@ examples: #size-cells = <0>; proximity@29 { - compatible = "st,vl53l0x"; + compatible = "st,vl53l1x"; reg = <0x29>; + vdd-supply = <®_3v3>; interrupt-parent = <&gpio>; interrupts = <23 IRQ_TYPE_EDGE_FALLING>; }; diff --git a/MAINTAINERS b/MAINTAINERS index d664add6d408..fba3096aefa9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25105,6 +25105,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml F: drivers/iio/proximity/vl53l0x-i2c.c +ST VL53L1X ToF RANGER(I2C) IIO DRIVER +M: Siratul Islam +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml + STABLE BRANCH M: Greg Kroah-Hartman M: Sasha Levin From 128e5ebec856ef8d30d4244751165ef50b41d2d2 Mon Sep 17 00:00:00 2001 From: Siratul Islam Date: Thu, 26 Mar 2026 02:19:42 +0600 Subject: [PATCH 271/405] iio: proximity: add driver for ST VL53L1X ToF sensor Add support for the STMicroelectronics VL53L1X Time-of-Flight ranging sensor with I2C interface. Reviewed-by: Andy Shevchenko Signed-off-by: Siratul Islam Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + drivers/iio/proximity/Kconfig | 15 + drivers/iio/proximity/Makefile | 1 + drivers/iio/proximity/vl53l1x-i2c.c | 756 ++++++++++++++++++++++++++++ 4 files changed, 773 insertions(+) create mode 100644 drivers/iio/proximity/vl53l1x-i2c.c diff --git a/MAINTAINERS b/MAINTAINERS index fba3096aefa9..48fda1f8332e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25110,6 +25110,7 @@ M: Siratul Islam L: linux-iio@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml +F: drivers/iio/proximity/vl53l1x-i2c.c STABLE BRANCH M: Greg Kroah-Hartman diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig index 6070974c2c85..bb77fad2a1b3 100644 --- a/drivers/iio/proximity/Kconfig +++ b/drivers/iio/proximity/Kconfig @@ -244,6 +244,21 @@ config VL53L0X_I2C To compile this driver as a module, choose M here: the module will be called vl53l0x-i2c. +config VL53L1X_I2C + tristate "STMicroelectronics VL53L1X ToF ranger sensor (I2C)" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select REGMAP_I2C + select RESET_CONTROLLER + help + Say Y here to build a driver for STMicroelectronics VL53L1X + ToF ranger sensors with i2c interface. + This driver can be used to measure the distance of objects. + + To compile this driver as a module, choose M here: the + module will be called vl53l1x-i2c. + config AW96103 tristate "AW96103/AW96105 Awinic proximity sensor" select REGMAP_I2C diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile index 152034d38c49..4352833dd8a4 100644 --- a/drivers/iio/proximity/Makefile +++ b/drivers/iio/proximity/Makefile @@ -23,5 +23,6 @@ obj-$(CONFIG_SX_COMMON) += sx_common.o obj-$(CONFIG_SX9500) += sx9500.o obj-$(CONFIG_VCNL3020) += vcnl3020.o obj-$(CONFIG_VL53L0X_I2C) += vl53l0x-i2c.o +obj-$(CONFIG_VL53L1X_I2C) += vl53l1x-i2c.o obj-$(CONFIG_AW96103) += aw96103.o diff --git a/drivers/iio/proximity/vl53l1x-i2c.c b/drivers/iio/proximity/vl53l1x-i2c.c new file mode 100644 index 000000000000..4d9cb3983dba --- /dev/null +++ b/drivers/iio/proximity/vl53l1x-i2c.c @@ -0,0 +1,756 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* + * Support for ST VL53L1X FlightSense ToF Ranging Sensor on a i2c bus. + * + * Copyright (C) 2026 Siratul Islam + * + * Datasheet available at + * + * + * Default 7-bit i2c slave address 0x29. + * + * The VL53L1X requires a firmware configuration blob to be loaded at boot. + * Register values for the default configuration are taken from + * ST's VL53L1X Ultra Lite Driver (STSW-IMG009). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#define VL53L1X_REG_SOFT_RESET 0x0000 +#define VL53L1X_REG_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND 0x0008 +#define VL53L1X_REG_VHV_CONFIG__INIT 0x000B +#define VL53L1X_REG_GPIO_HV_MUX__CTRL 0x0030 +#define VL53L1X_REG_GPIO__TIO_HV_STATUS 0x0031 +#define VL53L1X_REG_SYSTEM__INTERRUPT_CONFIG_GPIO 0x0046 +#define VL53L1X_REG_PHASECAL_CONFIG__TIMEOUT_MACROP 0x004B +#define VL53L1X_REG_RANGE_CONFIG__TIMEOUT_MACROP_A 0x005E +#define VL53L1X_REG_RANGE_CONFIG__VCSEL_PERIOD_A 0x0060 +#define VL53L1X_REG_RANGE_CONFIG__TIMEOUT_MACROP_B 0x0061 +#define VL53L1X_REG_RANGE_CONFIG__VCSEL_PERIOD_B 0x0063 +#define VL53L1X_REG_RANGE_CONFIG__VALID_PHASE_HIGH 0x0069 +#define VL53L1X_REG_SYSTEM__INTERMEASUREMENT_PERIOD 0x006C +#define VL53L1X_REG_SD_CONFIG__WOI_SD0 0x0078 +#define VL53L1X_REG_SD_CONFIG__WOI_SD1 0x0079 +#define VL53L1X_REG_SD_CONFIG__INITIAL_PHASE_SD0 0x007A +#define VL53L1X_REG_SD_CONFIG__INITIAL_PHASE_SD1 0x007B +#define VL53L1X_REG_SYSTEM__INTERRUPT_CLEAR 0x0086 +#define VL53L1X_REG_SYSTEM__MODE_START 0x0087 +#define VL53L1X_REG_RESULT__RANGE_STATUS 0x0089 +#define VL53L1X_REG_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 0x0096 +#define VL53L1X_REG_RESULT__OSC_CALIBRATE_VAL 0x00DE +#define VL53L1X_REG_FIRMWARE__SYSTEM_STATUS 0x00E5 +#define VL53L1X_REG_IDENTIFICATION__MODEL_ID 0x010F +#define VL53L1X_REG_DEFAULT_CONFIG 0x002D + +#define VL53L1X_MODEL_ID_VAL 0xEACC + +#define VL53L1X_MODE_START_TIMED 0x40 +#define VL53L1X_MODE_START_STOP 0x00 + +#define VL53L1X_INT_NEW_SAMPLE_READY 0x02 + +#define VL53L1X_GPIO_HV_MUX_POLARITY BIT(4) + +#define VL53L1X_VHV_LOOP_BOUND_TWO 0x09 + +#define VL53L1X_RANGE_STATUS_MASK GENMASK(4, 0) +#define VL53L1X_RANGE_STATUS_VALID 9 + +#define VL53L1X_OSC_CALIBRATE_MASK GENMASK(9, 0) + +/* Inter-measurement period uses PLL divider with 1.075 oscillator correction */ +static const struct u32_fract vl53l1x_osc_correction = { + .numerator = 1075, + .denominator = 1000, +}; + +enum vl53l1x_distance_mode { + VL53L1X_SHORT, + VL53L1X_LONG, +}; + +struct vl53l1x_data { + struct regmap *regmap; + struct completion completion; + struct reset_control *xshut_reset; + enum vl53l1x_distance_mode distance_mode; + u8 gpio_polarity; + int irq; +}; + +static const struct regmap_range vl53l1x_volatile_ranges[] = { + regmap_reg_range(VL53L1X_REG_GPIO__TIO_HV_STATUS, + VL53L1X_REG_GPIO__TIO_HV_STATUS), + regmap_reg_range(VL53L1X_REG_RESULT__RANGE_STATUS, + VL53L1X_REG_RESULT__RANGE_STATUS), + regmap_reg_range(VL53L1X_REG_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0, + VL53L1X_REG_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 + 1), + regmap_reg_range(VL53L1X_REG_RESULT__OSC_CALIBRATE_VAL, + VL53L1X_REG_RESULT__OSC_CALIBRATE_VAL + 1), + regmap_reg_range(VL53L1X_REG_FIRMWARE__SYSTEM_STATUS, + VL53L1X_REG_FIRMWARE__SYSTEM_STATUS), +}; + +static const struct regmap_access_table vl53l1x_volatile_table = { + .yes_ranges = vl53l1x_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(vl53l1x_volatile_ranges), +}; + +static const struct regmap_range vl53l1x_write_only_ranges[] = { + regmap_reg_range(VL53L1X_REG_SOFT_RESET, VL53L1X_REG_SOFT_RESET), + regmap_reg_range(VL53L1X_REG_SYSTEM__INTERRUPT_CLEAR, + VL53L1X_REG_SYSTEM__MODE_START), +}; + +static const struct regmap_access_table vl53l1x_readable_table = { + .no_ranges = vl53l1x_write_only_ranges, + .n_no_ranges = ARRAY_SIZE(vl53l1x_write_only_ranges), +}; + +static const struct regmap_config vl53l1x_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + /* MODEL_ID is 16-bit. +1 covers the second byte at 0x0110 */ + .max_register = VL53L1X_REG_IDENTIFICATION__MODEL_ID + 1, + .cache_type = REGCACHE_MAPLE, + .volatile_table = &vl53l1x_volatile_table, + .rd_table = &vl53l1x_readable_table, +}; + +static int vl53l1x_read_u16(struct vl53l1x_data *data, u16 reg, u16 *val) +{ + __be16 buf; + int ret; + + ret = regmap_bulk_read(data->regmap, reg, &buf, sizeof(buf)); + if (ret) + return ret; + + *val = be16_to_cpu(buf); + return 0; +} + +static int vl53l1x_write_u16(struct vl53l1x_data *data, u16 reg, u16 val) +{ + __be16 buf = cpu_to_be16(val); + + return regmap_bulk_write(data->regmap, reg, &buf, sizeof(buf)); +} + +static int vl53l1x_write_u32(struct vl53l1x_data *data, u16 reg, u32 val) +{ + __be32 buf = cpu_to_be32(val); + + return regmap_bulk_write(data->regmap, reg, &buf, sizeof(buf)); +} + +static int vl53l1x_clear_irq(struct vl53l1x_data *data) +{ + return regmap_write(data->regmap, VL53L1X_REG_SYSTEM__INTERRUPT_CLEAR, 0x01); +} + +static int vl53l1x_start_ranging(struct vl53l1x_data *data) +{ + int ret; + + ret = vl53l1x_clear_irq(data); + if (ret) + return ret; + + return regmap_write(data->regmap, VL53L1X_REG_SYSTEM__MODE_START, + VL53L1X_MODE_START_TIMED); +} + +static int vl53l1x_stop_ranging(struct vl53l1x_data *data) +{ + return regmap_write(data->regmap, VL53L1X_REG_SYSTEM__MODE_START, + VL53L1X_MODE_START_STOP); +} + +/* + * Default configuration blob from ST's VL53L1X Ultra Lite Driver + * (STSW-IMG009). + */ +static const u8 vl53l1x_default_config[] = { + 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x02, 0x08, /* reg 0x2d..0x34 */ + 0x00, 0x08, 0x10, 0x01, 0x01, 0x00, 0x00, 0x00, /* reg 0x35..0x3c */ + 0x00, 0xFF, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, /* reg 0x3d..0x44 */ + 0x00, 0x20, 0x0B, 0x00, 0x00, 0x02, 0x0A, 0x21, /* reg 0x45..0x4c */ + 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0xC8, /* reg 0x4d..0x54 */ + 0x00, 0x00, 0x38, 0xFF, 0x01, 0x00, 0x08, 0x00, /* reg 0x55..0x5c */ + 0x00, 0x01, 0xCC, 0x0F, 0x01, 0xF1, 0x0D, 0x01, /* reg 0x5d..0x64 */ + 0x68, 0x00, 0x80, 0x08, 0xB8, 0x00, 0x00, 0x00, /* reg 0x65..0x6c */ + 0x00, 0x0F, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg 0x6d..0x74 */ + 0x00, 0x00, 0x01, 0x0F, 0x0D, 0x0E, 0x0E, 0x00, /* reg 0x75..0x7c */ + 0x00, 0x02, 0xC7, 0xFF, 0x9B, 0x00, 0x00, 0x00, /* reg 0x7d..0x84 */ + 0x01, 0x00, 0x00, /* reg 0x85..0x87 */ +}; + +static int vl53l1x_chip_init(struct vl53l1x_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + unsigned int val; + u16 model_id; + int ret; + + if (!data->xshut_reset) { + ret = regmap_write(data->regmap, VL53L1X_REG_SOFT_RESET, 0x00); + if (ret) + return ret; + fsleep(100); /* conservative reset pulse, no spec */ + + ret = regmap_write(data->regmap, VL53L1X_REG_SOFT_RESET, 0x01); + if (ret) + return ret; + fsleep(1000); /* conservative boot wait, no spec */ + } + + ret = regmap_read_poll_timeout(data->regmap, + VL53L1X_REG_FIRMWARE__SYSTEM_STATUS, val, + val & BIT(0), + 1 * USEC_PER_MSEC, + 100 * USEC_PER_MSEC); + if (ret) + return dev_err_probe(dev, ret, "firmware boot timeout\n"); + + ret = vl53l1x_read_u16(data, VL53L1X_REG_IDENTIFICATION__MODEL_ID, + &model_id); + if (ret) + return ret; + + if (model_id != VL53L1X_MODEL_ID_VAL) + dev_info(dev, "unknown model id: 0x%04x, continuing\n", model_id); + + ret = regmap_bulk_write(data->regmap, VL53L1X_REG_DEFAULT_CONFIG, + vl53l1x_default_config, + sizeof(vl53l1x_default_config)); + if (ret) + return ret; + + ret = regmap_read(data->regmap, VL53L1X_REG_GPIO_HV_MUX__CTRL, &val); + if (ret) + return ret; + data->gpio_polarity = !!(val & VL53L1X_GPIO_HV_MUX_POLARITY); + + /* Initial ranging cycle for VHV calibration */ + ret = vl53l1x_start_ranging(data); + if (ret) + return ret; + + /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */ + ret = regmap_read_poll_timeout(data->regmap, + VL53L1X_REG_GPIO__TIO_HV_STATUS, val, + (val & 1) != data->gpio_polarity, + 1 * USEC_PER_MSEC, + 1000 * USEC_PER_MSEC); + if (ret) + return ret; + + ret = vl53l1x_clear_irq(data); + if (ret) + return ret; + + ret = vl53l1x_stop_ranging(data); + if (ret) + return ret; + + ret = regmap_write(data->regmap, + VL53L1X_REG_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND, + VL53L1X_VHV_LOOP_BOUND_TWO); + if (ret) + return ret; + + return regmap_write(data->regmap, VL53L1X_REG_VHV_CONFIG__INIT, 0x00); +} + +static const struct reg_sequence vl53l1x_mode_short[] = { + { VL53L1X_REG_PHASECAL_CONFIG__TIMEOUT_MACROP, 0x14 }, + { VL53L1X_REG_RANGE_CONFIG__VCSEL_PERIOD_A, 0x07 }, + { VL53L1X_REG_RANGE_CONFIG__VCSEL_PERIOD_B, 0x05 }, + { VL53L1X_REG_RANGE_CONFIG__VALID_PHASE_HIGH, 0x38 }, + { VL53L1X_REG_SD_CONFIG__WOI_SD0, 0x07 }, + { VL53L1X_REG_SD_CONFIG__WOI_SD1, 0x05 }, + { VL53L1X_REG_SD_CONFIG__INITIAL_PHASE_SD0, 0x06 }, + { VL53L1X_REG_SD_CONFIG__INITIAL_PHASE_SD1, 0x06 }, +}; + +static const struct reg_sequence vl53l1x_mode_long[] = { + { VL53L1X_REG_PHASECAL_CONFIG__TIMEOUT_MACROP, 0x0A }, + { VL53L1X_REG_RANGE_CONFIG__VCSEL_PERIOD_A, 0x0F }, + { VL53L1X_REG_RANGE_CONFIG__VCSEL_PERIOD_B, 0x0D }, + { VL53L1X_REG_RANGE_CONFIG__VALID_PHASE_HIGH, 0xB8 }, + { VL53L1X_REG_SD_CONFIG__WOI_SD0, 0x0F }, + { VL53L1X_REG_SD_CONFIG__WOI_SD1, 0x0D }, + { VL53L1X_REG_SD_CONFIG__INITIAL_PHASE_SD0, 0x0E }, + { VL53L1X_REG_SD_CONFIG__INITIAL_PHASE_SD1, 0x0E }, +}; + +static const struct { + const struct reg_sequence *regs; + size_t num_regs; +} vl53l1x_mode_configs[] = { + [VL53L1X_SHORT] = { vl53l1x_mode_short, ARRAY_SIZE(vl53l1x_mode_short) }, + [VL53L1X_LONG] = { vl53l1x_mode_long, ARRAY_SIZE(vl53l1x_mode_long) }, +}; + +static int vl53l1x_set_distance_mode(struct vl53l1x_data *data, + enum vl53l1x_distance_mode mode) +{ + int ret; + + if (mode >= ARRAY_SIZE(vl53l1x_mode_configs)) + return -EINVAL; + + ret = regmap_multi_reg_write(data->regmap, + vl53l1x_mode_configs[mode].regs, + vl53l1x_mode_configs[mode].num_regs); + if (ret) + return ret; + + data->distance_mode = mode; + return 0; +} + +/* + * The timing budget controls how long the sensor spends collecting + * a single range measurement. Pre-computed TIMEOUT_MACROP register + * values from ST's VL53L1X Ultra Lite Driver. + */ +static int vl53l1x_set_timing_budget(struct vl53l1x_data *data, u16 budget_ms) +{ + u16 timeout_a, timeout_b; + int ret; + + switch (data->distance_mode) { + case VL53L1X_SHORT: + switch (budget_ms) { + case 15: + timeout_a = 0x001D; + timeout_b = 0x0027; + break; + case 20: + timeout_a = 0x0051; + timeout_b = 0x006E; + break; + case 33: + timeout_a = 0x00D6; + timeout_b = 0x006E; + break; + case 50: + timeout_a = 0x01AE; + timeout_b = 0x01E8; + break; + case 100: + timeout_a = 0x02E1; + timeout_b = 0x0388; + break; + case 200: + timeout_a = 0x03E1; + timeout_b = 0x0496; + break; + case 500: + timeout_a = 0x0591; + timeout_b = 0x05C1; + break; + default: + return -EINVAL; + } + break; + case VL53L1X_LONG: + switch (budget_ms) { + case 20: + timeout_a = 0x001E; + timeout_b = 0x0022; + break; + case 33: + timeout_a = 0x0060; + timeout_b = 0x006E; + break; + case 50: + timeout_a = 0x00AD; + timeout_b = 0x00C6; + break; + case 100: + timeout_a = 0x01CC; + timeout_b = 0x01EA; + break; + case 200: + timeout_a = 0x02D9; + timeout_b = 0x02F8; + break; + case 500: + timeout_a = 0x048F; + timeout_b = 0x04A4; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + ret = vl53l1x_write_u16(data, VL53L1X_REG_RANGE_CONFIG__TIMEOUT_MACROP_A, + timeout_a); + if (ret) + return ret; + + return vl53l1x_write_u16(data, VL53L1X_REG_RANGE_CONFIG__TIMEOUT_MACROP_B, + timeout_b); +} + +static int vl53l1x_set_inter_measurement_ms(struct vl53l1x_data *data, + u16 period_ms) +{ + u16 osc_calibrate_val; + u16 clock_pll; + u32 inter_meas; + int ret; + + ret = vl53l1x_read_u16(data, VL53L1X_REG_RESULT__OSC_CALIBRATE_VAL, + &osc_calibrate_val); + if (ret) + return ret; + + clock_pll = osc_calibrate_val & VL53L1X_OSC_CALIBRATE_MASK; + inter_meas = (clock_pll * period_ms * vl53l1x_osc_correction.numerator) / + vl53l1x_osc_correction.denominator; + + return vl53l1x_write_u32(data, + VL53L1X_REG_SYSTEM__INTERMEASUREMENT_PERIOD, + inter_meas); +} + +static int vl53l1x_read_proximity(struct vl53l1x_data *data, int *val) +{ + unsigned int range_status; + u16 distance; + int ret; + + if (data->irq) { + reinit_completion(&data->completion); + + ret = vl53l1x_clear_irq(data); + if (ret) + return ret; + + if (!wait_for_completion_timeout(&data->completion, HZ)) + return -ETIMEDOUT; + } else { + unsigned int rdy; + + /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */ + ret = regmap_read_poll_timeout(data->regmap, + VL53L1X_REG_GPIO__TIO_HV_STATUS, rdy, + (rdy & 1) != data->gpio_polarity, + 1 * USEC_PER_MSEC, + 1000 * USEC_PER_MSEC); + if (ret) + return ret; + } + + ret = regmap_read(data->regmap, VL53L1X_REG_RESULT__RANGE_STATUS, + &range_status); + if (ret) + goto clear_irq; + + if (FIELD_GET(VL53L1X_RANGE_STATUS_MASK, range_status) != + VL53L1X_RANGE_STATUS_VALID) { + ret = -EIO; + goto clear_irq; + } + + ret = vl53l1x_read_u16(data, + VL53L1X_REG_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0, + &distance); + if (ret) + goto clear_irq; + + *val = distance; + +clear_irq: + vl53l1x_clear_irq(data); + return ret; +} + +static const struct iio_chan_spec vl53l1x_channels[] = { + { + .type = IIO_DISTANCE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static int vl53l1x_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct vl53l1x_data *data = iio_priv(indio_dev); + int ret; + + if (chan->type != IIO_DISTANCE) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = vl53l1x_read_proximity(data, val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = 1000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info vl53l1x_info = { + .read_raw = vl53l1x_read_raw, + .validate_trigger = iio_validate_own_trigger, +}; + +static irqreturn_t vl53l1x_trigger_handler(int irq, void *priv) +{ + struct iio_poll_func *pf = priv; + struct iio_dev *indio_dev = pf->indio_dev; + struct vl53l1x_data *data = iio_priv(indio_dev); + struct { + u16 distance; + aligned_s64 timestamp; + } scan = { }; + unsigned int range_status; + int ret; + + ret = regmap_read(data->regmap, VL53L1X_REG_RESULT__RANGE_STATUS, + &range_status); + if (ret) + goto notify_and_clear_irq; + if (FIELD_GET(VL53L1X_RANGE_STATUS_MASK, range_status) != + VL53L1X_RANGE_STATUS_VALID) + goto notify_and_clear_irq; + + ret = vl53l1x_read_u16(data, + VL53L1X_REG_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0, + &scan.distance); + if (ret) + goto notify_and_clear_irq; + + iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), + iio_get_time_ns(indio_dev)); + +notify_and_clear_irq: + iio_trigger_notify_done(indio_dev->trig); + vl53l1x_clear_irq(data); + + return IRQ_HANDLED; +} + +static irqreturn_t vl53l1x_irq_handler(int irq, void *priv) +{ + struct iio_dev *indio_dev = priv; + struct vl53l1x_data *data = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll(indio_dev->trig); + else + complete(&data->completion); + + return IRQ_HANDLED; +} + +static const struct iio_trigger_ops vl53l1x_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static void vl53l1x_stop_ranging_action(void *priv) +{ + vl53l1x_stop_ranging(priv); +} + +static int vl53l1x_configure_irq(struct device *dev, int irq, + struct iio_dev *indio_dev) +{ + struct vl53l1x_data *data = iio_priv(indio_dev); + int ret; + + ret = devm_request_irq(dev, irq, vl53l1x_irq_handler, IRQF_NO_THREAD, + indio_dev->name, indio_dev); + if (ret) + return ret; + + ret = regmap_write(data->regmap, VL53L1X_REG_SYSTEM__INTERRUPT_CONFIG_GPIO, + VL53L1X_INT_NEW_SAMPLE_READY); + if (ret) + return dev_err_probe(dev, ret, "failed to configure IRQ\n"); + + return 0; +} + +static int vl53l1x_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct vl53l1x_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->irq = client->irq; + + data->regmap = devm_regmap_init_i2c(client, &vl53l1x_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "regmap initialization failed\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable VDD regulator\n"); + + /* + * XSHUT held low puts the chip in hardware standby. All register + * state is lost on de-assert so this is functionally a reset. + */ + data->xshut_reset = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL); + if (IS_ERR(data->xshut_reset)) + return dev_err_probe(dev, PTR_ERR(data->xshut_reset), + "Cannot get reset control\n"); + + /* + * 1.2 ms max boot duration. + * Datasheet Section 3.6 "Power up and boot sequence". + */ + fsleep(1200); + + ret = vl53l1x_chip_init(data); + if (ret) + return ret; + + ret = vl53l1x_set_distance_mode(data, VL53L1X_LONG); + if (ret) + return ret; + + /* 50 ms timing budget (per ST Ultra Lite Driver) */ + ret = vl53l1x_set_timing_budget(data, 50); + if (ret) + return ret; + + /* 50 ms inter-measurement period (per ST Ultra Lite Driver) */ + ret = vl53l1x_set_inter_measurement_ms(data, 50); + if (ret) + return ret; + + /* + * The hardware only supports "autonomous" continuous ranging mode. + * Start ranging here and leave it running for the lifetime of + * the device. Both direct reads and the buffer path rely on this. + */ + ret = vl53l1x_start_ranging(data); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, vl53l1x_stop_ranging_action, data); + if (ret) + return ret; + + indio_dev->name = "vl53l1x"; + indio_dev->info = &vl53l1x_info; + indio_dev->channels = vl53l1x_channels; + indio_dev->num_channels = ARRAY_SIZE(vl53l1x_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + if (client->irq) { + struct iio_trigger *trig; + + init_completion(&data->completion); + + trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, + iio_device_id(indio_dev)); + if (!trig) + return -ENOMEM; + + trig->ops = &vl53l1x_trigger_ops; + iio_trigger_set_drvdata(trig, indio_dev); + ret = devm_iio_trigger_register(dev, trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(trig); + + ret = vl53l1x_configure_irq(dev, client->irq, indio_dev); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + &vl53l1x_trigger_handler, + NULL); + if (ret) + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct i2c_device_id vl53l1x_id[] = { + { "vl53l1x" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, vl53l1x_id); + +static const struct of_device_id st_vl53l1x_dt_match[] = { + { .compatible = "st,vl53l1x" }, + { } +}; +MODULE_DEVICE_TABLE(of, st_vl53l1x_dt_match); + +static struct i2c_driver vl53l1x_driver = { + .driver = { + .name = "vl53l1x-i2c", + .of_match_table = st_vl53l1x_dt_match, + }, + .probe = vl53l1x_probe, + .id_table = vl53l1x_id, +}; +module_i2c_driver(vl53l1x_driver); + +MODULE_AUTHOR("Siratul Islam "); +MODULE_DESCRIPTION("ST VL53L1X ToF ranging sensor driver"); +MODULE_LICENSE("Dual BSD/GPL"); From f64f37521c3492ea32dee9bce513145360f30f57 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 24 Mar 2026 02:10:37 +0200 Subject: [PATCH 272/405] dt-bindings: interconnect: qcom,msm8974: drop bus clocks Remove the wrong internal RPM bus clock representation that we've been carrying for years. They are an internal part of the interconnect fabric. They are not exported by any device and are not supposed to be used. Signed-off-by: Dmitry Baryshkov Link: https://msgid.link/20260324-msm8974-icc-v2-1-527280043ad8@oss.qualcomm.com Signed-off-by: Georgi Djakov --- .../bindings/interconnect/qcom,msm8974.yaml | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml index 95ce25ce1f7d..89a694501d8c 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml @@ -32,22 +32,32 @@ properties: clock-names: items: - const: bus - - const: bus_a clocks: items: - description: Bus Clock - - description: Bus A Clock required: - compatible - reg - '#interconnect-cells' - - clock-names - - clocks additionalProperties: false +allOf: + - if: + properties: + compatible: + const: qcom,msm8974-mmssnoc + then: + required: + - clocks + - clock-names + else: + properties: + clocks: false + clock-names: false + examples: - | #include @@ -56,7 +66,4 @@ examples: reg = <0xfc380000 0x6a000>; compatible = "qcom,msm8974-bimc"; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_BIMC_CLK>, - <&rpmcc RPM_SMD_BIMC_A_CLK>; }; From 199363ed2f6a351360fbc353e20059c763965130 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 24 Mar 2026 02:10:38 +0200 Subject: [PATCH 273/405] dt-bindings: interconnect: qcom,msm8974: use qcom,rpm-common Use qcom,rpm-common schema to declare interconnects property instead describing it again. In future this will allow the platform to switch to the two-cell interconnects, adding the tag to the specification. Signed-off-by: Dmitry Baryshkov Link: https://msgid.link/20260324-msm8974-icc-v2-2-527280043ad8@oss.qualcomm.com Signed-off-by: Georgi Djakov --- .../devicetree/bindings/interconnect/qcom,msm8974.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml index 89a694501d8c..b35f6dd11c71 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml @@ -26,9 +26,6 @@ properties: - qcom,msm8974-pnoc - qcom,msm8974-snoc - '#interconnect-cells': - const: 1 - clock-names: items: - const: bus @@ -40,11 +37,11 @@ properties: required: - compatible - reg - - '#interconnect-cells' -additionalProperties: false +unevaluatedProperties: false allOf: + - $ref: qcom,rpm-common.yaml# - if: properties: compatible: From b8498af901684912bd87a39c7b95ad955d9bc543 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 24 Mar 2026 02:10:39 +0200 Subject: [PATCH 274/405] interconnect: qcom: drop unused is_on flag The commit 2e2113c8a64f ("interconnect: qcom: rpm: Handle interface clocks") has added the is_on flag to the qcom_icc_provider, but failed to actually utilize it. Drop the flag. Fixes: 2e2113c8a64f ("interconnect: qcom: rpm: Handle interface clocks") Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Link: https://msgid.link/20260324-msm8974-icc-v2-3-527280043ad8@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index f4883d43eae4..3366531f66fc 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -51,7 +51,6 @@ struct rpm_clk_resource { * @bus_clk: a pointer to a HLOS-owned bus clock * @intf_clks: a clk_bulk_data array of interface clocks * @keep_alive: whether to always keep a minimum vote on the bus clocks - * @is_on: whether the bus is powered on */ struct qcom_icc_provider { struct icc_provider provider; @@ -66,7 +65,6 @@ struct qcom_icc_provider { struct clk *bus_clk; struct clk_bulk_data *intf_clks; bool keep_alive; - bool is_on; }; /** From fba5454ef58bbdf2334fc94c29ae3053acac574c Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 24 Mar 2026 02:10:40 +0200 Subject: [PATCH 275/405] interconnect: qcom: icc-rpm: allow overwriting get_bw callback MSM8974 requires a separate get_bw callback, since on that platform increasing the clock rate for some of the NoCs during boot may lead to hangs. For the details see commit 9caf2d956cfa ("interconnect: qcom: msm8974: Don't boost the NoC rate during boot"). Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Link: https://msgid.link/20260324-msm8974-icc-v2-4-527280043ad8@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 1 + drivers/interconnect/qcom/icc-rpm.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index ea1042d38128..aec2f84cd56f 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -553,6 +553,7 @@ int qnoc_probe(struct platform_device *pdev) provider->aggregate = qcom_icc_bw_aggregate; provider->xlate_extended = qcom_icc_xlate_extended; provider->data = data; + provider->get_bw = desc->get_bw; icc_provider_init(provider); diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index 3366531f66fc..cbf0a365839d 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -135,6 +135,7 @@ struct qcom_icc_desc { unsigned int qos_offset; u16 ab_coeff; u16 ib_coeff; + int (*get_bw)(struct icc_node *node, u32 *avg, u32 *peak); }; /* Valid for all bus types */ From 1d5b5f7d755be846e7b3a236855ee148b80b4fa3 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 24 Mar 2026 02:10:41 +0200 Subject: [PATCH 276/405] interconnect: qcom: define OCMEM bus resource Some of the platforms (MSM8974, MSM8x26) require voting on the OCMEM clock. Add new resource for that clock. Reviewed-by: Brian Masney Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://msgid.link/20260324-msm8974-icc-v2-5-527280043ad8@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm-clocks.c | 6 ++++++ drivers/interconnect/qcom/icc-rpm.h | 1 + 2 files changed, 7 insertions(+) diff --git a/drivers/interconnect/qcom/icc-rpm-clocks.c b/drivers/interconnect/qcom/icc-rpm-clocks.c index ac1677de7dfd..69846e26f46a 100644 --- a/drivers/interconnect/qcom/icc-rpm-clocks.c +++ b/drivers/interconnect/qcom/icc-rpm-clocks.c @@ -31,6 +31,12 @@ const struct rpm_clk_resource mem_1_clk = { }; EXPORT_SYMBOL_GPL(mem_1_clk); +const struct rpm_clk_resource gpu_mem_2_clk = { + .resource_type = QCOM_SMD_RPM_MEM_CLK, + .clock_id = 2, +}; +EXPORT_SYMBOL_GPL(gpu_mem_2_clk); + const struct rpm_clk_resource bus_0_clk = { .resource_type = QCOM_SMD_RPM_BUS_CLK, .clock_id = 0, diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index cbf0a365839d..ad554c63967b 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -151,6 +151,7 @@ extern const struct rpm_clk_resource bimc_clk; extern const struct rpm_clk_resource bus_0_clk; extern const struct rpm_clk_resource bus_1_clk; extern const struct rpm_clk_resource bus_2_clk; +extern const struct rpm_clk_resource gpu_mem_2_clk; extern const struct rpm_clk_resource mem_1_clk; extern const struct rpm_clk_resource mmaxi_0_clk; extern const struct rpm_clk_resource mmaxi_1_clk; From 91cfd1604f9ec73d3fde18204de263d0e2c79a3b Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 24 Mar 2026 02:10:42 +0200 Subject: [PATCH 277/405] interconnect: qcom: let platforms declare their bugginess On MSM8974 programming some of the RPM resources results in the "resource does not exist" messages from the firmware. This occurs even with the downstream bus driver, which happily ignores the errors. My assumption is that these resources existed in the earlier firmware revisions but were later switched to be programmed differently (for the later platforms corresponding nodes use qos.ap_owned, which prevents those resources from being programmed. In preparation for conversion of the MSM8974 driver (which doesn't have QoS code yet) to the main icc-rpm set of helpers, let the driver declare that those -ENXIO errors must be ignored (for now). Later, when the QoS programming is sorted out (and more interconnects are added to the DT), this quirk might be removed. Reviewed-by: Brian Masney Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://msgid.link/20260324-msm8974-icc-v2-6-527280043ad8@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 17 ++++++++++------- drivers/interconnect/qcom/icc-rpm.h | 3 +++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index aec2f84cd56f..23a1d116e79a 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -204,7 +204,7 @@ static int qcom_icc_qos_set(struct icc_node *node) } } -static int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 *bw) +static int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 *bw, bool ignore_enxio) { int ret, rpm_ctx = 0; u64 bw_bps; @@ -222,8 +222,9 @@ static int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 *bw) bw_bps); if (ret) { pr_err("qcom_icc_rpm_smd_send mas %d error %d\n", - qn->mas_rpm_id, ret); - return ret; + qn->mas_rpm_id, ret); + if (ret != -ENXIO || !ignore_enxio) + return ret; } } @@ -234,8 +235,9 @@ static int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 *bw) bw_bps); if (ret) { pr_err("qcom_icc_rpm_smd_send slv %d error %d\n", - qn->slv_rpm_id, ret); - return ret; + qn->slv_rpm_id, ret); + if (ret != -ENXIO || !ignore_enxio) + return ret; } } } @@ -361,12 +363,12 @@ static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) active_rate = agg_clk_rate[QCOM_SMD_RPM_ACTIVE_STATE]; sleep_rate = agg_clk_rate[QCOM_SMD_RPM_SLEEP_STATE]; - ret = qcom_icc_rpm_set(src_qn, src_qn->sum_avg); + ret = qcom_icc_rpm_set(src_qn, src_qn->sum_avg, qp->ignore_enxio); if (ret) return ret; if (dst_qn) { - ret = qcom_icc_rpm_set(dst_qn, dst_qn->sum_avg); + ret = qcom_icc_rpm_set(dst_qn, dst_qn->sum_avg, qp->ignore_enxio); if (ret) return ret; } @@ -509,6 +511,7 @@ int qnoc_probe(struct platform_device *pdev) for (i = 0; i < cd_num; i++) qp->intf_clks[i].id = cds[i]; + qp->ignore_enxio = desc->ignore_enxio; qp->keep_alive = desc->keep_alive; qp->type = desc->type; qp->qos_offset = desc->qos_offset; diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index ad554c63967b..7d1cb2efa9ee 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -51,6 +51,7 @@ struct rpm_clk_resource { * @bus_clk: a pointer to a HLOS-owned bus clock * @intf_clks: a clk_bulk_data array of interface clocks * @keep_alive: whether to always keep a minimum vote on the bus clocks + * @ignore_enxio: whether to ignore ENXIO errors (for MSM8974) */ struct qcom_icc_provider { struct icc_provider provider; @@ -65,6 +66,7 @@ struct qcom_icc_provider { struct clk *bus_clk; struct clk_bulk_data *intf_clks; bool keep_alive; + bool ignore_enxio; }; /** @@ -136,6 +138,7 @@ struct qcom_icc_desc { u16 ab_coeff; u16 ib_coeff; int (*get_bw)(struct icc_node *node, u32 *avg, u32 *peak); + bool ignore_enxio; }; /* Valid for all bus types */ From aa60d907b3c289832d2188e079fc126a700389fa Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 24 Mar 2026 02:10:43 +0200 Subject: [PATCH 278/405] interconnect: qcom: msm8974: switch to the main icc-rpm driver In preparation to restoring the ability of MSM8974 driver to work with the modern kernels, switch the driver to the main icc-rpm set of helper code. As platform-specific workarounds, set the get_bw callback (returning 0) to prevent initial setup from programming INT_MAX into the RPM (which otherwise might hang the platform) and tell RPM programming code to ignore -ENXIO errors from the firmware (until the QoS programming is sorted out). Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://msgid.link/20260324-msm8974-icc-v2-7-527280043ad8@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/msm8974.c | 304 ++++------------------------ 1 file changed, 43 insertions(+), 261 deletions(-) diff --git a/drivers/interconnect/qcom/msm8974.c b/drivers/interconnect/qcom/msm8974.c index 3239edc37f02..144f225ec885 100644 --- a/drivers/interconnect/qcom/msm8974.c +++ b/drivers/interconnect/qcom/msm8974.c @@ -173,65 +173,27 @@ enum { MSM8974_SNOC_SLV_QDSS_STM, }; -#define to_msm8974_icc_provider(_provider) \ - container_of(_provider, struct msm8974_icc_provider, provider) +static int msm8974_get_bw(struct icc_node *node, u32 *avg, u32 *peak) +{ + *avg = 0; + *peak = 0; -static const struct clk_bulk_data msm8974_icc_bus_clocks[] = { - { .id = "bus" }, - { .id = "bus_a" }, -}; - -/** - * struct msm8974_icc_provider - Qualcomm specific interconnect provider - * @provider: generic interconnect provider - * @bus_clks: the clk_bulk_data table of bus clocks - * @num_clks: the total number of clk_bulk_data entries - */ -struct msm8974_icc_provider { - struct icc_provider provider; - struct clk_bulk_data *bus_clks; - int num_clks; -}; - -#define MSM8974_ICC_MAX_LINKS 3 - -/** - * struct msm8974_icc_node - Qualcomm specific interconnect nodes - * @name: the node name used in debugfs - * @id: a unique node identifier - * @links: an array of nodes where we can go next while traversing - * @num_links: the total number of @links - * @buswidth: width of the interconnect between a node and the bus (bytes) - * @mas_rpm_id: RPM ID for devices that are bus masters - * @slv_rpm_id: RPM ID for devices that are bus slaves - * @rate: current bus clock rate in Hz - */ -struct msm8974_icc_node { - unsigned char *name; - u16 id; - u16 links[MSM8974_ICC_MAX_LINKS]; - u16 num_links; - u16 buswidth; - int mas_rpm_id; - int slv_rpm_id; - u64 rate; -}; - -struct msm8974_icc_desc { - struct msm8974_icc_node * const *nodes; - size_t num_nodes; + return 0; }; #define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id, \ ...) \ - static struct msm8974_icc_node _name = { \ + static const u16 _name ## _links[] = { \ + __VA_ARGS__ \ + }; \ + static struct qcom_icc_node _name = { \ .name = #_name, \ .id = _id, \ .buswidth = _buswidth, \ .mas_rpm_id = _mas_rpm_id, \ .slv_rpm_id = _slv_rpm_id, \ - .num_links = COUNT_ARGS(__VA_ARGS__), \ - .links = { __VA_ARGS__ }, \ + .num_links = ARRAY_SIZE(_name ## _links), \ + .links = _name ## _links, \ } DEFINE_QNODE(mas_ampss_m0, MSM8974_BIMC_MAS_AMPSS_M0, 8, 0, -1); @@ -242,7 +204,7 @@ DEFINE_QNODE(bimc_to_snoc, MSM8974_BIMC_TO_SNOC, 8, 3, 2, MSM8974_SNOC_TO_BIMC, DEFINE_QNODE(slv_ebi_ch0, MSM8974_BIMC_SLV_EBI_CH0, 8, -1, 0); DEFINE_QNODE(slv_ampss_l2, MSM8974_BIMC_SLV_AMPSS_L2, 8, -1, 1); -static struct msm8974_icc_node * const msm8974_bimc_nodes[] = { +static struct qcom_icc_node * const msm8974_bimc_nodes[] = { [BIMC_MAS_AMPSS_M0] = &mas_ampss_m0, [BIMC_MAS_AMPSS_M1] = &mas_ampss_m1, [BIMC_MAS_MSS_PROC] = &mas_mss_proc, @@ -252,9 +214,12 @@ static struct msm8974_icc_node * const msm8974_bimc_nodes[] = { [BIMC_SLV_AMPSS_L2] = &slv_ampss_l2, }; -static const struct msm8974_icc_desc msm8974_bimc = { +static const struct qcom_icc_desc msm8974_bimc = { .nodes = msm8974_bimc_nodes, .num_nodes = ARRAY_SIZE(msm8974_bimc_nodes), + .bus_clk_desc = &bimc_clk, + .get_bw = msm8974_get_bw, + .ignore_enxio = true, }; DEFINE_QNODE(mas_rpm_inst, MSM8974_CNOC_MAS_RPM_INST, 8, 45, -1); @@ -295,7 +260,7 @@ DEFINE_QNODE(slv_ebi1_phy_cfg, MSM8974_CNOC_SLV_EBI1_PHY_CFG, 8, -1, 73); DEFINE_QNODE(slv_rpm, MSM8974_CNOC_SLV_RPM, 8, -1, 74); DEFINE_QNODE(slv_service_cnoc, MSM8974_CNOC_SLV_SERVICE_CNOC, 8, -1, 76); -static struct msm8974_icc_node * const msm8974_cnoc_nodes[] = { +static struct qcom_icc_node * const msm8974_cnoc_nodes[] = { [CNOC_MAS_RPM_INST] = &mas_rpm_inst, [CNOC_MAS_RPM_DATA] = &mas_rpm_data, [CNOC_MAS_RPM_SYS] = &mas_rpm_sys, @@ -335,9 +300,12 @@ static struct msm8974_icc_node * const msm8974_cnoc_nodes[] = { [CNOC_SLV_SERVICE_CNOC] = &slv_service_cnoc, }; -static const struct msm8974_icc_desc msm8974_cnoc = { +static const struct qcom_icc_desc msm8974_cnoc = { .nodes = msm8974_cnoc_nodes, .num_nodes = ARRAY_SIZE(msm8974_cnoc_nodes), + .bus_clk_desc = &bus_2_clk, + .get_bw = msm8974_get_bw, + .ignore_enxio = true, }; DEFINE_QNODE(mas_graphics_3d, MSM8974_MNOC_MAS_GRAPHICS_3D, 16, 6, -1, MSM8974_MNOC_TO_BIMC); @@ -363,7 +331,7 @@ DEFINE_QNODE(slv_mnoc_mpu_cfg, MSM8974_MNOC_SLV_MNOC_MPU_CFG, 16, -1, 14); DEFINE_QNODE(slv_onoc_mpu_cfg, MSM8974_MNOC_SLV_ONOC_MPU_CFG, 16, -1, 15); DEFINE_QNODE(slv_service_mnoc, MSM8974_MNOC_SLV_SERVICE_MNOC, 16, -1, 17); -static struct msm8974_icc_node * const msm8974_mnoc_nodes[] = { +static struct qcom_icc_node * const msm8974_mnoc_nodes[] = { [MNOC_MAS_GRAPHICS_3D] = &mas_graphics_3d, [MNOC_MAS_JPEG] = &mas_jpeg, [MNOC_MAS_MDP_PORT0] = &mas_mdp_port0, @@ -388,9 +356,11 @@ static struct msm8974_icc_node * const msm8974_mnoc_nodes[] = { [MNOC_SLV_SERVICE_MNOC] = &slv_service_mnoc, }; -static const struct msm8974_icc_desc msm8974_mnoc = { +static const struct qcom_icc_desc msm8974_mnoc = { .nodes = msm8974_mnoc_nodes, .num_nodes = ARRAY_SIZE(msm8974_mnoc_nodes), + .get_bw = msm8974_get_bw, + .ignore_enxio = true, }; DEFINE_QNODE(ocmem_noc_to_ocmem_vnoc, MSM8974_OCMEM_NOC_TO_OCMEM_VNOC, 16, 54, 78, MSM8974_OCMEM_SLV_OCMEM); @@ -408,7 +378,7 @@ DEFINE_QNODE(ocmem_vnoc_to_onoc, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC, 16, 56, 79, MS DEFINE_QNODE(ocmem_vnoc_to_snoc, MSM8974_OCMEM_VNOC_TO_SNOC, 8, 57, 80); DEFINE_QNODE(mas_v_ocmem_gfx3d, MSM8974_OCMEM_VNOC_MAS_GFX3D, 8, 55, -1, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC); -static struct msm8974_icc_node * const msm8974_onoc_nodes[] = { +static struct qcom_icc_node * const msm8974_onoc_nodes[] = { [OCMEM_NOC_TO_OCMEM_VNOC] = &ocmem_noc_to_ocmem_vnoc, [OCMEM_MAS_JPEG_OCMEM] = &mas_jpeg_ocmem, [OCMEM_MAS_MDP_OCMEM] = &mas_mdp_ocmem, @@ -423,9 +393,12 @@ static struct msm8974_icc_node * const msm8974_onoc_nodes[] = { [OCMEM_SLV_OCMEM] = &slv_ocmem, }; -static const struct msm8974_icc_desc msm8974_onoc = { +static const struct qcom_icc_desc msm8974_onoc = { .nodes = msm8974_onoc_nodes, .num_nodes = ARRAY_SIZE(msm8974_onoc_nodes), + .bus_clk_desc = &gpu_mem_2_clk, + .get_bw = msm8974_get_bw, + .ignore_enxio = true, }; DEFINE_QNODE(mas_pnoc_cfg, MSM8974_PNOC_MAS_PNOC_CFG, 8, 43, -1); @@ -456,7 +429,7 @@ DEFINE_QNODE(slv_pnoc_mpu_cfg, MSM8974_PNOC_SLV_PNOC_MPU_CFG, 8, -1, 43); DEFINE_QNODE(slv_prng, MSM8974_PNOC_SLV_PRNG, 8, -1, 44, MSM8974_PNOC_TO_SNOC); DEFINE_QNODE(slv_service_pnoc, MSM8974_PNOC_SLV_SERVICE_PNOC, 8, -1, 46); -static struct msm8974_icc_node * const msm8974_pnoc_nodes[] = { +static struct qcom_icc_node * const msm8974_pnoc_nodes[] = { [PNOC_MAS_PNOC_CFG] = &mas_pnoc_cfg, [PNOC_MAS_SDCC_1] = &mas_sdcc_1, [PNOC_MAS_SDCC_3] = &mas_sdcc_3, @@ -486,9 +459,13 @@ static struct msm8974_icc_node * const msm8974_pnoc_nodes[] = { [PNOC_SLV_SERVICE_PNOC] = &slv_service_pnoc, }; -static const struct msm8974_icc_desc msm8974_pnoc = { +static const struct qcom_icc_desc msm8974_pnoc = { .nodes = msm8974_pnoc_nodes, .num_nodes = ARRAY_SIZE(msm8974_pnoc_nodes), + .bus_clk_desc = &bus_0_clk, + .get_bw = msm8974_get_bw, + .keep_alive = true, + .ignore_enxio = true, }; DEFINE_QNODE(mas_lpass_ahb, MSM8974_SNOC_MAS_LPASS_AHB, 8, 18, -1); @@ -516,7 +493,7 @@ DEFINE_QNODE(slv_snoc_ocmem, MSM8974_SNOC_SLV_SNOC_OCMEM, 8, -1, 27); DEFINE_QNODE(slv_service_snoc, MSM8974_SNOC_SLV_SERVICE_SNOC, 8, -1, 29); DEFINE_QNODE(slv_qdss_stm, MSM8974_SNOC_SLV_QDSS_STM, 8, -1, 30); -static struct msm8974_icc_node * const msm8974_snoc_nodes[] = { +static struct qcom_icc_node * const msm8974_snoc_nodes[] = { [SNOC_MAS_LPASS_AHB] = &mas_lpass_ahb, [SNOC_MAS_QDSS_BAM] = &mas_qdss_bam, [SNOC_MAS_SNOC_CFG] = &mas_snoc_cfg, @@ -543,209 +520,14 @@ static struct msm8974_icc_node * const msm8974_snoc_nodes[] = { [SNOC_SLV_QDSS_STM] = &slv_qdss_stm, }; -static const struct msm8974_icc_desc msm8974_snoc = { +static const struct qcom_icc_desc msm8974_snoc = { .nodes = msm8974_snoc_nodes, .num_nodes = ARRAY_SIZE(msm8974_snoc_nodes), + .bus_clk_desc = &bus_1_clk, + .get_bw = msm8974_get_bw, + .ignore_enxio = true, }; -static void msm8974_icc_rpm_smd_send(struct device *dev, int rsc_type, - char *name, int id, u64 val) -{ - int ret; - - if (id == -1) - return; - - /* - * Setting the bandwidth requests for some nodes fails and this same - * behavior occurs on the downstream MSM 3.4 kernel sources based on - * errors like this in that kernel: - * - * msm_rpm_get_error_from_ack(): RPM NACK Unsupported resource - * AXI: msm_bus_rpm_req(): RPM: Ack failed - * AXI: msm_bus_rpm_commit_arb(): RPM: Req fail: mas:32, bw:240000000 - * - * Since there's no publicly available documentation for this hardware, - * and the bandwidth for some nodes in the path can be set properly, - * let's not return an error. - */ - ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, rsc_type, id, - val); - if (ret) - dev_dbg(dev, "Cannot set bandwidth for node %s (%d): %d\n", - name, id, ret); -} - -static int msm8974_icc_set(struct icc_node *src, struct icc_node *dst) -{ - struct msm8974_icc_node *src_qn, *dst_qn; - struct msm8974_icc_provider *qp; - u64 sum_bw, max_peak_bw, rate; - u32 agg_avg = 0, agg_peak = 0; - struct icc_provider *provider; - struct icc_node *n; - int ret, i; - - src_qn = src->data; - dst_qn = dst->data; - provider = src->provider; - qp = to_msm8974_icc_provider(provider); - - list_for_each_entry(n, &provider->nodes, node_list) - provider->aggregate(n, 0, n->avg_bw, n->peak_bw, - &agg_avg, &agg_peak); - - sum_bw = icc_units_to_bps(agg_avg); - max_peak_bw = icc_units_to_bps(agg_peak); - - /* Set bandwidth on source node */ - msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_MASTER_REQ, - src_qn->name, src_qn->mas_rpm_id, sum_bw); - - msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_SLAVE_REQ, - src_qn->name, src_qn->slv_rpm_id, sum_bw); - - /* Set bandwidth on destination node */ - msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_MASTER_REQ, - dst_qn->name, dst_qn->mas_rpm_id, sum_bw); - - msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_SLAVE_REQ, - dst_qn->name, dst_qn->slv_rpm_id, sum_bw); - - rate = max(sum_bw, max_peak_bw); - - do_div(rate, src_qn->buswidth); - - rate = min_t(u32, rate, INT_MAX); - - if (src_qn->rate == rate) - return 0; - - for (i = 0; i < qp->num_clks; i++) { - ret = clk_set_rate(qp->bus_clks[i].clk, rate); - if (ret) { - dev_err(provider->dev, "%s clk_set_rate error: %d\n", - qp->bus_clks[i].id, ret); - ret = 0; - } - } - - src_qn->rate = rate; - - return 0; -} - -static int msm8974_get_bw(struct icc_node *node, u32 *avg, u32 *peak) -{ - *avg = 0; - *peak = 0; - - return 0; -} - -static int msm8974_icc_probe(struct platform_device *pdev) -{ - const struct msm8974_icc_desc *desc; - struct msm8974_icc_node * const *qnodes; - struct msm8974_icc_provider *qp; - struct device *dev = &pdev->dev; - struct icc_onecell_data *data; - struct icc_provider *provider; - struct icc_node *node; - size_t num_nodes, i; - int ret; - - /* wait for the RPM proxy */ - if (!qcom_icc_rpm_smd_available()) - return -EPROBE_DEFER; - - desc = of_device_get_match_data(dev); - if (!desc) - return -EINVAL; - - qnodes = desc->nodes; - num_nodes = desc->num_nodes; - - qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL); - if (!qp) - return -ENOMEM; - - data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes), - GFP_KERNEL); - if (!data) - return -ENOMEM; - data->num_nodes = num_nodes; - - qp->bus_clks = devm_kmemdup(dev, msm8974_icc_bus_clocks, - sizeof(msm8974_icc_bus_clocks), GFP_KERNEL); - if (!qp->bus_clks) - return -ENOMEM; - - qp->num_clks = ARRAY_SIZE(msm8974_icc_bus_clocks); - ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks); - if (ret) - return ret; - - ret = clk_bulk_prepare_enable(qp->num_clks, qp->bus_clks); - if (ret) - return ret; - - provider = &qp->provider; - provider->dev = dev; - provider->set = msm8974_icc_set; - provider->aggregate = icc_std_aggregate; - provider->xlate = of_icc_xlate_onecell; - provider->data = data; - provider->get_bw = msm8974_get_bw; - - icc_provider_init(provider); - - for (i = 0; i < num_nodes; i++) { - size_t j; - - node = icc_node_create(qnodes[i]->id); - if (IS_ERR(node)) { - ret = PTR_ERR(node); - goto err_remove_nodes; - } - - node->name = qnodes[i]->name; - node->data = qnodes[i]; - icc_node_add(node, provider); - - dev_dbg(dev, "registered node %s\n", node->name); - - /* populate links */ - for (j = 0; j < qnodes[i]->num_links; j++) - icc_link_create(node, qnodes[i]->links[j]); - - data->nodes[i] = node; - } - - ret = icc_provider_register(provider); - if (ret) - goto err_remove_nodes; - - platform_set_drvdata(pdev, qp); - - return 0; - -err_remove_nodes: - icc_nodes_remove(provider); - clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); - - return ret; -} - -static void msm8974_icc_remove(struct platform_device *pdev) -{ - struct msm8974_icc_provider *qp = platform_get_drvdata(pdev); - - icc_provider_deregister(&qp->provider); - icc_nodes_remove(&qp->provider); - clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); -} - static const struct of_device_id msm8974_noc_of_match[] = { { .compatible = "qcom,msm8974-bimc", .data = &msm8974_bimc}, { .compatible = "qcom,msm8974-cnoc", .data = &msm8974_cnoc}, @@ -758,8 +540,8 @@ static const struct of_device_id msm8974_noc_of_match[] = { MODULE_DEVICE_TABLE(of, msm8974_noc_of_match); static struct platform_driver msm8974_noc_driver = { - .probe = msm8974_icc_probe, - .remove = msm8974_icc_remove, + .probe = qnoc_probe, + .remove = qnoc_remove, .driver = { .name = "qnoc-msm8974", .of_match_table = msm8974_noc_of_match, From 39ecfef48384b3b8795df37a8211462a6666d9a6 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 24 Mar 2026 02:10:44 +0200 Subject: [PATCH 279/405] interconnect: qcom: msm8974: expand DEFINE_QNODE macros The rest of Qualcomm Interconnect drivers have stopped using DEFINE_QNODE long ago for the sake of readability. Stop using it inside the msm8974 interconnect driver too. Acked-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://msgid.link/20260324-msm8974-icc-v2-8-527280043ad8@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/msm8974.c | 1333 ++++++++++++++++++++++++--- 1 file changed, 1190 insertions(+), 143 deletions(-) diff --git a/drivers/interconnect/qcom/msm8974.c b/drivers/interconnect/qcom/msm8974.c index 144f225ec885..c020c61126ca 100644 --- a/drivers/interconnect/qcom/msm8974.c +++ b/drivers/interconnect/qcom/msm8974.c @@ -181,28 +181,75 @@ static int msm8974_get_bw(struct icc_node *node, u32 *avg, u32 *peak) return 0; }; -#define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id, \ - ...) \ - static const u16 _name ## _links[] = { \ - __VA_ARGS__ \ - }; \ - static struct qcom_icc_node _name = { \ - .name = #_name, \ - .id = _id, \ - .buswidth = _buswidth, \ - .mas_rpm_id = _mas_rpm_id, \ - .slv_rpm_id = _slv_rpm_id, \ - .num_links = ARRAY_SIZE(_name ## _links), \ - .links = _name ## _links, \ - } +static struct qcom_icc_node mas_ampss_m0 = { + .name = "mas_ampss_m0", + .id = MSM8974_BIMC_MAS_AMPSS_M0, + .buswidth = 8, + .mas_rpm_id = 0, + .slv_rpm_id = -1, +}; -DEFINE_QNODE(mas_ampss_m0, MSM8974_BIMC_MAS_AMPSS_M0, 8, 0, -1); -DEFINE_QNODE(mas_ampss_m1, MSM8974_BIMC_MAS_AMPSS_M1, 8, 0, -1); -DEFINE_QNODE(mas_mss_proc, MSM8974_BIMC_MAS_MSS_PROC, 8, 1, -1); -DEFINE_QNODE(bimc_to_mnoc, MSM8974_BIMC_TO_MNOC, 8, 2, -1, MSM8974_BIMC_SLV_EBI_CH0); -DEFINE_QNODE(bimc_to_snoc, MSM8974_BIMC_TO_SNOC, 8, 3, 2, MSM8974_SNOC_TO_BIMC, MSM8974_BIMC_SLV_EBI_CH0, MSM8974_BIMC_MAS_AMPSS_M0); -DEFINE_QNODE(slv_ebi_ch0, MSM8974_BIMC_SLV_EBI_CH0, 8, -1, 0); -DEFINE_QNODE(slv_ampss_l2, MSM8974_BIMC_SLV_AMPSS_L2, 8, -1, 1); +static struct qcom_icc_node mas_ampss_m1 = { + .name = "mas_ampss_m1", + .id = MSM8974_BIMC_MAS_AMPSS_M1, + .buswidth = 8, + .mas_rpm_id = 0, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_mss_proc = { + .name = "mas_mss_proc", + .id = MSM8974_BIMC_MAS_MSS_PROC, + .buswidth = 8, + .mas_rpm_id = 1, + .slv_rpm_id = -1, +}; + +static const u16 bimc_to_mnoc_links[] = { + MSM8974_BIMC_SLV_EBI_CH0 +}; + +static struct qcom_icc_node bimc_to_mnoc = { + .name = "bimc_to_mnoc", + .id = MSM8974_BIMC_TO_MNOC, + .buswidth = 8, + .mas_rpm_id = 2, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(bimc_to_mnoc_links), + .links = bimc_to_mnoc_links, +}; + +static const u16 bimc_to_snoc_links[] = { + MSM8974_SNOC_TO_BIMC, + MSM8974_BIMC_SLV_EBI_CH0, + MSM8974_BIMC_MAS_AMPSS_M0 +}; + +static struct qcom_icc_node bimc_to_snoc = { + .name = "bimc_to_snoc", + .id = MSM8974_BIMC_TO_SNOC, + .buswidth = 8, + .mas_rpm_id = 3, + .slv_rpm_id = 2, + .num_links = ARRAY_SIZE(bimc_to_snoc_links), + .links = bimc_to_snoc_links, +}; + +static struct qcom_icc_node slv_ebi_ch0 = { + .name = "slv_ebi_ch0", + .id = MSM8974_BIMC_SLV_EBI_CH0, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 0, +}; + +static struct qcom_icc_node slv_ampss_l2 = { + .name = "slv_ampss_l2", + .id = MSM8974_BIMC_SLV_AMPSS_L2, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 1, +}; static struct qcom_icc_node * const msm8974_bimc_nodes[] = { [BIMC_MAS_AMPSS_M0] = &mas_ampss_m0, @@ -222,43 +269,301 @@ static const struct qcom_icc_desc msm8974_bimc = { .ignore_enxio = true, }; -DEFINE_QNODE(mas_rpm_inst, MSM8974_CNOC_MAS_RPM_INST, 8, 45, -1); -DEFINE_QNODE(mas_rpm_data, MSM8974_CNOC_MAS_RPM_DATA, 8, 46, -1); -DEFINE_QNODE(mas_rpm_sys, MSM8974_CNOC_MAS_RPM_SYS, 8, 47, -1); -DEFINE_QNODE(mas_dehr, MSM8974_CNOC_MAS_DEHR, 8, 48, -1); -DEFINE_QNODE(mas_qdss_dap, MSM8974_CNOC_MAS_QDSS_DAP, 8, 49, -1); -DEFINE_QNODE(mas_spdm, MSM8974_CNOC_MAS_SPDM, 8, 50, -1); -DEFINE_QNODE(mas_tic, MSM8974_CNOC_MAS_TIC, 8, 51, -1); -DEFINE_QNODE(slv_clk_ctl, MSM8974_CNOC_SLV_CLK_CTL, 8, -1, 47); -DEFINE_QNODE(slv_cnoc_mss, MSM8974_CNOC_SLV_CNOC_MSS, 8, -1, 48); -DEFINE_QNODE(slv_security, MSM8974_CNOC_SLV_SECURITY, 8, -1, 49); -DEFINE_QNODE(slv_tcsr, MSM8974_CNOC_SLV_TCSR, 8, -1, 50); -DEFINE_QNODE(slv_tlmm, MSM8974_CNOC_SLV_TLMM, 8, -1, 51); -DEFINE_QNODE(slv_crypto_0_cfg, MSM8974_CNOC_SLV_CRYPTO_0_CFG, 8, -1, 52); -DEFINE_QNODE(slv_crypto_1_cfg, MSM8974_CNOC_SLV_CRYPTO_1_CFG, 8, -1, 53); -DEFINE_QNODE(slv_imem_cfg, MSM8974_CNOC_SLV_IMEM_CFG, 8, -1, 54); -DEFINE_QNODE(slv_message_ram, MSM8974_CNOC_SLV_MESSAGE_RAM, 8, -1, 55); -DEFINE_QNODE(slv_bimc_cfg, MSM8974_CNOC_SLV_BIMC_CFG, 8, -1, 56); -DEFINE_QNODE(slv_boot_rom, MSM8974_CNOC_SLV_BOOT_ROM, 8, -1, 57); -DEFINE_QNODE(slv_pmic_arb, MSM8974_CNOC_SLV_PMIC_ARB, 8, -1, 59); -DEFINE_QNODE(slv_spdm_wrapper, MSM8974_CNOC_SLV_SPDM_WRAPPER, 8, -1, 60); -DEFINE_QNODE(slv_dehr_cfg, MSM8974_CNOC_SLV_DEHR_CFG, 8, -1, 61); -DEFINE_QNODE(slv_mpm, MSM8974_CNOC_SLV_MPM, 8, -1, 62); -DEFINE_QNODE(slv_qdss_cfg, MSM8974_CNOC_SLV_QDSS_CFG, 8, -1, 63); -DEFINE_QNODE(slv_rbcpr_cfg, MSM8974_CNOC_SLV_RBCPR_CFG, 8, -1, 64); -DEFINE_QNODE(slv_rbcpr_qdss_apu_cfg, MSM8974_CNOC_SLV_RBCPR_QDSS_APU_CFG, 8, -1, 65); -DEFINE_QNODE(cnoc_to_snoc, MSM8974_CNOC_TO_SNOC, 8, 52, 75); -DEFINE_QNODE(slv_cnoc_onoc_cfg, MSM8974_CNOC_SLV_CNOC_ONOC_CFG, 8, -1, 68); -DEFINE_QNODE(slv_cnoc_mnoc_mmss_cfg, MSM8974_CNOC_SLV_CNOC_MNOC_MMSS_CFG, 8, -1, 58); -DEFINE_QNODE(slv_cnoc_mnoc_cfg, MSM8974_CNOC_SLV_CNOC_MNOC_CFG, 8, -1, 66); -DEFINE_QNODE(slv_pnoc_cfg, MSM8974_CNOC_SLV_PNOC_CFG, 8, -1, 69); -DEFINE_QNODE(slv_snoc_mpu_cfg, MSM8974_CNOC_SLV_SNOC_MPU_CFG, 8, -1, 67); -DEFINE_QNODE(slv_snoc_cfg, MSM8974_CNOC_SLV_SNOC_CFG, 8, -1, 70); -DEFINE_QNODE(slv_ebi1_dll_cfg, MSM8974_CNOC_SLV_EBI1_DLL_CFG, 8, -1, 71); -DEFINE_QNODE(slv_phy_apu_cfg, MSM8974_CNOC_SLV_PHY_APU_CFG, 8, -1, 72); -DEFINE_QNODE(slv_ebi1_phy_cfg, MSM8974_CNOC_SLV_EBI1_PHY_CFG, 8, -1, 73); -DEFINE_QNODE(slv_rpm, MSM8974_CNOC_SLV_RPM, 8, -1, 74); -DEFINE_QNODE(slv_service_cnoc, MSM8974_CNOC_SLV_SERVICE_CNOC, 8, -1, 76); +static struct qcom_icc_node mas_rpm_inst = { + .name = "mas_rpm_inst", + .id = MSM8974_CNOC_MAS_RPM_INST, + .buswidth = 8, + .mas_rpm_id = 45, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_rpm_data = { + .name = "mas_rpm_data", + .id = MSM8974_CNOC_MAS_RPM_DATA, + .buswidth = 8, + .mas_rpm_id = 46, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_rpm_sys = { + .name = "mas_rpm_sys", + .id = MSM8974_CNOC_MAS_RPM_SYS, + .buswidth = 8, + .mas_rpm_id = 47, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_dehr = { + .name = "mas_dehr", + .id = MSM8974_CNOC_MAS_DEHR, + .buswidth = 8, + .mas_rpm_id = 48, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_qdss_dap = { + .name = "mas_qdss_dap", + .id = MSM8974_CNOC_MAS_QDSS_DAP, + .buswidth = 8, + .mas_rpm_id = 49, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_spdm = { + .name = "mas_spdm", + .id = MSM8974_CNOC_MAS_SPDM, + .buswidth = 8, + .mas_rpm_id = 50, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_tic = { + .name = "mas_tic", + .id = MSM8974_CNOC_MAS_TIC, + .buswidth = 8, + .mas_rpm_id = 51, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_clk_ctl = { + .name = "slv_clk_ctl", + .id = MSM8974_CNOC_SLV_CLK_CTL, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 47, +}; + +static struct qcom_icc_node slv_cnoc_mss = { + .name = "slv_cnoc_mss", + .id = MSM8974_CNOC_SLV_CNOC_MSS, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 48, +}; + +static struct qcom_icc_node slv_security = { + .name = "slv_security", + .id = MSM8974_CNOC_SLV_SECURITY, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 49, +}; + +static struct qcom_icc_node slv_tcsr = { + .name = "slv_tcsr", + .id = MSM8974_CNOC_SLV_TCSR, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 50, +}; + +static struct qcom_icc_node slv_tlmm = { + .name = "slv_tlmm", + .id = MSM8974_CNOC_SLV_TLMM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 51, +}; + +static struct qcom_icc_node slv_crypto_0_cfg = { + .name = "slv_crypto_0_cfg", + .id = MSM8974_CNOC_SLV_CRYPTO_0_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 52, +}; + +static struct qcom_icc_node slv_crypto_1_cfg = { + .name = "slv_crypto_1_cfg", + .id = MSM8974_CNOC_SLV_CRYPTO_1_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 53, +}; + +static struct qcom_icc_node slv_imem_cfg = { + .name = "slv_imem_cfg", + .id = MSM8974_CNOC_SLV_IMEM_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 54, +}; + +static struct qcom_icc_node slv_message_ram = { + .name = "slv_message_ram", + .id = MSM8974_CNOC_SLV_MESSAGE_RAM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 55, +}; + +static struct qcom_icc_node slv_bimc_cfg = { + .name = "slv_bimc_cfg", + .id = MSM8974_CNOC_SLV_BIMC_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 56, +}; + +static struct qcom_icc_node slv_boot_rom = { + .name = "slv_boot_rom", + .id = MSM8974_CNOC_SLV_BOOT_ROM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 57, +}; + +static struct qcom_icc_node slv_pmic_arb = { + .name = "slv_pmic_arb", + .id = MSM8974_CNOC_SLV_PMIC_ARB, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 59, +}; + +static struct qcom_icc_node slv_spdm_wrapper = { + .name = "slv_spdm_wrapper", + .id = MSM8974_CNOC_SLV_SPDM_WRAPPER, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 60, +}; + +static struct qcom_icc_node slv_dehr_cfg = { + .name = "slv_dehr_cfg", + .id = MSM8974_CNOC_SLV_DEHR_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 61, +}; + +static struct qcom_icc_node slv_mpm = { + .name = "slv_mpm", + .id = MSM8974_CNOC_SLV_MPM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 62, +}; + +static struct qcom_icc_node slv_qdss_cfg = { + .name = "slv_qdss_cfg", + .id = MSM8974_CNOC_SLV_QDSS_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 63, +}; + +static struct qcom_icc_node slv_rbcpr_cfg = { + .name = "slv_rbcpr_cfg", + .id = MSM8974_CNOC_SLV_RBCPR_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 64, +}; + +static struct qcom_icc_node slv_rbcpr_qdss_apu_cfg = { + .name = "slv_rbcpr_qdss_apu_cfg", + .id = MSM8974_CNOC_SLV_RBCPR_QDSS_APU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 65, +}; + +static struct qcom_icc_node cnoc_to_snoc = { + .name = "cnoc_to_snoc", + .id = MSM8974_CNOC_TO_SNOC, + .buswidth = 8, + .mas_rpm_id = 52, + .slv_rpm_id = 75, +}; + +static struct qcom_icc_node slv_cnoc_onoc_cfg = { + .name = "slv_cnoc_onoc_cfg", + .id = MSM8974_CNOC_SLV_CNOC_ONOC_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 68, +}; + +static struct qcom_icc_node slv_cnoc_mnoc_mmss_cfg = { + .name = "slv_cnoc_mnoc_mmss_cfg", + .id = MSM8974_CNOC_SLV_CNOC_MNOC_MMSS_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 58, +}; + +static struct qcom_icc_node slv_cnoc_mnoc_cfg = { + .name = "slv_cnoc_mnoc_cfg", + .id = MSM8974_CNOC_SLV_CNOC_MNOC_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 66, +}; + +static struct qcom_icc_node slv_pnoc_cfg = { + .name = "slv_pnoc_cfg", + .id = MSM8974_CNOC_SLV_PNOC_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 69, +}; + +static struct qcom_icc_node slv_snoc_mpu_cfg = { + .name = "slv_snoc_mpu_cfg", + .id = MSM8974_CNOC_SLV_SNOC_MPU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 67, +}; + +static struct qcom_icc_node slv_snoc_cfg = { + .name = "slv_snoc_cfg", + .id = MSM8974_CNOC_SLV_SNOC_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 70, +}; + +static struct qcom_icc_node slv_ebi1_dll_cfg = { + .name = "slv_ebi1_dll_cfg", + .id = MSM8974_CNOC_SLV_EBI1_DLL_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 71, +}; + +static struct qcom_icc_node slv_phy_apu_cfg = { + .name = "slv_phy_apu_cfg", + .id = MSM8974_CNOC_SLV_PHY_APU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 72, +}; + +static struct qcom_icc_node slv_ebi1_phy_cfg = { + .name = "slv_ebi1_phy_cfg", + .id = MSM8974_CNOC_SLV_EBI1_PHY_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 73, +}; + +static struct qcom_icc_node slv_rpm = { + .name = "slv_rpm", + .id = MSM8974_CNOC_SLV_RPM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 74, +}; + +static struct qcom_icc_node slv_service_cnoc = { + .name = "slv_service_cnoc", + .id = MSM8974_CNOC_SLV_SERVICE_CNOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 76, +}; static struct qcom_icc_node * const msm8974_cnoc_nodes[] = { [CNOC_MAS_RPM_INST] = &mas_rpm_inst, @@ -308,28 +613,211 @@ static const struct qcom_icc_desc msm8974_cnoc = { .ignore_enxio = true, }; -DEFINE_QNODE(mas_graphics_3d, MSM8974_MNOC_MAS_GRAPHICS_3D, 16, 6, -1, MSM8974_MNOC_TO_BIMC); -DEFINE_QNODE(mas_jpeg, MSM8974_MNOC_MAS_JPEG, 16, 7, -1, MSM8974_MNOC_TO_BIMC); -DEFINE_QNODE(mas_mdp_port0, MSM8974_MNOC_MAS_MDP_PORT0, 16, 8, -1, MSM8974_MNOC_TO_BIMC); -DEFINE_QNODE(mas_video_p0, MSM8974_MNOC_MAS_VIDEO_P0, 16, 9, -1); -DEFINE_QNODE(mas_video_p1, MSM8974_MNOC_MAS_VIDEO_P1, 16, 10, -1); -DEFINE_QNODE(mas_vfe, MSM8974_MNOC_MAS_VFE, 16, 11, -1, MSM8974_MNOC_TO_BIMC); -DEFINE_QNODE(mnoc_to_cnoc, MSM8974_MNOC_TO_CNOC, 16, 4, -1); -DEFINE_QNODE(mnoc_to_bimc, MSM8974_MNOC_TO_BIMC, 16, -1, 16, MSM8974_BIMC_TO_MNOC); -DEFINE_QNODE(slv_camera_cfg, MSM8974_MNOC_SLV_CAMERA_CFG, 16, -1, 3); -DEFINE_QNODE(slv_display_cfg, MSM8974_MNOC_SLV_DISPLAY_CFG, 16, -1, 4); -DEFINE_QNODE(slv_ocmem_cfg, MSM8974_MNOC_SLV_OCMEM_CFG, 16, -1, 5); -DEFINE_QNODE(slv_cpr_cfg, MSM8974_MNOC_SLV_CPR_CFG, 16, -1, 6); -DEFINE_QNODE(slv_cpr_xpu_cfg, MSM8974_MNOC_SLV_CPR_XPU_CFG, 16, -1, 7); -DEFINE_QNODE(slv_misc_cfg, MSM8974_MNOC_SLV_MISC_CFG, 16, -1, 8); -DEFINE_QNODE(slv_misc_xpu_cfg, MSM8974_MNOC_SLV_MISC_XPU_CFG, 16, -1, 9); -DEFINE_QNODE(slv_venus_cfg, MSM8974_MNOC_SLV_VENUS_CFG, 16, -1, 10); -DEFINE_QNODE(slv_graphics_3d_cfg, MSM8974_MNOC_SLV_GRAPHICS_3D_CFG, 16, -1, 11); -DEFINE_QNODE(slv_mmss_clk_cfg, MSM8974_MNOC_SLV_MMSS_CLK_CFG, 16, -1, 12); -DEFINE_QNODE(slv_mmss_clk_xpu_cfg, MSM8974_MNOC_SLV_MMSS_CLK_XPU_CFG, 16, -1, 13); -DEFINE_QNODE(slv_mnoc_mpu_cfg, MSM8974_MNOC_SLV_MNOC_MPU_CFG, 16, -1, 14); -DEFINE_QNODE(slv_onoc_mpu_cfg, MSM8974_MNOC_SLV_ONOC_MPU_CFG, 16, -1, 15); -DEFINE_QNODE(slv_service_mnoc, MSM8974_MNOC_SLV_SERVICE_MNOC, 16, -1, 17); +static const u16 mas_graphics_3d_links[] = { + MSM8974_MNOC_TO_BIMC +}; + +static struct qcom_icc_node mas_graphics_3d = { + .name = "mas_graphics_3d", + .id = MSM8974_MNOC_MAS_GRAPHICS_3D, + .buswidth = 16, + .mas_rpm_id = 6, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_graphics_3d_links), + .links = mas_graphics_3d_links, +}; + +static const u16 mas_jpeg_links[] = { + MSM8974_MNOC_TO_BIMC +}; + +static struct qcom_icc_node mas_jpeg = { + .name = "mas_jpeg", + .id = MSM8974_MNOC_MAS_JPEG, + .buswidth = 16, + .mas_rpm_id = 7, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_jpeg_links), + .links = mas_jpeg_links, +}; + +static const u16 mas_mdp_port0_links[] = { + MSM8974_MNOC_TO_BIMC +}; + +static struct qcom_icc_node mas_mdp_port0 = { + .name = "mas_mdp_port0", + .id = MSM8974_MNOC_MAS_MDP_PORT0, + .buswidth = 16, + .mas_rpm_id = 8, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_mdp_port0_links), + .links = mas_mdp_port0_links, +}; + +static struct qcom_icc_node mas_video_p0 = { + .name = "mas_video_p0", + .id = MSM8974_MNOC_MAS_VIDEO_P0, + .buswidth = 16, + .mas_rpm_id = 9, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_video_p1 = { + .name = "mas_video_p1", + .id = MSM8974_MNOC_MAS_VIDEO_P1, + .buswidth = 16, + .mas_rpm_id = 10, + .slv_rpm_id = -1, +}; + +static const u16 mas_vfe_links[] = { + MSM8974_MNOC_TO_BIMC +}; + +static struct qcom_icc_node mas_vfe = { + .name = "mas_vfe", + .id = MSM8974_MNOC_MAS_VFE, + .buswidth = 16, + .mas_rpm_id = 11, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_vfe_links), + .links = mas_vfe_links, +}; + +static struct qcom_icc_node mnoc_to_cnoc = { + .name = "mnoc_to_cnoc", + .id = MSM8974_MNOC_TO_CNOC, + .buswidth = 16, + .mas_rpm_id = 4, + .slv_rpm_id = -1, +}; + +static const u16 mnoc_to_bimc_links[] = { + MSM8974_BIMC_TO_MNOC +}; + +static struct qcom_icc_node mnoc_to_bimc = { + .name = "mnoc_to_bimc", + .id = MSM8974_MNOC_TO_BIMC, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 16, + .num_links = ARRAY_SIZE(mnoc_to_bimc_links), + .links = mnoc_to_bimc_links, +}; + +static struct qcom_icc_node slv_camera_cfg = { + .name = "slv_camera_cfg", + .id = MSM8974_MNOC_SLV_CAMERA_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 3, +}; + +static struct qcom_icc_node slv_display_cfg = { + .name = "slv_display_cfg", + .id = MSM8974_MNOC_SLV_DISPLAY_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 4, +}; + +static struct qcom_icc_node slv_ocmem_cfg = { + .name = "slv_ocmem_cfg", + .id = MSM8974_MNOC_SLV_OCMEM_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 5, +}; + +static struct qcom_icc_node slv_cpr_cfg = { + .name = "slv_cpr_cfg", + .id = MSM8974_MNOC_SLV_CPR_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 6, +}; + +static struct qcom_icc_node slv_cpr_xpu_cfg = { + .name = "slv_cpr_xpu_cfg", + .id = MSM8974_MNOC_SLV_CPR_XPU_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 7, +}; + +static struct qcom_icc_node slv_misc_cfg = { + .name = "slv_misc_cfg", + .id = MSM8974_MNOC_SLV_MISC_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 8, +}; + +static struct qcom_icc_node slv_misc_xpu_cfg = { + .name = "slv_misc_xpu_cfg", + .id = MSM8974_MNOC_SLV_MISC_XPU_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 9, +}; + +static struct qcom_icc_node slv_venus_cfg = { + .name = "slv_venus_cfg", + .id = MSM8974_MNOC_SLV_VENUS_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 10, +}; + +static struct qcom_icc_node slv_graphics_3d_cfg = { + .name = "slv_graphics_3d_cfg", + .id = MSM8974_MNOC_SLV_GRAPHICS_3D_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 11, +}; + +static struct qcom_icc_node slv_mmss_clk_cfg = { + .name = "slv_mmss_clk_cfg", + .id = MSM8974_MNOC_SLV_MMSS_CLK_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 12, +}; + +static struct qcom_icc_node slv_mmss_clk_xpu_cfg = { + .name = "slv_mmss_clk_xpu_cfg", + .id = MSM8974_MNOC_SLV_MMSS_CLK_XPU_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 13, +}; + +static struct qcom_icc_node slv_mnoc_mpu_cfg = { + .name = "slv_mnoc_mpu_cfg", + .id = MSM8974_MNOC_SLV_MNOC_MPU_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 14, +}; + +static struct qcom_icc_node slv_onoc_mpu_cfg = { + .name = "slv_onoc_mpu_cfg", + .id = MSM8974_MNOC_SLV_ONOC_MPU_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 15, +}; + +static struct qcom_icc_node slv_service_mnoc = { + .name = "slv_service_mnoc", + .id = MSM8974_MNOC_SLV_SERVICE_MNOC, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 17, +}; static struct qcom_icc_node * const msm8974_mnoc_nodes[] = { [MNOC_MAS_GRAPHICS_3D] = &mas_graphics_3d, @@ -363,20 +851,121 @@ static const struct qcom_icc_desc msm8974_mnoc = { .ignore_enxio = true, }; -DEFINE_QNODE(ocmem_noc_to_ocmem_vnoc, MSM8974_OCMEM_NOC_TO_OCMEM_VNOC, 16, 54, 78, MSM8974_OCMEM_SLV_OCMEM); -DEFINE_QNODE(mas_jpeg_ocmem, MSM8974_OCMEM_MAS_JPEG_OCMEM, 16, 13, -1); -DEFINE_QNODE(mas_mdp_ocmem, MSM8974_OCMEM_MAS_MDP_OCMEM, 16, 14, -1); -DEFINE_QNODE(mas_video_p0_ocmem, MSM8974_OCMEM_MAS_VIDEO_P0_OCMEM, 16, 15, -1); -DEFINE_QNODE(mas_video_p1_ocmem, MSM8974_OCMEM_MAS_VIDEO_P1_OCMEM, 16, 16, -1); -DEFINE_QNODE(mas_vfe_ocmem, MSM8974_OCMEM_MAS_VFE_OCMEM, 16, 17, -1); -DEFINE_QNODE(mas_cnoc_onoc_cfg, MSM8974_OCMEM_MAS_CNOC_ONOC_CFG, 16, 12, -1); -DEFINE_QNODE(slv_service_onoc, MSM8974_OCMEM_SLV_SERVICE_ONOC, 16, -1, 19); -DEFINE_QNODE(slv_ocmem, MSM8974_OCMEM_SLV_OCMEM, 16, -1, 18); +static const u16 ocmem_noc_to_ocmem_vnoc_links[] = { + MSM8974_OCMEM_SLV_OCMEM +}; + +static struct qcom_icc_node ocmem_noc_to_ocmem_vnoc = { + .name = "ocmem_noc_to_ocmem_vnoc", + .id = MSM8974_OCMEM_NOC_TO_OCMEM_VNOC, + .buswidth = 16, + .mas_rpm_id = 54, + .slv_rpm_id = 78, + .num_links = ARRAY_SIZE(ocmem_noc_to_ocmem_vnoc_links), + .links = ocmem_noc_to_ocmem_vnoc_links, +}; + +static struct qcom_icc_node mas_jpeg_ocmem = { + .name = "mas_jpeg_ocmem", + .id = MSM8974_OCMEM_MAS_JPEG_OCMEM, + .buswidth = 16, + .mas_rpm_id = 13, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_mdp_ocmem = { + .name = "mas_mdp_ocmem", + .id = MSM8974_OCMEM_MAS_MDP_OCMEM, + .buswidth = 16, + .mas_rpm_id = 14, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_video_p0_ocmem = { + .name = "mas_video_p0_ocmem", + .id = MSM8974_OCMEM_MAS_VIDEO_P0_OCMEM, + .buswidth = 16, + .mas_rpm_id = 15, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_video_p1_ocmem = { + .name = "mas_video_p1_ocmem", + .id = MSM8974_OCMEM_MAS_VIDEO_P1_OCMEM, + .buswidth = 16, + .mas_rpm_id = 16, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_vfe_ocmem = { + .name = "mas_vfe_ocmem", + .id = MSM8974_OCMEM_MAS_VFE_OCMEM, + .buswidth = 16, + .mas_rpm_id = 17, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_cnoc_onoc_cfg = { + .name = "mas_cnoc_onoc_cfg", + .id = MSM8974_OCMEM_MAS_CNOC_ONOC_CFG, + .buswidth = 16, + .mas_rpm_id = 12, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_service_onoc = { + .name = "slv_service_onoc", + .id = MSM8974_OCMEM_SLV_SERVICE_ONOC, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 19, +}; + +static struct qcom_icc_node slv_ocmem = { + .name = "slv_ocmem", + .id = MSM8974_OCMEM_SLV_OCMEM, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 18, +}; /* Virtual NoC is needed for connection to OCMEM */ -DEFINE_QNODE(ocmem_vnoc_to_onoc, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC, 16, 56, 79, MSM8974_OCMEM_NOC_TO_OCMEM_VNOC); -DEFINE_QNODE(ocmem_vnoc_to_snoc, MSM8974_OCMEM_VNOC_TO_SNOC, 8, 57, 80); -DEFINE_QNODE(mas_v_ocmem_gfx3d, MSM8974_OCMEM_VNOC_MAS_GFX3D, 8, 55, -1, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC); +static const u16 ocmem_vnoc_to_onoc_links[] = { + MSM8974_OCMEM_NOC_TO_OCMEM_VNOC +}; + +static struct qcom_icc_node ocmem_vnoc_to_onoc = { + .name = "ocmem_vnoc_to_onoc", + .id = MSM8974_OCMEM_VNOC_TO_OCMEM_NOC, + .buswidth = 16, + .mas_rpm_id = 56, + .slv_rpm_id = 79, + .num_links = ARRAY_SIZE(ocmem_vnoc_to_onoc_links), + .links = ocmem_vnoc_to_onoc_links, +}; + +static struct qcom_icc_node ocmem_vnoc_to_snoc = { + .name = "ocmem_vnoc_to_snoc", + .id = MSM8974_OCMEM_VNOC_TO_SNOC, + .buswidth = 8, + .mas_rpm_id = 57, + .slv_rpm_id = 80, +}; + +static const u16 mas_v_ocmem_gfx3d_links[] = { + MSM8974_OCMEM_VNOC_TO_OCMEM_NOC +}; + +static struct qcom_icc_node mas_v_ocmem_gfx3d = { + .name = "mas_v_ocmem_gfx3d", + .id = MSM8974_OCMEM_VNOC_MAS_GFX3D, + .buswidth = 8, + .mas_rpm_id = 55, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_v_ocmem_gfx3d_links), + .links = mas_v_ocmem_gfx3d_links, +}; + static struct qcom_icc_node * const msm8974_onoc_nodes[] = { [OCMEM_NOC_TO_OCMEM_VNOC] = &ocmem_noc_to_ocmem_vnoc, @@ -401,33 +990,288 @@ static const struct qcom_icc_desc msm8974_onoc = { .ignore_enxio = true, }; -DEFINE_QNODE(mas_pnoc_cfg, MSM8974_PNOC_MAS_PNOC_CFG, 8, 43, -1); -DEFINE_QNODE(mas_sdcc_1, MSM8974_PNOC_MAS_SDCC_1, 8, 33, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(mas_sdcc_3, MSM8974_PNOC_MAS_SDCC_3, 8, 34, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(mas_sdcc_4, MSM8974_PNOC_MAS_SDCC_4, 8, 36, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(mas_sdcc_2, MSM8974_PNOC_MAS_SDCC_2, 8, 35, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(mas_tsif, MSM8974_PNOC_MAS_TSIF, 8, 37, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(mas_bam_dma, MSM8974_PNOC_MAS_BAM_DMA, 8, 38, -1); -DEFINE_QNODE(mas_blsp_2, MSM8974_PNOC_MAS_BLSP_2, 8, 39, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(mas_usb_hsic, MSM8974_PNOC_MAS_USB_HSIC, 8, 40, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(mas_blsp_1, MSM8974_PNOC_MAS_BLSP_1, 8, 41, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(mas_usb_hs, MSM8974_PNOC_MAS_USB_HS, 8, 42, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(pnoc_to_snoc, MSM8974_PNOC_TO_SNOC, 8, 44, 45, MSM8974_SNOC_TO_PNOC, MSM8974_PNOC_SLV_PRNG); -DEFINE_QNODE(slv_sdcc_1, MSM8974_PNOC_SLV_SDCC_1, 8, -1, 31); -DEFINE_QNODE(slv_sdcc_3, MSM8974_PNOC_SLV_SDCC_3, 8, -1, 32); -DEFINE_QNODE(slv_sdcc_2, MSM8974_PNOC_SLV_SDCC_2, 8, -1, 33); -DEFINE_QNODE(slv_sdcc_4, MSM8974_PNOC_SLV_SDCC_4, 8, -1, 34); -DEFINE_QNODE(slv_tsif, MSM8974_PNOC_SLV_TSIF, 8, -1, 35); -DEFINE_QNODE(slv_bam_dma, MSM8974_PNOC_SLV_BAM_DMA, 8, -1, 36); -DEFINE_QNODE(slv_blsp_2, MSM8974_PNOC_SLV_BLSP_2, 8, -1, 37); -DEFINE_QNODE(slv_usb_hsic, MSM8974_PNOC_SLV_USB_HSIC, 8, -1, 38); -DEFINE_QNODE(slv_blsp_1, MSM8974_PNOC_SLV_BLSP_1, 8, -1, 39); -DEFINE_QNODE(slv_usb_hs, MSM8974_PNOC_SLV_USB_HS, 8, -1, 40); -DEFINE_QNODE(slv_pdm, MSM8974_PNOC_SLV_PDM, 8, -1, 41); -DEFINE_QNODE(slv_periph_apu_cfg, MSM8974_PNOC_SLV_PERIPH_APU_CFG, 8, -1, 42); -DEFINE_QNODE(slv_pnoc_mpu_cfg, MSM8974_PNOC_SLV_PNOC_MPU_CFG, 8, -1, 43); -DEFINE_QNODE(slv_prng, MSM8974_PNOC_SLV_PRNG, 8, -1, 44, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(slv_service_pnoc, MSM8974_PNOC_SLV_SERVICE_PNOC, 8, -1, 46); +static struct qcom_icc_node mas_pnoc_cfg = { + .name = "mas_pnoc_cfg", + .id = MSM8974_PNOC_MAS_PNOC_CFG, + .buswidth = 8, + .mas_rpm_id = 43, + .slv_rpm_id = -1, +}; + +static const u16 mas_sdcc_1_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_sdcc_1 = { + .name = "mas_sdcc_1", + .id = MSM8974_PNOC_MAS_SDCC_1, + .buswidth = 8, + .mas_rpm_id = 33, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_sdcc_1_links), + .links = mas_sdcc_1_links, +}; + +static const u16 mas_sdcc_3_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_sdcc_3 = { + .name = "mas_sdcc_3", + .id = MSM8974_PNOC_MAS_SDCC_3, + .buswidth = 8, + .mas_rpm_id = 34, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_sdcc_3_links), + .links = mas_sdcc_3_links, +}; + +static const u16 mas_sdcc_4_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_sdcc_4 = { + .name = "mas_sdcc_4", + .id = MSM8974_PNOC_MAS_SDCC_4, + .buswidth = 8, + .mas_rpm_id = 36, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_sdcc_4_links), + .links = mas_sdcc_4_links, +}; + +static const u16 mas_sdcc_2_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_sdcc_2 = { + .name = "mas_sdcc_2", + .id = MSM8974_PNOC_MAS_SDCC_2, + .buswidth = 8, + .mas_rpm_id = 35, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_sdcc_2_links), + .links = mas_sdcc_2_links, +}; + +static const u16 mas_tsif_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_tsif = { + .name = "mas_tsif", + .id = MSM8974_PNOC_MAS_TSIF, + .buswidth = 8, + .mas_rpm_id = 37, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_tsif_links), + .links = mas_tsif_links, +}; + +static struct qcom_icc_node mas_bam_dma = { + .name = "mas_bam_dma", + .id = MSM8974_PNOC_MAS_BAM_DMA, + .buswidth = 8, + .mas_rpm_id = 38, + .slv_rpm_id = -1, +}; + +static const u16 mas_blsp_2_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_blsp_2 = { + .name = "mas_blsp_2", + .id = MSM8974_PNOC_MAS_BLSP_2, + .buswidth = 8, + .mas_rpm_id = 39, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_blsp_2_links), + .links = mas_blsp_2_links, +}; + +static const u16 mas_usb_hsic_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_usb_hsic = { + .name = "mas_usb_hsic", + .id = MSM8974_PNOC_MAS_USB_HSIC, + .buswidth = 8, + .mas_rpm_id = 40, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_usb_hsic_links), + .links = mas_usb_hsic_links, +}; + +static const u16 mas_blsp_1_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_blsp_1 = { + .name = "mas_blsp_1", + .id = MSM8974_PNOC_MAS_BLSP_1, + .buswidth = 8, + .mas_rpm_id = 41, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_blsp_1_links), + .links = mas_blsp_1_links, +}; + +static const u16 mas_usb_hs_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_usb_hs = { + .name = "mas_usb_hs", + .id = MSM8974_PNOC_MAS_USB_HS, + .buswidth = 8, + .mas_rpm_id = 42, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_usb_hs_links), + .links = mas_usb_hs_links, +}; + +static const u16 pnoc_to_snoc_links[] = { + MSM8974_SNOC_TO_PNOC, + MSM8974_PNOC_SLV_PRNG +}; + +static struct qcom_icc_node pnoc_to_snoc = { + .name = "pnoc_to_snoc", + .id = MSM8974_PNOC_TO_SNOC, + .buswidth = 8, + .mas_rpm_id = 44, + .slv_rpm_id = 45, + .num_links = ARRAY_SIZE(pnoc_to_snoc_links), + .links = pnoc_to_snoc_links, +}; + +static struct qcom_icc_node slv_sdcc_1 = { + .name = "slv_sdcc_1", + .id = MSM8974_PNOC_SLV_SDCC_1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 31, +}; + +static struct qcom_icc_node slv_sdcc_3 = { + .name = "slv_sdcc_3", + .id = MSM8974_PNOC_SLV_SDCC_3, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 32, +}; + +static struct qcom_icc_node slv_sdcc_2 = { + .name = "slv_sdcc_2", + .id = MSM8974_PNOC_SLV_SDCC_2, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 33, +}; + +static struct qcom_icc_node slv_sdcc_4 = { + .name = "slv_sdcc_4", + .id = MSM8974_PNOC_SLV_SDCC_4, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 34, +}; + +static struct qcom_icc_node slv_tsif = { + .name = "slv_tsif", + .id = MSM8974_PNOC_SLV_TSIF, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 35, +}; + +static struct qcom_icc_node slv_bam_dma = { + .name = "slv_bam_dma", + .id = MSM8974_PNOC_SLV_BAM_DMA, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 36, +}; + +static struct qcom_icc_node slv_blsp_2 = { + .name = "slv_blsp_2", + .id = MSM8974_PNOC_SLV_BLSP_2, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 37, +}; + +static struct qcom_icc_node slv_usb_hsic = { + .name = "slv_usb_hsic", + .id = MSM8974_PNOC_SLV_USB_HSIC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 38, +}; + +static struct qcom_icc_node slv_blsp_1 = { + .name = "slv_blsp_1", + .id = MSM8974_PNOC_SLV_BLSP_1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 39, +}; + +static struct qcom_icc_node slv_usb_hs = { + .name = "slv_usb_hs", + .id = MSM8974_PNOC_SLV_USB_HS, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 40, +}; + +static struct qcom_icc_node slv_pdm = { + .name = "slv_pdm", + .id = MSM8974_PNOC_SLV_PDM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 41, +}; + +static struct qcom_icc_node slv_periph_apu_cfg = { + .name = "slv_periph_apu_cfg", + .id = MSM8974_PNOC_SLV_PERIPH_APU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 42, +}; + +static struct qcom_icc_node slv_pnoc_mpu_cfg = { + .name = "slv_pnoc_mpu_cfg", + .id = MSM8974_PNOC_SLV_PNOC_MPU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 43, +}; + +static const u16 slv_prng_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node slv_prng = { + .name = "slv_prng", + .id = MSM8974_PNOC_SLV_PRNG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 44, + .num_links = ARRAY_SIZE(slv_prng_links), + .links = slv_prng_links, +}; + +static struct qcom_icc_node slv_service_pnoc = { + .name = "slv_service_pnoc", + .id = MSM8974_PNOC_SLV_SERVICE_PNOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 46, +}; static struct qcom_icc_node * const msm8974_pnoc_nodes[] = { [PNOC_MAS_PNOC_CFG] = &mas_pnoc_cfg, @@ -468,30 +1312,233 @@ static const struct qcom_icc_desc msm8974_pnoc = { .ignore_enxio = true, }; -DEFINE_QNODE(mas_lpass_ahb, MSM8974_SNOC_MAS_LPASS_AHB, 8, 18, -1); -DEFINE_QNODE(mas_qdss_bam, MSM8974_SNOC_MAS_QDSS_BAM, 8, 19, -1); -DEFINE_QNODE(mas_snoc_cfg, MSM8974_SNOC_MAS_SNOC_CFG, 8, 20, -1); -DEFINE_QNODE(snoc_to_bimc, MSM8974_SNOC_TO_BIMC, 8, 21, 24, MSM8974_BIMC_TO_SNOC); -DEFINE_QNODE(snoc_to_cnoc, MSM8974_SNOC_TO_CNOC, 8, 22, 25); -DEFINE_QNODE(snoc_to_pnoc, MSM8974_SNOC_TO_PNOC, 8, 29, 28, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(snoc_to_ocmem_vnoc, MSM8974_SNOC_TO_OCMEM_VNOC, 8, 53, 77, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC); -DEFINE_QNODE(mas_crypto_core0, MSM8974_SNOC_MAS_CRYPTO_CORE0, 8, 23, -1, MSM8974_SNOC_TO_BIMC); -DEFINE_QNODE(mas_crypto_core1, MSM8974_SNOC_MAS_CRYPTO_CORE1, 8, 24, -1); -DEFINE_QNODE(mas_lpass_proc, MSM8974_SNOC_MAS_LPASS_PROC, 8, 25, -1, MSM8974_SNOC_TO_OCMEM_VNOC); -DEFINE_QNODE(mas_mss, MSM8974_SNOC_MAS_MSS, 8, 26, -1); -DEFINE_QNODE(mas_mss_nav, MSM8974_SNOC_MAS_MSS_NAV, 8, 27, -1); -DEFINE_QNODE(mas_ocmem_dma, MSM8974_SNOC_MAS_OCMEM_DMA, 8, 28, -1); -DEFINE_QNODE(mas_wcss, MSM8974_SNOC_MAS_WCSS, 8, 30, -1); -DEFINE_QNODE(mas_qdss_etr, MSM8974_SNOC_MAS_QDSS_ETR, 8, 31, -1); -DEFINE_QNODE(mas_usb3, MSM8974_SNOC_MAS_USB3, 8, 32, -1, MSM8974_SNOC_TO_BIMC); -DEFINE_QNODE(slv_ampss, MSM8974_SNOC_SLV_AMPSS, 8, -1, 20); -DEFINE_QNODE(slv_lpass, MSM8974_SNOC_SLV_LPASS, 8, -1, 21); -DEFINE_QNODE(slv_usb3, MSM8974_SNOC_SLV_USB3, 8, -1, 22); -DEFINE_QNODE(slv_wcss, MSM8974_SNOC_SLV_WCSS, 8, -1, 23); -DEFINE_QNODE(slv_ocimem, MSM8974_SNOC_SLV_OCIMEM, 8, -1, 26); -DEFINE_QNODE(slv_snoc_ocmem, MSM8974_SNOC_SLV_SNOC_OCMEM, 8, -1, 27); -DEFINE_QNODE(slv_service_snoc, MSM8974_SNOC_SLV_SERVICE_SNOC, 8, -1, 29); -DEFINE_QNODE(slv_qdss_stm, MSM8974_SNOC_SLV_QDSS_STM, 8, -1, 30); +static struct qcom_icc_node mas_lpass_ahb = { + .name = "mas_lpass_ahb", + .id = MSM8974_SNOC_MAS_LPASS_AHB, + .buswidth = 8, + .mas_rpm_id = 18, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_qdss_bam = { + .name = "mas_qdss_bam", + .id = MSM8974_SNOC_MAS_QDSS_BAM, + .buswidth = 8, + .mas_rpm_id = 19, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_snoc_cfg = { + .name = "mas_snoc_cfg", + .id = MSM8974_SNOC_MAS_SNOC_CFG, + .buswidth = 8, + .mas_rpm_id = 20, + .slv_rpm_id = -1, +}; + +static const u16 snoc_to_bimc_links[] = { + MSM8974_BIMC_TO_SNOC +}; + +static struct qcom_icc_node snoc_to_bimc = { + .name = "snoc_to_bimc", + .id = MSM8974_SNOC_TO_BIMC, + .buswidth = 8, + .mas_rpm_id = 21, + .slv_rpm_id = 24, + .num_links = ARRAY_SIZE(snoc_to_bimc_links), + .links = snoc_to_bimc_links, +}; + +static struct qcom_icc_node snoc_to_cnoc = { + .name = "snoc_to_cnoc", + .id = MSM8974_SNOC_TO_CNOC, + .buswidth = 8, + .mas_rpm_id = 22, + .slv_rpm_id = 25, +}; + +static const u16 snoc_to_pnoc_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node snoc_to_pnoc = { + .name = "snoc_to_pnoc", + .id = MSM8974_SNOC_TO_PNOC, + .buswidth = 8, + .mas_rpm_id = 29, + .slv_rpm_id = 28, + .num_links = ARRAY_SIZE(snoc_to_pnoc_links), + .links = snoc_to_pnoc_links, +}; + +static const u16 snoc_to_ocmem_vnoc_links[] = { + MSM8974_OCMEM_VNOC_TO_OCMEM_NOC +}; + +static struct qcom_icc_node snoc_to_ocmem_vnoc = { + .name = "snoc_to_ocmem_vnoc", + .id = MSM8974_SNOC_TO_OCMEM_VNOC, + .buswidth = 8, + .mas_rpm_id = 53, + .slv_rpm_id = 77, + .num_links = ARRAY_SIZE(snoc_to_ocmem_vnoc_links), + .links = snoc_to_ocmem_vnoc_links, +}; + +static const u16 mas_crypto_core0_links[] = { + MSM8974_SNOC_TO_BIMC +}; + +static struct qcom_icc_node mas_crypto_core0 = { + .name = "mas_crypto_core0", + .id = MSM8974_SNOC_MAS_CRYPTO_CORE0, + .buswidth = 8, + .mas_rpm_id = 23, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_crypto_core0_links), + .links = mas_crypto_core0_links, +}; + +static struct qcom_icc_node mas_crypto_core1 = { + .name = "mas_crypto_core1", + .id = MSM8974_SNOC_MAS_CRYPTO_CORE1, + .buswidth = 8, + .mas_rpm_id = 24, + .slv_rpm_id = -1, +}; + +static const u16 mas_lpass_proc_links[] = { + MSM8974_SNOC_TO_OCMEM_VNOC +}; + +static struct qcom_icc_node mas_lpass_proc = { + .name = "mas_lpass_proc", + .id = MSM8974_SNOC_MAS_LPASS_PROC, + .buswidth = 8, + .mas_rpm_id = 25, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_lpass_proc_links), + .links = mas_lpass_proc_links, +}; + +static struct qcom_icc_node mas_mss = { + .name = "mas_mss", + .id = MSM8974_SNOC_MAS_MSS, + .buswidth = 8, + .mas_rpm_id = 26, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_mss_nav = { + .name = "mas_mss_nav", + .id = MSM8974_SNOC_MAS_MSS_NAV, + .buswidth = 8, + .mas_rpm_id = 27, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_ocmem_dma = { + .name = "mas_ocmem_dma", + .id = MSM8974_SNOC_MAS_OCMEM_DMA, + .buswidth = 8, + .mas_rpm_id = 28, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_wcss = { + .name = "mas_wcss", + .id = MSM8974_SNOC_MAS_WCSS, + .buswidth = 8, + .mas_rpm_id = 30, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_qdss_etr = { + .name = "mas_qdss_etr", + .id = MSM8974_SNOC_MAS_QDSS_ETR, + .buswidth = 8, + .mas_rpm_id = 31, + .slv_rpm_id = -1, +}; + +static const u16 mas_usb3_links[] = { + MSM8974_SNOC_TO_BIMC +}; + +static struct qcom_icc_node mas_usb3 = { + .name = "mas_usb3", + .id = MSM8974_SNOC_MAS_USB3, + .buswidth = 8, + .mas_rpm_id = 32, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_usb3_links), + .links = mas_usb3_links, +}; + +static struct qcom_icc_node slv_ampss = { + .name = "slv_ampss", + .id = MSM8974_SNOC_SLV_AMPSS, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 20, +}; + +static struct qcom_icc_node slv_lpass = { + .name = "slv_lpass", + .id = MSM8974_SNOC_SLV_LPASS, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 21, +}; + +static struct qcom_icc_node slv_usb3 = { + .name = "slv_usb3", + .id = MSM8974_SNOC_SLV_USB3, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 22, +}; + +static struct qcom_icc_node slv_wcss = { + .name = "slv_wcss", + .id = MSM8974_SNOC_SLV_WCSS, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 23, +}; + +static struct qcom_icc_node slv_ocimem = { + .name = "slv_ocimem", + .id = MSM8974_SNOC_SLV_OCIMEM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 26, +}; + +static struct qcom_icc_node slv_snoc_ocmem = { + .name = "slv_snoc_ocmem", + .id = MSM8974_SNOC_SLV_SNOC_OCMEM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 27, +}; + +static struct qcom_icc_node slv_service_snoc = { + .name = "slv_service_snoc", + .id = MSM8974_SNOC_SLV_SERVICE_SNOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 29, +}; + +static struct qcom_icc_node slv_qdss_stm = { + .name = "slv_qdss_stm", + .id = MSM8974_SNOC_SLV_QDSS_STM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 30, +}; static struct qcom_icc_node * const msm8974_snoc_nodes[] = { [SNOC_MAS_LPASS_AHB] = &mas_lpass_ahb, From 7913c1de9c3cbe3018fc29ce25a4d462ac2eaa82 Mon Sep 17 00:00:00 2001 From: Milan Misic Date: Tue, 24 Mar 2026 20:36:26 +0100 Subject: [PATCH 280/405] iio: imu: st_lsm6dsx: Add ACPI ID for SHIFT13mi gyroscope The SHIFT13mi or SHIFTbook tablet device by the German manufacturer SHIFT contains an STM LSM6DSO IMU declared in the DSDT with the hardware ID SMOCF00. Add this ID to the ACPI match table so that the driver binds correctly to this device. WHO_AM_I register returns 0x6c, confirming LSM6DSO. Signed-off-by: Milan Misic Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c index 7c933218036b..b2a7c2eaf50d 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -144,6 +144,7 @@ MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); static const struct acpi_device_id st_lsm6dsx_i2c_acpi_match[] = { { "SMO8B30", ST_LSM6DS3TRC_ID, }, + { "SMOCF00", ST_LSM6DSO_ID, }, { } }; MODULE_DEVICE_TABLE(acpi, st_lsm6dsx_i2c_acpi_match); From ab8293caad03a6a0cc83c7451ecfbe31f5f257f6 Mon Sep 17 00:00:00 2001 From: Nick Xie Date: Wed, 25 Mar 2026 15:06:15 +0800 Subject: [PATCH 281/405] dt-bindings: iio: adc: amlogic,meson-saradc: add S4 compatible Add the compatible string for the SARADC (Successive Approximation Register ADC) IP block found in the Amlogic Meson S4 SoC. There are no known differences between the SARADC on S4 and the one on G12A. Therefore, it uses "amlogic,meson-g12a-saradc" as a proper specific fallback. Also add a comment indicating that "amlogic,meson-saradc" must not be used for new devices. It's a made up compatible string that does not correspond to a specific hardware generation and is not used to match any driver. For old devices we keep it as it's part of the ABI. Signed-off-by: Nick Xie Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/amlogic,meson-saradc.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.yaml b/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.yaml index bb9825e7346d..70ab4e140e71 100644 --- a/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.yaml @@ -27,7 +27,11 @@ properties: - amlogic,meson-gxm-saradc - amlogic,meson-axg-saradc - amlogic,meson-g12a-saradc + # Usage of this generic fallback is not allowed for new devices - const: amlogic,meson-saradc + - items: + - const: amlogic,meson-s4-saradc + - const: amlogic,meson-g12a-saradc reg: maxItems: 1 From 8175ffc989d51c383c044a07bb54179a6e93437e Mon Sep 17 00:00:00 2001 From: Nick Xie Date: Wed, 25 Mar 2026 15:06:16 +0800 Subject: [PATCH 282/405] iio: adc: meson-saradc: add support for Meson S4 Add support for the SARADC found on the Amlogic Meson S4 SoC. According to the documentation and current testing, it is fully compatible with the G12A parameter set, so we reuse `meson_sar_adc_g12a_data` for this new compatible string. Although the device tree fallback mechanism could handle the match, a dedicated entry is added to ensure the userspace ABI correctly reports the specific part name ("meson-s4-saradc"). This allows userspace to accurately identify the exact device and maintains consistency across different firmware types where automatic fallback parsing might be problematic. Reviewed-by: Martin Blumenstingl Signed-off-by: Nick Xie Signed-off-by: Jonathan Cameron --- drivers/iio/adc/meson_saradc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index ed91edf0e391..23991a3612bd 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -1314,6 +1314,11 @@ static const struct meson_sar_adc_data meson_sar_adc_g12a_data = { .name = "meson-g12a-saradc", }; +static const struct meson_sar_adc_data meson_sar_adc_s4_data = { + .param = &meson_sar_adc_g12a_param, + .name = "meson-s4-saradc", +}; + static const struct of_device_id meson_sar_adc_of_match[] = { { .compatible = "amlogic,meson8-saradc", @@ -1342,6 +1347,9 @@ static const struct of_device_id meson_sar_adc_of_match[] = { }, { .compatible = "amlogic,meson-g12a-saradc", .data = &meson_sar_adc_g12a_data, + }, { + .compatible = "amlogic,meson-s4-saradc", + .data = &meson_sar_adc_s4_data, }, { } }; From 94e6fb6261a4b13b1ddbe8e5368334903cec40b3 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 26 Mar 2026 18:32:16 +0000 Subject: [PATCH 283/405] dt-bindings: iio: amplifiers: ad8366: add adrf5702/3 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add compatible entries for ADRF5702 and ADRF5703 Digital Attenuators. ADRF5702 is an 8-bit DSA with a step of 0.125 dB and ADRF5703 is a 7-bit DSA with a step 0.25 dB. Then, each device ends up with its own gain range, hence no fallback compatibles are used. Reviewed-by: Nuno Sá Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/amplifiers/adi,ad8366.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/amplifiers/adi,ad8366.yaml b/Documentation/devicetree/bindings/iio/amplifiers/adi,ad8366.yaml index 2719de1166a1..065637ce33a5 100644 --- a/Documentation/devicetree/bindings/iio/amplifiers/adi,ad8366.yaml +++ b/Documentation/devicetree/bindings/iio/amplifiers/adi,ad8366.yaml @@ -20,6 +20,8 @@ properties: - adi,ad8366 - adi,ada4961 - adi,adl5240 + - adi,adrf5702 + - adi,adrf5703 - adi,adrf5720 - adi,adrf5730 - adi,adrf5731 @@ -66,6 +68,8 @@ allOf: anyOf: - const: adi,ad8366 - const: adi,ada4961 + - const: adi,adrf5702 + - const: adi,adrf5703 - const: adi,adrf5720 - const: adi,adrf5730 - const: adi,adrf5731 From d185324efadc1e75acc6be2e4351ecfe8957b3a7 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 26 Mar 2026 18:32:17 +0000 Subject: [PATCH 284/405] iio: amplifiers: ad8366: add support for adrf5702/3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add chip info structs and device table entries for ADRF5702 and ADRF5703 Digital Step Attenuators. Reviewed-by: Nuno Sá Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/Kconfig | 2 ++ drivers/iio/amplifiers/ad8366.c | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/drivers/iio/amplifiers/Kconfig b/drivers/iio/amplifiers/Kconfig index 39d280d4d437..9e24421b5e97 100644 --- a/drivers/iio/amplifiers/Kconfig +++ b/drivers/iio/amplifiers/Kconfig @@ -18,6 +18,8 @@ config AD8366 AD8366 Dual-Digital Variable Gain Amplifier (VGA) ADA4961 BiCMOS RF Digital Gain Amplifier (DGA) ADL5240 Digitally controlled variable gain amplifier (VGA) + ADRF5702: 0.125 dB LSB, 8-Bit, Silicon Digital Attenuator + ADRF5703: 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator ADRF5720: 0.5 dB LSB, 6-Bit, Silicon Digital Attenuator ADRF5730: 0.5 dB LSB, 6-Bit, Silicon Digital Attenuator ADRF5731: 2 dB LSB, 4-Bit, Silicon Digital Attenuator diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index 334ca91c0f59..bbf41a1fb3a1 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -5,6 +5,8 @@ * AD8366 Dual-Digital Variable Gain Amplifier (VGA) * ADA4961 BiCMOS RF Digital Gain Amplifier (DGA) * ADL5240 Digitally controlled variable gain amplifier (VGA) + * ADRF5702: 0.125 dB LSB, 8-Bit, Silicon Digital Attenuator, 50 MHz to 20 GHz + * ADRF5703: 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator, 9 kHz to 20 GHz * ADRF5720: 0.5 dB LSB, 6-Bit, Silicon Digital Attenuator, 9 kHz to 40 GHz * ADRF5730: 0.5 dB LSB, 6-Bit, Silicon Digital Attenuator, 100 MHz to 40 GHz * ADRF5731: 2 dB LSB, 4-Bit, Silicon Digital Attenuator, 100 MHz to 40 GHz @@ -106,6 +108,22 @@ static const struct ad8366_info adl5240_chip_info = { .num_channels = 1, }; +static const struct ad8366_info adrf5702_chip_info = { + .name = "adrf5702", + .gain_min = -31875, + .gain_max = 0, + .gain_step = -125, + .num_channels = 1, +}; + +static const struct ad8366_info adrf5703_chip_info = { + .name = "adrf5703", + .gain_min = -31750, + .gain_max = 0, + .gain_step = -250, + .num_channels = 1, +}; + static const struct ad8366_info adrf5720_chip_info = { .name = "adrf5720", .gain_min = -31500, @@ -337,6 +355,8 @@ static const struct spi_device_id ad8366_id[] = { { "ad8366", (kernel_ulong_t)&ad8366_chip_info }, { "ada4961", (kernel_ulong_t)&ada4961_chip_info }, { "adl5240", (kernel_ulong_t)&adl5240_chip_info }, + { "adrf5702", (kernel_ulong_t)&adrf5702_chip_info }, + { "adrf5703", (kernel_ulong_t)&adrf5703_chip_info }, { "adrf5720", (kernel_ulong_t)&adrf5720_chip_info }, { "adrf5730", (kernel_ulong_t)&adrf5730_chip_info }, { "adrf5731", (kernel_ulong_t)&adrf5731_chip_info }, @@ -353,6 +373,8 @@ static const struct of_device_id ad8366_of_match[] = { { .compatible = "adi,ad8366", .data = &ad8366_chip_info }, { .compatible = "adi,ada4961", .data = &ada4961_chip_info }, { .compatible = "adi,adl5240", .data = &adl5240_chip_info }, + { .compatible = "adi,adrf5702", .data = &adrf5702_chip_info }, + { .compatible = "adi,adrf5703", .data = &adrf5703_chip_info }, { .compatible = "adi,adrf5720", .data = &adrf5720_chip_info }, { .compatible = "adi,adrf5730", .data = &adrf5730_chip_info }, { .compatible = "adi,adrf5731", .data = &adrf5731_chip_info }, From d2a4ec19d2a2e54c23b5180e939994d3da4a6b91 Mon Sep 17 00:00:00 2001 From: Ammar Mustafa Date: Fri, 27 Feb 2026 14:08:33 -0500 Subject: [PATCH 285/405] Docs: iio: ad7191 Correct clock configuration Correct the ad7191 documentation to match the datasheet: - Fix inverted CLKSEL pin logic: device uses external clock when pin is inactive, and internal CMOS/crystal when high. - Correct CMOS-compatible clock pin from MCLK2 to MCLK1. Signed-off-by: Ammar Mustafa Signed-off-by: Jonathan Cameron --- Documentation/iio/ad7191.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/iio/ad7191.rst b/Documentation/iio/ad7191.rst index 977d4fea14b0..fd6a23ad44fd 100644 --- a/Documentation/iio/ad7191.rst +++ b/Documentation/iio/ad7191.rst @@ -63,11 +63,11 @@ Clock Configuration The AD7191 supports both internal and external clock sources: -- When CLKSEL pin is tied LOW: Uses internal 4.92MHz clock (no clock property +- When CLKSEL pin is ACTIVE: Uses internal 4.92MHz clock (no clock property needed) -- When CLKSEL pin is tied HIGH: Requires external clock source +- When CLKSEL pin is INACTIVE: Requires external clock source - Can be a crystal between MCLK1 and MCLK2 pins - - Or a CMOS-compatible clock driving MCLK2 pin + - Or a CMOS-compatible clock driving MCLK1 pin and MCLK2 left unconnected - Must specify the "clocks" property in device tree when using external clock SPI Interface Requirements From 08643a8760e81fe0bf91c58f7ee9e19b4db3a24f Mon Sep 17 00:00:00 2001 From: Jie Gan Date: Fri, 27 Mar 2026 14:24:14 +0800 Subject: [PATCH 286/405] coresight: cti: fix the check condition in inout_sel_store Correct the upper bound from CTIINOUTEN_MAX to config->nr_trig_max, since nr_trig_max varies across CTI devices. An out-of-bounds issue occurs when a value greater than config->nr_trig_max is provided, leading to unexpected errors. Fixes: b5213376c240 ("coresight: cti: Add sysfs access to program function registers") Signed-off-by: Jie Gan Reviewed-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260327-fix-cti-issue-v1-1-2c8921e21fc8@oss.qualcomm.com --- drivers/hwtracing/coresight/coresight-cti-sysfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 4c0a60840efb..bf3c73607c1c 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -337,10 +337,11 @@ static ssize_t inout_sel_store(struct device *dev, { unsigned long val; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; if (kstrtoul(buf, 0, &val)) return -EINVAL; - if (val > (CTIINOUTEN_MAX - 1)) + if (val >= config->nr_trig_max) return -EINVAL; guard(raw_spinlock_irqsave)(&drvdata->spinlock); From ada4280812a7a40455a842b1de24f8450e04254e Mon Sep 17 00:00:00 2001 From: Jie Gan Date: Fri, 20 Mar 2026 15:31:12 +0800 Subject: [PATCH 287/405] coresight: platform: check the availability of the endpoint before parse Check endpoint availability before parsing it. If parsing a connected endpoint fails, the probe is deferred until the endpoint becomes available, or eventually fails. In some legacy cases, a replicator has two output ports where one is disabled and the other is available. The replicator probe always fails because the disabled endpoint never becomes available for parsing. In addition, there is no need to defer probing a device that is connected to a disabled device, which improves probe performance. Signed-off-by: Jie Gan Reviewed-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260320-add-availability-check-v1-1-b2e39cdeb6e0@oss.qualcomm.com --- drivers/hwtracing/coresight/coresight-platform.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c index 0ca3bd762454..e337b6e2bf32 100644 --- a/drivers/hwtracing/coresight/coresight-platform.c +++ b/drivers/hwtracing/coresight/coresight-platform.c @@ -220,6 +220,8 @@ static int of_coresight_parse_endpoint(struct device *dev, rparent = of_coresight_get_port_parent(rep); if (!rparent) break; + if (!of_device_is_available(rparent)) + break; if (of_graph_parse_endpoint(rep, &rendpoint)) break; From ec687ba84000d7d50cf243558041f6729d1d8119 Mon Sep 17 00:00:00 2001 From: Jie Gan Date: Tue, 31 Mar 2026 18:05:22 +0800 Subject: [PATCH 288/405] coresight: tpdm: add traceid_show for checking traceid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Save the trace ID in drvdata during TPDM enablement and expose it to userspace to support trace data parsing. The TPDM device’s trace ID corresponds to the trace ID allocated to the connected TPDA device. Signed-off-by: Jie Gan Reviewed-by: James Clark Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260331-add-traceid-show-for-tpdm-v4-1-ed3dda24a562@oss.qualcomm.com --- .../testing/sysfs-bus-coresight-devices-tpdm | 10 ++++++ drivers/hwtracing/coresight/coresight-tpdm.c | 34 ++++++++++++++++++- drivers/hwtracing/coresight/coresight-tpdm.h | 2 ++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-tpdm b/Documentation/ABI/testing/sysfs-bus-coresight-devices-tpdm index f8016df64532..bc36ba32c900 100644 --- a/Documentation/ABI/testing/sysfs-bus-coresight-devices-tpdm +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-tpdm @@ -278,3 +278,13 @@ Date: Aug 2025 KernelVersion 6.18 Contact: Mao Jinlong Description: (Read) Show hardware context information of device. + +What: /sys/bus/coresight/devices//traceid +Date: March 2026 +KernelVersion: 7.1 +Contact: Jie Gan +Description: + (R) Show the trace ID that will appear in the trace stream + coming from this TPDM. The trace ID is inherited from the + connected TPDA device and is fixed for the lifetime of the + device. Returns -EINVAL if the device has not been enabled yet. diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c index da77bdaad0a4..9b16f368a58b 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.c +++ b/drivers/hwtracing/coresight/coresight-tpdm.c @@ -481,7 +481,7 @@ static void __tpdm_enable(struct tpdm_drvdata *drvdata) static int tpdm_enable(struct coresight_device *csdev, struct perf_event *event, enum cs_mode mode, - __maybe_unused struct coresight_path *path) + struct coresight_path *path) { struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -497,6 +497,7 @@ static int tpdm_enable(struct coresight_device *csdev, struct perf_event *event, } __tpdm_enable(drvdata); + drvdata->traceid = path->trace_id; drvdata->enable = true; spin_unlock(&drvdata->spinlock); @@ -693,6 +694,29 @@ static struct attribute_group tpdm_attr_grp = { .attrs = tpdm_attrs, }; +static ssize_t traceid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->traceid; + if (!val) + return -EINVAL; + + return sysfs_emit(buf, "%#lx\n", val); +} +static DEVICE_ATTR_RO(traceid); + +static struct attribute *traceid_attrs[] = { + &dev_attr_traceid.attr, + NULL, +}; + +static struct attribute_group traceid_attr_grp = { + .attrs = traceid_attrs, +}; + static ssize_t dsb_mode_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1367,6 +1391,12 @@ static const struct attribute_group *tpdm_attr_grps[] = { &tpdm_cmb_patt_grp, &tpdm_cmb_msr_grp, &tpdm_mcmb_attr_grp, + &traceid_attr_grp, + NULL, +}; + +static const struct attribute_group *static_tpdm_attr_grps[] = { + &traceid_attr_grp, NULL, }; @@ -1425,6 +1455,8 @@ static int tpdm_probe(struct device *dev, struct resource *res) desc.access = CSDEV_ACCESS_IOMEM(base); if (res) desc.groups = tpdm_attr_grps; + else + desc.groups = static_tpdm_attr_grps; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); diff --git a/drivers/hwtracing/coresight/coresight-tpdm.h b/drivers/hwtracing/coresight/coresight-tpdm.h index 2867f3ab8186..11da64e1ade8 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.h +++ b/drivers/hwtracing/coresight/coresight-tpdm.h @@ -300,6 +300,7 @@ struct cmb_dataset { * @cmb Specifics associated to TPDM CMB. * @dsb_msr_num Number of MSR supported by DSB TPDM * @cmb_msr_num Number of MSR supported by CMB TPDM + * @traceid Trace ID of the path. */ struct tpdm_drvdata { @@ -313,6 +314,7 @@ struct tpdm_drvdata { struct cmb_dataset *cmb; u32 dsb_msr_num; u32 cmb_msr_num; + u8 traceid; }; /* Enumerate members of various datasets */ From 5326a18e3e640061ca4b65c1b732feaeace61c39 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 6 Mar 2026 11:28:46 +0000 Subject: [PATCH 289/405] rust_binder: introduce TransactionInfo Rust Binder exposes information about transactions that are sent in various ways: printing to the kernel log, tracepoints, files in binderfs, and the upcoming netlink support. Currently all these mechanisms use disparate ways of obtaining the same information, so let's introduce a single Info struct that collects all the required information in a single place, so that all of these different mechanisms can operate in a more uniform way. For now, the new info struct is only used to replace a few things: * The BinderTransactionDataSg struct that is passed as an argument to several methods is removed as the information is moved into the new info struct and passed down that way. * The oneway spam detection fields on Transaction and Allocation can be removed, as the information can be returned to the caller via the mutable info struct instead. But several other uses of the info struct are planned in follow-up patches. Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260306-transaction-info-v1-1-fda58fca558b@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/allocation.rs | 3 - drivers/android/binder/error.rs | 10 +- drivers/android/binder/process.rs | 15 +- drivers/android/binder/thread.rs | 190 +++++++++++++++----------- drivers/android/binder/transaction.rs | 83 ++++++----- rust/kernel/uaccess.rs | 2 +- 6 files changed, 168 insertions(+), 135 deletions(-) diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index 7f65a9c3a0e5..97edfb1ff382 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -56,7 +56,6 @@ pub(crate) struct Allocation { pub(crate) process: Arc, allocation_info: Option, free_on_drop: bool, - pub(crate) oneway_spam_detected: bool, #[allow(dead_code)] pub(crate) debug_id: usize, } @@ -68,7 +67,6 @@ pub(crate) fn new( offset: usize, size: usize, ptr: usize, - oneway_spam_detected: bool, ) -> Self { Self { process, @@ -76,7 +74,6 @@ pub(crate) fn new( size, ptr, debug_id, - oneway_spam_detected, allocation_info: None, free_on_drop: true, } diff --git a/drivers/android/binder/error.rs b/drivers/android/binder/error.rs index b24497cfa292..45d85d4c2815 100644 --- a/drivers/android/binder/error.rs +++ b/drivers/android/binder/error.rs @@ -13,7 +13,7 @@ /// errno. pub(crate) struct BinderError { pub(crate) reply: u32, - source: Option, + pub(crate) source: Option, } impl BinderError { @@ -41,14 +41,6 @@ pub(crate) fn new_frozen_oneway() -> Self { pub(crate) fn is_dead(&self) -> bool { self.reply == BR_DEAD_REPLY } - - pub(crate) fn as_errno(&self) -> kernel::ffi::c_int { - self.source.unwrap_or(EINVAL).to_errno() - } - - pub(crate) fn should_pr_warn(&self) -> bool { - self.source.is_some() - } } /// Convert an errno into a `BinderError` and store the errno used to construct it. The errno diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index da81a9569365..ae26fe817794 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -48,6 +48,7 @@ range_alloc::{RangeAllocator, ReserveNew, ReserveNewArgs}, stats::BinderStats, thread::{PushWorkRes, Thread}, + transaction::TransactionInfo, BinderfsProcFile, DArc, DLArc, DTRWrap, DeliverToRead, }; @@ -1003,16 +1004,15 @@ pub(crate) fn buffer_alloc( self: &Arc, debug_id: usize, size: usize, - is_oneway: bool, - from_pid: i32, + info: &mut TransactionInfo, ) -> BinderResult { use kernel::page::PAGE_SIZE; let mut reserve_new_args = ReserveNewArgs { debug_id, size, - is_oneway, - pid: from_pid, + is_oneway: info.is_oneway(), + pid: info.from_pid, ..ReserveNewArgs::default() }; @@ -1028,13 +1028,13 @@ pub(crate) fn buffer_alloc( reserve_new_args = alloc_request.make_alloc()?; }; + info.oneway_spam_suspect = new_alloc.oneway_spam_detected; let res = Allocation::new( self.clone(), debug_id, new_alloc.offset, size, addr + new_alloc.offset, - new_alloc.oneway_spam_detected, ); // This allocation will be marked as in use until the `Allocation` is used to free it. @@ -1066,7 +1066,7 @@ pub(crate) fn buffer_get(self: &Arc, ptr: usize) -> Option { let mapping = inner.mapping.as_mut()?; let offset = ptr.checked_sub(mapping.address)?; let (size, debug_id, odata) = mapping.alloc.reserve_existing(offset).ok()?; - let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr, false); + let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr); if let Some(data) = odata { alloc.set_info(data); } @@ -1414,8 +1414,7 @@ fn deferred_release(self: Arc) { .alloc .take_for_each(|offset, size, debug_id, odata| { let ptr = offset + address; - let mut alloc = - Allocation::new(self.clone(), debug_id, offset, size, ptr, false); + let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr); if let Some(data) = odata { alloc.set_info(data); } diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 6f283de53213..97a5e4acf64c 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -19,7 +19,7 @@ sync::poll::{PollCondVar, PollTable}, sync::{aref::ARef, Arc, SpinLock}, task::Task, - uaccess::UserSlice, + uaccess::{UserPtr, UserSlice, UserSliceReader}, uapi, }; @@ -30,7 +30,7 @@ process::{GetWorkOrRegister, Process}, ptr_align, stats::GLOBAL_STATS, - transaction::Transaction, + transaction::{Transaction, TransactionInfo}, BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead, }; @@ -951,13 +951,11 @@ fn apply_sg(&self, alloc: &mut Allocation, sg_state: &mut ScatterGatherState) -> pub(crate) fn copy_transaction_data( &self, to_process: Arc, - tr: &BinderTransactionDataSg, + info: &mut TransactionInfo, debug_id: usize, allow_fds: bool, txn_security_ctx_offset: Option<&mut usize>, ) -> BinderResult { - let trd = &tr.transaction_data; - let is_oneway = trd.flags & TF_ONE_WAY != 0; let mut secctx = if let Some(offset) = txn_security_ctx_offset { let secid = self.process.cred.get_secid(); let ctx = match security::SecurityCtx::from_secid(secid) { @@ -972,10 +970,10 @@ pub(crate) fn copy_transaction_data( None }; - let data_size = trd.data_size.try_into().map_err(|_| EINVAL)?; + let data_size = info.data_size; let aligned_data_size = ptr_align(data_size).ok_or(EINVAL)?; - let offsets_size: usize = trd.offsets_size.try_into().map_err(|_| EINVAL)?; - let buffers_size: usize = tr.buffers_size.try_into().map_err(|_| EINVAL)?; + let offsets_size = info.offsets_size; + let buffers_size = info.buffers_size; let aligned_secctx_size = match secctx.as_ref() { Some((_offset, ctx)) => ptr_align(ctx.len()).ok_or(EINVAL)?, None => 0, @@ -998,32 +996,25 @@ pub(crate) fn copy_transaction_data( size_of::(), ); let secctx_off = aligned_data_size + offsets_size + buffers_size; - let mut alloc = - match to_process.buffer_alloc(debug_id, len, is_oneway, self.process.task.pid()) { - Ok(alloc) => alloc, - Err(err) => { - pr_warn!( - "Failed to allocate buffer. len:{}, is_oneway:{}", - len, - is_oneway - ); - return Err(err); - } - }; + let mut alloc = match to_process.buffer_alloc(debug_id, len, info) { + Ok(alloc) => alloc, + Err(err) => { + pr_warn!( + "Failed to allocate buffer. len:{}, is_oneway:{}", + len, + info.is_oneway(), + ); + return Err(err); + } + }; - // SAFETY: This accesses a union field, but it's okay because the field's type is valid for - // all bit-patterns. - let trd_data_ptr = unsafe { &trd.data.ptr }; - let mut buffer_reader = - UserSlice::new(UserPtr::from_addr(trd_data_ptr.buffer as _), data_size).reader(); + let mut buffer_reader = UserSlice::new(info.data_ptr, data_size).reader(); let mut end_of_previous_object = 0; let mut sg_state = None; // Copy offsets if there are any. if offsets_size > 0 { - let mut offsets_reader = - UserSlice::new(UserPtr::from_addr(trd_data_ptr.offsets as _), offsets_size) - .reader(); + let mut offsets_reader = UserSlice::new(info.offsets_ptr, offsets_size).reader(); let offsets_start = aligned_data_size; let offsets_end = aligned_data_size + offsets_size; @@ -1198,37 +1189,92 @@ fn top_of_transaction_stack(&self) -> Result>> { } } - fn transaction(self: &Arc, tr: &BinderTransactionDataSg, inner: T) - where - T: FnOnce(&Arc, &BinderTransactionDataSg) -> BinderResult, - { - if let Err(err) = inner(self, tr) { - if err.should_pr_warn() { - let mut ee = self.inner.lock().extended_error; - ee.command = err.reply; - ee.param = err.as_errno(); - pr_warn!( - "Transaction failed: {:?} my_pid:{}", - err, - self.process.pid_in_current_ns() - ); + // No inlining avoids allocating stack space for `BinderTransactionData` for the entire + // duration of `transaction()`. + #[inline(never)] + fn read_transaction_info( + &self, + cmd: u32, + reader: &mut UserSliceReader, + info: &mut TransactionInfo, + ) -> Result<()> { + let td = match cmd { + BC_TRANSACTION | BC_REPLY => { + reader.read::()?.with_buffers_size(0) + } + BC_TRANSACTION_SG | BC_REPLY_SG => reader.read::()?, + _ => return Err(EINVAL), + }; + + // SAFETY: Above `read` call initializes all bytes, so this union read is ok. + let trd_data_ptr = unsafe { &td.transaction_data.data.ptr }; + + info.is_reply = matches!(cmd, BC_REPLY | BC_REPLY_SG); + info.from_pid = self.process.task.pid(); + info.from_tid = self.id; + info.code = td.transaction_data.code; + info.flags = td.transaction_data.flags; + info.data_ptr = UserPtr::from_addr(trd_data_ptr.buffer as usize); + info.data_size = td.transaction_data.data_size as usize; + info.offsets_ptr = UserPtr::from_addr(trd_data_ptr.offsets as usize); + info.offsets_size = td.transaction_data.offsets_size as usize; + info.buffers_size = td.buffers_size as usize; + // SAFETY: Above `read` call initializes all bytes, so this union read is ok. + info.target_handle = unsafe { td.transaction_data.target.handle }; + Ok(()) + } + + #[inline(never)] + fn transaction(self: &Arc, cmd: u32, reader: &mut UserSliceReader) -> Result<()> { + let mut info = TransactionInfo::zeroed(); + self.read_transaction_info(cmd, reader, &mut info)?; + + let ret = if info.is_reply { + self.reply_inner(&mut info) + } else if info.is_oneway() { + self.oneway_transaction_inner(&mut info) + } else { + self.transaction_inner(&mut info) + }; + + if let Err(err) = ret { + if err.reply != BR_TRANSACTION_COMPLETE { + info.reply = err.reply; } self.push_return_work(err.reply); + if let Some(source) = &err.source { + info.errno = source.to_errno(); + info.reply = err.reply; + + { + let mut ee = self.inner.lock().extended_error; + ee.command = err.reply; + ee.param = source.to_errno(); + } + + pr_warn!( + "{}:{} transaction to {} failed: {source:?}", + info.from_pid, + info.from_tid, + info.to_pid + ); + } } + + Ok(()) } - fn transaction_inner(self: &Arc, tr: &BinderTransactionDataSg) -> BinderResult { - // SAFETY: Handle's type has no invalid bit patterns. - let handle = unsafe { tr.transaction_data.target.handle }; - let node_ref = self.process.get_transaction_node(handle)?; + fn transaction_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { + let node_ref = self.process.get_transaction_node(info.target_handle)?; + info.to_pid = node_ref.node.owner.task.pid(); security::binder_transaction(&self.process.cred, &node_ref.node.owner.cred)?; // TODO: We need to ensure that there isn't a pending transaction in the work queue. How // could this happen? let top = self.top_of_transaction_stack()?; let list_completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?; let completion = list_completion.clone_arc(); - let transaction = Transaction::new(node_ref, top, self, tr)?; + let transaction = Transaction::new(node_ref, top, self, info)?; // Check that the transaction stack hasn't changed while the lock was released, then update // it with the new transaction. @@ -1244,7 +1290,7 @@ fn transaction_inner(self: &Arc, tr: &BinderTransactionDataSg) -> BinderRe inner.push_work_deferred(list_completion); } - if let Err(e) = transaction.submit() { + if let Err(e) = transaction.submit(info) { completion.skip(); // Define `transaction` first to drop it after `inner`. let transaction; @@ -1257,18 +1303,21 @@ fn transaction_inner(self: &Arc, tr: &BinderTransactionDataSg) -> BinderRe } } - fn reply_inner(self: &Arc, tr: &BinderTransactionDataSg) -> BinderResult { + fn reply_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { let orig = self.inner.lock().pop_transaction_to_reply(self)?; if !orig.from.is_current_transaction(&orig) { return Err(EINVAL.into()); } + info.to_tid = orig.from.id; + info.to_pid = orig.from.process.task.pid(); + // We need to complete the transaction even if we cannot complete building the reply. let out = (|| -> BinderResult<_> { let completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?; let process = orig.from.process.clone(); let allow_fds = orig.flags & TF_ACCEPT_FDS != 0; - let reply = Transaction::new_reply(self, process, tr, allow_fds)?; + let reply = Transaction::new_reply(self, process, info, allow_fds)?; self.inner.lock().push_work(completion); orig.from.deliver_reply(Ok(reply), &orig); Ok(()) @@ -1289,16 +1338,12 @@ fn reply_inner(self: &Arc, tr: &BinderTransactionDataSg) -> BinderResult { out } - fn oneway_transaction_inner(self: &Arc, tr: &BinderTransactionDataSg) -> BinderResult { - // SAFETY: The `handle` field is valid for all possible byte values, so reading from the - // union is okay. - let handle = unsafe { tr.transaction_data.target.handle }; - let node_ref = self.process.get_transaction_node(handle)?; + fn oneway_transaction_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { + let node_ref = self.process.get_transaction_node(info.target_handle)?; + info.to_pid = node_ref.node.owner.task.pid(); security::binder_transaction(&self.process.cred, &node_ref.node.owner.cred)?; - let transaction = Transaction::new(node_ref, None, self, tr)?; - let code = if self.process.is_oneway_spam_detection_enabled() - && transaction.oneway_spam_detected - { + let transaction = Transaction::new(node_ref, None, self, info)?; + let code = if self.process.is_oneway_spam_detection_enabled() && info.oneway_spam_suspect { BR_ONEWAY_SPAM_SUSPECT } else { BR_TRANSACTION_COMPLETE @@ -1306,7 +1351,7 @@ fn oneway_transaction_inner(self: &Arc, tr: &BinderTransactionDataSg) -> B let list_completion = DTRWrap::arc_try_new(DeliverCode::new(code))?; let completion = list_completion.clone_arc(); self.inner.lock().push_work(list_completion); - match transaction.submit() { + match transaction.submit(info) { Ok(()) => Ok(()), Err(err) => { completion.skip(); @@ -1327,29 +1372,8 @@ fn write(self: &Arc, req: &mut BinderWriteRead) -> Result { GLOBAL_STATS.inc_bc(cmd); self.process.stats.inc_bc(cmd); match cmd { - BC_TRANSACTION => { - let tr = reader.read::()?.with_buffers_size(0); - if tr.transaction_data.flags & TF_ONE_WAY != 0 { - self.transaction(&tr, Self::oneway_transaction_inner); - } else { - self.transaction(&tr, Self::transaction_inner); - } - } - BC_TRANSACTION_SG => { - let tr = reader.read::()?; - if tr.transaction_data.flags & TF_ONE_WAY != 0 { - self.transaction(&tr, Self::oneway_transaction_inner); - } else { - self.transaction(&tr, Self::transaction_inner); - } - } - BC_REPLY => { - let tr = reader.read::()?.with_buffers_size(0); - self.transaction(&tr, Self::reply_inner) - } - BC_REPLY_SG => { - let tr = reader.read::()?; - self.transaction(&tr, Self::reply_inner) + BC_TRANSACTION | BC_TRANSACTION_SG | BC_REPLY | BC_REPLY_SG => { + self.transaction(cmd, &mut reader)?; } BC_FREE_BUFFER => { let buffer = self.process.buffer_get(reader.read()?); diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 10af40527ca7..5dff3d655c4d 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -8,7 +8,7 @@ seq_print, sync::atomic::{ordering::Relaxed, Atomic}, sync::{Arc, SpinLock}, - task::Kuid, + task::{Kuid, Pid}, time::{Instant, Monotonic}, types::ScopeGuard, }; @@ -24,6 +24,33 @@ BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead, }; +#[derive(Zeroable)] +pub(crate) struct TransactionInfo { + pub(crate) from_pid: Pid, + pub(crate) from_tid: Pid, + pub(crate) to_pid: Pid, + pub(crate) to_tid: Pid, + pub(crate) code: u32, + pub(crate) flags: u32, + pub(crate) data_ptr: UserPtr, + pub(crate) data_size: usize, + pub(crate) offsets_ptr: UserPtr, + pub(crate) offsets_size: usize, + pub(crate) buffers_size: usize, + pub(crate) target_handle: u32, + pub(crate) errno: i32, + pub(crate) reply: u32, + pub(crate) oneway_spam_suspect: bool, + pub(crate) is_reply: bool, +} + +impl TransactionInfo { + #[inline] + pub(crate) fn is_oneway(&self) -> bool { + self.flags & TF_ONE_WAY != 0 + } +} + use core::mem::offset_of; use kernel::bindings::rb_transaction_layout; pub(crate) const TRANSACTION_LAYOUT: rb_transaction_layout = rb_transaction_layout { @@ -52,7 +79,6 @@ pub(crate) struct Transaction { data_address: usize, sender_euid: Kuid, txn_security_ctx_off: Option, - pub(crate) oneway_spam_detected: bool, start_time: Instant, } @@ -65,17 +91,16 @@ pub(crate) fn new( node_ref: NodeRef, from_parent: Option>, from: &Arc, - tr: &BinderTransactionDataSg, + info: &mut TransactionInfo, ) -> BinderResult> { let debug_id = super::next_debug_id(); - let trd = &tr.transaction_data; let allow_fds = node_ref.node.flags & FLAT_BINDER_FLAG_ACCEPTS_FDS != 0; let txn_security_ctx = node_ref.node.flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX != 0; let mut txn_security_ctx_off = if txn_security_ctx { Some(0) } else { None }; let to = node_ref.node.owner.clone(); let mut alloc = match from.copy_transaction_data( to.clone(), - tr, + info, debug_id, allow_fds, txn_security_ctx_off.as_mut(), @@ -88,15 +113,14 @@ pub(crate) fn new( return Err(err); } }; - let oneway_spam_detected = alloc.oneway_spam_detected; - if trd.flags & TF_ONE_WAY != 0 { + if info.is_oneway() { if from_parent.is_some() { pr_warn!("Oneway transaction should not be in a transaction stack."); return Err(EINVAL.into()); } alloc.set_info_oneway_node(node_ref.node.clone()); } - if trd.flags & TF_CLEAR_BUF != 0 { + if info.flags & TF_CLEAR_BUF != 0 { alloc.set_info_clear_on_drop(); } let target_node = node_ref.node.clone(); @@ -110,15 +134,14 @@ pub(crate) fn new( sender_euid: Kuid::current_euid(), from: from.clone(), to, - code: trd.code, - flags: trd.flags, - data_size: trd.data_size as _, - offsets_size: trd.offsets_size as _, + code: info.code, + flags: info.flags, + data_size: info.data_size, + offsets_size: info.offsets_size, data_address, allocation <- kernel::new_spinlock!(Some(alloc.success()), "Transaction::new"), is_outstanding: Atomic::new(false), txn_security_ctx_off, - oneway_spam_detected, start_time: Instant::now(), }))?) } @@ -126,21 +149,19 @@ pub(crate) fn new( pub(crate) fn new_reply( from: &Arc, to: Arc, - tr: &BinderTransactionDataSg, + info: &mut TransactionInfo, allow_fds: bool, ) -> BinderResult> { let debug_id = super::next_debug_id(); - let trd = &tr.transaction_data; - let mut alloc = match from.copy_transaction_data(to.clone(), tr, debug_id, allow_fds, None) - { - Ok(alloc) => alloc, - Err(err) => { - pr_warn!("Failure in copy_transaction_data: {:?}", err); - return Err(err); - } - }; - let oneway_spam_detected = alloc.oneway_spam_detected; - if trd.flags & TF_CLEAR_BUF != 0 { + let mut alloc = + match from.copy_transaction_data(to.clone(), info, debug_id, allow_fds, None) { + Ok(alloc) => alloc, + Err(err) => { + pr_warn!("Failure in copy_transaction_data: {:?}", err); + return Err(err); + } + }; + if info.flags & TF_CLEAR_BUF != 0 { alloc.set_info_clear_on_drop(); } Ok(DTRWrap::arc_pin_init(pin_init!(Transaction { @@ -150,15 +171,14 @@ pub(crate) fn new_reply( sender_euid: Kuid::current_euid(), from: from.clone(), to, - code: trd.code, - flags: trd.flags, - data_size: trd.data_size as _, - offsets_size: trd.offsets_size as _, + code: info.code, + flags: info.flags, + data_size: info.data_size, + offsets_size: info.offsets_size, data_address: alloc.ptr, allocation <- kernel::new_spinlock!(Some(alloc.success()), "Transaction::new"), is_outstanding: Atomic::new(false), txn_security_ctx_off: None, - oneway_spam_detected, start_time: Instant::now(), }))?) } @@ -248,7 +268,7 @@ fn drop_outstanding_txn(&self) { /// stack, otherwise uses the destination process. /// /// Not used for replies. - pub(crate) fn submit(self: DLArc) -> BinderResult { + pub(crate) fn submit(self: DLArc, info: &mut TransactionInfo) -> BinderResult { // Defined before `process_inner` so that the destructor runs after releasing the lock. let mut _t_outdated; @@ -298,6 +318,7 @@ pub(crate) fn submit(self: DLArc) -> BinderResult { } let res = if let Some(thread) = self.find_target_thread() { + info.to_tid = thread.id; crate::trace::trace_transaction(false, &self, Some(&thread.task)); match thread.push_work(self) { PushWorkRes::Ok => Ok(()), diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs index f989539a31b4..984c3ec03a7b 100644 --- a/rust/kernel/uaccess.rs +++ b/rust/kernel/uaccess.rs @@ -19,7 +19,7 @@ /// /// This is the Rust equivalent to C pointers tagged with `__user`. #[repr(transparent)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Zeroable)] pub struct UserPtr(*mut c_void); impl UserPtr { From 18e9fafb2672cb976edfe51e899484c2ea892170 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 24 Mar 2026 20:02:35 +0000 Subject: [PATCH 290/405] rust: sync: implement == operator for ARef Rust Binder wants to perform a comparison between ARef and &Task, so define the == operator for ARef<_> when compared with another ARef<_> or just a reference. The operator is implemented in terms of the same operator applied to the inner type. Note that PartialEq cannot be implemented because it would overlap with the impl for ARef. Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260324-close-fd-check-current-v3-1-b94274bedac7@google.com Signed-off-by: Greg Kroah-Hartman --- rust/kernel/sync/aref.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/rust/kernel/sync/aref.rs b/rust/kernel/sync/aref.rs index 0616c0353c2b..9989f56d0605 100644 --- a/rust/kernel/sync/aref.rs +++ b/rust/kernel/sync/aref.rs @@ -170,3 +170,25 @@ fn drop(&mut self) { unsafe { T::dec_ref(self.ptr) }; } } + +impl PartialEq> for ARef +where + T: AlwaysRefCounted + PartialEq, + U: AlwaysRefCounted, +{ + #[inline] + fn eq(&self, other: &ARef) -> bool { + T::eq(&**self, &**other) + } +} +impl Eq for ARef {} + +impl PartialEq<&'_ U> for ARef +where + T: AlwaysRefCounted + PartialEq, +{ + #[inline] + fn eq(&self, other: &&U) -> bool { + T::eq(&**self, other) + } +} From 12c688086f091c24e62504f5a26c009a6a8dd8bc Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 24 Mar 2026 20:02:36 +0000 Subject: [PATCH 291/405] rust: task: implement == operator for Task It's useful to compare if two tasks are the same task or not. Rust Binder wants this to check if a certain task is equal to the group leader of current. Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260324-close-fd-check-current-v3-2-b94274bedac7@google.com Signed-off-by: Greg Kroah-Hartman --- rust/kernel/task.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs index cc907fb531bc..deb948d99921 100644 --- a/rust/kernel/task.rs +++ b/rust/kernel/task.rs @@ -362,6 +362,15 @@ unsafe fn dec_ref(obj: ptr::NonNull) { } } +impl PartialEq for Task { + #[inline] + fn eq(&self, other: &Self) -> bool { + ptr::eq(self.as_ptr(), other.as_ptr()) + } +} + +impl Eq for Task {} + impl Kuid { /// Get the current euid. #[inline] From ed72cfffc491c88996addd387586234dd8141ee4 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 24 Mar 2026 20:02:37 +0000 Subject: [PATCH 292/405] rust_binder: make use of == for Task Now that we have implemented the == operator for Task, replace the two raw pointer comparisons in Binder with the == operator. Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260324-close-fd-check-current-v3-3-b94274bedac7@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index ae26fe817794..312e5c3f14cd 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -682,7 +682,7 @@ pub(crate) fn get_work_or_register<'a>( fn get_current_thread(self: ArcBorrow<'_, Self>) -> Result> { let id = { let current = kernel::current!(); - if !core::ptr::eq(current.group_leader(), &*self.task) { + if self.task != current.group_leader() { pr_err!("get_current_thread was called from the wrong process."); return Err(EINVAL); } @@ -1672,7 +1672,7 @@ pub(crate) fn mmap( vma: &mm::virt::VmaNew, ) -> Result { // We don't allow mmap to be used in a different process. - if !core::ptr::eq(kernel::current!().group_leader(), &*this.task) { + if this.task != kernel::current!().group_leader() { return Err(EINVAL); } if vma.start() == 0 { From fc74559e2dd4405492102ada28afa60d012a662f Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 24 Mar 2026 20:02:38 +0000 Subject: [PATCH 293/405] rust_binder: check current before closing fds This list gets populated once the transaction is delivered to the target process, at which point it's not touched again except in BC_FREE_BUFFER and process exit, so if the list has been populated then this code should not run in the context of the wrong userspace process. However, why tempt fate? The function itself can run in the context of both the sender and receiver, and if someone can engineer a scenario where it runs in the sender and this list is non-empty (or future Rust Binder changes make such a scenario possible), then that'd be a problem because we'd be closing random unrelated fds in the wrong process. Note that on process exit, the == comparison may actually fail because it's called from a kthread. The fd closing code is a no-op on kthreads, so there is no actual behavior different though. Suggested-by: Jann Horn Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260324-close-fd-check-current-v3-4-b94274bedac7@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/allocation.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index 97edfb1ff382..0dc4f364d86d 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -257,19 +257,22 @@ fn drop(&mut self) { } } - for &fd in &info.file_list.close_on_free { - let closer = match DeferredFdCloser::new(GFP_KERNEL) { - Ok(closer) => closer, - Err(kernel::alloc::AllocError) => { - // Ignore allocation failures. - break; - } - }; + if self.process.task == kernel::current!().group_leader() { + for &fd in &info.file_list.close_on_free { + let closer = match DeferredFdCloser::new(GFP_KERNEL) { + Ok(closer) => closer, + Err(kernel::alloc::AllocError) => { + // Ignore allocation failures. + break; + } + }; - // Here, we ignore errors. The operation can fail if the fd is not valid, or if the - // method is called from a kthread. However, this is always called from a syscall, - // so the latter case cannot happen, and we don't care about the first case. - let _ = closer.close_fd(fd); + // Here, we ignore errors. The operation can fail if the fd is not valid, or if + // the method is called from a kthread. However, this is always called from a + // syscall, so the latter case cannot happen, and we don't care about the first + // case. + let _ = closer.close_fd(fd); + } } if info.clear_on_free { From f698a253e3936ed516aa234495861eb707d608b9 Mon Sep 17 00:00:00 2001 From: Pedro Montes Alcalde Date: Fri, 27 Mar 2026 22:02:51 -0300 Subject: [PATCH 294/405] rust_binder: drop startup init log message The "Loaded Rust Binder." message is logged during normal initialization and does not indicate an error/warning condition. Logging it creates unnecessary noise and is inconsistent with other drivers, so this change fixes that Signed-off-by: Pedro Montes Alcalde Acked-by: Carlos Llamas Acked-by: Alice Ryhl Link: https://patch.msgid.link/20260328010250.249131-2-pedro.montes.alcalde@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/rust_binder_main.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index aa5f2a75adb4..dccb7ba3ffc0 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -292,8 +292,6 @@ fn init(_module: &'static kernel::ThisModule) -> Result { // SAFETY: The module initializer never runs twice, so we only call this once. unsafe { crate::context::CONTEXTS.init() }; - pr_warn!("Loaded Rust Binder."); - BINDER_SHRINKER.register(c"android-binder")?; // SAFETY: The module is being loaded, so we can initialize binderfs. From e3007a92332d15b085c5d1157ffeea26f03f0aa6 Mon Sep 17 00:00:00 2001 From: Mohamad Alsadhan Date: Tue, 17 Mar 2026 17:49:42 +0300 Subject: [PATCH 295/405] rust_binder: remove "rust_" prefix from tracepoints Remove the "rust_" prefix as the name is part of the uapi, and userspace expects tracepoints to have the old names. Link: https://github.com/Rust-for-Linux/linux/issues/1226 Suggested-by: Alice Ryhl Reviewed-by: Alice Ryhl Signed-off-by: Mohamad Alsadhan Link: https://patch.msgid.link/20260317-rust-binder-trace-v3-1-6fae4fbcf637@sdhn.cc Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/rust_binder_events.h | 4 ++-- drivers/android/binder/trace.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/android/binder/rust_binder_events.h b/drivers/android/binder/rust_binder_events.h index 8ad785c6bd0f..e3adfb93170d 100644 --- a/drivers/android/binder/rust_binder_events.h +++ b/drivers/android/binder/rust_binder_events.h @@ -15,7 +15,7 @@ #include -TRACE_EVENT(rust_binder_ioctl, +TRACE_EVENT(binder_ioctl, TP_PROTO(unsigned int cmd, unsigned long arg), TP_ARGS(cmd, arg), @@ -30,7 +30,7 @@ TRACE_EVENT(rust_binder_ioctl, TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg) ); -TRACE_EVENT(rust_binder_transaction, +TRACE_EVENT(binder_transaction, TP_PROTO(bool reply, rust_binder_transaction t, struct task_struct *thread), TP_ARGS(reply, t, thread), TP_STRUCT__entry( diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs index 9839901c7151..d54b18ab71a8 100644 --- a/drivers/android/binder/trace.rs +++ b/drivers/android/binder/trace.rs @@ -10,8 +10,8 @@ use kernel::tracepoint::declare_trace; declare_trace! { - unsafe fn rust_binder_ioctl(cmd: c_uint, arg: c_ulong); - unsafe fn rust_binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct); + unsafe fn binder_ioctl(cmd: c_uint, arg: c_ulong); + unsafe fn binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct); } #[inline] @@ -22,7 +22,7 @@ fn raw_transaction(t: &Transaction) -> rust_binder_transaction { #[inline] pub(crate) fn trace_ioctl(cmd: u32, arg: usize) { // SAFETY: Always safe to call. - unsafe { rust_binder_ioctl(cmd, arg as c_ulong) } + unsafe { binder_ioctl(cmd, arg as c_ulong) } } #[inline] @@ -33,5 +33,5 @@ pub(crate) fn trace_transaction(reply: bool, t: &Transaction, thread: Option<&Ta }; // SAFETY: The raw transaction is valid for the duration of this call. The thread pointer is // valid or null. - unsafe { rust_binder_transaction(reply, raw_transaction(t), thread) } + unsafe { binder_transaction(reply, raw_transaction(t), thread) } } From be3953bb2655fe4571a1b2cd1705bcc5a241a58e Mon Sep 17 00:00:00 2001 From: Mohamad Alsadhan Date: Tue, 17 Mar 2026 17:49:43 +0300 Subject: [PATCH 296/405] rust_binder: add ioctl/read/write done tracepoints Add Rust Binder tracepoints declarations for `ioctl_done`, `read_done` and `write_done`. Additionally, wire in the new tracepoints into the corresponding Binder call sites. Note that the new tracepoints report final errno-style return values, matching the existing C model for operation completion. Signed-off-by: Mohamad Alsadhan Link: https://patch.msgid.link/20260317-rust-binder-trace-v3-2-6fae4fbcf637@sdhn.cc Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 7 +++-- drivers/android/binder/rust_binder_events.h | 21 +++++++++++++++ drivers/android/binder/thread.rs | 2 ++ drivers/android/binder/trace.rs | 30 ++++++++++++++++++++- 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 312e5c3f14cd..820cbd541435 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -1659,11 +1659,14 @@ pub(crate) fn ioctl(this: ArcBorrow<'_, Process>, file: &File, cmd: u32, arg: us const _IOC_READ_WRITE: u32 = _IOC_READ | _IOC_WRITE; - match _IOC_DIR(cmd) { + let res = match _IOC_DIR(cmd) { _IOC_WRITE => Self::ioctl_write_only(this, file, cmd, &mut user_slice.reader()), _IOC_READ_WRITE => Self::ioctl_write_read(this, file, cmd, user_slice), _ => Err(EINVAL), - } + }; + + crate::trace::trace_ioctl_done(res); + res } pub(crate) fn mmap( diff --git a/drivers/android/binder/rust_binder_events.h b/drivers/android/binder/rust_binder_events.h index e3adfb93170d..4fda8576c01f 100644 --- a/drivers/android/binder/rust_binder_events.h +++ b/drivers/android/binder/rust_binder_events.h @@ -30,6 +30,27 @@ TRACE_EVENT(binder_ioctl, TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg) ); +DECLARE_EVENT_CLASS(binder_function_return_class, + TP_PROTO(int ret), + TP_ARGS(ret), + TP_STRUCT__entry( + __field(int, ret) + ), + TP_fast_assign( + __entry->ret = ret; + ), + TP_printk("ret=%d", __entry->ret) +); + +#define DEFINE_RBINDER_FUNCTION_RETURN_EVENT(name) \ +DEFINE_EVENT(binder_function_return_class, name, \ + TP_PROTO(int ret), \ + TP_ARGS(ret)) + +DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_ioctl_done); +DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_read_done); +DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_write_done); + TRACE_EVENT(binder_transaction, TP_PROTO(bool reply, rust_binder_transaction t, struct task_struct *thread), TP_ARGS(reply, t, thread), diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 97a5e4acf64c..138c45cecfa0 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -1507,6 +1507,7 @@ pub(crate) fn write_read(self: &Arc, data: UserSlice, wait: bool) -> Resul let mut ret = Ok(()); if req.write_size > 0 { ret = self.write(&mut req); + crate::trace::trace_write_done(ret); if let Err(err) = ret { pr_warn!( "Write failure {:?} in pid:{}", @@ -1523,6 +1524,7 @@ pub(crate) fn write_read(self: &Arc, data: UserSlice, wait: bool) -> Resul // Go through the work queue. if req.read_size > 0 { ret = self.read(&mut req, wait); + crate::trace::trace_read_done(ret); if ret.is_err() && ret != Err(EINTR) { pr_warn!( "Read failure {:?} in pid:{}", diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs index d54b18ab71a8..3b0458e2738c 100644 --- a/drivers/android/binder/trace.rs +++ b/drivers/android/binder/trace.rs @@ -5,12 +5,16 @@ use crate::transaction::Transaction; use kernel::bindings::{rust_binder_transaction, task_struct}; -use kernel::ffi::{c_uint, c_ulong}; +use kernel::error::Result; +use kernel::ffi::{c_int, c_uint, c_ulong}; use kernel::task::Task; use kernel::tracepoint::declare_trace; declare_trace! { unsafe fn binder_ioctl(cmd: c_uint, arg: c_ulong); + unsafe fn binder_ioctl_done(ret: c_int); + unsafe fn binder_read_done(ret: c_int); + unsafe fn binder_write_done(ret: c_int); unsafe fn binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct); } @@ -19,12 +23,36 @@ fn raw_transaction(t: &Transaction) -> rust_binder_transaction { t as *const Transaction as rust_binder_transaction } +#[inline] +fn to_errno(ret: Result) -> i32 { + match ret { + Ok(()) => 0, + Err(err) => err.to_errno(), + } +} + #[inline] pub(crate) fn trace_ioctl(cmd: u32, arg: usize) { // SAFETY: Always safe to call. unsafe { binder_ioctl(cmd, arg as c_ulong) } } +#[inline] +pub(crate) fn trace_ioctl_done(ret: Result) { + // SAFETY: Always safe to call. + unsafe { binder_ioctl_done(to_errno(ret)) } +} +#[inline] +pub(crate) fn trace_read_done(ret: Result) { + // SAFETY: Always safe to call. + unsafe { binder_read_done(to_errno(ret)) } +} +#[inline] +pub(crate) fn trace_write_done(ret: Result) { + // SAFETY: Always safe to call. + unsafe { binder_write_done(to_errno(ret)) } +} + #[inline] pub(crate) fn trace_transaction(reply: bool, t: &Transaction, thread: Option<&Task>) { let thread = match thread { From 2335167a614eceb506453dc27cc99a186b6eca76 Mon Sep 17 00:00:00 2001 From: Mohamad Alsadhan Date: Tue, 17 Mar 2026 17:49:44 +0300 Subject: [PATCH 297/405] rust_binder: add `wait_for_work` tracepoint Add the Rust Binder `wait_for_work` tracepoint declaration and wire it into the thread read path before selecting the work source. Signed-off-by: Mohamad Alsadhan Link: https://patch.msgid.link/20260317-rust-binder-trace-v3-3-6fae4fbcf637@sdhn.cc Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/rust_binder_events.h | 18 ++++++++++++++++++ drivers/android/binder/thread.rs | 11 +++++++++-- drivers/android/binder/trace.rs | 7 +++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/drivers/android/binder/rust_binder_events.h b/drivers/android/binder/rust_binder_events.h index 4fda8576c01f..62b587c7c494 100644 --- a/drivers/android/binder/rust_binder_events.h +++ b/drivers/android/binder/rust_binder_events.h @@ -51,6 +51,24 @@ DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_ioctl_done); DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_read_done); DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_write_done); +TRACE_EVENT(binder_wait_for_work, + TP_PROTO(bool proc_work, bool transaction_stack, bool thread_todo), + TP_ARGS(proc_work, transaction_stack, thread_todo), + TP_STRUCT__entry( + __field(bool, proc_work) + __field(bool, transaction_stack) + __field(bool, thread_todo) + ), + TP_fast_assign( + __entry->proc_work = proc_work; + __entry->transaction_stack = transaction_stack; + __entry->thread_todo = thread_todo; + ), + TP_printk("proc_work=%d transaction_stack=%d thread_todo=%d", + __entry->proc_work, __entry->transaction_stack, + __entry->thread_todo) +); + TRACE_EVENT(binder_transaction, TP_PROTO(bool reply, rust_binder_transaction t, struct task_struct *thread), TP_ARGS(reply, t, thread), diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 138c45cecfa0..62e293e1a4b8 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -1437,11 +1437,18 @@ fn read(self: &Arc, req: &mut BinderWriteRead, wait: bool) -> Result { UserSlice::new(UserPtr::from_addr(read_start as _), read_len as _).writer(), self, ); - let (in_pool, use_proc_queue) = { + let (in_pool, has_transaction, thread_todo, use_proc_queue) = { let inner = self.inner.lock(); - (inner.is_looper(), inner.should_use_process_work_queue()) + ( + inner.is_looper(), + inner.current_transaction.is_some(), + !inner.work_list.is_empty(), + inner.should_use_process_work_queue(), + ) }; + crate::trace::trace_wait_for_work(use_proc_queue, has_transaction, thread_todo); + let getter = if use_proc_queue { Self::get_work } else { diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs index 3b0458e2738c..1f62b2276740 100644 --- a/drivers/android/binder/trace.rs +++ b/drivers/android/binder/trace.rs @@ -15,6 +15,7 @@ unsafe fn binder_ioctl_done(ret: c_int); unsafe fn binder_read_done(ret: c_int); unsafe fn binder_write_done(ret: c_int); + unsafe fn binder_wait_for_work(proc_work: bool, transaction_stack: bool, thread_todo: bool); unsafe fn binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct); } @@ -53,6 +54,12 @@ pub(crate) fn trace_write_done(ret: Result) { unsafe { binder_write_done(to_errno(ret)) } } +#[inline] +pub(crate) fn trace_wait_for_work(proc_work: bool, transaction_stack: bool, thread_todo: bool) { + // SAFETY: Always safe to call. + unsafe { binder_wait_for_work(proc_work, transaction_stack, thread_todo) } +} + #[inline] pub(crate) fn trace_transaction(reply: bool, t: &Transaction, thread: Option<&Task>) { let thread = match thread { From caf3719f335dac62e5626baadb66836907176337 Mon Sep 17 00:00:00 2001 From: Mohamad Alsadhan Date: Tue, 17 Mar 2026 17:49:45 +0300 Subject: [PATCH 298/405] rust_binder: add `transaction_received` tracepoint Add Rust Binder `transaction_received` tracepoint decalaration and wire in the corresponding trace call when a transaction work item is accepted for execution. Signed-off-by: Mohamad Alsadhan Link: https://patch.msgid.link/20260317-rust-binder-trace-v3-4-6fae4fbcf637@sdhn.cc Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/rust_binder_events.h | 12 ++++++++++++ drivers/android/binder/trace.rs | 7 +++++++ drivers/android/binder/transaction.rs | 2 ++ 3 files changed, 21 insertions(+) diff --git a/drivers/android/binder/rust_binder_events.h b/drivers/android/binder/rust_binder_events.h index 62b587c7c494..8a0b72bf0255 100644 --- a/drivers/android/binder/rust_binder_events.h +++ b/drivers/android/binder/rust_binder_events.h @@ -99,6 +99,18 @@ TRACE_EVENT(binder_transaction, __entry->reply, __entry->flags, __entry->code) ); +TRACE_EVENT(binder_transaction_received, + TP_PROTO(rust_binder_transaction t), + TP_ARGS(t), + TP_STRUCT__entry( + __field(int, debug_id) + ), + TP_fast_assign( + __entry->debug_id = rust_binder_transaction_debug_id(t); + ), + TP_printk("transaction=%d", __entry->debug_id) +); + #endif /* _RUST_BINDER_TRACE_H */ /* This part must be outside protection */ diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs index 1f62b2276740..d96afdb79c65 100644 --- a/drivers/android/binder/trace.rs +++ b/drivers/android/binder/trace.rs @@ -17,6 +17,7 @@ unsafe fn binder_write_done(ret: c_int); unsafe fn binder_wait_for_work(proc_work: bool, transaction_stack: bool, thread_todo: bool); unsafe fn binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct); + unsafe fn binder_transaction_received(t: rust_binder_transaction); } #[inline] @@ -70,3 +71,9 @@ pub(crate) fn trace_transaction(reply: bool, t: &Transaction, thread: Option<&Ta // valid or null. unsafe { binder_transaction(reply, raw_transaction(t), thread) } } + +#[inline] +pub(crate) fn trace_transaction_received(t: &Transaction) { + // SAFETY: The raw transaction is valid for the duration of this call. + unsafe { binder_transaction_received(raw_transaction(t)) } +} diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 5dff3d655c4d..47d5e4d88b07 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -451,6 +451,8 @@ fn do_work( self.drop_outstanding_txn(); + crate::trace::trace_transaction_received(&self); + // When this is not a reply and not a oneway transaction, update `current_transaction`. If // it's a reply, `current_transaction` has already been updated appropriately. if self.target_node.is_some() && tr_sec.transaction_data.flags & TF_ONE_WAY == 0 { From 25917c05ab477bf60f8cf09acb1e9e89de3df61f Mon Sep 17 00:00:00 2001 From: Mohamad Alsadhan Date: Tue, 17 Mar 2026 17:49:46 +0300 Subject: [PATCH 299/405] rust_binder: add fd translation tracepoints Add Rust Binder tracepoint declarations for both `transaction_fd_send` and `transaction_fd_recv`. Also, wire in the corresponding trace calls where fd objects are serialised/deserialised. Signed-off-by: Mohamad Alsadhan Link: https://patch.msgid.link/20260317-rust-binder-trace-v3-5-6fae4fbcf637@sdhn.cc Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/allocation.rs | 1 + drivers/android/binder/rust_binder.h | 5 +++ drivers/android/binder/rust_binder_events.h | 34 +++++++++++++++++++++ drivers/android/binder/thread.rs | 1 + drivers/android/binder/trace.rs | 13 ++++++++ 5 files changed, 54 insertions(+) diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index 0dc4f364d86d..0cab959e4b7e 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -205,6 +205,7 @@ pub(crate) fn translate_fds(&mut self) -> Result { let res = FileDescriptorReservation::get_unused_fd_flags(bindings::O_CLOEXEC)?; let fd = res.reserved_fd(); self.write::(file_info.buffer_offset, &fd)?; + crate::trace::trace_transaction_fd_recv(self.debug_id, fd, file_info.buffer_offset); reservations.push( Reservation { diff --git a/drivers/android/binder/rust_binder.h b/drivers/android/binder/rust_binder.h index d2284726c025..3936b9d0b8cd 100644 --- a/drivers/android/binder/rust_binder.h +++ b/drivers/android/binder/rust_binder.h @@ -99,4 +99,9 @@ static inline size_t rust_binder_node_debug_id(rust_binder_node t) return *(size_t *) (t + RUST_BINDER_LAYOUT.n.debug_id); } +static inline binder_uintptr_t rust_binder_node_ptr(rust_binder_node t) +{ + return *(binder_uintptr_t *) (t + RUST_BINDER_LAYOUT.n.ptr); +} + #endif diff --git a/drivers/android/binder/rust_binder_events.h b/drivers/android/binder/rust_binder_events.h index 8a0b72bf0255..572a4bf7d1d0 100644 --- a/drivers/android/binder/rust_binder_events.h +++ b/drivers/android/binder/rust_binder_events.h @@ -111,6 +111,40 @@ TRACE_EVENT(binder_transaction_received, TP_printk("transaction=%d", __entry->debug_id) ); +TRACE_EVENT(binder_transaction_fd_send, + TP_PROTO(int t_debug_id, int fd, size_t offset), + TP_ARGS(t_debug_id, fd, offset), + TP_STRUCT__entry( + __field(int, debug_id) + __field(int, fd) + __field(size_t, offset) + ), + TP_fast_assign( + __entry->debug_id = t_debug_id; + __entry->fd = fd; + __entry->offset = offset; + ), + TP_printk("transaction=%d src_fd=%d offset=%zu", + __entry->debug_id, __entry->fd, __entry->offset) +); + +TRACE_EVENT(binder_transaction_fd_recv, + TP_PROTO(int t_debug_id, int fd, size_t offset), + TP_ARGS(t_debug_id, fd, offset), + TP_STRUCT__entry( + __field(int, debug_id) + __field(int, fd) + __field(size_t, offset) + ), + TP_fast_assign( + __entry->debug_id = t_debug_id; + __entry->fd = fd; + __entry->offset = offset; + ), + TP_printk("transaction=%d dest_fd=%d offset=%zu", + __entry->debug_id, __entry->fd, __entry->offset) +); + #endif /* _RUST_BINDER_TRACE_H */ /* This part must be outside protection */ diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 62e293e1a4b8..1feac87026e6 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -712,6 +712,7 @@ fn translate_object( core::mem::offset_of!(uapi::binder_fd_object, __bindgen_anon_1.fd); let field_offset = offset + FD_FIELD_OFFSET; + crate::trace::trace_transaction_fd_send(view.alloc.debug_id, fd, field_offset); view.alloc.info_add_fd(file, field_offset, false)?; } diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs index d96afdb79c65..c6f39d83314e 100644 --- a/drivers/android/binder/trace.rs +++ b/drivers/android/binder/trace.rs @@ -18,6 +18,8 @@ unsafe fn binder_wait_for_work(proc_work: bool, transaction_stack: bool, thread_todo: bool); unsafe fn binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct); unsafe fn binder_transaction_received(t: rust_binder_transaction); + unsafe fn binder_transaction_fd_send(t_debug_id: c_int, fd: c_int, offset: usize); + unsafe fn binder_transaction_fd_recv(t_debug_id: c_int, fd: c_int, offset: usize); } #[inline] @@ -77,3 +79,14 @@ pub(crate) fn trace_transaction_received(t: &Transaction) { // SAFETY: The raw transaction is valid for the duration of this call. unsafe { binder_transaction_received(raw_transaction(t)) } } + +#[inline] +pub(crate) fn trace_transaction_fd_send(t_debug_id: usize, fd: u32, offset: usize) { + // SAFETY: This function is always safe to call. + unsafe { binder_transaction_fd_send(t_debug_id as c_int, fd as c_int, offset) } +} +#[inline] +pub(crate) fn trace_transaction_fd_recv(t_debug_id: usize, fd: u32, offset: usize) { + // SAFETY: This function is always safe to call. + unsafe { binder_transaction_fd_recv(t_debug_id as c_int, fd as c_int, offset) } +} From 8f3481028b05e3418b6fe7119428ab44a5b2eb20 Mon Sep 17 00:00:00 2001 From: Mohamad Alsadhan Date: Tue, 17 Mar 2026 17:49:47 +0300 Subject: [PATCH 300/405] rust_binder: add `command`/`return` tracepoints Add Rust Binder `command` and `return` tracepoint declarations and wire them in where BC commands are parsed and BR return codes are emitted to userspace. Signed-off-by: Mohamad Alsadhan Link: https://patch.msgid.link/20260317-rust-binder-trace-v3-6-6fae4fbcf637@sdhn.cc Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/rust_binder_events.h | 32 +++++++++++++++++++++ drivers/android/binder/rust_binder_main.rs | 1 + drivers/android/binder/thread.rs | 1 + drivers/android/binder/trace.rs | 13 +++++++++ 4 files changed, 47 insertions(+) diff --git a/drivers/android/binder/rust_binder_events.h b/drivers/android/binder/rust_binder_events.h index 572a4bf7d1d0..1a446787c8b3 100644 --- a/drivers/android/binder/rust_binder_events.h +++ b/drivers/android/binder/rust_binder_events.h @@ -145,6 +145,38 @@ TRACE_EVENT(binder_transaction_fd_recv, __entry->debug_id, __entry->fd, __entry->offset) ); +TRACE_EVENT(binder_command, + TP_PROTO(uint32_t cmd), + TP_ARGS(cmd), + TP_STRUCT__entry( + __field(uint32_t, cmd) + ), + TP_fast_assign( + __entry->cmd = cmd; + ), + TP_printk("cmd=0x%x %s", + __entry->cmd, + _IOC_NR(__entry->cmd) < ARRAY_SIZE(binder_command_strings) ? + binder_command_strings[_IOC_NR(__entry->cmd)] : + "unknown") +); + +TRACE_EVENT(binder_return, + TP_PROTO(uint32_t cmd), + TP_ARGS(cmd), + TP_STRUCT__entry( + __field(uint32_t, cmd) + ), + TP_fast_assign( + __entry->cmd = cmd; + ), + TP_printk("cmd=0x%x %s", + __entry->cmd, + _IOC_NR(__entry->cmd) < ARRAY_SIZE(binder_return_strings) ? + binder_return_strings[_IOC_NR(__entry->cmd)] : + "unknown") +); + #endif /* _RUST_BINDER_TRACE_H */ /* This part must be outside protection */ diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index dccb7ba3ffc0..bd26b61b2192 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -116,6 +116,7 @@ fn new(writer: UserSliceWriter, thread: &'a Thread) -> Self { /// Write a return code back to user space. /// Should be a `BR_` constant from [`defs`] e.g. [`defs::BR_TRANSACTION_COMPLETE`]. fn write_code(&mut self, code: u32) -> Result { + crate::trace::trace_return(code); stats::GLOBAL_STATS.inc_br(code); self.thread.process.stats.inc_br(code); self.writer.write(&code) diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 1feac87026e6..97d5f31e8fe3 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -1370,6 +1370,7 @@ fn write(self: &Arc, req: &mut BinderWriteRead) -> Result { while reader.len() >= size_of::() && self.inner.lock().return_work.is_unused() { let before = reader.len(); let cmd = reader.read::()?; + crate::trace::trace_command(cmd); GLOBAL_STATS.inc_bc(cmd); self.process.stats.inc_bc(cmd); match cmd { diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs index c6f39d83314e..5539672d7285 100644 --- a/drivers/android/binder/trace.rs +++ b/drivers/android/binder/trace.rs @@ -20,6 +20,8 @@ unsafe fn binder_transaction_received(t: rust_binder_transaction); unsafe fn binder_transaction_fd_send(t_debug_id: c_int, fd: c_int, offset: usize); unsafe fn binder_transaction_fd_recv(t_debug_id: c_int, fd: c_int, offset: usize); + unsafe fn binder_command(cmd: u32); + unsafe fn binder_return(ret: u32); } #[inline] @@ -90,3 +92,14 @@ pub(crate) fn trace_transaction_fd_recv(t_debug_id: usize, fd: u32, offset: usiz // SAFETY: This function is always safe to call. unsafe { binder_transaction_fd_recv(t_debug_id as c_int, fd as c_int, offset) } } + +#[inline] +pub(crate) fn trace_command(cmd: u32) { + // SAFETY: This function is always safe to call. + unsafe { binder_command(cmd) } +} +#[inline] +pub(crate) fn trace_return(ret: u32) { + // SAFETY: This function is always safe to call. + unsafe { binder_return(ret) } +} From 028f3d0168f83be903f34a8b52cd6254d9695a57 Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Wed, 18 Mar 2026 10:48:15 +0800 Subject: [PATCH 301/405] interconnect: debugfs: fix devm_kstrdup and kfree mismatch debugfs_write_file_str() uses standard kfree() to release old strings. Initializing src_node and dst_node with devm_kstrdup() creates a memory management mismatch. If a user writes to these debugfs nodes, the devm-allocated memory is freed via kfree(), leaving a dangling pointer in the device resource list that can lead to a double free. Fix this by using standard kstrdup() instead. Since the interconnect subsystem is strictly built-in and cannot be unloaded as a module, there is no exit path requiring manual cleanup of these strings. The error handling path is also simplified by taking advantage of the fact that kfree(NULL) is a safe no-op. Fixes: 8cc27f5c6dd1 ("interconnect: debugfs: initialize src_node and dst_node to empty strings") Signed-off-by: Gui-Dong Han Reviewed-by: Kuan-Wei Chiu Link: https://msgid.link/20260318024815.7655-1-hanguidong02@gmail.com Signed-off-by: Georgi Djakov --- drivers/interconnect/debugfs-client.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/interconnect/debugfs-client.c b/drivers/interconnect/debugfs-client.c index 5107bff53173..08df9188ef94 100644 --- a/drivers/interconnect/debugfs-client.c +++ b/drivers/interconnect/debugfs-client.c @@ -150,10 +150,13 @@ int icc_debugfs_client_init(struct dentry *icc_dir) return ret; } - src_node = devm_kstrdup(&pdev->dev, "", GFP_KERNEL); - dst_node = devm_kstrdup(&pdev->dev, "", GFP_KERNEL); - if (!src_node || !dst_node) + src_node = kstrdup("", GFP_KERNEL); + dst_node = kstrdup("", GFP_KERNEL); + if (!src_node || !dst_node) { + kfree(dst_node); + kfree(src_node); return -ENOMEM; + } client_dir = debugfs_create_dir("test_client", icc_dir); From 0436cd305d0be28cde59efb137d15d1bc6af4b12 Mon Sep 17 00:00:00 2001 From: Kuan-Wei Chiu Date: Fri, 20 Mar 2026 19:06:23 +0000 Subject: [PATCH 302/405] MAINTAINERS: Add interconnect kunit test entry Add an entry for the interconnect Kunit tests. As the original author of this test suite, I would like to be notified of future related patches so I can help review them. Signed-off-by: Kuan-Wei Chiu Link: https://msgid.link/20260320190623.1846992-1-visitorckw@gmail.com Signed-off-by: Georgi Djakov --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 55af015174a5..3f0d087c970d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13327,6 +13327,12 @@ F: include/linux/interconnect-clk.h F: include/linux/interconnect-provider.h F: include/linux/interconnect.h +INTERCONNECT KUNIT TESTS +M: Kuan-Wei Chiu +L: linux-pm@vger.kernel.org +S: Maintained +F: drivers/interconnect/icc-kunit.c + INTERRUPT COUNTER DRIVER M: Oleksij Rempel R: Pengutronix Kernel Team From 241cb8dee0f83856c728f4fe2c29e331386c92f2 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:26 +0000 Subject: [PATCH 303/405] comedi: add comedi_check_request_region() There is an existing comedi_request_region(dev, start, len) function used by COMEDI drivers for legacy devices to request an I/O port region starting at a specified base address (which must be non-zero) and with a specified length. It uses request_region(). On success, it sets dev->iobase and dev->iolen and returns 0. There is a alternative function __comedi_request_region(dev, start, len) which does the same thing without setting dev->iobase and dev->iolen. Most hardware devices have restrictions on the allowed I/O port base address and alignment, so add new functions comedi_check_request_region(dev, start, len, minstart, maxend, minalign) and __comedi_check_request_region(dev, start, len, minstart, maxend, minalign) to perform these additional checks. Turn the original functions into static inline wrapper functions that call the new functions. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-2-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers.c | 46 ++++++++++++++++++++------- include/linux/comedi/comedidev.h | 53 +++++++++++++++++++++++++++++--- 2 files changed, 84 insertions(+), 15 deletions(-) diff --git a/drivers/comedi/drivers.c b/drivers/comedi/drivers.c index db225a3bf012..5b02107bb6bf 100644 --- a/drivers/comedi/drivers.c +++ b/drivers/comedi/drivers.c @@ -933,19 +933,24 @@ int comedi_load_firmware(struct comedi_device *dev, EXPORT_SYMBOL_GPL(comedi_load_firmware); /** - * __comedi_request_region() - Request an I/O region for a legacy driver + * __comedi_check_request_region() - Request an I/O region for a legacy driver * @dev: COMEDI device. * @start: Base address of the I/O region. * @len: Length of the I/O region. + * @minstart: Minimum allowed start address of region. + * @maxend: Maximum allowed region end address of region. + * @minalign: Required alignment for base address. * * Requests the specified I/O port region which must start at a non-zero - * address. + * address, must fall within specified bounds, and must be correctly aligned. * * Returns 0 on success, -EINVAL if @start is 0, or -EIO if the request * fails. */ -int __comedi_request_region(struct comedi_device *dev, - unsigned long start, unsigned long len) +int __comedi_check_request_region(struct comedi_device *dev, + unsigned long start, unsigned long len, + unsigned long minstart, unsigned long maxend, + unsigned long minalign) { if (!start) { dev_warn(dev->class_dev, @@ -954,6 +959,19 @@ int __comedi_request_region(struct comedi_device *dev, return -EINVAL; } + if (start < minstart || start > maxend || maxend - start < len - 1) { + dev_warn(dev->class_dev, + "%s: I/O base address or length out of range\n", + dev->board_name); + return -EINVAL; + } + if (!IS_ALIGNED(start, minalign)) { + dev_warn(dev->class_dev, + "%s: I/O base address not correctly aligned\n", + dev->board_name); + return -EINVAL; + } + if (!request_region(start, len, dev->board_name)) { dev_warn(dev->class_dev, "%s: I/O port conflict (%#lx,%lu)\n", dev->board_name, start, len); @@ -962,16 +980,19 @@ int __comedi_request_region(struct comedi_device *dev, return 0; } -EXPORT_SYMBOL_GPL(__comedi_request_region); +EXPORT_SYMBOL_GPL(__comedi_check_request_region); /** - * comedi_request_region() - Request an I/O region for a legacy driver + * comedi_check_request_region() - Request an I/O region for a legacy driver * @dev: COMEDI device. * @start: Base address of the I/O region. * @len: Length of the I/O region. + * @minstart: Minimum allowed start address of region. + * @maxend: Maximum allowed region end address of region. + * @minalign: Required alignment for base address. * * Requests the specified I/O port region which must start at a non-zero - * address. + * address, must fall within specified bounds, and must be correctly aligned. * * On success, @dev->iobase is set to the base address of the region and * @dev->iolen is set to its length. @@ -979,12 +1000,15 @@ EXPORT_SYMBOL_GPL(__comedi_request_region); * Returns 0 on success, -EINVAL if @start is 0, or -EIO if the request * fails. */ -int comedi_request_region(struct comedi_device *dev, - unsigned long start, unsigned long len) +int comedi_check_request_region(struct comedi_device *dev, + unsigned long start, unsigned long len, + unsigned long minstart, unsigned long maxend, + unsigned long minalign) { int ret; - ret = __comedi_request_region(dev, start, len); + ret = __comedi_check_request_region(dev, start, len, minstart, maxend, + minalign); if (ret == 0) { dev->iobase = start; dev->iolen = len; @@ -992,7 +1016,7 @@ int comedi_request_region(struct comedi_device *dev, return ret; } -EXPORT_SYMBOL_GPL(comedi_request_region); +EXPORT_SYMBOL_GPL(comedi_check_request_region); /** * comedi_legacy_detach() - A generic (*detach) function for legacy drivers diff --git a/include/linux/comedi/comedidev.h b/include/linux/comedi/comedidev.h index 35fdc41845ce..577a08f37aee 100644 --- a/include/linux/comedi/comedidev.h +++ b/include/linux/comedi/comedidev.h @@ -1026,10 +1026,55 @@ int comedi_load_firmware(struct comedi_device *dev, struct device *hw_dev, unsigned long context), unsigned long context); -int __comedi_request_region(struct comedi_device *dev, - unsigned long start, unsigned long len); -int comedi_request_region(struct comedi_device *dev, - unsigned long start, unsigned long len); +int __comedi_check_request_region(struct comedi_device *dev, + unsigned long start, unsigned long len, + unsigned long minstart, unsigned long maxend, + unsigned long minalign); +int comedi_check_request_region(struct comedi_device *dev, + unsigned long start, unsigned long len, + unsigned long minstart, unsigned long maxend, + unsigned long minalign); + +/** + * __comedi_request_region() - Request an I/O region for a legacy driver + * @dev: COMEDI device. + * @start: Base address of the I/O region. + * @len: Length of the I/O region. + * + * Requests the specified I/O port region which must start at a non-zero + * address. + * + * Returns 0 on success, -EINVAL if @start is 0, or -EIO if the request + * fails. + */ +static inline int __comedi_request_region(struct comedi_device *dev, + unsigned long start, + unsigned long len) +{ + return __comedi_check_request_region(dev, start, len, 0, ~0ul, 1); +} + +/** + * comedi_request_region() - Request an I/O region for a legacy driver + * @dev: COMEDI device. + * @start: Base address of the I/O region. + * @len: Length of the I/O region. + * + * Requests the specified I/O port region which must start at a non-zero + * address. + * + * On success, @dev->iobase is set to the base address of the region and + * @dev->iolen is set to its length. + * + * Returns 0 on success, -EINVAL if @start is 0, or -EIO if the request + * fails. + */ +static inline int comedi_request_region(struct comedi_device *dev, + unsigned long start, unsigned long len) +{ + return comedi_check_request_region(dev, start, len, 0, ~0ul, 1); +} + void comedi_legacy_detach(struct comedi_device *dev); int comedi_auto_config(struct device *hardware_device, From cb51837eeeddac2da41f4c202597c534bb9d8dc9 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:27 +0000 Subject: [PATCH 304/405] comedi: 8255: Add some I/O base address sanity checks The "8255" driver allows a COMEDI device to be constructed from one or more 8255 chips, each at an I/O port base address specified by the admin-supplied configuration options (`it->options[]`). Currently, the driver allows any I/O base addresses to be specified as long as the I/O regions can be reserved, and it converts the specified `int` option values holding the base address to `unsigned long`. It doesn't make sense to allow base addresses that are not aligned to 4-byte boundaries because the hardware register addresses would not be decoded properly, so add a check for valid alignment. Convert the option values that specify the base addresses from `int` to `unsigned int` instead of `unsigned long` so they end up the same on 32-bit and 64-bit systems. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-3-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/8255.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/comedi/drivers/8255.c b/drivers/comedi/drivers/8255.c index 5f70938b4477..ff45248ebb29 100644 --- a/drivers/comedi/drivers/8255.c +++ b/drivers/comedi/drivers/8255.c @@ -47,7 +47,7 @@ static int dev_8255_attach(struct comedi_device *dev, struct comedi_devconfig *it) { struct comedi_subdevice *s; - unsigned long iobase; + unsigned int iobase; int ret; int i; @@ -70,13 +70,15 @@ static int dev_8255_attach(struct comedi_device *dev, iobase = it->options[i]; /* - * __comedi_request_region() does not set dev->iobase. + * __comedi_check_request_region() does not set dev->iobase. * * For 8255 devices that are manually attached using * comedi_config, the 'iobase' is the actual I/O port - * base address of the chip. + * base address of the chip. It should be aligned on + * a 4-byte boundary. */ - ret = __comedi_request_region(dev, iobase, I8255_SIZE); + ret = __comedi_check_request_region(dev, iobase, I8255_SIZE, + 0, UINT_MAX, 4); if (ret) return ret; ret = subdev_8255_io_init(dev, s, iobase); From a02b67b23e2f1839498b040675c9c8ad864b6aea Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:28 +0000 Subject: [PATCH 305/405] comedi: adq12b: Add sanity checks for I/O base address The "adq12b" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of the ADQ12-B board. It currently allows any base address to be configured but the hardware only supports the following base addresses (set by an on-board jumper): 0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-4-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/adq12b.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/adq12b.c b/drivers/comedi/drivers/adq12b.c index 19d765182006..f8fa5c6ecdff 100644 --- a/drivers/comedi/drivers/adq12b.c +++ b/drivers/comedi/drivers/adq12b.c @@ -179,7 +179,8 @@ static int adq12b_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0x300, 0x3af, 0x20); if (ret) return ret; From 254ae67d39ab49945aa1ce0a711a499f63965a2c Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:29 +0000 Subject: [PATCH 306/405] comedi: aio_aio12_8: Add sanity checks for I/O base address The "aio_aio12_8" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a supported board (AIO12-8, AI12-8, or AO12-4). It currently allows any base address to be configured but the hardware only supports base addresses (set by on-board jumpers) in the range 0x100 to 0x3C0 on 32-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-5-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/aio_aio12_8.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/aio_aio12_8.c b/drivers/comedi/drivers/aio_aio12_8.c index 227a86a3a760..1d9e14981928 100644 --- a/drivers/comedi/drivers/aio_aio12_8.c +++ b/drivers/comedi/drivers/aio_aio12_8.c @@ -202,7 +202,8 @@ static int aio_aio12_8_attach(struct comedi_device *dev, struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 32); + ret = comedi_check_request_region(dev, it->options[0], 32, + 0x100, 0x3ff, 32); if (ret) return ret; From 81c2e0c8b7933b434163bcddb544094f609d4e1c Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:30 +0000 Subject: [PATCH 307/405] comedi: aio_iiro_16: Add sanity checks for I/O base address The "aio_iiro_16" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a supported board (IIRO-16). It currently allows any base address to be configured but the hardware only supports base addresses (set by on-board jumpers) in the range 0x100 to 0x3F8 on 8-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-6-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/aio_iiro_16.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/aio_iiro_16.c b/drivers/comedi/drivers/aio_iiro_16.c index 739cc4db52ac..00d0bb0f6256 100644 --- a/drivers/comedi/drivers/aio_iiro_16.c +++ b/drivers/comedi/drivers/aio_iiro_16.c @@ -167,7 +167,8 @@ static int aio_iiro_16_attach(struct comedi_device *dev, struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x8); + ret = comedi_check_request_region(dev, it->options[0], 0x8, + 0x100, 0x3ff, 0x8); if (ret) return ret; From 8e6b36f43465558dc8d6956dd8ef54f7c4046d03 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:31 +0000 Subject: [PATCH 308/405] comedi: amplc_dio200: Add sanity checks for I/O base address The "amplc_dio200" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a supported board (PC212E, PC214E, PC215E, PC218E, or PC272E). It currently allows any base address to be configured but the hardware only supports base addresses (set by on-board DIP switches) in the range 0 to 0xFE0 on 32-byte boundaries. (Technically, the DIP switches allow 16-byte boundaries, but I do not think that is advisable given that the boards decode a 32-byte address range.) Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-7-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/amplc_dio200.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/amplc_dio200.c b/drivers/comedi/drivers/amplc_dio200.c index 4544bcdd8a70..8b1cbc3d24e5 100644 --- a/drivers/comedi/drivers/amplc_dio200.c +++ b/drivers/comedi/drivers/amplc_dio200.c @@ -242,7 +242,8 @@ static int dio200_attach(struct comedi_device *dev, struct comedi_devconfig *it) { int ret; - ret = comedi_request_region(dev, it->options[0], 0x20); + ret = comedi_check_request_region(dev, it->options[0], 0x20, + 0, 0xfff, 0x20); if (ret) return ret; From c1eedf0a0fb1d53eba8f92fc8ac3c96de6f9c97e Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:32 +0000 Subject: [PATCH 309/405] comedi: amplc_pc236: Add sanity checks for I/O base address The "amplc_pc236" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a supported PC36AT board. It currently allows any base address to be configured but the hardware only supports base addresses (set by on-board DIP switches) in the range 0 to 0xFFC on 4-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-8-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/amplc_pc236.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/amplc_pc236.c b/drivers/comedi/drivers/amplc_pc236.c index b21e0c906aab..eadee10983bf 100644 --- a/drivers/comedi/drivers/amplc_pc236.c +++ b/drivers/comedi/drivers/amplc_pc236.c @@ -45,7 +45,8 @@ static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x4); + ret = comedi_check_request_region(dev, it->options[0], 0x4, + 0, 0xfff, 4); if (ret) return ret; From 052b102e2d0b188f5d87699b6f89da8c77bb7f20 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:33 +0000 Subject: [PATCH 310/405] comedi: amplc_pc263: Add sanity checks for I/O base address The "amplc_pc263" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a supported PC263 board. It currently allows any base address to be configured but the hardware only supports base addresses (set by on-board DIP switches) in the range 0 to 0x7FE on 2-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-9-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/amplc_pc263.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/amplc_pc263.c b/drivers/comedi/drivers/amplc_pc263.c index d7f088a8a5e3..e2196dc9d426 100644 --- a/drivers/comedi/drivers/amplc_pc263.c +++ b/drivers/comedi/drivers/amplc_pc263.c @@ -61,7 +61,8 @@ static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x2); + ret = comedi_check_request_region(dev, it->options[0], 0x2, + 0, 0x7ff, 2); if (ret) return ret; From 7f318c7223a27a80bf9e449d8c60db852ecd7814 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:34 +0000 Subject: [PATCH 311/405] comedi: c6xdigio: Add sanity checks for I/O base address The "c6xdigio" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a supported C6x_DIGIO DSP device connected to a PC printer parallel port (driving the port's I/O registers directly). Currently, the driver allows any I/O base address to be specified as long as the I/O region can be reserved, and it converts the specified `int` option value holding the base address to `unsigned long`. It doesn't make sense to allow base addresses that are not aligned to 4-byte boundaries (for SPP printer ports, although printer ports with EPP/ECP support actually need to be aligned on 8-byte boundaries), so add a check for 4-byte alignment. Convert the option value that specifies the base address from `int` to `unsigned int` instead of `unsigned long` so it ends up the same on 32-bit and 64-bit systems. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-10-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/c6xdigio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/c6xdigio.c b/drivers/comedi/drivers/c6xdigio.c index 8a38d97d463b..b6563a48ada6 100644 --- a/drivers/comedi/drivers/c6xdigio.c +++ b/drivers/comedi/drivers/c6xdigio.c @@ -239,9 +239,11 @@ static int c6xdigio_attach(struct comedi_device *dev, struct comedi_devconfig *it) { struct comedi_subdevice *s; + unsigned int iobase = it->options[0]; int ret; - ret = comedi_request_region(dev, it->options[0], 0x03); + ret = comedi_check_request_region(dev, iobase, 0x03, + 0, UINT_MAX, 4); if (ret) return ret; From 118fb051de0129e0565880119dd7fe64b233f969 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:35 +0000 Subject: [PATCH 312/405] comedi: comedi_parport: Add sanity checks for I/O base address The "comedi_parport" driver treats a standard printer parallel port as a COMEDI digital I/O device, driving the port's I/O registers directly. It uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of the device. Currently, the driver allows any I/O base address to be specified as long as the I/O region can be reserved, and it converts the specified `int` option value holding the base address to `unsigned long`. It doesn't make sense to allow base addresses that are not aligned to 4-byte boundaries (for SPP printer ports, although printer ports with EPP/ECP support actually need to be aligned on 8-byte boundaries), so add a check for 4-byte alignment. Convert the option value that specifies the base address from `int` to `unsigned int` instead of `unsigned long` so it ends up the same on 32-bit and 64-bit systems. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-11-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/comedi_parport.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/comedi_parport.c b/drivers/comedi/drivers/comedi_parport.c index 098738a688fe..2604680d86c4 100644 --- a/drivers/comedi/drivers/comedi_parport.c +++ b/drivers/comedi/drivers/comedi_parport.c @@ -225,9 +225,11 @@ static int parport_attach(struct comedi_device *dev, struct comedi_devconfig *it) { struct comedi_subdevice *s; + unsigned int iobase = it->options[0]; int ret; - ret = comedi_request_region(dev, it->options[0], 0x03); + ret = comedi_check_request_region(dev, iobase, 0x03, + 0, UINT_MAX, 4); if (ret) return ret; From 2750aecdf60524a7d58ecdaaa6e9728ad008cadb Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:36 +0000 Subject: [PATCH 313/405] comedi: dac02: Add sanity checks for I/O base address The "dac02" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a supported DAC-02 board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by an on-board DIP switch) in the range 0x200 to 0x3f8 on 8-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-12-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/dac02.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/dac02.c b/drivers/comedi/drivers/dac02.c index 4b011d66d7b0..2c04472c5078 100644 --- a/drivers/comedi/drivers/dac02.c +++ b/drivers/comedi/drivers/dac02.c @@ -103,7 +103,8 @@ static int dac02_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x08); + ret = comedi_check_request_region(dev, it->options[0], 0x08, + 0x200, 0x3ff, 8); if (ret) return ret; From 9a27a33cd9b80a7304637356e8a5a9c5b846734f Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:37 +0000 Subject: [PATCH 314/405] comedi: das08_isa: Add sanity checks for I/O base address The "das08_isa" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a supported board in the DAS08 family. It currently allows any base address to be configured but the hardware only supports base addresses (configured by an on-board DIP switch) in the range 0 to 0x3f0 on 16-byte boundaries. (Technically, the DIP switches allow 8-byte boundaries, but I do not think that is advisable given that the boards decode an 16-byte address range.) Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-13-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/das08_isa.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/das08_isa.c b/drivers/comedi/drivers/das08_isa.c index 3d43b77cc9f4..1b022dc10b78 100644 --- a/drivers/comedi/drivers/das08_isa.c +++ b/drivers/comedi/drivers/das08_isa.c @@ -167,7 +167,8 @@ static int das08_isa_attach(struct comedi_device *dev, if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], board->iosize); + ret = comedi_check_request_region(dev, it->options[0], board->iosize, + 0, 0x3ff, board->iosize); if (ret) return ret; From d0c1eee1975b582865b63f66901a6e50fcc6a299 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:38 +0000 Subject: [PATCH 315/405] comedi: das16: Add sanity checks for I/O base address The "das16" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a various DAS16 compatible boards. It currently allows any base address to be configured but the hardware only supports base addresses (configured by an on-board DIP switch) in the range 0 to 0x3f0 on 16- or 32-byte boundaries. Some of the boards have an 8255 chip at offset 0x10 and require the board to be configured on a 32-byte boundary unless some on-board jumpers are set to limit the board to decoding only the first 0x10 registers, disabling access to the 8255. Some other boards place the 8255 chip (and some other registers) at offset 0x400 from the base address, decoding 0x10 registers at the base address and 0x8 registers at the base address plus 0x400. Add a sanity check to ensure the device is not configured at an unsupported base address. If the device has the 8255 chip at offset 0x10, and is being configured with the base address at an odd 16-byte boundary, limit the size of the region to 0x10 and disable the 8255 subdevice. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-14-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/das16.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/comedi/drivers/das16.c b/drivers/comedi/drivers/das16.c index 1f85572c21b4..6eebfda2cb53 100644 --- a/drivers/comedi/drivers/das16.c +++ b/drivers/comedi/drivers/das16.c @@ -1018,6 +1018,7 @@ static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it) const struct das16_board *board = dev->board_ptr; struct das16_private_struct *devpriv; struct comedi_subdevice *s; + unsigned int iobase = it->options[0]; unsigned int osc_base; unsigned int status; int ret; @@ -1037,11 +1038,25 @@ static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it) devpriv->dev = dev; if (board->size < 0x400) { - ret = comedi_request_region(dev, it->options[0], board->size); + unsigned int size = board->size; + + if (size > 0x10 && (iobase & 0x10) != 0) { + /* + * The board has more than 0x10 registers and is + * being placed on an odd 16-byte boundary. The + * board has some jumpers to configure this mode, + * disabling the 8255 at offset 0x10, so only 0x10 + * registers will need to be mapped in this mode. + */ + size = 0x10; + } + ret = comedi_check_request_region(dev, iobase, size, + 0, 0x3ff, 16); if (ret) return ret; } else { - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, iobase, 0x10, + 0, 0x3ff, 16); if (ret) return ret; /* Request an additional region for the 8255 */ @@ -1146,9 +1161,15 @@ static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it) /* 8255 Digital I/O subdevice */ if (board->has_8255) { s = &dev->subdevices[4]; - ret = subdev_8255_io_init(dev, s, board->i8255_offset); - if (ret) - return ret; + if (board->i8255_offset == 0x10 && (dev->iobase & 0x10) != 0) { + dev_info(dev->class_dev, + "Disabling 8255 subdevice on unsupported base address\n"); + s->type = COMEDI_SUBD_UNUSED; + } else { + ret = subdev_8255_io_init(dev, s, board->i8255_offset); + if (ret) + return ret; + } } das16_reset(dev); From f8f950e030bee31b4a69ef29a6c2eeb57d31f7ae Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:39 +0000 Subject: [PATCH 316/405] comedi: das16m1: Add sanity checks for I/O base address The "das16m1" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a DAS16/M1 board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by an on-board DIP switch) in the range 0 to 0x3f0 on 16-byte boundaries. It has an additional span of 0x8 registers at offset 0x400 from the main 0x10 byte region. Add a sanity check to ensure the device is not configured at an unsupported base address. If the main base address is correctly aligned and within range, then the additional region at offset 0x400 from the configured base address will naturally be within range and correctly aligned. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-15-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/das16m1.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/das16m1.c b/drivers/comedi/drivers/das16m1.c index 1b638f5b5a4f..9f531ba593b4 100644 --- a/drivers/comedi/drivers/das16m1.c +++ b/drivers/comedi/drivers/das16m1.c @@ -511,7 +511,8 @@ static int das16m1_attach(struct comedi_device *dev, if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0, 0x3ff, 16); if (ret) return ret; /* Request an additional region for the 8255 and 3rd 8254 */ From b07802b8572601c01c6fc84e01c4af50b21da7f4 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:40 +0000 Subject: [PATCH 317/405] comedi: das1800: Add sanity checks for I/O base address The "das1800" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a board compatible with the DAS1800 series. It currently allows any base address to be configured but the hardware only supports base addresses (configured by an on-board DIP switch) in the range 0 to 0x3f0 on 16-byte boundaries. Some boards have an additional span of up to 0x10 registers at offset 0x400 from the main 0x10 byte region. Add a sanity check to ensure the device is not configured at an unsupported base address. If the main base address is correctly aligned and within range, then the additional region at offset 0x400 from the configured base address will naturally be within range and correctly aligned. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-16-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/das1800.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/das1800.c b/drivers/comedi/drivers/das1800.c index 7117c67aee7e..7d7ea99a81aa 100644 --- a/drivers/comedi/drivers/das1800.c +++ b/drivers/comedi/drivers/das1800.c @@ -1172,7 +1172,8 @@ static int das1800_attach(struct comedi_device *dev, if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], DAS1800_SIZE); + ret = comedi_check_request_region(dev, it->options[0], DAS1800_SIZE, + 0, 0x3ff, 16); if (ret) return ret; From 2ddb4c5699129d8fc9ce8ae0855f47ca2c6b9509 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:41 +0000 Subject: [PATCH 318/405] comedi: das6402: Add sanity checks for I/O base address The "das6402" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a supported board in the DAS6402 family. It currently allows any base address to be configured but the hardware only supports base addresses (configured by an on-board DIP switch) in the range 0 to 0x3f0 on 16-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-17-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/das6402.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/das6402.c b/drivers/comedi/drivers/das6402.c index 7660487e563c..516a5d5a2840 100644 --- a/drivers/comedi/drivers/das6402.c +++ b/drivers/comedi/drivers/das6402.c @@ -560,7 +560,8 @@ static int das6402_attach(struct comedi_device *dev, if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0, 0x3ff, 16); if (ret) return ret; From 46fe103de0af2707381a32974407a562b4f6e85a Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:42 +0000 Subject: [PATCH 319/405] comedi: das800: Add sanity checks for I/O base address The "das800" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a supported board in the DAS800 family. It currently allows any base address to be configured but the hardware only supports base addresses (configured by an on-board DIP switch) in the range 0 to 0x3f8 on 8-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-18-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/das800.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/das800.c b/drivers/comedi/drivers/das800.c index 300775523031..7563f40a4023 100644 --- a/drivers/comedi/drivers/das800.c +++ b/drivers/comedi/drivers/das800.c @@ -655,7 +655,8 @@ static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x8); + ret = comedi_check_request_region(dev, it->options[0], 0x8, + 0, 0x3ff, 8); if (ret) return ret; From 40f86ce1d117ee8defe7fc6b54e3fb355cde25e5 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:43 +0000 Subject: [PATCH 320/405] comedi: dmm32at: Add sanity check for I/O base address The "dmm32at" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a Diamond-MM-32-AT board. It currently allows any base address to be configured but the hardware only supports 8 possible base addresses (selected by 3 on-board jumpers). These are 0x100, 0x140, 0x180, 0x200, 0x280, 0x300, 0x340, and 0x380. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-19-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/dmm32at.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/comedi/drivers/dmm32at.c b/drivers/comedi/drivers/dmm32at.c index 910cd24b1bed..d1d9f75e168f 100644 --- a/drivers/comedi/drivers/dmm32at.c +++ b/drivers/comedi/drivers/dmm32at.c @@ -572,11 +572,27 @@ static int dmm32at_attach(struct comedi_device *dev, struct comedi_devconfig *it) { struct comedi_subdevice *s; + unsigned int iobase = it->options[0]; int ret; - ret = comedi_request_region(dev, it->options[0], 0x10); - if (ret) - return ret; + switch (iobase) { + case 0x100: + case 0x140: + case 0x180: + case 0x200: + case 0x280: + case 0x300: + case 0x340: + case 0x380: + ret = comedi_request_region(dev, iobase, 0x10); + if (ret) + return ret; + break; + default: + dev_err(dev->class_dev, "unsupported base address %#x\n", + iobase); + return -EINVAL; + } ret = dmm32at_reset(dev); if (ret) { From 4ede1d2e697bef66d068058bbe3ff5d9c392b56b Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:44 +0000 Subject: [PATCH 321/405] comedi: dt2801: Add sanity checks for I/O base address The "dt2801" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a supported board in the DT2801 family. It currently allows any base address to be configured but the hardware only supports base addresses (configured by an on-board DIP switch) in the range 0x200 to 0x3fe on 2-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-20-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/dt2801.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/dt2801.c b/drivers/comedi/drivers/dt2801.c index 230d25010f58..d790e69f98a0 100644 --- a/drivers/comedi/drivers/dt2801.c +++ b/drivers/comedi/drivers/dt2801.c @@ -540,7 +540,8 @@ static int dt2801_attach(struct comedi_device *dev, struct comedi_devconfig *it) int ret = 0; int n_ai_chans; - ret = comedi_request_region(dev, it->options[0], 0x2); + ret = comedi_check_request_region(dev, it->options[0], 0x2, + 0x200, 0x3ff, 2); if (ret) return ret; From 408bece94e7bdba3b420f1bf4607a2d2dbab1faf Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:45 +0000 Subject: [PATCH 322/405] comedi: dt2811: Add sanity checks for I/O base address The "dt2811" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a supported board in the DT2811 family. It currently allows any base address to be configured but the hardware only supports base addresses (configured by on-board jumpers) in the range 0x200 to 0x3f8 on 8-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-21-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/dt2811.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/dt2811.c b/drivers/comedi/drivers/dt2811.c index dbb9f38da289..bcc4b5ef48e8 100644 --- a/drivers/comedi/drivers/dt2811.c +++ b/drivers/comedi/drivers/dt2811.c @@ -556,7 +556,8 @@ static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x8); + ret = comedi_check_request_region(dev, it->options[0], 0x8, + 0x200, 0x3ff, 8); if (ret) return ret; From 576067b4c05c3f5470a35be2414f2b421042df3c Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:46 +0000 Subject: [PATCH 323/405] comedi: dt2814: Add sanity checks for I/O base address The "dt2814" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a DT2814 board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by an on-board DIP switch) in the range 0x200 to 0x3fe on 2-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-22-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/dt2814.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/dt2814.c b/drivers/comedi/drivers/dt2814.c index c98a5a4a7aec..caec16482afb 100644 --- a/drivers/comedi/drivers/dt2814.c +++ b/drivers/comedi/drivers/dt2814.c @@ -305,7 +305,8 @@ static int dt2814_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x2); + ret = comedi_check_request_region(dev, it->options[0], 0x2, + 0x200, 0x3ff, 2); if (ret) return ret; From 4804bacd9d7bfa56670d4449ef034234384afc93 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:47 +0000 Subject: [PATCH 324/405] comedi: dt2815: Add sanity checks for I/O base address The "dt2815" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a DT2815 board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by an on-board DIP switch) in the range 0x200 to 0x3fe on 2-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-23-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/dt2815.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/dt2815.c b/drivers/comedi/drivers/dt2815.c index 03ba2fd18a21..3fa184451523 100644 --- a/drivers/comedi/drivers/dt2815.c +++ b/drivers/comedi/drivers/dt2815.c @@ -144,7 +144,8 @@ static int dt2815_attach(struct comedi_device *dev, struct comedi_devconfig *it) const struct comedi_lrange *current_range_type, *voltage_range_type; int ret; - ret = comedi_request_region(dev, it->options[0], 0x2); + ret = comedi_check_request_region(dev, it->options[0], 0x2, + 0x200, 0x3ff, 2); if (ret) return ret; From 3742ff1605d50c8c2ea78a896f2e281b1f785c37 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:48 +0000 Subject: [PATCH 325/405] comedi: dt2817: Add sanity checks for I/O base address The "dt2817" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a DT2817 board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by on-board jumpers) in the range 0x200 to 0x3f8 on 8-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-24-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/dt2817.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/dt2817.c b/drivers/comedi/drivers/dt2817.c index 6738045c7531..9fd5b47c7fa7 100644 --- a/drivers/comedi/drivers/dt2817.c +++ b/drivers/comedi/drivers/dt2817.c @@ -103,7 +103,8 @@ static int dt2817_attach(struct comedi_device *dev, struct comedi_devconfig *it) int ret; struct comedi_subdevice *s; - ret = comedi_request_region(dev, it->options[0], 0x5); + ret = comedi_check_request_region(dev, it->options[0], 0x5, + 0x200, 0x3ff, 8); if (ret) return ret; From 7bd294e569114f05ed76d3fd3f0d1da9392bf89a Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:49 +0000 Subject: [PATCH 326/405] comedi: fl512: Add sanity checks for I/O base address The "fl512" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of an FL512 board. It currently allows any base address to be configured and uses a 16-byte register region. I cannot find any information about this board, but assume it needs to be aligned to a 16-byte boundary. I have no idea about the allowed range, so allow anything in a 32-bit range and add a "FIXME" comment (although most ancient ISA cards only support 10-bit address decoding). Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-25-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/dt282x.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/dt282x.c b/drivers/comedi/drivers/dt282x.c index 4ae80e6c7266..29832e1f062d 100644 --- a/drivers/comedi/drivers/dt282x.c +++ b/drivers/comedi/drivers/dt282x.c @@ -1064,7 +1064,12 @@ static int dt282x_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x10); + /* + * Although it has only 16 bytes (8 16-bit registers), it needs to + * start on a 32-byte boundary in range 0x200 to 0x3E0. + */ + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0x200, 0x3ff, 32); if (ret) return ret; From a9df1524dfcff792881556d9ab2fd17b38b330e1 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:50 +0000 Subject: [PATCH 327/405] comedi: mpc624: Add sanity checks for I/O base address The "mpc624" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a MPC624 board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by on-board jumpers) in the range 0 to 0x3F0 on 16-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-26-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/fl512.c | 8 +++++++- drivers/comedi/drivers/mpc624.c | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/comedi/drivers/fl512.c b/drivers/comedi/drivers/fl512.c index 139e801fc358..d9e6007556ac 100644 --- a/drivers/comedi/drivers/fl512.c +++ b/drivers/comedi/drivers/fl512.c @@ -98,9 +98,15 @@ static int fl512_ao_insn_write(struct comedi_device *dev, static int fl512_attach(struct comedi_device *dev, struct comedi_devconfig *it) { struct comedi_subdevice *s; + unsigned int iobase = it->options[0]; int ret; - ret = comedi_request_region(dev, it->options[0], 0x10); + /* + * FIXME: Don't know the allowed range, but assume it needs to be + * on a 16-byte boundary - Ian Abbott + */ + ret = comedi_check_request_region(dev, iobase, 0x10, + 0, UINT_MAX, 16); if (ret) return ret; diff --git a/drivers/comedi/drivers/mpc624.c b/drivers/comedi/drivers/mpc624.c index 9e51ff528ed1..e6343f4267c1 100644 --- a/drivers/comedi/drivers/mpc624.c +++ b/drivers/comedi/drivers/mpc624.c @@ -237,7 +237,8 @@ static int mpc624_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0, 0x3ff, 16); if (ret) return ret; From 66563dd6e3c5860e0a8b4b64a92f2ea1daf603b3 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:51 +0000 Subject: [PATCH 328/405] comedi: multiq3: Add sanity checks for I/O base address The "multiq3" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a Multiq-3 board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by on-board jumpers) in the range 0 to 0x3F0 on 16-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-27-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/multiq3.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/multiq3.c b/drivers/comedi/drivers/multiq3.c index ac369e9a262d..3dde3a1876d7 100644 --- a/drivers/comedi/drivers/multiq3.c +++ b/drivers/comedi/drivers/multiq3.c @@ -259,7 +259,8 @@ static int multiq3_attach(struct comedi_device *dev, int ret; int i; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0, 0x3ff, 16); if (ret) return ret; From 3389e476ec87fd210eaa0b071e148c8dbe0515ba Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:52 +0000 Subject: [PATCH 329/405] comedi: ni_at_a2150: Add sanity checks for I/O base address The "ni_at_a2150" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of an AT-A2150 series board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by on-board jumpers) in the range 0 to 0x3E0 on 32-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-28-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/ni_at_a2150.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/ni_at_a2150.c b/drivers/comedi/drivers/ni_at_a2150.c index e4e5a0ebd195..44221c928e32 100644 --- a/drivers/comedi/drivers/ni_at_a2150.c +++ b/drivers/comedi/drivers/ni_at_a2150.c @@ -694,7 +694,8 @@ static int a2150_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x1c); + ret = comedi_check_request_region(dev, it->options[0], 0x1c, + 0, 0x3ff, 32); if (ret) return ret; From f0995260a89e5a59cc9e24749c50ec20ec907dbc Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:53 +0000 Subject: [PATCH 330/405] comedi: ni_at_ao: Add sanity checks for I/O base address The "ni_at_ao" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of an AT-AO-6 or AT-AO-10 board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by on-board jumpers) in the range 0 to 0x3E0 on 32-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-29-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/ni_at_ao.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/ni_at_ao.c b/drivers/comedi/drivers/ni_at_ao.c index 9cf6b4ff6b65..31fd64bf8206 100644 --- a/drivers/comedi/drivers/ni_at_ao.c +++ b/drivers/comedi/drivers/ni_at_ao.c @@ -295,7 +295,8 @@ static int atao_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x20); + ret = comedi_check_request_region(dev, it->options[0], 0x20, + 0, 0x3ff, 32); if (ret) return ret; From 68fc1eebe9525bd6c2334d1394e4c46692c09806 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:54 +0000 Subject: [PATCH 331/405] comedi: ni_atmio: Add sanity checks for I/O base address The "ni_atmio" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of an AT E Series board. Or, if the option value is zero, it can search ISA PNP devices to look for a compatible board. If the base address is configured manually, it currently allows any base address to be configured but the hardware only supports base addresses (configured by a configuration utility and stored in nonvolatile memory) in the range 0x20 to 0xFFE0 on 32-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-30-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/ni_atmio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/ni_atmio.c b/drivers/comedi/drivers/ni_atmio.c index b4e759e5703f..7bc336333ace 100644 --- a/drivers/comedi/drivers/ni_atmio.c +++ b/drivers/comedi/drivers/ni_atmio.c @@ -311,7 +311,8 @@ static int ni_atmio_attach(struct comedi_device *dev, comedi_set_hw_dev(dev, &isapnp_dev->dev); } - ret = comedi_request_region(dev, iobase, 0x20); + ret = comedi_check_request_region(dev, iobase, 0x20, + 0x20, 0xffff, 32); if (ret) return ret; From df05bcfcbd32669dfa7e3d4a3d0f0b46ded5e61a Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:55 +0000 Subject: [PATCH 332/405] comedi: ni_atmio16d: Add sanity checks for I/O base address The "ni_atmio16d" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of an AT-MIO-16 o AT-MIO-16D board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by on-board DIP switches) in the range 0 to 0x3E0 on 32-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-31-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/ni_atmio16d.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/ni_atmio16d.c b/drivers/comedi/drivers/ni_atmio16d.c index e5e7cc423c87..cefa39b5077c 100644 --- a/drivers/comedi/drivers/ni_atmio16d.c +++ b/drivers/comedi/drivers/ni_atmio16d.c @@ -574,7 +574,8 @@ static int atmio16d_attach(struct comedi_device *dev, struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x20); + ret = comedi_check_request_region(dev, it->options[0], 0x20, + 0, 0x3ff, 32); if (ret) return ret; From 2555cab1cb79fe41fadca2eaf6674fddaf5f1404 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:56 +0000 Subject: [PATCH 333/405] comedi: ni_labpc: Add sanity checks for I/O base address The "ni_labpc" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a Lab-PC-1200 series or Lab-PC+ board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by a configuration utility and stored in nonvolatile memory) in the range 0 to 0x3E0 on 32-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-32-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/ni_labpc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/ni_labpc.c b/drivers/comedi/drivers/ni_labpc.c index b25a8e117072..93b5333a99b5 100644 --- a/drivers/comedi/drivers/ni_labpc.c +++ b/drivers/comedi/drivers/ni_labpc.c @@ -78,7 +78,8 @@ static int labpc_attach(struct comedi_device *dev, struct comedi_devconfig *it) unsigned int dma_chan = it->options[2]; int ret; - ret = comedi_request_region(dev, it->options[0], 0x20); + ret = comedi_check_request_region(dev, it->options[0], 0x20, + 0, 0x3ff, 32); if (ret) return ret; From 7e33ddd6d0282f34e63393e38110a3441a79f20a Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:57 +0000 Subject: [PATCH 334/405] comedi: pcl711: Add sanity checks for I/O base address The "pcl711" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of an Advantech PCL-711 series board or an Adlink ACL-8112 series board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by on-board DIP switches) in the range 0 to 0x3F0 (for PCL-711) or 0x200 to 0x3F0 (for ACL-8112) on 16-byte boundaries. Store the minimum supported I/O base address in the static board information array elements, and add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-33-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/pcl711.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/pcl711.c b/drivers/comedi/drivers/pcl711.c index 0cf3917defe7..5d2c4b2aa3bb 100644 --- a/drivers/comedi/drivers/pcl711.c +++ b/drivers/comedi/drivers/pcl711.c @@ -112,6 +112,7 @@ struct pcl711_board { int n_aichan; int n_aochan; int maxirq; + unsigned int min_io_start; const struct comedi_lrange *ai_range_type; }; @@ -132,12 +133,14 @@ static const struct pcl711_board boardtypes[] = { .n_aichan = 16, .n_aochan = 2, .maxirq = 15, + .min_io_start = 0x200, .ai_range_type = &range_acl8112hg_ai, }, { .name = "acl8112dg", .n_aichan = 16, .n_aochan = 2, .maxirq = 15, + .min_io_start = 0x200, .ai_range_type = &range_acl8112dg_ai, }, }; @@ -418,7 +421,8 @@ static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + board->min_io_start, 0x3ff, 16); if (ret) return ret; From b9723ebe043b49a93f4777173e202c2be5b53dcd Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:58 +0000 Subject: [PATCH 335/405] comedi: pcl724: Add sanity checks for I/O base address The "pcl724" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of various 8255 chip-based digital I/O ISA boards from Advantech, ADLINK, WinSystems, and Diamond Systems. It currently allows any base address to be configured but the hardware only supports base addresses (configured by on-board DIP switches or jumpers) in various ranges, and on various alignment boundaries, depending on the model. Store the minimum and maximum supported I/O address ranges in the static board information array elements (the required alignment is already stored in the `io_range` member), and add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-34-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/pcl724.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/pcl724.c b/drivers/comedi/drivers/pcl724.c index 00474710b81f..9707d0c89304 100644 --- a/drivers/comedi/drivers/pcl724.c +++ b/drivers/comedi/drivers/pcl724.c @@ -31,6 +31,8 @@ struct pcl724_board { const char *name; unsigned int io_range; + unsigned int min_io_start; + unsigned int max_io_end; unsigned int can_have96:1; unsigned int is_pet48:1; int numofports; @@ -40,37 +42,53 @@ static const struct pcl724_board boardtypes[] = { { .name = "pcl724", .io_range = 0x04, + .min_io_start = 0x200, + .max_io_end = 0x3ff, .numofports = 1, /* 24 DIO channels */ }, { .name = "pcl722", .io_range = 0x20, + .min_io_start = 0x200, + .max_io_end = 0x3ff, .can_have96 = 1, .numofports = 6, /* 144 (or 96) DIO channels */ }, { .name = "pcl731", .io_range = 0x08, + .min_io_start = 0, + .max_io_end = 0x3ff, .numofports = 2, /* 48 DIO channels */ }, { .name = "acl7122", .io_range = 0x20, + .min_io_start = 0x200, + .max_io_end = 0x3ff, .can_have96 = 1, .numofports = 6, /* 144 (or 96) DIO channels */ }, { .name = "acl7124", .io_range = 0x04, + .min_io_start = 0x200, + .max_io_end = 0x3ff, .numofports = 1, /* 24 DIO channels */ }, { .name = "pet48dio", .io_range = 0x02, + .min_io_start = 0, + .max_io_end = 0x3ff, .is_pet48 = 1, .numofports = 2, /* 48 DIO channels */ }, { .name = "pcmio48", .io_range = 0x08, + .min_io_start = 0x100, + .max_io_end = 0x17f, .numofports = 2, /* 48 DIO channels */ }, { .name = "onyx-mm-dio", .io_range = 0x10, + .min_io_start = 0, + .max_io_end = 0x3ff, .numofports = 2, /* 48 DIO channels */ }, }; @@ -112,7 +130,9 @@ static int pcl724_attach(struct comedi_device *dev, n_subdevices = 4; } - ret = comedi_request_region(dev, it->options[0], iorange); + ret = comedi_check_request_region(dev, it->options[0], iorange, + board->min_io_start, + board->max_io_end, iorange); if (ret) return ret; From 588b6ffa7775ed90d4afd4d2903fd49a6bb3cfbb Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:47:59 +0000 Subject: [PATCH 336/405] comedi: pcl726: Add sanity checks for I/O base address The "pcl726" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of various analog output ISA boards from Advantech (PCL-726/727/728) and ADLINK (ACL-6126/6128). (Most of them also have digital I/O.) It currently allows any base address to be configured but the hardware only supports base addresses (configured by on-board DIP switches) from 0 or 0x200 up to nearly 0x3FF, depending on the model. Store the minimum and maximum supported I/O address ranges in the static board information array elements (the required alignment is already stored in the `io_len` member), and add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-35-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/pcl726.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/comedi/drivers/pcl726.c b/drivers/comedi/drivers/pcl726.c index b542896fa0e4..d3b1f4388643 100644 --- a/drivers/comedi/drivers/pcl726.c +++ b/drivers/comedi/drivers/pcl726.c @@ -91,7 +91,8 @@ static const struct comedi_lrange *const rangelist_728[] = { struct pcl726_board { const char *name; - unsigned long io_len; + unsigned int io_len; + unsigned int min_io_start; unsigned int irq_mask; const struct comedi_lrange *const *ao_ranges; int ao_num_ranges; @@ -104,6 +105,7 @@ static const struct pcl726_board pcl726_boards[] = { { .name = "pcl726", .io_len = 0x10, + .min_io_start = 0x200, .ao_ranges = &rangelist_726[0], .ao_num_ranges = ARRAY_SIZE(rangelist_726), .ao_nchan = 6, @@ -111,6 +113,7 @@ static const struct pcl726_board pcl726_boards[] = { }, { .name = "pcl727", .io_len = 0x20, + .min_io_start = 0x200, .ao_ranges = &rangelist_727[0], .ao_num_ranges = ARRAY_SIZE(rangelist_727), .ao_nchan = 12, @@ -119,12 +122,14 @@ static const struct pcl726_board pcl726_boards[] = { }, { .name = "pcl728", .io_len = 0x08, + .min_io_start = 0, .ao_num_ranges = ARRAY_SIZE(rangelist_728), .ao_ranges = &rangelist_728[0], .ao_nchan = 2, }, { .name = "acl6126", .io_len = 0x10, + .min_io_start = 0x200, .irq_mask = 0x96e8, .ao_num_ranges = ARRAY_SIZE(rangelist_726), .ao_ranges = &rangelist_726[0], @@ -133,6 +138,7 @@ static const struct pcl726_board pcl726_boards[] = { }, { .name = "acl6128", .io_len = 0x08, + .min_io_start = 0, .ao_num_ranges = ARRAY_SIZE(rangelist_728), .ao_ranges = &rangelist_728[0], .ao_nchan = 2, @@ -316,7 +322,9 @@ static int pcl726_attach(struct comedi_device *dev, int ret; int i; - ret = comedi_request_region(dev, it->options[0], board->io_len); + ret = comedi_check_request_region(dev, it->options[0], board->io_len, + board->min_io_start, 0x3ff, + board->io_len); if (ret) return ret; From b5218a9897f5c381f513ea906a65655977a9c1b3 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:48:00 +0000 Subject: [PATCH 337/405] comedi: pcl730: Add sanity checks for I/O base address The "pcl730" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of various relay output and digital input ISA board from Advantech, ADLINK, ICP DAS, and Diamond Systems. It currently allows any base address to be configured but the hardware devices have restrictions on the base addresses (configured by on-board DIP switches or jumpers), including the alignment, which can be larger than the board's I/O register address span. The Diamond Systems IR104-PBF board is particularly restricted to 4 different base addresses with different sized gaps between the possible addresses. Store the minimum supported I/O base addresses and alignment in the static board information array elements and add a sanity check to ensure the device is not configured at an unsupported base address. For the IR104-PBF board, add a special check that the base address is one of the 4 supported base addresses for that board. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-36-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/pcl730.c | 49 +++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/drivers/comedi/drivers/pcl730.c b/drivers/comedi/drivers/pcl730.c index d2733cd5383d..cf0f97b6e2c0 100644 --- a/drivers/comedi/drivers/pcl730.c +++ b/drivers/comedi/drivers/pcl730.c @@ -101,7 +101,9 @@ struct pcl730_board { const char *name; - unsigned int io_range; + unsigned short io_range; + unsigned short min_io_start; + unsigned short align_io_start; unsigned is_pcl725:1; unsigned is_acl7225b:1; unsigned is_ir104:1; @@ -117,6 +119,8 @@ static const struct pcl730_board pcl730_boards[] = { { .name = "pcl730", .io_range = 0x04, + .min_io_start = 0, + .align_io_start = 0x04, .has_ttl_io = 1, .n_subdevs = 4, .n_iso_out_chan = 16, @@ -125,6 +129,8 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "iso730", .io_range = 0x04, + .min_io_start = 0, + .align_io_start = 0x04, .n_subdevs = 4, .n_iso_out_chan = 16, .n_iso_in_chan = 16, @@ -132,6 +138,8 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "acl7130", .io_range = 0x08, + .min_io_start = 0x200, + .align_io_start = 0x08, .has_ttl_io = 1, .n_subdevs = 4, .n_iso_out_chan = 16, @@ -140,6 +148,8 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "pcm3730", .io_range = 0x04, + .min_io_start = 0, + .align_io_start = 0x04, .has_ttl_io = 1, .n_subdevs = 4, .n_iso_out_chan = 8, @@ -148,6 +158,8 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "pcl725", .io_range = 0x02, + .min_io_start = 0x200, + .align_io_start = 0x02, .is_pcl725 = 1, .n_subdevs = 2, .n_iso_out_chan = 8, @@ -155,6 +167,8 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "p8r8dio", .io_range = 0x02, + .min_io_start = 0, + .align_io_start = 0x10, .is_pcl725 = 1, .has_readback = 1, .n_subdevs = 2, @@ -163,6 +177,8 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "acl7225b", .io_range = 0x08, /* only 4 are used */ + .min_io_start = 0x200, + .align_io_start = 0x08, .is_acl7225b = 1, .has_readback = 1, .n_subdevs = 2, @@ -171,6 +187,8 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "p16r16dio", .io_range = 0x04, + .min_io_start = 0, + .align_io_start = 0x08, .is_acl7225b = 1, .has_readback = 1, .n_subdevs = 2, @@ -179,16 +197,22 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "pcl733", .io_range = 0x04, + .min_io_start = 0, + .align_io_start = 0x04, .n_subdevs = 1, .n_iso_in_chan = 32, }, { .name = "pcl734", .io_range = 0x04, + .min_io_start = 0, + .align_io_start = 0x04, .n_subdevs = 1, .n_iso_out_chan = 32, }, { .name = "opmm-1616-xt", .io_range = 0x10, + .min_io_start = 0x100, + .align_io_start = 0x10, .is_acl7225b = 1, .has_readback = 1, .n_subdevs = 2, @@ -197,11 +221,15 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "pearl-mm-p", .io_range = 0x02, + .min_io_start = 0x240, + .align_io_start = 0x40, .n_subdevs = 1, .n_iso_out_chan = 16, }, { .name = "ir104-pbf", .io_range = 0x08, + .min_io_start = 0x240, + .align_io_start = 0x20, .is_ir104 = 1, .has_readback = 1, .n_iso_out_chan = 20, @@ -266,10 +294,27 @@ static int pcl730_attach(struct comedi_device *dev, { const struct pcl730_board *board = dev->board_ptr; struct comedi_subdevice *s; + unsigned int iobase = it->options[0]; int subdev; int ret; - ret = comedi_request_region(dev, it->options[0], board->io_range); + if (board->is_ir104) { + switch (iobase) { + case 0x240: + case 0x260: + case 0x280: + case 0x300: + break; + default: + dev_warn(dev->class_dev, + "%s: unsupported I/O base address %#x\n", + dev->board_name, iobase); + return -EINVAL; + } + } + ret = comedi_check_request_region(dev, iobase, board->io_range, + board->min_io_start, 0x3ff, + board->align_io_start); if (ret) return ret; From c24543463720ae8eb3aa1b35ff957fc96870cb94 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:48:01 +0000 Subject: [PATCH 338/405] comedi: pcl812: Add sanity checks for I/O base address The "pcl812" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of various analog/digital I/O ISA boards from Advantech, ADLINK, and ICP DAS. It currently allows any base address to be configured but the hardware devices only support base addresses (configured by on-board DIP switches) from 0 or 0x200 (depending on the model) to 0x3F0 on 16-byte boundaries. Store the minimum supported I/O base addresses in the static board information array elements and add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-37-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/pcl812.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/pcl812.c b/drivers/comedi/drivers/pcl812.c index abca61a72cf7..98b75792c09b 100644 --- a/drivers/comedi/drivers/pcl812.c +++ b/drivers/comedi/drivers/pcl812.c @@ -331,6 +331,7 @@ enum pcl812_boardtype { struct pcl812_board { const char *name; enum pcl812_boardtype board_type; + unsigned short min_io_start; int n_aichan; int n_aochan; unsigned int ai_ns_min; @@ -346,6 +347,7 @@ static const struct pcl812_board boardtypes[] = { { .name = "pcl812", .board_type = BOARD_PCL812, + .min_io_start = 0, .n_aichan = 16, .n_aochan = 2, .ai_ns_min = 33000, @@ -355,6 +357,7 @@ static const struct pcl812_board boardtypes[] = { .has_dio = 1, }, { .name = "pcl812pg", + .min_io_start = 0, .board_type = BOARD_PCL812PG, .n_aichan = 16, .n_aochan = 2, @@ -366,6 +369,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "acl8112pg", .board_type = BOARD_PCL812PG, + .min_io_start = 0x200, .n_aichan = 16, .n_aochan = 2, .ai_ns_min = 10000, @@ -376,6 +380,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "acl8112dg", .board_type = BOARD_ACL8112, + .min_io_start = 0x200, .n_aichan = 16, /* 8 differential */ .n_aochan = 2, .ai_ns_min = 10000, @@ -387,6 +392,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "acl8112hg", .board_type = BOARD_ACL8112, + .min_io_start = 0x200, .n_aichan = 16, /* 8 differential */ .n_aochan = 2, .ai_ns_min = 10000, @@ -398,6 +404,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "a821pgl", .board_type = BOARD_A821, + .min_io_start = 0, .n_aichan = 16, /* 8 differential */ .n_aochan = 1, .ai_ns_min = 10000, @@ -407,6 +414,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "a821pglnda", .board_type = BOARD_A821, + .min_io_start = 0, .n_aichan = 16, /* 8 differential */ .ai_ns_min = 10000, .rangelist_ai = &range_pcl813b_ai, @@ -414,6 +422,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "a821pgh", .board_type = BOARD_A821, + .min_io_start = 0, .n_aichan = 16, /* 8 differential */ .n_aochan = 1, .ai_ns_min = 10000, @@ -423,6 +432,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "a822pgl", .board_type = BOARD_ACL8112, + .min_io_start = 0, .n_aichan = 16, /* 8 differential */ .n_aochan = 2, .ai_ns_min = 10000, @@ -433,6 +443,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "a822pgh", .board_type = BOARD_ACL8112, + .min_io_start = 0, .n_aichan = 16, /* 8 differential */ .n_aochan = 2, .ai_ns_min = 10000, @@ -443,6 +454,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "a823pgl", .board_type = BOARD_ACL8112, + .min_io_start = 0, .n_aichan = 16, /* 8 differential */ .n_aochan = 2, .ai_ns_min = 8000, @@ -453,6 +465,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "a823pgh", .board_type = BOARD_ACL8112, + .min_io_start = 0, .n_aichan = 16, /* 8 differential */ .n_aochan = 2, .ai_ns_min = 8000, @@ -463,26 +476,31 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "pcl813", .board_type = BOARD_PCL813, + .min_io_start = 0, .n_aichan = 32, .rangelist_ai = &range_pcl813b_ai, }, { .name = "pcl813b", .board_type = BOARD_PCL813B, + .min_io_start = 0, .n_aichan = 32, .rangelist_ai = &range_pcl813b_ai, }, { .name = "acl8113", .board_type = BOARD_ACL8113, + .min_io_start = 0x200, .n_aichan = 32, .rangelist_ai = &range_acl8113_1_ai, }, { .name = "iso813", .board_type = BOARD_ISO813, + .min_io_start = 0, .n_aichan = 32, .rangelist_ai = &range_iso813_1_ai, }, { .name = "acl8216", .board_type = BOARD_ACL8216, + .min_io_start = 0x200, .n_aichan = 16, /* 8 differential */ .n_aochan = 2, .ai_ns_min = 10000, @@ -495,6 +513,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "a826pg", .board_type = BOARD_ACL8216, + .min_io_start = 0, .n_aichan = 16, /* 8 differential */ .n_aochan = 2, .ai_ns_min = 10000, @@ -1138,7 +1157,8 @@ static int pcl812_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + board->min_io_start, 0x3ff, 16); if (ret) return ret; From 55e39df167cbeaedc5744ba595514b7745454948 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:48:02 +0000 Subject: [PATCH 339/405] comedi: pcl816: Add sanity checks for I/O base address The "pcl816" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a PCL-816 or PCL-814B ISA board. It currently allows any base address to be configured but the hardware devices only support base addresses (configured by on-board DIP switches) from 0 to 0x3F0 on 16-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-38-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/pcl816.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/pcl816.c b/drivers/comedi/drivers/pcl816.c index 28d1a88c50f6..1fcb2f798c7a 100644 --- a/drivers/comedi/drivers/pcl816.c +++ b/drivers/comedi/drivers/pcl816.c @@ -608,7 +608,8 @@ static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0, 0x3ff, 16); if (ret) return ret; From aaddeab27c324f2885f5b2ca9f64f6ebd60df688 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:48:03 +0000 Subject: [PATCH 340/405] comedi: pcl818: Add sanity checks for I/O base address The "pcl818" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a board in the PCL-818 series. It currently allows any base address to be configured but the hardware devices only support base addresses (configured by on-board DIP switches) from 0 to 0x3F0 on 16-byte boundaries. If the board has a FIFO and jumper JP6 is in the "Enabled" (default) position, then the base address needs to be on a 32-byte boundary and the length of the I/O port region will be 32 (to allow access to the FIFO registers) instead of 16. The state of jumper JP6 is unknown, so if the board has a FIFO device and is being configured on an odd 16-byte boundary, assume that jumper JP6 is in the "Disabled" position (to disallow access to the FIFO registers). Add a sanity check to ensure the device is not configured at an unsupported base address. If the board has a FIFO and is configured on an odd 16-byte boundary, log a reminder that JP6 needs to be in the "Disabled" position for correct operation. If the board has a FIFO and is configured on an even 16-byte boundary and the configuration option has been set to use the FIFO (`it->options[2] == -1`), log a reminder that JP6 needs to be in the "Enabled" position. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-39-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/pcl818.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/comedi/drivers/pcl818.c b/drivers/comedi/drivers/pcl818.c index 06fe06396f23..aa775a024fc7 100644 --- a/drivers/comedi/drivers/pcl818.c +++ b/drivers/comedi/drivers/pcl818.c @@ -981,6 +981,10 @@ static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it) const struct pcl818_board *board = dev->board_ptr; struct pcl818_private *devpriv; struct comedi_subdevice *s; + unsigned int io_base = it->options[0]; + bool fifo_is_supported = board->has_fifo && !(io_base & 0x10); + bool fifo_is_wanted = board->has_fifo && it->options[2] == -1; + unsigned int io_len = fifo_is_supported ? 0x20 : 0x10; unsigned int osc_base; int ret; @@ -988,11 +992,28 @@ static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], - board->has_fifo ? 0x20 : 0x10); + ret = comedi_check_request_region(dev, io_base, io_len, + 0, 0x3ff, io_len); if (ret) return ret; + if (board->has_fifo) { + /* let user know about any required JP6 setting */ + if (fifo_is_supported) { + if (fifo_is_wanted) { + dev_info(dev->class_dev, + "Assuming JP6 is in \"Enabled\" (default) position to use the FIFO.\n"); + } + } else { + dev_info(dev->class_dev, + "JP6 needs to be in \"Disabled\" position for correct operation at this base address\n"); + if (fifo_is_wanted) { + dev_warn(dev->class_dev, + "FIFO cannot be used at this base address\n"); + } + } + } + /* we can use IRQ 2-7 for async command support */ if (it->options[1] >= 2 && it->options[1] <= 7) { ret = request_irq(it->options[1], pcl818_interrupt, 0, @@ -1002,7 +1023,7 @@ static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it) } /* should we use the FIFO? */ - if (dev->irq && board->has_fifo && it->options[2] == -1) + if (dev->irq && fifo_is_supported && fifo_is_wanted) devpriv->usefifo = 1; /* we need an IRQ to do DMA on channel 3 or 1 */ From ddd5b28744322f9434ab71661a1280f5069983fd Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:48:04 +0000 Subject: [PATCH 341/405] comedi: pcm3724: Add sanity checks for I/O base address The "pcm3724" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a PCM-3724 board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by on-board DIP switches) in the range 0 to 0x3F0 on 16-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-40-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/pcm3724.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/pcm3724.c b/drivers/comedi/drivers/pcm3724.c index fb41de3baef8..867bb5b3860a 100644 --- a/drivers/comedi/drivers/pcm3724.c +++ b/drivers/comedi/drivers/pcm3724.c @@ -194,7 +194,8 @@ static int pcm3724_attach(struct comedi_device *dev, if (!priv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0, 0x3ff, 16); if (ret) return ret; From 0ca9f8150ce5a41a3b4bb65f043f0335e7ef67b5 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:48:05 +0000 Subject: [PATCH 342/405] comedi: pcmad: Add sanity checks for I/O base address The "pcmad" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a PCM-A/D-12 or PCM-A/D-16 board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by on-board jumpers) in the range 0 to 0x3FC on 4-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-41-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/pcmad.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/pcmad.c b/drivers/comedi/drivers/pcmad.c index 976eda43881b..fd26b1eed1db 100644 --- a/drivers/comedi/drivers/pcmad.c +++ b/drivers/comedi/drivers/pcmad.c @@ -106,7 +106,8 @@ static int pcmad_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x04); + ret = comedi_check_request_region(dev, it->options[0], 0x04, + 0, 0x3ff, 4); if (ret) return ret; From c375d40dc77c449ded79675b6ae00d82e67559bd Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:48:06 +0000 Subject: [PATCH 343/405] comedi: pcmda12: Add sanity checks for I/O base address The "pcmda12" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a PCM-D/A-12 or PCM-A/D-16 board. It currently allows any base address to be configured. I cannot find a full manual, but the short datasheet says it uses 15 consecutive I/O addresses on "any even sixteen port boundary", so assume it supports base addresses (configured by on-board jumpers) in the range 0 to 0x3E0 on 32-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-42-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/pcmda12.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/pcmda12.c b/drivers/comedi/drivers/pcmda12.c index 611f13bedca0..6efd1ae6271a 100644 --- a/drivers/comedi/drivers/pcmda12.c +++ b/drivers/comedi/drivers/pcmda12.c @@ -120,7 +120,14 @@ static int pcmda12_attach(struct comedi_device *dev, struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x10); + /* + * The datasheet says it requires 16 contiguous addresses and is + * "configurable on any even sixteen port boundary". So require + * a 32-byte boundary and assume it uses 10-bit addresses like + * similar boards. + */ + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0, 0x3ff, 32); if (ret) return ret; From 78ba300a999179d79f4b8f479e58345c05e17baa Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:48:07 +0000 Subject: [PATCH 344/405] comedi: pcmmio: Add sanity checks for I/O base address The "pcmmio" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a PCM-MIO board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by on-board jumpers) in the range 0 to 0xFFE0 on 32-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-43-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/pcmmio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/pcmmio.c b/drivers/comedi/drivers/pcmmio.c index c2402239d551..d38202c8a12b 100644 --- a/drivers/comedi/drivers/pcmmio.c +++ b/drivers/comedi/drivers/pcmmio.c @@ -667,7 +667,8 @@ static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 32); + ret = comedi_check_request_region(dev, it->options[0], 32, + 0, 0xffff, 32); if (ret) return ret; From 3d4ab5484aa249f4950eba7f697cff6b3f024c7c Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:48:08 +0000 Subject: [PATCH 345/405] comedi: pcmuio: Add sanity checks for I/O base address The "pcmmio" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a PCM-UIO48A or PCM-UIO96A board. It will probably work with the later PCM-UIO48C and PCM-UIO96C boards. It currently allows any base address to be configured but the hardware only supports base addresses (configured by on-board jumpers) in the range 0 to 0xFFF0 on 16-byte boundaries (for PCM-UIO48C) or 0 to 0xFFE0 on 32-byte boundaries (for PCM-UIO96C). (The PCM-UIO48A supports base addresses up to 0xFF0 and the PCI-UIO96A supports base addresses up to 0x7E0.) Add a sanity check to ensure the device is not configured at an unsupported base address (allowing for the extended range of the "C" models). Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-44-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/pcmuio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/comedi/drivers/pcmuio.c b/drivers/comedi/drivers/pcmuio.c index 33b24dbbb919..0995911a3ea3 100644 --- a/drivers/comedi/drivers/pcmuio.c +++ b/drivers/comedi/drivers/pcmuio.c @@ -521,11 +521,12 @@ static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it) const struct pcmuio_board *board = dev->board_ptr; struct comedi_subdevice *s; struct pcmuio_private *devpriv; + unsigned int io_len = board->num_asics * PCMUIO_ASIC_IOSIZE; int ret; int i; - ret = comedi_request_region(dev, it->options[0], - board->num_asics * PCMUIO_ASIC_IOSIZE); + ret = comedi_check_request_region(dev, it->options[0], io_len, + 0, 0xffff, io_len); if (ret) return ret; From e2b311504acd03b5e720c763fcd41ccd928de3b3 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:48:09 +0000 Subject: [PATCH 346/405] comedi: rti800: Add sanity checks for I/O base address The "rti800" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a RTI-800 or RTI-815 board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by on-board DIP switches) in the range 0 to 0x3F0 on 16-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-45-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/rti800.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/rti800.c b/drivers/comedi/drivers/rti800.c index 1b02e47bdb4c..fa3965cf92f2 100644 --- a/drivers/comedi/drivers/rti800.c +++ b/drivers/comedi/drivers/rti800.c @@ -257,7 +257,8 @@ static int rti800_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0, 0x3ff, 16); if (ret) return ret; From 63c1983136d784d95054accd31d03006f518e71f Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:48:10 +0000 Subject: [PATCH 347/405] comedi: rti802: Add sanity checks for I/O base address The "rti800" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a RTI-802 board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by on-board DIP switches) in the range 0 to 0x3FC on 4-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-46-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/rti802.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/rti802.c b/drivers/comedi/drivers/rti802.c index d66762a22258..af990881c27a 100644 --- a/drivers/comedi/drivers/rti802.c +++ b/drivers/comedi/drivers/rti802.c @@ -72,7 +72,8 @@ static int rti802_attach(struct comedi_device *dev, struct comedi_devconfig *it) int i; int ret; - ret = comedi_request_region(dev, it->options[0], 0x04); + ret = comedi_check_request_region(dev, it->options[0], 0x04, + 0, 0x3ff, 4); if (ret) return ret; From ae377d6afbd5618500edf0954edfc15aa28b162c Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Jan 2026 16:48:11 +0000 Subject: [PATCH 348/405] comedi: s526: Add sanity checks for I/O base address The "s526" driver uses an admin-supplied configuration option (`it->options[0]`) to configure the I/O port base address of a Sensoray 526 board. It currently allows any base address to be configured but the hardware only supports base addresses (configured by on-board DIP switches) in the range 0 to 0xFFC0 on 64-byte boundaries. Add a sanity check to ensure the device is not configured at an unsupported base address. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260130170416.49994-47-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/s526.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/s526.c b/drivers/comedi/drivers/s526.c index 9245c679a3c4..dd27c754dea4 100644 --- a/drivers/comedi/drivers/s526.c +++ b/drivers/comedi/drivers/s526.c @@ -553,7 +553,8 @@ static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x40); + ret = comedi_check_request_region(dev, it->options[0], 0x40, + 0, 0xffc0, 64); if (ret) return ret; From b5720dabb04263c8fffc24983023a4e1f384049f Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 29 Jan 2026 11:44:02 +0000 Subject: [PATCH 349/405] comedi: Correct name of ACCES I/O Products Commit 6cd5a9a35c3d ("staging/trivial: fix typos concerning "access"") accidentally changed "Acces I/O Products" to "Access I/O Products", although "Acces" should actually be a capitalized acronym "ACCES" (standing for "Acquisition, Control, and Communication: Engineering & Systems"). Change it in the "aio_aio12_8" and "aio_iiro_16" drivers and change the Kconfig file to match. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260129114402.11033-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/Kconfig | 10 +++++----- drivers/comedi/drivers/aio_aio12_8.c | 12 ++++++------ drivers/comedi/drivers/aio_iiro_16.c | 8 ++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/comedi/Kconfig b/drivers/comedi/Kconfig index 6dcc2567de6d..c34a8d68547b 100644 --- a/drivers/comedi/Kconfig +++ b/drivers/comedi/Kconfig @@ -405,20 +405,20 @@ config COMEDI_FL512 called fl512. config COMEDI_AIO_AIO12_8 - tristate "I/O Products PC/104 AIO12-8 Analog I/O Board support" + tristate "ACCES I/O Products PC/104 AIO12-8 Analog I/O Board support" select COMEDI_8254 select COMEDI_8255 help - Enable support for I/O Products PC/104 AIO12-8 Analog I/O Board + Enable support for ACCES I/O Products PC/104 AIO12-8 Analog I/O Board To compile this driver as a module, choose M here: the module will be called aio_aio12_8. config COMEDI_AIO_IIRO_16 - tristate "I/O Products PC/104 IIRO16 Board support" + tristate "ACCES I/O Products PC/104 IIRO16 Board support" help - Enable support for I/O Products PC/104 IIRO16 Relay And Isolated - Input Board + Enable support for ACCES I/O Products PC/104 IIRO16 Relay And + Isolated Input Board To compile this driver as a module, choose M here: the module will be called aio_iiro_16. diff --git a/drivers/comedi/drivers/aio_aio12_8.c b/drivers/comedi/drivers/aio_aio12_8.c index 1d9e14981928..e0cbc2ec7c4d 100644 --- a/drivers/comedi/drivers/aio_aio12_8.c +++ b/drivers/comedi/drivers/aio_aio12_8.c @@ -1,17 +1,17 @@ // SPDX-License-Identifier: GPL-2.0+ /* * aio_aio12_8.c - * Driver for Access I/O Products PC-104 AIO12-8 Analog I/O Board + * Driver for ACCES I/O Products PC-104 AIO12-8 Analog I/O Board * Copyright (C) 2006 C&C Technologies, Inc. */ /* * Driver: aio_aio12_8 - * Description: Access I/O Products PC-104 AIO12-8 Analog I/O Board + * Description: ACCES I/O Products PC-104 AIO12-8 Analog I/O Board * Author: Pablo Mejia - * Devices: [Access I/O] PC-104 AIO12-8 (aio_aio12_8), - * [Access I/O] PC-104 AI12-8 (aio_ai12_8), - * [Access I/O] PC-104 AO12-4 (aio_ao12_4) + * Devices: [ACCES I/O] PC-104 AIO12-8 (aio_aio12_8), + * [ACCES I/O] PC-104 AI12-8 (aio_ai12_8), + * [ACCES I/O] PC-104 AO12-4 (aio_ao12_4) * Status: experimental * * Configuration Options: @@ -273,5 +273,5 @@ static struct comedi_driver aio_aio12_8_driver = { module_comedi_driver(aio_aio12_8_driver); MODULE_AUTHOR("Comedi https://www.comedi.org"); -MODULE_DESCRIPTION("Comedi driver for Access I/O AIO12-8 Analog I/O Board"); +MODULE_DESCRIPTION("Comedi driver for ACCES I/O AIO12-8 Analog I/O Board"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/aio_iiro_16.c b/drivers/comedi/drivers/aio_iiro_16.c index 00d0bb0f6256..d5d18fa2c638 100644 --- a/drivers/comedi/drivers/aio_iiro_16.c +++ b/drivers/comedi/drivers/aio_iiro_16.c @@ -1,15 +1,15 @@ // SPDX-License-Identifier: GPL-2.0+ /* * aio_iiro_16.c - * Comedi driver for Access I/O Products 104-IIRO-16 board + * Comedi driver for ACCES I/O Products 104-IIRO-16 board * Copyright (C) 2006 C&C Technologies, Inc. */ /* * Driver: aio_iiro_16 - * Description: Access I/O Products PC/104 Isolated Input/Relay Output Board + * Description: ACCES I/O Products PC/104 Isolated Input/Relay Output Board * Author: Zachary Ware - * Devices: [Access I/O] 104-IIRO-16 (aio_iiro_16) + * Devices: [ACCES I/O] 104-IIRO-16 (aio_iiro_16) * Status: experimental * * Configuration Options: @@ -232,5 +232,5 @@ static struct comedi_driver aio_iiro_16_driver = { module_comedi_driver(aio_iiro_16_driver); MODULE_AUTHOR("Comedi https://www.comedi.org"); -MODULE_DESCRIPTION("Comedi driver for Access I/O Products 104-IIRO-16 board"); +MODULE_DESCRIPTION("Comedi driver for ACCES I/O Products 104-IIRO-16 board"); MODULE_LICENSE("GPL"); From b06e78190f6fa58a4b53651aa6e4f8dfaec7bdd9 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Fri, 30 Jan 2026 17:36:52 -0800 Subject: [PATCH 350/405] comedi: remove unnecessary module_init/exit functions Many Comedi drivers have unnecessary empty module_init and module_exit functions. Remove them. Note that if a module_init function exists, a module_exit function must also exist; otherwise, the module cannot be unloaded. Signed-off-by: Ethan Nelson-Moore Reviewed-by: Ian Abbott Link: https://patch.msgid.link/20260131013810.32265-1-enelsonmoore@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/comedi_pci.c | 11 ----------- drivers/comedi/comedi_pcmcia.c | 11 ----------- drivers/comedi/comedi_usb.c | 11 ----------- drivers/comedi/drivers/addi_watchdog.c | 11 ----------- drivers/comedi/drivers/amplc_dio200_common.c | 11 ----------- drivers/comedi/drivers/amplc_pc236_common.c | 11 ----------- drivers/comedi/drivers/comedi_8254.c | 11 ----------- drivers/comedi/drivers/comedi_8255.c | 11 ----------- drivers/comedi/drivers/comedi_isadma.c | 11 ----------- drivers/comedi/drivers/das08.c | 11 ----------- drivers/comedi/drivers/mite.c | 11 ----------- drivers/comedi/drivers/ni_labpc_common.c | 11 ----------- drivers/comedi/drivers/ni_labpc_isadma.c | 11 ----------- drivers/comedi/drivers/ni_tio.c | 11 ----------- drivers/comedi/drivers/ni_tiocmd.c | 11 ----------- drivers/comedi/kcomedilib/kcomedilib_main.c | 12 ------------ 16 files changed, 177 deletions(-) diff --git a/drivers/comedi/comedi_pci.c b/drivers/comedi/comedi_pci.c index cc2581902195..da618f5e3a4d 100644 --- a/drivers/comedi/comedi_pci.c +++ b/drivers/comedi/comedi_pci.c @@ -211,17 +211,6 @@ void comedi_pci_driver_unregister(struct comedi_driver *comedi_driver, } EXPORT_SYMBOL_GPL(comedi_pci_driver_unregister); -static int __init comedi_pci_init(void) -{ - return 0; -} -module_init(comedi_pci_init); - -static void __exit comedi_pci_exit(void) -{ -} -module_exit(comedi_pci_exit); - MODULE_AUTHOR("https://www.comedi.org"); MODULE_DESCRIPTION("Comedi PCI interface module"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/comedi_pcmcia.c b/drivers/comedi/comedi_pcmcia.c index c53aad0fc2ce..17962bf66892 100644 --- a/drivers/comedi/comedi_pcmcia.c +++ b/drivers/comedi/comedi_pcmcia.c @@ -192,17 +192,6 @@ void comedi_pcmcia_driver_unregister(struct comedi_driver *comedi_driver, } EXPORT_SYMBOL_GPL(comedi_pcmcia_driver_unregister); -static int __init comedi_pcmcia_init(void) -{ - return 0; -} -module_init(comedi_pcmcia_init); - -static void __exit comedi_pcmcia_exit(void) -{ -} -module_exit(comedi_pcmcia_exit); - MODULE_AUTHOR("https://www.comedi.org"); MODULE_DESCRIPTION("Comedi PCMCIA interface module"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/comedi_usb.c b/drivers/comedi/comedi_usb.c index d11ea148ebf8..75d65171c76d 100644 --- a/drivers/comedi/comedi_usb.c +++ b/drivers/comedi/comedi_usb.c @@ -134,17 +134,6 @@ void comedi_usb_driver_unregister(struct comedi_driver *comedi_driver, } EXPORT_SYMBOL_GPL(comedi_usb_driver_unregister); -static int __init comedi_usb_init(void) -{ - return 0; -} -module_init(comedi_usb_init); - -static void __exit comedi_usb_exit(void) -{ -} -module_exit(comedi_usb_exit); - MODULE_AUTHOR("https://www.comedi.org"); MODULE_DESCRIPTION("Comedi USB interface module"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/addi_watchdog.c b/drivers/comedi/drivers/addi_watchdog.c index ed87ab432020..b778c88d4c11 100644 --- a/drivers/comedi/drivers/addi_watchdog.c +++ b/drivers/comedi/drivers/addi_watchdog.c @@ -124,17 +124,6 @@ int addi_watchdog_init(struct comedi_subdevice *s, unsigned long iobase) } EXPORT_SYMBOL_GPL(addi_watchdog_init); -static int __init addi_watchdog_module_init(void) -{ - return 0; -} -module_init(addi_watchdog_module_init); - -static void __exit addi_watchdog_module_exit(void) -{ -} -module_exit(addi_watchdog_module_exit); - MODULE_DESCRIPTION("ADDI-DATA Watchdog subdevice"); MODULE_AUTHOR("H Hartley Sweeten "); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/amplc_dio200_common.c b/drivers/comedi/drivers/amplc_dio200_common.c index b1a9b4c4a185..d61f51900976 100644 --- a/drivers/comedi/drivers/amplc_dio200_common.c +++ b/drivers/comedi/drivers/amplc_dio200_common.c @@ -901,17 +901,6 @@ int amplc_dio200_common_attach(struct comedi_device *dev, unsigned int irq, } EXPORT_SYMBOL_GPL(amplc_dio200_common_attach); -static int __init amplc_dio200_common_init(void) -{ - return 0; -} -module_init(amplc_dio200_common_init); - -static void __exit amplc_dio200_common_exit(void) -{ -} -module_exit(amplc_dio200_common_exit); - MODULE_AUTHOR("Comedi https://www.comedi.org"); MODULE_DESCRIPTION("Comedi helper for amplc_dio200 and amplc_dio200_pci"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/amplc_pc236_common.c b/drivers/comedi/drivers/amplc_pc236_common.c index 326ca72c24ec..c3692f50bdb9 100644 --- a/drivers/comedi/drivers/amplc_pc236_common.c +++ b/drivers/comedi/drivers/amplc_pc236_common.c @@ -176,17 +176,6 @@ int amplc_pc236_common_attach(struct comedi_device *dev, unsigned long iobase, } EXPORT_SYMBOL_GPL(amplc_pc236_common_attach); -static int __init amplc_pc236_common_init(void) -{ - return 0; -} -module_init(amplc_pc236_common_init); - -static void __exit amplc_pc236_common_exit(void) -{ -} -module_exit(amplc_pc236_common_exit); - MODULE_AUTHOR("Comedi https://www.comedi.org"); MODULE_DESCRIPTION("Comedi helper for amplc_pc236 and amplc_pci236"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/comedi_8254.c b/drivers/comedi/drivers/comedi_8254.c index a297dd650dab..158e8b4108a8 100644 --- a/drivers/comedi/drivers/comedi_8254.c +++ b/drivers/comedi/drivers/comedi_8254.c @@ -724,17 +724,6 @@ struct comedi_8254 *comedi_8254_mm_alloc(void __iomem *mmio, } EXPORT_SYMBOL_GPL(comedi_8254_mm_alloc); -static int __init comedi_8254_module_init(void) -{ - return 0; -} -module_init(comedi_8254_module_init); - -static void __exit comedi_8254_module_exit(void) -{ -} -module_exit(comedi_8254_module_exit); - MODULE_AUTHOR("H Hartley Sweeten "); MODULE_DESCRIPTION("Comedi: Generic 8254 timer/counter support"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/comedi_8255.c b/drivers/comedi/drivers/comedi_8255.c index a933ef53845a..fd6b1cf621a7 100644 --- a/drivers/comedi/drivers/comedi_8255.c +++ b/drivers/comedi/drivers/comedi_8255.c @@ -259,17 +259,6 @@ unsigned long subdev_8255_regbase(struct comedi_subdevice *s) } EXPORT_SYMBOL_GPL(subdev_8255_regbase); -static int __init comedi_8255_module_init(void) -{ - return 0; -} -module_init(comedi_8255_module_init); - -static void __exit comedi_8255_module_exit(void) -{ -} -module_exit(comedi_8255_module_exit); - MODULE_AUTHOR("Comedi https://www.comedi.org"); MODULE_DESCRIPTION("Comedi: Generic 8255 digital I/O support"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/comedi_isadma.c b/drivers/comedi/drivers/comedi_isadma.c index 04ce3eb9ae4f..f480640413f0 100644 --- a/drivers/comedi/drivers/comedi_isadma.c +++ b/drivers/comedi/drivers/comedi_isadma.c @@ -249,17 +249,6 @@ void comedi_isadma_free(struct comedi_isadma *dma) } EXPORT_SYMBOL_GPL(comedi_isadma_free); -static int __init comedi_isadma_init(void) -{ - return 0; -} -module_init(comedi_isadma_init); - -static void __exit comedi_isadma_exit(void) -{ -} -module_exit(comedi_isadma_exit); - MODULE_AUTHOR("H Hartley Sweeten "); MODULE_DESCRIPTION("Comedi ISA DMA support"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/das08.c b/drivers/comedi/drivers/das08.c index 49944ce1f813..a3298a3238e7 100644 --- a/drivers/comedi/drivers/das08.c +++ b/drivers/comedi/drivers/das08.c @@ -453,17 +453,6 @@ int das08_common_attach(struct comedi_device *dev, unsigned long iobase) } EXPORT_SYMBOL_GPL(das08_common_attach); -static int __init das08_init(void) -{ - return 0; -} -module_init(das08_init); - -static void __exit das08_exit(void) -{ -} -module_exit(das08_exit); - MODULE_AUTHOR("Comedi https://www.comedi.org"); MODULE_DESCRIPTION("Comedi common DAS08 support module"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/mite.c b/drivers/comedi/drivers/mite.c index e93150d6dc57..9098616900f7 100644 --- a/drivers/comedi/drivers/mite.c +++ b/drivers/comedi/drivers/mite.c @@ -921,17 +921,6 @@ void mite_detach(struct mite *mite) } EXPORT_SYMBOL_GPL(mite_detach); -static int __init mite_module_init(void) -{ - return 0; -} -module_init(mite_module_init); - -static void __exit mite_module_exit(void) -{ -} -module_exit(mite_module_exit); - MODULE_AUTHOR("Comedi https://www.comedi.org"); MODULE_DESCRIPTION("Comedi helper for NI Mite PCI interface chip"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/ni_labpc_common.c b/drivers/comedi/drivers/ni_labpc_common.c index 7e0ce0ce0adf..21b838a9d9ec 100644 --- a/drivers/comedi/drivers/ni_labpc_common.c +++ b/drivers/comedi/drivers/ni_labpc_common.c @@ -1357,17 +1357,6 @@ void labpc_common_detach(struct comedi_device *dev) } EXPORT_SYMBOL_GPL(labpc_common_detach); -static int __init labpc_common_init(void) -{ - return 0; -} -module_init(labpc_common_init); - -static void __exit labpc_common_exit(void) -{ -} -module_exit(labpc_common_exit); - MODULE_AUTHOR("Comedi https://www.comedi.org"); MODULE_DESCRIPTION("Comedi helper for ni_labpc, ni_labpc_pci, ni_labpc_cs"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/ni_labpc_isadma.c b/drivers/comedi/drivers/ni_labpc_isadma.c index 0652ca8345b6..125797f655f2 100644 --- a/drivers/comedi/drivers/ni_labpc_isadma.c +++ b/drivers/comedi/drivers/ni_labpc_isadma.c @@ -164,17 +164,6 @@ void labpc_free_dma_chan(struct comedi_device *dev) } EXPORT_SYMBOL_GPL(labpc_free_dma_chan); -static int __init ni_labpc_isadma_init_module(void) -{ - return 0; -} -module_init(ni_labpc_isadma_init_module); - -static void __exit ni_labpc_isadma_cleanup_module(void) -{ -} -module_exit(ni_labpc_isadma_cleanup_module); - MODULE_AUTHOR("Comedi https://www.comedi.org"); MODULE_DESCRIPTION("Comedi NI Lab-PC ISA DMA support"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/ni_tio.c b/drivers/comedi/drivers/ni_tio.c index 519359a870a7..c3a909abcfa7 100644 --- a/drivers/comedi/drivers/ni_tio.c +++ b/drivers/comedi/drivers/ni_tio.c @@ -1825,17 +1825,6 @@ void ni_gpct_device_destroy(struct ni_gpct_device *counter_dev) } EXPORT_SYMBOL_GPL(ni_gpct_device_destroy); -static int __init ni_tio_init_module(void) -{ - return 0; -} -module_init(ni_tio_init_module); - -static void __exit ni_tio_cleanup_module(void) -{ -} -module_exit(ni_tio_cleanup_module); - MODULE_AUTHOR("Comedi "); MODULE_DESCRIPTION("Comedi support for NI general-purpose counters"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/ni_tiocmd.c b/drivers/comedi/drivers/ni_tiocmd.c index ab6d9e8269f3..9c166703626b 100644 --- a/drivers/comedi/drivers/ni_tiocmd.c +++ b/drivers/comedi/drivers/ni_tiocmd.c @@ -494,17 +494,6 @@ void ni_tio_set_mite_channel(struct ni_gpct *counter, } EXPORT_SYMBOL_GPL(ni_tio_set_mite_channel); -static int __init ni_tiocmd_init_module(void) -{ - return 0; -} -module_init(ni_tiocmd_init_module); - -static void __exit ni_tiocmd_cleanup_module(void) -{ -} -module_exit(ni_tiocmd_cleanup_module); - MODULE_AUTHOR("Comedi "); MODULE_DESCRIPTION("Comedi command support for NI general-purpose counters"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/kcomedilib/kcomedilib_main.c b/drivers/comedi/kcomedilib/kcomedilib_main.c index baa9eaaf97d4..517e60ffd81b 100644 --- a/drivers/comedi/kcomedilib/kcomedilib_main.c +++ b/drivers/comedi/kcomedilib/kcomedilib_main.c @@ -351,15 +351,3 @@ int comedi_get_n_channels(struct comedi_device *dev, unsigned int subdevice) return n; } EXPORT_SYMBOL_GPL(comedi_get_n_channels); - -static int __init kcomedilib_module_init(void) -{ - return 0; -} - -static void __exit kcomedilib_module_exit(void) -{ -} - -module_init(kcomedilib_module_init); -module_exit(kcomedilib_module_exit); From 6c561848ee5246f083770aaf39b2666f940d60dd Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 11 Mar 2026 16:24:59 -0700 Subject: [PATCH 351/405] comedi: isadma: use kzalloc_flex Switched struct pointer member to a flexible array member to get rid of kzalloc_objs as there's no need for them to be separately allocated. AAdded __counted_by for extra runtime analysis. Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260311232459.18407-1-rosenp@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/comedi_isadma.c | 21 +++++++-------------- include/linux/comedi/comedi_isadma.h | 2 +- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/comedi/drivers/comedi_isadma.c b/drivers/comedi/drivers/comedi_isadma.c index f480640413f0..b346079b9b6b 100644 --- a/drivers/comedi/drivers/comedi_isadma.c +++ b/drivers/comedi/drivers/comedi_isadma.c @@ -161,14 +161,10 @@ struct comedi_isadma *comedi_isadma_alloc(struct comedi_device *dev, if (n_desc < 1 || n_desc > 2) goto no_dma; - dma = kzalloc_obj(*dma); + dma = kzalloc_flex(*dma, desc, n_desc); if (!dma) goto no_dma; - desc = kzalloc_objs(*desc, n_desc); - if (!desc) - goto no_dma; - dma->desc = desc; dma->n_desc = n_desc; if (dev->hw_dev) { dma->dev = dev->hw_dev; @@ -231,15 +227,12 @@ void comedi_isadma_free(struct comedi_isadma *dma) if (!dma) return; - if (dma->desc) { - for (i = 0; i < dma->n_desc; i++) { - desc = &dma->desc[i]; - if (desc->virt_addr) - dma_free_coherent(dma->dev, desc->maxsize, - desc->virt_addr, - desc->hw_addr); - } - kfree(dma->desc); + for (i = 0; i < dma->n_desc; i++) { + desc = &dma->desc[i]; + if (desc->virt_addr) + dma_free_coherent(dma->dev, desc->maxsize, + desc->virt_addr, + desc->hw_addr); } if (dma->chan2 && dma->chan2 != dma->chan) free_dma(dma->chan2); diff --git a/include/linux/comedi/comedi_isadma.h b/include/linux/comedi/comedi_isadma.h index 9d2b12db7e6e..7514ce222fa6 100644 --- a/include/linux/comedi/comedi_isadma.h +++ b/include/linux/comedi/comedi_isadma.h @@ -48,11 +48,11 @@ struct comedi_isadma_desc { */ struct comedi_isadma { struct device *dev; - struct comedi_isadma_desc *desc; int n_desc; int cur_dma; unsigned int chan; unsigned int chan2; + struct comedi_isadma_desc desc[] __counted_by(n_desc); }; #if IS_ENABLED(CONFIG_ISA_DMA_API) From 41837c1deaa1c5f4adc02ecbd77fe6e3adb7150c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 11:46:46 +0200 Subject: [PATCH 352/405] comedi: ni_usb6501: refactor endpoint lookup Use the common USB helper for looking up bulk and interrupt endpoints instead of open coding. Signed-off-by: Johan Hovold Reviewed-by: Ian Abbott Link: https://patch.msgid.link/20260330094646.1623523-1-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/ni_usb6501.c | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/drivers/comedi/drivers/ni_usb6501.c b/drivers/comedi/drivers/ni_usb6501.c index 0dd9edf7bced..7afc5661a5ec 100644 --- a/drivers/comedi/drivers/ni_usb6501.c +++ b/drivers/comedi/drivers/ni_usb6501.c @@ -477,31 +477,16 @@ static int ni6501_find_endpoints(struct comedi_device *dev) struct usb_interface *intf = comedi_to_usb_interface(dev); struct ni6501_private *devpriv = dev->private; struct usb_host_interface *iface_desc = intf->cur_altsetting; - struct usb_endpoint_descriptor *ep_desc; - int i; + int ret; if (iface_desc->desc.bNumEndpoints != 2) { dev_err(dev->class_dev, "Wrong number of endpoints\n"); return -ENODEV; } - for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { - ep_desc = &iface_desc->endpoint[i].desc; - - if (usb_endpoint_is_bulk_in(ep_desc)) { - if (!devpriv->ep_rx) - devpriv->ep_rx = ep_desc; - continue; - } - - if (usb_endpoint_is_bulk_out(ep_desc)) { - if (!devpriv->ep_tx) - devpriv->ep_tx = ep_desc; - continue; - } - } - - if (!devpriv->ep_rx || !devpriv->ep_tx) + ret = usb_find_common_endpoints(iface_desc, &devpriv->ep_rx, + &devpriv->ep_tx, NULL, NULL); + if (ret) return -ENODEV; if (usb_endpoint_maxp(devpriv->ep_rx) < RX_MAX_SIZE) From 8ca3d3b1c3383f5f23efe01c3fd9113ed06007bd Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 16 Mar 2026 20:14:58 -0700 Subject: [PATCH 353/405] greybus: svc: use kzalloc_flex Avoid manual sizeof math by using the proper helper. Also use struct_size for the buffer size. Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260317031458.93315-1-rosenp@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/greybus/svc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/greybus/svc.c b/drivers/greybus/svc.c index 1b854f53f21e..490577731a19 100644 --- a/drivers/greybus/svc.c +++ b/drivers/greybus/svc.c @@ -775,10 +775,9 @@ static void gb_svc_pwrmon_debugfs_init(struct gb_svc *svc) if (!rail_count || rail_count > GB_SVC_PWRMON_MAX_RAIL_COUNT) goto err_pwrmon_debugfs; - bufsize = sizeof(*rail_names) + - GB_SVC_PWRMON_RAIL_NAME_BUFSIZE * rail_count; + bufsize = struct_size(rail_names, name, rail_count); - rail_names = kzalloc(bufsize, GFP_KERNEL); + rail_names = kzalloc_flex(*rail_names, name, rail_count); if (!rail_names) goto err_pwrmon_debugfs; From cbc96a916b1a3be7039b0166c0fc56ec1632ba01 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sun, 22 Mar 2026 11:19:23 +0800 Subject: [PATCH 354/405] greybus: beagleplay: bound bootloader RX buffer copy When `flashing_mode` is set, `gb_tty_receive()` routes incoming bytes to `cc1352_bootloader_rx()`. That helper appends the new bytes to the shared `rx_buffer` with `memcpy()` but does not check that the chunk fits in the remaining space first. The normal HDLC receive path already enforces `MAX_RX_HDLC`, so do the same here before appending bootloader data. If a packet would overflow the receive buffer, drop it and reset the bootloader receive state instead of copying past the end of `rx_buffer`. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260322031923.58013-1-pengpeng@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/greybus/gb-beagleplay.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/greybus/gb-beagleplay.c b/drivers/greybus/gb-beagleplay.c index 87186f891a6a..bca3132adacd 100644 --- a/drivers/greybus/gb-beagleplay.c +++ b/drivers/greybus/gb-beagleplay.c @@ -535,6 +535,12 @@ static size_t cc1352_bootloader_rx(struct gb_beagleplay *bg, const u8 *data, int ret; size_t off = 0; + if (count > sizeof(bg->rx_buffer) - bg->rx_buffer_len) { + dev_err_ratelimited(&bg->sd->dev, "Bootloader RX buffer overflow"); + bg->rx_buffer_len = 0; + return count; + } + memcpy(bg->rx_buffer + bg->rx_buffer_len, data, count); bg->rx_buffer_len += count; From 6597a08dbd825fadf0da2e2552358dd95776c8dd Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 11 Mar 2026 09:22:26 +0100 Subject: [PATCH 355/405] greybus: es2: drop redundant device reference Driver core holds a reference to the USB interface and its parent USB device while the interface is bound to a driver and there is no need to take additional references unless the structures are needed after disconnect. Drop the redundant device reference to reduce cargo culting, make it easier to spot drivers where an extra reference is needed, and reduce the risk of memory leaks when drivers fail to release it. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260311082226.14865-1-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/greybus/es2.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/greybus/es2.c b/drivers/greybus/es2.c index 6ae0ac828afa..75b889fa21b4 100644 --- a/drivers/greybus/es2.c +++ b/drivers/greybus/es2.c @@ -772,7 +772,6 @@ static int check_urb_status(struct urb *urb) static void es2_destroy(struct es2_ap_dev *es2) { - struct usb_device *udev; struct urb *urb; int i; @@ -804,10 +803,7 @@ static void es2_destroy(struct es2_ap_dev *es2) gb_hd_cport_release_reserved(es2->hd, ES2_CPORT_CDSI1); gb_hd_cport_release_reserved(es2->hd, ES2_CPORT_CDSI0); - udev = es2->usb_dev; gb_hd_put(es2->hd); - - usb_put_dev(udev); } static void cport_in_callback(struct urb *urb) @@ -1257,11 +1253,10 @@ static int ap_probe(struct usb_interface *interface, bool bulk_in_found = false; bool arpc_in_found = false; - udev = usb_get_dev(interface_to_usbdev(interface)); + udev = interface_to_usbdev(interface); num_cports = apb_get_cport_count(udev); if (num_cports < 0) { - usb_put_dev(udev); dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n", num_cports); return num_cports; @@ -1269,10 +1264,8 @@ static int ap_probe(struct usb_interface *interface, hd = gb_hd_create(&es2_driver, &udev->dev, ES2_GBUF_MSG_SIZE_MAX, num_cports); - if (IS_ERR(hd)) { - usb_put_dev(udev); + if (IS_ERR(hd)) return PTR_ERR(hd); - } es2 = hd_to_es2(hd); es2->hd = hd; From 6b526dca0966f2370835765019a54319b78fca8d Mon Sep 17 00:00:00 2001 From: Weigang He Date: Mon, 30 Mar 2026 12:08:00 +0000 Subject: [PATCH 356/405] greybus: gb-beagleplay: fix sleep in atomic context in hdlc_tx_frames() hdlc_append() calls usleep_range() to wait for circular buffer space, but it is called with tx_producer_lock (a spinlock) held via hdlc_tx_frames() -> hdlc_append_tx_frame()/hdlc_append_tx_u8()/etc. Sleeping while holding a spinlock is illegal and can trigger "BUG: scheduling while atomic". Fix this by moving the buffer-space wait out of hdlc_append() and into hdlc_tx_frames(), before the spinlock is acquired. The new flow: 1. Pre-calculate the worst-case encoded frame length. 2. Wait (with sleep) outside the lock until enough space is available, kicking the TX consumer work to drain the buffer. 3. Acquire the spinlock, re-verify space, and write the entire frame atomically. This ensures that sleeping only happens without any lock held, and that frames are either fully enqueued or not written at all. This bug is found by CodeQL static analysis tool (interprocedural sleep-in-atomic query) and my code review. Fixes: ec558bbfea67 ("greybus: Add BeaglePlay Linux Driver") Cc: stable Cc: Ayush Singh Cc: Johan Hovold Cc: Alex Elder Cc: Greg Kroah-Hartman Signed-off-by: Weigang He Link: https://patch.msgid.link/20260330120801.981506-1-geoffreyhe2@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/greybus/gb-beagleplay.c | 105 +++++++++++++++++++++++++++----- 1 file changed, 89 insertions(+), 16 deletions(-) diff --git a/drivers/greybus/gb-beagleplay.c b/drivers/greybus/gb-beagleplay.c index bca3132adacd..79ae3ef8698e 100644 --- a/drivers/greybus/gb-beagleplay.c +++ b/drivers/greybus/gb-beagleplay.c @@ -242,30 +242,26 @@ static void hdlc_write(struct gb_beagleplay *bg) } /** - * hdlc_append() - Queue HDLC data for sending. + * hdlc_append() - Queue a single HDLC byte for sending. * @bg: beagleplay greybus driver * @value: hdlc byte to transmit * - * Assumes that producer lock as been acquired. + * Caller must hold tx_producer_lock and must have ensured sufficient + * space in the circular buffer before calling (see hdlc_tx_frames()). */ static void hdlc_append(struct gb_beagleplay *bg, u8 value) { - int tail, head = bg->tx_circ_buf.head; + int head = bg->tx_circ_buf.head; + int tail = READ_ONCE(bg->tx_circ_buf.tail); - while (true) { - tail = READ_ONCE(bg->tx_circ_buf.tail); + lockdep_assert_held(&bg->tx_producer_lock); + if (WARN_ON_ONCE(CIRC_SPACE(head, tail, TX_CIRC_BUF_SIZE) < 1)) + return; - if (CIRC_SPACE(head, tail, TX_CIRC_BUF_SIZE) >= 1) { - bg->tx_circ_buf.buf[head] = value; - - /* Finish producing HDLC byte */ - smp_store_release(&bg->tx_circ_buf.head, - (head + 1) & (TX_CIRC_BUF_SIZE - 1)); - return; - } - dev_warn(&bg->sd->dev, "Tx circ buf full"); - usleep_range(3000, 5000); - } + bg->tx_circ_buf.buf[head] = value; + /* Ensure buffer write is visible before advancing head. */ + smp_store_release(&bg->tx_circ_buf.head, + (head + 1) & (TX_CIRC_BUF_SIZE - 1)); } static void hdlc_append_escaped(struct gb_beagleplay *bg, u8 value) @@ -313,13 +309,90 @@ static void hdlc_transmit(struct work_struct *work) spin_unlock_bh(&bg->tx_consumer_lock); } +/** + * hdlc_encoded_length() - Calculate worst-case encoded length of an HDLC frame. + * @payloads: array of payload buffers + * @count: number of payloads + * + * Returns the maximum number of bytes needed in the circular buffer. + */ +static size_t hdlc_encoded_length(const struct hdlc_payload payloads[], + size_t count) +{ + size_t i, payload_len = 0; + + for (i = 0; i < count; i++) + payload_len += payloads[i].len; + + /* + * Worst case: every data byte needs escaping (doubles in size). + * data bytes = address(1) + control(1) + payload + crc(2) + * framing = opening flag(1) + closing flag(1) + */ + return 2 + (1 + 1 + payload_len + 2) * 2; +} + +#define HDLC_TX_BUF_WAIT_RETRIES 500 +#define HDLC_TX_BUF_WAIT_US_MIN 3000 +#define HDLC_TX_BUF_WAIT_US_MAX 5000 + +/** + * hdlc_tx_frames() - Encode and queue an HDLC frame for transmission. + * @bg: beagleplay greybus driver + * @address: HDLC address field + * @control: HDLC control field + * @payloads: array of payload buffers + * @count: number of payloads + * + * Sleeps outside the spinlock until enough circular-buffer space is + * available, then verifies space under the lock and writes the entire + * frame atomically. Either a complete frame is enqueued or nothing is + * written, avoiding both sleeping in atomic context and partial frames. + */ static void hdlc_tx_frames(struct gb_beagleplay *bg, u8 address, u8 control, const struct hdlc_payload payloads[], size_t count) { + size_t needed = hdlc_encoded_length(payloads, count); + int retries = HDLC_TX_BUF_WAIT_RETRIES; size_t i; + int head, tail; + + /* Wait outside the lock for sufficient buffer space. */ + while (retries--) { + /* Pairs with smp_store_release() in hdlc_append(). */ + head = smp_load_acquire(&bg->tx_circ_buf.head); + tail = READ_ONCE(bg->tx_circ_buf.tail); + + if (CIRC_SPACE(head, tail, TX_CIRC_BUF_SIZE) >= needed) + break; + + /* Kick the consumer and sleep — no lock held. */ + schedule_work(&bg->tx_work); + usleep_range(HDLC_TX_BUF_WAIT_US_MIN, HDLC_TX_BUF_WAIT_US_MAX); + } + + if (retries < 0) { + dev_warn_ratelimited(&bg->sd->dev, + "Tx circ buf full, dropping frame\n"); + return; + } spin_lock(&bg->tx_producer_lock); + /* + * Re-check under the lock. Should not fail since + * tx_producer_lock serialises all producers and the + * consumer only frees space, but guard against it. + */ + head = bg->tx_circ_buf.head; + tail = READ_ONCE(bg->tx_circ_buf.tail); + if (unlikely(CIRC_SPACE(head, tail, TX_CIRC_BUF_SIZE) < needed)) { + spin_unlock(&bg->tx_producer_lock); + dev_warn_ratelimited(&bg->sd->dev, + "Tx circ buf space lost, dropping frame\n"); + return; + } + hdlc_append_tx_frame(bg); hdlc_append_tx_u8(bg, address); hdlc_append_tx_u8(bg, control); From 58fa2357f5b5eb3a394571dd2fee6c6a1db242c3 Mon Sep 17 00:00:00 2001 From: Weigang He Date: Mon, 30 Mar 2026 12:08:01 +0000 Subject: [PATCH 357/405] greybus: gb-beagleplay: propagate hdlc_tx_frames() errors to callers Now that hdlc_tx_frames() can drop frames when the circular buffer is full, make the failure visible to callers: - Change hdlc_tx_frames() return type from void to int (-EAGAIN on buffer full). - Change gb_beagleplay_start_svc() / gb_beagleplay_stop_svc() to return int so probe and firmware-upload paths can detect failures. - gb_message_send(): propagate the error so the greybus core can handle the transport failure. - hdlc_tx_s_frame_ack(): log with dev_warn_ratelimited on failure (ACK loss is recoverable by HDLC retransmission). - Probe path: propagate start_svc failure via new free_greybus label. - Firmware upload paths: return FW_UPLOAD_ERR_RW_ERROR when SVC restart fails instead of silently continuing. - Remove path: best-effort stop_svc, ignore failure. Cc: Ayush Singh Cc: Johan Hovold Cc: Alex Elder Cc: Greg Kroah-Hartman Signed-off-by: Weigang He Link: https://patch.msgid.link/20260330120801.981506-2-geoffreyhe2@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/greybus/gb-beagleplay.c | 64 ++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/drivers/greybus/gb-beagleplay.c b/drivers/greybus/gb-beagleplay.c index 79ae3ef8698e..305066febbe7 100644 --- a/drivers/greybus/gb-beagleplay.c +++ b/drivers/greybus/gb-beagleplay.c @@ -314,6 +314,9 @@ static void hdlc_transmit(struct work_struct *work) * @payloads: array of payload buffers * @count: number of payloads * + * Every data byte may need HDLC escaping (doubling its size). + * Frame layout: flag(1) + address(1-2) + control(1-2) + payload + CRC(2-4) + flag(1). + * * Returns the maximum number of bytes needed in the circular buffer. */ static size_t hdlc_encoded_length(const struct hdlc_payload payloads[], @@ -348,8 +351,10 @@ static size_t hdlc_encoded_length(const struct hdlc_payload payloads[], * available, then verifies space under the lock and writes the entire * frame atomically. Either a complete frame is enqueued or nothing is * written, avoiding both sleeping in atomic context and partial frames. + * + * Returns 0 on success, -EAGAIN if the buffer remains full after retries. */ -static void hdlc_tx_frames(struct gb_beagleplay *bg, u8 address, u8 control, +static int hdlc_tx_frames(struct gb_beagleplay *bg, u8 address, u8 control, const struct hdlc_payload payloads[], size_t count) { size_t needed = hdlc_encoded_length(payloads, count); @@ -372,25 +377,24 @@ static void hdlc_tx_frames(struct gb_beagleplay *bg, u8 address, u8 control, } if (retries < 0) { - dev_warn_ratelimited(&bg->sd->dev, - "Tx circ buf full, dropping frame\n"); - return; + dev_warn_ratelimited(&bg->sd->dev, "Tx circ buf full, dropping frame\n"); + return -EAGAIN; } spin_lock(&bg->tx_producer_lock); /* - * Re-check under the lock. Should not fail since - * tx_producer_lock serialises all producers and the - * consumer only frees space, but guard against it. + * Re-check space under the lock to close the TOCTOU window. + * This should be rare since tx_producer_lock serialises all + * producers and the consumer only frees space. If it fires, + * the caller is expected to handle -EAGAIN (retry or report). */ head = bg->tx_circ_buf.head; tail = READ_ONCE(bg->tx_circ_buf.tail); if (unlikely(CIRC_SPACE(head, tail, TX_CIRC_BUF_SIZE) < needed)) { spin_unlock(&bg->tx_producer_lock); - dev_warn_ratelimited(&bg->sd->dev, - "Tx circ buf space lost, dropping frame\n"); - return; + dev_warn_ratelimited(&bg->sd->dev, "Tx circ buf space lost, dropping frame\n"); + return -EAGAIN; } hdlc_append_tx_frame(bg); @@ -406,11 +410,16 @@ static void hdlc_tx_frames(struct gb_beagleplay *bg, u8 address, u8 control, spin_unlock(&bg->tx_producer_lock); schedule_work(&bg->tx_work); + return 0; } static void hdlc_tx_s_frame_ack(struct gb_beagleplay *bg) { - hdlc_tx_frames(bg, bg->rx_buffer[0], (bg->rx_buffer[1] >> 1) & 0x7, NULL, 0); + int ret; + + ret = hdlc_tx_frames(bg, bg->rx_buffer[0], (bg->rx_buffer[1] >> 1) & 0x7, NULL, 0); + if (ret) + dev_warn_ratelimited(&bg->sd->dev, "Failed to send HDLC ACK: %d\n", ret); } static void hdlc_rx_frame(struct gb_beagleplay *bg) @@ -674,6 +683,7 @@ static int gb_message_send(struct gb_host_device *hd, u16 cport, struct gb_messa struct gb_beagleplay *bg = dev_get_drvdata(&hd->dev); struct hdlc_payload payloads[3]; __le16 cport_id = cpu_to_le16(cport); + int ret; dev_dbg(&hd->dev, "Sending greybus message with Operation %u, Type: %X on Cport %u", msg->header->operation_id, msg->header->type, cport); @@ -688,7 +698,10 @@ static int gb_message_send(struct gb_host_device *hd, u16 cport, struct gb_messa payloads[2].buf = msg->payload; payloads[2].len = msg->payload_size; - hdlc_tx_frames(bg, ADDRESS_GREYBUS, 0x03, payloads, 3); + ret = hdlc_tx_frames(bg, ADDRESS_GREYBUS, 0x03, payloads, 3); + if (ret) + return ret; + greybus_message_sent(bg->gb_hd, msg, 0); return 0; @@ -701,20 +714,20 @@ static void gb_message_cancel(struct gb_message *message) static struct gb_hd_driver gb_hdlc_driver = { .message_send = gb_message_send, .message_cancel = gb_message_cancel }; -static void gb_beagleplay_start_svc(struct gb_beagleplay *bg) +static int gb_beagleplay_start_svc(struct gb_beagleplay *bg) { const u8 command = CONTROL_SVC_START; const struct hdlc_payload payload = { .len = 1, .buf = (void *)&command }; - hdlc_tx_frames(bg, ADDRESS_CONTROL, 0x03, &payload, 1); + return hdlc_tx_frames(bg, ADDRESS_CONTROL, 0x03, &payload, 1); } -static void gb_beagleplay_stop_svc(struct gb_beagleplay *bg) +static int gb_beagleplay_stop_svc(struct gb_beagleplay *bg) { const u8 command = CONTROL_SVC_STOP; const struct hdlc_payload payload = { .len = 1, .buf = (void *)&command }; - hdlc_tx_frames(bg, ADDRESS_CONTROL, 0x03, &payload, 1); + return hdlc_tx_frames(bg, ADDRESS_CONTROL, 0x03, &payload, 1); } static int cc1352_bootloader_wait_for_ack(struct gb_beagleplay *bg) @@ -952,7 +965,9 @@ static enum fw_upload_err cc1352_prepare(struct fw_upload *fw_upload, gb_greybus_deinit(bg); msleep(5 * MSEC_PER_SEC); - gb_beagleplay_stop_svc(bg); + /* Best effort — device is entering bootloader mode regardless. */ + if (gb_beagleplay_stop_svc(bg)) + dev_warn(&bg->sd->dev, "Failed to send SVC stop before flashing\n"); msleep(200); flush_work(&bg->tx_work); @@ -994,7 +1009,9 @@ static enum fw_upload_err cc1352_prepare(struct fw_upload *fw_upload, if (gb_greybus_init(bg) < 0) return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_RW_ERROR, "Failed to initialize greybus"); - gb_beagleplay_start_svc(bg); + if (gb_beagleplay_start_svc(bg)) + return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_RW_ERROR, + "Failed to restart SVC after skip"); return FW_UPLOAD_ERR_FW_INVALID; } @@ -1075,7 +1092,9 @@ static enum fw_upload_err cc1352_poll_complete(struct fw_upload *fw_upload) return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_RW_ERROR, "Failed to initialize greybus"); - gb_beagleplay_start_svc(bg); + if (gb_beagleplay_start_svc(bg) < 0) + return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_RW_ERROR, + "Failed to start SVC"); return FW_UPLOAD_ERR_NONE; } @@ -1186,10 +1205,14 @@ static int gb_beagleplay_probe(struct serdev_device *serdev) if (ret) goto free_fw; - gb_beagleplay_start_svc(bg); + ret = gb_beagleplay_start_svc(bg); + if (ret) + goto free_greybus; return 0; +free_greybus: + gb_greybus_deinit(bg); free_fw: gb_fw_deinit(bg); free_hdlc: @@ -1205,6 +1228,7 @@ static void gb_beagleplay_remove(struct serdev_device *serdev) gb_fw_deinit(bg); gb_greybus_deinit(bg); + /* Best effort — device is being removed. */ gb_beagleplay_stop_svc(bg); hdlc_deinit(bg); gb_serdev_deinit(bg); From 21a8995cdbc7de4a57215a57095003d0e08ca1a3 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Tue, 31 Mar 2026 08:51:21 +0530 Subject: [PATCH 358/405] dt-bindings: misc: qcom,fastrpc: Add compatible for Glymur Document compatible for Qualcomm Glymur fastrpc which is fully compatible with Qualcomm Kaanapali fastrpc. Signed-off-by: Sibi Sankar Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260331032121.1279203-1-sibi.sankar@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/misc/qcom,fastrpc.yaml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml b/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml index d8e47db677cc..ca830dd06de2 100644 --- a/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml +++ b/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml @@ -18,9 +18,14 @@ description: | properties: compatible: - enum: - - qcom,kaanapali-fastrpc - - qcom,fastrpc + oneOf: + - enum: + - qcom,kaanapali-fastrpc + - qcom,fastrpc + - items: + - enum: + - qcom,glymur-fastrpc + - const: qcom,kaanapali-fastrpc label: enum: From 1214bf28965ceaf584fb20d357731264dd2e10e1 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 2 Apr 2026 13:40:16 +0800 Subject: [PATCH 359/405] greybus: gb-beagleplay: bound bootloader receive buffering cc1352_bootloader_rx() appends each serdev chunk into the fixed rx_buffer before parsing bootloader packets. The helper can keep leftover bytes between callbacks and may receive multiple packets in one callback, so a single count value is not constrained by one packet length. Check that the incoming chunk fits in the remaining receive buffer space before memcpy(). If it does not, drop the staged data and consume the bytes instead of overflowing rx_buffer. Fixes: 0cf7befa3ea2 ("greybus: gb-beagleplay: Add firmware upload API") Cc: stable Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260402054016.38587-1-pengpeng@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/greybus/gb-beagleplay.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/greybus/gb-beagleplay.c b/drivers/greybus/gb-beagleplay.c index 305066febbe7..244966d56c9b 100644 --- a/drivers/greybus/gb-beagleplay.c +++ b/drivers/greybus/gb-beagleplay.c @@ -623,6 +623,13 @@ static size_t cc1352_bootloader_rx(struct gb_beagleplay *bg, const u8 *data, return count; } + if (count > sizeof(bg->rx_buffer) - bg->rx_buffer_len) { + dev_warn(&bg->sd->dev, + "dropping oversized bootloader receive chunk"); + bg->rx_buffer_len = 0; + return count; + } + memcpy(bg->rx_buffer + bg->rx_buffer_len, data, count); bg->rx_buffer_len += count; From 58b140a67ae197b7cee4c768a05bc056dae74f36 Mon Sep 17 00:00:00 2001 From: Jingyi Wang Date: Fri, 27 Mar 2026 13:17:43 +0000 Subject: [PATCH 360/405] dt-bindings: nvmem: qfprom: Add Kaanapali compatible Document compatible string for the QFPROM on Kaanapali platform. Signed-off-by: Jingyi Wang Acked-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260327131751.3026030-2-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml index 839513d4b499..2ab047f2bb69 100644 --- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml +++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml @@ -26,6 +26,7 @@ properties: - qcom,ipq8064-qfprom - qcom,ipq8074-qfprom - qcom,ipq9574-qfprom + - qcom,kaanapali-qfprom - qcom,msm8226-qfprom - qcom,msm8916-qfprom - qcom,msm8917-qfprom From 4e012d4cb6d5bac43fc3e188b02dda42384ea89e Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 27 Mar 2026 13:17:44 +0000 Subject: [PATCH 361/405] nvmem: qnap-mcu-eeprom: Fix struct assignments using commas instead of semicolons The nvcfg struct member assignments were incorrectly using commas instead of semicolons. Signed-off-by: Felix Gu Reviewed-by: Heiko Stuebner Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260327131751.3026030-3-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/qnap-mcu-eeprom.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/nvmem/qnap-mcu-eeprom.c b/drivers/nvmem/qnap-mcu-eeprom.c index 0b919895b3b2..07bdaa2a33fa 100644 --- a/drivers/nvmem/qnap-mcu-eeprom.c +++ b/drivers/nvmem/qnap-mcu-eeprom.c @@ -86,10 +86,10 @@ static int qnap_mcu_eeprom_probe(struct platform_device *pdev) nvcfg.read_only = true; nvcfg.root_only = false; nvcfg.reg_read = qnap_mcu_eeprom_read; - nvcfg.size = QNAP_MCU_EEPROM_SIZE, - nvcfg.word_size = 1, - nvcfg.stride = 1, - nvcfg.priv = mcu, + nvcfg.size = QNAP_MCU_EEPROM_SIZE; + nvcfg.word_size = 1; + nvcfg.stride = 1; + nvcfg.priv = mcu; ndev = devm_nvmem_register(&pdev->dev, &nvcfg); if (IS_ERR(ndev)) From 63aad6176d644c733d77f390b87fafd4839e056b Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Fri, 27 Mar 2026 13:17:45 +0000 Subject: [PATCH 362/405] dt-bindings: nvmem: sl28cpld: Drop sa67mcu compatible I was just informed that this product is discontinued (without being ever released to the market). Pull the plug and let's not waste any more maintainers time and revert commit 4a9b344e90c7 ("dt-bindings: nvmem: sl28cpld: add sa67mcu compatible"). Acked-by: Conor Dooley Signed-off-by: Michael Walle Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260327131751.3026030-4-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- .../bindings/nvmem/layouts/kontron,sl28-vpd.yaml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/nvmem/layouts/kontron,sl28-vpd.yaml b/Documentation/devicetree/bindings/nvmem/layouts/kontron,sl28-vpd.yaml index afd1919c6b1c..c713e23819f1 100644 --- a/Documentation/devicetree/bindings/nvmem/layouts/kontron,sl28-vpd.yaml +++ b/Documentation/devicetree/bindings/nvmem/layouts/kontron,sl28-vpd.yaml @@ -19,12 +19,7 @@ select: false properties: compatible: - oneOf: - - items: - - enum: - - kontron,sa67-vpd - - const: kontron,sl28-vpd - - const: kontron,sl28-vpd + const: kontron,sl28-vpd serial-number: type: object From 18cd01d24fb21fc601f9f9102d28379630db716d Mon Sep 17 00:00:00 2001 From: Kever Yang Date: Fri, 27 Mar 2026 13:17:46 +0000 Subject: [PATCH 363/405] dt-bindings: nvmem: rockchip,otp: Add support for RK3562 and RK3568 Add compatible entry for the otp controller in RK3562 and RK3568, add schema for different clock names for new entry. Signed-off-by: Kever Yang Reviewed-by: Rob Herring (Arm) Signed-off-by: Heiko Stuebner Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260327131751.3026030-5-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- .../bindings/nvmem/rockchip,otp.yaml | 58 +++++++++++++++---- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/nvmem/rockchip,otp.yaml b/Documentation/devicetree/bindings/nvmem/rockchip,otp.yaml index dc89020b0950..e90136f7dcfb 100644 --- a/Documentation/devicetree/bindings/nvmem/rockchip,otp.yaml +++ b/Documentation/devicetree/bindings/nvmem/rockchip,otp.yaml @@ -14,6 +14,8 @@ properties: enum: - rockchip,px30-otp - rockchip,rk3308-otp + - rockchip,rk3562-otp + - rockchip,rk3568-otp - rockchip,rk3576-otp - rockchip,rk3588-otp @@ -26,19 +28,15 @@ properties: clock-names: minItems: 3 - items: - - const: otp - - const: apb_pclk - - const: phy - - const: arb + maxItems: 4 resets: minItems: 1 - maxItems: 3 + maxItems: 4 reset-names: minItems: 1 - maxItems: 3 + maxItems: 4 required: - compatible @@ -64,13 +62,44 @@ allOf: clocks: maxItems: 3 clock-names: - maxItems: 3 + items: + - const: otp + - const: apb_pclk + - const: phy resets: maxItems: 1 reset-names: items: - const: phy + - if: + properties: + compatible: + contains: + enum: + - rockchip,rk3562-otp + - rockchip,rk3568-otp + then: + properties: + clocks: + minItems: 4 + maxItems: 4 + clock-names: + items: + - const: otp + - const: apb_pclk + - const: phy + - const: sbpi + resets: + minItems: 4 + maxItems: 4 + reset-names: + items: + - const: otp + - const: apb + - const: phy + - const: sbpi + - if: properties: compatible: @@ -82,7 +111,10 @@ allOf: clocks: maxItems: 3 clock-names: - maxItems: 3 + items: + - const: otp + - const: apb_pclk + - const: phy resets: minItems: 2 maxItems: 2 @@ -101,10 +133,16 @@ allOf: properties: clocks: minItems: 4 + maxItems: 4 clock-names: - minItems: 4 + items: + - const: otp + - const: apb_pclk + - const: phy + - const: arb resets: minItems: 3 + maxItems: 3 reset-names: items: - const: otp From c5bc6d815c8d733bc8d915ffaf6fc9c248865216 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Fri, 27 Mar 2026 13:17:47 +0000 Subject: [PATCH 364/405] dt-bindings: nvmem: rockchip,otp: Add compatible for RK3528 Add compatible string for the OTP controller in RK3528. Compared to the RK3562 and RK3568 the OTP in RK3528 does not have a phy clock or reset. Signed-off-by: Jonas Karlman Signed-off-by: Heiko Stuebner Reviewed-by: Rob Herring (Arm) Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260327131751.3026030-6-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- .../bindings/nvmem/rockchip,otp.yaml | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Documentation/devicetree/bindings/nvmem/rockchip,otp.yaml b/Documentation/devicetree/bindings/nvmem/rockchip,otp.yaml index e90136f7dcfb..7e4d5e1c4ced 100644 --- a/Documentation/devicetree/bindings/nvmem/rockchip,otp.yaml +++ b/Documentation/devicetree/bindings/nvmem/rockchip,otp.yaml @@ -14,6 +14,7 @@ properties: enum: - rockchip,px30-otp - rockchip,rk3308-otp + - rockchip,rk3528-otp - rockchip,rk3562-otp - rockchip,rk3568-otp - rockchip,rk3576-otp @@ -72,6 +73,30 @@ allOf: items: - const: phy + - if: + properties: + compatible: + contains: + enum: + - rockchip,rk3528-otp + then: + properties: + clocks: + maxItems: 3 + clock-names: + items: + - const: otp + - const: apb_pclk + - const: sbpi + resets: + minItems: 3 + maxItems: 3 + reset-names: + items: + - const: otp + - const: apb + - const: sbpi + - if: properties: compatible: From 6c403594354d59fb288978c5a23008da89ba2741 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Fri, 27 Mar 2026 13:17:48 +0000 Subject: [PATCH 365/405] nvmem: rockchip-otp: Handle internal word_size in main reg_read op Rockchip SoCs RK3576 and RK3588 read data from the OTP using 32-bit words instead of normal 8-bit bytes. Similar RK3506, RK3528, RK3562 and RK3568 will read data from OTP using 16-bit words. The nvmem core stride and word_size cannot fully be used as cells is not always aligned. Continue to report a stride=1 and word_size=1 in nvmem_config and instead handle use of SoC specific word_size internally in the driver. Move current SoC specific word_size handling from the RK3588 read_reg operation to the main read_reg operation to help simplify the SoC specific read_reg operation and allow code reuse in a future RK3568 reg_read operation. Signed-off-by: Jonas Karlman Tested-by: Willy Tarreau Signed-off-by: Heiko Stuebner Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260327131751.3026030-7-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/rockchip-otp.c | 72 ++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/drivers/nvmem/rockchip-otp.c b/drivers/nvmem/rockchip-otp.c index d88f12c53242..45bbb6147fb7 100644 --- a/drivers/nvmem/rockchip-otp.c +++ b/drivers/nvmem/rockchip-otp.c @@ -59,7 +59,6 @@ #define RK3588_OTPC_AUTO_EN 0x08 #define RK3588_OTPC_INT_ST 0x84 #define RK3588_OTPC_DOUT0 0x20 -#define RK3588_NBYTES 4 #define RK3588_BURST_NUM 1 #define RK3588_BURST_SHIFT 8 #define RK3588_ADDR_SHIFT 16 @@ -69,6 +68,7 @@ struct rockchip_data { int size; int read_offset; + int word_size; const char * const *clks; int num_clks; nvmem_reg_read_t reg_read; @@ -185,48 +185,28 @@ static int px30_otp_read(void *context, unsigned int offset, } static int rk3588_otp_read(void *context, unsigned int offset, - void *val, size_t bytes) + void *val, size_t count) { struct rockchip_otp *otp = context; - unsigned int addr_start, addr_end, addr_len; - int ret, i = 0; - u32 data; - u8 *buf; + u32 *buf = val; + int ret; - addr_start = round_down(offset, RK3588_NBYTES) / RK3588_NBYTES; - addr_end = round_up(offset + bytes, RK3588_NBYTES) / RK3588_NBYTES; - addr_len = addr_end - addr_start; - addr_start += otp->data->read_offset / RK3588_NBYTES; - - buf = kzalloc(array_size(addr_len, RK3588_NBYTES), GFP_KERNEL); - if (!buf) - return -ENOMEM; - - while (addr_len--) { - writel((addr_start << RK3588_ADDR_SHIFT) | + while (count--) { + writel((offset++ << RK3588_ADDR_SHIFT) | (RK3588_BURST_NUM << RK3588_BURST_SHIFT), otp->base + RK3588_OTPC_AUTO_CTRL); writel(RK3588_AUTO_EN, otp->base + RK3588_OTPC_AUTO_EN); ret = rockchip_otp_wait_status(otp, RK3588_OTPC_INT_ST, RK3588_RD_DONE); - if (ret < 0) { + if (ret) { dev_err(otp->dev, "timeout during read setup\n"); - goto read_end; + return ret; } - data = readl(otp->base + RK3588_OTPC_DOUT0); - memcpy(&buf[i], &data, RK3588_NBYTES); - - i += RK3588_NBYTES; - addr_start++; + *buf++ = readl(otp->base + RK3588_OTPC_DOUT0); } - memcpy(val, buf + offset % RK3588_NBYTES, bytes); - -read_end: - kfree(buf); - return ret; } @@ -234,7 +214,7 @@ static int rockchip_otp_read(void *context, unsigned int offset, void *val, size_t bytes) { struct rockchip_otp *otp = context; - int ret; + int ret, word_size; if (!otp->data || !otp->data->reg_read) return -EINVAL; @@ -245,8 +225,34 @@ static int rockchip_otp_read(void *context, unsigned int offset, return ret; } - ret = otp->data->reg_read(context, offset, val, bytes); + offset += otp->data->read_offset; + word_size = otp->data->word_size; + if (word_size > 1) { + unsigned int addr_start, addr_end; + size_t count; + u8 *buf; + + addr_start = offset / word_size; + addr_end = DIV_ROUND_UP(offset + bytes, word_size); + count = addr_end - addr_start; + + buf = kzalloc(array_size(count, word_size), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto err; + } + + ret = otp->data->reg_read(context, addr_start, buf, count); + if (!ret) + memcpy(val, buf + (offset % word_size), bytes); + + kfree(buf); + } else { + ret = otp->data->reg_read(context, offset, val, bytes); + } + +err: clk_bulk_disable_unprepare(otp->data->num_clks, otp->clks); return ret; @@ -259,7 +265,7 @@ static struct nvmem_config otp_config = { .type = NVMEM_TYPE_OTP, .read_only = true, .stride = 1, - .word_size = 1, + .word_size = sizeof(u8), .reg_read = rockchip_otp_read, }; @@ -277,6 +283,7 @@ static const struct rockchip_data px30_data = { static const struct rockchip_data rk3576_data = { .size = 0x100, .read_offset = 0x700, + .word_size = sizeof(u32), .clks = px30_otp_clocks, .num_clks = ARRAY_SIZE(px30_otp_clocks), .reg_read = rk3588_otp_read, @@ -289,6 +296,7 @@ static const char * const rk3588_otp_clocks[] = { static const struct rockchip_data rk3588_data = { .size = 0x400, .read_offset = 0xc00, + .word_size = sizeof(u32), .clks = rk3588_otp_clocks, .num_clks = ARRAY_SIZE(rk3588_otp_clocks), .reg_read = rk3588_otp_read, From 902fa931a2095566de6be12c8153f7fa9b31ab5f Mon Sep 17 00:00:00 2001 From: Finley Xiao Date: Fri, 27 Mar 2026 13:17:49 +0000 Subject: [PATCH 366/405] nvmem: rockchip-otp: Add support for RK3568 This adds the necessary data for handling otp the rk3568. Signed-off-by: Finley Xiao Signed-off-by: Kever Yang Signed-off-by: Heiko Stuebner Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260327131751.3026030-8-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/rockchip-otp.c | 69 ++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/drivers/nvmem/rockchip-otp.c b/drivers/nvmem/rockchip-otp.c index 45bbb6147fb7..cfb69bc58869 100644 --- a/drivers/nvmem/rockchip-otp.c +++ b/drivers/nvmem/rockchip-otp.c @@ -27,6 +27,7 @@ #define OTPC_USER_CTRL 0x0100 #define OTPC_USER_ADDR 0x0104 #define OTPC_USER_ENABLE 0x0108 +#define OTPC_USER_QP 0x0120 #define OTPC_USER_Q 0x0124 #define OTPC_INT_STATUS 0x0304 #define OTPC_SBPI_CMD0_OFFSET 0x1000 @@ -184,6 +185,58 @@ static int px30_otp_read(void *context, unsigned int offset, return ret; } +static int rk3568_otp_read(void *context, unsigned int offset, void *val, + size_t count) +{ + struct rockchip_otp *otp = context; + u16 *buf = val; + u32 otp_qp; + int ret; + + ret = rockchip_otp_reset(otp); + if (ret) { + dev_err(otp->dev, "failed to reset otp phy\n"); + return ret; + } + + ret = rockchip_otp_ecc_enable(otp, true); + if (ret) { + dev_err(otp->dev, "rockchip_otp_ecc_enable err\n"); + return ret; + } + + writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); + udelay(5); + + while (count--) { + writel(offset++ | OTPC_USER_ADDR_MASK, + otp->base + OTPC_USER_ADDR); + writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK, + otp->base + OTPC_USER_ENABLE); + + ret = rockchip_otp_wait_status(otp, OTPC_INT_STATUS, + OTPC_USER_DONE); + if (ret) { + dev_err(otp->dev, "timeout during read setup\n"); + goto read_end; + } + + otp_qp = readl(otp->base + OTPC_USER_QP); + if (((otp_qp & 0xc0) == 0xc0) || (otp_qp & 0x20)) { + ret = -EIO; + dev_err(otp->dev, "ecc check error during read setup\n"); + goto read_end; + } + + *buf++ = readl(otp->base + OTPC_USER_Q); + } + +read_end: + writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); + + return ret; +} + static int rk3588_otp_read(void *context, unsigned int offset, void *val, size_t count) { @@ -280,6 +333,18 @@ static const struct rockchip_data px30_data = { .reg_read = px30_otp_read, }; +static const char * const rk3568_otp_clocks[] = { + "otp", "apb_pclk", "phy", "sbpi", +}; + +static const struct rockchip_data rk3568_data = { + .size = 0x80, + .word_size = sizeof(u16), + .clks = rk3568_otp_clocks, + .num_clks = ARRAY_SIZE(rk3568_otp_clocks), + .reg_read = rk3568_otp_read, +}; + static const struct rockchip_data rk3576_data = { .size = 0x100, .read_offset = 0x700, @@ -311,6 +376,10 @@ static const struct of_device_id rockchip_otp_match[] = { .compatible = "rockchip,rk3308-otp", .data = &px30_data, }, + { + .compatible = "rockchip,rk3568-otp", + .data = &rk3568_data, + }, { .compatible = "rockchip,rk3576-otp", .data = &rk3576_data, From 7efe11aace70faa2199bc42d8949cd289b2998da Mon Sep 17 00:00:00 2001 From: Finley Xiao Date: Fri, 27 Mar 2026 13:17:50 +0000 Subject: [PATCH 367/405] nvmem: rockchip-otp: Add support for RK3562 This adds the necessary data for handling otp on the rk3562. Signed-off-by: Finley Xiao Signed-off-by: Kever Yang Signed-off-by: Heiko Stuebner Tested-by: Willy Tarreau Reviewed-by: Heiko Stuebner Signed-off-by: Srinivas Kandagatla Signed-off-by: Jonas Karlman Link: https://patch.msgid.link/20260327131751.3026030-9-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/rockchip-otp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/nvmem/rockchip-otp.c b/drivers/nvmem/rockchip-otp.c index cfb69bc58869..62ce22d72586 100644 --- a/drivers/nvmem/rockchip-otp.c +++ b/drivers/nvmem/rockchip-otp.c @@ -376,6 +376,10 @@ static const struct of_device_id rockchip_otp_match[] = { .compatible = "rockchip,rk3308-otp", .data = &px30_data, }, + { + .compatible = "rockchip,rk3562-otp", + .data = &rk3568_data, + }, { .compatible = "rockchip,rk3568-otp", .data = &rk3568_data, From a255f352b0e0c06d4b91233f112ddd35eac89947 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Fri, 27 Mar 2026 13:17:51 +0000 Subject: [PATCH 368/405] nvmem: rockchip-otp: Add support for RK3528 Add support for the OTP controller in RK3528. The OTPC is similar to the OTPC in RK3562 and RK3568, exept for a missing phy clock and reset. Signed-off-by: Jonas Karlman Signed-off-by: Heiko Stuebner Tested-by: Willy Tarreau Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260327131751.3026030-10-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/rockchip-otp.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/nvmem/rockchip-otp.c b/drivers/nvmem/rockchip-otp.c index 62ce22d72586..0ec78b5e19e7 100644 --- a/drivers/nvmem/rockchip-otp.c +++ b/drivers/nvmem/rockchip-otp.c @@ -333,6 +333,18 @@ static const struct rockchip_data px30_data = { .reg_read = px30_otp_read, }; +static const char * const rk3528_otp_clocks[] = { + "otp", "apb_pclk", "sbpi", +}; + +static const struct rockchip_data rk3528_data = { + .size = 0x80, + .word_size = sizeof(u16), + .clks = rk3528_otp_clocks, + .num_clks = ARRAY_SIZE(rk3528_otp_clocks), + .reg_read = rk3568_otp_read, +}; + static const char * const rk3568_otp_clocks[] = { "otp", "apb_pclk", "phy", "sbpi", }; @@ -376,6 +388,10 @@ static const struct of_device_id rockchip_otp_match[] = { .compatible = "rockchip,rk3308-otp", .data = &px30_data, }, + { + .compatible = "rockchip,rk3528-otp", + .data = &rk3528_data, + }, { .compatible = "rockchip,rk3562-otp", .data = &rk3568_data, From dd3a68a74b29af20e63c1de98f3f175dc1aa7854 Mon Sep 17 00:00:00 2001 From: Andrew Donnellan Date: Wed, 10 Dec 2025 21:49:34 +1100 Subject: [PATCH 369/405] MAINTAINERS: Update ocxl maintainer details I am leaving IBM, and Fred isn't working on OpenCAPI either. Mahesh has kindly agreed to take over as maintainer to review the odd fixes that still come in, and he has plenty of powerpc-specific experience. Add Mahesh as ocxl maintainer, remove Fred as a maintainer, and downgrade myself to reviewer using my personal email address. Signed-off-by: Andrew Donnellan Acked-by: Frederic Barrat Acked-by: Madhavan Srinivasan Link: https://patch.msgid.link/20251210-ocxl-maintainer-status-v1-1-d73981866db9@linux.ibm.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 48fda1f8332e..4275040b372d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19253,8 +19253,8 @@ F: drivers/net/dsa/ocelot/ocelot_ext.c F: include/linux/mfd/ocelot.h OCXL (Open Coherent Accelerator Processor Interface OpenCAPI) DRIVER -M: Frederic Barrat -M: Andrew Donnellan +M: Mahesh J Salgaonkar +R: Andrew Donnellan L: linuxppc-dev@lists.ozlabs.org S: Odd Fixes F: Documentation/userspace-api/accelerators/ocxl.rst From ac7460c6037e45c72703cbe9a96ee6668601c80f Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Wed, 18 Mar 2026 15:17:06 +0530 Subject: [PATCH 370/405] misc: amd-sbi: Address CPUID extended function bits According to the UAPI header (amd-apml.h), the CPUID extended function capability is indicated by bits [55:48], but the driver currently checks bits [63:56]. Adjust the driver to use bits [55:48] so that extended function capability is detected correctly. Fixes: bb13a84ed6b7 ("misc: amd-sbi: Add support for CPUID protocol") Tested-by: Prathima L K Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: Akshay Gupta Link: https://patch.msgid.link/20260318094706.2623258-1-Akshay.Gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/amd-sbi/rmi-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/amd-sbi/rmi-core.c b/drivers/misc/amd-sbi/rmi-core.c index c3a58912d6db..6979bfd7da64 100644 --- a/drivers/misc/amd-sbi/rmi-core.c +++ b/drivers/misc/amd-sbi/rmi-core.c @@ -48,7 +48,7 @@ /* CPUID MCAMSR mask & index */ #define CPUID_MCA_THRD_INDEX 32 #define CPUID_MCA_FUNC_MASK GENMASK(31, 0) -#define CPUID_EXT_FUNC_INDEX 56 +#define CPUID_EXT_FUNC_INDEX 48 /* input for bulk write to CPUID protocol */ struct cpu_msr_indata { From b5e7fb39819aec09a27c89f203774ffc6b13a78d Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Wed, 18 Mar 2026 16:57:09 +0530 Subject: [PATCH 371/405] misc: amd-sbi: Add revision support for AMD Venice platform The AMD Venice platform uses revision 0x31 and a two-byte register address size. Add the revision to the CPUID and MCAMSR protocol functions to ensure correct protocol identification. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: Akshay Gupta Link: https://patch.msgid.link/20260318112711.2757467-1-Akshay.Gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/amd-sbi/rmi-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/misc/amd-sbi/rmi-core.c b/drivers/misc/amd-sbi/rmi-core.c index 6979bfd7da64..d4238ebad3c6 100644 --- a/drivers/misc/amd-sbi/rmi-core.c +++ b/drivers/misc/amd-sbi/rmi-core.c @@ -214,6 +214,7 @@ static int rmi_cpuid_read(struct sbrmi_data *data, goto exit_unlock; break; case 0x21: + case 0x31: ret = rmi_cpuid_input_ext(data, msg, thread); if (ret) goto exit_unlock; @@ -327,6 +328,7 @@ static int rmi_mca_msr_read(struct sbrmi_data *data, goto exit_unlock; break; case 0x21: + case 0x31: ret = rmi_mcamsr_input_ext(data, msg, thread); if (ret) goto exit_unlock; From 82e1288701c0b746397f2a133b1f93d3d48eee23 Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Wed, 18 Mar 2026 16:57:10 +0530 Subject: [PATCH 372/405] misc: amd-sbi: Add check to probe only SBRMI devices AMD OOB devices are differentiated by their Instance ID, with SBRMI assigned Instance ID 1. Since the device ID match does not consider the Instance ID, add an explicit check to restrict probing to only the SBRMI device and exclude other OOB devices. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: Akshay Gupta Link: https://patch.msgid.link/20260318112711.2757467-2-Akshay.Gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/amd-sbi/rmi-i2c.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/misc/amd-sbi/rmi-i2c.c b/drivers/misc/amd-sbi/rmi-i2c.c index f0cc99000b69..04182358bebb 100644 --- a/drivers/misc/amd-sbi/rmi-i2c.c +++ b/drivers/misc/amd-sbi/rmi-i2c.c @@ -170,6 +170,16 @@ static int sbrmi_i3c_probe(struct i3c_device *i3cdev) struct regmap *regmap; int rev, ret; + /* + * AMD OOB devices are distinguished by their Instance ID. + * For SBRMI, the Instance ID is 1. Since the device ID match + * does not account for the Instance ID, the following check + * ensures that only the SBRMI device is probed, excluding + * other OOB devices. + */ + if (I3C_PID_INSTANCE_ID(i3cdev->desc->info.pid) != 1) + return -ENXIO; + regmap = devm_regmap_init_i3c(i3cdev, &sbrmi_regmap_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); From eef2a8ddfaf80ecca82800f40bce8647ac1bdf57 Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Wed, 18 Mar 2026 16:57:11 +0530 Subject: [PATCH 373/405] misc: amd-sbi: Add device tree mapping for AMD SBRMI devices Add device tree mapping to enable SBRMI device support across different models and steppings on the AMD Venice platform. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: Akshay Gupta Link: https://patch.msgid.link/20260318112711.2757467-3-Akshay.Gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/amd-sbi/rmi-i2c.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/misc/amd-sbi/rmi-i2c.c b/drivers/misc/amd-sbi/rmi-i2c.c index 04182358bebb..37e5ea83bf97 100644 --- a/drivers/misc/amd-sbi/rmi-i2c.c +++ b/drivers/misc/amd-sbi/rmi-i2c.c @@ -222,6 +222,10 @@ static void sbrmi_i3c_remove(struct i3c_device *i3cdev) static const struct i3c_device_id sbrmi_i3c_id[] = { /* PID for AMD SBRMI device */ I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x2, NULL), + I3C_DEVICE_EXTRA_INFO(0x0, 0x0, 0x118, NULL), /* Socket:0, Venice */ + I3C_DEVICE_EXTRA_INFO(0x0, 0x100, 0x118, NULL), /* Socket:1, Venice */ + I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x119, NULL), /* Socket:0, Venice */ + I3C_DEVICE_EXTRA_INFO(0x112, 0x100, 0x119, NULL), /* Socket:1, Venice */ {} }; MODULE_DEVICE_TABLE(i3c, sbrmi_i3c_id); From 84664e43ef87413f81b779b6433f43e14bc558cb Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sat, 21 Feb 2026 01:20:31 +0800 Subject: [PATCH 374/405] misc: ti_fpc202: fix off-by-one error in port ID bounds check FPC202_NUM_PORTS is 2, valid port IDs should be 0 and 1. A port_id of 2 would incorrectly pass the check, potentially causing out-of-bounds access to the port-related arrays. Found by code review, compile pass. Fixes: 1e5c9b1efa1c ("misc: add FPC202 dual port controller driver") Signed-off-by: Felix Gu Reviewed-by: Romain Gantois Acked-by: Arnd Bergmann Reviewed-by: Andi Shyti Link: https://patch.msgid.link/20260221-fp202-v1-1-4d28cb8b28fb@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ti_fpc202.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/ti_fpc202.c b/drivers/misc/ti_fpc202.c index 8eb2b5ac9850..578feefb77f1 100644 --- a/drivers/misc/ti_fpc202.c +++ b/drivers/misc/ti_fpc202.c @@ -366,7 +366,7 @@ static int fpc202_probe(struct i2c_client *client) goto unregister_chans; } - if (port_id > FPC202_NUM_PORTS) { + if (port_id >= FPC202_NUM_PORTS) { dev_err(dev, "port ID %d is out of range!\n", port_id); ret = -EINVAL; goto unregister_chans; From f485ae8e9a251b7698f04270b9226fa05135fc43 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sat, 21 Feb 2026 01:20:32 +0800 Subject: [PATCH 375/405] misc: ti_fpc202: remove dead code in fpc202_detach_addr() val is assigned from addr_caches, which is a u8 array. So the check will never be true. Found by code review, compile pass. Signed-off-by: Felix Gu Reviewed-by: Romain Gantois Link: https://patch.msgid.link/20260221-fp202-v1-2-4d28cb8b28fb@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ti_fpc202.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/misc/ti_fpc202.c b/drivers/misc/ti_fpc202.c index 578feefb77f1..79a029d79f7a 100644 --- a/drivers/misc/ti_fpc202.c +++ b/drivers/misc/ti_fpc202.c @@ -243,23 +243,15 @@ static void fpc202_detach_addr(struct i2c_atr *atr, u32 chan_id, u16 addr) { struct fpc202_priv *priv = i2c_atr_get_driver_data(atr); - int dev_num, reg_mod, val; + int dev_num, val; for (dev_num = 0; dev_num < 2; dev_num++) { - reg_mod = FPC202_REG_MOD_DEV(chan_id, dev_num); - mutex_lock(&priv->reg_dev_lock); val = priv->addr_caches[chan_id][dev_num]; mutex_unlock(&priv->reg_dev_lock); - if (val < 0) { - dev_err(&priv->client->dev, "failed to read register 0x%x while detaching address 0x%02x\n", - reg_mod, addr); - return; - } - if (val == (addr & 0x7f)) { fpc202_write_dev_addr(priv, chan_id, dev_num, FPC202_REG_DEV_INVALID); return; From 01a80abdb204942bad1ef966e559cc92bd74279a Mon Sep 17 00:00:00 2001 From: Romain Gantois Date: Tue, 31 Mar 2026 11:20:56 +0200 Subject: [PATCH 376/405] misc: ti_fpc202: Depend on GPIOLIB instead of selecting it Selecting a foreign subsystem such as GPIOLIB may lead to dependency loops. Use a "depends on" instead. Signed-off-by: Romain Gantois Link: https://patch.msgid.link/20260331-fpc202-leds-v3-1-74b173537d42@bootlin.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 5cc79d1517af..dcb36e39d707 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -116,7 +116,7 @@ config RPMB config TI_FPC202 tristate "TI FPC202 Dual Port Controller" depends on I2C - select GPIOLIB + depends on GPIOLIB select I2C_ATR help If you say yes here you get support for the Texas Instruments FPC202 From 7bc515285086837c6737c0b4ecaadd7de631d221 Mon Sep 17 00:00:00 2001 From: Romain Gantois Date: Tue, 31 Mar 2026 11:20:57 +0200 Subject: [PATCH 377/405] dt-bindings: misc: Describe FPC202 LED features The FPC202 dual port controller has 20 regular GPIO lines and 8 special GPIO lines with LED features. Each one of these "LED GPIOs" can output PWM and blink signals. Describe these special-purpose GPIO lines. Acked-by: Conor Dooley Signed-off-by: Romain Gantois Link: https://patch.msgid.link/20260331-fpc202-leds-v3-2-74b173537d42@bootlin.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/misc/ti,fpc202.yaml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Documentation/devicetree/bindings/misc/ti,fpc202.yaml b/Documentation/devicetree/bindings/misc/ti,fpc202.yaml index a8cb10f2d0df..71c5859d2e13 100644 --- a/Documentation/devicetree/bindings/misc/ti,fpc202.yaml +++ b/Documentation/devicetree/bindings/misc/ti,fpc202.yaml @@ -53,6 +53,22 @@ patternProperties: unevaluatedProperties: false + "^led@1[4-b]$": + $ref: /schemas/leds/common.yaml# + description: Output GPIO line with advanced LED features enabled. + + properties: + reg: + minimum: 0x14 + maximum: 0x1b + description: + GPIO line ID + + required: + - reg + + unevaluatedProperties: false + required: - compatible - reg @@ -89,6 +105,11 @@ examples: #size-cells = <0>; reg = <1>; }; + + led@14 { + reg = <0x14>; + label = "phy0:green:indicator"; + }; }; }; ... From 2a8c2080b1a52ed70e01c29363539d15eca0a37b Mon Sep 17 00:00:00 2001 From: Romain Gantois Date: Tue, 31 Mar 2026 11:20:58 +0200 Subject: [PATCH 378/405] misc: ti_fpc202: Support special-purpose GPIO lines with LED features The FPC202 dual port controller has 20 regular GPIO lines and 8 special GPIO lines with LED features. Each one of these "LED GPIOs" can output PWM and blink signals. Add support for the eight special-purpose GPIO lines to the existing FPC202 driver's GPIO support. Add support for registering led-class devices on these GPIO lines. Signed-off-by: Romain Gantois Link: https://patch.msgid.link/20260331-fpc202-leds-v3-3-74b173537d42@bootlin.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 1 + drivers/misc/ti_fpc202.c | 339 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 327 insertions(+), 13 deletions(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index dcb36e39d707..00683bf06258 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -117,6 +117,7 @@ config TI_FPC202 tristate "TI FPC202 Dual Port Controller" depends on I2C depends on GPIOLIB + depends on LEDS_CLASS select I2C_ATR help If you say yes here you get support for the Texas Instruments FPC202 diff --git a/drivers/misc/ti_fpc202.c b/drivers/misc/ti_fpc202.c index 79a029d79f7a..2aac83ec4a39 100644 --- a/drivers/misc/ti_fpc202.c +++ b/drivers/misc/ti_fpc202.c @@ -7,12 +7,17 @@ */ #include +#include #include #include #include #include #include +#include +#include #include +#include +#include #define FPC202_NUM_PORTS 2 #define FPC202_ALIASES_PER_PORT 2 @@ -34,18 +39,55 @@ * ... * 19: P1_S1_OUT_B * + * Ports with optional LED control: + * + * 20: P0_S0_OUT_C (P0_S0_LED1) + * ... + * 23: P1_S1_OUT_C (P1_S1_LED1) + * 24: P0_S0_OUT_D (P0_S0_LED2 + * ... + * 27: P1_S1_OUT_D (P1_S1_LED2) + * */ -#define FPC202_GPIO_COUNT 20 +#define FPC202_GPIO_COUNT 28 #define FPC202_GPIO_P0_S0_IN_B 4 #define FPC202_GPIO_P0_S0_OUT_A 12 +#define FPC202_GPIO_P0_S0_OUT_C 20 +#define FPC202_GPIO_P0_S0_OUT_D 24 #define FPC202_REG_IN_A_INT 0x6 #define FPC202_REG_IN_C_IN_B 0x7 #define FPC202_REG_OUT_A_OUT_B 0x8 +#define FPC202_REG_OUT_C_OUT_D 0x9 #define FPC202_REG_OUT_A_OUT_B_VAL 0xa +#define FPC202_LED_COUNT 8 + +/* There are four LED GPIO mode registers which manage two GPIOs each. */ +#define FPC202_REG_LED_MODE(offset) (0x1a + 0x20 * ((offset) % 4)) + +/* LED1 GPIOs (*_OUT_C) are configured in bits 1:0, LED2 GPIOs (*_OUT_D) in bits 3:2. */ +#define FPC202_LED_MODE_SHIFT(offset) ((offset) < FPC202_GPIO_P0_S0_OUT_D ? 0 : 2) +#define FPC202_LED_MODE_MASK(offset) (GENMASK(1, 0) << FPC202_LED_MODE_SHIFT(offset)) + +/* There is one PWM control register for each GPIO LED */ +#define FPC202_REG_LED_PWM(offset) \ + (((offset) < FPC202_GPIO_P0_S0_OUT_D ? 0x14 : 0x15) + 0x20 * ((offset) % 4)) + +/* There are two blink delay registers (on/off time) for each GPIO LED */ +#define FPC202_REG_LED_BLINK_ON(offset) \ + (((offset) < FPC202_GPIO_P0_S0_OUT_D ? 0x16 : 0x18) + 0x20 * ((offset) % 4)) +#define FPC202_REG_LED_BLINK_OFF(offset) (FPC202_REG_LED_BLINK_ON(offset) + 1) + +/* The actual hardware precision is 2.5ms but since the LED API doesn't handle sub-millisecond + * timesteps this is rounded up to 5ms + */ +#define FPC202_LED_BLINK_PRECISION 5UL + +#define FPC202_LED_MAX_BRIGHTNESS 255 + #define FPC202_REG_MOD_DEV(port, dev) (0xb4 + ((port) * 4) + (dev)) #define FPC202_REG_AUX_DEV(port, dev) (0xb6 + ((port) * 4) + (dev)) @@ -59,15 +101,34 @@ /* Even aliases are assigned to device 0 and odd aliases to device 1 */ #define fpc202_dev_num_from_alias(alias) ((alias) % 2) +enum fpc202_led_mode { + FPC202_LED_MODE_OFF = 0, + FPC202_LED_MODE_ON = 1, + FPC202_LED_MODE_PWM = 2, + FPC202_LED_MODE_BLINK = 3, +}; + +struct fpc202_led { + int offset; + struct led_classdev led_cdev; + struct fpc202_priv *priv; + struct gpio_desc *gpio; + enum fpc202_led_mode mode; +}; + struct fpc202_priv { struct i2c_client *client; struct i2c_atr *atr; struct gpio_desc *en_gpio; struct gpio_chip gpio; + struct fpc202_led leds[FPC202_LED_COUNT]; /* Lock REG_MOD/AUX_DEV and addr_caches during attach/detach */ struct mutex reg_dev_lock; + /* Lock LED mode select register during accesses */ + struct mutex led_mode_lock; + /* Cached device addresses for both ports and their devices */ u8 addr_caches[2][2]; @@ -97,6 +158,11 @@ static int fpc202_gpio_get_dir(int offset) return offset < FPC202_GPIO_P0_S0_OUT_A ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; } +static int fpc202_gpio_has_led_caps(int offset) +{ + return offset >= FPC202_GPIO_P0_S0_OUT_C; +} + static int fpc202_read(struct fpc202_priv *priv, u8 reg) { int val; @@ -118,6 +184,37 @@ static void fpc202_set_enable(struct fpc202_priv *priv, int enable) gpiod_set_value(priv->en_gpio, enable); } +static int fpc202_led_mode_write(struct fpc202_priv *priv, + int offset, + enum fpc202_led_mode mode) +{ + u8 val, reg = FPC202_REG_LED_MODE(offset); + int ret; + + guard(mutex)(&priv->led_mode_lock); + + ret = fpc202_read(priv, reg); + if (ret < 0) { + dev_err(&priv->client->dev, "failed to read LED mode %d! err %d\n", + offset, ret); + return ret; + } + + val = (u8)ret & ~FPC202_LED_MODE_MASK(offset); + val |= mode << FPC202_LED_MODE_SHIFT(offset); + + return fpc202_write(priv, reg, val); +} + +static int fpc202_led_mode_set(struct fpc202_led *led, enum fpc202_led_mode mode) +{ + struct fpc202_priv *priv = led->priv; + + led->mode = mode; + + return fpc202_led_mode_write(priv, led->offset, mode); +} + static int fpc202_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { @@ -125,6 +222,16 @@ static int fpc202_gpio_set(struct gpio_chip *chip, unsigned int offset, int ret; u8 val; + if (fpc202_gpio_has_led_caps(offset)) { + ret = fpc202_led_mode_write(priv, offset, + value ? FPC202_LED_MODE_ON : FPC202_LED_MODE_OFF); + if (ret < 0) + dev_err(&priv->client->dev, "Failed to set GPIO %d LED mode! err %d\n", + offset, ret); + + return ret; + } + ret = fpc202_read(priv, FPC202_REG_OUT_A_OUT_B_VAL); if (ret < 0) { dev_err(&priv->client->dev, "Failed to set GPIO %d value! err %d\n", offset, ret); @@ -153,9 +260,11 @@ static int fpc202_gpio_get(struct gpio_chip *chip, unsigned int offset) } else if (offset < FPC202_GPIO_P0_S0_OUT_A) { reg = FPC202_REG_IN_C_IN_B; bit = BIT(offset - FPC202_GPIO_P0_S0_IN_B); - } else { + } else if (!fpc202_gpio_has_led_caps(offset)) { reg = FPC202_REG_OUT_A_OUT_B_VAL; bit = BIT(offset - FPC202_GPIO_P0_S0_OUT_A); + } else { + return -EOPNOTSUPP; } ret = fpc202_read(priv, reg); @@ -177,21 +286,29 @@ static int fpc202_gpio_direction_output(struct gpio_chip *chip, unsigned int off int value) { struct fpc202_priv *priv = gpiochip_get_data(chip); + u8 reg, val, bit; int ret; - u8 val; if (fpc202_gpio_get_dir(offset) == GPIO_LINE_DIRECTION_IN) return -EINVAL; fpc202_gpio_set(chip, offset, value); - ret = fpc202_read(priv, FPC202_REG_OUT_A_OUT_B); + if (fpc202_gpio_has_led_caps(offset)) { + reg = FPC202_REG_OUT_C_OUT_D; + bit = BIT(offset - FPC202_GPIO_P0_S0_OUT_C); + } else { + reg = FPC202_REG_OUT_A_OUT_B; + bit = BIT(offset - FPC202_GPIO_P0_S0_OUT_A); + } + + ret = fpc202_read(priv, reg); if (ret < 0) return ret; - val = (u8)ret | BIT(offset - FPC202_GPIO_P0_S0_OUT_A); + val = (u8)ret | bit; - return fpc202_write(priv, FPC202_REG_OUT_A_OUT_B, val); + return fpc202_write(priv, reg, val); } /* @@ -264,6 +381,183 @@ static const struct i2c_atr_ops fpc202_atr_ops = { .detach_addr = fpc202_detach_addr, }; +static struct fpc202_led *fpc202_cdev_to_led(struct led_classdev *cdev) +{ + return container_of(cdev, struct fpc202_led, led_cdev); +} + +static struct fpc202_led *fpc202_led_get(struct fpc202_priv *priv, int offset) +{ + return &priv->leds[offset - FPC202_GPIO_P0_S0_OUT_C]; +} + +static int fpc202_led_blink_set(struct led_classdev *cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct fpc202_led *led = fpc202_cdev_to_led(cdev); + struct fpc202_priv *priv = led->priv; + unsigned long val; + int ret; + + if (*delay_on == 0 && *delay_off == 0) { + *delay_on = 250; + *delay_off = 250; + } else { + if (*delay_on % FPC202_LED_BLINK_PRECISION) + *delay_on = roundup(*delay_on, FPC202_LED_BLINK_PRECISION); + + if (*delay_off % FPC202_LED_BLINK_PRECISION) + *delay_off = roundup(*delay_off, FPC202_LED_BLINK_PRECISION); + } + + /* Multiply the duration by two, since the actual precision is 2.5ms not 5ms*/ + val = 2 * (*delay_on / FPC202_LED_BLINK_PRECISION); + if (val > 255) { + val = 255; + *delay_on = (val / 2) * FPC202_LED_BLINK_PRECISION; + } + + ret = fpc202_write(priv, FPC202_REG_LED_BLINK_ON(led->offset), val); + if (ret) { + dev_err(&priv->client->dev, + "Failed to set blink on duration for LED %d, err %d\n", + led->offset, ret); + return ret; + } + + val = 2 * (*delay_off / FPC202_LED_BLINK_PRECISION); + if (val > 255) { + val = 255; + *delay_off = (val / 2) * FPC202_LED_BLINK_PRECISION; + } + + ret = fpc202_write(priv, FPC202_REG_LED_BLINK_OFF(led->offset), val); + if (ret) { + dev_err(&priv->client->dev, + "Failed to set blink off duration for LED %d, err %d\n", + led->offset, ret); + return ret; + } + + return fpc202_led_mode_set(led, FPC202_LED_MODE_BLINK); +} + +static enum led_brightness fpc202_led_brightness_get(struct led_classdev *cdev) +{ + struct fpc202_led *led = fpc202_cdev_to_led(cdev); + + if (led->mode == FPC202_LED_MODE_OFF) + return LED_OFF; + + return LED_ON; +} + +static int fpc202_led_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct fpc202_led *led = fpc202_cdev_to_led(cdev); + struct fpc202_priv *priv = led->priv; + int ret; + + if (!brightness) + return fpc202_led_mode_set(led, FPC202_LED_MODE_OFF); + + if (led->mode != FPC202_LED_MODE_BLINK) { + if (brightness == FPC202_LED_MAX_BRIGHTNESS) + return fpc202_led_mode_set(led, FPC202_LED_MODE_ON); + + ret = fpc202_led_mode_set(led, FPC202_LED_MODE_PWM); + if (ret) { + dev_err(&priv->client->dev, "Failed to set LED %d mode, err %d\n", + led->offset, ret); + return ret; + } + } + + return fpc202_write(priv, FPC202_REG_LED_PWM(led->offset), brightness); +} + +static int fpc202_register_led(struct fpc202_priv *priv, int offset, + struct device_node *led_handle) +{ + struct fpc202_led *led = fpc202_led_get(priv, offset); + struct device *dev = &priv->client->dev; + struct led_init_data init_data = { }; + int ret = 0; + + led->priv = priv; + led->offset = offset; + led->led_cdev.max_brightness = FPC202_LED_MAX_BRIGHTNESS; + led->led_cdev.brightness_set_blocking = fpc202_led_brightness_set; + led->led_cdev.brightness_get = fpc202_led_brightness_get; + led->led_cdev.blink_set = fpc202_led_blink_set; + + init_data.fwnode = of_fwnode_handle(led_handle); + init_data.default_label = NULL; + init_data.devicename = NULL; + init_data.devname_mandatory = false; + + ret = fpc202_led_mode_set(led, FPC202_LED_MODE_OFF); + if (ret) { + dev_err(dev, "Failed to set LED %d mode, err %d\n", offset, ret); + return ret; + } + + ret = devm_led_classdev_register_ext(dev, &led->led_cdev, &init_data); + if (ret) { + dev_err(dev, "Failed to register LED %d cdev, err %d\n", offset, ret); + return ret; + } + + /* Claim corresponding GPIO line so that it cannot be interfered with */ + led->gpio = gpiochip_request_own_desc(&priv->gpio, offset, led->led_cdev.name, + GPIO_ACTIVE_HIGH, GPIOD_ASIS); + if (IS_ERR(led->gpio)) { + ret = PTR_ERR(led->gpio); + dev_err(dev, "Failed to register LED %d cdev, err %d\n", offset, ret); + } + + return ret; +} + +static int fpc202_register_leds(struct fpc202_priv *priv) +{ + struct device *dev = &priv->client->dev; + int offset, ret = 0; + + if (!devres_open_group(dev, fpc202_register_leds, GFP_KERNEL)) + return -ENOMEM; + + for_each_child_of_node_scoped(dev->of_node, led_handle) { + ret = of_property_read_u32(led_handle, "reg", &offset); + if (ret) { + dev_err(dev, "Failed to read 'reg' property of child node, err %d\n", ret); + return ret; + } + + if (offset < FPC202_GPIO_P0_S0_OUT_C || offset > FPC202_GPIO_COUNT) + continue; + + ret = fpc202_register_led(priv, offset, led_handle); + if (ret) { + dev_err(dev, "Failed to register LED %d, err %d\n", offset, + ret); + goto free_own_gpios; + } + } + + devres_close_group(dev, fpc202_register_leds); + + return 0; + +free_own_gpios: + for (offset = 0; offset < FPC202_LED_COUNT; offset++) + if (priv->leds[offset].gpio) + gpiochip_free_own_desc(priv->leds[offset].gpio); + return ret; +} + static int fpc202_probe_port(struct fpc202_priv *priv, struct device_node *i2c_handle, int port_id) { u16 aliases[FPC202_ALIASES_PER_PORT] = { }; @@ -302,13 +596,14 @@ static int fpc202_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct fpc202_priv *priv; - int ret, port_id; + int ret, port_id, led_id; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; mutex_init(&priv->reg_dev_lock); + mutex_init(&priv->led_mode_lock); priv->client = client; i2c_set_clientdata(client, priv); @@ -346,6 +641,12 @@ static int fpc202_probe(struct i2c_client *client) i2c_atr_set_driver_data(priv->atr, priv); + ret = fpc202_register_leds(priv); + if (ret) { + dev_err(dev, "Failed to register LEDs, err %d\n", ret); + goto delete_atr; + } + bitmap_zero(priv->probed_ports, FPC202_NUM_PORTS); for_each_child_of_node_scoped(dev->of_node, i2c_handle) { @@ -358,11 +659,8 @@ static int fpc202_probe(struct i2c_client *client) goto unregister_chans; } - if (port_id >= FPC202_NUM_PORTS) { - dev_err(dev, "port ID %d is out of range!\n", port_id); - ret = -EINVAL; - goto unregister_chans; - } + if (port_id >= FPC202_NUM_PORTS) + continue; ret = fpc202_probe_port(priv, i2c_handle, port_id); if (ret) { @@ -377,11 +675,18 @@ static int fpc202_probe(struct i2c_client *client) for_each_set_bit(port_id, priv->probed_ports, FPC202_NUM_PORTS) fpc202_remove_port(priv, port_id); + for (led_id = 0; led_id < FPC202_LED_COUNT; led_id++) + if (priv->leds[led_id].gpio) + gpiochip_free_own_desc(priv->leds[led_id].gpio); + + devres_release_group(&client->dev, fpc202_register_leds); +delete_atr: i2c_atr_delete(priv->atr); disable_gpio: fpc202_set_enable(priv, 0); gpiochip_remove(&priv->gpio); destroy_mutex: + mutex_destroy(&priv->led_mode_lock); mutex_destroy(&priv->reg_dev_lock); out: return ret; @@ -390,11 +695,19 @@ static int fpc202_probe(struct i2c_client *client) static void fpc202_remove(struct i2c_client *client) { struct fpc202_priv *priv = i2c_get_clientdata(client); - int port_id; + int port_id, led_id; for_each_set_bit(port_id, priv->probed_ports, FPC202_NUM_PORTS) fpc202_remove_port(priv, port_id); + for (led_id = 0; led_id < FPC202_LED_COUNT; led_id++) + if (priv->leds[led_id].gpio) + gpiochip_free_own_desc(priv->leds[led_id].gpio); + + /* Release led devices early so that blink handlers don't trigger. */ + devres_release_group(&client->dev, fpc202_register_leds); + + mutex_destroy(&priv->led_mode_lock); mutex_destroy(&priv->reg_dev_lock); i2c_atr_delete(priv->atr); From 4b6e6ead556734bdc14024c5f837132b1e7a4b84 Mon Sep 17 00:00:00 2001 From: Tyllis Xu Date: Sun, 8 Mar 2026 00:21:08 -0600 Subject: [PATCH 379/405] misc: ibmasm: fix OOB MMIO read in ibmasm_handle_mouse_interrupt() ibmasm_handle_mouse_interrupt() performs an out-of-bounds MMIO read when the queue reader or writer index from hardware exceeds REMOTE_QUEUE_SIZE (60). A compromised service processor can trigger this by writing an out-of-range value to the reader or writer MMIO register before asserting an interrupt. Since writer is re-read from hardware on every loop iteration, it can also be set to an out-of-range value after the loop has already started. The root cause is that get_queue_reader() and get_queue_writer() return raw readl() values that are passed directly into get_queue_entry(), which computes: queue_begin + reader * sizeof(struct remote_input) with no bounds check. This unchecked MMIO address is then passed to memcpy_fromio(), reading 8 bytes from unintended device registers. For sufficiently large values the address falls outside the PCI BAR mapping entirely, triggering a machine check exception. Fix by checking both indices against REMOTE_QUEUE_SIZE at the top of the loop body, before any call to get_queue_entry(). On an out-of-range value, reset the reader register to 0 via set_queue_reader() before breaking, so that normal queue operation can resume if the corrupted hardware state is transient. Reported-by: Yuhao Jiang Fixes: 278d72ae8803 ("[PATCH] ibmasm driver: redesign handling of remote control events") Cc: stable@vger.kernel.org Cc: ychen@northwestern.edu Signed-off-by: Tyllis Xu Link: https://patch.msgid.link/20260308062108.258940-1-LivelyCarpet87@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ibmasm/remote.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/misc/ibmasm/remote.c b/drivers/misc/ibmasm/remote.c index ec816d3b38cb..521531738c9a 100644 --- a/drivers/misc/ibmasm/remote.c +++ b/drivers/misc/ibmasm/remote.c @@ -177,6 +177,11 @@ void ibmasm_handle_mouse_interrupt(struct service_processor *sp) writer = get_queue_writer(sp); while (reader != writer) { + if (reader >= REMOTE_QUEUE_SIZE || writer >= REMOTE_QUEUE_SIZE) { + set_queue_reader(sp, 0); + break; + } + memcpy_fromio(&input, get_queue_entry(sp, reader), sizeof(struct remote_input)); From 0eb09f737428e482a32a2e31e5e223f2b35a71d3 Mon Sep 17 00:00:00 2001 From: Tyllis Xu Date: Sat, 14 Mar 2026 11:53:54 -0500 Subject: [PATCH 380/405] ibmasm: fix OOB reads in command_file_write due to missing size checks The command_file_write() handler allocates a kernel buffer of exactly count bytes and copies user data into it, but does not validate the buffer against the dot command protocol before passing it to get_dot_command_size() and get_dot_command_timeout(). Since both the allocation size (count) and the header fields (command_size, data_size) are independently user-controlled, an attacker can cause get_dot_command_size() to return a value exceeding the allocation, triggering OOB reads in get_dot_command_timeout() and an out-of-bounds memcpy_toio() that leaks kernel heap memory to the service processor. Fix with two guards: reject writes smaller than sizeof(struct dot_command_header) before allocation, then after copying user data reject commands where the buffer is smaller than the total size declared by the header (sizeof(header) + command_size + data_size). This ensures all subsequent header and payload field accesses stay within the buffer. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Tyllis Xu Link: https://patch.msgid.link/20260314165355.548119-1-LivelyCarpet87@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ibmasm/ibmasmfs.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/misc/ibmasm/ibmasmfs.c b/drivers/misc/ibmasm/ibmasmfs.c index f68a8957b98f..dfdfa9ba4747 100644 --- a/drivers/misc/ibmasm/ibmasmfs.c +++ b/drivers/misc/ibmasm/ibmasmfs.c @@ -303,6 +303,8 @@ static ssize_t command_file_write(struct file *file, const char __user *ubuff, s return -EINVAL; if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE) return 0; + if (count < sizeof(struct dot_command_header)) + return -EINVAL; if (*offset != 0) return 0; @@ -319,6 +321,11 @@ static ssize_t command_file_write(struct file *file, const char __user *ubuff, s return -EFAULT; } + if (count < get_dot_command_size(cmd->buffer)) { + command_put(cmd); + return -EINVAL; + } + spin_lock_irqsave(&command_data->sp->lock, flags); if (command_data->command) { spin_unlock_irqrestore(&command_data->sp->lock, flags); From 9aad71144fa3682cca3837a06c8623016790e7ec Mon Sep 17 00:00:00 2001 From: Tyllis Xu Date: Sat, 14 Mar 2026 11:58:05 -0500 Subject: [PATCH 381/405] ibmasm: fix heap over-read in ibmasm_send_i2o_message() The ibmasm_send_i2o_message() function uses get_dot_command_size() to compute the byte count for memcpy_toio(), but this value is derived from user-controlled fields in the dot_command_header (command_size: u8, data_size: u16) and is never validated against the actual allocation size. A root user can write a small buffer with inflated header fields, causing memcpy_toio() to read up to ~65 KB past the end of the allocation into adjacent kernel heap, which is then forwarded to the service processor over MMIO. Silently clamping the copy size is not sufficient: if the header fields claim a larger size than the buffer, the SP receives a dot command whose own header is inconsistent with the I2O message length, which can cause the SP to desynchronize. Reject such commands outright by returning failure. Validate command_size before calling get_mfa_inbound() to avoid leaking an I2O message frame: reading INBOUND_QUEUE_PORT dequeues a hardware frame from the controller's free pool, and returning without a corresponding set_mfa_inbound() call would permanently exhaust it. Additionally, clamp command_size to I2O_COMMAND_SIZE before the memcpy_toio() so the MMIO write stays within the I2O message frame, consistent with the clamping already performed by outgoing_message_size() for the header field. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Tyllis Xu Link: https://patch.msgid.link/20260314165805.548293-1-LivelyCarpet87@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ibmasm/lowlevel.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/misc/ibmasm/lowlevel.c b/drivers/misc/ibmasm/lowlevel.c index 6922dc6c10db..5313230f36ad 100644 --- a/drivers/misc/ibmasm/lowlevel.c +++ b/drivers/misc/ibmasm/lowlevel.c @@ -19,17 +19,21 @@ static struct i2o_header header = I2O_HEADER_TEMPLATE; int ibmasm_send_i2o_message(struct service_processor *sp) { u32 mfa; - unsigned int command_size; + size_t command_size; struct i2o_message *message; struct command *command = sp->current_command; + command_size = get_dot_command_size(command->buffer); + if (command_size > command->buffer_size) + return 1; + if (command_size > I2O_COMMAND_SIZE) + command_size = I2O_COMMAND_SIZE; + mfa = get_mfa_inbound(sp->base_address); if (!mfa) return 1; - command_size = get_dot_command_size(command->buffer); - header.message_size = outgoing_message_size(command_size); - + header.message_size = outgoing_message_size((unsigned int)command_size); message = get_i2o_message(sp->base_address, mfa); memcpy_toio(&message->header, &header, sizeof(struct i2o_header)); From 4dc0c899b14c3ea2c9b5f42b523301040d1c8100 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Mon, 2 Mar 2026 15:24:36 +0100 Subject: [PATCH 382/405] pps: change pps_gen_class to a const struct The class_create() call has been deprecated in favor of class_register() as the driver core now allows for a struct class to be in read-only memory. Change pps_gen_class to be a const struct class and drop the class_create() call. Signed-off-by: Jori Koolstra Reviewed-by: Greg Kroah-Hartman Acked-by: Rodolfo Giometti Link: https://patch.msgid.link/20260302142436.3292766-1-jkoolstra@xs4all.nl Signed-off-by: Greg Kroah-Hartman --- drivers/pps/generators/pps_gen.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/pps/generators/pps_gen.c b/drivers/pps/generators/pps_gen.c index 7143c003366c..5e207c75e340 100644 --- a/drivers/pps/generators/pps_gen.c +++ b/drivers/pps/generators/pps_gen.c @@ -26,7 +26,10 @@ */ static dev_t pps_gen_devt; -static struct class *pps_gen_class; +static const struct class pps_gen_class = { + .name = "pps-gen", + .dev_groups = pps_gen_groups +}; static DEFINE_IDA(pps_gen_ida); @@ -183,7 +186,7 @@ static int pps_gen_register_cdev(struct pps_gen_device *pps_gen) MAJOR(pps_gen_devt), pps_gen->id); goto free_ida; } - pps_gen->dev = device_create(pps_gen_class, pps_gen->info->parent, devt, + pps_gen->dev = device_create(&pps_gen_class, pps_gen->info->parent, devt, pps_gen, "pps-gen%d", pps_gen->id); if (IS_ERR(pps_gen->dev)) { err = PTR_ERR(pps_gen->dev); @@ -207,7 +210,7 @@ static int pps_gen_register_cdev(struct pps_gen_device *pps_gen) static void pps_gen_unregister_cdev(struct pps_gen_device *pps_gen) { pr_debug("unregistering pps-gen%d\n", pps_gen->id); - device_destroy(pps_gen_class, pps_gen->dev->devt); + device_destroy(&pps_gen_class, pps_gen->dev->devt); } /* @@ -307,7 +310,7 @@ EXPORT_SYMBOL(pps_gen_event); static void __exit pps_gen_exit(void) { - class_destroy(pps_gen_class); + class_unregister(&pps_gen_class); unregister_chrdev_region(pps_gen_devt, PPS_GEN_MAX_SOURCES); } @@ -315,12 +318,11 @@ static int __init pps_gen_init(void) { int err; - pps_gen_class = class_create("pps-gen"); - if (IS_ERR(pps_gen_class)) { - pr_err("failed to allocate class\n"); - return PTR_ERR(pps_gen_class); + err = class_register(&pps_gen_class); + if (err) { + pr_err("failed to register class\n"); + return err; } - pps_gen_class->dev_groups = pps_gen_groups; err = alloc_chrdev_region(&pps_gen_devt, 0, PPS_GEN_MAX_SOURCES, "pps-gen"); @@ -332,7 +334,7 @@ static int __init pps_gen_init(void) return 0; remove_class: - class_destroy(pps_gen_class); + class_unregister(&pps_gen_class); return err; } From f7a1fec4de122bba0872addf757dd8c26efc2917 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Wed, 1 Apr 2026 19:00:43 +0200 Subject: [PATCH 383/405] most: replace cdev_component->class with a const struct class The class_create() call has been deprecated in favor of class_register() as the driver core now allows for a struct class to be in read-only memory. Replace cdev_component->class with a const struct class and drop the class_create() call. Compile tested only. Link: https://lore.kernel.org/all/2023040244-duffel-pushpin-f738@gregkh/ Suggested-by: Greg Kroah-Hartman Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260401170043.3844117-1-jkoolstra@xs4all.nl Signed-off-by: Greg Kroah-Hartman --- drivers/most/most_cdev.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/most/most_cdev.c b/drivers/most/most_cdev.c index b31dc824466f..5df508d8d60a 100644 --- a/drivers/most/most_cdev.c +++ b/drivers/most/most_cdev.c @@ -19,11 +19,14 @@ #define CHRDEV_REGION_SIZE 50 +static const struct class most_cdev_class = { + .name = "most_cdev" +}; + static struct cdev_component { dev_t devno; struct ida minor_id; unsigned int major; - struct class *class; struct most_component cc; } comp; @@ -91,7 +94,7 @@ static void destroy_cdev(struct comp_channel *c) { unsigned long flags; - device_destroy(comp.class, c->devno); + device_destroy(&most_cdev_class, c->devno); cdev_del(&c->cdev); spin_lock_irqsave(&ch_list_lock, flags); list_del(&c->list); @@ -455,7 +458,7 @@ static int comp_probe(struct most_interface *iface, int channel_id, spin_lock_irqsave(&ch_list_lock, cl_flags); list_add_tail(&c->list, &channel_list); spin_unlock_irqrestore(&ch_list_lock, cl_flags); - c->dev = device_create(comp.class, NULL, c->devno, NULL, "%s", name); + c->dev = device_create(&most_cdev_class, NULL, c->devno, NULL, "%s", name); if (IS_ERR(c->dev)) { retval = PTR_ERR(c->dev); @@ -487,13 +490,14 @@ static struct cdev_component comp = { }, }; + static int __init most_cdev_init(void) { int err; - comp.class = class_create("most_cdev"); - if (IS_ERR(comp.class)) - return PTR_ERR(comp.class); + err = class_register(&most_cdev_class); + if (err) + return err; ida_init(&comp.minor_id); @@ -515,7 +519,7 @@ static int __init most_cdev_init(void) unregister_chrdev_region(comp.devno, CHRDEV_REGION_SIZE); dest_ida: ida_destroy(&comp.minor_id); - class_destroy(comp.class); + class_unregister(&most_cdev_class); return err; } @@ -532,7 +536,7 @@ static void __exit most_cdev_exit(void) } unregister_chrdev_region(comp.devno, CHRDEV_REGION_SIZE); ida_destroy(&comp.minor_id); - class_destroy(comp.class); + class_unregister(&most_cdev_class); } module_init(most_cdev_init); From c230ae1f9480cf9d363ded8179b14b49c5d3dd69 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Mon, 2 Mar 2026 16:11:32 +0100 Subject: [PATCH 384/405] pps: change pps_class to a const struct The class_create() call has been deprecated in favor of class_register() as the driver core now allows for a struct class to be in read-only memory. Change pps_class to be a const struct class and drop the class_create() call. Suggested-by: Greg Kroah-Hartman Signed-off-by: Jori Koolstra Acked-by: Rodolfo Giometti Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260302151132.3302993-1-jkoolstra@xs4all.nl Signed-off-by: Greg Kroah-Hartman --- drivers/pps/pps.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c index c6b8b6478276..de1122bb69ea 100644 --- a/drivers/pps/pps.c +++ b/drivers/pps/pps.c @@ -26,7 +26,10 @@ */ static int pps_major; -static struct class *pps_class; +static const struct class pps_class = { + .name = "pps", + .dev_groups = pps_groups +}; static DEFINE_MUTEX(pps_idr_lock); static DEFINE_IDR(pps_idr); @@ -379,7 +382,7 @@ int pps_register_cdev(struct pps_device *pps) } pps->id = err; - pps->dev.class = pps_class; + pps->dev.class = &pps_class; pps->dev.parent = pps->info.dev; pps->dev.devt = MKDEV(pps_major, pps->id); dev_set_drvdata(&pps->dev, pps); @@ -408,7 +411,7 @@ void pps_unregister_cdev(struct pps_device *pps) { pr_debug("unregistering pps%d\n", pps->id); pps->lookup_cookie = NULL; - device_destroy(pps_class, pps->dev.devt); + device_destroy(&pps_class, pps->dev.devt); /* Now we can release the ID for re-use */ mutex_lock(&pps_idr_lock); @@ -460,18 +463,19 @@ EXPORT_SYMBOL(pps_lookup_dev); static void __exit pps_exit(void) { - class_destroy(pps_class); + class_unregister(&pps_class); __unregister_chrdev(pps_major, 0, PPS_MAX_SOURCES, "pps"); } static int __init pps_init(void) { - pps_class = class_create("pps"); - if (IS_ERR(pps_class)) { - pr_err("failed to allocate class\n"); - return PTR_ERR(pps_class); + int err; + + err = class_register(&pps_class); + if (err) { + pr_err("failed to register class\n"); + return err; } - pps_class->dev_groups = pps_groups; pps_major = __register_chrdev(0, 0, PPS_MAX_SOURCES, "pps", &pps_cdev_fops); @@ -487,7 +491,7 @@ static int __init pps_init(void) return 0; remove_class: - class_destroy(pps_class); + class_unregister(&pps_class); return pps_major; } From 138f2ea90b66efafb6612d0a34346b56099b1c71 Mon Sep 17 00:00:00 2001 From: Henry Zhang Date: Tue, 27 Jan 2026 20:45:01 -0500 Subject: [PATCH 385/405] speakup: Document bleeps parameter values The speakup documentation had a TODO about accepted values for the bleeps parameter. drivers/accessibility/speakup/main.c indicates that it's a bitmasked param where bit 0 controls beeping and bit 1 controls announcements. Signed-off-by: Henry Zhang Reviewed-by: Samuel Thibault Link: https://patch.msgid.link/20260128014501.1600263-1-zeri@umich.edu Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/stable/sysfs-driver-speakup | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/ABI/stable/sysfs-driver-speakup b/Documentation/ABI/stable/sysfs-driver-speakup index 8b508b4a7a00..69c26b911fe3 100644 --- a/Documentation/ABI/stable/sysfs-driver-speakup +++ b/Documentation/ABI/stable/sysfs-driver-speakup @@ -16,8 +16,8 @@ What: /sys/accessibility/speakup/bleeps KernelVersion: 2.6 Contact: speakup@linux-speakup.org Description: This controls whether one hears beeps through the PC speaker - when using speakup's review commands. - TODO: what values does it accept? + when using speakup's review commands. Range: 0-3. 0 = off, 1 = beeps + only, 2 = announcements only, 3 = beeps and announcements (default). What: /sys/accessibility/speakup/bleep_time KernelVersion: 2.6 From 16cf2e54cb5e2bf563fc11c1bebc662a49f2eeba Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Fri, 30 Jan 2026 18:00:26 -0800 Subject: [PATCH 386/405] char: remove unnecessary module_init/exit functions Two char drivers have unnecessary module_init and module_exit functions that are empty or just print a message. Remove them. Note that if a module_init function exists, a module_exit function must also exist; otherwise, the module cannot be unloaded. Signed-off-by: Ethan Nelson-Moore Link: https://patch.msgid.link/20260131020027.45775-1-enelsonmoore@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/agp/backend.c | 16 ---------------- drivers/char/nsc_gpio.c | 14 -------------- 2 files changed, 30 deletions(-) diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index 4d920ca534a4..8983addca695 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -323,18 +323,6 @@ int agp_try_unsupported_boot; EXPORT_SYMBOL(agp_off); EXPORT_SYMBOL(agp_try_unsupported_boot); -static int __init agp_init(void) -{ - if (!agp_off) - printk(KERN_INFO "Linux agpgart interface v%d.%d\n", - AGPGART_VERSION_MAJOR, AGPGART_VERSION_MINOR); - return 0; -} - -static void __exit agp_exit(void) -{ -} - #ifndef MODULE static __init int agp_setup(char *s) { @@ -351,7 +339,3 @@ MODULE_AUTHOR("Dave Jones, Jeff Hartmann"); MODULE_DESCRIPTION("AGP GART driver"); MODULE_LICENSE("GPL and additional rights"); MODULE_ALIAS_MISCDEV(AGPGART_MINOR); - -module_init(agp_init); -module_exit(agp_exit); - diff --git a/drivers/char/nsc_gpio.c b/drivers/char/nsc_gpio.c index da930c72bc74..2c9b12b85435 100644 --- a/drivers/char/nsc_gpio.c +++ b/drivers/char/nsc_gpio.c @@ -121,20 +121,6 @@ EXPORT_SYMBOL(nsc_gpio_write); EXPORT_SYMBOL(nsc_gpio_read); EXPORT_SYMBOL(nsc_gpio_dump); -static int __init nsc_gpio_init(void) -{ - printk(KERN_DEBUG NAME " initializing\n"); - return 0; -} - -static void __exit nsc_gpio_cleanup(void) -{ - printk(KERN_DEBUG NAME " cleanup\n"); -} - -module_init(nsc_gpio_init); -module_exit(nsc_gpio_cleanup); - MODULE_AUTHOR("Jim Cromie "); MODULE_DESCRIPTION("NatSemi GPIO Common Methods"); MODULE_LICENSE("GPL"); From e4599c1d2b0e5f50e6c317a5e045e986a43f30e4 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Fri, 30 Jan 2026 19:24:03 -0800 Subject: [PATCH 387/405] parport: Remove completed item from to-do list A driver for the Sun BPP hardware exists in the kernel. Signed-off-by: Ethan Nelson-Moore Link: https://patch.msgid.link/20260131032403.11118-1-enelsonmoore@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/parport/TODO-parport | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/parport/TODO-parport b/drivers/parport/TODO-parport index 089b14ee446f..661a5ab91c0a 100644 --- a/drivers/parport/TODO-parport +++ b/drivers/parport/TODO-parport @@ -13,7 +13,7 @@ Things to be done. bits when they have something to say. We should read out and deal with (maybe just log) whatever the printer wants to tell the world. -3. Support more hardware (eg m68k, Sun bpp). +3. Support more hardware (eg m68k). 4. A better PLIP (make use of bidirectional/ECP/EPP ports). From b1c7f7aaabc81ea8a6be2e5cf7d0c5959306f197 Mon Sep 17 00:00:00 2001 From: Tomasz Unger Date: Fri, 20 Feb 2026 13:09:04 +0100 Subject: [PATCH 388/405] misc: vmw_vmci: Fix spelling mistakes in comments 'occured' -> 'occurred' (two instances) Found by manual inspection. Signed-off-by: Tomasz Unger Link: https://patch.msgid.link/20260220120904.1907108-1-tomasz.unger@yahoo.pl Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_vmci/vmci_queue_pair.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c index 872aad355dbe..b777bc3fdde2 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c @@ -2532,7 +2532,7 @@ static bool qp_wait_for_ready_queue(struct vmci_qp *qpair) * VMCI_ERROR_QUEUEPAIR_NOSPACE if no space was available to enqueue * data, VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the * queue (as defined by the queue size), VMCI_ERROR_INVALID_ARGS, if - * an error occured when accessing the buffer, + * an error occurred when accessing the buffer, * VMCI_ERROR_QUEUEPAIR_NOTATTACHED, if the queue pair pages aren't * available. Otherwise, the number of bytes written to the queue is * returned. Updates the tail pointer of the produce queue. @@ -2598,7 +2598,7 @@ static ssize_t qp_enqueue_locked(struct vmci_queue *produce_q, * VMCI_ERROR_QUEUEPAIR_NODATA if no data was available to dequeue. * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue * (as defined by the queue size). - * VMCI_ERROR_INVALID_ARGS, if an error occured when accessing the buffer. + * VMCI_ERROR_INVALID_ARGS, if an error occurred when accessing the buffer. * Otherwise the number of bytes dequeued is returned. * Side effects: * Updates the head pointer of the consume queue. From 71f0a267346b330ab1c4d15d98fe6fa64b3b091b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 23 Feb 2026 16:49:45 +0100 Subject: [PATCH 389/405] hpet: Convert ACPI driver to a platform one In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert the HPET ACPI driver to a platform one. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/3611505.QJadu78ljV@rafael.j.wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/char/hpet.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 60dd09a56f50..d396823e5e64 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -971,8 +972,9 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data) return AE_OK; } -static int hpet_acpi_add(struct acpi_device *device) +static int hpet_acpi_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); acpi_status result; struct hpet_data data; @@ -1000,12 +1002,12 @@ static const struct acpi_device_id hpet_device_ids[] = { {"", 0}, }; -static struct acpi_driver hpet_acpi_driver = { - .name = "hpet", - .ids = hpet_device_ids, - .ops = { - .add = hpet_acpi_add, - }, +static struct platform_driver hpet_acpi_driver = { + .probe = hpet_acpi_probe, + .driver = { + .name = "hpet_acpi", + .acpi_match_table = hpet_device_ids, + }, }; static struct miscdevice hpet_misc = { HPET_MINOR, "hpet", &hpet_fops }; @@ -1020,7 +1022,7 @@ static int __init hpet_init(void) sysctl_header = register_sysctl("dev/hpet", hpet_table); - result = acpi_bus_register_driver(&hpet_acpi_driver); + result = platform_driver_register(&hpet_acpi_driver); if (result < 0) { unregister_sysctl_table(sysctl_header); misc_deregister(&hpet_misc); From bdb6189600cd0ac8d048550a7cdd773ce5516cca Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 25 Feb 2026 19:03:29 +0100 Subject: [PATCH 390/405] most: usb: Use kzalloc_objs for endpoint address array Replace kcalloc() with kzalloc_objs() when allocating the endpoint address array to keep the size type-safe and match nearby allocations. Reformat ->busy_urbs allocation to a single line. No functional change. Signed-off-by: Thorsten Blum Reviewed-by: Kees Cook Link: https://patch.msgid.link/20260225180329.712101-2-thorsten.blum@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/most/most_usb.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/most/most_usb.c b/drivers/most/most_usb.c index d2c0875727a3..6437733afee0 100644 --- a/drivers/most/most_usb.c +++ b/drivers/most/most_usb.c @@ -1009,13 +1009,11 @@ hdm_probe(struct usb_interface *interface, const struct usb_device_id *id) goto err_free_conf; mdev->iface.channel_vector = mdev->cap; - mdev->ep_address = - kcalloc(num_endpoints, sizeof(*mdev->ep_address), GFP_KERNEL); + mdev->ep_address = kzalloc_objs(*mdev->ep_address, num_endpoints); if (!mdev->ep_address) goto err_free_cap; - mdev->busy_urbs = - kzalloc_objs(*mdev->busy_urbs, num_endpoints); + mdev->busy_urbs = kzalloc_objs(*mdev->busy_urbs, num_endpoints); if (!mdev->busy_urbs) goto err_free_ep_address; From 2d7ce8eb59ec880774c7500ac949f0100acba521 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 25 Feb 2026 21:12:07 -0800 Subject: [PATCH 391/405] misc: apds990x: fix all kernel-doc warnings Move a #define so that it is not between kernel-doc and its struct declaration. Spell one struct member correctly. Warning: include/linux/platform_data/apds990x.h:33 #define APDS_PARAM_SCALE 4096; error: Cannot parse struct or union! Warning: include/linux/platform_data/apds990x.h:62 struct member 'pdrive' not described in 'apds990x_platform_data' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260226051207.547152-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- include/linux/platform_data/apds990x.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/platform_data/apds990x.h b/include/linux/platform_data/apds990x.h index 94dfbaa365e1..37684f68c04f 100644 --- a/include/linux/platform_data/apds990x.h +++ b/include/linux/platform_data/apds990x.h @@ -31,7 +31,6 @@ * itself. If the GA is zero, driver will use uncovered sensor default values * format: decimal value * APDS_PARAM_SCALE except df which is plain integer. */ -#define APDS_PARAM_SCALE 4096 struct apds990x_chip_factors { int ga; int cf1; @@ -40,11 +39,12 @@ struct apds990x_chip_factors { int irf2; int df; }; +#define APDS_PARAM_SCALE 4096 /** * struct apds990x_platform_data - platform data for apsd990x.c driver * @cf: chip factor data - * @pddrive: IR-led driving current + * @pdrive: IR-led driving current * @ppcount: number of IR pulses used for proximity estimation * @setup_resources: interrupt line setup call back function * @release_resources: interrupt line release call back function From 7e488b0af0216f40159cc19d5db1b614c80f5134 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 23 Feb 2026 16:59:14 +0100 Subject: [PATCH 392/405] sonypi: Convert ACPI driver to a platform one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert the sonypi ACPI driver to a platform one. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/2277493.Mh6RI2rZIc@rafael.j.wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/char/sonypi.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index 677bb5ac950a..ccda997a9098 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1115,15 +1115,17 @@ static int sonypi_disable(void) } #ifdef CONFIG_ACPI -static int sonypi_acpi_add(struct acpi_device *device) +static int sonypi_acpi_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + sonypi_acpi_device = device; strcpy(acpi_device_name(device), "Sony laptop hotkeys"); strcpy(acpi_device_class(device), "sony/hotkey"); return 0; } -static void sonypi_acpi_remove(struct acpi_device *device) +static void sonypi_acpi_remove(struct platform_device *pdev) { sonypi_acpi_device = NULL; } @@ -1133,13 +1135,12 @@ static const struct acpi_device_id sonypi_device_ids[] = { {"", 0}, }; -static struct acpi_driver sonypi_acpi_driver = { - .name = "sonypi", - .class = "hkey", - .ids = sonypi_device_ids, - .ops = { - .add = sonypi_acpi_add, - .remove = sonypi_acpi_remove, +static struct platform_driver sonypi_acpi_driver = { + .probe = sonypi_acpi_probe, + .remove = sonypi_acpi_remove, + .driver = { + .name = "sonypi_acpi", + .acpi_match_table = sonypi_device_ids, }, }; #endif @@ -1518,8 +1519,8 @@ static int __init sonypi_init(void) goto err_free_device; #ifdef CONFIG_ACPI - if (acpi_bus_register_driver(&sonypi_acpi_driver) >= 0) - acpi_driver_registered = 1; + error = platform_driver_register(&sonypi_acpi_driver); + acpi_driver_registered = !error; #endif return 0; @@ -1535,7 +1536,7 @@ static void __exit sonypi_exit(void) { #ifdef CONFIG_ACPI if (acpi_driver_registered) - acpi_bus_unregister_driver(&sonypi_acpi_driver); + platform_driver_unregister(&sonypi_acpi_driver); #endif platform_device_unregister(sonypi_platform_device); platform_driver_unregister(&sonypi_driver); From 9e7a2409ecf4d411b7cc91615b08f6a7576f0aaa Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 1 Feb 2026 11:43:52 +0200 Subject: [PATCH 393/405] mei: me: use PCI_DEVICE_DATA macro Drop old local MEI_PCI_DEVICE macro and use common PCI_DEVICE_DATA instead. Update defines to adhere to current naming convention. Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260201094358.1440593-2-alexander.usyskin@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus-fixup.c | 6 +- drivers/misc/mei/hw-me-regs.h | 162 +++++++++++++++++----------------- drivers/misc/mei/hw-me.h | 6 -- drivers/misc/mei/pci-me.c | 162 +++++++++++++++++----------------- 4 files changed, 165 insertions(+), 171 deletions(-) diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index e6a1d3534663..bea7a47d216e 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -303,9 +303,9 @@ static void mei_wd(struct mei_cl_device *cldev) { struct pci_dev *pdev = to_pci_dev(cldev->dev.parent); - if (pdev->device == MEI_DEV_ID_WPT_LP || - pdev->device == MEI_DEV_ID_SPT || - pdev->device == MEI_DEV_ID_SPT_H) + if (pdev->device == PCI_DEVICE_ID_INTEL_MEI_WPT_LP || + pdev->device == PCI_DEVICE_ID_INTEL_MEI_SPT || + pdev->device == PCI_DEVICE_ID_INTEL_MEI_SPT_H) cldev->me_cl->props.protocol_version = 0x2; cldev->do_match = 1; diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index fa30899a5fa2..840e1fd2714c 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -9,120 +9,120 @@ /* * MEI device IDs */ -#define MEI_DEV_ID_82946GZ 0x2974 /* 82946GZ/GL */ -#define MEI_DEV_ID_82G35 0x2984 /* 82G35 Express */ -#define MEI_DEV_ID_82Q965 0x2994 /* 82Q963/Q965 */ -#define MEI_DEV_ID_82G965 0x29A4 /* 82P965/G965 */ +#define PCI_DEVICE_ID_INTEL_MEI_82946GZ 0x2974 /* 82946GZ/GL */ +#define PCI_DEVICE_ID_INTEL_MEI_82G35 0x2984 /* 82G35 Express */ +#define PCI_DEVICE_ID_INTEL_MEI_82Q965 0x2994 /* 82Q963/Q965 */ +#define PCI_DEVICE_ID_INTEL_MEI_82G965 0x29A4 /* 82P965/G965 */ -#define MEI_DEV_ID_82GM965 0x2A04 /* Mobile PM965/GM965 */ -#define MEI_DEV_ID_82GME965 0x2A14 /* Mobile GME965/GLE960 */ +#define PCI_DEVICE_ID_INTEL_MEI_82GM965 0x2A04 /* Mobile PM965/GM965 */ +#define PCI_DEVICE_ID_INTEL_MEI_82GME965 0x2A14 /* Mobile GME965/GLE960 */ -#define MEI_DEV_ID_ICH9_82Q35 0x29B4 /* 82Q35 Express */ -#define MEI_DEV_ID_ICH9_82G33 0x29C4 /* 82G33/G31/P35/P31 Express */ -#define MEI_DEV_ID_ICH9_82Q33 0x29D4 /* 82Q33 Express */ -#define MEI_DEV_ID_ICH9_82X38 0x29E4 /* 82X38/X48 Express */ -#define MEI_DEV_ID_ICH9_3200 0x29F4 /* 3200/3210 Server */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_82Q35 0x29B4 /* 82Q35 Express */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_82G33 0x29C4 /* 82G33/G31/P35/P31 Express */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_82Q33 0x29D4 /* 82Q33 Express */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_82X38 0x29E4 /* 82X38/X48 Express */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_3200 0x29F4 /* 3200/3210 Server */ -#define MEI_DEV_ID_ICH9_6 0x28B4 /* Bearlake */ -#define MEI_DEV_ID_ICH9_7 0x28C4 /* Bearlake */ -#define MEI_DEV_ID_ICH9_8 0x28D4 /* Bearlake */ -#define MEI_DEV_ID_ICH9_9 0x28E4 /* Bearlake */ -#define MEI_DEV_ID_ICH9_10 0x28F4 /* Bearlake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_6 0x28B4 /* Bearlake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_7 0x28C4 /* Bearlake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_8 0x28D4 /* Bearlake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_9 0x28E4 /* Bearlake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_10 0x28F4 /* Bearlake */ -#define MEI_DEV_ID_ICH9M_1 0x2A44 /* Cantiga */ -#define MEI_DEV_ID_ICH9M_2 0x2A54 /* Cantiga */ -#define MEI_DEV_ID_ICH9M_3 0x2A64 /* Cantiga */ -#define MEI_DEV_ID_ICH9M_4 0x2A74 /* Cantiga */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9M_1 0x2A44 /* Cantiga */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9M_2 0x2A54 /* Cantiga */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9M_3 0x2A64 /* Cantiga */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9M_4 0x2A74 /* Cantiga */ -#define MEI_DEV_ID_ICH10_1 0x2E04 /* Eaglelake */ -#define MEI_DEV_ID_ICH10_2 0x2E14 /* Eaglelake */ -#define MEI_DEV_ID_ICH10_3 0x2E24 /* Eaglelake */ -#define MEI_DEV_ID_ICH10_4 0x2E34 /* Eaglelake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH10_1 0x2E04 /* Eaglelake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH10_2 0x2E14 /* Eaglelake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH10_3 0x2E24 /* Eaglelake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH10_4 0x2E34 /* Eaglelake */ -#define MEI_DEV_ID_IBXPK_1 0x3B64 /* Calpella */ -#define MEI_DEV_ID_IBXPK_2 0x3B65 /* Calpella */ +#define PCI_DEVICE_ID_INTEL_MEI_IBXPK_1 0x3B64 /* Calpella */ +#define PCI_DEVICE_ID_INTEL_MEI_IBXPK_2 0x3B65 /* Calpella */ -#define MEI_DEV_ID_CPT_1 0x1C3A /* Couger Point */ -#define MEI_DEV_ID_PBG_1 0x1D3A /* C600/X79 Patsburg */ +#define PCI_DEVICE_ID_INTEL_MEI_CPT_1 0x1C3A /* Couger Point */ +#define PCI_DEVICE_ID_INTEL_MEI_PBG_1 0x1D3A /* C600/X79 Patsburg */ -#define MEI_DEV_ID_PPT_1 0x1E3A /* Panther Point */ -#define MEI_DEV_ID_PPT_2 0x1CBA /* Panther Point */ -#define MEI_DEV_ID_PPT_3 0x1DBA /* Panther Point */ +#define PCI_DEVICE_ID_INTEL_MEI_PPT_1 0x1E3A /* Panther Point */ +#define PCI_DEVICE_ID_INTEL_MEI_PPT_2 0x1CBA /* Panther Point */ +#define PCI_DEVICE_ID_INTEL_MEI_PPT_3 0x1DBA /* Panther Point */ -#define MEI_DEV_ID_LPT_H 0x8C3A /* Lynx Point H */ -#define MEI_DEV_ID_LPT_W 0x8D3A /* Lynx Point - Wellsburg */ -#define MEI_DEV_ID_LPT_LP 0x9C3A /* Lynx Point LP */ -#define MEI_DEV_ID_LPT_HR 0x8CBA /* Lynx Point H Refresh */ +#define PCI_DEVICE_ID_INTEL_MEI_LPT_H 0x8C3A /* Lynx Point H */ +#define PCI_DEVICE_ID_INTEL_MEI_LPT_W 0x8D3A /* Lynx Point - Wellsburg */ +#define PCI_DEVICE_ID_INTEL_MEI_LPT_LP 0x9C3A /* Lynx Point LP */ +#define PCI_DEVICE_ID_INTEL_MEI_LPT_HR 0x8CBA /* Lynx Point H Refresh */ -#define MEI_DEV_ID_WPT_LP 0x9CBA /* Wildcat Point LP */ -#define MEI_DEV_ID_WPT_LP_2 0x9CBB /* Wildcat Point LP 2 */ +#define PCI_DEVICE_ID_INTEL_MEI_WPT_LP 0x9CBA /* Wildcat Point LP */ +#define PCI_DEVICE_ID_INTEL_MEI_WPT_LP_2 0x9CBB /* Wildcat Point LP 2 */ -#define MEI_DEV_ID_SPT 0x9D3A /* Sunrise Point */ -#define MEI_DEV_ID_SPT_2 0x9D3B /* Sunrise Point 2 */ -#define MEI_DEV_ID_SPT_3 0x9D3E /* Sunrise Point 3 (iToutch) */ -#define MEI_DEV_ID_SPT_H 0xA13A /* Sunrise Point H */ -#define MEI_DEV_ID_SPT_H_2 0xA13B /* Sunrise Point H 2 */ +#define PCI_DEVICE_ID_INTEL_MEI_SPT 0x9D3A /* Sunrise Point */ +#define PCI_DEVICE_ID_INTEL_MEI_SPT_2 0x9D3B /* Sunrise Point 2 */ +#define PCI_DEVICE_ID_INTEL_MEI_SPT_3 0x9D3E /* Sunrise Point 3 (iToutch) */ +#define PCI_DEVICE_ID_INTEL_MEI_SPT_H 0xA13A /* Sunrise Point H */ +#define PCI_DEVICE_ID_INTEL_MEI_SPT_H_2 0xA13B /* Sunrise Point H 2 */ -#define MEI_DEV_ID_LBG 0xA1BA /* Lewisburg (SPT) */ +#define PCI_DEVICE_ID_INTEL_MEI_LBG 0xA1BA /* Lewisburg (SPT) */ -#define MEI_DEV_ID_BXT_M 0x1A9A /* Broxton M */ -#define MEI_DEV_ID_APL_I 0x5A9A /* Apollo Lake I */ +#define PCI_DEVICE_ID_INTEL_MEI_BXT_M 0x1A9A /* Broxton M */ +#define PCI_DEVICE_ID_INTEL_MEI_APL_I 0x5A9A /* Apollo Lake I */ -#define MEI_DEV_ID_DNV_IE 0x19E5 /* Denverton IE */ +#define PCI_DEVICE_ID_INTEL_MEI_DNV_IE 0x19E5 /* Denverton IE */ -#define MEI_DEV_ID_GLK 0x319A /* Gemini Lake */ +#define PCI_DEVICE_ID_INTEL_MEI_GLK 0x319A /* Gemini Lake */ -#define MEI_DEV_ID_KBP 0xA2BA /* Kaby Point */ -#define MEI_DEV_ID_KBP_2 0xA2BB /* Kaby Point 2 */ -#define MEI_DEV_ID_KBP_3 0xA2BE /* Kaby Point 3 (iTouch) */ +#define PCI_DEVICE_ID_INTEL_MEI_KBP 0xA2BA /* Kaby Point */ +#define PCI_DEVICE_ID_INTEL_MEI_KBP_2 0xA2BB /* Kaby Point 2 */ +#define PCI_DEVICE_ID_INTEL_MEI_KBP_3 0xA2BE /* Kaby Point 3 (iTouch) */ -#define MEI_DEV_ID_CNP_LP 0x9DE0 /* Cannon Point LP */ -#define MEI_DEV_ID_CNP_LP_3 0x9DE4 /* Cannon Point LP 3 (iTouch) */ -#define MEI_DEV_ID_CNP_H 0xA360 /* Cannon Point H */ -#define MEI_DEV_ID_CNP_H_3 0xA364 /* Cannon Point H 3 (iTouch) */ +#define PCI_DEVICE_ID_INTEL_MEI_CNP_LP 0x9DE0 /* Cannon Point LP */ +#define PCI_DEVICE_ID_INTEL_MEI_CNP_LP_3 0x9DE4 /* Cannon Point LP 3 (iTouch) */ +#define PCI_DEVICE_ID_INTEL_MEI_CNP_H 0xA360 /* Cannon Point H */ +#define PCI_DEVICE_ID_INTEL_MEI_CNP_H_3 0xA364 /* Cannon Point H 3 (iTouch) */ -#define MEI_DEV_ID_CMP_LP 0x02e0 /* Comet Point LP */ -#define MEI_DEV_ID_CMP_LP_3 0x02e4 /* Comet Point LP 3 (iTouch) */ +#define PCI_DEVICE_ID_INTEL_MEI_CMP_LP 0x02e0 /* Comet Point LP */ +#define PCI_DEVICE_ID_INTEL_MEI_CMP_LP_3 0x02e4 /* Comet Point LP 3 (iTouch) */ -#define MEI_DEV_ID_CMP_V 0xA3BA /* Comet Point Lake V */ +#define PCI_DEVICE_ID_INTEL_MEI_CMP_V 0xA3BA /* Comet Point Lake V */ -#define MEI_DEV_ID_CMP_H 0x06e0 /* Comet Lake H */ -#define MEI_DEV_ID_CMP_H_3 0x06e4 /* Comet Lake H 3 (iTouch) */ +#define PCI_DEVICE_ID_INTEL_MEI_CMP_H 0x06e0 /* Comet Lake H */ +#define PCI_DEVICE_ID_INTEL_MEI_CMP_H_3 0x06e4 /* Comet Lake H 3 (iTouch) */ -#define MEI_DEV_ID_CDF 0x18D3 /* Cedar Fork */ +#define PCI_DEVICE_ID_INTEL_MEI_CDF 0x18D3 /* Cedar Fork */ -#define MEI_DEV_ID_ICP_LP 0x34E0 /* Ice Lake Point LP */ -#define MEI_DEV_ID_ICP_N 0x38E0 /* Ice Lake Point N */ +#define PCI_DEVICE_ID_INTEL_MEI_ICP_LP 0x34E0 /* Ice Lake Point LP */ +#define PCI_DEVICE_ID_INTEL_MEI_ICP_N 0x38E0 /* Ice Lake Point N */ -#define MEI_DEV_ID_JSP_N 0x4DE0 /* Jasper Lake Point N */ +#define PCI_DEVICE_ID_INTEL_MEI_JSP_N 0x4DE0 /* Jasper Lake Point N */ -#define MEI_DEV_ID_TGP_LP 0xA0E0 /* Tiger Lake Point LP */ -#define MEI_DEV_ID_TGP_H 0x43E0 /* Tiger Lake Point H */ +#define PCI_DEVICE_ID_INTEL_MEI_TGP_LP 0xA0E0 /* Tiger Lake Point LP */ +#define PCI_DEVICE_ID_INTEL_MEI_TGP_H 0x43E0 /* Tiger Lake Point H */ -#define MEI_DEV_ID_MCC 0x4B70 /* Mule Creek Canyon (EHL) */ -#define MEI_DEV_ID_MCC_4 0x4B75 /* Mule Creek Canyon 4 (EHL) */ +#define PCI_DEVICE_ID_INTEL_MEI_MCC 0x4B70 /* Mule Creek Canyon (EHL) */ +#define PCI_DEVICE_ID_INTEL_MEI_MCC_4 0x4B75 /* Mule Creek Canyon 4 (EHL) */ -#define MEI_DEV_ID_EBG 0x1BE0 /* Emmitsburg WS */ +#define PCI_DEVICE_ID_INTEL_MEI_EBG 0x1BE0 /* Emmitsburg WS */ -#define MEI_DEV_ID_ADP_S 0x7AE8 /* Alder Lake Point S */ -#define MEI_DEV_ID_ADP_LP 0x7A60 /* Alder Lake Point LP */ -#define MEI_DEV_ID_ADP_P 0x51E0 /* Alder Lake Point P */ -#define MEI_DEV_ID_ADP_N 0x54E0 /* Alder Lake Point N */ +#define PCI_DEVICE_ID_INTEL_MEI_ADP_S 0x7AE8 /* Alder Lake Point S */ +#define PCI_DEVICE_ID_INTEL_MEI_ADP_LP 0x7A60 /* Alder Lake Point LP */ +#define PCI_DEVICE_ID_INTEL_MEI_ADP_P 0x51E0 /* Alder Lake Point P */ +#define PCI_DEVICE_ID_INTEL_MEI_ADP_N 0x54E0 /* Alder Lake Point N */ -#define MEI_DEV_ID_RPL_S 0x7A68 /* Raptor Lake Point S */ +#define PCI_DEVICE_ID_INTEL_MEI_RPL_S 0x7A68 /* Raptor Lake Point S */ -#define MEI_DEV_ID_MTL_M 0x7E70 /* Meteor Lake Point M */ -#define MEI_DEV_ID_ARL_S 0x7F68 /* Arrow Lake Point S */ -#define MEI_DEV_ID_ARL_H 0x7770 /* Arrow Lake Point H */ +#define PCI_DEVICE_ID_INTEL_MEI_MTL_M 0x7E70 /* Meteor Lake Point M */ +#define PCI_DEVICE_ID_INTEL_MEI_ARL_S 0x7F68 /* Arrow Lake Point S */ +#define PCI_DEVICE_ID_INTEL_MEI_ARL_H 0x7770 /* Arrow Lake Point H */ -#define MEI_DEV_ID_LNL_M 0xA870 /* Lunar Lake Point M */ +#define PCI_DEVICE_ID_INTEL_MEI_LNL_M 0xA870 /* Lunar Lake Point M */ -#define MEI_DEV_ID_PTL_H 0xE370 /* Panther Lake H */ -#define MEI_DEV_ID_PTL_P 0xE470 /* Panther Lake P */ +#define PCI_DEVICE_ID_INTEL_MEI_PTL_H 0xE370 /* Panther Lake H */ +#define PCI_DEVICE_ID_INTEL_MEI_PTL_P 0xE470 /* Panther Lake P */ -#define MEI_DEV_ID_WCL_P 0x4D70 /* Wildcat Lake P */ +#define PCI_DEVICE_ID_INTEL_MEI_WCL_P 0x4D70 /* Wildcat Lake P */ -#define MEI_DEV_ID_NVL_S 0x6E68 /* Nova Lake Point S */ +#define PCI_DEVICE_ID_INTEL_MEI_NVL_S 0x6E68 /* Nova Lake Point S */ /* * MEI HW Section diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 204b92af6c47..843ec2497b52 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -33,12 +33,6 @@ struct mei_cfg { u32 hw_trc_supported:1; }; - -#define MEI_PCI_DEVICE(dev, cfg) \ - .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ - .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \ - .driver_data = (kernel_ulong_t)(cfg), - #define MEI_ME_RPM_TIMEOUT 500 /* ms */ /** diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 2a6e569558b9..fe5d5aee074c 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -26,110 +26,110 @@ /* mei_pci_tbl - PCI Device ID Table */ static const struct pci_device_id mei_me_pci_tbl[] = { - {MEI_PCI_DEVICE(MEI_DEV_ID_82946GZ, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_82G35, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_82Q965, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_82G965, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_82GM965, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_82GME965, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q35, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82G33, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q33, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82X38, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_3200, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_82946GZ, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_82G35, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_82Q965, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_82G965, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_82GM965, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_82GME965, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q35, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82G33, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q33, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82X38, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_3200, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_6, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_7, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_8, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_9, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_10, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_1, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_2, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_3, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_4, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_6, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_7, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_8, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_9, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_10, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_1, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_2, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_3, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_4, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_1, MEI_ME_ICH10_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_2, MEI_ME_ICH10_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_3, MEI_ME_ICH10_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_4, MEI_ME_ICH10_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH10_1, MEI_ME_ICH10_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH10_2, MEI_ME_ICH10_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH10_3, MEI_ME_ICH10_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH10_4, MEI_ME_ICH10_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_1, MEI_ME_PCH6_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_2, MEI_ME_PCH6_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CPT_1, MEI_ME_PCH_CPT_PBG_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PBG_1, MEI_ME_PCH_CPT_PBG_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_1, MEI_ME_PCH7_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_2, MEI_ME_PCH7_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_3, MEI_ME_PCH7_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_H, MEI_ME_PCH8_SPS_4_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_W, MEI_ME_PCH8_SPS_4_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_LP, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_HR, MEI_ME_PCH8_SPS_4_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP_2, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_IBXPK_1, MEI_ME_PCH6_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_IBXPK_2, MEI_ME_PCH6_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CPT_1, MEI_ME_PCH_CPT_PBG_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_PBG_1, MEI_ME_PCH_CPT_PBG_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_PPT_1, MEI_ME_PCH7_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_PPT_2, MEI_ME_PCH7_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_PPT_3, MEI_ME_PCH7_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_LPT_H, MEI_ME_PCH8_SPS_4_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_LPT_W, MEI_ME_PCH8_SPS_4_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_LPT_LP, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_LPT_HR, MEI_ME_PCH8_SPS_4_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_WPT_LP, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_WPT_LP_2, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_3, MEI_ME_PCH8_ITOUCH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, MEI_ME_PCH8_SPS_4_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, MEI_ME_PCH8_SPS_4_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LBG, MEI_ME_PCH12_SPS_4_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_SPT, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_SPT_2, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_SPT_3, MEI_ME_PCH8_ITOUCH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_SPT_H, MEI_ME_PCH8_SPS_4_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_SPT_H_2, MEI_ME_PCH8_SPS_4_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_LBG, MEI_ME_PCH12_SPS_4_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_APL_I, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_BXT_M, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_APL_I, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_DNV_IE, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_DNV_IE, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_GLK, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_GLK, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_KBP, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_3, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_KBP, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_KBP_2, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_KBP_3, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_LP, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H, MEI_ME_PCH12_SPS_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H_3, MEI_ME_PCH12_SPS_ITOUCH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CNP_LP, MEI_ME_PCH12_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CNP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CNP_H, MEI_ME_PCH12_SPS_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CNP_H_3, MEI_ME_PCH12_SPS_ITOUCH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_V, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_H, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CMP_LP, MEI_ME_PCH12_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CMP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CMP_V, MEI_ME_PCH12_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CMP_H, MEI_ME_PCH12_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_N, MEI_ME_PCH12_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICP_LP, MEI_ME_PCH12_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICP_N, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_TGP_LP, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_TGP_H, MEI_ME_PCH15_SPS_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_TGP_LP, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_TGP_H, MEI_ME_PCH15_SPS_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_JSP_N, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_JSP_N, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_MCC, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_MCC_4, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_MCC, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_MCC_4, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CDF, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CDF, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_EBG, MEI_ME_PCH15_SPS_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_EBG, MEI_ME_PCH15_SPS_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_S, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_LP, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_P, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_N, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ADP_S, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ADP_LP, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ADP_P, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ADP_N, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_RPL_S, MEI_ME_PCH15_SPS_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_RPL_S, MEI_ME_PCH15_SPS_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_MTL_M, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ARL_S, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ARL_H, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_MTL_M, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ARL_S, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ARL_H, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LNL_M, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_LNL_M, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PTL_H, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PTL_P, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_PTL_H, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_PTL_P, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_WCL_P, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_WCL_P, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_NVL_S, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_NVL_S, MEI_ME_PCH15_CFG)}, /* required last entry */ {0, } From f3d0423d4150614c0f6b68c307daa1a6b29730ea Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 1 Feb 2026 11:43:53 +0200 Subject: [PATCH 394/405] mei: fix idle print specifiers %01d is equal to %d, simplify the format. Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260201094358.1440593-3-alexander.usyskin@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index b789c4d9c709..f54991b40fc7 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -348,7 +348,7 @@ bool mei_write_is_idle(struct mei_device *dev) list_empty(&dev->write_list) && list_empty(&dev->write_waiting_list)); - dev_dbg(&dev->dev, "write pg: is idle[%d] state=%s ctrl=%01d write=%01d wwait=%01d\n", + dev_dbg(&dev->dev, "write pg: is idle[%d] state=%s ctrl=%d write=%d wwait=%d\n", idle, mei_dev_state_str(dev->dev_state), list_empty(&dev->ctrl_wr_list), From bf025a8447796e51b944ec87ac96f680fa4022e1 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 1 Feb 2026 11:43:54 +0200 Subject: [PATCH 395/405] mei: me: move trace into firmware status read Move register trace near it actual read in the firmware status callback and make it adhere to the actual read type. Reviewed-by: Mika Westerberg Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260201094358.1440593-4-alexander.usyskin@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/gsc-me.c | 3 ++- drivers/misc/mei/hw-me.c | 11 +++-------- drivers/misc/mei/hw-me.h | 2 +- drivers/misc/mei/pci-me.c | 8 ++++++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/misc/mei/gsc-me.c b/drivers/misc/mei/gsc-me.c index 93cba090ea08..73d5beeb9c34 100644 --- a/drivers/misc/mei/gsc-me.c +++ b/drivers/misc/mei/gsc-me.c @@ -23,11 +23,12 @@ #define MEI_GSC_RPM_TIMEOUT 500 -static int mei_gsc_read_hfs(const struct mei_device *dev, int where, u32 *val) +static int mei_gsc_read_hfs(const struct mei_device *dev, int where, const char *name, u32 *val) { struct mei_me_hw *hw = to_me_hw(dev); *val = ioread32(hw->mem_addr + where + 0xC00); + trace_mei_reg_read(&dev->dev, name, where, *val); return 0; } diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index d4612c659784..c0d4a02d9cae 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -215,11 +215,8 @@ static int mei_me_fw_status(struct mei_device *dev, fw_status->count = fw_src->count; for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) { - ret = hw->read_fws(dev, fw_src->status[i], + ret = hw->read_fws(dev, fw_src->status[i], "PCI_CFG_HFS_X", &fw_status->status[i]); - trace_mei_pci_cfg_read(&dev->dev, "PCI_CFG_HFS_X", - fw_src->status[i], - fw_status->status[i]); if (ret) return ret; } @@ -250,8 +247,7 @@ static int mei_me_hw_config(struct mei_device *dev) hw->hbuf_depth = (hcsr & H_CBD) >> 24; reg = 0; - hw->read_fws(dev, PCI_CFG_HFS_1, ®); - trace_mei_pci_cfg_read(&dev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg); + hw->read_fws(dev, PCI_CFG_HFS_1, "PCI_CFG_HFS_1", ®); hw->d0i3_supported = ((reg & PCI_CFG_HFS_1_D0I3_MSK) == PCI_CFG_HFS_1_D0I3_MSK); @@ -446,8 +442,7 @@ static void mei_gsc_pxp_check(struct mei_device *dev) if (!kind_is_gsc(dev) && !kind_is_gscfi(dev)) return; - hw->read_fws(dev, PCI_CFG_HFS_5, &fwsts5); - trace_mei_pci_cfg_read(&dev->dev, "PCI_CFG_HFS_5", PCI_CFG_HFS_5, fwsts5); + hw->read_fws(dev, PCI_CFG_HFS_5, "PCI_CFG_HFS_5", &fwsts5); if ((fwsts5 & GSC_CFG_HFS_5_BOOT_TYPE_MSK) == GSC_CFG_HFS_5_BOOT_TYPE_PXP) { if (dev->gsc_reset_to_pxp == MEI_DEV_RESET_TO_PXP_DEFAULT) diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 843ec2497b52..9df5899d2602 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -56,7 +56,7 @@ struct mei_me_hw { enum mei_pg_state pg_state; bool d0i3_supported; u8 hbuf_depth; - int (*read_fws)(const struct mei_device *dev, int where, u32 *val); + int (*read_fws)(const struct mei_device *dev, int where, const char *name, u32 *val); /* polling */ struct task_struct *polling_thread; wait_queue_head_t wait_active; diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index fe5d5aee074c..b4c9526857bb 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -23,6 +23,7 @@ #include "client.h" #include "hw-me-regs.h" #include "hw-me.h" +#include "mei-trace.h" /* mei_pci_tbl - PCI Device ID Table */ static const struct pci_device_id mei_me_pci_tbl[] = { @@ -145,11 +146,14 @@ static inline void mei_me_set_pm_domain(struct mei_device *dev) {} static inline void mei_me_unset_pm_domain(struct mei_device *dev) {} #endif /* CONFIG_PM */ -static int mei_me_read_fws(const struct mei_device *dev, int where, u32 *val) +static int mei_me_read_fws(const struct mei_device *dev, int where, const char *name, u32 *val) { struct pci_dev *pdev = to_pci_dev(dev->parent); + int ret; - return pci_read_config_dword(pdev, where, val); + ret = pci_read_config_dword(pdev, where, val); + trace_mei_pci_cfg_read(&dev->dev, name, where, *val); + return ret; } /** From eb1b5fc76a940ce309ecc57dbc465dda09171229 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 1 Feb 2026 11:43:55 +0200 Subject: [PATCH 396/405] mei: trace: print return value of pci_cfg_read Extend debug capabilities. Add return value print in the trace_mei_pci_cfg_read(). Reviewed-by: Andy Shevchenko Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260201094358.1440593-5-alexander.usyskin@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 15 +++++++++------ drivers/misc/mei/hw-txe.c | 2 +- drivers/misc/mei/mei-trace.h | 10 ++++++---- drivers/misc/mei/pci-me.c | 2 +- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index c0d4a02d9cae..72a7cfb2989f 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1505,10 +1505,11 @@ static bool mei_me_fw_type_nm(const struct pci_dev *pdev) { u32 reg; unsigned int devfn; + int ret; devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0); - pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_2, ®); - trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_2", PCI_CFG_HFS_2, reg); + ret = pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_2, ®); + trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_2", PCI_CFG_HFS_2, reg, ret); /* make sure that bit 9 (NM) is up and bit 10 (DM) is down */ return (reg & 0x600) == 0x200; } @@ -1531,10 +1532,11 @@ static bool mei_me_fw_type_sps_4(const struct pci_dev *pdev) { u32 reg; unsigned int devfn; + int ret; devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0); - pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_1, ®); - trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg); + ret = pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_1, ®); + trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg, ret); return (reg & PCI_CFG_HFS_1_OPMODE_MSK) == PCI_CFG_HFS_1_OPMODE_SPS; } @@ -1556,10 +1558,11 @@ static bool mei_me_fw_type_sps_ign(const struct pci_dev *pdev) u32 reg; u32 fw_type; unsigned int devfn; + int ret; devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0); - pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_3, ®); - trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_3", PCI_CFG_HFS_3, reg); + ret = pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_3, ®); + trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_3", PCI_CFG_HFS_3, reg, ret); fw_type = (reg & PCI_CFG_HFS_3_FW_SKU_MSK); dev_dbg(&pdev->dev, "fw type is %d\n", fw_type); diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index e4688c391027..008cb1ede56c 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -651,7 +651,7 @@ static int mei_txe_fw_status(struct mei_device *dev, &fw_status->status[i]); trace_mei_pci_cfg_read(&dev->dev, "PCI_CFG_HSF_X", fw_src->status[i], - fw_status->status[i]); + fw_status->status[i], ret); if (ret) return ret; } diff --git a/drivers/misc/mei/mei-trace.h b/drivers/misc/mei/mei-trace.h index 24fa321d88bd..fa5224e5353a 100644 --- a/drivers/misc/mei/mei-trace.h +++ b/drivers/misc/mei/mei-trace.h @@ -55,22 +55,24 @@ TRACE_EVENT(mei_reg_write, ); TRACE_EVENT(mei_pci_cfg_read, - TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val), - TP_ARGS(dev, reg, offs, val), + TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val, int ret), + TP_ARGS(dev, reg, offs, val, ret), TP_STRUCT__entry( __string(dev, dev_name(dev)) __string(reg, reg) __field(u32, offs) __field(u32, val) + __field(int, ret) ), TP_fast_assign( __assign_str(dev); __assign_str(reg); __entry->offs = offs; __entry->val = val; + __entry->ret = ret; ), - TP_printk("[%s] pci cfg read %s:[%#x] = %#x", - __get_str(dev), __get_str(reg), __entry->offs, __entry->val) + TP_printk("[%s] pci cfg read %s:[%#x] = %#x, ret = %d", + __get_str(dev), __get_str(reg), __entry->offs, __entry->val, __entry->ret) ); #endif /* _MEI_TRACE_H_ */ diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index b4c9526857bb..a75773cc8fb7 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -152,7 +152,7 @@ static int mei_me_read_fws(const struct mei_device *dev, int where, const char * int ret; ret = pci_read_config_dword(pdev, where, val); - trace_mei_pci_cfg_read(&dev->dev, name, where, *val); + trace_mei_pci_cfg_read(&dev->dev, name, where, *val, ret); return ret; } From 60ca15971a9aa0647d20488b450d095c81c5b70a Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 1 Feb 2026 11:43:56 +0200 Subject: [PATCH 397/405] mei: convert PCI error to common errno Ensure that callers receive only < 0 return value on error. Convert PCI error returned by pci_read_config_dword() to common errno before returning from function. Reviewed-by: Mika Westerberg Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260201094358.1440593-6-alexander.usyskin@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-txe.c | 2 +- drivers/misc/mei/pci-me.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index 008cb1ede56c..a83de653c603 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -653,7 +653,7 @@ static int mei_txe_fw_status(struct mei_device *dev, fw_src->status[i], fw_status->status[i], ret); if (ret) - return ret; + return pcibios_err_to_errno(ret); } return 0; diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index a75773cc8fb7..8d16bfa6027c 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -153,7 +153,7 @@ static int mei_me_read_fws(const struct mei_device *dev, int where, const char * ret = pci_read_config_dword(pdev, where, val); trace_mei_pci_cfg_read(&dev->dev, name, where, *val, ret); - return ret; + return pcibios_err_to_errno(ret); } /** From 72fdf0bbd3574a67148fb41473593196687a9a0e Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 1 Feb 2026 11:43:57 +0200 Subject: [PATCH 398/405] mei: csc: support controller with separate PCI device Intel PCI driver for chassis controller embedded in Intel graphics devices. An MEI device here called CSC can be embedded in discrete Intel graphics devices having separate PCI device, to support a range of chassis tasks such as graphics card firmware update and security tasks. Reviewed-by: Mika Westerberg Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260201094358.1440593-7-alexander.usyskin@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/Kconfig | 11 ++ drivers/misc/mei/Makefile | 3 + drivers/misc/mei/hw-me-regs.h | 3 + drivers/misc/mei/hw-me.c | 27 ++++ drivers/misc/mei/hw-me.h | 2 + drivers/misc/mei/init.c | 4 +- drivers/misc/mei/mei_dev.h | 3 + drivers/misc/mei/pci-csc.c | 259 ++++++++++++++++++++++++++++++++++ 8 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 drivers/misc/mei/pci-csc.c diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index 5902dd1ee44b..8d192c3a1d59 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -58,6 +58,17 @@ config INTEL_MEI_GSC tasks such as graphics card firmware update and security tasks. +config INTEL_MEI_CSC + tristate "Intel MEI CSC embedded device" + depends on INTEL_MEI_ME + help + Intel PCI driver for the chassis controller embedded in Intel graphics devices. + + An MEI device here called CSC can be embedded in discrete + Intel graphics devices, to support a range of chassis + tasks such as graphics card firmware update and security + tasks. + config INTEL_MEI_VSC_HW tristate "Intel visual sensing controller device transport driver" depends on ACPI && SPI diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index a203ed766b33..9a6aa335921e 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -21,6 +21,9 @@ mei-me-objs += hw-me.o obj-$(CONFIG_INTEL_MEI_GSC) += mei-gsc.o mei-gsc-objs := gsc-me.o +obj-$(CONFIG_INTEL_MEI_CSC) += mei-csc.o +mei-csc-objs := pci-csc.o + obj-$(CONFIG_INTEL_MEI_TXE) += mei-txe.o mei-txe-objs := pci-txe.o mei-txe-objs += hw-txe.o diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 840e1fd2714c..9b1675f98404 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -124,6 +124,8 @@ #define PCI_DEVICE_ID_INTEL_MEI_NVL_S 0x6E68 /* Nova Lake Point S */ +#define PCI_DEVICE_ID_INTEL_MEI_CRI 0x6766 /* Crescent Island */ + /* * MEI HW Section */ @@ -134,6 +136,7 @@ # define PCI_CFG_HFS_1_OPMODE_MSK 0xf0000 /* OP MODE Mask: SPS <= 4.0 */ # define PCI_CFG_HFS_1_OPMODE_SPS 0xf0000 /* SPS SKU : SPS <= 4.0 */ #define PCI_CFG_HFS_2 0x48 +# define PCI_CFG_HFS_2_D3_BLOCK BIT(7) # define PCI_CFG_HFS_2_PM_CMOFF_TO_CMX_ERROR 0x1000000 /* CMoff->CMx wake after an error */ # define PCI_CFG_HFS_2_PM_CM_RESET_ERROR 0x5000000 /* CME reset due to exception */ # define PCI_CFG_HFS_2_PM_EVENT_MASK 0xf000000 diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 72a7cfb2989f..3412a7b5b0e8 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -224,6 +224,15 @@ static int mei_me_fw_status(struct mei_device *dev, return 0; } +static bool mei_csc_pg_blocked(struct mei_device *dev) +{ + struct mei_me_hw *hw = to_me_hw(dev); + u32 reg = 0; + + hw->read_fws(dev, PCI_CFG_HFS_2, "PCI_CFG_HFS_2", ®); + return (reg & PCI_CFG_HFS_2_D3_BLOCK) == PCI_CFG_HFS_2_D3_BLOCK; +} + /** * mei_me_hw_config - configure hw dependent settings * @@ -1206,6 +1215,7 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) return ret; } else { hw->pg_state = MEI_PG_OFF; + dev->pg_blocked = mei_csc_pg_blocked(dev); } } @@ -1294,6 +1304,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) { struct mei_device *dev = (struct mei_device *) dev_id; struct list_head cmpl_list; + bool pg_blocked; s32 slots; u32 hcsr; int rets = 0; @@ -1351,6 +1362,14 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) } goto end; } + + pg_blocked = mei_csc_pg_blocked(dev); + if (pg_blocked && !dev->pg_blocked) /* PG block requested */ + pm_request_resume(&dev->dev); + else if (!pg_blocked && dev->pg_blocked) /* PG block lifted */ + pm_request_autosuspend(&dev->dev); + dev->pg_blocked = pg_blocked; + /* check slots available for reading */ slots = mei_count_full_read_slots(dev); while (slots > 0) { @@ -1726,6 +1745,13 @@ static const struct mei_cfg mei_me_gscfi_cfg = { MEI_CFG_FW_VER_SUPP, }; +/* Chassis System Controller Firmware Interface */ +static const struct mei_cfg mei_me_csc_cfg = { + MEI_CFG_TYPE_GSCFI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, +}; + /* * mei_cfg_list - A list of platform platform specific configurations. * Note: has to be synchronized with enum mei_cfg_idx. @@ -1748,6 +1774,7 @@ static const struct mei_cfg *const mei_cfg_list[] = { [MEI_ME_PCH15_SPS_CFG] = &mei_me_pch15_sps_cfg, [MEI_ME_GSC_CFG] = &mei_me_gsc_cfg, [MEI_ME_GSCFI_CFG] = &mei_me_gscfi_cfg, + [MEI_ME_CSC_CFG] = &mei_me_csc_cfg, }; const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx) diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 9df5899d2602..8da8662a9d61 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -104,6 +104,7 @@ static inline bool mei_me_hw_use_polling(const struct mei_me_hw *hw) * SPS firmware exclusion. * @MEI_ME_GSC_CFG: Graphics System Controller * @MEI_ME_GSCFI_CFG: Graphics System Controller Firmware Interface + * @MEI_ME_CSC_CFG: Chassis System Controller Firmware Interface * @MEI_ME_NUM_CFG: Upper Sentinel. */ enum mei_cfg_idx { @@ -124,6 +125,7 @@ enum mei_cfg_idx { MEI_ME_PCH15_SPS_CFG, MEI_ME_GSC_CFG, MEI_ME_GSCFI_CFG, + MEI_ME_CSC_CFG, MEI_ME_NUM_CFG, }; diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index f54991b40fc7..766f119f7ed0 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -344,13 +344,15 @@ EXPORT_SYMBOL_GPL(mei_stop); bool mei_write_is_idle(struct mei_device *dev) { bool idle = (dev->dev_state == MEI_DEV_ENABLED && + !dev->pg_blocked && list_empty(&dev->ctrl_wr_list) && list_empty(&dev->write_list) && list_empty(&dev->write_waiting_list)); - dev_dbg(&dev->dev, "write pg: is idle[%d] state=%s ctrl=%d write=%d wwait=%d\n", + dev_dbg(&dev->dev, "write pg: is idle[%d] state=%s blocked=%d ctrl=%d write=%d wwait=%d\n", idle, mei_dev_state_str(dev->dev_state), + dev->pg_blocked, list_empty(&dev->ctrl_wr_list), list_empty(&dev->write_list), list_empty(&dev->write_waiting_list)); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 0bf8d552c3ea..1796c6793a94 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -490,6 +490,7 @@ struct mei_dev_timeouts { * @timer_work : MEI timer delayed work (timeouts) * * @recvd_hw_ready : hw ready message received flag + * @pg_blocked : low power mode is not allowed * * @wait_hw_ready : wait queue for receive HW ready message form FW * @wait_pg : wait queue for receive PG message from FW @@ -575,6 +576,8 @@ struct mei_device { struct delayed_work timer_work; bool recvd_hw_ready; + bool pg_blocked; + /* * waiting queue for receive message from FW */ diff --git a/drivers/misc/mei/pci-csc.c b/drivers/misc/mei/pci-csc.c new file mode 100644 index 000000000000..15e170b1e0b6 --- /dev/null +++ b/drivers/misc/mei/pci-csc.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025-2026, Intel Corporation. All rights reserved. + * Intel Management Engine Interface (Intel MEI) Linux driver + * for CSC platforms. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client.h" +#include "hw-me-regs.h" +#include "hw-me.h" +#include "mei_dev.h" +#include "mei-trace.h" + +#define MEI_CSC_HECI2_OFFSET 0x1000 + +static int mei_csc_read_fws(const struct mei_device *mdev, int where, const char *name, u32 *val) +{ + struct mei_me_hw *hw = to_me_hw(mdev); + + *val = ioread32(hw->mem_addr + where + 0xC00); + trace_mei_reg_read(&mdev->dev, name, where, *val); + return 0; +} + +static int mei_csc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct device *dev = &pdev->dev; + const struct mei_cfg *cfg; + char __iomem *registers; + struct mei_device *mdev; + struct mei_me_hw *hw; + int err; + + cfg = mei_me_get_cfg(ent->driver_data); + if (!cfg) + return -ENODEV; + + err = pcim_enable_device(pdev); + if (err) + return dev_err_probe(dev, err, "Failed to enable PCI device.\n"); + + pci_set_master(pdev); + + registers = pcim_iomap_region(pdev, 0, KBUILD_MODNAME); + if (IS_ERR(registers)) + return dev_err_probe(dev, PTR_ERR(registers), "Failed to get PCI region.\n"); + + err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (err) + return dev_err_probe(dev, err, "No usable DMA configuration.\n"); + + /* allocates and initializes the mei dev structure */ + mdev = mei_me_dev_init(dev, cfg, false); + if (!mdev) + return -ENOMEM; + + hw = to_me_hw(mdev); + + /* + * Both HECI1 and HECI2 are on this device, but only HECI2 is supported. + */ + hw->mem_addr = registers + MEI_CSC_HECI2_OFFSET; + hw->read_fws = mei_csc_read_fws; + + /* + * mei_register() assumes ownership of mdev. + * No need to release it explicitly in error path. + */ + err = mei_register(mdev, dev); + if (err) + return err; + + pci_set_drvdata(pdev, mdev); + + err = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_INTX | PCI_IRQ_MSI); + if (err < 0) { + dev_err_probe(dev, err, "Failed to allocate IRQ.\n"); + goto err_mei_unreg; + } + + hw->irq = pci_irq_vector(pdev, 0); + + /* request and enable interrupt */ + err = request_threaded_irq(hw->irq, + mei_me_irq_quick_handler, mei_me_irq_thread_handler, + IRQF_SHARED | IRQF_ONESHOT, KBUILD_MODNAME, mdev); + if (err) + goto err_free_irq_vectors; + + /* + * Continue to char device setup in spite of firmware handshake failure. + * In order to provide access to the firmware status registers to the user + * space via sysfs. The firmware status registers required to understand + * firmware error state and possible recovery flow. + */ + if (mei_start(mdev)) + dev_warn(dev, "Failed to initialize HECI hardware.\n"); + + pm_runtime_set_autosuspend_delay(dev, MEI_ME_RPM_TIMEOUT); + pm_runtime_use_autosuspend(dev); + + /* + * MEI requires to resume from runtime suspend mode + * in order to perform link reset flow upon system suspend. + */ + dev_pm_set_driver_flags(dev, DPM_FLAG_NO_DIRECT_COMPLETE); + + pm_runtime_allow(dev); + pm_runtime_put_noidle(dev); + + return 0; + +err_free_irq_vectors: + pci_free_irq_vectors(pdev); +err_mei_unreg: + mei_deregister(mdev); + return err; +} + +static void mei_csc_shutdown(struct pci_dev *pdev) +{ + struct mei_device *mdev = pci_get_drvdata(pdev); + struct mei_me_hw *hw = to_me_hw(mdev); + + pm_runtime_get_noresume(&pdev->dev); + + mei_stop(mdev); + + mei_disable_interrupts(mdev); + free_irq(hw->irq, mdev); + pci_free_irq_vectors(pdev); +} + +static void mei_csc_remove(struct pci_dev *pdev) +{ + struct mei_device *mdev = pci_get_drvdata(pdev); + + mei_csc_shutdown(pdev); + + mei_deregister(mdev); +} + +static int mei_csc_pci_prepare(struct device *dev) +{ + pm_runtime_resume(dev); + return 0; +} + +static int mei_csc_pci_suspend(struct device *dev) +{ + struct mei_device *mdev = dev_get_drvdata(dev); + + mei_stop(mdev); + + mei_disable_interrupts(mdev); + + return 0; +} + +static int mei_csc_pci_resume(struct device *dev) +{ + struct mei_device *mdev = dev_get_drvdata(dev); + int err; + + err = mei_restart(mdev); + if (err) + return err; + + /* Start timer if stopped in suspend */ + schedule_delayed_work(&mdev->timer_work, HZ); + + return 0; +} + +static void mei_csc_pci_complete(struct device *dev) +{ + pm_runtime_suspend(dev); +} + +static int mei_csc_pm_runtime_idle(struct device *dev) +{ + struct mei_device *mdev = dev_get_drvdata(dev); + + return mei_write_is_idle(mdev) ? 0 : -EBUSY; +} + +static int mei_csc_pm_runtime_suspend(struct device *dev) +{ + struct mei_device *mdev = dev_get_drvdata(dev); + struct mei_me_hw *hw = to_me_hw(mdev); + + guard(mutex)(&mdev->device_lock); + + if (!mei_write_is_idle(mdev)) + return -EAGAIN; + + hw->pg_state = MEI_PG_ON; + return 0; +} + +static int mei_csc_pm_runtime_resume(struct device *dev) +{ + struct mei_device *mdev = dev_get_drvdata(dev); + struct mei_me_hw *hw = to_me_hw(mdev); + irqreturn_t irq_ret; + + scoped_guard(mutex, &mdev->device_lock) + hw->pg_state = MEI_PG_OFF; + + /* Process all queues that wait for resume */ + irq_ret = mei_me_irq_thread_handler(1, mdev); + if (irq_ret != IRQ_HANDLED) + dev_err(dev, "thread handler fail %d\n", irq_ret); + + return 0; +} + +static const struct dev_pm_ops mei_csc_pm_ops = { + .prepare = pm_sleep_ptr(mei_csc_pci_prepare), + .complete = pm_sleep_ptr(mei_csc_pci_complete), + SYSTEM_SLEEP_PM_OPS(mei_csc_pci_suspend, mei_csc_pci_resume) + RUNTIME_PM_OPS(mei_csc_pm_runtime_suspend, + mei_csc_pm_runtime_resume, mei_csc_pm_runtime_idle) +}; + +static const struct pci_device_id mei_csc_pci_tbl[] = { + { PCI_DEVICE_DATA(INTEL, MEI_CRI, MEI_ME_CSC_CFG) }, + {} +}; +MODULE_DEVICE_TABLE(pci, mei_csc_pci_tbl); + +static struct pci_driver mei_csc_driver = { + .name = KBUILD_MODNAME, + .id_table = mei_csc_pci_tbl, + .probe = mei_csc_probe, + .remove = mei_csc_remove, + .shutdown = mei_csc_shutdown, + .driver = { + .pm = &mei_csc_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + } +}; +module_pci_driver(mei_csc_driver); + +MODULE_DESCRIPTION("Intel(R) Management Engine Interface for discrete graphics (CSC)"); +MODULE_LICENSE("GPL"); From 0a18c3bc8d294f1f23daa9f9ee44e5dd2c2994d6 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 1 Feb 2026 11:43:58 +0200 Subject: [PATCH 399/405] mei: csc: wake device while reading firmware status The CSC has firmware status registers in MMIO and they may be unaccessible while device is suspended. Wake device while reading firmware status via sysfs. Reviewed-by: Mika Westerberg Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260201094358.1440593-8-alexander.usyskin@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/main.c | 18 ++++++++++++++---- drivers/misc/mei/mei_dev.h | 2 ++ drivers/misc/mei/pci-csc.c | 2 ++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 6f26d5160788..54f70f513482 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -4,6 +4,7 @@ * Intel Management Engine Interface (Intel MEI) Linux driver */ +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -982,14 +984,22 @@ static DEVICE_ATTR_RO(trc); static ssize_t fw_status_show(struct device *device, struct device_attribute *attr, char *buf) { - struct mei_device *dev = dev_get_drvdata(device); + struct mei_device *mdev = dev_get_drvdata(device); struct mei_fw_status fw_status; int err, i; ssize_t cnt = 0; - mutex_lock(&dev->device_lock); - err = mei_fw_status(dev, &fw_status); - mutex_unlock(&dev->device_lock); + if (mdev->read_fws_need_resume) { + err = pm_runtime_resume_and_get(mdev->parent); + if (err) { + dev_err(device, "read fw_status resume error = %d\n", err); + return err; + } + } + scoped_guard(mutex, &mdev->device_lock) + err = mei_fw_status(mdev, &fw_status); + if (mdev->read_fws_need_resume) + pm_runtime_put_autosuspend(mdev->parent); if (err) { dev_err(device, "read fw_status error = %d\n", err); return err; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 1796c6793a94..d8634a726990 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -491,6 +491,7 @@ struct mei_dev_timeouts { * * @recvd_hw_ready : hw ready message received flag * @pg_blocked : low power mode is not allowed + * @read_fws_need_resume: the FW status handler needs HW woken from sleep * * @wait_hw_ready : wait queue for receive HW ready message form FW * @wait_pg : wait queue for receive PG message from FW @@ -577,6 +578,7 @@ struct mei_device { bool recvd_hw_ready; bool pg_blocked; + bool read_fws_need_resume; /* * waiting queue for receive message from FW diff --git a/drivers/misc/mei/pci-csc.c b/drivers/misc/mei/pci-csc.c index 15e170b1e0b6..70792bf9b3c0 100644 --- a/drivers/misc/mei/pci-csc.c +++ b/drivers/misc/mei/pci-csc.c @@ -67,6 +67,8 @@ static int mei_csc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (!mdev) return -ENOMEM; + mdev->read_fws_need_resume = true; + hw = to_me_hw(mdev); /* From ac12b852b4ead4a586299c8f68cdcbcaf1bf6cbc Mon Sep 17 00:00:00 2001 From: Daniele Palmas Date: Mon, 23 Mar 2026 13:28:37 +0100 Subject: [PATCH 400/405] bus: mhi: host: pci_generic: Add Telit FE912C04 modem support Add SDX35 based modem Telit FE912C04, reusing FN920C04 configuration. 01:00.0 Unassigned class [ff00]: Qualcomm Device 011a Subsystem: Device 1c5d:2045 Signed-off-by: Daniele Palmas Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260323122837.3406521-1-dnlplm@gmail.com --- drivers/bus/mhi/host/pci_generic.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 391ab146f501..750da3dbb4c6 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -904,6 +904,16 @@ static const struct mhi_pci_dev_info mhi_telit_fe990b40_info = { .edl_trigger = true, }; +static const struct mhi_pci_dev_info mhi_telit_fe912c04_info = { + .name = "telit-fe912c04", + .config = &modem_telit_fn920c04_config, + .bar_num = MHI_PCI_DEFAULT_BAR_NUM, + .dma_data_width = 32, + .sideband_wake = false, + .mru_default = 32768, + .edl_trigger = true, +}; + static const struct mhi_pci_dev_info mhi_netprisma_lcur57_info = { .name = "netprisma-lcur57", .edl = "qcom/prog_firehose_sdx24.mbn", @@ -931,6 +941,9 @@ static const struct pci_device_id mhi_pci_id_table[] = { /* Telit FN920C04 (sdx35) */ {PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x011a, 0x1c5d, 0x2020), .driver_data = (kernel_ulong_t) &mhi_telit_fn920c04_info }, + /* Telit FE912C04 (sdx35) */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x011a, 0x1c5d, 0x2045), + .driver_data = (kernel_ulong_t) &mhi_telit_fe912c04_info }, { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x011a), .driver_data = (kernel_ulong_t) &mhi_qcom_sdx35_info }, { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0304), From 94e731cbe84533a37701b4089b685d39e584fbea Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 3 Apr 2026 10:44:51 +0200 Subject: [PATCH 401/405] w1: ds2490: drop redundant device reference Driver core holds a reference to the USB interface and its parent USB device while the interface is bound to a driver and there is no need to take additional references unless the structures are needed after disconnect. Drop the redundant device reference to reduce cargo culting, make it easier to spot drivers where an extra reference is needed, and reduce the risk of memory leaks when drivers fail to release it. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260305111613.18546-1-johan@kernel.org Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260403084450.6314-2-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/ds2490.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c index 35e75d07ef5d..aa1f57f74397 100644 --- a/drivers/w1/masters/ds2490.c +++ b/drivers/w1/masters/ds2490.c @@ -1022,11 +1022,8 @@ static int ds_probe(struct usb_interface *intf, if (!dev) return -ENOMEM; - dev->udev = usb_get_dev(udev); - if (!dev->udev) { - err = -ENOMEM; - goto err_out_free; - } + dev->udev = udev; + memset(dev->ep, 0, sizeof(dev->ep)); usb_set_intfdata(intf, dev); @@ -1085,9 +1082,8 @@ static int ds_probe(struct usb_interface *intf, err_out_clear: usb_set_intfdata(intf, NULL); - usb_put_dev(dev->udev); -err_out_free: kfree(dev); + return err; } @@ -1107,7 +1103,6 @@ static void ds_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); - usb_put_dev(dev->udev); kfree(dev); } From 3031b76d65e14a946cfb5000d79b642f58ffac5c Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 5 Apr 2026 14:23:25 +0300 Subject: [PATCH 402/405] mei: bus: add mei_cldev_uuid Add mei_cldev_uuid API on mei bus to allow client to query what UUID it bound to. Reviewed-by: Andy Shevchenko Reviewed-by: Badal Nilawar Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260405112326.1535208-2-alexander.usyskin@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 13 +++++++++++++ include/linux/mei_cl_bus.h | 1 + 2 files changed, 14 insertions(+) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index f739dbcdb04c..fcde082eb5e3 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -601,6 +601,19 @@ void mei_cldev_set_drvdata(struct mei_cl_device *cldev, void *data) } EXPORT_SYMBOL_GPL(mei_cldev_set_drvdata); +/** + * mei_cldev_uuid - return uuid of the underlying me client + * + * @cldev: mei client device + * + * Return: me client uuid + */ +const uuid_le *mei_cldev_uuid(const struct mei_cl_device *cldev) +{ + return mei_me_cl_uuid(cldev->me_cl); +} +EXPORT_SYMBOL_GPL(mei_cldev_uuid); + /** * mei_cldev_ver - return protocol version of the underlying me client * diff --git a/include/linux/mei_cl_bus.h b/include/linux/mei_cl_bus.h index a82755e1fc40..5bdbd9e1d460 100644 --- a/include/linux/mei_cl_bus.h +++ b/include/linux/mei_cl_bus.h @@ -112,6 +112,7 @@ int mei_cldev_register_rx_cb(struct mei_cl_device *cldev, mei_cldev_cb_t rx_cb); int mei_cldev_register_notif_cb(struct mei_cl_device *cldev, mei_cldev_cb_t notif_cb); +const uuid_le *mei_cldev_uuid(const struct mei_cl_device *cldev); u8 mei_cldev_ver(const struct mei_cl_device *cldev); size_t mei_cldev_mtu(const struct mei_cl_device *cldev); From 773a43b8627f54dca56d08949497014b4ee8878a Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 5 Apr 2026 14:23:26 +0300 Subject: [PATCH 403/405] mei: lb: add late binding version 2 The second Late Binding version allows to send payload bigger than client MTU by splitting it to chunks and uses separate firmware client for transfer. The component interface is unchanged and driver doing all splitting. Only one Late Binding version is supported by firmware. When Late binding version 2 is supported, the new client is advertised by firmware and existing MKHI will have version 2. This helps driver to select the right mode of work. Reviewed-by: Andy Shevchenko Reviewed-by: Badal Nilawar Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260405112326.1535208-3-alexander.usyskin@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/mei_lb.c | 252 ++++++++++++++++++--- include/drm/intel/intel_lb_mei_interface.h | 51 ++++- 2 files changed, 265 insertions(+), 38 deletions(-) diff --git a/drivers/misc/mei/mei_lb.c b/drivers/misc/mei/mei_lb.c index 78717ee8ac9a..f6a258c2b838 100644 --- a/drivers/misc/mei/mei_lb.c +++ b/drivers/misc/mei/mei_lb.c @@ -59,12 +59,17 @@ * 5. Status is returned back to the host via MEI. */ +/* Late Binding version 1 */ + #define INTEL_LB_CMD 0x12 #define INTEL_LB_RSP (INTEL_LB_CMD | 0x80) #define INTEL_LB_SEND_TIMEOUT_MSEC 3000 #define INTEL_LB_RECV_TIMEOUT_MSEC 3000 +#define MEI_GUID_MKHI UUID_LE(0xe2c2afa2, 0x3817, 0x4d19, \ + 0x9d, 0x95, 0x6, 0xb1, 0x6b, 0x58, 0x8a, 0x5d) + /** * struct mei_lb_req - Late Binding request structure * @header: MKHI message header (see struct mkhi_msg_hdr) @@ -97,8 +102,74 @@ struct mei_lb_rsp { __le32 status; } __packed; -static bool mei_lb_check_response(const struct device *dev, ssize_t bytes, - struct mei_lb_rsp *rsp) +/* Late Binding version 2 */ + +#define MEI_LB2_CMD 0x01 + +#define MEI_LB2_HDR_FLAG_RSP 0x01 + +#define MEI_GUID_LB UUID_LE(0x4ed87243, 0x3980, 0x4d8e, \ + 0xb1, 0xf9, 0x6f, 0xb7, 0xc0, 0x14, 0x8c, 0x4d) + +/** + * struct mei_lb2_header - Late Binding2 header + * @command_id: + * @flags: Flags for transport layer (e.g. MEI_LB2_HDR_FLAG_RSP) + * @reserved: Reserved for future use by authentication firmware, must be set to 0 + */ +struct mei_lb2_header { + __le32 command_id; + u8 flags; + u8 reserved[3]; +}; + +/** + * struct mei_lb2_rsp_header - Late Binding2 response header + * @header: Common command header + * @status: Status returned by authentication firmware (see &enum intel_lb_status) + */ +struct mei_lb2_rsp_header { + struct mei_lb2_header header; + __le32 status; +}; + +#define MEI_LB2_FLAG_FST_CHUNK 0x02 +#define MEI_LB2_FLAG_LST_CHUNK 0x04 + +/** + * struct mei_lb2_req - Late Binding2 request + * @header: Common command header + * @type: Type of the Late Binding payload (see &enum intel_lb_type) + * @flags: Flags to be passed to the authentication firmware (MEI_LB2_FLAG_*) + * @reserved: Reserved for future use by authentication firmware, must be set to 0 + * @total_payload_size: Size of whole Late Binding package in bytes + * @payload_size: Size of the payload chunk in bytes + * @payload: Data chunk to be sent to the authentication firmware + */ +struct mei_lb2_req { + struct mei_lb2_header header; + __le32 type; + __le32 flags; + __le32 reserved; + __le32 total_payload_size; + __le32 payload_size; + u8 payload[] __counted_by(payload_size); +}; + +/** + * struct mei_lb2_rsp - Late Binding2 response + * @rheader: Common response header + * @type: Type of the Late Binding payload (see &enum intel_lb_type) + * @reserved: Reserved for future use by authentication firmware, must be set to 0 + */ +struct mei_lb2_rsp { + struct mei_lb2_rsp_header rheader; + __le32 type; + __le32 reserved[2]; +}; + +static bool mei_lb_check_response_v1(const struct device *dev, ssize_t bytes, + struct mei_lb_rsp *rsp) { /* * Received message size may be smaller than the full message size when @@ -134,24 +205,15 @@ static bool mei_lb_check_response(const struct device *dev, ssize_t bytes, return true; } -static int mei_lb_push_payload(struct device *dev, u32 type, u32 flags, - const void *payload, size_t payload_size) +static int mei_lb_push_payload_v1(struct device *dev, struct mei_cl_device *cldev, + u32 type, u32 flags, const void *payload, size_t payload_size) { - struct mei_cl_device *cldev; struct mei_lb_req *req = NULL; struct mei_lb_rsp rsp; size_t req_size; ssize_t bytes; int ret; - cldev = to_mei_cl_device(dev); - - ret = mei_cldev_enable(cldev); - if (ret) { - dev_dbg(dev, "Failed to enable firmware client. %d\n", ret); - return ret; - } - req_size = struct_size(req, payload, payload_size); if (req_size > mei_cldev_mtu(cldev)) { dev_err(dev, "Payload is too big: %zu\n", payload_size); @@ -190,7 +252,7 @@ static int mei_lb_push_payload(struct device *dev, u32 type, u32 flags, ret = bytes; goto end; } - if (!mei_lb_check_response(dev, bytes, &rsp)) { + if (!mei_lb_check_response_v1(dev, bytes, &rsp)) { dev_err(dev, "Bad response from the firmware. header: %02x %02x %02x %02x\n", rsp.header.group_id, rsp.header.command, rsp.header.reserved, rsp.header.result); @@ -201,11 +263,130 @@ static int mei_lb_push_payload(struct device *dev, u32 type, u32 flags, dev_dbg(dev, "status = %u\n", le32_to_cpu(rsp.status)); ret = (int)le32_to_cpu(rsp.status); end: - mei_cldev_disable(cldev); kfree(req); return ret; } +static int mei_lb_check_response_v2(const struct device *dev, ssize_t bytes, + struct mei_lb2_rsp *rsp) +{ + /* + * Received message size may be smaller than the full message size when + * reply contains only header with status field set to the error code. + * Check the header size and content first to output exact error, if needed, + * and then process to the whole message. + */ + if (bytes < sizeof(rsp->rheader)) { + dev_err(dev, "Received less than header size from the firmware: %zd < %zu\n", + bytes, sizeof(rsp->rheader)); + return -ENOMSG; + } + if (rsp->rheader.header.command_id != MEI_LB2_CMD) { + dev_err(dev, "Mismatch command: 0x%x instead of 0x%x\n", + rsp->rheader.header.command_id, MEI_LB2_CMD); + return -EPROTO; + } + if (!(rsp->rheader.header.flags & MEI_LB2_HDR_FLAG_RSP)) { + dev_err(dev, "Not a response: 0x%x\n", rsp->rheader.header.flags); + return -EBADMSG; + } + if (rsp->rheader.status) { + dev_err(dev, "Error in result: 0x%x\n", rsp->rheader.status); + return (int)le32_to_cpu(rsp->rheader.status); + } + if (bytes < sizeof(*rsp)) { + dev_err(dev, "Received less than message size from the firmware: %zd < %zu\n", + bytes, sizeof(*rsp)); + return -ENODATA; + } + + return 0; +} + +static int mei_lb_push_payload_v2(struct device *dev, struct mei_cl_device *cldev, + u32 type, u32 flags, const void *payload, size_t payload_size) +{ + u32 first_chunk, last_chunk; + struct mei_lb2_rsp rsp; + size_t sent_data = 0; + size_t chunk_size; + size_t req_size; + ssize_t bytes; + int ret; + + struct mei_lb2_req *req __free(kfree) = kzalloc(mei_cldev_mtu(cldev), GFP_KERNEL); + if (!req) + return -ENOMEM; + + first_chunk = MEI_LB2_FLAG_FST_CHUNK; + last_chunk = 0; + do { + chunk_size = min(payload_size - sent_data, mei_cldev_mtu(cldev) - sizeof(*req)); + + req_size = struct_size(req, payload, chunk_size); + if (sent_data + chunk_size == payload_size) + last_chunk = MEI_LB2_FLAG_LST_CHUNK; + + req->header.command_id = MEI_LB2_CMD; + req->type = cpu_to_le32(type); + req->flags = cpu_to_le32(flags | first_chunk | last_chunk); + req->reserved = 0; + req->total_payload_size = cpu_to_le32(payload_size); + req->payload_size = cpu_to_le32(chunk_size); + memcpy(req->payload, payload + sent_data, chunk_size); + + dev_dbg(dev, "Sending %zu bytes from offset %zu of %zu%s%s\n", + chunk_size, sent_data, payload_size, + first_chunk ? " first" : "", last_chunk ? " last" : ""); + + bytes = mei_cldev_send_timeout(cldev, (u8 *)req, req_size, + INTEL_LB_SEND_TIMEOUT_MSEC); + if (bytes < 0) { + dev_err(dev, "Failed to send late binding request to firmware. %zd\n", + bytes); + return bytes; + } + + bytes = mei_cldev_recv_timeout(cldev, (u8 *)&rsp, sizeof(rsp), + INTEL_LB_RECV_TIMEOUT_MSEC); + if (bytes < 0) { + dev_err(dev, "Failed to receive late binding reply from firmware. %zd\n", + bytes); + return bytes; + } + ret = mei_lb_check_response_v2(dev, bytes, &rsp); + if (ret) + return ret; + + /* prepare for the next chunk */ + sent_data += chunk_size; + first_chunk = 0; + } while (!last_chunk); + + return 0; +} + +static int mei_lb_push_payload(struct device *dev, u32 type, u32 flags, + const void *payload, size_t payload_size) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + int ret; + + ret = mei_cldev_enable(cldev); + if (ret) { + dev_dbg(dev, "Failed to enable firmware client. %d\n", ret); + return ret; + } + + if (memcmp(&MEI_GUID_LB, mei_cldev_uuid(cldev), sizeof(uuid_le)) == 0) + ret = mei_lb_push_payload_v2(dev, cldev, type, flags, payload, payload_size); + else + ret = mei_lb_push_payload_v1(dev, cldev, type, flags, payload, payload_size); + + mei_cldev_disable(cldev); + return ret; +} + static const struct intel_lb_component_ops mei_lb_ops = { .push_payload = mei_lb_push_payload, }; @@ -229,11 +410,16 @@ static int mei_lb_component_match(struct device *dev, int subcomponent, void *data) { /* - * This function checks if requester is Intel %PCI_CLASS_DISPLAY_VGA or - * %PCI_CLASS_DISPLAY_OTHER device, and checks if the requester is the - * grand parent of mei_if i.e. late bind MEI device + * This function checks if requester is Intel vendor, + * determines if MEI is standalone PCI device or the auxiliary one + * and checks the following: + * 0) PCI parent: (e.g. /sys/class/mei/mei0/device -> ../../../0000:15:00.0) + * the requester and MEI device has the same grand parent + * 1) Auxiliary parent: (e.g. /sys/class/mei/mei1/device -> ../../../xe.mei-gscfi.768) + * the requester is the parent of MEI device */ struct device *base = data; + struct device *basep = dev; struct pci_dev *pdev; if (!dev) @@ -247,20 +433,30 @@ static int mei_lb_component_match(struct device *dev, int subcomponent, if (pdev->vendor != PCI_VENDOR_ID_INTEL) return 0; - if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8) && - pdev->class != (PCI_CLASS_DISPLAY_OTHER << 8)) - return 0; - if (subcomponent != INTEL_COMPONENT_LB) return 0; base = base->parent; - if (!base) /* mei device */ + if (!base) /* MEI device */ return 0; - base = base->parent; /* pci device */ + if (dev_is_pci(base)) { + /* case 0) PCI parent */ + base = base->parent; /* bridge 1 */ + if (!base) + return 0; + base = base->parent; /* bridge 2 */ - return !!base && dev == base; + basep = basep->parent; /* bridge 1 */ + if (!basep) + return 0; + basep = basep->parent; /* bridge 2 */ + } else { + /* case 1) Auxiliary parent */ + base = base->parent; /* PCI device */ + } + + return !!base && !!basep && base == basep; } static int mei_lb_probe(struct mei_cl_device *cldev, @@ -288,11 +484,9 @@ static void mei_lb_remove(struct mei_cl_device *cldev) component_master_del(&cldev->dev, &mei_lb_component_master_ops); } -#define MEI_GUID_MKHI UUID_LE(0xe2c2afa2, 0x3817, 0x4d19, \ - 0x9d, 0x95, 0x6, 0xb1, 0x6b, 0x58, 0x8a, 0x5d) - static const struct mei_cl_device_id mei_lb_tbl[] = { - { .uuid = MEI_GUID_MKHI, .version = MEI_CL_VERSION_ANY }, + { .uuid = MEI_GUID_MKHI, .version = 1 }, + { .uuid = MEI_GUID_LB, .version = MEI_CL_VERSION_ANY }, { } }; MODULE_DEVICE_TABLE(mei, mei_lb_tbl); diff --git a/include/drm/intel/intel_lb_mei_interface.h b/include/drm/intel/intel_lb_mei_interface.h index 0850738a30fc..7f533ac7cc10 100644 --- a/include/drm/intel/intel_lb_mei_interface.h +++ b/include/drm/intel/intel_lb_mei_interface.h @@ -6,6 +6,7 @@ #ifndef _INTEL_LB_MEI_INTERFACE_H_ #define _INTEL_LB_MEI_INTERFACE_H_ +#include #include struct device; @@ -21,9 +22,11 @@ struct device; /** * enum intel_lb_type - enum to determine late binding payload type * @INTEL_LB_TYPE_FAN_CONTROL: Fan controller configuration + * @INTEL_LB_TYPE_OCODE: Ocode firmware */ enum intel_lb_type { INTEL_LB_TYPE_FAN_CONTROL = 1, + INTEL_LB_TYPE_OCODE = 3, }; /** @@ -36,16 +39,46 @@ enum intel_lb_type { * @INTEL_LB_STATUS_INVALID_SIGNATURE: Payload has an invalid or untrusted signature * @INTEL_LB_STATUS_INVALID_PAYLOAD: Payload contents are not accepted by firmware * @INTEL_LB_STATUS_TIMEOUT: Operation timed out before completion + * @INTEL_LB_STATUS_BUFFER_TOO_SMALL: Buffer provided is smaller when expected + * @INTEL_LB_STATUS_INTERNAL_ERROR: Internal firmware error + * @INTEL_LB_STATUS_INVALID_FPT_TABLE: Invalid firmware format table + * @INTEL_LB_STATUS_SIGNED_PAYLOAD_VERIFICATION_ERROR: Error in signature verification + * @INTEL_LB_STATUS_SIGNED_PAYLOAD_INVALID_CPD: Invalid CPD + * @INTEL_LB_STATUS_SIGNED_PAYLOAD_FW_VERSION_MISMATCH: Firmware version mismatch + * @INTEL_LB_STATUS_SIGNED_PAYLOAD_INVALID_MANIFEST: Invalid firmware manifest + * @INTEL_LB_STATUS_SIGNED_PAYLOAD_INVALID_HASH: Wrong hash in signature + * @INTEL_LB_STATUS_SIGNED_PAYLOAD_BINDING_TYPE_MISMATCH: Wrong firmware type provided + * @INTEL_LB_STATUS_SIGNED_PAYLOAD_HANDLE_SVN_FAILED: SVN check failed + * @INTEL_LB_STATUS_DESTINATION_MBOX_FAILURE: Failed to send datat to destination + * @INTEL_LB_STATUS_MISSING_LOADING_PATCH: No loading patch found + * @INTEL_LB_STATUS_INVALID_COMMAND: Invalid command number + * @INTEL_LB_STATUS_INVALID_HECI_HEADER: Invalid transport header + * @INTEL_LB_STATUS_IP_ERROR_START: Base for internal errors */ enum intel_lb_status { - INTEL_LB_STATUS_SUCCESS = 0, - INTEL_LB_STATUS_4ID_MISMATCH = 1, - INTEL_LB_STATUS_ARB_FAILURE = 2, - INTEL_LB_STATUS_GENERAL_ERROR = 3, - INTEL_LB_STATUS_INVALID_PARAMS = 4, - INTEL_LB_STATUS_INVALID_SIGNATURE = 5, - INTEL_LB_STATUS_INVALID_PAYLOAD = 6, - INTEL_LB_STATUS_TIMEOUT = 7, + INTEL_LB_STATUS_SUCCESS = 0, + INTEL_LB_STATUS_4ID_MISMATCH = 1, + INTEL_LB_STATUS_ARB_FAILURE = 2, + INTEL_LB_STATUS_GENERAL_ERROR = 3, + INTEL_LB_STATUS_INVALID_PARAMS = 4, + INTEL_LB_STATUS_INVALID_SIGNATURE = 5, + INTEL_LB_STATUS_INVALID_PAYLOAD = 6, + INTEL_LB_STATUS_TIMEOUT = 7, + INTEL_LB_STATUS_BUFFER_TOO_SMALL = 8, + INTEL_LB_STATUS_INTERNAL_ERROR = 9, + INTEL_LB_STATUS_INVALID_FPT_TABLE = 10, + INTEL_LB_STATUS_SIGNED_PAYLOAD_VERIFICATION_ERROR = 11, + INTEL_LB_STATUS_SIGNED_PAYLOAD_INVALID_CPD = 12, + INTEL_LB_STATUS_SIGNED_PAYLOAD_FW_VERSION_MISMATCH = 13, + INTEL_LB_STATUS_SIGNED_PAYLOAD_INVALID_MANIFEST = 14, + INTEL_LB_STATUS_SIGNED_PAYLOAD_INVALID_HASH = 15, + INTEL_LB_STATUS_SIGNED_PAYLOAD_BINDING_TYPE_MISMATCH = 16, + INTEL_LB_STATUS_SIGNED_PAYLOAD_HANDLE_SVN_FAILED = 17, + INTEL_LB_STATUS_DESTINATION_MBOX_FAILURE = 18, + INTEL_LB_STATUS_MISSING_LOADING_PATCH = 19, + INTEL_LB_STATUS_INVALID_COMMAND = 20, + INTEL_LB_STATUS_INVALID_HECI_HEADER = 21, + INTEL_LB_STATUS_IP_ERROR_START = BIT(31), }; /** @@ -62,7 +95,7 @@ struct intel_lb_component_ops { * @payload_size: Payload buffer size in bytes * * Return: 0 success, negative errno value on transport failure, - * positive status returned by firmware + * positive error status returned by firmware */ int (*push_payload)(struct device *dev, u32 type, u32 flags, const void *payload, size_t payload_size); From a5a1804332afc7035d5c5b880548262e81d796bc Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 5 Apr 2026 17:17:58 +0300 Subject: [PATCH 404/405] mei: me: add nova lake point H DID Add Nova Lake H device id. Cc: stable Co-developed-by: Tomas Winkler Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260405141758.1634556-1-alexander.usyskin@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me-regs.h | 1 + drivers/misc/mei/pci-me.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 9b1675f98404..f145e8e36cb3 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -123,6 +123,7 @@ #define PCI_DEVICE_ID_INTEL_MEI_WCL_P 0x4D70 /* Wildcat Lake P */ #define PCI_DEVICE_ID_INTEL_MEI_NVL_S 0x6E68 /* Nova Lake Point S */ +#define PCI_DEVICE_ID_INTEL_MEI_NVL_H 0xD370 /* Nova Lake Point H */ #define PCI_DEVICE_ID_INTEL_MEI_CRI 0x6766 /* Crescent Island */ diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 8d16bfa6027c..9efeafa8f1ca 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -131,6 +131,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {PCI_DEVICE_DATA(INTEL, MEI_WCL_P, MEI_ME_PCH15_CFG)}, {PCI_DEVICE_DATA(INTEL, MEI_NVL_S, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_NVL_H, MEI_ME_PCH15_CFG)}, /* required last entry */ {0, } From 971f3474f8898ae8bbab19a9b547819a5e6fbcf1 Mon Sep 17 00:00:00 2001 From: Jie Gan Date: Tue, 7 Apr 2026 19:09:05 +0800 Subject: [PATCH 405/405] coresight: tpdm: fix invalid MMIO access issue Create the csdev_access struct only when a valid MMIO resource is available. In tpdm_probe(), base is uninitialized for static TPDM instances that lack an MMIO resource, causing csdev_access to be created with a garbage address. So far there has no register access for static instance, but this change helps mitigate potential risks in the future. Fixes: 14ae052f7947 ("coresight: tpdm: add static tpdm support") Reviewed-by: Leo Yan Signed-off-by: Jie Gan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260407-fix-potential-issue-in-tpdm-v2-1-1d0e0d3cb793@oss.qualcomm.com --- drivers/hwtracing/coresight/coresight-tpdm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c index 9b16f368a58b..eaf7210af648 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.c +++ b/drivers/hwtracing/coresight/coresight-tpdm.c @@ -1430,6 +1430,7 @@ static int tpdm_probe(struct device *dev, struct resource *res) if (ret) return ret; + desc.access = CSDEV_ACCESS_IOMEM(base); if (tpdm_has_dsb_dataset(drvdata)) of_property_read_u32(drvdata->dev->of_node, "qcom,dsb-msrs-num", &drvdata->dsb_msr_num); @@ -1452,7 +1453,6 @@ static int tpdm_probe(struct device *dev, struct resource *res) desc.ops = &tpdm_cs_ops; desc.pdata = dev->platform_data; desc.dev = dev; - desc.access = CSDEV_ACCESS_IOMEM(base); if (res) desc.groups = tpdm_attr_grps; else