From 53f33fd74d3993af0fbb5494b5be709f6ca6b712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20=C4=8Cern=C3=BD?= Date: Fri, 12 Jun 2026 09:55:07 +0200 Subject: [PATCH 001/513] w1: ds2482: add OF device match table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an of_device_id table and hook it into the i2c driver so the ds2482/ds2484 can be matched and auto-probed from Device Tree. This allows automatic module loading when instantiated via DT. Signed-off-by: Kryštof Černý Link: https://patch.msgid.link/20260612-w1-of-autoload-v1-1-74e8a17626e6@gmail.com Signed-off-by: Krzysztof Kozlowski --- drivers/w1/masters/ds2482.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index 0069e6f854d7..9e57c6e487d1 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -545,9 +545,17 @@ static const struct i2c_device_id ds2482_id[] = { }; MODULE_DEVICE_TABLE(i2c, ds2482_id); +static const struct of_device_id ds2482_of_match[] = { + { .compatible = "maxim,ds2482", }, + { .compatible = "maxim,ds2484", }, + { } +}; +MODULE_DEVICE_TABLE(of, ds2482_of_match); + static struct i2c_driver ds2482_driver = { .driver = { .name = "ds2482", + .of_match_table = ds2482_of_match, }, .probe = ds2482_probe, .remove = ds2482_remove, From 2fc3237e1398dc81f66842461db2f4a5466df6bf Mon Sep 17 00:00:00 2001 From: Tze Yee Ng Date: Thu, 14 May 2026 19:11:57 -0700 Subject: [PATCH 002/513] Documentation: ABI: add stratix10-rsu QSPI size and erase_size sysfs Document the eight read-only sysfs files (size0-size3, erase_size0- erase_size3) under platform/stratix10-rsu: flash size and erase size in bytes, 32-bit hex per line, EIO when the value is not available. Signed-off-by: Tze Yee Ng Signed-off-by: Dinh Nguyen --- .../sysfs-devices-platform-stratix10-rsu | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-devices-platform-stratix10-rsu b/Documentation/ABI/testing/sysfs-devices-platform-stratix10-rsu index ee253b033280..b93341e4471d 100644 --- a/Documentation/ABI/testing/sysfs-devices-platform-stratix10-rsu +++ b/Documentation/ABI/testing/sysfs-devices-platform-stratix10-rsu @@ -172,3 +172,61 @@ Contact: Richard Gong Description: (RO) max retry parameter is stored in the firmware decision IO section, as a byte located at offset 0x18c. + +What: /sys/devices/platform/stratix10-rsu.0/size0 +Date: May 2026 +KernelVersion: 7.1 +Contact: Tze Yee Ng +Description: + (RO) Flash size in bytes for QSPI device 0 (32-bit hex, one + line). EIO if unavailable. + +What: /sys/devices/platform/stratix10-rsu.0/size1 +Date: May 2026 +KernelVersion: 7.1 +Contact: Tze Yee Ng +Description: + (RO) Like size0 for device 1. + +What: /sys/devices/platform/stratix10-rsu.0/size2 +Date: May 2026 +KernelVersion: 7.1 +Contact: Tze Yee Ng +Description: + (RO) Like size0 for device 2. + +What: /sys/devices/platform/stratix10-rsu.0/size3 +Date: May 2026 +KernelVersion: 7.1 +Contact: Tze Yee Ng +Description: + (RO) Like size0 for device 3. + +What: /sys/devices/platform/stratix10-rsu.0/erase_size0 +Date: May 2026 +KernelVersion: 7.1 +Contact: Tze Yee Ng +Description: + (RO) Erase size in bytes for QSPI device 0 (32-bit hex, one + line). EIO if unavailable. + +What: /sys/devices/platform/stratix10-rsu.0/erase_size1 +Date: May 2026 +KernelVersion: 7.1 +Contact: Tze Yee Ng +Description: + (RO) Like erase_size0 for device 1. + +What: /sys/devices/platform/stratix10-rsu.0/erase_size2 +Date: May 2026 +KernelVersion: 7.1 +Contact: Tze Yee Ng +Description: + (RO) Like erase_size0 for device 2. + +What: /sys/devices/platform/stratix10-rsu.0/erase_size3 +Date: May 2026 +KernelVersion: 7.1 +Contact: Tze Yee Ng +Description: + (RO) Like erase_size0 for device 3. From fe8c977b35fd9e945ff281382c808026d41731e5 Mon Sep 17 00:00:00 2001 From: Tze Yee Ng Date: Thu, 14 May 2026 19:11:58 -0700 Subject: [PATCH 003/513] firmware: stratix10-rsu: Add flash device info retrieval via SMC Extend the Intel Remote System Update (RSU) driver to retrieve the device info table through an ARM SMC call to the service layer. The table reports flash size and erase size for multiple devices. Signed-off-by: Tze Yee Ng Signed-off-by: Dinh Nguyen --- drivers/firmware/stratix10-rsu.c | 200 +++++++++++++++++- drivers/firmware/stratix10-svc.c | 94 +++++++- include/linux/firmware/intel/stratix10-smc.h | 25 ++- .../firmware/intel/stratix10-svc-client.h | 12 +- 4 files changed, 315 insertions(+), 16 deletions(-) diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c index daddb5224794..d887c74b9821 100644 --- a/drivers/firmware/stratix10-rsu.c +++ b/drivers/firmware/stratix10-rsu.c @@ -7,17 +7,24 @@ #include #include #include +#include +#include +#include #include #include #include #include #include -#include #include #include -#include -#define RSU_ERASE_SIZE_MASK GENMASK_ULL(63, 32) +/* + * INTEL_SIP_SMC_RSU_GET_DEVICE_INFO packs each flash word as: + * [63:32] erase_size, [31:0] size (see stratix10-smc.h). + */ +#define RSU_DEVICE_INFO_SIZE_MASK GENMASK_ULL(31, 0) +#define RSU_DEVICE_INFO_ERASE_SIZE_MASK GENMASK_ULL(63, 32) + #define RSU_DCMF0_MASK GENMASK_ULL(31, 0) #define RSU_DCMF1_MASK GENMASK_ULL(63, 32) #define RSU_DCMF2_MASK GENMASK_ULL(31, 0) @@ -33,11 +40,31 @@ #define INVALID_DCMF_VERSION 0xFF #define INVALID_DCMF_STATUS 0xFFFFFFFF #define INVALID_SPT_ADDRESS 0x0 +#define INVALID_DEVICE_INFO (~0U) #define RSU_RETRY_SLEEP_MS (1U) #define RSU_ASYNC_MSG_RETRY (3U) #define RSU_GET_SPT_RESP_LEN (4 * sizeof(unsigned int)) +struct flash_device_info { + unsigned int size; + unsigned int erase_size; +}; + +/** + * rsu_device_info_set_from_packed() - Decode one RSU device-info SMC word + * @di: slot to fill + * @packed: register value: [63:32] erase_size, [31:0] size + * (INTEL_SIP_SMC_RSU_GET_DEVICE_INFO) + */ +static void rsu_device_info_set_from_packed(struct flash_device_info *di, + unsigned long packed) +{ + di->size = (unsigned int)FIELD_GET(RSU_DEVICE_INFO_SIZE_MASK, packed); + di->erase_size = (unsigned int)FIELD_GET(RSU_DEVICE_INFO_ERASE_SIZE_MASK, + packed); +} + typedef void (*rsu_callback)(struct stratix10_svc_client *client, struct stratix10_svc_cb_data *data); /** @@ -60,6 +87,8 @@ typedef void (*rsu_callback)(struct stratix10_svc_client *client, * @dcmf_status.dcmf1: dcmf1 status * @dcmf_status.dcmf2: dcmf2 status * @dcmf_status.dcmf3: dcmf3 status + * @device_info: per-device flash information array; each entry contains + * size and erase size for one flash device * @retry_counter: the current image's retry counter * @max_retry: the preset max retry value * @spt0_address: address of spt0 @@ -93,6 +122,8 @@ struct stratix10_rsu_priv { unsigned int dcmf3; } dcmf_status; + struct flash_device_info device_info[4]; + unsigned int retry_counter; unsigned int max_retry; @@ -100,6 +131,20 @@ struct stratix10_rsu_priv { unsigned long spt1_address; }; +/** + * rsu_device_info_invalidate() - Mark all cached QSPI device slots invalid + * @priv: RSU private data + */ +static void rsu_device_info_invalidate(struct stratix10_rsu_priv *priv) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(priv->device_info); i++) { + priv->device_info[i].size = INVALID_DEVICE_INFO; + priv->device_info[i].erase_size = INVALID_DEVICE_INFO; + } +} + typedef void (*rsu_async_callback)(struct device *dev, struct stratix10_rsu_priv *priv, struct stratix10_svc_cb_data *data); @@ -229,8 +274,57 @@ static void rsu_dcmf_status_callback(struct stratix10_svc_client *client, } /** - * rsu_async_get_spt_table_callback() - Callback to be used by the rsu_async_send() - * to retrieve the SPT table information. + * rsu_get_device_info_callback() - Callback from Intel service layer for + * getting the QSPI device info + * @client: pointer to client + * @data: pointer to callback data structure + * + * Callback from Intel service layer for QSPI device info. + * @data->kaddr1 points to struct arm_smccc_1_2_regs on SVC_STATUS_OK or + * SVC_STATUS_ERROR; it is NULL on SVC_STATUS_NO_SUPPORT (unsupported command). + */ +static void rsu_get_device_info_callback(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct stratix10_rsu_priv *priv = client->priv; + struct arm_smccc_1_2_regs *res = data->kaddr1; + + if (data->status == BIT(SVC_STATUS_OK)) { + if (!res) { + dev_err(client->dev, + "COMMAND_RSU_GET_DEVICE_INFO: missing result payload\n"); + rsu_device_info_invalidate(priv); + complete(&priv->completion); + return; + } + + rsu_device_info_set_from_packed(&priv->device_info[0], res->a1); + rsu_device_info_set_from_packed(&priv->device_info[1], res->a2); + rsu_device_info_set_from_packed(&priv->device_info[2], res->a3); + rsu_device_info_set_from_packed(&priv->device_info[3], res->a4); + + } else if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) { + dev_warn(client->dev, + "COMMAND_RSU_GET_DEVICE_INFO not supported by firmware\n"); + rsu_device_info_invalidate(priv); + } else { + if (res) + dev_err(client->dev, + "COMMAND_RSU_GET_DEVICE_INFO returned 0x%lX\n", + res->a0); + else + dev_err(client->dev, + "COMMAND_RSU_GET_DEVICE_INFO failed with status 0x%X\n", + data->status); + rsu_device_info_invalidate(priv); + } + + complete(&priv->completion); +} + +/** + * rsu_async_get_spt_table_callback() - Callback to be used by the + * rsu_async_send() to retrieve the SPT table information. * @dev: pointer to device object * @priv: pointer to priv object * @data: pointer to callback data structure @@ -698,6 +792,75 @@ static ssize_t notify_store(struct device *dev, return count; } +static ssize_t rsu_device_info_show(struct device *dev, char *buf, + unsigned int index, bool erase_size) +{ + struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); + unsigned int value; + + if (!priv) + return -ENODEV; + + if (index >= ARRAY_SIZE(priv->device_info)) + return -EINVAL; + + value = erase_size ? priv->device_info[index].erase_size : + priv->device_info[index].size; + + if (value == INVALID_DEVICE_INFO) + return -EIO; + + return sysfs_emit(buf, "0x%08x\n", value); +} + +static ssize_t size0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 0, false); +} + +static ssize_t size1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 1, false); +} + +static ssize_t size2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 2, false); +} + +static ssize_t size3_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 3, false); +} + +static ssize_t erase_size0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 0, true); +} + +static ssize_t erase_size1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 1, true); +} + +static ssize_t erase_size2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 2, true); +} + +static ssize_t erase_size3_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return rsu_device_info_show(dev, buf, 3, true); +} + static ssize_t spt0_address_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -742,6 +905,14 @@ static DEVICE_ATTR_RO(dcmf0_status); static DEVICE_ATTR_RO(dcmf1_status); static DEVICE_ATTR_RO(dcmf2_status); static DEVICE_ATTR_RO(dcmf3_status); +static DEVICE_ATTR_RO(size0); +static DEVICE_ATTR_RO(size1); +static DEVICE_ATTR_RO(size2); +static DEVICE_ATTR_RO(size3); +static DEVICE_ATTR_RO(erase_size0); +static DEVICE_ATTR_RO(erase_size1); +static DEVICE_ATTR_RO(erase_size2); +static DEVICE_ATTR_RO(erase_size3); static DEVICE_ATTR_WO(reboot_image); static DEVICE_ATTR_WO(notify); static DEVICE_ATTR_RO(spt0_address); @@ -764,6 +935,14 @@ static struct attribute *rsu_attrs[] = { &dev_attr_dcmf1_status.attr, &dev_attr_dcmf2_status.attr, &dev_attr_dcmf3_status.attr, + &dev_attr_size0.attr, + &dev_attr_size1.attr, + &dev_attr_size2.attr, + &dev_attr_size3.attr, + &dev_attr_erase_size0.attr, + &dev_attr_erase_size1.attr, + &dev_attr_erase_size2.attr, + &dev_attr_erase_size3.attr, &dev_attr_reboot_image.attr, &dev_attr_notify.attr, &dev_attr_spt0_address.attr, @@ -796,6 +975,7 @@ static int stratix10_rsu_probe(struct platform_device *pdev) priv->dcmf_status.dcmf2 = INVALID_DCMF_STATUS; priv->dcmf_status.dcmf3 = INVALID_DCMF_STATUS; /* spt0/1_address and status fields default to 0 from kzalloc */ + rsu_device_info_invalidate(priv); mutex_init(&priv->lock); init_completion(&priv->completion); @@ -846,6 +1026,16 @@ static int stratix10_rsu_probe(struct platform_device *pdev) goto remove_async_client; } + /* get QSPI device info from firmware */ + ret = rsu_send_msg(priv, COMMAND_RSU_GET_DEVICE_INFO, 0, + rsu_get_device_info_callback); + if (ret) { + dev_err(dev, "Error, getting QSPI Device Info %i\n", ret); + stratix10_svc_remove_async_client(priv->chan); + stratix10_svc_free_channel(priv->chan); + return ret; + } + ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_GET_SPT_TABLE, 0, rsu_async_get_spt_table_callback); if (ret) { diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index c24ca5823078..de938ab2db0b 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -445,13 +446,15 @@ static void svc_thread_cmd_config_status(struct stratix10_svc_controller *ctrl, * svc_thread_recv_status_ok() - handle the successful status * @p_data: pointer to service data structure * @cb_data: pointer to callback data structure to service client - * @res: result from SMC or HVC call + * @res: result from SMC or HVC call (a0-a3; used for routing and most commands) + * @res12: full v1.2 result for %COMMAND_RSU_GET_DEVICE_INFO, else NULL * * Send back the correspond status to the service clients. */ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, struct stratix10_svc_cb_data *cb_data, - struct arm_smccc_res res) + struct arm_smccc_res res, + struct arm_smccc_1_2_regs *res12) { cb_data->kaddr1 = NULL; cb_data->kaddr2 = NULL; @@ -513,6 +516,16 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, res.a2 = res.a2 * BYTE_TO_WORD_SIZE; cb_data->kaddr2 = &res.a2; break; + case COMMAND_RSU_GET_DEVICE_INFO: + if (WARN_ON(!res12)) { + cb_data->status = BIT(SVC_STATUS_ERROR); + break; + } + cb_data->status = BIT(SVC_STATUS_OK); + cb_data->kaddr1 = res12; + cb_data->kaddr2 = NULL; + cb_data->kaddr3 = NULL; + break; default: pr_warn("it shouldn't happen\n"); break; @@ -522,6 +535,10 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, p_data->chan->scl->receive_cb(p_data->chan->scl, cb_data); } +static void svc_smccc_1_2_full(struct stratix10_svc_controller *ctrl, + const struct arm_smccc_1_2_regs *args, + struct arm_smccc_1_2_regs *res); + /** * svc_normal_to_secure_thread() - the function to run in the kthread * @data: data pointer for kthread function @@ -539,6 +556,7 @@ static int svc_normal_to_secure_thread(void *data) struct stratix10_svc_data *pdata = NULL; struct stratix10_svc_cb_data *cbdata = NULL; struct arm_smccc_res res; + struct arm_smccc_1_2_regs res12 = { 0 }; unsigned long a0, a1, a2, a3, a4, a5, a6, a7; int ret_fifo = 0; @@ -727,6 +745,16 @@ static int svc_normal_to_secure_thread(void *data) a5 = (unsigned long)pdata->paddr_output; a6 = (unsigned long)pdata->size_output / BYTE_TO_WORD_SIZE; break; + case COMMAND_RSU_GET_DEVICE_INFO: + a0 = INTEL_SIP_SMC_RSU_GET_DEVICE_INFO; + a1 = 0; + a2 = 0; + a3 = 0; + a4 = 0; + a5 = 0; + a6 = 0; + a7 = 0; + break; default: pr_warn("it shouldn't happen\n"); mutex_unlock(&ctrl->sdm_lock); @@ -740,7 +768,18 @@ static int svc_normal_to_secure_thread(void *data) pr_debug(" a3=0x%016x\n", (unsigned int)a3); pr_debug(" a4=0x%016x\n", (unsigned int)a4); pr_debug(" a5=0x%016x\n", (unsigned int)a5); - ctrl->invoke_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + if (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) { + struct arm_smccc_1_2_regs args12 = { 0 }; + + args12.a0 = INTEL_SIP_SMC_RSU_GET_DEVICE_INFO; + svc_smccc_1_2_full(ctrl, &args12, &res12); + res.a0 = res12.a0; + res.a1 = res12.a1; + res.a2 = res12.a2; + res.a3 = res12.a3; + } else { + ctrl->invoke_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + } pr_debug("%s: %s: after SMC call -- res.a0=0x%016x", __func__, chan->name, (unsigned int)res.a0); @@ -763,9 +802,15 @@ static int svc_normal_to_secure_thread(void *data) } switch (res.a0) { - case INTEL_SIP_SMC_STATUS_OK: - svc_thread_recv_status_ok(pdata, cbdata, res); + case INTEL_SIP_SMC_STATUS_OK: { + struct arm_smccc_1_2_regs *devinfo_res = + (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) ? + &res12 : NULL; + + svc_thread_recv_status_ok(pdata, cbdata, res, + devinfo_res); break; + } case INTEL_SIP_SMC_STATUS_BUSY: switch (pdata->command) { case COMMAND_RECONFIG_DATA_SUBMIT: @@ -806,10 +851,16 @@ static int svc_normal_to_secure_thread(void *data) case INTEL_SIP_SMC_RSU_ERROR: pr_err("%s: STATUS_ERROR\n", __func__); cbdata->status = BIT(SVC_STATUS_ERROR); - cbdata->kaddr1 = &res.a1; - cbdata->kaddr2 = (res.a2) ? - svc_pa_to_va(res.a2) : NULL; - cbdata->kaddr3 = (res.a3) ? &res.a3 : NULL; + if (pdata->command == COMMAND_RSU_GET_DEVICE_INFO) { + cbdata->kaddr1 = &res12; + cbdata->kaddr2 = NULL; + cbdata->kaddr3 = NULL; + } else { + cbdata->kaddr1 = &res.a1; + cbdata->kaddr2 = (res.a2) ? + svc_pa_to_va(res.a2) : NULL; + cbdata->kaddr3 = (res.a3) ? &res.a3 : NULL; + } pdata->chan->scl->receive_cb(pdata->chan->scl, cbdata); break; default: @@ -1025,6 +1076,31 @@ static void svc_smccc_hvc(unsigned long a0, unsigned long a1, arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res); } +/** + * svc_smccc_1_2_full() - SMC/HVC v1.2 call matching the sync channel method + * @ctrl: service controller (selects SMC vs HVC) + * @args: arguments + * @res: full register-file result (a0-a17) + */ +static void svc_smccc_1_2_full(struct stratix10_svc_controller *ctrl, + const struct arm_smccc_1_2_regs *args, + struct arm_smccc_1_2_regs *res) +{ + if (ctrl->invoke_fn == svc_smccc_smc) { + arm_smccc_1_2_smc(args, res); + } else if (ctrl->invoke_fn == svc_smccc_hvc) { + arm_smccc_1_2_hvc(args, res); + } else { + WARN_ON_ONCE(1); + /* + * INTEL_SIP_SMC_STATUS_OK is 0; zero-filled res would be misrouted + * as success. Force an error path and clear fabricated payload. + */ + memset(res, 0, sizeof(*res)); + res->a0 = INTEL_SIP_SMC_STATUS_ERROR; + } +} + /** * get_invoke_func() - invoke SMC or HVC call * @dev: pointer to device diff --git a/include/linux/firmware/intel/stratix10-smc.h b/include/linux/firmware/intel/stratix10-smc.h index 9116512169dc..6e042943b6ce 100644 --- a/include/linux/firmware/intel/stratix10-smc.h +++ b/include/linux/firmware/intel/stratix10-smc.h @@ -429,6 +429,29 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE) #define INTEL_SIP_SMC_RSU_DCMF_STATUS \ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_RSU_DCMF_STATUS) +/** + * Request INTEL_SIP_SMC_RSU_GET_DEVICE_INFO + * + * Sync call used by service driver at EL1 to query QSPI device info from FW + * + * Call register usage: + * a0 INTEL_SIP_SMC_RSU_GET_DEVICE_INFO + * a1-7 not used + * + * Return status + * a0 INTEL_SIP_SMC_STATUS_OK + * a1 erasesize0 | size0 + * a2 erasesize1 | size1 + * a3 erasesize2 | size2 + * a4 erasesize3 | size3 + * Or + * + * a0 INTEL_SIP_SMC_RSU_ERROR + */ +#define INTEL_SIP_SMC_FUNCID_RSU_GET_DEVICE_INFO 22 +#define INTEL_SIP_SMC_RSU_GET_DEVICE_INFO \ + INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_RSU_GET_DEVICE_INFO) + /** * Request INTEL_SIP_SMC_SERVICE_COMPLETED * Sync call to check if the secure world have completed service request @@ -493,7 +516,7 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE) * a3 not used */ #define INTEL_SIP_SMC_FUNCID_MBOX_SEND_CMD 60 - #define INTEL_SIP_SMC_MBOX_SEND_CMD \ +#define INTEL_SIP_SMC_MBOX_SEND_CMD \ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_MBOX_SEND_CMD) /** diff --git a/include/linux/firmware/intel/stratix10-svc-client.h b/include/linux/firmware/intel/stratix10-svc-client.h index 3edd93502bf8..af13dacdf5ac 100644 --- a/include/linux/firmware/intel/stratix10-svc-client.h +++ b/include/linux/firmware/intel/stratix10-svc-client.h @@ -128,6 +128,10 @@ struct stratix10_svc_chan; * @COMMAND_RSU_DCMF_STATUS: query firmware for the DCMF status * return status is SVC_STATUS_OK or SVC_STATUS_ERROR * + * @COMMAND_RSU_GET_DEVICE_INFO: query firmware for QSPI device info; + * return status is SVC_STATUS_OK, SVC_STATUS_ERROR, or SVC_STATUS_NO_SUPPORT + * (unsupported command / firmware compatibility path in the service layer). + * * @COMMAND_RSU_GET_SPT_TABLE: query firmware for SPT table * return status is SVC_STATUS_OK or SVC_STATUS_ERROR * @@ -174,6 +178,7 @@ enum stratix10_svc_command_code { COMMAND_RSU_MAX_RETRY, COMMAND_RSU_DCMF_VERSION, COMMAND_RSU_DCMF_STATUS, + COMMAND_RSU_GET_DEVICE_INFO, COMMAND_FIRMWARE_VERSION, COMMAND_RSU_GET_SPT_TABLE, /* for FCS */ @@ -224,7 +229,12 @@ struct stratix10_svc_command_config_type { /** * struct stratix10_svc_cb_data - callback data structure from service layer * @status: the status of sent command - * @kaddr1: address of 1st completed data block + * @kaddr1: address of 1st completed data block, or command-specific payload. + * For COMMAND_RSU_GET_DEVICE_INFO on SVC_STATUS_OK or SVC_STATUS_ERROR, + * points to struct arm_smccc_1_2_regs filled by the SMC/HVC return + * registers (a0 status, a1-a4 packed device words per + * INTEL_SIP_SMC_RSU_GET_DEVICE_INFO). On SVC_STATUS_NO_SUPPORT (older + * firmware that does not handle this command), kaddr1 is NULL. * @kaddr2: address of 2nd completed data block * @kaddr3: address of 3rd completed data block */ From 7dfa980242fe340c50bcbe997d4293a34d699776 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Wed, 10 Jun 2026 13:58:30 +0000 Subject: [PATCH 004/513] iio: magnetometer: bmc150: sort includes Sort the include list alphabetically. Suggested-by: Andy Shevchenko Signed-off-by: Hungyu Lin Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/bmc150_magn.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index bf2551988008..f6639d7fab04 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -9,23 +9,24 @@ * (C) Copyright 2011~2014 Bosch Sensortec GmbH All Rights Reserved */ -#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 #include "bmc150_magn.h" From 2766d92b05047928bc77b17d021fb0173bf457ab Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Wed, 10 Jun 2026 13:58:31 +0000 Subject: [PATCH 005/513] iio: magnetometer: bmc150: use FIELD_PREP and FIELD_GET helpers Replace open-coded bitfield operations with FIELD_PREP() and FIELD_GET() helpers where appropriate. Also simplify bmc150_magn_set_odr() by returning directly from the matching table entry. Signed-off-by: Hungyu Lin Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/bmc150_magn.c | 33 ++++++++++++-------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index f6639d7fab04..32dff9cdd0f7 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -9,6 +9,7 @@ * (C) Copyright 2011~2014 Bosch Sensortec GmbH All Rights Reserved */ +#include #include #include #include @@ -246,14 +247,14 @@ static int bmc150_magn_set_power_mode(struct bmc150_magn_data *data, return regmap_update_bits(data->regmap, BMC150_MAGN_REG_OPMODE_ODR, BMC150_MAGN_MASK_OPMODE, - BMC150_MAGN_MODE_SLEEP << - BMC150_MAGN_SHIFT_OPMODE); + FIELD_PREP(BMC150_MAGN_MASK_OPMODE, + BMC150_MAGN_MODE_SLEEP)); case BMC150_MAGN_POWER_MODE_NORMAL: return regmap_update_bits(data->regmap, BMC150_MAGN_REG_OPMODE_ODR, BMC150_MAGN_MASK_OPMODE, - BMC150_MAGN_MODE_NORMAL << - BMC150_MAGN_SHIFT_OPMODE); + FIELD_PREP(BMC150_MAGN_MASK_OPMODE, + BMC150_MAGN_MODE_NORMAL)); } return -EINVAL; @@ -291,7 +292,7 @@ static int bmc150_magn_get_odr(struct bmc150_magn_data *data, int *val) ret = regmap_read(data->regmap, BMC150_MAGN_REG_OPMODE_ODR, ®_val); if (ret < 0) return ret; - odr_val = (reg_val & BMC150_MAGN_MASK_ODR) >> BMC150_MAGN_SHIFT_ODR; + odr_val = FIELD_GET(BMC150_MAGN_MASK_ODR, reg_val); for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++) if (bmc150_magn_samp_freq_table[i].reg_val == odr_val) { @@ -304,21 +305,17 @@ static int bmc150_magn_get_odr(struct bmc150_magn_data *data, int *val) static int bmc150_magn_set_odr(struct bmc150_magn_data *data, int val) { - int ret; u8 i; for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++) { - if (bmc150_magn_samp_freq_table[i].freq == val) { - ret = regmap_update_bits(data->regmap, - BMC150_MAGN_REG_OPMODE_ODR, - BMC150_MAGN_MASK_ODR, - bmc150_magn_samp_freq_table[i]. - reg_val << - BMC150_MAGN_SHIFT_ODR); - if (ret < 0) - return ret; - return 0; - } + if (bmc150_magn_samp_freq_table[i].freq != val) + continue; + + return regmap_update_bits(data->regmap, + BMC150_MAGN_REG_OPMODE_ODR, + BMC150_MAGN_MASK_ODR, + FIELD_PREP(BMC150_MAGN_MASK_ODR, + bmc150_magn_samp_freq_table[i].reg_val)); } return -EINVAL; @@ -801,7 +798,7 @@ static int bmc150_magn_data_rdy_trigger_set_state(struct iio_trigger *trig, ret = regmap_update_bits(data->regmap, BMC150_MAGN_REG_INT_DRDY, BMC150_MAGN_MASK_DRDY_EN, - state << BMC150_MAGN_SHIFT_DRDY_EN); + FIELD_PREP(BMC150_MAGN_MASK_DRDY_EN, state)); if (ret < 0) return ret; From 37a223279289c8affeed77527013e13dc550367c Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Mon, 8 Jun 2026 12:15:58 +0000 Subject: [PATCH 006/513] iio: imu: bmi323: remove unnecessary cast in watermark limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the explicit u32 cast in the watermark limit calculation. The BMI323_FIFO_FULL_IN_FRAMES macro can be used directly with min() without triggering type issues. Signed-off-by: Hungyu Lin Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/imu/bmi323/bmi323_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/imu/bmi323/bmi323_core.c b/drivers/iio/imu/bmi323/bmi323_core.c index f3d499423399..72955a697a93 100644 --- a/drivers/iio/imu/bmi323/bmi323_core.c +++ b/drivers/iio/imu/bmi323/bmi323_core.c @@ -1128,7 +1128,7 @@ static int bmi323_set_watermark(struct iio_dev *indio_dev, unsigned int val) { struct bmi323_data *data = iio_priv(indio_dev); - val = min(val, (u32)BMI323_FIFO_FULL_IN_FRAMES); + val = min(val, BMI323_FIFO_FULL_IN_FRAMES); guard(mutex)(&data->mutex); data->watermark = val; From f0611bb186e4025514bc4409e36c18a07b1cb519 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Tue, 9 Jun 2026 21:05:01 -0700 Subject: [PATCH 007/513] iio: core: trigger: correct CONFIG_IIO_TRIGGER macro name in #endif comment A comment in drivers/iio/iio_core_trigger.h incorrectly refers to CONFIG_TRIGGER_CONSUMER instead of CONFIG_IIO_TRIGGER. Correct it. Discovered while searching for CONFIG_* symbols referenced in code but not defined in any Kconfig file. Signed-off-by: Ethan Nelson-Moore Reviewed-by: Andy Shevchenko Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/iio_core_trigger.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/iio_core_trigger.h b/drivers/iio/iio_core_trigger.h index e1a56824e07f..4698a857739f 100644 --- a/drivers/iio/iio_core_trigger.h +++ b/drivers/iio/iio_core_trigger.h @@ -56,4 +56,4 @@ static inline int iio_trigger_detach_poll_func(struct iio_trigger *trig, return 0; } -#endif /* CONFIG_TRIGGER_CONSUMER */ +#endif /* CONFIG_IIO_TRIGGER */ From b66a56fae18f1d348d5e8dcfcb75d7800ab936f9 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Wed, 10 Jun 2026 21:07:02 +0530 Subject: [PATCH 008/513] iio: gyro: hid-sensor-gyro-3d: use u32 instead of unsigned Prefer 'u32' instead of bare 'unsigned' for usage_id variable. This matches expected callback API type and improves code clarity. No functional change. Signed-off-by: Sanjay Chitroda Reviewed-by: Maxwell Doose 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 e48c25c87b6d..bbca2111e79b 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -177,7 +177,7 @@ static const struct iio_info gyro_3d_info = { /* Callback handler to send event after all samples are received and captured */ static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); @@ -199,7 +199,7 @@ static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, size_t raw_len, char *raw_data, void *priv) { @@ -234,7 +234,7 @@ static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev, static int gyro_3d_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, struct iio_chan_spec *channels, - unsigned usage_id, + u32 usage_id, struct gyro_3d_state *st) { int ret; From b720b5d6835cd8a61db248b1ff5798a69a470719 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Wed, 10 Jun 2026 21:07:03 +0530 Subject: [PATCH 009/513] iio: accel: hid-sensor-accel-3d: use u32 instead of unsigned Prefer 'u32' instead of bare 'unsigned' for usage_id variable. This matches expected callback API type and improves code clarity. No functional change. Signed-off-by: Sanjay Chitroda Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/accel/hid-sensor-accel-3d.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 2bf05ab5235e..42c4259bf209 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -223,7 +223,7 @@ static void hid_sensor_push_data(struct iio_dev *indio_dev, void *data, /* Callback handler to send event after all samples are received and captured */ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); @@ -247,7 +247,7 @@ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, size_t raw_len, char *raw_data, void *priv) { @@ -283,7 +283,7 @@ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev, static int accel_3d_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, struct iio_chan_spec *channels, - unsigned usage_id, + u32 usage_id, struct accel_3d_state *st) { int ret; From d5b231ec6b0903480bae49475c7acd31e0077a4c Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Wed, 10 Jun 2026 21:07:04 +0530 Subject: [PATCH 010/513] iio: light: hid-sensor-als: use u32 instead of unsigned Prefer 'u32' instead of bare 'unsigned' for usage_id variable. This matches expected callback API type and improves code clarity. No functional change. Signed-off-by: Sanjay Chitroda Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/light/hid-sensor-als.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index d72e260b8266..ae2fda8dc500 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -241,7 +241,7 @@ static const struct iio_info als_info = { /* Callback handler to send event after all samples are received and captured */ static int als_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); @@ -263,7 +263,7 @@ static int als_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int als_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, size_t raw_len, char *raw_data, void *priv) { @@ -305,7 +305,7 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int als_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, struct als_state *st) { struct iio_chan_spec *channels; From 946d6045f442ad1c705c5dfb7f48747e84a4180a Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Wed, 10 Jun 2026 21:07:05 +0530 Subject: [PATCH 011/513] iio: light: hid-sensor-prox: use u32 instead of unsigned Prefer 'u32' instead of bare 'unsigned' for usage_id variable. This matches expected callback API type and improves code clarity. No functional change. Signed-off-by: Sanjay Chitroda Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/light/hid-sensor-prox.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index edc9274a2c07..9059f00f0ced 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -166,7 +166,7 @@ static const struct iio_info prox_info = { /* Callback handler to send event after all samples are received and captured */ static int prox_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); @@ -183,7 +183,7 @@ static int prox_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int prox_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, size_t raw_len, char *raw_data, void *priv) { From 2253c055bcdc298e49b2d2d5abcb784e2e9fd727 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Wed, 10 Jun 2026 21:07:06 +0530 Subject: [PATCH 012/513] iio: orientation: hid-sensor-incl-3d: use u32 instead of unsigned Prefer 'u32' instead of bare 'unsigned' for usage_id variable. This matches expected callback API type and improves code clarity. No functional change. Signed-off-by: Sanjay Chitroda Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/orientation/hid-sensor-incl-3d.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c index 4e23a598a3fb..c7fbff498be7 100644 --- a/drivers/iio/orientation/hid-sensor-incl-3d.c +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -189,7 +189,7 @@ static const struct iio_info incl_3d_info = { /* Callback handler to send event after all samples are received and captured */ static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); @@ -212,7 +212,7 @@ static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int incl_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, size_t raw_len, char *raw_data, void *priv) { @@ -247,7 +247,7 @@ static int incl_3d_capture_sample(struct hid_sensor_hub_device *hsdev, static int incl_3d_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, struct iio_chan_spec *channels, - unsigned usage_id, + u32 usage_id, struct incl_3d_state *st) { int ret; From d92974cded424b8161dd6b41e45fd2e7a2c69dbc Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Wed, 10 Jun 2026 21:07:07 +0530 Subject: [PATCH 013/513] iio: orientation: hid-sensor-rotation: use u32 instead of unsigned Prefer 'u32' instead of bare 'unsigned' for usage_id variable. This matches expected callback API type and improves code clarity. No functional change. Signed-off-by: Sanjay Chitroda Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/orientation/hid-sensor-rotation.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index 4a11e4555099..20563d8efaf6 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -176,7 +176,7 @@ static const struct iio_info dev_rot_info = { /* Callback handler to send event after all samples are received and captured */ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); @@ -209,7 +209,7 @@ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, size_t raw_len, char *raw_data, void *priv) { @@ -240,7 +240,7 @@ static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int dev_rot_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, struct dev_rot_state *st) { int ret; From 11f8f7e813edcab1b8bedd0a95da9d2c8835dc93 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Wed, 10 Jun 2026 21:07:08 +0530 Subject: [PATCH 014/513] iio: pressure: hid-sensor-press: use u32 instead of unsigned Prefer 'u32' instead of bare 'unsigned' for usage_id variable. This matches expected callback API type and improves code clarity. No functional change. Signed-off-by: Sanjay Chitroda Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/hid-sensor-press.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c index a039b99d9851..3e47a10d72a8 100644 --- a/drivers/iio/pressure/hid-sensor-press.c +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -155,7 +155,7 @@ static const struct iio_info press_info = { /* Callback handler to send event after all samples are received and captured */ static int press_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); @@ -176,7 +176,7 @@ static int press_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int press_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, size_t raw_len, char *raw_data, void *priv) { @@ -204,7 +204,7 @@ static int press_capture_sample(struct hid_sensor_hub_device *hsdev, static int press_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, struct iio_chan_spec *channels, - unsigned usage_id, + u32 usage_id, struct press_state *st) { int ret; From f0b9af38653ae748efc073ee0036b4d34ab99d6b Mon Sep 17 00:00:00 2001 From: Eduardo Guedes Date: Wed, 10 Jun 2026 10:23:37 -0300 Subject: [PATCH 015/513] iio: frequency: adf4377: replace mutex_lock/unlock with guard(mutex)() Replace manual mutex_lock()/mutex_unlock() calls with guard(mutex)() from cleanup.h. Using guard(mutex)() from cleanup.h allows the compiler to enforce lock release on every exit path, eliminating the error-prone manual lock/unlock pattern and the goto-based exit labels that existed in adf4377_get_freq() and adf4377_set_freq(). This reduces the chance of lock imbalance bugs. Signed-off-by: Eduardo Guedes Co-developed-by: Lucca Ciriac Signed-off-by: Lucca Ciriac Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adf4377.c | 81 +++++++++++++++------------------ 1 file changed, 36 insertions(+), 45 deletions(-) diff --git a/drivers/iio/frequency/adf4377.c b/drivers/iio/frequency/adf4377.c index ff6077e29c73..bb11ae1b8904 100644 --- a/drivers/iio/frequency/adf4377.c +++ b/drivers/iio/frequency/adf4377.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -518,14 +519,15 @@ static int adf4377_get_freq(struct adf4377_state *st, u64 *freq) u64 clkin_freq; int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); + ret = regmap_read(st->regmap, 0x12, &ref_div_factor); if (ret) - goto exit; + return ret; ret = regmap_bulk_read(st->regmap, 0x10, st->buf, sizeof(st->buf)); if (ret) - goto exit; + return ret; clkin_freq = clk_get_rate(st->clkin); ref_div_factor = FIELD_GET(ADF4377_0012_R_DIV_MSK, ref_div_factor); @@ -533,10 +535,8 @@ static int adf4377_get_freq(struct adf4377_state *st, u64 *freq) get_unaligned_le16(&st->buf)); *freq = div_u64(clkin_freq, ref_div_factor) * n_int; -exit: - mutex_unlock(&st->lock); - return ret; + return 0; } static int adf4377_set_freq(struct adf4377_state *st, u64 freq) @@ -545,26 +545,24 @@ static int adf4377_set_freq(struct adf4377_state *st, u64 freq) u64 f_vco; int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); - if (freq > ADF4377_MAX_CLKPN_FREQ || freq < ADF4377_MIN_CLKPN_FREQ) { - ret = -EINVAL; - goto exit; - } + if (freq > ADF4377_MAX_CLKPN_FREQ || freq < ADF4377_MIN_CLKPN_FREQ) + return -EINVAL; ret = regmap_update_bits(st->regmap, 0x1C, ADF4377_001C_EN_DNCLK_MSK | ADF4377_001C_EN_DRCLK_MSK, FIELD_PREP(ADF4377_001C_EN_DNCLK_MSK, 1) | FIELD_PREP(ADF4377_001C_EN_DRCLK_MSK, 1)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x11, ADF4377_0011_EN_AUTOCAL_MSK | ADF4377_0011_DCLK_DIV2_MSK, FIELD_PREP(ADF4377_0011_EN_AUTOCAL_MSK, 1) | FIELD_PREP(ADF4377_0011_DCLK_DIV2_MSK, st->dclk_div2)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x2E, ADF4377_002E_EN_ADC_CNV_MSK | ADF4377_002E_EN_ADC_MSK | @@ -574,56 +572,56 @@ static int adf4377_set_freq(struct adf4377_state *st, u64 freq) FIELD_PREP(ADF4377_002E_ADC_A_CONV_MSK, ADF4377_002E_ADC_A_CONV_VCO_CALIB)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x20, ADF4377_0020_EN_ADC_CLK_MSK, FIELD_PREP(ADF4377_0020_EN_ADC_CLK_MSK, 1)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x2F, ADF4377_002F_DCLK_DIV1_MSK, FIELD_PREP(ADF4377_002F_DCLK_DIV1_MSK, st->dclk_div1)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x24, ADF4377_0024_DCLK_MODE_MSK, FIELD_PREP(ADF4377_0024_DCLK_MODE_MSK, st->dclk_mode)); if (ret) - goto exit; + return ret; ret = regmap_write(st->regmap, 0x27, FIELD_PREP(ADF4377_0027_SYNTH_LOCK_TO_LSB_MSK, st->synth_lock_timeout)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x28, ADF4377_0028_SYNTH_LOCK_TO_MSB_MSK, FIELD_PREP(ADF4377_0028_SYNTH_LOCK_TO_MSB_MSK, st->synth_lock_timeout >> 8)); if (ret) - goto exit; + return ret; ret = regmap_write(st->regmap, 0x29, FIELD_PREP(ADF4377_0029_VCO_ALC_TO_LSB_MSK, st->vco_alc_timeout)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x2A, ADF4377_002A_VCO_ALC_TO_MSB_MSK, FIELD_PREP(ADF4377_002A_VCO_ALC_TO_MSB_MSK, st->vco_alc_timeout >> 8)); if (ret) - goto exit; + return ret; ret = regmap_write(st->regmap, 0x26, FIELD_PREP(ADF4377_0026_VCO_BAND_DIV_MSK, st->vco_band_div)); if (ret) - goto exit; + return ret; ret = regmap_write(st->regmap, 0x2D, FIELD_PREP(ADF4377_002D_ADC_CLK_DIV_MSK, st->adc_clk_div)); if (ret) - goto exit; + return ret; st->clkout_div_sel = 0; @@ -641,24 +639,24 @@ static int adf4377_set_freq(struct adf4377_state *st, u64 freq) FIELD_PREP(ADF4377_0011_EN_RDBLR_MSK, 0) | FIELD_PREP(ADF4377_0011_N_INT_MSB_MSK, st->n_int >> 8)); if (ret) - goto exit; + return ret; ret = regmap_update_bits(st->regmap, 0x12, ADF4377_0012_R_DIV_MSK | ADF4377_0012_CLKOUT_DIV_MSK, FIELD_PREP(ADF4377_0012_CLKOUT_DIV_MSK, st->clkout_div_sel) | FIELD_PREP(ADF4377_0012_R_DIV_MSK, st->ref_div_factor)); if (ret) - goto exit; + return ret; ret = regmap_write(st->regmap, 0x10, FIELD_PREP(ADF4377_0010_N_INT_LSB_MSK, st->n_int)); if (ret) - goto exit; + return ret; ret = regmap_read_poll_timeout(st->regmap, 0x49, read_val, !(read_val & (ADF4377_0049_FSM_BUSY_MSK)), 200, 200 * 100); if (ret) - goto exit; + return ret; /* Disable EN_DNCLK, EN_DRCLK */ ret = regmap_update_bits(st->regmap, 0x1C, ADF4377_001C_EN_DNCLK_MSK | @@ -666,26 +664,21 @@ static int adf4377_set_freq(struct adf4377_state *st, u64 freq) FIELD_PREP(ADF4377_001C_EN_DNCLK_MSK, 0) | FIELD_PREP(ADF4377_001C_EN_DRCLK_MSK, 0)); if (ret) - goto exit; + return ret; /* Disable EN_ADC_CLK */ ret = regmap_update_bits(st->regmap, 0x20, ADF4377_0020_EN_ADC_CLK_MSK, FIELD_PREP(ADF4377_0020_EN_ADC_CLK_MSK, 0)); if (ret) - goto exit; + return ret; /* Set output Amplitude */ - ret = regmap_update_bits(st->regmap, 0x19, ADF4377_0019_CLKOUT2_OP_MSK | - ADF4377_0019_CLKOUT1_OP_MSK, - FIELD_PREP(ADF4377_0019_CLKOUT1_OP_MSK, - ADF4377_0019_CLKOUT_420MV) | - FIELD_PREP(ADF4377_0019_CLKOUT2_OP_MSK, - ADF4377_0019_CLKOUT_420MV)); - -exit: - mutex_unlock(&st->lock); - - return ret; + return regmap_update_bits(st->regmap, 0x19, ADF4377_0019_CLKOUT2_OP_MSK | + ADF4377_0019_CLKOUT1_OP_MSK, + FIELD_PREP(ADF4377_0019_CLKOUT1_OP_MSK, + ADF4377_0019_CLKOUT_420MV) | + FIELD_PREP(ADF4377_0019_CLKOUT2_OP_MSK, + ADF4377_0019_CLKOUT_420MV)); } static void adf4377_gpio_init(struct adf4377_state *st) @@ -919,13 +912,11 @@ static int adf4377_properties_parse(struct adf4377_state *st) static int adf4377_freq_change(struct notifier_block *nb, unsigned long action, void *data) { struct adf4377_state *st = container_of(nb, struct adf4377_state, nb); - int ret; if (action == POST_RATE_CHANGE) { - mutex_lock(&st->lock); - ret = notifier_from_errno(adf4377_init(st)); - mutex_unlock(&st->lock); - return ret; + guard(mutex)(&st->lock); + + return notifier_from_errno(adf4377_init(st)); } return NOTIFY_OK; From 1326e71cd7c9f48b8da677742b7b553874b6c953 Mon Sep 17 00:00:00 2001 From: Aldo Conte Date: Tue, 9 Jun 2026 17:55:15 +0200 Subject: [PATCH 016/513] iio: light: tcs3472: implement wait time and sampling frequency The TCS3472 has a wait state controlled by the WEN bit in the ENABLE register and the WAIT register, with an additional WLONG bit in CONFIG that if set multiplies the wait step by 12. The driver previously defined TCS3472_WTIME but never used it leaving the TODO comment on the top of the source file. Implement control of the wait time through IIO_CHAN_INFO_SAMP_FREQ: - Reading sampling_frequency returns the chip's current cycle time, computed as the sum of ATIME, the fixed RGBC initialization time and the wait time (which depends on WEN and WLONG). - Writing sampling_frequency programs WTIME so that the resulting cycle period approximates the requested frequency. If the requested frequency cannot be reached with any non-zero wait time, WEN is disabled and the chip runs back-to-back conversions at the maximum rate allowed by ATIME. If the requested period exceeds the maximum WTIME range, WLONG is enabled to extend the wait step from 2.4 ms to 28.8 ms. - The user's last requested frequency is stored in the driver's private data so that subsequent changes to integration_time recompute WTIME and preserve the requested sampling rate as closely as possible. Add TCS3472_ENABLE_WEN, TCS3472_ENABLE_RUN and TCS3472_CONFIG_WLONG bit definitions. TCS3472_ENABLE_RUN bundles the bits (AEN | PON | WEN) that are simultaneously set when the chip is in running state and cleared during powerdown, and is used by tcs3472_probe(), tcs3472_powerdown(). Add a u8 enable_pre_suspend field to struct tcs3472_data: tcs3472_powerdown() snapshots data->enable into it, and tcs3472_resume() restores enable register content from the snapshot. This preserves the user's WEN choice across suspend/resume. Bound tcs3472_req_data() polling to the worst-case cycle time (~8 seconds with ATIME=0x00, WTIME=0x00, WLONG=1). Fix the event period calculation in tcs3472_read_event() and tcs3472_write_event() to use tcs3472_cycle_time_us() instead of ATIME alone. With WEN enabled, the chip cycle includes the wait time now. Remove the "TODO: wait time" comment at the top of the file. Reviewed-by: Andy Shevchenko Signed-off-by: Aldo Conte Signed-off-by: Jonathan Cameron --- drivers/iio/light/tcs3472.c | 317 +++++++++++++++++++++++++++++++++--- 1 file changed, 291 insertions(+), 26 deletions(-) diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index b61442c5c1be..b392943e51c8 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -9,8 +9,6 @@ * TCS34727) * * Datasheet: http://ams.com/eng/content/download/319364/1117183/file/TCS3472_Datasheet_EN_v2.pdf - * - * TODO: wait time */ #include @@ -19,6 +17,7 @@ #include #include #include +#include #include #include @@ -53,19 +52,30 @@ #define TCS3472_STATUS_AINT BIT(4) #define TCS3472_STATUS_AVALID BIT(0) #define TCS3472_ENABLE_AIEN BIT(4) +#define TCS3472_ENABLE_WEN BIT(3) #define TCS3472_ENABLE_AEN BIT(1) #define TCS3472_ENABLE_PON BIT(0) +#define TCS3472_ENABLE_RUN \ + (TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON | TCS3472_ENABLE_WEN) #define TCS3472_CONTROL_AGAIN_MASK (BIT(0) | BIT(1)) +#define TCS3472_CONFIG_WLONG BIT(1) + +#define TCS3472_ATIME_TO_US(atime) (((256) - (atime)) * 2400) struct tcs3472_data { struct i2c_client *client; struct mutex lock; + int target_freq_hz; + int target_freq_uhz; u16 low_thresh; u16 high_thresh; u8 enable; + u8 enable_pre_suspend; u8 control; u8 atime; u8 apers; + u8 wtime; + bool wlong; }; static const struct iio_event_spec tcs3472_events[] = { @@ -91,6 +101,7 @@ static const struct iio_event_spec tcs3472_events[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBSCALE) | \ BIT(IIO_CHAN_INFO_INT_TIME), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .channel2 = IIO_MOD_LIGHT_##_color, \ .address = _addr, \ .scan_index = _si, \ @@ -114,9 +125,65 @@ static const struct iio_chan_spec tcs3472_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(4), }; +/* + * The chip's cycle time is the sum of three components: + * - ATIME: the programmable RGBC integration time. + * - The fixed RGBC initialization time (2400 us). + * - WTIME: the wait time, used only if WEN is set. If WLONG is active, + * the wait step is multiplied by 12 (2400 us -> 28800 us). + */ +static unsigned int tcs3472_cycle_time_us(struct tcs3472_data *data) +{ + unsigned int atime_us = TCS3472_ATIME_TO_US(data->atime); + unsigned int init_us = 2400; + unsigned int wtime_us; + + if (!(data->enable & TCS3472_ENABLE_WEN)) + wtime_us = 0; + else if (data->wlong) + wtime_us = (256 - data->wtime) * 28800; + else + wtime_us = (256 - data->wtime) * 2400; + + return atime_us + init_us + wtime_us; +} + +/* + * Convert a cycle time in microseconds to a frequency in Hz and microhertz. + * + * Given cycle_us = T (the cycle period in microseconds), the corresponding + * frequency is: + * f = 1e6 / T [Hz] + * + * The result is split into the IIO_VAL_INT_PLUS_MICRO format: + * val = floor(1e6 / T) [Hz] + * val2 = (1e6 mod T) * 1e6 / T [microhertz] + */ +static void tcs3472_cycle_to_freq(unsigned int cycle_us, int *val, int *val2) +{ + *val = USEC_PER_SEC / cycle_us; + *val2 = div_u64((u64)(USEC_PER_SEC % cycle_us) * USEC_PER_SEC, + cycle_us); +} + static int tcs3472_req_data(struct tcs3472_data *data) { - int tries = 50; + /* + * The worst-case cycle time is reached with ATIME=0x00, WTIME=0x00 + * and WLONG=1. So: + * 614 ms (Max Integration Time) + * + 2.4 ms (RGBC Init) + * + 7.37 s (Max Wait Time) + * = ~ 8 s (Total Max cycle time). + * Use that as a polling upper bound; in normal operation the loop + * exits as soon as AVALID is set. So the total number of tries in 8 + * seconds considering a polling period of 20 ms is 400. + * Considering a 20% margin due to oscillator tolerance, the total + * duration becomes approximately 9.8 seconds, which corresponds to + * about 480 steps. Therefore, setting it to 500 appears to be a + * reasonable and safe trade-off. + */ + int tries = 500; int ret; while (tries--) { @@ -164,18 +231,165 @@ static int tcs3472_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_INT_TIME: *val = 0; - *val2 = (256 - data->atime) * 2400; + *val2 = TCS3472_ATIME_TO_US(data->atime); return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: { + unsigned int cycle_us; + + guard(mutex)(&data->lock); + cycle_us = tcs3472_cycle_time_us(data); + tcs3472_cycle_to_freq(cycle_us, val, val2); + return IIO_VAL_INT_PLUS_MICRO; + } default: return -EINVAL; } } +/* + * __tcs3472_set_sampling_freq() - implementation of sampling frequency + * configuration. The caller must hold data->lock. + */ +static int __tcs3472_set_sampling_freq(struct tcs3472_data *data, + int val, int val2) +{ + unsigned int atime_us; + unsigned int init_us = 2400; + u64 cycle_us; + s64 wait_us; + int wtime; + bool wlong = false; + u8 config; + int ret; + + if (val < 0 || val2 < 0 || (val == 0 && val2 == 0)) + return -EINVAL; + + atime_us = TCS3472_ATIME_TO_US(data->atime); + + /* + * cycle_us = 1 / freq, expressed in microseconds. + * Numerator: 1 [s] = PSEC_PER_SEC [ps] + * Denominator: freq [Hz] * MICROHZ_PER_HZ + val2 [uHz] = freq in [uHz] + * Result: ps / uHz = us + */ + cycle_us = div64_u64(PSEC_PER_SEC, (u64)val * MICROHZ_PER_HZ + val2); + + /* + * wait_us can be negative when the requested frequency is too high + * to be reached, or very large when the requested frequency is + * close to zero. Use s64 to cover the full range: + * + * cycle_us = PSEC_PER_SEC / (val * MICROHZ_PER_HZ + val2) + * + * The divisor of the formula above reaches its maximum when + * val = val2 = INT_MAX: + * INT_MAX * MICROHZ_PER_HZ + INT_MAX = ~2.15e18 + * so cycle_us_min = floor(1e12 / 2.15e18) = 0. + * + * The divisor reaches its minimum (1) when val = 0 and val2 = 1, + * so cycle_us_max = 1e12 / 1 = 1e12. + * + * Therefore: + * wait_us_min = 0 - 2400 - 612000 = -616800 + * wait_us_max = 1e12 - 2400 - 2400 = 999999995200 + * + * Both fit comfortably in s64. + */ + wait_us = (s64)cycle_us - init_us - atime_us; + if (wait_us < 2400) { + if (data->enable & TCS3472_ENABLE_WEN) { + u8 enable = data->enable & ~TCS3472_ENABLE_WEN; + + ret = i2c_smbus_write_byte_data(data->client, + TCS3472_ENABLE, enable); + if (ret) + return ret; + + data->enable = enable; + } + + data->target_freq_hz = val; + data->target_freq_uhz = val2; + return 0; + } + + /* + * Wait state is needed: make sure WEN is active before programming + * WTIME (and possibly WLONG). + */ + if (!(data->enable & TCS3472_ENABLE_WEN)) { + u8 enable = data->enable | TCS3472_ENABLE_WEN; + + ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, + enable); + if (ret) + return ret; + + data->enable = enable; + } + + wtime = 256 - DIV_ROUND_CLOSEST_ULL(wait_us, 2400); + if (wtime < 0) { + /* + * If wait_us is too high (so the requested frequency is too + * low), the resulting wait exceeds what WTIME can represent + * (max 614 ms without WLONG). Enable WLONG, whose step is 12x + * longer (28.8 ms instead of 2.4 ms), and recompute. + */ + wlong = true; + wtime = 256 - DIV_ROUND_CLOSEST_ULL(wait_us, 28800); + } + + if (wlong != data->wlong) { + ret = i2c_smbus_read_byte_data(data->client, TCS3472_CONFIG); + if (ret < 0) + return ret; + + config = ret; + if (wlong) + config |= TCS3472_CONFIG_WLONG; + else + config &= ~TCS3472_CONFIG_WLONG; + + ret = i2c_smbus_write_byte_data(data->client, TCS3472_CONFIG, + config); + if (ret) + return ret; + + data->wlong = wlong; + } + + /* + * If the requested wait is so long that even WLONG cannot + * cover it, wtime may still be negative. Saturate to 0, + * which is the largest possible wait (256 * 28.8 ms = 7.37 s). + */ + wtime = clamp(wtime, 0, 255); + ret = i2c_smbus_write_byte_data(data->client, TCS3472_WTIME, wtime); + if (ret) + return ret; + + data->wtime = wtime; + data->target_freq_hz = val; + data->target_freq_uhz = val2; + + return 0; +} + +static int tcs3472_set_sampling_freq(struct tcs3472_data *data, + int val, int val2) +{ + guard(mutex)(&data->lock); + return __tcs3472_set_sampling_freq(data, val, val2); +} + static int tcs3472_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct tcs3472_data *data = iio_priv(indio_dev); + int ret; int i; switch (mask) { @@ -196,15 +410,31 @@ static int tcs3472_write_raw(struct iio_dev *indio_dev, if (val != 0) return -EINVAL; for (i = 0; i < 256; i++) { - if (val2 == (256 - i) * 2400) { - data->atime = i; - return i2c_smbus_write_byte_data( - data->client, TCS3472_ATIME, - data->atime); - } + if (val2 != (256 - i) * 2400) + continue; + guard(mutex)(&data->lock); + + ret = i2c_smbus_write_byte_data(data->client, + TCS3472_ATIME, i); + if (ret) + return ret; + + data->atime = i; + + /* + * ATIME just changed, so the cycle time changed too. + * Re-run the sampling frequency logic to recompute + * WTIME and preserve the user's last requested + * frequency. Lock is already held. + */ + return __tcs3472_set_sampling_freq(data, + data->target_freq_hz, + data->target_freq_uhz); } return -EINVAL; + case IIO_CHAN_INFO_SAMP_FREQ: + return tcs3472_set_sampling_freq(data, val, val2); default: return -EINVAL; } @@ -234,7 +464,7 @@ static int tcs3472_read_event(struct iio_dev *indio_dev, data->high_thresh : data->low_thresh; return IIO_VAL_INT; case IIO_EV_INFO_PERIOD: - period = (256 - data->atime) * 2400 * + period = tcs3472_cycle_time_us(data) * tcs3472_intr_pers[data->apers]; *val = period / USEC_PER_SEC; *val2 = period % USEC_PER_SEC; @@ -279,11 +509,13 @@ static int tcs3472_write_event(struct iio_dev *indio_dev, data->low_thresh = val; return 0; - case IIO_EV_INFO_PERIOD: + case IIO_EV_INFO_PERIOD:{ + unsigned int cycle_us; + period = val * USEC_PER_SEC + val2; + cycle_us = tcs3472_cycle_time_us(data); for (i = 1; i < ARRAY_SIZE(tcs3472_intr_pers) - 1; i++) { - if (period <= (256 - data->atime) * 2400 * - tcs3472_intr_pers[i]) + if (period <= cycle_us * tcs3472_intr_pers[i]) break; } ret = i2c_smbus_write_byte_data(data->client, TCS3472_PERS, i); @@ -293,6 +525,7 @@ static int tcs3472_write_event(struct iio_dev *indio_dev, data->apers = i; return 0; + } default: return -EINVAL; } @@ -434,17 +667,16 @@ static const struct iio_info tcs3472_info = { static int tcs3472_powerdown(struct tcs3472_data *data) { int ret; - u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; guard(mutex)(&data->lock); + data->enable_pre_suspend = data->enable; + ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, - data->enable & ~enable_mask); + data->enable & ~TCS3472_ENABLE_RUN); if (ret) return ret; - data->enable &= ~enable_mask; - return 0; } @@ -458,7 +690,9 @@ static int tcs3472_probe(struct i2c_client *client) struct device *dev = &client->dev; struct tcs3472_data *data; struct iio_dev *indio_dev; + unsigned int cycle_us; int ret; + u8 enable; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) @@ -498,6 +732,16 @@ static int tcs3472_probe(struct i2c_client *client) return ret; data->atime = ret; + ret = i2c_smbus_read_byte_data(data->client, TCS3472_WTIME); + if (ret < 0) + return ret; + data->wtime = ret; + + ret = i2c_smbus_read_byte_data(data->client, TCS3472_CONFIG); + if (ret < 0) + return ret; + data->wlong = (ret & TCS3472_CONFIG_WLONG) ? 1 : 0; + ret = i2c_smbus_read_word_data(data->client, TCS3472_AILT); if (ret < 0) return ret; @@ -518,14 +762,30 @@ static int tcs3472_probe(struct i2c_client *client) if (ret < 0) return ret; - /* enable device */ - data->enable = ret | TCS3472_ENABLE_PON | TCS3472_ENABLE_AEN; - data->enable &= ~TCS3472_ENABLE_AIEN; - ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, - data->enable); + /* + * Enable the chip in its full running state, including WEN. The + * actual wait time is controlled by the WTIME and WLONG registers, + * which retain their power-on defaults until userspace writes to + * sampling_frequency. + */ + enable = (ret | TCS3472_ENABLE_RUN) & ~TCS3472_ENABLE_AIEN; + + ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, enable); if (ret < 0) return ret; + data->enable = enable; + + /* + * Initialize target frequency from the chip's current state so that + * subsequent integration_time changes via IIO_CHAN_INFO_INT_TIME can + * preserve a meaningful sampling rate, even before userspace writes + * sampling_frequency for the first time. + */ + cycle_us = tcs3472_cycle_time_us(data); + tcs3472_cycle_to_freq(cycle_us, &data->target_freq_hz, + &data->target_freq_uhz); + ret = devm_add_action_or_reset(dev, tcs3472_powerdown_action, data); if (ret) return ret; @@ -561,16 +821,21 @@ static int tcs3472_resume(struct device *dev) struct tcs3472_data *data = iio_priv(i2c_get_clientdata( to_i2c_client(dev))); int ret; - u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; guard(mutex)(&data->lock); + /* + * Restore the full ENABLE register from the snapshot taken in + * tcs3472_powerdown(). This preserves the user's last + * sampling_frequency configuration (in particular the WEN bit) + * across suspend/resume. + */ ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, - data->enable | enable_mask); + data->enable_pre_suspend); if (ret) return ret; - data->enable |= enable_mask; + data->enable = data->enable_pre_suspend; return 0; } From cae5bd202cfcac46762286591618b771c124727c Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Sat, 13 Jun 2026 09:01:32 +0530 Subject: [PATCH 017/513] iio: todo: fix typo and refine resource management items Fix a typo in the staging documentation path and adjust formatting of existing entries for readability. Add TODO items to track conversion of selected drivers to device managed resource APIs and evaluate usage of cleanup.h helpers for simplifying resource management. These updates aim to better reflect ongoing cleanup efforts within the IIO subsystem. Signed-off-by: Sanjay Chitroda Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/TODO | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/iio/TODO b/drivers/iio/TODO index 2ace27d1ac62..7b05308248d7 100644 --- a/drivers/iio/TODO +++ b/drivers/iio/TODO @@ -5,12 +5,17 @@ Documentation tree - Yaml conversions for abandoned drivers - ABI Documentation - - Audit driviers/iio/staging/Documentation + - Audit drivers/staging/iio/Documentation -- Converting drivers from device tree centric to more generic -property handlers. +- Converting drivers from device tree centric to generic property handlers. - Refactor old platform_data constructs from drivers and convert it -to state struct and using property handlers and readers. + to state struct and using property handlers and readers. + +- Convert selected IIO drivers to use fully device managed resource APIs. + +- Evaluate and possibly adopt cleanup.h helpers (guard()(), __free(), et cetera) + in drivers where manual resource management is still used and there is a + significant improvement to the code flow and complexity to be had. Mailing list: linux-iio@vger.kernel.org From 24221b0370d784fe54e85b1954262ea50de4faee Mon Sep 17 00:00:00 2001 From: Siratul Islam Date: Sun, 14 Jun 2026 14:43:39 +0600 Subject: [PATCH 018/513] iio: proximity: sort the register values for vl53l1x-i2c - Sort register defines list Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Siratul Islam Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/vl53l1x-i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/proximity/vl53l1x-i2c.c b/drivers/iio/proximity/vl53l1x-i2c.c index ff56bfcf8bd2..163ad9828fc4 100644 --- a/drivers/iio/proximity/vl53l1x-i2c.c +++ b/drivers/iio/proximity/vl53l1x-i2c.c @@ -43,6 +43,7 @@ #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_DEFAULT_CONFIG 0x002D #define VL53L1X_REG_GPIO_HV_MUX__CTRL 0x0030 #define VL53L1X_REG_GPIO__TIO_HV_STATUS 0x0031 #define VL53L1X_REG_SYSTEM__INTERRUPT_CONFIG_GPIO 0x0046 @@ -64,7 +65,6 @@ #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 From 9fb6c7ba7bb8eb5b14de3964c6128cbb5faca2a7 Mon Sep 17 00:00:00 2001 From: Siratul Islam Date: Sun, 14 Jun 2026 14:43:40 +0600 Subject: [PATCH 019/513] iio: proximity: cleanup fixes for vl53l1x-i2c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract data-ready polling into a helper, fix regmap_read_poll_timeout() argument alignment, and add field definitions for BIT(0). No functional changes. Suggested-by: Andy Shevchenko Reviewed-by: Nuno Sá Reviewed-by: Andy Shevchenko Signed-off-by: Siratul Islam Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/vl53l1x-i2c.c | 37 +++++++++++++++-------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/drivers/iio/proximity/vl53l1x-i2c.c b/drivers/iio/proximity/vl53l1x-i2c.c index 163ad9828fc4..bb6c59105c6a 100644 --- a/drivers/iio/proximity/vl53l1x-i2c.c +++ b/drivers/iio/proximity/vl53l1x-i2c.c @@ -82,6 +82,9 @@ #define VL53L1X_OSC_CALIBRATE_MASK GENMASK(9, 0) +#define VL53L1X_FIRMWARE__SYSTEM_STATUS_BOOTED BIT(0) +#define VL53L1X_GPIO__TIO_HV_STATUS_DATA_READY BIT(0) + /* Inter-measurement period uses PLL divider with 1.075 oscillator correction */ static const struct u32_fract vl53l1x_osc_correction = { .numerator = 1075, @@ -191,6 +194,17 @@ static int vl53l1x_stop_ranging(struct vl53l1x_data *data) VL53L1X_MODE_START_STOP); } +static int vl53l1x_wait_data_ready(struct vl53l1x_data *data) +{ + unsigned int val; + + /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */ + return regmap_read_poll_timeout(data->regmap, + VL53L1X_REG_GPIO__TIO_HV_STATUS, val, + (val & VL53L1X_GPIO__TIO_HV_STATUS_DATA_READY) != data->gpio_polarity, + 1 * USEC_PER_MSEC, 1 * USEC_PER_SEC); +} + /* * Default configuration blob from ST's VL53L1X Ultra Lite Driver * (STSW-IMG009). @@ -230,10 +244,9 @@ static int vl53l1x_chip_init(struct vl53l1x_data *data) } ret = regmap_read_poll_timeout(data->regmap, - VL53L1X_REG_FIRMWARE__SYSTEM_STATUS, val, - val & BIT(0), - 1 * USEC_PER_MSEC, - 100 * USEC_PER_MSEC); + VL53L1X_REG_FIRMWARE__SYSTEM_STATUS, + val, val & VL53L1X_FIRMWARE__SYSTEM_STATUS_BOOTED, + 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC); if (ret) return dev_err_probe(dev, ret, "firmware boot timeout\n"); @@ -261,12 +274,7 @@ static int vl53l1x_chip_init(struct vl53l1x_data *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); + ret = vl53l1x_wait_data_ready(data); if (ret) return ret; @@ -461,14 +469,7 @@ static int vl53l1x_read_proximity(struct vl53l1x_data *data, int *val) 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); + ret = vl53l1x_wait_data_ready(data); if (ret) return ret; } From 1a6808ca4ad50e709a4f9a2b6d9d840d497a272b Mon Sep 17 00:00:00 2001 From: David Laight Date: Sat, 6 Jun 2026 21:26:31 +0100 Subject: [PATCH 020/513] iio: test: Use strscpy() to copy the test description The buffer length is known to be KUNIT_PARAM_DESC_SIZE Signed-off-by: David Laight Signed-off-by: Jonathan Cameron --- drivers/iio/test/iio-test-rescale.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/test/iio-test-rescale.c b/drivers/iio/test/iio-test-rescale.c index ac6942cf1e44..bcd4a607be19 100644 --- a/drivers/iio/test/iio-test-rescale.c +++ b/drivers/iio/test/iio-test-rescale.c @@ -575,7 +575,7 @@ static const struct rescale_tc_data offset_cases[] = { static void case_to_desc(const struct rescale_tc_data *t, char *desc) { - strcpy(desc, t->name); + strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); } KUNIT_ARRAY_PARAM(iio_rescale_scale, scale_cases, case_to_desc); From 8c740dd4fe5313cf8cebc4b30fca78d73dcc0721 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Fri, 5 Jun 2026 19:42:04 -0500 Subject: [PATCH 021/513] iio: chemical: scd30: Use devm_mutex_init() over non-devm mutex_init() The current code uses mutex_init() instead of devm_mutex_init(), which is incorrect as the rest of the file uses the devm automatic resource management API. Fix this so that the mutex is set up in the same way as the rest of the device data structure. Signed-off-by: Maxwell Doose Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/scd30_core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index c5425136814b..23895744c42d 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -711,7 +711,11 @@ int scd30_probe(struct device *dev, int irq, const char *name, void *priv, state->pressure_comp = SCD30_PRESSURE_COMP_DEFAULT; state->meas_interval = SCD30_MEAS_INTERVAL_DEFAULT; state->command = command; - mutex_init(&state->lock); + + ret = devm_mutex_init(dev, &state->lock); + if (ret) + return ret; + init_completion(&state->meas_ready); dev_set_drvdata(dev, indio_dev); From 842059013e9a550dd9af51a5eb6ead4049caa19d Mon Sep 17 00:00:00 2001 From: Miao Li Date: Thu, 4 Jun 2026 15:00:53 +0800 Subject: [PATCH 022/513] iio: accel: stk8312: Update includes to match IWYU Update the list of included headers in stk8312.c using Include-What-You-Use (IWYU) tool, mainly to remove kernel.h and add missing headers such as array_size.h, bits.h, dev_printk.h, etc. Meanwhile, sort these headers in alphabetical order. Signed-off-by: Miao Li Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/accel/stk8312.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index 8b5a3e0489e9..ad4d11337f4b 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -7,12 +7,20 @@ * IIO driver for STK8312; 7-bit I2C address: 0x3D. */ +#include +#include +#include +#include +#include #include #include -#include #include -#include +#include +#include +#include +#include #include + #include #include #include From 0d294bcb76aff4d44866613573d58d42abc0eab5 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 4 Jun 2026 10:58:55 +0100 Subject: [PATCH 023/513] dt-bindings: iio: frequency: add adf41513 DT-bindings for ADF41513, an ultralow noise PLL frequency synthesizer that can be used to implement local oscillators (LOs) as high as 26.5 GHz. Some properties are based upon an existing PLL device properties (e.g. ADF4350). Reviewed-by: Krzysztof Kozlowski Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- .../bindings/iio/frequency/adi,adf41513.yaml | 227 ++++++++++++++++++ MAINTAINERS | 7 + 2 files changed, 234 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml diff --git a/Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml b/Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml new file mode 100644 index 000000000000..f4fae9210382 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml @@ -0,0 +1,227 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/frequency/adi,adf41513.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADF41513 PLL Frequency Synthesizer + +maintainers: + - Rodrigo Alencar + +description: + The ADF41513 is an ultralow noise frequency synthesizer that can be used to + implement local oscillators (LOs) as high as 26.5 GHz in the upconversion and + downconversion sections of wireless receivers and transmitters. The ADF41510 + supports frequencies up to 10 GHz. + + https://www.analog.com/en/products/adf41510.html + https://www.analog.com/en/products/adf41513.html + +properties: + compatible: + enum: + - adi,adf41510 + - adi,adf41513 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 25000000 + + clocks: + maxItems: 1 + description: Clock that provides the reference input frequency. + + avdd1-supply: + description: PFD and Up and Down Digital Driver Power Supply (3.3 V) + + avdd2-supply: + description: RF Buffer and Prescaler Power Supply (3.3 V) + + avdd3-supply: + description: N Divider Power Supply (3.3 V) + + avdd4-supply: + description: R Divider and Lock Detector Power Supply (3.3 V) + + avdd5-supply: + description: Sigma-Delta Modulator and SPI Power Supply (3.3 V) + + vp-supply: + description: Charge Pump Power Supply (3.3 V) + + enable-gpios: + description: + GPIO that controls the chip enable pin. A logic low on this pin + powers down the device and puts the charge pump output into + three-state mode. + maxItems: 1 + + lock-detect-gpios: + description: + GPIO for lock detect functionality. When configured for digital lock + detect, this pin will output a logic high when the PLL is locked. + maxItems: 1 + + adi,power-up-frequency-mhz: + minimum: 1000 + maximum: 26500 + default: 10000 + description: + The PLL tunes to this frequency during the initialization sequence. + This property should be set to a frequency supported by the loop filter + and VCO used in the design. Range is 1 GHz to 26.5 GHz for ADF41513, + and 1 GHz to 10 GHz for ADF41510. + + adi,reference-div-factor: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + maximum: 32 + default: 1 + description: + Value for the reference division factor (R Counter). The driver will + increment R Counter as needed to achieve a PFD frequency within the + allowed range. High R counter values will reduce the PFD frequency, which + lowers the frequency resolution, and affects phase noise performance. + As it affects the PFD frequency, this value depends on the loop filter + design. + + adi,reference-doubler-enable: + description: + Enables the reference doubler when deriving the PFD frequency. + The maximum reference frequency when the doubler is enabled is 225 MHz. + As it affects the PFD frequency, this value depends on the loop filter + design. + type: boolean + + adi,reference-div2-enable: + description: + Enables the reference divide-by-2 function when deriving the PFD + frequency. As it affects the PFD frequency, this value depends on the + loop filter design. + type: boolean + + adi,charge-pump-resistor-ohms: + minimum: 1800 + maximum: 10000 + default: 2700 + description: + External charge pump resistor (R_SET) value in ohms. This sets the maximum + charge pump current along with the charge pump current setting. + + adi,charge-pump-current-microamp: + minimum: 81 + maximum: 7200 + description: + Charge pump current (I_CP) in microamps. The value will be rounded to the + nearest supported value. Range of acceptable values depends on the + charge pump resistor value, such that 810 mV <= I_CP * R_SET <= 12960 mV. + This value depends on the loop filter and the VCO design. + + adi,logic-level-1v8-enable: + description: + Set MUXOUT and DLD logic levels to 1.8V. Default is 3.3V. + type: boolean + + adi,phase-detector-polarity-positive-enable: + description: + Set phase detector polarity to positive. Default is negative. + Use positive polarity with non-inverting loop filter and VCO with + positive tuning slope, or with inverting loop filter and VCO with + negative tuning slope. + type: boolean + + adi,lock-detector-count: + $ref: /schemas/types.yaml#/definitions/uint32 + default: 64 + description: + Sets the value for Lock Detector count of the PLL, which determines the + number of consecutive phase detector cycles that must be within the lock + detector window before lock is declared. Lower values increase the lock + detection sensitivity, while higher values provides a more stable lock + detection. Applications that consume the lock detect signal may require + different settings based on system requirements. + enum: [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192] + + adi,phase-resync-period-ns: + default: 0 + description: + When this value is non-zero, enable phase resync functionality, which + produces a consistent output phase offset with respect to the input + reference. The value specifies the resync period in nanoseconds, used + to configure clock dividers with respect to the PFD frequency. This value + should be set to a value that is at least as long as the worst case lock + time, i.e., it depends mostly on the loop filter design. + + adi,le-sync-enable: + description: + Synchronizes Load Enable (LE) transitions with the reference signal to + avoid asynchronous glitches in the output. This is recommended when using + the PLL as a frequency synthesizer, where the reference signal will always + be present while the device is being configured. When using the PLL as a + frequency tracker, where the reference signal may be absent, LE sync + should be left disabled. + type: boolean + +dependencies: + adi,charge-pump-resistor-ohms: [ 'adi,charge-pump-current-microamp' ] + +required: + - compatible + - reg + - clocks + - avdd1-supply + - avdd2-supply + - avdd3-supply + - avdd4-supply + - avdd5-supply + - vp-supply + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + - if: + properties: + compatible: + contains: + const: adi,adf41510 + then: + properties: + adi,power-up-frequency-mhz: + maximum: 10000 + +unevaluatedProperties: false + +examples: + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + + pll@0 { + compatible = "adi,adf41513"; + reg = <0>; + spi-max-frequency = <25000000>; + clocks = <&ref_clk>; + avdd1-supply = <&avdd1_3v3>; + avdd2-supply = <&avdd2_3v3>; + avdd3-supply = <&avdd3_3v3>; + avdd4-supply = <&avdd4_3v3>; + avdd5-supply = <&avdd5_3v3>; + vp-supply = <&vp_3v3>; + enable-gpios = <&gpio0 10 GPIO_ACTIVE_HIGH>; + lock-detect-gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>; + + adi,power-up-frequency-mhz = <15500>; + adi,charge-pump-current-microamp = <3600>; + adi,charge-pump-resistor-ohms = <2700>; + adi,reference-doubler-enable; + adi,lock-detector-count = <64>; + adi,phase-resync-period-ns = <0>; + adi,phase-detector-polarity-positive-enable; + adi,le-sync-enable; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..8c5623eb7a2c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1702,6 +1702,13 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml F: drivers/iio/adc/ade9000.c +ANALOG DEVICES INC ADF41513 DRIVER +M: Rodrigo Alencar +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml + ANALOG DEVICES INC ADF4377 DRIVER M: Antoniu Miclaus L: linux-iio@vger.kernel.org From ce0d6380547e3028560910e1c9c8120531341ac6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 4 Jun 2026 10:58:56 +0100 Subject: [PATCH 024/513] lib: kstrtox: Make _parse_integer() take variadic arguments Instead of having different functions that just use default parameters, combine those to use variadic arguments, so the user may call it using the same name. Signed-off-by: Andy Shevchenko Acked-by: Petr Mladek Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- lib/kstrtox.c | 6 ------ lib/kstrtox.h | 9 ++++++++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/kstrtox.c b/lib/kstrtox.c index edc4eb7c1bca..adc03e27e4a2 100644 --- a/lib/kstrtox.c +++ b/lib/kstrtox.c @@ -94,12 +94,6 @@ unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned lon return rv | overflow; } -noinline -unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *p) -{ - return _parse_integer_limit(s, base, p, INT_MAX); -} - static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) { unsigned long long _res; diff --git a/lib/kstrtox.h b/lib/kstrtox.h index 158c400ca865..00cf3255bdd2 100644 --- a/lib/kstrtox.h +++ b/lib/kstrtox.h @@ -2,10 +2,17 @@ #ifndef _LIB_KSTRTOX_H #define _LIB_KSTRTOX_H +#include + #define KSTRTOX_OVERFLOW (1U << 31) const char *_parse_integer_fixup_radix(const char *s, unsigned int *base); unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned long long *res, size_t max_chars); -unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *res); + +#define _parse_integer0(s, base, res, ...) \ + _parse_integer_limit(s, base, res, INT_MAX) + +#define _parse_integer(s, base, res, ...) \ + CONCATENATE(_parse_integer, COUNT_ARGS(__VA_ARGS__))(s, base, res, __VA_ARGS__) #endif From b482908f8cf66a079f2e85af6eb7799b63dda50d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 4 Jun 2026 10:58:57 +0100 Subject: [PATCH 025/513] lib: vsprintf: use _parse_integer() instead of _parse_integer_limit() Use _parse_integer() that allows optional arguments to be explicitly initialised. Signed-off-by: Andy Shevchenko Acked-by: Petr Mladek Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- lib/kstrtox.h | 3 +++ lib/vsprintf.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/kstrtox.h b/lib/kstrtox.h index 00cf3255bdd2..ff84fe434a00 100644 --- a/lib/kstrtox.h +++ b/lib/kstrtox.h @@ -12,6 +12,9 @@ unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned lon #define _parse_integer0(s, base, res, ...) \ _parse_integer_limit(s, base, res, INT_MAX) +#define _parse_integer1(s, base, res, max_chars, ...) \ + _parse_integer_limit(s, base, res, max_chars) + #define _parse_integer(s, base, res, ...) \ CONCATENATE(_parse_integer, COUNT_ARGS(__VA_ARGS__))(s, base, res, __VA_ARGS__) diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 2bc6ef483576..d2cfa1dfc343 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -86,7 +86,7 @@ static unsigned long long simple_strntoull(const char *startp, char **endp, unsi cp = _parse_integer_fixup_radix(startp, &base); prefix_chars = cp - startp; if (prefix_chars < max_chars) { - rv = _parse_integer_limit(cp, base, &result, max_chars - prefix_chars); + rv = _parse_integer(cp, base, &result, max_chars - prefix_chars); /* FIXME */ cp += (rv & ~KSTRTOX_OVERFLOW); } else { From 1eafa5eb97dcb6a76947ffc06c18c272f1eaf1b6 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 4 Jun 2026 10:58:58 +0100 Subject: [PATCH 026/513] lib: kstrtox: add initial value to _parse_integer_limit() Add init parameter to _parse_integer_limit() that defines an initial value for the accumulated result when parsing an 64-bit integer. The new function prototype is adjusted so that the _parse_integer() macros stay consistent allowing for one more argument, which defaults to 0. Signed-off-by: Rodrigo Alencar Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- lib/kstrtox.c | 5 +++-- lib/kstrtox.h | 9 ++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/kstrtox.c b/lib/kstrtox.c index adc03e27e4a2..6d00162aea6c 100644 --- a/lib/kstrtox.c +++ b/lib/kstrtox.c @@ -45,6 +45,7 @@ const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) * @base: Radix * @p: Where to store result * @max_chars: Maximum amount of characters to convert + * @init: Initial value of the multiply-accumulate result * * Convert non-negative integer string representation in explicitly given * radix to an integer. If overflow occurs, value at @p is set to ULLONG_MAX. @@ -56,12 +57,12 @@ const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) */ noinline unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned long long *p, - size_t max_chars) + size_t max_chars, unsigned long long init) { unsigned int rv, overflow = 0; unsigned long long res; - res = 0; + res = init; for (rv = 0; rv < max_chars; rv++, s++) { unsigned int c = *s; unsigned int lc = _tolower(c); diff --git a/lib/kstrtox.h b/lib/kstrtox.h index ff84fe434a00..73dee79fd8ed 100644 --- a/lib/kstrtox.h +++ b/lib/kstrtox.h @@ -7,13 +7,16 @@ #define KSTRTOX_OVERFLOW (1U << 31) const char *_parse_integer_fixup_radix(const char *s, unsigned int *base); unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned long long *res, - size_t max_chars); + size_t max_chars, unsigned long long init); #define _parse_integer0(s, base, res, ...) \ - _parse_integer_limit(s, base, res, INT_MAX) + _parse_integer_limit(s, base, res, INT_MAX, 0) #define _parse_integer1(s, base, res, max_chars, ...) \ - _parse_integer_limit(s, base, res, max_chars) + _parse_integer_limit(s, base, res, max_chars, 0) + +#define _parse_integer2(s, base, res, max_chars, init, ...) \ + _parse_integer_limit(s, base, res, max_chars, init) #define _parse_integer(s, base, res, ...) \ CONCATENATE(_parse_integer, COUNT_ARGS(__VA_ARGS__))(s, base, res, __VA_ARGS__) From b659b963206a9976a5ccc52a4b2aee1f674289cf Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 4 Jun 2026 10:58:59 +0100 Subject: [PATCH 027/513] lib: kstrtox: add kstrtoudec64() and kstrtodec64() Add helpers that parses decimal numbers into 64-bit number, i.e., decimal point numbers with pre-defined scale are parsed into a 64-bit value (fixed precision). After the decimal point, digits beyond the specified scale are ignored. Signed-off-by: Rodrigo Alencar Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- include/linux/kstrtox.h | 3 ++ lib/kstrtox.c | 104 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/include/linux/kstrtox.h b/include/linux/kstrtox.h index 6c9282866770..b41b9736886f 100644 --- a/include/linux/kstrtox.h +++ b/include/linux/kstrtox.h @@ -97,6 +97,9 @@ int __must_check kstrtou8(const char *s, unsigned int base, u8 *res); int __must_check kstrtos8(const char *s, unsigned int base, s8 *res); int __must_check kstrtobool(const char *s, bool *res); +int __must_check kstrtoudec64(const char *s, unsigned int scale, u64 *res); +int __must_check kstrtodec64(const char *s, unsigned int scale, s64 *res); + int __must_check kstrtoull_from_user(const char __user *s, size_t count, unsigned int base, unsigned long long *res); int __must_check kstrtoll_from_user(const char __user *s, size_t count, unsigned int base, long long *res); int __must_check kstrtoul_from_user(const char __user *s, size_t count, unsigned int base, unsigned long *res); diff --git a/lib/kstrtox.c b/lib/kstrtox.c index 6d00162aea6c..bac1c057e1b0 100644 --- a/lib/kstrtox.c +++ b/lib/kstrtox.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -394,6 +395,109 @@ int kstrtobool(const char *s, bool *res) } EXPORT_SYMBOL(kstrtobool); +static int _kstrtoudec64(const char *s, unsigned int scale, u64 *res) +{ + unsigned int rv_int, rv_frac; + u64 _res = 0; + + rv_int = _parse_integer(s, 10, &_res); + if (rv_int & KSTRTOX_OVERFLOW) + return -ERANGE; + s += rv_int; + + if (*s == '.') + s++; /* skip decimal point */ + + rv_frac = _parse_integer(s, 10, &_res, scale, _res); + if (rv_frac & KSTRTOX_OVERFLOW) + return -ERANGE; + s += rv_frac; + + /* + * Check input beyond rv_int and rv_frac to cover cases like ".5" with + * scale 0, which is considered a valid input, being parsed as 0. + */ + if (!rv_int && !rv_frac && !isdigit(*s)) + return -EINVAL; + + while (isdigit(*s)) /* truncate digits */ + s++; + + if (*s == '\n') + s++; + if (*s) + return -EINVAL; + + if (_res && ((scale - rv_frac) > 19 /* log10(2^64) = 19.26 */ || + check_mul_overflow(_res, int_pow(10, scale - rv_frac), &_res))) + return -ERANGE; + + *res = _res; + return 0; +} + +/** + * kstrtoudec64() - Convert a string to an unsigned 64-bit scaled decimal value. + * @s: The start of the string. The string must be null-terminated, and may also + * include a single newline before its terminating null. The first character + * may also be a plus sign, but not a minus sign. + * @scale: The number of digits to the right of the decimal point. + * @res: Where to write the result of the conversion on success. + * + * For example, a scale of 3 with input "123.45" results in 123450. Note that + * trailing zeros in the fractional part input to match the scale are not + * required. Also, digits beyond the specified scale are ignored. + * + * Return: 0 on success, -ERANGE on overflow and -EINVAL on parsing error. + */ +noinline +int kstrtoudec64(const char *s, unsigned int scale, u64 *res) +{ + if (s[0] == '+') + s++; + return _kstrtoudec64(s, scale, res); +} +EXPORT_SYMBOL(kstrtoudec64); + +/** + * kstrtodec64() - Convert a string to a signed 64-bit scaled decimal value. + * @s: The start of the string. The string must be null-terminated, and may also + * include a single newline before its terminating null. The first character + * may also be a plus sign or a minus sign. + * @scale: The number of digits to the right of the decimal point. + * @res: Where to write the result of the conversion on success. + * + * For example, a scale of 4 with input "-3.141592" results in -31415. Note + * that digits beyond the specified scale are ignored. Also, trailing zeros in + * the fractional part input to match the scale are not required. + * + * Return: 0 on success, -ERANGE on overflow and -EINVAL on parsing error. + */ +noinline +int kstrtodec64(const char *s, unsigned int scale, s64 *res) +{ + u64 tmp; + int rv; + + if (s[0] == '-') { + rv = _kstrtoudec64(s + 1, scale, &tmp); + if (rv < 0) + return rv; + if ((s64)-tmp > 0) + return -ERANGE; + *res = -tmp; + } else { + rv = kstrtoudec64(s, scale, &tmp); + if (rv < 0) + return rv; + if ((s64)tmp < 0) + return -ERANGE; + *res = tmp; + } + return 0; +} +EXPORT_SYMBOL(kstrtodec64); + /* * Since "base" would be a nonsense argument, this open-codes the * _from_user helper instead of using the helper macro below. From 467e6414151d621ad0f5dba6d12c91d7ec9a4f00 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 4 Jun 2026 10:59:00 +0100 Subject: [PATCH 028/513] lib: test-kstrtox: tests for kstrtodec64() and kstrtoudec64() Add tests for decimal parsing helpers kstrtodec64() and kstrtoudec64(). The test infrastructure is reused from other kstrto*() functions, i.e., the decimal parsers have fixed base of 10, so base field is used as scale input for the helpers. Reviewed-by: Andy Shevchenko Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- lib/test-kstrtox.c | 182 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/lib/test-kstrtox.c b/lib/test-kstrtox.c index 811128d0df16..9255234752db 100644 --- a/lib/test-kstrtox.c +++ b/lib/test-kstrtox.c @@ -709,6 +709,182 @@ static void __init test_kstrtos8_fail(void) TEST_FAIL(kstrtos8, s8, "%hhd", test_s8_fail); } +static void __init test_kstrtoudec64_ok(void) +{ + DECLARE_TEST_OK(u64, struct test_udec64); + static DEFINE_TEST_OK(struct test_udec64, test_udec64_ok) = { + /* basic: integer.fraction, exact digits */ + {"0.0", 1, 0}, + {"1.5", 1, 15}, + {"1.234", 3, 1234}, + {"42.0", 1, 420}, + /* zero */ + {"0.0", 1, 0}, + {"0.000", 3, 0}, + /* integer only */ + {"0", 1, 0}, + {"42", 3, 42000}, + {"123.", 2, 12300}, + {"1", 1, 10}, + /* fractional only (leading dot) */ + {".5", 1, 5}, + {".5", 0, 0}, + {".123", 3, 123}, + {".001", 3, 1}, + /* zero padding: fewer fractional digits than scale */ + {"1.2", 3, 1200}, + {"1.2", 6, 1200000}, + {"0.01", 3, 10}, + {"0.1", 9, 100000000ULL}, + {"0.01", 9, 10000000}, + /* truncation: more fractional digits than scale */ + {"1.23456", 3, 1234}, + {"3.1415926535", 6, 3141592}, + {"0.999999999", 3, 999}, + {"1.99", 1, 19}, + {"1.234", 0, 1}, + /* trailing newline */ + {"1.5\n", 1, 15}, + {"42\n", 3, 42000}, + /* plus sign */ + {"+1.5", 1, 15}, + {"+.5", 1, 5}, + /* scale progression */ + {"1.", 0, 1}, + {"1.0", 1, 10}, + {"1.00", 2, 100}, + {"1.000", 3, 1000}, + {"1.000000", 6, 1000000}, + {"1.000000000", 9, 1000000000ULL}, + /* max limit check */ + {"18446744073.709551615", 9, ULLONG_MAX}, + {"18446744073709.551615", 6, ULLONG_MAX}, + {"0.18446744073709551615", 20, ULLONG_MAX}, + /* scale > 19: representable when integer part is small */ + {"0.00000000000000000001", 20, 1}, + {"0.1", 20, 10000000000000000000ULL}, + {"0.00000000000000000000001", 23, 1}, + /* truncation with scale > 19 */ + {"0.0000000000000000000012345", 23, 123}, + /* truncation with many excess digits */ + {"0.00000000000000000000000000000000423", 34, 42}, + {"1.99999999999999999999999999999999999", 3, 1999}, + }; + TEST_OK(kstrtoudec64, u64, "%llu", test_udec64_ok); +} + +static void __init test_kstrtoudec64_fail(void) +{ + static DEFINE_TEST_FAIL(test_udec64_fail) = { + /* empty / whitespace */ + {"", 3}, + {"\n", 3}, + /* minus sign (unsigned) */ + {"-1.5", 1}, + {"-0.5", 1}, + /* only a decimal point */ + {".", 3}, + {".", 0}, + /* only a sign */ + {"+", 3}, + /* non-digit characters */ + {"abc", 3}, + {"1.2x", 3}, + /* leading/trailing space */ + {" 1.5", 1}, + {"1.5 ", 1}, + /* overflow */ + {"18446744073710.551615", 6}, + {"99999999999999999999", 1}, + /* overflow with scale > 19 */ + {"1.0", 21}, + {"0.2", 20}, + {"0.18446744073709551616", 20}, + {"1", 20}, + }; + TEST_FAIL(kstrtoudec64, u64, "%llu", test_udec64_fail); +} + +static void __init test_kstrtodec64_ok(void) +{ + DECLARE_TEST_OK(s64, struct test_dec64); + static DEFINE_TEST_OK(struct test_dec64, test_dec64_ok) = { + /* basic positive */ + {"0.0", 1, 0}, + {"1.5", 1, 15}, + {"1.234", 3, 1234}, + /* basic negative */ + {"-1.5", 1, -15}, + {"-1.234", 3, -1234}, + {"-0.5", 1, -5}, + {"-0.001", 3, -1}, + /* zero (signed) */ + {"-0", 1, 0}, + {"-0.0", 1, 0}, + {"0.000", 3, 0}, + /* integer only */ + {"42", 3, 42000}, + {"-42", 3, -42000}, + /* fractional only */ + {".5", 1, 5}, + {"-.5", 1, -5}, + /* zero padding */ + {"1.2", 3, 1200}, + {"-1.2", 3, -1200}, + {"0.01", 3, 10}, + {"-0.01", 3, -10}, + /* truncation */ + {"1.23456", 3, 1234}, + {"-1.23456", 3, -1234}, + {"0.999999999", 3, 999}, + {"-0.999999999", 3, -999}, + /* trailing newline */ + {"1.5\n", 1, 15}, + {"-1.5\n", 1, -15}, + /* plus sign */ + {"+1.5", 1, 15}, + /* limits */ + {"9223372036.854775807", 9, LLONG_MAX}, + {"-9223372036.854775808", 9, LLONG_MIN}, + /* scale > 19 */ + {"0.0", 20, 0}, + {"-0.0", 20, 0}, + {"0.00000000000000000001", 20, 1}, + {"-0.00000000000000000001", 20, -1}, + {"0.009223372036854775807", 21, LLONG_MAX}, + {"-0.009223372036854775808", 21, LLONG_MIN}, + }; + TEST_OK(kstrtodec64, s64, "%lld", test_dec64_ok); +} + +static void __init test_kstrtodec64_fail(void) +{ + static DEFINE_TEST_FAIL(test_dec64_fail) = { + /* empty / whitespace */ + {"", 3}, + {"\n", 3}, + /* no digits after dot */ + {".", 3}, + {"-.", 3}, + /* no digits at all */ + {"+", 3}, + {"-", 3}, + /* non-digit characters */ + {"abc", 3}, + {"-1.2x", 3}, + /* signed overflow */ + {"9223372036.854775808", 9}, + {"-9223372036.854775809", 9}, + {"99999999999999999999", 1}, + /* signed overflow with scale > 19 */ + {"0.1", 20}, + {"-0.1", 20}, + {"0.09223372036854775808", 20}, + {"-0.09223372036854775809", 20}, + }; + TEST_FAIL(kstrtodec64, s64, "%lld", test_dec64_fail); +} + static int __init test_kstrtox_init(void) { test_kstrtoull_ok(); @@ -735,6 +911,12 @@ static int __init test_kstrtox_init(void) test_kstrtou8_fail(); test_kstrtos8_ok(); test_kstrtos8_fail(); + + test_kstrtoudec64_ok(); + test_kstrtoudec64_fail(); + test_kstrtodec64_ok(); + test_kstrtodec64_fail(); + return -EINVAL; } module_init(test_kstrtox_init); From b89c1a68a5db61e63af9fdd41acc2a675187266c Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 4 Jun 2026 10:59:01 +0100 Subject: [PATCH 029/513] lib: math: div64: add div64_s64_rem() Add div64_s64_rem() function, with 32-bit implementation that uses div64_u64_rem() and a branchless approach to resolve the sign of the remainder and quotient (negation in two's complement). Reviewed-by: Andy Shevchenko Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- include/linux/math64.h | 18 ++++++++++++++++++ lib/math/div64.c | 15 +++++++++++++++ lib/math/test_mul_u64_u64_div_u64.c | 1 + 3 files changed, 34 insertions(+) diff --git a/include/linux/math64.h b/include/linux/math64.h index cc305206d89f..99189410d4bb 100644 --- a/include/linux/math64.h +++ b/include/linux/math64.h @@ -57,6 +57,20 @@ static inline u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder) return dividend / divisor; } +/** + * div64_s64_rem - signed 64bit divide with 64bit divisor and remainder + * @dividend: signed 64bit dividend + * @divisor: signed 64bit divisor + * @remainder: pointer to signed 64bit remainder + * + * Return: sets ``*remainder``, then returns dividend / divisor + */ +static inline s64 div64_s64_rem(s64 dividend, s64 divisor, s64 *remainder) +{ + *remainder = dividend % divisor; + return dividend / divisor; +} + /** * div64_u64 - unsigned 64bit divide with 64bit divisor * @dividend: unsigned 64bit dividend @@ -102,6 +116,10 @@ extern s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder); extern u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder); #endif +#ifndef div64_s64_rem +extern s64 div64_s64_rem(s64 dividend, s64 divisor, s64 *remainder); +#endif + #ifndef div64_u64 extern u64 div64_u64(u64 dividend, u64 divisor); #endif diff --git a/lib/math/div64.c b/lib/math/div64.c index d1e92ea24fce..0b10ded09a9b 100644 --- a/lib/math/div64.c +++ b/lib/math/div64.c @@ -158,6 +158,21 @@ u64 div64_u64(u64 dividend, u64 divisor) EXPORT_SYMBOL(div64_u64); #endif +#ifndef div64_s64_rem +s64 div64_s64_rem(s64 dividend, s64 divisor, s64 *remainder) +{ + s64 quot, t, rem; + + quot = div64_u64_rem(abs(dividend), abs(divisor), (u64 *)&rem); + t = dividend >> 63; + *remainder = (rem ^ t) - t; + t = (dividend ^ divisor) >> 63; + + return (quot ^ t) - t; +} +EXPORT_SYMBOL(div64_s64_rem); +#endif + #ifndef div64_s64 s64 div64_s64(s64 dividend, s64 divisor) { diff --git a/lib/math/test_mul_u64_u64_div_u64.c b/lib/math/test_mul_u64_u64_div_u64.c index 338d014f0c73..d12dc05938fb 100644 --- a/lib/math/test_mul_u64_u64_div_u64.c +++ b/lib/math/test_mul_u64_u64_div_u64.c @@ -157,6 +157,7 @@ static void __exit test_exit(void) #define __div64_32 __div64_32 #define div_s64_rem div_s64_rem #define div64_u64_rem div64_u64_rem +#define div64_s64_rem div64_s64_rem #define div64_u64 div64_u64 #define div64_s64 div64_s64 #define iter_div_u64_rem iter_div_u64_rem From 454f60336d59559de5b29c3f5a8ac3a4163ad42a Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 4 Jun 2026 10:59:02 +0100 Subject: [PATCH 030/513] iio: core: add decimal value formatting into 64-bit value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create new format types for iio values (IIO_VAL_DECIMAL64_*), which defines the representation of fixed decimal point values into a single 64-bit number. This new format increases the range of represented values, allowing for integer parts greater than 2^32, as bits are not "wasted" in the fractional part, which can be seen in IIO_VAL_INT_PLUS_MICRO and IIO_VAL_INT_PLUS_NANO. Helpers are created to compose and decompose 64-bit decimals into integer values used in IIO formatting interfaces, which creates consistency and avoid error-prone manual assignments when using wordpart macros. When doing the parsing, kstrtodec64() is used with the scale defined by the specific decimal format type. Signed-off-by: Rodrigo Alencar Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 49 ++++++++++++++++++++++++++------- include/linux/iio/types.h | 20 ++++++++++++++ 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 7e3d3872e2e6..bdf3d4c06331 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,6 @@ #include #include #include -#include #include #include @@ -656,6 +656,7 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, int size, const int *vals) { int tmp0, tmp1; + int l = 0; s64 tmp2; bool scale_db = false; @@ -699,7 +700,6 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, case IIO_VAL_INT_MULTIPLE: { int i; - int l = 0; for (i = 0; i < size; ++i) l += sysfs_emit_at(buf, offset + l, "%d ", vals[i]); @@ -708,8 +708,25 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, case IIO_VAL_CHAR: return sysfs_emit_at(buf, offset, "%c", (char)vals[0]); case IIO_VAL_INT_64: - tmp2 = (s64)((((u64)vals[1]) << 32) | (u32)vals[0]); - return sysfs_emit_at(buf, offset, "%lld", tmp2); + return sysfs_emit_at(buf, offset, "%lld", + iio_val_s64_compose(vals[0], vals[1])); + case IIO_VAL_DECIMAL64_MILLI: + case IIO_VAL_DECIMAL64_MICRO: + case IIO_VAL_DECIMAL64_NANO: + case IIO_VAL_DECIMAL64_PICO: + { + int scale = type - IIO_VAL_DECIMAL64_BASE; + s64 frac; + + tmp2 = div64_s64_rem(iio_val_s64_compose(vals[0], vals[1]), + int_pow(10, scale), &frac); + if (tmp2 == 0 && frac < 0) + l += sysfs_emit_at(buf, offset, "-"); + + l += sysfs_emit_at(buf, offset + l, "%lld.%0*lld", tmp2, scale, + abs(frac)); + return l; + } default: return 0; } @@ -979,6 +996,7 @@ static ssize_t iio_write_channel_info(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); int ret, fract_mult = 100000; + int type, dec_scale = 0; int integer, fract = 0; long long integer64; bool is_char = false; @@ -989,9 +1007,11 @@ static ssize_t iio_write_channel_info(struct device *dev, if (!indio_dev->info->write_raw) return -EINVAL; - if (indio_dev->info->write_raw_get_fmt) - switch (indio_dev->info->write_raw_get_fmt(indio_dev, - this_attr->c, this_attr->address)) { + if (indio_dev->info->write_raw_get_fmt) { + type = indio_dev->info->write_raw_get_fmt(indio_dev, + this_attr->c, + this_attr->address); + switch (type) { case IIO_VAL_INT: fract_mult = 0; break; @@ -1007,12 +1027,19 @@ static ssize_t iio_write_channel_info(struct device *dev, case IIO_VAL_CHAR: is_char = true; break; + case IIO_VAL_DECIMAL64_MILLI: + case IIO_VAL_DECIMAL64_MICRO: + case IIO_VAL_DECIMAL64_NANO: + case IIO_VAL_DECIMAL64_PICO: + dec_scale = type - IIO_VAL_DECIMAL64_BASE; + fallthrough; case IIO_VAL_INT_64: is_64bit = true; break; default: return -EINVAL; } + } if (is_char) { char ch; @@ -1021,12 +1048,14 @@ static ssize_t iio_write_channel_info(struct device *dev, return -EINVAL; integer = ch; } else if (is_64bit) { - ret = kstrtoll(buf, 0, &integer64); + if (dec_scale) + ret = kstrtodec64(buf, dec_scale, &integer64); + else + ret = kstrtoll(buf, 0, &integer64); if (ret) return ret; - fract = upper_32_bits(integer64); - integer = lower_32_bits(integer64); + iio_val_s64_decompose(integer64, &integer, &fract); } else { ret = __iio_str_to_fixpoint(buf, fract_mult, &integer, &fract, scale_db); diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 4e3099defc1d..924ac9dc6893 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -7,6 +7,9 @@ #ifndef _IIO_TYPES_H_ #define _IIO_TYPES_H_ +#include +#include + #include enum iio_event_info { @@ -34,6 +37,23 @@ enum iio_event_info { #define IIO_VAL_FRACTIONAL_LOG2 11 #define IIO_VAL_CHAR 12 +#define IIO_VAL_DECIMAL64_BASE 32 +#define IIO_VAL_DECIMAL64_MILLI (IIO_VAL_DECIMAL64_BASE + 3) +#define IIO_VAL_DECIMAL64_MICRO (IIO_VAL_DECIMAL64_BASE + 6) +#define IIO_VAL_DECIMAL64_NANO (IIO_VAL_DECIMAL64_BASE + 9) +#define IIO_VAL_DECIMAL64_PICO (IIO_VAL_DECIMAL64_BASE + 12) + +static inline s64 iio_val_s64_compose(s32 val0, s32 val1) +{ + return (s64)(((u64)val1 << 32) | (u32)val0); +} + +static inline void iio_val_s64_decompose(s64 dec64, s32 *val0, s32 *val1) +{ + *val0 = lower_32_bits(dec64); + *val1 = upper_32_bits(dec64); +} + enum iio_available_type { IIO_AVAIL_LIST, IIO_AVAIL_RANGE, From c2282e36b332acf04b3a3c4523e460c01c2887cf Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 4 Jun 2026 10:59:03 +0100 Subject: [PATCH 031/513] iio: test: iio-test-format: add test case for decimal format Add iio_test_iio_format_value_decimal_64() kunit test case for decimal value formatting, exploring different scales types. Also, the same iio_val_s64_decompose() helper used to populate local array is used in iio_test_iio_format_value_integer_64(). Signed-off-by: Rodrigo Alencar Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/test/iio-test-format.c | 97 +++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 22 deletions(-) diff --git a/drivers/iio/test/iio-test-format.c b/drivers/iio/test/iio-test-format.c index 872dd8582003..e5d0a2ac4670 100644 --- a/drivers/iio/test/iio-test-format.c +++ b/drivers/iio/test/iio-test-format.c @@ -200,56 +200,108 @@ static void iio_test_iio_format_value_multiple(struct kunit *test) static void iio_test_iio_format_value_integer_64(struct kunit *test) { int values[2]; - s64 value; char *buf; int ret; buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); - value = 24; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(24, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "24\n"); - value = -24; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(-24, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-24\n"); - value = 0; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(0, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0\n"); - value = UINT_MAX; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(UINT_MAX, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "4294967295\n"); - value = -((s64)UINT_MAX); - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(-((s64)UINT_MAX), &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-4294967295\n"); - value = LLONG_MAX; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(LLONG_MAX, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372036854775807\n"); - value = LLONG_MIN; - values[0] = lower_32_bits(value); - values[1] = upper_32_bits(value); + iio_val_s64_decompose(LLONG_MIN, &values[0], &values[1]); ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372036854775808\n"); } +static void iio_test_iio_format_value_decimal_64(struct kunit *test) +{ + int values[2]; + char *buf; + int ret; + + buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + + /* DECIMAL64_MILLI: positive >= 1, value 1.234 */ + iio_val_s64_decompose(1234, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.234\n"); + + /* DECIMAL64_MICRO: positive >= 1, value 3.141592 */ + iio_val_s64_decompose(3141592, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "3.141592\n"); + + /* DECIMAL64_MILLI: positive < 1, value 0.042 */ + iio_val_s64_decompose(42, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.042\n"); + + /* DECIMAL64_MILLI: negative <= -1, value -1.234 */ + iio_val_s64_decompose(-1234, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1.234\n"); + + /* DECIMAL64_MILLI: negative > -1, value -0.123 */ + iio_val_s64_decompose(-123, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.123\n"); + + /* DECIMAL64_MILLI: zero */ + iio_val_s64_decompose(0, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MILLI, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000\n"); + + /* DECIMAL64_NANO: value 1.000000001 */ + iio_val_s64_decompose(1000000001, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_NANO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.000000001\n"); + + /* DECIMAL64_MICRO: large value using upper 32 bits */ + iio_val_s64_decompose(5000000000000042LL, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "5000000000.000042\n"); + + /* limits */ + iio_val_s64_decompose(LLONG_MAX, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_PICO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372.036854775807\n"); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_NANO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372036.854775807\n"); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372036854.775807\n"); + + iio_val_s64_decompose(LLONG_MIN, &values[0], &values[1]); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_PICO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372.036854775808\n"); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_NANO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372036.854775808\n"); + ret = iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372036854.775808\n"); +} + static struct kunit_case iio_format_test_cases[] = { KUNIT_CASE(iio_test_iio_format_value_integer), KUNIT_CASE(iio_test_iio_format_value_fixedpoint), @@ -257,6 +309,7 @@ static struct kunit_case iio_format_test_cases[] = { KUNIT_CASE(iio_test_iio_format_value_fractional_log2), KUNIT_CASE(iio_test_iio_format_value_multiple), KUNIT_CASE(iio_test_iio_format_value_integer_64), + KUNIT_CASE(iio_test_iio_format_value_decimal_64), { } }; From b96ef22c7fb2fd386ee3fe149ed9d0c6b2018afc Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 4 Jun 2026 10:59:04 +0100 Subject: [PATCH 032/513] iio: frequency: adf41513: driver implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver is based on existing PLL drivers in the IIO subsystem and implements the following key features: - Integer-N and fractional-N (fixed/variable modulus) synthesis modes; - High-resolution frequency calculations using microhertz (µHz) precision to handle sub-Hz resolution across multi-GHz frequency ranges; - IIO debugfs interface for direct register access; - FW property parsing from devicetree including charge pump settings and reference path configuration; - Power management support with suspend/resume callbacks; - Lock detect GPIO monitoring. Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + drivers/iio/frequency/Kconfig | 10 + drivers/iio/frequency/Makefile | 1 + drivers/iio/frequency/adf41513.c | 1109 ++++++++++++++++++++++++++++++ 4 files changed, 1121 insertions(+) create mode 100644 drivers/iio/frequency/adf41513.c diff --git a/MAINTAINERS b/MAINTAINERS index 8c5623eb7a2c..f07d42807cf6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1708,6 +1708,7 @@ L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml +F: drivers/iio/frequency/adf41513.c ANALOG DEVICES INC ADF4377 DRIVER M: Antoniu Miclaus diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig index 583cbdf4e8cd..90c6304c4bcd 100644 --- a/drivers/iio/frequency/Kconfig +++ b/drivers/iio/frequency/Kconfig @@ -29,6 +29,16 @@ endmenu menu "Phase-Locked Loop (PLL) frequency synthesizers" +config ADF41513 + tristate "Analog Devices ADF41513 PLL Frequency Synthesizer" + depends on SPI + help + Say yes here to build support for Analog Devices ADF41513 + 26.5 GHz Integer-N/Fractional-N PLL Frequency Synthesizer. + + To compile this driver as a module, choose M here: the + module will be called adf41513. + config ADF4350 tristate "Analog Devices ADF4350/ADF4351 Wideband Synthesizers" depends on SPI diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile index 70d0e0b70e80..53b4d01414d8 100644 --- a/drivers/iio/frequency/Makefile +++ b/drivers/iio/frequency/Makefile @@ -5,6 +5,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AD9523) += ad9523.o +obj-$(CONFIG_ADF41513) += adf41513.o obj-$(CONFIG_ADF4350) += adf4350.o obj-$(CONFIG_ADF4371) += adf4371.o obj-$(CONFIG_ADF4377) += adf4377.o diff --git a/drivers/iio/frequency/adf41513.c b/drivers/iio/frequency/adf41513.c new file mode 100644 index 000000000000..1a084e6b3c65 --- /dev/null +++ b/drivers/iio/frequency/adf41513.c @@ -0,0 +1,1109 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADF41513 SPI PLL Frequency Synthesizer driver + * + * Copyright 2026 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 + +/* Registers */ +#define ADF41513_REG0 0 +#define ADF41513_REG1 1 +#define ADF41513_REG2 2 +#define ADF41513_REG3 3 +#define ADF41513_REG4 4 +#define ADF41513_REG5 5 +#define ADF41513_REG6 6 +#define ADF41513_REG7 7 +#define ADF41513_REG8 8 +#define ADF41513_REG9 9 +#define ADF41513_REG10 10 +#define ADF41513_REG11 11 +#define ADF41513_REG12 12 +#define ADF41513_REG13 13 +#define ADF41513_REG_NUM 14 + +#define ADF41513_SYNC_REG0 BIT(ADF41513_REG0) +#define ADF41513_SYNC_REG1 BIT(ADF41513_REG1) +#define ADF41513_SYNC_REG2 BIT(ADF41513_REG2) +#define ADF41513_SYNC_REG3 BIT(ADF41513_REG3) +#define ADF41513_SYNC_REG4 BIT(ADF41513_REG4) +#define ADF41513_SYNC_REG5 BIT(ADF41513_REG5) +#define ADF41513_SYNC_REG6 BIT(ADF41513_REG6) +#define ADF41513_SYNC_REG7 BIT(ADF41513_REG7) +#define ADF41513_SYNC_REG9 BIT(ADF41513_REG9) +#define ADF41513_SYNC_REG11 BIT(ADF41513_REG11) +#define ADF41513_SYNC_REG12 BIT(ADF41513_REG12) +#define ADF41513_SYNC_REG13 BIT(ADF41513_REG13) +#define ADF41513_SYNC_DIFF 0 +#define ADF41513_SYNC_ALL GENMASK(ADF41513_REG13, ADF41513_REG0) + +/* REG0 Bit Definitions */ +#define ADF41513_REG0_CTRL_BITS_MSK GENMASK(3, 0) +#define ADF41513_REG0_INT_MSK GENMASK(19, 4) +#define ADF41513_REG0_VAR_MOD_MSK BIT(28) + +/* REG1 Bit Definitions */ +#define ADF41513_REG1_FRAC1_MSK GENMASK(28, 4) +#define ADF41513_REG1_DITHER2_MSK BIT(31) + +/* REG2 Bit Definitions */ +#define ADF41513_REG2_PHASE_VAL_MSK GENMASK(15, 4) +#define ADF41513_REG2_PHASE_ADJ_MSK BIT(31) + +/* REG3 Bit Definitions */ +#define ADF41513_REG3_FRAC2_MSK GENMASK(27, 4) + +/* REG4 Bit Definitions */ +#define ADF41513_REG4_MOD2_MSK GENMASK(27, 4) + +/* REG5 Bit Definitions */ +#define ADF41513_REG5_CLK1_DIV_MSK GENMASK(15, 4) +#define ADF41513_REG5_R_CNT_MSK GENMASK(20, 16) +#define ADF41513_REG5_REF_DOUBLER_MSK BIT(21) +#define ADF41513_REG5_RDIV2_MSK BIT(22) +#define ADF41513_REG5_PRESCALER_MSK BIT(23) +#define ADF41513_REG5_LSB_P1_MSK BIT(24) +#define ADF41513_REG5_CP_CURRENT_MSK GENMASK(28, 25) +#define ADF41513_REG5_DLD_MODES_MSK GENMASK(31, 30) + +/* REG6 Bit Definitions */ +#define ADF41513_REG6_COUNTER_RESET_MSK BIT(4) +#define ADF41513_REG6_CP_TRISTATE_MSK BIT(5) +#define ADF41513_REG6_POWER_DOWN_MSK BIT(6) +#define ADF41513_REG6_PD_POLARITY_MSK BIT(7) +#define ADF41513_REG6_LDP_MSK GENMASK(9, 8) +#define ADF41513_REG6_CP_TRISTATE_PD_ON_MSK BIT(16) +#define ADF41513_REG6_SD_RESET_MSK BIT(17) +#define ADF41513_REG6_LOL_ENABLE_MSK BIT(18) +#define ADF41513_REG6_ABP_MSK BIT(19) +#define ADF41513_REG6_INT_MODE_MSK BIT(20) +#define ADF41513_REG6_BLEED_ENABLE_MSK BIT(22) +#define ADF41513_REG6_BLEED_POLARITY_MSK BIT(23) +#define ADF41513_REG6_BLEED_CURRENT_MSK GENMASK(31, 24) + +/* REG7 Bit Definitions */ +#define ADF41513_REG7_CLK2_DIV_MSK GENMASK(17, 6) +#define ADF41513_REG7_CLK_DIV_MODE_MSK GENMASK(19, 18) +#define ADF41513_REG7_PS_BIAS_MSK GENMASK(21, 20) +#define ADF41513_REG7_N_DELAY_MSK GENMASK(23, 22) +#define ADF41513_REG7_LD_CLK_SEL_MSK BIT(26) +#define ADF41513_REG7_LD_COUNT_MSK GENMASK(29, 27) + +/* REG9 Bit Definitions */ +#define ADF41513_REG9_LD_BIAS_MSK GENMASK(31, 30) + +/* REG11 Bit Definitions */ +#define ADF41513_REG11_POWER_DOWN_SEL_MSK BIT(31) + +/* REG12 Bit Definitions */ +#define ADF41513_REG12_READBACK_SEL_MSK GENMASK(19, 14) +#define ADF41513_REG12_LE_SELECT_MSK BIT(20) +#define ADF41513_REG12_MASTER_RESET_MSK BIT(22) +#define ADF41513_REG12_LOGIC_LEVEL_MSK BIT(27) +#define ADF41513_REG12_MUXOUT_MSK GENMASK(31, 28) + +/* MUXOUT Selection */ +#define ADF41513_MUXOUT_TRISTATE 0x0 +#define ADF41513_MUXOUT_DVDD 0x1 +#define ADF41513_MUXOUT_DGND 0x2 +#define ADF41513_MUXOUT_R_DIV 0x3 +#define ADF41513_MUXOUT_N_DIV 0x4 +#define ADF41513_MUXOUT_DIG_LD 0x6 +#define ADF41513_MUXOUT_SDO 0x7 +#define ADF41513_MUXOUT_READBACK 0x8 +#define ADF41513_MUXOUT_CLK1_DIV 0xA +#define ADF41513_MUXOUT_R_DIV2 0xD +#define ADF41513_MUXOUT_N_DIV2 0xE + +/* DLD Mode Selection */ +#define ADF41513_DLD_TRISTATE 0x0 +#define ADF41513_DLD_DIG_LD 0x1 +#define ADF41513_DLD_LOW 0x2 +#define ADF41513_DLD_HIGH 0x3 + +/* Prescaler Selection */ +#define ADF41513_PRESCALER_4_5 0 +#define ADF41513_PRESCALER_8_9 1 +#define ADF41513_PRESCALER_AUTO 2 + +/* Specifications */ +#define ADF41510_MAX_RF_FREQ_HZ (10ULL * HZ_PER_GHZ) +#define ADF41513_MIN_RF_FREQ_HZ (1ULL * HZ_PER_GHZ) +#define ADF41513_MAX_RF_FREQ_HZ (26500ULL * HZ_PER_MHZ) + +#define ADF41513_MIN_REF_FREQ_HZ (10 * HZ_PER_MHZ) +#define ADF41513_MAX_REF_FREQ_HZ (800 * HZ_PER_MHZ) +#define ADF41513_MAX_REF_FREQ_DOUBLER_HZ (225 * HZ_PER_MHZ) + +#define ADF41513_MAX_PFD_FREQ_INT_N_UHZ (250ULL * MEGA * MICROHZ_PER_HZ) +#define ADF41513_MAX_PFD_FREQ_FRAC_N_UHZ (125ULL * MEGA * MICROHZ_PER_HZ) +#define ADF41513_MAX_FREQ_RESOLUTION_UHZ (100ULL * KILO * MICROHZ_PER_HZ) + +#define ADF41513_MIN_INT_4_5 20 +#define ADF41513_MAX_INT_4_5 511 +#define ADF41513_MIN_INT_8_9 64 +#define ADF41513_MAX_INT_8_9 1023 + +#define ADF41513_MIN_INT_FRAC_4_5 23 +#define ADF41513_MIN_INT_FRAC_8_9 75 + +#define ADF41513_MIN_R_CNT 1 +#define ADF41513_MAX_R_CNT 32 + +#define ADF41513_MIN_R_SET 1800 +#define ADF41513_DEFAULT_R_SET 2700 +#define ADF41513_MAX_R_SET 10000 + +#define ADF41513_MIN_CP_VOLTAGE_mV 810 +#define ADF41513_DEFAULT_CP_VOLTAGE_mV 6480 +#define ADF41513_MAX_CP_VOLTAGE_mV 12960 + +#define ADF41513_MIN_CP_CURRENT_uA 81 +#define ADF41513_MAX_CP_CURRENT_uA 7200 + +#define ADF41513_LD_COUNT_FAST_MIN 2 +#define ADF41513_LD_COUNT_FAST_LIMIT 64 +#define ADF41513_LD_COUNT_MIN 64 +#define ADF41513_LD_COUNT_MAX 8192 + +#define ADF41513_FIXED_MODULUS BIT(25) +#define ADF41513_MAX_MOD2 (BIT(24) - 1) +#define ADF41513_MAX_PHASE_VAL (BIT(12) - 1) +#define ADF41513_MAX_CLK_DIVIDER (BIT(12) - 1) + +#define ADF41513_HZ_DECIMAL_SCALE 6 +#define ADF41513_PS_BIAS_INIT 0x2 +#define ADF41513_MAX_PHASE_MICRORAD ((2 * 314159265UL) / 100) + +enum adf41513_pll_mode { + ADF41513_MODE_INVALID, + ADF41513_MODE_INTEGER_N, + ADF41513_MODE_FIXED_MODULUS, + ADF41513_MODE_VARIABLE_MODULUS, +}; + +struct adf41513_chip_info { + const char *name; + u64 max_rf_freq_hz; + bool has_prescaler_8_9; +}; + +struct adf41513_data { + u64 power_up_frequency_hz; + u64 freq_resolution_uhz; + u32 charge_pump_voltage_mv; + u32 lock_detect_count; + + u8 ref_div_factor; + bool ref_doubler_en; + bool ref_div2_en; + bool phase_detector_polarity; + + bool logic_lvl_1v8_en; +}; + +struct adf41513_pll_settings { + enum adf41513_pll_mode mode; + + /* reference path parameters */ + u8 r_counter; + u8 ref_doubler; + u8 ref_div2; + u8 prescaler; + + /* frequency parameters */ + u64 target_frequency_uhz; + u64 actual_frequency_uhz; + u64 pfd_frequency_uhz; + + /* pll parameters */ + u32 frac1; + u32 frac2; + u32 mod2; + u16 int_val; +}; + +struct adf41513_state { + const struct adf41513_chip_info *chip_info; + struct spi_device *spi; + struct gpio_desc *lock_detect; + struct clk *ref_clk; + u32 ref_freq_hz; + + /* + * Lock for accessing device registers. Some operations require + * multiple consecutive R/W operations, during which the device + * shouldn't be interrupted. The buffers are also shared across + * all operations so need to be protected on stand alone reads and + * writes. + */ + struct mutex lock; + + /* Cached register values */ + u32 regs[ADF41513_REG_NUM]; + u32 regs_hw[ADF41513_REG_NUM]; + + struct adf41513_data data; + struct adf41513_pll_settings settings; + + bool powerdown; +}; + +static const char * const adf41513_power_supplies[] = { + "avdd1", "avdd2", "avdd3", "avdd4", "avdd5", "vp", +}; + +static int adf41513_sync_config(struct adf41513_state *st, u16 sync_mask) +{ + __be32 d32; + int ret; + + /* write registers in reverse order (R13 to R0)*/ + for (int i = ADF41513_REG13; i >= ADF41513_REG0; i--) { + if (st->regs_hw[i] == st->regs[i] && !(sync_mask & BIT(i))) + continue; + + d32 = cpu_to_be32(st->regs[i] | i); + ret = spi_write_then_read(st->spi, &d32, sizeof(d32), NULL, 0); + if (ret < 0) + return ret; + st->regs_hw[i] = st->regs[i]; + dev_dbg(&st->spi->dev, "REG%d <= 0x%08X\n", i, st->regs[i] | i); + } + + return 0; +} + +static u64 adf41513_pll_get_rate(struct adf41513_state *st) +{ + struct adf41513_pll_settings *cfg = &st->settings; + + if (cfg->mode != ADF41513_MODE_INVALID) + return cfg->actual_frequency_uhz; + + /* get pll settings from regs_hw */ + cfg->int_val = FIELD_GET(ADF41513_REG0_INT_MSK, st->regs_hw[ADF41513_REG0]); + cfg->frac1 = FIELD_GET(ADF41513_REG1_FRAC1_MSK, st->regs_hw[ADF41513_REG1]); + cfg->frac2 = FIELD_GET(ADF41513_REG3_FRAC2_MSK, st->regs_hw[ADF41513_REG3]); + cfg->mod2 = FIELD_GET(ADF41513_REG4_MOD2_MSK, st->regs_hw[ADF41513_REG4]); + cfg->r_counter = FIELD_GET(ADF41513_REG5_R_CNT_MSK, st->regs_hw[ADF41513_REG5]); + cfg->ref_doubler = FIELD_GET(ADF41513_REG5_REF_DOUBLER_MSK, st->regs_hw[ADF41513_REG5]); + cfg->ref_div2 = FIELD_GET(ADF41513_REG5_RDIV2_MSK, st->regs_hw[ADF41513_REG5]); + cfg->prescaler = FIELD_GET(ADF41513_REG5_PRESCALER_MSK, st->regs_hw[ADF41513_REG5]); + + if (!cfg->mod2) + cfg->mod2 = 1; + if (!cfg->r_counter) + cfg->r_counter = ADF41513_MAX_R_CNT; + + /* calculate pfd frequency */ + cfg->pfd_frequency_uhz = (u64)st->ref_freq_hz * MICRO; + if (cfg->ref_doubler) + cfg->pfd_frequency_uhz <<= 1; + if (cfg->ref_div2) + cfg->pfd_frequency_uhz >>= 1; + cfg->pfd_frequency_uhz = div_u64(cfg->pfd_frequency_uhz, cfg->r_counter); + cfg->actual_frequency_uhz = (u64)cfg->int_val * cfg->pfd_frequency_uhz; + + /* check if int mode is selected */ + if (FIELD_GET(ADF41513_REG6_INT_MODE_MSK, st->regs_hw[ADF41513_REG6])) { + cfg->mode = ADF41513_MODE_INTEGER_N; + } else { + cfg->actual_frequency_uhz += mul_u64_u32_div(cfg->pfd_frequency_uhz, + cfg->frac1, + ADF41513_FIXED_MODULUS); + + /* check if variable modulus is selected */ + if (FIELD_GET(ADF41513_REG0_VAR_MOD_MSK, st->regs_hw[ADF41513_REG0])) { + cfg->actual_frequency_uhz += + mul_u64_u64_div_u64(cfg->frac2, + cfg->pfd_frequency_uhz, + (u64)cfg->mod2 * ADF41513_FIXED_MODULUS); + + cfg->mode = ADF41513_MODE_VARIABLE_MODULUS; + } else { + /* LSB_P1 offset */ + if (!FIELD_GET(ADF41513_REG5_LSB_P1_MSK, st->regs_hw[ADF41513_REG5])) + cfg->actual_frequency_uhz += + div_u64(cfg->pfd_frequency_uhz, + 2 * ADF41513_FIXED_MODULUS); + cfg->mode = ADF41513_MODE_FIXED_MODULUS; + } + } + + cfg->target_frequency_uhz = cfg->actual_frequency_uhz; + + return cfg->actual_frequency_uhz; +} + +static int adf41513_calc_pfd_frequency(struct adf41513_state *st, + struct adf41513_pll_settings *result, + u64 fpfd_limit_uhz) +{ + result->ref_div2 = st->data.ref_div2_en; + result->ref_doubler = st->data.ref_doubler_en; + result->r_counter = st->data.ref_div_factor - 1; + + do { + result->r_counter++; + /* f_PFD = REF_IN × ((1 + D)/(R × (1 + T))) */ + result->pfd_frequency_uhz = (u64)st->ref_freq_hz * MICRO; + if (result->ref_doubler) + result->pfd_frequency_uhz <<= 1; + if (result->ref_div2) + result->pfd_frequency_uhz >>= 1; + result->pfd_frequency_uhz = div_u64(result->pfd_frequency_uhz, + result->r_counter); + } while (result->pfd_frequency_uhz > fpfd_limit_uhz); + + if (result->r_counter > ADF41513_MAX_R_CNT) { + dev_err(&st->spi->dev, "Cannot optimize PFD frequency\n"); + return -ERANGE; + } + + return 0; +} + +static int adf41513_calc_integer_n(struct adf41513_state *st, + struct adf41513_pll_settings *result) +{ + u32 max_int = st->chip_info->has_prescaler_8_9 ? + ADF41513_MAX_INT_8_9 : ADF41513_MAX_INT_4_5; + u64 freq_error_uhz; + u32 int_val = div64_u64_rem(result->target_frequency_uhz, result->pfd_frequency_uhz, + &freq_error_uhz); + + /* check if freq error is within a tolerance of 1/2 resolution */ + if (freq_error_uhz > (result->pfd_frequency_uhz >> 1) && int_val < max_int) { + int_val++; + freq_error_uhz = result->pfd_frequency_uhz - freq_error_uhz; + } + + if (freq_error_uhz > st->data.freq_resolution_uhz) + return -ERANGE; + + /* set prescaler */ + if (st->chip_info->has_prescaler_8_9 && int_val >= ADF41513_MIN_INT_8_9 && + int_val <= ADF41513_MAX_INT_8_9) + result->prescaler = 1; + else if (int_val >= ADF41513_MIN_INT_4_5 && int_val <= ADF41513_MAX_INT_4_5) + result->prescaler = 0; + else + return -ERANGE; + + result->actual_frequency_uhz = (u64)int_val * result->pfd_frequency_uhz; + result->mode = ADF41513_MODE_INTEGER_N; + result->int_val = int_val; + result->frac1 = 0; + result->frac2 = 0; + result->mod2 = 0; + + return 0; +} + +static int adf41513_calc_fixed_mod(struct adf41513_state *st, + struct adf41513_pll_settings *result) +{ + u64 resolution_uhz = div_u64(result->pfd_frequency_uhz, ADF41513_FIXED_MODULUS); + u64 target_frequency_uhz = result->target_frequency_uhz; + u64 freq_error_uhz; + u32 int_val, frac1; + bool lsb_p1_offset = !FIELD_GET(ADF41513_REG5_LSB_P1_MSK, st->regs[ADF41513_REG5]); + + /* LSB_P1 adds a frequency offset of f_pfd/2^26 */ + if (lsb_p1_offset) + target_frequency_uhz -= resolution_uhz >> 1; + + int_val = div64_u64_rem(target_frequency_uhz, result->pfd_frequency_uhz, + &freq_error_uhz); + + if (st->chip_info->has_prescaler_8_9 && int_val >= ADF41513_MIN_INT_FRAC_8_9 && + int_val <= ADF41513_MAX_INT_8_9) + result->prescaler = 1; + else if (int_val >= ADF41513_MIN_INT_FRAC_4_5 && int_val <= ADF41513_MAX_INT_4_5) + result->prescaler = 0; + else + return -ERANGE; + + /* compute frac1 and fixed modulus error */ + frac1 = mul_u64_u64_div_u64(freq_error_uhz, ADF41513_FIXED_MODULUS, + result->pfd_frequency_uhz); + freq_error_uhz -= mul_u64_u32_div(result->pfd_frequency_uhz, frac1, + ADF41513_FIXED_MODULUS); + + /* check if freq error is within a tolerance of 1/2 resolution */ + if (freq_error_uhz > (resolution_uhz >> 1) && frac1 < (ADF41513_FIXED_MODULUS - 1)) { + frac1++; + freq_error_uhz = freq_error_uhz < resolution_uhz ? + resolution_uhz - freq_error_uhz : 0; + } + + if (freq_error_uhz > st->data.freq_resolution_uhz) + return -ERANGE; + + /* integer part */ + result->actual_frequency_uhz = (u64)int_val * result->pfd_frequency_uhz; + /* fractional part */ + if (lsb_p1_offset) + result->actual_frequency_uhz += (resolution_uhz >> 1); + result->actual_frequency_uhz += mul_u64_u32_div(result->pfd_frequency_uhz, frac1, + ADF41513_FIXED_MODULUS); + result->mode = ADF41513_MODE_FIXED_MODULUS; + result->int_val = int_val; + result->frac1 = frac1; + result->frac2 = 0; + result->mod2 = 0; + + return 0; +} + +static int adf41513_calc_variable_mod(struct adf41513_state *st, + struct adf41513_pll_settings *result) +{ + u64 freq_error_uhz, mod2; + u32 frac1, frac2; + u32 int_val = div64_u64_rem(result->target_frequency_uhz, + result->pfd_frequency_uhz, &freq_error_uhz); + + if (st->chip_info->has_prescaler_8_9 && int_val >= ADF41513_MIN_INT_FRAC_8_9 && + int_val <= ADF41513_MAX_INT_8_9) + result->prescaler = 1; + else if (int_val >= ADF41513_MIN_INT_FRAC_4_5 && int_val <= ADF41513_MAX_INT_4_5) + result->prescaler = 0; + else + return -ERANGE; + + /* calculate required mod2 based on target resolution / 2 */ + mod2 = DIV64_U64_ROUND_CLOSEST(result->pfd_frequency_uhz << 1, + st->data.freq_resolution_uhz * ADF41513_FIXED_MODULUS); + /* ensure mod2 is at least 2 for meaningful operation */ + mod2 = clamp(mod2, 2, ADF41513_MAX_MOD2); + + /* calculate frac1 and frac2 */ + frac1 = mul_u64_u64_div_u64(freq_error_uhz, ADF41513_FIXED_MODULUS, + result->pfd_frequency_uhz); + frac2 = mul_u64_u64_div_u64(freq_error_uhz, mod2 * ADF41513_FIXED_MODULUS, + result->pfd_frequency_uhz) - mod2 * frac1; + + /* integer part */ + result->actual_frequency_uhz = (u64)int_val * result->pfd_frequency_uhz; + /* fractional part */ + result->actual_frequency_uhz += mul_u64_u64_div_u64(mod2 * frac1 + frac2, + result->pfd_frequency_uhz, + mod2 * ADF41513_FIXED_MODULUS); + result->mode = ADF41513_MODE_VARIABLE_MODULUS; + result->int_val = int_val; + result->frac1 = frac1; + result->frac2 = frac2; + result->mod2 = mod2; + + return 0; +} + +static int adf41513_calc_pll_settings(struct adf41513_state *st, + struct adf41513_pll_settings *result, + u64 rf_out_uhz) +{ + u64 max_rf_freq_uhz = st->chip_info->max_rf_freq_hz * MICRO; + u64 min_rf_freq_uhz = ADF41513_MIN_RF_FREQ_HZ * MICRO; + u64 pfd_freq_limit_uhz; + int ret; + + if (rf_out_uhz < min_rf_freq_uhz || rf_out_uhz > max_rf_freq_uhz) { + dev_err(&st->spi->dev, "RF frequency %llu uHz out of range [%llu, %llu] uHz\n", + rf_out_uhz, min_rf_freq_uhz, max_rf_freq_uhz); + return -EINVAL; + } + + result->target_frequency_uhz = rf_out_uhz; + + /* try integer-N first (best phase noise performance) */ + pfd_freq_limit_uhz = min(div_u64(rf_out_uhz, ADF41513_MIN_INT_4_5), + ADF41513_MAX_PFD_FREQ_INT_N_UHZ); + ret = adf41513_calc_pfd_frequency(st, result, pfd_freq_limit_uhz); + if (ret) + return ret; + + if (adf41513_calc_integer_n(st, result) == 0) + return 0; + + /* try fractional-N: recompute pfd frequency if necessary */ + pfd_freq_limit_uhz = min(div_u64(rf_out_uhz, ADF41513_MIN_INT_FRAC_4_5), + ADF41513_MAX_PFD_FREQ_FRAC_N_UHZ); + if (pfd_freq_limit_uhz < result->pfd_frequency_uhz) { + ret = adf41513_calc_pfd_frequency(st, result, pfd_freq_limit_uhz); + if (ret) + return ret; + } + + /* fixed-modulus attempt */ + if (adf41513_calc_fixed_mod(st, result) == 0) + return 0; + + /* variable-modulus attempt */ + ret = adf41513_calc_variable_mod(st, result); + if (ret) { + dev_err(&st->spi->dev, + "no valid PLL configuration found for %llu uHz\n", + rf_out_uhz); + return ret; + } + + return 0; +} + +static int adf41513_set_frequency(struct adf41513_state *st, u64 freq_uhz, u16 sync_mask) +{ + struct adf41513_pll_settings result; + int ret; + + ret = adf41513_calc_pll_settings(st, &result, freq_uhz); + if (ret < 0) + return ret; + + /* apply computed results to pll settings */ + st->settings = result; + + dev_dbg(&st->spi->dev, + "%s mode: int=%u, frac1=%u, frac2=%u, mod2=%u, fpdf=%llu Hz, prescaler=%s\n", + (result.mode == ADF41513_MODE_INTEGER_N) ? "integer-n" : + (result.mode == ADF41513_MODE_FIXED_MODULUS) ? "fixed-modulus" : "variable-modulus", + result.int_val, result.frac1, result.frac2, result.mod2, + div64_u64(result.pfd_frequency_uhz, MICRO), + result.prescaler ? "8/9" : "4/5"); + + st->regs[ADF41513_REG0] = FIELD_PREP(ADF41513_REG0_INT_MSK, + st->settings.int_val); + if (st->settings.mode == ADF41513_MODE_VARIABLE_MODULUS) + st->regs[ADF41513_REG0] |= ADF41513_REG0_VAR_MOD_MSK; + + st->regs[ADF41513_REG1] = FIELD_PREP(ADF41513_REG1_FRAC1_MSK, + st->settings.frac1); + if (st->settings.mode != ADF41513_MODE_INTEGER_N) + st->regs[ADF41513_REG1] |= ADF41513_REG1_DITHER2_MSK; + + st->regs[ADF41513_REG3] = FIELD_PREP(ADF41513_REG3_FRAC2_MSK, + st->settings.frac2); + FIELD_MODIFY(ADF41513_REG4_MOD2_MSK, &st->regs[ADF41513_REG4], + st->settings.mod2); + FIELD_MODIFY(ADF41513_REG5_R_CNT_MSK, &st->regs[ADF41513_REG5], + st->settings.r_counter % ADF41513_MAX_R_CNT); + FIELD_MODIFY(ADF41513_REG5_REF_DOUBLER_MSK, &st->regs[ADF41513_REG5], + st->settings.ref_doubler); + FIELD_MODIFY(ADF41513_REG5_RDIV2_MSK, &st->regs[ADF41513_REG5], + st->settings.ref_div2); + FIELD_MODIFY(ADF41513_REG5_PRESCALER_MSK, &st->regs[ADF41513_REG5], + st->settings.prescaler); + + if (st->settings.mode == ADF41513_MODE_INTEGER_N) { + st->regs[ADF41513_REG6] |= ADF41513_REG6_INT_MODE_MSK; + st->regs[ADF41513_REG6] &= ~ADF41513_REG6_BLEED_ENABLE_MSK; + } else { + st->regs[ADF41513_REG6] &= ~ADF41513_REG6_INT_MODE_MSK; + st->regs[ADF41513_REG6] |= ADF41513_REG6_BLEED_ENABLE_MSK; + } + + return adf41513_sync_config(st, sync_mask | ADF41513_SYNC_REG0); +} + +static int adf41513_suspend(struct adf41513_state *st) +{ + st->regs[ADF41513_REG6] |= FIELD_PREP(ADF41513_REG6_POWER_DOWN_MSK, 1); + return adf41513_sync_config(st, ADF41513_SYNC_DIFF); +} + +static int adf41513_resume(struct adf41513_state *st) +{ + st->regs[ADF41513_REG6] &= ~ADF41513_REG6_POWER_DOWN_MSK; + return adf41513_sync_config(st, ADF41513_SYNC_ALL); +} + +static ssize_t adf41513_read_resolution(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct adf41513_state *st = iio_priv(indio_dev); + int vals[2]; + + guard(mutex)(&st->lock); + + iio_val_s64_decompose(st->data.freq_resolution_uhz, &vals[0], &vals[1]); + return iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(vals), vals); +} + +static ssize_t adf41513_read_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct adf41513_state *st = iio_priv(indio_dev); + u32 val; + + guard(mutex)(&st->lock); + + val = FIELD_GET(ADF41513_REG6_POWER_DOWN_MSK, st->regs_hw[ADF41513_REG6]); + return sysfs_emit(buf, "%u\n", val); +} + +static ssize_t adf41513_write_resolution(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct adf41513_state *st = iio_priv(indio_dev); + u64 freq_uhz; + int ret; + + ret = kstrtoudec64(buf, ADF41513_HZ_DECIMAL_SCALE, &freq_uhz); + if (ret) + return ret; + + if (freq_uhz == 0 || freq_uhz > ADF41513_MAX_FREQ_RESOLUTION_UHZ) + return -EINVAL; + + guard(mutex)(&st->lock); + + st->data.freq_resolution_uhz = freq_uhz; + return len; +} + +static ssize_t adf41513_write_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct adf41513_state *st = iio_priv(indio_dev); + bool val; + int ret; + + ret = kstrtobool(buf, &val); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + if (val) + ret = adf41513_suspend(st); + else + ret = adf41513_resume(st); + if (ret) + return ret; + + st->powerdown = val; + return len; +} + +static const struct iio_chan_spec_ext_info adf41513_ext_info[] = { + { + .name = "frequency_resolution", + .read = adf41513_read_resolution, + .write = adf41513_write_resolution, + .shared = IIO_SEPARATE, + }, + { + .name = "powerdown", + .read = adf41513_read_powerdown, + .write = adf41513_write_powerdown, + .shared = IIO_SEPARATE, + }, + { } +}; + +static const struct iio_chan_spec adf41513_chan = { + .type = IIO_ALTVOLTAGE, + .indexed = 1, + .output = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_FREQUENCY) | + BIT(IIO_CHAN_INFO_PHASE), + .ext_info = adf41513_ext_info, +}; + +static int adf41513_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct adf41513_state *st = iio_priv(indio_dev); + u64 tmp64; + + guard(mutex)(&st->lock); + + switch (info) { + case IIO_CHAN_INFO_FREQUENCY: + if (st->lock_detect && + !gpiod_get_value_cansleep(st->lock_detect)) { + dev_dbg(&st->spi->dev, "PLL un-locked\n"); + return -EBUSY; + } + tmp64 = adf41513_pll_get_rate(st); + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_DECIMAL64_MICRO; + case IIO_CHAN_INFO_PHASE: + tmp64 = FIELD_GET(ADF41513_REG2_PHASE_VAL_MSK, + st->regs_hw[ADF41513_REG2]); + tmp64 = (tmp64 * ADF41513_MAX_PHASE_MICRORAD) >> 12; + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_DECIMAL64_MICRO; + default: + return -EINVAL; + } +} + +static int adf41513_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct adf41513_state *st = iio_priv(indio_dev); + u64 tmp64 = iio_val_s64_compose(val, val2); + u16 phase_val; + int ret; + + guard(mutex)(&st->lock); + + switch (info) { + case IIO_CHAN_INFO_FREQUENCY: + return adf41513_set_frequency(st, tmp64, ADF41513_SYNC_DIFF); + case IIO_CHAN_INFO_PHASE: + if (tmp64 >= ADF41513_MAX_PHASE_MICRORAD) + return -EINVAL; + + phase_val = DIV_U64_ROUND_CLOSEST(tmp64 << 12, + ADF41513_MAX_PHASE_MICRORAD); + phase_val = min(phase_val, ADF41513_MAX_PHASE_VAL); + st->regs[ADF41513_REG2] |= ADF41513_REG2_PHASE_ADJ_MSK; + FIELD_MODIFY(ADF41513_REG2_PHASE_VAL_MSK, + &st->regs[ADF41513_REG2], phase_val); + ret = adf41513_sync_config(st, ADF41513_SYNC_REG0); + /* clear phase adjust for the next sync */ + st->regs[ADF41513_REG2] &= ~ADF41513_REG2_PHASE_ADJ_MSK; + return ret; + default: + return -EINVAL; + } +} + +static int adf41513_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_FREQUENCY: + case IIO_CHAN_INFO_PHASE: + return IIO_VAL_DECIMAL64_MICRO; + default: + return -EINVAL; + } +} + +static int adf41513_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct adf41513_state *st = iio_priv(indio_dev); + + if (reg > ADF41513_REG13) + return -EINVAL; + + guard(mutex)(&st->lock); + + if (!readval) { + if (reg <= ADF41513_REG6) + st->settings.mode = ADF41513_MODE_INVALID; + st->regs[reg] = writeval & ~0xF; /* Clear control bits */ + return adf41513_sync_config(st, BIT(reg)); + } + + *readval = st->regs_hw[reg]; + return 0; +} + +static const struct iio_info adf41513_info = { + .read_raw = adf41513_read_raw, + .write_raw = adf41513_write_raw, + .write_raw_get_fmt = adf41513_write_raw_get_fmt, + .debugfs_reg_access = &adf41513_reg_access, +}; + +static int adf41513_parse_fw(struct adf41513_state *st) +{ + struct device *dev = &st->spi->dev; + u32 tmp, cp_resistance, cp_current; + int ret; + + tmp = ADF41510_MAX_RF_FREQ_HZ / MEGA; + device_property_read_u32(dev, "adi,power-up-frequency-mhz", &tmp); + st->data.power_up_frequency_hz = (u64)tmp * MEGA; + if (st->data.power_up_frequency_hz < ADF41513_MIN_RF_FREQ_HZ || + st->data.power_up_frequency_hz > st->chip_info->max_rf_freq_hz) + return dev_err_probe(dev, -ERANGE, + "power-up frequency %llu Hz out of range\n", + st->data.power_up_frequency_hz); + + tmp = ADF41513_MIN_R_CNT; + device_property_read_u32(dev, "adi,reference-div-factor", &tmp); + if (tmp < ADF41513_MIN_R_CNT || tmp > ADF41513_MAX_R_CNT) + return dev_err_probe(dev, -ERANGE, + "invalid reference div factor %u\n", tmp); + st->data.ref_div_factor = tmp; + + st->data.ref_div2_en = device_property_read_bool(dev, "adi,reference-div2-enable"); + st->data.ref_doubler_en = device_property_read_bool(dev, "adi,reference-doubler-enable"); + + if (st->data.ref_doubler_en && + st->ref_freq_hz > ADF41513_MAX_REF_FREQ_DOUBLER_HZ) { + return dev_err_probe(dev, -ERANGE, + "Ref frequency not supported with doubler enabled\n"); + } + + cp_resistance = ADF41513_DEFAULT_R_SET; + device_property_read_u32(dev, "adi,charge-pump-resistor-ohms", &cp_resistance); + if (cp_resistance < ADF41513_MIN_R_SET || cp_resistance > ADF41513_MAX_R_SET) + return dev_err_probe(dev, -ERANGE, "R_SET %u Ohms out of range\n", cp_resistance); + + st->data.charge_pump_voltage_mv = ADF41513_DEFAULT_CP_VOLTAGE_mV; + ret = device_property_read_u32(dev, "adi,charge-pump-current-microamp", &cp_current); + if (!ret) { + if (cp_current < ADF41513_MIN_CP_CURRENT_uA || + cp_current > ADF41513_MAX_CP_CURRENT_uA) + return dev_err_probe(dev, -ERANGE, + "I_CP %u uA out of range\n", cp_current); + + tmp = DIV_ROUND_CLOSEST(cp_current * cp_resistance, MILLI); + if (tmp < ADF41513_MIN_CP_VOLTAGE_mV || tmp > ADF41513_MAX_CP_VOLTAGE_mV) + return dev_err_probe(dev, -ERANGE, "I_CP %u uA (%u Ohms) out of range\n", + cp_current, cp_resistance); + st->data.charge_pump_voltage_mv = tmp; + } + + st->data.phase_detector_polarity = + device_property_read_bool(dev, "adi,phase-detector-polarity-positive-enable"); + + st->data.logic_lvl_1v8_en = device_property_read_bool(dev, "adi,logic-level-1v8-enable"); + + tmp = ADF41513_LD_COUNT_MIN; + device_property_read_u32(dev, "adi,lock-detector-count", &tmp); + if (tmp < ADF41513_LD_COUNT_FAST_MIN || tmp > ADF41513_LD_COUNT_MAX || + !is_power_of_2(tmp)) + return dev_err_probe(dev, -ERANGE, + "invalid lock detect count: %u\n", tmp); + st->data.lock_detect_count = tmp; + + st->data.freq_resolution_uhz = MICROHZ_PER_HZ; + + return 0; +} + +static void adf41513_chip_disable(void *data) +{ + gpiod_set_value_cansleep(data, 0); +} + +static void adf41513_close(void *data) +{ + adf41513_suspend(data); +} + +static int adf41513_setup(struct device *dev, struct adf41513_state *st) +{ + u32 tmp; + int ret; + + memset(st->regs_hw, 0xFF, sizeof(st->regs_hw)); + + /* assuming DLD pin is used for lock detection */ + st->regs[ADF41513_REG5] = FIELD_PREP(ADF41513_REG5_DLD_MODES_MSK, + ADF41513_DLD_DIG_LD); + + tmp = DIV_ROUND_CLOSEST(st->data.charge_pump_voltage_mv, ADF41513_MIN_CP_VOLTAGE_mV); + st->regs[ADF41513_REG5] |= FIELD_PREP(ADF41513_REG5_CP_CURRENT_MSK, tmp - 1); + + st->regs[ADF41513_REG6] = ADF41513_REG6_ABP_MSK | + ADF41513_REG6_LOL_ENABLE_MSK | + ADF41513_REG6_SD_RESET_MSK; + if (st->data.phase_detector_polarity) + st->regs[ADF41513_REG6] |= ADF41513_REG6_PD_POLARITY_MSK; + + st->regs[ADF41513_REG7] = FIELD_PREP(ADF41513_REG7_PS_BIAS_MSK, + ADF41513_PS_BIAS_INIT); + tmp = ilog2(st->data.lock_detect_count); + if (st->data.lock_detect_count < ADF41513_LD_COUNT_FAST_LIMIT) { + tmp -= const_ilog2(ADF41513_LD_COUNT_FAST_MIN); + st->regs[ADF41513_REG7] |= ADF41513_REG7_LD_CLK_SEL_MSK; + } else { + tmp -= const_ilog2(ADF41513_LD_COUNT_MIN); + } + st->regs[ADF41513_REG7] |= FIELD_PREP(ADF41513_REG7_LD_COUNT_MSK, tmp); + + st->regs[ADF41513_REG11] = ADF41513_REG11_POWER_DOWN_SEL_MSK; + st->regs[ADF41513_REG12] = FIELD_PREP(ADF41513_REG12_LOGIC_LEVEL_MSK, + st->data.logic_lvl_1v8_en ? 0 : 1); + + /* perform initialization sequence with power-up frequency */ + ret = adf41513_set_frequency(st, st->data.power_up_frequency_hz * MICRO, + ADF41513_SYNC_ALL); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, adf41513_close, st); +} + +static int adf41513_pm_suspend(struct device *dev) +{ + struct adf41513_state *st = dev_get_drvdata(dev); + + guard(mutex)(&st->lock); + return adf41513_suspend(st); +} + +static int adf41513_pm_resume(struct device *dev) +{ + struct adf41513_state *st = dev_get_drvdata(dev); + + guard(mutex)(&st->lock); + if (st->powerdown) + return 0; /* nothing to do */ + + return adf41513_resume(st); +} + +static const struct adf41513_chip_info adf41510_chip_info = { + .name = "adf41510", + .max_rf_freq_hz = ADF41510_MAX_RF_FREQ_HZ, + .has_prescaler_8_9 = false, +}; + +static const struct adf41513_chip_info adf41513_chip_info = { + .name = "adf41513", + .max_rf_freq_hz = ADF41513_MAX_RF_FREQ_HZ, + .has_prescaler_8_9 = true, +}; + +static int adf41513_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct gpio_desc *chip_enable; + struct iio_dev *indio_dev; + struct adf41513_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -EINVAL; + + spi_set_drvdata(spi, st); + + st->ref_clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(st->ref_clk)) + return PTR_ERR(st->ref_clk); + + st->ref_freq_hz = clk_get_rate(st->ref_clk); + if (st->ref_freq_hz < ADF41513_MIN_REF_FREQ_HZ || + st->ref_freq_hz > ADF41513_MAX_REF_FREQ_HZ) + return dev_err_probe(dev, -ERANGE, + "reference frequency %u Hz out of range\n", + st->ref_freq_hz); + + ret = adf41513_parse_fw(st); + if (ret) + return ret; + + ret = devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(adf41513_power_supplies), + adf41513_power_supplies); + if (ret) + return dev_err_probe(dev, ret, + "failed to get and enable regulators\n"); + + st->lock_detect = devm_gpiod_get_optional(dev, "lock-detect", GPIOD_IN); + if (IS_ERR(st->lock_detect)) + return dev_err_probe(dev, PTR_ERR(st->lock_detect), + "fail to request lock detect GPIO\n"); + + chip_enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(chip_enable)) + return dev_err_probe(dev, PTR_ERR(chip_enable), + "fail to request chip enable GPIO\n"); + + ret = devm_add_action_or_reset(dev, adf41513_chip_disable, chip_enable); + if (ret) + return dev_err_probe(dev, ret, "Failed to add disable action\n"); + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + indio_dev->name = st->chip_info->name; + indio_dev->info = &adf41513_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &adf41513_chan; + indio_dev->num_channels = 1; + + ret = adf41513_setup(dev, st); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to setup device\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id adf41513_id[] = { + { .name = "adf41510", .driver_data = (kernel_ulong_t)&adf41510_chip_info }, + { .name = "adf41513", .driver_data = (kernel_ulong_t)&adf41513_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, adf41513_id); + +static const struct of_device_id adf41513_of_match[] = { + { .compatible = "adi,adf41510", .data = &adf41510_chip_info }, + { .compatible = "adi,adf41513", .data = &adf41513_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, adf41513_of_match); + +static DEFINE_SIMPLE_DEV_PM_OPS(adf41513_pm_ops, adf41513_pm_suspend, adf41513_pm_resume); + +static struct spi_driver adf41513_driver = { + .driver = { + .name = "adf41513", + .pm = pm_ptr(&adf41513_pm_ops), + .of_match_table = adf41513_of_match, + }, + .probe = adf41513_probe, + .id_table = adf41513_id, +}; +module_spi_driver(adf41513_driver); + +MODULE_AUTHOR("Rodrigo Alencar "); +MODULE_DESCRIPTION("Analog Devices ADF41513 PLL Frequency Synthesizer"); +MODULE_LICENSE("GPL"); From 2d07e0a292a271976237bdeb36da4658b31f3b13 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 4 Jun 2026 10:59:05 +0100 Subject: [PATCH 033/513] iio: frequency: adf41513: handle LE synchronization feature When LE sync is enabled, it must be set after powering up and it must be disabled when powering down. It is recommended when using the PLL as a frequency synthesizer, where reference signal will always be present while the device is being configured. Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adf41513.c | 33 ++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/iio/frequency/adf41513.c b/drivers/iio/frequency/adf41513.c index 1a084e6b3c65..cc27edfbda9a 100644 --- a/drivers/iio/frequency/adf41513.c +++ b/drivers/iio/frequency/adf41513.c @@ -226,6 +226,7 @@ struct adf41513_data { bool phase_detector_polarity; bool logic_lvl_1v8_en; + bool le_sync_en; }; struct adf41513_pll_settings { @@ -635,13 +636,28 @@ static int adf41513_set_frequency(struct adf41513_state *st, u64 freq_uhz, u16 s static int adf41513_suspend(struct adf41513_state *st) { st->regs[ADF41513_REG6] |= FIELD_PREP(ADF41513_REG6_POWER_DOWN_MSK, 1); + st->regs[ADF41513_REG12] &= ~ADF41513_REG12_LE_SELECT_MSK; return adf41513_sync_config(st, ADF41513_SYNC_DIFF); } static int adf41513_resume(struct adf41513_state *st) { + int ret; + st->regs[ADF41513_REG6] &= ~ADF41513_REG6_POWER_DOWN_MSK; - return adf41513_sync_config(st, ADF41513_SYNC_ALL); + st->regs[ADF41513_REG12] &= ~ADF41513_REG12_LE_SELECT_MSK; + ret = adf41513_sync_config(st, ADF41513_SYNC_ALL); + if (ret) + return ret; + + if (st->data.le_sync_en) { + st->regs[ADF41513_REG12] |= ADF41513_REG12_LE_SELECT_MSK; + ret = adf41513_sync_config(st, ADF41513_SYNC_DIFF); + if (ret) + return ret; + } + + return 0; } static ssize_t adf41513_read_resolution(struct iio_dev *indio_dev, @@ -914,6 +930,8 @@ static int adf41513_parse_fw(struct adf41513_state *st) "invalid lock detect count: %u\n", tmp); st->data.lock_detect_count = tmp; + /* load enable sync */ + st->data.le_sync_en = device_property_read_bool(dev, "adi,le-sync-enable"); st->data.freq_resolution_uhz = MICROHZ_PER_HZ; return 0; @@ -970,7 +988,18 @@ static int adf41513_setup(struct device *dev, struct adf41513_state *st) if (ret) return ret; - return devm_add_action_or_reset(dev, adf41513_close, st); + ret = devm_add_action_or_reset(dev, adf41513_close, st); + if (ret) + return ret; + + if (st->data.le_sync_en) { + st->regs[ADF41513_REG12] |= ADF41513_REG12_LE_SELECT_MSK; + ret = adf41513_sync_config(st, ADF41513_SYNC_DIFF); + if (ret) + return ret; + } + + return 0; } static int adf41513_pm_suspend(struct device *dev) From 5809020d869f6615a5a1dd05bbe8bc0c17546820 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 4 Jun 2026 10:59:06 +0100 Subject: [PATCH 034/513] iio: frequency: adf41513: features on frequency change Set Bleed current when PFD frequency changes (bleed enabled when in fractional mode). Set lock detector window size, handling bias and precision. Add phase resync support, setting clock dividers when PFD frequency changes. Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adf41513.c | 108 +++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/drivers/iio/frequency/adf41513.c b/drivers/iio/frequency/adf41513.c index cc27edfbda9a..c497027012ba 100644 --- a/drivers/iio/frequency/adf41513.c +++ b/drivers/iio/frequency/adf41513.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -152,6 +153,10 @@ #define ADF41513_PRESCALER_8_9 1 #define ADF41513_PRESCALER_AUTO 2 +/* CLK Divider mode */ +#define ADF41513_CLK_DIV_MODE_OFF 0 +#define ADF41513_CLK_DIV_MODE_PHASE_RESYNC 2 + /* Specifications */ #define ADF41510_MAX_RF_FREQ_HZ (10ULL * HZ_PER_GHZ) #define ADF41513_MIN_RF_FREQ_HZ (1ULL * HZ_PER_GHZ) @@ -217,6 +222,7 @@ struct adf41513_chip_info { struct adf41513_data { u64 power_up_frequency_hz; u64 freq_resolution_uhz; + u32 phase_resync_period_ns; u32 charge_pump_voltage_mv; u32 lock_detect_count; @@ -276,6 +282,16 @@ struct adf41513_state { bool powerdown; }; +static const u16 adf41513_ld_window_x10_ns[] = { + 9, 12, 16, 17, 21, 28, 29, 35, /* 0 - 7 */ + 43, 47, 49, 52, 70, 79, 115, /* 8 - 14 */ +}; + +static const u8 adf41513_ldp_bias[] = { + 0xC, 0xD, 0xE, 0x8, 0x9, 0x4, 0xA, 0x5, /* 0 - 7 */ + 0x0, 0x6, 0xB, 0x1, 0x2, 0x7, 0x3, /* 8 - 14 */ +}; + static const char * const adf41513_power_supplies[] = { "avdd1", "avdd2", "avdd3", "avdd4", "avdd5", "vp", }; @@ -579,9 +595,86 @@ static int adf41513_calc_pll_settings(struct adf41513_state *st, return 0; } +static void adf41513_set_bleed_val(struct adf41513_state *st) +{ + u32 bleed_value, cp_index; + + if (!(st->regs[ADF41513_REG6] & ADF41513_REG6_BLEED_ENABLE_MSK)) + return; + + if (st->data.phase_detector_polarity) + bleed_value = 90; + else + bleed_value = 144; + + cp_index = 1 + FIELD_GET(ADF41513_REG5_CP_CURRENT_MSK, + st->regs[ADF41513_REG5]); + bleed_value = div64_u64(st->settings.pfd_frequency_uhz * cp_index * bleed_value, + 1600ULL * MEGA * MICROHZ_PER_HZ); + + FIELD_MODIFY(ADF41513_REG6_BLEED_CURRENT_MSK, &st->regs[ADF41513_REG6], + bleed_value); +} + +static void adf41513_set_ld_window(struct adf41513_state *st) +{ + /* + * The ideal lock detector window size is halfway between the max + * window, set by the phase comparison period t_PFD = (1 / f_PFD), + * and the minimum is set by (I_BLEED/I_CP) × t_PFD + */ + u16 ld_window_10x_ns = div64_u64(10ULL * NSEC_PER_SEC * MICROHZ_PER_HZ, + st->settings.pfd_frequency_uhz << 1); + u8 ld_idx, ldp, ld_bias; + + if (st->settings.mode != ADF41513_MODE_INTEGER_N) { + /* account for bleed current (deduced from eq.6 and eq.7) */ + if (st->data.phase_detector_polarity) + ld_window_10x_ns += 4; + else + ld_window_10x_ns += 6; + } + + ld_idx = find_closest(ld_window_10x_ns, adf41513_ld_window_x10_ns, + ARRAY_SIZE(adf41513_ld_window_x10_ns)); + ldp = (adf41513_ldp_bias[ld_idx] >> 2) & 0x3; + ld_bias = adf41513_ldp_bias[ld_idx] & 0x3; + + FIELD_MODIFY(ADF41513_REG6_LDP_MSK, &st->regs[ADF41513_REG6], ldp); + FIELD_MODIFY(ADF41513_REG9_LD_BIAS_MSK, &st->regs[ADF41513_REG9], ld_bias); +} + +static void adf41513_set_phase_resync(struct adf41513_state *st) +{ + u32 total_div, clk1_div, clk2_div; + + if (!st->data.phase_resync_period_ns) + return; + + /* assuming both clock dividers hold similar values */ + total_div = mul_u64_u64_div_u64(st->settings.pfd_frequency_uhz, + st->data.phase_resync_period_ns, + 1ULL * MICROHZ_PER_HZ * NSEC_PER_SEC); + clk1_div = clamp(int_sqrt(total_div), 1, + ADF41513_MAX_CLK_DIVIDER); + clk2_div = clamp(DIV_ROUND_CLOSEST(total_div, clk1_div), 1, + ADF41513_MAX_CLK_DIVIDER); + + FIELD_MODIFY(ADF41513_REG5_CLK1_DIV_MSK, &st->regs[ADF41513_REG5], + clk1_div); + FIELD_MODIFY(ADF41513_REG7_CLK2_DIV_MSK, &st->regs[ADF41513_REG7], + clk2_div); + + /* enable phase resync */ + FIELD_MODIFY(ADF41513_REG7_CLK_DIV_MODE_MSK, &st->regs[ADF41513_REG7], + ADF41513_CLK_DIV_MODE_PHASE_RESYNC); +} + static int adf41513_set_frequency(struct adf41513_state *st, u64 freq_uhz, u16 sync_mask) { struct adf41513_pll_settings result; + bool pfd_change = false; + bool mode_change = false; int ret; ret = adf41513_calc_pll_settings(st, &result, freq_uhz); @@ -589,6 +682,8 @@ static int adf41513_set_frequency(struct adf41513_state *st, u64 freq_uhz, u16 s return ret; /* apply computed results to pll settings */ + pfd_change = st->settings.pfd_frequency_uhz != result.pfd_frequency_uhz; + mode_change = st->settings.mode != result.mode; st->settings = result; dev_dbg(&st->spi->dev, @@ -630,6 +725,14 @@ static int adf41513_set_frequency(struct adf41513_state *st, u64 freq_uhz, u16 s st->regs[ADF41513_REG6] |= ADF41513_REG6_BLEED_ENABLE_MSK; } + if (pfd_change) + adf41513_set_phase_resync(st); + + if (pfd_change || mode_change) { + adf41513_set_bleed_val(st); + adf41513_set_ld_window(st); + } + return adf41513_sync_config(st, sync_mask | ADF41513_SYNC_REG0); } @@ -920,6 +1023,11 @@ static int adf41513_parse_fw(struct adf41513_state *st) st->data.phase_detector_polarity = device_property_read_bool(dev, "adi,phase-detector-polarity-positive-enable"); + st->data.phase_resync_period_ns = 0; + ret = device_property_read_u32(dev, "adi,phase-resync-period-ns", &tmp); + if (!ret) + st->data.phase_resync_period_ns = tmp; + st->data.logic_lvl_1v8_en = device_property_read_bool(dev, "adi,logic-level-1v8-enable"); tmp = ADF41513_LD_COUNT_MIN; From 3eaacb0f1f6337d159f96f878080f64eddd137f2 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 4 Jun 2026 10:59:07 +0100 Subject: [PATCH 035/513] docs: iio: add documentation for adf41513 driver Add documentation for ADF41513 driver, which describes the device driver files and shows how userspace may consume the ABI for various tasks. Tested-by: Randy Dunlap Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- Documentation/iio/adf41513.rst | 199 +++++++++++++++++++++++++++++++++ Documentation/iio/index.rst | 1 + MAINTAINERS | 1 + 3 files changed, 201 insertions(+) create mode 100644 Documentation/iio/adf41513.rst diff --git a/Documentation/iio/adf41513.rst b/Documentation/iio/adf41513.rst new file mode 100644 index 000000000000..244453cce6f6 --- /dev/null +++ b/Documentation/iio/adf41513.rst @@ -0,0 +1,199 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=============== +ADF41513 driver +=============== + +This driver supports Analog Devices' ADF41513 and similar SPI PLL frequency +synthesizers. + +1. Supported devices +==================== + +* `ADF41510 `_ +* `ADF41513 `_ + +The ADF41513 is an ultralow noise frequency synthesizer that can be used to +implement local oscillators (LOs) as high as 26.5 GHz in the upconversion and +downconversion sections of wireless receivers and transmitters. The ADF41510 +is a similar device that supports frequencies up to 10 GHz. + +Both devices support integer-N and fractional-N operation modes, providing +excellent phase noise performance and flexible frequency generation +capabilities. + +Key Features: + +- **ADF41510**: 1 GHz to 10 GHz frequency range +- **ADF41513**: 1 GHz to 26.5 GHz frequency range +- Integer-N and fractional-N operation modes +- Ultra-low phase noise (-235 dBc/Hz integer-N, -231 dBc/Hz fractional-N) +- High maximum PFD frequency (250 MHz integer-N, 125 MHz fractional-N) +- 25-bit fixed modulus or 49-bit variable modulus fractional modes +- Programmable charge pump currents with 16x range +- Digital lock detect functionality +- Phase resync capability for consistent output phase + +2. Device attributes +==================== + +The ADF41513 driver provides the following IIO extended attributes for +frequency control and monitoring: + +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 that provide access to the synthesizer's functionality. + +The following table shows the ADF41513 related device files: + ++--------------------------------------+-------------------------------------------------------+ +| Device file | Description | ++======================================+=======================================================+ +| out_altvoltage0_frequency | RF output frequency control and readback (Hz) | ++--------------------------------------+-------------------------------------------------------+ +| out_altvoltage0_frequency_resolution | Target frequency resolution control (Hz) | ++--------------------------------------+-------------------------------------------------------+ +| out_altvoltage0_powerdown | Power management control (0=active, 1=power down) | ++--------------------------------------+-------------------------------------------------------+ +| out_altvoltage0_phase | RF output phase adjustment and readback (radians) | ++--------------------------------------+-------------------------------------------------------+ + +2.1 Frequency Control +---------------------- + +The ``out_altvoltage0_frequency`` attribute controls the RF output frequency +with sub-Hz precision. The driver automatically selects between integer-N and +fractional-N modes to achieve the requested frequency with the best possible +phase noise performance. + +**Supported ranges:** + +- **ADF41510**: 1,000,000,000 Hz to 10,000,000,000 Hz (1 GHz to 10 GHz) +- **ADF41513**: 1,000,000,000 Hz to 26,500,000,000 Hz (1 GHz to 26.5 GHz) + +The frequency is specified in Hz, for sub-Hz precision use decimal notation. +For example, 12.102 GHz would be written as "12102000000.000000". + +2.2 Frequency Resolution Control +-------------------------------- + +The ``out_altvoltage0_frequency_resolution`` attribute controls the target +frequency resolution that the driver attempts to achieve. This affects the +choice between integer-N and fractional-N modes, including fixed modulus +(25-bit) and variable modulus (49-bit) fractional-N modes: + +- **Integer-N**: Resolution = :math:`f_{PFD}` (same as PFD frequency) +- **Fixed modulus**: Resolution = :math:`f_{PFD} / 2^{25}` (~3 Hz with 100 MHz PFD) +- **Variable modulus**: Resolution = :math:`f_{PFD} / 2^{49}` (µHz resolution possible) + +Default resolution is 1 Hz (1,000,000 µHz). + +2.3 Phase adjustment +-------------------- + +The ``out_altvoltage0_phase`` attribute allows adjustment of the output phase +in radians. Setting this attribute enables phase adjustment. It can be set +from 0 to :math:`2\pi` radians. Reading this attribute returns the current +phase offset of the output signal. To create a consistent phase relationship +with the reference signal, the phase resync feature needs to be enabled by +setting a non-zero value to the ``adi,phase-resync-period-ns`` device property, +which triggers a phase resynchronization after locking is achieved. + +3. Operating modes +================== + +3.1 Integer-N Mode +------------------ + +When the requested frequency can be achieved as an integer multiple of the PFD +frequency (within the specified resolution tolerance), the driver automatically +selects integer-N mode for optimal phase noise performance. + +In integer-N mode: + +- Phase noise: -235 dBc/Hz normalized floor +- Frequency resolution: :math:`f_{PFD}` (same as PFD frequency) +- Maximum PFD frequency: 250 MHz +- Bleed current: Disabled + +3.2 Fractional-N Mode +--------------------- + +When sub-integer frequency steps are required, the driver automatically selects +fractional-N mode using either fixed or variable modulus. + +**Fixed Modulus (25-bit)**: + +- Used when variable modulus is not required +- Resolution: :math:`f_{PFD} / 2^{25}` +- Simpler implementation, faster settling + +**Variable Modulus (49-bit)**: + +- Used for maximum resolution requirements +- Resolution: :math:`f_{PFD} / 2^{49}` (theoretical) +- Exact frequency synthesis capability + +In fractional-N mode: + +- Phase noise: -231 dBc/Hz normalized floor +- Maximum PFD frequency: 125 MHz +- Bleed current: Automatically enabled and optimized +- Dithering: Enabled to reduce fractional spurs + +3.3 Automatic Mode Selection +---------------------------- + +The driver automatically selects the optimal operating mode based on: + +1. **Frequency accuracy requirements**: Determined by ``frequency_resolution`` setting +2. **Phase noise optimization**: Integer-N preferred when possible +3. **PFD frequency constraints**: Different limits for integer vs fractional modes +4. **Prescaler selection**: Automatic 4/5 vs 8/9 prescaler selection based on frequency + +4. Usage examples +================= + +4.1 Basic Frequency Setting +---------------------------- + +Set output frequency to 12.102 GHz: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 12102000000 > out_altvoltage0_frequency + +Read current frequency: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> cat out_altvoltage0_frequency + 12101999999.582767 + +4.2 High Resolution Frequency Control +------------------------------------- + +Configure for sub-Hz resolution and set a precise frequency: + +.. code-block:: bash + + # Set resolution to 0.1 Hz (100,000 µHz) + root:/sys/bus/iio/devices/iio:device0> echo 0.1 > out_altvoltage0_frequency_resolution + + # Set frequency to 12.102 GHz (1 µHz precision) + root:/sys/bus/iio/devices/iio:device0> echo 12102000000 > out_altvoltage0_frequency + root:/sys/bus/iio/devices/iio:device0> cat out_altvoltage0_frequency + 12101999999.980131 + +4.3 Monitor Lock Status +----------------------- + +When lock detect GPIO is configured, check if PLL is locked: + +.. code-block:: bash + + # Read frequency - will return error if not locked + root:/sys/bus/iio/devices/iio:device0> cat out_altvoltage0_frequency + +If the PLL is not locked, the frequency read will return ``-EBUSY`` (Device or +resource busy). diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst index 007e0a1fcc5a..b02b879b053a 100644 --- a/Documentation/iio/index.rst +++ b/Documentation/iio/index.rst @@ -31,6 +31,7 @@ Industrial I/O Kernel Drivers ad7625 ad7944 ade9000 + adf41513 adis16475 adis16480 adis16550 diff --git a/MAINTAINERS b/MAINTAINERS index f07d42807cf6..1132eb39436d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1708,6 +1708,7 @@ L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml +F: Documentation/iio/adf41513.rst F: drivers/iio/frequency/adf41513.c ANALOG DEVICES INC ADF4377 DRIVER From e05c8a6b0b995bbaebbc5ef01c62c8c90280c9b3 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 4 Jun 2026 10:59:08 +0100 Subject: [PATCH 036/513] Documentation: ABI: testing: add common ABI file for iio/frequency Add ABI documentation file for PLL/DDS devices with frequency_resolution sysfs entry attribute used by both ADF4350 and ADF41513. Tested-by: Randy Dunlap Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio-frequency | 11 +++++++++++ .../ABI/testing/sysfs-bus-iio-frequency-adf4350 | 10 ---------- 2 files changed, 11 insertions(+), 10 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-frequency diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency b/Documentation/ABI/testing/sysfs-bus-iio-frequency new file mode 100644 index 000000000000..46075aee7804 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency @@ -0,0 +1,11 @@ +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency_resolution +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + Stores channel Y frequency resolution/channel spacing in Hz for PLL + devices. The given value directly influences the operating mode when + fractional-N synthesis is required, as it derives values for + configurable modulus parameters used in the calculation of the output + frequency. It is assumed that the algorithm used to compute the various + dividers is able to generate proper values for multiples of the channel + spacing. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 index 1254457a726e..76987a119feb 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 @@ -1,13 +1,3 @@ -What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency_resolution -KernelVersion: 3.4.0 -Contact: linux-iio@vger.kernel.org -Description: - Stores channel Y frequency resolution/channel spacing in Hz. - The value given directly influences the MODULUS used by - the fractional-N PLL. It is assumed that the algorithm - that is used to compute the various dividers, is able to - generate proper values for multiples of channel spacing. - What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_refin_frequency KernelVersion: 3.4.0 Contact: linux-iio@vger.kernel.org From fb57b5cdfb97c4f52ca21a432db2aa809ba6692d Mon Sep 17 00:00:00 2001 From: Piyush Patle Date: Thu, 4 Jun 2026 00:18:50 +0530 Subject: [PATCH 037/513] dt-bindings: iio: adc: hx711: add VSUP and DVDD supply properties Document the optional VSUP supply used by the HX711 on-chip regulator. Also document the DVDD digital supply. DVDD is a board-supplied digital input rail (figure 1 of the datasheet shows it as a separate supply, not merely a bypass capacitor point) that was missing from the original binding. Signed-off-by: Piyush Patle Reviewed-by: Andy Shevchenko Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/avia-hx711.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml b/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml index 1ea60dff98d5..0baf620e9057 100644 --- a/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml +++ b/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml @@ -33,6 +33,19 @@ properties: description: Analog supply voltage (AVDD). + vsup-supply: + description: + Supply voltage for the on-chip regulator (VSUP). + + dvdd-supply: + description: + Digital supply voltage (DVDD). On the HX711, DVDD is a + board-supplied digital rail; it was missing from the original + binding. On the HX710B, DVDD must be greater than or equal to + AVDD; when DVDD is a battery rail and AVDD is a regulated supply, + one channel monitors the DVDD-AVDD difference for battery level + detection. + clock-frequency: description: Controls the SCK bit-bang timing. The value is used to derive the From 15842186c358de52af5bdf3694739d429dfe2564 Mon Sep 17 00:00:00 2001 From: Piyush Patle Date: Thu, 4 Jun 2026 00:18:51 +0530 Subject: [PATCH 038/513] dt-bindings: iio: adc: hx711: add RATE GPIO property Document the optional RATE pin GPIO used to select the HX711 output data rate. Update the example to show the property in use. Signed-off-by: Piyush Patle Reviewed-by: Andy Shevchenko Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml b/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml index 0baf620e9057..099235b5fd54 100644 --- a/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml +++ b/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml @@ -46,6 +46,13 @@ properties: one channel monitors the DVDD-AVDD difference for battery level detection. + rate-gpios: + description: + GPIO connected to the RATE pin. When driven low the output data + rate is 10 SPS; when driven high it is 80 SPS. If omitted the + RATE pin state is determined by the board wiring. + maxItems: 1 + clock-frequency: description: Controls the SCK bit-bang timing. The value is used to derive the @@ -70,6 +77,7 @@ examples: compatible = "avia,hx711"; sck-gpios = <&gpio3 10 GPIO_ACTIVE_HIGH>; dout-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>; + rate-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; avdd-supply = <&avdd>; clock-frequency = <100000>; }; From e301c363db6064abf41bc52960b6a44257e52db3 Mon Sep 17 00:00:00 2001 From: Piyush Patle Date: Thu, 4 Jun 2026 00:18:54 +0530 Subject: [PATCH 039/513] iio: adc: hx711: introduce hx711_chip_info structure Add a per-variant static configuration structure and populate the IIO device fields from it at probe time. This is a preparatory change for adding support for more HX711-compatible hardware variants without duplicating the probe-time setup. No functional change for existing HX711 users. Signed-off-by: Piyush Patle Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/hx711.c | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c index 86d2a70dd3de..10ccc69f209b 100644 --- a/drivers/iio/adc/hx711.c +++ b/drivers/iio/adc/hx711.c @@ -4,6 +4,8 @@ * * Copyright (c) 2016 Andreas Klinger */ +#include +#include #include #include #include @@ -74,6 +76,20 @@ static int hx711_get_scale_to_gain(const int *gain_scale, int scale) return -EINVAL; } +/** + * struct hx711_chip_info - per-variant static configuration + * @name: IIO device name + * @channels: channel specification array + * @num_channels: number of entries in @channels + * @iio_info: IIO info ops for this variant + */ +struct hx711_chip_info { + const char *name; + const struct iio_chan_spec *channels __counted_by_ptr(num_channels); + unsigned int num_channels; + const struct iio_info *iio_info; +}; + struct hx711_data { struct device *dev; struct gpio_desc *gpiod_pd_sck; @@ -81,6 +97,7 @@ struct hx711_data { int gain_set; /* gain set on device */ int gain_chan_a; /* gain for channel A */ int gain_scale[HX711_GAIN_MAX]; + const struct hx711_chip_info *chip_info; struct mutex lock; /* * triggered buffer @@ -456,8 +473,16 @@ static const struct iio_chan_spec hx711_chan_spec[] = { IIO_CHAN_SOFT_TIMESTAMP(2), }; +static const struct hx711_chip_info hx711_chip = { + .name = "hx711", + .channels = hx711_chan_spec, + .iio_info = &hx711_iio_info, + .num_channels = ARRAY_SIZE(hx711_chan_spec), +}; + static int hx711_probe(struct platform_device *pdev) { + const struct hx711_chip_info *chip_info; struct device *dev = &pdev->dev; struct hx711_data *hx711_data; struct iio_dev *indio_dev; @@ -473,6 +498,12 @@ static int hx711_probe(struct platform_device *pdev) mutex_init(&hx711_data->lock); + chip_info = device_get_match_data(dev); + if (!chip_info) + return dev_err_probe(dev, -ENODEV, "missing driver data\n"); + + hx711_data->chip_info = chip_info; + /* * PD_SCK stands for power down and serial clock input of HX711 * in the driver it is an output @@ -534,11 +565,11 @@ static int hx711_probe(struct platform_device *pdev) hx711_data->data_ready_delay_ns = 1000000000 / hx711_data->clock_frequency; - indio_dev->name = "hx711"; - indio_dev->info = &hx711_iio_info; + indio_dev->name = chip_info->name; + indio_dev->info = chip_info->iio_info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = hx711_chan_spec; - indio_dev->num_channels = ARRAY_SIZE(hx711_chan_spec); + indio_dev->channels = chip_info->channels; + indio_dev->num_channels = chip_info->num_channels; ret = devm_iio_triggered_buffer_setup(dev, indio_dev, iio_pollfunc_store_time, @@ -555,7 +586,7 @@ static int hx711_probe(struct platform_device *pdev) } static const struct of_device_id of_hx711_match[] = { - { .compatible = "avia,hx711", }, + { .compatible = "avia,hx711", .data = &hx711_chip }, { } }; From 44a6b355da1301534a20c0acf33f2d5ab9c4b916 Mon Sep 17 00:00:00 2001 From: Piyush Patle Date: Thu, 4 Jun 2026 00:18:55 +0530 Subject: [PATCH 040/513] iio: adc: hx711: pass trailing pulse count into hx711_read Move the trailing-pulse selection out of hx711_read() and into its callers. This is a preparatory change so later variant-specific code can pass the per-channel pulse count without adding a separate read path. No functional change. Signed-off-by: Piyush Patle Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/hx711.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c index 10ccc69f209b..30e1ce321747 100644 --- a/drivers/iio/adc/hx711.c +++ b/drivers/iio/adc/hx711.c @@ -155,7 +155,7 @@ static int hx711_cycle(struct hx711_data *hx711_data) return gpiod_get_value(hx711_data->gpiod_dout); } -static int hx711_read(struct hx711_data *hx711_data) +static int hx711_read(struct hx711_data *hx711_data, int trailing_pulses) { int i, ret; int value = 0; @@ -174,7 +174,7 @@ static int hx711_read(struct hx711_data *hx711_data) value ^= 0x800000; - for (i = 0; i < hx711_get_gain_to_pulse(hx711_data->gain_set); i++) + for (i = 0; i < trailing_pulses; i++) hx711_cycle(hx711_data); return value; @@ -237,7 +237,8 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan) if (hx711_data->gain_set == 32) { hx711_data->gain_set = hx711_data->gain_chan_a; - ret = hx711_read(hx711_data); + ret = hx711_read(hx711_data, + hx711_get_gain_to_pulse(hx711_data->gain_set)); if (ret < 0) return ret; @@ -249,7 +250,8 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan) if (hx711_data->gain_set != 32) { hx711_data->gain_set = 32; - ret = hx711_read(hx711_data); + ret = hx711_read(hx711_data, + hx711_get_gain_to_pulse(hx711_data->gain_set)); if (ret < 0) return ret; @@ -264,8 +266,8 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan) static int hx711_reset_read(struct hx711_data *hx711_data, int chan) { + int trailing_pulses; int ret; - int val; /* * hx711_reset() must be called from here @@ -280,9 +282,8 @@ static int hx711_reset_read(struct hx711_data *hx711_data, int chan) if (ret < 0) return ret; - val = hx711_read(hx711_data); - - return val; + trailing_pulses = hx711_get_gain_to_pulse(hx711_data->gain_set); + return hx711_read(hx711_data, trailing_pulses); } static int hx711_read_raw(struct iio_dev *indio_dev, @@ -349,7 +350,8 @@ static int hx711_write_raw(struct iio_dev *indio_dev, if (gain != 32) hx711_data->gain_chan_a = gain; - ret = hx711_read(hx711_data); + ret = hx711_read(hx711_data, + hx711_get_gain_to_pulse(hx711_data->gain_set)); if (ret < 0) { mutex_unlock(&hx711_data->lock); return ret; From b74def999f929eb4d9fdf98a0d4f8ab5b8edeef2 Mon Sep 17 00:00:00 2001 From: Piyush Patle Date: Thu, 4 Jun 2026 00:18:56 +0530 Subject: [PATCH 041/513] iio: adc: hx711: split variable assignments in hx711_read and hx711_reset Separate the initial value assignments from the declarations in hx711_read() and hx711_reset(). This is a small preparatory cleanup before the later loop-iterator and variant-specific changes adjust the local variable layout in these functions. No functional change. Signed-off-by: Piyush Patle Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/hx711.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c index 30e1ce321747..4ccb0bf2d71f 100644 --- a/drivers/iio/adc/hx711.c +++ b/drivers/iio/adc/hx711.c @@ -159,9 +159,10 @@ static int hx711_read(struct hx711_data *hx711_data, int trailing_pulses) { int i, ret; int value = 0; - int val = gpiod_get_value(hx711_data->gpiod_dout); + int val; /* we double check if it's really down */ + val = gpiod_get_value(hx711_data->gpiod_dout); if (val) return -EIO; @@ -204,8 +205,9 @@ static int hx711_wait_for_ready(struct hx711_data *hx711_data) static int hx711_reset(struct hx711_data *hx711_data) { - int val = hx711_wait_for_ready(hx711_data); + int val; + val = hx711_wait_for_ready(hx711_data); if (val) { /* * an examination with the oszilloscope indicated From fc737af15882f72224c70d5f5922f2fd8c2aea70 Mon Sep 17 00:00:00 2001 From: Piyush Patle Date: Thu, 4 Jun 2026 00:18:57 +0530 Subject: [PATCH 042/513] iio: adc: hx711: localize loop iterators in hx711_read Tighten the scope of the loop variables in hx711_read() now that trailing-pulse selection is already handled by the callers. Also replace the 24-bit loop bound with a named constant while touching the same code. Suggested-by: Andy Shevchenko Signed-off-by: Piyush Patle Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/hx711.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c index 4ccb0bf2d71f..e5d842853b71 100644 --- a/drivers/iio/adc/hx711.c +++ b/drivers/iio/adc/hx711.c @@ -23,6 +23,8 @@ #include #include +#define HX711_DATA_BITS 24 + /* gain to pulse and scale conversion */ #define HX711_GAIN_MAX 3 #define HX711_RESET_GAIN 128 @@ -157,16 +159,16 @@ static int hx711_cycle(struct hx711_data *hx711_data) static int hx711_read(struct hx711_data *hx711_data, int trailing_pulses) { - int i, ret; int value = 0; int val; + int ret; /* we double check if it's really down */ val = gpiod_get_value(hx711_data->gpiod_dout); if (val) return -EIO; - for (i = 0; i < 24; i++) { + for (int i = 0; i < HX711_DATA_BITS; i++) { value <<= 1; ret = hx711_cycle(hx711_data); if (ret) @@ -175,7 +177,7 @@ static int hx711_read(struct hx711_data *hx711_data, int trailing_pulses) value ^= 0x800000; - for (i = 0; i < trailing_pulses; i++) + for (int i = 0; i < trailing_pulses; i++) hx711_cycle(hx711_data); return value; From 8a4eb07a9354f940e0f6f2aa5231e09cce5fb5ac Mon Sep 17 00:00:00 2001 From: Piyush Patle Date: Thu, 4 Jun 2026 00:18:58 +0530 Subject: [PATCH 043/513] iio: adc: hx711: pass iio_chan_spec to hx711_reset_read Change hx711_reset_read() to accept the channel descriptor directly and update its callers accordingly. Split the existing HX711 channel-selection work into a small helper so a later variant-specific change can add a matching helper without growing hx711_reset_read() further. No functional change. Signed-off-by: Piyush Patle Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/hx711.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c index e5d842853b71..fde95758b9a0 100644 --- a/drivers/iio/adc/hx711.c +++ b/drivers/iio/adc/hx711.c @@ -268,7 +268,23 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan) return 0; } -static int hx711_reset_read(struct hx711_data *hx711_data, int chan) +static int hx711_set_hx711_channel(struct hx711_data *hx711_data, + const struct iio_chan_spec *chan, + int *trailing_pulses) +{ + int ret; + + ret = hx711_set_gain_for_channel(hx711_data, chan->channel); + if (ret < 0) + return ret; + + *trailing_pulses = hx711_get_gain_to_pulse(hx711_data->gain_set); + + return 0; +} + +static int hx711_reset_read(struct hx711_data *hx711_data, + const struct iio_chan_spec *chan) { int trailing_pulses; int ret; @@ -282,11 +298,10 @@ static int hx711_reset_read(struct hx711_data *hx711_data, int chan) return -EIO; } - ret = hx711_set_gain_for_channel(hx711_data, chan); + ret = hx711_set_hx711_channel(hx711_data, chan, &trailing_pulses); if (ret < 0) return ret; - trailing_pulses = hx711_get_gain_to_pulse(hx711_data->gain_set); return hx711_read(hx711_data, trailing_pulses); } @@ -300,7 +315,7 @@ static int hx711_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_RAW: mutex_lock(&hx711_data->lock); - *val = hx711_reset_read(hx711_data, chan->channel); + *val = hx711_reset_read(hx711_data, chan); mutex_unlock(&hx711_data->lock); @@ -390,8 +405,8 @@ static irqreturn_t hx711_trigger(int irq, void *p) memset(&hx711_data->buffer, 0, sizeof(hx711_data->buffer)); iio_for_each_active_channel(indio_dev, i) { - hx711_data->buffer.channel[j] = hx711_reset_read(hx711_data, - indio_dev->channels[i].channel); + hx711_data->buffer.channel[j] = + hx711_reset_read(hx711_data, &indio_dev->channels[i]); j++; } From c2d11f5cc238233dccc6c6a9bcb7e591a327c118 Mon Sep 17 00:00:00 2001 From: Radu Sabau Date: Mon, 8 Jun 2026 12:02:54 +0300 Subject: [PATCH 044/513] iio: adc: ad_sigma_delta: allow COMPILE_TEST in isolation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AD_SIGMA_DELTA is a hidden tristate that can only be enabled by selecting drivers (AD7124, AD7192, etc.), all of which require SPI. This prevents compile-testing the library code directly with allmodconfig or by manually enabling it with COMPILE_TEST. Add a prompt string guarded by "if COMPILE_TEST" so the symbol becomes directly selectable when COMPILE_TEST is set. Also add an explicit "depends on SPI" since the ad_sigma_delta library directly calls SPI core symbols such as spi_bus_lock(), spi_bus_unlock(), and spi_sync_locked(). Signed-off-by: Radu Sabau Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 1c663c98c6c9..50eafa128cf4 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -33,7 +33,8 @@ config AB8500_GPADC and USB voltages integral to the U8500 platform. config AD_SIGMA_DELTA - tristate + tristate "Analog Devices Sigma-Delta Modulator support" if COMPILE_TEST + depends on SPI select IIO_BUFFER select IIO_BUFFER_DMAENGINE select IIO_TRIGGERED_BUFFER From 82fe80d3ac37beb338bcddcdc5a7900e465c1b15 Mon Sep 17 00:00:00 2001 From: Manuel Ebner Date: Fri, 12 Jun 2026 14:13:43 +0200 Subject: [PATCH 045/513] Documentation: ABI: testing: iio: minor cleanup Add parenthesis and dashes. Signed-off-by: Manuel Ebner Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 10 +++++----- Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index d8d6d85235b0..4ea5598e7cd2 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -917,7 +917,7 @@ Description: Note the driver will assume the last p events requested are to be enabled where p is how many it supports (which may vary - depending on the exact set requested. So if you want to be + depending on the exact set requested). So if you want to be sure you have set what you think you have, check the contents of these attributes after everything is configured. Drivers may have to buffer any parameters so that they are consistent when @@ -973,7 +973,7 @@ Description: Note the driver will assume the last p events requested are to be enabled where p is however many it supports (which may - vary depending on the exact set requested. So if you want to be + vary depending on the exact set requested). So if you want to be sure you have set what you think you have, check the contents of these attributes after everything is configured. Drivers may have to buffer any parameters so that they are consistent when @@ -1528,9 +1528,9 @@ Description: the unused bits, so to get a clean value the bits value must be used to mask the buffer output value appropriately. The storagebits value also specifies the data alignment. So s48/64>>2 will be a - signed 48 bit integer stored in a 64 bit location aligned to a 64 - bit boundary. To obtain the clean value, shift right 2 and apply a - mask to zero the top 16 bits of the result. + signed 48-bit integer stored in a 64-bit location aligned to a + 64-bit boundary. To obtain the clean value, shift right 2 and apply + a mask to zero the top 16 bits of the result. For other storage combinations this attribute will be extended appropriately. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 b/Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 index 28be1cabf112..d44eb5f8728b 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 @@ -16,7 +16,7 @@ Description: In bridge applications, such as strain gauges and load cells, the bridge itself consumes the majority of the current in the system. To minimize the current consumption of the system, - the bridge can be disconnected (when it is not being used + the bridge can be disconnected (when it is not being used) using the bridge_switch_en attribute. What: /sys/bus/iio/devices/iio:deviceX/in_voltage2-voltage2_shorted_raw From ec034d09813213634f3abf6eaa1880bef2584090 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Fri, 12 Jun 2026 16:51:50 -0500 Subject: [PATCH 046/513] iio: stm32-dfsdm: Treat flags as booleans The "st,adc-alt-channel" and "st,filter0-sync" properties are documented as boolean flags. The legacy parser read them as integer cells, unlike the child-node parser which already checks only for presence. Use presence and boolean helpers so both parsers follow the binding and the property type checker no longer reports the flags. Assisted-by: Codex:gpt-5-5 Signed-off-by: Rob Herring (Arm) Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-dfsdm-adc.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index 9664b9bd75d4..00f05e167afc 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -660,11 +660,8 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm, } df_ch->src = val; - ret = of_property_read_u32_index(indio_dev->dev.of_node, - "st,adc-alt-channel", chan_idx, - &df_ch->alt_si); - if (ret < 0) - df_ch->alt_si = 0; + df_ch->alt_si = of_property_present(indio_dev->dev.of_node, + "st,adc-alt-channel"); return 0; } @@ -1815,9 +1812,8 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev) adc->dfsdm->fl_list[adc->fl_id].ford = val; - ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val); - if (!ret) - adc->dfsdm->fl_list[adc->fl_id].sync_mode = val; + adc->dfsdm->fl_list[adc->fl_id].sync_mode = + of_property_read_bool(dev->of_node, "st,filter0-sync"); adc->dev_data = dev_data; ret = dev_data->init(dev, iio); From a3ed4362d3d902f0adf4e187eed15ccbe230fc7a Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Sun, 14 Jun 2026 15:19:03 +0200 Subject: [PATCH 047/513] iio: light: opt3001: move device registration to end of probe() Move IIO device registration to the end of the probe() function to follow standard driver teardown/setup ordering and improve driver logic. Additionally, switch devm_iio_device_register() to its unmanaged counterpart as current driver implementation mixes managed and unmanaged resources, causing potential resource leaks. Also, add iio_device_unregister() to remove() function to correctly handle teardown. Suggested-by: Jonathan Cameron Signed-off-by: Joshua Crofts Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt3001.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 0743e16f2a8f..0423c6de5321 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -876,12 +876,6 @@ static int opt3001_probe(struct i2c_client *client) iio->modes = INDIO_DIRECT_MODE; iio->info = &opt3001_info; - ret = devm_iio_device_register(dev, iio); - if (ret) { - dev_err(dev, "failed to register IIO device\n"); - return ret; - } - /* Make use of INT pin only if valid IRQ no. is given */ if (irq > 0) { ret = request_threaded_irq(irq, NULL, opt3001_irq, @@ -896,7 +890,17 @@ static int opt3001_probe(struct i2c_client *client) dev_dbg(opt->dev, "enabling interrupt-less operation\n"); } + ret = iio_device_register(iio); + if (ret) + goto free_irq; + return 0; + +free_irq: + if (irq > 0) + free_irq(irq, iio); + + return ret; } static void opt3001_remove(struct i2c_client *client) @@ -906,6 +910,8 @@ static void opt3001_remove(struct i2c_client *client) int ret; u16 reg; + iio_device_unregister(iio); + if (opt->use_irq) free_irq(client->irq, iio); From c9439c1de88fd7fba1e95fc4c756b2384beba423 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Sun, 14 Jun 2026 15:19:04 +0200 Subject: [PATCH 048/513] iio: light: opt3001: use local struct device and i2c_client variables Switch the driver to use local variables for struct device and struct i2c_client and remove struct device member from struct opt3001, as the former can be derived from struct client. No functional change. Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt3001.c | 139 +++++++++++++++++++----------------- 1 file changed, 73 insertions(+), 66 deletions(-) diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 0423c6de5321..16cc9b4bdef1 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -101,7 +101,6 @@ struct opt3001_chip_info { struct opt3001 { struct i2c_client *client; - struct device *dev; struct mutex lock; bool ok_to_ignore_lock; @@ -313,6 +312,8 @@ static const struct iio_chan_spec opt3002_channels[] = { static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) { + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; int ret; u16 mantissa; u16 reg; @@ -326,12 +327,12 @@ static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) * doing so will overwrite the low-level limit value however we * will restore this value later on. */ - ret = i2c_smbus_write_word_swapped(opt->client, - OPT3001_LOW_LIMIT, - OPT3001_LOW_LIMIT_EOC_ENABLE); + ret = i2c_smbus_write_word_swapped(client, + OPT3001_LOW_LIMIT, + OPT3001_LOW_LIMIT_EOC_ENABLE); if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_LOW_LIMIT); + dev_err(dev, "failed to write register %02x\n", + OPT3001_LOW_LIMIT); return ret; } @@ -343,21 +344,20 @@ static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) opt->result_ready = false; /* Configure for single-conversion mode and start a new conversion */ - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); goto err; } reg = ret; opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SINGLE); - ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, - reg); + ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_CONFIGURATION); + dev_err(dev, "failed to write register %02x\n", + OPT3001_CONFIGURATION); goto err; } @@ -377,10 +377,9 @@ static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) msleep(timeout); /* Check result ready flag */ - ret = i2c_smbus_read_word_swapped(opt->client, - OPT3001_CONFIGURATION); + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", + dev_err(dev, "failed to read register %02x\n", OPT3001_CONFIGURATION); goto err; } @@ -391,9 +390,9 @@ static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) } /* Obtain value */ - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT); + ret = i2c_smbus_read_word_swapped(client, OPT3001_RESULT); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", + dev_err(dev, "failed to read register %02x\n", OPT3001_RESULT); goto err; } @@ -418,12 +417,12 @@ static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) * bit-overlap and therefore can't be done. */ value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa; - ret = i2c_smbus_write_word_swapped(opt->client, + ret = i2c_smbus_write_word_swapped(client, OPT3001_LOW_LIMIT, value); if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_LOW_LIMIT); + dev_err(dev, "failed to write register %02x\n", + OPT3001_LOW_LIMIT); return ret; } } @@ -446,13 +445,15 @@ static int opt3001_get_int_time(struct opt3001 *opt, int *val, int *val2) static int opt3001_set_int_time(struct opt3001 *opt, int time) { + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; int ret; u16 reg; - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); return ret; } @@ -471,8 +472,7 @@ static int opt3001_set_int_time(struct opt3001 *opt, int time) return -EINVAL; } - return i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, - reg); + return i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); } static int opt3001_read_raw(struct iio_dev *iio, @@ -567,6 +567,8 @@ static int opt3001_write_event_value(struct iio_dev *iio, int val, int val2) { struct opt3001 *opt = iio_priv(iio); + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; int ret; int whole; int integer; @@ -585,7 +587,7 @@ static int opt3001_write_event_value(struct iio_dev *iio, ret = opt3001_find_scale(opt, val, val2, &exponent); if (ret < 0) { - dev_err(opt->dev, "can't find scale for %d.%06u\n", val, val2); + dev_err(dev, "can't find scale for %d.%06u\n", val, val2); goto err; } @@ -613,9 +615,9 @@ static int opt3001_write_event_value(struct iio_dev *iio, goto err; } - ret = i2c_smbus_write_word_swapped(opt->client, reg, value); + ret = i2c_smbus_write_word_swapped(client, reg, value); if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", reg); + dev_err(dev, "failed to write register %02x\n", reg); goto err; } @@ -639,6 +641,8 @@ static int opt3001_write_event_config(struct iio_dev *iio, enum iio_event_direction dir, bool state) { struct opt3001 *opt = iio_priv(iio); + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; int ret; u16 mode; u16 reg; @@ -654,21 +658,20 @@ static int opt3001_write_event_config(struct iio_dev *iio, mode = state ? OPT3001_CONFIGURATION_M_CONTINUOUS : OPT3001_CONFIGURATION_M_SHUTDOWN; - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); goto err; } reg = ret; opt3001_set_mode(opt, ®, mode); - ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, - reg); + ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_CONFIGURATION); + dev_err(dev, "failed to write register %02x\n", + OPT3001_CONFIGURATION); goto err; } @@ -690,13 +693,15 @@ static const struct iio_info opt3001_info = { static int opt3001_read_id(struct opt3001 *opt) { + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; char manufacturer[2]; u16 device_id; int ret; - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_MANUFACTURER_ID); + ret = i2c_smbus_read_word_swapped(client, OPT3001_MANUFACTURER_ID); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", + dev_err(dev, "failed to read register %02x\n", OPT3001_MANUFACTURER_ID); return ret; } @@ -704,29 +709,31 @@ static int opt3001_read_id(struct opt3001 *opt) manufacturer[0] = ret >> 8; manufacturer[1] = ret & 0xff; - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_DEVICE_ID); + ret = i2c_smbus_read_word_swapped(client, OPT3001_DEVICE_ID); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", + dev_err(dev, "failed to read register %02x\n", OPT3001_DEVICE_ID); return ret; } device_id = ret; - dev_info(opt->dev, "Found %c%c OPT%04x\n", manufacturer[0], - manufacturer[1], device_id); + dev_info(dev, "Found %c%c OPT%04x\n", manufacturer[0], manufacturer[1], + device_id); return 0; } static int opt3001_configure(struct opt3001 *opt) { + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; int ret; u16 reg; - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", + dev_err(dev, "failed to read register %02x\n", OPT3001_CONFIGURATION); return ret; } @@ -752,17 +759,16 @@ static int opt3001_configure(struct opt3001 *opt) reg &= ~OPT3001_CONFIGURATION_ME; reg &= ~OPT3001_CONFIGURATION_FC_MASK; - ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, - reg); + ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", + dev_err(dev, "failed to write register %02x\n", OPT3001_CONFIGURATION); return ret; } - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_LOW_LIMIT); + ret = i2c_smbus_read_word_swapped(client, OPT3001_LOW_LIMIT); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", + dev_err(dev, "failed to read register %02x\n", OPT3001_LOW_LIMIT); return ret; } @@ -770,9 +776,9 @@ static int opt3001_configure(struct opt3001 *opt) opt->low_thresh_mantissa = OPT3001_REG_MANTISSA(ret); opt->low_thresh_exp = OPT3001_REG_EXPONENT(ret); - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_HIGH_LIMIT); + ret = i2c_smbus_read_word_swapped(client, OPT3001_HIGH_LIMIT); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", + dev_err(dev, "failed to read register %02x\n", OPT3001_HIGH_LIMIT); return ret; } @@ -787,6 +793,8 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) { struct iio_dev *iio = _iio; struct opt3001 *opt = iio_priv(iio); + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; int ret; bool wake_result_ready_queue = false; enum iio_chan_type chan_type = opt->chip_info->chan_type; @@ -795,10 +803,10 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) if (!ok_to_ignore_lock) mutex_lock(&opt->lock); - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); goto out; } @@ -817,10 +825,10 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) IIO_EV_DIR_FALLING), iio_get_time_ns(iio)); } else if (ret & OPT3001_CONFIGURATION_CRF) { - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT); + ret = i2c_smbus_read_word_swapped(client, OPT3001_RESULT); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_RESULT); + dev_err(dev, "failed to read register %02x\n", + OPT3001_RESULT); goto out; } opt->result = ret; @@ -853,7 +861,6 @@ static int opt3001_probe(struct i2c_client *client) opt = iio_priv(iio); opt->client = client; - opt->dev = dev; opt->chip_info = i2c_get_match_data(client); mutex_init(&opt->lock); @@ -887,7 +894,7 @@ static int opt3001_probe(struct i2c_client *client) } opt->use_irq = true; } else { - dev_dbg(opt->dev, "enabling interrupt-less operation\n"); + dev_dbg(dev, "enabling interrupt-less operation\n"); } ret = iio_device_register(iio); @@ -907,6 +914,7 @@ static void opt3001_remove(struct i2c_client *client) { struct iio_dev *iio = i2c_get_clientdata(client); struct opt3001 *opt = iio_priv(iio); + struct device *dev = &client->dev; int ret; u16 reg; @@ -915,21 +923,20 @@ static void opt3001_remove(struct i2c_client *client) if (opt->use_irq) free_irq(client->irq, iio); - ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); if (ret < 0) { - dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); return; } reg = ret; opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SHUTDOWN); - ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, - reg); + ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_CONFIGURATION); + dev_err(dev, "failed to write register %02x\n", + OPT3001_CONFIGURATION); } } From 8289400299b2e8bff2fecccf734b9f6901fde537 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Sun, 14 Jun 2026 15:19:05 +0200 Subject: [PATCH 049/513] iio: light: opt3001: prefer dev_err_probe() Switch driver to use dev_err_probe() to unify error messages generated in *_probe() and probe path functions. Reviewed-by: Maxwell Doose Reviewed-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt3001.c | 57 +++++++++++++++---------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 16cc9b4bdef1..b11690f32398 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -700,21 +700,17 @@ static int opt3001_read_id(struct opt3001 *opt) int ret; ret = i2c_smbus_read_word_swapped(client, OPT3001_MANUFACTURER_ID); - if (ret < 0) { - dev_err(dev, "failed to read register %02x\n", - OPT3001_MANUFACTURER_ID); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read register %02x\n", + OPT3001_MANUFACTURER_ID); manufacturer[0] = ret >> 8; manufacturer[1] = ret & 0xff; ret = i2c_smbus_read_word_swapped(client, OPT3001_DEVICE_ID); - if (ret < 0) { - dev_err(dev, "failed to read register %02x\n", - OPT3001_DEVICE_ID); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read register %02x\n", + OPT3001_DEVICE_ID); device_id = ret; @@ -732,11 +728,9 @@ static int opt3001_configure(struct opt3001 *opt) u16 reg; ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); - if (ret < 0) { - dev_err(dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read register %02x\n", + OPT3001_CONFIGURATION); reg = ret; @@ -760,28 +754,22 @@ static int opt3001_configure(struct opt3001 *opt) reg &= ~OPT3001_CONFIGURATION_FC_MASK; ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); - if (ret < 0) { - dev_err(dev, "failed to write register %02x\n", - OPT3001_CONFIGURATION); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed to write register %02x\n", + OPT3001_CONFIGURATION); ret = i2c_smbus_read_word_swapped(client, OPT3001_LOW_LIMIT); - if (ret < 0) { - dev_err(dev, "failed to read register %02x\n", - OPT3001_LOW_LIMIT); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read register %02x\n", + OPT3001_LOW_LIMIT); opt->low_thresh_mantissa = OPT3001_REG_MANTISSA(ret); opt->low_thresh_exp = OPT3001_REG_EXPONENT(ret); ret = i2c_smbus_read_word_swapped(client, OPT3001_HIGH_LIMIT); - if (ret < 0) { - dev_err(dev, "failed to read register %02x\n", - OPT3001_HIGH_LIMIT); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read register %02x\n", + OPT3001_HIGH_LIMIT); opt->high_thresh_mantissa = OPT3001_REG_MANTISSA(ret); opt->high_thresh_exp = OPT3001_REG_EXPONENT(ret); @@ -888,10 +876,11 @@ static int opt3001_probe(struct i2c_client *client) ret = request_threaded_irq(irq, NULL, opt3001_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "opt3001", iio); - if (ret) { - dev_err(dev, "failed to request IRQ #%d\n", irq); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "failed to request IRQ #%d\n", + irq); + opt->use_irq = true; } else { dev_dbg(dev, "enabling interrupt-less operation\n"); From 9b7784495181f7dba1edbdb66bef9f681c7956c5 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Sun, 14 Jun 2026 15:19:06 +0200 Subject: [PATCH 050/513] iio: light: opt3001: ensure correct parenthesis alignment Ensure correct parenthesis alignment per checkpatch.pl report. No functional change. Suggested-by: Andy Shevchenko Reviewed-by: Maxwell Doose Reviewed-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt3001.c | 71 ++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index b11690f32398..6634054506da 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -223,8 +223,8 @@ static const struct opt3001_scale opt3002_scales[] = { }, }; -static int opt3001_find_scale(const struct opt3001 *opt, int val, - int val2, u8 *exponent) +static int opt3001_find_scale(const struct opt3001 *opt, int val, int val2, + u8 *exponent) { int i; for (i = 0; i < ARRAY_SIZE(*opt->chip_info->scales); i++) { @@ -242,8 +242,8 @@ static int opt3001_find_scale(const struct opt3001 *opt, int val, return -EINVAL; } -static void opt3001_to_iio_ret(struct opt3001 *opt, u8 exponent, - u16 mantissa, int *val, int *val2) +static void opt3001_to_iio_ret(struct opt3001 *opt, u8 exponent, u16 mantissa, + int *val, int *val2) { int ret; int whole = opt->chip_info->factor_whole; @@ -364,8 +364,8 @@ static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) if (opt->use_irq) { /* Wait for the IRQ to indicate the conversion is complete */ ret = wait_event_timeout(opt->result_ready_queue, - opt->result_ready, - msecs_to_jiffies(OPT3001_RESULT_READY_LONG)); + opt->result_ready, + msecs_to_jiffies(OPT3001_RESULT_READY_LONG)); if (ret == 0) { ret = -ETIMEDOUT; goto err; @@ -476,8 +476,8 @@ static int opt3001_set_int_time(struct opt3001 *opt, int time) } static int opt3001_read_raw(struct iio_dev *iio, - struct iio_chan_spec const *chan, int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct opt3001 *opt = iio_priv(iio); int ret; @@ -508,8 +508,8 @@ static int opt3001_read_raw(struct iio_dev *iio, } static int opt3001_write_raw(struct iio_dev *iio, - struct iio_chan_spec const *chan, int val, int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct opt3001 *opt = iio_priv(iio); int ret; @@ -534,9 +534,11 @@ static int opt3001_write_raw(struct iio_dev *iio, } static int opt3001_read_event_value(struct iio_dev *iio, - const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, enum iio_event_info info, - int *val, int *val2) + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) { struct opt3001 *opt = iio_priv(iio); int ret = IIO_VAL_INT_PLUS_MICRO; @@ -546,11 +548,11 @@ static int opt3001_read_event_value(struct iio_dev *iio, switch (dir) { case IIO_EV_DIR_RISING: opt3001_to_iio_ret(opt, opt->high_thresh_exp, - opt->high_thresh_mantissa, val, val2); + opt->high_thresh_mantissa, val, val2); break; case IIO_EV_DIR_FALLING: opt3001_to_iio_ret(opt, opt->low_thresh_exp, - opt->low_thresh_mantissa, val, val2); + opt->low_thresh_mantissa, val, val2); break; default: ret = -EINVAL; @@ -562,9 +564,11 @@ static int opt3001_read_event_value(struct iio_dev *iio, } static int opt3001_write_event_value(struct iio_dev *iio, - const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, enum iio_event_info info, - int val, int val2) + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) { struct opt3001 *opt = iio_priv(iio); struct i2c_client *client = opt->client; @@ -628,8 +632,9 @@ static int opt3001_write_event_value(struct iio_dev *iio, } static int opt3001_read_event_config(struct iio_dev *iio, - const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir) + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) { struct opt3001 *opt = iio_priv(iio); @@ -637,8 +642,10 @@ static int opt3001_read_event_config(struct iio_dev *iio, } static int opt3001_write_event_config(struct iio_dev *iio, - const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, bool state) + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) { struct opt3001 *opt = iio_priv(iio); struct i2c_client *client = opt->client; @@ -802,16 +809,16 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) OPT3001_CONFIGURATION_M_CONTINUOUS) { if (ret & OPT3001_CONFIGURATION_FH) iio_push_event(iio, - IIO_UNMOD_EVENT_CODE(chan_type, 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING), - iio_get_time_ns(iio)); + IIO_UNMOD_EVENT_CODE(chan_type, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns(iio)); if (ret & OPT3001_CONFIGURATION_FL) iio_push_event(iio, - IIO_UNMOD_EVENT_CODE(chan_type, 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_FALLING), - iio_get_time_ns(iio)); + IIO_UNMOD_EVENT_CODE(chan_type, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns(iio)); } else if (ret & OPT3001_CONFIGURATION_CRF) { ret = i2c_smbus_read_word_swapped(client, OPT3001_RESULT); if (ret < 0) { @@ -874,8 +881,8 @@ static int opt3001_probe(struct i2c_client *client) /* Make use of INT pin only if valid IRQ no. is given */ if (irq > 0) { ret = request_threaded_irq(irq, NULL, opt3001_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "opt3001", iio); + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "opt3001", iio); if (ret) return dev_err_probe(dev, ret, "failed to request IRQ #%d\n", From 960642100cabecd6f7138b8a8d62dd218ac9f2ca Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Sun, 14 Jun 2026 15:19:07 +0200 Subject: [PATCH 051/513] iio: light: opt3001: localize for loop iterator Localize loop iterator to tighten scope and improve code style per checkpatch.pl report. No functional change. Reviewed-by: Maxwell Doose Reviewed-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt3001.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 6634054506da..c301549ddaa7 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -226,8 +226,7 @@ static const struct opt3001_scale opt3002_scales[] = { static int opt3001_find_scale(const struct opt3001 *opt, int val, int val2, u8 *exponent) { - int i; - for (i = 0; i < ARRAY_SIZE(*opt->chip_info->scales); i++) { + for (unsigned int i = 0; i < ARRAY_SIZE(*opt->chip_info->scales); i++) { const struct opt3001_scale *scale = &(*opt->chip_info->scales)[i]; /* * Compare the integer and micro parts to determine value scale. From 1bff00e98c47d3d32c2df0781b14f7660ef58f31 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Sun, 14 Jun 2026 15:19:08 +0200 Subject: [PATCH 052/513] iio: light: opt3001: move driver to guard(mutex)() use Move driver to use guard(mutex)() macro, to facilitate automatic locking/unlocking of resources. This modernizes the driver and improves code style. While at it, remove unnecessary gotos and return variables. Reviewed-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt3001.c | 61 ++++++++++++------------------------- 1 file changed, 20 insertions(+), 41 deletions(-) diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index c301549ddaa7..b6026d7d0da4 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -479,7 +480,6 @@ static int opt3001_read_raw(struct iio_dev *iio, int *val, int *val2, long mask) { struct opt3001 *opt = iio_priv(iio); - int ret; if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS) return -EBUSY; @@ -487,23 +487,17 @@ static int opt3001_read_raw(struct iio_dev *iio, if (chan->type != opt->chip_info->chan_type) return -EINVAL; - mutex_lock(&opt->lock); + guard(mutex)(&opt->lock); switch (mask) { case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_PROCESSED: - ret = opt3001_get_processed(opt, val, val2); - break; + return opt3001_get_processed(opt, val, val2); case IIO_CHAN_INFO_INT_TIME: - ret = opt3001_get_int_time(opt, val, val2); - break; + return opt3001_get_int_time(opt, val, val2); default: - ret = -EINVAL; + return -EINVAL; } - - mutex_unlock(&opt->lock); - - return ret; } static int opt3001_write_raw(struct iio_dev *iio, @@ -511,7 +505,6 @@ static int opt3001_write_raw(struct iio_dev *iio, int val, int val2, long mask) { struct opt3001 *opt = iio_priv(iio); - int ret; if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS) return -EBUSY; @@ -525,11 +518,9 @@ static int opt3001_write_raw(struct iio_dev *iio, if (val != 0) return -EINVAL; - mutex_lock(&opt->lock); - ret = opt3001_set_int_time(opt, val2); - mutex_unlock(&opt->lock); + guard(mutex)(&opt->lock); - return ret; + return opt3001_set_int_time(opt, val2); } static int opt3001_read_event_value(struct iio_dev *iio, @@ -540,26 +531,21 @@ static int opt3001_read_event_value(struct iio_dev *iio, int *val, int *val2) { struct opt3001 *opt = iio_priv(iio); - int ret = IIO_VAL_INT_PLUS_MICRO; - mutex_lock(&opt->lock); + guard(mutex)(&opt->lock); switch (dir) { case IIO_EV_DIR_RISING: opt3001_to_iio_ret(opt, opt->high_thresh_exp, opt->high_thresh_mantissa, val, val2); - break; + return IIO_VAL_INT_PLUS_MICRO; case IIO_EV_DIR_FALLING: opt3001_to_iio_ret(opt, opt->low_thresh_exp, opt->low_thresh_mantissa, val, val2); - break; + return IIO_VAL_INT_PLUS_MICRO; default: - ret = -EINVAL; + return -EINVAL; } - - mutex_unlock(&opt->lock); - - return ret; } static int opt3001_write_event_value(struct iio_dev *iio, @@ -586,12 +572,12 @@ static int opt3001_write_event_value(struct iio_dev *iio, if (val < 0) return -EINVAL; - mutex_lock(&opt->lock); + guard(mutex)(&opt->lock); ret = opt3001_find_scale(opt, val, val2, &exponent); if (ret < 0) { dev_err(dev, "can't find scale for %d.%06u\n", val, val2); - goto err; + return ret; } whole = opt->chip_info->factor_whole; @@ -614,20 +600,16 @@ static int opt3001_write_event_value(struct iio_dev *iio, opt->low_thresh_exp = exponent; break; default: - ret = -EINVAL; - goto err; + return -EINVAL; } ret = i2c_smbus_write_word_swapped(client, reg, value); if (ret < 0) { dev_err(dev, "failed to write register %02x\n", reg); - goto err; + return ret; } -err: - mutex_unlock(&opt->lock); - - return ret; + return 0; } static int opt3001_read_event_config(struct iio_dev *iio, @@ -659,7 +641,7 @@ static int opt3001_write_event_config(struct iio_dev *iio, if (!state && opt->mode == OPT3001_CONFIGURATION_M_SHUTDOWN) return 0; - mutex_lock(&opt->lock); + guard(mutex)(&opt->lock); mode = state ? OPT3001_CONFIGURATION_M_CONTINUOUS : OPT3001_CONFIGURATION_M_SHUTDOWN; @@ -668,7 +650,7 @@ static int opt3001_write_event_config(struct iio_dev *iio, if (ret < 0) { dev_err(dev, "failed to read register %02x\n", OPT3001_CONFIGURATION); - goto err; + return ret; } reg = ret; @@ -678,13 +660,10 @@ static int opt3001_write_event_config(struct iio_dev *iio, if (ret < 0) { dev_err(dev, "failed to write register %02x\n", OPT3001_CONFIGURATION); - goto err; + return ret; } -err: - mutex_unlock(&opt->lock); - - return ret; + return 0; } static const struct iio_info opt3001_info = { From a4e80b835f4a0429649fac128665a218f372b4ab Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Sun, 14 Jun 2026 15:19:09 +0200 Subject: [PATCH 053/513] iio: light: opt3001: switch driver to managed resources Move the driver to use devm_* functions to automate resource management and simplify error handling. This also allows removal of the opt3001_remove() function. Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt3001.c | 88 ++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 50 deletions(-) diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index b6026d7d0da4..9594c9615dfb 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -705,6 +705,30 @@ static int opt3001_read_id(struct opt3001 *opt) return 0; } +static void opt3001_power_off(void *data) +{ + struct opt3001 *opt = data; + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; + u16 reg_val; + int ret; + + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); + if (ret < 0) { + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + return; + } + + reg_val = ret; + opt3001_set_mode(opt, ®_val, OPT3001_CONFIGURATION_M_SHUTDOWN); + + ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg_val); + if (ret < 0) + dev_err(dev, "failed to write to register %02x\n", + OPT3001_CONFIGURATION); +} + static int opt3001_configure(struct opt3001 *opt) { struct i2c_client *client = opt->client; @@ -743,6 +767,11 @@ static int opt3001_configure(struct opt3001 *opt) return dev_err_probe(dev, ret, "failed to write register %02x\n", OPT3001_CONFIGURATION); + ret = devm_add_action_or_reset(dev, opt3001_power_off, opt); + if (ret) + return dev_err_probe(dev, ret, + "failed to register power off function\n"); + ret = i2c_smbus_read_word_swapped(client, OPT3001_LOW_LIMIT); if (ret < 0) return dev_err_probe(dev, ret, "failed to read register %02x\n", @@ -836,9 +865,11 @@ static int opt3001_probe(struct i2c_client *client) opt->client = client; opt->chip_info = i2c_get_match_data(client); - mutex_init(&opt->lock); + ret = devm_mutex_init(dev, &opt->lock); + if (ret) + return ret; + init_waitqueue_head(&opt->result_ready_queue); - i2c_set_clientdata(client, iio); if (opt->chip_info->has_id) { ret = opt3001_read_id(opt); @@ -858,60 +889,18 @@ static int opt3001_probe(struct i2c_client *client) /* Make use of INT pin only if valid IRQ no. is given */ if (irq > 0) { - ret = request_threaded_irq(irq, NULL, opt3001_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "opt3001", iio); + ret = devm_request_threaded_irq(dev, irq, NULL, opt3001_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "opt3001", iio); if (ret) - return dev_err_probe(dev, ret, - "failed to request IRQ #%d\n", - irq); + return ret; opt->use_irq = true; } else { dev_dbg(dev, "enabling interrupt-less operation\n"); } - ret = iio_device_register(iio); - if (ret) - goto free_irq; - - return 0; - -free_irq: - if (irq > 0) - free_irq(irq, iio); - - return ret; -} - -static void opt3001_remove(struct i2c_client *client) -{ - struct iio_dev *iio = i2c_get_clientdata(client); - struct opt3001 *opt = iio_priv(iio); - struct device *dev = &client->dev; - int ret; - u16 reg; - - iio_device_unregister(iio); - - if (opt->use_irq) - free_irq(client->irq, iio); - - ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); - if (ret < 0) { - dev_err(dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); - return; - } - - reg = ret; - opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SHUTDOWN); - - ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); - if (ret < 0) { - dev_err(dev, "failed to write register %02x\n", - OPT3001_CONFIGURATION); - } + return devm_iio_device_register(dev, iio); } static const struct opt3001_chip_info opt3001_chip_information = { @@ -952,7 +941,6 @@ MODULE_DEVICE_TABLE(of, opt3001_of_match); static struct i2c_driver opt3001_driver = { .probe = opt3001_probe, - .remove = opt3001_remove, .id_table = opt3001_id, .driver = { From cf12f69291e21d3ca835d9556df246c836bb64d9 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Sun, 14 Jun 2026 15:19:10 +0200 Subject: [PATCH 054/513] iio: light: opt3001: add comment to mutex Add comment to mutex per checkpatch.pl report. While we're at it, add a comment to bool ok_to_ignore_lock. No functional change. Reviewed-by: Maxwell Doose Signed-off-by: Joshua Crofts Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt3001.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 9594c9615dfb..2bce6cd5f4e4 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -103,7 +103,13 @@ struct opt3001_chip_info { struct opt3001 { struct i2c_client *client; + /* + * Ensure data capture and read-modify-write sequences are + * not interrupted. + */ struct mutex lock; + + /* Allows for IRQs to bypass locking mechanism */ bool ok_to_ignore_lock; bool result_ready; wait_queue_head_t result_ready_queue; From e8de771d80a3a1ceb28d0fa98492acc10c251713 Mon Sep 17 00:00:00 2001 From: Miao Li Date: Sat, 20 Jun 2026 09:59:40 +0800 Subject: [PATCH 055/513] iio: accel: stk8ba50: Update includes to match IWYU Update the list of included headers in stk8ba50.c using Include-What-You-Use (IWYU) tool, mainly to remove kernel.h and add missing headers such as array_size.h, bitops.h, dev_printk.h, etc. Reviewed-by: Andy Shevchenko Signed-off-by: Miao Li Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/accel/stk8ba50.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index ccea1331cafc..4bacc007897a 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -7,12 +7,19 @@ * STK8BA50 7-bit I2C address: 0x18. */ +#include +#include +#include +#include #include #include -#include #include #include +#include +#include +#include #include + #include #include #include From d2a44111379c44011517795bf2b48aed79502fc8 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Tue, 16 Jun 2026 20:56:51 -0500 Subject: [PATCH 056/513] iio: accel: bmc150: clamp the device-reported FIFO frame count __bmc150_accel_fifo_flush() transfers the frame count the device reports in FIFO_STATUS into an on-stack buffer sized for BMC150_ACCEL_FIFO_LENGTH (32) samples, but the count is masked to 7 bits (0..127) and the optional caller budget does not bound the flush-all path. A device, or an attacker on the I2C/SPI bus, reporting up to 127 frames overflows the buffer by up to 570 bytes: a stack out-of-bounds write. Clamp the count to BMC150_ACCEL_FIFO_LENGTH before the transfer, mirroring the clamp already applied in bmc150_accel_set_watermark(). Conforming hardware reports at most that many frames and is unaffected. Signed-off-by: Bryam Vargas Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bmc150-accel-core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 2398eb7e12cd..ecaa4782d847 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -988,8 +988,10 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev, do_div(sample_period, count); tstamp = data->timestamp - (count - 1) * sample_period; - if (samples && count > samples) - count = samples; + if (samples) + count = min3(count, samples, BMC150_ACCEL_FIFO_LENGTH); + else + count = min(count, BMC150_ACCEL_FIFO_LENGTH); ret = bmc150_accel_fifo_transfer(data, (u8 *)buffer, count); if (ret) From 2f8225b91eb478ca2e9aec8aaef4d229765bcebe Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Tue, 16 Jun 2026 20:56:15 -0500 Subject: [PATCH 057/513] iio: accel: fxls8962af: clamp the device-reported FIFO sample count fxls8962af_fifo_flush() transfers the sample count the device reports in BUF_STATUS into an on-stack buffer sized for FXLS8962AF_FIFO_LENGTH (32) samples, but the count is a 6-bit field (0..63) that is only checked for zero. A device, or an attacker on the I2C/SPI bus, reporting 33..63 overflows the buffer by up to 186 bytes: a stack out-of-bounds write. Clamp the count to FXLS8962AF_FIFO_LENGTH before the transfer, mirroring the clamp already applied in fxls8962af_set_watermark(). Conforming hardware reports at most that many samples and is unaffected. Signed-off-by: Bryam Vargas Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/accel/fxls8962af-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c index 8763e91c63d2..1ecffbf41f64 100644 --- a/drivers/iio/accel/fxls8962af-core.c +++ b/drivers/iio/accel/fxls8962af-core.c @@ -970,6 +970,8 @@ static int fxls8962af_fifo_flush(struct iio_dev *indio_dev) if (!count) return 0; + count = min(count, FXLS8962AF_FIFO_LENGTH); + data->old_timestamp = data->timestamp; data->timestamp = iio_get_time_ns(indio_dev); From 7758cd492290af3ed3d21067de29d6ad36566719 Mon Sep 17 00:00:00 2001 From: Yash Suthar Date: Tue, 16 Jun 2026 17:16:21 +0530 Subject: [PATCH 058/513] iio: pressure: bmp280: return on runtime PM resume failure Replace pm_runtime_get_sync() with pm_runtime_resume_and_get() and propagate error. Signed-off-by: Yash Suthar Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 32 +++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 990340a9b10c..c89153dcc323 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -750,7 +750,10 @@ static int bmp280_read_raw(struct iio_dev *indio_dev, struct bmp280_data *data = iio_priv(indio_dev); int ret; - pm_runtime_get_sync(data->dev); + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; + ret = bmp280_read_raw_impl(indio_dev, chan, val, val2, mask); pm_runtime_put_autosuspend(data->dev); @@ -924,7 +927,10 @@ static int bmp280_write_raw(struct iio_dev *indio_dev, struct bmp280_data *data = iio_priv(indio_dev); int ret; - pm_runtime_get_sync(data->dev); + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; + ret = bmp280_write_raw_impl(indio_dev, chan, val, val2, mask); pm_runtime_put_autosuspend(data->dev); @@ -2254,7 +2260,10 @@ static int bmp580_nvmem_read(void *priv, unsigned int offset, void *val, struct bmp280_data *data = priv; int ret; - pm_runtime_get_sync(data->dev); + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; + ret = bmp580_nvmem_read_impl(priv, offset, val, bytes); pm_runtime_put_autosuspend(data->dev); @@ -2328,7 +2337,10 @@ static int bmp580_nvmem_write(void *priv, unsigned int offset, void *val, struct bmp280_data *data = priv; int ret; - pm_runtime_get_sync(data->dev); + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; + ret = bmp580_nvmem_write_impl(priv, offset, val, bytes); pm_runtime_put_autosuspend(data->dev); @@ -3108,11 +3120,17 @@ EXPORT_SYMBOL_NS(bmp085_chip_info, "IIO_BMP280"); static int bmp280_buffer_preenable(struct iio_dev *indio_dev) { struct bmp280_data *data = iio_priv(indio_dev); + int ret; - pm_runtime_get_sync(data->dev); - data->chip_info->set_mode(data, BMP280_NORMAL); + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; - return 0; + ret = data->chip_info->set_mode(data, BMP280_NORMAL); + if (ret) + pm_runtime_put_autosuspend(data->dev); + + return ret; } static int bmp280_buffer_postdisable(struct iio_dev *indio_dev) From 46d67896786c8a07e5ad6a9d6ace6cdd312ef158 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Tue, 16 Jun 2026 18:55:20 +0530 Subject: [PATCH 059/513] iio: hid-sensor-temperature: use u32 instead of unsigned int Prefer 'u32' instead of 'unsigned int' for usage_id variable. This matches expected callback API type and improves code clarity. No functional change. Signed-off-by: Sanjay Chitroda Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/hid-sensor-temperature.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c index 9f628a8e5cfb..a8d3a15f9c53 100644 --- a/drivers/iio/temperature/hid-sensor-temperature.c +++ b/drivers/iio/temperature/hid-sensor-temperature.c @@ -125,7 +125,7 @@ static const struct iio_info temperature_info = { /* Callback handler to send event after all samples are received and captured */ static int temperature_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, void *pdev) + u32 usage_id, void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct temperature_state *temp_st = iio_priv(indio_dev); @@ -140,7 +140,7 @@ static int temperature_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int temperature_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, size_t raw_len, + u32 usage_id, size_t raw_len, char *raw_data, void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); @@ -159,7 +159,7 @@ static int temperature_capture_sample(struct hid_sensor_hub_device *hsdev, static int temperature_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, struct iio_chan_spec *channels, - unsigned int usage_id, + u32 usage_id, struct temperature_state *st) { int ret; From dc0cbeb497b00ef1c1fc307fd7d9250893dc3f43 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Tue, 16 Jun 2026 18:55:21 +0530 Subject: [PATCH 060/513] iio: hid-sensor-humidity: use u32 instead of unsigned int Prefer 'u32' instead of 'unsigned int' for usage_id variable. This matches expected callback API type and improves code clarity. No functional change. Signed-off-by: Sanjay Chitroda Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hid-sensor-humidity.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/humidity/hid-sensor-humidity.c b/drivers/iio/humidity/hid-sensor-humidity.c index be2338d5f407..c376a247b137 100644 --- a/drivers/iio/humidity/hid-sensor-humidity.c +++ b/drivers/iio/humidity/hid-sensor-humidity.c @@ -127,7 +127,7 @@ static const struct iio_info humidity_info = { /* Callback handler to send event after all samples are received and captured */ static int humidity_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, void *pdev) + u32 usage_id, void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct hid_humidity_state *humid_st = iio_priv(indio_dev); @@ -141,7 +141,7 @@ static int humidity_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int humidity_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, size_t raw_len, + u32 usage_id, size_t raw_len, char *raw_data, void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); @@ -161,7 +161,7 @@ static int humidity_capture_sample(struct hid_sensor_hub_device *hsdev, static int humidity_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, struct iio_chan_spec *channels, - unsigned int usage_id, + u32 usage_id, struct hid_humidity_state *st) { int ret; From ef4c70122013c1e58b52a0d10bbca9a688be095a Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Tue, 16 Jun 2026 18:55:22 +0530 Subject: [PATCH 061/513] iio: hid-sensor-custom-intel-hinge: use u32 instead of unsigned int Prefer 'u32' instead of 'unsigned int' for usage_id variable. This matches expected callback API type and improves code clarity. No functional change. Signed-off-by: Sanjay Chitroda Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/position/hid-sensor-custom-intel-hinge.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/position/hid-sensor-custom-intel-hinge.c b/drivers/iio/position/hid-sensor-custom-intel-hinge.c index a26d391661fd..2139ddb670c4 100644 --- a/drivers/iio/position/hid-sensor-custom-intel-hinge.c +++ b/drivers/iio/position/hid-sensor-custom-intel-hinge.c @@ -190,7 +190,7 @@ static const struct iio_info hinge_info = { * and captured. */ static int hinge_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct hinge_state *st = iio_priv(indio_dev); @@ -209,7 +209,7 @@ static int hinge_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int hinge_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned int usage_id, size_t raw_len, + u32 usage_id, size_t raw_len, char *raw_data, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); @@ -236,7 +236,7 @@ static int hinge_capture_sample(struct hid_sensor_hub_device *hsdev, static int hinge_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, struct iio_chan_spec *channels, - unsigned int usage_id, struct hinge_state *st) + u32 usage_id, struct hinge_state *st) { int ret; int i; From 94ff11fb299fe16a8485eb5f067f66ae431f73b3 Mon Sep 17 00:00:00 2001 From: Siratul Islam Date: Fri, 19 Jun 2026 16:45:04 +0600 Subject: [PATCH 062/513] dt-bindings: add entry for qstcorp Add an entry for QST Corporation Limited Link: https://www.qstcorp.com/ Acked-by: Conor Dooley Signed-off-by: Siratul Islam Link: https://www.qstcorp.com/ Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 396044f368e7..f88e595f7077 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -1363,6 +1363,8 @@ patternProperties: description: Shenzhen QiShenglong Industrialist Co., Ltd. "^qnap,.*": description: QNAP Systems, Inc. + "^qstcorp,.*": + description: QST Corporation Limited "^quanta,.*": description: Quanta Computer Inc. "^radxa,.*": From adf3f9c39c2dfb51445b706f7d99786739593c7d Mon Sep 17 00:00:00 2001 From: Siratul Islam Date: Fri, 19 Jun 2026 16:45:05 +0600 Subject: [PATCH 063/513] dt-bindings: iio: magnetometer: add QST QMC5883L Sensor Add devicetree binding for the QST QMC5883L 3-Axis Magnetic Sensor connected via I2C. Used enum so that more devices could use this binding Reviewed-by: Conor Dooley Signed-off-by: Siratul Islam Signed-off-by: Jonathan Cameron --- .../iio/magnetometer/qstcorp,qmc5883l.yaml | 52 +++++++++++++++++++ MAINTAINERS | 6 +++ 2 files changed, 58 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml diff --git a/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml b/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml new file mode 100644 index 000000000000..38abd083a4fb --- /dev/null +++ b/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/magnetometer/qstcorp,qmc5883l.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: QST QMC5883L 3-Axis Magnetic Sensor + +maintainers: + - Siratul Islam + +description: | + QST QMC5883L 3-Axis Magnetic Sensor on I2C bus. + https://www.qstcorp.com/upload/pdf/202512/13-52-04%20QMC5883L%20Datasheet%20Rev.%20B.pdf + +properties: + compatible: + enum: + - qstcorp,qmc5883l + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vdd-supply: true + + vddio-supply: true + +additionalProperties: false + +required: + - compatible + - reg + - vdd-supply + - vddio-supply + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + magnetometer@d { + compatible = "qstcorp,qmc5883l"; + reg = <0x0d>; + vdd-supply = <&vdd_3v3_reg>; + vddio-supply = <&vdd_3v3_reg>; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 1132eb39436d..79213b4fbe92 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22047,6 +22047,12 @@ F: Documentation/networking/device_drivers/ethernet/freescale/dpaa2/overview.rst F: drivers/bus/fsl-mc/ F: include/uapi/linux/fsl_mc.h +QST QMC5883L 3-Axis Magnetic Sensor +M: Siratul Islam +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml + QT1010 MEDIA DRIVER L: linux-media@vger.kernel.org S: Orphan From e8349f9473423fdd357da33eec628d5ea9c555f5 Mon Sep 17 00:00:00 2001 From: Siratul Islam Date: Fri, 19 Jun 2026 16:45:06 +0600 Subject: [PATCH 064/513] iio: magnetometer: add driver for QST QMC5883L Sensor Add driver for the QST QMC5883L 3-Axis Magnetic Sensor connected via i2c. Signed-off-by: Siratul Islam Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + drivers/iio/magnetometer/Kconfig | 11 + drivers/iio/magnetometer/Makefile | 2 + drivers/iio/magnetometer/qmc5883l.c | 516 ++++++++++++++++++++++++++++ 4 files changed, 530 insertions(+) create mode 100644 drivers/iio/magnetometer/qmc5883l.c diff --git a/MAINTAINERS b/MAINTAINERS index 79213b4fbe92..0dde85039a14 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22052,6 +22052,7 @@ M: Siratul Islam L: linux-iio@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml +F: drivers/iio/magnetometer/qmc5883l.c QT1010 MEDIA DRIVER L: linux-media@vger.kernel.org diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index ea2697fb5ab6..2dee566ba703 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -209,6 +209,17 @@ config INFINEON_TLV493D To compile this driver as a module, choose M here: the module will be called tlv493d. +config QMC5883L + tristate "QST QMC5883L 3-Axis Magnetic Sensor" + depends on I2C + select REGMAP_I2C + help + Say Y here to add support driver for QST QMC5883L 3-Axis + Magnetic Sensor. + + To compile this driver as a module, choose M here: the + module will be called qmc5883l. + config SENSORS_HMC5843 tristate select IIO_BUFFER diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index 7fd9b3fd914e..b9478e6513e4 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -27,6 +27,8 @@ obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o obj-$(CONFIG_INFINEON_TLV493D) += tlv493d.o +obj-$(CONFIG_QMC5883L) += qmc5883l.o + obj-$(CONFIG_SENSORS_HMC5843) += hmc5843_core.o obj-$(CONFIG_SENSORS_HMC5843_I2C) += hmc5843_i2c.o obj-$(CONFIG_SENSORS_HMC5843_SPI) += hmc5843_spi.o diff --git a/drivers/iio/magnetometer/qmc5883l.c b/drivers/iio/magnetometer/qmc5883l.c new file mode 100644 index 000000000000..c3469715dc63 --- /dev/null +++ b/drivers/iio/magnetometer/qmc5883l.c @@ -0,0 +1,516 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* + * Support for QST QMC5883L 3-Axis Magnetic Sensor on I2C bus. + * + * Copyright (C) 2026 Siratul Islam + * + * Datasheet available at + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define QMC5883L_REG_X_LSB 0x00 +#define QMC5883L_REG_STATUS1 0x06 +#define QMC5883L_REG_CTRL1 0x09 +#define QMC5883L_REG_CTRL2 0x0A +#define QMC5883L_REG_SET_RESET 0x0B +#define QMC5883L_REG_ID 0x0D + +#define QMC5883L_CHIP_ID 0xFF + +#define QMC5883L_MODE_MASK GENMASK(1, 0) +#define QMC5883L_ODR_MASK GENMASK(3, 2) +#define QMC5883L_RNG_MASK GENMASK(5, 4) +#define QMC5883L_OSR_MASK GENMASK(7, 6) + +#define QMC5883L_MODE_STANDBY 0x00 +#define QMC5883L_MODE_CONT 0x01 + +#define QMC5883L_ODR_10HZ 0x00 +#define QMC5883L_ODR_50HZ 0x01 +#define QMC5883L_ODR_100HZ 0x02 +#define QMC5883L_ODR_200HZ 0x03 + +#define QMC5883L_RNG_2G 0x00 +#define QMC5883L_RNG_8G 0x01 + +#define QMC5883L_OSR_512 0x00 +#define QMC5883L_OSR_256 0x01 +#define QMC5883L_OSR_128 0x02 +#define QMC5883L_OSR_64 0x03 + +#define QMC5883L_STATUS_DRDY BIT(0) +#define QMC5883L_STATUS_OVL BIT(1) + +#define QMC5883L_SET_RESET_VAL BIT(0) +#define QMC5883L_INT_DISABLE BIT(0) +#define QMC5883L_SOFT_RESET BIT(7) + +#define QMC5883L_PORT_US 350 + +struct qmc5883l_data { + struct regmap *regmap; + /* + * Protect data->range/odr/osr. + * Protect poll and read during measurement. + */ + struct mutex mutex; + u8 range; + u8 odr; + u8 osr; +}; + +enum qmc5883l_chan { + QMC5883L_AXIS_X, + QMC5883L_AXIS_Y, + QMC5883L_AXIS_Z, +}; + +static const int qmc5883l_odr_avail[] = { 10, 50, 100, 200 }; + +static const int qmc5883l_osr_avail[] = { 512, 256, 128, 64 }; + +static const int qmc5883l_scales[][2] = { + [QMC5883L_RNG_2G] = { 0, 83333 }, + [QMC5883L_RNG_8G] = { 0, 333333 }, +}; + +static int qmc5883l_take_measurement(struct iio_dev *indio_dev, int index, + int *val) +{ + struct qmc5883l_data *data = iio_priv(indio_dev); + struct regmap *map = data->regmap; + unsigned int status; + __le16 buf[3]; + int ret; + + guard(mutex) (&data->mutex); + + /* 50ms headroom over the slowest ODR (10Hz) */ + ret = regmap_read_poll_timeout(map, QMC5883L_REG_STATUS1, + status, (status & QMC5883L_STATUS_DRDY), + 2 * USEC_PER_MSEC, 150 * USEC_PER_MSEC); + if (ret) + return ret; + + ret = regmap_bulk_read(map, QMC5883L_REG_X_LSB, buf, sizeof(buf)); + if (ret) + return ret; + + if (status & QMC5883L_STATUS_OVL) + return -ERANGE; + + *val = (s16)le16_to_cpu(buf[index]); + + return 0; +} + +static int qmc5883l_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct qmc5883l_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = qmc5883l_take_measurement(indio_dev, chan->address, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: { + guard(mutex)(&data->mutex); + + *val = qmc5883l_scales[data->range][0]; + *val2 = qmc5883l_scales[data->range][1]; + + return IIO_VAL_INT_PLUS_NANO; + } + case IIO_CHAN_INFO_SAMP_FREQ: { + guard(mutex)(&data->mutex); + + switch (data->odr) { + case QMC5883L_ODR_200HZ: + *val = 200; + break; + case QMC5883L_ODR_100HZ: + *val = 100; + break; + case QMC5883L_ODR_50HZ: + *val = 50; + break; + case QMC5883L_ODR_10HZ: + *val = 10; + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + guard(mutex)(&data->mutex); + + switch (data->osr) { + case QMC5883L_OSR_64: + *val = 64; + break; + case QMC5883L_OSR_128: + *val = 128; + break; + case QMC5883L_OSR_256: + *val = 256; + break; + case QMC5883L_OSR_512: + *val = 512; + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT; + } + default: + return -EINVAL; + } +} + +static int qmc5883l_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int val, int val2, long mask) +{ + struct qmc5883l_data *data = iio_priv(indio_dev); + u8 rng, osr, odr; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: { + if (val != 0) + return -EINVAL; + + if (val2 == qmc5883l_scales[QMC5883L_RNG_2G][1]) + rng = QMC5883L_RNG_2G; + else if (val2 == qmc5883l_scales[QMC5883L_RNG_8G][1]) + rng = QMC5883L_RNG_8G; + else + return -EINVAL; + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1, + QMC5883L_RNG_MASK, + FIELD_PREP(QMC5883L_RNG_MASK, rng)); + if (ret) + return ret; + + data->range = rng; + + return 0; + } + case IIO_CHAN_INFO_SAMP_FREQ: { + switch (val) { + case 200: + odr = QMC5883L_ODR_200HZ; + break; + case 100: + odr = QMC5883L_ODR_100HZ; + break; + case 50: + odr = QMC5883L_ODR_50HZ; + break; + case 10: + odr = QMC5883L_ODR_10HZ; + break; + default: + return -EINVAL; + } + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1, + QMC5883L_ODR_MASK, + FIELD_PREP(QMC5883L_ODR_MASK, odr)); + if (ret) + return ret; + + data->odr = odr; + + return 0; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + switch (val) { + case 64: + osr = QMC5883L_OSR_64; + break; + case 128: + osr = QMC5883L_OSR_128; + break; + case 256: + osr = QMC5883L_OSR_256; + break; + case 512: + osr = QMC5883L_OSR_512; + break; + default: + return -EINVAL; + } + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1, + QMC5883L_OSR_MASK, + FIELD_PREP(QMC5883L_OSR_MASK, osr)); + if (ret) + return ret; + + data->osr = osr; + + return 0; + } + default: + return -EINVAL; + } +} + +static int qmc5883l_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = qmc5883l_odr_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(qmc5883l_odr_avail); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = qmc5883l_osr_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(qmc5883l_osr_avail); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *vals = (const int *)qmc5883l_scales; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(qmc5883l_scales) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int qmc5883l_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; + } +} + +static const struct iio_info qmc5883l_info = { + .read_raw = qmc5883l_read_raw, + .write_raw = qmc5883l_write_raw, + .read_avail = qmc5883l_read_avail, + .write_raw_get_fmt = qmc5883l_write_raw_get_fmt, +}; + +static int qmc5883l_init(struct qmc5883l_data *data) +{ + struct regmap *map = data->regmap; + unsigned int reg; + int ret; + + ret = regmap_read(map, QMC5883L_REG_ID, ®); + if (ret) + return ret; + + /* Not failing because rev 1.0 had this register reserved */ + if (reg != QMC5883L_CHIP_ID) + dev_warn(regmap_get_device(map), + "Unknown chip id: 0x%02x, continuing\n", reg); + + ret = regmap_write(map, QMC5883L_REG_CTRL2, QMC5883L_SOFT_RESET); + if (ret) + return ret; + + /* Use POR completion time as a conservative bound */ + fsleep(QMC5883L_PORT_US); + + /* DRDY pin not used in this version of the driver */ + ret = regmap_write(map, QMC5883L_REG_CTRL2, QMC5883L_INT_DISABLE); + if (ret) + return ret; + + ret = regmap_write(map, QMC5883L_REG_SET_RESET, QMC5883L_SET_RESET_VAL); + if (ret) + return ret; + + data->odr = QMC5883L_ODR_50HZ; + data->range = QMC5883L_RNG_2G; + data->osr = QMC5883L_OSR_64; + + return regmap_write(map, QMC5883L_REG_CTRL1, + FIELD_PREP(QMC5883L_MODE_MASK, QMC5883L_MODE_CONT) | + FIELD_PREP(QMC5883L_ODR_MASK, data->odr) | + FIELD_PREP(QMC5883L_RNG_MASK, data->range) | + FIELD_PREP(QMC5883L_OSR_MASK, data->osr)); +} + +static void qmc5883l_power_down_action(void *priv) +{ + struct qmc5883l_data *data = priv; + + regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1, + QMC5883L_MODE_MASK, + FIELD_PREP(QMC5883L_MODE_MASK, QMC5883L_MODE_STANDBY)); +} + +static bool qmc5883l_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg <= QMC5883L_REG_STATUS1; +} + +static bool qmc5883l_writable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case QMC5883L_REG_CTRL1: + case QMC5883L_REG_CTRL2: + case QMC5883L_REG_SET_RESET: + return true; + default: + return false; + } +} + +static const struct regmap_config qmc5883l_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = QMC5883L_REG_ID, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = qmc5883l_volatile_reg, + .writeable_reg = qmc5883l_writable_reg, +}; + +#define QMC5883L_CHANNEL(_axis) \ + { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .address = QMC5883L_AXIS_##_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) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + } + +static const struct iio_chan_spec qmc5883l_channels[] = { + QMC5883L_CHANNEL(X), + QMC5883L_CHANNEL(Y), + QMC5883L_CHANNEL(Z), +}; + +static int qmc5883l_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct qmc5883l_data *data; + struct iio_dev *indio_dev; + struct regmap *map; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + map = devm_regmap_init_i2c(client, &qmc5883l_regmap_config); + if (IS_ERR(map)) + return dev_err_probe(dev, PTR_ERR(map), + "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"); + + ret = devm_regulator_get_enable(dev, "vddio"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable VDDIO regulator\n"); + + /* POR completion time max per datasheet Table 7 */ + fsleep(QMC5883L_PORT_US); + + data = iio_priv(indio_dev); + data->regmap = map; + + ret = devm_mutex_init(dev, &data->mutex); + if (ret) + return ret; + + indio_dev->name = "qmc5883l"; + indio_dev->info = &qmc5883l_info; + indio_dev->channels = qmc5883l_channels; + indio_dev->num_channels = ARRAY_SIZE(qmc5883l_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = qmc5883l_init(data); + if (ret) + return dev_err_probe(dev, ret, "qmc5883l init failed\n"); + + ret = devm_add_action_or_reset(dev, qmc5883l_power_down_action, data); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id qmc5883l_match[] = { + { .compatible = "qstcorp,qmc5883l" }, + { } +}; +MODULE_DEVICE_TABLE(of, qmc5883l_match); + +static const struct i2c_device_id qmc5883l_id[] = { + { .name = "qmc5883l" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, qmc5883l_id); + +static struct i2c_driver qmc5883l_driver = { + .driver = { + .name = "qmc5883l", + .of_match_table = qmc5883l_match, + }, + .id_table = qmc5883l_id, + .probe = qmc5883l_probe, +}; +module_i2c_driver(qmc5883l_driver); + +MODULE_DESCRIPTION("QST QMC5883L 3-Axis Magnetic Sensor driver"); +MODULE_AUTHOR("Siratul Islam "); +MODULE_LICENSE("Dual BSD/GPL"); From d60d6314519ec62ffe8691d033769c07999de84a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 19 Jun 2026 17:54:29 +0200 Subject: [PATCH 065/513] iio: Initialize i2c_device_id arrays using member names (part 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a follow-up for commit f68afce8e8a7 ("iio: Initialize i2c_device_id arrays using member names"). This previous commit missed these two instances because these drivers didn't exist yet in the tree where I prepared the respective patch. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/adc/rtq6056.c | 4 ++-- drivers/iio/magnetometer/mmc5983.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c index e2b1da13c0d3..ae50fb27bac9 100644 --- a/drivers/iio/adc/rtq6056.c +++ b/drivers/iio/adc/rtq6056.c @@ -872,8 +872,8 @@ static const struct richtek_dev_data rtq6059_devdata = { }; static const struct i2c_device_id rtq6056_id[] = { - { "rtq6056", (kernel_ulong_t)&rtq6056_devdata }, - { "rtq6059", (kernel_ulong_t)&rtq6059_devdata }, + { .name = "rtq6056", .driver_data = (kernel_ulong_t)&rtq6056_devdata }, + { .name = "rtq6059", .driver_data = (kernel_ulong_t)&rtq6059_devdata }, { } }; MODULE_DEVICE_TABLE(i2c, rtq6056_id); diff --git a/drivers/iio/magnetometer/mmc5983.c b/drivers/iio/magnetometer/mmc5983.c index a67b13393b6b..18f3f3d25c4e 100644 --- a/drivers/iio/magnetometer/mmc5983.c +++ b/drivers/iio/magnetometer/mmc5983.c @@ -328,7 +328,7 @@ static const struct of_device_id mmc5983_of_match[] = { MODULE_DEVICE_TABLE(of, mmc5983_of_match); static const struct i2c_device_id mmc5983_id[] = { - { "mmc5983" }, + { .name = "mmc5983" }, { } }; MODULE_DEVICE_TABLE(i2c, mmc5983_id); From d04a09b41d2e2d7c2063cf8f2addc1c6dd08de4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 19 Jun 2026 17:54:32 +0200 Subject: [PATCH 066/513] iio: adc: max1241: Simplify device abstraction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver was introduced in 2020 and since then only supports a single chip variant and .driver_data was unused from the start. Drop the assignment for .driver_data and hardcode the device name instead of doing a lookup in a table with only one entry. While touching that array, make use of a named initializer. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1241.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/max1241.c b/drivers/iio/adc/max1241.c index d62c1a011659..fb7f53316e3d 100644 --- a/drivers/iio/adc/max1241.c +++ b/drivers/iio/adc/max1241.c @@ -15,10 +15,6 @@ #define MAX1241_VAL_MASK GENMASK(11, 0) #define MAX1241_SHUTDOWN_DELAY_USEC 4 -enum max1241_id { - max1241, -}; - struct max1241 { struct spi_device *spi; struct mutex lock; @@ -166,7 +162,7 @@ static int max1241_probe(struct spi_device *spi) else dev_dbg(dev, "no shutdown pin passed, low-power mode disabled"); - indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->name = "max1241"; indio_dev->info = &max1241_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = max1241_channels; @@ -176,7 +172,7 @@ static int max1241_probe(struct spi_device *spi) } static const struct spi_device_id max1241_id[] = { - { "max1241", max1241 }, + { .name = "max1241" }, { } }; From 81c0bbfa510337b46b73e1bc734da7fa35d3f6fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 19 Jun 2026 17:54:33 +0200 Subject: [PATCH 067/513] iio: dac: max5522: Simplify device abstraction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver only supports a single chip variant since it's birth in 2022. Both the spi id_table and the of id_table are essentially unused (only assigned to a write-only member in private data). Hardcode the device name and then drop various unused stuff from the driver. While touching the spi id_table assign .name using a named initializer. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/dac/max5522.c | 31 +++---------------------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/drivers/iio/dac/max5522.c b/drivers/iio/dac/max5522.c index b52a9cc1da79..cfbe3a6e0449 100644 --- a/drivers/iio/dac/max5522.c +++ b/drivers/iio/dac/max5522.c @@ -25,15 +25,8 @@ #define MAX5522_REG_DATA(x) ((x) + MAX5522_CTRL_LOAD_IN_A) -struct max5522_chip_info { - const char *name; - const struct iio_chan_spec *channels; - unsigned int num_channels; -}; - struct max5522_state { struct regmap *regmap; - const struct max5522_chip_info *chip_info; unsigned short dac_cache[2]; int vref_mV; }; @@ -58,18 +51,6 @@ static const struct iio_chan_spec max5522_channels[] = { MAX5522_CHANNEL(1), }; -enum max5522_type { - ID_MAX5522, -}; - -static const struct max5522_chip_info max5522_chip_info_tbl[] = { - [ID_MAX5522] = { - .name = "max5522", - .channels = max5522_channels, - .num_channels = 2, - }, -}; - static inline int max5522_info_to_reg(struct iio_chan_spec const *chan) { return MAX5522_REG_DATA(chan->channel); @@ -140,9 +121,6 @@ static int max5522_spi_probe(struct spi_device *spi) } state = iio_priv(indio_dev); - state->chip_info = spi_get_device_match_data(spi); - if (!state->chip_info) - return -EINVAL; ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vrefin"); if (ret < 0) @@ -159,22 +137,19 @@ static int max5522_spi_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = max5522_channels; indio_dev->num_channels = ARRAY_SIZE(max5522_channels); - indio_dev->name = max5522_chip_info_tbl[ID_MAX5522].name; + indio_dev->name = "max5522"; return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id max5522_ids[] = { - { "max5522", (kernel_ulong_t)&max5522_chip_info_tbl[ID_MAX5522] }, + { .name = "max5522" }, { } }; MODULE_DEVICE_TABLE(spi, max5522_ids); static const struct of_device_id max5522_of_match[] = { - { - .compatible = "maxim,max5522", - .data = &max5522_chip_info_tbl[ID_MAX5522], - }, + { .compatible = "maxim,max5522" }, { } }; MODULE_DEVICE_TABLE(of, max5522_of_match); From 09bfa6acb14eb011a1177602ee0dcac25316b0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 19 Jun 2026 17:54:34 +0200 Subject: [PATCH 068/513] iio: frequency: ad9523: Simplify driver a bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spi driver data is only set but never used, so the assignment can be dropped. Also the spi id_table's .driver_data is unused and can be dropped. While touching the spi id_table modify it to use a named initializer. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/ad9523.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c index ea4d2763564a..1e393f40a8c8 100644 --- a/drivers/iio/frequency/ad9523.c +++ b/drivers/iio/frequency/ad9523.c @@ -990,7 +990,6 @@ static int ad9523_probe(struct spi_device *spi) if (IS_ERR(st->sync_gpio)) return PTR_ERR(st->sync_gpio); - spi_set_drvdata(spi, indio_dev); st->spi = spi; st->pdata = pdata; @@ -1009,7 +1008,7 @@ static int ad9523_probe(struct spi_device *spi) } static const struct spi_device_id ad9523_id[] = { - {"ad9523-1", 9523}, + { .name = "ad9523-1" }, { } }; MODULE_DEVICE_TABLE(spi, ad9523_id); From 9b6229006fa66fa96c80b7fa163911b1a80be939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 19 Jun 2026 17:54:36 +0200 Subject: [PATCH 069/513] iio: magnetometer: hmc5843: Simplify device abstraction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver supports a single chip variant only. Simplify the driver by hard-coding the device properties instead of using the id_table's abstraction for a single chip type and a lookup in a table with only one entry. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/hmc5843_spi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/magnetometer/hmc5843_spi.c b/drivers/iio/magnetometer/hmc5843_spi.c index 6a55c1559b0d..29dee5dce2ae 100644 --- a/drivers/iio/magnetometer/hmc5843_spi.c +++ b/drivers/iio/magnetometer/hmc5843_spi.c @@ -56,7 +56,6 @@ static int hmc5843_spi_probe(struct spi_device *spi) { int ret; struct regmap *regmap; - const struct spi_device_id *id = spi_get_device_id(spi); spi->mode = SPI_MODE_3; spi->max_speed_hz = 8000000; @@ -69,8 +68,7 @@ static int hmc5843_spi_probe(struct spi_device *spi) return PTR_ERR(regmap); return hmc5843_common_probe(&spi->dev, - regmap, - id->driver_data, id->name); + regmap, HMC5983_ID, "hmc5983"); } static void hmc5843_spi_remove(struct spi_device *spi) @@ -79,7 +77,7 @@ static void hmc5843_spi_remove(struct spi_device *spi) } static const struct spi_device_id hmc5843_id[] = { - { "hmc5983", HMC5983_ID }, + { .name = "hmc5983" }, { } }; MODULE_DEVICE_TABLE(spi, hmc5843_id); From 15568c60830e00f04188c2666d4f58fcff3247d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 19 Jun 2026 17:54:37 +0200 Subject: [PATCH 070/513] iio: accel: bmc150: Explicitly set spi .driver_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is one id entry that has an explicit assignment to .driver_data. To make the intention clearer, assign BOSCH_UNKNOWN (which is also 0) for all previously ids that had .driver_data = 0 implicitly before. While touching all entries in this array, convert to named initializers. This change is similar to commit e50856dc41e8 ("iio: accel: bmc150: Explicitly set .driver_data") but cares for the driver's spi part instead of i2c. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bmc150-accel-spi.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/iio/accel/bmc150-accel-spi.c b/drivers/iio/accel/bmc150-accel-spi.c index 26ce50b37716..6488d4a91c08 100644 --- a/drivers/iio/accel/bmc150-accel-spi.c +++ b/drivers/iio/accel/bmc150-accel-spi.c @@ -53,15 +53,15 @@ static const struct acpi_device_id bmc150_accel_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match); static const struct spi_device_id bmc150_accel_id[] = { - {"bma222"}, - {"bma222e"}, - {"bma250e"}, - {"bma253"}, - {"bma255"}, - {"bma280"}, - {"bmc150_accel"}, - {"bmc156_accel", BOSCH_BMC156}, - {"bmi055_accel"}, + { .name = "bma222", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma222e", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma250e", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma253", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma255", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma280", .driver_data = BOSCH_UNKNOWN }, + { .name = "bmc150_accel", .driver_data = BOSCH_UNKNOWN }, + { .name = "bmc156_accel", .driver_data = BOSCH_BMC156 }, + { .name = "bmi055_accel", .driver_data = BOSCH_UNKNOWN }, { } }; MODULE_DEVICE_TABLE(spi, bmc150_accel_id); From d7ed041207a7aa243e4f68e892d80e0d660d22b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 19 Jun 2026 17:54:38 +0200 Subject: [PATCH 071/513] iio: Drop unused assignment of spi_device_id driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drivers explicitly set the .driver_data member of struct spi_device_id to zero without relying on that value. Drop these unused assignments. While touching these arrays unify spacing and use named initializers for .name. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl367_spi.c | 2 +- drivers/iio/accel/bma220_spi.c | 2 +- drivers/iio/accel/bma400_spi.c | 2 +- drivers/iio/accel/kxsd9-spi.c | 2 +- drivers/iio/accel/mma7455_spi.c | 4 ++-- drivers/iio/adc/ad7266.c | 4 ++-- drivers/iio/adc/ad7280a.c | 2 +- drivers/iio/adc/ad7292.c | 2 +- drivers/iio/adc/ad7298.c | 2 +- drivers/iio/adc/ade9000.c | 2 +- drivers/iio/adc/hi8435.c | 2 +- drivers/iio/adc/ti-adc084s021.c | 2 +- drivers/iio/adc/ti-adc108s102.c | 2 +- drivers/iio/amplifiers/ada4250.c | 2 +- drivers/iio/chemical/bme680_spi.c | 2 +- drivers/iio/dac/ad5758.c | 2 +- drivers/iio/dac/ad5770r.c | 2 +- drivers/iio/dac/ad7293.c | 2 +- drivers/iio/dac/ad7303.c | 2 +- drivers/iio/filter/admv8818.c | 2 +- drivers/iio/frequency/admv1013.c | 2 +- drivers/iio/frequency/admv1014.c | 2 +- drivers/iio/frequency/adrf6780.c | 2 +- drivers/iio/gyro/bmg160_spi.c | 7 +++---- drivers/iio/gyro/fxas21002c_spi.c | 2 +- drivers/iio/health/afe4403.c | 2 +- drivers/iio/imu/adis16460.c | 2 +- drivers/iio/imu/bmi160/bmi160_spi.c | 4 ++-- drivers/iio/imu/fxos8700_spi.c | 2 +- drivers/iio/magnetometer/bmc150_magn_spi.c | 6 +++--- drivers/iio/pressure/mpl115_spi.c | 2 +- drivers/iio/pressure/zpa2326_spi.c | 2 +- drivers/iio/proximity/as3935.c | 2 +- drivers/iio/temperature/max31856.c | 2 +- drivers/iio/temperature/max31865.c | 2 +- 35 files changed, 42 insertions(+), 43 deletions(-) diff --git a/drivers/iio/accel/adxl367_spi.c b/drivers/iio/accel/adxl367_spi.c index 3fed56bb9054..e3ac44e68326 100644 --- a/drivers/iio/accel/adxl367_spi.c +++ b/drivers/iio/accel/adxl367_spi.c @@ -138,7 +138,7 @@ static int adxl367_spi_probe(struct spi_device *spi) } static const struct spi_device_id adxl367_spi_id[] = { - { "adxl367", 0 }, + { .name = "adxl367" }, { } }; MODULE_DEVICE_TABLE(spi, adxl367_spi_id); diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c index 383ee8a135ee..84a43842841c 100644 --- a/drivers/iio/accel/bma220_spi.c +++ b/drivers/iio/accel/bma220_spi.c @@ -26,7 +26,7 @@ static int bma220_spi_probe(struct spi_device *spi) } static const struct spi_device_id bma220_spi_id[] = { - { "bma220", 0 }, + { .name = "bma220" }, { } }; diff --git a/drivers/iio/accel/bma400_spi.c b/drivers/iio/accel/bma400_spi.c index d386f643515b..14815086fa42 100644 --- a/drivers/iio/accel/bma400_spi.c +++ b/drivers/iio/accel/bma400_spi.c @@ -88,7 +88,7 @@ static int bma400_spi_probe(struct spi_device *spi) } static const struct spi_device_id bma400_spi_ids[] = { - { "bma400", 0 }, + { .name = "bma400" }, { } }; MODULE_DEVICE_TABLE(spi, bma400_spi_ids); diff --git a/drivers/iio/accel/kxsd9-spi.c b/drivers/iio/accel/kxsd9-spi.c index cbb6c6412665..45257fbdbf8e 100644 --- a/drivers/iio/accel/kxsd9-spi.c +++ b/drivers/iio/accel/kxsd9-spi.c @@ -37,7 +37,7 @@ static void kxsd9_spi_remove(struct spi_device *spi) } static const struct spi_device_id kxsd9_spi_id[] = { - {"kxsd9", 0}, + { .name = "kxsd9" }, { } }; MODULE_DEVICE_TABLE(spi, kxsd9_spi_id); diff --git a/drivers/iio/accel/mma7455_spi.c b/drivers/iio/accel/mma7455_spi.c index aca02e83f789..872c47acf882 100644 --- a/drivers/iio/accel/mma7455_spi.c +++ b/drivers/iio/accel/mma7455_spi.c @@ -28,8 +28,8 @@ static void mma7455_spi_remove(struct spi_device *spi) } static const struct spi_device_id mma7455_spi_ids[] = { - { "mma7455", 0 }, - { "mma7456", 0 }, + { .name = "mma7455" }, + { .name = "mma7456" }, { } }; MODULE_DEVICE_TABLE(spi, mma7455_spi_ids); diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index 0ef36c249ab8..594a070c3745 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -453,8 +453,8 @@ static int ad7266_probe(struct spi_device *spi) } static const struct spi_device_id ad7266_id[] = { - { "ad7265", 0 }, - { "ad7266", 0 }, + { .name = "ad7265" }, + { .name = "ad7266" }, { } }; MODULE_DEVICE_TABLE(spi, ad7266_id); diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c index 01c2f55a680c..442e5f54d472 100644 --- a/drivers/iio/adc/ad7280a.c +++ b/drivers/iio/adc/ad7280a.c @@ -1083,7 +1083,7 @@ static int ad7280_probe(struct spi_device *spi) } static const struct spi_device_id ad7280_id[] = { - { "ad7280a", 0 }, + { .name = "ad7280a" }, { } }; MODULE_DEVICE_TABLE(spi, ad7280_id); diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c index e5ad83d2240a..0334f2b94aa4 100644 --- a/drivers/iio/adc/ad7292.c +++ b/drivers/iio/adc/ad7292.c @@ -300,7 +300,7 @@ static int ad7292_probe(struct spi_device *spi) } static const struct spi_device_id ad7292_id_table[] = { - { "ad7292", 0 }, + { .name = "ad7292" }, { } }; MODULE_DEVICE_TABLE(spi, ad7292_id_table); diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c index 7c0538ea15c8..27d406461144 100644 --- a/drivers/iio/adc/ad7298.c +++ b/drivers/iio/adc/ad7298.c @@ -354,7 +354,7 @@ static const struct acpi_device_id ad7298_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, ad7298_acpi_ids); static const struct spi_device_id ad7298_id[] = { - { "ad7298", 0 }, + { .name = "ad7298" }, { } }; MODULE_DEVICE_TABLE(spi, ad7298_id); diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index c0a9babfa519..b80cdd8ad982 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -1768,7 +1768,7 @@ static int ade9000_probe(struct spi_device *spi) }; static const struct spi_device_id ade9000_id[] = { - { "ade9000", 0 }, + { .name = "ade9000" }, { } }; MODULE_DEVICE_TABLE(spi, ade9000_id); diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c index 86c10ea7ded4..5b14ecec8006 100644 --- a/drivers/iio/adc/hi8435.c +++ b/drivers/iio/adc/hi8435.c @@ -527,7 +527,7 @@ static const struct of_device_id hi8435_dt_ids[] = { MODULE_DEVICE_TABLE(of, hi8435_dt_ids); static const struct spi_device_id hi8435_id[] = { - { "hi8435", 0 }, + { .name = "hi8435" }, { } }; MODULE_DEVICE_TABLE(spi, hi8435_id); diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c index a100f770fa1c..a4f82a629998 100644 --- a/drivers/iio/adc/ti-adc084s021.c +++ b/drivers/iio/adc/ti-adc084s021.c @@ -244,7 +244,7 @@ static const struct of_device_id adc084s021_of_match[] = { MODULE_DEVICE_TABLE(of, adc084s021_of_match); static const struct spi_device_id adc084s021_id[] = { - { ADC084S021_DRIVER_NAME, 0 }, + { .name = ADC084S021_DRIVER_NAME }, { } }; MODULE_DEVICE_TABLE(spi, adc084s021_id); diff --git a/drivers/iio/adc/ti-adc108s102.c b/drivers/iio/adc/ti-adc108s102.c index 7d615e2bbf39..81e25458ca6a 100644 --- a/drivers/iio/adc/ti-adc108s102.c +++ b/drivers/iio/adc/ti-adc108s102.c @@ -277,7 +277,7 @@ static const struct acpi_device_id adc108s102_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, adc108s102_acpi_ids); static const struct spi_device_id adc108s102_id[] = { - { "adc108s102", 0 }, + { .name = "adc108s102" }, { } }; MODULE_DEVICE_TABLE(spi, adc108s102_id); diff --git a/drivers/iio/amplifiers/ada4250.c b/drivers/iio/amplifiers/ada4250.c index 71e361af2074..9702de3a5074 100644 --- a/drivers/iio/amplifiers/ada4250.c +++ b/drivers/iio/amplifiers/ada4250.c @@ -358,7 +358,7 @@ static int ada4250_probe(struct spi_device *spi) } static const struct spi_device_id ada4250_id[] = { - { "ada4250", 0 }, + { .name = "ada4250" }, { } }; MODULE_DEVICE_TABLE(spi, ada4250_id); diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c index aa97645ba539..5fbabcb1d4b3 100644 --- a/drivers/iio/chemical/bme680_spi.c +++ b/drivers/iio/chemical/bme680_spi.c @@ -131,7 +131,7 @@ static int bme680_spi_probe(struct spi_device *spi) } static const struct spi_device_id bme680_spi_id[] = { - {"bme680", 0}, + { .name = "bme680" }, { } }; MODULE_DEVICE_TABLE(spi, bme680_spi_id); diff --git a/drivers/iio/dac/ad5758.c b/drivers/iio/dac/ad5758.c index 8e6fb46cce4d..632b60a8ed26 100644 --- a/drivers/iio/dac/ad5758.c +++ b/drivers/iio/dac/ad5758.c @@ -879,7 +879,7 @@ static int ad5758_probe(struct spi_device *spi) } static const struct spi_device_id ad5758_id[] = { - { "ad5758", 0 }, + { .name = "ad5758" }, { } }; MODULE_DEVICE_TABLE(spi, ad5758_id); diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c index 6027e8d88b27..bdc48e57013f 100644 --- a/drivers/iio/dac/ad5770r.c +++ b/drivers/iio/dac/ad5770r.c @@ -642,7 +642,7 @@ static const struct of_device_id ad5770r_of_id[] = { MODULE_DEVICE_TABLE(of, ad5770r_of_id); static const struct spi_device_id ad5770r_id[] = { - { "ad5770r", 0 }, + { .name = "ad5770r" }, { } }; MODULE_DEVICE_TABLE(spi, ad5770r_id); diff --git a/drivers/iio/dac/ad7293.c b/drivers/iio/dac/ad7293.c index df6f126abf05..caa5f45859b6 100644 --- a/drivers/iio/dac/ad7293.c +++ b/drivers/iio/dac/ad7293.c @@ -870,7 +870,7 @@ static int ad7293_probe(struct spi_device *spi) } static const struct spi_device_id ad7293_id[] = { - { "ad7293", 0 }, + { .name = "ad7293" }, { } }; MODULE_DEVICE_TABLE(spi, ad7293_id); diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c index 1c2960fa9743..e9c1cafad478 100644 --- a/drivers/iio/dac/ad7303.c +++ b/drivers/iio/dac/ad7303.c @@ -271,7 +271,7 @@ static const struct of_device_id ad7303_spi_of_match[] = { MODULE_DEVICE_TABLE(of, ad7303_spi_of_match); static const struct spi_device_id ad7303_spi_ids[] = { - { "ad7303", 0 }, + { .name = "ad7303" }, { } }; MODULE_DEVICE_TABLE(spi, ad7303_spi_ids); diff --git a/drivers/iio/filter/admv8818.c b/drivers/iio/filter/admv8818.c index a4984b867248..c188243269d4 100644 --- a/drivers/iio/filter/admv8818.c +++ b/drivers/iio/filter/admv8818.c @@ -790,7 +790,7 @@ static int admv8818_probe(struct spi_device *spi) } static const struct spi_device_id admv8818_id[] = { - { "admv8818", 0 }, + { .name = "admv8818" }, { } }; MODULE_DEVICE_TABLE(spi, admv8818_id); diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c index b852378b3f68..ba843324a678 100644 --- a/drivers/iio/frequency/admv1013.c +++ b/drivers/iio/frequency/admv1013.c @@ -620,7 +620,7 @@ static int admv1013_probe(struct spi_device *spi) } static const struct spi_device_id admv1013_id[] = { - { "admv1013", 0 }, + { .name = "admv1013" }, { } }; MODULE_DEVICE_TABLE(spi, admv1013_id); diff --git a/drivers/iio/frequency/admv1014.c b/drivers/iio/frequency/admv1014.c index 25e8cd8135ad..f4ddc5b3f481 100644 --- a/drivers/iio/frequency/admv1014.c +++ b/drivers/iio/frequency/admv1014.c @@ -782,7 +782,7 @@ static int admv1014_probe(struct spi_device *spi) } static const struct spi_device_id admv1014_id[] = { - { "admv1014", 0 }, + { .name = "admv1014" }, { } }; MODULE_DEVICE_TABLE(spi, admv1014_id); diff --git a/drivers/iio/frequency/adrf6780.c b/drivers/iio/frequency/adrf6780.c index 9911b5273b22..baf6f9f1faa3 100644 --- a/drivers/iio/frequency/adrf6780.c +++ b/drivers/iio/frequency/adrf6780.c @@ -487,7 +487,7 @@ static int adrf6780_probe(struct spi_device *spi) } static const struct spi_device_id adrf6780_id[] = { - { "adrf6780", 0 }, + { .name = "adrf6780" }, { } }; MODULE_DEVICE_TABLE(spi, adrf6780_id); diff --git a/drivers/iio/gyro/bmg160_spi.c b/drivers/iio/gyro/bmg160_spi.c index 6aecc5eb8347..14ae1d03be3d 100644 --- a/drivers/iio/gyro/bmg160_spi.c +++ b/drivers/iio/gyro/bmg160_spi.c @@ -33,12 +33,11 @@ static void bmg160_spi_remove(struct spi_device *spi) } static const struct spi_device_id bmg160_spi_id[] = { - {"bmg160", 0}, - {"bmi055_gyro", 0}, - {"bmi088_gyro", 0}, + { .name = "bmg160" }, + { .name = "bmi055_gyro" }, + { .name = "bmi088_gyro" }, { } }; - MODULE_DEVICE_TABLE(spi, bmg160_spi_id); static const struct of_device_id bmg160_of_match[] = { diff --git a/drivers/iio/gyro/fxas21002c_spi.c b/drivers/iio/gyro/fxas21002c_spi.c index d62efe50b697..000528d319a8 100644 --- a/drivers/iio/gyro/fxas21002c_spi.c +++ b/drivers/iio/gyro/fxas21002c_spi.c @@ -40,7 +40,7 @@ static void fxas21002c_spi_remove(struct spi_device *spi) } static const struct spi_device_id fxas21002c_spi_id[] = { - { "fxas21002c", 0 }, + { .name = "fxas21002c" }, { } }; MODULE_DEVICE_TABLE(spi, fxas21002c_spi_id); diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index d358f4d5e5da..27e32707f089 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -568,7 +568,7 @@ static int afe4403_probe(struct spi_device *spi) } static const struct spi_device_id afe4403_ids[] = { - { "afe4403", 0 }, + { .name = "afe4403" }, { } }; MODULE_DEVICE_TABLE(spi, afe4403_ids); diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c index ba1887d36577..cadf21c05bbf 100644 --- a/drivers/iio/imu/adis16460.c +++ b/drivers/iio/imu/adis16460.c @@ -394,7 +394,7 @@ static int adis16460_probe(struct spi_device *spi) } static const struct spi_device_id adis16460_ids[] = { - { "adis16460", 0 }, + { .name = "adis16460" }, { } }; MODULE_DEVICE_TABLE(spi, adis16460_ids); diff --git a/drivers/iio/imu/bmi160/bmi160_spi.c b/drivers/iio/imu/bmi160/bmi160_spi.c index 3581bd788483..42ad6fcdd1c2 100644 --- a/drivers/iio/imu/bmi160/bmi160_spi.c +++ b/drivers/iio/imu/bmi160/bmi160_spi.c @@ -35,8 +35,8 @@ static int bmi160_spi_probe(struct spi_device *spi) } static const struct spi_device_id bmi160_spi_id[] = { - {"bmi120", 0}, - {"bmi160", 0}, + { .name = "bmi120" }, + { .name = "bmi160" }, { } }; MODULE_DEVICE_TABLE(spi, bmi160_spi_id); diff --git a/drivers/iio/imu/fxos8700_spi.c b/drivers/iio/imu/fxos8700_spi.c index 6b0dc7a776b9..c85af547da49 100644 --- a/drivers/iio/imu/fxos8700_spi.c +++ b/drivers/iio/imu/fxos8700_spi.c @@ -24,7 +24,7 @@ static int fxos8700_spi_probe(struct spi_device *spi) } static const struct spi_device_id fxos8700_spi_id[] = { - {"fxos8700", 0}, + { .name = "fxos8700" }, { } }; MODULE_DEVICE_TABLE(spi, fxos8700_spi_id); diff --git a/drivers/iio/magnetometer/bmc150_magn_spi.c b/drivers/iio/magnetometer/bmc150_magn_spi.c index 896b1d280731..37acaf9650ee 100644 --- a/drivers/iio/magnetometer/bmc150_magn_spi.c +++ b/drivers/iio/magnetometer/bmc150_magn_spi.c @@ -34,9 +34,9 @@ static void bmc150_magn_spi_remove(struct spi_device *spi) } static const struct spi_device_id bmc150_magn_spi_id[] = { - {"bmc150_magn", 0}, - {"bmc156_magn", 0}, - {"bmm150_magn", 0}, + { .name = "bmc150_magn" }, + { .name = "bmc156_magn" }, + { .name = "bmm150_magn" }, { } }; MODULE_DEVICE_TABLE(spi, bmc150_magn_spi_id); diff --git a/drivers/iio/pressure/mpl115_spi.c b/drivers/iio/pressure/mpl115_spi.c index 4e1d24beff94..3cff616e8d2f 100644 --- a/drivers/iio/pressure/mpl115_spi.c +++ b/drivers/iio/pressure/mpl115_spi.c @@ -84,7 +84,7 @@ static int mpl115_spi_probe(struct spi_device *spi) } static const struct spi_device_id mpl115_spi_ids[] = { - { "mpl115", 0 }, + { .name = "mpl115" }, { } }; MODULE_DEVICE_TABLE(spi, mpl115_spi_ids); diff --git a/drivers/iio/pressure/zpa2326_spi.c b/drivers/iio/pressure/zpa2326_spi.c index af756e2b0f31..5dd0fa87c67d 100644 --- a/drivers/iio/pressure/zpa2326_spi.c +++ b/drivers/iio/pressure/zpa2326_spi.c @@ -61,7 +61,7 @@ static void zpa2326_remove_spi(struct spi_device *spi) } static const struct spi_device_id zpa2326_spi_ids[] = { - { "zpa2326", 0 }, + { .name = "zpa2326" }, { } }; MODULE_DEVICE_TABLE(spi, zpa2326_spi_ids); diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index f1018b14aecf..40392db09bf9 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -449,7 +449,7 @@ static const struct of_device_id as3935_of_match[] = { MODULE_DEVICE_TABLE(of, as3935_of_match); static const struct spi_device_id as3935_id[] = { - {"as3935", 0}, + { .name = "as3935" }, { } }; MODULE_DEVICE_TABLE(spi, as3935_id); diff --git a/drivers/iio/temperature/max31856.c b/drivers/iio/temperature/max31856.c index 7ddec5cbe558..af8f2f2fe5a6 100644 --- a/drivers/iio/temperature/max31856.c +++ b/drivers/iio/temperature/max31856.c @@ -462,7 +462,7 @@ static int max31856_probe(struct spi_device *spi) } static const struct spi_device_id max31856_id[] = { - { "max31856", 0 }, + { .name = "max31856" }, { } }; MODULE_DEVICE_TABLE(spi, max31856_id); diff --git a/drivers/iio/temperature/max31865.c b/drivers/iio/temperature/max31865.c index 5a6fbe3c80e5..aeb3375906a6 100644 --- a/drivers/iio/temperature/max31865.c +++ b/drivers/iio/temperature/max31865.c @@ -325,7 +325,7 @@ static int max31865_probe(struct spi_device *spi) } static const struct spi_device_id max31865_id[] = { - { "max31865", 0 }, + { .name = "max31865" }, { } }; MODULE_DEVICE_TABLE(spi, max31865_id); From 0a20c840d69b9489535099db541b744a678e0784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 19 Jun 2026 17:54:39 +0200 Subject: [PATCH 072/513] iio: Initialize spi_device_id arrays using member names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct spi_device_id that replaces .driver_data by an anonymous union. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Acked-by: Linus Walleij Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl345_spi.c | 4 +- drivers/iio/accel/adxl355_spi.c | 4 +- drivers/iio/accel/adxl372_spi.c | 4 +- drivers/iio/accel/adxl380_spi.c | 8 +- drivers/iio/accel/bmi088-accel-spi.c | 6 +- drivers/iio/accel/fxls8962af-spi.c | 4 +- drivers/iio/accel/sca3000.c | 8 +- drivers/iio/accel/sca3300.c | 4 +- drivers/iio/accel/st_accel_spi.c | 40 +++--- drivers/iio/adc/ad4000.c | 62 ++++----- drivers/iio/adc/ad4030.c | 14 +- drivers/iio/adc/ad4080.c | 22 +-- drivers/iio/adc/ad4130.c | 12 +- drivers/iio/adc/ad4134.c | 2 +- drivers/iio/adc/ad4170-4.c | 6 +- drivers/iio/adc/ad4851.c | 18 +-- drivers/iio/adc/ad7091r8.c | 6 +- drivers/iio/adc/ad7124.c | 4 +- drivers/iio/adc/ad7173.c | 26 ++-- drivers/iio/adc/ad7191.c | 2 +- drivers/iio/adc/ad7192.c | 10 +- drivers/iio/adc/ad7380.c | 36 ++--- drivers/iio/adc/ad7476.c | 60 ++++---- drivers/iio/adc/ad7606_spi.c | 22 +-- drivers/iio/adc/ad7766.c | 12 +- drivers/iio/adc/ad7768-1.c | 8 +- drivers/iio/adc/ad7780.c | 8 +- drivers/iio/adc/ad7791.c | 10 +- drivers/iio/adc/ad7793.c | 18 +-- drivers/iio/adc/ad7887.c | 2 +- drivers/iio/adc/ad7923.c | 14 +- drivers/iio/adc/ad7944.c | 6 +- drivers/iio/adc/ad7949.c | 6 +- drivers/iio/adc/ad9467.c | 14 +- drivers/iio/adc/max1027.c | 12 +- drivers/iio/adc/max1118.c | 6 +- drivers/iio/adc/max11205.c | 4 +- drivers/iio/adc/max11410.c | 2 +- drivers/iio/adc/max14001.c | 4 +- drivers/iio/adc/mcp320x.c | 26 ++-- drivers/iio/adc/mcp3564.c | 24 ++-- drivers/iio/adc/mcp3911.c | 14 +- drivers/iio/adc/rohm-bd79112.c | 2 +- drivers/iio/adc/ti-adc0832.c | 8 +- drivers/iio/adc/ti-adc12138.c | 6 +- drivers/iio/adc/ti-adc128s052.c | 24 ++-- drivers/iio/adc/ti-adc161s626.c | 4 +- drivers/iio/adc/ti-ads1018.c | 4 +- drivers/iio/adc/ti-ads124s08.c | 4 +- drivers/iio/adc/ti-ads1298.c | 2 +- drivers/iio/adc/ti-ads131e08.c | 6 +- drivers/iio/adc/ti-ads131m02.c | 10 +- drivers/iio/adc/ti-ads7950.c | 24 ++-- drivers/iio/adc/ti-ads8688.c | 4 +- drivers/iio/adc/ti-lmp92064.c | 2 +- drivers/iio/adc/ti-tlc4541.c | 4 +- drivers/iio/addac/ad74115.c | 2 +- drivers/iio/amplifiers/ad8366.c | 26 ++-- drivers/iio/chemical/ens160_spi.c | 2 +- drivers/iio/dac/ad3530r.c | 8 +- drivers/iio/dac/ad5064.c | 32 ++--- drivers/iio/dac/ad5360.c | 16 +-- drivers/iio/dac/ad5380.c | 32 ++--- drivers/iio/dac/ad5446-spi.c | 62 ++++----- drivers/iio/dac/ad5449.c | 14 +- drivers/iio/dac/ad5504.c | 4 +- drivers/iio/dac/ad5624r_spi.c | 12 +- drivers/iio/dac/ad5706r.c | 2 +- drivers/iio/dac/ad5755.c | 10 +- drivers/iio/dac/ad5761.c | 8 +- drivers/iio/dac/ad5764.c | 8 +- drivers/iio/dac/ad5766.c | 4 +- drivers/iio/dac/ad5791.c | 10 +- drivers/iio/dac/ad8801.c | 4 +- drivers/iio/dac/ad9739a.c | 2 +- drivers/iio/dac/ltc1660.c | 4 +- drivers/iio/dac/ltc2632.c | 44 +++--- drivers/iio/dac/ltc2664.c | 4 +- drivers/iio/dac/ltc2688.c | 2 +- drivers/iio/dac/max22007.c | 2 +- drivers/iio/dac/mcp4821.c | 12 +- drivers/iio/dac/mcp4922.c | 8 +- drivers/iio/dac/rohm-bd79703.c | 8 +- drivers/iio/dac/ti-dac082s085.c | 12 +- drivers/iio/dac/ti-dac7311.c | 6 +- drivers/iio/dac/ti-dac7612.c | 2 +- drivers/iio/frequency/adf4350.c | 4 +- drivers/iio/frequency/adf4371.c | 4 +- drivers/iio/frequency/adf4377.c | 4 +- drivers/iio/gyro/adis16080.c | 4 +- drivers/iio/gyro/adis16136.c | 8 +- drivers/iio/gyro/adis16260.c | 12 +- drivers/iio/gyro/adxrs450.c | 4 +- drivers/iio/gyro/st_gyro_spi.c | 18 +-- drivers/iio/humidity/hts221_spi.c | 2 +- drivers/iio/imu/adis16400.c | 30 ++-- drivers/iio/imu/adis16475.c | 54 ++++---- drivers/iio/imu/adis16480.c | 40 +++--- drivers/iio/imu/bmi270/bmi270_spi.c | 4 +- drivers/iio/imu/bmi323/bmi323_spi.c | 2 +- .../iio/imu/inv_icm42600/inv_icm42600_spi.c | 14 +- .../iio/imu/inv_icm45600/inv_icm45600_spi.c | 16 +-- drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c | 34 ++--- drivers/iio/imu/smi240.c | 2 +- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c | 48 +++---- drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c | 4 +- drivers/iio/light/st_uvis25_spi.c | 2 +- drivers/iio/magnetometer/st_magn_spi.c | 12 +- drivers/iio/potentiometer/max5481.c | 8 +- drivers/iio/potentiometer/max5487.c | 6 +- drivers/iio/potentiometer/mcp41010.c | 12 +- drivers/iio/potentiometer/mcp4131.c | 128 +++++++++--------- drivers/iio/potentiometer/x9250.c | 4 +- drivers/iio/pressure/abp2030pa_spi.c | 2 +- drivers/iio/pressure/bmp280-spi.c | 14 +- drivers/iio/pressure/hsc030pa_spi.c | 2 +- drivers/iio/pressure/mprls0025pa_spi.c | 2 +- drivers/iio/pressure/ms5611_spi.c | 4 +- drivers/iio/pressure/st_pressure_spi.c | 24 ++-- drivers/iio/resolver/ad2s1200.c | 4 +- drivers/iio/resolver/ad2s1210.c | 2 +- drivers/iio/resolver/ad2s90.c | 2 +- drivers/iio/temperature/ltc2983.c | 10 +- drivers/iio/temperature/maxim_thermocouple.c | 18 +-- 124 files changed, 802 insertions(+), 802 deletions(-) diff --git a/drivers/iio/accel/adxl345_spi.c b/drivers/iio/accel/adxl345_spi.c index 0315f4bfd69a..4fa1b04f75b6 100644 --- a/drivers/iio/accel/adxl345_spi.c +++ b/drivers/iio/accel/adxl345_spi.c @@ -60,8 +60,8 @@ static const struct adxl345_chip_info adxl375_spi_info = { }; static const struct spi_device_id adxl345_spi_id[] = { - { "adxl345", (kernel_ulong_t)&adxl345_spi_info }, - { "adxl375", (kernel_ulong_t)&adxl375_spi_info }, + { .name = "adxl345", .driver_data = (kernel_ulong_t)&adxl345_spi_info }, + { .name = "adxl375", .driver_data = (kernel_ulong_t)&adxl375_spi_info }, { } }; MODULE_DEVICE_TABLE(spi, adxl345_spi_id); diff --git a/drivers/iio/accel/adxl355_spi.c b/drivers/iio/accel/adxl355_spi.c index 347ed62b6582..793cad5eb443 100644 --- a/drivers/iio/accel/adxl355_spi.c +++ b/drivers/iio/accel/adxl355_spi.c @@ -41,8 +41,8 @@ static int adxl355_spi_probe(struct spi_device *spi) } static const struct spi_device_id adxl355_spi_id[] = { - { "adxl355", (kernel_ulong_t)&adxl35x_chip_info[ADXL355] }, - { "adxl359", (kernel_ulong_t)&adxl35x_chip_info[ADXL359] }, + { .name = "adxl355", .driver_data = (kernel_ulong_t)&adxl35x_chip_info[ADXL355] }, + { .name = "adxl359", .driver_data = (kernel_ulong_t)&adxl35x_chip_info[ADXL359] }, { } }; MODULE_DEVICE_TABLE(spi, adxl355_spi_id); diff --git a/drivers/iio/accel/adxl372_spi.c b/drivers/iio/accel/adxl372_spi.c index 1f9c1544e547..2503223e7c1b 100644 --- a/drivers/iio/accel/adxl372_spi.c +++ b/drivers/iio/accel/adxl372_spi.c @@ -35,8 +35,8 @@ 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 }, + { .name = "adxl371", .driver_data = (kernel_ulong_t)&adxl371_chip_info }, + { .name = "adxl372", .driver_data = (kernel_ulong_t)&adxl372_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adxl372_spi_id); diff --git a/drivers/iio/accel/adxl380_spi.c b/drivers/iio/accel/adxl380_spi.c index 4ead949b24f1..bfc7f153db98 100644 --- a/drivers/iio/accel/adxl380_spi.c +++ b/drivers/iio/accel/adxl380_spi.c @@ -35,10 +35,10 @@ static int adxl380_spi_probe(struct spi_device *spi) } static const struct spi_device_id adxl380_spi_id[] = { - { "adxl318", (kernel_ulong_t)&adxl318_chip_info }, - { "adxl319", (kernel_ulong_t)&adxl319_chip_info }, - { "adxl380", (kernel_ulong_t)&adxl380_chip_info }, - { "adxl382", (kernel_ulong_t)&adxl382_chip_info }, + { .name = "adxl318", .driver_data = (kernel_ulong_t)&adxl318_chip_info }, + { .name = "adxl319", .driver_data = (kernel_ulong_t)&adxl319_chip_info }, + { .name = "adxl380", .driver_data = (kernel_ulong_t)&adxl380_chip_info }, + { .name = "adxl382", .driver_data = (kernel_ulong_t)&adxl382_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adxl380_spi_id); diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c index 44cb50c76cb1..ae89e162e097 100644 --- a/drivers/iio/accel/bmi088-accel-spi.c +++ b/drivers/iio/accel/bmi088-accel-spi.c @@ -72,9 +72,9 @@ static const struct of_device_id bmi088_of_match[] = { MODULE_DEVICE_TABLE(of, bmi088_of_match); static const struct spi_device_id bmi088_accel_id[] = { - {"bmi085-accel", BOSCH_BMI085}, - {"bmi088-accel", BOSCH_BMI088}, - {"bmi090l-accel", BOSCH_BMI090L}, + { .name = "bmi085-accel", .driver_data = BOSCH_BMI085 }, + { .name = "bmi088-accel", .driver_data = BOSCH_BMI088 }, + { .name = "bmi090l-accel", .driver_data = BOSCH_BMI090L }, { } }; MODULE_DEVICE_TABLE(spi, bmi088_accel_id); diff --git a/drivers/iio/accel/fxls8962af-spi.c b/drivers/iio/accel/fxls8962af-spi.c index bdafd1f615d9..53bcb8071e2a 100644 --- a/drivers/iio/accel/fxls8962af-spi.c +++ b/drivers/iio/accel/fxls8962af-spi.c @@ -35,8 +35,8 @@ static const struct of_device_id fxls8962af_spi_of_match[] = { MODULE_DEVICE_TABLE(of, fxls8962af_spi_of_match); static const struct spi_device_id fxls8962af_spi_id_table[] = { - { "fxls8962af", fxls8962af }, - { "fxls8964af", fxls8964af }, + { .name = "fxls8962af", .driver_data = fxls8962af }, + { .name = "fxls8964af", .driver_data = fxls8964af }, { } }; MODULE_DEVICE_TABLE(spi, fxls8962af_spi_id_table); diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 573831199bba..a92a563f6a4b 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -1504,10 +1504,10 @@ static int sca3000_probe(struct spi_device *spi) } static const struct spi_device_id sca3000_id[] = { - { "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 }, + { .name = "sca3000_d01", .driver_data = (kernel_ulong_t)&sca3000_chip_info_d01 }, + { .name = "sca3000_e02", .driver_data = (kernel_ulong_t)&sca3000_chip_info_e02 }, + { .name = "sca3000_e04", .driver_data = (kernel_ulong_t)&sca3000_chip_info_e04 }, + { .name = "sca3000_e05", .driver_data = (kernel_ulong_t)&sca3000_chip_info_e05 }, { } }; MODULE_DEVICE_TABLE(spi, sca3000_id); diff --git a/drivers/iio/accel/sca3300.c b/drivers/iio/accel/sca3300.c index 8380b237831c..67582cc34149 100644 --- a/drivers/iio/accel/sca3300.c +++ b/drivers/iio/accel/sca3300.c @@ -664,8 +664,8 @@ static const struct of_device_id sca3300_dt_ids[] = { MODULE_DEVICE_TABLE(of, sca3300_dt_ids); static const struct spi_device_id sca3300_ids[] = { - { "sca3300" }, - { "scl3300" }, + { .name = "sca3300" }, + { .name = "scl3300" }, { } }; MODULE_DEVICE_TABLE(spi, sca3300_ids); diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index d8ec0555f42a..cd51244df05f 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -147,26 +147,26 @@ static int st_accel_spi_probe(struct spi_device *spi) } static const struct spi_device_id st_accel_id_table[] = { - { LIS3DH_ACCEL_DEV_NAME }, - { LSM330D_ACCEL_DEV_NAME }, - { LSM330DL_ACCEL_DEV_NAME }, - { LSM330DLC_ACCEL_DEV_NAME }, - { LIS331DLH_ACCEL_DEV_NAME }, - { LSM330_ACCEL_DEV_NAME }, - { LSM303AGR_ACCEL_DEV_NAME }, - { LIS2DH12_ACCEL_DEV_NAME }, - { LIS2DS12_ACCEL_DEV_NAME }, - { LIS3L02DQ_ACCEL_DEV_NAME }, - { LNG2DM_ACCEL_DEV_NAME }, - { H3LIS331DL_ACCEL_DEV_NAME }, - { LIS331DL_ACCEL_DEV_NAME }, - { LIS3LV02DL_ACCEL_DEV_NAME }, - { LIS2DW12_ACCEL_DEV_NAME }, - { LIS3DHH_ACCEL_DEV_NAME }, - { LIS3DE_ACCEL_DEV_NAME }, - { LIS302DL_ACCEL_DEV_NAME }, - { LSM303C_ACCEL_DEV_NAME }, - { IIS328DQ_ACCEL_DEV_NAME }, + { .name = LIS3DH_ACCEL_DEV_NAME }, + { .name = LSM330D_ACCEL_DEV_NAME }, + { .name = LSM330DL_ACCEL_DEV_NAME }, + { .name = LSM330DLC_ACCEL_DEV_NAME }, + { .name = LIS331DLH_ACCEL_DEV_NAME }, + { .name = LSM330_ACCEL_DEV_NAME }, + { .name = LSM303AGR_ACCEL_DEV_NAME }, + { .name = LIS2DH12_ACCEL_DEV_NAME }, + { .name = LIS2DS12_ACCEL_DEV_NAME }, + { .name = LIS3L02DQ_ACCEL_DEV_NAME }, + { .name = LNG2DM_ACCEL_DEV_NAME }, + { .name = H3LIS331DL_ACCEL_DEV_NAME }, + { .name = LIS331DL_ACCEL_DEV_NAME }, + { .name = LIS3LV02DL_ACCEL_DEV_NAME }, + { .name = LIS2DW12_ACCEL_DEV_NAME }, + { .name = LIS3DHH_ACCEL_DEV_NAME }, + { .name = LIS3DE_ACCEL_DEV_NAME }, + { .name = LIS302DL_ACCEL_DEV_NAME }, + { .name = LSM303C_ACCEL_DEV_NAME }, + { .name = IIS328DQ_ACCEL_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, st_accel_id_table); diff --git a/drivers/iio/adc/ad4000.c b/drivers/iio/adc/ad4000.c index fd3d79fca785..5ce7df63c6c8 100644 --- a/drivers/iio/adc/ad4000.c +++ b/drivers/iio/adc/ad4000.c @@ -1177,37 +1177,37 @@ static int ad4000_probe(struct spi_device *spi) } static const struct spi_device_id ad4000_id[] = { - { "ad4000", (kernel_ulong_t)&ad4000_chip_info }, - { "ad4001", (kernel_ulong_t)&ad4001_chip_info }, - { "ad4002", (kernel_ulong_t)&ad4002_chip_info }, - { "ad4003", (kernel_ulong_t)&ad4003_chip_info }, - { "ad4004", (kernel_ulong_t)&ad4004_chip_info }, - { "ad4005", (kernel_ulong_t)&ad4005_chip_info }, - { "ad4006", (kernel_ulong_t)&ad4006_chip_info }, - { "ad4007", (kernel_ulong_t)&ad4007_chip_info }, - { "ad4008", (kernel_ulong_t)&ad4008_chip_info }, - { "ad4010", (kernel_ulong_t)&ad4010_chip_info }, - { "ad4011", (kernel_ulong_t)&ad4011_chip_info }, - { "ad4020", (kernel_ulong_t)&ad4020_chip_info }, - { "ad4021", (kernel_ulong_t)&ad4021_chip_info }, - { "ad4022", (kernel_ulong_t)&ad4022_chip_info }, - { "adaq4001", (kernel_ulong_t)&adaq4001_chip_info }, - { "adaq4003", (kernel_ulong_t)&adaq4003_chip_info }, - { "ad7685", (kernel_ulong_t)&ad7685_chip_info }, - { "ad7686", (kernel_ulong_t)&ad7686_chip_info }, - { "ad7687", (kernel_ulong_t)&ad7687_chip_info }, - { "ad7688", (kernel_ulong_t)&ad7688_chip_info }, - { "ad7690", (kernel_ulong_t)&ad7690_chip_info }, - { "ad7691", (kernel_ulong_t)&ad7691_chip_info }, - { "ad7693", (kernel_ulong_t)&ad7693_chip_info }, - { "ad7942", (kernel_ulong_t)&ad7942_chip_info }, - { "ad7946", (kernel_ulong_t)&ad7946_chip_info }, - { "ad7980", (kernel_ulong_t)&ad7980_chip_info }, - { "ad7982", (kernel_ulong_t)&ad7982_chip_info }, - { "ad7983", (kernel_ulong_t)&ad7983_chip_info }, - { "ad7984", (kernel_ulong_t)&ad7984_chip_info }, - { "ad7988-1", (kernel_ulong_t)&ad7988_1_chip_info }, - { "ad7988-5", (kernel_ulong_t)&ad7988_5_chip_info }, + { .name = "ad4000", .driver_data = (kernel_ulong_t)&ad4000_chip_info }, + { .name = "ad4001", .driver_data = (kernel_ulong_t)&ad4001_chip_info }, + { .name = "ad4002", .driver_data = (kernel_ulong_t)&ad4002_chip_info }, + { .name = "ad4003", .driver_data = (kernel_ulong_t)&ad4003_chip_info }, + { .name = "ad4004", .driver_data = (kernel_ulong_t)&ad4004_chip_info }, + { .name = "ad4005", .driver_data = (kernel_ulong_t)&ad4005_chip_info }, + { .name = "ad4006", .driver_data = (kernel_ulong_t)&ad4006_chip_info }, + { .name = "ad4007", .driver_data = (kernel_ulong_t)&ad4007_chip_info }, + { .name = "ad4008", .driver_data = (kernel_ulong_t)&ad4008_chip_info }, + { .name = "ad4010", .driver_data = (kernel_ulong_t)&ad4010_chip_info }, + { .name = "ad4011", .driver_data = (kernel_ulong_t)&ad4011_chip_info }, + { .name = "ad4020", .driver_data = (kernel_ulong_t)&ad4020_chip_info }, + { .name = "ad4021", .driver_data = (kernel_ulong_t)&ad4021_chip_info }, + { .name = "ad4022", .driver_data = (kernel_ulong_t)&ad4022_chip_info }, + { .name = "adaq4001", .driver_data = (kernel_ulong_t)&adaq4001_chip_info }, + { .name = "adaq4003", .driver_data = (kernel_ulong_t)&adaq4003_chip_info }, + { .name = "ad7685", .driver_data = (kernel_ulong_t)&ad7685_chip_info }, + { .name = "ad7686", .driver_data = (kernel_ulong_t)&ad7686_chip_info }, + { .name = "ad7687", .driver_data = (kernel_ulong_t)&ad7687_chip_info }, + { .name = "ad7688", .driver_data = (kernel_ulong_t)&ad7688_chip_info }, + { .name = "ad7690", .driver_data = (kernel_ulong_t)&ad7690_chip_info }, + { .name = "ad7691", .driver_data = (kernel_ulong_t)&ad7691_chip_info }, + { .name = "ad7693", .driver_data = (kernel_ulong_t)&ad7693_chip_info }, + { .name = "ad7942", .driver_data = (kernel_ulong_t)&ad7942_chip_info }, + { .name = "ad7946", .driver_data = (kernel_ulong_t)&ad7946_chip_info }, + { .name = "ad7980", .driver_data = (kernel_ulong_t)&ad7980_chip_info }, + { .name = "ad7982", .driver_data = (kernel_ulong_t)&ad7982_chip_info }, + { .name = "ad7983", .driver_data = (kernel_ulong_t)&ad7983_chip_info }, + { .name = "ad7984", .driver_data = (kernel_ulong_t)&ad7984_chip_info }, + { .name = "ad7988-1", .driver_data = (kernel_ulong_t)&ad7988_1_chip_info }, + { .name = "ad7988-5", .driver_data = (kernel_ulong_t)&ad7988_5_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4000_id); diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c index 9c5f19321e3b..e97400a1aa48 100644 --- a/drivers/iio/adc/ad4030.c +++ b/drivers/iio/adc/ad4030.c @@ -1763,13 +1763,13 @@ static const struct ad4030_chip_info adaq4224_chip_info = { }; 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 }, + { .name = "ad4030-24", .driver_data = (kernel_ulong_t)&ad4030_24_chip_info }, + { .name = "ad4630-16", .driver_data = (kernel_ulong_t)&ad4630_16_chip_info }, + { .name = "ad4630-24", .driver_data = (kernel_ulong_t)&ad4630_24_chip_info }, + { .name = "ad4632-16", .driver_data = (kernel_ulong_t)&ad4632_16_chip_info }, + { .name = "ad4632-24", .driver_data = (kernel_ulong_t)&ad4632_24_chip_info }, + { .name = "adaq4216", .driver_data = (kernel_ulong_t)&adaq4216_chip_info }, + { .name = "adaq4224", .driver_data = (kernel_ulong_t)&adaq4224_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4030_id_table); diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c index 8d2953341b15..524318fe8bfb 100644 --- a/drivers/iio/adc/ad4080.c +++ b/drivers/iio/adc/ad4080.c @@ -849,17 +849,17 @@ 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 }, - { "ad4880", (kernel_ulong_t)&ad4880_chip_info }, - { "ad4884", (kernel_ulong_t)&ad4884_chip_info }, + { .name = "ad4080", .driver_data = (kernel_ulong_t)&ad4080_chip_info }, + { .name = "ad4081", .driver_data = (kernel_ulong_t)&ad4081_chip_info }, + { .name = "ad4082", .driver_data = (kernel_ulong_t)&ad4082_chip_info }, + { .name = "ad4083", .driver_data = (kernel_ulong_t)&ad4083_chip_info }, + { .name = "ad4084", .driver_data = (kernel_ulong_t)&ad4084_chip_info }, + { .name = "ad4085", .driver_data = (kernel_ulong_t)&ad4085_chip_info }, + { .name = "ad4086", .driver_data = (kernel_ulong_t)&ad4086_chip_info }, + { .name = "ad4087", .driver_data = (kernel_ulong_t)&ad4087_chip_info }, + { .name = "ad4088", .driver_data = (kernel_ulong_t)&ad4088_chip_info }, + { .name = "ad4880", .driver_data = (kernel_ulong_t)&ad4880_chip_info }, + { .name = "ad4884", .driver_data = (kernel_ulong_t)&ad4884_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4080_id); diff --git a/drivers/iio/adc/ad4130.c b/drivers/iio/adc/ad4130.c index 7f39f3484062..3ffc4b765e0e 100644 --- a/drivers/iio/adc/ad4130.c +++ b/drivers/iio/adc/ad4130.c @@ -2425,12 +2425,12 @@ static const struct of_device_id ad4130_of_match[] = { MODULE_DEVICE_TABLE(of, ad4130_of_match); static const struct spi_device_id ad4130_id_table[] = { - { "ad4129-4", (kernel_ulong_t)&ad4129_4_chip_info }, - { "ad4129-8", (kernel_ulong_t)&ad4129_8_chip_info }, - { "ad4130-4", (kernel_ulong_t)&ad4130_4_chip_info }, - { "ad4130", (kernel_ulong_t)&ad4130_8_chip_info }, - { "ad4131-4", (kernel_ulong_t)&ad4131_4_chip_info }, - { "ad4131-8", (kernel_ulong_t)&ad4131_8_chip_info }, + { .name = "ad4129-4", .driver_data = (kernel_ulong_t)&ad4129_4_chip_info }, + { .name = "ad4129-8", .driver_data = (kernel_ulong_t)&ad4129_8_chip_info }, + { .name = "ad4130-4", .driver_data = (kernel_ulong_t)&ad4130_4_chip_info }, + { .name = "ad4130", .driver_data = (kernel_ulong_t)&ad4130_8_chip_info }, + { .name = "ad4131-4", .driver_data = (kernel_ulong_t)&ad4131_4_chip_info }, + { .name = "ad4131-8", .driver_data = (kernel_ulong_t)&ad4131_8_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4130_id_table); diff --git a/drivers/iio/adc/ad4134.c b/drivers/iio/adc/ad4134.c index e42ee328fcbf..ab00564274a3 100644 --- a/drivers/iio/adc/ad4134.c +++ b/drivers/iio/adc/ad4134.c @@ -473,7 +473,7 @@ static int ad4134_probe(struct spi_device *spi) } static const struct spi_device_id ad4134_id[] = { - { "ad4134" }, + { .name = "ad4134" }, { } }; MODULE_DEVICE_TABLE(spi, ad4134_id); diff --git a/drivers/iio/adc/ad4170-4.c b/drivers/iio/adc/ad4170-4.c index 627cbf5a37b0..f7757b3a931c 100644 --- a/drivers/iio/adc/ad4170-4.c +++ b/drivers/iio/adc/ad4170-4.c @@ -2979,9 +2979,9 @@ static int ad4170_probe(struct spi_device *spi) } static const struct spi_device_id ad4170_id_table[] = { - { "ad4170-4", (kernel_ulong_t)&ad4170_chip_info }, - { "ad4190-4", (kernel_ulong_t)&ad4190_chip_info }, - { "ad4195-4", (kernel_ulong_t)&ad4195_chip_info }, + { .name = "ad4170-4", .driver_data = (kernel_ulong_t)&ad4170_chip_info }, + { .name = "ad4190-4", .driver_data = (kernel_ulong_t)&ad4190_chip_info }, + { .name = "ad4195-4", .driver_data = (kernel_ulong_t)&ad4195_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4170_id_table); diff --git a/drivers/iio/adc/ad4851.c b/drivers/iio/adc/ad4851.c index 1ad77f2a4580..df27940208e6 100644 --- a/drivers/iio/adc/ad4851.c +++ b/drivers/iio/adc/ad4851.c @@ -1286,15 +1286,15 @@ static const struct of_device_id ad4851_of_match[] = { }; static const struct spi_device_id ad4851_spi_id[] = { - { "ad4851", (kernel_ulong_t)&ad4851_info }, - { "ad4852", (kernel_ulong_t)&ad4852_info }, - { "ad4853", (kernel_ulong_t)&ad4853_info }, - { "ad4854", (kernel_ulong_t)&ad4854_info }, - { "ad4855", (kernel_ulong_t)&ad4855_info }, - { "ad4856", (kernel_ulong_t)&ad4856_info }, - { "ad4857", (kernel_ulong_t)&ad4857_info }, - { "ad4858", (kernel_ulong_t)&ad4858_info }, - { "ad4858i", (kernel_ulong_t)&ad4858i_info }, + { .name = "ad4851", .driver_data = (kernel_ulong_t)&ad4851_info }, + { .name = "ad4852", .driver_data = (kernel_ulong_t)&ad4852_info }, + { .name = "ad4853", .driver_data = (kernel_ulong_t)&ad4853_info }, + { .name = "ad4854", .driver_data = (kernel_ulong_t)&ad4854_info }, + { .name = "ad4855", .driver_data = (kernel_ulong_t)&ad4855_info }, + { .name = "ad4856", .driver_data = (kernel_ulong_t)&ad4856_info }, + { .name = "ad4857", .driver_data = (kernel_ulong_t)&ad4857_info }, + { .name = "ad4858", .driver_data = (kernel_ulong_t)&ad4858_info }, + { .name = "ad4858i", .driver_data = (kernel_ulong_t)&ad4858i_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4851_spi_id); diff --git a/drivers/iio/adc/ad7091r8.c b/drivers/iio/adc/ad7091r8.c index e93b8bb60e8e..465a8ba01a22 100644 --- a/drivers/iio/adc/ad7091r8.c +++ b/drivers/iio/adc/ad7091r8.c @@ -249,9 +249,9 @@ static const struct of_device_id ad7091r8_of_match[] = { MODULE_DEVICE_TABLE(of, ad7091r8_of_match); static const struct spi_device_id ad7091r8_spi_id[] = { - { "ad7091r2", (kernel_ulong_t)&ad7091r2_init_info }, - { "ad7091r4", (kernel_ulong_t)&ad7091r4_init_info }, - { "ad7091r8", (kernel_ulong_t)&ad7091r8_init_info }, + { .name = "ad7091r2", .driver_data = (kernel_ulong_t)&ad7091r2_init_info }, + { .name = "ad7091r4", .driver_data = (kernel_ulong_t)&ad7091r4_init_info }, + { .name = "ad7091r8", .driver_data = (kernel_ulong_t)&ad7091r8_init_info }, { } }; MODULE_DEVICE_TABLE(spi, ad7091r8_spi_id); diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index 5c1a8f886bcc..8ef5767d5d92 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -1684,8 +1684,8 @@ static const struct of_device_id ad7124_of_match[] = { MODULE_DEVICE_TABLE(of, ad7124_of_match); static const struct spi_device_id ad71124_ids[] = { - { "ad7124-4", (kernel_ulong_t)&ad7124_4_chip_info }, - { "ad7124-8", (kernel_ulong_t)&ad7124_8_chip_info }, + { .name = "ad7124-4", .driver_data = (kernel_ulong_t)&ad7124_4_chip_info }, + { .name = "ad7124-8", .driver_data = (kernel_ulong_t)&ad7124_8_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad71124_ids); diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index f76a9e08f39e..a00b69a4b84b 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -2066,19 +2066,19 @@ static const struct of_device_id ad7173_of_match[] = { MODULE_DEVICE_TABLE(of, ad7173_of_match); static const struct spi_device_id ad7173_id_table[] = { - { "ad4111", (kernel_ulong_t)&ad4111_device_info }, - { "ad4112", (kernel_ulong_t)&ad4112_device_info }, - { "ad4113", (kernel_ulong_t)&ad4113_device_info }, - { "ad4114", (kernel_ulong_t)&ad4114_device_info }, - { "ad4115", (kernel_ulong_t)&ad4115_device_info }, - { "ad4116", (kernel_ulong_t)&ad4116_device_info }, - { "ad7172-2", (kernel_ulong_t)&ad7172_2_device_info }, - { "ad7172-4", (kernel_ulong_t)&ad7172_4_device_info }, - { "ad7173-8", (kernel_ulong_t)&ad7173_8_device_info }, - { "ad7175-2", (kernel_ulong_t)&ad7175_2_device_info }, - { "ad7175-8", (kernel_ulong_t)&ad7175_8_device_info }, - { "ad7176-2", (kernel_ulong_t)&ad7176_2_device_info }, - { "ad7177-2", (kernel_ulong_t)&ad7177_2_device_info }, + { .name = "ad4111", .driver_data = (kernel_ulong_t)&ad4111_device_info }, + { .name = "ad4112", .driver_data = (kernel_ulong_t)&ad4112_device_info }, + { .name = "ad4113", .driver_data = (kernel_ulong_t)&ad4113_device_info }, + { .name = "ad4114", .driver_data = (kernel_ulong_t)&ad4114_device_info }, + { .name = "ad4115", .driver_data = (kernel_ulong_t)&ad4115_device_info }, + { .name = "ad4116", .driver_data = (kernel_ulong_t)&ad4116_device_info }, + { .name = "ad7172-2", .driver_data = (kernel_ulong_t)&ad7172_2_device_info }, + { .name = "ad7172-4", .driver_data = (kernel_ulong_t)&ad7172_4_device_info }, + { .name = "ad7173-8", .driver_data = (kernel_ulong_t)&ad7173_8_device_info }, + { .name = "ad7175-2", .driver_data = (kernel_ulong_t)&ad7175_2_device_info }, + { .name = "ad7175-8", .driver_data = (kernel_ulong_t)&ad7175_8_device_info }, + { .name = "ad7176-2", .driver_data = (kernel_ulong_t)&ad7176_2_device_info }, + { .name = "ad7177-2", .driver_data = (kernel_ulong_t)&ad7177_2_device_info }, { } }; MODULE_DEVICE_TABLE(spi, ad7173_id_table); diff --git a/drivers/iio/adc/ad7191.c b/drivers/iio/adc/ad7191.c index 51ec199fb06f..68a89e330ad8 100644 --- a/drivers/iio/adc/ad7191.c +++ b/drivers/iio/adc/ad7191.c @@ -536,7 +536,7 @@ static const struct of_device_id ad7191_of_match[] = { MODULE_DEVICE_TABLE(of, ad7191_of_match); static const struct spi_device_id ad7191_id_table[] = { - { "ad7191" }, + { .name = "ad7191" }, { } }; MODULE_DEVICE_TABLE(spi, ad7191_id_table); diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index caf4473ad643..428d772ea969 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -1449,11 +1449,11 @@ static const struct of_device_id ad7192_of_match[] = { MODULE_DEVICE_TABLE(of, ad7192_of_match); static const struct spi_device_id ad7192_ids[] = { - { "ad7190", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7190] }, - { "ad7192", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7192] }, - { "ad7193", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7193] }, - { "ad7194", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7194] }, - { "ad7195", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7195] }, + { .name = "ad7190", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7190] }, + { .name = "ad7192", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7192] }, + { .name = "ad7193", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7193] }, + { .name = "ad7194", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7194] }, + { .name = "ad7195", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7195] }, { } }; MODULE_DEVICE_TABLE(spi, ad7192_ids); diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c index 9f77990a03f9..2e90f125a797 100644 --- a/drivers/iio/adc/ad7380.c +++ b/drivers/iio/adc/ad7380.c @@ -2116,24 +2116,24 @@ static const struct of_device_id ad7380_of_match_table[] = { }; static const struct spi_device_id ad7380_id_table[] = { - { "ad7380", (kernel_ulong_t)&ad7380_chip_info }, - { "ad7381", (kernel_ulong_t)&ad7381_chip_info }, - { "ad7383", (kernel_ulong_t)&ad7383_chip_info }, - { "ad7384", (kernel_ulong_t)&ad7384_chip_info }, - { "ad7386", (kernel_ulong_t)&ad7386_chip_info }, - { "ad7387", (kernel_ulong_t)&ad7387_chip_info }, - { "ad7388", (kernel_ulong_t)&ad7388_chip_info }, - { "ad7380-4", (kernel_ulong_t)&ad7380_4_chip_info }, - { "ad7381-4", (kernel_ulong_t)&ad7381_4_chip_info }, - { "ad7383-4", (kernel_ulong_t)&ad7383_4_chip_info }, - { "ad7384-4", (kernel_ulong_t)&ad7384_4_chip_info }, - { "ad7386-4", (kernel_ulong_t)&ad7386_4_chip_info }, - { "ad7387-4", (kernel_ulong_t)&ad7387_4_chip_info }, - { "ad7388-4", (kernel_ulong_t)&ad7388_4_chip_info }, - { "ad7389-4", (kernel_ulong_t)&ad7389_4_chip_info }, - { "adaq4370-4", (kernel_ulong_t)&adaq4370_4_chip_info }, - { "adaq4380-4", (kernel_ulong_t)&adaq4380_4_chip_info }, - { "adaq4381-4", (kernel_ulong_t)&adaq4381_4_chip_info }, + { .name = "ad7380", .driver_data = (kernel_ulong_t)&ad7380_chip_info }, + { .name = "ad7381", .driver_data = (kernel_ulong_t)&ad7381_chip_info }, + { .name = "ad7383", .driver_data = (kernel_ulong_t)&ad7383_chip_info }, + { .name = "ad7384", .driver_data = (kernel_ulong_t)&ad7384_chip_info }, + { .name = "ad7386", .driver_data = (kernel_ulong_t)&ad7386_chip_info }, + { .name = "ad7387", .driver_data = (kernel_ulong_t)&ad7387_chip_info }, + { .name = "ad7388", .driver_data = (kernel_ulong_t)&ad7388_chip_info }, + { .name = "ad7380-4", .driver_data = (kernel_ulong_t)&ad7380_4_chip_info }, + { .name = "ad7381-4", .driver_data = (kernel_ulong_t)&ad7381_4_chip_info }, + { .name = "ad7383-4", .driver_data = (kernel_ulong_t)&ad7383_4_chip_info }, + { .name = "ad7384-4", .driver_data = (kernel_ulong_t)&ad7384_4_chip_info }, + { .name = "ad7386-4", .driver_data = (kernel_ulong_t)&ad7386_4_chip_info }, + { .name = "ad7387-4", .driver_data = (kernel_ulong_t)&ad7387_4_chip_info }, + { .name = "ad7388-4", .driver_data = (kernel_ulong_t)&ad7388_4_chip_info }, + { .name = "ad7389-4", .driver_data = (kernel_ulong_t)&ad7389_4_chip_info }, + { .name = "adaq4370-4", .driver_data = (kernel_ulong_t)&adaq4370_4_chip_info }, + { .name = "adaq4380-4", .driver_data = (kernel_ulong_t)&adaq4380_4_chip_info }, + { .name = "adaq4381-4", .driver_data = (kernel_ulong_t)&adaq4381_4_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad7380_id_table); diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index 21d3f6aae972..c007f453ae18 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -411,42 +411,42 @@ static int ad7476_probe(struct spi_device *spi) } static const struct spi_device_id ad7476_id[] = { - { "ad7091", (kernel_ulong_t)&ad7091_chip_info }, - { "ad7091r", (kernel_ulong_t)&ad7091r_chip_info }, - { "ad7273", (kernel_ulong_t)&ad7273_chip_info }, - { "ad7274", (kernel_ulong_t)&ad7274_chip_info }, - { "ad7276", (kernel_ulong_t)&ad7276_chip_info }, - { "ad7277", (kernel_ulong_t)&ad7277_chip_info }, - { "ad7278", (kernel_ulong_t)&ad7278_chip_info }, - { "ad7466", (kernel_ulong_t)&ad7466_chip_info }, - { "ad7467", (kernel_ulong_t)&ad7467_chip_info }, - { "ad7468", (kernel_ulong_t)&ad7468_chip_info }, - { "ad7475", (kernel_ulong_t)&ad7475_chip_info }, - { "ad7476", (kernel_ulong_t)&ad7466_chip_info }, - { "ad7476a", (kernel_ulong_t)&ad7466_chip_info }, - { "ad7477", (kernel_ulong_t)&ad7467_chip_info }, - { "ad7477a", (kernel_ulong_t)&ad7467_chip_info }, - { "ad7478", (kernel_ulong_t)&ad7468_chip_info }, - { "ad7478a", (kernel_ulong_t)&ad7468_chip_info }, - { "ad7495", (kernel_ulong_t)&ad7495_chip_info }, - { "ad7910", (kernel_ulong_t)&ad7467_chip_info }, - { "ad7920", (kernel_ulong_t)&ad7466_chip_info }, - { "ad7940", (kernel_ulong_t)&ad7940_chip_info }, - { "adc081s", (kernel_ulong_t)&adc081s_chip_info }, - { "adc101s", (kernel_ulong_t)&adc101s_chip_info }, - { "adc121s", (kernel_ulong_t)&adc121s_chip_info }, - { "ads7866", (kernel_ulong_t)&ads7866_chip_info }, - { "ads7867", (kernel_ulong_t)&ads7867_chip_info }, - { "ads7868", (kernel_ulong_t)&ads7868_chip_info }, - { "bd79105", (kernel_ulong_t)&bd79105_chip_info }, + { .name = "ad7091", .driver_data = (kernel_ulong_t)&ad7091_chip_info }, + { .name = "ad7091r", .driver_data = (kernel_ulong_t)&ad7091r_chip_info }, + { .name = "ad7273", .driver_data = (kernel_ulong_t)&ad7273_chip_info }, + { .name = "ad7274", .driver_data = (kernel_ulong_t)&ad7274_chip_info }, + { .name = "ad7276", .driver_data = (kernel_ulong_t)&ad7276_chip_info }, + { .name = "ad7277", .driver_data = (kernel_ulong_t)&ad7277_chip_info }, + { .name = "ad7278", .driver_data = (kernel_ulong_t)&ad7278_chip_info }, + { .name = "ad7466", .driver_data = (kernel_ulong_t)&ad7466_chip_info }, + { .name = "ad7467", .driver_data = (kernel_ulong_t)&ad7467_chip_info }, + { .name = "ad7468", .driver_data = (kernel_ulong_t)&ad7468_chip_info }, + { .name = "ad7475", .driver_data = (kernel_ulong_t)&ad7475_chip_info }, + { .name = "ad7476", .driver_data = (kernel_ulong_t)&ad7466_chip_info }, + { .name = "ad7476a", .driver_data = (kernel_ulong_t)&ad7466_chip_info }, + { .name = "ad7477", .driver_data = (kernel_ulong_t)&ad7467_chip_info }, + { .name = "ad7477a", .driver_data = (kernel_ulong_t)&ad7467_chip_info }, + { .name = "ad7478", .driver_data = (kernel_ulong_t)&ad7468_chip_info }, + { .name = "ad7478a", .driver_data = (kernel_ulong_t)&ad7468_chip_info }, + { .name = "ad7495", .driver_data = (kernel_ulong_t)&ad7495_chip_info }, + { .name = "ad7910", .driver_data = (kernel_ulong_t)&ad7467_chip_info }, + { .name = "ad7920", .driver_data = (kernel_ulong_t)&ad7466_chip_info }, + { .name = "ad7940", .driver_data = (kernel_ulong_t)&ad7940_chip_info }, + { .name = "adc081s", .driver_data = (kernel_ulong_t)&adc081s_chip_info }, + { .name = "adc101s", .driver_data = (kernel_ulong_t)&adc101s_chip_info }, + { .name = "adc121s", .driver_data = (kernel_ulong_t)&adc121s_chip_info }, + { .name = "ads7866", .driver_data = (kernel_ulong_t)&ads7866_chip_info }, + { .name = "ads7867", .driver_data = (kernel_ulong_t)&ads7867_chip_info }, + { .name = "ads7868", .driver_data = (kernel_ulong_t)&ads7868_chip_info }, + { .name = "bd79105", .driver_data = (kernel_ulong_t)&bd79105_chip_info }, /* * The ROHM BU79100G is identical to the TI's ADS7866 from the software * point of view. The binding document mandates the ADS7866 to be * marked as a fallback for the BU79100G, but we still need the SPI ID * here to make the module loading work. */ - { "bu79100g", (kernel_ulong_t)&ads7866_chip_info }, - { "ltc2314-14", (kernel_ulong_t)<c2314_14_chip_info }, + { .name = "bu79100g", .driver_data = (kernel_ulong_t)&ads7866_chip_info }, + { .name = "ltc2314-14", .driver_data = (kernel_ulong_t)<c2314_14_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad7476_id); diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c index 7e17ccbcedd0..1ebdfb8580ab 100644 --- a/drivers/iio/adc/ad7606_spi.c +++ b/drivers/iio/adc/ad7606_spi.c @@ -463,17 +463,17 @@ static int ad7606_spi_probe(struct spi_device *spi) } static const struct spi_device_id ad7606_id_table[] = { - { "ad7605-4", (kernel_ulong_t)&ad7605_4_bus_info }, - { "ad7606-4", (kernel_ulong_t)&ad7606_4_bus_info }, - { "ad7606-6", (kernel_ulong_t)&ad7606_6_bus_info }, - { "ad7606-8", (kernel_ulong_t)&ad7606_8_bus_info }, - { "ad7606b", (kernel_ulong_t)&ad7606b_bus_info }, - { "ad7606c-16", (kernel_ulong_t)&ad7606c_16_bus_info }, - { "ad7606c-18", (kernel_ulong_t)&ad7606c_18_bus_info }, - { "ad7607", (kernel_ulong_t)&ad7607_bus_info }, - { "ad7608", (kernel_ulong_t)&ad7608_bus_info }, - { "ad7609", (kernel_ulong_t)&ad7609_bus_info }, - { "ad7616", (kernel_ulong_t)&ad7616_bus_info }, + { .name = "ad7605-4", .driver_data = (kernel_ulong_t)&ad7605_4_bus_info }, + { .name = "ad7606-4", .driver_data = (kernel_ulong_t)&ad7606_4_bus_info }, + { .name = "ad7606-6", .driver_data = (kernel_ulong_t)&ad7606_6_bus_info }, + { .name = "ad7606-8", .driver_data = (kernel_ulong_t)&ad7606_8_bus_info }, + { .name = "ad7606b", .driver_data = (kernel_ulong_t)&ad7606b_bus_info }, + { .name = "ad7606c-16", .driver_data = (kernel_ulong_t)&ad7606c_16_bus_info }, + { .name = "ad7606c-18", .driver_data = (kernel_ulong_t)&ad7606c_18_bus_info }, + { .name = "ad7607", .driver_data = (kernel_ulong_t)&ad7607_bus_info }, + { .name = "ad7608", .driver_data = (kernel_ulong_t)&ad7608_bus_info }, + { .name = "ad7609", .driver_data = (kernel_ulong_t)&ad7609_bus_info }, + { .name = "ad7616", .driver_data = (kernel_ulong_t)&ad7616_bus_info }, { } }; MODULE_DEVICE_TABLE(spi, ad7606_id_table); diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c index 9e4a66477d2d..92957196de59 100644 --- a/drivers/iio/adc/ad7766.c +++ b/drivers/iio/adc/ad7766.c @@ -285,12 +285,12 @@ static int ad7766_probe(struct spi_device *spi) } static const struct spi_device_id ad7766_id[] = { - { "ad7766", ID_AD7766 }, - { "ad7766-1", ID_AD7766_1 }, - { "ad7766-2", ID_AD7766_2 }, - { "ad7767", ID_AD7766 }, - { "ad7767-1", ID_AD7766_1 }, - { "ad7767-2", ID_AD7766_2 }, + { .name = "ad7766", .driver_data = ID_AD7766 }, + { .name = "ad7766-1", .driver_data = ID_AD7766_1 }, + { .name = "ad7766-2", .driver_data = ID_AD7766_2 }, + { .name = "ad7767", .driver_data = ID_AD7766 }, + { .name = "ad7767-1", .driver_data = ID_AD7766_1 }, + { .name = "ad7767-2", .driver_data = ID_AD7766_2 }, { } }; MODULE_DEVICE_TABLE(spi, ad7766_id); diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 598936e47fd2..e9060c1bbe6f 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -1918,10 +1918,10 @@ static int ad7768_probe(struct spi_device *spi) } static const struct spi_device_id ad7768_id_table[] = { - { "ad7768-1", (kernel_ulong_t)&ad7768_chip_info }, - { "adaq7767-1", (kernel_ulong_t)&adaq7767_chip_info }, - { "adaq7768-1", (kernel_ulong_t)&adaq7768_chip_info }, - { "adaq7769-1", (kernel_ulong_t)&adaq7769_chip_info }, + { .name = "ad7768-1", .driver_data = (kernel_ulong_t)&ad7768_chip_info }, + { .name = "adaq7767-1", .driver_data = (kernel_ulong_t)&adaq7767_chip_info }, + { .name = "adaq7768-1", .driver_data = (kernel_ulong_t)&adaq7768_chip_info }, + { .name = "adaq7769-1", .driver_data = (kernel_ulong_t)&adaq7769_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad7768_id_table); diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c index 26382b1f8c9e..1cfad70320fe 100644 --- a/drivers/iio/adc/ad7780.c +++ b/drivers/iio/adc/ad7780.c @@ -347,10 +347,10 @@ static int ad7780_probe(struct spi_device *spi) } static const struct spi_device_id ad7780_id[] = { - { "ad7170", ID_AD7170 }, - { "ad7171", ID_AD7171 }, - { "ad7780", ID_AD7780 }, - { "ad7781", ID_AD7781 }, + { .name = "ad7170", .driver_data = ID_AD7170 }, + { .name = "ad7171", .driver_data = ID_AD7171 }, + { .name = "ad7780", .driver_data = ID_AD7780 }, + { .name = "ad7781", .driver_data = ID_AD7781 }, { } }; MODULE_DEVICE_TABLE(spi, ad7780_id); diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index bcdc19e799aa..c2688307bbba 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -458,11 +458,11 @@ static int ad7791_probe(struct spi_device *spi) } static const struct spi_device_id ad7791_spi_ids[] = { - { "ad7787", AD7787 }, - { "ad7788", AD7788 }, - { "ad7789", AD7789 }, - { "ad7790", AD7790 }, - { "ad7791", AD7791 }, + { .name = "ad7787", .driver_data = AD7787 }, + { .name = "ad7788", .driver_data = AD7788 }, + { .name = "ad7789", .driver_data = AD7789 }, + { .name = "ad7790", .driver_data = AD7790 }, + { .name = "ad7791", .driver_data = AD7791 }, { } }; MODULE_DEVICE_TABLE(spi, ad7791_spi_ids); diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index ea1c192e2142..b0f44c144f20 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -829,15 +829,15 @@ static int ad7793_probe(struct spi_device *spi) } static const struct spi_device_id ad7793_id[] = { - { "ad7785", ID_AD7785 }, - { "ad7792", ID_AD7792 }, - { "ad7793", ID_AD7793 }, - { "ad7794", ID_AD7794 }, - { "ad7795", ID_AD7795 }, - { "ad7796", ID_AD7796 }, - { "ad7797", ID_AD7797 }, - { "ad7798", ID_AD7798 }, - { "ad7799", ID_AD7799 }, + { .name = "ad7785", .driver_data = ID_AD7785 }, + { .name = "ad7792", .driver_data = ID_AD7792 }, + { .name = "ad7793", .driver_data = ID_AD7793 }, + { .name = "ad7794", .driver_data = ID_AD7794 }, + { .name = "ad7795", .driver_data = ID_AD7795 }, + { .name = "ad7796", .driver_data = ID_AD7796 }, + { .name = "ad7797", .driver_data = ID_AD7797 }, + { .name = "ad7798", .driver_data = ID_AD7798 }, + { .name = "ad7799", .driver_data = ID_AD7799 }, { } }; MODULE_DEVICE_TABLE(spi, ad7793_id); diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index 068171d54596..62aaedbce748 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -328,7 +328,7 @@ static int ad7887_probe(struct spi_device *spi) } static const struct spi_device_id ad7887_id[] = { - { "ad7887", ID_AD7887 }, + { .name = "ad7887", .driver_data = ID_AD7887 }, { } }; MODULE_DEVICE_TABLE(spi, ad7887_id); diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index acc87d486aa4..44e014da9722 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -360,13 +360,13 @@ static int ad7923_probe(struct spi_device *spi) } static const struct spi_device_id ad7923_id[] = { - { "ad7904", AD7904 }, - { "ad7914", AD7914 }, - { "ad7923", AD7924 }, - { "ad7924", AD7924 }, - { "ad7908", AD7908 }, - { "ad7918", AD7918 }, - { "ad7928", AD7928 }, + { .name = "ad7904", .driver_data = AD7904 }, + { .name = "ad7914", .driver_data = AD7914 }, + { .name = "ad7923", .driver_data = AD7924 }, + { .name = "ad7924", .driver_data = AD7924 }, + { .name = "ad7908", .driver_data = AD7908 }, + { .name = "ad7918", .driver_data = AD7918 }, + { .name = "ad7928", .driver_data = AD7928 }, { } }; MODULE_DEVICE_TABLE(spi, ad7923_id); diff --git a/drivers/iio/adc/ad7944.c b/drivers/iio/adc/ad7944.c index 7722cf9e8214..12656d3cdcec 100644 --- a/drivers/iio/adc/ad7944.c +++ b/drivers/iio/adc/ad7944.c @@ -865,9 +865,9 @@ static const struct of_device_id ad7944_of_match[] = { MODULE_DEVICE_TABLE(of, ad7944_of_match); static const struct spi_device_id ad7944_spi_id[] = { - { "ad7944", (kernel_ulong_t)&ad7944_chip_info }, - { "ad7985", (kernel_ulong_t)&ad7985_chip_info }, - { "ad7986", (kernel_ulong_t)&ad7986_chip_info }, + { .name = "ad7944", .driver_data = (kernel_ulong_t)&ad7944_chip_info }, + { .name = "ad7985", .driver_data = (kernel_ulong_t)&ad7985_chip_info }, + { .name = "ad7986", .driver_data = (kernel_ulong_t)&ad7986_chip_info }, { } }; diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c index ebc629bcfd4d..93a57a3d59b8 100644 --- a/drivers/iio/adc/ad7949.c +++ b/drivers/iio/adc/ad7949.c @@ -410,9 +410,9 @@ static const struct of_device_id ad7949_spi_of_id[] = { MODULE_DEVICE_TABLE(of, ad7949_spi_of_id); static const struct spi_device_id ad7949_spi_id[] = { - { "ad7949", ID_AD7949 }, - { "ad7682", ID_AD7682 }, - { "ad7689", ID_AD7689 }, + { .name = "ad7949", .driver_data = ID_AD7949 }, + { .name = "ad7682", .driver_data = ID_AD7682 }, + { .name = "ad7689", .driver_data = ID_AD7689 }, { } }; MODULE_DEVICE_TABLE(spi, ad7949_spi_id); diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c index 0c377f9a7f25..34b7b1256f0d 100644 --- a/drivers/iio/adc/ad9467.c +++ b/drivers/iio/adc/ad9467.c @@ -1406,13 +1406,13 @@ static const struct of_device_id ad9467_of_match[] = { MODULE_DEVICE_TABLE(of, ad9467_of_match); static const struct spi_device_id ad9467_ids[] = { - { "ad9211", (kernel_ulong_t)&ad9211_chip_tbl }, - { "ad9265", (kernel_ulong_t)&ad9265_chip_tbl }, - { "ad9434", (kernel_ulong_t)&ad9434_chip_tbl }, - { "ad9467", (kernel_ulong_t)&ad9467_chip_tbl }, - { "ad9643", (kernel_ulong_t)&ad9643_chip_tbl }, - { "ad9649", (kernel_ulong_t)&ad9649_chip_tbl, }, - { "ad9652", (kernel_ulong_t)&ad9652_chip_tbl, }, + { .name = "ad9211", .driver_data = (kernel_ulong_t)&ad9211_chip_tbl }, + { .name = "ad9265", .driver_data = (kernel_ulong_t)&ad9265_chip_tbl }, + { .name = "ad9434", .driver_data = (kernel_ulong_t)&ad9434_chip_tbl }, + { .name = "ad9467", .driver_data = (kernel_ulong_t)&ad9467_chip_tbl }, + { .name = "ad9643", .driver_data = (kernel_ulong_t)&ad9643_chip_tbl }, + { .name = "ad9649", .driver_data = (kernel_ulong_t)&ad9649_chip_tbl }, + { .name = "ad9652", .driver_data = (kernel_ulong_t)&ad9652_chip_tbl }, { } }; MODULE_DEVICE_TABLE(spi, ad9467_ids); diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 7e736e77d8bb..5d175fe42214 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -73,12 +73,12 @@ enum max1027_id { }; static const struct spi_device_id max1027_id[] = { - { "max1027", max1027 }, - { "max1029", max1029 }, - { "max1031", max1031 }, - { "max1227", max1227 }, - { "max1229", max1229 }, - { "max1231", max1231 }, + { .name = "max1027", .driver_data = max1027 }, + { .name = "max1029", .driver_data = max1029 }, + { .name = "max1031", .driver_data = max1031 }, + { .name = "max1227", .driver_data = max1227 }, + { .name = "max1229", .driver_data = max1229 }, + { .name = "max1231", .driver_data = max1231 }, { } }; MODULE_DEVICE_TABLE(spi, max1027_id); diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c index 7d7001e8e3d9..f5cf662a9841 100644 --- a/drivers/iio/adc/max1118.c +++ b/drivers/iio/adc/max1118.c @@ -257,9 +257,9 @@ static int max1118_probe(struct spi_device *spi) } static const struct spi_device_id max1118_id[] = { - { "max1117", max1117 }, - { "max1118", max1118 }, - { "max1119", max1119 }, + { .name = "max1117", .driver_data = max1117 }, + { .name = "max1118", .driver_data = max1118 }, + { .name = "max1119", .driver_data = max1119 }, { } }; MODULE_DEVICE_TABLE(spi, max1118_id); diff --git a/drivers/iio/adc/max11205.c b/drivers/iio/adc/max11205.c index 6c803df220b6..63b6561dcf47 100644 --- a/drivers/iio/adc/max11205.c +++ b/drivers/iio/adc/max11205.c @@ -145,8 +145,8 @@ static int max11205_probe(struct spi_device *spi) } static const struct spi_device_id max11205_spi_ids[] = { - { "max11205a", (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205A] }, - { "max11205b", (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205B] }, + { .name = "max11205a", .driver_data = (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205A] }, + { .name = "max11205b", .driver_data = (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205B] }, { } }; MODULE_DEVICE_TABLE(spi, max11205_spi_ids); diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c index dc1b96356592..23ae684cf08b 100644 --- a/drivers/iio/adc/max11410.c +++ b/drivers/iio/adc/max11410.c @@ -1025,7 +1025,7 @@ static const struct of_device_id max11410_spi_of_id[] = { MODULE_DEVICE_TABLE(of, max11410_spi_of_id); static const struct spi_device_id max11410_id[] = { - { "max11410" }, + { .name = "max11410" }, { } }; MODULE_DEVICE_TABLE(spi, max11410_id); diff --git a/drivers/iio/adc/max14001.c b/drivers/iio/adc/max14001.c index 90ad4cb5868d..c4b2cb9c5db5 100644 --- a/drivers/iio/adc/max14001.c +++ b/drivers/iio/adc/max14001.c @@ -363,8 +363,8 @@ static struct max14001_chip_info max14002_chip_info = { }; static const struct spi_device_id max14001_id_table[] = { - { "max14001", (kernel_ulong_t)&max14001_chip_info }, - { "max14002", (kernel_ulong_t)&max14002_chip_info }, + { .name = "max14001", .driver_data = (kernel_ulong_t)&max14001_chip_info }, + { .name = "max14002", .driver_data = (kernel_ulong_t)&max14002_chip_info }, { } }; diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c index 57cff3772ebe..d70c7eea8139 100644 --- a/drivers/iio/adc/mcp320x.c +++ b/drivers/iio/adc/mcp320x.c @@ -477,19 +477,19 @@ static const struct of_device_id mcp320x_dt_ids[] = { MODULE_DEVICE_TABLE(of, mcp320x_dt_ids); static const struct spi_device_id mcp320x_id[] = { - { "mcp3001", mcp3001 }, - { "mcp3002", mcp3002 }, - { "mcp3004", mcp3004 }, - { "mcp3008", mcp3008 }, - { "mcp3201", mcp3201 }, - { "mcp3202", mcp3202 }, - { "mcp3204", mcp3204 }, - { "mcp3208", mcp3208 }, - { "mcp3301", mcp3301 }, - { "mcp3550-50", mcp3550_50 }, - { "mcp3550-60", mcp3550_60 }, - { "mcp3551", mcp3551 }, - { "mcp3553", mcp3553 }, + { .name = "mcp3001", .driver_data = mcp3001 }, + { .name = "mcp3002", .driver_data = mcp3002 }, + { .name = "mcp3004", .driver_data = mcp3004 }, + { .name = "mcp3008", .driver_data = mcp3008 }, + { .name = "mcp3201", .driver_data = mcp3201 }, + { .name = "mcp3202", .driver_data = mcp3202 }, + { .name = "mcp3204", .driver_data = mcp3204 }, + { .name = "mcp3208", .driver_data = mcp3208 }, + { .name = "mcp3301", .driver_data = mcp3301 }, + { .name = "mcp3550-50", .driver_data = mcp3550_50 }, + { .name = "mcp3550-60", .driver_data = mcp3550_60 }, + { .name = "mcp3551", .driver_data = mcp3551 }, + { .name = "mcp3553", .driver_data = mcp3553 }, { } }; MODULE_DEVICE_TABLE(spi, mcp320x_id); diff --git a/drivers/iio/adc/mcp3564.c b/drivers/iio/adc/mcp3564.c index 36675563829e..259ac4ebdedf 100644 --- a/drivers/iio/adc/mcp3564.c +++ b/drivers/iio/adc/mcp3564.c @@ -1451,18 +1451,18 @@ static const struct of_device_id mcp3564_dt_ids[] = { MODULE_DEVICE_TABLE(of, mcp3564_dt_ids); static const struct spi_device_id mcp3564_id[] = { - { "mcp3461", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461] }, - { "mcp3462", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462] }, - { "mcp3464", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464] }, - { "mcp3561", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561] }, - { "mcp3562", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562] }, - { "mcp3564", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564] }, - { "mcp3461r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461r] }, - { "mcp3462r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462r] }, - { "mcp3464r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464r] }, - { "mcp3561r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561r] }, - { "mcp3562r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562r] }, - { "mcp3564r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564r] }, + { .name = "mcp3461", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461] }, + { .name = "mcp3462", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462] }, + { .name = "mcp3464", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464] }, + { .name = "mcp3561", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561] }, + { .name = "mcp3562", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562] }, + { .name = "mcp3564", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564] }, + { .name = "mcp3461r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461r] }, + { .name = "mcp3462r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462r] }, + { .name = "mcp3464r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464r] }, + { .name = "mcp3561r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561r] }, + { .name = "mcp3562r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562r] }, + { .name = "mcp3564r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564r] }, { } }; MODULE_DEVICE_TABLE(spi, mcp3564_id); diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index ddc3721f3f68..4f8e529629bd 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -928,13 +928,13 @@ static const struct of_device_id mcp3911_dt_ids[] = { MODULE_DEVICE_TABLE(of, mcp3911_dt_ids); static const struct spi_device_id mcp3911_id[] = { - { "mcp3910", (kernel_ulong_t)&mcp3911_chip_info[MCP3910] }, - { "mcp3911", (kernel_ulong_t)&mcp3911_chip_info[MCP3911] }, - { "mcp3912", (kernel_ulong_t)&mcp3911_chip_info[MCP3912] }, - { "mcp3913", (kernel_ulong_t)&mcp3911_chip_info[MCP3913] }, - { "mcp3914", (kernel_ulong_t)&mcp3911_chip_info[MCP3914] }, - { "mcp3918", (kernel_ulong_t)&mcp3911_chip_info[MCP3918] }, - { "mcp3919", (kernel_ulong_t)&mcp3911_chip_info[MCP3919] }, + { .name = "mcp3910", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3910] }, + { .name = "mcp3911", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3911] }, + { .name = "mcp3912", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3912] }, + { .name = "mcp3913", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3913] }, + { .name = "mcp3914", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3914] }, + { .name = "mcp3918", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3918] }, + { .name = "mcp3919", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3919] }, { } }; MODULE_DEVICE_TABLE(spi, mcp3911_id); diff --git a/drivers/iio/adc/rohm-bd79112.c b/drivers/iio/adc/rohm-bd79112.c index 7420aa6627d5..921936e47bf5 100644 --- a/drivers/iio/adc/rohm-bd79112.c +++ b/drivers/iio/adc/rohm-bd79112.c @@ -530,7 +530,7 @@ static const struct of_device_id bd79112_of_match[] = { MODULE_DEVICE_TABLE(of, bd79112_of_match); static const struct spi_device_id bd79112_id[] = { - { "bd79112" }, + { .name = "bd79112" }, { } }; MODULE_DEVICE_TABLE(spi, bd79112_id); diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c index cfcdafbe284b..cc5d37e59a5a 100644 --- a/drivers/iio/adc/ti-adc0832.c +++ b/drivers/iio/adc/ti-adc0832.c @@ -314,10 +314,10 @@ static const struct of_device_id adc0832_dt_ids[] = { MODULE_DEVICE_TABLE(of, adc0832_dt_ids); static const struct spi_device_id adc0832_id[] = { - { "adc0831", adc0831 }, - { "adc0832", adc0832 }, - { "adc0834", adc0834 }, - { "adc0838", adc0838 }, + { .name = "adc0831", .driver_data = adc0831 }, + { .name = "adc0832", .driver_data = adc0832 }, + { .name = "adc0834", .driver_data = adc0834 }, + { .name = "adc0838", .driver_data = adc0838 }, { } }; MODULE_DEVICE_TABLE(spi, adc0832_id); diff --git a/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c index e5ec4b073daa..279e4999eb49 100644 --- a/drivers/iio/adc/ti-adc12138.c +++ b/drivers/iio/adc/ti-adc12138.c @@ -516,9 +516,9 @@ static const struct of_device_id adc12138_dt_ids[] = { MODULE_DEVICE_TABLE(of, adc12138_dt_ids); static const struct spi_device_id adc12138_id[] = { - { "adc12130", adc12130 }, - { "adc12132", adc12132 }, - { "adc12138", adc12138 }, + { .name = "adc12130", .driver_data = adc12130 }, + { .name = "adc12132", .driver_data = adc12132 }, + { .name = "adc12138", .driver_data = adc12138 }, { } }; MODULE_DEVICE_TABLE(spi, adc12138_id); diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c index 4ae65793ad9b..e3d5f9c320e6 100644 --- a/drivers/iio/adc/ti-adc128s052.c +++ b/drivers/iio/adc/ti-adc128s052.c @@ -248,18 +248,18 @@ static const struct of_device_id adc128_of_match[] = { MODULE_DEVICE_TABLE(of, adc128_of_match); static const struct spi_device_id adc128_id[] = { - { "adc128s052", (kernel_ulong_t)&adc128s_config }, - { "adc122s021", (kernel_ulong_t)&adc122s_config }, - { "adc122s051", (kernel_ulong_t)&adc122s_config }, - { "adc122s101", (kernel_ulong_t)&adc122s_config }, - { "adc124s021", (kernel_ulong_t)&adc124s_config }, - { "adc124s051", (kernel_ulong_t)&adc124s_config }, - { "adc124s101", (kernel_ulong_t)&adc124s_config }, - { "bd79100", (kernel_ulong_t)&bd79100_config }, - { "bd79101", (kernel_ulong_t)&bd79101_config }, - { "bd79102", (kernel_ulong_t)&bd79102_config }, - { "bd79103", (kernel_ulong_t)&bd79104_config }, - { "bd79104", (kernel_ulong_t)&bd79104_config }, + { .name = "adc128s052", .driver_data = (kernel_ulong_t)&adc128s_config }, + { .name = "adc122s021", .driver_data = (kernel_ulong_t)&adc122s_config }, + { .name = "adc122s051", .driver_data = (kernel_ulong_t)&adc122s_config }, + { .name = "adc122s101", .driver_data = (kernel_ulong_t)&adc122s_config }, + { .name = "adc124s021", .driver_data = (kernel_ulong_t)&adc124s_config }, + { .name = "adc124s051", .driver_data = (kernel_ulong_t)&adc124s_config }, + { .name = "adc124s101", .driver_data = (kernel_ulong_t)&adc124s_config }, + { .name = "bd79100", .driver_data = (kernel_ulong_t)&bd79100_config }, + { .name = "bd79101", .driver_data = (kernel_ulong_t)&bd79101_config }, + { .name = "bd79102", .driver_data = (kernel_ulong_t)&bd79102_config }, + { .name = "bd79103", .driver_data = (kernel_ulong_t)&bd79104_config }, + { .name = "bd79104", .driver_data = (kernel_ulong_t)&bd79104_config }, { } }; MODULE_DEVICE_TABLE(spi, adc128_id); diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c index be1cc2e77862..3affcb1788f6 100644 --- a/drivers/iio/adc/ti-adc161s626.c +++ b/drivers/iio/adc/ti-adc161s626.c @@ -230,8 +230,8 @@ static const struct of_device_id ti_adc_dt_ids[] = { MODULE_DEVICE_TABLE(of, ti_adc_dt_ids); static const struct spi_device_id ti_adc_id[] = { - { "adc141s626", TI_ADC141S626 }, - { "adc161s626", TI_ADC161S626 }, + { .name = "adc141s626", .driver_data = TI_ADC141S626 }, + { .name = "adc161s626", .driver_data = TI_ADC161S626 }, { } }; MODULE_DEVICE_TABLE(spi, ti_adc_id); diff --git a/drivers/iio/adc/ti-ads1018.c b/drivers/iio/adc/ti-ads1018.c index 0780abd0d0db..8a06d226ea32 100644 --- a/drivers/iio/adc/ti-ads1018.c +++ b/drivers/iio/adc/ti-ads1018.c @@ -718,8 +718,8 @@ static const struct of_device_id ads1018_of_match[] = { MODULE_DEVICE_TABLE(of, ads1018_of_match); static const struct spi_device_id ads1018_spi_match[] = { - { "ads1018", (kernel_ulong_t)&ads1018_chip_info }, - { "ads1118", (kernel_ulong_t)&ads1118_chip_info }, + { .name = "ads1018", .driver_data = (kernel_ulong_t)&ads1018_chip_info }, + { .name = "ads1118", .driver_data = (kernel_ulong_t)&ads1118_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ads1018_spi_match); diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c index 8ea1269f74db..9999452ca6a4 100644 --- a/drivers/iio/adc/ti-ads124s08.c +++ b/drivers/iio/adc/ti-ads124s08.c @@ -348,8 +348,8 @@ static int ads124s_probe(struct spi_device *spi) } static const struct spi_device_id ads124s_id[] = { - { "ads124s06", ADS124S06_ID }, - { "ads124s08", ADS124S08_ID }, + { .name = "ads124s06", .driver_data = ADS124S06_ID }, + { .name = "ads124s08", .driver_data = ADS124S08_ID }, { } }; MODULE_DEVICE_TABLE(spi, ads124s_id); diff --git a/drivers/iio/adc/ti-ads1298.c b/drivers/iio/adc/ti-ads1298.c index 9e47ace16acd..06c6353aa1b2 100644 --- a/drivers/iio/adc/ti-ads1298.c +++ b/drivers/iio/adc/ti-ads1298.c @@ -743,7 +743,7 @@ static int ads1298_probe(struct spi_device *spi) } static const struct spi_device_id ads1298_id[] = { - { "ads1298" }, + { .name = "ads1298" }, { } }; MODULE_DEVICE_TABLE(spi, ads1298_id); diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c index a585621b0bc3..1d25f887cadb 100644 --- a/drivers/iio/adc/ti-ads131e08.c +++ b/drivers/iio/adc/ti-ads131e08.c @@ -917,9 +917,9 @@ static const struct of_device_id ads131e08_of_match[] = { MODULE_DEVICE_TABLE(of, ads131e08_of_match); static const struct spi_device_id ads131e08_ids[] = { - { "ads131e04", (kernel_ulong_t)&ads131e08_info_tbl[ads131e04] }, - { "ads131e06", (kernel_ulong_t)&ads131e08_info_tbl[ads131e06] }, - { "ads131e08", (kernel_ulong_t)&ads131e08_info_tbl[ads131e08] }, + { .name = "ads131e04", .driver_data = (kernel_ulong_t)&ads131e08_info_tbl[ads131e04] }, + { .name = "ads131e06", .driver_data = (kernel_ulong_t)&ads131e08_info_tbl[ads131e06] }, + { .name = "ads131e08", .driver_data = (kernel_ulong_t)&ads131e08_info_tbl[ads131e08] }, { } }; MODULE_DEVICE_TABLE(spi, ads131e08_ids); diff --git a/drivers/iio/adc/ti-ads131m02.c b/drivers/iio/adc/ti-ads131m02.c index 07d63bf62c5f..fdd9167033f9 100644 --- a/drivers/iio/adc/ti-ads131m02.c +++ b/drivers/iio/adc/ti-ads131m02.c @@ -944,11 +944,11 @@ static const struct of_device_id ads131m_of_match[] = { MODULE_DEVICE_TABLE(of, ads131m_of_match); static const struct spi_device_id ads131m_id[] = { - { "ads131m02", (kernel_ulong_t)&ads131m02_config }, - { "ads131m03", (kernel_ulong_t)&ads131m03_config }, - { "ads131m04", (kernel_ulong_t)&ads131m04_config }, - { "ads131m06", (kernel_ulong_t)&ads131m06_config }, - { "ads131m08", (kernel_ulong_t)&ads131m08_config }, + { .name = "ads131m02", .driver_data = (kernel_ulong_t)&ads131m02_config }, + { .name = "ads131m03", .driver_data = (kernel_ulong_t)&ads131m03_config }, + { .name = "ads131m04", .driver_data = (kernel_ulong_t)&ads131m04_config }, + { .name = "ads131m06", .driver_data = (kernel_ulong_t)&ads131m06_config }, + { .name = "ads131m08", .driver_data = (kernel_ulong_t)&ads131m08_config }, { } }; MODULE_DEVICE_TABLE(spi, ads131m_id); diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index 39a074bce6d5..d27c1f2885fd 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -619,18 +619,18 @@ static int ti_ads7950_probe(struct spi_device *spi) } static const struct spi_device_id ti_ads7950_id[] = { - { "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 }, + { .name = "ads7950", .driver_data = (kernel_ulong_t)&ti_ads7950_chip_info }, + { .name = "ads7951", .driver_data = (kernel_ulong_t)&ti_ads7951_chip_info }, + { .name = "ads7952", .driver_data = (kernel_ulong_t)&ti_ads7952_chip_info }, + { .name = "ads7953", .driver_data = (kernel_ulong_t)&ti_ads7953_chip_info }, + { .name = "ads7954", .driver_data = (kernel_ulong_t)&ti_ads7954_chip_info }, + { .name = "ads7955", .driver_data = (kernel_ulong_t)&ti_ads7955_chip_info }, + { .name = "ads7956", .driver_data = (kernel_ulong_t)&ti_ads7956_chip_info }, + { .name = "ads7957", .driver_data = (kernel_ulong_t)&ti_ads7957_chip_info }, + { .name = "ads7958", .driver_data = (kernel_ulong_t)&ti_ads7958_chip_info }, + { .name = "ads7959", .driver_data = (kernel_ulong_t)&ti_ads7959_chip_info }, + { .name = "ads7960", .driver_data = (kernel_ulong_t)&ti_ads7960_chip_info }, + { .name = "ads7961", .driver_data = (kernel_ulong_t)&ti_ads7961_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ti_ads7950_id); diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c index ebd2826a7ff6..db7f1f9f34c9 100644 --- a/drivers/iio/adc/ti-ads8688.c +++ b/drivers/iio/adc/ti-ads8688.c @@ -454,8 +454,8 @@ static int ads8688_probe(struct spi_device *spi) } static const struct spi_device_id ads8688_id[] = { - { "ads8684", ID_ADS8684 }, - { "ads8688", ID_ADS8688 }, + { .name = "ads8684", .driver_data = ID_ADS8684 }, + { .name = "ads8688", .driver_data = ID_ADS8688 }, { } }; MODULE_DEVICE_TABLE(spi, ads8688_id); diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c index 7e57006a8a12..d248bbea0d31 100644 --- a/drivers/iio/adc/ti-lmp92064.c +++ b/drivers/iio/adc/ti-lmp92064.c @@ -357,7 +357,7 @@ static int lmp92064_adc_probe(struct spi_device *spi) } static const struct spi_device_id lmp92064_id_table[] = { - { "lmp92064" }, + { .name = "lmp92064" }, { } }; MODULE_DEVICE_TABLE(spi, lmp92064_id_table); diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c index f67945c62c99..e923c5f3b169 100644 --- a/drivers/iio/adc/ti-tlc4541.c +++ b/drivers/iio/adc/ti-tlc4541.c @@ -241,8 +241,8 @@ static const struct of_device_id tlc4541_dt_ids[] = { MODULE_DEVICE_TABLE(of, tlc4541_dt_ids); static const struct spi_device_id tlc4541_id[] = { - { "tlc3541", TLC3541 }, - { "tlc4541", TLC4541 }, + { .name = "tlc3541", .driver_data = TLC3541 }, + { .name = "tlc4541", .driver_data = TLC4541 }, { } }; MODULE_DEVICE_TABLE(spi, tlc4541_id); diff --git a/drivers/iio/addac/ad74115.c b/drivers/iio/addac/ad74115.c index 41e0b1d334cc..9b9d7a12ce87 100644 --- a/drivers/iio/addac/ad74115.c +++ b/drivers/iio/addac/ad74115.c @@ -1904,7 +1904,7 @@ static int __init ad74115_register_driver(struct spi_driver *spi) } static const struct spi_device_id ad74115_spi_id[] = { - { "ad74115h" }, + { .name = "ad74115h" }, { } }; diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index bbf41a1fb3a1..9b808c7075b1 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -352,19 +352,19 @@ static int ad8366_probe(struct spi_device *spi) } 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 }, - { "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 }, + { .name = "ad8366", .driver_data = (kernel_ulong_t)&ad8366_chip_info }, + { .name = "ada4961", .driver_data = (kernel_ulong_t)&ada4961_chip_info }, + { .name = "adl5240", .driver_data = (kernel_ulong_t)&adl5240_chip_info }, + { .name = "adrf5702", .driver_data = (kernel_ulong_t)&adrf5702_chip_info }, + { .name = "adrf5703", .driver_data = (kernel_ulong_t)&adrf5703_chip_info }, + { .name = "adrf5720", .driver_data = (kernel_ulong_t)&adrf5720_chip_info }, + { .name = "adrf5730", .driver_data = (kernel_ulong_t)&adrf5730_chip_info }, + { .name = "adrf5731", .driver_data = (kernel_ulong_t)&adrf5731_chip_info }, + { .name = "hmc271a", .driver_data = (kernel_ulong_t)&hmc271_chip_info }, + { .name = "hmc792a", .driver_data = (kernel_ulong_t)&hmc792_chip_info }, + { .name = "hmc1018a", .driver_data = (kernel_ulong_t)&hmc1018_chip_info }, + { .name = "hmc1019a", .driver_data = (kernel_ulong_t)&hmc1019_chip_info }, + { .name = "hmc1119", .driver_data = (kernel_ulong_t)&hmc1119_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad8366_id); diff --git a/drivers/iio/chemical/ens160_spi.c b/drivers/iio/chemical/ens160_spi.c index a674c0e1bf4b..7c11d19291c9 100644 --- a/drivers/iio/chemical/ens160_spi.c +++ b/drivers/iio/chemical/ens160_spi.c @@ -39,7 +39,7 @@ static const struct of_device_id ens160_spi_of_match[] = { MODULE_DEVICE_TABLE(of, ens160_spi_of_match); static const struct spi_device_id ens160_spi_id[] = { - { "ens160" }, + { .name = "ens160" }, { } }; MODULE_DEVICE_TABLE(spi, ens160_spi_id); diff --git a/drivers/iio/dac/ad3530r.c b/drivers/iio/dac/ad3530r.c index d9db3226ecd6..9d4545e2c9b3 100644 --- a/drivers/iio/dac/ad3530r.c +++ b/drivers/iio/dac/ad3530r.c @@ -510,10 +510,10 @@ static int ad3530r_probe(struct spi_device *spi) } static const struct spi_device_id ad3530r_id[] = { - { "ad3530", (kernel_ulong_t)&ad3530_chip }, - { "ad3530r", (kernel_ulong_t)&ad3530r_chip }, - { "ad3531", (kernel_ulong_t)&ad3531_chip }, - { "ad3531r", (kernel_ulong_t)&ad3531r_chip }, + { .name = "ad3530", .driver_data = (kernel_ulong_t)&ad3530_chip }, + { .name = "ad3530r", .driver_data = (kernel_ulong_t)&ad3530r_chip }, + { .name = "ad3531", .driver_data = (kernel_ulong_t)&ad3531_chip }, + { .name = "ad3531r", .driver_data = (kernel_ulong_t)&ad3531r_chip }, { } }; MODULE_DEVICE_TABLE(spi, ad3530r_id); diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c index b5ec33f5c317..298f3a703571 100644 --- a/drivers/iio/dac/ad5064.c +++ b/drivers/iio/dac/ad5064.c @@ -920,22 +920,22 @@ static int ad5064_spi_probe(struct spi_device *spi) } static const struct spi_device_id ad5064_spi_ids[] = { - {"ad5024", ID_AD5024}, - {"ad5025", ID_AD5025}, - {"ad5044", ID_AD5044}, - {"ad5045", ID_AD5045}, - {"ad5064", ID_AD5064}, - {"ad5064-1", ID_AD5064_1}, - {"ad5065", ID_AD5065}, - {"ad5628-1", ID_AD5628_1}, - {"ad5628-2", ID_AD5628_2}, - {"ad5648-1", ID_AD5648_1}, - {"ad5648-2", ID_AD5648_2}, - {"ad5666-1", ID_AD5666_1}, - {"ad5666-2", ID_AD5666_2}, - {"ad5668-1", ID_AD5668_1}, - {"ad5668-2", ID_AD5668_2}, - {"ad5668-3", ID_AD5668_2}, /* similar enough to ad5668-2 */ + { .name = "ad5024", .driver_data = ID_AD5024 }, + { .name = "ad5025", .driver_data = ID_AD5025 }, + { .name = "ad5044", .driver_data = ID_AD5044 }, + { .name = "ad5045", .driver_data = ID_AD5045 }, + { .name = "ad5064", .driver_data = ID_AD5064 }, + { .name = "ad5064-1", .driver_data = ID_AD5064_1 }, + { .name = "ad5065", .driver_data = ID_AD5065 }, + { .name = "ad5628-1", .driver_data = ID_AD5628_1 }, + { .name = "ad5628-2", .driver_data = ID_AD5628_2 }, + { .name = "ad5648-1", .driver_data = ID_AD5648_1 }, + { .name = "ad5648-2", .driver_data = ID_AD5648_2 }, + { .name = "ad5666-1", .driver_data = ID_AD5666_1 }, + { .name = "ad5666-2", .driver_data = ID_AD5666_2 }, + { .name = "ad5668-1", .driver_data = ID_AD5668_1 }, + { .name = "ad5668-2", .driver_data = ID_AD5668_2 }, + { .name = "ad5668-3", .driver_data = ID_AD5668_2 }, /* similar enough to ad5668-2 */ { } }; MODULE_DEVICE_TABLE(spi, ad5064_spi_ids); diff --git a/drivers/iio/dac/ad5360.c b/drivers/iio/dac/ad5360.c index 20316fd568e6..c1553f18a26a 100644 --- a/drivers/iio/dac/ad5360.c +++ b/drivers/iio/dac/ad5360.c @@ -523,14 +523,14 @@ static void ad5360_remove(struct spi_device *spi) } static const struct spi_device_id ad5360_ids[] = { - { "ad5360", ID_AD5360 }, - { "ad5361", ID_AD5361 }, - { "ad5362", ID_AD5362 }, - { "ad5363", ID_AD5363 }, - { "ad5370", ID_AD5370 }, - { "ad5371", ID_AD5371 }, - { "ad5372", ID_AD5372 }, - { "ad5373", ID_AD5373 }, + { .name = "ad5360", .driver_data = ID_AD5360 }, + { .name = "ad5361", .driver_data = ID_AD5361 }, + { .name = "ad5362", .driver_data = ID_AD5362 }, + { .name = "ad5363", .driver_data = ID_AD5363 }, + { .name = "ad5370", .driver_data = ID_AD5370 }, + { .name = "ad5371", .driver_data = ID_AD5371 }, + { .name = "ad5372", .driver_data = ID_AD5372 }, + { .name = "ad5373", .driver_data = ID_AD5373 }, { } }; MODULE_DEVICE_TABLE(spi, ad5360_ids); diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 2e587bdd3214..4a5ad419f4ea 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -446,22 +446,22 @@ static int ad5380_spi_probe(struct spi_device *spi) } static const struct spi_device_id ad5380_spi_ids[] = { - { "ad5380-3", ID_AD5380_3 }, - { "ad5380-5", ID_AD5380_5 }, - { "ad5381-3", ID_AD5381_3 }, - { "ad5381-5", ID_AD5381_5 }, - { "ad5382-3", ID_AD5382_3 }, - { "ad5382-5", ID_AD5382_5 }, - { "ad5383-3", ID_AD5383_3 }, - { "ad5383-5", ID_AD5383_5 }, - { "ad5384-3", ID_AD5380_3 }, - { "ad5384-5", ID_AD5380_5 }, - { "ad5390-3", ID_AD5390_3 }, - { "ad5390-5", ID_AD5390_5 }, - { "ad5391-3", ID_AD5391_3 }, - { "ad5391-5", ID_AD5391_5 }, - { "ad5392-3", ID_AD5392_3 }, - { "ad5392-5", ID_AD5392_5 }, + { .name = "ad5380-3", .driver_data = ID_AD5380_3 }, + { .name = "ad5380-5", .driver_data = ID_AD5380_5 }, + { .name = "ad5381-3", .driver_data = ID_AD5381_3 }, + { .name = "ad5381-5", .driver_data = ID_AD5381_5 }, + { .name = "ad5382-3", .driver_data = ID_AD5382_3 }, + { .name = "ad5382-5", .driver_data = ID_AD5382_5 }, + { .name = "ad5383-3", .driver_data = ID_AD5383_3 }, + { .name = "ad5383-5", .driver_data = ID_AD5383_5 }, + { .name = "ad5384-3", .driver_data = ID_AD5380_3 }, + { .name = "ad5384-5", .driver_data = ID_AD5380_5 }, + { .name = "ad5390-3", .driver_data = ID_AD5390_3 }, + { .name = "ad5390-5", .driver_data = ID_AD5390_5 }, + { .name = "ad5391-3", .driver_data = ID_AD5391_3 }, + { .name = "ad5391-5", .driver_data = ID_AD5391_5 }, + { .name = "ad5392-3", .driver_data = ID_AD5392_3 }, + { .name = "ad5392-5", .driver_data = ID_AD5392_5 }, { } }; MODULE_DEVICE_TABLE(spi, ad5380_spi_ids); diff --git a/drivers/iio/dac/ad5446-spi.c b/drivers/iio/dac/ad5446-spi.c index e29d77f21482..aeeb3ece2717 100644 --- a/drivers/iio/dac/ad5446-spi.c +++ b/drivers/iio/dac/ad5446-spi.c @@ -164,38 +164,38 @@ static const struct ad5446_chip_info ad5662_chip_info = { }; static const struct spi_device_id ad5446_spi_ids[] = { - {"ad5300", (kernel_ulong_t)&ad5300_chip_info}, - {"ad5310", (kernel_ulong_t)&ad5310_chip_info}, - {"ad5320", (kernel_ulong_t)&ad5320_chip_info}, - {"ad5444", (kernel_ulong_t)&ad5444_chip_info}, - {"ad5446", (kernel_ulong_t)&ad5446_chip_info}, - {"ad5450", (kernel_ulong_t)&ad5450_chip_info}, - {"ad5451", (kernel_ulong_t)&ad5451_chip_info}, - {"ad5452", (kernel_ulong_t)&ad5444_chip_info}, /* ad5452 is compatible to the ad5444 */ - {"ad5453", (kernel_ulong_t)&ad5446_chip_info}, /* ad5453 is compatible to the ad5446 */ - {"ad5512a", (kernel_ulong_t)&ad5512a_chip_info}, - {"ad5541a", (kernel_ulong_t)&ad5541a_chip_info}, - {"ad5542", (kernel_ulong_t)&ad5541a_chip_info}, /* ad5541a and ad5542 are compatible */ - {"ad5542a", (kernel_ulong_t)&ad5541a_chip_info}, /* ad5541a and ad5542a are compatible */ - {"ad5543", (kernel_ulong_t)&ad5541a_chip_info}, /* ad5541a and ad5543 are compatible */ - {"ad5553", (kernel_ulong_t)&ad5553_chip_info}, - {"ad5600", (kernel_ulong_t)&ad5541a_chip_info}, /* ad5541a and ad5600 are compatible */ - {"ad5601", (kernel_ulong_t)&ad5601_chip_info}, - {"ad5611", (kernel_ulong_t)&ad5611_chip_info}, - {"ad5621", (kernel_ulong_t)&ad5621_chip_info}, - {"ad5641", (kernel_ulong_t)&ad5641_chip_info}, - {"ad5620-2500", (kernel_ulong_t)&ad5620_2500_chip_info}, /* AD5620/40/60: */ + { .name = "ad5300", .driver_data = (kernel_ulong_t)&ad5300_chip_info }, + { .name = "ad5310", .driver_data = (kernel_ulong_t)&ad5310_chip_info }, + { .name = "ad5320", .driver_data = (kernel_ulong_t)&ad5320_chip_info }, + { .name = "ad5444", .driver_data = (kernel_ulong_t)&ad5444_chip_info }, + { .name = "ad5446", .driver_data = (kernel_ulong_t)&ad5446_chip_info }, + { .name = "ad5450", .driver_data = (kernel_ulong_t)&ad5450_chip_info }, + { .name = "ad5451", .driver_data = (kernel_ulong_t)&ad5451_chip_info }, + { .name = "ad5452", .driver_data = (kernel_ulong_t)&ad5444_chip_info }, /* ad5452 is compatible to the ad5444 */ + { .name = "ad5453", .driver_data = (kernel_ulong_t)&ad5446_chip_info }, /* ad5453 is compatible to the ad5446 */ + { .name = "ad5512a", .driver_data = (kernel_ulong_t)&ad5512a_chip_info }, + { .name = "ad5541a", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, + { .name = "ad5542", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, /* ad5541a and ad5542 are compatible */ + { .name = "ad5542a", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, /* ad5541a and ad5542a are compatible */ + { .name = "ad5543", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, /* ad5541a and ad5543 are compatible */ + { .name = "ad5553", .driver_data = (kernel_ulong_t)&ad5553_chip_info }, + { .name = "ad5600", .driver_data = (kernel_ulong_t)&ad5541a_chip_info }, /* ad5541a and ad5600 are compatible */ + { .name = "ad5601", .driver_data = (kernel_ulong_t)&ad5601_chip_info }, + { .name = "ad5611", .driver_data = (kernel_ulong_t)&ad5611_chip_info }, + { .name = "ad5621", .driver_data = (kernel_ulong_t)&ad5621_chip_info }, + { .name = "ad5641", .driver_data = (kernel_ulong_t)&ad5641_chip_info }, + { .name = "ad5620-2500", .driver_data = (kernel_ulong_t)&ad5620_2500_chip_info }, /* AD5620/40/60: */ /* part numbers may look differently */ - {"ad5620-1250", (kernel_ulong_t)&ad5620_1250_chip_info}, - {"ad5640-2500", (kernel_ulong_t)&ad5640_2500_chip_info}, - {"ad5640-1250", (kernel_ulong_t)&ad5640_1250_chip_info}, - {"ad5660-2500", (kernel_ulong_t)&ad5660_2500_chip_info}, - {"ad5660-1250", (kernel_ulong_t)&ad5660_1250_chip_info}, - {"ad5662", (kernel_ulong_t)&ad5662_chip_info}, - {"dac081s101", (kernel_ulong_t)&ad5300_chip_info}, /* compatible Texas Instruments chips */ - {"dac101s101", (kernel_ulong_t)&ad5310_chip_info}, - {"dac121s101", (kernel_ulong_t)&ad5320_chip_info}, - {"dac7512", (kernel_ulong_t)&ad5320_chip_info}, + { .name = "ad5620-1250", .driver_data = (kernel_ulong_t)&ad5620_1250_chip_info }, + { .name = "ad5640-2500", .driver_data = (kernel_ulong_t)&ad5640_2500_chip_info }, + { .name = "ad5640-1250", .driver_data = (kernel_ulong_t)&ad5640_1250_chip_info }, + { .name = "ad5660-2500", .driver_data = (kernel_ulong_t)&ad5660_2500_chip_info }, + { .name = "ad5660-1250", .driver_data = (kernel_ulong_t)&ad5660_1250_chip_info }, + { .name = "ad5662", .driver_data = (kernel_ulong_t)&ad5662_chip_info }, + { .name = "dac081s101", .driver_data = (kernel_ulong_t)&ad5300_chip_info }, /* compatible Texas Instruments chips */ + { .name = "dac101s101", .driver_data = (kernel_ulong_t)&ad5310_chip_info }, + { .name = "dac121s101", .driver_data = (kernel_ulong_t)&ad5320_chip_info }, + { .name = "dac7512", .driver_data = (kernel_ulong_t)&ad5320_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad5446_spi_ids); diff --git a/drivers/iio/dac/ad5449.c b/drivers/iio/dac/ad5449.c index d8c325260259..19e66bc69676 100644 --- a/drivers/iio/dac/ad5449.c +++ b/drivers/iio/dac/ad5449.c @@ -330,13 +330,13 @@ static void ad5449_spi_remove(struct spi_device *spi) } static const struct spi_device_id ad5449_spi_ids[] = { - { "ad5415", ID_AD5449 }, - { "ad5426", ID_AD5426 }, - { "ad5429", ID_AD5429 }, - { "ad5432", ID_AD5432 }, - { "ad5439", ID_AD5439 }, - { "ad5443", ID_AD5443 }, - { "ad5449", ID_AD5449 }, + { .name = "ad5415", .driver_data = ID_AD5449 }, + { .name = "ad5426", .driver_data = ID_AD5426 }, + { .name = "ad5429", .driver_data = ID_AD5429 }, + { .name = "ad5432", .driver_data = ID_AD5432 }, + { .name = "ad5439", .driver_data = ID_AD5439 }, + { .name = "ad5443", .driver_data = ID_AD5443 }, + { .name = "ad5449", .driver_data = ID_AD5449 }, { } }; MODULE_DEVICE_TABLE(spi, ad5449_spi_ids); diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index 5e586185d857..ad1a74678cf2 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -319,8 +319,8 @@ static int ad5504_probe(struct spi_device *spi) } static const struct spi_device_id ad5504_id[] = { - {"ad5504", ID_AD5504}, - {"ad5501", ID_AD5501}, + { .name = "ad5504", .driver_data = ID_AD5504 }, + { .name = "ad5501", .driver_data = ID_AD5501 }, { } }; MODULE_DEVICE_TABLE(spi, ad5504_id); diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c index 13aefe769bad..56aea4b1c167 100644 --- a/drivers/iio/dac/ad5624r_spi.c +++ b/drivers/iio/dac/ad5624r_spi.c @@ -260,12 +260,12 @@ static int ad5624r_probe(struct spi_device *spi) } static const struct spi_device_id ad5624r_id[] = { - {"ad5624r3", ID_AD5624R3}, - {"ad5644r3", ID_AD5644R3}, - {"ad5664r3", ID_AD5664R3}, - {"ad5624r5", ID_AD5624R5}, - {"ad5644r5", ID_AD5644R5}, - {"ad5664r5", ID_AD5664R5}, + { .name = "ad5624r3", .driver_data = ID_AD5624R3 }, + { .name = "ad5644r3", .driver_data = ID_AD5644R3 }, + { .name = "ad5664r3", .driver_data = ID_AD5664R3 }, + { .name = "ad5624r5", .driver_data = ID_AD5624R5 }, + { .name = "ad5644r5", .driver_data = ID_AD5644R5 }, + { .name = "ad5664r5", .driver_data = ID_AD5664R5 }, { } }; MODULE_DEVICE_TABLE(spi, ad5624r_id); diff --git a/drivers/iio/dac/ad5706r.c b/drivers/iio/dac/ad5706r.c index f7872e92dc01..74614d88dd2a 100644 --- a/drivers/iio/dac/ad5706r.c +++ b/drivers/iio/dac/ad5706r.c @@ -233,7 +233,7 @@ static const struct of_device_id ad5706r_of_match[] = { MODULE_DEVICE_TABLE(of, ad5706r_of_match); static const struct spi_device_id ad5706r_id[] = { - { "ad5706r" }, + { .name = "ad5706r" }, { } }; MODULE_DEVICE_TABLE(spi, ad5706r_id); diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index cc6d56140d66..0828b9909099 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -849,11 +849,11 @@ static int ad5755_probe(struct spi_device *spi) } static const struct spi_device_id ad5755_id[] = { - { "ad5755", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] }, - { "ad5755-1", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] }, - { "ad5757", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5757] }, - { "ad5735", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5735] }, - { "ad5737", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5737] }, + { .name = "ad5755", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] }, + { .name = "ad5755-1", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] }, + { .name = "ad5757", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5757] }, + { .name = "ad5735", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5735] }, + { .name = "ad5737", .driver_data = (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5737] }, { } }; MODULE_DEVICE_TABLE(spi, ad5755_id); diff --git a/drivers/iio/dac/ad5761.c b/drivers/iio/dac/ad5761.c index b5d20f04f070..c49568b943d6 100644 --- a/drivers/iio/dac/ad5761.c +++ b/drivers/iio/dac/ad5761.c @@ -342,10 +342,10 @@ static int ad5761_probe(struct spi_device *spi) } static const struct spi_device_id ad5761_id[] = { - {"ad5721", ID_AD5721}, - {"ad5721r", ID_AD5721R}, - {"ad5761", ID_AD5761}, - {"ad5761r", ID_AD5761R}, + { .name = "ad5721", .driver_data = ID_AD5721 }, + { .name = "ad5721r", .driver_data = ID_AD5721R }, + { .name = "ad5761", .driver_data = ID_AD5761 }, + { .name = "ad5761r", .driver_data = ID_AD5761R }, { } }; MODULE_DEVICE_TABLE(spi, ad5761_id); diff --git a/drivers/iio/dac/ad5764.c b/drivers/iio/dac/ad5764.c index fbbd7105a80c..2be4bee7eb01 100644 --- a/drivers/iio/dac/ad5764.c +++ b/drivers/iio/dac/ad5764.c @@ -342,10 +342,10 @@ static void ad5764_remove(struct spi_device *spi) } static const struct spi_device_id ad5764_ids[] = { - { "ad5744", ID_AD5744 }, - { "ad5744r", ID_AD5744R }, - { "ad5764", ID_AD5764 }, - { "ad5764r", ID_AD5764R }, + { .name = "ad5744", .driver_data = ID_AD5744 }, + { .name = "ad5744r", .driver_data = ID_AD5744R }, + { .name = "ad5764", .driver_data = ID_AD5764 }, + { .name = "ad5764r", .driver_data = ID_AD5764R }, { } }; MODULE_DEVICE_TABLE(spi, ad5764_ids); diff --git a/drivers/iio/dac/ad5766.c b/drivers/iio/dac/ad5766.c index f6a0a0d84fef..061b43fc0345 100644 --- a/drivers/iio/dac/ad5766.c +++ b/drivers/iio/dac/ad5766.c @@ -651,8 +651,8 @@ static const struct of_device_id ad5766_dt_match[] = { MODULE_DEVICE_TABLE(of, ad5766_dt_match); static const struct spi_device_id ad5766_spi_ids[] = { - { "ad5766", ID_AD5766 }, - { "ad5767", ID_AD5767 }, + { .name = "ad5766", .driver_data = ID_AD5766 }, + { .name = "ad5767", .driver_data = ID_AD5767 }, { } }; MODULE_DEVICE_TABLE(spi, ad5766_spi_ids); diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c index ae7297f08398..a5eda135c9bd 100644 --- a/drivers/iio/dac/ad5791.c +++ b/drivers/iio/dac/ad5791.c @@ -586,11 +586,11 @@ static const struct of_device_id ad5791_of_match[] = { MODULE_DEVICE_TABLE(of, ad5791_of_match); static const struct spi_device_id ad5791_id[] = { - { "ad5760", (kernel_ulong_t)&ad5760_chip_info }, - { "ad5780", (kernel_ulong_t)&ad5780_chip_info }, - { "ad5781", (kernel_ulong_t)&ad5781_chip_info }, - { "ad5790", (kernel_ulong_t)&ad5790_chip_info }, - { "ad5791", (kernel_ulong_t)&ad5791_chip_info }, + { .name = "ad5760", .driver_data = (kernel_ulong_t)&ad5760_chip_info }, + { .name = "ad5780", .driver_data = (kernel_ulong_t)&ad5780_chip_info }, + { .name = "ad5781", .driver_data = (kernel_ulong_t)&ad5781_chip_info }, + { .name = "ad5790", .driver_data = (kernel_ulong_t)&ad5790_chip_info }, + { .name = "ad5791", .driver_data = (kernel_ulong_t)&ad5791_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad5791_id); diff --git a/drivers/iio/dac/ad8801.c b/drivers/iio/dac/ad8801.c index 60e663af1cc1..08d7d74e999e 100644 --- a/drivers/iio/dac/ad8801.c +++ b/drivers/iio/dac/ad8801.c @@ -151,8 +151,8 @@ static int ad8801_probe(struct spi_device *spi) } static const struct spi_device_id ad8801_ids[] = { - {"ad8801", ID_AD8801}, - {"ad8803", ID_AD8803}, + { .name = "ad8801", .driver_data = ID_AD8801 }, + { .name = "ad8803", .driver_data = ID_AD8803 }, { } }; MODULE_DEVICE_TABLE(spi, ad8801_ids); diff --git a/drivers/iio/dac/ad9739a.c b/drivers/iio/dac/ad9739a.c index d77b46d83bd4..f0561ed39da4 100644 --- a/drivers/iio/dac/ad9739a.c +++ b/drivers/iio/dac/ad9739a.c @@ -447,7 +447,7 @@ static const struct of_device_id ad9739a_of_match[] = { MODULE_DEVICE_TABLE(of, ad9739a_of_match); static const struct spi_device_id ad9739a_id[] = { - {"ad9739a"}, + { .name = "ad9739a" }, { } }; MODULE_DEVICE_TABLE(spi, ad9739a_id); diff --git a/drivers/iio/dac/ltc1660.c b/drivers/iio/dac/ltc1660.c index 6e80b49f4665..90ffb58e7927 100644 --- a/drivers/iio/dac/ltc1660.c +++ b/drivers/iio/dac/ltc1660.c @@ -224,8 +224,8 @@ static const struct of_device_id ltc1660_dt_ids[] = { MODULE_DEVICE_TABLE(of, ltc1660_dt_ids); static const struct spi_device_id ltc1660_id[] = { - {"ltc1660", ID_LTC1660}, - {"ltc1665", ID_LTC1665}, + { .name = "ltc1660", .driver_data = ID_LTC1660 }, + { .name = "ltc1665", .driver_data = ID_LTC1665 }, { } }; MODULE_DEVICE_TABLE(spi, ltc1660_id); diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c index d6a3d290e7a8..3800aee8dd6e 100644 --- a/drivers/iio/dac/ltc2632.c +++ b/drivers/iio/dac/ltc2632.c @@ -362,28 +362,28 @@ static int ltc2632_probe(struct spi_device *spi) } static const struct spi_device_id ltc2632_id[] = { - { "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 }, - { "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 }, + { .name = "ltc2632-l12", .driver_data = (kernel_ulong_t)<c2632l12_chip_info }, + { .name = "ltc2632-l10", .driver_data = (kernel_ulong_t)<c2632l10_chip_info }, + { .name = "ltc2632-l8", .driver_data = (kernel_ulong_t)<c2632l8_chip_info }, + { .name = "ltc2632-h12", .driver_data = (kernel_ulong_t)<c2632h12_chip_info }, + { .name = "ltc2632-h10", .driver_data = (kernel_ulong_t)<c2632h10_chip_info }, + { .name = "ltc2632-h8", .driver_data = (kernel_ulong_t)<c2632h8_chip_info }, + { .name = "ltc2634-l12", .driver_data = (kernel_ulong_t)<c2634l12_chip_info }, + { .name = "ltc2634-l10", .driver_data = (kernel_ulong_t)<c2634l10_chip_info }, + { .name = "ltc2634-l8", .driver_data = (kernel_ulong_t)<c2634l8_chip_info }, + { .name = "ltc2634-h12", .driver_data = (kernel_ulong_t)<c2634h12_chip_info }, + { .name = "ltc2634-h10", .driver_data = (kernel_ulong_t)<c2634h10_chip_info }, + { .name = "ltc2634-h8", .driver_data = (kernel_ulong_t)<c2634h8_chip_info }, + { .name = "ltc2636-l12", .driver_data = (kernel_ulong_t)<c2636l12_chip_info }, + { .name = "ltc2636-l10", .driver_data = (kernel_ulong_t)<c2636l10_chip_info }, + { .name = "ltc2636-l8", .driver_data = (kernel_ulong_t)<c2636l8_chip_info }, + { .name = "ltc2636-h12", .driver_data = (kernel_ulong_t)<c2636h12_chip_info }, + { .name = "ltc2636-h10", .driver_data = (kernel_ulong_t)<c2636h10_chip_info }, + { .name = "ltc2636-h8", .driver_data = (kernel_ulong_t)<c2636h8_chip_info }, + { .name = "ltc2654-l16", .driver_data = (kernel_ulong_t)<c2654l16_chip_info }, + { .name = "ltc2654-l12", .driver_data = (kernel_ulong_t)<c2634l12_chip_info }, + { .name = "ltc2654-h16", .driver_data = (kernel_ulong_t)<c2654h16_chip_info }, + { .name = "ltc2654-h12", .driver_data = (kernel_ulong_t)<c2634h12_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ltc2632_id); diff --git a/drivers/iio/dac/ltc2664.c b/drivers/iio/dac/ltc2664.c index 616806615d3d..ba7430a97396 100644 --- a/drivers/iio/dac/ltc2664.c +++ b/drivers/iio/dac/ltc2664.c @@ -709,8 +709,8 @@ static int ltc2664_probe(struct spi_device *spi) } static const struct spi_device_id ltc2664_id[] = { - { "ltc2664", (kernel_ulong_t)<c2664_chip }, - { "ltc2672", (kernel_ulong_t)<c2672_chip }, + { .name = "ltc2664", .driver_data = (kernel_ulong_t)<c2664_chip }, + { .name = "ltc2672", .driver_data = (kernel_ulong_t)<c2672_chip }, { } }; MODULE_DEVICE_TABLE(spi, ltc2664_id); diff --git a/drivers/iio/dac/ltc2688.c b/drivers/iio/dac/ltc2688.c index 02f408229681..c2aff6a48a01 100644 --- a/drivers/iio/dac/ltc2688.c +++ b/drivers/iio/dac/ltc2688.c @@ -992,7 +992,7 @@ static const struct of_device_id ltc2688_of_id[] = { MODULE_DEVICE_TABLE(of, ltc2688_of_id); static const struct spi_device_id ltc2688_id[] = { - { "ltc2688" }, + { .name = "ltc2688" }, { } }; MODULE_DEVICE_TABLE(spi, ltc2688_id); diff --git a/drivers/iio/dac/max22007.c b/drivers/iio/dac/max22007.c index 182ac7155a89..2b9478a4f2d4 100644 --- a/drivers/iio/dac/max22007.c +++ b/drivers/iio/dac/max22007.c @@ -465,7 +465,7 @@ static int max22007_probe(struct spi_device *spi) } static const struct spi_device_id max22007_id[] = { - { "max22007" }, + { .name = "max22007" }, { } }; MODULE_DEVICE_TABLE(spi, max22007_id); diff --git a/drivers/iio/dac/mcp4821.c b/drivers/iio/dac/mcp4821.c index 18b5934fb8a2..283c1e9982d1 100644 --- a/drivers/iio/dac/mcp4821.c +++ b/drivers/iio/dac/mcp4821.c @@ -298,12 +298,12 @@ static const struct of_device_id mcp4821_of_table[] = { MODULE_DEVICE_TABLE(of, mcp4821_of_table); static const struct spi_device_id mcp4821_id_table[] = { - { "mcp4801", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4801]}, - { "mcp4802", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4802]}, - { "mcp4811", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4811]}, - { "mcp4812", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4812]}, - { "mcp4821", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4821]}, - { "mcp4822", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4822]}, + { .name = "mcp4801", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4801] }, + { .name = "mcp4802", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4802] }, + { .name = "mcp4811", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4811] }, + { .name = "mcp4812", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4812] }, + { .name = "mcp4821", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4821] }, + { .name = "mcp4822", .driver_data = (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4822] }, { } }; MODULE_DEVICE_TABLE(spi, mcp4821_id_table); diff --git a/drivers/iio/dac/mcp4922.c b/drivers/iio/dac/mcp4922.c index 74f338afcab9..15072a9081ff 100644 --- a/drivers/iio/dac/mcp4922.c +++ b/drivers/iio/dac/mcp4922.c @@ -157,10 +157,10 @@ static int mcp4922_probe(struct spi_device *spi) static const struct spi_device_id mcp4922_id[] = { - {"mcp4902", ID_MCP4902}, - {"mcp4912", ID_MCP4912}, - {"mcp4921", ID_MCP4921}, - {"mcp4922", ID_MCP4922}, + { .name = "mcp4902", .driver_data = ID_MCP4902 }, + { .name = "mcp4912", .driver_data = ID_MCP4912 }, + { .name = "mcp4921", .driver_data = ID_MCP4921 }, + { .name = "mcp4922", .driver_data = ID_MCP4922 }, { } }; MODULE_DEVICE_TABLE(spi, mcp4922_id); diff --git a/drivers/iio/dac/rohm-bd79703.c b/drivers/iio/dac/rohm-bd79703.c index e91090e4a66d..6e2e7787f8a4 100644 --- a/drivers/iio/dac/rohm-bd79703.c +++ b/drivers/iio/dac/rohm-bd79703.c @@ -214,10 +214,10 @@ static int bd79703_probe(struct spi_device *spi) } static const struct spi_device_id bd79703_id[] = { - { "bd79700", (kernel_ulong_t)&bd79700_chip_data }, - { "bd79701", (kernel_ulong_t)&bd79701_chip_data }, - { "bd79702", (kernel_ulong_t)&bd79702_chip_data }, - { "bd79703", (kernel_ulong_t)&bd79703_chip_data }, + { .name = "bd79700", .driver_data = (kernel_ulong_t)&bd79700_chip_data }, + { .name = "bd79701", .driver_data = (kernel_ulong_t)&bd79701_chip_data }, + { .name = "bd79702", .driver_data = (kernel_ulong_t)&bd79702_chip_data }, + { .name = "bd79703", .driver_data = (kernel_ulong_t)&bd79703_chip_data }, { } }; MODULE_DEVICE_TABLE(spi, bd79703_id); diff --git a/drivers/iio/dac/ti-dac082s085.c b/drivers/iio/dac/ti-dac082s085.c index 715870c8a9c4..0c2f3c409473 100644 --- a/drivers/iio/dac/ti-dac082s085.c +++ b/drivers/iio/dac/ti-dac082s085.c @@ -335,12 +335,12 @@ static const struct of_device_id ti_dac_of_id[] = { MODULE_DEVICE_TABLE(of, ti_dac_of_id); static const struct spi_device_id ti_dac_spi_id[] = { - { "dac082s085", dual_8bit }, - { "dac102s085", dual_10bit }, - { "dac122s085", dual_12bit }, - { "dac084s085", quad_8bit }, - { "dac104s085", quad_10bit }, - { "dac124s085", quad_12bit }, + { .name = "dac082s085", .driver_data = dual_8bit }, + { .name = "dac102s085", .driver_data = dual_10bit }, + { .name = "dac122s085", .driver_data = dual_12bit }, + { .name = "dac084s085", .driver_data = quad_8bit }, + { .name = "dac104s085", .driver_data = quad_10bit }, + { .name = "dac124s085", .driver_data = quad_12bit }, { } }; MODULE_DEVICE_TABLE(spi, ti_dac_spi_id); diff --git a/drivers/iio/dac/ti-dac7311.c b/drivers/iio/dac/ti-dac7311.c index 5c1c5213962f..e5843bd35259 100644 --- a/drivers/iio/dac/ti-dac7311.c +++ b/drivers/iio/dac/ti-dac7311.c @@ -311,9 +311,9 @@ static const struct of_device_id ti_dac_of_id[] = { MODULE_DEVICE_TABLE(of, ti_dac_of_id); static const struct spi_device_id ti_dac_spi_id[] = { - { "dac5311", ID_DAC5311 }, - { "dac6311", ID_DAC6311 }, - { "dac7311", ID_DAC7311 }, + { .name = "dac5311", .driver_data = ID_DAC5311 }, + { .name = "dac6311", .driver_data = ID_DAC6311 }, + { .name = "dac7311", .driver_data = ID_DAC7311 }, { } }; MODULE_DEVICE_TABLE(spi, ti_dac_spi_id); diff --git a/drivers/iio/dac/ti-dac7612.c b/drivers/iio/dac/ti-dac7612.c index c308eca02b88..9065663cd609 100644 --- a/drivers/iio/dac/ti-dac7612.c +++ b/drivers/iio/dac/ti-dac7612.c @@ -165,7 +165,7 @@ static int dac7612_probe(struct spi_device *spi) } static const struct spi_device_id dac7612_id[] = { - {"ti-dac7612"}, + { .name = "ti-dac7612" }, { } }; MODULE_DEVICE_TABLE(spi, dac7612_id); diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index 6bbb6a8dd9d0..fe58ccd390cb 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -692,8 +692,8 @@ static const struct of_device_id adf4350_of_match[] = { MODULE_DEVICE_TABLE(of, adf4350_of_match); static const struct spi_device_id adf4350_id[] = { - {"adf4350", 4350}, - {"adf4351", 4351}, + { .name = "adf4350", .driver_data = 4350 }, + { .name = "adf4351", .driver_data = 4351 }, { } }; MODULE_DEVICE_TABLE(spi, adf4350_id); diff --git a/drivers/iio/frequency/adf4371.c b/drivers/iio/frequency/adf4371.c index d6dc7827fb41..e20ed718d01d 100644 --- a/drivers/iio/frequency/adf4371.c +++ b/drivers/iio/frequency/adf4371.c @@ -624,8 +624,8 @@ static int adf4371_probe(struct spi_device *spi) } static const struct spi_device_id adf4371_id_table[] = { - { "adf4371", (kernel_ulong_t)&adf4371_chip_info }, - { "adf4372", (kernel_ulong_t)&adf4372_chip_info }, + { .name = "adf4371", .driver_data = (kernel_ulong_t)&adf4371_chip_info }, + { .name = "adf4372", .driver_data = (kernel_ulong_t)&adf4372_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adf4371_id_table); diff --git a/drivers/iio/frequency/adf4377.c b/drivers/iio/frequency/adf4377.c index bb11ae1b8904..4dd19a9aa994 100644 --- a/drivers/iio/frequency/adf4377.c +++ b/drivers/iio/frequency/adf4377.c @@ -1088,8 +1088,8 @@ static int adf4377_probe(struct spi_device *spi) } static const struct spi_device_id adf4377_id[] = { - { "adf4377", (kernel_ulong_t)&adf4377_chip_info }, - { "adf4378", (kernel_ulong_t)&adf4378_chip_info }, + { .name = "adf4377", .driver_data = (kernel_ulong_t)&adf4377_chip_info }, + { .name = "adf4378", .driver_data = (kernel_ulong_t)&adf4378_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adf4377_id); diff --git a/drivers/iio/gyro/adis16080.c b/drivers/iio/gyro/adis16080.c index 178bba95a709..0c8bc1e569fe 100644 --- a/drivers/iio/gyro/adis16080.c +++ b/drivers/iio/gyro/adis16080.c @@ -212,8 +212,8 @@ static int adis16080_probe(struct spi_device *spi) } static const struct spi_device_id adis16080_ids[] = { - { "adis16080", ID_ADIS16080 }, - { "adis16100", ID_ADIS16100 }, + { .name = "adis16080", .driver_data = ID_ADIS16080 }, + { .name = "adis16100", .driver_data = ID_ADIS16100 }, { } }; MODULE_DEVICE_TABLE(spi, adis16080_ids); diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c index 369c7428e1ef..ff335f81e9ae 100644 --- a/drivers/iio/gyro/adis16136.c +++ b/drivers/iio/gyro/adis16136.c @@ -563,10 +563,10 @@ static int adis16136_probe(struct spi_device *spi) } static const struct spi_device_id adis16136_ids[] = { - { "adis16133", ID_ADIS16133 }, - { "adis16135", ID_ADIS16135 }, - { "adis16136", ID_ADIS16136 }, - { "adis16137", ID_ADIS16137 }, + { .name = "adis16133", .driver_data = ID_ADIS16133 }, + { .name = "adis16135", .driver_data = ID_ADIS16135 }, + { .name = "adis16136", .driver_data = ID_ADIS16136 }, + { .name = "adis16137", .driver_data = ID_ADIS16137 }, { } }; MODULE_DEVICE_TABLE(spi, adis16136_ids); diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c index 91b9c5f18ec4..a79a8edf4c98 100644 --- a/drivers/iio/gyro/adis16260.c +++ b/drivers/iio/gyro/adis16260.c @@ -411,12 +411,12 @@ static int adis16260_probe(struct spi_device *spi) * support for the on chip filtering. */ static const struct spi_device_id adis16260_id[] = { - {"adis16260", ADIS16260}, - {"adis16265", ADIS16260}, - {"adis16266", ADIS16266}, - {"adis16250", ADIS16260}, - {"adis16255", ADIS16260}, - {"adis16251", ADIS16251}, + { .name = "adis16260", .driver_data = ADIS16260 }, + { .name = "adis16265", .driver_data = ADIS16260 }, + { .name = "adis16266", .driver_data = ADIS16266 }, + { .name = "adis16250", .driver_data = ADIS16260 }, + { .name = "adis16255", .driver_data = ADIS16260 }, + { .name = "adis16251", .driver_data = ADIS16251 }, { } }; MODULE_DEVICE_TABLE(spi, adis16260_id); diff --git a/drivers/iio/gyro/adxrs450.c b/drivers/iio/gyro/adxrs450.c index a1d8d3cb301b..030d042cab8b 100644 --- a/drivers/iio/gyro/adxrs450.c +++ b/drivers/iio/gyro/adxrs450.c @@ -439,8 +439,8 @@ static int adxrs450_probe(struct spi_device *spi) } static const struct spi_device_id adxrs450_id[] = { - {"adxrs450", ID_ADXRS450}, - {"adxrs453", ID_ADXRS453}, + { .name = "adxrs450", .driver_data = ID_ADXRS450 }, + { .name = "adxrs453", .driver_data = ID_ADXRS453 }, { } }; MODULE_DEVICE_TABLE(spi, adxrs450_id); diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c index f645da157372..3defd7e95f65 100644 --- a/drivers/iio/gyro/st_gyro_spi.c +++ b/drivers/iio/gyro/st_gyro_spi.c @@ -98,15 +98,15 @@ static int st_gyro_spi_probe(struct spi_device *spi) } static const struct spi_device_id st_gyro_id_table[] = { - { L3G4200D_GYRO_DEV_NAME }, - { LSM330D_GYRO_DEV_NAME }, - { LSM330DL_GYRO_DEV_NAME }, - { LSM330DLC_GYRO_DEV_NAME }, - { L3GD20_GYRO_DEV_NAME }, - { L3GD20H_GYRO_DEV_NAME }, - { L3G4IS_GYRO_DEV_NAME }, - { LSM330_GYRO_DEV_NAME }, - { LSM9DS0_GYRO_DEV_NAME }, + { .name = L3G4200D_GYRO_DEV_NAME }, + { .name = LSM330D_GYRO_DEV_NAME }, + { .name = LSM330DL_GYRO_DEV_NAME }, + { .name = LSM330DLC_GYRO_DEV_NAME }, + { .name = L3GD20_GYRO_DEV_NAME }, + { .name = L3GD20H_GYRO_DEV_NAME }, + { .name = L3G4IS_GYRO_DEV_NAME }, + { .name = LSM330_GYRO_DEV_NAME }, + { .name = LSM9DS0_GYRO_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, st_gyro_id_table); diff --git a/drivers/iio/humidity/hts221_spi.c b/drivers/iio/humidity/hts221_spi.c index e6fef2acd523..f962842cc71d 100644 --- a/drivers/iio/humidity/hts221_spi.c +++ b/drivers/iio/humidity/hts221_spi.c @@ -47,7 +47,7 @@ static const struct of_device_id hts221_spi_of_match[] = { MODULE_DEVICE_TABLE(of, hts221_spi_of_match); static const struct spi_device_id hts221_spi_id_table[] = { - { HTS221_DEV_NAME }, + { .name = HTS221_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, hts221_spi_id_table); diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index 36323ad149e0..4842346f9f0c 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -1189,21 +1189,21 @@ static int adis16400_probe(struct spi_device *spi) } static const struct spi_device_id adis16400_id[] = { - { "adis16300", (kernel_ulong_t)&adis16300_chip_info }, - { "adis16305", (kernel_ulong_t)&adis16300_chip_info }, - { "adis16334", (kernel_ulong_t)&adis16334_chip_info }, - { "adis16350", (kernel_ulong_t)&adis16350_chip_info }, - { "adis16354", (kernel_ulong_t)&adis16350_chip_info }, - { "adis16355", (kernel_ulong_t)&adis16350_chip_info }, - { "adis16360", (kernel_ulong_t)&adis16360_chip_info }, - { "adis16362", (kernel_ulong_t)&adis16362_chip_info }, - { "adis16364", (kernel_ulong_t)&adis16364_chip_info }, - { "adis16365", (kernel_ulong_t)&adis16360_chip_info }, - { "adis16367", (kernel_ulong_t)&adis16367_chip_info }, - { "adis16400", (kernel_ulong_t)&adis16400_chip_info }, - { "adis16405", (kernel_ulong_t)&adis16400_chip_info }, - { "adis16445", (kernel_ulong_t)&adis16445_chip_info }, - { "adis16448", (kernel_ulong_t)&adis16448_chip_info }, + { .name = "adis16300", .driver_data = (kernel_ulong_t)&adis16300_chip_info }, + { .name = "adis16305", .driver_data = (kernel_ulong_t)&adis16300_chip_info }, + { .name = "adis16334", .driver_data = (kernel_ulong_t)&adis16334_chip_info }, + { .name = "adis16350", .driver_data = (kernel_ulong_t)&adis16350_chip_info }, + { .name = "adis16354", .driver_data = (kernel_ulong_t)&adis16350_chip_info }, + { .name = "adis16355", .driver_data = (kernel_ulong_t)&adis16350_chip_info }, + { .name = "adis16360", .driver_data = (kernel_ulong_t)&adis16360_chip_info }, + { .name = "adis16362", .driver_data = (kernel_ulong_t)&adis16362_chip_info }, + { .name = "adis16364", .driver_data = (kernel_ulong_t)&adis16364_chip_info }, + { .name = "adis16365", .driver_data = (kernel_ulong_t)&adis16360_chip_info }, + { .name = "adis16367", .driver_data = (kernel_ulong_t)&adis16367_chip_info }, + { .name = "adis16400", .driver_data = (kernel_ulong_t)&adis16400_chip_info }, + { .name = "adis16405", .driver_data = (kernel_ulong_t)&adis16400_chip_info }, + { .name = "adis16445", .driver_data = (kernel_ulong_t)&adis16445_chip_info }, + { .name = "adis16448", .driver_data = (kernel_ulong_t)&adis16448_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adis16400_id); diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index ab39bea1e729..138d43d3db05 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -2062,33 +2062,33 @@ static const struct of_device_id adis16475_of_match[] = { MODULE_DEVICE_TABLE(of, adis16475_of_match); static const struct spi_device_id adis16475_ids[] = { - { "adis16470", (kernel_ulong_t)&adis16475_chip_info[ADIS16470] }, - { "adis16475-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_1] }, - { "adis16475-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_2] }, - { "adis16475-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_3] }, - { "adis16477-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_1] }, - { "adis16477-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_2] }, - { "adis16477-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_3] }, - { "adis16465-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_1] }, - { "adis16465-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_2] }, - { "adis16465-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_3] }, - { "adis16467-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_1] }, - { "adis16467-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_2] }, - { "adis16467-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_3] }, - { "adis16500", (kernel_ulong_t)&adis16475_chip_info[ADIS16500] }, - { "adis16501", (kernel_ulong_t)&adis16475_chip_info[ADIS16501] }, - { "adis16505-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_1] }, - { "adis16505-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_2] }, - { "adis16505-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_3] }, - { "adis16507-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_1] }, - { "adis16507-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_2] }, - { "adis16507-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_3] }, - { "adis16575-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16575_2] }, - { "adis16575-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16575_3] }, - { "adis16576-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16576_2] }, - { "adis16576-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16576_3] }, - { "adis16577-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16577_2] }, - { "adis16577-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16577_3] }, + { .name = "adis16470", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16470] }, + { .name = "adis16475-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16475_1] }, + { .name = "adis16475-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16475_2] }, + { .name = "adis16475-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16475_3] }, + { .name = "adis16477-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16477_1] }, + { .name = "adis16477-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16477_2] }, + { .name = "adis16477-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16477_3] }, + { .name = "adis16465-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16465_1] }, + { .name = "adis16465-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16465_2] }, + { .name = "adis16465-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16465_3] }, + { .name = "adis16467-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16467_1] }, + { .name = "adis16467-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16467_2] }, + { .name = "adis16467-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16467_3] }, + { .name = "adis16500", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16500] }, + { .name = "adis16501", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16501] }, + { .name = "adis16505-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16505_1] }, + { .name = "adis16505-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16505_2] }, + { .name = "adis16505-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16505_3] }, + { .name = "adis16507-1", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16507_1] }, + { .name = "adis16507-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16507_2] }, + { .name = "adis16507-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16507_3] }, + { .name = "adis16575-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16575_2] }, + { .name = "adis16575-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16575_3] }, + { .name = "adis16576-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16576_2] }, + { .name = "adis16576-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16576_3] }, + { .name = "adis16577-2", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16577_2] }, + { .name = "adis16577-3", .driver_data = (kernel_ulong_t)&adis16475_chip_info[ADIS16577_3] }, { } }; MODULE_DEVICE_TABLE(spi, adis16475_ids); diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index 543d5c4bfb11..51254fe15a04 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -1807,26 +1807,26 @@ static int adis16480_probe(struct spi_device *spi) } static const struct spi_device_id adis16480_ids[] = { - { "adis16375", ADIS16375 }, - { "adis16480", ADIS16480 }, - { "adis16485", ADIS16485 }, - { "adis16486", ADIS16486 }, - { "adis16487", ADIS16487 }, - { "adis16488", ADIS16488 }, - { "adis16489", ADIS16489 }, - { "adis16490", ADIS16490 }, - { "adis16495-1", ADIS16495_1 }, - { "adis16495-2", ADIS16495_2 }, - { "adis16495-3", ADIS16495_3 }, - { "adis16497-1", ADIS16497_1 }, - { "adis16497-2", ADIS16497_2 }, - { "adis16497-3", ADIS16497_3 }, - { "adis16545-1", ADIS16545_1 }, - { "adis16545-2", ADIS16545_2 }, - { "adis16545-3", ADIS16545_3 }, - { "adis16547-1", ADIS16547_1 }, - { "adis16547-2", ADIS16547_2 }, - { "adis16547-3", ADIS16547_3 }, + { .name = "adis16375", .driver_data = ADIS16375 }, + { .name = "adis16480", .driver_data = ADIS16480 }, + { .name = "adis16485", .driver_data = ADIS16485 }, + { .name = "adis16486", .driver_data = ADIS16486 }, + { .name = "adis16487", .driver_data = ADIS16487 }, + { .name = "adis16488", .driver_data = ADIS16488 }, + { .name = "adis16489", .driver_data = ADIS16489 }, + { .name = "adis16490", .driver_data = ADIS16490 }, + { .name = "adis16495-1", .driver_data = ADIS16495_1 }, + { .name = "adis16495-2", .driver_data = ADIS16495_2 }, + { .name = "adis16495-3", .driver_data = ADIS16495_3 }, + { .name = "adis16497-1", .driver_data = ADIS16497_1 }, + { .name = "adis16497-2", .driver_data = ADIS16497_2 }, + { .name = "adis16497-3", .driver_data = ADIS16497_3 }, + { .name = "adis16545-1", .driver_data = ADIS16545_1 }, + { .name = "adis16545-2", .driver_data = ADIS16545_2 }, + { .name = "adis16545-3", .driver_data = ADIS16545_3 }, + { .name = "adis16547-1", .driver_data = ADIS16547_1 }, + { .name = "adis16547-2", .driver_data = ADIS16547_2 }, + { .name = "adis16547-3", .driver_data = ADIS16547_3 }, { } }; MODULE_DEVICE_TABLE(spi, adis16480_ids); diff --git a/drivers/iio/imu/bmi270/bmi270_spi.c b/drivers/iio/imu/bmi270/bmi270_spi.c index 80c9fa1d685a..8f55e57584c7 100644 --- a/drivers/iio/imu/bmi270/bmi270_spi.c +++ b/drivers/iio/imu/bmi270/bmi270_spi.c @@ -66,8 +66,8 @@ static int bmi270_spi_probe(struct spi_device *spi) } static const struct spi_device_id bmi270_spi_id[] = { - { "bmi260", (kernel_ulong_t)&bmi260_chip_info }, - { "bmi270", (kernel_ulong_t)&bmi270_chip_info }, + { .name = "bmi260", .driver_data = (kernel_ulong_t)&bmi260_chip_info }, + { .name = "bmi270", .driver_data = (kernel_ulong_t)&bmi270_chip_info }, { } }; diff --git a/drivers/iio/imu/bmi323/bmi323_spi.c b/drivers/iio/imu/bmi323/bmi323_spi.c index fd56ab620750..20501900336e 100644 --- a/drivers/iio/imu/bmi323/bmi323_spi.c +++ b/drivers/iio/imu/bmi323/bmi323_spi.c @@ -65,7 +65,7 @@ static int bmi323_spi_probe(struct spi_device *spi) } static const struct spi_device_id bmi323_spi_ids[] = { - { "bmi323" }, + { .name = "bmi323" }, { } }; MODULE_DEVICE_TABLE(spi, bmi323_spi_ids); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c index 13e2e7d38638..089579caf244 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c @@ -72,13 +72,13 @@ static int inv_icm42600_probe(struct spi_device *spi) * supported by this driver */ static const struct spi_device_id inv_icm42600_id[] = { - { "icm42600", INV_CHIP_ICM42600 }, - { "icm42602", INV_CHIP_ICM42602 }, - { "icm42605", INV_CHIP_ICM42605 }, - { "icm42686", INV_CHIP_ICM42686 }, - { "icm42622", INV_CHIP_ICM42622 }, - { "icm42688", INV_CHIP_ICM42688 }, - { "icm42631", INV_CHIP_ICM42631 }, + { .name = "icm42600", .driver_data = INV_CHIP_ICM42600 }, + { .name = "icm42602", .driver_data = INV_CHIP_ICM42602 }, + { .name = "icm42605", .driver_data = INV_CHIP_ICM42605 }, + { .name = "icm42686", .driver_data = INV_CHIP_ICM42686 }, + { .name = "icm42622", .driver_data = INV_CHIP_ICM42622 }, + { .name = "icm42688", .driver_data = INV_CHIP_ICM42688 }, + { .name = "icm42631", .driver_data = INV_CHIP_ICM42631 }, { } }; MODULE_DEVICE_TABLE(spi, inv_icm42600_id); diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c index 6288113a6d7c..60116baea694 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c @@ -49,14 +49,14 @@ static int inv_icm45600_probe(struct spi_device *spi) * supported by this driver. */ static const struct spi_device_id inv_icm45600_id[] = { - { "icm45605", (kernel_ulong_t)&inv_icm45605_chip_info }, - { "icm45606", (kernel_ulong_t)&inv_icm45606_chip_info }, - { "icm45608", (kernel_ulong_t)&inv_icm45608_chip_info }, - { "icm45634", (kernel_ulong_t)&inv_icm45634_chip_info }, - { "icm45686", (kernel_ulong_t)&inv_icm45686_chip_info }, - { "icm45687", (kernel_ulong_t)&inv_icm45687_chip_info }, - { "icm45688p", (kernel_ulong_t)&inv_icm45688p_chip_info }, - { "icm45689", (kernel_ulong_t)&inv_icm45689_chip_info }, + { .name = "icm45605", .driver_data = (kernel_ulong_t)&inv_icm45605_chip_info }, + { .name = "icm45606", .driver_data = (kernel_ulong_t)&inv_icm45606_chip_info }, + { .name = "icm45608", .driver_data = (kernel_ulong_t)&inv_icm45608_chip_info }, + { .name = "icm45634", .driver_data = (kernel_ulong_t)&inv_icm45634_chip_info }, + { .name = "icm45686", .driver_data = (kernel_ulong_t)&inv_icm45686_chip_info }, + { .name = "icm45687", .driver_data = (kernel_ulong_t)&inv_icm45687_chip_info }, + { .name = "icm45688p", .driver_data = (kernel_ulong_t)&inv_icm45688p_chip_info }, + { .name = "icm45689", .driver_data = (kernel_ulong_t)&inv_icm45689_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, inv_icm45600_id); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index 1f4c62142b60..5a4bc7a559f5 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -66,23 +66,23 @@ static int inv_mpu_probe(struct spi_device *spi) * supported by this driver */ static const struct spi_device_id inv_mpu_id[] = { - {"mpu6000", INV_MPU6000}, - {"mpu6500", INV_MPU6500}, - {"mpu6515", INV_MPU6515}, - {"mpu6880", INV_MPU6880}, - {"mpu9250", INV_MPU9250}, - {"mpu9255", INV_MPU9255}, - {"icm20608", INV_ICM20608}, - {"icm20608d", INV_ICM20608D}, - {"icm20609", INV_ICM20609}, - {"icm20689", INV_ICM20689}, - {"icm20600", INV_ICM20600}, - {"icm20602", INV_ICM20602}, - {"icm20690", INV_ICM20690}, - {"iam20380", INV_IAM20380}, - {"iam20680", INV_IAM20680}, - {"iam20680hp", INV_IAM20680HP}, - {"iam20680ht", INV_IAM20680HT}, + { .name = "mpu6000", .driver_data = INV_MPU6000 }, + { .name = "mpu6500", .driver_data = INV_MPU6500 }, + { .name = "mpu6515", .driver_data = INV_MPU6515 }, + { .name = "mpu6880", .driver_data = INV_MPU6880 }, + { .name = "mpu9250", .driver_data = INV_MPU9250 }, + { .name = "mpu9255", .driver_data = INV_MPU9255 }, + { .name = "icm20608", .driver_data = INV_ICM20608 }, + { .name = "icm20608d", .driver_data = INV_ICM20608D }, + { .name = "icm20609", .driver_data = INV_ICM20609 }, + { .name = "icm20689", .driver_data = INV_ICM20689 }, + { .name = "icm20600", .driver_data = INV_ICM20600 }, + { .name = "icm20602", .driver_data = INV_ICM20602 }, + { .name = "icm20690", .driver_data = INV_ICM20690 }, + { .name = "iam20380", .driver_data = INV_IAM20380 }, + { .name = "iam20680", .driver_data = INV_IAM20680 }, + { .name = "iam20680hp", .driver_data = INV_IAM20680HP }, + { .name = "iam20680ht", .driver_data = INV_IAM20680HT }, { } }; diff --git a/drivers/iio/imu/smi240.c b/drivers/iio/imu/smi240.c index d159ee59acdd..f419296bba88 100644 --- a/drivers/iio/imu/smi240.c +++ b/drivers/iio/imu/smi240.c @@ -593,7 +593,7 @@ static int smi240_probe(struct spi_device *spi) } static const struct spi_device_id smi240_spi_id[] = { - { "smi240" }, + { .name = "smi240" }, { } }; MODULE_DEVICE_TABLE(spi, smi240_spi_id); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c index 3389b15df0bc..ed47d5a6f11c 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -138,30 +138,30 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = { MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); static const struct spi_device_id st_lsm6dsx_spi_id_table[] = { - { ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID }, - { ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID }, - { ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID }, - { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, - { ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID }, - { ST_LSM6DSO_DEV_NAME, ST_LSM6DSO_ID }, - { ST_ASM330LHH_DEV_NAME, ST_ASM330LHH_ID }, - { ST_LSM6DSOX_DEV_NAME, ST_LSM6DSOX_ID }, - { ST_LSM6DSR_DEV_NAME, ST_LSM6DSR_ID }, - { ST_LSM6DS3TRC_DEV_NAME, ST_LSM6DS3TRC_ID }, - { ST_ISM330DHCX_DEV_NAME, ST_ISM330DHCX_ID }, - { ST_LSM9DS1_DEV_NAME, ST_LSM9DS1_ID }, - { ST_LSM6DS0_DEV_NAME, ST_LSM6DS0_ID }, - { ST_LSM6DSRX_DEV_NAME, ST_LSM6DSRX_ID }, - { ST_LSM6DST_DEV_NAME, ST_LSM6DST_ID }, - { ST_LSM6DSOP_DEV_NAME, ST_LSM6DSOP_ID }, - { ST_ASM330LHHX_DEV_NAME, ST_ASM330LHHX_ID }, - { ST_LSM6DSTX_DEV_NAME, ST_LSM6DSTX_ID }, - { ST_LSM6DSV_DEV_NAME, ST_LSM6DSV_ID }, - { ST_LSM6DSV16X_DEV_NAME, ST_LSM6DSV16X_ID }, - { ST_LSM6DSO16IS_DEV_NAME, ST_LSM6DSO16IS_ID }, - { ST_ISM330IS_DEV_NAME, ST_ISM330IS_ID }, - { ST_ASM330LHB_DEV_NAME, ST_ASM330LHB_ID }, - { ST_ASM330LHHXG1_DEV_NAME, ST_ASM330LHHXG1_ID }, + { .name = ST_LSM6DS3_DEV_NAME, .driver_data = ST_LSM6DS3_ID }, + { .name = ST_LSM6DS3H_DEV_NAME, .driver_data = ST_LSM6DS3H_ID }, + { .name = ST_LSM6DSL_DEV_NAME, .driver_data = ST_LSM6DSL_ID }, + { .name = ST_LSM6DSM_DEV_NAME, .driver_data = ST_LSM6DSM_ID }, + { .name = ST_ISM330DLC_DEV_NAME, .driver_data = ST_ISM330DLC_ID }, + { .name = ST_LSM6DSO_DEV_NAME, .driver_data = ST_LSM6DSO_ID }, + { .name = ST_ASM330LHH_DEV_NAME, .driver_data = ST_ASM330LHH_ID }, + { .name = ST_LSM6DSOX_DEV_NAME, .driver_data = ST_LSM6DSOX_ID }, + { .name = ST_LSM6DSR_DEV_NAME, .driver_data = ST_LSM6DSR_ID }, + { .name = ST_LSM6DS3TRC_DEV_NAME, .driver_data = ST_LSM6DS3TRC_ID }, + { .name = ST_ISM330DHCX_DEV_NAME, .driver_data = ST_ISM330DHCX_ID }, + { .name = ST_LSM9DS1_DEV_NAME, .driver_data = ST_LSM9DS1_ID }, + { .name = ST_LSM6DS0_DEV_NAME, .driver_data = ST_LSM6DS0_ID }, + { .name = ST_LSM6DSRX_DEV_NAME, .driver_data = ST_LSM6DSRX_ID }, + { .name = ST_LSM6DST_DEV_NAME, .driver_data = ST_LSM6DST_ID }, + { .name = ST_LSM6DSOP_DEV_NAME, .driver_data = ST_LSM6DSOP_ID }, + { .name = ST_ASM330LHHX_DEV_NAME, .driver_data = ST_ASM330LHHX_ID }, + { .name = ST_LSM6DSTX_DEV_NAME, .driver_data = ST_LSM6DSTX_ID }, + { .name = ST_LSM6DSV_DEV_NAME, .driver_data = ST_LSM6DSV_ID }, + { .name = ST_LSM6DSV16X_DEV_NAME, .driver_data = ST_LSM6DSV16X_ID }, + { .name = ST_LSM6DSO16IS_DEV_NAME, .driver_data = ST_LSM6DSO16IS_ID }, + { .name = ST_ISM330IS_DEV_NAME, .driver_data = ST_ISM330IS_ID }, + { .name = ST_ASM330LHB_DEV_NAME, .driver_data = ST_ASM330LHB_ID }, + { .name = ST_ASM330LHHXG1_DEV_NAME, .driver_data = ST_ASM330LHHXG1_ID }, { } }; MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c index acea8a0757d7..9b5989aa51aa 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c @@ -33,8 +33,8 @@ static const struct of_device_id st_lsm9ds0_of_match[] = { MODULE_DEVICE_TABLE(of, st_lsm9ds0_of_match); static const struct spi_device_id st_lsm9ds0_id_table[] = { - { LSM303D_IMU_DEV_NAME }, - { LSM9DS0_IMU_DEV_NAME }, + { .name = LSM303D_IMU_DEV_NAME }, + { .name = LSM9DS0_IMU_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, st_lsm9ds0_id_table); diff --git a/drivers/iio/light/st_uvis25_spi.c b/drivers/iio/light/st_uvis25_spi.c index a5aad74ce73e..f29831f4e2e5 100644 --- a/drivers/iio/light/st_uvis25_spi.c +++ b/drivers/iio/light/st_uvis25_spi.c @@ -47,7 +47,7 @@ static const struct of_device_id st_uvis25_spi_of_match[] = { MODULE_DEVICE_TABLE(of, st_uvis25_spi_of_match); static const struct spi_device_id st_uvis25_spi_id_table[] = { - { ST_UVIS25_DEV_NAME }, + { .name = ST_UVIS25_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, st_uvis25_spi_id_table); diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index 68816362bb95..9697edabf879 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -88,12 +88,12 @@ static int st_magn_spi_probe(struct spi_device *spi) } static const struct spi_device_id st_magn_id_table[] = { - { LIS3MDL_MAGN_DEV_NAME }, - { LSM303AGR_MAGN_DEV_NAME }, - { LIS2MDL_MAGN_DEV_NAME }, - { LSM9DS1_MAGN_DEV_NAME }, - { IIS2MDC_MAGN_DEV_NAME }, - { LSM303C_MAGN_DEV_NAME }, + { .name = LIS3MDL_MAGN_DEV_NAME }, + { .name = LSM303AGR_MAGN_DEV_NAME }, + { .name = LIS2MDL_MAGN_DEV_NAME }, + { .name = LSM9DS1_MAGN_DEV_NAME }, + { .name = IIS2MDC_MAGN_DEV_NAME }, + { .name = LSM303C_MAGN_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(spi, st_magn_id_table); diff --git a/drivers/iio/potentiometer/max5481.c b/drivers/iio/potentiometer/max5481.c index b40e5ac218d7..7c5636840931 100644 --- a/drivers/iio/potentiometer/max5481.c +++ b/drivers/iio/potentiometer/max5481.c @@ -170,10 +170,10 @@ static int max5481_probe(struct spi_device *spi) } static const struct spi_device_id max5481_id_table[] = { - { "max5481", max5481 }, - { "max5482", max5482 }, - { "max5483", max5483 }, - { "max5484", max5484 }, + { .name = "max5481", .driver_data = max5481 }, + { .name = "max5482", .driver_data = max5482 }, + { .name = "max5483", .driver_data = max5483 }, + { .name = "max5484", .driver_data = max5484 }, { } }; MODULE_DEVICE_TABLE(spi, max5481_id_table); diff --git a/drivers/iio/potentiometer/max5487.c b/drivers/iio/potentiometer/max5487.c index 3b11b991940b..9fa11dce4b06 100644 --- a/drivers/iio/potentiometer/max5487.c +++ b/drivers/iio/potentiometer/max5487.c @@ -126,9 +126,9 @@ static void max5487_spi_remove(struct spi_device *spi) } static const struct spi_device_id max5487_id[] = { - { "MAX5487", 10 }, - { "MAX5488", 50 }, - { "MAX5489", 100 }, + { .name = "MAX5487", .driver_data = 10 }, + { .name = "MAX5488", .driver_data = 50 }, + { .name = "MAX5489", .driver_data = 100 }, { } }; MODULE_DEVICE_TABLE(spi, max5487_id); diff --git a/drivers/iio/potentiometer/mcp41010.c b/drivers/iio/potentiometer/mcp41010.c index f35fc4a6c55b..0b404dbb77ce 100644 --- a/drivers/iio/potentiometer/mcp41010.c +++ b/drivers/iio/potentiometer/mcp41010.c @@ -176,12 +176,12 @@ static const struct of_device_id mcp41010_match[] = { MODULE_DEVICE_TABLE(of, mcp41010_match); static const struct spi_device_id mcp41010_id[] = { - { "mcp41010", MCP41010 }, - { "mcp41050", MCP41050 }, - { "mcp41100", MCP41100 }, - { "mcp42010", MCP42010 }, - { "mcp42050", MCP42050 }, - { "mcp42100", MCP42100 }, + { .name = "mcp41010", .driver_data = MCP41010 }, + { .name = "mcp41050", .driver_data = MCP41050 }, + { .name = "mcp41100", .driver_data = MCP41100 }, + { .name = "mcp42010", .driver_data = MCP42010 }, + { .name = "mcp42050", .driver_data = MCP42050 }, + { .name = "mcp42100", .driver_data = MCP42100 }, { } }; MODULE_DEVICE_TABLE(spi, mcp41010_id); diff --git a/drivers/iio/potentiometer/mcp4131.c b/drivers/iio/potentiometer/mcp4131.c index 56c9111ef5e8..4d7731b21a52 100644 --- a/drivers/iio/potentiometer/mcp4131.c +++ b/drivers/iio/potentiometer/mcp4131.c @@ -407,70 +407,70 @@ static const struct of_device_id mcp4131_dt_ids[] = { MODULE_DEVICE_TABLE(of, mcp4131_dt_ids); static const struct spi_device_id mcp4131_id[] = { - { "mcp4131-502", MCP413x_502 }, - { "mcp4131-103", MCP413x_103 }, - { "mcp4131-503", MCP413x_503 }, - { "mcp4131-104", MCP413x_104 }, - { "mcp4132-502", MCP413x_502 }, - { "mcp4132-103", MCP413x_103 }, - { "mcp4132-503", MCP413x_503 }, - { "mcp4132-104", MCP413x_104 }, - { "mcp4141-502", MCP414x_502 }, - { "mcp4141-103", MCP414x_103 }, - { "mcp4141-503", MCP414x_503 }, - { "mcp4141-104", MCP414x_104 }, - { "mcp4142-502", MCP414x_502 }, - { "mcp4142-103", MCP414x_103 }, - { "mcp4142-503", MCP414x_503 }, - { "mcp4142-104", MCP414x_104 }, - { "mcp4151-502", MCP415x_502 }, - { "mcp4151-103", MCP415x_103 }, - { "mcp4151-503", MCP415x_503 }, - { "mcp4151-104", MCP415x_104 }, - { "mcp4152-502", MCP415x_502 }, - { "mcp4152-103", MCP415x_103 }, - { "mcp4152-503", MCP415x_503 }, - { "mcp4152-104", MCP415x_104 }, - { "mcp4161-502", MCP416x_502 }, - { "mcp4161-103", MCP416x_103 }, - { "mcp4161-503", MCP416x_503 }, - { "mcp4161-104", MCP416x_104 }, - { "mcp4162-502", MCP416x_502 }, - { "mcp4162-103", MCP416x_103 }, - { "mcp4162-503", MCP416x_503 }, - { "mcp4162-104", MCP416x_104 }, - { "mcp4231-502", MCP423x_502 }, - { "mcp4231-103", MCP423x_103 }, - { "mcp4231-503", MCP423x_503 }, - { "mcp4231-104", MCP423x_104 }, - { "mcp4232-502", MCP423x_502 }, - { "mcp4232-103", MCP423x_103 }, - { "mcp4232-503", MCP423x_503 }, - { "mcp4232-104", MCP423x_104 }, - { "mcp4241-502", MCP424x_502 }, - { "mcp4241-103", MCP424x_103 }, - { "mcp4241-503", MCP424x_503 }, - { "mcp4241-104", MCP424x_104 }, - { "mcp4242-502", MCP424x_502 }, - { "mcp4242-103", MCP424x_103 }, - { "mcp4242-503", MCP424x_503 }, - { "mcp4242-104", MCP424x_104 }, - { "mcp4251-502", MCP425x_502 }, - { "mcp4251-103", MCP425x_103 }, - { "mcp4251-503", MCP425x_503 }, - { "mcp4251-104", MCP425x_104 }, - { "mcp4252-502", MCP425x_502 }, - { "mcp4252-103", MCP425x_103 }, - { "mcp4252-503", MCP425x_503 }, - { "mcp4252-104", MCP425x_104 }, - { "mcp4261-502", MCP426x_502 }, - { "mcp4261-103", MCP426x_103 }, - { "mcp4261-503", MCP426x_503 }, - { "mcp4261-104", MCP426x_104 }, - { "mcp4262-502", MCP426x_502 }, - { "mcp4262-103", MCP426x_103 }, - { "mcp4262-503", MCP426x_503 }, - { "mcp4262-104", MCP426x_104 }, + { .name = "mcp4131-502", .driver_data = MCP413x_502 }, + { .name = "mcp4131-103", .driver_data = MCP413x_103 }, + { .name = "mcp4131-503", .driver_data = MCP413x_503 }, + { .name = "mcp4131-104", .driver_data = MCP413x_104 }, + { .name = "mcp4132-502", .driver_data = MCP413x_502 }, + { .name = "mcp4132-103", .driver_data = MCP413x_103 }, + { .name = "mcp4132-503", .driver_data = MCP413x_503 }, + { .name = "mcp4132-104", .driver_data = MCP413x_104 }, + { .name = "mcp4141-502", .driver_data = MCP414x_502 }, + { .name = "mcp4141-103", .driver_data = MCP414x_103 }, + { .name = "mcp4141-503", .driver_data = MCP414x_503 }, + { .name = "mcp4141-104", .driver_data = MCP414x_104 }, + { .name = "mcp4142-502", .driver_data = MCP414x_502 }, + { .name = "mcp4142-103", .driver_data = MCP414x_103 }, + { .name = "mcp4142-503", .driver_data = MCP414x_503 }, + { .name = "mcp4142-104", .driver_data = MCP414x_104 }, + { .name = "mcp4151-502", .driver_data = MCP415x_502 }, + { .name = "mcp4151-103", .driver_data = MCP415x_103 }, + { .name = "mcp4151-503", .driver_data = MCP415x_503 }, + { .name = "mcp4151-104", .driver_data = MCP415x_104 }, + { .name = "mcp4152-502", .driver_data = MCP415x_502 }, + { .name = "mcp4152-103", .driver_data = MCP415x_103 }, + { .name = "mcp4152-503", .driver_data = MCP415x_503 }, + { .name = "mcp4152-104", .driver_data = MCP415x_104 }, + { .name = "mcp4161-502", .driver_data = MCP416x_502 }, + { .name = "mcp4161-103", .driver_data = MCP416x_103 }, + { .name = "mcp4161-503", .driver_data = MCP416x_503 }, + { .name = "mcp4161-104", .driver_data = MCP416x_104 }, + { .name = "mcp4162-502", .driver_data = MCP416x_502 }, + { .name = "mcp4162-103", .driver_data = MCP416x_103 }, + { .name = "mcp4162-503", .driver_data = MCP416x_503 }, + { .name = "mcp4162-104", .driver_data = MCP416x_104 }, + { .name = "mcp4231-502", .driver_data = MCP423x_502 }, + { .name = "mcp4231-103", .driver_data = MCP423x_103 }, + { .name = "mcp4231-503", .driver_data = MCP423x_503 }, + { .name = "mcp4231-104", .driver_data = MCP423x_104 }, + { .name = "mcp4232-502", .driver_data = MCP423x_502 }, + { .name = "mcp4232-103", .driver_data = MCP423x_103 }, + { .name = "mcp4232-503", .driver_data = MCP423x_503 }, + { .name = "mcp4232-104", .driver_data = MCP423x_104 }, + { .name = "mcp4241-502", .driver_data = MCP424x_502 }, + { .name = "mcp4241-103", .driver_data = MCP424x_103 }, + { .name = "mcp4241-503", .driver_data = MCP424x_503 }, + { .name = "mcp4241-104", .driver_data = MCP424x_104 }, + { .name = "mcp4242-502", .driver_data = MCP424x_502 }, + { .name = "mcp4242-103", .driver_data = MCP424x_103 }, + { .name = "mcp4242-503", .driver_data = MCP424x_503 }, + { .name = "mcp4242-104", .driver_data = MCP424x_104 }, + { .name = "mcp4251-502", .driver_data = MCP425x_502 }, + { .name = "mcp4251-103", .driver_data = MCP425x_103 }, + { .name = "mcp4251-503", .driver_data = MCP425x_503 }, + { .name = "mcp4251-104", .driver_data = MCP425x_104 }, + { .name = "mcp4252-502", .driver_data = MCP425x_502 }, + { .name = "mcp4252-103", .driver_data = MCP425x_103 }, + { .name = "mcp4252-503", .driver_data = MCP425x_503 }, + { .name = "mcp4252-104", .driver_data = MCP425x_104 }, + { .name = "mcp4261-502", .driver_data = MCP426x_502 }, + { .name = "mcp4261-103", .driver_data = MCP426x_103 }, + { .name = "mcp4261-503", .driver_data = MCP426x_503 }, + { .name = "mcp4261-104", .driver_data = MCP426x_104 }, + { .name = "mcp4262-502", .driver_data = MCP426x_502 }, + { .name = "mcp4262-103", .driver_data = MCP426x_103 }, + { .name = "mcp4262-503", .driver_data = MCP426x_503 }, + { .name = "mcp4262-104", .driver_data = MCP426x_104 }, { } }; MODULE_DEVICE_TABLE(spi, mcp4131_id); diff --git a/drivers/iio/potentiometer/x9250.c b/drivers/iio/potentiometer/x9250.c index 735348492699..974e4447fc2c 100644 --- a/drivers/iio/potentiometer/x9250.c +++ b/drivers/iio/potentiometer/x9250.c @@ -198,8 +198,8 @@ static const struct of_device_id x9250_of_match[] = { MODULE_DEVICE_TABLE(of, x9250_of_match); static const struct spi_device_id x9250_id_table[] = { - { "x9250t", (kernel_ulong_t)&x9250_cfg[X9250T] }, - { "x9250u", (kernel_ulong_t)&x9250_cfg[X9250U] }, + { .name = "x9250t", .driver_data = (kernel_ulong_t)&x9250_cfg[X9250T] }, + { .name = "x9250u", .driver_data = (kernel_ulong_t)&x9250_cfg[X9250U] }, { } }; MODULE_DEVICE_TABLE(spi, x9250_id_table); diff --git a/drivers/iio/pressure/abp2030pa_spi.c b/drivers/iio/pressure/abp2030pa_spi.c index eaea9a3ebf11..b5cbd2784657 100644 --- a/drivers/iio/pressure/abp2030pa_spi.c +++ b/drivers/iio/pressure/abp2030pa_spi.c @@ -46,7 +46,7 @@ static const struct of_device_id abp2_spi_match[] = { MODULE_DEVICE_TABLE(of, abp2_spi_match); static const struct spi_device_id abp2_spi_id[] = { - { "abp2030pa" }, + { .name = "abp2030pa" }, { } }; MODULE_DEVICE_TABLE(spi, abp2_spi_id); diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c index 04bf2f5be5b1..a02a621b8015 100644 --- a/drivers/iio/pressure/bmp280-spi.c +++ b/drivers/iio/pressure/bmp280-spi.c @@ -118,13 +118,13 @@ static const struct of_device_id bmp280_of_spi_match[] = { MODULE_DEVICE_TABLE(of, bmp280_of_spi_match); static const struct spi_device_id bmp280_spi_id[] = { - { "bmp085", (kernel_ulong_t)&bmp085_chip_info }, - { "bmp180", (kernel_ulong_t)&bmp180_chip_info }, - { "bmp181", (kernel_ulong_t)&bmp180_chip_info }, - { "bmp280", (kernel_ulong_t)&bmp280_chip_info }, - { "bme280", (kernel_ulong_t)&bme280_chip_info }, - { "bmp380", (kernel_ulong_t)&bmp380_chip_info }, - { "bmp580", (kernel_ulong_t)&bmp580_chip_info }, + { .name = "bmp085", .driver_data = (kernel_ulong_t)&bmp085_chip_info }, + { .name = "bmp180", .driver_data = (kernel_ulong_t)&bmp180_chip_info }, + { .name = "bmp181", .driver_data = (kernel_ulong_t)&bmp180_chip_info }, + { .name = "bmp280", .driver_data = (kernel_ulong_t)&bmp280_chip_info }, + { .name = "bme280", .driver_data = (kernel_ulong_t)&bme280_chip_info }, + { .name = "bmp380", .driver_data = (kernel_ulong_t)&bmp380_chip_info }, + { .name = "bmp580", .driver_data = (kernel_ulong_t)&bmp580_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, bmp280_spi_id); diff --git a/drivers/iio/pressure/hsc030pa_spi.c b/drivers/iio/pressure/hsc030pa_spi.c index 5d331b3b6da8..6cddd248916b 100644 --- a/drivers/iio/pressure/hsc030pa_spi.c +++ b/drivers/iio/pressure/hsc030pa_spi.c @@ -40,7 +40,7 @@ static const struct of_device_id hsc_spi_match[] = { MODULE_DEVICE_TABLE(of, hsc_spi_match); static const struct spi_device_id hsc_spi_id[] = { - { "hsc030pa" }, + { .name = "hsc030pa" }, { } }; MODULE_DEVICE_TABLE(spi, hsc_spi_id); diff --git a/drivers/iio/pressure/mprls0025pa_spi.c b/drivers/iio/pressure/mprls0025pa_spi.c index 8c8c726f703f..f8ab3f64f010 100644 --- a/drivers/iio/pressure/mprls0025pa_spi.c +++ b/drivers/iio/pressure/mprls0025pa_spi.c @@ -60,7 +60,7 @@ static const struct of_device_id mpr_spi_match[] = { MODULE_DEVICE_TABLE(of, mpr_spi_match); static const struct spi_device_id mpr_spi_id[] = { - { "mprls0025pa" }, + { .name = "mprls0025pa" }, { } }; MODULE_DEVICE_TABLE(spi, mpr_spi_id); diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c index 25c7bd2d8fdf..69efd194f337 100644 --- a/drivers/iio/pressure/ms5611_spi.c +++ b/drivers/iio/pressure/ms5611_spi.c @@ -114,8 +114,8 @@ static const struct of_device_id ms5611_spi_matches[] = { MODULE_DEVICE_TABLE(of, ms5611_spi_matches); static const struct spi_device_id ms5611_id[] = { - { "ms5611", MS5611 }, - { "ms5607", MS5607 }, + { .name = "ms5611", .driver_data = MS5611 }, + { .name = "ms5607", .driver_data = MS5607 }, { } }; MODULE_DEVICE_TABLE(spi, ms5611_id); diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index 39827e6841ca..4af9ecd46d51 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -94,18 +94,18 @@ static int st_press_spi_probe(struct spi_device *spi) } static const struct spi_device_id st_press_id_table[] = { - { LPS001WP_PRESS_DEV_NAME }, - { LPS25H_PRESS_DEV_NAME }, - { LPS331AP_PRESS_DEV_NAME }, - { LPS22HB_PRESS_DEV_NAME }, - { LPS33HW_PRESS_DEV_NAME }, - { LPS35HW_PRESS_DEV_NAME }, - { LPS22HH_PRESS_DEV_NAME }, - { LPS22DF_PRESS_DEV_NAME }, - { "lps001wp-press" }, - { "lps25h-press", }, - { "lps331ap-press" }, - { "lps22hb-press" }, + { .name = LPS001WP_PRESS_DEV_NAME }, + { .name = LPS25H_PRESS_DEV_NAME }, + { .name = LPS331AP_PRESS_DEV_NAME }, + { .name = LPS22HB_PRESS_DEV_NAME }, + { .name = LPS33HW_PRESS_DEV_NAME }, + { .name = LPS35HW_PRESS_DEV_NAME }, + { .name = LPS22HH_PRESS_DEV_NAME }, + { .name = LPS22DF_PRESS_DEV_NAME }, + { .name = "lps001wp-press" }, + { .name = "lps25h-press" }, + { .name = "lps331ap-press" }, + { .name = "lps22hb-press" }, { } }; MODULE_DEVICE_TABLE(spi, st_press_id_table); diff --git a/drivers/iio/resolver/ad2s1200.c b/drivers/iio/resolver/ad2s1200.c index c00a60cb31a5..671e4db7efb4 100644 --- a/drivers/iio/resolver/ad2s1200.c +++ b/drivers/iio/resolver/ad2s1200.c @@ -183,8 +183,8 @@ static const struct of_device_id ad2s1200_of_match[] = { MODULE_DEVICE_TABLE(of, ad2s1200_of_match); static const struct spi_device_id ad2s1200_id[] = { - { "ad2s1200" }, - { "ad2s1205" }, + { .name = "ad2s1200" }, + { .name = "ad2s1205" }, { } }; MODULE_DEVICE_TABLE(spi, ad2s1200_id); diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c index 1be19fe8aa3f..db8e34e2ba73 100644 --- a/drivers/iio/resolver/ad2s1210.c +++ b/drivers/iio/resolver/ad2s1210.c @@ -1597,7 +1597,7 @@ static const struct of_device_id ad2s1210_of_match[] = { MODULE_DEVICE_TABLE(of, ad2s1210_of_match); static const struct spi_device_id ad2s1210_id[] = { - { "ad2s1210" }, + { .name = "ad2s1210" }, { } }; MODULE_DEVICE_TABLE(spi, ad2s1210_id); diff --git a/drivers/iio/resolver/ad2s90.c b/drivers/iio/resolver/ad2s90.c index 18f1c905eeac..b7a2dcc42871 100644 --- a/drivers/iio/resolver/ad2s90.c +++ b/drivers/iio/resolver/ad2s90.c @@ -110,7 +110,7 @@ static const struct of_device_id ad2s90_of_match[] = { MODULE_DEVICE_TABLE(of, ad2s90_of_match); static const struct spi_device_id ad2s90_id[] = { - { "ad2s90" }, + { .name = "ad2s90" }, { } }; MODULE_DEVICE_TABLE(spi, ad2s90_id); diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index fc65d8352d12..6bf710493d3f 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -2071,11 +2071,11 @@ static const struct ltc2983_chip_info ltm2985_chip_info_data = { }; static const struct spi_device_id ltc2983_id_table[] = { - { "adt7604", (kernel_ulong_t)&adt7604_chip_info_data }, - { "ltc2983", (kernel_ulong_t)<c2983_chip_info_data }, - { "ltc2984", (kernel_ulong_t)<c2984_chip_info_data }, - { "ltc2986", (kernel_ulong_t)<c2986_chip_info_data }, - { "ltm2985", (kernel_ulong_t)<m2985_chip_info_data }, + { .name = "adt7604", .driver_data = (kernel_ulong_t)&adt7604_chip_info_data }, + { .name = "ltc2983", .driver_data = (kernel_ulong_t)<c2983_chip_info_data }, + { .name = "ltc2984", .driver_data = (kernel_ulong_t)<c2984_chip_info_data }, + { .name = "ltc2986", .driver_data = (kernel_ulong_t)<c2986_chip_info_data }, + { .name = "ltm2985", .driver_data = (kernel_ulong_t)<m2985_chip_info_data }, { } }; MODULE_DEVICE_TABLE(spi, ltc2983_id_table); diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c index e898f56d1196..6b8adff42fd1 100644 --- a/drivers/iio/temperature/maxim_thermocouple.c +++ b/drivers/iio/temperature/maxim_thermocouple.c @@ -263,15 +263,15 @@ static int maxim_thermocouple_probe(struct spi_device *spi) } static const struct spi_device_id maxim_thermocouple_id[] = { - {"max6675", MAX6675}, - {"max31855", MAX31855}, - {"max31855k", MAX31855K}, - {"max31855j", MAX31855J}, - {"max31855n", MAX31855N}, - {"max31855s", MAX31855S}, - {"max31855t", MAX31855T}, - {"max31855e", MAX31855E}, - {"max31855r", MAX31855R}, + { .name = "max6675", .driver_data = MAX6675 }, + { .name = "max31855", .driver_data = MAX31855 }, + { .name = "max31855k", .driver_data = MAX31855K }, + { .name = "max31855j", .driver_data = MAX31855J }, + { .name = "max31855n", .driver_data = MAX31855N }, + { .name = "max31855s", .driver_data = MAX31855S }, + { .name = "max31855t", .driver_data = MAX31855T }, + { .name = "max31855e", .driver_data = MAX31855E }, + { .name = "max31855r", .driver_data = MAX31855R }, { } }; MODULE_DEVICE_TABLE(spi, maxim_thermocouple_id); From 91cf4be76708e9880f40c91ca1cb7b266334c332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 19 Jun 2026 17:54:40 +0200 Subject: [PATCH 073/513] staging: iio: Drop unused assignment of spi_device_id driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drivers explicitly set the .driver_data member of struct spi_device_id to zero without relying on that value. Drop these unused assignments. While touching these arrays use named initializers for .name. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/staging/iio/addac/adt7316-spi.c | 13 ++++++------- drivers/staging/iio/frequency/ad9832.c | 4 ++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/staging/iio/addac/adt7316-spi.c b/drivers/staging/iio/addac/adt7316-spi.c index 1debcc36c1af..459dd7416b1d 100644 --- a/drivers/staging/iio/addac/adt7316-spi.c +++ b/drivers/staging/iio/addac/adt7316-spi.c @@ -126,15 +126,14 @@ static int adt7316_spi_probe(struct spi_device *spi_dev) } static const struct spi_device_id adt7316_spi_id[] = { - { "adt7316", 0 }, - { "adt7317", 0 }, - { "adt7318", 0 }, - { "adt7516", 0 }, - { "adt7517", 0 }, - { "adt7519", 0 }, + { .name = "adt7316" }, + { .name = "adt7317" }, + { .name = "adt7318" }, + { .name = "adt7516" }, + { .name = "adt7517" }, + { .name = "adt7519" }, { } }; - MODULE_DEVICE_TABLE(spi, adt7316_spi_id); static const struct of_device_id adt7316_of_spi_match[] = { diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c index 659821a1e2cb..1a6666c1948e 100644 --- a/drivers/staging/iio/frequency/ad9832.c +++ b/drivers/staging/iio/frequency/ad9832.c @@ -381,8 +381,8 @@ static const struct of_device_id ad9832_of_match[] = { MODULE_DEVICE_TABLE(of, ad9832_of_match); static const struct spi_device_id ad9832_id[] = { - {"ad9832", 0}, - {"ad9835", 0}, + { .name = "ad9832" }, + { .name = "ad9835" }, { } }; MODULE_DEVICE_TABLE(spi, ad9832_id); From d68399de723fcedfad97702d1083dda7ff4df8e9 Mon Sep 17 00:00:00 2001 From: Sai Krishna Potthuri Date: Mon, 29 Jun 2026 19:36:15 +0530 Subject: [PATCH 074/513] MAINTAINERS: Update Xilinx AMS driver maintainers Salih Erim is no longer with AMD to maintain the Xilinx AMS driver. Replace Salih Erim with Sai Krishna Potthuri in the Xilinx AMS driver MAINTAINERS entry for continued development and maintenance of the driver. Signed-off-by: Sai Krishna Potthuri Acked-by: Michal Simek Acked-by: "O'Griofa, Conall" Signed-off-by: Jonathan Cameron --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 0dde85039a14..bf8f3f565fda 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -29521,7 +29521,7 @@ F: include/uapi/linux/dqblk_xfs.h F: include/uapi/linux/fsmap.h XILINX AMS DRIVER -M: Salih Erim +M: Sai Krishna Potthuri M: Conall O'Griofa L: linux-iio@vger.kernel.org S: Maintained From e99f2ceb8a571920c43291fab1225b6575e4c377 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Sun, 28 Jun 2026 15:08:08 +0100 Subject: [PATCH 075/513] dt-bindings: iio: dac: ad5696: add reset/ldac/gain support Add GPIO property for RESET, LDAC and GAIN pin. RESET is active-low, LDAC is used to load DAC channels with values from input registers and GAIN can double the voltage in output channels. The gain-gpios property is not available to all supported parts. The adi,range-double property indicates that GAIN pin is hardwired to high in case gain-gpios is not set, otherwise it sets the initial value for the gain setting. Acked-by: Conor Dooley Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- .../bindings/iio/dac/adi,ad5696.yaml | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml index b5a88b03dc2f..cc343cdf6085 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml @@ -37,14 +37,52 @@ properties: description: | The regulator supply for DAC reference voltage. + reset-gpios: + description: Active-low RESET pin to reset the device. + maxItems: 1 + + ldac-gpios: + description: + Active-low LDAC pin used to asynchronously update the DAC channels. + maxItems: 1 + + gain-gpios: + description: + GAIN pin that sets a multiplier for the DAC output voltage. When high, + the DAC output voltage is multiplied by 2, otherwise it is unchanged. + maxItems: 1 + + adi,range-double: + description: + Sets the initial voltage output range from 0 to 2xVREF. On devices that + have a GAIN pin and no gain-gpios property is set, this indicates the pin + is hardwired high. + type: boolean + required: - compatible - reg +allOf: + - if: + properties: + compatible: + contains: + enum: + - adi,ad5311r + - adi,ad5691r + - adi,ad5692r + - adi,ad5693 + - adi,ad5693r + then: + properties: + gain-gpios: false + additionalProperties: false examples: - | + #include i2c { #address-cells = <1>; #size-cells = <0>; @@ -53,6 +91,7 @@ examples: compatible = "adi,ad5696"; reg = <0>; vcc-supply = <&dac_vref>; + ldac-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; }; }; ... From 5b78dd16e56a4c2737e35e73fa559dfd985a6163 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Sun, 28 Jun 2026 15:08:09 +0100 Subject: [PATCH 076/513] dt-bindings: iio: dac: ad5696: rework on power supplies Add supplies for VDD, VLOGIC and VREF input voltage pins. The vcc-supply property is deprecated, once it does not really exist as none of the devices describe any power input with that name. VCC is also misleading as it sounds like the input power supply, but it is being used as an external voltage reference, which should be called VREF. Certain devices require vref-supply to be available once an internal reference voltage is absent. For correct operation vdd and vlogic supplies are required. Acked-by: Conor Dooley Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- .../bindings/iio/dac/adi,ad5696.yaml | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml index cc343cdf6085..e10f8596f9d3 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml @@ -33,9 +33,22 @@ properties: reg: maxItems: 1 + vdd-supply: + description: Input power supply. + + vlogic-supply: + description: + Digital power supply. On some tiny package variants for single-channel + devices, this supply is internally connected to vdd; in that case, specify + this property with the same regulator as vdd. + + vref-supply: + description: + Reference voltage supply. If not supplied the internal reference is used. + vcc-supply: - description: | - The regulator supply for DAC reference voltage. + deprecated: true + description: Use vref-supply instead. reset-gpios: description: Active-low RESET pin to reset the device. @@ -62,8 +75,21 @@ properties: required: - compatible - reg + - vdd-supply + - vlogic-supply allOf: + - if: + properties: + compatible: + contains: + enum: + - adi,ad5693 + - adi,ad5694 + - adi,ad5696 + then: + required: + - vref-supply - if: properties: compatible: @@ -90,7 +116,9 @@ examples: ad5696: dac@0 { compatible = "adi,ad5696"; reg = <0>; - vcc-supply = <&dac_vref>; + vdd-supply = <&dac_vdd>; + vlogic-supply = <&dac_vlogic>; + vref-supply = <&dac_vref>; ldac-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; }; }; From 1c110ae2ba4fea247ea724ae7f263c19c5e03a01 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Sun, 28 Jun 2026 15:08:10 +0100 Subject: [PATCH 077/513] dt-bindings: iio: dac: ad5686: add reset/ldac/gain support Add GPIO property for RESET, LDAC and GAIN pin. RESET is active-low, LDAC is used to load DAC channels with values from input registers and GAIN can double the voltage in output channels. The gain-gpios property is not available to all supported parts. The adi,range-double property indicates that GAIN pin is hardwired to high in case gain-gpios is not set, otherwise it sets the initial value for the gain setting. Acked-by: Conor Dooley Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- .../bindings/iio/dac/adi,ad5686.yaml | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml index 713f535bb33a..d781baca6a6c 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml @@ -35,17 +35,53 @@ properties: vcc-supply: description: If not supplied the internal reference is used. + reset-gpios: + description: Active-low RESET pin to reset the device. + maxItems: 1 + + ldac-gpios: + description: + Active-low LDAC pin used to asynchronously update the DAC channels. + maxItems: 1 + + gain-gpios: + description: + GAIN pin that sets a multiplier for the DAC output voltage. When high, + the DAC output voltage is multiplied by 2, otherwise it is unchanged. + maxItems: 1 + + adi,range-double: + description: + Sets the initial voltage output range from 0 to 2xVREF. On devices that + have a GAIN pin and no gain-gpios property is set, this indicates the pin + is hardwired high. + type: boolean + required: - compatible - reg allOf: - $ref: /schemas/spi/spi-peripheral-props.yaml# + - if: + properties: + compatible: + contains: + enum: + - adi,ad5310r + - adi,ad5681r + - adi,ad5682r + - adi,ad5683 + - adi,ad5683r + then: + properties: + gain-gpios: false unevaluatedProperties: false examples: - | + #include spi { #address-cells = <1>; #size-cells = <0>; @@ -53,6 +89,8 @@ examples: reg = <0>; compatible = "adi,ad5310r"; vcc-supply = <&dac_vref0>; + reset-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>; + ldac-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; }; }; ... From 692d2294c268efa0b5ab7076e0ae272a1ec17c2e Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Sun, 28 Jun 2026 15:08:11 +0100 Subject: [PATCH 078/513] dt-bindings: iio: dac: ad5686: rework on power supplies Add supplies for VDD, VLOGIC and VREF input voltage pins. The vcc-supply property is deprecated, once it does not really exist as none of the devices describe any power input with that name. VCC is also misleading as it sounds like the input power supply, but it is being used as an external voltage reference, which should be called VREF. Certain devices require vref-supply to be available once an internal reference voltage is absent. For correct operation vdd and vlogic supplies are required. Acked-by: Conor Dooley Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- .../bindings/iio/dac/adi,ad5686.yaml | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml index d781baca6a6c..02e8c78e36d3 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml @@ -32,8 +32,22 @@ properties: reg: maxItems: 1 + vdd-supply: + description: Input power supply. + + vlogic-supply: + description: + Digital power supply. On some tiny package variants for single-channel + devices, this supply is internally connected to vdd; in that case, specify + this property with the same regulator as vdd. + + vref-supply: + description: + Reference voltage supply. If not supplied the internal reference is used. + vcc-supply: - description: If not supplied the internal reference is used. + deprecated: true + description: Use vref-supply instead. reset-gpios: description: Active-low RESET pin to reset the device. @@ -60,9 +74,23 @@ properties: required: - compatible - reg + - vdd-supply + - vlogic-supply allOf: - $ref: /schemas/spi/spi-peripheral-props.yaml# + - if: + properties: + compatible: + contains: + enum: + - adi,ad5676 + - adi,ad5683 + - adi,ad5684 + - adi,ad5686 + then: + required: + - vref-supply - if: properties: compatible: @@ -88,7 +116,9 @@ examples: dac@0 { reg = <0>; compatible = "adi,ad5310r"; - vcc-supply = <&dac_vref0>; + vdd-supply = <&dac_vdd>; + vlogic-supply = <&dac_vlogic>; + vref-supply = <&dac_vref>; reset-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>; ldac-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; }; From 4dd5a69dbe214a039cea46f237b2426f9318b3e1 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Sun, 28 Jun 2026 15:08:12 +0100 Subject: [PATCH 079/513] iio: dac: ad5686: add support for missing power supplies Get and enable regulators for vdd, vlogic and vref input power pins. Vdd is the input power supply, while vlogic powers the digital side. vref is replacing vcc, which is being deprecated, but still supported. The value of vref_mv is checked so that a device without internal voltage reference cannot proceed without an explicit supply. For correct operation, vdd and vlogic are required, then devm_regulator_get_enable() is used so the driver can still work without them by using the stub/dummy regulators. Error report uses dev_err_probe(), which helps debugging an init issue. A small delay is added after the regulators are enabled to consider for the power-up time (4.5 us). Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 5840fda4b011..e2ebabca6887 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -484,12 +486,28 @@ int ad5686_probe(struct device *dev, st->ops = ops; st->chip_info = chip_info; - ret = devm_regulator_get_enable_read_voltage(dev, "vcc"); + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "failed to enable vdd supply\n"); + + ret = devm_regulator_get_enable(dev, "vlogic"); + if (ret) + return dev_err_probe(dev, ret, "failed to enable vlogic supply\n"); + + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret == -ENODEV) /* vcc-supply is deprecated, but supported still */ + ret = devm_regulator_get_enable_read_voltage(dev, "vcc"); if (ret < 0 && ret != -ENODEV) - return ret; + return dev_err_probe(dev, ret, "failed to read vref voltage\n"); st->use_internal_vref = ret == -ENODEV; st->vref_mv = st->use_internal_vref ? st->chip_info->int_vref_mv : ret / 1000; + if (!st->vref_mv) + return dev_err_probe(dev, -EINVAL, + "invalid or not provided vref voltage\n"); + + /* 4.5us power-up time: Datasheet Table 4: Timing Characteristics */ + fsleep(5); /* Initialize masks to all ones */ st->pwr_down_mask = ~0; From a1f417cc59eb6401411cc5b4bb70fdcf31df07c7 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Sun, 28 Jun 2026 15:08:13 +0100 Subject: [PATCH 080/513] iio: dac: ad5686: consume optional reset signal Add RESET pin GPIO support through an optional reset control, which is local to the probe function. A reset pulse is manually generated after the device is powered up. Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index e2ebabca6887..8ad8931a3d7f 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -472,6 +473,7 @@ int ad5686_probe(struct device *dev, const struct ad5686_chip_info *chip_info, const char *name, const struct ad5686_bus_ops *ops) { + struct reset_control *rstc; struct ad5686_state *st; struct iio_dev *indio_dev; int ret, i; @@ -486,6 +488,11 @@ int ad5686_probe(struct device *dev, st->ops = ops; st->chip_info = chip_info; + rstc = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(rstc)) + return dev_err_probe(dev, PTR_ERR(rstc), + "Failed to get reset control\n"); + ret = devm_regulator_get_enable(dev, "vdd"); if (ret) return dev_err_probe(dev, ret, "failed to enable vdd supply\n"); @@ -509,6 +516,11 @@ int ad5686_probe(struct device *dev, /* 4.5us power-up time: Datasheet Table 4: Timing Characteristics */ fsleep(5); + /* 1us >> 30ns reset pulse activation time: Datasheet Table 4 */ + reset_control_assert(rstc); + fsleep(1); + reset_control_deassert(rstc); + /* Initialize masks to all ones */ st->pwr_down_mask = ~0; st->pwr_down_mode = ~0; From b15df8dab7e101dcaf1b8b40d282199b77becb2e Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Sun, 28 Jun 2026 15:08:14 +0100 Subject: [PATCH 081/513] iio: dac: ad5686: add ldac gpio If wired LDAC, should be asserted when unused (pin is active-low), which allows for synchronous DAC updates. This will be used to update all the channels at the same time when adding buffer support. Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 6 ++++++ drivers/iio/dac/ad5686.h | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 8ad8931a3d7f..713fe71ad1e7 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -521,6 +522,11 @@ int ad5686_probe(struct device *dev, fsleep(1); reset_control_deassert(rstc); + st->ldac_gpio = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_HIGH); + if (IS_ERR(st->ldac_gpio)) + return dev_err_probe(dev, PTR_ERR(st->ldac_gpio), + "Failed to get LDAC GPIO\n"); + /* Initialize masks to all ones */ st->pwr_down_mask = ~0; st->pwr_down_mode = ~0; diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index a06fe7d89305..c424720f8f72 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -60,6 +60,8 @@ enum ad5686_regmap_type { AD5686_REGMAP, }; +struct gpio_desc; + struct ad5686_state; /** @@ -119,6 +121,7 @@ extern const struct ad5686_chip_info ad5679r_chip_info; * @dev: device instance * @chip_info: chip model specific constants, available modes etc * @ops: bus specific operations + * @ldac_gpio: LDAC pin GPIO descriptor * @vref_mv: actual reference voltage used * @pwr_down_mask: power down mask * @pwr_down_mode: current power down mode @@ -131,6 +134,7 @@ struct ad5686_state { struct device *dev; const struct ad5686_chip_info *chip_info; const struct ad5686_bus_ops *ops; + struct gpio_desc *ldac_gpio; unsigned short vref_mv; unsigned int pwr_down_mask; unsigned int pwr_down_mode; From 0846f13ef4deb406a527afdf8d515712ba611e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 30 Jun 2026 17:35:36 +0200 Subject: [PATCH 082/513] staging: iio: Initialize spi_device_id arrays using member names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct spi_device_id that replaces .driver_data by an anonymous union. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Nuno Sá Link: https://patch.msgid.link/cac7a68e6a6adb1b58207640ab045e59eec86f53.1781883685.git.u.kleine-koenig@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/adc/ad7816.c | 6 +++--- drivers/staging/iio/frequency/ad9834.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/iio/adc/ad7816.c b/drivers/staging/iio/adc/ad7816.c index 0e32a2295990..30644d2d7c54 100644 --- a/drivers/staging/iio/adc/ad7816.c +++ b/drivers/staging/iio/adc/ad7816.c @@ -426,9 +426,9 @@ static const struct of_device_id ad7816_of_match[] = { MODULE_DEVICE_TABLE(of, ad7816_of_match); static const struct spi_device_id ad7816_id[] = { - { "ad7816", ID_AD7816 }, - { "ad7817", ID_AD7817 }, - { "ad7818", ID_AD7818 }, + { .name = "ad7816", .driver_data = ID_AD7816 }, + { .name = "ad7817", .driver_data = ID_AD7817 }, + { .name = "ad7818", .driver_data = ID_AD7818 }, { } }; diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index 4359b358e0e5..f95c5365cd03 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -465,10 +465,10 @@ static int ad9834_probe(struct spi_device *spi) } static const struct spi_device_id ad9834_id[] = { - {"ad9833", ID_AD9833}, - {"ad9834", ID_AD9834}, - {"ad9837", ID_AD9837}, - {"ad9838", ID_AD9838}, + { .name = "ad9833", .driver_data = ID_AD9833 }, + { .name = "ad9834", .driver_data = ID_AD9834 }, + { .name = "ad9837", .driver_data = ID_AD9837 }, + { .name = "ad9838", .driver_data = ID_AD9838 }, { } }; MODULE_DEVICE_TABLE(spi, ad9834_id); From e4f75f21445884c7727eb14de82021546993c71b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 30 Jun 2026 17:35:34 +0200 Subject: [PATCH 083/513] iio: adc: ti-tsc2046: Simplify device handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver was introduced in 2021 and since then only supports a single chip variant. Simplify the driver by hard-coding the device properties instead of using the id_table's abstraction for a single chip type. Also drop the write-only struct member dcfg and the then unused struct definition. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Oleksij Rempel Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-tsc2046.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c index aba4b10a17ac..125f6d7e2d84 100644 --- a/drivers/iio/adc/ti-tsc2046.c +++ b/drivers/iio/adc/ti-tsc2046.c @@ -121,11 +121,6 @@ struct tsc2046_adc_group_layout { unsigned int skip; }; -struct tsc2046_adc_dcfg { - const struct iio_chan_spec *channels; - unsigned int num_channels; -}; - struct tsc2046_adc_ch_cfg { unsigned int settling_time_us; unsigned int oversampling_ratio; @@ -141,7 +136,6 @@ enum tsc2046_state { struct tsc2046_adc_priv { struct spi_device *spi; - const struct tsc2046_adc_dcfg *dcfg; bool internal_vref; struct iio_trigger *trig; @@ -214,11 +208,6 @@ const struct iio_chan_spec name ## _channels[] = { \ static DECLARE_TI_TSC2046_8_CHANNELS(tsc2046_adc, 12); -static const struct tsc2046_adc_dcfg tsc2046_adc_dcfg_tsc2046e = { - .channels = tsc2046_adc_channels, - .num_channels = ARRAY_SIZE(tsc2046_adc_channels), -}; - /* * Convert time to a number of samples which can be transferred within this * time. @@ -739,7 +728,6 @@ static void tsc2046_adc_parse_fwnode(struct tsc2046_adc_priv *priv) static int tsc2046_adc_probe(struct spi_device *spi) { - const struct tsc2046_adc_dcfg *dcfg; struct device *dev = &spi->dev; struct tsc2046_adc_priv *priv; struct iio_dev *indio_dev; @@ -752,10 +740,6 @@ static int tsc2046_adc_probe(struct spi_device *spi) return -EINVAL; } - dcfg = spi_get_device_match_data(spi); - if (!dcfg) - return -EINVAL; - spi->mode &= ~SPI_MODE_X_MASK; spi->mode |= SPI_MODE_0; ret = spi_setup(spi); @@ -767,14 +751,13 @@ static int tsc2046_adc_probe(struct spi_device *spi) return -ENOMEM; priv = iio_priv(indio_dev); - priv->dcfg = dcfg; priv->spi = spi; indio_dev->name = TI_TSC2046_NAME; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = dcfg->channels; - indio_dev->num_channels = dcfg->num_channels; + indio_dev->channels = tsc2046_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(tsc2046_adc_channels); indio_dev->info = &tsc2046_adc_info; ret = devm_regulator_get_enable_read_voltage(dev, "vref"); @@ -829,13 +812,13 @@ static int tsc2046_adc_probe(struct spi_device *spi) } static const struct of_device_id ads7950_of_table[] = { - { .compatible = "ti,tsc2046e-adc", .data = &tsc2046_adc_dcfg_tsc2046e }, + { .compatible = "ti,tsc2046e-adc" }, { } }; MODULE_DEVICE_TABLE(of, ads7950_of_table); static const struct spi_device_id tsc2046_adc_spi_ids[] = { - { "tsc2046e-adc", (unsigned long)&tsc2046_adc_dcfg_tsc2046e }, + { .name = "tsc2046e-adc" }, { } }; MODULE_DEVICE_TABLE(spi, tsc2046_adc_spi_ids); From 1777f50b92e6196b58b19dcfb4692e222a086058 Mon Sep 17 00:00:00 2001 From: Romain Gantois Date: Tue, 30 Jun 2026 11:26:34 +0200 Subject: [PATCH 084/513] dt-bindings: iio: ti,tmp117: add binding for the TMP119 The TMP119 has the same register layout as the TMP117, and a better local sensor accuracy. Add a compatible for it. Use ti,tmp117 as a fallback compatible for ti,tmp119. Signed-off-by: Romain Gantois Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- .../bindings/iio/temperature/ti,tmp117.yaml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml b/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml index fbba5e934861..7fb3e6820791 100644 --- a/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml +++ b/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml @@ -7,19 +7,25 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: TI TMP117 - Digital temperature sensor with integrated NV memory description: | - TI TMP116/117 - Digital temperature sensor with integrated NV memory that - supports I2C interface. + TI TMP116/117/119 - Digital temperature sensor with integrated NV memory + that supports I2C interface. https://www.ti.com/lit/gpn/tmp116 https://www.ti.com/lit/gpn/tmp117 + https://www.ti.com/lit/gpn/tmp119 maintainers: - Puranjay Mohan properties: compatible: - enum: - - ti,tmp116 - - ti,tmp117 + oneOf: + - enum: + - ti,tmp116 + - ti,tmp117 + - items: + - enum: + - ti,tmp119 + - const: ti,tmp117 reg: maxItems: 1 From fef4337eb2888c758c7058e1723903204f012a26 Mon Sep 17 00:00:00 2001 From: Wil Stark Date: Tue, 30 Jun 2026 11:26:35 +0200 Subject: [PATCH 085/513] iio: temperature: tmp117: add TI TMP119 support Add support for the TMP119 temperature sensor, which has the same programming model as the TMP117. [rgantois: Commit log] Signed-off-by: Wil Stark [rgantois: Rebased from v6.6.58 to v7.2-rc1] Signed-off-by: Romain Gantois Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/tmp117.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/iio/temperature/tmp117.c b/drivers/iio/temperature/tmp117.c index 6bc18616ad15..74cb8d62bef3 100644 --- a/drivers/iio/temperature/tmp117.c +++ b/drivers/iio/temperature/tmp117.c @@ -9,6 +9,7 @@ * Note: This driver assumes that the sensor has been calibrated beforehand. */ +#include #include #include #include @@ -38,6 +39,7 @@ #define TMP116_DEVICE_ID 0x1116 #define TMP117_DEVICE_ID 0x0117 +#define TMP119_DEVICE_ID 0x2117 struct tmp117_data { struct i2c_client *client; @@ -139,6 +141,12 @@ static const struct tmp11x_info tmp117_channels_info = { .num_channels = ARRAY_SIZE(tmp117_channels) }; +static const struct tmp11x_info tmp119_channels_info = { + .name = "tmp119", + .channels = tmp117_channels, + .num_channels = ARRAY_SIZE(tmp117_channels) +}; + static const struct iio_info tmp117_info = { .read_raw = tmp117_read_raw, .write_raw = tmp117_write_raw, @@ -172,6 +180,9 @@ static int tmp117_probe(struct i2c_client *client) case TMP117_DEVICE_ID: match_data = &tmp117_channels_info; break; + case TMP119_DEVICE_ID: + match_data = &tmp119_channels_info; + break; default: dev_info(&client->dev, "Unknown device id (0x%x), use fallback compatible\n", @@ -204,6 +215,7 @@ static int tmp117_probe(struct i2c_client *client) static const struct of_device_id tmp117_of_match[] = { { .compatible = "ti,tmp116", .data = &tmp116_channels_info }, { .compatible = "ti,tmp117", .data = &tmp117_channels_info }, + { .compatible = "ti,tmp119", .data = &tmp119_channels_info }, { } }; MODULE_DEVICE_TABLE(of, tmp117_of_match); @@ -211,6 +223,7 @@ MODULE_DEVICE_TABLE(of, tmp117_of_match); static const struct i2c_device_id tmp117_id[] = { { .name = "tmp116", .driver_data = (kernel_ulong_t)&tmp116_channels_info }, { .name = "tmp117", .driver_data = (kernel_ulong_t)&tmp117_channels_info }, + { .name = "tmp119", .driver_data = (kernel_ulong_t)&tmp119_channels_info }, { } }; MODULE_DEVICE_TABLE(i2c, tmp117_id); From d9df71de85c2687d4250f8662296902bed430458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 30 Jun 2026 17:35:35 +0200 Subject: [PATCH 086/513] iio: imu: adis16550: Simplify device abstraction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver supports a single chip variant only. Simplify the driver by hard-coding the device properties instead of using the id_table's abstraction for a single chip type and a lookup in a table with only one entry. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16550.c | 89 +++++++++++-------------------------- 1 file changed, 26 insertions(+), 63 deletions(-) diff --git a/drivers/iio/imu/adis16550.c b/drivers/iio/imu/adis16550.c index 75679612052f..fe0034964408 100644 --- a/drivers/iio/imu/adis16550.c +++ b/drivers/iio/imu/adis16550.c @@ -89,25 +89,7 @@ struct adis16550_sync { u16 max_rate; }; -struct adis16550_chip_info { - const struct iio_chan_spec *channels; - const struct adis16550_sync *sync_mode; - char *name; - u32 num_channels; - u32 gyro_max_val; - u32 gyro_max_scale; - u32 accel_max_val; - u32 accel_max_scale; - u32 temp_scale; - u32 deltang_max_val; - u32 deltvel_max_val; - u32 int_clk; - u16 max_dec; - u16 num_sync; -}; - struct adis16550 { - const struct adis16550_chip_info *info; struct adis adis; unsigned long clk_freq_hz; u32 sync_mode; @@ -450,8 +432,8 @@ static int adis16550_set_freq_hz(struct adis16550 *st, u32 freq_hz) * The optimal sample rate for the supported IMUs is between * int_clk - 1000 and int_clk + 500. */ - u32 max_sample_rate = st->info->int_clk * 1000 + 500000; - u32 min_sample_rate = st->info->int_clk * 1000 - 1000000; + u32 max_sample_rate = 4000 * 1000 + 500000; + u32 min_sample_rate = 4000 * 1000 - 1000000; if (!freq_hz) return -EINVAL; @@ -484,7 +466,7 @@ static int adis16550_set_freq_hz(struct adis16550 *st, u32 freq_hz) if (dec) dec--; - dec = min(dec, st->info->max_dec); + dec = min(dec, 4095); return __adis_write_reg_16(&st->adis, ADIS16550_REG_DEC_RATE, dec); } @@ -592,30 +574,30 @@ static int adis16550_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_ANGL_VEL: - *val = st->info->gyro_max_val; - *val2 = st->info->gyro_max_scale; + *val = 1; + *val2 = IIO_RAD_TO_DEGREE(80 << 16); return IIO_VAL_FRACTIONAL; case IIO_ACCEL: - *val = st->info->accel_max_val; - *val2 = st->info->accel_max_scale; + *val = 1; + *val2 = IIO_M_S_2_TO_G(102400000); return IIO_VAL_FRACTIONAL; case IIO_TEMP: - *val = st->info->temp_scale; + *val = 4; return IIO_VAL_INT; case IIO_DELTA_ANGL: - *val = st->info->deltang_max_val; + *val = IIO_DEGREE_TO_RAD(720); *val2 = 31; return IIO_VAL_FRACTIONAL_LOG2; case IIO_DELTA_VELOCITY: - *val = st->info->deltvel_max_val; + *val = 125; *val2 = 31; return IIO_VAL_FRACTIONAL_LOG2; default: return -EINVAL; } case IIO_CHAN_INFO_OFFSET: - /* temperature centered at 25°C */ - *val = DIV_ROUND_CLOSEST(25000, st->info->temp_scale); + /* temperature centered at 25°C divided by temp scale */ + *val = 25000 / 4; return IIO_VAL_INT; case IIO_CHAN_INFO_CALIBBIAS: ret = adis_read_reg_32(&st->adis, @@ -793,23 +775,6 @@ static const struct adis16550_sync adis16550_sync_modes[] = { { ADIS16550_SYNC_MODE_SCALED, 1, 128 }, }; -static const struct adis16550_chip_info adis16550_chip_info = { - .num_channels = ARRAY_SIZE(adis16550_channels), - .channels = adis16550_channels, - .name = "adis16550", - .gyro_max_val = 1, - .gyro_max_scale = IIO_RAD_TO_DEGREE(80 << 16), - .accel_max_val = 1, - .accel_max_scale = IIO_M_S_2_TO_G(102400000), - .temp_scale = 4, - .deltang_max_val = IIO_DEGREE_TO_RAD(720), - .deltvel_max_val = 125, - .int_clk = 4000, - .max_dec = 4095, - .sync_mode = adis16550_sync_modes, - .num_sync = ARRAY_SIZE(adis16550_sync_modes), -}; - static u32 adis16550_validate_crc(__be32 *buffer, const u8 n_elem) { int i; @@ -918,21 +883,21 @@ static int adis16550_config_sync(struct adis16550 *st) if (IS_ERR(clk)) return PTR_ERR(clk); if (!clk) { - st->clk_freq_hz = st->info->int_clk * 1000; + st->clk_freq_hz = 4000000; return 0; } st->clk_freq_hz = clk_get_rate(clk); - for (i = 0; i < st->info->num_sync; i++) { - if (st->clk_freq_hz >= st->info->sync_mode[i].min_rate && - st->clk_freq_hz <= st->info->sync_mode[i].max_rate) { - sync_mode_data = &st->info->sync_mode[i]; + for (i = 0; i < ARRAY_SIZE(adis16550_sync_modes); i++) { + if (st->clk_freq_hz >= adis16550_sync_modes[i].min_rate && + st->clk_freq_hz <= adis16550_sync_modes[i].max_rate) { + sync_mode_data = &adis16550_sync_modes[i]; break; } } - if (i == st->info->num_sync) + if (i == ARRAY_SIZE(adis16550_sync_modes)) return dev_err_probe(dev, -EINVAL, "Clk rate: %lu not in a valid range", st->clk_freq_hz); @@ -943,7 +908,7 @@ static int adis16550_config_sync(struct adis16550 *st) * of [3000 4500]. */ - sync_scale = DIV_ROUND_CLOSEST(st->info->int_clk, st->clk_freq_hz); + sync_scale = DIV_ROUND_CLOSEST(4000, st->clk_freq_hz); if (3000 > sync_scale || 4500 < sync_scale) return dev_err_probe(dev, -EINVAL, @@ -955,7 +920,7 @@ static int adis16550_config_sync(struct adis16550 *st) if (ret) return ret; - st->clk_freq_hz = st->info->int_clk; + st->clk_freq_hz = 4000; } st->clk_freq_hz *= 1000; @@ -1064,13 +1029,11 @@ static int adis16550_probe(struct spi_device *spi) return -ENOMEM; st = iio_priv(indio_dev); - st->info = spi_get_device_match_data(spi); - if (!st->info) - return -EINVAL; + adis = &st->adis; - indio_dev->name = st->info->name; - indio_dev->channels = st->info->channels; - indio_dev->num_channels = st->info->num_channels; + indio_dev->name = "adis16550"; + indio_dev->channels = adis16550_channels; + indio_dev->num_channels = ARRAY_SIZE(adis16550_channels); indio_dev->available_scan_masks = adis16550_channel_masks; indio_dev->info = &adis16550_info; indio_dev->modes = INDIO_DIRECT_MODE; @@ -1117,13 +1080,13 @@ static int adis16550_probe(struct spi_device *spi) } static const struct spi_device_id adis16550_id[] = { - { "adis16550", (kernel_ulong_t)&adis16550_chip_info}, + { .name = "adis16550" }, { } }; MODULE_DEVICE_TABLE(spi, adis16550_id); static const struct of_device_id adis16550_of_match[] = { - { .compatible = "adi,adis16550", .data = &adis16550_chip_info }, + { .compatible = "adi,adis16550" }, { } }; MODULE_DEVICE_TABLE(of, adis16550_of_match); From c4f4b964dd4fedd2654355bc6dfc0011b3984517 Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Wed, 1 Jul 2026 16:17:23 +0200 Subject: [PATCH 087/513] iio: dac: add mcf54415 DAC Add basic version of mcf54415 DAC driver. DAC is embedded in the SoC and DAC configuration registers are mapped in the internal IO address space. The DAC accepts a 12-bit digital signal and creates a monotonic 12-bit analog output varying from DAC_VREFL to DAC_VREFH. The DAC module consists of a conversion unit, an output amplifier, and the associated digital control blocks. Default register values for DAC_VREFL and DAC_VREFH are respectively 0 and 0xfff, left untouched in this initial version. This initial version of the driver is minimalistic, "output raw" only, to be extended in the future. DMA and external sync are disabled, default mode is high speed, default format is right-justified 12-bit on 16-bit word. Reviewed-by: Andy Shevchenko Signed-off-by: Angelo Dureghello Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 11 ++ drivers/iio/dac/Makefile | 1 + drivers/iio/dac/mcf54415_dac.c | 180 +++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 drivers/iio/dac/mcf54415_dac.c diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 657c68e75542..ebf7144f922a 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -527,6 +527,17 @@ config MAX5821 Say yes here to build support for Maxim MAX5821 10 bits DAC. +config MCF54415_DAC + tristate "NXP MCF54415 DAC driver" + depends on M5441x || COMPILE_TEST + select REGMAP_MMIO + help + Say yes here if you want to build support for NXP ColdFire + MCF54415/6/7/8 12-bit DAC module. + + To compile this driver as a module, choose M here: the module + will be called mcf54415_dac. + config MCP4725 tristate "MCP4725/6 DAC driver" depends on I2C diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 003431798498..5d20d37e44ce 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_MAX517) += max517.o obj-$(CONFIG_MAX22007) += max22007.o obj-$(CONFIG_MAX5522) += max5522.o obj-$(CONFIG_MAX5821) += max5821.o +obj-$(CONFIG_MCF54415_DAC) += mcf54415_dac.o obj-$(CONFIG_MCP4725) += mcp4725.o obj-$(CONFIG_MCP4728) += mcp4728.o obj-$(CONFIG_MCP47FEB02) += mcp47feb02.o diff --git a/drivers/iio/dac/mcf54415_dac.c b/drivers/iio/dac/mcf54415_dac.c new file mode 100644 index 000000000000..e2c12241a534 --- /dev/null +++ b/drivers/iio/dac/mcf54415_dac.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NXP mcf54415 DAC driver + * + * Copyright 2026 BayLibre - adureghello@baylibre.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MCF54415_DAC_CR 0x00 +#define MCF54415_DAC_CR_PDN BIT(0) +#define MCF54415_DAC_CR_HSLS BIT(6) +#define MCF54415_DAC_CR_WMLVL GENMASK(9, 8) +#define MCF54415_DAC_CR_FILT BIT(12) + +#define MCF54415_DAC_DATA 0x02 + +struct mcf54415_dac { + struct regmap *map; + struct clk *clk; +}; + +static const struct regmap_config mcf54415_dac_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x0c, /* DACX_FILTCNT, R.M. Table 30-2 */ + .val_format_endian = REGMAP_ENDIAN_BIG, + .reg_format_endian = REGMAP_ENDIAN_BIG, +}; + +static int mcf54415_dac_init(struct mcf54415_dac *info) +{ + u16 val = MCF54415_DAC_CR_FILT | FIELD_PREP(MCF54415_DAC_CR_WMLVL, 1); + int ret; + + /* Fixed defaults and enable DAC (bit 0 set to 0) */ + ret = regmap_write(info->map, MCF54415_DAC_CR, val); + if (ret) + return ret; + + /* DAC is ready after 12us, from RM table 40-3 */ + fsleep(12); + + return 0; +} + +static void mcf54415_dac_exit(void *data) +{ + struct mcf54415_dac *info = data; + + regmap_set_bits(info->map, MCF54415_DAC_CR, MCF54415_DAC_CR_PDN); +} + +static const struct iio_chan_spec mcf54415_dac_iio_channel = { + .type = IIO_VOLTAGE, + .output = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +}; + +static int mcf54415_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mcf54415_dac *info = iio_priv(indio_dev); + unsigned int reg; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = regmap_read(info->map, MCF54415_DAC_DATA, ®); + if (ret) + return ret; + *val = reg & GENMASK(11, 0); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* Reference voltage as per ColdFire datasheet is 3.3V */ + *val = 3300 /* mV */; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int mcf54415_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct mcf54415_dac *info = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + /* Check based on RM 30.3.2 (DACn_DATA) reg. resolution */ + if (val < 0 || val > 4095) + return -EINVAL; + return regmap_write(info->map, MCF54415_DAC_DATA, val); + default: + return -EINVAL; + } +} + +static const struct iio_info mcf54415_dac_iio_info = { + .read_raw = &mcf54415_read_raw, + .write_raw = &mcf54415_write_raw, +}; + +static int mcf54415_dac_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct mcf54415_dac *info; + void __iomem *regs; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return dev_err_probe(dev, PTR_ERR(regs), "failed to get io regs\n"); + + info->map = devm_regmap_init_mmio(dev, regs, &mcf54415_dac_regmap_config); + if (IS_ERR(info->map)) + return PTR_ERR(info->map); + + info->clk = devm_clk_get_enabled(dev, "dac"); + if (IS_ERR(info->clk)) + return dev_err_probe(dev, PTR_ERR(info->clk), "failed getting clock\n"); + + indio_dev->name = "mcf54415"; + indio_dev->info = &mcf54415_dac_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &mcf54415_dac_iio_channel; + indio_dev->num_channels = 1; + + ret = mcf54415_dac_init(info); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, mcf54415_dac_exit, info); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct platform_device_id mcf54415_dac_ids[] = { + { .name = "mcfdac" }, + { } +}; +MODULE_DEVICE_TABLE(platform, mcf54415_dac_ids); + +static struct platform_driver mcf54415_dac_driver = { + .driver = { + .name = "mcf54415_dac", + }, + .probe = mcf54415_dac_probe, + .id_table = mcf54415_dac_ids, +}; +module_platform_driver(mcf54415_dac_driver); + +MODULE_AUTHOR("Angelo Dureghello "); +MODULE_DESCRIPTION("NXP MCF54415 DAC driver"); +MODULE_LICENSE("GPL"); From 348ff65577603c0565257ecfcaa015ebaaeb200e Mon Sep 17 00:00:00 2001 From: Roman Vivchar Date: Tue, 23 Jun 2026 11:16:13 +0300 Subject: [PATCH 088/513] dt-bindings: iio: adc: mediatek,mt6359-auxadc: add mt6323 PMIC AUXADC The MediaTek mt6323 PMIC includes an AUXADC used for battery voltage, temperature, and other internal measurements. The IP block is not register-compatible with mt6359. Add the devicetree binding documentation and the associated header file defining the ADC channel constants. Also change the description to 'MT6350 series and similar' because the binding already includes more than mt635x series PMICs. Finally, add the MAINTAINERS entry for the header with ADC constants. Acked-by: Conor Dooley Signed-off-by: Roman Vivchar Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- .../iio/adc/mediatek,mt6359-auxadc.yaml | 3 ++- MAINTAINERS | 6 +++++ .../iio/adc/mediatek,mt6323-auxadc.h | 24 +++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h diff --git a/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml b/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml index 9936aa605c7b..c2f7387e4bfc 100644 --- a/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/iio/adc/mediatek,mt6359-auxadc.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: MediaTek MT6350 series PMIC AUXADC +title: MediaTek MT6350 series and similar PMIC AUXADC maintainers: - AngeloGioacchino Del Regno @@ -20,6 +20,7 @@ properties: compatible: oneOf: - enum: + - mediatek,mt6323-auxadc - mediatek,mt6357-auxadc - mediatek,mt6358-auxadc - mediatek,mt6359-auxadc diff --git a/MAINTAINERS b/MAINTAINERS index bf8f3f565fda..c413c2cb23ed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16564,6 +16564,12 @@ S: Maintained F: Documentation/devicetree/bindings/mmc/mtk-sd.yaml F: drivers/mmc/host/mtk-sd.c +MEDIATEK MT6323 PMIC AUXADC DRIVER +M: Roman Vivchar +L: linux-iio@vger.kernel.org +S: Maintained +F: include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h + MEDIATEK MT6735 CLOCK & RESET DRIVERS M: Yassine Oudjana L: linux-clk@vger.kernel.org diff --git a/include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h b/include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h new file mode 100644 index 000000000000..6ee9a9ecffc1 --- /dev/null +++ b/include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ + +#ifndef _DT_BINDINGS_MEDIATEK_MT6323_AUXADC_H +#define _DT_BINDINGS_MEDIATEK_MT6323_AUXADC_H + +#define MT6323_AUXADC_BATON2 0 +#define MT6323_AUXADC_CH6 1 +#define MT6323_AUXADC_BAT_TEMP 2 +#define MT6323_AUXADC_CHIP_TEMP 3 +#define MT6323_AUXADC_VCDT 4 +#define MT6323_AUXADC_BATON1 5 +#define MT6323_AUXADC_ISENSE 6 +#define MT6323_AUXADC_BATSNS 7 +#define MT6323_AUXADC_ACCDET 8 +#define MT6323_AUXADC_AUDIO0 9 +#define MT6323_AUXADC_AUDIO1 10 +#define MT6323_AUXADC_AUDIO2 11 +#define MT6323_AUXADC_AUDIO3 12 +#define MT6323_AUXADC_AUDIO4 13 +#define MT6323_AUXADC_AUDIO5 14 +#define MT6323_AUXADC_AUDIO6 15 +#define MT6323_AUXADC_AUDIO7 16 + +#endif From d6c384c3269dd76cd5dcff83932ebac7bda016f0 Mon Sep 17 00:00:00 2001 From: Roman Vivchar Date: Tue, 23 Jun 2026 11:16:14 +0300 Subject: [PATCH 089/513] iio: adc: mt6323-auxadc: add mt6323 PMIC AUXADC driver The mt6323 AUXADC is a 15-bit ADC used for system monitoring. This driver provides support for reading various channels including battery and charger voltages, battery and chip temperature, current sensing and accessory detection. Add a driver for the AUXADC found in the MediaTek mt6323 PMIC. Tested-by: Ben Grisdale # Amazon Echo Dot (2nd Generation) Signed-off-by: Roman Vivchar Reviewed-by: Andy Shevchenko Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + drivers/iio/adc/Kconfig | 11 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/mt6323-auxadc.c | 314 ++++++++++++++++++++++++++++++++ 4 files changed, 327 insertions(+) create mode 100644 drivers/iio/adc/mt6323-auxadc.c diff --git a/MAINTAINERS b/MAINTAINERS index c413c2cb23ed..c401a3ff5994 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16568,6 +16568,7 @@ MEDIATEK MT6323 PMIC AUXADC DRIVER M: Roman Vivchar L: linux-iio@vger.kernel.org S: Maintained +F: drivers/iio/adc/mt6323-auxadc.c F: include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h MEDIATEK MT6735 CLOCK & RESET DRIVERS diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 50eafa128cf4..441e5c660716 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1163,6 +1163,17 @@ config MCP3911 This driver can also be built as a module. If so, the module will be called mcp3911. +config MEDIATEK_MT6323_AUXADC + tristate "MediaTek MT6323 PMIC AUXADC driver" + depends on MFD_MT6397 + help + Say yes here to enable support for MediaTek MT6323 PMIC Auxiliary ADC. + This driver provides multiple channels for system monitoring, + such as battery voltage, PMIC temperature, and others. + + This driver can also be built as a module. If so, the module will be + called mt6323-auxadc. + config MEDIATEK_MT6359_AUXADC tristate "MediaTek MT6359 PMIC AUXADC driver" depends on MFD_MT6397 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 707dd708912f..dc24d0ae76a5 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -100,6 +100,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o obj-$(CONFIG_MCP3564) += mcp3564.o obj-$(CONFIG_MCP3911) += mcp3911.o +obj-$(CONFIG_MEDIATEK_MT6323_AUXADC) += mt6323-auxadc.o obj-$(CONFIG_MEDIATEK_MT6359_AUXADC) += mt6359-auxadc.o obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o obj-$(CONFIG_MEDIATEK_MT6370_ADC) += mt6370-adc.o diff --git a/drivers/iio/adc/mt6323-auxadc.c b/drivers/iio/adc/mt6323-auxadc.c new file mode 100644 index 000000000000..c450fb6f09cb --- /dev/null +++ b/drivers/iio/adc/mt6323-auxadc.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2026 Roman Vivchar + * + * Based on drivers/iio/adc/mt6359-auxadc.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define AUXADC_STRUP_CON10_RSTB_SEL BIT(7) +#define AUXADC_STRUP_CON10_RSTB_SW BIT(5) + +#define AUXADC_TOP_CKPDN2_CTL_CK BIT(5) + +#define AUXADC_TRIM_CH2_MASK GENMASK(11, 10) +#define AUXADC_TRIM_CH4_MASK GENMASK(9, 8) +#define AUXADC_TRIM_CH5_MASK GENMASK(5, 4) +#define AUXADC_TRIM_CH6_MASK GENMASK(3, 2) + +#define AUXADC_CON27_VREF18_ENB_MD BIT(15) +#define AUXADC_CON27_MD_STATUS BIT(0) + +#define AUXADC_CON19_GPS_STATUS BIT(1) + +#define AUXADC_CON26_VREF18_SELB BIT(1) +#define AUXADC_CON26_DECI_GDLY_SEL BIT(0) + +#define AUXADC_CON11_VBUF_EN BIT(4) + +#define AUXADC_CON19_DECI_GDLY_MASK GENMASK(15, 14) +#define AUXADC_ADC19_BUSY_MASK GENMASK(15, 1) +#define AUXADC_READY_MASK BIT(15) +#define AUXADC_DATA_MASK GENMASK(14, 0) + +#define AUXADC_CON9_OSR_MASK GENMASK(12, 10) +#define AUXADC_DEFAULT_OSR 3 + +#define MTK_PMIC_IIO_CHAN(_name, _chan, _addr) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _chan, \ + .address = _addr, \ + .datasheet_name = __stringify(_name), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ +} + +/* + * AUXADC reports everything in mV, including temperature and + * current channels. Channel macros are mapped such that their + * ID matches their respective hardware bit position in CON22. + */ +static const struct iio_chan_spec mt6323_auxadc_channels[] = { + MTK_PMIC_IIO_CHAN(baton2, MT6323_AUXADC_BATON2, MT6323_AUXADC_ADC6), + MTK_PMIC_IIO_CHAN(ch6, MT6323_AUXADC_CH6, MT6323_AUXADC_ADC11), + MTK_PMIC_IIO_CHAN(bat_temp, MT6323_AUXADC_BAT_TEMP, MT6323_AUXADC_ADC5), + MTK_PMIC_IIO_CHAN(chip_temp, MT6323_AUXADC_CHIP_TEMP, MT6323_AUXADC_ADC4), + MTK_PMIC_IIO_CHAN(vcdt, MT6323_AUXADC_VCDT, MT6323_AUXADC_ADC2), + MTK_PMIC_IIO_CHAN(baton1, MT6323_AUXADC_BATON1, MT6323_AUXADC_ADC3), + MTK_PMIC_IIO_CHAN(isense, MT6323_AUXADC_ISENSE, MT6323_AUXADC_ADC1), + MTK_PMIC_IIO_CHAN(batsns, MT6323_AUXADC_BATSNS, MT6323_AUXADC_ADC0), + MTK_PMIC_IIO_CHAN(accdet, MT6323_AUXADC_ACCDET, MT6323_AUXADC_ADC7), +}; + +/* + * The MediaTek MT6323 (as well as a lot of other PMICs) has the following hierarchy: + * PMIC AUXADC <- PMIC MFD <- SoC PWRAP (wrapper for PWRAP FSM) + * + * Therefore, PWRAP regmap should be obtained using dev->parent->parent. + */ +struct mt6323_auxadc { + struct regmap *regmap; + /* AUXADC doesn't support reading multiple channels simultaneously. */ + struct mutex lock; +}; + +static int mt6323_auxadc_prepare_channel(struct mt6323_auxadc *auxadc) +{ + struct regmap *map = auxadc->regmap; + u32 val; + int ret; + + ret = regmap_read(map, MT6323_AUXADC_CON19, &val); + if (ret) + return ret; + + /* The ADC is idle. */ + if (!(val & AUXADC_CON19_DECI_GDLY_MASK)) + return 0; + + ret = regmap_read_poll_timeout(map, MT6323_AUXADC_ADC19, + val, !(val & AUXADC_ADC19_BUSY_MASK), + 10, 500); + if (ret) + return ret; + + return regmap_clear_bits(map, MT6323_AUXADC_CON19, + AUXADC_CON19_DECI_GDLY_MASK); +} + +static int mt6323_auxadc_request(struct mt6323_auxadc *auxadc, + unsigned long channel) +{ + struct regmap *map = auxadc->regmap; + int ret; + + ret = regmap_set_bits(map, MT6323_AUXADC_CON11, AUXADC_CON11_VBUF_EN); + if (ret) + return ret; + + return regmap_set_bits(map, MT6323_AUXADC_CON22, BIT(channel)); +} + +static int mt6323_auxadc_release(struct mt6323_auxadc *auxadc, + unsigned long channel) +{ + struct regmap *map = auxadc->regmap; + int ret; + + ret = regmap_clear_bits(map, MT6323_AUXADC_CON22, BIT(channel)); + if (ret) + return ret; + + return regmap_clear_bits(map, MT6323_AUXADC_CON11, AUXADC_CON11_VBUF_EN); +} + +static int mt6323_auxadc_read(struct mt6323_auxadc *auxadc, + const struct iio_chan_spec *chan, int *out) +{ + struct regmap *map = auxadc->regmap; + u32 val; + int ret; + + ret = regmap_read_poll_timeout(map, chan->address, + val, (val & AUXADC_READY_MASK), + 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC); + if (ret) + return ret; + + *out = FIELD_GET(AUXADC_DATA_MASK, val); + + return 0; +} + +static int mt6323_auxadc_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct mt6323_auxadc *auxadc = iio_priv(indio_dev); + int ret, mult; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->channel == MT6323_AUXADC_ISENSE || + chan->channel == MT6323_AUXADC_BATSNS) + mult = 4; + else + mult = 1; + + /* 1800mV full range with 15-bit resolution. */ + *val = mult * 1800; + *val2 = 15; + + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_RAW: { + guard(mutex)(&auxadc->lock); + + ret = mt6323_auxadc_prepare_channel(auxadc); + if (ret) + return ret; + + ret = mt6323_auxadc_request(auxadc, chan->channel); + if (ret) + return ret; + + /* Hardware limitation: the AUXADC needs a delay to become ready. */ + fsleep(300); + + ret = mt6323_auxadc_read(auxadc, chan, val); + + if (mt6323_auxadc_release(auxadc, chan->channel)) + dev_err(&indio_dev->dev, + "failed to release channel %d\n", chan->channel); + + if (ret) + return ret; + + return IIO_VAL_INT; + } + default: + return -EINVAL; + } +} + +static int mt6323_auxadc_init(struct mt6323_auxadc *auxadc) +{ + struct regmap *map = auxadc->regmap; + int ret; + + ret = regmap_set_bits(map, MT6323_STRUP_CON10, + AUXADC_STRUP_CON10_RSTB_SW | + AUXADC_STRUP_CON10_RSTB_SEL); + if (ret) + return ret; + + ret = regmap_set_bits(map, MT6323_TOP_CKPDN2, AUXADC_TOP_CKPDN2_CTL_CK); + if (ret) + return ret; + + ret = regmap_update_bits(map, MT6323_AUXADC_CON10, + AUXADC_TRIM_CH2_MASK | AUXADC_TRIM_CH4_MASK | + AUXADC_TRIM_CH5_MASK | AUXADC_TRIM_CH6_MASK, + FIELD_PREP(AUXADC_TRIM_CH2_MASK, 1) | + FIELD_PREP(AUXADC_TRIM_CH4_MASK, 1) | + FIELD_PREP(AUXADC_TRIM_CH5_MASK, 1) | + FIELD_PREP(AUXADC_TRIM_CH6_MASK, 1)); + if (ret) + return ret; + + ret = regmap_set_bits(map, MT6323_AUXADC_CON27, + AUXADC_CON27_VREF18_ENB_MD | + AUXADC_CON27_MD_STATUS); + if (ret) + return ret; + + ret = regmap_set_bits(map, MT6323_AUXADC_CON19, AUXADC_CON19_GPS_STATUS); + if (ret) + return ret; + + ret = regmap_set_bits(map, MT6323_AUXADC_CON26, + AUXADC_CON26_VREF18_SELB | + AUXADC_CON26_DECI_GDLY_SEL); + if (ret) + return ret; + + return regmap_update_bits(map, MT6323_AUXADC_CON9, AUXADC_CON9_OSR_MASK, + FIELD_PREP(AUXADC_CON9_OSR_MASK, AUXADC_DEFAULT_OSR)); +} + +static const struct iio_info mt6323_auxadc_iio_info = { + .read_raw = mt6323_auxadc_read_raw, +}; + +static int mt6323_auxadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mt6323_auxadc *auxadc; + struct regmap *regmap; + struct iio_dev *iio; + int ret; + + regmap = dev_get_regmap(dev->parent->parent, NULL); + if (!regmap) + return dev_err_probe(dev, -ENODEV, "failed to get regmap\n"); + + iio = devm_iio_device_alloc(dev, sizeof(*auxadc)); + if (!iio) + return -ENOMEM; + + auxadc = iio_priv(iio); + auxadc->regmap = regmap; + + ret = devm_mutex_init(dev, &auxadc->lock); + if (ret) + return ret; + + ret = mt6323_auxadc_init(auxadc); + if (ret) + return dev_err_probe(dev, ret, "failed to initialize auxadc\n"); + + iio->name = "mt6323-auxadc"; + iio->info = &mt6323_auxadc_iio_info; + iio->modes = INDIO_DIRECT_MODE; + iio->channels = mt6323_auxadc_channels; + iio->num_channels = ARRAY_SIZE(mt6323_auxadc_channels); + + return devm_iio_device_register(dev, iio); +} + +static const struct of_device_id mt6323_auxadc_of_match[] = { + { .compatible = "mediatek,mt6323-auxadc" }, + { } +}; +MODULE_DEVICE_TABLE(of, mt6323_auxadc_of_match); + +static struct platform_driver mt6323_auxadc_driver = { + .driver = { + .name = "mt6323-auxadc", + .of_match_table = mt6323_auxadc_of_match, + }, + .probe = mt6323_auxadc_probe, +}; +module_platform_driver(mt6323_auxadc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MediaTek MT6323 PMIC AUXADC Driver"); From 53cea40388d6485abdc4e7fd24f05198d88f66cd Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Thu, 25 Jun 2026 14:24:52 +0200 Subject: [PATCH 090/513] iio: imu: inv_icm42600: reorder all driver headers Reorder headers includes following IIO subsystem preferences. Suggested-by: Andy Shevchenko Signed-off-by: Jean-Baptiste Maneyrol Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_icm42600/inv_icm42600.h | 8 +++++--- drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c | 13 +++++++------ drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c | 7 ++++--- drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h | 2 +- drivers/iio/imu/inv_icm42600/inv_icm42600_core.c | 12 ++++++------ drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c | 11 ++++++----- drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c | 8 ++++---- drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c | 8 ++++---- drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c | 3 ++- 9 files changed, 39 insertions(+), 33 deletions(-) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h index c8b48a5c5ed0..2ab863c9d53a 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h @@ -6,13 +6,15 @@ #ifndef INV_ICM42600_H_ #define INV_ICM42600_H_ -#include #include -#include +#include #include -#include #include +#include +#include + #include + #include #include "inv_icm42600_buffer.h" diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index 532d5fdffaf8..87471e8c9bc7 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -3,25 +3,26 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include +#include #include +#include +#include +#include #include #include #include -#include -#include -#include #include #include -#include #include #include #include +#include + #include "inv_icm42600.h" -#include "inv_icm42600_temp.h" #include "inv_icm42600_buffer.h" +#include "inv_icm42600_temp.h" #define INV_ICM42600_ACCEL_CHAN(_modifier, _index, _ext_info) \ { \ diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c index 68a395758031..26817158ec78 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c @@ -3,18 +3,19 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include +#include #include +#include #include #include #include #include -#include #include -#include #include +#include + #include "inv_icm42600.h" #include "inv_icm42600_buffer.h" diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h index ffca4da1e249..6ab1eb1a0051 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h @@ -6,8 +6,8 @@ #ifndef INV_ICM42600_BUFFER_H_ #define INV_ICM42600_BUFFER_H_ -#include #include +#include struct inv_icm42600_state; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c index 76eb22488e5f..dc97d8a274e3 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c @@ -3,18 +3,18 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include -#include -#include -#include #include -#include +#include #include #include -#include +#include +#include +#include #include #include #include +#include +#include #include diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index 11339ddf1da3..ea8aa64a3f33 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -3,22 +3,23 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include +#include #include +#include +#include #include #include #include -#include -#include #include -#include #include #include +#include + #include "inv_icm42600.h" -#include "inv_icm42600_temp.h" #include "inv_icm42600_buffer.h" +#include "inv_icm42600_temp.h" #define INV_ICM42600_GYRO_CHAN(_modifier, _index, _ext_info) \ { \ diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c index 99d37ac53bbe..f422523207ee 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c @@ -3,13 +3,13 @@ * Copyright (C) 2020 InvenSense, Inc. */ -#include #include -#include -#include #include -#include +#include +#include +#include #include +#include #include "inv_icm42600.h" diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c index 089579caf244..7c49c527d3ec 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c @@ -3,13 +3,13 @@ * Copyright (C) 2020 InvenSense, Inc. */ -#include #include -#include +#include #include -#include -#include +#include #include +#include +#include #include "inv_icm42600.h" diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c index 727b03d541a5..7f80bda471c3 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c @@ -3,11 +3,12 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include #include +#include #include #include #include + #include #include "inv_icm42600.h" From 9f16490c4ed130fa35928db6dc5b624890bddc32 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Thu, 25 Jun 2026 14:24:53 +0200 Subject: [PATCH 091/513] iio: imu: inv_icm42600: add buffer hwfifo watermark attributes Add hwfifo_watermark/min/max/enabled buffer attributes. Hardware FIFO is always enabled and used. Signed-off-by: Jean-Baptiste Maneyrol Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_icm42600/inv_icm42600.h | 2 ++ .../iio/imu/inv_icm42600/inv_icm42600_accel.c | 5 +-- .../imu/inv_icm42600/inv_icm42600_buffer.c | 36 +++++++++++++++++++ .../imu/inv_icm42600/inv_icm42600_buffer.h | 1 + .../iio/imu/inv_icm42600/inv_icm42600_gyro.c | 5 +-- 5 files changed, 45 insertions(+), 4 deletions(-) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h index 2ab863c9d53a..b55d993f0264 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h @@ -356,6 +356,8 @@ struct inv_icm42600_sensor_state { cpu_to_le16((_wm) & GENMASK(11, 0)) /* FIFO is 2048 bytes, let 12 samples for reading latency */ #define INV_ICM42600_FIFO_WATERMARK_MAX (2048 - 12 * 16) +/* INV_ICM42600_FIFO_WATERMARK_MAX / 8 = 232 */ +#define INV_ICM42600_FIFO_WATERMARK_MAX_SAMPLES 232 #define INV_ICM42600_REG_INT_CONFIG1 0x0064 #define INV_ICM42600_INT_CONFIG1_TPULSE_DURATION BIT(6) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index 87471e8c9bc7..2421de63f5bf 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -1187,8 +1187,9 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_accel_channels); indio_dev->available_scan_masks = inv_icm42600_accel_scan_masks; - ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, - &inv_icm42600_buffer_ops); + ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev, + &inv_icm42600_buffer_ops, + inv_icm42600_buffer_attrs); if (ret) return ERR_PTR(ret); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c index 26817158ec78..c3c2fbd8d9ce 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c @@ -10,9 +10,11 @@ #include #include #include +#include #include #include +#include #include @@ -438,6 +440,40 @@ const struct iio_buffer_setup_ops inv_icm42600_buffer_ops = { .postdisable = inv_icm42600_buffer_postdisable, }; +static ssize_t hwfifo_watermark_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + unsigned int wm; + + guard(mutex)(&st->lock); + + if (indio_dev == st->indio_accel) + wm = st->fifo.watermark.eff_accel; + else if (indio_dev == st->indio_gyro) + wm = st->fifo.watermark.eff_gyro; + else + return -EINVAL; + + return sysfs_emit(buf, "%u\n", wm); +} + +IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_watermark_min, "1"); +IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_watermark_max, + __stringify(INV_ICM42600_FIFO_WATERMARK_MAX_SAMPLES)); +static IIO_DEVICE_ATTR_RO(hwfifo_watermark, 0); +IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_enabled, "1"); + +const struct iio_dev_attr *inv_icm42600_buffer_attrs[] = { + &iio_dev_attr_hwfifo_watermark_min, + &iio_dev_attr_hwfifo_watermark_max, + &iio_dev_attr_hwfifo_watermark, + &iio_dev_attr_hwfifo_enabled, + NULL +}; + int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st, unsigned int max) { diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h index 6ab1eb1a0051..3b81dba4b7c4 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h @@ -79,6 +79,7 @@ ssize_t inv_icm42600_fifo_decode_packet(const void *packet, const void **accel, const void **timestamp, unsigned int *odr); extern const struct iio_buffer_setup_ops inv_icm42600_buffer_ops; +extern const struct iio_dev_attr *inv_icm42600_buffer_attrs[]; int inv_icm42600_buffer_init(struct inv_icm42600_state *st); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index ea8aa64a3f33..881612a8e92d 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -773,8 +773,9 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) indio_dev->available_scan_masks = inv_icm42600_gyro_scan_masks; indio_dev->setup_ops = &inv_icm42600_buffer_ops; - ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, - &inv_icm42600_buffer_ops); + ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev, + &inv_icm42600_buffer_ops, + inv_icm42600_buffer_attrs); if (ret) return ERR_PTR(ret); From e6026ff7e67dda516dd4701956160649b97c9636 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 24 Jun 2026 13:38:23 +0800 Subject: [PATCH 092/513] iio: imu: inv_icm45600: Use I2C match data The I2C driver provides chip_info through both the OF match table and the I2C id table, but probe reads it with device_get_match_data(). That misses id-table driver_data for non-firmware I2C matches and can reject a supported device with -ENODEV. Use i2c_get_match_data() so the id-table chip_info is used when firmware match data is not present. Signed-off-by: Pengpeng Hou Reviewed-by: Andy Shevchenko Reviewed-by: Jean-Baptiste Maneyrol Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c index 26fba538a3cf..f606e54a195a 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c @@ -23,7 +23,7 @@ static int inv_icm45600_probe(struct i2c_client *client) if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) return -ENODEV; - chip_info = device_get_match_data(&client->dev); + chip_info = i2c_get_match_data(client); if (!chip_info) return -ENODEV; From bb6ea868b83ebcfa680db1ac2669fa96569160b5 Mon Sep 17 00:00:00 2001 From: Salih Erim Date: Wed, 24 Jun 2026 15:37:18 +0100 Subject: [PATCH 093/513] dt-bindings: iio: adc: add xlnx,versal-sysmon binding Add devicetree binding for the AMD/Xilinx Versal System Monitor (SysMon). The Versal SysMon is the successor to the Zynq UltraScale+ AMS block, providing on-chip voltage and temperature monitoring. The hardware supports up to 160 supply voltage measurement points and up to 64 temperature satellites distributed across the SoC, with configurable threshold alarms and oversampling. The device can be accessed via memory-mapped I/O or via an I2C interface. Supply and temperature channels are described as child nodes under container nodes, referencing the standard adc.yaml binding for channel properties. Co-developed-by: Michal Simek Signed-off-by: Michal Simek Signed-off-by: Salih Erim Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/xlnx,versal-sysmon.yaml | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/xlnx,versal-sysmon.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/xlnx,versal-sysmon.yaml b/Documentation/devicetree/bindings/iio/adc/xlnx,versal-sysmon.yaml new file mode 100644 index 000000000000..1ad58e3d6163 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/xlnx,versal-sysmon.yaml @@ -0,0 +1,154 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/xlnx,versal-sysmon.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AMD/Xilinx Versal System Monitor + +maintainers: + - Salih Erim + +description: + The AMD/Xilinx Versal System Monitor (SysMon) is the successor to the + Zynq UltraScale+ AMS block. It provides on-chip voltage and temperature + monitoring with up to 160 voltage measurement points and up to + 64 temperature satellites distributed across the SoC. The hardware + supports configurable threshold alarms and oversampling. The device + can be accessed via memory-mapped I/O or via an I2C interface. + +properties: + compatible: + const: xlnx,versal-sysmon + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + '#io-channel-cells': + const: 1 + + voltage-channels: + type: object + description: + Container for voltage measurement channels. + + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + patternProperties: + '^channel@([0-9a-f]|[1-9][0-9a-f])$': + $ref: adc.yaml + + description: + Measures a voltage rail. The register index and rail + name are assigned by the hardware design tool (Vivado). + + properties: + reg: + minimum: 0 + maximum: 159 + description: + Voltage measurement register index assigned by the hardware + design tool. + + required: + - reg + - label + + unevaluatedProperties: false + + required: + - '#address-cells' + - '#size-cells' + + additionalProperties: false + + temperature-channels: + type: object + description: + Container for temperature satellite measurement channels. + + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + patternProperties: + '^channel@([1-9a-f]|[1-3][0-9a-f]|40)$': + $ref: adc.yaml + + description: + Reads a temperature satellite sensor. Each satellite monitors + a specific region of the SoC die. + + properties: + reg: + minimum: 1 + maximum: 64 + description: + Temperature satellite number (1-based hardware index). + + required: + - reg + - label + + unevaluatedProperties: false + + required: + - '#address-cells' + - '#size-cells' + + additionalProperties: false + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + + sysmon@f1270000 { + compatible = "xlnx,versal-sysmon"; + reg = <0xf1270000 0x4000>; + interrupts = ; + #io-channel-cells = <1>; + + voltage-channels { + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + label = "vccaux"; + }; + + channel@3 { + reg = <3>; + label = "vcc_ram"; + bipolar; + }; + }; + + temperature-channels { + #address-cells = <1>; + #size-cells = <0>; + + channel@a { + reg = <10>; + label = "aie-temp-ch1"; + }; + }; + }; From 50bf4fd85b25e26eac72e6c65b4b54ba6e9f6bda Mon Sep 17 00:00:00 2001 From: Salih Erim Date: Wed, 24 Jun 2026 15:37:19 +0100 Subject: [PATCH 094/513] iio: adc: add Versal SysMon driver Add the core driver and MMIO platform driver for the AMD/Xilinx Versal System Monitor (SysMon) block. The SysMon block resides in the platform management controller (PMC) and provides on-chip voltage and temperature monitoring through a 10-bit, 200 kSPS ADC. It can monitor up to 160 voltage channels and 64 temperature satellites distributed across the SoC, with a consistent sample rate of 8 kSPS per channel regardless of how many channels are enabled. The hardware also provides four aggregate temperature registers that are always present regardless of the device tree configuration: the current max and min across all active satellites, and the peak and trough values recorded since the last hardware reset. The driver is split into two compilation units: - versal-sysmon-core: Channel parsing, IIO registration, read_raw - versal-sysmon: MMIO platform driver with custom regmap accessors Voltage results are stored in a 19-bit modified floating-point format and converted to millivolts. Temperature results are stored in Q8.7 signed fixed-point Celsius format and converted to millicelsius. The MMIO regmap backend uses a custom reg_write accessor that automatically unlocks the NPI (NoC programming interface) lock register before each write, as required by the hardware. The regmap is configured with fast_io since the underlying MMIO accessors are safe to call from atomic context. Co-developed-by: Michal Simek Signed-off-by: Michal Simek Signed-off-by: Salih Erim Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- MAINTAINERS | 8 + drivers/iio/adc/Kconfig | 20 ++ drivers/iio/adc/Makefile | 2 + drivers/iio/adc/versal-sysmon-core.c | 292 +++++++++++++++++++++++++++ drivers/iio/adc/versal-sysmon.c | 92 +++++++++ drivers/iio/adc/versal-sysmon.h | 67 ++++++ 6 files changed, 481 insertions(+) create mode 100644 drivers/iio/adc/versal-sysmon-core.c create mode 100644 drivers/iio/adc/versal-sysmon.c create mode 100644 drivers/iio/adc/versal-sysmon.h diff --git a/MAINTAINERS b/MAINTAINERS index c401a3ff5994..0b46a74d74bb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -29627,6 +29627,14 @@ F: Documentation/devicetree/bindings/memory-controllers/xlnx,versal-net-ddrmc5.y F: drivers/edac/versalnet_edac.c F: include/linux/cdx/edac_cdx_pcol.h +XILINX VERSAL SYSMON DRIVER +M: Salih Erim +M: Sai Krishna Potthuri +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/adc/xlnx,versal-sysmon.yaml +F: drivers/iio/adc/versal-sysmon* + XILINX WATCHDOG DRIVER M: Srinivas Neeli R: Shubhrajyoti Datta diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 441e5c660716..d1d776a7a9ba 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1973,6 +1973,26 @@ config TWL6030_GPADC This driver can also be built as a module. If so, the module will be called twl6030-gpadc. +config VERSAL_SYSMON_CORE + tristate + select REGMAP + +config VERSAL_SYSMON + tristate "AMD Versal SysMon driver" + depends on ARCH_ZYNQMP || COMPILE_TEST + depends on HAS_IOMEM + select VERSAL_SYSMON_CORE + help + Say yes here to have support for the AMD/Xilinx Versal System + Monitor (SysMon). This driver provides voltage and temperature + monitoring through the IIO subsystem. + + The SysMon measures up to 160 supply voltages and reads up to + 64 temperature satellites distributed across the SoC. + + To compile this driver as a module, choose M here: the module + will be called versal-sysmon. + config VF610_ADC tristate "Freescale vf610 ADC driver" depends on HAS_IOMEM diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index dc24d0ae76a5..667f7e18506e 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -169,6 +169,8 @@ obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o obj-$(CONFIG_TI_TSC2046) += ti-tsc2046.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o +obj-$(CONFIG_VERSAL_SYSMON_CORE) += versal-sysmon-core.o +obj-$(CONFIG_VERSAL_SYSMON) += versal-sysmon.o obj-$(CONFIG_VF610_ADC) += vf610_adc.o obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o diff --git a/drivers/iio/adc/versal-sysmon-core.c b/drivers/iio/adc/versal-sysmon-core.c new file mode 100644 index 000000000000..03a745d3fb4f --- /dev/null +++ b/drivers/iio/adc/versal-sysmon-core.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Versal SysMon core driver + * + * Copyright (C) 2019 - 2022, Xilinx, Inc. + * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "versal-sysmon.h" + +#define SYSMON_CHAN_TEMP(_chan, _address, _name) \ +{ \ + .type = IIO_TEMP, \ + .indexed = 1, \ + .address = _address, \ + .channel = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = _name, \ +} + +/* + * Static temperature channels (always present). + * + * These are hardware-computed aggregate registers across all active + * temperature satellites: + * temp: current max temperature across all active satellites + * min: current min temperature across all active satellites + * max_max: highest peak recorded since last hardware reset + * min_min: lowest trough recorded since last hardware reset + */ +static const struct iio_chan_spec temp_channels[] = { + SYSMON_CHAN_TEMP(0, SYSMON_TEMP_MAX, "temp"), + SYSMON_CHAN_TEMP(1, SYSMON_TEMP_MIN, "min"), + SYSMON_CHAN_TEMP(2, SYSMON_TEMP_MAX_MAX, "max_max"), + SYSMON_CHAN_TEMP(3, SYSMON_TEMP_MIN_MIN, "min_min"), +}; + +static void sysmon_supply_rawtoprocessed(int raw_data, int *val) +{ + int mantissa, format, exponent; + + mantissa = FIELD_GET(SYSMON_MANTISSA_MASK, raw_data); + exponent = SYSMON_SUPPLY_MANTISSA_BITS - FIELD_GET(SYSMON_MODE_MASK, raw_data); + format = FIELD_GET(SYSMON_FMT_MASK, raw_data); + /* + * When format bit is set the mantissa is two's complement + * (per hardware spec); sign-extend to int for correct arithmetic. + */ + if (format) + mantissa = sign_extend32(mantissa, 15); + + *val = (mantissa * (int)MILLI) >> exponent; +} + +static int sysmon_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int regval; + int ret; + + guard(mutex)(&sysmon->lock); + + switch (chan->type) { + case IIO_TEMP: + if (mask == IIO_CHAN_INFO_SCALE) { + /* Q8.7 to millicelsius: raw * 1000 / 128 */ + *val = MILLIDEGREE_PER_DEGREE; + *val2 = BIT(SYSMON_FRACTIONAL_SHIFT); + return IIO_VAL_FRACTIONAL; + } + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + ret = regmap_read(sysmon->regmap, chan->address, ®val); + if (ret) + return ret; + + *val = sign_extend32(regval, 15); + return IIO_VAL_INT; + + case IIO_VOLTAGE: + if (mask != IIO_CHAN_INFO_PROCESSED) + return -EINVAL; + + ret = regmap_read(sysmon->regmap, + chan->address * SYSMON_REG_STRIDE + + SYSMON_SUPPLY_BASE, ®val); + if (ret) + return ret; + + sysmon_supply_rawtoprocessed(regval, val); + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int sysmon_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + char *label) +{ + if (chan->datasheet_name) + return sysfs_emit(label, "%s\n", chan->datasheet_name); + + return -EINVAL; +} + +static const struct iio_info sysmon_iio_info = { + .read_raw = sysmon_read_raw, + .read_label = sysmon_read_label, +}; + +/** + * sysmon_parse_fw() - Parse firmware nodes and configure IIO channels. + * @indio_dev: IIO device instance + * @dev: Parent device + * + * Reads voltage-channels and temperature-channels container nodes from + * firmware and builds the IIO channel array. Static temperature channels + * are prepended, followed by supply and satellite channels from DT. + * + * Return: 0 on success, negative errno on failure. + */ +static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev) +{ + unsigned int num_chan, num_static, num_supply, num_temp; + unsigned int idx, temp_chan_idx, volt_chan_idx; + struct iio_chan_spec *sysmon_channels; + const char *label; + u32 reg; + int ret; + + struct fwnode_handle *supply_node __free(fwnode_handle) = + device_get_named_child_node(dev, "voltage-channels"); + num_supply = fwnode_get_child_node_count(supply_node); + + struct fwnode_handle *temp_node __free(fwnode_handle) = + device_get_named_child_node(dev, "temperature-channels"); + num_temp = fwnode_get_child_node_count(temp_node); + + num_static = ARRAY_SIZE(temp_channels); + num_chan = size_add(num_temp, size_add(num_static, num_supply)); + sysmon_channels = devm_kcalloc(dev, num_chan, sizeof(*sysmon_channels), GFP_KERNEL); + if (!sysmon_channels) + return -ENOMEM; + + /* Static temperature channels first */ + memcpy(sysmon_channels, temp_channels, sizeof(temp_channels)); + idx = num_static; + + /* Supply channels from DT */ + fwnode_for_each_child_node_scoped(supply_node, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return dev_err_probe(dev, ret, + "missing reg for supply channel\n"); + + if (reg > SYSMON_SUPPLY_IDX_MAX) + return dev_err_probe(dev, -EINVAL, + "supply reg %u exceeds max %u\n", + reg, SYSMON_SUPPLY_IDX_MAX); + + ret = fwnode_property_read_string(child, "label", &label); + if (ret) + return dev_err_probe(dev, ret, + "missing label for supply channel\n"); + + sysmon_channels[idx++] = (struct iio_chan_spec) { + .type = IIO_VOLTAGE, + .indexed = 1, + .address = reg, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .datasheet_name = label, + }; + } + + /* Temperature satellite channels from DT */ + fwnode_for_each_child_node_scoped(temp_node, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return dev_err_probe(dev, ret, + "missing reg for temp channel\n"); + + if (reg < 1 || reg > SYSMON_TEMP_SAT_MAX) + return dev_err_probe(dev, -EINVAL, + "temp reg %u out of range [1..%u]\n", + reg, SYSMON_TEMP_SAT_MAX); + + ret = fwnode_property_read_string(child, "label", &label); + if (ret) + return dev_err_probe(dev, ret, + "missing label for temp channel\n"); + + sysmon_channels[idx++] = (struct iio_chan_spec) { + .type = IIO_TEMP, + .indexed = 1, + .address = SYSMON_TEMP_SAT_BASE + + (reg - 1) * SYSMON_REG_STRIDE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .datasheet_name = label, + }; + } + + indio_dev->num_channels = idx; + indio_dev->info = &sysmon_iio_info; + + /* + * Assign per-type sequential channel numbers. + * IIO sysfs uses type prefix (in_tempN, in_voltageN) + * so numbers only need to be unique within each type. + */ + temp_chan_idx = 0; + volt_chan_idx = 0; + for (unsigned int idx = 0; idx < indio_dev->num_channels; idx++) { + if (sysmon_channels[idx].type == IIO_TEMP) + sysmon_channels[idx].channel = temp_chan_idx++; + else + sysmon_channels[idx].channel = volt_chan_idx++; + } + + indio_dev->channels = sysmon_channels; + + return 0; +} + +/** + * devm_versal_sysmon_core_probe() - Initialize Versal SysMon core + * @dev: Parent device + * @regmap: Register map for hardware access + * + * Return: 0 on success, negative errno on failure. + */ +int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap) +{ + struct iio_dev *indio_dev; + struct sysmon *sysmon; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*sysmon)); + if (!indio_dev) + return -ENOMEM; + + sysmon = iio_priv(indio_dev); + sysmon->regmap = regmap; + + ret = devm_mutex_init(dev, &sysmon->lock); + if (ret) + return ret; + + /* Disable all interrupts and clear pending status */ + ret = regmap_write(sysmon->regmap, SYSMON_IDR, SYSMON_INTR_ALL_MASK); + if (ret) + return ret; + ret = regmap_write(sysmon->regmap, SYSMON_ISR, SYSMON_INTR_ALL_MASK); + if (ret) + return ret; + + indio_dev->name = "versal-sysmon"; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = sysmon_parse_fw(indio_dev, dev); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_NS_GPL(devm_versal_sysmon_core_probe, "VERSAL_SYSMON"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AMD Versal SysMon Core Driver"); +MODULE_AUTHOR("Salih Erim "); diff --git a/drivers/iio/adc/versal-sysmon.c b/drivers/iio/adc/versal-sysmon.c new file mode 100644 index 000000000000..529d0486c9f9 --- /dev/null +++ b/drivers/iio/adc/versal-sysmon.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Versal SysMon MMIO platform driver + * + * Copyright (C) 2019 - 2022, Xilinx, Inc. + * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "versal-sysmon.h" + +struct sysmon_mmio { + void __iomem *base; +}; + +static int sysmon_mmio_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct sysmon_mmio *mmio = context; + + *val = readl(mmio->base + reg); + return 0; +} + +static int sysmon_mmio_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct sysmon_mmio *mmio = context; + + /* NPI must be unlocked before any register write except to NPI_LOCK */ + if (reg != SYSMON_NPI_LOCK) + writel(SYSMON_NPI_UNLOCK_CODE, mmio->base + SYSMON_NPI_LOCK); + writel(val, mmio->base + reg); + + return 0; +} + +static const struct regmap_config sysmon_mmio_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = SYSMON_REG_STRIDE, + .max_register = SYSMON_MAX_REG, + .reg_read = sysmon_mmio_reg_read, + .reg_write = sysmon_mmio_reg_write, + .fast_io = true, +}; + +static int sysmon_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sysmon_mmio *mmio; + struct regmap *regmap; + + mmio = devm_kzalloc(dev, sizeof(*mmio), GFP_KERNEL); + if (!mmio) + return -ENOMEM; + + mmio->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mmio->base)) + return PTR_ERR(mmio->base); + + regmap = devm_regmap_init(dev, NULL, mmio, &sysmon_mmio_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return devm_versal_sysmon_core_probe(dev, regmap); +} + +static const struct of_device_id sysmon_of_match_table[] = { + { .compatible = "xlnx,versal-sysmon" }, + { } +}; +MODULE_DEVICE_TABLE(of, sysmon_of_match_table); + +static struct platform_driver sysmon_platform_driver = { + .probe = sysmon_platform_probe, + .driver = { + .name = "versal-sysmon", + .of_match_table = sysmon_of_match_table, + }, +}; +module_platform_driver(sysmon_platform_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AMD Versal SysMon Platform Driver"); +MODULE_IMPORT_NS("VERSAL_SYSMON"); +MODULE_AUTHOR("Salih Erim "); diff --git a/drivers/iio/adc/versal-sysmon.h b/drivers/iio/adc/versal-sysmon.h new file mode 100644 index 000000000000..e27a53575752 --- /dev/null +++ b/drivers/iio/adc/versal-sysmon.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * AMD Versal SysMon driver + * + * Copyright (C) 2019 - 2022, Xilinx, Inc. + * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc. + */ + +#ifndef _VERSAL_SYSMON_H_ +#define _VERSAL_SYSMON_H_ + +#include +#include + +struct device; +struct regmap; + +/* Register offsets (sorted by address) */ +#define SYSMON_NPI_LOCK 0x000C +#define SYSMON_ISR 0x0044 +#define SYSMON_IDR 0x0050 +#define SYSMON_TEMP_MAX 0x1030 +#define SYSMON_TEMP_MIN 0x1034 +#define SYSMON_SUPPLY_BASE 0x1040 +#define SYSMON_TEMP_MIN_MIN 0x1F8C +#define SYSMON_TEMP_MAX_MAX 0x1F90 +#define SYSMON_TEMP_SAT_BASE 0x1FAC +#define SYSMON_MAX_REG 0x24C0 + +/* NPI unlock value written to SYSMON_NPI_LOCK */ +#define SYSMON_NPI_UNLOCK_CODE 0xF9E8D7C6 + +/* Register stride: 4 bytes per 32-bit register */ +#define SYSMON_REG_STRIDE 4 + +#define SYSMON_SUPPLY_IDX_MAX 159 +#define SYSMON_TEMP_SAT_MAX 64 +#define SYSMON_INTR_ALL_MASK GENMASK(31, 0) + +/* Supply voltage conversion register fields */ +#define SYSMON_MANTISSA_MASK GENMASK(15, 0) +#define SYSMON_FMT_MASK BIT(16) +#define SYSMON_MODE_MASK GENMASK(18, 17) + +/* Q8.7 fractional shift */ +#define SYSMON_FRACTIONAL_SHIFT 7U +#define SYSMON_SUPPLY_MANTISSA_BITS 16 + +/** + * struct sysmon - Driver data for Versal SysMon + * @regmap: register map for hardware access + * @lock: protects read-modify-write sequences on threshold registers + * and cached state that spans multiple regmap calls + */ +struct sysmon { + struct regmap *regmap; + /* + * Protects read-modify-write sequences on threshold registers + * and cached state (oversampling ratios, hysteresis values) + * that spans multiple regmap calls. + */ + struct mutex lock; +}; + +int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap); + +#endif /* _VERSAL_SYSMON_H_ */ From 2dd8c526a272433e0aaefc9db08296f742f2629c Mon Sep 17 00:00:00 2001 From: Salih Erim Date: Wed, 24 Jun 2026 15:37:20 +0100 Subject: [PATCH 095/513] iio: adc: versal-sysmon: add I2C driver Add an I2C transport driver for the Versal SysMon block. The SysMon provides an I2C slave interface that allows an external master to read voltage and temperature measurements through the same register map used by the MMIO path. The I2C command frame is an 8-byte structure containing a 4-byte data payload, a 2-byte register offset, and a 1-byte instruction field. Read operations send the frame with a read instruction, then receive a 4-byte response containing the register value. Events are not supported on the I2C path because there is no interrupt line and the I2C regmap backend cannot be called from atomic context. Co-developed-by: Conall O'Griofa Signed-off-by: Conall O'Griofa Signed-off-by: Salih Erim Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 13 +++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/versal-sysmon-i2c.c | 134 ++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 drivers/iio/adc/versal-sysmon-i2c.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index d1d776a7a9ba..d1b198cb8a80 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1993,6 +1993,19 @@ config VERSAL_SYSMON To compile this driver as a module, choose M here: the module will be called versal-sysmon. +config VERSAL_SYSMON_I2C + tristate "AMD Versal SysMon I2C driver" + depends on I2C + select VERSAL_SYSMON_CORE + help + Say yes here to have support for the AMD/Xilinx Versal System + Monitor (SysMon) via I2C interface. This driver enables voltage + and temperature monitoring when the Versal chip has SysMon + configured with I2C access. + + To compile this driver as a module, choose M here: the module + will be called versal-sysmon-i2c. + config VF610_ADC tristate "Freescale vf610 ADC driver" depends on HAS_IOMEM diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 667f7e18506e..0f90b75577ff 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -171,6 +171,7 @@ obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o obj-$(CONFIG_VERSAL_SYSMON_CORE) += versal-sysmon-core.o obj-$(CONFIG_VERSAL_SYSMON) += versal-sysmon.o +obj-$(CONFIG_VERSAL_SYSMON_I2C) += versal-sysmon-i2c.o obj-$(CONFIG_VF610_ADC) += vf610_adc.o obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o diff --git a/drivers/iio/adc/versal-sysmon-i2c.c b/drivers/iio/adc/versal-sysmon-i2c.c new file mode 100644 index 000000000000..e9a7629159ab --- /dev/null +++ b/drivers/iio/adc/versal-sysmon-i2c.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Versal SysMon I2C driver + * + * Copyright (C) 2023 - 2026, Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "versal-sysmon.h" + +#define SYSMON_I2C_INSTR_READ BIT(2) +#define SYSMON_I2C_INSTR_WRITE BIT(3) + +/* + * I2C command frame layout (8 bytes): + * [0..3] data payload (little-endian u32) + * [4..5] register offset >> 2 (little-endian u16) + * [6] instruction (read/write) + * [7] reserved + */ +#define SYSMON_I2C_DATA_OFS 0 +#define SYSMON_I2C_REG_OFS 4 +#define SYSMON_I2C_INSTR_OFS 6 + +static int sysmon_i2c_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct i2c_client *client = context; + u8 write_buf[8] = { }; + u8 read_buf[4]; + int ret; + + put_unaligned_le16(reg >> 2, &write_buf[SYSMON_I2C_REG_OFS]); + write_buf[SYSMON_I2C_INSTR_OFS] = SYSMON_I2C_INSTR_READ; + + ret = i2c_master_send(client, write_buf, sizeof(write_buf)); + if (ret < 0) + return ret; + if (ret != sizeof(write_buf)) + return -EIO; + + ret = i2c_master_recv(client, read_buf, sizeof(read_buf)); + if (ret < 0) + return ret; + if (ret != sizeof(read_buf)) + return -EIO; + + *val = get_unaligned_le32(read_buf); + + return 0; +} + +static int sysmon_i2c_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct i2c_client *client = context; + u8 write_buf[8] = { }; + int ret; + + put_unaligned_le32(val, &write_buf[SYSMON_I2C_DATA_OFS]); + put_unaligned_le16(reg >> 2, &write_buf[SYSMON_I2C_REG_OFS]); + write_buf[SYSMON_I2C_INSTR_OFS] = SYSMON_I2C_INSTR_WRITE; + + ret = i2c_master_send(client, write_buf, sizeof(write_buf)); + if (ret < 0) + return ret; + if (ret != sizeof(write_buf)) + return -EIO; + + return 0; +} + +/* + * Almost all registers are volatile (live ADC readings, interrupt + * status). The rest are not accessed often enough to benefit from + * caching. + */ +static const struct regmap_config sysmon_i2c_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = SYSMON_REG_STRIDE, + .max_register = SYSMON_MAX_REG, + .reg_read = sysmon_i2c_reg_read, + .reg_write = sysmon_i2c_reg_write, +}; + +static int sysmon_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct regmap *regmap; + + regmap = devm_regmap_init(dev, NULL, client, &sysmon_i2c_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* I2C has no IRQ connection; events are not supported */ + return devm_versal_sysmon_core_probe(dev, regmap); +} + +static const struct of_device_id sysmon_i2c_of_match_table[] = { + { .compatible = "xlnx,versal-sysmon" }, + { } +}; +MODULE_DEVICE_TABLE(of, sysmon_i2c_of_match_table); + +static const struct i2c_device_id sysmon_i2c_id_table[] = { + { .name = "versal-sysmon" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sysmon_i2c_id_table); + +static struct i2c_driver sysmon_i2c_driver = { + .probe = sysmon_i2c_probe, + .driver = { + .name = "versal-sysmon-i2c", + .of_match_table = sysmon_i2c_of_match_table, + }, + .id_table = sysmon_i2c_id_table, +}; +module_i2c_driver(sysmon_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AMD Versal SysMon I2C Driver"); +MODULE_IMPORT_NS("VERSAL_SYSMON"); +MODULE_AUTHOR("Conall O'Griofa "); +MODULE_AUTHOR("Salih Erim "); From c7f7eb10638f8757129fcf9785e70d364b8c0f3d Mon Sep 17 00:00:00 2001 From: Salih Erim Date: Wed, 24 Jun 2026 15:37:21 +0100 Subject: [PATCH 096/513] iio: adc: versal-sysmon: add threshold event support Add threshold event support for temperature and supply voltage channels. Temperature events: - Rising threshold with configurable value on the device temperature channel (current max across all satellites) - Per-channel hysteresis as a millicelsius value - Event direction is IIO_EV_DIR_RISING (hysteresis mode) Supply voltage events: - Rising/falling threshold per supply channel - Per-channel alarm enable via alarm configuration registers The hardware supports both window and hysteresis alarm modes for temperature. This driver uses hysteresis mode, where the upper threshold triggers the alarm and the lower threshold clears it (re-arm point). The hardware has a single ISR bit per temperature channel with no indication of which threshold was crossed, so hysteresis mode is the natural fit. The lower threshold register is computed internally as (upper - hysteresis). Hysteresis is stored in the driver as a millicelsius value, initialized from the hardware registers at probe. Writing the rising threshold or hysteresis recomputes the lower register. ALARM_CONFIG is hard-coded to hysteresis mode during init. The hardware also provides a separate over-temperature (OT) threshold, but it is not exposed through IIO as it serves as a hardware safety mechanism for platform shutdown. OT will be exposed through the thermal framework in a follow-up series. The interrupt handler masks active threshold interrupts (which are level-sensitive) and schedules a delayed worker to poll for condition clear before unmasking. When no hardware IRQ is available, event specs are not attached and interrupt init is skipped, since the I2C regmap backend cannot be called from atomic context. When disabling a supply channel alarm, the group interrupt remains active if any other channel in the same alarm group still has an alarm enabled. A devm cleanup action masks all interrupts on driver unbind to prevent unhandled interrupt storms after the IRQ handler is freed. Signed-off-by: Salih Erim Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/versal-sysmon-core.c | 613 ++++++++++++++++++++++++++- drivers/iio/adc/versal-sysmon.h | 36 ++ 2 files changed, 645 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/versal-sysmon-core.c b/drivers/iio/adc/versal-sysmon-core.c index 03a745d3fb4f..8f2c502d9cb6 100644 --- a/drivers/iio/adc/versal-sysmon-core.c +++ b/drivers/iio/adc/versal-sysmon-core.c @@ -12,6 +12,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -20,10 +23,18 @@ #include #include +#include #include #include "versal-sysmon.h" +/* TEMP hysteresis mode bit in SYSMON_TEMP_EV_CFG */ +#define SYSMON_TEMP_HYST_MASK BIT(1) + +/* Compute alarm register offset from a channel address */ +#define SYSMON_ALARM_OFFSET(addr) \ + (SYSMON_ALARM_REG + ((addr) / SYSMON_ALARM_BITS_PER_REG) * SYSMON_REG_STRIDE) + #define SYSMON_CHAN_TEMP(_chan, _address, _name) \ { \ .type = IIO_TEMP, \ @@ -35,6 +46,45 @@ .datasheet_name = _name, \ } +enum sysmon_alarm_bit { + SYSMON_BIT_ALARM0 = 0, + SYSMON_BIT_ALARM1 = 1, + SYSMON_BIT_ALARM2 = 2, + SYSMON_BIT_ALARM3 = 3, + SYSMON_BIT_ALARM4 = 4, + SYSMON_BIT_TEMP = 9, +}; + +/* Temperature event specification: rising threshold + hysteresis only */ +static const struct iio_event_spec sysmon_temp_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +/* Supply event specifications */ +static const struct iio_event_spec sysmon_supply_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + /* * Static temperature channels (always present). * @@ -52,6 +102,16 @@ static const struct iio_chan_spec temp_channels[] = { SYSMON_CHAN_TEMP(3, SYSMON_TEMP_MIN_MIN, "min_min"), }; +static void sysmon_q8p7_to_millicelsius(s16 raw_data, int *val) +{ + *val = (raw_data * MILLIDEGREE_PER_DEGREE) >> SYSMON_FRACTIONAL_SHIFT; +} + +static void sysmon_millicelsius_to_q8p7(u32 *raw_data, int val) +{ + *raw_data = (val << SYSMON_FRACTIONAL_SHIFT) / MILLIDEGREE_PER_DEGREE; +} + static void sysmon_supply_rawtoprocessed(int raw_data, int *val) { int mantissa, format, exponent; @@ -69,6 +129,33 @@ static void sysmon_supply_rawtoprocessed(int raw_data, int *val) *val = (mantissa * (int)MILLI) >> exponent; } +static void sysmon_supply_processedtoraw(int val, u32 reg_val, u32 *raw_data) +{ + int exponent = FIELD_GET(SYSMON_MODE_MASK, reg_val); + int format = FIELD_GET(SYSMON_FMT_MASK, reg_val); + int scale, tmp; + + scale = BIT(SYSMON_SUPPLY_MANTISSA_BITS - exponent); + tmp = (val * scale) / (int)MILLI; + + if (format) + tmp = clamp(tmp, S16_MIN, S16_MAX); + else + tmp = clamp(tmp, 0, U16_MAX); + + *raw_data = (u16)tmp; +} + +static int sysmon_supply_thresh_offset(unsigned long address, enum iio_event_direction dir) +{ + if (dir == IIO_EV_DIR_RISING) + return (address * SYSMON_REG_STRIDE) + SYSMON_SUPPLY_TH_UP; + if (dir == IIO_EV_DIR_FALLING) + return (address * SYSMON_REG_STRIDE) + SYSMON_SUPPLY_TH_LOW; + + return -EINVAL; +} + static int sysmon_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -115,6 +202,269 @@ static int sysmon_read_raw(struct iio_dev *indio_dev, } } +static u32 sysmon_get_event_mask(const struct iio_chan_spec *chan) +{ + if (chan->type == IIO_TEMP) + return BIT(SYSMON_BIT_TEMP); + + return BIT(chan->address / SYSMON_ALARM_BITS_PER_REG); +} + +static int sysmon_read_alarm_config(struct sysmon *sysmon, + unsigned long address) +{ + u32 shift = address % SYSMON_ALARM_BITS_PER_REG; + u32 offset = SYSMON_ALARM_OFFSET(address); + + return regmap_test_bits(sysmon->regmap, offset, BIT(shift)); +} + +static int sysmon_write_alarm_config(struct sysmon *sysmon, + unsigned long address, bool enable) +{ + u32 shift = address % SYSMON_ALARM_BITS_PER_REG; + u32 offset = SYSMON_ALARM_OFFSET(address); + + return regmap_assign_bits(sysmon->regmap, offset, BIT(shift), enable); +} + +static int sysmon_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + u32 mask = sysmon_get_event_mask(chan); + unsigned int imr; + int config_value; + int ret; + + ret = regmap_read(sysmon->regmap, SYSMON_IMR, &imr); + if (ret) + return ret; + + /* IMR bits are 1=masked, invert to get 1=enabled */ + imr = ~imr; + + switch (chan->type) { + case IIO_VOLTAGE: + config_value = sysmon_read_alarm_config(sysmon, chan->address); + if (config_value < 0) + return config_value; + return config_value && (imr & mask); + + case IIO_TEMP: + /* + * Return the administrative state, not the hardware IMR. + * The IRQ handler temporarily masks the interrupt during + * the polling window; reading IMR would show it as disabled. + * temp_mask bit is set when administratively disabled. + */ + return !(sysmon->temp_mask & mask); + + default: + return -EINVAL; + } +} + +static int sysmon_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + u32 offset = SYSMON_ALARM_OFFSET(chan->address); + struct sysmon *sysmon = iio_priv(indio_dev); + u32 mask = sysmon_get_event_mask(chan); + unsigned int alarm_config; + int ret; + + guard(mutex)(&sysmon->lock); + + switch (chan->type) { + case IIO_VOLTAGE: + ret = sysmon_write_alarm_config(sysmon, chan->address, state); + if (ret) + return ret; + + ret = regmap_read(sysmon->regmap, offset, &alarm_config); + if (ret) + return ret; + + if (alarm_config) + return regmap_write(sysmon->regmap, SYSMON_IER, mask); + + return regmap_write(sysmon->regmap, SYSMON_IDR, mask); + + case IIO_TEMP: + if (state) { + ret = regmap_write(sysmon->regmap, SYSMON_IER, mask); + if (ret) + return ret; + + scoped_guard(spinlock_irq, &sysmon->irq_lock) + sysmon->temp_mask &= ~mask; + } else { + ret = regmap_write(sysmon->regmap, SYSMON_IDR, mask); + if (ret) + return ret; + + scoped_guard(spinlock_irq, &sysmon->irq_lock) + sysmon->temp_mask |= mask; + } + return 0; + + default: + return -EINVAL; + } +} + +/* + * Recompute the lower threshold register from upper threshold and + * cached hysteresis. Called when either upper threshold or hysteresis + * is written. + */ +static int sysmon_update_temp_lower(struct sysmon *sysmon) +{ + unsigned int upper_reg; + int upper_mc, lower_mc; + u32 raw_val; + int ret; + + ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, &upper_reg); + if (ret) + return ret; + + sysmon_q8p7_to_millicelsius(upper_reg, &upper_mc); + lower_mc = clamp(upper_mc - sysmon->temp_hysteresis, -256000, 255992); + sysmon_millicelsius_to_q8p7(&raw_val, lower_mc); + + return regmap_write(sysmon->regmap, SYSMON_TEMP_TH_LOW, raw_val); +} + +static int sysmon_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int reg_val; + int offset; + int ret; + + guard(mutex)(&sysmon->lock); + + switch (chan->type) { + case IIO_TEMP: + switch (info) { + case IIO_EV_INFO_VALUE: + ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, ®_val); + if (ret) + return ret; + + sysmon_q8p7_to_millicelsius(reg_val, val); + + return IIO_VAL_INT; + + case IIO_EV_INFO_HYSTERESIS: + *val = sysmon->temp_hysteresis; + return IIO_VAL_INT; + + default: + return -EINVAL; + } + + case IIO_VOLTAGE: + offset = sysmon_supply_thresh_offset(chan->address, dir); + if (offset < 0) + return offset; + + ret = regmap_read(sysmon->regmap, offset, ®_val); + if (ret) + return ret; + + sysmon_supply_rawtoprocessed(reg_val, val); + + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int sysmon_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int reg_val; + u32 raw_val; + int offset; + int ret; + + guard(mutex)(&sysmon->lock); + + switch (chan->type) { + case IIO_TEMP: + switch (info) { + case IIO_EV_INFO_VALUE: + /* Q8.7 signed range: -256000 to +255992 mC */ + if (val < -256000 || val > 255992) + return -EINVAL; + + sysmon_millicelsius_to_q8p7(&raw_val, val); + + ret = regmap_write(sysmon->regmap, SYSMON_TEMP_TH_UP, raw_val); + if (ret) + return ret; + + /* Recompute lower = upper - hysteresis */ + return sysmon_update_temp_lower(sysmon); + + case IIO_EV_INFO_HYSTERESIS: + if (val < 0) + return -EINVAL; + + sysmon->temp_hysteresis = val; + + return sysmon_update_temp_lower(sysmon); + + default: + return -EINVAL; + } + + case IIO_VOLTAGE: + offset = sysmon_supply_thresh_offset(chan->address, dir); + if (offset < 0) + return offset; + + ret = regmap_read(sysmon->regmap, offset, ®_val); + if (ret) + return ret; + + /* Clamp to prevent overflow in processedtoraw conversion */ + if (val < -32768 || val > 32767) + return -EINVAL; + + sysmon_supply_processedtoraw(val, reg_val, &raw_val); + + /* + * The hardware threshold register returns FMT and MODE + * bits in the upper 16 bits on read, but only the lower + * 16-bit mantissa is used on write. + */ + return regmap_write(sysmon->regmap, offset, raw_val); + + default: + return -EINVAL; + } +} + static int sysmon_read_label(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, char *label) @@ -128,20 +478,242 @@ static int sysmon_read_label(struct iio_dev *indio_dev, static const struct iio_info sysmon_iio_info = { .read_raw = sysmon_read_raw, .read_label = sysmon_read_label, + .read_event_config = sysmon_read_event_config, + .write_event_config = sysmon_write_event_config, + .read_event_value = sysmon_read_event_value, + .write_event_value = sysmon_write_event_value, }; +static void sysmon_push_event(struct iio_dev *indio_dev, u32 address) +{ + const struct iio_chan_spec *chan; + enum iio_event_direction dir; + + for (unsigned int i = 0; i < indio_dev->num_channels; i++) { + if (indio_dev->channels[i].address != address) + continue; + + chan = &indio_dev->channels[i]; + /* Temp uses hysteresis mode (rising only), voltage uses window */ + dir = (chan->type == IIO_TEMP) ? IIO_EV_DIR_RISING : + IIO_EV_DIR_EITHER; + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(chan->type, + chan->channel, + IIO_EV_TYPE_THRESH, + dir), + iio_get_time_ns(indio_dev)); + } +} + +static int sysmon_handle_event(struct iio_dev *indio_dev, u32 event) +{ + u32 alarm_flag_offset = SYSMON_ALARM_FLAG + event * SYSMON_REG_STRIDE; + u32 alarm_reg_offset = SYSMON_ALARM_REG + event * SYSMON_REG_STRIDE; + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned long alarm_flag_reg; + unsigned int reg_val; + u32 address, bit; + int ret; + + switch (event) { + case SYSMON_BIT_TEMP: + sysmon_push_event(indio_dev, SYSMON_TEMP_MAX); + + ret = regmap_write(sysmon->regmap, SYSMON_IDR, BIT(SYSMON_BIT_TEMP)); + if (ret) + return ret; + + sysmon->masked_temp |= BIT(SYSMON_BIT_TEMP); + return 0; + + case SYSMON_BIT_ALARM0: + case SYSMON_BIT_ALARM1: + case SYSMON_BIT_ALARM2: + case SYSMON_BIT_ALARM3: + case SYSMON_BIT_ALARM4: + ret = regmap_read(sysmon->regmap, alarm_flag_offset, ®_val); + if (ret) + return ret; + + alarm_flag_reg = reg_val; + + for_each_set_bit(bit, &alarm_flag_reg, SYSMON_ALARM_BITS_PER_REG) { + address = bit + SYSMON_ALARM_BITS_PER_REG * event; + sysmon_push_event(indio_dev, address); + ret = regmap_clear_bits(sysmon->regmap, alarm_reg_offset, BIT(bit)); + if (ret) + return ret; + } + + return regmap_write(sysmon->regmap, alarm_flag_offset, alarm_flag_reg); + + default: + return -EINVAL; + } +} + +static void sysmon_handle_events(struct iio_dev *indio_dev, + unsigned long events) +{ + unsigned int bit; + + for_each_set_bit(bit, &events, SYSMON_NO_OF_EVENTS) + sysmon_handle_event(indio_dev, bit); +} + +static void sysmon_unmask_temp(struct sysmon *sysmon, unsigned int isr) +{ + unsigned int status; + u32 ier; + + status = isr & SYSMON_TEMP_INTR_MASK; + + ier = ~status & sysmon->masked_temp; + sysmon->masked_temp &= status; + + /* Only unmask if not administratively disabled by userspace */ + ier &= ~sysmon->temp_mask; + + regmap_write(sysmon->regmap, SYSMON_IER, ier); +} + +/* + * Versal threshold interrupts are level-sensitive. Active threshold + * interrupts are masked in the handler and polled via delayed work + * until the condition clears, then unmasked. + */ +static void sysmon_unmask_worker(struct work_struct *work) +{ + struct sysmon *sysmon = + container_of(work, struct sysmon, sysmon_unmask_work.work); + unsigned int isr; + + /* + * If the ISR read fails, skip processing to avoid acting + * on undefined data. + */ + scoped_guard(spinlock_irq, &sysmon->irq_lock) { + if (regmap_read(sysmon->regmap, SYSMON_ISR, &isr)) + break; + regmap_write(sysmon->regmap, SYSMON_ISR, isr); + sysmon_unmask_temp(sysmon, isr); + } + + if (sysmon->masked_temp) + schedule_delayed_work(&sysmon->sysmon_unmask_work, + msecs_to_jiffies(SYSMON_UNMASK_WORK_DELAY_MS)); + else + regmap_write(sysmon->regmap, SYSMON_STATUS_RESET, 1); +} + +static irqreturn_t sysmon_iio_irq(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int isr, imr; + + guard(spinlock)(&sysmon->irq_lock); + + if (regmap_read(sysmon->regmap, SYSMON_ISR, &isr) || + regmap_read(sysmon->regmap, SYSMON_IMR, &imr)) + return IRQ_NONE; + + isr &= ~imr; + if (!isr) + return IRQ_NONE; + + regmap_write(sysmon->regmap, SYSMON_ISR, isr); + + sysmon_handle_events(indio_dev, isr); + schedule_delayed_work(&sysmon->sysmon_unmask_work, + msecs_to_jiffies(SYSMON_UNMASK_WORK_DELAY_MS)); + + return IRQ_HANDLED; +} + +static void sysmon_disable_interrupts(void *data) +{ + struct sysmon *sysmon = data; + + regmap_write(sysmon->regmap, SYSMON_IDR, SYSMON_INTR_ALL_MASK); + + scoped_guard(spinlock_irq, &sysmon->irq_lock) + sysmon->masked_temp = 0; + + cancel_delayed_work_sync(&sysmon->sysmon_unmask_work); +} + +static int sysmon_init_interrupt(struct sysmon *sysmon, + struct device *dev, + struct iio_dev *indio_dev, + int irq) +{ + unsigned int imr; + int ret; + + /* Events not supported without IRQ (e.g. I2C path) */ + if (!irq) + return 0; + + INIT_DELAYED_WORK(&sysmon->sysmon_unmask_work, sysmon_unmask_worker); + + ret = regmap_read(sysmon->regmap, SYSMON_IMR, &imr); + if (ret) + return ret; + sysmon->temp_mask = imr & SYSMON_TEMP_INTR_MASK; + + ret = devm_request_irq(dev, irq, sysmon_iio_irq, 0, "sysmon-irq", indio_dev); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, sysmon_disable_interrupts, sysmon); +} + +/* + * Initialize the cached hysteresis for a temperature channel from the + * current hardware threshold registers: hysteresis = upper - lower. + */ +static int sysmon_init_hysteresis(struct sysmon *sysmon, int *hysteresis) +{ + unsigned int upper_reg, lower_reg; + int upper_mc, lower_mc; + int ret; + + ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, &upper_reg); + if (ret) + return ret; + + ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_LOW, &lower_reg); + if (ret) + return ret; + + sysmon_q8p7_to_millicelsius(upper_reg, &upper_mc); + sysmon_q8p7_to_millicelsius(lower_reg, &lower_mc); + *hysteresis = upper_mc - lower_mc; + + return 0; +} + /** * sysmon_parse_fw() - Parse firmware nodes and configure IIO channels. * @indio_dev: IIO device instance * @dev: Parent device + * @irq: IRQ number (positive enables event channels, 0 disables) * * Reads voltage-channels and temperature-channels container nodes from * firmware and builds the IIO channel array. Static temperature channels - * are prepended, followed by supply and satellite channels from DT. + * and event channels are prepended, followed by supply and satellite + * channels from DT. + * + * Event channels and per-channel event specs are only added when the + * device has an IRQ. I2C devices have no interrupt line, and the I2C + * regmap cannot be called from atomic context, so events are not + * supported on that path. * * Return: 0 on success, negative errno on failure. */ -static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev) +static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev, int irq) { unsigned int num_chan, num_static, num_supply, num_temp; unsigned int idx, temp_chan_idx, volt_chan_idx; @@ -164,8 +736,14 @@ static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev) if (!sysmon_channels) return -ENOMEM; - /* Static temperature channels first */ memcpy(sysmon_channels, temp_channels, sizeof(temp_channels)); + + /* Attach event spec to channel 0 when IRQ is available */ + if (irq > 0) { + sysmon_channels[0].event_spec = sysmon_temp_events; + sysmon_channels[0].num_event_specs = ARRAY_SIZE(sysmon_temp_events); + } + idx = num_static; /* Supply channels from DT */ @@ -190,6 +768,10 @@ static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev) .indexed = 1, .address = reg, .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .event_spec = irq > 0 ? + sysmon_supply_events : NULL, + .num_event_specs = irq > 0 ? + ARRAY_SIZE(sysmon_supply_events) : 0, .datasheet_name = label, }; } @@ -255,6 +837,7 @@ int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap) { struct iio_dev *indio_dev; struct sysmon *sysmon; + int irq; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*sysmon)); @@ -267,6 +850,7 @@ int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap) ret = devm_mutex_init(dev, &sysmon->lock); if (ret) return ret; + spin_lock_init(&sysmon->irq_lock); /* Disable all interrupts and clear pending status */ ret = regmap_write(sysmon->regmap, SYSMON_IDR, SYSMON_INTR_ALL_MASK); @@ -276,13 +860,34 @@ int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap) if (ret) return ret; + irq = fwnode_irq_get(dev_fwnode(dev), 0); + if (irq == -EPROBE_DEFER) + return dev_err_probe(dev, irq, "failed to get IRQ\n"); + indio_dev->name = "versal-sysmon"; indio_dev->modes = INDIO_DIRECT_MODE; - ret = sysmon_parse_fw(indio_dev, dev); + ret = sysmon_parse_fw(indio_dev, dev, irq); if (ret) return ret; + if (irq > 0) { + /* Set hysteresis mode for temperature threshold */ + ret = regmap_set_bits(sysmon->regmap, SYSMON_TEMP_EV_CFG, + SYSMON_TEMP_HYST_MASK); + if (ret) + return ret; + + /* Initialize cached hysteresis from hardware registers */ + ret = sysmon_init_hysteresis(sysmon, &sysmon->temp_hysteresis); + if (ret) + return ret; + + ret = sysmon_init_interrupt(sysmon, dev, indio_dev, irq); + if (ret) + return ret; + } + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(devm_versal_sysmon_core_probe, "VERSAL_SYSMON"); diff --git a/drivers/iio/adc/versal-sysmon.h b/drivers/iio/adc/versal-sysmon.h index e27a53575752..9fe2793757a4 100644 --- a/drivers/iio/adc/versal-sysmon.h +++ b/drivers/iio/adc/versal-sysmon.h @@ -11,6 +11,8 @@ #include #include +#include +#include struct device; struct regmap; @@ -18,12 +20,22 @@ struct regmap; /* Register offsets (sorted by address) */ #define SYSMON_NPI_LOCK 0x000C #define SYSMON_ISR 0x0044 +#define SYSMON_IMR 0x0048 +#define SYSMON_IER 0x004C #define SYSMON_IDR 0x0050 #define SYSMON_TEMP_MAX 0x1030 #define SYSMON_TEMP_MIN 0x1034 #define SYSMON_SUPPLY_BASE 0x1040 +#define SYSMON_ALARM_FLAG 0x1018 +#define SYSMON_ALARM_REG 0x1940 +#define SYSMON_TEMP_TH_LOW 0x1970 +#define SYSMON_TEMP_TH_UP 0x1974 +#define SYSMON_SUPPLY_TH_LOW 0x1980 +#define SYSMON_SUPPLY_TH_UP 0x1C80 +#define SYSMON_TEMP_EV_CFG 0x1F84 #define SYSMON_TEMP_MIN_MIN 0x1F8C #define SYSMON_TEMP_MAX_MAX 0x1F90 +#define SYSMON_STATUS_RESET 0x1F94 #define SYSMON_TEMP_SAT_BASE 0x1FAC #define SYSMON_MAX_REG 0x24C0 @@ -35,8 +47,12 @@ struct regmap; #define SYSMON_SUPPLY_IDX_MAX 159 #define SYSMON_TEMP_SAT_MAX 64 +#define SYSMON_NO_OF_EVENTS 32 #define SYSMON_INTR_ALL_MASK GENMASK(31, 0) +/* ISR/IMR temperature alarm mask (bit 9) */ +#define SYSMON_TEMP_INTR_MASK BIT(9) + /* Supply voltage conversion register fields */ #define SYSMON_MANTISSA_MASK GENMASK(15, 0) #define SYSMON_FMT_MASK BIT(16) @@ -46,11 +62,21 @@ struct regmap; #define SYSMON_FRACTIONAL_SHIFT 7U #define SYSMON_SUPPLY_MANTISSA_BITS 16 +/* Bits per alarm register */ +#define SYSMON_ALARM_BITS_PER_REG 32 + +#define SYSMON_UNMASK_WORK_DELAY_MS 500 + /** * struct sysmon - Driver data for Versal SysMon * @regmap: register map for hardware access * @lock: protects read-modify-write sequences on threshold registers * and cached state that spans multiple regmap calls + * @irq_lock: protects interrupt mask register updates (MMIO path only) + * @masked_temp: currently masked temperature alarm bits + * @temp_mask: temperature interrupt configuration mask + * @temp_hysteresis: cached DEVICE_TEMP hysteresis in millicelsius + * @sysmon_unmask_work: re-enables events after alarm condition clears */ struct sysmon { struct regmap *regmap; @@ -60,6 +86,16 @@ struct sysmon { * that spans multiple regmap calls. */ struct mutex lock; + /* + * Protects interrupt mask register updates. Only used on the + * MMIO path (fast_io regmap); I2C has no IRQ and never reaches + * the event code that takes this lock. + */ + spinlock_t irq_lock; + unsigned int masked_temp; + unsigned int temp_mask; + int temp_hysteresis; + struct delayed_work sysmon_unmask_work; }; int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap); From cbfd04ca564037db65e98f97893f49b6d41d84bf Mon Sep 17 00:00:00 2001 From: Salih Erim Date: Wed, 24 Jun 2026 15:37:22 +0100 Subject: [PATCH 097/513] iio: adc: versal-sysmon: add oversampling support Add support for reading and writing the oversampling ratio through the IIO oversampling_ratio attribute. The hardware supports averaging 2, 4, 8, or 16 samples, plus a ratio of 1 (no averaging). Temperature and supply channels share oversampling configuration at the type level (all temperature channels share one ratio, all supply channels share another), exposed through info_mask_shared_by_type. The hardware encoding uses sample_count / 2 in a 4-bit field within the CONFIG register. Per-channel averaging enable registers must also be updated to activate or deactivate averaging. Signed-off-by: Salih Erim Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/versal-sysmon-core.c | 159 ++++++++++++++++++++++++++- drivers/iio/adc/versal-sysmon.h | 17 +++ 2 files changed, 174 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/versal-sysmon-core.c b/drivers/iio/adc/versal-sysmon-core.c index 8f2c502d9cb6..1b55d343982e 100644 --- a/drivers/iio/adc/versal-sysmon-core.c +++ b/drivers/iio/adc/versal-sysmon-core.c @@ -28,6 +28,12 @@ #include "versal-sysmon.h" +/* + * Oversampling ratio values exposed to userspace via IIO. + * Actual number of samples averaged: 1=none, 2=2x, 4=4x, 8=8x, 16=16x. + */ +static const int sysmon_oversampling_avail[] = { 1, 2, 4, 8, 16 }; + /* TEMP hysteresis mode bit in SYSMON_TEMP_EV_CFG */ #define SYSMON_TEMP_HYST_MASK BIT(1) @@ -42,7 +48,11 @@ .address = _address, \ .channel = _chan, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .datasheet_name = _name, \ } @@ -166,6 +176,12 @@ static int sysmon_read_raw(struct iio_dev *indio_dev, guard(mutex)(&sysmon->lock); + if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) { + *val = (chan->type == IIO_TEMP) ? sysmon->temp_oversampling : + sysmon->supply_oversampling; + return IIO_VAL_INT; + } + switch (chan->type) { case IIO_TEMP: if (mask == IIO_CHAN_INFO_SCALE) { @@ -465,6 +481,132 @@ static int sysmon_write_event_value(struct iio_dev *indio_dev, } } +static int sysmon_set_avg_enable(struct sysmon *sysmon, + u32 base, u32 count, u32 val) +{ + struct regmap *map = sysmon->regmap; + int ret; + + for (unsigned int i = 0; i < count; i++) { + ret = regmap_write(map, base + i * SYSMON_REG_STRIDE, val); + if (ret) + return ret; + } + + return 0; +} + +static int sysmon_osr_write_temp(struct sysmon *sysmon, unsigned int val) +{ + /* + * HW register encoding is sample_count / 2: + * 0=none, 1=2x, 2=4x, 4=8x, 8=16x (not log2-based). + */ + unsigned int hw_val = val >> 1; + unsigned int readback; + int ret; + + ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG, + SYSMON_CONFIG_TEMP_SAT_OSR, + FIELD_PREP(SYSMON_CONFIG_TEMP_SAT_OSR, hw_val)); + if (ret) + return ret; + + /* + * Readback fence: the SysMon CONFIG register resides in the + * PMC domain behind the NoC. A posted write may not reach the + * hardware before the next MMIO access. Reading the register + * back forces the interconnect to complete the write, preventing + * a bus hang on the subsequent access. + */ + regmap_read(sysmon->regmap, SYSMON_CONFIG, &readback); + + return sysmon_set_avg_enable(sysmon, SYSMON_TEMP_EN_AVG_BASE, + SYSMON_TEMP_EN_AVG_COUNT, + hw_val ? ~0 : 0); +} + +static int sysmon_osr_write_supply(struct sysmon *sysmon, unsigned int val) +{ + /* HW encoding: sample_count / 2 (see sysmon_osr_write_temp) */ + unsigned int hw_val = val >> 1; + unsigned int readback; + int ret; + + ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG, + SYSMON_CONFIG_SUPPLY_OSR, + FIELD_PREP(SYSMON_CONFIG_SUPPLY_OSR, hw_val)); + if (ret) + return ret; + + /* Readback fence -- see sysmon_osr_write_temp for details */ + regmap_read(sysmon->regmap, SYSMON_CONFIG, &readback); + + return sysmon_set_avg_enable(sysmon, SYSMON_SUPPLY_EN_AVG_BASE, + SYSMON_SUPPLY_EN_AVG_COUNT, + hw_val ? ~0 : 0); +} + +static int sysmon_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int i; + int ret; + + if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(sysmon_oversampling_avail); i++) { + if (val == sysmon_oversampling_avail[i]) + break; + } + if (i == ARRAY_SIZE(sysmon_oversampling_avail)) + return -EINVAL; + + guard(mutex)(&sysmon->lock); + + if (chan->type == IIO_TEMP) { + ret = sysmon_osr_write_temp(sysmon, val); + if (ret) + return ret; + sysmon->temp_oversampling = val; + } else { + ret = sysmon_osr_write_supply(sysmon, val); + if (ret) + return ret; + sysmon->supply_oversampling = val; + } + + return 0; +} + +static int sysmon_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) + return IIO_VAL_INT; + + return -EINVAL; +} + +static int sysmon_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, + int *length, long mask) +{ + if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO) + return -EINVAL; + + *vals = sysmon_oversampling_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(sysmon_oversampling_avail); + + return IIO_AVAIL_LIST; +} + static int sysmon_read_label(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, char *label) @@ -477,6 +619,9 @@ static int sysmon_read_label(struct iio_dev *indio_dev, static const struct iio_info sysmon_iio_info = { .read_raw = sysmon_read_raw, + .write_raw = sysmon_write_raw, + .write_raw_get_fmt = sysmon_write_raw_get_fmt, + .read_avail = sysmon_read_avail, .read_label = sysmon_read_label, .read_event_config = sysmon_read_event_config, .write_event_config = sysmon_write_event_config, @@ -768,6 +913,10 @@ static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev, int ir .indexed = 1, .address = reg, .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_shared_by_type = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_type_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), .event_spec = irq > 0 ? sysmon_supply_events : NULL, .num_event_specs = irq > 0 ? @@ -799,7 +948,11 @@ static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev, int ir .address = SYSMON_TEMP_SAT_BASE + (reg - 1) * SYSMON_REG_STRIDE, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type = + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_type_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), .datasheet_name = label, }; } @@ -846,6 +999,8 @@ int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap) sysmon = iio_priv(indio_dev); sysmon->regmap = regmap; + sysmon->temp_oversampling = 1; + sysmon->supply_oversampling = 1; ret = devm_mutex_init(dev, &sysmon->lock); if (ret) diff --git a/drivers/iio/adc/versal-sysmon.h b/drivers/iio/adc/versal-sysmon.h index 9fe2793757a4..bb9a75bf71c0 100644 --- a/drivers/iio/adc/versal-sysmon.h +++ b/drivers/iio/adc/versal-sysmon.h @@ -23,11 +23,13 @@ struct regmap; #define SYSMON_IMR 0x0048 #define SYSMON_IER 0x004C #define SYSMON_IDR 0x0050 +#define SYSMON_CONFIG 0x0100 #define SYSMON_TEMP_MAX 0x1030 #define SYSMON_TEMP_MIN 0x1034 #define SYSMON_SUPPLY_BASE 0x1040 #define SYSMON_ALARM_FLAG 0x1018 #define SYSMON_ALARM_REG 0x1940 +#define SYSMON_SUPPLY_EN_AVG_BASE 0x1958 #define SYSMON_TEMP_TH_LOW 0x1970 #define SYSMON_TEMP_TH_UP 0x1974 #define SYSMON_SUPPLY_TH_LOW 0x1980 @@ -37,6 +39,7 @@ struct regmap; #define SYSMON_TEMP_MAX_MAX 0x1F90 #define SYSMON_STATUS_RESET 0x1F94 #define SYSMON_TEMP_SAT_BASE 0x1FAC +#define SYSMON_TEMP_EN_AVG_BASE 0x24B4 #define SYSMON_MAX_REG 0x24C0 /* NPI unlock value written to SYSMON_NPI_LOCK */ @@ -53,6 +56,16 @@ struct regmap; /* ISR/IMR temperature alarm mask (bit 9) */ #define SYSMON_TEMP_INTR_MASK BIT(9) +/* SYSMON_CONFIG: supply oversampling ratio */ +#define SYSMON_CONFIG_SUPPLY_OSR GENMASK(17, 14) + +/* SYSMON_CONFIG: temperature satellite oversampling ratio */ +#define SYSMON_CONFIG_TEMP_SAT_OSR GENMASK(27, 24) + +/* Per-channel averaging enable register counts */ +#define SYSMON_SUPPLY_EN_AVG_COUNT 5 +#define SYSMON_TEMP_EN_AVG_COUNT 2 + /* Supply voltage conversion register fields */ #define SYSMON_MANTISSA_MASK GENMASK(15, 0) #define SYSMON_FMT_MASK BIT(16) @@ -77,6 +90,8 @@ struct regmap; * @temp_mask: temperature interrupt configuration mask * @temp_hysteresis: cached DEVICE_TEMP hysteresis in millicelsius * @sysmon_unmask_work: re-enables events after alarm condition clears + * @temp_oversampling: current temp oversampling ratio + * @supply_oversampling: current supply oversampling ratio */ struct sysmon { struct regmap *regmap; @@ -96,6 +111,8 @@ struct sysmon { unsigned int temp_mask; int temp_hysteresis; struct delayed_work sysmon_unmask_work; + unsigned int temp_oversampling; + unsigned int supply_oversampling; }; int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap); From 84635517785582f94993ed13eb7d370a76900878 Mon Sep 17 00:00:00 2001 From: Xiaofeng Yuan Date: Wed, 24 Jun 2026 02:16:12 +0000 Subject: [PATCH 098/513] staging: iio: frequency: reorder dds.h macro parameters to match IIO convention The IIO subsystem convention requires that the file permission (_mode) parameter be the first argument of IIO_DEV_ATTR_* macros. The dds.h macros had _mode after _channel, causing checkpatch to misinterpret the channel number as a permission value. Reorder the parameters so _mode is first, matching the convention established by IIO_DEV_ATTR_SAMP_FREQ in include/linux/iio/sysfs.h. Where _mode is hard coded, make it a parameter in the interests of consistency and update all callers to match previously hard coded value. Update all callers in ad9834.c and ad9832.c accordingly. Compile tested. Signed-off-by: Xiaofeng Yuan Signed-off-by: Jonathan Cameron --- drivers/staging/iio/frequency/ad9832.c | 20 ++++++++++---------- drivers/staging/iio/frequency/ad9834.c | 22 +++++++++++----------- drivers/staging/iio/frequency/dds.h | 24 ++++++++++++------------ 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c index 1a6666c1948e..4528f8d694e4 100644 --- a/drivers/staging/iio/frequency/ad9832.c +++ b/drivers/staging/iio/frequency/ad9832.c @@ -253,22 +253,22 @@ static ssize_t ad9832_write(struct device *dev, struct device_attribute *attr, * see dds.h for further information */ -static IIO_DEV_ATTR_FREQ(0, 0, 0200, NULL, ad9832_write, AD9832_FREQ0HM); -static IIO_DEV_ATTR_FREQ(0, 1, 0200, NULL, ad9832_write, AD9832_FREQ1HM); -static IIO_DEV_ATTR_FREQSYMBOL(0, 0200, NULL, ad9832_write, AD9832_FREQ_SYM); +static IIO_DEV_ATTR_FREQ(0200, 0, 0, NULL, ad9832_write, AD9832_FREQ0HM); +static IIO_DEV_ATTR_FREQ(0200, 0, 1, NULL, ad9832_write, AD9832_FREQ1HM); +static IIO_DEV_ATTR_FREQSYMBOL(0200, 0, NULL, ad9832_write, AD9832_FREQ_SYM); static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */ -static IIO_DEV_ATTR_PHASE(0, 0, 0200, NULL, ad9832_write, AD9832_PHASE0H); -static IIO_DEV_ATTR_PHASE(0, 1, 0200, NULL, ad9832_write, AD9832_PHASE1H); -static IIO_DEV_ATTR_PHASE(0, 2, 0200, NULL, ad9832_write, AD9832_PHASE2H); -static IIO_DEV_ATTR_PHASE(0, 3, 0200, NULL, ad9832_write, AD9832_PHASE3H); -static IIO_DEV_ATTR_PHASESYMBOL(0, 0200, NULL, +static IIO_DEV_ATTR_PHASE(0200, 0, 0, NULL, ad9832_write, AD9832_PHASE0H); +static IIO_DEV_ATTR_PHASE(0200, 0, 1, NULL, ad9832_write, AD9832_PHASE1H); +static IIO_DEV_ATTR_PHASE(0200, 0, 2, NULL, ad9832_write, AD9832_PHASE2H); +static IIO_DEV_ATTR_PHASE(0200, 0, 3, NULL, ad9832_write, AD9832_PHASE3H); +static IIO_DEV_ATTR_PHASESYMBOL(0200, 0, NULL, ad9832_write, AD9832_PHASE_SYM); static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/ -static IIO_DEV_ATTR_PINCONTROL_EN(0, 0200, NULL, +static IIO_DEV_ATTR_PINCONTROL_EN(0200, 0, NULL, ad9832_write, AD9832_PINCTRL_EN); -static IIO_DEV_ATTR_OUT_ENABLE(0, 0200, NULL, +static IIO_DEV_ATTR_OUT_ENABLE(0200, 0, NULL, ad9832_write, AD9832_OUTPUT_EN); static struct attribute *ad9832_attributes[] = { diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index f95c5365cd03..bf3ad50052b3 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -315,21 +315,21 @@ static IIO_DEVICE_ATTR(out_altvoltage0_out1_wavetype_available, 0444, * see dds.h for further information */ -static IIO_DEV_ATTR_FREQ(0, 0, 0200, NULL, ad9834_write, AD9834_REG_FREQ0); -static IIO_DEV_ATTR_FREQ(0, 1, 0200, NULL, ad9834_write, AD9834_REG_FREQ1); -static IIO_DEV_ATTR_FREQSYMBOL(0, 0200, NULL, ad9834_write, AD9834_FSEL); +static IIO_DEV_ATTR_FREQ(0200, 0, 0, NULL, ad9834_write, AD9834_REG_FREQ0); +static IIO_DEV_ATTR_FREQ(0200, 0, 1, NULL, ad9834_write, AD9834_REG_FREQ1); +static IIO_DEV_ATTR_FREQSYMBOL(0200, 0, NULL, ad9834_write, AD9834_FSEL); static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */ -static IIO_DEV_ATTR_PHASE(0, 0, 0200, NULL, ad9834_write, AD9834_REG_PHASE0); -static IIO_DEV_ATTR_PHASE(0, 1, 0200, NULL, ad9834_write, AD9834_REG_PHASE1); -static IIO_DEV_ATTR_PHASESYMBOL(0, 0200, NULL, ad9834_write, AD9834_PSEL); +static IIO_DEV_ATTR_PHASE(0200, 0, 0, NULL, ad9834_write, AD9834_REG_PHASE0); +static IIO_DEV_ATTR_PHASE(0200, 0, 1, NULL, ad9834_write, AD9834_REG_PHASE1); +static IIO_DEV_ATTR_PHASESYMBOL(0200, 0, NULL, ad9834_write, AD9834_PSEL); static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/ -static IIO_DEV_ATTR_PINCONTROL_EN(0, 0200, NULL, ad9834_write, AD9834_PIN_SW); -static IIO_DEV_ATTR_OUT_ENABLE(0, 0200, NULL, ad9834_write, AD9834_RESET); -static IIO_DEV_ATTR_OUTY_ENABLE(0, 1, 0200, NULL, ad9834_write, AD9834_OPBITEN); -static IIO_DEV_ATTR_OUT_WAVETYPE(0, 0, ad9834_store_wavetype, 0); -static IIO_DEV_ATTR_OUT_WAVETYPE(0, 1, ad9834_store_wavetype, 1); +static IIO_DEV_ATTR_PINCONTROL_EN(0200, 0, NULL, ad9834_write, AD9834_PIN_SW); +static IIO_DEV_ATTR_OUT_ENABLE(0200, 0, NULL, ad9834_write, AD9834_RESET); +static IIO_DEV_ATTR_OUTY_ENABLE(0200, 0, 1, NULL, ad9834_write, AD9834_OPBITEN); +static IIO_DEV_ATTR_OUT_WAVETYPE(0200, 0, 0, ad9834_store_wavetype, 0); +static IIO_DEV_ATTR_OUT_WAVETYPE(0200, 0, 1, ad9834_store_wavetype, 1); static struct attribute *ad9834_attributes[] = { &iio_dev_attr_out_altvoltage0_frequency0.dev_attr.attr, diff --git a/drivers/staging/iio/frequency/dds.h b/drivers/staging/iio/frequency/dds.h index 2ebe68eb7398..b2ca8bb971e8 100644 --- a/drivers/staging/iio/frequency/dds.h +++ b/drivers/staging/iio/frequency/dds.h @@ -11,7 +11,7 @@ * /sys/bus/iio/devices/.../out_altvoltageX_frequencyY */ -#define IIO_DEV_ATTR_FREQ(_channel, _num, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_FREQ(_mode, _channel, _num, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_frequency##_num, \ _mode, _show, _store, _addr) @@ -26,7 +26,7 @@ * /sys/bus/iio/devices/.../out_altvoltageX_frequencysymbol */ -#define IIO_DEV_ATTR_FREQSYMBOL(_channel, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_FREQSYMBOL(_mode, _channel, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_frequencysymbol, \ _mode, _show, _store, _addr) @@ -34,7 +34,7 @@ * /sys/bus/iio/devices/.../out_altvoltageX_phaseY */ -#define IIO_DEV_ATTR_PHASE(_channel, _num, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_PHASE(_mode, _channel, _num, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_phase##_num, \ _mode, _show, _store, _addr) @@ -49,7 +49,7 @@ * /sys/bus/iio/devices/.../out_altvoltageX_phasesymbol */ -#define IIO_DEV_ATTR_PHASESYMBOL(_channel, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_PHASESYMBOL(_mode, _channel, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_phasesymbol, \ _mode, _show, _store, _addr) @@ -57,7 +57,7 @@ * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_en */ -#define IIO_DEV_ATTR_PINCONTROL_EN(_channel, _mode, _show, _store, _addr)\ +#define IIO_DEV_ATTR_PINCONTROL_EN(_mode, _channel, _show, _store, _addr)\ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_en, \ _mode, _show, _store, _addr) @@ -65,7 +65,7 @@ * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_frequency_en */ -#define IIO_DEV_ATTR_PINCONTROL_FREQ_EN(_channel, _mode, _show, _store, _addr)\ +#define IIO_DEV_ATTR_PINCONTROL_FREQ_EN(_mode, _channel, _show, _store, _addr)\ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_frequency_en,\ _mode, _show, _store, _addr) @@ -73,7 +73,7 @@ * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_phase_en */ -#define IIO_DEV_ATTR_PINCONTROL_PHASE_EN(_channel, _mode, _show, _store, _addr)\ +#define IIO_DEV_ATTR_PINCONTROL_PHASE_EN(_mode, _channel, _show, _store, _addr)\ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_phase_en, \ _mode, _show, _store, _addr) @@ -81,7 +81,7 @@ * /sys/bus/iio/devices/.../out_altvoltageX_out_enable */ -#define IIO_DEV_ATTR_OUT_ENABLE(_channel, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_OUT_ENABLE(_mode, _channel, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out_enable, \ _mode, _show, _store, _addr) @@ -89,8 +89,8 @@ * /sys/bus/iio/devices/.../out_altvoltageX_outY_enable */ -#define IIO_DEV_ATTR_OUTY_ENABLE(_channel, _output, \ - _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_OUTY_ENABLE(_mode, _channel, _output, \ + _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out##_output##_enable,\ _mode, _show, _store, _addr) @@ -98,9 +98,9 @@ * /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype */ -#define IIO_DEV_ATTR_OUT_WAVETYPE(_channel, _output, _store, _addr) \ +#define IIO_DEV_ATTR_OUT_WAVETYPE(_mode, _channel, _output, _store, _addr)\ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out##_output##_wavetype,\ - 0200, NULL, _store, _addr) + _mode, NULL, _store, _addr) /** * /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype_available From a160afd32993b6d561d2886b498d31f304db1bca Mon Sep 17 00:00:00 2001 From: Anas Khan Date: Thu, 2 Jul 2026 15:41:29 +0530 Subject: [PATCH 099/513] iio: backend: fix typos in comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix several comment typos in industrialio-backend.c: correct "through" in a channel-spec comment, "initiates a process" in the activation comment, and drop a stray apostrophe from "Gets" in two getter kerneldoc comments. Signed-off-by: Anas Khan Reviewed-by: Nuno Sá Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-backend.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 4763e224ebc6..f7a4be8ec320 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -649,7 +649,7 @@ EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_get, "IIO_BACKEND"); * @len: Buffer length * * This helper is intended to be used by backends that extend an IIO channel - * (trough iio_backend_extend_chan_spec()) with extended info. In that case, + * (through iio_backend_extend_chan_spec()) with extended info. In that case, * backends are not supposed to give their own callbacks (as they would not have * a way to get the backend from indio_dev). This is the setter. * @@ -851,7 +851,7 @@ EXPORT_SYMBOL_NS_GPL(iio_backend_filter_type_set, "IIO_BACKEND"); * @back: Backend device * @timeout_us: Timeout value in us. * - * When activated, it initates a proccess that aligns the sample's most + * When activated, it initiates a process that aligns the sample's most * significant bit (MSB) based solely on the captured data, without * considering any other external signals. * @@ -1017,7 +1017,7 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con * @dev: Consumer device for the backend * @name: Backend name * - * Get's the backend associated with @dev. + * Gets the backend associated with @dev. * * RETURNS: * A backend pointer, negative error pointer otherwise. @@ -1050,7 +1050,7 @@ EXPORT_SYMBOL_NS_GPL(devm_iio_backend_get_by_index, "IIO_BACKEND"); * @name: Backend name * @fwnode: Firmware node of the backend consumer * - * Get's the backend associated with a firmware node. + * Gets the backend associated with a firmware node. * * RETURNS: * A backend pointer, negative error pointer otherwise. From 0790de31298adfb8f7c69235e3839c8a5f285dd7 Mon Sep 17 00:00:00 2001 From: Moksh Panicker Date: Thu, 2 Jul 2026 11:46:48 +0000 Subject: [PATCH 100/513] iio: adc: ad7779: Remove unused completion field struct ad7779_state contains a completion field that is initialized in ad7779_setup_without_backend() but never waited on or signaled anywhere in the driver. Remove the dead code. Suggested-by: Joshua Crofts Signed-off-by: Moksh Panicker Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7779.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/iio/adc/ad7779.c b/drivers/iio/adc/ad7779.c index 695cc79e78da..5e2e1c31748b 100644 --- a/drivers/iio/adc/ad7779.c +++ b/drivers/iio/adc/ad7779.c @@ -143,7 +143,6 @@ struct ad7779_state { const struct ad7779_chip_info *chip_info; struct clk *mclk; struct iio_trigger *trig; - struct completion completion; unsigned int sampling_freq; enum ad7779_filter filter_enabled; struct iio_backend *back; @@ -852,8 +851,6 @@ static int ad7779_setup_without_backend(struct ad7779_state *st, struct iio_dev indio_dev->trig = iio_trigger_get(st->trig); - init_completion(&st->completion); - ret = devm_iio_triggered_buffer_setup(dev, indio_dev, &iio_pollfunc_store_time, &ad7779_trigger_handler, From 220c2ad6f3ac4b32654404965991617c1cfe7861 Mon Sep 17 00:00:00 2001 From: Moksh Panicker Date: Thu, 2 Jul 2026 11:46:49 +0000 Subject: [PATCH 101/513] iio: adc: ad7779: Remove redundant dev_err_probe() after devm_request_irq() devm_request_irq() already prints an error message on failure via the IRQ core, so wrapping its return value in dev_err_probe() results in a duplicate error message. Return the error directly instead. Suggested-by: Joshua Crofts Signed-off-by: Moksh Panicker Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7779.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/adc/ad7779.c b/drivers/iio/adc/ad7779.c index 5e2e1c31748b..87e0139c0f13 100644 --- a/drivers/iio/adc/ad7779.c +++ b/drivers/iio/adc/ad7779.c @@ -842,8 +842,7 @@ static int ad7779_setup_without_backend(struct ad7779_state *st, struct iio_dev IRQF_NO_THREAD | IRQF_NO_AUTOEN, indio_dev->name, st->trig); if (ret) - return dev_err_probe(dev, ret, "request IRQ %d failed\n", - st->spi->irq); + return ret; ret = devm_iio_trigger_register(dev, st->trig); if (ret) From 9da9ee8bea6aa1b699ed93f2dc1a825c148ddbf0 Mon Sep 17 00:00:00 2001 From: Anas Khan Date: Wed, 1 Jul 2026 21:56:59 +0530 Subject: [PATCH 102/513] iio: imu: fix typos in comments Fix several comment misspellings across drivers/iio/imu/, found with codespell and manual review. The corrected words are coefficient, temperature, sensitivity, access, chosen, and buses. Signed-off-by: Anas Khan Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16480.c | 4 ++-- drivers/iio/imu/bmi160/bmi160_core.c | 2 +- drivers/iio/imu/fxos8700_core.c | 2 +- drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c | 2 +- drivers/iio/imu/inv_icm45600/inv_icm45600.h | 2 +- drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index 51254fe15a04..3852203127f1 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -111,7 +111,7 @@ #define ADIS16480_REG_SERIAL_NUM ADIS16480_REG(0x04, 0x20) -/* Each filter coefficent bank spans two pages */ +/* Each filter coefficient bank spans two pages */ #define ADIS16480_FIR_COEF(page) (x < 60 ? ADIS16480_REG(page, (x) + 8) : \ ADIS16480_REG((page) + 1, (x) - 60 + 8)) #define ADIS16480_FIR_COEF_A(x) ADIS16480_FIR_COEF(0x05, (x)) @@ -1468,7 +1468,7 @@ static irqreturn_t adis16480_trigger_handler(int irq, void *p) * We need to perform the padding to have the buffer * elements naturally aligned in case there are any * 32-bit storage size channels enabled which are added - * in the buffer after the temprature data. In case + * in the buffer after the temperature data. In case * there is no data being added after the temperature * data, the padding is harmless. */ diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index 4abb83b75e2e..2c0d21eb853a 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -5,7 +5,7 @@ * Copyright (c) 2016, Intel Corporation. * Copyright (c) 2019, Martin Kelly. * - * IIO core driver for BMI160, with support for I2C/SPI busses + * IIO core driver for BMI160, with support for I2C/SPI buses * * TODO: magnetometer, hardware FIFO */ diff --git a/drivers/iio/imu/fxos8700_core.c b/drivers/iio/imu/fxos8700_core.c index 281ebfd9c15a..9d2eb6fbab4a 100644 --- a/drivers/iio/imu/fxos8700_core.c +++ b/drivers/iio/imu/fxos8700_core.c @@ -2,7 +2,7 @@ /* * FXOS8700 - NXP IMU (accelerometer plus magnetometer) * - * IIO core driver for FXOS8700, with support for I2C/SPI busses + * IIO core driver for FXOS8700, with support for I2C/SPI buses * * TODO: Buffer, trigger, and IRQ support */ diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c index c3c2fbd8d9ce..8ba9774e66bf 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c @@ -189,7 +189,7 @@ static unsigned int inv_icm42600_wm_truncate(unsigned int watermark, * smallest latency but this is not as simple as choosing the smallest watermark * value. Latency depends on watermark and ODR. It requires several steps: * 1) compute gyro and accel latencies and choose the smallest value. - * 2) adapt the choosen latency so that it is a multiple of both gyro and accel + * 2) adapt the chosen latency so that it is a multiple of both gyro and accel * ones. Otherwise it is possible that you don't meet a requirement. (for * example with gyro @100Hz wm 4 and accel @100Hz with wm 6, choosing the * value of 4 will not meet accel latency requirement because 6 is not a diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/inv_icm45600/inv_icm45600.h index 1c796d4b2a40..95fa934a42b4 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600.h +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h @@ -190,7 +190,7 @@ struct inv_icm45600_sensor_state { #define INV_ICM45600_REG_IREG_ADDR 0x7C #define INV_ICM45600_REG_IREG_DATA 0x7E -/* Direct acces registers */ +/* Direct access registers */ #define INV_ICM45600_REG_MISC2 0x007F #define INV_ICM45600_MISC2_SOFT_RESET BIT(1) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c index 47394594d17a..6b858fdfd1c6 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c @@ -106,9 +106,9 @@ static int inv_magn_init(struct inv_mpu6050_state *st) return ret; /* - * Sensor sentivity + * Sensor sensitivity * 1 uT = 0.01 G and value is in micron (1e6) - * sensitvity = x uT * 0.01 * 1e6 + * sensitivity = x uT * 0.01 * 1e6 */ switch (st->chip_type) { case INV_MPU9150: From cff496bda5128dd9cf7a38fc2933440ee58b8ad1 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Thu, 2 Jul 2026 21:48:01 +0530 Subject: [PATCH 103/513] iio: humidity: hid-sensor-humidity: use common device for devres MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kmemdup() is used for memory that is logically tied to the HID platform device, even though the driver binds into the IIO framework. Using &indio_dev->dev for devres allocations works functionally, but it results in two separate devres ownership trees—one for the HID platform device (pdev) and another for the IIO device (indio_dev). The devres framework is intended to have a single, well-defined parent device. Since the memory originates from HID sensor probing and is not IIO-specific, &pdev->dev is the correct and logical owner. Switch to using the platform device for devm_kmemdup() so that all resources are released deterministically and consistently. Signed-off-by: Sanjay Chitroda Tested-by: Zhang Lixu Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hid-sensor-humidity.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/humidity/hid-sensor-humidity.c b/drivers/iio/humidity/hid-sensor-humidity.c index c376a247b137..1d4612a471db 100644 --- a/drivers/iio/humidity/hid-sensor-humidity.c +++ b/drivers/iio/humidity/hid-sensor-humidity.c @@ -215,7 +215,7 @@ static int hid_humidity_probe(struct platform_device *pdev) if (ret) return ret; - humid_chans = devm_kmemdup(&indio_dev->dev, humidity_channels, + humid_chans = devm_kmemdup(&pdev->dev, humidity_channels, sizeof(humidity_channels), GFP_KERNEL); if (!humid_chans) return -ENOMEM; From d9290c908d6f31bcdf79c1fec9b7287cf65df19b Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Thu, 2 Jul 2026 21:48:02 +0530 Subject: [PATCH 104/513] iio: position: hid-sensor-custom-intel-hinge: use common device for devres MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kmemdup() is used for memory that is logically tied to the HID platform device, even though the driver binds into the IIO framework. Using &indio_dev->dev for devres allocations works functionally, but it results in two separate devres ownership trees—one for the HID platform device (pdev) and another for the IIO device (indio_dev). The devres framework is intended to have a single, well-defined parent device. Since the memory originates from HID sensor probing and is not IIO-specific, &pdev->dev is the correct and logical owner. Switch to using the platform device for devm_kmemdup() so that all resources are released deterministically and consistently. Signed-off-by: Sanjay Chitroda Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/position/hid-sensor-custom-intel-hinge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/position/hid-sensor-custom-intel-hinge.c b/drivers/iio/position/hid-sensor-custom-intel-hinge.c index 2139ddb670c4..7d3f4907e083 100644 --- a/drivers/iio/position/hid-sensor-custom-intel-hinge.c +++ b/drivers/iio/position/hid-sensor-custom-intel-hinge.c @@ -292,7 +292,7 @@ static int hid_hinge_probe(struct platform_device *pdev) } indio_dev->num_channels = ARRAY_SIZE(hinge_channels); - indio_dev->channels = devm_kmemdup(&indio_dev->dev, hinge_channels, + indio_dev->channels = devm_kmemdup(&pdev->dev, hinge_channels, sizeof(hinge_channels), GFP_KERNEL); if (!indio_dev->channels) return -ENOMEM; From 0c50c9e3b2a4acb2b5b238ba58537f5525532527 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Thu, 2 Jul 2026 21:48:03 +0530 Subject: [PATCH 105/513] iio: temperature: hid-sensor-temperature: use common device for devres MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kmemdup() is used for memory that is logically tied to the HID platform device, even though the driver binds into the IIO framework. Using &indio_dev->dev for devres allocations works functionally, but it results in two separate devres ownership trees—one for the HID platform device (pdev) and another for the IIO device (indio_dev). The devres framework is intended to have a single, well-defined parent device. Since the memory originates from HID sensor probing and is not IIO-specific, &pdev->dev is the correct and logical owner. Switch to using the platform device for devm_kmemdup() so that all resources are released deterministically and consistently. Signed-off-by: Sanjay Chitroda Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/hid-sensor-temperature.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c index a8d3a15f9c53..d3bc13b4258e 100644 --- a/drivers/iio/temperature/hid-sensor-temperature.c +++ b/drivers/iio/temperature/hid-sensor-temperature.c @@ -213,8 +213,8 @@ static int hid_temperature_probe(struct platform_device *pdev) if (ret) return ret; - temp_chans = devm_kmemdup(&indio_dev->dev, temperature_channels, - sizeof(temperature_channels), GFP_KERNEL); + temp_chans = devm_kmemdup(&pdev->dev, temperature_channels, + sizeof(temperature_channels), GFP_KERNEL); if (!temp_chans) return -ENOMEM; From a30824bbfb22f890df7e92448522b696c62ce965 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Thu, 2 Jul 2026 21:47:58 +0530 Subject: [PATCH 106/513] iio: hid-sensors: add/remove blank line Add a blank line after variable declarations and remove multiple blank line across HID sensor IIO drivers to improve readability and align with kernel coding style. Signed-off-by: Sanjay Chitroda Signed-off-by: Jonathan Cameron --- drivers/iio/common/hid-sensors/hid-sensor-attributes.c | 1 - drivers/iio/common/hid-sensors/hid-sensor-trigger.c | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index c115a72832b2..2f0a1ea42f48 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -282,7 +282,6 @@ int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1, } EXPORT_SYMBOL_NS(hid_sensor_read_raw_hyst_rel_value, "IIO_HID"); - int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, int val1, int val2) { diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index 417c4ab8c1b2..c8ccf96f3d03 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -314,7 +314,9 @@ static int __maybe_unused hid_sensor_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); + schedule_work(&attrb->work); + return 0; } @@ -322,6 +324,7 @@ static int __maybe_unused hid_sensor_runtime_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); + return _hid_sensor_power_state(attrb, true); } From 0e32649a7cf3cd784862f8dc0c68a5134731bfff Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Mon, 22 Jun 2026 10:59:57 +0530 Subject: [PATCH 107/513] iio: orientation: hid-sensor-rotation: Avoid race between callback setup and device exposure The driver currently exposes the IIO device to userspace before completing sensor hub callback registration, and similarly removes callbacks while the device can still be accessed during teardown. This creates a timing window where userspace may enable the buffer before callbacks are available. In such cases: - samples can be dropped, - buffered reads may observe stale or no data. Reorder probe and remove paths to ensure callbacks are active before device exposure and are removed after device is no longer accessible. This avoids a race window leading to data loss. Signed-off-by: Sanjay Chitroda Fixes: fc18dddc0625 ("iio: hid-sensors: Added device rotation support") Reviewed-by: Andy Shevchenko Acked-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/orientation/hid-sensor-rotation.c | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index 20563d8efaf6..6773bb0ec204 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -332,12 +332,6 @@ static int hid_dev_rot_probe(struct platform_device *pdev) return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - rot_state->callbacks.send_event = dev_rot_proc_event; rot_state->callbacks.capture_sample = dev_rot_capture_sample; rot_state->callbacks.pdev = pdev; @@ -345,13 +339,19 @@ static int hid_dev_rot_probe(struct platform_device *pdev) &rot_state->callbacks); if (ret) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return 0; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, hsdev->usage); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes); return ret; @@ -364,8 +364,8 @@ static void hid_dev_rot_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct dev_rot_state *rot_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, hsdev->usage); hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes); } From 28afc251ad71646d501191225ddd4db57c670a47 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Mon, 22 Jun 2026 10:59:58 +0530 Subject: [PATCH 108/513] iio: orientation: hid-sensor-incl-3d: Avoid race between callback setup and device exposure The driver currently exposes the IIO device to userspace before completing sensor hub callback registration, and similarly removes callbacks while the device can still be accessed during teardown. This creates a timing window where userspace may enable the buffer before callbacks are available. In such cases: - samples can be dropped, - buffered reads may observe stale or no data. Reorder probe and remove paths to ensure callbacks are active before device exposure and are removed after device is no longer accessible. This avoids a race window leading to data loss. Signed-off-by: Sanjay Chitroda Reviewed-by: Andy Shevchenko Acked-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/orientation/hid-sensor-incl-3d.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c index c7fbff498be7..5696e4ef3633 100644 --- a/drivers/iio/orientation/hid-sensor-incl-3d.c +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -356,12 +356,6 @@ static int hid_incl_3d_probe(struct platform_device *pdev) return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - incl_state->callbacks.send_event = incl_3d_proc_event; incl_state->callbacks.capture_sample = incl_3d_capture_sample; incl_state->callbacks.pdev = pdev; @@ -370,13 +364,19 @@ static int hid_incl_3d_probe(struct platform_device *pdev) &incl_state->callbacks); if (ret) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return 0; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &incl_state->common_attributes); return ret; @@ -389,8 +389,8 @@ static void hid_incl_3d_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct incl_3d_state *incl_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D); hid_sensor_remove_trigger(indio_dev, &incl_state->common_attributes); } From 50d8d72e4f28202e18a687ed868ddd3225b2ac1b Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Mon, 22 Jun 2026 10:59:59 +0530 Subject: [PATCH 109/513] iio: gyro: hid-sensor-gyro-3d: Avoid race between callback setup and device exposure The driver currently exposes the IIO device to userspace before completing sensor hub callback registration, and similarly removes callbacks while the device can still be accessed during teardown. This creates a timing window where userspace may enable the buffer before callbacks are available. In such cases: - samples can be dropped, - buffered reads may observe stale or no data. Reorder probe and remove paths to ensure callbacks are active before device exposure and are removed after device is no longer accessible. This avoids a race window leading to data loss. Signed-off-by: Sanjay Chitroda Reviewed-by: Andy Shevchenko Acked-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/hid-sensor-gyro-3d.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index bbca2111e79b..c8130b488f10 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -324,12 +324,6 @@ static int hid_gyro_3d_probe(struct platform_device *pdev) return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - gyro_state->callbacks.send_event = gyro_3d_proc_event; gyro_state->callbacks.capture_sample = gyro_3d_capture_sample; gyro_state->callbacks.pdev = pdev; @@ -337,13 +331,19 @@ static int hid_gyro_3d_probe(struct platform_device *pdev) &gyro_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &gyro_state->common_attributes); return ret; @@ -356,8 +356,8 @@ static void hid_gyro_3d_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct gyro_3d_state *gyro_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D); hid_sensor_remove_trigger(indio_dev, &gyro_state->common_attributes); } From 724d0351cd08eb93f3cd9021c3a26ce1f1c79f7f Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Mon, 22 Jun 2026 11:00:00 +0530 Subject: [PATCH 110/513] iio: pressure: hid-sensor-press: Avoid race between callback setup and device exposure The driver currently exposes the IIO device to userspace before completing sensor hub callback registration, and similarly removes callbacks while the device can still be accessed during teardown. This creates a timing window where userspace may enable the buffer before callbacks are available. In such cases: - samples can be dropped, - buffered reads may observe stale or no data. Reorder probe and remove paths to ensure callbacks are active before device exposure and are removed after device is no longer accessible. This avoids a race window leading to data loss. Signed-off-by: Sanjay Chitroda Reviewed-by: Andy Shevchenko Acked-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/hid-sensor-press.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c index 3e47a10d72a8..8f81a6d65b9f 100644 --- a/drivers/iio/pressure/hid-sensor-press.c +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -291,12 +291,6 @@ static int hid_press_probe(struct platform_device *pdev) return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - press_state->callbacks.send_event = press_proc_event; press_state->callbacks.capture_sample = press_capture_sample; press_state->callbacks.pdev = pdev; @@ -304,13 +298,19 @@ static int hid_press_probe(struct platform_device *pdev) &press_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes); return ret; @@ -323,8 +323,8 @@ static void hid_press_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct press_state *press_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE); hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes); } From 49e663471992611f586598d2bbd23f94b760f9fa Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Mon, 22 Jun 2026 11:00:01 +0530 Subject: [PATCH 111/513] iio: light: hid-sensor-prox: Avoid race between callback setup and device exposure The driver currently exposes the IIO device to userspace before completing sensor hub callback registration, and similarly removes callbacks while the device can still be accessed during teardown. This creates a timing window where userspace may enable the buffer before callbacks are available. In such cases: - samples can be dropped, - buffered reads may observe stale or no data. Reorder probe and remove paths to ensure callbacks are active before device exposure and are removed after device is no longer accessible. This avoids a race window leading to data loss. Signed-off-by: Sanjay Chitroda Reviewed-by: Andy Shevchenko Acked-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/light/hid-sensor-prox.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index 9059f00f0ced..11609dc4c5dc 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -312,12 +312,6 @@ static int hid_prox_probe(struct platform_device *pdev) return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - prox_state->callbacks.send_event = prox_proc_event; prox_state->callbacks.capture_sample = prox_capture_sample; prox_state->callbacks.pdev = pdev; @@ -325,13 +319,19 @@ static int hid_prox_probe(struct platform_device *pdev) &prox_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, hsdev->usage); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes); return ret; @@ -344,8 +344,8 @@ static void hid_prox_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct prox_state *prox_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, hsdev->usage); hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes); } From 7d362d339391780c964b06bec9b209b0f9e229b4 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Mon, 22 Jun 2026 11:00:02 +0530 Subject: [PATCH 112/513] iio: light: hid-sensor-als: Avoid race between callback setup and device exposure The driver currently exposes the IIO device to userspace before completing sensor hub callback registration, and similarly removes callbacks while the device can still be accessed during teardown. This creates a timing window where userspace may enable the buffer before callbacks are available. In such cases: - samples can be dropped, - buffered reads may observe stale or no data. Reorder probe and remove paths to ensure callbacks are active before device exposure and are removed after device is no longer accessible. This avoids a race window leading to data loss. Signed-off-by: Sanjay Chitroda Reviewed-by: Andy Shevchenko Acked-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/light/hid-sensor-als.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index ae2fda8dc500..823bb56b9873 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -406,25 +406,25 @@ static int hid_als_probe(struct platform_device *pdev) return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - als_state->callbacks.send_event = als_proc_event; als_state->callbacks.capture_sample = als_capture_sample; als_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, hsdev->usage, &als_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, hsdev->usage); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes); return ret; @@ -437,8 +437,8 @@ static void hid_als_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct als_state *als_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, hsdev->usage); hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes); } From 3e37afb5697e1b30bd739fe38909d3dbf2493bb9 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Mon, 22 Jun 2026 11:00:03 +0530 Subject: [PATCH 113/513] iio: magnetometer: hid-sensor-magn-3d: Avoid race between callback setup and device exposure The driver currently exposes the IIO device to userspace before completing sensor hub callback registration, and similarly removes callbacks while the device can still be accessed during teardown. This creates a timing window where userspace may enable the buffer before callbacks are available. In such cases: - samples can be dropped, - buffered reads may observe stale or no data. Reorder probe and remove paths to ensure callbacks are active before device exposure and are removed after device is no longer accessible. This avoids a race window leading to data loss. Signed-off-by: Sanjay Chitroda Reviewed-by: Andy Shevchenko Acked-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/hid-sensor-magn-3d.c | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index 23884825eb00..acae77273bd5 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -513,12 +513,6 @@ static int hid_magn_3d_probe(struct platform_device *pdev) return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - magn_state->callbacks.send_event = magn_3d_proc_event; magn_state->callbacks.capture_sample = magn_3d_capture_sample; magn_state->callbacks.pdev = pdev; @@ -526,13 +520,19 @@ static int hid_magn_3d_probe(struct platform_device *pdev) &magn_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes); return ret; @@ -545,8 +545,8 @@ static void hid_magn_3d_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct magn_3d_state *magn_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D); hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes); } From eb787019c42072cf13470afca673dab0b49cabb6 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Mon, 22 Jun 2026 11:00:04 +0530 Subject: [PATCH 114/513] iio: accel: hid-sensor-accel-3d: Avoid race between callback setup and device exposure The driver currently exposes the IIO device to userspace before completing sensor hub callback registration, and similarly removes callbacks while the device can still be accessed during teardown. This creates a timing window where userspace may enable the buffer before callbacks are available. In such cases: - samples can be dropped, - buffered reads may observe stale or no data. Reorder probe and remove paths to ensure callbacks are active before device exposure and are removed after device is no longer accessible. This avoids a race window leading to data loss. Signed-off-by: Sanjay Chitroda Reviewed-by: Andy Shevchenko Acked-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/accel/hid-sensor-accel-3d.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 42c4259bf209..12481cfe9800 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -386,12 +386,6 @@ static int hid_accel_3d_probe(struct platform_device *pdev) return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - accel_state->callbacks.send_event = accel_3d_proc_event; accel_state->callbacks.capture_sample = accel_3d_capture_sample; accel_state->callbacks.pdev = pdev; @@ -399,13 +393,19 @@ static int hid_accel_3d_probe(struct platform_device *pdev) &accel_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, hsdev->usage); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes); return ret; @@ -418,8 +418,8 @@ static void hid_accel_3d_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct accel_3d_state *accel_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, hsdev->usage); hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes); } From 97e20f3d8630510a1fbb3e066c0f9bf02d8befde Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Mon, 22 Jun 2026 03:55:13 -0500 Subject: [PATCH 115/513] iio: imu: inv_icm45600: clamp the device-reported FIFO sample count inv_icm45600_buffer_fifo_read() uses the FIFO_COUNT the device reports, unclamped, as the length of a regmap_noinc_read() into the fixed INV_ICM45600_FIFO_SIZE_MAX (8 KiB) st->fifo.data buffer. The only bound is the caller's "max", which the interrupt path skips (it passes 0). A device, or an attacker on the bus, reporting up to 65535 makes the read as large as ~1 MiB: a heap out-of-bounds write of device-controlled data. Clamp st->fifo.count to the buffer capacity before the read, and allocate the buffer with the same INV_ICM45600_FIFO_SIZE_MAX define, so the bound and the allocation reference one constant. The clamp is a no-op for conforming hardware. Signed-off-by: Bryam Vargas Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c | 7 +++++-- drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c index 2b9ea317385c..42111c543d3c 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c @@ -422,8 +422,11 @@ int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st, if (max > 0 && fifo_nb > max) fifo_nb = max; - /* Try to read all FIFO data in internal buffer. */ - st->fifo.count = fifo_nb * packet_size; + /* + * Read all FIFO data into the internal buffer, clamping the + * device-reported count to the buffer capacity. + */ + st->fifo.count = min(fifo_nb * packet_size, INV_ICM45600_FIFO_SIZE_MAX); ret = regmap_noinc_read(st->map, INV_ICM45600_REG_FIFO_DATA, st->fifo.data, st->fifo.count); if (ret == -ENOTSUPP || ret == -EFBIG) { diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c index d49053161a65..c1d7aa7e950d 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c @@ -716,7 +716,7 @@ int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chi dev_set_drvdata(dev, st); - st->fifo.data = devm_kzalloc(dev, 8192, GFP_KERNEL); + st->fifo.data = devm_kzalloc(dev, INV_ICM45600_FIFO_SIZE_MAX, GFP_KERNEL); if (!st->fifo.data) return -ENOMEM; From 306e3e855f2b905740434af03ba87df81772891a Mon Sep 17 00:00:00 2001 From: Prashant Rahul Date: Fri, 19 Jun 2026 04:44:34 +0530 Subject: [PATCH 116/513] iio: adc: ti-adc081c: use dev_err_probe() for probe time error This simplifies error handling and ensures consistent error reporting. Signed-off-by: Prashant Rahul Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-adc081c.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c index 33f82bdfeb94..29dd7cf25f79 100644 --- a/drivers/iio/adc/ti-adc081c.c +++ b/drivers/iio/adc/ti-adc081c.c @@ -190,10 +190,8 @@ static int adc081c_probe(struct i2c_client *client) err = devm_iio_triggered_buffer_setup(&client->dev, iio, NULL, adc081c_trigger_handler, NULL); - if (err < 0) { - dev_err(&client->dev, "iio triggered buffer setup failed\n"); - return err; - } + if (err < 0) + return dev_err_probe(&client->dev, err, "iio triggered buffer setup failed\n"); return devm_iio_device_register(&client->dev, iio); } From 32782e3115aa892f7e67cc8254808327f77217c9 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 26 May 2026 14:39:07 -0400 Subject: [PATCH 117/513] rust: binder: use strict provenance APIs Replace the pointer-to-integer conversions in the Binder Rust driver with calls to the strict provenance APIs. The strict provenance APIs were stabilized in Rust 1.84.0 [1]. Since commit f32fb9c58a5b ("rust: bump Rust minimum supported version to 1.85.0 (Debian Trixie)"), the minimum supported Rust version is 1.85.0, so no polyfills are needed. Link: https://blog.rust-lang.org/2025/01/09/Rust-1.84.0.html#strict-provenance-apis [1] Reviewed-by: Alice Ryhl Assisted-by: Codex:gpt-5 Signed-off-by: Tamir Duberstein Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260526-binder-strict-provenance-v2-1-a41d89c29bc5@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/node.rs | 2 +- drivers/android/binder/rust_binder_main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs index 69f757ff7461..d710940c3c8f 100644 --- a/drivers/android/binder/node.rs +++ b/drivers/android/binder/node.rs @@ -321,7 +321,7 @@ pub(crate) unsafe fn remove_node_info( /// An id that is unique across all binder nodes on the system. Used as the key in the /// `by_node` map. pub(crate) fn global_id(&self) -> usize { - self as *const Node as usize + (self as *const Node).addr() } pub(crate) fn get_id(&self) -> (u64, u64) { diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index dc1941cd2407..d487638266e3 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -511,7 +511,7 @@ unsafe impl Sync for AssertSync {} _: *mut kernel::ffi::c_void, ) -> kernel::ffi::c_int { // SAFETY: Accessing the private field of `seq_file` is okay. - let pid = (unsafe { (*ptr).private }) as usize as Pid; + let pid = unsafe { (*ptr).private }.addr() as Pid; // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which // this method is called. let m = unsafe { SeqFile::from_raw(ptr) }; From 7d7b2011e7554d481a6db9cf362a58508b3e009e Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 26 May 2026 14:39:08 -0400 Subject: [PATCH 118/513] rust: binder: enable `clippy::ptr_as_ptr` lint In Rust 1.51.0, Clippy introduced the `ptr_as_ptr` lint [1]: > Though `as` casts between raw pointers are not terrible, > `pointer::cast` is safer because it cannot accidentally change pointer > mutability or cast the pointer to other types like `usize`. Apply the required changes and enable the lint in the Binder Rust driver -- no functional change intended. Link: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr [1] Assisted-by: Codex:gpt-5 Signed-off-by: Tamir Duberstein Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260526-binder-strict-provenance-v2-2-a41d89c29bc5@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/defs.rs | 4 ++-- drivers/android/binder/page_range.rs | 6 +++--- drivers/android/binder/rust_binder_main.rs | 7 +------ 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/android/binder/defs.rs b/drivers/android/binder/defs.rs index 33f51b4139c7..354c32c39e83 100644 --- a/drivers/android/binder/defs.rs +++ b/drivers/android/binder/defs.rs @@ -165,8 +165,8 @@ impl BinderTransactionDataSecctx { pub(crate) fn tr_data(&mut self) -> &mut BinderTransactionData { // SAFETY: Transparent wrapper is safe to transmute. unsafe { - &mut *(&mut self.transaction_data as *mut uapi::binder_transaction_data - as *mut BinderTransactionData) + &mut *((&mut self.transaction_data as *mut uapi::binder_transaction_data) + .cast::()) } } } diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs index e82a5523804f..fbb93ebb9697 100644 --- a/drivers/android/binder/page_range.rs +++ b/drivers/android/binder/page_range.rs @@ -571,7 +571,7 @@ pub(crate) unsafe fn read(&self, offset: usize) -> Result { unsafe { self.iterate(offset, size_of::(), |page, offset, to_copy| { // SAFETY: The sum of `offset` and `to_copy` is bounded by the size of T. - let obj_ptr = (out.as_mut_ptr() as *mut u8).add(out_offset); + let obj_ptr = out.as_mut_ptr().cast::().add(out_offset); // SAFETY: The pointer points is in-bounds of the `out` variable, so it is valid. page.read_raw(obj_ptr, offset, to_copy)?; out_offset += to_copy; @@ -593,7 +593,7 @@ pub(crate) unsafe fn write(&self, offset: usize, obj: &T) -> Result { unsafe { self.iterate(offset, size_of_val(obj), |page, offset, to_copy| { // SAFETY: The sum of `offset` and `to_copy` is bounded by the size of T. - let obj_ptr = (obj as *const T as *const u8).add(obj_offset); + let obj_ptr = (obj as *const T).cast::().add(obj_offset); // SAFETY: We have a reference to the object, so the pointer is valid. page.write_raw(obj_ptr, offset, to_copy)?; obj_offset += to_copy; @@ -712,7 +712,7 @@ fn drop(self: Pin<&mut Self>) { { // CAST: The `list_head` field is first in `PageInfo`. - let info = item as *mut PageInfo; + let info = item.cast::(); // SAFETY: The `range` field of `PageInfo` is immutable. range_ptr = unsafe { (*info).range }; // SAFETY: The `range` outlives its `PageInfo` values. diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index d487638266e3..fa28697982d3 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -6,12 +6,7 @@ #![crate_name = "rust_binder"] #![recursion_limit = "256"] -#![allow( - clippy::as_underscore, - clippy::ref_as_ptr, - clippy::ptr_as_ptr, - clippy::cast_lossless -)] +#![allow(clippy::as_underscore, clippy::ref_as_ptr, clippy::cast_lossless)] use kernel::{ bindings::{self, seq_file}, From 36bddf13dc1b88616a2ecdb331ffc23c42a3eb67 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 26 May 2026 14:39:09 -0400 Subject: [PATCH 119/513] rust: binder: enable `clippy::ref_as_ptr` lint In Rust 1.78.0, Clippy introduced the `ref_as_ptr` lint [1]: > Using `as` casts may result in silently changing mutability or type. While this does not eliminate unchecked `as` conversions, it makes such conversions easier to scrutinize. It also has the slight benefit of removing a degree of freedom on which to bikeshed. Thus apply the changes and enable the lint in the Binder Rust driver -- no functional change intended. Link: https://rust-lang.github.io/rust-clippy/master/index.html#ref_as_ptr [1] Assisted-by: Codex:gpt-5 Signed-off-by: Tamir Duberstein Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260526-binder-strict-provenance-v2-3-a41d89c29bc5@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/defs.rs | 4 +++- drivers/android/binder/node.rs | 3 ++- drivers/android/binder/page_range.rs | 4 ++-- drivers/android/binder/rust_binder_main.rs | 2 +- drivers/android/binder/trace.rs | 4 +++- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/android/binder/defs.rs b/drivers/android/binder/defs.rs index 354c32c39e83..e697c2c13e29 100644 --- a/drivers/android/binder/defs.rs +++ b/drivers/android/binder/defs.rs @@ -4,6 +4,8 @@ use core::mem::MaybeUninit; use core::ops::{Deref, DerefMut}; +use core::ptr; + use kernel::{ transmute::{AsBytes, FromBytes}, uapi::{self, *}, @@ -165,7 +167,7 @@ impl BinderTransactionDataSecctx { pub(crate) fn tr_data(&mut self) -> &mut BinderTransactionData { // SAFETY: Transparent wrapper is safe to transmute. unsafe { - &mut *((&mut self.transaction_data as *mut uapi::binder_transaction_data) + &mut *(ptr::from_mut::(&mut self.transaction_data) .cast::()) } } diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs index d710940c3c8f..3d96738dee66 100644 --- a/drivers/android/binder/node.rs +++ b/drivers/android/binder/node.rs @@ -21,6 +21,7 @@ }; use core::mem; +use core::ptr; mod wrapper; pub(crate) use self::wrapper::CritIncrWrapper; @@ -321,7 +322,7 @@ pub(crate) unsafe fn remove_node_info( /// An id that is unique across all binder nodes on the system. Used as the key in the /// `by_node` map. pub(crate) fn global_id(&self) -> usize { - (self as *const Node).addr() + ptr::from_ref(self).addr() } pub(crate) fn get_id(&self) -> (u64, u64) { diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs index fbb93ebb9697..52ffbf3504e7 100644 --- a/drivers/android/binder/page_range.rs +++ b/drivers/android/binder/page_range.rs @@ -312,7 +312,7 @@ pub(crate) fn register_with_vma(&self, vma: &virt::VmaNew) -> Result { // SAFETY: This just initializes the pages array. unsafe { - let self_ptr = self as *const ShrinkablePageRange; + let self_ptr = ptr::from_ref(self); for i in 0..num_pages { let info = pages.as_mut_ptr().add(i); (&raw mut (*info).range).write(self_ptr); @@ -593,7 +593,7 @@ pub(crate) unsafe fn write(&self, offset: usize, obj: &T) -> Result { unsafe { self.iterate(offset, size_of_val(obj), |page, offset, to_copy| { // SAFETY: The sum of `offset` and `to_copy` is bounded by the size of T. - let obj_ptr = (obj as *const T).cast::().add(obj_offset); + let obj_ptr = ptr::from_ref(obj).cast::().add(obj_offset); // SAFETY: We have a reference to the object, so the pointer is valid. page.write_raw(obj_ptr, offset, to_copy)?; obj_offset += to_copy; diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index fa28697982d3..88da29413e16 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -6,7 +6,7 @@ #![crate_name = "rust_binder"] #![recursion_limit = "256"] -#![allow(clippy::as_underscore, clippy::ref_as_ptr, clippy::cast_lossless)] +#![allow(clippy::as_underscore, clippy::cast_lossless)] use kernel::{ bindings::{self, seq_file}, diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs index 5539672d7285..06aabb3cc2f1 100644 --- a/drivers/android/binder/trace.rs +++ b/drivers/android/binder/trace.rs @@ -4,6 +4,8 @@ use crate::transaction::Transaction; +use core::ptr; + use kernel::bindings::{rust_binder_transaction, task_struct}; use kernel::error::Result; use kernel::ffi::{c_int, c_uint, c_ulong}; @@ -26,7 +28,7 @@ #[inline] fn raw_transaction(t: &Transaction) -> rust_binder_transaction { - t as *const Transaction as rust_binder_transaction + ptr::from_ref(t).cast_mut().cast() } #[inline] From e9217e9776812aa63ca428d04053f6239e4308a5 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 26 May 2026 14:39:10 -0400 Subject: [PATCH 120/513] rust: binder: enable `clippy::as_underscore` In Rust 1.63.0, Clippy introduced the `as_underscore` lint [1]: > The conversion might include lossy conversion or a dangerous cast that > might go undetected due to the type being inferred. > > The lint is allowed by default as using `_` is less wordy than always > specifying the type. Always specifying the type is especially helpful in function call contexts where the inferred type may change at a distance. Specifying the type also allows Clippy to spot more cases of `useless_conversion`. Several inferred conversions from `binder_uintptr_t` to the driver's internal `u64` node identifiers are identity conversions. Although the UAPI header retains `BINDER_IPC_32BIT` for userspace building against older kernels, commit 1190b4e38f97 ("ANDROID: binder: remove 32-bit binder interface.") removed kernel support for selecting that protocol. Rust Binder therefore uses the 64-bit Binder protocol on every supported architecture. While this does not eliminate unchecked `as` conversions, it makes such conversions easier to scrutinize. It also has the slight benefit of removing a degree of freedom on which to bikeshed. Thus apply the changes and enable the lint in the Binder Rust driver -- no functional change intended. Link: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore [1] Assisted-by: Codex:gpt-5 Signed-off-by: Tamir Duberstein Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260526-binder-strict-provenance-v2-4-a41d89c29bc5@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/allocation.rs | 4 ++-- drivers/android/binder/defs.rs | 2 +- drivers/android/binder/node.rs | 11 +++++----- drivers/android/binder/node/wrapper.rs | 2 +- drivers/android/binder/process.rs | 8 ++++---- drivers/android/binder/rust_binder_main.rs | 4 ++-- drivers/android/binder/thread.rs | 24 ++++++++++++---------- drivers/android/binder/transaction.rs | 14 +++++++------ 8 files changed, 37 insertions(+), 32 deletions(-) diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index b7b05e72970a..d411a16bc63f 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -384,8 +384,8 @@ pub(crate) fn transfer_binder_object( BINDER_TYPE_WEAK_BINDER }; newobj.flags = obj.flags; - newobj.__bindgen_anon_1.binder = ptr as _; - newobj.cookie = cookie as _; + newobj.__bindgen_anon_1.binder = ptr as uapi::binder_uintptr_t; + newobj.cookie = cookie as uapi::binder_uintptr_t; self.write(offset, &newobj)?; // Increment the user ref count on the node. It will be decremented as part of the // destruction of the buffer, when we see a binder or weak-binder object. diff --git a/drivers/android/binder/defs.rs b/drivers/android/binder/defs.rs index e697c2c13e29..8ac9bdd7a499 100644 --- a/drivers/android/binder/defs.rs +++ b/drivers/android/binder/defs.rs @@ -148,7 +148,7 @@ fn default() -> Self { impl BinderVersion { pub(crate) fn current() -> Self { Self(MaybeUninit::new(uapi::binder_version { - protocol_version: BINDER_CURRENT_PROTOCOL_VERSION as _, + protocol_version: BINDER_CURRENT_PROTOCOL_VERSION as i32, })) } } diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs index 3d96738dee66..4e75f58bf0db 100644 --- a/drivers/android/binder/node.rs +++ b/drivers/android/binder/node.rs @@ -9,6 +9,7 @@ seq_print, sync::lock::{spinlock::SpinLockBackend, Guard}, sync::{Arc, LockedBy, SpinLock}, + uapi, }; use crate::{ @@ -465,7 +466,7 @@ pub(crate) fn incr_refcount_allow_zero2one_with_wrapper( owner_inner: &mut ProcessInner, ) -> Option> { match self.incr_refcount_allow_zero2one(strong, owner_inner) { - Ok(Some(node)) => Some(node as _), + Ok(Some(node)) => Some(node as DLArc), Ok(None) => None, Err(CouldNotDeliverCriticalIncrement) => { assert!(strong); @@ -490,8 +491,8 @@ pub(crate) fn populate_counts( guard: &Guard<'_, ProcessInner, SpinLockBackend>, ) { let inner = self.inner.access(guard); - out.strong_count = inner.strong.count as _; - out.weak_count = inner.weak.count as _; + out.strong_count = inner.strong.count as u32; + out.weak_count = inner.weak.count as u32; } pub(crate) fn populate_debug_info( @@ -499,8 +500,8 @@ pub(crate) fn populate_debug_info( out: &mut BinderNodeDebugInfo, guard: &Guard<'_, ProcessInner, SpinLockBackend>, ) { - out.ptr = self.ptr as _; - out.cookie = self.cookie as _; + out.ptr = self.ptr as uapi::binder_uintptr_t; + out.cookie = self.cookie as uapi::binder_uintptr_t; let inner = self.inner.access(guard); if inner.strong.has_count { out.has_strong_ref = 1; diff --git a/drivers/android/binder/node/wrapper.rs b/drivers/android/binder/node/wrapper.rs index 43294c050502..6e4ca01c941a 100644 --- a/drivers/android/binder/node/wrapper.rs +++ b/drivers/android/binder/node/wrapper.rs @@ -21,7 +21,7 @@ pub(crate) fn new() -> Result { pub(super) fn init(self, node: DArc) -> DLArc { match self.inner.pin_init_with(DTRWrap::new(NodeWrapper { node })) { - Ok(initialized) => ListArc::from(initialized) as _, + Ok(initialized) => ListArc::from(initialized) as DLArc, Err(err) => match err {}, } } diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 96b8440ceac6..49ca1bcd21a1 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -32,7 +32,7 @@ lock::{spinlock::SpinLockBackend, Guard}, Arc, ArcBorrow, CondVar, CondVarTimeoutResult, Mutex, SpinLock, UniqueArc, }, - task::Task, + task::{Pid, Task}, uaccess::{UserSlice, UserSliceReader}, uapi, workqueue::{self, Work}, @@ -259,7 +259,7 @@ pub(crate) fn new_node_ref_with_thread( let push = match wrapper { None => node .incr_refcount_allow_zero2one(strong, self)? - .map(|node| node as _), + .map(|node| node as DLArc), Some(wrapper) => node.incr_refcount_allow_zero2one_with_wrapper(strong, wrapper, self), }; if let Some(node) = push { @@ -741,7 +741,7 @@ fn set_as_manager( } else { (0, 0, 0) }; - let node_ref = self.get_node(ptr, cookie, flags as _, true, thread)?; + let node_ref = self.get_node(ptr, cookie, flags, true, thread)?; let node = node_ref.node.clone(); self.ctx.set_manager_node(node_ref)?; self.inner.lock().is_manager = true; @@ -1522,7 +1522,7 @@ fn get_frozen_status(data: UserSlice) -> Result { for ctx in crate::context::get_all_contexts()? { ctx.for_each_proc(|proc| { - if proc.task.pid() == info.pid as _ { + if proc.task.pid() == info.pid as Pid { found = true; let inner = proc.inner.lock(); let txns_pending = inner.txns_pending_locked(); diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index 88da29413e16..2c10a8cd3d88 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -6,7 +6,7 @@ #![crate_name = "rust_binder"] #![recursion_limit = "256"] -#![allow(clippy::as_underscore, clippy::cast_lossless)] +#![allow(clippy::cast_lossless)] use kernel::{ bindings::{self, seq_file}, @@ -412,7 +412,7 @@ unsafe impl Sync for AssertSync {} // SAFETY: We previously set `private_data` in `rust_binder_open`. let f = unsafe { Arc::::borrow((*file).private_data) }; // SAFETY: The caller ensures that the file is valid. - match Process::ioctl(f, unsafe { File::from_raw_file(file) }, cmd as _, arg as _) { + match Process::ioctl(f, unsafe { File::from_raw_file(file) }, cmd, arg) { Ok(()) => 0, Err(err) => err.to_errno() as isize, } diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 97d5f31e8fe3..87298a8c597d 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -666,9 +666,9 @@ fn translate_object( let strong = obj.hdr.type_ == BINDER_TYPE_BINDER; // SAFETY: `binder` is a `binder_uintptr_t`; any bit pattern is a valid // representation. - let ptr = unsafe { obj.__bindgen_anon_1.binder } as _; - let cookie = obj.cookie as _; - let flags = obj.flags as _; + let ptr = unsafe { obj.__bindgen_anon_1.binder }; + let cookie = obj.cookie; + let flags = obj.flags; let node = self .process .as_arc_borrow() @@ -679,7 +679,7 @@ fn translate_object( BinderObjectRef::Handle(obj) => { let strong = obj.hdr.type_ == BINDER_TYPE_HANDLE; // SAFETY: `handle` is a `u32`; any bit pattern is a valid representation. - let handle = unsafe { obj.__bindgen_anon_1.handle } as _; + let handle = unsafe { obj.__bindgen_anon_1.handle }; let node = self.process.get_node_from_handle(handle, strong)?; security::binder_transfer_binder(&self.process.cred, &view.alloc.process.cred)?; view.transfer_binder_object(offset, obj, strong, node)?; @@ -736,7 +736,7 @@ fn translate_object( ScatterGatherEntry { obj_index, offset: alloc_offset, - sender_uaddr: obj.buffer as _, + sender_uaddr: obj.buffer as usize, length: obj_length, pointer_fixups: KVec::new(), fixup_min_offset: 0, @@ -843,7 +843,7 @@ fn translate_object( .ok_or(EINVAL)?; let mut fda_bytes = KVec::new(); - UserSlice::new(UserPtr::from_addr(fda_uaddr as _), fds_len) + UserSlice::new(UserPtr::from_addr(fda_uaddr as usize), fds_len) .read_all(&mut fda_bytes, GFP_KERNEL)?; if fds_len != fda_bytes.len() { @@ -1365,7 +1365,7 @@ fn write(self: &Arc, req: &mut BinderWriteRead) -> Result { let write_start = req.write_buffer.wrapping_add(req.write_consumed); let write_len = req.write_size.saturating_sub(req.write_consumed); let mut reader = - UserSlice::new(UserPtr::from_addr(write_start as _), write_len as _).reader(); + UserSlice::new(UserPtr::from_addr(write_start as usize), write_len as usize).reader(); while reader.len() >= size_of::() && self.inner.lock().return_work.is_unused() { let before = reader.len(); @@ -1436,7 +1436,7 @@ fn read(self: &Arc, req: &mut BinderWriteRead, wait: bool) -> Result { let read_start = req.read_buffer.wrapping_add(req.read_consumed); let read_len = req.read_size.saturating_sub(req.read_consumed); let mut writer = BinderReturnWriter::new( - UserSlice::new(UserPtr::from_addr(read_start as _), read_len as _).writer(), + UserSlice::new(UserPtr::from_addr(read_start as usize), read_len as usize).writer(), self, ); let (in_pool, has_transaction, thread_todo, use_proc_queue) = { @@ -1500,9 +1500,11 @@ fn read(self: &Arc, req: &mut BinderWriteRead, wait: bool) -> Result { // Write BR_SPAWN_LOOPER if the process needs more threads for its pool. if has_noop_placeholder && in_pool && self.process.needs_thread() { - let mut writer = - UserSlice::new(UserPtr::from_addr(req.read_buffer as _), req.read_size as _) - .writer(); + let mut writer = UserSlice::new( + UserPtr::from_addr(req.read_buffer as usize), + req.read_size as usize, + ) + .writer(); writer.write(&BR_SPAWN_LOOPER)?; } Ok(()) diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 1d9b66920a21..38795224a784 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -11,6 +11,7 @@ task::{Kuid, Pid}, time::{Instant, Monotonic}, types::ScopeGuard, + uapi, }; use crate::{ @@ -411,16 +412,17 @@ fn do_work( let tr = tr_sec.tr_data(); if let Some(target_node) = &self.target_node { let (ptr, cookie) = target_node.get_id(); - tr.target.ptr = ptr as _; - tr.cookie = cookie as _; + tr.target.ptr = ptr as uapi::binder_uintptr_t; + tr.cookie = cookie as uapi::binder_uintptr_t; }; tr.code = self.code; tr.flags = self.flags; - tr.data_size = self.data_size as _; - tr.data.ptr.buffer = self.data_address as _; - tr.offsets_size = self.offsets_size as _; + tr.data_size = self.data_size as uapi::binder_size_t; + tr.data.ptr.buffer = self.data_address as uapi::binder_uintptr_t; + tr.offsets_size = self.offsets_size as uapi::binder_size_t; if tr.offsets_size > 0 { - tr.data.ptr.offsets = (self.data_address + ptr_align(self.data_size).unwrap()) as _; + tr.data.ptr.offsets = + (self.data_address + ptr_align(self.data_size).unwrap()) as uapi::binder_uintptr_t; } tr.sender_euid = self.sender_euid.into_uid_in_current_ns(); tr.sender_pid = 0; From 9e32d2a9784736b3fc262f51ddda1141de753314 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 26 May 2026 14:39:11 -0400 Subject: [PATCH 121/513] rust: binder: enable `clippy::cast_lossless` Before Rust 1.29.0, Clippy introduced the `cast_lossless` lint [1]: > Rust's `as` keyword will perform many kinds of conversions, including > silently lossy conversions. Conversion functions such as `i32::from` > will only perform lossless conversions. Using the conversion functions > prevents conversions from becoming silently lossy if the input types > ever change, and makes it clear for people reading the code that the > conversion is lossless. While this does not eliminate unchecked `as` conversions, it makes such conversions easier to scrutinize. It also has the slight benefit of removing a degree of freedom on which to bikeshed. Thus apply the changes and enable the lint in the Binder Rust driver -- no functional change intended. Link: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless [1] Reviewed-by: Alice Ryhl Assisted-by: Codex:gpt-5 Signed-off-by: Tamir Duberstein Link: https://patch.msgid.link/20260526-binder-strict-provenance-v2-5-a41d89c29bc5@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/freeze.rs | 2 +- drivers/android/binder/process.rs | 6 +++--- drivers/android/binder/rust_binder_main.rs | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs index 53b60035639a..2178258772e5 100644 --- a/drivers/android/binder/freeze.rs +++ b/drivers/android/binder/freeze.rs @@ -127,7 +127,7 @@ fn do_work( } let mut state_info = BinderFrozenStateInfo::default(); - state_info.is_frozen = is_frozen as u32; + state_info.is_frozen = u32::from(is_frozen); state_info.cookie = freeze.cookie.0; freeze.is_pending = true; freeze.last_is_frozen = Some(is_frozen); diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 49ca1bcd21a1..99d1a7ade599 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -1526,9 +1526,9 @@ fn get_frozen_status(data: UserSlice) -> Result { found = true; let inner = proc.inner.lock(); let txns_pending = inner.txns_pending_locked(); - info.async_recv |= inner.async_recv as u32; - info.sync_recv |= inner.sync_recv as u32; - info.sync_recv |= (txns_pending as u32) << 1; + info.async_recv |= u32::from(inner.async_recv); + info.sync_recv |= u32::from(inner.sync_recv); + info.sync_recv |= u32::from(txns_pending) << 1; } }); } diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index 2c10a8cd3d88..432390aab25b 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -6,7 +6,6 @@ #![crate_name = "rust_binder"] #![recursion_limit = "256"] -#![allow(clippy::cast_lossless)] use kernel::{ bindings::{self, seq_file}, From 2b26ccd8869278a4975dccda0b77798aca286470 Mon Sep 17 00:00:00 2001 From: Prashant Rahul Date: Fri, 19 Jun 2026 04:44:35 +0530 Subject: [PATCH 122/513] iio: adc: ti-adc084s021: use dev_err_probe for probe time error This simplifies error handling and ensures consistent error reporting. Signed-off-by: Prashant Rahul Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-adc084s021.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c index a4f82a629998..97e5eefc78f6 100644 --- a/drivers/iio/adc/ti-adc084s021.c +++ b/drivers/iio/adc/ti-adc084s021.c @@ -229,10 +229,8 @@ static int adc084s021_probe(struct spi_device *spi) ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, adc084s021_buffer_trigger_handler, &adc084s021_buffer_setup_ops); - if (ret) { - dev_err(&spi->dev, "Failed to setup triggered buffer\n"); - return ret; - } + if (ret) + return dev_err_probe(&spi->dev, ret, "Failed to setup triggered buffer\n"); return devm_iio_device_register(&spi->dev, indio_dev); } From 1e3a7393a602762e91aa84bad72cde40ccb30b35 Mon Sep 17 00:00:00 2001 From: Tomasz Duszynski Date: Thu, 2 Jul 2026 21:17:41 +0200 Subject: [PATCH 123/513] mailmap: add email mapping for Tomasz Duszynski Old email is no longer valid hence add mapping to avoid bounces. Reviewed-by: Andy Shevchenko Acked-by: Maxwell Doose Signed-off-by: Tomasz Duszynski Signed-off-by: Jonathan Cameron --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index 23eb9a4b04f4..d6c1581bcccf 100644 --- a/.mailmap +++ b/.mailmap @@ -868,6 +868,7 @@ Tobias Klauser Tobias Klauser Tobias Klauser Todor Tomov +Tomasz Duszynski Tomasz Jeznach Tony Luck Trilok Soni From 24659af6994603d0314b2a2bad9df6bcdfbaeb0d Mon Sep 17 00:00:00 2001 From: Tomasz Duszynski Date: Thu, 2 Jul 2026 21:17:42 +0200 Subject: [PATCH 124/513] iio: chemical: sps30: update email for Tomasz Duszynski The old email has been inactive for a while. Replace all occurrences with the current address. Reviewed-by: Andy Shevchenko Acked-by: Maxwell Doose Signed-off-by: Tomasz Duszynski Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/sps30_i2c.c | 4 ++-- drivers/iio/chemical/sps30_serial.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/chemical/sps30_i2c.c b/drivers/iio/chemical/sps30_i2c.c index 61781aaabd85..bd4a2e6eff5f 100644 --- a/drivers/iio/chemical/sps30_i2c.c +++ b/drivers/iio/chemical/sps30_i2c.c @@ -2,7 +2,7 @@ /* * Sensirion SPS30 particulate matter sensor i2c driver * - * Copyright (c) 2020 Tomasz Duszynski + * Copyright (c) 2020 Tomasz Duszynski * * I2C slave address: 0x69 */ @@ -253,7 +253,7 @@ static struct i2c_driver sps30_i2c_driver = { }; module_i2c_driver(sps30_i2c_driver); -MODULE_AUTHOR("Tomasz Duszynski "); +MODULE_AUTHOR("Tomasz Duszynski "); MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor i2c driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_SPS30"); diff --git a/drivers/iio/chemical/sps30_serial.c b/drivers/iio/chemical/sps30_serial.c index a5e6bc08d5fd..c8bad4de0c26 100644 --- a/drivers/iio/chemical/sps30_serial.c +++ b/drivers/iio/chemical/sps30_serial.c @@ -2,7 +2,7 @@ /* * Sensirion SPS30 particulate matter sensor serial driver * - * Copyright (c) 2021 Tomasz Duszynski + * Copyright (c) 2021 Tomasz Duszynski */ #include #include @@ -426,7 +426,7 @@ static struct serdev_device_driver sps30_serial_driver = { }; module_serdev_device_driver(sps30_serial_driver); -MODULE_AUTHOR("Tomasz Duszynski "); +MODULE_AUTHOR("Tomasz Duszynski "); MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor serial driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_SPS30"); From 023a6a1f09ad52dc9b54dec867331c499f6757af Mon Sep 17 00:00:00 2001 From: Tomasz Duszynski Date: Thu, 2 Jul 2026 21:17:43 +0200 Subject: [PATCH 125/513] iio: chemical: scd30: update email for Tomasz Duszynski The old email has been inactive for a while. Replace all occurrences with the current address. While at it, add myself to MAINTAINERS file as relevant driver files still hold my address. Reviewed-by: Andy Shevchenko Acked-by: Maxwell Doose Signed-off-by: Tomasz Duszynski Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + drivers/iio/chemical/scd30_core.c | 4 ++-- drivers/iio/chemical/scd30_i2c.c | 4 ++-- drivers/iio/chemical/scd30_serial.c | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 0b46a74d74bb..8b7ad5311383 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24550,6 +24550,7 @@ F: drivers/iio/chemical/sunrise_co2.c SENSIRION SCD30 CARBON DIOXIDE SENSOR DRIVER M: Maxwell Doose +M: Tomasz Duszynski S: Maintained F: Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml F: drivers/iio/chemical/scd30.h diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index 23895744c42d..f85cdd8bd84f 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -2,7 +2,7 @@ /* * Sensirion SCD30 carbon dioxide sensor core driver * - * Copyright (c) 2020 Tomasz Duszynski + * Copyright (c) 2020 Tomasz Duszynski */ #include @@ -774,6 +774,6 @@ int scd30_probe(struct device *dev, int irq, const char *name, void *priv, } EXPORT_SYMBOL_NS(scd30_probe, "IIO_SCD30"); -MODULE_AUTHOR("Tomasz Duszynski "); +MODULE_AUTHOR("Tomasz Duszynski "); MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor core driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/chemical/scd30_i2c.c b/drivers/iio/chemical/scd30_i2c.c index 9e841f565149..406f650a0083 100644 --- a/drivers/iio/chemical/scd30_i2c.c +++ b/drivers/iio/chemical/scd30_i2c.c @@ -2,7 +2,7 @@ /* * Sensirion SCD30 carbon dioxide sensor i2c driver * - * Copyright (c) 2020 Tomasz Duszynski + * Copyright (c) 2020 Tomasz Duszynski * * I2C slave address: 0x61 */ @@ -137,7 +137,7 @@ static struct i2c_driver scd30_i2c_driver = { }; module_i2c_driver(scd30_i2c_driver); -MODULE_AUTHOR("Tomasz Duszynski "); +MODULE_AUTHOR("Tomasz Duszynski "); MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor i2c driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_SCD30"); diff --git a/drivers/iio/chemical/scd30_serial.c b/drivers/iio/chemical/scd30_serial.c index e8b453aae859..05d9e0e9de1e 100644 --- a/drivers/iio/chemical/scd30_serial.c +++ b/drivers/iio/chemical/scd30_serial.c @@ -2,7 +2,7 @@ /* * Sensirion SCD30 carbon dioxide sensor serial driver * - * Copyright (c) 2020 Tomasz Duszynski + * Copyright (c) 2020 Tomasz Duszynski */ #include #include @@ -258,7 +258,7 @@ static struct serdev_device_driver scd30_serdev_driver = { }; module_serdev_device_driver(scd30_serdev_driver); -MODULE_AUTHOR("Tomasz Duszynski "); +MODULE_AUTHOR("Tomasz Duszynski "); MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor serial driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_SCD30"); From b4d8a2e93ff3d963f672e2e8c866caf35585139a Mon Sep 17 00:00:00 2001 From: Tomasz Duszynski Date: Thu, 2 Jul 2026 21:17:44 +0200 Subject: [PATCH 126/513] dt-bindings: iio: scd30: update maintainers field I was dropped as maintainer because emails to my previous address were bouncing. I have not stepped down from maintainership, so re-add me with my current email address. Signed-off-by: Tomasz Duszynski Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/chemical/sensirion,scd30.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml b/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml index a5b0debe85b1..25291bda1a8b 100644 --- a/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml +++ b/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml @@ -8,6 +8,7 @@ title: Sensirion SCD30 carbon dioxide sensor maintainers: - Maxwell Doose + - Tomasz Duszynski description: | Air quality sensor capable of measuring co2 concentration, temperature From 331ca76fe5cee0df5a025d97d68fee53140053c7 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 16 May 2026 10:09:37 -0500 Subject: [PATCH 127/513] iio: resolver: ad2s1210: refactor trigger handler Refactor the trigger handler to avoid use of goto in a guard() scope. Ideally, we should not be mixing goto and automatic cleanup. iio_trigger_notify_done() was not part of the critical section anyway, so is not moved to the new function. Signed-off-by: David Lechner Reviewed-by: Stepan Ionichev Signed-off-by: Jonathan Cameron --- drivers/iio/resolver/ad2s1210.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c index db8e34e2ba73..3b5ec21e3446 100644 --- a/drivers/iio/resolver/ad2s1210.c +++ b/drivers/iio/resolver/ad2s1210.c @@ -1276,10 +1276,8 @@ static int ad2s1210_debugfs_reg_access(struct iio_dev *indio_dev, return regmap_write(st->regmap, reg, writeval); } -static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) +static void ad2s1210_scan_to_buffers(struct iio_dev *indio_dev, s64 timestamp) { - struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->indio_dev; struct ad2s1210_state *st = iio_priv(indio_dev); size_t chan = 0; int ret; @@ -1295,15 +1293,15 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) AD2S1210_REG_POSITION_MSB, &st->sample.raw, 2); if (ret < 0) - goto error_ret; + return; } else { ret = ad2s1210_set_mode(st, MOD_POS); if (ret < 0) - goto error_ret; + return; ret = spi_read(st->sdev, &st->sample, 3); if (ret < 0) - goto error_ret; + return; } memcpy(&st->scan.chan[chan++], &st->sample.raw, 2); @@ -1315,15 +1313,15 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) AD2S1210_REG_VELOCITY_MSB, &st->sample.raw, 2); if (ret < 0) - goto error_ret; + return; } else { ret = ad2s1210_set_mode(st, MOD_VEL); if (ret < 0) - goto error_ret; + return; ret = spi_read(st->sdev, &st->sample, 3); if (ret < 0) - goto error_ret; + return; } memcpy(&st->scan.chan[chan++], &st->sample.raw, 2); @@ -1334,16 +1332,22 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, ®_val); if (ret < 0) - goto error_ret; + return; st->sample.fault = reg_val; } - ad2s1210_push_events(indio_dev, st->sample.fault, pf->timestamp); + ad2s1210_push_events(indio_dev, st->sample.fault, timestamp); iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan), - pf->timestamp); + timestamp); +} -error_ret: +static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + + ad2s1210_scan_to_buffers(indio_dev, pf->timestamp); iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; From d07bf43d175a2be1371ef5460b3e8f8ba3d60bd7 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Mon, 18 May 2026 23:11:10 +0500 Subject: [PATCH 128/513] iio: pressure: rohm-bm1390: harden trigger handler against transient errors bm1390_trigger_handler() returns from three error paths without calling iio_trigger_notify_done(). The success path at the end does, so on a transient regmap or read failure the trigger's use_count is never decremented and iio_trigger_poll_chained() drops subsequent dispatches until the trigger is reattached. This is not a fix for a reported bug, only hardening against hardware or bus glitches; if a glitch is persistent the device is wedged and needs an unbind anyway, which is left to the user. Split the function so the inner helper returns bool with the data-read outcome, and the outer IRQ handler calls iio_trigger_notify_done() once and reports the result via IRQ_RETVAL(). Signed-off-by: Stepan Ionichev Reviewed-by: Matti Vaittinen Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/rohm-bm1390.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c index b3be9de03678..9d72ae64126c 100644 --- a/drivers/iio/pressure/rohm-bm1390.c +++ b/drivers/iio/pressure/rohm-bm1390.c @@ -621,17 +621,15 @@ static const struct iio_buffer_setup_ops bm1390_buffer_ops = { .predisable = bm1390_buffer_predisable, }; -static irqreturn_t bm1390_trigger_handler(int irq, void *p) +static bool bm1390_handle_trigger(struct iio_dev *idev) { - struct iio_poll_func *pf = p; - struct iio_dev *idev = pf->indio_dev; struct bm1390_data *data = iio_priv(idev); int ret, status; /* DRDY is acked by reading status reg */ ret = regmap_read(data->regmap, BM1390_REG_STATUS, &status); if (ret || !status) - return IRQ_NONE; + return false; dev_dbg(data->dev, "DRDY trig status 0x%x\n", status); @@ -639,7 +637,7 @@ static irqreturn_t bm1390_trigger_handler(int irq, void *p) ret = bm1390_pressure_read(data, &data->buf.pressure); if (ret) { dev_warn(data->dev, "sample read failed %d\n", ret); - return IRQ_NONE; + return false; } } @@ -648,15 +646,26 @@ static irqreturn_t bm1390_trigger_handler(int irq, void *p) &data->buf.temp, sizeof(data->buf.temp)); if (ret) { dev_warn(data->dev, "temp read failed %d\n", ret); - return IRQ_HANDLED; + return true; } } iio_push_to_buffers_with_ts(idev, &data->buf, sizeof(data->buf), data->timestamp); + + return true; +} + +static irqreturn_t bm1390_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *idev = pf->indio_dev; + bool result; + + result = bm1390_handle_trigger(idev); iio_trigger_notify_done(idev->trig); - return IRQ_HANDLED; + return IRQ_RETVAL(result); } /* Get timestamps and wake the thread if we need to read data */ From 8adc81ba2e674888e95015066ea73f382bd33209 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Fri, 29 May 2026 23:37:08 +0500 Subject: [PATCH 129/513] iio: adc: ti-ads1015: use devm helpers to fix probe error paths Once ads1015_probe() gets past ads1015_set_conv_mode(CONTINUOUS), two resources leak on its error paths and on driver unbind: - pm_runtime_enable() is left enabled if iio_device_register() fails; on subsequent probe/rebind the runtime PM tracking complains about an unbalanced enable. - The CONTINUOUS conversion mode written to the chip is never restored on any error path after that point, so on probe failure the chip is left running. Convert all three teardown operations of ads1015_remove() to devm so that the error paths and the unbind path share the same unwind in reverse-of-setup order: - ads1015_set_conv_mode(SINGLESHOT) becomes a devm action registered immediately after CONTINUOUS mode is enabled. - pm_runtime_enable() becomes devm_pm_runtime_enable(). - iio_device_register() becomes devm_iio_device_register(), so the iio device is unregistered first on unbind. ads1015_remove() is no longer needed and is dropped. Signed-off-by: Stepan Ionichev Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1015.c | 46 +++++++++++++++--------------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index 8a272af69f7d..0fbfa4e499aa 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -930,6 +930,17 @@ static int ads1015_set_conv_mode(struct ads1015_data *data, int mode) mode << ADS1015_CFG_MOD_SHIFT); } +static void ads1015_power_down(void *p) +{ + struct ads1015_data *data = p; + int ret; + + ret = ads1015_set_conv_mode(data, ADS1015_SINGLESHOT); + if (ret) + dev_warn(regmap_get_device(data->regmap), + "Failed to power down (%pe)\n", ERR_PTR(ret)); +} + static int ads1015_probe(struct i2c_client *client) { const struct ads1015_chip_data *chip; @@ -1030,6 +1041,10 @@ static int ads1015_probe(struct i2c_client *client) if (ret) return ret; + ret = devm_add_action_or_reset(&client->dev, ads1015_power_down, data); + if (ret) + return ret; + data->conv_invalid = true; ret = pm_runtime_set_active(&client->dev); @@ -1037,33 +1052,11 @@ static int ads1015_probe(struct i2c_client *client) return ret; pm_runtime_set_autosuspend_delay(&client->dev, ADS1015_SLEEP_DELAY_MS); pm_runtime_use_autosuspend(&client->dev); - pm_runtime_enable(&client->dev); - - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(&client->dev, "Failed to register IIO device\n"); - return ret; - } - - return 0; -} - -static void ads1015_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct ads1015_data *data = iio_priv(indio_dev); - int ret; - - iio_device_unregister(indio_dev); - - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - - /* power down single shot mode */ - ret = ads1015_set_conv_mode(data, ADS1015_SINGLESHOT); + ret = devm_pm_runtime_enable(&client->dev); if (ret) - dev_warn(&client->dev, "Failed to power down (%pe)\n", - ERR_PTR(ret)); + return ret; + + return devm_iio_device_register(&client->dev, indio_dev); } #ifdef CONFIG_PM @@ -1150,7 +1143,6 @@ static struct i2c_driver ads1015_driver = { .pm = &ads1015_pm_ops, }, .probe = ads1015_probe, - .remove = ads1015_remove, .id_table = ads1015_id, }; From faa63a51bea10151952c6b35d66d72ce0d25efa1 Mon Sep 17 00:00:00 2001 From: Eduard Bostina Date: Tue, 30 Jun 2026 12:48:25 +0000 Subject: [PATCH 130/513] dt-bindings: w1: Convert HDQ One Wire to DT schema Convert the Texas Instruments OMAP HDQ One Wire bindings to DT schema. During the conversion, several updates were made to reflect actual hardware usage and resolve dtbs_check warnings: - 'ti,hwmods' has been made optional. - Added 'clocks' and 'clock-names' properties as they are used in actual device trees (e.g., am437x-l4.dtsi). - Added a second example for the AM4372 HDQ controller to demonstrate the clock and interrupt configuration. Signed-off-by: Eduard Bostina Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260630124826.2992529-1-egbostina@gmail.com [krzk: Rename node names to onewire to match convention, fix path in omap-hdq.rst] Signed-off-by: Krzysztof Kozlowski --- .../devicetree/bindings/w1/omap-hdq.txt | 22 ------ .../devicetree/bindings/w1/ti,hdq.yaml | 70 +++++++++++++++++++ Documentation/w1/masters/omap-hdq.rst | 2 +- 3 files changed, 71 insertions(+), 23 deletions(-) delete mode 100644 Documentation/devicetree/bindings/w1/omap-hdq.txt create mode 100644 Documentation/devicetree/bindings/w1/ti,hdq.yaml diff --git a/Documentation/devicetree/bindings/w1/omap-hdq.txt b/Documentation/devicetree/bindings/w1/omap-hdq.txt deleted file mode 100644 index 913c5f91a0f9..000000000000 --- a/Documentation/devicetree/bindings/w1/omap-hdq.txt +++ /dev/null @@ -1,22 +0,0 @@ -* OMAP HDQ One wire bus master controller - -Required properties: -- compatible : should be "ti,omap3-1w" or "ti,am4372-hdq" -- reg : Address and length of the register set for the device -- interrupts : interrupt line. -- ti,hwmods : "hdq1w" - -Optional properties: -- ti,mode: should be "hdq": HDQ mode "1w": one-wire mode. - If not specified HDQ mode is implied. - -Example: - -- From omap3.dtsi - hdqw1w: 1w@480b2000 { - compatible = "ti,omap3-1w"; - reg = <0x480b2000 0x1000>; - interrupts = <58>; - ti,hwmods = "hdq1w"; - ti,mode = "hdq"; - }; diff --git a/Documentation/devicetree/bindings/w1/ti,hdq.yaml b/Documentation/devicetree/bindings/w1/ti,hdq.yaml new file mode 100644 index 000000000000..fd402c0510db --- /dev/null +++ b/Documentation/devicetree/bindings/w1/ti,hdq.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/w1/ti,hdq.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments OMAP HDQ One Wire Bus Master Controller + +maintainers: + - Eduard Bostina + +properties: + compatible: + enum: + - ti,omap3-1w + - ti,am4372-hdq + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: fck + + ti,hwmods: + description: Name of the hwmod associated to the hdq + $ref: /schemas/types.yaml#/definitions/string + const: hdq1w + + ti,mode: + description: | + Operation mode. "hdq" for HDQ mode, "1w" for One-Wire mode. + If not specified, HDQ mode is implied. + $ref: /schemas/types.yaml#/definitions/string + enum: + - hdq + - 1w + default: hdq + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + #include + onewire@0 { + compatible = "ti,am4372-hdq"; + reg = <0x0 0x1000>; + interrupts = ; + clocks = <&func_12m_clk>; + clock-names = "fck"; + }; + - | + onewire@480b2000 { + compatible = "ti,omap3-1w"; + reg = <0x480b2000 0x1000>; + interrupts = <58>; + ti,hwmods = "hdq1w"; + ti,mode = "hdq"; + }; diff --git a/Documentation/w1/masters/omap-hdq.rst b/Documentation/w1/masters/omap-hdq.rst index 5347b5d9e90a..2ecf572be1a4 100644 --- a/Documentation/w1/masters/omap-hdq.rst +++ b/Documentation/w1/masters/omap-hdq.rst @@ -50,5 +50,5 @@ The driver also supports 1-wire mode. In this mode, there is no need to pass slave ID as parameter. The driver will auto-detect slaves connected to the bus using SEARCH_ROM procedure. 1-wire mode can be selected by setting "ti,mode" property to "1w" in DT (see -Documentation/devicetree/bindings/w1/omap-hdq.txt for more details). +Documentation/devicetree/bindings/w1/ti,hdq.yaml for more details). By default driver is in HDQ mode. From 169ae5e65e5aaf213b6a578f6478a9fd2e523606 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Mon, 29 Jun 2026 20:10:43 +0800 Subject: [PATCH 131/513] w1: ds28e17: reject an oversize length on an I2C block read w1_f19_i2c_master_transfer() is the master_xfer for the DS28E17 1-Wire to I2C bridge. On an I2C_M_RECV_LEN read, it takes the length from the device. The downstream slave puts a length byte in buf[0]. The driver then reads that many bytes into buf[1] with w1_f19_i2c_read(). buf[0] is controlled by the device and can be 0 to 255. w1_f19_i2c_read() only rejects a zero count. The caller buffer is I2C_SMBUS_BLOCK_MAX + 2, so 34 bytes. A length above 32 makes the read run past it, up to about 222 bytes out of bounds. The SMBus core does check buf[0] against I2C_SMBUS_BLOCK_MAX. That check runs after master_xfer returns. By then the write is already done. i2c-algo-bit rejects an oversize length before it copies, and returns -EPROTO. Reject a length above I2C_SMBUS_BLOCK_MAX at both RECV_LEN sites, the same way i2c-algo-bit does. Fixes: ebc4768ac497 ("add w1_ds28e17 driver for the DS28E17 Onewire to I2C master bridge") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Andi Shyti Link: https://patch.msgid.link/20260629121043.199487-1-maoyixie.tju@gmail.com Signed-off-by: Krzysztof Kozlowski --- drivers/w1/slaves/w1_ds28e17.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/w1/slaves/w1_ds28e17.c b/drivers/w1/slaves/w1_ds28e17.c index e53bc41bde3c..b638963d4b59 100644 --- a/drivers/w1/slaves/w1_ds28e17.c +++ b/drivers/w1/slaves/w1_ds28e17.c @@ -389,6 +389,10 @@ static int w1_f19_i2c_master_transfer(struct i2c_adapter *adapter, * another simple read in that case. */ if (msgs[i+1].flags & I2C_M_RECV_LEN) { + if (msgs[i+1].buf[0] > I2C_SMBUS_BLOCK_MAX) { + i = -EPROTO; + goto error; + } result = w1_f19_i2c_read(sl, msgs[i+1].addr, &(msgs[i+1].buf[1]), msgs[i+1].buf[0]); if (result < 0) { @@ -415,6 +419,10 @@ static int w1_f19_i2c_master_transfer(struct i2c_adapter *adapter, * another simple read in that case. */ if (msgs[i].flags & I2C_M_RECV_LEN) { + if (msgs[i].buf[0] > I2C_SMBUS_BLOCK_MAX) { + i = -EPROTO; + goto error; + } result = w1_f19_i2c_read(sl, msgs[i].addr, &(msgs[i].buf[1]), From c85df72d8dcc4f93ca9d3195670eff826ce3bef2 Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Fri, 3 Jul 2026 18:10:06 +0800 Subject: [PATCH 132/513] iio: ABI: add DAC 10kohm_to_gnd powerdown mode Add a new powerdown mode for DACs with 10kohm resistor to GND. Signed-off-by: Kim Seer Paller Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 4ea5598e7cd2..5cc2e82c4997 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -770,6 +770,7 @@ Description: 3.85kohm_to_gnd: connected to ground via a 3.85kOhm resistor, 6kohm_to_gnd: connected to ground via a 6kOhm resistor, 7.7kohm_to_gnd: connected to ground via a 7.7kOhm resistor, + 10kohm_to_gnd: connected to ground via a 10kOhm resistor, 16kohm_to_gnd: connected to ground via a 16kOhm resistor, 20kohm_to_gnd: connected to ground via a 20kOhm resistor, 32kohm_to_gnd: connected to ground via a 32kOhm resistor, From 57fe3897e12e6a3a17ba7378e5e2eec2fe0f3d9d Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Fri, 3 Jul 2026 18:10:07 +0800 Subject: [PATCH 133/513] dt-bindings: iio: dac: add support for AD3532R/AD3532 The AD3532R/AD3532 is a 16-channel version of the AD3530R/AD3530. This adds compatible strings for the AD3532R/AD3532. Acked-by: Conor Dooley Signed-off-by: Kim Seer Paller Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/dac/adi,ad3530r.yaml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad3530r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad3530r.yaml index a355d52a9d64..2fe098619772 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad3530r.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad3530r.yaml @@ -10,15 +10,17 @@ maintainers: - Kim Seer Paller description: | - The AD3530/AD3530R (8-channel) and AD3531/AD3531R (4-channel) are low-power, - 16-bit, buffered voltage output digital-to-analog converters (DACs) with - software-programmable gain controls, providing full-scale output spans of 2.5V - or 5V for reference voltages of 2.5V. These devices operate from a single 2.7V - to 5.5V supply and are guaranteed monotonic by design. The "R" variants - include a 2.5V, 5ppm/°C internal reference, which is disabled by default. + The AD3530/AD3530R (8-channel), AD3531/AD3531R (4-channel), and AD3532/AD3532R + (16-channel) are low-power, 16-bit, buffered voltage output digital-to-analog + converters (DACs) with software-programmable gain controls, providing + full-scale output spans of 2.5V or 5V for reference voltages of 2.5V. These + devices operate from a single 2.7V to 5.5V supply and are guaranteed monotonic + by design. The "R" variants include a 2.5V, 5ppm/°C internal reference, which + is disabled by default. Datasheet can be found here: https://www.analog.com/media/en/technical-documentation/data-sheets/ad3530_ad530r.pdf https://www.analog.com/media/en/technical-documentation/data-sheets/ad3531-ad3531r.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad3532r.pdf properties: compatible: @@ -27,6 +29,8 @@ properties: - adi,ad3530r - adi,ad3531 - adi,ad3531r + - adi,ad3532 + - adi,ad3532r reg: maxItems: 1 From f1d92ca09f35cc07740e2ba95afe109218f7c9e7 Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Fri, 3 Jul 2026 18:10:08 +0800 Subject: [PATCH 134/513] iio: dac: ad3530r: Convert sw_ldac_trig_reg to a function pointer The software LDAC trigger register is stored in the chip_info table as a fixed register address. Devices with a multi-bank register architecture select the trigger register based on the channel being updated, which a single static address cannot express. Convert sw_ldac_trig_reg into a function pointer that returns the trigger register for a given channel, mirroring the input_ch_reg callback. Signed-off-by: Kim Seer Paller Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad3530r.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/iio/dac/ad3530r.c b/drivers/iio/dac/ad3530r.c index 9d4545e2c9b3..7841546f0c07 100644 --- a/drivers/iio/dac/ad3530r.c +++ b/drivers/iio/dac/ad3530r.c @@ -69,8 +69,8 @@ struct ad3530r_chip_info { const char *name; const struct iio_chan_spec *channels; int (*input_ch_reg)(unsigned int channel); + int (*sw_ldac_trig_reg)(unsigned int channel); unsigned int num_channels; - unsigned int sw_ldac_trig_reg; bool internal_ref_support; }; @@ -190,6 +190,16 @@ static ssize_t ad3530r_set_dac_powerdown(struct iio_dev *indio_dev, return len; } +static int ad3530r_trigger_sw_ldac_reg(unsigned int channel) +{ + return AD3530R_SW_LDAC_TRIG_A; +} + +static int ad3531r_trigger_sw_ldac_reg(unsigned int channel) +{ + return AD3531R_SW_LDAC_TRIG_A; +} + static int ad3530r_trigger_hw_ldac(struct gpio_desc *ldac_gpio) { gpiod_set_value_cansleep(ldac_gpio, 1); @@ -215,7 +225,7 @@ static int ad3530r_dac_write(struct ad3530r_state *st, unsigned int chan, if (st->ldac_gpio) return ad3530r_trigger_hw_ldac(st->ldac_gpio); - return regmap_set_bits(st->regmap, st->chip_info->sw_ldac_trig_reg, + return regmap_set_bits(st->regmap, st->chip_info->sw_ldac_trig_reg(chan), AD3530R_SLD_TRIG_A); } @@ -335,7 +345,7 @@ static const struct ad3530r_chip_info ad3530_chip = { .name = "ad3530", .channels = ad3530r_channels, .num_channels = ARRAY_SIZE(ad3530r_channels), - .sw_ldac_trig_reg = AD3530R_SW_LDAC_TRIG_A, + .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, .input_ch_reg = ad3530r_input_ch_reg, .internal_ref_support = false, }; @@ -344,7 +354,7 @@ static const struct ad3530r_chip_info ad3530r_chip = { .name = "ad3530r", .channels = ad3530r_channels, .num_channels = ARRAY_SIZE(ad3530r_channels), - .sw_ldac_trig_reg = AD3530R_SW_LDAC_TRIG_A, + .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, .input_ch_reg = ad3530r_input_ch_reg, .internal_ref_support = true, }; @@ -353,7 +363,7 @@ static const struct ad3530r_chip_info ad3531_chip = { .name = "ad3531", .channels = ad3531r_channels, .num_channels = ARRAY_SIZE(ad3531r_channels), - .sw_ldac_trig_reg = AD3531R_SW_LDAC_TRIG_A, + .sw_ldac_trig_reg = ad3531r_trigger_sw_ldac_reg, .input_ch_reg = ad3531r_input_ch_reg, .internal_ref_support = false, }; @@ -362,7 +372,7 @@ static const struct ad3530r_chip_info ad3531r_chip = { .name = "ad3531r", .channels = ad3531r_channels, .num_channels = ARRAY_SIZE(ad3531r_channels), - .sw_ldac_trig_reg = AD3531R_SW_LDAC_TRIG_A, + .sw_ldac_trig_reg = ad3531r_trigger_sw_ldac_reg, .input_ch_reg = ad3531r_input_ch_reg, .internal_ref_support = true, }; From d373ecd2347f6b8b82b8cefa3a267cef569f5c2b Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Fri, 3 Jul 2026 18:10:09 +0800 Subject: [PATCH 135/513] iio: dac: ad3530r: Refactor setup to table-driven register banks Devices with a multi-bank register map repeat the same configuration across several banks, which the hardcoded register addresses in ad3530r_setup() cannot cover. Move the addresses into per-chip arrays and add ad3530r_set_reg_bank_bits() and ad3530r_write_reg_banks() to apply an operation to every bank. Each current device has a single bank, so no functional change. Signed-off-by: Kim Seer Paller Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad3530r.c | 113 ++++++++++++++++++++++++++++++++------ 1 file changed, 97 insertions(+), 16 deletions(-) diff --git a/drivers/iio/dac/ad3530r.c b/drivers/iio/dac/ad3530r.c index 7841546f0c07..42e77b150760 100644 --- a/drivers/iio/dac/ad3530r.c +++ b/drivers/iio/dac/ad3530r.c @@ -70,7 +70,13 @@ struct ad3530r_chip_info { const struct iio_chan_spec *channels; int (*input_ch_reg)(unsigned int channel); int (*sw_ldac_trig_reg)(unsigned int channel); + const unsigned int *interface_config_a; + const unsigned int *output_control; + const unsigned int *reference_control; + const unsigned int *op_mode; unsigned int num_channels; + unsigned int num_banks; + unsigned int num_op_mode_regs; bool internal_ref_support; }; @@ -341,12 +347,39 @@ static const struct iio_chan_spec ad3531r_channels[] = { AD3530R_CHAN(3, ad3531r_ext_info), }; +static const unsigned int ad3530r_if_config[] = { + AD3530R_INTERFACE_CONFIG_A, +}; + +static const unsigned int ad3530r_out_ctrl[] = { + AD3530R_OUTPUT_CONTROL_0, +}; + +static const unsigned int ad3530r_ref_ctrl[] = { + AD3530R_REFERENCE_CONTROL_0, +}; + +static const unsigned int ad3530r_op_mode[] = { + AD3530R_OUTPUT_OPERATING_MODE_0, + AD3530R_OUTPUT_OPERATING_MODE_1, +}; + +static const unsigned int ad3531r_op_mode[] = { + AD3530R_OUTPUT_OPERATING_MODE_0, +}; + static const struct ad3530r_chip_info ad3530_chip = { .name = "ad3530", .channels = ad3530r_channels, .num_channels = ARRAY_SIZE(ad3530r_channels), .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, .input_ch_reg = ad3530r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3530r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3530r_op_mode), .internal_ref_support = false, }; @@ -356,6 +389,12 @@ static const struct ad3530r_chip_info ad3530r_chip = { .num_channels = ARRAY_SIZE(ad3530r_channels), .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, .input_ch_reg = ad3530r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3530r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3530r_op_mode), .internal_ref_support = true, }; @@ -365,6 +404,12 @@ static const struct ad3530r_chip_info ad3531_chip = { .num_channels = ARRAY_SIZE(ad3531r_channels), .sw_ldac_trig_reg = ad3531r_trigger_sw_ldac_reg, .input_ch_reg = ad3531r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3531r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3531r_op_mode), .internal_ref_support = false, }; @@ -374,15 +419,54 @@ static const struct ad3530r_chip_info ad3531r_chip = { .num_channels = ARRAY_SIZE(ad3531r_channels), .sw_ldac_trig_reg = ad3531r_trigger_sw_ldac_reg, .input_ch_reg = ad3531r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3531r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3531r_op_mode), .internal_ref_support = true, }; +static int ad3530r_set_reg_bank_bits(const struct ad3530r_state *st, + const unsigned int *regs, + unsigned int num_regs, + unsigned int mask) +{ + int ret; + + for (unsigned int i = 0; i < num_regs; i++) { + ret = regmap_set_bits(st->regmap, regs[i], mask); + if (ret) + return ret; + } + + return 0; +} + +static int ad3530r_write_reg_banks(const struct ad3530r_state *st, + const unsigned int *regs, + unsigned int num_regs, + unsigned int val) +{ + int ret; + + for (unsigned int i = 0; i < num_regs; i++) { + ret = regmap_write(st->regmap, regs[i], val); + if (ret) + return ret; + } + + return 0; +} + static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) { + const struct ad3530r_chip_info *chip_info = st->chip_info; struct device *dev = regmap_get_device(st->regmap); struct gpio_desc *reset_gpio; - int i, ret; u8 range_multiplier, val; + int ret; reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(reset_gpio)) @@ -395,8 +479,9 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) gpiod_set_value_cansleep(reset_gpio, 0); } else { /* Perform software reset */ - ret = regmap_update_bits(st->regmap, AD3530R_INTERFACE_CONFIG_A, - AD3530R_SW_RESET, AD3530R_SW_RESET); + ret = ad3530r_set_reg_bank_bits(st, chip_info->interface_config_a, + chip_info->num_banks, + AD3530R_SW_RESET); if (ret) return ret; } @@ -405,8 +490,9 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) range_multiplier = 1; if (device_property_read_bool(dev, "adi,range-double")) { - ret = regmap_set_bits(st->regmap, AD3530R_OUTPUT_CONTROL_0, - AD3530R_OUTPUT_CONTROL_RANGE); + ret = ad3530r_set_reg_bank_bits(st, chip_info->output_control, + chip_info->num_banks, + AD3530R_OUTPUT_CONTROL_RANGE); if (ret) return ret; @@ -416,8 +502,9 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) if (external_vref_uV) { st->vref_mV = range_multiplier * external_vref_uV / MILLI; } else { - ret = regmap_set_bits(st->regmap, AD3530R_REFERENCE_CONTROL_0, - AD3530R_REFERENCE_CONTROL_SEL); + ret = ad3530r_set_reg_bank_bits(st, chip_info->reference_control, + chip_info->num_banks, + AD3530R_REFERENCE_CONTROL_SEL); if (ret) return ret; @@ -430,18 +517,12 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(2), AD3530R_NORMAL_OP) | FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(3), AD3530R_NORMAL_OP); - ret = regmap_write(st->regmap, AD3530R_OUTPUT_OPERATING_MODE_0, val); + ret = ad3530r_write_reg_banks(st, st->chip_info->op_mode, + st->chip_info->num_op_mode_regs, val); if (ret) return ret; - if (st->chip_info->num_channels > 4) { - ret = regmap_write(st->regmap, AD3530R_OUTPUT_OPERATING_MODE_1, - val); - if (ret) - return ret; - } - - for (i = 0; i < st->chip_info->num_channels; i++) + for (unsigned int i = 0; i < st->chip_info->num_channels; i++) st->chan[i].powerdown_mode = AD3530R_POWERDOWN_32K; st->ldac_gpio = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_LOW); From 7511d97a8f20b56762e5efc7dfd4a35d41d15b93 Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Fri, 3 Jul 2026 18:10:10 +0800 Subject: [PATCH 136/513] iio: dac: ad3530r: Make regmap_config selectable per chip Devices with a larger register map need their own regmap_config so debugfs register access stays within each device's address range. Move the config into the chip_info table and let probe pass the per-chip config. All current devices share the same config, so no functional change. Signed-off-by: Kim Seer Paller Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad3530r.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/iio/dac/ad3530r.c b/drivers/iio/dac/ad3530r.c index 42e77b150760..be63eb819c93 100644 --- a/drivers/iio/dac/ad3530r.c +++ b/drivers/iio/dac/ad3530r.c @@ -68,6 +68,7 @@ struct ad3530r_chan { struct ad3530r_chip_info { const char *name; const struct iio_chan_spec *channels; + const struct regmap_config *regmap_config; int (*input_ch_reg)(unsigned int channel); int (*sw_ldac_trig_reg)(unsigned int channel); const unsigned int *interface_config_a; @@ -368,9 +369,16 @@ static const unsigned int ad3531r_op_mode[] = { AD3530R_OUTPUT_OPERATING_MODE_0, }; +static const struct regmap_config ad3530r_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = AD3530R_MAX_REG_ADDR, +}; + static const struct ad3530r_chip_info ad3530_chip = { .name = "ad3530", .channels = ad3530r_channels, + .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3530r_channels), .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, .input_ch_reg = ad3530r_input_ch_reg, @@ -386,6 +394,7 @@ static const struct ad3530r_chip_info ad3530_chip = { static const struct ad3530r_chip_info ad3530r_chip = { .name = "ad3530r", .channels = ad3530r_channels, + .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3530r_channels), .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, .input_ch_reg = ad3530r_input_ch_reg, @@ -401,6 +410,7 @@ static const struct ad3530r_chip_info ad3530r_chip = { static const struct ad3530r_chip_info ad3531_chip = { .name = "ad3531", .channels = ad3531r_channels, + .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3531r_channels), .sw_ldac_trig_reg = ad3531r_trigger_sw_ldac_reg, .input_ch_reg = ad3531r_input_ch_reg, @@ -416,6 +426,7 @@ static const struct ad3530r_chip_info ad3531_chip = { static const struct ad3530r_chip_info ad3531r_chip = { .name = "ad3531r", .channels = ad3531r_channels, + .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3531r_channels), .sw_ldac_trig_reg = ad3531r_trigger_sw_ldac_reg, .input_ch_reg = ad3531r_input_ch_reg, @@ -533,12 +544,6 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) return 0; } -static const struct regmap_config ad3530r_regmap_config = { - .reg_bits = 16, - .val_bits = 8, - .max_register = AD3530R_MAX_REG_ADDR, -}; - static const struct iio_info ad3530r_info = { .read_raw = ad3530r_read_raw, .write_raw = ad3530r_write_raw, @@ -559,7 +564,11 @@ static int ad3530r_probe(struct spi_device *spi) st = iio_priv(indio_dev); - st->regmap = devm_regmap_init_spi(spi, &ad3530r_regmap_config); + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -ENODEV; + + st->regmap = devm_regmap_init_spi(spi, st->chip_info->regmap_config); if (IS_ERR(st->regmap)) return dev_err_probe(dev, PTR_ERR(st->regmap), "Failed to init regmap"); @@ -568,10 +577,6 @@ static int ad3530r_probe(struct spi_device *spi) if (ret) return ret; - st->chip_info = spi_get_device_match_data(spi); - if (!st->chip_info) - return -ENODEV; - ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulators), regulators); if (ret) From 97e5f68ca4acc9cec2827e2aacce340096bc5e8e Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Fri, 3 Jul 2026 18:10:11 +0800 Subject: [PATCH 137/513] iio: dac: ad3530r: Add support for AD3532R/AD3532 The AD3532R/AD3532 is a 16-channel, 16-bit voltage output DAC. It shares similar functionality with AD3530R but splits its registers into two banks: bank 0 at 0x10xx for channels 0-7 and bank 1 at 0x30xx for channels 8-15. The input, LDAC trigger and operating-mode registers are therefore selected per bank. Add the AD3532R register map, channel specs, per-bank register arrays, a dedicated powerdown handler and its own regmap_config, reusing the table-driven helpers for the shared configuration steps. Signed-off-by: Kim Seer Paller Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 7 +- drivers/iio/dac/ad3530r.c | 189 +++++++++++++++++++++++++++++++++++++- 2 files changed, 192 insertions(+), 4 deletions(-) diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index ebf7144f922a..17529509da9d 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -11,8 +11,11 @@ config AD3530R depends on SPI select REGMAP_SPI help - Say yes here to build support for Analog Devices AD3530R, AD3531R - Digital to Analog Converter. + Say yes here to build support for the following Analog Devices + Digital to Analog Converters: + - AD3530/AD3530R (8-channel) + - AD3531/AD3531R (4-channel) + - AD3532/AD3532R (16-channel) To compile this driver as a module, choose M here: the module will be called ad3530r. diff --git a/drivers/iio/dac/ad3530r.c b/drivers/iio/dac/ad3530r.c index be63eb819c93..23426b20f9aa 100644 --- a/drivers/iio/dac/ad3530r.c +++ b/drivers/iio/dac/ad3530r.c @@ -2,6 +2,7 @@ /* * AD3530R/AD3530 8-channel, 16-bit Voltage Output DAC Driver * AD3531R/AD3531 4-channel, 16-bit Voltage Output DAC Driver + * AD3532R/AD3532 16-channel, 16-bit Voltage Output DAC Driver * * Copyright 2025 Analog Devices Inc. */ @@ -39,6 +40,25 @@ #define AD3531R_SW_LDAC_TRIG_A 0xDD #define AD3531R_INPUT_CH 0xE3 +/* AD3532R/AD3532 bank 0 registers (channels 0-7) */ +#define AD3532R_INTERFACE_CONFIG_A_0 0x1000 +#define AD3532R_OUTPUT_OPERATING_MODE_0 0x1020 +#define AD3532R_OUTPUT_OPERATING_MODE_1 0x1021 +#define AD3532R_OUTPUT_CONTROL_0 0x102A +#define AD3532R_REFERENCE_CONTROL_0 0x103C +#define AD3532R_SW_LDAC_TRIG_0 0x10E5 +#define AD3532R_INPUT_CH_0 0x10EB + +/* AD3532R/AD3532 bank 1 registers (channels 8-15) */ +#define AD3532R_INTERFACE_CONFIG_A_1 0x3000 +#define AD3532R_OUTPUT_OPERATING_MODE_2 0x3020 +#define AD3532R_OUTPUT_OPERATING_MODE_3 0x3021 +#define AD3532R_OUTPUT_CONTROL_1 0x302A +#define AD3532R_REFERENCE_CONTROL_1 0x303C +#define AD3532R_SW_LDAC_TRIG_1 0x30E5 +#define AD3532R_INPUT_CH_1 0x30EB +#define AD3532R_MAX_REG_ADDR 0x30F9 + #define AD3530R_SLD_TRIG_A BIT(7) #define AD3530R_OUTPUT_CONTROL_RANGE BIT(2) #define AD3530R_REFERENCE_CONTROL_SEL BIT(0) @@ -50,8 +70,10 @@ #define AD3530R_LDAC_PULSE_US 100 #define AD3530R_DAC_MAX_VAL GENMASK(15, 0) -#define AD3530R_MAX_CHANNELS 8 +#define AD3530R_CH_PER_REG 4 +#define AD3530R_CH_PER_BANK 8 #define AD3531R_MAX_CHANNELS 4 +#define AD3532R_MAX_CHANNELS 16 enum ad3530r_mode { AD3530R_NORMAL_OP, @@ -85,7 +107,7 @@ struct ad3530r_state { struct regmap *regmap; /* lock to protect against multiple access to the device and shared data */ struct mutex lock; - struct ad3530r_chan chan[AD3530R_MAX_CHANNELS]; + struct ad3530r_chan chan[AD3532R_MAX_CHANNELS]; const struct ad3530r_chip_info *chip_info; struct gpio_desc *ldac_gpio; int vref_mV; @@ -106,6 +128,14 @@ static int ad3531r_input_ch_reg(unsigned int channel) return 2 * channel + AD3531R_INPUT_CH; } +static int ad3532r_input_ch_reg(unsigned int channel) +{ + unsigned int bank = channel / AD3530R_CH_PER_BANK; + unsigned int local_ch = channel % AD3530R_CH_PER_BANK; + + return 2 * local_ch + (bank ? AD3532R_INPUT_CH_1 : AD3532R_INPUT_CH_0); +} + static const char * const ad3530r_powerdown_modes[] = { "1kohm_to_gnd", "7.7kohm_to_gnd", @@ -118,6 +148,12 @@ static const char * const ad3531r_powerdown_modes[] = { "16kohm_to_gnd", }; +static const char * const ad3532r_powerdown_modes[] = { + "1kohm_to_gnd", + "10kohm_to_gnd", + "three_state", +}; + static int ad3530r_get_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { @@ -153,6 +189,13 @@ static const struct iio_enum ad3531r_powerdown_mode_enum = { .set = ad3530r_set_powerdown_mode, }; +static const struct iio_enum ad3532r_powerdown_mode_enum = { + .items = ad3532r_powerdown_modes, + .num_items = ARRAY_SIZE(ad3532r_powerdown_modes), + .get = ad3530r_get_powerdown_mode, + .set = ad3530r_set_powerdown_mode, +}; + static ssize_t ad3530r_get_dac_powerdown(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, @@ -197,6 +240,45 @@ static ssize_t ad3530r_set_dac_powerdown(struct iio_dev *indio_dev, return len; } +static ssize_t ad3532r_set_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad3530r_state *st = iio_priv(indio_dev); + unsigned int bank, local_ch, reg_in_bank, ch_in_reg; + unsigned int reg, mask, val; + bool powerdown; + int ret; + + ret = kstrtobool(buf, &powerdown); + if (ret) + return ret; + + bank = chan->channel / AD3530R_CH_PER_BANK; + local_ch = chan->channel % AD3530R_CH_PER_BANK; + reg_in_bank = local_ch / AD3530R_CH_PER_REG; + ch_in_reg = local_ch % AD3530R_CH_PER_REG; + + reg = reg_in_bank + (bank ? AD3532R_OUTPUT_OPERATING_MODE_2 : + AD3532R_OUTPUT_OPERATING_MODE_0); + mask = AD3530R_OP_MODE_CHAN_MSK(ch_in_reg); + + guard(mutex)(&st->lock); + if (powerdown) { + val = field_prep(mask, st->chan[chan->channel].powerdown_mode); + ret = regmap_update_bits(st->regmap, reg, mask, val); + } else { + ret = regmap_clear_bits(st->regmap, reg, mask); + } + if (ret) + return ret; + + st->chan[chan->channel].powerdown = powerdown; + + return len; +} + static int ad3530r_trigger_sw_ldac_reg(unsigned int channel) { return AD3530R_SW_LDAC_TRIG_A; @@ -207,6 +289,13 @@ static int ad3531r_trigger_sw_ldac_reg(unsigned int channel) return AD3531R_SW_LDAC_TRIG_A; } +static int ad3532r_trigger_sw_ldac_reg(unsigned int channel) +{ + unsigned int bank = channel / AD3530R_CH_PER_BANK; + + return bank ? AD3532R_SW_LDAC_TRIG_1 : AD3532R_SW_LDAC_TRIG_0; +} + static int ad3530r_trigger_hw_ldac(struct gpio_desc *ldac_gpio) { gpiod_set_value_cansleep(ldac_gpio, 1); @@ -319,6 +408,19 @@ static const struct iio_chan_spec_ext_info ad3531r_ext_info[] = { { } }; +static const struct iio_chan_spec_ext_info ad3532r_ext_info[] = { + { + .name = "powerdown", + .shared = IIO_SEPARATE, + .read = ad3530r_get_dac_powerdown, + .write = ad3532r_set_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad3532r_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, + &ad3532r_powerdown_mode_enum), + { } +}; + #define AD3530R_CHAN(_chan, _ext_info) \ { \ .type = IIO_VOLTAGE, \ @@ -348,6 +450,25 @@ static const struct iio_chan_spec ad3531r_channels[] = { AD3530R_CHAN(3, ad3531r_ext_info), }; +static const struct iio_chan_spec ad3532r_channels[] = { + AD3530R_CHAN(0, ad3532r_ext_info), + AD3530R_CHAN(1, ad3532r_ext_info), + AD3530R_CHAN(2, ad3532r_ext_info), + AD3530R_CHAN(3, ad3532r_ext_info), + AD3530R_CHAN(4, ad3532r_ext_info), + AD3530R_CHAN(5, ad3532r_ext_info), + AD3530R_CHAN(6, ad3532r_ext_info), + AD3530R_CHAN(7, ad3532r_ext_info), + AD3530R_CHAN(8, ad3532r_ext_info), + AD3530R_CHAN(9, ad3532r_ext_info), + AD3530R_CHAN(10, ad3532r_ext_info), + AD3530R_CHAN(11, ad3532r_ext_info), + AD3530R_CHAN(12, ad3532r_ext_info), + AD3530R_CHAN(13, ad3532r_ext_info), + AD3530R_CHAN(14, ad3532r_ext_info), + AD3530R_CHAN(15, ad3532r_ext_info), +}; + static const unsigned int ad3530r_if_config[] = { AD3530R_INTERFACE_CONFIG_A, }; @@ -369,12 +490,40 @@ static const unsigned int ad3531r_op_mode[] = { AD3530R_OUTPUT_OPERATING_MODE_0, }; +static const unsigned int ad3532r_if_config[] = { + AD3532R_INTERFACE_CONFIG_A_0, + AD3532R_INTERFACE_CONFIG_A_1, +}; + +static const unsigned int ad3532r_out_ctrl[] = { + AD3532R_OUTPUT_CONTROL_0, + AD3532R_OUTPUT_CONTROL_1, +}; + +static const unsigned int ad3532r_ref_ctrl[] = { + AD3532R_REFERENCE_CONTROL_0, + AD3532R_REFERENCE_CONTROL_1, +}; + +static const unsigned int ad3532r_op_mode[] = { + AD3532R_OUTPUT_OPERATING_MODE_0, + AD3532R_OUTPUT_OPERATING_MODE_1, + AD3532R_OUTPUT_OPERATING_MODE_2, + AD3532R_OUTPUT_OPERATING_MODE_3, +}; + static const struct regmap_config ad3530r_regmap_config = { .reg_bits = 16, .val_bits = 8, .max_register = AD3530R_MAX_REG_ADDR, }; +static const struct regmap_config ad3532r_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = AD3532R_MAX_REG_ADDR, +}; + static const struct ad3530r_chip_info ad3530_chip = { .name = "ad3530", .channels = ad3530r_channels, @@ -439,6 +588,38 @@ static const struct ad3530r_chip_info ad3531r_chip = { .internal_ref_support = true, }; +static const struct ad3530r_chip_info ad3532_chip = { + .name = "ad3532", + .channels = ad3532r_channels, + .regmap_config = &ad3532r_regmap_config, + .num_channels = ARRAY_SIZE(ad3532r_channels), + .sw_ldac_trig_reg = ad3532r_trigger_sw_ldac_reg, + .input_ch_reg = ad3532r_input_ch_reg, + .interface_config_a = ad3532r_if_config, + .output_control = ad3532r_out_ctrl, + .reference_control = ad3532r_ref_ctrl, + .op_mode = ad3532r_op_mode, + .num_banks = ARRAY_SIZE(ad3532r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3532r_op_mode), + .internal_ref_support = false, +}; + +static const struct ad3530r_chip_info ad3532r_chip = { + .name = "ad3532r", + .channels = ad3532r_channels, + .regmap_config = &ad3532r_regmap_config, + .num_channels = ARRAY_SIZE(ad3532r_channels), + .sw_ldac_trig_reg = ad3532r_trigger_sw_ldac_reg, + .input_ch_reg = ad3532r_input_ch_reg, + .interface_config_a = ad3532r_if_config, + .output_control = ad3532r_out_ctrl, + .reference_control = ad3532r_ref_ctrl, + .op_mode = ad3532r_op_mode, + .num_banks = ARRAY_SIZE(ad3532r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3532r_op_mode), + .internal_ref_support = true, +}; + static int ad3530r_set_reg_bank_bits(const struct ad3530r_state *st, const unsigned int *regs, unsigned int num_regs, @@ -610,6 +791,8 @@ static const struct spi_device_id ad3530r_id[] = { { .name = "ad3530r", .driver_data = (kernel_ulong_t)&ad3530r_chip }, { .name = "ad3531", .driver_data = (kernel_ulong_t)&ad3531_chip }, { .name = "ad3531r", .driver_data = (kernel_ulong_t)&ad3531r_chip }, + { .name = "ad3532", .driver_data = (kernel_ulong_t)&ad3532_chip }, + { .name = "ad3532r", .driver_data = (kernel_ulong_t)&ad3532r_chip }, { } }; MODULE_DEVICE_TABLE(spi, ad3530r_id); @@ -619,6 +802,8 @@ static const struct of_device_id ad3530r_of_match[] = { { .compatible = "adi,ad3530r", .data = &ad3530r_chip }, { .compatible = "adi,ad3531", .data = &ad3531_chip }, { .compatible = "adi,ad3531r", .data = &ad3531r_chip }, + { .compatible = "adi,ad3532", .data = &ad3532_chip }, + { .compatible = "adi,ad3532r", .data = &ad3532r_chip }, { } }; MODULE_DEVICE_TABLE(of, ad3530r_of_match); From 790a2080e0c9eabefea3561dec301172043de236 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 23:16:33 +0800 Subject: [PATCH 138/513] iio: adc: max1241: add missing MODULE_DEVICE_TABLE() The driver has a match table for the spi bus wired into its driver structure, but the table is not exported with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE() entry so module alias information is generated for automatic module loading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the driver registration structure, and the missing module alias publication. Signed-off-by: Pengpeng Hou Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1241.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/max1241.c b/drivers/iio/adc/max1241.c index fb7f53316e3d..b3cecbf9a89e 100644 --- a/drivers/iio/adc/max1241.c +++ b/drivers/iio/adc/max1241.c @@ -175,6 +175,7 @@ static const struct spi_device_id max1241_id[] = { { .name = "max1241" }, { } }; +MODULE_DEVICE_TABLE(spi, max1241_id); static const struct of_device_id max1241_dt_ids[] = { { .compatible = "maxim,max1241" }, From 87359c88846facff35e91608ccd531592e91ba0b Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 20:42:24 +0800 Subject: [PATCH 139/513] iio: imu: bmi270: add missing MODULE_DEVICE_TABLE() The driver has an OF match table wired to .of_match_table, but does not export the table with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE(of, ...) entry so module alias information is generated for OF based module autoloading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the platform driver, and the missing module alias publication. Signed-off-by: Pengpeng Hou Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/imu/bmi270/bmi270_spi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/imu/bmi270/bmi270_spi.c b/drivers/iio/imu/bmi270/bmi270_spi.c index 8f55e57584c7..eec51c04d58f 100644 --- a/drivers/iio/imu/bmi270/bmi270_spi.c +++ b/drivers/iio/imu/bmi270/bmi270_spi.c @@ -76,6 +76,7 @@ static const struct of_device_id bmi270_of_match[] = { { .compatible = "bosch,bmi270", .data = &bmi270_chip_info }, { } }; +MODULE_DEVICE_TABLE(of, bmi270_of_match); static struct spi_driver bmi270_spi_driver = { .driver = { From ebf1d03dab9659749d5605656cf92fb6700f33bb Mon Sep 17 00:00:00 2001 From: Romain Gantois Date: Tue, 9 Dec 2025 09:25:56 +0100 Subject: [PATCH 140/513] iio: inkern: Use namespaced exports Use namespaced exports for IIO consumer API functions. This will make it easier to manage the IIO export surface. Consumer drivers will only be provided access to a specific set of functions, thereby restricting usage of internal IIO functions by other parts of the kernel. This change cannot be split into several parts without breaking bisectability, thus all of the affected drivers are modified at once. Acked-by: Sebastian Reichel # for power-supply Acked-by: Guenter Roeck Reviewed-by: Andy Shevchenko Signed-off-by: Romain Gantois Acked-by: Dmitry Torokhov Acked-by: Vinod Koul Signed-off-by: Jonathan Cameron --- drivers/extcon/extcon-adc-jack.c | 1 + drivers/hwmon/iio_hwmon.c | 1 + drivers/hwmon/ntc_thermistor.c | 1 + drivers/iio/adc/envelope-detector.c | 1 + drivers/iio/afe/iio-rescale.c | 1 + drivers/iio/buffer/industrialio-buffer-cb.c | 1 + drivers/iio/buffer/industrialio-hw-consumer.c | 1 + drivers/iio/dac/ad8460.c | 1 + drivers/iio/dac/dpot-dac.c | 1 + drivers/iio/inkern.c | 54 +++++++++---------- drivers/iio/light/cm3605.c | 1 + drivers/iio/light/gp2ap002.c | 1 + drivers/iio/multiplexer/iio-mux.c | 1 + drivers/iio/potentiostat/lmp91000.c | 1 + drivers/input/joystick/adc-joystick.c | 1 + drivers/input/keyboard/adc-keys.c | 1 + drivers/input/touchscreen/colibri-vf50-ts.c | 1 + .../input/touchscreen/resistive-adc-touch.c | 1 + drivers/phy/motorola/phy-cpcap-usb.c | 1 + drivers/power/supply/ab8500_btemp.c | 1 + drivers/power/supply/ab8500_charger.c | 1 + drivers/power/supply/ab8500_fg.c | 1 + drivers/power/supply/axp20x_ac_power.c | 1 + drivers/power/supply/axp20x_battery.c | 1 + drivers/power/supply/axp20x_usb_power.c | 1 + drivers/power/supply/axp288_fuel_gauge.c | 1 + drivers/power/supply/cpcap-battery.c | 1 + drivers/power/supply/cpcap-charger.c | 1 + drivers/power/supply/da9150-charger.c | 1 + drivers/power/supply/generic-adc-battery.c | 1 + drivers/power/supply/ingenic-battery.c | 1 + drivers/power/supply/intel_dc_ti_battery.c | 1 + drivers/power/supply/lego_ev3_battery.c | 1 + drivers/power/supply/lp8788-charger.c | 1 + drivers/power/supply/max17040_battery.c | 1 + drivers/power/supply/mp2629_charger.c | 1 + drivers/power/supply/mt6370-charger.c | 1 + drivers/power/supply/qcom_smbx.c | 1 + drivers/power/supply/rn5t618_power.c | 1 + drivers/power/supply/rx51_battery.c | 1 + drivers/power/supply/sc27xx_fuel_gauge.c | 1 + drivers/power/supply/twl4030_charger.c | 1 + drivers/power/supply/twl4030_madc_battery.c | 1 + drivers/power/supply/twl6030_charger.c | 1 + drivers/thermal/qcom/qcom-spmi-adc-tm5.c | 1 + drivers/thermal/qcom/qcom-spmi-temp-alarm.c | 1 + drivers/thermal/renesas/rzg3s_thermal.c | 1 + drivers/thermal/thermal-generic-adc.c | 1 + sound/soc/codecs/audio-iio-aux.c | 1 + sound/soc/samsung/aries_wm8994.c | 1 + sound/soc/samsung/midas_wm1811.c | 1 + sound/soc/stm/stm32_adfsdm.c | 1 + 52 files changed, 78 insertions(+), 27 deletions(-) diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index 7e3c9f38297b..e735f43dcdeb 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -210,3 +210,4 @@ module_platform_driver(adc_jack_driver); MODULE_AUTHOR("MyungJoo Ham "); MODULE_DESCRIPTION("ADC Jack extcon driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index e376d4cde5ad..4c7843fbcc50 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -222,3 +222,4 @@ module_platform_driver(iio_hwmon_driver); MODULE_AUTHOR("Jonathan Cameron "); MODULE_DESCRIPTION("IIO to hwmon driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index 6f82a6c49393..a6cd879a50d1 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -749,3 +749,4 @@ MODULE_DESCRIPTION("NTC Thermistor Driver"); MODULE_AUTHOR("MyungJoo Ham "); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:ntc-thermistor"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c index 5b16fe737659..fea20e7e6cd9 100644 --- a/drivers/iio/adc/envelope-detector.c +++ b/drivers/iio/adc/envelope-detector.c @@ -406,3 +406,4 @@ module_platform_driver(envelope_detector_driver); MODULE_DESCRIPTION("Envelope detector using a DAC and a comparator"); MODULE_AUTHOR("Peter Rosin "); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c index ecaf59278c6f..d7f55109af3e 100644 --- a/drivers/iio/afe/iio-rescale.c +++ b/drivers/iio/afe/iio-rescale.c @@ -609,3 +609,4 @@ module_platform_driver(rescale_driver); MODULE_DESCRIPTION("IIO rescale driver"); MODULE_AUTHOR("Peter Rosin "); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c index 0c266c216525..b58263bcf3cb 100644 --- a/drivers/iio/buffer/industrialio-buffer-cb.c +++ b/drivers/iio/buffer/industrialio-buffer-cb.c @@ -154,3 +154,4 @@ EXPORT_SYMBOL_GPL(iio_channel_cb_get_iio_dev); MODULE_AUTHOR("Jonathan Cameron "); MODULE_DESCRIPTION("Industrial I/O callback buffer"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c index 10e912bbf0c5..d035aa33b357 100644 --- a/drivers/iio/buffer/industrialio-hw-consumer.c +++ b/drivers/iio/buffer/industrialio-hw-consumer.c @@ -216,3 +216,4 @@ EXPORT_SYMBOL_GPL(iio_hw_consumer_disable); MODULE_AUTHOR("Lars-Peter Clausen "); MODULE_DESCRIPTION("Hardware consumer buffer the IIO framework"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/dac/ad8460.c b/drivers/iio/dac/ad8460.c index 6e45686902dd..ad654819ca22 100644 --- a/drivers/iio/dac/ad8460.c +++ b/drivers/iio/dac/ad8460.c @@ -955,3 +955,4 @@ MODULE_AUTHOR("Mariel Tinaco "); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 9ce20cb05a9b..fb08e4f02520 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -281,7 +281,7 @@ struct iio_channel *fwnode_iio_channel_get_by_name(struct fwnode_handle *fwnode, return ERR_PTR(-ENODEV); } -EXPORT_SYMBOL_GPL(fwnode_iio_channel_get_by_name); +EXPORT_SYMBOL_NS_GPL(fwnode_iio_channel_get_by_name, "IIO_CONSUMER"); static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev) { @@ -386,7 +386,7 @@ struct iio_channel *iio_channel_get(struct device *dev, return iio_channel_get_sys(name, channel_name); } -EXPORT_SYMBOL_GPL(iio_channel_get); +EXPORT_SYMBOL_NS_GPL(iio_channel_get, "IIO_CONSUMER"); void iio_channel_release(struct iio_channel *channel) { @@ -395,7 +395,7 @@ void iio_channel_release(struct iio_channel *channel) iio_device_put(channel->indio_dev); kfree(channel); } -EXPORT_SYMBOL_GPL(iio_channel_release); +EXPORT_SYMBOL_NS_GPL(iio_channel_release, "IIO_CONSUMER"); static void devm_iio_channel_free(void *iio_channel) { @@ -418,7 +418,7 @@ struct iio_channel *devm_iio_channel_get(struct device *dev, return channel; } -EXPORT_SYMBOL_GPL(devm_iio_channel_get); +EXPORT_SYMBOL_NS_GPL(devm_iio_channel_get, "IIO_CONSUMER"); struct iio_channel *devm_fwnode_iio_channel_get_by_name(struct device *dev, struct fwnode_handle *fwnode, @@ -437,7 +437,7 @@ struct iio_channel *devm_fwnode_iio_channel_get_by_name(struct device *dev, return channel; } -EXPORT_SYMBOL_GPL(devm_fwnode_iio_channel_get_by_name); +EXPORT_SYMBOL_NS_GPL(devm_fwnode_iio_channel_get_by_name, "IIO_CONSUMER"); struct iio_channel *iio_channel_get_all(struct device *dev) { @@ -506,7 +506,7 @@ struct iio_channel *iio_channel_get_all(struct device *dev) iio_device_put(chans[i].indio_dev); return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(iio_channel_get_all); +EXPORT_SYMBOL_NS_GPL(iio_channel_get_all, "IIO_CONSUMER"); void iio_channel_release_all(struct iio_channel *channels) { @@ -518,7 +518,7 @@ void iio_channel_release_all(struct iio_channel *channels) } kfree(channels); } -EXPORT_SYMBOL_GPL(iio_channel_release_all); +EXPORT_SYMBOL_NS_GPL(iio_channel_release_all, "IIO_CONSUMER"); static void devm_iio_channel_free_all(void *iio_channels) { @@ -541,7 +541,7 @@ struct iio_channel *devm_iio_channel_get_all(struct device *dev) return channels; } -EXPORT_SYMBOL_GPL(devm_iio_channel_get_all); +EXPORT_SYMBOL_NS_GPL(devm_iio_channel_get_all, "IIO_CONSUMER"); static int iio_channel_read(struct iio_channel *chan, int *val, int *val2, enum iio_chan_info_enum info) @@ -585,7 +585,7 @@ int iio_read_channel_raw(struct iio_channel *chan, int *val) return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW); } -EXPORT_SYMBOL_GPL(iio_read_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_raw, "IIO_CONSUMER"); int iio_read_channel_average_raw(struct iio_channel *chan, int *val) { @@ -597,7 +597,7 @@ int iio_read_channel_average_raw(struct iio_channel *chan, int *val) return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW); } -EXPORT_SYMBOL_GPL(iio_read_channel_average_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_average_raw, "IIO_CONSUMER"); int iio_multiply_value(int *result, s64 multiplier, unsigned int type, int val, int val2) @@ -701,7 +701,7 @@ int iio_convert_raw_to_processed(struct iio_channel *chan, int raw, return iio_convert_raw_to_processed_unlocked(chan, raw, processed, scale); } -EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed); +EXPORT_SYMBOL_NS_GPL(iio_convert_raw_to_processed, "IIO_CONSUMER"); int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2, enum iio_chan_info_enum attribute) @@ -714,13 +714,13 @@ int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2, return iio_channel_read(chan, val, val2, attribute); } -EXPORT_SYMBOL_GPL(iio_read_channel_attribute); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_attribute, "IIO_CONSUMER"); int iio_read_channel_offset(struct iio_channel *chan, int *val, int *val2) { return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_OFFSET); } -EXPORT_SYMBOL_GPL(iio_read_channel_offset); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_offset, "IIO_CONSUMER"); int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, unsigned int scale) @@ -752,20 +752,20 @@ int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, scale); } } -EXPORT_SYMBOL_GPL(iio_read_channel_processed_scale); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_processed_scale, "IIO_CONSUMER"); int iio_read_channel_processed(struct iio_channel *chan, int *val) { /* This is just a special case with scale factor 1 */ return iio_read_channel_processed_scale(chan, val, 1); } -EXPORT_SYMBOL_GPL(iio_read_channel_processed); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_processed, "IIO_CONSUMER"); int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2) { return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_SCALE); } -EXPORT_SYMBOL_GPL(iio_read_channel_scale); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_scale, "IIO_CONSUMER"); static int iio_channel_read_avail(struct iio_channel *chan, const int **vals, int *type, int *length, @@ -794,7 +794,7 @@ int iio_read_avail_channel_attribute(struct iio_channel *chan, return iio_channel_read_avail(chan, vals, type, length, attribute); } -EXPORT_SYMBOL_GPL(iio_read_avail_channel_attribute); +EXPORT_SYMBOL_NS_GPL(iio_read_avail_channel_attribute, "IIO_CONSUMER"); int iio_read_avail_channel_raw(struct iio_channel *chan, const int **vals, int *length) @@ -811,7 +811,7 @@ int iio_read_avail_channel_raw(struct iio_channel *chan, return ret; } -EXPORT_SYMBOL_GPL(iio_read_avail_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_avail_channel_raw, "IIO_CONSUMER"); static int iio_channel_read_max(struct iio_channel *chan, int *val, int *val2, int *type, @@ -867,7 +867,7 @@ int iio_read_max_channel_raw(struct iio_channel *chan, int *val) return iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); } -EXPORT_SYMBOL_GPL(iio_read_max_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_max_channel_raw, "IIO_CONSUMER"); static int iio_channel_read_min(struct iio_channel *chan, int *val, int *val2, int *type, @@ -923,7 +923,7 @@ int iio_read_min_channel_raw(struct iio_channel *chan, int *val) return iio_channel_read_min(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); } -EXPORT_SYMBOL_GPL(iio_read_min_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_min_channel_raw, "IIO_CONSUMER"); int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) { @@ -937,7 +937,7 @@ int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) return 0; } -EXPORT_SYMBOL_GPL(iio_get_channel_type); +EXPORT_SYMBOL_NS_GPL(iio_get_channel_type, "IIO_CONSUMER"); static int iio_channel_write(struct iio_channel *chan, int val, int val2, enum iio_chan_info_enum info) @@ -961,13 +961,13 @@ int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2, return iio_channel_write(chan, val, val2, attribute); } -EXPORT_SYMBOL_GPL(iio_write_channel_attribute); +EXPORT_SYMBOL_NS_GPL(iio_write_channel_attribute, "IIO_CONSUMER"); int iio_write_channel_raw(struct iio_channel *chan, int val) { return iio_write_channel_attribute(chan, val, 0, IIO_CHAN_INFO_RAW); } -EXPORT_SYMBOL_GPL(iio_write_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_write_channel_raw, "IIO_CONSUMER"); unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan) { @@ -982,7 +982,7 @@ unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan) return i; } -EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count); +EXPORT_SYMBOL_NS_GPL(iio_get_channel_ext_info_count, "IIO_CONSUMER"); static const struct iio_chan_spec_ext_info * iio_lookup_ext_info(const struct iio_channel *chan, const char *attr) @@ -1017,7 +1017,7 @@ ssize_t iio_read_channel_ext_info(struct iio_channel *chan, return ext_info->read(chan->indio_dev, ext_info->private, chan->channel, buf); } -EXPORT_SYMBOL_GPL(iio_read_channel_ext_info); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_ext_info, "IIO_CONSUMER"); ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr, const char *buf, size_t len) @@ -1031,7 +1031,7 @@ ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr, return ext_info->write(chan->indio_dev, ext_info->private, chan->channel, buf, len); } -EXPORT_SYMBOL_GPL(iio_write_channel_ext_info); +EXPORT_SYMBOL_NS_GPL(iio_write_channel_ext_info, "IIO_CONSUMER"); ssize_t iio_read_channel_label(struct iio_channel *chan, char *buf) { @@ -1042,4 +1042,4 @@ ssize_t iio_read_channel_label(struct iio_channel *chan, char *buf) return do_iio_read_channel_label(chan->indio_dev, chan->channel, buf); } -EXPORT_SYMBOL_GPL(iio_read_channel_label); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_label, "IIO_CONSUMER"); diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c index 0c17378e27d1..1bd11292d005 100644 --- a/drivers/iio/light/cm3605.c +++ b/drivers/iio/light/cm3605.c @@ -325,3 +325,4 @@ module_platform_driver(cm3605_driver); MODULE_AUTHOR("Linus Walleij "); MODULE_DESCRIPTION("CM3605 ambient light and proximity sensor driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index c83f67ff2464..6c532898f6e4 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -717,3 +717,4 @@ module_i2c_driver(gp2ap002_driver); MODULE_AUTHOR("Linus Walleij "); MODULE_DESCRIPTION("GP2AP002 ambient light and proximity sensor driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/multiplexer/iio-mux.c b/drivers/iio/multiplexer/iio-mux.c index b742ca9a99d1..e193913f5af7 100644 --- a/drivers/iio/multiplexer/iio-mux.c +++ b/drivers/iio/multiplexer/iio-mux.c @@ -464,3 +464,4 @@ module_platform_driver(mux_driver); MODULE_DESCRIPTION("IIO multiplexer driver"); MODULE_AUTHOR("Peter Rosin "); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c index 359dffa47091..80c901a42722 100644 --- a/drivers/iio/potentiostat/lmp91000.c +++ b/drivers/iio/potentiostat/lmp91000.c @@ -423,3 +423,4 @@ module_i2c_driver(lmp91000_driver); MODULE_AUTHOR("Matt Ranostay "); MODULE_DESCRIPTION("LMP91000 digital potentiostat"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/input/joystick/adc-joystick.c b/drivers/input/joystick/adc-joystick.c index ff44f9978b71..4fa42f88bcfa 100644 --- a/drivers/input/joystick/adc-joystick.c +++ b/drivers/input/joystick/adc-joystick.c @@ -329,3 +329,4 @@ module_platform_driver(adc_joystick_driver); MODULE_DESCRIPTION("Input driver for joysticks connected over ADC"); MODULE_AUTHOR("Artur Rojek "); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/input/keyboard/adc-keys.c b/drivers/input/keyboard/adc-keys.c index f1753207429d..d687459a0c80 100644 --- a/drivers/input/keyboard/adc-keys.c +++ b/drivers/input/keyboard/adc-keys.c @@ -202,3 +202,4 @@ module_platform_driver(adc_keys_driver); MODULE_AUTHOR("Alexandre Belloni "); MODULE_DESCRIPTION("Input driver for resistor ladder connected on ADC"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/input/touchscreen/colibri-vf50-ts.c b/drivers/input/touchscreen/colibri-vf50-ts.c index 98d5b2ba63fb..89c4d7b2b89e 100644 --- a/drivers/input/touchscreen/colibri-vf50-ts.c +++ b/drivers/input/touchscreen/colibri-vf50-ts.c @@ -372,3 +372,4 @@ module_platform_driver(vf50_touch_driver); MODULE_AUTHOR("Sanchayan Maity"); MODULE_DESCRIPTION("Colibri VF50 Touchscreen driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/input/touchscreen/resistive-adc-touch.c b/drivers/input/touchscreen/resistive-adc-touch.c index 7e761ec73273..2fefd652864c 100644 --- a/drivers/input/touchscreen/resistive-adc-touch.c +++ b/drivers/input/touchscreen/resistive-adc-touch.c @@ -301,3 +301,4 @@ module_platform_driver(grts_driver); MODULE_AUTHOR("Eugen Hristev "); MODULE_DESCRIPTION("Generic ADC Resistive Touch Driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/phy/motorola/phy-cpcap-usb.c b/drivers/phy/motorola/phy-cpcap-usb.c index 7cb020dd3423..9591672b0511 100644 --- a/drivers/phy/motorola/phy-cpcap-usb.c +++ b/drivers/phy/motorola/phy-cpcap-usb.c @@ -717,3 +717,4 @@ MODULE_ALIAS("platform:cpcap_usb"); MODULE_AUTHOR("Tony Lindgren "); MODULE_DESCRIPTION("CPCAP usb phy driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index e5202a7b6209..36b0c52a4b8b 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -829,3 +829,4 @@ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy"); MODULE_ALIAS("platform:ab8500-btemp"); MODULE_DESCRIPTION("AB8500 battery temperature driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index 1813fbdfa1c1..5c3af3571013 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -3751,3 +3751,4 @@ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy"); MODULE_ALIAS("platform:ab8500-charger"); MODULE_DESCRIPTION("AB8500 charger management driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index eb5c1ae68e44..3ab33cf465c6 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -3252,3 +3252,4 @@ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Johan Palsson, Karl Komierowski"); MODULE_ALIAS("platform:ab8500-fg"); MODULE_DESCRIPTION("AB8500 Fuel Gauge driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/axp20x_ac_power.c b/drivers/power/supply/axp20x_ac_power.c index 5f6ea416fa30..e9049d6229df 100644 --- a/drivers/power/supply/axp20x_ac_power.c +++ b/drivers/power/supply/axp20x_ac_power.c @@ -421,3 +421,4 @@ module_platform_driver(axp20x_ac_power_driver); MODULE_AUTHOR("Quentin Schulz "); MODULE_DESCRIPTION("AXP20X and AXP22X PMICs' AC power supply driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c index 50ca8e110085..ee8701a6e907 100644 --- a/drivers/power/supply/axp20x_battery.c +++ b/drivers/power/supply/axp20x_battery.c @@ -1155,3 +1155,4 @@ module_platform_driver(axp20x_batt_driver); MODULE_DESCRIPTION("Battery power supply driver for AXP20X and AXP22X PMICs"); MODULE_AUTHOR("Quentin Schulz "); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index e75d1e377ac1..599adcf84968 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -1080,3 +1080,4 @@ module_platform_driver(axp20x_usb_power_driver); MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("AXP20x PMIC USB power supply status driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index 5af334c0a980..05a850ec0131 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -817,3 +817,4 @@ MODULE_AUTHOR("Ramakrishna Pallala "); MODULE_AUTHOR("Todd Brandt "); MODULE_DESCRIPTION("Xpower AXP288 Fuel Gauge Driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 59c741993ef8..4d7e77d7375b 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1206,3 +1206,4 @@ module_platform_driver(cpcap_battery_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Tony Lindgren "); MODULE_DESCRIPTION("CPCAP PMIC Battery Driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c index 24221244b45b..db69020385ee 100644 --- a/drivers/power/supply/cpcap-charger.c +++ b/drivers/power/supply/cpcap-charger.c @@ -978,3 +978,4 @@ MODULE_AUTHOR("Tony Lindgren "); MODULE_DESCRIPTION("CPCAP Battery Charger Interface driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:cpcap-charger"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/da9150-charger.c b/drivers/power/supply/da9150-charger.c index 27f36ef5b88d..58449df6068c 100644 --- a/drivers/power/supply/da9150-charger.c +++ b/drivers/power/supply/da9150-charger.c @@ -644,3 +644,4 @@ module_platform_driver(da9150_charger_driver); MODULE_DESCRIPTION("Charger Driver for DA9150"); MODULE_AUTHOR("Adam Thomson "); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/generic-adc-battery.c b/drivers/power/supply/generic-adc-battery.c index f5f2566b3a32..d18c8ee40405 100644 --- a/drivers/power/supply/generic-adc-battery.c +++ b/drivers/power/supply/generic-adc-battery.c @@ -298,3 +298,4 @@ module_platform_driver(gab_driver); MODULE_AUTHOR("anish kumar "); MODULE_DESCRIPTION("generic battery driver using IIO"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/ingenic-battery.c b/drivers/power/supply/ingenic-battery.c index b111c7ce2be3..5be269f17bff 100644 --- a/drivers/power/supply/ingenic-battery.c +++ b/drivers/power/supply/ingenic-battery.c @@ -190,3 +190,4 @@ module_platform_driver(ingenic_battery_driver); MODULE_DESCRIPTION("Battery driver for Ingenic JZ47xx SoCs"); MODULE_AUTHOR("Artur Rojek "); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/intel_dc_ti_battery.c b/drivers/power/supply/intel_dc_ti_battery.c index 67a75281b0ac..cb6fa8d88b43 100644 --- a/drivers/power/supply/intel_dc_ti_battery.c +++ b/drivers/power/supply/intel_dc_ti_battery.c @@ -389,3 +389,4 @@ MODULE_ALIAS("platform:" DEV_NAME); MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("Intel Dollar Cove (TI) battery driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/lego_ev3_battery.c b/drivers/power/supply/lego_ev3_battery.c index 28454de05761..414816662b06 100644 --- a/drivers/power/supply/lego_ev3_battery.c +++ b/drivers/power/supply/lego_ev3_battery.c @@ -231,3 +231,4 @@ module_platform_driver(lego_ev3_battery_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Lechner "); MODULE_DESCRIPTION("LEGO MINDSTORMS EV3 Battery Driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c index f0a680c155c4..8c6ec98362d0 100644 --- a/drivers/power/supply/lp8788-charger.c +++ b/drivers/power/supply/lp8788-charger.c @@ -727,3 +727,4 @@ MODULE_DESCRIPTION("TI LP8788 Charger Driver"); MODULE_AUTHOR("Milo Kim"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:lp8788-charger"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index e94d53b36aa4..19a9b1bed29e 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -639,3 +639,4 @@ module_i2c_driver(max17040_i2c_driver); MODULE_AUTHOR("Minkyu Kang "); MODULE_DESCRIPTION("MAX17040 Fuel Gauge"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/mp2629_charger.c b/drivers/power/supply/mp2629_charger.c index d281c1059629..ed49f9a04c8c 100644 --- a/drivers/power/supply/mp2629_charger.c +++ b/drivers/power/supply/mp2629_charger.c @@ -660,3 +660,4 @@ module_platform_driver(mp2629_charger_driver); MODULE_AUTHOR("Saravanan Sekar "); MODULE_DESCRIPTION("MP2629 Charger driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/mt6370-charger.c b/drivers/power/supply/mt6370-charger.c index 916556baa854..8ac5f4d59f39 100644 --- a/drivers/power/supply/mt6370-charger.c +++ b/drivers/power/supply/mt6370-charger.c @@ -930,3 +930,4 @@ module_platform_driver(mt6370_chg_driver); MODULE_AUTHOR("ChiaEn Wu "); MODULE_DESCRIPTION("MediaTek MT6370 Charger Driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/qcom_smbx.c b/drivers/power/supply/qcom_smbx.c index bf2e2ccc454a..7010374b302a 100644 --- a/drivers/power/supply/qcom_smbx.c +++ b/drivers/power/supply/qcom_smbx.c @@ -1057,3 +1057,4 @@ module_platform_driver(qcom_spmi_smb); MODULE_AUTHOR("Casey Connolly "); MODULE_DESCRIPTION("Qualcomm SMB2 Charger Driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/rn5t618_power.c b/drivers/power/supply/rn5t618_power.c index 40dec55a9f73..a3f30e390c11 100644 --- a/drivers/power/supply/rn5t618_power.c +++ b/drivers/power/supply/rn5t618_power.c @@ -821,3 +821,4 @@ module_platform_driver(rn5t618_power_driver); MODULE_ALIAS("platform:rn5t618-power"); MODULE_DESCRIPTION("Power supply driver for RICOH RN5T618"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/rx51_battery.c b/drivers/power/supply/rx51_battery.c index b0220ec2d926..57266921dc8e 100644 --- a/drivers/power/supply/rx51_battery.c +++ b/drivers/power/supply/rx51_battery.c @@ -246,3 +246,4 @@ MODULE_ALIAS("platform:rx51-battery"); MODULE_AUTHOR("Pali Rohár "); MODULE_DESCRIPTION("Nokia RX-51 battery driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index a7ed9de8a289..1719ec4173e6 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -1350,3 +1350,4 @@ module_platform_driver(sc27xx_fgu_driver); MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c index 04216b2bfb6c..151f7b24e9b9 100644 --- a/drivers/power/supply/twl4030_charger.c +++ b/drivers/power/supply/twl4030_charger.c @@ -1144,3 +1144,4 @@ MODULE_AUTHOR("Gražvydas Ignotas"); MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:twl4030_bci"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/twl4030_madc_battery.c b/drivers/power/supply/twl4030_madc_battery.c index a99b3ff26929..5e04b4f0a135 100644 --- a/drivers/power/supply/twl4030_madc_battery.c +++ b/drivers/power/supply/twl4030_madc_battery.c @@ -235,3 +235,4 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Lukas Märdian "); MODULE_DESCRIPTION("twl4030_madc battery driver"); MODULE_ALIAS("platform:twl4030_madc_battery"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/twl6030_charger.c b/drivers/power/supply/twl6030_charger.c index b4ec26ff257c..82911a811f4e 100644 --- a/drivers/power/supply/twl6030_charger.c +++ b/drivers/power/supply/twl6030_charger.c @@ -579,3 +579,4 @@ module_platform_driver(twl6030_charger_driver); MODULE_DESCRIPTION("TWL6030 Battery Charger Interface driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c index d7f2e6ca92c2..bb6222c8cc5f 100644 --- a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c +++ b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c @@ -1069,3 +1069,4 @@ module_platform_driver(adc_tm5_driver); MODULE_DESCRIPTION("SPMI PMIC Thermal Monitor ADC driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c index f39ca0ddd17b..fb003ca96454 100644 --- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c @@ -904,3 +904,4 @@ module_platform_driver(qpnp_tm_driver); MODULE_ALIAS("platform:spmi-temp-alarm"); MODULE_DESCRIPTION("QPNP PMIC Temperature Alarm driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/thermal/renesas/rzg3s_thermal.c b/drivers/thermal/renesas/rzg3s_thermal.c index e25e36c99a88..7ced8f76a0ec 100644 --- a/drivers/thermal/renesas/rzg3s_thermal.c +++ b/drivers/thermal/renesas/rzg3s_thermal.c @@ -270,3 +270,4 @@ module_platform_driver(rzg3s_thermal_driver); MODULE_DESCRIPTION("Renesas RZ/G3S Thermal Sensor Unit Driver"); MODULE_AUTHOR("Claudiu Beznea "); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c index 7c844589b153..cfdb8e674dd2 100644 --- a/drivers/thermal/thermal-generic-adc.c +++ b/drivers/thermal/thermal-generic-adc.c @@ -228,3 +228,4 @@ module_platform_driver(gadc_thermal_driver); MODULE_AUTHOR("Laxman Dewangan "); MODULE_DESCRIPTION("Generic ADC thermal driver using IIO framework with DT"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/sound/soc/codecs/audio-iio-aux.c b/sound/soc/codecs/audio-iio-aux.c index 066e401912b0..4baf3005a431 100644 --- a/sound/soc/codecs/audio-iio-aux.c +++ b/sound/soc/codecs/audio-iio-aux.c @@ -312,3 +312,4 @@ module_platform_driver(audio_iio_aux_driver); MODULE_AUTHOR("Herve Codina "); MODULE_DESCRIPTION("IIO ALSA SoC aux driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c index 48ccc1d1854b..3dc96af7eb4f 100644 --- a/sound/soc/samsung/aries_wm8994.c +++ b/sound/soc/samsung/aries_wm8994.c @@ -700,3 +700,4 @@ module_platform_driver(aries_audio_driver); MODULE_DESCRIPTION("ALSA SoC ARIES WM8994"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:aries-audio-wm8994"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c index bc34dbbb50c9..d43af5069baf 100644 --- a/sound/soc/samsung/midas_wm1811.c +++ b/sound/soc/samsung/midas_wm1811.c @@ -773,3 +773,4 @@ module_platform_driver(midas_driver); MODULE_AUTHOR("Simon Shields "); MODULE_DESCRIPTION("ASoC support for Midas"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index 66efb9a0acb9..c8022f6efd8a 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -392,3 +392,4 @@ MODULE_DESCRIPTION("stm32 DFSDM DAI driver"); MODULE_AUTHOR("Arnaud Pouliquen "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" STM32_ADFSDM_DRV_NAME); +MODULE_IMPORT_NS("IIO_CONSUMER"); From 206664f22ec6303c8fbc81cd38bff8caf4e70088 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 5 Jul 2026 19:24:40 +0200 Subject: [PATCH 141/513] iio: adc: ade9000: Drop incorrect kerneldoc marker Top-level comment is not a kerneldoc, so drop marker to fix W=1 warning: drivers/iio/adc/ade9000.c:2 This comment starts with '/**', but isn't a kernel-doc comment. Refer to Documentation/doc-guide/kernel-doc.rst Signed-off-by: Krzysztof Kozlowski Reviewed-by: Maxwell Doose Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ade9000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index b80cdd8ad982..c6c3ea953fea 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * ADE9000 driver * * Copyright 2025 Analog Devices Inc. From 636deb551c2da89e798b2057d417be86ab9a3efc Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Tue, 7 Jul 2026 00:29:43 +0530 Subject: [PATCH 142/513] iio: hid-sensors: align function parenthesis for readability Adjust alignment of parentheses across HID sensor IIO drivers to improve readability and maintain consistency with kernel coding style. While updating the formatting, group related arguments consistently in multi-line function signatures where appropriate. No functional change intended. Signed-off-by: Sanjay Chitroda Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/accel/hid-sensor-accel-3d.c | 46 +++++------ .../hid-sensors/hid-sensor-attributes.c | 80 +++++++++---------- .../common/hid-sensors/hid-sensor-trigger.c | 2 +- .../common/hid-sensors/hid-sensor-trigger.h | 2 +- drivers/iio/gyro/hid-sensor-gyro-3d.c | 35 ++++---- drivers/iio/humidity/hid-sensor-humidity.c | 33 ++++---- drivers/iio/light/hid-sensor-als.c | 32 ++++---- drivers/iio/light/hid-sensor-prox.c | 29 +++---- drivers/iio/magnetometer/hid-sensor-magn-3d.c | 49 +++++------- drivers/iio/orientation/hid-sensor-incl-3d.c | 31 +++---- drivers/iio/orientation/hid-sensor-rotation.c | 24 +++--- .../position/hid-sensor-custom-intel-hinge.c | 13 +-- drivers/iio/pressure/hid-sensor-press.c | 35 ++++---- .../iio/temperature/hid-sensor-temperature.c | 30 +++---- 14 files changed, 202 insertions(+), 239 deletions(-) diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 189032c8f417..2e30c0054ff8 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -121,9 +121,8 @@ static const struct iio_chan_spec gravity_channels[] = { /* Channel read_raw handler */ static int accel_3d_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct accel_3d_state *accel_state = iio_priv(indio_dev); int report_id = -1; @@ -150,7 +149,7 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev, else { *val = 0; hid_sensor_power_state(&accel_state->common_attributes, - false); + false); return -EINVAL; } hid_sensor_power_state(&accel_state->common_attributes, false); @@ -183,10 +182,8 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int accel_3d_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct accel_3d_state *accel_state = iio_priv(indio_dev); int ret = 0; @@ -222,8 +219,7 @@ static void hid_sensor_push_data(struct iio_dev *indio_dev, void *data, /* Callback handler to send event after all samples are received and captured */ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct accel_3d_state *accel_state = iio_priv(indio_dev); @@ -246,9 +242,9 @@ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct accel_3d_state *accel_state = iio_priv(indio_dev); @@ -280,10 +276,10 @@ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int accel_3d_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, - u32 usage_id, - struct accel_3d_state *st) + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + u32 usage_id, + struct accel_3d_state *st) { int ret; @@ -302,10 +298,10 @@ static int accel_3d_parse_report(struct platform_device *pdev, }; } dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n", - st->accel[0].index, - st->accel[0].report_id, - st->accel[1].index, st->accel[1].report_id, - st->accel[2].index, st->accel[2].report_id); + st->accel[0].index, + st->accel[0].report_id, + st->accel[1].index, st->accel[1].report_id, + st->accel[2].index, st->accel[2].report_id); st->scale_precision = hid_sensor_format_scale( hsdev->usage, @@ -365,8 +361,8 @@ static int hid_accel_3d_probe(struct platform_device *pdev) return -ENOMEM; } ret = accel_3d_parse_report(pdev, hsdev, - (struct iio_chan_spec *)indio_dev->channels, - hsdev->usage, accel_state); + (struct iio_chan_spec *)indio_dev->channels, + hsdev->usage, accel_state); if (ret) { dev_err(&pdev->dev, "failed to setup attributes\n"); return ret; @@ -379,7 +375,7 @@ static int hid_accel_3d_probe(struct platform_device *pdev) atomic_set(&accel_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &accel_state->common_attributes); + &accel_state->common_attributes); if (ret < 0) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; @@ -389,7 +385,7 @@ static int hid_accel_3d_probe(struct platform_device *pdev) accel_state->callbacks.capture_sample = accel_3d_capture_sample; accel_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, hsdev->usage, - &accel_state->callbacks); + &accel_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); goto error_remove_trigger; diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index 2f0a1ea42f48..9e7c26e37880 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -72,8 +72,7 @@ static const struct { {HID_USAGE_SENSOR_HUMAN_ATTENTION, 0, 1, 0}, }; -static void simple_div(int dividend, int divisor, int *whole, - int *micro_frac) +static void simple_div(int dividend, int divisor, int *whole, int *micro_frac) { int rem; int exp = 0; @@ -111,7 +110,7 @@ for 10^-2. Negative numbers are 2's complement */ static void convert_from_vtf_format(u32 value, int size, int exp, - int *val1, int *val2) + int *val1, int *val2) { int sign = 1; @@ -176,7 +175,7 @@ s32 hid_sensor_read_poll_value(struct hid_sensor_common *st) EXPORT_SYMBOL_NS(hid_sensor_read_poll_value, "IIO_HID_ATTRIBUTES"); int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st, - int *val1, int *val2) + int *val1, int *val2) { s32 value; int ret; @@ -203,7 +202,7 @@ int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st, EXPORT_SYMBOL_NS(hid_sensor_read_samp_freq_value, "IIO_HID"); int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st, - int val1, int val2) + int val1, int val2) { s32 value; int ret; @@ -238,15 +237,15 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st, EXPORT_SYMBOL_NS(hid_sensor_write_samp_freq_value, "IIO_HID"); int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st, - int *val1, int *val2) + int *val1, int *val2) { s32 value; int ret; ret = sensor_hub_get_feature(st->hsdev, st->sensitivity.report_id, - st->sensitivity.index, sizeof(value), - &value); + st->sensitivity.index, + sizeof(value), &value); if (ret < 0 || value < 0) { *val1 = *val2 = 0; return -EINVAL; @@ -268,8 +267,8 @@ int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1, ret = sensor_hub_get_feature(st->hsdev, st->sensitivity_rel.report_id, - st->sensitivity_rel.index, sizeof(value), - &value); + st->sensitivity_rel.index, + sizeof(value), &value); if (ret < 0 || value < 0) { *val1 = *val2 = 0; return -EINVAL; @@ -283,7 +282,7 @@ int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1, EXPORT_SYMBOL_NS(hid_sensor_read_raw_hyst_rel_value, "IIO_HID"); int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, - int val1, int val2) + int val1, int val2) { s32 value; int ret; @@ -292,8 +291,8 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, return -EINVAL; value = convert_to_vtf_format(st->sensitivity.size, - st->sensitivity.unit_expo, - val1, val2); + st->sensitivity.unit_expo, + val1, val2); ret = sensor_hub_set_feature(st->hsdev, st->sensitivity.report_id, st->sensitivity.index, sizeof(value), &value); @@ -302,8 +301,8 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, ret = sensor_hub_get_feature(st->hsdev, st->sensitivity.report_id, - st->sensitivity.index, sizeof(value), - &value); + st->sensitivity.index, + sizeof(value), &value); if (ret < 0 || value < 0) return -EINVAL; @@ -323,8 +322,8 @@ int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st, return -EINVAL; value = convert_to_vtf_format(st->sensitivity_rel.size, - st->sensitivity_rel.unit_expo, - val1, val2); + st->sensitivity_rel.unit_expo, + val1, val2); ret = sensor_hub_set_feature(st->hsdev, st->sensitivity_rel.report_id, st->sensitivity_rel.index, sizeof(value), &value); @@ -333,8 +332,8 @@ int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st, ret = sensor_hub_get_feature(st->hsdev, st->sensitivity_rel.report_id, - st->sensitivity_rel.index, sizeof(value), - &value); + st->sensitivity_rel.index, + sizeof(value), &value); if (ret < 0 || value < 0) return -EINVAL; @@ -355,8 +354,8 @@ EXPORT_SYMBOL_NS(hid_sensor_write_raw_hyst_rel_value, "IIO_HID"); * 1.001745329 ->exp:4-> val0[10017]val1[453290000] * 9.806650000 ->exp:-2-> val0[0]val1[98066500] */ -static void adjust_exponent_nano(int *val0, int *val1, int scale0, - int scale1, int exp) +static void adjust_exponent_nano(int *val0, int *val1, + int scale0, int scale1, int exp) { int divisor; int i; @@ -403,8 +402,8 @@ static void adjust_exponent_nano(int *val0, int *val1, int scale0, } int hid_sensor_format_scale(u32 usage_id, - struct hid_sensor_hub_attribute_info *attr_info, - int *val0, int *val1) + struct hid_sensor_hub_attribute_info *attr_info, + int *val0, int *val1) { int i; int exp; @@ -414,12 +413,11 @@ int hid_sensor_format_scale(u32 usage_id, for (i = 0; i < ARRAY_SIZE(unit_conversion); ++i) { if (unit_conversion[i].usage_id == usage_id && - unit_conversion[i].unit == attr_info->units) { - exp = hid_sensor_convert_exponent( - attr_info->unit_expo); + unit_conversion[i].unit == attr_info->units) { + exp = hid_sensor_convert_exponent(attr_info->unit_expo); adjust_exponent_nano(val0, val1, - unit_conversion[i].scale_val0, - unit_conversion[i].scale_val1, exp); + unit_conversion[i].scale_val0, + unit_conversion[i].scale_val1, exp); break; } } @@ -437,8 +435,8 @@ EXPORT_SYMBOL_NS(hid_sensor_convert_timestamp, "IIO_HID"); static int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - struct hid_sensor_common *st) + u32 usage_id, + struct hid_sensor_common *st) { sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, usage_id, @@ -472,9 +470,10 @@ int hid_sensor_get_report_latency(struct hid_sensor_common *st) int ret; int value; - ret = sensor_hub_get_feature(st->hsdev, st->report_latency.report_id, - st->report_latency.index, sizeof(value), - &value); + ret = sensor_hub_get_feature(st->hsdev, + st->report_latency.report_id, + st->report_latency.index, + sizeof(value), &value); if (ret < 0) return ret; @@ -497,10 +496,10 @@ bool hid_sensor_batch_mode_supported(struct hid_sensor_common *st) EXPORT_SYMBOL_NS(hid_sensor_batch_mode_supported, "IIO_HID_ATTRIBUTES"); int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - struct hid_sensor_common *st, - const u32 *sensitivity_addresses, - u32 sensitivity_addresses_len) + u32 usage_id, + struct hid_sensor_common *st, + const u32 *sensitivity_addresses, + u32 sensitivity_addresses_len) { struct hid_sensor_hub_attribute_info timestamp; @@ -526,7 +525,7 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, usage_id, HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS, - &st->sensitivity); + &st->sensitivity); sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, usage_id, @@ -577,8 +576,9 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, timestamp.index, timestamp.report_id); ret = sensor_hub_get_feature(hsdev, - st->power_state.report_id, - st->power_state.index, sizeof(value), &value); + st->power_state.report_id, + st->power_state.index, + sizeof(value), &value); if (ret < 0) return ret; if (value < 0) diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index c8ccf96f3d03..fffaebe8c7f0 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -233,7 +233,7 @@ void hid_sensor_remove_trigger(struct iio_dev *indio_dev, EXPORT_SYMBOL_NS(hid_sensor_remove_trigger, "IIO_HID"); int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, - struct hid_sensor_common *attrb) + struct hid_sensor_common *attrb) { const struct iio_dev_attr **fifo_attrs; int ret; diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h index f94fca4f1edf..589de858e369 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h @@ -15,7 +15,7 @@ struct iio_dev; extern const struct dev_pm_ops hid_sensor_pm_ops; int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, - struct hid_sensor_common *attrb); + struct hid_sensor_common *attrb); void hid_sensor_remove_trigger(struct iio_dev *indio_dev, struct hid_sensor_common *attrb); int hid_sensor_power_state(struct hid_sensor_common *st, bool state); diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index 2ec4a38f2d46..58250a972567 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -84,9 +84,8 @@ static const struct iio_chan_spec gyro_3d_channels[] = { /* Channel read_raw handler */ static int gyro_3d_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct gyro_3d_state *gyro_state = iio_priv(indio_dev); int report_id = -1; @@ -111,8 +110,7 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev, min < 0); else { *val = 0; - hid_sensor_power_state(&gyro_state->common_attributes, - false); + hid_sensor_power_state(&gyro_state->common_attributes, false); return -EINVAL; } hid_sensor_power_state(&gyro_state->common_attributes, false); @@ -145,10 +143,8 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int gyro_3d_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct gyro_3d_state *gyro_state = iio_priv(indio_dev); int ret = 0; @@ -176,8 +172,7 @@ static const struct iio_info gyro_3d_info = { /* Callback handler to send event after all samples are received and captured */ static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct gyro_3d_state *gyro_state = iio_priv(indio_dev); @@ -198,9 +193,9 @@ static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct gyro_3d_state *gyro_state = iio_priv(indio_dev); @@ -253,10 +248,10 @@ static int gyro_3d_parse_report(struct platform_device *pdev, }; } dev_dbg(&pdev->dev, "gyro_3d %x:%x, %x:%x, %x:%x\n", - st->gyro[0].index, - st->gyro[0].report_id, - st->gyro[1].index, st->gyro[1].report_id, - st->gyro[2].index, st->gyro[2].report_id); + st->gyro[0].index, + st->gyro[0].report_id, + st->gyro[1].index, st->gyro[1].report_id, + st->gyro[2].index, st->gyro[2].report_id); st->scale_precision = hid_sensor_format_scale( HID_USAGE_SENSOR_GYRO_3D, @@ -317,7 +312,7 @@ static int hid_gyro_3d_probe(struct platform_device *pdev) atomic_set(&gyro_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &gyro_state->common_attributes); + &gyro_state->common_attributes); if (ret < 0) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; @@ -327,7 +322,7 @@ static int hid_gyro_3d_probe(struct platform_device *pdev) gyro_state->callbacks.capture_sample = gyro_3d_capture_sample; gyro_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D, - &gyro_state->callbacks); + &gyro_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); goto error_remove_trigger; diff --git a/drivers/iio/humidity/hid-sensor-humidity.c b/drivers/iio/humidity/hid-sensor-humidity.c index 081c88525524..7cec81ff5685 100644 --- a/drivers/iio/humidity/hid-sensor-humidity.c +++ b/drivers/iio/humidity/hid-sensor-humidity.c @@ -44,7 +44,7 @@ static const struct iio_chan_spec humidity_channels[] = { /* Adjust channel real bits based on report descriptor */ static void humidity_adjust_channel_bit_mask(struct iio_chan_spec *channels, - int channel, int size) + int channel, int size) { channels[channel].scan_type.sign = 's'; /* Real storage bits will change based on the report desc. */ @@ -54,8 +54,8 @@ static void humidity_adjust_channel_bit_mask(struct iio_chan_spec *channels, } static int humidity_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct hid_humidity_state *humid_st = iio_priv(indio_dev); @@ -100,8 +100,8 @@ static int humidity_read_raw(struct iio_dev *indio_dev, } static int humidity_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct hid_humidity_state *humid_st = iio_priv(indio_dev); @@ -126,7 +126,7 @@ static const struct iio_info humidity_info = { /* Callback handler to send event after all samples are received and captured */ static int humidity_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, void *pdev) + u32 usage_id, void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct hid_humidity_state *humid_st = iio_priv(indio_dev); @@ -140,8 +140,9 @@ static int humidity_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int humidity_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, size_t raw_len, - char *raw_data, void *pdev) + u32 usage_id, + size_t raw_len, char *raw_data, + void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct hid_humidity_state *humid_st = iio_priv(indio_dev); @@ -158,10 +159,10 @@ static int humidity_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id */ static int humidity_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, - u32 usage_id, - struct hid_humidity_state *st) + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + u32 usage_id, + struct hid_humidity_state *st) { int ret; @@ -215,12 +216,12 @@ static int hid_humidity_probe(struct platform_device *pdev) return ret; humid_chans = devm_kmemdup(&pdev->dev, humidity_channels, - sizeof(humidity_channels), GFP_KERNEL); + sizeof(humidity_channels), GFP_KERNEL); if (!humid_chans) return -ENOMEM; ret = humidity_parse_report(pdev, hsdev, humid_chans, - HID_USAGE_SENSOR_HUMIDITY, humid_st); + HID_USAGE_SENSOR_HUMIDITY, humid_st); if (ret) return ret; @@ -233,7 +234,7 @@ static int hid_humidity_probe(struct platform_device *pdev) atomic_set(&humid_st->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &humid_st->common_attributes); + &humid_st->common_attributes); if (ret) return ret; @@ -241,7 +242,7 @@ static int hid_humidity_probe(struct platform_device *pdev) humidity_callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_HUMIDITY, - &humidity_callbacks); + &humidity_callbacks); if (ret) goto error_remove_trigger; diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 14e20bab0ecc..6fb6ace26834 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -119,9 +119,8 @@ static const struct iio_chan_spec als_channels[] = { /* Channel read_raw handler */ static int als_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct als_state *als_state = iio_priv(indio_dev); struct hid_sensor_hub_device *hsdev = als_state->common_attributes.hsdev; @@ -162,12 +161,12 @@ static int als_read_raw(struct iio_dev *indio_dev, } if (report_id >= 0) { hid_sensor_power_state(&als_state->common_attributes, - true); + true); *val = sensor_hub_input_attr_get_raw_value( hsdev, hsdev->usage, address, report_id, SENSOR_HUB_SYNC, min < 0); hid_sensor_power_state(&als_state->common_attributes, - false); + false); } else { *val = 0; return -EINVAL; @@ -205,10 +204,8 @@ static int als_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int als_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct als_state *als_state = iio_priv(indio_dev); int ret = 0; @@ -240,8 +237,7 @@ static const struct iio_info als_info = { /* Callback handler to send event after all samples are received and captured */ static int als_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct als_state *als_state = iio_priv(indio_dev); @@ -262,9 +258,9 @@ static int als_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int als_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct als_state *als_state = iio_priv(indio_dev); @@ -303,9 +299,9 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int als_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - u32 usage_id, - struct als_state *st) + struct hid_sensor_hub_device *hsdev, + u32 usage_id, + struct als_state *st) { struct iio_chan_spec *channels; int ret, index = 0; @@ -399,7 +395,7 @@ static int hid_als_probe(struct platform_device *pdev) atomic_set(&als_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &als_state->common_attributes); + &als_state->common_attributes); if (ret < 0) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index 1dafad03c8f7..63fd3eff171c 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -69,9 +69,8 @@ static const struct iio_chan_spec prox_channels[] = { /* Channel read_raw handler */ static int prox_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct prox_state *prox_state = iio_priv(indio_dev); struct hid_sensor_hub_device *hsdev; @@ -134,10 +133,8 @@ static int prox_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int prox_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct prox_state *prox_state = iio_priv(indio_dev); int ret = 0; @@ -165,8 +162,7 @@ static const struct iio_info prox_info = { /* Callback handler to send event after all samples are received and captured */ static int prox_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct prox_state *prox_state = iio_priv(indio_dev); @@ -182,9 +178,9 @@ static int prox_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int prox_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct prox_state *prox_state = iio_priv(indio_dev); @@ -217,8 +213,8 @@ static int prox_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int prox_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct prox_state *st) + struct hid_sensor_hub_device *hsdev, + struct prox_state *st) { struct iio_chan_spec *channels = st->channels; int index = 0; @@ -270,8 +266,7 @@ static int hid_prox_probe(struct platform_device *pdev) struct iio_dev *indio_dev; struct prox_state *prox_state; - indio_dev = devm_iio_device_alloc(&pdev->dev, - sizeof(struct prox_state)); + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct prox_state)); if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -305,7 +300,7 @@ static int hid_prox_probe(struct platform_device *pdev) atomic_set(&prox_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &prox_state->common_attributes); + &prox_state->common_attributes); if (ret) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index 15252ea704da..ff6f309795bc 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -134,9 +134,8 @@ static const struct iio_chan_spec magn_3d_channels[] = { /* Channel read_raw handler */ static int magn_3d_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct magn_3d_state *magn_state = iio_priv(indio_dev); int report_id = -1; @@ -166,8 +165,7 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev, false); return -EINVAL; } - hid_sensor_power_state(&magn_state->magn_flux_attributes, - false); + hid_sensor_power_state(&magn_state->magn_flux_attributes, false); ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: @@ -228,10 +226,8 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int magn_3d_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct magn_3d_state *magn_state = iio_priv(indio_dev); int ret = 0; @@ -269,8 +265,7 @@ static const struct iio_info magn_3d_info = { /* Callback handler to send event after all samples are received and captured */ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct magn_3d_state *magn_state = iio_priv(indio_dev); @@ -291,9 +286,9 @@ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct magn_3d_state *magn_state = iio_priv(indio_dev); @@ -367,18 +362,17 @@ static int magn_3d_parse_report(struct platform_device *pdev, return -EINVAL; } - dev_dbg(&pdev->dev, "magn_3d Found %d usage attributes\n", - attr_count); + dev_dbg(&pdev->dev, "magn_3d Found %d usage attributes\n", attr_count); dev_dbg(&pdev->dev, "magn_3d X: %x:%x Y: %x:%x Z: %x:%x\n", - st->magn[0].index, - st->magn[0].report_id, - st->magn[1].index, st->magn[1].report_id, - st->magn[2].index, st->magn[2].report_id); + st->magn[0].index, + st->magn[0].report_id, + st->magn[1].index, st->magn[1].report_id, + st->magn[2].index, st->magn[2].report_id); /* Setup IIO channel array */ _channels = devm_kcalloc(&pdev->dev, attr_count, - sizeof(struct iio_chan_spec), - GFP_KERNEL); + sizeof(struct iio_chan_spec), + GFP_KERNEL); if (!_channels) { dev_err(&pdev->dev, "failed to allocate space for iio channels\n"); @@ -425,8 +419,7 @@ static int magn_3d_parse_report(struct platform_device *pdev, *channels = _channels; - dev_dbg(&pdev->dev, "magn_3d Setup %d IIO channels\n", - *chan_count); + dev_dbg(&pdev->dev, "magn_3d Setup %d IIO channels\n", *chan_count); st->magn_flux_attr.scale_precision = hid_sensor_format_scale( HID_USAGE_SENSOR_COMPASS_3D, @@ -490,8 +483,8 @@ static int hid_magn_3d_probe(struct platform_device *pdev) magn_state->rot_attributes.sensitivity.index = -1; ret = magn_3d_parse_report(pdev, hsdev, - &channels, &chan_count, - HID_USAGE_SENSOR_COMPASS_3D, magn_state); + &channels, &chan_count, + HID_USAGE_SENSOR_COMPASS_3D, magn_state); if (ret) { dev_err(&pdev->dev, "failed to parse report\n"); return ret; @@ -506,7 +499,7 @@ static int hid_magn_3d_probe(struct platform_device *pdev) atomic_set(&magn_state->magn_flux_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &magn_state->magn_flux_attributes); + &magn_state->magn_flux_attributes); if (ret < 0) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; @@ -516,7 +509,7 @@ static int hid_magn_3d_probe(struct platform_device *pdev) magn_state->callbacks.capture_sample = magn_3d_capture_sample; magn_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D, - &magn_state->callbacks); + &magn_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); goto error_remove_trigger; diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c index 53f39efa644d..136aa6851888 100644 --- a/drivers/iio/orientation/hid-sensor-incl-3d.c +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -85,8 +85,7 @@ static const struct iio_chan_spec incl_3d_channels[] = { }; /* Adjust channel real bits based on report descriptor */ -static void incl_3d_adjust_channel_bit_mask(struct iio_chan_spec *chan, - int size) +static void incl_3d_adjust_channel_bit_mask(struct iio_chan_spec *chan, int size) { chan->scan_type.sign = 's'; /* Real storage bits will change based on the report desc. */ @@ -97,9 +96,8 @@ static void incl_3d_adjust_channel_bit_mask(struct iio_chan_spec *chan, /* Channel read_raw handler */ static int incl_3d_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct incl_3d_state *incl_state = iio_priv(indio_dev); int report_id = -1; @@ -124,7 +122,7 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev, min < 0); else { hid_sensor_power_state(&incl_state->common_attributes, - false); + false); return -EINVAL; } hid_sensor_power_state(&incl_state->common_attributes, false); @@ -157,10 +155,8 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int incl_3d_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct incl_3d_state *incl_state = iio_priv(indio_dev); int ret; @@ -188,8 +184,7 @@ static const struct iio_info incl_3d_info = { /* Callback handler to send event after all samples are received and captured */ static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct incl_3d_state *incl_state = iio_priv(indio_dev); @@ -211,9 +206,9 @@ static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int incl_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct incl_3d_state *incl_state = iio_priv(indio_dev); @@ -349,7 +344,7 @@ static int hid_incl_3d_probe(struct platform_device *pdev) atomic_set(&incl_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &incl_state->common_attributes); + &incl_state->common_attributes); if (ret) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; @@ -359,8 +354,8 @@ static int hid_incl_3d_probe(struct platform_device *pdev) incl_state->callbacks.capture_sample = incl_3d_capture_sample; incl_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, - HID_USAGE_SENSOR_INCLINOMETER_3D, - &incl_state->callbacks); + HID_USAGE_SENSOR_INCLINOMETER_3D, + &incl_state->callbacks); if (ret) { dev_err(&pdev->dev, "callback reg failed\n"); goto error_remove_trigger; diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index 7d2e66ad2712..ccfd0487f349 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -80,9 +80,8 @@ static const struct iio_chan_spec dev_rot_channels[] = { /* Channel read_raw handler */ static int dev_rot_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int size, int *vals, int *val_len, - long mask) + struct iio_chan_spec const *chan, + int size, int *vals, int *val_len, long mask) { struct dev_rot_state *rot_state = iio_priv(indio_dev); int ret_type; @@ -128,10 +127,8 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int dev_rot_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct dev_rot_state *rot_state = iio_priv(indio_dev); int ret; @@ -175,8 +172,7 @@ static const struct iio_info dev_rot_info = { /* Callback handler to send event after all samples are received and captured */ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct dev_rot_state *rot_state = iio_priv(indio_dev); @@ -208,9 +204,9 @@ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct dev_rot_state *rot_state = iio_priv(indio_dev); @@ -325,7 +321,7 @@ static int hid_dev_rot_probe(struct platform_device *pdev) atomic_set(&rot_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &rot_state->common_attributes); + &rot_state->common_attributes); if (ret) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; @@ -335,7 +331,7 @@ static int hid_dev_rot_probe(struct platform_device *pdev) rot_state->callbacks.capture_sample = dev_rot_capture_sample; rot_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, hsdev->usage, - &rot_state->callbacks); + &rot_state->callbacks); if (ret) { dev_err(&pdev->dev, "callback reg failed\n"); goto error_remove_trigger; diff --git a/drivers/iio/position/hid-sensor-custom-intel-hinge.c b/drivers/iio/position/hid-sensor-custom-intel-hinge.c index 5fec7d803cda..d275bc1413fe 100644 --- a/drivers/iio/position/hid-sensor-custom-intel-hinge.c +++ b/drivers/iio/position/hid-sensor-custom-intel-hinge.c @@ -106,8 +106,8 @@ static void hinge_adjust_channel_realbits(struct iio_chan_spec *channels, /* Channel read_raw handler */ static int hinge_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct hinge_state *st = iio_priv(indio_dev); struct hid_sensor_hub_device *hsdev; @@ -153,8 +153,8 @@ static int hinge_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int hinge_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int val, int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct hinge_state *st = iio_priv(indio_dev); @@ -208,8 +208,9 @@ static int hinge_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int hinge_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, size_t raw_len, - char *raw_data, void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct hinge_state *st = iio_priv(indio_dev); diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c index 4935e3b2a5c3..e688b0776547 100644 --- a/drivers/iio/pressure/hid-sensor-press.c +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -55,9 +55,8 @@ static const struct iio_chan_spec press_channels[] = { /* Channel read_raw handler */ static int press_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct press_state *press_state = iio_priv(indio_dev); int report_id = -1; @@ -81,7 +80,7 @@ static int press_read_raw(struct iio_dev *indio_dev, } if (report_id >= 0) { hid_sensor_power_state(&press_state->common_attributes, - true); + true); *val = sensor_hub_input_attr_get_raw_value( press_state->common_attributes.hsdev, HID_USAGE_SENSOR_PRESSURE, address, @@ -89,7 +88,7 @@ static int press_read_raw(struct iio_dev *indio_dev, SENSOR_HUB_SYNC, min < 0); hid_sensor_power_state(&press_state->common_attributes, - false); + false); } else { *val = 0; return -EINVAL; @@ -123,10 +122,8 @@ static int press_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int press_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct press_state *press_state = iio_priv(indio_dev); int ret = 0; @@ -154,8 +151,7 @@ static const struct iio_info press_info = { /* Callback handler to send event after all samples are received and captured */ static int press_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct press_state *press_state = iio_priv(indio_dev); @@ -201,10 +197,10 @@ static int press_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int press_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, - u32 usage_id, - struct press_state *st) + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + u32 usage_id, + struct press_state *st) { int ret; @@ -221,7 +217,7 @@ static int press_parse_report(struct platform_device *pdev, }; dev_dbg(&pdev->dev, "press %x:%x\n", st->press_attr.index, - st->press_attr.report_id); + st->press_attr.report_id); st->scale_precision = hid_sensor_format_scale( HID_USAGE_SENSOR_PRESSURE, @@ -240,8 +236,7 @@ static int hid_press_probe(struct platform_device *pdev) struct iio_dev *indio_dev; struct press_state *press_state; - indio_dev = devm_iio_device_alloc(&pdev->dev, - sizeof(struct press_state)); + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct press_state)); if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -284,7 +279,7 @@ static int hid_press_probe(struct platform_device *pdev) atomic_set(&press_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &press_state->common_attributes); + &press_state->common_attributes); if (ret) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; @@ -294,7 +289,7 @@ static int hid_press_probe(struct platform_device *pdev) press_state->callbacks.capture_sample = press_capture_sample; press_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_PRESSURE, - &press_state->callbacks); + &press_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); goto error_remove_trigger; diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c index fabd5c9b391e..76723176d6b6 100644 --- a/drivers/iio/temperature/hid-sensor-temperature.c +++ b/drivers/iio/temperature/hid-sensor-temperature.c @@ -44,7 +44,7 @@ static const struct iio_chan_spec temperature_channels[] = { /* Adjust channel real bits based on report descriptor */ static void temperature_adjust_channel_bit_mask(struct iio_chan_spec *channels, - int channel, int size) + int channel, int size) { channels[channel].scan_type.sign = 's'; /* Real storage bits will change based on the report desc. */ @@ -100,8 +100,8 @@ static int temperature_read_raw(struct iio_dev *indio_dev, } static int temperature_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct temperature_state *temp_st = iio_priv(indio_dev); @@ -124,7 +124,7 @@ static const struct iio_info temperature_info = { /* Callback handler to send event after all samples are received and captured */ static int temperature_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, void *pdev) + u32 usage_id, void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct temperature_state *temp_st = iio_priv(indio_dev); @@ -139,8 +139,9 @@ static int temperature_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int temperature_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, size_t raw_len, - char *raw_data, void *pdev) + u32 usage_id, + size_t raw_len, char *raw_data, + void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct temperature_state *temp_st = iio_priv(indio_dev); @@ -156,10 +157,10 @@ static int temperature_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int temperature_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, - u32 usage_id, - struct temperature_state *st) + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + u32 usage_id, + struct temperature_state *st) { int ret; @@ -170,8 +171,7 @@ static int temperature_parse_report(struct platform_device *pdev, if (ret < 0) return ret; - temperature_adjust_channel_bit_mask(channels, 0, - st->temperature_attr.size); + temperature_adjust_channel_bit_mask(channels, 0, st->temperature_attr.size); st->scale_precision = hid_sensor_format_scale( HID_USAGE_SENSOR_TEMPERATURE, @@ -218,7 +218,7 @@ static int hid_temperature_probe(struct platform_device *pdev) return -ENOMEM; ret = temperature_parse_report(pdev, hsdev, temp_chans, - HID_USAGE_SENSOR_TEMPERATURE, temp_st); + HID_USAGE_SENSOR_TEMPERATURE, temp_st); if (ret) return ret; @@ -231,7 +231,7 @@ static int hid_temperature_probe(struct platform_device *pdev) atomic_set(&temp_st->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &temp_st->common_attributes); + &temp_st->common_attributes); if (ret) return ret; @@ -239,7 +239,7 @@ static int hid_temperature_probe(struct platform_device *pdev) temperature_callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE, - &temperature_callbacks); + &temperature_callbacks); if (ret) goto error_remove_trigger; From 2e2f2de7532cbbc2269de8be20ec709606c6e79b Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Tue, 7 Jul 2026 00:29:44 +0530 Subject: [PATCH 143/513] iio: hid-sensors: Use implicit NULL pointer checks Replace explicit NULL pointer comparisons with implicit checks across HID sensor IIO drivers to fix kernel coding style. CHECK: Comparison to NULL could be written ... No functional change. Signed-off-by: Sanjay Chitroda Reviewed-by: Maxwell Doose Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/accel/hid-sensor-accel-3d.c | 2 +- drivers/iio/common/hid-sensors/hid-sensor-trigger.c | 2 +- drivers/iio/magnetometer/hid-sensor-magn-3d.c | 4 ++-- drivers/iio/orientation/hid-sensor-incl-3d.c | 2 +- drivers/iio/orientation/hid-sensor-rotation.c | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 2e30c0054ff8..eb521125f4eb 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -324,7 +324,7 @@ static int hid_accel_3d_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct accel_3d_state)); - if (indio_dev == NULL) + if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index fffaebe8c7f0..60808e2430ca 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -266,7 +266,7 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, trig = iio_trigger_alloc(indio_dev->dev.parent, "%s-dev%d", name, iio_device_id(indio_dev)); - if (trig == NULL) { + if (!trig) { dev_err(&indio_dev->dev, "Trigger Allocate Failed\n"); return -ENOMEM; } diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index ff6f309795bc..ad18f233ee16 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -321,7 +321,7 @@ static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev, iio_val = magn_state->magn_val_addr[offset]; - if (iio_val != NULL) + if (iio_val) *iio_val = *((u32 *)raw_data); else ret = -EINVAL; @@ -460,7 +460,7 @@ static int hid_magn_3d_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct magn_3d_state)); - if (indio_dev == NULL) + if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c index 136aa6851888..aea28321db95 100644 --- a/drivers/iio/orientation/hid-sensor-incl-3d.c +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -301,7 +301,7 @@ static int hid_incl_3d_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct incl_3d_state)); - if (indio_dev == NULL) + if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index ccfd0487f349..9e13a0ecd584 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -273,7 +273,7 @@ static int hid_dev_rot_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct dev_rot_state)); - if (indio_dev == NULL) + if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); From 08303f16480e24705dc4ffc7f2d89c2afbd33a58 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Tue, 23 Jun 2026 11:01:27 +0200 Subject: [PATCH 144/513] fpga: xilinx-pr-decoupler: Use devm_clk_get_prepared() The driver keeps the "aclk" clock prepared but disabled in its idle state, toggling only the atomic clk_enable()/clk_disable() around register accesses in the bridge enable_set/enable_show callbacks. At probe time this was open-coded as clk_prepare_enable() immediately followed by clk_disable(), leaving the clock prepared, with a matching clk_unprepare() in the error path and in remove(). devm_clk_get_prepared() expresses exactly this: it gets and prepares the clock and unprepares it automatically on driver detach. Use it to drop the manual prepare/disable dance, the error-path unprepare, and the now-empty clock teardown in remove(). Signed-off-by: Michal Simek Reviewed-by: Radhey Shyam Pandey Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/8ca8ee5ba720b608a41f842d2b743302e5500ad0.1782205286.git.michal.simek@amd.com Signed-off-by: Xu Yilun --- drivers/fpga/xilinx-pr-decoupler.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c index 6994d68e9036..45b65a3264af 100644 --- a/drivers/fpga/xilinx-pr-decoupler.c +++ b/drivers/fpga/xilinx-pr-decoupler.c @@ -118,46 +118,30 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev) if (IS_ERR(priv->io_base)) return PTR_ERR(priv->io_base); - priv->clk = devm_clk_get(&pdev->dev, "aclk"); + priv->clk = devm_clk_get_prepared(&pdev->dev, "aclk"); if (IS_ERR(priv->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk), "input clock not found\n"); - err = clk_prepare_enable(priv->clk); - if (err) { - dev_err(&pdev->dev, "unable to enable clock\n"); - return err; - } - - clk_disable(priv->clk); - br = fpga_bridge_register(&pdev->dev, priv->ipconfig->name, &xlnx_pr_decoupler_br_ops, priv); if (IS_ERR(br)) { err = PTR_ERR(br); dev_err(&pdev->dev, "unable to register %s", priv->ipconfig->name); - goto err_clk; + return err; } platform_set_drvdata(pdev, br); return 0; - -err_clk: - clk_unprepare(priv->clk); - - return err; } static void xlnx_pr_decoupler_remove(struct platform_device *pdev) { struct fpga_bridge *bridge = platform_get_drvdata(pdev); - struct xlnx_pr_decoupler_data *p = bridge->priv; fpga_bridge_unregister(bridge); - - clk_unprepare(p->clk); } static struct platform_driver xlnx_pr_decoupler_driver = { From c14a8b15c87b49efc3ef898cec8ac7c30336a080 Mon Sep 17 00:00:00 2001 From: Tien Sung Ang Date: Mon, 29 Jun 2026 23:57:19 -0700 Subject: [PATCH 145/513] fpga: stratix10-soc: Fix SVC mailbox handling during reconfiguration Fix incorrect stratix10_svc_done() usage during FPGA reconfiguration. Do not call stratix10_svc_done() at the end of write_init() on success, so the SVC session remains active through write() and write_complete(). Call stratix10_svc_done() on failure in write_init() and write() so the shared SVC mailbox is released when reconfiguration aborts, allowing coexistence with other SVC clients such as soc64-hwmon. Fixes: e7eef1d7633a ("fpga: add intel stratix10 soc fpga manager driver") Cc: stable@vger.kernel.org # 5.1+ Signed-off-by: Tien Sung Ang Signed-off-by: Tze Yee Ng Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/8768ce3260489c9febdfce08e27d03f5f5ed9c33.1782801986.git.tze.yee.ng@altera.com Signed-off-by: Xu Yilun --- drivers/fpga/stratix10-soc.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c index 0a295ccf1644..b8ec2e6f615f 100644 --- a/drivers/fpga/stratix10-soc.c +++ b/drivers/fpga/stratix10-soc.c @@ -195,20 +195,18 @@ static int s10_ops_write_init(struct fpga_manager *mgr, ret = s10_svc_send_msg(priv, COMMAND_RECONFIG, &ctype, sizeof(ctype)); if (ret < 0) - goto init_done; + goto init_error; - ret = wait_for_completion_timeout( - &priv->status_return_completion, S10_RECONFIG_TIMEOUT); - if (!ret) { + if (!wait_for_completion_timeout(&priv->status_return_completion, + S10_RECONFIG_TIMEOUT)) { dev_err(dev, "timeout waiting for RECONFIG_REQUEST\n"); ret = -ETIMEDOUT; - goto init_done; + goto init_error; } - ret = 0; if (!test_and_clear_bit(SVC_STATUS_OK, &priv->status)) { ret = -ETIMEDOUT; - goto init_done; + goto init_error; } /* Allocate buffers from the service layer's pool. */ @@ -217,14 +215,16 @@ static int s10_ops_write_init(struct fpga_manager *mgr, if (IS_ERR(kbuf)) { s10_free_buffers(mgr); ret = PTR_ERR(kbuf); - goto init_done; + goto init_error; } priv->svc_bufs[i].buf = kbuf; priv->svc_bufs[i].lock = 0; } -init_done: + return 0; + +init_error: stratix10_svc_done(priv->chan); return ret; } @@ -342,6 +342,9 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf, if (!s10_free_buffers(mgr)) dev_err(dev, "%s not all buffers were freed\n", __func__); + if (ret < 0) + stratix10_svc_done(priv->chan); + return ret; } From b5ba63e247075087ab8a6a087622c762dc4172e9 Mon Sep 17 00:00:00 2001 From: Griffin Kroah-Hartman Date: Mon, 6 Jul 2026 16:58:21 +0200 Subject: [PATCH 146/513] fpga: dfl: fme: add error handling Add error handling to devm_kasprint in fme_perf_pmu_register(). Assisted-by: gkh_clanker_2000 Fixes: 724142f8c42a ("fpga: dfl: fme: add performance reporting support") Cc: stable@kernel.org Cc: Xu Yilun Cc: Tom Rix Cc: Moritz Fischer Signed-off-by: Griffin Kroah-Hartman Signed-off-by: Greg Kroah-Hartman [ Yilun: Fix stable tag, add Fixes tag ] Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/2026070620-unwired-clay-f6cc@gregkh Signed-off-by: Xu Yilun --- drivers/fpga/dfl-fme-perf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/fpga/dfl-fme-perf.c b/drivers/fpga/dfl-fme-perf.c index 7422d2bc6f37..db56d52411ef 100644 --- a/drivers/fpga/dfl-fme-perf.c +++ b/drivers/fpga/dfl-fme-perf.c @@ -925,6 +925,8 @@ static int fme_perf_pmu_register(struct platform_device *pdev, PERF_PMU_CAP_NO_EXCLUDE; name = devm_kasprintf(priv->dev, GFP_KERNEL, "dfl_fme%d", pdev->id); + if (!name) + return -ENOMEM; ret = perf_pmu_register(pmu, name, -1); if (ret) From 2c12d05d863708bc2e9d1ee13c6c1534bb990c0d Mon Sep 17 00:00:00 2001 From: Xilin Wu Date: Thu, 7 May 2026 22:25:12 +0800 Subject: [PATCH 147/513] dt-bindings: interconnect: qcom,sc8280xp-rpmh: Add reg and clocks for QoS Add the register range and clock properties needed for programming NoC QoS configuration on SC8280XP. Require a register range for the real NoC providers, require QoS clocks for aggre1_noc and aggre2_noc, and keep the virtual clk_virt and mc_virt providers without MMIO resources or clocks. Signed-off-by: Xilin Wu Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260507-sc8280xp-qos-v1-1-15135858cd98@radxa.com Signed-off-by: Georgi Djakov --- .../interconnect/qcom,sc8280xp-rpmh.yaml | 101 +++++++++++++++++- 1 file changed, 99 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sc8280xp-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sc8280xp-rpmh.yaml index 2a5a7594bafd..cd327a3bf3b9 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sc8280xp-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sc8280xp-rpmh.yaml @@ -32,18 +32,115 @@ properties: - qcom,sc8280xp-nspb-noc - qcom,sc8280xp-system-noc + reg: + maxItems: 1 + + clocks: + minItems: 7 + maxItems: 8 + required: - compatible allOf: - $ref: qcom,rpmh-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - qcom,sc8280xp-clk-virt + - qcom,sc8280xp-mc-virt + then: + properties: + reg: false + clocks: false + else: + required: + - reg + + - if: + properties: + compatible: + contains: + enum: + - qcom,sc8280xp-aggre1-noc + then: + properties: + clocks: + items: + - description: aggre UFS PHY AXI clock + - description: aggre USB3 PRIM AXI clock + - description: aggre USB3 SEC AXI clock + - description: aggre USB3 MP AXI clock + - description: aggre USB4 AXI clock + - description: aggre USB4 1 AXI clock + - description: aggre USB NOC SOUTH AXI clock + - description: RPMH CC IPA clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,sc8280xp-aggre2-noc + then: + properties: + clocks: + items: + - description: aggre NOC PCIE0 tunnel AXI clock + - description: aggre NOC PCIE1 tunnel AXI clock + - description: aggre NOC PCIE 4 AXI clock + - description: aggre NOC PCIE SOUTH SF AXI clock + - description: aggre UFS CARD AXI clock + - description: DDRSS GPU AXI clock + - description: DDRSS PCIE SF TBU clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,sc8280xp-aggre1-noc + - qcom,sc8280xp-aggre2-noc + then: + required: + - clocks + else: + properties: + clocks: false unevaluatedProperties: false examples: - | - interconnect-0 { - compatible = "qcom,sc8280xp-aggre1-noc"; + #include + #include + + interconnect { + compatible = "qcom,sc8280xp-clk-virt"; #interconnect-cells = <2>; qcom,bcm-voters = <&apps_bcm_voter>; }; + + interconnect@9100000 { + compatible = "qcom,sc8280xp-gem-noc"; + reg = <0x9100000 0xb8400>; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + interconnect@16c0000 { + compatible = "qcom,sc8280xp-aggre1-noc"; + reg = <0x16c0000 0x3af80>; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + clocks = <&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>, + <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, + <&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>, + <&gcc GCC_AGGRE_USB3_MP_AXI_CLK>, + <&gcc GCC_AGGRE_USB4_AXI_CLK>, + <&gcc GCC_AGGRE_USB4_1_AXI_CLK>, + <&gcc GCC_AGGRE_USB_NOC_SOUTH_AXI_CLK>, + <&rpmhcc RPMH_IPA_CLK>; + }; From 3ee8b9b7c4255f3858ea45f32b0907fa5f88b9cf Mon Sep 17 00:00:00 2001 From: Xilin Wu Date: Thu, 7 May 2026 22:25:13 +0800 Subject: [PATCH 148/513] interconnect: qcom: sc8280xp: Enable QoS configuration Enable static QoS configuration for SC8280XP NoC master ports with predefined priority and urgency forwarding values. Add the QoS box data for the SC8280XP providers, add regmap configurations for the real NoCs, and mark only aggre1_noc and aggre2_noc as requiring clocks for QoS register access. Signed-off-by: Xilin Wu Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260507-sc8280xp-qos-v1-2-15135858cd98@radxa.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/sc8280xp.c | 356 +++++++++++++++++++++++++++ 1 file changed, 356 insertions(+) diff --git a/drivers/interconnect/qcom/sc8280xp.c b/drivers/interconnect/qcom/sc8280xp.c index ed2161da37bf..333e0aa6b953 100644 --- a/drivers/interconnect/qcom/sc8280xp.c +++ b/drivers/interconnect/qcom/sc8280xp.c @@ -216,6 +216,11 @@ 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 = { 0x5000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -224,6 +229,11 @@ 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 = { 0x6000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -232,6 +242,11 @@ 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 = { 0xc000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -248,6 +263,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 = { 0x8000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -256,6 +277,11 @@ static struct qcom_icc_node xm_emac_1 = { .name = "xm_emac_1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -264,6 +290,11 @@ static struct qcom_icc_node xm_sdc4 = { .name = "xm_sdc4", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x7000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -272,6 +303,11 @@ 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 = { 0x9000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -280,6 +316,11 @@ 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 = { 0xb000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_snoc }, }; @@ -288,6 +329,11 @@ static struct qcom_icc_node xm_usb3_1 = { .name = "xm_usb3_1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_snoc }, }; @@ -296,6 +342,11 @@ static struct qcom_icc_node xm_usb3_mp = { .name = "xm_usb3_mp", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_snoc }, }; @@ -304,6 +355,11 @@ static struct qcom_icc_node xm_usb4_host0 = { .name = "xm_usb4_host0", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_snoc }, }; @@ -312,6 +368,11 @@ static struct qcom_icc_node xm_usb4_host1 = { .name = "xm_usb4_host1", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x10000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_snoc }, }; @@ -320,6 +381,11 @@ 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 = { 0xa000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -328,6 +394,11 @@ 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 = { 0xb000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -344,6 +415,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 = { 0xe000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -368,6 +445,11 @@ 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 = { 0x11000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -376,6 +458,11 @@ 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 = { 0x12000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -384,6 +471,11 @@ static struct qcom_icc_node xm_pcie3_1 = { .name = "xm_pcie3_1", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -392,6 +484,11 @@ static struct qcom_icc_node xm_pcie3_2a = { .name = "xm_pcie3_2a", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x13000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -400,6 +497,11 @@ static struct qcom_icc_node xm_pcie3_2b = { .name = "xm_pcie3_2b", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -408,6 +510,11 @@ static struct qcom_icc_node xm_pcie3_3a = { .name = "xm_pcie3_3a", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -416,6 +523,11 @@ static struct qcom_icc_node xm_pcie3_3b = { .name = "xm_pcie3_3b", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -424,6 +536,11 @@ static struct qcom_icc_node xm_pcie3_4 = { .name = "xm_pcie3_4", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x10000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_pcie_gem_noc }, }; @@ -432,6 +549,11 @@ 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 = { 0x7000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -440,6 +562,11 @@ 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 = { 0x8000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -448,6 +575,11 @@ static struct qcom_icc_node xm_ufs_card = { .name = "xm_ufs_card", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x9000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -587,6 +719,11 @@ 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 = { 0xa0000 }, + .prio = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -596,6 +733,11 @@ 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 = { 0xa1000 }, + .prio = 3, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -605,6 +747,11 @@ 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 = { 0xa2000 }, + .prio = 6, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -624,6 +771,11 @@ 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 = { 0x1b000, 0x66000 }, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -633,6 +785,11 @@ static struct qcom_icc_node qnm_cmpnoc1 = { .name = "qnm_cmpnoc1", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x24000, 0x6f000 }, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -652,6 +809,11 @@ static struct qcom_icc_node qnm_gpu = { .name = "qnm_gpu", .channels = 4, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 4, + .port_offsets = { 0x1c000, 0x1d000, 0x67000, 0x68000 }, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -661,6 +823,11 @@ 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 = { 0x1e000, 0x69000 }, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_llcc, &qns_pcie }, @@ -670,6 +837,11 @@ 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 = { 0x1f000, 0x6a000 }, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -679,6 +851,12 @@ 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 = { 0xa3000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, @@ -688,6 +866,11 @@ 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 = { 0xa4000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_llcc }, }; @@ -696,6 +879,11 @@ 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 = { 0xa5000 }, + .urg_fwd = 1, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, @@ -738,6 +926,11 @@ 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 = { 0x10000, 0x10180 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -746,6 +939,11 @@ 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 = { 0x16000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -754,6 +952,11 @@ 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 = { 0x16080 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -762,6 +965,11 @@ static struct qcom_icc_node qnm_mdp1_0 = { .name = "qnm_mdp1_0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -770,6 +978,11 @@ static struct qcom_icc_node qnm_mdp1_1 = { .name = "qnm_mdp1_1", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf800 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -786,6 +999,11 @@ static struct qcom_icc_node qnm_rot_0 = { .name = "qnm_rot_0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x17000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -794,6 +1012,11 @@ static struct qcom_icc_node qnm_rot_1 = { .name = "qnm_rot_1", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -802,6 +1025,11 @@ 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 = { 0x14000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -810,6 +1038,11 @@ static struct qcom_icc_node qnm_video1 = { .name = "qnm_video1", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14080 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -818,6 +1051,11 @@ 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 = { 0x15000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -826,6 +1064,11 @@ static struct qcom_icc_node qxm_camnoc_icp = { .name = "qxm_camnoc_icp", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x11000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -834,6 +1077,11 @@ 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 = { 0x12000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -900,6 +1148,11 @@ 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 = { 0xe000 }, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_sf }, }; @@ -916,6 +1169,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 = { 0x10000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_gc }, }; @@ -924,6 +1183,11 @@ 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 = { 0x12000 }, + .prio = 2, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_gc }, }; @@ -1997,11 +2261,21 @@ static struct qcom_icc_node * const aggre1_noc_nodes[] = { [SLAVE_SERVICE_A1NOC] = &srvc_aggre1_noc, }; +static const struct regmap_config sc8280xp_aggre1_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3af80, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_aggre1_noc = { + .config = &sc8280xp_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[] = { @@ -2033,11 +2307,21 @@ static struct qcom_icc_node * const aggre2_noc_nodes[] = { [SLAVE_SERVICE_A2NOC] = &srvc_aggre2_noc, }; +static const struct regmap_config sc8280xp_aggre2_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3af80, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_aggre2_noc = { + .config = &sc8280xp_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[] = { @@ -2159,7 +2443,16 @@ static struct qcom_icc_node * const config_noc_nodes[] = { [SLAVE_TCU] = &xs_sys_tcu_cfg, }; +static const struct regmap_config sc8280xp_config_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x2c000, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_config_noc = { + .config = &sc8280xp_config_noc_regmap_config, .nodes = config_noc_nodes, .num_nodes = ARRAY_SIZE(config_noc_nodes), .bcms = config_noc_bcms, @@ -2175,7 +2468,16 @@ static struct qcom_icc_node * const dc_noc_nodes[] = { [SLAVE_GEM_NOC_CFG] = &qns_gemnoc, }; +static const struct regmap_config sc8280xp_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 sc8280xp_dc_noc = { + .config = &sc8280xp_dc_noc_regmap_config, .nodes = dc_noc_nodes, .num_nodes = ARRAY_SIZE(dc_noc_nodes), .bcms = dc_noc_bcms, @@ -2209,7 +2511,16 @@ static struct qcom_icc_node * const gem_noc_nodes[] = { [SLAVE_SERVICE_GEM_NOC] = &srvc_sys_gemnoc, }; +static const struct regmap_config sc8280xp_gem_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xb8400, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_gem_noc = { + .config = &sc8280xp_gem_noc_regmap_config, .nodes = gem_noc_nodes, .num_nodes = ARRAY_SIZE(gem_noc_nodes), .bcms = gem_noc_bcms, @@ -2232,7 +2543,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 sc8280xp_lpass_ag_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xf080, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_lpass_ag_noc = { + .config = &sc8280xp_lpass_ag_noc_regmap_config, .nodes = lpass_ag_noc_nodes, .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), .bcms = lpass_ag_noc_bcms, @@ -2280,7 +2600,16 @@ static struct qcom_icc_node * const mmss_noc_nodes[] = { [SLAVE_SERVICE_MNOC] = &srvc_mnoc, }; +static const struct regmap_config sc8280xp_mmss_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1fa80, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_mmss_noc = { + .config = &sc8280xp_mmss_noc_regmap_config, .nodes = mmss_noc_nodes, .num_nodes = ARRAY_SIZE(mmss_noc_nodes), .bcms = mmss_noc_bcms, @@ -2300,7 +2629,16 @@ static struct qcom_icc_node * const nspa_noc_nodes[] = { [SLAVE_SERVICE_NSP_NOC] = &service_nsp_noc, }; +static const struct regmap_config sc8280xp_nspa_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x10000, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_nspa_noc = { + .config = &sc8280xp_nspa_noc_regmap_config, .nodes = nspa_noc_nodes, .num_nodes = ARRAY_SIZE(nspa_noc_nodes), .bcms = nspa_noc_bcms, @@ -2320,7 +2658,16 @@ static struct qcom_icc_node * const nspb_noc_nodes[] = { [SLAVE_SERVICE_NSPB_NOC] = &service_nspb_noc, }; +static const struct regmap_config sc8280xp_nspb_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x10000, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_nspb_noc = { + .config = &sc8280xp_nspb_noc_regmap_config, .nodes = nspb_noc_nodes, .num_nodes = ARRAY_SIZE(nspb_noc_nodes), .bcms = nspb_noc_bcms, @@ -2349,7 +2696,16 @@ static struct qcom_icc_node * const system_noc_main_nodes[] = { [SLAVE_SERVICE_SNOC] = &srvc_snoc, }; +static const struct regmap_config sc8280xp_system_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1a400, + .fast_io = true, +}; + static const struct qcom_icc_desc sc8280xp_system_noc_main = { + .config = &sc8280xp_system_noc_regmap_config, .nodes = system_noc_main_nodes, .num_nodes = ARRAY_SIZE(system_noc_main_nodes), .bcms = system_noc_main_bcms, From b09a9fc46657f210e5fbc5299b7624ff54f7ee6e Mon Sep 17 00:00:00 2001 From: Raviteja Laggyshetty Date: Wed, 27 May 2026 05:37:09 +0000 Subject: [PATCH 149/513] dt-bindings: interconnect: qcom,x1e80100-rpmh: add clocks property to enable QoS Some interconnect nodes on X1E80100 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: Raviteja Laggyshetty Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260527-x1e80100_qos-v2-1-305c6539e6d2@oss.qualcomm.com Signed-off-by: Georgi Djakov --- .../interconnect/qcom,x1e80100-rpmh.yaml | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,x1e80100-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,x1e80100-rpmh.yaml index 0840b0ec6e27..d863cddb21ac 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,x1e80100-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,x1e80100-rpmh.yaml @@ -46,6 +46,10 @@ properties: reg: maxItems: 1 + clocks: + minItems: 1 + maxItems: 6 + required: - compatible @@ -65,6 +69,73 @@ allOf: required: - reg + - if: + properties: + compatible: + contains: + enum: + - qcom,x1e80100-aggre1-noc + then: + properties: + clocks: + items: + - description: aggre UFS PHY AXI clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,x1e80100-usb-north-anoc + then: + properties: + clocks: + items: + - description: aggre USB2 PRIM AXI clock + - description: aggre USB3 MP AXI clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,x1e80100-usb-south-anoc + then: + properties: + clocks: + items: + - description: aggre USB3 PRIM AXI clock + - description: aggre USB3 SEC AXI clock + - description: aggre USB3 TERT AXI clock + - description: aggre USB4_0 AXI clock + - description: aggre USB4_1 AXI clock + - description: aggre USB4_2 AXI clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,x1e80100-aggre2-noc + - qcom,x1e80100-clk-virt + - qcom,x1e80100-cnoc-cfg + - qcom,x1e80100-cnoc-main + - qcom,x1e80100-gem-noc + - qcom,x1e80100-lpass-ag-noc + - qcom,x1e80100-lpass-lpiaon-noc + - qcom,x1e80100-lpass-lpicx-noc + - qcom,x1e80100-mc-virt + - qcom,x1e80100-mmss-noc + - qcom,x1e80100-nsp-noc + - qcom,x1e80100-pcie-center-anoc + - qcom,x1e80100-pcie-north-anoc + - qcom,x1e80100-pcie-south-anoc + - qcom,x1e80100-system-noc + - qcom,x1e80100-usb-center-anoc + then: + properties: + clocks: false + unevaluatedProperties: false examples: @@ -80,4 +151,5 @@ examples: reg = <0x016e0000 0x14400>; #interconnect-cells = <2>; qcom,bcm-voters = <&apps_bcm_voter>; + clocks = <&gcc_aggre_ufs_phy_axi_clk>; }; From 5a8b2cc36e796d595c9d97eab23c2be22805cc1c Mon Sep 17 00:00:00 2001 From: Raviteja Laggyshetty Date: Wed, 27 May 2026 05:37:10 +0000 Subject: [PATCH 150/513] interconnect: qcom: x1e80100: enable QoS configuration Enable QoS configuration for master ports with predefined priority and urgency forwarding. Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Signed-off-by: Raviteja Laggyshetty Link: https://patch.msgid.link/20260527-x1e80100_qos-v2-2-305c6539e6d2@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/x1e80100.c | 485 +++++++++++++++++++++++++++ 1 file changed, 485 insertions(+) diff --git a/drivers/interconnect/qcom/x1e80100.c b/drivers/interconnect/qcom/x1e80100.c index 2ba2823c7860..8075e0ff2059 100644 --- a/drivers/interconnect/qcom/x1e80100.c +++ b/drivers/interconnect/qcom/x1e80100.c @@ -173,6 +173,13 @@ 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 = { 0xb000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -181,6 +188,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 = { 0xc000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -189,6 +203,13 @@ static struct qcom_icc_node xm_sdc4 = { .name = "xm_sdc4", .channels = 1, .buswidth = 8, + .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 }, }; @@ -197,6 +218,13 @@ 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 = { 0xe000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -205,6 +233,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 = { 0x16000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -213,6 +248,13 @@ 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 = { 0x11000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -221,6 +263,13 @@ 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 = { 0x12000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -237,6 +286,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 = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -245,6 +301,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 = { 0x14000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -253,6 +316,13 @@ 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 = { 0x15000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -337,6 +407,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 = { 0x271000 }, + .prio = 1, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -345,6 +422,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 = { 0x27d000 }, + .prio = 3, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -353,6 +437,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 = { 0x273000 }, + .prio = 6, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -370,6 +461,13 @@ static struct qcom_icc_node qnm_gpu = { .name = "qnm_gpu", .channels = 4, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 4, + .port_offsets = { 0x51000, 0x58000, 0xd1000, 0xd8000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -378,6 +476,13 @@ static struct qcom_icc_node qnm_lpass = { .name = "qnm_lpass", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x275000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, &qns_pcie }, @@ -387,6 +492,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 = { 0x53000, 0xd3000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -395,6 +507,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 = { 0x55000, 0xd5000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -403,6 +522,13 @@ static struct qcom_icc_node qnm_nsp_noc = { .name = "qnm_nsp_noc", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x57000, 0xd7000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, &qns_pcie }, @@ -412,6 +538,13 @@ static struct qcom_icc_node qnm_pcie = { .name = "qnm_pcie", .channels = 1, .buswidth = 64, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x277000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -420,6 +553,13 @@ static struct qcom_icc_node qnm_snoc_sf = { .name = "qnm_snoc_sf", .channels = 1, .buswidth = 64, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x27b000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, &qns_pcie }, @@ -429,6 +569,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 = { 0x27f000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_llcc }, }; @@ -469,6 +616,13 @@ static struct qcom_icc_node qnm_av1_enc = { .name = "qnm_av1_enc", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2f000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -477,6 +631,13 @@ 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 = { 0x28000, 0x29000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -485,6 +646,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 = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -493,6 +661,13 @@ 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 = { 0x2b000, 0x2c000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -501,6 +676,13 @@ static struct qcom_icc_node qnm_eva = { .name = "qnm_eva", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x33000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -509,6 +691,13 @@ 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 = { 0x2d000, 0x2e000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -517,6 +706,13 @@ static struct qcom_icc_node qnm_video = { .name = "qnm_video", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x30000, 0x31000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -525,6 +721,13 @@ static struct qcom_icc_node qnm_video_cv_cpu = { .name = "qnm_video_cv_cpu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x32000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -533,6 +736,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 = { 0x34000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -573,6 +783,13 @@ static struct qcom_icc_node xm_pcie_3 = { .name = "xm_pcie_3", .channels = 1, .buswidth = 64, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x7000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_north_gem_noc }, }; @@ -581,6 +798,13 @@ static struct qcom_icc_node xm_pcie_4 = { .name = "xm_pcie_4", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x8000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_north_gem_noc }, }; @@ -589,6 +813,13 @@ static struct qcom_icc_node xm_pcie_5 = { .name = "xm_pcie_5", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x9000 }, + .prio = 3, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_north_gem_noc }, }; @@ -597,6 +828,13 @@ static struct qcom_icc_node xm_pcie_0 = { .name = "xm_pcie_0", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x9000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_south_gem_noc }, }; @@ -605,6 +843,13 @@ static struct qcom_icc_node xm_pcie_1 = { .name = "xm_pcie_1", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_south_gem_noc }, }; @@ -613,6 +858,13 @@ static struct qcom_icc_node xm_pcie_2 = { .name = "xm_pcie_2", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_pcie_south_gem_noc }, }; @@ -621,6 +873,13 @@ static struct qcom_icc_node xm_pcie_6a = { .name = "xm_pcie_6a", .channels = 1, .buswidth = 32, + .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_pcie_south_gem_noc }, }; @@ -629,6 +888,13 @@ static struct qcom_icc_node xm_pcie_6b = { .name = "xm_pcie_6b", .channels = 1, .buswidth = 16, + .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_pcie_south_gem_noc }, }; @@ -653,6 +919,13 @@ static struct qcom_icc_node qnm_gic = { .name = "qnm_gic", .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 }, }; @@ -685,6 +958,13 @@ static struct qcom_icc_node xm_usb2_0 = { .name = "xm_usb2_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x6000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_north_snoc }, }; @@ -693,6 +973,13 @@ static struct qcom_icc_node xm_usb3_mp = { .name = "xm_usb3_mp", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x7000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_north_snoc }, }; @@ -701,6 +988,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 = { 0xa000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_south_snoc }, }; @@ -709,6 +1003,13 @@ static struct qcom_icc_node xm_usb3_1 = { .name = "xm_usb3_1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_south_snoc }, }; @@ -717,6 +1018,13 @@ static struct qcom_icc_node xm_usb3_2 = { .name = "xm_usb3_2", .channels = 1, .buswidth = 8, + .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_aggre_usb_south_snoc }, }; @@ -725,6 +1033,13 @@ static struct qcom_icc_node xm_usb4_0 = { .name = "xm_usb4_0", .channels = 1, .buswidth = 16, + .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_aggre_usb_south_snoc }, }; @@ -733,6 +1048,13 @@ static struct qcom_icc_node xm_usb4_1 = { .name = "xm_usb4_1", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, .num_links = 1, .link_nodes = { &qns_aggre_usb_south_snoc }, }; @@ -741,6 +1063,13 @@ static struct qcom_icc_node xm_usb4_2 = { .name = "xm_usb4_2", .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_aggre_usb_south_snoc }, }; @@ -1466,11 +1795,21 @@ static struct qcom_icc_node * const aggre1_noc_nodes[] = { [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, }; +static const struct regmap_config x1e80100_aggre1_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x14400, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_aggre1_noc = { + .config = &x1e80100_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[] = { @@ -1488,7 +1827,16 @@ static struct qcom_icc_node * const aggre2_noc_nodes[] = { [SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc, }; +static const struct regmap_config x1e80100_aggre2_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1c400, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_aggre2_noc = { + .config = &x1e80100_aggre2_noc_regmap_config, .nodes = aggre2_noc_nodes, .num_nodes = ARRAY_SIZE(aggre2_noc_nodes), .bcms = aggre2_noc_bcms, @@ -1573,7 +1921,16 @@ static struct qcom_icc_node * const cnoc_cfg_nodes[] = { [SLAVE_TCU] = &xs_sys_tcu_cfg, }; +static const struct regmap_config x1e80100_cnoc_cfg_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x6600, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_cnoc_cfg = { + .config = &x1e80100_cnoc_cfg_regmap_config, .nodes = cnoc_cfg_nodes, .num_nodes = ARRAY_SIZE(cnoc_cfg_nodes), .bcms = cnoc_cfg_bcms, @@ -1603,7 +1960,16 @@ static struct qcom_icc_node * const cnoc_main_nodes[] = { [SLAVE_PCIE_6B] = &xs_pcie_6b, }; +static const struct regmap_config x1e80100_cnoc_main_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x14400, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_cnoc_main = { + .config = &x1e80100_cnoc_main_regmap_config, .nodes = cnoc_main_nodes, .num_nodes = ARRAY_SIZE(cnoc_main_nodes), .bcms = cnoc_main_bcms, @@ -1633,7 +1999,16 @@ static struct qcom_icc_node * const gem_noc_nodes[] = { [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, }; +static const struct regmap_config x1e80100_gem_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x311200, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_gem_noc = { + .config = &x1e80100_gem_noc_regmap_config, .nodes = gem_noc_nodes, .num_nodes = ARRAY_SIZE(gem_noc_nodes), .bcms = gem_noc_bcms, @@ -1648,7 +2023,16 @@ static struct qcom_icc_node * const lpass_ag_noc_nodes[] = { [SLAVE_LPASS_GEM_NOC] = &qns_lpass_ag_noc_gemnoc, }; +static const struct regmap_config x1e80100_lpass_ag_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xe080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_lpass_ag_noc = { + .config = &x1e80100_lpass_ag_noc_regmap_config, .nodes = lpass_ag_noc_nodes, .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), .bcms = lpass_ag_noc_bcms, @@ -1664,7 +2048,16 @@ static struct qcom_icc_node * const lpass_lpiaon_noc_nodes[] = { [SLAVE_LPIAON_NOC_LPASS_AG_NOC] = &qns_lpass_aggnoc, }; +static const struct regmap_config x1e80100_lpass_lpiaon_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x19080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_lpass_lpiaon_noc = { + .config = &x1e80100_lpass_lpiaon_noc_regmap_config, .nodes = lpass_lpiaon_noc_nodes, .num_nodes = ARRAY_SIZE(lpass_lpiaon_noc_nodes), .bcms = lpass_lpiaon_noc_bcms, @@ -1679,7 +2072,16 @@ static struct qcom_icc_node * const lpass_lpicx_noc_nodes[] = { [SLAVE_LPICX_NOC_LPIAON_NOC] = &qns_lpi_aon_noc, }; +static const struct regmap_config x1e80100_lpass_lpicx_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3a200, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_lpass_lpicx_noc = { + .config = &x1e80100_lpass_lpicx_noc_regmap_config, .nodes = lpass_lpicx_noc_nodes, .num_nodes = ARRAY_SIZE(lpass_lpicx_noc_nodes), .bcms = lpass_lpicx_noc_bcms, @@ -1724,7 +2126,16 @@ static struct qcom_icc_node * const mmss_noc_nodes[] = { [SLAVE_SERVICE_MNOC] = &srvc_mnoc, }; +static const struct regmap_config x1e80100_mmss_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x5b800, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_mmss_noc = { + .config = &x1e80100_mmss_noc_regmap_config, .nodes = mmss_noc_nodes, .num_nodes = ARRAY_SIZE(mmss_noc_nodes), .bcms = mmss_noc_bcms, @@ -1740,7 +2151,16 @@ static struct qcom_icc_node * const nsp_noc_nodes[] = { [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc, }; +static const struct regmap_config x1e80100_nsp_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xe080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_nsp_noc = { + .config = &x1e80100_nsp_noc_regmap_config, .nodes = nsp_noc_nodes, .num_nodes = ARRAY_SIZE(nsp_noc_nodes), .bcms = nsp_noc_bcms, @@ -1757,7 +2177,16 @@ static struct qcom_icc_node * const pcie_center_anoc_nodes[] = { [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc, }; +static const struct regmap_config x1e80100_pcie_center_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x7000, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_pcie_center_anoc = { + .config = &x1e80100_pcie_center_anoc_regmap_config, .nodes = pcie_center_anoc_nodes, .num_nodes = ARRAY_SIZE(pcie_center_anoc_nodes), .bcms = pcie_center_anoc_bcms, @@ -1774,7 +2203,16 @@ static struct qcom_icc_node * const pcie_north_anoc_nodes[] = { [SLAVE_PCIE_NORTH] = &qns_pcie_north_gem_noc, }; +static const struct regmap_config x1e80100_pcie_north_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x9080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_pcie_north_anoc = { + .config = &x1e80100_pcie_north_anoc_regmap_config, .nodes = pcie_north_anoc_nodes, .num_nodes = ARRAY_SIZE(pcie_north_anoc_nodes), .bcms = pcie_north_anoc_bcms, @@ -1793,7 +2231,16 @@ static struct qcom_icc_node * const pcie_south_anoc_nodes[] = { [SLAVE_PCIE_SOUTH] = &qns_pcie_south_gem_noc, }; +static const struct regmap_config x1e80100_pcie_south_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xd080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_pcie_south_anoc = { + .config = &x1e80100_pcie_south_anoc_regmap_config, .nodes = pcie_south_anoc_nodes, .num_nodes = ARRAY_SIZE(pcie_south_anoc_nodes), .bcms = pcie_south_anoc_bcms, @@ -1815,7 +2262,16 @@ static struct qcom_icc_node * const system_noc_nodes[] = { [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf, }; +static const struct regmap_config x1e80100_system_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1c080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_system_noc = { + .config = &x1e80100_system_noc_regmap_config, .nodes = system_noc_nodes, .num_nodes = ARRAY_SIZE(system_noc_nodes), .bcms = system_noc_bcms, @@ -1831,7 +2287,16 @@ static struct qcom_icc_node * const usb_center_anoc_nodes[] = { [SLAVE_USB_NOC_SNOC] = &qns_aggre_usb_snoc, }; +static const struct regmap_config x1e80100_usb_center_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x8800, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_usb_center_anoc = { + .config = &x1e80100_usb_center_anoc_regmap_config, .nodes = usb_center_anoc_nodes, .num_nodes = ARRAY_SIZE(usb_center_anoc_nodes), .bcms = usb_center_anoc_bcms, @@ -1847,11 +2312,21 @@ static struct qcom_icc_node * const usb_north_anoc_nodes[] = { [SLAVE_AGGRE_USB_NORTH] = &qns_aggre_usb_north_snoc, }; +static const struct regmap_config x1e80100_usb_north_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x7080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_usb_north_anoc = { + .config = &x1e80100_usb_north_anoc_regmap_config, .nodes = usb_north_anoc_nodes, .num_nodes = ARRAY_SIZE(usb_north_anoc_nodes), .bcms = usb_north_anoc_bcms, .num_bcms = ARRAY_SIZE(usb_north_anoc_bcms), + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const usb_south_anoc_bcms[] = { @@ -1867,11 +2342,21 @@ static struct qcom_icc_node * const usb_south_anoc_nodes[] = { [SLAVE_AGGRE_USB_SOUTH] = &qns_aggre_usb_south_snoc, }; +static const struct regmap_config x1e80100_usb_south_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xf080, + .fast_io = true, +}; + static const struct qcom_icc_desc x1e80100_usb_south_anoc = { + .config = &x1e80100_usb_south_anoc_regmap_config, .nodes = usb_south_anoc_nodes, .num_nodes = ARRAY_SIZE(usb_south_anoc_nodes), .bcms = usb_south_anoc_bcms, .num_bcms = ARRAY_SIZE(usb_south_anoc_bcms), + .qos_requires_clocks = true, }; static const struct of_device_id qnoc_of_match[] = { From 070c42f9e6d937087ac95cd00381d36d3b3c1de9 Mon Sep 17 00:00:00 2001 From: Raviteja Laggyshetty Date: Wed, 3 Jun 2026 11:26:12 +0000 Subject: [PATCH 151/513] dt-bindings: interconnect: qcom,osm-l3: Add EPSS L3 DT binding for Qualcomm Shikra SoC Document the EPSS L3 interconnect provider binding for Qualcomm Shikra SoC. The Shikra EPSS L3 block is similar to existing Qualcomm EPSS/OSM L3 providers, but supports only up to 12 frequency lookup table entries. Co-developed-by: Odelu Kukatla Signed-off-by: Odelu Kukatla Signed-off-by: Raviteja Laggyshetty Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260603-shikra_epss_l3-v3-1-3c2e0b796e78@oss.qualcomm.com Signed-off-by: Georgi Djakov --- Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml index 41b9f758bf8b..3b8ebe17a976 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml @@ -17,6 +17,8 @@ description: properties: compatible: oneOf: + - enum: + - qcom,shikra-epss-l3 - items: - enum: - qcom,sc7180-osm-l3 From b148c1f785f3a641fa58a23da5f344a0dfa038a1 Mon Sep 17 00:00:00 2001 From: Raviteja Laggyshetty Date: Wed, 3 Jun 2026 11:26:13 +0000 Subject: [PATCH 152/513] interconnect: qcom: Add EPSS L3 scaling support for Shikra SoC Add Epoch Subsystem (EPSS) L3 interconnect provider support on Qualcomm Shikra SoC. The EPSS L3 block on Shikra SoC is similar to existing Qualcomm EPSS/OSM L3 providers, but supports only up to 12 frequency lookup table entries. Reading beyond the supported LUT entries can expose incorrect frequencies. Add shikra-specific EPSS descriptor shikra_epss_l3_perf_state that reuses existing EPSS configuration with appropriate LUT entries limit. Co-developed-by: Odelu Kukatla Signed-off-by: Odelu Kukatla Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Signed-off-by: Raviteja Laggyshetty Link: https://patch.msgid.link/20260603-shikra_epss_l3-v3-2-3c2e0b796e78@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/osm-l3.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/osm-l3.c b/drivers/interconnect/qcom/osm-l3.c index b33f00da1880..ecad636b53e0 100644 --- a/drivers/interconnect/qcom/osm-l3.c +++ b/drivers/interconnect/qcom/osm-l3.c @@ -60,6 +60,7 @@ struct qcom_osm_l3_desc { unsigned int lut_row_size; unsigned int reg_freq_lut; unsigned int reg_perf_state; + unsigned int lut_max_entries; }; #define DEFINE_QNODE(_name, _buswidth) \ @@ -90,6 +91,7 @@ static const struct qcom_osm_l3_desc osm_l3 = { .lut_row_size = OSM_LUT_ROW_SIZE, .reg_freq_lut = OSM_REG_FREQ_LUT, .reg_perf_state = OSM_REG_PERF_STATE, + .lut_max_entries = LUT_MAX_ENTRIES, }; static const struct qcom_osm_l3_desc epss_l3_perf_state = { @@ -98,6 +100,16 @@ static const struct qcom_osm_l3_desc epss_l3_perf_state = { .lut_row_size = EPSS_LUT_ROW_SIZE, .reg_freq_lut = EPSS_REG_FREQ_LUT, .reg_perf_state = EPSS_REG_PERF_STATE, + .lut_max_entries = LUT_MAX_ENTRIES, +}; + +static const struct qcom_osm_l3_desc shikra_epss_l3_perf_state = { + .nodes = epss_l3_nodes, + .num_nodes = ARRAY_SIZE(epss_l3_nodes), + .lut_row_size = EPSS_LUT_ROW_SIZE, + .reg_freq_lut = EPSS_REG_FREQ_LUT, + .reg_perf_state = EPSS_REG_PERF_STATE, + .lut_max_entries = 12, }; static const struct qcom_osm_l3_desc epss_l3_l3_vote = { @@ -106,6 +118,7 @@ static const struct qcom_osm_l3_desc epss_l3_l3_vote = { .lut_row_size = EPSS_LUT_ROW_SIZE, .reg_freq_lut = EPSS_REG_FREQ_LUT, .reg_perf_state = EPSS_REG_L3_VOTE, + .lut_max_entries = LUT_MAX_ENTRIES, }; static int qcom_osm_l3_set(struct icc_node *src, struct icc_node *dst) @@ -189,7 +202,7 @@ static int qcom_osm_l3_probe(struct platform_device *pdev) qp->reg_perf_state = desc->reg_perf_state; - for (i = 0; i < LUT_MAX_ENTRIES; i++) { + for (i = 0; i < desc->lut_max_entries; i++) { info = readl_relaxed(qp->base + desc->reg_freq_lut + i * desc->lut_row_size); src = FIELD_GET(LUT_SRC, info); @@ -272,6 +285,7 @@ static const struct of_device_id osm_l3_of_match[] = { { .compatible = "qcom,sc7180-osm-l3", .data = &osm_l3 }, { .compatible = "qcom,sc7280-epss-l3", .data = &epss_l3_perf_state }, { .compatible = "qcom,sdm845-osm-l3", .data = &osm_l3 }, + { .compatible = "qcom,shikra-epss-l3", .data = &shikra_epss_l3_perf_state }, { .compatible = "qcom,sm8150-osm-l3", .data = &osm_l3 }, { .compatible = "qcom,sc8180x-osm-l3", .data = &osm_l3 }, { .compatible = "qcom,sm8250-epss-l3", .data = &epss_l3_perf_state }, From 565de44266e64bec1554e86584083b41ab76d9bd Mon Sep 17 00:00:00 2001 From: Raviteja Laggyshetty Date: Mon, 22 Jun 2026 06:34:45 +0000 Subject: [PATCH 153/513] dt-bindings: interconnect: qcom: document the RPMh Network-On-Chip interconnect in Maili SoC Document the RPMh Network-On-Chip interconnect for the Qualcomm Maili SoC. Co-developed-by: Odelu Kukatla Signed-off-by: Odelu Kukatla Signed-off-by: Raviteja Laggyshetty Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260622-maili_icc-v2-1-18b5ac08c04f@oss.qualcomm.com Signed-off-by: Georgi Djakov --- .../interconnect/qcom,maili-rpmh.yaml | 127 +++++++++++++ .../interconnect/qcom,maili-rpmh.h | 171 ++++++++++++++++++ 2 files changed, 298 insertions(+) create mode 100644 Documentation/devicetree/bindings/interconnect/qcom,maili-rpmh.yaml create mode 100644 include/dt-bindings/interconnect/qcom,maili-rpmh.h diff --git a/Documentation/devicetree/bindings/interconnect/qcom,maili-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,maili-rpmh.yaml new file mode 100644 index 000000000000..3db8d8b23219 --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,maili-rpmh.yaml @@ -0,0 +1,127 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,maili-rpmh.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm RPMh Network-On-Chip Interconnect on Maili SoC + +maintainers: + - Raviteja Laggyshetty + +description: | + RPMh interconnect providers support system bandwidth requirements through + RPMh hardware accelerators known as Bus Clock Manager (BCM). The provider is + able to communicate with the BCM through the Resource State Coordinator (RSC) + associated with each execution environment. Provider nodes must point to at + least one RPMh device child node pertaining to their RSC and each provider + can map to multiple RPMh resources. + + See also: include/dt-bindings/interconnect/qcom,maili-rpmh.h + +properties: + compatible: + enum: + - qcom,maili-aggre-noc + - qcom,maili-clk-virt + - qcom,maili-cnoc-main + - qcom,maili-gem-noc + - qcom,maili-llclpi-noc + - qcom,maili-lpass-ag-noc + - qcom,maili-lpass-lpiaon-noc + - qcom,maili-lpass-lpicx-noc + - qcom,maili-mc-virt + - qcom,maili-mmss-noc + - qcom,maili-nsp-noc + - qcom,maili-pcie-anoc + - qcom,maili-stdst-cfg + - qcom,maili-stdst-main + - qcom,maili-system-noc + + reg: + maxItems: 1 + + clocks: + minItems: 2 + maxItems: 3 + +required: + - compatible + +allOf: + - $ref: qcom,rpmh-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - qcom,maili-clk-virt + - qcom,maili-mc-virt + then: + properties: + reg: false + else: + required: + - reg + + - if: + properties: + compatible: + contains: + enum: + - qcom,maili-aggre-noc + then: + properties: + clocks: + items: + - description: aggre UFS PHY AXI clock + - description: aggre USB3 PRIM AXI clock + - description: RPMH CC IPA clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,maili-pcie-anoc + then: + properties: + clocks: + items: + - description: aggre-NOC PCIe AXI clock + - description: cfg-NOC PCIe a-NOC AHB clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,maili-aggre-noc + - qcom,maili-pcie-anoc + then: + required: + - clocks + else: + properties: + clocks: false + +unevaluatedProperties: false + +examples: + - | + gem_noc: interconnect@31100000 { + compatible = "qcom,maili-gem-noc"; + reg = <0x31100000 0x160200>; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + aggre_noc: interconnect@f00000 { + compatible = "qcom,maili-aggre-noc"; + reg = <0x00f00000 0x56200>; + #interconnect-cells = <2>; + clocks = <&gcc_phy_axi_clk>, + <&gcc_prim_axi_clk>, + <&rpmhcc_ipa_clk>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; diff --git a/include/dt-bindings/interconnect/qcom,maili-rpmh.h b/include/dt-bindings/interconnect/qcom,maili-rpmh.h new file mode 100644 index 000000000000..ae3e48b14eab --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,maili-rpmh.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_MAILI_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_MAILI_H + +#define MASTER_QSPI_0 0 +#define MASTER_QUP_2 1 +#define MASTER_QUP_3 2 +#define MASTER_QUP_4 3 +#define MASTER_QUP_5 4 +#define MASTER_CRYPTO 5 +#define MASTER_IPA 6 +#define MASTER_QUP_1 7 +#define MASTER_SOCCP_PROC 8 +#define MASTER_QDSS_ETR 9 +#define MASTER_QDSS_ETR_1 10 +#define MASTER_SDCC_2 11 +#define MASTER_SDCC_4 12 +#define MASTER_UFS_MEM 13 +#define MASTER_USB3 14 +#define SLAVE_A1NOC_SNOC 15 + +#define MASTER_DDR_EFF_VETO 0 +#define MASTER_QUP_CORE_0 1 +#define MASTER_QUP_CORE_1 2 +#define MASTER_QUP_CORE_2 3 +#define MASTER_QUP_CORE_3 4 +#define MASTER_QUP_CORE_4 5 +#define MASTER_QUP_CORE_5 6 +#define SLAVE_DDR_EFF_VETO 7 +#define SLAVE_QUP_CORE_0 8 +#define SLAVE_QUP_CORE_1 9 +#define SLAVE_QUP_CORE_2 10 +#define SLAVE_QUP_CORE_3 11 +#define SLAVE_QUP_CORE_4 12 +#define SLAVE_QUP_CORE_5 13 + +#define MASTER_GEM_NOC_CNOC 0 +#define MASTER_GEM_NOC_PCIE_SNOC 1 +#define SLAVE_AOSS 2 +#define SLAVE_IPA_CFG 3 +#define SLAVE_IPC_ROUTER_FENCE 4 +#define SLAVE_SOCCP 5 +#define SLAVE_TME_CFG 6 +#define SLAVE_CNOC_CFG 7 +#define SLAVE_DDRSS_CFG 8 +#define SLAVE_IMEM 9 +#define SLAVE_PCIE_0 10 +#define SLAVE_PCIE_1 11 + +#define MASTER_GIC 0 +#define MASTER_GPU_TCU 1 +#define MASTER_SYS_TCU 2 +#define MASTER_APPSS_PROC 3 +#define MASTER_GFX3D 4 +#define MASTER_LPASS_GEM_NOC 5 +#define MASTER_MSS_PROC 6 +#define MASTER_MNOC_HF_MEM_NOC 7 +#define MASTER_MNOC_SF_MEM_NOC 8 +#define MASTER_COMPUTE_NOC 9 +#define MASTER_ANOC_PCIE_GEM_NOC 10 +#define MASTER_QPACE 11 +#define MASTER_SNOC_SF_MEM_NOC 12 +#define MASTER_WLAN_Q6 13 +#define SLAVE_GEM_NOC_CNOC 14 +#define SLAVE_LLCC 15 +#define SLAVE_MEM_NOC_PCIE_SNOC 16 + +#define MASTER_LPIAON_NOC_LLCLPI_NOC 0 +#define SLAVE_LPASS_LPI_CC 1 +#define SLAVE_LLCC_ISLAND 2 +#define SLAVE_SERVICE_LLCLPI_NOC 3 +#define SLAVE_SERVICE_LLCLPI_NOC_CHIPCX 4 + +#define MASTER_LPIAON_NOC 0 +#define SLAVE_LPASS_GEM_NOC 1 + +#define MASTER_LPASS_LPINOC 0 +#define SLAVE_LPIAON_NOC_LLCLPI_NOC 1 +#define SLAVE_LPIAON_NOC_LPASS_AG_NOC 2 + +#define MASTER_LPASS_PROC 0 +#define SLAVE_LPICX_NOC_LPIAON_NOC 1 + +#define MASTER_LLCC 0 +#define MASTER_DDR_RT 1 +#define SLAVE_EBI1 2 +#define SLAVE_DDR_RT 3 + +#define MASTER_CAMNOC_HF 0 +#define MASTER_CAMNOC_NRT_ICP_SF 1 +#define MASTER_CAMNOC_RT_CDM_SF 2 +#define MASTER_CAMNOC_SF 3 +#define MASTER_MDP 4 +#define MASTER_MDSS_DCP 5 +#define MASTER_CDSP_HCP 6 +#define MASTER_VIDEO_CV_PROC 7 +#define MASTER_VIDEO_EVA 8 +#define MASTER_VIDEO_MVP 9 +#define MASTER_VIDEO_V_PROC 10 +#define SLAVE_MNOC_HF_MEM_NOC 11 +#define SLAVE_MNOC_SF_MEM_NOC 12 + +#define MASTER_CDSP_PROC 0 +#define SLAVE_CDSP_MEM_NOC 1 + +#define MASTER_PCIE_ANOC_CFG 0 +#define MASTER_PCIE_0 1 +#define MASTER_PCIE_1 2 +#define SLAVE_ANOC_PCIE_GEM_NOC 3 +#define SLAVE_SERVICE_PCIE_ANOC 4 + +#define MASTER_CFG_CENTER 0 +#define MASTER_CFG_EAST 1 +#define MASTER_CFG_MM_HF 2 +#define MASTER_CFG_MM_SF 3 +#define MASTER_CFG_NORTH 4 +#define MASTER_CFG_SOUTH 5 +#define MASTER_CFG_WEST 6 +#define SLAVE_AHB2PHY_SOUTH 7 +#define SLAVE_BOOT_ROM 8 +#define SLAVE_CAMERA_CFG 9 +#define SLAVE_CLK_CTL 10 +#define SLAVE_CRYPTO_CFG 11 +#define SLAVE_DISPLAY_CFG 12 +#define SLAVE_EVA_CFG 13 +#define SLAVE_GFX3D_CFG 14 +#define SLAVE_I2C 15 +#define SLAVE_IMEM_CFG 16 +#define SLAVE_IPC_ROUTER_CFG 17 +#define SLAVE_IRIS_CFG 18 +#define SLAVE_CNOC_MSS 19 +#define SLAVE_PCIE_0_CFG 20 +#define SLAVE_PCIE_1_CFG 21 +#define SLAVE_PRNG 22 +#define SLAVE_QSPI_0 23 +#define SLAVE_QUP_1 24 +#define SLAVE_QUP_2 25 +#define SLAVE_QUP_3 26 +#define SLAVE_QUP_4 27 +#define SLAVE_QUP_5 28 +#define SLAVE_SDCC_2 29 +#define SLAVE_SDCC_4 30 +#define SLAVE_TLMM 31 +#define SLAVE_UFS_MEM_CFG 32 +#define SLAVE_USB3 33 +#define SLAVE_VSENSE_CTRL_CFG 34 +#define SLAVE_PCIE_ANOC_CFG 35 +#define SLAVE_QDSS_CFG 36 +#define SLAVE_QDSS_STM 37 +#define SLAVE_TCSR 38 +#define SLAVE_TCU 39 + +#define MASTER_CNOC_STARDUST 0 +#define SLAVE_STARDUST_CENTER_CFG 1 +#define SLAVE_STARDUST_EAST_CFG 2 +#define SLAVE_STARDUST_MM_HF_CFG 3 +#define SLAVE_STARDUST_MM_SF_CFG 4 +#define SLAVE_STARDUST_NORTH_CFG 5 +#define SLAVE_STARDUST_SOUTH_CFG 6 +#define SLAVE_STARDUST_WEST_CFG 7 + +#define MASTER_A1NOC_SNOC 0 +#define MASTER_APSS_NOC 1 +#define MASTER_CNOC_SNOC 2 +#define SLAVE_SNOC_GEM_NOC_SF 3 + +#endif From fb8190f09bffaac5005084cf665743aef199b01f Mon Sep 17 00:00:00 2001 From: Raviteja Laggyshetty Date: Mon, 22 Jun 2026 06:34:46 +0000 Subject: [PATCH 154/513] interconnect: qcom: add Maili interconnect provider driver Add driver for the Qualcomm interconnect buses found in Maili 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. Co-developed-by: Odelu Kukatla Signed-off-by: Odelu Kukatla Signed-off-by: Raviteja Laggyshetty Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260622-maili_icc-v2-2-18b5ac08c04f@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/Kconfig | 10 + drivers/interconnect/qcom/Makefile | 2 + drivers/interconnect/qcom/maili.c | 2091 ++++++++++++++++++++++++++++ 3 files changed, 2103 insertions(+) create mode 100644 drivers/interconnect/qcom/maili.c diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 5b8a094ca4ed..5b57f111c63c 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -47,6 +47,16 @@ config INTERCONNECT_QCOM_KAANAPALI This is a driver for the Qualcomm Network-on-Chip on kaanapali-based platforms. +config INTERCONNECT_QCOM_MAILI + tristate "Qualcomm MAILI interconnect driver" + depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + select INTERCONNECT_QCOM_RPMH + select INTERCONNECT_QCOM_BCM_VOTER + default ARCH_QCOM + help + This is a driver for the Qualcomm Network-on-Chip on maili-based + platforms. + config INTERCONNECT_QCOM_MSM8909 tristate "Qualcomm MSM8909 interconnect driver" depends on INTERCONNECT_QCOM diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 1c7d410b40cc..5f139a370bbe 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -8,6 +8,7 @@ qnoc-eliza-objs := eliza.o qnoc-glymur-objs := glymur.o qnoc-hawi-objs := hawi.o qnoc-kaanapali-objs := kaanapali.o +qnoc-maili-objs := maili.o qnoc-milos-objs := milos.o qnoc-msm8909-objs := msm8909.o qnoc-msm8916-objs := msm8916.o @@ -56,6 +57,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM_ELIZA) += qnoc-eliza.o obj-$(CONFIG_INTERCONNECT_QCOM_GLYMUR) += qnoc-glymur.o obj-$(CONFIG_INTERCONNECT_QCOM_HAWI) += qnoc-hawi.o obj-$(CONFIG_INTERCONNECT_QCOM_KAANAPALI) += qnoc-kaanapali.o +obj-$(CONFIG_INTERCONNECT_QCOM_MAILI) += qnoc-maili.o obj-$(CONFIG_INTERCONNECT_QCOM_MILOS) += qnoc-milos.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8909) += qnoc-msm8909.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += qnoc-msm8916.o diff --git a/drivers/interconnect/qcom/maili.c b/drivers/interconnect/qcom/maili.c new file mode 100644 index 000000000000..e5eed7f998f9 --- /dev/null +++ b/drivers/interconnect/qcom/maili.c @@ -0,0 +1,2091 @@ +// 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 ddr_eff_veto_slave = { + .name = "ddr_eff_veto_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup0_core_slave = { + .name = "qup0_core_slave", + .channels = 1, + .buswidth = 4, +}; + +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 qup3_core_slave = { + .name = "qup3_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup4_core_slave = { + .name = "qup4_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup5_core_slave = { + .name = "qup5_core_slave", + .channels = 1, + .buswidth = 4, +}; + +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_fence = { + .name = "qhs_ipc_router_fence", + .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_ddrss_cfg = { + .name = "qss_ddrss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qxs_imem = { + .name = "qxs_imem", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node xs_pcie = { + .name = "xs_pcie", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node xs_pcie_g4x1 = { + .name = "xs_pcie_g4x1", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qhs_lpi_cc = { + .name = "qhs_lpi_cc", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qns_lb = { + .name = "qns_lb", + .channels = 4, + .buswidth = 16, +}; + +static struct qcom_icc_node srvc_llclpi_noc = { + .name = "srvc_llclpi_noc", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node srvc_llclpi_noc_chipcx = { + .name = "srvc_llclpi_noc_chipcx", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node ebi = { + .name = "ebi", + .channels = 4, + .buswidth = 4, +}; + +static struct qcom_icc_node ddr_rt_slave = { + .name = "ddr_rt_slave", + .channels = 4, + .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 qhs_ahb2phy0 = { + .name = "qhs_ahb2phy0", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_boot_rom = { + .name = "qhs_boot_rom", + .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_crypto_cfg = { + .name = "qhs_crypto_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_eva_cfg = { + .name = "qhs_eva_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_i2c = { + .name = "qhs_i2c", + .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_ipc_router = { + .name = "qhs_ipc_router", + .channels = 4, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_iris_cfg = { + .name = "qhs_iris_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_cfg = { + .name = "qhs_pcie_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_pcie_g4x1_cfg = { + .name = "qhs_pcie_g4x1_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_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_qup3 = { + .name = "qhs_qup3", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup4 = { + .name = "qhs_qup4", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup5 = { + .name = "qhs_qup5", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_sdc2 = { + .name = "qhs_sdc2", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_sdc4 = { + .name = "qhs_sdc4", + .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 = { + .name = "qhs_usb3", + .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 qss_qdss_cfg = { + .name = "qss_qdss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_qdss_stm = { + .name = "qss_qdss_stm", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_tcsr = { + .name = "qss_tcsr", + .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 ddr_eff_veto_master = { + .name = "ddr_eff_veto_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &ddr_eff_veto_slave }, +}; + +static struct qcom_icc_node qup0_core_master = { + .name = "qup0_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup0_core_slave }, +}; + +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 qup3_core_master = { + .name = "qup3_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup3_core_slave }, +}; + +static struct qcom_icc_node qup4_core_master = { + .name = "qup4_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup4_core_slave }, +}; + +static struct qcom_icc_node qup5_core_master = { + .name = "qup5_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup5_core_slave }, +}; + +static struct qcom_icc_node qnm_gemnoc_pcie = { + .name = "qnm_gemnoc_pcie", + .channels = 1, + .buswidth = 8, + .num_links = 2, + .link_nodes = { &xs_pcie, &xs_pcie_g4x1 }, +}; + +static struct qcom_icc_node qnm_lpiaon_noc_llclpi_noc = { + .name = "qnm_lpiaon_noc_llclpi_noc", + .channels = 1, + .buswidth = 16, + .num_links = 4, + .link_nodes = { &qhs_lpi_cc, &qns_lb, + &srvc_llclpi_noc, &srvc_llclpi_noc_chipcx }, +}; + +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 ddr_rt_mc = { + .name = "ddr_rt_mc", + .channels = 4, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &ddr_rt_slave }, +}; + +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 qsm_cfg_center = { + .name = "qsm_cfg_center", + .channels = 1, + .buswidth = 4, + .num_links = 14, + .link_nodes = { &qhs_boot_rom, &qhs_clk_ctl, + &qhs_crypto_cfg, &qhs_gpuss_cfg, + &qhs_imem_cfg, &qhs_ipc_router, + &qhs_mss_cfg, &qhs_prng, + &qhs_tlmm, &qhs_vsense_ctrl_cfg, + &qss_qdss_cfg, &qss_qdss_stm, + &qss_tcsr, &xs_sys_tcu_cfg }, +}; + +static struct qcom_icc_node qsm_cfg_east = { + .name = "qsm_cfg_east", + .channels = 1, + .buswidth = 4, + .num_links = 2, + .link_nodes = { &qhs_qup2, &qhs_qup4 }, +}; + +static struct qcom_icc_node qsm_cfg_mm_hf = { + .name = "qsm_cfg_mm_hf", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qhs_display_cfg }, +}; + +static struct qcom_icc_node qsm_cfg_mm_sf = { + .name = "qsm_cfg_mm_sf", + .channels = 1, + .buswidth = 4, + .num_links = 3, + .link_nodes = { &qhs_camera_cfg, &qhs_eva_cfg, + &qhs_iris_cfg }, +}; + +static struct qcom_icc_node qsm_cfg_north = { + .name = "qsm_cfg_north", + .channels = 1, + .buswidth = 4, + .num_links = 3, + .link_nodes = { &qhs_ahb2phy0, &qhs_ufs_mem_cfg, + &qhs_usb3 }, +}; + +static struct qcom_icc_node qsm_cfg_west = { + .name = "qsm_cfg_west", + .channels = 1, + .buswidth = 4, + .num_links = 2, + .link_nodes = { &qhs_qup1, &qhs_sdc2 }, +}; + +static struct qcom_icc_node qns_llcc = { + .name = "qns_llcc", + .channels = 4, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &llcc_mc }, +}; + +static struct qcom_icc_node qns_pcie = { + .name = "qns_pcie", + .channels = 1, + .buswidth = 8, + .num_links = 1, + .link_nodes = { &qnm_gemnoc_pcie }, +}; + +static struct qcom_icc_node qns_llc_lpinoc = { + .name = "qns_llc_lpinoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_lpiaon_noc_llclpi_noc }, +}; + +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 qss_stdst_center_cfg = { + .name = "qss_stdst_center_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_center }, +}; + +static struct qcom_icc_node qss_stdst_east_cfg = { + .name = "qss_stdst_east_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_east }, +}; + +static struct qcom_icc_node qss_stdst_mm_hf_cfg = { + .name = "qss_stdst_mm_hf_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_mm_hf }, +}; + +static struct qcom_icc_node qss_stdst_mm_sf_cfg = { + .name = "qss_stdst_mm_sf_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_mm_sf }, +}; + +static struct qcom_icc_node qss_stdst_north_cfg = { + .name = "qss_stdst_north_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_north }, +}; + +static struct qcom_icc_node qss_stdst_west_cfg = { + .name = "qss_stdst_west_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_west }, +}; + +static struct qcom_icc_node alm_gic = { + .name = "alm_gic", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14d000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_llcc }, +}; + +static struct qcom_icc_node qnm_qpace = { + .name = "qnm_qpace", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x153000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_llcc }, +}; + +static struct qcom_icc_node qsm_cfg_south = { + .name = "qsm_cfg_south", + .channels = 1, + .buswidth = 4, + .num_links = 8, + .link_nodes = { &qhs_i2c, &qhs_pcie_cfg, + &qhs_pcie_g4x1_cfg, &qhs_qspi, + &qhs_qup3, &qhs_qup5, + &qhs_sdc4, &qss_pcie_anoc_cfg }, +}; + +static struct qcom_icc_node qss_stdst_south_cfg = { + .name = "qss_stdst_south_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_south }, +}; + +static struct qcom_icc_node qsm_cnoc_main = { + .name = "qsm_cnoc_main", + .channels = 1, + .buswidth = 4, + .num_links = 7, + .link_nodes = { &qss_stdst_center_cfg, &qss_stdst_east_cfg, + &qss_stdst_mm_hf_cfg, &qss_stdst_mm_sf_cfg, + &qss_stdst_north_cfg, &qss_stdst_south_cfg, + &qss_stdst_west_cfg }, +}; + +static struct qcom_icc_node qss_cfg = { + .name = "qss_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cnoc_main }, +}; + +static struct qcom_icc_node qnm_gemnoc_cnoc = { + .name = "qnm_gemnoc_cnoc", + .channels = 1, + .buswidth = 16, + .num_links = 8, + .link_nodes = { &qhs_aoss, &qhs_ipa, + &qhs_ipc_router_fence, &qhs_soccp, + &qhs_tme_cfg, &qss_cfg, + &qss_ddrss_cfg, &qxs_imem }, +}; + +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_node alm_gpu_tcu = { + .name = "alm_gpu_tcu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x145000 }, + .prio = 1, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +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 = { 0x147000 }, + .prio = 6, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node chm_apps = { + .name = "chm_apps", + .channels = 2, + .buswidth = 32, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +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 = { 0x51000, 0xd1000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_lpass_gemnoc = { + .name = "qnm_lpass_gemnoc", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x149000 }, + .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 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_node qnm_mnoc_hf = { + .name = "qnm_mnoc_hf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x55000, 0xd5000 }, + .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 qnm_mnoc_sf = { + .name = "qnm_mnoc_sf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x57000, 0xd7000 }, + .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 qnm_nsp_gemnoc = { + .name = "qnm_nsp_gemnoc", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x59000, 0xd9000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_pcie = { + .name = "qnm_pcie", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14b000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .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 = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14f000 }, + .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 qnm_wlan_q6 = { + .name = "qnm_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 = 32, + .num_links = 1, + .link_nodes = { &qnm_lpass_gemnoc }, +}; + +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_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_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_gemnoc = { + .name = "qns_pcie_gemnoc", + .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 = 32, + .num_links = 1, + .link_nodes = { &qnm_snoc_sf }, +}; + +static struct qcom_icc_node qnm_lpiaon_noc = { + .name = "qnm_lpiaon_noc", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_lpass_ag_noc_gemnoc }, +}; + +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 = { 0x50000, 0x51000 }, + .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 = { 0x52000, 0x53000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_node qnm_camnoc_nrt_icp_sf = { + .name = "qnm_camnoc_nrt_icp_sf", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x20000 }, + .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_rt_cdm_sf = { + .name = "qnm_camnoc_rt_cdm_sf", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x28000 }, + .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 = { 0x21000, 0x22000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_mdss_dcp = { + .name = "qnm_mdss_dcp", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x29000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_vapss_hcp = { + .name = "qnm_vapss_hcp", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_cv_cpu = { + .name = "qnm_video_cv_cpu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x24000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_eva = { + .name = "qnm_video_eva", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x25000, 0x26000 }, + .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 = { 0x23000 }, + .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 = { 0x27000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_nsp = { + .name = "qnm_nsp", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_nsp_gemnoc }, +}; + +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 = { 0xc000 }, + .prio = 3, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_pcie_gemnoc }, +}; + +static struct qcom_icc_node xm_pcie_g4x1 = { + .name = "xm_pcie_g4x1", + .channels = 1, + .buswidth = 8, + .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_pcie_gemnoc }, +}; + +static struct qcom_icc_node qnm_aggre_noc = { + .name = "qnm_aggre_noc", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x20000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qnm_apss_noc = { + .name = "qnm_apss_noc", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1e000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .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 = { 0x1f000 }, + .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 = 32, + .num_links = 1, + .link_nodes = { &qnm_aggre_noc }, +}; + +static struct qcom_icc_node qns_lpass_aggnoc = { + .name = "qns_lpass_aggnoc", + .channels = 1, + .buswidth = 32, + .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 = { 0x49000 }, + .prio = 0, + .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 = { 0x48000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup3 = { + .name = "qhm_qup3", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x46000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup4 = { + .name = "qhm_qup4", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x47000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup5 = { + .name = "qhm_qup5", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4e000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qxm_crypto = { + .name = "qxm_crypto", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x40000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qxm_ipa = { + .name = "qxm_ipa", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x41000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qxm_qup1 = { + .name = "qxm_qup1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4d000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_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 = { 0x45000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_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 = { 0x42000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_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 = { 0x43000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_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 = { 0x44000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_sdc4 = { + .name = "xm_sdc4", + .channels = 1, + .buswidth = 8, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_ufs_mem = { + .name = "xm_ufs_mem", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4b000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_usb3 = { + .name = "xm_usb3", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4c000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qnm_lpass_lpinoc = { + .name = "qnm_lpass_lpinoc", + .channels = 1, + .buswidth = 32, + .num_links = 2, + .link_nodes = { &qns_llc_lpinoc, &qns_lpass_aggnoc }, +}; + +static struct qcom_icc_node qns_lpi_aon_noc = { + .name = "qns_lpi_aon_noc", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_lpass_lpinoc }, +}; + +static struct qcom_icc_node qnm_lpinoc_dsp_qns4m = { + .name = "qnm_lpinoc_dsp_qns4m", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_lpi_aon_noc }, +}; + +static struct qcom_icc_bcm bcm_acv = { + .name = "ACV", + .enable_mask = BIT(3), + .num_nodes = 1, + .nodes = { &ebi }, +}; + +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 = 25, + .nodes = { &qnm_gemnoc_cnoc, &qnm_gemnoc_pcie, + &qhs_aoss, &qhs_ipa, + &qhs_ipc_router_fence, &qhs_soccp, + &qhs_tme_cfg, &qss_cfg, + &qss_ddrss_cfg, &qxs_imem, + &xs_pcie, &xs_pcie_g4x1, + &qsm_cfg_center, &qsm_cfg_east, + &qsm_cfg_mm_hf, &qsm_cfg_mm_sf, + &qsm_cfg_north, &qsm_cfg_south, + &qsm_cfg_west, &qhs_ahb2phy0, + &qhs_boot_rom, &qhs_camera_cfg, + &qhs_clk_ctl, &qhs_crypto_cfg, + &qhs_eva_cfg }, +}; + +static struct qcom_icc_bcm bcm_cn1 = { + .name = "CN1", + .num_nodes = 6, + .nodes = { &qhs_display_cfg, &qhs_qup1, + &qhs_qup2, &qhs_qup3, + &qhs_qup4, &qhs_qup5 }, +}; + +static struct qcom_icc_bcm bcm_co0 = { + .name = "CO0", + .enable_mask = BIT(0), + .num_nodes = 2, + .nodes = { &qnm_nsp, &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_bcm bcm_de0 = { + .name = "DE0", + .enable_mask = BIT(0), + .num_nodes = 1, + .nodes = { &ddr_eff_veto_slave }, +}; + +static struct qcom_icc_bcm bcm_lp0 = { + .name = "LP0", + .num_nodes = 5, + .nodes = { &qnm_lpiaon_noc_llclpi_noc, &qns_lb, + &qnm_lpass_lpinoc, &qns_llc_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_mc5 = { + .name = "MC5", + .num_nodes = 1, + .nodes = { &ddr_rt_slave }, +}; + +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 = 9, + .nodes = { &qnm_camnoc_hf, &qnm_camnoc_nrt_icp_sf, + &qnm_camnoc_rt_cdm_sf, &qnm_camnoc_sf, + &qnm_vapss_hcp, &qnm_video_cv_cpu, + &qnm_video_mvp, &qnm_video_v_cpu, + &qns_mem_noc_sf }, +}; + +static struct qcom_icc_bcm bcm_qpc0 = { + .name = "QPC0", + .num_nodes = 1, + .nodes = { &qnm_qpace }, +}; + +static struct qcom_icc_bcm bcm_qup0 = { + .name = "QUP0", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup0_core_slave }, +}; + +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_qup3 = { + .name = "QUP3", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup3_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup4 = { + .name = "QUP4", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup4_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup5 = { + .name = "QUP5", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup5_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 = 15, + .nodes = { &alm_gic, &alm_gpu_tcu, + &alm_sys_tcu, &chm_apps, + &qnm_gpu, &qnm_lpass_gemnoc, + &qnm_mdsp, &qnm_mnoc_hf, + &qnm_mnoc_sf, &qnm_nsp_gemnoc, + &qnm_pcie, &qnm_snoc_sf, + &qnm_wlan_q6, &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_aggre_noc }, +}; + +static struct qcom_icc_bcm bcm_sn3 = { + .name = "SN3", + .num_nodes = 1, + .nodes = { &qns_pcie_gemnoc }, +}; + +static struct qcom_icc_bcm * const aggre1_noc_bcms[] = { + &bcm_ce0, +}; + +static struct qcom_icc_node * const aggre1_noc_nodes[] = { + [MASTER_QSPI_0] = &qhm_qspi, + [MASTER_QUP_2] = &qhm_qup2, + [MASTER_QUP_3] = &qhm_qup3, + [MASTER_QUP_4] = &qhm_qup4, + [MASTER_QUP_5] = &qhm_qup5, + [MASTER_CRYPTO] = &qxm_crypto, + [MASTER_IPA] = &qxm_ipa, + [MASTER_QUP_1] = &qxm_qup1, + [MASTER_SOCCP_PROC] = &qxm_soccp, + [MASTER_QDSS_ETR] = &xm_qdss_etr_0, + [MASTER_QDSS_ETR_1] = &xm_qdss_etr_1, + [MASTER_SDCC_2] = &xm_sdc2, + [MASTER_SDCC_4] = &xm_sdc4, + [MASTER_UFS_MEM] = &xm_ufs_mem, + [MASTER_USB3] = &xm_usb3, + [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, +}; + +static const struct regmap_config maili_aggre1_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x56200, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_aggre1_noc = { + .config = &maili_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 clk_virt_bcms[] = { + &bcm_de0, + &bcm_qup0, + &bcm_qup1, + &bcm_qup2, + &bcm_qup3, + &bcm_qup4, + &bcm_qup5, +}; + +static struct qcom_icc_node * const clk_virt_nodes[] = { + [MASTER_DDR_EFF_VETO] = &ddr_eff_veto_master, + [MASTER_QUP_CORE_0] = &qup0_core_master, + [MASTER_QUP_CORE_1] = &qup1_core_master, + [MASTER_QUP_CORE_2] = &qup2_core_master, + [MASTER_QUP_CORE_3] = &qup3_core_master, + [MASTER_QUP_CORE_4] = &qup4_core_master, + [MASTER_QUP_CORE_5] = &qup5_core_master, + [SLAVE_DDR_EFF_VETO] = &ddr_eff_veto_slave, + [SLAVE_QUP_CORE_0] = &qup0_core_slave, + [SLAVE_QUP_CORE_1] = &qup1_core_slave, + [SLAVE_QUP_CORE_2] = &qup2_core_slave, + [SLAVE_QUP_CORE_3] = &qup3_core_slave, + [SLAVE_QUP_CORE_4] = &qup4_core_slave, + [SLAVE_QUP_CORE_5] = &qup5_core_slave, +}; + +static const struct qcom_icc_desc maili_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_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_FENCE] = &qhs_ipc_router_fence, + [SLAVE_SOCCP] = &qhs_soccp, + [SLAVE_TME_CFG] = &qhs_tme_cfg, + [SLAVE_CNOC_CFG] = &qss_cfg, + [SLAVE_DDRSS_CFG] = &qss_ddrss_cfg, + [SLAVE_IMEM] = &qxs_imem, + [SLAVE_PCIE_0] = &xs_pcie, + [SLAVE_PCIE_1] = &xs_pcie_g4x1, +}; + +static const struct regmap_config maili_cnoc_main_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x20000, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_cnoc_main = { + .config = &maili_cnoc_main_regmap_config, + .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_qpc0, + &bcm_sh0, + &bcm_sh1, +}; + +static struct qcom_icc_node * const gem_noc_nodes[] = { + [MASTER_GIC] = &alm_gic, + [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_QPACE] = &qnm_qpace, + [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, + [MASTER_WLAN_Q6] = &qnm_wlan_q6, + [SLAVE_GEM_NOC_CNOC] = &qns_gem_noc_cnoc, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, +}; + +static const struct regmap_config maili_gem_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x160200, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_gem_noc = { + .config = &maili_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), +}; + +static struct qcom_icc_bcm * const llclpi_noc_bcms[] = { + &bcm_lp0, +}; + +static struct qcom_icc_node * const llclpi_noc_nodes[] = { + [MASTER_LPIAON_NOC_LLCLPI_NOC] = &qnm_lpiaon_noc_llclpi_noc, + [SLAVE_LPASS_LPI_CC] = &qhs_lpi_cc, + [SLAVE_LLCC_ISLAND] = &qns_lb, + [SLAVE_SERVICE_LLCLPI_NOC] = &srvc_llclpi_noc, + [SLAVE_SERVICE_LLCLPI_NOC_CHIPCX] = &srvc_llclpi_noc_chipcx, +}; + +static const struct regmap_config maili_llclpi_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x17200, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_llclpi_noc = { + .config = &maili_llclpi_noc_regmap_config, + .nodes = llclpi_noc_nodes, + .num_nodes = ARRAY_SIZE(llclpi_noc_nodes), + .bcms = llclpi_noc_bcms, + .num_bcms = ARRAY_SIZE(llclpi_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 regmap_config maili_lpass_ag_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xc080, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_lpass_ag_noc = { + .config = &maili_lpass_ag_noc_regmap_config, + .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_LLCLPI_NOC] = &qns_llc_lpinoc, + [SLAVE_LPIAON_NOC_LPASS_AG_NOC] = &qns_lpass_aggnoc, +}; + +static const struct regmap_config maili_lpass_lpiaon_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x19080, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_lpass_lpiaon_noc = { + .config = &maili_lpass_lpiaon_noc_regmap_config, + .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] = &qnm_lpinoc_dsp_qns4m, + [SLAVE_LPICX_NOC_LPIAON_NOC] = &qns_lpi_aon_noc, +}; + +static const struct regmap_config maili_lpass_lpicx_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x46080, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_lpass_lpicx_noc = { + .config = &maili_lpass_lpicx_noc_regmap_config, + .nodes = lpass_lpicx_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_lpicx_noc_nodes), +}; + +static struct qcom_icc_bcm * const mc_virt_bcms[] = { + &bcm_acv, + &bcm_mc0, + &bcm_mc5, +}; + +static struct qcom_icc_node * const mc_virt_nodes[] = { + [MASTER_LLCC] = &llcc_mc, + [MASTER_DDR_RT] = &ddr_rt_mc, + [SLAVE_EBI1] = &ebi, + [SLAVE_DDR_RT] = &ddr_rt_slave, +}; + +static const struct qcom_icc_desc maili_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_HF] = &qnm_camnoc_hf, + [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_MDP] = &qnm_mdp, + [MASTER_MDSS_DCP] = &qnm_mdss_dcp, + [MASTER_CDSP_HCP] = &qnm_vapss_hcp, + [MASTER_VIDEO_CV_PROC] = &qnm_video_cv_cpu, + [MASTER_VIDEO_EVA] = &qnm_video_eva, + [MASTER_VIDEO_MVP] = &qnm_video_mvp, + [MASTER_VIDEO_V_PROC] = &qnm_video_v_cpu, + [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf, + [SLAVE_MNOC_SF_MEM_NOC] = &qns_mem_noc_sf, +}; + +static const struct regmap_config maili_mmss_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x5f800, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_mmss_noc = { + .config = &maili_mmss_noc_regmap_config, + .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] = &qnm_nsp, + [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc, +}; + +static const struct regmap_config maili_nsp_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x21280, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_nsp_noc = { + .config = &maili_nsp_noc_regmap_config, + .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_sn3, +}; + +static struct qcom_icc_node * const pcie_anoc_nodes[] = { + [MASTER_PCIE_ANOC_CFG] = &qsm_pcie_anoc_cfg, + [MASTER_PCIE_0] = &xm_pcie, + [MASTER_PCIE_1] = &xm_pcie_g4x1, + [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_gemnoc, + [SLAVE_SERVICE_PCIE_ANOC] = &srvc_pcie_aggre_noc, +}; + +static const struct regmap_config maili_pcie_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x12400, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_pcie_anoc = { + .config = &maili_pcie_anoc_regmap_config, + .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 stdst_cfg_bcms[] = { + &bcm_cn0, + &bcm_cn1, +}; + +static struct qcom_icc_node * const stdst_cfg_nodes[] = { + [MASTER_CFG_CENTER] = &qsm_cfg_center, + [MASTER_CFG_EAST] = &qsm_cfg_east, + [MASTER_CFG_MM_HF] = &qsm_cfg_mm_hf, + [MASTER_CFG_MM_SF] = &qsm_cfg_mm_sf, + [MASTER_CFG_NORTH] = &qsm_cfg_north, + [MASTER_CFG_SOUTH] = &qsm_cfg_south, + [MASTER_CFG_WEST] = &qsm_cfg_west, + [SLAVE_AHB2PHY_SOUTH] = &qhs_ahb2phy0, + [SLAVE_BOOT_ROM] = &qhs_boot_rom, + [SLAVE_CAMERA_CFG] = &qhs_camera_cfg, + [SLAVE_CLK_CTL] = &qhs_clk_ctl, + [SLAVE_CRYPTO_CFG] = &qhs_crypto_cfg, + [SLAVE_DISPLAY_CFG] = &qhs_display_cfg, + [SLAVE_EVA_CFG] = &qhs_eva_cfg, + [SLAVE_GFX3D_CFG] = &qhs_gpuss_cfg, + [SLAVE_I2C] = &qhs_i2c, + [SLAVE_IMEM_CFG] = &qhs_imem_cfg, + [SLAVE_IPC_ROUTER_CFG] = &qhs_ipc_router, + [SLAVE_IRIS_CFG] = &qhs_iris_cfg, + [SLAVE_CNOC_MSS] = &qhs_mss_cfg, + [SLAVE_PCIE_0_CFG] = &qhs_pcie_cfg, + [SLAVE_PCIE_1_CFG] = &qhs_pcie_g4x1_cfg, + [SLAVE_PRNG] = &qhs_prng, + [SLAVE_QSPI_0] = &qhs_qspi, + [SLAVE_QUP_1] = &qhs_qup1, + [SLAVE_QUP_2] = &qhs_qup2, + [SLAVE_QUP_3] = &qhs_qup3, + [SLAVE_QUP_4] = &qhs_qup4, + [SLAVE_QUP_5] = &qhs_qup5, + [SLAVE_SDCC_2] = &qhs_sdc2, + [SLAVE_SDCC_4] = &qhs_sdc4, + [SLAVE_TLMM] = &qhs_tlmm, + [SLAVE_UFS_MEM_CFG] = &qhs_ufs_mem_cfg, + [SLAVE_USB3] = &qhs_usb3, + [SLAVE_VSENSE_CTRL_CFG] = &qhs_vsense_ctrl_cfg, + [SLAVE_PCIE_ANOC_CFG] = &qss_pcie_anoc_cfg, + [SLAVE_QDSS_CFG] = &qss_qdss_cfg, + [SLAVE_QDSS_STM] = &qss_qdss_stm, + [SLAVE_TCSR] = &qss_tcsr, + [SLAVE_TCU] = &xs_sys_tcu_cfg, +}; + +static const struct regmap_config maili_stdst_cfg_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x9000, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_stdst_cfg = { + .config = &maili_stdst_cfg_regmap_config, + .nodes = stdst_cfg_nodes, + .num_nodes = ARRAY_SIZE(stdst_cfg_nodes), + .bcms = stdst_cfg_bcms, + .num_bcms = ARRAY_SIZE(stdst_cfg_bcms), +}; + +static struct qcom_icc_node * const stdst_main_nodes[] = { + [MASTER_CNOC_STARDUST] = &qsm_cnoc_main, + [SLAVE_STARDUST_CENTER_CFG] = &qss_stdst_center_cfg, + [SLAVE_STARDUST_EAST_CFG] = &qss_stdst_east_cfg, + [SLAVE_STARDUST_MM_HF_CFG] = &qss_stdst_mm_hf_cfg, + [SLAVE_STARDUST_MM_SF_CFG] = &qss_stdst_mm_sf_cfg, + [SLAVE_STARDUST_NORTH_CFG] = &qss_stdst_north_cfg, + [SLAVE_STARDUST_SOUTH_CFG] = &qss_stdst_south_cfg, + [SLAVE_STARDUST_WEST_CFG] = &qss_stdst_west_cfg, +}; + +static const struct regmap_config maili_stdst_main_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xf9000, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_stdst_main = { + .config = &maili_stdst_main_regmap_config, + .nodes = stdst_main_nodes, + .num_nodes = ARRAY_SIZE(stdst_main_nodes), +}; + +static struct qcom_icc_bcm * const system_noc_bcms[] = { + &bcm_sn0, + &bcm_sn2, +}; + +static struct qcom_icc_node * const system_noc_nodes[] = { + [MASTER_A1NOC_SNOC] = &qnm_aggre_noc, + [MASTER_APSS_NOC] = &qnm_apss_noc, + [MASTER_CNOC_SNOC] = &qnm_cnoc_data, + [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf, +}; + +static const struct regmap_config maili_system_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x20080, + .fast_io = true, +}; + +static const struct qcom_icc_desc maili_system_noc = { + .config = &maili_system_noc_regmap_config, + .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,maili-aggre-noc", .data = &maili_aggre1_noc }, + { .compatible = "qcom,maili-clk-virt", .data = &maili_clk_virt }, + { .compatible = "qcom,maili-cnoc-main", .data = &maili_cnoc_main }, + { .compatible = "qcom,maili-gem-noc", .data = &maili_gem_noc }, + { .compatible = "qcom,maili-llclpi-noc", .data = &maili_llclpi_noc }, + { .compatible = "qcom,maili-lpass-ag-noc", .data = &maili_lpass_ag_noc }, + { .compatible = "qcom,maili-lpass-lpiaon-noc", .data = &maili_lpass_lpiaon_noc }, + { .compatible = "qcom,maili-lpass-lpicx-noc", .data = &maili_lpass_lpicx_noc }, + { .compatible = "qcom,maili-mc-virt", .data = &maili_mc_virt }, + { .compatible = "qcom,maili-mmss-noc", .data = &maili_mmss_noc }, + { .compatible = "qcom,maili-nsp-noc", .data = &maili_nsp_noc }, + { .compatible = "qcom,maili-pcie-anoc", .data = &maili_pcie_anoc }, + { .compatible = "qcom,maili-stdst-cfg", .data = &maili_stdst_cfg }, + { .compatible = "qcom,maili-stdst-main", .data = &maili_stdst_main }, + { .compatible = "qcom,maili-system-noc", .data = &maili_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-maili", + .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 Maili NoC driver"); +MODULE_LICENSE("GPL"); From 22568e8f13a42ab69b7d624741768cbf15dded74 Mon Sep 17 00:00:00 2001 From: Qingtao Cao Date: Wed, 3 Jun 2026 11:06:11 +1000 Subject: [PATCH 155/513] bus: mhi: core: Fix sys error transition latency Bring forward the idea to fix the power down latency in mhi_pm_disable_transition() further to mhi_pm_sys_error_transition() so that the transition into system error (triggered by AT!RESET) in the modems won't have to return only after the timeout of up to 24 seconds. Once the device gets reset, there is no guarantee that it will send an interrupt to indicate the state transition. So polling is the sensible option here. Signed-off-by: Qingtao Cao [mani: commit log] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Krishna Chaitanya Chundru Link: https://patch.msgid.link/20260603011333.3306102-2-qingtao.cao.au@gmail.com --- drivers/bus/mhi/host/pm.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/bus/mhi/host/pm.c b/drivers/bus/mhi/host/pm.c index f799503c8f36..9d29f1591a45 100644 --- a/drivers/bus/mhi/host/pm.c +++ b/drivers/bus/mhi/host/pm.c @@ -651,21 +651,13 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl) /* Trigger MHI RESET so that the device will not access host memory */ if (reset_device) { - u32 in_reset = -1; - unsigned long timeout = msecs_to_jiffies(mhi_cntrl->timeout_ms); - dev_dbg(dev, "Triggering MHI Reset in device\n"); mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET); /* Wait for the reset bit to be cleared by the device */ - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_read_reg_field(mhi_cntrl, - mhi_cntrl->regs, - MHICTRL, - MHICTRL_RESET_MASK, - &in_reset) || - !in_reset, timeout); - if (!ret || in_reset) { + ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, + MHICTRL_RESET_MASK, 0, 25000, mhi_cntrl->timeout_ms); + if (ret) { dev_err(dev, "Device failed to exit MHI Reset state\n"); write_lock_irq(&mhi_cntrl->pm_lock); cur_state = mhi_tryset_pm_state(mhi_cntrl, From 6f12862600bb70e599a614d706a095ea5f8f9858 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Wed, 3 Jun 2026 15:51:42 -0400 Subject: [PATCH 156/513] bus: mhi: ep: Fix device refcount leak in the error path of MHI device creation mhi_ep_create_device() takes one device reference for the UL channel and another for the DL channel after allocating the transfer device. These references are normally released by mhi_ep_destroy_device() before the device itself is removed. If dev_set_name() or device_add() fails, the error path currently drops only one reference. The remaining channel references keep the device from being released and leave the channels associated with a device that was never registered. Route both failures through a common unwind path that drops the DL channel reference, the UL channel reference, and the initial reference from device_initialize(). Fixes: 297c77a0f273 ("bus: mhi: ep: Add support for creating and destroying MHI EP devices") Signed-off-by: Yuho Choi Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260603195142.2189386-1-dbgh9129@gmail.com --- drivers/bus/mhi/ep/main.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index 9db2a2a2c913..43d680778704 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -1341,14 +1341,19 @@ static int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id) ret = dev_set_name(&mhi_dev->dev, "%s_%s", dev_name(&mhi_cntrl->mhi_dev->dev), mhi_dev->name); - if (ret) { - put_device(&mhi_dev->dev); - return ret; - } + if (ret) + goto err_put_channels; ret = device_add(&mhi_dev->dev); if (ret) - put_device(&mhi_dev->dev); + goto err_put_channels; + + return 0; + +err_put_channels: + put_device(&mhi_dev->dev); /* DL channel reference */ + put_device(&mhi_dev->dev); /* UL channel reference */ + put_device(&mhi_dev->dev); /* device_initialize() reference */ return ret; } From ad7a9a2d295c0525e0f4ba1b85045d160458e8b3 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 10 Jun 2026 20:47:52 +0530 Subject: [PATCH 157/513] MAINTAINERS: Add Jeff Hugo as the Reviewer of MHI bus Jeff has been active in reviewing the MHI patches and he volunteered to become an official reviewer. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260610151752.9373-1-manivannan.sadhasivam@oss.qualcomm.com --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..a4b89bd497ad 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17463,6 +17463,7 @@ F: arch/arm64/boot/dts/marvell/armada-3720-uDPU.* MHI BUS M: Manivannan Sadhasivam +R: Jeff Hugo L: mhi@lists.linux.dev L: linux-arm-msm@vger.kernel.org S: Maintained From 947f1079074a260ba200419d5cba6b8549d5ac0c Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Wed, 8 Jul 2026 07:34:13 +0200 Subject: [PATCH 158/513] iio: adc: max34408: add missing 'select REGMAP_I2C' to Kconfig The Kconfig entry for the MAX34408 is missing a 'select REGMAP_I2C', causing build failures. Fixes: cf27775838c5 ("iio: adc: Add driver support for MAX34408/9") Cc: stable@vger.kernel.org Signed-off-by: Joshua Crofts Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 3755a81c1efd..9deecd2cd3b4 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1086,6 +1086,7 @@ config MAX14001 config MAX34408 tristate "Maxim max34408/max344089 ADC driver" depends on I2C + select REGMAP_I2C help Say yes here to build support for Maxim max34408/max34409 current sense monitor with 8-bits ADC interface with overcurrent delay/threshold and From b7c969d0d445c415b8e9f32627d7e8f092c7e916 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Wed, 8 Jul 2026 07:34:14 +0200 Subject: [PATCH 159/513] iio: adc: max14001: add missing 'select REGMAP' to Kconfig The Kconfig entry for the MAX14001 is missing a 'select REGMAP', causing build failures. Fixes: 59795109fa67 ("iio: adc: max14001: New driver") Cc: stable@vger.kernel.org Signed-off-by: Joshua Crofts Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 9deecd2cd3b4..c6d3cf68b1f7 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1076,6 +1076,7 @@ config MAX1363 config MAX14001 tristate "Analog Devices MAX14001/MAX14002 ADC driver" depends on SPI + select REGMAP help Say yes here to build support for Analog Devices MAX14001/MAX14002 Configurable, Isolated 10-bit ADCs for Multi-Range Binary Inputs. From b9d17aa74ddd79e2d081db5aacccc2992efceb4c Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 15 Jun 2026 12:36:41 +0000 Subject: [PATCH 160/513] rust_binder: avoid allocating under node_refs for freeze listeners The node_refs mutex needs to be changed to a spinlock, so in preparation for that, update freeze.rs to avoid allocating under the node_refs lock. This is done by adding a retry loop so that if add_freeze_listener() requires reallocating the KVVec<_> of freeze listeners, the caller will allocate a larger vector and retry. Analogously, the remove_freeze_listener() function is updated to return the empty KVVec<_> when it is no longer needed, to avoid calling kvfree() under the node_refs lock. Reviewed-by: Matthew Maurer Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260615-binder-noderefs-spin-v3-1-3235f5a3e0a0@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/freeze.rs | 65 +++++++++++++++++++++----------- drivers/android/binder/node.rs | 47 +++++++++++------------ 2 files changed, 67 insertions(+), 45 deletions(-) diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs index 2178258772e5..918c4e98b66f 100644 --- a/drivers/android/binder/freeze.rs +++ b/drivers/android/binder/freeze.rs @@ -173,36 +173,58 @@ pub(crate) fn request_freeze_notif( let msg = FreezeMessage::new(GFP_KERNEL)?; let alloc = RBTreeNodeReservation::new(GFP_KERNEL)?; + let mut afl_vec_alloc = KVVec::new(); + let mut info; + let mut freeze_entry; let mut node_refs_guard = self.node_refs.lock(); - let node_refs = &mut *node_refs_guard; - let Some(info) = node_refs.by_handle.get_mut(&handle) else { - pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION invalid ref {}\n", handle); - return Err(EINVAL); - }; - if info.freeze().is_some() { - pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION already set\n"); - return Err(EINVAL); - } - let node_ref = info.node_ref(); - let freeze_entry = node_refs.freeze_listeners.entry(cookie); - - if let rbtree::Entry::Occupied(ref dupe) = freeze_entry { - if !dupe.get().allow_duplicate(&node_ref.node) { - pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n"); + loop { + let node_refs = &mut *node_refs_guard; + info = match node_refs.by_handle.get_mut(&handle) { + Some(info) => info, + None => { + pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION invalid ref {}\n", handle); + return Err(EINVAL); + } + }; + if info.freeze().is_some() { + pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION already set\n"); return Err(EINVAL); } - } + let node_ref = info.node_ref(); + freeze_entry = node_refs.freeze_listeners.entry(cookie); - // All failure paths must come before this call, and all modifications must come after this - // call. - node_ref.node.add_freeze_listener(self, GFP_KERNEL)?; + if let rbtree::Entry::Occupied(ref dupe) = freeze_entry { + if !dupe.get().allow_duplicate(&node_ref.node) { + pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n"); + return Err(EINVAL); + } + } + + // Now we add to the node's freeze listener list, with retry and re-allocate if the + // vector is full. + // + // To ensure that the node is added atomically, this is the first time we modify any + // state. When this call succeeds, all other modifications must occur without the + // possibility for any failure paths. + match node_ref + .node + .add_freeze_listener(self, &mut afl_vec_alloc)? + { + Ok(()) => break, + Err(resize_target) => { + drop(node_refs_guard); + afl_vec_alloc = KVVec::with_capacity(resize_target, GFP_KERNEL)?; + node_refs_guard = self.node_refs.lock(); + } + } + } match freeze_entry { rbtree::Entry::Vacant(entry) => { entry.insert( FreezeListener { cookie, - node: node_ref.node.clone(), + node: info.node_ref().node.clone(), last_is_frozen: None, is_pending: false, is_clearing: false, @@ -273,6 +295,7 @@ pub(crate) fn clear_freeze_notif(self: &Arc, reader: &mut UserSliceReader) let handle = hc.handle; let cookie = FreezeCookie(hc.cookie); + let _to_free_fl; let alloc = FreezeMessage::new(GFP_KERNEL)?; let mut node_refs_guard = self.node_refs.lock(); let node_refs = &mut *node_refs_guard; @@ -293,7 +316,7 @@ pub(crate) fn clear_freeze_notif(self: &Arc, reader: &mut UserSliceReader) return Err(EINVAL); }; listener.is_clearing = true; - listener.node.remove_freeze_listener(self); + _to_free_fl = listener.node.remove_freeze_listener(self); *info.freeze() = None; let mut msg = None; if !listener.is_pending { diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs index 4e75f58bf0db..fb57c0b20888 100644 --- a/drivers/android/binder/node.rs +++ b/drivers/android/binder/node.rs @@ -659,33 +659,29 @@ fn do_work_locked( pub(crate) fn add_freeze_listener( &self, process: &Arc, - flags: kernel::alloc::Flags, - ) -> Result { - let mut vec_alloc = KVVec::>::new(); - loop { - let mut guard = self.owner.inner.lock(); - // Do not check for `guard.dead`. The `dead` flag that matters here is the owner of the - // listener, no the target. - let inner = self.inner.access_mut(&mut guard); - let len = inner.freeze_list.len(); - if len >= inner.freeze_list.capacity() { - if len >= vec_alloc.capacity() { - drop(guard); - vec_alloc = KVVec::with_capacity((1 + len).next_power_of_two(), flags)?; - continue; - } - mem::swap(&mut inner.freeze_list, &mut vec_alloc); - for elem in vec_alloc.drain_all() { - inner.freeze_list.push_within_capacity(elem)?; - } + // If the vector needs to be resized, it's done via this argument. + vec_alloc: &mut KVVec>, + ) -> Result> { + let mut guard = self.owner.inner.lock(); + // Do not check for `guard.dead`. The `dead` flag that matters here is the owner of the + // listener, not the target. + let inner = self.inner.access_mut(&mut guard); + let len = inner.freeze_list.len(); + if len == inner.freeze_list.capacity() { + if len >= vec_alloc.capacity() { + // Request the caller to reallocate. + return Ok(Err((1 + len).next_power_of_two())); + } + mem::swap(&mut inner.freeze_list, vec_alloc); + for elem in vec_alloc.drain_all() { + inner.freeze_list.push_within_capacity(elem)?; } - inner.freeze_list.push_within_capacity(process.clone())?; - return Ok(()); } + inner.freeze_list.push_within_capacity(process.clone())?; + Ok(Ok(())) } - pub(crate) fn remove_freeze_listener(&self, p: &Arc) { - let _unused_capacity; + pub(crate) fn remove_freeze_listener(&self, p: &Arc) -> KVVec> { let mut guard = self.owner.inner.lock(); let inner = self.inner.access_mut(&mut guard); let len = inner.freeze_list.len(); @@ -696,9 +692,12 @@ pub(crate) fn remove_freeze_listener(&self, p: &Arc) { p.pid_in_current_ns() ); } + // If the vector is empty it needs to be freed. However, we can't free it here because that + // might sleep, so return it to the caller. if inner.freeze_list.is_empty() { - _unused_capacity = mem::take(&mut inner.freeze_list); + return mem::take(&mut inner.freeze_list); } + KVVec::new() } pub(crate) fn freeze_list<'a>(&'a self, guard: &'a ProcessInner) -> &'a [Arc] { From 521eae8326a18cbf7fb4640dcfb2d1396423d1ab Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 15 Jun 2026 12:36:42 +0000 Subject: [PATCH 161/513] rust_binder: avoid dropping NodeRef in update_ref() under lock In preparation for changing the node_refs lock to a spinlock, move the cleanup of NodeRefInfo in update_ref() so that it occurs without the node_refs lock held. This avoids dropping an Arc with the lock held. Furthermore, the NodeDeath field is kept in the NodeRefInfo to be dropped outside the lock as well. The removal from the rbtree is updated to use remove_node(), which keeps the rbtree node allocation until after node_refs is unlocked as well. This is not strictly necessary as it just moves a kfree() outside the lock, but there's no reason to invoke the kfree() under the lock if we can easily avoid it, so avoid it. Reviewed-by: Matthew Maurer Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260615-binder-noderefs-spin-v3-2-3235f5a3e0a0@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 99d1a7ade599..73a2582c4d9c 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -942,13 +942,17 @@ pub(crate) fn update_ref( // To preserve original binder behaviour, we only fail requests where the manager tries to // increment references on itself. + let _to_free_by_handle; + let _to_free_by_node; let mut refs = self.node_refs.lock(); if let Some(info) = refs.by_handle.get_mut(&handle) { if info.node_ref().update(inc, strong) { // Clean up death if there is one attached to this node reference. - if let Some(death) = info.death().take() { + // + // We remove the entire `info` below, so no need to remove `death` from `info`. + if let Some(death) = info.death().as_ref() { death.set_cleared(true); - self.remove_from_delivered_deaths(&death); + self.remove_from_delivered_deaths(death); } // Remove reference from process tables, and from the node's `refs` list. @@ -957,8 +961,8 @@ pub(crate) fn update_ref( unsafe { info.node_ref2().node.remove_node_info(info) }; let id = info.node_ref().node.global_id(); - refs.by_handle.remove(&handle); - refs.by_node.remove(&id); + _to_free_by_handle = refs.by_handle.remove_node(&handle); + _to_free_by_node = refs.by_node.remove_node(&id); refs.handle_is_present.release_id(handle as usize); if let Some(shrink) = refs.handle_is_present.shrink_request() { From 56c650167ea9627ba734e375bff1b68d2039b88a Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 15 Jun 2026 12:36:43 +0000 Subject: [PATCH 162/513] rust_binder: schedule NodeDeath outside of node_refs lock There's no reason to hold the node_refs lock while scheduling the NodeDeath to the thread todo list, so don't. The call to set_cleared() is kept under the lock so that the state update is kept atomic. Reviewed-by: Matthew Maurer Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260615-binder-noderefs-spin-v3-3-3235f5a3e0a0@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 73a2582c4d9c..7b214dc51586 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -1289,7 +1289,10 @@ pub(crate) fn clear_death(&self, reader: &mut UserSliceReader, thread: &Thread) // Update state and determine if we need to queue a work item. We only need to do it when // the node is not dead or if the user already completed the death notification. - if death.set_cleared(false) { + let should_schedule = death.set_cleared(false); + drop(refs); + + if should_schedule { if let Some(death) = ListArc::try_from_arc_or_drop(death) { let _ = thread.push_work_if_looper(death); } From 2812b20e165dbd9764d31d25686e305c7f329010 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 15 Jun 2026 12:36:44 +0000 Subject: [PATCH 163/513] rust_binder: keep NodeDeath in NodeRefInfo during process cleanup By keeping the NodeDeath inside the NodeRefInfo structure during process cleanup, we avoid running its destructor under the node_refs lock. It is still dropped shortly thereafter when the entire rbtree holding the NodeRefInfo objects is dropped, but that occurs outside of the lock. Reviewed-by: Matthew Maurer Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260615-binder-noderefs-spin-v3-4-3235f5a3e0a0@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 7b214dc51586..7a45d478e33a 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -1375,13 +1375,11 @@ fn deferred_release(self: Arc) { // SAFETY: We are removing the `NodeRefInfo` from the right node. unsafe { info.node_ref2().node.remove_node_info(info) }; - // Remove all death notifications from the nodes (that belong to a different process). - let death = if let Some(existing) = info.death().take() { - existing - } else { - continue; - }; - death.set_cleared(false); + // Clear death notifications from the nodes (that belong to a different process). + // No need to remove them from `info` as we clear info below. + if let Some(death) = info.death().as_ref() { + death.set_cleared(false); + } } // Clean up freeze listeners. From 63b4af40e260cf472c2946459a62060983451668 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 15 Jun 2026 12:36:45 +0000 Subject: [PATCH 164/513] rust_binder: avoid destructors in insert_or_update_handle() The insert_or_update_handle() function currently has two places where it drops objects under the node_refs lock. In preparation for changing node_refs into a spinlock, update the code to either entirely remove the codepath or drop the node_refs lock first before running the destructor. This also has the side-benefit that we avoid traversing the by_node rbtree twice. Currently it's first traversed to see if the new node is present, and then traversed again to insert it. By saving the VacantEntry from the first lookup, we can perform the insertion without traversing the tree again. Reviewed-by: Matthew Maurer Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260615-binder-noderefs-spin-v3-5-3235f5a3e0a0@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 7a45d478e33a..7a21e4475c80 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -861,14 +861,17 @@ pub(crate) fn insert_or_update_handle( let handle = unused_id.as_u32(); // Do a lookup again as node may have been inserted before the lock was reacquired. - if let Some(handle_ref) = refs.by_node.get(&node_ref.node.global_id()) { - let handle = *handle_ref; - let info = refs.by_handle.get_mut(&handle).unwrap(); - info.node_ref().absorb(node_ref); - return Ok(handle); - } + let by_node_slot = match refs.by_node.entry(node_ref.node.global_id()) { + rbtree::Entry::Vacant(by_node_slot) => by_node_slot, + rbtree::Entry::Occupied(handle_ref) => { + // The node was inserted by another thread while we didn't hold the lock. + let handle = handle_ref.get(); + let info = refs.by_handle.get_mut(handle).unwrap(); + info.node_ref().absorb(node_ref); + return Ok(*handle); + } + }; - let gid = node_ref.node.global_id(); let (info_proc, info_node) = { let info_init = NodeRefInfo::new(node_ref, handle, self.into()); match info.pin_init_with(info_init) { @@ -884,6 +887,9 @@ pub(crate) fn insert_or_update_handle( // first thing in `deferred_release`, process cleanup will not miss the items inserted into // `refs` below. if self.inner.lock().is_dead { + // Explicitly drop the lock so that `info_proc` and `info_node` are dropped outside of + // the lock. + drop(refs_lock); return Err(ESRCH); } @@ -891,7 +897,7 @@ pub(crate) fn insert_or_update_handle( // `info_node` into the right node's `refs` list. unsafe { info_proc.node_ref2().node.insert_node_info(info_node) }; - refs.by_node.insert(reserve1.into_node(gid, handle)); + by_node_slot.insert(handle, reserve1); by_handle_slot.insert(info_proc, reserve2); unused_id.acquire(); Ok(handle) From ef7453e1f986813d8bf734af33088e34bf91f70a Mon Sep 17 00:00:00 2001 From: Mahesh Rao Date: Fri, 10 Jul 2026 00:04:40 -0700 Subject: [PATCH 165/513] firmware: stratix10-rsu: Add synchronous fallback for async SVC operations The RSU driver was migrated to the Stratix10 asynchronous service framework, which assumes async client registration always succeeds. On platforms where the async controller is unavailable or RSU async support is not present, probe fails or sysfs operations cannot reach firmware. Detect async capability at probe via stratix10_svc_add_async_client() and fall back to the legacy synchronous rsu_send_msg() path when registration fails. Track capability in priv->async and branch accordingly for: - Initial RSU status and retry counter during probe (retry is bundled in async status; issue COMMAND_RSU_RETRY separately in sync mode) - notify sysfs store (notify, status, and retry) - SPT table retrieval (COMMAND_RSU_GET_SPT_TABLE async vs mbox COMMAND_MBOX_SEND_CMD synchronous) Restore synchronous callbacks (rsu_status_callback, rsu_retry_callback, rsu_get_spt_callback) and wire COMMAND_MBOX_SEND_CMD payload handling in __rsu_send_msg_locked. Improve probe and remove cleanup: return on intermediate probe failures, remove the async client when registered, and free the SPT response buffer on sync-path allocation errors. Suggested-by: Anders Hedlund Signed-off-by: Mahesh Rao Signed-off-by: Asyraaf Azhar Signed-off-by: Tze Yee Ng Signed-off-by: Dinh Nguyen --- drivers/firmware/stratix10-rsu.c | 227 ++++++++++++++++++++++++++++--- 1 file changed, 207 insertions(+), 20 deletions(-) diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c index d887c74b9821..b360f752d191 100644 --- a/drivers/firmware/stratix10-rsu.c +++ b/drivers/firmware/stratix10-rsu.c @@ -18,6 +18,10 @@ #include #include +#define RSU_STATE_MASK GENMASK_ULL(31, 0) +#define RSU_VERSION_MASK GENMASK_ULL(63, 32) +#define RSU_ERROR_LOCATION_MASK GENMASK_ULL(31, 0) +#define RSU_ERROR_DETAIL_MASK GENMASK_ULL(63, 32) /* * INTEL_SIP_SMC_RSU_GET_DEVICE_INFO packs each flash word as: * [63:32] erase_size, [31:0] size (see stratix10-smc.h). @@ -44,6 +48,7 @@ #define RSU_RETRY_SLEEP_MS (1U) #define RSU_ASYNC_MSG_RETRY (3U) +#define RSU_GET_SPT_CMD 0x5A #define RSU_GET_SPT_RESP_LEN (4 * sizeof(unsigned int)) struct flash_device_info { @@ -73,6 +78,7 @@ typedef void (*rsu_callback)(struct stratix10_svc_client *client, * @client: active service client * @completion: state for callback completion * @lock: a mutex to protect callback completion state + * @async: supports async operations * @status.current_image: address of image currently running in flash * @status.fail_image: address of failed image in flash * @status.version: the interface version number of RSU firmware @@ -93,12 +99,14 @@ typedef void (*rsu_callback)(struct stratix10_svc_client *client, * @max_retry: the preset max retry value * @spt0_address: address of spt0 * @spt1_address: address of spt1 + * @get_spt_response_buf: response from sdm for get_spt command */ struct stratix10_rsu_priv { struct stratix10_svc_chan *chan; struct stratix10_svc_client client; struct completion completion; struct mutex lock; + bool async; struct { unsigned long current_image; unsigned long fail_image; @@ -129,6 +137,8 @@ struct stratix10_rsu_priv { unsigned long spt0_address; unsigned long spt1_address; + + unsigned int *get_spt_response_buf; }; /** @@ -148,6 +158,45 @@ static void rsu_device_info_invalidate(struct stratix10_rsu_priv *priv) typedef void (*rsu_async_callback)(struct device *dev, struct stratix10_rsu_priv *priv, struct stratix10_svc_cb_data *data); +/** + * rsu_status_callback() - Status callback from Intel Service Layer + * @client: pointer to service client + * @data: pointer to callback data structure + * + * Callback from Intel service layer for RSU status request. Status is + * only updated after a system reboot, so a get updated status call is + * made during driver probe. + */ +static void rsu_status_callback(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct stratix10_rsu_priv *priv = client->priv; + struct arm_smccc_res *res = (struct arm_smccc_res *)data->kaddr1; + + if (data->status == BIT(SVC_STATUS_OK)) { + priv->status.version = FIELD_GET(RSU_VERSION_MASK, + res->a2); + priv->status.state = FIELD_GET(RSU_STATE_MASK, res->a2); + priv->status.fail_image = res->a1; + priv->status.current_image = res->a0; + priv->status.error_location = + FIELD_GET(RSU_ERROR_LOCATION_MASK, res->a3); + priv->status.error_details = + FIELD_GET(RSU_ERROR_DETAIL_MASK, res->a3); + } else { + dev_err(client->dev, "COMMAND_RSU_STATUS returned 0x%lX\n", + res->a0); + priv->status.version = 0; + priv->status.state = 0; + priv->status.fail_image = 0; + priv->status.current_image = 0; + priv->status.error_location = 0; + priv->status.error_details = 0; + } + + complete(&priv->completion); +} + /** * rsu_async_status_callback() - Status callback from rsu_async_send() * @dev: pointer to device object @@ -192,6 +241,32 @@ static void rsu_command_callback(struct stratix10_svc_client *client, complete(&priv->completion); } +/** + * rsu_retry_callback() - Callback from Intel service layer for getting + * the current image's retry counter from the firmware + * @client: pointer to client + * @data: pointer to callback data structure + * + * Callback from Intel service layer for retry counter, which is used by + * user to know how many times the images is still allowed to reload + * itself before giving up and starting RSU fail-over flow. + */ +static void rsu_retry_callback(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct stratix10_rsu_priv *priv = client->priv; + unsigned int *counter = (unsigned int *)data->kaddr1; + + if (data->status == BIT(SVC_STATUS_OK)) + priv->retry_counter = *counter; + else if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) + dev_warn(client->dev, "Secure FW doesn't support retry\n"); + else + dev_err(client->dev, "Failed to get retry counter %lu\n", + BIT(data->status)); + + complete(&priv->completion); +} /** * rsu_max_retry_callback() - Callback from Intel service layer for getting @@ -337,6 +412,38 @@ static void rsu_async_get_spt_table_callback(struct device *dev, priv->spt1_address = *((unsigned long *)data->kaddr2); } +static void rsu_get_spt_callback(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct stratix10_rsu_priv *priv = client->priv; + unsigned long *mbox_err = (unsigned long *)data->kaddr1; + unsigned long *resp_len = (unsigned long *)data->kaddr2; + + if (data->status != BIT(SVC_STATUS_OK) || (*mbox_err) || + (*resp_len != RSU_GET_SPT_RESP_LEN)) + goto error; + + priv->spt0_address = priv->get_spt_response_buf[0]; + priv->spt0_address <<= 32; + priv->spt0_address |= priv->get_spt_response_buf[1]; + priv->spt1_address = priv->get_spt_response_buf[2]; + priv->spt1_address <<= 32; + priv->spt1_address |= priv->get_spt_response_buf[3]; + + goto complete; + +error: + dev_err(priv->client.dev, + "failed to get SPTs (status=%#x, mbox_err=%lu, resp_len=%lu)\n", + data->status, mbox_err ? *mbox_err : 0, + resp_len ? *resp_len : 0); + +complete: + stratix10_svc_free_memory(priv->chan, priv->get_spt_response_buf); + priv->get_spt_response_buf = NULL; + complete(&priv->completion); +} + /** * __rsu_send_msg_locked() - send a message to Intel service layer * @priv: pointer to rsu private data @@ -353,7 +460,7 @@ static int __rsu_send_msg_locked(struct stratix10_rsu_priv *priv, unsigned long arg, rsu_callback callback) { - struct stratix10_svc_client_msg msg; + struct stratix10_svc_client_msg msg = {0}; int ret; lockdep_assert_held(&priv->lock); @@ -365,6 +472,14 @@ static int __rsu_send_msg_locked(struct stratix10_rsu_priv *priv, if (arg) msg.arg[0] = arg; + if (command == COMMAND_MBOX_SEND_CMD) { + msg.arg[1] = 0; + msg.payload = NULL; + msg.payload_length = 0; + msg.payload_output = priv->get_spt_response_buf; + msg.payload_length_output = RSU_GET_SPT_RESP_LEN; + } + ret = stratix10_svc_send(priv->chan, &msg); if (ret < 0) goto status_done; @@ -489,6 +604,8 @@ static int rsu_send_async_msg(struct device *dev, struct stratix10_rsu_priv *pri if (status && !handle) { dev_err(dev, "Failed to send async message\n"); + if (msg.payload_output) + stratix10_svc_free_memory(priv->chan, msg.payload_output); return -ETIMEDOUT; } @@ -528,6 +645,8 @@ static int rsu_send_async_msg(struct device *dev, struct stratix10_rsu_priv *pri } status_done: + if (msg.payload_output) + stratix10_svc_free_memory(priv->chan, msg.payload_output); stratix10_svc_async_done(priv->chan, handle); return ret; } @@ -775,15 +894,30 @@ static ssize_t notify_store(struct device *dev, if (ret) return ret; - ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_NOTIFY, status, NULL); + if (priv->async) + ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_NOTIFY, status, NULL); + else + ret = rsu_send_msg(priv, COMMAND_RSU_NOTIFY, status, rsu_command_callback); if (ret) { dev_err(dev, "Error, RSU notify returned %i\n", ret); return ret; } /* to get the updated state */ - ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0, - rsu_async_status_callback); + if (priv->async) { + ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0, + rsu_async_status_callback); + } else { + ret = rsu_send_msg(priv, COMMAND_RSU_STATUS, 0, + rsu_status_callback); + /* + * In async mode COMMAND_RSU_RETRY is part of COMMAND_RSU_STATUS; + * issue it separately only in synchronous mode. + */ + if (!ret) + ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, + rsu_retry_callback); + } if (ret) { dev_err(dev, "Error, getting RSU status %i\n", ret); return ret; @@ -974,7 +1108,11 @@ static int stratix10_rsu_probe(struct platform_device *pdev) priv->dcmf_status.dcmf1 = INVALID_DCMF_STATUS; priv->dcmf_status.dcmf2 = INVALID_DCMF_STATUS; priv->dcmf_status.dcmf3 = INVALID_DCMF_STATUS; - /* spt0/1_address and status fields default to 0 from kzalloc */ + priv->max_retry = INVALID_RETRY_COUNTER; + priv->spt0_address = INVALID_SPT_ADDRESS; + priv->spt1_address = INVALID_SPT_ADDRESS; + priv->get_spt_response_buf = NULL; + priv->async = false; rsu_device_info_invalidate(priv); mutex_init(&priv->lock); @@ -989,19 +1127,39 @@ static int stratix10_rsu_probe(struct platform_device *pdev) } ret = stratix10_svc_add_async_client(priv->chan, false); - if (ret) { - dev_err(dev, "failed to add async client\n"); - goto free_channel; + if (ret < 0) { + dev_dbg(dev, "Async operations not supported, fallback to non-async mode\n"); + priv->async = false; + } else { + priv->async = true; } platform_set_drvdata(pdev, priv); /* get the initial state from firmware */ - ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0, - rsu_async_status_callback); + if (priv->async) + ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_STATUS, 0, + rsu_async_status_callback); + else + ret = rsu_send_msg(priv, COMMAND_RSU_STATUS, 0, + rsu_status_callback); if (ret) { dev_err(dev, "Error, getting RSU status %i\n", ret); - goto remove_async_client; + if (priv->async) + goto remove_async_client; + goto free_channel; + } + + /* + * In async mode COMMAND_RSU_RETRY is part of COMMAND_RSU_STATUS; + * issue it separately only in synchronous mode. + */ + if (!priv->async) { + ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, rsu_retry_callback); + if (ret) { + dev_err(dev, "Error, getting RSU retry %i\n", ret); + goto free_channel; + } } /* get DCMF version from firmware */ @@ -1009,21 +1167,27 @@ static int stratix10_rsu_probe(struct platform_device *pdev) rsu_dcmf_version_callback); if (ret) { dev_err(dev, "Error, getting DCMF version %i\n", ret); - goto remove_async_client; + if (priv->async) + goto remove_async_client; + goto free_channel; } ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_STATUS, 0, rsu_dcmf_status_callback); if (ret) { dev_err(dev, "Error, getting DCMF status %i\n", ret); - goto remove_async_client; + if (priv->async) + goto remove_async_client; + goto free_channel; } ret = rsu_send_msg(priv, COMMAND_RSU_MAX_RETRY, 0, rsu_max_retry_callback); if (ret) { dev_err(dev, "Error, getting RSU max retry %i\n", ret); - goto remove_async_client; + if (priv->async) + goto remove_async_client; + goto free_channel; } /* get QSPI device info from firmware */ @@ -1031,16 +1195,36 @@ static int stratix10_rsu_probe(struct platform_device *pdev) rsu_get_device_info_callback); if (ret) { dev_err(dev, "Error, getting QSPI Device Info %i\n", ret); - stratix10_svc_remove_async_client(priv->chan); - stratix10_svc_free_channel(priv->chan); - return ret; + if (priv->async) + goto remove_async_client; + goto free_channel; } - ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_GET_SPT_TABLE, 0, - rsu_async_get_spt_table_callback); + if (priv->async) { + ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_GET_SPT_TABLE, + 0, rsu_async_get_spt_table_callback); + } else { + priv->get_spt_response_buf = + stratix10_svc_allocate_memory(priv->chan, RSU_GET_SPT_RESP_LEN); + if (IS_ERR(priv->get_spt_response_buf)) { + ret = PTR_ERR(priv->get_spt_response_buf); + priv->get_spt_response_buf = NULL; + dev_err(dev, "failed to allocate get spt buffer\n"); + } else { + ret = rsu_send_msg(priv, COMMAND_MBOX_SEND_CMD, + RSU_GET_SPT_CMD, rsu_get_spt_callback); + } + } if (ret) { dev_err(dev, "Error, getting SPT table %i\n", ret); - goto remove_async_client; + if (priv->async) { + goto remove_async_client; + } else if (!IS_ERR_OR_NULL(priv->get_spt_response_buf)) { + stratix10_svc_free_memory(priv->chan, + priv->get_spt_response_buf); + priv->get_spt_response_buf = NULL; + } + goto free_channel; } return 0; @@ -1056,6 +1240,9 @@ static void stratix10_rsu_remove(struct platform_device *pdev) { struct stratix10_rsu_priv *priv = platform_get_drvdata(pdev); + if (priv->async) + stratix10_svc_remove_async_client(priv->chan); + stratix10_svc_free_channel(priv->chan); } From f8d269390cd2a7a9fb5a31f153e7c7b709defea0 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 15 Jun 2026 12:36:46 +0000 Subject: [PATCH 166/513] rust_binder: update Process::node_refs to use SpinLock Unfortunately the current use of a mutex for this lock leads to priority inversion. Traces have been observed where a process is trying to obtain this mutex for 22ms, but it's unable to do so because the thread holding the lock is scheduled out. Since this occurred on a UI thread, that is an extremely long delay. Code paths that might sleep under this lock have already been updated in patches leading up to this one. Reviewed-by: Matthew Maurer Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260615-binder-noderefs-spin-v3-6-3235f5a3e0a0@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 7a21e4475c80..1abeb83684e4 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -30,7 +30,7 @@ sync::{ aref::ARef, lock::{spinlock::SpinLockBackend, Guard}, - Arc, ArcBorrow, CondVar, CondVarTimeoutResult, Mutex, SpinLock, UniqueArc, + Arc, ArcBorrow, CondVar, CondVarTimeoutResult, SpinLock, UniqueArc, }, task::{Pid, Task}, uaccess::{UserSlice, UserSliceReader}, @@ -455,7 +455,7 @@ pub(crate) struct Process { // Node references are in a different lock to avoid recursive acquisition when // incrementing/decrementing a node in another process. #[pin] - node_refs: Mutex, + node_refs: SpinLock, // Work node for deferred work item. #[pin] @@ -510,7 +510,7 @@ fn new(ctx: Arc, cred: ARef) -> Result> { cred, inner <- kernel::new_spinlock!(ProcessInner::new(), "Process::inner"), pages <- ShrinkablePageRange::new(&super::BINDER_SHRINKER), - node_refs <- kernel::new_mutex!(ProcessNodeRefs::new(), "Process::node_refs"), + node_refs <- kernel::new_spinlock!(ProcessNodeRefs::new(), "Process::node_refs"), freeze_wait <- kernel::new_condvar!("Process::freeze_wait"), task: current.group_leader().into(), defer_work <- kernel::new_work!("Process::defer_work"), From 967d066f5334740f656577bc51c381a1bb707b61 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Thu, 25 Jun 2026 17:46:11 +0530 Subject: [PATCH 167/513] iio: temperature: hid-sensor-temperature: switch to non-devm iio_device_register() Avoid using devm_iio_device_register(), as this driver requires explicit error handling and teardown ordering. With devm_iio_device_register(), IIO device remains registered until the devres cleanup phase. However, driver's remove() callback removes the sensor hub callback and trigger support. This can create a race window where IIO device is still visible and read_raw() requests are issued. These requests might call sensor_hub_input_attr_get_raw_value(), which waits up to 5 seconds for a response from the sensor hub callback that has already been removed. Add an explicit iio_device_unregister() call in the teardown path to ensure deterministic cleanup, so that userspace can no longer access the device once backend resources begin to be dismantled. Fixes: 59d0f2da3569 ("iio: hid: Add temperature sensor support") Cc: stable@vger.kernel.org Reviewed-by: Maxwell Doose Reviewed-by: Andy Shevchenko Signed-off-by: Sanjay Chitroda Acked-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/hid-sensor-temperature.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c index 9f628a8e5cfb..34bff7e9f3a3 100644 --- a/drivers/iio/temperature/hid-sensor-temperature.c +++ b/drivers/iio/temperature/hid-sensor-temperature.c @@ -244,7 +244,7 @@ static int hid_temperature_probe(struct platform_device *pdev) if (ret) goto error_remove_trigger; - ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev); + ret = iio_device_register(indio_dev); if (ret) goto error_remove_callback; @@ -264,6 +264,7 @@ static void hid_temperature_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct temperature_state *temp_st = iio_priv(indio_dev); + iio_device_unregister(indio_dev); sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE); hid_sensor_remove_trigger(indio_dev, &temp_st->common_attributes); } From a130404ce0b69ca1438126bd81c1985d3b4d2e6f Mon Sep 17 00:00:00 2001 From: Erick Henrique Date: Fri, 3 Jul 2026 17:52:36 -0300 Subject: [PATCH 168/513] iio: dac: m62332: Fix regulator reference count imbalance m62332_set_value() enables the Vcc regulator on every write of a non-zero value and disables it on every write of zero, without tracking the channel's current state. Because the regulator is reference counted, changing a channel directly from one non-zero value to another enables it more than once, while a later write of zero disables it only once. The reference count never returns to zero and the regulator is left enabled indefinitely. Only enable the regulator on the transition from zero to non-zero, and only disable it on the transition from non-zero to zero, using the previously stored channel value to detect the edge. Balance the regulator on the I2C error path so the reference count stays consistent if the write fails. Fixes: b87b0c0f81e8 ("iio: add m62332 DAC driver") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260418130322.106769-1-erick.henrique.rodrigues%40usp.br Cc: stable@vger.kernel.org Signed-off-by: Erick Henrique Signed-off-by: Jonathan Cameron --- drivers/iio/dac/m62332.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/iio/dac/m62332.c b/drivers/iio/dac/m62332.c index 7e80c0eb5cc1..60bb672b70e2 100644 --- a/drivers/iio/dac/m62332.c +++ b/drivers/iio/dac/m62332.c @@ -32,6 +32,7 @@ static int m62332_set_value(struct iio_dev *indio_dev, u8 val, int channel) { struct m62332_data *data = iio_priv(indio_dev); struct i2c_client *client = data->client; + bool enabling, disabling; u8 outbuf[2]; int res; @@ -43,7 +44,10 @@ static int m62332_set_value(struct iio_dev *indio_dev, u8 val, int channel) mutex_lock(&data->mutex); - if (val) { + enabling = val && !data->raw[channel]; + disabling = !val && data->raw[channel]; + + if (enabling) { res = regulator_enable(data->vcc); if (res) goto out; @@ -52,14 +56,17 @@ static int m62332_set_value(struct iio_dev *indio_dev, u8 val, int channel) res = i2c_master_send(client, outbuf, ARRAY_SIZE(outbuf)); if (res >= 0 && res != ARRAY_SIZE(outbuf)) res = -EIO; - if (res < 0) + if (res < 0) { + if (enabling) + regulator_disable(data->vcc); goto out; + } + + if (disabling) + regulator_disable(data->vcc); data->raw[channel] = val; - if (!val) - regulator_disable(data->vcc); - mutex_unlock(&data->mutex); return 0; From 7a2ccd0a17fe3183714b9c00810027064b2a3fba Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:14:10 +0800 Subject: [PATCH 169/513] iio: light: vcnl4035: Propagate regcache_sync() errors vcnl4035_runtime_suspend() marks the regmap cache dirty before runtime suspend. vcnl4035_runtime_resume() then syncs the cache before re- enabling ALS, but currently ignores a regcache_sync() failure and can still report resume success. Return the regcache_sync() error so the PM core sees the failed register restore. Signed-off-by: Pengpeng Hou Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4035.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c index bf3a49b4351d..d13c5ff33e07 100644 --- a/drivers/iio/light/vcnl4035.c +++ b/drivers/iio/light/vcnl4035.c @@ -647,7 +647,10 @@ static int vcnl4035_runtime_resume(struct device *dev) struct vcnl4035_data *data = iio_priv(indio_dev); int ret; - regcache_sync(data->regmap); + ret = regcache_sync(data->regmap); + if (ret < 0) + return ret; + ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE); if (ret < 0) return ret; From daf79105f9b08a3f2cb205a32bf8e57c936095f4 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:15:11 +0800 Subject: [PATCH 170/513] iio: light: rpr0521: Propagate regcache_sync() errors rpr0521_runtime_suspend() powers the device down and marks the regmap cache dirty. rpr0521_runtime_resume() syncs the cache before restoring enabled ALS/PXS channels, but currently ignores a regcache_sync() failure and can still return success. Return the regcache_sync() error so resume does not hide a failed register restore. Signed-off-by: Pengpeng Hou Signed-off-by: Jonathan Cameron --- drivers/iio/light/rpr0521.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index f961973892f2..e0914f699dfd 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -1071,7 +1071,10 @@ static int rpr0521_runtime_resume(struct device *dev) struct rpr0521_data *data = iio_priv(indio_dev); int ret; - regcache_sync(data->regmap); + ret = regcache_sync(data->regmap); + if (ret < 0) + return ret; + if (data->als_ps_need_en) { ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE); if (ret < 0) From a41000ba3a230bed1e422f283486ff8f77fe0d30 Mon Sep 17 00:00:00 2001 From: Laxman Acharya Padhya Date: Sat, 4 Jul 2026 14:19:12 +0545 Subject: [PATCH 171/513] iio: light: gp2ap002: Disable regulators on resume failure If enabling VIO fails after VDD has been enabled, runtime resume returns without disabling VDD. Likewise, if device reinitialization fails, both supplies remain enabled. The runtime PM core keeps the device suspended when its resume callback fails, so the supplies must be restored to the suspended state. Disable the supplies enabled by the callback before returning an error. Fixes: 97d642e23037 ("iio: light: Add a driver for Sharp GP2AP002x00F") Assisted-by: Codex:gpt-5 Signed-off-by: Laxman Acharya Padhya Reviewed-by: Linus Walleij Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap002.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index a8db514cca5e..05773e24931b 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -669,7 +669,7 @@ static int gp2ap002_runtime_resume(struct device *dev) ret = regulator_enable(gp2ap002->vio); if (ret) { dev_err(dev, "failed to enable VIO regulator in resume path\n"); - return ret; + goto out_disable_vdd; } msleep(20); @@ -677,13 +677,19 @@ static int gp2ap002_runtime_resume(struct device *dev) ret = gp2ap002_init(gp2ap002); if (ret) { dev_err(dev, "re-initialization failed\n"); - return ret; + goto out_disable_vio; } /* Re-activate the IRQ */ enable_irq(gp2ap002->irq); return 0; + +out_disable_vio: + regulator_disable(gp2ap002->vio); +out_disable_vdd: + regulator_disable(gp2ap002->vdd); + return ret; } static DEFINE_RUNTIME_DEV_PM_OPS(gp2ap002_dev_pm_ops, gp2ap002_runtime_suspend, From aa58ecc73466d0cb8c418de98e2225490bf600e3 Mon Sep 17 00:00:00 2001 From: Mert Seftali Date: Fri, 10 Jul 2026 10:36:23 +0200 Subject: [PATCH 172/513] iio: accel: dmard09: Implement IIO_CHAN_INFO_SCALE Reading the in_accel_scale attribute on the DMARD09 has always returned -EINVAL: the channels advertise scale via info_mask_shared_by_type so the IIO core exposes the attribute, but dmard09_read_raw() only handles IIO_CHAN_INFO_RAW, so a SCALE read falls through to 'default: return -EINVAL': $ cat .../iio:deviceX/in_accel_scale cat: in_accel_scale: Invalid argument leaving userspace with raw counts it cannot convert to m/s^2. The driver was written from a vendor source [1] without a datasheet, and the scale was declared but never implemented. The vendor source carries the sensitivity: its conversion is acc = raw * GRAVITY_EARTH_1000 / sensitivity (then / 1000 -> m/s^2) with sensitivity = 32 and GRAVITY_EARTH_1000 = 9807 ("about (9.80665)*1000"), i.e. 32 counts correspond to 1 g. That sensitivity applies to the value this driver already reports as raw: the vendor reduces each 16-bit sample to a signed 9-bit value, and the preparation in dmard09_read_raw() yields the same value. It is self-consistent: 256 counts / 32 = 8 g full scale, matching the +/-8g range. Implement the scale derived from that sensitivity using standard gravity: scale = 9.80665 / 32 = 0.3064578125 m/s^2 per LSB Link: https://github.com/minstrelsy/mediatek/blob/1f49d8c87b839651bc89afc870277e8e0f2e2d55/custom/common/kernel/accelerometer/dmard09/dmard09.c [1] Fixes: a4fa6509dda4 ("iio: accel: add support for the Domintech DMARD09 3-axis accelerometer") Signed-off-by: Mert Seftali Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/accel/dmard09.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/iio/accel/dmard09.c b/drivers/iio/accel/dmard09.c index fe35a1270786..6f0497ab6133 100644 --- a/drivers/iio/accel/dmard09.c +++ b/drivers/iio/accel/dmard09.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #define DMARD09_DRV_NAME "dmard09" @@ -79,6 +80,12 @@ static int dmard09_read_raw(struct iio_dev *indio_dev, *val = accel; return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + /* 1 g / 32 LSB, in m/s^2 */ + *val2 = IIO_G_TO_M_S_2(NANO / 32); + + return IIO_VAL_INT_PLUS_NANO; default: return -EINVAL; } From deb9d74056351e5c62bb467b65444d3252f78d64 Mon Sep 17 00:00:00 2001 From: Roman Vivchar Date: Sun, 12 Jul 2026 09:26:36 +0300 Subject: [PATCH 173/513] iio: adc: mt6323: remove unused header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unused mod_devicetable.h header. Signed-off-by: Roman Vivchar Acked-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Siratul Islam Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mt6323-auxadc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/adc/mt6323-auxadc.c b/drivers/iio/adc/mt6323-auxadc.c index c450fb6f09cb..4f73ac42abb7 100644 --- a/drivers/iio/adc/mt6323-auxadc.c +++ b/drivers/iio/adc/mt6323-auxadc.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include From 036d96ceec6d966339aea34d034420991e24cfac Mon Sep 17 00:00:00 2001 From: Babanpreet Singh Date: Sun, 12 Jul 2026 22:30:04 +0000 Subject: [PATCH 174/513] staging: iio: frequency: remove incorrect kernel-doc markings in dds.h The 13 comment blocks in dds.h noting which sysfs attribute each convenience macro creates are opened with the kernel-doc /** marker, but they are not kernel-doc: there is no identifier to document. Running kernel-doc on the file emits 103 warnings: $ scripts/kernel-doc -none drivers/staging/iio/frequency/dds.h Warning: drivers/staging/iio/frequency/dds.h:10 Cannot find identifier on line: * /sys/bus/iio/devices/.../out_altvoltageX_frequencyY The header is not scanned during normal builds (build-time kernel-doc checks cover .c files), so the warnings only show up when kernel-doc is invoked on the file directly. The comments have carried the /** marker since the file was introduced in commit 4851d97d87c8 ("Staging: IIO: dds.h convenience macros"). The attributes themselves are already documented in drivers/staging/iio/Documentation/sysfs-bus-iio-dds, so demote the comments to plain /* style rather than converting them into kernel-doc for the macros. No functional change. Assisted-by: Claude:claude-sonnet-5 Signed-off-by: Babanpreet Singh Signed-off-by: Jonathan Cameron --- drivers/staging/iio/frequency/dds.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/staging/iio/frequency/dds.h b/drivers/staging/iio/frequency/dds.h index b2ca8bb971e8..d5f0139b79f0 100644 --- a/drivers/staging/iio/frequency/dds.h +++ b/drivers/staging/iio/frequency/dds.h @@ -7,7 +7,7 @@ #ifndef IIO_DDS_H_ #define IIO_DDS_H_ -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_frequencyY */ @@ -15,14 +15,14 @@ IIO_DEVICE_ATTR(out_altvoltage##_channel##_frequency##_num, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_frequencyY_scale */ #define IIO_CONST_ATTR_FREQ_SCALE(_channel, _string) \ IIO_CONST_ATTR(out_altvoltage##_channel##_frequency_scale, _string) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_frequencysymbol */ @@ -30,7 +30,7 @@ IIO_DEVICE_ATTR(out_altvoltage##_channel##_frequencysymbol, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_phaseY */ @@ -38,14 +38,14 @@ IIO_DEVICE_ATTR(out_altvoltage##_channel##_phase##_num, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_phaseY_scale */ #define IIO_CONST_ATTR_PHASE_SCALE(_channel, _string) \ IIO_CONST_ATTR(out_altvoltage##_channel##_phase_scale, _string) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_phasesymbol */ @@ -53,7 +53,7 @@ IIO_DEVICE_ATTR(out_altvoltage##_channel##_phasesymbol, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_en */ @@ -61,7 +61,7 @@ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_en, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_frequency_en */ @@ -69,7 +69,7 @@ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_frequency_en,\ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_phase_en */ @@ -77,7 +77,7 @@ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_phase_en, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_out_enable */ @@ -85,7 +85,7 @@ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out_enable, \ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_outY_enable */ @@ -94,7 +94,7 @@ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out##_output##_enable,\ _mode, _show, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype */ @@ -102,7 +102,7 @@ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out##_output##_wavetype,\ _mode, NULL, _store, _addr) -/** +/* * /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype_available */ From 28a561eeaf656f6a18703ec1f54143dd68598b2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Karol=20Pi=C4=85tkowski?= Date: Thu, 28 May 2026 17:23:55 +0000 Subject: [PATCH 175/513] gpib: fmh_gpib: Fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix typo: Hueristically -> Heuristically Signed-off-by: Dominik Karol Piątkowski Link: https://patch.msgid.link/20260528172306.34050-1-dominik.karol.piatkowski@protonmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/fmh_gpib/fmh_gpib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpib/fmh_gpib/fmh_gpib.c b/drivers/gpib/fmh_gpib/fmh_gpib.c index fcafdc02ea2e..5e10e9353fed 100644 --- a/drivers/gpib/fmh_gpib/fmh_gpib.c +++ b/drivers/gpib/fmh_gpib/fmh_gpib.c @@ -1338,7 +1338,7 @@ static int fmh_gpib_init(struct fmh_priv *e_priv, struct gpib_board *board, int write_byte(nec_priv, AUX_LO_SPEED, AUXMR); nec7210_set_handshake_mode(board, nec_priv, handshake_mode); - /* Hueristically check if hardware supports fifo half full/empty interrupts */ + /* Heuristically check if hardware supports fifo half full/empty interrupts */ fifo_status_bits = fifos_read(e_priv, FIFO_CONTROL_STATUS_REG); e_priv->supports_fifo_interrupts = (fifo_status_bits & TX_FIFO_EMPTY) && (fifo_status_bits & TX_FIFO_HALF_EMPTY); From 75f9481e0479c3faadf4d88baffe84b3d23d5763 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 26 May 2026 08:13:15 +0200 Subject: [PATCH 176/513] tlclk: if sscanf() fails, fall back to 0, not random value If sscanf(IN, FMT, &OUT) fails, OUT may be unchanged. So if OUT was never initialized, it may be still uninitialized memory. To prevent such, initialize OUT=0 first. Fixes: 648bf4fb21f5 ("[PATCH] tlclk driver update") Fixes: 1a80ba882730 ("[PATCH] Telecom Clock Driver for MPCBL0010 ATCA computer blade") Signed-off-by: Alexander A. Klimov Link: https://patch.msgid.link/20260526061321.6123-4-grandmaster@al2klimov.de Signed-off-by: Greg Kroah-Hartman --- drivers/char/tlclk.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c index dd45fe5eb6f2..255f69123af5 100644 --- a/drivers/char/tlclk.c +++ b/drivers/char/tlclk.c @@ -328,7 +328,7 @@ static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); static ssize_t store_received_ref_clk3a(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -350,7 +350,7 @@ static DEVICE_ATTR(received_ref_clk3a, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_received_ref_clk3b(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -372,7 +372,7 @@ static DEVICE_ATTR(received_ref_clk3b, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_enable_clk3b_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -394,7 +394,7 @@ static ssize_t store_enable_clk3a_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -415,7 +415,7 @@ static ssize_t store_enable_clkb1_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -437,7 +437,7 @@ static ssize_t store_enable_clka1_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -458,7 +458,7 @@ static ssize_t store_enable_clkb0_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -479,7 +479,7 @@ static ssize_t store_enable_clka0_output(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -500,7 +500,7 @@ static ssize_t store_select_amcb2_transmit_clock(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; sscanf(buf, "%lX", &tmp); @@ -541,7 +541,7 @@ static DEVICE_ATTR(select_amcb2_transmit_clock, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_select_amcb1_transmit_clock(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -583,7 +583,7 @@ static DEVICE_ATTR(select_amcb1_transmit_clock, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_select_redundant_clock(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -604,7 +604,7 @@ static DEVICE_ATTR(select_redundant_clock, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_select_ref_frequency(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -625,7 +625,7 @@ static DEVICE_ATTR(select_ref_frequency, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_filter_select(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -645,7 +645,7 @@ static DEVICE_ATTR(filter_select, (S_IWUSR|S_IWGRP), NULL, store_filter_select); static ssize_t store_hardware_switching_mode(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -666,7 +666,7 @@ static DEVICE_ATTR(hardware_switching_mode, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_hardware_switching(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -687,7 +687,7 @@ static DEVICE_ATTR(hardware_switching, (S_IWUSR|S_IWGRP), NULL, static ssize_t store_refalign (struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned long flags; sscanf(buf, "%lX", &tmp); @@ -706,7 +706,7 @@ static DEVICE_ATTR(refalign, (S_IWUSR|S_IWGRP), NULL, store_refalign); static ssize_t store_mode_select (struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; @@ -726,7 +726,7 @@ static DEVICE_ATTR(mode_select, (S_IWUSR|S_IWGRP), NULL, store_mode_select); static ssize_t store_reset (struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long tmp; + unsigned long tmp = 0; unsigned char val; unsigned long flags; From 7ddb521ab097413fbdff483b53b4a8c73a0e2b40 Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Fri, 22 May 2026 15:34:47 +0800 Subject: [PATCH 177/513] gpib: Move stuck SRQ update under lock Move the stuck SRQ state update into autopoll_all_devices() and keep it under big_gpib_mutex. Except for initialization, keep the stuck_srq users under this mutex. autopoll_all_devices() is only called by autospoll_thread(), so there is no need to return to autospoll_thread() and set this state after dropping big_gpib_mutex. Without the mutex, a newly opened device can clear stuck_srq and have that clear overwritten by the previous autospoll result: autospoll: serial_poll_all() returns 0 and unlocks big_gpib_mutex open_dev_ioctl: open new device and clear stuck_srq with big_gpib_mutex held autospoll: set stuck_srq That leaves the board marked stuck again after the new device is opened. autospoll_wait_should_wake_up() then refuses to poll while stuck_srq is set, so later SRQ handling can be mistakenly suppressed. Without the mutex, atomic_set() and set_bit() only make individual updates atomic. They do not order the two updates or make stuck_srq and status visible as a consistent pair. Taking big_gpib_mutex serializes the state transition with the other runtime users. Keep the existing wakeup behavior unchanged and only move the stuck SRQ state update under the mutex. Fixes: 9dde4559e939 ("staging: gpib: Add GPIB common core driver") Signed-off-by: Gui-Dong Han Link: https://patch.msgid.link/20260522073447.4117690-1-hanguidong02@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/common/gpib_os.c | 21 +++++++++++---------- drivers/gpib/common/iblib.c | 3 --- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/gpib/common/gpib_os.c b/drivers/gpib/common/gpib_os.c index 69f6aa73ab9a..384800e6bf97 100644 --- a/drivers/gpib/common/gpib_os.c +++ b/drivers/gpib/common/gpib_os.c @@ -289,18 +289,19 @@ int autopoll_all_devices(struct gpib_board *board) dev_dbg(board->gpib_dev, "autopoll has board lock\n"); retval = serial_poll_all(board, serial_timeout); - if (retval < 0) { - mutex_unlock(&board->big_gpib_mutex); - mutex_unlock(&board->user_mutex); - return retval; + if (retval >= 0) { + dev_dbg(board->gpib_dev, "complete\n"); + /* + * need to wake wait queue in case someone is + * waiting on RQS + */ + wake_up_interruptible(&board->wait); } - dev_dbg(board->gpib_dev, "complete\n"); - /* - * need to wake wait queue in case someone is - * waiting on RQS - */ - wake_up_interruptible(&board->wait); + if (retval <= 0) { + atomic_set(&board->stuck_srq, 1); + set_bit(SRQI_NUM, &board->status); + } mutex_unlock(&board->big_gpib_mutex); mutex_unlock(&board->user_mutex); diff --git a/drivers/gpib/common/iblib.c b/drivers/gpib/common/iblib.c index b672dd6aad25..511e1d61c1fb 100644 --- a/drivers/gpib/common/iblib.c +++ b/drivers/gpib/common/iblib.c @@ -193,9 +193,6 @@ static int autospoll_thread(void *board_void) } if (retval <= 0) { dev_err(board->gpib_dev, "stuck SRQ\n"); - - atomic_set(&board->stuck_srq, 1); // XXX could be better - set_bit(SRQI_NUM, &board->status); } } return retval; From 6cc667892818fe44bef02193dfb9c3f843ade2b2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 Jun 2026 15:10:07 +0200 Subject: [PATCH 178/513] gpib: use 'static inline' instead of 'extern inline' With GNU inline semantics, an 'extern inline' function is only included in the build if it can be inlined. When the compiler for some reason decides against inlining it, this causes a link failure, as observed in one function in the tnt4882_gpib driver: ld.lld: error: undefined symbol: mite_irq >>> referenced by tnt4882_gpib.c:974 (/home/arnd/arm-soc/drivers/gpib/tnt4882/tnt4882_gpib.c:974) >>> drivers/gpib/tnt4882/tnt4882_gpib.o:(ni_pci_attach) in archive vmlinux.a Change all of the 'extern inline' definitions in gpib to the regular 'static inline' to avoid this. Fixes: 0cd5b05551e0 ("staging: gpib: Add TNT4882 chip based GPIB driver") Fixes: 6c52d5e3cde2 ("staging: gpib: Add common include files for GPIB drivers") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260611131018.3662609-1-arnd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/include/amccs5933.h | 10 +++++----- drivers/gpib/tnt4882/mite.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpib/include/amccs5933.h b/drivers/gpib/include/amccs5933.h index d7f63c795096..f8a80bdc70dc 100644 --- a/drivers/gpib/include/amccs5933.h +++ b/drivers/gpib/include/amccs5933.h @@ -13,7 +13,7 @@ enum { }; // incoming mailbox 0-3 register offsets -extern inline int INCOMING_MAILBOX_REG(unsigned int mailbox) +static inline int INCOMING_MAILBOX_REG(unsigned int mailbox) { return (0x10 + 4 * mailbox); }; @@ -29,25 +29,25 @@ enum { }; // select byte 0 to 3 of incoming mailbox -extern inline int INBOX_BYTE_BITS(unsigned int byte) +static inline int INBOX_BYTE_BITS(unsigned int byte) { return (byte & 0x3) << 8; }; // select incoming mailbox 0 to 3 -extern inline int INBOX_SELECT_BITS(unsigned int mailbox) +static inline int INBOX_SELECT_BITS(unsigned int mailbox) { return (mailbox & 0x3) << 10; }; // select byte 0 to 3 of outgoing mailbox -extern inline int OUTBOX_BYTE_BITS(unsigned int byte) +static inline int OUTBOX_BYTE_BITS(unsigned int byte) { return (byte & 0x3); }; // select outgoing mailbox 0 to 3 -extern inline int OUTBOX_SELECT_BITS(unsigned int mailbox) +static inline int OUTBOX_SELECT_BITS(unsigned int mailbox) { return (mailbox & 0x3) << 2; }; diff --git a/drivers/gpib/tnt4882/mite.h b/drivers/gpib/tnt4882/mite.h index a1fdba9672a0..dd251afa90e3 100644 --- a/drivers/gpib/tnt4882/mite.h +++ b/drivers/gpib/tnt4882/mite.h @@ -45,12 +45,12 @@ struct mite_struct { extern struct mite_struct *mite_devices; -extern inline unsigned int mite_irq(struct mite_struct *mite) +static inline unsigned int mite_irq(struct mite_struct *mite) { return mite->pcidev->irq; }; -extern inline unsigned int mite_device_id(struct mite_struct *mite) +static inline unsigned int mite_device_id(struct mite_struct *mite) { return mite->pcidev->device; }; From a168e2cfb741d81be3736dba6dbe5edc2295aacb Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Sun, 21 Jun 2026 15:49:26 +0800 Subject: [PATCH 179/513] gpib: lpvo_usb: fix path of the "debug" module parameter in comment The correct path of the "debug" module parameter should be /sys/module/lpvo_usb_gpib/parameters/debug. Fix it. Signed-off-by: Zenghui Yu Acked-by: Randy Dunlap Link: https://patch.msgid.link/20260621074926.11252-1-zenghui.yu@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c index e6ea9422d6f2..13a067010db7 100644 --- a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c +++ b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c @@ -68,7 +68,7 @@ MODULE_DEVICE_TABLE(usb, lpvo_table); * (about twice the log volume of [1]) * To switch debug level: * At module loading: modprobe lpvo_usb_gpib debug={0,1,2} - * On the fly: echo {0,1,2} > /sys/modules/lpvo_usb_gpib/parameters/debug + * On the fly: echo {0,1,2} > /sys/module/lpvo_usb_gpib/parameters/debug */ static int debug; From 9d4ce1c7cd35f6333785d0ef1c3032f6ecb1a5b8 Mon Sep 17 00:00:00 2001 From: Mohammad Shahid Date: Sun, 5 Jul 2026 10:47:01 +0530 Subject: [PATCH 180/513] gpib: lpvo_usb_gpib: use memdup_user() instead of kmalloc() and copy_from_user() Use memdup_user() to replace the open-coded kmalloc() and copy_from_user() sequence. This simplifies the code while preserving the existing behavior. This issue was reported by memdup_user.cocci. Signed-off-by: Mohammad Shahid Link: https://patch.msgid.link/20260705051701.142070-1-mdshahid03@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c index 13a067010db7..dfb8f3ebc27f 100644 --- a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c +++ b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c @@ -1811,14 +1811,9 @@ static ssize_t lpvo_write(struct file *file, const char __user *user_buffer, dev = file->private_data; - buf = kmalloc(count, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (copy_from_user(buf, user_buffer, count)) { - kfree(buf); - return -EFAULT; - } + buf = memdup_user(user_buffer, count); + if (IS_ERR(buf)) + return PTR_ERR(buf); rv = lpvo_do_write(dev, buf, count); kfree(buf); From fbf64f3595a68b56df324bdea96fb03542ae9334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 29 Jun 2026 18:54:26 +0200 Subject: [PATCH 181/513] gpib: Initialize pci_device_ids using PCI_DEVICE macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCI_DEVICE macro and its variants allow to initialize the first four members of a struct pci_device_id in a bit more compact form and also with an easier to grasp semantic. Explicit zeros are not needed, the compiler fills these in automatically. So convert all array members to such a macro and drop unneeded zeros. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260629165426.3330888-2-u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/agilent_82350b/agilent_82350b.c | 9 ++++----- drivers/gpib/cb7210/cb7210.c | 8 ++++---- drivers/gpib/cec/cec_gpib.c | 4 ++-- drivers/gpib/ines/ines_gpib.c | 21 ++++++++++++-------- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/gpib/agilent_82350b/agilent_82350b.c b/drivers/gpib/agilent_82350b/agilent_82350b.c index 9787c09faad8..490d44ad1b50 100644 --- a/drivers/gpib/agilent_82350b/agilent_82350b.c +++ b/drivers/gpib/agilent_82350b/agilent_82350b.c @@ -839,11 +839,10 @@ static int agilent_82350b_pci_probe(struct pci_dev *dev, const struct pci_device } static const struct pci_device_id agilent_82350b_pci_table[] = { - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_HP, - PCI_SUBDEVICE_ID_82350A, 0, 0, 0 }, - { PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82350B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82351A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0 } + { PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_HP, PCI_SUBDEVICE_ID_82350A) }, + { PCI_VDEVICE(AGILENT, PCI_DEVICE_ID_82350B) }, + { PCI_VDEVICE(AGILENT, PCI_DEVICE_ID_82351A) }, + { } }; MODULE_DEVICE_TABLE(pci, agilent_82350b_pci_table); diff --git a/drivers/gpib/cb7210/cb7210.c b/drivers/gpib/cb7210/cb7210.c index 6dd8637c5964..c62c30fc3472 100644 --- a/drivers/gpib/cb7210/cb7210.c +++ b/drivers/gpib/cb7210/cb7210.c @@ -1093,10 +1093,10 @@ static int cb7210_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } static const struct pci_device_id cb7210_pci_table[] = { - {PCI_VENDOR_ID_CBOARDS, PCI_DEVICE_ID_CBOARDS_PCI_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - {PCI_VENDOR_ID_CBOARDS, PCI_DEVICE_ID_CBOARDS_CPCI_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - {PCI_VENDOR_ID_QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0 } + { PCI_VDEVICE(CBOARDS, PCI_DEVICE_ID_CBOARDS_PCI_GPIB) }, + { PCI_VDEVICE(CBOARDS, PCI_DEVICE_ID_CBOARDS_CPCI_GPIB) }, + { PCI_VDEVICE(QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB) }, + { } }; MODULE_DEVICE_TABLE(pci, cb7210_pci_table); diff --git a/drivers/gpib/cec/cec_gpib.c b/drivers/gpib/cec/cec_gpib.c index c13bc302d9e9..530a777b1384 100644 --- a/drivers/gpib/cec/cec_gpib.c +++ b/drivers/gpib/cec/cec_gpib.c @@ -353,8 +353,8 @@ static int cec_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } static const struct pci_device_id cec_pci_table[] = { - {CEC_VENDOR_ID, CEC_DEV_ID, PCI_ANY_ID, CEC_SUBID, 0, 0, 0 }, - {0} + { PCI_DEVICE_SUB(CEC_VENDOR_ID, CEC_DEV_ID, PCI_ANY_ID, CEC_SUBID) }, + { } }; MODULE_DEVICE_TABLE(pci, cec_pci_table); diff --git a/drivers/gpib/ines/ines_gpib.c b/drivers/gpib/ines/ines_gpib.c index 3562f3184c28..6cd6ff596fda 100644 --- a/drivers/gpib/ines/ines_gpib.c +++ b/drivers/gpib/ines/ines_gpib.c @@ -371,14 +371,19 @@ enum ines_pci_subdevice_ids { }; static struct pci_device_id ines_pci_table[] = { - {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_PLX, - PCI_SUBDEVICE_ID_INES_GPIB, 0, 0, 0}, - {PCI_VENDOR_ID_AMCC, PCI_DEVICE_ID_INES_GPIB_AMCC, PCI_VENDOR_ID_AMCC, - PCI_SUBDEVICE_ID_INES_GPIB, 0, 0, 0}, - {PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030, - PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030, 0, 0, 0}, - {PCI_DEVICE(PCI_VENDOR_ID_QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB)}, - {0} + { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, PCI_SUBDEVICE_ID_INES_GPIB), + }, { + PCI_VDEVICE_SUB(AMCC, PCI_DEVICE_ID_INES_GPIB_AMCC, + PCI_VENDOR_ID_AMCC, PCI_SUBDEVICE_ID_INES_GPIB), + }, { + PCI_VDEVICE_SUB(INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030, + PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030), + }, { + PCI_VDEVICE(QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB), + }, + { } }; MODULE_DEVICE_TABLE(pci, ines_pci_table); From bb14d970bf664a719570370b669a412581a77a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 10 Jun 2026 10:31:04 +0200 Subject: [PATCH 182/513] gpib: Improve style of pnp_device_id array terminators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To match how device-id array terminators look like for other device types drop `.id = ""` from it and let the compiler care for zeroing the entry. While touching these arrays, also align spacing to how these arrays are usually written. There are no changes in the compiled drivers, only the source looks nicer. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/f23c9d77836f3b14b1efc58e3969333e8addb1c1.1781078782.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/hp_82341/hp_82341.c | 4 ++-- drivers/gpib/tnt4882/tnt4882_gpib.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpib/hp_82341/hp_82341.c b/drivers/gpib/hp_82341/hp_82341.c index 46175ba2ac36..e98cf5aea99f 100644 --- a/drivers/gpib/hp_82341/hp_82341.c +++ b/drivers/gpib/hp_82341/hp_82341.c @@ -803,8 +803,8 @@ static void hp_82341_detach(struct gpib_board *board) #if 0 /* unused, will be needed when the driver is turned into a pnp_driver */ static const struct pnp_device_id hp_82341_pnp_table[] = { - {.id = "HWP1411"}, - {.id = ""} + { .id = "HWP1411" }, + { } }; MODULE_DEVICE_TABLE(pnp, hp_82341_pnp_table); #endif diff --git a/drivers/gpib/tnt4882/tnt4882_gpib.c b/drivers/gpib/tnt4882/tnt4882_gpib.c index 51a920e1d9a4..3cd13f637ed4 100644 --- a/drivers/gpib/tnt4882/tnt4882_gpib.c +++ b/drivers/gpib/tnt4882/tnt4882_gpib.c @@ -1373,8 +1373,8 @@ static struct pci_driver tnt4882_pci_driver = { #if 0 /* unused, will be needed when the driver is turned into a pnp_driver */ static const struct pnp_device_id tnt4882_pnp_table[] = { - {.id = "NICC601"}, - {.id = ""} + { .id = "NICC601" }, + { } }; MODULE_DEVICE_TABLE(pnp, tnt4882_pnp_table); #endif From 7bf924f96d6ef4dc7700e7adf984e3b90492c435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 29 Apr 2026 18:14:53 +0200 Subject: [PATCH 183/513] parport: Consistently define pci_device_ids using named initializers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ... and PCI device helpers. The various struct pci_device_id arrays were initialized mostly by list expressions. This isn't easily readable if you're not into PCI. Use PCI_DEVICE* helper macros and named initializers which is more explicit and thus easier to parse. Also skip explicit assignments of 0 (which the compiler then takes care of). The secret plan is to make struct pci_device_id::driver_data an anonymous union (similar to https://lore.kernel.org/all/cover.1776579304.git.u.kleine-koenig@baylibre.com/) and that requires named initializers. But it's also a nice cleanup on its own. This change doesn't introduce changes to the compiled pci_device_id arrays. Tested on x86 and arm64. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260429161453.4154681-2-u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/parport/parport_pc.c | 204 +++++++++++----------- drivers/parport/parport_serial.c | 280 ++++++++++++++++--------------- 2 files changed, 257 insertions(+), 227 deletions(-) diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index c75abdd8ef25..bccfe75fe6e5 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -2745,116 +2745,128 @@ static struct parport_pc_pci { static const struct pci_device_id parport_pc_pci_tbl[] = { /* Super-IO onboard chips */ - { 0x1106, 0x0686, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_via_686a }, - { 0x1106, 0x8231, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_via_8231 }, - { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8872, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_ite_8872 }, + { PCI_DEVICE(0x1106, 0x0686), + .driver_data = sio_via_686a }, + { PCI_DEVICE(0x1106, 0x8231), + .driver_data = sio_via_8231 }, + { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8872), + .driver_data = sio_ite_8872 }, /* PCI cards */ - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_10x, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_10x, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_20x, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_20x, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p_20x }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PARALLEL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel_dual_a }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, lava_parallel_dual_b }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_BOCA_IOPPAR, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, boca_ioppar }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4014, 0, 0, plx_9050 }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1P_10x), + .driver_data = siig_1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P_10x), + .driver_data = siig_2p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1P_20x), + .driver_data = siig_1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P_20x), + .driver_data = siig_2p_20x }, + { PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_PARALLEL), + .driver_data = lava_parallel }, + { PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_A), + .driver_data = lava_parallel_dual_a }, + { PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_B), + .driver_data = lava_parallel_dual_b }, + { PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_BOCA_IOPPAR), + .driver_data = boca_ioppar }, + { PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4014), + .driver_data = plx_9050 }, /* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/ - { 0x1409, 0x7268, 0x1409, 0x0101, 0, 0, timedia_4006a }, - { 0x1409, 0x7268, 0x1409, 0x0102, 0, 0, timedia_4014 }, - { 0x1409, 0x7268, 0x1409, 0x0103, 0, 0, timedia_4008a }, - { 0x1409, 0x7268, 0x1409, 0x0104, 0, 0, timedia_4018 }, - { 0x1409, 0x7268, 0x1409, 0x9018, 0, 0, timedia_9018a }, - { PCI_VENDOR_ID_SYBA, PCI_DEVICE_ID_SYBA_2P_EPP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, syba_2p_epp }, - { PCI_VENDOR_ID_SYBA, PCI_DEVICE_ID_SYBA_1P_ECP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, syba_1p_ecp }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_010L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_010l }, + { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x0101), + .driver_data = timedia_4006a }, + { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x0102), + .driver_data = timedia_4014 }, + { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x0103), + .driver_data = timedia_4008a }, + { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x0104), + .driver_data = timedia_4018 }, + { PCI_DEVICE_SUB(0x1409, 0x7268, 0x1409, 0x9018), + .driver_data = timedia_9018a }, + { PCI_VDEVICE(SYBA, PCI_DEVICE_ID_SYBA_2P_EPP), + .driver_data = syba_2p_epp }, + { PCI_VDEVICE(SYBA, PCI_DEVICE_ID_SYBA_1P_ECP), + .driver_data = syba_1p_ecp }, + { PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_010L), + .driver_data = titan_010l }, /* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/ /* AFAVLAB_TK9902 */ - { 0x14db, 0x2120, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1p}, - { 0x14db, 0x2121, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2p}, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952PP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_952 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954PP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_954 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_12PCI840, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_840 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840_G, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0_G, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_G, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_U, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_GU, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { PCI_VENDOR_ID_AKS, PCI_DEVICE_ID_AKS_ALADDINCARD, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, aks_0100 }, - { 0x14f2, 0x0121, PCI_ANY_ID, PCI_ANY_ID, 0, 0, mobility_pp }, + { PCI_DEVICE(0x14db, 0x2120), + .driver_data = avlab_1p }, + { PCI_DEVICE(0x14db, 0x2121), + .driver_data = avlab_2p }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952PP), + .driver_data = oxsemi_952 }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954PP), + .driver_data = oxsemi_954 }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_12PCI840), + .driver_data = oxsemi_840 }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe840_G), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_0_G), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_G), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_U), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_PCIe952_1_GU), + .driver_data = oxsemi_pcie_pport }, + { PCI_VDEVICE(AKS, PCI_DEVICE_ID_AKS_ALADDINCARD), + .driver_data = aks_0100 }, + { PCI_DEVICE(0x14f2, 0x0121), + .driver_data = mobility_pp }, /* NetMos communication controllers */ - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, - 0xA000, 0x2000, 0, 0, netmos_9900 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9705, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9705 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9715, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9715 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9755, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9755 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9805, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9805 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9815, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9815 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901, - 0xA000, 0x2000, 0, 0, netmos_9901 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, - 0xA000, 0x1000, 0, 0, netmos_9865 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, - 0xA000, 0x2000, 0, 0, netmos_9865 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, 0xA000, 0x2000), + .driver_data = netmos_9900 }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9705), + .driver_data = netmos_9705 }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9715), + .driver_data = netmos_9715 }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9755), + .driver_data = netmos_9755 }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9805), + .driver_data = netmos_9805 }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9815), + .driver_data = netmos_9815 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9901, 0xA000, 0x2000), + .driver_data = netmos_9901 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9865, 0xA000, 0x1000), + .driver_data = netmos_9865 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9865, 0xA000, 0x2000), + .driver_data = netmos_9865 }, /* ASIX AX99100 PCIe to Multi I/O Controller */ - { PCI_VENDOR_ID_ASIX, PCI_DEVICE_ID_ASIX_AX99100, - 0xA000, 0x2000, 0, 0, asix_ax99100 }, + { PCI_VDEVICE_SUB(ASIX, PCI_DEVICE_ID_ASIX_AX99100, 0xA000, 0x2000), + .driver_data = asix_ax99100 }, /* Quatech SPPXP-100 Parallel port PCI ExpressCard */ - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, quatech_sppxp100 }, + { PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100), + .driver_data = quatech_sppxp100 }, /* WCH CH382L PCI-E single parallel port card */ - { 0x1c00, 0x3050, 0x1c00, 0x3050, 0, 0, wch_ch382l }, + { PCI_DEVICE_SUB(0x1c00, 0x3050, 0x1c00, 0x3050), + .driver_data = wch_ch382l }, /* Brainboxes IX-500/550 */ - { PCI_VENDOR_ID_INTASHIELD, 0x402a, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, + { PCI_VDEVICE(INTASHIELD, 0x402a), + .driver_data = oxsemi_pcie_pport }, /* Brainboxes UC-146/UC-157 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0be1, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc146 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0be2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc146 }, + { PCI_VDEVICE(INTASHIELD, 0x0be1), + .driver_data = brainboxes_uc146 }, + { PCI_VDEVICE(INTASHIELD, 0x0be2), + .driver_data = brainboxes_uc146 }, /* Brainboxes PX-146/PX-257 */ - { PCI_VENDOR_ID_INTASHIELD, 0x401c, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, + { PCI_VDEVICE(INTASHIELD, 0x401c), + .driver_data = oxsemi_pcie_pport }, /* Brainboxes PX-203 */ - { PCI_VENDOR_ID_INTASHIELD, 0x4007, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_px203 }, + { PCI_VDEVICE(INTASHIELD, 0x4007), + .driver_data = brainboxes_px203 }, /* Brainboxes PX-475 */ - { PCI_VENDOR_ID_INTASHIELD, 0x401f, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_pcie_pport }, - { 0, } /* terminate list */ + { PCI_VDEVICE(INTASHIELD, 0x401f), + .driver_data = oxsemi_pcie_pport }, + { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, parport_pc_pci_tbl); diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index 24d4f3a3ec3d..4d4140c8584d 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -170,158 +170,176 @@ static struct parport_pc_pci cards[] = { static struct pci_device_id parport_serial_pci_tbl[] = { /* PCI cards */ - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_110L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_110l }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_210L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_210l }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9735, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9745, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9845, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, - 0x1000, 0x0020, 0, 0, netmos_9855_2p }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, - 0x1000, 0x0022, 0, 0, netmos_9855_2p }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9855 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, - 0xA000, 0x3011, 0, 0, netmos_9900 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, - 0xA000, 0x3012, 0, 0, netmos_9900 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, - 0xA000, 0x3020, 0, 0, netmos_9900_2p }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912, - 0xA000, 0x2000, 0, 0, netmos_99xx_1p }, + { PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_110L), + .driver_data = titan_110l }, + { PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_210L), + .driver_data = titan_210l }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9735), + .driver_data = netmos_9xx5_combo }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9745), + .driver_data = netmos_9xx5_combo }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9835), + .driver_data = netmos_9xx5_combo }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9845), + .driver_data = netmos_9xx5_combo }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9855, 0x1000, 0x0020), + .driver_data = netmos_9855_2p }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9855, 0x1000, 0x0022), + .driver_data = netmos_9855_2p }, + { PCI_VDEVICE(NETMOS, PCI_DEVICE_ID_NETMOS_9855), + .driver_data = netmos_9855 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, 0xA000, 0x3011), + .driver_data = netmos_9900 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, 0xA000, 0x3012), + .driver_data = netmos_9900 }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, 0xA000, 0x3020), + .driver_data = netmos_9900_2p }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9912, 0xA000, 0x2000), + .driver_data = netmos_99xx_1p }, /* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/ - { PCI_VENDOR_ID_AFAVLAB, 0x2110, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2111, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2112, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2140, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2141, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2142, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2160, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2161, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p }, - { PCI_VENDOR_ID_AFAVLAB, 0x2162, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, + { PCI_VDEVICE(AFAVLAB, 0x2110), + .driver_data = avlab_1s1p }, + { PCI_VDEVICE(AFAVLAB, 0x2111), + .driver_data = avlab_1s1p }, + { PCI_VDEVICE(AFAVLAB, 0x2112), + .driver_data = avlab_1s1p }, + { PCI_VDEVICE(AFAVLAB, 0x2140), + .driver_data = avlab_1s2p }, + { PCI_VDEVICE(AFAVLAB, 0x2141), + .driver_data = avlab_1s2p }, + { PCI_VDEVICE(AFAVLAB, 0x2142), + .driver_data = avlab_1s2p }, + { PCI_VDEVICE(AFAVLAB, 0x2160), + .driver_data = avlab_2s1p }, + { PCI_VDEVICE(AFAVLAB, 0x2161), + .driver_data = avlab_2s1p }, + { PCI_VDEVICE(AFAVLAB, 0x2162), + .driver_data = avlab_2s1p }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550), + .driver_data = siig_1s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650), + .driver_data = siig_1s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850), + .driver_data = siig_1s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550), + .driver_data = siig_2s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650), + .driver_data = siig_2s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850), + .driver_data = siig_2s1p_10x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550), + .driver_data = siig_2p1s_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650), + .driver_data = siig_2p1s_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850), + .driver_data = siig_2p1s_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550), + .driver_data = siig_2s1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650), + .driver_data = siig_1s1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850), + .driver_data = siig_1s1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550), + .driver_data = siig_2s1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650), + .driver_data = siig_2s1p_20x }, + { PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850), + .driver_data = siig_2s1p_20x }, /* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/ - { 0x1409, 0x7168, 0x1409, 0x4078, 0, 0, timedia_4078a }, - { 0x1409, 0x7168, 0x1409, 0x4079, 0, 0, timedia_4079h }, - { 0x1409, 0x7168, 0x1409, 0x4085, 0, 0, timedia_4085h }, - { 0x1409, 0x7168, 0x1409, 0x4088, 0, 0, timedia_4088a }, - { 0x1409, 0x7168, 0x1409, 0x4089, 0, 0, timedia_4089a }, - { 0x1409, 0x7168, 0x1409, 0x4095, 0, 0, timedia_4095a }, - { 0x1409, 0x7168, 0x1409, 0x4096, 0, 0, timedia_4096a }, - { 0x1409, 0x7168, 0x1409, 0x5078, 0, 0, timedia_4078u }, - { 0x1409, 0x7168, 0x1409, 0x5079, 0, 0, timedia_4079a }, - { 0x1409, 0x7168, 0x1409, 0x5085, 0, 0, timedia_4085u }, - { 0x1409, 0x7168, 0x1409, 0x6079, 0, 0, timedia_4079r }, - { 0x1409, 0x7168, 0x1409, 0x7079, 0, 0, timedia_4079s }, - { 0x1409, 0x7168, 0x1409, 0x8079, 0, 0, timedia_4079d }, - { 0x1409, 0x7168, 0x1409, 0x9079, 0, 0, timedia_4079e }, - { 0x1409, 0x7168, 0x1409, 0xa079, 0, 0, timedia_4079f }, - { 0x1409, 0x7168, 0x1409, 0xb079, 0, 0, timedia_9079a }, - { 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b }, - { 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4078), + .driver_data = timedia_4078a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4079), + .driver_data = timedia_4079h }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4085), + .driver_data = timedia_4085h }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4088), + .driver_data = timedia_4088a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4089), + .driver_data = timedia_4089a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4095), + .driver_data = timedia_4095a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x4096), + .driver_data = timedia_4096a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x5078), + .driver_data = timedia_4078u }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x5079), + .driver_data = timedia_4079a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x5085), + .driver_data = timedia_4085u }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x6079), + .driver_data = timedia_4079r }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x7079), + .driver_data = timedia_4079s }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x8079), + .driver_data = timedia_4079d }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0x9079), + .driver_data = timedia_4079e }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0xa079), + .driver_data = timedia_4079f }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0xb079), + .driver_data = timedia_9079a }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0xc079), + .driver_data = timedia_9079b }, + { PCI_DEVICE_SUB(0x1409, 0x7168, 0x1409, 0xd079), + .driver_data = timedia_9079c }, /* WCH CARDS */ - { PCI_VENDOR_ID_WCHCN, PCI_DEVICE_ID_WCHCN_CH353_1S1P, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, wch_ch353_1s1p }, - { PCI_VENDOR_ID_WCHCN, PCI_DEVICE_ID_WCHCN_CH353_2S1P, - 0x4348, 0x3253, 0, 0, wch_ch353_2s1p }, - { PCI_VENDOR_ID_WCHIC, PCI_DEVICE_ID_WCHIC_CH382_0S1P, - 0x1c00, 0x3050, 0, 0, wch_ch382_0s1p }, - { PCI_VENDOR_ID_WCHIC, PCI_DEVICE_ID_WCHIC_CH382_2S1P, - 0x1c00, 0x3250, 0, 0, wch_ch382_2s1p }, + { PCI_VDEVICE(WCHCN, PCI_DEVICE_ID_WCHCN_CH353_1S1P), + .driver_data = wch_ch353_1s1p }, + { PCI_VDEVICE_SUB(WCHCN, PCI_DEVICE_ID_WCHCN_CH353_2S1P, 0x4348, 0x3253), + .driver_data = wch_ch353_2s1p }, + { PCI_VDEVICE_SUB(WCHIC, PCI_DEVICE_ID_WCHIC_CH382_0S1P, 0x1c00, 0x3050), + .driver_data = wch_ch382_0s1p }, + { PCI_VDEVICE_SUB(WCHIC, PCI_DEVICE_ID_WCHIC_CH382_2S1P, 0x1c00, 0x3250), + .driver_data = wch_ch382_2s1p }, /* BrainBoxes PX272/PX306 MIO card */ - { PCI_VENDOR_ID_INTASHIELD, 0x4100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_5s1p }, + { PCI_VDEVICE(INTASHIELD, 0x4100), + .driver_data = brainboxes_5s1p }, /* Sunix boards */ - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, - 0x0100, 0, 0, sunix_4008a }, - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, - 0x0101, 0, 0, sunix_5069a }, - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, - 0x0102, 0, 0, sunix_5079a }, - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, - 0x0104, 0, 0, sunix_5099a }, + { PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, 0x0100), + .driver_data = sunix_4008a }, + { PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, 0x0101), + .driver_data = sunix_5069a }, + { PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, 0x0102), + .driver_data = sunix_5079a }, + { PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, 0x0104), + .driver_data = sunix_5099a }, /* Brainboxes UC-203 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0bc1, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0bc2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0bc1), + .driver_data = brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0bc2), + .driver_data = brainboxes_uc257 }, /* Brainboxes UC-257 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0861, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0862, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0863, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0861), + .driver_data = brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0862), + .driver_data = brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0863), + .driver_data = brainboxes_uc257 }, /* Brainboxes UC-414 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0e61, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc414 }, + { PCI_VDEVICE(INTASHIELD, 0x0e61), + .driver_data = brainboxes_uc414 }, /* Brainboxes UC-475 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0981, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0982, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0981), + .driver_data = brainboxes_uc257 }, + { PCI_VDEVICE(INTASHIELD, 0x0982), + .driver_data = brainboxes_uc257 }, /* Brainboxes IS-300/IS-500 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0da0, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_is300 }, + { PCI_VDEVICE(INTASHIELD, 0x0da0), + .driver_data = brainboxes_is300 }, /* Brainboxes PX-263/PX-295 */ - { PCI_VENDOR_ID_INTASHIELD, 0x402c, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_px263 }, + { PCI_VDEVICE(INTASHIELD, 0x402c), + .driver_data = brainboxes_px263 }, - { 0, } /* terminate list */ + { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl); From 67b6fc084b034a91c3ec7907a3fed89a2450f30b Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Tue, 30 Jun 2026 15:27:14 -0400 Subject: [PATCH 184/513] uio: Fix stale info pointer in failed registration path After device_add(), the UIO device is visible to userspace and /dev/uioX can be opened. If a later setup step fails, __uio_register_device() unwinds the device but leaves idev->info pointing at the caller-owned struct uio_info. That is unsafe when an opener races with the failed registration path. The open file keeps a reference to the uio_device, while the caller sees registration failure and may free its struct uio_info. Later file operations can then follow idev->info and dereference freed memory. Handle post-device_add() failures like unregister: remove UIO attributes while the info pointer is still valid, then clear idev->info under info_lock and wake existing waiters/async users before removing the device and minor. This makes already-open file descriptors observe the same "device gone" state as normal uio_unregister_device(). Fixes: a93e7b331568 ("uio: Prevent device destruction while fds are open") Signed-off-by: Yuho Choi Link: https://patch.msgid.link/20260630192714.1867170-1-dbgh9129@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 1e4ade78ed84..e77d5e7d5f64 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -1057,6 +1057,11 @@ int __uio_register_device(struct module *owner, err_request_irq: uio_dev_del_attributes(idev); err_uio_dev_add_attributes: + mutex_lock(&idev->info_lock); + idev->info = NULL; + mutex_unlock(&idev->info_lock); + wake_up_interruptible(&idev->wait); + kill_fasync(&idev->async_queue, SIGIO, POLL_HUP); device_del(&idev->dev); err_device_create: uio_free_minor(idev->minor); From a432f68c51fc6761658445e80a798b8425fdb3dc Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 23:26:42 +0800 Subject: [PATCH 185/513] uio: sercos3: add missing MODULE_DEVICE_TABLE() The driver has a match table for the pci bus wired into its driver structure, but the table is not exported with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE() entry so module alias information is generated for automatic module loading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the driver registration structure, and the missing module alias publication. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260704152642.54769-1-pengpeng@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_sercos3.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/uio/uio_sercos3.c b/drivers/uio/uio_sercos3.c index 12afc2fa1a0b..80087897f9b8 100644 --- a/drivers/uio/uio_sercos3.c +++ b/drivers/uio/uio_sercos3.c @@ -212,6 +212,7 @@ static const struct pci_device_id sercos3_pci_ids[] = { }, { 0, } }; +MODULE_DEVICE_TABLE(pci, sercos3_pci_ids); static struct pci_driver sercos3_pci_driver = { .name = "sercos3", From a477830bc2d615fa0dfd52508d8c94cec6721299 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 14 Jul 2026 19:56:24 +0100 Subject: [PATCH 186/513] uio: make read-only const array porttypes static Don't populate the read-only const array porttypes on the stack at run time, instead make it static const char * const Signed-off-by: Colin Ian King Link: https://patch.msgid.link/20260714185624.192829-1-colin.i.king@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index e77d5e7d5f64..f8fa20522660 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -150,7 +150,7 @@ static ssize_t portio_size_show(struct uio_port *port, char *buf) static ssize_t portio_porttype_show(struct uio_port *port, char *buf) { - const char *porttypes[] = {"none", "x86", "gpio", "other"}; + static const char * const porttypes[] = {"none", "x86", "gpio", "other"}; if ((port->porttype < 0) || (port->porttype > UIO_PORT_OTHER)) return -EINVAL; From 3adfac7a7494253c05b2a76508aec52dd0894ddf Mon Sep 17 00:00:00 2001 From: Li zeming Date: Mon, 1 Jun 2026 01:07:50 +0200 Subject: [PATCH 187/513] accessibility/speakup/speakup_acnt: Add header file macro definition I think the header file could avoid redefinition errors. at compile time by adding macro definitions. Signed-off-by: Li zeming Signed-off-by: Samuel Thibault Reviewed-by: Samuel Thibault Link: https://patch.msgid.link/20260531230804.254962-2-samuel.thibault@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/speakup_acnt.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/accessibility/speakup/speakup_acnt.h b/drivers/accessibility/speakup/speakup_acnt.h index cffa938ae580..cea05d770f6d 100644 --- a/drivers/accessibility/speakup/speakup_acnt.h +++ b/drivers/accessibility/speakup/speakup_acnt.h @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* speakup_acntpc.h - header file for speakups Accent-PC driver. */ +#ifndef _SPEAKUP_ACNT_H +#define _SPEAKUP_ACNT_H #define SYNTH_IO_EXTENT 0x02 @@ -17,3 +19,4 @@ #define SYNTH_FULL 'F' /* synth is full. */ #define SYNTH_ALMOST_EMPTY 'M' /* synth has less than 2 seconds of text left */ #define SYNTH_SPEAKING 's' /* synth is speaking and has a fare way to go */ +#endif From 9601f59a1966f12c0d4652e32663f3553f20a9e9 Mon Sep 17 00:00:00 2001 From: Li zeming Date: Mon, 1 Jun 2026 01:07:51 +0200 Subject: [PATCH 188/513] accessibility/speakup/speakup_dtlk: Add header file macro definition Add header file macro definition. Signed-off-by: Li zeming Signed-off-by: Samuel Thibault Reviewed-by: Samuel Thibault Link: https://patch.msgid.link/20260531230804.254962-3-samuel.thibault@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/speakup_dtlk.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/accessibility/speakup/speakup_dtlk.h b/drivers/accessibility/speakup/speakup_dtlk.h index 9c378b58066e..101848edec2e 100644 --- a/drivers/accessibility/speakup/speakup_dtlk.h +++ b/drivers/accessibility/speakup/speakup_dtlk.h @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* speakup_dtlk.h - header file for speakups DoubleTalk driver. */ +#ifndef _SPEAKUP_DTLK_H +#define _SPEAKUP_DTLK_H #define SYNTH_IO_EXTENT 0x02 #define SYNTH_CLEAR 0x18 /* stops speech */ @@ -61,3 +63,4 @@ struct synth_settings { */ u_char has_indexing; /* nonzero if indexing is implemented */ }; +#endif From fe58bfdd013ed84bd308321fac8f54c8ec4b823c Mon Sep 17 00:00:00 2001 From: Xu Panda Date: Mon, 1 Jun 2026 01:07:52 +0200 Subject: [PATCH 189/513] speakup/utils: use "!P" instead of "P == 0" comparing pointer to 0, use !P instead of it. Reported-by: Zeal Robot Signed-off-by: Xu Panda Signed-off-by: Samuel Thibault Reviewed-by: Samuel Thibault Link: https://patch.msgid.link/20260531230804.254962-4-samuel.thibault@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accessibility/speakup/utils.h b/drivers/accessibility/speakup/utils.h index 4ce9a12f7664..db00c962f8e2 100644 --- a/drivers/accessibility/speakup/utils.h +++ b/drivers/accessibility/speakup/utils.h @@ -36,7 +36,7 @@ static inline void open_input(const char *dir_name, const char *name) else snprintf(filename, sizeof(filename), "%s", name); infile = fopen(filename, "r"); - if (infile == 0) { + if (!infile) { fprintf(stderr, "can't open %s\n", filename); exit(1); } From bce0e640623372520d9d90c42f33ddbfb576ce69 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 1 Jun 2026 01:07:53 +0200 Subject: [PATCH 190/513] accessibility: speakup: Fix incorrect string length computation in report_char_chartab_status() snprintf() returns the "number of characters which *would* be generated for the given input", not the size *really* generated. In order to avoid too large values for 'len' (and potential negative values for "sizeof(buf) - (len - 1)") use scnprintf() instead of snprintf(). Fixes: c6e3fd22cd53 ("Staging: add speakup to the staging directory") Signed-off-by: Christophe JAILLET Signed-off-by: Samuel Thibault Reviewed-by: Samuel Thibault Reviewed-by: Dan Carpenter Link: https://patch.msgid.link/20260531230804.254962-5-samuel.thibault@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/kobjects.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/accessibility/speakup/kobjects.c b/drivers/accessibility/speakup/kobjects.c index 0dfdb6608e02..943ef71b1329 100644 --- a/drivers/accessibility/speakup/kobjects.c +++ b/drivers/accessibility/speakup/kobjects.c @@ -92,9 +92,9 @@ static void report_char_chartab_status(int reset, int received, int used, if (reset) { pr_info("%s reset to defaults\n", object_type[do_characters]); } else if (received) { - len = snprintf(buf, sizeof(buf), - " updated %d of %d %s\n", - used, received, object_type[do_characters]); + len = scnprintf(buf, sizeof(buf), + " updated %d of %d %s\n", + used, received, object_type[do_characters]); if (rejected) snprintf(buf + (len - 1), sizeof(buf) - (len - 1), " with %d reject%s\n", From d59f36095e115315cdb14ef1f8a56c55f5d36243 Mon Sep 17 00:00:00 2001 From: bajing Date: Mon, 1 Jun 2026 01:07:54 +0200 Subject: [PATCH 191/513] speakup: genmap: remove redundant post-increment In the while loop, the variable lc is unused and is reinitialized later, so this redundant operation should be removed. Signed-off-by: bajing Signed-off-by: Samuel Thibault Reviewed-by: Samuel Thibault Link: https://patch.msgid.link/20260531230804.254962-6-samuel.thibault@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/genmap.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/accessibility/speakup/genmap.c b/drivers/accessibility/speakup/genmap.c index 0882bab10fb8..8a5125db471e 100644 --- a/drivers/accessibility/speakup/genmap.c +++ b/drivers/accessibility/speakup/genmap.c @@ -71,7 +71,6 @@ main(int argc, char *argv[]) open_input(NULL, argv[1]); while (fgets(buffer, sizeof(buffer), infile)) { - lc++; value = shift_state = 0; cp = strtok(buffer, delims); From 2d2e326af3df23ed926b19f74454aaf00ad0eaed Mon Sep 17 00:00:00 2001 From: liujing Date: Mon, 1 Jun 2026 01:07:55 +0200 Subject: [PATCH 192/513] speakup: Fix the wrong format specifier Make a minor change to eliminate a static checker warning. The type of '(unsigned int)kp[i]' is unsigned int, so the correct format specifier should be %u instead of %d. Signed-off-by: liujing Signed-off-by: Samuel Thibault Reviewed-by: Samuel Thibault Link: https://patch.msgid.link/20260531230804.254962-7-samuel.thibault@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/genmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accessibility/speakup/genmap.c b/drivers/accessibility/speakup/genmap.c index 8a5125db471e..a9e308bdd0be 100644 --- a/drivers/accessibility/speakup/genmap.c +++ b/drivers/accessibility/speakup/genmap.c @@ -152,7 +152,7 @@ main(int argc, char *argv[]) continue; printf("\n\t%d,", lc); for (i = 0; i < max_states; i++) - printf(" %d,", (unsigned int)kp[i]); + printf(" %u,", (unsigned int)kp[i]); } printf("\n\t0, %d\n", map_ver); From 5c3949ef38c86822dd18bef38a0e970052ebc27c Mon Sep 17 00:00:00 2001 From: Jagadeesh Yalapalli Date: Mon, 1 Jun 2026 01:07:56 +0200 Subject: [PATCH 193/513] speakup: Standardize character attribute types to u16 This change replaces non-portable `u_short` types with standardized `u16` throughout the speakup subsystem to ensure: 1. Consistent 16-bit width across all architectures. 2. Improved code portability and readability. 3. Elimination of platform-dependent type sizes. 4. Safe bitwise operations without sign-extension risks. Signed-off-by: Jagadeesh Yalapalli Signed-off-by: Samuel Thibault Reviewed-by: Samuel Thibault Link: https://patch.msgid.link/20260531230804.254962-8-samuel.thibault@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/keyhelp.c | 10 +-- drivers/accessibility/speakup/kobjects.c | 4 +- drivers/accessibility/speakup/main.c | 74 ++++++++++---------- drivers/accessibility/speakup/selection.c | 2 +- drivers/accessibility/speakup/speakup.h | 10 +-- drivers/accessibility/speakup/speakup_dtlk.h | 2 +- drivers/accessibility/speakup/spk_types.h | 2 +- drivers/accessibility/speakup/synth.c | 2 +- 8 files changed, 53 insertions(+), 53 deletions(-) diff --git a/drivers/accessibility/speakup/keyhelp.c b/drivers/accessibility/speakup/keyhelp.c index 822ceac83068..9c6e488adc2a 100644 --- a/drivers/accessibility/speakup/keyhelp.c +++ b/drivers/accessibility/speakup/keyhelp.c @@ -14,8 +14,8 @@ #define MAXFUNCS 130 #define MAXKEYS 256 static const int num_key_names = MSG_KEYNAMES_END - MSG_KEYNAMES_START + 1; -static u_short key_offsets[MAXFUNCS], key_data[MAXKEYS]; -static u_short masks[] = { 32, 16, 8, 4, 2, 1 }; +static u16 key_offsets[MAXFUNCS], key_data[MAXKEYS]; +static u16 masks[] = { 32, 16, 8, 4, 2, 1 }; static short letter_offsets[26] = { -1, -1, -1, -1, -1, -1, -1, -1, @@ -49,7 +49,7 @@ static int cur_item, nstates; static void build_key_data(void) { u_char *kp, counters[MAXFUNCS], ch, ch1; - u_short *p_key, key; + u16 *p_key, key; int i, offset = 1; nstates = (int)(state_tbl[-1]); @@ -129,12 +129,12 @@ static int help_init(void) return 0; } -int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key) +int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u16 key) { int i, n; char *name; u_char func, *kp; - u_short *p_keys, val; + u16 *p_keys, val; if (letter_offsets[0] == -1) help_init(); diff --git a/drivers/accessibility/speakup/kobjects.c b/drivers/accessibility/speakup/kobjects.c index 943ef71b1329..9ff7a4c680db 100644 --- a/drivers/accessibility/speakup/kobjects.c +++ b/drivers/accessibility/speakup/kobjects.c @@ -120,7 +120,7 @@ static ssize_t chars_chartab_store(struct kobject *kobj, ssize_t retval = count; unsigned long flags; unsigned long index = 0; - int charclass = 0; + u16 charclass = 0; int received = 0; int used = 0; int rejected = 0; @@ -461,7 +461,7 @@ static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr, struct st_var_header *p_header; struct punc_var_t *var; struct st_bits_data *pb; - short mask; + u16 mask; unsigned long flags; p_header = spk_var_header_by_name(attr->attr.name); diff --git a/drivers/accessibility/speakup/main.c b/drivers/accessibility/speakup/main.c index 78a77dd789a2..0962741a2ca2 100644 --- a/drivers/accessibility/speakup/main.c +++ b/drivers/accessibility/speakup/main.c @@ -63,7 +63,7 @@ int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10; int spk_no_intr, spk_spell_delay; int spk_key_echo, spk_say_word_ctl; int spk_say_ctrl, spk_bell_pos; -short spk_punc_mask; +u16 spk_punc_mask; int spk_punc_level, spk_reading_punc; int spk_cur_phonetic; char spk_str_caps_start[MAXVARLEN + 1] = "\0"; @@ -183,13 +183,13 @@ char *spk_default_chars[256] = { /* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut" }; -/* array of 256 u_short (one for each character) +/* array of 256 u16 (one for each character) * initialized to default_chartab and user selectable via * /sys/module/speakup/parameters/chartab */ -u_short spk_chartab[256]; +u16 spk_chartab[256]; -static u_short default_chartab[256] = { +static u16 default_chartab[256] = { B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */ B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */ B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */ @@ -267,10 +267,10 @@ static void speakup_date(struct vc_data *vc) spk_y = spk_cy = vc->state.y; spk_pos = spk_cp = vc->vc_pos; spk_old_attr = spk_attr; - spk_attr = get_attributes(vc, (u_short *)spk_pos); + spk_attr = get_attributes(vc, (u16 *)spk_pos); } -static void bleep(u_short val) +static void bleep(u16 val) { static const short vals[] = { 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659 @@ -346,14 +346,14 @@ static void speakup_cut(struct vc_data *vc) if (!mark_cut_flag) { mark_cut_flag = 1; - spk_xs = (u_short)spk_x; - spk_ys = (u_short)spk_y; + spk_xs = (u16)spk_x; + spk_ys = (u16)spk_y; spk_sel_cons = vc; synth_printf("%s\n", spk_msg_get(MSG_MARK)); return; } - spk_xe = (u_short)spk_x; - spk_ye = (u_short)spk_y; + spk_xe = (u16)spk_x; + spk_ye = (u16)spk_y; mark_cut_flag = 0; synth_printf("%s\n", spk_msg_get(MSG_CUT)); @@ -482,7 +482,7 @@ static void say_char(struct vc_data *vc) u16 ch; spk_old_attr = spk_attr; - ch = get_char(vc, (u_short *)spk_pos, &spk_attr); + ch = get_char(vc, (u16 *)spk_pos, &spk_attr); if (spk_attr != spk_old_attr) { if (spk_attrib_bleep & 1) bleep(spk_y); @@ -497,7 +497,7 @@ static void say_phonetic_char(struct vc_data *vc) u16 ch; spk_old_attr = spk_attr; - ch = get_char(vc, (u_short *)spk_pos, &spk_attr); + ch = get_char(vc, (u16 *)spk_pos, &spk_attr); if (ch <= 0x7f && isalpha(ch)) { ch &= 0x1f; synth_printf("%s\n", phonetic[--ch]); @@ -549,7 +549,7 @@ static u_long get_word(struct vc_data *vc) u_char temp; spk_old_attr = spk_attr; - ch = get_char(vc, (u_short *)tmp_pos, &temp); + ch = get_char(vc, (u16 *)tmp_pos, &temp); /* decided to take out the sayword if on a space (mis-information */ if (spk_say_word_ctl && ch == SPACE) { @@ -558,26 +558,26 @@ static u_long get_word(struct vc_data *vc) return 0; } else if (tmpx < vc->vc_cols - 2 && (ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) && - get_char(vc, (u_short *)tmp_pos + 1, &temp) > SPACE) { + get_char(vc, (u16 *)tmp_pos + 1, &temp) > SPACE) { tmp_pos += 2; tmpx++; } else { while (tmpx > 0) { - ch = get_char(vc, (u_short *)tmp_pos - 1, &temp); + ch = get_char(vc, (u16 *)tmp_pos - 1, &temp); if ((ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) && - get_char(vc, (u_short *)tmp_pos, &temp) > SPACE) + get_char(vc, (u16 *)tmp_pos, &temp) > SPACE) break; tmp_pos -= 2; tmpx--; } } - attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr); + attr_ch = get_char(vc, (u16 *)tmp_pos, &spk_attr); buf[cnt++] = attr_ch; while (tmpx < vc->vc_cols - 1 && cnt < ARRAY_SIZE(buf) - 1) { tmp_pos += 2; tmpx++; - ch = get_char(vc, (u_short *)tmp_pos, &temp); + ch = get_char(vc, (u16 *)tmp_pos, &temp); if (ch == SPACE || ch == 0 || (buf[cnt - 1] < 0x100 && IS_WDLM(buf[cnt - 1]) && ch > SPACE)) @@ -591,7 +591,7 @@ static u_long get_word(struct vc_data *vc) static void say_word(struct vc_data *vc) { u_long cnt = get_word(vc); - u_short saved_punc_mask = spk_punc_mask; + u16 saved_punc_mask = spk_punc_mask; if (cnt == 0) return; @@ -606,7 +606,7 @@ static void say_prev_word(struct vc_data *vc) u_char temp; u16 ch; enum edge edge_said = edge_none; - u_short last_state = 0, state = 0; + u16 last_state = 0, state = 0; spk_parked |= 0x01; @@ -635,7 +635,7 @@ static void say_prev_word(struct vc_data *vc) spk_x--; } spk_pos -= 2; - ch = get_char(vc, (u_short *)spk_pos, &temp); + ch = get_char(vc, (u16 *)spk_pos, &temp); if (ch == SPACE || ch == 0) state = 0; else if (ch < 0x100 && IS_WDLM(ch)) @@ -661,7 +661,7 @@ static void say_next_word(struct vc_data *vc) u_char temp; u16 ch; enum edge edge_said = edge_none; - u_short last_state = 2, state = 0; + u16 last_state = 2, state = 0; spk_parked |= 0x01; if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) { @@ -669,7 +669,7 @@ static void say_next_word(struct vc_data *vc) return; } while (1) { - ch = get_char(vc, (u_short *)spk_pos, &temp); + ch = get_char(vc, (u16 *)spk_pos, &temp); if (ch == SPACE || ch == 0) state = 0; else if (ch < 0x100 && IS_WDLM(ch)) @@ -755,9 +755,9 @@ static int get_line(struct vc_data *vc) u_char tmp2; spk_old_attr = spk_attr; - spk_attr = get_attributes(vc, (u_short *)spk_pos); + spk_attr = get_attributes(vc, (u16 *)spk_pos); for (i = 0; i < vc->vc_cols; i++) { - buf[i] = get_char(vc, (u_short *)tmp, &tmp2); + buf[i] = get_char(vc, (u16 *)tmp, &tmp2); tmp += 2; } for (--i; i >= 0; i--) @@ -770,7 +770,7 @@ static void say_line(struct vc_data *vc) { int i = get_line(vc); u16 *cp; - u_short saved_punc_mask = spk_punc_mask; + u16 saved_punc_mask = spk_punc_mask; if (i == 0) { synth_printf("%s\n", spk_msg_get(MSG_BLANK)); @@ -817,12 +817,12 @@ static int say_from_to(struct vc_data *vc, u_long from, u_long to, { int i = 0; u_char tmp; - u_short saved_punc_mask = spk_punc_mask; + u16 saved_punc_mask = spk_punc_mask; spk_old_attr = spk_attr; - spk_attr = get_attributes(vc, (u_short *)from); + spk_attr = get_attributes(vc, (u16 *)from); while (from < to) { - buf[i++] = get_char(vc, (u_short *)from, &tmp); + buf[i++] = get_char(vc, (u16 *)from, &tmp); from += 2; if (i >= vc->vc_size_row) break; @@ -895,10 +895,10 @@ static int get_sentence_buf(struct vc_data *vc, int read_punc) sentmarks[bn][0] = &sentbuf[bn][0]; i = 0; spk_old_attr = spk_attr; - spk_attr = get_attributes(vc, (u_short *)start); + spk_attr = get_attributes(vc, (u16 *)start); while (start < end) { - sentbuf[bn][i] = get_char(vc, (u_short *)start, &tmp); + sentbuf[bn][i] = get_char(vc, (u16 *)start, &tmp); if (i > 0) { if (sentbuf[bn][i] == SPACE && sentbuf[bn][i - 1] == '.' && @@ -1047,7 +1047,7 @@ static void say_position(struct vc_data *vc) static void say_char_num(struct vc_data *vc) { u_char tmp; - u16 ch = get_char(vc, (u_short *)spk_pos, &tmp); + u16 ch = get_char(vc, (u16 *)spk_pos, &tmp); synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch); } @@ -1080,7 +1080,7 @@ static void spkup_write(const u16 *in_buf, int count) { static int rep_count; static u16 ch = '\0', old_ch = '\0'; - static u_short char_type, last_type; + static u16 char_type, last_type; int in_count = count; spk_keydown = 0; @@ -1325,9 +1325,9 @@ void spk_reset_default_chartab(void) static const struct st_bits_data *pb_edit; -static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key) +static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u16 key) { - short mask = pb_edit->mask, ch_type = spk_chartab[ch]; + u16 mask = pb_edit->mask, ch_type = spk_chartab[ch]; if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE) return -1; @@ -1947,7 +1947,7 @@ static void speakup_bits(struct vc_data *vc) spk_special_handler = edit_bits; } -static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key) +static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u16 key) { static u_char goto_buf[8]; static int num; @@ -2105,7 +2105,7 @@ static void do_spkup(struct vc_data *vc, u_char value) static const char *pad_chars = "0123456789+-*/\015,.?()"; static int -speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym, +speakup_key(struct vc_data *vc, int shift_state, int keycode, u16 keysym, int up_flag) { unsigned long flags; diff --git a/drivers/accessibility/speakup/selection.c b/drivers/accessibility/speakup/selection.c index 7df7afad5ab4..1713ce4e0ba5 100644 --- a/drivers/accessibility/speakup/selection.c +++ b/drivers/accessibility/speakup/selection.c @@ -13,7 +13,7 @@ #include "speakup.h" -unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ +u16 spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ struct vc_data *spk_sel_cons; struct speakup_selection_work { diff --git a/drivers/accessibility/speakup/speakup.h b/drivers/accessibility/speakup/speakup.h index 54f1226ea061..984a729fd82d 100644 --- a/drivers/accessibility/speakup/speakup.h +++ b/drivers/accessibility/speakup/speakup.h @@ -62,7 +62,7 @@ int spk_set_num_var(int val, struct st_var_header *var, int how); int spk_set_string_var(const char *page, struct st_var_header *var, int len); int spk_set_mask_bits(const char *input, const int which, const int how); extern special_func spk_special_handler; -int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key); +int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u16 key); int synth_init(char *name); void synth_release(void); @@ -82,7 +82,7 @@ void synth_writeu(const char *buf, size_t count); int synth_supports_indexing(void); extern struct vc_data *spk_sel_cons; -extern unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ +extern u16 spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ extern wait_queue_head_t speakup_event; extern struct kobject *speakup_kobj; @@ -95,20 +95,20 @@ extern struct st_spk_t *speakup_console[]; extern struct spk_synth *synth; extern char spk_pitch_buff[]; extern u_char *spk_our_keys[]; -extern short spk_punc_masks[]; +extern u16 spk_punc_masks[]; extern char spk_str_caps_start[], spk_str_caps_stop[], spk_str_pause[]; extern bool spk_paused; extern const struct st_bits_data spk_punc_info[]; extern u_char spk_key_buf[600]; extern char *spk_characters[]; extern char *spk_default_chars[]; -extern u_short spk_chartab[]; +extern u16 spk_chartab[]; extern int spk_no_intr, spk_say_ctrl, spk_say_word_ctl, spk_punc_level; extern int spk_reading_punc, spk_attrib_bleep, spk_bleeps; extern int spk_bleep_time, spk_bell_pos; extern int spk_spell_delay, spk_key_echo; extern int spk_cur_phonetic; -extern short spk_punc_mask; +extern u16 spk_punc_mask; extern short spk_pitch_shift, synth_flags; extern bool spk_quiet_boot; extern char *synth_name; diff --git a/drivers/accessibility/speakup/speakup_dtlk.h b/drivers/accessibility/speakup/speakup_dtlk.h index 101848edec2e..9354e3382bba 100644 --- a/drivers/accessibility/speakup/speakup_dtlk.h +++ b/drivers/accessibility/speakup/speakup_dtlk.h @@ -41,7 +41,7 @@ /* data returned by Interrogate command */ struct synth_settings { - u_short serial_number; /* 0-7Fh:0-7Fh */ + u16 serial_number; /* 0-7Fh:0-7Fh */ u_char rom_version[24]; /* null terminated string */ u_char mode; /* 0=Character; 1=Phoneme; 2=Text */ u_char punc_level; /* nB; 0-7 */ diff --git a/drivers/accessibility/speakup/spk_types.h b/drivers/accessibility/speakup/spk_types.h index 08011518a28a..a5762330e249 100644 --- a/drivers/accessibility/speakup/spk_types.h +++ b/drivers/accessibility/speakup/spk_types.h @@ -53,7 +53,7 @@ enum var_id_t { }; typedef int (*special_func)(struct vc_data *vc, u_char type, u_char ch, - u_short key); + u16 key); #define COLOR_BUFFER_SIZE 160 diff --git a/drivers/accessibility/speakup/synth.c b/drivers/accessibility/speakup/synth.c index d8addbf3ad0d..d1ec1a7eb160 100644 --- a/drivers/accessibility/speakup/synth.c +++ b/drivers/accessibility/speakup/synth.c @@ -574,4 +574,4 @@ struct spk_synth *synth_current(void) } EXPORT_SYMBOL_GPL(synth_current); -short spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM }; +u16 spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM }; From 768ce60dc8fdea4ce37267d6a6d56193e9e7dc03 Mon Sep 17 00:00:00 2001 From: Xichao Zhao Date: Mon, 1 Jun 2026 01:07:57 +0200 Subject: [PATCH 194/513] accessibility: Use str_plural() to simplify the code Use the string choice helper function str_plural() to simplify the code. Signed-off-by: Xichao Zhao Signed-off-by: Samuel Thibault Reviewed-by: Samuel Thibault Link: https://patch.msgid.link/20260531230804.254962-9-samuel.thibault@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/kobjects.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/accessibility/speakup/kobjects.c b/drivers/accessibility/speakup/kobjects.c index 9ff7a4c680db..a73b478e06de 100644 --- a/drivers/accessibility/speakup/kobjects.c +++ b/drivers/accessibility/speakup/kobjects.c @@ -98,7 +98,7 @@ static void report_char_chartab_status(int reset, int received, int used, if (rejected) snprintf(buf + (len - 1), sizeof(buf) - (len - 1), " with %d reject%s\n", - rejected, rejected > 1 ? "s" : ""); + rejected, str_plural(rejected)); pr_info("%s", buf); } } @@ -740,7 +740,7 @@ static void report_msg_status(int reset, int received, int used, if (rejected) snprintf(buf + (len - 1), sizeof(buf) - (len - 1), " with %d reject%s\n", - rejected, rejected > 1 ? "s" : ""); + rejected, str_plural(rejected)); pr_info("%s", buf); } } From 6a19ad4d68c95185308cd9e5d169b10a2cf236c8 Mon Sep 17 00:00:00 2001 From: Pavel Zhigulin Date: Mon, 1 Jun 2026 01:07:58 +0200 Subject: [PATCH 195/513] speakup: keyhelp: guard letter_offsets possible out-of-range indexing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit help_init() builds letter_offsets[] by using the first byte of each function name as an index via `(start & 31) - 1`. If function_names are overridden from sysfs (root) with a name starting outside [a–z], the index underflows or exceeds the array, leading to OOB write. Function names can be overridden with the following commands as root: modprobe speakup_soft echo "0 _bad" > /sys/accessibility/speakup/i18n/function_names # then press Insert+2 on /dev/tty This fix checks the first letter in help_init(), and if it is not in the [a–z] range the function returns an error to the caller. Eventually this error is propagated to drivers/accessibility/speakup/main.c:2217, which causes a bleep sound. Fixes: c6e3fd22cd53 ("Staging: add speakup to the staging directory") Signed-off-by: Pavel Zhigulin Signed-off-by: Samuel Thibault Link: https://patch.msgid.link/20260531230804.254962-10-samuel.thibault@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/keyhelp.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/accessibility/speakup/keyhelp.c b/drivers/accessibility/speakup/keyhelp.c index 9c6e488adc2a..0940f430ac0f 100644 --- a/drivers/accessibility/speakup/keyhelp.c +++ b/drivers/accessibility/speakup/keyhelp.c @@ -8,6 +8,7 @@ */ #include +#include #include "spk_priv.h" #include "speakup.h" @@ -111,7 +112,7 @@ static void say_key(int key) spk_msg_get(MSG_KEYNAMES_START + (key - 1))); } -static int help_init(void) +static void help_init(void) { char start = SPACE; int i; @@ -120,13 +121,19 @@ static int help_init(void) state_tbl = spk_our_keys[0] + SHIFT_TBL_SIZE + 2; for (i = 0; i < num_funcs; i++) { char *cur_funcname = spk_msg_get(MSG_FUNCNAMES_START + i); + char first_letter; - if (start == *cur_funcname) + first_letter = tolower(*cur_funcname); + + /* Accept only 'a'..'z' to index letter_offsets[] safely */ + if (first_letter < 'a' || first_letter > 'z') continue; - start = *cur_funcname; + + if (start == first_letter) + continue; + start = first_letter; letter_offsets[(start & 31) - 1] = i; } - return 0; } int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u16 key) @@ -144,7 +151,7 @@ int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u16 key) synth_printf("%s\n", spk_msg_get(MSG_LEAVING_HELP)); return 1; } - ch |= 32; /* lower case */ + ch = tolower(ch); if (ch < 'a' || ch > 'z') return -1; if (letter_offsets[ch - 'a'] == -1) { From 7955022a98cef5dd475fe1b814650a79c5a3113f Mon Sep 17 00:00:00 2001 From: Bo Liu Date: Mon, 1 Jun 2026 01:07:59 +0200 Subject: [PATCH 196/513] Accessibility: speakup_soft: Fix double word in comments Remove the repeated word "the" in comments. Signed-off-by: Bo Liu Signed-off-by: Samuel Thibault Reviewed-by: Samuel Thibault Link: https://patch.msgid.link/20260531230804.254962-11-samuel.thibault@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/speakup_soft.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accessibility/speakup/speakup_soft.c b/drivers/accessibility/speakup/speakup_soft.c index 6d446824677b..6549bfb96e7f 100644 --- a/drivers/accessibility/speakup/speakup_soft.c +++ b/drivers/accessibility/speakup/speakup_soft.c @@ -446,7 +446,7 @@ static int softsynth_adjust(struct spk_synth *synth, struct st_var_header *var) if (var->var_id != PUNC_LEVEL) return 0; - /* We want to set the the speech synthesis punctuation level + /* We want to set the speech synthesis punctuation level * accordingly, so it properly tunes speaking A_PUNC characters */ var_data = var->data; if (!var_data) From 255418300df2c020d1a8848472b3481137a49342 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Mon, 1 Jun 2026 01:08:00 +0200 Subject: [PATCH 197/513] speakup: Fix spelling of "re-enable" Detected using codespell. Signed-off-by: Bastien Nocera Signed-off-by: Samuel Thibault Link: https://patch.msgid.link/20260531230804.254962-12-samuel.thibault@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/fakekey.c | 4 ++-- drivers/accessibility/speakup/synth.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/accessibility/speakup/fakekey.c b/drivers/accessibility/speakup/fakekey.c index 868c47b2a59b..9c0b2403876a 100644 --- a/drivers/accessibility/speakup/fakekey.c +++ b/drivers/accessibility/speakup/fakekey.c @@ -71,9 +71,9 @@ void speakup_fake_down_arrow(void) input_sync(virt_keyboard); __this_cpu_write(reporting_keystroke, false); - /* reenable preemption */ + /* re-enable preemption */ preempt_enable(); - /* reenable keyboard interrupts */ + /* re-enable keyboard interrupts */ local_irq_restore(flags); } diff --git a/drivers/accessibility/speakup/synth.c b/drivers/accessibility/speakup/synth.c index d1ec1a7eb160..11cba1fd8715 100644 --- a/drivers/accessibility/speakup/synth.c +++ b/drivers/accessibility/speakup/synth.c @@ -163,7 +163,7 @@ int spk_synth_is_alive_restart(struct spk_synth *synth) /* restart */ synth->alive = 1; synth_printf("%s", synth->init); - return 2; /* reenabled */ + return 2; /* re-enabled */ } pr_warn("%s: can't restart synth\n", synth->long_name); return 0; From 17035422e1f24d65c0b6b7d153e4f3e6fc59f410 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Mon, 1 Jun 2026 01:08:01 +0200 Subject: [PATCH 198/513] speakup: Fix incorrect "index" plural It's indexes or indices. Given that the constant is called "STAT_index_valid", "indexes" was the preferred plural. Signed-off-by: Bastien Nocera Signed-off-by: Samuel Thibault Link: https://patch.msgid.link/20260531230804.254962-13-samuel.thibault@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/speakup_decpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accessibility/speakup/speakup_decpc.c b/drivers/accessibility/speakup/speakup_decpc.c index 083ca9265805..1bf36d1e5477 100644 --- a/drivers/accessibility/speakup/speakup_decpc.c +++ b/drivers/accessibility/speakup/speakup_decpc.c @@ -41,7 +41,7 @@ #define STAT_new_index 0x0040 /* new last index ready */ #define STAT_new_status 0x0080 /* new status posted */ #define STAT_dma_state 0x0100 /* dma state toggle */ -#define STAT_index_valid 0x0200 /* indexs are valid */ +#define STAT_index_valid 0x0200 /* indexes are valid */ #define STAT_flushing 0x0400 /* flush in progress */ #define STAT_self_test 0x0800 /* module in self test */ #define MODE_ready 0xc000 /* module ready for next phase */ From 6a4c7d85f02df4b1def6ca3aa74668fa50848ed4 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Mon, 1 Jun 2026 01:08:02 +0200 Subject: [PATCH 199/513] speakup: Fix typo in a speakup message s/read windo/read window/ Signed-off-by: Colin Ian King Signed-off-by: Bastien Nocera Signed-off-by: Samuel Thibault Reviewed-by: Samuel Thibault Link: https://patch.msgid.link/20260531230804.254962-14-samuel.thibault@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/i18n.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accessibility/speakup/i18n.c b/drivers/accessibility/speakup/i18n.c index d62079b1661f..554bf81f2c1c 100644 --- a/drivers/accessibility/speakup/i18n.c +++ b/drivers/accessibility/speakup/i18n.c @@ -31,7 +31,7 @@ static char *speakup_default_msgs[MSG_LAST_INDEX] = { [MSG_CURSORING_OFF] = "cursoring off", [MSG_CURSORING_ON] = "cursoring on", [MSG_HIGHLIGHT_TRACKING] = "highlight tracking", - [MSG_READ_WINDOW] = "read windo", + [MSG_READ_WINDOW] = "read window", [MSG_READ_ALL] = "read all", [MSG_EDIT_DONE] = "edit done", [MSG_WINDOW_ALREADY_SET] = "window already set, clear then reset", From a21164f89e09e84f50f8954ea4b0f8cc55af2d0b Mon Sep 17 00:00:00 2001 From: Francisco Maestre Date: Mon, 1 Jun 2026 01:08:03 +0200 Subject: [PATCH 200/513] speakup: speakup_soft: fix comment style and repeated word Fix comment style issues in speakup_soft.c: - Move the closing '*/' of the block comment to its own line, as required by the kernel coding style Signed-off-by: Francisco Maestre Signed-off-by: Samuel Thibault Link: https://patch.msgid.link/20260531230804.254962-15-samuel.thibault@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/speakup_soft.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/accessibility/speakup/speakup_soft.c b/drivers/accessibility/speakup/speakup_soft.c index 6549bfb96e7f..57d83b82f1d1 100644 --- a/drivers/accessibility/speakup/speakup_soft.c +++ b/drivers/accessibility/speakup/speakup_soft.c @@ -447,7 +447,8 @@ static int softsynth_adjust(struct spk_synth *synth, struct st_var_header *var) return 0; /* We want to set the speech synthesis punctuation level - * accordingly, so it properly tunes speaking A_PUNC characters */ + * accordingly, so it properly tunes speaking A_PUNC characters + */ var_data = var->data; if (!var_data) return 0; From a76acbaec9b8fd74413646984d2e3626d0543e39 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 1 Jun 2026 01:08:04 +0200 Subject: [PATCH 201/513] accessibility: speakup: unregister tty ldisc on later init failures The ldisc registration is intentionally non-fatal, since some synth drivers do not use tty/ldisc. However, once speakup_init() continues past the registration point and later fails, the init unwind path should mirror speakup_exit() and call spk_ttyio_unregister_ldisc(). Add the missing unregister call to the error path after synth_release(), matching the normal module exit cleanup order. Signed-off-by: Haoxiang Li Signed-off-by: Samuel Thibault Fixes: e23a9b439ce9 ("staging: speakup: safely register and unregister ldisc") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260531230804.254962-16-samuel.thibault@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/accessibility/speakup/main.c b/drivers/accessibility/speakup/main.c index 0962741a2ca2..e9b7c2761f6f 100644 --- a/drivers/accessibility/speakup/main.c +++ b/drivers/accessibility/speakup/main.c @@ -2444,6 +2444,7 @@ static int __init speakup_init(void) mutex_lock(&spk_mutex); synth_release(); mutex_unlock(&spk_mutex); + spk_ttyio_unregister_ldisc(); speakup_kobj_exit(); error_kobjects: From 71375da8b473445f51f0668d5622922827402357 Mon Sep 17 00:00:00 2001 From: Shashwat Agrawal Date: Mon, 29 Jun 2026 19:24:04 +0530 Subject: [PATCH 202/513] comedi: ni_pcimio: set PCI-6220 dio_speed and ai_fifo_depth from NI specs The PCI-6220 board entry was missing .dio_speed, unlike the PXI-6220 and the other 622x boards in the table. Set it to 1000 ns to match those entries and the 1 MHz maximum DI/DO sample clock on Port 0 in the NI PCI/PXI-6220 specifications. Also update .ai_fifo_depth for PCI-6220 and PXI-6220 from 512 to 4095 samples, matching the documented AI input FIFO size and the rest of the 622x entries. Link: https://www.ni.com/docs/en-US/bundle/pci-pxi-6220-specs/page/specs.html Signed-off-by: Shashwat Agrawal Reviewed-by: Ian Abbott Link: https://patch.msgid.link/20260629135404.19835-1-shashwatagrawal473@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/ni_pcimio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/comedi/drivers/ni_pcimio.c b/drivers/comedi/drivers/ni_pcimio.c index 4be9ca4f4828..299e2a0e9474 100644 --- a/drivers/comedi/drivers/ni_pcimio.c +++ b/drivers/comedi/drivers/ni_pcimio.c @@ -685,17 +685,18 @@ static const struct ni_board_struct ni_boards[] = { .name = "pci-6220", .n_adchan = 16, .ai_maxdata = 0xffff, - .ai_fifo_depth = 512, /* FIXME: guess */ + .ai_fifo_depth = 4095, .gainlkup = ai_gain_622x, .ai_speed = 4000, .reg_type = ni_reg_622x, .caldac = { caldac_none }, + .dio_speed = 1000, }, [BOARD_PXI6220] = { .name = "pxi-6220", .n_adchan = 16, .ai_maxdata = 0xffff, - .ai_fifo_depth = 512, /* FIXME: guess */ + .ai_fifo_depth = 4095, .gainlkup = ai_gain_622x, .ai_speed = 4000, .reg_type = ni_reg_622x, From 1ca44751915134f19e8627768159781e75b5da50 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 18 Jun 2026 11:09:08 +0100 Subject: [PATCH 203/513] comedi: aio_iiro_16: Add sanity check to interrupt handler The driver requests an interrupt handler for the device, after setting device registers to disable interrupt generation. The interrupt handler should not be called prematurely unless the user-configured I/O port base address and/or IRQ number are incorrect or the hardware is bad. For safety, check the dev->attached flag in the interrupt handler to ensure the device has been fully set up, avoiding a possible null pointer dereference of dev->read_subdev. Reported-by: Jaeyoung Chung Link: https://lore.kernel.org/lkml/20260610115912.780131-1-jjy600901@snu.ac.kr/ Reported-by: Sangyun Kim Reported-by: Kyungwook Boo Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260618102949.26607-2-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/aio_iiro_16.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/comedi/drivers/aio_iiro_16.c b/drivers/comedi/drivers/aio_iiro_16.c index d5d18fa2c638..52385b14a9a4 100644 --- a/drivers/comedi/drivers/aio_iiro_16.c +++ b/drivers/comedi/drivers/aio_iiro_16.c @@ -59,6 +59,9 @@ static irqreturn_t aio_iiro_16_cos(int irq, void *d) unsigned int status; unsigned int val; + if (!dev->attached) + return IRQ_NONE; + status = inb(dev->iobase + AIO_IIRO_16_STATUS); if (!(status & AIO_IIRO_16_STATUS_IRQE)) return IRQ_NONE; From e90d0550c5b3056c8821f5cd714f80902de0fca6 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 18 Jun 2026 11:09:09 +0100 Subject: [PATCH 204/513] comedi: das6402: Add sanity check to interrupt handler The driver requests an interrupt handler for the device, after setting device registers to disable interrupt generation. The interrupt handler should not be called prematurely unless the user-configured I/O port base address and/or IRQ number are incorrect or the hardware is bad. For safety, check the dev->attached flag in the interrupt handler to ensure the device has been fully set up, avoiding a possible null pointer dereference of dev->read_subdev. Reported-by: Jaeyoung Chung Link: https://lore.kernel.org/lkml/20260610115912.780131-1-jjy600901@snu.ac.kr/ Reported-by: Sangyun Kim Reported-by: Kyungwook Boo Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260618102949.26607-3-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/das6402.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/comedi/drivers/das6402.c b/drivers/comedi/drivers/das6402.c index 516a5d5a2840..384e9ba8d5df 100644 --- a/drivers/comedi/drivers/das6402.c +++ b/drivers/comedi/drivers/das6402.c @@ -173,10 +173,16 @@ static irqreturn_t das6402_interrupt(int irq, void *d) { struct comedi_device *dev = d; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &async->cmd; + struct comedi_async *async; + struct comedi_cmd *cmd; unsigned int status; + if (!dev->attached) + return IRQ_NONE; + + async = s->async; + cmd = &async->cmd; + status = inb(dev->iobase + DAS6402_STATUS_REG); if ((status & DAS6402_STATUS_INT) == 0) return IRQ_NONE; From 8dda63825752ab19fcbc2a19ecab91d128112415 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 18 Jun 2026 11:09:10 +0100 Subject: [PATCH 205/513] comedi: dt2811: Fix sanity check in interrupt handler The driver requests an interrupt handler for the device, after setting device registers to disable interrupt generation. The interrupt handler should not be called prematurely unless the user-configured I/O port base address and/or IRQ number are incorrect or the hardware is bad. For safety, the interrupt handler checks the dev->attached flag to ensure the device is fully set up, but it currently does that after dereferencing dev->read_subdev, which may be NULL if dev->attached is false. Move the check to avoid the possible null pointer dereference. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260618102949.26607-4-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/dt2811.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/comedi/drivers/dt2811.c b/drivers/comedi/drivers/dt2811.c index bcc4b5ef48e8..0438b8c90e44 100644 --- a/drivers/comedi/drivers/dt2811.c +++ b/drivers/comedi/drivers/dt2811.c @@ -193,13 +193,16 @@ static irqreturn_t dt2811_interrupt(int irq, void *d) { struct comedi_device *dev = d; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &async->cmd; + struct comedi_async *async; + struct comedi_cmd *cmd; unsigned int status; if (!dev->attached) return IRQ_NONE; + async = s->async; + cmd = &async->cmd; + status = inb(dev->iobase + DT2811_ADCSR_REG); if (status & DT2811_ADCSR_ADERROR) { From e7356cdc7077a4f30fc5232a18e9fc6a4d404581 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 18 Jun 2026 11:09:11 +0100 Subject: [PATCH 206/513] comedi: ni_at_a2150: Fix sanity check in interrupt handler The driver requests an interrupt handler for the device before it is fully set up. For safety, the interrupt handler checks the dev->attached flag to ensure the device is fully set up, but it currently does that after dereferencing various pointers which may be NULL if dev->attached is false. Move the check to avoid the possible null pointer dereferences. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260618102949.26607-5-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/ni_at_a2150.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/comedi/drivers/ni_at_a2150.c b/drivers/comedi/drivers/ni_at_a2150.c index 44221c928e32..86629b495fed 100644 --- a/drivers/comedi/drivers/ni_at_a2150.c +++ b/drivers/comedi/drivers/ni_at_a2150.c @@ -132,11 +132,11 @@ static irqreturn_t a2150_interrupt(int irq, void *d) struct comedi_device *dev = d; struct a2150_private *devpriv = dev->private; struct comedi_isadma *dma = devpriv->dma; - struct comedi_isadma_desc *desc = &dma->desc[0]; + struct comedi_isadma_desc *desc; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &async->cmd; - unsigned short *buf = desc->virt_addr; + struct comedi_async *async; + struct comedi_cmd *cmd; + unsigned short *buf; unsigned int max_points, num_points, residue, leftover; unsigned short dpnt; int status; @@ -145,6 +145,11 @@ static irqreturn_t a2150_interrupt(int irq, void *d) if (!dev->attached) return IRQ_HANDLED; + desc = &dma->desc[0]; + async = s->async; + cmd = &async->cmd; + buf = desc->virt_addr; + status = inw(dev->iobase + STATUS_REG); if ((status & INTR_BIT) == 0) return IRQ_NONE; From 13f4796223489a5349b466ce54bb868f46c9fefd Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 18 Jun 2026 11:09:12 +0100 Subject: [PATCH 207/513] comedi: ni_atmio16d: Add sanity check to interrupt handler The driver requests an interrupt handler for the device, after setting device registers to disable interrupt generation. The interrupt handler should not be called prematurely unless the user-configured I/O port base address and/or IRQ number are incorrect or the hardware is bad. For safety, check the dev->attached flag in the interrupt handler to ensure the device has been fully set up, avoiding a possible null pointer dereference of dev->read_subdev. Reported-by: Jaeyoung Chung Link: https://lore.kernel.org/lkml/20260610115912.780131-1-jjy600901@snu.ac.kr/ Reported-by: Sangyun Kim Reported-by: Kyungwook Boo Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260618102949.26607-6-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/ni_atmio16d.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/comedi/drivers/ni_atmio16d.c b/drivers/comedi/drivers/ni_atmio16d.c index b2772d909896..6765cdc276ca 100644 --- a/drivers/comedi/drivers/ni_atmio16d.c +++ b/drivers/comedi/drivers/ni_atmio16d.c @@ -223,6 +223,9 @@ static irqreturn_t atmio16d_interrupt(int irq, void *d) struct comedi_subdevice *s = dev->read_subdev; unsigned short val; + if (!dev->attached) + return IRQ_NONE; + val = inw(dev->iobase + AD_FIFO_REG); comedi_buf_write_samples(s, &val, 1); comedi_handle_events(dev, s); From f876cbfe3e066bc9e3f3210907bd534be82946c8 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 18 Jun 2026 11:09:13 +0100 Subject: [PATCH 208/513] comedi: pcm711: Fix sanity check in interrupt handler The driver requests an interrupt handler for the device before it is fully set up. For safety, the interrupt handler checks the dev->attached flag to ensure the device is fully set up, but it currently does that after dereferencing the dev->read_dev pointer which may be NULL if dev->attached is false. Move the check to avoid the possible null pointer dereference. Reported-by: Jaeyoung Chung Link: https://lore.kernel.org/lkml/20260610115912.780131-1-jjy600901@snu.ac.kr/ Reported-by: Sangyun Kim Reported-by: Kyungwook Boo Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260618102949.26607-7-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/pcl711.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/pcl711.c b/drivers/comedi/drivers/pcl711.c index 5d2c4b2aa3bb..8ad5789966f0 100644 --- a/drivers/comedi/drivers/pcl711.c +++ b/drivers/comedi/drivers/pcl711.c @@ -184,7 +184,7 @@ static irqreturn_t pcl711_interrupt(int irq, void *d) { struct comedi_device *dev = d; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_cmd *cmd = &s->async->cmd; + struct comedi_cmd *cmd; unsigned short data; if (!dev->attached) { @@ -192,6 +192,7 @@ static irqreturn_t pcl711_interrupt(int irq, void *d) return IRQ_HANDLED; } + cmd = &s->async->cmd; data = pcl711_ai_get_sample(dev, s); outb(PCL711_INT_STAT_CLR, dev->iobase + PCL711_INT_STAT_REG); From 4fdac5090ce3616283216d9a1d2b9664e8e20747 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 18 Jun 2026 11:09:14 +0100 Subject: [PATCH 209/513] comedi: pcm816: Fix sanity check in interrupt handler The driver requests an interrupt handler for the device before it is fully set up. For safety, the interrupt handler checks the dev->attached flag to ensure the device is fully set up, but it currently does that after dereferencing the devpriv->dma pointer which may be NULL if dev->attached is false. Move the dereference of the devpriv->dma pointer after dev->attached has been checked to avoid the possible null pointer dereference. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260618102949.26607-8-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 1fcb2f798c7a..b0d30cb1cfe9 100644 --- a/drivers/comedi/drivers/pcl816.c +++ b/drivers/comedi/drivers/pcl816.c @@ -242,7 +242,7 @@ static irqreturn_t pcl816_interrupt(int irq, void *d) struct comedi_subdevice *s = dev->read_subdev; struct pcl816_private *devpriv = dev->private; struct comedi_isadma *dma = devpriv->dma; - struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma]; + struct comedi_isadma_desc *desc; unsigned int nsamples; unsigned int bufptr; @@ -257,6 +257,7 @@ static irqreturn_t pcl816_interrupt(int irq, void *d) return IRQ_HANDLED; } + desc = &dma->desc[dma->cur_dma]; nsamples = comedi_bytes_to_samples(s, desc->size) - devpriv->ai_poll_ptr; bufptr = devpriv->ai_poll_ptr; From 956e8478261a575d1e87490f2e5db1788b8c209d Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 18 Jun 2026 11:09:15 +0100 Subject: [PATCH 210/513] comedi: pcm818: Fix sanity check in interrupt handler The driver requests an interrupt handler for the device before it is fully set up. For safety, the interrupt handler checks the dev->attached flag to ensure the device is fully set up, but it currently does that after dereferencing the dev->read_dev pointer which may be NULL if dev->attached is false. Move the check to avoid the possible null pointer dereference. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260618102949.26607-9-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/pcl818.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/pcl818.c b/drivers/comedi/drivers/pcl818.c index aa775a024fc7..89f32c493caa 100644 --- a/drivers/comedi/drivers/pcl818.c +++ b/drivers/comedi/drivers/pcl818.c @@ -534,13 +534,14 @@ static irqreturn_t pcl818_interrupt(int irq, void *d) struct comedi_device *dev = d; struct pcl818_private *devpriv = dev->private; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_cmd *cmd = &s->async->cmd; + struct comedi_cmd *cmd; if (!dev->attached || !devpriv->ai_cmd_running) { pcl818_ai_clear_eoc(dev); return IRQ_HANDLED; } + cmd = &s->async->cmd; if (devpriv->ai_cmd_canceled) { /* * The cleanup from ai_cancel() has been delayed From ea72e2fc9026f375314421ddfa4dbdc49432a899 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 18 Jun 2026 11:09:16 +0100 Subject: [PATCH 211/513] comedi: pcmmio: Add sanity check to interrupt handler The driver requests an interrupt handler for the device, after setting device registers to disable interrupt generation. The interrupt handler should not be called prematurely unless the user-configured I/O port base address and/or IRQ number are incorrect or the hardware is bad. For safety, check the dev->attached flag in the interrupt handler to ensure the device has been fully set up, avoiding a possible null pointer dereference of dev->read_subdev. Reported-by: Jaeyoung Chung Link: https://lore.kernel.org/lkml/20260610115912.780131-1-jjy600901@snu.ac.kr/ Reported-by: Sangyun Kim Reported-by: Kyungwook Boo Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260618102949.26607-10-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/pcmmio.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/comedi/drivers/pcmmio.c b/drivers/comedi/drivers/pcmmio.c index d38202c8a12b..f42b7343b4e4 100644 --- a/drivers/comedi/drivers/pcmmio.c +++ b/drivers/comedi/drivers/pcmmio.c @@ -362,6 +362,9 @@ static irqreturn_t interrupt_pcmmio(int irq, void *d) unsigned int triggered; unsigned char int_pend; + if (!dev->attached) + return IRQ_NONE; + /* are there any interrupts pending */ int_pend = inb(dev->iobase + PCMMIO_INT_PENDING_REG) & 0x07; if (!int_pend) From 6f6f6644e011fa910db744e9978127a443b94001 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 18 Jun 2026 11:09:17 +0100 Subject: [PATCH 212/513] comedi: pcmuio: Add sanity check to interrupt handler The driver requests an interrupt handler for the device, after setting device registers to disable interrupt generation. The interrupt handler should not be called prematurely unless the user-configured I/O port base address and/or IRQ number are incorrect or the hardware is bad. For safety, check the dev->attached flag in the interrupt handler pcmuio_interrupt() to ensure the device has been fully set up, avoiding a possible null pointer dereference of dev->subdevices by pcmuio_handle_asic_interrupt(). Also make use of the IRQ_HANDLED(x) macro for the normal return path of the interrupt handler. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260618102949.26607-11-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/pcmuio.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/pcmuio.c b/drivers/comedi/drivers/pcmuio.c index 0995911a3ea3..d9995cbeecb6 100644 --- a/drivers/comedi/drivers/pcmuio.c +++ b/drivers/comedi/drivers/pcmuio.c @@ -362,12 +362,15 @@ static irqreturn_t pcmuio_interrupt(int irq, void *d) struct pcmuio_private *devpriv = dev->private; int handled = 0; + if (!dev->attached) + return IRQ_NONE; + if (irq == dev->irq) handled += pcmuio_handle_asic_interrupt(dev, 0); if (irq == devpriv->irq2) handled += pcmuio_handle_asic_interrupt(dev, 1); - return handled ? IRQ_HANDLED : IRQ_NONE; + return IRQ_RETVAL(handled); } /* chip->spinlock is already locked */ From 1bc9538df6be8dac8727179d8bcc0d880648361b Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 18 Jun 2026 11:09:18 +0100 Subject: [PATCH 213/513] comedi: quatech_daqp_cs: Fix sanity check in interrupt handler The driver requests an interrupt handler for the device before it is fully set up. For safety, the interrupt handler checks the dev->attached flag to ensure the device is fully set up, but it currently does that after dereferencing the dev->read_dev pointer which may be NULL if dev->attached is false. Move the check to avoid the possible null pointer dereference. Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260618102949.26607-12-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/quatech_daqp_cs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/quatech_daqp_cs.c b/drivers/comedi/drivers/quatech_daqp_cs.c index 2a76c75c513b..d5e3e213e233 100644 --- a/drivers/comedi/drivers/quatech_daqp_cs.c +++ b/drivers/comedi/drivers/quatech_daqp_cs.c @@ -211,13 +211,15 @@ static irqreturn_t daqp_interrupt(int irq, void *dev_id) { struct comedi_device *dev = dev_id; struct comedi_subdevice *s = dev->read_subdev; - struct comedi_cmd *cmd = &s->async->cmd; + struct comedi_cmd *cmd; int loop_limit = 10000; int status; if (!dev->attached) return IRQ_NONE; + cmd = &s->async->cmd; + status = inb(dev->iobase + DAQP_STATUS_REG); if (!(status & DAQP_STATUS_EVENTS)) return IRQ_NONE; From 1123dc1ae35f8600ef191930b3c4635b62300cb2 Mon Sep 17 00:00:00 2001 From: Pankaj Patil Date: Sat, 30 May 2026 21:53:20 +0100 Subject: [PATCH 214/513] dt-bindings: nvmem: qfprom: Add glymur compatible Document compatible string for the QFPROM on Glymur platform. Signed-off-by: Pankaj Patil Reviewed-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260530205333.117458-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 2ab047f2bb69..aad8f5ea6fff 100644 --- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml +++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml @@ -19,6 +19,7 @@ properties: - enum: - qcom,apq8064-qfprom - qcom,apq8084-qfprom + - qcom,glymur-qfprom - qcom,ipq5018-qfprom - qcom,ipq5332-qfprom - qcom,ipq5424-qfprom From 9f99bb979f930e52e6861114fe03535d4bc2f374 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 30 May 2026 21:53:21 +0100 Subject: [PATCH 215/513] nvmem: rockchip-otp: alloc clks with main struct Use a flexible array member to simplify allocation slightly. No need for a separate calloc. Signed-off-by: Rosen Penev Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260530205333.117458-3-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/rockchip-otp.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/nvmem/rockchip-otp.c b/drivers/nvmem/rockchip-otp.c index 0ec78b5e19e7..2c0feb036f3f 100644 --- a/drivers/nvmem/rockchip-otp.c +++ b/drivers/nvmem/rockchip-otp.c @@ -78,9 +78,9 @@ struct rockchip_data { struct rockchip_otp { struct device *dev; void __iomem *base; - struct clk_bulk_data *clks; struct reset_control *rst; const struct rockchip_data *data; + struct clk_bulk_data clks[]; }; static int rockchip_otp_reset(struct rockchip_otp *otp) @@ -424,7 +424,7 @@ static int rockchip_otp_probe(struct platform_device *pdev) if (!data) return dev_err_probe(dev, -EINVAL, "failed to get match data\n"); - otp = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_otp), + otp = devm_kzalloc(&pdev->dev, struct_size(otp, clks, data->num_clks), GFP_KERNEL); if (!otp) return -ENOMEM; @@ -436,11 +436,6 @@ static int rockchip_otp_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(otp->base), "failed to ioremap resource\n"); - otp->clks = devm_kcalloc(dev, data->num_clks, sizeof(*otp->clks), - GFP_KERNEL); - if (!otp->clks) - return -ENOMEM; - for (i = 0; i < data->num_clks; ++i) otp->clks[i].id = data->clks[i]; From bd66bfb0bf20919b07681a589bb2aa3f22b191b9 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 30 May 2026 21:53:22 +0100 Subject: [PATCH 216/513] nvmem: core: Default to read-only if wp-gpios present In case the nvmem DT node contains "wp-gpios" DT property, the device currently defaults to read-write and the force_ro sysfs attribute reads 0. Switch to the default read-only, which is both safer, and aligned with eMMC HW BOOT partition force_ro sysfs attribute behavior, which also defaults to read-only. The adjustment of nvmem->read_only value to read-only in case wp-gpios DT property is present must be done only after the device_add() got called because device_add() does internally call nvmem_bin_attr_get_umode(), which configures the permissions of 'nvmem' bin attr based on the value of nvmem->read_only that is only parsed from DT property 'read-only', without any adjustment. This way, if DT property 'read-only' is present, the 'nvmem' attribute is always read-only. Otherwise, if the device is writeable, then 'nvmem' attribute is writeable, and nvmem->read_only defaults to read-only, but can be switched to read-write at runtime via the 'force_ro' attribute. The updated behavior can be tested as follows: Current content: " $ cat /sys/bus/nvmem/devices/logging7/force_ro 1 $ hexdump -C /sys/bus/nvmem/devices/logging7/nvmem 00000000 66 6f 6f 0a ff ff ff ff " Write into default-read-only device: " $ echo bar > /sys/bus/nvmem/devices/logging7/nvmem bash: echo: write error: Operation not permitted $ cat /sys/bus/nvmem/devices/logging7/force_ro 1 " Unlock and write into device: " $ echo 0 > /sys/bus/nvmem/devices/logging7/force_ro $ cat /sys/bus/nvmem/devices/logging7/force_ro 0 $ echo bar > /sys/bus/nvmem/devices/logging7/nvmem $ hexdump -C /sys/bus/nvmem/devices/logging7/nvmem 00000000 62 61 72 0a ff ff ff ff " Relock and write into device, fails because device is read-only again: " $ echo 1 > /sys/bus/nvmem/devices/logging7/force_ro $ echo baz > /sys/bus/nvmem/devices/logging7/nvmem bash: echo: write error: Operation not permitted $ hexdump -C /sys/bus/nvmem/devices/logging7/nvmem 00000000 62 61 72 0a ff ff ff ff " Reviewed-by: Bartosz Golaszewski Signed-off-by: Marek Vasut Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260530205333.117458-4-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index e871181751f3..6bcb90760bee 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -1019,6 +1019,10 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) if (rval) goto err_remove_dev; + /* If the device has WP GPIO, default to read-only */ + if (nvmem->wp_gpio) + nvmem->read_only = true; + #ifdef CONFIG_NVMEM_SYSFS rval = nvmem_populate_sysfs_cells(nvmem); if (rval) From ec4b806d0c7e688337241e9aa4b153082fd3275b Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Sat, 30 May 2026 21:53:23 +0100 Subject: [PATCH 217/513] dt-bindings: nvmem: qfprom: qcom: Add Hawi compatible Document compatible string for the QFPROM on Hawi platform. Signed-off-by: Mukesh Ojha Acked-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260530205333.117458-5-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 aad8f5ea6fff..721c34388746 100644 --- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml +++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml @@ -20,6 +20,7 @@ properties: - qcom,apq8064-qfprom - qcom,apq8084-qfprom - qcom,glymur-qfprom + - qcom,hawi-qfprom - qcom,ipq5018-qfprom - qcom,ipq5332-qfprom - qcom,ipq5424-qfprom From 18036ec7334ba6440eb774cdd7e94db09c581fbd Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 30 May 2026 21:53:24 +0100 Subject: [PATCH 218/513] nvmem: nintendo-otp: Use of_device_get_match_data() Use of_device_get_match_data() to retrieve the devtype data instead of open-coding the OF match lookup and dereferencing match->data. This also replaces the deprecated of_device.h include with of.h. Assisted-by: Codex:GPT-5.5 Signed-off-by: Rosen Penev Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260530205333.117458-6-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/nintendo-otp.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/nvmem/nintendo-otp.c b/drivers/nvmem/nintendo-otp.c index 4440d4e5fb83..a4d986e4588e 100644 --- a/drivers/nvmem/nintendo-otp.c +++ b/drivers/nvmem/nintendo-otp.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #define HW_OTPCMD 0 @@ -73,8 +73,7 @@ MODULE_DEVICE_TABLE(of, nintendo_otp_of_table); static int nintendo_otp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - const struct of_device_id *of_id = - of_match_device(nintendo_otp_of_table, dev); + const struct nintendo_otp_devtype_data *data; struct nvmem_device *nvmem; struct nintendo_otp_priv *priv; @@ -94,8 +93,8 @@ static int nintendo_otp_probe(struct platform_device *pdev) if (IS_ERR(priv->regs)) return PTR_ERR(priv->regs); - if (of_id->data) { - const struct nintendo_otp_devtype_data *data = of_id->data; + data = of_device_get_match_data(dev); + if (data) { config.name = data->name; config.size = data->num_banks * BANK_SIZE; } From ef558843eff499590e5123c5478140c37dadee2f Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Sat, 30 May 2026 21:53:25 +0100 Subject: [PATCH 219/513] dt-bindings: nvmem: qfprom: Add Milos compatible Document compatible string for the QFPROM on Milos platform. Signed-off-by: Alexander Koskovich Reviewed-by: Bjorn Andersson Reviewed-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260530205333.117458-7-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 721c34388746..646a0da7e839 100644 --- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml +++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml @@ -29,6 +29,7 @@ properties: - qcom,ipq8074-qfprom - qcom,ipq9574-qfprom - qcom,kaanapali-qfprom + - qcom,milos-qfprom - qcom,msm8226-qfprom - qcom,msm8916-qfprom - qcom,msm8917-qfprom From 7acd1e983c9c3b8f5749bea497208d5656e23dee Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Sat, 30 May 2026 21:53:26 +0100 Subject: [PATCH 220/513] dt-bindings: nvmem: lan9662-otpc: Add LAN969x series Unlike LAN966x series which has 8K of OTP space, LAN969x series has 16K of OTP space, so document the compatible. Acked-by: Conor Dooley Signed-off-by: Robert Marko Reviewed-by: Claudiu Beznea Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260530205333.117458-8-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/nvmem/microchip,lan9662-otpc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/nvmem/microchip,lan9662-otpc.yaml b/Documentation/devicetree/bindings/nvmem/microchip,lan9662-otpc.yaml index f97c6beb4766..c03e96afe564 100644 --- a/Documentation/devicetree/bindings/nvmem/microchip,lan9662-otpc.yaml +++ b/Documentation/devicetree/bindings/nvmem/microchip,lan9662-otpc.yaml @@ -25,6 +25,7 @@ properties: - const: microchip,lan9662-otpc - enum: - microchip,lan9662-otpc + - microchip,lan9691-otpc reg: maxItems: 1 From 4d8d405b139c7c2fd59cd4d1cc15bea0120c4dc7 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Sat, 30 May 2026 21:53:27 +0100 Subject: [PATCH 221/513] nvmem: lan9662-otp: add support for LAN969x Microchip LAN969x provides OTP with the same control logic, only the size differs as LAN969x has 16KB of OTP instead of 8KB like on LAN966x. Signed-off-by: Horatiu Vultur Signed-off-by: Robert Marko Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260530205333.117458-9-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 2 +- drivers/nvmem/lan9662-otpc.c | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 74ddbd0f79b0..78b648e14727 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -138,7 +138,7 @@ config NVMEM_JZ4780_EFUSE config NVMEM_LAN9662_OTPC tristate "Microchip LAN9662 OTP controller support" - depends on SOC_LAN966 || COMPILE_TEST + depends on SOC_LAN966 || ARCH_LAN969X || COMPILE_TEST depends on HAS_IOMEM help This driver enables the OTP controller available on Microchip LAN9662 diff --git a/drivers/nvmem/lan9662-otpc.c b/drivers/nvmem/lan9662-otpc.c index 56fc19f092a7..62d1d6381bf8 100644 --- a/drivers/nvmem/lan9662-otpc.c +++ b/drivers/nvmem/lan9662-otpc.c @@ -27,7 +27,6 @@ #define OTP_OTP_STATUS_OTP_CPUMPEN BIT(1) #define OTP_OTP_STATUS_OTP_BUSY BIT(0) -#define OTP_MEM_SIZE 8192 #define OTP_SLEEP_US 10 #define OTP_TIMEOUT_US 500000 @@ -176,7 +175,6 @@ static struct nvmem_config otp_config = { .word_size = 1, .reg_read = lan9662_otp_read, .reg_write = lan9662_otp_write, - .size = OTP_MEM_SIZE, }; static int lan9662_otp_probe(struct platform_device *pdev) @@ -196,6 +194,7 @@ static int lan9662_otp_probe(struct platform_device *pdev) otp_config.priv = otp; otp_config.dev = dev; + otp_config.size = (uintptr_t) device_get_match_data(dev); nvmem = devm_nvmem_register(dev, &otp_config); @@ -203,7 +202,14 @@ static int lan9662_otp_probe(struct platform_device *pdev) } static const struct of_device_id lan9662_otp_match[] = { - { .compatible = "microchip,lan9662-otpc", }, + { + .compatible = "microchip,lan9662-otpc", + .data = (const void *) SZ_8K, + }, + { + .compatible = "microchip,lan9691-otpc", + .data = (const void *) SZ_16K, + }, { }, }; MODULE_DEVICE_TABLE(of, lan9662_otp_match); From 31a75f07b9e90f46087a19b7eea3e6f96055a34b Mon Sep 17 00:00:00 2001 From: Komal Bajaj Date: Sat, 30 May 2026 21:53:28 +0100 Subject: [PATCH 222/513] dt-bindings: nvmem: qcom,qfprom: Add Shikra compatible Document compatible string for the QFPROM on Qualcomm Shikra SoC. Signed-off-by: Komal Bajaj Acked-by: Rob Herring (Arm) Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260530205333.117458-10-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 646a0da7e839..8134ddb54e13 100644 --- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml +++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml @@ -51,6 +51,7 @@ properties: - qcom,sdm630-qfprom - qcom,sdm670-qfprom - qcom,sdm845-qfprom + - qcom,shikra-qfprom - qcom,sm6115-qfprom - qcom,sm6350-qfprom - qcom,sm6375-qfprom From 2956111189fb240a5f2ea1816c7ce97d219b558c Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Sat, 30 May 2026 21:53:29 +0100 Subject: [PATCH 223/513] dt-bindings: nvmem: airoha: add SMC eFuses schema Add Airoha SMC eFuses schema to document new Airoha SoC AN7581/AN7583 way of accessing the 2 eFuse bank via the SMC command. Each eFuse bank expose 64 eFuse cells of 32 bit used to give information on HW Revision, PHY Calibration, Device Model, Private Key and all kind of other info specific to the SoC or the running system. Signed-off-by: Christian Marangi Reviewed-by: Rob Herring (Arm) Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260530205333.117458-11-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- .../bindings/nvmem/airoha,smc-efuses.yaml | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 Documentation/devicetree/bindings/nvmem/airoha,smc-efuses.yaml diff --git a/Documentation/devicetree/bindings/nvmem/airoha,smc-efuses.yaml b/Documentation/devicetree/bindings/nvmem/airoha,smc-efuses.yaml new file mode 100644 index 000000000000..c52f8d4bec39 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/airoha,smc-efuses.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/airoha,smc-efuses.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Airoha SMC eFuses + +description: | + Airoha new SoC AN7581 expose banks of eFuse accessible + via specific SMC commands. + + 2 different bank of eFuse or 64 cells of 32 bit are exposed + read-only used to give information on HW Revision, PHY Calibration, + Device Model, Private Key... + +maintainers: + - Christian Marangi + +properties: + compatible: + enum: + - airoha,an7581-efuses + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + '^efuse-bank@[0-1]$': + type: object + + allOf: + - $ref: nvmem.yaml# + + properties: + reg: + description: Identify the eFuse bank. + enum: [0, 1] + + required: + - reg + + unevaluatedProperties: false + +required: + - compatible + - '#address-cells' + - '#size-cells' + +additionalProperties: false + +examples: + - | + efuse { + compatible = "airoha,an7581-efuses"; + #address-cells = <1>; + #size-cells = <0>; + + efuse-bank@0 { + reg = <0>; + }; + }; + +... From b7846af2e6ca87fecac0e90c15e69217e174b126 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Sat, 30 May 2026 21:53:30 +0100 Subject: [PATCH 224/513] nvmem: airoha: Add support for SMC eFUSE Add support for SMC eFUSE on AN7581 SoC. The SoC have 2 set of 2048 bits of eFUSE that are used to read calibration value for PCIe, Thermal, USB and other specific info of the SoC like revision and HW device present. eFuse value are taken by sending SMC command. ATF is responsible of validaing the data and rejecting reading protected data (like Private Key). In such case the SMC command will return non-zero value on a0 register. Signed-off-by: Christian Marangi Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260530205333.117458-12-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 13 ++++ drivers/nvmem/Makefile | 2 + drivers/nvmem/airoha-smc-efuses.c | 125 ++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 drivers/nvmem/airoha-smc-efuses.c diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 78b648e14727..77ff62d1cd01 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -28,6 +28,19 @@ source "drivers/nvmem/layouts/Kconfig" # Devices +config NVMEM_AIROHA_SMC_EFUSES + tristate "Airoha SMC eFuse support" + depends on ARCH_AIROHA || COMPILE_TEST + depends on HAVE_ARM_SMCCC + default ARCH_AIROHA + help + Say y here to enable support for reading eFuses on Airoha AN7581 + SoCs. These are e.g. used to store factory programmed + calibration data required for the PCIe or the USB-C PHY or Thermal. + + This driver can also be built as a module. If so, the module will + be called nvmem-airoha-smc-efuses. + config NVMEM_AN8855_EFUSE tristate "Airoha AN8855 eFuse support" depends on COMPILE_TEST diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 7252b8ec88d4..f6f2bc51dee1 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -10,6 +10,8 @@ nvmem_layouts-y := layouts.o obj-y += layouts/ # Devices +obj-$(CONFIG_NVMEM_AIROHA_SMC_EFUSES) += nvmem-airoha-smc-efuses.o +nvmem-airoha-smc-efuses-y := airoha-smc-efuses.o obj-$(CONFIG_NVMEM_AN8855_EFUSE) += nvmem-an8855-efuse.o nvmem-an8855-efuse-y := an8855-efuse.o obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o diff --git a/drivers/nvmem/airoha-smc-efuses.c b/drivers/nvmem/airoha-smc-efuses.c new file mode 100644 index 000000000000..e56a99f4aa1f --- /dev/null +++ b/drivers/nvmem/airoha-smc-efuses.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Author: Christian Marangi + */ + +#include +#include +#include +#include +#include +#include +#include + +#define AIROHA_SMC_EFUSE_FID 0x82000001 +#define AIROHA_SMC_EFUSE_SUB_ID_READ 0x44414552 + +#define AIROHA_EFUSE_CELLS 64 + +struct airoha_efuse_bank_priv { + u32 bank_index; +}; + +static int airoha_efuse_read(void *context, unsigned int offset, + void *val, size_t bytes) +{ + struct regmap *regmap = context; + + return regmap_bulk_read(regmap, offset, + val, bytes / sizeof(u32)); +} + +static int airoha_efuse_reg_read(void *context, unsigned int offset, + unsigned int *val) +{ + struct airoha_efuse_bank_priv *priv = context; + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(AIROHA_SMC_EFUSE_FID, + AIROHA_SMC_EFUSE_SUB_ID_READ, + priv->bank_index, offset, 0, 0, 0, 0, &res); + + /* check if SMC reported an error */ + if (res.a0) + return -EIO; + + *val = res.a1; + return 0; +} + +static int airoha_efuse_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret; + + for_each_child_of_node_scoped(dev->of_node, child) { + struct nvmem_config nvmem_config = { + .size = AIROHA_EFUSE_CELLS * sizeof(u32), + .stride = sizeof(u32), + .word_size = sizeof(u32), + .reg_read = airoha_efuse_read, + }; + struct regmap_config regmap_config = { + .reg_read = airoha_efuse_reg_read, + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + }; + struct airoha_efuse_bank_priv *priv; + struct nvmem_device *nvmem; + struct regmap *regmap; + const char *name; + u32 bank; + + ret = of_property_read_u32(child, "reg", &bank); + if (ret) + return ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + name = devm_kasprintf(dev, GFP_KERNEL, "airoha-efuse-%u", + bank); + if (!name) + return -ENOMEM; + + priv->bank_index = bank; + + regmap_config.name = name; + regmap = devm_regmap_init(dev, NULL, priv, + ®map_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + nvmem_config.name = name; + nvmem_config.priv = regmap; + nvmem_config.dev = dev; + nvmem_config.id = bank; + nvmem_config.of_node = child; + nvmem = devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + } + + return 0; +} + +static const struct of_device_id airoha_efuse_of_match[] = { + { .compatible = "airoha,an7581-efuses", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, airoha_efuse_of_match); + +static struct platform_driver airoha_efuse_driver = { + .probe = airoha_efuse_probe, + .driver = { + .name = "airoha-efuse", + .of_match_table = airoha_efuse_of_match, + }, +}; +module_platform_driver(airoha_efuse_driver); + +MODULE_AUTHOR("Christian Marangi "); +MODULE_DESCRIPTION("Driver for Airoha SMC eFUSEs"); +MODULE_LICENSE("GPL"); From 302fbf6e36aa465f49b7733fc29e280d8ebeb7a6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 30 May 2026 21:53:31 +0100 Subject: [PATCH 225/513] nvmem: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260530205333.117458-13-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 77ff62d1cd01..730d71642214 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -275,7 +275,7 @@ config NVMEM_S32G_OCOTP Programmable memory pages. config NVMEM_QCOM_QFPROM - tristate "QCOM QFPROM Support" + tristate "Qualcomm QFPROM Support" depends on ARCH_QCOM || COMPILE_TEST depends on HAS_IOMEM help @@ -286,7 +286,7 @@ config NVMEM_QCOM_QFPROM will be called nvmem_qfprom. config NVMEM_QCOM_SEC_QFPROM - tristate "QCOM SECURE QFPROM Support" + tristate "Qualcomm SECURE QFPROM Support" depends on ARCH_QCOM || COMPILE_TEST depends on HAS_IOMEM depends on OF From 804a588eb58a29c74c4c2f0deea204870929b599 Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Sat, 30 May 2026 21:53:32 +0100 Subject: [PATCH 226/513] nvmem: cleanup dead code in Kconfig There is already an 'if NVMEM' condition wrapping NVMEM_RCAR_EFUSE, making the 'depends on' statement a duplicate dependency (dead code). I propose leaving the outer 'if NVMEM...endif' and removing the individual 'depends on' statement. This dead code was found by kconfirm, a static analysis tool for Kconfig. Signed-off-by: Julian Braha Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260530205333.117458-14-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 730d71642214..e10f7ff725ff 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -316,7 +316,6 @@ config NVMEM_RAVE_SP_EEPROM config NVMEM_RCAR_EFUSE tristate "Renesas R-Car Gen4 E-FUSE support" depends on (ARCH_RENESAS && ARM64) || COMPILE_TEST - depends on NVMEM help Enable support for reading the fuses in the E-FUSE or OTP non-volatile memory block on Renesas R-Car Gen4 SoCs. @@ -496,4 +495,4 @@ config NVMEM_QORIQ_EFUSE This driver can also be built as a module. If so, the module will be called nvmem_qoriq_efuse. -endif +endif # NVMEM From 45cb0223740b863089ce3cc523155cf7a55e1479 Mon Sep 17 00:00:00 2001 From: Tomasz Maciej Nowak Date: Sat, 30 May 2026 21:53:33 +0100 Subject: [PATCH 227/513] nvmem: layouts: u-boot-env: check earlier for ethaddr length Unfortunately the ethaddr value in U-Boot environment might be enclosed in single/double quotes or be something completely different. This can make it different than MAC_ADDR_STR_LEN, which results in EINVAL returned by ethaddr post process. Move the check for length earlier, to skip post processing, so nvmem could still present ethaddr value as a string if the value doesn't match MAC_ADDR_STR_LEN. Signed-off-by: Tomasz Maciej Nowak Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260530205333.117458-15-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/layouts/u-boot-env.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/nvmem/layouts/u-boot-env.c b/drivers/nvmem/layouts/u-boot-env.c index f27f387bb52a..33ec2350386f 100644 --- a/drivers/nvmem/layouts/u-boot-env.c +++ b/drivers/nvmem/layouts/u-boot-env.c @@ -38,9 +38,6 @@ static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, i { u8 mac[ETH_ALEN]; - if (bytes != MAC_ADDR_STR_LEN) - return -EINVAL; - if (!mac_pton(buf, mac)) return -EINVAL; @@ -75,7 +72,7 @@ static int u_boot_env_parse_cells(struct device *dev, struct nvmem_device *nvmem info.offset = data_offset + value - data; info.bytes = strlen(value); info.np = of_get_child_by_name(dev->of_node, info.name); - if (!strcmp(var, "ethaddr")) { + if (!strcmp(var, "ethaddr") && info.bytes == MAC_ADDR_STR_LEN) { info.raw_len = strlen(value); info.bytes = ETH_ALEN; info.read_post_process = u_boot_env_read_post_process_ethaddr; From 0c419df9c190b80136cc774325f84238e430e905 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 9 Jul 2026 13:47:22 +0300 Subject: [PATCH 228/513] mei: lb: fix incorrect type in assignment Fix the mix between __le32 and integer by casting the MEI_LB2_CMD constant as __le32 while using it. Fixes sparse waring: drivers/misc/mei/mei_lb.c:284:32: sparse: sparse: restricted __le32 degrades to integer drivers/misc/mei/mei_lb.c:330:40: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __le32 [usertype] command_id @@ got int @@ drivers/misc/mei/mei_lb.c:330:40: sparse: expected restricted __le32 [usertype] command_id drivers/misc/mei/mei_lb.c:330:40: sparse: got int Fixes: 773a43b8627f ("mei: lb: add late binding version 2") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605091533.79Zcv3CX-lkp@intel.com/ Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260709-fix_type_le-v3-1-478761151e05@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/mei_lb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mei/mei_lb.c b/drivers/misc/mei/mei_lb.c index f6a258c2b838..9fa69acf28d5 100644 --- a/drivers/misc/mei/mei_lb.c +++ b/drivers/misc/mei/mei_lb.c @@ -281,7 +281,7 @@ static int mei_lb_check_response_v2(const struct device *dev, ssize_t bytes, bytes, sizeof(rsp->rheader)); return -ENOMSG; } - if (rsp->rheader.header.command_id != MEI_LB2_CMD) { + if (rsp->rheader.header.command_id != cpu_to_le32(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; @@ -327,7 +327,7 @@ static int mei_lb_push_payload_v2(struct device *dev, struct mei_cl_device *clde if (sent_data + chunk_size == payload_size) last_chunk = MEI_LB2_FLAG_LST_CHUNK; - req->header.command_id = MEI_LB2_CMD; + req->header.command_id = cpu_to_le32(MEI_LB2_CMD); req->type = cpu_to_le32(type); req->flags = cpu_to_le32(flags | first_chunk | last_chunk); req->reserved = 0; From 0502b95447e89c0ce1bff198e429fcdacb93d389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 9 Jun 2026 16:55:13 +0200 Subject: [PATCH 229/513] comedi: Drop unused assignments from pnp_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Explicitly assigning .driver_data in drivers that don't use this member is silly and a bit irritating. Drop these. Also simplify the list terminator entry to be just empty to match what most other device_id tables do. There is no changed semantic, not even a change in the compiled result. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/c938b407cc16e9db2a59c67f390f073eeee6f1b3.1781016848.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/c6xdigio.c | 6 +++--- drivers/comedi/drivers/ni_atmio.c | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/comedi/drivers/c6xdigio.c b/drivers/comedi/drivers/c6xdigio.c index b6563a48ada6..fd36f820979c 100644 --- a/drivers/comedi/drivers/c6xdigio.c +++ b/drivers/comedi/drivers/c6xdigio.c @@ -224,10 +224,10 @@ static void c6xdigio_init(struct comedi_device *dev) static const struct pnp_device_id c6xdigio_pnp_tbl[] = { /* Standard LPT Printer Port */ - {.id = "PNP0400", .driver_data = 0}, + { .id = "PNP0400" }, /* ECP Printer Port */ - {.id = "PNP0401", .driver_data = 0}, - {} + { .id = "PNP0401" }, + { } }; static struct pnp_driver c6xdigio_pnp_driver = { diff --git a/drivers/comedi/drivers/ni_atmio.c b/drivers/comedi/drivers/ni_atmio.c index 7bc336333ace..537301eee9bf 100644 --- a/drivers/comedi/drivers/ni_atmio.c +++ b/drivers/comedi/drivers/ni_atmio.c @@ -215,13 +215,13 @@ static const int ni_irqpin[] = { #include "ni_mio_common.c" -static const struct pnp_device_id __maybe_unused device_ids[] = { - {.id = "NIC1900", .driver_data = 0}, - {.id = "NIC2400", .driver_data = 0}, - {.id = "NIC2500", .driver_data = 0}, - {.id = "NIC2600", .driver_data = 0}, - {.id = "NIC2700", .driver_data = 0}, - {.id = ""} +static const struct pnp_device_id device_ids[] = { + { .id = "NIC1900" }, + { .id = "NIC2400" }, + { .id = "NIC2500" }, + { .id = "NIC2600" }, + { .id = "NIC2700" }, + { } }; MODULE_DEVICE_TABLE(pnp, device_ids); From 88bf4a3d7d47a971449541957018ae54ca87f80f Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 17 Jul 2026 18:05:30 +0800 Subject: [PATCH 230/513] misc: bcm-vk: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Link: https://patch.msgid.link/20260717100533.601899-2-panchuang@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/bcm-vk/bcm_vk_dev.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/misc/bcm-vk/bcm_vk_dev.c b/drivers/misc/bcm-vk/bcm_vk_dev.c index 5773ffb46f0f..54b40b4d556d 100644 --- a/drivers/misc/bcm-vk/bcm_vk_dev.c +++ b/drivers/misc/bcm-vk/bcm_vk_dev.c @@ -1370,21 +1370,15 @@ static int bcm_vk_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs), bcm_vk_msgq_irqhandler, IRQF_SHARED, DRV_MODULE_NAME, vk); - if (err) { - dev_err(dev, "failed to request msgq IRQ %d for MSIX %d\n", - pdev->irq + vk->num_irqs, vk->num_irqs + 1); + if (err) goto err_irq; - } } /* one irq for notification from VK */ err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs), bcm_vk_notf_irqhandler, IRQF_SHARED, DRV_MODULE_NAME, vk); - if (err) { - dev_err(dev, "failed to request notf IRQ %d for MSIX %d\n", - pdev->irq + vk->num_irqs, vk->num_irqs + 1); + if (err) goto err_irq; - } vk->num_irqs++; for (i = 0; @@ -1393,11 +1387,8 @@ static int bcm_vk_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs), bcm_vk_tty_irqhandler, IRQF_SHARED, DRV_MODULE_NAME, vk); - if (err) { - dev_err(dev, "failed request tty IRQ %d for MSIX %d\n", - pdev->irq + vk->num_irqs, vk->num_irqs + 1); + if (err) goto err_irq; - } bcm_vk_tty_set_irq_enabled(vk, i); } From 7db324fbee6c5fe19ea99c8183d2e1cb6102f109 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 17 Jul 2026 18:05:31 +0800 Subject: [PATCH 231/513] misc: Remove redundant dev_err()/dev_err_probe() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() and devm_request_threaded_irq() automatically log detailed error messages on failure. Remove the now-redundant driver-specific dev_err() and dev_err_probe() calls. Signed-off-by: Pan Chuang Link: https://patch.msgid.link/20260717100533.601899-3-panchuang@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/hi6421v600-irq.c | 5 +---- drivers/misc/mrvl_cn10k_dpi.c | 4 +--- drivers/misc/tps6594-esm.c | 2 +- drivers/misc/tps6594-pfsm.c | 2 +- drivers/misc/xilinx_sdfec.c | 4 +--- 5 files changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/misc/hi6421v600-irq.c b/drivers/misc/hi6421v600-irq.c index 5ba40222eb12..5f65ca91b342 100644 --- a/drivers/misc/hi6421v600-irq.c +++ b/drivers/misc/hi6421v600-irq.c @@ -274,11 +274,8 @@ static int hi6421v600_irq_probe(struct platform_device *pdev) NULL, IRQF_TRIGGER_LOW | IRQF_SHARED | IRQF_NO_SUSPEND, "pmic", priv); - if (ret < 0) { - dev_err(dev, "Failed to start IRQ handling thread: error %d\n", - ret); + if (ret < 0) return ret; - } return 0; } diff --git a/drivers/misc/mrvl_cn10k_dpi.c b/drivers/misc/mrvl_cn10k_dpi.c index 7d5433121ff6..d26c53af05eb 100644 --- a/drivers/misc/mrvl_cn10k_dpi.c +++ b/drivers/misc/mrvl_cn10k_dpi.c @@ -470,10 +470,8 @@ static int dpi_irq_init(struct dpipf *dpi) ret = devm_request_irq(dev, pci_irq_vector(pdev, DPI_MBOX_PF_VF_INT_IDX), dpi_mbox_intr_handler, 0, "dpi-mbox", dpi); - if (ret) { - dev_err(dev, "DPI: request_irq failed for mbox; err=%d\n", ret); + if (ret) return ret; - } dpi_reg_write(dpi, DPI_MBOX_VF_PF_INT_ENA_W1S, GENMASK_ULL(31, 0)); diff --git a/drivers/misc/tps6594-esm.c b/drivers/misc/tps6594-esm.c index 2fbd3fbdf713..50e121669c6d 100644 --- a/drivers/misc/tps6594-esm.c +++ b/drivers/misc/tps6594-esm.c @@ -62,7 +62,7 @@ static int tps6594_esm_probe(struct platform_device *pdev) tps6594_esm_isr, IRQF_ONESHOT, pdev->resource[i].name, pdev); if (ret) - return dev_err_probe(dev, ret, "Failed to request irq\n"); + return ret; } ret = regmap_set_bits(tps->regmap, TPS6594_REG_ESM_SOC_MODE_CFG, diff --git a/drivers/misc/tps6594-pfsm.c b/drivers/misc/tps6594-pfsm.c index 44fa81d6cec2..c3a0d6367f2c 100644 --- a/drivers/misc/tps6594-pfsm.c +++ b/drivers/misc/tps6594-pfsm.c @@ -308,7 +308,7 @@ static int tps6594_pfsm_probe(struct platform_device *pdev) tps6594_pfsm_isr, IRQF_ONESHOT, pdev->resource[i].name, pdev); if (ret) - return dev_err_probe(dev, ret, "Failed to request irq\n"); + return ret; } platform_set_drvdata(pdev, pfsm); diff --git a/drivers/misc/xilinx_sdfec.c b/drivers/misc/xilinx_sdfec.c index 3135ba3a58ee..fe7bea3b14bf 100644 --- a/drivers/misc/xilinx_sdfec.c +++ b/drivers/misc/xilinx_sdfec.c @@ -1390,10 +1390,8 @@ static int xsdfec_probe(struct platform_device *pdev) err = devm_request_threaded_irq(dev, xsdfec->irq, NULL, xsdfec_irq_thread, IRQF_ONESHOT, "xilinx-sdfec16", xsdfec); - if (err < 0) { - dev_err(dev, "unable to request IRQ%d", xsdfec->irq); + if (err < 0) goto err_xsdfec_dev; - } } err = ida_alloc(&dev_nrs, GFP_KERNEL); From cfefdadbae4b4c8c353ab5f397d3bf77a71279b4 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 17 Jul 2026 18:05:32 +0800 Subject: [PATCH 232/513] mei: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_threaded_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Link: https://patch.msgid.link/20260717100533.601899-4-panchuang@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/gsc-me.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/misc/mei/gsc-me.c b/drivers/misc/mei/gsc-me.c index 73d5beeb9c34..376840b2c354 100644 --- a/drivers/misc/mei/gsc-me.c +++ b/drivers/misc/mei/gsc-me.c @@ -101,10 +101,8 @@ static int mei_gsc_probe(struct auxiliary_device *aux_dev, mei_me_irq_quick_handler, mei_me_irq_thread_handler, IRQF_ONESHOT, KBUILD_MODNAME, dev); - if (ret) { - dev_err(device, "irq register failed %d\n", ret); + if (ret) goto err; - } } ret = mei_register(dev, device); From 2847d9ab088bb559f4f03070f6ad103070362b25 Mon Sep 17 00:00:00 2001 From: Jahnavi MN Date: Thu, 16 Jul 2026 08:37:43 +0000 Subject: [PATCH 233/513] rust_binder: Add dynamic debug logging mask Implement a dynamic debug logging mask (`debug_mask`) for the `rust_binder` module to allow dynamic runtime configuration of log levels. This enables parity with the legacy C driver's debug mask. Since the Rust `module!` macro in the current kernel build does not yet support declaring module parameters directly in Rust, we define the `debug_mask` variable in Rust as an `Atomic` exported via FFI using `#[no_mangle]`, and link to it as `extern` in a C companion file to expose it to the kernel runtime. To verify the setup, instrument process lifecycle events (open, flush, and release) in `process.rs` under the new `BINDER_DEBUG_OPEN_CLOSE` logging mask. These entry-point events are chosen for initial validation because they represent the start of the Binder lifecycle and occur at low frequency, allowing simple runtime verification of the dynamic toggle without log noise. Reviewed-by: Carlos Llamas Reviewed-by: Alice Ryhl Signed-off-by: Jahnavi MN Link: https://patch.msgid.link/20260716-rust_binder_debug_mask-v4-1-3d7436c2d2f2@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/debug.rs | 76 ++++++++++++++++++++++ drivers/android/binder/process.rs | 7 +- drivers/android/binder/rust_binder_main.rs | 2 + drivers/android/binder/rust_binderfs.c | 3 + rust/kernel/task.rs | 7 ++ 5 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 drivers/android/binder/debug.rs diff --git a/drivers/android/binder/debug.rs b/drivers/android/binder/debug.rs new file mode 100644 index 000000000000..824b10c004c3 --- /dev/null +++ b/drivers/android/binder/debug.rs @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2026 Google LLC. + +//! Binder debugging helpers. + +#![allow(dead_code)] + +use kernel::bits::bit_u32; +use kernel::sync::atomic::Atomic; + +kernel::impl_flags!( + /// Represents multiple debug mask flags. + #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] + pub struct DebugMasks(u32); + + /// Represents a single debug mask category. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum DebugMask { + UserError = bit_u32(0), + FailedTransaction = bit_u32(1), + DeadTransaction = bit_u32(2), + OpenClose = bit_u32(3), + DeadBinder = bit_u32(4), + DeathNotification = bit_u32(5), + ReadWrite = bit_u32(6), + UserRefs = bit_u32(7), + Threads = bit_u32(8), + Transaction = bit_u32(9), + TransactionComplete = bit_u32(10), + FreeBuffer = bit_u32(11), + InternalRefs = bit_u32(12), + PriorityCap = bit_u32(13), + Spinlocks = bit_u32(14), + } +); + +#[no_mangle] +pub(crate) static rust_binder_debug_mask: Atomic = Atomic::new( + (DebugMask::UserError as u32) + | (DebugMask::FailedTransaction as u32) + | (DebugMask::DeadTransaction as u32), +); + +/// Checks if the given debug logging category is enabled in the mask. +pub(crate) fn debug_mask_enabled(mask: DebugMask) -> bool { + let current_mask = rust_binder_debug_mask.load(kernel::sync::atomic::Relaxed); + DebugMasks(current_mask).contains(mask) +} + +/// Prints a debug log if the specified mask category is enabled. +#[macro_export] +macro_rules! binder_debug { + // Rule to explicitly specify a PID (used in kworkers). + (pid=$pid:expr, $mask:ident, $($arg:tt)*) => { + if $crate::debug::debug_mask_enabled($crate::debug::DebugMask::$mask) { + kernel::pr_info!( + "{}: {}\n", + $pid, + kernel::prelude::fmt!($($arg)*) + ); + } + }; + + // Default rule (automatically prepends "PID:TID" of the current calling thread). + ($mask:ident, $($arg:tt)*) => { + if $crate::debug::debug_mask_enabled($crate::debug::DebugMask::$mask) { + let thread = kernel::current!(); + kernel::pr_info!( + "{}:{} {}\n", + thread.tgid(), + thread.pid(), + kernel::prelude::fmt!($($arg)*) + ); + } + }; +} diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 0555c4bd503e..5240686324cf 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -1341,6 +1341,7 @@ pub(crate) fn lock_with_nodes(&self) -> WithNodes<'_> { } fn deferred_flush(&self) { + binder_debug!(pid = self.task.pid(), OpenClose, "flushing process"); let inner = self.inner.lock(); for thread in inner.threads.values() { thread.exit_looper(); @@ -1348,6 +1349,8 @@ fn deferred_flush(&self) { } fn deferred_release(self: Arc) { + binder_debug!(pid = self.task.pid(), OpenClose, "releasing process"); + let is_manager = { let mut inner = self.inner.lock(); inner.is_dead = true; @@ -1641,7 +1644,9 @@ fn ioctl_write_read( /// The file operations supported by `Process`. impl Process { pub(crate) fn open(ctx: ArcBorrow<'_, Context>, file: &File) -> Result> { - Self::new(ctx.into(), ARef::from(file.cred())) + let proc = Self::new(ctx.into(), ARef::from(file.cred()))?; + binder_debug!(OpenClose, "opened process"); + Ok(proc) } pub(crate) fn release(this: Arc, _file: &File) { diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index 432390aab25b..29829cb210a4 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -31,6 +31,8 @@ mod context; mod deferred_close; mod defs; +#[macro_use] +mod debug; mod error; mod node; mod page_range; diff --git a/drivers/android/binder/rust_binderfs.c b/drivers/android/binder/rust_binderfs.c index ade1c4d92499..300cc65562d1 100644 --- a/drivers/android/binder/rust_binderfs.c +++ b/drivers/android/binder/rust_binderfs.c @@ -51,6 +51,9 @@ DEFINE_SHOW_ATTRIBUTE(rust_binder_proc); char *rust_binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES; module_param_named(rust_devices, rust_binder_devices_param, charp, 0444); +extern u32 rust_binder_debug_mask; +module_param_named(debug_mask, rust_binder_debug_mask, uint, 0644); + static dev_t binderfs_dev; static DEFINE_MUTEX(binderfs_minors_mutex); static DEFINE_IDA(binderfs_minors); diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs index 38273f4eedb5..1b290c61714d 100644 --- a/rust/kernel/task.rs +++ b/rust/kernel/task.rs @@ -210,6 +210,13 @@ pub fn pid(&self) -> Pid { unsafe { *ptr::addr_of!((*self.as_ptr()).pid) } } + /// Returns the TGID (Thread Group ID / Process ID) of the given task. + pub fn tgid(&self) -> Pid { + // SAFETY: The tgid of a task never changes after initialization, so reading this field is + // not a data race. + unsafe { *ptr::addr_of!((*self.as_ptr()).tgid) } + } + /// Returns the UID of the given task. #[inline] pub fn uid(&self) -> Kuid { From d8f87e4eded64b9e27f6c0f815b71489f29cb95c Mon Sep 17 00:00:00 2001 From: Jahnavi MN Date: Thu, 16 Jul 2026 08:37:44 +0000 Subject: [PATCH 234/513] rust_binder: Implement BINDER_DEBUG_USER_ERROR for freezer-related operation This adds dynamic debug logs for: - Requesting freeze notifications on invalid references, duplicate cookies, or already active registrations. - Completing freeze notifications that are not pending or not found. - Clearing freeze notifications on invalid references, inactive notifications, or cookie mismatches. Reviewed-by: Carlos Llamas Reviewed-by: Alice Ryhl Signed-off-by: Jahnavi MN Link: https://patch.msgid.link/20260716-rust_binder_debug_mask-v4-2-3d7436c2d2f2@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/freeze.rs | 40 ++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs index f43388ed6ae2..318a9d2bb261 100644 --- a/drivers/android/binder/freeze.rs +++ b/drivers/android/binder/freeze.rs @@ -189,12 +189,15 @@ pub(crate) fn request_freeze_notif( info = match node_refs.by_handle.get_mut(&handle) { Some(info) => info, None => { - pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION invalid ref {}\n", handle); + binder_debug!( + UserError, + "BC_REQUEST_FREEZE_NOTIFICATION invalid ref {handle}" + ); return Err(EINVAL); } }; if info.freeze().is_some() { - pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION already set\n"); + binder_debug!(UserError, "BC_REQUEST_FREEZE_NOTIFICATION already set"); return Err(EINVAL); } let node_ref = info.node_ref(); @@ -202,7 +205,7 @@ pub(crate) fn request_freeze_notif( if let rbtree::Entry::Occupied(ref dupe) = freeze_entry { if !dupe.get().allow_duplicate(&node_ref.node) { - pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n"); + binder_debug!(UserError, "BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie"); return Err(EINVAL); } } @@ -267,7 +270,11 @@ pub(crate) fn freeze_notif_done(self: &Arc, reader: &mut UserSliceReader) let mut node_refs_guard = self.node_refs.lock(); let node_refs = &mut *node_refs_guard; let Some(freeze) = node_refs.freeze_listeners.get_mut(&cookie) else { - pr_warn!("BC_FREEZE_NOTIFICATION_DONE {:016x} not found\n", cookie.0); + binder_debug!( + UserError, + "BC_FREEZE_NOTIFICATION_DONE {:016x} not found", + cookie.0 + ); return Err(EINVAL); }; let mut clear_msg = None; @@ -277,8 +284,9 @@ pub(crate) fn freeze_notif_done(self: &Arc, reader: &mut UserSliceReader) freeze.num_cleared_duplicates += 1; } else { if !freeze.is_pending { - pr_warn!( - "BC_FREEZE_NOTIFICATION_DONE {:016x} not pending\n", + binder_debug!( + UserError, + "BC_FREEZE_NOTIFICATION_DONE {:016x} not pending", cookie.0 ); return Err(EINVAL); @@ -307,19 +315,31 @@ pub(crate) fn clear_freeze_notif(self: &Arc, reader: &mut UserSliceReader) let mut node_refs_guard = self.node_refs.lock(); let node_refs = &mut *node_refs_guard; let Some(info) = node_refs.by_handle.get_mut(&handle) else { - pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid ref {}\n", handle); + binder_debug!( + UserError, + "BC_CLEAR_FREEZE_NOTIFICATION invalid ref {handle}" + ); return Err(EINVAL); }; let Some(info_cookie) = info.freeze() else { - pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n"); + binder_debug!( + UserError, + "BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active" + ); return Err(EINVAL); }; if *info_cookie != cookie { - pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch\n"); + binder_debug!( + UserError, + "BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch" + ); return Err(EINVAL); } let Some(listener) = node_refs.freeze_listeners.get_mut(&cookie) else { - pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {}\n", handle); + binder_debug!( + UserError, + "BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {handle}" + ); return Err(EINVAL); }; listener.is_clearing = true; From 11071c63a91eefaef25d602697fe04fc2b7748e8 Mon Sep 17 00:00:00 2001 From: Jahnavi MN Date: Thu, 16 Jul 2026 08:37:45 +0000 Subject: [PATCH 235/513] rust_binder: Implement BINDER_DEBUG_USER_ERROR for refcounting and death notifications This adds dynamic debug logs for: - Decrementing handle reference counts that are already zero. - Mismatched reference states (calling inc_ref_done with no active inc_refs, or using a weak reference as a strong reference). - Requesting or clearing death notifications on invalid references, already active notifications, or with mismatched cookies. Reviewed-by: Carlos Llamas Reviewed-by: Alice Ryhl Signed-off-by: Jahnavi MN Link: https://patch.msgid.link/20260716-rust_binder_debug_mask-v4-3-3d7436c2d2f2@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/node.rs | 10 +++++---- drivers/android/binder/process.rs | 35 ++++++++++++++++++++++++------- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs index 59c5ab747bf4..fefa723d13c4 100644 --- a/drivers/android/binder/node.rs +++ b/drivers/android/binder/node.rs @@ -345,7 +345,7 @@ pub(crate) fn inc_ref_done_locked( ) -> Option> { let inner = self.inner.access_mut(owner_inner); if inner.active_inc_refs == 0 { - pr_err!("inc_ref_done called when no active inc_refs"); + binder_debug!(UserError, "inc_ref_done called when no active inc_refs"); return None; } @@ -821,6 +821,7 @@ pub(crate) fn get_count(&self) -> (usize, usize) { pub(crate) fn clone(&self, strong: bool) -> Result { if strong && self.strong_count == 0 { + binder_debug!(UserError, "tried to use weak ref as strong ref"); return Err(EINVAL); } Ok(self @@ -861,9 +862,10 @@ pub(crate) fn update(&mut self, inc: bool, strong: bool) -> bool { *count += 1; } else { if *count == 0 { - pr_warn!( - "pid {} performed invalid decrement on ref\n", - kernel::current!().pid() + binder_debug!( + UserError, + "performed invalid {} decrement on ref", + if strong { "strong" } else { "weak" } ); return false; } diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 5240686324cf..1d3a71292de0 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -912,7 +912,13 @@ pub(crate) fn get_transaction_node(&self, handle: u32) -> BinderResult } Ok(node_ref) } else { - Ok(self.get_node_from_handle(handle, true)?) + match self.get_node_from_handle(handle, true) { + Ok(node_ref) => Ok(node_ref), + Err(err) => { + binder_debug!(UserError, "got transaction to invalid handle {handle}"); + Err(err.into()) + } + } } } @@ -997,7 +1003,7 @@ pub(crate) fn update_ref( } else { // All refs are cleared in process exit, so this warning is expected in that case. if !self.inner.lock().is_dead { - pr_warn!("{}: no such ref {handle}\n", self.pid_in_current_ns()); + binder_debug!(UserError, "no such ref {handle}"); } } Ok(()) @@ -1250,13 +1256,19 @@ pub(crate) fn request_death( })?; let mut refs = self.node_refs.lock(); let Some(info) = refs.by_handle.get_mut(&handle) else { - pr_warn!("BC_REQUEST_DEATH_NOTIFICATION invalid ref {handle}\n"); + binder_debug!( + UserError, + "BC_REQUEST_DEATH_NOTIFICATION invalid ref {handle}" + ); return Ok(()); }; // Nothing to do if there is already a death notification request for this handle. if info.death().is_some() { - pr_warn!("BC_REQUEST_DEATH_NOTIFICATION death notification already set\n"); + binder_debug!( + UserError, + "BC_REQUEST_DEATH_NOTIFICATION death notification already set" + ); return Ok(()); } @@ -1293,17 +1305,26 @@ pub(crate) fn clear_death(&self, reader: &mut UserSliceReader, thread: &Thread) let mut refs = self.node_refs.lock(); let Some(info) = refs.by_handle.get_mut(&handle) else { - pr_warn!("BC_CLEAR_DEATH_NOTIFICATION invalid ref {handle}\n"); + binder_debug!( + UserError, + "BC_CLEAR_DEATH_NOTIFICATION invalid ref {handle}" + ); return Ok(()); }; let Some(death) = info.death().take() else { - pr_warn!("BC_CLEAR_DEATH_NOTIFICATION death notification not active\n"); + binder_debug!( + UserError, + "BC_CLEAR_DEATH_NOTIFICATION death notification not active" + ); return Ok(()); }; if death.cookie != cookie { *info.death() = Some(death); - pr_warn!("BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch\n"); + binder_debug!( + UserError, + "BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch" + ); return Ok(()); } From e49c203bce47d96456138a8a0819284c6dabbd84 Mon Sep 17 00:00:00 2001 From: Jahnavi MN Date: Thu, 16 Jul 2026 08:37:46 +0000 Subject: [PATCH 236/513] rust_binder: Implement BINDER_DEBUG_USER_ERROR for transaction parsing failures This adds dynamic debug logs in `thread.rs` for: - File descriptor array (FDA) parent offset and parent buffer address alignment misalignments. - Memory copy, write, and translation failures during transaction serialization (including out-of-bounds pointer fixups). - Incoming transactions or replies that do not match the expected thread calling stack (such as out-of-order replies). Reviewed-by: Carlos Llamas Reviewed-by: Alice Ryhl Signed-off-by: Jahnavi MN Link: https://patch.msgid.link/20260716-rust_binder_debug_mask-v4-4-3d7436c2d2f2@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/thread.rs | 54 +++++++++++++++++++------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 19f881948a84..9f0178a13d6f 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -728,11 +728,12 @@ fn translate_object( let alloc_offset = match sg_state.unused_buffer_space.claim_next(obj_length) { Ok(alloc_offset) => alloc_offset, Err(err) => { - pr_warn!( - "Failed to claim space for a BINDER_TYPE_PTR. (offset: {}, limit: {}, size: {})", + binder_debug!( + UserError, + "failed to claim space for a BINDER_TYPE_PTR (offset: {}, limit: {}, size: {})", sg_state.unused_buffer_space.offset, sg_state.unused_buffer_space.limit, - obj_length, + obj_length ); return Err(err.into()); } @@ -811,6 +812,7 @@ fn translate_object( let fds_len = num_fds.checked_mul(size_of::()).ok_or(EINVAL)?; if !is_aligned(parent_offset, size_of::()) { + binder_debug!(UserError, "FDA parent offset not aligned correctly"); return Err(EINVAL.into()); } @@ -829,6 +831,7 @@ fn translate_object( }; if !is_aligned(parent_entry.sender_uaddr, size_of::()) { + binder_debug!(UserError, "FDA parent buffer not aligned correctly"); return Err(EINVAL.into()); } @@ -912,12 +915,9 @@ fn apply_sg(&self, alloc: &mut Allocation, sg_state: &mut ScatterGatherState) -> let target_offset_end = fixup_offset.checked_add(fixup_len).ok_or(EINVAL)?; if fixup_offset < end_of_previous_fixup || offset_end < target_offset_end { - pr_warn!( - "Fixups oob {} {} {} {}", - fixup_offset, - end_of_previous_fixup, - offset_end, - target_offset_end + binder_debug!( + UserError, + "fixups oob {fixup_offset} {end_of_previous_fixup} {offset_end} {target_offset_end}" ); return Err(EINVAL.into()); } @@ -925,18 +925,21 @@ fn apply_sg(&self, alloc: &mut Allocation, sg_state: &mut ScatterGatherState) -> let copy_off = end_of_previous_fixup; let copy_len = fixup_offset - end_of_previous_fixup; if let Err(err) = alloc.copy_into(&mut reader, copy_off, copy_len) { - pr_warn!("Failed copying into alloc: {:?}", err); + binder_debug!(UserError, "failed copying into alloc: {err:?}"); return Err(err.into()); } if let PointerFixupEntry::Fixup { pointer_value, .. } = fixup { let res = alloc.write::(fixup_offset, pointer_value); if let Err(err) = res { - pr_warn!("Failed copying ptr into alloc: {:?}", err); + binder_debug!(UserError, "failed copying ptr into alloc: {err:?}"); return Err(err.into()); } } if let Err(err) = reader.skip(fixup_len) { - pr_warn!("Failed skipping {} from reader: {:?}", fixup_len, err); + binder_debug!( + UserError, + "failed skipping {fixup_len} from reader: {err:?}" + ); return Err(err.into()); } end_of_previous_fixup = target_offset_end; @@ -944,7 +947,7 @@ fn apply_sg(&self, alloc: &mut Allocation, sg_state: &mut ScatterGatherState) -> let copy_off = end_of_previous_fixup; let copy_len = offset_end - end_of_previous_fixup; if let Err(err) = alloc.copy_into(&mut reader, copy_off, copy_len) { - pr_warn!("Failed copying remainder into alloc: {:?}", err); + binder_debug!(UserError, "failed copying remainder into alloc: {err:?}"); return Err(err.into()); } } @@ -1048,7 +1051,7 @@ pub(crate) fn copy_transaction_data( let offset: usize = offset.try_into().map_err(|_| EINVAL)?; if offset < end_of_previous_object || !is_aligned(offset, size_of::()) { - pr_warn!("Got transaction with invalid offset."); + binder_debug!(UserError, "got transaction with invalid offset"); return Err(EINVAL.into()); } @@ -1073,7 +1076,7 @@ pub(crate) fn copy_transaction_data( ) { Ok(()) => end_of_previous_object = offset + object.size(), Err(err) => { - pr_warn!("Error while translating object."); + binder_debug!(UserError, "error while translating object: {err:?}"); return Err(err); } } @@ -1093,15 +1096,12 @@ pub(crate) fn copy_transaction_data( )?; if let Some(sg_state) = sg_state.as_mut() { - if let Err(err) = self.apply_sg(&mut alloc, sg_state) { - pr_warn!("Failure in apply_sg: {:?}", err); - return Err(err); - } + self.apply_sg(&mut alloc, sg_state)?; } if let Some((off_out, secctx)) = secctx.as_mut() { if let Err(err) = alloc.write(secctx_off, secctx.as_bytes()) { - pr_warn!("Failed to write security context: {:?}", err); + binder_debug!(UserError, "failed to write security context: {err:?}"); return Err(err.into()); } **off_out = secctx_off; @@ -1303,7 +1303,7 @@ fn transaction_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResu { let mut inner = self.inner.lock(); if !transaction.is_stacked_on(&inner.current_transaction) { - pr_warn!("Transaction stack changed during transaction!"); + binder_debug!(UserError, "got new transaction with bad transaction stack"); return Err(EINVAL.into()); } inner.current_transaction = Some(transaction.clone_arc()); @@ -1326,8 +1326,18 @@ fn transaction_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResu } fn reply_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { - let orig = self.inner.lock().pop_transaction_to_reply(self)?; + let orig = match self.inner.lock().pop_transaction_to_reply(self) { + Ok(orig) => orig, + Err(err) => { + binder_debug!(UserError, "got reply transaction with no transaction stack"); + return Err(err.into()); + } + }; if !orig.from.is_current_transaction(&orig) { + binder_debug!( + UserError, + "got reply transaction with bad transaction stack" + ); return Err(EINVAL.into()); } From c61f3ad2213cfb1d9f383d3b083fb7f1472cc657 Mon Sep 17 00:00:00 2001 From: Jahnavi MN Date: Thu, 16 Jul 2026 08:37:47 +0000 Subject: [PATCH 237/513] rust_binder: Implement BINDER_DEBUG_FAILED_TRANSACTION This adds dynamic debug logs for: - Failed replies, target process deaths, and error code deliveries. - Detailed transaction failure diagnostics (including sender/receiver PIDs, TIDs, transaction IDs, buffer sizes, and error codes). Reviewed-by: Carlos Llamas Reviewed-by: Alice Ryhl Signed-off-by: Jahnavi MN Link: https://patch.msgid.link/20260716-rust_binder_debug_mask-v4-5-3d7436c2d2f2@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/thread.rs | 21 ++++++++++++++++----- drivers/android/binder/transaction.rs | 8 ++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 9f0178a13d6f..38b90c79c057 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -1275,11 +1275,22 @@ fn transaction(self: &Arc, cmd: u32, reader: &mut UserSliceReader) -> Resu } } - pr_warn!( - "{}:{} transaction to {} failed: {err:?}", - info.from_pid, - info.from_tid, - info.to_pid + binder_debug!( + FailedTransaction, + "transaction {} to {}:{} failed {:?}, code {} size {}-{}", + if info.is_reply { + "reply" + } else if info.is_oneway() { + "async" + } else { + "call" + }, + info.to_pid, + info.to_tid, + err, + info.code, + info.data_size, + info.offsets_size ); } } diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index afef5b46eac2..069c792d2200 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -404,6 +404,14 @@ fn do_work( } else { // On failure to process the list, we send a reply back to the sender and ignore the // transaction on the recipient. + binder_debug!( + FailedTransaction, + "transaction {} to {} failed, fd fixups failed, size {}-{}", + self.debug_id, + self.to.task.pid(), + self.data_size, + self.offsets_size + ); return Ok(true); }; From 7ddb9f5d4564103d79c82f89f3d356629631add3 Mon Sep 17 00:00:00 2001 From: Jahnavi MN Date: Thu, 16 Jul 2026 08:37:48 +0000 Subject: [PATCH 238/513] rust_binder: Implement BINDER_DEBUG_DEATH_NOTIFICATION This adds dynamic debug logs for: - Memory allocation (OOM) failures when requesting death notifications - Registration and cancellation lifecycle events (BC_REQUEST / BC_CLEAR) - Delivery of death notification events to userspace (BR_DEAD_BINDER) Reviewed-by: Carlos Llamas Reviewed-by: Alice Ryhl Signed-off-by: Jahnavi MN Link: https://patch.msgid.link/20260716-rust_binder_debug_mask-v4-6-3d7436c2d2f2@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/node.rs | 5 +++++ drivers/android/binder/process.rs | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs index fefa723d13c4..8a87dc366aa9 100644 --- a/drivers/android/binder/node.rs +++ b/drivers/android/binder/node.rs @@ -1107,6 +1107,11 @@ fn do_work( // We're still holding the inner lock, so it cannot be aborted while we insert it into // the delivered list. process_inner.death_delivered(self.clone()); + binder_debug!( + DeathNotification, + "sending death notification, cookie {:016x}", + cookie + ); BR_DEAD_BINDER }; diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 1d3a71292de0..eb2f08bec655 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -1253,6 +1253,10 @@ pub(crate) fn request_death( // Queue BR_ERROR if we can't allocate memory for the death notification. let death = UniqueArc::new_uninit(GFP_KERNEL).inspect_err(|_| { thread.push_return_work(BR_ERROR); + binder_debug!( + DeathNotification, + "BC_REQUEST_DEATH_NOTIFICATION failed due to memory allocation failure" + ); })?; let mut refs = self.node_refs.lock(); let Some(info) = refs.by_handle.get_mut(&handle) else { @@ -1296,6 +1300,11 @@ pub(crate) fn request_death( info.node_ref().node.add_death(death, &mut owner_inner); } } + binder_debug!( + DeathNotification, + "BC_REQUEST_DEATH_NOTIFICATION handle {handle} cookie {:016x}", + cookie + ); Ok(()) } @@ -1339,6 +1348,11 @@ pub(crate) fn clear_death(&self, reader: &mut UserSliceReader, thread: &Thread) } } + binder_debug!( + DeathNotification, + "BC_CLEAR_DEATH_NOTIFICATION handle {handle} cookie {:016x}", + cookie + ); Ok(()) } From 5757ed4d9543ce1d5940c995e03fb0c6d32615cf Mon Sep 17 00:00:00 2001 From: Jahnavi MN Date: Thu, 16 Jul 2026 08:37:49 +0000 Subject: [PATCH 239/513] rust_binder: Implement BINDER_DEBUG_DEAD_TRANSACTION This adds dynamic debug logs for: - Releasing active transactions during thread stack unwinding. - Discarded transaction error codes when a thread exits. - Undelivered transaction acknowledgments (TRANSACTION_COMPLETE) upon thread exit. - Undelivered process death and freeze notifications when processes exit or die. - Undelivered transactions canceled due to target process death. We now store the process PID in `ThreadError`, `DeliverCode`, and `FreezeMessage` to ensure the correct PID is logged on cancellation. This is necessary because `cancel()` runs from background `kworkers`, which would otherwise print the wrong PID. Reviewed-by: Alice Ryhl Reviewed-by: Carlos Llamas Signed-off-by: Jahnavi MN Link: https://patch.msgid.link/20260716-rust_binder_debug_mask-v4-7-3d7436c2d2f2@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/freeze.rs | 24 ++++++++----- drivers/android/binder/node.rs | 9 ++++- drivers/android/binder/rust_binder_main.rs | 14 ++++++-- drivers/android/binder/thread.rs | 42 +++++++++++++++++----- drivers/android/binder/transaction.rs | 7 ++++ 5 files changed, 76 insertions(+), 20 deletions(-) diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs index 318a9d2bb261..66912b4cb527 100644 --- a/drivers/android/binder/freeze.rs +++ b/drivers/android/binder/freeze.rs @@ -60,6 +60,7 @@ fn allow_duplicate(&self, node: &DArc) -> bool { /// Represents a notification that the freeze state has changed. pub(crate) struct FreezeMessage { cookie: FreezeCookie, + pid: i32, } kernel::list::impl_list_arc_safe! { @@ -73,8 +74,8 @@ fn new(flags: kernel::alloc::Flags) -> Result { UniqueArc::new_uninit(flags) } - fn init(ua: UninitFM, cookie: FreezeCookie) -> DLArc { - match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie })) { + fn init(ua: UninitFM, cookie: FreezeCookie, pid: i32) -> DLArc { + match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie, pid })) { Ok(msg) => ListArc::from(msg), Err(err) => match err {}, } @@ -140,7 +141,14 @@ fn do_work( } } - fn cancel(self: DArc) {} + fn cancel(self: DArc) { + binder_debug!( + pid = self.pid, + DeadTransaction, + "undelivered freeze notification, {:016x}", + self.cookie.0 + ); + } fn should_sync_wakeup(&self) -> bool { false @@ -258,7 +266,7 @@ pub(crate) fn request_freeze_notif( } *info.freeze() = Some(cookie); - let msg = FreezeMessage::init(msg, cookie); + let msg = FreezeMessage::init(msg, cookie, self.task.pid()); drop(node_refs_guard); let _ = self.push_work(msg); Ok(()) @@ -279,7 +287,7 @@ pub(crate) fn freeze_notif_done(self: &Arc, reader: &mut UserSliceReader) }; let mut clear_msg = None; if freeze.num_pending_duplicates > 0 { - clear_msg = Some(FreezeMessage::init(alloc, cookie)); + clear_msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid())); freeze.num_pending_duplicates -= 1; freeze.num_cleared_duplicates += 1; } else { @@ -294,7 +302,7 @@ pub(crate) fn freeze_notif_done(self: &Arc, reader: &mut UserSliceReader) let is_frozen = freeze.node.owner.inner.lock().is_frozen.is_fully_frozen(); if freeze.is_clearing || freeze.last_is_frozen != Some(is_frozen) { // Immediately send another FreezeMessage. - clear_msg = Some(FreezeMessage::init(alloc, cookie)); + clear_msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid())); } freeze.is_pending = false; } @@ -347,7 +355,7 @@ pub(crate) fn clear_freeze_notif(self: &Arc, reader: &mut UserSliceReader) *info.freeze() = None; let mut msg = None; if !listener.is_pending { - msg = Some(FreezeMessage::init(alloc, cookie)); + msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid())); } drop(node_refs_guard); @@ -427,7 +435,7 @@ pub(crate) fn prepare_freeze_messages(&self) -> Result) {} + fn cancel(self: DArc) { + binder_debug!( + pid = self.process.task.pid(), + DeadTransaction, + "undelivered death notification, {:016x}", + self.cookie + ); + } fn should_sync_wakeup(&self) -> bool { false diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index 29829cb210a4..15c7b65928d8 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -221,6 +221,7 @@ fn arc_pin_init(init: impl PinInit) -> Result, kernel::error::Error> struct DeliverCode { code: u32, skip: Atomic, + pid: i32, } kernel::list::impl_list_arc_safe! { @@ -228,10 +229,11 @@ struct DeliverCode { } impl DeliverCode { - fn new(code: u32) -> Self { + fn new(code: u32, pid: i32) -> Self { Self { code, skip: Atomic::new(false), + pid, } } @@ -256,7 +258,15 @@ fn do_work( Ok(true) } - fn cancel(self: DArc) {} + fn cancel(self: DArc) { + if !self.skip.load(Relaxed) { + binder_debug!( + pid = self.pid, + DeadTransaction, + "undelivered TRANSACTION_COMPLETE" + ); + } + } fn should_sync_wakeup(&self) -> bool { false diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 38b90c79c057..edc2613d13b5 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -279,7 +279,7 @@ struct InnerThread { const LOOPER_POLL: u32 = 0x40; impl InnerThread { - fn new() -> Result { + fn new(pid: i32) -> Result { fn next_err_id() -> u32 { static EE_ID: Atomic = Atomic::new(0); EE_ID.fetch_add(1, Relaxed) @@ -290,8 +290,8 @@ fn next_err_id() -> u32 { looper_need_return: false, is_dead: false, process_work_list: false, - reply_work: ThreadError::try_new()?, - return_work: ThreadError::try_new()?, + reply_work: ThreadError::try_new(pid)?, + return_work: ThreadError::try_new(pid)?, work_list: List::new(), current_transaction: None, extended_error: ExtendedError::new(next_err_id(), BR_OK, 0), @@ -445,7 +445,7 @@ impl ListItem<0> for Thread { impl Thread { pub(crate) fn new(id: i32, process: Arc) -> Result> { - let inner = InnerThread::new()?; + let inner = InnerThread::new(process.task.pid())?; Arc::pin_init( try_pin_init!(Thread { @@ -1115,6 +1115,12 @@ fn unwind_transaction_stack(self: &Arc) { let mut inner = thread.inner.lock(); inner.pop_transaction_to_reply(thread.as_ref()) } { + binder_debug!( + DeadTransaction, + "release transaction {} in, still active", + transaction.debug_id + ); + let reply = Err(BR_DEAD_REPLY); if !transaction .from @@ -1305,7 +1311,10 @@ fn transaction_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResu // 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 list_completion = DTRWrap::arc_try_new(DeliverCode::new( + BR_TRANSACTION_COMPLETE, + self.process.task.pid(), + ))?; let completion = list_completion.clone_arc(); let transaction = Transaction::new(node_ref, top, self, info)?; @@ -1357,7 +1366,10 @@ fn reply_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { // 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 completion = DTRWrap::arc_try_new(DeliverCode::new( + BR_TRANSACTION_COMPLETE, + self.process.task.pid(), + ))?; let process = orig.from.process.clone(); let allow_fds = orig.flags & TF_ACCEPT_FDS != 0; let reply = Transaction::new_reply(self, process, info, allow_fds)?; @@ -1397,7 +1409,8 @@ fn oneway_transaction_inner(self: &Arc, info: &mut TransactionInfo) -> Bin } else { BR_TRANSACTION_COMPLETE }; - let list_completion = DTRWrap::arc_try_new(DeliverCode::new(code))?; + let list_completion = + DTRWrap::arc_try_new(DeliverCode::new(code, self.process.task.pid()))?; let completion = list_completion.clone_arc(); self.inner.lock().push_work(list_completion); match transaction.submit(info) { @@ -1653,14 +1666,16 @@ pub(crate) fn release(self: &Arc) { #[pin_data] struct ThreadError { error_code: Atomic, + pid: i32, #[pin] links_track: AtomicTracker, } impl ThreadError { - fn try_new() -> Result> { + fn try_new(pid: i32) -> Result> { DTRWrap::arc_pin_init(pin_init!(Self { error_code: Atomic::new(BR_OK), + pid, links_track <- AtomicTracker::new(), })) .map(ListArc::into_arc) @@ -1687,7 +1702,16 @@ fn do_work( Ok(true) } - fn cancel(self: DArc) {} + fn cancel(self: DArc) { + let code = self.error_code.load(Relaxed); + if code != BR_OK { + binder_debug!( + pid = self.pid, + DeadTransaction, + "undelivered TRANSACTION_ERROR: {code}" + ); + } + } fn should_sync_wakeup(&self) -> bool { false diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 069c792d2200..0528070fe700 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -488,6 +488,13 @@ fn cancel(self: DArc) { if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 { let reply = Err(BR_DEAD_REPLY); self.from.deliver_reply(reply, &self, None); + } else { + binder_debug!( + pid = self.to.task.pid(), + DeadTransaction, + "undelivered transaction {}, process died", + self.debug_id + ); } self.drop_outstanding_txn(); From 2e70c06873c6e23441485d50b8ecac693e7f71c6 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 7 Jul 2026 10:06:49 +0000 Subject: [PATCH 240/513] rust: net: add rust/kernel/net to NETWORKING [GENERAL] To ensure that networking code can be found in a single shared place, add it to the relevant NETWORKING entry. The net.rs file is moved into the net/ directory so that it's included under the MAINTAINERS entry without needing a separate rust/kernel/net.rs entry. Reviewed-by: Carlos Llamas Acked-by: Jakub Kicinski Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260707-binder-netlink-v7-1-42b40e4b1ac8@google.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 2 ++ rust/kernel/{net.rs => net/mod.rs} | 0 2 files changed, 2 insertions(+) rename rust/kernel/{net.rs => net/mod.rs} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 806bd2d80d15..be0af1665945 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18850,6 +18850,8 @@ F: include/uapi/linux/sctp.h F: lib/net_utils.c F: lib/random32.c F: net/ +F: rust/helpers/net/ +F: rust/kernel/net/ F: samples/pktgen/ F: tools/net/ F: tools/testing/selftests/net/ diff --git a/rust/kernel/net.rs b/rust/kernel/net/mod.rs similarity index 100% rename from rust/kernel/net.rs rename to rust/kernel/net/mod.rs From 5eaa5fbb6e6ce6d779fddf7aee42884d60990d40 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 7 Jul 2026 10:06:50 +0000 Subject: [PATCH 241/513] rust: netlink: add raw netlink abstraction This implements a safe and relatively simple API over the netlink API, that allows you to add different attributes to a netlink message and broadcast it. As the first user of this API only makes use of broadcast, only broadcast messages are supported here. This API is intended to be safe and to be easy to use in *generated* code. This is because netlink is generally used with yaml files that describe the underlying API, and the python generator outputs C code (or, soon, Rust code) that lets you use the API more easily. So for example, if there is a string field, the code generator will output a method that internall calls `put_string()` with the right attr type. Reviewed-by: Matthew Maurer Reviewed-by: Andrew Lunn Reviewed-by: Carlos Llamas Acked-by: Jakub Kicinski Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260707-binder-netlink-v7-2-42b40e4b1ac8@google.com Signed-off-by: Greg Kroah-Hartman --- rust/bindings/bindings_helper.h | 3 + rust/helpers/helpers.c | 1 + rust/helpers/net/genetlink.c | 46 +++++ rust/kernel/net/mod.rs | 2 + rust/kernel/net/netlink.rs | 337 ++++++++++++++++++++++++++++++++ 5 files changed, 389 insertions(+) create mode 100644 rust/helpers/net/genetlink.c create mode 100644 rust/kernel/net/netlink.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 1124785e210b..ec96e811610d 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -93,6 +93,8 @@ #include #include #include +#include +#include #include /* @@ -110,6 +112,7 @@ const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN; const size_t RUST_CONST_HELPER_ARCH_KMALLOC_MINALIGN = ARCH_KMALLOC_MINALIGN; const size_t RUST_CONST_HELPER_PAGE_SIZE = PAGE_SIZE; +const size_t RUST_CONST_HELPER_GENLMSG_DEFAULT_SIZE = GENLMSG_DEFAULT_SIZE; const gfp_t RUST_CONST_HELPER_GFP_ATOMIC = GFP_ATOMIC; const gfp_t RUST_CONST_HELPER_GFP_KERNEL = GFP_KERNEL; const gfp_t RUST_CONST_HELPER_GFP_KERNEL_ACCOUNT = GFP_KERNEL_ACCOUNT; diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 998e31052e66..1d4ee51f576b 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -72,6 +72,7 @@ #include "maple_tree.c" #include "mm.c" #include "mutex.c" +#include "net/genetlink.c" #include "of.c" #include "page.c" #include "pci.c" diff --git a/rust/helpers/net/genetlink.c b/rust/helpers/net/genetlink.c new file mode 100644 index 000000000000..3530b69f6cf7 --- /dev/null +++ b/rust/helpers/net/genetlink.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (C) 2026 Google LLC. + */ + +#include + +#ifdef CONFIG_NET + +__rust_helper struct sk_buff *rust_helper_genlmsg_new(size_t payload, gfp_t flags) +{ + return genlmsg_new(payload, flags); +} + +__rust_helper +int rust_helper_genlmsg_multicast(const struct genl_family *family, + struct sk_buff *skb, u32 portid, + unsigned int group, gfp_t flags) +{ + return genlmsg_multicast(family, skb, portid, group, flags); +} + +__rust_helper void rust_helper_genlmsg_cancel(struct sk_buff *skb, void *hdr) +{ + genlmsg_cancel(skb, hdr); +} + +__rust_helper void rust_helper_genlmsg_end(struct sk_buff *skb, void *hdr) +{ + genlmsg_end(skb, hdr); +} + +__rust_helper void rust_helper_nlmsg_free(struct sk_buff *skb) +{ + nlmsg_free(skb); +} + +__rust_helper +int rust_helper_genl_has_listeners(const struct genl_family *family, + struct net *net, unsigned int group) +{ + return genl_has_listeners(family, net, group); +} + +#endif diff --git a/rust/kernel/net/mod.rs b/rust/kernel/net/mod.rs index fe415cb369d3..8ecae7577ed2 100644 --- a/rust/kernel/net/mod.rs +++ b/rust/kernel/net/mod.rs @@ -4,3 +4,5 @@ #[cfg(CONFIG_RUST_PHYLIB_ABSTRACTIONS)] pub mod phy; + +pub mod netlink; diff --git a/rust/kernel/net/netlink.rs b/rust/kernel/net/netlink.rs new file mode 100644 index 000000000000..22ef3dde36fa --- /dev/null +++ b/rust/kernel/net/netlink.rs @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2026 Google LLC. + +//! Rust support for generic netlink. +//! +//! Currently only supports exposing multicast groups. +//! +//! C header: [`include/net/genetlink.h`](srctree/include/net/genetlink.h) + +use kernel::{ + alloc::{self, AllocError}, + error::to_result, + prelude::*, + transmute::AsBytes, + types::Opaque, + ThisModule, +}; + +use core::{ + mem::ManuallyDrop, + ptr::NonNull, // +}; + +/// The default netlink message size. +pub const GENLMSG_DEFAULT_SIZE: usize = bindings::GENLMSG_DEFAULT_SIZE; + +/// A wrapper around `struct sk_buff` for generic netlink messages. +/// +/// This type is intended to be specific for buffers used with netlink only, and other usecases for +/// `struct sk_buff` are out-of-scope for this abstraction. +/// +/// # Invariants +/// +/// The pointer has ownership over a valid `sk_buff`. +pub struct NetlinkSkBuff { + skb: NonNull, +} + +impl NetlinkSkBuff { + /// Creates a new `NetlinkSkBuff` with the given size. + pub fn new(size: usize, flags: alloc::Flags) -> Result { + // SAFETY: `genlmsg_new` only requires its arguments to be valid integers. + let skb = unsafe { bindings::genlmsg_new(size, flags.as_raw()) }; + let skb = NonNull::new(skb).ok_or(AllocError)?; + Ok(NetlinkSkBuff { skb }) + } + + /// Puts a generic netlink header into the `NetlinkSkBuff`. + pub fn genlmsg_put( + self, + portid: u32, + seq: u32, + family: &'static Family, + cmd: u8, + ) -> Result { + let skb = self.skb.as_ptr(); + // SAFETY: The skb and family pointers are valid. + let hdr = unsafe { bindings::genlmsg_put(skb, portid, seq, family.as_raw(), 0, cmd) }; + let hdr = NonNull::new(hdr).ok_or(AllocError)?; + Ok(GenlMsg { skb: self, hdr }) + } +} + +impl Drop for NetlinkSkBuff { + fn drop(&mut self) { + // SAFETY: We have ownership over the `sk_buff`, so we may free it. + unsafe { bindings::nlmsg_free(self.skb.as_ptr()) } + } +} + +/// A generic netlink message being constructed. +/// +/// # Invariants +/// +/// `hdr` references the header in this netlink message. +pub struct GenlMsg { + skb: NetlinkSkBuff, + hdr: NonNull, +} + +impl GenlMsg { + /// Puts an attribute into the message. + #[inline] + fn put(&mut self, attrtype: c_int, value: &T) -> Result + where + T: ?Sized + AsBytes, + { + let skb = self.skb.skb.as_ptr(); + let len = size_of_val(value); + let ptr = core::ptr::from_ref(value).cast::(); + // SAFETY: `skb` is valid by `NetlinkSkBuff` type invariants, and the provided value is + // readable and initialized for its `size_of` bytes. + to_result(unsafe { bindings::nla_put(skb, attrtype, len as c_int, ptr) }) + } + + /// Puts a `u32` attribute into the message. + #[inline] + pub fn put_u32(&mut self, attrtype: c_int, value: u32) -> Result { + self.put(attrtype, &value) + } + + /// Puts a string attribute into the message. + #[inline] + pub fn put_string(&mut self, attrtype: c_int, value: &CStr) -> Result { + self.put(attrtype, value.to_bytes_with_nul()) + } + + /// Puts a flag attribute into the message. + #[inline] + pub fn put_flag(&mut self, attrtype: c_int) -> Result { + let skb = self.skb.skb.as_ptr(); + // SAFETY: `skb` is valid by `NetlinkSkBuff` type invariants, and a null pointer is valid + // when the length is zero. + to_result(unsafe { bindings::nla_put(skb, attrtype, 0, core::ptr::null()) }) + } + + /// Sends the generic netlink message as a multicast message. + #[inline] + pub fn multicast( + self, + family: &'static Family, + portid: u32, + group: u32, + flags: alloc::Flags, + ) -> Result { + let me = ManuallyDrop::new(self); + // SAFETY: The `skb` and `family` pointers are valid. We pass ownership of the `skb` to + // `genlmsg_multicast` by not dropping `self`. + unsafe { + bindings::genlmsg_end(me.skb.skb.as_ptr(), me.hdr.as_ptr()); + to_result(bindings::genlmsg_multicast( + family.as_raw(), + me.skb.skb.as_ptr(), + portid, + group, + flags.as_raw(), + )) + } + } +} +impl Drop for GenlMsg { + fn drop(&mut self) { + // SAFETY: The `hdr` pointer references the header of this generic netlink message. + unsafe { bindings::genlmsg_cancel(self.skb.skb.as_ptr(), self.hdr.as_ptr()) }; + } +} + +/// Flags for a generic netlink family. +struct FamilyFlags { + /// Whether the family supports network namespaces. + netnsok: bool, + /// Whether the family supports parallel operations. + parallel_ops: bool, +} + +impl FamilyFlags { + /// Converts the flags to the bitfield representation used by `genl_family`. + const fn into_bitfield(self) -> bindings::__BindgenBitfieldUnit<[u8; 1]> { + // The below shifts are verified correct by test_family_flags_bitfield() below. + // + // Although bindgen generates helpers to change bitfields based on the C headers, these + // helpers unfortunately can't be used in const context. Since `Family` needs to be filled + // out at build-time, we use this helper instead. + let mut bits = 0; + if self.netnsok { + bits |= 1 << 0; + } + if self.parallel_ops { + bits |= 1 << 1; + } + // Convert from little endian to the target's endianness. + bits = u8::from_le(bits); + // SAFETY: This bitfield is represented as an u8. + unsafe { core::mem::transmute::>(bits) } + } +} + +/// A generic netlink family. +#[repr(transparent)] +pub struct Family { + inner: Opaque, +} + +// SAFETY: The `Family` type is thread safe. +unsafe impl Sync for Family {} + +impl Family { + /// Creates a new `Family` instance. + /// + /// Intended to be used from const context only. Will panic if provided with invalid arguments. + /// + /// The name must be a nul-terminated string, but it is taken as `&[u8]` so that it can be used + /// more conveniently with the strings generated by bindgen. + pub const fn const_new( + module: &ThisModule, + name: &[u8], + version: u32, + mcgrps: &'static [MulticastGroup], + ) -> Family { + let n_mcgrps = mcgrps.len() as u8; + if n_mcgrps as usize != mcgrps.len() { + panic!("too many mcgrps"); + } + let mut genl_family = bindings::genl_family { + version, + _bitfield_1: FamilyFlags { + netnsok: true, + parallel_ops: true, + } + .into_bitfield(), + module: module.as_ptr(), + mcgrps: mcgrps.as_ptr().cast(), + n_mcgrps, + ..pin_init::zeroed() + }; + if CStr::from_bytes_with_nul(name).is_err() { + panic!("genl_family name not nul-terminated"); + } + if genl_family.name.len() < name.len() { + panic!("genl_family name too long"); + } + let mut i = 0; + while i < name.len() { + genl_family.name[i] = name[i]; + i += 1; + } + Family { + inner: Opaque::new(genl_family), + } + } + + /// Checks if there are any listeners for the given multicast group. + pub fn has_listeners(&self, group: u32) -> bool { + // SAFETY: The family and init_net pointers are valid. + unsafe { + bindings::genl_has_listeners(self.as_raw(), &raw mut bindings::init_net, group) != 0 + } + } + + /// Returns a raw pointer to the underlying `genl_family` structure. + pub fn as_raw(&self) -> *mut bindings::genl_family { + self.inner.get() + } +} + +/// A generic netlink multicast group. +#[repr(transparent)] +pub struct MulticastGroup { + // No Opaque because fully immutable + group: bindings::genl_multicast_group, +} + +// SAFETY: Pure data so thread safe. +unsafe impl Sync for MulticastGroup {} + +impl MulticastGroup { + /// Creates a new `MulticastGroup` instance. + /// + /// Intended to be used from const context only. Will panic if provided with invalid arguments. + pub const fn const_new(name: &CStr) -> MulticastGroup { + let mut group: bindings::genl_multicast_group = pin_init::zeroed(); + + let name = name.to_bytes_with_nul(); + if group.name.len() < name.len() { + panic!("genl_multicast_group name too long"); + } + let mut i = 0; + while i < name.len() { + group.name[i] = name[i]; + i += 1; + } + + MulticastGroup { group } + } +} + +/// A registration of a generic netlink family. +/// +/// This type represents the registration of a [`Family`]. When an instance of this type is +/// dropped, its respective generic netlink family will be unregistered from the system. +/// +/// # Invariants +/// +/// `self.family` always holds a valid reference to an initialized and registered [`Family`]. +pub struct Registration { + family: &'static Family, +} + +impl Family { + /// Registers the generic netlink family with the kernel. + pub fn register(&'static self) -> Result { + // SAFETY: `self.as_raw()` is a valid pointer to a `genl_family` struct. + // The `genl_family` struct is static, so it will outlive the registration. + to_result(unsafe { bindings::genl_register_family(self.as_raw()) })?; + Ok(Registration { family: self }) + } +} + +impl Drop for Registration { + fn drop(&mut self) { + // SAFETY: `self.family.as_raw()` is a valid pointer to a registered `genl_family` struct. + // The `Registration` struct ensures that `genl_unregister_family` is called exactly once + // for this family when it goes out of scope. + unsafe { bindings::genl_unregister_family(self.family.as_raw()) }; + } +} + +#[macros::kunit_tests(rust_netlink)] +mod tests { + use super::*; + + #[test] + fn test_family_flags_bitfield() { + for netnsok in [false, true] { + for parallel_ops in [false, true] { + let mut b_fam = bindings::genl_family { + ..Default::default() + }; + b_fam.set_netnsok(if netnsok { 1 } else { 0 }); + b_fam.set_parallel_ops(if parallel_ops { 1 } else { 0 }); + + let c_bitfield = FamilyFlags { + netnsok, + parallel_ops, + } + .into_bitfield(); + + // SAFETY: The bit field is stored as u8. + let b_val: u8 = unsafe { core::mem::transmute(b_fam._bitfield_1) }; + // SAFETY: The bit field is stored as u8. + let c_val: u8 = unsafe { core::mem::transmute(c_bitfield) }; + assert_eq!(b_val, c_val); + } + } + } +} From f14e0c8183bc73c5ca0ad93670b5b74c71d86df2 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Tue, 7 Jul 2026 10:06:51 +0000 Subject: [PATCH 242/513] rust_binder: report netlink transactions The Android Binder driver supports a netlink API that reports transaction *failures* to a userspace daemon. This allows devices to monitor processes with many failed transactions so that it can e.g. kill misbehaving apps. One very important thing that this monitors is when many oneway messages are sent to a frozen process, so there is special handling to ensure this scenario is surfaced over netlink. Signed-off-by: Carlos Llamas Acked-by: Carlos Llamas Co-developed-by: Alice Ryhl Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260707-binder-netlink-v7-3-42b40e4b1ac8@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/Kconfig | 2 +- drivers/android/binder/netlink.rs | 117 +++++++++++++++++++++ drivers/android/binder/rust_binder_main.rs | 8 +- drivers/android/binder/thread.rs | 11 +- drivers/android/binder/transaction.rs | 40 +++++++ rust/uapi/uapi_helper.h | 1 + 6 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 drivers/android/binder/netlink.rs diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig index e2e402c9d175..606a9d07f774 100644 --- a/drivers/android/Kconfig +++ b/drivers/android/Kconfig @@ -16,7 +16,7 @@ config ANDROID_BINDER_IPC config ANDROID_BINDER_IPC_RUST bool "Rust version of Android Binder IPC Driver" - depends on RUST && MMU && !ANDROID_BINDER_IPC + depends on RUST && MMU && NET && !ANDROID_BINDER_IPC help This enables the Rust implementation of the Binder driver. diff --git a/drivers/android/binder/netlink.rs b/drivers/android/binder/netlink.rs new file mode 100644 index 000000000000..beb7ea2edaff --- /dev/null +++ b/drivers/android/binder/netlink.rs @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) +/* Based on: Documentation/netlink/specs/binder.yaml */ + +#![allow(unreachable_pub, clippy::wrong_self_convention)] +use kernel::{ + net::netlink::{ + Family, + GenlMsg, + MulticastGroup, + NetlinkSkBuff, // + }, + prelude::*, // +}; + +pub static BINDER_NL_FAMILY: Family = Family::const_new( + &crate::THIS_MODULE, + kernel::uapi::BINDER_FAMILY_NAME, + kernel::uapi::BINDER_FAMILY_VERSION, + &BINDER_NL_FAMILY_MCGRPS, +); + +static BINDER_NL_FAMILY_MCGRPS: [MulticastGroup; 1] = [MulticastGroup::const_new(c"report")]; + +/// A multicast event sent to userspace subscribers to notify them about +/// binder transaction failures. The generated report provides the full +/// details of the specific transaction that failed. The intention is for +/// programs to monitor these events and react to the failures as needed. +pub struct Report { + skb: GenlMsg, +} + +impl Report { + /// Create a new multicast message. + pub fn new( + size: usize, + portid: u32, + seq: u32, + flags: kernel::alloc::Flags, + ) -> Result { + const BINDER_CMD_REPORT: u8 = kernel::uapi::BINDER_CMD_REPORT as u8; + let skb = NetlinkSkBuff::new(size, flags)?; + let skb = skb.genlmsg_put(portid, seq, &BINDER_NL_FAMILY, BINDER_CMD_REPORT)?; + Ok(Self { skb }) + } + + /// Broadcast this message. + pub fn multicast(self, portid: u32, flags: kernel::alloc::Flags) -> Result { + self.skb.multicast(&BINDER_NL_FAMILY, portid, 0, flags) + } + + /// Check if this message type has listeners. + pub fn has_listeners() -> bool { + BINDER_NL_FAMILY.has_listeners(0) + } + + /// The enum binder_driver_return_protocol returned to the sender. + pub fn error(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_ERROR: c_int = kernel::uapi::BINDER_A_REPORT_ERROR as c_int; + self.skb.put_u32(BINDER_A_REPORT_ERROR, val) + } + + /// The binder context where the transaction occurred. + pub fn context(&mut self, val: &CStr) -> Result { + const BINDER_A_REPORT_CONTEXT: c_int = kernel::uapi::BINDER_A_REPORT_CONTEXT as c_int; + self.skb.put_string(BINDER_A_REPORT_CONTEXT, val) + } + + /// The PID of the sender process. + pub fn from_pid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FROM_PID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_PID as c_int; + self.skb.put_u32(BINDER_A_REPORT_FROM_PID, val) + } + + /// The TID of the sender thread. + pub fn from_tid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FROM_TID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_TID as c_int; + self.skb.put_u32(BINDER_A_REPORT_FROM_TID, val) + } + + /// The PID of the recipient process. This attribute may not be present + /// if the target could not be determined. + pub fn to_pid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_TO_PID: c_int = kernel::uapi::BINDER_A_REPORT_TO_PID as c_int; + self.skb.put_u32(BINDER_A_REPORT_TO_PID, val) + } + + /// The TID of the recipient thread. This attribute may not be present + /// if the target could not be determined. + pub fn to_tid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_TO_TID: c_int = kernel::uapi::BINDER_A_REPORT_TO_TID as c_int; + self.skb.put_u32(BINDER_A_REPORT_TO_TID, val) + } + + /// When present, indicates the failed transaction is a reply. + pub fn is_reply(&mut self) -> Result { + const BINDER_A_REPORT_IS_REPLY: c_int = kernel::uapi::BINDER_A_REPORT_IS_REPLY as c_int; + self.skb.put_flag(BINDER_A_REPORT_IS_REPLY) + } + + /// The bitmask of enum transaction_flags from the transaction. + pub fn flags(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FLAGS: c_int = kernel::uapi::BINDER_A_REPORT_FLAGS as c_int; + self.skb.put_u32(BINDER_A_REPORT_FLAGS, val) + } + + /// The application-defined code from the transaction. + pub fn code(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_CODE: c_int = kernel::uapi::BINDER_A_REPORT_CODE as c_int; + self.skb.put_u32(BINDER_A_REPORT_CODE, val) + } + + /// The transaction payload size in bytes. + pub fn data_size(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_DATA_SIZE: c_int = kernel::uapi::BINDER_A_REPORT_DATA_SIZE as c_int; + self.skb.put_u32(BINDER_A_REPORT_DATA_SIZE, val) + } +} diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index 15c7b65928d8..9e6cda960722 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -34,6 +34,7 @@ #[macro_use] mod debug; mod error; +mod netlink; mod node; mod page_range; mod process; @@ -294,19 +295,22 @@ fn ptr_align(value: usize) -> Option { // SAFETY: We call register in `init`. static BINDER_SHRINKER: Shrinker = unsafe { Shrinker::new() }; -struct BinderModule {} +struct BinderModule { + _netlink: kernel::net::netlink::Registration, +} impl kernel::Module for BinderModule { 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() }; + let netlink = crate::netlink::BINDER_NL_FAMILY.register()?; BINDER_SHRINKER.register(c"android-binder")?; // SAFETY: The module is being loaded, so we can initialize binderfs. unsafe { kernel::error::to_result(binderfs::init_rust_binderfs())? }; - Ok(Self {}) + Ok(Self { _netlink: netlink }) } } diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index edc2613d13b5..a7a190e1b000 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -1301,6 +1301,15 @@ fn transaction(self: &Arc, cmd: u32, reader: &mut UserSliceReader) -> Resu } } + if info.oneway_spam_suspect { + // If this is both a oneway spam suspect and a failure, we report it twice. This is + // useful in case the transaction failed with BR_TRANSACTION_PENDING_FROZEN. + info.report_netlink(BR_ONEWAY_SPAM_SUSPECT, &self.process.ctx); + } + if info.reply != 0 { + info.report_netlink(info.reply, &self.process.ctx); + } + Ok(()) } @@ -1387,11 +1396,11 @@ fn reply_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { info.from_tid, info.to_pid ); - let param = err.source.as_ref().map_or(0, |e| e.to_errno()); let ee = ExtendedError::new(info.debug_id as u32, err.reply, param); orig.from .deliver_reply(Err(BR_FAILED_REPLY), &orig, Some(ee)); + info.reply = BR_FAILED_REPLY; err.reply = BR_TRANSACTION_COMPLETE; err }); diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 0528070fe700..96d45c6816fe 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -3,6 +3,7 @@ // Copyright (C) 2025 Google LLC. use kernel::{ + net::netlink::GENLMSG_DEFAULT_SIZE, prelude::*, seq_file::SeqFile, seq_print, @@ -18,6 +19,7 @@ allocation::{Allocation, TranslatedFds}, defs::*, error::{BinderError, BinderResult}, + netlink::Report, node::{Node, NodeRef}, process::{Process, ProcessInner}, ptr_align, @@ -51,6 +53,44 @@ impl TransactionInfo { pub(crate) fn is_oneway(&self) -> bool { self.flags & TF_ONE_WAY != 0 } + + pub(crate) fn report_netlink(&self, reply: u32, ctx: &crate::Context) { + if let Err(err) = self.report_netlink_inner(reply, ctx) { + pr_warn!( + "{}:{} netlink report failed: {err:?}\n", + self.from_pid, + self.from_tid + ); + } + } + + fn report_netlink_inner(&self, reply: u32, ctx: &crate::Context) -> kernel::error::Result { + if !Report::has_listeners() { + return Ok(()); + } + let mut report = Report::new(GENLMSG_DEFAULT_SIZE, 0, 0, GFP_KERNEL)?; + + report.error(reply)?; + report.context(&ctx.name)?; + report.from_pid(self.from_pid as u32)?; + report.from_tid(self.from_tid as u32)?; + if self.to_pid != 0 { + report.to_pid(self.to_pid as u32)?; + } + if self.to_tid != 0 { + report.to_tid(self.to_tid as u32)?; + } + + if self.is_reply { + report.is_reply()?; + } + report.flags(self.flags)?; + report.code(self.code)?; + report.data_size(self.data_size as u32)?; + + report.multicast(0, GFP_KERNEL)?; + Ok(()) + } } use core::mem::offset_of; diff --git a/rust/uapi/uapi_helper.h b/rust/uapi/uapi_helper.h index 06d7d1a2e8da..86c7b6b284b0 100644 --- a/rust/uapi/uapi_helper.h +++ b/rust/uapi/uapi_helper.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include From e5e86df8b666152bc99fab4be0906b92a271964d Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 7 Jul 2026 10:43:12 +0000 Subject: [PATCH 243/513] rust: poll: use kfree_rcu() for PollCondVar Rust Binder currently uses PollCondVar, but it calls synchronize_rcu() in the destructor, which we would like to avoid. Add a variation of PollCondVar that kfree_rcu() instead. One could avoid the `rcu` field and allocate the rcu_head on drop using a fallback to synchronize_rcu() on ENOMEM. However, I'd prefer to avoid the potential for synchronize_rcu(), and Binder will only use this for a small fraction of processes, so even if it changes which kmalloc bucket it falls into, the extra memory is not a problem. Signed-off-by: Alice Ryhl Reviewed-by: Boqun Feng Link: https://patch.msgid.link/20260707-upgrade-poll-v6-1-4b8fae7bf1d9@google.com Signed-off-by: Greg Kroah-Hartman --- rust/kernel/sync/poll.rs | 73 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/rust/kernel/sync/poll.rs b/rust/kernel/sync/poll.rs index 0ec985d560c8..684dfa242b1a 100644 --- a/rust/kernel/sync/poll.rs +++ b/rust/kernel/sync/poll.rs @@ -5,12 +5,18 @@ //! Utilities for working with `struct poll_table`. use crate::{ + alloc::AllocError, bindings, fs::File, prelude::*, sync::{CondVar, LockClassKey}, + types::Opaque, // +}; +use core::{ + marker::PhantomData, + mem::ManuallyDrop, + ops::Deref, // }; -use core::{marker::PhantomData, ops::Deref}; /// Creates a [`PollCondVar`] initialiser with the given name and a newly-created lock class. #[macro_export] @@ -66,6 +72,7 @@ pub fn register_wait(&self, file: &File, cv: &PollCondVar) { /// /// [`CondVar`]: crate::sync::CondVar #[pin_data(PinnedDrop)] +#[repr(transparent)] pub struct PollCondVar { #[pin] inner: CondVar, @@ -104,3 +111,67 @@ fn drop(self: Pin<&mut Self>) { unsafe { bindings::synchronize_rcu() }; } } + +/// A [`KBox`] that uses `kfree_rcu`. +/// +/// [`KBox`]: PollCondVar +pub struct PollCondVarBox { + inner: ManuallyDrop>>, +} + +#[pin_data] +#[repr(C)] +struct PollCondVarBoxInner { + #[pin] + inner: PollCondVar, + rcu: Opaque, +} + +// SAFETY: PollCondVar is Send +unsafe impl Send for PollCondVarBoxInner {} +// SAFETY: PollCondVar is Sync +unsafe impl Sync for PollCondVarBoxInner {} + +impl PollCondVarBox { + /// Constructs a new boxed [`PollCondVar`]. + pub fn new(name: &'static CStr, key: Pin<&'static LockClassKey>) -> Result { + let b = KBox::pin_init( + pin_init!(PollCondVarBoxInner { + inner <- PollCondVar::new(name, key), + rcu: Opaque::uninit(), + }), + GFP_KERNEL, + ) + .map_err(|_| AllocError)?; + + Ok(PollCondVarBox { + inner: ManuallyDrop::new(b), + }) + } +} + +impl Deref for PollCondVarBox { + type Target = PollCondVar; + fn deref(&self) -> &PollCondVar { + &self.inner.inner + } +} + +impl Drop for PollCondVarBox { + #[inline] + fn drop(&mut self) { + // SAFETY: ManuallyDrop::take ok because not already taken. + let boxed = unsafe { ManuallyDrop::take(&mut self.inner) }; + + // SAFETY: The code below frees the box without calling the actual destructor of the type, + // but it's okay because it re-implements the destructor using `kfree_rcu()` in place of + // `synchronize_rcu()`. + let ptr = KBox::into_raw(unsafe { Pin::into_inner_unchecked(boxed) }); + + // SAFETY: The pointer points at a valid `wait_queue_head`. + unsafe { bindings::__wake_up_pollfree((*ptr).inner.inner.wait_queue_head.get()) }; + + // SAFETY: This was allocated using `KBox::pin_init`, so it can be freed with `kvfree`. + unsafe { bindings::kvfree_call_rcu((*ptr).rcu.get(), ptr.cast::()) }; + } +} From dbb17c9ea7567c6ecefe47104cfcc255b91e4089 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 7 Jul 2026 10:43:13 +0000 Subject: [PATCH 244/513] rust_binder: move (e)poll wait queue to Process Most processes do not use Rust Binder with epoll, so avoid paying the synchronize_rcu() cost in drop for those that don't need it. For those that do, we also manage to replace synchronize_rcu() with kfree_rcu(), though we introduce an extra allocation. In case the last ref to an Arc is dropped outside of deferred_release(), this also ensures that synchronize_rcu() is not called in destructor of Arc in other places. Theoretically that could lead to jank by making other syscalls slow, which would be problematic. Signed-off-by: Alice Ryhl Reviewed-by: Boqun Feng Link: https://patch.msgid.link/20260707-upgrade-poll-v6-2-4b8fae7bf1d9@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/node.rs | 4 +- drivers/android/binder/process.rs | 67 ++++++++++++++++------- drivers/android/binder/thread.rs | 78 +++++++++++++-------------- drivers/android/binder/transaction.rs | 6 ++- 4 files changed, 95 insertions(+), 60 deletions(-) diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs index c73cdf82100f..b74ef32b0d94 100644 --- a/drivers/android/binder/node.rs +++ b/drivers/android/binder/node.rs @@ -538,7 +538,7 @@ pub(crate) fn submit_oneway( inner.oneway_todo.push_back(transaction); } else { inner.has_oneway_transaction = true; - guard.push_work(transaction)?; + guard.push_work(&self.owner, transaction)?; } Ok(()) } @@ -570,7 +570,7 @@ pub(crate) fn pending_oneway_finished(&self) { let transaction = inner.oneway_todo.pop_front(); inner.has_oneway_transaction = transaction.is_some(); if let Some(transaction) = transaction { - match guard.push_work(transaction) { + match guard.push_work(&self.owner, transaction) { Ok(()) => {} Err((_err, work)) => { // Process is dead. diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index eb2f08bec655..962f38453252 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -30,7 +30,8 @@ sync::{ aref::ARef, lock::{spinlock::SpinLockBackend, Guard}, - Arc, ArcBorrow, CondVar, CondVarTimeoutResult, SpinLock, UniqueArc, + poll::PollCondVarBox, + Arc, ArcBorrow, CondVar, CondVarTimeoutResult, SetOnce, SpinLock, UniqueArc, }, task::{Pid, Task}, uaccess::{UserSlice, UserSliceReader}, @@ -172,21 +173,26 @@ fn new() -> Self { /// taken while holding the inner process lock. pub(crate) fn push_work( &mut self, + proc: &Process, work: DLArc, ) -> Result<(), (BinderError, DLArc)> { + let sync = work.should_sync_wakeup(); + // Try to find a ready thread to which to push the work. if let Some(thread) = self.ready_threads.pop_front() { // Push to thread while holding state lock. This prevents the thread from giving up // (for example, because of a signal) when we're about to deliver work. - match thread.push_work(work) { + match thread.push_work_inner(work, sync) { PushWorkRes::Ok => Ok(()), + PushWorkRes::OkNotifyPoll => { + proc.notify_poll(sync); + Ok(()) + } PushWorkRes::FailedDead(work) => Err((BinderError::new_dead(), work)), } } else if self.is_dead { Err((BinderError::new_dead(), work)) } else { - let sync = work.should_sync_wakeup(); - // Didn't find a thread waiting for proc work; this can happen // in two scenarios: // 1. All threads are busy handling transactions @@ -194,17 +200,12 @@ pub(crate) fn push_work( // the kernel driver soon and pick up this work. // 2. Threads are using the (e)poll interface, in which case // they may be blocked on the waitqueue without having been - // added to waiting_threads. For this case, we just iterate - // over all threads not handling transaction work, and - // wake them all up. We wake all because we don't know whether - // a thread that called into (e)poll is handling non-binder - // work currently. + // added to waiting_threads. For this case, we wake it up + // directly. self.work.push_back(work); // Wake up polling threads, if any. - for thread in self.threads.values() { - thread.notify_if_poll_ready(sync); - } + proc.notify_poll(sync); Ok(()) } @@ -227,11 +228,11 @@ pub(crate) fn update_node_refcount( // If we decided that we need to push work, push either to the process or to a thread if // one is specified. - if let Some(node) = push { + if let Some(pnode) = push { if let Some(thread) = othread { - thread.push_work_deferred(node); + thread.push_work_deferred(pnode); } else { - let _ = self.push_work(node); + let _ = self.push_work(&node.owner, pnode); // Nothing to do: `push_work` may fail if the process is dead, but that's ok as in // that case, it doesn't care about the notification. } @@ -457,6 +458,12 @@ pub(crate) struct Process { #[pin] node_refs: SpinLock, + // Synchronizes `register_wait` calls to the `PollCondVarBox`. + // + // The `PollCondVarBox` is not stored here because synchronization is + // done for `register_wait` only. Wakeups do not take this lock. + poll: SetOnce, + // Work node for deferred work item. #[pin] defer_work: Work, @@ -516,6 +523,7 @@ fn new(ctx: Arc, cred: ARef) -> Result> { defer_work <- kernel::new_work!("Process::defer_work"), links <- ListLinks::new(), stats: BinderStats::new(), + poll: SetOnce::new(), }), GFP_KERNEL, )?; @@ -715,7 +723,7 @@ fn get_current_thread(self: ArcBorrow<'_, Self>) -> Result> { pub(crate) fn push_work(&self, work: DLArc) -> BinderResult { // If push_work fails, drop the work item outside the lock. - let res = self.inner.lock().push_work(work); + let res = self.inner.lock().push_work(self, work); match res { Ok(()) => Ok(()), Err((err, work)) => { @@ -1024,7 +1032,7 @@ pub(crate) fn inc_ref_done(&self, reader: &mut UserSliceReader, strong: bool) -> if let Ok(Some(node)) = inner.get_existing_node(ptr, cookie) { if let Some(node) = node.inc_ref_done_locked(strong, &mut inner) { // This only fails if the process is dead. - let _ = inner.push_work(node); + let _ = inner.push_work(self, node); } } Ok(()) @@ -1573,6 +1581,15 @@ pub(crate) fn ioctl_freeze(&self, info: &BinderFreezeInfo) -> Result { } } } + + pub(crate) fn notify_poll(&self, sync: bool) { + if let Some(poll) = self.poll.as_ref() { + if sync { + poll.notify_sync(); + } + poll.notify_all(); + } + } } fn get_frozen_status(data: UserSlice) -> Result { @@ -1766,7 +1783,21 @@ pub(crate) fn poll( table: PollTable<'_>, ) -> Result { let thread = this.get_current_thread()?; - let (from_proc, mut mask) = thread.poll(file, table); + { + let poll = loop { + if let Some(poll) = this.poll.as_ref() { + break poll; + } + + let poll = PollCondVarBox::new(c"Process::poll", kernel::static_lock_class!())?; + // Reuse our existing lock to synchronize callers initializing. + let _guard = this.node_refs.lock(); + this.poll.populate(poll); + }; + + table.register_wait(file, poll); + } + let (from_proc, mut mask) = thread.poll()?; if mask == 0 && from_proc && !this.inner.lock().work.is_empty() { mask |= bindings::POLLIN; } diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index a7a190e1b000..a51821dde0ad 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -9,15 +9,14 @@ use kernel::{ bindings, - fs::{File, LocalFile}, + fs::LocalFile, list::{AtomicTracker, List, ListArc, ListLinks, TryNewListArc}, prelude::*, security, seq_file::SeqFile, seq_print, sync::atomic::{ordering::Relaxed, Atomic}, - sync::poll::{PollCondVar, PollTable}, - sync::{aref::ARef, Arc, SpinLock}, + sync::{aref::ARef, Arc, CondVar, SpinLock}, task::Task, uaccess::{UserPtr, UserSlice, UserSliceReader}, uapi, @@ -225,8 +224,10 @@ fn claim_next(&mut self, size: usize) -> Result { } } +#[must_use] pub(crate) enum PushWorkRes { Ok, + OkNotifyPoll, FailedDead(DLArc), } @@ -234,6 +235,7 @@ impl PushWorkRes { fn is_ok(&self) -> bool { match self { PushWorkRes::Ok => true, + PushWorkRes::OkNotifyPoll => true, PushWorkRes::FailedDead(_) => false, } } @@ -310,27 +312,32 @@ fn pop_work(&mut self) -> Option> { fn push_work(&mut self, work: DLArc) -> PushWorkRes { if self.is_dead { - PushWorkRes::FailedDead(work) + return PushWorkRes::FailedDead(work); + } + self.work_list.push_back(work); + self.process_work_list = true; + if self.looper_flags & LOOPER_POLL != 0 { + PushWorkRes::OkNotifyPoll } else { - self.work_list.push_back(work); - self.process_work_list = true; PushWorkRes::Ok } } - fn push_reply_work(&mut self, code: u32) { + fn push_reply_work(&mut self, code: u32) -> PushWorkRes { if let Ok(work) = ListArc::try_from_arc(self.reply_work.clone()) { work.set_error_code(code); - self.push_work(work); + self.push_work(work) } else { pr_warn!("Thread reply work is already in use."); + PushWorkRes::Ok } } fn push_return_work(&mut self, reply: u32) { if let Ok(work) = ListArc::try_from_arc(self.return_work.clone()) { work.set_error_code(reply); - self.push_work(work); + // Not notifying: Reply to current thread. + let _ = self.push_work(work); } else { pr_warn!("Thread return work is already in use."); } @@ -422,7 +429,7 @@ pub(crate) struct Thread { #[pin] inner: SpinLock, #[pin] - work_condvar: PollCondVar, + work_condvar: CondVar, /// Used to insert this thread into the process' `ready_threads` list. /// /// INVARIANT: May never be used for any other list than the `self.process.ready_threads`. @@ -453,7 +460,7 @@ pub(crate) fn new(id: i32, process: Arc) -> Result> { process, task: ARef::from(&**kernel::current!()), inner <- kernel::new_spinlock!(inner, "Thread::inner"), - work_condvar <- kernel::new_poll_condvar!("Thread::work_condvar"), + work_condvar <- kernel::new_condvar!("Thread::work_condvar"), links <- ListLinks::new(), links_track <- AtomicTracker::new(), }), @@ -624,7 +631,14 @@ fn get_work(self: &Arc, wait: bool) -> Result) -> PushWorkRes { let sync = work.should_sync_wakeup(); + self.push_work_inner(work, sync) + } + pub(crate) fn push_work_inner( + &self, + work: DLArc, + sync: bool, + ) -> PushWorkRes { let res = self.inner.lock().push_work(work); if res.is_ok() { @@ -643,7 +657,8 @@ pub(crate) fn push_work(&self, work: DLArc) -> PushWorkRes { pub(crate) fn push_work_if_looper(&self, work: DLArc) -> BinderResult { let mut inner = self.inner.lock(); if inner.is_looper() && !inner.is_dead { - inner.push_work(work); + // Not notifying: Reply to current thread. + let _ = inner.push_work(work); Ok(()) } else { drop(inner); @@ -1160,7 +1175,7 @@ fn deliver_single_reply( transaction.set_outstanding(&mut self.process.inner.lock()); } - { + let ret = { let mut inner = self.inner.lock(); if !inner.pop_transaction_replied(transaction) { return false; @@ -1177,15 +1192,16 @@ fn deliver_single_reply( } match reply { - Ok(work) => { - inner.push_work(work); - } + Ok(work) => inner.push_work(work), Err(code) => inner.push_reply_work(code), } - } + }; // Notify the thread now that we've released the inner lock. self.work_condvar.notify_sync(); + if matches!(ret, PushWorkRes::OkNotifyPoll) { + self.process.notify_poll(true); + } false } @@ -1382,7 +1398,8 @@ fn reply_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { let process = orig.from.process.clone(); let allow_fds = orig.flags & TF_ACCEPT_FDS != 0; let reply = Transaction::new_reply(self, process, info, allow_fds)?; - self.inner.lock().push_work(completion); + // Not notifying: Reply to current thread. + let _ = self.inner.lock().push_work(completion); orig.from.deliver_reply(Ok(reply), &orig, None); Ok(()) })() @@ -1421,7 +1438,8 @@ fn oneway_transaction_inner(self: &Arc, info: &mut TransactionInfo) -> Bin let list_completion = DTRWrap::arc_try_new(DeliverCode::new(code, self.process.task.pid()))?; let completion = list_completion.clone_arc(); - self.inner.lock().push_work(list_completion); + // Not notifying: Reply to current thread. + let _ = self.inner.lock().push_work(list_completion); match transaction.submit(info) { Ok(()) => Ok(()), Err(err) => { @@ -1623,10 +1641,9 @@ pub(crate) fn write_read(self: &Arc, data: UserSlice, wait: bool) -> Resul ret } - pub(crate) fn poll(&self, file: &File, table: PollTable<'_>) -> (bool, u32) { - table.register_wait(file, &self.work_condvar); + pub(crate) fn poll(&self) -> Result<(bool, u32)> { let mut inner = self.inner.lock(); - (inner.should_use_process_work_queue(), inner.poll()) + Ok((inner.should_use_process_work_queue(), inner.poll())) } /// Make the call to `get_work` or `get_work_local` return immediately, if any. @@ -1643,26 +1660,9 @@ pub(crate) fn exit_looper(&self) { } } - pub(crate) fn notify_if_poll_ready(&self, sync: bool) { - // Determine if we need to notify. This requires the lock. - let inner = self.inner.lock(); - let notify = inner.looper_flags & LOOPER_POLL != 0 && inner.should_use_process_work_queue(); - drop(inner); - - // Now that the lock is no longer held, notify the waiters if we have to. - if notify { - if sync { - self.work_condvar.notify_sync(); - } else { - self.work_condvar.notify_one(); - } - } - } - pub(crate) fn release(self: &Arc) { self.inner.lock().is_dead = true; - //self.work_condvar.clear(); self.unwind_transaction_stack(); // Cancel all pending work items. diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 96d45c6816fe..13dfb5c5c955 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -371,11 +371,15 @@ pub(crate) fn submit(self: DLArc, info: &mut TransactionInfo) -> BinderRes crate::trace::trace_transaction(false, &self, Some(&thread.task)); match thread.push_work(self) { PushWorkRes::Ok => Ok(()), + PushWorkRes::OkNotifyPoll => { + process.notify_poll(true); + Ok(()) + } PushWorkRes::FailedDead(me) => Err((BinderError::new_dead(), me)), } } else { crate::trace::trace_transaction(false, &self, None); - process_inner.push_work(self) + process_inner.push_work(&process, self) }; drop(process_inner); From 0f7f34c67ead630bf485c805a95fc540d80f2c7f Mon Sep 17 00:00:00 2001 From: Jahnavi MN Date: Thu, 16 Jul 2026 13:02:34 +0000 Subject: [PATCH 245/513] rust_binder: Update defer_work bitmaps to use kernel::impl_flags! - Define `DeferWorks(u8)` and `DeferWork` enum using `bit_u8` offsets. - Change `ProcessInner.defer_work` type from `u8` to `DeferWorks`. - Update `Process::release()` and `Process::flush()` to check for empty states using `DeferWorks::empty()`. - Update the workqueue runner to inspect flags using `.contains()`. Signed-off-by: Jahnavi MN Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260716-b4-rust_binder_impl_flags-v1-1-b4201d3f15b3@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 34 ++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 962f38453252..1778628d8acd 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -16,6 +16,7 @@ use kernel::{ bindings, + bits::bit_u8, cred::Credential, error::Error, fs::file::{self, File}, @@ -71,9 +72,18 @@ fn new(address: usize, size: usize) -> Self { } } -// bitflags for defer_work. -const PROC_DEFER_FLUSH: u8 = 1; -const PROC_DEFER_RELEASE: u8 = 2; +kernel::impl_flags!( + /// Represents multiple deferred work flags. + #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] + pub struct DeferWorks(u8); + + /// Represents a single deferred work category. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum DeferWork { + Flush = bit_u8(0), + Release = bit_u8(1), + } +); #[derive(Copy, Clone)] pub(crate) enum IsFrozen { @@ -122,7 +132,7 @@ pub(crate) struct ProcessInner { started_thread_count: u32, /// Bitmap of deferred work to do. - defer_work: u8, + defer_work: DeferWorks, /// Number of transactions to be transmitted before processes in freeze_wait /// are woken up. @@ -152,7 +162,7 @@ fn new() -> Self { requested_thread_count: 0, max_threads: 0, started_thread_count: 0, - defer_work: 0, + defer_work: DeferWorks::default(), outstanding_txns: 0, is_frozen: IsFrozen::No, sync_recv: false, @@ -496,13 +506,13 @@ fn run(me: Arc) { { let mut inner = me.inner.lock(); defer = inner.defer_work; - inner.defer_work = 0; + inner.defer_work = DeferWorks::default(); } - if defer & PROC_DEFER_FLUSH != 0 { + if defer.contains(DeferWork::Flush) { me.deferred_flush(); } - if defer & PROC_DEFER_RELEASE != 0 { + if defer.contains(DeferWork::Release) { me.deferred_release(); } } @@ -1706,8 +1716,8 @@ pub(crate) fn release(this: Arc, _file: &File) { let should_schedule; { let mut inner = this.inner.lock(); - should_schedule = inner.defer_work == 0; - inner.defer_work |= PROC_DEFER_RELEASE; + should_schedule = inner.defer_work == DeferWorks::empty(); + inner.defer_work |= DeferWork::Release; binderfs_file = inner.binderfs_file.take(); } @@ -1724,8 +1734,8 @@ pub(crate) fn flush(this: ArcBorrow<'_, Process>) -> Result { let should_schedule; { let mut inner = this.inner.lock(); - should_schedule = inner.defer_work == 0; - inner.defer_work |= PROC_DEFER_FLUSH; + should_schedule = inner.defer_work == DeferWorks::empty(); + inner.defer_work |= DeferWork::Flush; } if should_schedule { From 4b17dfb3e22fdccf74839d2fc52362ddc257024e Mon Sep 17 00:00:00 2001 From: Yilin Chen <1479826151@qq.com> Date: Tue, 7 Jul 2026 16:10:30 +0000 Subject: [PATCH 246/513] rust: miscdevice: fix write_iter safety docs The write_iter callback consumes data from the supplied iov_iter and wraps it as an IovIterSource. Its Safety docs required a valid iov_iter for writing, but the implementation and the IovIterSource contract require one that is valid for reading. Update the docs to match that direction. Assisted-by: Codex:GPT-5 Signed-off-by: Yilin Chen <1479826151@qq.com> Link: https://patch.msgid.link/tencent_8CD671E0F35223030143524D045F3BCAD506@qq.com Signed-off-by: Greg Kroah-Hartman --- rust/kernel/miscdevice.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index 83ce50def5ac..3abef1b8543d 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -289,7 +289,7 @@ impl MiscdeviceVTable { /// # Safety /// /// `kiocb` must be correspond to a valid file that is associated with a - /// `MiscDeviceRegistration`. `iter` must be a valid `struct iov_iter` for writing. + /// `MiscDeviceRegistration`. `iter` must be a valid `struct iov_iter` for reading. unsafe extern "C" fn write_iter( kiocb: *mut bindings::kiocb, iter: *mut bindings::iov_iter, From 5d577fa6feaf2ef02751fc4a89fc9a695aa0f6b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Antinori?= Date: Thu, 2 Jul 2026 17:58:00 -0300 Subject: [PATCH 247/513] rust_binder: use pin_init::zeroed for file_operations initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All types in `bindings` implement `Zeroable` if they can. This enables using `pin_init::zeroed()` for `file_operations` initialization instead of relying on `unsafe { core::mem::MaybeUninit::zeroed().assume_init() }`. This change improves readability and removes an unnecessary unsafe block. Link: https://github.com/Rust-for-Linux/linux/issues/1189 Suggested-by: Benno Lossin Signed-off-by: Nicolás Antinori Link: https://patch.msgid.link/20260702205803.552476-1-nico.antinori.7@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/rust_binder_main.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index 9e6cda960722..e6805bc06f43 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -324,9 +324,6 @@ unsafe impl Sync for AssertSync {} #[no_mangle] #[used] pub static rust_binder_fops: AssertSync = { - // SAFETY: All zeroes is safe for the `file_operations` type. - let zeroed_ops = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; - let ops = kernel::bindings::file_operations { owner: THIS_MODULE.as_ptr(), poll: Some(rust_binder_poll), @@ -336,7 +333,7 @@ unsafe impl Sync for AssertSync {} open: Some(rust_binder_open), release: Some(rust_binder_release), flush: Some(rust_binder_flush), - ..zeroed_ops + ..pin_init::zeroed() }; AssertSync(ops) }; From 65f0ecb9ec85cb3a2e2372a6427dcc100691fc57 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Wed, 13 May 2026 17:18:42 +0300 Subject: [PATCH 248/513] issei: initial driver skeleton The ISSEI (Intel Silicon Security Engine Interface) subsystem provides a communication channel between the host and the Silicon Security Engine. Prepare basic driver functions and character device for user-space communication. Add DMA access routines for ISSEI HECI devices. Add of DMA-related structures and implementation of routines for setting up DMA, as well as reading and writing DMA buffers. Reviewed-by: Karol Wachowski Co-developed-by: Vitaly Lubart Signed-off-by: Vitaly Lubart Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260513-issei-for-upstream-v1-1-f590038678f9@intel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/index.rst | 1 + Documentation/driver-api/issei/index.rst | 16 ++ Documentation/driver-api/issei/issei.rst | 135 ++++++++++++++ MAINTAINERS | 7 + drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + drivers/misc/issei/Kconfig | 13 ++ drivers/misc/issei/Makefile | 7 + drivers/misc/issei/cdev.c | 219 +++++++++++++++++++++++ drivers/misc/issei/cdev.h | 16 ++ drivers/misc/issei/dma.c | 154 ++++++++++++++++ drivers/misc/issei/dma.h | 69 +++++++ drivers/misc/issei/hw_msg.h | 163 +++++++++++++++++ drivers/misc/issei/issei_dev.h | 160 +++++++++++++++++ include/uapi/linux/issei.h | 69 +++++++ 15 files changed, 1031 insertions(+) create mode 100644 Documentation/driver-api/issei/index.rst create mode 100644 Documentation/driver-api/issei/issei.rst create mode 100644 drivers/misc/issei/Kconfig create mode 100644 drivers/misc/issei/Makefile create mode 100644 drivers/misc/issei/cdev.c create mode 100644 drivers/misc/issei/cdev.h create mode 100644 drivers/misc/issei/dma.c create mode 100644 drivers/misc/issei/dma.h create mode 100644 drivers/misc/issei/hw_msg.h create mode 100644 drivers/misc/issei/issei_dev.h create mode 100644 include/uapi/linux/issei.h diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst index eaf7161ff957..6601a258690f 100644 --- a/Documentation/driver-api/index.rst +++ b/Documentation/driver-api/index.rst @@ -105,6 +105,7 @@ Subsystem-specific APIs interconnect ipmb ipmi + issei/index libata mailbox md/index diff --git a/Documentation/driver-api/issei/index.rst b/Documentation/driver-api/issei/index.rst new file mode 100644 index 000000000000..604267463fd4 --- /dev/null +++ b/Documentation/driver-api/issei/index.rst @@ -0,0 +1,16 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: + +========================================================= +The Intel Silicon Security Engine Interface (Intel SSEI) +========================================================= + +**Copyright** |copy| 2026 Intel Corporation + + +.. toctree:: + :caption: Table of Contents + :maxdepth: 3 + + issei diff --git a/Documentation/driver-api/issei/issei.rst b/Documentation/driver-api/issei/issei.rst new file mode 100644 index 000000000000..a5e99e92e095 --- /dev/null +++ b/Documentation/driver-api/issei/issei.rst @@ -0,0 +1,135 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Introduction +============ + +The Intel Silicon Security Engine (Intel SSE) is an isolated and +protected computing resource (Co-processor) residing inside +Intel client chipsets released in 2024 (Lunar Lake) or later. +The Intel SSE provide security support and platform boot orchestration. +The actual feature set depends on the Intel chipset SKU. + +The Intel Silicon Security Engine Interface (Intel SSEI) +is the interface between the Host and Intel SSE. +This interface is exposed to the host as one or more PCI devices. +The Intel SSEI Driver is in charge of the communication channel between +a host application and the Intel SSE features. + +Each Intel SSE feature, or Intel SSE Client is addressed by a unique UUID and +each client has its own protocol. The protocol is message-based with a +header and payload up to maximal number of bytes advertised by the client, +upon connection. + +Intel SSEI Driver +================= + +The driver exposes a character device with device nodes /dev/isseiX. + +An application maintains communication with an Intel SSE feature while +/dev/isseiX is open. The binding to a specific feature is performed by calling +:c:macro:`IOCTL_ISSEI_CONNECT_CLIENT`, which passes the desired UUID. +The number of instances of an Intel SSE feature that can be opened +at the same time is limited to single instance. + +The driver is transparent to data that are passed between firmware feature +and host application. + +Because some of the Intel SSE features can change the system +configuration, the driver by default allows only a privileged +user to access it. + +The connection termination is performed by calling +:c:macro:`IOCTL_ISSEI_DISCONNECT_CLIENT`. + +The session is terminated calling :c:expr:`close(fd)`. + +A code snippet for an application communicating with SPDM client: + +.. code-block:: C + + struct issei_connect_client_data data = {.in_client_uuid = + {0xe8, 0x51, 0x49, 0xdf, 0x94, 0x47, 0x4C, + 0x9A, 0x83, 0x67, 0xC4, 0xE3, 0x34, 0x64, 0xF1, 0xB4}}; + __u8 req_data[] = {0x10, 0x84, 0x00, 0x00}; /* SPDM Get Version */ + size_t req_data_len = sizeof(req_data); + __u8 res_data[256]; + size_t res_data_len = sizeof(res_data); + int fd = open("/dev/issei0", O_RDWR); + + ioctl(fd, IOCTL_ISSEI_CONNECT_CLIENT, &data); + + printf("Ver=%d, MaxLen=%u, Flags=0x%08X\n", + data.out_client_properties.protocol_version, + data.out_client_properties.max_msg_length, + data.out_client_properties.flags); + + [...] + + write(fd, req_data, req_data_len); + + [...] + + read(fd, res_data, res_data_len); + + printf("SPDM version count %u, version[0]=%02X%02X\n", + res_data[5], res_data[6], res_data[7]); + + [...] + + ioctl(fd, IOCTL_ISSEI_DISCONNECT_CLIENT, &data); + + [...] + + close(fd); + + +User space API ioctl +==================== + +The Intel SSEI Driver supports the following ioctl commands: + +IOCTL_ISSEI_CONNECT_CLIENT +-------------------------- +Connect to firmware Feature/Client. + +.. code-block:: none + + Usage: + + struct issei_connect_client_data client_data; + + ioctl(fd, IOCTL_ISSEI_CONNECT_CLIENT, &client_data); + + struct issei_connect_client_data - contain the following + Inputs: + in_client_uuid - UUID of the FW Feature that needs to connect to. + Outputs: + out_client_properties - Client Properties: MTU, Protocol Version and Flags. + + Error returns: + ENOTTY No such client (i.e. wrong UUID) or connection is not allowed. + EINVAL Wrong IOCTL Number + ENODEV Device or Connection is not initialized or ready. + ENOMEM Unable to allocate memory to client internal data. + EFAULT Fatal Error (e.g. Unable to access user input data) + EBUSY Connection Already Open + +:Note: + max_msg_length (MTU) in client properties describes the maximum + data that can be sent or received. (e.g. with MTU=2K, can send + requests up to bytes 2k and received responses up to 2k bytes). + +IOCTL_ISSEI_DISCONNECT_CLIENT +----------------------------- +Disconnect from firmware Feature/Client. + +.. code-block:: none + + Usage: + + ioctl(fd, IOCTL_ISSEI_DISCONNECT_CLIENT, NULL); + + Error returns: + EINVAL Wrong IOCTL Number + ENODEV Device or Connection is not initialized or ready. + ENOTCONN Feature/Client is not connected. diff --git a/MAINTAINERS b/MAINTAINERS index be0af1665945..b6bf86f9f8ab 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13360,6 +13360,13 @@ F: drivers/platform/x86/intel/sdsi.c F: tools/arch/x86/intel_sdsi/ F: tools/testing/selftests/drivers/sdsi/ +INTEL SILICON SECURITY ENGINE INTERFACE (ISSEI) +M: Alexander Usyskin +S: Supported +F: Documentation/driver-api/issei/issei.rst +F: drivers/misc/issei/ +F: include/uapi/linux/issei.h + INTEL SGX M: Jarkko Sakkinen R: Dave Hansen diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 390256ed91f4..e594edf86941 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -651,4 +651,5 @@ source "drivers/misc/mchp_pci1xxxx/Kconfig" source "drivers/misc/keba/Kconfig" source "drivers/misc/amd-sbi/Kconfig" source "drivers/misc/rp1/Kconfig" +source "drivers/misc/issei/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index fed47c7672b9..086ac3f75935 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -74,3 +74,4 @@ obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o obj-y += keba/ obj-y += amd-sbi/ obj-$(CONFIG_MISC_RP1) += rp1/ +obj-$(CONFIG_INTEL_SSEI) += issei/ diff --git a/drivers/misc/issei/Kconfig b/drivers/misc/issei/Kconfig new file mode 100644 index 000000000000..d98ac7925ce6 --- /dev/null +++ b/drivers/misc/issei/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2023-2026 Intel Corporation +config INTEL_SSEI + tristate "Intel Silicon Security Engine Interface" + help + The ISSEI (Intel Silicon Security Engine Interface) + subsystem provides a communication channel between the host and the + Silicon Security Engine. + Enable this driver to get SPDM and other features on Intel client CPUs + released in 2024 (Lunar Lake) or later. + + If selected, the /dev/isseiX device will be created. + If in doubt, select N. diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile new file mode 100644 index 000000000000..f13bcf3e1699 --- /dev/null +++ b/drivers/misc/issei/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2023-2026 Intel Corporation +ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE='"INTEL_SSEI"' + +obj-$(CONFIG_INTEL_SSEI) += issei.o +issei-objs += cdev.o +issei-objs += dma.o diff --git a/drivers/misc/issei/cdev.c b/drivers/misc/issei/cdev.c new file mode 100644 index 000000000000..d3d53dad088e --- /dev/null +++ b/drivers/misc/issei/cdev.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "issei_dev.h" +#include "cdev.h" + +struct class *issei_class; +static dev_t issei_devt; + +#define ISSEI_MAX_DEVS MINORMASK + +static DEFINE_XARRAY_ALLOC(issei_minor_xa); + +static ssize_t fw_ver_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct issei_device *idev = dev_get_drvdata(device); + + return sysfs_emit(buf, "%u.%u.%u.%u\n", idev->fw_version[0], idev->fw_version[1], + idev->fw_version[2], idev->fw_version[3]); +} +static DEVICE_ATTR_RO(fw_ver); + +static struct attribute *issei_attrs[] = { + &dev_attr_fw_ver.attr, + NULL +}; +ATTRIBUTE_GROUPS(issei); + +static const struct file_operations issei_fops = { + .owner = THIS_MODULE, +}; + +static void issei_device_release(struct device *dev) +{ + kfree(dev_get_drvdata(dev)); +} + +static void issei_device_init(struct issei_device *idev, struct device *parent, + const struct issei_dma_length *dma_length, + const struct issei_hw_ops *ops) +{ + idev->parent = parent; + idev->power_down = false; + init_waitqueue_head(&idev->wait_has_data); + idev->has_data = false; + init_waitqueue_head(&idev->wait_rst_state); + idev->rst_state = ISSEI_RST_STATE_INIT; + + mutex_init(&idev->client_lock); + INIT_LIST_HEAD(&idev->host_client_list); + idev->host_client_last_id = 0; + idev->host_client_count = 0; + INIT_LIST_HEAD(&idev->fw_client_list); + INIT_LIST_HEAD(&idev->write_queue); + idev->last_write_ts = 0; + + idev->dma.length = *dma_length; + + idev->ops = ops; +} + +/** + * issei_register: register issei character device + * @hw_size: size of the hardware structure to allocate + * @parent: parent device + * @dma_length: structure with DMA sizes + * @ops: hardware-related operations + * + * Return: pointer allocated to issei_device structure, error on failure + */ +struct issei_device *issei_register(size_t hw_size, struct device *parent, + const struct issei_dma_length *dma_length, + const struct issei_hw_ops *ops) +{ + struct issei_device *idev; + u32 minor; + int ret, devno; + + idev = kzalloc(sizeof(*idev) + hw_size, GFP_KERNEL); + if (!idev) + return ERR_PTR(-ENOMEM); + + issei_device_init(idev, parent, dma_length, ops); + + ret = xa_alloc(&issei_minor_xa, &minor, idev, XA_LIMIT(0, ISSEI_MAX_DEVS), GFP_KERNEL); + if (ret < 0) { + dev_err(&idev->dev, "Failed to allocate minor. ret = %d\n", ret); + kfree(idev); + return ERR_PTR(ret); + } + + idev->minor = minor; + devno = MKDEV(MAJOR(issei_devt), idev->minor); + + device_initialize(&idev->dev); + idev->dev.devt = devno; + idev->dev.class = issei_class; + idev->dev.parent = parent; + idev->dev.groups = issei_groups; + idev->dev.release = issei_device_release; + dev_set_drvdata(&idev->dev, idev); + + idev->cdev = cdev_alloc(); + if (!idev->cdev) { + ret = -ENOMEM; + goto err; + } + idev->cdev->ops = &issei_fops; + if (parent->driver) + idev->cdev->owner = parent->driver->owner; + cdev_set_parent(idev->cdev, &idev->dev.kobj); + + ret = cdev_add(idev->cdev, devno, 1); + if (ret) { + dev_err(parent, "unable to add device %d:%u ret = %d\n", + MAJOR(issei_devt), idev->minor, ret); + goto err_del_cdev; + } + + ret = dev_set_name(&idev->dev, "issei%u", idev->minor); + if (ret) { + dev_err(parent, "unable to set name to device %d:%u ret = %d\n", + MAJOR(issei_devt), idev->minor, ret); + goto err_del_cdev; + } + + ret = device_add(&idev->dev); + if (ret) { + dev_err(parent, "unable to add device %d:%u ret = %d\n", + MAJOR(issei_devt), idev->minor, ret); + goto err_del_cdev; + } + + idev->fw_clients = kset_create_and_add("fw_clients", NULL, &idev->dev.kobj); + if (!idev->fw_clients) { + ret = -ENOMEM; + goto err_del_dev; + } + + return idev; + +err_del_dev: + device_del(&idev->dev); +err_del_cdev: + cdev_del(idev->cdev); +err: + put_device(&idev->dev); + xa_erase(&issei_minor_xa, minor); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(issei_register); + +/** + * issei_deregister: remove issei character device + * @idev: the device structure + */ +void issei_deregister(struct issei_device *idev) +{ + u32 minor = idev->minor; + + cdev_del(idev->cdev); + + kset_unregister(idev->fw_clients); + + device_del(&idev->dev); + + put_device(&idev->dev); + + xa_erase(&issei_minor_xa, minor); +} +EXPORT_SYMBOL_GPL(issei_deregister); + +static int __init issei_cdev_init(void) +{ + int ret; + + issei_class = class_create("issei"); + if (IS_ERR(issei_class)) { + pr_err("couldn't create class\n"); + return PTR_ERR(issei_class); + } + + ret = alloc_chrdev_region(&issei_devt, 0, ISSEI_MAX_DEVS, "issei"); + if (ret < 0) { + pr_err("unable to allocate char dev region\n"); + class_destroy(issei_class); + return ret; + } + + return 0; +} + +static void __exit issei_cdev_exit(void) +{ + unregister_chrdev_region(issei_devt, ISSEI_MAX_DEVS); + class_destroy(issei_class); +} + +module_init(issei_cdev_init); +module_exit(issei_cdev_exit); + +MODULE_DESCRIPTION("Intel(R) Silicon Security Engine Interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/issei/cdev.h b/drivers/misc/issei/cdev.h new file mode 100644 index 000000000000..30075a624f2d --- /dev/null +++ b/drivers/misc/issei/cdev.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_CDEV_H_ +#define _ISSEI_CDEV_H_ + +struct device; +struct issei_device; +struct issei_dma_length; +struct issei_hw_ops; + +struct issei_device *issei_register(size_t hw_size, struct device *parent, + const struct issei_dma_length *dma_length, + const struct issei_hw_ops *ops); +void issei_deregister(struct issei_device *idev); + +#endif /* _ISSEI_CDEV_H_ */ diff --git a/drivers/misc/issei/dma.c b/drivers/misc/issei/dma.c new file mode 100644 index 000000000000..457d28f31c06 --- /dev/null +++ b/drivers/misc/issei/dma.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include + +#include "issei_dev.h" +#include "hw_msg.h" + +static inline size_t __issei_dma_size(const struct issei_dma *dma) +{ + return dma->length.h2f + dma->length.f2h + dma->length.ctl; +} + +/** + * issei_dmam_setup - setup DMA buffer and clean it + * @idev: issei device object + * + * Return: 0 on success, <0 on failures + */ +int issei_dmam_setup(struct issei_device *idev) +{ + struct issei_dma *dma = &idev->dma; + size_t size; + + size = __issei_dma_size(dma); + if (!size) + return -EINVAL; + + if (!dma->vaddr) + dma->vaddr = dmam_alloc_coherent(idev->parent, size, &dma->daddr, + GFP_KERNEL | __GFP_ZERO); + if (dma->vaddr) + memset(dma->vaddr, 0, size); + return dma->vaddr ? 0 : -ENOMEM; +} + +static inline struct control_buffer *__dma_get_ctl_buf(struct issei_dma *dma) +{ + return dma->vaddr + dma->length.h2f + dma->length.f2h; +} + +static bool __issei_dma_is_read_busy(struct issei_dma *dma) +{ + struct control_buffer *ctl = __dma_get_ctl_buf(dma); + + return ctl->f2h_counter_wr != ctl->f2h_counter_rd; +} + +static bool __issei_dma_is_write_busy(struct issei_dma *dma) +{ + struct control_buffer *ctl = __dma_get_ctl_buf(dma); + + return ctl->h2f_counter_wr != ctl->h2f_counter_rd; +} + +static void __issei_dma_read_finalize(struct issei_device *idev) +{ + struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma); + + dev_dbg(&idev->dev, "ctl->f2h_counter_rd %u\n", ctl->f2h_counter_rd); + /* No need to check overflow - the firmware counters overflow the same way */ + ctl->f2h_counter_rd++; +} + +static void __issei_dma_write_finalize(struct issei_device *idev) +{ + struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma); + + dev_dbg(&idev->dev, "ctl->h2f_counter_wr %u\n", ctl->h2f_counter_wr); + /* No need to check overflow - the firmware counters overflow the same way */ + ctl->h2f_counter_wr++; +} + +/** + * issei_dma_write - write data package to DMA + * @idev: issei device object + * @data: data atructure + * + * Return: 0 on success, <0 on failures + */ +int issei_dma_write(struct issei_device *idev, const struct issei_dma_data *data) +{ + u8 *write_buf = idev->dma.vaddr; + struct ham_message_header *hdr = (struct ham_message_header *)write_buf; + + if (data->length > idev->dma.length.h2f - sizeof(*hdr)) { + dev_err(&idev->dev, "Message is too big\n"); + return -EMSGSIZE; + } + + if (__issei_dma_is_write_busy(&idev->dma)) { + if (ktime_ms_delta(ktime_get(), idev->last_write_ts) > ISSEI_WRITE_TIMEOUT_MSEC) { + dev_err(&idev->dev, "Write stuck in queue\n"); + return -EIO; + } + dev_info(&idev->dev, "Write is busy\n"); + return -EBUSY; + } + + hdr->length = data->length; + hdr->fw_id = data->fw_id; + hdr->host_id = data->host_id; + hdr->flags = data->flags; + hdr->status = data->status; + hdr->reserved = 0; + + memcpy(write_buf + sizeof(*hdr), data->buf, data->length); + + __issei_dma_write_finalize(idev); + idev->last_write_ts = ktime_get(); + return 0; +} + +/** + * issei_dma_read - read data package from DMA + * @idev: issei device object + * @data: data atructure + * + * Return: %0 on success, <0 on failures + */ +int issei_dma_read(struct issei_device *idev, struct issei_dma_data *data) +{ + u8 *read_buf = idev->dma.vaddr + idev->dma.length.h2f; + struct ham_message_header *hdr = (struct ham_message_header *)read_buf; + + if (!__issei_dma_is_read_busy(&idev->dma)) { + dev_dbg(&idev->dev, "Nothing to read\n"); + return -ENODATA; + } + + dev_dbg(&idev->dev, "Reading header\n"); + data->length = hdr->length; + data->fw_id = hdr->fw_id; + data->host_id = hdr->host_id; + data->flags = hdr->flags; + data->status = hdr->status; + + if (data->length > idev->dma.length.f2h - sizeof(*hdr)) { + dev_err(&idev->dev, "Message length %u is bigger than buffer %zu\n", + data->length, idev->dma.length.f2h - sizeof(*hdr)); + return -EIO; + } + + dev_dbg(&idev->dev, "Reading data (size %u)\n", data->length); + data->buf = kmemdup(read_buf + sizeof(*hdr), data->length, GFP_KERNEL); + if (!data->buf) + return -ENOMEM; + __issei_dma_read_finalize(idev); + return 0; +} diff --git a/drivers/misc/issei/dma.h b/drivers/misc/issei/dma.h new file mode 100644 index 000000000000..e6b7aeb50ae6 --- /dev/null +++ b/drivers/misc/issei/dma.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_DMA_H_ +#define _ISSEI_DMA_H_ + +#include + +struct issei_device; + +/** + * struct issei_dma_length - sizes of DMA memory portions + * @h2f: host to firmware buffer size + * @f2h: firmware to host buffer size + * @ctl: control buffer size + */ +struct issei_dma_length { + size_t h2f; + size_t f2h; + size_t ctl; +}; + +/** + * struct issei_dma - DMA memory structure + * @vaddr: virtual address + * @daddr: physical address + * @length: memory sizes structure + */ +struct issei_dma { + void *vaddr; + dma_addr_t daddr; + struct issei_dma_length length; +}; + +/* Operation statuses */ +#define HAMS_SUCCESS 0x00 +#define HAMS_PROTOCOL_NOT_SUPPORTED 0x01 +#define HAMS_DEPRECATED_BUS_MSG 0x02 +#define HAMS_CLIENT_NOT_EXISTS 0x03 +#define HAMS_MSG_TOO_BIG 0x04 +#define HAMS_MSG_NOT_CONSUMED 0x05 +#define HAMS_CORRUPTED_BUS_MSG 0x06 +#define HAMS_CORRUPTED_HEADER 0x07 +#define HAMS_INVALID_LENGTH 0x08 +#define HAMS_SHARED_MEMORY_SIZE_UNSUPPORTED 0x09 +#define HAMS_GENERAL_FATAL_ERROR 0xff + +/** + * struct issei_dma_data - data passed through channel + * @fw_id: firmware client id + * @host_id: host client id + * @flags: flags bitmap + * @status: operation status + * @length: data length + * @buf: pointer to data buffer + */ +struct issei_dma_data { + u16 fw_id; + u16 host_id; + u32 flags; + u32 status; + u32 length; + void *buf; +}; + +int issei_dmam_setup(struct issei_device *idev); +int issei_dma_write(struct issei_device *idev, const struct issei_dma_data *data); +int issei_dma_read(struct issei_device *idev, struct issei_dma_data *data); + +#endif /*_ISSEI_DMA_H_*/ diff --git a/drivers/misc/issei/hw_msg.h b/drivers/misc/issei/hw_msg.h new file mode 100644 index 000000000000..28fd3775f64c --- /dev/null +++ b/drivers/misc/issei/hw_msg.h @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HW_MSG_H_ +#define _ISSEI_HW_MSG_H_ + +#include +#include + +#define HAM_CB_MESSAGE_ID_REQ 0x8086cafe +#define HAM_CB_MESSAGE_ID_RES 0xcafe8086 +#define HAM_CB_MESSAGE_VER 0x1 + +/** + * struct ham_setup_shared_memory_req - shared memory setup request + * @msg_id: message id, should be %HAM_CB_MESSAGE_ID_REQ + * @ver: message version (%HAM_CB_MESSAGE_VER) + * @reserved: reserved + * @buffer_physical_address: physical address of DMA buffer + * @host_to_fw_section_length: memory size for host to fw communication + * @fw_to_host_section_length: memory size for fw to host communication + * @control_length: memory size for control buffer + */ +struct ham_setup_shared_memory_req { + u32 msg_id; + u16 ver; + u16 reserved; + u64 buffer_physical_address; + u32 host_to_fw_section_length; + u32 fw_to_host_section_length; + u32 control_length; +} __packed __aligned(4); + +/** + * struct ham_setup_shared_memory_res - shared memory setup response + * @msg_id: message id, should be %HAM_CB_MESSAGE_ID_RES + * @status: operation status + */ +struct ham_setup_shared_memory_res { + u32 msg_id; + u32 status; +}; + +/** + * struct control_buffer - control buffer structure + * @h2f_counter_wr: write counter host to fw + * @h2f_counter_rd: read counter host to fw + * @f2h_counter_wr: write counter fw to host + * @f2h_counter_rd: read counter fw to host + */ +struct control_buffer { + u32 h2f_counter_wr; + u32 h2f_counter_rd; + u32 f2h_counter_wr; + u32 f2h_counter_rd; +}; + +/* HAM messages over DMA */ + +/** + * struct ham_message_header - message header over DMA + * @length: message length (payload only, not including header) + * @fw_id: firmware client id (0 means Bus Message) + * @host_id: host client id (0 means Bus Message) + * @flags: message flags + * @status: operation status + * @reserved: reserved + */ +struct ham_message_header { + u32 length; + u16 fw_id; + u16 host_id; + u32 flags; + u32 status; + u32 reserved; +}; + +/* Bus Commands */ +#define HAM_BUS_CMD_START_REQ 0x00 +#define HAM_BUS_CMD_START_RSP 0x80 +#define HAM_BUS_CMD_CLIENT_REQ 0x01 +#define HAM_BUS_CMD_CLIENT_RSP 0x81 + +/** + * struct ham_bus_message - bus message header + * @cmd: command code + */ +struct ham_bus_message { + u32 cmd; +}; + +#define HAM_SUPPORTED_VERSION 0x01 + +/** + * struct ham_start_message_req - start message + * @header: bus message header (%HAM_BUS_CMD_START_REQ) + * @supported_version: supported protocol version + * @heci_capabilities_length: protocol capabilities length in bytes + * @heci_capabilities: protocol capabilities data + */ +struct ham_start_message_req { + struct ham_bus_message header; + u16 supported_version; + u8 heci_capabilities_length; + u8 heci_capabilities[] __counted_by(heci_capabilities_length); +} __packed; + +/** + * struct ham_start_message_res - start message response + * @header: bus message header (%HAM_BUS_CMD_START_RSP) + * @fw_version: firmware version (four u16 blocks) + * @supported_version: supported protocol version + * @heci_capabilities_length: protocol capabilities length in bytes + * @heci_capabilities: protocol capabilities data + */ +struct ham_start_message_res { + struct ham_bus_message header; + u16 fw_version[4]; + u16 supported_version; + u8 heci_capabilities_length; + u8 heci_capabilities[] __counted_by(heci_capabilities_length); +} __packed; + +/** + * struct ham_get_clients_req - clients list request + * @header: bus message header (%HAM_BUS_CMD_CLIENT_REQ) + */ +struct ham_get_clients_req { + struct ham_bus_message header; +}; + +/** + * struct ham_client_properties - single client properties + * @client_number: client id in firmware + * @protocol_ver: client protocol version + * @reserved: reserved + * @client_uuid: protocol name (UUID) + * @client_mtu: max message length supported by client + * @flags: client flags + */ +struct ham_client_properties { + u16 client_number; + u8 protocol_ver; + u8 reserved; + uuid_t client_uuid; + u32 client_mtu; + u32 flags; +}; + +/** + * struct ham_get_clients_res - client properties response + * @header: bus message header (%HAM_BUS_CMD_CLIENT_RSP) + * @client_count: number of clients in firmware + * @reserved: reserved + * @clients_props: list of client properties + */ +struct ham_get_clients_res { + struct ham_bus_message header; + u16 client_count; + u16 reserved; + struct ham_client_properties clients_props[] __counted_by(client_count); +}; + +#endif /* _ISSEI_HW_MSG_H_ */ diff --git a/drivers/misc/issei/issei_dev.h b/drivers/misc/issei/issei_dev.h new file mode 100644 index 000000000000..c742e7fe6cb6 --- /dev/null +++ b/drivers/misc/issei/issei_dev.h @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_DEV_H_ +#define _ISSEI_DEV_H_ + +#include +#include +#include +#include +#include +#include + +#include "dma.h" + +struct cdev; +struct kset; + +struct issei_device; +struct issei_host_client; + +extern struct class *issei_class; + +#define ISSEI_HOST_CLIENTS_MAX 255 + +#define ISSEI_SUPPORTED_PROTOCOL_VER 1 + +#define ISSEI_MAX_CONSEC_RESET 3 + +#define ISSEI_RST_HW_READY_TIMEOUT_MSEC (2 * MSEC_PER_SEC) +#define ISSEI_RST_STEP_TIMEOUT_MSEC (2 * MSEC_PER_SEC) +#define ISSEI_STOP_TIMEOUT_MSEC 500 +#define ISSEI_WRITE_TIMEOUT_MSEC (MSEC_PER_SEC) + +/** + * struct issei_write_buf - write buffer object + * @list: linked list pointer + * @cl: host client that requested this write + * @data: data to write + * @data_size: data size + */ +struct issei_write_buf { + struct list_head list; + struct issei_host_client *cl; + const u8 *data; + size_t data_size; +}; + +/** + * struct issei_hw_ops - callbacks for hardware operations + * @irq_clear: clear irq + * @irq_enable: enable irq + * @irq_disable: disable irq + * @irq_sync: sync irq + * @hw_reset: initiate hardware reset + * @hw_config: initial hardware config + * @hw_is_ready: check if hardware is ready + * @hw_reset_release: release hardware from reset + * @host_set_ready: set host ready indicator + * @setup_message_send: send setup message + * @setup_message_recv: receive setup message + * @irq_write_generate: generate interrupt on write complete + */ +struct issei_hw_ops { + void (*irq_clear)(struct issei_device *idev); + void (*irq_enable)(struct issei_device *idev); + void (*irq_disable)(struct issei_device *idev); + void (*irq_sync)(struct issei_device *idev); + int (*hw_reset)(struct issei_device *idev, bool enable); + int (*hw_config)(struct issei_device *idev); + bool (*hw_is_ready)(struct issei_device *idev); + void (*hw_reset_release)(struct issei_device *idev); + void (*host_set_ready)(struct issei_device *idev); + int (*setup_message_send)(struct issei_device *idev); + int (*setup_message_recv)(struct issei_device *idev); + int (*irq_write_generate)(struct issei_device *idev); +}; + +/** + * enum issei_rst_state: driver reset flow states + * @ISSEI_RST_STATE_INIT: initial state + * @ISSEI_RST_STATE_HW_READY: waiting for HW to be ready + * @ISSEI_RST_STATE_SETUP: waiting for channel setup completion + * @ISSEI_RST_STATE_START: waiting for start handshake completion + * @ISSEI_RST_STATE_CLIENT_ENUM: waiting for client enumeration + * @ISSEI_RST_STATE_DONE: reset flow is done + * @ISSEI_RST_STATE_DISABLED: flow is disabled + */ +enum issei_rst_state { + ISSEI_RST_STATE_INIT, + ISSEI_RST_STATE_HW_READY, + ISSEI_RST_STATE_SETUP, + ISSEI_RST_STATE_START, + ISSEI_RST_STATE_CLIENT_ENUM, + ISSEI_RST_STATE_DONE, + ISSEI_RST_STATE_DISABLED, +}; + +/** + * struct issei_device - issei device + * @parent: parent device object + * @dev: associated device object + * @cdev: character device + * @minor: allocated minor number + * @wait_has_data: wait queue for data + * @has_data: there are data to process + * @power_down: device is powering down + * @wait_rst_state: waitqueue for reset state processing + * @rst_state: reset state + * @fw_protocol_ver: protocol version + * @fw_version: firmware version + * @process_thread: worker thread + * @reset_count: number of consecutive link reset attempts + * @all_reset_count: cumilative number of link reset attempts + * @client_lock: mutex to protect client lists and write queue + * @host_client_list: host clients list + * @host_client_last_id: last allocated host client id + * @host_client_count: number of active host clients + * @fw_client_list: firmware clients list + * @write_queue: write queue + * @last_write_ts: last write timestamp + * @dma: DMA memory configuration + * @ops: hardware operations + * @hw: hw-specific data + */ +struct issei_device { + struct device *parent; + struct device dev; + struct cdev *cdev; + u32 minor; + wait_queue_head_t wait_has_data; + bool has_data; + bool power_down; + wait_queue_head_t wait_rst_state; + enum issei_rst_state rst_state; + u16 fw_protocol_ver; + u16 fw_version[4]; + /* reset flow */ + struct task_struct *process_thread; + u8 reset_count; + u8 all_reset_count; + /* clients */ + struct mutex client_lock; + struct list_head host_client_list; + u16 host_client_last_id; + u8 host_client_count; + struct kset *fw_clients; + struct list_head fw_client_list; + struct list_head write_queue; + ktime_t last_write_ts; + struct issei_dma dma; + const struct issei_hw_ops *ops; + char hw[]; +}; + +static inline void issei_poke_process_thread(struct issei_device *idev) +{ + WRITE_ONCE(idev->has_data, true); + wake_up_interruptible(&idev->wait_has_data); +} +#endif /* _ISSEI_DEV_H_ */ diff --git a/include/uapi/linux/issei.h b/include/uapi/linux/issei.h new file mode 100644 index 000000000000..3bfb89330265 --- /dev/null +++ b/include/uapi/linux/issei.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (C) 2023-2026 Intel Corporation + * Intel Silicon Security Engine Interface (ISSEI) Linux driver: + * ISSEI Interface Header + */ +#ifndef _LINUX_ISSEI_H +#define _LINUX_ISSEI_H + +#include +#include + +/* + * This ioctl is used to associate the current file descriptor with a + * FW Client (given by UUID). This opens a communication channel + * between a host client and a FW client. From this point every read and write + * will communicate with the associated FW client. + * The communication between the clients can be terminated by + * IOCTL_ISSEI_DISCONNECT_CLIENT IOCTL or by + * closing the file descriptor (file_operation release()). + * + * The ioctl argument is a struct with a union that contains + * the input parameter and the output parameter for this ioctl. + * + * The input parameter is UUID of the FW Client. + * The output parameter is the properties of the FW client + * (FW protocol version, max message size and client flags). + */ +#define IOCTL_ISSEI_CONNECT_CLIENT \ + _IOWR('H', 0x01, struct issei_connect_client_data) + +/** + * struct issei_client - ISSEI client information structure + * @max_msg_length: maximum message length supported by the firmware client (in bytes) + * @protocol_version: protocol version reported by the firmware client + * @reserved1: reserved + * @flags: flag bitmask reported by the firmware client + * @reserved2: reserved + */ +struct issei_client { + __u32 max_msg_length; + __u8 protocol_version; + __u8 reserved1[3]; + __u32 flags; + __u32 reserved2; +}; + +#define ISSEI_IOCTL_UUID_LEN 16 + +/** + * struct issei_connect_client_data - ioctl Connect Client Data structure + * @in_client_uuid: unique id of the firmware client to connect to (from user space to kernel) + * @out_client_properties: connected firmware client properties (from kernel to user space) + */ +struct issei_connect_client_data { + union { + __u8 in_client_uuid[ISSEI_IOCTL_UUID_LEN]; + struct issei_client out_client_properties; + }; +}; + +/* + * This ioctl is used to terminate association between + * the host client and the FW client. + */ +#define IOCTL_ISSEI_DISCONNECT_CLIENT \ + _IO('H', 0x02) + +#endif /* _LINUX_ISSEI_H */ From 2207e8d48088e7358dd6b08163dac95730cc1c66 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Wed, 13 May 2026 17:18:43 +0300 Subject: [PATCH 249/513] issei: add firmware and host clients implementation, finish character device Add the core implementation for firmware and host client management within the ISSEI (Intel Silicon Security Engine Interface) subsystem support for a character device to expose the ISSEI HECI interface to user space. The firmware client (fw_client) and host client (host_client) modules are responsible for managing communication between the host software and the firmware. The character device provides a communication channel for user-space applications to interact with the firmware on the platform. The client modules enable the ISSEI driver to manage multiple host clients communicating with corresponding firmware clients, facilitating data transfers and control operations over the HECI interface. The character device allows user-space applications to establish connections to firmware clients using UUIDs, exchange messages, and control the communication flow using standard file operation calls. Reviewed-by: Karol Wachowski Co-developed-by: Vitaly Lubart Signed-off-by: Vitaly Lubart Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260513-issei-for-upstream-v1-2-f590038678f9@intel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-issei | 73 +++ MAINTAINERS | 1 + drivers/misc/issei/Makefile | 2 + drivers/misc/issei/cdev.c | 227 +++++++++ drivers/misc/issei/fw_client.c | 240 +++++++++ drivers/misc/issei/fw_client.h | 45 ++ drivers/misc/issei/host_client.c | 519 ++++++++++++++++++++ drivers/misc/issei/host_client.h | 75 +++ 8 files changed, 1182 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-issei create mode 100644 drivers/misc/issei/fw_client.c create mode 100644 drivers/misc/issei/fw_client.h create mode 100644 drivers/misc/issei/host_client.c create mode 100644 drivers/misc/issei/host_client.h diff --git a/Documentation/ABI/testing/sysfs-class-issei b/Documentation/ABI/testing/sysfs-class-issei new file mode 100644 index 000000000000..73a01f4627cb --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-issei @@ -0,0 +1,73 @@ +What: /sys/class/issei/ +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: + The issei/ class sub-directory belongs to issei device class + +What: /sys/class/issei/issei/ +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: + The /sys/class/issei/isseiN directory is created for + each probed issei device + +What: /sys/class/issei/issei/fw_ver +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: Display the ISSE firmware version. + + The version of the ISSE firmware is in format: + .... + +What: /sys/class/issei/issei/fw_clients +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: + The fw_clients directory stores all firmware clients on the + probed issei device + +What: /sys/class/issei/issei/fw_clients/ +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: + The /sys/class/issei/isseiN/fw_client/M directory is created for + each firmware client on the probed issei device where M is the + id of firmware client. + +What: /sys/class/issei/issei/fw_clients//id +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: Displays id of the firmware client + + The id of firmware client is it's number in client enumeration order, + starting from 1. + +What: /sys/class/issei/issei/fw_clients//uuid +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: Displays uuid of the firmware client + + The universally unique identifier of the firmware client + +What: /sys/class/issei/issei/fw_clients//mtu +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: Displays maximum transmission unit of the firmware client + + The maximum transmission unit (in bytes) used by the firmware client. + +What: /sys/class/issei/issei/fw_clients//ver +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: Displays version of the firmware client + + The version of the firmware client diff --git a/MAINTAINERS b/MAINTAINERS index b6bf86f9f8ab..a3edb1fa7954 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13363,6 +13363,7 @@ F: tools/testing/selftests/drivers/sdsi/ INTEL SILICON SECURITY ENGINE INTERFACE (ISSEI) M: Alexander Usyskin S: Supported +F: Documentation/ABI/testing/sysfs-class-issei F: Documentation/driver-api/issei/issei.rst F: drivers/misc/issei/ F: include/uapi/linux/issei.h diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile index f13bcf3e1699..1471ed99d619 100644 --- a/drivers/misc/issei/Makefile +++ b/drivers/misc/issei/Makefile @@ -5,3 +5,5 @@ ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE='"INTEL_SSEI"' obj-$(CONFIG_INTEL_SSEI) += issei.o issei-objs += cdev.o issei-objs += dma.o +issei-objs += fw_client.o +issei-objs += host_client.o diff --git a/drivers/misc/issei/cdev.c b/drivers/misc/issei/cdev.c index d3d53dad088e..b7d65e2d0813 100644 --- a/drivers/misc/issei/cdev.c +++ b/drivers/misc/issei/cdev.c @@ -2,6 +2,7 @@ /* Copyright (C) 2023-2026 Intel Corporation */ #include #include +#include #include #include #include @@ -11,11 +12,15 @@ #include #include #include +#include +#include +#include #include #include #include #include "issei_dev.h" +#include "host_client.h" #include "cdev.h" struct class *issei_class; @@ -25,6 +30,221 @@ static dev_t issei_devt; static DEFINE_XARRAY_ALLOC(issei_minor_xa); +static int issei_open(struct inode *inode, struct file *fp) +{ + struct issei_host_client *cl; + struct issei_device *idev; + + xa_lock(&issei_minor_xa); + idev = xa_load(&issei_minor_xa, iminor(inode)); + if (idev) + get_device(&idev->dev); + xa_unlock(&issei_minor_xa); + if (!idev) + return -ENODEV; + + cl = issei_cl_create(idev, fp); + if (IS_ERR(cl)) { + put_device(&idev->dev); + return PTR_ERR(cl); + } + fp->private_data = cl; + + return nonseekable_open(inode, fp); +} + +static int issei_release(struct inode *inode, struct file *fp) +{ + struct issei_host_client *cl = fp->private_data; + struct issei_device *idev = cl->idev; + + issei_cl_remove(cl); + put_device(&idev->dev); + + return 0; +} + +static long issei_ioctl(struct file *file, unsigned int cmd, unsigned long data) +{ + struct issei_host_client *cl = file->private_data; + struct issei_connect_client_data conn; + struct issei_device *idev = cl->idev; + int ret; + + switch (cmd) { + case IOCTL_ISSEI_CONNECT_CLIENT: + dev_dbg(&idev->dev, "IOCTL_ISSEI_CONNECT_CLIENT\n"); + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return -ENODEV; + } + + if (copy_from_user(&conn, (char __user *)data, sizeof(conn))) { + dev_dbg(&idev->dev, "failed to copy data from userland\n"); + return -EFAULT; + } + + ret = issei_cl_connect(cl, (uuid_t *)&conn.in_client_uuid, + &conn.out_client_properties.max_msg_length, + &conn.out_client_properties.protocol_version, + &conn.out_client_properties.flags); + if (ret) + return ret; + + if (copy_to_user((char __user *)data, &conn, sizeof(conn))) { + dev_dbg(&idev->dev, "failed to copy data to userland\n"); + issei_cl_disconnect(cl); + return -EFAULT; + } + return 0; + + case IOCTL_ISSEI_DISCONNECT_CLIENT: + dev_dbg(&idev->dev, "IOCTL_ISSEI_DISCONNECT_CLIENT\n"); + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return -ENODEV; + } + + return issei_cl_disconnect(cl); + + default: + return -ENOIOCTLCMD; + } +} + +static ssize_t issei_write(struct file *file, const char __user *ubuf, + size_t length, loff_t *offset) +{ + struct issei_host_client *cl = file->private_data; + struct issei_device *idev = cl->idev; + ssize_t ret; + + if (!length) + return 0; + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return -EBUSY; + } + + /* sanity check */ + if (length > idev->dma.length.h2f) { + dev_dbg(&idev->dev, "Write is too big %zu > %zu\n", + length, idev->dma.length.h2f); + return -EFBIG; + } + + u8 *buf __free(kfree) = memdup_user(ubuf, length); + if (IS_ERR(buf)) { + dev_dbg(&idev->dev, "failed to copy data from userland\n"); + return PTR_ERR(buf); + } + + do { + ret = issei_cl_write(cl, buf, length); + if (ret < 0 && ret != -EAGAIN) + return ret; + /* buf is consumed by issei_cl_write on success */ + if (ret >= 0) + retain_and_null_ptr(buf); + if (wait_event_interruptible(cl->write_wait, issei_cl_check_write(cl) != 1)) { + issei_cl_clean_all_wbuf(cl); + if (signal_pending(current)) + return -EINTR; + return -ERESTARTSYS; + } + } while (ret == -EAGAIN); + + return ret; +} + +static ssize_t issei_read(struct file *file, char __user *ubuf, + size_t length, loff_t *offset) +{ + struct issei_host_client *cl = file->private_data; + struct issei_device *idev = cl->idev; + u8 *data = NULL; + ssize_t ret; + + if (!length) + return 0; + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return -EBUSY; + } + + /* sanity check */ + if (length > idev->dma.length.f2h) { + dev_dbg(&idev->dev, "Read is too big %zu > %zu\n", + length, idev->dma.length.f2h); + return -EFBIG; + } + + ret = issei_cl_read(cl, &data, length); + if (ret < 0) { + if (ret != -ENOENT) + return ret; + + if (wait_event_interruptible(cl->read_wait, issei_cl_check_read(cl) != 0)) { + if (signal_pending(current)) + return -EINTR; + return -ERESTARTSYS; + } + + ret = issei_cl_read(cl, &data, length); + if (ret < 0) + return ret; + } + + if (copy_to_user(ubuf, data, ret)) { + dev_dbg(&idev->dev, "failed to copy data to userland\n"); + ret = -EFAULT; + } else { + *offset = 0; + } + + kfree(data); + + return ret; +} + +static __poll_t issei_poll(struct file *file, poll_table *wait) +{ + __poll_t req_events = poll_requested_events(wait); + struct issei_host_client *cl = file->private_data; + struct issei_device *idev = cl->idev; + __poll_t mask = 0; + int ret; + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return EPOLLERR; + } + + if (req_events & (EPOLLIN | EPOLLRDNORM)) { + poll_wait(file, &cl->read_wait, wait); + ret = issei_cl_check_read(cl); + if (ret == 1) + mask |= EPOLLIN | EPOLLRDNORM; + else if (ret < 0) + mask |= EPOLLERR; + } + + if (req_events & (EPOLLOUT | EPOLLWRNORM)) { + poll_wait(file, &cl->write_wait, wait); + ret = issei_cl_check_write(cl); + if (ret == 0) + mask |= EPOLLOUT | EPOLLWRNORM; + else if (ret < 0) + mask |= EPOLLERR; + } + + return mask; +} + static ssize_t fw_ver_show(struct device *device, struct device_attribute *attr, char *buf) { @@ -43,6 +263,13 @@ ATTRIBUTE_GROUPS(issei); static const struct file_operations issei_fops = { .owner = THIS_MODULE, + .open = issei_open, + .unlocked_ioctl = issei_ioctl, + .compat_ioctl = compat_ptr_ioctl, + .write = issei_write, + .read = issei_read, + .release = issei_release, + .poll = issei_poll, }; static void issei_device_release(struct device *dev) diff --git a/drivers/misc/issei/fw_client.c b/drivers/misc/issei/fw_client.c new file mode 100644 index 000000000000..b8e48dbf1c9c --- /dev/null +++ b/drivers/misc/issei/fw_client.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "issei_dev.h" +#include "fw_client.h" + +/* + * Specific attribute handlers for fw_clients kset. + * Provide only show function as all fw_client attributes are read-only. + */ + +struct issei_fw_cl_attr { + struct attribute attr; + ssize_t (*show)(struct issei_fw_client *fw_cl, const struct issei_fw_cl_attr *attr, + char *buf); +}; +#define to_issei_fw_cl_attr(x) container_of_const(x, struct issei_fw_cl_attr, attr) + +static ssize_t fw_cl_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + const struct issei_fw_cl_attr *issei_attr; + struct issei_fw_client *fw_cl; + + issei_attr = to_issei_fw_cl_attr(attr); + fw_cl = to_issei_fw_client(kobj); + + if (!issei_attr->show) + return -EIO; + + return issei_attr->show(fw_cl, issei_attr, buf); +} + +static const struct sysfs_ops fw_cl_sysfs_ops = { + .show = fw_cl_attr_show, +}; + +#define FW_CL_ATTR_RO(_name) \ + struct issei_fw_cl_attr fw_cl_attr_##_name = __ATTR_RO(_name) + +/* fw_client attributes */ + +static ssize_t id_show(struct issei_fw_client *fw_cl, + const struct issei_fw_cl_attr *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", fw_cl->id); +} +static FW_CL_ATTR_RO(id); + +static ssize_t ver_show(struct issei_fw_client *fw_cl, + const struct issei_fw_cl_attr *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", fw_cl->ver); +} +static FW_CL_ATTR_RO(ver); + +static ssize_t uuid_show(struct issei_fw_client *fw_cl, + const struct issei_fw_cl_attr *attr, char *buf) +{ + return sysfs_emit(buf, "%pUb\n", &fw_cl->uuid); +} +static FW_CL_ATTR_RO(uuid); + +static ssize_t mtu_show(struct issei_fw_client *fw_cl, + const struct issei_fw_cl_attr *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", fw_cl->mtu); +} +static FW_CL_ATTR_RO(mtu); + +static const struct attribute *const fw_cl_attrs[] = { + &fw_cl_attr_id.attr, + &fw_cl_attr_ver.attr, + &fw_cl_attr_uuid.attr, + &fw_cl_attr_mtu.attr, + NULL, +}; + +static const struct attribute_group fw_cl_group = { + .attrs_const = fw_cl_attrs, +}; +__ATTRIBUTE_GROUPS(fw_cl); + +static void issei_fw_cl_init(struct issei_fw_client *fw_cl, u16 id, u8 ver, const uuid_t *uuid, + u32 mtu, u32 flags) +{ + INIT_LIST_HEAD(&fw_cl->list); + fw_cl->id = id; + fw_cl->ver = ver; + fw_cl->uuid = *uuid; + fw_cl->mtu = mtu; + fw_cl->flags = flags; +} + +static void fw_cl_release(struct kobject *kobj) +{ + struct issei_fw_client *fw_cl = to_issei_fw_client(kobj); + + kfree(fw_cl); +} + +static const struct kobj_type fw_client_ktype = { + .sysfs_ops = &fw_cl_sysfs_ops, + .release = fw_cl_release, + .default_groups = fw_cl_groups, +}; + +/** + * issei_fw_cl_create - create firmware client object and add to list + * @idev: issei device object + * @id: firmware client id + * @ver: firmware client version + * @uuid: firmware client unique id + * @mtu: firmware client maximum message size + * @flags: firmware client flags + * + * Should be called under idev->client_lock + * + * Return: pointer to newly created object on success, ERR_PTR on failure + */ +struct issei_fw_client *issei_fw_cl_create(struct issei_device *idev, u16 id, u8 ver, + const uuid_t *uuid, u32 mtu, u32 flags) +{ + int ret; + struct issei_fw_client *fw_cl = kzalloc_obj(*fw_cl); + + if (!fw_cl) + return ERR_PTR(-ENOMEM); + + WARN_ON(!mutex_is_locked(&idev->client_lock)); + + issei_fw_cl_init(fw_cl, id, ver, uuid, mtu, flags); + fw_cl->kobj.kset = idev->fw_clients; + + ret = kobject_init_and_add(&fw_cl->kobj, &fw_client_ktype, NULL, "%u", id); + if (ret) { + kobject_put(&fw_cl->kobj); + return ERR_PTR(ret); + } + + list_add_tail(&fw_cl->list, &idev->fw_client_list); + + dev_dbg(&idev->dev, "FW client %pUb created\n", uuid); + + kobject_uevent(&fw_cl->kobj, KOBJ_ADD); + + return fw_cl; +} + +static void __issei_fw_cl_remove(struct issei_device *idev, struct issei_fw_client *fw_cl) +{ + WARN(fw_cl->cl, "Removing connected client!\n"); + + dev_dbg(&idev->dev, "FW client %pUb will be removed\n", &fw_cl->uuid); + + list_del(&fw_cl->list); + kobject_put(&fw_cl->kobj); +} + +/** + * issei_fw_cl_remove_all - remove all firmware client objects + * @idev: issei device object + */ +void issei_fw_cl_remove_all(struct issei_device *idev) +{ + struct issei_fw_client *fw_cl, *next; + + guard(mutex)(&idev->client_lock); + + list_for_each_entry_safe(fw_cl, next, &idev->fw_client_list, list) + __issei_fw_cl_remove(idev, fw_cl); +} + +/** + * issei_fw_cl_find_by_uuid - find firmware client by uuid + * @idev: issei device object + * @uuid: uuid to search by it + * + * Should be called under idev->client_lock + * + * Return: pointer to firmware client object if found, NULL on failure + */ +struct issei_fw_client *issei_fw_cl_find_by_uuid(struct issei_device *idev, const uuid_t *uuid) +{ + struct issei_fw_client *fw_cl; + + WARN_ON(!mutex_is_locked(&idev->client_lock)); + + list_for_each_entry(fw_cl, &idev->fw_client_list, list) { + if (uuid_equal(&fw_cl->uuid, uuid)) { + kobject_get(&fw_cl->kobj); + return fw_cl; + } + } + return NULL; +} + +/** + * issei_fw_cl_connect - connect firmware and host client + * @fw_cl: firmware client + * @cl: host client + * + * Should be called under idev->client_lock + * + * Return: 0 on success, -EBUSY if already connected + */ +int issei_fw_cl_connect(struct issei_fw_client *fw_cl, struct issei_host_client *cl) +{ + if (fw_cl->cl) + return -EBUSY; + + kobject_get(&fw_cl->kobj); + fw_cl->cl = cl; + return 0; +} + +/** + * issei_fw_cl_disconnect - disconnect firmware and host client + * @fw_cl: firmware client + * + * Should be called under idev->client_lock + */ +void issei_fw_cl_disconnect(struct issei_fw_client *fw_cl) +{ + WARN_ON(!fw_cl->cl); + + fw_cl->cl = NULL; + kobject_put(&fw_cl->kobj); +} diff --git a/drivers/misc/issei/fw_client.h b/drivers/misc/issei/fw_client.h new file mode 100644 index 000000000000..377f733d7e91 --- /dev/null +++ b/drivers/misc/issei/fw_client.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_FW_CLIENT_H_ +#define _ISSEI_FW_CLIENT_H_ + +#include +#include +#include +#include + +struct issei_device; +struct issei_host_client; + +/** + * struct issei_fw_client - represents firmware queue + * @kobj: associated kobject + * @list: link in firmware clients list + * @id: firmware client id + * @ver: firmware client version + * @uuid: firmware client protocol id + * @mtu: firmware client maximum buffer size + * @flags: firmware client flags + * @cl: pointer to host client, if connected + */ +struct issei_fw_client { + struct kobject kobj; + struct list_head list; + u16 id; + u8 ver; + uuid_t uuid; + u32 mtu; + u32 flags; + struct issei_host_client *cl; +}; +#define to_issei_fw_client(x) container_of(x, struct issei_fw_client, kobj) + +struct issei_fw_client *issei_fw_cl_create(struct issei_device *idev, u16 id, u8 ver, + const uuid_t *uuid, u32 mtu, u32 flags); +void issei_fw_cl_remove_all(struct issei_device *idev); +struct issei_fw_client *issei_fw_cl_find_by_uuid(struct issei_device *idev, const uuid_t *uuid); + +int issei_fw_cl_connect(struct issei_fw_client *fw_cl, struct issei_host_client *cl); +void issei_fw_cl_disconnect(struct issei_fw_client *fw_cl); + +#endif /* _ISSEI_FW_CLIENT_H_ */ diff --git a/drivers/misc/issei/host_client.c b/drivers/misc/issei/host_client.c new file mode 100644 index 000000000000..8f36d1c319ec --- /dev/null +++ b/drivers/misc/issei/host_client.c @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fw_client.h" +#include "host_client.h" +#include "issei_dev.h" + +static inline u8 __issei_cl_fw_id(const struct issei_host_client *cl) +{ + return cl->fw_cl ? cl->fw_cl->id : 0; +} + +#define ISSEI_CL_FMT "cl:host=%02d fw=%02d " + +#define cl_dbg(_dev_, _cl_, format, arg...) do { \ + struct issei_host_client *_l_cl_ = _cl_; \ + dev_dbg(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \ + __issei_cl_fw_id(_l_cl_), ##arg); \ +} while (0) + +#define cl_warn(_dev_, _cl_, format, arg...) do { \ + struct issei_host_client *_l_cl_ = _cl_; \ + dev_warn(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \ + __issei_cl_fw_id(_l_cl_), ##arg); \ +} while (0) + +#define cl_err(_dev_, _cl_, format, arg...) do { \ + struct issei_host_client *_l_cl_ = _cl_; \ + dev_err(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \ + __issei_cl_fw_id(_l_cl_), ##arg); \ +} while (0) + +static void __issei_cl_clean_wbuf(struct issei_write_buf *wbuf) +{ + list_del(&wbuf->list); + kfree(wbuf->data); + kfree(wbuf); +} + +static void __issei_cl_release_rbuf(struct issei_host_client *cl) +{ + cl->read_data = NULL; + cl->read_data_size = 0; +} + +static void __issei_cl_clean_rbuf(struct issei_host_client *cl) +{ + kfree(cl->read_data); + __issei_cl_release_rbuf(cl); +} + +static void __issei_cl_clean_all_wbuf(struct issei_device *idev, struct issei_host_client *cl) +{ + struct issei_write_buf *wbuf, *next; + + if (!cl->write_in_progress) + return; + list_for_each_entry_safe(wbuf, next, &idev->write_queue, list) { + if (wbuf->cl == cl) { + __issei_cl_clean_wbuf(wbuf); + break; + } + } + cl->write_in_progress = false; + /* synchronized under host client mutex */ + if (waitqueue_active(&cl->write_wait)) + wake_up_interruptible(&cl->write_wait); +} + +static struct issei_host_client *__issei_cl_by_id(struct issei_device *idev, u16 id) +{ + struct issei_host_client *cl; + + list_for_each_entry(cl, &idev->host_client_list, list) { + if (cl->id == id) + return cl; + } + return NULL; +} + +static void __issei_cl_disconnect(struct issei_device *idev, struct issei_host_client *cl) +{ + if (cl->state == ISSEI_HOST_CL_STATE_DISCONNECTED) + return; + + __issei_cl_clean_all_wbuf(idev, cl); + + if (!WARN_ON(!cl->fw_cl)) { + issei_fw_cl_disconnect(cl->fw_cl); + cl->fw_cl = NULL; + } + cl->state = ISSEI_HOST_CL_STATE_DISCONNECTED; + + if (cl->read_data) + __issei_cl_clean_rbuf(cl); + /* synchronized under host client mutex */ + if (waitqueue_active(&cl->read_wait)) + wake_up_interruptible(&cl->read_wait); + cl_dbg(idev, cl, "Disconnected\n"); +} + +static void __issei_cl_init(struct issei_host_client *cl, struct issei_device *idev, + u16 id, struct file *fp) +{ + INIT_LIST_HEAD(&cl->list); + cl->idev = idev; + cl->id = id; + cl->state = ISSEI_HOST_CL_STATE_DISCONNECTED; + cl->fp = fp; + init_waitqueue_head(&cl->write_wait); + init_waitqueue_head(&cl->read_wait); + __issei_cl_release_rbuf(cl); +} + +/** + * issei_cl_create - create the host client + * @idev: issei device + * @fp: file pointer to associate with host client + * + * Return: client pointer on success, ERR_PTR on error + */ +struct issei_host_client *issei_cl_create(struct issei_device *idev, struct file *fp) +{ + struct issei_host_client *cl; + u16 id; + + guard(mutex)(&idev->client_lock); + + if (idev->host_client_count == ISSEI_HOST_CLIENTS_MAX) { + dev_err(&idev->dev, "Maximum open clients %d is reached.\n", + ISSEI_HOST_CLIENTS_MAX); + return ERR_PTR(-EMFILE); + } + + do { + if (check_add_overflow(idev->host_client_last_id, 1, &id)) /* overflow */ + id = 1; + idev->host_client_last_id = id; + /* Not an endless loop as we have less clients then id's */ + } while (__issei_cl_by_id(idev, id)); + + cl = kzalloc_obj(*cl); + if (!cl) + return ERR_PTR(-ENOMEM); + + __issei_cl_init(cl, idev, id, fp); + list_add_tail(&cl->list, &idev->host_client_list); + idev->host_client_count++; + + cl_dbg(idev, cl, "Created\n"); + return cl; +} + +/** + * issei_cl_remove - disconnect and free the host client + * @cl: host client + */ +void issei_cl_remove(struct issei_host_client *cl) +{ + struct issei_device *idev; + + /* don't shout on error exit path */ + if (!cl) + return; + + idev = cl->idev; + + guard(mutex)(&idev->client_lock); + + idev->host_client_count--; + list_del(&cl->list); + + __issei_cl_disconnect(idev, cl); + + cl_dbg(idev, cl, "Removed\n"); + kfree(cl); +} + +/** + * issei_cl_connect - connect between FW and host client + * @cl: host client + * @uuid: FW client unique ID + * @mtu: memory for FW client max message size + * @ver: memory for FW client version + * @flags: memory for FW client flags + * + * Search for firmware client by UUID and connect it to provided + * host client, if not already connected to some client. + * + * Return: 0 on success, <0 on error + */ +int issei_cl_connect(struct issei_host_client *cl, const uuid_t *uuid, u32 *mtu, u8 *ver, + u32 *flags) +{ + struct issei_device *idev = cl->idev; + struct issei_fw_client *fw_cl; + int ret; + + guard(mutex)(&idev->client_lock); + + if (cl->state == ISSEI_HOST_CL_STATE_CONNECTED) { + cl_err(idev, cl, "Already connected\n"); + return -EISCONN; + } + + fw_cl = issei_fw_cl_find_by_uuid(idev, uuid); + if (!fw_cl) { + cl_dbg(idev, cl, "FW client %pUb not found\n", uuid); + return -ENOTTY; + } + + ret = issei_fw_cl_connect(fw_cl, cl); /* calls kobject_get for fw_cl on success */ + kobject_put(&fw_cl->kobj); + if (ret) { + cl_err(idev, cl, "FW client is already connected ret = %d\n", ret); + return ret; + } + + cl->fw_cl = fw_cl; + cl->state = ISSEI_HOST_CL_STATE_CONNECTED; + + *mtu = fw_cl->mtu; + *ver = fw_cl->ver; + *flags = fw_cl->flags; + cl_dbg(idev, cl, "Connected\n"); + return 0; +} + +/** + * issei_cl_disconnect - disconnect between FW and host client + * @cl: host client + * + * Return: 0 on success, -ENOTCONN if not connected + */ +int issei_cl_disconnect(struct issei_host_client *cl) +{ + struct issei_device *idev = cl->idev; + + guard(mutex)(&idev->client_lock); + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + __issei_cl_disconnect(idev, cl); + return 0; +} + +/** + * issei_cl_all_disconnect - disconnect all FW clients + * @idev: issei device + */ +void issei_cl_all_disconnect(struct issei_device *idev) +{ + struct issei_host_client *cl; + + guard(mutex)(&idev->client_lock); + + list_for_each_entry(cl, &idev->host_client_list, list) + __issei_cl_disconnect(idev, cl); +} + +/** + * issei_cl_write - enqueue write request + * @cl: host client + * @buf: buffer to write + * @buf_size: buffer size + * + * Add write request to the write queue and wakes working thread. + * This call takes ownership of buf memory, if succeeded. + * + * Return: size of data on success, <0 on error + */ +ssize_t issei_cl_write(struct issei_host_client *cl, const u8 *buf, size_t buf_size) +{ + struct issei_device *idev = cl->idev; + struct issei_write_buf *wbuf; + + guard(mutex)(&idev->client_lock); + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + + if (cl->write_in_progress) { + cl_dbg(idev, cl, "Another write is in progress\n"); + return -EAGAIN; + } + + if (buf_size > cl->fw_cl->mtu) { + cl_err(idev, cl, "Write is too big %zu > %u\n", buf_size, cl->fw_cl->mtu); + return -EFBIG; + } + + wbuf = kmalloc_obj(*wbuf); + if (!wbuf) + return -ENOMEM; + wbuf->cl = cl; + wbuf->data = buf; + wbuf->data_size = buf_size; + list_add_tail(&wbuf->list, &idev->write_queue); + cl->write_in_progress = true; + cl_dbg(idev, cl, "Write queued %zu bytes\n", buf_size); + + issei_poke_process_thread(idev); + + return buf_size; +} + +/** + * issei_cl_write_from_queue - writes first request from queue to firmware + * @idev: issei device + * + * Tries to write first request from the write queue to firmware. + * Releases buf memory, if succeeded. + * + * Return: 0 on success, <0 on error + */ +int issei_cl_write_from_queue(struct issei_device *idev) +{ + struct issei_write_buf *wbuf; + struct issei_dma_data data; + struct issei_host_client *cl; + int ret; + + guard(mutex)(&idev->client_lock); + + wbuf = list_first_entry_or_null(&idev->write_queue, struct issei_write_buf, list); + if (!wbuf) + return 0; + + cl = wbuf->cl; + + data.fw_id = cl->fw_cl->id; + data.host_id = cl->id; + data.flags = 0; + data.status = 0; + data.length = wbuf->data_size; + data.buf = (void *)wbuf->data; + ret = issei_dma_write(idev, &data); + if (ret == -EBUSY) + return 0; + if (ret == -EIO) + return ret; + if (ret >= 0) + idev->ops->irq_write_generate(idev); + cl->write_in_progress = false; + /* synchronized under host client mutex */ + if (waitqueue_active(&cl->write_wait)) + wake_up_interruptible(&cl->write_wait); + cl_dbg(idev, cl, "Write %zu bytes\n", wbuf->data_size); + __issei_cl_clean_wbuf(wbuf); + return 0; +} + +static struct issei_host_client *__issei_cl_read_buf_check(struct issei_device *idev, u16 fw_id, + u16 host_id, size_t buf_size) +{ + struct issei_host_client *cl; + + cl = __issei_cl_by_id(idev, host_id); + if (!cl) { + dev_dbg(&idev->dev, "No client %u\n", host_id); + return ERR_PTR(-ENOTTY); + } + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) { + cl_dbg(idev, cl, "Not connected\n"); + return ERR_PTR(-ENODEV); + } + if (cl->fw_cl->id != fw_id) { + cl_dbg(idev, cl, "Wrong firmware client %u ?= %u\n", cl->fw_cl->id, fw_id); + return ERR_PTR(-ENODEV); + } + + if (buf_size > cl->fw_cl->mtu) { + cl_err(idev, cl, "Read is too big %zu > %u\n", buf_size, cl->fw_cl->mtu); + __issei_cl_disconnect(idev, cl); + return NULL; + } + + if (cl->read_data) { + cl_err(idev, cl, "Previous data was not read by user-space, disconnecting\n"); + __issei_cl_disconnect(idev, cl); + return NULL; + } + + return cl; +} + +/** + * issei_cl_read_buf - process data from firmware + * @idev: issei device + * @fw_id: firmware client id + * @host_id: host client id + * @buf: buffer with data + * @buf_size: buffer size + * + * Puts data from firmware into provided host client storage. + * Free buffer or consume it. + * + * Return: 0 on success or recoverable error, <0 on unrecoverable error + */ +int issei_cl_read_buf(struct issei_device *idev, u16 fw_id, u16 host_id, u8 *buf, size_t buf_size) +{ + struct issei_host_client *cl; + + guard(mutex)(&idev->client_lock); + + cl = __issei_cl_read_buf_check(idev, fw_id, host_id, buf_size); + if (IS_ERR_OR_NULL(cl)) { + kfree(buf); + return PTR_ERR(cl); + } + + cl->read_data = buf; + cl->read_data_size = buf_size; + + /* synchronized under host client mutex */ + if (waitqueue_active(&cl->read_wait)) + wake_up_interruptible(&cl->read_wait); + cl_dbg(idev, cl, "Read %zu bytes\n", buf_size); + + return 0; +} + +/** + * issei_cl_read - read data from queue to provided buffer + * @cl: host client + * @buf: buffer to store data + * @buf_size: buffer size + * + * Tries to take data buffer from client and return it to caller. + * The caller receives ownership of the data buffer. + * + * Return: read data size on success, <0 on error + */ +ssize_t issei_cl_read(struct issei_host_client *cl, u8 **buf, size_t buf_size) +{ + struct issei_device *idev = cl->idev; + size_t read_data_size; + + guard(mutex)(&idev->client_lock); + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + + if (!cl->read_data) + return -ENOENT; + + if (cl->read_data_size > buf_size) { + cl_err(idev, cl, "Buffer is too small %zu > %zu\n", + cl->read_data_size, buf_size); + return -EFBIG; + } + + *buf = cl->read_data; + read_data_size = cl->read_data_size; + cl_dbg(idev, cl, "Read by client %zu bytes\n", read_data_size); + __issei_cl_release_rbuf(cl); + return read_data_size; +} + +/** + * issei_cl_check_read - check if client has data to read + * + * @cl: host client + * + * Return: 1 - data available, 0 - no data, < 0 on error + */ +int issei_cl_check_read(struct issei_host_client *cl) +{ + struct issei_device *idev = cl->idev; + + guard(mutex)(&idev->client_lock); + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + if (!cl->read_data) + return 0; + return 1; +} + +/** + * issei_cl_check_write - check if client is ready to write + * + * @cl: host client + * + * Return: 1 - can not write, 0 - can write, < 0 on error + */ +int issei_cl_check_write(struct issei_host_client *cl) +{ + struct issei_device *idev = cl->idev; + + guard(mutex)(&idev->client_lock); + + if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + if (cl->write_in_progress) + return 1; + return 0; +} + +void issei_cl_clean_all_wbuf(struct issei_host_client *cl) +{ + struct issei_device *idev = cl->idev; + + guard(mutex)(&idev->client_lock); + + __issei_cl_clean_all_wbuf(idev, cl); +} diff --git a/drivers/misc/issei/host_client.h b/drivers/misc/issei/host_client.h new file mode 100644 index 000000000000..05e2f4ade9c3 --- /dev/null +++ b/drivers/misc/issei/host_client.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HOST_CLIENT_H_ +#define _ISSEI_HOST_CLIENT_H_ + +#include +#include +#include + +struct file; + +struct issei_device; +struct issei_fw_client; + +/** + * enum issei_host_client_state - host client states + * @ISSEI_HOST_CL_STATE_DISCONNECTED: host client is disconnected + * @ISSEI_HOST_CL_STATE_CONNECTED: host client is connected + */ +enum issei_host_client_state { + ISSEI_HOST_CL_STATE_DISCONNECTED, + ISSEI_HOST_CL_STATE_CONNECTED, +}; + +/** + * struct issei_host_client - represents host client + * @list: link in host clients list + * @idev: issei parent device + * @id: host client id + * @fp: file associated with client + * + * @write_wait: waitqueue for pending write data + * @write_in_progress: indicator for write in process + * + * @state: host client state + * @fw_cl: pointer to firmware client, if connected + * + * @read_wait: waitqueue for read object + * @read_data: received data pointer + * @read_data_size: received data size + */ +struct issei_host_client { + struct list_head list; + struct issei_device *idev; + u16 id; + const struct file *fp; + + wait_queue_head_t write_wait; + bool write_in_progress; + + enum issei_host_client_state state; + struct issei_fw_client *fw_cl; + + wait_queue_head_t read_wait; + u8 *read_data; + size_t read_data_size; +}; + +struct issei_host_client *issei_cl_create(struct issei_device *idev, struct file *fp); +void issei_cl_remove(struct issei_host_client *cl); + +int issei_cl_connect(struct issei_host_client *cl, const uuid_t *uuid, u32 *mtu, u8 *ver, + u32 *flags); +int issei_cl_disconnect(struct issei_host_client *cl); +void issei_cl_all_disconnect(struct issei_device *idev); + +ssize_t issei_cl_write(struct issei_host_client *cl, const u8 *buf, size_t buf_size); +int issei_cl_write_from_queue(struct issei_device *idev); +int issei_cl_read_buf(struct issei_device *idev, u16 fw_id, u16 host_id, u8 *buf, size_t buf_size); +ssize_t issei_cl_read(struct issei_host_client *cl, u8 **buf, size_t buf_size); +int issei_cl_check_read(struct issei_host_client *cl); +int issei_cl_check_write(struct issei_host_client *cl); +void issei_cl_clean_all_wbuf(struct issei_host_client *cl); + +#endif /* ISSEI_HOST_CLIENT_H_ */ From 7bd4b9991db20b0df5a8dfbef05920eabd40de28 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Wed, 13 May 2026 17:18:44 +0300 Subject: [PATCH 250/513] issei: implement main thread and ham messages Introduce the main thread and HECI Active Management (HAM) message handling for the ISSEI (Intel Silicon Security Engine Interface) subsystem. The main thread is responsible for managing the reset flow and processing messages, while the HAM message handling is crucial for initializing communication with the firmware and managing clients. With this implementation, the ISSEI driver is capable of performing the required initialization and management of communication between the host and the firmware. Reviewed-by: Karol Wachowski Co-developed-by: Vitaly Lubart Signed-off-by: Vitaly Lubart Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260513-issei-for-upstream-v1-3-f590038678f9@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/issei/Makefile | 2 + drivers/misc/issei/ham.c | 163 ++++++++++++++++++ drivers/misc/issei/ham.h | 20 +++ drivers/misc/issei/issei_dev.h | 3 + drivers/misc/issei/main.c | 296 +++++++++++++++++++++++++++++++++ 5 files changed, 484 insertions(+) create mode 100644 drivers/misc/issei/ham.c create mode 100644 drivers/misc/issei/ham.h create mode 100644 drivers/misc/issei/main.c diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile index 1471ed99d619..f21e0b985c94 100644 --- a/drivers/misc/issei/Makefile +++ b/drivers/misc/issei/Makefile @@ -7,3 +7,5 @@ issei-objs += cdev.o issei-objs += dma.o issei-objs += fw_client.o issei-objs += host_client.o +issei-objs += ham.o +issei-objs += main.o diff --git a/drivers/misc/issei/ham.c b/drivers/misc/issei/ham.c new file mode 100644 index 000000000000..17eae91f077d --- /dev/null +++ b/drivers/misc/issei/ham.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include + +#include "dma.h" +#include "fw_client.h" +#include "ham.h" +#include "hw_msg.h" +#include "issei_dev.h" + +static int __issei_ham_send_msg(struct issei_device *idev, u32 length, void *buf) +{ + struct issei_dma_data data = { }; + int ret; + + data.length = length; + data.buf = buf; + ret = issei_dma_write(idev, &data); + if (ret) + return ret; + return idev->ops->irq_write_generate(idev); +} + +/** + * issei_ham_send_start_req - send start request to firmware + * @idev: issei device object + * + * Return: 0 on success, <0 on failures + */ +int issei_ham_send_start_req(struct issei_device *idev) +{ + struct ham_start_message_req req; + + req.header.cmd = HAM_BUS_CMD_START_REQ; + req.supported_version = ISSEI_SUPPORTED_PROTOCOL_VER; + req.heci_capabilities_length = 0; + + return __issei_ham_send_msg(idev, sizeof(req), &req); +} + +/** + * issei_ham_send_clients_req - send clients request to firmware + * @idev: issei device object + * + * Return: 0 on success, <0 on failures + */ +int issei_ham_send_clients_req(struct issei_device *idev) +{ + struct ham_get_clients_req req; + + req.header.cmd = HAM_BUS_CMD_CLIENT_REQ; + + return __issei_ham_send_msg(idev, sizeof(req), &req); +} + +static int issei_ham_start_rsp(struct issei_device *idev, const u8 *buf, size_t length) +{ + struct ham_start_message_res *res = (struct ham_start_message_res *)buf; + int ret; + + if (idev->rst_state != ISSEI_RST_STATE_START) { + dev_err(&idev->dev, "Wrong state %d != %d\n", + idev->rst_state, ISSEI_RST_STATE_START); + return -EPROTO; + } + + if (length < sizeof(*res)) { + dev_err(&idev->dev, "Small start response size %zu < %zu\n", + length, sizeof(*res)); + return -EPROTO; + } + + if (length - sizeof(*res) != res->heci_capabilities_length) { + dev_err(&idev->dev, "Wrong start response size %zu != %u\n", + length - sizeof(*res), res->heci_capabilities_length); + return -EPROTO; + } + + memcpy(idev->fw_version, res->fw_version, sizeof(idev->fw_version)); + idev->fw_protocol_ver = res->supported_version; + dev_dbg(&idev->dev, "FW protocol: %u FW version %u.%u.%u.%u", idev->fw_protocol_ver, + idev->fw_version[0], idev->fw_version[1], + idev->fw_version[2], idev->fw_version[3]); + + ret = issei_ham_send_clients_req(idev); + if (ret == -EBUSY) + ret = 0; + + return ret; +} + +static int issei_ham_client_rsp(struct issei_device *idev, const u8 *buf, size_t length) +{ + struct ham_get_clients_res *res = (struct ham_get_clients_res *)buf; + struct ham_client_properties *client; + + if (idev->rst_state != ISSEI_RST_STATE_CLIENT_ENUM) { + dev_err(&idev->dev, "Wrong state %d != %d\n", + idev->rst_state, ISSEI_RST_STATE_CLIENT_ENUM); + return -EPROTO; + } + + if (length < sizeof(*res)) { + dev_err(&idev->dev, "Small response size %zu < %zu\n", length, sizeof(*res)); + return -EPROTO; + } + + if (length - sizeof(*res) != res->client_count * sizeof(struct ham_client_properties)) { + dev_err(&idev->dev, "Wrong response size %zu < %zu\n", + length - sizeof(*res), + res->client_count * sizeof(struct ham_client_properties)); + return -EPROTO; + } + + guard(mutex)(&idev->client_lock); + + for (size_t i = 0; i < res->client_count; i++) { + client = &res->clients_props[i]; + dev_dbg(&idev->dev, "client: id = %u ver = %u uuid = %pUb mtu = %u flags = %u", + client->client_number, client->protocol_ver, &client->client_uuid, + client->client_mtu, client->flags); + issei_fw_cl_create(idev, client->client_number, client->protocol_ver, + &client->client_uuid, client->client_mtu, client->flags); + } + return 0; +} + +static int __issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length) +{ + struct ham_bus_message *hdr = (struct ham_bus_message *)buf; + + switch (hdr->cmd) { + case HAM_BUS_CMD_START_RSP: + return issei_ham_start_rsp(idev, buf, length); + + case HAM_BUS_CMD_CLIENT_RSP: + return issei_ham_client_rsp(idev, buf, length); + + default: + dev_err(&idev->dev, "Unexpected command 0x%x", hdr->cmd); + return -EPROTO; + } +} + +/** + * issei_ham_process_ham_rsp - process response from firmware and release buffer + * @idev: issei device object + * @buf: response buffer + * @length: response buffer length + * + * Return: 0 on success, <0 on failures + */ +int issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length) +{ + int ret; + + ret = __issei_ham_process_ham_rsp(idev, buf, length); + kfree(buf); + return ret; +} diff --git a/drivers/misc/issei/ham.h b/drivers/misc/issei/ham.h new file mode 100644 index 000000000000..37ea64bb2920 --- /dev/null +++ b/drivers/misc/issei/ham.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HAM_H_ +#define _ISSEI_HAM_H_ + +#include + +struct issei_device; + +int issei_ham_send_start_req(struct issei_device *idev); +int issei_ham_send_clients_req(struct issei_device *idev); + +static inline bool issei_is_ham_rsp(u16 fw_id, u16 host_id) +{ + return fw_id == 0 && host_id == 0; +} + +int issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length); + +#endif /* _ISSEI_HAM_H_ */ diff --git a/drivers/misc/issei/issei_dev.h b/drivers/misc/issei/issei_dev.h index c742e7fe6cb6..b47c4a7c2da4 100644 --- a/drivers/misc/issei/issei_dev.h +++ b/drivers/misc/issei/issei_dev.h @@ -157,4 +157,7 @@ static inline void issei_poke_process_thread(struct issei_device *idev) WRITE_ONCE(idev->has_data, true); wake_up_interruptible(&idev->wait_has_data); } + +int issei_start(struct issei_device *idev); +void issei_stop(struct issei_device *idev); #endif /* _ISSEI_DEV_H_ */ diff --git a/drivers/misc/issei/main.c b/drivers/misc/issei/main.c new file mode 100644 index 000000000000..5987fb340250 --- /dev/null +++ b/drivers/misc/issei/main.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdev.h" +#include "fw_client.h" +#include "host_client.h" +#include "ham.h" +#include "issei_dev.h" + +static void issei_rst_state_set(struct issei_device *idev, enum issei_rst_state state) +{ + idev->rst_state = state; + /* wake up the thread */ + if (waitqueue_active(&idev->wait_rst_state)) + wake_up(&idev->wait_rst_state); +} + +static int issei_reset(struct issei_device *idev) +{ + int ret; + + idev->ops->irq_clear(idev); + + issei_cl_all_disconnect(idev); + issei_fw_cl_remove_all(idev); + /* No need to check for overflow here, the counter is used only for info */ + idev->all_reset_count++; + ret = idev->ops->hw_reset(idev, !idev->power_down); + issei_dmam_setup(idev); + if (ret) { + dev_err(&idev->dev, "hw_reset failed ret = %d\n", ret); + return ret; + } + + if (idev->power_down) { + dev_dbg(&idev->dev, "powering down: end of reset\n"); + issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED); + return -ENODEV; + } + return 0; +} + +static int issei_process_read_msg(struct issei_device *idev) +{ + struct issei_dma_data data = {}; + int ret; + + ret = issei_dma_read(idev, &data); + if (ret) + return ret; + + dev_dbg(&idev->dev, "Processing response %u %u %u %u\n", data.fw_id, data.host_id, + data.status, data.length); + if (data.status != HAMS_SUCCESS) { + dev_err(&idev->dev, "Command failed with status 0x%02X", data.status); + kfree(data.buf); + ret = -EIO; + } else { + if (issei_is_ham_rsp(data.fw_id, data.host_id)) + ret = issei_ham_process_ham_rsp(idev, data.buf, data.length); + else + ret = issei_cl_read_buf(idev, data.fw_id, data.host_id, + data.buf, data.length); + } + idev->ops->irq_write_generate(idev); + return ret; +} + +static int issei_process_write_msg(struct issei_device *idev) +{ + if (idev->rst_state != ISSEI_RST_STATE_DONE) + return 0; + + return issei_cl_write_from_queue(idev); +} + +static int issei_process_thread(void *_dev) +{ + long timeout, old_timeout = MAX_SCHEDULE_TIMEOUT; + struct issei_device *idev = _dev; + int ret; + + while (!kthread_should_stop()) { + dev_dbg(&idev->dev, "process_work in %d\n", idev->rst_state); + if (!idev->ops->hw_is_ready(idev) && idev->rst_state > ISSEI_RST_STATE_HW_READY) { + if (!idev->power_down) + dev_dbg(&idev->dev, "HW not ready, resetting\n"); + idev->rst_state = ISSEI_RST_STATE_INIT; + } + if (idev->power_down) + idev->rst_state = ISSEI_RST_STATE_INIT; + WRITE_ONCE(idev->has_data, false); + dev_dbg(&idev->dev, "reset_step in %d\n", idev->rst_state); + timeout = MAX_SCHEDULE_TIMEOUT; + ret = 0; + switch (idev->rst_state) { + case ISSEI_RST_STATE_DISABLED: + if (idev->power_down) { + dev_dbg(&idev->dev, "Interrupt in power down?\n"); + break; + } + idev->rst_state = ISSEI_RST_STATE_INIT; + fallthrough; + + case ISSEI_RST_STATE_INIT: + idev->ops->irq_clear(idev); + idev->ops->irq_sync(idev); + + if (!idev->power_down) { + idev->reset_count++; + if (idev->reset_count > ISSEI_MAX_CONSEC_RESET) { + dev_err(&idev->dev, "reset: reached maximal consecutive resets: disabling the device\n"); + issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED); + break; + } + } + + ret = issei_reset(idev); + if (idev->power_down) { + dev_dbg(&idev->dev, "Powering down\n"); + return 0; + } + if (ret) + break; + + idev->rst_state = ISSEI_RST_STATE_HW_READY; + timeout = msecs_to_jiffies(ISSEI_RST_HW_READY_TIMEOUT_MSEC); + break; + + case ISSEI_RST_STATE_HW_READY: + if (!idev->ops->hw_is_ready(idev)) { + dev_dbg(&idev->dev, "HW is not ready?\n"); + timeout = old_timeout; + break; + } + + dev_dbg(&idev->dev, "HW is ready\n"); + idev->ops->hw_reset_release(idev); + idev->ops->host_set_ready(idev); + ret = idev->ops->setup_message_send(idev); + if (ret) + break; + + idev->rst_state = ISSEI_RST_STATE_SETUP; + timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC); + break; + + case ISSEI_RST_STATE_SETUP: + ret = idev->ops->setup_message_recv(idev); + if (ret) { + if (ret == -ENODATA) { + ret = 0; + timeout = old_timeout; + } + } else { + timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC); + ret = issei_ham_send_start_req(idev); + idev->rst_state = ISSEI_RST_STATE_START; + } + break; + + case ISSEI_RST_STATE_START: + ret = issei_process_read_msg(idev); + if (!ret) { + timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC); + idev->rst_state = ISSEI_RST_STATE_CLIENT_ENUM; + } else if (ret == -ENODATA) { + ret = 0; + timeout = old_timeout; + } + break; + + case ISSEI_RST_STATE_CLIENT_ENUM: + ret = issei_process_read_msg(idev); + if (ret) { + if (ret == -ENODATA) { + ret = 0; + timeout = old_timeout; + } + } else { + idev->reset_count = 0; + idev->rst_state = ISSEI_RST_STATE_DONE; + dev_dbg(&idev->dev, "Reset finished successfully\n"); + } + break; + + case ISSEI_RST_STATE_DONE: + ret = issei_process_read_msg(idev); + if (ret != 0 && ret != -ENODATA) + break; + + ret = issei_process_write_msg(idev); + break; + } + + if (ret) { + dev_warn(&idev->dev, "Process failed ret = %d\n", ret); + idev->rst_state = ISSEI_RST_STATE_INIT; + continue; + } + + /* + * Every thread that has data to process sets the 'has_data' flag and + * triggers the wait queue. + * The processing thread, in each loop iteration, resets 'has_data' + * and processes all available data. + * + * After processing, the thread waits for 'has_data' to be set again. + * + * If the wait function times out but 'has_data' becomes 1 before + * the subsequent atomic read check, this is acceptable from a flow + * perspective - the thread will continue processing the data. + * + * The 'has_data' flag cannot become 0 between the wait function and + * the atomic read check, since only this thread is allowed to reset it to 0. + */ + + old_timeout = wait_event_interruptible_timeout(idev->wait_has_data, + READ_ONCE(idev->has_data), + timeout); + if (idev->rst_state == ISSEI_RST_STATE_DISABLED) + continue; + + if (!READ_ONCE(idev->has_data)) { + dev_warn(&idev->dev, "Timed out at state %d, resetting\n", + idev->rst_state); + idev->rst_state = ISSEI_RST_STATE_INIT; + } + } + + return 0; +} + +/** + * issei_start - configure HW device and start processing thread. + * @idev: the device structure + * + * Return: 0 on success, < 0 on failure + */ +int issei_start(struct issei_device *idev) +{ + int ret; + + idev->power_down = false; + + ret = issei_dmam_setup(idev); + if (ret) + return ret; + + idev->ops->irq_clear(idev); + + ret = idev->ops->hw_config(idev); + if (ret) + return ret; + + idev->process_thread = kthread_run(issei_process_thread, idev, + "kisseiprocess/%s", dev_name(&idev->dev)); + if (IS_ERR(idev->process_thread)) { + ret = PTR_ERR(idev->process_thread); + dev_err(&idev->dev, "unable to create process thread. ret = %d\n", ret); + return ret; + } + + issei_poke_process_thread(idev); + return 0; +} +EXPORT_SYMBOL_GPL(issei_start); + +/** + * issei_stop - stop interrupts and processing thread. + * @idev: the device structure + */ +void issei_stop(struct issei_device *idev) +{ + idev->power_down = true; + + idev->ops->irq_clear(idev); + idev->ops->irq_sync(idev); + + issei_poke_process_thread(idev); + + wait_event_timeout(idev->wait_rst_state, + (idev->rst_state == ISSEI_RST_STATE_DISABLED), + msecs_to_jiffies(ISSEI_STOP_TIMEOUT_MSEC)); + kthread_stop(idev->process_thread); +} +EXPORT_SYMBOL_GPL(issei_stop); From 8bf5e84998c3fd00ca9e29a8a8143d23ea1e8663 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Wed, 13 May 2026 17:18:45 +0300 Subject: [PATCH 251/513] issei: add heci hardware module Add support for the ISSEI (Intel Silicon Security Engine Interface) HECI PCI devices. Add the necessary PCI handling routines, hardware definitions, register mappings and hardware access routines. This enables the communication via HECI PCI device advertized by BIOS. Reviewed-by: Karol Wachowski Co-developed-by: Vitaly Lubart Signed-off-by: Vitaly Lubart Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260513-issei-for-upstream-v1-4-f590038678f9@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/issei/Kconfig | 16 + drivers/misc/issei/Makefile | 4 + drivers/misc/issei/hw_heci.c | 550 ++++++++++++++++++++++++++++++ drivers/misc/issei/hw_heci.h | 47 +++ drivers/misc/issei/hw_heci_regs.h | 35 ++ drivers/misc/issei/pci_heci.c | 151 ++++++++ 6 files changed, 803 insertions(+) create mode 100644 drivers/misc/issei/hw_heci.c create mode 100644 drivers/misc/issei/hw_heci.h create mode 100644 drivers/misc/issei/hw_heci_regs.h create mode 100644 drivers/misc/issei/pci_heci.c diff --git a/drivers/misc/issei/Kconfig b/drivers/misc/issei/Kconfig index d98ac7925ce6..12a3fd809969 100644 --- a/drivers/misc/issei/Kconfig +++ b/drivers/misc/issei/Kconfig @@ -11,3 +11,19 @@ config INTEL_SSEI If selected, the /dev/isseiX device will be created. If in doubt, select N. + +if INTEL_SSEI + +config INTEL_SSEI_HW_HECI + tristate "Intel Silicon Security Engine Interface Hardware" + depends on X86 && PCI + help + HECI interface of communication channel between + the host and the Silicon Security Engine. + + Implementation of ISSEI communication channel over + the HECI hardware PCI device. + This device is available on Intel client CPUs released in 2024 + (Lunar Lake) or later. + +endif diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile index f21e0b985c94..8fac360c3342 100644 --- a/drivers/misc/issei/Makefile +++ b/drivers/misc/issei/Makefile @@ -9,3 +9,7 @@ issei-objs += fw_client.o issei-objs += host_client.o issei-objs += ham.o issei-objs += main.o + +obj-$(CONFIG_INTEL_SSEI_HW_HECI) += issei-heci.o +issei-heci-objs := pci_heci.o +issei-heci-objs += hw_heci.o diff --git a/drivers/misc/issei/hw_heci.c b/drivers/misc/issei/hw_heci.c new file mode 100644 index 000000000000..35c55de5c67a --- /dev/null +++ b/drivers/misc/issei/hw_heci.c @@ -0,0 +1,550 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hw_heci.h" +#include "hw_heci_regs.h" +#include "hw_msg.h" + +/** + * heci_reg_read - Reads 32bit data from the issei heci device + * @hw: the heci hardware structure + * @offset: offset from which to read the data + * + * Return: register value (u32) + */ +static inline u32 heci_reg_read(const struct issei_heci_hw *hw, unsigned long offset) +{ + return ioread32(hw->mem_addr + offset); +} + +/** + * heci_reg_write - Writes 32bit data to the issei heci device + * + * @hw: the heci hardware structure + * @offset: offset from which to write the data + * @value: register value to write (u32) + */ +static inline void heci_reg_write(const struct issei_heci_hw *hw, unsigned long offset, u32 value) +{ + iowrite32(value, hw->mem_addr + offset); +} + +/** + * heci_fwcbrw_read - Reads 32bit data from heci circular buffer + * @hw: the heci hardware structure + * + * Return: FW_CB_RW register value (u32) + */ +static inline u32 heci_fwcbrw_read(const struct issei_heci_hw *hw) +{ + return heci_reg_read(hw, FW_CB_RW); +} + +/** + * heci_hcbww_write - write 32bit data to the host circular buffer + * @hw: the heci hardware structure + * @data: 32bit data to be written to the host circular buffer + */ +static inline void heci_hcbww_write(const struct issei_heci_hw *hw, u32 data) +{ + heci_reg_write(hw, H_CB_WW, data); +} + +/** + * heci_irq_src - Filters IRQ source bits from the host CSR + * @hcsr: host CSR register value + * + * Return: interrupt source bits of host CSR + */ +static inline u32 heci_irq_src(u32 hcsr) +{ + return hcsr & H_CSR_IS; +} + +/** + * heci_hcsr_read - Reads 32bit data from the host CSR + * @idev: the device structure + * + * Return: H_CSR register value (u32) + */ +static inline u32 heci_hcsr_read(const struct issei_device *idev) +{ + return heci_reg_read(to_heci_hw(idev), H_CSR); +} + +/** + * heci_hcsr_write - writes H_CSR register to device + * @idev: the device structure + * @reg: new register value + */ +static inline void heci_hcsr_write(struct issei_device *idev, u32 reg) +{ + heci_reg_write(to_heci_hw(idev), H_CSR, reg); +} + +/** + * heci_hcsr_set - writes H_CSR register to the heci device + * @idev: the device structure + * @reg: new register value + * + * Writes H_CSR register to the heci device + * and ignores the H_IS bit for it is write-one-to-zero. + * + */ +static inline void heci_hcsr_set(struct issei_device *idev, u32 reg) +{ + reg &= ~H_CSR_IS; + heci_hcsr_write(idev, reg); +} + +/** + * heci_hcsr_set_hig - set host interrupt (set H_CSR_IG) + * @idev: the device structure + */ +static inline void heci_hcsr_set_hig(struct issei_device *idev) +{ + u32 reg; + + reg = heci_hcsr_read(idev) | H_CSR_IG; + heci_hcsr_set(idev, reg); +} + +/** + * heci_fwcsr_read - Reads 32bit data from the FW CSR + * @idev: the device structure + * + * Return: FW_CSR_HA register value (u32) + */ +static inline u32 heci_fwcsr_read(const struct issei_device *idev) +{ + return heci_reg_read(to_heci_hw(idev), FW_CSR_HA); +} + +/** + * heci_count_full_read_slots - counts read full slots. + * @idev: the device structure + * + * Return: -EOVERFLOW if overflow, otherwise filled slots count + */ +static int heci_count_full_read_slots(struct issei_device *idev) +{ + u8 buffer_depth, filled_slots; + u8 read_ptr, write_ptr; + u32 reg; + + reg = heci_fwcsr_read(idev); + buffer_depth = (u8)FIELD_GET(FW_CSR_CBD, reg); + read_ptr = (u8)FIELD_GET(FW_CSR_CBRP, reg); + write_ptr = (u8)FIELD_GET(FW_CSR_CBWP, reg); + filled_slots = write_ptr - read_ptr; + + /* check for overflow */ + if (filled_slots > buffer_depth) + return -EOVERFLOW; + + dev_dbg(&idev->dev, "filled_slots = %08x\n", filled_slots); + return filled_slots; +} + +/** + * heci_irq_disable - disables heci device interrupts + * @idev: the device structure + * @reg: supplied hcsr register value + * + * disables heci device interrupts using supplied hcsr register value. + */ +static inline void heci_irq_disable(struct issei_device *idev, u32 reg) +{ + reg &= ~H_CSR_IE; + heci_hcsr_set(idev, reg); +} + +/** + * heci_irq_clear - clear and stop interrupts + * @idev: the device structure + * @reg: supplied hcsr register value + */ +static inline void heci_irq_clear(struct issei_device *idev, u32 reg) +{ + if (heci_irq_src(reg)) + heci_hcsr_write(idev, reg); +} + +/** + * issei_heci_irq_clear - clear and stop interrupts + * @idev: the device structure + */ +static void issei_heci_irq_clear(struct issei_device *idev) +{ + u32 reg = heci_hcsr_read(idev); + + heci_irq_clear(idev, reg); +} + +/** + * issei_heci_irq_enable - enables heci device interrupts + * @idev: the device structure + */ +static void issei_heci_irq_enable(struct issei_device *idev) +{ + u32 reg; + + reg = heci_hcsr_read(idev) | H_CSR_IE; + heci_hcsr_set(idev, reg); +} + +/** + * issei_heci_irq_disable - disables heci device interrupts + * @idev: the device structure + */ +static void issei_heci_irq_disable(struct issei_device *idev) +{ + u32 reg = heci_hcsr_read(idev); + + heci_irq_disable(idev, reg); +} + +static void issei_heci_irq_sync(struct issei_device *idev) +{ + synchronize_irq(to_heci_hw(idev)->irq); +} + +/** + * issei_heci_hw_reset_release - release device from the reset + * @idev: the device structure + */ +static void issei_heci_hw_reset_release(struct issei_device *idev) +{ + u32 reg = heci_hcsr_read(idev); + + reg |= H_CSR_IG; + reg &= ~H_CSR_RST; + heci_hcsr_set(idev, reg); +} + +/** + * heci_hw_is_ready - check whether the hw has turned ready + * @idev: the device structure + * + * Return: bool + */ +static bool heci_hw_is_ready(struct issei_device *idev) +{ + u32 reg = heci_fwcsr_read(idev); + + return reg & FW_CSR_RDY; +} + +/** + * heci_hw_is_resetting - check whether the hw is in reset + * @idev: the device structure + * + * Return: bool + */ +static bool heci_hw_is_resetting(struct issei_device *idev) +{ + u32 reg = heci_fwcsr_read(idev); + + return reg & FW_CSR_RST; +} + +/** + * issei_heci_host_set_ready - enable device + * @idev: the device structure + */ +static void issei_heci_host_set_ready(struct issei_device *idev) +{ + u32 reg = heci_hcsr_read(idev); + + reg |= H_CSR_IE | H_CSR_IG | H_CSR_RDY; + heci_hcsr_set(idev, reg); +} + +/** + * issei_heci_hw_reset - resets fw via heci csr register. + * @idev: the device structure + * @enable: if interrupt should be enabled after reset. + * + * Return: 0 on success an error code otherwise + */ +static int issei_heci_hw_reset(struct issei_device *idev, bool enable) +{ + u32 reg; + + if (enable) + issei_heci_irq_enable(idev); + + reg = heci_hcsr_read(idev); + /* + * H_CSR_RST may be found lit before reset is started, + * for example if preceding reset flow hasn't completed. + * In that case asserting H_CSR_RST will be ignored, therefore + * we need to clean H_CSR_RST bit to start a successful reset sequence. + */ + if (reg & H_CSR_RST) { + dev_warn(&idev->dev, "H_CSR_RST is set = 0x%08X", reg); + reg &= ~H_CSR_RST; + heci_hcsr_set(idev, reg); + reg = heci_hcsr_read(idev); + } + + reg |= H_CSR_RST | H_CSR_IG | H_CSR_IS; + + if (!enable) + reg &= ~H_CSR_IE; + + heci_hcsr_write(idev, reg); + + /* + * Host reads the H_CSR once to ensure that the + * posted write to H_CSR completes. + */ + reg = heci_hcsr_read(idev); + + if (!(reg & H_CSR_RST)) + dev_warn(&idev->dev, "H_CSR_RST is not set = 0x%08X", reg); + + if (reg & H_CSR_RDY) + dev_warn(&idev->dev, "H_CSR_RDY is not cleared 0x%08X", reg); + + if (!enable) + issei_heci_hw_reset_release(idev); + return 0; +} + +/** + * issei_heci_irq_write_generate - generate interrupt to signal write completion. + * @idev: the device structure + * + * Return: 0 on success, -EIO if hardware is not ready and requires reset + */ +static int issei_heci_irq_write_generate(struct issei_device *idev) +{ + struct issei_heci_hw *hw = to_heci_hw(idev); + + scoped_guard(spinlock_irqsave, &hw->access_lock) + heci_hcsr_set_hig(idev); + if (!heci_hw_is_ready(idev)) + return -EIO; + return 0; +} + +/** + * issei_heci_hw_config - initial hardware configuration. + * @idev: the device structure + * + * Return: 0 always + */ +static int issei_heci_hw_config(struct issei_device *idev) +{ + struct issei_heci_hw *hw = to_heci_hw(idev); + u32 reg; + + /* Doesn't change in runtime */ + reg = heci_hcsr_read(idev); + hw->hbuf_depth = FIELD_GET(H_CSR_CBD, reg); + + return 0; +} + +/** + * heci_write_hbuf - write to hardware buffer + * @idev: the device structure + * @data: data to write + * @data_len: data size + * + * Return: 0 on success, <0 on error + */ +static int heci_write_hbuf(struct issei_device *idev, const void *data, size_t data_len) +{ + struct issei_heci_hw *hw = to_heci_hw(idev); + + if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) { + dev_err(&idev->dev, "Data size %zu not aligned to slot size %lu\n", + data_len, CB_SLOT_SIZE); + return -EINVAL; + } + + scoped_guard(spinlock_irqsave, &hw->access_lock) { + const u32 *reg_buf = data; + size_t i; + + for (i = 0; i < data_len / CB_SLOT_SIZE; i++) + heci_hcbww_write(hw, reg_buf[i]); + } + + return issei_heci_irq_write_generate(idev); +} + +/** + * heci_read_hbuf - read data from hardware buffer + * @idev: the device structure + * @data: buffer to store read data + * @data_len: buffer size + * + * Return: 0 on success, <0 on error + */ +static int heci_read_hbuf(struct issei_device *idev, void *data, size_t data_len) +{ + struct issei_heci_hw *hw = to_heci_hw(idev); + u32 *reg_buf = data; + + if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) { + dev_err(&idev->dev, "Data size %zu not aligned to slot size %lu\n", + data_len, CB_SLOT_SIZE); + return -EINVAL; + } + + scoped_guard(spinlock_irqsave, &hw->access_lock) { + for (; data_len >= CB_SLOT_SIZE; data_len -= CB_SLOT_SIZE) + *reg_buf++ = heci_fwcbrw_read(hw); + } + return 0; +} + +/** + * issei_heci_setup_message_send - send setup message to firmware + * @idev: the device structure + * + * Return: 0 on success, <0 on error + */ +static int issei_heci_setup_message_send(struct issei_device *idev) +{ + struct ham_setup_shared_memory_req req = { + .msg_id = HAM_CB_MESSAGE_ID_REQ, + .ver = HAM_CB_MESSAGE_VER, + .reserved = 0, + .buffer_physical_address = idev->dma.daddr, + .host_to_fw_section_length = idev->dma.length.h2f, + .fw_to_host_section_length = idev->dma.length.f2h, + .control_length = idev->dma.length.ctl + }; + int ret; + + ret = heci_write_hbuf(idev, &req, sizeof(req)); + if (ret) + dev_err(&idev->dev, "Shared memory req write failed ret = %d\n", ret); + + return ret; +} + +/** + * issei_heci_setup_message_recv - receive setup message response from firmware + * @idev: the device structure + * + * Return: 0 on success, <0 on error + */ +static int issei_heci_setup_message_recv(struct issei_device *idev) +{ + struct ham_setup_shared_memory_res res; + int ret; + + if (heci_count_full_read_slots(idev) != sizeof(res) / CB_SLOT_SIZE) { + dev_dbg(&idev->dev, "Setup response is not fully received\n"); + return -ENODATA; + } + + ret = heci_read_hbuf(idev, &res, sizeof(res)); + if (ret) { + dev_err(&idev->dev, "Shared memory res read failed ret = %d\n", ret); + return ret; + } + + if (res.msg_id != HAM_CB_MESSAGE_ID_RES) { + dev_err(&idev->dev, "Shared memory res header 0x%x != 0x%x\n", + res.msg_id, HAM_CB_MESSAGE_ID_RES); + return -EPROTO; + } + if (res.status != 0) { + dev_err(&idev->dev, "Shared memory res status %d != 0\n", res.status); + return -EPROTO; + } + + return 0; +} + +static const struct issei_hw_ops hw_heci_ops = { + .irq_clear = issei_heci_irq_clear, + .irq_enable = issei_heci_irq_enable, + .irq_disable = issei_heci_irq_disable, + .irq_sync = issei_heci_irq_sync, + + .hw_reset = issei_heci_hw_reset, + .hw_config = issei_heci_hw_config, + .hw_reset_release = issei_heci_hw_reset_release, + .host_set_ready = issei_heci_host_set_ready, + + .hw_is_ready = heci_hw_is_ready, + + .setup_message_send = issei_heci_setup_message_send, + .setup_message_recv = issei_heci_setup_message_recv, + .irq_write_generate = issei_heci_irq_write_generate, +}; + +const struct issei_hw_ops *issei_heci_get_ops(void) +{ + return &hw_heci_ops; +} + +irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id) +{ + struct issei_device *idev = dev_id; + struct issei_heci_hw *hw = to_heci_hw(idev); + u32 reg; + + reg = heci_hcsr_read(idev); + if (!heci_irq_src(reg)) + return IRQ_NONE; + + scoped_guard(spinlock_irqsave, &hw->access_lock) { + reg = heci_hcsr_read(idev); + heci_irq_clear(idev, reg); + + if (heci_hw_is_resetting(idev)) + heci_hcsr_set_hig(idev); + } + + dev_dbg(&idev->dev, "interrupt source 0x%08X\n", heci_irq_src(reg)); + + issei_poke_process_thread(idev); + + return IRQ_HANDLED; +} + +static const struct hw_heci_cfg hw_heci_pch_cfg = { + .dma_length.h2f = SZ_64K, + .dma_length.f2h = SZ_64K, + .dma_length.ctl = SZ_4K, +}; + +const struct hw_heci_cfg *issei_heci_get_cfg(kernel_ulong_t idx) +{ + return &hw_heci_pch_cfg; +} + +/** + * issei_heci_dev_init - initializes the issei device structure with hw_heci + * @idev: device structure + * @mem_addr: memory address on bar + * @cfg: per device generation config + */ +void issei_heci_dev_init(struct issei_device *idev, + void __iomem *mem_addr, const struct hw_heci_cfg *cfg) +{ + struct issei_heci_hw *hw = to_heci_hw(idev); + + spin_lock_init(&hw->access_lock); + hw->mem_addr = mem_addr; + hw->cfg = cfg; +} diff --git a/drivers/misc/issei/hw_heci.h b/drivers/misc/issei/hw_heci.h new file mode 100644 index 000000000000..0ae2faeedf12 --- /dev/null +++ b/drivers/misc/issei/hw_heci.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HW_HECI_H_ +#define _ISSEI_HW_HECI_H_ +#include +#include +#include + +#include "issei_dev.h" + +/* + * hw_heci_cfg - issei heci device configuration + * + * @dma_length: DMA area length + */ +struct hw_heci_cfg { + const struct issei_dma_length dma_length; +}; + +/** + * struct issei_heci_hw - issei heci hw specific data + * + * @cfg: per device generation config and ops + * @mem_addr: io memory address + * @irq: device irq number + * @access_lock: spinlock to protect hw access + * @hbuf_depth: depth of hardware host/write buffer in slots + */ +struct issei_heci_hw { + const struct hw_heci_cfg *cfg; + void __iomem *mem_addr; + int irq; + spinlock_t access_lock; + u8 hbuf_depth; +}; + +#define to_heci_hw(dev) ((struct issei_heci_hw *)(dev)->hw) + +const struct hw_heci_cfg *issei_heci_get_cfg(kernel_ulong_t idx); +const struct issei_hw_ops *issei_heci_get_ops(void); + +void issei_heci_dev_init(struct issei_device *idev, + void __iomem *mem_addr, const struct hw_heci_cfg *cfg); + +irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id); + +#endif /* _ISSEI_HW_HECI_H_ */ diff --git a/drivers/misc/issei/hw_heci_regs.h b/drivers/misc/issei/hw_heci_regs.h new file mode 100644 index 000000000000..a991b670a9d3 --- /dev/null +++ b/drivers/misc/issei/hw_heci_regs.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HW_HECI_REGS_H_ +#define _ISSEI_HW_HECI_REGS_H_ + +#include + +/* H_CB_WW - Host Circular Buffer (CB) Write Window register */ +#define H_CB_WW 0x0 +/* H_CSR - Host Control Status register */ +#define H_CSR 0x4 +#define H_CSR_CBD GENMASK(31, 24) /* Host Circular Buffer Depth */ +#define H_CSR_CBWP GENMASK(23, 16) /* Host Circular Buffer Write Pointer */ +#define H_CSR_CBRP GENMASK(15, 8) /* Host Circular Buffer Read Pointer */ +#define H_CSR_RST BIT(4) /* Host Reset */ +#define H_CSR_RDY BIT(3) /* Host Ready */ +#define H_CSR_IG BIT(2) /* Host Interrupt Generate */ +#define H_CSR_IS BIT(1) /* Host Interrupt Status */ +#define H_CSR_IE BIT(0) /* Host Interrupt Enable */ +/* FW_CB_RW - FW Circular Buffer Read Window register (read only) */ +#define FW_CB_RW 0x8 +/* FW_CSR_HA - FW Control Status Host Access register (read only) */ +#define FW_CSR_HA 0xC +#define FW_CSR_CBD GENMASK(31, 24) /* FW CB (Circular Buffer) Depth */ +#define FW_CSR_CBWP GENMASK(23, 16) /* FW CB Write Pointer */ +#define FW_CSR_CBRP GENMASK(15, 8) /* FW CB Read Pointer */ +#define FW_CSR_RST BIT(4) /* FW Reset */ +#define FW_CSR_RDY BIT(3) /* FW Ready */ +#define FW_CSR_IG BIT(2) /* FW Interrupt Generate */ +#define FW_CSR_IS BIT(1) /* FW Interrupt Status */ +#define FW_CSR_IE BIT(0) /* FW Interrupt Enable */ + +#define CB_SLOT_SIZE sizeof(u32) /* Circular Buffer windows size */ + +#endif /* _ISSEI_HW_HECI_REGS_H_ */ diff --git a/drivers/misc/issei/pci_heci.c b/drivers/misc/issei/pci_heci.c new file mode 100644 index 000000000000..86c567a4bf0f --- /dev/null +++ b/drivers/misc/issei/pci_heci.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdev.h" +#include "hw_heci.h" +#include "hw_heci_regs.h" + +static int issei_heci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct device *dev = &pdev->dev; + const struct hw_heci_cfg *cfg; + struct issei_device *idev; + struct issei_heci_hw *hw; + char __iomem *registers; + int err; + + cfg = issei_heci_get_cfg(ent->driver_data); + if (!cfg) + return dev_err_probe(dev, -ENODEV, "no usable configuration.\n"); + + 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"); + + idev = issei_register(sizeof(*hw), dev, &cfg->dma_length, issei_heci_get_ops()); + if (IS_ERR(idev)) + return dev_err_probe(dev, PTR_ERR(idev), "register failure.\n"); + + issei_heci_dev_init(idev, registers, cfg); + + pci_set_drvdata(pdev, idev); + + err = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); + if (err < 0) { + dev_err_probe(dev, err, "pci_alloc_irq_vectors failure.\n"); + goto deregister; + } + + hw = to_heci_hw(idev); + hw->irq = pci_irq_vector(pdev, 0); + + err = request_threaded_irq(hw->irq, + issei_heci_irq_quick_handler, + NULL, + IRQF_SHARED, KBUILD_MODNAME, idev); + if (err) + goto release_irq; + + err = issei_start(idev); + if (err) { + dev_err_probe(dev, err, "init hw failure.\n"); + goto free_irq; + } + + return 0; + +free_irq: + idev->ops->irq_disable(idev); + free_irq(hw->irq, idev); +release_irq: + pci_free_irq_vectors(pdev); +deregister: + issei_deregister(idev); + return err; +} + +static void issei_heci_shutdown(struct pci_dev *pdev) +{ + struct issei_device *idev = pci_get_drvdata(pdev); + struct issei_heci_hw *hw = to_heci_hw(idev); + + issei_stop(idev); + + idev->ops->irq_disable(idev); + free_irq(hw->irq, idev); + pci_free_irq_vectors(pdev); +} + +static void issei_heci_remove(struct pci_dev *pdev) +{ + issei_heci_shutdown(pdev); + + issei_deregister(pci_get_drvdata(pdev)); +} + +static int issei_heci_pm_suspend(struct device *device) +{ + struct issei_device *idev = dev_get_drvdata(device); + + issei_stop(idev); + idev->ops->irq_disable(idev); + + return 0; +} + +static int issei_heci_pm_resume(struct device *device) +{ + struct issei_device *idev = dev_get_drvdata(device); + + return issei_start(idev); +} + +static const struct dev_pm_ops issei_heci_pm_ops = { + SYSTEM_SLEEP_PM_OPS(issei_heci_pm_suspend, issei_heci_pm_resume) +}; + +static const struct pci_device_id heci_pci_tbl[] = { + {PCI_VDEVICE(INTEL, 0xA85D)}, /* Lunar Lake M */ + {PCI_VDEVICE(INTEL, 0xE35D)}, /* Panther Lake H */ + {PCI_VDEVICE(INTEL, 0xE45D)}, /* Panther Lake P */ + {PCI_VDEVICE(INTEL, 0xD470)}, /* Nova Lake S */ + {PCI_VDEVICE(INTEL, 0xD358)}, /* Nova Lake H */ + {PCI_VDEVICE(INTEL, 0x4D5D)}, /* Wildcat Lake */ + {} +}; +MODULE_DEVICE_TABLE(pci, heci_pci_tbl); + +static struct pci_driver issei_heci_driver = { + .name = KBUILD_MODNAME, + .id_table = heci_pci_tbl, + .probe = issei_heci_probe, + .remove = issei_heci_remove, + .shutdown = issei_heci_shutdown, + .driver = { + .pm = &issei_heci_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; +module_pci_driver(issei_heci_driver); + +MODULE_DESCRIPTION("Intel(R) Silicon Security Engine Interface - HECI"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("INTEL_SSEI"); From 077b4d1aa01a1b34c5b768b8c7a77c74b35b190b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 26 May 2026 10:52:07 +0200 Subject: [PATCH 252/513] misc: pch_phub: Complete enum usage for device identification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recently an enum was introduced to identify the different hardware variants instead of magic constants. The respective commit however missed to adapt one code location that still checks the old values. As the values shifted by one this is a relevant fix. Fixes: 7b1d4ad96ea4 ("misc: pch_phub: Introduce an enum for device indentification") Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/8a97d9d5fb0a4abf7032324643e3e2337b1347bd.1779785111.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pch_phub.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 19c4fa017f24..0097611b97af 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -83,6 +83,14 @@ #define PCH_PHUB_OROM_SIZE 15360 +enum pch_phub_type { + PCH_EG20T, + PCH_ML7213, + PCH_ML7223M, + PCH_ML7223N, + PCH_ML7831, +}; + /** * struct pch_phub_reg - PHUB register structure * @phub_id_reg: PHUB_ID register val @@ -125,7 +133,7 @@ struct pch_phub_reg { void __iomem *pch_phub_extrom_base_address; u32 pch_mac_start_address; u32 pch_opt_rom_start_address; - int ioh_type; + enum pch_phub_type ioh_type; struct pci_dev *pdev; }; @@ -344,7 +352,7 @@ static int pch_phub_write_gbe_mac_addr(struct pch_phub_reg *chip, u8 *data) int retval; int i; - if ((chip->ioh_type == 1) || (chip->ioh_type == 5)) /* EG20T or ML7831*/ + if (chip->ioh_type == PCH_EG20T || chip->ioh_type == PCH_ML7831) retval = pch_phub_gbe_serial_rom_conf(chip); else /* ML7223 */ retval = pch_phub_gbe_serial_rom_conf_mp(chip); @@ -537,14 +545,6 @@ static const struct bin_attribute pch_bin_attr = { .write = pch_phub_bin_write, }; -enum { - PCH_EG20T, - PCH_ML7213, - PCH_ML7223M, - PCH_ML7223N, - PCH_ML7831, -}; - static int pch_phub_probe(struct pci_dev *pdev, const struct pci_device_id *id) { From 5fd370856b840ed8164154bf832ed0d4dff87c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 26 May 2026 10:52:08 +0200 Subject: [PATCH 253/513] misc: pch_phub: Drop unused members from struct pch_phub_reg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit d14b649fd99f ("misc: pch_phub: Drop two unused functions") all the register values in struct pch_phub_reg are unused. Drop them. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/459d402dad63a6cc0e230b6f2305556bb915579c.1779785111.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pch_phub.c | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 0097611b97af..5adf7f0c84ab 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -93,20 +93,6 @@ enum pch_phub_type { /** * struct pch_phub_reg - PHUB register structure - * @phub_id_reg: PHUB_ID register val - * @q_pri_val_reg: QUEUE_PRI_VAL register val - * @rc_q_maxsize_reg: RC_QUEUE_MAXSIZE register val - * @bri_q_maxsize_reg: BRI_QUEUE_MAXSIZE register val - * @comp_resp_timeout_reg: COMP_RESP_TIMEOUT register val - * @bus_slave_control_reg: BUS_SLAVE_CONTROL_REG register val - * @deadlock_avoid_type_reg: DEADLOCK_AVOID_TYPE register val - * @intpin_reg_wpermit_reg0: INTPIN_REG_WPERMIT register 0 val - * @intpin_reg_wpermit_reg1: INTPIN_REG_WPERMIT register 1 val - * @intpin_reg_wpermit_reg2: INTPIN_REG_WPERMIT register 2 val - * @intpin_reg_wpermit_reg3: INTPIN_REG_WPERMIT register 3 val - * @int_reduce_control_reg: INT_REDUCE_CONTROL registers val - * @clkcfg_reg: CLK CFG register val - * @funcsel_reg: Function select register value * @pch_phub_base_address: Register base address * @pch_phub_extrom_base_address: external rom base address * @pch_mac_start_address: MAC address area start address @@ -115,20 +101,6 @@ enum pch_phub_type { * @pdev: pointer to pci device struct */ struct pch_phub_reg { - u32 phub_id_reg; - u32 q_pri_val_reg; - u32 rc_q_maxsize_reg; - u32 bri_q_maxsize_reg; - u32 comp_resp_timeout_reg; - u32 bus_slave_control_reg; - u32 deadlock_avoid_type_reg; - u32 intpin_reg_wpermit_reg0; - u32 intpin_reg_wpermit_reg1; - u32 intpin_reg_wpermit_reg2; - u32 intpin_reg_wpermit_reg3; - u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG]; - u32 clkcfg_reg; - u32 funcsel_reg; void __iomem *pch_phub_base_address; void __iomem *pch_phub_extrom_base_address; u32 pch_mac_start_address; From aefcde780b39c11d4ac9e191299b281af99dab3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 26 May 2026 10:52:09 +0200 Subject: [PATCH 254/513] misc: pch_phub: Make MAC address configuration more robust MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The comment in pch_phub_write_gbe_mac_addr() suggests that only EG20T, ML7831 and ML7223 are handled. Replace the code construct using an if with a switch that has the same semantics but issues a warning if a new device type is added to the driver without adapting this function. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/796b3667ea49d9156ecc03d1ce9668972316d90b.1779785111.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pch_phub.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 5adf7f0c84ab..15785597da40 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -324,10 +324,19 @@ static int pch_phub_write_gbe_mac_addr(struct pch_phub_reg *chip, u8 *data) int retval; int i; - if (chip->ioh_type == PCH_EG20T || chip->ioh_type == PCH_ML7831) + switch (chip->ioh_type) { + case PCH_EG20T: + case PCH_ML7831: retval = pch_phub_gbe_serial_rom_conf(chip); - else /* ML7223 */ + break; + + case PCH_ML7213: + case PCH_ML7223M: + case PCH_ML7223N: retval = pch_phub_gbe_serial_rom_conf_mp(chip); + break; + } + if (retval) return retval; From 61b101c6a150057b6d512421ed108aed16e822ea Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Wed, 3 Jun 2026 10:11:27 +0800 Subject: [PATCH 255/513] misc: bcm-vk: Use acquire/release for msgq_inited bcm_vk_sync_msgq() fills the message queue information and then sets msgq_inited. Readers call bcm_vk_drv_access_ok() before accessing the message queues and their cached queue information. atomic_set()/atomic_read() do not order those accesses. A reader can see msgq_inited set while still seeing stale queue information. Use release when publishing the initialized queues and acquire when checking the gate. Keep the clear in bcm_vk_blk_drv_access() as atomic_set(). It closes the gate and does not publish queue state to readers. Fixes: 111d746bb476 ("misc: bcm-vk: add VK messaging support") Signed-off-by: Gui-Dong Han Link: https://patch.msgid.link/20260603021127.3285057-1-hanguidong02@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/bcm-vk/bcm_vk_msg.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/misc/bcm-vk/bcm_vk_msg.c b/drivers/misc/bcm-vk/bcm_vk_msg.c index 3916ec07ecad..2c084a6b3a92 100644 --- a/drivers/misc/bcm-vk/bcm_vk_msg.c +++ b/drivers/misc/bcm-vk/bcm_vk_msg.c @@ -108,7 +108,8 @@ u32 msgq_avail_space(const struct bcm_vk_msgq __iomem *msgq, bool bcm_vk_drv_access_ok(struct bcm_vk *vk) { - return (!!atomic_read(&vk->msgq_inited)); + /* Pair with the release store after message queue initialization. */ + return !!atomic_read_acquire(&vk->msgq_inited); } void bcm_vk_set_host_alert(struct bcm_vk *vk, u32 bit_mask) @@ -501,7 +502,8 @@ int bcm_vk_sync_msgq(struct bcm_vk *vk, bool force_sync) msgq++; } } - atomic_set(&vk->msgq_inited, 1); + /* Publish message queue info before allowing driver access. */ + atomic_set_release(&vk->msgq_inited, 1); return ret; } From 6994c8b4ef95114073a51d6143185bca39d6e5d5 Mon Sep 17 00:00:00 2001 From: David Laight Date: Mon, 8 Jun 2026 10:55:02 +0100 Subject: [PATCH 256/513] drivers/misc/enclosure: Replace strcpy() + strcat() with snprintf() While the sizeof the target buffer is (should be) ENCLOSURE_NAME_SIZE and the copies should not overrrun this stops any static analysis objecting to the unbounded strcpy() and strcat() calls Signed-off-by: David Laight Link: https://patch.msgid.link/20260608095523.2606-18-david.laight.linux@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/enclosure.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index cf6382981777..de457378c501 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -184,8 +184,8 @@ EXPORT_SYMBOL_GPL(enclosure_unregister); static void enclosure_link_name(struct enclosure_component *cdev, char *name) { - strcpy(name, "enclosure_device:"); - strcat(name, dev_name(&cdev->cdev)); + snprintf(name, ENCLOSURE_NAME_SIZE, "enclosure_device:%s", + dev_name(&cdev->cdev)); } static void enclosure_remove_links(struct enclosure_component *cdev) From 937cd823bb2c950a935dbd32313586911caae2f2 Mon Sep 17 00:00:00 2001 From: Vu Nguyen Anh Khoa Date: Sun, 21 Jun 2026 15:57:43 +0700 Subject: [PATCH 257/513] misc: nsm: do not unlock mutex before locking it nsm_dev_ioctl() jumps to the common out label when the initial copy_from_user() fails. That failure path runs before mutex_lock(&nsm->lock), but the out label unconditionally calls mutex_unlock(&nsm->lock). Return -EFAULT directly for the pre-lock copy_from_user() failure so only paths that acquired the mutex release it. Signed-off-by: Vu Nguyen Anh Khoa Reviewed-by: Alexander Graf Reviewed-by: Arnd Bergmann Link: https://patch.msgid.link/20260621085743.76329-2-khoavna.tin.2225@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/nsm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/misc/nsm.c b/drivers/misc/nsm.c index ef7b32742340..e39ff00714f7 100644 --- a/drivers/misc/nsm.c +++ b/drivers/misc/nsm.c @@ -365,9 +365,8 @@ static long nsm_dev_ioctl(struct file *file, unsigned int cmd, return -EINVAL; /* Copy user argument struct to kernel argument struct */ - r = -EFAULT; if (copy_from_user(&raw, argp, _IOC_SIZE(cmd))) - goto out; + return -EFAULT; mutex_lock(&nsm->lock); From 4965758a48ce8d3e193b7c48a0c91ef433724906 Mon Sep 17 00:00:00 2001 From: Batu Ada Tutkun Date: Mon, 22 Jun 2026 23:16:32 +0300 Subject: [PATCH 258/513] misc: ibmasm: add parentheses around sizeof operand sizeof used without parentheses around its operand on two occasions in r_heartbeat.c. Add them to comply with the kernel coding style. Signed-off-by: Batu Ada Tutkun Link: https://patch.msgid.link/20260622201633.2577-1-batuadatutkun@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ibmasm/r_heartbeat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/ibmasm/r_heartbeat.c b/drivers/misc/ibmasm/r_heartbeat.c index 21c9b6a6f2c3..8d3fa0ce4b6c 100644 --- a/drivers/misc/ibmasm/r_heartbeat.c +++ b/drivers/misc/ibmasm/r_heartbeat.c @@ -51,12 +51,12 @@ int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_ int times_failed = 0; int result = 1; - cmd = ibmasm_new_command(sp, sizeof rhb_dot_cmd); + cmd = ibmasm_new_command(sp, sizeof(rhb_dot_cmd)); if (!cmd) return -ENOMEM; while (times_failed < 3) { - memcpy(cmd->buffer, (void *)&rhb_dot_cmd, sizeof rhb_dot_cmd); + memcpy(cmd->buffer, (void *)&rhb_dot_cmd, sizeof(rhb_dot_cmd)); cmd->status = IBMASM_CMD_PENDING; ibmasm_exec_command(sp, cmd); ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL); From 808e530654a5354e6df78863a5d61e4d44e67235 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sat, 20 Jun 2026 21:42:11 -0500 Subject: [PATCH 259/513] misc: nsm: bound the device-reported response length nsm_sendrecv_msg_locked() stores the virtqueue used-ring length reported by the NSM device into msg->resp.len without bounding it to the response buffer. A malicious or buggy backend can report a length larger than the response buffer; parse_resp_raw() then copies that many bytes out of the fixed buffer to user space, disclosing adjacent kernel heap (an out-of-bounds read). The request path already floors its length in fill_req_raw(); the response path lacks the symmetric check. Clamp the stored length to the size of the response buffer. Well-behaved devices report no more than the posted buffer size, so conforming traffic is unaffected. Fixes: b9873755a6c8 ("misc: Add Nitro Secure Module driver") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: Alexander Graf Link: https://patch.msgid.link/20260620-b4-disp-a54b7dd6-v1-1-79d1f236a854@proton.me Signed-off-by: Greg Kroah-Hartman --- drivers/misc/nsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/nsm.c b/drivers/misc/nsm.c index e39ff00714f7..bb254ee65b84 100644 --- a/drivers/misc/nsm.c +++ b/drivers/misc/nsm.c @@ -243,7 +243,7 @@ static int nsm_sendrecv_msg_locked(struct nsm *nsm) goto cleanup; } - msg->resp.len = len; + msg->resp.len = min_t(unsigned int, len, sizeof(msg->resp.data)); rc = 0; From f66c40cd90954f52809b3eabd386137f56bb6215 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Mon, 29 Jun 2026 18:06:05 +0200 Subject: [PATCH 260/513] misc: bcm-vk: validate write size before allocation bcm_vk_write() uses the user-supplied write count to size a flexible-array work entry and then copies count bytes into that array. The allocation expression is evaluated before any overflow check, so a very large count can wrap the allocation smaller than the subsequent copy. Reject empty writes, check the allocation arithmetic before kzalloc(), and initialize the __counted_by field before copying into to_v_msg[]. Signed-off-by: Yousef Alhouseen Link: https://patch.msgid.link/20260629160605.29412-1-alhouseenyousef@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/bcm-vk/bcm_vk_msg.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/misc/bcm-vk/bcm_vk_msg.c b/drivers/misc/bcm-vk/bcm_vk_msg.c index 2c084a6b3a92..17114092a284 100644 --- a/drivers/misc/bcm-vk/bcm_vk_msg.c +++ b/drivers/misc/bcm-vk/bcm_vk_msg.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -1090,6 +1091,7 @@ ssize_t bcm_vk_write(struct file *p_file, u32 q_num; u32 msg_size; u32 msgq_size; + size_t entry_size; if (!bcm_vk_drv_access_ok(vk)) return -EPERM; @@ -1097,20 +1099,26 @@ ssize_t bcm_vk_write(struct file *p_file, dev_dbg(dev, "Msg count %zu\n", count); /* first, do sanity check where count should be multiple of basic blk */ - if (count & (VK_MSGQ_BLK_SIZE - 1)) { - dev_err(dev, "Failure with size %zu not multiple of %zu\n", + if (!count || count & (VK_MSGQ_BLK_SIZE - 1)) { + dev_err(dev, "Failure with size %zu not a positive multiple of %zu\n", count, VK_MSGQ_BLK_SIZE); rc = -EINVAL; goto write_err; } + if (check_add_overflow(sizeof(*entry), count, &entry_size) || + check_add_overflow(entry_size, vk->ib_sgl_size, &entry_size)) { + rc = -EOVERFLOW; + goto write_err; + } + /* allocate the work entry + buffer for size count and inband sgl */ - entry = kzalloc(sizeof(*entry) + count + vk->ib_sgl_size, - GFP_KERNEL); + entry = kzalloc(entry_size, GFP_KERNEL); if (!entry) { rc = -ENOMEM; goto write_err; } + entry->to_v_blks = count >> VK_MSGQ_BLK_SZ_SHIFT; /* now copy msg from user space, and then formulate the work entry */ if (copy_from_user(&entry->to_v_msg[0], buf, count)) { @@ -1118,7 +1126,6 @@ ssize_t bcm_vk_write(struct file *p_file, goto write_free_ent; } - entry->to_v_blks = count >> VK_MSGQ_BLK_SZ_SHIFT; entry->ctx = ctx; /* do a check on the blk size which could not exceed queue space */ @@ -1355,4 +1362,3 @@ void bcm_vk_msg_remove(struct bcm_vk *vk) bcm_vk_drain_all_pend(&vk->pdev->dev, &vk->to_v_msg_chan, NULL); bcm_vk_drain_all_pend(&vk->pdev->dev, &vk->to_h_msg_chan, NULL); } - From 655faba1ccf195e22a7a83146ef6015e3271233c Mon Sep 17 00:00:00 2001 From: Gleb Markov Date: Mon, 29 Jun 2026 16:09:18 +0300 Subject: [PATCH 261/513] misc: rtsx: add missing write register handling If an error occurs at the stage of working with registers in conjunction with MCU_Block, it will not be processed. The occurrence of errors at this stage may signal an impact on writes to the device's PCI registers and is a more global problem than a driver-level security problem, but adding a handler would be a good practice. Add a missing error handling. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: c0e5f4e73a71 ("misc: rtsx: Add support for RTS5261") Signed-off-by: Gleb Markov Link: https://patch.msgid.link/20260629130920.1260-1-markov.gi@npc-ksb.ru Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rtsx_pcr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index c4d54ca2fa80..c6e602523538 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -1196,6 +1196,8 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) /* Gating real mcu clock */ err = rtsx_pci_write_register(pcr, RTS5261_FW_CFG1, RTS5261_MCU_CLOCK_GATING, 0); + if (err < 0) + return err; err = rtsx_pci_write_register(pcr, RTS5261_REG_FPDCTL, SSC_POWER_DOWN, 0); } else { From 7bf6940d7cf41f1f0330a9bae4b7a886e18fd8e3 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Wed, 24 Jun 2026 21:09:19 +0200 Subject: [PATCH 262/513] misc: hpilo: validate device queue entries before use ilo_pkt_dequeue() trusts descriptor IDs and lengths read from the shared FIFO entry. A bad entry can select a descriptor outside the allocated queue memory or report a packet length larger than one descriptor. Reject entries whose descriptor index or packet length exceeds the queue layout before deriving the packet pointer returned to read and write paths. Signed-off-by: Yousef Alhouseen Link: https://patch.msgid.link/20260624190919.3432-1-alhouseenyousef@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/hpilo.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index ff3f03ea577e..9b54a81b43d8 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -160,11 +160,16 @@ static int ilo_pkt_dequeue(struct ilo_hwinfo *hw, struct ccb *ccb, ret = fifo_dequeue(hw, fifobar, &entry); if (ret) { + int pkt_len; + pkt_id = get_entry_id(entry); + pkt_len = get_entry_len(entry); + if (pkt_id >= NR_QENTRY || pkt_len > desc_mem_sz(1)) + return 0; if (id) *id = pkt_id; if (len) - *len = get_entry_len(entry); + *len = pkt_len; if (pkt) *pkt = (void *)(desc + desc_mem_sz(pkt_id)); } From fd62c1f591372f7dc4c5bc041569c2f0a4a86be1 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Wed, 24 Jun 2026 20:59:25 +0200 Subject: [PATCH 263/513] misc: ibmvmc: release send buffer on write errors ibmvmc_get_valid_hmc_buffer() marks the selected send buffer busy before ibmvmc_write() validates the backing storage or copies data from user space. Error exits after that point leave the buffer permanently busy. Keep the buffer pointer until ownership is handed to the hypervisor, and mark it free again on local write failures. Also report an RDMA send failure instead of returning a successful byte count. Signed-off-by: Yousef Alhouseen Link: https://patch.msgid.link/20260624185925.2133-1-alhouseenyousef@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ibmvmc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/misc/ibmvmc.c b/drivers/misc/ibmvmc.c index beb18c34f20d..1f2968d9d01b 100644 --- a/drivers/misc/ibmvmc.c +++ b/drivers/misc/ibmvmc.c @@ -1040,7 +1040,7 @@ static ssize_t ibmvmc_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct inode *inode; - struct ibmvmc_buffer *vmc_buffer; + struct ibmvmc_buffer *vmc_buffer = NULL; struct ibmvmc_file_session *session; struct crq_server_adapter *adapter; struct ibmvmc_hmc *hmc; @@ -1130,9 +1130,15 @@ static ssize_t ibmvmc_write(struct file *file, const char *buffer, dev_dbg(adapter->dev, "write: file = 0x%lx, count = 0x%lx\n", (unsigned long)file, (unsigned long)count); - ibmvmc_send_msg(adapter, vmc_buffer, hmc, count); + if (ibmvmc_send_msg(adapter, vmc_buffer, hmc, count)) { + ret = -EIO; + goto out; + } + vmc_buffer = NULL; ret = p - buffer; out: + if (vmc_buffer) + vmc_buffer->free = 1; spin_unlock_irqrestore(&hmc->lock, flags); return (ssize_t)(ret); } From 18189e5d84aa0b3bc89189cba13b9105634cb6fb Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Wed, 24 Jun 2026 19:51:39 +0200 Subject: [PATCH 264/513] misc: ibmvmc: reject oversized inbound messages ibmvmc_recv_msg() trusts the message length from the CRQ. It passes that length directly to h_copy_rdma(). The destination buffer is only max_mtu bytes. A larger length can overrun it before userspace reads the message. Validate the CRQ length before issuing the RDMA copy. Signed-off-by: Yousef Alhouseen Link: https://patch.msgid.link/20260624175139.7981-1-alhouseenyousef@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ibmvmc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/misc/ibmvmc.c b/drivers/misc/ibmvmc.c index 1f2968d9d01b..28bf4c352317 100644 --- a/drivers/misc/ibmvmc.c +++ b/drivers/misc/ibmvmc.c @@ -1659,6 +1659,13 @@ static int ibmvmc_recv_msg(struct crq_server_adapter *adapter, return -1; } + if (msg_len > buffer->size) { + dev_err(adapter->dev, "Recv_msg: msg_len 0x%lx exceeds buffer size 0x%x\n", + msg_len, buffer->size); + spin_unlock_irqrestore(&hmc->lock, flags); + return -1; + } + /* RDMA the data into the partition. */ rc = h_copy_rdma(msg_len, adapter->riobn, From 82aa033fef523ab4f0165afbc1c1fb0dadb89f94 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 16 Jun 2026 23:10:28 +0800 Subject: [PATCH 265/513] misc: rp1: clear chained IRQ handlers on teardown rp1_probe() installs a chained handler for each parent MSI-X vector and stores the rp1 device pointer as handler data. rp1_unregister_interrupts() then disposes the child IRQ mappings, removes the IRQ domain, and frees the PCI IRQ vectors without first removing those chained handlers. If a teardown path runs after the handlers have been installed, a later parent IRQ can still call rp1_chained_handle_irq() with stale handler data and a removed IRQ domain. Clear the chained handlers before disposing mappings and freeing the vectors. Signed-off-by: Pengpeng Hou Reviewed-by: Andrea della Porta Link: https://patch.msgid.link/20260616151028.69890-1-pengpeng@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/misc/rp1/rp1_pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/misc/rp1/rp1_pci.c b/drivers/misc/rp1/rp1_pci.c index 81685e3f3296..a1f20d88be5d 100644 --- a/drivers/misc/rp1/rp1_pci.c +++ b/drivers/misc/rp1/rp1_pci.c @@ -166,6 +166,9 @@ static void rp1_unregister_interrupts(struct pci_dev *pdev) struct rp1_dev *rp1 = pci_get_drvdata(pdev); int irq, i; + for (i = 0; i < RP1_INT_END; i++) + irq_set_chained_handler_and_data(pci_irq_vector(pdev, i), NULL, NULL); + if (rp1->domain) { for (i = 0; i < RP1_INT_END; i++) { irq = irq_find_mapping(rp1->domain, i); From 0764fd10406f38bf6d86077a43301f0c35cc88f4 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 16 Jun 2026 23:08:02 +0800 Subject: [PATCH 266/513] misc: rp1: do not put borrowed OF node dev_of_node() returns the device's OF node without taking a new reference. rp1_probe() stores that borrowed pointer in rp1_node, but drops it with of_node_put() on both success and failure paths. Dropping a reference that was never acquired can underflow the node's refcount and leave later users with a stale OF node. Remove the of_node_put() calls and keep rp1_node as a borrowed pointer. Signed-off-by: Pengpeng Hou Reviewed-by: Andrea della Porta Link: https://patch.msgid.link/20260616150802.52050-1-pengpeng@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/misc/rp1/rp1_pci.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/misc/rp1/rp1_pci.c b/drivers/misc/rp1/rp1_pci.c index a1f20d88be5d..0e87633fe4f8 100644 --- a/drivers/misc/rp1/rp1_pci.c +++ b/drivers/misc/rp1/rp1_pci.c @@ -194,13 +194,13 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (!rp1_node) { dev_err(dev, "Missing of_node for device\n"); err = -EINVAL; - goto err_put_node; + goto err_out; } rp1 = devm_kzalloc(&pdev->dev, sizeof(*rp1), GFP_KERNEL); if (!rp1) { err = -ENOMEM; - goto err_put_node; + goto err_out; } rp1->pdev = pdev; @@ -209,21 +209,21 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev_err(&pdev->dev, "Not initialized - is the firmware running?\n"); err = -EINVAL; - goto err_put_node; + goto err_out; } err = pcim_enable_device(pdev); if (err < 0) { err = dev_err_probe(&pdev->dev, err, "Enabling PCI device has failed"); - goto err_put_node; + goto err_out; } rp1->bar1 = pcim_iomap(pdev, 1, 0); if (!rp1->bar1) { dev_err(&pdev->dev, "Cannot map PCI BAR\n"); err = -EIO; - goto err_put_node; + goto err_out; } pci_set_master(pdev); @@ -233,11 +233,11 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err < 0) { err = dev_err_probe(&pdev->dev, err, "Failed to allocate MSI-X vectors\n"); - goto err_put_node; + goto err_out; } else if (err != RP1_INT_END) { dev_err(&pdev->dev, "Cannot allocate enough interrupts\n"); err = -EINVAL; - goto err_put_node; + goto err_out; } pci_set_drvdata(pdev, rp1); @@ -270,15 +270,11 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_unregister_interrupts; } - of_node_put(rp1_node); - return 0; err_unregister_interrupts: rp1_unregister_interrupts(pdev); -err_put_node: - of_node_put(rp1_node); - +err_out: return err; } From e3a8557e88eb26278eda60bf64f2ef33ce7de8bf Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 23 Jun 2026 09:56:43 +0800 Subject: [PATCH 267/513] misc: ad525x_dpot: use driver core groups for sysfs files ad_dpot_probe() creates per-RDAC sysfs files manually and then optionally creates the command sysfs group. This leaves probe responsible for rolling back partial sysfs state and makes remove responsible for matching every file that probe created. Move the device attributes into driver core dev_groups for the I2C and SPI drivers and use an is_visible() callback to expose only the attributes supported by the probed device. With this shape, the driver core creates the sysfs files only after probe succeeds and removes them before the remove callback frees the driver data. Fixes: 4eb174bee6f8 ("ad525x_dpot: new driver for AD525x digital potentiometers") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260623015643.36508-1-pengpeng@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ad525x_dpot-i2c.c | 1 + drivers/misc/ad525x_dpot-spi.c | 1 + drivers/misc/ad525x_dpot.c | 177 ++++++++++++++++++++------------- drivers/misc/ad525x_dpot.h | 3 + 4 files changed, 112 insertions(+), 70 deletions(-) diff --git a/drivers/misc/ad525x_dpot-i2c.c b/drivers/misc/ad525x_dpot-i2c.c index 469478f7a1d3..896ad61bb9e1 100644 --- a/drivers/misc/ad525x_dpot-i2c.c +++ b/drivers/misc/ad525x_dpot-i2c.c @@ -105,6 +105,7 @@ MODULE_DEVICE_TABLE(i2c, ad_dpot_id); static struct i2c_driver ad_dpot_i2c_driver = { .driver = { .name = "ad_dpot", + .dev_groups = ad_dpot_groups, }, .probe = ad_dpot_i2c_probe, .remove = ad_dpot_i2c_remove, diff --git a/drivers/misc/ad525x_dpot-spi.c b/drivers/misc/ad525x_dpot-spi.c index 263055bda48b..1ebe629715a8 100644 --- a/drivers/misc/ad525x_dpot-spi.c +++ b/drivers/misc/ad525x_dpot-spi.c @@ -131,6 +131,7 @@ MODULE_DEVICE_TABLE(spi, ad_dpot_spi_id); static struct spi_driver ad_dpot_spi_driver = { .driver = { .name = "ad_dpot", + .dev_groups = ad_dpot_groups, }, .probe = ad_dpot_spi_probe, .remove = ad_dpot_spi_remove, diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c index 57bead9fba1b..a4e22fd4a107 100644 --- a/drivers/misc/ad525x_dpot.c +++ b/drivers/misc/ad525x_dpot.c @@ -630,66 +630,132 @@ static struct attribute *ad525x_attributes_commands[] = { NULL }; -static const struct attribute_group ad525x_group_commands = { - .attrs = ad525x_attributes_commands, +static struct attribute *ad525x_attributes[] = { + &dev_attr_rdac0.attr, + &dev_attr_rdac1.attr, + &dev_attr_rdac2.attr, + &dev_attr_rdac3.attr, + &dev_attr_rdac4.attr, + &dev_attr_rdac5.attr, + &dev_attr_eeprom0.attr, + &dev_attr_eeprom1.attr, + &dev_attr_eeprom2.attr, + &dev_attr_eeprom3.attr, + &dev_attr_eeprom4.attr, + &dev_attr_eeprom5.attr, + &dev_attr_tolerance0.attr, + &dev_attr_tolerance1.attr, + &dev_attr_tolerance2.attr, + &dev_attr_tolerance3.attr, + &dev_attr_tolerance4.attr, + &dev_attr_tolerance5.attr, + &dev_attr_otp0.attr, + &dev_attr_otp1.attr, + &dev_attr_otp2.attr, + &dev_attr_otp3.attr, + &dev_attr_otp4.attr, + &dev_attr_otp5.attr, + &dev_attr_otp0en.attr, + &dev_attr_otp1en.attr, + &dev_attr_otp2en.attr, + &dev_attr_otp3en.attr, + &dev_attr_otp4en.attr, + &dev_attr_otp5en.attr, + &dev_attr_inc_all.attr, + &dev_attr_dec_all.attr, + &dev_attr_inc_all_6db.attr, + &dev_attr_dec_all_6db.attr, + NULL }; -static int ad_dpot_add_files(struct device *dev, - unsigned int features, unsigned int rdac) +static int ad525x_attr_index(struct attribute *attr, + const struct attribute * const *attrs) { - int err = sysfs_create_file(&dev->kobj, - dpot_attrib_wipers[rdac]); - if (features & F_CMD_EEP) - err |= sysfs_create_file(&dev->kobj, - dpot_attrib_eeprom[rdac]); - if (features & F_CMD_TOL) - err |= sysfs_create_file(&dev->kobj, - dpot_attrib_tolerance[rdac]); - if (features & F_CMD_OTP) { - err |= sysfs_create_file(&dev->kobj, - dpot_attrib_otp_en[rdac]); - err |= sysfs_create_file(&dev->kobj, - dpot_attrib_otp[rdac]); - } + int i; - if (err) - dev_err(dev, "failed to register sysfs hooks for RDAC%d\n", - rdac); + for (i = 0; attrs[i]; i++) + if (attr == attrs[i]) + return i; - return err; + return -ENOENT; } -static inline void ad_dpot_remove_files(struct device *dev, - unsigned int features, unsigned int rdac) +static bool ad525x_is_command_attr(struct attribute *attr) { - sysfs_remove_file(&dev->kobj, - dpot_attrib_wipers[rdac]); - if (features & F_CMD_EEP) - sysfs_remove_file(&dev->kobj, - dpot_attrib_eeprom[rdac]); - if (features & F_CMD_TOL) - sysfs_remove_file(&dev->kobj, - dpot_attrib_tolerance[rdac]); - if (features & F_CMD_OTP) { - sysfs_remove_file(&dev->kobj, - dpot_attrib_otp_en[rdac]); - sysfs_remove_file(&dev->kobj, - dpot_attrib_otp[rdac]); + int i; + + for (i = 0; ad525x_attributes_commands[i]; i++) { + if (attr == ad525x_attributes_commands[i]) + return true; } + + return false; } +static umode_t ad525x_is_visible(struct kobject *kobj, struct attribute *attr, + int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct dpot_data *data = dev_get_drvdata(dev); + int rdac; + + if (!data) + return 0; + + rdac = ad525x_attr_index(attr, dpot_attrib_wipers); + if (rdac >= 0) + return data->wipers & BIT(rdac) ? attr->mode : 0; + + rdac = ad525x_attr_index(attr, dpot_attrib_eeprom); + if (rdac >= 0) + return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_EEP) ? + attr->mode : 0; + + rdac = ad525x_attr_index(attr, dpot_attrib_tolerance); + if (rdac >= 0) + return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_TOL) ? + attr->mode : 0; + + rdac = ad525x_attr_index(attr, dpot_attrib_otp); + if (rdac >= 0) + return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_OTP) ? + attr->mode : 0; + + rdac = ad525x_attr_index(attr, dpot_attrib_otp_en); + if (rdac >= 0) + return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_OTP) ? + attr->mode : 0; + + if (ad525x_is_command_attr(attr)) + return data->feat & F_CMD_INC ? attr->mode : 0; + + return attr->mode; +} + +static const struct attribute_group ad525x_group = { + .attrs = ad525x_attributes, + .is_visible = ad525x_is_visible, +}; + +const struct attribute_group *ad_dpot_groups[] = { + &ad525x_group, + NULL +}; +EXPORT_SYMBOL(ad_dpot_groups); + int ad_dpot_probe(struct device *dev, struct ad_dpot_bus_data *bdata, unsigned long devid, const char *name) { struct dpot_data *data; - int i, err = 0; + int i; data = kzalloc_obj(struct dpot_data); if (!data) { - err = -ENOMEM; - goto exit; + dev_err(dev, "failed to create client for %s ID 0x%lX\n", + name, devid); + return -ENOMEM; } dev_set_drvdata(dev, data); @@ -705,51 +771,22 @@ int ad_dpot_probe(struct device *dev, data->wipers = DPOT_WIPERS(devid); for (i = DPOT_RDAC0; i < MAX_RDACS; i++) - if (data->wipers & (1 << i)) { - err = ad_dpot_add_files(dev, data->feat, i); - if (err) - goto exit_remove_files; + if (data->wipers & BIT(i)) { /* power-up midscale */ if (data->feat & F_RDACS_WONLY) data->rdac_cache[i] = data->max_pos / 2; } - if (data->feat & F_CMD_INC) - err = sysfs_create_group(&dev->kobj, &ad525x_group_commands); - - if (err) { - dev_err(dev, "failed to register sysfs hooks\n"); - goto exit_free; - } - dev_info(dev, "%s %d-Position Digital Potentiometer registered\n", name, data->max_pos); return 0; - -exit_remove_files: - for (i = DPOT_RDAC0; i < MAX_RDACS; i++) - if (data->wipers & (1 << i)) - ad_dpot_remove_files(dev, data->feat, i); - -exit_free: - kfree(data); - dev_set_drvdata(dev, NULL); -exit: - dev_err(dev, "failed to create client for %s ID 0x%lX\n", - name, devid); - return err; } EXPORT_SYMBOL(ad_dpot_probe); void ad_dpot_remove(struct device *dev) { struct dpot_data *data = dev_get_drvdata(dev); - int i; - - for (i = DPOT_RDAC0; i < MAX_RDACS; i++) - if (data->wipers & (1 << i)) - ad_dpot_remove_files(dev, data->feat, i); kfree(data); } diff --git a/drivers/misc/ad525x_dpot.h b/drivers/misc/ad525x_dpot.h index 72a9d6801937..2e877c89523b 100644 --- a/drivers/misc/ad525x_dpot.h +++ b/drivers/misc/ad525x_dpot.h @@ -10,6 +10,8 @@ #include +struct attribute_group; + #define DPOT_CONF(features, wipers, max_pos, uid) \ (((features) << 18) | (((wipers) & 0xFF) << 10) | \ ((max_pos & 0xF) << 6) | (uid & 0x3F)) @@ -210,5 +212,6 @@ struct ad_dpot_bus_data { int ad_dpot_probe(struct device *dev, struct ad_dpot_bus_data *bdata, unsigned long devid, const char *name); void ad_dpot_remove(struct device *dev); +extern const struct attribute_group *ad_dpot_groups[]; #endif From f6e2ed54db95286d9512b1cff38264e2f6299814 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 23 Jun 2026 09:52:48 +0800 Subject: [PATCH 268/513] misc: lan966x_pci: depopulate children on populate failure lan966x_pci_probe() applies a device-tree overlay and then populates platform children from the overlaid node. If of_platform_default_populate() creates some children and then fails, the current error path only unloads the overlay. Depopulate the children before unloading the overlay on that failure path, matching the remove path order. Fixes: 185686beb464 ("misc: Add support for LAN966x PCI device") Reviewed-by: Herve Codina Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260623015248.22721-1-pengpeng@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lan966x_pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/lan966x_pci.c b/drivers/misc/lan966x_pci.c index 0bb90c0943bf..b0949c653e5b 100644 --- a/drivers/misc/lan966x_pci.c +++ b/drivers/misc/lan966x_pci.c @@ -183,6 +183,7 @@ static int lan966x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i return 0; err_unload_overlay: + of_platform_depopulate(dev); lan966x_pci_unload_overlay(data); return ret; } From 7a7a0d97d541512f7a935125274b79914223c2c1 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Mon, 29 Jun 2026 17:28:57 +0200 Subject: [PATCH 269/513] misc: xilinx_sdfec: validate LDPC code register offsets The LDPC code register helpers check the target MMIO address after adding code_id * XSDFEC_LDPC_REG_JUMP to the register base. code_id is supplied through the ioctl path, so the multiplication and addition can wrap before the bounds check. Validate the code_id against the register window size before computing the final address, then write using the checked address. Signed-off-by: Yousef Alhouseen Reviewed-by: Cvetic, Dragan Link: https://patch.msgid.link/20260629152857.13553-1-alhouseenyousef@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/xilinx_sdfec.c | 74 +++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 40 deletions(-) diff --git a/drivers/misc/xilinx_sdfec.c b/drivers/misc/xilinx_sdfec.c index fe7bea3b14bf..a19eed39c516 100644 --- a/drivers/misc/xilinx_sdfec.c +++ b/drivers/misc/xilinx_sdfec.c @@ -456,10 +456,25 @@ static int xsdfec_get_turbo(struct xsdfec_dev *xsdfec, void __user *arg) return err; } +static int xsdfec_ldpc_reg_addr(struct xsdfec_dev *xsdfec, u32 base, u32 high, + u32 offset, u32 *addr) +{ + if (high < base || offset > (high - base) / XSDFEC_LDPC_REG_JUMP) { + dev_dbg(xsdfec->dev, + "LDPC register offset %u outside space 0x%x-0x%x", + offset, base, high); + return -EINVAL; + } + + *addr = base + offset * XSDFEC_LDPC_REG_JUMP; + return 0; +} + static int xsdfec_reg0_write(struct xsdfec_dev *xsdfec, u32 n, u32 k, u32 psize, u32 offset) { u32 wdata; + u32 addr; if (n < XSDFEC_REG0_N_MIN || n > XSDFEC_REG0_N_MAX || psize == 0 || (n > XSDFEC_REG0_N_MUL_P * psize) || n <= k || ((n % psize) != 0)) { @@ -476,17 +491,11 @@ static int xsdfec_reg0_write(struct xsdfec_dev *xsdfec, u32 n, u32 k, u32 psize, k = k << XSDFEC_REG0_K_LSB; wdata = k | n; - if (XSDFEC_LDPC_CODE_REG0_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) > - XSDFEC_LDPC_CODE_REG0_ADDR_HIGH) { - dev_dbg(xsdfec->dev, "Writing outside of LDPC reg0 space 0x%x", - XSDFEC_LDPC_CODE_REG0_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP)); + if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG0_ADDR_BASE, + XSDFEC_LDPC_CODE_REG0_ADDR_HIGH, offset, + &addr)) return -EINVAL; - } - xsdfec_regwrite(xsdfec, - XSDFEC_LDPC_CODE_REG0_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP), - wdata); + xsdfec_regwrite(xsdfec, addr, wdata); return 0; } @@ -494,6 +503,7 @@ static int xsdfec_reg1_write(struct xsdfec_dev *xsdfec, u32 psize, u32 no_packing, u32 nm, u32 offset) { u32 wdata; + u32 addr; if (psize < XSDFEC_REG1_PSIZE_MIN || psize > XSDFEC_REG1_PSIZE_MAX) { dev_dbg(xsdfec->dev, "Psize is not in range"); @@ -510,17 +520,11 @@ static int xsdfec_reg1_write(struct xsdfec_dev *xsdfec, u32 psize, nm = (nm << XSDFEC_REG1_NM_LSB) & XSDFEC_REG1_NM_MASK; wdata = nm | no_packing | psize; - if (XSDFEC_LDPC_CODE_REG1_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) > - XSDFEC_LDPC_CODE_REG1_ADDR_HIGH) { - dev_dbg(xsdfec->dev, "Writing outside of LDPC reg1 space 0x%x", - XSDFEC_LDPC_CODE_REG1_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP)); + if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG1_ADDR_BASE, + XSDFEC_LDPC_CODE_REG1_ADDR_HIGH, offset, + &addr)) return -EINVAL; - } - xsdfec_regwrite(xsdfec, - XSDFEC_LDPC_CODE_REG1_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP), - wdata); + xsdfec_regwrite(xsdfec, addr, wdata); return 0; } @@ -529,6 +533,7 @@ static int xsdfec_reg2_write(struct xsdfec_dev *xsdfec, u32 nlayers, u32 nmqc, u32 max_schedule, u32 offset) { u32 wdata; + u32 addr; if (nlayers < XSDFEC_REG2_NLAYERS_MIN || nlayers > XSDFEC_REG2_NLAYERS_MAX) { @@ -563,17 +568,11 @@ static int xsdfec_reg2_write(struct xsdfec_dev *xsdfec, u32 nlayers, u32 nmqc, wdata = (max_schedule | no_final_parity | special_qc | norm_type | nmqc | nlayers); - if (XSDFEC_LDPC_CODE_REG2_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) > - XSDFEC_LDPC_CODE_REG2_ADDR_HIGH) { - dev_dbg(xsdfec->dev, "Writing outside of LDPC reg2 space 0x%x", - XSDFEC_LDPC_CODE_REG2_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP)); + if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG2_ADDR_BASE, + XSDFEC_LDPC_CODE_REG2_ADDR_HIGH, offset, + &addr)) return -EINVAL; - } - xsdfec_regwrite(xsdfec, - XSDFEC_LDPC_CODE_REG2_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP), - wdata); + xsdfec_regwrite(xsdfec, addr, wdata); return 0; } @@ -581,20 +580,15 @@ static int xsdfec_reg3_write(struct xsdfec_dev *xsdfec, u8 sc_off, u8 la_off, u16 qc_off, u32 offset) { u32 wdata; + u32 addr; wdata = ((qc_off << XSDFEC_REG3_QC_OFF_LSB) | (la_off << XSDFEC_REG3_LA_OFF_LSB) | sc_off); - if (XSDFEC_LDPC_CODE_REG3_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) > - XSDFEC_LDPC_CODE_REG3_ADDR_HIGH) { - dev_dbg(xsdfec->dev, "Writing outside of LDPC reg3 space 0x%x", - XSDFEC_LDPC_CODE_REG3_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP)); + if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG3_ADDR_BASE, + XSDFEC_LDPC_CODE_REG3_ADDR_HIGH, offset, + &addr)) return -EINVAL; - } - xsdfec_regwrite(xsdfec, - XSDFEC_LDPC_CODE_REG3_ADDR_BASE + - (offset * XSDFEC_LDPC_REG_JUMP), - wdata); + xsdfec_regwrite(xsdfec, addr, wdata); return 0; } From d401772a9e18e837631450844f835229354528a4 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Tue, 30 Jun 2026 12:49:23 +0200 Subject: [PATCH 270/513] misc: genwqe: handle a first DMA address of zero genwqe_setup_sgl() uses zero as the initial previous DMA address. DMA address zero is valid, so a first entry at that address enters the merge path before last_s has been assigned and dereferences NULL. Only merge adjacent mappings after an SGL data entry has been created. Signed-off-by: Yousef Alhouseen Link: https://patch.msgid.link/20260630104923.53827-1-alhouseenyousef@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index a2c4a9b4f871..e3b5fb337182 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c @@ -413,7 +413,7 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, size -= size_to_map; map_offs = 0; - if (prev_daddr == daddr) { + if (last_s && prev_daddr == daddr) { u32 prev_len = be32_to_cpu(last_s->len); /* pr_info("daddr combining: " From a361bd5bcad55b943c5974413b25854fb87fc3f6 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Tue, 30 Jun 2026 12:49:41 +0200 Subject: [PATCH 271/513] misc: sgi-gru: fill execution status in exception details gru_retry_exception() tests cbrexecstatus after asking gru_get_cb_exception_detail() to fill the detail structure. The helper leaves that field uninitialized, so retry decisions depend on stale stack data. Populate the address, CBR state, and execution status alongside the other exception fields, matching the user exception-detail path. Signed-off-by: Yousef Alhouseen Link: https://patch.msgid.link/20260630104941.53862-1-alhouseenyousef@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sgi-gru/grukservices.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc/sgi-gru/grukservices.c b/drivers/misc/sgi-gru/grukservices.c index 205945ce9e86..c16012ee976f 100644 --- a/drivers/misc/sgi-gru/grukservices.c +++ b/drivers/misc/sgi-gru/grukservices.c @@ -411,11 +411,14 @@ int gru_get_cb_exception_detail(void *cb, cbe = get_cbe(GRUBASE(cb), cbrnum); gru_flush_cache(cbe); /* CBE not coherent */ sync_core(); + excdet->cb = (unsigned long)cb; excdet->opc = cbe->opccpy; excdet->exopc = cbe->exopccpy; excdet->ecause = cbe->ecause; excdet->exceptdet0 = cbe->idef1upd; excdet->exceptdet1 = cbe->idef3upd; + excdet->cbrstate = cbe->cbrstate; + excdet->cbrexecstatus = cbe->cbrexecstatus; gru_flush_cache(cbe); return 0; } @@ -1154,4 +1157,3 @@ void gru_kservices_exit(void) if (gru_free_kernel_contexts()) BUG(); } - From 1bb5c324b872c2e71fa1b12a5f59615b994501da Mon Sep 17 00:00:00 2001 From: Griffin Kroah-Hartman Date: Thu, 9 Jul 2026 15:20:52 +0200 Subject: [PATCH 272/513] misc: amd-sbi: Add null check for devm_kasprintf() Add two checks for devm_kasprintf() errors in create_misc_rmi_device(), returning -ENOMEM if the function failed. Assisted-by: gkh_clanker_t1000 CC: Arnd Bergmann CC: Greg Kroah-Hartman CC: Naveen Krishna Chatradhi CC: Akshay Gupta Signed-off-by: Griffin Kroah-Hartman Link: https://patch.msgid.link/20260709132052.211683-1-griffin@kroah.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/amd-sbi/rmi-core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/misc/amd-sbi/rmi-core.c b/drivers/misc/amd-sbi/rmi-core.c index d4238ebad3c6..95c9109101b5 100644 --- a/drivers/misc/amd-sbi/rmi-core.c +++ b/drivers/misc/amd-sbi/rmi-core.c @@ -581,6 +581,8 @@ int create_misc_rmi_device(struct sbrmi_data *data, GFP_KERNEL, "sbrmi-%x", data->dev_static_addr); + if (!data->sbrmi_misc_dev.name) + return -ENOMEM; data->sbrmi_misc_dev.minor = MISC_DYNAMIC_MINOR; data->sbrmi_misc_dev.fops = &sbrmi_fops; data->sbrmi_misc_dev.parent = dev; @@ -588,6 +590,8 @@ int create_misc_rmi_device(struct sbrmi_data *data, GFP_KERNEL, "sbrmi-%x", data->dev_static_addr); + if (!data->sbrmi_misc_dev.nodename) + return -ENOMEM; data->sbrmi_misc_dev.mode = 0600; return misc_register(&data->sbrmi_misc_dev); From 242b9a9e3df32b9fd6089e01ebdad5a95864ff26 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 13 Jul 2026 19:50:25 -0700 Subject: [PATCH 273/513] misc: keba: cp500: use pcim_enable_device() Switch from pci_enable_device() to pcim_enable_device() so the PCI device is automatically disabled on probe error and driver removal. Drop the now redundant manual pci_disable_device() and pci_clear_master() calls, since pcim's release path clears bus mastering and disables the device. Assisted-by: opencode:hy3-free Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260714025025.2055506-1-rosenp@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/keba/cp500.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/misc/keba/cp500.c b/drivers/misc/keba/cp500.c index 6c65fbf22e75..c2ca63e40814 100644 --- a/drivers/misc/keba/cp500.c +++ b/drivers/misc/keba/cp500.c @@ -887,7 +887,7 @@ static int cp500_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) else return -ENODEV; - ret = pci_enable_device(pci_dev); + ret = pcim_enable_device(pci_dev); if (ret) return ret; pci_set_master(pci_dev); @@ -896,18 +896,15 @@ static int cp500_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) startup.end = startup.start + cp500->devs->startup.size - 1; cp500->system_startup_addr = devm_ioremap_resource(&pci_dev->dev, &startup); - if (IS_ERR(cp500->system_startup_addr)) { - ret = PTR_ERR(cp500->system_startup_addr); - goto out_disable; - } + if (IS_ERR(cp500->system_startup_addr)) + return PTR_ERR(cp500->system_startup_addr); cp500->msix_num = pci_alloc_irq_vectors(pci_dev, CP500_NUM_MSIX_NO_MMI, CP500_NUM_MSIX, PCI_IRQ_MSIX); if (cp500->msix_num < CP500_NUM_MSIX_NO_MMI) { dev_err(&pci_dev->dev, "Hardware does not support enough MSI-X interrupts\n"); - ret = -ENODEV; - goto out_disable; + return -ENODEV; } cp500_vers = ioread32(cp500->system_startup_addr + CP500_VERSION_REG); @@ -937,9 +934,6 @@ static int cp500_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) nvmem_unregister_notifier(&cp500->nvmem_notifier); out_free_irq: pci_free_irq_vectors(pci_dev); -out_disable: - pci_clear_master(pci_dev); - pci_disable_device(pci_dev); return ret; } @@ -962,9 +956,6 @@ static void cp500_remove(struct pci_dev *pci_dev) pci_set_drvdata(pci_dev, 0); pci_free_irq_vectors(pci_dev); - - pci_clear_master(pci_dev); - pci_disable_device(pci_dev); } static struct pci_device_id cp500_ids[] = { From 319138ffcf83de6547651924030eb17c0d7977e5 Mon Sep 17 00:00:00 2001 From: Prathima Date: Fri, 10 Jul 2026 16:46:35 +0530 Subject: [PATCH 274/513] hwmon/misc: amd-sbi: Move core sbtsi support from hwmon to misc Move SBTSI(Side-Band Temperature Sensor Interface) core functionality out of the hwmon-only path and into drivers/misc/amd-sbi so it can be reused by non-hwmon consumers. I2C probe parsing is moved from drivers/hwmon/sbtsi_temp.c into drivers/misc/amd-sbi/tsi.c under CONFIG_AMD_SBTSI. The core driver stores struct sbtsi_data on the bus device and registers an auxiliary device amd-sbtsi.temp-sensor. per target. The current hwmon temp sensor will now require the CONFIG_AMD_SBTSI configuration as a new dependency. This split prepares the driver for additional interfaces while keeping hwmon support in hwmon subsystem on top of common SBTSI core logic. Add platform dependencies to clarify this driver is intended to run on the BMC and not on the managed node. Reviewed-by: Akshay Gupta Signed-off-by: Prathima Acked-by: Guenter Roeck Link: https://patch.msgid.link/20260710111642.850022-2-Akshay.Gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/Kconfig | 2 +- drivers/hwmon/sbtsi_temp.c | 71 ++++-------------- drivers/misc/amd-sbi/Kconfig | 13 ++++ drivers/misc/amd-sbi/Makefile | 3 + drivers/misc/amd-sbi/tsi.c | 135 ++++++++++++++++++++++++++++++++++ include/linux/misc/tsi.h | 34 +++++++++ 6 files changed, 199 insertions(+), 59 deletions(-) create mode 100644 drivers/misc/amd-sbi/tsi.c create mode 100644 include/linux/misc/tsi.h diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 2bfbcc033d59..2b4342abc0a7 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1976,7 +1976,7 @@ config SENSORS_SL28CPLD config SENSORS_SBTSI tristate "Emulated SB-TSI temperature sensor" - depends on I2C + depends on AMD_SBTSI help If you say yes here you get support for emulated temperature sensors on AMD SoCs with SB-TSI interface connected to a BMC device. diff --git a/drivers/hwmon/sbtsi_temp.c b/drivers/hwmon/sbtsi_temp.c index c28f8625cd3a..28258bf49922 100644 --- a/drivers/hwmon/sbtsi_temp.c +++ b/drivers/hwmon/sbtsi_temp.c @@ -7,13 +7,12 @@ * Copyright (c) 2020, Kun Yi */ +#include #include -#include -#include #include +#include #include -#include -#include +#include /* * SB-TSI registers only support SMBus byte data access. "_INT" registers are @@ -22,39 +21,17 @@ */ #define SBTSI_REG_TEMP_INT 0x01 /* RO */ #define SBTSI_REG_STATUS 0x02 /* RO */ -#define SBTSI_REG_CONFIG 0x03 /* RO */ #define SBTSI_REG_TEMP_HIGH_INT 0x07 /* RW */ #define SBTSI_REG_TEMP_LOW_INT 0x08 /* RW */ #define SBTSI_REG_TEMP_DEC 0x10 /* RW */ #define SBTSI_REG_TEMP_HIGH_DEC 0x13 /* RW */ #define SBTSI_REG_TEMP_LOW_DEC 0x14 /* RW */ -/* - * Bit for reporting value with temperature measurement range. - * bit == 0: Use default temperature range (0C to 255.875C). - * bit == 1: Use extended temperature range (-49C to +206.875C). - */ -#define SBTSI_CONFIG_EXT_RANGE_SHIFT 2 -/* - * ReadOrder bit specifies the reading order of integer and decimal part of - * CPU temperature for atomic reads. If bit == 0, reading integer part triggers - * latching of the decimal part, so integer part should be read first. - * If bit == 1, read order should be reversed. - */ -#define SBTSI_CONFIG_READ_ORDER_SHIFT 5 - #define SBTSI_TEMP_EXT_RANGE_ADJ 49000 #define SBTSI_TEMP_MIN 0 #define SBTSI_TEMP_MAX 255875 -/* Each client has this additional data */ -struct sbtsi_data { - struct i2c_client *client; - bool ext_range_mode; - bool read_order; -}; - /* * From SB-TSI spec: CPU temperature readings and limit registers encode the * temperature in increments of 0.125 from 0 to 255.875. The "high byte" @@ -195,55 +172,33 @@ static const struct hwmon_chip_info sbtsi_chip_info = { .info = sbtsi_info, }; -static int sbtsi_probe(struct i2c_client *client) +static int sbtsi_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) { - struct device *dev = &client->dev; + struct sbtsi_data *data = dev_get_drvdata(adev->dev.parent); + struct device *dev = &adev->dev; struct device *hwmon_dev; - struct sbtsi_data *data; - int err; - data = devm_kzalloc(dev, sizeof(struct sbtsi_data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->client = client; - - err = i2c_smbus_read_byte_data(data->client, SBTSI_REG_CONFIG); - if (err < 0) - return err; - data->ext_range_mode = FIELD_GET(BIT(SBTSI_CONFIG_EXT_RANGE_SHIFT), err); - data->read_order = FIELD_GET(BIT(SBTSI_CONFIG_READ_ORDER_SHIFT), err); - - hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + hwmon_dev = devm_hwmon_device_register_with_info(dev, "sbtsi", data, &sbtsi_chip_info, NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } -static const struct i2c_device_id sbtsi_id[] = { - { .name = "sbtsi" }, +static const struct auxiliary_device_id sbtsi_id[] = { + { .name = AMD_SBTSI_ADEV "." AMD_SBTSI_AUX_HWMON }, { } }; -MODULE_DEVICE_TABLE(i2c, sbtsi_id); +MODULE_DEVICE_TABLE(auxiliary, sbtsi_id); -static const struct of_device_id __maybe_unused sbtsi_of_match[] = { - { - .compatible = "amd,sbtsi", - }, - { }, -}; -MODULE_DEVICE_TABLE(of, sbtsi_of_match); - -static struct i2c_driver sbtsi_driver = { +static struct auxiliary_driver sbtsi_driver = { .driver = { .name = "sbtsi", - .of_match_table = of_match_ptr(sbtsi_of_match), }, .probe = sbtsi_probe, .id_table = sbtsi_id, }; - -module_i2c_driver(sbtsi_driver); +module_auxiliary_driver(sbtsi_driver); MODULE_AUTHOR("Kun Yi "); MODULE_DESCRIPTION("Hwmon driver for AMD SB-TSI emulated sensor"); diff --git a/drivers/misc/amd-sbi/Kconfig b/drivers/misc/amd-sbi/Kconfig index 30e7fad7356c..512251690e0e 100644 --- a/drivers/misc/amd-sbi/Kconfig +++ b/drivers/misc/amd-sbi/Kconfig @@ -20,3 +20,16 @@ config AMD_SBRMI_HWMON This provides support for RMI device hardware monitoring. If enabled, a hardware monitoring device will be created for each socket in the system. + +config AMD_SBTSI + tristate "AMD side band TSI support" + depends on I2C + depends on ARM || ARM64 || COMPILE_TEST + select AUXILIARY_BUS + help + Enables support for the AMD SB-TSI (Side Band Temperature Sensor + Interface) driver, which provides access to emulated CPU temperature + sensors on AMD SoCs via an I2C connected BMC device. + + This driver can also be built as a module. If so, the module will + be called sbtsi. diff --git a/drivers/misc/amd-sbi/Makefile b/drivers/misc/amd-sbi/Makefile index 38eaaa651fd9..28f95b9e204f 100644 --- a/drivers/misc/amd-sbi/Makefile +++ b/drivers/misc/amd-sbi/Makefile @@ -2,3 +2,6 @@ sbrmi-i2c-objs += rmi-i2c.o rmi-core.o sbrmi-i2c-$(CONFIG_AMD_SBRMI_HWMON) += rmi-hwmon.o obj-$(CONFIG_AMD_SBRMI_I2C) += sbrmi-i2c.o +# SBTSI Configuration +sbtsi-objs += tsi.o +obj-$(CONFIG_AMD_SBTSI) += sbtsi.o diff --git a/drivers/misc/amd-sbi/tsi.c b/drivers/misc/amd-sbi/tsi.c new file mode 100644 index 000000000000..67d08df28429 --- /dev/null +++ b/drivers/misc/amd-sbi/tsi.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * tsi.c - AMD SBTSI I2C core driver. Probes the SBTSI device over I2C + * and publishes an auxiliary device on the auxiliary bus. + * + * Copyright (C) 2026 Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include + +#define SBTSI_REG_CONFIG 0x03 /* RO */ + +/* + * Bit for reporting value with temperature measurement range. + * bit == 0: Use default temperature range (0C to 255.875C). + * bit == 1: Use extended temperature range (-49C to +206.875C). + */ +#define SBTSI_CONFIG_EXT_RANGE_SHIFT 2 + +/* + * ReadOrder bit specifies the reading order of integer and decimal part of + * CPU temperature for atomic reads. If bit == 0, reading integer part triggers + * latching of the decimal part, so integer part should be read first. + */ +#define SBTSI_CONFIG_READ_ORDER_SHIFT 5 + +static void sbtsi_adev_release(struct device *dev) +{ + kfree(to_auxiliary_dev(dev)); +} + +static void sbtsi_unregister_hwmon_adev(void *_adev) +{ + struct auxiliary_device *adev = _adev; + + auxiliary_device_delete(adev); + auxiliary_device_uninit(adev); +} + +/* + * Create and publish an auxiliary device. The hwmon driver in + * drivers/hwmon/sbtsi_temp.c binds to this device. + * + * @dev: I2C device (parent of the auxiliary device) + * @dev_addr: I2C address — used as the auxiliary device instance ID so that + * each socket gets a unique name. + */ +static int sbtsi_create_hwmon_adev(struct device *dev, u8 dev_addr) +{ + struct auxiliary_device *adev; + int ret; + + adev = kzalloc_obj(*adev); + if (!adev) + return -ENOMEM; + + adev->name = AMD_SBTSI_AUX_HWMON; + adev->id = dev_addr; + adev->dev.parent = dev; + adev->dev.release = sbtsi_adev_release; + + ret = auxiliary_device_init(adev); + if (ret) { + kfree(adev); + return ret; + } + + ret = __auxiliary_device_add(adev, AMD_SBTSI_ADEV); + if (ret) { + auxiliary_device_uninit(adev); + return ret; + } + + return devm_add_action_or_reset(dev, sbtsi_unregister_hwmon_adev, adev); +} + +static int sbtsi_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct sbtsi_data *data; + int err; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + err = i2c_smbus_read_byte_data(data->client, SBTSI_REG_CONFIG); + if (err < 0) + return err; + data->ext_range_mode = FIELD_GET(BIT(SBTSI_CONFIG_EXT_RANGE_SHIFT), err); + data->read_order = FIELD_GET(BIT(SBTSI_CONFIG_READ_ORDER_SHIFT), err); + + dev_set_drvdata(dev, data); + /* In a multi-socket system, devices that are otherwise identical do not + * share the same static address; each instance resides at a unique I2C + * client address on the same or different bus. Use the I2C client + * address as the auxiliary device instance ID to ensure each socket + * receives a distinct auxiliary device name. + */ + return sbtsi_create_hwmon_adev(dev, client->addr); +} + +static const struct i2c_device_id sbtsi_id[] = { + { .name = "sbtsi" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sbtsi_id); + +static const struct of_device_id __maybe_unused sbtsi_of_match[] = { + { + .compatible = "amd,sbtsi", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, sbtsi_of_match); + +static struct i2c_driver sbtsi_driver = { + .driver = { + .name = "sbtsi-i2c", + .of_match_table = of_match_ptr(sbtsi_of_match), + }, + .probe = sbtsi_i2c_probe, + .id_table = sbtsi_id, +}; + +module_i2c_driver(sbtsi_driver); + +MODULE_DESCRIPTION("AMD SB-TSI I2C core driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/misc/tsi.h b/include/linux/misc/tsi.h new file mode 100644 index 000000000000..befdc2d14160 --- /dev/null +++ b/include/linux/misc/tsi.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * AMD SBTSI shared data structure and auxiliary bus definitions. + * + * Copyright (C) 2026 Advanced Micro Devices, Inc. + */ + +#ifndef _LINUX_MISC_TSI_H_ +#define _LINUX_MISC_TSI_H_ + +#include +#include + +/** + * struct sbtsi_data - driver private data for an AMD SB-TSI device + * @client: underlying I2C client + * @ext_range_mode: sensor uses extended temperature range + * @read_order: if set, decimal part must be read before integer part + */ +struct sbtsi_data { + struct i2c_client *client; + bool ext_range_mode; + bool read_order; +}; + +/* + * Name of the auxiliary device published on the auxiliary bus by the core + * driver. The full device name is "amd-sbtsi.temp-sensor.". where + * is the auxiliary device instance id. + */ +#define AMD_SBTSI_ADEV "amd-sbtsi" +#define AMD_SBTSI_AUX_HWMON "temp-sensor" + +#endif /* _LINUX_MISC_TSI_H_ */ From 54a7848c24a5e786d25b209f61d3c86528bdce62 Mon Sep 17 00:00:00 2001 From: Prathima Date: Fri, 10 Jul 2026 16:46:36 +0530 Subject: [PATCH 275/513] hwmon: sbtsi_temp: Refactor temperature register access into helpers Extract the paired integer/decimal register reads and writes from the hwmon read/write callbacks into sbtsi_temp_read() and sbtsi_temp_write() helpers. This consolidates error handling and respects the ReadOrder bit for atomic temperature latching. This keeps register access independent while preserving existing hwmon functionality. Reviewed-by: Akshay Gupta Signed-off-by: Prathima Acked-by: Guenter Roeck Link: https://patch.msgid.link/20260710111642.850022-3-Akshay.Gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/sbtsi_temp.c | 84 +++++++++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 23 deletions(-) diff --git a/drivers/hwmon/sbtsi_temp.c b/drivers/hwmon/sbtsi_temp.c index 28258bf49922..078f4ab25bde 100644 --- a/drivers/hwmon/sbtsi_temp.c +++ b/drivers/hwmon/sbtsi_temp.c @@ -61,40 +61,82 @@ static inline void sbtsi_mc_to_reg(s32 temp, u8 *integer, u8 *decimal) *decimal = (temp & 0x7) << 5; } +/* + * Read integer and decimal parts of an SB-TSI temperature register pair + * The read order is determined by the ReadOrder bit to ensure atomic latching. + */ +static int sbtsi_temp_read(struct sbtsi_data *data, u8 reg1, u8 reg2, + u8 *val1, u8 *val2) +{ + int ret; + + ret = i2c_smbus_read_byte_data(data->client, reg1); + if (ret < 0) + return ret; + *val1 = ret; + ret = i2c_smbus_read_byte_data(data->client, reg2); + if (ret < 0) + return ret; + *val2 = ret; + return 0; +} + +/* + * Write integer and decimal parts of an SB-TSI temperature register pair. + */ +static int sbtsi_temp_write(struct sbtsi_data *data, u8 reg_int, u8 reg_dec, + u8 val_int, u8 val_dec) +{ + int ret; + + ret = i2c_smbus_write_byte_data(data->client, reg_int, val_int); + if (!ret) + ret = i2c_smbus_write_byte_data(data->client, reg_dec, val_dec); + return ret; +} + static int sbtsi_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { struct sbtsi_data *data = dev_get_drvdata(dev); s32 temp_int, temp_dec; + int err; + u8 val_int, val_dec; switch (attr) { case hwmon_temp_input: - if (data->read_order) { - temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC); - temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT); - } else { - temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT); - temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC); - } + if (data->read_order) + err = sbtsi_temp_read(data, + SBTSI_REG_TEMP_DEC, SBTSI_REG_TEMP_INT, + &val_dec, &val_int); + else + err = sbtsi_temp_read(data, + SBTSI_REG_TEMP_INT, SBTSI_REG_TEMP_DEC, + &val_int, &val_dec); + if (err < 0) + return err; break; case hwmon_temp_max: - temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_INT); - temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_DEC); + err = sbtsi_temp_read(data, + SBTSI_REG_TEMP_HIGH_INT, SBTSI_REG_TEMP_HIGH_DEC, + &val_int, &val_dec); + if (err < 0) + return err; break; case hwmon_temp_min: - temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_INT); - temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_DEC); + err = sbtsi_temp_read(data, + SBTSI_REG_TEMP_LOW_INT, SBTSI_REG_TEMP_LOW_DEC, + &val_int, &val_dec); + + if (err < 0) + return err; break; default: return -EINVAL; } - - if (temp_int < 0) - return temp_int; - if (temp_dec < 0) - return temp_dec; - + temp_int = val_int; + temp_dec = val_dec; *val = sbtsi_reg_to_mc(temp_int, temp_dec); if (data->ext_range_mode) *val -= SBTSI_TEMP_EXT_RANGE_ADJ; @@ -106,7 +148,7 @@ static int sbtsi_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { struct sbtsi_data *data = dev_get_drvdata(dev); - int reg_int, reg_dec, err; + int reg_int, reg_dec; u8 temp_int, temp_dec; switch (attr) { @@ -127,11 +169,7 @@ static int sbtsi_write(struct device *dev, enum hwmon_sensor_types type, val = clamp_val(val, SBTSI_TEMP_MIN, SBTSI_TEMP_MAX); sbtsi_mc_to_reg(val, &temp_int, &temp_dec); - err = i2c_smbus_write_byte_data(data->client, reg_int, temp_int); - if (err) - return err; - - return i2c_smbus_write_byte_data(data->client, reg_dec, temp_dec); + return sbtsi_temp_write(data, reg_int, reg_dec, temp_int, temp_dec); } static umode_t sbtsi_is_visible(const void *data, From d4f8babf8e8ffb8cd75d12d1003d2a2c17a53420 Mon Sep 17 00:00:00 2001 From: Prathima Date: Fri, 10 Jul 2026 16:46:37 +0530 Subject: [PATCH 276/513] hwmon/misc: amd-sbi: Move sbtsi register transfer to core abstraction Move the I2C read/write byte operations from the sbtsi hwmon driver into a common sbtsi_xfer() function in tsi-core.c. This decouples the hwmon sensor driver from the underlying bus transport, preparing for I3C support in a subsequent patch. This patch does not introduce any functional changes. The updates are limited to code organization/cleanup and should not affect the runtime behavior of the driver Reviewed-by: Akshay Gupta Signed-off-by: Prathima Acked-by: Guenter Roeck Link: https://patch.msgid.link/20260710111642.850022-4-Akshay.Gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/sbtsi_temp.c | 17 ++++++----------- drivers/misc/amd-sbi/Makefile | 2 +- drivers/misc/amd-sbi/tsi-core.c | 30 ++++++++++++++++++++++++++++++ include/linux/misc/tsi.h | 13 +++++++++++++ 4 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 drivers/misc/amd-sbi/tsi-core.c diff --git a/drivers/hwmon/sbtsi_temp.c b/drivers/hwmon/sbtsi_temp.c index 078f4ab25bde..d7ae986d824c 100644 --- a/drivers/hwmon/sbtsi_temp.c +++ b/drivers/hwmon/sbtsi_temp.c @@ -70,15 +70,10 @@ static int sbtsi_temp_read(struct sbtsi_data *data, u8 reg1, u8 reg2, { int ret; - ret = i2c_smbus_read_byte_data(data->client, reg1); - if (ret < 0) - return ret; - *val1 = ret; - ret = i2c_smbus_read_byte_data(data->client, reg2); - if (ret < 0) - return ret; - *val2 = ret; - return 0; + ret = sbtsi_xfer(data, reg1, val1, true); + if (!ret) + ret = sbtsi_xfer(data, reg2, val2, true); + return ret; } /* @@ -89,9 +84,9 @@ static int sbtsi_temp_write(struct sbtsi_data *data, u8 reg_int, u8 reg_dec, { int ret; - ret = i2c_smbus_write_byte_data(data->client, reg_int, val_int); + ret = sbtsi_xfer(data, reg_int, &val_int, false); if (!ret) - ret = i2c_smbus_write_byte_data(data->client, reg_dec, val_dec); + ret = sbtsi_xfer(data, reg_dec, &val_dec, false); return ret; } diff --git a/drivers/misc/amd-sbi/Makefile b/drivers/misc/amd-sbi/Makefile index 28f95b9e204f..ce9321f5c601 100644 --- a/drivers/misc/amd-sbi/Makefile +++ b/drivers/misc/amd-sbi/Makefile @@ -3,5 +3,5 @@ sbrmi-i2c-objs += rmi-i2c.o rmi-core.o sbrmi-i2c-$(CONFIG_AMD_SBRMI_HWMON) += rmi-hwmon.o obj-$(CONFIG_AMD_SBRMI_I2C) += sbrmi-i2c.o # SBTSI Configuration -sbtsi-objs += tsi.o +sbtsi-objs += tsi.o tsi-core.o obj-$(CONFIG_AMD_SBTSI) += sbtsi.o diff --git a/drivers/misc/amd-sbi/tsi-core.c b/drivers/misc/amd-sbi/tsi-core.c new file mode 100644 index 000000000000..6ef1831515bb --- /dev/null +++ b/drivers/misc/amd-sbi/tsi-core.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * tsi-core.c - file defining SB-TSI protocols compliant + * AMD SoC device. + * + * Copyright (C) 2026 Advanced Micro Devices, Inc. + */ + +#include +#include + +/* I2C transfer function */ +static int sbtsi_i2c_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read) +{ + if (is_read) { + int ret = i2c_smbus_read_byte_data(data->client, reg); + + if (ret < 0) + return ret; + *val = ret; + return 0; + } + return i2c_smbus_write_byte_data(data->client, reg, *val); +} + +int sbtsi_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read) +{ + return sbtsi_i2c_xfer(data, reg, val, is_read); +} +EXPORT_SYMBOL_GPL(sbtsi_xfer); diff --git a/include/linux/misc/tsi.h b/include/linux/misc/tsi.h index befdc2d14160..2d2709f1ff32 100644 --- a/include/linux/misc/tsi.h +++ b/include/linux/misc/tsi.h @@ -31,4 +31,17 @@ struct sbtsi_data { #define AMD_SBTSI_ADEV "amd-sbtsi" #define AMD_SBTSI_AUX_HWMON "temp-sensor" +/** + * sbtsi_xfer - Perform a register read or write transfer on an AMD SB-TSI device. + * + * @data: Pointer to the sbtsi_data structure containing the device context + * @reg: Register address to access. + * @val: Pointer to the value to read into or write from. + * @is_read: If true, performs a read transfer and stores the result in @val. + * If false, performs a write transfer using the value in @val. + * + * Returns 0 on success, or a negative error code on failure. + */ +int sbtsi_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read); + #endif /* _LINUX_MISC_TSI_H_ */ From ba27ea7abd35d260e3b489f985ec2b4113c0fd85 Mon Sep 17 00:00:00 2001 From: Prathima Date: Fri, 10 Jul 2026 16:46:38 +0530 Subject: [PATCH 277/513] misc: amd-sbi: Consolidate Common SBTSI Probe Path Refactor shared probe procedures into sbtsi_probe_common() to ensure that I2C and I3C probes focus solely on bus-specific allocation and device configuration. The utility function reads the configuration register via sbtsi_xfer(), initializes ext_range_mode and read_order, assigns the driver data, and registers the hwmon auxiliary device. Routing register access through sbtsi_xfer() keeps the probe path bus-agnostic, so no transfer logic has to be duplicated when SB-TSI over I3C support is added in a later patch. Reviewed-by: Akshay Gupta Signed-off-by: Prathima Link: https://patch.msgid.link/20260710111642.850022-5-Akshay.Gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/amd-sbi/tsi.c | 26 ++++++++++++++++++-------- include/linux/misc/tsi.h | 2 ++ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/misc/amd-sbi/tsi.c b/drivers/misc/amd-sbi/tsi.c index 67d08df28429..35b9f40741e7 100644 --- a/drivers/misc/amd-sbi/tsi.c +++ b/drivers/misc/amd-sbi/tsi.c @@ -79,31 +79,41 @@ static int sbtsi_create_hwmon_adev(struct device *dev, u8 dev_addr) return devm_add_action_or_reset(dev, sbtsi_unregister_hwmon_adev, adev); } +static int sbtsi_probe_common(struct device *dev, struct sbtsi_data *data) +{ + u8 val; + int err; + + err = sbtsi_xfer(data, SBTSI_REG_CONFIG, &val, true); + if (err) + return err; + + data->ext_range_mode = FIELD_GET(BIT(SBTSI_CONFIG_EXT_RANGE_SHIFT), val); + data->read_order = FIELD_GET(BIT(SBTSI_CONFIG_READ_ORDER_SHIFT), val); + + dev_set_drvdata(dev, data); + return sbtsi_create_hwmon_adev(dev, data->dev_addr); +} + static int sbtsi_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct sbtsi_data *data; - int err; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->client = client; - err = i2c_smbus_read_byte_data(data->client, SBTSI_REG_CONFIG); - if (err < 0) - return err; - data->ext_range_mode = FIELD_GET(BIT(SBTSI_CONFIG_EXT_RANGE_SHIFT), err); - data->read_order = FIELD_GET(BIT(SBTSI_CONFIG_READ_ORDER_SHIFT), err); - dev_set_drvdata(dev, data); /* In a multi-socket system, devices that are otherwise identical do not * share the same static address; each instance resides at a unique I2C * client address on the same or different bus. Use the I2C client * address as the auxiliary device instance ID to ensure each socket * receives a distinct auxiliary device name. */ - return sbtsi_create_hwmon_adev(dev, client->addr); + data->dev_addr = client->addr; + return sbtsi_probe_common(dev, data); } static const struct i2c_device_id sbtsi_id[] = { diff --git a/include/linux/misc/tsi.h b/include/linux/misc/tsi.h index 2d2709f1ff32..6533879cc358 100644 --- a/include/linux/misc/tsi.h +++ b/include/linux/misc/tsi.h @@ -14,11 +14,13 @@ /** * struct sbtsi_data - driver private data for an AMD SB-TSI device * @client: underlying I2C client + * @dev_addr: I2C device address, used as the auxiliary device instance id * @ext_range_mode: sensor uses extended temperature range * @read_order: if set, decimal part must be read before integer part */ struct sbtsi_data { struct i2c_client *client; + u8 dev_addr; bool ext_range_mode; bool read_order; }; From f61a6fd4593bddc0f2109a1d3359fd2a88124d33 Mon Sep 17 00:00:00 2001 From: Prathima Date: Fri, 10 Jul 2026 16:46:39 +0530 Subject: [PATCH 278/513] misc: amd-sbi: Add support for SB-TSI over I3C AMD SB-TSI temperature sensors can be accessed over both I2C and I3C buses depending on the platform configuration. Extend the SB-TSI driver to support both I2C and I3C bus interfaces by selecting the appropriate transport based on the probed bus type. The driver maintains backward compatibility with existing I2C deployments while enabling support for systems using the I3C bus. Register both I2C and I3C drivers using module_i3c_i2c_driver() and update the Kconfig dependency from I2C to I3C_OR_I2C. Reviewed-by: Akshay Gupta Signed-off-by: Prathima Link: https://patch.msgid.link/20260710111642.850022-6-Akshay.Gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/amd-sbi/Kconfig | 4 +- drivers/misc/amd-sbi/tsi-core.c | 55 +++++++++++++++++++++++++++- drivers/misc/amd-sbi/tsi-core.h | 26 +++++++++++++ drivers/misc/amd-sbi/tsi.c | 65 +++++++++++++++++++++++++++++++-- include/linux/misc/tsi.h | 11 +++++- 5 files changed, 152 insertions(+), 9 deletions(-) create mode 100644 drivers/misc/amd-sbi/tsi-core.h diff --git a/drivers/misc/amd-sbi/Kconfig b/drivers/misc/amd-sbi/Kconfig index 512251690e0e..1a96b71f8506 100644 --- a/drivers/misc/amd-sbi/Kconfig +++ b/drivers/misc/amd-sbi/Kconfig @@ -23,13 +23,13 @@ config AMD_SBRMI_HWMON config AMD_SBTSI tristate "AMD side band TSI support" - depends on I2C + depends on I3C_OR_I2C depends on ARM || ARM64 || COMPILE_TEST select AUXILIARY_BUS help Enables support for the AMD SB-TSI (Side Band Temperature Sensor Interface) driver, which provides access to emulated CPU temperature - sensors on AMD SoCs via an I2C connected BMC device. + sensors on AMD SoCs via an I2C/I3C connected BMC device. This driver can also be built as a module. If so, the module will be called sbtsi. diff --git a/drivers/misc/amd-sbi/tsi-core.c b/drivers/misc/amd-sbi/tsi-core.c index 6ef1831515bb..1c6f37f26d94 100644 --- a/drivers/misc/amd-sbi/tsi-core.c +++ b/drivers/misc/amd-sbi/tsi-core.c @@ -7,7 +7,12 @@ */ #include -#include +#include "tsi-core.h" + +static inline struct sbtsi_i3c_priv *to_sbtsi_i3c_priv(struct sbtsi_data *data) +{ + return container_of(data, struct sbtsi_i3c_priv, data); +} /* I2C transfer function */ static int sbtsi_i2c_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read) @@ -23,8 +28,56 @@ static int sbtsi_i2c_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read return i2c_smbus_write_byte_data(data->client, reg, *val); } +/* I3C read transfer function */ +static int sbtsi_i3c_read(struct sbtsi_data *data, u8 reg, u8 *val) +{ + struct sbtsi_i3c_priv *priv = to_sbtsi_i3c_priv(data); + struct i3c_xfer xfers[2] = { }; + int ret; + + priv->tx[0] = reg; + + /* Write the register address (DMA_TO_DEVICE). */ + xfers[0].rnw = false; + xfers[0].len = 1; + xfers[0].data.out = priv->tx; + + /* Read the data byte into a separate buffer (DMA_FROM_DEVICE). */ + xfers[1].rnw = true; + xfers[1].len = 1; + xfers[1].data.in = &priv->rx; + + ret = i3c_device_do_xfers(data->i3cdev, xfers, 2, I3C_SDR); + if (ret) + return ret; + + *val = priv->rx; + return ret; +} + +/* I3C write transfer function */ +static int sbtsi_i3c_write(struct sbtsi_data *data, u8 reg, u8 val) +{ + struct sbtsi_i3c_priv *priv = to_sbtsi_i3c_priv(data); + struct i3c_xfer xfers = { + .rnw = false, + .len = 2, + .data.out = priv->tx, + }; + + priv->tx[0] = reg; + priv->tx[1] = val; + + return i3c_device_do_xfers(data->i3cdev, &xfers, 1, I3C_SDR); +} + +/* Unified transfer function for I2C and I3C access */ int sbtsi_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read) { + if (data->is_i3c) + return is_read ? sbtsi_i3c_read(data, reg, val) + : sbtsi_i3c_write(data, reg, *val); + return sbtsi_i2c_xfer(data, reg, val, is_read); } EXPORT_SYMBOL_GPL(sbtsi_xfer); diff --git a/drivers/misc/amd-sbi/tsi-core.h b/drivers/misc/amd-sbi/tsi-core.h new file mode 100644 index 000000000000..7e8c0e7c3bcf --- /dev/null +++ b/drivers/misc/amd-sbi/tsi-core.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * AMD SBTSI core driver private definitions. + * + * Copyright (C) 2026 Advanced Micro Devices, Inc. + */ + +#ifndef _LINUX_TSI_CORE_H_ +#define _LINUX_TSI_CORE_H_ + +#include +#include + +/** + * struct sbtsi_i3c_priv - per-device state for I3C SBTSI (includes DMA-safe buffers) + * @data: public device state exposed via dev_set_drvdata() + * @tx: outgoing I3C bytes (DMA_TO_DEVICE); [0] register address, [1] value + * @rx: incoming I3C data byte (DMA_FROM_DEVICE) + */ +struct sbtsi_i3c_priv { + struct sbtsi_data data; + u8 tx[2]; + u8 rx __aligned(ARCH_DMA_MINALIGN); +}; + +#endif /* _LINUX_TSI_CORE_H_ */ diff --git a/drivers/misc/amd-sbi/tsi.c b/drivers/misc/amd-sbi/tsi.c index 35b9f40741e7..1530f440a020 100644 --- a/drivers/misc/amd-sbi/tsi.c +++ b/drivers/misc/amd-sbi/tsi.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * tsi.c - AMD SBTSI I2C core driver. Probes the SBTSI device over I2C + * tsi.c - AMD SBTSI I2C/I3C core driver. Probes the SBTSI device over I2C/I3C * and publishes an auxiliary device on the auxiliary bus. * * Copyright (C) 2026 Advanced Micro Devices, Inc. @@ -10,8 +10,8 @@ #include #include #include -#include #include +#include "tsi-core.h" #define SBTSI_REG_CONFIG 0x03 /* RO */ @@ -104,6 +104,7 @@ static int sbtsi_i2c_probe(struct i2c_client *client) if (!data) return -ENOMEM; + data->is_i3c = false; data->client = client; /* In a multi-socket system, devices that are otherwise identical do not @@ -139,7 +140,63 @@ static struct i2c_driver sbtsi_driver = { .id_table = sbtsi_id, }; -module_i2c_driver(sbtsi_driver); +static int sbtsi_i3c_probe(struct i3c_device *i3cdev) +{ + struct device *dev = i3cdev_to_dev(i3cdev); + struct i3c_device_info devinfo; + struct sbtsi_i3c_priv *i3c_priv; + struct sbtsi_data *data; -MODULE_DESCRIPTION("AMD SB-TSI I2C core driver"); + /* + * AMD OOB devices differ on basis of Instance ID, + * for SBTSI, instance ID is 0. + * As the device Id match is not on basis of Instance ID, + * add the below check to probe the SBTSI device only and + * not other OOB devices. + */ + i3c_device_get_info(i3cdev, &devinfo); + if (I3C_PID_INSTANCE_ID(devinfo.pid) != 0) + return -ENXIO; + + i3c_priv = devm_kzalloc(dev, sizeof(*i3c_priv), GFP_KERNEL); + if (!i3c_priv) + return -ENOMEM; + + data = &i3c_priv->data; + data->i3cdev = i3cdev; + data->is_i3c = true; + /* + * In a multi-socket system, otherwise identical devices do not share + * the same address; each instance is enumerated with a distinct dynamic + * (assigned) address on the I3C bus. Use that address (passed in as + * dev_addr) as the auxiliary device instance ID so that every socket + * gets a unique auxiliary device name. + */ + data->dev_addr = devinfo.dyn_addr; + + return sbtsi_probe_common(dev, data); +} + +static const struct i3c_device_id sbtsi_i3c_id[] = { + /* PID for AMD SBTSI device */ + I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x1, NULL), /* Socket:0, Turin and Genoa */ + 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, sbtsi_i3c_id); + +static struct i3c_driver sbtsi_i3c_driver = { + .driver = { + .name = "sbtsi-i3c", + }, + .probe = sbtsi_i3c_probe, + .id_table = sbtsi_i3c_id, +}; + +module_i3c_i2c_driver(sbtsi_i3c_driver, &sbtsi_driver); + +MODULE_DESCRIPTION("AMD SB-TSI I2C/I3C core driver"); MODULE_LICENSE("GPL"); diff --git a/include/linux/misc/tsi.h b/include/linux/misc/tsi.h index 6533879cc358..0bdd9d923f92 100644 --- a/include/linux/misc/tsi.h +++ b/include/linux/misc/tsi.h @@ -9,20 +9,27 @@ #define _LINUX_MISC_TSI_H_ #include +#include #include /** * struct sbtsi_data - driver private data for an AMD SB-TSI device * @client: underlying I2C client - * @dev_addr: I2C device address, used as the auxiliary device instance id + * @i3cdev: underlying I3C device (when using I3C bus) + * @dev_addr: I2C/I3C device address, used as the auxiliary device instance id * @ext_range_mode: sensor uses extended temperature range * @read_order: if set, decimal part must be read before integer part + * @is_i3c: true when the device is accessed over I3C */ struct sbtsi_data { - struct i2c_client *client; + union { + struct i2c_client *client; + struct i3c_device *i3cdev; + }; u8 dev_addr; bool ext_range_mode; bool read_order; + bool is_i3c; }; /* From 48ad55cda029dca47f4cd76b88f18fdfb1d309a8 Mon Sep 17 00:00:00 2001 From: Prathima Date: Fri, 10 Jul 2026 16:46:40 +0530 Subject: [PATCH 279/513] misc: amd-sbi: Add SBTSI ioctl register transfer interface Implement IOCTL interface for SB-TSI driver to enable userspace access to TSI register read/write operations through the AMD Advanced Platform Management Link (APML) protocol. Add an ioctl command (SBTSI_IOCTL_REG_XFER_CMD) that accepts a register address, data byte, and direction flag. The mutex is taken on the ioctl path here; the hwmon path is placed under the same lock in the next patch, which completes serialization between the hwmon and ioctl paths. Reviewed-by: Akshay Gupta Signed-off-by: Prathima Link: https://patch.msgid.link/20260710111642.850022-7-Akshay.Gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/amd-sbi/tsi-core.c | 126 +++++++++++++++++++++++++++++++- drivers/misc/amd-sbi/tsi-core.h | 3 + drivers/misc/amd-sbi/tsi.c | 38 +++++++++- include/linux/misc/tsi.h | 16 ++++ include/uapi/misc/amd-apml.h | 23 ++++++ 5 files changed, 202 insertions(+), 4 deletions(-) diff --git a/drivers/misc/amd-sbi/tsi-core.c b/drivers/misc/amd-sbi/tsi-core.c index 1c6f37f26d94..5c178702c67a 100644 --- a/drivers/misc/amd-sbi/tsi-core.c +++ b/drivers/misc/amd-sbi/tsi-core.c @@ -6,7 +6,11 @@ * Copyright (C) 2026 Advanced Micro Devices, Inc. */ +#include +#include #include +#include +#include #include "tsi-core.h" static inline struct sbtsi_i3c_priv *to_sbtsi_i3c_priv(struct sbtsi_data *data) @@ -14,6 +18,17 @@ static inline struct sbtsi_i3c_priv *to_sbtsi_i3c_priv(struct sbtsi_data *data) return container_of(data, struct sbtsi_i3c_priv, data); } +void sbtsi_data_release(struct kref *kref) +{ + struct sbtsi_data *data = container_of(kref, struct sbtsi_data, kref); + + mutex_destroy(&data->lock); + if (data->is_i3c) + kfree(to_sbtsi_i3c_priv(data)); + else + kfree(data); +} + /* I2C transfer function */ static int sbtsi_i2c_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read) { @@ -77,7 +92,116 @@ int sbtsi_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read) if (data->is_i3c) return is_read ? sbtsi_i3c_read(data, reg, val) : sbtsi_i3c_write(data, reg, *val); - return sbtsi_i2c_xfer(data, reg, val, is_read); } EXPORT_SYMBOL_GPL(sbtsi_xfer); + +/* + * The mutex protects against concurrent register transfers to the device + * over the shared bus. + */ +static int sbtsi_xfer_ioctl(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read) +{ + guard(sbtsi)(data); + + if (data->detached) + return -ENODEV; + + return sbtsi_xfer(data, reg, val, is_read); +} + +static int apml_tsi_reg_xfer(struct sbtsi_data *data, + struct apml_tsi_xfer_msg __user *arg) +{ + struct apml_tsi_xfer_msg msg = { 0 }; + int ret; + + if (copy_from_user(&msg, arg, sizeof(struct apml_tsi_xfer_msg))) + return -EFAULT; + + /* + * rflag is a boolean direction flag (0 = write, 1 = read). Reject + * any other value so the upper values stay reserved for future + * extensions instead of being silently treated as a read. + */ + if (msg.pad || msg.rflag > 1) + return -EINVAL; + + ret = sbtsi_xfer_ioctl(data, msg.reg_addr, &msg.data_in_out, msg.rflag); + + if (msg.rflag && !ret) { + if (copy_to_user(arg, &msg, sizeof(struct apml_tsi_xfer_msg))) + return -EFAULT; + } + return ret; +} + +static int sbtsi_open(struct inode *inode, struct file *fp) +{ + struct sbtsi_data *data; + + data = container_of(fp->private_data, struct sbtsi_data, sbtsi_misc_dev); + scoped_guard(sbtsi, data) { + if (data->detached) + return -ENODEV; + } + + kref_get(&data->kref); + + return 0; +} + +static int sbtsi_release(struct inode *inode, struct file *fp) +{ + struct sbtsi_data *data; + + data = container_of(fp->private_data, struct sbtsi_data, sbtsi_misc_dev); + kref_put(&data->kref, sbtsi_data_release); + return 0; +} + +static long sbtsi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct sbtsi_data *data; + + data = container_of(fp->private_data, struct sbtsi_data, sbtsi_misc_dev); + switch (cmd) { + case SBTSI_IOCTL_REG_XFER_CMD: + return apml_tsi_reg_xfer(data, argp); + default: + return -ENOTTY; + } +} + +static const struct file_operations sbtsi_fops = { + .owner = THIS_MODULE, + .open = sbtsi_open, + .release = sbtsi_release, + .unlocked_ioctl = sbtsi_ioctl, + .compat_ioctl = compat_ptr_ioctl, +}; + +int create_misc_tsi_device(struct sbtsi_data *data, struct device *dev) +{ + int ret; + + data->sbtsi_misc_dev.name = devm_kasprintf(dev, GFP_KERNEL, + "sbtsi-%x", data->dev_addr); + if (!data->sbtsi_misc_dev.name) + return -ENOMEM; + data->sbtsi_misc_dev.minor = MISC_DYNAMIC_MINOR; + data->sbtsi_misc_dev.fops = &sbtsi_fops; + data->sbtsi_misc_dev.parent = dev; + data->sbtsi_misc_dev.nodename = devm_kasprintf(dev, GFP_KERNEL, + "sbtsi-%x", data->dev_addr); + if (!data->sbtsi_misc_dev.nodename) + return -ENOMEM; + data->sbtsi_misc_dev.mode = 0600; + + ret = misc_register(&data->sbtsi_misc_dev); + if (ret) + return ret; + + return 0; +} diff --git a/drivers/misc/amd-sbi/tsi-core.h b/drivers/misc/amd-sbi/tsi-core.h index 7e8c0e7c3bcf..4cf55c46230e 100644 --- a/drivers/misc/amd-sbi/tsi-core.h +++ b/drivers/misc/amd-sbi/tsi-core.h @@ -23,4 +23,7 @@ struct sbtsi_i3c_priv { u8 rx __aligned(ARCH_DMA_MINALIGN); }; +int create_misc_tsi_device(struct sbtsi_data *data, struct device *dev); + +void sbtsi_data_release(struct kref *kref); #endif /* _LINUX_TSI_CORE_H_ */ diff --git a/drivers/misc/amd-sbi/tsi.c b/drivers/misc/amd-sbi/tsi.c index 1530f440a020..f06f417f451c 100644 --- a/drivers/misc/amd-sbi/tsi.c +++ b/drivers/misc/amd-sbi/tsi.c @@ -42,6 +42,23 @@ static void sbtsi_unregister_hwmon_adev(void *_adev) auxiliary_device_uninit(adev); } +static void sbtsi_misc_unregister(void *arg) +{ + struct sbtsi_data *data = arg; + + misc_deregister(&data->sbtsi_misc_dev); + + guard(sbtsi)(data); + data->detached = true; +} + +static void sbtsi_driver_unref(void *arg) +{ + struct sbtsi_data *data = arg; + + kref_put(&data->kref, sbtsi_data_release); +} + /* * Create and publish an auxiliary device. The hwmon driver in * drivers/hwmon/sbtsi_temp.c binds to this device. @@ -84,6 +101,13 @@ static int sbtsi_probe_common(struct device *dev, struct sbtsi_data *data) u8 val; int err; + mutex_init(&data->lock); + kref_init(&data->kref); + + err = devm_add_action_or_reset(dev, sbtsi_driver_unref, data); + if (err) + return err; + err = sbtsi_xfer(data, SBTSI_REG_CONFIG, &val, true); if (err) return err; @@ -92,7 +116,15 @@ static int sbtsi_probe_common(struct device *dev, struct sbtsi_data *data) data->read_order = FIELD_GET(BIT(SBTSI_CONFIG_READ_ORDER_SHIFT), val); dev_set_drvdata(dev, data); - return sbtsi_create_hwmon_adev(dev, data->dev_addr); + err = sbtsi_create_hwmon_adev(dev, data->dev_addr); + if (err < 0) + return err; + + err = create_misc_tsi_device(data, dev); + if (err) + return err; + + return devm_add_action_or_reset(dev, sbtsi_misc_unregister, data); } static int sbtsi_i2c_probe(struct i2c_client *client) @@ -100,7 +132,7 @@ static int sbtsi_i2c_probe(struct i2c_client *client) struct device *dev = &client->dev; struct sbtsi_data *data; - data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + data = kzalloc_obj(*data); if (!data) return -ENOMEM; @@ -158,7 +190,7 @@ static int sbtsi_i3c_probe(struct i3c_device *i3cdev) if (I3C_PID_INSTANCE_ID(devinfo.pid) != 0) return -ENXIO; - i3c_priv = devm_kzalloc(dev, sizeof(*i3c_priv), GFP_KERNEL); + i3c_priv = kzalloc_obj(*i3c_priv); if (!i3c_priv) return -ENOMEM; diff --git a/include/linux/misc/tsi.h b/include/linux/misc/tsi.h index 0bdd9d923f92..5273c44688f0 100644 --- a/include/linux/misc/tsi.h +++ b/include/linux/misc/tsi.h @@ -8,30 +8,46 @@ #ifndef _LINUX_MISC_TSI_H_ #define _LINUX_MISC_TSI_H_ +#include #include #include +#include +#include +#include #include /** * struct sbtsi_data - driver private data for an AMD SB-TSI device * @client: underlying I2C client * @i3cdev: underlying I3C device (when using I3C bus) + * @sbtsi_misc_dev: miscdevice exposing ioctl interface at /dev/sbtsi- + * @lock: mutex protecting concurrent access to the device + * @kref: reference count; keeps @sbtsi_data alive while misc fds are open * @dev_addr: I2C/I3C device address, used as the auxiliary device instance id + * and name the misc device node * @ext_range_mode: sensor uses extended temperature range * @read_order: if set, decimal part must be read before integer part * @is_i3c: true when the device is accessed over I3C + * @detached: set on driver unbind; open/ioctl return -ENODEV afterward */ struct sbtsi_data { union { struct i2c_client *client; struct i3c_device *i3cdev; }; + struct miscdevice sbtsi_misc_dev; + struct mutex lock; /* protects concurrent access to the device */ + struct kref kref; u8 dev_addr; bool ext_range_mode; bool read_order; bool is_i3c; + bool detached; }; +DEFINE_GUARD(sbtsi, struct sbtsi_data *, mutex_lock(&_T->lock), + mutex_unlock(&_T->lock)) + /* * Name of the auxiliary device published on the auxiliary bus by the core * driver. The full device name is "amd-sbtsi.temp-sensor.". where diff --git a/include/uapi/misc/amd-apml.h b/include/uapi/misc/amd-apml.h index 745b3338fc06..8a85f79b0938 100644 --- a/include/uapi/misc/amd-apml.h +++ b/include/uapi/misc/amd-apml.h @@ -73,6 +73,13 @@ struct apml_reg_xfer_msg { __u8 rflag; }; +struct apml_tsi_xfer_msg { + __u8 reg_addr; /* TSI register address offset */ + __u8 data_in_out; /* Register data for read/write */ + __u8 rflag; /* Register read or write */ + __u8 pad; /* Explicit padding */ +}; + /* * AMD sideband interface base IOCTL */ @@ -149,4 +156,20 @@ struct apml_reg_xfer_msg { */ #define SBRMI_IOCTL_REG_XFER_CMD _IOWR(SB_BASE_IOCTL_NR, 3, struct apml_reg_xfer_msg) +/** + * DOC: SBTSI_IOCTL_REG_XFER_CMD + * + * @Parameters + * + * @struct apml_tsi_xfer_msg + * Pointer to the &struct apml_tsi_xfer_msg that will contain the protocol + * information + * + * @Description + * IOCTL command for APML TSI messages using generic _IOWR + * The IOCTL provides userspace access to AMD sideband TSI register xfer protocol + * - TSI protocol to read/write temperature sensor registers + */ +#define SBTSI_IOCTL_REG_XFER_CMD _IOWR(SB_BASE_IOCTL_NR, 4, struct apml_tsi_xfer_msg) + #endif /*_AMD_APML_H_*/ From 47f6311f8f523ed3671611e12f94e0cbb193861b Mon Sep 17 00:00:00 2001 From: Prathima Date: Fri, 10 Jul 2026 16:46:41 +0530 Subject: [PATCH 280/513] hwmon: Add mutex protecting for sbtsi read/write through hwmon Add a mutex and take it around SBTSI read/write paths so that only one transaction runs at a time. The lock is held only for the duration of the bus transfer and associated driver bookkeeping, not across blocking work unrelated to SBTSI. This is a concurrency hardening fix. Reviewed-by: Akshay Gupta Signed-off-by: Prathima Acked-by: Guenter Roeck Link: https://patch.msgid.link/20260710111642.850022-8-Akshay.Gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/sbtsi_temp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hwmon/sbtsi_temp.c b/drivers/hwmon/sbtsi_temp.c index d7ae986d824c..11c8108d69b2 100644 --- a/drivers/hwmon/sbtsi_temp.c +++ b/drivers/hwmon/sbtsi_temp.c @@ -70,6 +70,7 @@ static int sbtsi_temp_read(struct sbtsi_data *data, u8 reg1, u8 reg2, { int ret; + guard(sbtsi)(data); ret = sbtsi_xfer(data, reg1, val1, true); if (!ret) ret = sbtsi_xfer(data, reg2, val2, true); @@ -84,6 +85,7 @@ static int sbtsi_temp_write(struct sbtsi_data *data, u8 reg_int, u8 reg_dec, { int ret; + guard(sbtsi)(data); ret = sbtsi_xfer(data, reg_int, &val_int, false); if (!ret) ret = sbtsi_xfer(data, reg_dec, &val_dec, false); From 9bdecb9173801ce558296b3f68ddc28e030a1849 Mon Sep 17 00:00:00 2001 From: Prathima Date: Fri, 10 Jul 2026 16:46:42 +0530 Subject: [PATCH 281/513] docs: misc: amd-sbi: Document SBTSI userspace interface - Document AMD sideband IOCTL description defined for SBTSI and its usage. User space C-APIs are made available by esmi_oob_library [1], which is provided by the E-SMS project [2]. Link: https://github.com/amd/esmi_oob_library [1] Link: https://www.amd.com/en/developer/e-sms.html [2] Include a user-space open example for /dev/sbtsi-* and list auxiliary bus sysfs paths. Reviewed-by: Akshay Gupta Signed-off-by: Prathima Link: https://github.com/amd/esmi_oob_library [1] Link: https://www.amd.com/en/developer/e-sms.html [2] Link: https://patch.msgid.link/20260710111642.850022-9-Akshay.Gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- Documentation/misc-devices/amd-sbi.rst | 74 ++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/Documentation/misc-devices/amd-sbi.rst b/Documentation/misc-devices/amd-sbi.rst index f91ddadefe48..648d743903b7 100644 --- a/Documentation/misc-devices/amd-sbi.rst +++ b/Documentation/misc-devices/amd-sbi.rst @@ -48,6 +48,66 @@ Access restrictions: * APML Mailbox messages and Register xfer access are read-write, * CPUID and MCA_MSR access is read-only. +SBTSI device +============ + +sbtsi driver under the drivers/misc/amd-sbi creates miscdevice +/dev/sbtsi-* to let user space programs run APML TSI register transfer +commands. + +The driver supports both I2C and I3C transports for SB-TSI targets. +The transport is selected by the bus where the device is enumerated. + +Misc device: + * In 1P socket 0: /dev/sbtsi-4c + * In 2P socket 0: /dev/sbtsi-4c, socket 1: /dev/sbtsi-48 + +.. code-block:: bash + + $ ls -al /dev/sbtsi-4c + crw------- 1 root root 10, 116 Apr 2 05:22 /dev/sbtsi-4c + + +Access restrictions: + * Only root user is allowed to open the file. + * APML TSI Register transfer access is read-write. + +SBTSI hwmon interface +===================== + +The sbtsi_temp auxiliary driver binds to the auxiliary device published +by the core sbtsi driver on the auxiliary bus. The auxiliary device is +named amd-sbtsi.temp-sensor., where is the device's transfer +address: the client address for I2C, or the assigned-address for I3C. + +Note that the auxiliary bus formats in decimal, whereas the +/dev/sbtsi-* misc node formats its address in hex. The two therefore +differ for the same device: an I2C/I3C sensor at address 0x4c appears as the +misc node /dev/sbtsi-4c and the auxiliary device +amd-sbtsi.temp-sensor.76. + +It registers a hwmon device, providing a standard Linux hwmon interface +for reading CPU temperature and managing temperature limits. + +The hwmon device appears under ``/sys/class/hwmon/`` when both ``sbtsi.ko`` +and ``sbtsi_temp.ko`` are loaded. + +Verify auxiliary bus device:: + + ls /sys/bus/auxiliary/devices/ + # e.g. amd-sbtsi.temp-sensor.76 for an I2C/I3C sensor at address 0x4c + +Example usage:: + + # Read current temperature + cat /sys/class/hwmon/hwmon/temp1_input + + # Set high temperature limit to 70 °C + echo 70000 > /sys/class/hwmon/hwmon/temp1_max + + # Verify + cat /sys/class/hwmon/hwmon/temp1_max + Driver IOCTLs ============= @@ -63,6 +123,9 @@ Driver IOCTLs .. c:macro:: SBRMI_IOCTL_REG_XFER_CMD .. kernel-doc:: include/uapi/misc/amd-apml.h :doc: SBRMI_IOCTL_REG_XFER_CMD +.. c:macro:: SBTSI_IOCTL_REG_XFER_CMD +.. kernel-doc:: include/uapi/misc/amd-apml.h + :doc: SBTSI_IOCTL_REG_XFER_CMD User-space usage ================ @@ -85,6 +148,16 @@ Next thing, open the device file, as follows:: exit(1); } +To open SB-TSI device:: + + int file; + + file = open("/dev/sbtsi-4c", O_RDWR); + if (file < 0) { + /* ERROR HANDLING */ + exit(1); + } + The following IOCTLs are defined: ``#define SB_BASE_IOCTL_NR 0xF9`` @@ -92,6 +165,7 @@ The following IOCTLs are defined: ``#define SBRMI_IOCTL_CPUID_CMD _IOWR(SB_BASE_IOCTL_NR, 1, struct apml_cpuid_msg)`` ``#define SBRMI_IOCTL_MCAMSR_CMD _IOWR(SB_BASE_IOCTL_NR, 2, struct apml_mcamsr_msg)`` ``#define SBRMI_IOCTL_REG_XFER_CMD _IOWR(SB_BASE_IOCTL_NR, 3, struct apml_reg_xfer_msg)`` +``#define SBTSI_IOCTL_REG_XFER_CMD _IOWR(SB_BASE_IOCTL_NR, 4, struct apml_tsi_xfer_msg)`` User space C-APIs are made available by esmi_oob_library, hosted at From b072266078c9dfc3e59022c79575bd37796bb0bd Mon Sep 17 00:00:00 2001 From: Song Guo Date: Wed, 15 Jul 2026 12:21:44 +0000 Subject: [PATCH 282/513] misc: open-dice: do not assume dev->of_node is valid dev->of_node is not null only when the device is configured via device tree. When the matching device is configured by other means (like ACPI), the current code will cause null pointer dereference. Signed-off-by: Song Guo Link: https://patch.msgid.link/20260715122146.4069884-2-songguo@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/open-dice.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/misc/open-dice.c b/drivers/misc/open-dice.c index 45060fb4ea27..094b24dfc6ea 100644 --- a/drivers/misc/open-dice.c +++ b/drivers/misc/open-dice.c @@ -118,13 +118,18 @@ static int __init open_dice_probe(struct platform_device *pdev) { static unsigned int dev_idx; struct device *dev = &pdev->dev; - struct reserved_mem *rmem; + struct reserved_mem *rmem = NULL; struct open_dice_drvdata *drvdata; int ret; - rmem = of_reserved_mem_lookup(dev->of_node); - if (!rmem) { - dev_err(dev, "failed to lookup reserved memory\n"); + if (dev->of_node) { + rmem = of_reserved_mem_lookup(dev->of_node); + if (!rmem) { + dev_err(dev, "failed to lookup reserved memory\n"); + return -EINVAL; + } + } else { + dev_err(dev, "device not supported (no DT node)\n"); return -EINVAL; } From 9ddb0ea18fee2e60473e10989e743c918c0388ed Mon Sep 17 00:00:00 2001 From: Song Guo Date: Wed, 15 Jul 2026 12:21:45 +0000 Subject: [PATCH 283/513] misc: open-dice: save mem_base and mem_size in drvdata The reserved_mem only works on device tree systems. This commit replaced it by phys_addr_t and resource_size_t to make it possible to use open dice on non-DT platforms. Signed-off-by: Song Guo Link: https://patch.msgid.link/20260715122146.4069884-3-songguo@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/open-dice.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/misc/open-dice.c b/drivers/misc/open-dice.c index 094b24dfc6ea..aabece6ed3d1 100644 --- a/drivers/misc/open-dice.c +++ b/drivers/misc/open-dice.c @@ -31,7 +31,8 @@ struct open_dice_drvdata { struct mutex lock; char name[16]; - struct reserved_mem *rmem; + phys_addr_t mem_base; + resource_size_t mem_size; struct miscdevice misc; }; @@ -45,14 +46,14 @@ static int open_dice_wipe(struct open_dice_drvdata *drvdata) void *kaddr; mutex_lock(&drvdata->lock); - kaddr = devm_memremap(drvdata->misc.this_device, drvdata->rmem->base, - drvdata->rmem->size, MEMREMAP_WC); + kaddr = devm_memremap(drvdata->misc.this_device, drvdata->mem_base, + drvdata->mem_size, MEMREMAP_WC); if (IS_ERR(kaddr)) { mutex_unlock(&drvdata->lock); return PTR_ERR(kaddr); } - memset(kaddr, 0, drvdata->rmem->size); + memset(kaddr, 0, drvdata->mem_size); devm_memunmap(drvdata->misc.this_device, kaddr); mutex_unlock(&drvdata->lock); return 0; @@ -64,7 +65,7 @@ static int open_dice_wipe(struct open_dice_drvdata *drvdata) static ssize_t open_dice_read(struct file *filp, char __user *ptr, size_t len, loff_t *off) { - unsigned long val = to_open_dice_drvdata(filp)->rmem->size; + unsigned long val = to_open_dice_drvdata(filp)->mem_size; return simple_read_from_buffer(ptr, len, off, &val, sizeof(val)); } @@ -102,8 +103,8 @@ static int open_dice_mmap_prepare(struct vm_area_desc *desc) /* Create write-combine mapping so all clients observe a wipe. */ desc->page_prot = pgprot_writecombine(desc->page_prot); vma_desc_set_flags(desc, VMA_DONTCOPY_BIT, VMA_DONTDUMP_BIT); - mmap_action_simple_ioremap(desc, drvdata->rmem->base, - drvdata->rmem->size); + mmap_action_simple_ioremap(desc, drvdata->mem_base, + drvdata->mem_size); return 0; } @@ -118,27 +119,31 @@ static int __init open_dice_probe(struct platform_device *pdev) { static unsigned int dev_idx; struct device *dev = &pdev->dev; - struct reserved_mem *rmem = NULL; struct open_dice_drvdata *drvdata; + phys_addr_t mem_base; + resource_size_t mem_size; int ret; if (dev->of_node) { - rmem = of_reserved_mem_lookup(dev->of_node); + struct reserved_mem *rmem = of_reserved_mem_lookup(dev->of_node); + if (!rmem) { dev_err(dev, "failed to lookup reserved memory\n"); return -EINVAL; } + mem_base = rmem->base; + mem_size = rmem->size; } else { dev_err(dev, "device not supported (no DT node)\n"); return -EINVAL; } - if (!rmem->size || (rmem->size > ULONG_MAX)) { + if (!mem_size || (mem_size > ULONG_MAX)) { dev_err(dev, "invalid memory region size\n"); return -EINVAL; } - if (!PAGE_ALIGNED(rmem->base) || !PAGE_ALIGNED(rmem->size)) { + if (!PAGE_ALIGNED(mem_base) || !PAGE_ALIGNED(mem_size)) { dev_err(dev, "memory region must be page-aligned\n"); return -EINVAL; } @@ -148,7 +153,8 @@ static int __init open_dice_probe(struct platform_device *pdev) return -ENOMEM; *drvdata = (struct open_dice_drvdata){ - .rmem = rmem, + .mem_base = mem_base, + .mem_size = mem_size, .misc = (struct miscdevice){ .parent = dev, .name = drvdata->name, From deda667260ae495f5edc68d56e229dbfbf732243 Mon Sep 17 00:00:00 2001 From: Song Guo Date: Wed, 15 Jul 2026 12:21:46 +0000 Subject: [PATCH 284/513] misc: open-dice: add ACPI device discovery support OpenDICE can also used on x86 platforms for attestation, one of the usecase is Android's protected VM. The OpenDICE device driver only supports device tree, adding ACPI support so it can also be used on x86 environments easily. The patch is verified using crosvm, with the following ACPI table passed using --acpi-table, with --file-backed-mapping for the corresponding memory region. DefinitionBlock ( "opendice.aml", "SSDT", 2, "GOOGLE", "OpenDICE", 0x00000001 ) { Scope (\_SB) { Device (DICE) { Name (_HID, "PRP0001") Name (_DSD, Package () { ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package () { "compatible", Package () { "google,open-dice" } } } }) Name (_CRS, ResourceTemplate () { Memory32Fixed (ReadOnly, 0x9D1C3000, 0x00001000) }) } } } Signed-off-by: Song Guo Link: https://patch.msgid.link/20260715122146.4069884-4-songguo@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/open-dice.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/misc/open-dice.c b/drivers/misc/open-dice.c index aabece6ed3d1..303b35b03cb4 100644 --- a/drivers/misc/open-dice.c +++ b/drivers/misc/open-dice.c @@ -2,6 +2,7 @@ /* * Copyright (C) 2021 - Google LLC * Author: David Brazdil + * Author: Song Guo * * Driver for Open Profile for DICE. * @@ -19,6 +20,7 @@ * close(fd); */ +#include #include #include #include @@ -133,8 +135,17 @@ static int __init open_dice_probe(struct platform_device *pdev) } mem_base = rmem->base; mem_size = rmem->size; + } else if (is_acpi_node(dev->fwnode)) { + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res) { + dev_err(dev, "failed to get MMIO resource\n"); + return -EINVAL; + } + mem_base = res->start; + mem_size = resource_size(res); } else { - dev_err(dev, "device not supported (no DT node)\n"); + dev_err(dev, "device not supported (no DT or ACPI node)\n"); return -EINVAL; } @@ -218,3 +229,4 @@ module_exit(open_dice_exit); MODULE_DESCRIPTION("Driver for Open Profile for DICE."); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("David Brazdil "); +MODULE_AUTHOR("Song Guo "); From d7f0b6a15dde2e87a617095fa273f7d9c6e2d87b Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Wed, 8 Jul 2026 11:57:33 +0200 Subject: [PATCH 285/513] misc: rp1: Switch to irq_domain_create_linear() irq_domain_add_linear() is going away as being obsolete now. Switch to the preferred irq_domain_create_linear(). That differs in the first parameter: It takes more generic struct fwnode_handle instead of struct device_node. Therefore, of_fwnode_handle() is added around the parameter. Note some of the users can likely use dev->fwnode directly instead of indirect of_fwnode_handle(dev->of_node). But dev->fwnode is not guaranteed to be set for all, so this has to be investigated on case to case basis (by people who can actually test with the HW). Signed-off-by: Jiri Slaby (SUSE) Cc: Thomas Gleixner Cc: Andrea della Porta Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Tested-by: Andrea della Porta Link: https://patch.msgid.link/20260708095733.385396-1-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/rp1/rp1_pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/rp1/rp1_pci.c b/drivers/misc/rp1/rp1_pci.c index 0e87633fe4f8..94ad022cd138 100644 --- a/drivers/misc/rp1/rp1_pci.c +++ b/drivers/misc/rp1/rp1_pci.c @@ -241,8 +241,8 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) } pci_set_drvdata(pdev, rp1); - rp1->domain = irq_domain_add_linear(rp1_node, RP1_INT_END, - &rp1_domain_ops, rp1); + rp1->domain = irq_domain_create_linear(of_fwnode_handle(rp1_node), RP1_INT_END, + &rp1_domain_ops, rp1); if (!rp1->domain) { dev_err(&pdev->dev, "Error creating IRQ domain\n"); err = -ENOMEM; From 274259391c14166fcabae74f9fc0104223ff27a1 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Thu, 11 Jun 2026 04:55:13 -0700 Subject: [PATCH 286/513] cacheinfo: don't propagate DT/ACPI error when arch supplies info (arm64) cache_setup_properties() sets use_arch_info = true when DT/ACPI provide no cache nodes and the arch can derive the topology from CPU registers (e.g. arm64 reading CLIDR_EL1), but still returns the original -ENOENT. cache_shared_cpu_map_setup() bails on that error before the new flag can take effect, so the first CPU brought online always trips a misleading warning: cacheinfo: Unable to detect cache hierarchy for CPU 0 Subsequent CPUs skip cache_setup_properties() entirely because use_arch_info is now true, which is why only CPU0 hits it. This is reproducible on arm64 with the QEMU 'virt' machine, whose default DT has no cache nodes. Clear ret after setting use_arch_info so the caller proceeds and populates the shared cpu map via the arch-supplied leaves. Fixes: ef9f643a9f8b ("cacheinfo: Add use_arch[|_cache]_info field/function") Reviewed-by: Pierre Gondois Signed-off-by: Breno Leitao Reviewed-by: Sudeep Holla Link: https://patch.msgid.link/20260611-cacheinfo-v2-1-6069ef066cf3@debian.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/cacheinfo.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index 70701d3bc81c..9f9c72727a05 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -401,9 +401,14 @@ static int cache_setup_properties(unsigned int cpu) else if (!acpi_disabled) ret = cache_setup_acpi(cpu); - // Assume there is no cache information available in DT/ACPI from now. - if (ret && use_arch_cache_info()) + /* + * No DT/ACPI cache nodes; fall back to arch-derived topology (e.g. + * arm64 CLIDR_EL1) and clear the error to avoid a spurious warning. + */ + if (ret && use_arch_cache_info()) { use_arch_info = true; + ret = 0; + } return ret; } From 3c0cf801ea2fa40daa5e7d1e6d32adca5ff75ad9 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Thu, 16 Jul 2026 09:39:23 +0800 Subject: [PATCH 287/513] ppdev: prevent overflow when setting port timeout PPSETTIME64 supplies the timeval fields as s64 values, but pp_set_timeout() narrows tv_usec to int and calculates tv_sec * HZ in a signed long. Large positive values can therefore be truncated or overflow and install an unintended timeout. Keep both fields as s64, reject a non-canonical microsecond value, and use timespec64_to_jiffies() to cap excessively large timeouts at MAX_JIFFY_OFFSET. This is a behavior change because both PPSETTIME ioctls could previously accept values with tv_usec >= USEC_PER_SEC. The validation follows the precedent set by sock_set_timeout(). Fixes: 3b9ab374a1e6 ("ppdev: convert to y2038 safe") Signed-off-by: Linmao Li Reviewed-by: Arnd Bergmann Link: https://patch.msgid.link/20260716013923.19494-1-lilinmao@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- drivers/char/ppdev.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 6da817b9849f..8803268b4cdc 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -340,15 +340,17 @@ static enum ieee1284_phase init_phase(int mode) return IEEE1284_PH_FWD_IDLE; } -static int pp_set_timeout(struct pardevice *pdev, long tv_sec, int tv_usec) +static int pp_set_timeout(struct pardevice *pdev, s64 tv_sec, s64 tv_usec) { + struct timespec64 ts; long to_jiffies; - if ((tv_sec < 0) || (tv_usec < 0)) + if (tv_sec < 0 || tv_usec < 0 || tv_usec >= USEC_PER_SEC) return -EINVAL; - to_jiffies = usecs_to_jiffies(tv_usec); - to_jiffies += tv_sec * HZ; + ts.tv_sec = tv_sec; + ts.tv_nsec = tv_usec * NSEC_PER_USEC; + to_jiffies = timespec64_to_jiffies(&ts); if (to_jiffies <= 0) return -EINVAL; From de1dea6fad7891ff3e7adb57cb0446e657864389 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Fri, 17 Jul 2026 11:45:19 +0200 Subject: [PATCH 288/513] char: powernv-op-panel: remove unnecessary reset of position pointer The position pointer is only advanced if the return value of the write operation is positive at ksys_write(). Therefore no need to manually reset it when doing error handling. Assisted-by: coccinelle # to find it Signed-off-by: Fernando Fernandez Mancera Link: https://patch.msgid.link/20260717094519.20656-1-fmancera@suse.de Signed-off-by: Greg Kroah-Hartman --- drivers/char/powernv-op-panel.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/char/powernv-op-panel.c b/drivers/char/powernv-op-panel.c index 63175b765c90..a98acb199805 100644 --- a/drivers/char/powernv-op-panel.c +++ b/drivers/char/powernv-op-panel.c @@ -89,7 +89,6 @@ static int __op_panel_update_display(void) static ssize_t oppanel_write(struct file *filp, const char __user *userbuf, size_t len, loff_t *f_pos) { - loff_t f_pos_prev = *f_pos; ssize_t ret; int rc; @@ -105,7 +104,6 @@ static ssize_t oppanel_write(struct file *filp, const char __user *userbuf, if (rc != OPAL_SUCCESS) { pr_err_ratelimited("OPAL call failed to write to op panel display [rc=%d]\n", rc); - *f_pos = f_pos_prev; return -EIO; } } From b6b5d64cb161a28347d64dc3168a636c4abb68d5 Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Wed, 1 Jul 2026 10:01:09 +0800 Subject: [PATCH 289/513] ipack: ipoctal: fix UAF, null-ptr-deref, and use-after-free in cleanup on remove MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three issues arise when the device is removed while a tty session is still active: 1. UAF of struct ipoctal: the remove callback frees ipoctal via kfree() while tty ops may still access it. Fix by introducing kref-based lifetime management — kref is taken in install() when a tty is opened and released in cleanup() when the tty is finally destroyed; remove() uses kref_put() instead of kfree(). 2. NULL dereference in ipoctal_write_tty(): __ipoctal_remove() frees xmit_buf via tty_port_free_xmit_buf() while a userspace process may still hold the tty fd and call write(). Fix by checking for NULL xmit_buf in ipoctal_write_tty(). 3. UAF in ipoctal_cleanup(): ipack_put_carrier(ipoctal->dev) dereferences ipoctal->dev after the ipack_device has been freed by ipack_device_del(). Fix by caching ipoctal->carrier_owner during probe() and calling module_put() on the cached pointer directly in cleanup(), avoiding any access to ipoctal->dev. Also introduce a "removed" flag in struct ipoctal, set at the start of __ipoctal_remove(), and checked in every tty op that accesses hardware resources (port_activate, write_tty, set_termios, hangup, shutdown). This prevents page faults when devm_ioremap() regions are unmapped after remove() returns. Reported-by: Shuangpeng Bai Closes: https://lore.kernel.org/lkml/178144969601.60470.1257088106279546587@gmail.com/ Fixes: 05e5027efc9c ("Staging: ipack: move out of staging") Signed-off-by: Pei Xiao Link: https://patch.msgid.link/e3b0a90b07f079c5bcd5ca90d1dd3b79bb29adb5.1782870760.git.xiaopei01@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- drivers/ipack/devices/ipoctal.c | 56 ++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c index 1bbefc6de708..bf71b8952a7c 100644 --- a/drivers/ipack/devices/ipoctal.c +++ b/drivers/ipack/devices/ipoctal.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,8 @@ static const struct tty_operations ipoctal_fops; +static void ipoctal_release(struct kref *kref); + struct ipoctal_channel { struct ipoctal_stats stats; unsigned int nb_bytes; @@ -49,6 +52,9 @@ struct ipoctal { struct tty_driver *tty_drv; u8 __iomem *mem8_space; u8 __iomem *int_space; + struct kref kref; + struct module *carrier_owner; + bool removed; }; static inline struct ipoctal *chan_to_ipoctal(struct ipoctal_channel *chan, @@ -70,8 +76,14 @@ static void ipoctal_reset_channel(struct ipoctal_channel *channel) static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty) { struct ipoctal_channel *channel; + struct ipoctal *ipoctal; channel = dev_get_drvdata(tty->dev); + ipoctal = chan_to_ipoctal(channel, tty->index); + + + if (ipoctal->removed) + return -ENODEV; /* * Enable RX. TX will be enabled when @@ -95,6 +107,7 @@ static int ipoctal_install(struct tty_driver *driver, struct tty_struct *tty) if (res) goto err_put_carrier; + kref_get(&ipoctal->kref); tty->driver_data = channel; return 0; @@ -460,8 +473,13 @@ static ssize_t ipoctal_write_tty(struct tty_struct *tty, const u8 *buf, size_t count) { struct ipoctal_channel *channel = tty->driver_data; + struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index); size_t char_copied; + + if (ipoctal->removed || !channel->tty_port.xmit_buf) + return 0; + char_copied = ipoctal_copy_write_buffer(channel, buf, count); /* As the IP-OCTAL 485 only supports half duplex, do it manually */ @@ -501,8 +519,13 @@ static void ipoctal_set_termios(struct tty_struct *tty, unsigned char mr2 = 0; unsigned char csr = 0; struct ipoctal_channel *channel = tty->driver_data; + struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index); speed_t baud; + + if (ipoctal->removed) + return; + cflag = tty->termios.c_cflag; /* Disable and reset everything before change the setup */ @@ -631,10 +654,16 @@ static void ipoctal_hangup(struct tty_struct *tty) { unsigned long flags; struct ipoctal_channel *channel = tty->driver_data; + struct ipoctal *ipoctal; if (channel == NULL) return; + ipoctal = chan_to_ipoctal(channel, tty->index); + + if (ipoctal->removed) + return; + spin_lock_irqsave(&channel->lock, flags); channel->nb_bytes = 0; channel->pointer_read = 0; @@ -651,10 +680,16 @@ static void ipoctal_hangup(struct tty_struct *tty) static void ipoctal_shutdown(struct tty_struct *tty) { struct ipoctal_channel *channel = tty->driver_data; + struct ipoctal *ipoctal; if (channel == NULL) return; + ipoctal = chan_to_ipoctal(channel, tty->index); + + if (ipoctal->removed) + return; + ipoctal_reset_channel(channel); tty_port_set_initialized(&channel->tty_port, false); } @@ -664,8 +699,9 @@ static void ipoctal_cleanup(struct tty_struct *tty) struct ipoctal_channel *channel = tty->driver_data; struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index); - /* release the carrier driver */ - ipack_put_carrier(ipoctal->dev); + /* release the carrier driver via cached owner */ + module_put(ipoctal->carrier_owner); + kref_put(&ipoctal->kref, ipoctal_release); } static const struct tty_operations ipoctal_fops = { @@ -683,6 +719,13 @@ static const struct tty_operations ipoctal_fops = { .cleanup = ipoctal_cleanup, }; +static void ipoctal_release(struct kref *kref) +{ + struct ipoctal *ipoctal = container_of(kref, struct ipoctal, kref); + + kfree(ipoctal); +} + static int ipoctal_probe(struct ipack_device *dev) { int res; @@ -692,7 +735,10 @@ static int ipoctal_probe(struct ipack_device *dev) if (ipoctal == NULL) return -ENOMEM; + kref_init(&ipoctal->kref); + ipoctal->dev = dev; + ipoctal->carrier_owner = dev->bus->owner; res = ipoctal_inst_slot(ipoctal, dev->bus->bus_nr, dev->slot); if (res) goto out_uninst; @@ -701,7 +747,7 @@ static int ipoctal_probe(struct ipack_device *dev) return 0; out_uninst: - kfree(ipoctal); + kref_put(&ipoctal->kref, ipoctal_release); return res; } @@ -709,6 +755,8 @@ static void __ipoctal_remove(struct ipoctal *ipoctal) { int i; + ipoctal->removed = true; + ipoctal->dev->bus->ops->free_irq(ipoctal->dev); for (i = 0; i < NR_CHANNELS; i++) { @@ -725,7 +773,7 @@ static void __ipoctal_remove(struct ipoctal *ipoctal) tty_unregister_driver(ipoctal->tty_drv); kfree(ipoctal->tty_drv->name); tty_driver_kref_put(ipoctal->tty_drv); - kfree(ipoctal); + kref_put(&ipoctal->kref, ipoctal_release); } static void ipoctal_remove(struct ipack_device *idev) From 7d3a708af7f4e2af9e114d731bd60d30c1ec884e Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Wed, 1 Jul 2026 10:01:10 +0800 Subject: [PATCH 290/513] ipack: ipoctal: add rwsem to guard against TOCTOU in remove path The "removed" flag check in each tty op has a TOCTOU race with __ipoctal_remove(): the device could be removed between the flag check and the subsequent access to hardware resources (channel registers via iowrite8, or xmit_buf in write_tty). Close this race by introducing a read-write semaphore (remove_sem). The tty ops acquire the read lock via guard(rwsem_read) for the full duration of the operation, while __ipoctal_remove() acquires the write lock via scoped_guard(rwsem_write) when setting the removed flag. This ensures that once removed is true, no in-flight tty op can still be accessing resources that are about to be freed by the remove path. Signed-off-by: Pei Xiao Link: https://patch.msgid.link/fbce75010a0f0a3a3709a5e06fd0ffd19ca0a0ed.1782870760.git.xiaopei01@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- drivers/ipack/devices/ipoctal.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c index bf71b8952a7c..2169e4b75f98 100644 --- a/drivers/ipack/devices/ipoctal.c +++ b/drivers/ipack/devices/ipoctal.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,7 @@ struct ipoctal { u8 __iomem *int_space; struct kref kref; struct module *carrier_owner; + struct rw_semaphore remove_sem; bool removed; }; @@ -81,7 +83,7 @@ static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty) channel = dev_get_drvdata(tty->dev); ipoctal = chan_to_ipoctal(channel, tty->index); - + guard(rwsem_read)(&ipoctal->remove_sem); if (ipoctal->removed) return -ENODEV; @@ -476,7 +478,7 @@ static ssize_t ipoctal_write_tty(struct tty_struct *tty, const u8 *buf, struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index); size_t char_copied; - + guard(rwsem_read)(&ipoctal->remove_sem); if (ipoctal->removed || !channel->tty_port.xmit_buf) return 0; @@ -522,7 +524,7 @@ static void ipoctal_set_termios(struct tty_struct *tty, struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index); speed_t baud; - + guard(rwsem_read)(&ipoctal->remove_sem); if (ipoctal->removed) return; @@ -660,7 +662,7 @@ static void ipoctal_hangup(struct tty_struct *tty) return; ipoctal = chan_to_ipoctal(channel, tty->index); - + guard(rwsem_read)(&ipoctal->remove_sem); if (ipoctal->removed) return; @@ -686,7 +688,7 @@ static void ipoctal_shutdown(struct tty_struct *tty) return; ipoctal = chan_to_ipoctal(channel, tty->index); - + guard(rwsem_read)(&ipoctal->remove_sem); if (ipoctal->removed) return; @@ -736,6 +738,7 @@ static int ipoctal_probe(struct ipack_device *dev) return -ENOMEM; kref_init(&ipoctal->kref); + init_rwsem(&ipoctal->remove_sem); ipoctal->dev = dev; ipoctal->carrier_owner = dev->bus->owner; @@ -755,7 +758,8 @@ static void __ipoctal_remove(struct ipoctal *ipoctal) { int i; - ipoctal->removed = true; + scoped_guard(rwsem_write, &ipoctal->remove_sem) + ipoctal->removed = true; ipoctal->dev->bus->ops->free_irq(ipoctal->dev); From 18b6510f642dbcb856c3af8be974d1600f3ba2d3 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 6 Jul 2026 17:19:35 +0800 Subject: [PATCH 291/513] greybus: manifest: validate string descriptor header identify_descriptor() computes a string descriptor size from desc->string.length. Require the descriptor to contain the fixed string descriptor header before reading the variable string length. The existing descriptor-size check then reports the short descriptor. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260706091935.78020-1-pengpeng@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/greybus/manifest.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/greybus/manifest.c b/drivers/greybus/manifest.c index 9be5d95da587..3b685ace2036 100644 --- a/drivers/greybus/manifest.c +++ b/drivers/greybus/manifest.c @@ -122,6 +122,8 @@ static int identify_descriptor(struct gb_interface *intf, switch (desc_header->type) { case GREYBUS_TYPE_STRING: expected_size += sizeof(struct greybus_descriptor_string); + if (desc_size < expected_size) + break; expected_size += desc->string.length; /* String descriptors are padded to 4 byte boundaries */ From 535332e9fb99673538e6c3992659c866bdad4b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 6 Jul 2026 18:09:32 +0200 Subject: [PATCH 292/513] greybus: Drop #include of MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The header itself also includes and additional to that doesn't make use of any symbol defined (transitively) by . Also the .c files that include that header don't need it (there is no direct include, only via ): $ git grep -l greybus\\.h | xargs grep -E "\<(acpi_device_id|amba_id|ap_device_id|apr_device_id|auxiliary_device_id|bcma_device_id|ccw_device_id|cdx_device_id|coreboot_device_id|css_device_id|dfl_device_id|dmi_(device|system)_id|eisa_device_id|fsl_mc_device_id|hda_device_id|hid_device_id|hv_vmbus_device_id|i2c_device_id|i3c_device_id|ieee1394_device_id|input_device_id|ipack_device_id|isapnp_device_id|ishtp_device_id|mcb_device_id|mdio_device_id|mei_cl_device_id|mhi_device_id|mips_cdmm_device_id|of_device_id|parisc_device_id|pci_device_id|pci_epf_device_id|pcmcia_device_id|platform_device_id|pnp_(card_)?device_id|rio_device_id|rpmsg_device_id|sdio_device_id|sdw_device_id|serio_device_id|slim_device_id|spi_device_id|spmi_device_id|ssam_device_id|ssb_device_id|tb_service_id|tee_client_device_id|typec_device_id|ulpi_device_id|usb_device_id|vchiq_device_id|virtio_device_id|wmi_device_id|x86_(cpu|device)_id|zorro_device_id|cpu_feature)\>" drivers/greybus/es2.c:static const struct usb_device_id id_table[] = { drivers/greybus/es2.c: const struct usb_device_id *id) drivers/greybus/gb-beagleplay.c:static const struct of_device_id gb_beagleplay_of_match[] = { drivers/staging/greybus/arche-platform.c:static const struct of_device_id arche_platform_of_match[] = { drivers/greybus/es2.c includes , drivers/greybus/gb-beagleplay.c includes which provides of_device_id via , similar drivers/staging/greybus/arche-platform.c includes which also provides of_device_id. So the #include can go away without further adaption. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/80964227feede2d8f1978f13f4219fcf9e63d8d0.1783354012.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- include/linux/greybus/greybus_id.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/linux/greybus/greybus_id.h b/include/linux/greybus/greybus_id.h index f4c8440093e4..72f330a35569 100644 --- a/include/linux/greybus/greybus_id.h +++ b/include/linux/greybus/greybus_id.h @@ -1,14 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* FIXME - * move this to include/linux/mod_devicetable.h when merging + * move this to include/linux/device-id/greybus.h when merging */ #ifndef __LINUX_GREYBUS_ID_H #define __LINUX_GREYBUS_ID_H #include -#include - struct greybus_bundle_id { __u16 match_flags; From 919d1ba86be65fc08a83c5def5cc08a61a5ace62 Mon Sep 17 00:00:00 2001 From: Griffin Kroah-Hartman Date: Thu, 9 Jul 2026 15:16:40 +0200 Subject: [PATCH 293/513] mailbox: mchp-ipc-sbi: Add null check for devm_kasprintf() Add a check to see if devm_kasprintf() is not NULL in mchp_ipc_get_cluster_aggr_irq(), returning -ENOMEM if the function failed. Assisted-by: gkh_clanker_t1000 CC: Jassi Brar Signed-off-by: Griffin Kroah-Hartman Link: https://patch.msgid.link/20260709131640.210914-1-griffin@kroah.com Signed-off-by: Greg Kroah-Hartman --- drivers/mailbox/mailbox-mchp-ipc-sbi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mailbox/mailbox-mchp-ipc-sbi.c b/drivers/mailbox/mailbox-mchp-ipc-sbi.c index b87bf2fb4b9b..f081f8a9bcf8 100644 --- a/drivers/mailbox/mailbox-mchp-ipc-sbi.c +++ b/drivers/mailbox/mailbox-mchp-ipc-sbi.c @@ -378,6 +378,8 @@ static int mchp_ipc_get_cluster_aggr_irq(struct mchp_ipc_sbi_mbox *ipc) for_each_online_cpu(cpuid) { hartid = cpuid_to_hartid_map(cpuid); irq_name = devm_kasprintf(ipc->dev, GFP_KERNEL, "hart-%lu", hartid); + if (!irq_name) + return -ENOMEM; ret = platform_get_irq_byname_optional(pdev, irq_name); if (ret <= 0) continue; From f5af7132db239c5d13f20b7ac01db62b62830fb1 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 23 Jun 2026 10:21:41 +0100 Subject: [PATCH 294/513] virtio_console: fix endian conversion in handle_control_message() There are a couple of prints in handle_control_message() which should have converted cpkt->id through virtio32_to_cpu() before passing to a print. This fixes the following (prototype) sparse warnings: drivers/char/virtio_console.c:1538:17: warning: incorrect type in argument 4 (different base types) drivers/char/virtio_console.c:1538:17: expected unsigned int drivers/char/virtio_console.c:1538:17: got restricted __virtio32 [usertype] id drivers/char/virtio_console.c:1553:25: warning: incorrect type in argument 3 (different base types) drivers/char/virtio_console.c:1553:25: expected unsigned int drivers/char/virtio_console.c:1553:25: got restricted __virtio32 [usertype] id Signed-off-by: Ben Dooks Acked-by: Arnd Bergmann Reviewed-by: Amit Shah Link: https://patch.msgid.link/20260623092141.631355-1-ben.dooks@codethink.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/char/virtio_console.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 198b97314168..cbdc497f5160 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1536,7 +1536,8 @@ static void handle_control_message(struct virtio_device *vdev, cpkt->event != cpu_to_virtio16(vdev, VIRTIO_CONSOLE_PORT_ADD)) { /* No valid header at start of buffer. Drop it. */ dev_dbg(&portdev->vdev->dev, - "Invalid index %u in control packet\n", cpkt->id); + "Invalid index %u in control packet\n", + virtio32_to_cpu(vdev, cpkt->id)); return; } @@ -1553,7 +1554,8 @@ static void handle_control_message(struct virtio_device *vdev, dev_warn(&portdev->vdev->dev, "Request for adding port with " "out-of-bound id %u, max. supported id: %u\n", - cpkt->id, portdev->max_nr_ports - 1); + virtio32_to_cpu(vdev, cpkt->id), + portdev->max_nr_ports - 1); break; } add_port(portdev, virtio32_to_cpu(vdev, cpkt->id)); From e7e12b4cc0f0c3a2782aea084d4215e23f5512b3 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Tue, 23 Jun 2026 17:55:55 +0900 Subject: [PATCH 295/513] char: xilinx_hwicap: unregister class on init errors hwicap_module_init() registers icap_class before reserving the character-device region and registering the platform driver. If either of those later steps fails, the init path must undo the successful class registration before returning an error. Route the chrdev registration failure through a class unwind label, and let the platform-driver registration failure fall through the existing chrdev unwind before unregistering the class. The normal module exit path is unchanged. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: ef141a0bb0dc ("[POWERPC] Xilinx: hwicap driver") Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: Radhey Shyam Pandey Link: https://patch.msgid.link/20260623085604.89284-1-mhun512@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/xilinx_hwicap/xilinx_hwicap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index 34a345dc5e72..9bb5fa642fd8 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -760,7 +760,7 @@ static int __init hwicap_module_init(void) HWICAP_DEVICES, DRIVER_NAME); if (retval < 0) - return retval; + goto failed_class; retval = platform_driver_register(&hwicap_platform_driver); if (retval) @@ -771,6 +771,9 @@ static int __init hwicap_module_init(void) failed: unregister_chrdev_region(devt, HWICAP_DEVICES); + failed_class: + class_unregister(&icap_class); + return retval; } From e798d274c9fc70c9d2335fce9b7aae7722b42782 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 10 Jul 2026 18:53:06 +0800 Subject: [PATCH 296/513] hwrng: drivers - Remove redundant dev_err()/dev_err_probe() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() and dev_err_probe() calls. Signed-off-by: Pan Chuang Link: https://patch.msgid.link/20260710105318.376496-2-panchuang@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/hw_random/airoha-trng.c | 4 +--- drivers/char/hw_random/cctrng.c | 2 +- drivers/char/hw_random/imx-rngc.c | 2 +- drivers/char/hw_random/jh7110-trng.c | 3 +-- drivers/char/hw_random/omap-rng.c | 5 +---- drivers/char/hw_random/xgene-rng.c | 2 +- 6 files changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/char/hw_random/airoha-trng.c b/drivers/char/hw_random/airoha-trng.c index 076519a2f100..98c131ee9891 100644 --- a/drivers/char/hw_random/airoha-trng.c +++ b/drivers/char/hw_random/airoha-trng.c @@ -186,10 +186,8 @@ static int airoha_trng_probe(struct platform_device *pdev) airoha_trng_irq_mask(trng); ret = devm_request_irq(&pdev->dev, irq, airoha_trng_irq, 0, pdev->name, (void *)trng); - if (ret) { - dev_err(dev, "Can't get interrupt working.\n"); + if (ret) return ret; - } init_completion(&trng->rng_op_done); diff --git a/drivers/char/hw_random/cctrng.c b/drivers/char/hw_random/cctrng.c index a5be9258037f..a6925211c3b5 100644 --- a/drivers/char/hw_random/cctrng.c +++ b/drivers/char/hw_random/cctrng.c @@ -509,7 +509,7 @@ static int cctrng_probe(struct platform_device *pdev) /* register the driver isr function */ rc = devm_request_irq(dev, irq, cc_isr, IRQF_SHARED, "cctrng", drvdata); if (rc) - return dev_err_probe(dev, rc, "Could not register to interrupt %d\n", irq); + return rc; dev_dbg(dev, "Registered to IRQ: %d\n", irq); /* Clear all pending interrupts */ diff --git a/drivers/char/hw_random/imx-rngc.c b/drivers/char/hw_random/imx-rngc.c index 28c56c2d1bf6..bae8cdca13fe 100644 --- a/drivers/char/hw_random/imx-rngc.c +++ b/drivers/char/hw_random/imx-rngc.c @@ -296,7 +296,7 @@ static int __init imx_rngc_probe(struct platform_device *pdev) irq, imx_rngc_irq, 0, pdev->name, (void *)rngc); if (ret) { clk_disable_unprepare(rngc->clk); - return dev_err_probe(&pdev->dev, ret, "Can't get interrupt working.\n"); + return ret; } if (self_test) { diff --git a/drivers/char/hw_random/jh7110-trng.c b/drivers/char/hw_random/jh7110-trng.c index 4712c3c530e4..aee12caab578 100644 --- a/drivers/char/hw_random/jh7110-trng.c +++ b/drivers/char/hw_random/jh7110-trng.c @@ -303,8 +303,7 @@ static int starfive_trng_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, starfive_trng_irq, 0, pdev->name, (void *)trng); if (ret) - return dev_err_probe(&pdev->dev, ret, - "Failed to register interrupt handler\n"); + return ret; trng->hclk = devm_clk_get(&pdev->dev, "hclk"); if (IS_ERR(trng->hclk)) diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index 5e8b50f15db7..327643ba971c 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -391,11 +391,8 @@ static int of_get_omap_rng_device_details(struct omap_rng_dev *priv, err = devm_request_irq(dev, irq, omap4_rng_irq, IRQF_TRIGGER_NONE, dev_name(dev), priv); - if (err) { - dev_err(dev, "unable to request irq %d, err = %d\n", - irq, err); + if (err) return err; - } /* * On OMAP4, enabling the shutdown_oflo interrupt is diff --git a/drivers/char/hw_random/xgene-rng.c b/drivers/char/hw_random/xgene-rng.c index 1f4b95341c2e..629dc85c3741 100644 --- a/drivers/char/hw_random/xgene-rng.c +++ b/drivers/char/hw_random/xgene-rng.c @@ -336,7 +336,7 @@ static int xgene_rng_probe(struct platform_device *pdev) rc = devm_request_irq(&pdev->dev, ctx->irq, xgene_rng_irq_handler, 0, dev_name(&pdev->dev), ctx); if (rc) - return dev_err_probe(&pdev->dev, rc, "Could not request RNG alarm IRQ\n"); + return rc; /* Enable IP clock */ clk = devm_clk_get_optional_enabled(&pdev->dev, NULL); From 0446c8456caad083d0aa29511757355d84051805 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 10 Jul 2026 18:53:07 +0800 Subject: [PATCH 297/513] tpm: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Reviewed-by: Jarkko Sakkinen Link: https://patch.msgid.link/20260710105318.376496-3-panchuang@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/st33zp24/st33zp24.c | 5 +---- drivers/char/tpm/tpm_i2c_nuvoton.c | 2 -- drivers/char/tpm/tpm_tis_i2c_cr50.c | 4 +--- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c index e2b7451ea7cc..8d5179367eac 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.c +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -506,11 +506,8 @@ int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops, ret = devm_request_irq(dev, irq, tpm_ioserirq_handler, IRQF_TRIGGER_HIGH, "TPM SERIRQ management", chip); - if (ret < 0) { - dev_err(&chip->dev, "TPM SERIRQ signals %d not available\n", - irq); + if (ret < 0) goto _tpm_clean_answer; - } intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_STS_VALID_INT diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index d44903b29929..129aa222cc25 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -574,8 +574,6 @@ static int i2c_nuvoton_probe(struct i2c_client *client) dev_name(&chip->dev), chip); if (rc) { - dev_err(dev, "%s() Unable to request irq: %d for use\n", - __func__, priv->irq); priv->irq = 0; } else { chip->flags |= TPM_CHIP_FLAG_IRQ; diff --git a/drivers/char/tpm/tpm_tis_i2c_cr50.c b/drivers/char/tpm/tpm_tis_i2c_cr50.c index b48cacacc066..7f828fae70d3 100644 --- a/drivers/char/tpm/tpm_tis_i2c_cr50.c +++ b/drivers/char/tpm/tpm_tis_i2c_cr50.c @@ -751,10 +751,8 @@ static int tpm_cr50_i2c_probe(struct i2c_client *client) rc = devm_request_irq(dev, client->irq, tpm_cr50_i2c_int_handler, IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN, dev->driver->name, chip); - if (rc < 0) { - dev_err(dev, "Failed to probe IRQ %d\n", client->irq); + if (rc < 0) return rc; - } priv->irq = client->irq; } else { From 2cedf2272f1bb42471e646868ac572cc5752bd91 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 10 Jul 2026 18:53:08 +0800 Subject: [PATCH 298/513] char: xillybus: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Acked-by: Eli Billauer Link: https://patch.msgid.link/20260710105318.376496-4-panchuang@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/xillybus/xillybus_of.c | 5 +---- drivers/char/xillybus/xillybus_pcie.c | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/char/xillybus/xillybus_of.c b/drivers/char/xillybus/xillybus_of.c index 1a1e64133315..46e1046abfca 100644 --- a/drivers/char/xillybus/xillybus_of.c +++ b/drivers/char/xillybus/xillybus_of.c @@ -55,11 +55,8 @@ static int xilly_drv_probe(struct platform_device *op) rc = devm_request_irq(dev, irq, xillybus_isr, 0, xillyname, endpoint); - if (rc) { - dev_err(endpoint->dev, - "Failed to register IRQ handler. Aborting.\n"); + if (rc) return -ENODEV; - } return xillybus_endpoint_discovery(endpoint); } diff --git a/drivers/char/xillybus/xillybus_pcie.c b/drivers/char/xillybus/xillybus_pcie.c index 9858711e3e79..32064b6c7627 100644 --- a/drivers/char/xillybus/xillybus_pcie.c +++ b/drivers/char/xillybus/xillybus_pcie.c @@ -83,11 +83,8 @@ static int xilly_probe(struct pci_dev *pdev, } rc = devm_request_irq(&pdev->dev, pdev->irq, xillybus_isr, 0, xillyname, endpoint); - if (rc) { - dev_err(endpoint->dev, - "Failed to register MSI handler. Aborting.\n"); + if (rc) return -ENODEV; - } /* * Some (old and buggy?) hardware drops 64-bit addressed PCIe packets, From 298b0f9c0e8eafd3e14002d83b9481f101973b95 Mon Sep 17 00:00:00 2001 From: Yahya Toubali Date: Sat, 18 Jul 2026 17:56:14 +0100 Subject: [PATCH 299/513] dt-bindings: iio: dac: fix spelling of 'reference' in adi,ad5791 Fix misspelling of 'reference' (was 'referance') in the vrefp-supply and vrefn-supply descriptions. Signed-off-by: Yahya Toubali Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml index 2bd89e0aa46b..80fb98035c6f 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml @@ -36,11 +36,11 @@ properties: vrefp-supply: description: - Positive referance input voltage range. From 5v to (vdd - 2.5) + Positive reference input voltage range. From 5v to (vdd - 2.5) vrefn-supply: description: - Negative referance input voltage range. From (vss + 2.5) to 0. + Negative reference input voltage range. From (vss + 2.5) to 0. adi,rbuf-gain2-en: description: Specify to allow an external amplifier to be connected in a From a1c5ca7b8034efe2870a0e31aa1200530ead513f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Kr=C3=A1tk=C3=BD?= Date: Thu, 16 Jul 2026 19:03:06 +0200 Subject: [PATCH 300/513] iio: resolver: ad2s1200: use dev_err_probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use dev_err_probe() instead of dev_err() to simplify the error path and cleanly handle deferred probing. Signed-off-by: Vojtěch Krátký Signed-off-by: Jonathan Cameron --- drivers/iio/resolver/ad2s1200.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/iio/resolver/ad2s1200.c b/drivers/iio/resolver/ad2s1200.c index ec7afad8d45c..27ecc434eee9 100644 --- a/drivers/iio/resolver/ad2s1200.c +++ b/drivers/iio/resolver/ad2s1200.c @@ -143,18 +143,14 @@ static int ad2s1200_probe(struct spi_device *spi) st->sdev = spi; st->sample = devm_gpiod_get(&spi->dev, "adi,sample", GPIOD_OUT_LOW); - if (IS_ERR(st->sample)) { - dev_err(&spi->dev, "Failed to claim SAMPLE gpio: err=%ld\n", - PTR_ERR(st->sample)); - return PTR_ERR(st->sample); - } + if (IS_ERR(st->sample)) + return dev_err_probe(&spi->dev, PTR_ERR(st->sample), + "Failed to claim SAMPLE gpio\n"); st->rdvel = devm_gpiod_get(&spi->dev, "adi,rdvel", GPIOD_OUT_LOW); - if (IS_ERR(st->rdvel)) { - dev_err(&spi->dev, "Failed to claim RDVEL gpio: err=%ld\n", - PTR_ERR(st->rdvel)); - return PTR_ERR(st->rdvel); - } + if (IS_ERR(st->rdvel)) + return dev_err_probe(&spi->dev, PTR_ERR(st->rdvel), + "Failed to claim RDVEL gpio\n"); indio_dev->info = &ad2s1200_info; indio_dev->modes = INDIO_DIRECT_MODE; From 572a008526359cdac2fa935dad75e9d50a79792f Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 16 Jul 2026 13:14:18 +0100 Subject: [PATCH 301/513] iio: dac: ad5686: missing NULL check on match data Verify that chip_info pointer is not NULL. If a user binds the driver using driver_override via sysfs with a device name not present in the id_table or of_match_table, match data will be NULL. Fixes: 0eb1728461a1 ("iio: dac: ad5686: drop enum id") Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/all/20260710113149.53EC51F000E9@smtp.kernel.org/ Signed-off-by: Rodrigo Alencar Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686-spi.c | 9 +++++++-- drivers/iio/dac/ad5696-i2c.c | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c index 8abfaf8f0c46..859874ab861c 100644 --- a/drivers/iio/dac/ad5686-spi.c +++ b/drivers/iio/dac/ad5686-spi.c @@ -98,8 +98,13 @@ static const struct ad5686_bus_ops ad5686_spi_ops = { static int ad5686_spi_probe(struct spi_device *spi) { - return ad5686_probe(&spi->dev, spi_get_device_match_data(spi), - spi->modalias, &ad5686_spi_ops); + const struct ad5686_chip_info *info; + + info = spi_get_device_match_data(spi); + if (!info) + return -ENODATA; + + return ad5686_probe(&spi->dev, info, spi->modalias, &ad5686_spi_ops); } static const struct spi_device_id ad5686_spi_id[] = { diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index d49946adbde3..d5934405d555 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -68,8 +68,13 @@ static const struct ad5686_bus_ops ad5686_i2c_ops = { static int ad5686_i2c_probe(struct i2c_client *i2c) { - return ad5686_probe(&i2c->dev, i2c_get_match_data(i2c), - i2c->name, &ad5686_i2c_ops); + const struct ad5686_chip_info *info; + + info = i2c_get_match_data(i2c); + if (!info) + return -ENODATA; + + return ad5686_probe(&i2c->dev, info, i2c->name, &ad5686_i2c_ops); } static const struct i2c_device_id ad5686_i2c_id[] = { From 797d37e46c947dda659e0d523fbb936679025950 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 16 Jul 2026 13:14:19 +0100 Subject: [PATCH 302/513] iio: dac: ad5686: refactor command/data macros Replace usage of bit shifting macros for FIELD_PREP(), which would not ignore bit masking when preparing SPI/I2C commands. This change is a code hardening measure to be paired with the upcoming triggered buffer support. For the AD5310 regmap case, 16-bit data coming from the buffer may overlap with command bits if the data field is unmasked. *_REF_BIT_MSK and *_PD_MSK bit position macros are renamed (with a DATA prefix) so to indicate that they are relative to the DATA field. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/all/20260628143026.EC6CA1F000E9@smtp.kernel.org/ Reviewed-by: David Lechner Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686-spi.c | 21 +++++++++++---------- drivers/iio/dac/ad5686.c | 10 +++++----- drivers/iio/dac/ad5686.h | 22 ++++++++++++---------- drivers/iio/dac/ad5696-i2c.c | 11 ++++++----- 4 files changed, 34 insertions(+), 30 deletions(-) diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c index 859874ab861c..4604a882e785 100644 --- a/drivers/iio/dac/ad5686-spi.c +++ b/drivers/iio/dac/ad5686-spi.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -25,21 +26,21 @@ static int ad5686_spi_write(struct ad5686_state *st, switch (st->chip_info->regmap_type) { case AD5310_REGMAP: - st->data[0].d16 = cpu_to_be16(AD5310_CMD(cmd) | - val); + st->data[0].d16 = cpu_to_be16(FIELD_PREP(AD5310_CMD_MSK, cmd) | + FIELD_PREP(AD5310_DATA_MSK, val)); buf = &st->data[0].d8[0]; tx_len = 2; break; case AD5683_REGMAP: - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | - AD5683_DATA(val)); + st->data[0].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) | + FIELD_PREP(AD5683_DATA_MSK, val)); buf = &st->data[0].d8[1]; tx_len = 3; break; case AD5686_REGMAP: - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | - AD5686_ADDR(addr) | - val); + st->data[0].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) | + FIELD_PREP(AD5686_ADDR_MSK, addr) | + FIELD_PREP(AD5686_DATA_MSK, val)); buf = &st->data[0].d8[1]; tx_len = 3; break; @@ -80,9 +81,9 @@ static int ad5686_spi_read(struct ad5686_state *st, u8 addr) return -EINVAL; } - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | - AD5686_ADDR(addr)); - st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP)); + st->data[0].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) | + FIELD_PREP(AD5686_ADDR_MSK, addr)); + st->data[1].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, AD5686_CMD_NOOP)); ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t)); if (ret < 0) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 713fe71ad1e7..120dd095dd2e 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -35,8 +35,8 @@ static int ad5310_control_sync(struct ad5686_state *st) unsigned int pd_val = st->pwr_down_mask & st->pwr_down_mode; return ad5686_write(st, AD5686_CMD_CONTROL_REG, 0, - FIELD_PREP(AD5310_PD_MSK, pd_val & AD5686_PD_MSK) | - FIELD_PREP(AD5310_REF_BIT_MSK, st->use_internal_vref ? 0 : 1)); + FIELD_PREP(AD5310_DATA_PD_MSK, pd_val & AD5686_PD_MSK) | + FIELD_PREP(AD5310_DATA_REF_MSK, st->use_internal_vref ? 0 : 1)); } static int ad5683_control_sync(struct ad5686_state *st) @@ -44,8 +44,8 @@ static int ad5683_control_sync(struct ad5686_state *st) unsigned int pd_val = st->pwr_down_mask & st->pwr_down_mode; return ad5686_write(st, AD5686_CMD_CONTROL_REG, 0, - FIELD_PREP(AD5683_PD_MSK, pd_val & AD5686_PD_MSK) | - FIELD_PREP(AD5683_REF_BIT_MSK, st->use_internal_vref ? 0 : 1)); + FIELD_PREP(AD5683_DATA_PD_MSK, pd_val & AD5686_PD_MSK) | + FIELD_PREP(AD5683_DATA_REF_MSK, st->use_internal_vref ? 0 : 1)); } static inline unsigned int ad5686_pd_mask_shift(const struct iio_chan_spec *chan) @@ -562,7 +562,7 @@ int ad5686_probe(struct device *dev, break; case AD5686_REGMAP: ret = ad5686_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0, - st->use_internal_vref ? 0 : AD5686_REF_BIT_MSK); + st->use_internal_vref ? 0 : AD5686_DATA_REF_MSK); if (ret) return ret; break; diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index c424720f8f72..32cb3931413c 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -14,12 +14,14 @@ #include -#define AD5310_CMD(x) ((x) << 12) +#define AD5310_CMD_MSK GENMASK(15, 12) +#define AD5310_DATA_MSK GENMASK(11, 0) -#define AD5683_DATA(x) ((x) << 4) +#define AD5683_DATA_MSK GENMASK(19, 4) -#define AD5686_ADDR(x) ((x) << 16) -#define AD5686_CMD(x) ((x) << 20) +#define AD5686_CMD_MSK GENMASK(23, 20) +#define AD5686_ADDR_MSK GENMASK(19, 16) +#define AD5686_DATA_MSK GENMASK(15, 0) #define AD5686_ADDR_DAC(chan) (0x1 << (chan)) #define AD5686_ADDR_ALL_DAC 0xF @@ -38,15 +40,15 @@ #define AD5686_CMD_CONTROL_REG 0x4 #define AD5686_CMD_READBACK_ENABLE_V2 0x5 -#define AD5310_REF_BIT_MSK BIT(8) -#define AD5310_PD_MSK GENMASK(10, 9) +#define AD5310_DATA_REF_MSK BIT(8) +#define AD5310_DATA_PD_MSK GENMASK(10, 9) -#define AD5683_REF_BIT_MSK BIT(12) -#define AD5683_PD_MSK GENMASK(14, 13) +#define AD5683_DATA_REF_MSK BIT(12) /* DB16 */ +#define AD5683_DATA_PD_MSK GENMASK(14, 13) /* DB18:DB17 */ + +#define AD5686_DATA_REF_MSK BIT(0) -#define AD5686_REF_BIT_MSK BIT(0) #define AD5686_PD_MSK GENMASK(1, 0) - #define AD5686_PD_MODE_1K_TO_GND 0x1 #define AD5686_PD_MODE_100K_TO_GND 0x2 #define AD5686_PD_MODE_THREE_STATE 0x3 diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index d5934405d555..a3ec87fbdf9d 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -7,6 +7,7 @@ * Copyright 2018 Analog Devices Inc. */ +#include #include #include #include @@ -34,9 +35,8 @@ static int ad5686_i2c_read(struct ad5686_state *st, u8 addr) }; int ret; - st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP) | - AD5686_ADDR(addr) | - 0x00); + st->data[0].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, AD5686_CMD_NOOP) | + FIELD_PREP(AD5686_ADDR_MSK, addr)); ret = i2c_transfer(i2c->adapter, msg, 2); if (ret < 0) @@ -51,8 +51,9 @@ static int ad5686_i2c_write(struct ad5686_state *st, struct i2c_client *i2c = to_i2c_client(st->dev); int ret; - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | AD5686_ADDR(addr) - | val); + st->data[0].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) | + FIELD_PREP(AD5686_ADDR_MSK, addr) | + FIELD_PREP(AD5686_DATA_MSK, val)); ret = i2c_master_send(i2c, &st->data[0].d8[1], 3); if (ret < 0) From 92b7963fb544d42b0d974bfd86921566b404fabc Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 16 Jul 2026 13:14:20 +0100 Subject: [PATCH 303/513] iio: dac: ad5686: introduce sync operation Add sync() to operation to ad5686_bus_ops, which can be used to flush multiple pending data transfers at once. This is going to be used when implementing triggered buffer support. Reviewed-by: David Lechner Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index 32cb3931413c..ae9aeda2d201 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -70,10 +70,12 @@ struct ad5686_state; * struct ad5686_bus_ops - bus specific read/write operations * @read: read a register value at the given address * @write: write a command, address and value to the device + * @sync: ensure the completion of the write operation (optional) */ struct ad5686_bus_ops { int (*read)(struct ad5686_state *st, u8 addr); int (*write)(struct ad5686_state *st, u8 cmd, u8 addr, u16 val); + int (*sync)(struct ad5686_state *st); }; /** @@ -162,7 +164,13 @@ int ad5686_probe(struct device *dev, static inline int ad5686_write(struct ad5686_state *st, u8 cmd, u8 addr, u16 val) { - return st->ops->write(st, cmd, addr, val); + int ret; + + ret = st->ops->write(st, cmd, addr, val); + if (ret) + return ret; + + return st->ops->sync ? st->ops->sync(st) : 0; } static inline int ad5686_read(struct ad5686_state *st, u8 addr) From 154a7560b4d2252f29f73a2d174fea86e32a469a Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 16 Jul 2026 13:14:21 +0100 Subject: [PATCH 304/513] iio: dac: ad5686: implement new sync() op for the spi bus Use of local SPI bus data to manage a collection of SPI transfers and flush them to the SPI platform driver with the sync() operation. This allows for faster handling of multiple channel DAC writes, avoiding kernel overhead per spi_sync() call, which will be helpful when enabling triggered buffer support. For consistency, the read operation leverages the same transfer data rather than a stack-allocated array. Reviewed-by: David Lechner Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686-spi.c | 128 +++++++++++++++++++++++++++-------- drivers/iio/dac/ad5686.c | 4 +- drivers/iio/dac/ad5686.h | 8 ++- drivers/iio/dac/ad5696-i2c.c | 2 +- 4 files changed, 108 insertions(+), 34 deletions(-) diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c index 4604a882e785..1877d7cd2155 100644 --- a/drivers/iio/dac/ad5686-spi.c +++ b/drivers/iio/dac/ad5686-spi.c @@ -12,59 +12,101 @@ #include #include #include +#include #include #include #include "ad5686.h" +/** + * struct ad5686_spi_data - SPI bus specific data + * @msg: SPI message used for transfers + * @size: number of transfers currently in the message + * @capacity: maximum number of transfers that can be added to the message + * @xfers: array of SPI transfers, allocated with the provided capacity + */ +struct ad5686_spi_data { + struct spi_message msg; + unsigned int size; + unsigned int capacity; + struct spi_transfer xfers[] __counted_by(capacity); +}; + static int ad5686_spi_write(struct ad5686_state *st, u8 cmd, u8 addr, u16 val) { - struct spi_device *spi = to_spi_device(st->dev); - u8 tx_len, *buf; + struct ad5686_spi_data *bus_data = st->bus_data; + struct spi_transfer *xfer; + if (bus_data->size >= bus_data->capacity) + return -E2BIG; + + /* + * This function stores spi transfers to a spi message to be sent over + * the bus when sync() op is called. If there are already transfers in + * the spi message, set the cs_change flag on the last transfer to + * ensure that the chip select is deasserted between transfers. If this + * is the first transfer, initialize the spi message. Later on, the + * current transfer is added to the message with spi_message_add_tail(). + */ + if (bus_data->size) + bus_data->xfers[bus_data->size - 1].cs_change = 1; + else + spi_message_init(&bus_data->msg); + + xfer = &bus_data->xfers[bus_data->size]; + auto buf = &st->data[bus_data->size]; switch (st->chip_info->regmap_type) { case AD5310_REGMAP: - st->data[0].d16 = cpu_to_be16(FIELD_PREP(AD5310_CMD_MSK, cmd) | - FIELD_PREP(AD5310_DATA_MSK, val)); - buf = &st->data[0].d8[0]; - tx_len = 2; + buf->d16 = cpu_to_be16(FIELD_PREP(AD5310_CMD_MSK, cmd) | + FIELD_PREP(AD5310_DATA_MSK, val)); + *xfer = (struct spi_transfer) { + .tx_buf = &buf->d16, + .len = sizeof(buf->d16), + }; break; case AD5683_REGMAP: - st->data[0].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) | - FIELD_PREP(AD5683_DATA_MSK, val)); - buf = &st->data[0].d8[1]; - tx_len = 3; + buf->d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) | + FIELD_PREP(AD5683_DATA_MSK, val)); + *xfer = (struct spi_transfer) { + .tx_buf = &buf->d8[1], + .len = sizeof(buf->d8) - 1, + }; break; case AD5686_REGMAP: - st->data[0].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) | - FIELD_PREP(AD5686_ADDR_MSK, addr) | - FIELD_PREP(AD5686_DATA_MSK, val)); - buf = &st->data[0].d8[1]; - tx_len = 3; + buf->d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, cmd) | + FIELD_PREP(AD5686_ADDR_MSK, addr) | + FIELD_PREP(AD5686_DATA_MSK, val)); + *xfer = (struct spi_transfer) { + .tx_buf = &buf->d8[1], + .len = sizeof(buf->d8) - 1, + }; break; default: return -EINVAL; } - return spi_write(spi, buf, tx_len); + spi_message_add_tail(xfer, &bus_data->msg); + bus_data->size++; + + return 0; +} + +static int ad5686_spi_sync(struct ad5686_state *st) +{ + struct spi_device *spi = to_spi_device(st->dev); + struct ad5686_spi_data *bus_data = st->bus_data; + + bus_data->size = 0; /* always reset, even on sync failure */ + return spi_sync(spi, &bus_data->msg); } static int ad5686_spi_read(struct ad5686_state *st, u8 addr) { - struct spi_transfer t[] = { - { - .tx_buf = &st->data[0].d8[1], - .len = 3, - .cs_change = 1, - }, { - .tx_buf = &st->data[1].d8[1], - .rx_buf = &st->data[2].d8[1], - .len = 3, - }, - }; struct spi_device *spi = to_spi_device(st->dev); + struct ad5686_spi_data *bus_data = st->bus_data; + struct spi_transfer *xfer = &bus_data->xfers[0]; u8 cmd = 0; int ret; @@ -85,8 +127,21 @@ static int ad5686_spi_read(struct ad5686_state *st, u8 addr) FIELD_PREP(AD5686_ADDR_MSK, addr)); st->data[1].d32 = cpu_to_be32(FIELD_PREP(AD5686_CMD_MSK, AD5686_CMD_NOOP)); - ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t)); - if (ret < 0) + xfer[0] = (struct spi_transfer) { + .tx_buf = &st->data[0].d8[1], + .len = sizeof(st->data[0].d8) - 1, + .cs_change = 1, + }; + xfer[1] = (struct spi_transfer) { + .tx_buf = &st->data[1].d8[1], + .rx_buf = &st->data[2].d8[1], + .len = sizeof(st->data[1].d8) - 1, + }; + + spi_message_init_with_transfers(&bus_data->msg, xfer, 2); + + ret = spi_sync(spi, &bus_data->msg); + if (ret) return ret; return be32_to_cpu(st->data[2].d32); @@ -95,17 +150,30 @@ static int ad5686_spi_read(struct ad5686_state *st, u8 addr) static const struct ad5686_bus_ops ad5686_spi_ops = { .write = ad5686_spi_write, .read = ad5686_spi_read, + .sync = ad5686_spi_sync, }; static int ad5686_spi_probe(struct spi_device *spi) { const struct ad5686_chip_info *info; + struct ad5686_spi_data *bus_data; + struct device *dev = &spi->dev; + unsigned int capacity; info = spi_get_device_match_data(spi); if (!info) return -ENODATA; - return ad5686_probe(&spi->dev, info, spi->modalias, &ad5686_spi_ops); + /* read operation requires at least 2 transfers */ + capacity = max(info->num_channels, 2); + bus_data = devm_kzalloc(dev, struct_size(bus_data, xfers, capacity), + GFP_KERNEL); + if (!bus_data) + return -ENOMEM; + + bus_data->capacity = capacity; + + return ad5686_probe(dev, info, spi->modalias, &ad5686_spi_ops, bus_data); } static const struct spi_device_id ad5686_spi_id[] = { diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 120dd095dd2e..316f9ccf54d9 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -472,7 +472,8 @@ EXPORT_SYMBOL_NS_GPL(ad5679r_chip_info, "IIO_AD5686"); int ad5686_probe(struct device *dev, const struct ad5686_chip_info *chip_info, - const char *name, const struct ad5686_bus_ops *ops) + const char *name, const struct ad5686_bus_ops *ops, + void *bus_data) { struct reset_control *rstc; struct ad5686_state *st; @@ -487,6 +488,7 @@ int ad5686_probe(struct device *dev, st->dev = dev; st->ops = ops; + st->bus_data = bus_data; st->chip_info = chip_info; rstc = devm_reset_control_get_optional_exclusive(dev, NULL); diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index ae9aeda2d201..2c3d7d5b1d6b 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -25,6 +25,7 @@ #define AD5686_ADDR_DAC(chan) (0x1 << (chan)) #define AD5686_ADDR_ALL_DAC 0xF +#define AD5686_MAX_CHANNELS 16 #define AD5686_CMD_NOOP 0x0 #define AD5686_CMD_WRITE_INPUT_N 0x1 @@ -132,6 +133,7 @@ extern const struct ad5686_chip_info ad5679r_chip_info; * @use_internal_vref: set to true if the internal reference voltage is used * @lock: lock to protect access to state fields, which includes * the data buffer during regmap ops + * @bus_data: bus specific data * @data: transfer buffers */ struct ad5686_state { @@ -144,6 +146,7 @@ struct ad5686_state { unsigned int pwr_down_mode; bool use_internal_vref; struct mutex lock; + void *bus_data; /* * DMA (thus cache coherency maintenance) may require the @@ -154,13 +157,14 @@ struct ad5686_state { __be32 d32; __be16 d16; u8 d8[4]; - } data[3] __aligned(IIO_DMA_MINALIGN); + } data[AD5686_MAX_CHANNELS] __aligned(IIO_DMA_MINALIGN); }; int ad5686_probe(struct device *dev, const struct ad5686_chip_info *chip_info, - const char *name, const struct ad5686_bus_ops *ops); + const char *name, const struct ad5686_bus_ops *ops, + void *bus_data); static inline int ad5686_write(struct ad5686_state *st, u8 cmd, u8 addr, u16 val) { diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index a3ec87fbdf9d..046f904f7c88 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -75,7 +75,7 @@ static int ad5686_i2c_probe(struct i2c_client *i2c) if (!info) return -ENODATA; - return ad5686_probe(&i2c->dev, info, i2c->name, &ad5686_i2c_ops); + return ad5686_probe(&i2c->dev, info, i2c->name, &ad5686_i2c_ops, NULL); } static const struct i2c_device_id ad5686_i2c_id[] = { From 32052904ae30a14707278cd87062750c67a1c8e6 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 16 Jul 2026 13:14:22 +0100 Subject: [PATCH 305/513] iio: dac: ad5686: read_raw/write_raw: use guard(mutex)() Use guarded mutex lock to facilitate code review when adding new attributes. This will allow for early returns, avoiding error-prone locking and unlocking in error paths. This also adds missing include linux/cleanup.h. Gain-control support will allow the scale attribute to be configurable. Reviewed-by: Maxwell Doose Reviewed-by: Joshua Crofts Reviewed-by: Andy Shevchenko Reviewed-by: David Lechner Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 316f9ccf54d9..df32f46db81e 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -177,11 +178,11 @@ static int ad5686_read_raw(struct iio_dev *indio_dev, struct ad5686_state *st = iio_priv(indio_dev); int ret; + guard(mutex)(&st->lock); + switch (m) { case IIO_CHAN_INFO_RAW: - mutex_lock(&st->lock); ret = ad5686_read(st, chan->address); - mutex_unlock(&st->lock); if (ret < 0) return ret; *val = (ret >> chan->scan_type.shift) & @@ -202,23 +203,19 @@ static int ad5686_write_raw(struct iio_dev *indio_dev, long mask) { struct ad5686_state *st = iio_priv(indio_dev); - int ret; + + guard(mutex)(&st->lock); switch (mask) { case IIO_CHAN_INFO_RAW: if (val >= (1 << chan->scan_type.realbits) || val < 0) return -EINVAL; - mutex_lock(&st->lock); - ret = ad5686_write(st, AD5686_CMD_WRITE_INPUT_N_UPDATE_N, - chan->address, val << chan->scan_type.shift); - mutex_unlock(&st->lock); - break; + return ad5686_write(st, AD5686_CMD_WRITE_INPUT_N_UPDATE_N, + chan->address, val << chan->scan_type.shift); default: - ret = -EINVAL; + return -EINVAL; } - - return ret; } static const struct iio_info ad5686_info = { From a3c37fa97b134588471d20e2132626994f9954f2 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 16 Jul 2026 13:14:23 +0100 Subject: [PATCH 306/513] iio: dac: ad5686: add triggered buffer support Implement trigger handler by leveraging the LDAC gpio to update all DAC channels at once when it is available. Also, the multiple channel writes can be flushed at once with the sync() operation. Reviewed-by: David Lechner Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 2 ++ drivers/iio/dac/ad5686.c | 65 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 17529509da9d..d6d560c09e25 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -243,6 +243,8 @@ config LTC2688 config AD5686 tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER config AD5686_SPI tristate "Analog Devices AD5686 and similar multi-channel DACs (SPI)" diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index df32f46db81e..dfb19793a65e 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -21,7 +21,11 @@ #include #include +#include #include +#include +#include +#include #include "ad5686.h" @@ -243,6 +247,7 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ .address = addr, \ + .scan_index = chan, \ .scan_type = { \ .sign = 'u', \ .realbits = (bits), \ @@ -467,6 +472,59 @@ const struct ad5686_chip_info ad5679r_chip_info = { }; EXPORT_SYMBOL_NS_GPL(ad5679r_chip_info, "IIO_AD5686"); +static void do_ad5686_trigger_handler(struct iio_dev *indio_dev) +{ + struct ad5686_state *st = iio_priv(indio_dev); + u16 val[AD5686_MAX_CHANNELS] = { }; + unsigned int scan_count, ch, i; + bool async_update; + u8 cmd; + + if (iio_pop_from_buffer(indio_dev->buffer, val)) + return; + + guard(mutex)(&st->lock); + + scan_count = bitmap_weight(indio_dev->active_scan_mask, + iio_get_masklength(indio_dev)); + async_update = st->ldac_gpio && scan_count > 1; + if (async_update) { + /* use LDAC to update all channels simultaneously */ + cmd = AD5686_CMD_WRITE_INPUT_N; + gpiod_set_value_cansleep(st->ldac_gpio, 0); + } else { + cmd = AD5686_CMD_WRITE_INPUT_N_UPDATE_N; + } + + i = 0; + iio_for_each_active_channel(indio_dev, ch) { + if (st->ops->write(st, cmd, indio_dev->channels[ch].address, val[i++])) + break; + } + + /* + * If sync() is available, it is called here regardless of write + * failure to allow bus implementation to reset. In that case, partial + * writes are unlikely as the write operations would just queue up + * the transfers. + */ + if (st->ops->sync) + st->ops->sync(st); + + if (async_update) + gpiod_set_value_cansleep(st->ldac_gpio, 1); +} + +static irqreturn_t ad5686_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + + do_ad5686_trigger_handler(indio_dev); + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + int ad5686_probe(struct device *dev, const struct ad5686_chip_info *chip_info, const char *name, const struct ad5686_bus_ops *ops, @@ -569,6 +627,13 @@ int ad5686_probe(struct device *dev, return -EINVAL; } + ret = devm_iio_triggered_buffer_setup_ext(dev, indio_dev, NULL, + &ad5686_trigger_handler, + IIO_BUFFER_DIRECTION_OUT, + NULL, NULL); + if (ret) + return ret; + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(ad5686_probe, "IIO_AD5686"); From 23f66b58b14bd5068ece70c118755cc24ff75baf Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Thu, 16 Jul 2026 13:14:24 +0100 Subject: [PATCH 307/513] iio: dac: ad5686: add gain control support Most of the supported devices rely on a GAIN pin to control a 2x multiplier applied to the output voltage. Other devices, e.g. the single-channel ones, provides a gain control through a bit field in the control register. Some designs might have the GAIN pin hardwired to VDD/VLOGIC or GND, which would have no "gain-gpios" device property, being able to set "adi,range-double" if it is hardwired to VDD. The vref_mv field is moved down in the struct ad5686_state, so that the overall size increase is reduced. Reviewed-by: David Lechner Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 120 +++++++++++++++++++++++++++++++++++++-- drivers/iio/dac/ad5686.h | 12 +++- 2 files changed, 125 insertions(+), 7 deletions(-) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index dfb19793a65e..873dfb5b9107 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -15,10 +15,13 @@ #include #include #include +#include #include +#include #include #include #include +#include #include #include @@ -41,7 +44,8 @@ static int ad5310_control_sync(struct ad5686_state *st) return ad5686_write(st, AD5686_CMD_CONTROL_REG, 0, FIELD_PREP(AD5310_DATA_PD_MSK, pd_val & AD5686_PD_MSK) | - FIELD_PREP(AD5310_DATA_REF_MSK, st->use_internal_vref ? 0 : 1)); + FIELD_PREP(AD5310_DATA_REF_MSK, st->use_internal_vref ? 0 : 1) | + FIELD_PREP(AD5310_DATA_GAIN_MSK, st->double_scale ? 1 : 0)); } static int ad5683_control_sync(struct ad5686_state *st) @@ -50,7 +54,8 @@ static int ad5683_control_sync(struct ad5686_state *st) return ad5686_write(st, AD5686_CMD_CONTROL_REG, 0, FIELD_PREP(AD5683_DATA_PD_MSK, pd_val & AD5686_PD_MSK) | - FIELD_PREP(AD5683_DATA_REF_MSK, st->use_internal_vref ? 0 : 1)); + FIELD_PREP(AD5683_DATA_REF_MSK, st->use_internal_vref ? 0 : 1) | + FIELD_PREP(AD5683_DATA_GAIN_MSK, st->double_scale ? 1 : 0)); } static inline unsigned int ad5686_pd_mask_shift(const struct iio_chan_spec *chan) @@ -193,9 +198,14 @@ static int ad5686_read_raw(struct iio_dev *indio_dev, GENMASK(chan->scan_type.realbits - 1, 0); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = st->vref_mv; - *val2 = chan->scan_type.realbits; - return IIO_VAL_FRACTIONAL_LOG2; + if (st->double_scale) { + *val = st->scale_avail[2]; + *val2 = st->scale_avail[3]; + } else { + *val = st->scale_avail[0]; + *val2 = st->scale_avail[1]; + } + return IIO_VAL_INT_PLUS_NANO; } return -EINVAL; } @@ -207,6 +217,8 @@ static int ad5686_write_raw(struct iio_dev *indio_dev, long mask) { struct ad5686_state *st = iio_priv(indio_dev); + bool double_scale; + int ret; guard(mutex)(&st->lock); @@ -217,6 +229,82 @@ static int ad5686_write_raw(struct iio_dev *indio_dev, return ad5686_write(st, AD5686_CMD_WRITE_INPUT_N_UPDATE_N, chan->address, val << chan->scan_type.shift); + case IIO_CHAN_INFO_SCALE: + if (val == st->scale_avail[0] && val2 == st->scale_avail[1]) + double_scale = false; + else if (val == st->scale_avail[2] && val2 == st->scale_avail[3]) + double_scale = true; + else + return -EINVAL; + + if (st->double_scale == double_scale) + return 0; /* no change */ + + if (st->chip_info->regmap_type == AD5686_REGMAP && !st->gain_gpio) + return -EINVAL; /* GAIN pin is board-strapped */ + + st->double_scale = double_scale; + switch (st->chip_info->regmap_type) { + case AD5310_REGMAP: + ret = ad5310_control_sync(st); + break; + case AD5683_REGMAP: + ret = ad5683_control_sync(st); + break; + case AD5686_REGMAP: + ret = gpiod_set_value_cansleep(st->gain_gpio, + st->double_scale ? 1 : 0); + break; + default: + ret = -EINVAL; + } + if (ret) + st->double_scale = !double_scale; /* revert on failure */ + return ret; + default: + return -EINVAL; + } +} + +static int ad5686_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_RAW: + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static int ad5686_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ad5686_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_INT_PLUS_NANO; + + if (st->chip_info->regmap_type == AD5686_REGMAP && !st->gain_gpio) { + /* + * GAIN pin is board-strapped, so only the current + * scale is available. + */ + *vals = st->double_scale ? &st->scale_avail[2] : + &st->scale_avail[0]; + *length = 2; + return IIO_AVAIL_LIST; + } + + *vals = st->scale_avail; + *length = ARRAY_SIZE(st->scale_avail); + return IIO_AVAIL_LIST; default: return -EINVAL; } @@ -225,6 +313,8 @@ static int ad5686_write_raw(struct iio_dev *indio_dev, static const struct iio_info ad5686_info = { .read_raw = ad5686_read_raw, .write_raw = ad5686_write_raw, + .write_raw_get_fmt = ad5686_write_raw_get_fmt, + .read_avail = ad5686_read_avail, }; static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { @@ -246,6 +336,7 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { .channel = chan, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),\ .address = addr, \ .scan_index = chan, \ .scan_type = { \ @@ -472,6 +563,15 @@ const struct ad5686_chip_info ad5679r_chip_info = { }; EXPORT_SYMBOL_NS_GPL(ad5679r_chip_info, "IIO_AD5686"); +static void ad5686_init_scale_avail(struct ad5686_state *st) +{ + int realbits = st->chip_info->channels[0].scan_type.realbits; + s64 tmp = 2ULL * st->vref_mv * NANO >> realbits; + + st->scale_avail[2] = div_s64_rem(tmp, NANO, &st->scale_avail[3]); + st->scale_avail[0] = div_s64_rem(tmp >> 1, NANO, &st->scale_avail[1]); +} + static void do_ad5686_trigger_handler(struct iio_dev *indio_dev) { struct ad5686_state *st = iio_priv(indio_dev); @@ -584,6 +684,16 @@ int ad5686_probe(struct device *dev, return dev_err_probe(dev, PTR_ERR(st->ldac_gpio), "Failed to get LDAC GPIO\n"); + st->double_scale = device_property_read_bool(dev, "adi,range-double"); + st->gain_gpio = devm_gpiod_get_optional(dev, "gain", + st->double_scale ? GPIOD_OUT_HIGH : + GPIOD_OUT_LOW); + if (IS_ERR(st->gain_gpio)) + return dev_err_probe(dev, PTR_ERR(st->gain_gpio), + "Failed to get GAIN GPIO\n"); + + ad5686_init_scale_avail(st); + /* Initialize masks to all ones */ st->pwr_down_mask = ~0; st->pwr_down_mode = ~0; diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index 2c3d7d5b1d6b..c1222ef83a4f 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -41,9 +41,11 @@ #define AD5686_CMD_CONTROL_REG 0x4 #define AD5686_CMD_READBACK_ENABLE_V2 0x5 +#define AD5310_DATA_GAIN_MSK BIT(7) #define AD5310_DATA_REF_MSK BIT(8) #define AD5310_DATA_PD_MSK GENMASK(10, 9) +#define AD5683_DATA_GAIN_MSK BIT(11) /* DB15 */ #define AD5683_DATA_REF_MSK BIT(12) /* DB16 */ #define AD5683_DATA_PD_MSK GENMASK(14, 13) /* DB18:DB17 */ @@ -127,9 +129,12 @@ extern const struct ad5686_chip_info ad5679r_chip_info; * @chip_info: chip model specific constants, available modes etc * @ops: bus specific operations * @ldac_gpio: LDAC pin GPIO descriptor - * @vref_mv: actual reference voltage used + * @gain_gpio: GAIN pin GPIO descriptor * @pwr_down_mask: power down mask * @pwr_down_mode: current power down mode + * @scale_avail: pre-calculated available scale values + * @vref_mv: actual reference voltage used + * @double_scale: flag to indicate the gain multiplier is applied * @use_internal_vref: set to true if the internal reference voltage is used * @lock: lock to protect access to state fields, which includes * the data buffer during regmap ops @@ -141,9 +146,12 @@ struct ad5686_state { const struct ad5686_chip_info *chip_info; const struct ad5686_bus_ops *ops; struct gpio_desc *ldac_gpio; - unsigned short vref_mv; + struct gpio_desc *gain_gpio; unsigned int pwr_down_mask; unsigned int pwr_down_mode; + int scale_avail[4]; + unsigned short vref_mv; + bool double_scale; bool use_internal_vref; struct mutex lock; void *bus_data; From 0777fb25ed35cbb1d0f10625e3b0362a139f8f46 Mon Sep 17 00:00:00 2001 From: Chengrui Liu Date: Thu, 16 Jul 2026 09:04:35 +0800 Subject: [PATCH 308/513] iio: light: tsl2583: migrate to sysfs_emit() and sysfs_emit_at() Replace sprintf() with sysfs_emit() for single-value sysfs show functions, and use sysfs_emit_at() for lux_table_show() which concatenates multiple values. This ensures buffer safety and follows the modern kernel sysfs API. Signed-off-by: Chengrui Liu Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2583.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c index a0dd122af2cf..a210d87a1519 100644 --- a/drivers/iio/light/tsl2583.c +++ b/drivers/iio/light/tsl2583.c @@ -475,7 +475,7 @@ static ssize_t in_illuminance_input_target_show(struct device *dev, int ret; mutex_lock(&chip->als_mutex); - ret = sprintf(buf, "%d\n", chip->als_settings.als_cal_target); + ret = sysfs_emit(buf, "%d\n", chip->als_settings.als_cal_target); mutex_unlock(&chip->als_mutex); return ret; @@ -533,10 +533,10 @@ static ssize_t in_illuminance_lux_table_show(struct device *dev, int offset = 0; for (i = 0; i < ARRAY_SIZE(chip->als_settings.als_device_lux); i++) { - offset += sprintf(buf + offset, "%u,%u,%u,", - chip->als_settings.als_device_lux[i].ratio, - chip->als_settings.als_device_lux[i].ch0, - chip->als_settings.als_device_lux[i].ch1); + offset += sysfs_emit_at(buf, offset, "%u,%u,%u,", + chip->als_settings.als_device_lux[i].ratio, + chip->als_settings.als_device_lux[i].ch0, + chip->als_settings.als_device_lux[i].ch1); if (chip->als_settings.als_device_lux[i].ratio == 0) { /* * We just printed the first "0" entry. @@ -547,7 +547,7 @@ static ssize_t in_illuminance_lux_table_show(struct device *dev, } } - offset += sprintf(buf + offset, "\n"); + offset += sysfs_emit_at(buf, offset, "\n"); return offset; } From 0c76b5213482b597d545255710768e4b7934defc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Wed, 15 Jul 2026 13:24:53 +0200 Subject: [PATCH 309/513] iio: buffer-dmaengine: Add support for cyclic DMA transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow buffer blocks flagged as cyclic to be submitted as repeating DMA transfers. For cyclic blocks, use DMA_PREP_REPEAT so the engine keeps replaying the descriptor. This is useful for output buffers where the same data should be driven continuously without userspace having to requeue it. Examples include continuous RF transmit paths replaying a calibration, test or beacon pattern. Skip installing the completion callback for cyclic blocks. Since the transfer is continuously replayed, the callback would fire on every period, throwing off the block refcount. Because nothing prevents a new cyclic transfer from replacing an already active cyclic one, always set DMA_PREP_LOAD_EOT so the engine correctly terminates the active transfer before loading the new descriptor. Limit the DMA buffer queue to one cyclic DMABUF at a time. There is currently no known use case for queueing multiple cyclic blocks, and cyclic blocks stay referenced until the buffer is disabled. Signed-off-by: Nuno Sá Signed-off-by: Jonathan Cameron --- .../buffer/industrialio-buffer-dmaengine.c | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index 98acce909854..ecc02a427b92 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -56,6 +56,23 @@ static void iio_dmaengine_buffer_block_done(void *data, iio_dma_buffer_block_done(block); } +/* + * Cyclic transfers run until abort. Cap active cyclic blocks to one until there + * is a use case for more. + */ +static bool iio_dmaengine_buffer_has_active_cyclic(struct dmaengine_buffer *dmaengine_buffer) +{ + struct iio_dma_buffer_block *block; + + guard(spinlock_irqsave)(&dmaengine_buffer->queue.list_lock); + list_for_each_entry(block, &dmaengine_buffer->active, head) { + if (block->cyclic) + return true; + } + + return false; +} + static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, struct iio_dma_buffer_block *block) { @@ -79,7 +96,14 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, else dma_dir = DMA_MEM_TO_DEV; + if (block->cyclic && iio_dmaengine_buffer_has_active_cyclic(dmaengine_buffer)) { + dev_err(queue->dev, "cyclic DMA transfer already active\n"); + return -EBUSY; + } + if (block->sg_table) { + unsigned long flags; + sgl = block->sg_table->sgl; nents = sg_nents_for_len(sgl, block->bytes_used); if (nents < 0) @@ -99,9 +123,19 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, sgl = sg_next(sgl); } + if (block->cyclic) + flags = DMA_PREP_REPEAT; + else + flags = DMA_PREP_INTERRUPT; + + /* + * A new transfer may need to end an already active cyclic transfer + * before it can run, so always set the EOT flag. + */ + flags |= DMA_PREP_LOAD_EOT; desc = dmaengine_prep_peripheral_dma_vec(dmaengine_buffer->chan, vecs, nents, dma_dir, - DMA_PREP_INTERRUPT); + flags); kfree(vecs); } else { max_size = min(block->size, dmaengine_buffer->max_size); @@ -122,8 +156,10 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, if (!desc) return -ENOMEM; - desc->callback_result = iio_dmaengine_buffer_block_done; - desc->callback_param = block; + if (!block->cyclic) { + desc->callback_result = iio_dmaengine_buffer_block_done; + desc->callback_param = block; + } cookie = dmaengine_submit(desc); if (dma_submit_error(cookie)) From d8a07f620077fc84a2e94610334a35132dbeb2a8 Mon Sep 17 00:00:00 2001 From: Kurt Borja Date: Tue, 14 Jul 2026 18:21:23 -0500 Subject: [PATCH 310/513] dt-bindings: iio: adc: Add reference-sources property Some ADCs have configurable voltage reference sources for each channel. Signed-off-by: Kurt Borja Acked-by: Conor Dooley Signed-off-by: David Lechner (TI) Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/adc.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/adc.yaml b/Documentation/devicetree/bindings/iio/adc/adc.yaml index b9bc02b5b07a..8f608bf0b24d 100644 --- a/Documentation/devicetree/bindings/iio/adc/adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adc.yaml @@ -73,6 +73,13 @@ properties: device design and can interact with other characteristics such as settling time. + reference-sources: + $ref: /schemas/types.yaml#/definitions/string-array + description: + Indicates the voltage reference source or sources for this channel. Some + ADCs usually allow choosing between internal reference sources or a pair + of external pins. + anyOf: - oneOf: - required: From fc418aa7742b799a56923016121841806a3e6545 Mon Sep 17 00:00:00 2001 From: Kurt Borja Date: Tue, 14 Jul 2026 18:21:24 -0500 Subject: [PATCH 311/513] dt-bindings: iio: adc: Add excitation current sources properties Some ADCs incorporate current sources that provide excitation current to resistive temperature devices (RTDs), thermistors, diodes and other resistive sensors that require constant current biasing. Signed-off-by: Kurt Borja Acked-by: Conor Dooley Signed-off-by: David Lechner (TI) Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/adc.yaml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/adc.yaml b/Documentation/devicetree/bindings/iio/adc/adc.yaml index 8f608bf0b24d..9f7196d61ac3 100644 --- a/Documentation/devicetree/bindings/iio/adc/adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adc.yaml @@ -80,6 +80,27 @@ properties: ADCs usually allow choosing between internal reference sources or a pair of external pins. + excitation-channels: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + Excitation current sources provide current to resistive temperature + devices (RTDs), thermistors, diodes and other resistive sensors that + require constant current biasing. + + This array describes the mux configuration of the excitation current + sources. The values correspond to pins similar to the diff-channels and + single-channel properties. + + excitation-current-nanoamp: + description: + Excitation current sources provide current to resistive temperature + devices (RTDs), thermistors, diodes and other resistive sensors that + require constant current biasing. + + This array describes the current configuration of the excitation current + sources. The index in the array corresponds to the same index in the + excitation-channels array. + anyOf: - oneOf: - required: From 4bbfdc86dc98b890db3d5b9c3456e73cea4767ab Mon Sep 17 00:00:00 2001 From: Kurt Borja Date: Tue, 14 Jul 2026 18:21:25 -0500 Subject: [PATCH 312/513] dt-bindings: iio: adc: Add burn-out current properties Some ADCs incorporate burn-out current sources that provide current to the channel's input pins for open-circuit or short-circuit detection. Signed-off-by: Kurt Borja Acked-by: Conor Dooley Signed-off-by: David Lechner (TI) Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/adc.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/adc.yaml b/Documentation/devicetree/bindings/iio/adc/adc.yaml index 9f7196d61ac3..e1da63119648 100644 --- a/Documentation/devicetree/bindings/iio/adc/adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adc.yaml @@ -101,6 +101,11 @@ properties: sources. The index in the array corresponds to the same index in the excitation-channels array. + burn-out-current-nanoamp: + description: + Burn-out current sources provide current to the channel's input pins for + open-circuit or short-circuit detection. + anyOf: - oneOf: - required: From ba19e177301a64e00f73d31513282378dade98a9 Mon Sep 17 00:00:00 2001 From: "David Lechner (TI)" Date: Tue, 14 Jul 2026 18:21:26 -0500 Subject: [PATCH 313/513] dt-bindings: iio: adc: add input-chopping property Add a generic input-chopping property to adc.yaml. This is a feature seen frequently in ADCs that are designed to measure resistive loads, such as RTDs and strain gauges. Enabling this can reduce offset errors by swapping the positive and negative input channels on every other conversion. This can have side-effects in timing and filter response, so it is not always desirable to enable this feature in some applications. Therefore, it is best to make this a property that can be enabled or disabled in the device tree. Signed-off-by: David Lechner (TI) Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/adc.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/adc.yaml b/Documentation/devicetree/bindings/iio/adc/adc.yaml index e1da63119648..b673eaa5d072 100644 --- a/Documentation/devicetree/bindings/iio/adc/adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adc.yaml @@ -80,6 +80,14 @@ properties: ADCs usually allow choosing between internal reference sources or a pair of external pins. + input-chopping: + type: boolean + description: + If set, the positive and negative input channels are swapped on every + other conversion to reduce offset error. This generally comes at the + expense of added settling time between conversions, so should be omitted + on systems with high-frequency signals or high data rate requirements. + excitation-channels: $ref: /schemas/types.yaml#/definitions/uint32-array description: From 97c957e5cb9416c0096a34228417767ed8c44cd1 Mon Sep 17 00:00:00 2001 From: "David Lechner (TI)" Date: Tue, 14 Jul 2026 18:21:27 -0500 Subject: [PATCH 314/513] dt-bindings: iio: adc: add ti,ads112c14 Add new bindings for ti,ads112c14 and similar devices. This is an ADC that is primarily intended for use with resistive sensors (RTD, thermocouple, Wheatstone bridge, etc.). There are a few unusual properties because of this. In particular, the reference voltage source and current output requirements can be different for each measurement, so these are included in the channel bindings. The REFP/REFN reference voltage is usually just connected to a resistor that is being driven by the ADC's current outputs, so there is special property for this case rather than requiring a regulator to be defined to represent that. Reviewed-by: Conor Dooley Signed-off-by: David Lechner (TI) Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/ti,ads112c14.yaml | 219 ++++++++++++++++++ MAINTAINERS | 6 + 2 files changed, 225 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/ti,ads112c14.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads112c14.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads112c14.yaml new file mode 100644 index 000000000000..facfeee61330 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads112c14.yaml @@ -0,0 +1,219 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/ti,ads112c14.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments' ADS112C14 and similar ADC chips + +description: | + Supports the following Texas Instruments' ADC chips: + - ADS112C14 (16-bit) + - ADS122C14 (24-bit) + + https://www.ti.com/lit/ds/symlink/ads122c14.pdf + + These chips are primarily designed for use with resistive sensors such as + RTDs, thermocouples, Wheatstone bridges, etc. The channel bindings reflect + this in that each channel represents the conditions required to make a + measurement rather than strictly just the physical input channels. + +maintainers: + - David Lechner + +properties: + compatible: + enum: + - ti,ads112c14 + - ti,ads122c14 + + reg: + items: + - minimum: 0x40 + maximum: 0x47 + + clocks: + maxItems: 1 + description: Optional external clock connected to GPIO3 pin. + + avdd-supply: true + dvdd-supply: true + + refp-supply: true + refn-supply: true + + ti,refp-refn-resistor-ohms: + description: + The resistance of the external resistor between REFP and REFN when using + resistor bridge driven by current outputs for RTD measurements. + + interrupts: + minItems: 1 + items: + - description: /FAULT interrupt (GPIO2 pin) + - description: /DRDY interrupt (GPIO3 pin) + + interrupt-names: + minItems: 1 + maxItems: 2 + items: + enum: [fault, drdy] + + gpio-controller: true + '#gpio-cells': + const: 2 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + ^channel@[0-9a-f]$: + $ref: adc.yaml + + unevaluatedProperties: false + + properties: + reg: + items: + - maximum: 15 # arbitrary limit, channel@ can be any combination of AIN0-AIN7 + + single-channel: + maximum: 7 + + diff-channels: + items: + maximum: 7 + + bipolar: true + + input-chopping: true + + excitation-channels: + maxItems: 2 + items: + maximum: 7 + + excitation-current-nanoamp: + maxItems: 2 + items: + enum: [1000, 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, + 90000, 100000, 200000, 300000, 400000, 500000, 600000, 700000, + 800000, 900000, 1000000] + + burn-out-current-nanoamp: + items: + - enum: [200, 1000, 10000] + + reference-sources: + items: + - enum: [internal-2.5v, internal-1.25v, external, avdd] + default: internal-2.5v + + dependencies: + excitation-channels: [ excitation-current-nanoamp ] + excitation-current-nanoamp: [ excitation-channels ] + + oneOf: + - required: [ single-channel ] + - required: [ diff-channels ] + +unevaluatedProperties: false + +required: + - compatible + - reg + - avdd-supply + - dvdd-supply + +dependencies: + refn-supply: [ refp-supply ] + +oneOf: + - required: [ refp-supply ] + - required: [ "ti,refp-refn-resistor-ohms" ] + - properties: + refp-supply: false + refn-supply: false + ti,refp-refn-resistor-ohms: false + +# /DRDY out and CLK in use the same AIN7/GPIO3 pin. +if: + required: + - interrupt-names + properties: + interrupt-names: + contains: + const: drdy +then: + properties: + clocks: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + adc@40 { + compatible = "ti,ads112c14"; + reg = <0x40>; + + avdd-supply = <&avdd>; + dvdd-supply = <&dvdd>; + + /* 3-Wire RTD: Two IDACs, One Measurement (AIN1-AIN2) */ + + ti,refp-refn-resistor-ohms = <500>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + diff-channels = <1>, <2>; + input-chopping; + excitation-channels = <0>, <3>; + excitation-current-nanoamp = <500000>, <500000>; + reference-sources = "external"; + label = "rtd"; + }; + }; + }; + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + adc@40 { + compatible = "ti,ads112c14"; + reg = <0x40>; + + avdd-supply = <&avdd>; + dvdd-supply = <&dvdd>; + + /* Resistive Bridge Measurement With a Thermistor for Temperature Compensation */ + + refp-supply = <&avdd>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + diff-channels = <6>, <7>; + bipolar; + reference-sources = "external"; + label = "bridge"; + }; + + channel@1 { + reg = <1>; + diff-channels = <1>, <2>; + reference-sources = "internal-2.5v"; + label = "thermistor"; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 2b1ec46c5919..3e114e0c9c5f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -26958,6 +26958,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/adc/ti,ads1119.yaml F: drivers/iio/adc/ti-ads1119.c +TI ADS112C14 ADC DRIVER +M: David Lechner +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/adc/ti,ads112c14.yaml + TI ADS1018 ADC DRIVER M: Kurt Borja L: linux-iio@vger.kernel.org From b7fa84f60ffe17cf35fc05da7c6b8565dc2da200 Mon Sep 17 00:00:00 2001 From: "David Lechner (TI)" Date: Tue, 14 Jul 2026 18:21:28 -0500 Subject: [PATCH 315/513] iio: adc: add ti-ads112c14 driver Add a new driver for the TI ADS112C14/ADS122C14 ADC chips. This first step is adding a very basic driver that only supports power on/reset and reading the system monitor channels. ADS112C14_SYS_MON_CHANNEL_SHORT is the last channel rather than being in logical order by address to keep the voltage channels together and in case we find we need to add variants of this channel with different voltage reference later. Signed-off-by: David Lechner (TI) Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + drivers/iio/adc/Kconfig | 12 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ti-ads112c14.c | 582 +++++++++++++++++++++++++++++++++ 4 files changed, 596 insertions(+) create mode 100644 drivers/iio/adc/ti-ads112c14.c diff --git a/MAINTAINERS b/MAINTAINERS index 3e114e0c9c5f..7077ed7c9efe 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -26963,6 +26963,7 @@ M: David Lechner L: linux-iio@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/iio/adc/ti,ads112c14.yaml +F: drivers/iio/adc/ti-ads112c14.c TI ADS1018 ADC DRIVER M: Kurt Borja diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index d1b198cb8a80..642c42b1fae7 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1798,6 +1798,18 @@ config TI_ADS1119 This driver can also be built as a module. If so, the module will be called ti-ads1119. +config TI_ADS112C14 + tristate "Texas Instruments ADS112C14/ADS122C14" + depends on I2C + select REGMAP + select REGMAP_I2C + help + If you say yes here you get support for Texas Instruments ADS112C14, + ADS122C14 ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-ads112c14. + config TI_ADS124S08 tristate "Texas Instruments ADS124S08" depends on SPI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 0f90b75577ff..3778c7b0b8b5 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -154,6 +154,7 @@ obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o obj-$(CONFIG_TI_ADS1018) += ti-ads1018.o obj-$(CONFIG_TI_ADS1100) += ti-ads1100.o obj-$(CONFIG_TI_ADS1119) += ti-ads1119.o +obj-$(CONFIG_TI_ADS112C14) += ti-ads112c14.o obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c new file mode 100644 index 000000000000..bc0fd6839b72 --- /dev/null +++ b/drivers/iio/adc/ti-ads112c14.c @@ -0,0 +1,582 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * IIO driver for Texas Instruments ADS112C14 and similar ADCs. + * + * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2026 Baylibre Inc. + * + * Datasheet: https://www.ti.com/lit/ds/symlink/ads122c14.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Datasheet t_d(RST) - time to wait after reset before next I2C use. */ +#define ADS112C14_DELAY_RESET_US 500 + +#define ADS112C14_CMD_RDATA 0x00 +#define ADS112C14_CMD_RREG 0x40 +#define ADS112C14_CMD_WREG 0x80 + +#define ADS112C14_REG_DEVICE_ID 0x00 +#define ADS112C14_DEVICE_ID_BITS GENMASK(3, 0) + +#define ADS112C14_REG_REVISION_ID 0x01 + +#define ADS112C14_REG_STATUS_MSB 0x02 +#define ADS112C14_STATUS_MSB_RESETN BIT(7) +#define ADS112C14_STATUS_MSB_AVDD_UVN BIT(6) +#define ADS112C14_STATUS_MSB_REF_UVN BIT(5) +#define ADS112C14_STATUS_MSB_REG_MAP_CRC_FAULTN BIT(3) +#define ADS112C14_STATUS_MSB_MEM_FAULTN BIT(2) +#define ADS112C14_STATUS_MSB_REG_WRITE_FAULTN BIT(1) +#define ADS112C14_STATUS_MSB_DRDY BIT(0) + +#define ADS112C14_REG_STATUS_LSB 0x03 +#define ADS112C14_STATUS_LSB_CONV_COUNT GENMASK(7, 4) +#define ADS112C14_STATUS_LSB_GPIO3_DAT_IN BIT(3) +#define ADS112C14_STATUS_LSB_GPIO2_DAT_IN BIT(2) +#define ADS112C14_STATUS_LSB_GPIO1_DAT_IN BIT(1) +#define ADS112C14_STATUS_LSB_GPIO0_DAT_IN BIT(0) + +#define ADS112C14_REG_CONVERSION_CTRL 0x04 +#define ADS112C14_CONVERSION_CTRL_RESET GENMASK(7, 2) +#define ADS112C14_CONVERSION_CTRL_START BIT(1) +#define ADS112C14_CONVERSION_CTRL_STOP BIT(0) + +#define ADS112C14_REG_DEVICE_CFG 0x05 +#define ADS112C14_DEVICE_CFG_PWDN BIT(7) +#define ADS112C14_DEVICE_CFG_STBY_MODE BIT(6) +#define ADS112C14_DEVICE_CFG_BOCS GENMASK(5, 4) +#define ADS112C14_DEVICE_CFG_CLK_SEL BIT(3) +#define ADS112C14_DEVICE_CFG_CONV_MODE BIT(2) +#define ADS112C14_DEVICE_CFG_CONV_MODE_CONTINUOUS 0 +#define ADS112C14_DEVICE_CFG_CONV_MODE_SINGLE_SHOT 1 +#define ADS112C14_DEVICE_CFG_SPEED_MODE GENMASK(1, 0) + +#define ADS112C14_REG_DATA_RATE_CFG 0x06 +#define ADS112C14_DATA_RATE_CFG_DELAY GENMASK(7, 4) +#define ADS112C14_DATA_RATE_CFG_GC_EN BIT(3) +#define ADS112C14_DATA_RATE_CFG_FLTR_OSR GENMASK(2, 0) + +#define ADS112C14_REG_MUX_CFG 0x07 +#define ADS112C14_MUX_CFG_AINP GENMASK(7, 4) +#define ADS112C14_MUX_CFG_AINN GENMASK(3, 0) + +#define ADS112C14_REG_GAIN_CFG 0x08 +#define ADS112C14_GAIN_CFG_SPARE BIT(7) +#define ADS112C14_GAIN_CFG_SYS_MON GENMASK(6, 4) +#define ADS112C14_GAIN_CFG_GAIN GENMASK(3, 0) + +#define ADS112C14_REG_REFERENCE_CFG 0x09 +#define ADS112C14_REFERENCE_CFG_REF_UV_EN BIT(7) +#define ADS112C14_REFERENCE_CFG_REFP_BUF_EN BIT(5) +#define ADS112C14_REFERENCE_CFG_REFN_BUF_EN BIT(4) +#define ADS112C14_REFERENCE_CFG_REF_VAL BIT(2) +#define ADS112C14_REFERENCE_CFG_REF_VAL_1_25V 0 +#define ADS112C14_REFERENCE_CFG_REF_VAL_2_5V 1 +#define ADS112C14_REFERENCE_CFG_REF_SEL GENMASK(1, 0) +#define ADS112C14_REFERENCE_CFG_REF_SEL_INTERNAL 0 +#define ADS112C14_REFERENCE_CFG_REF_SEL_EXTERNAL 1 +#define ADS112C14_REFERENCE_CFG_REF_SEL_AVDD 2 + +#define ADS112C14_REG_DIGITAL_CFG 0x0A +#define ADS112C14_DIGITAL_CFG_REG_MAP_CRC_EN BIT(6) +#define ADS112C14_DIGITAL_CFG_I2C_CRC_EN BIT(5) +#define ADS112C14_DIGITAL_CFG_STATUS_EN BIT(4) +#define ADS112C14_DIGITAL_CFG_FAULT_PIN_BEHAVIOR BIT(3) +#define ADS112C14_DIGITAL_CFG_CODING BIT(1) + +#define ADS112C14_REG_GPIO_CFG 0x0B +#define ADS112C14_GPIO_CFG_GPIO3_CFG GENMASK(7, 6) +#define ADS112C14_GPIO_CFG_GPIO2_CFG GENMASK(5, 4) +#define ADS112C14_GPIO_CFG_GPIO1_CFG GENMASK(3, 2) +#define ADS112C14_GPIO_CFG_GPIO0_CFG GENMASK(1, 0) + +#define ADS112C14_REG_GPIO_DATA_OUTPUT 0x0C +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC BIT(7) +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO2_SRC BIT(6) +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_DAT_OUT BIT(3) +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO2_DAT_OUT BIT(2) +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO1_DAT_OUT BIT(1) +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO0_DAT_OUT BIT(0) + +#define ADS112C14_REG_IDAC_MAG_CFG 0x0D +#define ADS112C14_IDAC_MAG_CFG_I2MAG GENMASK(7, 4) +#define ADS112C14_IDAC_MAG_CFG_I1MAG GENMASK(3, 0) + +#define ADS112C14_REG_IDAC_MUX_CFG 0x0E +#define ADS112C14_IDAC_MUX_CFG_IUNIT BIT(7) +#define ADS112C14_IDAC_MUX_CFG_I2MUX GENMASK(6, 4) +#define ADS112C14_IDAC_MUX_CFG_I1MUX GENMASK(2, 0) + +#define ADS112C14_REG_REG_MAP_CRC 0x0F + +#define ADS112C14_INT_REF0_mV 1250 +#define ADS112C14_INT_REF1_mV 2500 + +struct ads112c14_chip_info { + const char *name; + u8 device_id; + u32 resolution_bits; +}; + +/* Fixed channels for system monitor measurements. */ + +#define ADS112C14_SYS_MON_CHANNEL_BASE 100 + +enum { + ADS112C14_SYS_MON_CHANNEL_TEMP = ADS112C14_SYS_MON_CHANNEL_BASE, + ADS112C14_SYS_MON_CHANNEL_EXT_REF, + ADS112C14_SYS_MON_CHANNEL_AVDD, + ADS112C14_SYS_MON_CHANNEL_DVDD, + ADS112C14_SYS_MON_CHANNEL_SHORT, +}; + +static const struct iio_chan_spec ads112c14_sys_mon_channels[] = { + { + .type = IIO_TEMP, + .indexed = 1, + .channel = ADS112C14_SYS_MON_CHANNEL_TEMP, + .address = 2, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE) + | BIT(IIO_CHAN_INFO_OFFSET), + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = ADS112C14_SYS_MON_CHANNEL_EXT_REF, + .address = 3, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = ADS112C14_SYS_MON_CHANNEL_AVDD, + .address = 4, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = ADS112C14_SYS_MON_CHANNEL_DVDD, + .address = 5, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = ADS112C14_SYS_MON_CHANNEL_SHORT, + .channel2 = ADS112C14_SYS_MON_CHANNEL_SHORT, + .differential = 1, + .address = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +struct ads112c14_data { + const struct ads112c14_chip_info *chip_info; + struct regmap *regmap; +}; + +static bool ads112c14_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADS112C14_REG_DEVICE_ID: + case ADS112C14_REG_REVISION_ID: + case ADS112C14_REG_STATUS_LSB: + return false; + default: + return true; + } +} + +static bool ads112c14_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADS112C14_REG_STATUS_MSB: + case ADS112C14_REG_STATUS_LSB: + case ADS112C14_REG_CONVERSION_CTRL: + return true; + default: + return false; + } +} + +static const struct reg_default ads112c14_reg_defaults[] = { + { ADS112C14_REG_DEVICE_CFG, 0 }, + { ADS112C14_REG_DATA_RATE_CFG, 0 }, + { ADS112C14_REG_MUX_CFG, 0 }, + { ADS112C14_REG_GAIN_CFG, FIELD_PREP_CONST(ADS112C14_GAIN_CFG_GAIN, 1) }, + { ADS112C14_REG_REFERENCE_CFG, 0 }, + { ADS112C14_REG_DIGITAL_CFG, 0 }, + { ADS112C14_REG_GPIO_CFG, 0 }, + { ADS112C14_REG_GPIO_DATA_OUTPUT, 0 }, + { ADS112C14_REG_IDAC_MAG_CFG, 0 }, + { ADS112C14_REG_IDAC_MUX_CFG, FIELD_PREP_CONST(ADS112C14_IDAC_MUX_CFG_I2MUX, 1) }, +}; + +static const struct regmap_config ads112c14_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = ADS112C14_CMD_RREG, + .write_flag_mask = ADS112C14_CMD_WREG, + .max_register = ADS112C14_REG_REG_MAP_CRC, + .writeable_reg = ads112c14_writeable_reg, + .volatile_reg = ads112c14_volatile_reg, + .reg_defaults = ads112c14_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ads112c14_reg_defaults), + .cache_type = REGCACHE_MAPLE, +}; + +static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data, + const struct iio_chan_spec *chan) +{ + int ret; + + /* TODO: GAIN is useful for shorted PGA inputs. */ + /* All SYS_MON channels use GAIN of 1 to keep it simple. */ + ret = regmap_update_bits(data->regmap, ADS112C14_REG_GAIN_CFG, + ADS112C14_GAIN_CFG_SYS_MON | + ADS112C14_GAIN_CFG_GAIN, + FIELD_PREP(ADS112C14_GAIN_CFG_SYS_MON, chan->address) | + FIELD_PREP(ADS112C14_GAIN_CFG_GAIN, 1)); + if (ret) + return ret; + + /* All SYS_MON channels use signed data to keep it simple. */ + ret = regmap_clear_bits(data->regmap, ADS112C14_REG_DIGITAL_CFG, + ADS112C14_DIGITAL_CFG_CODING); + if (ret) + return ret; + + /* + * REVISIT: if we implement regulator support for the REFOUT pin, we + * might need to make this voltage match what is required by that. In + * that case, we could also adjust GAIN so that we still get the same + * range. + */ + /* + * NB: SYS_MON channels ignore REF_SEL except for the shorted input + * channel, so we set it here to internal reference to be consistent. + * If we ever need to make a measurement of shorted input with other + * reference source, we could add additional channels for that. + */ + ret = regmap_update_bits(data->regmap, ADS112C14_REG_REFERENCE_CFG, + ADS112C14_REFERENCE_CFG_REF_VAL | + ADS112C14_REFERENCE_CFG_REF_SEL, + FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_VAL, + ADS112C14_REFERENCE_CFG_REF_VAL_2_5V) | + FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_SEL, + ADS112C14_REFERENCE_CFG_REF_SEL_INTERNAL)); + if (ret) + return ret; + + return 0; +} + +static int ads112c14_single_conversion(struct ads112c14_data *data, + const struct iio_chan_spec *chan, + u8 *buf) +{ + struct i2c_client *client = to_i2c_client(regmap_get_device(data->regmap)); + u32 reg_val; + int ret; + + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + /* Not implemented yet. */ + return -EINVAL; + } else { + ret = ads112c14_prepare_sys_mon_channel(data, chan); + if (ret) + return ret; + } + + ret = regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL, + ADS112C14_CONVERSION_CTRL_START); + if (ret) + return ret; + + ret = regmap_read_poll_timeout(data->regmap, + ADS112C14_REG_STATUS_MSB, reg_val, + FIELD_GET(ADS112C14_STATUS_MSB_DRDY, reg_val), + 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC); + if (ret) + return ret; + + ret = i2c_smbus_read_i2c_block_data(client, ADS112C14_CMD_RDATA, + BITS_TO_BYTES(data->chip_info->resolution_bits), + buf); + if (ret < 0) + return ret; + + return 0; +} + +static int ads112c14_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + u32 vref_uV, fsr_bits; + + /* Selecting V_REF source is not implemented yet. */ + vref_uV = ADS112C14_INT_REF1_mV * (MICRO / MILLI); + + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + /* Not implemented yet. */ + return -EINVAL; + } else { + /* All SYS_MON channels are using signed coding. */ + fsr_bits = data->chip_info->resolution_bits - 1; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + u8 buf[3]; + int ret; + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + ret = ads112c14_single_conversion(data, chan, buf); + if (ret) + return ret; + + switch (data->chip_info->resolution_bits) { + case 16: + *val = get_unaligned_be16(buf); + break; + case 24: + *val = get_unaligned_be24(buf); + break; + default: + return -EINVAL; + } + + *val = sign_extend32(*val, fsr_bits); + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_TEMP) { + /* TS_TC (typical) = 405 uV/°C */ + *val = MILLI * vref_uV / 405; + *val2 = fsr_bits; + return IIO_VAL_FRACTIONAL_LOG2; + } + + *val = vref_uV / (MICRO / MILLI); + + /* + * Some SYS_MON channels (ext ref, AVDD, DVDD) need to be + * multiplied by 8 to account for internal attenuation of / 8. + */ + switch (chan->address) { + case 3 ... 5: + *val2 = fsr_bits - 3; + break; + default: + *val2 = fsr_bits; + break; + } + + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + /* Only the temperature channel has an offset. */ + if (chan->type != IIO_TEMP) + return -EINVAL; + /* + * Die temperature [°C] = 25°C + (Measured voltage – TS_Offset) / TS_TC + * TS_TC (typical) = 405 uV/°C + * TS_Offset (typical) = 119.5 mV + */ + *val = div_s64((s64)(25 * 405 - 119500) * BIT(fsr_bits), vref_uV); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ads112c14_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, char *label) +{ + const char *label_source; + + /* System monitor channels. */ + switch (chan->channel) { + case ADS112C14_SYS_MON_CHANNEL_TEMP: + label_source = "Internal temperature sensor"; + break; + case ADS112C14_SYS_MON_CHANNEL_EXT_REF: + label_source = "External reference"; + break; + case ADS112C14_SYS_MON_CHANNEL_AVDD: + label_source = "AVDD"; + break; + case ADS112C14_SYS_MON_CHANNEL_DVDD: + label_source = "DVDD"; + break; + case ADS112C14_SYS_MON_CHANNEL_SHORT: + label_source = "Internal short (internal reference source)"; + break; + default: + return -EINVAL; + } + + return sysfs_emit(label, "%s\n", label_source); +} + +static const struct iio_info ads112c14_info = { + .read_raw = ads112c14_read_raw, + .read_label = ads112c14_read_label, +}; + +static int ads112c14_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + const struct ads112c14_chip_info *info; + struct iio_dev *indio_dev; + struct ads112c14_data *data; + u32 reg_val; + int ret; + + info = i2c_get_match_data(client); + if (!info) + return dev_err_probe(dev, -ENODEV, "missing match data\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->chip_info = info; + + ret = devm_regulator_get_enable(dev, "dvdd"); + if (ret) + return dev_err_probe(dev, ret, "failed to get dvdd regulator\n"); + + ret = devm_regulator_get_enable(dev, "avdd"); + if (ret) + return dev_err_probe(dev, ret, "failed to get avdd regulator\n"); + + /* It takes some time for the internal reference to stabilize. */ + fsleep(10 * USEC_PER_MSEC); + + data->regmap = devm_regmap_init_i2c(client, &ads112c14_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "failed to init regmap\n"); + + /* + * Write magic reset value (0x16) to ensure known state. The reset may + * cause an error because of failing to get the I2C ACK at the end of + * the message. The device still gets reset so it is safe to ignore the + * return value here. If something else is wrong, later read/write will + * likely have the same error. + */ + regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL, + FIELD_PREP(ADS112C14_CONVERSION_CTRL_RESET, 0x16)); + + fsleep(ADS112C14_DELAY_RESET_US); + + ret = regmap_read(data->regmap, ADS112C14_REG_STATUS_MSB, ®_val); + if (ret) + return ret; + + if (FIELD_GET(ADS112C14_STATUS_MSB_RESETN, reg_val)) + return dev_err_probe(dev, -EIO, "reset failed\n"); + + /* + * Clear reset bit to prepare for next probe. And clear AVDD fault since + * that happens on every reset. + */ + ret = regmap_write(data->regmap, ADS112C14_REG_STATUS_MSB, + ADS112C14_STATUS_MSB_RESETN | + ADS112C14_STATUS_MSB_AVDD_UVN); + if (ret) + return ret; + + ret = regmap_read(data->regmap, ADS112C14_REG_DEVICE_ID, ®_val); + if (ret) + return ret; + + if (FIELD_GET(ADS112C14_DEVICE_ID_BITS, reg_val) != info->device_id) + dev_info(dev, "device ID mismatch, expected 0x%X, got 0x%lX\n", + info->device_id, + FIELD_GET(ADS112C14_DEVICE_ID_BITS, reg_val)); + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG, + ADS112C14_DEVICE_CFG_CONV_MODE, + FIELD_PREP(ADS112C14_DEVICE_CFG_CONV_MODE, + ADS112C14_DEVICE_CFG_CONV_MODE_SINGLE_SHOT)); + if (ret) + return ret; + + indio_dev->name = info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ads112c14_sys_mon_channels; + indio_dev->num_channels = ARRAY_SIZE(ads112c14_sys_mon_channels); + indio_dev->info = &ads112c14_info; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct ads112c14_chip_info ads112c14_chip_info = { + .name = "ads112c14", + .device_id = 0xE, + .resolution_bits = 16, +}; + +static const struct ads112c14_chip_info ads122c14_chip_info = { + .name = "ads122c14", + .device_id = 0xF, + .resolution_bits = 24, +}; + +static const struct of_device_id ads112c14_of_match[] = { + { .compatible = "ti,ads112c14", .data = &ads112c14_chip_info }, + { .compatible = "ti,ads122c14", .data = &ads122c14_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ads112c14_of_match); + +static const struct i2c_device_id ads112c14_id[] = { + { .name = "ads112c14", .driver_data = (kernel_ulong_t)&ads112c14_chip_info }, + { .name = "ads122c14", .driver_data = (kernel_ulong_t)&ads122c14_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ads112c14_id); + +static struct i2c_driver ads112c14_driver = { + .driver = { + .name = "ads112c14", + .of_match_table = ads112c14_of_match, + }, + .probe = ads112c14_probe, + .id_table = ads112c14_id, +}; +module_i2c_driver(ads112c14_driver); + +MODULE_AUTHOR("David Lechner (TI) "); +MODULE_DESCRIPTION("TI ADS112C14 I2C ADC driver"); +MODULE_LICENSE("GPL"); From aa372a9abce320d2f69a25bdbaa5f04c25ee4714 Mon Sep 17 00:00:00 2001 From: "David Lechner (TI)" Date: Tue, 14 Jul 2026 18:21:29 -0500 Subject: [PATCH 316/513] iio: adc: ti-ads112c14: implement gain on internal short SYS_MON channel Implement support for the programmable gain amplifier on the internal short SYS_MON channel. This channel is used for calibration, so it is useful to be able to set the PGA to the same gain as the external channels. The gain setting is implemented via the `_scale` attribute. In the future, we may want to support different reference voltages for this channel, so the scale_available table is populated during probe rather than being a static table. Signed-off-by: David Lechner (TI) Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads112c14.c | 152 ++++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c index bc0fd6839b72..3bf27c30fd00 100644 --- a/drivers/iio/adc/ti-ads112c14.c +++ b/drivers/iio/adc/ti-ads112c14.c @@ -9,12 +9,14 @@ */ #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -128,6 +130,15 @@ #define ADS112C14_INT_REF0_mV 1250 #define ADS112C14_INT_REF1_mV 2500 +/* + * Available gains as tenths (e.g. value 5 == 0.5 gain). Indexes correspond to + * ADS112C14_GAIN_CFG_GAIN values. + */ +static const u32 ads112c14_pga_gains_x10[] = { + 5, 10, 20, 40, 50, 80, 100, 160, /* 0 - 7 */ + 200, 320, 500, 640, 1000, 1280, 2000, 2560, /* 8 - 15 */ +}; + struct ads112c14_chip_info { const char *name; u8 device_id; @@ -189,12 +200,17 @@ static const struct iio_chan_spec ads112c14_sys_mon_channels[] = { .address = 1, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), }, }; struct ads112c14_data { const struct ads112c14_chip_info *chip_info; struct regmap *regmap; + /* Synchronizes access to register value fields. */ + struct mutex lock; + u8 sys_mon_chan_short_gain_val; + int sys_mon_chan_short_scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2]; }; static bool ads112c14_writeable_reg(struct device *dev, unsigned int reg) @@ -250,15 +266,21 @@ static const struct regmap_config ads112c14_regmap_config = { static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data, const struct iio_chan_spec *chan) { + u32 gain_val; int ret; - /* TODO: GAIN is useful for shorted PGA inputs. */ - /* All SYS_MON channels use GAIN of 1 to keep it simple. */ + /* + * All SYS_MON channels use GAIN of 1 to keep it simple. Other than + * the internal short channel, where it is useful in practice. + */ + gain_val = chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT ? + data->sys_mon_chan_short_gain_val : 1; + ret = regmap_update_bits(data->regmap, ADS112C14_REG_GAIN_CFG, ADS112C14_GAIN_CFG_SYS_MON | ADS112C14_GAIN_CFG_GAIN, FIELD_PREP(ADS112C14_GAIN_CFG_SYS_MON, chan->address) | - FIELD_PREP(ADS112C14_GAIN_CFG_GAIN, 1)); + FIELD_PREP(ADS112C14_GAIN_CFG_GAIN, gain_val)); if (ret) return ret; @@ -301,6 +323,8 @@ static int ads112c14_single_conversion(struct ads112c14_data *data, u32 reg_val; int ret; + guard(mutex)(&data->lock); + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { /* Not implemented yet. */ return -EINVAL; @@ -336,6 +360,7 @@ static int ads112c14_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct ads112c14_data *data = iio_priv(indio_dev); + const int *scale_avail; u32 vref_uV, fsr_bits; /* Selecting V_REF source is not implemented yet. */ @@ -385,6 +410,19 @@ static int ads112c14_read_raw(struct iio_dev *indio_dev, return IIO_VAL_FRACTIONAL_LOG2; } + if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) { + u8 idx; + + guard(mutex)(&data->lock); + + idx = data->sys_mon_chan_short_gain_val; + scale_avail = data->sys_mon_chan_short_scale_available[idx]; + *val = scale_avail[0]; + *val2 = scale_avail[1]; + + return IIO_VAL_DECIMAL64_PICO; + } + *val = vref_uV / (MICRO / MILLI); /* @@ -417,6 +455,75 @@ static int ads112c14_read_raw(struct iio_dev *indio_dev, } } +static int ads112c14_read_avail(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, const int **vals, + int *type, int *length, long mask) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) { + guard(mutex)(&data->lock); + + *vals = (const int *)data->sys_mon_chan_short_scale_available; + *length = 2 * ARRAY_SIZE(data->sys_mon_chan_short_scale_available); + *type = IIO_VAL_DECIMAL64_PICO; + return IIO_AVAIL_LIST; + } + + return -EINVAL; + default: + return -EINVAL; + } +} + +static int ads112c14_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + const int (*scale_avail)[2]; + u8 *gain_val; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: { + guard(mutex)(&data->lock); + + if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) { + scale_avail = data->sys_mon_chan_short_scale_available; + gain_val = &data->sys_mon_chan_short_gain_val; + } else { + return -EINVAL; + } + + for (u32 i = 0; i < ARRAY_SIZE(ads112c14_pga_gains_x10); i++) { + if (iio_val_s64_compose(val, val2) == + iio_val_s64_compose(scale_avail[i][0], scale_avail[i][1])) { + *gain_val = i; + return 0; + } + } + + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ads112c14_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_DECIMAL64_PICO; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + static int ads112c14_read_label(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, char *label) { @@ -448,9 +555,39 @@ static int ads112c14_read_label(struct iio_dev *indio_dev, static const struct iio_info ads112c14_info = { .read_raw = ads112c14_read_raw, + .read_avail = ads112c14_read_avail, + .write_raw = ads112c14_write_raw, + .write_raw_get_fmt = ads112c14_write_raw_get_fmt, .read_label = ads112c14_read_label, }; +static void ads112c14_populate_scale_available(int (*scale_avail)[2], + u32 full_scale, u32 fsr_bits) +{ + for (u32 i = 0; i < ARRAY_SIZE(ads112c14_pga_gains_x10); i++) { + u64 gain_x10 = ads112c14_pga_gains_x10[i]; + s64 scale; + + scale = div64_u64((u64)PICO * 10U * full_scale, + gain_x10 * BIT(fsr_bits)); + + iio_val_s64_decompose(scale, &scale_avail[i][0], + &scale_avail[i][1]); + } +} + +static void ads112c14_populate_tables(struct ads112c14_data *data) +{ + u32 full_scale, fsr_bits; + + /* For now, assuming all sys_mon channels are using 2.5V reference. */ + full_scale = ADS112C14_INT_REF1_mV; + fsr_bits = data->chip_info->resolution_bits - 1; + + ads112c14_populate_scale_available(data->sys_mon_chan_short_scale_available, + full_scale, fsr_bits); +} + static int ads112c14_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -471,6 +608,10 @@ static int ads112c14_probe(struct i2c_client *client) data = iio_priv(indio_dev); data->chip_info = info; + ret = devm_mutex_init(dev, &data->lock); + if (ret) + return ret; + ret = devm_regulator_get_enable(dev, "dvdd"); if (ret) return dev_err_probe(dev, ret, "failed to get dvdd regulator\n"); @@ -506,6 +647,9 @@ static int ads112c14_probe(struct i2c_client *client) if (FIELD_GET(ADS112C14_STATUS_MSB_RESETN, reg_val)) return dev_err_probe(dev, -EIO, "reset failed\n"); + /* Default gain after reset is 1. */ + data->sys_mon_chan_short_gain_val = 1; + /* * Clear reset bit to prepare for next probe. And clear AVDD fault since * that happens on every reset. @@ -532,6 +676,8 @@ static int ads112c14_probe(struct i2c_client *client) if (ret) return ret; + ads112c14_populate_tables(data); + indio_dev->name = info->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = ads112c14_sys_mon_channels; From 36c12dbda81c284d72f3c64689461647497b643b Mon Sep 17 00:00:00 2001 From: "David Lechner (TI)" Date: Tue, 14 Jul 2026 18:21:30 -0500 Subject: [PATCH 317/513] iio: adc: ti-ads112c14: add measurement channel support Add support for parsing devicetree properties for measurement channels and doing direct reads on these. There are quite a lot of conditions that have to be met for each measurement to be made, so quite a bit of state and algorithms are required to handle it. Channels are created dynamically since the number of possibilities is unreasonably large. Signed-off-by: David Lechner (TI) Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads112c14.c | 504 ++++++++++++++++++++++++++++++++- 1 file changed, 493 insertions(+), 11 deletions(-) diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c index 3bf27c30fd00..5ba21bcc4994 100644 --- a/drivers/iio/adc/ti-ads112c14.c +++ b/drivers/iio/adc/ti-ads112c14.c @@ -12,9 +12,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -77,6 +79,7 @@ #define ADS112C14_REG_MUX_CFG 0x07 #define ADS112C14_MUX_CFG_AINP GENMASK(7, 4) #define ADS112C14_MUX_CFG_AINN GENMASK(3, 0) +#define ADS112C14_MUX_CFG_AIN_GND 8 #define ADS112C14_REG_GAIN_CFG 0x08 #define ADS112C14_GAIN_CFG_SPARE BIT(7) @@ -130,6 +133,20 @@ #define ADS112C14_INT_REF0_mV 1250 #define ADS112C14_INT_REF1_mV 2500 +enum { + ADS112C14_VREF_SOURCE_INTERNAL_2_5V, + ADS112C14_VREF_SOURCE_INTERNAL_1_25V, + ADS112C14_VREF_SOURCE_EXTERNAL, + ADS112C14_VREF_SOURCE_AVDD, +}; + +static const char * const ads112c14_vref_source_names[] = { + [ADS112C14_VREF_SOURCE_INTERNAL_2_5V] = "internal-2.5v", + [ADS112C14_VREF_SOURCE_INTERNAL_1_25V] = "internal-1.25v", + [ADS112C14_VREF_SOURCE_EXTERNAL] = "external", + [ADS112C14_VREF_SOURCE_AVDD] = "avdd", +}; + /* * Available gains as tenths (e.g. value 5 == 0.5 gain). Indexes correspond to * ADS112C14_GAIN_CFG_GAIN values. @@ -204,11 +221,33 @@ static const struct iio_chan_spec ads112c14_sys_mon_channels[] = { }, }; +struct ads112c14_measurement { + const char *label; + u32 vref_source; + u8 iunit; + u8 idac1_mag; + u8 idac2_mag; + u8 idac1_mux; + u8 idac2_mux; + u8 iadc_count; + u8 gain_val; + bool global_chop; + bool bipolar; + int scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2]; +}; + struct ads112c14_data { const struct ads112c14_chip_info *chip_info; struct regmap *regmap; /* Synchronizes access to register value fields. */ struct mutex lock; + u32 avdd_uV; + u32 ext_ref_uV; + bool refp_is_avdd; + bool refn_is_gnd; + u32 ext_ref_ohms; + struct ads112c14_measurement *measurements; + u32 num_measurements; u8 sys_mon_chan_short_gain_val; int sys_mon_chan_short_scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2]; }; @@ -263,12 +302,112 @@ static const struct regmap_config ads112c14_regmap_config = { .cache_type = REGCACHE_MAPLE, }; +static int ads112c14_prepare_measurement_channel(struct ads112c14_data *data, + const struct iio_chan_spec *chan) +{ + struct ads112c14_measurement *measurement = &data->measurements[chan->scan_index]; + u32 refp_buf_en, refn_buf_en, ref_val, ref_sel; + int ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_MUX_CFG, + ADS112C14_MUX_CFG_AINP | ADS112C14_MUX_CFG_AINN, + FIELD_PREP(ADS112C14_MUX_CFG_AINP, chan->channel) | + FIELD_PREP(ADS112C14_MUX_CFG_AINN, chan->channel2)); + if (ret) + return ret; + + ret = regmap_assign_bits(data->regmap, ADS112C14_REG_DIGITAL_CFG, + ADS112C14_DIGITAL_CFG_CODING, + !measurement->bipolar); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_GAIN_CFG, + ADS112C14_GAIN_CFG_SYS_MON | + ADS112C14_GAIN_CFG_GAIN, + FIELD_PREP(ADS112C14_GAIN_CFG_SYS_MON, 0) | + FIELD_PREP(ADS112C14_GAIN_CFG_GAIN, + measurement->gain_val)); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_IDAC_MAG_CFG, + ADS112C14_IDAC_MAG_CFG_I2MAG | + ADS112C14_IDAC_MAG_CFG_I1MAG, + FIELD_PREP(ADS112C14_IDAC_MAG_CFG_I2MAG, + measurement->idac2_mag) | + FIELD_PREP(ADS112C14_IDAC_MAG_CFG_I1MAG, + measurement->idac1_mag)); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_IDAC_MUX_CFG, + ADS112C14_IDAC_MUX_CFG_IUNIT | + ADS112C14_IDAC_MUX_CFG_I2MUX | + ADS112C14_IDAC_MUX_CFG_I1MUX, + FIELD_PREP(ADS112C14_IDAC_MUX_CFG_IUNIT, + measurement->iunit) | + FIELD_PREP(ADS112C14_IDAC_MUX_CFG_I2MUX, + measurement->idac2_mux) | + FIELD_PREP(ADS112C14_IDAC_MUX_CFG_I1MUX, + measurement->idac1_mux)); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_DATA_RATE_CFG, + ADS112C14_DATA_RATE_CFG_GC_EN, + FIELD_PREP(ADS112C14_DATA_RATE_CFG_GC_EN, + measurement->global_chop)); + if (ret) + return ret; + + refp_buf_en = !data->refp_is_avdd && + measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL; + refn_buf_en = !data->refn_is_gnd && + measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL; + + ref_val = measurement->vref_source == ADS112C14_VREF_SOURCE_INTERNAL_2_5V ? + ADS112C14_REFERENCE_CFG_REF_VAL_2_5V : + ADS112C14_REFERENCE_CFG_REF_VAL_1_25V; + + switch (measurement->vref_source) { + case ADS112C14_VREF_SOURCE_AVDD: + ref_sel = ADS112C14_REFERENCE_CFG_REF_SEL_AVDD; + break; + case ADS112C14_VREF_SOURCE_EXTERNAL: + ref_sel = ADS112C14_REFERENCE_CFG_REF_SEL_EXTERNAL; + break; + default: + ref_sel = ADS112C14_REFERENCE_CFG_REF_SEL_INTERNAL; + break; + } + + return regmap_update_bits(data->regmap, ADS112C14_REG_REFERENCE_CFG, + ADS112C14_REFERENCE_CFG_REFP_BUF_EN | + ADS112C14_REFERENCE_CFG_REFN_BUF_EN | + ADS112C14_REFERENCE_CFG_REF_VAL | + ADS112C14_REFERENCE_CFG_REF_SEL, + FIELD_PREP(ADS112C14_REFERENCE_CFG_REFP_BUF_EN, + refp_buf_en) | + FIELD_PREP(ADS112C14_REFERENCE_CFG_REFN_BUF_EN, + refn_buf_en) | + FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_VAL, + ref_val) | + FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_SEL, + ref_sel)); +} + static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data, const struct iio_chan_spec *chan) { u32 gain_val; int ret; + /* + * NB: IDAC registers are left as-is in case they are generating current + * needed for the external reference measurement. + */ + /* * All SYS_MON channels use GAIN of 1 to keep it simple. Other than * the internal short channel, where it is useful in practice. @@ -326,8 +465,9 @@ static int ads112c14_single_conversion(struct ads112c14_data *data, guard(mutex)(&data->lock); if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { - /* Not implemented yet. */ - return -EINVAL; + ret = ads112c14_prepare_measurement_channel(data, chan); + if (ret) + return ret; } else { ret = ads112c14_prepare_sys_mon_channel(data, chan); if (ret) @@ -360,6 +500,7 @@ static int ads112c14_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct ads112c14_data *data = iio_priv(indio_dev); + struct ads112c14_measurement *measurement = NULL; const int *scale_avail; u32 vref_uV, fsr_bits; @@ -367,8 +508,8 @@ static int ads112c14_read_raw(struct iio_dev *indio_dev, vref_uV = ADS112C14_INT_REF1_mV * (MICRO / MILLI); if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { - /* Not implemented yet. */ - return -EINVAL; + measurement = &data->measurements[chan->scan_index]; + fsr_bits = data->chip_info->resolution_bits - measurement->bipolar; } else { /* All SYS_MON channels are using signed coding. */ fsr_bits = data->chip_info->resolution_bits - 1; @@ -398,7 +539,8 @@ static int ads112c14_read_raw(struct iio_dev *indio_dev, return -EINVAL; } - *val = sign_extend32(*val, fsr_bits); + if (!measurement || measurement->bipolar) + *val = sign_extend32(*val, fsr_bits); return IIO_VAL_INT; } @@ -410,6 +552,16 @@ static int ads112c14_read_raw(struct iio_dev *indio_dev, return IIO_VAL_FRACTIONAL_LOG2; } + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + guard(mutex)(&data->lock); + + scale_avail = measurement->scale_available[measurement->gain_val]; + *val = scale_avail[0]; + *val2 = scale_avail[1]; + + return IIO_VAL_DECIMAL64_PICO; + } + if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) { u8 idx; @@ -463,6 +615,18 @@ static int ads112c14_read_avail(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_SCALE: + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + struct ads112c14_measurement *measurement; + + guard(mutex)(&data->lock); + + measurement = &data->measurements[chan->scan_index]; + *vals = (const int *)measurement->scale_available; + *length = 2 * ARRAY_SIZE(measurement->scale_available); + *type = IIO_VAL_DECIMAL64_PICO; + return IIO_AVAIL_LIST; + } + if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) { guard(mutex)(&data->lock); @@ -490,7 +654,13 @@ static int ads112c14_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: { guard(mutex)(&data->lock); - if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) { + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + struct ads112c14_measurement *measurement; + + measurement = &data->measurements[chan->scan_index]; + scale_avail = measurement->scale_available; + gain_val = &measurement->gain_val; + } else if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) { scale_avail = data->sys_mon_chan_short_scale_available; gain_val = &data->sys_mon_chan_short_gain_val; } else { @@ -527,8 +697,20 @@ static int ads112c14_write_raw_get_fmt(struct iio_dev *indio_dev, static int ads112c14_read_label(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, char *label) { + struct ads112c14_data *data = iio_priv(indio_dev); const char *label_source; + /* measurement channels */ + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + struct ads112c14_measurement *measurement; + + measurement = &data->measurements[chan->scan_index]; + if (!measurement->label) + return -EINVAL; + + return sysfs_emit(label, "%s\n", measurement->label); + } + /* System monitor channels. */ switch (chan->channel) { case ADS112C14_SYS_MON_CHANNEL_TEMP: @@ -561,6 +743,215 @@ static const struct iio_info ads112c14_info = { .read_label = ads112c14_read_label, }; +static int ads112c14_populate_idac_mag(u32 current_nA, u8 *idac_mag) +{ + u32 current_uA = current_nA / (NANO / MICRO); + + /* Convert microamps to IMAG bits */ + if (current_uA == 1) + *idac_mag = 1; + else if (in_range(current_uA, 10, 100) && current_uA % 10 == 0) + *idac_mag = current_uA / 10 + 1; + else + return dev_err_probe(NULL, -EINVAL, + "invalid excitation-current-nanoamp value\n"); + + return 0; +} + +static int ads112c14_parse_channels(struct iio_dev *indio_dev, + bool *need_avdd_ref, bool *need_ext_ref) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + struct iio_chan_spec *channels; + u32 num_child_nodes, i, pair[2]; + int ret; + + *need_avdd_ref = false; + *need_ext_ref = false; + + num_child_nodes = device_get_named_child_node_count(dev, "channel"); + + data->measurements = devm_kcalloc(dev, num_child_nodes, + sizeof(*data->measurements), GFP_KERNEL); + if (!data->measurements) + return -ENOMEM; + + channels = devm_kcalloc(dev, num_child_nodes + + ARRAY_SIZE(ads112c14_sys_mon_channels), + sizeof(*channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + i = 0; + device_for_each_named_child_node_scoped(dev, child, "channel") { + struct ads112c14_measurement *measurement = &data->measurements[i]; + struct iio_chan_spec *spec = &channels[i]; + + spec->indexed = 1; + spec->scan_index = i; + measurement->gain_val = 1; + + if (fwnode_property_present(child, "label")) { + ret = fwnode_property_read_string(child, "label", &measurement->label); + if (ret) + return dev_err_probe(dev, ret, + "failed to read label property\n"); + } + + if (fwnode_property_present(child, "single-channel")) { + ret = fwnode_property_read_u32(child, "single-channel", + &pair[0]); + if (ret) + return dev_err_probe(dev, ret, + "failed to read single-channel property\n"); + + if (pair[0] >= 8) + return dev_err_probe(dev, -EINVAL, + "single-channel value must be between 0 and 7\n"); + + spec->channel = pair[0]; + /* + * NB: channel2 is unused by iio core code in this case. + * Let's us avoid special case for negative input mux + * for single-ended channels when taking measurements. + */ + spec->channel2 = ADS112C14_MUX_CFG_AIN_GND; + } else if (fwnode_property_present(child, "diff-channels")) { + ret = fwnode_property_read_u32_array(child, "diff-channels", + pair, ARRAY_SIZE(pair)); + if (ret) + return dev_err_probe(dev, ret, + "failed to read diff-channels property\n"); + + if (pair[0] >= 8 || pair[1] >= 8) + return dev_err_probe(dev, -EINVAL, + "diff-channels values must be between 0 and 7\n"); + + spec->differential = 1; + spec->channel = pair[0]; + spec->channel2 = pair[1]; + } else { + return dev_err_probe(dev, -EINVAL, + "channel node missing channel type property\n"); + } + + if (fwnode_property_present(child, "excitation-channels")) { + ret = fwnode_property_count_u32(child, "excitation-channels"); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to read excitation-channels property\n"); + + if (ret < 1 || ret > 2) + return dev_err_probe(dev, -EINVAL, + "excitation-channels property must have 1 or 2 values\n"); + + measurement->iadc_count = ret; + pair[1] = 0; + + ret = fwnode_property_read_u32_array(child, "excitation-channels", + pair, measurement->iadc_count); + if (ret) + return dev_err_probe(dev, ret, + "failed to read excitation-channels property\n"); + + if (pair[0] >= 8 || pair[1] >= 8) + return dev_err_probe(dev, -EINVAL, + "excitation-channels values must be between 0 and 7\n"); + + measurement->idac1_mux = pair[0]; + measurement->idac2_mux = measurement->iadc_count > 1 ? pair[1] : 0; + + ret = fwnode_property_read_u32_array(child, "excitation-current-nanoamp", + pair, measurement->iadc_count); + if (ret) + return dev_err_probe(dev, ret, + "failed to read excitation-current-nanoamp property\n"); + + if (pair[0] <= 100 * (NANO / MICRO) && + (measurement->iadc_count == 1 || pair[1] <= 100 * (NANO / MICRO))) { + /* + * If both values are 100µA or less, then we can + * use IUNIT = 1µA for better precision. + */ + ret = ads112c14_populate_idac_mag(pair[0], + &measurement->idac1_mag); + if (ret) + return ret; + + if (measurement->iadc_count > 1) { + ret = ads112c14_populate_idac_mag(pair[1], + &measurement->idac2_mag); + if (ret) + return ret; + } + } else { + /* + * Otherwise, IUINT is 10µA (flag set) and so + * IxMAG is 1/10 of the actual current. + */ + measurement->iunit = 1; + + ret = ads112c14_populate_idac_mag(pair[0] / 10, + &measurement->idac1_mag); + if (ret) + return ret; + + if (measurement->iadc_count > 1) { + ret = ads112c14_populate_idac_mag(pair[1] / 10, + &measurement->idac2_mag); + if (ret) + return ret; + } + } + } + + measurement->bipolar = fwnode_property_read_bool(child, "bipolar"); + measurement->global_chop = fwnode_property_read_bool(child, + "input-chopping"); + + if (fwnode_property_present(child, "reference-sources")) { + ret = fwnode_property_match_property_string(child, + "reference-sources", ads112c14_vref_source_names, + ARRAY_SIZE(ads112c14_vref_source_names)); + if (ret < 0) + return dev_err_probe(dev, ret, + "invalid reference-sources value\n"); + + measurement->vref_source = ret; + } + + if (measurement->vref_source == ADS112C14_VREF_SOURCE_AVDD) + *need_avdd_ref = true; + if (measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL) + *need_ext_ref = true; + + spec->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE); + spec->info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE); + + /* + * If reference source is resistor rather than voltage supply, + * then the measurement is effectively a resistance measurement. + */ + spec->type = (measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL && + data->ext_ref_ohms) ? IIO_RESISTANCE : IIO_VOLTAGE; + + if (spec->type == IIO_RESISTANCE) + spec->differential = 0; + + i++; + } + + data->num_measurements = i; + memcpy(channels + i, ads112c14_sys_mon_channels, sizeof(ads112c14_sys_mon_channels)); + + indio_dev->channels = channels; + indio_dev->num_channels = i + ARRAY_SIZE(ads112c14_sys_mon_channels); + + return 0; +} + static void ads112c14_populate_scale_available(int (*scale_avail)[2], u32 full_scale, u32 fsr_bits) { @@ -580,6 +971,33 @@ static void ads112c14_populate_tables(struct ads112c14_data *data) { u32 full_scale, fsr_bits; + for (u32 i = 0; i < data->num_measurements; i++) { + struct ads112c14_measurement *measurement = &data->measurements[i]; + + switch (measurement->vref_source) { + case ADS112C14_VREF_SOURCE_EXTERNAL: + if (data->ext_ref_ohms) + full_scale = data->ext_ref_ohms; + else + full_scale = data->ext_ref_uV / (MICRO / MILLI); + break; + case ADS112C14_VREF_SOURCE_AVDD: + full_scale = data->avdd_uV / (MICRO / MILLI); + break; + case ADS112C14_VREF_SOURCE_INTERNAL_1_25V: + full_scale = ADS112C14_INT_REF0_mV; + break; + default: + full_scale = ADS112C14_INT_REF1_mV; + break; + } + + fsr_bits = data->chip_info->resolution_bits - measurement->bipolar; + + ads112c14_populate_scale_available(measurement->scale_available, + full_scale, fsr_bits); + } + /* For now, assuming all sys_mon channels are using 2.5V reference. */ full_scale = ADS112C14_INT_REF1_mV; fsr_bits = data->chip_info->resolution_bits - 1; @@ -594,6 +1012,9 @@ static int ads112c14_probe(struct i2c_client *client) const struct ads112c14_chip_info *info; struct iio_dev *indio_dev; struct ads112c14_data *data; + bool need_avdd_ref, need_ext_ref; + u32 refp_uV = 0; + u32 refn_uV = 0; u32 reg_val; int ret; @@ -612,13 +1033,76 @@ static int ads112c14_probe(struct i2c_client *client) if (ret) return ret; + if (device_property_present(dev, "ti,refp-refn-resistor-ohms")) { + ret = device_property_read_u32(dev, "ti,refp-refn-resistor-ohms", + &data->ext_ref_ohms); + if (ret) + return dev_err_probe(dev, ret, + "failed to read ti,refp-refn-resistor-ohms property\n"); + } + + ret = ads112c14_parse_channels(indio_dev, &need_avdd_ref, &need_ext_ref); + if (ret) + return ret; + ret = devm_regulator_get_enable(dev, "dvdd"); if (ret) return dev_err_probe(dev, ret, "failed to get dvdd regulator\n"); - ret = devm_regulator_get_enable(dev, "avdd"); - if (ret) - return dev_err_probe(dev, ret, "failed to get avdd regulator\n"); + if (need_avdd_ref) { + ret = devm_regulator_get_enable_read_voltage(dev, "avdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get avdd voltage\n"); + + data->avdd_uV = ret; + } else { + ret = devm_regulator_get_enable(dev, "avdd"); + if (ret) + return dev_err_probe(dev, ret, "failed to get avdd regulator\n"); + } + + if (device_property_present(dev, "refp-supply")) { + ret = devm_regulator_get_enable_read_voltage(dev, "refp"); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get refp voltage\n"); + + refp_uV = ret; + + struct fwnode_handle *refp_fwnode __free(fwnode_handle) = + fwnode_find_reference(dev->fwnode, "refp-supply", 0); + if (IS_ERR(refp_fwnode)) + return dev_err_probe(dev, PTR_ERR(refp_fwnode), + "failed to get refp fwnode\n"); + + struct fwnode_handle *avdd_fwnode __free(fwnode_handle) = + fwnode_find_reference(dev->fwnode, "avdd-supply", 0); + if (IS_ERR(avdd_fwnode)) + return dev_err_probe(dev, PTR_ERR(avdd_fwnode), + "failed to get avdd fwnode\n"); + + /* REFP buffer should not be enabled when connected to AVDD */ + data->refp_is_avdd = refp_fwnode == avdd_fwnode; + } + + if (device_property_present(dev, "refn-supply")) { + ret = devm_regulator_get_enable_read_voltage(dev, "refn"); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get refn voltage\n"); + + refn_uV = ret; + } else { + data->refn_is_gnd = true; + } + + data->ext_ref_uV = refp_uV - refn_uV; + + if (data->ext_ref_uV && data->ext_ref_ohms) + return dev_err_probe(dev, -EINVAL, + "ti,refp-refn-resistor-ohms property should not be present when refp-supply or refn-supply is present\n"); + + if (need_ext_ref && !data->ext_ref_uV && !data->ext_ref_ohms) + return dev_err_probe(dev, -EINVAL, + "external reference measurements require either refp-supply or ti,refp-refn-resistor-ohms property\n"); /* It takes some time for the internal reference to stabilize. */ fsleep(10 * USEC_PER_MSEC); @@ -680,8 +1164,6 @@ static int ads112c14_probe(struct i2c_client *client) indio_dev->name = info->name; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = ads112c14_sys_mon_channels; - indio_dev->num_channels = ARRAY_SIZE(ads112c14_sys_mon_channels); indio_dev->info = &ads112c14_info; return devm_iio_device_register(dev, indio_dev); From 1ddd64c456d0776ba025adc5d4cefe4b513f5249 Mon Sep 17 00:00:00 2001 From: "David Lechner (TI)" Date: Tue, 14 Jul 2026 19:19:30 -0500 Subject: [PATCH 318/513] iio: adc: ti-ads112c14: add debugfs register access Add debugfs register access to the ads112c14 driver. This is a complex chip and being able to poke registers is useful for debugging and diagnostic/calibration purposes. Signed-off-by: David Lechner (TI) Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads112c14.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c index 5ba21bcc4994..3035d45b64b0 100644 --- a/drivers/iio/adc/ti-ads112c14.c +++ b/drivers/iio/adc/ti-ads112c14.c @@ -694,6 +694,19 @@ static int ads112c14_write_raw_get_fmt(struct iio_dev *indio_dev, } } +static int ads112c14_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + + if (readval) + return regmap_read(data->regmap, reg, readval); + + return regmap_write(data->regmap, reg, writeval); +} + static int ads112c14_read_label(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, char *label) { @@ -740,6 +753,7 @@ static const struct iio_info ads112c14_info = { .read_avail = ads112c14_read_avail, .write_raw = ads112c14_write_raw, .write_raw_get_fmt = ads112c14_write_raw_get_fmt, + .debugfs_reg_access = ads112c14_debugfs_reg_access, .read_label = ads112c14_read_label, }; From bdba14f6122cb234a22284d2716941d55f499091 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 14 Jul 2026 13:16:32 -0600 Subject: [PATCH 319/513] dt-bindings: iio: adc: rockchip-saradc: Add RV1106 compatible Add the compatible for the SARADC of the Rockchip RV1106, which is compatible with the RK3588 variant. Signed-off-by: Simon Glass Reviewed-by: Heiko Stuebner Acked-by: Krzysztof Kozlowski Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/rockchip-saradc.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.yaml b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.yaml index 6769d679c907..f0551d665916 100644 --- a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.yaml @@ -22,7 +22,9 @@ properties: - const: rockchip,rk3562-saradc - const: rockchip,rk3588-saradc - items: - - const: rockchip,rk3576-saradc + - enum: + - rockchip,rk3576-saradc + - rockchip,rv1106-saradc - const: rockchip,rk3588-saradc - items: - enum: From 091e7a67a908354a01914cc3000b9be97f989121 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 14 Jul 2026 17:57:39 +0100 Subject: [PATCH 320/513] iio: light: vcnl4000: make read-only const array regulator_names static Don't populate the read-only const array regulator_names on the stack at run time, instead make it static const char * const Signed-off-by: Colin Ian King Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 128ae3f94074..b8ac156ad5c9 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -1922,7 +1922,7 @@ static void vcnl4000_cleanup(void *data) static int vcnl4000_probe(struct i2c_client *client) { - const char * const regulator_names[] = { "vdd", "vio", "vled" }; + static const char * const regulator_names[] = { "vdd", "vio", "vled" }; struct device *dev = &client->dev; struct vcnl4000_data *data; struct iio_dev *indio_dev; From 1c286917a27aa72e4486eca79e0cdab8d67717b3 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 14 Jul 2026 17:50:12 +0100 Subject: [PATCH 321/513] iio: adc: make read-only const array config static Don't populate the read-only const array config on the stack at run time, instead make it static. Signed-off-by: Colin Ian King Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/88pm886-gpadc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/88pm886-gpadc.c b/drivers/iio/adc/88pm886-gpadc.c index ff9bc5f06c18..938ba43c2c35 100644 --- a/drivers/iio/adc/88pm886-gpadc.c +++ b/drivers/iio/adc/88pm886-gpadc.c @@ -279,7 +279,7 @@ static int pm886_gpadc_read_raw(struct iio_dev *iio, struct iio_chan_spec const static int pm886_gpadc_hw_enable(struct regmap *map) { - const u8 config[] = { + static const u8 config[] = { PM886_GPADC_CONFIG1_EN_ALL, PM886_GPADC_CONFIG2_EN_ALL, PM886_GPADC_GND_DET2_EN, From 176790d0d6e78026eb350de8a5943b289db82bd0 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 14 Jul 2026 17:36:48 +0200 Subject: [PATCH 322/513] iio: light: opt3001: split opt3001_get_processed() logic Split the logic inside the opt3001_get_processed() function, as the current flow is hard to read, mixing IRQ and non-IRQ code blocks. Separate the IRQ code path into its own function, same for the non-IRQ path. Suggested-by: Jonathan Cameron Signed-off-by: Joshua Crofts Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt3001.c | 192 ++++++++++++++++++++---------------- 1 file changed, 106 insertions(+), 86 deletions(-) diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 434ef7e034fe..a948353014ac 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -315,35 +315,12 @@ static const struct iio_chan_spec opt3002_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(1), }; -static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) +static int opt3001_start_conversion(struct opt3001 *opt) { struct i2c_client *client = opt->client; struct device *dev = &client->dev; - int ret; - u16 mantissa; u16 reg; - u8 exponent; - u16 value; - long timeout; - - if (opt->use_irq) { - /* - * Enable the end-of-conversion interrupt mechanism. Note that - * doing so will overwrite the low-level limit value however we - * will restore this value later on. - */ - ret = i2c_smbus_write_word_swapped(client, - OPT3001_LOW_LIMIT, - OPT3001_LOW_LIMIT_EOC_ENABLE); - if (ret < 0) { - dev_err(dev, "failed to write register %02x\n", - OPT3001_LOW_LIMIT); - return ret; - } - - /* Allow IRQ to access the device despite lock being set */ - opt->ok_to_ignore_lock = true; - } + int ret; /* Reset data-ready indicator flag */ opt->result_ready = false; @@ -353,85 +330,128 @@ static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) if (ret < 0) { dev_err(dev, "failed to read register %02x\n", OPT3001_CONFIGURATION); - goto err; + return ret; } reg = ret; opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SINGLE); ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); - if (ret < 0) { + if (ret) dev_err(dev, "failed to write register %02x\n", OPT3001_CONFIGURATION); + + return ret; +} + +static int opt3001_get_processed_irq(struct opt3001 *opt) +{ + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; + u16 value; + int ret; + + /* + * Enable the end-of-conversion interrupt mechanism. Note that doing so + * will overwrite the low-level limit value however we will restore this + * value later on. + */ + ret = i2c_smbus_write_word_swapped(client, + OPT3001_LOW_LIMIT, + OPT3001_LOW_LIMIT_EOC_ENABLE); + if (ret < 0) { + dev_err(dev, "failed to write register %02x\n", + OPT3001_LOW_LIMIT); + return ret; + } + + /* Allow IRQ to access the device despite lock being set */ + opt->ok_to_ignore_lock = true; + + ret = opt3001_start_conversion(opt); + if (ret) goto err; - } - if (opt->use_irq) { - /* Wait for the IRQ to indicate the conversion is complete */ - ret = wait_event_timeout(opt->result_ready_queue, - opt->result_ready, - msecs_to_jiffies(OPT3001_RESULT_READY_LONG)); - if (ret == 0) { - ret = -ETIMEDOUT; - goto err; - } - } else { - /* Sleep for result ready time */ - timeout = (opt->int_time == OPT3001_INT_TIME_SHORT) ? - OPT3001_RESULT_READY_SHORT : OPT3001_RESULT_READY_LONG; - msleep(timeout); - - /* Check result ready flag */ - ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); - if (ret < 0) { - dev_err(dev, "failed to read register %02x\n", - OPT3001_CONFIGURATION); - goto err; - } - - if (!(ret & OPT3001_CONFIGURATION_CRF)) { - ret = -ETIMEDOUT; - goto err; - } - - /* Obtain value */ - ret = i2c_smbus_read_word_swapped(client, OPT3001_RESULT); - if (ret < 0) { - dev_err(dev, "failed to read register %02x\n", - OPT3001_RESULT); - goto err; - } - opt->result = ret; - opt->result_ready = true; - } + if (wait_event_timeout(opt->result_ready_queue, opt->result_ready, + msecs_to_jiffies(OPT3001_RESULT_READY_LONG))) + ret = 0; + else + ret = -ETIMEDOUT; err: - if (opt->use_irq) - /* Disallow IRQ to access the device while lock is active */ - opt->ok_to_ignore_lock = false; + opt->ok_to_ignore_lock = false; if (ret < 0) return ret; - if (opt->use_irq) { - /* - * Disable the end-of-conversion interrupt mechanism by - * restoring the low-level limit value (clearing - * OPT3001_LOW_LIMIT_EOC_ENABLE). Note that selectively clearing - * those enable bits would affect the actual limit value due to - * bit-overlap and therefore can't be done. - */ - value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa; - ret = i2c_smbus_write_word_swapped(client, - OPT3001_LOW_LIMIT, - value); - if (ret < 0) { - dev_err(dev, "failed to write register %02x\n", - OPT3001_LOW_LIMIT); - return ret; - } + /* + * Disable the end-of-conversion interrupt mechanism by restoring the + * low-level limit value (clearing OPT3001_LOW_LIMIT_EOC_ENABLE). Note + * that selectively clearing those enable bits would affect the actual + * limit value due to bit-overlap and therefore can't be done. + */ + value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa; + ret = i2c_smbus_write_word_swapped(client, OPT3001_LOW_LIMIT, value); + if (ret) + dev_err(dev, "failed to write register %02x\n", + OPT3001_LOW_LIMIT); + + return ret; +} + +static int opt3001_get_processed_noirq(struct opt3001 *opt) +{ + struct i2c_client *client = opt->client; + struct device *dev = &client->dev; + int ret; + + ret = opt3001_start_conversion(opt); + if (ret) + return ret; + + if (opt->int_time == OPT3001_INT_TIME_SHORT) + msleep(OPT3001_RESULT_READY_SHORT); + else + msleep(OPT3001_RESULT_READY_LONG); + + /* Check result ready flag */ + ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); + if (ret < 0) { + dev_err(dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + return ret; } + if (!(ret & OPT3001_CONFIGURATION_CRF)) + return -ETIMEDOUT; + + /* Obtain value */ + ret = i2c_smbus_read_word_swapped(client, OPT3001_RESULT); + if (ret < 0) { + dev_err(dev, "failed to read register %02x\n", + OPT3001_RESULT); + return ret; + } + + opt->result = ret; + opt->result_ready = true; + + return 0; +} + +static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) +{ + u16 mantissa; + u8 exponent; + int ret; + + if (opt->use_irq) + ret = opt3001_get_processed_irq(opt); + else + ret = opt3001_get_processed_noirq(opt); + if (ret) + return ret; + exponent = OPT3001_REG_EXPONENT(opt->result); mantissa = OPT3001_REG_MANTISSA(opt->result); From c96fc14322cee45cdf751cb4ae62c9eab25bc643 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 7 Jun 2026 22:05:42 -0700 Subject: [PATCH 323/513] interconnect: qcom: add COMPILE_TEST Allow non-ARM to build this. Signed-off-by: Rosen Penev Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260608050542.6167-1-rosenp@gmail.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 5b8a094ca4ed..e6a65ca611a0 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config INTERCONNECT_QCOM tristate "Qualcomm Network-on-Chip interconnect drivers" - depends on ARCH_QCOM + depends on ARCH_QCOM || COMPILE_TEST help Support for Qualcomm's Network-on-Chip interconnect hardware. From bd8a131fbe4989b8e57aa74fdc974900460ac387 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 9 Jun 2026 15:24:54 -0700 Subject: [PATCH 324/513] interconnect: qcom: simplify allocation Use a flexible array member to reduce allocation by 1. Add __counted_by for extra runtime analysis. Move counting variable assignment after allocation before any array access. Signed-off-by: Rosen Penev Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260609222454.37352-1-rosenp@gmail.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 7 ++----- drivers/interconnect/qcom/icc-rpm.h | 8 ++++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index 23a1d116e79a..ca4c8a944755 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -479,13 +479,11 @@ int qnoc_probe(struct platform_device *pdev) cd_num = 0; } - qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL); + qp = devm_kzalloc(dev, struct_size(qp, intf_clks, cd_num), GFP_KERNEL); if (!qp) return -ENOMEM; - qp->intf_clks = devm_kcalloc(dev, cd_num, sizeof(*qp->intf_clks), GFP_KERNEL); - if (!qp->intf_clks) - return -ENOMEM; + qp->num_intf_clks = cd_num; if (desc->bus_clk_desc) { qp->bus_clk_desc = devm_kzalloc(dev, sizeof(*qp->bus_clk_desc), @@ -507,7 +505,6 @@ int qnoc_probe(struct platform_device *pdev) return -ENOMEM; data->num_nodes = num_nodes; - qp->num_intf_clks = cd_num; for (i = 0; i < cd_num; i++) qp->intf_clks[i].id = cds[i]; diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index 7d1cb2efa9ee..cdd5f444b152 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -40,7 +40,6 @@ struct rpm_clk_resource { /** * struct qcom_icc_provider - Qualcomm specific interconnect provider * @provider: generic interconnect provider - * @num_intf_clks: the total number of intf_clks clk_bulk_data entries * @type: the ICC provider type * @regmap: regmap for QoS registers read/write access * @qos_offset: offset to QoS registers @@ -49,13 +48,13 @@ struct rpm_clk_resource { * @bus_clk_rate: bus clock rate in Hz * @bus_clk_desc: a pointer to a rpm_clk_resource description of bus clocks * @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) + * @num_intf_clks: the total number of intf_clks clk_bulk_data entries + * @intf_clks: a clk_bulk_data array of interface clocks */ struct qcom_icc_provider { struct icc_provider provider; - int num_intf_clks; enum qcom_icc_type type; struct regmap *regmap; unsigned int qos_offset; @@ -64,9 +63,10 @@ struct qcom_icc_provider { u32 bus_clk_rate[QCOM_SMD_RPM_STATE_NUM]; const struct rpm_clk_resource *bus_clk_desc; struct clk *bus_clk; - struct clk_bulk_data *intf_clks; bool keep_alive; bool ignore_enxio; + int num_intf_clks; + struct clk_bulk_data intf_clks[] __counted_by(num_intf_clks); }; /** From ecf20a5e15f9c81a97b1249c979b9c439e777592 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Tue, 2 Jun 2026 18:31:20 +0800 Subject: [PATCH 325/513] interconnect: debugfs-client: add NULL check for platform_device_alloc platform_device_alloc() may return NULL when memory allocation fails. Add proper error handling to prevent a potential NULL pointer dereference when calling platform_device_add() with a NULL pointer. Signed-off-by: Li Jun Link: https://patch.msgid.link/20260602103120.2966458-1-lijun01@kylinos.cn Signed-off-by: Georgi Djakov --- drivers/interconnect/debugfs-client.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/interconnect/debugfs-client.c b/drivers/interconnect/debugfs-client.c index 08df9188ef94..91f86d9237a6 100644 --- a/drivers/interconnect/debugfs-client.c +++ b/drivers/interconnect/debugfs-client.c @@ -142,6 +142,8 @@ int icc_debugfs_client_init(struct dentry *icc_dir) int ret; pdev = platform_device_alloc("icc-debugfs-client", PLATFORM_DEVID_NONE); + if (!pdev) + return -ENOMEM; ret = platform_device_add(pdev); if (ret) { From 25c7e242aca084fdc1098248194032317dca625d Mon Sep 17 00:00:00 2001 From: Kuan-Wei Chiu Date: Thu, 16 Apr 2026 19:08:40 +0000 Subject: [PATCH 326/513] interconnect: Fix use after free in icc_get() and of_icc_get_by_index() In of_icc_get_by_index() and icc_get(), if the dynamic allocation for path->name fails via kasprintf(), the error handling path directly calls kfree(path) to free the path object and returns an error. However, prior to this point, path_find() calls path_init(), which already links the path's requests into the req_list of the respective interconnect nodes via hlist_add_head(). Directly invoking kfree(path) leaves dangling pointers in the hlist. A subsequent call to icc_get() or icc_set_bw() will traverse or modify these corrupted lists, triggering a slab use afterfree. KASAN report showing the vulnerability when reproducing via debugfs: BUG: KASAN: slab-use-after-free in path_find+0x6f8/0xcfc Write of size 8 at addr fff000000d43f748 by task sh/1 ... Call trace: kasan_report+0xac/0xfc path_find+0x6f8/0xcfc icc_get+0x148/0x380 icc_get_set+0xf8/0x2d0 ... Freed by task 1: kfree+0x1a0/0x4a4 icc_get+0x2cc/0x380 icc_get_set+0xf8/0x2d0 Fix this by replacing kfree(path) with the proper teardown function, icc_put(path), which safely removes the requests from the req_list using hlist_del() and drops the provider usage references before freeing the memory. Additionally, in icc_get(), ensure that the icc_lock mutex is released prior to calling icc_put(path) to avoid a deadlock, as icc_put() internally acquires the same lock. Fixes: 3791163602f7 ("interconnect: Handle memory allocation errors") Cc: stable@vger.kernel.org Signed-off-by: Kuan-Wei Chiu Link: https://patch.msgid.link/20260416190840.1753468-1-visitorckw@gmail.com Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 73a9db2c7537..4aa991a54101 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -548,7 +548,7 @@ struct icc_path *of_icc_get_by_index(struct device *dev, int idx) path->name = kasprintf(GFP_KERNEL, "%s-%s", src_data->node->name, dst_data->node->name); if (!path->name) { - kfree(path); + icc_put(path); path = ERR_PTR(-ENOMEM); } @@ -646,8 +646,9 @@ struct icc_path *icc_get(struct device *dev, const char *src, const char *dst) path->name = kasprintf(GFP_KERNEL, "%s-%s", src_node->name, dst_node->name); if (!path->name) { - kfree(path); - path = ERR_PTR(-ENOMEM); + mutex_unlock(&icc_lock); + icc_put(path); + return ERR_PTR(-ENOMEM); } out: mutex_unlock(&icc_lock); From 2c972f03ccdda4fe9df4833056c5b4bfd953a61c Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Mon, 13 Jul 2026 21:22:36 +0800 Subject: [PATCH 327/513] fpga: zynq-fpga: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Reviewed-by: Michal Simek Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260713132237.370272-1-panchuang@vivo.com Signed-off-by: Xu Yilun --- drivers/fpga/zynq-fpga.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c index 9d1d599ef718..2f8ba32e4002 100644 --- a/drivers/fpga/zynq-fpga.c +++ b/drivers/fpga/zynq-fpga.c @@ -598,7 +598,6 @@ static int zynq_fpga_probe(struct platform_device *pdev) err = devm_request_irq(dev, priv->irq, zynq_fpga_isr, 0, dev_name(dev), priv); if (err) { - dev_err(dev, "unable to request IRQ\n"); clk_disable_unprepare(priv->clk); return err; } From 4516d204aa0cf180bb76a8af50014d93ba6cf3fc Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 20 Jul 2026 16:37:06 +0300 Subject: [PATCH 328/513] dt-bindings: iio: adc: ad4080: add AD4883 support Add AD4883 compatible string to the AD4080 devicetree binding. The AD4883 is a dual-channel, 16-bit, 40 MSPS SAR ADC, sharing the same register map and interface as the AD4080 family. Like the AD4880, it requires two SPI chip selects and two io-backends for its independent ADC channels. The AD4883 differs from the AD4880 in resolution (16-bit vs 20-bit), which requires distinct channel configuration in the driver, precluding a fallback compatible. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml index 4a3f7d3e05c3..db8467b98e67 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml @@ -39,6 +39,7 @@ properties: - adi,ad4087 - adi,ad4088 - adi,ad4880 + - adi,ad4883 - adi,ad4884 reg: @@ -102,6 +103,7 @@ allOf: contains: enum: - adi,ad4880 + - adi,ad4883 - adi,ad4884 then: properties: From fa2000702618660ec9ab111d9b0f0a99c6ff50ab Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 20 Jul 2026 16:37:07 +0300 Subject: [PATCH 329/513] iio: adc: ad4080: add support for AD4883 Add support for the AD4883, a dual-channel, 16-bit, 40 MSPS SAR ADC. The AD4883 is the dual-channel variant of the AD4083, sharing the same register map and SPI interface as the rest of the AD4080 family. Like the AD4880, it uses two independent ADC channels, each with its own SPI configuration interface. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4080.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c index c7ecf6971be4..543728c7e88c 100644 --- a/drivers/iio/adc/ad4080.c +++ b/drivers/iio/adc/ad4080.c @@ -135,6 +135,7 @@ #define AD4087_CHIP_ID 0x0057 #define AD4088_CHIP_ID 0x0058 #define AD4880_CHIP_ID 0x0059 +#define AD4883_CHIP_ID 0x005B #define AD4884_CHIP_ID 0x005C #define AD4080_MAX_CHANNELS 2 @@ -541,6 +542,11 @@ static const struct iio_chan_spec ad4880_channels[] = { AD4880_CHANNEL_DEFINE(20, 32, 1), }; +static const struct iio_chan_spec ad4883_channels[] = { + AD4880_CHANNEL_DEFINE(16, 16, 0), + AD4880_CHANNEL_DEFINE(16, 16, 1), +}; + static const struct iio_chan_spec ad4884_channels[] = { AD4880_CHANNEL_DEFINE(16, 16, 0), AD4880_CHANNEL_DEFINE(16, 16, 1), @@ -646,6 +652,16 @@ static const struct ad4080_chip_info ad4880_chip_info = { .lvds_cnv_clk_cnt_max = AD4080_LVDS_CNV_CLK_CNT_MAX, }; +static const struct ad4080_chip_info ad4883_chip_info = { + .name = "ad4883", + .product_id = AD4883_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 2, + .channels = ad4883_channels, + .lvds_cnv_clk_cnt_max = 5, +}; + static const struct ad4080_chip_info ad4884_chip_info = { .name = "ad4884", .product_id = AD4884_CHIP_ID, @@ -858,6 +874,7 @@ static const struct spi_device_id ad4080_id[] = { { .name = "ad4087", .driver_data = (kernel_ulong_t)&ad4087_chip_info }, { .name = "ad4088", .driver_data = (kernel_ulong_t)&ad4088_chip_info }, { .name = "ad4880", .driver_data = (kernel_ulong_t)&ad4880_chip_info }, + { .name = "ad4883", .driver_data = (kernel_ulong_t)&ad4883_chip_info }, { .name = "ad4884", .driver_data = (kernel_ulong_t)&ad4884_chip_info }, { } }; @@ -874,6 +891,7 @@ static const struct of_device_id ad4080_of_match[] = { { .compatible = "adi,ad4087", &ad4087_chip_info }, { .compatible = "adi,ad4088", &ad4088_chip_info }, { .compatible = "adi,ad4880", &ad4880_chip_info }, + { .compatible = "adi,ad4883", &ad4883_chip_info }, { .compatible = "adi,ad4884", &ad4884_chip_info }, { } }; From 0b5e142ced4bcf20532da051934bd694d1bbd470 Mon Sep 17 00:00:00 2001 From: Can Peng Date: Mon, 20 Jul 2026 10:43:12 +0800 Subject: [PATCH 330/513] iio: pressure: mpl115: Fix runtime PM cleanup mpl115_probe() enables runtime PM when a shutdown GPIO is present and then returns the result of devm_iio_device_register(). If registration fails, runtime PM remains enabled and autosuspend remains selected. The same unmanaged runtime PM state is also left behind on driver unbind, as the IIO device registration is managed but the runtime PM setup is not. Use devm_pm_runtime_enable() so runtime PM is disabled automatically on probe failure and driver unbind, and check pm_runtime_set_active() so setup errors are reported. Set the autosuspend parameters before enabling runtime PM. Once probe has completed, the driver core queues an idle request for the device, so an explicit pm_runtime_get_noresume()/pm_runtime_put() pair is not needed to start autosuspend. Fixes: 0c3a333524a3 ("iio: pressure: mpl115: Implementing low power mode by shutdown gpio") Cc: stable@vger.kernel.org Suggested-by: jonathan.cameron@oss.qualcomm.com Signed-off-by: Can Peng Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/mpl115.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c index 830a5065c008..ca260fd83cf9 100644 --- a/drivers/iio/pressure/mpl115.c +++ b/drivers/iio/pressure/mpl115.c @@ -203,9 +203,9 @@ int mpl115_probe(struct device *dev, const char *name, if (data->shutdown) { /* Enable runtime PM */ - pm_runtime_get_noresume(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); + ret = pm_runtime_set_active(dev); + if (ret) + return ret; /* * As the device takes 3 ms to come up with a fresh @@ -215,7 +215,10 @@ int mpl115_probe(struct device *dev, const char *name, */ pm_runtime_set_autosuspend_delay(dev, 2000); pm_runtime_use_autosuspend(dev); - pm_runtime_put(dev); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; dev_dbg(dev, "low-power mode enabled"); } else From 1330597c7480611334977ff66878fd40b411fa41 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Mon, 20 Jul 2026 12:38:32 +0200 Subject: [PATCH 331/513] iio: inv_sensors: convert to kernel types Convert standard types (u)intXX_t to kernel type u/sXX. Suggested-by: Andy Shevchenko Signed-off-by: Jean-Baptiste Maneyrol Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- .../inv_sensors/inv_sensors_timestamp.c | 48 +++++++++---------- .../linux/iio/common/inv_sensors_timestamp.h | 34 ++++++------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c index e0b10366ed2b..ecfd54ffe7e9 100644 --- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c +++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c @@ -19,9 +19,9 @@ (((_val) * (1000 + (_jitter))) / 1000) /* Add a new value inside an accumulator and update the estimate value */ -static void inv_update_acc(struct inv_sensors_timestamp_acc *acc, uint32_t val) +static void inv_update_acc(struct inv_sensors_timestamp_acc *acc, u32 val) { - uint64_t sum = 0; + u64 sum = 0; size_t i; acc->values[acc->idx++] = val; @@ -58,9 +58,9 @@ void inv_sensors_timestamp_init(struct inv_sensors_timestamp *ts, EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_init, "IIO_INV_SENSORS_TIMESTAMP"); int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts, - uint32_t period, bool fifo) + u32 period, bool fifo) { - uint32_t mult; + u32 mult; /* when FIFO is on, prevent odr change if one is already pending */ if (fifo && ts->new_mult != 0) @@ -78,9 +78,9 @@ int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts, } EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_update_odr, "IIO_INV_SENSORS_TIMESTAMP"); -static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period) +static bool inv_validate_period(struct inv_sensors_timestamp *ts, u32 period) { - uint32_t period_min, period_max; + u32 period_min, period_max; /* check that period is acceptable */ period_min = ts->min_period * ts->mult; @@ -92,9 +92,9 @@ static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t perio } static bool inv_update_chip_period(struct inv_sensors_timestamp *ts, - uint32_t period) + u32 period) { - uint32_t new_chip_period; + u32 new_chip_period; if (!inv_validate_period(ts, period)) return false; @@ -109,19 +109,19 @@ static bool inv_update_chip_period(struct inv_sensors_timestamp *ts, static void inv_align_timestamp_it(struct inv_sensors_timestamp *ts) { - const int64_t period_min = (int64_t)ts->min_period * ts->mult; - const int64_t period_max = (int64_t)ts->max_period * ts->mult; - int64_t add_max, sub_max; - int64_t delta, jitter; - int64_t adjust; + const s64 period_min = (s64)ts->min_period * ts->mult; + const s64 period_max = (s64)ts->max_period * ts->mult; + s64 add_max, sub_max; + s64 delta, jitter; + s64 adjust; /* delta time between last sample and last interrupt */ delta = ts->it.lo - ts->timestamp; /* adjust timestamp while respecting jitter */ - add_max = period_max - (int64_t)ts->period; - sub_max = period_min - (int64_t)ts->period; - jitter = INV_SENSORS_TIMESTAMP_JITTER((int64_t)ts->period, ts->chip.jitter); + add_max = period_max - (s64)ts->period; + sub_max = period_min - (s64)ts->period; + jitter = INV_SENSORS_TIMESTAMP_JITTER((s64)ts->period, ts->chip.jitter); if (delta > jitter) adjust = add_max; else if (delta < -jitter) @@ -133,11 +133,11 @@ static void inv_align_timestamp_it(struct inv_sensors_timestamp *ts) } void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, - size_t sample_nb, int64_t timestamp) + size_t sample_nb, s64 timestamp) { struct inv_sensors_timestamp_interval *it; - int64_t delta, interval; - uint32_t period; + s64 delta, interval; + u32 period; bool valid = false; if (sample_nb == 0) @@ -157,7 +157,7 @@ void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, /* 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; + interval = (s64)ts->period * (s64)sample_nb; ts->timestamp = it->up - interval; return; } @@ -169,11 +169,11 @@ void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_interrupt, "IIO_INV_SENSORS_TIMESTAMP"); void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts, - uint32_t fifo_period, size_t fifo_nb, + u32 fifo_period, size_t fifo_nb, unsigned int fifo_no) { - int64_t interval; - uint32_t fifo_mult; + s64 interval; + u32 fifo_mult; if (ts->new_mult == 0) return; @@ -194,7 +194,7 @@ void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts, fifo_mult = fifo_period / ts->chip.clock_period; fifo_period = fifo_mult * ts->chip_period.val; /* computes time interval between interrupt and this sample */ - interval = (int64_t)(fifo_nb - fifo_no) * (int64_t)fifo_period; + interval = (s64)(fifo_nb - fifo_no) * (s64)fifo_period; ts->timestamp = ts->it.up - interval; } } diff --git a/include/linux/iio/common/inv_sensors_timestamp.h b/include/linux/iio/common/inv_sensors_timestamp.h index 8d506f1e9df2..e4e720e6f4b7 100644 --- a/include/linux/iio/common/inv_sensors_timestamp.h +++ b/include/linux/iio/common/inv_sensors_timestamp.h @@ -13,9 +13,9 @@ * @init_period: chip initial period at reset in ns */ struct inv_sensors_timestamp_chip { - uint32_t clock_period; - uint32_t jitter; - uint32_t init_period; + u32 clock_period; + u32 jitter; + u32 init_period; }; /** @@ -24,8 +24,8 @@ struct inv_sensors_timestamp_chip { * @up: interval upper bound */ struct inv_sensors_timestamp_interval { - int64_t lo; - int64_t up; + s64 lo; + s64 up; }; /** @@ -35,9 +35,9 @@ struct inv_sensors_timestamp_interval { * @values: table of all measured values, use for computing the mean */ struct inv_sensors_timestamp_acc { - uint32_t val; + u32 val; size_t idx; - uint32_t values[32]; + u32 values[32]; }; /** @@ -54,13 +54,13 @@ struct inv_sensors_timestamp_acc { */ struct inv_sensors_timestamp { struct inv_sensors_timestamp_chip chip; - uint32_t min_period; - uint32_t max_period; + u32 min_period; + u32 max_period; struct inv_sensors_timestamp_interval it; - int64_t timestamp; - uint32_t mult; - uint32_t new_mult; - uint32_t period; + s64 timestamp; + u32 mult; + u32 new_mult; + u32 period; struct inv_sensors_timestamp_acc chip_period; }; @@ -68,19 +68,19 @@ void inv_sensors_timestamp_init(struct inv_sensors_timestamp *ts, const struct inv_sensors_timestamp_chip *chip); int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts, - uint32_t period, bool fifo); + u32 period, bool fifo); void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, - size_t sample_nb, int64_t timestamp); + size_t sample_nb, s64 timestamp); -static inline int64_t inv_sensors_timestamp_pop(struct inv_sensors_timestamp *ts) +static inline s64 inv_sensors_timestamp_pop(struct inv_sensors_timestamp *ts) { ts->timestamp += ts->period; return ts->timestamp; } void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts, - uint32_t fifo_period, size_t fifo_nb, + u32 fifo_period, size_t fifo_nb, unsigned int fifo_no); static inline void inv_sensors_timestamp_reset(struct inv_sensors_timestamp *ts) From 8f4b627656fa1767d8337a95849fe7c895b6f68a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Mon, 20 Jul 2026 12:38:33 +0200 Subject: [PATCH 332/513] iio: inv_sensors: better timestamp alignment when using watermark Current interrupt timestamp alignment only changes the final timestamp. When the watermark is in use, we have a batch of samples for each interrupt. The current code doesn't manage to align the timestamp because the jitter is too high. Instead modify the estimated inter interrupt period and use that to adjust the timestamp alignment over the batch in a linear fashion. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- .../inv_sensors/inv_sensors_timestamp.c | 49 +++++++------------ 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c index ecfd54ffe7e9..2aaaa8df6d03 100644 --- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c +++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c @@ -10,9 +10,7 @@ #include -/* compute jitter, min and max following jitter in per mille */ -#define INV_SENSORS_TIMESTAMP_JITTER(_val, _jitter) \ - (div_s64((_val) * (_jitter), 1000)) +/* compute min and max following jitter in per mille */ #define INV_SENSORS_TIMESTAMP_MIN(_val, _jitter) \ (((_val) * (1000 - (_jitter))) / 1000) #define INV_SENSORS_TIMESTAMP_MAX(_val, _jitter) \ @@ -102,34 +100,22 @@ static bool inv_update_chip_period(struct inv_sensors_timestamp *ts, /* update chip internal period estimation */ new_chip_period = period / ts->mult; inv_update_acc(&ts->chip_period, new_chip_period); - ts->period = ts->mult * ts->chip_period.val; return true; } -static void inv_align_timestamp_it(struct inv_sensors_timestamp *ts) +static u32 inv_align_timestamp_it(struct inv_sensors_timestamp *ts, + unsigned int sample_nb) { const s64 period_min = (s64)ts->min_period * ts->mult; const s64 period_max = (s64)ts->max_period * ts->mult; - s64 add_max, sub_max; - s64 delta, jitter; - s64 adjust; + s64 new_period; - /* delta time between last sample and last interrupt */ - delta = ts->it.lo - ts->timestamp; + /* compute new period aligning last timestamp with interrupt timestamp */ + new_period = div_s64(ts->it.up - ts->timestamp, sample_nb); - /* adjust timestamp while respecting jitter */ - add_max = period_max - (s64)ts->period; - sub_max = period_min - (s64)ts->period; - jitter = INV_SENSORS_TIMESTAMP_JITTER((s64)ts->period, ts->chip.jitter); - if (delta > jitter) - adjust = add_max; - else if (delta < -jitter) - adjust = sub_max; - else - adjust = 0; - - ts->timestamp += adjust; + /* ensure that period never overflows the jitter */ + return clamp(new_period, period_min, period_max); } void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, @@ -143,6 +129,13 @@ void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, if (sample_nb == 0) return; + /* no previous data, compute theoretical value from interrupt */ + if (ts->timestamp == 0) { + /* elapsed time: sensor period * sensor samples number */ + interval = (s64)ts->period * (s64)sample_nb; + ts->timestamp = timestamp - interval; + } + /* update interrupt timestamp and compute chip and sensor periods */ it = &ts->it; it->lo = it->up; @@ -154,17 +147,11 @@ void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, valid = inv_update_chip_period(ts, period); } - /* no previous data, compute theoretical value from interrupt */ - if (ts->timestamp == 0) { - /* elapsed time: sensor period * sensor samples number */ - interval = (s64)ts->period * (s64)sample_nb; - ts->timestamp = it->up - interval; - return; - } - /* if interrupt interval is valid, sync with interrupt timestamp */ if (valid) - inv_align_timestamp_it(ts); + ts->period = inv_align_timestamp_it(ts, sample_nb); + else + ts->period = ts->mult * ts->chip_period.val; } EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_interrupt, "IIO_INV_SENSORS_TIMESTAMP"); From 82bd09ce4fd3b15680735eaeafe2e688901e3b00 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Mon, 20 Jul 2026 12:38:34 +0200 Subject: [PATCH 333/513] iio: inv_sensors: improve period measurement by using a longer delay Period measurement can be difficult when using high sampling frequency where the jitter criteria is hard to meet because of the system jitter. This new version is using the delta time between 2 distant interrupts to measure an interval of at least 20ms. 20ms is a good compromise between the mitigation of system jitter and the delay to update period. This way we decorrelate the period measurement from the interrupt timestamps syncing using only the 2 last interrupts. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- .../inv_sensors/inv_sensors_timestamp.c | 26 ++++++++++++++++--- .../linux/iio/common/inv_sensors_timestamp.h | 6 +++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c index 2aaaa8df6d03..88a82d1370c5 100644 --- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c +++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c @@ -16,6 +16,9 @@ #define INV_SENSORS_TIMESTAMP_MAX(_val, _jitter) \ (((_val) * (1000 + (_jitter))) / 1000) +/* minimum timestamp delta between 2 interrupts for measuring period (20ms) */ +#define INV_SENSORS_MIN_IT_DELTA (20 * NSEC_PER_MSEC) + /* Add a new value inside an accumulator and update the estimate value */ static void inv_update_acc(struct inv_sensors_timestamp_acc *acc, u32 val) { @@ -122,7 +125,7 @@ void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, size_t sample_nb, s64 timestamp) { struct inv_sensors_timestamp_interval *it; - s64 delta, interval; + s64 delta, delta_threshold, interval; u32 period; bool valid = false; @@ -136,15 +139,32 @@ void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, ts->timestamp = timestamp - interval; } + /* update delta timestamps and estimated period */ + it = &ts->delta; + ts->delta_counter += sample_nb; + delta = timestamp - it->up; + delta_threshold = INV_SENSORS_TIMESTAMP_MIN(INV_SENSORS_MIN_IT_DELTA, ts->chip.jitter); + if (delta >= delta_threshold) { + it->lo = it->up; + it->up = timestamp; + if (it->lo != 0) { + /* compute period: delta time divided by number of samples */ + delta = it->up - it->lo; + period = div_s64(delta, ts->delta_counter); + inv_update_chip_period(ts, period); + } + ts->delta_counter = 0; + } + /* update interrupt timestamp and compute chip and sensor periods */ it = &ts->it; it->lo = it->up; it->up = timestamp; delta = it->up - it->lo; if (it->lo != 0) { - /* compute period: delta time divided by number of samples */ + /* compute period and check validity */ period = div_s64(delta, sample_nb); - valid = inv_update_chip_period(ts, period); + valid = inv_validate_period(ts, period); } /* if interrupt interval is valid, sync with interrupt timestamp */ diff --git a/include/linux/iio/common/inv_sensors_timestamp.h b/include/linux/iio/common/inv_sensors_timestamp.h index e4e720e6f4b7..4f08204ede3b 100644 --- a/include/linux/iio/common/inv_sensors_timestamp.h +++ b/include/linux/iio/common/inv_sensors_timestamp.h @@ -46,6 +46,8 @@ struct inv_sensors_timestamp_acc { * @min_period: minimal acceptable clock period * @max_period: maximal acceptable clock period * @it: interrupts interval timestamps + * @delta: interval timestamps between several interrupts + * @delta_counter: number of data samples in the delta interval * @timestamp: store last timestamp for computing next data timestamp * @mult: current internal period multiplier * @new_mult: new set internal period multiplier (not yet effective) @@ -57,6 +59,8 @@ struct inv_sensors_timestamp { u32 min_period; u32 max_period; struct inv_sensors_timestamp_interval it; + struct inv_sensors_timestamp_interval delta; + u32 delta_counter; s64 timestamp; u32 mult; u32 new_mult; @@ -88,6 +92,8 @@ static inline void inv_sensors_timestamp_reset(struct inv_sensors_timestamp *ts) const struct inv_sensors_timestamp_interval interval_init = {0LL, 0LL}; ts->it = interval_init; + ts->delta = interval_init; + ts->delta_counter = 0; ts->timestamp = 0; } From afce56f9ec3796e6ce5ebf8e10e109b3c1327174 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Mon, 20 Jul 2026 21:43:10 +0800 Subject: [PATCH 334/513] staging: iio: adt7316: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_threaded_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Signed-off-by: Jonathan Cameron --- drivers/staging/iio/addac/adt7316.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/staging/iio/addac/adt7316.c b/drivers/staging/iio/addac/adt7316.c index 59fb3bd26bc1..20f6052cc7fb 100644 --- a/drivers/staging/iio/addac/adt7316.c +++ b/drivers/staging/iio/addac/adt7316.c @@ -1814,11 +1814,8 @@ static int adt7316_setup_irq(struct iio_dev *indio_dev) NULL, adt7316_event_handler, irq_type | IRQF_ONESHOT, indio_dev->name, indio_dev); - if (ret) { - dev_err(&indio_dev->dev, "failed to request irq %d\n", - chip->bus.irq); + if (ret) return ret; - } if (irq_type & IRQF_TRIGGER_HIGH) chip->config1 |= ADT7316_INT_POLARITY; From 76fde0f9eacce64938f669d1bcafbef1e4b54417 Mon Sep 17 00:00:00 2001 From: Siratul Islam Date: Tue, 21 Jul 2026 21:03:08 +0600 Subject: [PATCH 335/513] iio: update email for Siratul Islam Change personal email to the Linux specific one, to have a single and focused point of interaction. Signed-off-by: Siratul Islam Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/linux-iio/a509d034-de90-4e46-bd7e-75f61002d6ba@kernel.org/ Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/proximity/st,vl53l0x.yaml | 2 +- MAINTAINERS | 2 +- drivers/iio/proximity/vl53l1x-i2c.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml b/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml index f7f8be1e379d..d23dc59505cc 100644 --- a/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml +++ b/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml @@ -8,7 +8,7 @@ title: ST VL53L0X/VL53L1X ToF ranging sensor maintainers: - Song Qiang - - Siratul Islam + - Siratul Islam properties: compatible: diff --git a/MAINTAINERS b/MAINTAINERS index 7077ed7c9efe..d2be2f40f906 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25702,7 +25702,7 @@ 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 +M: Siratul Islam L: linux-iio@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml diff --git a/drivers/iio/proximity/vl53l1x-i2c.c b/drivers/iio/proximity/vl53l1x-i2c.c index 7ca0f2e652f4..bce238d1b6de 100644 --- a/drivers/iio/proximity/vl53l1x-i2c.c +++ b/drivers/iio/proximity/vl53l1x-i2c.c @@ -2,7 +2,7 @@ /* * Support for ST VL53L1X FlightSense ToF Ranging Sensor on a i2c bus. * - * Copyright (C) 2026 Siratul Islam + * Copyright (C) 2026 Siratul Islam * * Datasheet available at * @@ -751,6 +751,6 @@ static struct i2c_driver vl53l1x_driver = { }; module_i2c_driver(vl53l1x_driver); -MODULE_AUTHOR("Siratul Islam "); +MODULE_AUTHOR("Siratul Islam "); MODULE_DESCRIPTION("ST VL53L1X ToF ranging sensor driver"); MODULE_LICENSE("Dual BSD/GPL"); From 3e37fdbc6722314c6b18e3c73771547ac3b89ae4 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Tue, 21 Jul 2026 15:48:42 +0200 Subject: [PATCH 336/513] dt-bindings: iio: magnetometer: add QST QMC6308 Add device tree bindings for the QST QMC6308, a 3-axis anisotropic magnetoresistive (AMR) magnetometer. It is an I2C device with a single supply and no interrupt pin, at I2C address 0x2c. Assisted-by: Claude:claude-fable-5 Signed-off-by: Jorijn van der Graaf Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jonathan Cameron --- .../iio/magnetometer/qstcorp,qmc6308.yaml | 47 +++++++++++++++++++ MAINTAINERS | 6 +++ 2 files changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc6308.yaml diff --git a/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc6308.yaml b/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc6308.yaml new file mode 100644 index 000000000000..b7ab6decdfbc --- /dev/null +++ b/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc6308.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/magnetometer/qstcorp,qmc6308.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: QST QMC6308 3-Axis Magnetic Sensor + +maintainers: + - Jorijn van der Graaf + +description: + QST QMC6308 3-axis anisotropic magnetoresistive (AMR) magnetic sensor + on I2C bus, with a single supply and no interrupt pin. + https://qstcorp.com/upload/pdf/202202/13-52-15%20QMC6308%20Datasheet%20Rev.%20F(1).pdf + +properties: + compatible: + const: qstcorp,qmc6308 + + reg: + maxItems: 1 + + vdd-supply: true + + mount-matrix: true + +required: + - compatible + - reg + - vdd-supply + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + magnetometer@2c { + compatible = "qstcorp,qmc6308"; + reg = <0x2c>; + vdd-supply = <&vdd_1v8_reg>; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index d2be2f40f906..7d6504e1c762 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22061,6 +22061,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml F: drivers/iio/magnetometer/qmc5883l.c +QST QMC6308 3-AXIS MAGNETIC SENSOR +M: Jorijn van der Graaf +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc6308.yaml + QT1010 MEDIA DRIVER L: linux-media@vger.kernel.org S: Orphan From 8dc6d241fc1f50231d56d6600ef23a5ee4a53967 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Tue, 21 Jul 2026 15:48:43 +0200 Subject: [PATCH 337/513] iio: magnetometer: add support for QST QMC6308 The QST QMC6308 is a 3-axis anisotropic magnetoresistive (AMR) magnetometer on I2C, found e.g. in the Fairphone 6. Its register map is not compatible with the QMC5883L's, so add a separate driver rather than extending the QMC5883L driver. Support raw X/Y/Z reads, output data rate, field range, filter oversampling ratio, the mount matrix, and runtime PM. Run measurements in the chip's periodic "normal" mode paced by the DRDY flag rather than in its one-shot "single" mode: the datasheet specifies no conversion time that could bound a one-shot wait, while normal mode is paced by the specified output data rates, which also keeps the sampling_frequency ABI meaningful. Runtime PM puts the chip into its suspend mode after 500 ms without a reading, dropping supply current to a few microamps. The suspended chip retains its registers and keeps responding on I2C, so resuming only rewrites the mode field and discards one stale sample, and configuration changes apply even while suspended; VDD is left enabled across runtime suspend. Assisted-by: Claude:claude-fable-5 Signed-off-by: Jorijn van der Graaf Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + drivers/iio/magnetometer/Kconfig | 11 + drivers/iio/magnetometer/Makefile | 1 + drivers/iio/magnetometer/qmc6308.c | 601 +++++++++++++++++++++++++++++ 4 files changed, 614 insertions(+) create mode 100644 drivers/iio/magnetometer/qmc6308.c diff --git a/MAINTAINERS b/MAINTAINERS index 7d6504e1c762..ce020ded5fd3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22066,6 +22066,7 @@ M: Jorijn van der Graaf L: linux-iio@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc6308.yaml +F: drivers/iio/magnetometer/qmc6308.c QT1010 MEDIA DRIVER L: linux-media@vger.kernel.org diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index 2dee566ba703..d359003b248e 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -220,6 +220,17 @@ config QMC5883L To compile this driver as a module, choose M here: the module will be called qmc5883l. +config QMC6308 + tristate "QST QMC6308 3-Axis Magnetic Sensor" + depends on I2C + select REGMAP_I2C + help + Say Y here to add support for the QST QMC6308 3-Axis + Magnetic Sensor. + + To compile this driver as a module, choose M here: the + module will be called qmc6308. + config SENSORS_HMC5843 tristate select IIO_BUFFER diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index b9478e6513e4..8f2205975c1b 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o obj-$(CONFIG_INFINEON_TLV493D) += tlv493d.o obj-$(CONFIG_QMC5883L) += qmc5883l.o +obj-$(CONFIG_QMC6308) += qmc6308.o obj-$(CONFIG_SENSORS_HMC5843) += hmc5843_core.o obj-$(CONFIG_SENSORS_HMC5843_I2C) += hmc5843_i2c.o diff --git a/drivers/iio/magnetometer/qmc6308.c b/drivers/iio/magnetometer/qmc6308.c new file mode 100644 index 000000000000..72348cd9ef01 --- /dev/null +++ b/drivers/iio/magnetometer/qmc6308.c @@ -0,0 +1,601 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Support for QST QMC6308 3-Axis Magnetic Sensor on I2C bus. + * + * Copyright (C) 2026 Jorijn van der Graaf + * + * Datasheet available at + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define QMC6308_REG_ID 0x00 +#define QMC6308_REG_X_LSB 0x01 +#define QMC6308_REG_STATUS 0x09 +#define QMC6308_REG_CTRL1 0x0A +#define QMC6308_REG_CTRL2 0x0B +#define QMC6308_REG_CTRL3 0x0D +#define QMC6308_REG_CTRL4 0x29 + +#define QMC6308_CHIP_ID 0x80 + +/* Control register 1 */ +#define QMC6308_MODE_MASK GENMASK(1, 0) +#define QMC6308_ODR_MASK GENMASK(3, 2) +#define QMC6308_OSR1_MASK GENMASK(5, 4) +#define QMC6308_OSR2_MASK GENMASK(7, 6) + +#define QMC6308_MODE_SUSPEND 0x00 +#define QMC6308_MODE_NORMAL 0x01 + +#define QMC6308_ODR_10HZ 0x00 +#define QMC6308_ODR_50HZ 0x01 +#define QMC6308_ODR_100HZ 0x02 +#define QMC6308_ODR_200HZ 0x03 + +#define QMC6308_OSR1_8 0x00 +#define QMC6308_OSR1_4 0x01 +#define QMC6308_OSR1_2 0x02 +#define QMC6308_OSR1_1 0x03 + +/* Control register 2 */ +#define QMC6308_SET_RESET_MASK GENMASK(1, 0) +#define QMC6308_RNG_MASK GENMASK(3, 2) +#define QMC6308_SELF_TEST BIT(6) +#define QMC6308_SOFT_RST BIT(7) + +#define QMC6308_SET_RESET_ON 0x00 + +#define QMC6308_RNG_30G 0x00 +#define QMC6308_RNG_12G 0x01 +#define QMC6308_RNG_8G 0x02 +#define QMC6308_RNG_2G 0x03 + +/* Status register */ +#define QMC6308_STATUS_DRDY BIT(0) +#define QMC6308_STATUS_OVFL BIT(1) + +/* Power-on completion time (datasheet Table 7) */ +#define QMC6308_POR_US 250 + +#define QMC6308_AUTOSUSPEND_DELAY_MS 500 + +struct qmc6308_data { + struct regmap *regmap; + /* Protect data->range/odr/osr and serialize measurements */ + struct mutex mutex; + struct iio_mount_matrix orientation; + u8 range; + u8 odr; + u8 osr; +}; + +enum qmc6308_axis { + QMC6308_AXIS_X, + QMC6308_AXIS_Y, + QMC6308_AXIS_Z, +}; + +static const int qmc6308_odr_avail[] = { + [QMC6308_ODR_10HZ] = 10, + [QMC6308_ODR_50HZ] = 50, + [QMC6308_ODR_100HZ] = 100, + [QMC6308_ODR_200HZ] = 200, +}; + +static const int qmc6308_osr1_avail[] = { + [QMC6308_OSR1_8] = 8, + [QMC6308_OSR1_4] = 4, + [QMC6308_OSR1_2] = 2, + [QMC6308_OSR1_1] = 1, +}; + +/* + * Sensitivity is 1000/2500/3750/15000 LSB/Gauss for the + * +-30/12/8/2 Gauss ranges respectively. + */ +static const int qmc6308_scales[][2] = { + [QMC6308_RNG_30G] = { 0, 1000000 }, + [QMC6308_RNG_12G] = { 0, 400000 }, + [QMC6308_RNG_8G] = { 0, 266667 }, + [QMC6308_RNG_2G] = { 0, 66667 }, +}; + +static int qmc6308_set_mode(struct qmc6308_data *data, unsigned int mode) +{ + return regmap_update_bits(data->regmap, QMC6308_REG_CTRL1, + QMC6308_MODE_MASK, + FIELD_PREP(QMC6308_MODE_MASK, mode)); +} + +static int qmc6308_take_measurement(struct iio_dev *indio_dev, int index, + int *val) +{ + struct qmc6308_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + unsigned int status; + __le16 buf[3]; + int ret; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) { + dev_err(dev, "Failed to power on (%d)\n", ret); + return ret; + } + + guard(mutex)(&data->mutex); + + /* + * Reading the status register clears DRDY, which is why the poll + * and the data read stay under one mutex hold. A runtime resume + * clears DRDY too, so a sample converted before the last suspend + * is never returned here. + * + * The timeout is 50ms of headroom over the slowest ODR (10Hz). + */ + ret = regmap_read_poll_timeout(data->regmap, QMC6308_REG_STATUS, + status, (status & QMC6308_STATUS_DRDY), + 2 * USEC_PER_MSEC, + 150 * USEC_PER_MSEC); + if (ret) + return ret; + + ret = regmap_bulk_read(data->regmap, QMC6308_REG_X_LSB, buf, + sizeof(buf)); + if (ret) + return ret; + + if (status & QMC6308_STATUS_OVFL) + return -ERANGE; + + *val = (s16)le16_to_cpu(buf[index]); + + return 0; +} + +static int qmc6308_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct qmc6308_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = qmc6308_take_measurement(indio_dev, chan->address, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: { + guard(mutex)(&data->mutex); + + *val = qmc6308_scales[data->range][0]; + *val2 = qmc6308_scales[data->range][1]; + + return IIO_VAL_INT_PLUS_NANO; + } + case IIO_CHAN_INFO_SAMP_FREQ: { + guard(mutex)(&data->mutex); + + *val = qmc6308_odr_avail[data->odr]; + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + guard(mutex)(&data->mutex); + + *val = qmc6308_osr1_avail[data->osr]; + + return IIO_VAL_INT; + } + default: + return -EINVAL; + } +} + +static int qmc6308_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int val, int val2, long mask) +{ + struct qmc6308_data *data = iio_priv(indio_dev); + unsigned int status; + unsigned int i; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: { + if (val != 0) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(qmc6308_scales); i++) { + if (val2 == qmc6308_scales[i][1]) + break; + } + if (i == ARRAY_SIZE(qmc6308_scales)) + return -EINVAL; + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC6308_REG_CTRL2, + QMC6308_RNG_MASK, + FIELD_PREP(QMC6308_RNG_MASK, i)); + if (ret) + return ret; + + data->range = i; + + /* + * The data registers still hold (and DRDY still + * advertises) a sample converted at the previous range; + * discard it so that a read does not pair old-range data + * with the new scale. A conversion already in flight may + * still complete at the old range, so this narrows the + * window rather than closing it. The range change itself + * took effect, so only log a failure here: an error + * would mislead userspace about an effective write. + */ + ret = regmap_read(data->regmap, QMC6308_REG_STATUS, + &status); + if (ret) + dev_warn(regmap_get_device(data->regmap), + "Failed to discard stale sample (%d)\n", ret); + + return 0; + } + case IIO_CHAN_INFO_SAMP_FREQ: { + for (i = 0; i < ARRAY_SIZE(qmc6308_odr_avail); i++) { + if (val == qmc6308_odr_avail[i]) + break; + } + if (i == ARRAY_SIZE(qmc6308_odr_avail)) + return -EINVAL; + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC6308_REG_CTRL1, + QMC6308_ODR_MASK, + FIELD_PREP(QMC6308_ODR_MASK, i)); + if (ret) + return ret; + + data->odr = i; + + return 0; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + for (i = 0; i < ARRAY_SIZE(qmc6308_osr1_avail); i++) { + if (val == qmc6308_osr1_avail[i]) + break; + } + if (i == ARRAY_SIZE(qmc6308_osr1_avail)) + return -EINVAL; + + guard(mutex)(&data->mutex); + + ret = regmap_update_bits(data->regmap, QMC6308_REG_CTRL1, + QMC6308_OSR1_MASK, + FIELD_PREP(QMC6308_OSR1_MASK, i)); + if (ret) + return ret; + + data->osr = i; + + return 0; + } + default: + return -EINVAL; + } +} + +static int qmc6308_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = qmc6308_odr_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(qmc6308_odr_avail); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = qmc6308_osr1_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(qmc6308_osr1_avail); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *vals = (const int *)qmc6308_scales; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(qmc6308_scales) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int qmc6308_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; + } +} + +static const struct iio_mount_matrix * +qmc6308_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct qmc6308_data *data = iio_priv(indio_dev); + + return &data->orientation; +} + +static const struct iio_chan_spec_ext_info qmc6308_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, qmc6308_get_mount_matrix), + { } +}; + +static const struct iio_info qmc6308_info = { + .read_raw = qmc6308_read_raw, + .write_raw = qmc6308_write_raw, + .read_avail = qmc6308_read_avail, + .write_raw_get_fmt = qmc6308_write_raw_get_fmt, +}; + +static int qmc6308_init(struct qmc6308_data *data) +{ + struct regmap *map = data->regmap; + unsigned int reg; + int ret; + + ret = regmap_read(map, QMC6308_REG_ID, ®); + if (ret) + return ret; + + /* Allow unknown IDs so that fallback compatibles work */ + if (reg != QMC6308_CHIP_ID) + dev_warn(regmap_get_device(map), + "Unknown chip id: 0x%02x, continuing\n", reg); + + /* The SOFT_RST bit is not auto-cleared and must be written back 0 */ + ret = regmap_write(map, QMC6308_REG_CTRL2, QMC6308_SOFT_RST); + if (ret) + return ret; + + /* + * The datasheet gives no soft-reset completion figure; reuse the + * power-on time as a conservative bound. + */ + fsleep(QMC6308_POR_US); + + data->range = QMC6308_RNG_30G; + data->odr = QMC6308_ODR_50HZ; + data->osr = QMC6308_OSR1_8; + + ret = regmap_write(map, QMC6308_REG_CTRL2, + FIELD_PREP(QMC6308_SET_RESET_MASK, + QMC6308_SET_RESET_ON) | + FIELD_PREP(QMC6308_RNG_MASK, data->range)); + if (ret) + return ret; + + /* OSR2 (second-stage filter) set to its power-on default of 0 */ + return regmap_write(map, QMC6308_REG_CTRL1, + FIELD_PREP(QMC6308_MODE_MASK, + QMC6308_MODE_NORMAL) | + FIELD_PREP(QMC6308_ODR_MASK, data->odr) | + FIELD_PREP(QMC6308_OSR1_MASK, data->osr) | + FIELD_PREP(QMC6308_OSR2_MASK, 0)); +} + +static void qmc6308_power_down_action(void *priv) +{ + struct qmc6308_data *data = priv; + + if (!pm_runtime_status_suspended(regmap_get_device(data->regmap))) + qmc6308_set_mode(data, QMC6308_MODE_SUSPEND); +} + +static bool qmc6308_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg >= QMC6308_REG_X_LSB && reg <= QMC6308_REG_STATUS; +} + +static bool qmc6308_writable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case QMC6308_REG_CTRL1: + case QMC6308_REG_CTRL2: + case QMC6308_REG_CTRL3: + case QMC6308_REG_CTRL4: + return true; + default: + return false; + } +} + +static const struct regmap_config qmc6308_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = QMC6308_REG_CTRL4, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = qmc6308_volatile_reg, + .writeable_reg = qmc6308_writable_reg, +}; + +#define QMC6308_CHANNEL(_axis) \ + { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .address = QMC6308_AXIS_##_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) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .ext_info = qmc6308_ext_info, \ + } + +static const struct iio_chan_spec qmc6308_channels[] = { + QMC6308_CHANNEL(X), + QMC6308_CHANNEL(Y), + QMC6308_CHANNEL(Z), +}; + +static int qmc6308_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct qmc6308_data *data; + struct iio_dev *indio_dev; + struct regmap *map; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + i2c_set_clientdata(client, indio_dev); + + map = devm_regmap_init_i2c(client, &qmc6308_regmap_config); + if (IS_ERR(map)) + return dev_err_probe(dev, PTR_ERR(map), + "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"); + + fsleep(QMC6308_POR_US); + + data = iio_priv(indio_dev); + data->regmap = map; + + ret = devm_mutex_init(dev, &data->mutex); + if (ret) + return ret; + + ret = iio_read_mount_matrix(dev, &data->orientation); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read mount matrix\n"); + + indio_dev->name = "qmc6308"; + indio_dev->info = &qmc6308_info; + indio_dev->channels = qmc6308_channels; + indio_dev->num_channels = ARRAY_SIZE(qmc6308_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = qmc6308_init(data); + if (ret) + return dev_err_probe(dev, ret, "qmc6308 init failed\n"); + + ret = pm_runtime_set_active(dev); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, qmc6308_power_down_action, data); + if (ret) + return ret; + + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, QMC6308_AUTOSUSPEND_DELAY_MS); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static int qmc6308_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct qmc6308_data *data = iio_priv(indio_dev); + + return qmc6308_set_mode(data, QMC6308_MODE_SUSPEND); +} + +static int qmc6308_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct qmc6308_data *data = iio_priv(indio_dev); + unsigned int status; + int ret; + + ret = qmc6308_set_mode(data, QMC6308_MODE_NORMAL); + if (ret) + return ret; + + /* + * DRDY may still be set for a sample converted before the last + * suspend; reading the status register clears it so the next + * measurement waits for fresh data. + */ + ret = regmap_read(data->regmap, QMC6308_REG_STATUS, &status); + if (ret) { + /* Best effort to leave the chip in a consistent state */ + qmc6308_set_mode(data, QMC6308_MODE_SUSPEND); + } + + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(qmc6308_pm_ops, qmc6308_runtime_suspend, + qmc6308_runtime_resume, NULL); + +static const struct of_device_id qmc6308_match[] = { + { .compatible = "qstcorp,qmc6308" }, + { } +}; +MODULE_DEVICE_TABLE(of, qmc6308_match); + +static const struct i2c_device_id qmc6308_id[] = { + { .name = "qmc6308" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, qmc6308_id); + +static struct i2c_driver qmc6308_driver = { + .driver = { + .name = "qmc6308", + .of_match_table = qmc6308_match, + .pm = pm_ptr(&qmc6308_pm_ops), + }, + .id_table = qmc6308_id, + .probe = qmc6308_probe, +}; +module_i2c_driver(qmc6308_driver); + +MODULE_DESCRIPTION("QST QMC6308 3-Axis Magnetic Sensor driver"); +MODULE_AUTHOR("Jorijn van der Graaf "); +MODULE_LICENSE("GPL"); From 1135d6875d2dbda3f6ec718f3421a6ce4378bd63 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Wed, 22 Jul 2026 15:48:37 +0800 Subject: [PATCH 338/513] iio: chemical: sgp30: Handle IAQ thread creation failure kthread_run() can fail and return an error pointer, but sgp_probe() stores it and returns success, so the device is registered without its IAQ thread and sgp_remove() later passes the error pointer to kthread_stop(). Return the error from probe instead. Fixes: ce514124161a ("iio: chemical: sgp30: Support Sensirion SGP30/SGPC3 sensors") Signed-off-by: Linmao Li Reviewed-by: Joshua Crofts Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/sgp30.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/iio/chemical/sgp30.c b/drivers/iio/chemical/sgp30.c index f10bbebc29e4..379c1c4af8d8 100644 --- a/drivers/iio/chemical/sgp30.c +++ b/drivers/iio/chemical/sgp30.c @@ -548,6 +548,9 @@ static int sgp_probe(struct i2c_client *client) data->iaq_thread = kthread_run(sgp_iaq_threadfn, data, "%s-iaq", data->client->name); + if (IS_ERR(data->iaq_thread)) + return dev_err_probe(dev, PTR_ERR(data->iaq_thread), + "failed to start IAQ thread\n"); return 0; } From 73f46553fd74a1fc56eb2e6218ff3a4ea1de43b5 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 30 Jun 2026 14:57:38 +0800 Subject: [PATCH 339/513] w1: validate slave string length before checking separator w1_atoreg_num() checks buf[2] for the family/id separator before proving the input contains that byte. Require at least the family and separator prefix before checking the separator. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/2026063007047999.4-ccfa108-0039-w1-validate-slave-string-le-pengpeng@iscas.ac.cn Signed-off-by: Krzysztof Kozlowski --- drivers/w1/w1.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 486f321eadc8..c16946642789 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -403,6 +403,11 @@ static int w1_atoreg_num(struct device *dev, const char *buf, size_t count, const char *error_msg = "bad slave string format, expecting " "ff-dddddddddddd\n"; + if (count < 3) { + dev_err(dev, "%s", error_msg); + return -EINVAL; + } + if (buf[2] != '-') { dev_err(dev, "%s", error_msg); return -EINVAL; From 4d3721b204f961e905714954ff95633337b768e3 Mon Sep 17 00:00:00 2001 From: Babanpreet Singh Date: Tue, 14 Jul 2026 04:10:10 +0000 Subject: [PATCH 340/513] w1: ds2482: Fix signedness bug in ds2482_w1_triplet() ds2482_wait_1wire_idle() returns the status register value (0..255) on success, or a negative value on I2C failure: -1 when selecting the status register fails, or a negative errno from i2c_smbus_read_byte(). ds2482_w1_triplet() feeds that result into "return (status >> 5);" without checking for errors, and the function returns u8. For a negative status the arithmetic shift keeps the sign and the u8 truncation fabricates a triplet result whose meaning depends on the errno value: -1 and -EIO happen to become 0xff, whose set low bits make w1_search() abort, but -ETIMEDOUT (-110 >> 5 = -4) becomes 0xfc - "devices responded on both branches, wrote 1" - and -EOPNOTSUPP (-95 >> 5 = -3) becomes 0xfd - "only the zero branch responded". w1_search() then continues the ROM search with a fabricated direction bit instead of aborting, and the corrupted id is either rejected by the ROM CRC (existing device missed) or registers a phantom slave. The function already defines an in-band error value: status is initialized to (3 << 5), which decodes to 3 (both branch bits set, "no device responded") and makes w1_search() terminate the search when sending the triplet command fails. Decode a negative status to the same value. Found by smatch: drivers/w1/masters/ds2482.c:314 ds2482_w1_triplet() warn: signedness bug returning '(-67108864)' Fixes: baf12ae29ab4 ("[PATCH] W1: Add the DS2482 I2C-to-w1 bridge driver.") Assisted-by: Claude:claude-sonnet-5 Signed-off-by: Babanpreet Singh Link: https://patch.msgid.link/20260714041011.7-1-bbnpreetsingh@gmail.com Signed-off-by: Krzysztof Kozlowski --- drivers/w1/masters/ds2482.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index 9e57c6e487d1..cbd11bc61274 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -310,6 +310,10 @@ static u8 ds2482_w1_triplet(void *data, u8 dbit) mutex_unlock(&pdev->access_lock); + /* On bus error, decode to 3 (no device responded) to abort the search */ + if (status < 0) + status = 3 << 5; + /* Decode the status */ return (status >> 5); } From cad928dad9a4c26aa56b4223dc2a29bca517fb5f Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 23 Jul 2026 01:18:58 +0800 Subject: [PATCH 341/513] iio: hid-sensor: als: scale each channel individually MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some sensors have multiple channels (not just brightness but color ALS sensors) with different scaling factors. Signed-off-by: Daniel Schaefer Link: https://gitlab.freedesktop.org/hadess/iio-sensor-proxy/-/merge_requests/413 Tested-by: Sven Vainküla #Asus ProArt PX13 laptop. Acked-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/light/hid-sensor-als.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 6fb6ace26834..929343e42d83 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -33,9 +33,9 @@ struct als_state { u32 illum[CHANNEL_SCAN_INDEX_MAX]; aligned_s64 timestamp; } scan; - int scale_pre_decml; - int scale_post_decml; - int scale_precision; + int scale_pre_decml[CHANNEL_SCAN_INDEX_MAX]; + int scale_post_decml[CHANNEL_SCAN_INDEX_MAX]; + int scale_precision[CHANNEL_SCAN_INDEX_MAX]; int value_offset; int num_channels; s64 timestamp; @@ -174,9 +174,11 @@ static int als_read_raw(struct iio_dev *indio_dev, ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: - *val = als_state->scale_pre_decml; - *val2 = als_state->scale_post_decml; - ret_type = als_state->scale_precision; + if (chan->scan_index >= CHANNEL_SCAN_INDEX_MAX) + return -EINVAL; + *val = als_state->scale_pre_decml[chan->scan_index]; + *val2 = als_state->scale_post_decml[chan->scan_index]; + ret_type = als_state->scale_precision[chan->scan_index]; break; case IIO_CHAN_INFO_OFFSET: *val = als_state->value_offset; @@ -327,6 +329,10 @@ static int als_parse_report(struct platform_device *pdev, }; ++index; + st->scale_precision[i] = hid_sensor_format_scale(usage_id, + &st->als[i], &st->scale_pre_decml[i], + &st->scale_post_decml[i]); + dev_dbg(&pdev->dev, "als %x:%x\n", st->als[i].index, st->als[i].report_id); } @@ -336,10 +342,6 @@ static int als_parse_report(struct platform_device *pdev, if (index) ret = 0; - st->scale_precision = hid_sensor_format_scale(usage_id, - &st->als[CHANNEL_SCAN_INDEX_INTENSITY], - &st->scale_pre_decml, &st->scale_post_decml); - return ret; } From 9e6679200309ce746f409cc9bc2d2c49361f79c5 Mon Sep 17 00:00:00 2001 From: Amin GATTOUT Date: Wed, 22 Jul 2026 21:39:32 +0200 Subject: [PATCH 342/513] iio: adc: at91_adc: use const char * for DT string property Declare the local variable as const char * and remove the unnecessary cast when passing it to of_property_read_string(). Signed-off-by: Amin GATTOUT Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91_adc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index f610ad729bf3..0ac74ad92fca 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -988,7 +988,7 @@ static int at91_adc_probe(struct platform_device *pdev) struct iio_dev *idev; struct at91_adc_state *st; u32 reg, prop; - char *s; + const char *s; idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct at91_adc_state)); if (!idev) @@ -1023,7 +1023,7 @@ static int at91_adc_probe(struct platform_device *pdev) st->res = st->caps->high_res_bits; if (st->caps->low_res_bits && - !of_property_read_string(node, "atmel,adc-use-res", (const char **)&s) + !of_property_read_string(node, "atmel,adc-use-res", &s) && !strcmp(s, "lowres")) st->res = st->caps->low_res_bits; From fc5040337a61ae087f96c567ad1c7e77df08013c Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 24 Jul 2026 11:03:36 +0800 Subject: [PATCH 343/513] iio: accel: Remove redundant dev_err()/dev_err_probe() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() and devm_request_threaded_irq() automatically log detailed error messages on failure. Remove the now-redundant driver-specific dev_err() and dev_err_probe() calls. Standardize on if (ret) check rather than if (ret < 0). Signed-off-by: Pan Chuang Reviewed-by: Andy Shevchenko Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl355_core.c | 3 +-- drivers/iio/accel/adxl367.c | 2 +- drivers/iio/accel/bma180.c | 4 +--- drivers/iio/accel/bma220_core.c | 3 +-- drivers/iio/accel/bma400_core.c | 3 +-- drivers/iio/accel/kionix-kx022a.c | 2 +- drivers/iio/accel/mma9551.c | 4 +--- drivers/iio/accel/mma9553.c | 5 +---- drivers/iio/accel/msa311.c | 2 +- drivers/iio/accel/mxc4005.c | 5 +---- drivers/iio/accel/stk8312.c | 5 +---- drivers/iio/accel/stk8ba50.c | 5 +---- 12 files changed, 12 insertions(+), 31 deletions(-) diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c index 68cb2557f390..080c9f6f9a11 100644 --- a/drivers/iio/accel/adxl355_core.c +++ b/drivers/iio/accel/adxl355_core.c @@ -768,8 +768,7 @@ static int adxl355_probe_trigger(struct iio_dev *indio_dev, int irq) ret = devm_request_irq(data->dev, irq, &iio_trigger_generic_data_rdy_poll, IRQF_NO_THREAD, "adxl355_irq", data->dready_trig); if (ret) - return dev_err_probe(data->dev, ret, "request irq %d failed\n", - irq); + return ret; ret = devm_iio_trigger_register(data->dev, data->dready_trig); if (ret) diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c index 8c3de11a10a3..4ff1c7a0988b 100644 --- a/drivers/iio/accel/adxl367.c +++ b/drivers/iio/accel/adxl367.c @@ -1486,7 +1486,7 @@ int adxl367_probe(struct device *dev, const struct adxl367_ops *ops, adxl367_irq_handler, IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(st->dev, ret, "Failed to request irq\n"); + return ret; return devm_iio_device_register(dev, indio_dev); } diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 62bda8d76691..e7dbbd4766dd 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -988,10 +988,8 @@ static int bma180_probe(struct i2c_client *client) iio_trigger_generic_data_rdy_poll, IRQF_TRIGGER_RISING | IRQF_NO_THREAD, "bma180_event", data->trig); - if (ret) { - dev_err(dev, "unable to request IRQ\n"); + if (ret) goto err_trigger_free; - } data->trig->ops = &bma180_trigger_ops; iio_trigger_set_drvdata(data->trig, indio_dev); diff --git a/drivers/iio/accel/bma220_core.c b/drivers/iio/accel/bma220_core.c index 269e2b720ddb..b83abfdd84fd 100644 --- a/drivers/iio/accel/bma220_core.c +++ b/drivers/iio/accel/bma220_core.c @@ -544,8 +544,7 @@ int bma220_common_probe(struct device *dev, struct regmap *regmap, int irq) &bma220_irq_handler, IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(dev, ret, - "request irq %d failed\n", irq); + return ret; } ret = devm_add_action_or_reset(dev, bma220_deinit, data); diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index 05f72707f830..7e3cff4cca91 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -1789,8 +1789,7 @@ int bma400_probe(struct device *dev, struct regmap *regmap, int irq, IRQF_TRIGGER_RISING | IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(data->dev, ret, - "request irq %d failed\n", irq); + return ret; } ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index 39485572a76b..02dd1db7a646 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -1435,7 +1435,7 @@ int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chi &kx022a_irq_thread_handler, IRQF_ONESHOT, name, idev); if (ret) - return dev_err_probe(data->dev, ret, "Could not request IRQ\n"); + return ret; ret = devm_iio_trigger_register(dev, indio_trig); if (ret) diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c index 7d9cbfa01360..7e01427fd9c4 100644 --- a/drivers/iio/accel/mma9551.c +++ b/drivers/iio/accel/mma9551.c @@ -420,10 +420,8 @@ static int mma9551_gpio_probe(struct iio_dev *indio_dev) NULL, mma9551_event_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "mma9551_event", indio_dev); - if (ret < 0) { - dev_err(dev, "request irq %d failed\n", data->irqs[i]); + if (ret) return ret; - } dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio), data->irqs[i]); diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index ab43b1e0ff04..8e7aaac89d47 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -1099,11 +1099,8 @@ static int mma9553_probe(struct i2c_client *client) mma9553_event_handler, IRQF_TRIGGER_RISING, "mma9553_event", indio_dev); - if (ret < 0) { - dev_err(&client->dev, "request irq %d failed\n", - client->irq); + if (ret) goto out_poweroff; - } } ret = pm_runtime_set_active(&client->dev); diff --git a/drivers/iio/accel/msa311.c b/drivers/iio/accel/msa311.c index e0e73b87cba8..caf9b1b6397f 100644 --- a/drivers/iio/accel/msa311.c +++ b/drivers/iio/accel/msa311.c @@ -1059,7 +1059,7 @@ static int msa311_setup_interrupts(struct msa311_priv *msa311) msa311_irq_thread, IRQF_ONESHOT, msa311->chip_name, indio_dev); if (err) - return dev_err_probe(dev, err, "failed to request IRQ\n"); + return err; trig = devm_iio_trigger_alloc(dev, "%s-new-data", msa311->chip_name); if (!trig) diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c index 2034fe92bae3..a66fee828ec1 100644 --- a/drivers/iio/accel/mxc4005.c +++ b/drivers/iio/accel/mxc4005.c @@ -489,11 +489,8 @@ static int mxc4005_probe(struct i2c_client *client) iio_trigger_generic_data_rdy_poll, IRQF_TRIGGER_FALLING | IRQF_NO_THREAD, "mxc4005_event", data->dready_trig); - if (ret) { - dev_err(&client->dev, - "failed to init threaded irq\n"); + if (ret) return ret; - } data->dready_trig->ops = &mxc4005_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index ad4d11337f4b..50840c3440c4 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -550,11 +550,8 @@ static int stk8312_probe(struct i2c_client *client) IRQF_ONESHOT, "stk8312_event", indio_dev); - if (ret < 0) { - dev_err(&client->dev, "request irq %d failed\n", - client->irq); + if (ret) goto err_power_off; - } data->dready_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index 54638cd75ea7..6c1e286c0a1d 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -438,11 +438,8 @@ static int stk8ba50_probe(struct i2c_client *client) stk8ba50_data_rdy_trig_poll, IRQF_TRIGGER_RISING | IRQF_NO_THREAD, "stk8ba50_event", indio_dev); - if (ret < 0) { - dev_err(&client->dev, "request irq %d failed\n", - client->irq); + if (ret) goto err_power_off; - } data->dready_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", From 62fc8c097c511bb372f9ec2bb701bbd3ccea3ee2 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 24 Jul 2026 11:03:37 +0800 Subject: [PATCH 344/513] iio: addac: ad74413r: Remove redundant dev_err_probe() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err_probe() calls. Signed-off-by: Pan Chuang Reviewed-by: Andy Shevchenko Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/addac/ad74413r.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index 43bd2079cf6d..2e5a58e48a89 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -1457,7 +1457,7 @@ static int ad74413r_probe(struct spi_device *spi) ret = devm_request_irq(st->dev, spi->irq, ad74413r_adc_data_interrupt, 0, st->chip_info->name, indio_dev); if (ret) - return dev_err_probe(st->dev, ret, "Failed to request irq\n"); + return ret; ret = devm_iio_triggered_buffer_setup(st->dev, indio_dev, &iio_pollfunc_store_time, From 8fe69fa2e4070ca495a69e728ae0dd662a48d4b0 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 24 Jul 2026 11:03:38 +0800 Subject: [PATCH 345/513] iio: chemical: Remove redundant dev_err()/dev_err_probe() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() and devm_request_threaded_irq() automatically log detailed error messages on failure. Remove the now-redundant driver-specific dev_err() and dev_err_probe() calls. Signed-off-by: Pan Chuang Reviewed-by: Andy Shevchenko Reviewed-by: Gustavo Silva # for ens160 Reviewed-by: Maxwell Doose Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/ccs811.c | 4 +--- drivers/iio/chemical/ens160_core.c | 2 +- drivers/iio/chemical/scd30_core.c | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/iio/chemical/ccs811.c b/drivers/iio/chemical/ccs811.c index ce7187ccd706..d126ee12ffe5 100644 --- a/drivers/iio/chemical/ccs811.c +++ b/drivers/iio/chemical/ccs811.c @@ -484,10 +484,8 @@ static int ccs811_probe(struct i2c_client *client) IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "ccs811_irq", indio_dev); - if (ret) { - dev_err(&client->dev, "irq request error %d\n", -ret); + if (ret) goto err_poweroff; - } data->drdy_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", diff --git a/drivers/iio/chemical/ens160_core.c b/drivers/iio/chemical/ens160_core.c index bbc96c4c6283..7c1ffda38b99 100644 --- a/drivers/iio/chemical/ens160_core.c +++ b/drivers/iio/chemical/ens160_core.c @@ -320,7 +320,7 @@ static int ens160_setup_trigger(struct iio_dev *indio_dev, int irq) IRQF_NO_THREAD, indio_dev->name, indio_dev->trig); if (ret) - return dev_err_probe(dev, ret, "failed to request irq\n"); + return ret; return 0; } diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index f85cdd8bd84f..770571c21521 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -686,7 +686,7 @@ static int scd30_setup_trigger(struct iio_dev *indio_dev) IRQF_NO_AUTOEN, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(dev, ret, "failed to request irq\n"); + return ret; return 0; } From 953587cc90322c6e227b5f7e0ab0039b704e43bd Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 24 Jul 2026 11:03:39 +0800 Subject: [PATCH 346/513] iio: st_sensors: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_threaded_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Reviewed-by: Andy Shevchenko Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/common/st_sensors/st_sensors_trigger.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index 8a8ab688d798..7b3e9732cc3e 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -211,10 +211,8 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, irq_trig, sdata->trig->name, sdata->trig); - if (err) { - dev_err(parent, "failed to request trigger IRQ.\n"); + if (err) return err; - } err = devm_iio_trigger_register(parent, sdata->trig); if (err < 0) { From a2a2c3afab3343b7d3bb06fe52a924cdf952f446 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 24 Jul 2026 11:03:40 +0800 Subject: [PATCH 347/513] iio: gyro: adxrs290: Remove redundant dev_err_probe() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err_probe() calls. Signed-off-by: Pan Chuang Reviewed-by: Joshua Crofts Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/adxrs290.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/iio/gyro/adxrs290.c b/drivers/iio/gyro/adxrs290.c index 35928383d4f2..563de2724396 100644 --- a/drivers/iio/gyro/adxrs290.c +++ b/drivers/iio/gyro/adxrs290.c @@ -585,9 +585,8 @@ static int adxrs290_probe_trigger(struct iio_dev *indio_dev) ret = devm_request_irq(&st->spi->dev, st->spi->irq, &iio_trigger_generic_data_rdy_poll, IRQF_NO_THREAD, "adxrs290_irq", st->dready_trig); - if (ret < 0) - return dev_err_probe(&st->spi->dev, ret, - "request irq %d failed\n", st->spi->irq); + if (ret) + return ret; ret = devm_iio_trigger_register(&st->spi->dev, st->dready_trig); if (ret) { From aa624c43a3699fd28d675fd93216fc4e2df67a2f Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 24 Jul 2026 11:03:41 +0800 Subject: [PATCH 348/513] iio: health: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() and devm_request_threaded_irq() automatically log detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Reviewed-by: Andy Shevchenko Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe4403.c | 4 +--- drivers/iio/health/afe4404.c | 4 +--- drivers/iio/health/max30100.c | 4 +--- drivers/iio/health/max30102.c | 4 +--- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 27e32707f089..2a648532242a 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -544,10 +544,8 @@ static int afe4403_probe(struct spi_device *spi) iio_trigger_generic_data_rdy_poll, IRQF_NO_THREAD, AFE4403_DRIVER_NAME, afe->trig); - if (ret) { - dev_err(dev, "Unable to request IRQ\n"); + if (ret) return ret; - } } ret = devm_iio_triggered_buffer_setup(dev, indio_dev, diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 2357e2dd7017..22cd46eca217 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -551,10 +551,8 @@ static int afe4404_probe(struct i2c_client *client) iio_trigger_generic_data_rdy_poll, IRQF_NO_THREAD, AFE4404_DRIVER_NAME, afe->trig); - if (ret) { - dev_err(dev, "Unable to request IRQ\n"); + if (ret) return ret; - } } ret = devm_iio_triggered_buffer_setup(dev, indio_dev, diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c index 97352485f5e2..191540e10c20 100644 --- a/drivers/iio/health/max30100.c +++ b/drivers/iio/health/max30100.c @@ -489,10 +489,8 @@ static int max30100_probe(struct i2c_client *client) NULL, max30100_interrupt_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "max30100_irq", indio_dev); - if (ret) { - dev_err(&client->dev, "request irq (%d) failed\n", client->irq); + if (ret) return ret; - } return iio_device_register(indio_dev); } diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index 0eeaa378b10d..c37316c86f14 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -577,10 +577,8 @@ static int max30102_probe(struct i2c_client *client) NULL, max30102_interrupt_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "max30102_irq", indio_dev); - if (ret) { - dev_err(&client->dev, "request irq (%d) failed\n", client->irq); + if (ret) return ret; - } return iio_device_register(indio_dev); } From ec0c25c9b39b91987c8be86604e44402d29c9541 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 24 Jul 2026 11:03:42 +0800 Subject: [PATCH 349/513] iio: humidity: Remove redundant dev_err()/dev_err_probe() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_threaded_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() and dev_err_probe() calls. Signed-off-by: Pan Chuang Reviewed-by: Andy Shevchenko Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hdc3020.c | 3 +-- drivers/iio/humidity/hts221_buffer.c | 5 +---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/iio/humidity/hdc3020.c b/drivers/iio/humidity/hdc3020.c index 1ae8702ada54..75971c19624a 100644 --- a/drivers/iio/humidity/hdc3020.c +++ b/drivers/iio/humidity/hdc3020.c @@ -843,8 +843,7 @@ static int hdc3020_probe(struct i2c_client *client) IRQF_ONESHOT, "hdc3020", indio_dev); if (ret) - return dev_err_probe(&client->dev, ret, - "Failed to request IRQ\n"); + return ret; } ret = devm_iio_device_register(&data->client->dev, indio_dev); diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c index 4d03db19063e..df12c7a25b2e 100644 --- a/drivers/iio/humidity/hts221_buffer.c +++ b/drivers/iio/humidity/hts221_buffer.c @@ -122,11 +122,8 @@ int hts221_allocate_trigger(struct iio_dev *iio_dev) hts221_trigger_handler_thread, irq_type | IRQF_ONESHOT, hw->name, hw); - if (err) { - dev_err(hw->dev, "failed to request trigger irq %d\n", - hw->irq); + if (err) return err; - } hw->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger", iio_dev->name); From 40d5ff6a6c359a613551019984a265b7ed9b8318 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 24 Jul 2026 11:03:43 +0800 Subject: [PATCH 350/513] iio: imu: Remove redundant dev_err()/dev_err_probe() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_threaded_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() and dev_err_probe() calls. Signed-off-by: Pan Chuang Reviewed-by: Andy Shevchenko Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/imu/bmi270/bmi270_core.c | 2 +- drivers/iio/imu/bmi323/bmi323_core.c | 2 +- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 5 +---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/iio/imu/bmi270/bmi270_core.c b/drivers/iio/imu/bmi270/bmi270_core.c index 2ad230788532..fbcf6bb87acc 100644 --- a/drivers/iio/imu/bmi270/bmi270_core.c +++ b/drivers/iio/imu/bmi270/bmi270_core.c @@ -1434,7 +1434,7 @@ static int bmi270_trigger_probe(struct bmi270_data *data, bmi270_irq_thread_handler, IRQF_ONESHOT, "bmi270-int", indio_dev); if (ret) - return dev_err_probe(data->dev, ret, "Failed to request IRQ\n"); + return ret; ret = devm_iio_trigger_register(data->dev, data->trig); if (ret) diff --git a/drivers/iio/imu/bmi323/bmi323_core.c b/drivers/iio/imu/bmi323/bmi323_core.c index 72955a697a93..ebeb1b10c38c 100644 --- a/drivers/iio/imu/bmi323/bmi323_core.c +++ b/drivers/iio/imu/bmi323/bmi323_core.c @@ -1946,7 +1946,7 @@ static int bmi323_trigger_probe(struct bmi323_data *data, bmi323_irq_thread_handler, IRQF_ONESHOT, "bmi323-int", indio_dev); if (ret) - return dev_err_probe(data->dev, ret, "Failed to request IRQ\n"); + return ret; ret = devm_iio_trigger_register(data->dev, data->trig); if (ret) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 630e2cae6f19..6052191ffda5 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -2833,11 +2833,8 @@ static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw) st_lsm6dsx_handler_thread, irq_type | IRQF_ONESHOT, "lsm6dsx", hw); - if (err) { - dev_err(hw->dev, "failed to request trigger irq %d\n", - hw->irq); + if (err) return err; - } return 0; } From b182dbeda98f1ad61754c64c1a213360e5315652 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 24 Jul 2026 11:03:44 +0800 Subject: [PATCH 351/513] iio: magnetometer: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_threaded_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Reviewed-by: Andy Shevchenko Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8974.c | 5 +---- drivers/iio/magnetometer/rm3100-core.c | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c index c7fdb7c2f543..28d6cadfd9f8 100644 --- a/drivers/iio/magnetometer/ak8974.c +++ b/drivers/iio/magnetometer/ak8974.c @@ -926,11 +926,8 @@ static int ak8974_probe(struct i2c_client *i2c) irq_trig, ak8974->name, ak8974); - if (ret) { - dev_err(&i2c->dev, "unable to request DRDY IRQ " - "- proceeding without IRQ\n"); + if (ret) goto no_irq; - } ak8974->drdy_irq = true; } diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c index ac3f9f7fc808..46fce2978d76 100644 --- a/drivers/iio/magnetometer/rm3100-core.c +++ b/drivers/iio/magnetometer/rm3100-core.c @@ -568,10 +568,8 @@ int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq) IRQF_ONESHOT, indio_dev->name, indio_dev); - if (ret < 0) { - dev_err(dev, "request irq line failed.\n"); + if (ret) return ret; - } data->drdy_trig = devm_iio_trigger_alloc(dev, "%s-drdy%d", indio_dev->name, From bd402b8ac0158dc6c7c04eb308095c9574813854 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 24 Jul 2026 11:03:45 +0800 Subject: [PATCH 352/513] iio: pressure: Remove redundant dev_err()/dev_err_probe() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() and devm_request_threaded_irq() automatically log detailed error messages on failure. Remove the now-redundant driver-specific dev_err() and dev_err_probe() calls. Signed-off-by: Pan Chuang Reviewed-by: Andy Shevchenko Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 2 +- drivers/iio/pressure/dlhl60d.c | 4 +--- drivers/iio/pressure/rohm-bm1390.c | 2 +- drivers/iio/pressure/zpa2326.c | 5 +---- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index c89153dcc323..1066792b9087 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -1339,7 +1339,7 @@ static int __bmp280_trigger_probe(struct iio_dev *indio_dev, irq_thread_handler, IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(dev, ret, "request IRQ failed.\n"); + return ret; ret = devm_iio_trigger_register(data->dev, data->trig); if (ret) diff --git a/drivers/iio/pressure/dlhl60d.c b/drivers/iio/pressure/dlhl60d.c index 01a873165923..961888fd03a7 100644 --- a/drivers/iio/pressure/dlhl60d.c +++ b/drivers/iio/pressure/dlhl60d.c @@ -309,10 +309,8 @@ static int dlh_probe(struct i2c_client *client) ret = devm_request_irq(&client->dev, client->irq, dlh_interrupt, IRQF_TRIGGER_RISING | IRQF_NO_THREAD, st->info->name, indio_dev); - if (ret) { - dev_err(&client->dev, "failed to allocate threaded irq"); + if (ret) return ret; - } st->use_interrupt = true; init_completion(&st->completion); diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c index 9d72ae64126c..57941fb4a535 100644 --- a/drivers/iio/pressure/rohm-bm1390.c +++ b/drivers/iio/pressure/rohm-bm1390.c @@ -805,7 +805,7 @@ static int bm1390_setup_trigger(struct bm1390_data *data, struct iio_dev *idev, &bm1390_irq_thread_handler, IRQF_ONESHOT, name, idev); if (ret) - return dev_err_probe(data->dev, ret, "Could not request IRQ\n"); + return ret; ret = devm_iio_trigger_register(data->dev, itrig); diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index 2c68fdf2744e..b38493ff3b8b 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -911,11 +911,8 @@ static int zpa2326_init_managed_irq(struct device *parent, zpa2326_handle_threaded_irq, IRQF_TRIGGER_RISING | IRQF_ONESHOT, dev_name(parent), indio_dev); - if (err) { - dev_err(parent, "failed to request interrupt %d (%d)", irq, - err); + if (err) return err; - } dev_info(parent, "using interrupt %d", irq); From 7963090626a5de900d2404e451234ea098012ba2 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 24 Jul 2026 11:03:46 +0800 Subject: [PATCH 353/513] iio: proximity: Remove redundant dev_err()/dev_err_probe() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() and devm_request_threaded_irq() automatically log detailed error messages on failure. Remove the now-redundant driver-specific dev_err() and dev_err_probe() calls. Standardize error check on if (ret) rather than if (ret < 0) Signed-off-by: Pan Chuang Reviewed-by: Andy Shevchenko Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/as3935.c | 4 +--- drivers/iio/proximity/d3323aa.c | 2 +- drivers/iio/proximity/hx9023s.c | 2 +- drivers/iio/proximity/irsd200.c | 2 +- drivers/iio/proximity/mb1232.c | 4 +--- drivers/iio/proximity/srf04.c | 4 +--- drivers/iio/proximity/sx_common.c | 2 +- drivers/iio/proximity/vcnl3020.c | 6 +----- drivers/iio/proximity/vl53l0x-i2c.c | 4 +--- 9 files changed, 9 insertions(+), 21 deletions(-) diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index 93833037bc94..d7f43c3af165 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -428,10 +428,8 @@ static int as3935_probe(struct spi_device *spi) dev_name(dev), indio_dev); - if (ret) { - dev_err(dev, "unable to request irq\n"); + if (ret) return ret; - } ret = devm_iio_device_register(dev, indio_dev); if (ret < 0) { diff --git a/drivers/iio/proximity/d3323aa.c b/drivers/iio/proximity/d3323aa.c index d40e3dff9eb1..1f43c1fed342 100644 --- a/drivers/iio/proximity/d3323aa.c +++ b/drivers/iio/proximity/d3323aa.c @@ -771,7 +771,7 @@ static int d3323aa_probe(struct platform_device *pdev) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, dev_name(dev), indio_dev); if (ret) - return dev_err_probe(dev, ret, "Could not request IRQ\n"); + return ret; ret = d3323aa_setup(indio_dev, D3323AA_LP_FILTER_FREQ_DEFAULT_IDX, D3323AA_FILTER_GAIN_DEFAULT_IDX, diff --git a/drivers/iio/proximity/hx9023s.c b/drivers/iio/proximity/hx9023s.c index a6ff7cbe9e65..0199a6351a46 100644 --- a/drivers/iio/proximity/hx9023s.c +++ b/drivers/iio/proximity/hx9023s.c @@ -1139,7 +1139,7 @@ static int hx9023s_probe(struct i2c_client *client) IRQF_ONESHOT, "hx9023s_event", indio_dev); if (ret) - return dev_err_probe(dev, ret, "irq request failed\n"); + return ret; data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, diff --git a/drivers/iio/proximity/irsd200.c b/drivers/iio/proximity/irsd200.c index 65af31d43453..b3db7f3a49f6 100644 --- a/drivers/iio/proximity/irsd200.c +++ b/drivers/iio/proximity/irsd200.c @@ -910,7 +910,7 @@ static int irsd200_probe(struct i2c_client *client) IRQF_TRIGGER_RISING | IRQF_ONESHOT, NULL, indio_dev); if (ret) - return dev_err_probe(data->dev, ret, "Could not request irq\n"); + return ret; trigger = devm_iio_trigger_alloc(data->dev, "%s-dev%d", indio_dev->name, iio_device_id(indio_dev)); diff --git a/drivers/iio/proximity/mb1232.c b/drivers/iio/proximity/mb1232.c index eab881b0cdc7..accfc296e1f0 100644 --- a/drivers/iio/proximity/mb1232.c +++ b/drivers/iio/proximity/mb1232.c @@ -213,10 +213,8 @@ static int mb1232_probe(struct i2c_client *client) if (data->irqnr > 0) { ret = devm_request_irq(dev, data->irqnr, mb1232_handle_irq, IRQF_TRIGGER_FALLING, id->name, indio_dev); - if (ret < 0) { - dev_err(dev, "request_irq: %d\n", ret); + if (ret) return ret; - } } ret = devm_iio_triggered_buffer_setup(dev, indio_dev, diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c index 7be50bdebfcb..01fbde4d9d36 100644 --- a/drivers/iio/proximity/srf04.c +++ b/drivers/iio/proximity/srf04.c @@ -303,10 +303,8 @@ static int srf04_probe(struct platform_device *pdev) ret = devm_request_irq(dev, data->irqnr, srf04_handle_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, pdev->name, indio_dev); - if (ret < 0) { - dev_err(data->dev, "request_irq: %d\n", ret); + if (ret) return ret; - } platform_set_drvdata(pdev, indio_dev); diff --git a/drivers/iio/proximity/sx_common.c b/drivers/iio/proximity/sx_common.c index 59b35e40739b..4d4937395d9a 100644 --- a/drivers/iio/proximity/sx_common.c +++ b/drivers/iio/proximity/sx_common.c @@ -517,7 +517,7 @@ int sx_common_probe(struct i2c_client *client, IRQF_ONESHOT, "sx_event", indio_dev); if (ret) - return dev_err_probe(dev, ret, "No IRQ\n"); + return ret; data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, diff --git a/drivers/iio/proximity/vcnl3020.c b/drivers/iio/proximity/vcnl3020.c index 7f417372566a..6b0d7a9038d9 100644 --- a/drivers/iio/proximity/vcnl3020.c +++ b/drivers/iio/proximity/vcnl3020.c @@ -638,12 +638,8 @@ static int vcnl3020_probe(struct i2c_client *client) NULL, vcnl3020_handle_irq_thread, IRQF_ONESHOT, indio_dev->name, indio_dev); - if (rc) { - dev_err(&client->dev, - "Error (%d) irq request failed (%u)\n", rc, - client->irq); + if (rc) return rc; - } } return devm_iio_device_register(&client->dev, indio_dev); diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c index b7127c3c52d2..9fe14ceb8be7 100644 --- a/drivers/iio/proximity/vl53l0x-i2c.c +++ b/drivers/iio/proximity/vl53l0x-i2c.c @@ -127,10 +127,8 @@ static int vl53l0x_configure_irq(struct i2c_client *client, ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, vl53l0x_threaded_irq, irq_flags | IRQF_ONESHOT, indio_dev->name, indio_dev); - if (ret) { - dev_err(&client->dev, "devm_request_irq error: %d\n", ret); + if (ret) return ret; - } ret = i2c_smbus_write_byte_data(data->client, VL_REG_SYSTEM_INTERRUPT_CONFIG_GPIO, From 9cf89c342bc2508b79b4ab8823df90da8f77f1f1 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 24 Jul 2026 11:03:47 +0800 Subject: [PATCH 354/513] iio: light: Remove redundant dev_err()/dev_err_probe() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() and devm_request_threaded_irq() automatically log detailed error messages on failure. Remove the now-redundant driver-specific dev_err() and dev_err_probe() calls. Standardize error check on if (ret) rather than if (ret < 0). Signed-off-by: Pan Chuang Reviewed-by: Andy Shevchenko Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/adux1020.c | 4 +--- drivers/iio/light/apds9160.c | 7 ++----- drivers/iio/light/apds9300.c | 4 +--- drivers/iio/light/apds9306.c | 3 +-- drivers/iio/light/apds9960.c | 4 +--- drivers/iio/light/bh1745.c | 3 +-- drivers/iio/light/cm3605.c | 4 +--- drivers/iio/light/gp2ap002.c | 4 +--- drivers/iio/light/ltr390.c | 3 +-- drivers/iio/light/ltr501.c | 5 +---- drivers/iio/light/opt4060.c | 2 +- drivers/iio/light/rpr0521.c | 5 +---- drivers/iio/light/si1145.c | 4 +--- drivers/iio/light/st_uvis25_core.c | 5 +---- drivers/iio/light/stk3310.c | 5 +---- drivers/iio/light/tsl2563.c | 2 +- drivers/iio/light/tsl2772.c | 5 +---- drivers/iio/light/veml6030.c | 4 +--- drivers/iio/light/vl6180.c | 2 +- 19 files changed, 20 insertions(+), 55 deletions(-) diff --git a/drivers/iio/light/adux1020.c b/drivers/iio/light/adux1020.c index 633a105fd7f0..0ea282ecc99a 100644 --- a/drivers/iio/light/adux1020.c +++ b/drivers/iio/light/adux1020.c @@ -808,10 +808,8 @@ static int adux1020_probe(struct i2c_client *client) NULL, adux1020_interrupt_handler, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, ADUX1020_DRV_NAME, indio_dev); - if (ret) { - dev_err(&client->dev, "irq request error %d\n", -ret); + if (ret) return ret; - } } return devm_iio_device_register(&client->dev, indio_dev); diff --git a/drivers/iio/light/apds9160.c b/drivers/iio/light/apds9160.c index 8dacb1730429..d7e0924b8106 100644 --- a/drivers/iio/light/apds9160.c +++ b/drivers/iio/light/apds9160.c @@ -1545,11 +1545,8 @@ static int apds9160_probe(struct i2c_client *client) apds9160_irq_handler, IRQF_ONESHOT, "apds9160_event", indio_dev); - if (ret) { - return dev_err_probe(dev, ret, - "request irq (%d) failed\n", - client->irq); - } + if (ret) + return ret; } else { indio_dev->info = &apds9160_info_no_events; indio_dev->channels = apds9160_channels_without_events; diff --git a/drivers/iio/light/apds9300.c b/drivers/iio/light/apds9300.c index d60ade1209f3..f0abbc4a862a 100644 --- a/drivers/iio/light/apds9300.c +++ b/drivers/iio/light/apds9300.c @@ -432,10 +432,8 @@ static int apds9300_probe(struct i2c_client *client) NULL, apds9300_interrupt_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "apds9300_event", indio_dev); - if (ret) { - dev_err(&client->dev, "irq request error %d\n", -ret); + if (ret) goto err; - } } ret = iio_device_register(indio_dev); diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c index 5ca4c87524fe..697cb36b975b 100644 --- a/drivers/iio/light/apds9306.c +++ b/drivers/iio/light/apds9306.c @@ -1286,8 +1286,7 @@ static int apds9306_probe(struct i2c_client *client) apds9306_irq_handler, IRQF_ONESHOT, "apds9306_event", indio_dev); if (ret) - return dev_err_probe(dev, ret, - "failed to assign interrupt.\n"); + return ret; } else { indio_dev->info = &apds9306_info_no_events; indio_dev->channels = apds9306_channels_without_events; diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c index 2686c3b0c03b..afe520de60e2 100644 --- a/drivers/iio/light/apds9960.c +++ b/drivers/iio/light/apds9960.c @@ -1098,10 +1098,8 @@ static int apds9960_probe(struct i2c_client *client) IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "apds9960_event", indio_dev); - if (ret) { - dev_err(&client->dev, "request irq (%d) failed\n", client->irq); + if (ret) goto error_power_down; - } ret = iio_device_register(indio_dev); if (ret) diff --git a/drivers/iio/light/bh1745.c b/drivers/iio/light/bh1745.c index 0aa8e5cc6c56..2b8ff36071b8 100644 --- a/drivers/iio/light/bh1745.c +++ b/drivers/iio/light/bh1745.c @@ -784,8 +784,7 @@ static int bh1745_setup_triggered_buffer(struct iio_dev *indio_dev, IRQF_ONESHOT, "bh1745_interrupt", indio_dev); if (ret) - return dev_err_probe(dev, ret, - "Request for IRQ failed\n"); + return ret; } return 0; diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c index 9f799041edf9..34c127593947 100644 --- a/drivers/iio/light/cm3605.c +++ b/drivers/iio/light/cm3605.c @@ -232,10 +232,8 @@ static int cm3605_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(dev, irq, cm3605_prox_irq, NULL, 0, "cm3605", indio_dev); - if (ret) { - dev_err(dev, "unable to request IRQ\n"); + if (ret) goto out_disable_aset; - } /* Just name the trigger the same as the driver */ led_trigger_register_simple("cm3605", &cm3605->led); diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index 6c532898f6e4..cc7d3632060f 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -573,10 +573,8 @@ static int gp2ap002_probe(struct i2c_client *client) ret = devm_request_threaded_irq(dev, client->irq, NULL, gp2ap002_prox_irq, IRQF_ONESHOT, "gp2ap002", indio_dev); - if (ret) { - dev_err(dev, "unable to request IRQ\n"); + if (ret) goto out_put_pm; - } gp2ap002->irq = client->irq; /* diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c index bdc74b8226c8..bc031f2c3141 100644 --- a/drivers/iio/light/ltr390.c +++ b/drivers/iio/light/ltr390.c @@ -838,8 +838,7 @@ static int ltr390_probe(struct i2c_client *client) "ltr390_thresh_event", indio_dev); if (ret) - return dev_err_probe(dev, ret, - "request irq (%d) failed\n", client->irq); + return ret; } ret = ltr390_pm_init(data); diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 7d045be78c6d..fa0d16890c8b 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -1537,11 +1537,8 @@ static int ltr501_probe(struct i2c_client *client) IRQF_ONESHOT, "ltr501_thresh_event", indio_dev); - if (ret) { - dev_err(&client->dev, "request irq (%d) failed\n", - client->irq); + if (ret) return ret; - } } else { indio_dev->info = data->chip_info->info_no_irq; } diff --git a/drivers/iio/light/opt4060.c b/drivers/iio/light/opt4060.c index c391ad3271c6..98e644192ce3 100644 --- a/drivers/iio/light/opt4060.c +++ b/drivers/iio/light/opt4060.c @@ -1200,7 +1200,7 @@ static int opt4060_setup_trigger(struct opt4060_chip *chip, struct iio_dev *idev IRQF_TRIGGER_FALLING | IRQF_ONESHOT, name, idev); if (ret) - return dev_err_probe(chip->dev, ret, "Could not request IRQ\n"); + return ret; init_completion(&chip->completion); diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index e0914f699dfd..7d82efc31f3a 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -987,11 +987,8 @@ static int rpr0521_probe(struct i2c_client *client) rpr0521_drdy_irq_handler, rpr0521_drdy_irq_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rpr0521_event", indio_dev); - if (ret < 0) { - dev_err(&client->dev, "request irq %d for trigger0 failed\n", - client->irq); + if (ret) goto err_pm_disable; - } ret = devm_iio_trigger_register(indio_dev->dev.parent, data->drdy_trigger0); diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c index 4601ae5d2009..bb7c99041819 100644 --- a/drivers/iio/light/si1145.c +++ b/drivers/iio/light/si1145.c @@ -1251,10 +1251,8 @@ static int si1145_probe_trigger(struct iio_dev *indio_dev) IRQF_TRIGGER_FALLING | IRQF_NO_THREAD, "si1145_irq", trig); - if (ret < 0) { - dev_err(&client->dev, "irq request failed\n"); + if (ret) return ret; - } ret = devm_iio_trigger_register(&client->dev, trig); if (ret) diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c index bcd729a9924e..94d5261b2663 100644 --- a/drivers/iio/light/st_uvis25_core.c +++ b/drivers/iio/light/st_uvis25_core.c @@ -196,11 +196,8 @@ static int st_uvis25_allocate_trigger(struct iio_dev *iio_dev) st_uvis25_trigger_handler_thread, irq_type | IRQF_ONESHOT, iio_dev->name, hw); - if (err) { - dev_err(dev, "failed to request trigger irq %d\n", - hw->irq); + if (err) return err; - } hw->trig = devm_iio_trigger_alloc(dev, "%s-trigger", iio_dev->name); diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index e7ce6f32592b..7c8a1d2b2ed0 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -673,11 +673,8 @@ static int stk3310_probe(struct i2c_client *client) IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "stk3310_event", indio_dev); - if (ret < 0) { - dev_err(&client->dev, "request irq %d failed\n", - client->irq); + if (ret) goto err_standby; - } } ret = iio_device_register(indio_dev); diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index 45f3513d931e..7bef0c3118c2 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -754,7 +754,7 @@ static int tsl2563_probe(struct i2c_client *client) "tsl2563_event", indio_dev); if (err) - return dev_err_probe(dev, err, "irq request error\n"); + return err; } err = tsl2563_configure(chip); diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c index 244f44379c36..83ef7c665146 100644 --- a/drivers/iio/light/tsl2772.c +++ b/drivers/iio/light/tsl2772.c @@ -1849,11 +1849,8 @@ static int tsl2772_probe(struct i2c_client *clientp) IRQF_ONESHOT, "TSL2772_event", indio_dev); - if (ret) { - dev_err(&clientp->dev, - "%s: irq request failed\n", __func__); + if (ret) return ret; - } } else { indio_dev->channels = chip->chip_info->channel_without_events; } diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c index 7c837d60dad8..ec9c127e7d8a 100644 --- a/drivers/iio/light/veml6030.c +++ b/drivers/iio/light/veml6030.c @@ -921,9 +921,7 @@ static int veml6030_set_info(struct iio_dev *indio_dev) IRQF_TRIGGER_LOW | IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret < 0) - return dev_err_probe(&client->dev, ret, - "irq %d request failed\n", - client->irq); + return ret; indio_dev->info = &veml6030_info; } else { diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c index 4f270f405b21..aef8a5333442 100644 --- a/drivers/iio/light/vl6180.c +++ b/drivers/iio/light/vl6180.c @@ -721,7 +721,7 @@ static int vl6180_probe(struct i2c_client *client) IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) - return dev_err_probe(&client->dev, ret, "devm_request_irq error\n"); + return ret; init_completion(&data->completion); From e7dddb2a85e4199f4022a0246a5e3b9d551c2007 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 24 Jul 2026 11:03:48 +0800 Subject: [PATCH 355/513] iio: temperature: Remove redundant dev_err()/dev_err_probe() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() and devm_request_threaded_irq() automatically log detailed error messages on failure. Remove the now-redundant driver-specific dev_err() and dev_err_probe() calls. Signed-off-by: Pan Chuang Reviewed-by: Andy Shevchenko Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/ltc2983.c | 2 +- drivers/iio/temperature/tmp007.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index a9d38ed45da6..6efb5252a773 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -1994,7 +1994,7 @@ static int ltc2983_probe(struct spi_device *spi) ret = devm_request_irq(dev, spi->irq, ltc2983_irq_handler, IRQF_TRIGGER_RISING, st->info->name, st); if (ret) - return dev_err_probe(dev, ret, "failed to request an irq\n"); + return ret; if (st->info->has_eeprom) { ret = ltc2983_eeprom_cmd(st, LTC2983_EEPROM_WRITE_CMD, diff --git a/drivers/iio/temperature/tmp007.c b/drivers/iio/temperature/tmp007.c index 2f6ff87d2a37..4e743424f88e 100644 --- a/drivers/iio/temperature/tmp007.c +++ b/drivers/iio/temperature/tmp007.c @@ -527,10 +527,8 @@ static int tmp007_probe(struct i2c_client *client) NULL, tmp007_interrupt_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, tmp007_id->name, indio_dev); - if (ret) { - dev_err(&client->dev, "irq request error %d\n", -ret); + if (ret) return ret; - } } return devm_iio_device_register(&client->dev, indio_dev); From f648c716c49dbd674101b60de0ad7dd1ebc7017c Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Fri, 24 Jul 2026 13:03:11 +0100 Subject: [PATCH 356/513] dt-bindings: iio: dac: ad5696: add AD5673R/AD5677R support Add compatible entries for AD5673R and AD5677R. These 16-channel devices have different bit resolutions so no fallback compatibles are used. Signed-off-by: Rodrigo Alencar Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml index e10f8596f9d3..5a93edc976f0 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml @@ -19,7 +19,9 @@ properties: - adi,ad5337r - adi,ad5338r - adi,ad5671r + - adi,ad5673r - adi,ad5675r + - adi,ad5677r - adi,ad5691r - adi,ad5692r - adi,ad5693 From 3e8c4da1ba94e8dec8d18984fea1a9016449e0df Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Fri, 24 Jul 2026 13:03:12 +0100 Subject: [PATCH 357/513] iio: dac: ad5696: add AD5673R/AD5677R entries to of_match table Populate of_match table with AD5673R/AD5677R entries, already supported by the driver and available in the id_table. Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5696-i2c.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index 046f904f7c88..bc4e76789d30 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -104,7 +104,9 @@ static const struct of_device_id ad5686_of_match[] = { { .compatible = "adi,ad5337r", .data = &ad5337r_chip_info }, { .compatible = "adi,ad5338r", .data = &ad5338r_chip_info }, { .compatible = "adi,ad5671r", .data = &ad5672r_chip_info }, + { .compatible = "adi,ad5673r", .data = &ad5674r_chip_info }, { .compatible = "adi,ad5675r", .data = &ad5676r_chip_info }, + { .compatible = "adi,ad5677r", .data = &ad5679r_chip_info }, { .compatible = "adi,ad5691r", .data = &ad5681r_chip_info }, { .compatible = "adi,ad5692r", .data = &ad5682r_chip_info }, { .compatible = "adi,ad5693", .data = &ad5683_chip_info }, From d6afd59fd32197474aa3672e4b6b14b1483fa176 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Fri, 24 Jul 2026 13:03:13 +0100 Subject: [PATCH 358/513] iio: dac: ad5686: refactor device list and file header comments Refactor device list in Kconfig, sorting devices by channel count. Also, remove device description from file headers. One can rely on the ID table(s) to check for supported devices. AD5337R is ommited as it does not seem to exist, i.e. its documentation could not be found. Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 29 ++++++++++++++++++++++------- drivers/iio/dac/ad5686-spi.c | 5 +---- drivers/iio/dac/ad5686.c | 2 +- drivers/iio/dac/ad5696-i2c.c | 4 +--- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index d6d560c09e25..3ae997939ff5 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -251,9 +251,16 @@ config AD5686_SPI depends on SPI select AD5686 help - Say yes here to build support for Analog Devices AD5672R, AD5674R, - AD5676, AD5676R, AD5679R, AD5684, AD5684R, AD5684R, AD5685R, AD5686, - AD5686R Voltage Output Digital to Analog Converter. + Say yes here to build support for Analog Devices Voltage Output + Digital to Analog Converters: + - Single-channel: + AD5310R, AD5681R, AD5682R, AD5683, AD5683R + - Quad-channel: + AD5684, AD5684R, AD5685R, AD5686, AD5686R + - 8-channel: + AD5672R, AD5676, AD5676R + - 16-channel: + AD5674R, AD5679R To compile this driver as a module, choose M here: the module will be called ad5686. @@ -263,10 +270,18 @@ config AD5696_I2C depends on I2C select AD5686 help - Say yes here to build support for Analog Devices AD5311R, AD5337, - AD5338R, AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693, - AD5693R, AD5694, AD5694R, AD5695R, AD5696, and AD5696R Digital to - Analog converters. + Say yes here to build support for Analog Devices Voltage Output + Digital to Analog Converters: + - Single-channel: + AD5311R, AD5691R, AD5692R, AD5693, AD5693R + - Dual-channel: + AD5338R + - Quad-channel: + AD5694, AD5694R, AD5695R, AD5696, AD5696R + - 8-channel: + AD5671R, AD5675R + - 16-channel: + AD5673R, AD5677R To compile this driver as a module, choose M here: the module will be called ad5696. diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c index 1877d7cd2155..f983ed6560ea 100644 --- a/drivers/iio/dac/ad5686-spi.c +++ b/drivers/iio/dac/ad5686-spi.c @@ -1,9 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * AD5672R, AD5674R, AD5676, AD5676R, AD5679R, - * AD5681R, AD5682R, AD5683, AD5683R, AD5684, - * AD5684R, AD5685R, AD5686, AD5686R - * Digital to analog converters driver + * SPI driver for AD5686 and similar Digital to Analog Converters * * Copyright 2018 Analog Devices Inc. */ diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 873dfb5b9107..e2beba6d54b1 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * AD5686R, AD5685R, AD5684R Digital to analog converters driver + * Core driver for AD5686 and similar Digital to Analog Converters * * Copyright 2011 Analog Devices Inc. */ diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index bc4e76789d30..7064e294819b 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * AD5338R, AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693, - * AD5693R, AD5694, AD5694R, AD5695R, AD5696, AD5696R - * Digital to analog converters driver + * I2C driver for AD5696 and similar Digital to Analog Converters * * Copyright 2018 Analog Devices Inc. */ From cda84e7b247aa62d5ac6ef07ddfebbab3e4e95e8 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Fri, 24 Jul 2026 13:03:14 +0100 Subject: [PATCH 359/513] dt-bindings: iio: dac: ad5696: extend device support Add support for AD5316R, AD5675 and AD5697R. These devices have different bit resolutions or different number of channels so no fallback compatibles are used. Signed-off-by: Rodrigo Alencar Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml index 5a93edc976f0..835fa21c474e 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml @@ -16,10 +16,12 @@ properties: compatible: enum: - adi,ad5311r + - adi,ad5316r - adi,ad5337r - adi,ad5338r - adi,ad5671r - adi,ad5673r + - adi,ad5675 - adi,ad5675r - adi,ad5677r - adi,ad5691r @@ -31,6 +33,7 @@ properties: - adi,ad5695r - adi,ad5696 - adi,ad5696r + - adi,ad5697r reg: maxItems: 1 @@ -86,6 +89,7 @@ allOf: compatible: contains: enum: + - adi,ad5675 - adi,ad5693 - adi,ad5694 - adi,ad5696 From 70de3868ebba924926cac30e35c2e2739a30a31d Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Fri, 24 Jul 2026 13:03:15 +0100 Subject: [PATCH 360/513] dt-bindings: iio: dac: ad5686: extend device support Add compatible entries for AD5313R, AD5317R, AD5674, AD5679, AD5687, AD5687R, AD5689, AD5689R. These devices have unique combination of channel count, bit resolution and supported command set, so that fallback compatibles are not used. Also, a small copy-and-paste error is fixed to the title field. Acked-by: Conor Dooley Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/dac/adi,ad5686.yaml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml index 02e8c78e36d3..d1bb09054440 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/iio/dac/adi,ad5686.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Analog Devices AD5360 and similar SPI DACs +title: Analog Devices AD5686 and similar SPI DACs maintainers: - Michael Hennerich @@ -14,10 +14,14 @@ properties: compatible: enum: - adi,ad5310r + - adi,ad5313r + - adi,ad5317r - adi,ad5672r + - adi,ad5674 - adi,ad5674r - adi,ad5676 - adi,ad5676r + - adi,ad5679 - adi,ad5679r - adi,ad5681r - adi,ad5682r @@ -28,6 +32,10 @@ properties: - adi,ad5685r - adi,ad5686 - adi,ad5686r + - adi,ad5687 + - adi,ad5687r + - adi,ad5689 + - adi,ad5689r reg: maxItems: 1 @@ -84,10 +92,14 @@ allOf: compatible: contains: enum: + - adi,ad5674 - adi,ad5676 + - adi,ad5679 - adi,ad5683 - adi,ad5684 - adi,ad5686 + - adi,ad5687 + - adi,ad5689 then: required: - vref-supply From b18d51935a4292233c9fb4d3e4737fe92cda4014 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Fri, 24 Jul 2026 13:03:16 +0100 Subject: [PATCH 361/513] iio: dac: ad5686: extend device support with new parts Add support for AD5313R, AD5317R, AD5674, AD5679, AD5687, AD5687R, AD5689, AD5689R to the AD5686 SPI driver. Also adding support for AD5316R, AD5675, AD5697R to the AD5696 I2C driver. This includes the creation of seven chip info struct instances and reuse of existing ones. Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 12 ++++---- drivers/iio/dac/ad5686-spi.c | 16 +++++++++++ drivers/iio/dac/ad5686.c | 55 ++++++++++++++++++++++++++++++++++++ drivers/iio/dac/ad5686.h | 7 +++++ drivers/iio/dac/ad5696-i2c.c | 6 ++++ 5 files changed, 91 insertions(+), 5 deletions(-) diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 3ae997939ff5..18a27785e28b 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -255,12 +255,14 @@ config AD5686_SPI Digital to Analog Converters: - Single-channel: AD5310R, AD5681R, AD5682R, AD5683, AD5683R + - Dual-channel: + AD5313R, AD5687, AD5687R, AD5689, AD5689R - Quad-channel: - AD5684, AD5684R, AD5685R, AD5686, AD5686R + AD5317R, AD5684, AD5684R, AD5685R, AD5686, AD5686R - 8-channel: AD5672R, AD5676, AD5676R - 16-channel: - AD5674R, AD5679R + AD5674, AD5674R, AD5679, AD5679R To compile this driver as a module, choose M here: the module will be called ad5686. @@ -275,11 +277,11 @@ config AD5696_I2C - Single-channel: AD5311R, AD5691R, AD5692R, AD5693, AD5693R - Dual-channel: - AD5338R + AD5338R, AD5697R - Quad-channel: - AD5694, AD5694R, AD5695R, AD5696, AD5696R + AD5316R, AD5694, AD5694R, AD5695R, AD5696, AD5696R - 8-channel: - AD5671R, AD5675R + AD5671R, AD5675, AD5675R - 16-channel: AD5673R, AD5677R diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c index f983ed6560ea..69cd9753f339 100644 --- a/drivers/iio/dac/ad5686-spi.c +++ b/drivers/iio/dac/ad5686-spi.c @@ -175,10 +175,14 @@ static int ad5686_spi_probe(struct spi_device *spi) static const struct spi_device_id ad5686_spi_id[] = { { .name = "ad5310r", .driver_data = (kernel_ulong_t)&ad5310r_chip_info }, + { .name = "ad5313r", .driver_data = (kernel_ulong_t)&ad5338r_chip_info }, + { .name = "ad5317r", .driver_data = (kernel_ulong_t)&ad5317r_chip_info }, { .name = "ad5672r", .driver_data = (kernel_ulong_t)&ad5672r_chip_info }, + { .name = "ad5674", .driver_data = (kernel_ulong_t)&ad5674_chip_info }, { .name = "ad5674r", .driver_data = (kernel_ulong_t)&ad5674r_chip_info }, { .name = "ad5676", .driver_data = (kernel_ulong_t)&ad5676_chip_info }, { .name = "ad5676r", .driver_data = (kernel_ulong_t)&ad5676r_chip_info }, + { .name = "ad5679", .driver_data = (kernel_ulong_t)&ad5679_chip_info }, { .name = "ad5679r", .driver_data = (kernel_ulong_t)&ad5679r_chip_info }, { .name = "ad5681r", .driver_data = (kernel_ulong_t)&ad5681r_chip_info }, { .name = "ad5682r", .driver_data = (kernel_ulong_t)&ad5682r_chip_info }, @@ -190,16 +194,24 @@ static const struct spi_device_id ad5686_spi_id[] = { { .name = "ad5685r", .driver_data = (kernel_ulong_t)&ad5685r_chip_info }, { .name = "ad5686", .driver_data = (kernel_ulong_t)&ad5686_chip_info }, { .name = "ad5686r", .driver_data = (kernel_ulong_t)&ad5686r_chip_info }, + { .name = "ad5687", .driver_data = (kernel_ulong_t)&ad5687_chip_info }, + { .name = "ad5687r", .driver_data = (kernel_ulong_t)&ad5687r_chip_info }, + { .name = "ad5689", .driver_data = (kernel_ulong_t)&ad5689_chip_info }, + { .name = "ad5689r", .driver_data = (kernel_ulong_t)&ad5689r_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad5686_spi_id); static const struct of_device_id ad5686_of_match[] = { { .compatible = "adi,ad5310r", .data = &ad5310r_chip_info }, + { .compatible = "adi,ad5313r", .data = &ad5338r_chip_info }, + { .compatible = "adi,ad5317r", .data = &ad5317r_chip_info }, { .compatible = "adi,ad5672r", .data = &ad5672r_chip_info }, + { .compatible = "adi,ad5674", .data = &ad5674_chip_info }, { .compatible = "adi,ad5674r", .data = &ad5674r_chip_info }, { .compatible = "adi,ad5676", .data = &ad5676_chip_info }, { .compatible = "adi,ad5676r", .data = &ad5676r_chip_info }, + { .compatible = "adi,ad5679", .data = &ad5679_chip_info }, { .compatible = "adi,ad5679r", .data = &ad5679r_chip_info }, { .compatible = "adi,ad5681r", .data = &ad5681r_chip_info }, { .compatible = "adi,ad5682r", .data = &ad5682r_chip_info }, @@ -210,6 +222,10 @@ static const struct of_device_id ad5686_of_match[] = { { .compatible = "adi,ad5685r", .data = &ad5685r_chip_info }, { .compatible = "adi,ad5686", .data = &ad5686_chip_info }, { .compatible = "adi,ad5686r", .data = &ad5686r_chip_info }, + { .compatible = "adi,ad5687", .data = &ad5687_chip_info }, + { .compatible = "adi,ad5687r", .data = &ad5687r_chip_info }, + { .compatible = "adi,ad5689", .data = &ad5689_chip_info }, + { .compatible = "adi,ad5689r", .data = &ad5689r_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad5686_of_match); diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index e2beba6d54b1..0cc1c9c22a28 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -409,8 +409,11 @@ DECLARE_AD5683_CHANNELS(ad5683r_channels, 16, 0); /* dual-channel */ DECLARE_AD5338_CHANNELS(ad5337r_channels, 8, 8); DECLARE_AD5338_CHANNELS(ad5338r_channels, 10, 6); +DECLARE_AD5338_CHANNELS(ad5687r_channels, 12, 4); +DECLARE_AD5338_CHANNELS(ad5689r_channels, 16, 0); /* quad-channel */ +DECLARE_AD5686_CHANNELS(ad5317r_channels, 10, 6); DECLARE_AD5686_CHANNELS(ad5684r_channels, 12, 4); DECLARE_AD5686_CHANNELS(ad5685r_channels, 14, 2); DECLARE_AD5686_CHANNELS(ad5686r_channels, 16, 0); @@ -486,6 +489,44 @@ const struct ad5686_chip_info ad5338r_chip_info = { }; EXPORT_SYMBOL_NS_GPL(ad5338r_chip_info, "IIO_AD5686"); +const struct ad5686_chip_info ad5687_chip_info = { + .channels = ad5687r_channels, + .num_channels = 2, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5687_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5687r_chip_info = { + .channels = ad5687r_channels, + .int_vref_mv = 2500, + .num_channels = 2, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5687r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5689_chip_info = { + .channels = ad5689r_channels, + .num_channels = 2, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5689_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5689r_chip_info = { + .channels = ad5689r_channels, + .int_vref_mv = 2500, + .num_channels = 2, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5689r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5317r_chip_info = { + .channels = ad5317r_channels, + .int_vref_mv = 2500, + .num_channels = 4, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5317r_chip_info, "IIO_AD5686"); + const struct ad5686_chip_info ad5684_chip_info = { .channels = ad5684r_channels, .num_channels = 4, @@ -547,6 +588,13 @@ const struct ad5686_chip_info ad5676r_chip_info = { }; EXPORT_SYMBOL_NS_GPL(ad5676r_chip_info, "IIO_AD5686"); +const struct ad5686_chip_info ad5674_chip_info = { + .channels = ad5674r_channels, + .num_channels = 16, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5674_chip_info, "IIO_AD5686"); + const struct ad5686_chip_info ad5674r_chip_info = { .channels = ad5674r_channels, .int_vref_mv = 2500, @@ -555,6 +603,13 @@ const struct ad5686_chip_info ad5674r_chip_info = { }; EXPORT_SYMBOL_NS_GPL(ad5674r_chip_info, "IIO_AD5686"); +const struct ad5686_chip_info ad5679_chip_info = { + .channels = ad5679r_channels, + .num_channels = 16, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5679_chip_info, "IIO_AD5686"); + const struct ad5686_chip_info ad5679r_chip_info = { .channels = ad5679r_channels, .int_vref_mv = 2500, diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index c1222ef83a4f..482bc70515f7 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -106,8 +106,13 @@ extern const struct ad5686_chip_info ad5683r_chip_info; /* dual-channel instances */ extern const struct ad5686_chip_info ad5337r_chip_info; extern const struct ad5686_chip_info ad5338r_chip_info; +extern const struct ad5686_chip_info ad5687_chip_info; +extern const struct ad5686_chip_info ad5687r_chip_info; +extern const struct ad5686_chip_info ad5689_chip_info; +extern const struct ad5686_chip_info ad5689r_chip_info; /* quad-channel instances */ +extern const struct ad5686_chip_info ad5317r_chip_info; extern const struct ad5686_chip_info ad5684_chip_info; extern const struct ad5686_chip_info ad5684r_chip_info; extern const struct ad5686_chip_info ad5685r_chip_info; @@ -120,7 +125,9 @@ extern const struct ad5686_chip_info ad5676_chip_info; extern const struct ad5686_chip_info ad5676r_chip_info; /* 16-channel instances */ +extern const struct ad5686_chip_info ad5674_chip_info; extern const struct ad5686_chip_info ad5674r_chip_info; +extern const struct ad5686_chip_info ad5679_chip_info; extern const struct ad5686_chip_info ad5679r_chip_info; /** diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index 7064e294819b..f91abfcdbada 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -78,10 +78,12 @@ static int ad5686_i2c_probe(struct i2c_client *i2c) static const struct i2c_device_id ad5686_i2c_id[] = { { .name = "ad5311r", .driver_data = (kernel_ulong_t)&ad5311r_chip_info }, + { .name = "ad5316r", .driver_data = (kernel_ulong_t)&ad5317r_chip_info }, { .name = "ad5337r", .driver_data = (kernel_ulong_t)&ad5337r_chip_info }, { .name = "ad5338r", .driver_data = (kernel_ulong_t)&ad5338r_chip_info }, { .name = "ad5671r", .driver_data = (kernel_ulong_t)&ad5672r_chip_info }, { .name = "ad5673r", .driver_data = (kernel_ulong_t)&ad5674r_chip_info }, + { .name = "ad5675", .driver_data = (kernel_ulong_t)&ad5676_chip_info }, { .name = "ad5675r", .driver_data = (kernel_ulong_t)&ad5676r_chip_info }, { .name = "ad5677r", .driver_data = (kernel_ulong_t)&ad5679r_chip_info }, { .name = "ad5691r", .driver_data = (kernel_ulong_t)&ad5681r_chip_info }, @@ -93,16 +95,19 @@ static const struct i2c_device_id ad5686_i2c_id[] = { { .name = "ad5695r", .driver_data = (kernel_ulong_t)&ad5685r_chip_info }, { .name = "ad5696", .driver_data = (kernel_ulong_t)&ad5686_chip_info }, { .name = "ad5696r", .driver_data = (kernel_ulong_t)&ad5686r_chip_info }, + { .name = "ad5697r", .driver_data = (kernel_ulong_t)&ad5687r_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, ad5686_i2c_id); static const struct of_device_id ad5686_of_match[] = { { .compatible = "adi,ad5311r", .data = &ad5311r_chip_info }, + { .compatible = "adi,ad5316r", .data = &ad5317r_chip_info }, { .compatible = "adi,ad5337r", .data = &ad5337r_chip_info }, { .compatible = "adi,ad5338r", .data = &ad5338r_chip_info }, { .compatible = "adi,ad5671r", .data = &ad5672r_chip_info }, { .compatible = "adi,ad5673r", .data = &ad5674r_chip_info }, + { .compatible = "adi,ad5675", .data = &ad5676_chip_info }, { .compatible = "adi,ad5675r", .data = &ad5676r_chip_info }, { .compatible = "adi,ad5677r", .data = &ad5679r_chip_info }, { .compatible = "adi,ad5691r", .data = &ad5681r_chip_info }, @@ -114,6 +119,7 @@ static const struct of_device_id ad5686_of_match[] = { { .compatible = "adi,ad5695r", .data = &ad5685r_chip_info }, { .compatible = "adi,ad5696", .data = &ad5686_chip_info }, { .compatible = "adi,ad5696r", .data = &ad5686r_chip_info }, + { .compatible = "adi,ad5697r", .data = &ad5687r_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad5686_of_match); From d231462ae5b1b509b24517ff0e9890478dec4f22 Mon Sep 17 00:00:00 2001 From: Babanpreet Singh Date: Sun, 26 Jul 2026 07:22:34 +0000 Subject: [PATCH 362/513] iio: pressure: abp2030pa: Remove unreachable return in abp2_read_raw() In the IIO_CHAN_INFO_RAW case every arm of the inner switch on channel->type returns so this final return is never reached. Found by smatch: drivers/iio/pressure/abp2030pa.c:382 abp2_read_raw() warn: ignoring unreachable code. Assisted-by: Claude:claude-opus-5 Signed-off-by: Babanpreet Singh Reviewed-by: Siratul Islam Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/abp2030pa.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/pressure/abp2030pa.c b/drivers/iio/pressure/abp2030pa.c index b44f1bf4c633..41395036e9ad 100644 --- a/drivers/iio/pressure/abp2030pa.c +++ b/drivers/iio/pressure/abp2030pa.c @@ -379,7 +379,6 @@ static int abp2_read_raw(struct iio_dev *indio_dev, default: return -EINVAL; } - return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: switch (channel->type) { From 810634c1f8ce0b7c5b748a5e8f3ce724bc0a5f2e Mon Sep 17 00:00:00 2001 From: Aldo Conte Date: Tue, 7 Apr 2026 17:17:02 +0200 Subject: [PATCH 363/513] iio: light: cm3323: use dev_err_probe() in probe path Replace dev_err() calls with dev_err_probe() in cm3323_init() and cm3323_probe(). cm3323_init() is called by cm3323_probe(), so using dev_err_probe() ensures that deferred probing is handled correctly and simplifies error paths. Remove the redundant error message in cm3323_probe(). Tested on a Raspberry Pi 3B using i2c-stub. The driver probes successfully and successfully read integration_time after this change. Reviewed-by: Andy Shevchenko Suggested-by: Andy Shevchenko Signed-off-by: Aldo Conte Signed-off-by: Jonathan Cameron --- drivers/iio/light/cm3323.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/iio/light/cm3323.c b/drivers/iio/light/cm3323.c index fbb9e0a13733..506c91f44f43 100644 --- a/drivers/iio/light/cm3323.c +++ b/drivers/iio/light/cm3323.c @@ -80,22 +80,19 @@ static int cm3323_init(struct iio_dev *indio_dev) { int ret; struct cm3323_data *data = iio_priv(indio_dev); + struct device *dev = &data->client->dev; ret = i2c_smbus_read_word_data(data->client, CM3323_CMD_CONF); - if (ret < 0) { - dev_err(&data->client->dev, "Error reading reg_conf\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Error reading reg_conf\n"); /* enable sensor and set auto force mode */ ret &= ~(CM3323_CONF_SD_BIT | CM3323_CONF_AF_BIT); data->reg_conf = ret; ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, data->reg_conf); - if (ret < 0) { - dev_err(&data->client->dev, "Error writing reg_conf\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Error writing reg_conf\n"); return 0; } @@ -236,10 +233,8 @@ static int cm3323_probe(struct i2c_client *client) indio_dev->modes = INDIO_DIRECT_MODE; ret = cm3323_init(indio_dev); - if (ret < 0) { - dev_err(&client->dev, "cm3323 chip init failed\n"); + if (ret < 0) return ret; - } ret = devm_add_action_or_reset(&client->dev, cm3323_disable, indio_dev); if (ret < 0) From 15792e49564853c7bc7b977c9712a982d4eb57c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Kr=C3=A1tk=C3=BD?= Date: Sun, 26 Jul 2026 17:21:07 +0200 Subject: [PATCH 364/513] iio: chemical: bme680: Use dev_err_probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Used dev_err_probe() instead of dev_err() to simplify error handling. Signed-off-by: Vojtěch Krátký Reviewed-by: Siratul Islam Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/bme680_i2c.c | 7 +++---- drivers/iio/chemical/bme680_spi.c | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c index 4cc0e491621a..c34d67bcb2a3 100644 --- a/drivers/iio/chemical/bme680_i2c.c +++ b/drivers/iio/chemical/bme680_i2c.c @@ -24,10 +24,9 @@ static int bme680_i2c_probe(struct i2c_client *client) const char *name = NULL; regmap = devm_regmap_init_i2c(client, &bme680_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&client->dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap)); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&client->dev, PTR_ERR(regmap), + "Failed to register i2c regmap\n"); if (id) name = id->name; diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c index c55c6c514594..2175b9564fa1 100644 --- a/drivers/iio/chemical/bme680_spi.c +++ b/drivers/iio/chemical/bme680_spi.c @@ -121,10 +121,9 @@ static int bme680_spi_probe(struct spi_device *spi) regmap = devm_regmap_init(&spi->dev, &bme680_regmap_bus, bus_context, &bme680_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&spi->dev, "Failed to register spi regmap %ld\n", PTR_ERR(regmap)); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&spi->dev, PTR_ERR(regmap), + "Failed to register spi regmap\n"); return bme680_core_probe(&spi->dev, regmap, id->name); } From 728e9895bd648760fbac90f32a679415c44068a6 Mon Sep 17 00:00:00 2001 From: Tze Yee Ng Date: Wed, 22 Jul 2026 23:02:06 -0700 Subject: [PATCH 365/513] firmware: stratix10-svc: add async HWMON read commands and register socfpga-hwmon device Add asynchronous Stratix 10 service layer support for hardware monitor temperature and voltage read commands in stratix10_svc_async_send() and stratix10_svc_async_prepare_response(). Register a socfpga-hwmon platform device from the service layer driver when hardware monitor support is enabled, similar to the RSU device. Signed-off-by: Nazim Amirul Signed-off-by: Tze Yee Ng Signed-off-by: Dinh Nguyen --- drivers/firmware/stratix10-svc.c | 46 ++++++++++++++++++-- include/linux/firmware/intel/stratix10-smc.h | 38 ++++++++++++++++ 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index de938ab2db0b..08395fe3ddaa 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -46,6 +46,7 @@ /* stratix10 service layer clients */ #define STRATIX10_RSU "stratix10-rsu" +#define SOCFPGA_HWMON "socfpga-hwmon" /* Maximum number of SDM client IDs. */ #define MAX_SDM_CLIENT_IDS 16 @@ -105,9 +106,11 @@ struct stratix10_svc_chan; /** * struct stratix10_svc - svc private data * @stratix10_svc_rsu: pointer to stratix10 RSU device + * @stratix10_svc_hwmon: pointer to stratix10 HWMON device */ struct stratix10_svc { struct platform_device *stratix10_svc_rsu; + struct platform_device *stratix10_svc_hwmon; }; /** @@ -1405,6 +1408,14 @@ int stratix10_svc_async_send(struct stratix10_svc_chan *chan, void *msg, args.a0 = INTEL_SIP_SMC_ASYNC_RSU_NOTIFY; args.a2 = p_msg->arg[0]; break; + case COMMAND_HWMON_READTEMP: + args.a0 = INTEL_SIP_SMC_ASYNC_HWMON_READTEMP; + args.a2 = p_msg->arg[0]; + break; + case COMMAND_HWMON_READVOLT: + args.a0 = INTEL_SIP_SMC_ASYNC_HWMON_READVOLT; + args.a2 = p_msg->arg[0]; + break; default: dev_err(ctrl->dev, "Invalid command ,%d\n", p_msg->command); ret = -EINVAL; @@ -1498,6 +1509,10 @@ static int stratix10_svc_async_prepare_response(struct stratix10_svc_chan *chan, */ data->kaddr1 = (void *)&handle->res; break; + case COMMAND_HWMON_READTEMP: + case COMMAND_HWMON_READVOLT: + data->kaddr1 = (void *)&handle->res.a2; + break; default: dev_alert(ctrl->dev, "Invalid command\n ,%d", p_msg->command); @@ -2089,16 +2104,38 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev) if (ret) goto err_put_device; + if (IS_ENABLED(CONFIG_SENSORS_ALTERA_SOCFPGA_HWMON)) { + svc->stratix10_svc_hwmon = + platform_device_alloc(SOCFPGA_HWMON, 0); + if (!svc->stratix10_svc_hwmon) { + dev_err(dev, "failed to allocate %s device\n", + SOCFPGA_HWMON); + } else { + svc->stratix10_svc_hwmon->dev.parent = dev; + + ret = platform_device_add(svc->stratix10_svc_hwmon); + if (ret) { + dev_err(dev, "failed to add %s device: %d\n", + SOCFPGA_HWMON, ret); + platform_device_put(svc->stratix10_svc_hwmon); + svc->stratix10_svc_hwmon = NULL; + } + } + } + ret = of_platform_default_populate(dev_of_node(dev), NULL, dev); if (ret) - goto err_unregister_rsu_dev; + goto err_unregister_clients; pr_info("Intel Service Layer Driver Initialized\n"); return 0; -err_unregister_rsu_dev: - platform_device_unregister(svc->stratix10_svc_rsu); +err_unregister_clients: + if (svc->stratix10_svc_hwmon) + platform_device_unregister(svc->stratix10_svc_hwmon); + if (svc->stratix10_svc_rsu) + platform_device_unregister(svc->stratix10_svc_rsu); goto err_free_fifos; err_put_device: platform_device_put(svc->stratix10_svc_rsu); @@ -2122,6 +2159,9 @@ static void stratix10_svc_drv_remove(struct platform_device *pdev) struct stratix10_svc_controller *ctrl = platform_get_drvdata(pdev); struct stratix10_svc *svc = ctrl->svc; + if (svc->stratix10_svc_hwmon) + platform_device_unregister(svc->stratix10_svc_hwmon); + stratix10_svc_async_exit(ctrl); of_platform_depopulate(ctrl->dev); diff --git a/include/linux/firmware/intel/stratix10-smc.h b/include/linux/firmware/intel/stratix10-smc.h index 6e042943b6ce..ce1d2c7203b8 100644 --- a/include/linux/firmware/intel/stratix10-smc.h +++ b/include/linux/firmware/intel/stratix10-smc.h @@ -718,6 +718,44 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE) #define INTEL_SIP_SMC_ASYNC_POLL \ INTEL_SIP_SMC_ASYNC_VAL(INTEL_SIP_SMC_ASYNC_FUNC_ID_POLL) +/** + * Request INTEL_SIP_SMC_ASYNC_HWMON_READTEMP + * Async call to request temperature + * + * Call register usage: + * a0 INTEL_SIP_SMC_ASYNC_HWMON_READTEMP + * a1 transaction job id + * a2 Temperature Channel + * a3-a17 not used + * + * Return status + * a0 INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_STATUS_REJECTED + * or INTEL_SIP_SMC_STATUS_BUSY + * a1-a17 not used + */ +#define INTEL_SIP_SMC_ASYNC_FUNC_ID_HWMON_READTEMP 0xE8 +#define INTEL_SIP_SMC_ASYNC_HWMON_READTEMP \ + INTEL_SIP_SMC_ASYNC_VAL(INTEL_SIP_SMC_ASYNC_FUNC_ID_HWMON_READTEMP) + +/** + * Request INTEL_SIP_SMC_ASYNC_HWMON_READVOLT + * Async call to request voltage + * + * Call register usage: + * a0 INTEL_SIP_SMC_ASYNC_HWMON_READVOLT + * a1 transaction job id + * a2 Voltage Channel + * a3-a17 not used + * + * Return status + * a0 INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_STATUS_REJECTED + * or INTEL_SIP_SMC_STATUS_BUSY + * a1-a17 not used + */ +#define INTEL_SIP_SMC_ASYNC_FUNC_ID_HWMON_READVOLT 0xE9 +#define INTEL_SIP_SMC_ASYNC_HWMON_READVOLT \ + INTEL_SIP_SMC_ASYNC_VAL(INTEL_SIP_SMC_ASYNC_FUNC_ID_HWMON_READVOLT) + /** * Request INTEL_SIP_SMC_ASYNC_RSU_GET_SPT * Async call to get RSU SPT from SDM. From 758331ab5f9355352ccac3d7a83d3586bec19f9e Mon Sep 17 00:00:00 2001 From: Tze Yee Ng Date: Wed, 22 Jul 2026 23:02:07 -0700 Subject: [PATCH 366/513] hwmon: add Altera SoC FPGA hardware monitoring driver Add a hardware monitor driver for Altera SoC FPGA devices using the Stratix 10 service layer. Sensor channels are selected based on the service layer compatible string. Signed-off-by: Nazim Amirul Signed-off-by: Tze Yee Ng Acked-by: Guenter Roeck Link: https://lore.kernel.org/all/a79aee37be7067691fd02a3a9cfd134c995e5a95.1784785709.git.tze.yee.ng@altera.com/#r Signed-off-by: Dinh Nguyen --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/socfpga-hwmon.rst | 34 ++ MAINTAINERS | 8 + drivers/hwmon/Kconfig | 10 + drivers/hwmon/Makefile | 1 + drivers/hwmon/socfpga-hwmon.c | 579 ++++++++++++++++++++++++++ 6 files changed, 633 insertions(+) create mode 100644 Documentation/hwmon/socfpga-hwmon.rst create mode 100644 drivers/hwmon/socfpga-hwmon.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 29130df44d12..dfab6fec09bb 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -251,6 +251,7 @@ Hardware Monitoring Kernel Drivers smsc47b397 smsc47m192 smsc47m1 + socfpga-hwmon sparx5-temp spd5118 stpddc60 diff --git a/Documentation/hwmon/socfpga-hwmon.rst b/Documentation/hwmon/socfpga-hwmon.rst new file mode 100644 index 000000000000..e5da42556a62 --- /dev/null +++ b/Documentation/hwmon/socfpga-hwmon.rst @@ -0,0 +1,34 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver socfpga-hwmon +============================= + +Supported chips: + + * Altera Stratix 10 SoC FPGA + * Altera Agilex SoC FPGA + +Authors: + - Nazim Amirul + - Tze Yee Ng + +Description +----------- + +This driver supports hardware monitoring for Altera SoC +FPGA devices through the Secure Device Manager and Stratix 10 service layer. + +The following sensor types are supported: + + * temperature + * voltage + +Usage Notes +----------- + +The stratix10-svc driver registers a socfpga-hwmon platform device when +hardware monitor support is enabled. Sensor channels are selected in the +driver based on the service layer compatible string: + + * intel,stratix10-svc + * intel,agilex-svc diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..2b0ee6d5c6ec 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -965,6 +965,14 @@ L: linux-gpio@vger.kernel.org S: Maintained F: drivers/gpio/gpio-altera.c +ALTERA SoC FPGA HWMON DRIVER +M: Nazim Amirul +M: Tze Yee Ng +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/hwmon/socfpga-hwmon.rst +F: drivers/hwmon/socfpga-hwmon.c + ALTERA TRIPLE SPEED ETHERNET DRIVER M: Boon Khai Ng L: netdev@vger.kernel.org diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5c2d3ff5fce8..54ce630c7de6 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2157,6 +2157,16 @@ config SENSORS_SMSC47M192 This driver can also be built as a module. If so, the module will be called smsc47m192. +config SENSORS_ALTERA_SOCFPGA_HWMON + tristate "Altera SoC FPGA hardware monitoring features" + depends on INTEL_STRATIX10_SERVICE + help + If you say yes here you get support for the temperature and + voltage sensors of Altera SoC FPGA devices. + + This driver can also be built as a module. If so, the module + will be called socfpga-hwmon. + config SENSORS_SMSC47B397 tristate "SMSC LPC47B397-NC" depends on HAS_IOPORT diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 63effc0ab8d1..aeedee80e1f0 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -221,6 +221,7 @@ obj-$(CONFIG_SENSORS_SMPRO) += smpro-hwmon.o obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o +obj-$(CONFIG_SENSORS_ALTERA_SOCFPGA_HWMON) += socfpga-hwmon.o obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o obj-$(CONFIG_SENSORS_SPD5118) += spd5118.o obj-$(CONFIG_SENSORS_STTS751) += stts751.o diff --git a/drivers/hwmon/socfpga-hwmon.c b/drivers/hwmon/socfpga-hwmon.c new file mode 100644 index 000000000000..5b43274d0aa2 --- /dev/null +++ b/drivers/hwmon/socfpga-hwmon.c @@ -0,0 +1,579 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Altera SoC FPGA hardware monitoring driver + * + * Copyright (c) 2026 Altera Corporation + * + * Authors: + * Nazim Amirul + * Tze Yee Ng + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HWMON_TIMEOUT msecs_to_jiffies(SVC_HWMON_REQUEST_TIMEOUT_MS) +#define HWMON_RETRY_SLEEP_US 1000U +#define HWMON_ASYNC_MSG_RETRY 3U +#define SOCFPGA_HWMON_MAXSENSORS 16 +#define SOCFPGA_HWMON_CHANNEL_MASK GENMASK(15, 0) +#define SOCFPGA_HWMON_PAGE_SHIFT 16 +#define SOCFPGA_HWMON_CHAN(page, channel) \ + (((page) << SOCFPGA_HWMON_PAGE_SHIFT) | \ + ((channel) & SOCFPGA_HWMON_CHANNEL_MASK)) +#define SOCFPGA_HWMON_ATTR_VISIBLE 0444 +/* Temperature from SDM is signed Q8.8 degrees Celsius (8 fractional bits). */ +#define SOCFPGA_HWMON_TEMP_FRAC_BITS 8 +#define SOCFPGA_HWMON_TEMP_FRAC_DIV BIT(SOCFPGA_HWMON_TEMP_FRAC_BITS) +#define SOCFPGA_HWMON_TEMP_MDEG_SCALE 1000 +/* Voltage from SDM is unsigned Q16 volts (16 fractional bits). */ +#define SOCFPGA_HWMON_VOLT_FRAC_BITS 16 +#define SOCFPGA_HWMON_VOLT_FRAC_DIV BIT(SOCFPGA_HWMON_VOLT_FRAC_BITS) +#define SOCFPGA_HWMON_VOLT_MV_SCALE 1000 + +#define ETEMP_INACTIVE 0x80000000U +#define ETEMP_TOO_OLD 0x80000001U +#define ETEMP_NOT_PRESENT 0x80000002U +#define ETEMP_TIMEOUT 0x80000003U +#define ETEMP_CORRUPT 0x80000004U +#define ETEMP_BUSY 0x80000005U +#define ETEMP_NOT_INITIALIZED 0x800000FFU + +struct socfpga_hwmon_channel { + u32 reg; + const char *label; +}; + +struct socfpga_hwmon_board_data { + const struct socfpga_hwmon_channel *temp; + unsigned int num_temp; + const struct socfpga_hwmon_channel *volt; + unsigned int num_volt; +}; + +struct socfpga_hwmon_priv { + struct stratix10_svc_chan *chan; + struct stratix10_svc_client client; + struct completion completion; + struct mutex lock; /* protect SVC calls */ + bool async; + int last_err; /* sync-mode SVC result; 0 on success */ + u32 temperature; + u32 voltage; + int temperature_channels; + int voltage_channels; + const char *temp_chan_names[SOCFPGA_HWMON_MAXSENSORS]; + const char *volt_chan_names[SOCFPGA_HWMON_MAXSENSORS]; + u32 temp_chan[SOCFPGA_HWMON_MAXSENSORS]; + u32 volt_chan[SOCFPGA_HWMON_MAXSENSORS]; +}; + +static umode_t socfpga_hwmon_is_visible(const void *dev, + enum hwmon_sensor_types type, + u32 attr, int chan) +{ + const struct socfpga_hwmon_priv *priv = dev; + + switch (type) { + case hwmon_temp: + if (chan < priv->temperature_channels) + return SOCFPGA_HWMON_ATTR_VISIBLE; + return 0; + case hwmon_in: + if (chan < priv->voltage_channels) + return SOCFPGA_HWMON_ATTR_VISIBLE; + return 0; + default: + return 0; + } +} + +static void socfpga_hwmon_readtemp_cb(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct socfpga_hwmon_priv *priv = client->priv; + + priv->last_err = -EIO; + if (data->status == BIT(SVC_STATUS_OK)) { + priv->last_err = 0; + priv->temperature = (u32)*(unsigned long *)data->kaddr1; + } else if (data->kaddr1) { + dev_err(client->dev, "%s failed with status 0x%x, value 0x%lx\n", + __func__, data->status, + *(unsigned long *)data->kaddr1); + } else { + dev_err(client->dev, "%s failed with status 0x%x\n", + __func__, data->status); + } + + complete(&priv->completion); +} + +static void socfpga_hwmon_readvolt_cb(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct socfpga_hwmon_priv *priv = client->priv; + + priv->last_err = -EIO; + if (data->status == BIT(SVC_STATUS_OK)) { + priv->last_err = 0; + priv->voltage = (u32)*(unsigned long *)data->kaddr1; + } else if (data->kaddr1) { + dev_err(client->dev, "%s failed with status 0x%x, value 0x%lx\n", + __func__, data->status, + *(unsigned long *)data->kaddr1); + } else { + dev_err(client->dev, "%s failed with status 0x%x\n", + __func__, data->status); + } + + complete(&priv->completion); +} + +static int socfpga_hwmon_parse_temp(long *val, u32 temperature) +{ + switch (temperature) { + case ETEMP_INACTIVE: + case ETEMP_NOT_PRESENT: + case ETEMP_CORRUPT: + case ETEMP_NOT_INITIALIZED: + return -EOPNOTSUPP; + case ETEMP_TIMEOUT: + case ETEMP_BUSY: + case ETEMP_TOO_OLD: + return -EAGAIN; + default: + /* SDM returns a 16-bit signed Q8.8 value in the low 16 bits. */ + *val = (long)(s16)(temperature & SOCFPGA_HWMON_CHANNEL_MASK) * + SOCFPGA_HWMON_TEMP_MDEG_SCALE / SOCFPGA_HWMON_TEMP_FRAC_DIV; + return 0; + } +} + +static int socfpga_hwmon_encode_temp_arg(u32 reg, u64 *arg) +{ + u32 page = (reg >> SOCFPGA_HWMON_PAGE_SHIFT) & SOCFPGA_HWMON_CHANNEL_MASK; + u32 channel = reg & SOCFPGA_HWMON_CHANNEL_MASK; + + if (channel >= SOCFPGA_HWMON_MAXSENSORS) + return -EINVAL; + + *arg = (1ULL << channel) | ((u64)page << SOCFPGA_HWMON_PAGE_SHIFT); + return 0; +} + +static int socfpga_hwmon_encode_volt_arg(u32 reg, u64 *arg) +{ + u32 channel = reg & SOCFPGA_HWMON_CHANNEL_MASK; + + if (channel >= SOCFPGA_HWMON_MAXSENSORS) + return -EINVAL; + + *arg = 1ULL << channel; + return 0; +} + +static int socfpga_hwmon_async_read(struct device *dev, + enum hwmon_sensor_types type, + struct stratix10_svc_client_msg *msg) +{ + struct socfpga_hwmon_priv *priv = dev_get_drvdata(dev); + struct stratix10_svc_cb_data data = {}; + unsigned long deadline = jiffies + HWMON_TIMEOUT; + void *handle = NULL; + int status, index, ret; + + for (index = 0; index < HWMON_ASYNC_MSG_RETRY; index++) { + status = stratix10_svc_async_send(priv->chan, msg, &handle, + NULL, NULL); + if (status == 0) + break; + dev_warn(dev, "Failed to send async message: %d\n", status); + usleep_range(HWMON_RETRY_SLEEP_US, HWMON_RETRY_SLEEP_US * 2); + } + + if (status && !handle) { + dev_err(dev, "Failed to send async message after %u retries: %d\n", + HWMON_ASYNC_MSG_RETRY, status); + return status; + } + + ret = -ETIMEDOUT; + while (!time_after(jiffies, deadline)) { + status = stratix10_svc_async_poll(priv->chan, handle, &data); + if (status == -EAGAIN) { + /* still in progress */ + } else if (status < 0) { + ret = status; + break; + } else if (status == 0) { + ret = 0; + break; + } + usleep_range(HWMON_RETRY_SLEEP_US, HWMON_RETRY_SLEEP_US * 2); + } + + if (ret) { + dev_err(dev, "Failed to get async response\n"); + goto done; + } + + if (data.status) { + dev_err(dev, "%s returned 0x%x from SDM\n", __func__, + data.status); + ret = -EFAULT; + goto done; + } + + if (type == hwmon_temp) + priv->temperature = (u32)*(unsigned long *)data.kaddr1; + else + priv->voltage = (u32)*(unsigned long *)data.kaddr1; + + ret = 0; + +done: + stratix10_svc_async_done(priv->chan, handle); + return ret; +} + +static int socfpga_hwmon_sync_read(struct device *dev, + enum hwmon_sensor_types type, + struct stratix10_svc_client_msg *msg) +{ + struct socfpga_hwmon_priv *priv = dev_get_drvdata(dev); + int ret; + + reinit_completion(&priv->completion); + + if (type == hwmon_temp) + priv->client.receive_cb = socfpga_hwmon_readtemp_cb; + else + priv->client.receive_cb = socfpga_hwmon_readvolt_cb; + + ret = stratix10_svc_send(priv->chan, msg); + if (ret < 0) + goto status_done; + + ret = wait_for_completion_timeout(&priv->completion, HWMON_TIMEOUT); + if (!ret) { + dev_err(priv->client.dev, "timeout waiting for SMC call\n"); + ret = -ETIMEDOUT; + goto status_done; + } + + ret = priv->last_err; + +status_done: + stratix10_svc_done(priv->chan); + return ret; +} + +static int socfpga_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int chan, long *val) +{ + struct socfpga_hwmon_priv *priv = dev_get_drvdata(dev); + struct stratix10_svc_client_msg msg = {0}; + int ret; + + if (chan >= SOCFPGA_HWMON_MAXSENSORS) + return -EOPNOTSUPP; + + switch (type) { + case hwmon_temp: + ret = socfpga_hwmon_encode_temp_arg(priv->temp_chan[chan], + &msg.arg[0]); + if (ret) + return ret; + msg.command = COMMAND_HWMON_READTEMP; + break; + case hwmon_in: + ret = socfpga_hwmon_encode_volt_arg(priv->volt_chan[chan], + &msg.arg[0]); + if (ret) + return ret; + msg.command = COMMAND_HWMON_READVOLT; + break; + default: + return -EOPNOTSUPP; + } + + guard(mutex)(&priv->lock); + if (priv->async) + ret = socfpga_hwmon_async_read(dev, type, &msg); + else + ret = socfpga_hwmon_sync_read(dev, type, &msg); + if (ret) + return ret; + + if (type == hwmon_temp) + ret = socfpga_hwmon_parse_temp(val, priv->temperature); + else + /* SDM returns Q16 volts; convert to hwmon millivolts. */ + *val = (long)priv->voltage * SOCFPGA_HWMON_VOLT_MV_SCALE / + SOCFPGA_HWMON_VOLT_FRAC_DIV; + return ret; +} + +static int socfpga_hwmon_read_string(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int chan, const char **str) +{ + struct socfpga_hwmon_priv *priv = dev_get_drvdata(dev); + + switch (type) { + case hwmon_in: + *str = priv->volt_chan_names[chan]; + return 0; + case hwmon_temp: + *str = priv->temp_chan_names[chan]; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_ops socfpga_hwmon_ops = { + .is_visible = socfpga_hwmon_is_visible, + .read = socfpga_hwmon_read, + .read_string = socfpga_hwmon_read_string, +}; + +static const struct hwmon_channel_info *socfpga_hwmon_info[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL), + NULL +}; + +static const struct hwmon_chip_info socfpga_hwmon_chip_info = { + .ops = &socfpga_hwmon_ops, + .info = socfpga_hwmon_info, +}; + +static const struct socfpga_hwmon_channel s10_hwmon_volt_channels[] = { + { SOCFPGA_HWMON_CHAN(0, 2), "0.8V VCC" }, + { SOCFPGA_HWMON_CHAN(0, 3), "1.8V VCCIO_SDM" }, + { SOCFPGA_HWMON_CHAN(0, 6), "0.9V VCCERAM" }, +}; + +static const struct socfpga_hwmon_channel s10_hwmon_temp_channels[] = { + { SOCFPGA_HWMON_CHAN(0, 0), "Main Die SDM" }, +}; + +static const struct socfpga_hwmon_board_data s10_hwmon_board = { + .temp = s10_hwmon_temp_channels, + .num_temp = ARRAY_SIZE(s10_hwmon_temp_channels), + .volt = s10_hwmon_volt_channels, + .num_volt = ARRAY_SIZE(s10_hwmon_volt_channels), +}; + +static const struct socfpga_hwmon_channel agilex_hwmon_volt_channels[] = { + { SOCFPGA_HWMON_CHAN(0, 2), "0.8V VCC" }, + { SOCFPGA_HWMON_CHAN(0, 3), "1.8V VCCIO_SDM" }, + { SOCFPGA_HWMON_CHAN(0, 4), "1.8V VCCPT" }, + { SOCFPGA_HWMON_CHAN(0, 5), "1.2V VCCCRCORE" }, + { SOCFPGA_HWMON_CHAN(0, 6), "0.9V VCCH" }, + { SOCFPGA_HWMON_CHAN(0, 7), "0.8V VCCL" }, +}; + +static const struct socfpga_hwmon_channel agilex_hwmon_temp_channels[] = { + { SOCFPGA_HWMON_CHAN(0, 0), "Main Die SDM" }, + { SOCFPGA_HWMON_CHAN(1, 0), "Main Die corner bottom left max" }, + { SOCFPGA_HWMON_CHAN(2, 0), "Main Die corner top left max" }, + { SOCFPGA_HWMON_CHAN(3, 0), "Main Die corner bottom right max" }, + { SOCFPGA_HWMON_CHAN(4, 0), "Main Die corner top right max" }, +}; + +static const struct socfpga_hwmon_board_data agilex_hwmon_board = { + .temp = agilex_hwmon_temp_channels, + .num_temp = ARRAY_SIZE(agilex_hwmon_temp_channels), + .volt = agilex_hwmon_volt_channels, + .num_volt = ARRAY_SIZE(agilex_hwmon_volt_channels), +}; + +static const struct socfpga_hwmon_board_data * +socfpga_hwmon_get_board(struct device *dev) +{ + struct device_node *np = dev->of_node; + + if (!np) + return NULL; + + if (of_device_is_compatible(np, "intel,stratix10-svc")) + return &s10_hwmon_board; + if (of_device_is_compatible(np, "intel,agilex-svc")) + return &agilex_hwmon_board; + + return NULL; +} + +static int socfpga_hwmon_init_channels(struct device *dev, + const struct socfpga_hwmon_board_data *board, + struct socfpga_hwmon_priv *priv) +{ + unsigned int i; + + if (board->num_temp > SOCFPGA_HWMON_MAXSENSORS || + board->num_volt > SOCFPGA_HWMON_MAXSENSORS) + return -EINVAL; + + for (i = 0; i < board->num_temp; i++) { + priv->temp_chan_names[i] = board->temp[i].label; + priv->temp_chan[i] = board->temp[i].reg; + } + priv->temperature_channels = board->num_temp; + + for (i = 0; i < board->num_volt; i++) { + priv->volt_chan_names[i] = board->volt[i].label; + priv->volt_chan[i] = board->volt[i].reg; + } + priv->voltage_channels = board->num_volt; + + return 0; +} + +static void socfpga_hwmon_release_svc(void *data) +{ + struct socfpga_hwmon_priv *priv = data; + + if (priv->async) + stratix10_svc_remove_async_client(priv->chan); + stratix10_svc_free_channel(priv->chan); +} + +static int socfpga_hwmon_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device *parent = dev->parent; + const struct socfpga_hwmon_board_data *board; + struct socfpga_hwmon_priv *priv; + struct device *hwmon_dev; + int ret; + + if (!parent || !parent->of_node) { + dev_err(dev, "missing parent device node\n"); + return -ENODEV; + } + + board = socfpga_hwmon_get_board(parent); + if (!board) { + dev_err(dev, "unsupported service layer compatible\n"); + return -ENODEV; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client.dev = dev; + priv->client.priv = priv; + init_completion(&priv->completion); + mutex_init(&priv->lock); + + ret = socfpga_hwmon_init_channels(dev, board, priv); + if (ret) + return ret; + + priv->chan = stratix10_svc_request_channel_byname(&priv->client, + SVC_CLIENT_HWMON); + if (IS_ERR(priv->chan)) { + ret = PTR_ERR(priv->chan); + if (ret == -EPROBE_DEFER) + dev_dbg(dev, "service channel %s not ready, deferring probe\n", + SVC_CLIENT_HWMON); + else + dev_err(dev, "couldn't get service channel %s: %d\n", + SVC_CLIENT_HWMON, ret); + return ret; + } + + ret = stratix10_svc_add_async_client(priv->chan, false); + switch (ret) { + case 0: + priv->async = true; + break; + case -EINVAL: + case -EOPNOTSUPP: + /* + * stratix10_svc_add_async_client() returns -EINVAL when the + * async controller is not initialized; fall back to sync mode. + */ + dev_dbg(dev, "async operations not supported, using sync mode\n"); + priv->async = false; + break; + default: + dev_err(dev, "failed to add async client: %d\n", ret); + stratix10_svc_free_channel(priv->chan); + return ret; + } + + ret = devm_add_action_or_reset(dev, socfpga_hwmon_release_svc, priv); + if (ret) + return ret; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, "socfpga_hwmon", + priv, + &socfpga_hwmon_chip_info, + NULL); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + platform_set_drvdata(pdev, priv); + return 0; +} + +static struct platform_driver socfpga_hwmon_driver = { + .probe = socfpga_hwmon_probe, + .driver = { + .name = "socfpga-hwmon", + }, +}; +module_platform_driver(socfpga_hwmon_driver); + +MODULE_AUTHOR("Nazim Amirul "); +MODULE_AUTHOR("Tze Yee Ng "); +MODULE_DESCRIPTION("Altera SoC FPGA hardware monitoring driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:socfpga-hwmon"); From 24f4423cbc89548def2b05ae86de6175086dbf94 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 23 Jun 2026 16:51:34 +0200 Subject: [PATCH 367/513] bus: mhi: host: Flush the posted write after writing to MHI_SOC_RESET_REQ_OFFSET mhi_soc_reset() tries to reset the device by writing to the MHI_SOC_RESET_REQ_OFFSET register. But it doesn't do a read-back to ensure that the write gets flushed to the device before returning to the caller. This may lead to the delay (if implemented) on the caller to be insufficient, if the posted write doesn't reach the device before the delay. So add a read-back after writing to the MHI_SOC_RESET_REQ_OFFSET register. Fixes: b5a8d233a588 ("bus: mhi: core: Add device hardware reset support") Reported-by: Alex Williamson Closes: https://lore.kernel.org/linux-pci/20260622160822.09350246@shazbot.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Reviewed-by: Jeff Hugo Link: https://patch.msgid.link/20260623145134.43976-1-manivannan.sadhasivam@oss.qualcomm.com --- drivers/bus/mhi/host/main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c index 53c0ffe30070..4d458396233a 100644 --- a/drivers/bus/mhi/host/main.c +++ b/drivers/bus/mhi/host/main.c @@ -170,6 +170,9 @@ EXPORT_SYMBOL_GPL(mhi_get_mhi_state); void mhi_soc_reset(struct mhi_controller *mhi_cntrl) { + int __maybe_unused ret; + u32 tmp; + if (mhi_cntrl->reset) { mhi_cntrl->reset(mhi_cntrl); return; @@ -178,6 +181,9 @@ void mhi_soc_reset(struct mhi_controller *mhi_cntrl) /* Generic MHI SoC reset */ mhi_write_reg(mhi_cntrl, mhi_cntrl->regs, MHI_SOC_RESET_REQ_OFFSET, MHI_SOC_RESET_REQ); + /* Flush the posted write to the device (ignore return value) */ + ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, MHI_SOC_RESET_REQ_OFFSET, + &tmp); } EXPORT_SYMBOL_GPL(mhi_soc_reset); From 753aa72545ce0b2503eca784f35062d0bf5418d7 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 10 Jul 2026 13:51:20 +0200 Subject: [PATCH 368/513] bus: mhi: host: Add support for devices with no M3 state MHI bus transitions the device into M3 state during suspend and back to M0 state during resume. But due to hardware issues, some devices do not support M3 state. To support these devices properly, MHI bus needs to skip transitioning the device to M3 during suspend and back to M0 during resume. For this purpose, introduce the 'mhi_cntrl->no_m3' flag and allow it to be set by the MHI controller drivers. Once set, this flag lets the MHI bus skip transitioning the device to M3/M0 during suspend/resume. But, simply skipping suspend/resume for such devices is not sufficient, as it leaves the MHI host in M0 state with device access enabled. Client drivers that do not implement PM callbacks (for instance, the non-freezable rx_refill worker in mhi_net driver) could then keep ringing channel doorbells and issue MMIO to the device even after the controller driver has disabled it and moved it to D3 during its own suspend, resulting in access to a powered down device. So instead of skipping the entire suspend/resume operation, run the full host suspend/resume sequence but without the device-side M state handshake. During suspend, only transition the host to M3 without sending the MHICTRL M3 command or waiting for the device M3 event. During resume, bring the host back to M0 through mhi_pm_m0_transition() without sending the MHICTRL M0 command. With the host in M3, all device access is gated by MHI_DB_ACCESS_VALID() and MHI_REG_ACCESS_VALID(), so any transfer queued by the clients during suspend is deferred until resume, where mhi_pm_m0_transition() rings the pending doorbells. Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/host/pm.c | 57 ++++++++++++++++++++++++++++----------- include/linux/mhi.h | 2 ++ 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/drivers/bus/mhi/host/pm.c b/drivers/bus/mhi/host/pm.c index 9d29f1591a45..ced83fbb1a51 100644 --- a/drivers/bus/mhi/host/pm.c +++ b/drivers/bus/mhi/host/pm.c @@ -914,22 +914,39 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl) return -EIO; } - /* Set MHI to M3 and wait for completion */ - mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M3); - write_unlock_irq(&mhi_cntrl->pm_lock); - dev_dbg(dev, "Waiting for M3 completion\n"); + /* + * For devices without M3 support, just set the host state to M3. This + * host transition is needed to prevent the client drivers from + * accessing the device during suspend. + */ + if (mhi_cntrl->no_m3) { + new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M3); + write_unlock_irq(&mhi_cntrl->pm_lock); + if (new_state != MHI_PM_M3) { + dev_err(dev, + "Error setting to PM state: %s from: %s\n", + to_mhi_pm_state_str(MHI_PM_M3), + to_mhi_pm_state_str(mhi_cntrl->pm_state)); + return -EIO; + } + } else { + /* Set MHI to M3 and wait for completion */ + mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M3); + write_unlock_irq(&mhi_cntrl->pm_lock); + dev_dbg(dev, "Waiting for M3 completion\n"); - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_cntrl->dev_state == MHI_STATE_M3 || - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), - msecs_to_jiffies(mhi_cntrl->timeout_ms)); + ret = wait_event_timeout(mhi_cntrl->state_event, + mhi_cntrl->dev_state == MHI_STATE_M3 || + MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), + msecs_to_jiffies(mhi_cntrl->timeout_ms)); - if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - dev_err(dev, - "Did not enter M3 state, MHI state: %s, PM state: %s\n", - mhi_state_str(mhi_cntrl->dev_state), - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - return -EIO; + if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { + dev_err(dev, + "Did not enter M3 state, MHI state: %s, PM state: %s\n", + mhi_state_str(mhi_cntrl->dev_state), + to_mhi_pm_state_str(mhi_cntrl->pm_state)); + return -EIO; + } } /* Notify clients about entering LPM */ @@ -961,7 +978,8 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force) if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) return -EIO; - if (mhi_get_mhi_state(mhi_cntrl) != MHI_STATE_M3) { + if (!mhi_cntrl->no_m3 && + mhi_get_mhi_state(mhi_cntrl) != MHI_STATE_M3) { dev_warn(dev, "Resuming from non M3 state (%s)\n", mhi_state_str(mhi_get_mhi_state(mhi_cntrl))); if (!force) @@ -987,6 +1005,15 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force) return -EIO; } + /* + * For devices without M3 support, just move the host back to M0 + * directly. + */ + if (mhi_cntrl->no_m3) { + write_unlock_irq(&mhi_cntrl->pm_lock); + return mhi_pm_m0_transition(mhi_cntrl); + } + /* Set MHI to M0 and wait for completion */ mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M0); write_unlock_irq(&mhi_cntrl->pm_lock); diff --git a/include/linux/mhi.h b/include/linux/mhi.h index fb3ba639f4f8..0d60058bf5ae 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -374,6 +374,7 @@ struct mhi_controller_config { * @bounce_buf: Use of bounce buffer * @fbc_download: MHI host needs to do complete image transfer (optional) * @wake_set: Device wakeup set flag + * @no_m3: Device doesn't support M3 state * @irq_flags: irq flags passed to request_irq (optional) * @mru: the default MRU for the MHI device * @@ -459,6 +460,7 @@ struct mhi_controller { bool bounce_buf; bool fbc_download; bool wake_set; + bool no_m3; unsigned long irq_flags; u32 mru; }; From 0936a8780ec2d239993c6cec988834fc9779fc01 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 9 Jul 2026 09:26:49 +0200 Subject: [PATCH 369/513] bus: mhi: host: pci_generic: Set 'mhi_cntrl->no_m3' flag Commit 0494cf9793b7 ("bus: mhi: host: pci_generic: Disable runtime PM for QDU100") added the 'no_m3' flag to indicate that the QDU100 device doesn't support M3 state and used this flag to skip runtime PM. But it didn't prevent the MHI bus from transitioning the device to M3 during system suspend. So set 'mhi_cntrl->no_m3' flag based on the local 'info->no_m3' flag to indicate MHI bus that this device doesn't support M3 state so that it can skip the transition. Cc: stable+noautosel@kernel.org # depends on the 'mhi_cntrl->no_m3' flag addition Fixes: 0494cf9793b7 ("bus: mhi: host: pci_generic: Disable runtime PM for QDU100") Reported-by: Krishna Chaitanya Chundru Signed-off-by: Manivannan Sadhasivam --- 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 0d0d9c7ffa4b..7e340d6112bf 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -1395,6 +1395,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) mhi_cntrl->iova_stop = (dma_addr_t)DMA_BIT_MASK(dma_data_width); mhi_cntrl->fw_image = info->fw; mhi_cntrl->edl_image = info->edl; + mhi_cntrl->no_m3 = info->no_m3; mhi_cntrl->read_reg = mhi_pci_read_reg; mhi_cntrl->write_reg = mhi_pci_write_reg; From 5fe63c1bba57492c97cceb34c35e276d3c81e4fe Mon Sep 17 00:00:00 2001 From: Slark Xiao Date: Fri, 26 Jun 2026 11:46:33 +0800 Subject: [PATCH 370/513] bus: mhi: pci_generic: Add SAHARA channel support for Foxconn products Add SAHARA channel to support capturing crash dump (ramdump) using the in-kernel sahara client driver. Signed-off-by: Slark Xiao [mani: commit log] Signed-off-by: Manivannan Sadhasivam --- 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 7e340d6112bf..b636e2c23b4d 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -491,6 +491,8 @@ static const struct mhi_pci_dev_info mhi_quectel_rm5xx_info = { static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = { MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0), MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0), + MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0), + MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 32, 0), MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 1), MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 1), MHI_CHANNEL_CONFIG_UL(12, "MBIM", 32, 0), @@ -506,6 +508,8 @@ static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = { static const struct mhi_channel_config mhi_foxconn_sdx61_channels[] = { MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0), MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0), + MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0), + MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 32, 0), MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 1), MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 1), MHI_CHANNEL_CONFIG_UL(12, "MBIM", 32, 0), From 0d5b9e66591d4e2a4376ac82c8cda889a29ba3ee Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Tue, 7 Jul 2026 14:51:00 -0400 Subject: [PATCH 371/513] bus: mhi: host: Fix controller cleanup on EDL sysfs failure mhi_register_controller() adds the controller device before creating the optional trigger_edl sysfs file. If sysfs_create_file() fails, the error path only drops the device reference and leaves the device registered. Hence, call device_del() in the error path before put_device(). Fixes: 17553ba8e19d ("bus: mhi: host: Add sysfs entry to force device to enter EDL") Signed-off-by: Yuho Choi Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/host/init.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c index 0a728ca2c494..0eeee26893f3 100644 --- a/drivers/bus/mhi/host/init.c +++ b/drivers/bus/mhi/host/init.c @@ -1030,7 +1030,7 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, if (mhi_cntrl->edl_trigger) { ret = sysfs_create_file(&mhi_dev->dev.kobj, &dev_attr_trigger_edl.attr); if (ret) - goto err_release_dev; + goto err_del_dev; } mhi_cntrl->mhi_dev = mhi_dev; @@ -1039,6 +1039,8 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, return 0; +err_del_dev: + device_del(&mhi_dev->dev); err_release_dev: put_device(&mhi_dev->dev); error_setup_irq: From abe5c5f1ad5575d04e53a91d5c2ead2bb50292a7 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 19 Jul 2026 15:32:31 -0700 Subject: [PATCH 372/513] bus: mhi: Clean up some kernel-doc warnings Clean up some kernel-doc comments and warnings: - use correct format for struct members - add one struct member description - add one function parameter description - mark one enum as private - add a leading '*' on one kernel-doc line Fixes these warnings: Warning: include/linux/mhi.h:108 struct member 'target_link_speed' not described in 'mhi_link_info' Warning: include/linux/mhi.h:108 struct member 'target_link_width' not described in 'mhi_link_info' Warning: ../include/linux/mhi.h:159 Enum value 'MHI_STATE_MAX' not described in enum 'mhi_state' Warning: ../include/linux/mhi.h:212 bad line: for UL channels, multiple of 8 ring elements for DL channels Warning: ../include/linux/mhi.h:236 struct member 'wake_capable' not described in 'mhi_channel_config' Warning: ../include/linux/mhi.h:449 struct member 'M0' not described in 'mhi_controller' Warning: ../include/linux/mhi.h:449 struct member 'M2' not described in 'mhi_controller' Warning: ../include/linux/mhi.h:449 struct member 'M3' not described in 'mhi_controller' Warning: ../include/linux/mhi.h:528 struct member 'id_table' not described in 'mhi_driver' Warning: ../include/linux/mhi.h:543 function parameter 'mhi_cntrl' not described in 'mhi_free_controller' Signed-off-by: Randy Dunlap Signed-off-by: Manivannan Sadhasivam --- include/linux/mhi.h | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/include/linux/mhi.h b/include/linux/mhi.h index 0d60058bf5ae..ca24ca6c6a48 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -116,8 +116,8 @@ struct image_info { /** * struct mhi_link_info - BW requirement - * target_link_speed - Link speed as defined by TLS bits in LinkControl reg - * target_link_width - Link width as defined by NLW bits in LinkStatus reg + * @target_link_speed: Link speed as defined by TLS bits in LinkControl reg + * @target_link_width: Link width as defined by NLW bits in LinkStatus reg */ struct mhi_link_info { unsigned int target_link_speed; @@ -172,6 +172,7 @@ enum mhi_state { MHI_STATE_M3_FAST = 0x6, MHI_STATE_BHI = 0x7, MHI_STATE_SYS_ERR = 0xFF, + /* private: */ MHI_STATE_MAX, }; @@ -226,12 +227,12 @@ enum mhi_db_brst_mode { * @type: Channel type * @ee_mask: Execution Environment mask for this channel * @pollcfg: Polling configuration for burst mode. 0 is default. milliseconds - for UL channels, multiple of 8 ring elements for DL channels + * for UL channels, multiple of 8 ring elements for DL channels * @doorbell: Doorbell mode * @lpm_notify: The channel master requires low power mode notifications * @offload_channel: The client manages the channel completely * @doorbell_mode_switch: Channel switches to doorbell mode on M0 transition - * @wake-capable: Channel capable of waking up the system + * @wake_capable: Channel capable of waking up the system */ struct mhi_channel_config { char *name; @@ -349,7 +350,9 @@ struct mhi_controller_config { * @dev_state: MHI device state * @dev_wake: Device wakeup count * @pending_pkts: Pending packets for the controller - * @M0, M2, M3: Counters to track number of device MHI state changes + * @M0: Counter to track number of device MHI state changes + * @M2: Counter to track number of device MHI state changes + * @M3: Counter to track number of device MHI state changes * @transition_list: List of MHI state transitions * @transition_lock: Lock for protecting MHI state transition list * @wlock: Lock for protecting device wakeup @@ -508,6 +511,7 @@ struct mhi_result { /** * struct mhi_driver - Structure representing a MHI client driver + * @id_table: table of MHI channel names that a driver supports * @probe: CB function for client driver probe function * @remove: CB function for client driver remove function * @ul_xfer_cb: CB function for UL data transfer @@ -539,6 +543,7 @@ struct mhi_controller *mhi_alloc_controller(void); /** * mhi_free_controller - Free the MHI Controller structure + * @mhi_cntrl: MHI controller to free * Free the mhi_controller structure which was previously allocated */ void mhi_free_controller(struct mhi_controller *mhi_cntrl); From 7cc5ddce0a622359eecf16d97080dc96edf13b52 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 22 Jul 2026 07:54:44 +0200 Subject: [PATCH 373/513] bus: mhi: ep: Add mhi_cntrl->flush_async() callback to flush the async read/write MHI EP stack makes use of the MHI controller drivers like MHI EPF to do read/write to the host memory. And that driver is free to use mechanisms like DMA to offload the read/write operations. So if DMA is used for offload, then there is no guarantee that those DMA operations would be completed by the time mhi_ep_remove() gets called. This can lead to UAF (Use-After-Free) issues as the DMA callback can trigger xfer_cb() even after mhi_ep_remove() has returned. So to fix this issue, introduce the mhi_cntrl->flush_async() callback and call it in mhi_ep_remove() to drain all the in-flight async transfers before disconnecting the channels. The completion handlers keep triggering xfer_cb() as long as it is set. So flushing the transfers after notifying the client about the disconnect (-ENOTCONN) would still let a success callback slip through afterwards and lead to the same UAF. So disable the channels first to prevent new transfers, then flush the in-flight transfers so that their completions are delivered while xfer_cb() is still valid and only then notify the disconnect and clear xfer_cb(). Fixes: 2547beb00ddb ("bus: mhi: ep: Add support for async DMA read operation") Fixes: ee08acb58fe4 ("bus: mhi: ep: Add support for async DMA write operation") Reviewed-by: Frank Li Cc: stable+noautosel@kernel.org # Needs dmaengine driver fix as well Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/ep/main.c | 18 +++++++++++++++++- include/linux/mhi_ep.h | 2 ++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index 43d680778704..73663e7c1120 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -1620,6 +1620,7 @@ static void mhi_ep_remove(struct device *dev) { struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev); struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver); + struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl; struct mhi_result result = {}; struct mhi_ep_chan *mhi_chan; int dir; @@ -1628,6 +1629,22 @@ static void mhi_ep_remove(struct device *dev) if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER) return; + /* Disable the channels to prevent new transfers */ + for (dir = 0; dir < 2; dir++) { + mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; + + if (!mhi_chan) + continue; + + mutex_lock(&mhi_chan->lock); + mhi_chan->state = MHI_CH_STATE_DISABLED; + mutex_unlock(&mhi_chan->lock); + } + + /* Flush in-flight transfers before notifying disconnect */ + if (mhi_cntrl->flush_async) + mhi_cntrl->flush_async(mhi_cntrl); + /* Disconnect the channels associated with the driver */ for (dir = 0; dir < 2; dir++) { mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; @@ -1643,7 +1660,6 @@ static void mhi_ep_remove(struct device *dev) mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); } - mhi_chan->state = MHI_CH_STATE_DISABLED; mhi_chan->xfer_cb = NULL; mutex_unlock(&mhi_chan->lock); } diff --git a/include/linux/mhi_ep.h b/include/linux/mhi_ep.h index 7b40fc8cbe77..f6383a57a872 100644 --- a/include/linux/mhi_ep.h +++ b/include/linux/mhi_ep.h @@ -107,6 +107,7 @@ struct mhi_ep_buf_info { * @write_sync: CB function for writing to host memory synchronously * @read_async: CB function for reading from host memory asynchronously * @write_async: CB function for writing to host memory asynchronously + * @flush_async: CB function for flushing asynchronous read/writes * @mhi_state: MHI Endpoint state * @max_chan: Maximum channels supported by the endpoint controller * @mru: MRU (Maximum Receive Unit) value of the endpoint controller @@ -164,6 +165,7 @@ struct mhi_ep_cntrl { int (*write_sync)(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_buf_info *buf_info); int (*read_async)(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_buf_info *buf_info); int (*write_async)(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_buf_info *buf_info); + void (*flush_async)(struct mhi_ep_cntrl *mhi_cntrl); enum mhi_state mhi_state; From 5993e10b82a4bc5b034870dae788a7eb5ce62e93 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 22 Jul 2026 07:54:45 +0200 Subject: [PATCH 374/513] bus: mhi: ep: Flush async transfers before notifying disconnect in mhi_ep_abort_transfer() mhi_ep_abort_transfer() notifies the client drivers about the channel disconnect using -ENOTCONN and only then flushes the ring workqueue to drain the in-flight transfers. But the async DMA transfers issued by the ring workers can still complete after the notification. And the completion handlers trigger the client xfer_cb() as long as it is set. So a transfer completing during the flush can deliver a success callback to the client even after it has been notified about the disconnect. This can lead to UAF (Use-After-Free) issues as the client can free its per-transfer resources in response to the -ENOTCONN notification and the trailing success callback would then reference the freed resources. So to fix this issue, disable all the channels first to prevent new transfers and then drain both the ring workqueue and the in-flight async transfers before notifying the disconnect. The completion and queue paths bail out once the channel state is not MHI_CH_STATE_RUNNING, so disabling the channels upfront makes sure that no new transfer sneaks in during the drain and all the pending completions are delivered while xfer_cb() is still valid. Reviewed-by: Frank Li Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/ep/main.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index 73663e7c1120..038b47158f0e 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -1027,26 +1027,37 @@ static void mhi_ep_abort_transfer(struct mhi_ep_cntrl *mhi_cntrl) struct mhi_ep_chan *mhi_chan; int i; - /* Stop all the channels */ + /* Disable all the channels to prevent new transfers */ + for (i = 0; i < mhi_cntrl->max_chan; i++) { + mhi_chan = &mhi_cntrl->mhi_chan[i]; + if (!mhi_chan->ring.started) + continue; + + mutex_lock(&mhi_chan->lock); + mhi_chan->state = MHI_CH_STATE_DISABLED; + mutex_unlock(&mhi_chan->lock); + } + + /* Drain ring workers and in-flight transfers before notifying disconnect */ + flush_workqueue(mhi_cntrl->wq); + if (mhi_cntrl->flush_async) + mhi_cntrl->flush_async(mhi_cntrl); + + /* Send channel disconnect status to client drivers */ for (i = 0; i < mhi_cntrl->max_chan; i++) { mhi_chan = &mhi_cntrl->mhi_chan[i]; if (!mhi_chan->ring.started) continue; mutex_lock(&mhi_chan->lock); - /* Send channel disconnect status to client drivers */ if (mhi_chan->xfer_cb) { result.transaction_status = -ENOTCONN; result.bytes_xferd = 0; mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); } - - mhi_chan->state = MHI_CH_STATE_DISABLED; mutex_unlock(&mhi_chan->lock); } - flush_workqueue(mhi_cntrl->wq); - /* Destroy devices associated with all channels */ device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_ep_destroy_device); From 9656bcd4c321a799148d00dd830ce7ebf20011da Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 22 Jul 2026 07:54:46 +0200 Subject: [PATCH 375/513] PCI: epf-mhi: Implement mhi_cntrl->flush_async() to flush DMA read/write The MHI core needs to make sure that all the current DMA transactions are completed before removing the channels. So implement the mhi_cntrl->flush_async() callback by first making sure all the in-flight DMA operations are completed and then flushing the DMA workqueue. Reviewed-by: Frank Li Signed-off-by: Manivannan Sadhasivam --- drivers/pci/endpoint/functions/pci-epf-mhi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c index 7f5326925ed5..8d2d9d01cfd2 100644 --- a/drivers/pci/endpoint/functions/pci-epf-mhi.c +++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c @@ -644,6 +644,15 @@ static int pci_epf_mhi_edma_write_async(struct mhi_ep_cntrl *mhi_cntrl, return ret; } +static void pci_epf_mhi_edma_flush_async(struct mhi_ep_cntrl *mhi_cntrl) +{ + struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl); + + dmaengine_synchronize(epf_mhi->dma_chan_rx); + dmaengine_synchronize(epf_mhi->dma_chan_tx); + flush_workqueue(epf_mhi->dma_wq); +} + struct epf_dma_filter { struct device *dev; u32 dma_mask; @@ -812,6 +821,7 @@ static int pci_epf_mhi_link_up(struct pci_epf *epf) mhi_cntrl->write_sync = pci_epf_mhi_edma_write; mhi_cntrl->read_async = pci_epf_mhi_edma_read_async; mhi_cntrl->write_async = pci_epf_mhi_edma_write_async; + mhi_cntrl->flush_async = pci_epf_mhi_edma_flush_async; } /* Register the MHI EP controller */ From c3306dd3bd4c4eb3d8452f21a4487c05facd4d4a Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Wed, 29 Jul 2026 10:43:43 +0100 Subject: [PATCH 376/513] misc: fastrpc: Move prints outside spinlock in fastrpc_cb_probe dev_err() and dev_info() were called while holding a spinlock with IRQs disabled, which is incorrect as printk can be slow and should not be called in atomic context. Move the dev_err() for the FASTRPC_MAX_SESSIONS check to after the spinlock is released, and save the return value of of_property_read_u32() to print dev_info() after the lock is dropped. Minor variable style correction in probe function. Signed-off-by: Mukesh Ojha Reviewed-by: Bjorn Andersson Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094352.111065-2-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index f3a49384586d..a9b2ae44c06f 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -2223,19 +2223,22 @@ static int fastrpc_cb_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; int i, sessions = 0; unsigned long flags; - int rc; u32 dma_bits; + u32 sid = 0; + int rc; cctx = dev_get_drvdata(dev->parent); if (!cctx) return -EINVAL; of_property_read_u32(dev->of_node, "qcom,nsessions", &sessions); + if (of_property_read_u32(dev->of_node, "reg", &sid)) + dev_info(dev, "FastRPC Session ID not specified in DT\n"); spin_lock_irqsave(&cctx->lock, flags); if (cctx->sesscount >= FASTRPC_MAX_SESSIONS) { - dev_err(&pdev->dev, "too many sessions\n"); spin_unlock_irqrestore(&cctx->lock, flags); + dev_err(&pdev->dev, "too many sessions\n"); return -ENOSPC; } dma_bits = cctx->soc_data->dma_addr_bits_default; @@ -2244,13 +2247,11 @@ static int fastrpc_cb_probe(struct platform_device *pdev) sess->valid = true; sess->dev = dev; dev_set_drvdata(dev, sess); + sess->sid = sid; if (cctx->domain_id == CDSP_DOMAIN_ID) dma_bits = cctx->soc_data->dma_addr_bits_cdsp; - if (of_property_read_u32(dev->of_node, "reg", &sess->sid)) - dev_info(dev, "FastRPC Session ID not specified in DT\n"); - if (sessions > 0) { struct fastrpc_session_ctx *dup_sess; From 23fce99d0555d053669d2da41214eef5cb32f7b3 Mon Sep 17 00:00:00 2001 From: Ekansh Gupta Date: Wed, 29 Jul 2026 10:43:44 +0100 Subject: [PATCH 377/513] misc: fastrpc: Allow fastrpc_buf_free() to accept NULL Make fastrpc_buf_free() a no-op when passed a NULL pointer, allowing callers to avoid open-coded NULL checks. Reviewed-by: Dmitry Baryshkov Signed-off-by: Ekansh Gupta Signed-off-by: Jianping Li Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094352.111065-3-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index a9b2ae44c06f..32da4664de58 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -422,6 +422,9 @@ static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd, static void fastrpc_buf_free(struct fastrpc_buf *buf) { + if (!buf) + return; + dma_free_coherent(buf->dev, buf->size, buf->virt, fastrpc_ipa_to_dma_addr(buf->fl->cctx, buf->dma_addr)); kfree(buf); @@ -514,8 +517,7 @@ static void fastrpc_user_free(struct kref *ref) struct fastrpc_map *map, *m; struct fastrpc_buf *buf, *b; - if (fl->init_mem) - fastrpc_buf_free(fl->init_mem); + fastrpc_buf_free(fl->init_mem); list_for_each_entry_safe(ctx, n, &fl->pending, node) { list_del(&ctx->node); @@ -560,8 +562,7 @@ static void fastrpc_context_free(struct kref *ref) for (i = 0; i < ctx->nbufs; i++) fastrpc_map_put(ctx->maps[i]); - if (ctx->buf) - fastrpc_buf_free(ctx->buf); + fastrpc_buf_free(ctx->buf); spin_lock_irqsave(&cctx->lock, flags); idr_remove(&cctx->ctx_idr, ctx->ctxid >> 4); From bc53df3d174f480c553e608fe862b39dc0da4fc1 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 29 Jul 2026 10:43:45 +0100 Subject: [PATCH 378/513] MAINTAINERS: fastrpc: remove inactive maintainer and add reviewer Amol Maheshwari has not been actively involved in the FASTRPC driver for some time. Remove him from the maintainer list. Add Ekansh Gupta as a reviewer to reflect his ongoing involvement and active contributions to the FASTRPC driver from the Qualcomm side. Signed-off-by: Srinivas Kandagatla Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094352.111065-4-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index c4f6a6c32384..e3ebcfdeba39 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22212,7 +22212,7 @@ F: drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c QUALCOMM FASTRPC DRIVER M: Srinivas Kandagatla -M: Amol Maheshwari +R: Ekansh Gupta L: linux-arm-msm@vger.kernel.org L: dri-devel@lists.freedesktop.org S: Maintained From 1b59271fa645f76b10e3edfe7c76e75aa95c042c Mon Sep 17 00:00:00 2001 From: Ekansh Gupta Date: Wed, 29 Jul 2026 10:43:46 +0100 Subject: [PATCH 379/513] misc: fastrpc: Move fdlist to invoke context structure The fdlist is currently part of the meta buffer which is set during fastrpc_get_args(), this fdlist is getting recalculated during fastrpc_put_args(). Move fdlist to the invoke context structure to improve maintainability and reduce redundancy. This centralizes its handling and simplifies meta buffer preparation and reading logic. Reviewed-by: Dmitry Baryshkov Signed-off-by: Ekansh Gupta Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094352.111065-5-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 32da4664de58..885ba7d1bff5 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -233,6 +233,7 @@ struct fastrpc_invoke_ctx { int pid; int client_id; u32 sc; + u64 *fdlist; u32 *crc; u64 ctxid; u64 msg_sz; @@ -1063,6 +1064,7 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx) rpra = ctx->buf->virt; list = fastrpc_invoke_buf_start(rpra, ctx->nscalars); pages = fastrpc_phy_page_start(list, ctx->nscalars); + ctx->fdlist = (u64 *)(pages + ctx->nscalars); args = (uintptr_t)ctx->buf->virt + metalen; rlen = pkt_size - metalen; ctx->rpra = rpra; @@ -1165,18 +1167,11 @@ static int fastrpc_put_args(struct fastrpc_invoke_ctx *ctx, union fastrpc_remote_arg *rpra = ctx->rpra; struct fastrpc_user *fl = ctx->fl; struct fastrpc_map *mmap = NULL; - struct fastrpc_invoke_buf *list; - struct fastrpc_phy_page *pages; - u64 *fdlist; - int i, inbufs, outbufs, handles; + u64 *fdlist = ctx->fdlist; + int i, inbufs; int ret = 0; inbufs = REMOTE_SCALARS_INBUFS(ctx->sc); - outbufs = REMOTE_SCALARS_OUTBUFS(ctx->sc); - handles = REMOTE_SCALARS_INHANDLES(ctx->sc) + REMOTE_SCALARS_OUTHANDLES(ctx->sc); - list = fastrpc_invoke_buf_start(rpra, ctx->nscalars); - pages = fastrpc_phy_page_start(list, ctx->nscalars); - fdlist = (uint64_t *)(pages + inbufs + outbufs + handles); for (i = inbufs; i < ctx->nbufs; ++i) { if (!ctx->maps[i]) { From 713b54b86aab2ba2e477540f707a7aa6d295d443 Mon Sep 17 00:00:00 2001 From: Ekansh Gupta Date: Wed, 29 Jul 2026 10:43:47 +0100 Subject: [PATCH 380/513] misc: fastrpc: Replace hardcoded ctxid mask with GENMASK Replace the hardcoded context ID mask (0xFF0) with GENMASK(11, 4) to improve readability and follow kernel bitfield conventions. Use FIELD_PREP and FIELD_GET instead of manual shifts for setting and extracting ctxid values. Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Signed-off-by: Ekansh Gupta Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094352.111065-6-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 885ba7d1bff5..9d8dcd836a75 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -22,7 +22,9 @@ #include #include #include +#include #include +#include #define ADSP_DOMAIN_ID (0) #define MDSP_DOMAIN_ID (1) @@ -37,7 +39,7 @@ #define FASTRPC_CTX_MAX (256) #define FASTRPC_INIT_HANDLE 1 #define FASTRPC_DSP_UTILITIES_HANDLE 2 -#define FASTRPC_CTXID_MASK (0xFF0) +#define FASTRPC_CTXID_MASK GENMASK(11, 4) #define INIT_FILELEN_MAX (2 * 1024 * 1024) #define INIT_FILE_NAMELEN_MAX (128) #define FASTRPC_DEVICE_NAME "fastrpc" @@ -566,7 +568,7 @@ static void fastrpc_context_free(struct kref *ref) fastrpc_buf_free(ctx->buf); spin_lock_irqsave(&cctx->lock, flags); - idr_remove(&cctx->ctx_idr, ctx->ctxid >> 4); + idr_remove(&cctx->ctx_idr, FIELD_GET(FASTRPC_CTXID_MASK, ctx->ctxid)); spin_unlock_irqrestore(&cctx->lock, flags); kfree(ctx->maps); @@ -704,7 +706,7 @@ static struct fastrpc_invoke_ctx *fastrpc_context_alloc( spin_unlock_irqrestore(&cctx->lock, flags); goto err_idr; } - ctx->ctxid = ret << 4; + ctx->ctxid = FIELD_PREP(FASTRPC_CTXID_MASK, ret); spin_unlock_irqrestore(&cctx->lock, flags); kref_init(&ctx->refcount); @@ -2539,7 +2541,7 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data, if (!cctx) return -ENODEV; - ctxid = ((rsp->ctx & FASTRPC_CTXID_MASK) >> 4); + ctxid = FIELD_GET(FASTRPC_CTXID_MASK, rsp->ctx); spin_lock_irqsave(&cctx->lock, flags); ctx = idr_find(&cctx->ctx_idr, ctxid); From 62373b7879ada744ea4fc6766b2fb55229eac11f Mon Sep 17 00:00:00 2001 From: Ekansh Gupta Date: Wed, 29 Jul 2026 10:43:48 +0100 Subject: [PATCH 381/513] misc: fastrpc: Expand context ID mask for DSP polling mode support Current FastRPC context uses a 12-bit mask: [ID(8 bits)][PD type(4 bits)] = GENMASK(11, 4) This works for normal calls but fails for DSP polling mode. Polling mode expects a 16-bit layout: [15:8] = context ID (8 bits) [7:5] = reserved [4] = async mode bit [3:0] = PD type (4 bits) If async bit (bit 4) is set, DSP disables polling. With current mask, odd IDs can set this bit, causing DSP to skip poll updates. Update FASTRPC_CTXID_MASK to GENMASK(15, 8) so IDs occupy upper byte and lower byte is left for DSP flags and PD type. Reserved bits remain unused. This change is compatible with polling mode and does not break non-polling behavior. Bit layout: [15:8] = CCCCCCCC (context ID) [7:5] = xxx (reserved) [4] = A (async mode) [3:0] = PPPP (PD type) Reviewed-by: Dmitry Baryshkov Signed-off-by: Ekansh Gupta Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094352.111065-7-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 9d8dcd836a75..d42b3e9e2a07 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -39,7 +39,7 @@ #define FASTRPC_CTX_MAX (256) #define FASTRPC_INIT_HANDLE 1 #define FASTRPC_DSP_UTILITIES_HANDLE 2 -#define FASTRPC_CTXID_MASK GENMASK(11, 4) +#define FASTRPC_CTXID_MASK GENMASK(15, 8) #define INIT_FILELEN_MAX (2 * 1024 * 1024) #define INIT_FILE_NAMELEN_MAX (128) #define FASTRPC_DEVICE_NAME "fastrpc" From 96e62cc0a84dfd3822b614cd149b4e932c6953c7 Mon Sep 17 00:00:00 2001 From: Ekansh Gupta Date: Wed, 29 Jul 2026 10:43:49 +0100 Subject: [PATCH 382/513] misc: fastrpc: Add polling mode support for fastRPC driver For any remote call to DSP, after sending an invocation message, the fastRPC driver waits for a glink response, during which the CPU can enter low power modes. This adds latency to the fastRPC call due to CPU wakeup and scheduling overhead. Add polling mode support where the fastRPC driver polls a shared memory location for completion after sending the invocation, avoiding CPU wakeup and scheduling latency and reducing fastRPC overhead. If the poll times out, the call falls back to the normal interrupt/glink-based completion path. Poll mode is only applied to dynamic modules running in a user PD (handle > FASTRPC_MAX_STATIC_HANDLE), since static/root-PD handles are not expected to benefit from, or require, this optimization. Support is advertised per SoC via fastrpc_soc_data, with a closed exception list for older platforms whose DSP firmware is known to support polling but which otherwise use the default soc_data. Poll mode can be enabled by userspace via the FASTRPC_IOCTL_SET_OPTION ioctl with the FASTRPC_POLL_MODE request id. Since context IDs (ctxid) are allocated from a fixed-size, per-channel cyclic IDR shared by all processes on a DSP, a context ID can be recycled for a new request soon after it is freed. In poll mode the context can be considered complete (and released) as soon as the poll memory is updated, while the corresponding glink COMPLETE response from the DSP may still be in flight. If that response arrives after the ctxid has been reused, it would otherwise match the new context and incorrectly signal completion for it while the DSP may still be operating on the new context's buffers. To prevent this, embed a monotonically increasing per-channel sequence number in the unused upper bits of the ctxid/message context and validate it in the rpmsg callback, dropping any response whose sequence number does not match the current owner of that ctxid slot. Signed-off-by: Ekansh Gupta Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094352.111065-8-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 194 ++++++++++++++++++++++++++++++++++-- include/uapi/misc/fastrpc.h | 29 ++++++ 2 files changed, 215 insertions(+), 8 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index d42b3e9e2a07..93aa6556bd47 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #define ADSP_DOMAIN_ID (0) #define MDSP_DOMAIN_ID (1) @@ -39,7 +41,16 @@ #define FASTRPC_CTX_MAX (256) #define FASTRPC_INIT_HANDLE 1 #define FASTRPC_DSP_UTILITIES_HANDLE 2 +/* + * Maximum handle value for static handles. + * Static handles are pre-defined, fixed numeric values statically assigned + * in the IDL file or FastRPC framework. + */ +#define FASTRPC_MAX_STATIC_HANDLE (20) #define FASTRPC_CTXID_MASK GENMASK(15, 8) +/* Sequence number occupies bits 63:16 of the ctxid / message context */ +#define FASTRPC_CTXID_SEQ_SHIFT 16 +#define FASTRPC_CTXID_SEQ_MASK GENMASK_ULL(63, 16) #define INIT_FILELEN_MAX (2 * 1024 * 1024) #define INIT_FILE_NAMELEN_MAX (128) #define FASTRPC_DEVICE_NAME "fastrpc" @@ -107,6 +118,12 @@ #define miscdev_to_fdevice(d) container_of(d, struct fastrpc_device, miscdev) +/* Poll response number from remote processor for call completion */ +#define FASTRPC_POLL_RESPONSE (0xdecaf) + +/* Polling mode timeout limit */ +#define FASTRPC_POLL_MAX_TIMEOUT_US (10000) + struct fastrpc_phy_page { dma_addr_t addr; /* dma address */ u64 size; /* size of contiguous region */ @@ -237,8 +254,14 @@ struct fastrpc_invoke_ctx { u32 sc; u64 *fdlist; u32 *crc; + /* Poll memory that DSP updates */ + u32 *poll_addr; u64 ctxid; u64 msg_sz; + /* work done status flag */ + bool is_work_done; + /* process updates poll memory instead of glink response */ + bool is_polled; struct kref refcount; struct list_head node; /* list of ctxs */ struct completion work; @@ -264,6 +287,7 @@ struct fastrpc_soc_data { u32 sid_pos; u32 dma_addr_bits_cdsp; u32 dma_addr_bits_default; + bool poll_mode_supported; }; struct fastrpc_channel_ctx { @@ -286,6 +310,9 @@ struct fastrpc_channel_ctx { struct list_head invoke_interrupted_mmaps; bool secure; bool unsigned_support; + bool poll_mode_supported; + /* Per-channel sequence counter; incremented on every context allocation */ + atomic_t ctx_seq; u64 dma_mask; const struct fastrpc_soc_data *soc_data; }; @@ -309,6 +336,8 @@ struct fastrpc_user { int client_id; int pd; bool is_secure_dev; + /* Flags poll mode state */ + bool poll_mode; /* Lock for lists */ spinlock_t lock; /* lock for allocations */ @@ -706,7 +735,9 @@ static struct fastrpc_invoke_ctx *fastrpc_context_alloc( spin_unlock_irqrestore(&cctx->lock, flags); goto err_idr; } - ctx->ctxid = FIELD_PREP(FASTRPC_CTXID_MASK, ret); + ctx->ctxid = FIELD_PREP(FASTRPC_CTXID_MASK, ret) | + FIELD_PREP(FASTRPC_CTXID_SEQ_MASK, + (u64)atomic_inc_return(&cctx->ctx_seq)); spin_unlock_irqrestore(&cctx->lock, flags); kref_init(&ctx->refcount); @@ -971,7 +1002,8 @@ static int fastrpc_get_meta_size(struct fastrpc_invoke_ctx *ctx) sizeof(struct fastrpc_invoke_buf) + sizeof(struct fastrpc_phy_page)) * ctx->nscalars + sizeof(u64) * FASTRPC_MAX_FDLIST + - sizeof(u32) * FASTRPC_MAX_CRCLIST; + sizeof(u32) * FASTRPC_MAX_CRCLIST + + sizeof(u32); return size; } @@ -1067,6 +1099,9 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx) list = fastrpc_invoke_buf_start(rpra, ctx->nscalars); pages = fastrpc_phy_page_start(list, ctx->nscalars); ctx->fdlist = (u64 *)(pages + ctx->nscalars); + ctx->poll_addr = (u32 *)((uintptr_t)ctx->fdlist + sizeof(u64) * FASTRPC_MAX_FDLIST + + sizeof(u32) * FASTRPC_MAX_CRCLIST); + args = (uintptr_t)ctx->buf->virt + metalen; rlen = pkt_size - metalen; ctx->rpra = rpra; @@ -1236,6 +1271,71 @@ static int fastrpc_invoke_send(struct fastrpc_session_ctx *sctx, } +static u32 fastrpc_read_poll_addr(struct fastrpc_invoke_ctx *ctx) +{ + dma_rmb(); + return READ_ONCE(*ctx->poll_addr); +} + +static int poll_for_remote_response(struct fastrpc_invoke_ctx *ctx) +{ + u32 val; + int ret; + + /* + * Poll until DSP writes FASTRPC_POLL_RESPONSE into *ctx->poll_addr + * or until another path marks the work done. + */ + ret = read_poll_timeout_atomic(fastrpc_read_poll_addr, val, + (val == FASTRPC_POLL_RESPONSE) || ctx->is_work_done, 1, + FASTRPC_POLL_MAX_TIMEOUT_US, false, ctx); + + if (!ret && val == FASTRPC_POLL_RESPONSE) { + /* + * DSP writes FASTRPC_POLL_RESPONSE to signal successful + * completion via the poll path. + */ + ctx->is_work_done = true; + ctx->retval = 0; + } + + if (ret == -ETIMEDOUT) + ret = -EIO; + + return ret; +} + +static inline int fastrpc_wait_for_response(struct fastrpc_invoke_ctx *ctx, + u32 kernel) +{ + int err = 0; + + if (kernel) { + if (!wait_for_completion_timeout(&ctx->work, 10 * HZ)) + err = -ETIMEDOUT; + } else { + err = wait_for_completion_interruptible(&ctx->work); + } + + return err; +} + +static int fastrpc_wait_for_completion(struct fastrpc_invoke_ctx *ctx, + u32 kernel) +{ + int err; + + if (ctx->is_polled) { + err = poll_for_remote_response(ctx); + if (!err) + return 0; + /* If polling timed out or failed, move to normal response mode */ + ctx->is_polled = false; + } + + return fastrpc_wait_for_response(ctx, kernel); +} + static int fastrpc_internal_invoke(struct fastrpc_user *fl, u32 kernel, u32 handle, u32 sc, struct fastrpc_invoke_args *args) @@ -1271,13 +1371,14 @@ static int fastrpc_internal_invoke(struct fastrpc_user *fl, u32 kernel, if (err) goto bail; - if (kernel) { - if (!wait_for_completion_timeout(&ctx->work, 10 * HZ)) - err = -ETIMEDOUT; - } else { - err = wait_for_completion_interruptible(&ctx->work); - } + /* + * Set message context as polled if the call is for a user PD + * dynamic module and user has enabled poll mode. + */ + if (handle > FASTRPC_MAX_STATIC_HANDLE && fl->pd == USER_PD && fl->poll_mode) + ctx->is_polled = true; + err = fastrpc_wait_for_completion(ctx, kernel); if (err) goto bail; @@ -1841,6 +1942,35 @@ static int fastrpc_get_info_from_kernel(struct fastrpc_ioctl_capability *cap, return 0; } +static int fastrpc_set_option(struct fastrpc_user *fl, char __user *argp) +{ + struct fastrpc_ioctl_set_option opt = {0}; + int i; + + if (copy_from_user(&opt, argp, sizeof(opt))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(opt.reserved); i++) { + if (opt.reserved[i] != 0) + return -EINVAL; + } + + if (opt.request_id != FASTRPC_POLL_MODE) + return -EINVAL; + + if (!fl->cctx->poll_mode_supported) + return -EOPNOTSUPP; + + if (opt.value == FASTRPC_POLL_MODE_ENABLE) + fl->poll_mode = true; + else if (opt.value == FASTRPC_POLL_MODE_DISABLE) + fl->poll_mode = false; + else + return -EINVAL; + + return 0; +} + static int fastrpc_get_dsp_info(struct fastrpc_user *fl, char __user *argp) { struct fastrpc_ioctl_capability cap = {0}; @@ -2196,6 +2326,9 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int cmd, case FASTRPC_IOCTL_MEM_UNMAP: err = fastrpc_req_mem_unmap(fl, argp); break; + case FASTRPC_IOCTL_SET_OPTION: + err = fastrpc_set_option(fl, argp); + break; case FASTRPC_IOCTL_GET_DSP_INFO: err = fastrpc_get_dsp_info(fl, argp); break; @@ -2352,6 +2485,7 @@ static const struct fastrpc_soc_data kaanapali_soc_data = { .sid_pos = 56, .dma_addr_bits_cdsp = 34, .dma_addr_bits_default = 32, + .poll_mode_supported = true, }; static const struct fastrpc_soc_data default_soc_data = { @@ -2360,6 +2494,29 @@ static const struct fastrpc_soc_data default_soc_data = { .dma_addr_bits_default = 32, }; +/* + * Exception list for older platforms that use default_soc_data but whose + * DSP firmware supports FastRPC polling mode. + * + * NOTE: This list is intentionally closed. + * Do NOT add new platforms here. New SoCs must advertise polling mode + * support via their soc_data. + */ + +static const struct of_device_id fastrpc_poll_supported_machines[] __maybe_unused = { + { .compatible = "qcom,milos" }, + { .compatible = "qcom,qcs8300" }, + { .compatible = "qcom,sa8775p" }, + { .compatible = "qcom,sar2130p" }, + { .compatible = "qcom,sm8450" }, + { .compatible = "qcom,sm8550" }, + { .compatible = "qcom,sm8650" }, + { .compatible = "qcom,sm8750" }, + { .compatible = "qcom,x1e80100" }, + { .compatible = "qcom,x1p42100" }, + {}, +}; + static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) { struct device *rdev = &rpdev->dev; @@ -2426,6 +2583,8 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) secure_dsp = !(of_property_read_bool(rdev->of_node, "qcom,non-secure-domain")); data->secure = secure_dsp; data->soc_data = soc_data; + data->poll_mode_supported = soc_data->poll_mode_supported || + of_machine_get_match(fastrpc_poll_supported_machines); switch (domain_id) { case ADSP_DOMAIN_ID: @@ -2455,6 +2614,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) } kref_init(&data->refcount); + atomic_set(&data->ctx_seq, 0); rdev->dma_mask = &data->dma_mask; dma_set_mask_and_coherent(rdev, DMA_BIT_MASK(32)); @@ -2552,7 +2712,25 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data, return -ENOENT; } + /* + * Validate the sequence number embedded in the upper bits of the + * context ID. Under high concurrency the IDR slot can be recycled + * for a new request before a late (or duplicate) glink COMPLETE + * response for the previous request arrives. Without this check the + * driver would call complete() on the wrong context, waking a thread + * whose buffers are still being accessed by the DSP. + */ + if (FIELD_GET(FASTRPC_CTXID_SEQ_MASK, rsp->ctx) != + FIELD_GET(FASTRPC_CTXID_SEQ_MASK, ctx->ctxid)) { + dev_dbg(&rpdev->dev, + "Stale glink response ctx 0x%llx (expected seq 0x%llx), dropping\n", + rsp->ctx, + FIELD_GET(FASTRPC_CTXID_SEQ_MASK, ctx->ctxid)); + return 0; + } + ctx->retval = rsp->retval; + ctx->is_work_done = true; complete(&ctx->work); /* diff --git a/include/uapi/misc/fastrpc.h b/include/uapi/misc/fastrpc.h index c6e2925f47e6..ba1ea5ed426c 100644 --- a/include/uapi/misc/fastrpc.h +++ b/include/uapi/misc/fastrpc.h @@ -16,6 +16,7 @@ #define FASTRPC_IOCTL_INIT_CREATE_STATIC _IOWR('R', 9, struct fastrpc_init_create_static) #define FASTRPC_IOCTL_MEM_MAP _IOWR('R', 10, struct fastrpc_mem_map) #define FASTRPC_IOCTL_MEM_UNMAP _IOWR('R', 11, struct fastrpc_mem_unmap) +#define FASTRPC_IOCTL_SET_OPTION _IOWR('R', 12, struct fastrpc_ioctl_set_option) #define FASTRPC_IOCTL_GET_DSP_INFO _IOWR('R', 13, struct fastrpc_ioctl_capability) /** @@ -67,6 +68,28 @@ enum fastrpc_proc_attr { /* Fastrpc attribute for memory protection of buffers */ #define FASTRPC_ATTR_SECUREMAP (1) +/** + * FASTRPC_POLL_MODE - Enable/disable poll mode for FastRPC invocations + * + * Poll mode is an optimization that allows the CPU to poll shared memory + * for completion instead of waiting for an interrupt-based response. + * This reduces latency for fast-completing operations. + * + * Restrictions: + * - Only supported for USER_PD (User Protection Domain) + * - Only applies to dynamic modules (handle > 20) + * - Static modules always use interrupt-based completion + * + * Values: + * - 0: Disable poll mode (use interrupt-based completion) + * - 1: Enable poll mode (poll shared memory for completion) + */ +#define FASTRPC_POLL_MODE (1) + +/* Values for FASTRPC_POLL_MODE request */ +#define FASTRPC_POLL_MODE_DISABLE 0 +#define FASTRPC_POLL_MODE_ENABLE 1 + struct fastrpc_invoke_args { __u64 ptr; __u64 length; @@ -133,6 +156,12 @@ struct fastrpc_mem_unmap { __s32 reserved[5]; }; +struct fastrpc_ioctl_set_option { + __u32 request_id; /* Request type (e.g., FASTRPC_POLL_MODE) */ + __u32 value; /* Request-specific value */ + __s32 reserved[6]; +}; + struct fastrpc_ioctl_capability { __u32 unused; /* deprecated, ignored by the kernel */ __u32 attribute_id; From 0273a35af9326ac5434746faf577e1f3afe4121c Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 29 Jul 2026 10:43:50 +0100 Subject: [PATCH 383/513] dt-bindings: misc: qcom,fastrpc: Document Nord FastRPC Add compatible for Qualcomm Nord FastRPC which is compatible with Kaanapali FastRPC. As there are 4 CDSPs on Nord, add label for cdsp2 and cdsp3 as well. Reviewed-by: Bartosz Golaszewski Reviewed-by: Ekansh Gupta Signed-off-by: Shawn Guo Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094352.111065-9-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml b/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml index 2876fdd7c6e6..47e5976c8743 100644 --- a/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml +++ b/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml @@ -26,6 +26,7 @@ properties: - enum: - qcom,glymur-fastrpc - qcom,hawi-fastrpc + - qcom,nord-fastrpc - const: qcom,kaanapali-fastrpc label: @@ -35,6 +36,8 @@ properties: - sdsp - cdsp - cdsp1 + - cdsp2 + - cdsp3 - gdsp0 - gdsp1 From c499ea39f003bb6cfe6d4ad869b33603b6e85400 Mon Sep 17 00:00:00 2001 From: Yijie Yang Date: Wed, 29 Jul 2026 10:43:51 +0100 Subject: [PATCH 384/513] dt-bindings: misc: qcom,fastrpc: Add Maili FastRPC compatible Document compatible string for the FastRPC interface on the Qualcomm Maili SoC, which is compatible with the Qualcomm Kaanapali FastRPC and can fallback to Kaanapali. Signed-off-by: Yijie Yang Acked-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094352.111065-10-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml b/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml index 47e5976c8743..49d2a5e8ba39 100644 --- a/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml +++ b/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml @@ -26,6 +26,7 @@ properties: - enum: - qcom,glymur-fastrpc - qcom,hawi-fastrpc + - qcom,maili-fastrpc - qcom,nord-fastrpc - const: qcom,kaanapali-fastrpc From 5533bb4bc53c6cd401b9b329d7e26802fd5aa1f5 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 29 Jul 2026 10:43:52 +0100 Subject: [PATCH 385/513] misc: fastrpc: Drop unhandled DSP PD exit notification Newer DSP firmware implements a PD (Protection Domain) notification framework that sends PD state notifications upon request. The PD exit notification is unconditionally sent by the DSP with a fixed sentinel 0xABCDABCD in the context field. fastrpc_rpmsg_callback() treats every inbound message as an invoke response, so the sentinel is masked and shifted like any real response ((0xABCDABCD & 0xFF0) >> 4 == 188) and looked up in the channel's context idr. This is not merely cosmetic. In the common case idr slot 188 is empty, the lookup fails, and the driver only logs a spurious "No context ID matches response" error on every teardown. But the context idr is shared by every protection domain and the listener thread on the channel and is filled cyclically over [1, FASTRPC_CTX_MAX]. If slot 188 holds a live context when the sentinel arrives, the sentinel's return value is written into that unrelated in-flight invocation and it is completed early. Since neither the fastrpc library nor the driver supports the DSP PD notification framework, it is safe to drop the PD exit notification before it is ever turned into a context lookup. This removes both the log spam and the mis-completion race. A genuine response can never be masked: a real context is (idr_index << 4) | pd (at most 0xFF3) and can never equal the sentinel. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Ekansh Gupta Signed-off-by: Shawn Guo Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094352.111065-11-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 93aa6556bd47..65f2154e3a1d 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -51,6 +51,17 @@ /* Sequence number occupies bits 63:16 of the ctxid / message context */ #define FASTRPC_CTXID_SEQ_SHIFT 16 #define FASTRPC_CTXID_SEQ_MASK GENMASK_ULL(63, 16) + +/* + * Newer DSP firmware implements a PD (Protection Domain) notification + * framework that sends PD state notifications upon request. The PD exit + * notification is unconditionally sent by the DSP with this fixed sentinel + * in the context field rather than the context of an outstanding invocation. + * Since the fastrpc driver does not support the DSP PD notification framework, + * this message must be dropped rather than matched against the context idr. + */ +#define FASTRPC_DSP_PD_NOTIFY_CTX 0xABCDABCD + #define INIT_FILELEN_MAX (2 * 1024 * 1024) #define INIT_FILE_NAMELEN_MAX (128) #define FASTRPC_DEVICE_NAME "fastrpc" @@ -2701,6 +2712,14 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data, if (!cctx) return -ENODEV; + /* + * A PD exit notification from the DSP PD notification framework carries + * this sentinel rather than a real context. Drop it: a real context is + * (idr_index << 4) | pd and can never collide with this value. + */ + if (rsp->ctx == FASTRPC_DSP_PD_NOTIFY_CTX) + return 0; + ctxid = FIELD_GET(FASTRPC_CTXID_MASK, rsp->ctx); spin_lock_irqsave(&cctx->lock, flags); From aa6eb6ceef7434975e5af7fa4830a2756e1388b4 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 29 Jul 2026 10:46:34 +0100 Subject: [PATCH 386/513] nvmem: remove unused field from struct nvmem_device The node list_head in struct nvmem_device was added accidentally by commit ec9c08a1cb8d ("nvmem: Create a header for internal sharing") and is unused so remove it. Signed-off-by: Bartosz Golaszewski Tested-by: Loic Poulain Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094647.111468-2-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/internals.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/nvmem/internals.h b/drivers/nvmem/internals.h index 18fed57270e5..7cbc55f40259 100644 --- a/drivers/nvmem/internals.h +++ b/drivers/nvmem/internals.h @@ -10,7 +10,6 @@ struct nvmem_device { struct module *owner; struct device dev; - struct list_head node; int stride; int word_size; int id; From 06b360155d1281931fc6a3363dc6323a7858c1f0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 29 Jul 2026 10:46:35 +0100 Subject: [PATCH 387/513] nvmem: return -EOPNOTSUPP to in-kernel users on missing callbacks __nvmem_reg_read/write() currently return -EINVAL if the relevant callback is not present. User-space helpers again check the presence of the callbacks to see if they should return -EPERM. Ahead of adding SRCU synchronization: change the error code returned to in-kernel users to -EOPNOTSUPP which is more indicative of the actual reason for the failure. Remove the checks from the sysfs attribute callbacks as these are not visible without the required callbacks. Signed-off-by: Bartosz Golaszewski Tested-by: Loic Poulain Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094647.111468-3-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 6bcb90760bee..40a00f650261 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -55,10 +55,10 @@ static BLOCKING_NOTIFIER_HEAD(nvmem_notifier); static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset, void *val, size_t bytes) { - if (nvmem->reg_read) - return nvmem->reg_read(nvmem->priv, offset, val, bytes); + if (!nvmem->reg_read) + return -EOPNOTSUPP; - return -EINVAL; + return nvmem->reg_read(nvmem->priv, offset, val, bytes); } static int __nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset, @@ -66,14 +66,14 @@ static int __nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset, { int ret; - if (nvmem->reg_write) { - gpiod_set_value_cansleep(nvmem->wp_gpio, 0); - ret = nvmem->reg_write(nvmem->priv, offset, val, bytes); - gpiod_set_value_cansleep(nvmem->wp_gpio, 1); - return ret; - } + if (!nvmem->reg_write) + return -EOPNOTSUPP; - return -EINVAL; + gpiod_set_value_cansleep(nvmem->wp_gpio, 0); + ret = nvmem->reg_write(nvmem->priv, offset, val, bytes); + gpiod_set_value_cansleep(nvmem->wp_gpio, 1); + + return ret; } static int nvmem_access_with_keepouts(struct nvmem_device *nvmem, @@ -231,13 +231,12 @@ static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj, count = round_down(count, nvmem->word_size); - if (!nvmem->reg_read) - return -EPERM; - rc = nvmem_reg_read(nvmem, pos, buf, count); - - if (rc) + if (rc) { + if (rc == -EOPNOTSUPP) + return -EPERM; return rc; + } return count; } @@ -264,13 +263,15 @@ static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj, count = round_down(count, nvmem->word_size); - if (!nvmem->reg_write || nvmem->read_only) + if (nvmem->read_only) return -EPERM; rc = nvmem_reg_write(nvmem, pos, buf, count); - - if (rc) + if (rc) { + if (rc == -EOPNOTSUPP) + return -EPERM; return rc; + } return count; } From c9cde184269d5fd0854d9ffda3ea678888dc44b9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 29 Jul 2026 10:46:36 +0100 Subject: [PATCH 388/513] nvmem: check the return value of gpiod_set_value_cansleep() GPIO setters now return integer values and can indicate failures in lower abstraction layers. Check the return values of gpiod_set_value_cansleep() calls in nvmem core. Reviewed-by: Johan Hovold Signed-off-by: Bartosz Golaszewski Tested-by: Loic Poulain Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094647.111468-4-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 40a00f650261..51ebe82ade93 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -64,16 +64,22 @@ static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset, static int __nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset, void *val, size_t bytes) { - int ret; + int ret, wr_ok; if (!nvmem->reg_write) return -EOPNOTSUPP; - gpiod_set_value_cansleep(nvmem->wp_gpio, 0); - ret = nvmem->reg_write(nvmem->priv, offset, val, bytes); - gpiod_set_value_cansleep(nvmem->wp_gpio, 1); + ret = gpiod_set_value_cansleep(nvmem->wp_gpio, 0); + if (ret) + return ret; - return ret; + wr_ok = nvmem->reg_write(nvmem->priv, offset, val, bytes); + + ret = gpiod_set_value_cansleep(nvmem->wp_gpio, 1); + if (ret) + return ret; + + return wr_ok; } static int nvmem_access_with_keepouts(struct nvmem_device *nvmem, From 2aa495438cc6c6deeb9da562128701ca17cce091 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 29 Jul 2026 10:46:37 +0100 Subject: [PATCH 389/513] nvmem: simplify locking with guard() Use lock guards from cleanup.h to simplify locking. While at it: add the missing mutex.h include. Signed-off-by: Bartosz Golaszewski Tested-by: Loic Poulain Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094647.111468-5-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 88 +++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 51 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 51ebe82ade93..2742de390de6 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -6,6 +6,7 @@ * Copyright (C) 2013 Maxime Ripard */ +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -468,27 +470,23 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) const struct bin_attribute **pattrs; struct bin_attribute *attrs; unsigned int ncells = 0, i = 0; - int ret = 0; + int ret; - mutex_lock(&nvmem_mutex); + guard(mutex)(&nvmem_mutex); if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated) - goto unlock_mutex; + return 0; /* Allocate an array of attributes with a sentinel */ ncells = list_count_nodes(&nvmem->cells); pattrs = devm_kcalloc(&nvmem->dev, ncells + 1, sizeof(struct bin_attribute *), GFP_KERNEL); - if (!pattrs) { - ret = -ENOMEM; - goto unlock_mutex; - } + if (!pattrs) + return -ENOMEM; attrs = devm_kcalloc(&nvmem->dev, ncells, sizeof(struct bin_attribute), GFP_KERNEL); - if (!attrs) { - ret = -ENOMEM; - goto unlock_mutex; - } + if (!attrs) + return -ENOMEM; /* Initialize each attribute to take the name and size of the cell */ list_for_each_entry(entry, &nvmem->cells, node) { @@ -501,10 +499,8 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) attrs[i].size = entry->bytes; attrs[i].read = &nvmem_cell_attr_read; attrs[i].private = entry; - if (!attrs[i].attr.name) { - ret = -ENOMEM; - goto unlock_mutex; - } + if (!attrs[i].attr.name) + return -ENOMEM; pattrs[i] = &attrs[i]; i++; @@ -514,13 +510,10 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) ret = device_add_group(&nvmem->dev, &group); if (ret) - goto unlock_mutex; + return ret; nvmem->sysfs_cells_populated = true; -unlock_mutex: - mutex_unlock(&nvmem_mutex); - return ret; } @@ -558,9 +551,8 @@ static const struct bus_type nvmem_bus_type = { static void nvmem_cell_entry_drop(struct nvmem_cell_entry *cell) { blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_REMOVE, cell); - mutex_lock(&nvmem_mutex); - list_del(&cell->node); - mutex_unlock(&nvmem_mutex); + scoped_guard(mutex, &nvmem_mutex) + list_del(&cell->node); of_node_put(cell->np); kfree_const(cell->name); kfree(cell); @@ -576,9 +568,8 @@ static void nvmem_device_remove_all_cells(const struct nvmem_device *nvmem) static void nvmem_cell_entry_add(struct nvmem_cell_entry *cell) { - mutex_lock(&nvmem_mutex); - list_add_tail(&cell->node, &cell->nvmem->cells); - mutex_unlock(&nvmem_mutex); + scoped_guard(mutex, &nvmem_mutex) + list_add_tail(&cell->node, &cell->nvmem->cells); blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_ADD, cell); } @@ -728,14 +719,14 @@ nvmem_find_cell_entry_by_name(struct nvmem_device *nvmem, const char *cell_id) { struct nvmem_cell_entry *iter, *cell = NULL; - mutex_lock(&nvmem_mutex); + guard(mutex)(&nvmem_mutex); + list_for_each_entry(iter, &nvmem->cells, node) { if (strcmp(cell_id, iter->name) == 0) { cell = iter; break; } } - mutex_unlock(&nvmem_mutex); return cell; } @@ -1125,11 +1116,11 @@ static struct nvmem_device *__nvmem_device_get(void *data, struct nvmem_device *nvmem = NULL; struct device *dev; - mutex_lock(&nvmem_mutex); - dev = bus_find_device(&nvmem_bus_type, NULL, data, match); - if (dev) - nvmem = to_nvmem_device(dev); - mutex_unlock(&nvmem_mutex); + scoped_guard(mutex, &nvmem_mutex) { + dev = bus_find_device(&nvmem_bus_type, NULL, data, match); + if (dev) + nvmem = to_nvmem_device(dev); + } if (!nvmem) return ERR_PTR(-EPROBE_DEFER); @@ -1339,7 +1330,7 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id) dev_id = dev_name(dev); - mutex_lock(&nvmem_lookup_mutex); + guard(mutex)(&nvmem_lookup_mutex); list_for_each_entry(lookup, &nvmem_lookup_list, node) { if ((strcmp(lookup->dev_id, dev_id) == 0) && @@ -1347,11 +1338,9 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id) /* This is the right entry. */ nvmem = __nvmem_device_get((void *)lookup->nvmem_name, device_match_name); - if (IS_ERR(nvmem)) { + if (IS_ERR(nvmem)) /* Provider may not be registered yet. */ - cell = ERR_CAST(nvmem); - break; - } + return ERR_CAST(nvmem); cell_entry = nvmem_find_cell_entry_by_name(nvmem, lookup->cell_name); @@ -1367,7 +1356,6 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id) } } - mutex_unlock(&nvmem_lookup_mutex); return cell; } @@ -1381,18 +1369,16 @@ static void nvmem_layout_module_put(struct nvmem_device *nvmem) static struct nvmem_cell_entry * nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np) { - struct nvmem_cell_entry *iter, *cell = NULL; + struct nvmem_cell_entry *cell; - mutex_lock(&nvmem_mutex); - list_for_each_entry(iter, &nvmem->cells, node) { - if (np == iter->np) { - cell = iter; - break; - } + guard(mutex)(&nvmem_mutex); + + list_for_each_entry(cell, &nvmem->cells, node) { + if (np == cell->np) + return cell; } - mutex_unlock(&nvmem_mutex); - return cell; + return NULL; } static int nvmem_layout_module_get_optional(struct nvmem_device *nvmem) @@ -2125,10 +2111,10 @@ void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) { int i; - mutex_lock(&nvmem_lookup_mutex); + guard(mutex)(&nvmem_lookup_mutex); + for (i = 0; i < nentries; i++) list_add_tail(&entries[i].node, &nvmem_lookup_list); - mutex_unlock(&nvmem_lookup_mutex); } EXPORT_SYMBOL_GPL(nvmem_add_cell_lookups); @@ -2143,10 +2129,10 @@ void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) { int i; - mutex_lock(&nvmem_lookup_mutex); + guard(mutex)(&nvmem_lookup_mutex); + for (i = 0; i < nentries; i++) list_del(&entries[i].node); - mutex_unlock(&nvmem_lookup_mutex); } EXPORT_SYMBOL_GPL(nvmem_del_cell_lookups); From 1f4b4165741b88bcbf506b1d9ed05f38a36a3a9f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 29 Jul 2026 10:46:38 +0100 Subject: [PATCH 390/513] nvmem: remove unneeded __nvmem_device_put() __nvmem_device_put() is wrapped by nvmem_device_put() but there's no extra functionality offered by the latter so just fold one into the other. There's still the corresponding __nvmem_device_get() so in order to keep things symmetrical: rename it to nvmem_device_match() to better reflect its functionality and not confuse users by its presence in the absence of the similarly prefixed put() counterpart. Signed-off-by: Bartosz Golaszewski Tested-by: Loic Poulain Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094647.111468-6-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 2742de390de6..5f8bee5b9bff 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -1110,7 +1110,7 @@ struct nvmem_device *devm_nvmem_register(struct device *dev, } EXPORT_SYMBOL_GPL(devm_nvmem_register); -static struct nvmem_device *__nvmem_device_get(void *data, +static struct nvmem_device *nvmem_device_match(void *data, int (*match)(struct device *dev, const void *data)) { struct nvmem_device *nvmem = NULL; @@ -1138,13 +1138,6 @@ static struct nvmem_device *__nvmem_device_get(void *data, return nvmem; } -static void __nvmem_device_put(struct nvmem_device *nvmem) -{ - put_device(&nvmem->dev); - module_put(nvmem->owner); - kref_put(&nvmem->refcnt, nvmem_device_release); -} - #if IS_ENABLED(CONFIG_OF) /** * of_nvmem_device_get() - Get nvmem device from a given id @@ -1169,7 +1162,7 @@ struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id) if (!nvmem_np) return ERR_PTR(-ENOENT); - nvmem = __nvmem_device_get(nvmem_np, device_match_of_node); + nvmem = nvmem_device_match(nvmem_np, device_match_of_node); of_node_put(nvmem_np); return nvmem; } @@ -1197,7 +1190,7 @@ struct nvmem_device *nvmem_device_get(struct device *dev, const char *dev_name) } - return __nvmem_device_get((void *)dev_name, device_match_name); + return nvmem_device_match((void *)dev_name, device_match_name); } EXPORT_SYMBOL_GPL(nvmem_device_get); @@ -1213,7 +1206,7 @@ EXPORT_SYMBOL_GPL(nvmem_device_get); struct nvmem_device *nvmem_device_find(void *data, int (*match)(struct device *dev, const void *data)) { - return __nvmem_device_get(data, match); + return nvmem_device_match(data, match); } EXPORT_SYMBOL_GPL(nvmem_device_find); @@ -1257,7 +1250,9 @@ EXPORT_SYMBOL_GPL(devm_nvmem_device_put); */ void nvmem_device_put(struct nvmem_device *nvmem) { - __nvmem_device_put(nvmem); + put_device(&nvmem->dev); + module_put(nvmem->owner); + kref_put(&nvmem->refcnt, nvmem_device_release); } EXPORT_SYMBOL_GPL(nvmem_device_put); @@ -1336,7 +1331,7 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id) if ((strcmp(lookup->dev_id, dev_id) == 0) && (strcmp(lookup->con_id, con_id) == 0)) { /* This is the right entry. */ - nvmem = __nvmem_device_get((void *)lookup->nvmem_name, + nvmem = nvmem_device_match((void *)lookup->nvmem_name, device_match_name); if (IS_ERR(nvmem)) /* Provider may not be registered yet. */ @@ -1345,12 +1340,12 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id) cell_entry = nvmem_find_cell_entry_by_name(nvmem, lookup->cell_name); if (!cell_entry) { - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); cell = ERR_PTR(-ENOENT); } else { cell = nvmem_create_cell(cell_entry, con_id, 0); if (IS_ERR(cell)) - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); } break; } @@ -1448,7 +1443,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) } } - nvmem = __nvmem_device_get(nvmem_np, device_match_of_node); + nvmem = nvmem_device_match(nvmem_np, device_match_of_node); of_node_put(nvmem_np); if (IS_ERR(nvmem)) { of_node_put(cell_np); @@ -1458,7 +1453,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) ret = nvmem_layout_module_get_optional(nvmem); if (ret) { of_node_put(cell_np); - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); return ERR_PTR(ret); } @@ -1467,14 +1462,14 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) if (!cell_entry) { nvmem_layout_module_put(nvmem); ret = nvmem->layout ? -EPROBE_DEFER : -ENOENT; - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); return ERR_PTR(ret); } cell = nvmem_create_cell(cell_entry, id, cell_index); if (IS_ERR(cell)) { nvmem_layout_module_put(nvmem); - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); } return cell; @@ -1589,7 +1584,7 @@ void nvmem_cell_put(struct nvmem_cell *cell) kfree(cell); nvmem_layout_module_put(nvmem); - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); } EXPORT_SYMBOL_GPL(nvmem_cell_put); From 7f1dbd7a6e309dff4aed8d2b787c96c6901cfbe2 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 29 Jul 2026 10:46:39 +0100 Subject: [PATCH 391/513] nvmem: split out the reg_read/write() callbacks out of struct nvmem_device The reg_read/write() fields of struct nvmem_device point to memory owned by the nvmem provider. They must not be dereferenced after the provider is unregistered. Ahead of protecting against accesses to invalid memory with SRCU, move the callbacks into a separate structure the address of which is stored in nvmem_device. Signed-off-by: Bartosz Golaszewski Tested-by: Loic Poulain Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094647.111468-7-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 37 +++++++++++++++++++++++++++---------- drivers/nvmem/internals.h | 9 +++++++-- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 5f8bee5b9bff..bc5eb44431ca 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -57,25 +57,28 @@ static BLOCKING_NOTIFIER_HEAD(nvmem_notifier); static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset, void *val, size_t bytes) { - if (!nvmem->reg_read) + struct nvmem_operations *ops = nvmem->ops; + + if (!ops->reg_read) return -EOPNOTSUPP; - return nvmem->reg_read(nvmem->priv, offset, val, bytes); + return ops->reg_read(nvmem->priv, offset, val, bytes); } static int __nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset, void *val, size_t bytes) { + struct nvmem_operations *ops = nvmem->ops; int ret, wr_ok; - if (!nvmem->reg_write) + if (!ops->reg_write) return -EOPNOTSUPP; ret = gpiod_set_value_cansleep(nvmem->wp_gpio, 0); if (ret) return ret; - wr_ok = nvmem->reg_write(nvmem->priv, offset, val, bytes); + wr_ok = ops->reg_write(nvmem->priv, offset, val, bytes); ret = gpiod_set_value_cansleep(nvmem->wp_gpio, 1); if (ret) @@ -286,6 +289,8 @@ static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj, static umode_t nvmem_bin_attr_get_umode(struct nvmem_device *nvmem) { + struct nvmem_operations *ops = nvmem->ops; + umode_t mode = 0400; if (!nvmem->root_only) @@ -294,10 +299,10 @@ static umode_t nvmem_bin_attr_get_umode(struct nvmem_device *nvmem) if (!nvmem->read_only) mode |= 0200; - if (!nvmem->reg_write) + if (!ops->reg_write) mode &= ~0200; - if (!nvmem->reg_read) + if (!ops->reg_read) mode &= ~0444; return mode; @@ -328,6 +333,7 @@ static umode_t nvmem_attr_is_visible(struct kobject *kobj, { struct device *dev = kobj_to_dev(kobj); struct nvmem_device *nvmem = to_nvmem_device(dev); + struct nvmem_operations *ops = nvmem->ops; /* * If the device has no .reg_write operation, do not allow @@ -336,7 +342,7 @@ static umode_t nvmem_attr_is_visible(struct kobject *kobj, * can be forced into read-write mode using the 'force_ro' * attribute. */ - if (attr == &dev_attr_force_ro.attr && !nvmem->reg_write) + if (attr == &dev_attr_force_ro.attr && !ops->reg_write) return 0; /* Attribute not visible */ return attr->mode; @@ -537,6 +543,7 @@ static void nvmem_release(struct device *dev) ida_free(&nvmem_ida, nvmem->id); gpiod_put(nvmem->wp_gpio); + kfree(nvmem->ops); kfree(nvmem); } @@ -897,6 +904,7 @@ EXPORT_SYMBOL_GPL(nvmem_layout_unregister); struct nvmem_device *nvmem_register(const struct nvmem_config *config) { + struct nvmem_operations *ops; struct nvmem_device *nvmem; int rval; @@ -910,8 +918,15 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) if (!nvmem) return ERR_PTR(-ENOMEM); + ops = kzalloc_obj(*ops); + if (!ops) { + kfree(nvmem); + return ERR_PTR(-ENOMEM); + } + rval = ida_alloc(&nvmem_ida, GFP_KERNEL); if (rval < 0) { + kfree(ops); kfree(nvmem); return ERR_PTR(rval); } @@ -921,6 +936,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) nvmem->dev.type = &nvmem_provider_type; nvmem->dev.bus = &nvmem_bus_type; nvmem->dev.parent = config->dev; + nvmem->ops = ops; device_initialize(&nvmem->dev); @@ -937,6 +953,9 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) INIT_LIST_HEAD(&nvmem->cells); nvmem->fixup_dt_cell_info = config->fixup_dt_cell_info; + ops->reg_read = config->reg_read; + ops->reg_write = config->reg_write; + nvmem->owner = config->owner; if (!nvmem->owner && config->dev->driver) nvmem->owner = config->dev->driver->owner; @@ -946,8 +965,6 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) nvmem->root_only = config->root_only; nvmem->priv = config->priv; nvmem->type = config->type; - nvmem->reg_read = config->reg_read; - nvmem->reg_write = config->reg_write; nvmem->keepout = config->keepout; nvmem->nkeepout = config->nkeepout; if (config->of_node) @@ -973,7 +990,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) goto err_put_device; nvmem->read_only = device_property_present(config->dev, "read-only") || - config->read_only || !nvmem->reg_write; + config->read_only || !ops->reg_write; #ifdef CONFIG_NVMEM_SYSFS nvmem->dev.groups = nvmem_dev_groups; diff --git a/drivers/nvmem/internals.h b/drivers/nvmem/internals.h index 7cbc55f40259..070eef89ffca 100644 --- a/drivers/nvmem/internals.h +++ b/drivers/nvmem/internals.h @@ -7,6 +7,12 @@ #include #include +/* Hold pointers to callbacks owned by the nvmem provider module. */ +struct nvmem_operations { + nvmem_reg_read_t reg_read; + nvmem_reg_write_t reg_write; +}; + struct nvmem_device { struct module *owner; struct device dev; @@ -26,10 +32,9 @@ struct nvmem_device { struct nvmem_cell_info *cell); const struct nvmem_keepout *keepout; unsigned int nkeepout; - nvmem_reg_read_t reg_read; - nvmem_reg_write_t reg_write; struct gpio_desc *wp_gpio; struct nvmem_layout *layout; + struct nvmem_operations *ops; void *priv; bool sysfs_cells_populated; }; From b594cbb7e8cfc8e2faeebf6b7b11b4a6990fc732 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 29 Jul 2026 10:46:40 +0100 Subject: [PATCH 392/513] nvmem: simplify nvmem_sysfs_remove_compat() There's no need for the config argument in nvmem_sysfs_remove_compat(). Once the compat attribute is registered, that information is carried in nvmem_device::flags. Rework the code to always query that field and drop the second argument. Signed-off-by: Bartosz Golaszewski Tested-by: Loic Poulain Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094647.111468-8-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index bc5eb44431ca..ad2987a9d1a7 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -460,10 +460,9 @@ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, return 0; } -static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem, - const struct nvmem_config *config) +static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem) { - if (config->compat) + if (nvmem->flags & FLAG_COMPAT) device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); } @@ -530,8 +529,7 @@ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, { return -ENOSYS; } -static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem, - const struct nvmem_config *config) +static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem) { } @@ -1056,8 +1054,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) device_del(&nvmem->dev); err_remove_cells: nvmem_device_remove_all_cells(nvmem); - if (config->compat) - nvmem_sysfs_remove_compat(nvmem, config); + nvmem_sysfs_remove_compat(nvmem); err_put_device: put_device(&nvmem->dev); @@ -1073,8 +1070,7 @@ static void nvmem_device_release(struct kref *kref) blocking_notifier_call_chain(&nvmem_notifier, NVMEM_REMOVE, nvmem); - if (nvmem->flags & FLAG_COMPAT) - device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); + nvmem_sysfs_remove_compat(nvmem); nvmem_device_remove_all_cells(nvmem); nvmem_destroy_layout(nvmem); From cf09b7a05618bea99468b67fe80202efba1b48ad Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 29 Jul 2026 10:46:43 +0100 Subject: [PATCH 393/513] eeprom: move nvmem EEPROM drivers to drivers/nvmem/ For historical reasons EEPROM drivers have lived under drivers/misc/eeprom/. Also for historical reasons changes to most of them would go through the char-misc tree while some would be queued through the I2C tree. Over the years some of them have also been converted to using nvmem - the dedicated subsystem for non-volatile memory - while get_maintainer.pl does not Cc the maintainer of nvmem on patches changing them. Move the EEPROM drivers using nvmem under drivers/nvmem/ for consistency of the review process and path upstream. Reviewed-by: Wolfram Sang Acked-by: Greg Kroah-Hartman Acked-by: Arnd Bergmann Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094647.111468-11-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 4 +- drivers/misc/eeprom/Kconfig | 93 ------------------- drivers/misc/eeprom/Makefile | 5 - drivers/nvmem/Kconfig | 87 +++++++++++++++++ drivers/nvmem/Makefile | 5 + drivers/{misc/eeprom => nvmem}/at24.c | 0 drivers/{misc/eeprom => nvmem}/at25.c | 0 drivers/{misc/eeprom => nvmem}/ee1004.c | 0 .../{misc/eeprom => nvmem}/eeprom_93xx46.c | 0 drivers/{misc/eeprom => nvmem}/m24lr.c | 0 10 files changed, 94 insertions(+), 100 deletions(-) rename drivers/{misc/eeprom => nvmem}/at24.c (100%) rename drivers/{misc/eeprom => nvmem}/at25.c (100%) rename drivers/{misc/eeprom => nvmem}/ee1004.c (100%) rename drivers/{misc/eeprom => nvmem}/eeprom_93xx46.c (100%) rename drivers/{misc/eeprom => nvmem}/m24lr.c (100%) diff --git a/MAINTAINERS b/MAINTAINERS index e3ebcfdeba39..a15a5b6fa91f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4176,11 +4176,11 @@ F: include/linux/async_tx.h AT24 EEPROM DRIVER M: Bartosz Golaszewski -L: linux-i2c@vger.kernel.org +L: linux-kernel@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git F: Documentation/devicetree/bindings/eeprom/at24.yaml -F: drivers/misc/eeprom/at24.c +F: drivers/nvmem/at24.c ATA OVER ETHERNET (AOE) DRIVER M: "Justin Sanders" diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 4d0ce47aa282..d6af02588c66 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -1,52 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menu "EEPROM support" -config EEPROM_AT24 - tristate "I2C EEPROMs / RAMs / ROMs from most vendors" - depends on I2C && SYSFS - select NVMEM - select NVMEM_SYSFS - select REGMAP - select REGMAP_I2C - help - Enable this driver to get read/write support to most I2C EEPROMs - and compatible devices like FRAMs, SRAMs, ROMs etc. After you - configure the driver to know about each chip on your target - board. Use these generic chip names, instead of vendor-specific - ones like at24c64, 24lc02 or fm24c04: - - 24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08, - 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024, 24c2048 - - Unless you like data loss puzzles, always be sure that any chip - you configure as a 24c32 (32 kbit) or larger is NOT really a - 24c16 (16 kbit) or smaller, and vice versa. Marking the chip - as read-only won't help recover from this. Also, if your chip - has any software write-protect mechanism you may want to review the - code to make sure this driver won't turn it on by accident. - - If you use this with an SMBus adapter instead of an I2C adapter, - full functionality is not available. Only smaller devices are - supported (24c16 and below, max 4 kByte). - - This driver can also be built as a module. If so, the module - will be called at24. - -config EEPROM_AT25 - tristate "SPI EEPROMs (FRAMs) from most vendors" - depends on SPI && SYSFS - select NVMEM - select NVMEM_SYSFS - select SPI_MEM - help - Enable this driver to get read/write support to most SPI EEPROMs - and Cypress FRAMs, - after you configure the board init code to know about each eeprom - on your target board. - - This driver can also be built as a module. If so, the module - will be called at25. - config EEPROM_MAX6875 tristate "Maxim MAX6874/5 power supply supervisor" depends on I2C @@ -69,22 +23,6 @@ config EEPROM_93CX6 If unsure, say N. -config EEPROM_93XX46 - tristate "Microwire EEPROM 93XX46 support" - depends on SPI && SYSFS - select REGMAP - select NVMEM - select NVMEM_SYSFS - help - Driver for the microwire EEPROM chipsets 93xx46x. The driver - supports both read and write commands and also the command to - erase the whole EEPROM. - - This driver can also be built as a module. If so, the module - will be called eeprom_93xx46. - - If unsure, say N. - config EEPROM_DIGSY_MTC_CFG bool "DigsyMTC display configuration EEPROMs device" depends on GPIO_MPC5200 && SPI_GPIO @@ -107,35 +45,4 @@ config EEPROM_IDT_89HPESX This driver can also be built as a module. If so, the module will be called idt_89hpesx. -config EEPROM_EE1004 - tristate "SPD EEPROMs on DDR4 memory modules" - depends on I2C && SYSFS - select NVMEM - select NVMEM_SYSFS - help - Enable this driver to get read support to SPD EEPROMs following - the JEDEC EE1004 standard. These are typically found on DDR4 - SDRAM memory modules. - - This driver can also be built as a module. If so, the module - will be called ee1004. - -config EEPROM_M24LR - tristate "STMicroelectronics M24LR RFID/NFC EEPROM support" - depends on I2C && SYSFS - select REGMAP_I2C - select NVMEM - select NVMEM_SYSFS - help - This enables support for STMicroelectronics M24LR RFID/NFC EEPROM - chips. These dual-interface devices expose two I2C addresses: - one for EEPROM memory access and another for control and system - configuration (e.g. UID, password handling). - - This driver provides a sysfs interface for control functions and - integrates with the nvmem subsystem for EEPROM access. - - To compile this driver as a module, choose M here: the - module will be called m24lr. - endmenu diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile index 8f311fd6a4ce..44a20cf025a1 100644 --- a/drivers/misc/eeprom/Makefile +++ b/drivers/misc/eeprom/Makefile @@ -1,10 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_EEPROM_AT24) += at24.o -obj-$(CONFIG_EEPROM_AT25) += at25.o obj-$(CONFIG_EEPROM_MAX6875) += max6875.o obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o -obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o obj-$(CONFIG_EEPROM_IDT_89HPESX) += idt_89hpesx.o -obj-$(CONFIG_EEPROM_EE1004) += ee1004.o -obj-$(CONFIG_EEPROM_M24LR) += m24lr.o diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index e10f7ff725ff..b0044c4c63c6 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -28,6 +28,93 @@ source "drivers/nvmem/layouts/Kconfig" # Devices +if NVMEM_SYSFS + +config EEPROM_AT24 + tristate "I2C EEPROMs / RAMs / ROMs from most vendors" + depends on I2C && SYSFS + select REGMAP + select REGMAP_I2C + help + Enable this driver to get read/write support to most I2C EEPROMs + and compatible devices like FRAMs, SRAMs, ROMs etc. After you + configure the driver to know about each chip on your target + board. Use these generic chip names, instead of vendor-specific + ones like at24c64, 24lc02 or fm24c04: + + 24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08, + 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024, 24c2048 + + Unless you like data loss puzzles, always be sure that any chip + you configure as a 24c32 (32 kbit) or larger is NOT really a + 24c16 (16 kbit) or smaller, and vice versa. Marking the chip + as read-only won't help recover from this. Also, if your chip + has any software write-protect mechanism you may want to review the + code to make sure this driver won't turn it on by accident. + + If you use this with an SMBus adapter instead of an I2C adapter, + full functionality is not available. Only smaller devices are + supported (24c16 and below, max 4 kByte). + + This driver can also be built as a module. If so, the module + will be called at24. + +config EEPROM_AT25 + tristate "SPI EEPROMs (FRAMs) from most vendors" + depends on SPI && SYSFS + select SPI_MEM + help + Enable this driver to get read/write support to most SPI EEPROMs + and Cypress FRAMs, + after you configure the board init code to know about each eeprom + on your target board. + + This driver can also be built as a module. If so, the module + will be called at25. + +config EEPROM_93XX46 + tristate "Microwire EEPROM 93XX46 support" + depends on SPI && SYSFS + select REGMAP + help + Driver for the microwire EEPROM chipsets 93xx46x. The driver + supports both read and write commands and also the command to + erase the whole EEPROM. + + This driver can also be built as a module. If so, the module + will be called eeprom_93xx46. + + If unsure, say N. + +config EEPROM_EE1004 + tristate "SPD EEPROMs on DDR4 memory modules" + depends on I2C && SYSFS + help + Enable this driver to get read support to SPD EEPROMs following + the JEDEC EE1004 standard. These are typically found on DDR4 + SDRAM memory modules. + + This driver can also be built as a module. If so, the module + will be called ee1004. + +config EEPROM_M24LR + tristate "STMicroelectronics M24LR RFID/NFC EEPROM support" + depends on I2C && SYSFS + select REGMAP_I2C + help + This enables support for STMicroelectronics M24LR RFID/NFC EEPROM + chips. These dual-interface devices expose two I2C addresses: + one for EEPROM memory access and another for control and system + configuration (e.g. UID, password handling). + + This driver provides a sysfs interface for control functions and + integrates with the nvmem subsystem for EEPROM access. + + To compile this driver as a module, choose M here: the + module will be called m24lr. + +endif # NVMEM_SYSFS + config NVMEM_AIROHA_SMC_EFUSES tristate "Airoha SMC eFuse support" depends on ARCH_AIROHA || COMPILE_TEST diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index f6f2bc51dee1..2bbfb9ff1885 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -10,6 +10,11 @@ nvmem_layouts-y := layouts.o obj-y += layouts/ # Devices +obj-$(CONFIG_EEPROM_AT24) += at24.o +obj-$(CONFIG_EEPROM_AT25) += at25.o +obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o +obj-$(CONFIG_EEPROM_EE1004) += ee1004.o +obj-$(CONFIG_EEPROM_M24LR) += m24lr.o obj-$(CONFIG_NVMEM_AIROHA_SMC_EFUSES) += nvmem-airoha-smc-efuses.o nvmem-airoha-smc-efuses-y := airoha-smc-efuses.o obj-$(CONFIG_NVMEM_AN8855_EFUSE) += nvmem-an8855-efuse.o diff --git a/drivers/misc/eeprom/at24.c b/drivers/nvmem/at24.c similarity index 100% rename from drivers/misc/eeprom/at24.c rename to drivers/nvmem/at24.c diff --git a/drivers/misc/eeprom/at25.c b/drivers/nvmem/at25.c similarity index 100% rename from drivers/misc/eeprom/at25.c rename to drivers/nvmem/at25.c diff --git a/drivers/misc/eeprom/ee1004.c b/drivers/nvmem/ee1004.c similarity index 100% rename from drivers/misc/eeprom/ee1004.c rename to drivers/nvmem/ee1004.c diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/nvmem/eeprom_93xx46.c similarity index 100% rename from drivers/misc/eeprom/eeprom_93xx46.c rename to drivers/nvmem/eeprom_93xx46.c diff --git a/drivers/misc/eeprom/m24lr.c b/drivers/nvmem/m24lr.c similarity index 100% rename from drivers/misc/eeprom/m24lr.c rename to drivers/nvmem/m24lr.c From 1ae37d355114a94eb88c418e22e3e9a9e241b393 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 29 Jul 2026 10:46:44 +0100 Subject: [PATCH 394/513] nvmem: airoha: add ARM64 dependency The driver already depends on HAVE_ARM_SMCCC and ARCH_AIROHA, but both are available for 32-bit and 64-bit targets. However, the smccc invocation fails on thumb2 builds with clang: drivers/nvmem/airoha-smc-efuses.c:38:2: error: write to reserved register 'R7' 38 | arm_smccc_1_1_invoke(AIROHA_SMC_EFUSE_FID, | ^ note: (skipping 1 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all) arch/arm/include/asm/opcodes.h:215:2: note: expanded from macro '__inst_arm_thumb32' 215 | __inst_thumb32(thumb_opcode) | ^ Since the driver is only used on the 64-bit an7581 soc, avoid this problem with a stricter dependency. Fixes: b7846af2e6ca ("nvmem: airoha: Add support for SMC eFUSE") Signed-off-by: Arnd Bergmann Reviewed-by: Christian Marangi Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094647.111468-12-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index b0044c4c63c6..c36c2a4c2a0b 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -117,6 +117,7 @@ endif # NVMEM_SYSFS config NVMEM_AIROHA_SMC_EFUSES tristate "Airoha SMC eFuse support" + depends on ARM64 depends on ARCH_AIROHA || COMPILE_TEST depends on HAVE_ARM_SMCCC default ARCH_AIROHA From fde46579cf8872138b296349c2bd8a3cd3f132e2 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 29 Jul 2026 10:46:45 +0100 Subject: [PATCH 395/513] nvmem: brcm_nvram: reject empty NVRAM partition If the partition is completely erased (all padding bytes), the trimming loop reduces data_len to 0. devm_kzalloc(dev, 0, GFP_KERNEL) returns ZERO_SIZE_PTR ((void *)16), which is non-NULL and bypasses the NULL check. The subsequent cast of priv->data to struct brcm_nvram_header * and dereference of header->magic causes a page fault on address 0x10. Reject data_len smaller than the header before allocating. Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094647.111468-13-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/brcm_nvram.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/nvmem/brcm_nvram.c b/drivers/nvmem/brcm_nvram.c index aaa6537798bf..c3b4282aa164 100644 --- a/drivers/nvmem/brcm_nvram.c +++ b/drivers/nvmem/brcm_nvram.c @@ -84,6 +84,11 @@ static int brcm_nvram_copy_data(struct brcm_nvram *priv, struct platform_device } WARN(priv->data_len > SZ_128K, "Unexpected (big) NVRAM size: %zu B\n", priv->data_len); + if (priv->data_len < sizeof(struct brcm_nvram_header)) { + dev_err(priv->dev, "NVRAM data too small (%zu)\n", priv->data_len); + return -EINVAL; + } + priv->data = devm_kzalloc(priv->dev, priv->data_len, GFP_KERNEL); if (!priv->data) return -ENOMEM; From a67e2c323a47a2254ed3472b5e4c53abac20f116 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 29 Jul 2026 10:46:46 +0100 Subject: [PATCH 396/513] nvmem: brcm_nvram: fix out-of-bounds access on malformed flash data The length check in brcm_nvram_parse() validated header->len against priv->nvmem_size (the full partition size) instead of priv->data_len (the actual allocated data buffer). A malformed flash partition with header->len between the two would pass the check, causing brcm_nvram_add_cells() to read and write priv->data[len - 1] beyond the heap allocation. Also add a minimum bound: len < sizeof(*header) could underflow the data[len - 1] access. Fix both bounds by rejecting len outside [sizeof(*header), priv->data_len]. Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094647.111468-14-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/brcm_nvram.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/nvmem/brcm_nvram.c b/drivers/nvmem/brcm_nvram.c index c3b4282aa164..9f77aee37121 100644 --- a/drivers/nvmem/brcm_nvram.c +++ b/drivers/nvmem/brcm_nvram.c @@ -192,9 +192,13 @@ static int brcm_nvram_parse(struct brcm_nvram *priv) } len = le32_to_cpu(header->len); - if (len > priv->nvmem_size) { - dev_err(dev, "NVRAM length (%zd) exceeds mapped size (%zd)\n", len, - priv->nvmem_size); + if (len < sizeof(*header)) { + dev_err(dev, "NVRAM length (%zd) too small\n", len); + return -EINVAL; + } + if (len > priv->data_len) { + dev_err(dev, "NVRAM length (%zd) exceeds data size (%zd)\n", len, + priv->data_len); return -EINVAL; } From e38b0c8141cd9bf28a17259d69b5f3c96c4e6b3e Mon Sep 17 00:00:00 2001 From: Varadarajan Narayanan Date: Wed, 29 Jul 2026 10:46:47 +0100 Subject: [PATCH 397/513] dt-bindings: nvmem: qfprom: Add ipq5210 & ipq9650 compatible Document compatible string for the QFPROM on ipq5210 & ipq9650 platforms. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Varadarajan Narayanan Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260729094647.111468-15-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml index 8134ddb54e13..bc0a0898b9e4 100644 --- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml +++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml @@ -22,12 +22,14 @@ properties: - qcom,glymur-qfprom - qcom,hawi-qfprom - qcom,ipq5018-qfprom + - qcom,ipq5210-qfprom - qcom,ipq5332-qfprom - qcom,ipq5424-qfprom - qcom,ipq6018-qfprom - qcom,ipq8064-qfprom - qcom,ipq8074-qfprom - qcom,ipq9574-qfprom + - qcom,ipq9650-qfprom - qcom,kaanapali-qfprom - qcom,milos-qfprom - qcom,msm8226-qfprom From 19183c0ef0d73ff3ca7caf8a205edcea922f8c24 Mon Sep 17 00:00:00 2001 From: Georgios Androutsopoulos Date: Tue, 16 Jun 2026 13:09:56 -0400 Subject: [PATCH 398/513] rust_binder: add ownership assertion to Node::add_death The `// SAFETY:` comment in NodeDeath::set_cleared assumes that a NodeDeath is never inserted into the death list of any Node other than its owner. However, this invariant is not enforced by the safe function Node::add_death, which inserts NodeDeath into the death list without checking that death.node == self, leaving a risk for future code that may miss this implicit invariant and cause undefined behavior. Add an assertion to make this precondition explicit and catch potential violations early. Link: https://github.com/Rust-for-Linux/linux/issues/1237 Signed-off-by: Georgios Androutsopoulos Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260616170956.2580772-1-georgeandrout13@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/node.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs index b74ef32b0d94..0a82af14cda3 100644 --- a/drivers/android/binder/node.rs +++ b/drivers/android/binder/node.rs @@ -335,6 +335,10 @@ pub(crate) fn add_death( death: ListArc, 1>, guard: &mut Guard<'_, ProcessInner, SpinLockBackend>, ) { + assert!( + core::ptr::eq(self, &**death.node), + "attempt to add NodeDeath to the wrong death list" + ); self.inner.access_mut(guard).death_list.push_back(death); } From d6e8b74fe7689f8ac1ee41f001e75715692a7cef Mon Sep 17 00:00:00 2001 From: Jahnavi MN Date: Sun, 19 Jul 2026 11:51:09 +0000 Subject: [PATCH 399/513] rust_binder: Update looper_flags bitmaps to use kernel::impl_flags! Thread looper states are currently represented as raw integers and manipulated via bitwise operations. This lacks type safety, making it possible to mix up different flag types without compile-time warnings. Use kernel::impl_flags! to migrate looper_flags to a strongly-typed bitmask, enforcing compile-time safety. Key changes: - Define `LooperFlags(u32)` and `LooperFlag` enum with 7 variants. - Change `InnerThread.looper_flags` type to `LooperFlags`. - Update looper state transitions and checks to use type-safe methods. - Convert `looper_flags` to `u32` for hex formatting in `debug_print`. Reviewed-by: Alice Ryhl Signed-off-by: Jahnavi MN Link: https://patch.msgid.link/20260719-b4-rust_binder_impl_flags-v3-1-f8d0b3ea1b87@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/thread.rs | 62 +++++++++++++++++++------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 9f3b0c9255b3..e2479486113a 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -9,6 +9,7 @@ use kernel::{ bindings, + bits::bit_u32, fs::LocalFile, list::{AtomicTracker, List, ListArc, ListLinks, TryNewListArc}, prelude::*, @@ -245,7 +246,7 @@ fn is_ok(&self) -> bool { struct InnerThread { /// Determines the looper state of the thread. It is a bit-wise combination of the constants /// prefixed with `LOOPER_`. - looper_flags: u32, + looper_flags: LooperFlags, /// Determines whether the looper should return. looper_need_return: bool, @@ -272,13 +273,23 @@ struct InnerThread { extended_error: ExtendedError, } -const LOOPER_REGISTERED: u32 = 0x01; -const LOOPER_ENTERED: u32 = 0x02; -const LOOPER_EXITED: u32 = 0x04; -const LOOPER_INVALID: u32 = 0x08; -const LOOPER_WAITING: u32 = 0x10; -const LOOPER_WAITING_PROC: u32 = 0x20; -const LOOPER_POLL: u32 = 0x40; +kernel::impl_flags!( + /// Represents multiple looper flags. + #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] + pub struct LooperFlags(u32); + + /// Represents a single looper flag. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum LooperFlag { + Registered = bit_u32(0), + Entered = bit_u32(1), + Exited = bit_u32(2), + Invalid = bit_u32(3), + Waiting = bit_u32(4), + WaitingProc = bit_u32(5), + Poll = bit_u32(6), + } +); impl InnerThread { fn new(pid: i32) -> Result { @@ -288,7 +299,7 @@ fn next_err_id() -> u32 { } Ok(Self { - looper_flags: 0, + looper_flags: LooperFlags::default(), looper_need_return: false, is_dead: false, process_work_list: false, @@ -316,7 +327,7 @@ fn push_work(&mut self, work: DLArc) -> PushWorkRes { } self.work_list.push_back(work); self.process_work_list = true; - if self.looper_flags & LOOPER_POLL != 0 { + if self.looper_flags.contains(LooperFlag::Poll) { PushWorkRes::OkNotifyPoll } else { PushWorkRes::Ok @@ -380,26 +391,27 @@ fn pop_transaction_replied(&mut self, transaction: &DArc) -> bool { } fn looper_enter(&mut self) { - self.looper_flags |= LOOPER_ENTERED; - if self.looper_flags & LOOPER_REGISTERED != 0 { - self.looper_flags |= LOOPER_INVALID; + self.looper_flags |= LooperFlag::Entered; + if self.looper_flags.contains(LooperFlag::Registered) { + self.looper_flags |= LooperFlag::Invalid; } } fn looper_register(&mut self, valid: bool) { - self.looper_flags |= LOOPER_REGISTERED; - if !valid || self.looper_flags & LOOPER_ENTERED != 0 { - self.looper_flags |= LOOPER_INVALID; + self.looper_flags |= LooperFlag::Registered; + if !valid || self.looper_flags.contains(LooperFlag::Entered) { + self.looper_flags |= LooperFlag::Invalid; } } fn looper_exit(&mut self) { - self.looper_flags |= LOOPER_EXITED; + self.looper_flags |= LooperFlag::Exited; } /// Determines whether the thread is part of a pool, i.e., if it is a looper. fn is_looper(&self) -> bool { - self.looper_flags & (LOOPER_ENTERED | LOOPER_REGISTERED) != 0 + self.looper_flags + .contains_any(LooperFlag::Entered | LooperFlag::Registered) } /// Determines whether the thread should attempt to fetch work items from the process queue. @@ -411,7 +423,7 @@ fn should_use_process_work_queue(&self) -> bool { } fn poll(&mut self) -> u32 { - self.looper_flags |= LOOPER_POLL; + self.looper_flags |= LooperFlag::Poll; if self.process_work_list || self.looper_need_return { bindings::POLLIN } else { @@ -477,7 +489,7 @@ pub(crate) fn debug_print(self: &Arc, m: &SeqFile, print_all: bool) -> Res m, " thread {}: l {:02x} need_return {}\n", self.id, - inner.looper_flags, + u32::from(inner.looper_flags), inner.looper_need_return, ); } @@ -550,9 +562,9 @@ fn get_work_local(self: &Arc, wait: bool) -> Result, wait: bool) -> Result Result<(bool, u32)> { /// Make the call to `get_work` or `get_work_local` return immediately, if any. pub(crate) fn exit_looper(&self) { let mut inner = self.inner.lock(); - let should_notify = inner.looper_flags & LOOPER_WAITING != 0; + let should_notify = inner.looper_flags.contains(LooperFlag::Waiting); if should_notify { inner.looper_need_return = true; } From 0649be3cc4fd09025ad2429919cbda8711a467de Mon Sep 17 00:00:00 2001 From: Jahnavi MN Date: Sun, 19 Jul 2026 11:51:10 +0000 Subject: [PATCH 400/513] rust_binder: Update transaction flags to use kernel::impl_flags! Transaction configuration flags are currently represented as raw integers and manipulated via bitwise operations. This lacks type safety, making it possible to mix up different flag types without compile-time warnings. Use kernel::impl_flags! to migrate the transaction flags to a strongly-typed bitmask, enforcing compile-time safety. Key changes: - Define `TransactionFlags(u32)` and `TransactionFlag` with 4 variants. - Change flags field type to `TransactionFlags` in structs. - Add `is_oneway` helper on `TransactionFlags` to simplify checks. - Update `can_replace` logic to use type-safe combined flag checks. - Convert `flags` to `u32` for FFI boundaries and logging. Reviewed-by: Alice Ryhl Signed-off-by: Jahnavi MN Link: https://patch.msgid.link/20260719-b4-rust_binder_impl_flags-v3-2-f8d0b3ea1b87@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/thread.rs | 6 +-- drivers/android/binder/transaction.rs | 58 ++++++++++++++++++++------- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index e2479486113a..b41a62b864da 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -30,7 +30,7 @@ process::{GetWorkOrRegister, Process}, ptr_align, stats::GLOBAL_STATS, - transaction::{Transaction, TransactionInfo}, + transaction::{Transaction, TransactionFlag, TransactionFlags, TransactionInfo}, BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead, }; @@ -1266,7 +1266,7 @@ fn read_transaction_info( 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.flags = TransactionFlags::from_bits(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); @@ -1408,7 +1408,7 @@ fn reply_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { self.process.task.pid(), ))?; let process = orig.from.process.clone(); - let allow_fds = orig.flags & TF_ACCEPT_FDS != 0; + let allow_fds = orig.flags.contains(TransactionFlag::AcceptFds); let reply = Transaction::new_reply(self, process, info, allow_fds)?; // Not notifying: Reply to current thread. let _ = self.inner.lock().push_work(completion); diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 13dfb5c5c955..245f1556b5db 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -27,6 +27,33 @@ BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead, }; +kernel::impl_flags!( + /// Represents multiple transaction flags. + #[derive(Debug, Clone, Default, Copy, PartialEq, Eq, Zeroable)] + pub struct TransactionFlags(u32); + + /// Represents a single transaction flag. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum TransactionFlag { + OneWay = TF_ONE_WAY, + AcceptFds = TF_ACCEPT_FDS, + ClearBuf = TF_CLEAR_BUF, + UpdateTxn = TF_UPDATE_TXN, + } +); + +impl TransactionFlags { + /// Creates a `TransactionFlags` from a raw `u32` value. + pub(crate) fn from_bits(bits: u32) -> Self { + Self(bits) + } + + /// Checks if the Oneway flag is set. + pub(crate) fn is_oneway(self) -> bool { + self.contains(TransactionFlag::OneWay) + } +} + #[derive(Zeroable)] pub(crate) struct TransactionInfo { pub(crate) from_pid: Pid, @@ -34,7 +61,7 @@ pub(crate) struct TransactionInfo { pub(crate) to_pid: Pid, pub(crate) to_tid: Pid, pub(crate) code: u32, - pub(crate) flags: u32, + pub(crate) flags: TransactionFlags, pub(crate) data_ptr: UserPtr, pub(crate) data_size: usize, pub(crate) offsets_ptr: UserPtr, @@ -51,7 +78,7 @@ pub(crate) struct TransactionInfo { impl TransactionInfo { #[inline] pub(crate) fn is_oneway(&self) -> bool { - self.flags & TF_ONE_WAY != 0 + self.flags.is_oneway() } pub(crate) fn report_netlink(&self, reply: u32, ctx: &crate::Context) { @@ -84,7 +111,7 @@ fn report_netlink_inner(&self, reply: u32, ctx: &crate::Context) -> kernel::erro if self.is_reply { report.is_reply()?; } - report.flags(self.flags)?; + report.flags(u32::from(self.flags))?; report.code(self.code)?; report.data_size(self.data_size as u32)?; @@ -115,7 +142,7 @@ pub(crate) struct Transaction { allocation: SpinLock>, is_outstanding: Atomic, code: u32, - pub(crate) flags: u32, + pub(crate) flags: TransactionFlags, data_size: usize, offsets_size: usize, data_address: usize, @@ -161,7 +188,7 @@ pub(crate) fn new( } alloc.set_info_oneway_node(node_ref.node.clone()); } - if info.flags & TF_CLEAR_BUF != 0 { + if info.flags.contains(TransactionFlag::ClearBuf) { alloc.set_info_clear_on_drop(); } let target_node = node_ref.node.clone(); @@ -201,7 +228,7 @@ pub(crate) fn new_reply( return Err(err); } }; - if info.flags & TF_CLEAR_BUF != 0 { + if info.flags.contains(TransactionFlag::ClearBuf) { alloc.set_info_clear_on_drop(); } Ok(DTRWrap::arc_pin_init(pin_init!(Transaction { @@ -234,7 +261,7 @@ pub(crate) fn debug_print_inner(&self, m: &SeqFile, prefix: &str) { self.from.id, self.to.task.pid(), self.code, - self.flags, + u32::from(self.flags), self.start_time.elapsed().as_millis(), ); if let Some(target_node) = &self.target_node { @@ -313,7 +340,7 @@ pub(crate) fn submit(self: DLArc, info: &mut TransactionInfo) -> BinderRes let _t_outdated; let _oneway_node; - let oneway = self.flags & TF_ONE_WAY != 0; + let oneway = self.flags.is_oneway(); let process = self.to.clone(); let mut process_inner = process.inner.lock(); @@ -324,7 +351,7 @@ pub(crate) fn submit(self: DLArc, info: &mut TransactionInfo) -> BinderRes crate::trace::trace_transaction(false, &self, None); if process_inner.is_frozen.is_frozen() { process_inner.async_recv = true; - if self.flags & TF_UPDATE_TXN != 0 { + if self.flags.contains(TransactionFlag::UpdateTxn) { if let Some(t_outdated) = target_node.take_outdated_transaction(&self, &mut process_inner) { @@ -399,7 +426,8 @@ pub(crate) fn can_replace(&self, old: &Transaction) -> bool { return false; } - if self.flags & old.flags & (TF_ONE_WAY | TF_UPDATE_TXN) != (TF_ONE_WAY | TF_UPDATE_TXN) { + let required = TransactionFlag::OneWay | TransactionFlag::UpdateTxn; + if !(self.flags.contains_all(required) && old.flags.contains_all(required)) { return false; } @@ -436,7 +464,7 @@ fn do_work( writer: &mut BinderReturnWriter<'_>, ) -> Result { let send_failed_reply = ScopeGuard::new(|| { - if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 { + if self.target_node.is_some() && !self.flags.is_oneway() { let reply = Err(BR_FAILED_REPLY); self.from.deliver_reply(reply, &self, None); } @@ -467,7 +495,7 @@ fn do_work( tr.cookie = cookie as uapi::binder_uintptr_t; }; tr.code = self.code; - tr.flags = self.flags; + tr.flags = u32::from(self.flags); tr.data_size = self.data_size as uapi::binder_size_t; tr.data.ptr.buffer = self.data_address as uapi::binder_uintptr_t; tr.offsets_size = self.offsets_size as uapi::binder_size_t; @@ -477,7 +505,7 @@ fn do_work( } tr.sender_euid = self.sender_euid.into_uid_in_current_ns(); tr.sender_pid = 0; - if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 { + if self.target_node.is_some() && !self.flags.is_oneway() { // Not a reply and not one-way. tr.sender_pid = self.from.process.pid_in_current_ns(); } @@ -529,7 +557,7 @@ fn cancel(self: DArc) { drop(allocation); // If this is not a reply or oneway transaction, then send a dead reply. - if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 { + if self.target_node.is_some() && !self.flags.is_oneway() { let reply = Err(BR_DEAD_REPLY); self.from.deliver_reply(reply, &self, None); } else { @@ -545,7 +573,7 @@ fn cancel(self: DArc) { } fn should_sync_wakeup(&self) -> bool { - self.flags & TF_ONE_WAY == 0 + !self.flags.is_oneway() } fn debug_print(&self, m: &SeqFile, _prefix: &str, tprefix: &str) -> Result<()> { From d9e81c7199baa728a84e8881317b3709f0ff4d5a Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 28 Jul 2026 06:12:35 +0000 Subject: [PATCH 401/513] rust_binder: update indentation of failed transaction print To properly take the changes from commit bb66b1a34525 ("rust_binder: only print failure if error has source") into account, the binder_debug! statement was moved inside the if {} block, and so there must be one more level of indentation. Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260728061236.198267-1-aliceryhl@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/thread.rs | 34 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index b41a62b864da..18a14aa8a835 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -1308,23 +1308,23 @@ fn transaction(self: &Arc, cmd: u32, reader: &mut UserSliceReader) -> Resu ExtendedError::new(info.debug_id as u32, err.reply, source.to_errno()); } - binder_debug!( - FailedTransaction, - "transaction {} to {}:{} failed {:?}, code {} size {}-{}", - if info.is_reply { - "reply" - } else if info.is_oneway() { - "async" - } else { - "call" - }, - info.to_pid, - info.to_tid, - err, - info.code, - info.data_size, - info.offsets_size - ); + binder_debug!( + FailedTransaction, + "transaction {} to {}:{} failed {:?}, code {} size {}-{}", + if info.is_reply { + "reply" + } else if info.is_oneway() { + "async" + } else { + "call" + }, + info.to_pid, + info.to_tid, + err, + info.code, + info.data_size, + info.offsets_size + ); } } } From 210854a96ef18b09b45a2a59ff14ca06dfe5ad4d Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Wed, 22 Jul 2026 15:42:13 +0530 Subject: [PATCH 402/513] misc: vmc_vmci: Fix potential memory leak in vmci_event_subscribe() The memory allocated for struct vmci_subscription (sub) is not freed in the error path when have_new_id is false. Fix that by adding a kfree() call, and moving the read of sub->id to a point before freeing. Fixes: 1d990201f9bb ("VMCI: event handling implementation.") Signed-off-by: Abdun Nihaal Acked-by: Vishnu Dasa Link: https://patch.msgid.link/20260722101215.76680-1-nihaal@cse.iitm.ac.in Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_vmci/vmci_event.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/vmw_vmci/vmci_event.c b/drivers/misc/vmw_vmci/vmci_event.c index fffe068a26eb..c3ef3b98c432 100644 --- a/drivers/misc/vmw_vmci/vmci_event.c +++ b/drivers/misc/vmw_vmci/vmci_event.c @@ -179,16 +179,16 @@ int vmci_event_subscribe(u32 event, } } + *new_subscription_id = sub->id; if (have_new_id) { list_add_rcu(&sub->node, &subscriber_array[event]); retval = VMCI_SUCCESS; } else { + kfree(sub); retval = VMCI_ERROR_NO_RESOURCES; } mutex_unlock(&subscriber_mutex); - - *new_subscription_id = sub->id; return retval; } EXPORT_SYMBOL_GPL(vmci_event_subscribe); From 16dc8c8683342cd170c741c6bf80c4f8d249acb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ezequiel=20Rodriguez?= Date: Thu, 23 Jul 2026 15:12:57 -0500 Subject: [PATCH 403/513] selftests: ntsync: fix wake_all CREATE_EVENT fd expectation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wake_all used EXPECT_EQ(0, objs[3]) after NTSYNC_IOC_CREATE_EVENT. The ioctl returns a non-negative file descriptor on success; check EXPECT_LE(0, objs[3]) like the other CREATE_* paths. The incorrect expectation was noted on list (Mar 2025) but is still present in mainline. Signed-off-by: Iván Ezequiel Rodriguez Reviewed-by: Elizabeth Figura Signed-off-by: Elizabeth Figura Link: https://patch.msgid.link/20260723201301.11826-2-zfigura@codeweavers.com Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/drivers/ntsync/ntsync.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c index e6a37214aa46..12b4b81edf7f 100644 --- a/tools/testing/selftests/drivers/ntsync/ntsync.c +++ b/tools/testing/selftests/drivers/ntsync/ntsync.c @@ -968,7 +968,7 @@ TEST(wake_all) auto_event_args.manual = false; auto_event_args.signaled = true; objs[3] = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &auto_event_args); - EXPECT_EQ(0, objs[3]); + EXPECT_LE(0, objs[3]); wait_args.timeout = get_abs_timeout(1000); wait_args.objs = (uintptr_t)objs; From 1d076f7524b57e7db32aeac68009c72653cb3d1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ezequiel=20Rodriguez?= Date: Thu, 23 Jul 2026 15:12:58 -0500 Subject: [PATCH 404/513] selftests: ntsync: add wait argument validation tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add coverage for documented EINVAL cases: zero owner on wait any/all, non-zero pad, and objects from a different /dev/ntsync instance. Signed-off-by: Iván Ezequiel Rodriguez Reviewed-by: Elizabeth Figura Signed-off-by: Elizabeth Figura Link: https://patch.msgid.link/20260723201301.11826-3-zfigura@codeweavers.com Signed-off-by: Greg Kroah-Hartman --- .../testing/selftests/drivers/ntsync/ntsync.c | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c index 12b4b81edf7f..c9fe4d5987ec 100644 --- a/tools/testing/selftests/drivers/ntsync/ntsync.c +++ b/tools/testing/selftests/drivers/ntsync/ntsync.c @@ -1340,4 +1340,48 @@ TEST(stress_wait) close(stress_device); } +TEST(wait_args_validation) +{ + struct ntsync_sem_args sem_args = { .count = 1, .max = 1 }; + struct ntsync_wait_args wait_args = {0}; + struct timespec timeout; + int fd, fd2, sem, ret; + __u32 index; + + fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY); + ASSERT_GE(fd, 0); + + fd2 = open("/dev/ntsync", O_CLOEXEC | O_RDONLY); + ASSERT_GE(fd2, 0); + + sem = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args); + EXPECT_GE(sem, 0); + + ret = wait_any(fd, 1, &sem, 0, &index); + EXPECT_EQ(-1, ret); + EXPECT_EQ(EINVAL, errno); + + ret = wait_all(fd, 1, &sem, 0, &index); + EXPECT_EQ(-1, ret); + EXPECT_EQ(EINVAL, errno); + + clock_gettime(CLOCK_MONOTONIC, &timeout); + wait_args.timeout = timeout.tv_sec * 1000000000ULL + timeout.tv_nsec; + wait_args.count = 0; + wait_args.objs = 0; + wait_args.owner = 123; + wait_args.pad = 1; + ret = ioctl(fd, NTSYNC_IOC_WAIT_ANY, &wait_args); + EXPECT_EQ(-1, ret); + EXPECT_EQ(EINVAL, errno); + + ret = wait_any(fd2, 1, &sem, 123, &index); + EXPECT_EQ(-1, ret); + EXPECT_EQ(EINVAL, errno); + + close(sem); + close(fd2); + close(fd); +} + TEST_HARNESS_MAIN From 61611481f7b599e0526a3782c626b1a9deeb48bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ezequiel=20Rodriguez?= Date: Thu, 23 Jul 2026 15:12:59 -0500 Subject: [PATCH 405/513] ntsync: reject wait ioctls with zero owner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setup_wait() already validates pad and flags but not owner, while Documentation/userspace-api/ntsync.rst requires EINVAL when owner is zero. Reject early before queueing waiters. Signed-off-by: Iván Ezequiel Rodriguez Signed-off-by: Griffin Kroah-Hartman Reviewed-by: Elizabeth Figura Signed-off-by: Elizabeth Figura Link: https://patch.msgid.link/20260723201301.11826-4-zfigura@codeweavers.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ntsync.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c index 02c9d1192812..4a805919bb0c 100644 --- a/drivers/misc/ntsync.c +++ b/drivers/misc/ntsync.c @@ -875,6 +875,9 @@ static int setup_wait(struct ntsync_device *dev, if (args->pad || (args->flags & ~NTSYNC_WAIT_REALTIME)) return -EINVAL; + if (!args->owner) + return -EINVAL; + if (size >= sizeof(fds)) return -EINVAL; From 2fded3140d606d4b5d5544612076a28659fa0ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ezequiel=20Rodriguez?= Date: Thu, 23 Jul 2026 15:13:00 -0500 Subject: [PATCH 406/513] docs: ntsync: align ioctl names and struct layouts with uapi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The uAPI documentation used outdated or conceptual ioctl macro names and listed ntsync_event_args / ntsync_wait_args fields in the wrong order. Match include/uapi/linux/ntsync.h and note that absolute MONOTONIC timeouts are interpreted in the caller's time namespace. Signed-off-by: Iván Ezequiel Rodriguez Reviewed-by: Elizabeth Figura Signed-off-by: Elizabeth Figura Link: https://patch.msgid.link/20260723201301.11826-5-zfigura@codeweavers.com Signed-off-by: Greg Kroah-Hartman --- Documentation/userspace-api/ntsync.rst | 33 +++++++++++++------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/Documentation/userspace-api/ntsync.rst b/Documentation/userspace-api/ntsync.rst index 25e7c4aef968..a778016feb87 100644 --- a/Documentation/userspace-api/ntsync.rst +++ b/Documentation/userspace-api/ntsync.rst @@ -83,18 +83,18 @@ structures used in ioctl calls:: }; struct ntsync_event_args { - __u32 signaled; __u32 manual; + __u32 signaled; }; struct ntsync_wait_args { __u64 timeout; __u64 objs; __u32 count; - __u32 owner; __u32 index; - __u32 alert; __u32 flags; + __u32 owner; + __u32 alert; __u32 pad; }; @@ -152,7 +152,7 @@ The ioctls on the device file are as follows: The ioctls on the individual objects are as follows: -.. c:macro:: NTSYNC_IOC_SEM_POST +.. c:macro:: NTSYNC_IOC_SEM_RELEASE Post to a semaphore object. Takes a pointer to a 32-bit integer, which on input holds the count to be added to the semaphore, and on @@ -186,7 +186,7 @@ The ioctls on the individual objects are as follows: unowned and signaled, and eligible threads waiting on it will be woken as appropriate. -.. c:macro:: NTSYNC_IOC_SET_EVENT +.. c:macro:: NTSYNC_IOC_EVENT_SET Signal an event object. Takes a pointer to a 32-bit integer, which on output contains the previous state of the event. @@ -194,12 +194,12 @@ The ioctls on the individual objects are as follows: Eligible threads will be woken, and auto-reset events will be designaled appropriately. -.. c:macro:: NTSYNC_IOC_RESET_EVENT +.. c:macro:: NTSYNC_IOC_EVENT_RESET Designal an event object. Takes a pointer to a 32-bit integer, which on output contains the previous state of the event. -.. c:macro:: NTSYNC_IOC_PULSE_EVENT +.. c:macro:: NTSYNC_IOC_EVENT_PULSE Wake threads waiting on an event object while leaving it in an unsignaled state. Takes a pointer to a 32-bit integer, which on @@ -213,7 +213,7 @@ The ioctls on the individual objects are as follows: afterwards, and a simultaneous read operation will always report the event as unsignaled. -.. c:macro:: NTSYNC_IOC_READ_SEM +.. c:macro:: NTSYNC_IOC_SEM_READ Read the current state of a semaphore object. Takes a pointer to struct :c:type:`ntsync_sem_args`, which is used as follows: @@ -225,7 +225,7 @@ The ioctls on the individual objects are as follows: * - ``max`` - On output, contains the maximum count of the semaphore. -.. c:macro:: NTSYNC_IOC_READ_MUTEX +.. c:macro:: NTSYNC_IOC_MUTEX_READ Read the current state of a mutex object. Takes a pointer to struct :c:type:`ntsync_mutex_args`, which is used as follows: @@ -242,7 +242,7 @@ The ioctls on the individual objects are as follows: ``EOWNERDEAD``. In this case, ``count`` and ``owner`` are set to zero. -.. c:macro:: NTSYNC_IOC_READ_EVENT +.. c:macro:: NTSYNC_IOC_EVENT_READ Read the current state of an event object. Takes a pointer to struct :c:type:`ntsync_event_args`, which is used as follows: @@ -255,7 +255,7 @@ The ioctls on the individual objects are as follows: - On output, contains 1 if the event is a manual-reset event, and 0 otherwise. -.. c:macro:: NTSYNC_IOC_KILL_OWNER +.. c:macro:: NTSYNC_IOC_MUTEX_KILL Mark a mutex as unowned and abandoned if it is owned by the given owner. Takes an input-only pointer to a 32-bit integer denoting the @@ -276,11 +276,12 @@ The ioctls on the individual objects are as follows: * - ``timeout`` - Absolute timeout in nanoseconds. If ``NTSYNC_WAIT_REALTIME`` is set, the timeout is measured against the REALTIME clock; - otherwise it is measured against the MONOTONIC clock. If the - timeout is equal to or earlier than the current time, the - function returns immediately without sleeping. If ``timeout`` - is U64_MAX, the function will sleep until an object is - signaled, and will not fail with ``ETIMEDOUT``. + otherwise it is measured against the MONOTONIC clock in the + caller's time namespace. If the timeout is equal to or earlier + than the current time, the function returns immediately + without sleeping. If ``timeout`` is U64_MAX, the function will + sleep until an object is signaled, and will not fail with + ``ETIMEDOUT``. * - ``objs`` - Pointer to an array of ``count`` file descriptors (specified as an integer so that the structure has the same From facd8463a5b62278fd5cdaa3fd6bad49315702a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ezequiel=20Rodriguez?= Date: Thu, 23 Jul 2026 15:13:01 -0500 Subject: [PATCH 407/513] selftests: ntsync: test absolute MONOTONIC waits under time namespaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cover the timens conversion in ntsync_schedule(): with a negative CLOCK_MONOTONIC offset, a 100 ms absolute wait must not return immediately against the host clock. Suggested-by: Maoyi Xie Signed-off-by: Iván Ezequiel Rodriguez Reviewed-by: Elizabeth Figura Signed-off-by: Elizabeth Figura Link: https://patch.msgid.link/20260723201301.11826-6-zfigura@codeweavers.com Signed-off-by: Greg Kroah-Hartman --- .../testing/selftests/drivers/ntsync/ntsync.c | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c index c9fe4d5987ec..1f0dc43bb4c0 100644 --- a/tools/testing/selftests/drivers/ntsync/ntsync.c +++ b/tools/testing/selftests/drivers/ntsync/ntsync.c @@ -8,12 +8,18 @@ #define _GNU_SOURCE #include #include +#include #include +#include #include #include #include #include "kselftest_harness.h" +#ifndef CLONE_NEWTIME +#define CLONE_NEWTIME 0x00000080 +#endif + static int read_sem_state(int sem, __u32 *count, __u32 *max) { struct ntsync_sem_args args; @@ -1384,4 +1390,85 @@ TEST(wait_args_validation) close(fd); } +/* + * Absolute MONOTONIC timeouts must honour the caller's time namespace. + * With a negative monotonic offset, a 100 ms wait must still take ~100 ms + * of namespace time (not return immediately against the host clock). + */ +TEST(wait_any_monotonic_timens) +{ + struct ntsync_sem_args sem_args = {0}; + struct ntsync_wait_args wait_args = {0}; + struct timespec start, end; + char buf[64]; + __u64 elapsed_ns; + int fd, offset_fd, sem, ret, status, len; + pid_t pid; + + if (access("/proc/self/ns/time", F_OK)) + SKIP(return, "Time namespaces are not supported"); + + fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY); + if (fd < 0) + SKIP(return, "/dev/ntsync is not available"); + + ret = unshare(CLONE_NEWTIME); + if (ret) { + close(fd); + if (errno == EPERM) + SKIP(return, "need CAP_SYS_ADMIN for CLONE_NEWTIME"); + ASSERT_EQ(0, ret); + } + + len = snprintf(buf, sizeof(buf), "%d %d 0", CLOCK_MONOTONIC, -10); + offset_fd = open("/proc/self/timens_offsets", O_WRONLY); + ASSERT_LE(0, offset_fd); + ASSERT_EQ(len, write(offset_fd, buf, len)); + close(offset_fd); + + pid = fork(); + ASSERT_LE(0, pid); + if (!pid) { + int obj; + + sem_args.count = 0; + sem_args.max = 1; + sem = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args); + if (sem < 0) + _exit(1); + + obj = sem; + wait_args.timeout = get_abs_timeout(100); + wait_args.objs = (uintptr_t)&obj; + wait_args.count = 1; + wait_args.owner = 123; + wait_args.index = 0xdeadbeef; + + if (clock_gettime(CLOCK_MONOTONIC, &start)) + _exit(2); + ret = ioctl(fd, NTSYNC_IOC_WAIT_ANY, &wait_args); + if (clock_gettime(CLOCK_MONOTONIC, &end)) + _exit(2); + + if (ret != -1 || errno != ETIMEDOUT) + _exit(3); + + elapsed_ns = (end.tv_sec - start.tv_sec) * 1000000000ULL + + (end.tv_nsec - start.tv_nsec); + /* Without timens conversion this returns in ~0 ms. */ + if (elapsed_ns < 50 * 1000000ULL) + _exit(4); + if (elapsed_ns > 1000 * 1000000ULL) + _exit(5); + + _exit(0); + } + + ASSERT_EQ(pid, waitpid(pid, &status, 0)); + EXPECT_TRUE(WIFEXITED(status)); + EXPECT_EQ(0, WEXITSTATUS(status)); + + close(fd); +} + TEST_HARNESS_MAIN From 928a8e9f523df845fc496bcb9811013b67aabec5 Mon Sep 17 00:00:00 2001 From: Muhammad Usama Anjum Date: Thu, 30 Jul 2026 12:12:50 +0100 Subject: [PATCH 408/513] misc: sgi-gru: remove interrupt-context page-table walks The GRU TLB miss handler walks a process's page tables without holding page-table locks or a reference to the mapped page. It also uses a kernel page-table accessor on user page tables and supports only PMD-level large mappings on x86-64. Remove the direct walker. Send interrupt faults directly to user polling mode so the existing call-OS fallback retries them in process context. Remove the mmap-lock failure statistic that can no longer be incremented. Fixes: 142586409c8b ("GRU Driver: page faults & exceptions") Signed-off-by: Muhammad Usama Anjum Link: https://patch.msgid.link/20260730111316.3672672-2-usama.anjum@arm.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sgi-gru/grufault.c | 101 ++++--------------------------- drivers/misc/sgi-gru/gruprocfs.c | 1 - drivers/misc/sgi-gru/grutables.h | 1 - 3 files changed, 12 insertions(+), 91 deletions(-) diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c index 3557d78ee47a..5a87c12f444a 100644 --- a/drivers/misc/sgi-gru/grufault.c +++ b/drivers/misc/sgi-gru/grufault.c @@ -166,13 +166,8 @@ static void get_clear_fault_map(struct gru_state *gru, } /* - * Atomic (interrupt context) & non-atomic (user context) functions to - * convert a vaddr into a physical address. The size of the page - * is returned in pageshift. - * returns: - * 0 - successful - * < 0 - error code - * 1 - (atomic only) try again in non-atomic context + * Convert a user virtual address to a physical address in process context. + * The size of the page is returned in pageshift. */ static int non_atomic_pte_lookup(struct vm_area_struct *vma, unsigned long vaddr, int write, @@ -192,87 +187,25 @@ static int non_atomic_pte_lookup(struct vm_area_struct *vma, return 0; } -/* - * atomic_pte_lookup - * - * Convert a user virtual address to a physical address - * Only supports Intel large pages (2MB only) on x86_64. - * ZZZ - hugepage support is incomplete - * - * NOTE: mmap_lock is already held on entry to this function. This - * guarantees existence of the page tables. - */ -static int atomic_pte_lookup(struct vm_area_struct *vma, unsigned long vaddr, - int write, unsigned long *paddr, int *pageshift) -{ - pgd_t *pgdp; - p4d_t *p4dp; - pud_t *pudp; - pmd_t *pmdp; - pte_t pte; - - pgdp = pgd_offset(vma->vm_mm, vaddr); - if (unlikely(pgd_none(*pgdp))) - goto err; - - p4dp = p4d_offset(pgdp, vaddr); - if (unlikely(p4d_none(*p4dp))) - goto err; - - pudp = pud_offset(p4dp, vaddr); - if (unlikely(pud_none(*pudp))) - goto err; - - pmdp = pmd_offset(pudp, vaddr); - if (unlikely(pmd_none(*pmdp))) - goto err; -#ifdef CONFIG_X86_64 - if (unlikely(pmd_leaf(*pmdp))) - pte = ptep_get((pte_t *)pmdp); - else -#endif - pte = *pte_offset_kernel(pmdp, vaddr); - - if (unlikely(!pte_present(pte) || - (write && (!pte_write(pte) || !pte_dirty(pte))))) - return 1; - - *paddr = pte_pfn(pte) << PAGE_SHIFT; -#ifdef CONFIG_HUGETLB_PAGE - *pageshift = is_vm_hugetlb_page(vma) ? HPAGE_SHIFT : PAGE_SHIFT; -#else - *pageshift = PAGE_SHIFT; -#endif - return 0; - -err: - return 1; -} - static int gru_vtop(struct gru_thread_state *gts, unsigned long vaddr, int write, int atomic, unsigned long *gpa, int *pageshift) { struct mm_struct *mm = gts->ts_mm; struct vm_area_struct *vma; unsigned long paddr; - int ret, ps; + int ps; vma = find_vma(mm, vaddr); if (!vma) goto inval; - /* - * Atomic lookup is faster & usually works even if called in non-atomic - * context. - */ - rmb(); /* Must/check ms_range_active before loading PTEs */ - ret = atomic_pte_lookup(vma, vaddr, write, &paddr, &ps); - if (ret) { - if (atomic) - goto upm; - if (non_atomic_pte_lookup(vma, vaddr, write, &paddr, &ps)) - goto inval; - } + if (atomic) + goto upm; + + /* Order the caller's ms_range_active check before loading PTEs. */ + rmb(); + if (non_atomic_pte_lookup(vma, vaddr, write, &paddr, &ps)) + goto inval; if (is_gru_paddr(paddr)) goto inval; paddr = paddr & ~((1UL << ps) - 1); @@ -569,19 +502,9 @@ static irqreturn_t gru_intr(int chiplet, int blade) continue; } - /* - * This is running in interrupt context. Trylock the mmap_lock. - * If it fails, retry the fault in user context. - */ + /* Address translation may sleep, so retry the fault in user context. */ gts->ustats.fmm_tlbmiss++; - if (!gts->ts_force_cch_reload && - mmap_read_trylock(gts->ts_mm)) { - gru_try_dropin(gru, gts, tfh, NULL); - mmap_read_unlock(gts->ts_mm); - } else { - tfh_user_polling_mode(tfh); - STAT(intr_mm_lock_failed); - } + tfh_user_polling_mode(tfh); } return IRQ_HANDLED; } diff --git a/drivers/misc/sgi-gru/gruprocfs.c b/drivers/misc/sgi-gru/gruprocfs.c index 97b8b38ab47d..b8139c27bc7f 100644 --- a/drivers/misc/sgi-gru/gruprocfs.c +++ b/drivers/misc/sgi-gru/gruprocfs.c @@ -54,7 +54,6 @@ static int statistics_show(struct seq_file *s, void *p) printstat(s, intr_cbr); printstat(s, intr_tfh); printstat(s, intr_spurious); - printstat(s, intr_mm_lock_failed); printstat(s, call_os); printstat(s, call_os_wait_queue); printstat(s, user_flush_tlb); diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h index 640daf1994df..3348552925c6 100644 --- a/drivers/misc/sgi-gru/grutables.h +++ b/drivers/misc/sgi-gru/grutables.h @@ -182,7 +182,6 @@ struct gru_stats_s { atomic_long_t intr_cbr; atomic_long_t intr_tfh; atomic_long_t intr_spurious; - atomic_long_t intr_mm_lock_failed; atomic_long_t call_os; atomic_long_t call_os_wait_queue; atomic_long_t user_flush_tlb; From 7d58b218e7e3552d718117eb859acb1b2df90720 Mon Sep 17 00:00:00 2001 From: Muhammad Usama Anjum Date: Thu, 30 Jul 2026 12:12:51 +0100 Subject: [PATCH 409/513] misc: sgi-gru: remove obsolete atomic fault-handling state After interrupt faults are sent directly to user polling mode, only the process-context call path remains. Remove the atomic flag passed through the fault-handling interfaces and the related dead code. No functional change. Signed-off-by: Muhammad Usama Anjum Link: https://patch.msgid.link/20260730111316.3672672-3-usama.anjum@arm.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sgi-gru/grufault.c | 68 +++++++++++++-------------------- 1 file changed, 26 insertions(+), 42 deletions(-) diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c index 5a87c12f444a..e43fdea01cb6 100644 --- a/drivers/misc/sgi-gru/grufault.c +++ b/drivers/misc/sgi-gru/grufault.c @@ -31,7 +31,6 @@ /* Return codes for vtop functions */ #define VTOP_SUCCESS 0 #define VTOP_INVALID -1 -#define VTOP_RETRY -2 /* @@ -188,7 +187,7 @@ static int non_atomic_pte_lookup(struct vm_area_struct *vma, } static int gru_vtop(struct gru_thread_state *gts, unsigned long vaddr, - int write, int atomic, unsigned long *gpa, int *pageshift) + int write, unsigned long *gpa, int *pageshift) { struct mm_struct *mm = gts->ts_mm; struct vm_area_struct *vma; @@ -199,9 +198,6 @@ static int gru_vtop(struct gru_thread_state *gts, unsigned long vaddr, if (!vma) goto inval; - if (atomic) - goto upm; - /* Order the caller's ms_range_active check before loading PTEs. */ rmb(); if (non_atomic_pte_lookup(vma, vaddr, write, &paddr, &ps)) @@ -215,8 +211,6 @@ static int gru_vtop(struct gru_thread_state *gts, unsigned long vaddr, inval: return VTOP_INVALID; -upm: - return VTOP_RETRY; } @@ -240,7 +234,7 @@ static void gru_flush_cache_cbe(struct gru_control_block_extended *cbe) * the end of the bcopy tranfer, whichever is smaller. */ static void gru_preload_tlb(struct gru_state *gru, - struct gru_thread_state *gts, int atomic, + struct gru_thread_state *gts, unsigned long fault_vaddr, int asid, int write, unsigned char tlb_preload_count, struct gru_tlb_fault_handle *tfh, @@ -262,13 +256,13 @@ static void gru_preload_tlb(struct gru_state *gru, vaddr = min(vaddr, fault_vaddr + tlb_preload_count * PAGE_SIZE); while (vaddr > fault_vaddr) { - ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift); + ret = gru_vtop(gts, vaddr, write, &gpa, &pageshift); if (ret || tfh_write_only(tfh, gpa, GAA_RAM, vaddr, asid, write, GRU_PAGESIZE(pageshift))) return; gru_dbg(grudev, - "%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, rw %d, ps %d, gpa 0x%lx\n", - atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh, + "gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, rw %d, ps %d, gpa 0x%lx\n", + gru->gs_gid, gts, tfh, vaddr, asid, write, pageshift, gpa); vaddr -= PAGE_SIZE; STAT(tlb_preload_page); @@ -276,13 +270,13 @@ static void gru_preload_tlb(struct gru_state *gru, } /* - * Drop a TLB entry into the GRU. The fault is described by info in an TFH. - * Input: - * cb Address of user CBR. Null if not running in user context - * Return: - * 0 = dropin, exception, or switch to UPM successful - * 1 = range invalidate active - * < 0 = error code + * Drop a TLB entry into the GRU. The fault is described by info in a TFH. + * Input: + * cbk Address of the user CBR + * Return: + * 0 = dropin, exception, or switch to UPM successful + * 1 = retry required + * < 0 = error code * */ static int gru_try_dropin(struct gru_state *gru, @@ -292,7 +286,7 @@ static int gru_try_dropin(struct gru_state *gru, { struct gru_control_block_extended *cbe = NULL; unsigned char tlb_preload_count = gts->ts_tlb_preload_count; - int pageshift = 0, asid, write, ret, atomic = !cbk, indexway; + int pageshift = 0, asid, write, ret, indexway; unsigned long gpa = 0, vaddr = 0; /* @@ -324,7 +318,7 @@ static int gru_try_dropin(struct gru_state *gru, } if (tfh->state == TFHSTATE_IDLE) goto failidle; - if (tfh->state == TFHSTATE_MISS_FMM && cbk) + if (tfh->state == TFHSTATE_MISS_FMM) goto failfmm; write = (tfh->cause & TFHCAUSE_TLB_MOD) != 0; @@ -343,22 +337,20 @@ static int gru_try_dropin(struct gru_state *gru, if (atomic_read(>s->ts_gms->ms_range_active)) goto failactive; - ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift); + ret = gru_vtop(gts, vaddr, write, &gpa, &pageshift); if (ret == VTOP_INVALID) goto failinval; - if (ret == VTOP_RETRY) - goto failupm; if (!(gts->ts_sizeavail & GRU_SIZEAVAIL(pageshift))) { gts->ts_sizeavail |= GRU_SIZEAVAIL(pageshift); - if (atomic || !gru_update_cch(gts)) { + if (!gru_update_cch(gts)) { gts->ts_force_cch_reload = 1; goto failupm; } } if (unlikely(cbe) && pageshift == PAGE_SHIFT) { - gru_preload_tlb(gru, gts, atomic, vaddr, asid, write, tlb_preload_count, tfh, cbe); + gru_preload_tlb(gru, gts, vaddr, asid, write, tlb_preload_count, tfh, cbe); gru_flush_cache_cbe(cbe); } @@ -367,9 +359,9 @@ static int gru_try_dropin(struct gru_state *gru, tfh_write_restart(tfh, gpa, GAA_RAM, vaddr, asid, write, GRU_PAGESIZE(pageshift)); gru_dbg(grudev, - "%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, indexway 0x%x," + "gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, indexway 0x%x," " rw %d, ps %d, gpa 0x%lx\n", - atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh, vaddr, asid, + gru->gs_gid, gts, tfh, vaddr, asid, indexway, write, pageshift, gpa); STAT(tlb_dropin); return 0; @@ -378,15 +370,12 @@ static int gru_try_dropin(struct gru_state *gru, /* No asid (delayed unload). */ STAT(tlb_dropin_fail_no_asid); gru_dbg(grudev, "FAILED no_asid tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); - if (!cbk) - tfh_user_polling_mode(tfh); - else - gru_flush_cache(tfh); + gru_flush_cache(tfh); gru_flush_cache_cbe(cbe); return -EAGAIN; failupm: - /* Atomic failure switch CBR to UPM */ + /* CCH update failure switches the CBR back to UPM. */ tfh_user_polling_mode(tfh); gru_flush_cache_cbe(cbe); STAT(tlb_dropin_fail_upm); @@ -405,8 +394,7 @@ static int gru_try_dropin(struct gru_state *gru, /* TFH status did not show exception pending */ gru_flush_cache(tfh); gru_flush_cache_cbe(cbe); - if (cbk) - gru_flush_cache(cbk); + gru_flush_cache(cbk); STAT(tlb_dropin_fail_no_exception); gru_dbg(grudev, "FAILED non-exception tfh: 0x%p, status %d, state %d\n", tfh, tfh->status, tfh->state); @@ -416,14 +404,13 @@ static int gru_try_dropin(struct gru_state *gru, /* TFH state was idle - no miss pending */ gru_flush_cache(tfh); gru_flush_cache_cbe(cbe); - if (cbk) - gru_flush_cache(cbk); + gru_flush_cache(cbk); STAT(tlb_dropin_fail_idle); gru_dbg(grudev, "FAILED idle tfh: 0x%p, state %d\n", tfh, tfh->state); return 0; failinval: - /* All errors (atomic & non-atomic) switch CBR to EXCEPTION state */ + /* Invalid translations switch the CBR to EXCEPTION state. */ tfh_exception(tfh); gru_flush_cache_cbe(cbe); STAT(tlb_dropin_fail_invalid); @@ -431,11 +418,8 @@ static int gru_try_dropin(struct gru_state *gru, return -EFAULT; failactive: - /* Range invalidate active. Switch to UPM iff atomic */ - if (!cbk) - tfh_user_polling_mode(tfh); - else - gru_flush_cache(tfh); + /* Retry after the active range invalidation completes. */ + gru_flush_cache(tfh); gru_flush_cache_cbe(cbe); STAT(tlb_dropin_fail_range_active); gru_dbg(grudev, "FAILED range active: tfh 0x%p, vaddr 0x%lx\n", From 48c7907d928139debf4d73575f688e858ff24aca Mon Sep 17 00:00:00 2001 From: Muhammad Usama Anjum Date: Thu, 30 Jul 2026 12:12:52 +0100 Subject: [PATCH 410/513] misc: sgi-gru: inline the user CBR status update The process-context fault path always has a valid user CBR pointer. Inline the one-use status helper and remove its obsolete NULL check. No functional change. Signed-off-by: Muhammad Usama Anjum Link: https://patch.msgid.link/20260730111316.3672672-4-usama.anjum@arm.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sgi-gru/grufault.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c index e43fdea01cb6..6b7e7dc37eac 100644 --- a/drivers/misc/sgi-gru/grufault.c +++ b/drivers/misc/sgi-gru/grufault.c @@ -111,21 +111,6 @@ static void gru_unlock_gts(struct gru_thread_state *gts) mmap_read_unlock(current->mm); } -/* - * Set a CB.istatus to active using a user virtual address. This must be done - * just prior to a TFH RESTART. The new cb.istatus is an in-cache status ONLY. - * If the line is evicted, the status may be lost. The in-cache update - * is necessary to prevent the user from seeing a stale cb.istatus that will - * change as soon as the TFH restart is complete. Races may cause an - * occasional failure to clear the cb.istatus, but that is ok. - */ -static void gru_cb_set_istatus_active(struct gru_instruction_bits *cbk) -{ - if (cbk) { - cbk->istatus = CBS_ACTIVE; - } -} - /* * Read & clear a TFM * @@ -354,7 +339,12 @@ static int gru_try_dropin(struct gru_state *gru, gru_flush_cache_cbe(cbe); } - gru_cb_set_istatus_active(cbk); + /* + * Set CB.istatus active in cache before restarting the TFH to avoid + * exposing stale pre-restart status. Cacheline eviction may lose the + * update, but an occasional stale status is harmless. + */ + cbk->istatus = CBS_ACTIVE; gts->ustats.tlbdropin++; tfh_write_restart(tfh, gpa, GAA_RAM, vaddr, asid, write, GRU_PAGESIZE(pageshift)); From 18dd0b4e489e4f0e3b748cf36ccb909c75a3efab Mon Sep 17 00:00:00 2001 From: Hari Mishal Date: Fri, 17 Jul 2026 17:06:22 +0200 Subject: [PATCH 411/513] virtio_console: take a kref in find_port_by_vq() to fix port UAF find_port_by_vq() returns a raw struct port pointer without taking a reference on it, unlike find_port_by_devt_in_portdev() which does. find_port_by_vq()'s only two callers, in_intr() and out_intr(), run as virtqueue interrupt callbacks, entirely independent of and possibly concurrently with unplug_port(), which itself runs from a workqueue when the host sends a VIRTIO_CONSOLE_PORT_REMOVE control message. unplug_port() removes the port from portdev->ports under ports_lock, then later drops its last reference with kref_put(), freeing it via remove_port(). find_port_by_vq() also walks portdev->ports under ports_lock, so if it finds the port still on the list, the list removal, and therefore the eventual kref_put(), has not happened yet, and taking a reference at that point is always safe. Without doing so, in_intr()/out_intr() can be left holding a pointer to a port that unplug_port() frees on another core before they are done using it. Both triggers are host-controlled as the host decides when to send the PORT_REMOVE control message and when to kick the port's data vq. So a malicious backend could race the two on purpose, without any guest side cooperation. The freed object is a generic kmalloc allocation containing a wait_queue_head_t, which in_intr()/out_intr() pass to wake_up_interruptible() after touching the stale pointer. wake_up_interruptible() invokes a function pointer read out of the wait queue's entries. If the freed slab slot is reclaimed with attacker influenced content before that call, then this is an arbitrary function call primitive rather than just undefined behaviour. Take a reference in find_port_by_vq() while still holding ports_lock, matching find_port_by_devt_in_portdev(), and release it in in_intr() and out_intr() once they are done with the port. Signed-off-by: Hari Mishal Link: https://patch.msgid.link/20260717150622.23636-1-harimishal1@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/virtio_console.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index cbdc497f5160..74599ba9160e 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -304,6 +304,12 @@ static struct port *find_port_by_id(struct ports_device *portdev, u32 id) return port; } +/* + * Finds a port by the virtqueue and returns a pointer to struct port + * with the reference count incremented. + * + * Callers MUST decrement it when finished. + */ static struct port *find_port_by_vq(struct ports_device *portdev, struct virtqueue *vq) { @@ -312,8 +318,10 @@ static struct port *find_port_by_vq(struct ports_device *portdev, spin_lock_irqsave(&portdev->ports_lock, flags); list_for_each_entry(port, &portdev->ports, list) - if (port->in_vq == vq || port->out_vq == vq) + if (port->in_vq == vq || port->out_vq == vq) { + kref_get(&port->kref); goto out; + } port = NULL; out: spin_unlock_irqrestore(&portdev->ports_lock, flags); @@ -1708,6 +1716,7 @@ static void out_intr(struct virtqueue *vq) } wake_up_interruptible(&port->waitqueue); + kref_put(&port->kref, remove_port); } static void in_intr(struct virtqueue *vq) @@ -1753,6 +1762,8 @@ static void in_intr(struct virtqueue *vq) if (is_console_port(port) && hvc_poll(port->cons.hvc)) hvc_kick(); + + kref_put(&port->kref, remove_port); } static void control_intr(struct virtqueue *vq) From d07b6f53f0908e3014d9db901ef9ebcfc3d40743 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 18 Jul 2026 22:11:23 -0700 Subject: [PATCH 412/513] most: most.h: fix lots of kernel-doc warnings - document enums with the "enum" keyword - add a description for @dbr_size - add missing descriptions to struct most_interface (some just need a ':' to be good) - add missing descriptions to struct most_component - use Returns: syntax for function return values - convert most_deregister_interface() to kernel-doc notation and fix its parameter name Repairs these warnings: Warning: include/linux/most.h:17 This comment starts with '/**', but isn't a kernel-doc comment. * Interface type Warning: include/linux/most.h:32 This comment starts with '/**', but isn't a kernel-doc comment. * Channel direction. Warning: include/linux/most.h:40 This comment starts with '/**', but isn't a kernel-doc comment. * Channel data type. Warning: include/linux/most.h:131 struct member 'dbr_size' not described in 'most_channel_config' Warning: include/linux/most.h:191 This comment starts with '/**', but isn't a kernel-doc comment. * Interface instance description. Warning: include/linux/most.h:274 struct member 'mod' not described in 'most_component' Warning: include/linux/most.h:274 struct member 'cfg_complete' not described in 'most_component' Warning: include/linux/most.h:288 This comment starts with '/**', but isn't a kernel-doc comment. * Deregisters instance of the interface. Warning: include/linux/most.h:292 No description found for return value of 'most_register_interface' Warning: include/linux/most.h:298 function parameter 'iface' not described in 'most_deregister_interface' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260719051123.2458641-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- include/linux/most.h | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/include/linux/most.h b/include/linux/most.h index 232e01b7f5d2..4aeed08f816e 100644 --- a/include/linux/most.h +++ b/include/linux/most.h @@ -15,7 +15,7 @@ struct module; struct interface_private; /** - * Interface type + * enum most_interface_type - Interface type */ enum most_interface_type { ITYPE_LOOPBACK = 1, @@ -30,7 +30,7 @@ enum most_interface_type { }; /** - * Channel direction. + * enum most_channel_direction - Channel direction. */ enum most_channel_direction { MOST_CH_RX = 1 << 0, @@ -38,7 +38,7 @@ enum most_channel_direction { }; /** - * Channel data type. + * enum most_channel_data_type - Channel data type. */ enum most_channel_data_type { MOST_CH_CONTROL = 1 << 0, @@ -115,6 +115,7 @@ struct most_channel_capability { * @subbuffer_size: size of a subbuffer * @packets_per_xact: number of MOST frames that are packet inside one USB * packet. This is USB specific + * @dbr_size: DBR data buffer size (MediaLB communication only) * * Describes the configuration for a MOST channel. This information is * provided from the MostCore to a HDM (like the Medusa PCIe Interface) as a @@ -189,28 +190,31 @@ struct mbo { }; /** - * Interface instance description. + * struct most_interface - Interface instance description. * * Describes an interface of a MOST device the core driver is bound to. * This structure is allocated and initialized in the HDM. MostCore may not * modify this structure. * * @dev: the actual device + * @driver_dev: struct device for calling snd_card_new() * @mod: module - * @interface Interface type. \sa most_interface_type. - * @description PRELIMINARY. + * @interface: Interface type. \sa most_interface_type. + * @description: PRELIMINARY. * Unique description of the device instance from point of view of the * interface in free text form (ASCII). * It may be a hexadecimal presentation of the memory address for the MediaLB * IP or USB device ID with USB properties for USB interface, etc. - * @num_channels Number of channels and size of the channel_vector. - * @channel_vector Properties of the channels. + * @num_channels: Number of channels and size of the channel_vector. + * @channel_vector: Properties of the channels. * Array index represents channel ID by the driver. - * @configure Callback to change data type for the channel of the + * @dma_alloc: Callback to interface driver to allocation DMA memory. + * @dma_free: Callback to interface driver to free a DMA memory allocation. + * @configure: Callback to change data type for the channel of the * interface instance. May be zero if the instance of the interface is not * configurable. Parameter channel_config describes direction and data * type for the channel, configured by the higher level. The content of - * @enqueue Delivers MBO to the HDM for processing. + * @enqueue: Delivers MBO to the HDM for processing. * After HDM completes Rx- or Tx- operation the processed MBO shall * be returned back to the MostCore using completion routine. * The reason to get the MBO delivered from the MostCore after the channel @@ -218,7 +222,7 @@ struct mbo { * In this case the HDM shall hold MBOs and service the channel as usual. * The HDM must be able to hold at least one MBO for each channel. * The callback returns a negative value on error, otherwise 0. - * @poison_channel Informs HDM about closing the channel. The HDM shall + * @poison_channel: Informs HDM about closing the channel. The HDM shall * cancel all transfers and synchronously or asynchronously return * all enqueued for this channel MBOs using the completion routine. * The callback returns a negative value on error, otherwise 0. @@ -226,7 +230,8 @@ struct mbo { * means of "Message exchange over MDP/MEP" * The call of the function request_netinfo with the parameter on_netinfo as * NULL prohibits use of the previously obtained function pointer. - * @priv Private field used by mostcore to store context information. + * @priv: Private field used by mostcore to store context information. + * @p: &struct interface_private pointer */ struct most_interface { struct device *dev; @@ -255,10 +260,12 @@ struct most_interface { * struct most_component - identifies a loadable component for the mostcore * @list: list_head * @name: component name + * @mod: owning module * @probe_channel: function for core to notify driver about channel connection * @disconnect_channel: callback function to disconnect a certain channel * @rx_completion: completion handler for received packets * @tx_completion: completion handler for transmitted packets + * @cfg_complete: setup completion function called after the device is matched */ struct most_component { struct list_head list; @@ -278,7 +285,7 @@ struct most_component { * most_register_interface - Registers instance of the interface. * @iface: Pointer to the interface instance description. * - * Returns a pointer to the kobject of the generated instance. + * Returns: a pointer to the kobject of the generated instance. * * Note: HDM has to ensure that any reference held on the kobj is * released before deregistering the interface. @@ -286,8 +293,8 @@ struct most_component { int most_register_interface(struct most_interface *iface); /** - * Deregisters instance of the interface. - * @intf_instance Pointer to the interface instance description. + * most_deregister_interface - Deregisters instance of the interface. + * @iface: Pointer to the interface instance description. */ void most_deregister_interface(struct most_interface *iface); void most_submit_mbo(struct mbo *mbo); From c16ce856e422e73a54c41131e0332de1afe09b8b Mon Sep 17 00:00:00 2001 From: Carlos Sampaio Ribeiro Date: Sun, 19 Jul 2026 05:18:59 -0300 Subject: [PATCH 413/513] scripts/sbom: catch ValueError from malformed shell quoting parse_inputs_from_commands() only caught CmdParsingError and IndexError when dispatching to command parsers, but several parsers call shlex.split() internally, which raises ValueError on malformed shell quoting (e.g. an unterminated quote). This exception was not caught, so a single malformed build command would abort SBOM generation entirely, even with fail_on_unknown_build_command=False, defeating the purpose of tolerant mode. The issue was found while reviewing the exception handling around the saved-command parser after running its existing tests. It can be reproduced with: parse_inputs_from_commands('gcc "unterminated', fail_on_unknown_build_command=False) Catch ValueError alongside CmdParsingError and IndexError so such commands are logged as a warning/error and skipped instead of aborting the whole run. Add tests covering malformed shell quoting and a missing positional argument. Signed-off-by: Carlos Sampaio Ribeiro Link: https://patch.msgid.link/20260719081859.1001-1-otakurack@gmail.com Signed-off-by: Greg Kroah-Hartman --- .../savedcmd_parser/savedcmd_parser.py | 2 +- .../tests/cmd_graph/test_savedcmd_parser.py | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/scripts/sbom/sbom/cmd_graph/savedcmd_parser/savedcmd_parser.py b/scripts/sbom/sbom/cmd_graph/savedcmd_parser/savedcmd_parser.py index 6a7ea4787aa1..d2ca842a7849 100644 --- a/scripts/sbom/sbom/cmd_graph/savedcmd_parser/savedcmd_parser.py +++ b/scripts/sbom/sbom/cmd_graph/savedcmd_parser/savedcmd_parser.py @@ -57,7 +57,7 @@ def parse_inputs_from_commands( try: inputs = matched_parser(single_command) input_files.extend(inputs) - except (CmdParsingError, IndexError) as e: + except (CmdParsingError, IndexError, ValueError) as e: log_error_or_warning( "Skipped parsing command {single_command} because of command parsing error: {error_message}", single_command=single_command, diff --git a/scripts/sbom/tests/cmd_graph/test_savedcmd_parser.py b/scripts/sbom/tests/cmd_graph/test_savedcmd_parser.py index a061a748e1bf..d7776f072e03 100644 --- a/scripts/sbom/tests/cmd_graph/test_savedcmd_parser.py +++ b/scripts/sbom/tests/cmd_graph/test_savedcmd_parser.py @@ -19,6 +19,31 @@ class TestSavedCmdParser(unittest.TestCase): errors = sbom_logging._error_logger._message_counts # type: ignore self.assertEqual(errors, {}) + # Error handling tests + def test_malformed_shell_quoting(self): + command = 'gcc "unterminated' + with patch.object(sbom_logging, "warning") as warning: + parsed = parse_inputs_from_commands(command, fail_on_unknown_build_command=False) + + self.assertEqual(parsed, []) + warning.assert_called_once_with( + "Skipped parsing command {single_command} because of command parsing error: {error_message}", + single_command=command, + error_message="No closing quotation", + ) + + def test_missing_positional_argument(self): + command = "objcopy" + with patch.object(sbom_logging, "warning") as warning: + parsed = parse_inputs_from_commands(command, fail_on_unknown_build_command=False) + + self.assertEqual(parsed, []) + warning.assert_called_once_with( + "Skipped parsing command {single_command} because of command parsing error: {error_message}", + single_command=command, + error_message="list index out of range", + ) + # Compound command tests def test_dd_cat(self): cmd = "(dd if=arch/x86/boot/setup.bin bs=4k conv=sync status=none; cat arch/x86/boot/vmlinux.bin) >arch/x86/boot/bzImage" From fc220d6be3c7e484b83f92bf6327e17c1ab1d962 Mon Sep 17 00:00:00 2001 From: Sungho Bae Date: Thu, 4 Jun 2026 03:37:54 +0900 Subject: [PATCH 414/513] virtio_console: refactor __send_to_port() buffer ownership Modify __send_to_port() to take ownership of a struct port_buffer * instead of a void * raw buffer. Previously, put_chars() would pass a raw kmemdup'd buffer and free it immediately after __send_to_port() returned. This caused a potential Use-After-Free and data corruption if the virtqueue was shared with nonblocking writers, as virtqueue_get_buf() might return an older completed buffer, causing the newly added buffer to be kfree'd while the host is still DMAing from it. By transferring ownership of the allocated port_buffer to __send_to_port(), we ensure that the exact buffer returned by the host is the one that gets freed, resolving the memory lifecycle mismatch. Signed-off-by: Sungho Bae Link: https://patch.msgid.link/20260603183757.21587-2-baver.bae@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/virtio_console.c | 69 +++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 74599ba9160e..25fa26cef7bd 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -410,7 +410,7 @@ static void reclaim_dma_bufs(void) } static struct port_buffer *alloc_buf(struct virtio_device *vdev, size_t buf_size, - int pages) + int pages, gfp_t gfp) { struct port_buffer *buf; @@ -444,11 +444,10 @@ static struct port_buffer *alloc_buf(struct virtio_device *vdev, size_t buf_size /* Increase device refcnt to avoid freeing it */ get_device(buf->dev); - buf->buf = dma_alloc_coherent(buf->dev, buf_size, &buf->dma, - GFP_KERNEL); + buf->buf = dma_alloc_coherent(buf->dev, buf_size, &buf->dma, gfp); } else { buf->dev = NULL; - buf->buf = kmalloc(buf_size, GFP_KERNEL); + buf->buf = kmalloc(buf_size, gfp); } if (!buf->buf) @@ -603,7 +602,7 @@ static void reclaim_consumed_buffers(struct port *port) static ssize_t __send_to_port(struct port *port, struct scatterlist *sg, int nents, size_t in_count, - void *data, bool nonblock) + struct port_buffer *buf, bool nonblock) { struct virtqueue *out_vq; int err; @@ -616,14 +615,14 @@ static ssize_t __send_to_port(struct port *port, struct scatterlist *sg, reclaim_consumed_buffers(port); - err = virtqueue_add_outbuf(out_vq, sg, nents, data, GFP_ATOMIC); + err = virtqueue_add_outbuf(out_vq, sg, nents, buf, GFP_ATOMIC); /* Tell Host to go! */ virtqueue_kick(out_vq); if (err) { in_count = 0; - goto done; + goto free_and_done; } if (out_vq->num_free == 0) @@ -640,10 +639,19 @@ static ssize_t __send_to_port(struct port *port, struct scatterlist *sg, * buffer and relax the spinning requirement. The downside is * we need to kmalloc a GFP_ATOMIC buffer each time the * console driver writes something out. + * + * Spin until host returns the buffer. + * Capture the returned buf so we can free it. + * If broken, buf == NULL and buf stays in the vq; + * remove_vqs() will call virtqueue_detach_unused_buf() -> free_buf(). */ - while (!virtqueue_get_buf(out_vq, &len) + while (!(buf = virtqueue_get_buf(out_vq, &len)) && !virtqueue_is_broken(out_vq)) cpu_relax(); + +free_and_done: + if (buf) + free_buf(buf, false); done: spin_unlock_irqrestore(&port->outvq_lock, flags); @@ -824,14 +832,14 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, count = min((size_t)(32 * 1024), count); - buf = alloc_buf(port->portdev->vdev, count, 0); + buf = alloc_buf(port->portdev->vdev, count, 0, GFP_KERNEL); if (!buf) return -ENOMEM; ret = copy_from_user(buf->buf, ubuf, count); if (ret) { - ret = -EFAULT; - goto free_buf; + free_buf(buf, true); + return -EFAULT; } /* @@ -843,15 +851,7 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, */ nonblock = true; sg_init_one(sg, buf->buf, count); - ret = __send_to_port(port, sg, 1, count, buf, nonblock); - - if (nonblock && ret > 0) - goto out; - -free_buf: - free_buf(buf, true); -out: - return ret; + return __send_to_port(port, sg, 1, count, buf, nonblock); } struct sg_list { @@ -940,7 +940,7 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, goto error_out; occupancy = pipe_buf_usage(pipe); - buf = alloc_buf(port->portdev->vdev, 0, occupancy); + buf = alloc_buf(port->portdev->vdev, 0, occupancy, GFP_KERNEL); if (!buf) { ret = -ENOMEM; @@ -954,11 +954,12 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, sg_init_table(sgl.sg, sgl.size); ret = __splice_from_pipe(pipe, &sd, pipe_to_sg); pipe_unlock(pipe); + if (likely(ret > 0)) ret = __send_to_port(port, buf->sg, sgl.n, sgl.len, buf, true); - - if (unlikely(ret <= 0)) + else free_buf(buf, true); + return ret; error_out: @@ -1116,21 +1117,25 @@ static ssize_t put_chars(u32 vtermno, const u8 *buf, size_t count) { struct port *port; struct scatterlist sg[1]; - void *data; - int ret; + struct port_buffer *pbuf; port = find_port_by_vtermno(vtermno); if (!port) return -EPIPE; - data = kmemdup(buf, count, GFP_ATOMIC); - if (!data) + pbuf = alloc_buf(port->portdev->vdev, count, 0, GFP_ATOMIC); + if (!pbuf) return -ENOMEM; - sg_init_one(sg, data, count); - ret = __send_to_port(port, sg, 1, count, data, false); - kfree(data); - return ret; + memcpy(pbuf->buf, buf, count); + pbuf->len = count; + sg_init_one(sg, pbuf->buf, count); + + /* + * Ownership of pbuf is transferred to __send_to_port(). + * Do not touch or free pbuf after this call. + */ + return __send_to_port(port, sg, 1, count, pbuf, false); } /* @@ -1303,7 +1308,7 @@ static int fill_queue(struct virtqueue *vq, spinlock_t *lock) nr_added_bufs = 0; do { - buf = alloc_buf(vq->vdev, PAGE_SIZE, 0); + buf = alloc_buf(vq->vdev, PAGE_SIZE, 0, GFP_KERNEL); if (!buf) return -ENOMEM; From a616ad344d9f7fe6abc17aa0a43bba93fb077384 Mon Sep 17 00:00:00 2001 From: Sungho Bae Date: Thu, 4 Jun 2026 03:37:55 +0900 Subject: [PATCH 415/513] virtio_console: fix hot-unplug races in TX paths When a port is hot-unplugged, unplug_port() nullifies port->portdev. However, concurrent TX paths (__send_to_port, put_chars) could read a stale pointer or encounter a NULL pointer dereference. Add READ_ONCE(port->portdev) and NULL checks in the TX paths. In __send_to_port(), move the out_vq assignment inside the outvq_lock and check portdev under the lock. Correspondingly, update unplug_port() to NULL out port->portdev while holding the outvq_lock to serialize with __send_to_port(). In put_chars(), return count instead of 0 on unplug to prevent the hvc layer from spinning in an infinite retry loop. Signed-off-by: Sungho Bae Link: https://patch.msgid.link/20260603183757.21587-3-baver.bae@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/virtio_console.c | 37 +++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 25fa26cef7bd..217ca6129fc0 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -608,11 +608,19 @@ static ssize_t __send_to_port(struct port *port, struct scatterlist *sg, int err; unsigned long flags; unsigned int len; - - out_vq = port->out_vq; + struct ports_device *portdev; spin_lock_irqsave(&port->outvq_lock, flags); + portdev = READ_ONCE(port->portdev); + + if (!portdev) { + in_count = 0; + goto free_and_done; + } + + out_vq = port->out_vq; + reclaim_consumed_buffers(port); err = virtqueue_add_outbuf(out_vq, sg, nents, buf, GFP_ATOMIC); @@ -1118,12 +1126,24 @@ static ssize_t put_chars(u32 vtermno, const u8 *buf, size_t count) struct port *port; struct scatterlist sg[1]; struct port_buffer *pbuf; + struct ports_device *portdev; port = find_port_by_vtermno(vtermno); if (!port) return -EPIPE; - pbuf = alloc_buf(port->portdev->vdev, count, 0, GFP_ATOMIC); + /* + * Silently drop output if device hot-unplug is in progress. + * portdev was NULLed by unplug_port() after hvc_remove() was + * already called, so the hvc layer will stop invoking put_chars() + * very soon. Returning count avoids a pointless retry loop in the + * interim. + */ + portdev = READ_ONCE(port->portdev); + if (!portdev) + return count; + + pbuf = alloc_buf(portdev->vdev, count, 0, GFP_ATOMIC); if (!pbuf) return -ENOMEM; @@ -1511,11 +1531,16 @@ static void unplug_port(struct port *port) remove_port_data(port); /* - * We should just assume the device itself has gone off -- - * else a close on an open port later will try to send out a - * control message. + * Null out portdev under outvq_lock so that __send_to_port() + * cannot race: it checks port->portdev inside the same lock + * and bails out if NULL, preventing any buffer from being + * enqueued to an already torn-down virtqueue. Also prevents + * a close on an open port later from sending a stale control + * message. */ + spin_lock_irq(&port->outvq_lock); port->portdev = NULL; + spin_unlock_irq(&port->outvq_lock); sysfs_remove_group(&port->dev->kobj, &port_attribute_group); device_destroy(&port_class, port->dev->devt); From 952261953cf79820aa4350679882db0d4f71f493 Mon Sep 17 00:00:00 2001 From: Sungho Bae Date: Thu, 4 Jun 2026 03:37:56 +0900 Subject: [PATCH 416/513] virtio_console: fix control queue race during restore In virtcons_restore(), after virtio_device_ready() sets DRIVER_OK, the device becomes active. If the control receive queue (c_ivq) is populated immediately, the host can instantly deliver pending control messages (e.g., VIRTIO_CONSOLE_PORT_REMOVE). This triggers the control_work_handler(), which can modify the portdev->ports list concurrently with the unprotected list_for_each_entry loop in virtcons_restore(), leading to list corruption or Use-After-Free. Fix this by deferring the population of the control receive queue (fill_queue for c_ivq) until after the list iteration is complete. This ensures the host cannot inject control messages during the vulnerable window. Signed-off-by: Sungho Bae Link: https://patch.msgid.link/20260603183757.21587-4-baver.bae@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/virtio_console.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 217ca6129fc0..a95e4ac87c71 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -2187,9 +2187,6 @@ static int virtcons_restore(struct virtio_device *vdev) virtio_device_ready(portdev->vdev); - if (use_multiport(portdev)) - fill_queue(portdev->c_ivq, &portdev->c_ivq_lock); - list_for_each_entry(port, &portdev->ports, list) { port->in_vq = portdev->in_vqs[port->id]; port->out_vq = portdev->out_vqs[port->id]; @@ -2206,6 +2203,18 @@ static int virtcons_restore(struct virtio_device *vdev) if (port->guest_connected) send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1); } + + /* + * Populate the control receive queue only after the list iteration + * is complete. If we fill this queue before iterating, the host could + * immediately deliver a VIRTIO_CONSOLE_PORT_REMOVE message. + * This would trigger the control workqueue, which modifies the + * portdev->ports list concurrently with the unprotected loop above, + * leading to a Use-After-Free and list corruption. + */ + if (use_multiport(portdev)) + fill_queue(portdev->c_ivq, &portdev->c_ivq_lock); + return 0; } #endif From b187532ac6dba56df9367ae7698001b4623d4cba Mon Sep 17 00:00:00 2001 From: Sungho Bae Date: Thu, 4 Jun 2026 03:37:57 +0900 Subject: [PATCH 417/513] virtio_console: fix race between hvc put_chars and virtqueue teardown on freeze With no_console_suspend enabled, hvc console output can continue while virtio_console is freezing. In that window, put_chars can still enqueue buffers to the output virtqueue while virtcons_freeze is tearing queues down, triggering a BUG_ON in virtqueue_detach_unused_buf_split: BUG_ON(vq->vq.num_free != vq->split.vring.num) Add a pm_freezing flag to ports_device. Set it via smp_store_release() at the start of virtcons_freeze(); put_chars() and __send_to_port() drop output while the flag is set, checked via smp_load_acquire(). The check in __send_to_port() is placed under outvq_lock, making it atomic with remove_port_data() which also acquires outvq_lock. Once remove_port_data() returns for a given port, no concurrent __send_to_port() can add buffers before remove_vqs() tears down the vq. After setting pm_freezing, acquire and release outvq_lock for each port (protected by ports_lock to prevent list manipulation races) before calling virtio_reset_device(). A TX thread that already passed the pm_freezing check may still hold outvq_lock while spinning for host acknowledgment; the drain loop ensures all such threads have completed before the device is reset. Clear pm_freezing in virtcons_restore() only after all port->out_vq pointers have been reassigned to the newly allocated virtqueues, preventing TX paths from dereferencing freed vqs during restore. Link: https://sashiko.dev/#/patchset/20260519162242.7324-1-baver.bae%40gmail.com Signed-off-by: Sungho Bae Link: https://patch.msgid.link/20260603183757.21587-5-baver.bae@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/virtio_console.c | 70 ++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index a95e4ac87c71..62eecfa61646 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -157,6 +157,12 @@ struct ports_device { /* Major number for this device. Ports will be created as minors. */ int chr_major; + + /* + * Set to true during PM freeze to block TX paths that may race + * with virtqueue teardown (e.g. hvc put_chars with no_console_suspend). + */ + bool pm_freezing; }; struct port_stats { @@ -619,6 +625,17 @@ static ssize_t __send_to_port(struct port *port, struct scatterlist *sg, goto free_and_done; } + /* + * Check freeze flag under the lock so that the flag check and + * virtqueue_add_outbuf() are atomic with respect to + * remove_port_data() which also takes outvq_lock. This + * guarantees that once remove_port_data() returns, no new + * buffers can be added before remove_vqs() tears down the vq. + * Pairs with smp_store_release() in virtcons_freeze/restore. + */ + if (smp_load_acquire(&portdev->pm_freezing)) /* pairs with freeze/restore */ + goto free_and_done; + out_vq = port->out_vq; reclaim_consumed_buffers(port); @@ -1133,14 +1150,27 @@ static ssize_t put_chars(u32 vtermno, const u8 *buf, size_t count) return -EPIPE; /* - * Silently drop output if device hot-unplug is in progress. - * portdev was NULLed by unplug_port() after hvc_remove() was - * already called, so the hvc layer will stop invoking put_chars() - * very soon. Returning count avoids a pointless retry loop in the - * interim. + * Silently drop output in two cases, both by returning count so + * that the hvc layer does not spin-retry: + * + * 1. Device hot-unplug (!portdev): portdev was NULLed by + * unplug_port() after hvc_remove() was already called, so + * the hvc layer will stop invoking put_chars() very soon. + * Returning count avoids a pointless retry loop in the + * interim. + * + * 2. PM freeze (pm_freezing): the hvc console stays active + * under no_console_suspend but virtqueues are being torn + * down. Drop the output silently so the hvc layer does not + * stall suspend. + * + * This early check avoids a pointless GFP_ATOMIC allocation; + * __send_to_port() rechecks under outvq_lock for correctness. + * Pairs with smp_store_release() in virtcons_freeze/restore. */ portdev = READ_ONCE(port->portdev); - if (!portdev) + if (!portdev || + smp_load_acquire(&portdev->pm_freezing)) /* pairs with freeze/restore */ return count; pbuf = alloc_buf(portdev->vdev, count, 0, GFP_ATOMIC); @@ -2023,6 +2053,7 @@ static int virtcons_probe(struct virtio_device *vdev) /* Attach this portdev to this virtio_device, and vice-versa. */ portdev->vdev = vdev; vdev->priv = portdev; + portdev->pm_freezing = false; portdev->chr_major = register_chrdev(0, "virtio-portsdev", &portdev_fops); @@ -2142,9 +2173,30 @@ static int virtcons_freeze(struct virtio_device *vdev) { struct ports_device *portdev; struct port *port; + unsigned long flags; portdev = vdev->priv; + /* + * Block TX paths (put_chars, __send_to_port) before resetting the + * device and tearing down virtqueues. This prevents races with + * hvc console writes that remain active under no_console_suspend. + */ + smp_store_release(&portdev->pm_freezing, true); + + /* + * Synchronize with any concurrent __send_to_port() that may have + * passed the pm_freezing check. By acquiring and releasing the + * outvq_lock for each port, we ensure all active TX paths have + * completed before we reset the device. + */ + spin_lock_irqsave(&portdev->ports_lock, flags); + list_for_each_entry(port, &portdev->ports, list) { + spin_lock(&port->outvq_lock); + spin_unlock(&port->outvq_lock); + } + spin_unlock_irqrestore(&portdev->ports_lock, flags); + virtio_reset_device(vdev); if (use_multiport(portdev)) @@ -2215,6 +2267,12 @@ static int virtcons_restore(struct virtio_device *vdev) if (use_multiport(portdev)) fill_queue(portdev->c_ivq, &portdev->c_ivq_lock); + /* + * Allow TX paths only after all port->out_vq pointers have + * been reassigned to the newly allocated virtqueues. + */ + smp_store_release(&portdev->pm_freezing, false); + return 0; } #endif From 4eb0946caf764350e0787fc6fbefe260ed949873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 17 Jul 2026 16:50:47 +0200 Subject: [PATCH 418/513] misc: Drop unused assignment of spi_device_id driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drivers don't make use of the values that were explicitly assigned to the .driver_data members. Drop these assignments. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/e2ad3f93b358cc246a64a3d0a7c3f037b2201412.1784299069.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/gehc-achc.c | 4 ++-- drivers/misc/lattice-ecp3-config.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/gehc-achc.c b/drivers/misc/gehc-achc.c index b8fca4d393c6..1abfa6c257de 100644 --- a/drivers/misc/gehc-achc.c +++ b/drivers/misc/gehc-achc.c @@ -538,8 +538,8 @@ static int gehc_achc_probe(struct spi_device *spi) } static const struct spi_device_id gehc_achc_id[] = { - { "ge,achc", 0 }, - { "achc", 0 }, + { "ge,achc" }, + { "achc" }, { } }; MODULE_DEVICE_TABLE(spi, gehc_achc_id); diff --git a/drivers/misc/lattice-ecp3-config.c b/drivers/misc/lattice-ecp3-config.c index 93949df3bcff..2b93d5660bf2 100644 --- a/drivers/misc/lattice-ecp3-config.c +++ b/drivers/misc/lattice-ecp3-config.c @@ -219,8 +219,8 @@ static void lattice_ecp3_remove(struct spi_device *spi) } static const struct spi_device_id lattice_ecp3_id[] = { - { "ecp3-17", 0 }, - { "ecp3-35", 0 }, + { "ecp3-17" }, + { "ecp3-35" }, { } }; MODULE_DEVICE_TABLE(spi, lattice_ecp3_id); From 125e27168b7866eff0f1b709adc9e7dddef7c738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 17 Jul 2026 16:50:48 +0200 Subject: [PATCH 419/513] misc/pvpanic: Drop unused assignment of acpi_device_id driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver explicitly sets the .driver_data member of struct acpi_device_id to zero without relying on that value. Drop this unused assignments. While touching this array simplify the list terminator and use a named initalizer. This patch doesn't modify the compiled array, only its representation in source form benefits. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/98f1fb885b797f8b3ccf24980d8036d67eb3966d.1784299069.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pvpanic/pvpanic-mmio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/pvpanic/pvpanic-mmio.c b/drivers/misc/pvpanic/pvpanic-mmio.c index bedcda9b6ac5..43a094bfdaec 100644 --- a/drivers/misc/pvpanic/pvpanic-mmio.c +++ b/drivers/misc/pvpanic/pvpanic-mmio.c @@ -57,8 +57,8 @@ static const struct of_device_id pvpanic_mmio_match[] = { MODULE_DEVICE_TABLE(of, pvpanic_mmio_match); static const struct acpi_device_id pvpanic_device_ids[] = { - { "QEMU0001", 0 }, - { "", 0 } + { .id = "QEMU0001" }, + { } }; MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids); From f21139e60b515d14fecb98344913a0cc35a8e2bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 17 Jul 2026 16:50:49 +0200 Subject: [PATCH 420/513] misc: genwqe: Drop unused assignment of pci_device_id driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver explicitly sets the .driver_data member of struct pci_device_id to zero without relying on that value. Drop this unused assignment. While touching this array, also convert it to use one of the PCI_DEVICE macros that is usually used for these and drop the explicit 0 in the list terminator. This patch doesn't modify the compiled array. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/0756a50095f65ada4b82a65a4f827bcd5f0e5d84.1784299069.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_base.c | 88 +++++++++++++-------------------- 1 file changed, 35 insertions(+), 53 deletions(-) diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index 86bfa82723ff..e602eb3c51fa 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c @@ -48,60 +48,42 @@ static struct genwqe_dev *genwqe_devices[GENWQE_CARD_NO_MAX]; /* PCI structure for identifying device by PCI vendor and device ID */ static const struct pci_device_id genwqe_device_table[] = { - { .vendor = PCI_VENDOR_ID_IBM, - .device = PCI_DEVICE_GENWQE, - .subvendor = PCI_SUBVENDOR_ID_IBM, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5, - .class = (PCI_CLASSCODE_GENWQE5 << 8), - .class_mask = ~0, - .driver_data = 0 }, - - /* Initial SR-IOV bring-up image */ - { .vendor = PCI_VENDOR_ID_IBM, - .device = PCI_DEVICE_GENWQE, - .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5_SRIOV, - .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8), - .class_mask = ~0, - .driver_data = 0 }, - - { .vendor = PCI_VENDOR_ID_IBM, /* VF Vendor ID */ - .device = 0x0000, /* VF Device ID */ - .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5_SRIOV, - .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8), - .class_mask = ~0, - .driver_data = 0 }, - - /* Fixed up image */ - { .vendor = PCI_VENDOR_ID_IBM, - .device = PCI_DEVICE_GENWQE, - .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5, - .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8), - .class_mask = ~0, - .driver_data = 0 }, - - { .vendor = PCI_VENDOR_ID_IBM, /* VF Vendor ID */ - .device = 0x0000, /* VF Device ID */ - .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5, - .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8), - .class_mask = ~0, - .driver_data = 0 }, - - /* Even one more ... */ - { .vendor = PCI_VENDOR_ID_IBM, - .device = PCI_DEVICE_GENWQE, - .subvendor = PCI_SUBVENDOR_ID_IBM, - .subdevice = PCI_SUBSYSTEM_ID_GENWQE5_NEW, - .class = (PCI_CLASSCODE_GENWQE5 << 8), - .class_mask = ~0, - .driver_data = 0 }, - - { 0, } /* 0 terminated list. */ + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE, + PCI_SUBVENDOR_ID_IBM, PCI_SUBSYSTEM_ID_GENWQE5), + .class = PCI_CLASSCODE_GENWQE5 << 8, + .class_mask = ~0, + }, { + /* Initial SR-IOV bring-up image */ + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE, + PCI_SUBVENDOR_ID_IBM_SRIOV), + .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8, + .class_mask = ~0, + }, { + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, 0x0000, + PCI_SUBVENDOR_ID_IBM_SRIOV, PCI_SUBSYSTEM_ID_GENWQE5_SRIOV), + .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8, + .class_mask = ~0, + }, { + /* Fixed up image */ + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE, + PCI_SUBVENDOR_ID_IBM_SRIOV, PCI_SUBSYSTEM_ID_GENWQE5), + .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8, + .class_mask = ~0, + }, { + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, 0x0000, + PCI_SUBVENDOR_ID_IBM_SRIOV, PCI_SUBSYSTEM_ID_GENWQE5), + .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8, + .class_mask = ~0, + }, { + /* Even one more ... */ + PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE, + PCI_SUBVENDOR_ID_IBM, PCI_SUBSYSTEM_ID_GENWQE5_NEW), + .class = (PCI_CLASSCODE_GENWQE5 << 8), + .class_mask = ~0, + }, + { } /* 0 terminated list. */ }; - MODULE_DEVICE_TABLE(pci, genwqe_device_table); /** From 230cb31dc3cb8163772fe7c47440227d5285f1e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 17 Jul 2026 16:50:50 +0200 Subject: [PATCH 421/513] misc: eeprom: Use named initializers for arrays of i2c_device_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. While touching all these arrays, unify usage of whitespace. This patch doesn't modify the compiled arrays, only their representation in source form benefits. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/e9ad2fe1d850ffd64eb053cb03395e7ca6f848dc.1784299069.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/idt_89hpesx.c | 108 +++++++++++++++--------------- drivers/misc/eeprom/max6875.c | 2 +- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index e056d2dea8c3..5bc0294349a0 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -1375,12 +1375,12 @@ static void idt_remove(struct i2c_client *client) * ee_ids - array of supported EEPROMs */ static const struct i2c_device_id ee_ids[] = { - { "24c32", 4096}, - { "24c64", 8192}, - { "24c128", 16384}, - { "24c256", 32768}, - { "24c512", 65536}, - {} + { .name = "24c32", .driver_data = 4096 }, + { .name = "24c64", .driver_data = 8192 }, + { .name = "24c128", .driver_data = 16384 }, + { .name = "24c256", .driver_data = 32768 }, + { .name = "24c512", .driver_data = 65536 }, + { } }; MODULE_DEVICE_TABLE(i2c, ee_ids); @@ -1388,58 +1388,58 @@ MODULE_DEVICE_TABLE(i2c, ee_ids); * idt_ids - supported IDT 89HPESx devices */ static const struct i2c_device_id idt_ids[] = { - { "89hpes8nt2" }, - { "89hpes12nt3" }, + { .name = "89hpes8nt2" }, + { .name = "89hpes12nt3" }, - { "89hpes24nt6ag2" }, - { "89hpes32nt8ag2" }, - { "89hpes32nt8bg2" }, - { "89hpes12nt12g2" }, - { "89hpes16nt16g2" }, - { "89hpes24nt24g2" }, - { "89hpes32nt24ag2" }, - { "89hpes32nt24bg2" }, + { .name = "89hpes24nt6ag2" }, + { .name = "89hpes32nt8ag2" }, + { .name = "89hpes32nt8bg2" }, + { .name = "89hpes12nt12g2" }, + { .name = "89hpes16nt16g2" }, + { .name = "89hpes24nt24g2" }, + { .name = "89hpes32nt24ag2" }, + { .name = "89hpes32nt24bg2" }, - { "89hpes12n3" }, - { "89hpes12n3a" }, - { "89hpes24n3" }, - { "89hpes24n3a" }, + { .name = "89hpes12n3" }, + { .name = "89hpes12n3a" }, + { .name = "89hpes24n3" }, + { .name = "89hpes24n3a" }, - { "89hpes32h8" }, - { "89hpes32h8g2" }, - { "89hpes48h12" }, - { "89hpes48h12g2" }, - { "89hpes48h12ag2" }, - { "89hpes16h16" }, - { "89hpes22h16" }, - { "89hpes22h16g2" }, - { "89hpes34h16" }, - { "89hpes34h16g2" }, - { "89hpes64h16" }, - { "89hpes64h16g2" }, - { "89hpes64h16ag2" }, + { .name = "89hpes32h8" }, + { .name = "89hpes32h8g2" }, + { .name = "89hpes48h12" }, + { .name = "89hpes48h12g2" }, + { .name = "89hpes48h12ag2" }, + { .name = "89hpes16h16" }, + { .name = "89hpes22h16" }, + { .name = "89hpes22h16g2" }, + { .name = "89hpes34h16" }, + { .name = "89hpes34h16g2" }, + { .name = "89hpes64h16" }, + { .name = "89hpes64h16g2" }, + { .name = "89hpes64h16ag2" }, - /* { "89hpes3t3" }, // No SMBus-slave iface */ - { "89hpes12t3g2" }, - { "89hpes24t3g2" }, - /* { "89hpes4t4" }, // No SMBus-slave iface */ - { "89hpes16t4" }, - { "89hpes4t4g2" }, - { "89hpes10t4g2" }, - { "89hpes16t4g2" }, - { "89hpes16t4ag2" }, - { "89hpes5t5" }, - { "89hpes6t5" }, - { "89hpes8t5" }, - { "89hpes8t5a" }, - { "89hpes24t6" }, - { "89hpes6t6g2" }, - { "89hpes24t6g2" }, - { "89hpes16t7" }, - { "89hpes32t8" }, - { "89hpes32t8g2" }, - { "89hpes48t12" }, - { "89hpes48t12g2" }, + /* { .name = "89hpes3t3" }, // No SMBus-slave iface */ + { .name = "89hpes12t3g2" }, + { .name = "89hpes24t3g2" }, + /* { .name = "89hpes4t4" }, // No SMBus-slave iface */ + { .name = "89hpes16t4" }, + { .name = "89hpes4t4g2" }, + { .name = "89hpes10t4g2" }, + { .name = "89hpes16t4g2" }, + { .name = "89hpes16t4ag2" }, + { .name = "89hpes5t5" }, + { .name = "89hpes6t5" }, + { .name = "89hpes8t5" }, + { .name = "89hpes8t5a" }, + { .name = "89hpes24t6" }, + { .name = "89hpes6t6g2" }, + { .name = "89hpes24t6g2" }, + { .name = "89hpes16t7" }, + { .name = "89hpes32t8" }, + { .name = "89hpes32t8g2" }, + { .name = "89hpes48t12" }, + { .name = "89hpes48t12g2" }, { /* END OF LIST */ } }; MODULE_DEVICE_TABLE(i2c, idt_ids); diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c index 5731d2dc8a57..be5e6d19be10 100644 --- a/drivers/misc/eeprom/max6875.c +++ b/drivers/misc/eeprom/max6875.c @@ -183,7 +183,7 @@ static void max6875_remove(struct i2c_client *client) } static const struct i2c_device_id max6875_id[] = { - { "max6875" }, + { .name = "max6875" }, { } }; MODULE_DEVICE_TABLE(i2c, max6875_id); From a4fa3300f02e2c47ccd3ff95b364256782576320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 17 Jul 2026 16:50:51 +0200 Subject: [PATCH 422/513] misc: Use named initializers for arrays of i2c_device_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct i2c_device_id that replaces .driver_data by an anonymous union. While touching all these arrays, unify usage of whitespace. This patch doesn't modify the compiled arrays, only their representation in source form benefits. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/5a4fb64166ab7e06adf94e5fa3d46bb5f6493324.1784299069.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ad525x_dpot-i2c.c | 52 +++++++++++++------------- drivers/misc/amd-sbi/rmi-i2c.c | 4 +- drivers/misc/apds9802als.c | 2 +- drivers/misc/bh1770glc.c | 6 +-- drivers/misc/ds1682.c | 2 +- drivers/misc/hmc6352.c | 2 +- drivers/misc/ics932s401.c | 2 +- drivers/misc/isl29003.c | 4 +- drivers/misc/isl29020.c | 2 +- drivers/misc/lis3lv02d/lis3lv02d_i2c.c | 6 +-- drivers/misc/tsl2550.c | 2 +- 11 files changed, 42 insertions(+), 42 deletions(-) diff --git a/drivers/misc/ad525x_dpot-i2c.c b/drivers/misc/ad525x_dpot-i2c.c index 896ad61bb9e1..a74b0266ea16 100644 --- a/drivers/misc/ad525x_dpot-i2c.c +++ b/drivers/misc/ad525x_dpot-i2c.c @@ -73,32 +73,32 @@ static void ad_dpot_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id ad_dpot_id[] = { - {"ad5258", AD5258_ID}, - {"ad5259", AD5259_ID}, - {"ad5251", AD5251_ID}, - {"ad5252", AD5252_ID}, - {"ad5253", AD5253_ID}, - {"ad5254", AD5254_ID}, - {"ad5255", AD5255_ID}, - {"ad5241", AD5241_ID}, - {"ad5242", AD5242_ID}, - {"ad5243", AD5243_ID}, - {"ad5245", AD5245_ID}, - {"ad5246", AD5246_ID}, - {"ad5247", AD5247_ID}, - {"ad5248", AD5248_ID}, - {"ad5280", AD5280_ID}, - {"ad5282", AD5282_ID}, - {"adn2860", ADN2860_ID}, - {"ad5273", AD5273_ID}, - {"ad5161", AD5161_ID}, - {"ad5171", AD5171_ID}, - {"ad5170", AD5170_ID}, - {"ad5172", AD5172_ID}, - {"ad5173", AD5173_ID}, - {"ad5272", AD5272_ID}, - {"ad5274", AD5274_ID}, - {} + { .name = "ad5258", .driver_data = AD5258_ID }, + { .name = "ad5259", .driver_data = AD5259_ID }, + { .name = "ad5251", .driver_data = AD5251_ID }, + { .name = "ad5252", .driver_data = AD5252_ID }, + { .name = "ad5253", .driver_data = AD5253_ID }, + { .name = "ad5254", .driver_data = AD5254_ID }, + { .name = "ad5255", .driver_data = AD5255_ID }, + { .name = "ad5241", .driver_data = AD5241_ID }, + { .name = "ad5242", .driver_data = AD5242_ID }, + { .name = "ad5243", .driver_data = AD5243_ID }, + { .name = "ad5245", .driver_data = AD5245_ID }, + { .name = "ad5246", .driver_data = AD5246_ID }, + { .name = "ad5247", .driver_data = AD5247_ID }, + { .name = "ad5248", .driver_data = AD5248_ID }, + { .name = "ad5280", .driver_data = AD5280_ID }, + { .name = "ad5282", .driver_data = AD5282_ID }, + { .name = "adn2860", .driver_data = ADN2860_ID }, + { .name = "ad5273", .driver_data = AD5273_ID }, + { .name = "ad5161", .driver_data = AD5161_ID }, + { .name = "ad5171", .driver_data = AD5171_ID }, + { .name = "ad5170", .driver_data = AD5170_ID }, + { .name = "ad5172", .driver_data = AD5172_ID }, + { .name = "ad5173", .driver_data = AD5173_ID }, + { .name = "ad5272", .driver_data = AD5272_ID }, + { .name = "ad5274", .driver_data = AD5274_ID }, + { } }; MODULE_DEVICE_TABLE(i2c, ad_dpot_id); diff --git a/drivers/misc/amd-sbi/rmi-i2c.c b/drivers/misc/amd-sbi/rmi-i2c.c index 37e5ea83bf97..bbc0d14d1cfc 100644 --- a/drivers/misc/amd-sbi/rmi-i2c.c +++ b/drivers/misc/amd-sbi/rmi-i2c.c @@ -141,8 +141,8 @@ static void sbrmi_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id sbrmi_id[] = { - {"sbrmi-i2c"}, - {} + { .name = "sbrmi-i2c" }, + { } }; MODULE_DEVICE_TABLE(i2c, sbrmi_id); diff --git a/drivers/misc/apds9802als.c b/drivers/misc/apds9802als.c index fc504bd8d916..1488e28819b7 100644 --- a/drivers/misc/apds9802als.c +++ b/drivers/misc/apds9802als.c @@ -285,7 +285,7 @@ static UNIVERSAL_DEV_PM_OPS(apds9802als_pm_ops, apds9802als_suspend, #endif /* CONFIG_PM */ static const struct i2c_device_id apds9802als_id[] = { - { DRIVER_NAME }, + { .name = DRIVER_NAME }, { } }; diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c index 45f8fc69a711..98fc5970b2bb 100644 --- a/drivers/misc/bh1770glc.c +++ b/drivers/misc/bh1770glc.c @@ -1363,9 +1363,9 @@ static int bh1770_runtime_resume(struct device *dev) #endif static const struct i2c_device_id bh1770_id[] = { - { "bh1770glc" }, - { "sfh7770" }, - {} + { .name = "bh1770glc" }, + { .name = "sfh7770" }, + { } }; MODULE_DEVICE_TABLE(i2c, bh1770_id); diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c index cb09e056531a..f073a5067fc8 100644 --- a/drivers/misc/ds1682.c +++ b/drivers/misc/ds1682.c @@ -271,7 +271,7 @@ static void ds1682_remove(struct i2c_client *client) } static const struct i2c_device_id ds1682_id[] = { - { "ds1682" }, + { .name = "ds1682" }, { } }; MODULE_DEVICE_TABLE(i2c, ds1682_id); diff --git a/drivers/misc/hmc6352.c b/drivers/misc/hmc6352.c index ff92c6edff6b..275dc054ca04 100644 --- a/drivers/misc/hmc6352.c +++ b/drivers/misc/hmc6352.c @@ -121,7 +121,7 @@ static void hmc6352_remove(struct i2c_client *client) } static const struct i2c_device_id hmc6352_id[] = { - { "hmc6352" }, + { .name = "hmc6352" }, { } }; diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c index 015710762a65..e8bcf0df2da8 100644 --- a/drivers/misc/ics932s401.c +++ b/drivers/misc/ics932s401.c @@ -95,7 +95,7 @@ static int ics932s401_detect(struct i2c_client *client, static void ics932s401_remove(struct i2c_client *client); static const struct i2c_device_id ics932s401_id[] = { - { "ics932s401" }, + { .name = "ics932s401" }, { } }; MODULE_DEVICE_TABLE(i2c, ics932s401_id); diff --git a/drivers/misc/isl29003.c b/drivers/misc/isl29003.c index 95480e16ae5f..c9b526a212d0 100644 --- a/drivers/misc/isl29003.c +++ b/drivers/misc/isl29003.c @@ -449,8 +449,8 @@ static SIMPLE_DEV_PM_OPS(isl29003_pm_ops, isl29003_suspend, isl29003_resume); #endif /* CONFIG_PM_SLEEP */ static const struct i2c_device_id isl29003_id[] = { - { "isl29003" }, - {} + { .name = "isl29003" }, + { } }; MODULE_DEVICE_TABLE(i2c, isl29003_id); diff --git a/drivers/misc/isl29020.c b/drivers/misc/isl29020.c index c288aeec16c0..dd966db28ec4 100644 --- a/drivers/misc/isl29020.c +++ b/drivers/misc/isl29020.c @@ -177,7 +177,7 @@ static void isl29020_remove(struct i2c_client *client) } static const struct i2c_device_id isl29020_id[] = { - { "isl29020" }, + { .name = "isl29020" }, { } }; diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c index 15119584473c..36032fc125f3 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c +++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c @@ -247,9 +247,9 @@ static int lis3_i2c_runtime_resume(struct device *dev) #endif /* CONFIG_PM */ static const struct i2c_device_id lis3lv02d_id[] = { - {"lis3lv02d", LIS3LV02D}, - {"lis331dlh", LIS331DLH}, - {} + { .name = "lis3lv02d", .driver_data = LIS3LV02D }, + { .name = "lis331dlh", .driver_data = LIS331DLH }, + { } }; MODULE_DEVICE_TABLE(i2c, lis3lv02d_id); diff --git a/drivers/misc/tsl2550.c b/drivers/misc/tsl2550.c index 03f19eda641e..c871ea860bb5 100644 --- a/drivers/misc/tsl2550.c +++ b/drivers/misc/tsl2550.c @@ -420,7 +420,7 @@ static SIMPLE_DEV_PM_OPS(tsl2550_pm_ops, tsl2550_suspend, tsl2550_resume); #endif /* CONFIG_PM_SLEEP */ static const struct i2c_device_id tsl2550_id[] = { - { "tsl2550" }, + { .name = "tsl2550" }, { } }; MODULE_DEVICE_TABLE(i2c, tsl2550_id); From 3e97a4e171abe676dd6018ad4aa6170e68b87b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 17 Jul 2026 16:50:52 +0200 Subject: [PATCH 423/513] misc: Use named initializers for arrays of spi_device_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct spi_device_id that replaces .driver_data by an anonymous union. While touching all these arrays, unify usage of whitespace. This patch doesn't modify the compiled arrays, only their representation in source form benefits. Signed-off-by: Uwe Kleine-König Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/ad1f0b3efcc3d9d362b33753734cfb064c079cd3.1784299069.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ad525x_dpot-spi.c | 58 +++++++++++++++--------------- drivers/misc/gehc-achc.c | 4 +-- drivers/misc/keba/lan9252.c | 4 +-- drivers/misc/lattice-ecp3-config.c | 4 +-- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/drivers/misc/ad525x_dpot-spi.c b/drivers/misc/ad525x_dpot-spi.c index 1ebe629715a8..c4c2ec52a83d 100644 --- a/drivers/misc/ad525x_dpot-spi.c +++ b/drivers/misc/ad525x_dpot-spi.c @@ -96,35 +96,35 @@ static void ad_dpot_spi_remove(struct spi_device *spi) } static const struct spi_device_id ad_dpot_spi_id[] = { - {"ad5160", AD5160_ID}, - {"ad5161", AD5161_ID}, - {"ad5162", AD5162_ID}, - {"ad5165", AD5165_ID}, - {"ad5200", AD5200_ID}, - {"ad5201", AD5201_ID}, - {"ad5203", AD5203_ID}, - {"ad5204", AD5204_ID}, - {"ad5206", AD5206_ID}, - {"ad5207", AD5207_ID}, - {"ad5231", AD5231_ID}, - {"ad5232", AD5232_ID}, - {"ad5233", AD5233_ID}, - {"ad5235", AD5235_ID}, - {"ad5260", AD5260_ID}, - {"ad5262", AD5262_ID}, - {"ad5263", AD5263_ID}, - {"ad5290", AD5290_ID}, - {"ad5291", AD5291_ID}, - {"ad5292", AD5292_ID}, - {"ad5293", AD5293_ID}, - {"ad7376", AD7376_ID}, - {"ad8400", AD8400_ID}, - {"ad8402", AD8402_ID}, - {"ad8403", AD8403_ID}, - {"adn2850", ADN2850_ID}, - {"ad5270", AD5270_ID}, - {"ad5271", AD5271_ID}, - {} + { .name = "ad5160", .driver_data = AD5160_ID }, + { .name = "ad5161", .driver_data = AD5161_ID }, + { .name = "ad5162", .driver_data = AD5162_ID }, + { .name = "ad5165", .driver_data = AD5165_ID }, + { .name = "ad5200", .driver_data = AD5200_ID }, + { .name = "ad5201", .driver_data = AD5201_ID }, + { .name = "ad5203", .driver_data = AD5203_ID }, + { .name = "ad5204", .driver_data = AD5204_ID }, + { .name = "ad5206", .driver_data = AD5206_ID }, + { .name = "ad5207", .driver_data = AD5207_ID }, + { .name = "ad5231", .driver_data = AD5231_ID }, + { .name = "ad5232", .driver_data = AD5232_ID }, + { .name = "ad5233", .driver_data = AD5233_ID }, + { .name = "ad5235", .driver_data = AD5235_ID }, + { .name = "ad5260", .driver_data = AD5260_ID }, + { .name = "ad5262", .driver_data = AD5262_ID }, + { .name = "ad5263", .driver_data = AD5263_ID }, + { .name = "ad5290", .driver_data = AD5290_ID }, + { .name = "ad5291", .driver_data = AD5291_ID }, + { .name = "ad5292", .driver_data = AD5292_ID }, + { .name = "ad5293", .driver_data = AD5293_ID }, + { .name = "ad7376", .driver_data = AD7376_ID }, + { .name = "ad8400", .driver_data = AD8400_ID }, + { .name = "ad8402", .driver_data = AD8402_ID }, + { .name = "ad8403", .driver_data = AD8403_ID }, + { .name = "adn2850", .driver_data = ADN2850_ID }, + { .name = "ad5270", .driver_data = AD5270_ID }, + { .name = "ad5271", .driver_data = AD5271_ID }, + { } }; MODULE_DEVICE_TABLE(spi, ad_dpot_spi_id); diff --git a/drivers/misc/gehc-achc.c b/drivers/misc/gehc-achc.c index 1abfa6c257de..9792189c0923 100644 --- a/drivers/misc/gehc-achc.c +++ b/drivers/misc/gehc-achc.c @@ -538,8 +538,8 @@ static int gehc_achc_probe(struct spi_device *spi) } static const struct spi_device_id gehc_achc_id[] = { - { "ge,achc" }, - { "achc" }, + { .name = "ge,achc" }, + { .name = "achc" }, { } }; MODULE_DEVICE_TABLE(spi, gehc_achc_id); diff --git a/drivers/misc/keba/lan9252.c b/drivers/misc/keba/lan9252.c index fc54afd1d05b..874dd50fac66 100644 --- a/drivers/misc/keba/lan9252.c +++ b/drivers/misc/keba/lan9252.c @@ -339,8 +339,8 @@ static int lan9252_probe(struct spi_device *spi) } static const struct spi_device_id lan9252_id[] = { - {"lan9252"}, - {} + { .name = "lan9252" }, + { } }; MODULE_DEVICE_TABLE(spi, lan9252_id); diff --git a/drivers/misc/lattice-ecp3-config.c b/drivers/misc/lattice-ecp3-config.c index 2b93d5660bf2..740b5cf53842 100644 --- a/drivers/misc/lattice-ecp3-config.c +++ b/drivers/misc/lattice-ecp3-config.c @@ -219,8 +219,8 @@ static void lattice_ecp3_remove(struct spi_device *spi) } static const struct spi_device_id lattice_ecp3_id[] = { - { "ecp3-17" }, - { "ecp3-35" }, + { .name = "ecp3-17" }, + { .name = "ecp3-35" }, { } }; MODULE_DEVICE_TABLE(spi, lattice_ecp3_id); From 3cd092ac5508993dc98bcf7ae32aea578cae9640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 17 Jul 2026 16:50:53 +0200 Subject: [PATCH 424/513] misc: mei: Use named initializers for acpi_device_id array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. While touching this array, add a space to the list terminator to make it match the most common usage. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/c189ce8aeae288e9d8bcdfff150957c6f3e6e37d.1784299069.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/vsc-tp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/misc/mei/vsc-tp.c b/drivers/misc/mei/vsc-tp.c index 5ecf99883996..432bc59f11f9 100644 --- a/drivers/misc/mei/vsc-tp.c +++ b/drivers/misc/mei/vsc-tp.c @@ -552,11 +552,11 @@ static void vsc_tp_remove(struct spi_device *spi) } static const struct acpi_device_id vsc_tp_acpi_ids[] = { - { "INTC1009" }, /* Raptor Lake */ - { "INTC1058" }, /* Tiger Lake */ - { "INTC1094" }, /* Alder Lake */ - { "INTC10D0" }, /* Meteor Lake */ - {} + { .id = "INTC1009" }, /* Raptor Lake */ + { .id = "INTC1058" }, /* Tiger Lake */ + { .id = "INTC1094" }, /* Alder Lake */ + { .id = "INTC10D0" }, /* Meteor Lake */ + { } }; MODULE_DEVICE_TABLE(acpi, vsc_tp_acpi_ids); From 14e8c32dbc6b0b60f199074a887c4298890b9fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 17 Jul 2026 16:50:54 +0200 Subject: [PATCH 425/513] misc: mei: Use named initializer for platform_device_id array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct platform_device_id that replaces .driver_data by an anonymous union (though this driver doesn't use .driver_data, so this patch isn't strictly necessary for the plan, but still ok for consistency). This patch doesn't modify the compiled array, only its representation in source form benefits. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/9c4a5a5b086d4ce913e84dfa7d140a8b492d557b.1784299069.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/platform-vsc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mei/platform-vsc.c b/drivers/misc/mei/platform-vsc.c index 5100234ba5b6..ef6dcc043862 100644 --- a/drivers/misc/mei/platform-vsc.c +++ b/drivers/misc/mei/platform-vsc.c @@ -435,7 +435,7 @@ static int mei_vsc_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(mei_vsc_pm_ops, mei_vsc_suspend, mei_vsc_resume); static const struct platform_device_id mei_vsc_id_table[] = { - { MEI_VSC_DRV_NAME }, + { .name = MEI_VSC_DRV_NAME }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, mei_vsc_id_table); From a503587751651b23461646c91f7ee6d1d3cd8ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 17 Jul 2026 16:50:55 +0200 Subject: [PATCH 426/513] misc: ibmvmc: Use named initializers for vio_device_id array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. While touching this array, also simplify the list terminator. This change doesn't introduce differences to the compiled result. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/a451863ca59ab700b6e65b76a17a8b6eb1431e6b.1784299069.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ibmvmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/ibmvmc.c b/drivers/misc/ibmvmc.c index 28bf4c352317..da82b1edb7c9 100644 --- a/drivers/misc/ibmvmc.c +++ b/drivers/misc/ibmvmc.c @@ -2311,8 +2311,8 @@ static void ibmvmc_remove(struct vio_dev *vdev) } static struct vio_device_id ibmvmc_device_table[] = { - { "ibm,vmc", "IBM,vmc" }, - { "", "" } + { .type = "ibm,vmc", .compat = "IBM,vmc" }, + { } }; MODULE_DEVICE_TABLE(vio, ibmvmc_device_table); From 50b712469c068a960e3b5cb24e5a007852824847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 17 Jul 2026 16:50:56 +0200 Subject: [PATCH 427/513] misc: eeprom: idt_89hpesx: Unify style of of_device_id array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the of_device_id array to conform to the most used and generally recommended coding style. That is: - No comma after the list terminator; - A comma after an initializer if (and only if) the closing } is not directly following; - No explicit zeros in the list terminator; - A space after an opening { and before a closing }, a single space in the list terminator; Adapt the driver's array accordingly. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/2c35ea101e5eb28334137d9341a8b1e564102ae5.1784299069.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/idt_89hpesx.c | 94 +++++++++++++++---------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index 5bc0294349a0..ea7478cfdd27 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -1445,58 +1445,58 @@ static const struct i2c_device_id idt_ids[] = { MODULE_DEVICE_TABLE(i2c, idt_ids); static const struct of_device_id idt_of_match[] = { - { .compatible = "idt,89hpes8nt2", }, - { .compatible = "idt,89hpes12nt3", }, + { .compatible = "idt,89hpes8nt2" }, + { .compatible = "idt,89hpes12nt3" }, - { .compatible = "idt,89hpes24nt6ag2", }, - { .compatible = "idt,89hpes32nt8ag2", }, - { .compatible = "idt,89hpes32nt8bg2", }, - { .compatible = "idt,89hpes12nt12g2", }, - { .compatible = "idt,89hpes16nt16g2", }, - { .compatible = "idt,89hpes24nt24g2", }, - { .compatible = "idt,89hpes32nt24ag2", }, - { .compatible = "idt,89hpes32nt24bg2", }, + { .compatible = "idt,89hpes24nt6ag2" }, + { .compatible = "idt,89hpes32nt8ag2" }, + { .compatible = "idt,89hpes32nt8bg2" }, + { .compatible = "idt,89hpes12nt12g2" }, + { .compatible = "idt,89hpes16nt16g2" }, + { .compatible = "idt,89hpes24nt24g2" }, + { .compatible = "idt,89hpes32nt24ag2" }, + { .compatible = "idt,89hpes32nt24bg2" }, - { .compatible = "idt,89hpes12n3", }, - { .compatible = "idt,89hpes12n3a", }, - { .compatible = "idt,89hpes24n3", }, - { .compatible = "idt,89hpes24n3a", }, + { .compatible = "idt,89hpes12n3" }, + { .compatible = "idt,89hpes12n3a" }, + { .compatible = "idt,89hpes24n3" }, + { .compatible = "idt,89hpes24n3a" }, - { .compatible = "idt,89hpes32h8", }, - { .compatible = "idt,89hpes32h8g2", }, - { .compatible = "idt,89hpes48h12", }, - { .compatible = "idt,89hpes48h12g2", }, - { .compatible = "idt,89hpes48h12ag2", }, - { .compatible = "idt,89hpes16h16", }, - { .compatible = "idt,89hpes22h16", }, - { .compatible = "idt,89hpes22h16g2", }, - { .compatible = "idt,89hpes34h16", }, - { .compatible = "idt,89hpes34h16g2", }, - { .compatible = "idt,89hpes64h16", }, - { .compatible = "idt,89hpes64h16g2", }, - { .compatible = "idt,89hpes64h16ag2", }, + { .compatible = "idt,89hpes32h8" }, + { .compatible = "idt,89hpes32h8g2" }, + { .compatible = "idt,89hpes48h12" }, + { .compatible = "idt,89hpes48h12g2" }, + { .compatible = "idt,89hpes48h12ag2" }, + { .compatible = "idt,89hpes16h16" }, + { .compatible = "idt,89hpes22h16" }, + { .compatible = "idt,89hpes22h16g2" }, + { .compatible = "idt,89hpes34h16" }, + { .compatible = "idt,89hpes34h16g2" }, + { .compatible = "idt,89hpes64h16" }, + { .compatible = "idt,89hpes64h16g2" }, + { .compatible = "idt,89hpes64h16ag2" }, - { .compatible = "idt,89hpes12t3g2", }, - { .compatible = "idt,89hpes24t3g2", }, + { .compatible = "idt,89hpes12t3g2" }, + { .compatible = "idt,89hpes24t3g2" }, - { .compatible = "idt,89hpes16t4", }, - { .compatible = "idt,89hpes4t4g2", }, - { .compatible = "idt,89hpes10t4g2", }, - { .compatible = "idt,89hpes16t4g2", }, - { .compatible = "idt,89hpes16t4ag2", }, - { .compatible = "idt,89hpes5t5", }, - { .compatible = "idt,89hpes6t5", }, - { .compatible = "idt,89hpes8t5", }, - { .compatible = "idt,89hpes8t5a", }, - { .compatible = "idt,89hpes24t6", }, - { .compatible = "idt,89hpes6t6g2", }, - { .compatible = "idt,89hpes24t6g2", }, - { .compatible = "idt,89hpes16t7", }, - { .compatible = "idt,89hpes32t8", }, - { .compatible = "idt,89hpes32t8g2", }, - { .compatible = "idt,89hpes48t12", }, - { .compatible = "idt,89hpes48t12g2", }, - { }, + { .compatible = "idt,89hpes16t4" }, + { .compatible = "idt,89hpes4t4g2" }, + { .compatible = "idt,89hpes10t4g2" }, + { .compatible = "idt,89hpes16t4g2" }, + { .compatible = "idt,89hpes16t4ag2" }, + { .compatible = "idt,89hpes5t5" }, + { .compatible = "idt,89hpes6t5" }, + { .compatible = "idt,89hpes8t5" }, + { .compatible = "idt,89hpes8t5a" }, + { .compatible = "idt,89hpes24t6" }, + { .compatible = "idt,89hpes6t6g2" }, + { .compatible = "idt,89hpes24t6g2" }, + { .compatible = "idt,89hpes16t7" }, + { .compatible = "idt,89hpes32t8" }, + { .compatible = "idt,89hpes32t8g2" }, + { .compatible = "idt,89hpes48t12" }, + { .compatible = "idt,89hpes48t12g2" }, + { } }; MODULE_DEVICE_TABLE(of, idt_of_match); From 6658673030d3c90038ddac0ec6ec40e2cc889ded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 17 Jul 2026 16:50:57 +0200 Subject: [PATCH 428/513] misc: Unify style of of_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the various of_device_id arrays to conform to the most used and generally recommended coding style. That is: - No comma after the list terminator; - A comma after an initializer if (and only if) the closing } is not directly following; - No explicit zeros in the list terminator; - A space after an opening { and before a closing }, a single space in the list terminator; Adapt the few offenders accordingly. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Romain Gantois Link: https://patch.msgid.link/fbf51b8f413c516977e2c8a81896221795df77e4.1784299069.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ds1682.c | 4 ++-- drivers/misc/fastrpc.c | 6 +++--- drivers/misc/hisi_hikey_usb.c | 2 +- drivers/misc/lis3lv02d/lis3lv02d_i2c.c | 2 +- drivers/misc/lis3lv02d/lis3lv02d_spi.c | 2 +- drivers/misc/open-dice.c | 2 +- drivers/misc/pvpanic/pvpanic-mmio.c | 4 ++-- drivers/misc/qcom-coincell.c | 4 ++-- drivers/misc/sram.c | 2 +- drivers/misc/ti_fpc202.c | 2 +- drivers/misc/vcpu_stall_detector.c | 4 ++-- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c index f073a5067fc8..4500d7d0ea77 100644 --- a/drivers/misc/ds1682.c +++ b/drivers/misc/ds1682.c @@ -277,8 +277,8 @@ static const struct i2c_device_id ds1682_id[] = { MODULE_DEVICE_TABLE(i2c, ds1682_id); static const struct of_device_id ds1682_of_match[] = { - { .compatible = "dallas,ds1682", }, - {} + { .compatible = "dallas,ds1682" }, + { } }; MODULE_DEVICE_TABLE(of, ds1682_of_match); diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 65f2154e3a1d..3e088a5231dd 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -2432,8 +2432,8 @@ static void fastrpc_cb_remove(struct platform_device *pdev) } static const struct of_device_id fastrpc_match_table[] = { - { .compatible = "qcom,fastrpc-compute-cb", }, - {} + { .compatible = "qcom,fastrpc-compute-cb" }, + { } }; static struct platform_driver fastrpc_cb_driver = { @@ -2765,7 +2765,7 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data, static const struct of_device_id fastrpc_rpmsg_of_match[] = { { .compatible = "qcom,kaanapali-fastrpc", .data = &kaanapali_soc_data }, { .compatible = "qcom,fastrpc", .data = &default_soc_data }, - { }, + { } }; MODULE_DEVICE_TABLE(of, fastrpc_rpmsg_of_match); diff --git a/drivers/misc/hisi_hikey_usb.c b/drivers/misc/hisi_hikey_usb.c index 79f06001259b..e68bc6e384d5 100644 --- a/drivers/misc/hisi_hikey_usb.c +++ b/drivers/misc/hisi_hikey_usb.c @@ -254,7 +254,7 @@ static void hisi_hikey_usb_remove(struct platform_device *pdev) static const struct of_device_id id_table_hisi_hikey_usb[] = { { .compatible = "hisilicon,usbhub" }, - {} + { } }; MODULE_DEVICE_TABLE(of, id_table_hisi_hikey_usb); diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c index 36032fc125f3..7c3ca6d604d1 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c +++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c @@ -95,7 +95,7 @@ static union axis_conversion lis3lv02d_axis_map = #ifdef CONFIG_OF static const struct of_device_id lis3lv02d_i2c_dt_ids[] = { { .compatible = "st,lis3lv02d" }, - {} + { } }; MODULE_DEVICE_TABLE(of, lis3lv02d_i2c_dt_ids); #endif diff --git a/drivers/misc/lis3lv02d/lis3lv02d_spi.c b/drivers/misc/lis3lv02d/lis3lv02d_spi.c index 203a108b8883..5bf95176a00e 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d_spi.c +++ b/drivers/misc/lis3lv02d/lis3lv02d_spi.c @@ -60,7 +60,7 @@ static union axis_conversion lis3lv02d_axis_normal = #ifdef CONFIG_OF static const struct of_device_id lis302dl_spi_dt_ids[] = { { .compatible = "st,lis302dl-spi" }, - {} + { } }; MODULE_DEVICE_TABLE(of, lis302dl_spi_dt_ids); #endif diff --git a/drivers/misc/open-dice.c b/drivers/misc/open-dice.c index 303b35b03cb4..b3cf59954111 100644 --- a/drivers/misc/open-dice.c +++ b/drivers/misc/open-dice.c @@ -199,7 +199,7 @@ static void open_dice_remove(struct platform_device *pdev) static const struct of_device_id open_dice_of_match[] = { { .compatible = "google,open-dice" }, - {}, + { } }; static struct platform_driver open_dice_driver = { diff --git a/drivers/misc/pvpanic/pvpanic-mmio.c b/drivers/misc/pvpanic/pvpanic-mmio.c index 43a094bfdaec..724dfddf69e4 100644 --- a/drivers/misc/pvpanic/pvpanic-mmio.c +++ b/drivers/misc/pvpanic/pvpanic-mmio.c @@ -51,8 +51,8 @@ static int pvpanic_mmio_probe(struct platform_device *pdev) } static const struct of_device_id pvpanic_mmio_match[] = { - { .compatible = "qemu,pvpanic-mmio", }, - {} + { .compatible = "qemu,pvpanic-mmio" }, + { } }; MODULE_DEVICE_TABLE(of, pvpanic_mmio_match); diff --git a/drivers/misc/qcom-coincell.c b/drivers/misc/qcom-coincell.c index 3c57f7429147..b58bd34fc471 100644 --- a/drivers/misc/qcom-coincell.c +++ b/drivers/misc/qcom-coincell.c @@ -124,8 +124,8 @@ static int qcom_coincell_probe(struct platform_device *pdev) } static const struct of_device_id qcom_coincell_match_table[] = { - { .compatible = "qcom,pm8941-coincell", }, - {} + { .compatible = "qcom,pm8941-coincell" }, + { } }; MODULE_DEVICE_TABLE(of, qcom_coincell_match_table); diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index 1919d24c8236..c8990e83a65a 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -370,7 +370,7 @@ static const struct of_device_id sram_dt_ids[] = { { .compatible = "nvidia,tegra186-sysram", .data = &tegra_sysram_config }, { .compatible = "nvidia,tegra194-sysram", .data = &tegra_sysram_config }, { .compatible = "nvidia,tegra234-sysram", .data = &tegra_sysram_config }, - {} + { } }; static int sram_probe(struct platform_device *pdev) diff --git a/drivers/misc/ti_fpc202.c b/drivers/misc/ti_fpc202.c index 2aac83ec4a39..9e2d24f89457 100644 --- a/drivers/misc/ti_fpc202.c +++ b/drivers/misc/ti_fpc202.c @@ -718,7 +718,7 @@ static void fpc202_remove(struct i2c_client *client) static const struct of_device_id fpc202_of_match[] = { { .compatible = "ti,fpc202" }, - {} + { } }; MODULE_DEVICE_TABLE(of, fpc202_of_match); diff --git a/drivers/misc/vcpu_stall_detector.c b/drivers/misc/vcpu_stall_detector.c index 26166357b255..1306b2c1ef99 100644 --- a/drivers/misc/vcpu_stall_detector.c +++ b/drivers/misc/vcpu_stall_detector.c @@ -224,8 +224,8 @@ static void vcpu_stall_detect_remove(struct platform_device *pdev) } static const struct of_device_id vcpu_stall_detect_of_match[] = { - { .compatible = "qemu,vcpu-stall-detector", }, - {} + { .compatible = "qemu,vcpu-stall-detector" }, + { } }; MODULE_DEVICE_TABLE(of, vcpu_stall_detect_of_match); From f06bae4ddc70db7ccd3c434b4ef8f40b478ae955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 17 Jul 2026 16:50:58 +0200 Subject: [PATCH 429/513] misc: Unify code style for various *_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add a trailing comma for initializers unless the closing brace is on the same line and for the list terminator; - Use a single space in the list terminator; - A space after an opening { and before a closing }; - Use the PCI_DEVICE* macro in two drivers for pci_device_id entry which is usual for these arrays - No explicit zeros in the list terminator; None of these changes introduces changes to the compiled result. Signed-off-by: Uwe Kleine-König (The Capable Hub) Acked-by: Michał Mirosław Link: https://patch.msgid.link/583653bc7b9ae19707bc28be69a20e66b8c3720a.1784299069.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/amd-sbi/rmi-i2c.c | 2 +- drivers/misc/bcm-vk/bcm_vk_dev.c | 2 +- drivers/misc/cardreader/alcor_pci.c | 2 +- drivers/misc/cb710/core.c | 5 +- drivers/misc/hi6421v600-irq.c | 2 +- drivers/misc/hpilo.c | 6 +- drivers/misc/ibmasm/module.c | 2 +- drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c | 2 +- .../misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c | 4 +- .../misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c | 4 +- drivers/misc/mei/pci-csc.c | 2 +- drivers/misc/mei/pci-me.c | 166 +++++++++--------- drivers/misc/mei/pci-txe.c | 6 +- drivers/misc/mrvl_cn10k_dpi.c | 2 +- drivers/misc/nsm.c | 2 +- drivers/misc/ocxl/pci.c | 2 +- drivers/misc/pci_endpoint_test.c | 16 +- drivers/misc/phantom.c | 6 +- drivers/misc/pvpanic/pvpanic-pci.c | 4 +- drivers/misc/rp1/rp1_pci.c | 2 +- drivers/misc/vmw_vmci/vmci_guest.c | 4 +- 21 files changed, 121 insertions(+), 122 deletions(-) diff --git a/drivers/misc/amd-sbi/rmi-i2c.c b/drivers/misc/amd-sbi/rmi-i2c.c index bbc0d14d1cfc..463a9a757464 100644 --- a/drivers/misc/amd-sbi/rmi-i2c.c +++ b/drivers/misc/amd-sbi/rmi-i2c.c @@ -226,7 +226,7 @@ static const struct i3c_device_id sbrmi_i3c_id[] = { 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); diff --git a/drivers/misc/bcm-vk/bcm_vk_dev.c b/drivers/misc/bcm-vk/bcm_vk_dev.c index 54b40b4d556d..88ee3a417616 100644 --- a/drivers/misc/bcm-vk/bcm_vk_dev.c +++ b/drivers/misc/bcm-vk/bcm_vk_dev.c @@ -1623,7 +1623,7 @@ static void bcm_vk_shutdown(struct pci_dev *pdev) } static const struct pci_device_id bcm_vk_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_VALKYRIE), }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_VALKYRIE) }, { } }; MODULE_DEVICE_TABLE(pci, bcm_vk_ids); diff --git a/drivers/misc/cardreader/alcor_pci.c b/drivers/misc/cardreader/alcor_pci.c index 8e7ea2c9142d..9e0f4f91763e 100644 --- a/drivers/misc/cardreader/alcor_pci.c +++ b/drivers/misc/cardreader/alcor_pci.c @@ -47,7 +47,7 @@ static const struct pci_device_id pci_ids[] = { .driver_data = (kernel_ulong_t)&au6621_cfg }, { PCI_DEVICE(PCI_ID_ALCOR_MICRO, PCI_ID_AU6625), .driver_data = (kernel_ulong_t)&au6625_cfg }, - {}, + { } }; MODULE_DEVICE_TABLE(pci, pci_ids); diff --git a/drivers/misc/cb710/core.c b/drivers/misc/cb710/core.c index 2dd212f04fed..8e0d11e59c4e 100644 --- a/drivers/misc/cb710/core.c +++ b/drivers/misc/cb710/core.c @@ -292,9 +292,8 @@ static void cb710_remove_one(struct pci_dev *pdev) } static const struct pci_device_id cb710_pci_tbl[] = { - { PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_CB710_FLASH, - PCI_ANY_ID, PCI_ANY_ID, }, - { 0, } + { PCI_DEVICE(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_CB710_FLASH) }, + { } }; static SIMPLE_DEV_PM_OPS(cb710_pm_ops, cb710_suspend, cb710_resume); diff --git a/drivers/misc/hi6421v600-irq.c b/drivers/misc/hi6421v600-irq.c index 5f65ca91b342..033f30014aa6 100644 --- a/drivers/misc/hi6421v600-irq.c +++ b/drivers/misc/hi6421v600-irq.c @@ -282,7 +282,7 @@ static int hi6421v600_irq_probe(struct platform_device *pdev) static const struct platform_device_id hi6421v600_irq_table[] = { { .name = "hi6421v600-irq" }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, hi6421v600_irq_table); diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index 9b54a81b43d8..8988858c2f31 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -33,10 +33,10 @@ static unsigned int max_ccb = 16; static char ilo_hwdev[MAX_ILO_DEV]; static const struct pci_device_id ilo_blacklist[] = { /* auxiliary iLO */ - {PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP, 0x1979)}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP, 0x1979) }, /* CL */ - {PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP_3PAR, 0x0289)}, - {} + { PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP_3PAR, 0x0289) }, + { } }; static inline int get_entry_id(int entry) diff --git a/drivers/misc/ibmasm/module.c b/drivers/misc/ibmasm/module.c index 4509c15a76a8..97f661f54b31 100644 --- a/drivers/misc/ibmasm/module.c +++ b/drivers/misc/ibmasm/module.c @@ -178,7 +178,7 @@ static void ibmasm_remove_one(struct pci_dev *pdev) static struct pci_device_id ibmasm_pci_table[] = { { PCI_DEVICE(VENDORID_IBM, DEVICEID_RSA) }, - {}, + { } }; static struct pci_driver ibmasm_driver = { diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c index 48bea1117771..b0b678e6b28d 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c @@ -150,7 +150,7 @@ static const struct pci_device_id pci1xxxx_tbl[] = { { PCI_DEVICE(0x1055, 0xA035) }, { PCI_DEVICE(0x1055, 0xA045) }, { PCI_DEVICE(0x1055, 0xA055) }, - {0,} + { } }; MODULE_DEVICE_TABLE(pci, pci1xxxx_tbl); diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c index 8eddbaa1fccd..82635db04dc5 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c @@ -513,8 +513,8 @@ static int pci1xxxx_gpio_probe(struct auxiliary_device *aux_dev, static DEFINE_SIMPLE_DEV_PM_OPS(pci1xxxx_gpio_pm_ops, pci1xxxx_gpio_suspend, pci1xxxx_gpio_resume); static const struct auxiliary_device_id pci1xxxx_gpio_auxiliary_id_table[] = { - {.name = "mchp_pci1xxxx_gp.gp_gpio"}, - {} + { .name = "mchp_pci1xxxx_gp.gp_gpio" }, + { } }; MODULE_DEVICE_TABLE(auxiliary, pci1xxxx_gpio_auxiliary_id_table); diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c index a2ed477e0370..09841c4f1a06 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c @@ -419,8 +419,8 @@ static void pci1xxxx_otp_eeprom_remove(struct auxiliary_device *aux_dev) } static const struct auxiliary_device_id pci1xxxx_otp_eeprom_auxiliary_id_table[] = { - {.name = "mchp_pci1xxxx_gp.gp_otp_e2p"}, - {}, + { .name = "mchp_pci1xxxx_gp.gp_otp_e2p" }, + { } }; MODULE_DEVICE_TABLE(auxiliary, pci1xxxx_otp_eeprom_auxiliary_id_table); diff --git a/drivers/misc/mei/pci-csc.c b/drivers/misc/mei/pci-csc.c index 70792bf9b3c0..6e1e8008f267 100644 --- a/drivers/misc/mei/pci-csc.c +++ b/drivers/misc/mei/pci-csc.c @@ -240,7 +240,7 @@ static const struct dev_pm_ops mei_csc_pm_ops = { 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); diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 55e0b8a98827..790a4647811b 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -27,114 +27,114 @@ /* mei_pci_tbl - PCI Device ID Table */ static const struct pci_device_id mei_me_pci_tbl[] = { - {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)}, + { 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) }, - {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)}, + { 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) }, - {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)}, + { 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) }, - {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)}, + { 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) }, - {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)}, + { 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) }, - {PCI_DEVICE_DATA(INTEL, MEI_BXT_M, MEI_ME_PCH8_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_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) }, - {PCI_DEVICE_DATA(INTEL, MEI_DNV_IE, MEI_ME_PCH8_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_DNV_IE, MEI_ME_PCH8_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_GLK, MEI_ME_PCH8_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_GLK, 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)}, + { 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) }, - {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)}, + { 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) }, - {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)}, + { 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) }, - {PCI_DEVICE_DATA(INTEL, MEI_ICP_LP, MEI_ME_PCH12_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_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) }, - {PCI_DEVICE_DATA(INTEL, MEI_TGP_LP, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_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) }, - {PCI_DEVICE_DATA(INTEL, MEI_JSP_N, MEI_ME_PCH15_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_JSP_N, MEI_ME_PCH15_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_MCC, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_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) }, - {PCI_DEVICE_DATA(INTEL, MEI_CDF, MEI_ME_PCH8_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_CDF, MEI_ME_PCH8_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_EBG, MEI_ME_PCH15_SPS_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_EBG, MEI_ME_PCH15_SPS_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)}, + { 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) }, - {PCI_DEVICE_DATA(INTEL, MEI_RPL_S, MEI_ME_PCH15_SPS_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_RPL_S, MEI_ME_PCH15_SPS_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)}, + { 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) }, - {PCI_DEVICE_DATA(INTEL, MEI_LNL_M, MEI_ME_PCH15_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_LNL_M, 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)}, + { PCI_DEVICE_DATA(INTEL, MEI_PTL_H, MEI_ME_PCH15_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_PTL_P, MEI_ME_PCH15_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_WCL_P, MEI_ME_PCH15_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_WCL_P, MEI_ME_PCH15_CFG) }, - {PCI_DEVICE_DATA(INTEL, MEI_NVL_S, MEI_ME_PCH22_IOE_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_NVL_H, MEI_ME_PCH15_CFG)}, + { PCI_DEVICE_DATA(INTEL, MEI_NVL_S, MEI_ME_PCH22_IOE_CFG) }, + { PCI_DEVICE_DATA(INTEL, MEI_NVL_H, MEI_ME_PCH15_CFG) }, /* required last entry */ - {0, } + { } }; MODULE_DEVICE_TABLE(pci, mei_me_pci_tbl); diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index f5441bc5efe2..815ca72877ce 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -24,10 +24,10 @@ #include "hw-txe.h" static const struct pci_device_id mei_txe_pci_tbl[] = { - {PCI_VDEVICE(INTEL, 0x0F18)}, /* Baytrail */ - {PCI_VDEVICE(INTEL, 0x2298)}, /* Cherrytrail */ + { PCI_VDEVICE(INTEL, 0x0F18) }, /* Baytrail */ + { PCI_VDEVICE(INTEL, 0x2298) }, /* Cherrytrail */ - {0, } + { } }; MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl); diff --git a/drivers/misc/mrvl_cn10k_dpi.c b/drivers/misc/mrvl_cn10k_dpi.c index d26c53af05eb..73ff0f198ec3 100644 --- a/drivers/misc/mrvl_cn10k_dpi.c +++ b/drivers/misc/mrvl_cn10k_dpi.c @@ -656,7 +656,7 @@ static void dpi_remove(struct pci_dev *pdev) static const struct pci_device_id dpi_id_table[] = { { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_MRVL_CN10K_DPI_PF, PCI_VENDOR_ID_CAVIUM, PCI_SUBDEVID_MRVL_CN10K_DPI_PF) }, - { 0, } /* end of table */ + { } /* end of table */ }; static struct pci_driver dpi_driver = { diff --git a/drivers/misc/nsm.c b/drivers/misc/nsm.c index 7d691f841deb..a3a9f9bfc6b7 100644 --- a/drivers/misc/nsm.c +++ b/drivers/misc/nsm.c @@ -61,7 +61,7 @@ struct nsm { /* NSM device ID */ static const struct virtio_device_id id_table[] = { { VIRTIO_ID_NITRO_SEC_MOD, VIRTIO_DEV_ANY_ID }, - { 0 }, + { } }; static struct nsm *file_to_nsm(struct file *file) diff --git a/drivers/misc/ocxl/pci.c b/drivers/misc/ocxl/pci.c index cb920aa88d3a..0cf38bd482a9 100644 --- a/drivers/misc/ocxl/pci.c +++ b/drivers/misc/ocxl/pci.c @@ -9,7 +9,7 @@ * vendor/device ID to help differentiate devices. */ static const struct pci_device_id ocxl_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x062B), }, + { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x062B) }, { } }; MODULE_DEVICE_TABLE(pci, ocxl_pci_tbl); diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 3635741c3e7a..a10fce77e0b4 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -1416,18 +1416,18 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x81c0), .driver_data = (kernel_ulong_t)&default_data, }, - { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_IMX8),}, + { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_IMX8) }, { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_LS1088A), .driver_data = (kernel_ulong_t)&default_data, }, { PCI_DEVICE_DATA(SYNOPSYS, EDDA, NULL) }, { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_AM654), - .driver_data = (kernel_ulong_t)&am654_data + .driver_data = (kernel_ulong_t)&am654_data, }, - { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774A1),}, - { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774B1),}, - { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774C0),}, - { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774E1),}, + { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774A1) }, + { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774B1) }, + { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774C0) }, + { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774E1) }, { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A779F0), .driver_data = (kernel_ulong_t)&default_data, }, @@ -1446,8 +1446,8 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_ROCKCHIP, PCI_DEVICE_ID_ROCKCHIP_RK3588), .driver_data = (kernel_ulong_t)&rk3588_data, }, - { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA194_EP),}, - { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA234_EP),}, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA194_EP) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA234_EP) }, { } }; MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl); diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index 34a5054a6b40..331cae539290 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c @@ -481,10 +481,10 @@ static int __maybe_unused phantom_resume(struct device *dev_d) } static struct pci_device_id phantom_pci_tbl[] = { - { .vendor = PCI_VENDOR_ID_PLX, .device = PCI_DEVICE_ID_PLX_9050, - .subvendor = PCI_VENDOR_ID_PLX, .subdevice = PCI_DEVICE_ID_PLX_9050, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050), .class = PCI_CLASS_BRIDGE_OTHER << 8, .class_mask = 0xffff00 }, - { 0, } + { } }; MODULE_DEVICE_TABLE(pci, phantom_pci_tbl); diff --git a/drivers/misc/pvpanic/pvpanic-pci.c b/drivers/misc/pvpanic/pvpanic-pci.c index b21598a18f6d..572c6148a297 100644 --- a/drivers/misc/pvpanic/pvpanic-pci.c +++ b/drivers/misc/pvpanic/pvpanic-pci.c @@ -35,8 +35,8 @@ static int pvpanic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e } static const struct pci_device_id pvpanic_pci_id_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_PVPANIC)}, - {} + { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_PVPANIC) }, + { } }; MODULE_DEVICE_TABLE(pci, pvpanic_pci_id_tbl); diff --git a/drivers/misc/rp1/rp1_pci.c b/drivers/misc/rp1/rp1_pci.c index 94ad022cd138..207a676913a2 100644 --- a/drivers/misc/rp1/rp1_pci.c +++ b/drivers/misc/rp1/rp1_pci.c @@ -287,7 +287,7 @@ static void rp1_remove(struct pci_dev *pdev) } static const struct pci_device_id dev_id_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RPI_RP1_C0), }, + { PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RPI_RP1_C0) }, { } }; MODULE_DEVICE_TABLE(pci, dev_id_table); diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c index 476af89e751b..83355c0ec1d2 100644 --- a/drivers/misc/vmw_vmci/vmci_guest.c +++ b/drivers/misc/vmw_vmci/vmci_guest.c @@ -960,8 +960,8 @@ static void vmci_guest_remove_device(struct pci_dev *pdev) } static const struct pci_device_id vmci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_VMCI), }, - { 0 }, + { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_VMCI) }, + { } }; MODULE_DEVICE_TABLE(pci, vmci_ids); From c8295feb756f5e3ccf02c14a90a2ee0aed95e409 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 22 Jul 2026 13:48:27 -0700 Subject: [PATCH 430/513] uio: fsl_elbc_gpcm: use platform helpers for resource and IRQ Replace the open-coded of_address_to_resource() in get_of_data() and the later ioremap() of the memory region with a single devm_platform_get_and_ioremap_resource() call in probe(). This requests the region and maps it once, instead of mapping it without a region request, and removes the redundant second ioremap. Similarly replace irq_of_parse_and_map() with platform_get_irq(), which returns a negative errno on failure (including -EPROBE_DEFER) instead of 0. Both substitutions are equivalent for a DT-backed platform device. The remaining OF accessors are now covered by linux/of.h, so the unused linux/of_address.h and linux/of_irq.h includes are dropped. No functional change; the GPCM window is not claimed by any other driver (fsl_lbc only of_iomap()s its own registers, and sibling eLBC-window drivers use distinct windows), so the new region request cannot conflict. Built for powerpc (allmodconfig + CONFIG_UIO_FSL_ELBC_GPCM) with LLVM=1; drivers/uio/uio_fsl_elbc_gpcm.o compiles cleanly. Assisted-by: opencode:hy3-free Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260722204827.510133-1-rosenp@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_fsl_elbc_gpcm.c | 60 +++++++++++++-------------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/drivers/uio/uio_fsl_elbc_gpcm.c b/drivers/uio/uio_fsl_elbc_gpcm.c index 338dd2aaabc8..08cb150fb12f 100644 --- a/drivers/uio/uio_fsl_elbc_gpcm.c +++ b/drivers/uio/uio_fsl_elbc_gpcm.c @@ -45,8 +45,7 @@ #include #include #include -#include -#include +#include #include @@ -249,19 +248,12 @@ static int check_of_data(struct fsl_elbc_gpcm *priv, static int get_of_data(struct fsl_elbc_gpcm *priv, struct device_node *node, struct resource *res, u32 *reg_br, - u32 *reg_or, unsigned int *irq, char **name) + u32 *reg_or, char **name) { const char *dt_name; const char *type; int ret; - /* get the memory resource */ - ret = of_address_to_resource(node, 0, res); - if (ret) { - dev_err(priv->dev, "failed to get resource\n"); - return ret; - } - /* get the bank number */ ret = of_property_read_u32(node, "reg", &priv->bank); if (ret) { @@ -288,9 +280,6 @@ static int get_of_data(struct fsl_elbc_gpcm *priv, struct device_node *node, if (of_property_read_string(node, "device_type", &type) == 0) setup_periph(priv, type); - /* get optional irq value */ - *irq = irq_of_parse_and_map(node, 0); - /* sanity check device tree data */ ret = check_of_data(priv, res, *reg_br, *reg_or); if (ret) @@ -312,7 +301,8 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) struct fsl_elbc_gpcm *priv; struct uio_info *info; char *uio_name = NULL; - struct resource res; + struct resource *res; + void __iomem *base; unsigned int irq; u32 reg_br_cur; u32 reg_or_cur; @@ -323,6 +313,16 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) return -ENODEV; + /* map the memory resource */ + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) + return PTR_ERR(base); + + /* get optional irq value */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + /* allocate private data */ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -331,8 +331,8 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) priv->lbc = fsl_lbc_ctrl_dev->regs; /* get device tree data */ - ret = get_of_data(priv, node, &res, ®_br_new, ®_or_new, - &irq, &uio_name); + ret = get_of_data(priv, node, res, ®_br_new, ®_or_new, + &uio_name); if (ret) return ret; @@ -349,7 +349,7 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) if ((reg_br_cur & BR_V)) { if ((reg_br_cur & BR_MSEL) != BR_MS_GPCM || (reg_br_cur & reg_or_cur & BR_BA) - != fsl_lbc_addr(res.start)) { + != fsl_lbc_addr(res->start)) { dev_err(priv->dev, "bank in use by another peripheral\n"); return -ENODEV; @@ -371,26 +371,18 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) /* configure the bank (force base address and GPCM) */ reg_br_new &= ~(BR_BA | BR_MSEL); - reg_br_new |= fsl_lbc_addr(res.start) | BR_MS_GPCM | BR_V; + reg_br_new |= fsl_lbc_addr(res->start) | BR_MS_GPCM | BR_V; out_be32(&priv->lbc->bank[priv->bank].or, reg_or_new); out_be32(&priv->lbc->bank[priv->bank].br, reg_br_new); - /* map the memory resource */ - info->mem[0].internal_addr = ioremap(res.start, resource_size(&res)); - if (!info->mem[0].internal_addr) { - dev_err(priv->dev, "failed to map chip region\n"); - return -ENODEV; - } - /* set all UIO data */ info->mem[0].name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%pOFn", node); - if (!info->mem[0].name) { - ret = -ENODEV; - goto out_err3; - } + if (!info->mem[0].name) + return -ENODEV; - info->mem[0].addr = res.start; - info->mem[0].size = resource_size(&res); + info->mem[0].internal_addr = base; + info->mem[0].addr = res->start; + info->mem[0].size = resource_size(res); info->mem[0].memtype = UIO_MEM_PHYS; info->priv = priv; info->name = uio_name; @@ -421,16 +413,13 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) dev_info(priv->dev, "eLBC/GPCM device (%s) at 0x%llx, bank %d, irq=%d\n", - priv->name, (unsigned long long)res.start, priv->bank, + priv->name, (unsigned long long)res->start, priv->bank, irq ? : -1); return 0; out_err2: if (priv->shutdown) priv->shutdown(info, true); - -out_err3: - iounmap(info->mem[0].internal_addr); return ret; } @@ -443,7 +432,6 @@ static void uio_fsl_elbc_gpcm_remove(struct platform_device *pdev) uio_unregister_device(info); if (priv->shutdown) priv->shutdown(info, false); - iounmap(info->mem[0].internal_addr); } From 63d436b42395d714c3a1c55e0c42733a26673460 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Fri, 24 Jul 2026 23:32:03 +0200 Subject: [PATCH 431/513] EISA: Use str_plural() in eisa_probe() Replace the manual ternary "s" pluralization with str_plural() to simplify the code. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260724213204.251083-2-thorsten.blum@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/eisa/eisa-bus.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/eisa/eisa-bus.c b/drivers/eisa/eisa-bus.c index 79ab44fe26aa..73c4ba3dc0bc 100644 --- a/drivers/eisa/eisa-bus.c +++ b/drivers/eisa/eisa-bus.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -393,7 +394,7 @@ static int __init eisa_probe(struct eisa_root_device *root) } } - dev_info(root->dev, "EISA: Detected %d card%s\n", c, c == 1 ? "" : "s"); + dev_info(root->dev, "EISA: Detected %d card%s\n", c, str_plural(c)); return 0; } From 6f4acc3a3c300e174e3f586b97b04ed8f5948c36 Mon Sep 17 00:00:00 2001 From: Prasanna Kumar T S M Date: Fri, 24 Jul 2026 02:27:12 -0700 Subject: [PATCH 432/513] cdx: Fix double free when sysfs file creation fails In cdx_create_res_attr(), if sysfs_create_bin_file() fails, the code frees res_attr but doesn't set cdx_dev->res_attr[num] to NULL. This leaves a dangling pointer in the array. Then cdx_destroy_res_attr() frees the already-freed memory. Fix the double free by initializing cdx_dev->res_attr[num] after sysfs_create_bin_file() completes. Fixes: aeda33ab8160 ("cdx: create sysfs bin files for cdx resources") Cc: stable@vger.kernel.org Signed-off-by: Prasanna Kumar T S M Acked-by: Nikhil Agarwal Acked-by: Nipun Gupta Link: https://patch.msgid.link/20260724092712.2119149-1-ptsm@linux.microsoft.com Signed-off-by: Greg Kroah-Hartman --- drivers/cdx/cdx.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c index d3d230247262..a118a89a7779 100644 --- a/drivers/cdx/cdx.c +++ b/drivers/cdx/cdx.c @@ -738,7 +738,6 @@ static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num) sysfs_bin_attr_init(res_attr); - cdx_dev->res_attr[num] = res_attr; sprintf(res_attr_name, "resource%d", num); res_attr->mmap = cdx_mmap_resource; @@ -747,8 +746,12 @@ static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num) res_attr->size = cdx_resource_len(cdx_dev, num); res_attr->private = (void *)(unsigned long)num; ret = sysfs_create_bin_file(&cdx_dev->dev.kobj, res_attr); - if (ret) + if (ret) { kfree(res_attr); + return ret; + } + + cdx_dev->res_attr[num] = res_attr; return ret; } From 284497419abd878994f0e0bdda962758f0a568dc Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 21 Jul 2026 14:32:22 -0700 Subject: [PATCH 433/513] issei: Fix size_t printk specifier in heci_{write,read}_buf() When building for 32-bit platforms, for which 'size_t' is 'unsigned int', there are a couple of warnings around incorrect printk specifiers: In file included from drivers/misc/issei/hw_heci.c:7: drivers/misc/issei/hw_heci.c: In function 'heci_write_hbuf': drivers/misc/issei/hw_heci.c:374:37: error: format '%lu' expects argument of type 'long unsigned int', but argument 4 has type 'unsigned int' [-Werror=format=] 374 | dev_err(&idev->dev, "Data size %zu not aligned to slot size %lu\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ... drivers/misc/issei/hw_heci.c:374:79: note: format string is defined here 374 | dev_err(&idev->dev, "Data size %zu not aligned to slot size %lu\n", | ~~^ | | | long unsigned int | %u drivers/misc/issei/hw_heci.c: In function 'heci_read_hbuf': drivers/misc/issei/hw_heci.c:404:37: error: format '%lu' expects argument of type 'long unsigned int', but argument 4 has type 'unsigned int' [-Werror=format=] 404 | dev_err(&idev->dev, "Data size %zu not aligned to slot size %lu\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ... drivers/misc/issei/hw_heci.c:404:79: note: format string is defined here 404 | dev_err(&idev->dev, "Data size %zu not aligned to slot size %lu\n", | ~~^ | | | long unsigned int | %u cc1: all warnings being treated as errors Use '%zu' for printing these values, the proper printk specifier for 'size_t'. Fixes: 8bf5e84998c3 ("issei: add heci hardware module") Signed-off-by: Nathan Chancellor Acked-by: Alexander Usyskin Link: https://patch.msgid.link/20260721-issei-fix-size_t-specifier-v1-1-246155b42d48@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/issei/hw_heci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/issei/hw_heci.c b/drivers/misc/issei/hw_heci.c index 35c55de5c67a..501774aa95ff 100644 --- a/drivers/misc/issei/hw_heci.c +++ b/drivers/misc/issei/hw_heci.c @@ -371,7 +371,7 @@ static int heci_write_hbuf(struct issei_device *idev, const void *data, size_t d struct issei_heci_hw *hw = to_heci_hw(idev); if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) { - dev_err(&idev->dev, "Data size %zu not aligned to slot size %lu\n", + dev_err(&idev->dev, "Data size %zu not aligned to slot size %zu\n", data_len, CB_SLOT_SIZE); return -EINVAL; } @@ -401,7 +401,7 @@ static int heci_read_hbuf(struct issei_device *idev, void *data, size_t data_len u32 *reg_buf = data; if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) { - dev_err(&idev->dev, "Data size %zu not aligned to slot size %lu\n", + dev_err(&idev->dev, "Data size %zu not aligned to slot size %zu\n", data_len, CB_SLOT_SIZE); return -EINVAL; } From 9bcb5dbf0d0284c982613ebce7d2fda726159589 Mon Sep 17 00:00:00 2001 From: Hongyan Xu Date: Wed, 29 Jul 2026 23:45:23 +0800 Subject: [PATCH 434/513] misc: hisi_hikey_usb: remove untested role-switch driver During review of a lifetime fix for this driver, it turned out that the role-switch path has no upstream DT binding or in-tree user and has not been tested in years. Rather than keep carrying and fixing an effectively unused board-specific driver, remove it. If this functionality is needed again later, it can be reintroduced in drivers/phy/, where USB role-switch support lives today. Link: https://lore.kernel.org/r/20260728065558.1532-1-getshell@seu.edu.cn Suggested-by: Greg Kroah-Hartman Suggested-by: Arnd Bergmann Signed-off-by: Hongyan Xu Link: https://patch.msgid.link/20260729154523.1116-1-getshell@seu.edu.cn Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 6 - drivers/misc/Kconfig | 10 -- drivers/misc/Makefile | 1 - drivers/misc/hisi_hikey_usb.c | 274 ---------------------------------- 4 files changed, 291 deletions(-) delete mode 100644 drivers/misc/hisi_hikey_usb.c diff --git a/MAINTAINERS b/MAINTAINERS index a15a5b6fa91f..1e27c49ffe48 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11649,12 +11649,6 @@ W: http://www.highpoint-tech.com F: Documentation/scsi/hptiop.rst F: drivers/scsi/hptiop.c -HIKEY960 ONBOARD USB GPIO HUB DRIVER -M: John Stultz -L: linux-kernel@vger.kernel.org -S: Maintained -F: drivers/misc/hisi_hikey_usb.c - HIMAX HX83112B TOUCHSCREEN SUPPORT M: Job Noorman L: linux-input@vger.kernel.org diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index e594edf86941..7f4900efe726 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -494,16 +494,6 @@ config MISC_RTSX tristate default MISC_RTSX_PCI || MISC_RTSX_USB -config HISI_HIKEY_USB - tristate "USB GPIO Hub on HiSilicon Hikey 960/970 Platform" - depends on (OF && GPIOLIB) || COMPILE_TEST - depends on USB_ROLE_SWITCH - help - If you say yes here this adds support for the on-board USB GPIO hub - found on HiKey 960/970 boards, which is necessary to support - switching between the dual-role USB-C port and the USB-A host ports - using only one USB controller. - config OPEN_DICE tristate "Open Profile for DICE driver" depends on OF_RESERVED_MEM diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 086ac3f75935..812b68d21185 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -56,7 +56,6 @@ obj-y += cardreader/ obj-$(CONFIG_PVPANIC) += pvpanic/ obj-$(CONFIG_UACCE) += uacce/ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o -obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o obj-$(CONFIG_NTSYNC) += ntsync.o obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o obj-$(CONFIG_OPEN_DICE) += open-dice.o diff --git a/drivers/misc/hisi_hikey_usb.c b/drivers/misc/hisi_hikey_usb.c deleted file mode 100644 index e68bc6e384d5..000000000000 --- a/drivers/misc/hisi_hikey_usb.c +++ /dev/null @@ -1,274 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Support for usb functionality of Hikey series boards - * based on Hisilicon Kirin Soc. - * - * Copyright (C) 2017-2018 Hilisicon Electronics Co., Ltd. - * http://www.huawei.com - * - * Authors: Yu Chen - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEVICE_DRIVER_NAME "hisi_hikey_usb" - -#define HUB_VBUS_POWER_ON 1 -#define HUB_VBUS_POWER_OFF 0 -#define USB_SWITCH_TO_HUB 1 -#define USB_SWITCH_TO_TYPEC 0 -#define TYPEC_VBUS_POWER_ON 1 -#define TYPEC_VBUS_POWER_OFF 0 - -struct hisi_hikey_usb { - struct device *dev; - struct gpio_desc *otg_switch; - struct gpio_desc *typec_vbus; - struct gpio_desc *reset; - - struct regulator *regulator; - - struct usb_role_switch *hub_role_sw; - - struct usb_role_switch *dev_role_sw; - enum usb_role role; - - struct mutex lock; - struct work_struct work; - - struct notifier_block nb; -}; - -static void hub_power_ctrl(struct hisi_hikey_usb *hisi_hikey_usb, int value) -{ - int ret, status; - - if (!hisi_hikey_usb->regulator) - return; - - status = regulator_is_enabled(hisi_hikey_usb->regulator); - if (status == !!value) - return; - - if (value) - ret = regulator_enable(hisi_hikey_usb->regulator); - else - ret = regulator_disable(hisi_hikey_usb->regulator); - - if (ret) - dev_err(hisi_hikey_usb->dev, - "Can't switch regulator state to %s\n", - str_enabled_disabled(value)); -} - -static void usb_switch_ctrl(struct hisi_hikey_usb *hisi_hikey_usb, - int switch_to) -{ - if (!hisi_hikey_usb->otg_switch) - return; - - gpiod_set_value_cansleep(hisi_hikey_usb->otg_switch, switch_to); -} - -static void usb_typec_power_ctrl(struct hisi_hikey_usb *hisi_hikey_usb, - int value) -{ - if (!hisi_hikey_usb->typec_vbus) - return; - - gpiod_set_value_cansleep(hisi_hikey_usb->typec_vbus, value); -} - -static void relay_set_role_switch(struct work_struct *work) -{ - struct hisi_hikey_usb *hisi_hikey_usb = container_of(work, - struct hisi_hikey_usb, - work); - struct usb_role_switch *sw; - enum usb_role role; - - if (!hisi_hikey_usb || !hisi_hikey_usb->dev_role_sw) - return; - - mutex_lock(&hisi_hikey_usb->lock); - switch (hisi_hikey_usb->role) { - case USB_ROLE_NONE: - usb_typec_power_ctrl(hisi_hikey_usb, TYPEC_VBUS_POWER_OFF); - usb_switch_ctrl(hisi_hikey_usb, USB_SWITCH_TO_HUB); - hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_ON); - break; - case USB_ROLE_HOST: - hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_OFF); - usb_switch_ctrl(hisi_hikey_usb, USB_SWITCH_TO_TYPEC); - usb_typec_power_ctrl(hisi_hikey_usb, TYPEC_VBUS_POWER_ON); - break; - case USB_ROLE_DEVICE: - hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_OFF); - usb_typec_power_ctrl(hisi_hikey_usb, TYPEC_VBUS_POWER_OFF); - usb_switch_ctrl(hisi_hikey_usb, USB_SWITCH_TO_TYPEC); - break; - default: - break; - } - sw = hisi_hikey_usb->dev_role_sw; - role = hisi_hikey_usb->role; - mutex_unlock(&hisi_hikey_usb->lock); - - usb_role_switch_set_role(sw, role); -} - -static int hub_usb_role_switch_set(struct usb_role_switch *sw, enum usb_role role) -{ - struct hisi_hikey_usb *hisi_hikey_usb = usb_role_switch_get_drvdata(sw); - - if (!hisi_hikey_usb || !hisi_hikey_usb->dev_role_sw) - return -EINVAL; - - mutex_lock(&hisi_hikey_usb->lock); - hisi_hikey_usb->role = role; - mutex_unlock(&hisi_hikey_usb->lock); - - schedule_work(&hisi_hikey_usb->work); - - return 0; -} - -static int hisi_hikey_usb_of_role_switch(struct platform_device *pdev, - struct hisi_hikey_usb *hisi_hikey_usb) -{ - struct device *dev = &pdev->dev; - struct usb_role_switch_desc hub_role_switch = {NULL}; - - if (!device_property_read_bool(dev, "usb-role-switch")) - return 0; - - hisi_hikey_usb->otg_switch = devm_gpiod_get(dev, "otg-switch", - GPIOD_OUT_HIGH); - if (IS_ERR(hisi_hikey_usb->otg_switch)) { - dev_err(dev, "get otg-switch failed with error %ld\n", - PTR_ERR(hisi_hikey_usb->otg_switch)); - return PTR_ERR(hisi_hikey_usb->otg_switch); - } - - hisi_hikey_usb->typec_vbus = devm_gpiod_get(dev, "typec-vbus", - GPIOD_OUT_LOW); - if (IS_ERR(hisi_hikey_usb->typec_vbus)) { - dev_err(dev, "get typec-vbus failed with error %ld\n", - PTR_ERR(hisi_hikey_usb->typec_vbus)); - return PTR_ERR(hisi_hikey_usb->typec_vbus); - } - - hisi_hikey_usb->reset = devm_gpiod_get_optional(dev, - "hub-reset-en", - GPIOD_OUT_HIGH); - if (IS_ERR(hisi_hikey_usb->reset)) { - dev_err(dev, "get hub-reset-en failed with error %ld\n", - PTR_ERR(hisi_hikey_usb->reset)); - return PTR_ERR(hisi_hikey_usb->reset); - } - - hisi_hikey_usb->dev_role_sw = usb_role_switch_get(dev); - if (!hisi_hikey_usb->dev_role_sw) - return -EPROBE_DEFER; - if (IS_ERR(hisi_hikey_usb->dev_role_sw)) { - dev_err(dev, "get device role switch failed with error %ld\n", - PTR_ERR(hisi_hikey_usb->dev_role_sw)); - return PTR_ERR(hisi_hikey_usb->dev_role_sw); - } - - INIT_WORK(&hisi_hikey_usb->work, relay_set_role_switch); - - hub_role_switch.fwnode = dev_fwnode(dev); - hub_role_switch.set = hub_usb_role_switch_set; - hub_role_switch.driver_data = hisi_hikey_usb; - - hisi_hikey_usb->hub_role_sw = usb_role_switch_register(dev, - &hub_role_switch); - - if (IS_ERR(hisi_hikey_usb->hub_role_sw)) { - dev_err(dev, - "failed to register hub role with error %ld\n", - PTR_ERR(hisi_hikey_usb->hub_role_sw)); - usb_role_switch_put(hisi_hikey_usb->dev_role_sw); - return PTR_ERR(hisi_hikey_usb->hub_role_sw); - } - - return 0; -} - -static int hisi_hikey_usb_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct hisi_hikey_usb *hisi_hikey_usb; - int ret; - - hisi_hikey_usb = devm_kzalloc(dev, sizeof(*hisi_hikey_usb), GFP_KERNEL); - if (!hisi_hikey_usb) - return -ENOMEM; - - hisi_hikey_usb->dev = &pdev->dev; - mutex_init(&hisi_hikey_usb->lock); - - hisi_hikey_usb->regulator = devm_regulator_get(dev, "hub-vdd"); - if (IS_ERR(hisi_hikey_usb->regulator)) { - if (PTR_ERR(hisi_hikey_usb->regulator) == -EPROBE_DEFER) { - dev_info(dev, "waiting for hub-vdd-supply\n"); - return PTR_ERR(hisi_hikey_usb->regulator); - } - dev_err(dev, "get hub-vdd-supply failed with error %ld\n", - PTR_ERR(hisi_hikey_usb->regulator)); - return PTR_ERR(hisi_hikey_usb->regulator); - } - - ret = hisi_hikey_usb_of_role_switch(pdev, hisi_hikey_usb); - if (ret) - return ret; - - platform_set_drvdata(pdev, hisi_hikey_usb); - - return 0; -} - -static void hisi_hikey_usb_remove(struct platform_device *pdev) -{ - struct hisi_hikey_usb *hisi_hikey_usb = platform_get_drvdata(pdev); - - if (hisi_hikey_usb->hub_role_sw) { - usb_role_switch_unregister(hisi_hikey_usb->hub_role_sw); - - if (hisi_hikey_usb->dev_role_sw) - usb_role_switch_put(hisi_hikey_usb->dev_role_sw); - } else { - hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_OFF); - } -} - -static const struct of_device_id id_table_hisi_hikey_usb[] = { - { .compatible = "hisilicon,usbhub" }, - { } -}; -MODULE_DEVICE_TABLE(of, id_table_hisi_hikey_usb); - -static struct platform_driver hisi_hikey_usb_driver = { - .probe = hisi_hikey_usb_probe, - .remove = hisi_hikey_usb_remove, - .driver = { - .name = DEVICE_DRIVER_NAME, - .of_match_table = id_table_hisi_hikey_usb, - }, -}; - -module_platform_driver(hisi_hikey_usb_driver); - -MODULE_AUTHOR("Yu Chen "); -MODULE_DESCRIPTION("Driver Support for USB functionality of Hikey"); -MODULE_LICENSE("GPL v2"); From 220190f97da558e67cd01c62f1b84fe77b267a5a Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Fri, 31 Jul 2026 10:59:52 +0800 Subject: [PATCH 435/513] misc: issei: check bus message length before reading the command __issei_ham_process_ham_rsp() dispatches on hdr->cmd before the message length is validated. The length comes from the firmware-owned DMA header read in issei_dma_read(), which only bounds it from above, so firmware sending a short bus message reaches the dispatch with less than sizeof(struct ham_bus_message) bytes available. For a zero-length message kmemdup() returns ZERO_SIZE_PTR, which passes the NULL check in issei_dma_read(), and the dispatch dereferences it. A length of one to three bytes gives a slab out-of-bounds read instead. Reject bus messages shorter than the header before touching it, the way the individual response handlers already validate their own length. Fixes: 7bd4b9991db20 ("issei: implement main thread and ham messages") Signed-off-by: Linmao Li Link: https://patch.msgid.link/20260731025952.3505287-1-lilinmao@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- drivers/misc/issei/ham.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/misc/issei/ham.c b/drivers/misc/issei/ham.c index 17eae91f077d..674d9733d16c 100644 --- a/drivers/misc/issei/ham.c +++ b/drivers/misc/issei/ham.c @@ -132,6 +132,12 @@ static int __issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, { struct ham_bus_message *hdr = (struct ham_bus_message *)buf; + if (length < sizeof(*hdr)) { + dev_err(&idev->dev, "Small bus message size %zu < %zu\n", + length, sizeof(*hdr)); + return -EPROTO; + } + switch (hdr->cmd) { case HAM_BUS_CMD_START_RSP: return issei_ham_start_rsp(idev, buf, length); From 505d48c93896948c9a430fa96a303af114566db9 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Wed, 15 Jul 2026 21:50:28 +0800 Subject: [PATCH 436/513] counter: stm32-timer-cnt: Remove redundant dev_err() devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Reviewed-by: Joshua Crofts Link: https://lore.kernel.org/r/20260715135030.326290-2-panchuang@vivo.com Signed-off-by: William Breathitt Gray --- drivers/counter/stm32-timer-cnt.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index 3d3384cbea87..954a22dabb99 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -760,11 +760,8 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev) /* All events reported through the global interrupt */ ret = devm_request_irq(&pdev->dev, ddata->irq[0], stm32_timer_cnt_isr, 0, dev_name(dev), counter); - if (ret) { - dev_err(dev, "Failed to request irq %d (err %d)\n", - ddata->irq[0], ret); + if (ret) return ret; - } } else { for (i = 0; i < priv->nr_irqs; i++) { /* @@ -776,11 +773,8 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, ddata->irq[i], stm32_timer_cnt_isr, 0, dev_name(dev), counter); - if (ret) { - dev_err(dev, "Failed to request irq %d (err %d)\n", - ddata->irq[i], ret); + if (ret) return ret; - } } } From ccd73fc6fbd775b4981855717f85b769f0cf4f59 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Wed, 15 Jul 2026 21:50:29 +0800 Subject: [PATCH 437/513] counter: ti-ecap-capture: Remove redundant dev_err_probe() devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err_probe() call. Signed-off-by: Pan Chuang Reviewed-by: Joshua Crofts Link: https://lore.kernel.org/r/20260715135030.326290-3-panchuang@vivo.com Signed-off-by: William Breathitt Gray --- drivers/counter/ti-ecap-capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/counter/ti-ecap-capture.c b/drivers/counter/ti-ecap-capture.c index 3586a7ab9887..5fe51e894145 100644 --- a/drivers/counter/ti-ecap-capture.c +++ b/drivers/counter/ti-ecap-capture.c @@ -514,7 +514,7 @@ static int ecap_cnt_probe(struct platform_device *pdev) ret = devm_request_irq(dev, ret, ecap_cnt_isr, 0, pdev->name, counter_dev); if (ret) - return dev_err_probe(dev, ret, "failed to request irq\n"); + return ret; platform_set_drvdata(pdev, counter_dev); From 353b2e09f44a91e64c6b8117df46b6999c52e465 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Wed, 15 Jul 2026 21:50:30 +0800 Subject: [PATCH 438/513] counter: ti-eqep: Remove redundant dev_err_probe() devm_request_threaded_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err_probe() call. Signed-off-by: Pan Chuang Reviewed-by: Joshua Crofts Reviewed-by: David Lechner Link: https://lore.kernel.org/r/20260715135030.326290-4-panchuang@vivo.com Signed-off-by: William Breathitt Gray --- drivers/counter/ti-eqep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c index d21c157e531a..ec6633e5a289 100644 --- a/drivers/counter/ti-eqep.c +++ b/drivers/counter/ti-eqep.c @@ -527,7 +527,7 @@ static int ti_eqep_probe(struct platform_device *pdev) err = devm_request_threaded_irq(dev, irq, NULL, ti_eqep_irq_handler, IRQF_ONESHOT, dev_name(dev), counter); if (err < 0) - return dev_err_probe(dev, err, "failed to request IRQ\n"); + return err; counter->name = dev_name(dev); counter->parent = dev; From ac75550ab5b5d73649bffea245c2075fd9249bd0 Mon Sep 17 00:00:00 2001 From: Yuanshen Cao Date: Fri, 24 Jul 2026 23:46:01 +0000 Subject: [PATCH 439/513] iio: light: tsl2772: fix ALS calibscale readback The read_raw() implementation uses IIO_LIGHT to distinguish between the ambient light and proximity channels when handling IIO_CHAN_INFO_CALIBSCALE. However, the ALS channel is registered as IIO_INTENSITY, while write_raw() correctly writes to IIO_INTENSITY. As a result, reading in_intensity0_calibscale incorrectly returns the proximity gain instead of the ALS gain. This causes the following user-visible behavior: - Writing in_intensity0_calibscale appears to have no effect because the readback reports the proximity gain. - Writing in_proximity0_calibscale causes both in_proximity0_calibscale and in_intensity0_calibscale to report the same value. Fix this by checking for IIO_INTENSITY in read_raw(), matching the channel definition and the existing write_raw() implementation. Fixes: 3c97c08b5735 ("staging: iio: add TAOS tsl2x7x driver") Signed-off-by: Yuanshen Cao Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2772.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c index 83ef7c665146..4486a1d9d84d 100644 --- a/drivers/iio/light/tsl2772.c +++ b/drivers/iio/light/tsl2772.c @@ -1274,7 +1274,7 @@ static int tsl2772_read_raw(struct iio_dev *indio_dev, } break; case IIO_CHAN_INFO_CALIBSCALE: - if (chan->type == IIO_LIGHT) + if (chan->type == IIO_INTENSITY) *val = tsl2772_als_gain[chip->settings.als_gain]; else *val = tsl2772_prox_gain[chip->settings.prox_gain]; From c6955f7de5506990853ca7da74950088da101b2d Mon Sep 17 00:00:00 2001 From: "David Lechner (TI)" Date: Mon, 27 Jul 2026 09:09:50 -0500 Subject: [PATCH 440/513] iio: adc: ti-ads112c14: add support for I2C CRC8 Add support for I2C CRC8 to the TI ADS112C14 ADC driver. This verifies data integrity of all I2C transactions with the device. For now, it is always enabled, but it could be made optional in the future if needed (e.g. for higher-speed data acquisition). Signed-off-by: David Lechner (TI) Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 2 +- drivers/iio/adc/ti-ads112c14.c | 148 +++++++++++++++++++++++++++++++-- 2 files changed, 140 insertions(+), 10 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 642c42b1fae7..b043c00f8939 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1801,8 +1801,8 @@ config TI_ADS1119 config TI_ADS112C14 tristate "Texas Instruments ADS112C14/ADS122C14" depends on I2C + select CRC8 select REGMAP - select REGMAP_I2C help If you say yes here you get support for Texas Instruments ADS112C14, ADS122C14 ADC chips. diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c index 3035d45b64b0..5610d16f4467 100644 --- a/drivers/iio/adc/ti-ads112c14.c +++ b/drivers/iio/adc/ti-ads112c14.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -156,6 +157,9 @@ static const u32 ads112c14_pga_gains_x10[] = { 200, 320, 500, 640, 1000, 1280, 2000, 2560, /* 8 - 15 */ }; +#define ADS112C14_I2C_CRC8_POLYNOMIAL 0x07 +DECLARE_CRC8_TABLE(ads112c14_crc8_table); + struct ads112c14_chip_info { const char *name; u8 device_id; @@ -241,6 +245,7 @@ struct ads112c14_data { struct regmap *regmap; /* Synchronizes access to register value fields. */ struct mutex lock; + bool i2c_crc_enabled; u32 avdd_uV; u32 ext_ref_uV; bool refp_is_avdd; @@ -289,6 +294,120 @@ static const struct reg_default ads112c14_reg_defaults[] = { { ADS112C14_REG_IDAC_MUX_CFG, FIELD_PREP_CONST(ADS112C14_IDAC_MUX_CFG_I2MUX, 1) }, }; +/** + * ads112c14_i2c_read_bytes() - Read bytes from the device over I2C + * @client: I2C client for the device + * @cmd: Command to send to the device before reading + * @buf: Buffer to store the read bytes + * @len: Number of bytes to read + * @use_crc: Whether to use CRC8 for data integrity check + * + * If I2C_CRC is enabled, @use_crc may be set to true to perform a CRC8 check + * on the received data. + */ +static int ads112c14_i2c_read_bytes(struct i2c_client *client, u8 cmd, + u8 *buf, u8 len, bool use_crc) +{ + u8 rx_buf[4]; /* Up to 3 data bytes + 1 CRC byte. */ + u8 rx_len; + int ret; + + rx_len = len + (use_crc ? 1 : 0); + + if (rx_len > sizeof(rx_buf)) + return -EINVAL; + + ret = i2c_smbus_read_i2c_block_data(client, cmd, rx_len, rx_buf); + if (ret < 0) + return ret; + + if (use_crc) { + u8 crc = crc8(ads112c14_crc8_table, rx_buf, len, CRC8_INIT_VALUE); + + if (crc != rx_buf[len]) + return -EBADMSG; + } + + memcpy(buf, rx_buf, len); + + return 0; +} + +/** + * ads112c14_regmap_bus_read() - Read a register from the device + * @context: Pointer to the device context + * @reg_buf: Register address to read + * @reg_size: Size of the register address (should be 1) + * @val_buf: Buffer to store the read value + * @val_size: Size of the value to read + * + * Custom regmap read function that also does CRC check when enabled. + */ +static int ads112c14_regmap_bus_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, + size_t val_size) +{ + struct ads112c14_data *data = context; + struct device *dev = regmap_get_device(data->regmap); + struct i2c_client *client = to_i2c_client(dev); + const u8 *cmd = reg_buf; + + if (reg_size != 1) + return -EINVAL; + + return ads112c14_i2c_read_bytes(client, cmd[0], val_buf, val_size, + data->i2c_crc_enabled); +} + +/** + * ads112c14_regmap_bus_write() - Write a register to the device + * @context: Pointer to the device context + * @data_buf: Buffer containing the register address and value to write + * @count: Number of bytes to write + * + * Custom regmap write function that also does readback with CRC check of + * nonvolatile registers when CRC is enabled. + */ +static int ads112c14_regmap_bus_write(void *context, const void *data_buf, + size_t count) +{ + struct ads112c14_data *data = context; + struct device *dev = regmap_get_device(data->regmap); + struct i2c_client *client = to_i2c_client(dev); + const u8 *tx = data_buf; + u8 reg, readback; + int ret; + + if (count != 2) + return -EINVAL; + + ret = i2c_smbus_write_byte_data(client, tx[0], tx[1]); + if (ret) + return ret; + + reg = tx[0] & ~ADS112C14_CMD_WREG; + + if (!data->i2c_crc_enabled || ads112c14_volatile_reg(dev, reg)) + return 0; + + ret = ads112c14_i2c_read_bytes(client, reg | ADS112C14_CMD_RREG, + &readback, sizeof(readback), true); + if (ret) + return ret; + + if (readback != tx[1]) + return -EIO; + + return 0; +} + +static const struct regmap_bus ads112c14_regmap_bus = { + .read = ads112c14_regmap_bus_read, + .write = ads112c14_regmap_bus_write, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + static const struct regmap_config ads112c14_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -486,13 +605,9 @@ static int ads112c14_single_conversion(struct ads112c14_data *data, if (ret) return ret; - ret = i2c_smbus_read_i2c_block_data(client, ADS112C14_CMD_RDATA, - BITS_TO_BYTES(data->chip_info->resolution_bits), - buf); - if (ret < 0) - return ret; - - return 0; + return ads112c14_i2c_read_bytes(client, ADS112C14_CMD_RDATA, buf, + BITS_TO_BYTES(data->chip_info->resolution_bits), + data->i2c_crc_enabled); } static int ads112c14_read_raw(struct iio_dev *indio_dev, @@ -1121,7 +1236,8 @@ static int ads112c14_probe(struct i2c_client *client) /* It takes some time for the internal reference to stabilize. */ fsleep(10 * USEC_PER_MSEC); - data->regmap = devm_regmap_init_i2c(client, &ads112c14_regmap_config); + data->regmap = devm_regmap_init(dev, &ads112c14_regmap_bus, data, + &ads112c14_regmap_config); if (IS_ERR(data->regmap)) return dev_err_probe(dev, PTR_ERR(data->regmap), "failed to init regmap\n"); @@ -1158,6 +1274,13 @@ static int ads112c14_probe(struct i2c_client *client) if (ret) return ret; + ret = regmap_set_bits(data->regmap, ADS112C14_REG_DIGITAL_CFG, + ADS112C14_DIGITAL_CFG_I2C_CRC_EN); + if (ret) + return ret; + + data->i2c_crc_enabled = true; + ret = regmap_read(data->regmap, ADS112C14_REG_DEVICE_ID, ®_val); if (ret) return ret; @@ -1209,6 +1332,13 @@ static const struct i2c_device_id ads112c14_id[] = { }; MODULE_DEVICE_TABLE(i2c, ads112c14_id); +static int ads112c14_i2c_add_driver(struct i2c_driver *driver) +{ + crc8_populate_msb(ads112c14_crc8_table, ADS112C14_I2C_CRC8_POLYNOMIAL); + + return i2c_add_driver(driver); +} + static struct i2c_driver ads112c14_driver = { .driver = { .name = "ads112c14", @@ -1217,7 +1347,7 @@ static struct i2c_driver ads112c14_driver = { .probe = ads112c14_probe, .id_table = ads112c14_id, }; -module_i2c_driver(ads112c14_driver); +module_driver(ads112c14_driver, ads112c14_i2c_add_driver, i2c_del_driver); MODULE_AUTHOR("David Lechner (TI) "); MODULE_DESCRIPTION("TI ADS112C14 I2C ADC driver"); From 31640de34d5a2fdd9a150bca1fa8f15796eae358 Mon Sep 17 00:00:00 2001 From: "David Lechner (TI)" Date: Mon, 27 Jul 2026 09:09:51 -0500 Subject: [PATCH 441/513] iio: adc: ti-ads112c14: add support for buffered read Add support for buffered reads using a triggered buffer. The device has a continuous conversion mode, but that can only be used with one channel at a time since there is nothing like a sequencer to support that in hardware. Instead, we use single-shot reads like we do for direct reads to be able to read multiple channels. Since CRC is always enabled, buffered reads include the CRC byte along with the raw data without verifying it. This allows userspace to perform it's own CRC verification per-sample and drop bad samples rather than failing the entire buffered read for one bad sample. Support for continuous conversion mode could be added in the future if needed. Signed-off-by: David Lechner (TI) Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads112c14.c | 108 +++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c index 5610d16f4467..60eab4852ba2 100644 --- a/drivers/iio/adc/ti-ads112c14.c +++ b/drivers/iio/adc/ti-ads112c14.c @@ -15,7 +15,10 @@ #include #include #include +#include #include +#include +#include #include #include #include @@ -29,6 +32,9 @@ #include #include +/* Arbitrary limit since channels are dynamic. */ +#define ADS112C14_MAX_MEASUREMENT_CHANNELS 16 + /* Datasheet t_d(RST) - time to wait after reset before next I2C use. */ #define ADS112C14_DELAY_RESET_US 500 @@ -255,6 +261,8 @@ struct ads112c14_data { u32 num_measurements; u8 sys_mon_chan_short_gain_val; int sys_mon_chan_short_scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2]; + IIO_DECLARE_BUFFER_WITH_TS(__be32, scan, ADS112C14_MAX_MEASUREMENT_CHANNELS + + ARRAY_SIZE(ads112c14_sys_mon_channels)); }; static bool ads112c14_writeable_reg(struct device *dev, unsigned int reg) @@ -575,7 +583,7 @@ static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data, static int ads112c14_single_conversion(struct ads112c14_data *data, const struct iio_chan_spec *chan, - u8 *buf) + u8 *buf, bool for_scan) { struct i2c_client *client = to_i2c_client(regmap_get_device(data->regmap)); u32 reg_val; @@ -605,6 +613,24 @@ static int ads112c14_single_conversion(struct ads112c14_data *data, if (ret) return ret; + /* + * When doing buffered read, we don't check the CRC, but rather pass it + * along with the raw data. This way, we don't silently drop samples + * with CRC errors, but rather leave it to userspace to decide what to + * do. + */ + if (for_scan) { + u8 len = BITS_TO_BYTES(data->chip_info->resolution_bits) + + (data->i2c_crc_enabled ? 1 : 0); + + ret = i2c_smbus_read_i2c_block_data(client, ADS112C14_CMD_RDATA, + len, buf); + if (ret < 0) + return ret; + + return 0; + } + return ads112c14_i2c_read_bytes(client, ADS112C14_CMD_RDATA, buf, BITS_TO_BYTES(data->chip_info->resolution_bits), data->i2c_crc_enabled); @@ -639,7 +665,7 @@ static int ads112c14_read_raw(struct iio_dev *indio_dev, if (IIO_DEV_ACQUIRE_FAILED(claim)) return -EBUSY; - ret = ads112c14_single_conversion(data, chan, buf); + ret = ads112c14_single_conversion(data, chan, buf, false); if (ret) return ret; @@ -765,6 +791,10 @@ static int ads112c14_write_raw(struct iio_dev *indio_dev, const int (*scale_avail)[2]; u8 *gain_val; + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + switch (mask) { case IIO_CHAN_INFO_SCALE: { guard(mutex)(&data->lock); @@ -863,6 +893,37 @@ static int ads112c14_read_label(struct iio_dev *indio_dev, return sysfs_emit(label, "%s\n", label_source); } +static irqreturn_t ads112c14_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct ads112c14_data *data = iio_priv(indio_dev); + u32 offset = 0; + u32 i; + int ret; + + iio_for_each_active_channel(indio_dev, i) { + const struct iio_chan_spec *chan = &indio_dev->channels[i]; + + ret = ads112c14_single_conversion(data, chan, + (u8 *)&data->scan[offset++], + true); + if (ret) { + dev_err_once(indio_dev->dev.parent, + "failed to read channel %d: %pe; additional errors will be suppressed\n", + chan->channel, ERR_PTR(ret)); + goto out; + } + } + + iio_push_to_buffers_with_ts(indio_dev, data->scan, + sizeof(data->scan), pf->timestamp); +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static const struct iio_info ads112c14_info = { .read_raw = ads112c14_read_raw, .read_avail = ads112c14_read_avail, @@ -908,7 +969,7 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev, return -ENOMEM; channels = devm_kcalloc(dev, num_child_nodes + - ARRAY_SIZE(ads112c14_sys_mon_channels), + ARRAY_SIZE(ads112c14_sys_mon_channels) + 1, sizeof(*channels), GFP_KERNEL); if (!channels) return -ENOMEM; @@ -1069,14 +1130,47 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev, if (spec->type == IIO_RESISTANCE) spec->differential = 0; + spec->scan_type = (struct iio_scan_type){ + .format = measurement->bipolar ? + IIO_SCAN_FORMAT_SIGNED_INT : + IIO_SCAN_FORMAT_UNSIGNED_INT, + .realbits = data->chip_info->resolution_bits, + .storagebits = 32, + .shift = 32 - data->chip_info->resolution_bits, + .endianness = IIO_BE, + }; + i++; } data->num_measurements = i; + if (data->num_measurements > ADS112C14_MAX_MEASUREMENT_CHANNELS) + return dev_err_probe(dev, -EINVAL, + "too many measurement channels defined\n"); + memcpy(channels + i, ads112c14_sys_mon_channels, sizeof(ads112c14_sys_mon_channels)); + for (u32 j = 0; j < ARRAY_SIZE(ads112c14_sys_mon_channels); j++) { + struct iio_chan_spec *spec = &channels[i]; + + /* Update the template that was already copied with dynamic values. */ + spec->scan_index = i; + spec->scan_type = (struct iio_scan_type){ + .format = IIO_SCAN_FORMAT_SIGNED_INT, + .realbits = data->chip_info->resolution_bits, + .storagebits = 32, + .shift = 32 - data->chip_info->resolution_bits, + .endianness = IIO_BE, + }; + + i++; + } + + channels[i] = IIO_CHAN_SOFT_TIMESTAMP(i); + i++; + indio_dev->channels = channels; - indio_dev->num_channels = i + ARRAY_SIZE(ads112c14_sys_mon_channels); + indio_dev->num_channels = i; return 0; } @@ -1303,6 +1397,12 @@ static int ads112c14_probe(struct i2c_client *client) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &ads112c14_info; + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ads112c14_trigger_handler, NULL); + if (ret) + return ret; + return devm_iio_device_register(dev, indio_dev); } From e097c0108de9068daf7f7c5b328e0b5938694d1c Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Tue, 28 Jul 2026 17:55:32 -0500 Subject: [PATCH 442/513] dt-bindings: iio: imu: icm42600: Add mount-matrix Add mount-matrix attribute to schema. This attribute has been supported since the first revision of this driver, but was not documented. Signed-off-by: Chris Morgan Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/imu/invensense,icm42600.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml index 119e28a833fd..9b2af104f186 100644 --- a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml +++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml @@ -53,6 +53,8 @@ properties: drive-open-drain: type: boolean + mount-matrix: true + vdd-supply: description: Regulator that provides power to the sensor From 22addefdabdf58b29697b071caf26e5f27c73195 Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Tue, 28 Jul 2026 17:55:33 -0500 Subject: [PATCH 443/513] dt-bindings: iio: imu: icm42600: Add icm42607 Add the ICM42607 and ICM42607P inertial measurement unit. This device is functionally very similar to the icm42600 series with a very different register layout. The icm42607 currently does not use an interrupt because wake on movement and buffers are not supported, and cannot be implemented on my test device which lacks an interrupt line. Signed-off-by: Chris Morgan Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jonathan Cameron --- .../bindings/iio/imu/invensense,icm42600.yaml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml index 9b2af104f186..81b6e85decd5 100644 --- a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml +++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml @@ -30,6 +30,8 @@ properties: - invensense,icm42600 - invensense,icm42602 - invensense,icm42605 + - invensense,icm42607 + - invensense,icm42607p - invensense,icm42622 - invensense,icm42631 - invensense,icm42686 @@ -67,10 +69,24 @@ properties: required: - compatible - reg - - interrupts allOf: - $ref: /schemas/spi/spi-peripheral-props.yaml# + - if: + properties: + compatible: + contains: + enum: + - invensense,icm42600 + - invensense,icm42602 + - invensense,icm42605 + - invensense,icm42622 + - invensense,icm42631 + - invensense,icm42686 + - invensense,icm42688 + then: + required: + - interrupts unevaluatedProperties: false From 2871c45d946993c1e397081826e1551c5a1bb4fb Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Tue, 28 Jul 2026 17:55:34 -0500 Subject: [PATCH 444/513] iio: imu: inv_icm42607: Add inv_icm42607 Core Driver Add the core component of a new inv_icm42607 driver. This includes a few setup functions and the full register definition in the header file, as well as the bits necessary to compile and probe the device when used on an i2c bus. Signed-off-by: Chris Morgan Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/imu/Kconfig | 1 + drivers/iio/imu/Makefile | 1 + drivers/iio/imu/inv_icm42607/Kconfig | 18 + drivers/iio/imu/inv_icm42607/Makefile | 7 + drivers/iio/imu/inv_icm42607/inv_icm42607.h | 365 ++++++++++++++++++ .../iio/imu/inv_icm42607/inv_icm42607_core.c | 284 ++++++++++++++ .../iio/imu/inv_icm42607/inv_icm42607_i2c.c | 96 +++++ 7 files changed, 772 insertions(+) create mode 100644 drivers/iio/imu/inv_icm42607/Kconfig create mode 100644 drivers/iio/imu/inv_icm42607/Makefile create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607.h create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_core.c create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index 7e0181c27bb6..8bab4616be20 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -109,6 +109,7 @@ config KMX61 be called kmx61. source "drivers/iio/imu/inv_icm42600/Kconfig" +source "drivers/iio/imu/inv_icm42607/Kconfig" source "drivers/iio/imu/inv_icm45600/Kconfig" source "drivers/iio/imu/inv_mpu6050/Kconfig" diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile index 13fb7846e9c9..3268dc2371ae 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o obj-$(CONFIG_FXOS8700_SPI) += fxos8700_spi.o obj-y += inv_icm42600/ +obj-y += inv_icm42607/ obj-y += inv_icm45600/ obj-y += inv_mpu6050/ diff --git a/drivers/iio/imu/inv_icm42607/Kconfig b/drivers/iio/imu/inv_icm42607/Kconfig new file mode 100644 index 000000000000..083c212087ab --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config INV_ICM42607 + tristate + select IIO_BUFFER + select IIO_INV_SENSORS_TIMESTAMP + +config INV_ICM42607_I2C + tristate "InvenSense ICM-42607 I2C driver" + depends on I2C + select INV_ICM42607 + select REGMAP_I2C + help + This driver supports the InvenSense ICM-42607 motion tracking + device over I2C. + + This driver can be built as a module. The module will be called + inv-icm42607-i2c. diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile new file mode 100644 index 000000000000..32046e2727d7 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o +inv-icm42607-y += inv_icm42607_core.o + +obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o +inv-icm42607-i2c-y += inv_icm42607_i2c.o diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h new file mode 100644 index 000000000000..169e848caf2e --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h @@ -0,0 +1,365 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#ifndef INV_ICM42607_H_ +#define INV_ICM42607_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Serial bus slew rates. Rates are expressed as range between the two + * values with the midpoint as the typical rate. For the final value of + * 2ns, 2ns is considered the max value with no expressed minimum or + * typical value. + */ +enum inv_icm42607_slew_rate { + INV_ICM42607_SLEW_RATE_20_60NS = 0, + INV_ICM42607_SLEW_RATE_12_36NS = 1, + INV_ICM42607_SLEW_RATE_6_19NS = 2, + INV_ICM42607_SLEW_RATE_4_14NS = 3, + INV_ICM42607_SLEW_RATE_2_6NS = 4, + INV_ICM42607_SLEW_RATE_2NS = 5, + INV_ICM42607_SLEW_RATE_NB +}; + +enum inv_icm42607_sensor_mode { + INV_ICM42607_SENSOR_MODE_OFF = 0, + INV_ICM42607_SENSOR_MODE_STANDBY = 1, + INV_ICM42607_SENSOR_MODE_LOW_POWER = 2, + INV_ICM42607_SENSOR_MODE_LOW_NOISE = 3, + INV_ICM42607_SENSOR_MODE_NB +}; + +/* gyroscope fullscale values */ +enum inv_icm42607_gyro_fs { + INV_ICM42607_GYRO_FS_2000DPS = 0, + INV_ICM42607_GYRO_FS_1000DPS = 1, + INV_ICM42607_GYRO_FS_500DPS = 2, + INV_ICM42607_GYRO_FS_250DPS = 3, + INV_ICM42607_GYRO_FS_NB +}; + +/* accelerometer fullscale values */ +enum inv_icm42607_accel_fs { + INV_ICM42607_ACCEL_FS_16G = 0, + INV_ICM42607_ACCEL_FS_8G = 1, + INV_ICM42607_ACCEL_FS_4G = 2, + INV_ICM42607_ACCEL_FS_2G = 3, + INV_ICM42607_ACCEL_FS_NB +}; + +/* ODR values - Note Gyro does not support ODR less than 12.5Hz */ +enum inv_icm42607_odr { + INV_ICM42607_ODR_1600HZ = 5, + INV_ICM42607_ODR_800HZ = 6, + INV_ICM42607_ODR_400HZ = 7, + INV_ICM42607_ODR_200HZ = 8, + INV_ICM42607_ODR_100HZ = 9, + INV_ICM42607_ODR_50HZ = 10, + INV_ICM42607_ODR_25HZ = 11, + INV_ICM42607_ODR_12_5HZ = 12, + INV_ICM42607_ODR_6_25HZ_LP = 13, + INV_ICM42607_ODR_3_125HZ_LP = 14, + INV_ICM42607_ODR_1_5625HZ_LP = 15, + INV_ICM42607_ODR_NB +}; + +/* Low-Noise mode sensor data filter (bandwidth) */ +enum inv_icm42607_filter_bw { + INV_ICM42607_FILTER_BYPASS = 0, + INV_ICM42607_FILTER_BW_180HZ = 1, + INV_ICM42607_FILTER_BW_121HZ = 2, + INV_ICM42607_FILTER_BW_73HZ = 3, + INV_ICM42607_FILTER_BW_53HZ = 4, + INV_ICM42607_FILTER_BW_34HZ = 5, + INV_ICM42607_FILTER_BW_25HZ = 6, + INV_ICM42607_FILTER_BW_16HZ = 7, + INV_ICM42607_FILTER_BW_NB +}; + +/* Temperature sensor data filter (bandwidth) */ +enum inv_icm42607_temp_filter_bw { + INV_ICM42607_TEMP_FILTER_BYPASS = 0, + INV_ICM42607_TEMP_FILTER_BW_180HZ = 1, + INV_ICM42607_TEMP_FILTER_BW_72HZ = 2, + INV_ICM42607_TEMP_FILTER_BW_34HZ = 3, + INV_ICM42607_TEMP_FILTER_BW_16HZ = 4, + INV_ICM42607_TEMP_FILTER_BW_8HZ = 5, + INV_ICM42607_TEMP_FILTER_BW_4HZ = 6, + /* value 7 also corresponds to 4Hz */ +}; + +/* Signed so that negative values can signify an invalid condition. */ +struct inv_icm42607_sensor_conf { + int mode; + int fs; + int odr; + int filter; +}; + +struct inv_icm42607_conf { + struct inv_icm42607_sensor_conf gyro; + struct inv_icm42607_sensor_conf accel; + ktime_t gyro_stop; /* earliest time to stop the gyro */ +}; + +struct inv_icm42607_hw { + const char *name; + const struct inv_icm42607_conf *conf; + u8 whoami; +}; + +/** + * struct inv_icm42607_state - driver state variables + * @hw: Hardware specific data. + * @lock: lock for serializing multiple registers access. + * @map: regmap pointer. + * @vddio_supply: I/O voltage regulator for the chip. + * @conf: chip sensors configurations. + * @orientation: sensor chip orientation relative to main hardware. + */ +struct inv_icm42607_state { + const struct inv_icm42607_hw *hw; + struct mutex lock; + struct regmap *map; + struct regulator *vddio_supply; + struct inv_icm42607_conf conf; + struct iio_mount_matrix orientation; +}; + +/* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */ + +/* Register Map for User Bank 0 */ +#define INV_ICM42607_REG_MCLK_RDY 0x00 + +#define INV_ICM42607_REG_DEVICE_CONFIG 0x01 +#define INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE BIT(2) +#define INV_ICM42607_DEVICE_CONFIG_SPI_MODE BIT(0) + +#define INV_ICM42607_REG_SIGNAL_PATH_RESET 0x02 +#define INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET BIT(4) +#define INV_ICM42607_SIGNAL_PATH_RESET_FIFO_FLUSH BIT(2) + +#define INV_ICM42607_REG_DRIVE_CONFIG1 0x03 +#define INV_ICM42607_DRIVE_CONFIG1_I3C_DDR_MASK GENMASK(5, 3) +#define INV_ICM42607_DRIVE_CONFIG1_I3C_SDR_MASK GENMASK(2, 0) + +#define INV_ICM42607_REG_DRIVE_CONFIG2 0x04 +#define INV_ICM42607_DRIVE_CONFIG2_I2C_MASK GENMASK(5, 3) +#define INV_ICM42607_DRIVE_CONFIG2_ALL_MASK GENMASK(2, 0) + +#define INV_ICM42607_REG_DRIVE_CONFIG3 0x05 +#define INV_ICM42607_DRIVE_CONFIG3_SPI_MASK GENMASK(2, 0) + +#define INV_ICM42607_REG_INT_CONFIG 0x06 +#define INV_ICM42607_INT_CONFIG_INT2_LATCHED BIT(5) +#define INV_ICM42607_INT_CONFIG_INT2_PUSH_PULL BIT(4) +#define INV_ICM42607_INT_CONFIG_INT2_ACTIVE_HIGH BIT(3) +#define INV_ICM42607_INT_CONFIG_INT2_ACTIVE_LOW 0x00 +#define INV_ICM42607_INT_CONFIG_INT1_LATCHED BIT(2) +#define INV_ICM42607_INT_CONFIG_INT1_PUSH_PULL BIT(1) +#define INV_ICM42607_INT_CONFIG_INT1_ACTIVE_HIGH BIT(0) +#define INV_ICM42607_INT_CONFIG_INT1_ACTIVE_LOW 0x00 + +/* all sensor data are 16 bits (2 registers wide) in big-endian */ +#define INV_ICM42607_REG_TEMP_DATA1 0x09 +#define INV_ICM42607_REG_TEMP_DATA0 0x0A +#define INV_ICM42607_REG_ACCEL_DATA_X1 0x0B +#define INV_ICM42607_REG_ACCEL_DATA_X0 0x0C +#define INV_ICM42607_REG_ACCEL_DATA_Y1 0x0D +#define INV_ICM42607_REG_ACCEL_DATA_Y0 0x0E +#define INV_ICM42607_REG_ACCEL_DATA_Z1 0x0F +#define INV_ICM42607_REG_ACCEL_DATA_Z0 0x10 +#define INV_ICM42607_REG_GYRO_DATA_X1 0x11 +#define INV_ICM42607_REG_GYRO_DATA_X0 0x12 +#define INV_ICM42607_REG_GYRO_DATA_Y1 0x13 +#define INV_ICM42607_REG_GYRO_DATA_Y0 0x14 +#define INV_ICM42607_REG_GYRO_DATA_Z1 0x15 +#define INV_ICM42607_REG_GYRO_DATA_Z0 0x16 +#define INV_ICM42607_DATA_INVALID -32768 + +#define INV_ICM42607_REG_TMST_FSYNCH 0x17 +#define INV_ICM42607_REG_TMST_FSYNCL 0x18 + +/* APEX Data Registers */ +#define INV_ICM42607_REG_APEX_DATA0 0x31 +#define INV_ICM42607_REG_APEX_DATA1 0x32 +#define INV_ICM42607_REG_APEX_DATA2 0x33 +#define INV_ICM42607_REG_APEX_DATA3 0x34 +#define INV_ICM42607_REG_APEX_DATA4 0x1D +#define INV_ICM42607_REG_APEX_DATA5 0x1E + +#define INV_ICM42607_REG_PWR_MGMT0 0x1F +#define INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL BIT(7) +#define INV_ICM42607_PWR_MGMT0_IDLE BIT(4) +#define INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK GENMASK(3, 2) +#define INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK GENMASK(1, 0) + +#define INV_ICM42607_REG_GYRO_CONFIG0 0x20 +#define INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK GENMASK(6, 5) +#define INV_ICM42607_GYRO_CONFIG0_ODR_MASK GENMASK(3, 0) + +#define INV_ICM42607_REG_ACCEL_CONFIG0 0x21 +#define INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK GENMASK(6, 5) +#define INV_ICM42607_ACCEL_CONFIG0_ODR_MASK GENMASK(3, 0) + +#define INV_ICM42607_REG_TEMP_CONFIG0 0x22 +#define INV_ICM42607_TEMP_CONFIG0_FILTER_MASK GENMASK(6, 4) + +#define INV_ICM42607_REG_GYRO_CONFIG1 0x23 +#define INV_ICM42607_GYRO_CONFIG1_FILTER_MASK GENMASK(2, 0) + +#define INV_ICM42607_REG_ACCEL_CONFIG1 0x24 +#define INV_ICM42607_ACCEL_CONFIG1_AVG_MASK GENMASK(6, 4) +#define INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK GENMASK(2, 0) + +#define INV_ICM42607_REG_APEX_CONFIG0 0x25 +#define INV_ICM42607_APEX_CONFIG0_DMP_POWER_SAVE_EN BIT(3) +#define INV_ICM42607_APEX_CONFIG0_DMP_INIT_EN BIT(2) +#define INV_ICM42607_APEX_CONFIG0_DMP_MEM_RESET_EN BIT(0) + +#define INV_ICM42607_REG_APEX_CONFIG1 0x26 +#define INV_ICM42607_APEX_CONFIG1_SMD_ENABLE BIT(6) +#define INV_ICM42607_APEX_CONFIG1_FF_ENABLE BIT(5) +#define INV_ICM42607_APEX_CONFIG1_TILT_ENABLE BIT(4) +#define INV_ICM42607_APEX_CONFIG1_PED_ENABLE BIT(3) +#define INV_ICM42607_APEX_CONFIG1_DMP_ODR_MASK GENMASK(1, 0) + +#define INV_ICM42607_REG_WOM_CONFIG 0x27 +#define INV_ICM42607_WOM_CONFIG_INT_DUR_MASK GENMASK(4, 3) +#define INV_ICM42607_WOM_CONFIG_INT_MODE BIT(2) +#define INV_ICM42607_WOM_CONFIG_MODE BIT(1) +#define INV_ICM42607_WOM_CONFIG_EN BIT(0) + +#define INV_ICM42607_REG_FIFO_CONFIG1 0x28 +#define INV_ICM42607_FIFO_CONFIG1_MODE BIT(1) +#define INV_ICM42607_FIFO_CONFIG1_BYPASS BIT(0) + +#define INV_ICM42607_REG_FIFO_CONFIG2 0x29 +#define INV_ICM42607_REG_FIFO_CONFIG3 0x2A +#define INV_ICM42607_FIFO_WATERMARK_VAL(_wm) \ + cpu_to_le16((_wm) & GENMASK(11, 0)) +/* FIFO is 2048 bytes, let 12 samples for reading latency */ +#define INV_ICM42607_FIFO_WATERMARK_MAX (2048 - 12 * 16) +#define INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE 8 +#define INV_ICM42607_FIFO_2SENSORS_PACKET_SIZE 16 + +#define INV_ICM42607_REG_INT_SOURCE0 0x2B +#define INV_ICM42607_INT_SOURCE0_ST_INT1_EN BIT(7) +#define INV_ICM42607_INT_SOURCE0_FSYNC_INT1_EN BIT(6) +#define INV_ICM42607_INT_SOURCE0_PLL_RDY_INT1_EN BIT(5) +#define INV_ICM42607_INT_SOURCE0_RESET_DONE_INT1_EN BIT(4) +#define INV_ICM42607_INT_SOURCE0_DRDY_INT1_EN BIT(3) +#define INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN BIT(2) +#define INV_ICM42607_INT_SOURCE0_FIFO_FULL_INT1_EN BIT(1) +#define INV_ICM42607_INT_SOURCE0_AGC_RDY_INT1_EN BIT(0) + +#define INV_ICM42607_REG_INT_SOURCE1 0x2C +#define INV_ICM42607_INT_SOURCE1_I3C_ERROR_INT1_EN BIT(6) +#define INV_ICM42607_INT_SOURCE1_SMD_INT1_EN BIT(3) +#define INV_ICM42607_INT_SOURCE1_WOM_INT1_EN GENMASK(2, 0) + +#define INV_ICM42607_REG_INT_SOURCE3 0x2D +#define INV_ICM42607_INT_SOURCE3_ST_INT2_EN BIT(7) +#define INV_ICM42607_INT_SOURCE3_FSYNC_INT2_EN BIT(6) +#define INV_ICM42607_INT_SOURCE3_PLL_RDY_INT2_EN BIT(5) +#define INV_ICM42607_INT_SOURCE3_RESET_DONE_INT2_EN BIT(4) +#define INV_ICM42607_INT_SOURCE3_DRDY_INT2_EN BIT(3) +#define INV_ICM42607_INT_SOURCE3_FIFO_THS_INT2_EN BIT(2) +#define INV_ICM42607_INT_SOURCE3_FIFO_FULL_INT2_EN BIT(1) +#define INV_ICM42607_INT_SOURCE3_AGC_RDY_INT2_EN BIT(0) + +#define INV_ICM42607_REG_INT_SOURCE4 0x2E +#define INV_ICM42607_INT_SOURCE4_I3C_ERROR_INT2_EN BIT(6) +#define INV_ICM42607_INT_SOURCE4_SMD_INT2_EN BIT(3) +#define INV_ICM42607_INT_SOURCE4_WOM_Z_INT2_EN BIT(2) +#define INV_ICM42607_INT_SOURCE4_WOM_Y_INT2_EN BIT(1) +#define INV_ICM42607_INT_SOURCE4_WOM_X_INT2_EN BIT(0) + +#define INV_ICM42607_REG_FIFO_LOST_PKT0 0x2F +#define INV_ICM42607_REG_FIFO_LOST_PKT1 0x30 + +#define INV_ICM42607_REG_INTF_CONFIG0 0x35 +#define INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_FORMAT BIT(6) +#define INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_ENDIAN BIT(5) +#define INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN BIT(4) +#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK GENMASK(1, 0) +#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS 2 +#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS 3 + +#define INV_ICM42607_REG_INTF_CONFIG1 0x36 +#define INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN BIT(3) +#define INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN BIT(2) +#define INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK GENMASK(1, 0) +#define INV_ICM42607_INTF_CONFIG1_CLKSEL_INT 0 +#define INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL 1 +#define INV_ICM42607_INTF_CONFIG1_CLKSEL_OFF 2 + +#define INV_ICM42607_REG_INT_STATUS_DRDY 0x39 +#define INV_ICM42607_INT_STATUS_DRDY_DATA_RDY BIT(0) + +#define INV_ICM42607_REG_INT_STATUS 0x3A +#define INV_ICM42607_INT_STATUS_ST BIT(7) +#define INV_ICM42607_INT_STATUS_FSYNC BIT(6) +#define INV_ICM42607_INT_STATUS_PLL_RDY BIT(5) +#define INV_ICM42607_INT_STATUS_RESET_DONE BIT(4) +#define INV_ICM42607_INT_STATUS_FIFO_THS BIT(2) +#define INV_ICM42607_INT_STATUS_FIFO_FULL BIT(1) +#define INV_ICM42607_INT_STATUS_AGC_RDY BIT(0) + +#define INV_ICM42607_REG_INT_STATUS2 0x3B +#define INV_ICM42607_INT_STATUS2_SMD BIT(3) +#define INV_ICM42607_INT_STATUS2_WOM_INT GENMASK(2, 0) + +#define INV_ICM42607_REG_INT_STATUS3 0x3C +#define INV_ICM42607_INT_STATUS3_STEP_DET BIT(5) +#define INV_ICM42607_INT_STATUS3_STEP_CNT_OVF BIT(4) +#define INV_ICM42607_INT_STATUS3_TILT_DET BIT(3) +#define INV_ICM42607_INT_STATUS3_FF_DET BIT(2) + +/* + * FIFO access registers + * FIFO count is 16 bits (2 registers) big-endian + * FIFO data is a continuous read register to read FIFO content + */ +#define INV_ICM42607_REG_FIFO_COUNTH 0x3D +#define INV_ICM42607_REG_FIFO_COUNTL 0x3E +#define INV_ICM42607_REG_FIFO_DATA 0x3F + +#define INV_ICM42607_REG_WHOAMI 0x75 +#define INV_ICM42607P_WHOAMI 0x60 +#define INV_ICM42607_WHOAMI 0x67 + +/* + * Timings as listed in section 3 of datasheet, all values listed in datasheet + * in ms except temp startup time... setting all values in us and using + * USEC_PER_MSEC to convert from values displayed in datasheet. + */ +#define INV_ICM42607_POWER_UP_TIME_US (100 * USEC_PER_MSEC) +#define INV_ICM42607_RESET_TIME_US (1 * USEC_PER_MSEC) +#define INV_ICM42607_ACCEL_STARTUP_TIME_US (10 * USEC_PER_MSEC) +#define INV_ICM42607_GYRO_STARTUP_TIME_US (30 * USEC_PER_MSEC) +#define INV_ICM42607_GYRO_STOP_TIME_US (45 * USEC_PER_MSEC) +#define INV_ICM42607_TEMP_STARTUP_TIME_US 77 + +typedef int (*inv_icm42607_bus_setup)(struct inv_icm42607_state *); + +extern const struct regmap_config inv_icm42607_regmap_config; +extern const struct inv_icm42607_hw inv_icm42607_hw_data; +extern const struct inv_icm42607_hw inv_icm42607p_hw_data; + +int inv_icm42607_core_probe(struct regmap *regmap, + const struct inv_icm42607_hw *hw, + inv_icm42607_bus_setup bus_setup); + +#endif diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file mode 100644 index 000000000000..6ec1d730017e --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inv_icm42607.h" + +static bool inv_icm42607_is_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case INV_ICM42607_REG_MCLK_RDY ... INV_ICM42607_REG_INT_CONFIG: + case INV_ICM42607_REG_TEMP_DATA1 ... INV_ICM42607_REG_TMST_FSYNCL: + case INV_ICM42607_REG_APEX_DATA4 ... INV_ICM42607_REG_INTF_CONFIG1: + case INV_ICM42607_REG_INT_STATUS_DRDY ... INV_ICM42607_REG_FIFO_DATA: + case INV_ICM42607_REG_WHOAMI: + return true; + } + + return false; +} + +static bool inv_icm42607_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case INV_ICM42607_REG_DEVICE_CONFIG ... INV_ICM42607_REG_INT_CONFIG: + case INV_ICM42607_REG_PWR_MGMT0 ... INV_ICM42607_REG_INT_SOURCE4: + case INV_ICM42607_REG_INTF_CONFIG0 ... INV_ICM42607_REG_INTF_CONFIG1: + return true; + } + + return false; +} + +static bool inv_icm42607_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case INV_ICM42607_REG_MCLK_RDY: + case INV_ICM42607_REG_SIGNAL_PATH_RESET: + case INV_ICM42607_REG_TEMP_DATA1 ... INV_ICM42607_REG_APEX_DATA5: + case INV_ICM42607_REG_APEX_CONFIG0: + case INV_ICM42607_REG_FIFO_LOST_PKT0 ... INV_ICM42607_REG_APEX_DATA3: + case INV_ICM42607_REG_INT_STATUS_DRDY: + case INV_ICM42607_REG_INT_STATUS ... INV_ICM42607_REG_FIFO_DATA: + return true; + } + + return false; +} + +const struct regmap_config inv_icm42607_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = inv_icm42607_is_writeable_reg, + .readable_reg = inv_icm42607_is_readable_reg, + .volatile_reg = inv_icm42607_is_volatile_reg, + .max_register = INV_ICM42607_REG_WHOAMI, + .cache_type = REGCACHE_MAPLE, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm42607_regmap_config, "IIO_ICM42607"); + +/* chip initial default configuration */ +static const struct inv_icm42607_conf inv_icm42607_default_conf = { + .gyro = { + .mode = INV_ICM42607_SENSOR_MODE_OFF, + .fs = INV_ICM42607_GYRO_FS_1000DPS, + .odr = INV_ICM42607_ODR_100HZ, + .filter = INV_ICM42607_FILTER_BW_25HZ, + }, + .accel = { + .mode = INV_ICM42607_SENSOR_MODE_OFF, + .fs = INV_ICM42607_ACCEL_FS_4G, + .odr = INV_ICM42607_ODR_100HZ, + .filter = INV_ICM42607_FILTER_BW_25HZ, + }, +}; + +const struct inv_icm42607_hw inv_icm42607_hw_data = { + .whoami = INV_ICM42607_WHOAMI, + .name = "icm42607", + .conf = &inv_icm42607_default_conf, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm42607_hw_data, "IIO_ICM42607"); + +const struct inv_icm42607_hw inv_icm42607p_hw_data = { + .whoami = INV_ICM42607P_WHOAMI, + .name = "icm42607p", + .conf = &inv_icm42607_default_conf, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607"); + +static int inv_icm42607_set_init_conf(struct inv_icm42607_state *st, + const struct inv_icm42607_conf *conf) +{ + unsigned int val; + int ret; + + val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, conf->gyro.mode); + val |= FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, conf->accel.mode); + ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK, conf->gyro.fs); + val |= FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_ODR_MASK, conf->gyro.odr); + ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK, conf->accel.fs); + val |= FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_ODR_MASK, conf->accel.odr); + ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, conf->gyro.filter); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_GYRO_CONFIG1, + INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, conf->accel.filter); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1, + INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_TEMP_CONFIG0_FILTER_MASK, + INV_ICM42607_TEMP_FILTER_BW_34HZ); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_TEMP_CONFIG0, + INV_ICM42607_TEMP_CONFIG0_FILTER_MASK, val); + if (ret) + return ret; + + st->conf = *conf; + + return 0; +} + +static int inv_icm42607_setup(struct inv_icm42607_state *st, + inv_icm42607_bus_setup inv_icm42607_bus_setup) +{ + const struct device *dev = regmap_get_device(st->map); + unsigned int val; + int ret; + + ret = regmap_read(st->map, INV_ICM42607_REG_WHOAMI, &val); + if (ret) + return ret; + + /* Warn, but don't fail. */ + if (val != st->hw->whoami) + dev_warn(dev, "Unknown whoami %#02x expected %#02x (%s)\n", + val, st->hw->whoami, st->hw->name); + + ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET, + INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET); + if (ret) + return ret; + + fsleep(1 * USEC_PER_MSEC); + + /* + * No polling interval specified in datasheet, so use reset time as + * polling interval and 10x reset time as timeout period. + */ + ret = regmap_read_poll_timeout(st->map, INV_ICM42607_REG_INT_STATUS, + val, val & INV_ICM42607_INT_STATUS_RESET_DONE, + 1 * USEC_PER_MSEC, 10 * USEC_PER_MSEC); + if (ret) + return dev_err_probe(dev, ret, + "reset error, reset done bit not set\n"); + + /* Sync the regcache again after a reset. */ + regcache_mark_dirty(st->map); + ret = regcache_sync(st->map); + if (ret) + return ret; + + ret = inv_icm42607_bus_setup(st); + if (ret) + return ret; + + ret = regmap_set_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0, + INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK, + INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1, + INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK, + val); + if (ret) + return ret; + + return inv_icm42607_set_init_conf(st, st->hw->conf); +} + +static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st) +{ + int ret; + + ret = regulator_enable(st->vddio_supply); + if (ret) + return ret; + + fsleep(INV_ICM42607_POWER_UP_TIME_US); + + return 0; +} + +static void inv_icm42607_disable_vddio_reg(void *_data) +{ + struct inv_icm42607_state *st = _data; + + regulator_disable(st->vddio_supply); +} + +int inv_icm42607_core_probe(struct regmap *regmap, + const struct inv_icm42607_hw *hw, + inv_icm42607_bus_setup inv_icm42607_bus_setup) +{ + struct device *dev = regmap_get_device(regmap); + struct inv_icm42607_state *st; + int ret; + + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + st->hw = hw; + st->map = regmap; + + ret = iio_read_mount_matrix(dev, &st->orientation); + if (ret) + return dev_err_probe(dev, ret, + "failed to retrieve mounting matrix\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get vdd regulator\n"); + + st->vddio_supply = devm_regulator_get(dev, "vddio"); + if (IS_ERR(st->vddio_supply)) + return dev_err_probe(dev, PTR_ERR(st->vddio_supply), + "Failed to get vddio regulator\n"); + + ret = inv_icm42607_enable_vddio_reg(st); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, inv_icm42607_disable_vddio_reg, st); + if (ret) + return ret; + + /* Setup chip registers (includes WHOAMI check, reset check, bus setup) */ + ret = inv_icm42607_setup(st, inv_icm42607_bus_setup); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607"); + +MODULE_AUTHOR("InvenSense, Inc."); +MODULE_DESCRIPTION("InvenSense ICM-42607 device driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c new file mode 100644 index 000000000000..f2b9067815b0 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include +#include +#include +#include +#include +#include + +#include "inv_icm42607.h" + +static int inv_icm42607_i2c_bus_setup(struct inv_icm42607_state *st) +{ + unsigned int val; + int ret; + + ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1, + INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN | + INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_DRIVE_CONFIG2_I2C_MASK, + INV_ICM42607_SLEW_RATE_12_36NS); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG2, + INV_ICM42607_DRIVE_CONFIG2_I2C_MASK, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS); + + return regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0, + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, + val); +} + +static int inv_icm42607_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + const struct inv_icm42607_hw *hw; + struct regmap *regmap; + + hw = i2c_get_match_data(client); + if (!hw) + return dev_err_probe(dev, -ENODEV, "Failed to get i2c data\n"); + + regmap = devm_regmap_init_i2c(client, &inv_icm42607_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to register i2c regmap\n"); + + return inv_icm42607_core_probe(regmap, hw, inv_icm42607_i2c_bus_setup); +} + +static const struct i2c_device_id inv_icm42607_id[] = { + { + .name = "icm42607", + .driver_data = (kernel_ulong_t)&inv_icm42607_hw_data, + }, { + .name = "icm42607p", + .driver_data = (kernel_ulong_t)&inv_icm42607p_hw_data, + }, + { } +}; +MODULE_DEVICE_TABLE(i2c, inv_icm42607_id); + +static const struct of_device_id inv_icm42607_of_matches[] = { + { + .compatible = "invensense,icm42607", + .data = &inv_icm42607_hw_data, + }, { + .compatible = "invensense,icm42607p", + .data = &inv_icm42607p_hw_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, inv_icm42607_of_matches); + +static struct i2c_driver inv_icm42607_driver = { + .driver = { + .name = "inv-icm42607-i2c", + .of_match_table = inv_icm42607_of_matches, + }, + .id_table = inv_icm42607_id, + .probe = inv_icm42607_probe, +}; +module_i2c_driver(inv_icm42607_driver); + +MODULE_AUTHOR("InvenSense, Inc."); +MODULE_DESCRIPTION("InvenSense ICM-42607 I2C driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_ICM42607"); From 337cb9a3fa7ffbd8594d6031f852894219d9c2ce Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Tue, 28 Jul 2026 17:55:35 -0500 Subject: [PATCH 445/513] iio: imu: inv_icm42607: Add SPI For icm42607 Add SPI driver support for InvenSense ICM-42607 devices. Signed-off-by: Chris Morgan Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_icm42607/Kconfig | 12 ++ drivers/iio/imu/inv_icm42607/Makefile | 3 + .../iio/imu/inv_icm42607/inv_icm42607_spi.c | 106 ++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c diff --git a/drivers/iio/imu/inv_icm42607/Kconfig b/drivers/iio/imu/inv_icm42607/Kconfig index 083c212087ab..23f461f57afc 100644 --- a/drivers/iio/imu/inv_icm42607/Kconfig +++ b/drivers/iio/imu/inv_icm42607/Kconfig @@ -16,3 +16,15 @@ config INV_ICM42607_I2C This driver can be built as a module. The module will be called inv-icm42607-i2c. + +config INV_ICM42607_SPI + tristate "InvenSense ICM-42607 SPI driver" + depends on SPI_MASTER + select INV_ICM42607 + select REGMAP_SPI + help + This driver supports the InvenSense ICM-42607 motion tracking + device over SPI. + + This driver can be built as a module. The module will be called + inv-icm42607-spi. diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile index 32046e2727d7..be109102e203 100644 --- a/drivers/iio/imu/inv_icm42607/Makefile +++ b/drivers/iio/imu/inv_icm42607/Makefile @@ -5,3 +5,6 @@ inv-icm42607-y += inv_icm42607_core.o obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o inv-icm42607-i2c-y += inv_icm42607_i2c.o + +obj-$(CONFIG_INV_ICM42607_SPI) += inv-icm42607-spi.o +inv-icm42607-spi-y += inv_icm42607_spi.o diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c new file mode 100644 index 000000000000..eb04036a6712 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include +#include +#include +#include +#include +#include + +#include "inv_icm42607.h" + +static int inv_icm42607_spi_bus_setup(struct inv_icm42607_state *st) +{ + unsigned int val; + int ret; + + /* Only support 4-wire mode for now. */ + ret = regmap_set_bits(st->map, INV_ICM42607_REG_DEVICE_CONFIG, + INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE); + if (ret) + return ret; + + ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1, + INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN | + INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_DRIVE_CONFIG3_SPI_MASK, + INV_ICM42607_SLEW_RATE_2NS); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG3, + INV_ICM42607_DRIVE_CONFIG3_SPI_MASK, val); + if (ret) + return ret; + + val = FIELD_PREP(INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS); + + return regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0, + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, + val); +} + +static int inv_icm42607_probe(struct spi_device *spi) +{ + const struct inv_icm42607_hw *hw; + struct device *dev = &spi->dev; + struct regmap *regmap; + + hw = spi_get_device_match_data(spi); + if (!hw) + return dev_err_probe(dev, -ENODATA, "Failed to get SPI data\n"); + + if (spi->mode & SPI_3WIRE) + return dev_err_probe(dev, -ENODEV, "SPI 3-wire mode not supported\n"); + + regmap = devm_regmap_init_spi(spi, &inv_icm42607_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to register spi regmap\n"); + + return inv_icm42607_core_probe(regmap, hw, inv_icm42607_spi_bus_setup); +} + +static const struct spi_device_id inv_icm42607_spi_id_table[] = { + { + .name = "icm42607", + .driver_data = (kernel_ulong_t)&inv_icm42607_hw_data, + }, { + .name = "icm42607p", + .driver_data = (kernel_ulong_t)&inv_icm42607p_hw_data, + }, + { } +}; +MODULE_DEVICE_TABLE(spi, inv_icm42607_spi_id_table); + +static const struct of_device_id inv_icm42607_of_matches[] = { + { + .compatible = "invensense,icm42607", + .data = &inv_icm42607_hw_data, + }, + { + .compatible = "invensense,icm42607p", + .data = &inv_icm42607p_hw_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, inv_icm42607_of_matches); + +static struct spi_driver inv_icm42607_driver = { + .driver = { + .name = "inv-icm42607-spi", + .of_match_table = inv_icm42607_of_matches, + }, + .id_table = inv_icm42607_spi_id_table, + .probe = inv_icm42607_probe, +}; +module_spi_driver(inv_icm42607_driver); + +MODULE_AUTHOR("InvenSense, Inc."); +MODULE_DESCRIPTION("InvenSense ICM-42607 SPI driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_ICM42607"); From 3007c1530f96f2a2ecf8ca545d1feddc40c8451b Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Tue, 28 Jul 2026 17:55:36 -0500 Subject: [PATCH 446/513] iio: imu: inv_icm42607: Add PM support for icm42607 Add power management support for the ICM42607 device driver. Signed-off-by: Chris Morgan Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_icm42607/inv_icm42607.h | 14 ++ .../iio/imu/inv_icm42607/inv_icm42607_core.c | 187 ++++++++++++++++++ .../iio/imu/inv_icm42607/inv_icm42607_i2c.c | 1 + .../iio/imu/inv_icm42607/inv_icm42607_spi.c | 1 + 4 files changed, 203 insertions(+) diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h index 169e848caf2e..61927a6c2663 100644 --- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -125,6 +126,7 @@ struct inv_icm42607_hw { * @lock: lock for serializing multiple registers access. * @map: regmap pointer. * @vddio_supply: I/O voltage regulator for the chip. + * @vddio_en: I/O voltage status for runtime PM. * @conf: chip sensors configurations. * @orientation: sensor chip orientation relative to main hardware. */ @@ -133,6 +135,7 @@ struct inv_icm42607_state { struct mutex lock; struct regmap *map; struct regulator *vddio_supply; + bool vddio_en; struct inv_icm42607_conf conf; struct iio_mount_matrix orientation; }; @@ -352,11 +355,22 @@ struct inv_icm42607_state { #define INV_ICM42607_GYRO_STOP_TIME_US (45 * USEC_PER_MSEC) #define INV_ICM42607_TEMP_STARTUP_TIME_US 77 +/* + * Suspend delay assumed from other icm42600 series device, not + * documented in datasheet. + */ +#define INV_ICM42607_SUSPEND_DELAY_MS (2 * MSEC_PER_SEC) + typedef int (*inv_icm42607_bus_setup)(struct inv_icm42607_state *); extern const struct regmap_config inv_icm42607_regmap_config; extern const struct inv_icm42607_hw inv_icm42607_hw_data; extern const struct inv_icm42607_hw inv_icm42607p_hw_data; +extern const struct dev_pm_ops inv_icm42607_pm_ops; + +int inv_icm42607_get_pwr_mgmt0(struct inv_icm42607_state *st, + enum inv_icm42607_sensor_mode *gyro, + enum inv_icm42607_sensor_mode *accel); int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw *hw, diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c index 6ec1d730017e..af301bde3f17 100644 --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c @@ -4,15 +4,19 @@ */ #include +#include #include #include #include #include +#include #include #include +#include #include #include #include +#include #include #include "inv_icm42607.h" @@ -100,6 +104,92 @@ const struct inv_icm42607_hw inv_icm42607p_hw_data = { }; EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607"); +int inv_icm42607_get_pwr_mgmt0(struct inv_icm42607_state *st, + enum inv_icm42607_sensor_mode *gyro, + enum inv_icm42607_sensor_mode *accel) +{ + unsigned int val; + int ret; + + ret = regmap_read(st->map, INV_ICM42607_REG_PWR_MGMT0, &val); + if (ret) + return ret; + + *gyro = FIELD_GET(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, val); + *accel = FIELD_GET(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, val); + + return 0; +} + +static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st, + enum inv_icm42607_sensor_mode gyro, + enum inv_icm42607_sensor_mode accel) +{ + enum inv_icm42607_sensor_mode oldaccel, oldgyro; + unsigned int sleepval_us; + unsigned int val; + s64 disable_wait; + int ret; + + ret = inv_icm42607_get_pwr_mgmt0(st, &oldgyro, &oldaccel); + if (ret) + return ret; + + if (gyro == oldgyro && accel == oldaccel) + return 0; + + /* + * Datasheet on page 14.26 says we need to ensure the gyro sensor is on + * for a minimum of 45ms. So if we transition from an on state to an + * off state make sure at least 45ms have passed before power off and + * wait if it hasn't. In case some platforms don't respond well to a + * sleep of 0, make sure the fsleep duration is > 0. + */ + if (!gyro && oldgyro) { + disable_wait = clamp(ktime_us_delta(st->conf.gyro_stop, ktime_get()), + 0, INV_ICM42607_GYRO_STOP_TIME_US); + + if (disable_wait > 0) + fsleep(disable_wait); + } + + val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, gyro) | + FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, accel); + ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val); + if (ret) + return ret; + + /* + * If a state change occurs from off to on, sleep for the startup time + * of the sensor. Since more than one sensor can be transitioned from + * off to on, select the maximum time from each of the sensors changing + * from off to on. The startup time for the temp sensor is considerably + * smaller than the startup time for the other sensors and one or more + * are required to be on for the temp sensor to function, so any start + * delay should be enough. + */ + sleepval_us = 0; + if (accel && !oldaccel) + sleepval_us = max(sleepval_us, INV_ICM42607_ACCEL_STARTUP_TIME_US); + + if (gyro && !oldgyro) { + sleepval_us = max(sleepval_us, INV_ICM42607_GYRO_STARTUP_TIME_US); + /* Track the earliest we can turn off the gyroscope. */ + st->conf.gyro_stop = ktime_add_us(ktime_get(), + INV_ICM42607_GYRO_STOP_TIME_US); + } + + /* + * Only sleep if sleepval_us is greater than 0 in case some platforms + * have issues with a 0 delay. The 0 delay can happen if one or both + * sensors is shut down. + */ + if (sleepval_us > 0) + fsleep(sleepval_us); + + return 0; +} + static int inv_icm42607_set_init_conf(struct inv_icm42607_state *st, const struct inv_icm42607_conf *conf) { @@ -212,20 +302,47 @@ static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st) { int ret; + if (st->vddio_en) + return 0; + ret = regulator_enable(st->vddio_supply); if (ret) return ret; fsleep(INV_ICM42607_POWER_UP_TIME_US); + st->vddio_en = true; + return 0; } +static void inv_icm42607_sensors_off(void *_data) +{ + struct inv_icm42607_state *st = _data; + const struct device *dev = regmap_get_device(st->map); + int ret; + + guard(mutex)(&st->lock); + + st->conf.gyro.mode = INV_ICM42607_SENSOR_MODE_OFF; + st->conf.accel.mode = INV_ICM42607_SENSOR_MODE_OFF; + + ret = inv_icm42607_set_pwr_mgmt0(st, st->conf.gyro.mode, + st->conf.accel.mode); + if (ret) + dev_err(dev, "Unable to turn off sensors\n"); +} + static void inv_icm42607_disable_vddio_reg(void *_data) { struct inv_icm42607_state *st = _data; + if (!st->vddio_en) + return; + regulator_disable(st->vddio_supply); + + st->vddio_en = false; } int inv_icm42607_core_probe(struct regmap *regmap, @@ -240,6 +357,8 @@ int inv_icm42607_core_probe(struct regmap *regmap, if (!st) return -ENOMEM; + dev_set_drvdata(dev, st); + ret = devm_mutex_init(dev, &st->lock); if (ret) return ret; @@ -275,10 +394,78 @@ int inv_icm42607_core_probe(struct regmap *regmap, if (ret) return ret; + /* + * Ensure if sensors get turned on at some point, they're turned off + * as part of teardown. + */ + ret = devm_add_action_or_reset(dev, inv_icm42607_sensors_off, st); + if (ret) + return ret; + + ret = devm_pm_runtime_set_active_enabled(dev); + if (ret) + return ret; + + pm_runtime_set_autosuspend_delay(dev, INV_ICM42607_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + return 0; } EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607"); +static int inv_icm42607_suspend(struct device *dev) +{ + struct inv_icm42607_state *st = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_suspend(dev); + if (ret) + return ret; + + inv_icm42607_disable_vddio_reg(st); + + return 0; +} + +static int inv_icm42607_resume(struct device *dev) +{ + struct inv_icm42607_state *st = dev_get_drvdata(dev); + int ret; + + ret = inv_icm42607_enable_vddio_reg(st); + if (ret) + return ret; + + /* Sync the regcache again after regulator shutdown. */ + regcache_mark_dirty(st->map); + ret = regcache_sync(st->map); + if (ret) + return ret; + + return pm_runtime_force_resume(dev); +} + +static int inv_icm42607_runtime_suspend(struct device *dev) +{ + struct inv_icm42607_state *st = dev_get_drvdata(dev); + + /* + * Set sensors state to off. Since we only support one-shot + * today we can use runtime PM to turn sensors off when not + * in use, and then when needed the reads/writes will + * re-enable the sensors as needed. This reduces complexity, + * however the tradeoff is that an unused sensor won't be + * turned off until the entire chip is no longer in use. + */ + inv_icm42607_sensors_off(st); + return 0; +} + +EXPORT_NS_GPL_DEV_PM_OPS(inv_icm42607_pm_ops, IIO_ICM42607) = { + SYSTEM_SLEEP_PM_OPS(inv_icm42607_suspend, inv_icm42607_resume) + RUNTIME_PM_OPS(inv_icm42607_runtime_suspend, NULL, NULL) +}; + MODULE_AUTHOR("InvenSense, Inc."); MODULE_DESCRIPTION("InvenSense ICM-42607 device driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c index f2b9067815b0..e903106af84a 100644 --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c @@ -84,6 +84,7 @@ static struct i2c_driver inv_icm42607_driver = { .driver = { .name = "inv-icm42607-i2c", .of_match_table = inv_icm42607_of_matches, + .pm = pm_ptr(&inv_icm42607_pm_ops), }, .id_table = inv_icm42607_id, .probe = inv_icm42607_probe, diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c index eb04036a6712..cd739375f6b2 100644 --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c @@ -94,6 +94,7 @@ static struct spi_driver inv_icm42607_driver = { .driver = { .name = "inv-icm42607-spi", .of_match_table = inv_icm42607_of_matches, + .pm = pm_ptr(&inv_icm42607_pm_ops), }, .id_table = inv_icm42607_spi_id_table, .probe = inv_icm42607_probe, From cbf992414e30319603b85de0c1d6882cfc53750f Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Tue, 28 Jul 2026 17:55:37 -0500 Subject: [PATCH 447/513] iio: imu: inv_icm42607: Add Accelerometer for icm42607 Add icm42607 accelerometer sensor for icm42607. Signed-off-by: Chris Morgan Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_icm42607/Makefile | 1 + drivers/iio/imu/inv_icm42607/inv_icm42607.h | 38 +++ .../iio/imu/inv_icm42607/inv_icm42607_accel.c | 308 ++++++++++++++++++ .../iio/imu/inv_icm42607/inv_icm42607_core.c | 232 ++++++++++++- 4 files changed, 576 insertions(+), 3 deletions(-) create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile index be109102e203..372c6d6bdcec 100644 --- a/drivers/iio/imu/inv_icm42607/Makefile +++ b/drivers/iio/imu/inv_icm42607/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o inv-icm42607-y += inv_icm42607_core.o +inv-icm42607-y += inv_icm42607_accel.o obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o inv-icm42607-i2c-y += inv_icm42607_i2c.o diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h index 61927a6c2663..fd3e57ee4f14 100644 --- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h @@ -88,6 +88,17 @@ enum inv_icm42607_filter_bw { INV_ICM42607_FILTER_BW_NB }; +/* Low-Power mode sensor data filter (averaging) */ +enum inv_icm42607_filter_avg { + INV_ICM42607_FILTER_AVG_2X = 0, + INV_ICM42607_FILTER_AVG_4X = 1, + INV_ICM42607_FILTER_AVG_8X = 2, + INV_ICM42607_FILTER_AVG_16X = 3, + INV_ICM42607_FILTER_AVG_32X = 4, + INV_ICM42607_FILTER_AVG_64X = 5, + /* values 6 and 7 also correspond to 64x. */ +}; + /* Temperature sensor data filter (bandwidth) */ enum inv_icm42607_temp_filter_bw { INV_ICM42607_TEMP_FILTER_BYPASS = 0, @@ -107,6 +118,7 @@ struct inv_icm42607_sensor_conf { int odr; int filter; }; +#define INV_ICM42607_SENSOR_CONF_INIT { -1, -1, -1, -1 } struct inv_icm42607_conf { struct inv_icm42607_sensor_conf gyro; @@ -125,6 +137,7 @@ struct inv_icm42607_hw { * @hw: Hardware specific data. * @lock: lock for serializing multiple registers access. * @map: regmap pointer. + * @indio_accel: accelerometer IIO device. * @vddio_supply: I/O voltage regulator for the chip. * @vddio_en: I/O voltage status for runtime PM. * @conf: chip sensors configurations. @@ -134,12 +147,23 @@ struct inv_icm42607_state { const struct inv_icm42607_hw *hw; struct mutex lock; struct regmap *map; + struct iio_dev *indio_accel; struct regulator *vddio_supply; bool vddio_en; struct inv_icm42607_conf conf; struct iio_mount_matrix orientation; }; +/** + * struct inv_icm42607_sensor_state - sensor state variables + * @power_mode: sensor requested power mode (for common frequencies) + * @filter: sensor filter. + */ +struct inv_icm42607_sensor_state { + enum inv_icm42607_sensor_mode power_mode; + int filter; +}; + /* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */ /* Register Map for User Bank 0 */ @@ -368,12 +392,26 @@ extern const struct inv_icm42607_hw inv_icm42607_hw_data; extern const struct inv_icm42607_hw inv_icm42607p_hw_data; extern const struct dev_pm_ops inv_icm42607_pm_ops; +const struct iio_mount_matrix * +inv_icm42607_get_mount_matrix(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan); + int inv_icm42607_get_pwr_mgmt0(struct inv_icm42607_state *st, enum inv_icm42607_sensor_mode *gyro, enum inv_icm42607_sensor_mode *accel); +int inv_icm42607_set_sensor_conf(struct inv_icm42607_state *st, + struct inv_icm42607_sensor_conf *conf, + enum iio_chan_type chan_type); + +int inv_icm42607_read_sensor(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + s16 *val); + int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw *hw, inv_icm42607_bus_setup bus_setup); +struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st); + #endif diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file mode 100644 index 000000000000..f563c7708420 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inv_icm42607.h" + +#define INV_ICM42607_ACCEL_CHAN(_modifier, _index, _ext_info) \ +{ \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = _modifier, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + .ext_info = _ext_info, \ +} + +enum inv_icm42607_accel_scan { + INV_ICM42607_ACCEL_SCAN_X, + INV_ICM42607_ACCEL_SCAN_Y, + INV_ICM42607_ACCEL_SCAN_Z, +}; + +static const struct iio_chan_spec_ext_info inv_icm42607_accel_ext_infos[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42607_get_mount_matrix), + { } +}; + +static const struct iio_chan_spec inv_icm42607_accel_channels[] = { + INV_ICM42607_ACCEL_CHAN(IIO_MOD_X, INV_ICM42607_ACCEL_SCAN_X, + inv_icm42607_accel_ext_infos), + INV_ICM42607_ACCEL_CHAN(IIO_MOD_Y, INV_ICM42607_ACCEL_SCAN_Y, + inv_icm42607_accel_ext_infos), + INV_ICM42607_ACCEL_CHAN(IIO_MOD_Z, INV_ICM42607_ACCEL_SCAN_Z, + inv_icm42607_accel_ext_infos), +}; + +static const int inv_icm42607_accel_scale_nano[][2] = { + [INV_ICM42607_ACCEL_FS_16G] = { 0, 4788403 }, + [INV_ICM42607_ACCEL_FS_8G] = { 0, 2394202 }, + [INV_ICM42607_ACCEL_FS_4G] = { 0, 1197101 }, + [INV_ICM42607_ACCEL_FS_2G] = { 0, 598550 }, +}; + +static int inv_icm42607_accel_read_scale(struct iio_dev *indio_dev, + int *val, int *val2) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + unsigned int idx; + + guard(mutex)(&st->lock); + + idx = st->conf.accel.fs; + + *val = inv_icm42607_accel_scale_nano[idx][0]; + *val2 = inv_icm42607_accel_scale_nano[idx][1]; + return IIO_VAL_INT_PLUS_NANO; +} + +static int inv_icm42607_accel_write_scale(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + size_t scales_len = ARRAY_SIZE(inv_icm42607_accel_scale_nano); + struct device *dev = regmap_get_device(st->map); + unsigned int idx; + int ret; + + for (idx = 0; idx < scales_len; idx++) { + if (val == inv_icm42607_accel_scale_nano[idx][0] && + val2 == inv_icm42607_accel_scale_nano[idx][1]) + break; + } + if (idx == scales_len) + return -EINVAL; + + conf.fs = idx; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + return inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL); +} + +/* IIO format int + micro , values 0-4 reserved. */ +static const int inv_icm42607_accel_odr[][2] = { + [INV_ICM42607_ODR_1600HZ] = { 1600, 0 }, + [INV_ICM42607_ODR_800HZ] = { 800, 0 }, + [INV_ICM42607_ODR_400HZ] = { 400, 0 }, + [INV_ICM42607_ODR_200HZ] = { 200, 0 }, + [INV_ICM42607_ODR_100HZ] = { 100, 0 }, + [INV_ICM42607_ODR_50HZ] = { 50, 0 }, + [INV_ICM42607_ODR_25HZ] = { 25, 0 }, + [INV_ICM42607_ODR_12_5HZ] = { 12, 500000 }, + [INV_ICM42607_ODR_6_25HZ_LP] = { 6, 250000 }, + [INV_ICM42607_ODR_3_125HZ_LP] = { 3, 125000 }, + [INV_ICM42607_ODR_1_5625HZ_LP] = { 1, 562500 }, +}; + +static int inv_icm42607_accel_read_odr(struct inv_icm42607_state *st, + int *val, int *val2) +{ + unsigned int odr; + unsigned int i; + + guard(mutex)(&st->lock); + + odr = st->conf.accel.odr; + + for (i = INV_ICM42607_ODR_1600HZ; i < ARRAY_SIZE(inv_icm42607_accel_odr); i++) { + if (i == odr) + break; + } + if (i == ARRAY_SIZE(inv_icm42607_accel_odr)) + return -EINVAL; + + *val = inv_icm42607_accel_odr[i][0]; + *val2 = inv_icm42607_accel_odr[i][1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int inv_icm42607_accel_write_odr(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + struct device *dev = regmap_get_device(st->map); + unsigned int idx; + int ret; + + for (idx = INV_ICM42607_ODR_1600HZ; + idx < ARRAY_SIZE(inv_icm42607_accel_odr); idx++) { + if (val == inv_icm42607_accel_odr[idx][0] && + val2 == inv_icm42607_accel_odr[idx][1]) + break; + } + if (idx == ARRAY_SIZE(inv_icm42607_accel_odr)) + return -EINVAL; + + conf.odr = idx; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + return inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL); +} + +static int inv_icm42607_accel_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + s16 data; + int ret; + + switch (chan->type) { + case IIO_ACCEL: + break; + default: + return -EINVAL; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = inv_icm42607_read_sensor(indio_dev, chan, &data); + if (ret) + return ret; + *val = data; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return inv_icm42607_accel_read_scale(indio_dev, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm42607_accel_read_odr(st, val, val2); + default: + return -EINVAL; + } +} + +static int inv_icm42607_accel_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, + int *type, int *length, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ACCEL) + return -EINVAL; + *vals = (const int *)inv_icm42607_accel_scale_nano; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(inv_icm42607_accel_scale_nano) * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (const int *)inv_icm42607_accel_odr[INV_ICM42607_ODR_1600HZ]; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = (ARRAY_SIZE(inv_icm42607_accel_odr) - + INV_ICM42607_ODR_1600HZ) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int inv_icm42607_accel_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ACCEL) + return -EINVAL; + ret = inv_icm42607_accel_write_scale(indio_dev, val, val2); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm42607_accel_write_odr(indio_dev, val, val2); + default: + return -EINVAL; + } +} + +static int inv_icm42607_accel_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ACCEL) + return -EINVAL; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info inv_icm42607_accel_info = { + .read_raw = inv_icm42607_accel_read_raw, + .read_avail = inv_icm42607_accel_read_avail, + .write_raw = inv_icm42607_accel_write_raw, + .write_raw_get_fmt = inv_icm42607_accel_write_raw_get_fmt, +}; + +struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st) +{ + struct device *dev = regmap_get_device(st->map); + struct inv_icm42607_sensor_state *accel_st; + struct iio_dev *indio_dev; + const char *name; + int ret; + + name = devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->hw->name); + if (!name) + return ERR_PTR(-ENOMEM); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*accel_st)); + if (!indio_dev) + return ERR_PTR(-ENOMEM); + + accel_st = iio_priv(indio_dev); + accel_st->power_mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE; + accel_st->filter = INV_ICM42607_FILTER_BW_73HZ; + + indio_dev->name = name; + indio_dev->info = &inv_icm42607_accel_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = inv_icm42607_accel_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_icm42607_accel_channels); + iio_device_set_drvdata(indio_dev, st); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ERR_PTR(ret); + + return indio_dev; +} diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c index af301bde3f17..68e84597241a 100644 --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c @@ -19,6 +19,8 @@ #include #include +#include + #include "inv_icm42607.h" static bool inv_icm42607_is_readable_reg(struct device *dev, unsigned int reg) @@ -104,6 +106,49 @@ const struct inv_icm42607_hw inv_icm42607p_hw_data = { }; EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607"); +const struct iio_mount_matrix * +inv_icm42607_get_mount_matrix(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + const struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + + return &st->orientation; +} + +static u32 inv_icm42607_odr_to_period_us(enum inv_icm42607_odr odr) +{ + static const u32 odr_periods[INV_ICM42607_ODR_NB] = { + /* Reserved values */ + 0, 0, 0, 0, 0, + /* 1600Hz */ + 625, + /* 800Hz */ + 1250, + /* 400Hz */ + 2500, + /* 200Hz */ + 5000, + /* 100 Hz */ + 10000, + /* 50Hz */ + 20000, + /* 25Hz */ + 40000, + /* 12.5Hz */ + 80000, + /* 6.25Hz */ + 160000, + /* 3.125Hz */ + 320000, + /* 1.5625Hz */ + 640000, + }; + + odr = clamp(odr, INV_ICM42607_ODR_1600HZ, INV_ICM42607_ODR_1_5625HZ_LP); + + return odr_periods[odr]; +} + int inv_icm42607_get_pwr_mgmt0(struct inv_icm42607_state *st, enum inv_icm42607_sensor_mode *gyro, enum inv_icm42607_sensor_mode *accel) @@ -126,7 +171,7 @@ static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st, enum inv_icm42607_sensor_mode accel) { enum inv_icm42607_sensor_mode oldaccel, oldgyro; - unsigned int sleepval_us; + unsigned int sleepval_us, odr_us; unsigned int val; s64 disable_wait; int ret; @@ -163,22 +208,30 @@ static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st, * If a state change occurs from off to on, sleep for the startup time * of the sensor. Since more than one sensor can be transitioned from * off to on, select the maximum time from each of the sensors changing - * from off to on. The startup time for the temp sensor is considerably + * from off to on. Also account for the time it takes for the first + * sample to be ready by looking up the odr value and adding it to the + * sleep time. The startup time for the temp sensor is considerably * smaller than the startup time for the other sensors and one or more * are required to be on for the temp sensor to function, so any start * delay should be enough. */ sleepval_us = 0; - if (accel && !oldaccel) + odr_us = 0; + if (accel && !oldaccel) { sleepval_us = max(sleepval_us, INV_ICM42607_ACCEL_STARTUP_TIME_US); + odr_us = max(odr_us, inv_icm42607_odr_to_period_us(st->conf.accel.odr)); + } if (gyro && !oldgyro) { sleepval_us = max(sleepval_us, INV_ICM42607_GYRO_STARTUP_TIME_US); + odr_us = max(odr_us, inv_icm42607_odr_to_period_us(st->conf.gyro.odr)); /* Track the earliest we can turn off the gyroscope. */ st->conf.gyro_stop = ktime_add_us(ktime_get(), INV_ICM42607_GYRO_STOP_TIME_US); } + sleepval_us += odr_us; + /* * Only sleep if sleepval_us is greater than 0 in case some platforms * have issues with a 0 delay. The 0 delay can happen if one or both @@ -190,6 +243,174 @@ static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st, return 0; } +/* + * Sanity test between old and new config values, and note if we need + * to update register config0 or config1. + */ +static void inv_icm42607_update_config(struct inv_icm42607_sensor_conf *conf, + struct inv_icm42607_sensor_conf *oldconf, + bool *config0, bool *config1) +{ + if (conf->mode < 0) + conf->mode = oldconf->mode; + if (conf->fs < 0) + conf->fs = oldconf->fs; + if (conf->odr < 0) + conf->odr = oldconf->odr; + if (conf->filter < 0) + conf->filter = oldconf->filter; + + *config0 = (conf->fs != oldconf->fs) || (conf->odr != oldconf->odr); + + *config1 = (conf->filter != oldconf->filter); +} + +int inv_icm42607_set_sensor_conf(struct inv_icm42607_state *st, + struct inv_icm42607_sensor_conf *conf, + enum iio_chan_type chan_type) +{ + enum inv_icm42607_sensor_mode accel_mode, gyro_mode; + struct inv_icm42607_sensor_conf *oldconf; + bool config0, config1; + unsigned int val; + int ret; + + switch (chan_type) { + case IIO_ACCEL: + oldconf = &st->conf.accel; + break; + case IIO_ANGL_VEL: + oldconf = &st->conf.gyro; + break; + default: + return -EINVAL; + } + + /* + * Since we are only making one-shot calls today, we can avoid needless + * on/off cycles if we rely on the runtime power management to handle + * shutting off the sensors when not in use. The downside is if we + * enable both sensors then only read from one, we have to wait for + * runtime PM to turn it off. So just keep the mode of the sensor we + * are not actively using to whatever it's already set as. + */ + ret = inv_icm42607_get_pwr_mgmt0(st, &gyro_mode, &accel_mode); + if (ret) + return ret; + + inv_icm42607_update_config(conf, oldconf, &config0, &config1); + + if (config0) { + if (chan_type == IIO_ANGL_VEL) { + val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK, conf->fs); + val |= FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_ODR_MASK, conf->odr); + ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val); + } else { + val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK, conf->fs); + val |= FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_ODR_MASK, conf->odr); + ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val); + } + if (ret) + return ret; + + oldconf->fs = conf->fs; + oldconf->odr = conf->odr; + } + + if (config1) { + if (chan_type == IIO_ANGL_VEL) { + val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, + conf->filter); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_GYRO_CONFIG1, + INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, val); + } else { + val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, + conf->filter); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1, + INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, val); + } + if (ret) + return ret; + + oldconf->filter = conf->filter; + } + + oldconf->mode = conf->mode; + + switch (chan_type) { + case IIO_ACCEL: + return inv_icm42607_set_pwr_mgmt0(st, gyro_mode, conf->mode); + case IIO_ANGL_VEL: + return inv_icm42607_set_pwr_mgmt0(st, conf->mode, accel_mode); + default: + return -EINVAL; + } +} + +int inv_icm42607_read_sensor(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + s16 *val) +{ + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + struct inv_icm42607_sensor_state *sensor_st = iio_priv(indio_dev); + struct device *dev = regmap_get_device(st->map); + unsigned int reg; + __be16 data; + int ret; + + if ((chan->type != IIO_ANGL_VEL) && (chan->type != IIO_ACCEL)) + return -EINVAL; + + switch (chan->channel2) { + case IIO_MOD_X: + if (chan->type == IIO_ANGL_VEL) + reg = INV_ICM42607_REG_GYRO_DATA_X1; + else + reg = INV_ICM42607_REG_ACCEL_DATA_X1; + break; + case IIO_MOD_Y: + if (chan->type == IIO_ANGL_VEL) + reg = INV_ICM42607_REG_GYRO_DATA_Y1; + else + reg = INV_ICM42607_REG_ACCEL_DATA_Y1; + break; + case IIO_MOD_Z: + if (chan->type == IIO_ANGL_VEL) + reg = INV_ICM42607_REG_GYRO_DATA_Z1; + else + reg = INV_ICM42607_REG_ACCEL_DATA_Z1; + break; + default: + return -EINVAL; + } + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + /* enable sensor */ + conf.mode = sensor_st->power_mode; + conf.filter = sensor_st->filter; + ret = inv_icm42607_set_sensor_conf(st, &conf, chan->type); + if (ret) + return ret; + + /* read sensor register data */ + ret = regmap_bulk_read(st->map, reg, &data, sizeof(data)); + if (ret) + return ret; + + *val = be16_to_cpu(data); + if (*val == INV_ICM42607_DATA_INVALID) + return -EINVAL; + + return 0; +} + static int inv_icm42607_set_init_conf(struct inv_icm42607_state *st, const struct inv_icm42607_conf *conf) { @@ -409,6 +630,11 @@ int inv_icm42607_core_probe(struct regmap *regmap, pm_runtime_set_autosuspend_delay(dev, INV_ICM42607_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(dev); + /* Initialize IIO device for Accel */ + st->indio_accel = inv_icm42607_accel_init(st); + if (IS_ERR(st->indio_accel)) + return PTR_ERR(st->indio_accel); + return 0; } EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607"); From 55abd7edfb193e2dd13122d60358b975fc368553 Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Tue, 28 Jul 2026 17:55:38 -0500 Subject: [PATCH 448/513] iio: imu: inv_icm42607: Add Gyroscope to icm42607 Add gyroscope functions to the icm42607 driver. Signed-off-by: Chris Morgan Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_icm42607/Makefile | 1 + drivers/iio/imu/inv_icm42607/inv_icm42607.h | 4 + .../iio/imu/inv_icm42607/inv_icm42607_core.c | 5 + .../iio/imu/inv_icm42607/inv_icm42607_gyro.c | 305 ++++++++++++++++++ 4 files changed, 315 insertions(+) create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile index 372c6d6bdcec..8e73385c8f4b 100644 --- a/drivers/iio/imu/inv_icm42607/Makefile +++ b/drivers/iio/imu/inv_icm42607/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o inv-icm42607-y += inv_icm42607_core.o +inv-icm42607-y += inv_icm42607_gyro.o inv-icm42607-y += inv_icm42607_accel.o obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h index fd3e57ee4f14..4d51b0da1aa1 100644 --- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h @@ -138,6 +138,7 @@ struct inv_icm42607_hw { * @lock: lock for serializing multiple registers access. * @map: regmap pointer. * @indio_accel: accelerometer IIO device. + * @indio_gyro: gyroscope IIO device. * @vddio_supply: I/O voltage regulator for the chip. * @vddio_en: I/O voltage status for runtime PM. * @conf: chip sensors configurations. @@ -148,6 +149,7 @@ struct inv_icm42607_state { struct mutex lock; struct regmap *map; struct iio_dev *indio_accel; + struct iio_dev *indio_gyro; struct regulator *vddio_supply; bool vddio_en; struct inv_icm42607_conf conf; @@ -412,6 +414,8 @@ int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw *hw, inv_icm42607_bus_setup bus_setup); +struct iio_dev *inv_icm42607_gyro_init(struct inv_icm42607_state *st); + struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st); #endif diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c index 68e84597241a..190e998f7b8e 100644 --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c @@ -635,6 +635,11 @@ int inv_icm42607_core_probe(struct regmap *regmap, if (IS_ERR(st->indio_accel)) return PTR_ERR(st->indio_accel); + /* Initialize IIO device for Gyro */ + st->indio_gyro = inv_icm42607_gyro_init(st); + if (IS_ERR(st->indio_gyro)) + return PTR_ERR(st->indio_gyro); + return 0; } EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607"); diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c new file mode 100644 index 000000000000..b68d4d6f5c3f --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inv_icm42607.h" + +#define INV_ICM42607_GYRO_CHAN(_modifier, _index, _ext_info) \ +{ \ + .type = IIO_ANGL_VEL, \ + .modified = 1, \ + .channel2 = _modifier, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + .ext_info = _ext_info, \ +} + +enum inv_icm42607_gyro_scan { + INV_ICM42607_GYRO_SCAN_X, + INV_ICM42607_GYRO_SCAN_Y, + INV_ICM42607_GYRO_SCAN_Z, +}; + +static const struct iio_chan_spec_ext_info inv_icm42607_gyro_ext_infos[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42607_get_mount_matrix), + { } +}; + +static const struct iio_chan_spec inv_icm42607_gyro_channels[] = { + INV_ICM42607_GYRO_CHAN(IIO_MOD_X, INV_ICM42607_GYRO_SCAN_X, + inv_icm42607_gyro_ext_infos), + INV_ICM42607_GYRO_CHAN(IIO_MOD_Y, INV_ICM42607_GYRO_SCAN_Y, + inv_icm42607_gyro_ext_infos), + INV_ICM42607_GYRO_CHAN(IIO_MOD_Z, INV_ICM42607_GYRO_SCAN_Z, + inv_icm42607_gyro_ext_infos), +}; + +static const int inv_icm42607_gyro_scale_nano[][2] = { + [INV_ICM42607_GYRO_FS_2000DPS] = { 0, 1065264 }, + [INV_ICM42607_GYRO_FS_1000DPS] = { 0, 532632 }, + [INV_ICM42607_GYRO_FS_500DPS] = { 0, 266316 }, + [INV_ICM42607_GYRO_FS_250DPS] = { 0, 133158 }, +}; + +static int inv_icm42607_gyro_read_scale(struct iio_dev *indio_dev, + int *val, int *val2) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + unsigned int idx; + + guard(mutex)(&st->lock); + + idx = st->conf.gyro.fs; + + *val = inv_icm42607_gyro_scale_nano[idx][0]; + *val2 = inv_icm42607_gyro_scale_nano[idx][1]; + return IIO_VAL_INT_PLUS_NANO; +} + +static int inv_icm42607_gyro_write_scale(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + struct device *dev = regmap_get_device(st->map); + unsigned int idx; + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + size_t scales_len = ARRAY_SIZE(inv_icm42607_gyro_scale_nano); + int ret; + + for (idx = 0; idx < scales_len; idx++) { + if (val == inv_icm42607_gyro_scale_nano[idx][0] && + val2 == inv_icm42607_gyro_scale_nano[idx][1]) + break; + } + if (idx == scales_len) + return -EINVAL; + + conf.fs = idx; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + return inv_icm42607_set_sensor_conf(st, &conf, IIO_ANGL_VEL); +} + +/* IIO format int + micro , values 0-4 reserved. */ +static const int inv_icm42607_gyro_odr[][2] = { + [INV_ICM42607_ODR_1600HZ] = { 1600, 0 }, + [INV_ICM42607_ODR_800HZ] = { 800, 0 }, + [INV_ICM42607_ODR_400HZ] = { 400, 0 }, + [INV_ICM42607_ODR_200HZ] = { 200, 0 }, + [INV_ICM42607_ODR_100HZ] = { 100, 0 }, + [INV_ICM42607_ODR_50HZ] = { 50, 0 }, + [INV_ICM42607_ODR_25HZ] = { 25, 0 }, + [INV_ICM42607_ODR_12_5HZ] = { 12, 500000 }, +}; + +static int inv_icm42607_gyro_read_odr(struct inv_icm42607_state *st, + int *val, int *val2) +{ + unsigned int odr; + unsigned int i; + + guard(mutex)(&st->lock); + + odr = st->conf.gyro.odr; + + for (i = INV_ICM42607_ODR_1600HZ; i < ARRAY_SIZE(inv_icm42607_gyro_odr); i++) { + if (i == odr) + break; + } + if (i == ARRAY_SIZE(inv_icm42607_gyro_odr)) + return -EINVAL; + + *val = inv_icm42607_gyro_odr[i][0]; + *val2 = inv_icm42607_gyro_odr[i][1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int inv_icm42607_gyro_write_odr(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + struct device *dev = regmap_get_device(st->map); + unsigned int idx; + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + int ret; + + for (idx = INV_ICM42607_ODR_1600HZ; + idx < ARRAY_SIZE(inv_icm42607_gyro_odr); idx++) { + if (val == inv_icm42607_gyro_odr[idx][0] && + val2 == inv_icm42607_gyro_odr[idx][1]) + break; + } + if (idx == ARRAY_SIZE(inv_icm42607_gyro_odr)) + return -EINVAL; + + conf.odr = idx; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + return inv_icm42607_set_sensor_conf(st, &conf, IIO_ANGL_VEL); +} + +static int inv_icm42607_gyro_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + s16 data; + int ret; + + switch (chan->type) { + case IIO_ANGL_VEL: + break; + default: + return -EINVAL; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = inv_icm42607_read_sensor(indio_dev, chan, &data); + if (ret) + return ret; + *val = data; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return inv_icm42607_gyro_read_scale(indio_dev, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm42607_gyro_read_odr(st, val, val2); + default: + return -EINVAL; + } +} + +static int inv_icm42607_gyro_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, + int *type, int *length, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ANGL_VEL) + return -EINVAL; + *vals = (const int *)inv_icm42607_gyro_scale_nano; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(inv_icm42607_gyro_scale_nano) * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (const int *)inv_icm42607_gyro_odr[INV_ICM42607_ODR_1600HZ]; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = (ARRAY_SIZE(inv_icm42607_gyro_odr) - + INV_ICM42607_ODR_1600HZ) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int inv_icm42607_gyro_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ANGL_VEL) + return -EINVAL; + ret = inv_icm42607_gyro_write_scale(indio_dev, val, val2); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm42607_gyro_write_odr(indio_dev, val, val2); + default: + return -EINVAL; + } +} + +static int inv_icm42607_gyro_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_ANGL_VEL) + return -EINVAL; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info inv_icm42607_gyro_info = { + .read_raw = inv_icm42607_gyro_read_raw, + .read_avail = inv_icm42607_gyro_read_avail, + .write_raw = inv_icm42607_gyro_write_raw, + .write_raw_get_fmt = inv_icm42607_gyro_write_raw_get_fmt, +}; + +struct iio_dev *inv_icm42607_gyro_init(struct inv_icm42607_state *st) +{ + struct device *dev = regmap_get_device(st->map); + const char *name; + struct inv_icm42607_sensor_state *gyro_st; + struct iio_dev *indio_dev; + int ret; + + name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->hw->name); + if (!name) + return ERR_PTR(-ENOMEM); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*gyro_st)); + if (!indio_dev) + return ERR_PTR(-ENOMEM); + + gyro_st = iio_priv(indio_dev); + gyro_st->power_mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE; + gyro_st->filter = INV_ICM42607_FILTER_BW_73HZ; + + indio_dev->name = name; + indio_dev->info = &inv_icm42607_gyro_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = inv_icm42607_gyro_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_icm42607_gyro_channels); + iio_device_set_drvdata(indio_dev, st); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ERR_PTR(ret); + + return indio_dev; +} From 512d321b9397092d779120a590682c8f632a9e3a Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Tue, 28 Jul 2026 17:55:39 -0500 Subject: [PATCH 449/513] iio: imu: inv_icm42607: Add Temp Support in icm42607 Add functions for reading temperature sensor data. Signed-off-by: Chris Morgan Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_icm42607/Makefile | 1 + .../iio/imu/inv_icm42607/inv_icm42607_accel.c | 8 ++ .../iio/imu/inv_icm42607/inv_icm42607_gyro.c | 8 ++ .../iio/imu/inv_icm42607/inv_icm42607_temp.c | 99 +++++++++++++++++++ .../iio/imu/inv_icm42607/inv_icm42607_temp.h | 38 +++++++ 5 files changed, 154 insertions(+) create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile index 8e73385c8f4b..7b907e019601 100644 --- a/drivers/iio/imu/inv_icm42607/Makefile +++ b/drivers/iio/imu/inv_icm42607/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o inv-icm42607-y += inv_icm42607_core.o inv-icm42607-y += inv_icm42607_gyro.o inv-icm42607-y += inv_icm42607_accel.o +inv-icm42607-y += inv_icm42607_temp.o obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o inv-icm42607-i2c-y += inv_icm42607_i2c.o diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c index f563c7708420..0b3f035c2da0 100644 --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c @@ -15,6 +15,7 @@ #include #include "inv_icm42607.h" +#include "inv_icm42607_temp.h" #define INV_ICM42607_ACCEL_CHAN(_modifier, _index, _ext_info) \ { \ @@ -40,6 +41,7 @@ enum inv_icm42607_accel_scan { INV_ICM42607_ACCEL_SCAN_X, INV_ICM42607_ACCEL_SCAN_Y, INV_ICM42607_ACCEL_SCAN_Z, + INV_ICM42607_ACCEL_SCAN_TEMP, }; static const struct iio_chan_spec_ext_info inv_icm42607_accel_ext_infos[] = { @@ -54,6 +56,7 @@ static const struct iio_chan_spec inv_icm42607_accel_channels[] = { inv_icm42607_accel_ext_infos), INV_ICM42607_ACCEL_CHAN(IIO_MOD_Z, INV_ICM42607_ACCEL_SCAN_Z, inv_icm42607_accel_ext_infos), + INV_ICM42607_TEMP_CHAN(INV_ICM42607_ACCEL_SCAN_TEMP), }; static const int inv_icm42607_accel_scale_nano[][2] = { @@ -187,6 +190,11 @@ static int inv_icm42607_accel_read_raw(struct iio_dev *indio_dev, switch (chan->type) { case IIO_ACCEL: break; + case IIO_TEMP: + if (mask != IIO_CHAN_INFO_SAMP_FREQ) + return inv_icm42607_temp_read_raw(indio_dev, chan, + val, val2, mask); + break; default: return -EINVAL; } diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c index b68d4d6f5c3f..5b4683c2dd1e 100644 --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c @@ -15,6 +15,7 @@ #include #include "inv_icm42607.h" +#include "inv_icm42607_temp.h" #define INV_ICM42607_GYRO_CHAN(_modifier, _index, _ext_info) \ { \ @@ -40,6 +41,7 @@ enum inv_icm42607_gyro_scan { INV_ICM42607_GYRO_SCAN_X, INV_ICM42607_GYRO_SCAN_Y, INV_ICM42607_GYRO_SCAN_Z, + INV_ICM42607_GYRO_SCAN_TEMP, }; static const struct iio_chan_spec_ext_info inv_icm42607_gyro_ext_infos[] = { @@ -54,6 +56,7 @@ static const struct iio_chan_spec inv_icm42607_gyro_channels[] = { inv_icm42607_gyro_ext_infos), INV_ICM42607_GYRO_CHAN(IIO_MOD_Z, INV_ICM42607_GYRO_SCAN_Z, inv_icm42607_gyro_ext_infos), + INV_ICM42607_TEMP_CHAN(INV_ICM42607_GYRO_SCAN_TEMP), }; static const int inv_icm42607_gyro_scale_nano[][2] = { @@ -184,6 +187,11 @@ static int inv_icm42607_gyro_read_raw(struct iio_dev *indio_dev, switch (chan->type) { case IIO_ANGL_VEL: break; + case IIO_TEMP: + if (mask != IIO_CHAN_INFO_SAMP_FREQ) + return inv_icm42607_temp_read_raw(indio_dev, chan, + val, val2, mask); + break; default: return -EINVAL; } diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c new file mode 100644 index 000000000000..53f0484c0845 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inv_icm42607.h" +#include "inv_icm42607_temp.h" + +static int inv_icm42607_temp_read(struct inv_icm42607_state *st, s16 *temp) +{ + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; + struct device *dev = regmap_get_device(st->map); + int ret, gyro_mode, accel_mode; + unsigned int val; + u8 raw[2]; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + /* + * Check if both the gyro and accel are off and if so, enable one + * of them. The temp sensor cannot be read if both the gyro and + * accel sensor are off. Prefer to enable the accel over the gyro + * as the datasheet says the gyro uses 5x more power and it has + * a minimum run time of 45ms. + */ + ret = regmap_read(st->map, INV_ICM42607_REG_PWR_MGMT0, &val); + if (ret) + return ret; + + accel_mode = FIELD_GET(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, val); + gyro_mode = FIELD_GET(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, val); + if (!gyro_mode && !accel_mode) { + /* enable accel sensor */ + conf.mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE; + ret = inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL); + if (ret) + return ret; + } + + ret = regmap_bulk_read(st->map, INV_ICM42607_REG_TEMP_DATA1, + raw, sizeof(raw)); + if (ret) + return ret; + + *temp = get_unaligned_be16(raw); + if (*temp == INV_ICM42607_DATA_INVALID) + return -EINVAL; + + return 0; +} + +int inv_icm42607_temp_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); + s16 temp; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = inv_icm42607_temp_read(st, &temp); + if (ret) + return ret; + *val = temp; + return IIO_VAL_INT; + /* + * T°C = (temp / 128) + 25 + * Tm°C = 1000 * ((temp * 100 / 12800) + 25) + * scale: 100000 / 12800 ~= 7.8125 + * offset: 3200 + */ + case IIO_CHAN_INFO_SCALE: + *val = 7; + *val2 = 812500000; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + *val = 3200; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h new file mode 100644 index 000000000000..18499b4d0b94 --- /dev/null +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2026 InvenSense, Inc. + */ + +#ifndef INV_ICM42607_TEMP_H_ +#define INV_ICM42607_TEMP_H_ + +#include + +struct iio_dev; +struct iio_chan_spec; + +#define INV_ICM42607_TEMP_CHAN(_index) \ +{ \ + .type = IIO_TEMP, \ + .info_mask_separate = \ + BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ +} + +int inv_icm42607_temp_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask); + +#endif From 55b75622829779223b9e32aa9600a8651d3e2df4 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Tue, 28 Jul 2026 03:47:38 +0900 Subject: [PATCH 450/513] iio: light: isl29028: return zero in write_raw() on success isl29028_write_raw() returns the value of pm_runtime_put_autosuspend(), which is 1 if the device is already runtime suspended. In that case write() on the sysfs attribute returns 1 instead of the number of bytes written. Make isl29028_write_raw() always return zero on success. Fixes: 2db5054ac28d ("staging: iio: isl29028: add runtime power management support") Signed-off-by: Sang-Heon Jeon Reviewed-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/iio/light/isl29028.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/isl29028.c b/drivers/iio/light/isl29028.c index b88e7c4eae3e..6d5dd17e7a7d 100644 --- a/drivers/iio/light/isl29028.c +++ b/drivers/iio/light/isl29028.c @@ -409,7 +409,7 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, if (ret < 0) return ret; - return ret; + return 0; } static int isl29028_read_raw(struct iio_dev *indio_dev, From 42e8791841e0677418a3ccc97fa5c22a1455f417 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Tue, 28 Jul 2026 03:47:39 +0900 Subject: [PATCH 451/513] iio: light: tsl2583: return zero in write_raw() on success tsl2583_write_raw() returns the value of pm_runtime_put_autosuspend(), which is 1 if the device is already runtime suspended. In that case write() on the sysfs attribute returns 1 instead of the number of bytes written. Make tsl2583_write_raw() always return zero on success. Fixes: 371894f5d1a0 ("iio: tsl2583: add runtime power management support") Signed-off-by: Sang-Heon Jeon Reviewed-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2583.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c index a210d87a1519..54fa426c5755 100644 --- a/drivers/iio/light/tsl2583.c +++ b/drivers/iio/light/tsl2583.c @@ -794,7 +794,7 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev, if (ret < 0) return ret; - return ret; + return 0; } static const struct iio_info tsl2583_info = { From 7f61f41a3f5e8f9aa6c264833504995c89712cfc Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Tue, 28 Jul 2026 03:47:40 +0900 Subject: [PATCH 452/513] iio: light: isl29028: remove isl29028_set_pm_runtime_busy() helper isl29028_set_pm_runtime_busy() just calls pm_runtime_resume_and_get() or pm_runtime_put_autosuspend() depending on its bool argument. Call them directly and remove the helper. No functional change. Suggested-by: Jonathan Cameron Signed-off-by: Sang-Heon Jeon Reviewed-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/iio/light/isl29028.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/drivers/iio/light/isl29028.c b/drivers/iio/light/isl29028.c index 6d5dd17e7a7d..33deb1726689 100644 --- a/drivers/iio/light/isl29028.c +++ b/drivers/iio/light/isl29028.c @@ -333,16 +333,6 @@ static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data) return isl29028_read_als_ir(chip, ir_data); } -static int isl29028_set_pm_runtime_busy(struct isl29028_chip *chip, bool on) -{ - struct device *dev = regmap_get_device(chip->regmap); - - if (on) - return pm_runtime_resume_and_get(dev); - - return pm_runtime_put_autosuspend(dev); -} - /* Channel IO */ static int isl29028_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, @@ -352,7 +342,7 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, struct device *dev = regmap_get_device(chip->regmap); int ret; - ret = isl29028_set_pm_runtime_busy(chip, true); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) return ret; @@ -405,7 +395,7 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, if (ret < 0) return ret; - ret = isl29028_set_pm_runtime_busy(chip, false); + ret = pm_runtime_put_autosuspend(dev); if (ret < 0) return ret; @@ -420,7 +410,7 @@ static int isl29028_read_raw(struct iio_dev *indio_dev, struct device *dev = regmap_get_device(chip->regmap); int ret, pm_ret; - ret = isl29028_set_pm_runtime_busy(chip, true); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) return ret; @@ -476,10 +466,10 @@ static int isl29028_read_raw(struct iio_dev *indio_dev, /** * Preserve the ret variable if the call to - * isl29028_set_pm_runtime_busy() is successful so the reading + * pm_runtime_put_autosuspend() is successful so the reading * (if applicable) is returned to user space. */ - pm_ret = isl29028_set_pm_runtime_busy(chip, false); + pm_ret = pm_runtime_put_autosuspend(dev); if (pm_ret < 0) return pm_ret; From 75056737b69029cb08a294a2ef746ecfdb598abd Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Tue, 28 Jul 2026 03:47:41 +0900 Subject: [PATCH 453/513] iio: light: tsl2583: remove tsl2583_set_pm_runtime_busy() helper tsl2583_set_pm_runtime_busy() just calls pm_runtime_resume_and_get() or pm_runtime_put_autosuspend() depending on its bool argument. Call them directly and remove the helper. No functional change. Suggested-by: Jonathan Cameron Signed-off-by: Sang-Heon Jeon Reviewed-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2583.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c index 54fa426c5755..06da145569f7 100644 --- a/drivers/iio/light/tsl2583.c +++ b/drivers/iio/light/tsl2583.c @@ -639,14 +639,6 @@ static const struct iio_chan_spec tsl2583_channels[] = { }, }; -static int tsl2583_set_pm_runtime_busy(struct tsl2583_chip *chip, bool on) -{ - if (on) - return pm_runtime_resume_and_get(&chip->client->dev); - - return pm_runtime_put_autosuspend(&chip->client->dev); -} - static int tsl2583_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -654,7 +646,7 @@ static int tsl2583_read_raw(struct iio_dev *indio_dev, struct tsl2583_chip *chip = iio_priv(indio_dev); int ret, pm_ret; - ret = tsl2583_set_pm_runtime_busy(chip, true); + ret = pm_runtime_resume_and_get(&chip->client->dev); if (ret < 0) return ret; @@ -722,16 +714,16 @@ static int tsl2583_read_raw(struct iio_dev *indio_dev, mutex_unlock(&chip->als_mutex); if (ret < 0) { - tsl2583_set_pm_runtime_busy(chip, false); + pm_runtime_put_autosuspend(&chip->client->dev); return ret; } /* * Preserve the ret variable if the call to - * tsl2583_set_pm_runtime_busy() is successful so the reading + * pm_runtime_put_autosuspend() is successful so the reading * (if applicable) is returned to user space. */ - pm_ret = tsl2583_set_pm_runtime_busy(chip, false); + pm_ret = pm_runtime_put_autosuspend(&chip->client->dev); if (pm_ret < 0) return pm_ret; @@ -745,7 +737,7 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev, struct tsl2583_chip *chip = iio_priv(indio_dev); int ret; - ret = tsl2583_set_pm_runtime_busy(chip, true); + ret = pm_runtime_resume_and_get(&chip->client->dev); if (ret < 0) return ret; @@ -786,11 +778,11 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev, mutex_unlock(&chip->als_mutex); if (ret < 0) { - tsl2583_set_pm_runtime_busy(chip, false); + pm_runtime_put_autosuspend(&chip->client->dev); return ret; } - ret = tsl2583_set_pm_runtime_busy(chip, false); + ret = pm_runtime_put_autosuspend(&chip->client->dev); if (ret < 0) return ret; From 434291cc8a0a01c893a18ea53f161e4d2df72a17 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Tue, 28 Jul 2026 03:47:42 +0900 Subject: [PATCH 454/513] iio: light: vcnl4035: remove vcnl4035_set_pm_runtime_state() helper vcnl4035_set_pm_runtime_state() just calls pm_runtime_resume_and_get() or pm_runtime_put_autosuspend() depending on its bool argument. Call them directly and remove the helper. No functional change. Signed-off-by: Sang-Heon Jeon Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4035.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c index d13c5ff33e07..ce1bd42b0c7c 100644 --- a/drivers/iio/light/vcnl4035.c +++ b/drivers/iio/light/vcnl4035.c @@ -145,16 +145,6 @@ static const struct iio_trigger_ops vcnl4035_trigger_ops = { .set_trigger_state = vcnl4035_als_drdy_set_state, }; -static int vcnl4035_set_pm_runtime_state(struct vcnl4035_data *data, bool on) -{ - struct device *dev = &data->client->dev; - - if (on) - return pm_runtime_resume_and_get(dev); - - return pm_runtime_put_autosuspend(dev); -} - static int vcnl4035_read_info_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { @@ -202,11 +192,11 @@ static int vcnl4035_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = vcnl4035_set_pm_runtime_state(data, true); + ret = pm_runtime_resume_and_get(&data->client->dev); if (ret < 0) return ret; ret = vcnl4035_read_info_raw(indio_dev, chan, val); - vcnl4035_set_pm_runtime_state(data, false); + pm_runtime_put_autosuspend(&data->client->dev); return ret; case IIO_CHAN_INFO_INT_TIME: *val = 50; @@ -237,7 +227,7 @@ static int vcnl4035_write_raw(struct iio_dev *indio_dev, if (val <= 0 || val > 800) return -EINVAL; - ret = vcnl4035_set_pm_runtime_state(data, true); + ret = pm_runtime_resume_and_get(&data->client->dev); if (ret < 0) return ret; @@ -247,7 +237,7 @@ static int vcnl4035_write_raw(struct iio_dev *indio_dev, if (!ret) data->als_it_val = val / 100; - vcnl4035_set_pm_runtime_state(data, false); + pm_runtime_put_autosuspend(&data->client->dev); return ret; default: return -EINVAL; From 9bd7d3ea0531715ff1fc9e51e0aed51d2300f4a6 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Tue, 28 Jul 2026 03:47:43 +0900 Subject: [PATCH 455/513] iio: light: vcnl4000: remove vcnl4000_set_pm_runtime_state() helper vcnl4000_set_pm_runtime_state() just calls pm_runtime_resume_and_get() or pm_runtime_put_autosuspend() depending on its bool argument. Call them directly and remove the helper. No functional change. Signed-off-by: Sang-Heon Jeon Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index b8ac156ad5c9..336468d59ea7 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -537,16 +537,6 @@ static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data) return !!(ret & VCNL4000_SELF_TIMED_EN); } -static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) -{ - struct device *dev = &data->client->dev; - - if (on) - return pm_runtime_resume_and_get(dev); - - return pm_runtime_put_autosuspend(dev); -} - static int vcnl4040_read_als_it(struct vcnl4000_data *data, int *val, int *val2) { int ret; @@ -850,7 +840,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = vcnl4000_set_pm_runtime_state(data, true); + ret = pm_runtime_resume_and_get(&data->client->dev); if (ret < 0) return ret; @@ -869,7 +859,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, default: ret = -EINVAL; } - vcnl4000_set_pm_runtime_state(data, false); + pm_runtime_put_autosuspend(&data->client->dev); return ret; case IIO_CHAN_INFO_SCALE: if (chan->type != IIO_LIGHT) From 14ba50c03644d79b6ca987429ad9f7117dcfb8ea Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Tue, 28 Jul 2026 03:47:44 +0900 Subject: [PATCH 456/513] iio: adc: rcar-gyroadc: remove rcar_gyroadc_set_power() helper rcar_gyroadc_set_power() just calls pm_runtime_resume_and_get() or pm_runtime_put_autosuspend() depending on its bool argument. Call them directly and remove the helper. No functional change. Signed-off-by: Sang-Heon Jeon Signed-off-by: Jonathan Cameron --- drivers/iio/adc/rcar-gyroadc.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c index 3a17b3898bf6..d38cf3caeae0 100644 --- a/drivers/iio/adc/rcar-gyroadc.c +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -159,16 +159,6 @@ static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = { RCAR_GYROADC_CHAN(7), }; -static int rcar_gyroadc_set_power(struct rcar_gyroadc *priv, bool on) -{ - struct device *dev = priv->dev; - - if (on) - return pm_runtime_resume_and_get(dev); - - return pm_runtime_put_autosuspend(dev); -} - static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -200,7 +190,7 @@ static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev, if (!iio_device_claim_direct(indio_dev)) return -EBUSY; - ret = rcar_gyroadc_set_power(priv, true); + ret = pm_runtime_resume_and_get(priv->dev); if (ret < 0) { iio_device_release_direct(indio_dev); return ret; @@ -209,7 +199,7 @@ static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev, *val = readl(priv->regs + datareg); *val &= BIT(priv->sample_width) - 1; - ret = rcar_gyroadc_set_power(priv, false); + ret = pm_runtime_put_autosuspend(priv->dev); iio_device_release_direct(indio_dev); if (ret < 0) return ret; From 0addd3b68830a6870d556c6c570837f2a823343d Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Tue, 28 Jul 2026 03:47:45 +0900 Subject: [PATCH 457/513] iio: light: tsl2583: remove conditional return with no effect Both branches of the check return the same value, so the check has no effect. Remove it and return the value directly. This is the result of running the Coccinelle script from scripts/coccinelle/misc/cond_return_no_effect.cocci. Signed-off-by: Sang-Heon Jeon Reviewed-by: Joshua Crofts Reviewed-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2583.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c index 06da145569f7..53fd423aa7ae 100644 --- a/drivers/iio/light/tsl2583.c +++ b/drivers/iio/light/tsl2583.c @@ -456,12 +456,8 @@ static int tsl2583_chip_init_and_power_on(struct iio_dev *indio_dev) usleep_range(3000, 3500); - ret = tsl2583_set_power_state(chip, TSL2583_CNTL_PWR_ON | - TSL2583_CNTL_ADC_ENBL); - if (ret < 0) - return ret; - - return ret; + return tsl2583_set_power_state(chip, + TSL2583_CNTL_PWR_ON | TSL2583_CNTL_ADC_ENBL); } /* Sysfs Interface Functions */ From 017e71cbe0cf25052d7baf42332cb5279f3ac036 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Tue, 28 Jul 2026 03:47:46 +0900 Subject: [PATCH 458/513] iio: magnetometer: ak8974: remove conditional return with no effect Both branches of the check return the same value, so the check has no effect. Remove it and return the value directly. This is the result of running the Coccinelle script from scripts/coccinelle/misc/cond_return_no_effect.cocci. Signed-off-by: Sang-Heon Jeon Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8974.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c index 28d6cadfd9f8..375023db2758 100644 --- a/drivers/iio/magnetometer/ak8974.c +++ b/drivers/iio/magnetometer/ak8974.c @@ -379,11 +379,7 @@ static int ak8974_getresult(struct ak8974 *ak8974, __le16 *result) return -ERANGE; } - ret = regmap_bulk_read(ak8974->map, AK8974_DATA_X, result, 6); - if (ret) - return ret; - - return ret; + return regmap_bulk_read(ak8974->map, AK8974_DATA_X, result, 6); } static irqreturn_t ak8974_drdy_irq(int irq, void *d) From 6c0e7268214bd893d0c5fd4e8d4711976aad4043 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Mon, 27 Jul 2026 18:30:35 -0300 Subject: [PATCH 459/513] dt-bindings: iio: adc: Add ltc2378 Document how to describe LTC2378-20 and similar ADCs in device tree. Reviewed-by: David Lechner Reviewed-by: Conor Dooley Signed-off-by: Marcelo Schmitt Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/adi,ltc2378.yaml | 170 ++++++++++++++++++ MAINTAINERS | 7 + 2 files changed, 177 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml new file mode 100644 index 000000000000..33de1dc4c256 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml @@ -0,0 +1,170 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ltc2378.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices LTC2378 and similar Analog to Digital Converters + +maintainers: + - Marcelo Schmitt + +description: | + Analog Devices LTC2378 series of ADCs. + Specifications can be found at: + https://www.analog.com/media/en/technical-documentation/data-sheets/233818fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236416fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236418f.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236716fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236718f.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236816f.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236818f.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236918fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237016fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237616fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237618fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237620fb.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237716fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237718fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237720fb.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237816fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237818fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237820fb.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237918fb.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/238016fb.pdf + +$ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + oneOf: + # Single compatible string match. + - enum: + - adi,ltc2338-18 + - adi,ltc2364-16 + - adi,ltc2364-18 + - adi,ltc2376-16 + - adi,ltc2376-18 + - adi,ltc2376-20 + + # Low sample rate fallback for 16-bit unipolar sensors. + - items: + - enum: + - adi,ltc2370-16 # 2 MSPS + - adi,ltc2368-16 # 1 MSPS + - adi,ltc2367-16 # 500 kSPS + - const: adi,ltc2364-16 # fallback (250 kSPS) + + # Low sample rate fallback for 18-bit unipolar sensors. + - items: + - enum: + - adi,ltc2369-18 # 1.6 MSPS + - adi,ltc2368-18 # 1 MSPS + - adi,ltc2367-18 # 500 kSPS + - const: adi,ltc2364-18 # fallback (250 kSPS) + + # Low sample rate fallback for 16-bit bipolar sensors. + - items: + - enum: + - adi,ltc2380-16 # 2 MSPS + - adi,ltc2378-16 # 1 MSPS + - adi,ltc2377-16 # 500 kSPS + - const: adi,ltc2376-16 # fallback (250 kSPS) + + # Low sample rate fallback for 18-bit bipolar sensors. + - items: + - enum: + - adi,ltc2379-18 # 1.6 MSPS + - adi,ltc2378-18 # 1 MSPS + - adi,ltc2377-18 # 500 kSPS + - const: adi,ltc2376-18 # fallback (250 kSPS) + + # Low sample rate fallback for 20-bit bipolar sensors. + - items: + - enum: + - adi,ltc2378-20 # 1 MSPS + - adi,ltc2377-20 # 500 kSPS + - const: adi,ltc2376-20 # fallback (250 kSPS) + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 100000000 + + vdd-supply: + description: A 2.5V supply that powers the chip (VDD). + + ovdd-supply: + description: + A 1.71V to 5.25V supply that sets the logic level for digital interface. + + ref-supply: + description: + Voltage reference input that determines the scale of ADC conversions. + + refin-supply: + description: + Alternative voltage reference input. + + cnv-gpios: + description: + When provided, this property indicates the GPIO that is connected to the + CNV pin. + maxItems: 1 + + pwms: + description: PWM signal connected to the CNV pin. + maxItems: 1 + + interrupts: + description: + Interrupt for signaling the completion of conversion results. The active + low signal provided on the BUSY pin asserts when ADC conversions finish. + maxItems: 1 + +required: + - compatible + - reg + - vdd-supply + - ovdd-supply + +allOf: + # Except for LTC2338, all designs require a voltage reference input + - if: + properties: + compatible: + not: + contains: + enum: + - adi,ltc2338-18 + then: + required: + - ref-supply + properties: + refin-supply: false + else: + properties: + ref-supply: false + +unevaluatedProperties: false + +examples: + - | + #include + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + adc@0 { + compatible = "adi,ltc2378-20", "adi,ltc2376-20"; + reg = <0>; + spi-max-frequency = <71000000>; + vdd-supply = <&supply_2_5V>; + ovdd-supply = <&supply_3_3V>; + ref-supply = <&supply_5V>; + cnv-gpios = <&gpio0 88 GPIO_ACTIVE_HIGH>; + interrupts = <7 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index ce020ded5fd3..0c73ddcaf605 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15343,6 +15343,13 @@ S: Maintained F: Documentation/devicetree/bindings/iio/dac/lltc,ltc1660.yaml F: drivers/iio/dac/ltc1660.c +LTC2378 IIO ADC DRIVER +M: Marcelo Schmitt +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml + LTC2664 IIO DAC DRIVER M: Michael Hennerich M: Kim Seer Paller From 469eafdc69a41d7edb5c957638865f9943a37a83 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Mon, 27 Jul 2026 18:30:54 -0300 Subject: [PATCH 460/513] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs Support for LTC2378-20 and similar analog-to-digital converters. Reviewed-by: Andy Shevchenko Reviewed-by: David Lechner Signed-off-by: Marcelo Schmitt Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + drivers/iio/adc/Kconfig | 12 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ltc2378.c | 387 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 401 insertions(+) create mode 100644 drivers/iio/adc/ltc2378.c diff --git a/MAINTAINERS b/MAINTAINERS index 0c73ddcaf605..cf8c63f72ca0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15349,6 +15349,7 @@ L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml +F: drivers/iio/adc/ltc2378.c LTC2664 IIO DAC DRIVER M: Michael Hennerich diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index b043c00f8939..51d127f4957a 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -943,6 +943,18 @@ config LTC2309 This driver can also be built as a module. If so, the module will be called ltc2309. +config LTC2378 + tristate "Analog Devices LTC2378 ADC driver" + depends on SPI + depends on REGULATOR || COMPILE_TEST + depends on GPIOLIB + help + Say yes here to build support for Analog Devices LTC2378-20 and + similar analog to digital converters. + + This driver can also be built as a module. If so, the module will + be called ltc2378. + config LTC2471 tristate "Linear Technology LTC2471 and LTC2473 ADC driver" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 3778c7b0b8b5..dcec0abb03b7 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -81,6 +81,7 @@ obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o obj-$(CONFIG_LTC2309) += ltc2309.o +obj-$(CONFIG_LTC2378) += ltc2378.o obj-$(CONFIG_LTC2471) += ltc2471.o obj-$(CONFIG_LTC2485) += ltc2485.o obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o diff --git a/drivers/iio/adc/ltc2378.c b/drivers/iio/adc/ltc2378.c new file mode 100644 index 000000000000..d161435fc3a3 --- /dev/null +++ b/drivers/iio/adc/ltc2378.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Analog Devices LTC2378 ADC series driver + * + * Copyright (C) 2026 Analog Devices Inc. + * Author: Marcelo Schmitt + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define LTC2378_MAX_DATA_WAIT_US 4 /* max(TBUSYLH + TCONV + TDSDOBUSYL) */ + +#define LTC2378_CHANNEL(_sign, _real_bits, _storage_bits) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .differential = _sign, \ + .channel = 0, \ + .channel2 = _sign ? 1 : 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = 0, \ + .scan_type = { \ + .format = _sign ? IIO_SCAN_FORMAT_SIGNED_INT : \ + IIO_SCAN_FORMAT_UNSIGNED_INT, \ + .realbits = _real_bits, \ + .storagebits = _storage_bits, \ + .shift = _storage_bits - _real_bits, \ + .endianness = IIO_BE, \ + }, \ +} + +#define LTC2378_DIFF_CHANNEL(_real_bits) \ + LTC2378_CHANNEL(1, _real_bits, (((_real_bits) > 16) ? 32 : 16)) + +#define LTC2378_PSEUDO_DIFF_CHANNEL(_real_bits) \ + LTC2378_CHANNEL(0, _real_bits, (((_real_bits) > 16) ? 32 : 16)) + +struct ltc2378_chip_info { + const char *name; + struct iio_chan_spec chan; +}; + +struct ltc2378_state { + const struct ltc2378_chip_info *info; + struct gpio_desc *cnv_gpio; + struct spi_device *spi; + struct mutex lock; /* Protect data acquisition cycle */ + int ref_uV; + struct spi_transfer xfer; + + /* + * DMA (thus cache coherency maintenance) requires the transfer buffers + * to live in their own cache lines. + */ + struct { + union { + __be16 sample_buf16_be; + __be32 sample_buf32_be; + u16 sample_buf16; + u32 sample_buf32; + } data; + aligned_s64 timestamp; + } scan __aligned(IIO_DMA_MINALIGN); +}; + +static const struct ltc2378_chip_info ltc2364_16_chip_info = { + .name = "ltc2364-16", + .chan = LTC2378_PSEUDO_DIFF_CHANNEL(16), +}; + +static const struct ltc2378_chip_info ltc2364_18_chip_info = { + .name = "ltc2364-18", + .chan = LTC2378_PSEUDO_DIFF_CHANNEL(18), +}; + +static const struct ltc2378_chip_info ltc2367_16_chip_info = { + .name = "ltc2367-16", + .chan = LTC2378_PSEUDO_DIFF_CHANNEL(16), +}; + +static const struct ltc2378_chip_info ltc2367_18_chip_info = { + .name = "ltc2367-18", + .chan = LTC2378_PSEUDO_DIFF_CHANNEL(18), +}; + +static const struct ltc2378_chip_info ltc2368_16_chip_info = { + .name = "ltc2368-16", + .chan = LTC2378_PSEUDO_DIFF_CHANNEL(16), +}; + +static const struct ltc2378_chip_info ltc2368_18_chip_info = { + .name = "ltc2368-18", + .chan = LTC2378_PSEUDO_DIFF_CHANNEL(18), +}; + +static const struct ltc2378_chip_info ltc2369_18_chip_info = { + .name = "ltc2369-18", + .chan = LTC2378_PSEUDO_DIFF_CHANNEL(18), +}; + +static const struct ltc2378_chip_info ltc2370_16_chip_info = { + .name = "ltc2370-16", + .chan = LTC2378_PSEUDO_DIFF_CHANNEL(16), +}; + +static const struct ltc2378_chip_info ltc2376_16_chip_info = { + .name = "ltc2376-16", + .chan = LTC2378_DIFF_CHANNEL(16), +}; + +static const struct ltc2378_chip_info ltc2376_18_chip_info = { + .name = "ltc2376-18", + .chan = LTC2378_DIFF_CHANNEL(18), +}; + +static const struct ltc2378_chip_info ltc2376_20_chip_info = { + .name = "ltc2376-20", + .chan = LTC2378_DIFF_CHANNEL(20), +}; + +static const struct ltc2378_chip_info ltc2377_16_chip_info = { + .name = "ltc2377-16", + .chan = LTC2378_DIFF_CHANNEL(16), +}; + +static const struct ltc2378_chip_info ltc2377_18_chip_info = { + .name = "ltc2377-18", + .chan = LTC2378_DIFF_CHANNEL(18), +}; + +static const struct ltc2378_chip_info ltc2377_20_chip_info = { + .name = "ltc2377-20", + .chan = LTC2378_DIFF_CHANNEL(20), +}; + +static const struct ltc2378_chip_info ltc2378_16_chip_info = { + .name = "ltc2378-16", + .chan = LTC2378_DIFF_CHANNEL(16), +}; + +static const struct ltc2378_chip_info ltc2378_18_chip_info = { + .name = "ltc2378-18", + .chan = LTC2378_DIFF_CHANNEL(18), +}; + +static const struct ltc2378_chip_info ltc2378_20_chip_info = { + .name = "ltc2378-20", + .chan = LTC2378_DIFF_CHANNEL(20), +}; + +static const struct ltc2378_chip_info ltc2379_18_chip_info = { + .name = "ltc2379-18", + .chan = LTC2378_DIFF_CHANNEL(18), +}; + +static const struct ltc2378_chip_info ltc2380_16_chip_info = { + .name = "ltc2380-16", + .chan = LTC2378_DIFF_CHANNEL(16), +}; + +static int ltc2378_convert_and_acquire(struct ltc2378_state *st) +{ + int ret; + + /* Cause a rising edge of CNV to initiate a new ADC conversion */ + gpiod_set_value_cansleep(st->cnv_gpio, 1); + fsleep(LTC2378_MAX_DATA_WAIT_US); + ret = spi_sync_transfer(st->spi, &st->xfer, 1); + gpiod_set_value_cansleep(st->cnv_gpio, 0); + + return ret; +} + +static int ltc2378_channel_single_read(const struct iio_chan_spec *chan, + struct ltc2378_state *st, int *val) +{ + const struct iio_scan_type *scan_type = &chan->scan_type; + u32 sample; + int ret; + + guard(mutex)(&st->lock); + ret = ltc2378_convert_and_acquire(st); + if (ret) + return ret; + + if (chan->scan_type.endianness == IIO_BE) { + if (chan->scan_type.realbits > 16) + sample = be32_to_cpu(st->scan.data.sample_buf32_be); + else + sample = be16_to_cpu(st->scan.data.sample_buf16_be); + } else { /* IIO_CPU */ + if (chan->scan_type.realbits > 16) + sample = st->scan.data.sample_buf32; + else + sample = st->scan.data.sample_buf16; + } + + sample >>= chan->scan_type.shift; + + if (scan_type->format == IIO_SCAN_FORMAT_SIGNED_INT) + *val = sign_extend32(sample, scan_type->realbits - 1); + else + *val = sample; + + return 0; +} + +static int ltc2378_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct ltc2378_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + ret = ltc2378_channel_single_read(chan, st, val); + if (ret) + return ret; + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + *val = st->ref_uV / MILLI; + /* + * For all LTC2378-like devices, the amount of bits that express + * voltage magnitude depend on the polarity / output code format: + * - straight binary: All precision/resolution bits are used. + * - 2's complement: One of the precision bits is used for sign. + */ + if (chan->scan_type.format == IIO_SCAN_FORMAT_SIGNED_INT) + *val2 = chan->scan_type.realbits - 1; + else + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static const struct iio_info ltc2378_iio_info = { + .read_raw = <c2378_read_raw, +}; + +static int ltc2378_ref_setup(struct device *dev, struct ltc2378_state *st) +{ + int ret; + + ret = devm_regulator_get_enable_read_voltage(dev, "ref"); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read ref regulator\n"); + + st->ref_uV = ret; + + return 0; +} + +static int ltc2378_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ltc2378_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + st->info = spi_get_device_match_data(spi); + if (!st->info) + return -EINVAL; + + ret = ltc2378_ref_setup(dev, st); + if (ret) + return ret; + + indio_dev->name = st->info->name; + indio_dev->info = <c2378_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + st->cnv_gpio = devm_gpiod_get(dev, "cnv", GPIOD_OUT_LOW); + if (IS_ERR(st->cnv_gpio)) + return dev_err_probe(dev, PTR_ERR(st->cnv_gpio), + "failed to get CNV GPIO"); + + indio_dev->channels = &st->info->chan; + indio_dev->num_channels = 1; + + st->xfer.rx_buf = &st->scan.data; + st->xfer.len = spi_bpw_to_bytes(indio_dev->channels[0].scan_type.realbits); + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id ltc2378_of_match[] = { + { .compatible = "adi,ltc2364-16", .data = <c2364_16_chip_info }, + { .compatible = "adi,ltc2364-18", .data = <c2364_18_chip_info }, + { .compatible = "adi,ltc2367-16", .data = <c2367_16_chip_info }, + { .compatible = "adi,ltc2367-18", .data = <c2367_18_chip_info }, + { .compatible = "adi,ltc2368-16", .data = <c2368_16_chip_info }, + { .compatible = "adi,ltc2368-18", .data = <c2368_18_chip_info }, + { .compatible = "adi,ltc2369-18", .data = <c2369_18_chip_info }, + { .compatible = "adi,ltc2370-16", .data = <c2370_16_chip_info }, + { .compatible = "adi,ltc2376-16", .data = <c2376_16_chip_info }, + { .compatible = "adi,ltc2376-18", .data = <c2376_18_chip_info }, + { .compatible = "adi,ltc2376-20", .data = <c2376_20_chip_info }, + { .compatible = "adi,ltc2377-16", .data = <c2377_16_chip_info }, + { .compatible = "adi,ltc2377-18", .data = <c2377_18_chip_info }, + { .compatible = "adi,ltc2377-20", .data = <c2377_20_chip_info }, + { .compatible = "adi,ltc2378-16", .data = <c2378_16_chip_info }, + { .compatible = "adi,ltc2378-18", .data = <c2378_18_chip_info }, + { .compatible = "adi,ltc2378-20", .data = <c2378_20_chip_info }, + { .compatible = "adi,ltc2379-18", .data = <c2379_18_chip_info }, + { .compatible = "adi,ltc2380-16", .data = <c2380_16_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ltc2378_of_match); + +static const struct spi_device_id ltc2378_spi_id[] = { + { .name = "ltc2364-16", .driver_data = (kernel_ulong_t)<c2364_16_chip_info }, + { .name = "ltc2364-18", .driver_data = (kernel_ulong_t)<c2364_18_chip_info }, + { .name = "ltc2367-16", .driver_data = (kernel_ulong_t)<c2367_16_chip_info }, + { .name = "ltc2367-18", .driver_data = (kernel_ulong_t)<c2367_18_chip_info }, + { .name = "ltc2368-16", .driver_data = (kernel_ulong_t)<c2368_16_chip_info }, + { .name = "ltc2368-18", .driver_data = (kernel_ulong_t)<c2368_18_chip_info }, + { .name = "ltc2369-18", .driver_data = (kernel_ulong_t)<c2369_18_chip_info }, + { .name = "ltc2370-16", .driver_data = (kernel_ulong_t)<c2370_16_chip_info }, + { .name = "ltc2376-16", .driver_data = (kernel_ulong_t)<c2376_16_chip_info }, + { .name = "ltc2376-18", .driver_data = (kernel_ulong_t)<c2376_18_chip_info }, + { .name = "ltc2376-20", .driver_data = (kernel_ulong_t)<c2376_20_chip_info }, + { .name = "ltc2377-16", .driver_data = (kernel_ulong_t)<c2377_16_chip_info }, + { .name = "ltc2377-18", .driver_data = (kernel_ulong_t)<c2377_18_chip_info }, + { .name = "ltc2377-20", .driver_data = (kernel_ulong_t)<c2377_20_chip_info }, + { .name = "ltc2378-16", .driver_data = (kernel_ulong_t)<c2378_16_chip_info }, + { .name = "ltc2378-18", .driver_data = (kernel_ulong_t)<c2378_18_chip_info }, + { .name = "ltc2378-20", .driver_data = (kernel_ulong_t)<c2378_20_chip_info }, + { .name = "ltc2379-18", .driver_data = (kernel_ulong_t)<c2379_18_chip_info }, + { .name = "ltc2380-16", .driver_data = (kernel_ulong_t)<c2380_16_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ltc2378_spi_id); + +static struct spi_driver ltc2378_driver = { + .driver = { + .name = "ltc2378", + .of_match_table = ltc2378_of_match + }, + .probe = ltc2378_probe, + .id_table = ltc2378_spi_id, +}; +module_spi_driver(ltc2378_driver); + +MODULE_AUTHOR("Marcelo Schmitt "); +MODULE_DESCRIPTION("Analog Devices LTC2378 ADC series driver"); +MODULE_LICENSE("GPL"); From b587716e8cc6c5701cec835c1929391a3fa4690c Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Mon, 27 Jul 2026 18:31:16 -0300 Subject: [PATCH 461/513] iio: adc: ltc2378: Enable high-speed data capture Make use of SPI transfer offloading to speed up data capture, enabling data acquisition at faster sample rates (up to 2 MSPS). Reviewed-by: Andy Shevchenko Reviewed-by: David Lechner Signed-off-by: Marcelo Schmitt Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 6 + drivers/iio/adc/ltc2378.c | 404 +++++++++++++++++++++++++++++++++++++- 2 files changed, 407 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 51d127f4957a..155c1079e5bc 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -948,6 +948,12 @@ config LTC2378 depends on SPI depends on REGULATOR || COMPILE_TEST depends on GPIOLIB + depends on PWM + select IIO_BUFFER + select IIO_BUFFER_DMA + select IIO_BUFFER_DMAENGINE + select SPI_OFFLOAD + select SPI_OFFLOAD_TRIGGER_PWM help Say yes here to build support for Analog Devices LTC2378-20 and similar analog to digital converters. diff --git a/drivers/iio/adc/ltc2378.c b/drivers/iio/adc/ltc2378.c index d161435fc3a3..48380a2d7dc6 100644 --- a/drivers/iio/adc/ltc2378.c +++ b/drivers/iio/adc/ltc2378.c @@ -16,16 +16,27 @@ #include #include #include +#include +#include #include #include #include +#include #include +#include +#include +#include #include #include +#include +#include #include #include +#define LTC2378_TDSDOBUSYL_NS 5 +#define LTC2378_TBUSYLH_NS 13 +#define LTC2378_TCNV_HIGH_NS 20 #define LTC2378_MAX_DATA_WAIT_US 4 /* max(TBUSYLH + TCONV + TDSDOBUSYL) */ #define LTC2378_CHANNEL(_sign, _real_bits, _storage_bits) \ @@ -54,9 +65,49 @@ #define LTC2378_PSEUDO_DIFF_CHANNEL(_real_bits) \ LTC2378_CHANNEL(0, _real_bits, (((_real_bits) > 16) ? 32 : 16)) +#define LTC2378_OFFLOAD_CHANNEL(_sign, _real_bits, _storage_bits) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .differential = _sign, \ + .channel = 0, \ + .channel2 = _sign ? 1 : 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = 0, \ + .scan_type = { \ + .format = _sign ? IIO_SCAN_FORMAT_SIGNED_INT : \ + IIO_SCAN_FORMAT_UNSIGNED_INT, \ + .realbits = _real_bits, \ + .storagebits = _storage_bits, \ + .shift = 0, \ + .endianness = IIO_CPU, \ + }, \ +} + +/* + * Currently, the available offload hardware + DMA configuration only supports + * pushing 32-bit data elements to DMA IIO buffers in CPU endianness. For 16-bit + * precision parts, those 32-bit elements (in CPU endianness) contain 2 bytes + * with data and 2 bytes always zeroed out. Nevertheless, for the offload use + * case, the IIO buffer is configured for 32 storage bits in CPU endianness so + * data is correctly aligned in user space despite 2 out of the 4 bytes being + * zeros. + */ +#define LTC2378_OFFLOAD_DIFF_CHANNEL(_real_bits) \ + LTC2378_OFFLOAD_CHANNEL(1, (_real_bits), 32) + +#define LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(_real_bits) \ + LTC2378_OFFLOAD_CHANNEL(0, (_real_bits), 32) + struct ltc2378_chip_info { const char *name; struct iio_chan_spec chan; + struct iio_chan_spec offload_chan; + unsigned int max_sample_rate_Hz; + unsigned int tconv_ns; }; struct ltc2378_state { @@ -66,6 +117,15 @@ struct ltc2378_state { struct mutex lock; /* Protect data acquisition cycle */ int ref_uV; struct spi_transfer xfer; + struct spi_transfer offload_xfer; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + struct pwm_waveform cnv_wf; + struct spi_message offload_msg; + struct spi_offload_trigger_config offload_trigger_config; + struct pwm_device *cnv_trigger; + unsigned int cnv_Hz; + unsigned int sample_freq_range[3]; /* * DMA (thus cache coherency maintenance) requires the transfer buffers @@ -85,96 +145,153 @@ struct ltc2378_state { static const struct ltc2378_chip_info ltc2364_16_chip_info = { .name = "ltc2364-16", .chan = LTC2378_PSEUDO_DIFF_CHANNEL(16), + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 250 * HZ_PER_KHZ, + .tconv_ns = 3000, }; static const struct ltc2378_chip_info ltc2364_18_chip_info = { .name = "ltc2364-18", .chan = LTC2378_PSEUDO_DIFF_CHANNEL(18), + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 250 * HZ_PER_KHZ, + .tconv_ns = 3000, }; static const struct ltc2378_chip_info ltc2367_16_chip_info = { .name = "ltc2367-16", .chan = LTC2378_PSEUDO_DIFF_CHANNEL(16), + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 500 * HZ_PER_KHZ, + .tconv_ns = 1500, }; static const struct ltc2378_chip_info ltc2367_18_chip_info = { .name = "ltc2367-18", .chan = LTC2378_PSEUDO_DIFF_CHANNEL(18), + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 500 * HZ_PER_KHZ, + .tconv_ns = 1500, }; static const struct ltc2378_chip_info ltc2368_16_chip_info = { .name = "ltc2368-16", .chan = LTC2378_PSEUDO_DIFF_CHANNEL(16), + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 527, }; static const struct ltc2378_chip_info ltc2368_18_chip_info = { .name = "ltc2368-18", .chan = LTC2378_PSEUDO_DIFF_CHANNEL(18), + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 527, }; static const struct ltc2378_chip_info ltc2369_18_chip_info = { .name = "ltc2369-18", .chan = LTC2378_PSEUDO_DIFF_CHANNEL(18), + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 1600 * HZ_PER_KHZ, + .tconv_ns = 412, }; static const struct ltc2378_chip_info ltc2370_16_chip_info = { .name = "ltc2370-16", .chan = LTC2378_PSEUDO_DIFF_CHANNEL(16), + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 2 * HZ_PER_MHZ, + .tconv_ns = 322, }; static const struct ltc2378_chip_info ltc2376_16_chip_info = { .name = "ltc2376-16", .chan = LTC2378_DIFF_CHANNEL(16), + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 250 * HZ_PER_KHZ, + .tconv_ns = 3000, }; static const struct ltc2378_chip_info ltc2376_18_chip_info = { .name = "ltc2376-18", .chan = LTC2378_DIFF_CHANNEL(18), + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 250 * HZ_PER_KHZ, + .tconv_ns = 3000, }; static const struct ltc2378_chip_info ltc2376_20_chip_info = { .name = "ltc2376-20", .chan = LTC2378_DIFF_CHANNEL(20), + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20), + .max_sample_rate_Hz = 250 * HZ_PER_KHZ, + .tconv_ns = 3000, }; static const struct ltc2378_chip_info ltc2377_16_chip_info = { .name = "ltc2377-16", .chan = LTC2378_DIFF_CHANNEL(16), + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 500 * HZ_PER_KHZ, + .tconv_ns = 1500, }; static const struct ltc2378_chip_info ltc2377_18_chip_info = { .name = "ltc2377-18", .chan = LTC2378_DIFF_CHANNEL(18), + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 500 * HZ_PER_KHZ, + .tconv_ns = 1500, }; static const struct ltc2378_chip_info ltc2377_20_chip_info = { .name = "ltc2377-20", .chan = LTC2378_DIFF_CHANNEL(20), + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20), + .max_sample_rate_Hz = 500 * HZ_PER_KHZ, + .tconv_ns = 1500, }; static const struct ltc2378_chip_info ltc2378_16_chip_info = { .name = "ltc2378-16", .chan = LTC2378_DIFF_CHANNEL(16), + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 527, }; static const struct ltc2378_chip_info ltc2378_18_chip_info = { .name = "ltc2378-18", .chan = LTC2378_DIFF_CHANNEL(18), + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 527, }; static const struct ltc2378_chip_info ltc2378_20_chip_info = { .name = "ltc2378-20", .chan = LTC2378_DIFF_CHANNEL(20), + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 675, }; static const struct ltc2378_chip_info ltc2379_18_chip_info = { .name = "ltc2379-18", .chan = LTC2378_DIFF_CHANNEL(18), + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 1600 * HZ_PER_KHZ, + .tconv_ns = 412, }; static const struct ltc2378_chip_info ltc2380_16_chip_info = { .name = "ltc2380-16", .chan = LTC2378_DIFF_CHANNEL(16), + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 2 * HZ_PER_MHZ, + .tconv_ns = 322, }; static int ltc2378_convert_and_acquire(struct ltc2378_state *st) @@ -257,7 +374,137 @@ static int ltc2378_read_raw(struct iio_dev *indio_dev, *val2 = chan->scan_type.realbits; return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->cnv_Hz; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} +static int ltc2378_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, long mask) +{ + struct ltc2378_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = st->sample_freq_range; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +/* + * SPI offload wiring schema + * + * +-------------+ +-------------+ + * | CNV |<-----+--| GPIO | + * | | +--| PWM0 | + * | | | | + * | | +--| PWM1 | + * | | | +-------------+ + * | | +->| TRIGGER | + * | | | | + * | ADC | | SPI | + * | | | controller | + * | | | | + * | SDI |<--------| SDO | + * | SDO |-------->| SDI | + * | SCLK |<--------| SCLK | + * +-------------+ +-------------+ + * + */ +static int ltc2378_update_conversion_rate(struct ltc2378_state *st, int freq_Hz) +{ + struct spi_offload_trigger_config config = st->offload_trigger_config; + unsigned int min_read_offset, offload_period_ns; + struct pwm_waveform cnv_wf = { }; + u64 target = LTC2378_TCNV_HIGH_NS; + unsigned int count; + u64 offload_offset_ns; + int ret; + + if (freq_Hz == 0) + return -EINVAL; + + if (!in_range(freq_Hz, 1, st->info->max_sample_rate_Hz)) + return -ERANGE; + + /* Configure CNV PWM waveform */ + cnv_wf.period_length_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq_Hz); + + /* + * Ensure CNV high time meets minimum requirement (20ns). The PWM + * hardware may round the duty cycle, so iterate until we get at least + * the minimum required high time (or reach a try count limit). + */ + count = 100; + do { + cnv_wf.duty_length_ns = target; + ret = pwm_round_waveform_might_sleep(st->cnv_trigger, &cnv_wf); + if (ret) + return ret; + target += 10; /* Increment by PWM duty cycle period */ + } while (count-- && cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS); + + /* Check the minimum CNV high time is met */ + if (cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS) + return -EDOM; + + /* + * Configure SPI offload PWM trigger. + * The trigger should fire after tBUSYLH + tCONV + tDSDOBUSYL. + * Minimum time needed: TBUSYLH (13ns) + TCONV (part-specific) + TDSDOBUSYL (5ns) + * + * Use the same period as CNV PWM to avoid timing issues. + * Convert back from period to frequency for the SPI offload API. + */ + offload_period_ns = cnv_wf.period_length_ns; + config.periodic.frequency_hz = div_u64(HZ_PER_GHZ, offload_period_ns); + min_read_offset = LTC2378_TBUSYLH_NS + st->info->tconv_ns + LTC2378_TDSDOBUSYL_NS; + offload_offset_ns = min_read_offset; + count = 100; + do { + config.periodic.offset_ns = offload_offset_ns; + ret = spi_offload_trigger_validate(st->offload_trigger, &config); + if (ret) + return ret; + offload_offset_ns += 10; + } while (count-- && config.periodic.offset_ns < min_read_offset); + + /* Check the minimum CNV to SCLK delay is met */ + if (config.periodic.offset_ns < min_read_offset) + return -EDOM; + + /* Check the PWM periods remain the same */ + offload_period_ns = div64_u64(HZ_PER_GHZ, config.periodic.frequency_hz); + if (cnv_wf.period_length_ns != offload_period_ns) + return -EDOM; + + st->offload_trigger_config = config; + st->cnv_wf = cnv_wf; + st->cnv_Hz = DIV_ROUND_CLOSEST_ULL(HZ_PER_GHZ, cnv_wf.period_length_ns); + + return 0; +} + +static int ltc2378_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ltc2378_state *st = iio_priv(indio_dev); + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return ltc2378_update_conversion_rate(st, val); default: return -EINVAL; } @@ -267,6 +514,111 @@ static const struct iio_info ltc2378_iio_info = { .read_raw = <c2378_read_raw, }; +static const struct iio_info ltc2378_offload_iio_info = { + .read_raw = <c2378_read_raw, + .read_avail = <c2378_read_avail, + .write_raw = <c2378_write_raw, +}; + +static int ltc2378_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ltc2378_state *st = iio_priv(indio_dev); + int ret; + + ret = pwm_set_waveform_might_sleep(st->cnv_trigger, &st->cnv_wf, true); + if (ret) + return ret; + + 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); + return ret; +} + +static int ltc2378_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ltc2378_state *st = iio_priv(indio_dev); + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + pwm_disable(st->cnv_trigger); + + return 0; +} + +static const struct iio_buffer_setup_ops ltc2378_offload_buffer_ops = { + .postenable = <c2378_offload_buffer_postenable, + .predisable = <c2378_offload_buffer_predisable, +}; + +static int ltc2378_prepare_offload_message(struct device *dev, + struct ltc2378_state *st) +{ + unsigned int resolution = st->info->offload_chan.scan_type.realbits; + + st->offload_xfer.bits_per_word = resolution; + st->offload_xfer.len = spi_bpw_to_bytes(resolution); + st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + + /* Initialize message with offload */ + spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1); + st->offload_msg.offload = st->offload; + + return devm_spi_optimize_message(dev, st->spi, &st->offload_msg); +} + +static int ltc2378_spi_offload_setup(struct iio_dev *indio_dev, + struct ltc2378_state *st) +{ + struct device *dev = &st->spi->dev; + struct dma_chan *rx_dma; + + indio_dev->setup_ops = <c2378_offload_buffer_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 ltc2378_pwm_get(struct ltc2378_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"); + + /* + * Disable the PWM connected to CNV in case it was left running by + * something else. + */ + pwm_disable(st->cnv_trigger); + + return 0; +} + +static const struct spi_offload_config ltc2378_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + static int ltc2378_ref_setup(struct device *dev, struct ltc2378_state *st) { int ret; @@ -307,7 +659,6 @@ static int ltc2378_probe(struct spi_device *spi) return ret; indio_dev->name = st->info->name; - indio_dev->info = <c2378_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; st->cnv_gpio = devm_gpiod_get(dev, "cnv", GPIOD_OUT_LOW); @@ -315,8 +666,53 @@ static int ltc2378_probe(struct spi_device *spi) return dev_err_probe(dev, PTR_ERR(st->cnv_gpio), "failed to get CNV GPIO"); - indio_dev->channels = &st->info->chan; - indio_dev->num_channels = 1; + st->offload = devm_spi_offload_get(dev, spi, <c2378_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + /* Fall back to low speed usage when no SPI offload is available. */ + if (ret == -ENODEV) { + indio_dev->info = <c2378_iio_info; + indio_dev->channels = &st->info->chan; + indio_dev->num_channels = 1; + } else if (ret) { + return dev_err_probe(dev, ret, "failed to get offload\n"); + } else { + indio_dev->info = <c2378_offload_iio_info; + indio_dev->channels = &st->info->offload_chan; + indio_dev->num_channels = 1; + ret = ltc2378_spi_offload_setup(indio_dev, st); + if (ret) + return dev_err_probe(dev, ret, + "failed to setup SPI offload\n"); + + ret = ltc2378_pwm_get(st); + if (ret) + return dev_err_probe(dev, ret, "failed to get PWM\n"); + + st->sample_freq_range[0] = 1; /* min */ + st->sample_freq_range[1] = 1; /* step */ + st->sample_freq_range[2] = st->info->max_sample_rate_Hz; /* max */ + + /* + * 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 = ltc2378_update_conversion_rate(st, st->info->max_sample_rate_Hz >> 4); + if (ret) + return dev_err_probe(dev, ret, + "failed to set offload samp freq\n"); + + ret = ltc2378_prepare_offload_message(&spi->dev, st); + if (ret) + return dev_err_probe(dev, ret, "failed to optimize SPI message\n"); + + /* + * Set single-read transfer bits_per_word so the SPI subsystem + * rearranges data to CPU endianness, enabling us to reuse + * offload_chan specifications for single-shot reads. + */ + st->xfer.bits_per_word = st->info->offload_chan.scan_type.realbits; + } st->xfer.rx_buf = &st->scan.data; st->xfer.len = spi_bpw_to_bytes(indio_dev->channels[0].scan_type.realbits); @@ -385,3 +781,5 @@ module_spi_driver(ltc2378_driver); MODULE_AUTHOR("Marcelo Schmitt "); MODULE_DESCRIPTION("Analog Devices LTC2378 ADC series driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); +MODULE_IMPORT_NS("SPI_OFFLOAD"); From 54355e3b189c7015d4575ae04a3bc7b803975bd1 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Mon, 27 Jul 2026 18:31:37 -0300 Subject: [PATCH 462/513] iio: adc: ltc2378: Enable triggered buffer data capture Enable users to run triggered data captures with LTC2378 and similar ADCs. Reviewed-by: Andy Shevchenko Reviewed-by: David Lechner Signed-off-by: Marcelo Schmitt Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 1 + drivers/iio/adc/ltc2378.c | 73 +++++++++++++++++++++++++++------------ 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 155c1079e5bc..990e7b3e7212 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -952,6 +952,7 @@ config LTC2378 select IIO_BUFFER select IIO_BUFFER_DMA select IIO_BUFFER_DMAENGINE + select IIO_TRIGGERED_BUFFER select SPI_OFFLOAD select SPI_OFFLOAD_TRIGGER_PWM help diff --git a/drivers/iio/adc/ltc2378.c b/drivers/iio/adc/ltc2378.c index 48380a2d7dc6..146eaadbeb6c 100644 --- a/drivers/iio/adc/ltc2378.c +++ b/drivers/iio/adc/ltc2378.c @@ -6,6 +6,7 @@ * Author: Marcelo Schmitt */ +#include #include #include #include @@ -32,6 +33,8 @@ #include #include #include +#include +#include #include #define LTC2378_TDSDOBUSYL_NS 5 @@ -104,7 +107,7 @@ struct ltc2378_chip_info { const char *name; - struct iio_chan_spec chan; + struct iio_chan_spec chan[2]; /* 1 physical chan + 1 timestamp chan */ struct iio_chan_spec offload_chan; unsigned int max_sample_rate_Hz; unsigned int tconv_ns; @@ -144,7 +147,7 @@ struct ltc2378_state { static const struct ltc2378_chip_info ltc2364_16_chip_info = { .name = "ltc2364-16", - .chan = LTC2378_PSEUDO_DIFF_CHANNEL(16), + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), .max_sample_rate_Hz = 250 * HZ_PER_KHZ, .tconv_ns = 3000, @@ -152,7 +155,7 @@ static const struct ltc2378_chip_info ltc2364_16_chip_info = { static const struct ltc2378_chip_info ltc2364_18_chip_info = { .name = "ltc2364-18", - .chan = LTC2378_PSEUDO_DIFF_CHANNEL(18), + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), .max_sample_rate_Hz = 250 * HZ_PER_KHZ, .tconv_ns = 3000, @@ -160,7 +163,7 @@ static const struct ltc2378_chip_info ltc2364_18_chip_info = { static const struct ltc2378_chip_info ltc2367_16_chip_info = { .name = "ltc2367-16", - .chan = LTC2378_PSEUDO_DIFF_CHANNEL(16), + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), .max_sample_rate_Hz = 500 * HZ_PER_KHZ, .tconv_ns = 1500, @@ -168,7 +171,7 @@ static const struct ltc2378_chip_info ltc2367_16_chip_info = { static const struct ltc2378_chip_info ltc2367_18_chip_info = { .name = "ltc2367-18", - .chan = LTC2378_PSEUDO_DIFF_CHANNEL(18), + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), .max_sample_rate_Hz = 500 * HZ_PER_KHZ, .tconv_ns = 1500, @@ -176,7 +179,7 @@ static const struct ltc2378_chip_info ltc2367_18_chip_info = { static const struct ltc2378_chip_info ltc2368_16_chip_info = { .name = "ltc2368-16", - .chan = LTC2378_PSEUDO_DIFF_CHANNEL(16), + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), .max_sample_rate_Hz = 1 * HZ_PER_MHZ, .tconv_ns = 527, @@ -184,7 +187,7 @@ static const struct ltc2378_chip_info ltc2368_16_chip_info = { static const struct ltc2378_chip_info ltc2368_18_chip_info = { .name = "ltc2368-18", - .chan = LTC2378_PSEUDO_DIFF_CHANNEL(18), + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), .max_sample_rate_Hz = 1 * HZ_PER_MHZ, .tconv_ns = 527, @@ -192,7 +195,7 @@ static const struct ltc2378_chip_info ltc2368_18_chip_info = { static const struct ltc2378_chip_info ltc2369_18_chip_info = { .name = "ltc2369-18", - .chan = LTC2378_PSEUDO_DIFF_CHANNEL(18), + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), .max_sample_rate_Hz = 1600 * HZ_PER_KHZ, .tconv_ns = 412, @@ -200,7 +203,7 @@ static const struct ltc2378_chip_info ltc2369_18_chip_info = { static const struct ltc2378_chip_info ltc2370_16_chip_info = { .name = "ltc2370-16", - .chan = LTC2378_PSEUDO_DIFF_CHANNEL(16), + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), .max_sample_rate_Hz = 2 * HZ_PER_MHZ, .tconv_ns = 322, @@ -208,7 +211,7 @@ static const struct ltc2378_chip_info ltc2370_16_chip_info = { static const struct ltc2378_chip_info ltc2376_16_chip_info = { .name = "ltc2376-16", - .chan = LTC2378_DIFF_CHANNEL(16), + .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), .max_sample_rate_Hz = 250 * HZ_PER_KHZ, .tconv_ns = 3000, @@ -216,7 +219,7 @@ static const struct ltc2378_chip_info ltc2376_16_chip_info = { static const struct ltc2378_chip_info ltc2376_18_chip_info = { .name = "ltc2376-18", - .chan = LTC2378_DIFF_CHANNEL(18), + .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), .max_sample_rate_Hz = 250 * HZ_PER_KHZ, .tconv_ns = 3000, @@ -224,7 +227,7 @@ static const struct ltc2378_chip_info ltc2376_18_chip_info = { static const struct ltc2378_chip_info ltc2376_20_chip_info = { .name = "ltc2376-20", - .chan = LTC2378_DIFF_CHANNEL(20), + .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20), .max_sample_rate_Hz = 250 * HZ_PER_KHZ, .tconv_ns = 3000, @@ -232,7 +235,7 @@ static const struct ltc2378_chip_info ltc2376_20_chip_info = { static const struct ltc2378_chip_info ltc2377_16_chip_info = { .name = "ltc2377-16", - .chan = LTC2378_DIFF_CHANNEL(16), + .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), .max_sample_rate_Hz = 500 * HZ_PER_KHZ, .tconv_ns = 1500, @@ -240,7 +243,7 @@ static const struct ltc2378_chip_info ltc2377_16_chip_info = { static const struct ltc2378_chip_info ltc2377_18_chip_info = { .name = "ltc2377-18", - .chan = LTC2378_DIFF_CHANNEL(18), + .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), .max_sample_rate_Hz = 500 * HZ_PER_KHZ, .tconv_ns = 1500, @@ -248,7 +251,7 @@ static const struct ltc2378_chip_info ltc2377_18_chip_info = { static const struct ltc2378_chip_info ltc2377_20_chip_info = { .name = "ltc2377-20", - .chan = LTC2378_DIFF_CHANNEL(20), + .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20), .max_sample_rate_Hz = 500 * HZ_PER_KHZ, .tconv_ns = 1500, @@ -256,7 +259,7 @@ static const struct ltc2378_chip_info ltc2377_20_chip_info = { static const struct ltc2378_chip_info ltc2378_16_chip_info = { .name = "ltc2378-16", - .chan = LTC2378_DIFF_CHANNEL(16), + .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), .max_sample_rate_Hz = 1 * HZ_PER_MHZ, .tconv_ns = 527, @@ -264,7 +267,7 @@ static const struct ltc2378_chip_info ltc2378_16_chip_info = { static const struct ltc2378_chip_info ltc2378_18_chip_info = { .name = "ltc2378-18", - .chan = LTC2378_DIFF_CHANNEL(18), + .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), .max_sample_rate_Hz = 1 * HZ_PER_MHZ, .tconv_ns = 527, @@ -272,7 +275,7 @@ static const struct ltc2378_chip_info ltc2378_18_chip_info = { static const struct ltc2378_chip_info ltc2378_20_chip_info = { .name = "ltc2378-20", - .chan = LTC2378_DIFF_CHANNEL(20), + .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20), .max_sample_rate_Hz = 1 * HZ_PER_MHZ, .tconv_ns = 675, @@ -280,7 +283,7 @@ static const struct ltc2378_chip_info ltc2378_20_chip_info = { static const struct ltc2378_chip_info ltc2379_18_chip_info = { .name = "ltc2379-18", - .chan = LTC2378_DIFF_CHANNEL(18), + .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), .max_sample_rate_Hz = 1600 * HZ_PER_KHZ, .tconv_ns = 412, @@ -288,7 +291,7 @@ static const struct ltc2378_chip_info ltc2379_18_chip_info = { static const struct ltc2378_chip_info ltc2380_16_chip_info = { .name = "ltc2380-16", - .chan = LTC2378_DIFF_CHANNEL(16), + .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), .max_sample_rate_Hz = 2 * HZ_PER_MHZ, .tconv_ns = 322, @@ -307,6 +310,25 @@ static int ltc2378_convert_and_acquire(struct ltc2378_state *st) return ret; } +static irqreturn_t ltc2378_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ltc2378_state *st = iio_priv(indio_dev); + int ret; + + ret = ltc2378_convert_and_acquire(st); + if (ret < 0) + goto err_out; + + iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan), + pf->timestamp); + +err_out: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + static int ltc2378_channel_single_read(const struct iio_chan_spec *chan, struct ltc2378_state *st, int *val) { @@ -671,8 +693,15 @@ static int ltc2378_probe(struct spi_device *spi) /* Fall back to low speed usage when no SPI offload is available. */ if (ret == -ENODEV) { indio_dev->info = <c2378_iio_info; - indio_dev->channels = &st->info->chan; - indio_dev->num_channels = 1; + indio_dev->channels = st->info->chan; + indio_dev->num_channels = ARRAY_SIZE(st->info->chan); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ltc2378_trigger_handler, + NULL); + if (ret) + return ret; } else if (ret) { return dev_err_probe(dev, ret, "failed to get offload\n"); } else { From d9166f1a996c91f480736dc588701d1028cec9a8 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Mon, 27 Jul 2026 18:31:58 -0300 Subject: [PATCH 463/513] iio: adc: ltc2378: Add support for LTC2338-18 LTC2338-18 is similar to LTC2378-18, differentiating from the already supported part mainly on the embedment of an internal voltage reference and addition of a resistor divider network connected to the input signal path. Extend the device driver, handling the internal reference and input signal attenuation, enabling it to also support LTC2338-18. Signed-off-by: Marcelo Schmitt Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ltc2378.c | 47 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ltc2378.c b/drivers/iio/adc/ltc2378.c index 146eaadbeb6c..b0ff2be1aee0 100644 --- a/drivers/iio/adc/ltc2378.c +++ b/drivers/iio/adc/ltc2378.c @@ -107,6 +107,8 @@ struct ltc2378_chip_info { const char *name; + unsigned int internal_ref_uV; + struct u32_fract internal_div; struct iio_chan_spec chan[2]; /* 1 physical chan + 1 timestamp chan */ struct iio_chan_spec offload_chan; unsigned int max_sample_rate_Hz; @@ -145,6 +147,16 @@ struct ltc2378_state { } scan __aligned(IIO_DMA_MINALIGN); }; +static const struct ltc2378_chip_info ltc2338_18_chip_info = { + .name = "ltc2338-18", + .internal_ref_uV = 2048000, + .internal_div = { .numerator = 5, .denominator = 2 }, + .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 527, +}; + static const struct ltc2378_chip_info ltc2364_16_chip_info = { .name = "ltc2364-16", .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, @@ -382,8 +394,11 @@ static int ltc2378_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; } - case IIO_CHAN_INFO_SCALE: + case IIO_CHAN_INFO_SCALE: { + struct u32_fract fract = st->info->internal_div; *val = st->ref_uV / MILLI; + if (fract.numerator && fract.denominator) + *val = mult_frac(*val, fract.numerator, fract.denominator); /* * For all LTC2378-like devices, the amount of bits that express * voltage magnitude depend on the polarity / output code format: @@ -396,6 +411,7 @@ static int ltc2378_read_raw(struct iio_dev *indio_dev, *val2 = chan->scan_type.realbits; return IIO_VAL_FRACTIONAL_LOG2; + } case IIO_CHAN_INFO_SAMP_FREQ: *val = st->cnv_Hz; return IIO_VAL_INT; @@ -641,6 +657,28 @@ static const struct spi_offload_config ltc2378_offload_config = { SPI_OFFLOAD_CAP_RX_STREAM_DMA, }; +static int ltc2378_refin_setup(struct device *dev, struct ltc2378_state *st) +{ + int ret; + + /* + * The internal reference buffer amplifies both the internal reference + * and REFIN by a factor of 2. + */ + ret = devm_regulator_get_enable_read_voltage(dev, "refin"); + if (ret == -ENODEV) { /* refin is optional */ + st->ref_uV = st->info->internal_ref_uV * 2; + return 0; + } + + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read refin regulator\n"); + + st->ref_uV = ret * 2; + + return 0; +} + static int ltc2378_ref_setup(struct device *dev, struct ltc2378_state *st) { int ret; @@ -676,7 +714,10 @@ static int ltc2378_probe(struct spi_device *spi) if (!st->info) return -EINVAL; - ret = ltc2378_ref_setup(dev, st); + if (st->info->internal_ref_uV) + ret = ltc2378_refin_setup(dev, st); + else + ret = ltc2378_ref_setup(dev, st); if (ret) return ret; @@ -750,6 +791,7 @@ static int ltc2378_probe(struct spi_device *spi) } static const struct of_device_id ltc2378_of_match[] = { + { .compatible = "adi,ltc2338-18", .data = <c2338_18_chip_info }, { .compatible = "adi,ltc2364-16", .data = <c2364_16_chip_info }, { .compatible = "adi,ltc2364-18", .data = <c2364_18_chip_info }, { .compatible = "adi,ltc2367-16", .data = <c2367_16_chip_info }, @@ -774,6 +816,7 @@ static const struct of_device_id ltc2378_of_match[] = { MODULE_DEVICE_TABLE(of, ltc2378_of_match); static const struct spi_device_id ltc2378_spi_id[] = { + { .name = "ltc2338-18", .driver_data = (kernel_ulong_t)<c2338_18_chip_info }, { .name = "ltc2364-16", .driver_data = (kernel_ulong_t)<c2364_16_chip_info }, { .name = "ltc2364-18", .driver_data = (kernel_ulong_t)<c2364_18_chip_info }, { .name = "ltc2367-16", .driver_data = (kernel_ulong_t)<c2367_16_chip_info }, From ce717fbfda6c697c5f0b4e081cdac4718c7016f5 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Mon, 27 Jul 2026 18:32:18 -0300 Subject: [PATCH 464/513] iio: ABI: Encourage differential voltage ABI usage So far, parts that could only do differential voltage measurements were only required to have a consistent labeling of their channels by complying either to voltageY or to voltageY-voltageZ ABI. Over time, code maintainers came to the conclusion that the voltageY-voltageZ ABI is better suited for differential-only chips as it provides a simple indication to users that the channels measure the difference of two inputs. Adjust the ABI documentation to recommend using the voltageY-voltageZ ABI for chips that do only differential voltage measurements. Signed-off-by: Marcelo Schmitt Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 5cc2e82c4997..7446e07b9ab2 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -162,10 +162,11 @@ Contact: linux-iio@vger.kernel.org Description: Raw (unscaled) differential voltage measurement equivalent to channel Y - channel Z where these channel numbers apply to the - physically equivalent inputs when non differential readings are - separately available. In differential only parts, then all that - is required is a consistent labeling. Units after application - of scale and offset are millivolts. + physically equivalent inputs when non-differential readings are + separately available. Even though differential-only parts had + been merely required to have consistent channel labeling, this + ABI is preferred over the non-differential one for those chips. + Units after application of scale and offset are millivolts. What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_rms_raw KernelVersion: 6.18 From 9da70a43b5fea60d758137f7f0ccfe19356cb5bb Mon Sep 17 00:00:00 2001 From: Daisuke Matsuda Date: Thu, 23 Jul 2026 08:19:12 +0000 Subject: [PATCH 465/513] fpga: altera-cvp: Avoid out-of-bounds read in trailing byte write The trailing byte path in altera_cvp_send_block() dereferences a u32 pointer even when only 1-3 bytes remain in the input buffer. If the buffer ends at a page or scatterlist boundary, this can read past the valid image data and fault. Copy the remaining bytes into a zero-initialized u32 before writing the final word so only valid bytes are read from the input buffer. Fixes: 34d1dc17ce97 ("fpga manager: Add Altera CvP driver") Cc: stable@vger.kernel.org Signed-off-by: Daisuke Matsuda Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260723081912.74082-1-dskmtsd@gmail.com Signed-off-by: Xu Yilun --- drivers/fpga/altera-cvp.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c index 44badfd11e1b..070c025ee215 100644 --- a/drivers/fpga/altera-cvp.c +++ b/drivers/fpga/altera-cvp.c @@ -16,6 +16,7 @@ #include #include #include +#include #define CVP_BAR 0 /* BAR used for data transfer in memory mode */ #define CVP_DUMMY_WR 244 /* dummy writes to clear CvP state machine */ @@ -261,7 +262,7 @@ static int altera_cvp_v2_wait_for_credit(struct fpga_manager *mgr, static int altera_cvp_send_block(struct altera_cvp_conf *conf, const u32 *data, size_t len) { - u32 mask, words = len / sizeof(u32); + u32 words = len / sizeof(u32); int i, remainder; for (i = 0; i < words; i++) @@ -270,9 +271,10 @@ static int altera_cvp_send_block(struct altera_cvp_conf *conf, /* write up to 3 trailing bytes, if any */ remainder = len % sizeof(u32); if (remainder) { - mask = BIT(remainder * 8) - 1; - if (mask) - conf->write_data(conf, *data & mask); + u32 word = 0; + + memcpy(&word, data, remainder); + conf->write_data(conf, word); } return 0; From 4216e549a265701c88df660f42d74a7eb4819af2 Mon Sep 17 00:00:00 2001 From: Ayananta Paul Date: Mon, 27 Jul 2026 20:56:34 +0530 Subject: [PATCH 466/513] fpga: dfl: fix spelling in sysfs-platform-dfl-port ABI documentation Correct the spelling of "responding" which was incorrectly written as "respoding" in the Document/ABI/testing/sysfs-platform-dfl-port ABI testing file. Issue identified using: `codespell -D - -S "*.bin,*.o,*.a,*.so,*.ko,*.gz,*.png,*.jpg" Documentation/` Signed-off-by: Ayananta Paul Acked-by: Randy Dunlap Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260727152634.5983-1-ayanantapaul@gmail.com Signed-off-by: Xu Yilun --- Documentation/ABI/testing/sysfs-platform-dfl-port | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-platform-dfl-port b/Documentation/ABI/testing/sysfs-platform-dfl-port index 65658267fcc0..40e555abca20 100644 --- a/Documentation/ABI/testing/sysfs-platform-dfl-port +++ b/Documentation/ABI/testing/sysfs-platform-dfl-port @@ -84,7 +84,7 @@ Description: Read-Write. Read this file to get errors detected on port and to clear errors. Write fails with -EINVAL if input parsing fails or input error code doesn't match. Write fails with -EBUSY or -ETIMEDOUT if error can't be cleared as hardware - in low power state (-EBUSY) or not respoding (-ETIMEDOUT). + in low power state (-EBUSY) or not responding (-ETIMEDOUT). What: /sys/bus/platform/devices/dfl-port.0/errors/first_error Date: August 2019 From 22bba9dfd2d47d9f702f641537b0ef5e0ab1a957 Mon Sep 17 00:00:00 2001 From: Wadim Mueller Date: Tue, 28 Jul 2026 23:49:40 +0200 Subject: [PATCH 467/513] iio: types: add IIO_VOLUMEFLOW channel type Add a new IIO channel type for liquid volumetric flow sensors. The unit exposed via the standard _scale attribute is cubic metres per second (m^3/s), following the SI convention used by the other IIO channel types. Update iio-core's name table, the iio_event_monitor whitelist and the sysfs-bus-iio ABI document to match. The new _scale attribute is folded into the existing shared _scale block; only the per-type _raw needs a fresh entry. Signed-off-by: Wadim Mueller Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 11 +++++++++++ drivers/iio/industrialio-core.c | 1 + include/uapi/linux/iio/types.h | 1 + tools/iio/iio_event_monitor.c | 2 ++ 4 files changed, 15 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 7446e07b9ab2..a4f5595722ad 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -508,6 +508,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_intensity_red_scale What: /sys/bus/iio/devices/iio:deviceX/in_intensity_green_scale What: /sys/bus/iio/devices/iio:deviceX/in_intensity_blue_scale What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_scale +What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_scale +What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_scale KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -2498,3 +2500,12 @@ Description: seconds, expressed as: - a range specified as "[min step max]" + +What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_raw +What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_raw +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled) volumetric flow rate reading from the channel. + To convert to standard units (cubic metres per second, m^3/s) + apply the channel's _scale (and _offset, when present). diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index bdf3d4c06331..37cf2817b3d0 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -99,6 +99,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_ATTENTION] = "attention", [IIO_ALTCURRENT] = "altcurrent", [IIO_COVERAGE] = "coverage", + [IIO_VOLUMEFLOW] = "volumeflow", }; static const char * const iio_modifier_names[] = { diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index c9295c707041..d0fb714ca0fd 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -54,6 +54,7 @@ enum iio_chan_type { IIO_ATTENTION, IIO_ALTCURRENT, IIO_COVERAGE, + IIO_VOLUMEFLOW, }; enum iio_modifier { diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index bc3ef4c77c2b..b5c5479b9d78 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -66,6 +66,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_ATTENTION] = "attention", [IIO_ALTCURRENT] = "altcurrent", [IIO_COVERAGE] = "coverage", + [IIO_VOLUMEFLOW] = "volumeflow", }; static const char * const iio_ev_type_text[] = { @@ -196,6 +197,7 @@ static bool event_is_known(struct iio_event_data *event) case IIO_ATTENTION: case IIO_ALTCURRENT: case IIO_COVERAGE: + case IIO_VOLUMEFLOW: break; default: return false; From 7462dce2368da9c055bd91f9622eaf393eb75049 Mon Sep 17 00:00:00 2001 From: Wadim Mueller Date: Tue, 28 Jul 2026 23:49:41 +0200 Subject: [PATCH 468/513] dt-bindings: iio: flow: add Sensirion SLF3S liquid flow sensor Document the bindings for the Sensirion SLF3S family of digital liquid-flow sensors on I2C. The family currently covers the SLF3S-0600F, SLF3S-1300F and SLF3S-4000B variants. All variants share the same register map and differ only in the flow scale factor and the calibrated measurement range, which the driver reads back from the product-information register. Since that lookup is needed in any case, a fallback compatible would buy nothing, so each variant gets its own compatible without a fallback. A future drop-in compatible part can then claim one of these compatibles and work on kernels that do not know its product ID yet. The active calibration medium (water / IPA) is runtime-switchable via the in_volumeflow_medium sysfs attribute and therefore not a DT property. Signed-off-by: Wadim Mueller Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jonathan Cameron --- .../bindings/iio/flow/sensirion,slf3s.yaml | 55 +++++++++++++++++++ MAINTAINERS | 7 +++ 2 files changed, 62 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml diff --git a/Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml b/Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml new file mode 100644 index 000000000000..79718f4f98bd --- /dev/null +++ b/Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/flow/sensirion,slf3s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sensirion SLF3S liquid flow sensor + +maintainers: + - Wadim Mueller + +description: + Family of digital liquid-flow sensors from Sensirion with I2C interface. All + family members share the same register map; sub-types differ only in the flow + scale factor and the calibrated measurement range. The sub-type can be + identified from the product-information register. + +properties: + compatible: + enum: + - sensirion,slf3s-0600f + - sensirion,slf3s-1300f + - sensirion,slf3s-4000b + + reg: + maxItems: 1 + + vdd-supply: true + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - vdd-supply + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + flow-sensor@8 { + compatible = "sensirion,slf3s-0600f"; + reg = <0x08>; + vdd-supply = <®_3v3>; + interrupt-parent = <&gpio0>; + interrupts = <0 IRQ_TYPE_EDGE_RISING>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index cf8c63f72ca0..21e120ad5586 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24591,6 +24591,13 @@ S: Maintained F: Documentation/ABI/testing/sysfs-bus-iio-chemical-sgp40 F: drivers/iio/chemical/sgp40.c +SENSIRION SLF3S LIQUID FLOW SENSOR DRIVER +M: Wadim Mueller +R: Maxwell Doose +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml + SENSIRION SPS30 AIR POLLUTION SENSOR DRIVER M: Tomasz Duszynski S: Maintained From 1647a1b67377e9f7b4ca75bc5607eda6791abd0a Mon Sep 17 00:00:00 2001 From: Wadim Mueller Date: Tue, 28 Jul 2026 23:49:42 +0200 Subject: [PATCH 469/513] iio: core: add IIO_VAL_DECIMAL64_FEMTO format type Extend the IIO_VAL_DECIMAL64_* family with a femto-scaled variant (scale 15), following the existing MILLI/MICRO/NANO/PICO pattern. Both the read formatting path in __iio_format_value() and the write parsing path in iio_write_channel_info() (via kstrtodec64()) already derive their scale from "type - IIO_VAL_DECIMAL64_BASE", so the new type only needs to be added to the respective switch cases. This is needed by drivers reporting very small SI quantities where the existing pico scale loses precision. For example the Sensirion SLF3S liquid flow sensor reports its volume-flow scale in m^3/s, where the SLF3S-0600F scale is ~1.667e-12 m^3/s: at pico scale only a single significant digit survives, whereas femto scale preserves the full sensor resolution. Signed-off-by: Wadim Mueller Reviewed-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 2 ++ include/linux/iio/types.h | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 37cf2817b3d0..767a7794624a 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -715,6 +715,7 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, case IIO_VAL_DECIMAL64_MICRO: case IIO_VAL_DECIMAL64_NANO: case IIO_VAL_DECIMAL64_PICO: + case IIO_VAL_DECIMAL64_FEMTO: { int scale = type - IIO_VAL_DECIMAL64_BASE; s64 frac; @@ -1032,6 +1033,7 @@ static ssize_t iio_write_channel_info(struct device *dev, case IIO_VAL_DECIMAL64_MICRO: case IIO_VAL_DECIMAL64_NANO: case IIO_VAL_DECIMAL64_PICO: + case IIO_VAL_DECIMAL64_FEMTO: dec_scale = type - IIO_VAL_DECIMAL64_BASE; fallthrough; case IIO_VAL_INT_64: diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 924ac9dc6893..d8944c9e5e90 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -42,6 +42,7 @@ enum iio_event_info { #define IIO_VAL_DECIMAL64_MICRO (IIO_VAL_DECIMAL64_BASE + 6) #define IIO_VAL_DECIMAL64_NANO (IIO_VAL_DECIMAL64_BASE + 9) #define IIO_VAL_DECIMAL64_PICO (IIO_VAL_DECIMAL64_BASE + 12) +#define IIO_VAL_DECIMAL64_FEMTO (IIO_VAL_DECIMAL64_BASE + 15) static inline s64 iio_val_s64_compose(s32 val0, s32 val1) { From d240b0b8a1ceeacc618890de971d9f4e40661c27 Mon Sep 17 00:00:00 2001 From: Wadim Mueller Date: Tue, 28 Jul 2026 23:49:43 +0200 Subject: [PATCH 470/513] iio: flow: add Sensirion SLF3S liquid flow sensor driver Add a driver for the Sensirion SLF3S family of digital liquid-flow sensors on I2C. Currently supported variants are SLF3S-0600F, SLF3S-1300F and SLF3S-4000B; they share the same register map and differ only in flow-scale factor and calibrated measurement range. The variant (and therefore the scale) is auto-detected from the product-information register at probe time; a sensor reporting an unknown sub-type falls back to the variant named in the device tree / I2C table, so a drop-in replacement part keeps working on a kernel that does not know its sub-type yet. Each measurement frame returns a 16-bit signed flow value, a 16-bit signed temperature reading and a status word, each protected by a CRC-8 byte. The driver exposes the flow rate as IIO_VOLUMEFLOW and the temperature as IIO_TEMP via the standard IIO read_raw / read_scale interface. The volume-flow scale is reported in m^3/s. As the per-LSB scale is on the order of 1e-12 m^3/s, it is emitted as a 64-bit fixed-point value with femto (1e-15) resolution (IIO_VAL_DECIMAL64_FEMTO) so the small SI value keeps full precision. This relies on the IIO_VAL_DECIMAL64_FEMTO format type added earlier in this series. The active calibration medium can be switched at runtime between the factory-calibrated water and isopropyl-alcohol modes via the in_volumeflow_medium sysfs attribute; the sensor starts in water mode after probe. The sensor has no low-power state of its own, so system suspend stops the measurement and disables the vdd supply; resume powers the sensor back up, waits out the power-up time and restarts the measurement with the previously active medium, following the scd30/scd4x precedent. This driver also creates the drivers/iio/flow/ subdirectory and the corresponding Kconfig/Makefile glue. Signed-off-by: Wadim Mueller Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio-flow | 21 + MAINTAINERS | 1 + drivers/iio/Kconfig | 1 + drivers/iio/Makefile | 1 + drivers/iio/flow/Kconfig | 22 + drivers/iio/flow/Makefile | 7 + drivers/iio/flow/slf3s.c | 542 +++++++++++++++++++ 7 files changed, 595 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-flow create mode 100644 drivers/iio/flow/Kconfig create mode 100644 drivers/iio/flow/Makefile create mode 100644 drivers/iio/flow/slf3s.c diff --git a/Documentation/ABI/testing/sysfs-bus-iio-flow b/Documentation/ABI/testing/sysfs-bus-iio-flow new file mode 100644 index 000000000000..fece2ecfa994 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-flow @@ -0,0 +1,21 @@ +What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_medium +What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_medium +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + The calibration medium the flow sensor uses to convert its + raw reading into a volumetric flow rate. Liquid flow sensors + are factory-calibrated per medium, so the selected medium has + to match the fluid actually flowing through the sensor for the + reported flow rate to be correct. + + Reading returns the currently active medium; writing one of the + strings listed in in_volumeflow_medium_available selects it. + +What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_medium_available +What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_medium_available +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Space separated list of the calibration media supported by the + device, e.g. "water ipa". diff --git a/MAINTAINERS b/MAINTAINERS index 21e120ad5586..bd5cfcb7d887 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24597,6 +24597,7 @@ R: Maxwell Doose L: linux-iio@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml +F: drivers/iio/flow/slf3s.c SENSIRION SPS30 AIR POLLUTION SENSOR DRIVER M: Tomasz Duszynski diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 661127aed2f9..652557a5b851 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -92,6 +92,7 @@ source "drivers/iio/common/Kconfig" source "drivers/iio/dac/Kconfig" source "drivers/iio/dummy/Kconfig" source "drivers/iio/filter/Kconfig" +source "drivers/iio/flow/Kconfig" source "drivers/iio/frequency/Kconfig" source "drivers/iio/gyro/Kconfig" source "drivers/iio/health/Kconfig" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index cb80ef837e84..f03a4100c800 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -29,6 +29,7 @@ obj-y += dac/ obj-y += dummy/ obj-y += gyro/ obj-y += filter/ +obj-y += flow/ obj-y += frequency/ obj-y += health/ obj-y += humidity/ diff --git a/drivers/iio/flow/Kconfig b/drivers/iio/flow/Kconfig new file mode 100644 index 000000000000..e0e1a8e3654a --- /dev/null +++ b/drivers/iio/flow/Kconfig @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Liquid / gas flow sensor drivers +# +# When adding new entries keep the list in alphabetical order + +menu "Flow sensors" + +config SENSIRION_SLF3S + tristate "Sensirion SLF3S liquid flow sensor" + depends on I2C + select CRC8 + help + Say yes here to build support for the Sensirion SLF3S family of + digital liquid-flow sensors (SLF3S-0600F, SLF3S-1300F and + SLF3S-4000B). The driver reports the volumetric flow rate and the + embedded temperature reading via the standard IIO interface. + + To compile this driver as a module, choose M here: the module + will be called slf3s. + +endmenu diff --git a/drivers/iio/flow/Makefile b/drivers/iio/flow/Makefile new file mode 100644 index 000000000000..3cf4ab95c69c --- /dev/null +++ b/drivers/iio/flow/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for industrial I/O flow sensor drivers +# + +# When adding new entries keep the list in alphabetical order +obj-$(CONFIG_SENSIRION_SLF3S) += slf3s.o diff --git a/drivers/iio/flow/slf3s.c b/drivers/iio/flow/slf3s.c new file mode 100644 index 000000000000..dfa7c1409045 --- /dev/null +++ b/drivers/iio/flow/slf3s.c @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sensirion SLF3S liquid flow sensor driver. + * + * Supports the SLF3S-0600F, SLF3S-1300F and SLF3S-4000B liquid-flow + * sensors over I2C. Each measurement frame returns a 16-bit signed + * flow value, a 16-bit signed temperature value and a status word, + * each protected by a CRC-8 byte. + * + * The active calibration medium (water or isopropyl alcohol) is + * runtime-switchable via the in_volumeflow_medium sysfs attribute and + * defaults to water. + * + * Datasheet: https://sensirion.com/products/catalog/SLF3S-0600F/ + * + * Copyright (C) 2026 CMBlu Energy GmbH + * Author: Wadim Mueller + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SLF3S_CRC8_POLY 0x31 +#define SLF3S_CRC8_INIT 0xff + +#define SLF3S_PRODUCT_ID_LEN 18 +#define SLF3S_PRODUCT_FAMILY_BYTE 1 +#define SLF3S_PRODUCT_SUBTYPE_BYTE 3 +#define SLF3S_PRODUCT_FAMILY_ID 0x03 + +/* Datasheet section 2.2: tPU = 25 ms max from power-on to first cmd. */ +#define SLF3S_POWER_UP_DELAY_US (25 * USEC_PER_MSEC) +/* Datasheet section 2.2: tw = 60 ms typical until first valid sample. */ +#define SLF3S_MEAS_START_DELAY_US (60 * USEC_PER_MSEC) + +static const u8 slf3s_cmd_prep_pid[] = { 0x36, 0x7c }; +static const u8 slf3s_cmd_read_pid[] = { 0xe1, 0x02 }; +static const u8 slf3s_cmd_start_water[] = { 0x36, 0x08 }; +static const u8 slf3s_cmd_start_ipa[] = { 0x36, 0x15 }; +static const u8 slf3s_cmd_stop_meas[] = { 0x3f, 0xf9 }; + +enum slf3s_medium { + SLF3S_MEDIUM_WATER, + SLF3S_MEDIUM_IPA, +}; + +static const char * const slf3s_medium_modes[] = { + [SLF3S_MEDIUM_WATER] = "water", + [SLF3S_MEDIUM_IPA] = "ipa", +}; + +enum slf3s_variant_id { + SLF3S_0600F, + SLF3S_1300F, + SLF3S_4000B, +}; + +/** + * struct slf3s_variant - per-variant calibration constants + * @sub_type: product-info sub-type byte returned by the sensor + * @name: name reported via @iio_dev.name + * @scale: flow scale in l/s per LSB + */ +struct slf3s_variant { + u8 sub_type; + const char *name; + struct s32_fract scale; +}; + +static const struct slf3s_variant slf3s_variants[] = { + [SLF3S_0600F] = { + .sub_type = 0x03, + .name = "slf3s-0600f", + .scale = { .numerator = 1, .denominator = 600 * MICRO }, + }, + [SLF3S_1300F] = { + .sub_type = 0x02, + .name = "slf3s-1300f", + .scale = { .numerator = 1, .denominator = 30 * MICRO }, + }, + [SLF3S_4000B] = { + .sub_type = 0x05, + .name = "slf3s-4000b", + .scale = { .numerator = 1, .denominator = 1920 * MILLI }, + }, +}; + +/** + * struct slf3s_data - per-device state + * @client: I2C client this instance is bound to + * @vdd: supply regulator, disabled while suspended + * @variant: pointer into @slf3s_variants for the detected device + * @medium: currently active calibration medium + * @lock: serialises the multi-step command/response exchanges + * @crc_table: pre-computed CRC-8 lookup table for SLF3S_CRC8_POLY + */ +struct slf3s_data { + struct i2c_client *client; + struct regulator *vdd; + const struct slf3s_variant *variant; + enum slf3s_medium medium; + struct mutex lock; + u8 crc_table[CRC8_TABLE_SIZE]; +}; + +static int slf3s_send_cmd(struct i2c_client *client, const u8 *cmd) +{ + int ret; + + ret = i2c_master_send(client, cmd, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + + return 0; +} + +/* Start continuous measurement and wait until the first sample is valid. */ +static int slf3s_start_meas(struct slf3s_data *sf, enum slf3s_medium medium) +{ + const u8 *cmd = (medium == SLF3S_MEDIUM_IPA) ? slf3s_cmd_start_ipa + : slf3s_cmd_start_water; + int ret; + + ret = slf3s_send_cmd(sf->client, cmd); + if (ret) + return ret; + + fsleep(SLF3S_MEAS_START_DELAY_US); + + return 0; +} + +static bool slf3s_crc_valid(const struct slf3s_data *sf, const u8 *block) +{ + return crc8(sf->crc_table, block, 2, SLF3S_CRC8_INIT) == block[2]; +} + +/* + * Read the product-info block and pick the matching variant. The + * sub-type byte returned by the sensor is the source of truth; a + * DT-supplied compatible only seeds an initial guess and is overridden + * on mismatch (with an informational message so misconfigured device + * trees are easy to spot). + * + * Bus / CRC failures are real errors and fail probe. An unknown + * sub-type byte falls back to the variant named in the device tree / + * I2C table, so a drop-in replacement part that lists one of the known + * compatibles keeps working on an older kernel that does not know its + * sub-type yet. Without any match data probe fails since no + * meaningful scale can be published. + */ +static int slf3s_detect_variant(struct slf3s_data *sf) +{ + struct i2c_client *client = sf->client; + u8 buf[SLF3S_PRODUCT_ID_LEN]; + int ret; + + ret = slf3s_send_cmd(client, slf3s_cmd_prep_pid); + if (ret) + return ret; + + ret = slf3s_send_cmd(client, slf3s_cmd_read_pid); + if (ret) + return ret; + + ret = i2c_master_recv(client, buf, sizeof(buf)); + if (ret < 0) + return ret; + if (ret != sizeof(buf)) + return -EIO; + + for (unsigned int i = 0; i < SLF3S_PRODUCT_ID_LEN; i += 3) { + if (!slf3s_crc_valid(sf, &buf[i])) + return -EIO; + } + + if (buf[SLF3S_PRODUCT_FAMILY_BYTE] != SLF3S_PRODUCT_FAMILY_ID) + dev_info(&client->dev, + "unexpected family byte 0x%02x (expected 0x%02x)\n", + buf[SLF3S_PRODUCT_FAMILY_BYTE], + SLF3S_PRODUCT_FAMILY_ID); + + for (unsigned int i = 0; i < ARRAY_SIZE(slf3s_variants); i++) { + if (buf[SLF3S_PRODUCT_SUBTYPE_BYTE] != + slf3s_variants[i].sub_type) + continue; + + if (sf->variant && sf->variant != &slf3s_variants[i]) + dev_info(&client->dev, + "DT compatible says %s but sensor reports %s; using the latter\n", + sf->variant->name, + slf3s_variants[i].name); + + sf->variant = &slf3s_variants[i]; + + return 0; + } + + if (sf->variant) { + dev_warn(&client->dev, + "unknown SLF3S sub-type 0x%02x, assuming %s\n", + buf[SLF3S_PRODUCT_SUBTYPE_BYTE], sf->variant->name); + return 0; + } + + dev_err(&client->dev, "unknown SLF3S sub-type 0x%02x\n", + buf[SLF3S_PRODUCT_SUBTYPE_BYTE]); + + return -ENODEV; +} + +static int slf3s_read_sample(struct slf3s_data *sf, int *flow, int *temp) +{ + /* + * A measurement frame is flow, temperature and a signaling-flags + * word, each followed by a CRC byte. Only flow and temperature are + * used, so the read is stopped after their two words (6 bytes). + */ + u8 buf[6]; + int ret; + + ret = i2c_master_recv(sf->client, buf, sizeof(buf)); + if (ret < 0) + return ret; + if (ret != sizeof(buf)) + return -EIO; + + for (unsigned int i = 0; i < sizeof(buf); i += 3) { + if (!slf3s_crc_valid(sf, &buf[i])) + return -EIO; + } + + *flow = sign_extend32(get_unaligned_be16(&buf[0]), 15); + *temp = sign_extend32(get_unaligned_be16(&buf[3]), 15); + + return 0; +} + +static int slf3s_get_medium(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct slf3s_data *sf = iio_priv(indio_dev); + + return sf->medium; +} + +static int slf3s_set_medium(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct slf3s_data *sf = iio_priv(indio_dev); + int ret; + + guard(mutex)(&sf->lock); + + ret = slf3s_send_cmd(sf->client, slf3s_cmd_stop_meas); + if (ret) + return ret; + + ret = slf3s_start_meas(sf, mode); + if (ret) { + /* + * Try to restart with the previous medium so the sensor is + * not left idle, which would fail all subsequent reads. + */ + if (slf3s_start_meas(sf, sf->medium)) + dev_warn(&sf->client->dev, + "failed to restart measurement, reads will fail until a medium is set\n"); + return ret; + } + + sf->medium = mode; + + return 0; +} + +static const struct iio_enum slf3s_medium_enum = { + .items = slf3s_medium_modes, + .num_items = ARRAY_SIZE(slf3s_medium_modes), + .get = slf3s_get_medium, + .set = slf3s_set_medium, +}; + +static const struct iio_chan_spec_ext_info slf3s_ext_info[] = { + IIO_ENUM("medium", IIO_SHARED_BY_TYPE, &slf3s_medium_enum), + IIO_ENUM_AVAILABLE("medium", IIO_SHARED_BY_TYPE, &slf3s_medium_enum), + { } +}; + +static const struct iio_chan_spec slf3s_channels[] = { + { + .type = IIO_VOLUMEFLOW, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .ext_info = slf3s_ext_info, + }, + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static int slf3s_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct slf3s_data *sf = iio_priv(indio_dev); + int flow, temp, ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + scoped_guard(mutex, &sf->lock) + ret = slf3s_read_sample(sf, &flow, &temp); + if (ret) + return ret; + + *val = (chan->type == IIO_VOLUMEFLOW) ? flow : temp; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_VOLUMEFLOW) { + /* + * The variant scale is the flow per LSB in l/s, but + * IIO reports volume flow in m^3/s (1 l = 1e-3 m^3). + * These values are tiny (~1.67e-12 m^3/s for the + * SLF3S-0600F), so emit a 64-bit fixed-point value with + * femto (1e-15) resolution to preserve precision. + * Converting l/s to m^3/s (/ MILLI) and scaling to femto + * (* FEMTO) leaves a net * (FEMTO / MILLI) factor. + */ + const struct slf3s_variant *v = sf->variant; + s64 num = (s64)v->scale.numerator * (FEMTO / MILLI); + s64 scale = DIV_S64_ROUND_CLOSEST(num, + v->scale.denominator); + + iio_val_s64_decompose(scale, val, val2); + + return IIO_VAL_DECIMAL64_FEMTO; + } + /* Temperature LSB = 1/200 degC; IIO_TEMP wants milli-degC. */ + *val = MILLIDEGREE_PER_DEGREE / 200; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info slf3s_info = { + .read_raw = slf3s_read_raw, +}; + +static void slf3s_stop_meas(void *data) +{ + struct slf3s_data *sf = data; + + slf3s_send_cmd(sf->client, slf3s_cmd_stop_meas); +} + +static void slf3s_disable_vdd(void *data) +{ + struct slf3s_data *sf = data; + + regulator_disable(sf->vdd); +} + +static int slf3s_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct iio_dev *indio_dev; + struct slf3s_data *sf; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*sf)); + if (!indio_dev) + return -ENOMEM; + + sf = iio_priv(indio_dev); + sf->client = client; + i2c_set_clientdata(client, indio_dev); + sf->variant = i2c_get_match_data(client); + sf->medium = SLF3S_MEDIUM_WATER; + crc8_populate_msb(sf->crc_table, SLF3S_CRC8_POLY); + + ret = devm_mutex_init(dev, &sf->lock); + if (ret) + return ret; + + sf->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(sf->vdd)) + return dev_err_probe(dev, PTR_ERR(sf->vdd), + "failed to get vdd supply\n"); + + ret = regulator_enable(sf->vdd); + if (ret) + return dev_err_probe(dev, ret, "failed to enable vdd supply\n"); + + ret = devm_add_action_or_reset(dev, slf3s_disable_vdd, sf); + if (ret) + return ret; + + fsleep(SLF3S_POWER_UP_DELAY_US); + + /* + * The sensor may still be in continuous measurement mode from a + * previous boot (warm reboot / kexec); in that case it would NACK + * the product-id command below. Stop it first and ignore the error + * if it was already idle. + */ + slf3s_send_cmd(client, slf3s_cmd_stop_meas); + + ret = slf3s_detect_variant(sf); + if (ret) + return dev_err_probe(dev, ret, "product info read failed\n"); + + ret = slf3s_start_meas(sf, sf->medium); + if (ret) + return dev_err_probe(dev, ret, + "failed to start measurement\n"); + + ret = devm_add_action_or_reset(dev, slf3s_stop_meas, sf); + if (ret) + return ret; + + indio_dev->name = sf->variant->name; + indio_dev->channels = slf3s_channels; + indio_dev->num_channels = ARRAY_SIZE(slf3s_channels); + indio_dev->info = &slf3s_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + return devm_iio_device_register(dev, indio_dev); +} + +/* + * The sensor has no low-power state of its own, so stop the measurement + * and cut the supply while suspended. Resume powers it back up, waits + * out the power-up time and restarts with the medium that was active + * before. + */ +static int slf3s_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct slf3s_data *sf = iio_priv(indio_dev); + int ret; + + guard(mutex)(&sf->lock); + + ret = slf3s_send_cmd(sf->client, slf3s_cmd_stop_meas); + if (ret) + return ret; + + return regulator_disable(sf->vdd); +} + +static int slf3s_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct slf3s_data *sf = iio_priv(indio_dev); + int ret; + + guard(mutex)(&sf->lock); + + ret = regulator_enable(sf->vdd); + if (ret) + return ret; + + fsleep(SLF3S_POWER_UP_DELAY_US); + + return slf3s_start_meas(sf, sf->medium); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(slf3s_pm_ops, slf3s_suspend, slf3s_resume); + +static const struct i2c_device_id slf3s_id[] = { + { + .name = "slf3s-0600f", + .driver_data = (kernel_ulong_t)&slf3s_variants[SLF3S_0600F], + }, + { + .name = "slf3s-1300f", + .driver_data = (kernel_ulong_t)&slf3s_variants[SLF3S_1300F], + }, + { + .name = "slf3s-4000b", + .driver_data = (kernel_ulong_t)&slf3s_variants[SLF3S_4000B], + }, + { } +}; +MODULE_DEVICE_TABLE(i2c, slf3s_id); + +static const struct of_device_id slf3s_of_match[] = { + { + .compatible = "sensirion,slf3s-0600f", + .data = &slf3s_variants[SLF3S_0600F], + }, + { + .compatible = "sensirion,slf3s-1300f", + .data = &slf3s_variants[SLF3S_1300F], + }, + { + .compatible = "sensirion,slf3s-4000b", + .data = &slf3s_variants[SLF3S_4000B], + }, + { } +}; +MODULE_DEVICE_TABLE(of, slf3s_of_match); + +static struct i2c_driver slf3s_driver = { + .driver = { + .name = "slf3s", + .of_match_table = slf3s_of_match, + .pm = pm_sleep_ptr(&slf3s_pm_ops), + }, + .probe = slf3s_probe, + .id_table = slf3s_id, +}; +module_i2c_driver(slf3s_driver); + +MODULE_AUTHOR("Wadim Mueller "); +MODULE_DESCRIPTION("Sensirion SLF3S liquid flow sensor driver"); +MODULE_LICENSE("GPL"); From 8756acd30919a3e9f547ea4a1d4b7f7895f4d340 Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Fri, 31 Jul 2026 17:20:48 +0400 Subject: [PATCH 471/513] iio: light: cm32181: return zero after writing calibscale The write_raw callback is documented to return 0 on success or a negative error code. However, the IIO_CHAN_INFO_CALIBSCALE case returns 'val' (the user-supplied value) instead of 0. Fix it by returning 0 on success, matching the behavior of other calibscale implementations in the subsystem. Fixes: 971672c0b3cc ("iio: add Capella CM32181 ambient light sensor driver.") Signed-off-by: Giorgi Tchankvetadze Reviewed-by: Joshua Crofts Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/cm32181.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c index 2590fc8fd154..b32a94028f09 100644 --- a/drivers/iio/light/cm32181.c +++ b/drivers/iio/light/cm32181.c @@ -368,7 +368,7 @@ static int cm32181_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_CALIBSCALE: cm32181->calibscale = val; - return val; + return 0; case IIO_CHAN_INFO_INT_TIME: ret = cm32181_write_als_it(cm32181, val2); return ret; From 4a56c646e1bd4f22a2dcd65ef3723af957c651a9 Mon Sep 17 00:00:00 2001 From: Francesco Lavra Date: Fri, 31 Jul 2026 19:47:02 +0200 Subject: [PATCH 472/513] iio: imu: st_lsm6dsx: Update enable mask when using sensor fusion The enable_mask struct member keeps track of which sensors are enabled in the IMU. When enabling and disabling the sensor fusion functionality, the driver does not properly update this struct member. This prevents a correct calculation of the number of samples that should be read from the hardware FIFO; as a result, reads from the FIFO can be unnecessarily split into multiple transactions, some of which can read past the FIFO length. Fixes: cd4e1141bff8 ("iio: imu: st_lsm6dsx: Add support for rotation sensor") Signed-off-by: Francesco Lavra Acked-by: Lorenzo Bianconi Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c index 2e72c7ba94dd..5321033daebc 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c @@ -112,6 +112,11 @@ int st_lsm6dsx_fusion_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable) return err; } + if (enable) + hw->enable_mask |= BIT(ST_LSM6DSX_ID_FUSION); + else + hw->enable_mask &= ~BIT(ST_LSM6DSX_ID_FUSION); + return st_lsm6dsx_fusion_page_disable(hw); } From 0efaefce4e95a3331550329c0078b2fb38b3ff1f Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Fri, 31 Jul 2026 16:39:11 -0500 Subject: [PATCH 473/513] iio: Update email for Maxwell Doose I'm transitioning to a new email, add mapping and update MAINTAINERS entries to keep email up to date. Signed-off-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- .mailmap | 1 + .../devicetree/bindings/iio/chemical/sensirion,scd30.yaml | 2 +- MAINTAINERS | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.mailmap b/.mailmap index 2b09883a19d6..5261076e32b4 100644 --- a/.mailmap +++ b/.mailmap @@ -589,6 +589,7 @@ Maxim Mikityanskiy Maxime Ripard Maxime Ripard Maxime Ripard +Maxwell Doose Maya Erez Mayuresh Janorkar Md Sadre Alam diff --git a/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml b/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml index 25291bda1a8b..ea0e72f2a690 100644 --- a/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml +++ b/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Sensirion SCD30 carbon dioxide sensor maintainers: - - Maxwell Doose + - Maxwell Doose - Tomasz Duszynski description: | diff --git a/MAINTAINERS b/MAINTAINERS index bd5cfcb7d887..e84cd5d08e86 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24564,7 +24564,7 @@ F: Documentation/devicetree/bindings/iio/chemical/senseair,sunrise.yaml F: drivers/iio/chemical/sunrise_co2.c SENSIRION SCD30 CARBON DIOXIDE SENSOR DRIVER -M: Maxwell Doose +M: Maxwell Doose M: Tomasz Duszynski S: Maintained F: Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml From 3a2f15765b1d2df7152d63c6498883e70f1844d6 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Mon, 3 Aug 2026 19:32:25 +0200 Subject: [PATCH 474/513] dt-bindings: iio: dac: add support for mcp47a1 The Microchip MCP47A1 is a 6-bit volatile Digital-to-Analog converter which communicates via I2C. Reviewed-by: Conor Dooley Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- .../bindings/iio/dac/microchip,mcp47a1.yaml | 50 +++++++++++++++++++ MAINTAINERS | 5 ++ 2 files changed, 55 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/dac/microchip,mcp47a1.yaml diff --git a/Documentation/devicetree/bindings/iio/dac/microchip,mcp47a1.yaml b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47a1.yaml new file mode 100644 index 000000000000..b181fe73dce8 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47a1.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- + +$id: http://devicetree.org/schemas/iio/dac/microchip,mcp47a1.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip MCP47A1 DAC + +maintainers: + - Joshua Crofts + +description: | + The Microchip MCP47A1 is a 6-bit single output volatile DAC. + This device can have different IDs (0x2e and 0x3e). + https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/25154A.pdf + +properties: + compatible: + const: microchip,mcp47a1 + + reg: + items: + - enum: [0x2e, 0x3e] + + vref-supply: true + + vdd-supply: true + +required: + - compatible + - reg + - vref-supply + - vdd-supply + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + dac@2e { + compatible = "microchip,mcp47a1"; + reg = <0x2e>; + vref-supply = <&vref_regulator>; + vdd-supply = <&vdd_regulator>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index e84cd5d08e86..e016180d372a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17707,6 +17707,11 @@ S: Maintained F: Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml F: drivers/iio/adc/mcp3911.c +MICROCHIP MCP47A1 DAC DRIVER +M: Joshua Crofts +S: Maintained +F: Documentation/devicetree/bindings/iio/dac/microchip,mcp47a1.yaml + MICROCHIP MCP9982 TEMPERATURE DRIVER M: Victor Duicu L: linux-hwmon@vger.kernel.org From 350d1fb9204b13c5f95e511e98b8bcb47574d425 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Mon, 3 Aug 2026 19:32:26 +0200 Subject: [PATCH 475/513] iio: dac: mcp47a1: add support for new device The Microchip MCP47A1 is a 6-bit volatile Digital-to-Analog converter which communicates via I2C. Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + drivers/iio/dac/Kconfig | 10 +++ drivers/iio/dac/Makefile | 1 + drivers/iio/dac/mcp47a1.c | 166 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 drivers/iio/dac/mcp47a1.c diff --git a/MAINTAINERS b/MAINTAINERS index e016180d372a..04fa5322d9f7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17711,6 +17711,7 @@ MICROCHIP MCP47A1 DAC DRIVER M: Joshua Crofts S: Maintained F: Documentation/devicetree/bindings/iio/dac/microchip,mcp47a1.yaml +F: drivers/iio/dac/mcp47a1.c MICROCHIP MCP9982 TEMPERATURE DRIVER M: Victor Duicu diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 18a27785e28b..04c90d98f86f 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -582,6 +582,16 @@ config MCP4728 To compile this driver as a module, choose M here: the module will be called mcp4728. +config MCP47A1 + tristate "MCP47A1 DAC driver" + depends on I2C + help + Say Y here if you want to build a driver for the Microchip + MCP47A1 digital-to-analog converter with an I2C interface. + + To compile this driver as a module, choose M here: the module + will be called mcp47a1. + config MCP47FEB02 tristate "MCP47F(E/V)B01/02/04/08/11/12/14/18/21/22/24/28 DAC driver" depends on I2C diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 5d20d37e44ce..992f8930f95c 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_MAX5821) += max5821.o obj-$(CONFIG_MCF54415_DAC) += mcf54415_dac.o obj-$(CONFIG_MCP4725) += mcp4725.o obj-$(CONFIG_MCP4728) += mcp4728.o +obj-$(CONFIG_MCP47A1) += mcp47a1.o obj-$(CONFIG_MCP47FEB02) += mcp47feb02.o obj-$(CONFIG_MCP4821) += mcp4821.o obj-$(CONFIG_MCP4922) += mcp4922.o diff --git a/drivers/iio/dac/mcp47a1.c b/drivers/iio/dac/mcp47a1.c new file mode 100644 index 000000000000..0bf994aa0e4f --- /dev/null +++ b/drivers/iio/dac/mcp47a1.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Microchip MCP47A1 DAC driver + * + * Copyright (c) 2026 Joshua Crofts + * + * Datasheet: https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/25154A.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MCP47A1_CMD_CODE 0x00 +#define MCP47A1_MAX_STEPS 64 + +struct mcp47a1_data { + struct i2c_client *client; + int vref_mV; +}; + +static const int mcp47a1_raw_avail[] = { 0, 1, MCP47A1_MAX_STEPS - 1 }; + +static const struct iio_chan_spec mcp47a1_channel = { + .type = IIO_VOLTAGE, + .indexed = 1, + .output = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +}; + +static int mcp47a1_write(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct mcp47a1_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!in_range(val, 0, MCP47A1_MAX_STEPS)) + return -EINVAL; + + return i2c_smbus_write_byte_data(data->client, MCP47A1_CMD_CODE, + val); + default: + return -EINVAL; + } +} + +static int mcp47a1_read(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mcp47a1_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = i2c_smbus_read_byte_data(data->client, MCP47A1_CMD_CODE); + if (ret < 0) + return ret; + + *val = ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = data->vref_mV; + *val2 = MCP47A1_MAX_STEPS; + + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +static int mcp47a1_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_RAW: + *vals = mcp47a1_raw_avail; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +static const struct iio_info mcp47a1_info = { + .write_raw = mcp47a1_write, + .read_raw = mcp47a1_read, + .read_avail = mcp47a1_read_avail, +}; + +static int mcp47a1_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct mcp47a1_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->client = client; + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulator\n"); + + /* Delay after device exits reset state (see AC/DC characteristics) */ + fsleep(20); + + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read vref\n"); + + data->vref_mV = ret / (MICRO / MILLI); + + indio_dev->name = "mcp47a1"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mcp47a1_info; + indio_dev->channels = &mcp47a1_channel; + indio_dev->num_channels = 1; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id mcp47a1_of_match[] = { + { .compatible = "microchip,mcp47a1" }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp47a1_of_match); + +static const struct i2c_device_id mcp47a1_id[] = { + { .name = "mcp47a1" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcp47a1_id); + +static struct i2c_driver mcp47a1_driver = { + .driver = { + .name = "mcp47a1", + .of_match_table = mcp47a1_of_match, + }, + .probe = mcp47a1_probe, + .id_table = mcp47a1_id, +}; +module_i2c_driver(mcp47a1_driver); + +MODULE_AUTHOR("Joshua Crofts "); +MODULE_DESCRIPTION("Microchip MCP47A1 DAC Driver"); +MODULE_LICENSE("GPL"); From c20800490fd7de317aad2773040961202f5555d2 Mon Sep 17 00:00:00 2001 From: Mingyu Wang <25181214217@stu.xidian.edu.cn> Date: Sat, 1 Aug 2026 15:07:56 +0800 Subject: [PATCH 476/513] misc: ibmasm: Remove obsolete IBM Remote Supervisor Adapter driver The IBM Remote Supervisor Adapter (RSA) and RSA II were out-of-band management PCI/ISA cards introduced in the early 2000s for ancient IBM eServer xSeries machines. IBM deprecated the RSA family and replaced it with the Integrated Management Module (IMM) around 2008. IBM subsequently sold its x86 server business to Lenovo in 2014, and modern systems use entirely different BMC architectures. This hardware has been completely obsolete and out of production for over 15 years. Surviving physical servers using this specific hardware would be running ancient 32-bit processors vastly unsuited for modern kernels. A review of the recent git history shows that aside from mechanical treewide VFS API updates, the only recent activities are out-of-bounds fixes in command_file_write and MFA handling. These fixes address userspace-triggered security vulnerabilities and fuzzer-discovered MMIO bugs rather than functional issues reported by active hardware users. Keeping this obsolete driver in the tree leaves an unnecessary attack surface and acts as a "fuzzing honeypot", forcing core maintainers to endlessly review CVEs and OOB fixes for dead hardware, while also paying a maintenance tax to drag it through modern API refactorings. Remove the driver entirely. Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> Acked-by: Arnd Bergmann Link: https://patch.msgid.link/20260801070756.161698-1-25181214217@stu.xidian.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 22 -- drivers/misc/Makefile | 1 - drivers/misc/ibmasm/Makefile | 16 - drivers/misc/ibmasm/command.c | 173 --------- drivers/misc/ibmasm/dot_command.c | 138 ------- drivers/misc/ibmasm/dot_command.h | 64 ---- drivers/misc/ibmasm/event.c | 163 -------- drivers/misc/ibmasm/heartbeat.c | 88 ----- drivers/misc/ibmasm/i2o.h | 63 ---- drivers/misc/ibmasm/ibmasm.h | 209 ----------- drivers/misc/ibmasm/ibmasmfs.c | 604 ------------------------------ drivers/misc/ibmasm/lowlevel.c | 75 ---- drivers/misc/ibmasm/lowlevel.h | 123 ------ drivers/misc/ibmasm/module.c | 224 ----------- drivers/misc/ibmasm/r_heartbeat.c | 86 ----- drivers/misc/ibmasm/remote.c | 273 -------------- drivers/misc/ibmasm/remote.h | 257 ------------- drivers/misc/ibmasm/uart.c | 58 --- 18 files changed, 2637 deletions(-) delete mode 100644 drivers/misc/ibmasm/Makefile delete mode 100644 drivers/misc/ibmasm/command.c delete mode 100644 drivers/misc/ibmasm/dot_command.c delete mode 100644 drivers/misc/ibmasm/dot_command.h delete mode 100644 drivers/misc/ibmasm/event.c delete mode 100644 drivers/misc/ibmasm/heartbeat.c delete mode 100644 drivers/misc/ibmasm/i2o.h delete mode 100644 drivers/misc/ibmasm/ibmasm.h delete mode 100644 drivers/misc/ibmasm/ibmasmfs.c delete mode 100644 drivers/misc/ibmasm/lowlevel.c delete mode 100644 drivers/misc/ibmasm/lowlevel.h delete mode 100644 drivers/misc/ibmasm/module.c delete mode 100644 drivers/misc/ibmasm/r_heartbeat.c delete mode 100644 drivers/misc/ibmasm/remote.c delete mode 100644 drivers/misc/ibmasm/remote.h delete mode 100644 drivers/misc/ibmasm/uart.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 7f4900efe726..10803a6f1227 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -57,28 +57,6 @@ config DUMMY_IRQ The sole purpose of this module is to help with debugging of systems on which spurious IRQs would happen on disabled IRQ vector. -config IBM_ASM - tristate "Device driver for IBM RSA service processor" - depends on X86 && PCI && INPUT - depends on SERIAL_8250 || SERIAL_8250=n - help - This option enables device driver support for in-band access to the - IBM RSA (Condor) service processor in eServer xSeries systems. - The ibmasm device driver allows user space application to access - ASM (Advanced Systems Management) functions on the service - processor. The driver is meant to be used in conjunction with - a user space API. - The ibmasm driver also enables the OS to use the UART on the - service processor board as a regular serial port. To make use of - this feature serial driver support (CONFIG_SERIAL_8250) must be - enabled. - - WARNING: This software may not be supported or function - correctly on your IBM server. Please consult the IBM ServerProven - website - for information on the specific driver level and support statement - for your IBM server. - config IBMVMC tristate "IBM Virtual Management Channel support" depends on PPC_PSERIES diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 812b68d21185..f317fd6c9f55 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -3,7 +3,6 @@ # Makefile for misc devices that really don't fit anywhere else. # -obj-$(CONFIG_IBM_ASM) += ibmasm/ obj-$(CONFIG_IBMVMC) += ibmvmc.o obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o diff --git a/drivers/misc/ibmasm/Makefile b/drivers/misc/ibmasm/Makefile deleted file mode 100644 index 1b9dd0f44411..000000000000 --- a/drivers/misc/ibmasm/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -obj-$(CONFIG_IBM_ASM) := ibmasm.o - -ibmasm-y := module.o \ - ibmasmfs.o \ - event.o \ - command.o \ - remote.o \ - heartbeat.o \ - r_heartbeat.o \ - dot_command.o \ - lowlevel.o - -ibmasm-$(CONFIG_SERIAL_8250) += uart.o - diff --git a/drivers/misc/ibmasm/command.c b/drivers/misc/ibmasm/command.c deleted file mode 100644 index 6b037675ff45..000000000000 --- a/drivers/misc/ibmasm/command.c +++ /dev/null @@ -1,173 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#include -#include -#include "ibmasm.h" -#include "lowlevel.h" - -static void exec_next_command(struct service_processor *sp); - -static atomic_t command_count = ATOMIC_INIT(0); - -struct command *ibmasm_new_command(struct service_processor *sp, size_t buffer_size) -{ - struct command *cmd; - - if (buffer_size > IBMASM_CMD_MAX_BUFFER_SIZE) - return NULL; - - cmd = kzalloc_obj(struct command); - if (cmd == NULL) - return NULL; - - - cmd->buffer = kzalloc(buffer_size, GFP_KERNEL); - if (cmd->buffer == NULL) { - kfree(cmd); - return NULL; - } - cmd->buffer_size = buffer_size; - - kref_init(&cmd->kref); - cmd->lock = &sp->lock; - - cmd->status = IBMASM_CMD_PENDING; - init_waitqueue_head(&cmd->wait); - INIT_LIST_HEAD(&cmd->queue_node); - - atomic_inc(&command_count); - dbg("command count: %d\n", atomic_read(&command_count)); - - return cmd; -} - -void ibmasm_free_command(struct kref *kref) -{ - struct command *cmd = to_command(kref); - - list_del(&cmd->queue_node); - atomic_dec(&command_count); - dbg("command count: %d\n", atomic_read(&command_count)); - kfree(cmd->buffer); - kfree(cmd); -} - -static void enqueue_command(struct service_processor *sp, struct command *cmd) -{ - list_add_tail(&cmd->queue_node, &sp->command_queue); -} - -static struct command *dequeue_command(struct service_processor *sp) -{ - struct command *cmd; - struct list_head *next; - - if (list_empty(&sp->command_queue)) - return NULL; - - next = sp->command_queue.next; - list_del_init(next); - cmd = list_entry(next, struct command, queue_node); - - return cmd; -} - -static inline void do_exec_command(struct service_processor *sp) -{ - char tsbuf[32]; - - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - - if (ibmasm_send_i2o_message(sp)) { - sp->current_command->status = IBMASM_CMD_FAILED; - wake_up(&sp->current_command->wait); - command_put(sp->current_command); - exec_next_command(sp); - } -} - -/* - * exec_command - * send a command to a service processor - * Commands are executed sequentially. One command (sp->current_command) - * is sent to the service processor. Once the interrupt handler gets a - * message of type command_response, the message is copied into - * the current commands buffer, - */ -void ibmasm_exec_command(struct service_processor *sp, struct command *cmd) -{ - unsigned long flags; - char tsbuf[32]; - - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - - spin_lock_irqsave(&sp->lock, flags); - - if (!sp->current_command) { - sp->current_command = cmd; - command_get(sp->current_command); - spin_unlock_irqrestore(&sp->lock, flags); - do_exec_command(sp); - } else { - enqueue_command(sp, cmd); - spin_unlock_irqrestore(&sp->lock, flags); - } -} - -static void exec_next_command(struct service_processor *sp) -{ - unsigned long flags; - char tsbuf[32]; - - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - - spin_lock_irqsave(&sp->lock, flags); - sp->current_command = dequeue_command(sp); - if (sp->current_command) { - command_get(sp->current_command); - spin_unlock_irqrestore(&sp->lock, flags); - do_exec_command(sp); - } else { - spin_unlock_irqrestore(&sp->lock, flags); - } -} - -/* - * Sleep until a command has failed or a response has been received - * and the command status been updated by the interrupt handler. - * (see receive_response). - */ -void ibmasm_wait_for_response(struct command *cmd, int timeout) -{ - wait_event_interruptible_timeout(cmd->wait, - cmd->status == IBMASM_CMD_COMPLETE || - cmd->status == IBMASM_CMD_FAILED, - timeout * HZ); -} - -/* - * receive_command_response - * called by the interrupt handler when a dot command of type command_response - * was received. - */ -void ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size) -{ - struct command *cmd = sp->current_command; - - if (!sp->current_command) - return; - - memcpy_fromio(cmd->buffer, response, min(size, cmd->buffer_size)); - cmd->status = IBMASM_CMD_COMPLETE; - wake_up(&sp->current_command->wait); - command_put(sp->current_command); - exec_next_command(sp); -} diff --git a/drivers/misc/ibmasm/dot_command.c b/drivers/misc/ibmasm/dot_command.c deleted file mode 100644 index df389bd4c9df..000000000000 --- a/drivers/misc/ibmasm/dot_command.c +++ /dev/null @@ -1,138 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#include "ibmasm.h" -#include "dot_command.h" - -/* - * Dispatch an incoming message to the specific handler for the message. - * Called from interrupt context. - */ -void ibmasm_receive_message(struct service_processor *sp, void *message, int message_size) -{ - u32 size; - struct dot_command_header *header = (struct dot_command_header *)message; - - if (message_size == 0) - return; - - size = get_dot_command_size(message); - if (size == 0) - return; - - if (size > message_size) - size = message_size; - - switch (header->type) { - case sp_event: - ibmasm_receive_event(sp, message, size); - break; - case sp_command_response: - ibmasm_receive_command_response(sp, message, size); - break; - case sp_heartbeat: - ibmasm_receive_heartbeat(sp, message, size); - break; - default: - dev_err(sp->dev, "Received unknown message from service processor\n"); - } -} - - -#define INIT_BUFFER_SIZE 32 - - -/* - * send the 4.3.5.10 dot command (driver VPD) to the service processor - */ -int ibmasm_send_driver_vpd(struct service_processor *sp) -{ - struct command *command; - struct dot_command_header *header; - u8 *vpd_command; - u8 *vpd_data; - int result = 0; - - command = ibmasm_new_command(sp, INIT_BUFFER_SIZE); - if (command == NULL) - return -ENOMEM; - - header = (struct dot_command_header *)command->buffer; - header->type = sp_write; - header->command_size = 4; - header->data_size = 16; - header->status = 0; - header->reserved = 0; - - vpd_command = command->buffer + sizeof(struct dot_command_header); - vpd_command[0] = 0x4; - vpd_command[1] = 0x3; - vpd_command[2] = 0x5; - vpd_command[3] = 0xa; - - vpd_data = vpd_command + header->command_size; - vpd_data[0] = 0; - strcat(vpd_data, IBMASM_DRIVER_VPD); - vpd_data[10] = 0; - vpd_data[15] = 0; - - ibmasm_exec_command(sp, command); - ibmasm_wait_for_response(command, IBMASM_CMD_TIMEOUT_NORMAL); - - if (command->status != IBMASM_CMD_COMPLETE) - result = -ENODEV; - - command_put(command); - - return result; -} - -struct os_state_command { - struct dot_command_header header; - unsigned char command[3]; - unsigned char data; -}; - -/* - * send the 4.3.6 dot command (os state) to the service processor - * During driver init this function is called with os state "up". - * This causes the service processor to start sending heartbeats the - * driver. - * During driver exit the function is called with os state "down", - * causing the service processor to stop the heartbeats. - */ -int ibmasm_send_os_state(struct service_processor *sp, int os_state) -{ - struct command *cmd; - struct os_state_command *os_state_cmd; - int result = 0; - - cmd = ibmasm_new_command(sp, sizeof(struct os_state_command)); - if (cmd == NULL) - return -ENOMEM; - - os_state_cmd = (struct os_state_command *)cmd->buffer; - os_state_cmd->header.type = sp_write; - os_state_cmd->header.command_size = 3; - os_state_cmd->header.data_size = 1; - os_state_cmd->header.status = 0; - os_state_cmd->command[0] = 4; - os_state_cmd->command[1] = 3; - os_state_cmd->command[2] = 6; - os_state_cmd->data = os_state; - - ibmasm_exec_command(sp, cmd); - ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL); - - if (cmd->status != IBMASM_CMD_COMPLETE) - result = -ENODEV; - - command_put(cmd); - return result; -} diff --git a/drivers/misc/ibmasm/dot_command.h b/drivers/misc/ibmasm/dot_command.h deleted file mode 100644 index e03399ebe239..000000000000 --- a/drivers/misc/ibmasm/dot_command.h +++ /dev/null @@ -1,64 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#ifndef __DOT_COMMAND_H__ -#define __DOT_COMMAND_H__ - -/* - * dot commands are the protocol used to communicate with the service - * processor. - * They consist of header, a command of variable length and data of - * variable length. - */ - -/* dot command types */ -#define sp_write 0 -#define sp_write_next 1 -#define sp_read 2 -#define sp_read_next 3 -#define sp_command_response 4 -#define sp_event 5 -#define sp_heartbeat 6 - -#pragma pack(1) -struct dot_command_header { - u8 type; - u8 command_size; - u16 data_size; - u8 status; - u8 reserved; -}; -#pragma pack() - -static inline size_t get_dot_command_size(void *buffer) -{ - struct dot_command_header *cmd = (struct dot_command_header *)buffer; - return sizeof(struct dot_command_header) + cmd->command_size + cmd->data_size; -} - -static inline unsigned int get_dot_command_timeout(void *buffer) -{ - struct dot_command_header *header = (struct dot_command_header *)buffer; - unsigned char *cmd = buffer + sizeof(struct dot_command_header); - - /* dot commands 6.3.1, 7.1 and 8.x need a longer timeout */ - - if (header->command_size == 3) { - if ((cmd[0] == 6) && (cmd[1] == 3) && (cmd[2] == 1)) - return IBMASM_CMD_TIMEOUT_EXTRA; - } else if (header->command_size == 2) { - if ((cmd[0] == 7) && (cmd[1] == 1)) - return IBMASM_CMD_TIMEOUT_EXTRA; - if (cmd[0] == 8) - return IBMASM_CMD_TIMEOUT_EXTRA; - } - return IBMASM_CMD_TIMEOUT_NORMAL; -} - -#endif /* __DOT_COMMAND_H__ */ diff --git a/drivers/misc/ibmasm/event.c b/drivers/misc/ibmasm/event.c deleted file mode 100644 index 3990bf2b1728..000000000000 --- a/drivers/misc/ibmasm/event.c +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#include -#include -#include "ibmasm.h" -#include "lowlevel.h" - -/* - * ASM service processor event handling routines. - * - * Events are signalled to the device drivers through interrupts. - * They have the format of dot commands, with the type field set to - * sp_event. - * The driver does not interpret the events, it simply stores them in a - * circular buffer. - */ - -static void wake_up_event_readers(struct service_processor *sp) -{ - struct event_reader *reader; - - list_for_each_entry(reader, &sp->event_buffer->readers, node) - wake_up_interruptible(&reader->wait); -} - -/* - * receive_event - * Called by the interrupt handler when a dot command of type sp_event is - * received. - * Store the event in the circular event buffer, wake up any sleeping - * event readers. - * There is no reader marker in the buffer, therefore readers are - * responsible for keeping up with the writer, or they will lose events. - */ -void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size) -{ - struct event_buffer *buffer = sp->event_buffer; - struct ibmasm_event *event; - unsigned long flags; - - data_size = min(data_size, IBMASM_EVENT_MAX_SIZE); - - spin_lock_irqsave(&sp->lock, flags); - /* copy the event into the next slot in the circular buffer */ - event = &buffer->events[buffer->next_index]; - memcpy_fromio(event->data, data, data_size); - event->data_size = data_size; - event->serial_number = buffer->next_serial_number; - - /* advance indices in the buffer */ - buffer->next_index = (buffer->next_index + 1) % IBMASM_NUM_EVENTS; - buffer->next_serial_number++; - spin_unlock_irqrestore(&sp->lock, flags); - - wake_up_event_readers(sp); -} - -static inline int event_available(struct event_buffer *b, struct event_reader *r) -{ - return (r->next_serial_number < b->next_serial_number); -} - -/* - * get_next_event - * Called by event readers (initiated from user space through the file - * system). - * Sleeps until a new event is available. - */ -int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader) -{ - struct event_buffer *buffer = sp->event_buffer; - struct ibmasm_event *event; - unsigned int index; - unsigned long flags; - - reader->cancelled = 0; - - if (wait_event_interruptible(reader->wait, - event_available(buffer, reader) || reader->cancelled)) - return -ERESTARTSYS; - - if (!event_available(buffer, reader)) - return 0; - - spin_lock_irqsave(&sp->lock, flags); - - index = buffer->next_index; - event = &buffer->events[index]; - while (event->serial_number < reader->next_serial_number) { - index = (index + 1) % IBMASM_NUM_EVENTS; - event = &buffer->events[index]; - } - memcpy(reader->data, event->data, event->data_size); - reader->data_size = event->data_size; - reader->next_serial_number = event->serial_number + 1; - - spin_unlock_irqrestore(&sp->lock, flags); - - return event->data_size; -} - -void ibmasm_cancel_next_event(struct event_reader *reader) -{ - reader->cancelled = 1; - wake_up_interruptible(&reader->wait); -} - -void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader) -{ - unsigned long flags; - - reader->next_serial_number = sp->event_buffer->next_serial_number; - init_waitqueue_head(&reader->wait); - spin_lock_irqsave(&sp->lock, flags); - list_add(&reader->node, &sp->event_buffer->readers); - spin_unlock_irqrestore(&sp->lock, flags); -} - -void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader) -{ - unsigned long flags; - - spin_lock_irqsave(&sp->lock, flags); - list_del(&reader->node); - spin_unlock_irqrestore(&sp->lock, flags); -} - -int ibmasm_event_buffer_init(struct service_processor *sp) -{ - struct event_buffer *buffer; - struct ibmasm_event *event; - int i; - - buffer = kmalloc_obj(struct event_buffer); - if (!buffer) - return -ENOMEM; - - buffer->next_index = 0; - buffer->next_serial_number = 1; - - event = buffer->events; - for (i=0; iserial_number = 0; - - INIT_LIST_HEAD(&buffer->readers); - - sp->event_buffer = buffer; - - return 0; -} - -void ibmasm_event_buffer_exit(struct service_processor *sp) -{ - kfree(sp->event_buffer); -} diff --git a/drivers/misc/ibmasm/heartbeat.c b/drivers/misc/ibmasm/heartbeat.c deleted file mode 100644 index 59c9a0d95659..000000000000 --- a/drivers/misc/ibmasm/heartbeat.c +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#include -#include -#include "ibmasm.h" -#include "dot_command.h" -#include "lowlevel.h" - -static int suspend_heartbeats = 0; - -/* - * Once the driver indicates to the service processor that it is running - * - see send_os_state() - the service processor sends periodic heartbeats - * to the driver. The driver must respond to the heartbeats or else the OS - * will be rebooted. - * In the case of a panic the interrupt handler continues to work and thus - * continues to respond to heartbeats, making the service processor believe - * the OS is still running and thus preventing a reboot. - * To prevent this from happening a callback is added the panic_notifier_list. - * Before responding to a heartbeat the driver checks if a panic has happened, - * if yes it suspends heartbeat, causing the service processor to reboot as - * expected. - */ -static int panic_happened(struct notifier_block *n, unsigned long val, void *v) -{ - suspend_heartbeats = 1; - return 0; -} - -static struct notifier_block panic_notifier = { panic_happened, NULL, 1 }; - -void ibmasm_register_panic_notifier(void) -{ - atomic_notifier_chain_register(&panic_notifier_list, &panic_notifier); -} - -void ibmasm_unregister_panic_notifier(void) -{ - atomic_notifier_chain_unregister(&panic_notifier_list, - &panic_notifier); -} - - -int ibmasm_heartbeat_init(struct service_processor *sp) -{ - sp->heartbeat = ibmasm_new_command(sp, HEARTBEAT_BUFFER_SIZE); - if (sp->heartbeat == NULL) - return -ENOMEM; - - return 0; -} - -void ibmasm_heartbeat_exit(struct service_processor *sp) -{ - char tsbuf[32]; - - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - ibmasm_wait_for_response(sp->heartbeat, IBMASM_CMD_TIMEOUT_NORMAL); - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - suspend_heartbeats = 1; - command_put(sp->heartbeat); -} - -void ibmasm_receive_heartbeat(struct service_processor *sp, void *message, size_t size) -{ - struct command *cmd = sp->heartbeat; - struct dot_command_header *header = (struct dot_command_header *)cmd->buffer; - char tsbuf[32]; - - dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); - if (suspend_heartbeats) - return; - - /* return the received dot command to sender */ - cmd->status = IBMASM_CMD_PENDING; - size = min(size, cmd->buffer_size); - memcpy_fromio(cmd->buffer, message, size); - header->type = sp_write; - ibmasm_exec_command(sp, cmd); -} diff --git a/drivers/misc/ibmasm/i2o.h b/drivers/misc/ibmasm/i2o.h deleted file mode 100644 index 468fa84a3257..000000000000 --- a/drivers/misc/ibmasm/i2o.h +++ /dev/null @@ -1,63 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#pragma pack(1) -struct i2o_header { - u8 version; - u8 message_flags; - u16 message_size; - u8 target; - u8 initiator_and_target; - u8 initiator; - u8 function; - u32 initiator_context; -}; -#pragma pack() - -#define I2O_HEADER_TEMPLATE \ - { .version = 0x01, \ - .message_flags = 0x00, \ - .function = 0xFF, \ - .initiator = 0x00, \ - .initiator_and_target = 0x40, \ - .target = 0x00, \ - .initiator_context = 0x0 } - -#define I2O_MESSAGE_SIZE 0x1000 -#define I2O_COMMAND_SIZE (I2O_MESSAGE_SIZE - sizeof(struct i2o_header)) - -#pragma pack(1) -struct i2o_message { - struct i2o_header header; - void *data; -}; -#pragma pack() - -static inline unsigned short outgoing_message_size(unsigned int data_size) -{ - unsigned int size; - unsigned short i2o_size; - - if (data_size > I2O_COMMAND_SIZE) - data_size = I2O_COMMAND_SIZE; - - size = sizeof(struct i2o_header) + data_size; - - i2o_size = size / sizeof(u32); - - if (size % sizeof(u32)) - i2o_size++; - - return i2o_size; -} - -static inline u32 incoming_data_size(struct i2o_message *i2o_message) -{ - return (sizeof(u32) * i2o_message->header.message_size); -} diff --git a/drivers/misc/ibmasm/ibmasm.h b/drivers/misc/ibmasm/ibmasm.h deleted file mode 100644 index a5ced88ca923..000000000000 --- a/drivers/misc/ibmasm/ibmasm.h +++ /dev/null @@ -1,209 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Driver identification */ -#define DRIVER_NAME "ibmasm" -#define DRIVER_VERSION "1.0" -#define DRIVER_AUTHOR "Max Asbock , Vernon Mauery " -#define DRIVER_DESC "IBM ASM Service Processor Driver" - -#define err(msg) printk(KERN_ERR "%s: " msg "\n", DRIVER_NAME) -#define info(msg) printk(KERN_INFO "%s: " msg "\n", DRIVER_NAME) - -extern int ibmasm_debug; -#define dbg(STR, ARGS...) \ - do { \ - if (ibmasm_debug) \ - printk(KERN_DEBUG STR , ##ARGS); \ - } while (0) - -static inline char *get_timestamp(char *buf) -{ - struct timespec64 now; - - ktime_get_real_ts64(&now); - sprintf(buf, "%llu.%.08lu", (long long)now.tv_sec, - now.tv_nsec / NSEC_PER_USEC); - return buf; -} - -#define IBMASM_CMD_PENDING 0 -#define IBMASM_CMD_COMPLETE 1 -#define IBMASM_CMD_FAILED 2 - -#define IBMASM_CMD_TIMEOUT_NORMAL 45 -#define IBMASM_CMD_TIMEOUT_EXTRA 240 - -#define IBMASM_CMD_MAX_BUFFER_SIZE 0x8000 - -#define REVERSE_HEARTBEAT_TIMEOUT 120 - -#define HEARTBEAT_BUFFER_SIZE 0x400 - -#ifdef IA64 -#define IBMASM_DRIVER_VPD "Lin64 6.08 " -#else -#define IBMASM_DRIVER_VPD "Lin32 6.08 " -#endif - -#define SYSTEM_STATE_OS_UP 5 -#define SYSTEM_STATE_OS_DOWN 4 - -#define IBMASM_NAME_SIZE 16 - -#define IBMASM_NUM_EVENTS 10 -#define IBMASM_EVENT_MAX_SIZE 2048u - - -struct command { - struct list_head queue_node; - wait_queue_head_t wait; - unsigned char *buffer; - size_t buffer_size; - int status; - struct kref kref; - spinlock_t *lock; -}; -#define to_command(c) container_of(c, struct command, kref) - -void ibmasm_free_command(struct kref *kref); -static inline void command_put(struct command *cmd) -{ - unsigned long flags; - spinlock_t *lock = cmd->lock; - - spin_lock_irqsave(lock, flags); - kref_put(&cmd->kref, ibmasm_free_command); - spin_unlock_irqrestore(lock, flags); -} - -static inline void command_get(struct command *cmd) -{ - kref_get(&cmd->kref); -} - - -struct ibmasm_event { - unsigned int serial_number; - unsigned int data_size; - unsigned char data[IBMASM_EVENT_MAX_SIZE]; -}; - -struct event_buffer { - struct ibmasm_event events[IBMASM_NUM_EVENTS]; - unsigned int next_serial_number; - unsigned int next_index; - struct list_head readers; -}; - -struct event_reader { - int cancelled; - unsigned int next_serial_number; - wait_queue_head_t wait; - struct list_head node; - unsigned int data_size; - unsigned char data[IBMASM_EVENT_MAX_SIZE]; -}; - -struct reverse_heartbeat { - wait_queue_head_t wait; - unsigned int stopped; -}; - -struct ibmasm_remote { - struct input_dev *keybd_dev; - struct input_dev *mouse_dev; -}; - -struct service_processor { - struct list_head node; - spinlock_t lock; - void __iomem *base_address; - unsigned int irq; - struct command *current_command; - struct command *heartbeat; - struct list_head command_queue; - struct event_buffer *event_buffer; - char dirname[IBMASM_NAME_SIZE]; - char devname[IBMASM_NAME_SIZE]; - unsigned int number; - struct ibmasm_remote remote; - int serial_line; - struct device *dev; -}; - -/* command processing */ -struct command *ibmasm_new_command(struct service_processor *sp, size_t buffer_size); -void ibmasm_exec_command(struct service_processor *sp, struct command *cmd); -void ibmasm_wait_for_response(struct command *cmd, int timeout); -void ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size); - -/* event processing */ -int ibmasm_event_buffer_init(struct service_processor *sp); -void ibmasm_event_buffer_exit(struct service_processor *sp); -void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size); -void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader); -void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader); -int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader); -void ibmasm_cancel_next_event(struct event_reader *reader); - -/* heartbeat - from SP to OS */ -void ibmasm_register_panic_notifier(void); -void ibmasm_unregister_panic_notifier(void); -int ibmasm_heartbeat_init(struct service_processor *sp); -void ibmasm_heartbeat_exit(struct service_processor *sp); -void ibmasm_receive_heartbeat(struct service_processor *sp, void *message, size_t size); - -/* reverse heartbeat - from OS to SP */ -void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb); -int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb); -void ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb); - -/* dot commands */ -void ibmasm_receive_message(struct service_processor *sp, void *data, int data_size); -int ibmasm_send_driver_vpd(struct service_processor *sp); -int ibmasm_send_os_state(struct service_processor *sp, int os_state); - -/* low level message processing */ -int ibmasm_send_i2o_message(struct service_processor *sp); -irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id); - -/* remote console */ -void ibmasm_handle_mouse_interrupt(struct service_processor *sp); -int ibmasm_init_remote_input_dev(struct service_processor *sp); -void ibmasm_free_remote_input_dev(struct service_processor *sp); - -/* file system */ -int ibmasmfs_register(void); -void ibmasmfs_unregister(void); -void ibmasmfs_add_sp(struct service_processor *sp); - -/* uart */ -#if IS_ENABLED(CONFIG_SERIAL_8250) -void ibmasm_register_uart(struct service_processor *sp); -void ibmasm_unregister_uart(struct service_processor *sp); -#else -#define ibmasm_register_uart(sp) do { } while(0) -#define ibmasm_unregister_uart(sp) do { } while(0) -#endif diff --git a/drivers/misc/ibmasm/ibmasmfs.c b/drivers/misc/ibmasm/ibmasmfs.c deleted file mode 100644 index dfdfa9ba4747..000000000000 --- a/drivers/misc/ibmasm/ibmasmfs.c +++ /dev/null @@ -1,604 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -/* - * Parts of this code are based on an article by Jonathan Corbet - * that appeared in Linux Weekly News. - */ - - -/* - * The IBMASM file virtual filesystem. It creates the following hierarchy - * dynamically when mounted from user space: - * - * /ibmasm - * |-- 0 - * | |-- command - * | |-- event - * | |-- reverse_heartbeat - * | `-- remote_video - * | |-- depth - * | |-- height - * | `-- width - * . - * . - * . - * `-- n - * |-- command - * |-- event - * |-- reverse_heartbeat - * `-- remote_video - * |-- depth - * |-- height - * `-- width - * - * For each service processor the following files are created: - * - * command: execute dot commands - * write: execute a dot command on the service processor - * read: return the result of a previously executed dot command - * - * events: listen for service processor events - * read: sleep (interruptible) until an event occurs - * write: wakeup sleeping event listener - * - * reverse_heartbeat: send a heartbeat to the service processor - * read: sleep (interruptible) until the reverse heartbeat fails - * write: wakeup sleeping heartbeat listener - * - * remote_video/width - * remote_video/height - * remote_video/width: control remote display settings - * write: set value - * read: read value - */ - -#include -#include -#include -#include -#include -#include -#include "ibmasm.h" -#include "remote.h" -#include "dot_command.h" - -#define IBMASMFS_MAGIC 0x66726f67 - -static LIST_HEAD(service_processors); - -static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode); -static void ibmasmfs_create_files (struct super_block *sb); -static int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc); - -static int ibmasmfs_get_tree(struct fs_context *fc) -{ - return get_tree_single(fc, ibmasmfs_fill_super); -} - -static const struct fs_context_operations ibmasmfs_context_ops = { - .get_tree = ibmasmfs_get_tree, -}; - -static int ibmasmfs_init_fs_context(struct fs_context *fc) -{ - fc->ops = &ibmasmfs_context_ops; - return 0; -} - -static const struct super_operations ibmasmfs_s_ops = { - .statfs = simple_statfs, - .drop_inode = inode_just_drop, -}; - -static struct file_system_type ibmasmfs_type = { - .owner = THIS_MODULE, - .name = "ibmasmfs", - .init_fs_context = ibmasmfs_init_fs_context, - .kill_sb = kill_anon_super, -}; -MODULE_ALIAS_FS("ibmasmfs"); - -static int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc) -{ - struct inode *root; - - sb->s_blocksize = PAGE_SIZE; - sb->s_blocksize_bits = PAGE_SHIFT; - sb->s_magic = IBMASMFS_MAGIC; - sb->s_op = &ibmasmfs_s_ops; - sb->s_time_gran = 1; - - root = ibmasmfs_make_inode (sb, S_IFDIR | 0500); - if (!root) - return -ENOMEM; - - root->i_op = &simple_dir_inode_operations; - root->i_fop = &simple_dir_operations; - - sb->s_root = d_make_root(root); - if (!sb->s_root) - return -ENOMEM; - - ibmasmfs_create_files(sb); - return 0; -} - -static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode) -{ - struct inode *ret = new_inode(sb); - - if (ret) { - ret->i_ino = get_next_ino(); - ret->i_mode = mode; - simple_inode_init_ts(ret); - } - return ret; -} - -static int ibmasmfs_create_file(struct dentry *parent, - const char *name, - const struct file_operations *fops, - void *data, - int mode) -{ - struct dentry *dentry; - struct inode *inode; - - dentry = d_alloc_name(parent, name); - if (!dentry) - return -ENOMEM; - - inode = ibmasmfs_make_inode(parent->d_sb, S_IFREG | mode); - if (!inode) { - dput(dentry); - return -ENOMEM; - } - - inode->i_fop = fops; - inode->i_private = data; - - d_make_persistent(dentry, inode); - dput(dentry); - return 0; -} - -static struct dentry *ibmasmfs_create_dir(struct dentry *parent, - const char *name) -{ - struct dentry *dentry; - struct inode *inode; - - dentry = d_alloc_name(parent, name); - if (!dentry) - return NULL; - - inode = ibmasmfs_make_inode(parent->d_sb, S_IFDIR | 0500); - if (!inode) { - dput(dentry); - return NULL; - } - - inode->i_op = &simple_dir_inode_operations; - inode->i_fop = &simple_dir_operations; - - d_make_persistent(dentry, inode); - dput(dentry); - return dentry; // borrowed -} - -int ibmasmfs_register(void) -{ - return register_filesystem(&ibmasmfs_type); -} - -void ibmasmfs_unregister(void) -{ - unregister_filesystem(&ibmasmfs_type); -} - -void ibmasmfs_add_sp(struct service_processor *sp) -{ - list_add(&sp->node, &service_processors); -} - -/* struct to save state between command file operations */ -struct ibmasmfs_command_data { - struct service_processor *sp; - struct command *command; -}; - -/* struct to save state between event file operations */ -struct ibmasmfs_event_data { - struct service_processor *sp; - struct event_reader reader; - int active; -}; - -/* struct to save state between reverse heartbeat file operations */ -struct ibmasmfs_heartbeat_data { - struct service_processor *sp; - struct reverse_heartbeat heartbeat; - int active; -}; - -static int command_file_open(struct inode *inode, struct file *file) -{ - struct ibmasmfs_command_data *command_data; - - if (!inode->i_private) - return -ENODEV; - - command_data = kmalloc_obj(struct ibmasmfs_command_data); - if (!command_data) - return -ENOMEM; - - command_data->command = NULL; - command_data->sp = inode->i_private; - file->private_data = command_data; - return 0; -} - -static int command_file_close(struct inode *inode, struct file *file) -{ - struct ibmasmfs_command_data *command_data = file->private_data; - - if (command_data->command) - command_put(command_data->command); - - kfree(command_data); - return 0; -} - -static ssize_t command_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) -{ - struct ibmasmfs_command_data *command_data = file->private_data; - struct command *cmd; - int len; - unsigned long flags; - - if (*offset < 0) - return -EINVAL; - if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE) - return 0; - if (*offset != 0) - return 0; - - spin_lock_irqsave(&command_data->sp->lock, flags); - cmd = command_data->command; - if (cmd == NULL) { - spin_unlock_irqrestore(&command_data->sp->lock, flags); - return 0; - } - command_data->command = NULL; - spin_unlock_irqrestore(&command_data->sp->lock, flags); - - if (cmd->status != IBMASM_CMD_COMPLETE) { - command_put(cmd); - return -EIO; - } - len = min(count, cmd->buffer_size); - if (copy_to_user(buf, cmd->buffer, len)) { - command_put(cmd); - return -EFAULT; - } - command_put(cmd); - - return len; -} - -static ssize_t command_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset) -{ - struct ibmasmfs_command_data *command_data = file->private_data; - struct command *cmd; - unsigned long flags; - - if (*offset < 0) - 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; - - /* commands are executed sequentially, only one command at a time */ - if (command_data->command) - return -EAGAIN; - - cmd = ibmasm_new_command(command_data->sp, count); - if (!cmd) - return -ENOMEM; - - if (copy_from_user(cmd->buffer, ubuff, count)) { - command_put(cmd); - 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); - command_put(cmd); - return -EAGAIN; - } - command_data->command = cmd; - spin_unlock_irqrestore(&command_data->sp->lock, flags); - - ibmasm_exec_command(command_data->sp, cmd); - ibmasm_wait_for_response(cmd, get_dot_command_timeout(cmd->buffer)); - - return count; -} - -static int event_file_open(struct inode *inode, struct file *file) -{ - struct ibmasmfs_event_data *event_data; - struct service_processor *sp; - - if (!inode->i_private) - return -ENODEV; - - sp = inode->i_private; - - event_data = kmalloc_obj(struct ibmasmfs_event_data); - if (!event_data) - return -ENOMEM; - - ibmasm_event_reader_register(sp, &event_data->reader); - - event_data->sp = sp; - event_data->active = 0; - file->private_data = event_data; - return 0; -} - -static int event_file_close(struct inode *inode, struct file *file) -{ - struct ibmasmfs_event_data *event_data = file->private_data; - - ibmasm_event_reader_unregister(event_data->sp, &event_data->reader); - kfree(event_data); - return 0; -} - -static ssize_t event_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) -{ - struct ibmasmfs_event_data *event_data = file->private_data; - struct event_reader *reader = &event_data->reader; - struct service_processor *sp = event_data->sp; - int ret; - unsigned long flags; - - if (*offset < 0) - return -EINVAL; - if (count == 0 || count > IBMASM_EVENT_MAX_SIZE) - return 0; - if (*offset != 0) - return 0; - - spin_lock_irqsave(&sp->lock, flags); - if (event_data->active) { - spin_unlock_irqrestore(&sp->lock, flags); - return -EBUSY; - } - event_data->active = 1; - spin_unlock_irqrestore(&sp->lock, flags); - - ret = ibmasm_get_next_event(sp, reader); - if (ret <= 0) - goto out; - - if (count < reader->data_size) { - ret = -EINVAL; - goto out; - } - - if (copy_to_user(buf, reader->data, reader->data_size)) { - ret = -EFAULT; - goto out; - } - ret = reader->data_size; - -out: - event_data->active = 0; - return ret; -} - -static ssize_t event_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) -{ - struct ibmasmfs_event_data *event_data = file->private_data; - - if (*offset < 0) - return -EINVAL; - if (count != 1) - return 0; - if (*offset != 0) - return 0; - - ibmasm_cancel_next_event(&event_data->reader); - return 0; -} - -static int r_heartbeat_file_open(struct inode *inode, struct file *file) -{ - struct ibmasmfs_heartbeat_data *rhbeat; - - if (!inode->i_private) - return -ENODEV; - - rhbeat = kmalloc_obj(struct ibmasmfs_heartbeat_data); - if (!rhbeat) - return -ENOMEM; - - rhbeat->sp = inode->i_private; - rhbeat->active = 0; - ibmasm_init_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat); - file->private_data = rhbeat; - return 0; -} - -static int r_heartbeat_file_close(struct inode *inode, struct file *file) -{ - struct ibmasmfs_heartbeat_data *rhbeat = file->private_data; - - kfree(rhbeat); - return 0; -} - -static ssize_t r_heartbeat_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) -{ - struct ibmasmfs_heartbeat_data *rhbeat = file->private_data; - unsigned long flags; - int result; - - if (*offset < 0) - return -EINVAL; - if (count == 0 || count > 1024) - return 0; - if (*offset != 0) - return 0; - - /* allow only one reverse heartbeat per process */ - spin_lock_irqsave(&rhbeat->sp->lock, flags); - if (rhbeat->active) { - spin_unlock_irqrestore(&rhbeat->sp->lock, flags); - return -EBUSY; - } - rhbeat->active = 1; - spin_unlock_irqrestore(&rhbeat->sp->lock, flags); - - result = ibmasm_start_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat); - rhbeat->active = 0; - - return result; -} - -static ssize_t r_heartbeat_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) -{ - struct ibmasmfs_heartbeat_data *rhbeat = file->private_data; - - if (*offset < 0) - return -EINVAL; - if (count != 1) - return 0; - if (*offset != 0) - return 0; - - if (rhbeat->active) - ibmasm_stop_reverse_heartbeat(&rhbeat->heartbeat); - - return 1; -} - -static int remote_settings_file_close(struct inode *inode, struct file *file) -{ - return 0; -} - -static ssize_t remote_settings_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset) -{ - void __iomem *address = (void __iomem *)file->private_data; - int len = 0; - unsigned int value; - char lbuf[20]; - - value = readl(address); - len = snprintf(lbuf, sizeof(lbuf), "%d\n", value); - - return simple_read_from_buffer(buf, count, offset, lbuf, len); -} - -static ssize_t remote_settings_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset) -{ - void __iomem *address = (void __iomem *)file->private_data; - char *buff; - unsigned int value; - - if (*offset < 0) - return -EINVAL; - if (count == 0 || count > 1024) - return 0; - if (*offset != 0) - return 0; - - buff = memdup_user_nul(ubuff, count); - if (IS_ERR(buff)) - return PTR_ERR(buff); - - value = simple_strtoul(buff, NULL, 10); - writel(value, address); - kfree(buff); - - return count; -} - -static const struct file_operations command_fops = { - .open = command_file_open, - .release = command_file_close, - .read = command_file_read, - .write = command_file_write, - .llseek = generic_file_llseek, -}; - -static const struct file_operations event_fops = { - .open = event_file_open, - .release = event_file_close, - .read = event_file_read, - .write = event_file_write, - .llseek = generic_file_llseek, -}; - -static const struct file_operations r_heartbeat_fops = { - .open = r_heartbeat_file_open, - .release = r_heartbeat_file_close, - .read = r_heartbeat_file_read, - .write = r_heartbeat_file_write, - .llseek = generic_file_llseek, -}; - -static const struct file_operations remote_settings_fops = { - .open = simple_open, - .release = remote_settings_file_close, - .read = remote_settings_file_read, - .write = remote_settings_file_write, - .llseek = generic_file_llseek, -}; - - -static void ibmasmfs_create_files (struct super_block *sb) -{ - struct list_head *entry; - struct service_processor *sp; - - list_for_each(entry, &service_processors) { - struct dentry *dir; - struct dentry *remote_dir; - sp = list_entry(entry, struct service_processor, node); - dir = ibmasmfs_create_dir(sb->s_root, sp->dirname); - if (!dir) - continue; - - ibmasmfs_create_file(dir, "command", &command_fops, sp, S_IRUSR|S_IWUSR); - ibmasmfs_create_file(dir, "event", &event_fops, sp, S_IRUSR|S_IWUSR); - ibmasmfs_create_file(dir, "reverse_heartbeat", &r_heartbeat_fops, sp, S_IRUSR|S_IWUSR); - - remote_dir = ibmasmfs_create_dir(dir, "remote_video"); - if (!remote_dir) - continue; - - ibmasmfs_create_file(remote_dir, "width", &remote_settings_fops, (void *)display_width(sp), S_IRUSR|S_IWUSR); - ibmasmfs_create_file(remote_dir, "height", &remote_settings_fops, (void *)display_height(sp), S_IRUSR|S_IWUSR); - ibmasmfs_create_file(remote_dir, "depth", &remote_settings_fops, (void *)display_depth(sp), S_IRUSR|S_IWUSR); - } -} diff --git a/drivers/misc/ibmasm/lowlevel.c b/drivers/misc/ibmasm/lowlevel.c deleted file mode 100644 index 5313230f36ad..000000000000 --- a/drivers/misc/ibmasm/lowlevel.c +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#include "ibmasm.h" -#include "lowlevel.h" -#include "i2o.h" -#include "dot_command.h" -#include "remote.h" - -static struct i2o_header header = I2O_HEADER_TEMPLATE; - - -int ibmasm_send_i2o_message(struct service_processor *sp) -{ - u32 mfa; - 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; - - 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)); - memcpy_toio(&message->data, command->buffer, command_size); - - set_mfa_inbound(sp->base_address, mfa); - - return 0; -} - -irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id) -{ - u32 mfa; - struct service_processor *sp = (struct service_processor *)dev_id; - void __iomem *base_address = sp->base_address; - char tsbuf[32]; - - if (!sp_interrupt_pending(base_address)) - return IRQ_NONE; - - dbg("respond to interrupt at %s\n", get_timestamp(tsbuf)); - - if (mouse_interrupt_pending(sp)) { - ibmasm_handle_mouse_interrupt(sp); - clear_mouse_interrupt(sp); - } - - mfa = get_mfa_outbound(base_address); - if (valid_mfa(mfa)) { - struct i2o_message *msg = get_i2o_message(base_address, mfa); - ibmasm_receive_message(sp, &msg->data, incoming_data_size(msg)); - } else - dbg("didn't get a valid MFA\n"); - - set_mfa_outbound(base_address, mfa); - dbg("finished interrupt at %s\n", get_timestamp(tsbuf)); - - return IRQ_HANDLED; -} diff --git a/drivers/misc/ibmasm/lowlevel.h b/drivers/misc/ibmasm/lowlevel.h deleted file mode 100644 index 25f1ed07c3c5..000000000000 --- a/drivers/misc/ibmasm/lowlevel.h +++ /dev/null @@ -1,123 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -/* Condor service processor specific hardware definitions */ - -#ifndef __IBMASM_CONDOR_H__ -#define __IBMASM_CONDOR_H__ - -#include - -#define VENDORID_IBM 0x1014 -#define DEVICEID_RSA 0x010F - -#define GET_MFA_ADDR(x) (x & 0xFFFFFF00) - -#define MAILBOX_FULL(x) (x & 0x00000001) - -#define NO_MFAS_AVAILABLE 0xFFFFFFFF - - -#define INBOUND_QUEUE_PORT 0x40 /* contains address of next free MFA */ -#define OUTBOUND_QUEUE_PORT 0x44 /* contains address of posted MFA */ - -#define SP_INTR_MASK 0x00000008 -#define UART_INTR_MASK 0x00000010 - -#define INTR_STATUS_REGISTER 0x13A0 -#define INTR_CONTROL_REGISTER 0x13A4 - -#define SCOUT_COM_A_BASE 0x0000 -#define SCOUT_COM_B_BASE 0x0100 -#define SCOUT_COM_C_BASE 0x0200 -#define SCOUT_COM_D_BASE 0x0300 - -static inline int sp_interrupt_pending(void __iomem *base_address) -{ - return SP_INTR_MASK & readl(base_address + INTR_STATUS_REGISTER); -} - -static inline int uart_interrupt_pending(void __iomem *base_address) -{ - return UART_INTR_MASK & readl(base_address + INTR_STATUS_REGISTER); -} - -static inline void ibmasm_enable_interrupts(void __iomem *base_address, int mask) -{ - void __iomem *ctrl_reg = base_address + INTR_CONTROL_REGISTER; - writel( readl(ctrl_reg) & ~mask, ctrl_reg); -} - -static inline void ibmasm_disable_interrupts(void __iomem *base_address, int mask) -{ - void __iomem *ctrl_reg = base_address + INTR_CONTROL_REGISTER; - writel( readl(ctrl_reg) | mask, ctrl_reg); -} - -static inline void enable_sp_interrupts(void __iomem *base_address) -{ - ibmasm_enable_interrupts(base_address, SP_INTR_MASK); -} - -static inline void disable_sp_interrupts(void __iomem *base_address) -{ - ibmasm_disable_interrupts(base_address, SP_INTR_MASK); -} - -static inline void enable_uart_interrupts(void __iomem *base_address) -{ - ibmasm_enable_interrupts(base_address, UART_INTR_MASK); -} - -static inline void disable_uart_interrupts(void __iomem *base_address) -{ - ibmasm_disable_interrupts(base_address, UART_INTR_MASK); -} - -#define valid_mfa(mfa) ( (mfa) != NO_MFAS_AVAILABLE ) - -static inline u32 get_mfa_outbound(void __iomem *base_address) -{ - int retry; - u32 mfa; - - for (retry=0; retry<=10; retry++) { - mfa = readl(base_address + OUTBOUND_QUEUE_PORT); - if (valid_mfa(mfa)) - break; - } - return mfa; -} - -static inline void set_mfa_outbound(void __iomem *base_address, u32 mfa) -{ - writel(mfa, base_address + OUTBOUND_QUEUE_PORT); -} - -static inline u32 get_mfa_inbound(void __iomem *base_address) -{ - u32 mfa = readl(base_address + INBOUND_QUEUE_PORT); - - if (MAILBOX_FULL(mfa)) - return 0; - - return mfa; -} - -static inline void set_mfa_inbound(void __iomem *base_address, u32 mfa) -{ - writel(mfa, base_address + INBOUND_QUEUE_PORT); -} - -static inline struct i2o_message *get_i2o_message(void __iomem *base_address, u32 mfa) -{ - return (struct i2o_message *)(GET_MFA_ADDR(mfa) + base_address); -} - -#endif /* __IBMASM_CONDOR_H__ */ diff --git a/drivers/misc/ibmasm/module.c b/drivers/misc/ibmasm/module.c deleted file mode 100644 index 97f661f54b31..000000000000 --- a/drivers/misc/ibmasm/module.c +++ /dev/null @@ -1,224 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - * - * This driver is based on code originally written by Pete Reynolds - * and others. - */ - -/* - * The ASM device driver does the following things: - * - * 1) When loaded it sends a message to the service processor, - * indicating that an OS is * running. This causes the service processor - * to send periodic heartbeats to the OS. - * - * 2) Answers the periodic heartbeats sent by the service processor. - * Failure to do so would result in system reboot. - * - * 3) Acts as a pass through for dot commands sent from user applications. - * The interface for this is the ibmasmfs file system. - * - * 4) Allows user applications to register for event notification. Events - * are sent to the driver through interrupts. They can be read from user - * space through the ibmasmfs file system. - * - * 5) Allows user space applications to send heartbeats to the service - * processor (aka reverse heartbeats). Again this happens through ibmasmfs. - * - * 6) Handles remote mouse and keyboard event interrupts and makes them - * available to user applications through ibmasmfs. - * - */ - -#include -#include -#include -#include "ibmasm.h" -#include "lowlevel.h" -#include "remote.h" - -int ibmasm_debug = 0; -module_param(ibmasm_debug, int , S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(ibmasm_debug, " Set debug mode on or off"); - - -static int ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id) -{ - int result; - struct service_processor *sp; - - if ((result = pci_enable_device(pdev))) { - dev_err(&pdev->dev, "Failed to enable PCI device\n"); - return result; - } - if ((result = pci_request_regions(pdev, DRIVER_NAME))) { - dev_err(&pdev->dev, "Failed to allocate PCI resources\n"); - goto error_resources; - } - /* vnc client won't work without bus-mastering */ - pci_set_master(pdev); - - sp = kzalloc_obj(struct service_processor); - if (sp == NULL) { - dev_err(&pdev->dev, "Failed to allocate memory\n"); - result = -ENOMEM; - goto error_kmalloc; - } - - spin_lock_init(&sp->lock); - INIT_LIST_HEAD(&sp->command_queue); - - pci_set_drvdata(pdev, (void *)sp); - sp->dev = &pdev->dev; - sp->number = pdev->bus->number; - snprintf(sp->dirname, IBMASM_NAME_SIZE, "%d", sp->number); - snprintf(sp->devname, IBMASM_NAME_SIZE, "%s%d", DRIVER_NAME, sp->number); - - result = ibmasm_event_buffer_init(sp); - if (result) { - dev_err(sp->dev, "Failed to allocate event buffer\n"); - goto error_eventbuffer; - } - - result = ibmasm_heartbeat_init(sp); - if (result) { - dev_err(sp->dev, "Failed to allocate heartbeat command\n"); - goto error_heartbeat; - } - - sp->irq = pdev->irq; - sp->base_address = pci_ioremap_bar(pdev, 0); - if (!sp->base_address) { - dev_err(sp->dev, "Failed to ioremap pci memory\n"); - result = -ENODEV; - goto error_ioremap; - } - - result = request_irq(sp->irq, ibmasm_interrupt_handler, IRQF_SHARED, sp->devname, (void*)sp); - if (result) { - dev_err(sp->dev, "Failed to register interrupt handler\n"); - goto error_request_irq; - } - - enable_sp_interrupts(sp->base_address); - - result = ibmasm_init_remote_input_dev(sp); - if (result) { - dev_err(sp->dev, "Failed to initialize remote queue\n"); - goto error_init_remote; - } - - result = ibmasm_send_driver_vpd(sp); - if (result) { - dev_err(sp->dev, "Failed to send driver VPD to service processor\n"); - goto error_send_message; - } - result = ibmasm_send_os_state(sp, SYSTEM_STATE_OS_UP); - if (result) { - dev_err(sp->dev, "Failed to send OS state to service processor\n"); - goto error_send_message; - } - ibmasmfs_add_sp(sp); - - ibmasm_register_uart(sp); - - return 0; - -error_send_message: - ibmasm_free_remote_input_dev(sp); -error_init_remote: - disable_sp_interrupts(sp->base_address); - free_irq(sp->irq, (void *)sp); -error_request_irq: - iounmap(sp->base_address); -error_ioremap: - ibmasm_heartbeat_exit(sp); -error_heartbeat: - ibmasm_event_buffer_exit(sp); -error_eventbuffer: - kfree(sp); -error_kmalloc: - pci_release_regions(pdev); -error_resources: - pci_disable_device(pdev); - - return result; -} - -static void ibmasm_remove_one(struct pci_dev *pdev) -{ - struct service_processor *sp = pci_get_drvdata(pdev); - - dbg("Unregistering UART\n"); - ibmasm_unregister_uart(sp); - dbg("Sending OS down message\n"); - if (ibmasm_send_os_state(sp, SYSTEM_STATE_OS_DOWN)) - err("failed to get response to 'Send OS State' command\n"); - dbg("Disabling heartbeats\n"); - ibmasm_heartbeat_exit(sp); - dbg("Disabling interrupts\n"); - disable_sp_interrupts(sp->base_address); - dbg("Freeing SP irq\n"); - free_irq(sp->irq, (void *)sp); - dbg("Cleaning up\n"); - ibmasm_free_remote_input_dev(sp); - iounmap(sp->base_address); - ibmasm_event_buffer_exit(sp); - kfree(sp); - pci_release_regions(pdev); - pci_disable_device(pdev); -} - -static struct pci_device_id ibmasm_pci_table[] = -{ - { PCI_DEVICE(VENDORID_IBM, DEVICEID_RSA) }, - { } -}; - -static struct pci_driver ibmasm_driver = { - .name = DRIVER_NAME, - .id_table = ibmasm_pci_table, - .probe = ibmasm_init_one, - .remove = ibmasm_remove_one, -}; - -static void __exit ibmasm_exit (void) -{ - ibmasm_unregister_panic_notifier(); - ibmasmfs_unregister(); - pci_unregister_driver(&ibmasm_driver); - info(DRIVER_DESC " version " DRIVER_VERSION " unloaded"); -} - -static int __init ibmasm_init(void) -{ - int result = pci_register_driver(&ibmasm_driver); - if (result) - return result; - - result = ibmasmfs_register(); - if (result) { - pci_unregister_driver(&ibmasm_driver); - err("Failed to register ibmasmfs file system"); - return result; - } - - ibmasm_register_panic_notifier(); - info(DRIVER_DESC " version " DRIVER_VERSION " loaded"); - return 0; -} - -module_init(ibmasm_init); -module_exit(ibmasm_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, ibmasm_pci_table); - diff --git a/drivers/misc/ibmasm/r_heartbeat.c b/drivers/misc/ibmasm/r_heartbeat.c deleted file mode 100644 index 8d3fa0ce4b6c..000000000000 --- a/drivers/misc/ibmasm/r_heartbeat.c +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#include -#include "ibmasm.h" -#include "dot_command.h" - -/* - * Reverse Heartbeat, i.e. heartbeats sent from the driver to the - * service processor. - * These heartbeats are initiated by user level programs. - */ - -/* the reverse heartbeat dot command */ -#pragma pack(1) -static struct { - struct dot_command_header header; - unsigned char command[3]; -} rhb_dot_cmd = { - .header = { - .type = sp_read, - .command_size = 3, - .data_size = 0, - .status = 0 - }, - .command = { 4, 3, 6 } -}; -#pragma pack() - -void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb) -{ - init_waitqueue_head(&rhb->wait); - rhb->stopped = 0; -} - -/* - * start_reverse_heartbeat - * Loop forever, sending a reverse heartbeat dot command to the service - * processor, then sleeping. The loop comes to an end if the service - * processor fails to respond 3 times or we were interrupted. - */ -int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb) -{ - struct command *cmd; - int times_failed = 0; - int result = 1; - - cmd = ibmasm_new_command(sp, sizeof(rhb_dot_cmd)); - if (!cmd) - return -ENOMEM; - - while (times_failed < 3) { - memcpy(cmd->buffer, (void *)&rhb_dot_cmd, sizeof(rhb_dot_cmd)); - cmd->status = IBMASM_CMD_PENDING; - ibmasm_exec_command(sp, cmd); - ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL); - - if (cmd->status != IBMASM_CMD_COMPLETE) - times_failed++; - - wait_event_interruptible_timeout(rhb->wait, - rhb->stopped, - REVERSE_HEARTBEAT_TIMEOUT * HZ); - - if (signal_pending(current) || rhb->stopped) { - result = -EINTR; - break; - } - } - command_put(cmd); - rhb->stopped = 0; - - return result; -} - -void ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb) -{ - rhb->stopped = 1; - wake_up_interruptible(&rhb->wait); -} diff --git a/drivers/misc/ibmasm/remote.c b/drivers/misc/ibmasm/remote.c deleted file mode 100644 index 521531738c9a..000000000000 --- a/drivers/misc/ibmasm/remote.c +++ /dev/null @@ -1,273 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Authors: Max Asböck - * Vernon Mauery - */ - -/* Remote mouse and keyboard event handling functions */ - -#include -#include "ibmasm.h" -#include "remote.h" - -#define MOUSE_X_MAX 1600 -#define MOUSE_Y_MAX 1200 - -static const unsigned short xlate_high[XLATE_SIZE] = { - [KEY_SYM_ENTER & 0xff] = KEY_ENTER, - [KEY_SYM_KPSLASH & 0xff] = KEY_KPSLASH, - [KEY_SYM_KPSTAR & 0xff] = KEY_KPASTERISK, - [KEY_SYM_KPMINUS & 0xff] = KEY_KPMINUS, - [KEY_SYM_KPDOT & 0xff] = KEY_KPDOT, - [KEY_SYM_KPPLUS & 0xff] = KEY_KPPLUS, - [KEY_SYM_KP0 & 0xff] = KEY_KP0, - [KEY_SYM_KP1 & 0xff] = KEY_KP1, - [KEY_SYM_KP2 & 0xff] = KEY_KP2, [KEY_SYM_KPDOWN & 0xff] = KEY_KP2, - [KEY_SYM_KP3 & 0xff] = KEY_KP3, - [KEY_SYM_KP4 & 0xff] = KEY_KP4, [KEY_SYM_KPLEFT & 0xff] = KEY_KP4, - [KEY_SYM_KP5 & 0xff] = KEY_KP5, - [KEY_SYM_KP6 & 0xff] = KEY_KP6, [KEY_SYM_KPRIGHT & 0xff] = KEY_KP6, - [KEY_SYM_KP7 & 0xff] = KEY_KP7, - [KEY_SYM_KP8 & 0xff] = KEY_KP8, [KEY_SYM_KPUP & 0xff] = KEY_KP8, - [KEY_SYM_KP9 & 0xff] = KEY_KP9, - [KEY_SYM_BK_SPC & 0xff] = KEY_BACKSPACE, - [KEY_SYM_TAB & 0xff] = KEY_TAB, - [KEY_SYM_CTRL & 0xff] = KEY_LEFTCTRL, - [KEY_SYM_ALT & 0xff] = KEY_LEFTALT, - [KEY_SYM_INSERT & 0xff] = KEY_INSERT, - [KEY_SYM_DELETE & 0xff] = KEY_DELETE, - [KEY_SYM_SHIFT & 0xff] = KEY_LEFTSHIFT, - [KEY_SYM_UARROW & 0xff] = KEY_UP, - [KEY_SYM_DARROW & 0xff] = KEY_DOWN, - [KEY_SYM_LARROW & 0xff] = KEY_LEFT, - [KEY_SYM_RARROW & 0xff] = KEY_RIGHT, - [KEY_SYM_ESCAPE & 0xff] = KEY_ESC, - [KEY_SYM_PAGEUP & 0xff] = KEY_PAGEUP, - [KEY_SYM_PAGEDOWN & 0xff] = KEY_PAGEDOWN, - [KEY_SYM_HOME & 0xff] = KEY_HOME, - [KEY_SYM_END & 0xff] = KEY_END, - [KEY_SYM_F1 & 0xff] = KEY_F1, - [KEY_SYM_F2 & 0xff] = KEY_F2, - [KEY_SYM_F3 & 0xff] = KEY_F3, - [KEY_SYM_F4 & 0xff] = KEY_F4, - [KEY_SYM_F5 & 0xff] = KEY_F5, - [KEY_SYM_F6 & 0xff] = KEY_F6, - [KEY_SYM_F7 & 0xff] = KEY_F7, - [KEY_SYM_F8 & 0xff] = KEY_F8, - [KEY_SYM_F9 & 0xff] = KEY_F9, - [KEY_SYM_F10 & 0xff] = KEY_F10, - [KEY_SYM_F11 & 0xff] = KEY_F11, - [KEY_SYM_F12 & 0xff] = KEY_F12, - [KEY_SYM_CAP_LOCK & 0xff] = KEY_CAPSLOCK, - [KEY_SYM_NUM_LOCK & 0xff] = KEY_NUMLOCK, - [KEY_SYM_SCR_LOCK & 0xff] = KEY_SCROLLLOCK, -}; - -static const unsigned short xlate[XLATE_SIZE] = { - [NO_KEYCODE] = KEY_RESERVED, - [KEY_SYM_SPACE] = KEY_SPACE, - [KEY_SYM_TILDE] = KEY_GRAVE, [KEY_SYM_BKTIC] = KEY_GRAVE, - [KEY_SYM_ONE] = KEY_1, [KEY_SYM_BANG] = KEY_1, - [KEY_SYM_TWO] = KEY_2, [KEY_SYM_AT] = KEY_2, - [KEY_SYM_THREE] = KEY_3, [KEY_SYM_POUND] = KEY_3, - [KEY_SYM_FOUR] = KEY_4, [KEY_SYM_DOLLAR] = KEY_4, - [KEY_SYM_FIVE] = KEY_5, [KEY_SYM_PERCENT] = KEY_5, - [KEY_SYM_SIX] = KEY_6, [KEY_SYM_CARAT] = KEY_6, - [KEY_SYM_SEVEN] = KEY_7, [KEY_SYM_AMPER] = KEY_7, - [KEY_SYM_EIGHT] = KEY_8, [KEY_SYM_STAR] = KEY_8, - [KEY_SYM_NINE] = KEY_9, [KEY_SYM_LPAREN] = KEY_9, - [KEY_SYM_ZERO] = KEY_0, [KEY_SYM_RPAREN] = KEY_0, - [KEY_SYM_MINUS] = KEY_MINUS, [KEY_SYM_USCORE] = KEY_MINUS, - [KEY_SYM_EQUAL] = KEY_EQUAL, [KEY_SYM_PLUS] = KEY_EQUAL, - [KEY_SYM_LBRKT] = KEY_LEFTBRACE, [KEY_SYM_LCURLY] = KEY_LEFTBRACE, - [KEY_SYM_RBRKT] = KEY_RIGHTBRACE, [KEY_SYM_RCURLY] = KEY_RIGHTBRACE, - [KEY_SYM_SLASH] = KEY_BACKSLASH, [KEY_SYM_PIPE] = KEY_BACKSLASH, - [KEY_SYM_TIC] = KEY_APOSTROPHE, [KEY_SYM_QUOTE] = KEY_APOSTROPHE, - [KEY_SYM_SEMIC] = KEY_SEMICOLON, [KEY_SYM_COLON] = KEY_SEMICOLON, - [KEY_SYM_COMMA] = KEY_COMMA, [KEY_SYM_LT] = KEY_COMMA, - [KEY_SYM_PERIOD] = KEY_DOT, [KEY_SYM_GT] = KEY_DOT, - [KEY_SYM_BSLASH] = KEY_SLASH, [KEY_SYM_QMARK] = KEY_SLASH, - [KEY_SYM_A] = KEY_A, [KEY_SYM_a] = KEY_A, - [KEY_SYM_B] = KEY_B, [KEY_SYM_b] = KEY_B, - [KEY_SYM_C] = KEY_C, [KEY_SYM_c] = KEY_C, - [KEY_SYM_D] = KEY_D, [KEY_SYM_d] = KEY_D, - [KEY_SYM_E] = KEY_E, [KEY_SYM_e] = KEY_E, - [KEY_SYM_F] = KEY_F, [KEY_SYM_f] = KEY_F, - [KEY_SYM_G] = KEY_G, [KEY_SYM_g] = KEY_G, - [KEY_SYM_H] = KEY_H, [KEY_SYM_h] = KEY_H, - [KEY_SYM_I] = KEY_I, [KEY_SYM_i] = KEY_I, - [KEY_SYM_J] = KEY_J, [KEY_SYM_j] = KEY_J, - [KEY_SYM_K] = KEY_K, [KEY_SYM_k] = KEY_K, - [KEY_SYM_L] = KEY_L, [KEY_SYM_l] = KEY_L, - [KEY_SYM_M] = KEY_M, [KEY_SYM_m] = KEY_M, - [KEY_SYM_N] = KEY_N, [KEY_SYM_n] = KEY_N, - [KEY_SYM_O] = KEY_O, [KEY_SYM_o] = KEY_O, - [KEY_SYM_P] = KEY_P, [KEY_SYM_p] = KEY_P, - [KEY_SYM_Q] = KEY_Q, [KEY_SYM_q] = KEY_Q, - [KEY_SYM_R] = KEY_R, [KEY_SYM_r] = KEY_R, - [KEY_SYM_S] = KEY_S, [KEY_SYM_s] = KEY_S, - [KEY_SYM_T] = KEY_T, [KEY_SYM_t] = KEY_T, - [KEY_SYM_U] = KEY_U, [KEY_SYM_u] = KEY_U, - [KEY_SYM_V] = KEY_V, [KEY_SYM_v] = KEY_V, - [KEY_SYM_W] = KEY_W, [KEY_SYM_w] = KEY_W, - [KEY_SYM_X] = KEY_X, [KEY_SYM_x] = KEY_X, - [KEY_SYM_Y] = KEY_Y, [KEY_SYM_y] = KEY_Y, - [KEY_SYM_Z] = KEY_Z, [KEY_SYM_z] = KEY_Z, -}; - -static void print_input(struct remote_input *input) -{ - if (input->type == INPUT_TYPE_MOUSE) { - unsigned char buttons = input->mouse_buttons; - dbg("remote mouse movement: (x,y)=(%d,%d)%s%s%s%s\n", - input->data.mouse.x, input->data.mouse.y, - (buttons) ? " -- buttons:" : "", - (buttons & REMOTE_BUTTON_LEFT) ? "left " : "", - (buttons & REMOTE_BUTTON_MIDDLE) ? "middle " : "", - (buttons & REMOTE_BUTTON_RIGHT) ? "right" : "" - ); - } else { - dbg("remote keypress (code, flag, down):" - "%d (0x%x) [0x%x] [0x%x]\n", - input->data.keyboard.key_code, - input->data.keyboard.key_code, - input->data.keyboard.key_flag, - input->data.keyboard.key_down - ); - } -} - -static void send_mouse_event(struct input_dev *dev, struct remote_input *input) -{ - unsigned char buttons = input->mouse_buttons; - - input_report_abs(dev, ABS_X, input->data.mouse.x); - input_report_abs(dev, ABS_Y, input->data.mouse.y); - input_report_key(dev, BTN_LEFT, buttons & REMOTE_BUTTON_LEFT); - input_report_key(dev, BTN_MIDDLE, buttons & REMOTE_BUTTON_MIDDLE); - input_report_key(dev, BTN_RIGHT, buttons & REMOTE_BUTTON_RIGHT); - input_sync(dev); -} - -static void send_keyboard_event(struct input_dev *dev, - struct remote_input *input) -{ - unsigned int key; - unsigned short code = input->data.keyboard.key_code; - - if (code & 0xff00) - key = xlate_high[code & 0xff]; - else - key = xlate[code]; - input_report_key(dev, key, input->data.keyboard.key_down); - input_sync(dev); -} - -void ibmasm_handle_mouse_interrupt(struct service_processor *sp) -{ - unsigned long reader; - unsigned long writer; - struct remote_input input; - - reader = get_queue_reader(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)); - - print_input(&input); - if (input.type == INPUT_TYPE_MOUSE) { - send_mouse_event(sp->remote.mouse_dev, &input); - } else if (input.type == INPUT_TYPE_KEYBOARD) { - send_keyboard_event(sp->remote.keybd_dev, &input); - } else - break; - - reader = advance_queue_reader(sp, reader); - writer = get_queue_writer(sp); - } -} - -int ibmasm_init_remote_input_dev(struct service_processor *sp) -{ - /* set up the mouse input device */ - struct input_dev *mouse_dev, *keybd_dev; - struct pci_dev *pdev = to_pci_dev(sp->dev); - int error = -ENOMEM; - int i; - - sp->remote.mouse_dev = mouse_dev = input_allocate_device(); - sp->remote.keybd_dev = keybd_dev = input_allocate_device(); - - if (!mouse_dev || !keybd_dev) - goto err_free_devices; - - mouse_dev->id.bustype = BUS_PCI; - mouse_dev->id.vendor = pdev->vendor; - mouse_dev->id.product = pdev->device; - mouse_dev->id.version = 1; - mouse_dev->dev.parent = sp->dev; - mouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - mouse_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); - set_bit(BTN_TOUCH, mouse_dev->keybit); - mouse_dev->name = "ibmasm RSA I remote mouse"; - input_set_abs_params(mouse_dev, ABS_X, 0, MOUSE_X_MAX, 0, 0); - input_set_abs_params(mouse_dev, ABS_Y, 0, MOUSE_Y_MAX, 0, 0); - - keybd_dev->id.bustype = BUS_PCI; - keybd_dev->id.vendor = pdev->vendor; - keybd_dev->id.product = pdev->device; - keybd_dev->id.version = 2; - keybd_dev->dev.parent = sp->dev; - keybd_dev->evbit[0] = BIT_MASK(EV_KEY); - keybd_dev->name = "ibmasm RSA I remote keyboard"; - - for (i = 0; i < XLATE_SIZE; i++) { - if (xlate_high[i]) - set_bit(xlate_high[i], keybd_dev->keybit); - if (xlate[i]) - set_bit(xlate[i], keybd_dev->keybit); - } - - error = input_register_device(mouse_dev); - if (error) - goto err_free_devices; - - error = input_register_device(keybd_dev); - if (error) - goto err_unregister_mouse_dev; - - enable_mouse_interrupts(sp); - - printk(KERN_INFO "ibmasm remote responding to events on RSA card %d\n", sp->number); - - return 0; - - err_unregister_mouse_dev: - input_unregister_device(mouse_dev); - mouse_dev = NULL; /* so we don't try to free it again below */ - err_free_devices: - input_free_device(mouse_dev); - input_free_device(keybd_dev); - - return error; -} - -void ibmasm_free_remote_input_dev(struct service_processor *sp) -{ - disable_mouse_interrupts(sp); - input_unregister_device(sp->remote.mouse_dev); - input_unregister_device(sp->remote.keybd_dev); -} - diff --git a/drivers/misc/ibmasm/remote.h b/drivers/misc/ibmasm/remote.h deleted file mode 100644 index ec4e78ec5a67..000000000000 --- a/drivers/misc/ibmasm/remote.h +++ /dev/null @@ -1,257 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - * - * Originally written by Pete Reynolds - */ - -#ifndef _IBMASM_REMOTE_H_ -#define _IBMASM_REMOTE_H_ - -#include - -/* pci offsets */ -#define CONDOR_MOUSE_DATA 0x000AC000 -#define CONDOR_MOUSE_ISR_CONTROL 0x00 -#define CONDOR_MOUSE_ISR_STATUS 0x04 -#define CONDOR_MOUSE_Q_READER 0x08 -#define CONDOR_MOUSE_Q_WRITER 0x0C -#define CONDOR_MOUSE_Q_BEGIN 0x10 -#define CONDOR_MOUSE_MAX_X 0x14 -#define CONDOR_MOUSE_MAX_Y 0x18 - -#define CONDOR_INPUT_DESKTOP_INFO 0x1F0 -#define CONDOR_INPUT_DISPLAY_RESX 0x1F4 -#define CONDOR_INPUT_DISPLAY_RESY 0x1F8 -#define CONDOR_INPUT_DISPLAY_BITS 0x1FC -#define CONDOR_OUTPUT_VNC_STATUS 0x200 - -#define CONDOR_MOUSE_INTR_STATUS_MASK 0x00000001 - -#define INPUT_TYPE_MOUSE 0x1 -#define INPUT_TYPE_KEYBOARD 0x2 - - -/* mouse button states received from SP */ -#define REMOTE_DOUBLE_CLICK 0xF0 -#define REMOTE_BUTTON_LEFT 0x01 -#define REMOTE_BUTTON_MIDDLE 0x02 -#define REMOTE_BUTTON_RIGHT 0x04 - -/* size of keysym/keycode translation matrices */ -#define XLATE_SIZE 256 - -struct mouse_input { - unsigned short y; - unsigned short x; -}; - - -struct keyboard_input { - unsigned short key_code; - unsigned char key_flag; - unsigned char key_down; -}; - - - -struct remote_input { - union { - struct mouse_input mouse; - struct keyboard_input keyboard; - } data; - - unsigned char type; - unsigned char pad1; - unsigned char mouse_buttons; - unsigned char pad3; -}; - -#define mouse_addr(sp) (sp->base_address + CONDOR_MOUSE_DATA) -#define display_width(sp) (mouse_addr(sp) + CONDOR_INPUT_DISPLAY_RESX) -#define display_height(sp) (mouse_addr(sp) + CONDOR_INPUT_DISPLAY_RESY) -#define display_depth(sp) (mouse_addr(sp) + CONDOR_INPUT_DISPLAY_BITS) -#define desktop_info(sp) (mouse_addr(sp) + CONDOR_INPUT_DESKTOP_INFO) -#define vnc_status(sp) (mouse_addr(sp) + CONDOR_OUTPUT_VNC_STATUS) -#define isr_control(sp) (mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL) - -#define mouse_interrupt_pending(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_ISR_STATUS) -#define clear_mouse_interrupt(sp) writel(0, mouse_addr(sp) + CONDOR_MOUSE_ISR_STATUS) -#define enable_mouse_interrupts(sp) writel(1, mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL) -#define disable_mouse_interrupts(sp) writel(0, mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL) - -/* remote input queue operations */ -#define REMOTE_QUEUE_SIZE 60 - -#define get_queue_writer(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_Q_WRITER) -#define get_queue_reader(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_Q_READER) -#define set_queue_reader(sp, reader) writel(reader, mouse_addr(sp) + CONDOR_MOUSE_Q_READER) - -#define queue_begin (mouse_addr(sp) + CONDOR_MOUSE_Q_BEGIN) - -#define get_queue_entry(sp, read_index) \ - ((void*)(queue_begin + read_index * sizeof(struct remote_input))) - -static inline int advance_queue_reader(struct service_processor *sp, unsigned long reader) -{ - reader++; - if (reader == REMOTE_QUEUE_SIZE) - reader = 0; - - set_queue_reader(sp, reader); - return reader; -} - -#define NO_KEYCODE 0 -#define KEY_SYM_BK_SPC 0xFF08 -#define KEY_SYM_TAB 0xFF09 -#define KEY_SYM_ENTER 0xFF0D -#define KEY_SYM_SCR_LOCK 0xFF14 -#define KEY_SYM_ESCAPE 0xFF1B -#define KEY_SYM_HOME 0xFF50 -#define KEY_SYM_LARROW 0xFF51 -#define KEY_SYM_UARROW 0xFF52 -#define KEY_SYM_RARROW 0xFF53 -#define KEY_SYM_DARROW 0xFF54 -#define KEY_SYM_PAGEUP 0xFF55 -#define KEY_SYM_PAGEDOWN 0xFF56 -#define KEY_SYM_END 0xFF57 -#define KEY_SYM_INSERT 0xFF63 -#define KEY_SYM_NUM_LOCK 0xFF7F -#define KEY_SYM_KPSTAR 0xFFAA -#define KEY_SYM_KPPLUS 0xFFAB -#define KEY_SYM_KPMINUS 0xFFAD -#define KEY_SYM_KPDOT 0xFFAE -#define KEY_SYM_KPSLASH 0xFFAF -#define KEY_SYM_KPRIGHT 0xFF96 -#define KEY_SYM_KPUP 0xFF97 -#define KEY_SYM_KPLEFT 0xFF98 -#define KEY_SYM_KPDOWN 0xFF99 -#define KEY_SYM_KP0 0xFFB0 -#define KEY_SYM_KP1 0xFFB1 -#define KEY_SYM_KP2 0xFFB2 -#define KEY_SYM_KP3 0xFFB3 -#define KEY_SYM_KP4 0xFFB4 -#define KEY_SYM_KP5 0xFFB5 -#define KEY_SYM_KP6 0xFFB6 -#define KEY_SYM_KP7 0xFFB7 -#define KEY_SYM_KP8 0xFFB8 -#define KEY_SYM_KP9 0xFFB9 -#define KEY_SYM_F1 0xFFBE // 1B 5B 5B 41 -#define KEY_SYM_F2 0xFFBF // 1B 5B 5B 42 -#define KEY_SYM_F3 0xFFC0 // 1B 5B 5B 43 -#define KEY_SYM_F4 0xFFC1 // 1B 5B 5B 44 -#define KEY_SYM_F5 0xFFC2 // 1B 5B 5B 45 -#define KEY_SYM_F6 0xFFC3 // 1B 5B 31 37 7E -#define KEY_SYM_F7 0xFFC4 // 1B 5B 31 38 7E -#define KEY_SYM_F8 0xFFC5 // 1B 5B 31 39 7E -#define KEY_SYM_F9 0xFFC6 // 1B 5B 32 30 7E -#define KEY_SYM_F10 0xFFC7 // 1B 5B 32 31 7E -#define KEY_SYM_F11 0xFFC8 // 1B 5B 32 33 7E -#define KEY_SYM_F12 0xFFC9 // 1B 5B 32 34 7E -#define KEY_SYM_SHIFT 0xFFE1 -#define KEY_SYM_CTRL 0xFFE3 -#define KEY_SYM_ALT 0xFFE9 -#define KEY_SYM_CAP_LOCK 0xFFE5 -#define KEY_SYM_DELETE 0xFFFF -#define KEY_SYM_TILDE 0x60 -#define KEY_SYM_BKTIC 0x7E -#define KEY_SYM_ONE 0x31 -#define KEY_SYM_BANG 0x21 -#define KEY_SYM_TWO 0x32 -#define KEY_SYM_AT 0x40 -#define KEY_SYM_THREE 0x33 -#define KEY_SYM_POUND 0x23 -#define KEY_SYM_FOUR 0x34 -#define KEY_SYM_DOLLAR 0x24 -#define KEY_SYM_FIVE 0x35 -#define KEY_SYM_PERCENT 0x25 -#define KEY_SYM_SIX 0x36 -#define KEY_SYM_CARAT 0x5E -#define KEY_SYM_SEVEN 0x37 -#define KEY_SYM_AMPER 0x26 -#define KEY_SYM_EIGHT 0x38 -#define KEY_SYM_STAR 0x2A -#define KEY_SYM_NINE 0x39 -#define KEY_SYM_LPAREN 0x28 -#define KEY_SYM_ZERO 0x30 -#define KEY_SYM_RPAREN 0x29 -#define KEY_SYM_MINUS 0x2D -#define KEY_SYM_USCORE 0x5F -#define KEY_SYM_EQUAL 0x2B -#define KEY_SYM_PLUS 0x3D -#define KEY_SYM_LBRKT 0x5B -#define KEY_SYM_LCURLY 0x7B -#define KEY_SYM_RBRKT 0x5D -#define KEY_SYM_RCURLY 0x7D -#define KEY_SYM_SLASH 0x5C -#define KEY_SYM_PIPE 0x7C -#define KEY_SYM_TIC 0x27 -#define KEY_SYM_QUOTE 0x22 -#define KEY_SYM_SEMIC 0x3B -#define KEY_SYM_COLON 0x3A -#define KEY_SYM_COMMA 0x2C -#define KEY_SYM_LT 0x3C -#define KEY_SYM_PERIOD 0x2E -#define KEY_SYM_GT 0x3E -#define KEY_SYM_BSLASH 0x2F -#define KEY_SYM_QMARK 0x3F -#define KEY_SYM_A 0x41 -#define KEY_SYM_B 0x42 -#define KEY_SYM_C 0x43 -#define KEY_SYM_D 0x44 -#define KEY_SYM_E 0x45 -#define KEY_SYM_F 0x46 -#define KEY_SYM_G 0x47 -#define KEY_SYM_H 0x48 -#define KEY_SYM_I 0x49 -#define KEY_SYM_J 0x4A -#define KEY_SYM_K 0x4B -#define KEY_SYM_L 0x4C -#define KEY_SYM_M 0x4D -#define KEY_SYM_N 0x4E -#define KEY_SYM_O 0x4F -#define KEY_SYM_P 0x50 -#define KEY_SYM_Q 0x51 -#define KEY_SYM_R 0x52 -#define KEY_SYM_S 0x53 -#define KEY_SYM_T 0x54 -#define KEY_SYM_U 0x55 -#define KEY_SYM_V 0x56 -#define KEY_SYM_W 0x57 -#define KEY_SYM_X 0x58 -#define KEY_SYM_Y 0x59 -#define KEY_SYM_Z 0x5A -#define KEY_SYM_a 0x61 -#define KEY_SYM_b 0x62 -#define KEY_SYM_c 0x63 -#define KEY_SYM_d 0x64 -#define KEY_SYM_e 0x65 -#define KEY_SYM_f 0x66 -#define KEY_SYM_g 0x67 -#define KEY_SYM_h 0x68 -#define KEY_SYM_i 0x69 -#define KEY_SYM_j 0x6A -#define KEY_SYM_k 0x6B -#define KEY_SYM_l 0x6C -#define KEY_SYM_m 0x6D -#define KEY_SYM_n 0x6E -#define KEY_SYM_o 0x6F -#define KEY_SYM_p 0x70 -#define KEY_SYM_q 0x71 -#define KEY_SYM_r 0x72 -#define KEY_SYM_s 0x73 -#define KEY_SYM_t 0x74 -#define KEY_SYM_u 0x75 -#define KEY_SYM_v 0x76 -#define KEY_SYM_w 0x77 -#define KEY_SYM_x 0x78 -#define KEY_SYM_y 0x79 -#define KEY_SYM_z 0x7A -#define KEY_SYM_SPACE 0x20 -#endif /* _IBMASM_REMOTE_H_ */ diff --git a/drivers/misc/ibmasm/uart.c b/drivers/misc/ibmasm/uart.c deleted file mode 100644 index a5d4c8e010ee..000000000000 --- a/drivers/misc/ibmasm/uart.c +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * IBM ASM Service Processor Device Driver - * - * Copyright (C) IBM Corporation, 2004 - * - * Author: Max Asböck - */ - -#include -#include -#include -#include -#include -#include "ibmasm.h" -#include "lowlevel.h" - - -void ibmasm_register_uart(struct service_processor *sp) -{ - struct uart_8250_port uart; - void __iomem *iomem_base; - - iomem_base = sp->base_address + SCOUT_COM_B_BASE; - - /* read the uart scratch register to determine if the UART - * is dedicated to the service processor or if the OS can use it - */ - if (0 == readl(iomem_base + UART_SCR)) { - dev_info(sp->dev, "IBM SP UART not registered, owned by service processor\n"); - sp->serial_line = -1; - return; - } - - memset(&uart, 0, sizeof(uart)); - uart.port.irq = sp->irq; - uart.port.uartclk = 3686400; - uart.port.flags = UPF_SHARE_IRQ; - uart.port.iotype = UPIO_MEM; - uart.port.membase = iomem_base; - - sp->serial_line = serial8250_register_8250_port(&uart); - if (sp->serial_line < 0) { - dev_err(sp->dev, "Failed to register serial port\n"); - return; - } - enable_uart_interrupts(sp->base_address); -} - -void ibmasm_unregister_uart(struct service_processor *sp) -{ - if (sp->serial_line < 0) - return; - - disable_uart_interrupts(sp->base_address); - serial8250_unregister_port(sp->serial_line); -} From dbf69afe320ebdafc63c96d411b588f3b6167419 Mon Sep 17 00:00:00 2001 From: Dimitri Sivanich Date: Fri, 31 Jul 2026 09:30:24 -0500 Subject: [PATCH 477/513] misc: sgi-xp: Remove SGI XP drivers Working XP drivers require the GRU driver. The GRU driver is being removed, so remove XP as well. Signed-off-by: Dimitri Sivanich Acked-by: Robin Holt Acked-by: Steve Wahl Link: https://patch.msgid.link/amyxgPYeowzWt_8W@hpe.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 6 - drivers/misc/Kconfig | 13 - drivers/misc/Makefile | 1 - drivers/misc/sgi-xp/Makefile | 13 - drivers/misc/sgi-xp/xp.h | 341 ------ drivers/misc/sgi-xp/xp_main.c | 261 ---- drivers/misc/sgi-xp/xp_uv.c | 151 --- drivers/misc/sgi-xp/xpc.h | 732 ------------ drivers/misc/sgi-xp/xpc_channel.c | 1011 ---------------- drivers/misc/sgi-xp/xpc_main.c | 1309 -------------------- drivers/misc/sgi-xp/xpc_partition.c | 545 --------- drivers/misc/sgi-xp/xpc_uv.c | 1728 --------------------------- drivers/misc/sgi-xp/xpnet.c | 599 ---------- 13 files changed, 6710 deletions(-) delete mode 100644 drivers/misc/sgi-xp/Makefile delete mode 100644 drivers/misc/sgi-xp/xp.h delete mode 100644 drivers/misc/sgi-xp/xp_main.c delete mode 100644 drivers/misc/sgi-xp/xp_uv.c delete mode 100644 drivers/misc/sgi-xp/xpc.h delete mode 100644 drivers/misc/sgi-xp/xpc_channel.c delete mode 100644 drivers/misc/sgi-xp/xpc_main.c delete mode 100644 drivers/misc/sgi-xp/xpc_partition.c delete mode 100644 drivers/misc/sgi-xp/xpc_uv.c delete mode 100644 drivers/misc/sgi-xp/xpnet.c diff --git a/MAINTAINERS b/MAINTAINERS index 1e27c49ffe48..52275c7169e1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24635,12 +24635,6 @@ M: Dimitri Sivanich S: Maintained F: drivers/misc/sgi-gru/ -SGI XP/XPC/XPNET DRIVER -M: Robin Holt -M: Steve Wahl -S: Maintained -F: drivers/misc/sgi-xp/ - SHARED MEMORY COMMUNICATIONS (SMC) SOCKETS M: D. Wythe M: Dust Li diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 10803a6f1227..1a718763a44f 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -163,19 +163,6 @@ config ENCLOSURE_SERVICES driver (SCSI/ATA) which supports enclosures or a SCSI enclosure device (SES) to use these services. -config SGI_XP - tristate "Support communication between SGI SSIs" - depends on NET - depends on X86_UV && SMP - depends on X86_64 || BROKEN - select SGI_GRU if X86_64 && SMP - help - An SGI machine can be divided into multiple Single System - Images which act independently of each other and have - hardware based memory protection from the others. Enabling - this feature will allow for direct communication between SSIs - based on a network adapter and DMA messaging. - config SMPRO_ERRMON tristate "Ampere Computing SMPro error monitor driver" depends on MFD_SMPRO || COMPILE_TEST diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index f317fd6c9f55..2db1f9fe48b7 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -21,7 +21,6 @@ obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o obj-$(CONFIG_KGDB_TESTS) += kgdbts.o -obj-$(CONFIG_SGI_XP) += sgi-xp/ obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_SMPRO_ERRMON) += smpro-errmon.o obj-$(CONFIG_SMPRO_MISC) += smpro-misc.o diff --git a/drivers/misc/sgi-xp/Makefile b/drivers/misc/sgi-xp/Makefile deleted file mode 100644 index 34c55a4045af..000000000000 --- a/drivers/misc/sgi-xp/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for SGI's XP devices. -# - -obj-$(CONFIG_SGI_XP) += xp.o -xp-y := xp_main.o xp_uv.o - -obj-$(CONFIG_SGI_XP) += xpc.o -xpc-y := xpc_main.o xpc_channel.o xpc_partition.o \ - xpc_uv.o - -obj-$(CONFIG_SGI_XP) += xpnet.o diff --git a/drivers/misc/sgi-xp/xp.h b/drivers/misc/sgi-xp/xp.h deleted file mode 100644 index 3185711beb07..000000000000 --- a/drivers/misc/sgi-xp/xp.h +++ /dev/null @@ -1,341 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (C) 2004-2008 Silicon Graphics, Inc. All rights reserved. - */ - -/* - * External Cross Partition (XP) structures and defines. - */ - -#ifndef _DRIVERS_MISC_SGIXP_XP_H -#define _DRIVERS_MISC_SGIXP_XP_H - -#include - -#if defined CONFIG_X86_UV -#include -#endif - -#ifdef USE_DBUG_ON -#define DBUG_ON(condition) BUG_ON(condition) -#else -#define DBUG_ON(condition) -#endif - -/* - * Define the maximum number of partitions the system can possibly support. - * It is based on the maximum number of hardware partitionable regions. The - * term 'region' in this context refers to the minimum number of nodes that - * can comprise an access protection grouping. The access protection is in - * regards to memory, IPI and IOI. - * - * The maximum number of hardware partitionable regions is equal to the - * maximum number of nodes in the entire system divided by the minimum number - * of nodes that comprise an access protection grouping. - */ -#define XP_MAX_NPARTITIONS_SN2 64 -#define XP_MAX_NPARTITIONS_UV 256 - -/* - * XPC establishes channel connections between the local partition and any - * other partition that is currently up. Over these channels, kernel-level - * `users' can communicate with their counterparts on the other partitions. - * - * If the need for additional channels arises, one can simply increase - * XPC_MAX_NCHANNELS accordingly. If the day should come where that number - * exceeds the absolute MAXIMUM number of channels possible (eight), then one - * will need to make changes to the XPC code to accommodate for this. - * - * The absolute maximum number of channels possible is limited to eight for - * performance reasons on sn2 hardware. The internal cross partition structures - * require sixteen bytes per channel, and eight allows all of this - * interface-shared info to fit in one 128-byte cacheline. - */ -#define XPC_MEM_CHANNEL 0 /* memory channel number */ -#define XPC_NET_CHANNEL 1 /* network channel number */ - -#define XPC_MAX_NCHANNELS 2 /* max #of channels allowed */ - -#if XPC_MAX_NCHANNELS > 8 -#error XPC_MAX_NCHANNELS exceeds absolute MAXIMUM possible. -#endif - -/* - * Define macro, XPC_MSG_SIZE(), is provided for the user - * that wants to fit as many msg entries as possible in a given memory size - * (e.g. a memory page). - */ -#define XPC_MSG_MAX_SIZE 128 -#define XPC_MSG_HDR_MAX_SIZE 16 -#define XPC_MSG_PAYLOAD_MAX_SIZE (XPC_MSG_MAX_SIZE - XPC_MSG_HDR_MAX_SIZE) - -#define XPC_MSG_SIZE(_payload_size) \ - ALIGN(XPC_MSG_HDR_MAX_SIZE + (_payload_size), \ - is_uv_system() ? 64 : 128) - - -/* - * Define the return values and values passed to user's callout functions. - * (It is important to add new value codes at the end just preceding - * xpUnknownReason, which must have the highest numerical value.) - */ -enum xp_retval { - xpSuccess = 0, - - xpNotConnected, /* 1: channel is not connected */ - xpConnected, /* 2: channel connected (opened) */ - xpRETIRED1, /* 3: (formerly xpDisconnected) */ - - xpMsgReceived, /* 4: message received */ - xpMsgDelivered, /* 5: message delivered and acknowledged */ - - xpRETIRED2, /* 6: (formerly xpTransferFailed) */ - - xpNoWait, /* 7: operation would require wait */ - xpRetry, /* 8: retry operation */ - xpTimeout, /* 9: timeout in xpc_allocate_msg_wait() */ - xpInterrupted, /* 10: interrupted wait */ - - xpUnequalMsgSizes, /* 11: message size disparity between sides */ - xpInvalidAddress, /* 12: invalid address */ - - xpNoMemory, /* 13: no memory available for XPC structures */ - xpLackOfResources, /* 14: insufficient resources for operation */ - xpUnregistered, /* 15: channel is not registered */ - xpAlreadyRegistered, /* 16: channel is already registered */ - - xpPartitionDown, /* 17: remote partition is down */ - xpNotLoaded, /* 18: XPC module is not loaded */ - xpUnloading, /* 19: this side is unloading XPC module */ - - xpBadMagic, /* 20: XPC MAGIC string not found */ - - xpReactivating, /* 21: remote partition was reactivated */ - - xpUnregistering, /* 22: this side is unregistering channel */ - xpOtherUnregistering, /* 23: other side is unregistering channel */ - - xpCloneKThread, /* 24: cloning kernel thread */ - xpCloneKThreadFailed, /* 25: cloning kernel thread failed */ - - xpNoHeartbeat, /* 26: remote partition has no heartbeat */ - - xpPioReadError, /* 27: PIO read error */ - xpPhysAddrRegFailed, /* 28: registration of phys addr range failed */ - - xpRETIRED3, /* 29: (formerly xpBteDirectoryError) */ - xpRETIRED4, /* 30: (formerly xpBtePoisonError) */ - xpRETIRED5, /* 31: (formerly xpBteWriteError) */ - xpRETIRED6, /* 32: (formerly xpBteAccessError) */ - xpRETIRED7, /* 33: (formerly xpBtePWriteError) */ - xpRETIRED8, /* 34: (formerly xpBtePReadError) */ - xpRETIRED9, /* 35: (formerly xpBteTimeOutError) */ - xpRETIRED10, /* 36: (formerly xpBteXtalkError) */ - xpRETIRED11, /* 37: (formerly xpBteNotAvailable) */ - xpRETIRED12, /* 38: (formerly xpBteUnmappedError) */ - - xpBadVersion, /* 39: bad version number */ - xpVarsNotSet, /* 40: the XPC variables are not set up */ - xpNoRsvdPageAddr, /* 41: unable to get rsvd page's phys addr */ - xpInvalidPartid, /* 42: invalid partition ID */ - xpLocalPartid, /* 43: local partition ID */ - - xpOtherGoingDown, /* 44: other side going down, reason unknown */ - xpSystemGoingDown, /* 45: system is going down, reason unknown */ - xpSystemHalt, /* 46: system is being halted */ - xpSystemReboot, /* 47: system is being rebooted */ - xpSystemPoweroff, /* 48: system is being powered off */ - - xpDisconnecting, /* 49: channel disconnecting (closing) */ - - xpOpenCloseError, /* 50: channel open/close protocol error */ - - xpDisconnected, /* 51: channel disconnected (closed) */ - - xpBteCopyError, /* 52: bte_copy() returned error */ - xpSalError, /* 53: sn SAL error */ - xpRsvdPageNotSet, /* 54: the reserved page is not set up */ - xpPayloadTooBig, /* 55: payload too large for message slot */ - - xpUnsupported, /* 56: unsupported functionality or resource */ - xpNeedMoreInfo, /* 57: more info is needed by SAL */ - - xpGruCopyError, /* 58: gru_copy_gru() returned error */ - xpGruSendMqError, /* 59: gru send message queue related error */ - - xpBadChannelNumber, /* 60: invalid channel number */ - xpBadMsgType, /* 61: invalid message type */ - xpBiosError, /* 62: BIOS error */ - - xpUnknownReason /* 63: unknown reason - must be last in enum */ -}; - -/* - * Define the callout function type used by XPC to update the user on - * connection activity and state changes via the user function registered - * by xpc_connect(). - * - * Arguments: - * - * reason - reason code. - * partid - partition ID associated with condition. - * ch_number - channel # associated with condition. - * data - pointer to optional data. - * key - pointer to optional user-defined value provided as the "key" - * argument to xpc_connect(). - * - * A reason code of xpConnected indicates that a connection has been - * established to the specified partition on the specified channel. The data - * argument indicates the max number of entries allowed in the message queue. - * - * A reason code of xpMsgReceived indicates that a XPC message arrived from - * the specified partition on the specified channel. The data argument - * specifies the address of the message's payload. The user must call - * xpc_received() when finished with the payload. - * - * All other reason codes indicate failure. The data argmument is NULL. - * When a failure reason code is received, one can assume that the channel - * is not connected. - */ -typedef void (*xpc_channel_func) (enum xp_retval reason, short partid, - int ch_number, void *data, void *key); - -/* - * Define the callout function type used by XPC to notify the user of - * messages received and delivered via the user function registered by - * xpc_send_notify(). - * - * Arguments: - * - * reason - reason code. - * partid - partition ID associated with condition. - * ch_number - channel # associated with condition. - * key - pointer to optional user-defined value provided as the "key" - * argument to xpc_send_notify(). - * - * A reason code of xpMsgDelivered indicates that the message was delivered - * to the intended recipient and that they have acknowledged its receipt by - * calling xpc_received(). - * - * All other reason codes indicate failure. - * - * NOTE: The user defined function must be callable by an interrupt handler - * and thus cannot block. - */ -typedef void (*xpc_notify_func) (enum xp_retval reason, short partid, - int ch_number, void *key); - -/* - * The following is a registration entry. There is a global array of these, - * one per channel. It is used to record the connection registration made - * by the users of XPC. As long as a registration entry exists, for any - * partition that comes up, XPC will attempt to establish a connection on - * that channel. Notification that a connection has been made will occur via - * the xpc_channel_func function. - * - * The 'func' field points to the function to call when aynchronous - * notification is required for such events as: a connection established/lost, - * or an incoming message received, or an error condition encountered. A - * non-NULL 'func' field indicates that there is an active registration for - * the channel. - */ -struct xpc_registration { - struct mutex mutex; - xpc_channel_func func; /* function to call */ - void *key; /* pointer to user's key */ - u16 nentries; /* #of msg entries in local msg queue */ - u16 entry_size; /* message queue's message entry size */ - u32 assigned_limit; /* limit on #of assigned kthreads */ - u32 idle_limit; /* limit on #of idle kthreads */ -} ____cacheline_aligned; - -#define XPC_CHANNEL_REGISTERED(_c) (xpc_registrations[_c].func != NULL) - -/* the following are valid xpc_send() or xpc_send_notify() flags */ -#define XPC_WAIT 0 /* wait flag */ -#define XPC_NOWAIT 1 /* no wait flag */ - -struct xpc_interface { - void (*connect) (int); - void (*disconnect) (int); - enum xp_retval (*send) (short, int, u32, void *, u16); - enum xp_retval (*send_notify) (short, int, u32, void *, u16, - xpc_notify_func, void *); - void (*received) (short, int, void *); - enum xp_retval (*partid_to_nasids) (short, void *); -}; - -extern struct xpc_interface xpc_interface; - -extern void xpc_set_interface(void (*)(int), - void (*)(int), - enum xp_retval (*)(short, int, u32, void *, u16), - enum xp_retval (*)(short, int, u32, void *, u16, - xpc_notify_func, void *), - void (*)(short, int, void *), - enum xp_retval (*)(short, void *)); -extern void xpc_clear_interface(void); - -extern enum xp_retval xpc_connect(int, xpc_channel_func, void *, u16, - u16, u32, u32); -extern void xpc_disconnect(int); - -static inline enum xp_retval -xpc_send(short partid, int ch_number, u32 flags, void *payload, - u16 payload_size) -{ - if (!xpc_interface.send) - return xpNotLoaded; - - return xpc_interface.send(partid, ch_number, flags, payload, - payload_size); -} - -static inline enum xp_retval -xpc_send_notify(short partid, int ch_number, u32 flags, void *payload, - u16 payload_size, xpc_notify_func func, void *key) -{ - if (!xpc_interface.send_notify) - return xpNotLoaded; - - return xpc_interface.send_notify(partid, ch_number, flags, payload, - payload_size, func, key); -} - -static inline void -xpc_received(short partid, int ch_number, void *payload) -{ - if (xpc_interface.received) - xpc_interface.received(partid, ch_number, payload); -} - -static inline enum xp_retval -xpc_partid_to_nasids(short partid, void *nasids) -{ - if (!xpc_interface.partid_to_nasids) - return xpNotLoaded; - - return xpc_interface.partid_to_nasids(partid, nasids); -} - -extern short xp_max_npartitions; -extern short xp_partition_id; -extern u8 xp_region_size; - -extern unsigned long (*xp_pa) (void *); -extern unsigned long (*xp_socket_pa) (unsigned long); -extern enum xp_retval (*xp_remote_memcpy) (unsigned long, const unsigned long, - size_t); -extern int (*xp_cpu_to_nasid) (int); -extern enum xp_retval (*xp_expand_memprotect) (unsigned long, unsigned long); -extern enum xp_retval (*xp_restrict_memprotect) (unsigned long, unsigned long); - -extern struct device *xp; -extern enum xp_retval xp_init_uv(void); -extern void xp_exit_uv(void); - -#endif /* _DRIVERS_MISC_SGIXP_XP_H */ diff --git a/drivers/misc/sgi-xp/xp_main.c b/drivers/misc/sgi-xp/xp_main.c deleted file mode 100644 index 87d156c15f35..000000000000 --- a/drivers/misc/sgi-xp/xp_main.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition (XP) base. - * - * XP provides a base from which its users can interact - * with XPC, yet not be dependent on XPC. - * - */ - -#include -#include -#include "xp.h" - -/* define the XP debug device structures to be used with dev_dbg() et al */ - -static struct device_driver xp_dbg_name = { - .name = "xp" -}; - -static struct device xp_dbg_subname = { - .init_name = "", /* set to "" */ - .driver = &xp_dbg_name -}; - -struct device *xp = &xp_dbg_subname; - -/* max #of partitions possible */ -short xp_max_npartitions; -EXPORT_SYMBOL_GPL(xp_max_npartitions); - -short xp_partition_id; -EXPORT_SYMBOL_GPL(xp_partition_id); - -u8 xp_region_size; -EXPORT_SYMBOL_GPL(xp_region_size); - -unsigned long (*xp_pa) (void *addr); -EXPORT_SYMBOL_GPL(xp_pa); - -unsigned long (*xp_socket_pa) (unsigned long gpa); -EXPORT_SYMBOL_GPL(xp_socket_pa); - -enum xp_retval (*xp_remote_memcpy) (unsigned long dst_gpa, - const unsigned long src_gpa, size_t len); -EXPORT_SYMBOL_GPL(xp_remote_memcpy); - -int (*xp_cpu_to_nasid) (int cpuid); -EXPORT_SYMBOL_GPL(xp_cpu_to_nasid); - -enum xp_retval (*xp_expand_memprotect) (unsigned long phys_addr, - unsigned long size); -EXPORT_SYMBOL_GPL(xp_expand_memprotect); -enum xp_retval (*xp_restrict_memprotect) (unsigned long phys_addr, - unsigned long size); -EXPORT_SYMBOL_GPL(xp_restrict_memprotect); - -/* - * xpc_registrations[] keeps track of xpc_connect()'s done by the kernel-level - * users of XPC. - */ -struct xpc_registration xpc_registrations[XPC_MAX_NCHANNELS]; -EXPORT_SYMBOL_GPL(xpc_registrations); - -/* - * Initialize the XPC interface to NULL to indicate that XPC isn't loaded. - */ -struct xpc_interface xpc_interface = { }; -EXPORT_SYMBOL_GPL(xpc_interface); - -/* - * XPC calls this when it (the XPC module) has been loaded. - */ -void -xpc_set_interface(void (*connect) (int), - void (*disconnect) (int), - enum xp_retval (*send) (short, int, u32, void *, u16), - enum xp_retval (*send_notify) (short, int, u32, void *, u16, - xpc_notify_func, void *), - void (*received) (short, int, void *), - enum xp_retval (*partid_to_nasids) (short, void *)) -{ - xpc_interface.connect = connect; - xpc_interface.disconnect = disconnect; - xpc_interface.send = send; - xpc_interface.send_notify = send_notify; - xpc_interface.received = received; - xpc_interface.partid_to_nasids = partid_to_nasids; -} -EXPORT_SYMBOL_GPL(xpc_set_interface); - -/* - * XPC calls this when it (the XPC module) is being unloaded. - */ -void -xpc_clear_interface(void) -{ - memset(&xpc_interface, 0, sizeof(xpc_interface)); -} -EXPORT_SYMBOL_GPL(xpc_clear_interface); - -/* - * Register for automatic establishment of a channel connection whenever - * a partition comes up. - * - * Arguments: - * - * ch_number - channel # to register for connection. - * func - function to call for asynchronous notification of channel - * state changes (i.e., connection, disconnection, error) and - * the arrival of incoming messages. - * key - pointer to optional user-defined value that gets passed back - * to the user on any callouts made to func. - * payload_size - size in bytes of the XPC message's payload area which - * contains a user-defined message. The user should make - * this large enough to hold their largest message. - * nentries - max #of XPC message entries a message queue can contain. - * The actual number, which is determined when a connection - * is established and may be less then requested, will be - * passed to the user via the xpConnected callout. - * assigned_limit - max number of kthreads allowed to be processing - * messages (per connection) at any given instant. - * idle_limit - max number of kthreads allowed to be idle at any given - * instant. - */ -enum xp_retval -xpc_connect(int ch_number, xpc_channel_func func, void *key, u16 payload_size, - u16 nentries, u32 assigned_limit, u32 idle_limit) -{ - struct xpc_registration *registration; - - DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS); - DBUG_ON(payload_size == 0 || nentries == 0); - DBUG_ON(func == NULL); - DBUG_ON(assigned_limit == 0 || idle_limit > assigned_limit); - - if (XPC_MSG_SIZE(payload_size) > XPC_MSG_MAX_SIZE) - return xpPayloadTooBig; - - registration = &xpc_registrations[ch_number]; - - if (mutex_lock_interruptible(®istration->mutex) != 0) - return xpInterrupted; - - /* if XPC_CHANNEL_REGISTERED(ch_number) */ - if (registration->func != NULL) { - mutex_unlock(®istration->mutex); - return xpAlreadyRegistered; - } - - /* register the channel for connection */ - registration->entry_size = XPC_MSG_SIZE(payload_size); - registration->nentries = nentries; - registration->assigned_limit = assigned_limit; - registration->idle_limit = idle_limit; - registration->key = key; - registration->func = func; - - mutex_unlock(®istration->mutex); - - if (xpc_interface.connect) - xpc_interface.connect(ch_number); - - return xpSuccess; -} -EXPORT_SYMBOL_GPL(xpc_connect); - -/* - * Remove the registration for automatic connection of the specified channel - * when a partition comes up. - * - * Before returning this xpc_disconnect() will wait for all connections on the - * specified channel have been closed/torndown. So the caller can be assured - * that they will not be receiving any more callouts from XPC to their - * function registered via xpc_connect(). - * - * Arguments: - * - * ch_number - channel # to unregister. - */ -void -xpc_disconnect(int ch_number) -{ - struct xpc_registration *registration; - - DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS); - - registration = &xpc_registrations[ch_number]; - - /* - * We've decided not to make this a down_interruptible(), since we - * figured XPC's users will just turn around and call xpc_disconnect() - * again anyways, so we might as well wait, if need be. - */ - mutex_lock(®istration->mutex); - - /* if !XPC_CHANNEL_REGISTERED(ch_number) */ - if (registration->func == NULL) { - mutex_unlock(®istration->mutex); - return; - } - - /* remove the connection registration for the specified channel */ - registration->func = NULL; - registration->key = NULL; - registration->nentries = 0; - registration->entry_size = 0; - registration->assigned_limit = 0; - registration->idle_limit = 0; - - if (xpc_interface.disconnect) - xpc_interface.disconnect(ch_number); - - mutex_unlock(®istration->mutex); - - return; -} -EXPORT_SYMBOL_GPL(xpc_disconnect); - -static int __init -xp_init(void) -{ - enum xp_retval ret; - int ch_number; - - /* initialize the connection registration mutex */ - for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++) - mutex_init(&xpc_registrations[ch_number].mutex); - - if (is_uv_system()) - ret = xp_init_uv(); - else - ret = 0; - - if (ret != xpSuccess) - return ret; - - return 0; -} - -module_init(xp_init); - -static void __exit -xp_exit(void) -{ - if (is_uv_system()) - xp_exit_uv(); -} - -module_exit(xp_exit); - -MODULE_AUTHOR("Silicon Graphics, Inc."); -MODULE_DESCRIPTION("Cross Partition (XP) base"); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/sgi-xp/xp_uv.c b/drivers/misc/sgi-xp/xp_uv.c deleted file mode 100644 index 3faa7eadf679..000000000000 --- a/drivers/misc/sgi-xp/xp_uv.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition (XP) uv-based functions. - * - * Architecture specific implementation of common functions. - * - */ - -#include -#include -#if defined CONFIG_X86_64 -#include -#endif -#include "../sgi-gru/grukservices.h" -#include "xp.h" - -/* - * Convert a virtual memory address to a physical memory address. - */ -static unsigned long -xp_pa_uv(void *addr) -{ - return uv_gpa(addr); -} - -/* - * Convert a global physical to socket physical address. - */ -static unsigned long -xp_socket_pa_uv(unsigned long gpa) -{ - return uv_gpa_to_soc_phys_ram(gpa); -} - -static enum xp_retval -xp_remote_mmr_read(unsigned long dst_gpa, const unsigned long src_gpa, - size_t len) -{ - int ret; - unsigned long *dst_va = __va(uv_gpa_to_soc_phys_ram(dst_gpa)); - - BUG_ON(!uv_gpa_in_mmr_space(src_gpa)); - BUG_ON(len != 8); - - ret = gru_read_gpa(dst_va, src_gpa); - if (ret == 0) - return xpSuccess; - - dev_err(xp, "gru_read_gpa() failed, dst_gpa=0x%016lx src_gpa=0x%016lx " - "len=%ld\n", dst_gpa, src_gpa, len); - return xpGruCopyError; -} - - -static enum xp_retval -xp_remote_memcpy_uv(unsigned long dst_gpa, const unsigned long src_gpa, - size_t len) -{ - int ret; - - if (uv_gpa_in_mmr_space(src_gpa)) - return xp_remote_mmr_read(dst_gpa, src_gpa, len); - - ret = gru_copy_gpa(dst_gpa, src_gpa, len); - if (ret == 0) - return xpSuccess; - - dev_err(xp, "gru_copy_gpa() failed, dst_gpa=0x%016lx src_gpa=0x%016lx " - "len=%ld\n", dst_gpa, src_gpa, len); - return xpGruCopyError; -} - -static int -xp_cpu_to_nasid_uv(int cpuid) -{ - /* ??? Is this same as sn2 nasid in mach/part bitmaps set up by SAL? */ - return UV_PNODE_TO_NASID(uv_cpu_to_pnode(cpuid)); -} - -static enum xp_retval -xp_expand_memprotect_uv(unsigned long phys_addr, unsigned long size) -{ - int ret; - -#if defined CONFIG_X86_64 - ret = uv_bios_change_memprotect(phys_addr, size, UV_MEMPROT_ALLOW_RW); - if (ret != BIOS_STATUS_SUCCESS) { - dev_err(xp, "uv_bios_change_memprotect(,, " - "UV_MEMPROT_ALLOW_RW) failed, ret=%d\n", ret); - return xpBiosError; - } -#else - #error not a supported configuration -#endif - return xpSuccess; -} - -static enum xp_retval -xp_restrict_memprotect_uv(unsigned long phys_addr, unsigned long size) -{ - int ret; - -#if defined CONFIG_X86_64 - ret = uv_bios_change_memprotect(phys_addr, size, - UV_MEMPROT_RESTRICT_ACCESS); - if (ret != BIOS_STATUS_SUCCESS) { - dev_err(xp, "uv_bios_change_memprotect(,, " - "UV_MEMPROT_RESTRICT_ACCESS) failed, ret=%d\n", ret); - return xpBiosError; - } -#else - #error not a supported configuration -#endif - return xpSuccess; -} - -enum xp_retval -xp_init_uv(void) -{ - WARN_ON(!is_uv_system()); - if (!is_uv_system()) - return xpUnsupported; - - xp_max_npartitions = XP_MAX_NPARTITIONS_UV; -#ifdef CONFIG_X86 - xp_partition_id = sn_partition_id; - xp_region_size = sn_region_size; -#endif - xp_pa = xp_pa_uv; - xp_socket_pa = xp_socket_pa_uv; - xp_remote_memcpy = xp_remote_memcpy_uv; - xp_cpu_to_nasid = xp_cpu_to_nasid_uv; - xp_expand_memprotect = xp_expand_memprotect_uv; - xp_restrict_memprotect = xp_restrict_memprotect_uv; - - return xpSuccess; -} - -void -xp_exit_uv(void) -{ - WARN_ON(!is_uv_system()); -} diff --git a/drivers/misc/sgi-xp/xpc.h b/drivers/misc/sgi-xp/xpc.h deleted file mode 100644 index 225f2bb84e39..000000000000 --- a/drivers/misc/sgi-xp/xpc.h +++ /dev/null @@ -1,732 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition Communication (XPC) structures and macros. - */ - -#ifndef _DRIVERS_MISC_SGIXP_XPC_H -#define _DRIVERS_MISC_SGIXP_XPC_H - -#include -#include -#include -#include -#include "xp.h" - -/* - * XPC Version numbers consist of a major and minor number. XPC can always - * talk to versions with same major #, and never talk to versions with a - * different major #. - */ -#define _XPC_VERSION(_maj, _min) (((_maj) << 4) | ((_min) & 0xf)) -#define XPC_VERSION_MAJOR(_v) ((_v) >> 4) -#define XPC_VERSION_MINOR(_v) ((_v) & 0xf) - -/* define frequency of the heartbeat and frequency how often it's checked */ -#define XPC_HB_DEFAULT_INTERVAL 5 /* incr HB every x secs */ -#define XPC_HB_CHECK_DEFAULT_INTERVAL 20 /* check HB every x secs */ - -/* define the process name of HB checker and the CPU it is pinned to */ -#define XPC_HB_CHECK_THREAD_NAME "xpc_hb" -#define XPC_HB_CHECK_CPU 0 - -/* define the process name of the discovery thread */ -#define XPC_DISCOVERY_THREAD_NAME "xpc_discovery" - -/* - * the reserved page - * - * SAL reserves one page of memory per partition for XPC. Though a full page - * in length (16384 bytes), its starting address is not page aligned, but it - * is cacheline aligned. The reserved page consists of the following: - * - * reserved page header - * - * The first two 64-byte cachelines of the reserved page contain the - * header (struct xpc_rsvd_page). Before SAL initialization has completed, - * SAL has set up the following fields of the reserved page header: - * SAL_signature, SAL_version, SAL_partid, and SAL_nasids_size. The - * other fields are set up by XPC. (xpc_rsvd_page points to the local - * partition's reserved page.) - * - * part_nasids mask - * mach_nasids mask - * - * SAL also sets up two bitmaps (or masks), one that reflects the actual - * nasids in this partition (part_nasids), and the other that reflects - * the actual nasids in the entire machine (mach_nasids). We're only - * interested in the even numbered nasids (which contain the processors - * and/or memory), so we only need half as many bits to represent the - * nasids. When mapping nasid to bit in a mask (or bit to nasid) be sure - * to either divide or multiply by 2. The part_nasids mask is located - * starting at the first cacheline following the reserved page header. The - * mach_nasids mask follows right after the part_nasids mask. The size in - * bytes of each mask is reflected by the reserved page header field - * 'SAL_nasids_size'. (Local partition's mask pointers are xpc_part_nasids - * and xpc_mach_nasids.) - * - * Immediately following the mach_nasids mask are the XPC variables - * required by other partitions. First are those that are generic to all - * partitions (vars), followed on the next available cacheline by those - * which are partition specific (vars part). These are setup by XPC. - * - * Note: Until 'ts_jiffies' is set non-zero, the partition XPC code has not been - * initialized. - */ -struct xpc_rsvd_page { - u64 SAL_signature; /* SAL: unique signature */ - u64 SAL_version; /* SAL: version */ - short SAL_partid; /* SAL: partition ID */ - short max_npartitions; /* value of XPC_MAX_PARTITIONS */ - u8 version; - u8 pad1[3]; /* align to next u64 in 1st 64-byte cacheline */ - unsigned long ts_jiffies; /* timestamp when rsvd pg was setup by XPC */ - union { - struct { - unsigned long heartbeat_gpa; /* phys addr */ - unsigned long activate_gru_mq_desc_gpa; /* phys addr */ - } uv; - } sn; - u64 pad2[9]; /* align to last u64 in 2nd 64-byte cacheline */ - u64 SAL_nasids_size; /* SAL: size of each nasid mask in bytes */ -}; - -#define XPC_RP_VERSION _XPC_VERSION(3, 0) /* version 3.0 of the reserved page */ - -/* the reserved page sizes and offsets */ - -#define XPC_RP_HEADER_SIZE L1_CACHE_ALIGN(sizeof(struct xpc_rsvd_page)) - -#define XPC_RP_PART_NASIDS(_rp) ((unsigned long *)((u8 *)(_rp) + \ - XPC_RP_HEADER_SIZE)) -#define XPC_RP_MACH_NASIDS(_rp) (XPC_RP_PART_NASIDS(_rp) + \ - xpc_nasid_mask_nlongs) - - -/* - * The following structure describes the partition's heartbeat info which - * will be periodically read by other partitions to determine whether this - * XPC is still 'alive'. - */ -struct xpc_heartbeat_uv { - unsigned long value; - unsigned long offline; /* if 0, heartbeat should be changing */ -}; - -/* - * Info pertinent to a GRU message queue using a watch list for irq generation. - */ -struct xpc_gru_mq_uv { - void *address; /* address of GRU message queue */ - unsigned int order; /* size of GRU message queue as a power of 2 */ - int irq; /* irq raised when message is received in mq */ - int mmr_blade; /* blade where watchlist was allocated from */ - unsigned long mmr_offset; /* offset of irq mmr located on mmr_blade */ - unsigned long mmr_value; /* value of irq mmr located on mmr_blade */ - int watchlist_num; /* number of watchlist allocatd by BIOS */ - void *gru_mq_desc; /* opaque structure used by the GRU driver */ -}; - -/* - * The activate_mq is used to send/receive GRU messages that affect XPC's - * partition active state and channel state. This is uv only. - */ -struct xpc_activate_mq_msghdr_uv { - unsigned int gru_msg_hdr; /* FOR GRU INTERNAL USE ONLY */ - short partid; /* sender's partid */ - u8 act_state; /* sender's act_state at time msg sent */ - u8 type; /* message's type */ - unsigned long rp_ts_jiffies; /* timestamp of sender's rp setup by XPC */ -}; - -/* activate_mq defined message types */ -#define XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV 0 - -#define XPC_ACTIVATE_MQ_MSG_ACTIVATE_REQ_UV 1 -#define XPC_ACTIVATE_MQ_MSG_DEACTIVATE_REQ_UV 2 - -#define XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREQUEST_UV 3 -#define XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREPLY_UV 4 -#define XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREQUEST_UV 5 -#define XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREPLY_UV 6 -#define XPC_ACTIVATE_MQ_MSG_CHCTL_OPENCOMPLETE_UV 7 - -#define XPC_ACTIVATE_MQ_MSG_MARK_ENGAGED_UV 8 -#define XPC_ACTIVATE_MQ_MSG_MARK_DISENGAGED_UV 9 - -struct xpc_activate_mq_msg_uv { - struct xpc_activate_mq_msghdr_uv hdr; -}; - -struct xpc_activate_mq_msg_activate_req_uv { - struct xpc_activate_mq_msghdr_uv hdr; - unsigned long rp_gpa; - unsigned long heartbeat_gpa; - unsigned long activate_gru_mq_desc_gpa; -}; - -struct xpc_activate_mq_msg_deactivate_req_uv { - struct xpc_activate_mq_msghdr_uv hdr; - enum xp_retval reason; -}; - -struct xpc_activate_mq_msg_chctl_closerequest_uv { - struct xpc_activate_mq_msghdr_uv hdr; - short ch_number; - enum xp_retval reason; -}; - -struct xpc_activate_mq_msg_chctl_closereply_uv { - struct xpc_activate_mq_msghdr_uv hdr; - short ch_number; -}; - -struct xpc_activate_mq_msg_chctl_openrequest_uv { - struct xpc_activate_mq_msghdr_uv hdr; - short ch_number; - short entry_size; /* size of notify_mq's GRU messages */ - short local_nentries; /* ??? Is this needed? What is? */ -}; - -struct xpc_activate_mq_msg_chctl_openreply_uv { - struct xpc_activate_mq_msghdr_uv hdr; - short ch_number; - short remote_nentries; /* ??? Is this needed? What is? */ - short local_nentries; /* ??? Is this needed? What is? */ - unsigned long notify_gru_mq_desc_gpa; -}; - -struct xpc_activate_mq_msg_chctl_opencomplete_uv { - struct xpc_activate_mq_msghdr_uv hdr; - short ch_number; -}; - -/* - * Functions registered by add_timer() or called by kernel_thread() only - * allow for a single 64-bit argument. The following macros can be used to - * pack and unpack two (32-bit, 16-bit or 8-bit) arguments into or out from - * the passed argument. - */ -#define XPC_PACK_ARGS(_arg1, _arg2) \ - ((((u64)_arg1) & 0xffffffff) | \ - ((((u64)_arg2) & 0xffffffff) << 32)) - -#define XPC_UNPACK_ARG1(_args) (((u64)_args) & 0xffffffff) -#define XPC_UNPACK_ARG2(_args) ((((u64)_args) >> 32) & 0xffffffff) - -/* - * Define a structure that contains arguments associated with opening and - * closing a channel. - */ -struct xpc_openclose_args { - u16 reason; /* reason why channel is closing */ - u16 entry_size; /* sizeof each message entry */ - u16 remote_nentries; /* #of message entries in remote msg queue */ - u16 local_nentries; /* #of message entries in local msg queue */ - unsigned long local_msgqueue_pa; /* phys addr of local message queue */ -}; - -#define XPC_OPENCLOSE_ARGS_SIZE \ - L1_CACHE_ALIGN(sizeof(struct xpc_openclose_args) * \ - XPC_MAX_NCHANNELS) - - -/* - * Structures to define a fifo singly-linked list. - */ - -struct xpc_fifo_entry_uv { - struct xpc_fifo_entry_uv *next; -}; - -struct xpc_fifo_head_uv { - struct xpc_fifo_entry_uv *first; - struct xpc_fifo_entry_uv *last; - spinlock_t lock; - int n_entries; -}; - -/* - * The format of a uv XPC notify_mq GRU message is as follows: - * - * A user-defined message resides in the payload area. The max size of the - * payload is defined by the user via xpc_connect(). - * - * The size of a message (payload and header) sent via the GRU must be either 1 - * or 2 GRU_CACHE_LINE_BYTES in length. - */ - -struct xpc_notify_mq_msghdr_uv { - union { - unsigned int gru_msg_hdr; /* FOR GRU INTERNAL USE ONLY */ - struct xpc_fifo_entry_uv next; /* FOR XPC INTERNAL USE ONLY */ - } u; - short partid; /* FOR XPC INTERNAL USE ONLY */ - u8 ch_number; /* FOR XPC INTERNAL USE ONLY */ - u8 size; /* FOR XPC INTERNAL USE ONLY */ - unsigned int msg_slot_number; /* FOR XPC INTERNAL USE ONLY */ -}; - -struct xpc_notify_mq_msg_uv { - struct xpc_notify_mq_msghdr_uv hdr; - unsigned long payload; -}; - -/* struct xpc_notify_sn2 type of notification */ - -#define XPC_N_CALL 0x01 /* notify function provided by user */ - -/* - * Define uv's version of the notify entry. It additionally is used to allocate - * a msg slot on the remote partition into which is copied a sent message. - */ -struct xpc_send_msg_slot_uv { - struct xpc_fifo_entry_uv next; - unsigned int msg_slot_number; - xpc_notify_func func; /* user's notify function */ - void *key; /* pointer to user's key */ -}; - -/* - * Define the structure that manages all the stuff required by a channel. In - * particular, they are used to manage the messages sent across the channel. - * - * This structure is private to a partition, and is NOT shared across the - * partition boundary. - * - * There is an array of these structures for each remote partition. It is - * allocated at the time a partition becomes active. The array contains one - * of these structures for each potential channel connection to that partition. - */ - -struct xpc_channel_uv { - void *cached_notify_gru_mq_desc; /* remote partition's notify mq's */ - /* gru mq descriptor */ - - struct xpc_send_msg_slot_uv *send_msg_slots; - void *recv_msg_slots; /* each slot will hold a xpc_notify_mq_msg_uv */ - /* structure plus the user's payload */ - - struct xpc_fifo_head_uv msg_slot_free_list; - struct xpc_fifo_head_uv recv_msg_list; /* deliverable payloads */ -}; - -struct xpc_channel { - short partid; /* ID of remote partition connected */ - spinlock_t lock; /* lock for updating this structure */ - unsigned int flags; /* general flags */ - - enum xp_retval reason; /* reason why channel is disconnect'g */ - int reason_line; /* line# disconnect initiated from */ - - u16 number; /* channel # */ - - u16 entry_size; /* sizeof each msg entry */ - u16 local_nentries; /* #of msg entries in local msg queue */ - u16 remote_nentries; /* #of msg entries in remote msg queue */ - - atomic_t references; /* #of external references to queues */ - - atomic_t n_on_msg_allocate_wq; /* #on msg allocation wait queue */ - wait_queue_head_t msg_allocate_wq; /* msg allocation wait queue */ - - u8 delayed_chctl_flags; /* chctl flags received, but delayed */ - /* action until channel disconnected */ - - atomic_t n_to_notify; /* #of msg senders to notify */ - - xpc_channel_func func; /* user's channel function */ - void *key; /* pointer to user's key */ - - struct completion wdisconnect_wait; /* wait for channel disconnect */ - - /* kthread management related fields */ - - atomic_t kthreads_assigned; /* #of kthreads assigned to channel */ - u32 kthreads_assigned_limit; /* limit on #of kthreads assigned */ - atomic_t kthreads_idle; /* #of kthreads idle waiting for work */ - u32 kthreads_idle_limit; /* limit on #of kthreads idle */ - atomic_t kthreads_active; /* #of kthreads actively working */ - - wait_queue_head_t idle_wq; /* idle kthread wait queue */ - - union { - struct xpc_channel_uv uv; - } sn; - -} ____cacheline_aligned; - -/* struct xpc_channel flags */ - -#define XPC_C_WASCONNECTED 0x00000001 /* channel was connected */ - -#define XPC_C_ROPENCOMPLETE 0x00000002 /* remote open channel complete */ -#define XPC_C_OPENCOMPLETE 0x00000004 /* local open channel complete */ -#define XPC_C_ROPENREPLY 0x00000008 /* remote open channel reply */ -#define XPC_C_OPENREPLY 0x00000010 /* local open channel reply */ -#define XPC_C_ROPENREQUEST 0x00000020 /* remote open channel request */ -#define XPC_C_OPENREQUEST 0x00000040 /* local open channel request */ - -#define XPC_C_SETUP 0x00000080 /* channel's msgqueues are alloc'd */ -#define XPC_C_CONNECTEDCALLOUT 0x00000100 /* connected callout initiated */ -#define XPC_C_CONNECTEDCALLOUT_MADE \ - 0x00000200 /* connected callout completed */ -#define XPC_C_CONNECTED 0x00000400 /* local channel is connected */ -#define XPC_C_CONNECTING 0x00000800 /* channel is being connected */ - -#define XPC_C_RCLOSEREPLY 0x00001000 /* remote close channel reply */ -#define XPC_C_CLOSEREPLY 0x00002000 /* local close channel reply */ -#define XPC_C_RCLOSEREQUEST 0x00004000 /* remote close channel request */ -#define XPC_C_CLOSEREQUEST 0x00008000 /* local close channel request */ - -#define XPC_C_DISCONNECTED 0x00010000 /* channel is disconnected */ -#define XPC_C_DISCONNECTING 0x00020000 /* channel is being disconnected */ -#define XPC_C_DISCONNECTINGCALLOUT \ - 0x00040000 /* disconnecting callout initiated */ -#define XPC_C_DISCONNECTINGCALLOUT_MADE \ - 0x00080000 /* disconnecting callout completed */ -#define XPC_C_WDISCONNECT 0x00100000 /* waiting for channel disconnect */ - -/* - * The channel control flags (chctl) union consists of a 64-bit variable which - * is divided up into eight bytes, ordered from right to left. Byte zero - * pertains to channel 0, byte one to channel 1, and so on. Each channel's byte - * can have one or more of the chctl flags set in it. - */ - -union xpc_channel_ctl_flags { - u64 all_flags; - u8 flags[XPC_MAX_NCHANNELS]; -}; - -/* chctl flags */ -#define XPC_CHCTL_CLOSEREQUEST 0x01 -#define XPC_CHCTL_CLOSEREPLY 0x02 -#define XPC_CHCTL_OPENREQUEST 0x04 -#define XPC_CHCTL_OPENREPLY 0x08 -#define XPC_CHCTL_OPENCOMPLETE 0x10 -#define XPC_CHCTL_MSGREQUEST 0x20 - -#define XPC_OPENCLOSE_CHCTL_FLAGS \ - (XPC_CHCTL_CLOSEREQUEST | XPC_CHCTL_CLOSEREPLY | \ - XPC_CHCTL_OPENREQUEST | XPC_CHCTL_OPENREPLY | \ - XPC_CHCTL_OPENCOMPLETE) -#define XPC_MSG_CHCTL_FLAGS XPC_CHCTL_MSGREQUEST - -static inline int -xpc_any_openclose_chctl_flags_set(union xpc_channel_ctl_flags *chctl) -{ - int ch_number; - - for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++) { - if (chctl->flags[ch_number] & XPC_OPENCLOSE_CHCTL_FLAGS) - return 1; - } - return 0; -} - -static inline int -xpc_any_msg_chctl_flags_set(union xpc_channel_ctl_flags *chctl) -{ - int ch_number; - - for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++) { - if (chctl->flags[ch_number] & XPC_MSG_CHCTL_FLAGS) - return 1; - } - return 0; -} - -struct xpc_partition_uv { - unsigned long heartbeat_gpa; /* phys addr of partition's heartbeat */ - struct xpc_heartbeat_uv cached_heartbeat; /* cached copy of */ - /* partition's heartbeat */ - unsigned long activate_gru_mq_desc_gpa; /* phys addr of parititon's */ - /* activate mq's gru mq */ - /* descriptor */ - void *cached_activate_gru_mq_desc; /* cached copy of partition's */ - /* activate mq's gru mq descriptor */ - struct mutex cached_activate_gru_mq_desc_mutex; - spinlock_t flags_lock; /* protect updating of flags */ - unsigned int flags; /* general flags */ - u8 remote_act_state; /* remote partition's act_state */ - u8 act_state_req; /* act_state request from remote partition */ - enum xp_retval reason; /* reason for deactivate act_state request */ -}; - -/* struct xpc_partition_uv flags */ - -#define XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV 0x00000001 -#define XPC_P_ENGAGED_UV 0x00000002 - -/* struct xpc_partition_uv act_state change requests */ - -#define XPC_P_ASR_ACTIVATE_UV 0x01 -#define XPC_P_ASR_REACTIVATE_UV 0x02 -#define XPC_P_ASR_DEACTIVATE_UV 0x03 - -struct xpc_partition { - - /* XPC HB infrastructure */ - - u8 remote_rp_version; /* version# of partition's rsvd pg */ - unsigned long remote_rp_ts_jiffies; /* timestamp when rsvd pg setup */ - unsigned long remote_rp_pa; /* phys addr of partition's rsvd pg */ - u64 last_heartbeat; /* HB at last read */ - u32 activate_IRQ_rcvd; /* IRQs since activation */ - spinlock_t act_lock; /* protect updating of act_state */ - u8 act_state; /* from XPC HB viewpoint */ - enum xp_retval reason; /* reason partition is deactivating */ - int reason_line; /* line# deactivation initiated from */ - - unsigned long disengage_timeout; /* timeout in jiffies */ - struct timer_list disengage_timer; - - /* XPC infrastructure referencing and teardown control */ - - u8 setup_state; /* infrastructure setup state */ - wait_queue_head_t teardown_wq; /* kthread waiting to teardown infra */ - atomic_t references; /* #of references to infrastructure */ - - u8 nchannels; /* #of defined channels supported */ - atomic_t nchannels_active; /* #of channels that are not DISCONNECTED */ - atomic_t nchannels_engaged; /* #of channels engaged with remote part */ - struct xpc_channel *channels; /* array of channel structures */ - - /* fields used for managing channel avialability and activity */ - - union xpc_channel_ctl_flags chctl; /* chctl flags yet to be processed */ - spinlock_t chctl_lock; /* chctl flags lock */ - - void *remote_openclose_args_base; /* base address of kmalloc'd space */ - struct xpc_openclose_args *remote_openclose_args; /* copy of remote's */ - /* args */ - - /* channel manager related fields */ - - atomic_t channel_mgr_requests; /* #of requests to activate chan mgr */ - wait_queue_head_t channel_mgr_wq; /* channel mgr's wait queue */ - - union { - struct xpc_partition_uv uv; - } sn; - -} ____cacheline_aligned; - -struct xpc_arch_operations { - int (*setup_partitions) (void); - void (*teardown_partitions) (void); - void (*process_activate_IRQ_rcvd) (void); - enum xp_retval (*get_partition_rsvd_page_pa) - (void *, u64 *, unsigned long *, size_t *); - int (*setup_rsvd_page) (struct xpc_rsvd_page *); - - void (*allow_hb) (short); - void (*disallow_hb) (short); - void (*disallow_all_hbs) (void); - void (*increment_heartbeat) (void); - void (*offline_heartbeat) (void); - void (*online_heartbeat) (void); - void (*heartbeat_init) (void); - void (*heartbeat_exit) (void); - enum xp_retval (*get_remote_heartbeat) (struct xpc_partition *); - - void (*request_partition_activation) (struct xpc_rsvd_page *, - unsigned long, int); - void (*request_partition_reactivation) (struct xpc_partition *); - void (*request_partition_deactivation) (struct xpc_partition *); - void (*cancel_partition_deactivation_request) (struct xpc_partition *); - enum xp_retval (*setup_ch_structures) (struct xpc_partition *); - void (*teardown_ch_structures) (struct xpc_partition *); - - enum xp_retval (*make_first_contact) (struct xpc_partition *); - - u64 (*get_chctl_all_flags) (struct xpc_partition *); - void (*send_chctl_closerequest) (struct xpc_channel *, unsigned long *); - void (*send_chctl_closereply) (struct xpc_channel *, unsigned long *); - void (*send_chctl_openrequest) (struct xpc_channel *, unsigned long *); - void (*send_chctl_openreply) (struct xpc_channel *, unsigned long *); - void (*send_chctl_opencomplete) (struct xpc_channel *, unsigned long *); - void (*process_msg_chctl_flags) (struct xpc_partition *, int); - - enum xp_retval (*save_remote_msgqueue_pa) (struct xpc_channel *, - unsigned long); - - enum xp_retval (*setup_msg_structures) (struct xpc_channel *); - void (*teardown_msg_structures) (struct xpc_channel *); - - void (*indicate_partition_engaged) (struct xpc_partition *); - void (*indicate_partition_disengaged) (struct xpc_partition *); - void (*assume_partition_disengaged) (short); - int (*partition_engaged) (short); - int (*any_partition_engaged) (void); - - int (*n_of_deliverable_payloads) (struct xpc_channel *); - enum xp_retval (*send_payload) (struct xpc_channel *, u32, void *, - u16, u8, xpc_notify_func, void *); - void *(*get_deliverable_payload) (struct xpc_channel *); - void (*received_payload) (struct xpc_channel *, void *); - void (*notify_senders_of_disconnect) (struct xpc_channel *); -}; - -/* struct xpc_partition act_state values (for XPC HB) */ - -#define XPC_P_AS_INACTIVE 0x00 /* partition is not active */ -#define XPC_P_AS_ACTIVATION_REQ 0x01 /* created thread to activate */ -#define XPC_P_AS_ACTIVATING 0x02 /* activation thread started */ -#define XPC_P_AS_ACTIVE 0x03 /* xpc_partition_up() was called */ -#define XPC_P_AS_DEACTIVATING 0x04 /* partition deactivation initiated */ - -#define XPC_DEACTIVATE_PARTITION(_p, _reason) \ - xpc_deactivate_partition(__LINE__, (_p), (_reason)) - -/* struct xpc_partition setup_state values */ - -#define XPC_P_SS_UNSET 0x00 /* infrastructure was never setup */ -#define XPC_P_SS_SETUP 0x01 /* infrastructure is setup */ -#define XPC_P_SS_WTEARDOWN 0x02 /* waiting to teardown infrastructure */ -#define XPC_P_SS_TORNDOWN 0x03 /* infrastructure is torndown */ - -/* number of seconds to wait for other partitions to disengage */ -#define XPC_DISENGAGE_DEFAULT_TIMELIMIT 90 - -/* interval in seconds to print 'waiting deactivation' messages */ -#define XPC_DEACTIVATE_PRINTMSG_INTERVAL 10 - -#define XPC_PARTID(_p) ((short)((_p) - &xpc_partitions[0])) - -/* found in xp_main.c */ -extern struct xpc_registration xpc_registrations[]; - -/* found in xpc_main.c */ -extern struct device *xpc_part; -extern struct device *xpc_chan; -extern struct xpc_arch_operations xpc_arch_ops; -extern int xpc_disengage_timelimit; -extern int xpc_disengage_timedout; -extern int xpc_activate_IRQ_rcvd; -extern spinlock_t xpc_activate_IRQ_rcvd_lock; -extern wait_queue_head_t xpc_activate_IRQ_wq; -extern void *xpc_kzalloc_cacheline_aligned(size_t, gfp_t, void **); -extern void xpc_activate_partition(struct xpc_partition *); -extern void xpc_activate_kthreads(struct xpc_channel *, int); -extern void xpc_create_kthreads(struct xpc_channel *, int, int); -extern void xpc_disconnect_wait(int); - -/* found in xpc_uv.c */ -extern int xpc_init_uv(void); -extern void xpc_exit_uv(void); - -/* found in xpc_partition.c */ -extern int xpc_exiting; -extern int xpc_nasid_mask_nlongs; -extern struct xpc_rsvd_page *xpc_rsvd_page; -extern unsigned long *xpc_mach_nasids; -extern struct xpc_partition *xpc_partitions; -extern void *xpc_kmalloc_cacheline_aligned(size_t, gfp_t, void **); -extern int xpc_setup_rsvd_page(void); -extern void xpc_teardown_rsvd_page(void); -extern int xpc_identify_activate_IRQ_sender(void); -extern int xpc_partition_disengaged(struct xpc_partition *); -extern int xpc_partition_disengaged_from_timer(struct xpc_partition *part); -extern enum xp_retval xpc_mark_partition_active(struct xpc_partition *); -extern void xpc_mark_partition_inactive(struct xpc_partition *); -extern void xpc_discovery(void); -extern enum xp_retval xpc_get_remote_rp(int, unsigned long *, - struct xpc_rsvd_page *, - unsigned long *); -extern void xpc_deactivate_partition(const int, struct xpc_partition *, - enum xp_retval); -extern enum xp_retval xpc_initiate_partid_to_nasids(short, void *); - -/* found in xpc_channel.c */ -extern void xpc_initiate_connect(int); -extern void xpc_initiate_disconnect(int); -extern enum xp_retval xpc_allocate_msg_wait(struct xpc_channel *); -extern enum xp_retval xpc_initiate_send(short, int, u32, void *, u16); -extern enum xp_retval xpc_initiate_send_notify(short, int, u32, void *, u16, - xpc_notify_func, void *); -extern void xpc_initiate_received(short, int, void *); -extern void xpc_process_sent_chctl_flags(struct xpc_partition *); -extern void xpc_connected_callout(struct xpc_channel *); -extern void xpc_deliver_payload(struct xpc_channel *); -extern void xpc_disconnect_channel(const int, struct xpc_channel *, - enum xp_retval, unsigned long *); -extern void xpc_disconnect_callout(struct xpc_channel *, enum xp_retval); -extern void xpc_partition_going_down(struct xpc_partition *, enum xp_retval); - -static inline void -xpc_wakeup_channel_mgr(struct xpc_partition *part) -{ - if (atomic_inc_return(&part->channel_mgr_requests) == 1) - wake_up(&part->channel_mgr_wq); -} - -/* - * These next two inlines are used to keep us from tearing down a channel's - * msg queues while a thread may be referencing them. - */ -static inline void -xpc_msgqueue_ref(struct xpc_channel *ch) -{ - atomic_inc(&ch->references); -} - -static inline void -xpc_msgqueue_deref(struct xpc_channel *ch) -{ - s32 refs = atomic_dec_return(&ch->references); - - DBUG_ON(refs < 0); - if (refs == 0) - xpc_wakeup_channel_mgr(&xpc_partitions[ch->partid]); -} - -#define XPC_DISCONNECT_CHANNEL(_ch, _reason, _irqflgs) \ - xpc_disconnect_channel(__LINE__, _ch, _reason, _irqflgs) - -/* - * These two inlines are used to keep us from tearing down a partition's - * setup infrastructure while a thread may be referencing it. - */ -static inline void -xpc_part_deref(struct xpc_partition *part) -{ - s32 refs = atomic_dec_return(&part->references); - - DBUG_ON(refs < 0); - if (refs == 0 && part->setup_state == XPC_P_SS_WTEARDOWN) - wake_up(&part->teardown_wq); -} - -static inline int -xpc_part_ref(struct xpc_partition *part) -{ - int setup; - - atomic_inc(&part->references); - setup = (part->setup_state == XPC_P_SS_SETUP); - if (!setup) - xpc_part_deref(part); - - return setup; -} - -/* - * The following macro is to be used for the setting of the reason and - * reason_line fields in both the struct xpc_channel and struct xpc_partition - * structures. - */ -#define XPC_SET_REASON(_p, _reason, _line) \ - { \ - (_p)->reason = _reason; \ - (_p)->reason_line = _line; \ - } - -#endif /* _DRIVERS_MISC_SGIXP_XPC_H */ diff --git a/drivers/misc/sgi-xp/xpc_channel.c b/drivers/misc/sgi-xp/xpc_channel.c deleted file mode 100644 index 8e6607fc8a67..000000000000 --- a/drivers/misc/sgi-xp/xpc_channel.c +++ /dev/null @@ -1,1011 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition Communication (XPC) channel support. - * - * This is the part of XPC that manages the channels and - * sends/receives messages across them to/from other partitions. - * - */ - -#include -#include "xpc.h" - -/* - * Process a connect message from a remote partition. - * - * Note: xpc_process_connect() is expecting to be called with the - * spin_lock_irqsave held and will leave it locked upon return. - */ -static void -xpc_process_connect(struct xpc_channel *ch, unsigned long *irq_flags) -{ - enum xp_retval ret; - - lockdep_assert_held(&ch->lock); - - if (!(ch->flags & XPC_C_OPENREQUEST) || - !(ch->flags & XPC_C_ROPENREQUEST)) { - /* nothing more to do for now */ - return; - } - DBUG_ON(!(ch->flags & XPC_C_CONNECTING)); - - if (!(ch->flags & XPC_C_SETUP)) { - spin_unlock_irqrestore(&ch->lock, *irq_flags); - ret = xpc_arch_ops.setup_msg_structures(ch); - spin_lock_irqsave(&ch->lock, *irq_flags); - - if (ret != xpSuccess) - XPC_DISCONNECT_CHANNEL(ch, ret, irq_flags); - else - ch->flags |= XPC_C_SETUP; - - if (ch->flags & XPC_C_DISCONNECTING) - return; - } - - if (!(ch->flags & XPC_C_OPENREPLY)) { - ch->flags |= XPC_C_OPENREPLY; - xpc_arch_ops.send_chctl_openreply(ch, irq_flags); - } - - if (!(ch->flags & XPC_C_ROPENREPLY)) - return; - - if (!(ch->flags & XPC_C_OPENCOMPLETE)) { - ch->flags |= (XPC_C_OPENCOMPLETE | XPC_C_CONNECTED); - xpc_arch_ops.send_chctl_opencomplete(ch, irq_flags); - } - - if (!(ch->flags & XPC_C_ROPENCOMPLETE)) - return; - - dev_info(xpc_chan, "channel %d to partition %d connected\n", - ch->number, ch->partid); - - ch->flags = (XPC_C_CONNECTED | XPC_C_SETUP); /* clear all else */ -} - -/* - * spin_lock_irqsave() is expected to be held on entry. - */ -static void -xpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_partition *part = &xpc_partitions[ch->partid]; - u32 channel_was_connected = (ch->flags & XPC_C_WASCONNECTED); - - lockdep_assert_held(&ch->lock); - - if (!(ch->flags & XPC_C_DISCONNECTING)) - return; - - DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); - - /* make sure all activity has settled down first */ - - if (atomic_read(&ch->kthreads_assigned) > 0 || - atomic_read(&ch->references) > 0) { - return; - } - DBUG_ON((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) && - !(ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE)); - - if (part->act_state == XPC_P_AS_DEACTIVATING) { - /* can't proceed until the other side disengages from us */ - if (xpc_arch_ops.partition_engaged(ch->partid)) - return; - - } else { - - /* as long as the other side is up do the full protocol */ - - if (!(ch->flags & XPC_C_RCLOSEREQUEST)) - return; - - if (!(ch->flags & XPC_C_CLOSEREPLY)) { - ch->flags |= XPC_C_CLOSEREPLY; - xpc_arch_ops.send_chctl_closereply(ch, irq_flags); - } - - if (!(ch->flags & XPC_C_RCLOSEREPLY)) - return; - } - - /* wake those waiting for notify completion */ - if (atomic_read(&ch->n_to_notify) > 0) { - /* we do callout while holding ch->lock, callout can't block */ - xpc_arch_ops.notify_senders_of_disconnect(ch); - } - - /* both sides are disconnected now */ - - if (ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE) { - spin_unlock_irqrestore(&ch->lock, *irq_flags); - xpc_disconnect_callout(ch, xpDisconnected); - spin_lock_irqsave(&ch->lock, *irq_flags); - } - - DBUG_ON(atomic_read(&ch->n_to_notify) != 0); - - /* it's now safe to free the channel's message queues */ - xpc_arch_ops.teardown_msg_structures(ch); - - ch->func = NULL; - ch->key = NULL; - ch->entry_size = 0; - ch->local_nentries = 0; - ch->remote_nentries = 0; - ch->kthreads_assigned_limit = 0; - ch->kthreads_idle_limit = 0; - - /* - * Mark the channel disconnected and clear all other flags, including - * XPC_C_SETUP (because of call to - * xpc_arch_ops.teardown_msg_structures()) but not including - * XPC_C_WDISCONNECT (if it was set). - */ - ch->flags = (XPC_C_DISCONNECTED | (ch->flags & XPC_C_WDISCONNECT)); - - atomic_dec(&part->nchannels_active); - - if (channel_was_connected) { - dev_info(xpc_chan, "channel %d to partition %d disconnected, " - "reason=%d\n", ch->number, ch->partid, ch->reason); - } - - if (ch->flags & XPC_C_WDISCONNECT) { - /* we won't lose the CPU since we're holding ch->lock */ - complete(&ch->wdisconnect_wait); - } else if (ch->delayed_chctl_flags) { - if (part->act_state != XPC_P_AS_DEACTIVATING) { - /* time to take action on any delayed chctl flags */ - spin_lock(&part->chctl_lock); - part->chctl.flags[ch->number] |= - ch->delayed_chctl_flags; - spin_unlock(&part->chctl_lock); - } - ch->delayed_chctl_flags = 0; - } -} - -/* - * Process a change in the channel's remote connection state. - */ -static void -xpc_process_openclose_chctl_flags(struct xpc_partition *part, int ch_number, - u8 chctl_flags) -{ - unsigned long irq_flags; - struct xpc_openclose_args *args = - &part->remote_openclose_args[ch_number]; - struct xpc_channel *ch = &part->channels[ch_number]; - enum xp_retval reason; - enum xp_retval ret; - int create_kthread = 0; - - spin_lock_irqsave(&ch->lock, irq_flags); - -again: - - if ((ch->flags & XPC_C_DISCONNECTED) && - (ch->flags & XPC_C_WDISCONNECT)) { - /* - * Delay processing chctl flags until thread waiting disconnect - * has had a chance to see that the channel is disconnected. - */ - ch->delayed_chctl_flags |= chctl_flags; - goto out; - } - - if (chctl_flags & XPC_CHCTL_CLOSEREQUEST) { - - dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREQUEST (reason=%d) received " - "from partid=%d, channel=%d\n", args->reason, - ch->partid, ch->number); - - /* - * If RCLOSEREQUEST is set, we're probably waiting for - * RCLOSEREPLY. We should find it and a ROPENREQUEST packed - * with this RCLOSEREQUEST in the chctl_flags. - */ - - if (ch->flags & XPC_C_RCLOSEREQUEST) { - DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING)); - DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); - DBUG_ON(!(ch->flags & XPC_C_CLOSEREPLY)); - DBUG_ON(ch->flags & XPC_C_RCLOSEREPLY); - - DBUG_ON(!(chctl_flags & XPC_CHCTL_CLOSEREPLY)); - chctl_flags &= ~XPC_CHCTL_CLOSEREPLY; - ch->flags |= XPC_C_RCLOSEREPLY; - - /* both sides have finished disconnecting */ - xpc_process_disconnect(ch, &irq_flags); - DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED)); - goto again; - } - - if (ch->flags & XPC_C_DISCONNECTED) { - if (!(chctl_flags & XPC_CHCTL_OPENREQUEST)) { - if (part->chctl.flags[ch_number] & - XPC_CHCTL_OPENREQUEST) { - - DBUG_ON(ch->delayed_chctl_flags != 0); - spin_lock(&part->chctl_lock); - part->chctl.flags[ch_number] |= - XPC_CHCTL_CLOSEREQUEST; - spin_unlock(&part->chctl_lock); - } - goto out; - } - - XPC_SET_REASON(ch, 0, 0); - ch->flags &= ~XPC_C_DISCONNECTED; - - atomic_inc(&part->nchannels_active); - ch->flags |= (XPC_C_CONNECTING | XPC_C_ROPENREQUEST); - } - - chctl_flags &= ~(XPC_CHCTL_OPENREQUEST | XPC_CHCTL_OPENREPLY | - XPC_CHCTL_OPENCOMPLETE); - - /* - * The meaningful CLOSEREQUEST connection state fields are: - * reason = reason connection is to be closed - */ - - ch->flags |= XPC_C_RCLOSEREQUEST; - - if (!(ch->flags & XPC_C_DISCONNECTING)) { - reason = args->reason; - if (reason <= xpSuccess || reason > xpUnknownReason) - reason = xpUnknownReason; - else if (reason == xpUnregistering) - reason = xpOtherUnregistering; - - XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags); - - DBUG_ON(chctl_flags & XPC_CHCTL_CLOSEREPLY); - goto out; - } - - xpc_process_disconnect(ch, &irq_flags); - } - - if (chctl_flags & XPC_CHCTL_CLOSEREPLY) { - - dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREPLY received from partid=" - "%d, channel=%d\n", ch->partid, ch->number); - - if (ch->flags & XPC_C_DISCONNECTED) { - DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING); - goto out; - } - - DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); - - if (!(ch->flags & XPC_C_RCLOSEREQUEST)) { - if (part->chctl.flags[ch_number] & - XPC_CHCTL_CLOSEREQUEST) { - - DBUG_ON(ch->delayed_chctl_flags != 0); - spin_lock(&part->chctl_lock); - part->chctl.flags[ch_number] |= - XPC_CHCTL_CLOSEREPLY; - spin_unlock(&part->chctl_lock); - } - goto out; - } - - ch->flags |= XPC_C_RCLOSEREPLY; - - if (ch->flags & XPC_C_CLOSEREPLY) { - /* both sides have finished disconnecting */ - xpc_process_disconnect(ch, &irq_flags); - } - } - - if (chctl_flags & XPC_CHCTL_OPENREQUEST) { - - dev_dbg(xpc_chan, "XPC_CHCTL_OPENREQUEST (entry_size=%d, " - "local_nentries=%d) received from partid=%d, " - "channel=%d\n", args->entry_size, args->local_nentries, - ch->partid, ch->number); - - if (part->act_state == XPC_P_AS_DEACTIVATING || - (ch->flags & XPC_C_ROPENREQUEST)) { - goto out; - } - - if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_WDISCONNECT)) { - ch->delayed_chctl_flags |= XPC_CHCTL_OPENREQUEST; - goto out; - } - DBUG_ON(!(ch->flags & (XPC_C_DISCONNECTED | - XPC_C_OPENREQUEST))); - DBUG_ON(ch->flags & (XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY | - XPC_C_OPENREPLY | XPC_C_CONNECTED)); - - /* - * The meaningful OPENREQUEST connection state fields are: - * entry_size = size of channel's messages in bytes - * local_nentries = remote partition's local_nentries - */ - if (args->entry_size == 0 || args->local_nentries == 0) { - /* assume OPENREQUEST was delayed by mistake */ - goto out; - } - - ch->flags |= (XPC_C_ROPENREQUEST | XPC_C_CONNECTING); - ch->remote_nentries = args->local_nentries; - - if (ch->flags & XPC_C_OPENREQUEST) { - if (args->entry_size != ch->entry_size) { - XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes, - &irq_flags); - goto out; - } - } else { - ch->entry_size = args->entry_size; - - XPC_SET_REASON(ch, 0, 0); - ch->flags &= ~XPC_C_DISCONNECTED; - - atomic_inc(&part->nchannels_active); - } - - xpc_process_connect(ch, &irq_flags); - } - - if (chctl_flags & XPC_CHCTL_OPENREPLY) { - - dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY (local_msgqueue_pa=" - "0x%lx, local_nentries=%d, remote_nentries=%d) " - "received from partid=%d, channel=%d\n", - args->local_msgqueue_pa, args->local_nentries, - args->remote_nentries, ch->partid, ch->number); - - if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) - goto out; - - if (!(ch->flags & XPC_C_OPENREQUEST)) { - XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError, - &irq_flags); - goto out; - } - - DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST)); - DBUG_ON(ch->flags & XPC_C_CONNECTED); - - /* - * The meaningful OPENREPLY connection state fields are: - * local_msgqueue_pa = physical address of remote - * partition's local_msgqueue - * local_nentries = remote partition's local_nentries - * remote_nentries = remote partition's remote_nentries - */ - DBUG_ON(args->local_msgqueue_pa == 0); - DBUG_ON(args->local_nentries == 0); - DBUG_ON(args->remote_nentries == 0); - - ret = xpc_arch_ops.save_remote_msgqueue_pa(ch, - args->local_msgqueue_pa); - if (ret != xpSuccess) { - XPC_DISCONNECT_CHANNEL(ch, ret, &irq_flags); - goto out; - } - ch->flags |= XPC_C_ROPENREPLY; - - if (args->local_nentries < ch->remote_nentries) { - dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new " - "remote_nentries=%d, old remote_nentries=%d, " - "partid=%d, channel=%d\n", - args->local_nentries, ch->remote_nentries, - ch->partid, ch->number); - - ch->remote_nentries = args->local_nentries; - } - if (args->remote_nentries < ch->local_nentries) { - dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new " - "local_nentries=%d, old local_nentries=%d, " - "partid=%d, channel=%d\n", - args->remote_nentries, ch->local_nentries, - ch->partid, ch->number); - - ch->local_nentries = args->remote_nentries; - } - - xpc_process_connect(ch, &irq_flags); - } - - if (chctl_flags & XPC_CHCTL_OPENCOMPLETE) { - - dev_dbg(xpc_chan, "XPC_CHCTL_OPENCOMPLETE received from " - "partid=%d, channel=%d\n", ch->partid, ch->number); - - if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) - goto out; - - if (!(ch->flags & XPC_C_OPENREQUEST) || - !(ch->flags & XPC_C_OPENREPLY)) { - XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError, - &irq_flags); - goto out; - } - - DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST)); - DBUG_ON(!(ch->flags & XPC_C_ROPENREPLY)); - DBUG_ON(!(ch->flags & XPC_C_CONNECTED)); - - ch->flags |= XPC_C_ROPENCOMPLETE; - - xpc_process_connect(ch, &irq_flags); - create_kthread = 1; - } - -out: - spin_unlock_irqrestore(&ch->lock, irq_flags); - - if (create_kthread) - xpc_create_kthreads(ch, 1, 0); -} - -/* - * Attempt to establish a channel connection to a remote partition. - */ -static enum xp_retval -xpc_connect_channel(struct xpc_channel *ch) -{ - unsigned long irq_flags; - struct xpc_registration *registration = &xpc_registrations[ch->number]; - - if (mutex_trylock(®istration->mutex) == 0) - return xpRetry; - - if (!XPC_CHANNEL_REGISTERED(ch->number)) { - mutex_unlock(®istration->mutex); - return xpUnregistered; - } - - spin_lock_irqsave(&ch->lock, irq_flags); - - DBUG_ON(ch->flags & XPC_C_CONNECTED); - DBUG_ON(ch->flags & XPC_C_OPENREQUEST); - - if (ch->flags & XPC_C_DISCONNECTING) { - spin_unlock_irqrestore(&ch->lock, irq_flags); - mutex_unlock(®istration->mutex); - return ch->reason; - } - - /* add info from the channel connect registration to the channel */ - - ch->kthreads_assigned_limit = registration->assigned_limit; - ch->kthreads_idle_limit = registration->idle_limit; - DBUG_ON(atomic_read(&ch->kthreads_assigned) != 0); - DBUG_ON(atomic_read(&ch->kthreads_idle) != 0); - DBUG_ON(atomic_read(&ch->kthreads_active) != 0); - - ch->func = registration->func; - DBUG_ON(registration->func == NULL); - ch->key = registration->key; - - ch->local_nentries = registration->nentries; - - if (ch->flags & XPC_C_ROPENREQUEST) { - if (registration->entry_size != ch->entry_size) { - /* the local and remote sides aren't the same */ - - /* - * Because XPC_DISCONNECT_CHANNEL() can block we're - * forced to up the registration sema before we unlock - * the channel lock. But that's okay here because we're - * done with the part that required the registration - * sema. XPC_DISCONNECT_CHANNEL() requires that the - * channel lock be locked and will unlock and relock - * the channel lock as needed. - */ - mutex_unlock(®istration->mutex); - XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes, - &irq_flags); - spin_unlock_irqrestore(&ch->lock, irq_flags); - return xpUnequalMsgSizes; - } - } else { - ch->entry_size = registration->entry_size; - - XPC_SET_REASON(ch, 0, 0); - ch->flags &= ~XPC_C_DISCONNECTED; - - atomic_inc(&xpc_partitions[ch->partid].nchannels_active); - } - - mutex_unlock(®istration->mutex); - - /* initiate the connection */ - - ch->flags |= (XPC_C_OPENREQUEST | XPC_C_CONNECTING); - xpc_arch_ops.send_chctl_openrequest(ch, &irq_flags); - - xpc_process_connect(ch, &irq_flags); - - spin_unlock_irqrestore(&ch->lock, irq_flags); - - return xpSuccess; -} - -void -xpc_process_sent_chctl_flags(struct xpc_partition *part) -{ - unsigned long irq_flags; - union xpc_channel_ctl_flags chctl; - struct xpc_channel *ch; - int ch_number; - u32 ch_flags; - - chctl.all_flags = xpc_arch_ops.get_chctl_all_flags(part); - - /* - * Initiate channel connections for registered channels. - * - * For each connected channel that has pending messages activate idle - * kthreads and/or create new kthreads as needed. - */ - - for (ch_number = 0; ch_number < part->nchannels; ch_number++) { - ch = &part->channels[ch_number]; - - /* - * Process any open or close related chctl flags, and then deal - * with connecting or disconnecting the channel as required. - */ - - if (chctl.flags[ch_number] & XPC_OPENCLOSE_CHCTL_FLAGS) { - xpc_process_openclose_chctl_flags(part, ch_number, - chctl.flags[ch_number]); - } - - ch_flags = ch->flags; /* need an atomic snapshot of flags */ - - if (ch_flags & XPC_C_DISCONNECTING) { - spin_lock_irqsave(&ch->lock, irq_flags); - xpc_process_disconnect(ch, &irq_flags); - spin_unlock_irqrestore(&ch->lock, irq_flags); - continue; - } - - if (part->act_state == XPC_P_AS_DEACTIVATING) - continue; - - if (!(ch_flags & XPC_C_CONNECTED)) { - if (!(ch_flags & XPC_C_OPENREQUEST)) { - DBUG_ON(ch_flags & XPC_C_SETUP); - (void)xpc_connect_channel(ch); - } - continue; - } - - /* - * Process any message related chctl flags, this may involve - * the activation of kthreads to deliver any pending messages - * sent from the other partition. - */ - - if (chctl.flags[ch_number] & XPC_MSG_CHCTL_FLAGS) - xpc_arch_ops.process_msg_chctl_flags(part, ch_number); - } -} - -/* - * XPC's heartbeat code calls this function to inform XPC that a partition is - * going down. XPC responds by tearing down the XPartition Communication - * infrastructure used for the just downed partition. - * - * XPC's heartbeat code will never call this function and xpc_partition_up() - * at the same time. Nor will it ever make multiple calls to either function - * at the same time. - */ -void -xpc_partition_going_down(struct xpc_partition *part, enum xp_retval reason) -{ - unsigned long irq_flags; - int ch_number; - struct xpc_channel *ch; - - dev_dbg(xpc_chan, "deactivating partition %d, reason=%d\n", - XPC_PARTID(part), reason); - - if (!xpc_part_ref(part)) { - /* infrastructure for this partition isn't currently set up */ - return; - } - - /* disconnect channels associated with the partition going down */ - - for (ch_number = 0; ch_number < part->nchannels; ch_number++) { - ch = &part->channels[ch_number]; - - xpc_msgqueue_ref(ch); - spin_lock_irqsave(&ch->lock, irq_flags); - - XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags); - - spin_unlock_irqrestore(&ch->lock, irq_flags); - xpc_msgqueue_deref(ch); - } - - xpc_wakeup_channel_mgr(part); - - xpc_part_deref(part); -} - -/* - * Called by XP at the time of channel connection registration to cause - * XPC to establish connections to all currently active partitions. - */ -void -xpc_initiate_connect(int ch_number) -{ - short partid; - struct xpc_partition *part; - - DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS); - - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - if (xpc_part_ref(part)) { - /* - * Initiate the establishment of a connection on the - * newly registered channel to the remote partition. - */ - xpc_wakeup_channel_mgr(part); - xpc_part_deref(part); - } - } -} - -void -xpc_connected_callout(struct xpc_channel *ch) -{ - /* let the registerer know that a connection has been established */ - - if (ch->func != NULL) { - dev_dbg(xpc_chan, "ch->func() called, reason=xpConnected, " - "partid=%d, channel=%d\n", ch->partid, ch->number); - - ch->func(xpConnected, ch->partid, ch->number, - (void *)(u64)ch->local_nentries, ch->key); - - dev_dbg(xpc_chan, "ch->func() returned, reason=xpConnected, " - "partid=%d, channel=%d\n", ch->partid, ch->number); - } -} - -/* - * Called by XP at the time of channel connection unregistration to cause - * XPC to teardown all current connections for the specified channel. - * - * Before returning xpc_initiate_disconnect() will wait until all connections - * on the specified channel have been closed/torndown. So the caller can be - * assured that they will not be receiving any more callouts from XPC to the - * function they registered via xpc_connect(). - * - * Arguments: - * - * ch_number - channel # to unregister. - */ -void -xpc_initiate_disconnect(int ch_number) -{ - unsigned long irq_flags; - short partid; - struct xpc_partition *part; - struct xpc_channel *ch; - - DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS); - - /* initiate the channel disconnect for every active partition */ - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - if (xpc_part_ref(part)) { - ch = &part->channels[ch_number]; - xpc_msgqueue_ref(ch); - - spin_lock_irqsave(&ch->lock, irq_flags); - - if (!(ch->flags & XPC_C_DISCONNECTED)) { - ch->flags |= XPC_C_WDISCONNECT; - - XPC_DISCONNECT_CHANNEL(ch, xpUnregistering, - &irq_flags); - } - - spin_unlock_irqrestore(&ch->lock, irq_flags); - - xpc_msgqueue_deref(ch); - xpc_part_deref(part); - } - } - - xpc_disconnect_wait(ch_number); -} - -/* - * To disconnect a channel, and reflect it back to all who may be waiting. - * - * An OPEN is not allowed until XPC_C_DISCONNECTING is cleared by - * xpc_process_disconnect(), and if set, XPC_C_WDISCONNECT is cleared by - * xpc_disconnect_wait(). - * - * THE CHANNEL IS TO BE LOCKED BY THE CALLER AND WILL REMAIN LOCKED UPON RETURN. - */ -void -xpc_disconnect_channel(const int line, struct xpc_channel *ch, - enum xp_retval reason, unsigned long *irq_flags) -{ - u32 channel_was_connected = (ch->flags & XPC_C_CONNECTED); - - lockdep_assert_held(&ch->lock); - - if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) - return; - - DBUG_ON(!(ch->flags & (XPC_C_CONNECTING | XPC_C_CONNECTED))); - - dev_dbg(xpc_chan, "reason=%d, line=%d, partid=%d, channel=%d\n", - reason, line, ch->partid, ch->number); - - XPC_SET_REASON(ch, reason, line); - - ch->flags |= (XPC_C_CLOSEREQUEST | XPC_C_DISCONNECTING); - /* some of these may not have been set */ - ch->flags &= ~(XPC_C_OPENREQUEST | XPC_C_OPENREPLY | - XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY | - XPC_C_CONNECTING | XPC_C_CONNECTED); - - xpc_arch_ops.send_chctl_closerequest(ch, irq_flags); - - if (channel_was_connected) - ch->flags |= XPC_C_WASCONNECTED; - - spin_unlock_irqrestore(&ch->lock, *irq_flags); - - /* wake all idle kthreads so they can exit */ - if (atomic_read(&ch->kthreads_idle) > 0) { - wake_up_all(&ch->idle_wq); - - } else if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) && - !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) { - /* start a kthread that will do the xpDisconnecting callout */ - xpc_create_kthreads(ch, 1, 1); - } - - /* wake those waiting to allocate an entry from the local msg queue */ - if (atomic_read(&ch->n_on_msg_allocate_wq) > 0) - wake_up(&ch->msg_allocate_wq); - - spin_lock_irqsave(&ch->lock, *irq_flags); -} - -void -xpc_disconnect_callout(struct xpc_channel *ch, enum xp_retval reason) -{ - /* - * Let the channel's registerer know that the channel is being - * disconnected. We don't want to do this if the registerer was never - * informed of a connection being made. - */ - - if (ch->func != NULL) { - dev_dbg(xpc_chan, "ch->func() called, reason=%d, partid=%d, " - "channel=%d\n", reason, ch->partid, ch->number); - - ch->func(reason, ch->partid, ch->number, NULL, ch->key); - - dev_dbg(xpc_chan, "ch->func() returned, reason=%d, partid=%d, " - "channel=%d\n", reason, ch->partid, ch->number); - } -} - -/* - * Wait for a message entry to become available for the specified channel, - * but don't wait any longer than 1 jiffy. - */ -enum xp_retval -xpc_allocate_msg_wait(struct xpc_channel *ch) -{ - enum xp_retval ret; - DEFINE_WAIT(wait); - - if (ch->flags & XPC_C_DISCONNECTING) { - DBUG_ON(ch->reason == xpInterrupted); - return ch->reason; - } - - atomic_inc(&ch->n_on_msg_allocate_wq); - prepare_to_wait(&ch->msg_allocate_wq, &wait, TASK_INTERRUPTIBLE); - ret = schedule_timeout(1); - finish_wait(&ch->msg_allocate_wq, &wait); - atomic_dec(&ch->n_on_msg_allocate_wq); - - if (ch->flags & XPC_C_DISCONNECTING) { - ret = ch->reason; - DBUG_ON(ch->reason == xpInterrupted); - } else if (ret == 0) { - ret = xpTimeout; - } else { - ret = xpInterrupted; - } - - return ret; -} - -/* - * Send a message that contains the user's payload on the specified channel - * connected to the specified partition. - * - * NOTE that this routine can sleep waiting for a message entry to become - * available. To not sleep, pass in the XPC_NOWAIT flag. - * - * Once sent, this routine will not wait for the message to be received, nor - * will notification be given when it does happen. - * - * Arguments: - * - * partid - ID of partition to which the channel is connected. - * ch_number - channel # to send message on. - * flags - see xp.h for valid flags. - * payload - pointer to the payload which is to be sent. - * payload_size - size of the payload in bytes. - */ -enum xp_retval -xpc_initiate_send(short partid, int ch_number, u32 flags, void *payload, - u16 payload_size) -{ - struct xpc_partition *part = &xpc_partitions[partid]; - enum xp_retval ret = xpUnknownReason; - - dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload, - partid, ch_number); - - DBUG_ON(partid < 0 || partid >= xp_max_npartitions); - DBUG_ON(ch_number < 0 || ch_number >= part->nchannels); - DBUG_ON(payload == NULL); - - if (xpc_part_ref(part)) { - ret = xpc_arch_ops.send_payload(&part->channels[ch_number], - flags, payload, payload_size, 0, NULL, NULL); - xpc_part_deref(part); - } - - return ret; -} - -/* - * Send a message that contains the user's payload on the specified channel - * connected to the specified partition. - * - * NOTE that this routine can sleep waiting for a message entry to become - * available. To not sleep, pass in the XPC_NOWAIT flag. - * - * This routine will not wait for the message to be sent or received. - * - * Once the remote end of the channel has received the message, the function - * passed as an argument to xpc_initiate_send_notify() will be called. This - * allows the sender to free up or re-use any buffers referenced by the - * message, but does NOT mean the message has been processed at the remote - * end by a receiver. - * - * If this routine returns an error, the caller's function will NOT be called. - * - * Arguments: - * - * partid - ID of partition to which the channel is connected. - * ch_number - channel # to send message on. - * flags - see xp.h for valid flags. - * payload - pointer to the payload which is to be sent. - * payload_size - size of the payload in bytes. - * func - function to call with asynchronous notification of message - * receipt. THIS FUNCTION MUST BE NON-BLOCKING. - * key - user-defined key to be passed to the function when it's called. - */ -enum xp_retval -xpc_initiate_send_notify(short partid, int ch_number, u32 flags, void *payload, - u16 payload_size, xpc_notify_func func, void *key) -{ - struct xpc_partition *part = &xpc_partitions[partid]; - enum xp_retval ret = xpUnknownReason; - - dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload, - partid, ch_number); - - DBUG_ON(partid < 0 || partid >= xp_max_npartitions); - DBUG_ON(ch_number < 0 || ch_number >= part->nchannels); - DBUG_ON(payload == NULL); - DBUG_ON(func == NULL); - - if (xpc_part_ref(part)) { - ret = xpc_arch_ops.send_payload(&part->channels[ch_number], - flags, payload, payload_size, XPC_N_CALL, func, key); - xpc_part_deref(part); - } - return ret; -} - -/* - * Deliver a message's payload to its intended recipient. - */ -void -xpc_deliver_payload(struct xpc_channel *ch) -{ - void *payload; - - payload = xpc_arch_ops.get_deliverable_payload(ch); - if (payload != NULL) { - - /* - * This ref is taken to protect the payload itself from being - * freed before the user is finished with it, which the user - * indicates by calling xpc_initiate_received(). - */ - xpc_msgqueue_ref(ch); - - atomic_inc(&ch->kthreads_active); - - if (ch->func != NULL) { - dev_dbg(xpc_chan, "ch->func() called, payload=0x%p " - "partid=%d channel=%d\n", payload, ch->partid, - ch->number); - - /* deliver the message to its intended recipient */ - ch->func(xpMsgReceived, ch->partid, ch->number, payload, - ch->key); - - dev_dbg(xpc_chan, "ch->func() returned, payload=0x%p " - "partid=%d channel=%d\n", payload, ch->partid, - ch->number); - } - - atomic_dec(&ch->kthreads_active); - } -} - -/* - * Acknowledge receipt of a delivered message's payload. - * - * This function, although called by users, does not call xpc_part_ref() to - * ensure that the partition infrastructure is in place. It relies on the - * fact that we called xpc_msgqueue_ref() in xpc_deliver_payload(). - * - * Arguments: - * - * partid - ID of partition to which the channel is connected. - * ch_number - channel # message received on. - * payload - pointer to the payload area allocated via - * xpc_initiate_send() or xpc_initiate_send_notify(). - */ -void -xpc_initiate_received(short partid, int ch_number, void *payload) -{ - struct xpc_partition *part = &xpc_partitions[partid]; - struct xpc_channel *ch; - - DBUG_ON(partid < 0 || partid >= xp_max_npartitions); - DBUG_ON(ch_number < 0 || ch_number >= part->nchannels); - - ch = &part->channels[ch_number]; - xpc_arch_ops.received_payload(ch, payload); - - /* the call to xpc_msgqueue_ref() was done by xpc_deliver_payload() */ - xpc_msgqueue_deref(ch); -} diff --git a/drivers/misc/sgi-xp/xpc_main.c b/drivers/misc/sgi-xp/xpc_main.c deleted file mode 100644 index 15b6a8e35681..000000000000 --- a/drivers/misc/sgi-xp/xpc_main.c +++ /dev/null @@ -1,1309 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition Communication (XPC) support - standard version. - * - * XPC provides a message passing capability that crosses partition - * boundaries. This module is made up of two parts: - * - * partition This part detects the presence/absence of other - * partitions. It provides a heartbeat and monitors - * the heartbeats of other partitions. - * - * channel This part manages the channels and sends/receives - * messages across them to/from other partitions. - * - * There are a couple of additional functions residing in XP, which - * provide an interface to XPC for its users. - * - * - * Caveats: - * - * . Currently on sn2, we have no way to determine which nasid an IRQ - * came from. Thus, xpc_send_IRQ_sn2() does a remote amo write - * followed by an IPI. The amo indicates where data is to be pulled - * from, so after the IPI arrives, the remote partition checks the amo - * word. The IPI can actually arrive before the amo however, so other - * code must periodically check for this case. Also, remote amo - * operations do not reliably time out. Thus we do a remote PIO read - * solely to know whether the remote partition is down and whether we - * should stop sending IPIs to it. This remote PIO read operation is - * set up in a special nofault region so SAL knows to ignore (and - * cleanup) any errors due to the remote amo write, PIO read, and/or - * PIO write operations. - * - * If/when new hardware solves this IPI problem, we should abandon - * the current approach. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "xpc.h" - -#ifdef CONFIG_X86_64 -#include -#endif - -/* define two XPC debug device structures to be used with dev_dbg() et al */ - -static struct device_driver xpc_dbg_name = { - .name = "xpc" -}; - -static struct device xpc_part_dbg_subname = { - .init_name = "", /* set to "part" at xpc_init() time */ - .driver = &xpc_dbg_name -}; - -static struct device xpc_chan_dbg_subname = { - .init_name = "", /* set to "chan" at xpc_init() time */ - .driver = &xpc_dbg_name -}; - -struct device *xpc_part = &xpc_part_dbg_subname; -struct device *xpc_chan = &xpc_chan_dbg_subname; - -static int xpc_kdebug_ignore; - -/* systune related variables for /proc/sys directories */ - -static int xpc_hb_interval = XPC_HB_DEFAULT_INTERVAL; -static int xpc_hb_min_interval = 1; -static int xpc_hb_max_interval = 10; - -static int xpc_hb_check_interval = XPC_HB_CHECK_DEFAULT_INTERVAL; -static int xpc_hb_check_min_interval = 10; -static int xpc_hb_check_max_interval = 120; - -int xpc_disengage_timelimit = XPC_DISENGAGE_DEFAULT_TIMELIMIT; -static int xpc_disengage_min_timelimit; /* = 0 */ -static int xpc_disengage_max_timelimit = 120; - -static const struct ctl_table xpc_sys_xpc_hb[] = { - { - .procname = "hb_interval", - .data = &xpc_hb_interval, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &xpc_hb_min_interval, - .extra2 = &xpc_hb_max_interval}, - { - .procname = "hb_check_interval", - .data = &xpc_hb_check_interval, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &xpc_hb_check_min_interval, - .extra2 = &xpc_hb_check_max_interval}, -}; -static const struct ctl_table xpc_sys_xpc[] = { - { - .procname = "disengage_timelimit", - .data = &xpc_disengage_timelimit, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &xpc_disengage_min_timelimit, - .extra2 = &xpc_disengage_max_timelimit}, -}; - -static struct ctl_table_header *xpc_sysctl; -static struct ctl_table_header *xpc_sysctl_hb; - -/* non-zero if any remote partition disengage was timed out */ -int xpc_disengage_timedout; - -/* #of activate IRQs received and not yet processed */ -int xpc_activate_IRQ_rcvd; -DEFINE_SPINLOCK(xpc_activate_IRQ_rcvd_lock); - -/* IRQ handler notifies this wait queue on receipt of an IRQ */ -DECLARE_WAIT_QUEUE_HEAD(xpc_activate_IRQ_wq); - -static unsigned long xpc_hb_check_timeout; -static struct timer_list xpc_hb_timer; - -/* notification that the xpc_hb_checker thread has exited */ -static DECLARE_COMPLETION(xpc_hb_checker_exited); - -/* notification that the xpc_discovery thread has exited */ -static DECLARE_COMPLETION(xpc_discovery_exited); - -static void xpc_kthread_waitmsgs(struct xpc_partition *, struct xpc_channel *); - -static int xpc_system_reboot(struct notifier_block *, unsigned long, void *); -static struct notifier_block xpc_reboot_notifier = { - .notifier_call = xpc_system_reboot, -}; - -static int xpc_system_die(struct notifier_block *, unsigned long, void *); -static struct notifier_block xpc_die_notifier = { - .notifier_call = xpc_system_die, -}; - -struct xpc_arch_operations xpc_arch_ops; - -/* - * Timer function to enforce the timelimit on the partition disengage. - */ -static void -xpc_timeout_partition_disengage(struct timer_list *t) -{ - struct xpc_partition *part = timer_container_of(part, t, - disengage_timer); - - DBUG_ON(time_is_after_jiffies(part->disengage_timeout)); - - xpc_partition_disengaged_from_timer(part); - - DBUG_ON(part->disengage_timeout != 0); - DBUG_ON(xpc_arch_ops.partition_engaged(XPC_PARTID(part))); -} - -/* - * Timer to produce the heartbeat. The timer structures function is - * already set when this is initially called. A tunable is used to - * specify when the next timeout should occur. - */ -static void -xpc_hb_beater(struct timer_list *unused) -{ - xpc_arch_ops.increment_heartbeat(); - - if (time_is_before_eq_jiffies(xpc_hb_check_timeout)) - wake_up_interruptible(&xpc_activate_IRQ_wq); - - xpc_hb_timer.expires = jiffies + (xpc_hb_interval * HZ); - add_timer(&xpc_hb_timer); -} - -static void -xpc_start_hb_beater(void) -{ - xpc_arch_ops.heartbeat_init(); - timer_setup(&xpc_hb_timer, xpc_hb_beater, 0); - xpc_hb_beater(NULL); -} - -static void -xpc_stop_hb_beater(void) -{ - timer_delete_sync(&xpc_hb_timer); - xpc_arch_ops.heartbeat_exit(); -} - -/* - * At periodic intervals, scan through all active partitions and ensure - * their heartbeat is still active. If not, the partition is deactivated. - */ -static void -xpc_check_remote_hb(void) -{ - struct xpc_partition *part; - short partid; - enum xp_retval ret; - - for (partid = 0; partid < xp_max_npartitions; partid++) { - - if (xpc_exiting) - break; - - if (partid == xp_partition_id) - continue; - - part = &xpc_partitions[partid]; - - if (part->act_state == XPC_P_AS_INACTIVE || - part->act_state == XPC_P_AS_DEACTIVATING) { - continue; - } - - ret = xpc_arch_ops.get_remote_heartbeat(part); - if (ret != xpSuccess) - XPC_DEACTIVATE_PARTITION(part, ret); - } -} - -/* - * This thread is responsible for nearly all of the partition - * activation/deactivation. - */ -static int -xpc_hb_checker(void *ignore) -{ - int force_IRQ = 0; - - /* this thread was marked active by xpc_hb_init() */ - - set_cpus_allowed_ptr(current, cpumask_of(XPC_HB_CHECK_CPU)); - - /* set our heartbeating to other partitions into motion */ - xpc_hb_check_timeout = jiffies + (xpc_hb_check_interval * HZ); - xpc_start_hb_beater(); - - while (!xpc_exiting) { - - dev_dbg(xpc_part, "woke up with %d ticks rem; %d IRQs have " - "been received\n", - (int)(xpc_hb_check_timeout - jiffies), - xpc_activate_IRQ_rcvd); - - /* checking of remote heartbeats is skewed by IRQ handling */ - if (time_is_before_eq_jiffies(xpc_hb_check_timeout)) { - xpc_hb_check_timeout = jiffies + - (xpc_hb_check_interval * HZ); - - dev_dbg(xpc_part, "checking remote heartbeats\n"); - xpc_check_remote_hb(); - } - - /* check for outstanding IRQs */ - if (xpc_activate_IRQ_rcvd > 0 || force_IRQ != 0) { - force_IRQ = 0; - dev_dbg(xpc_part, "processing activate IRQs " - "received\n"); - xpc_arch_ops.process_activate_IRQ_rcvd(); - } - - /* wait for IRQ or timeout */ - (void)wait_event_interruptible(xpc_activate_IRQ_wq, - (time_is_before_eq_jiffies( - xpc_hb_check_timeout) || - xpc_activate_IRQ_rcvd > 0 || - xpc_exiting)); - } - - xpc_stop_hb_beater(); - - dev_dbg(xpc_part, "heartbeat checker is exiting\n"); - - /* mark this thread as having exited */ - complete(&xpc_hb_checker_exited); - return 0; -} - -/* - * This thread will attempt to discover other partitions to activate - * based on info provided by SAL. This new thread is short lived and - * will exit once discovery is complete. - */ -static int -xpc_initiate_discovery(void *ignore) -{ - xpc_discovery(); - - dev_dbg(xpc_part, "discovery thread is exiting\n"); - - /* mark this thread as having exited */ - complete(&xpc_discovery_exited); - return 0; -} - -/* - * The first kthread assigned to a newly activated partition is the one - * created by XPC HB with which it calls xpc_activating(). XPC hangs on to - * that kthread until the partition is brought down, at which time that kthread - * returns back to XPC HB. (The return of that kthread will signify to XPC HB - * that XPC has dismantled all communication infrastructure for the associated - * partition.) This kthread becomes the channel manager for that partition. - * - * Each active partition has a channel manager, who, besides connecting and - * disconnecting channels, will ensure that each of the partition's connected - * channels has the required number of assigned kthreads to get the work done. - */ -static void -xpc_channel_mgr(struct xpc_partition *part) -{ - while (part->act_state != XPC_P_AS_DEACTIVATING || - atomic_read(&part->nchannels_active) > 0 || - !xpc_partition_disengaged(part)) { - - xpc_process_sent_chctl_flags(part); - - /* - * Wait until we've been requested to activate kthreads or - * all of the channel's message queues have been torn down or - * a signal is pending. - * - * The channel_mgr_requests is set to 1 after being awakened, - * This is done to prevent the channel mgr from making one pass - * through the loop for each request, since he will - * be servicing all the requests in one pass. The reason it's - * set to 1 instead of 0 is so that other kthreads will know - * that the channel mgr is running and won't bother trying to - * wake him up. - */ - atomic_dec(&part->channel_mgr_requests); - (void)wait_event_interruptible(part->channel_mgr_wq, - (atomic_read(&part->channel_mgr_requests) > 0 || - part->chctl.all_flags != 0 || - (part->act_state == XPC_P_AS_DEACTIVATING && - atomic_read(&part->nchannels_active) == 0 && - xpc_partition_disengaged(part)))); - atomic_set(&part->channel_mgr_requests, 1); - } -} - -/* - * Guarantee that the kzalloc'd memory is cacheline aligned. - */ -void * -xpc_kzalloc_cacheline_aligned(size_t size, gfp_t flags, void **base) -{ - /* see if kzalloc will give us cachline aligned memory by default */ - *base = kzalloc(size, flags); - if (*base == NULL) - return NULL; - - if ((u64)*base == L1_CACHE_ALIGN((u64)*base)) - return *base; - - kfree(*base); - - /* nope, we'll have to do it ourselves */ - *base = kzalloc(size + L1_CACHE_BYTES, flags); - if (*base == NULL) - return NULL; - - return (void *)L1_CACHE_ALIGN((u64)*base); -} - -/* - * Setup the channel structures necessary to support XPartition Communication - * between the specified remote partition and the local one. - */ -static enum xp_retval -xpc_setup_ch_structures(struct xpc_partition *part) -{ - enum xp_retval ret; - int ch_number; - struct xpc_channel *ch; - short partid = XPC_PARTID(part); - - /* - * Allocate all of the channel structures as a contiguous chunk of - * memory. - */ - DBUG_ON(part->channels != NULL); - part->channels = kzalloc_objs(struct xpc_channel, XPC_MAX_NCHANNELS); - if (part->channels == NULL) { - dev_err(xpc_chan, "can't get memory for channels\n"); - return xpNoMemory; - } - - /* allocate the remote open and close args */ - - part->remote_openclose_args = - xpc_kzalloc_cacheline_aligned(XPC_OPENCLOSE_ARGS_SIZE, - GFP_KERNEL, &part-> - remote_openclose_args_base); - if (part->remote_openclose_args == NULL) { - dev_err(xpc_chan, "can't get memory for remote connect args\n"); - ret = xpNoMemory; - goto out_1; - } - - part->chctl.all_flags = 0; - spin_lock_init(&part->chctl_lock); - - atomic_set(&part->channel_mgr_requests, 1); - init_waitqueue_head(&part->channel_mgr_wq); - - part->nchannels = XPC_MAX_NCHANNELS; - - atomic_set(&part->nchannels_active, 0); - atomic_set(&part->nchannels_engaged, 0); - - for (ch_number = 0; ch_number < part->nchannels; ch_number++) { - ch = &part->channels[ch_number]; - - ch->partid = partid; - ch->number = ch_number; - ch->flags = XPC_C_DISCONNECTED; - - atomic_set(&ch->kthreads_assigned, 0); - atomic_set(&ch->kthreads_idle, 0); - atomic_set(&ch->kthreads_active, 0); - - atomic_set(&ch->references, 0); - atomic_set(&ch->n_to_notify, 0); - - spin_lock_init(&ch->lock); - init_completion(&ch->wdisconnect_wait); - - atomic_set(&ch->n_on_msg_allocate_wq, 0); - init_waitqueue_head(&ch->msg_allocate_wq); - init_waitqueue_head(&ch->idle_wq); - } - - ret = xpc_arch_ops.setup_ch_structures(part); - if (ret != xpSuccess) - goto out_2; - - /* - * With the setting of the partition setup_state to XPC_P_SS_SETUP, - * we're declaring that this partition is ready to go. - */ - part->setup_state = XPC_P_SS_SETUP; - - return xpSuccess; - - /* setup of ch structures failed */ -out_2: - kfree(part->remote_openclose_args_base); - part->remote_openclose_args = NULL; -out_1: - kfree(part->channels); - part->channels = NULL; - return ret; -} - -/* - * Teardown the channel structures necessary to support XPartition Communication - * between the specified remote partition and the local one. - */ -static void -xpc_teardown_ch_structures(struct xpc_partition *part) -{ - DBUG_ON(atomic_read(&part->nchannels_engaged) != 0); - DBUG_ON(atomic_read(&part->nchannels_active) != 0); - - /* - * Make this partition inaccessible to local processes by marking it - * as no longer setup. Then wait before proceeding with the teardown - * until all existing references cease. - */ - DBUG_ON(part->setup_state != XPC_P_SS_SETUP); - part->setup_state = XPC_P_SS_WTEARDOWN; - - wait_event(part->teardown_wq, (atomic_read(&part->references) == 0)); - - /* now we can begin tearing down the infrastructure */ - - xpc_arch_ops.teardown_ch_structures(part); - - kfree(part->remote_openclose_args_base); - part->remote_openclose_args = NULL; - kfree(part->channels); - part->channels = NULL; - - part->setup_state = XPC_P_SS_TORNDOWN; -} - -/* - * When XPC HB determines that a partition has come up, it will create a new - * kthread and that kthread will call this function to attempt to set up the - * basic infrastructure used for Cross Partition Communication with the newly - * upped partition. - * - * The kthread that was created by XPC HB and which setup the XPC - * infrastructure will remain assigned to the partition becoming the channel - * manager for that partition until the partition is deactivating, at which - * time the kthread will teardown the XPC infrastructure and then exit. - */ -static int -xpc_activating(void *__partid) -{ - short partid = (u64)__partid; - struct xpc_partition *part = &xpc_partitions[partid]; - unsigned long irq_flags; - - DBUG_ON(partid < 0 || partid >= xp_max_npartitions); - - spin_lock_irqsave(&part->act_lock, irq_flags); - - if (part->act_state == XPC_P_AS_DEACTIVATING) { - part->act_state = XPC_P_AS_INACTIVE; - spin_unlock_irqrestore(&part->act_lock, irq_flags); - part->remote_rp_pa = 0; - return 0; - } - - /* indicate the thread is activating */ - DBUG_ON(part->act_state != XPC_P_AS_ACTIVATION_REQ); - part->act_state = XPC_P_AS_ACTIVATING; - - XPC_SET_REASON(part, 0, 0); - spin_unlock_irqrestore(&part->act_lock, irq_flags); - - dev_dbg(xpc_part, "activating partition %d\n", partid); - - xpc_arch_ops.allow_hb(partid); - - if (xpc_setup_ch_structures(part) == xpSuccess) { - (void)xpc_part_ref(part); /* this will always succeed */ - - if (xpc_arch_ops.make_first_contact(part) == xpSuccess) { - xpc_mark_partition_active(part); - xpc_channel_mgr(part); - /* won't return until partition is deactivating */ - } - - xpc_part_deref(part); - xpc_teardown_ch_structures(part); - } - - xpc_arch_ops.disallow_hb(partid); - xpc_mark_partition_inactive(part); - - if (part->reason == xpReactivating) { - /* interrupting ourselves results in activating partition */ - xpc_arch_ops.request_partition_reactivation(part); - } - - return 0; -} - -void -xpc_activate_partition(struct xpc_partition *part) -{ - short partid = XPC_PARTID(part); - unsigned long irq_flags; - struct task_struct *kthread; - - spin_lock_irqsave(&part->act_lock, irq_flags); - - DBUG_ON(part->act_state != XPC_P_AS_INACTIVE); - - part->act_state = XPC_P_AS_ACTIVATION_REQ; - XPC_SET_REASON(part, xpCloneKThread, __LINE__); - - spin_unlock_irqrestore(&part->act_lock, irq_flags); - - kthread = kthread_run(xpc_activating, (void *)((u64)partid), "xpc%02d", - partid); - if (IS_ERR(kthread)) { - spin_lock_irqsave(&part->act_lock, irq_flags); - part->act_state = XPC_P_AS_INACTIVE; - XPC_SET_REASON(part, xpCloneKThreadFailed, __LINE__); - spin_unlock_irqrestore(&part->act_lock, irq_flags); - } -} - -void -xpc_activate_kthreads(struct xpc_channel *ch, int needed) -{ - int idle = atomic_read(&ch->kthreads_idle); - int assigned = atomic_read(&ch->kthreads_assigned); - int wakeup; - - DBUG_ON(needed <= 0); - - if (idle > 0) { - wakeup = (needed > idle) ? idle : needed; - needed -= wakeup; - - dev_dbg(xpc_chan, "wakeup %d idle kthreads, partid=%d, " - "channel=%d\n", wakeup, ch->partid, ch->number); - - /* only wakeup the requested number of kthreads */ - wake_up_nr(&ch->idle_wq, wakeup); - } - - if (needed <= 0) - return; - - if (needed + assigned > ch->kthreads_assigned_limit) { - needed = ch->kthreads_assigned_limit - assigned; - if (needed <= 0) - return; - } - - dev_dbg(xpc_chan, "create %d new kthreads, partid=%d, channel=%d\n", - needed, ch->partid, ch->number); - - xpc_create_kthreads(ch, needed, 0); -} - -/* - * This function is where XPC's kthreads wait for messages to deliver. - */ -static void -xpc_kthread_waitmsgs(struct xpc_partition *part, struct xpc_channel *ch) -{ - int (*n_of_deliverable_payloads) (struct xpc_channel *) = - xpc_arch_ops.n_of_deliverable_payloads; - - do { - /* deliver messages to their intended recipients */ - - while (n_of_deliverable_payloads(ch) > 0 && - !(ch->flags & XPC_C_DISCONNECTING)) { - xpc_deliver_payload(ch); - } - - if (atomic_inc_return(&ch->kthreads_idle) > - ch->kthreads_idle_limit) { - /* too many idle kthreads on this channel */ - atomic_dec(&ch->kthreads_idle); - break; - } - - dev_dbg(xpc_chan, "idle kthread calling " - "wait_event_interruptible_exclusive()\n"); - - (void)wait_event_interruptible_exclusive(ch->idle_wq, - (n_of_deliverable_payloads(ch) > 0 || - (ch->flags & XPC_C_DISCONNECTING))); - - atomic_dec(&ch->kthreads_idle); - - } while (!(ch->flags & XPC_C_DISCONNECTING)); -} - -static int -xpc_kthread_start(void *args) -{ - short partid = XPC_UNPACK_ARG1(args); - u16 ch_number = XPC_UNPACK_ARG2(args); - struct xpc_partition *part = &xpc_partitions[partid]; - struct xpc_channel *ch; - int n_needed; - unsigned long irq_flags; - int (*n_of_deliverable_payloads) (struct xpc_channel *) = - xpc_arch_ops.n_of_deliverable_payloads; - - dev_dbg(xpc_chan, "kthread starting, partid=%d, channel=%d\n", - partid, ch_number); - - ch = &part->channels[ch_number]; - - if (!(ch->flags & XPC_C_DISCONNECTING)) { - - /* let registerer know that connection has been established */ - - spin_lock_irqsave(&ch->lock, irq_flags); - if (!(ch->flags & XPC_C_CONNECTEDCALLOUT)) { - ch->flags |= XPC_C_CONNECTEDCALLOUT; - spin_unlock_irqrestore(&ch->lock, irq_flags); - - xpc_connected_callout(ch); - - spin_lock_irqsave(&ch->lock, irq_flags); - ch->flags |= XPC_C_CONNECTEDCALLOUT_MADE; - spin_unlock_irqrestore(&ch->lock, irq_flags); - - /* - * It is possible that while the callout was being - * made that the remote partition sent some messages. - * If that is the case, we may need to activate - * additional kthreads to help deliver them. We only - * need one less than total #of messages to deliver. - */ - n_needed = n_of_deliverable_payloads(ch) - 1; - if (n_needed > 0 && !(ch->flags & XPC_C_DISCONNECTING)) - xpc_activate_kthreads(ch, n_needed); - - } else { - spin_unlock_irqrestore(&ch->lock, irq_flags); - } - - xpc_kthread_waitmsgs(part, ch); - } - - /* let registerer know that connection is disconnecting */ - - spin_lock_irqsave(&ch->lock, irq_flags); - if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) && - !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) { - ch->flags |= XPC_C_DISCONNECTINGCALLOUT; - spin_unlock_irqrestore(&ch->lock, irq_flags); - - xpc_disconnect_callout(ch, xpDisconnecting); - - spin_lock_irqsave(&ch->lock, irq_flags); - ch->flags |= XPC_C_DISCONNECTINGCALLOUT_MADE; - } - spin_unlock_irqrestore(&ch->lock, irq_flags); - - if (atomic_dec_return(&ch->kthreads_assigned) == 0 && - atomic_dec_return(&part->nchannels_engaged) == 0) { - xpc_arch_ops.indicate_partition_disengaged(part); - } - - xpc_msgqueue_deref(ch); - - dev_dbg(xpc_chan, "kthread exiting, partid=%d, channel=%d\n", - partid, ch_number); - - xpc_part_deref(part); - return 0; -} - -/* - * For each partition that XPC has established communications with, there is - * a minimum of one kernel thread assigned to perform any operation that - * may potentially sleep or block (basically the callouts to the asynchronous - * functions registered via xpc_connect()). - * - * Additional kthreads are created and destroyed by XPC as the workload - * demands. - * - * A kthread is assigned to one of the active channels that exists for a given - * partition. - */ -void -xpc_create_kthreads(struct xpc_channel *ch, int needed, - int ignore_disconnecting) -{ - unsigned long irq_flags; - u64 args = XPC_PACK_ARGS(ch->partid, ch->number); - struct xpc_partition *part = &xpc_partitions[ch->partid]; - struct task_struct *kthread; - void (*indicate_partition_disengaged) (struct xpc_partition *) = - xpc_arch_ops.indicate_partition_disengaged; - - while (needed-- > 0) { - - /* - * The following is done on behalf of the newly created - * kthread. That kthread is responsible for doing the - * counterpart to the following before it exits. - */ - if (ignore_disconnecting) { - if (!atomic_inc_not_zero(&ch->kthreads_assigned)) { - /* kthreads assigned had gone to zero */ - BUG_ON(!(ch->flags & - XPC_C_DISCONNECTINGCALLOUT_MADE)); - break; - } - - } else if (ch->flags & XPC_C_DISCONNECTING) { - break; - - } else if (atomic_inc_return(&ch->kthreads_assigned) == 1 && - atomic_inc_return(&part->nchannels_engaged) == 1) { - xpc_arch_ops.indicate_partition_engaged(part); - } - (void)xpc_part_ref(part); - xpc_msgqueue_ref(ch); - - kthread = kthread_run(xpc_kthread_start, (void *)args, - "xpc%02dc%d", ch->partid, ch->number); - if (IS_ERR(kthread)) { - /* the fork failed */ - - /* - * NOTE: if (ignore_disconnecting && - * !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) is true, - * then we'll deadlock if all other kthreads assigned - * to this channel are blocked in the channel's - * registerer, because the only thing that will unblock - * them is the xpDisconnecting callout that this - * failed kthread_run() would have made. - */ - - if (atomic_dec_return(&ch->kthreads_assigned) == 0 && - atomic_dec_return(&part->nchannels_engaged) == 0) { - indicate_partition_disengaged(part); - } - xpc_msgqueue_deref(ch); - xpc_part_deref(part); - - if (atomic_read(&ch->kthreads_assigned) < - ch->kthreads_idle_limit) { - /* - * Flag this as an error only if we have an - * insufficient #of kthreads for the channel - * to function. - */ - spin_lock_irqsave(&ch->lock, irq_flags); - XPC_DISCONNECT_CHANNEL(ch, xpLackOfResources, - &irq_flags); - spin_unlock_irqrestore(&ch->lock, irq_flags); - } - break; - } - } -} - -void -xpc_disconnect_wait(int ch_number) -{ - unsigned long irq_flags; - short partid; - struct xpc_partition *part; - struct xpc_channel *ch; - int wakeup_channel_mgr; - - /* now wait for all callouts to the caller's function to cease */ - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - if (!xpc_part_ref(part)) - continue; - - ch = &part->channels[ch_number]; - - if (!(ch->flags & XPC_C_WDISCONNECT)) { - xpc_part_deref(part); - continue; - } - - wait_for_completion(&ch->wdisconnect_wait); - - spin_lock_irqsave(&ch->lock, irq_flags); - DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED)); - wakeup_channel_mgr = 0; - - if (ch->delayed_chctl_flags) { - if (part->act_state != XPC_P_AS_DEACTIVATING) { - spin_lock(&part->chctl_lock); - part->chctl.flags[ch->number] |= - ch->delayed_chctl_flags; - spin_unlock(&part->chctl_lock); - wakeup_channel_mgr = 1; - } - ch->delayed_chctl_flags = 0; - } - - ch->flags &= ~XPC_C_WDISCONNECT; - spin_unlock_irqrestore(&ch->lock, irq_flags); - - if (wakeup_channel_mgr) - xpc_wakeup_channel_mgr(part); - - xpc_part_deref(part); - } -} - -static int -xpc_setup_partitions(void) -{ - short partid; - struct xpc_partition *part; - - xpc_partitions = kzalloc_objs(struct xpc_partition, xp_max_npartitions); - if (xpc_partitions == NULL) { - dev_err(xpc_part, "can't get memory for partition structure\n"); - return -ENOMEM; - } - - /* - * The first few fields of each entry of xpc_partitions[] need to - * be initialized now so that calls to xpc_connect() and - * xpc_disconnect() can be made prior to the activation of any remote - * partition. NOTE THAT NONE OF THE OTHER FIELDS BELONGING TO THESE - * ENTRIES ARE MEANINGFUL UNTIL AFTER AN ENTRY'S CORRESPONDING - * PARTITION HAS BEEN ACTIVATED. - */ - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - DBUG_ON((u64)part != L1_CACHE_ALIGN((u64)part)); - - part->activate_IRQ_rcvd = 0; - spin_lock_init(&part->act_lock); - part->act_state = XPC_P_AS_INACTIVE; - XPC_SET_REASON(part, 0, 0); - - timer_setup(&part->disengage_timer, - xpc_timeout_partition_disengage, 0); - - part->setup_state = XPC_P_SS_UNSET; - init_waitqueue_head(&part->teardown_wq); - atomic_set(&part->references, 0); - } - - return xpc_arch_ops.setup_partitions(); -} - -static void -xpc_teardown_partitions(void) -{ - xpc_arch_ops.teardown_partitions(); - kfree(xpc_partitions); -} - -static void -xpc_do_exit(enum xp_retval reason) -{ - short partid; - int active_part_count, printed_waiting_msg = 0; - struct xpc_partition *part; - unsigned long printmsg_time, disengage_timeout = 0; - - /* a 'rmmod XPC' and a 'reboot' cannot both end up here together */ - DBUG_ON(xpc_exiting == 1); - - /* - * Let the heartbeat checker thread and the discovery thread - * (if one is running) know that they should exit. Also wake up - * the heartbeat checker thread in case it's sleeping. - */ - xpc_exiting = 1; - wake_up_interruptible(&xpc_activate_IRQ_wq); - - /* wait for the discovery thread to exit */ - wait_for_completion(&xpc_discovery_exited); - - /* wait for the heartbeat checker thread to exit */ - wait_for_completion(&xpc_hb_checker_exited); - - /* sleep for a 1/3 of a second or so */ - (void)msleep_interruptible(300); - - /* wait for all partitions to become inactive */ - - printmsg_time = jiffies + (XPC_DEACTIVATE_PRINTMSG_INTERVAL * HZ); - xpc_disengage_timedout = 0; - - do { - active_part_count = 0; - - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - if (xpc_partition_disengaged(part) && - part->act_state == XPC_P_AS_INACTIVE) { - continue; - } - - active_part_count++; - - XPC_DEACTIVATE_PARTITION(part, reason); - - if (part->disengage_timeout > disengage_timeout) - disengage_timeout = part->disengage_timeout; - } - - if (xpc_arch_ops.any_partition_engaged()) { - if (time_is_before_jiffies(printmsg_time)) { - dev_info(xpc_part, "waiting for remote " - "partitions to deactivate, timeout in " - "%ld seconds\n", (disengage_timeout - - jiffies) / HZ); - printmsg_time = jiffies + - (XPC_DEACTIVATE_PRINTMSG_INTERVAL * HZ); - printed_waiting_msg = 1; - } - - } else if (active_part_count > 0) { - if (printed_waiting_msg) { - dev_info(xpc_part, "waiting for local partition" - " to deactivate\n"); - printed_waiting_msg = 0; - } - - } else { - if (!xpc_disengage_timedout) { - dev_info(xpc_part, "all partitions have " - "deactivated\n"); - } - break; - } - - /* sleep for a 1/3 of a second or so */ - (void)msleep_interruptible(300); - - } while (1); - - DBUG_ON(xpc_arch_ops.any_partition_engaged()); - - xpc_teardown_rsvd_page(); - - if (reason == xpUnloading) { - (void)unregister_die_notifier(&xpc_die_notifier); - (void)unregister_reboot_notifier(&xpc_reboot_notifier); - } - - /* clear the interface to XPC's functions */ - xpc_clear_interface(); - - if (xpc_sysctl) - unregister_sysctl_table(xpc_sysctl); - if (xpc_sysctl_hb) - unregister_sysctl_table(xpc_sysctl_hb); - - xpc_teardown_partitions(); - - if (is_uv_system()) - xpc_exit_uv(); -} - -/* - * This function is called when the system is being rebooted. - */ -static int -xpc_system_reboot(struct notifier_block *nb, unsigned long event, void *unused) -{ - enum xp_retval reason; - - switch (event) { - case SYS_RESTART: - reason = xpSystemReboot; - break; - case SYS_HALT: - reason = xpSystemHalt; - break; - case SYS_POWER_OFF: - reason = xpSystemPoweroff; - break; - default: - reason = xpSystemGoingDown; - } - - xpc_do_exit(reason); - return NOTIFY_DONE; -} - -/* Used to only allow one cpu to complete disconnect */ -static unsigned int xpc_die_disconnecting; - -/* - * Notify other partitions to deactivate from us by first disengaging from all - * references to our memory. - */ -static void -xpc_die_deactivate(void) -{ - struct xpc_partition *part; - short partid; - int any_engaged; - long keep_waiting; - long wait_to_print; - - if (cmpxchg(&xpc_die_disconnecting, 0, 1)) - return; - - /* keep xpc_hb_checker thread from doing anything (just in case) */ - xpc_exiting = 1; - - xpc_arch_ops.disallow_all_hbs(); /*indicate we're deactivated */ - - for (partid = 0; partid < xp_max_npartitions; partid++) { - part = &xpc_partitions[partid]; - - if (xpc_arch_ops.partition_engaged(partid) || - part->act_state != XPC_P_AS_INACTIVE) { - xpc_arch_ops.request_partition_deactivation(part); - xpc_arch_ops.indicate_partition_disengaged(part); - } - } - - /* - * Though we requested that all other partitions deactivate from us, - * we only wait until they've all disengaged or we've reached the - * defined timelimit. - * - * Given that one iteration through the following while-loop takes - * approximately 200 microseconds, calculate the #of loops to take - * before bailing and the #of loops before printing a waiting message. - */ - keep_waiting = xpc_disengage_timelimit * 1000 * 5; - wait_to_print = XPC_DEACTIVATE_PRINTMSG_INTERVAL * 1000 * 5; - - while (1) { - any_engaged = xpc_arch_ops.any_partition_engaged(); - if (!any_engaged) { - dev_info(xpc_part, "all partitions have deactivated\n"); - break; - } - - if (!keep_waiting--) { - for (partid = 0; partid < xp_max_npartitions; - partid++) { - if (xpc_arch_ops.partition_engaged(partid)) { - dev_info(xpc_part, "deactivate from " - "remote partition %d timed " - "out\n", partid); - } - } - break; - } - - if (!wait_to_print--) { - dev_info(xpc_part, "waiting for remote partitions to " - "deactivate, timeout in %ld seconds\n", - keep_waiting / (1000 * 5)); - wait_to_print = XPC_DEACTIVATE_PRINTMSG_INTERVAL * - 1000 * 5; - } - - udelay(200); - } -} - -/* - * This function is called when the system is being restarted or halted due - * to some sort of system failure. If this is the case we need to notify the - * other partitions to disengage from all references to our memory. - * This function can also be called when our heartbeater could be offlined - * for a time. In this case we need to notify other partitions to not worry - * about the lack of a heartbeat. - */ -static int -xpc_system_die(struct notifier_block *nb, unsigned long event, void *_die_args) -{ - struct die_args *die_args = _die_args; - - switch (event) { - case DIE_TRAP: - if (die_args->trapnr == X86_TRAP_DF) - xpc_die_deactivate(); - - if (((die_args->trapnr == X86_TRAP_MF) || - (die_args->trapnr == X86_TRAP_XF)) && - !user_mode(die_args->regs)) - xpc_die_deactivate(); - - break; - case DIE_INT3: - case DIE_DEBUG: - break; - case DIE_OOPS: - case DIE_GPF: - default: - xpc_die_deactivate(); - } - - return NOTIFY_DONE; -} - -static int __init -xpc_init(void) -{ - int ret; - struct task_struct *kthread; - - dev_set_name(xpc_part, "part"); - dev_set_name(xpc_chan, "chan"); - - if (is_uv_system()) { - ret = xpc_init_uv(); - - } else { - ret = -ENODEV; - } - - if (ret != 0) - return ret; - - ret = xpc_setup_partitions(); - if (ret != 0) { - dev_err(xpc_part, "can't get memory for partition structure\n"); - goto out_1; - } - - xpc_sysctl = register_sysctl("xpc", xpc_sys_xpc); - xpc_sysctl_hb = register_sysctl("xpc/hb", xpc_sys_xpc_hb); - - /* - * Fill the partition reserved page with the information needed by - * other partitions to discover we are alive and establish initial - * communications. - */ - ret = xpc_setup_rsvd_page(); - if (ret != 0) { - dev_err(xpc_part, "can't setup our reserved page\n"); - goto out_2; - } - - /* add ourselves to the reboot_notifier_list */ - ret = register_reboot_notifier(&xpc_reboot_notifier); - if (ret != 0) - dev_warn(xpc_part, "can't register reboot notifier\n"); - - /* add ourselves to the die_notifier list */ - ret = register_die_notifier(&xpc_die_notifier); - if (ret != 0) - dev_warn(xpc_part, "can't register die notifier\n"); - - /* - * The real work-horse behind xpc. This processes incoming - * interrupts and monitors remote heartbeats. - */ - kthread = kthread_run(xpc_hb_checker, NULL, XPC_HB_CHECK_THREAD_NAME); - if (IS_ERR(kthread)) { - dev_err(xpc_part, "failed while forking hb check thread\n"); - ret = -EBUSY; - goto out_3; - } - - /* - * Startup a thread that will attempt to discover other partitions to - * activate based on info provided by SAL. This new thread is short - * lived and will exit once discovery is complete. - */ - kthread = kthread_run(xpc_initiate_discovery, NULL, - XPC_DISCOVERY_THREAD_NAME); - if (IS_ERR(kthread)) { - dev_err(xpc_part, "failed while forking discovery thread\n"); - - /* mark this new thread as a non-starter */ - complete(&xpc_discovery_exited); - - xpc_do_exit(xpUnloading); - return -EBUSY; - } - - /* set the interface to point at XPC's functions */ - xpc_set_interface(xpc_initiate_connect, xpc_initiate_disconnect, - xpc_initiate_send, xpc_initiate_send_notify, - xpc_initiate_received, xpc_initiate_partid_to_nasids); - - return 0; - - /* initialization was not successful */ -out_3: - xpc_teardown_rsvd_page(); - - (void)unregister_die_notifier(&xpc_die_notifier); - (void)unregister_reboot_notifier(&xpc_reboot_notifier); -out_2: - if (xpc_sysctl_hb) - unregister_sysctl_table(xpc_sysctl_hb); - if (xpc_sysctl) - unregister_sysctl_table(xpc_sysctl); - - xpc_teardown_partitions(); -out_1: - if (is_uv_system()) - xpc_exit_uv(); - return ret; -} - -module_init(xpc_init); - -static void __exit -xpc_exit(void) -{ - xpc_do_exit(xpUnloading); -} - -module_exit(xpc_exit); - -MODULE_AUTHOR("Silicon Graphics, Inc."); -MODULE_DESCRIPTION("Cross Partition Communication (XPC) support"); -MODULE_LICENSE("GPL"); - -module_param(xpc_hb_interval, int, 0); -MODULE_PARM_DESC(xpc_hb_interval, "Number of seconds between " - "heartbeat increments."); - -module_param(xpc_hb_check_interval, int, 0); -MODULE_PARM_DESC(xpc_hb_check_interval, "Number of seconds between " - "heartbeat checks."); - -module_param(xpc_disengage_timelimit, int, 0); -MODULE_PARM_DESC(xpc_disengage_timelimit, "Number of seconds to wait " - "for disengage to complete."); - -module_param(xpc_kdebug_ignore, int, 0); -MODULE_PARM_DESC(xpc_kdebug_ignore, "Should lack of heartbeat be ignored by " - "other partitions when dropping into kdebug."); diff --git a/drivers/misc/sgi-xp/xpc_partition.c b/drivers/misc/sgi-xp/xpc_partition.c deleted file mode 100644 index d0467010558c..000000000000 --- a/drivers/misc/sgi-xp/xpc_partition.c +++ /dev/null @@ -1,545 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition Communication (XPC) partition support. - * - * This is the part of XPC that detects the presence/absence of - * other partitions. It provides a heartbeat and monitors the - * heartbeats of other partitions. - * - */ - -#include -#include -#include -#include "xpc.h" -#include - -/* XPC is exiting flag */ -int xpc_exiting; - -/* this partition's reserved page pointers */ -struct xpc_rsvd_page *xpc_rsvd_page; -static unsigned long *xpc_part_nasids; -unsigned long *xpc_mach_nasids; - -static int xpc_nasid_mask_nbytes; /* #of bytes in nasid mask */ -int xpc_nasid_mask_nlongs; /* #of longs in nasid mask */ - -struct xpc_partition *xpc_partitions; - -/* - * Guarantee that the kmalloc'd memory is cacheline aligned. - */ -void * -xpc_kmalloc_cacheline_aligned(size_t size, gfp_t flags, void **base) -{ - /* see if kmalloc will give us cachline aligned memory by default */ - *base = kmalloc(size, flags); - if (*base == NULL) - return NULL; - - if ((u64)*base == L1_CACHE_ALIGN((u64)*base)) - return *base; - - kfree(*base); - - /* nope, we'll have to do it ourselves */ - *base = kmalloc(size + L1_CACHE_BYTES, flags); - if (*base == NULL) - return NULL; - - return (void *)L1_CACHE_ALIGN((u64)*base); -} - -/* - * Given a nasid, get the physical address of the partition's reserved page - * for that nasid. This function returns 0 on any error. - */ -static unsigned long -xpc_get_rsvd_page_pa(int nasid) -{ - enum xp_retval ret; - u64 cookie = 0; - unsigned long rp_pa = nasid; /* seed with nasid */ - size_t len = 0; - size_t buf_len = 0; - void *buf = NULL; - void *buf_base = NULL; - enum xp_retval (*get_partition_rsvd_page_pa) - (void *, u64 *, unsigned long *, size_t *) = - xpc_arch_ops.get_partition_rsvd_page_pa; - - while (1) { - - /* !!! rp_pa will need to be _gpa on UV. - * ??? So do we save it into the architecture specific parts - * ??? of the xpc_partition structure? Do we rename this - * ??? function or have two versions? Rename rp_pa for UV to - * ??? rp_gpa? - */ - ret = get_partition_rsvd_page_pa(buf, &cookie, &rp_pa, &len); - - dev_dbg(xpc_part, "SAL returned with ret=%d, cookie=0x%016lx, " - "address=0x%016lx, len=0x%016lx\n", ret, - (unsigned long)cookie, rp_pa, len); - - if (ret != xpNeedMoreInfo) - break; - - if (len > buf_len) { - kfree(buf_base); - buf_len = L1_CACHE_ALIGN(len); - buf = xpc_kmalloc_cacheline_aligned(buf_len, GFP_KERNEL, - &buf_base); - if (buf_base == NULL) { - dev_err(xpc_part, "unable to kmalloc " - "len=0x%016lx\n", buf_len); - ret = xpNoMemory; - break; - } - } - - ret = xp_remote_memcpy(xp_pa(buf), rp_pa, len); - if (ret != xpSuccess) { - dev_dbg(xpc_part, "xp_remote_memcpy failed %d\n", ret); - break; - } - } - - kfree(buf_base); - - if (ret != xpSuccess) - rp_pa = 0; - - dev_dbg(xpc_part, "reserved page at phys address 0x%016lx\n", rp_pa); - return rp_pa; -} - -/* - * Fill the partition reserved page with the information needed by - * other partitions to discover we are alive and establish initial - * communications. - */ -int -xpc_setup_rsvd_page(void) -{ - int ret; - struct xpc_rsvd_page *rp; - unsigned long rp_pa; - unsigned long new_ts_jiffies; - - /* get the local reserved page's address */ - - preempt_disable(); - rp_pa = xpc_get_rsvd_page_pa(xp_cpu_to_nasid(smp_processor_id())); - preempt_enable(); - if (rp_pa == 0) { - dev_err(xpc_part, "SAL failed to locate the reserved page\n"); - return -ESRCH; - } - rp = (struct xpc_rsvd_page *)__va(xp_socket_pa(rp_pa)); - - if (rp->SAL_version < 3) { - /* SAL_versions < 3 had a SAL_partid defined as a u8 */ - rp->SAL_partid &= 0xff; - } - BUG_ON(rp->SAL_partid != xp_partition_id); - - if (rp->SAL_partid < 0 || rp->SAL_partid >= xp_max_npartitions) { - dev_err(xpc_part, "the reserved page's partid of %d is outside " - "supported range (< 0 || >= %d)\n", rp->SAL_partid, - xp_max_npartitions); - return -EINVAL; - } - - rp->version = XPC_RP_VERSION; - rp->max_npartitions = xp_max_npartitions; - - /* establish the actual sizes of the nasid masks */ - if (rp->SAL_version == 1) { - /* SAL_version 1 didn't set the nasids_size field */ - rp->SAL_nasids_size = 128; - } - xpc_nasid_mask_nbytes = rp->SAL_nasids_size; - xpc_nasid_mask_nlongs = BITS_TO_LONGS(rp->SAL_nasids_size * - BITS_PER_BYTE); - - /* setup the pointers to the various items in the reserved page */ - xpc_part_nasids = XPC_RP_PART_NASIDS(rp); - xpc_mach_nasids = XPC_RP_MACH_NASIDS(rp); - - ret = xpc_arch_ops.setup_rsvd_page(rp); - if (ret != 0) - return ret; - - /* - * Set timestamp of when reserved page was setup by XPC. - * This signifies to the remote partition that our reserved - * page is initialized. - */ - new_ts_jiffies = jiffies; - if (new_ts_jiffies == 0 || new_ts_jiffies == rp->ts_jiffies) - new_ts_jiffies++; - rp->ts_jiffies = new_ts_jiffies; - - xpc_rsvd_page = rp; - return 0; -} - -void -xpc_teardown_rsvd_page(void) -{ - /* a zero timestamp indicates our rsvd page is not initialized */ - xpc_rsvd_page->ts_jiffies = 0; -} - -/* - * Get a copy of a portion of the remote partition's rsvd page. - * - * remote_rp points to a buffer that is cacheline aligned for BTE copies and - * is large enough to contain a copy of their reserved page header and - * part_nasids mask. - */ -enum xp_retval -xpc_get_remote_rp(int nasid, unsigned long *discovered_nasids, - struct xpc_rsvd_page *remote_rp, unsigned long *remote_rp_pa) -{ - int l; - enum xp_retval ret; - - /* get the reserved page's physical address */ - - *remote_rp_pa = xpc_get_rsvd_page_pa(nasid); - if (*remote_rp_pa == 0) - return xpNoRsvdPageAddr; - - /* pull over the reserved page header and part_nasids mask */ - ret = xp_remote_memcpy(xp_pa(remote_rp), *remote_rp_pa, - XPC_RP_HEADER_SIZE + xpc_nasid_mask_nbytes); - if (ret != xpSuccess) - return ret; - - if (discovered_nasids != NULL) { - unsigned long *remote_part_nasids = - XPC_RP_PART_NASIDS(remote_rp); - - for (l = 0; l < xpc_nasid_mask_nlongs; l++) - discovered_nasids[l] |= remote_part_nasids[l]; - } - - /* zero timestamp indicates the reserved page has not been setup */ - if (remote_rp->ts_jiffies == 0) - return xpRsvdPageNotSet; - - if (XPC_VERSION_MAJOR(remote_rp->version) != - XPC_VERSION_MAJOR(XPC_RP_VERSION)) { - return xpBadVersion; - } - - /* check that both remote and local partids are valid for each side */ - if (remote_rp->SAL_partid < 0 || - remote_rp->SAL_partid >= xp_max_npartitions || - remote_rp->max_npartitions <= xp_partition_id) { - return xpInvalidPartid; - } - - if (remote_rp->SAL_partid == xp_partition_id) - return xpLocalPartid; - - return xpSuccess; -} - -/* - * See if the other side has responded to a partition deactivate request - * from us. Though we requested the remote partition to deactivate with regard - * to us, we really only need to wait for the other side to disengage from us. - */ -static int __xpc_partition_disengaged(struct xpc_partition *part, - bool from_timer) -{ - short partid = XPC_PARTID(part); - int disengaged; - - disengaged = !xpc_arch_ops.partition_engaged(partid); - if (part->disengage_timeout) { - if (!disengaged) { - if (time_is_after_jiffies(part->disengage_timeout)) { - /* timelimit hasn't been reached yet */ - return 0; - } - - /* - * Other side hasn't responded to our deactivate - * request in a timely fashion, so assume it's dead. - */ - - dev_info(xpc_part, "deactivate request to remote " - "partition %d timed out\n", partid); - xpc_disengage_timedout = 1; - xpc_arch_ops.assume_partition_disengaged(partid); - disengaged = 1; - } - part->disengage_timeout = 0; - - /* Cancel the timer function if not called from it */ - if (!from_timer) - timer_delete_sync(&part->disengage_timer); - - DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING && - part->act_state != XPC_P_AS_INACTIVE); - if (part->act_state != XPC_P_AS_INACTIVE) - xpc_wakeup_channel_mgr(part); - - xpc_arch_ops.cancel_partition_deactivation_request(part); - } - return disengaged; -} - -int xpc_partition_disengaged(struct xpc_partition *part) -{ - return __xpc_partition_disengaged(part, false); -} - -int xpc_partition_disengaged_from_timer(struct xpc_partition *part) -{ - return __xpc_partition_disengaged(part, true); -} - -/* - * Mark specified partition as active. - */ -enum xp_retval -xpc_mark_partition_active(struct xpc_partition *part) -{ - unsigned long irq_flags; - enum xp_retval ret; - - dev_dbg(xpc_part, "setting partition %d to ACTIVE\n", XPC_PARTID(part)); - - spin_lock_irqsave(&part->act_lock, irq_flags); - if (part->act_state == XPC_P_AS_ACTIVATING) { - part->act_state = XPC_P_AS_ACTIVE; - ret = xpSuccess; - } else { - DBUG_ON(part->reason == xpSuccess); - ret = part->reason; - } - spin_unlock_irqrestore(&part->act_lock, irq_flags); - - return ret; -} - -/* - * Start the process of deactivating the specified partition. - */ -void -xpc_deactivate_partition(const int line, struct xpc_partition *part, - enum xp_retval reason) -{ - unsigned long irq_flags; - - spin_lock_irqsave(&part->act_lock, irq_flags); - - if (part->act_state == XPC_P_AS_INACTIVE) { - XPC_SET_REASON(part, reason, line); - spin_unlock_irqrestore(&part->act_lock, irq_flags); - if (reason == xpReactivating) { - /* we interrupt ourselves to reactivate partition */ - xpc_arch_ops.request_partition_reactivation(part); - } - return; - } - if (part->act_state == XPC_P_AS_DEACTIVATING) { - if ((part->reason == xpUnloading && reason != xpUnloading) || - reason == xpReactivating) { - XPC_SET_REASON(part, reason, line); - } - spin_unlock_irqrestore(&part->act_lock, irq_flags); - return; - } - - part->act_state = XPC_P_AS_DEACTIVATING; - XPC_SET_REASON(part, reason, line); - - spin_unlock_irqrestore(&part->act_lock, irq_flags); - - /* ask remote partition to deactivate with regard to us */ - xpc_arch_ops.request_partition_deactivation(part); - - /* set a timelimit on the disengage phase of the deactivation request */ - part->disengage_timeout = jiffies + (xpc_disengage_timelimit * HZ); - part->disengage_timer.expires = part->disengage_timeout; - add_timer(&part->disengage_timer); - - dev_dbg(xpc_part, "bringing partition %d down, reason = %d\n", - XPC_PARTID(part), reason); - - xpc_partition_going_down(part, reason); -} - -/* - * Mark specified partition as inactive. - */ -void -xpc_mark_partition_inactive(struct xpc_partition *part) -{ - unsigned long irq_flags; - - dev_dbg(xpc_part, "setting partition %d to INACTIVE\n", - XPC_PARTID(part)); - - spin_lock_irqsave(&part->act_lock, irq_flags); - part->act_state = XPC_P_AS_INACTIVE; - spin_unlock_irqrestore(&part->act_lock, irq_flags); - part->remote_rp_pa = 0; -} - -/* - * SAL has provided a partition and machine mask. The partition mask - * contains a bit for each even nasid in our partition. The machine - * mask contains a bit for each even nasid in the entire machine. - * - * Using those two bit arrays, we can determine which nasids are - * known in the machine. Each should also have a reserved page - * initialized if they are available for partitioning. - */ -void -xpc_discovery(void) -{ - void *remote_rp_base; - struct xpc_rsvd_page *remote_rp; - unsigned long remote_rp_pa; - int region; - int region_size; - int max_regions; - int nasid; - unsigned long *discovered_nasids; - enum xp_retval ret; - - remote_rp = xpc_kmalloc_cacheline_aligned(XPC_RP_HEADER_SIZE + - xpc_nasid_mask_nbytes, - GFP_KERNEL, &remote_rp_base); - if (remote_rp == NULL) - return; - - discovered_nasids = kcalloc(xpc_nasid_mask_nlongs, sizeof(long), - GFP_KERNEL); - if (discovered_nasids == NULL) { - kfree(remote_rp_base); - return; - } - - /* - * The term 'region' in this context refers to the minimum number of - * nodes that can comprise an access protection grouping. The access - * protection is in regards to memory, IOI and IPI. - */ - region_size = xp_region_size; - - if (is_uv_system()) - max_regions = 256; - else { - max_regions = 64; - - switch (region_size) { - case 128: - max_regions *= 2; - fallthrough; - case 64: - max_regions *= 2; - fallthrough; - case 32: - max_regions *= 2; - region_size = 16; - } - } - - for (region = 0; region < max_regions; region++) { - - if (xpc_exiting) - break; - - dev_dbg(xpc_part, "searching region %d\n", region); - - for (nasid = (region * region_size * 2); - nasid < ((region + 1) * region_size * 2); nasid += 2) { - - if (xpc_exiting) - break; - - dev_dbg(xpc_part, "checking nasid %d\n", nasid); - - if (test_bit(nasid / 2, xpc_part_nasids)) { - dev_dbg(xpc_part, "PROM indicates Nasid %d is " - "part of the local partition; skipping " - "region\n", nasid); - break; - } - - if (!(test_bit(nasid / 2, xpc_mach_nasids))) { - dev_dbg(xpc_part, "PROM indicates Nasid %d was " - "not on Numa-Link network at reset\n", - nasid); - continue; - } - - if (test_bit(nasid / 2, discovered_nasids)) { - dev_dbg(xpc_part, "Nasid %d is part of a " - "partition which was previously " - "discovered\n", nasid); - continue; - } - - /* pull over the rsvd page header & part_nasids mask */ - - ret = xpc_get_remote_rp(nasid, discovered_nasids, - remote_rp, &remote_rp_pa); - if (ret != xpSuccess) { - dev_dbg(xpc_part, "unable to get reserved page " - "from nasid %d, reason=%d\n", nasid, - ret); - - if (ret == xpLocalPartid) - break; - - continue; - } - - xpc_arch_ops.request_partition_activation(remote_rp, - remote_rp_pa, nasid); - } - } - - kfree(discovered_nasids); - kfree(remote_rp_base); -} - -/* - * Given a partid, get the nasids owned by that partition from the - * remote partition's reserved page. - */ -enum xp_retval -xpc_initiate_partid_to_nasids(short partid, void *nasid_mask) -{ - struct xpc_partition *part; - unsigned long part_nasid_pa; - - part = &xpc_partitions[partid]; - if (part->remote_rp_pa == 0) - return xpPartitionDown; - - memset(nasid_mask, 0, xpc_nasid_mask_nbytes); - - part_nasid_pa = (unsigned long)XPC_RP_PART_NASIDS(part->remote_rp_pa); - - return xp_remote_memcpy(xp_pa(nasid_mask), part_nasid_pa, - xpc_nasid_mask_nbytes); -} diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c deleted file mode 100644 index 772c78726893..000000000000 --- a/drivers/misc/sgi-xp/xpc_uv.c +++ /dev/null @@ -1,1728 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2008-2009 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * Cross Partition Communication (XPC) uv-based functions. - * - * Architecture specific implementation of common functions. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../sgi-gru/gru.h" -#include "../sgi-gru/grukservices.h" -#include "xpc.h" - -static struct xpc_heartbeat_uv *xpc_heartbeat_uv; - -#define XPC_ACTIVATE_MSG_SIZE_UV (1 * GRU_CACHE_LINE_BYTES) -#define XPC_ACTIVATE_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \ - XPC_ACTIVATE_MSG_SIZE_UV) -#define XPC_ACTIVATE_IRQ_NAME "xpc_activate" - -#define XPC_NOTIFY_MSG_SIZE_UV (2 * GRU_CACHE_LINE_BYTES) -#define XPC_NOTIFY_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \ - XPC_NOTIFY_MSG_SIZE_UV) -#define XPC_NOTIFY_IRQ_NAME "xpc_notify" - -static int xpc_mq_node = NUMA_NO_NODE; - -static struct xpc_gru_mq_uv *xpc_activate_mq_uv; -static struct xpc_gru_mq_uv *xpc_notify_mq_uv; - -static int -xpc_setup_partitions_uv(void) -{ - short partid; - struct xpc_partition_uv *part_uv; - - for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) { - part_uv = &xpc_partitions[partid].sn.uv; - - mutex_init(&part_uv->cached_activate_gru_mq_desc_mutex); - spin_lock_init(&part_uv->flags_lock); - part_uv->remote_act_state = XPC_P_AS_INACTIVE; - } - return 0; -} - -static void -xpc_teardown_partitions_uv(void) -{ - short partid; - struct xpc_partition_uv *part_uv; - unsigned long irq_flags; - - for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) { - part_uv = &xpc_partitions[partid].sn.uv; - - if (part_uv->cached_activate_gru_mq_desc != NULL) { - mutex_lock(&part_uv->cached_activate_gru_mq_desc_mutex); - spin_lock_irqsave(&part_uv->flags_lock, irq_flags); - part_uv->flags &= ~XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV; - spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags); - kfree(part_uv->cached_activate_gru_mq_desc); - part_uv->cached_activate_gru_mq_desc = NULL; - mutex_unlock(&part_uv-> - cached_activate_gru_mq_desc_mutex); - } - } -} - -static int -xpc_get_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq, int cpu, char *irq_name) -{ - int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade); - - mq->irq = uv_setup_irq(irq_name, cpu, mq->mmr_blade, mq->mmr_offset, - UV_AFFINITY_CPU); - if (mq->irq < 0) - return mq->irq; - - mq->mmr_value = uv_read_global_mmr64(mmr_pnode, mq->mmr_offset); - - return 0; -} - -static void -xpc_release_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq) -{ - uv_teardown_irq(mq->irq); -} - -static int -xpc_gru_mq_watchlist_alloc_uv(struct xpc_gru_mq_uv *mq) -{ - int ret; - - ret = uv_bios_mq_watchlist_alloc(uv_gpa(mq->address), - mq->order, &mq->mmr_offset); - if (ret < 0) { - dev_err(xpc_part, "uv_bios_mq_watchlist_alloc() failed, " - "ret=%d\n", ret); - return ret; - } - - mq->watchlist_num = ret; - return 0; -} - -static void -xpc_gru_mq_watchlist_free_uv(struct xpc_gru_mq_uv *mq) -{ - int ret; - int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade); - - ret = uv_bios_mq_watchlist_free(mmr_pnode, mq->watchlist_num); - BUG_ON(ret != BIOS_STATUS_SUCCESS); -} - -static struct xpc_gru_mq_uv * -xpc_create_gru_mq_uv(unsigned int mq_size, int cpu, char *irq_name, - irq_handler_t irq_handler) -{ - enum xp_retval xp_ret; - int ret; - int nid; - int nasid; - int pg_order; - struct page *page; - struct xpc_gru_mq_uv *mq; - struct uv_IO_APIC_route_entry *mmr_value; - - mq = kmalloc_obj(struct xpc_gru_mq_uv); - if (mq == NULL) { - dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to kmalloc() " - "a xpc_gru_mq_uv structure\n"); - ret = -ENOMEM; - goto out_0; - } - - mq->gru_mq_desc = kzalloc_obj(struct gru_message_queue_desc); - if (mq->gru_mq_desc == NULL) { - dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to kmalloc() " - "a gru_message_queue_desc structure\n"); - ret = -ENOMEM; - goto out_1; - } - - pg_order = get_order(mq_size); - mq->order = pg_order + PAGE_SHIFT; - mq_size = 1UL << mq->order; - - mq->mmr_blade = uv_cpu_to_blade_id(cpu); - - nid = cpu_to_node(cpu); - page = __alloc_pages_node(nid, - GFP_KERNEL | __GFP_ZERO | __GFP_THISNODE, - pg_order); - if (page == NULL) { - dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to alloc %d " - "bytes of memory on nid=%d for GRU mq\n", mq_size, nid); - ret = -ENOMEM; - goto out_2; - } - mq->address = page_address(page); - - /* enable generation of irq when GRU mq operation occurs to this mq */ - ret = xpc_gru_mq_watchlist_alloc_uv(mq); - if (ret != 0) - goto out_3; - - ret = xpc_get_gru_mq_irq_uv(mq, cpu, irq_name); - if (ret != 0) - goto out_4; - - ret = request_irq(mq->irq, irq_handler, 0, irq_name, NULL); - if (ret != 0) { - dev_err(xpc_part, "request_irq(irq=%d) returned error=%d\n", - mq->irq, -ret); - goto out_5; - } - - nasid = UV_PNODE_TO_NASID(uv_cpu_to_pnode(cpu)); - - mmr_value = (struct uv_IO_APIC_route_entry *)&mq->mmr_value; - ret = gru_create_message_queue(mq->gru_mq_desc, mq->address, mq_size, - nasid, mmr_value->vector, mmr_value->dest); - if (ret != 0) { - dev_err(xpc_part, "gru_create_message_queue() returned " - "error=%d\n", ret); - ret = -EINVAL; - goto out_6; - } - - /* allow other partitions to access this GRU mq */ - xp_ret = xp_expand_memprotect(xp_pa(mq->address), mq_size); - if (xp_ret != xpSuccess) { - ret = -EACCES; - goto out_6; - } - - return mq; - - /* something went wrong */ -out_6: - free_irq(mq->irq, NULL); -out_5: - xpc_release_gru_mq_irq_uv(mq); -out_4: - xpc_gru_mq_watchlist_free_uv(mq); -out_3: - free_pages((unsigned long)mq->address, pg_order); -out_2: - kfree(mq->gru_mq_desc); -out_1: - kfree(mq); -out_0: - return ERR_PTR(ret); -} - -static void -xpc_destroy_gru_mq_uv(struct xpc_gru_mq_uv *mq) -{ - unsigned int mq_size; - int pg_order; - int ret; - - /* disallow other partitions to access GRU mq */ - mq_size = 1UL << mq->order; - ret = xp_restrict_memprotect(xp_pa(mq->address), mq_size); - BUG_ON(ret != xpSuccess); - - /* unregister irq handler and release mq irq/vector mapping */ - free_irq(mq->irq, NULL); - xpc_release_gru_mq_irq_uv(mq); - - /* disable generation of irq when GRU mq op occurs to this mq */ - xpc_gru_mq_watchlist_free_uv(mq); - - pg_order = mq->order - PAGE_SHIFT; - free_pages((unsigned long)mq->address, pg_order); - - kfree(mq); -} - -static enum xp_retval -xpc_send_gru_msg(struct gru_message_queue_desc *gru_mq_desc, void *msg, - size_t msg_size) -{ - enum xp_retval xp_ret; - int ret; - - while (1) { - ret = gru_send_message_gpa(gru_mq_desc, msg, msg_size); - if (ret == MQE_OK) { - xp_ret = xpSuccess; - break; - } - - if (ret == MQE_QUEUE_FULL) { - dev_dbg(xpc_chan, "gru_send_message_gpa() returned " - "error=MQE_QUEUE_FULL\n"); - /* !!! handle QLimit reached; delay & try again */ - /* ??? Do we add a limit to the number of retries? */ - (void)msleep_interruptible(10); - } else if (ret == MQE_CONGESTION) { - dev_dbg(xpc_chan, "gru_send_message_gpa() returned " - "error=MQE_CONGESTION\n"); - /* !!! handle LB Overflow; simply try again */ - /* ??? Do we add a limit to the number of retries? */ - } else { - /* !!! Currently this is MQE_UNEXPECTED_CB_ERR */ - dev_err(xpc_chan, "gru_send_message_gpa() returned " - "error=%d\n", ret); - xp_ret = xpGruSendMqError; - break; - } - } - return xp_ret; -} - -static void -xpc_process_activate_IRQ_rcvd_uv(void) -{ - unsigned long irq_flags; - short partid; - struct xpc_partition *part; - u8 act_state_req; - - DBUG_ON(xpc_activate_IRQ_rcvd == 0); - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) { - part = &xpc_partitions[partid]; - - if (part->sn.uv.act_state_req == 0) - continue; - - xpc_activate_IRQ_rcvd--; - BUG_ON(xpc_activate_IRQ_rcvd < 0); - - act_state_req = part->sn.uv.act_state_req; - part->sn.uv.act_state_req = 0; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - if (act_state_req == XPC_P_ASR_ACTIVATE_UV) { - if (part->act_state == XPC_P_AS_INACTIVE) - xpc_activate_partition(part); - else if (part->act_state == XPC_P_AS_DEACTIVATING) - XPC_DEACTIVATE_PARTITION(part, xpReactivating); - - } else if (act_state_req == XPC_P_ASR_REACTIVATE_UV) { - if (part->act_state == XPC_P_AS_INACTIVE) - xpc_activate_partition(part); - else - XPC_DEACTIVATE_PARTITION(part, xpReactivating); - - } else if (act_state_req == XPC_P_ASR_DEACTIVATE_UV) { - XPC_DEACTIVATE_PARTITION(part, part->sn.uv.reason); - - } else { - BUG(); - } - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (xpc_activate_IRQ_rcvd == 0) - break; - } - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - -} - -static void -xpc_handle_activate_mq_msg_uv(struct xpc_partition *part, - struct xpc_activate_mq_msghdr_uv *msg_hdr, - int part_setup, - int *wakeup_hb_checker) -{ - unsigned long irq_flags; - struct xpc_partition_uv *part_uv = &part->sn.uv; - struct xpc_openclose_args *args; - - part_uv->remote_act_state = msg_hdr->act_state; - - switch (msg_hdr->type) { - case XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV: - /* syncing of remote_act_state was just done above */ - break; - - case XPC_ACTIVATE_MQ_MSG_ACTIVATE_REQ_UV: { - struct xpc_activate_mq_msg_activate_req_uv *msg; - - /* - * ??? Do we deal here with ts_jiffies being different - * ??? if act_state != XPC_P_AS_INACTIVE instead of - * ??? below? - */ - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_activate_req_uv, hdr); - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = XPC_P_ASR_ACTIVATE_UV; - part->remote_rp_pa = msg->rp_gpa; /* !!! _pa is _gpa */ - part->remote_rp_ts_jiffies = msg_hdr->rp_ts_jiffies; - part_uv->heartbeat_gpa = msg->heartbeat_gpa; - - if (msg->activate_gru_mq_desc_gpa != - part_uv->activate_gru_mq_desc_gpa) { - spin_lock(&part_uv->flags_lock); - part_uv->flags &= ~XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV; - spin_unlock(&part_uv->flags_lock); - part_uv->activate_gru_mq_desc_gpa = - msg->activate_gru_mq_desc_gpa; - } - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - (*wakeup_hb_checker)++; - break; - } - case XPC_ACTIVATE_MQ_MSG_DEACTIVATE_REQ_UV: { - struct xpc_activate_mq_msg_deactivate_req_uv *msg; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_deactivate_req_uv, hdr); - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = XPC_P_ASR_DEACTIVATE_UV; - part_uv->reason = msg->reason; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - (*wakeup_hb_checker)++; - return; - } - case XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREQUEST_UV: { - struct xpc_activate_mq_msg_chctl_closerequest_uv *msg; - - if (!part_setup) - break; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_chctl_closerequest_uv, - hdr); - args = &part->remote_openclose_args[msg->ch_number]; - args->reason = msg->reason; - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[msg->ch_number] |= XPC_CHCTL_CLOSEREQUEST; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); - break; - } - case XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREPLY_UV: { - struct xpc_activate_mq_msg_chctl_closereply_uv *msg; - - if (!part_setup) - break; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_chctl_closereply_uv, - hdr); - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[msg->ch_number] |= XPC_CHCTL_CLOSEREPLY; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); - break; - } - case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREQUEST_UV: { - struct xpc_activate_mq_msg_chctl_openrequest_uv *msg; - - if (!part_setup) - break; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_chctl_openrequest_uv, - hdr); - args = &part->remote_openclose_args[msg->ch_number]; - args->entry_size = msg->entry_size; - args->local_nentries = msg->local_nentries; - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[msg->ch_number] |= XPC_CHCTL_OPENREQUEST; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); - break; - } - case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREPLY_UV: { - struct xpc_activate_mq_msg_chctl_openreply_uv *msg; - - if (!part_setup) - break; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_chctl_openreply_uv, hdr); - args = &part->remote_openclose_args[msg->ch_number]; - args->remote_nentries = msg->remote_nentries; - args->local_nentries = msg->local_nentries; - args->local_msgqueue_pa = msg->notify_gru_mq_desc_gpa; - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[msg->ch_number] |= XPC_CHCTL_OPENREPLY; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); - break; - } - case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENCOMPLETE_UV: { - struct xpc_activate_mq_msg_chctl_opencomplete_uv *msg; - - if (!part_setup) - break; - - msg = container_of(msg_hdr, struct - xpc_activate_mq_msg_chctl_opencomplete_uv, hdr); - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[msg->ch_number] |= XPC_CHCTL_OPENCOMPLETE; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); - } - fallthrough; - case XPC_ACTIVATE_MQ_MSG_MARK_ENGAGED_UV: - spin_lock_irqsave(&part_uv->flags_lock, irq_flags); - part_uv->flags |= XPC_P_ENGAGED_UV; - spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags); - break; - - case XPC_ACTIVATE_MQ_MSG_MARK_DISENGAGED_UV: - spin_lock_irqsave(&part_uv->flags_lock, irq_flags); - part_uv->flags &= ~XPC_P_ENGAGED_UV; - spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags); - break; - - default: - dev_err(xpc_part, "received unknown activate_mq msg type=%d " - "from partition=%d\n", msg_hdr->type, XPC_PARTID(part)); - - /* get hb checker to deactivate from the remote partition */ - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = XPC_P_ASR_DEACTIVATE_UV; - part_uv->reason = xpBadMsgType; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - (*wakeup_hb_checker)++; - return; - } - - if (msg_hdr->rp_ts_jiffies != part->remote_rp_ts_jiffies && - part->remote_rp_ts_jiffies != 0) { - /* - * ??? Does what we do here need to be sensitive to - * ??? act_state or remote_act_state? - */ - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = XPC_P_ASR_REACTIVATE_UV; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - (*wakeup_hb_checker)++; - } -} - -static irqreturn_t -xpc_handle_activate_IRQ_uv(int irq, void *dev_id) -{ - struct xpc_activate_mq_msghdr_uv *msg_hdr; - short partid; - struct xpc_partition *part; - int wakeup_hb_checker = 0; - int part_referenced; - - while (1) { - msg_hdr = gru_get_next_message(xpc_activate_mq_uv->gru_mq_desc); - if (msg_hdr == NULL) - break; - - partid = msg_hdr->partid; - if (partid < 0 || partid >= XP_MAX_NPARTITIONS_UV) { - dev_err(xpc_part, "xpc_handle_activate_IRQ_uv() " - "received invalid partid=0x%x in message\n", - partid); - } else { - part = &xpc_partitions[partid]; - - part_referenced = xpc_part_ref(part); - xpc_handle_activate_mq_msg_uv(part, msg_hdr, - part_referenced, - &wakeup_hb_checker); - if (part_referenced) - xpc_part_deref(part); - } - - gru_free_message(xpc_activate_mq_uv->gru_mq_desc, msg_hdr); - } - - if (wakeup_hb_checker) - wake_up_interruptible(&xpc_activate_IRQ_wq); - - return IRQ_HANDLED; -} - -static enum xp_retval -xpc_cache_remote_gru_mq_desc_uv(struct gru_message_queue_desc *gru_mq_desc, - unsigned long gru_mq_desc_gpa) -{ - enum xp_retval ret; - - ret = xp_remote_memcpy(uv_gpa(gru_mq_desc), gru_mq_desc_gpa, - sizeof(struct gru_message_queue_desc)); - if (ret == xpSuccess) - gru_mq_desc->mq = NULL; - - return ret; -} - -static enum xp_retval -xpc_send_activate_IRQ_uv(struct xpc_partition *part, void *msg, size_t msg_size, - int msg_type) -{ - struct xpc_activate_mq_msghdr_uv *msg_hdr = msg; - struct xpc_partition_uv *part_uv = &part->sn.uv; - struct gru_message_queue_desc *gru_mq_desc; - unsigned long irq_flags; - enum xp_retval ret; - - DBUG_ON(msg_size > XPC_ACTIVATE_MSG_SIZE_UV); - - msg_hdr->type = msg_type; - msg_hdr->partid = xp_partition_id; - msg_hdr->act_state = part->act_state; - msg_hdr->rp_ts_jiffies = xpc_rsvd_page->ts_jiffies; - - mutex_lock(&part_uv->cached_activate_gru_mq_desc_mutex); -again: - if (!(part_uv->flags & XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV)) { - gru_mq_desc = part_uv->cached_activate_gru_mq_desc; - if (gru_mq_desc == NULL) { - gru_mq_desc = kmalloc_obj(struct gru_message_queue_desc, - GFP_ATOMIC); - if (gru_mq_desc == NULL) { - ret = xpNoMemory; - goto done; - } - part_uv->cached_activate_gru_mq_desc = gru_mq_desc; - } - - ret = xpc_cache_remote_gru_mq_desc_uv(gru_mq_desc, - part_uv-> - activate_gru_mq_desc_gpa); - if (ret != xpSuccess) - goto done; - - spin_lock_irqsave(&part_uv->flags_lock, irq_flags); - part_uv->flags |= XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV; - spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags); - } - - /* ??? Is holding a spin_lock (ch->lock) during this call a bad idea? */ - ret = xpc_send_gru_msg(part_uv->cached_activate_gru_mq_desc, msg, - msg_size); - if (ret != xpSuccess) { - smp_rmb(); /* ensure a fresh copy of part_uv->flags */ - if (!(part_uv->flags & XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV)) - goto again; - } -done: - mutex_unlock(&part_uv->cached_activate_gru_mq_desc_mutex); - return ret; -} - -static void -xpc_send_activate_IRQ_part_uv(struct xpc_partition *part, void *msg, - size_t msg_size, int msg_type) -{ - enum xp_retval ret; - - ret = xpc_send_activate_IRQ_uv(part, msg, msg_size, msg_type); - if (unlikely(ret != xpSuccess)) - XPC_DEACTIVATE_PARTITION(part, ret); -} - -static void -xpc_send_activate_IRQ_ch_uv(struct xpc_channel *ch, unsigned long *irq_flags, - void *msg, size_t msg_size, int msg_type) -{ - struct xpc_partition *part = &xpc_partitions[ch->partid]; - enum xp_retval ret; - - ret = xpc_send_activate_IRQ_uv(part, msg, msg_size, msg_type); - if (unlikely(ret != xpSuccess)) { - if (irq_flags != NULL) - spin_unlock_irqrestore(&ch->lock, *irq_flags); - - XPC_DEACTIVATE_PARTITION(part, ret); - - if (irq_flags != NULL) - spin_lock_irqsave(&ch->lock, *irq_flags); - } -} - -static void -xpc_send_local_activate_IRQ_uv(struct xpc_partition *part, int act_state_req) -{ - unsigned long irq_flags; - struct xpc_partition_uv *part_uv = &part->sn.uv; - - /* - * !!! Make our side think that the remote partition sent an activate - * !!! mq message our way by doing what the activate IRQ handler would - * !!! do had one really been sent. - */ - - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = act_state_req; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - wake_up_interruptible(&xpc_activate_IRQ_wq); -} - -static enum xp_retval -xpc_get_partition_rsvd_page_pa_uv(void *buf, u64 *cookie, unsigned long *rp_pa, - size_t *len) -{ - s64 status; - enum xp_retval ret; - - status = uv_bios_reserved_page_pa((u64)buf, cookie, (u64 *)rp_pa, - (u64 *)len); - if (status == BIOS_STATUS_SUCCESS) - ret = xpSuccess; - else if (status == BIOS_STATUS_MORE_PASSES) - ret = xpNeedMoreInfo; - else - ret = xpBiosError; - - return ret; -} - -static int -xpc_setup_rsvd_page_uv(struct xpc_rsvd_page *rp) -{ - xpc_heartbeat_uv = - &xpc_partitions[sn_partition_id].sn.uv.cached_heartbeat; - rp->sn.uv.heartbeat_gpa = uv_gpa(xpc_heartbeat_uv); - rp->sn.uv.activate_gru_mq_desc_gpa = - uv_gpa(xpc_activate_mq_uv->gru_mq_desc); - return 0; -} - -static void -xpc_allow_hb_uv(short partid) -{ -} - -static void -xpc_disallow_hb_uv(short partid) -{ -} - -static void -xpc_disallow_all_hbs_uv(void) -{ -} - -static void -xpc_increment_heartbeat_uv(void) -{ - xpc_heartbeat_uv->value++; -} - -static void -xpc_offline_heartbeat_uv(void) -{ - xpc_increment_heartbeat_uv(); - xpc_heartbeat_uv->offline = 1; -} - -static void -xpc_online_heartbeat_uv(void) -{ - xpc_increment_heartbeat_uv(); - xpc_heartbeat_uv->offline = 0; -} - -static void -xpc_heartbeat_init_uv(void) -{ - xpc_heartbeat_uv->value = 1; - xpc_heartbeat_uv->offline = 0; -} - -static void -xpc_heartbeat_exit_uv(void) -{ - xpc_offline_heartbeat_uv(); -} - -static enum xp_retval -xpc_get_remote_heartbeat_uv(struct xpc_partition *part) -{ - struct xpc_partition_uv *part_uv = &part->sn.uv; - enum xp_retval ret; - - ret = xp_remote_memcpy(uv_gpa(&part_uv->cached_heartbeat), - part_uv->heartbeat_gpa, - sizeof(struct xpc_heartbeat_uv)); - if (ret != xpSuccess) - return ret; - - if (part_uv->cached_heartbeat.value == part->last_heartbeat && - !part_uv->cached_heartbeat.offline) { - - ret = xpNoHeartbeat; - } else { - part->last_heartbeat = part_uv->cached_heartbeat.value; - } - return ret; -} - -static void -xpc_request_partition_activation_uv(struct xpc_rsvd_page *remote_rp, - unsigned long remote_rp_gpa, int nasid) -{ - short partid = remote_rp->SAL_partid; - struct xpc_partition *part = &xpc_partitions[partid]; - struct xpc_activate_mq_msg_activate_req_uv msg; - - part->remote_rp_pa = remote_rp_gpa; /* !!! _pa here is really _gpa */ - part->remote_rp_ts_jiffies = remote_rp->ts_jiffies; - part->sn.uv.heartbeat_gpa = remote_rp->sn.uv.heartbeat_gpa; - part->sn.uv.activate_gru_mq_desc_gpa = - remote_rp->sn.uv.activate_gru_mq_desc_gpa; - - /* - * ??? Is it a good idea to make this conditional on what is - * ??? potentially stale state information? - */ - if (part->sn.uv.remote_act_state == XPC_P_AS_INACTIVE) { - msg.rp_gpa = uv_gpa(xpc_rsvd_page); - msg.heartbeat_gpa = xpc_rsvd_page->sn.uv.heartbeat_gpa; - msg.activate_gru_mq_desc_gpa = - xpc_rsvd_page->sn.uv.activate_gru_mq_desc_gpa; - xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_ACTIVATE_REQ_UV); - } - - if (part->act_state == XPC_P_AS_INACTIVE) - xpc_send_local_activate_IRQ_uv(part, XPC_P_ASR_ACTIVATE_UV); -} - -static void -xpc_request_partition_reactivation_uv(struct xpc_partition *part) -{ - xpc_send_local_activate_IRQ_uv(part, XPC_P_ASR_ACTIVATE_UV); -} - -static void -xpc_request_partition_deactivation_uv(struct xpc_partition *part) -{ - struct xpc_activate_mq_msg_deactivate_req_uv msg; - - /* - * ??? Is it a good idea to make this conditional on what is - * ??? potentially stale state information? - */ - if (part->sn.uv.remote_act_state != XPC_P_AS_DEACTIVATING && - part->sn.uv.remote_act_state != XPC_P_AS_INACTIVE) { - - msg.reason = part->reason; - xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_DEACTIVATE_REQ_UV); - } -} - -static void -xpc_cancel_partition_deactivation_request_uv(struct xpc_partition *part) -{ - /* nothing needs to be done */ - return; -} - -static void -xpc_init_fifo_uv(struct xpc_fifo_head_uv *head) -{ - head->first = NULL; - head->last = NULL; - spin_lock_init(&head->lock); - head->n_entries = 0; -} - -static void * -xpc_get_fifo_entry_uv(struct xpc_fifo_head_uv *head) -{ - unsigned long irq_flags; - struct xpc_fifo_entry_uv *first; - - spin_lock_irqsave(&head->lock, irq_flags); - first = head->first; - if (head->first != NULL) { - head->first = first->next; - if (head->first == NULL) - head->last = NULL; - - head->n_entries--; - BUG_ON(head->n_entries < 0); - - first->next = NULL; - } - spin_unlock_irqrestore(&head->lock, irq_flags); - return first; -} - -static void -xpc_put_fifo_entry_uv(struct xpc_fifo_head_uv *head, - struct xpc_fifo_entry_uv *last) -{ - unsigned long irq_flags; - - last->next = NULL; - spin_lock_irqsave(&head->lock, irq_flags); - if (head->last != NULL) - head->last->next = last; - else - head->first = last; - head->last = last; - head->n_entries++; - spin_unlock_irqrestore(&head->lock, irq_flags); -} - -static int -xpc_n_of_fifo_entries_uv(struct xpc_fifo_head_uv *head) -{ - return head->n_entries; -} - -/* - * Setup the channel structures that are uv specific. - */ -static enum xp_retval -xpc_setup_ch_structures_uv(struct xpc_partition *part) -{ - struct xpc_channel_uv *ch_uv; - int ch_number; - - for (ch_number = 0; ch_number < part->nchannels; ch_number++) { - ch_uv = &part->channels[ch_number].sn.uv; - - xpc_init_fifo_uv(&ch_uv->msg_slot_free_list); - xpc_init_fifo_uv(&ch_uv->recv_msg_list); - } - - return xpSuccess; -} - -/* - * Teardown the channel structures that are uv specific. - */ -static void -xpc_teardown_ch_structures_uv(struct xpc_partition *part) -{ - /* nothing needs to be done */ - return; -} - -static enum xp_retval -xpc_make_first_contact_uv(struct xpc_partition *part) -{ - struct xpc_activate_mq_msg_uv msg; - - /* - * We send a sync msg to get the remote partition's remote_act_state - * updated to our current act_state which at this point should - * be XPC_P_AS_ACTIVATING. - */ - xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV); - - while (!((part->sn.uv.remote_act_state == XPC_P_AS_ACTIVATING) || - (part->sn.uv.remote_act_state == XPC_P_AS_ACTIVE))) { - - dev_dbg(xpc_part, "waiting to make first contact with " - "partition %d\n", XPC_PARTID(part)); - - /* wait a 1/4 of a second or so */ - (void)msleep_interruptible(250); - - if (part->act_state == XPC_P_AS_DEACTIVATING) - return part->reason; - } - - return xpSuccess; -} - -static u64 -xpc_get_chctl_all_flags_uv(struct xpc_partition *part) -{ - unsigned long irq_flags; - union xpc_channel_ctl_flags chctl; - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - chctl = part->chctl; - if (chctl.all_flags != 0) - part->chctl.all_flags = 0; - - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - return chctl.all_flags; -} - -static enum xp_retval -xpc_allocate_send_msg_slot_uv(struct xpc_channel *ch) -{ - struct xpc_channel_uv *ch_uv = &ch->sn.uv; - struct xpc_send_msg_slot_uv *msg_slot; - unsigned long irq_flags; - int nentries; - int entry; - size_t nbytes; - - for (nentries = ch->local_nentries; nentries > 0; nentries--) { - nbytes = nentries * sizeof(struct xpc_send_msg_slot_uv); - ch_uv->send_msg_slots = kzalloc(nbytes, GFP_KERNEL); - if (ch_uv->send_msg_slots == NULL) - continue; - - for (entry = 0; entry < nentries; entry++) { - msg_slot = &ch_uv->send_msg_slots[entry]; - - msg_slot->msg_slot_number = entry; - xpc_put_fifo_entry_uv(&ch_uv->msg_slot_free_list, - &msg_slot->next); - } - - spin_lock_irqsave(&ch->lock, irq_flags); - if (nentries < ch->local_nentries) - ch->local_nentries = nentries; - spin_unlock_irqrestore(&ch->lock, irq_flags); - return xpSuccess; - } - - return xpNoMemory; -} - -static enum xp_retval -xpc_allocate_recv_msg_slot_uv(struct xpc_channel *ch) -{ - struct xpc_channel_uv *ch_uv = &ch->sn.uv; - struct xpc_notify_mq_msg_uv *msg_slot; - unsigned long irq_flags; - int nentries; - int entry; - size_t nbytes; - - for (nentries = ch->remote_nentries; nentries > 0; nentries--) { - nbytes = nentries * ch->entry_size; - ch_uv->recv_msg_slots = kzalloc(nbytes, GFP_KERNEL); - if (ch_uv->recv_msg_slots == NULL) - continue; - - for (entry = 0; entry < nentries; entry++) { - msg_slot = ch_uv->recv_msg_slots + - entry * ch->entry_size; - - msg_slot->hdr.msg_slot_number = entry; - } - - spin_lock_irqsave(&ch->lock, irq_flags); - if (nentries < ch->remote_nentries) - ch->remote_nentries = nentries; - spin_unlock_irqrestore(&ch->lock, irq_flags); - return xpSuccess; - } - - return xpNoMemory; -} - -/* - * Allocate msg_slots associated with the channel. - */ -static enum xp_retval -xpc_setup_msg_structures_uv(struct xpc_channel *ch) -{ - static enum xp_retval ret; - struct xpc_channel_uv *ch_uv = &ch->sn.uv; - - DBUG_ON(ch->flags & XPC_C_SETUP); - - ch_uv->cached_notify_gru_mq_desc = kmalloc_obj(struct gru_message_queue_desc); - if (ch_uv->cached_notify_gru_mq_desc == NULL) - return xpNoMemory; - - ret = xpc_allocate_send_msg_slot_uv(ch); - if (ret == xpSuccess) { - - ret = xpc_allocate_recv_msg_slot_uv(ch); - if (ret != xpSuccess) { - kfree(ch_uv->send_msg_slots); - xpc_init_fifo_uv(&ch_uv->msg_slot_free_list); - } - } - return ret; -} - -/* - * Free up msg_slots and clear other stuff that were setup for the specified - * channel. - */ -static void -xpc_teardown_msg_structures_uv(struct xpc_channel *ch) -{ - struct xpc_channel_uv *ch_uv = &ch->sn.uv; - - lockdep_assert_held(&ch->lock); - - kfree(ch_uv->cached_notify_gru_mq_desc); - ch_uv->cached_notify_gru_mq_desc = NULL; - - if (ch->flags & XPC_C_SETUP) { - xpc_init_fifo_uv(&ch_uv->msg_slot_free_list); - kfree(ch_uv->send_msg_slots); - xpc_init_fifo_uv(&ch_uv->recv_msg_list); - kfree(ch_uv->recv_msg_slots); - } -} - -static void -xpc_send_chctl_closerequest_uv(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_activate_mq_msg_chctl_closerequest_uv msg; - - msg.ch_number = ch->number; - msg.reason = ch->reason; - xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREQUEST_UV); -} - -static void -xpc_send_chctl_closereply_uv(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_activate_mq_msg_chctl_closereply_uv msg; - - msg.ch_number = ch->number; - xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREPLY_UV); -} - -static void -xpc_send_chctl_openrequest_uv(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_activate_mq_msg_chctl_openrequest_uv msg; - - msg.ch_number = ch->number; - msg.entry_size = ch->entry_size; - msg.local_nentries = ch->local_nentries; - xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREQUEST_UV); -} - -static void -xpc_send_chctl_openreply_uv(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_activate_mq_msg_chctl_openreply_uv msg; - - msg.ch_number = ch->number; - msg.local_nentries = ch->local_nentries; - msg.remote_nentries = ch->remote_nentries; - msg.notify_gru_mq_desc_gpa = uv_gpa(xpc_notify_mq_uv->gru_mq_desc); - xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREPLY_UV); -} - -static void -xpc_send_chctl_opencomplete_uv(struct xpc_channel *ch, unsigned long *irq_flags) -{ - struct xpc_activate_mq_msg_chctl_opencomplete_uv msg; - - msg.ch_number = ch->number; - xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_CHCTL_OPENCOMPLETE_UV); -} - -static void -xpc_send_chctl_local_msgrequest_uv(struct xpc_partition *part, int ch_number) -{ - unsigned long irq_flags; - - spin_lock_irqsave(&part->chctl_lock, irq_flags); - part->chctl.flags[ch_number] |= XPC_CHCTL_MSGREQUEST; - spin_unlock_irqrestore(&part->chctl_lock, irq_flags); - - xpc_wakeup_channel_mgr(part); -} - -static enum xp_retval -xpc_save_remote_msgqueue_pa_uv(struct xpc_channel *ch, - unsigned long gru_mq_desc_gpa) -{ - struct xpc_channel_uv *ch_uv = &ch->sn.uv; - - DBUG_ON(ch_uv->cached_notify_gru_mq_desc == NULL); - return xpc_cache_remote_gru_mq_desc_uv(ch_uv->cached_notify_gru_mq_desc, - gru_mq_desc_gpa); -} - -static void -xpc_indicate_partition_engaged_uv(struct xpc_partition *part) -{ - struct xpc_activate_mq_msg_uv msg; - - xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_MARK_ENGAGED_UV); -} - -static void -xpc_indicate_partition_disengaged_uv(struct xpc_partition *part) -{ - struct xpc_activate_mq_msg_uv msg; - - xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg), - XPC_ACTIVATE_MQ_MSG_MARK_DISENGAGED_UV); -} - -static void -xpc_assume_partition_disengaged_uv(short partid) -{ - struct xpc_partition_uv *part_uv = &xpc_partitions[partid].sn.uv; - unsigned long irq_flags; - - spin_lock_irqsave(&part_uv->flags_lock, irq_flags); - part_uv->flags &= ~XPC_P_ENGAGED_UV; - spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags); -} - -static int -xpc_partition_engaged_uv(short partid) -{ - return (xpc_partitions[partid].sn.uv.flags & XPC_P_ENGAGED_UV) != 0; -} - -static int -xpc_any_partition_engaged_uv(void) -{ - struct xpc_partition_uv *part_uv; - short partid; - - for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) { - part_uv = &xpc_partitions[partid].sn.uv; - if ((part_uv->flags & XPC_P_ENGAGED_UV) != 0) - return 1; - } - return 0; -} - -static enum xp_retval -xpc_allocate_msg_slot_uv(struct xpc_channel *ch, u32 flags, - struct xpc_send_msg_slot_uv **address_of_msg_slot) -{ - enum xp_retval ret; - struct xpc_send_msg_slot_uv *msg_slot; - struct xpc_fifo_entry_uv *entry; - - while (1) { - entry = xpc_get_fifo_entry_uv(&ch->sn.uv.msg_slot_free_list); - if (entry != NULL) - break; - - if (flags & XPC_NOWAIT) - return xpNoWait; - - ret = xpc_allocate_msg_wait(ch); - if (ret != xpInterrupted && ret != xpTimeout) - return ret; - } - - msg_slot = container_of(entry, struct xpc_send_msg_slot_uv, next); - *address_of_msg_slot = msg_slot; - return xpSuccess; -} - -static void -xpc_free_msg_slot_uv(struct xpc_channel *ch, - struct xpc_send_msg_slot_uv *msg_slot) -{ - xpc_put_fifo_entry_uv(&ch->sn.uv.msg_slot_free_list, &msg_slot->next); - - /* wakeup anyone waiting for a free msg slot */ - if (atomic_read(&ch->n_on_msg_allocate_wq) > 0) - wake_up(&ch->msg_allocate_wq); -} - -static void -xpc_notify_sender_uv(struct xpc_channel *ch, - struct xpc_send_msg_slot_uv *msg_slot, - enum xp_retval reason) -{ - xpc_notify_func func = msg_slot->func; - - if (func != NULL && cmpxchg(&msg_slot->func, func, NULL) == func) { - - atomic_dec(&ch->n_to_notify); - - dev_dbg(xpc_chan, "msg_slot->func() called, msg_slot=0x%p " - "msg_slot_number=%d partid=%d channel=%d\n", msg_slot, - msg_slot->msg_slot_number, ch->partid, ch->number); - - func(reason, ch->partid, ch->number, msg_slot->key); - - dev_dbg(xpc_chan, "msg_slot->func() returned, msg_slot=0x%p " - "msg_slot_number=%d partid=%d channel=%d\n", msg_slot, - msg_slot->msg_slot_number, ch->partid, ch->number); - } -} - -static void -xpc_handle_notify_mq_ack_uv(struct xpc_channel *ch, - struct xpc_notify_mq_msg_uv *msg) -{ - struct xpc_send_msg_slot_uv *msg_slot; - int entry = msg->hdr.msg_slot_number % ch->local_nentries; - - msg_slot = &ch->sn.uv.send_msg_slots[entry]; - - BUG_ON(msg_slot->msg_slot_number != msg->hdr.msg_slot_number); - msg_slot->msg_slot_number += ch->local_nentries; - - if (msg_slot->func != NULL) - xpc_notify_sender_uv(ch, msg_slot, xpMsgDelivered); - - xpc_free_msg_slot_uv(ch, msg_slot); -} - -static void -xpc_handle_notify_mq_msg_uv(struct xpc_partition *part, - struct xpc_notify_mq_msg_uv *msg) -{ - struct xpc_partition_uv *part_uv = &part->sn.uv; - struct xpc_channel *ch; - struct xpc_channel_uv *ch_uv; - struct xpc_notify_mq_msg_uv *msg_slot; - unsigned long irq_flags; - int ch_number = msg->hdr.ch_number; - - if (unlikely(ch_number >= part->nchannels)) { - dev_err(xpc_part, "xpc_handle_notify_IRQ_uv() received invalid " - "channel number=0x%x in message from partid=%d\n", - ch_number, XPC_PARTID(part)); - - /* get hb checker to deactivate from the remote partition */ - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); - if (part_uv->act_state_req == 0) - xpc_activate_IRQ_rcvd++; - part_uv->act_state_req = XPC_P_ASR_DEACTIVATE_UV; - part_uv->reason = xpBadChannelNumber; - spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags); - - wake_up_interruptible(&xpc_activate_IRQ_wq); - return; - } - - ch = &part->channels[ch_number]; - xpc_msgqueue_ref(ch); - - if (!(ch->flags & XPC_C_CONNECTED)) { - xpc_msgqueue_deref(ch); - return; - } - - /* see if we're really dealing with an ACK for a previously sent msg */ - if (msg->hdr.size == 0) { - xpc_handle_notify_mq_ack_uv(ch, msg); - xpc_msgqueue_deref(ch); - return; - } - - /* we're dealing with a normal message sent via the notify_mq */ - ch_uv = &ch->sn.uv; - - msg_slot = ch_uv->recv_msg_slots + - (msg->hdr.msg_slot_number % ch->remote_nentries) * ch->entry_size; - - BUG_ON(msg_slot->hdr.size != 0); - - memcpy(msg_slot, msg, msg->hdr.size); - - xpc_put_fifo_entry_uv(&ch_uv->recv_msg_list, &msg_slot->hdr.u.next); - - if (ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) { - /* - * If there is an existing idle kthread get it to deliver - * the payload, otherwise we'll have to get the channel mgr - * for this partition to create a kthread to do the delivery. - */ - if (atomic_read(&ch->kthreads_idle) > 0) - wake_up_nr(&ch->idle_wq, 1); - else - xpc_send_chctl_local_msgrequest_uv(part, ch->number); - } - xpc_msgqueue_deref(ch); -} - -static irqreturn_t -xpc_handle_notify_IRQ_uv(int irq, void *dev_id) -{ - struct xpc_notify_mq_msg_uv *msg; - short partid; - struct xpc_partition *part; - - while ((msg = gru_get_next_message(xpc_notify_mq_uv->gru_mq_desc)) != - NULL) { - - partid = msg->hdr.partid; - if (partid < 0 || partid >= XP_MAX_NPARTITIONS_UV) { - dev_err(xpc_part, "xpc_handle_notify_IRQ_uv() received " - "invalid partid=0x%x in message\n", partid); - } else { - part = &xpc_partitions[partid]; - - if (xpc_part_ref(part)) { - xpc_handle_notify_mq_msg_uv(part, msg); - xpc_part_deref(part); - } - } - - gru_free_message(xpc_notify_mq_uv->gru_mq_desc, msg); - } - - return IRQ_HANDLED; -} - -static int -xpc_n_of_deliverable_payloads_uv(struct xpc_channel *ch) -{ - return xpc_n_of_fifo_entries_uv(&ch->sn.uv.recv_msg_list); -} - -static void -xpc_process_msg_chctl_flags_uv(struct xpc_partition *part, int ch_number) -{ - struct xpc_channel *ch = &part->channels[ch_number]; - int ndeliverable_payloads; - - xpc_msgqueue_ref(ch); - - ndeliverable_payloads = xpc_n_of_deliverable_payloads_uv(ch); - - if (ndeliverable_payloads > 0 && - (ch->flags & XPC_C_CONNECTED) && - (ch->flags & XPC_C_CONNECTEDCALLOUT_MADE)) { - - xpc_activate_kthreads(ch, ndeliverable_payloads); - } - - xpc_msgqueue_deref(ch); -} - -static enum xp_retval -xpc_send_payload_uv(struct xpc_channel *ch, u32 flags, void *payload, - u16 payload_size, u8 notify_type, xpc_notify_func func, - void *key) -{ - enum xp_retval ret = xpSuccess; - struct xpc_send_msg_slot_uv *msg_slot = NULL; - struct xpc_notify_mq_msg_uv *msg; - u8 msg_buffer[XPC_NOTIFY_MSG_SIZE_UV]; - size_t msg_size; - - DBUG_ON(notify_type != XPC_N_CALL); - - msg_size = sizeof(struct xpc_notify_mq_msghdr_uv) + payload_size; - if (msg_size > ch->entry_size) - return xpPayloadTooBig; - - xpc_msgqueue_ref(ch); - - if (ch->flags & XPC_C_DISCONNECTING) { - ret = ch->reason; - goto out_1; - } - if (!(ch->flags & XPC_C_CONNECTED)) { - ret = xpNotConnected; - goto out_1; - } - - ret = xpc_allocate_msg_slot_uv(ch, flags, &msg_slot); - if (ret != xpSuccess) - goto out_1; - - if (func != NULL) { - atomic_inc(&ch->n_to_notify); - - msg_slot->key = key; - smp_wmb(); /* a non-NULL func must hit memory after the key */ - msg_slot->func = func; - - if (ch->flags & XPC_C_DISCONNECTING) { - ret = ch->reason; - goto out_2; - } - } - - msg = (struct xpc_notify_mq_msg_uv *)&msg_buffer; - msg->hdr.partid = xp_partition_id; - msg->hdr.ch_number = ch->number; - msg->hdr.size = msg_size; - msg->hdr.msg_slot_number = msg_slot->msg_slot_number; - memcpy(&msg->payload, payload, payload_size); - - ret = xpc_send_gru_msg(ch->sn.uv.cached_notify_gru_mq_desc, msg, - msg_size); - if (ret == xpSuccess) - goto out_1; - - XPC_DEACTIVATE_PARTITION(&xpc_partitions[ch->partid], ret); -out_2: - if (func != NULL) { - /* - * Try to NULL the msg_slot's func field. If we fail, then - * xpc_notify_senders_of_disconnect_uv() beat us to it, in which - * case we need to pretend we succeeded to send the message - * since the user will get a callout for the disconnect error - * by xpc_notify_senders_of_disconnect_uv(), and to also get an - * error returned here will confuse them. Additionally, since - * in this case the channel is being disconnected we don't need - * to put the msg_slot back on the free list. - */ - if (cmpxchg(&msg_slot->func, func, NULL) != func) { - ret = xpSuccess; - goto out_1; - } - - msg_slot->key = NULL; - atomic_dec(&ch->n_to_notify); - } - xpc_free_msg_slot_uv(ch, msg_slot); -out_1: - xpc_msgqueue_deref(ch); - return ret; -} - -/* - * Tell the callers of xpc_send_notify() that the status of their payloads - * is unknown because the channel is now disconnecting. - * - * We don't worry about putting these msg_slots on the free list since the - * msg_slots themselves are about to be kfree'd. - */ -static void -xpc_notify_senders_of_disconnect_uv(struct xpc_channel *ch) -{ - struct xpc_send_msg_slot_uv *msg_slot; - int entry; - - DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING)); - - for (entry = 0; entry < ch->local_nentries; entry++) { - - if (atomic_read(&ch->n_to_notify) == 0) - break; - - msg_slot = &ch->sn.uv.send_msg_slots[entry]; - if (msg_slot->func != NULL) - xpc_notify_sender_uv(ch, msg_slot, ch->reason); - } -} - -/* - * Get the next deliverable message's payload. - */ -static void * -xpc_get_deliverable_payload_uv(struct xpc_channel *ch) -{ - struct xpc_fifo_entry_uv *entry; - struct xpc_notify_mq_msg_uv *msg; - void *payload = NULL; - - if (!(ch->flags & XPC_C_DISCONNECTING)) { - entry = xpc_get_fifo_entry_uv(&ch->sn.uv.recv_msg_list); - if (entry != NULL) { - msg = container_of(entry, struct xpc_notify_mq_msg_uv, - hdr.u.next); - payload = &msg->payload; - } - } - return payload; -} - -static void -xpc_received_payload_uv(struct xpc_channel *ch, void *payload) -{ - struct xpc_notify_mq_msg_uv *msg; - enum xp_retval ret; - - msg = container_of(payload, struct xpc_notify_mq_msg_uv, payload); - - /* return an ACK to the sender of this message */ - - msg->hdr.partid = xp_partition_id; - msg->hdr.size = 0; /* size of zero indicates this is an ACK */ - - ret = xpc_send_gru_msg(ch->sn.uv.cached_notify_gru_mq_desc, msg, - sizeof(struct xpc_notify_mq_msghdr_uv)); - if (ret != xpSuccess) - XPC_DEACTIVATE_PARTITION(&xpc_partitions[ch->partid], ret); -} - -static const struct xpc_arch_operations xpc_arch_ops_uv = { - .setup_partitions = xpc_setup_partitions_uv, - .teardown_partitions = xpc_teardown_partitions_uv, - .process_activate_IRQ_rcvd = xpc_process_activate_IRQ_rcvd_uv, - .get_partition_rsvd_page_pa = xpc_get_partition_rsvd_page_pa_uv, - .setup_rsvd_page = xpc_setup_rsvd_page_uv, - - .allow_hb = xpc_allow_hb_uv, - .disallow_hb = xpc_disallow_hb_uv, - .disallow_all_hbs = xpc_disallow_all_hbs_uv, - .increment_heartbeat = xpc_increment_heartbeat_uv, - .offline_heartbeat = xpc_offline_heartbeat_uv, - .online_heartbeat = xpc_online_heartbeat_uv, - .heartbeat_init = xpc_heartbeat_init_uv, - .heartbeat_exit = xpc_heartbeat_exit_uv, - .get_remote_heartbeat = xpc_get_remote_heartbeat_uv, - - .request_partition_activation = - xpc_request_partition_activation_uv, - .request_partition_reactivation = - xpc_request_partition_reactivation_uv, - .request_partition_deactivation = - xpc_request_partition_deactivation_uv, - .cancel_partition_deactivation_request = - xpc_cancel_partition_deactivation_request_uv, - - .setup_ch_structures = xpc_setup_ch_structures_uv, - .teardown_ch_structures = xpc_teardown_ch_structures_uv, - - .make_first_contact = xpc_make_first_contact_uv, - - .get_chctl_all_flags = xpc_get_chctl_all_flags_uv, - .send_chctl_closerequest = xpc_send_chctl_closerequest_uv, - .send_chctl_closereply = xpc_send_chctl_closereply_uv, - .send_chctl_openrequest = xpc_send_chctl_openrequest_uv, - .send_chctl_openreply = xpc_send_chctl_openreply_uv, - .send_chctl_opencomplete = xpc_send_chctl_opencomplete_uv, - .process_msg_chctl_flags = xpc_process_msg_chctl_flags_uv, - - .save_remote_msgqueue_pa = xpc_save_remote_msgqueue_pa_uv, - - .setup_msg_structures = xpc_setup_msg_structures_uv, - .teardown_msg_structures = xpc_teardown_msg_structures_uv, - - .indicate_partition_engaged = xpc_indicate_partition_engaged_uv, - .indicate_partition_disengaged = xpc_indicate_partition_disengaged_uv, - .assume_partition_disengaged = xpc_assume_partition_disengaged_uv, - .partition_engaged = xpc_partition_engaged_uv, - .any_partition_engaged = xpc_any_partition_engaged_uv, - - .n_of_deliverable_payloads = xpc_n_of_deliverable_payloads_uv, - .send_payload = xpc_send_payload_uv, - .get_deliverable_payload = xpc_get_deliverable_payload_uv, - .received_payload = xpc_received_payload_uv, - .notify_senders_of_disconnect = xpc_notify_senders_of_disconnect_uv, -}; - -static int -xpc_init_mq_node(int nid) -{ - int cpu; - - cpus_read_lock(); - - for_each_cpu(cpu, cpumask_of_node(nid)) { - xpc_activate_mq_uv = - xpc_create_gru_mq_uv(XPC_ACTIVATE_MQ_SIZE_UV, nid, - XPC_ACTIVATE_IRQ_NAME, - xpc_handle_activate_IRQ_uv); - if (!IS_ERR(xpc_activate_mq_uv)) - break; - } - if (IS_ERR(xpc_activate_mq_uv)) { - cpus_read_unlock(); - return PTR_ERR(xpc_activate_mq_uv); - } - - for_each_cpu(cpu, cpumask_of_node(nid)) { - xpc_notify_mq_uv = - xpc_create_gru_mq_uv(XPC_NOTIFY_MQ_SIZE_UV, nid, - XPC_NOTIFY_IRQ_NAME, - xpc_handle_notify_IRQ_uv); - if (!IS_ERR(xpc_notify_mq_uv)) - break; - } - if (IS_ERR(xpc_notify_mq_uv)) { - xpc_destroy_gru_mq_uv(xpc_activate_mq_uv); - cpus_read_unlock(); - return PTR_ERR(xpc_notify_mq_uv); - } - - cpus_read_unlock(); - return 0; -} - -int -xpc_init_uv(void) -{ - int nid; - int ret = 0; - - xpc_arch_ops = xpc_arch_ops_uv; - - if (sizeof(struct xpc_notify_mq_msghdr_uv) > XPC_MSG_HDR_MAX_SIZE) { - dev_err(xpc_part, "xpc_notify_mq_msghdr_uv is larger than %d\n", - XPC_MSG_HDR_MAX_SIZE); - return -E2BIG; - } - - if (xpc_mq_node < 0) - for_each_online_node(nid) { - ret = xpc_init_mq_node(nid); - - if (!ret) - break; - } - else - ret = xpc_init_mq_node(xpc_mq_node); - - if (ret < 0) - dev_err(xpc_part, "xpc_init_mq_node() returned error=%d\n", - -ret); - - return ret; -} - -void -xpc_exit_uv(void) -{ - xpc_destroy_gru_mq_uv(xpc_notify_mq_uv); - xpc_destroy_gru_mq_uv(xpc_activate_mq_uv); -} - -module_param(xpc_mq_node, int, 0); -MODULE_PARM_DESC(xpc_mq_node, "Node number on which to allocate message queues."); diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c deleted file mode 100644 index 1533b72d57b1..000000000000 --- a/drivers/misc/sgi-xp/xpnet.c +++ /dev/null @@ -1,599 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (C) 1999-2009 Silicon Graphics, Inc. All rights reserved. - */ - -/* - * Cross Partition Network Interface (XPNET) support - * - * XPNET provides a virtual network layered on top of the Cross - * Partition communication layer. - * - * XPNET provides direct point-to-point and broadcast-like support - * for an ethernet-like device. The ethernet broadcast medium is - * replaced with a point-to-point message structure which passes - * pointers to a DMA-capable block that a remote partition should - * retrieve and pass to the upper level networking layer. - * - */ - -#include -#include -#include -#include -#include "xp.h" - -/* - * The message payload transferred by XPC. - * - * buf_pa is the physical address where the DMA should pull from. - * - * NOTE: for performance reasons, buf_pa should _ALWAYS_ begin on a - * cacheline boundary. To accomplish this, we record the number of - * bytes from the beginning of the first cacheline to the first useful - * byte of the skb (leadin_ignore) and the number of bytes from the - * last useful byte of the skb to the end of the last cacheline - * (tailout_ignore). - * - * size is the number of bytes to transfer which includes the skb->len - * (useful bytes of the senders skb) plus the leadin and tailout - */ -struct xpnet_message { - u16 version; /* Version for this message */ - u16 embedded_bytes; /* #of bytes embedded in XPC message */ - u32 magic; /* Special number indicating this is xpnet */ - unsigned long buf_pa; /* phys address of buffer to retrieve */ - u32 size; /* #of bytes in buffer */ - u8 leadin_ignore; /* #of bytes to ignore at the beginning */ - u8 tailout_ignore; /* #of bytes to ignore at the end */ - unsigned char data; /* body of small packets */ -}; - -/* - * Determine the size of our message, the cacheline aligned size, - * and then the number of message will request from XPC. - * - * XPC expects each message to exist in an individual cacheline. - */ -#define XPNET_MSG_SIZE XPC_MSG_PAYLOAD_MAX_SIZE -#define XPNET_MSG_DATA_MAX \ - (XPNET_MSG_SIZE - offsetof(struct xpnet_message, data)) -#define XPNET_MSG_NENTRIES (PAGE_SIZE / XPC_MSG_MAX_SIZE) - -#define XPNET_MAX_KTHREADS (XPNET_MSG_NENTRIES + 1) -#define XPNET_MAX_IDLE_KTHREADS (XPNET_MSG_NENTRIES + 1) - -/* - * Version number of XPNET implementation. XPNET can always talk to versions - * with same major #, and never talk to versions with a different version. - */ -#define _XPNET_VERSION(_major, _minor) (((_major) << 4) | (_minor)) -#define XPNET_VERSION_MAJOR(_v) ((_v) >> 4) -#define XPNET_VERSION_MINOR(_v) ((_v) & 0xf) - -#define XPNET_VERSION _XPNET_VERSION(1, 0) /* version 1.0 */ -#define XPNET_VERSION_EMBED _XPNET_VERSION(1, 1) /* version 1.1 */ -#define XPNET_MAGIC 0x88786984 /* "XNET" */ - -#define XPNET_VALID_MSG(_m) \ - ((XPNET_VERSION_MAJOR(_m->version) == XPNET_VERSION_MAJOR(XPNET_VERSION)) \ - && (msg->magic == XPNET_MAGIC)) - -#define XPNET_DEVICE_NAME "xp0" - -/* - * When messages are queued with xpc_send_notify, a kmalloc'd buffer - * of the following type is passed as a notification cookie. When the - * notification function is called, we use the cookie to decide - * whether all outstanding message sends have completed. The skb can - * then be released. - */ -struct xpnet_pending_msg { - struct sk_buff *skb; - atomic_t use_count; -}; - -static struct net_device *xpnet_device; - -/* - * When we are notified of other partitions activating, we add them to - * our bitmask of partitions to which we broadcast. - */ -static unsigned long *xpnet_broadcast_partitions; -/* protect above */ -static DEFINE_SPINLOCK(xpnet_broadcast_lock); - -/* - * Since the Block Transfer Engine (BTE) is being used for the transfer - * and it relies upon cache-line size transfers, we need to reserve at - * least one cache-line for head and tail alignment. The BTE is - * limited to 8MB transfers. - * - * Testing has shown that changing MTU to greater than 64KB has no effect - * on TCP as the two sides negotiate a Max Segment Size that is limited - * to 64K. Other protocols May use packets greater than this, but for - * now, the default is 64KB. - */ -#define XPNET_MAX_MTU (0x800000UL - L1_CACHE_BYTES) -/* 68 comes from min TCP+IP+MAC header */ -#define XPNET_MIN_MTU 68 -/* 32KB has been determined to be the ideal */ -#define XPNET_DEF_MTU (0x8000UL) - -/* - * The partid is encapsulated in the MAC address beginning in the following - * octet and it consists of two octets. - */ -#define XPNET_PARTID_OCTET 2 - -/* Define the XPNET debug device structures to be used with dev_dbg() et al */ - -static struct device_driver xpnet_dbg_name = { - .name = "xpnet" -}; - -static struct device xpnet_dbg_subname = { - .init_name = "", /* set to "" */ - .driver = &xpnet_dbg_name -}; - -static struct device *xpnet = &xpnet_dbg_subname; - -/* - * Packet was recevied by XPC and forwarded to us. - */ -static void -xpnet_receive(short partid, int channel, struct xpnet_message *msg) -{ - struct sk_buff *skb; - void *dst; - enum xp_retval ret; - - if (!XPNET_VALID_MSG(msg)) { - /* - * Packet with a different XPC version. Ignore. - */ - xpc_received(partid, channel, (void *)msg); - - xpnet_device->stats.rx_errors++; - - return; - } - dev_dbg(xpnet, "received 0x%lx, %d, %d, %d\n", msg->buf_pa, msg->size, - msg->leadin_ignore, msg->tailout_ignore); - - /* reserve an extra cache line */ - skb = dev_alloc_skb(msg->size + L1_CACHE_BYTES); - if (!skb) { - dev_err(xpnet, "failed on dev_alloc_skb(%d)\n", - msg->size + L1_CACHE_BYTES); - - xpc_received(partid, channel, (void *)msg); - - xpnet_device->stats.rx_errors++; - - return; - } - - /* - * The allocated skb has some reserved space. - * In order to use xp_remote_memcpy(), we need to get the - * skb->data pointer moved forward. - */ - skb_reserve(skb, (L1_CACHE_BYTES - ((u64)skb->data & - (L1_CACHE_BYTES - 1)) + - msg->leadin_ignore)); - - /* - * Update the tail pointer to indicate data actually - * transferred. - */ - skb_put(skb, (msg->size - msg->leadin_ignore - msg->tailout_ignore)); - - /* - * Move the data over from the other side. - */ - if ((XPNET_VERSION_MINOR(msg->version) == 1) && - (msg->embedded_bytes != 0)) { - dev_dbg(xpnet, "copying embedded message. memcpy(0x%p, 0x%p, " - "%lu)\n", skb->data, &msg->data, - (size_t)msg->embedded_bytes); - - skb_copy_to_linear_data(skb, &msg->data, - (size_t)msg->embedded_bytes); - } else { - dst = (void *)((u64)skb->data & ~(L1_CACHE_BYTES - 1)); - dev_dbg(xpnet, "transferring buffer to the skb->data area;\n\t" - "xp_remote_memcpy(0x%p, 0x%p, %u)\n", dst, - (void *)msg->buf_pa, msg->size); - - ret = xp_remote_memcpy(xp_pa(dst), msg->buf_pa, msg->size); - if (ret != xpSuccess) { - /* - * !!! Need better way of cleaning skb. Currently skb - * !!! appears in_use and we can't just call - * !!! dev_kfree_skb. - */ - dev_err(xpnet, "xp_remote_memcpy(0x%p, 0x%p, 0x%x) " - "returned error=0x%x\n", dst, - (void *)msg->buf_pa, msg->size, ret); - - xpc_received(partid, channel, (void *)msg); - - xpnet_device->stats.rx_errors++; - - return; - } - } - - dev_dbg(xpnet, "head=0x%p skb->data=0x%p skb->tail=0x%p " - "skb->end=0x%p skb->len=%d\n", (void *)skb->head, - (void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb), - skb->len); - - skb->protocol = eth_type_trans(skb, xpnet_device); - skb->ip_summed = CHECKSUM_UNNECESSARY; - - dev_dbg(xpnet, "passing skb to network layer\n" - "\tskb->head=0x%p skb->data=0x%p skb->tail=0x%p " - "skb->end=0x%p skb->len=%d\n", - (void *)skb->head, (void *)skb->data, skb_tail_pointer(skb), - skb_end_pointer(skb), skb->len); - - xpnet_device->stats.rx_packets++; - xpnet_device->stats.rx_bytes += skb->len + ETH_HLEN; - - netif_rx(skb); - xpc_received(partid, channel, (void *)msg); -} - -/* - * This is the handler which XPC calls during any sort of change in - * state or message reception on a connection. - */ -static void -xpnet_connection_activity(enum xp_retval reason, short partid, int channel, - void *data, void *key) -{ - DBUG_ON(partid < 0 || partid >= xp_max_npartitions); - DBUG_ON(channel != XPC_NET_CHANNEL); - - switch (reason) { - case xpMsgReceived: /* message received */ - DBUG_ON(data == NULL); - - xpnet_receive(partid, channel, (struct xpnet_message *)data); - break; - - case xpConnected: /* connection completed to a partition */ - spin_lock_bh(&xpnet_broadcast_lock); - __set_bit(partid, xpnet_broadcast_partitions); - spin_unlock_bh(&xpnet_broadcast_lock); - - netif_carrier_on(xpnet_device); - - dev_dbg(xpnet, "%s connected to partition %d\n", - xpnet_device->name, partid); - break; - - default: - spin_lock_bh(&xpnet_broadcast_lock); - __clear_bit(partid, xpnet_broadcast_partitions); - spin_unlock_bh(&xpnet_broadcast_lock); - - if (bitmap_empty(xpnet_broadcast_partitions, - xp_max_npartitions)) { - netif_carrier_off(xpnet_device); - } - - dev_dbg(xpnet, "%s disconnected from partition %d\n", - xpnet_device->name, partid); - break; - } -} - -static int -xpnet_dev_open(struct net_device *dev) -{ - enum xp_retval ret; - - dev_dbg(xpnet, "calling xpc_connect(%d, 0x%p, NULL, %ld, %ld, %ld, " - "%ld)\n", XPC_NET_CHANNEL, xpnet_connection_activity, - (unsigned long)XPNET_MSG_SIZE, - (unsigned long)XPNET_MSG_NENTRIES, - (unsigned long)XPNET_MAX_KTHREADS, - (unsigned long)XPNET_MAX_IDLE_KTHREADS); - - ret = xpc_connect(XPC_NET_CHANNEL, xpnet_connection_activity, NULL, - XPNET_MSG_SIZE, XPNET_MSG_NENTRIES, - XPNET_MAX_KTHREADS, XPNET_MAX_IDLE_KTHREADS); - if (ret != xpSuccess) { - dev_err(xpnet, "ifconfig up of %s failed on XPC connect, " - "ret=%d\n", dev->name, ret); - - return -ENOMEM; - } - - dev_dbg(xpnet, "ifconfig up of %s; XPC connected\n", dev->name); - - return 0; -} - -static int -xpnet_dev_stop(struct net_device *dev) -{ - xpc_disconnect(XPC_NET_CHANNEL); - - dev_dbg(xpnet, "ifconfig down of %s; XPC disconnected\n", dev->name); - - return 0; -} - -/* - * Notification that the other end has received the message and - * DMA'd the skb information. At this point, they are done with - * our side. When all recipients are done processing, we - * release the skb and then release our pending message structure. - */ -static void -xpnet_send_completed(enum xp_retval reason, short partid, int channel, - void *__qm) -{ - struct xpnet_pending_msg *queued_msg = (struct xpnet_pending_msg *)__qm; - - DBUG_ON(queued_msg == NULL); - - dev_dbg(xpnet, "message to %d notified with reason %d\n", - partid, reason); - - if (atomic_dec_return(&queued_msg->use_count) == 0) { - dev_dbg(xpnet, "all acks for skb->head=-x%p\n", - (void *)queued_msg->skb->head); - - dev_kfree_skb_any(queued_msg->skb); - kfree(queued_msg); - } -} - -static void -xpnet_send(struct sk_buff *skb, struct xpnet_pending_msg *queued_msg, - u64 start_addr, u64 end_addr, u16 embedded_bytes, int dest_partid) -{ - u8 msg_buffer[XPNET_MSG_SIZE]; - struct xpnet_message *msg = (struct xpnet_message *)&msg_buffer; - u16 msg_size = sizeof(struct xpnet_message); - enum xp_retval ret; - - msg->embedded_bytes = embedded_bytes; - if (unlikely(embedded_bytes != 0)) { - msg->version = XPNET_VERSION_EMBED; - dev_dbg(xpnet, "calling memcpy(0x%p, 0x%p, 0x%lx)\n", - &msg->data, skb->data, (size_t)embedded_bytes); - skb_copy_from_linear_data(skb, &msg->data, - (size_t)embedded_bytes); - msg_size += embedded_bytes - 1; - } else { - msg->version = XPNET_VERSION; - } - msg->magic = XPNET_MAGIC; - msg->size = end_addr - start_addr; - msg->leadin_ignore = (u64)skb->data - start_addr; - msg->tailout_ignore = end_addr - (u64)skb_tail_pointer(skb); - msg->buf_pa = xp_pa((void *)start_addr); - - dev_dbg(xpnet, "sending XPC message to %d:%d\n" - "msg->buf_pa=0x%lx, msg->size=%u, " - "msg->leadin_ignore=%u, msg->tailout_ignore=%u\n", - dest_partid, XPC_NET_CHANNEL, msg->buf_pa, msg->size, - msg->leadin_ignore, msg->tailout_ignore); - - atomic_inc(&queued_msg->use_count); - - ret = xpc_send_notify(dest_partid, XPC_NET_CHANNEL, XPC_NOWAIT, msg, - msg_size, xpnet_send_completed, queued_msg); - if (unlikely(ret != xpSuccess)) - atomic_dec(&queued_msg->use_count); -} - -/* - * Network layer has formatted a packet (skb) and is ready to place it - * "on the wire". Prepare and send an xpnet_message to all partitions - * which have connected with us and are targets of this packet. - * - * MAC-NOTE: For the XPNET driver, the MAC address contains the - * destination partid. If the destination partid octets are 0xffff, - * this packet is to be broadcast to all connected partitions. - */ -static netdev_tx_t -xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct xpnet_pending_msg *queued_msg; - u64 start_addr, end_addr; - short dest_partid; - u16 embedded_bytes = 0; - - dev_dbg(xpnet, ">skb->head=0x%p skb->data=0x%p skb->tail=0x%p " - "skb->end=0x%p skb->len=%d\n", (void *)skb->head, - (void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb), - skb->len); - - if (skb->data[0] == 0x33) { - dev_kfree_skb(skb); - return NETDEV_TX_OK; /* nothing needed to be done */ - } - - /* - * The xpnet_pending_msg tracks how many outstanding - * xpc_send_notifies are relying on this skb. When none - * remain, release the skb. - */ - queued_msg = kmalloc_obj(struct xpnet_pending_msg, GFP_ATOMIC); - if (queued_msg == NULL) { - dev_warn(xpnet, "failed to kmalloc %ld bytes; dropping " - "packet\n", sizeof(struct xpnet_pending_msg)); - - dev->stats.tx_errors++; - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - /* get the beginning of the first cacheline and end of last */ - start_addr = ((u64)skb->data & ~(L1_CACHE_BYTES - 1)); - end_addr = L1_CACHE_ALIGN((u64)skb_tail_pointer(skb)); - - /* calculate how many bytes to embed in the XPC message */ - if (unlikely(skb->len <= XPNET_MSG_DATA_MAX)) { - /* skb->data does fit so embed */ - embedded_bytes = skb->len; - } - - /* - * Since the send occurs asynchronously, we set the count to one - * and begin sending. Any sends that happen to complete before - * we are done sending will not free the skb. We will be left - * with that task during exit. This also handles the case of - * a packet destined for a partition which is no longer up. - */ - atomic_set(&queued_msg->use_count, 1); - queued_msg->skb = skb; - - if (skb->data[0] == 0xff) { - /* we are being asked to broadcast to all partitions */ - for_each_set_bit(dest_partid, xpnet_broadcast_partitions, - xp_max_npartitions) { - - xpnet_send(skb, queued_msg, start_addr, end_addr, - embedded_bytes, dest_partid); - } - } else { - dest_partid = (short)skb->data[XPNET_PARTID_OCTET + 1]; - dest_partid |= (short)skb->data[XPNET_PARTID_OCTET + 0] << 8; - - if (dest_partid >= 0 && - dest_partid < xp_max_npartitions && - test_bit(dest_partid, xpnet_broadcast_partitions) != 0) { - - xpnet_send(skb, queued_msg, start_addr, end_addr, - embedded_bytes, dest_partid); - } - } - - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - if (atomic_dec_return(&queued_msg->use_count) == 0) { - dev_kfree_skb(skb); - kfree(queued_msg); - } - - return NETDEV_TX_OK; -} - -/* - * Deal with transmit timeouts coming from the network layer. - */ -static void -xpnet_dev_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - dev->stats.tx_errors++; -} - -static const struct net_device_ops xpnet_netdev_ops = { - .ndo_open = xpnet_dev_open, - .ndo_stop = xpnet_dev_stop, - .ndo_start_xmit = xpnet_dev_hard_start_xmit, - .ndo_tx_timeout = xpnet_dev_tx_timeout, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int __init -xpnet_init(void) -{ - u8 addr[ETH_ALEN]; - int result; - - if (!is_uv_system()) - return -ENODEV; - - dev_info(xpnet, "registering network device %s\n", XPNET_DEVICE_NAME); - - xpnet_broadcast_partitions = bitmap_zalloc(xp_max_npartitions, - GFP_KERNEL); - if (xpnet_broadcast_partitions == NULL) - return -ENOMEM; - - /* - * use ether_setup() to init the majority of our device - * structure and then override the necessary pieces. - */ - xpnet_device = alloc_netdev(0, XPNET_DEVICE_NAME, NET_NAME_UNKNOWN, - ether_setup); - if (xpnet_device == NULL) { - bitmap_free(xpnet_broadcast_partitions); - return -ENOMEM; - } - - netif_carrier_off(xpnet_device); - - xpnet_device->netdev_ops = &xpnet_netdev_ops; - xpnet_device->mtu = XPNET_DEF_MTU; - xpnet_device->min_mtu = XPNET_MIN_MTU; - xpnet_device->max_mtu = XPNET_MAX_MTU; - - memset(addr, 0, sizeof(addr)); - /* - * Multicast assumes the LSB of the first octet is set for multicast - * MAC addresses. We chose the first octet of the MAC to be unlikely - * to collide with any vendor's officially issued MAC. - */ - addr[0] = 0x02; /* locally administered, no OUI */ - - addr[XPNET_PARTID_OCTET + 1] = xp_partition_id; - addr[XPNET_PARTID_OCTET + 0] = (xp_partition_id >> 8); - eth_hw_addr_set(xpnet_device, addr); - - /* - * ether_setup() sets this to a multicast device. We are - * really not supporting multicast at this time. - */ - xpnet_device->flags &= ~IFF_MULTICAST; - - /* - * No need to checksum as it is a DMA transfer. The BTE will - * report an error if the data is not retrievable and the - * packet will be dropped. - */ - xpnet_device->features = NETIF_F_HW_CSUM; - - result = register_netdev(xpnet_device); - if (result != 0) { - free_netdev(xpnet_device); - bitmap_free(xpnet_broadcast_partitions); - } - - return result; -} - -module_init(xpnet_init); - -static void __exit -xpnet_exit(void) -{ - dev_info(xpnet, "unregistering network device %s\n", - xpnet_device[0].name); - - unregister_netdev(xpnet_device); - free_netdev(xpnet_device); - bitmap_free(xpnet_broadcast_partitions); -} - -module_exit(xpnet_exit); - -MODULE_AUTHOR("Silicon Graphics, Inc."); -MODULE_DESCRIPTION("Cross Partition Network adapter (XPNET)"); -MODULE_LICENSE("GPL"); From 85de4712bccfa0004bb50994001ee132869336af Mon Sep 17 00:00:00 2001 From: Dimitri Sivanich Date: Fri, 31 Jul 2026 09:37:15 -0500 Subject: [PATCH 478/513] misc: sgi-gru: Remove SGI GRU driver Due to security concerns, remove the SGI GRU driver, which cannot be used on anything newer than the long time unsupported UV2 platform. Signed-off-by: Dimitri Sivanich Acked-by: Muhammad Usama Anjum Acked-By: Robin Holt Acked-by: Steve Wahl Link: https://patch.msgid.link/amyzGw1-MVLpNH-d@hpe.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 5 - drivers/misc/Kconfig | 21 - drivers/misc/Makefile | 1 - drivers/misc/sgi-gru/Makefile | 6 - drivers/misc/sgi-gru/gru.h | 76 -- drivers/misc/sgi-gru/gru_instructions.h | 726 -------------- drivers/misc/sgi-gru/grufault.c | 800 ---------------- drivers/misc/sgi-gru/grufile.c | 540 ----------- drivers/misc/sgi-gru/gruhandles.c | 192 ---- drivers/misc/sgi-gru/gruhandles.h | 517 ---------- drivers/misc/sgi-gru/grukdump.c | 223 ----- drivers/misc/sgi-gru/grukservices.c | 1159 ----------------------- drivers/misc/sgi-gru/grukservices.h | 201 ---- drivers/misc/sgi-gru/grulib.h | 153 --- drivers/misc/sgi-gru/grumain.c | 969 ------------------- drivers/misc/sgi-gru/gruprocfs.c | 307 ------ drivers/misc/sgi-gru/grutables.h | 658 ------------- drivers/misc/sgi-gru/grutlbpurge.c | 316 ------ 18 files changed, 6870 deletions(-) delete mode 100644 drivers/misc/sgi-gru/Makefile delete mode 100644 drivers/misc/sgi-gru/gru.h delete mode 100644 drivers/misc/sgi-gru/gru_instructions.h delete mode 100644 drivers/misc/sgi-gru/grufault.c delete mode 100644 drivers/misc/sgi-gru/grufile.c delete mode 100644 drivers/misc/sgi-gru/gruhandles.c delete mode 100644 drivers/misc/sgi-gru/gruhandles.h delete mode 100644 drivers/misc/sgi-gru/grukdump.c delete mode 100644 drivers/misc/sgi-gru/grukservices.c delete mode 100644 drivers/misc/sgi-gru/grukservices.h delete mode 100644 drivers/misc/sgi-gru/grulib.h delete mode 100644 drivers/misc/sgi-gru/grumain.c delete mode 100644 drivers/misc/sgi-gru/gruprocfs.c delete mode 100644 drivers/misc/sgi-gru/grutables.h delete mode 100644 drivers/misc/sgi-gru/grutlbpurge.c diff --git a/MAINTAINERS b/MAINTAINERS index 52275c7169e1..c1be2a2df744 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24630,11 +24630,6 @@ F: include/linux/phylink.h F: include/linux/sfp.h K: phylink\.h|struct\s+phylink|\.phylink|>phylink_|phylink_(autoneg|clear|connect|create|destroy|disconnect|ethtool|helper|mac|mii|of|set|start|stop|test|validate) -SGI GRU DRIVER -M: Dimitri Sivanich -S: Maintained -F: drivers/misc/sgi-gru/ - SHARED MEMORY COMMUNICATIONS (SMC) SOCKETS M: D. Wythe M: Dust Li diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 1a718763a44f..7364931dad3a 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -275,27 +275,6 @@ config QCOM_FASTRPC applications DSP processor. Say M if you want to enable this module. -config SGI_GRU - tristate "SGI GRU driver" - depends on X86_UV && SMP - select MMU_NOTIFIER - help - The GRU is a hardware resource located in the system chipset. The GRU - contains memory that can be mmapped into the user address space. - This memory is used to communicate with the GRU to perform functions - such as load/store, scatter/gather, bcopy, AMOs, etc. The GRU is - directly accessed by user instructions using user virtual addresses. - GRU instructions (ex., bcopy) use user virtual addresses for operands. - - If you are not running on a SGI UV system, say N. - -config SGI_GRU_DEBUG - bool "SGI GRU driver debug" - depends on SGI_GRU - help - This option enables additional debugging code for the SGI GRU driver. - If you are unsure, say N. - config APDS9802ALS tristate "Medfield Avago APDS9802 ALS Sensor module" depends on I2C diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 2db1f9fe48b7..e8d8d5d88c0d 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -21,7 +21,6 @@ obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o obj-$(CONFIG_KGDB_TESTS) += kgdbts.o -obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_SMPRO_ERRMON) += smpro-errmon.o obj-$(CONFIG_SMPRO_MISC) += smpro-misc.o obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o diff --git a/drivers/misc/sgi-gru/Makefile b/drivers/misc/sgi-gru/Makefile deleted file mode 100644 index 8132116ec0f0..000000000000 --- a/drivers/misc/sgi-gru/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -ccflags-$(CONFIG_SGI_GRU_DEBUG) := -DDEBUG - -obj-$(CONFIG_SGI_GRU) := gru.o -gru-y := grufile.o grumain.o grufault.o grutlbpurge.o gruprocfs.o grukservices.o gruhandles.o grukdump.o - diff --git a/drivers/misc/sgi-gru/gru.h b/drivers/misc/sgi-gru/gru.h deleted file mode 100644 index 6ae045037219..000000000000 --- a/drivers/misc/sgi-gru/gru.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __GRU_H__ -#define __GRU_H__ - -/* - * GRU architectural definitions - */ -#define GRU_CACHE_LINE_BYTES 64 -#define GRU_HANDLE_STRIDE 256 -#define GRU_CB_BASE 0 -#define GRU_DS_BASE 0x20000 - -/* - * Size used to map GRU GSeg - */ -#if defined(CONFIG_X86_64) -#define GRU_GSEG_PAGESIZE (256 * 1024UL) /* ZZZ 2MB ??? */ -#else -#error "Unsupported architecture" -#endif - -/* - * Structure for obtaining GRU resource information - */ -struct gru_chiplet_info { - int node; - int chiplet; - int blade; - int total_dsr_bytes; - int total_cbr; - int total_user_dsr_bytes; - int total_user_cbr; - int free_user_dsr_bytes; - int free_user_cbr; -}; - -/* - * Statictics kept for each context. - */ -struct gru_gseg_statistics { - unsigned long fmm_tlbmiss; - unsigned long upm_tlbmiss; - unsigned long tlbdropin; - unsigned long context_stolen; - unsigned long reserved[10]; -}; - -/* Flags for GRU options on the gru_create_context() call */ -/* Select one of the follow 4 options to specify how TLB misses are handled */ -#define GRU_OPT_MISS_DEFAULT 0x0000 /* Use default mode */ -#define GRU_OPT_MISS_USER_POLL 0x0001 /* User will poll CB for faults */ -#define GRU_OPT_MISS_FMM_INTR 0x0002 /* Send interrupt to cpu to - handle fault */ -#define GRU_OPT_MISS_FMM_POLL 0x0003 /* Use system polling thread */ -#define GRU_OPT_MISS_MASK 0x0003 /* Mask for TLB MISS option */ - - - -#endif /* __GRU_H__ */ diff --git a/drivers/misc/sgi-gru/gru_instructions.h b/drivers/misc/sgi-gru/gru_instructions.h deleted file mode 100644 index da5eb9edf9ec..000000000000 --- a/drivers/misc/sgi-gru/gru_instructions.h +++ /dev/null @@ -1,726 +0,0 @@ -/* - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __GRU_INSTRUCTIONS_H__ -#define __GRU_INSTRUCTIONS_H__ - -extern int gru_check_status_proc(void *cb); -extern int gru_wait_proc(void *cb); -extern void gru_wait_abort_proc(void *cb); - - - -/* - * Architecture dependent functions - */ - -#if defined(CONFIG_X86_64) -#include -#define __flush_cache(p) clflush(p) -#define gru_ordered_store_ulong(p, v) \ - do { \ - barrier(); \ - *(unsigned long *)p = v; \ - } while (0) -#else -#error "Unsupported architecture" -#endif - -/* - * Control block status and exception codes - */ -#define CBS_IDLE 0 -#define CBS_EXCEPTION 1 -#define CBS_ACTIVE 2 -#define CBS_CALL_OS 3 - -/* CB substatus bitmasks */ -#define CBSS_MSG_QUEUE_MASK 7 -#define CBSS_IMPLICIT_ABORT_ACTIVE_MASK 8 - -/* CB substatus message queue values (low 3 bits of substatus) */ -#define CBSS_NO_ERROR 0 -#define CBSS_LB_OVERFLOWED 1 -#define CBSS_QLIMIT_REACHED 2 -#define CBSS_PAGE_OVERFLOW 3 -#define CBSS_AMO_NACKED 4 -#define CBSS_PUT_NACKED 5 - -/* - * Structure used to fetch exception detail for CBs that terminate with - * CBS_EXCEPTION - */ -struct control_block_extended_exc_detail { - unsigned long cb; - int opc; - int ecause; - int exopc; - long exceptdet0; - int exceptdet1; - int cbrstate; - int cbrexecstatus; -}; - -/* - * Instruction formats - */ - -/* - * Generic instruction format. - * This definition has precise bit field definitions. - */ -struct gru_instruction_bits { - /* DW 0 - low */ - unsigned int icmd: 1; - unsigned char ima: 3; /* CB_DelRep, unmapped mode */ - unsigned char reserved0: 4; - unsigned int xtype: 3; - unsigned int iaa0: 2; - unsigned int iaa1: 2; - unsigned char reserved1: 1; - unsigned char opc: 8; /* opcode */ - unsigned char exopc: 8; /* extended opcode */ - /* DW 0 - high */ - unsigned int idef2: 22; /* TRi0 */ - unsigned char reserved2: 2; - unsigned char istatus: 2; - unsigned char isubstatus:4; - unsigned char reserved3: 1; - unsigned char tlb_fault_color: 1; - /* DW 1 */ - unsigned long idef4; /* 42 bits: TRi1, BufSize */ - /* DW 2-6 */ - unsigned long idef1; /* BAddr0 */ - unsigned long idef5; /* Nelem */ - unsigned long idef6; /* Stride, Operand1 */ - unsigned long idef3; /* BAddr1, Value, Operand2 */ - unsigned long reserved4; - /* DW 7 */ - unsigned long avalue; /* AValue */ -}; - -/* - * Generic instruction with friendlier names. This format is used - * for inline instructions. - */ -struct gru_instruction { - /* DW 0 */ - union { - unsigned long op64; /* icmd,xtype,iaa0,ima,opc,tri0 */ - struct { - unsigned int op32; - unsigned int tri0; - }; - }; - unsigned long tri1_bufsize; /* DW 1 */ - unsigned long baddr0; /* DW 2 */ - unsigned long nelem; /* DW 3 */ - unsigned long op1_stride; /* DW 4 */ - unsigned long op2_value_baddr1; /* DW 5 */ - unsigned long reserved0; /* DW 6 */ - unsigned long avalue; /* DW 7 */ -}; - -/* Some shifts and masks for the low 64 bits of a GRU command */ -#define GRU_CB_ICMD_SHFT 0 -#define GRU_CB_ICMD_MASK 0x1 -#define GRU_CB_XTYPE_SHFT 8 -#define GRU_CB_XTYPE_MASK 0x7 -#define GRU_CB_IAA0_SHFT 11 -#define GRU_CB_IAA0_MASK 0x3 -#define GRU_CB_IAA1_SHFT 13 -#define GRU_CB_IAA1_MASK 0x3 -#define GRU_CB_IMA_SHFT 1 -#define GRU_CB_IMA_MASK 0x3 -#define GRU_CB_OPC_SHFT 16 -#define GRU_CB_OPC_MASK 0xff -#define GRU_CB_EXOPC_SHFT 24 -#define GRU_CB_EXOPC_MASK 0xff -#define GRU_IDEF2_SHFT 32 -#define GRU_IDEF2_MASK 0x3ffff -#define GRU_ISTATUS_SHFT 56 -#define GRU_ISTATUS_MASK 0x3 - -/* GRU instruction opcodes (opc field) */ -#define OP_NOP 0x00 -#define OP_BCOPY 0x01 -#define OP_VLOAD 0x02 -#define OP_IVLOAD 0x03 -#define OP_VSTORE 0x04 -#define OP_IVSTORE 0x05 -#define OP_VSET 0x06 -#define OP_IVSET 0x07 -#define OP_MESQ 0x08 -#define OP_GAMXR 0x09 -#define OP_GAMIR 0x0a -#define OP_GAMIRR 0x0b -#define OP_GAMER 0x0c -#define OP_GAMERR 0x0d -#define OP_BSTORE 0x0e -#define OP_VFLUSH 0x0f - - -/* Extended opcodes values (exopc field) */ - -/* GAMIR - AMOs with implicit operands */ -#define EOP_IR_FETCH 0x01 /* Plain fetch of memory */ -#define EOP_IR_CLR 0x02 /* Fetch and clear */ -#define EOP_IR_INC 0x05 /* Fetch and increment */ -#define EOP_IR_DEC 0x07 /* Fetch and decrement */ -#define EOP_IR_QCHK1 0x0d /* Queue check, 64 byte msg */ -#define EOP_IR_QCHK2 0x0e /* Queue check, 128 byte msg */ - -/* GAMIRR - Registered AMOs with implicit operands */ -#define EOP_IRR_FETCH 0x01 /* Registered fetch of memory */ -#define EOP_IRR_CLR 0x02 /* Registered fetch and clear */ -#define EOP_IRR_INC 0x05 /* Registered fetch and increment */ -#define EOP_IRR_DEC 0x07 /* Registered fetch and decrement */ -#define EOP_IRR_DECZ 0x0f /* Registered fetch and decrement, update on zero*/ - -/* GAMER - AMOs with explicit operands */ -#define EOP_ER_SWAP 0x00 /* Exchange argument and memory */ -#define EOP_ER_OR 0x01 /* Logical OR with memory */ -#define EOP_ER_AND 0x02 /* Logical AND with memory */ -#define EOP_ER_XOR 0x03 /* Logical XOR with memory */ -#define EOP_ER_ADD 0x04 /* Add value to memory */ -#define EOP_ER_CSWAP 0x08 /* Compare with operand2, write operand1 if match*/ -#define EOP_ER_CADD 0x0c /* Queue check, operand1*64 byte msg */ - -/* GAMERR - Registered AMOs with explicit operands */ -#define EOP_ERR_SWAP 0x00 /* Exchange argument and memory */ -#define EOP_ERR_OR 0x01 /* Logical OR with memory */ -#define EOP_ERR_AND 0x02 /* Logical AND with memory */ -#define EOP_ERR_XOR 0x03 /* Logical XOR with memory */ -#define EOP_ERR_ADD 0x04 /* Add value to memory */ -#define EOP_ERR_CSWAP 0x08 /* Compare with operand2, write operand1 if match*/ -#define EOP_ERR_EPOLL 0x09 /* Poll for equality */ -#define EOP_ERR_NPOLL 0x0a /* Poll for inequality */ - -/* GAMXR - SGI Arithmetic unit */ -#define EOP_XR_CSWAP 0x0b /* Masked compare exchange */ - - -/* Transfer types (xtype field) */ -#define XTYPE_B 0x0 /* byte */ -#define XTYPE_S 0x1 /* short (2-byte) */ -#define XTYPE_W 0x2 /* word (4-byte) */ -#define XTYPE_DW 0x3 /* doubleword (8-byte) */ -#define XTYPE_CL 0x6 /* cacheline (64-byte) */ - - -/* Instruction access attributes (iaa0, iaa1 fields) */ -#define IAA_RAM 0x0 /* normal cached RAM access */ -#define IAA_NCRAM 0x2 /* noncoherent RAM access */ -#define IAA_MMIO 0x1 /* noncoherent memory-mapped I/O space */ -#define IAA_REGISTER 0x3 /* memory-mapped registers, etc. */ - - -/* Instruction mode attributes (ima field) */ -#define IMA_MAPPED 0x0 /* Virtual mode */ -#define IMA_CB_DELAY 0x1 /* hold read responses until status changes */ -#define IMA_UNMAPPED 0x2 /* bypass the TLBs (OS only) */ -#define IMA_INTERRUPT 0x4 /* Interrupt when instruction completes */ - -/* CBE ecause bits */ -#define CBE_CAUSE_RI (1 << 0) -#define CBE_CAUSE_INVALID_INSTRUCTION (1 << 1) -#define CBE_CAUSE_UNMAPPED_MODE_FORBIDDEN (1 << 2) -#define CBE_CAUSE_PE_CHECK_DATA_ERROR (1 << 3) -#define CBE_CAUSE_IAA_GAA_MISMATCH (1 << 4) -#define CBE_CAUSE_DATA_SEGMENT_LIMIT_EXCEPTION (1 << 5) -#define CBE_CAUSE_OS_FATAL_TLB_FAULT (1 << 6) -#define CBE_CAUSE_EXECUTION_HW_ERROR (1 << 7) -#define CBE_CAUSE_TLBHW_ERROR (1 << 8) -#define CBE_CAUSE_RA_REQUEST_TIMEOUT (1 << 9) -#define CBE_CAUSE_HA_REQUEST_TIMEOUT (1 << 10) -#define CBE_CAUSE_RA_RESPONSE_FATAL (1 << 11) -#define CBE_CAUSE_RA_RESPONSE_NON_FATAL (1 << 12) -#define CBE_CAUSE_HA_RESPONSE_FATAL (1 << 13) -#define CBE_CAUSE_HA_RESPONSE_NON_FATAL (1 << 14) -#define CBE_CAUSE_ADDRESS_SPACE_DECODE_ERROR (1 << 15) -#define CBE_CAUSE_PROTOCOL_STATE_DATA_ERROR (1 << 16) -#define CBE_CAUSE_RA_RESPONSE_DATA_ERROR (1 << 17) -#define CBE_CAUSE_HA_RESPONSE_DATA_ERROR (1 << 18) -#define CBE_CAUSE_FORCED_ERROR (1 << 19) - -/* CBE cbrexecstatus bits */ -#define CBR_EXS_ABORT_OCC_BIT 0 -#define CBR_EXS_INT_OCC_BIT 1 -#define CBR_EXS_PENDING_BIT 2 -#define CBR_EXS_QUEUED_BIT 3 -#define CBR_EXS_TLB_INVAL_BIT 4 -#define CBR_EXS_EXCEPTION_BIT 5 -#define CBR_EXS_CB_INT_PENDING_BIT 6 - -#define CBR_EXS_ABORT_OCC (1 << CBR_EXS_ABORT_OCC_BIT) -#define CBR_EXS_INT_OCC (1 << CBR_EXS_INT_OCC_BIT) -#define CBR_EXS_PENDING (1 << CBR_EXS_PENDING_BIT) -#define CBR_EXS_QUEUED (1 << CBR_EXS_QUEUED_BIT) -#define CBR_EXS_TLB_INVAL (1 << CBR_EXS_TLB_INVAL_BIT) -#define CBR_EXS_EXCEPTION (1 << CBR_EXS_EXCEPTION_BIT) -#define CBR_EXS_CB_INT_PENDING (1 << CBR_EXS_CB_INT_PENDING_BIT) - -/* - * Exceptions are retried for the following cases. If any OTHER bits are set - * in ecause, the exception is not retryable. - */ -#define EXCEPTION_RETRY_BITS (CBE_CAUSE_EXECUTION_HW_ERROR | \ - CBE_CAUSE_TLBHW_ERROR | \ - CBE_CAUSE_RA_REQUEST_TIMEOUT | \ - CBE_CAUSE_RA_RESPONSE_NON_FATAL | \ - CBE_CAUSE_HA_RESPONSE_NON_FATAL | \ - CBE_CAUSE_RA_RESPONSE_DATA_ERROR | \ - CBE_CAUSE_HA_RESPONSE_DATA_ERROR \ - ) - -/* Message queue head structure */ -union gru_mesqhead { - unsigned long val; - struct { - unsigned int head; - unsigned int limit; - }; -}; - - -/* Generate the low word of a GRU instruction */ -static inline unsigned long -__opdword(unsigned char opcode, unsigned char exopc, unsigned char xtype, - unsigned char iaa0, unsigned char iaa1, - unsigned long idef2, unsigned char ima) -{ - return (1 << GRU_CB_ICMD_SHFT) | - ((unsigned long)CBS_ACTIVE << GRU_ISTATUS_SHFT) | - (idef2<< GRU_IDEF2_SHFT) | - (iaa0 << GRU_CB_IAA0_SHFT) | - (iaa1 << GRU_CB_IAA1_SHFT) | - (ima << GRU_CB_IMA_SHFT) | - (xtype << GRU_CB_XTYPE_SHFT) | - (opcode << GRU_CB_OPC_SHFT) | - (exopc << GRU_CB_EXOPC_SHFT); -} - -/* - * Architecture specific intrinsics - */ -static inline void gru_flush_cache(void *p) -{ - __flush_cache(p); -} - -/* - * Store the lower 64 bits of the command including the "start" bit. Then - * start the instruction executing. - */ -static inline void gru_start_instruction(struct gru_instruction *ins, unsigned long op64) -{ - gru_ordered_store_ulong(ins, op64); - mb(); - gru_flush_cache(ins); -} - - -/* Convert "hints" to IMA */ -#define CB_IMA(h) ((h) | IMA_UNMAPPED) - -/* Convert data segment cache line index into TRI0 / TRI1 value */ -#define GRU_DINDEX(i) ((i) * GRU_CACHE_LINE_BYTES) - -/* Inline functions for GRU instructions. - * Note: - * - nelem and stride are in elements - * - tri0/tri1 is in bytes for the beginning of the data segment. - */ -static inline void gru_vload_phys(void *cb, unsigned long gpa, - unsigned int tri0, int iaa, unsigned long hints) -{ - struct gru_instruction *ins = (struct gru_instruction *)cb; - - ins->baddr0 = (long)gpa | ((unsigned long)iaa << 62); - ins->nelem = 1; - ins->op1_stride = 1; - gru_start_instruction(ins, __opdword(OP_VLOAD, 0, XTYPE_DW, iaa, 0, - (unsigned long)tri0, CB_IMA(hints))); -} - -static inline void gru_vstore_phys(void *cb, unsigned long gpa, - unsigned int tri0, int iaa, unsigned long hints) -{ - struct gru_instruction *ins = (struct gru_instruction *)cb; - - ins->baddr0 = (long)gpa | ((unsigned long)iaa << 62); - ins->nelem = 1; - ins->op1_stride = 1; - gru_start_instruction(ins, __opdword(OP_VSTORE, 0, XTYPE_DW, iaa, 0, - (unsigned long)tri0, CB_IMA(hints))); -} - -static inline void gru_vload(void *cb, unsigned long mem_addr, - unsigned int tri0, unsigned char xtype, unsigned long nelem, - unsigned long stride, unsigned long hints) -{ - struct gru_instruction *ins = (struct gru_instruction *)cb; - - ins->baddr0 = (long)mem_addr; - ins->nelem = nelem; - ins->op1_stride = stride; - gru_start_instruction(ins, __opdword(OP_VLOAD, 0, xtype, IAA_RAM, 0, - (unsigned long)tri0, CB_IMA(hints))); -} - -static inline void gru_vstore(void *cb, unsigned long mem_addr, - unsigned int tri0, unsigned char xtype, unsigned long nelem, - unsigned long stride, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->nelem = nelem; - ins->op1_stride = stride; - gru_start_instruction(ins, __opdword(OP_VSTORE, 0, xtype, IAA_RAM, 0, - tri0, CB_IMA(hints))); -} - -static inline void gru_ivload(void *cb, unsigned long mem_addr, - unsigned int tri0, unsigned int tri1, unsigned char xtype, - unsigned long nelem, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->nelem = nelem; - ins->tri1_bufsize = tri1; - gru_start_instruction(ins, __opdword(OP_IVLOAD, 0, xtype, IAA_RAM, 0, - tri0, CB_IMA(hints))); -} - -static inline void gru_ivstore(void *cb, unsigned long mem_addr, - unsigned int tri0, unsigned int tri1, - unsigned char xtype, unsigned long nelem, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->nelem = nelem; - ins->tri1_bufsize = tri1; - gru_start_instruction(ins, __opdword(OP_IVSTORE, 0, xtype, IAA_RAM, 0, - tri0, CB_IMA(hints))); -} - -static inline void gru_vset(void *cb, unsigned long mem_addr, - unsigned long value, unsigned char xtype, unsigned long nelem, - unsigned long stride, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->op2_value_baddr1 = value; - ins->nelem = nelem; - ins->op1_stride = stride; - gru_start_instruction(ins, __opdword(OP_VSET, 0, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_ivset(void *cb, unsigned long mem_addr, - unsigned int tri1, unsigned long value, unsigned char xtype, - unsigned long nelem, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->op2_value_baddr1 = value; - ins->nelem = nelem; - ins->tri1_bufsize = tri1; - gru_start_instruction(ins, __opdword(OP_IVSET, 0, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_vflush(void *cb, unsigned long mem_addr, - unsigned long nelem, unsigned char xtype, unsigned long stride, - unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)mem_addr; - ins->op1_stride = stride; - ins->nelem = nelem; - gru_start_instruction(ins, __opdword(OP_VFLUSH, 0, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_nop(void *cb, int hints) -{ - struct gru_instruction *ins = (void *)cb; - - gru_start_instruction(ins, __opdword(OP_NOP, 0, 0, 0, 0, 0, CB_IMA(hints))); -} - - -static inline void gru_bcopy(void *cb, const unsigned long src, - unsigned long dest, - unsigned int tri0, unsigned int xtype, unsigned long nelem, - unsigned int bufsize, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - ins->op2_value_baddr1 = (long)dest; - ins->nelem = nelem; - ins->tri1_bufsize = bufsize; - gru_start_instruction(ins, __opdword(OP_BCOPY, 0, xtype, IAA_RAM, - IAA_RAM, tri0, CB_IMA(hints))); -} - -static inline void gru_bstore(void *cb, const unsigned long src, - unsigned long dest, unsigned int tri0, unsigned int xtype, - unsigned long nelem, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - ins->op2_value_baddr1 = (long)dest; - ins->nelem = nelem; - gru_start_instruction(ins, __opdword(OP_BSTORE, 0, xtype, 0, IAA_RAM, - tri0, CB_IMA(hints))); -} - -static inline void gru_gamir(void *cb, int exopc, unsigned long src, - unsigned int xtype, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - gru_start_instruction(ins, __opdword(OP_GAMIR, exopc, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_gamirr(void *cb, int exopc, unsigned long src, - unsigned int xtype, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - gru_start_instruction(ins, __opdword(OP_GAMIRR, exopc, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_gamer(void *cb, int exopc, unsigned long src, - unsigned int xtype, - unsigned long operand1, unsigned long operand2, - unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - ins->op1_stride = operand1; - ins->op2_value_baddr1 = operand2; - gru_start_instruction(ins, __opdword(OP_GAMER, exopc, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_gamerr(void *cb, int exopc, unsigned long src, - unsigned int xtype, unsigned long operand1, - unsigned long operand2, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - ins->op1_stride = operand1; - ins->op2_value_baddr1 = operand2; - gru_start_instruction(ins, __opdword(OP_GAMERR, exopc, xtype, IAA_RAM, 0, - 0, CB_IMA(hints))); -} - -static inline void gru_gamxr(void *cb, unsigned long src, - unsigned int tri0, unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)src; - ins->nelem = 4; - gru_start_instruction(ins, __opdword(OP_GAMXR, EOP_XR_CSWAP, XTYPE_DW, - IAA_RAM, 0, 0, CB_IMA(hints))); -} - -static inline void gru_mesq(void *cb, unsigned long queue, - unsigned long tri0, unsigned long nelem, - unsigned long hints) -{ - struct gru_instruction *ins = (void *)cb; - - ins->baddr0 = (long)queue; - ins->nelem = nelem; - gru_start_instruction(ins, __opdword(OP_MESQ, 0, XTYPE_CL, IAA_RAM, 0, - tri0, CB_IMA(hints))); -} - -static inline unsigned long gru_get_amo_value(void *cb) -{ - struct gru_instruction *ins = (void *)cb; - - return ins->avalue; -} - -static inline int gru_get_amo_value_head(void *cb) -{ - struct gru_instruction *ins = (void *)cb; - - return ins->avalue & 0xffffffff; -} - -static inline int gru_get_amo_value_limit(void *cb) -{ - struct gru_instruction *ins = (void *)cb; - - return ins->avalue >> 32; -} - -static inline union gru_mesqhead gru_mesq_head(int head, int limit) -{ - union gru_mesqhead mqh; - - mqh.head = head; - mqh.limit = limit; - return mqh; -} - -/* - * Get struct control_block_extended_exc_detail for CB. - */ -extern int gru_get_cb_exception_detail(void *cb, - struct control_block_extended_exc_detail *excdet); - -#define GRU_EXC_STR_SIZE 256 - - -/* - * Control block definition for checking status - */ -struct gru_control_block_status { - unsigned int icmd :1; - unsigned int ima :3; - unsigned int reserved0 :4; - unsigned int unused1 :24; - unsigned int unused2 :24; - unsigned int istatus :2; - unsigned int isubstatus :4; - unsigned int unused3 :2; -}; - -/* Get CB status */ -static inline int gru_get_cb_status(void *cb) -{ - struct gru_control_block_status *cbs = (void *)cb; - - return cbs->istatus; -} - -/* Get CB message queue substatus */ -static inline int gru_get_cb_message_queue_substatus(void *cb) -{ - struct gru_control_block_status *cbs = (void *)cb; - - return cbs->isubstatus & CBSS_MSG_QUEUE_MASK; -} - -/* Get CB substatus */ -static inline int gru_get_cb_substatus(void *cb) -{ - struct gru_control_block_status *cbs = (void *)cb; - - return cbs->isubstatus; -} - -/* - * User interface to check an instruction status. UPM and exceptions - * are handled automatically. However, this function does NOT wait - * for an active instruction to complete. - * - */ -static inline int gru_check_status(void *cb) -{ - struct gru_control_block_status *cbs = (void *)cb; - int ret; - - ret = cbs->istatus; - if (ret != CBS_ACTIVE) - ret = gru_check_status_proc(cb); - return ret; -} - -/* - * User interface (via inline function) to wait for an instruction - * to complete. Completion status (IDLE or EXCEPTION is returned - * to the user. Exception due to hardware errors are automatically - * retried before returning an exception. - * - */ -static inline int gru_wait(void *cb) -{ - return gru_wait_proc(cb); -} - -/* - * Wait for CB to complete. Aborts program if error. (Note: error does NOT - * mean TLB mis - only fatal errors such as memory parity error or user - * bugs will cause termination. - */ -static inline void gru_wait_abort(void *cb) -{ - gru_wait_abort_proc(cb); -} - -/* - * Get a pointer to the start of a gseg - * p - Any valid pointer within the gseg - */ -static inline void *gru_get_gseg_pointer (void *p) -{ - return (void *)((unsigned long)p & ~(GRU_GSEG_PAGESIZE - 1)); -} - -/* - * Get a pointer to a control block - * gseg - GSeg address returned from gru_get_thread_gru_segment() - * index - index of desired CB - */ -static inline void *gru_get_cb_pointer(void *gseg, - int index) -{ - return gseg + GRU_CB_BASE + index * GRU_HANDLE_STRIDE; -} - -/* - * Get a pointer to a cacheline in the data segment portion of a GSeg - * gseg - GSeg address returned from gru_get_thread_gru_segment() - * index - index of desired cache line - */ -static inline void *gru_get_data_pointer(void *gseg, int index) -{ - return gseg + GRU_DS_BASE + index * GRU_CACHE_LINE_BYTES; -} - -/* - * Convert a vaddr into the tri index within the GSEG - * vaddr - virtual address of within gseg - */ -static inline int gru_get_tri(void *vaddr) -{ - return ((unsigned long)vaddr & (GRU_GSEG_PAGESIZE - 1)) - GRU_DS_BASE; -} -#endif /* __GRU_INSTRUCTIONS_H__ */ diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c deleted file mode 100644 index 6b7e7dc37eac..000000000000 --- a/drivers/misc/sgi-gru/grufault.c +++ /dev/null @@ -1,800 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * FAULT HANDLER FOR GRU DETECTED TLB MISSES - * - * This file contains code that handles TLB misses within the GRU. - * These misses are reported either via interrupts or user polling of - * the user CB. - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "gru.h" -#include "grutables.h" -#include "grulib.h" -#include "gru_instructions.h" -#include - -/* Return codes for vtop functions */ -#define VTOP_SUCCESS 0 -#define VTOP_INVALID -1 - - -/* - * Test if a physical address is a valid GRU GSEG address - */ -static inline int is_gru_paddr(unsigned long paddr) -{ - return paddr >= gru_start_paddr && paddr < gru_end_paddr; -} - -/* - * Find the vma of a GRU segment. Caller must hold mmap_lock. - */ -struct vm_area_struct *gru_find_vma(unsigned long vaddr) -{ - struct vm_area_struct *vma; - - vma = vma_lookup(current->mm, vaddr); - if (vma && vma->vm_ops == &gru_vm_ops) - return vma; - return NULL; -} - -/* - * Find and lock the gts that contains the specified user vaddr. - * - * Returns: - * - *gts with the mmap_lock locked for read and the GTS locked. - * - NULL if vaddr invalid OR is not a valid GSEG vaddr. - */ - -static struct gru_thread_state *gru_find_lock_gts(unsigned long vaddr) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - struct gru_thread_state *gts = NULL; - - mmap_read_lock(mm); - vma = gru_find_vma(vaddr); - if (vma) - gts = gru_find_thread_state(vma, TSID(vaddr, vma)); - if (gts) - mutex_lock(>s->ts_ctxlock); - else - mmap_read_unlock(mm); - return gts; -} - -static struct gru_thread_state *gru_alloc_locked_gts(unsigned long vaddr) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - struct gru_thread_state *gts = ERR_PTR(-EINVAL); - - mmap_write_lock(mm); - vma = gru_find_vma(vaddr); - if (!vma) - goto err; - - gts = gru_alloc_thread_state(vma, TSID(vaddr, vma)); - if (IS_ERR(gts)) - goto err; - mutex_lock(>s->ts_ctxlock); - mmap_write_downgrade(mm); - return gts; - -err: - mmap_write_unlock(mm); - return gts; -} - -/* - * Unlock a GTS that was previously locked with gru_find_lock_gts(). - */ -static void gru_unlock_gts(struct gru_thread_state *gts) -{ - mutex_unlock(>s->ts_ctxlock); - mmap_read_unlock(current->mm); -} - -/* - * Read & clear a TFM - * - * The GRU has an array of fault maps. A map is private to a cpu - * Only one cpu will be accessing a cpu's fault map. - * - * This function scans the cpu-private fault map & clears all bits that - * are set. The function returns a bitmap that indicates the bits that - * were cleared. Note that sense the maps may be updated asynchronously by - * the GRU, atomic operations must be used to clear bits. - */ -static void get_clear_fault_map(struct gru_state *gru, - struct gru_tlb_fault_map *imap, - struct gru_tlb_fault_map *dmap) -{ - unsigned long i, k; - struct gru_tlb_fault_map *tfm; - - tfm = get_tfm_for_cpu(gru, gru_cpu_fault_map_id()); - prefetchw(tfm); /* Helps on hardware, required for emulator */ - for (i = 0; i < BITS_TO_LONGS(GRU_NUM_CBE); i++) { - k = tfm->fault_bits[i]; - if (k) - k = xchg(&tfm->fault_bits[i], 0UL); - imap->fault_bits[i] = k; - k = tfm->done_bits[i]; - if (k) - k = xchg(&tfm->done_bits[i], 0UL); - dmap->fault_bits[i] = k; - } - - /* - * Not functionally required but helps performance. (Required - * on emulator) - */ - gru_flush_cache(tfm); -} - -/* - * Convert a user virtual address to a physical address in process context. - * The size of the page is returned in pageshift. - */ -static int non_atomic_pte_lookup(struct vm_area_struct *vma, - unsigned long vaddr, int write, - unsigned long *paddr, int *pageshift) -{ - struct page *page; - -#ifdef CONFIG_HUGETLB_PAGE - *pageshift = is_vm_hugetlb_page(vma) ? HPAGE_SHIFT : PAGE_SHIFT; -#else - *pageshift = PAGE_SHIFT; -#endif - if (get_user_pages(vaddr, 1, write ? FOLL_WRITE : 0, &page) <= 0) - return -EFAULT; - *paddr = page_to_phys(page); - put_page(page); - return 0; -} - -static int gru_vtop(struct gru_thread_state *gts, unsigned long vaddr, - int write, unsigned long *gpa, int *pageshift) -{ - struct mm_struct *mm = gts->ts_mm; - struct vm_area_struct *vma; - unsigned long paddr; - int ps; - - vma = find_vma(mm, vaddr); - if (!vma) - goto inval; - - /* Order the caller's ms_range_active check before loading PTEs. */ - rmb(); - if (non_atomic_pte_lookup(vma, vaddr, write, &paddr, &ps)) - goto inval; - if (is_gru_paddr(paddr)) - goto inval; - paddr = paddr & ~((1UL << ps) - 1); - *gpa = uv_soc_phys_ram_to_gpa(paddr); - *pageshift = ps; - return VTOP_SUCCESS; - -inval: - return VTOP_INVALID; -} - - -/* - * Flush a CBE from cache. The CBE is clean in the cache. Dirty the - * CBE cacheline so that the line will be written back to home agent. - * Otherwise the line may be silently dropped. This has no impact - * except on performance. - */ -static void gru_flush_cache_cbe(struct gru_control_block_extended *cbe) -{ - if (unlikely(cbe)) { - cbe->cbrexecstatus = 0; /* make CL dirty */ - gru_flush_cache(cbe); - } -} - -/* - * Preload the TLB with entries that may be required. Currently, preloading - * is implemented only for BCOPY. Preload pages OR to - * the end of the bcopy tranfer, whichever is smaller. - */ -static void gru_preload_tlb(struct gru_state *gru, - struct gru_thread_state *gts, - unsigned long fault_vaddr, int asid, int write, - unsigned char tlb_preload_count, - struct gru_tlb_fault_handle *tfh, - struct gru_control_block_extended *cbe) -{ - unsigned long vaddr = 0, gpa; - int ret, pageshift; - - if (cbe->opccpy != OP_BCOPY) - return; - - if (fault_vaddr == cbe->cbe_baddr0) - vaddr = fault_vaddr + GRU_CACHE_LINE_BYTES * cbe->cbe_src_cl - 1; - else if (fault_vaddr == cbe->cbe_baddr1) - vaddr = fault_vaddr + (1 << cbe->xtypecpy) * cbe->cbe_nelemcur - 1; - - fault_vaddr &= PAGE_MASK; - vaddr &= PAGE_MASK; - vaddr = min(vaddr, fault_vaddr + tlb_preload_count * PAGE_SIZE); - - while (vaddr > fault_vaddr) { - ret = gru_vtop(gts, vaddr, write, &gpa, &pageshift); - if (ret || tfh_write_only(tfh, gpa, GAA_RAM, vaddr, asid, write, - GRU_PAGESIZE(pageshift))) - return; - gru_dbg(grudev, - "gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, rw %d, ps %d, gpa 0x%lx\n", - gru->gs_gid, gts, tfh, - vaddr, asid, write, pageshift, gpa); - vaddr -= PAGE_SIZE; - STAT(tlb_preload_page); - } -} - -/* - * Drop a TLB entry into the GRU. The fault is described by info in a TFH. - * Input: - * cbk Address of the user CBR - * Return: - * 0 = dropin, exception, or switch to UPM successful - * 1 = retry required - * < 0 = error code - * - */ -static int gru_try_dropin(struct gru_state *gru, - struct gru_thread_state *gts, - struct gru_tlb_fault_handle *tfh, - struct gru_instruction_bits *cbk) -{ - struct gru_control_block_extended *cbe = NULL; - unsigned char tlb_preload_count = gts->ts_tlb_preload_count; - int pageshift = 0, asid, write, ret, indexway; - unsigned long gpa = 0, vaddr = 0; - - /* - * NOTE: The GRU contains magic hardware that eliminates races between - * TLB invalidates and TLB dropins. If an invalidate occurs - * in the window between reading the TFH and the subsequent TLB dropin, - * the dropin is ignored. This eliminates the need for additional locks. - */ - - /* - * Prefetch the CBE if doing TLB preloading - */ - if (unlikely(tlb_preload_count)) { - cbe = gru_tfh_to_cbe(tfh); - prefetchw(cbe); - } - - /* - * Error if TFH state is IDLE or FMM mode & the user issuing a UPM call. - * Might be a hardware race OR a stupid user. Ignore FMM because FMM - * is a transient state. - */ - if (tfh->status != TFHSTATUS_EXCEPTION) { - gru_flush_cache(tfh); - sync_core(); - if (tfh->status != TFHSTATUS_EXCEPTION) - goto failnoexception; - STAT(tfh_stale_on_fault); - } - if (tfh->state == TFHSTATE_IDLE) - goto failidle; - if (tfh->state == TFHSTATE_MISS_FMM) - goto failfmm; - - write = (tfh->cause & TFHCAUSE_TLB_MOD) != 0; - vaddr = tfh->missvaddr; - asid = tfh->missasid; - indexway = tfh->indexway; - if (asid == 0) - goto failnoasid; - - rmb(); /* TFH must be cache resident before reading ms_range_active */ - - /* - * TFH is cache resident - at least briefly. Fail the dropin - * if a range invalidate is active. - */ - if (atomic_read(>s->ts_gms->ms_range_active)) - goto failactive; - - ret = gru_vtop(gts, vaddr, write, &gpa, &pageshift); - if (ret == VTOP_INVALID) - goto failinval; - - if (!(gts->ts_sizeavail & GRU_SIZEAVAIL(pageshift))) { - gts->ts_sizeavail |= GRU_SIZEAVAIL(pageshift); - if (!gru_update_cch(gts)) { - gts->ts_force_cch_reload = 1; - goto failupm; - } - } - - if (unlikely(cbe) && pageshift == PAGE_SHIFT) { - gru_preload_tlb(gru, gts, vaddr, asid, write, tlb_preload_count, tfh, cbe); - gru_flush_cache_cbe(cbe); - } - - /* - * Set CB.istatus active in cache before restarting the TFH to avoid - * exposing stale pre-restart status. Cacheline eviction may lose the - * update, but an occasional stale status is harmless. - */ - cbk->istatus = CBS_ACTIVE; - gts->ustats.tlbdropin++; - tfh_write_restart(tfh, gpa, GAA_RAM, vaddr, asid, write, - GRU_PAGESIZE(pageshift)); - gru_dbg(grudev, - "gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, indexway 0x%x," - " rw %d, ps %d, gpa 0x%lx\n", - gru->gs_gid, gts, tfh, vaddr, asid, - indexway, write, pageshift, gpa); - STAT(tlb_dropin); - return 0; - -failnoasid: - /* No asid (delayed unload). */ - STAT(tlb_dropin_fail_no_asid); - gru_dbg(grudev, "FAILED no_asid tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); - gru_flush_cache(tfh); - gru_flush_cache_cbe(cbe); - return -EAGAIN; - -failupm: - /* CCH update failure switches the CBR back to UPM. */ - tfh_user_polling_mode(tfh); - gru_flush_cache_cbe(cbe); - STAT(tlb_dropin_fail_upm); - gru_dbg(grudev, "FAILED upm tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); - return 1; - -failfmm: - /* FMM state on UPM call */ - gru_flush_cache(tfh); - gru_flush_cache_cbe(cbe); - STAT(tlb_dropin_fail_fmm); - gru_dbg(grudev, "FAILED fmm tfh: 0x%p, state %d\n", tfh, tfh->state); - return 0; - -failnoexception: - /* TFH status did not show exception pending */ - gru_flush_cache(tfh); - gru_flush_cache_cbe(cbe); - gru_flush_cache(cbk); - STAT(tlb_dropin_fail_no_exception); - gru_dbg(grudev, "FAILED non-exception tfh: 0x%p, status %d, state %d\n", - tfh, tfh->status, tfh->state); - return 0; - -failidle: - /* TFH state was idle - no miss pending */ - gru_flush_cache(tfh); - gru_flush_cache_cbe(cbe); - gru_flush_cache(cbk); - STAT(tlb_dropin_fail_idle); - gru_dbg(grudev, "FAILED idle tfh: 0x%p, state %d\n", tfh, tfh->state); - return 0; - -failinval: - /* Invalid translations switch the CBR to EXCEPTION state. */ - tfh_exception(tfh); - gru_flush_cache_cbe(cbe); - STAT(tlb_dropin_fail_invalid); - gru_dbg(grudev, "FAILED inval tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr); - return -EFAULT; - -failactive: - /* Retry after the active range invalidation completes. */ - gru_flush_cache(tfh); - gru_flush_cache_cbe(cbe); - STAT(tlb_dropin_fail_range_active); - gru_dbg(grudev, "FAILED range active: tfh 0x%p, vaddr 0x%lx\n", - tfh, vaddr); - return 1; -} - -/* - * Process an external interrupt from the GRU. This interrupt is - * caused by a TLB miss. - * Note that this is the interrupt handler that is registered with linux - * interrupt handlers. - */ -static irqreturn_t gru_intr(int chiplet, int blade) -{ - struct gru_state *gru; - struct gru_tlb_fault_map imap, dmap; - struct gru_thread_state *gts; - struct gru_tlb_fault_handle *tfh = NULL; - struct completion *cmp; - int cbrnum, ctxnum; - - STAT(intr); - - gru = &gru_base[blade]->bs_grus[chiplet]; - if (!gru) { - dev_err(grudev, "GRU: invalid interrupt: cpu %d, chiplet %d\n", - raw_smp_processor_id(), chiplet); - return IRQ_NONE; - } - get_clear_fault_map(gru, &imap, &dmap); - gru_dbg(grudev, - "cpu %d, chiplet %d, gid %d, imap %016lx %016lx, dmap %016lx %016lx\n", - smp_processor_id(), chiplet, gru->gs_gid, - imap.fault_bits[0], imap.fault_bits[1], - dmap.fault_bits[0], dmap.fault_bits[1]); - - for_each_cbr_in_tfm(cbrnum, dmap.fault_bits) { - STAT(intr_cbr); - cmp = gru->gs_blade->bs_async_wq; - if (cmp) - complete(cmp); - gru_dbg(grudev, "gid %d, cbr_done %d, done %d\n", - gru->gs_gid, cbrnum, cmp ? cmp->done : -1); - } - - for_each_cbr_in_tfm(cbrnum, imap.fault_bits) { - STAT(intr_tfh); - tfh = get_tfh_by_index(gru, cbrnum); - prefetchw(tfh); /* Helps on hdw, required for emulator */ - - /* - * When hardware sets a bit in the faultmap, it implicitly - * locks the GRU context so that it cannot be unloaded. - * The gts cannot change until a TFH start/writestart command - * is issued. - */ - ctxnum = tfh->ctxnum; - gts = gru->gs_gts[ctxnum]; - - /* Spurious interrupts can cause this. Ignore. */ - if (!gts) { - STAT(intr_spurious); - continue; - } - - /* Address translation may sleep, so retry the fault in user context. */ - gts->ustats.fmm_tlbmiss++; - tfh_user_polling_mode(tfh); - } - return IRQ_HANDLED; -} - -irqreturn_t gru0_intr(int irq, void *dev_id) -{ - return gru_intr(0, uv_numa_blade_id()); -} - -irqreturn_t gru1_intr(int irq, void *dev_id) -{ - return gru_intr(1, uv_numa_blade_id()); -} - -irqreturn_t gru_intr_mblade(int irq, void *dev_id) -{ - int blade; - - for_each_possible_blade(blade) { - if (uv_blade_nr_possible_cpus(blade)) - continue; - gru_intr(0, blade); - gru_intr(1, blade); - } - return IRQ_HANDLED; -} - - -static int gru_user_dropin(struct gru_thread_state *gts, - struct gru_tlb_fault_handle *tfh, - void *cb) -{ - struct gru_mm_struct *gms = gts->ts_gms; - int ret; - - gts->ustats.upm_tlbmiss++; - while (1) { - wait_event(gms->ms_wait_queue, - atomic_read(&gms->ms_range_active) == 0); - prefetchw(tfh); /* Helps on hdw, required for emulator */ - ret = gru_try_dropin(gts->ts_gru, gts, tfh, cb); - if (ret <= 0) - return ret; - STAT(call_os_wait_queue); - } -} - -/* - * This interface is called as a result of a user detecting a "call OS" bit - * in a user CB. Normally means that a TLB fault has occurred. - * cb - user virtual address of the CB - */ -int gru_handle_user_call_os(unsigned long cb) -{ - struct gru_tlb_fault_handle *tfh; - struct gru_thread_state *gts; - void *cbk; - int ucbnum, cbrnum, ret = -EINVAL; - - STAT(call_os); - - /* sanity check the cb pointer */ - ucbnum = get_cb_number((void *)cb); - if ((cb & (GRU_HANDLE_STRIDE - 1)) || ucbnum >= GRU_NUM_CB) - return -EINVAL; - -again: - gts = gru_find_lock_gts(cb); - if (!gts) - return -EINVAL; - gru_dbg(grudev, "address 0x%lx, gid %d, gts 0x%p\n", cb, gts->ts_gru ? gts->ts_gru->gs_gid : -1, gts); - - if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE) - goto exit; - - if (gru_check_context_placement(gts)) { - gru_unlock_gts(gts); - gru_unload_context(gts, 1); - goto again; - } - - /* - * CCH may contain stale data if ts_force_cch_reload is set. - */ - if (gts->ts_gru && gts->ts_force_cch_reload) { - gts->ts_force_cch_reload = 0; - gru_update_cch(gts); - } - - ret = -EAGAIN; - cbrnum = thread_cbr_number(gts, ucbnum); - if (gts->ts_gru) { - tfh = get_tfh_by_index(gts->ts_gru, cbrnum); - cbk = get_gseg_base_address_cb(gts->ts_gru->gs_gru_base_vaddr, - gts->ts_ctxnum, ucbnum); - ret = gru_user_dropin(gts, tfh, cbk); - } -exit: - gru_unlock_gts(gts); - return ret; -} - -/* - * Fetch the exception detail information for a CB that terminated with - * an exception. - */ -int gru_get_exception_detail(unsigned long arg) -{ - struct control_block_extended_exc_detail excdet; - struct gru_control_block_extended *cbe; - struct gru_thread_state *gts; - int ucbnum, cbrnum, ret; - - STAT(user_exception); - if (copy_from_user(&excdet, (void __user *)arg, sizeof(excdet))) - return -EFAULT; - - gts = gru_find_lock_gts(excdet.cb); - if (!gts) - return -EINVAL; - - gru_dbg(grudev, "address 0x%lx, gid %d, gts 0x%p\n", excdet.cb, gts->ts_gru ? gts->ts_gru->gs_gid : -1, gts); - ucbnum = get_cb_number((void *)excdet.cb); - if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE) { - ret = -EINVAL; - } else if (gts->ts_gru) { - cbrnum = thread_cbr_number(gts, ucbnum); - cbe = get_cbe_by_index(gts->ts_gru, cbrnum); - gru_flush_cache(cbe); /* CBE not coherent */ - sync_core(); /* make sure we are have current data */ - excdet.opc = cbe->opccpy; - excdet.exopc = cbe->exopccpy; - excdet.ecause = cbe->ecause; - excdet.exceptdet0 = cbe->idef1upd; - excdet.exceptdet1 = cbe->idef3upd; - excdet.cbrstate = cbe->cbrstate; - excdet.cbrexecstatus = cbe->cbrexecstatus; - gru_flush_cache_cbe(cbe); - ret = 0; - } else { - ret = -EAGAIN; - } - gru_unlock_gts(gts); - - gru_dbg(grudev, - "cb 0x%lx, op %d, exopc %d, cbrstate %d, cbrexecstatus 0x%x, ecause 0x%x, " - "exdet0 0x%lx, exdet1 0x%x\n", - excdet.cb, excdet.opc, excdet.exopc, excdet.cbrstate, excdet.cbrexecstatus, - excdet.ecause, excdet.exceptdet0, excdet.exceptdet1); - if (!ret && copy_to_user((void __user *)arg, &excdet, sizeof(excdet))) - ret = -EFAULT; - return ret; -} - -/* - * User request to unload a context. Content is saved for possible reload. - */ -static int gru_unload_all_contexts(void) -{ - struct gru_thread_state *gts; - struct gru_state *gru; - int gid, ctxnum; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - foreach_gid(gid) { - gru = GID_TO_GRU(gid); - spin_lock(&gru->gs_lock); - for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) { - gts = gru->gs_gts[ctxnum]; - if (gts && mutex_trylock(>s->ts_ctxlock)) { - spin_unlock(&gru->gs_lock); - gru_unload_context(gts, 1); - mutex_unlock(>s->ts_ctxlock); - spin_lock(&gru->gs_lock); - } - } - spin_unlock(&gru->gs_lock); - } - return 0; -} - -int gru_user_unload_context(unsigned long arg) -{ - struct gru_thread_state *gts; - struct gru_unload_context_req req; - - STAT(user_unload_context); - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - - gru_dbg(grudev, "gseg 0x%lx\n", req.gseg); - - if (!req.gseg) - return gru_unload_all_contexts(); - - gts = gru_find_lock_gts(req.gseg); - if (!gts) - return -EINVAL; - - if (gts->ts_gru) - gru_unload_context(gts, 1); - gru_unlock_gts(gts); - - return 0; -} - -/* - * User request to flush a range of virtual addresses from the GRU TLB - * (Mainly for testing). - */ -int gru_user_flush_tlb(unsigned long arg) -{ - struct gru_thread_state *gts; - struct gru_flush_tlb_req req; - struct gru_mm_struct *gms; - - STAT(user_flush_tlb); - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - - gru_dbg(grudev, "gseg 0x%lx, vaddr 0x%lx, len 0x%lx\n", req.gseg, - req.vaddr, req.len); - - gts = gru_find_lock_gts(req.gseg); - if (!gts) - return -EINVAL; - - gms = gts->ts_gms; - gru_unlock_gts(gts); - gru_flush_tlb_range(gms, req.vaddr, req.len); - - return 0; -} - -/* - * Fetch GSEG statisticss - */ -long gru_get_gseg_statistics(unsigned long arg) -{ - struct gru_thread_state *gts; - struct gru_get_gseg_statistics_req req; - - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - - /* - * The library creates arrays of contexts for threaded programs. - * If no gts exists in the array, the context has never been used & all - * statistics are implicitly 0. - */ - gts = gru_find_lock_gts(req.gseg); - if (gts) { - memcpy(&req.stats, >s->ustats, sizeof(gts->ustats)); - gru_unlock_gts(gts); - } else { - memset(&req.stats, 0, sizeof(gts->ustats)); - } - - if (copy_to_user((void __user *)arg, &req, sizeof(req))) - return -EFAULT; - - return 0; -} - -/* - * Register the current task as the user of the GSEG slice. - * Needed for TLB fault interrupt targeting. - */ -int gru_set_context_option(unsigned long arg) -{ - struct gru_thread_state *gts; - struct gru_set_context_option_req req; - int ret = 0; - - STAT(set_context_option); - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - gru_dbg(grudev, "op %d, gseg 0x%lx, value1 0x%lx\n", req.op, req.gseg, req.val1); - - gts = gru_find_lock_gts(req.gseg); - if (!gts) { - gts = gru_alloc_locked_gts(req.gseg); - if (IS_ERR(gts)) - return PTR_ERR(gts); - } - - switch (req.op) { - case sco_blade_chiplet: - /* Select blade/chiplet for GRU context */ - if (req.val0 < -1 || req.val0 >= GRU_CHIPLETS_PER_HUB || - req.val1 < -1 || req.val1 >= GRU_MAX_BLADES || - (req.val1 >= 0 && !gru_base[req.val1])) { - ret = -EINVAL; - } else { - gts->ts_user_blade_id = req.val1; - gts->ts_user_chiplet_id = req.val0; - if (gru_check_context_placement(gts)) { - gru_unlock_gts(gts); - gru_unload_context(gts, 1); - return ret; - } - } - break; - case sco_gseg_owner: - /* Register the current task as the GSEG owner */ - gts->ts_tgid_owner = current->tgid; - break; - case sco_cch_req_slice: - /* Set the CCH slice option */ - gts->ts_cch_req_slice = req.val1 & 3; - break; - default: - ret = -EINVAL; - } - gru_unlock_gts(gts); - - return ret; -} diff --git a/drivers/misc/sgi-gru/grufile.c b/drivers/misc/sgi-gru/grufile.c deleted file mode 100644 index e755690c9805..000000000000 --- a/drivers/misc/sgi-gru/grufile.c +++ /dev/null @@ -1,540 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * FILE OPERATIONS & DRIVER INITIALIZATION - * - * This file supports the user system call for file open, close, mmap, etc. - * This also incudes the driver initialization code. - * - * (C) Copyright 2020 Hewlett Packard Enterprise Development LP - * Copyright (c) 2008-2014 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_X86_64 -#include -#endif -#include -#include "gru.h" -#include "grulib.h" -#include "grutables.h" - -#include -#include - -struct gru_blade_state *gru_base[GRU_MAX_BLADES] __read_mostly; -unsigned long gru_start_paddr __read_mostly; -void *gru_start_vaddr __read_mostly; -unsigned long gru_end_paddr __read_mostly; -unsigned int gru_max_gids __read_mostly; -struct gru_stats_s gru_stats; - -/* Guaranteed user available resources on each node */ -static int max_user_cbrs, max_user_dsr_bytes; - -static struct miscdevice gru_miscdev; - -static int gru_supported(void) -{ - return is_uv_system() && - (uv_hub_info->hub_revision < UV3_HUB_REVISION_BASE); -} - -/* - * gru_vma_close - * - * Called when unmapping a device mapping. Frees all gru resources - * and tables belonging to the vma. - */ -static void gru_vma_close(struct vm_area_struct *vma) -{ - struct gru_vma_data *vdata; - struct gru_thread_state *gts; - struct list_head *entry, *next; - - if (!vma->vm_private_data) - return; - - vdata = vma->vm_private_data; - vma->vm_private_data = NULL; - gru_dbg(grudev, "vma %p, file %p, vdata %p\n", vma, vma->vm_file, - vdata); - list_for_each_safe(entry, next, &vdata->vd_head) { - gts = - list_entry(entry, struct gru_thread_state, ts_next); - list_del(>s->ts_next); - mutex_lock(>s->ts_ctxlock); - if (gts->ts_gru) - gru_unload_context(gts, 0); - mutex_unlock(>s->ts_ctxlock); - gts_drop(gts); - } - kfree(vdata); - STAT(vdata_free); -} - -/* - * gru_file_mmap - * - * Called when mmapping the device. Initializes the vma with a fault handler - * and private data structure necessary to allocate, track, and free the - * underlying pages. - */ -static int gru_file_mmap(struct file *file, struct vm_area_struct *vma) -{ - if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) != (VM_SHARED | VM_WRITE)) - return -EPERM; - - if (vma->vm_start & (GRU_GSEG_PAGESIZE - 1) || - vma->vm_end & (GRU_GSEG_PAGESIZE - 1)) - return -EINVAL; - - vm_flags_set(vma, VM_IO | VM_PFNMAP | VM_LOCKED | - VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP); - vma->vm_page_prot = PAGE_SHARED; - vma->vm_ops = &gru_vm_ops; - - vma->vm_private_data = gru_alloc_vma_data(vma, 0); - if (!vma->vm_private_data) - return -ENOMEM; - - gru_dbg(grudev, "file %p, vaddr 0x%lx, vma %p, vdata %p\n", - file, vma->vm_start, vma, vma->vm_private_data); - return 0; -} - -/* - * Create a new GRU context - */ -static int gru_create_new_context(unsigned long arg) -{ - struct gru_create_context_req req; - struct vm_area_struct *vma; - struct gru_vma_data *vdata; - int ret = -EINVAL; - - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - - if (req.data_segment_bytes > max_user_dsr_bytes) - return -EINVAL; - if (req.control_blocks > max_user_cbrs || !req.maximum_thread_count) - return -EINVAL; - - if (!(req.options & GRU_OPT_MISS_MASK)) - req.options |= GRU_OPT_MISS_FMM_INTR; - - mmap_write_lock(current->mm); - vma = gru_find_vma(req.gseg); - if (vma) { - vdata = vma->vm_private_data; - vdata->vd_user_options = req.options; - vdata->vd_dsr_au_count = - GRU_DS_BYTES_TO_AU(req.data_segment_bytes); - vdata->vd_cbr_au_count = GRU_CB_COUNT_TO_AU(req.control_blocks); - vdata->vd_tlb_preload_count = req.tlb_preload_count; - ret = 0; - } - mmap_write_unlock(current->mm); - - return ret; -} - -/* - * Get GRU configuration info (temp - for emulator testing) - */ -static long gru_get_config_info(unsigned long arg) -{ - struct gru_config_info info; - int nodesperblade; - - if (num_online_nodes() > 1 && - (uv_node_to_blade_id(1) == uv_node_to_blade_id(0))) - nodesperblade = 2; - else - nodesperblade = 1; - memset(&info, 0, sizeof(info)); - info.cpus = num_online_cpus(); - info.nodes = num_online_nodes(); - info.blades = info.nodes / nodesperblade; - info.chiplets = GRU_CHIPLETS_PER_BLADE * info.blades; - - if (copy_to_user((void __user *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; -} - -/* - * gru_file_unlocked_ioctl - * - * Called to update file attributes via IOCTL calls. - */ -static long gru_file_unlocked_ioctl(struct file *file, unsigned int req, - unsigned long arg) -{ - int err = -EBADRQC; - - gru_dbg(grudev, "file %p, req 0x%x, 0x%lx\n", file, req, arg); - - switch (req) { - case GRU_CREATE_CONTEXT: - err = gru_create_new_context(arg); - break; - case GRU_SET_CONTEXT_OPTION: - err = gru_set_context_option(arg); - break; - case GRU_USER_GET_EXCEPTION_DETAIL: - err = gru_get_exception_detail(arg); - break; - case GRU_USER_UNLOAD_CONTEXT: - err = gru_user_unload_context(arg); - break; - case GRU_USER_FLUSH_TLB: - err = gru_user_flush_tlb(arg); - break; - case GRU_USER_CALL_OS: - err = gru_handle_user_call_os(arg); - break; - case GRU_GET_GSEG_STATISTICS: - err = gru_get_gseg_statistics(arg); - break; - case GRU_KTEST: - err = gru_ktest(arg); - break; - case GRU_GET_CONFIG_INFO: - err = gru_get_config_info(arg); - break; - case GRU_DUMP_CHIPLET_STATE: - err = gru_dump_chiplet_request(arg); - break; - } - return err; -} - -/* - * Called at init time to build tables for all GRUs that are present in the - * system. - */ -static void gru_init_chiplet(struct gru_state *gru, unsigned long paddr, - void *vaddr, int blade_id, int chiplet_id) -{ - spin_lock_init(&gru->gs_lock); - spin_lock_init(&gru->gs_asid_lock); - gru->gs_gru_base_paddr = paddr; - gru->gs_gru_base_vaddr = vaddr; - gru->gs_gid = blade_id * GRU_CHIPLETS_PER_BLADE + chiplet_id; - gru->gs_blade = gru_base[blade_id]; - gru->gs_blade_id = blade_id; - gru->gs_chiplet_id = chiplet_id; - gru->gs_cbr_map = (GRU_CBR_AU == 64) ? ~0 : (1UL << GRU_CBR_AU) - 1; - gru->gs_dsr_map = (1UL << GRU_DSR_AU) - 1; - gru->gs_asid_limit = MAX_ASID; - gru_tgh_flush_init(gru); - if (gru->gs_gid >= gru_max_gids) - gru_max_gids = gru->gs_gid + 1; - gru_dbg(grudev, "bid %d, gid %d, vaddr %p (0x%lx)\n", - blade_id, gru->gs_gid, gru->gs_gru_base_vaddr, - gru->gs_gru_base_paddr); -} - -static int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr) -{ - int pnode, nid, bid, chip; - int cbrs, dsrbytes, n; - int order = get_order(sizeof(struct gru_blade_state)); - struct page *page; - struct gru_state *gru; - unsigned long paddr; - void *vaddr; - - max_user_cbrs = GRU_NUM_CB; - max_user_dsr_bytes = GRU_NUM_DSR_BYTES; - for_each_possible_blade(bid) { - pnode = uv_blade_to_pnode(bid); - nid = uv_blade_to_memory_nid(bid);/* -1 if no memory on blade */ - page = alloc_pages_node(nid, GFP_KERNEL, order); - if (!page) - goto fail; - gru_base[bid] = page_address(page); - memset(gru_base[bid], 0, sizeof(struct gru_blade_state)); - gru_base[bid]->bs_lru_gru = &gru_base[bid]->bs_grus[0]; - spin_lock_init(&gru_base[bid]->bs_lock); - init_rwsem(&gru_base[bid]->bs_kgts_sema); - - dsrbytes = 0; - cbrs = 0; - for (gru = gru_base[bid]->bs_grus, chip = 0; - chip < GRU_CHIPLETS_PER_BLADE; - chip++, gru++) { - paddr = gru_chiplet_paddr(gru_base_paddr, pnode, chip); - vaddr = gru_chiplet_vaddr(gru_base_vaddr, pnode, chip); - gru_init_chiplet(gru, paddr, vaddr, bid, chip); - n = hweight64(gru->gs_cbr_map) * GRU_CBR_AU_SIZE; - cbrs = max(cbrs, n); - n = hweight64(gru->gs_dsr_map) * GRU_DSR_AU_BYTES; - dsrbytes = max(dsrbytes, n); - } - max_user_cbrs = min(max_user_cbrs, cbrs); - max_user_dsr_bytes = min(max_user_dsr_bytes, dsrbytes); - } - - return 0; - -fail: - for (bid--; bid >= 0; bid--) - free_pages((unsigned long)gru_base[bid], order); - return -ENOMEM; -} - -static void gru_free_tables(void) -{ - int bid; - int order = get_order(sizeof(struct gru_state) * - GRU_CHIPLETS_PER_BLADE); - - for (bid = 0; bid < GRU_MAX_BLADES; bid++) - free_pages((unsigned long)gru_base[bid], order); -} - -static unsigned long gru_chiplet_cpu_to_mmr(int chiplet, int cpu, int *corep) -{ - unsigned long mmr = 0; - int core; - - /* - * We target the cores of a blade and not the hyperthreads themselves. - * There is a max of 8 cores per socket and 2 sockets per blade, - * making for a max total of 16 cores (i.e., 16 CPUs without - * hyperthreading and 32 CPUs with hyperthreading). - */ - core = uv_cpu_core_number(cpu) + UV_MAX_INT_CORES * uv_cpu_socket_number(cpu); - if (core >= GRU_NUM_TFM || uv_cpu_ht_number(cpu)) - return 0; - - if (chiplet == 0) { - mmr = UVH_GR0_TLB_INT0_CONFIG + - core * (UVH_GR0_TLB_INT1_CONFIG - UVH_GR0_TLB_INT0_CONFIG); - } else if (chiplet == 1) { - mmr = UVH_GR1_TLB_INT0_CONFIG + - core * (UVH_GR1_TLB_INT1_CONFIG - UVH_GR1_TLB_INT0_CONFIG); - } else { - BUG(); - } - - *corep = core; - return mmr; -} - -static int gru_chiplet_setup_tlb_irq(int chiplet, char *irq_name, - irq_handler_t irq_handler, int cpu, int blade) -{ - unsigned long mmr; - int irq, core; - int ret; - - mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core); - if (mmr == 0) - return 0; - - irq = uv_setup_irq(irq_name, cpu, blade, mmr, UV_AFFINITY_CPU); - if (irq < 0) { - printk(KERN_ERR "%s: uv_setup_irq failed, errno=%d\n", - GRU_DRIVER_ID_STR, -irq); - return irq; - } - - ret = request_irq(irq, irq_handler, 0, irq_name, NULL); - if (ret) { - uv_teardown_irq(irq); - printk(KERN_ERR "%s: request_irq failed, errno=%d\n", - GRU_DRIVER_ID_STR, -ret); - return ret; - } - gru_base[blade]->bs_grus[chiplet].gs_irq[core] = irq; - return 0; -} - -static void gru_chiplet_teardown_tlb_irq(int chiplet, int cpu, int blade) -{ - int irq, core; - unsigned long mmr; - - mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core); - if (mmr) { - irq = gru_base[blade]->bs_grus[chiplet].gs_irq[core]; - if (irq) { - free_irq(irq, NULL); - uv_teardown_irq(irq); - } - } -} - -static void gru_teardown_tlb_irqs(void) -{ - int blade; - int cpu; - - for_each_online_cpu(cpu) { - blade = uv_cpu_to_blade_id(cpu); - gru_chiplet_teardown_tlb_irq(0, cpu, blade); - gru_chiplet_teardown_tlb_irq(1, cpu, blade); - } - for_each_possible_blade(blade) { - if (uv_blade_nr_possible_cpus(blade)) - continue; - gru_chiplet_teardown_tlb_irq(0, 0, blade); - gru_chiplet_teardown_tlb_irq(1, 0, blade); - } -} - -static int gru_setup_tlb_irqs(void) -{ - int blade; - int cpu; - int ret; - - for_each_online_cpu(cpu) { - blade = uv_cpu_to_blade_id(cpu); - ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru0_intr, cpu, blade); - if (ret != 0) - goto exit1; - - ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru1_intr, cpu, blade); - if (ret != 0) - goto exit1; - } - for_each_possible_blade(blade) { - if (uv_blade_nr_possible_cpus(blade)) - continue; - ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru_intr_mblade, 0, blade); - if (ret != 0) - goto exit1; - - ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru_intr_mblade, 0, blade); - if (ret != 0) - goto exit1; - } - - return 0; - -exit1: - gru_teardown_tlb_irqs(); - return ret; -} - -/* - * gru_init - * - * Called at boot or module load time to initialize the GRUs. - */ -static int __init gru_init(void) -{ - int ret; - - if (!gru_supported()) - return 0; - - gru_start_paddr = uv_read_local_mmr(UVH_RH_GAM_GRU_OVERLAY_CONFIG) & - 0x7fffffffffffUL; - gru_start_vaddr = __va(gru_start_paddr); - gru_end_paddr = gru_start_paddr + GRU_MAX_BLADES * GRU_SIZE; - printk(KERN_INFO "GRU space: 0x%lx - 0x%lx\n", - gru_start_paddr, gru_end_paddr); - ret = misc_register(&gru_miscdev); - if (ret) { - printk(KERN_ERR "%s: misc_register failed\n", - GRU_DRIVER_ID_STR); - goto exit0; - } - - ret = gru_proc_init(); - if (ret) { - printk(KERN_ERR "%s: proc init failed\n", GRU_DRIVER_ID_STR); - goto exit1; - } - - ret = gru_init_tables(gru_start_paddr, gru_start_vaddr); - if (ret) { - printk(KERN_ERR "%s: init tables failed\n", GRU_DRIVER_ID_STR); - goto exit2; - } - - ret = gru_setup_tlb_irqs(); - if (ret != 0) - goto exit3; - - gru_kservices_init(); - - printk(KERN_INFO "%s: v%s\n", GRU_DRIVER_ID_STR, - GRU_DRIVER_VERSION_STR); - return 0; - -exit3: - gru_free_tables(); -exit2: - gru_proc_exit(); -exit1: - misc_deregister(&gru_miscdev); -exit0: - return ret; - -} - -static void __exit gru_exit(void) -{ - if (!gru_supported()) - return; - - gru_teardown_tlb_irqs(); - gru_kservices_exit(); - gru_free_tables(); - misc_deregister(&gru_miscdev); - gru_proc_exit(); - mmu_notifier_synchronize(); -} - -static const struct file_operations gru_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = gru_file_unlocked_ioctl, - .mmap = gru_file_mmap, - .llseek = noop_llseek, -}; - -static struct miscdevice gru_miscdev = { - .minor = MISC_DYNAMIC_MINOR, - .name = "gru", - .fops = &gru_fops, -}; - -const struct vm_operations_struct gru_vm_ops = { - .close = gru_vma_close, - .fault = gru_fault, -}; - -#ifndef MODULE -fs_initcall(gru_init); -#else -module_init(gru_init); -#endif -module_exit(gru_exit); - -module_param(gru_options, ulong, 0644); -MODULE_PARM_DESC(gru_options, "Various debug options"); - -MODULE_AUTHOR("Silicon Graphics, Inc."); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION(GRU_DRIVER_ID_STR GRU_DRIVER_VERSION_STR); -MODULE_VERSION(GRU_DRIVER_VERSION_STR); - diff --git a/drivers/misc/sgi-gru/gruhandles.c b/drivers/misc/sgi-gru/gruhandles.c deleted file mode 100644 index 695316a83b01..000000000000 --- a/drivers/misc/sgi-gru/gruhandles.c +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * GRU KERNEL MCS INSTRUCTIONS - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include -#include "gru.h" -#include "grulib.h" -#include "grutables.h" - -/* 10 sec */ -#include -#include -#define GRU_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000) -#define CLKS2NSEC(c) ((c) * 1000000 / tsc_khz) - -/* Extract the status field from a kernel handle */ -#define GET_MSEG_HANDLE_STATUS(h) (((*(unsigned long *)(h)) >> 16) & 3) - -struct mcs_op_statistic mcs_op_statistics[mcsop_last]; - -static void update_mcs_stats(enum mcs_op op, unsigned long clks) -{ - unsigned long nsec; - - nsec = CLKS2NSEC(clks); - atomic_long_inc(&mcs_op_statistics[op].count); - atomic_long_add(nsec, &mcs_op_statistics[op].total); - if (mcs_op_statistics[op].max < nsec) - mcs_op_statistics[op].max = nsec; -} - -static void start_instruction(void *h) -{ - unsigned long *w0 = h; - - wmb(); /* setting CMD/STATUS bits must be last */ - *w0 = *w0 | 0x20001; - gru_flush_cache(h); -} - -static void report_instruction_timeout(void *h) -{ - unsigned long goff = GSEGPOFF((unsigned long)h); - char *id = "???"; - - if (TYPE_IS(CCH, goff)) - id = "CCH"; - else if (TYPE_IS(TGH, goff)) - id = "TGH"; - else if (TYPE_IS(TFH, goff)) - id = "TFH"; - - panic(KERN_ALERT "GRU %p (%s) is malfunctioning\n", h, id); -} - -static int wait_instruction_complete(void *h, enum mcs_op opc) -{ - int status; - unsigned long start_time = get_cycles(); - - while (1) { - cpu_relax(); - status = GET_MSEG_HANDLE_STATUS(h); - if (status != CCHSTATUS_ACTIVE) - break; - if (GRU_OPERATION_TIMEOUT < (get_cycles() - start_time)) { - report_instruction_timeout(h); - start_time = get_cycles(); - } - } - if (gru_options & OPT_STATS) - update_mcs_stats(opc, get_cycles() - start_time); - return status; -} - -int cch_allocate(struct gru_context_configuration_handle *cch) -{ - int ret; - - cch->opc = CCHOP_ALLOCATE; - start_instruction(cch); - ret = wait_instruction_complete(cch, cchop_allocate); - - /* - * Stop speculation into the GSEG being mapped by the previous ALLOCATE. - * The GSEG memory does not exist until the ALLOCATE completes. - */ - sync_core(); - return ret; -} - -int cch_start(struct gru_context_configuration_handle *cch) -{ - cch->opc = CCHOP_START; - start_instruction(cch); - return wait_instruction_complete(cch, cchop_start); -} - -int cch_interrupt(struct gru_context_configuration_handle *cch) -{ - cch->opc = CCHOP_INTERRUPT; - start_instruction(cch); - return wait_instruction_complete(cch, cchop_interrupt); -} - -int cch_deallocate(struct gru_context_configuration_handle *cch) -{ - int ret; - - cch->opc = CCHOP_DEALLOCATE; - start_instruction(cch); - ret = wait_instruction_complete(cch, cchop_deallocate); - - /* - * Stop speculation into the GSEG being unmapped by the previous - * DEALLOCATE. - */ - sync_core(); - return ret; -} - -int cch_interrupt_sync(struct gru_context_configuration_handle - *cch) -{ - cch->opc = CCHOP_INTERRUPT_SYNC; - start_instruction(cch); - return wait_instruction_complete(cch, cchop_interrupt_sync); -} - -int tgh_invalidate(struct gru_tlb_global_handle *tgh, - unsigned long vaddr, unsigned long vaddrmask, - int asid, int pagesize, int global, int n, - unsigned short ctxbitmap) -{ - tgh->vaddr = vaddr; - tgh->asid = asid; - tgh->pagesize = pagesize; - tgh->n = n; - tgh->global = global; - tgh->vaddrmask = vaddrmask; - tgh->ctxbitmap = ctxbitmap; - tgh->opc = TGHOP_TLBINV; - start_instruction(tgh); - return wait_instruction_complete(tgh, tghop_invalidate); -} - -int tfh_write_only(struct gru_tlb_fault_handle *tfh, - unsigned long paddr, int gaa, - unsigned long vaddr, int asid, int dirty, - int pagesize) -{ - tfh->fillasid = asid; - tfh->fillvaddr = vaddr; - tfh->pfn = paddr >> GRU_PADDR_SHIFT; - tfh->gaa = gaa; - tfh->dirty = dirty; - tfh->pagesize = pagesize; - tfh->opc = TFHOP_WRITE_ONLY; - start_instruction(tfh); - return wait_instruction_complete(tfh, tfhop_write_only); -} - -void tfh_write_restart(struct gru_tlb_fault_handle *tfh, - unsigned long paddr, int gaa, - unsigned long vaddr, int asid, int dirty, - int pagesize) -{ - tfh->fillasid = asid; - tfh->fillvaddr = vaddr; - tfh->pfn = paddr >> GRU_PADDR_SHIFT; - tfh->gaa = gaa; - tfh->dirty = dirty; - tfh->pagesize = pagesize; - tfh->opc = TFHOP_WRITE_RESTART; - start_instruction(tfh); -} - -void tfh_user_polling_mode(struct gru_tlb_fault_handle *tfh) -{ - tfh->opc = TFHOP_USER_POLLING_MODE; - start_instruction(tfh); -} - -void tfh_exception(struct gru_tlb_fault_handle *tfh) -{ - tfh->opc = TFHOP_EXCEPTION; - start_instruction(tfh); -} - diff --git a/drivers/misc/sgi-gru/gruhandles.h b/drivers/misc/sgi-gru/gruhandles.h deleted file mode 100644 index 5a498bf8d003..000000000000 --- a/drivers/misc/sgi-gru/gruhandles.h +++ /dev/null @@ -1,517 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * SN Platform GRU Driver - * - * GRU HANDLE DEFINITION - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#ifndef __GRUHANDLES_H__ -#define __GRUHANDLES_H__ -#include "gru_instructions.h" - -/* - * Manifest constants for GRU Memory Map - */ -#define GRU_GSEG0_BASE 0 -#define GRU_MCS_BASE (64 * 1024 * 1024) -#define GRU_SIZE (128UL * 1024 * 1024) - -/* Handle & resource counts */ -#define GRU_NUM_CB 128 -#define GRU_NUM_DSR_BYTES (32 * 1024) -#define GRU_NUM_TFM 16 -#define GRU_NUM_TGH 24 -#define GRU_NUM_CBE 128 -#define GRU_NUM_TFH 128 -#define GRU_NUM_CCH 16 - -/* Maximum resource counts that can be reserved by user programs */ -#define GRU_NUM_USER_CBR GRU_NUM_CBE -#define GRU_NUM_USER_DSR_BYTES GRU_NUM_DSR_BYTES - -/* Bytes per handle & handle stride. Code assumes all cb, tfh, cbe handles - * are the same */ -#define GRU_HANDLE_BYTES 64 -#define GRU_HANDLE_STRIDE 256 - -/* Base addresses of handles */ -#define GRU_TFM_BASE (GRU_MCS_BASE + 0x00000) -#define GRU_TGH_BASE (GRU_MCS_BASE + 0x08000) -#define GRU_CBE_BASE (GRU_MCS_BASE + 0x10000) -#define GRU_TFH_BASE (GRU_MCS_BASE + 0x18000) -#define GRU_CCH_BASE (GRU_MCS_BASE + 0x20000) - -/* User gseg constants */ -#define GRU_GSEG_STRIDE (4 * 1024 * 1024) -#define GSEG_BASE(a) ((a) & ~(GRU_GSEG_PAGESIZE - 1)) - -/* Data segment constants */ -#define GRU_DSR_AU_BYTES 1024 -#define GRU_DSR_CL (GRU_NUM_DSR_BYTES / GRU_CACHE_LINE_BYTES) -#define GRU_DSR_AU_CL (GRU_DSR_AU_BYTES / GRU_CACHE_LINE_BYTES) -#define GRU_DSR_AU (GRU_NUM_DSR_BYTES / GRU_DSR_AU_BYTES) - -/* Control block constants */ -#define GRU_CBR_AU_SIZE 2 -#define GRU_CBR_AU (GRU_NUM_CBE / GRU_CBR_AU_SIZE) - -/* Convert resource counts to the number of AU */ -#define GRU_DS_BYTES_TO_AU(n) DIV_ROUND_UP(n, GRU_DSR_AU_BYTES) -#define GRU_CB_COUNT_TO_AU(n) DIV_ROUND_UP(n, GRU_CBR_AU_SIZE) - -/* UV limits */ -#define GRU_CHIPLETS_PER_HUB 2 -#define GRU_HUBS_PER_BLADE 1 -#define GRU_CHIPLETS_PER_BLADE (GRU_HUBS_PER_BLADE * GRU_CHIPLETS_PER_HUB) - -/* User GRU Gseg offsets */ -#define GRU_CB_BASE 0 -#define GRU_CB_LIMIT (GRU_CB_BASE + GRU_HANDLE_STRIDE * GRU_NUM_CBE) -#define GRU_DS_BASE 0x20000 -#define GRU_DS_LIMIT (GRU_DS_BASE + GRU_NUM_DSR_BYTES) - -/* Convert a GRU physical address to the chiplet offset */ -#define GSEGPOFF(h) ((h) & (GRU_SIZE - 1)) - -/* Convert an arbitrary handle address to the beginning of the GRU segment */ -#define GRUBASE(h) ((void *)((unsigned long)(h) & ~(GRU_SIZE - 1))) - -/* Test a valid handle address to determine the type */ -#define TYPE_IS(hn, h) ((h) >= GRU_##hn##_BASE && (h) < \ - GRU_##hn##_BASE + GRU_NUM_##hn * GRU_HANDLE_STRIDE && \ - (((h) & (GRU_HANDLE_STRIDE - 1)) == 0)) - - -/* General addressing macros. */ -static inline void *get_gseg_base_address(void *base, int ctxnum) -{ - return (void *)(base + GRU_GSEG0_BASE + GRU_GSEG_STRIDE * ctxnum); -} - -static inline void *get_gseg_base_address_cb(void *base, int ctxnum, int line) -{ - return (void *)(get_gseg_base_address(base, ctxnum) + - GRU_CB_BASE + GRU_HANDLE_STRIDE * line); -} - -static inline void *get_gseg_base_address_ds(void *base, int ctxnum, int line) -{ - return (void *)(get_gseg_base_address(base, ctxnum) + GRU_DS_BASE + - GRU_CACHE_LINE_BYTES * line); -} - -static inline struct gru_tlb_fault_map *get_tfm(void *base, int ctxnum) -{ - return (struct gru_tlb_fault_map *)(base + GRU_TFM_BASE + - ctxnum * GRU_HANDLE_STRIDE); -} - -static inline struct gru_tlb_global_handle *get_tgh(void *base, int ctxnum) -{ - return (struct gru_tlb_global_handle *)(base + GRU_TGH_BASE + - ctxnum * GRU_HANDLE_STRIDE); -} - -static inline struct gru_control_block_extended *get_cbe(void *base, int ctxnum) -{ - return (struct gru_control_block_extended *)(base + GRU_CBE_BASE + - ctxnum * GRU_HANDLE_STRIDE); -} - -static inline struct gru_tlb_fault_handle *get_tfh(void *base, int ctxnum) -{ - return (struct gru_tlb_fault_handle *)(base + GRU_TFH_BASE + - ctxnum * GRU_HANDLE_STRIDE); -} - -static inline struct gru_context_configuration_handle *get_cch(void *base, - int ctxnum) -{ - return (struct gru_context_configuration_handle *)(base + - GRU_CCH_BASE + ctxnum * GRU_HANDLE_STRIDE); -} - -static inline unsigned long get_cb_number(void *cb) -{ - return (((unsigned long)cb - GRU_CB_BASE) % GRU_GSEG_PAGESIZE) / - GRU_HANDLE_STRIDE; -} - -/* byte offset to a specific GRU chiplet. (p=pnode, c=chiplet (0 or 1)*/ -static inline unsigned long gru_chiplet_paddr(unsigned long paddr, int pnode, - int chiplet) -{ - return paddr + GRU_SIZE * (2 * pnode + chiplet); -} - -static inline void *gru_chiplet_vaddr(void *vaddr, int pnode, int chiplet) -{ - return vaddr + GRU_SIZE * (2 * pnode + chiplet); -} - -static inline struct gru_control_block_extended *gru_tfh_to_cbe( - struct gru_tlb_fault_handle *tfh) -{ - unsigned long cbe; - - cbe = (unsigned long)tfh - GRU_TFH_BASE + GRU_CBE_BASE; - return (struct gru_control_block_extended*)cbe; -} - - - - -/* - * Global TLB Fault Map - * Bitmap of outstanding TLB misses needing interrupt/polling service. - * - */ -struct gru_tlb_fault_map { - unsigned long fault_bits[BITS_TO_LONGS(GRU_NUM_CBE)]; - unsigned long fill0[2]; - unsigned long done_bits[BITS_TO_LONGS(GRU_NUM_CBE)]; - unsigned long fill1[2]; -}; - -/* - * TGH - TLB Global Handle - * Used for TLB flushing. - * - */ -struct gru_tlb_global_handle { - unsigned int cmd:1; /* DW 0 */ - unsigned int delresp:1; - unsigned int opc:1; - unsigned int fill1:5; - - unsigned int fill2:8; - - unsigned int status:2; - unsigned long fill3:2; - unsigned int state:3; - unsigned long fill4:1; - - unsigned int cause:3; - unsigned long fill5:37; - - unsigned long vaddr:64; /* DW 1 */ - - unsigned int asid:24; /* DW 2 */ - unsigned int fill6:8; - - unsigned int pagesize:5; - unsigned int fill7:11; - - unsigned int global:1; - unsigned int fill8:15; - - unsigned long vaddrmask:39; /* DW 3 */ - unsigned int fill9:9; - unsigned int n:10; - unsigned int fill10:6; - - unsigned int ctxbitmap:16; /* DW4 */ - unsigned long fill11[3]; -}; - -enum gru_tgh_cmd { - TGHCMD_START -}; - -enum gru_tgh_opc { - TGHOP_TLBNOP, - TGHOP_TLBINV -}; - -enum gru_tgh_status { - TGHSTATUS_IDLE, - TGHSTATUS_EXCEPTION, - TGHSTATUS_ACTIVE -}; - -enum gru_tgh_state { - TGHSTATE_IDLE, - TGHSTATE_PE_INVAL, - TGHSTATE_INTERRUPT_INVAL, - TGHSTATE_WAITDONE, - TGHSTATE_RESTART_CTX, -}; - -enum gru_tgh_cause { - TGHCAUSE_RR_ECC, - TGHCAUSE_TLB_ECC, - TGHCAUSE_LRU_ECC, - TGHCAUSE_PS_ECC, - TGHCAUSE_MUL_ERR, - TGHCAUSE_DATA_ERR, - TGHCAUSE_SW_FORCE -}; - - -/* - * TFH - TLB Global Handle - * Used for TLB dropins into the GRU TLB. - * - */ -struct gru_tlb_fault_handle { - unsigned int cmd:1; /* DW 0 - low 32*/ - unsigned int delresp:1; - unsigned int fill0:2; - unsigned int opc:3; - unsigned int fill1:9; - - unsigned int status:2; - unsigned int fill2:2; - unsigned int state:3; - unsigned int fill3:1; - - unsigned int cause:6; - unsigned int cb_int:1; - unsigned int fill4:1; - - unsigned int indexway:12; /* DW 0 - high 32 */ - unsigned int fill5:4; - - unsigned int ctxnum:4; - unsigned int fill6:12; - - unsigned long missvaddr:64; /* DW 1 */ - - unsigned int missasid:24; /* DW 2 */ - unsigned int fill7:8; - unsigned int fillasid:24; - unsigned int dirty:1; - unsigned int gaa:2; - unsigned long fill8:5; - - unsigned long pfn:41; /* DW 3 */ - unsigned int fill9:7; - unsigned int pagesize:5; - unsigned int fill10:11; - - unsigned long fillvaddr:64; /* DW 4 */ - - unsigned long fill11[3]; -}; - -enum gru_tfh_opc { - TFHOP_NOOP, - TFHOP_RESTART, - TFHOP_WRITE_ONLY, - TFHOP_WRITE_RESTART, - TFHOP_EXCEPTION, - TFHOP_USER_POLLING_MODE = 7, -}; - -enum tfh_status { - TFHSTATUS_IDLE, - TFHSTATUS_EXCEPTION, - TFHSTATUS_ACTIVE, -}; - -enum tfh_state { - TFHSTATE_INACTIVE, - TFHSTATE_IDLE, - TFHSTATE_MISS_UPM, - TFHSTATE_MISS_FMM, - TFHSTATE_HW_ERR, - TFHSTATE_WRITE_TLB, - TFHSTATE_RESTART_CBR, -}; - -/* TFH cause bits */ -enum tfh_cause { - TFHCAUSE_NONE, - TFHCAUSE_TLB_MISS, - TFHCAUSE_TLB_MOD, - TFHCAUSE_HW_ERROR_RR, - TFHCAUSE_HW_ERROR_MAIN_ARRAY, - TFHCAUSE_HW_ERROR_VALID, - TFHCAUSE_HW_ERROR_PAGESIZE, - TFHCAUSE_INSTRUCTION_EXCEPTION, - TFHCAUSE_UNCORRECTIBLE_ERROR, -}; - -/* GAA values */ -#define GAA_RAM 0x0 -#define GAA_NCRAM 0x2 -#define GAA_MMIO 0x1 -#define GAA_REGISTER 0x3 - -/* GRU paddr shift for pfn. (NOTE: shift is NOT by actual pagesize) */ -#define GRU_PADDR_SHIFT 12 - -/* - * Context Configuration handle - * Used to allocate resources to a GSEG context. - * - */ -struct gru_context_configuration_handle { - unsigned int cmd:1; /* DW0 */ - unsigned int delresp:1; - unsigned int opc:3; - unsigned int unmap_enable:1; - unsigned int req_slice_set_enable:1; - unsigned int req_slice:2; - unsigned int cb_int_enable:1; - unsigned int tlb_int_enable:1; - unsigned int tfm_fault_bit_enable:1; - unsigned int tlb_int_select:4; - - unsigned int status:2; - unsigned int state:2; - unsigned int reserved2:4; - - unsigned int cause:4; - unsigned int tfm_done_bit_enable:1; - unsigned int unused:3; - - unsigned int dsr_allocation_map; - - unsigned long cbr_allocation_map; /* DW1 */ - - unsigned int asid[8]; /* DW 2 - 5 */ - unsigned short sizeavail[8]; /* DW 6 - 7 */ -} __attribute__ ((packed)); - -enum gru_cch_opc { - CCHOP_START = 1, - CCHOP_ALLOCATE, - CCHOP_INTERRUPT, - CCHOP_DEALLOCATE, - CCHOP_INTERRUPT_SYNC, -}; - -enum gru_cch_status { - CCHSTATUS_IDLE, - CCHSTATUS_EXCEPTION, - CCHSTATUS_ACTIVE, -}; - -enum gru_cch_state { - CCHSTATE_INACTIVE, - CCHSTATE_MAPPED, - CCHSTATE_ACTIVE, - CCHSTATE_INTERRUPTED, -}; - -/* CCH Exception cause */ -enum gru_cch_cause { - CCHCAUSE_REGION_REGISTER_WRITE_ERROR = 1, - CCHCAUSE_ILLEGAL_OPCODE = 2, - CCHCAUSE_INVALID_START_REQUEST = 3, - CCHCAUSE_INVALID_ALLOCATION_REQUEST = 4, - CCHCAUSE_INVALID_DEALLOCATION_REQUEST = 5, - CCHCAUSE_INVALID_INTERRUPT_REQUEST = 6, - CCHCAUSE_CCH_BUSY = 7, - CCHCAUSE_NO_CBRS_TO_ALLOCATE = 8, - CCHCAUSE_BAD_TFM_CONFIG = 9, - CCHCAUSE_CBR_RESOURCES_OVERSUBSCRIPED = 10, - CCHCAUSE_DSR_RESOURCES_OVERSUBSCRIPED = 11, - CCHCAUSE_CBR_DEALLOCATION_ERROR = 12, -}; -/* - * CBE - Control Block Extended - * Maintains internal GRU state for active CBs. - * - */ -struct gru_control_block_extended { - unsigned int reserved0:1; /* DW 0 - low */ - unsigned int imacpy:3; - unsigned int reserved1:4; - unsigned int xtypecpy:3; - unsigned int iaa0cpy:2; - unsigned int iaa1cpy:2; - unsigned int reserved2:1; - unsigned int opccpy:8; - unsigned int exopccpy:8; - - unsigned int idef2cpy:22; /* DW 0 - high */ - unsigned int reserved3:10; - - unsigned int idef4cpy:22; /* DW 1 */ - unsigned int reserved4:10; - unsigned int idef4upd:22; - unsigned int reserved5:10; - - unsigned long idef1upd:64; /* DW 2 */ - - unsigned long idef5cpy:64; /* DW 3 */ - - unsigned long idef6cpy:64; /* DW 4 */ - - unsigned long idef3upd:64; /* DW 5 */ - - unsigned long idef5upd:64; /* DW 6 */ - - unsigned int idef2upd:22; /* DW 7 */ - unsigned int reserved6:10; - - unsigned int ecause:20; - unsigned int cbrstate:4; - unsigned int cbrexecstatus:8; -}; - -/* CBE fields for active BCOPY instructions */ -#define cbe_baddr0 idef1upd -#define cbe_baddr1 idef3upd -#define cbe_src_cl idef6cpy -#define cbe_nelemcur idef5upd - -enum gru_cbr_state { - CBRSTATE_INACTIVE, - CBRSTATE_IDLE, - CBRSTATE_PE_CHECK, - CBRSTATE_QUEUED, - CBRSTATE_WAIT_RESPONSE, - CBRSTATE_INTERRUPTED, - CBRSTATE_INTERRUPTED_MISS_FMM, - CBRSTATE_BUSY_INTERRUPT_MISS_FMM, - CBRSTATE_INTERRUPTED_MISS_UPM, - CBRSTATE_BUSY_INTERRUPTED_MISS_UPM, - CBRSTATE_REQUEST_ISSUE, - CBRSTATE_BUSY_INTERRUPT, -}; - -/* CBE cbrexecstatus bits - defined in gru_instructions.h*/ -/* CBE ecause bits - defined in gru_instructions.h */ - -/* - * Convert a processor pagesize into the strange encoded pagesize used by the - * GRU. Processor pagesize is encoded as log of bytes per page. (or PAGE_SHIFT) - * pagesize log pagesize grupagesize - * 4k 12 0 - * 16k 14 1 - * 64k 16 2 - * 256k 18 3 - * 1m 20 4 - * 2m 21 5 - * 4m 22 6 - * 16m 24 7 - * 64m 26 8 - * ... - */ -#define GRU_PAGESIZE(sh) ((((sh) > 20 ? (sh) + 2 : (sh)) >> 1) - 6) -#define GRU_SIZEAVAIL(sh) (1UL << GRU_PAGESIZE(sh)) - -/* minimum TLB purge count to ensure a full purge */ -#define GRUMAXINVAL 1024UL - -int cch_allocate(struct gru_context_configuration_handle *cch); -int cch_start(struct gru_context_configuration_handle *cch); -int cch_interrupt(struct gru_context_configuration_handle *cch); -int cch_deallocate(struct gru_context_configuration_handle *cch); -int cch_interrupt_sync(struct gru_context_configuration_handle *cch); -int tgh_invalidate(struct gru_tlb_global_handle *tgh, unsigned long vaddr, - unsigned long vaddrmask, int asid, int pagesize, int global, int n, - unsigned short ctxbitmap); -int tfh_write_only(struct gru_tlb_fault_handle *tfh, unsigned long paddr, - int gaa, unsigned long vaddr, int asid, int dirty, int pagesize); -void tfh_write_restart(struct gru_tlb_fault_handle *tfh, unsigned long paddr, - int gaa, unsigned long vaddr, int asid, int dirty, int pagesize); -void tfh_user_polling_mode(struct gru_tlb_fault_handle *tfh); -void tfh_exception(struct gru_tlb_fault_handle *tfh); - -#endif /* __GRUHANDLES_H__ */ diff --git a/drivers/misc/sgi-gru/grukdump.c b/drivers/misc/sgi-gru/grukdump.c deleted file mode 100644 index 9869f4f2f476..000000000000 --- a/drivers/misc/sgi-gru/grukdump.c +++ /dev/null @@ -1,223 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * Dump GRU State - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "gru.h" -#include "grutables.h" -#include "gruhandles.h" -#include "grulib.h" - -#define CCH_LOCK_ATTEMPTS 10 - -static int gru_user_copy_handle(void __user **dp, void *s) -{ - if (copy_to_user(*dp, s, GRU_HANDLE_BYTES)) - return -1; - *dp += GRU_HANDLE_BYTES; - return 0; -} - -static int gru_dump_context_data(void *grubase, - struct gru_context_configuration_handle *cch, - void __user *ubuf, int ctxnum, int dsrcnt, - int flush_cbrs) -{ - void *cb, *cbe, *tfh, *gseg; - int i, scr; - - gseg = grubase + ctxnum * GRU_GSEG_STRIDE; - cb = gseg + GRU_CB_BASE; - cbe = grubase + GRU_CBE_BASE; - tfh = grubase + GRU_TFH_BASE; - - for_each_cbr_in_allocation_map(i, &cch->cbr_allocation_map, scr) { - if (flush_cbrs) - gru_flush_cache(cb); - if (gru_user_copy_handle(&ubuf, cb)) - goto fail; - if (gru_user_copy_handle(&ubuf, tfh + i * GRU_HANDLE_STRIDE)) - goto fail; - if (gru_user_copy_handle(&ubuf, cbe + i * GRU_HANDLE_STRIDE)) - goto fail; - cb += GRU_HANDLE_STRIDE; - } - if (dsrcnt) - memcpy(ubuf, gseg + GRU_DS_BASE, dsrcnt * GRU_HANDLE_STRIDE); - return 0; - -fail: - return -EFAULT; -} - -static int gru_dump_tfm(struct gru_state *gru, - void __user *ubuf, void __user *ubufend) -{ - struct gru_tlb_fault_map *tfm; - int i; - - if (GRU_NUM_TFM * GRU_CACHE_LINE_BYTES > ubufend - ubuf) - return -EFBIG; - - for (i = 0; i < GRU_NUM_TFM; i++) { - tfm = get_tfm(gru->gs_gru_base_vaddr, i); - if (gru_user_copy_handle(&ubuf, tfm)) - goto fail; - } - return GRU_NUM_TFM * GRU_CACHE_LINE_BYTES; - -fail: - return -EFAULT; -} - -static int gru_dump_tgh(struct gru_state *gru, - void __user *ubuf, void __user *ubufend) -{ - struct gru_tlb_global_handle *tgh; - int i; - - if (GRU_NUM_TGH * GRU_CACHE_LINE_BYTES > ubufend - ubuf) - return -EFBIG; - - for (i = 0; i < GRU_NUM_TGH; i++) { - tgh = get_tgh(gru->gs_gru_base_vaddr, i); - if (gru_user_copy_handle(&ubuf, tgh)) - goto fail; - } - return GRU_NUM_TGH * GRU_CACHE_LINE_BYTES; - -fail: - return -EFAULT; -} - -static int gru_dump_context(struct gru_state *gru, int ctxnum, - void __user *ubuf, void __user *ubufend, char data_opt, - char lock_cch, char flush_cbrs) -{ - struct gru_dump_context_header hdr; - struct gru_dump_context_header __user *uhdr = ubuf; - struct gru_context_configuration_handle *cch, *ubufcch; - struct gru_thread_state *gts; - int try, cch_locked, cbrcnt = 0, dsrcnt = 0, bytes = 0, ret = 0; - void *grubase; - - memset(&hdr, 0, sizeof(hdr)); - grubase = gru->gs_gru_base_vaddr; - cch = get_cch(grubase, ctxnum); - for (try = 0; try < CCH_LOCK_ATTEMPTS; try++) { - cch_locked = trylock_cch_handle(cch); - if (cch_locked) - break; - msleep(1); - } - - ubuf += sizeof(hdr); - ubufcch = ubuf; - if (gru_user_copy_handle(&ubuf, cch)) { - if (cch_locked) - unlock_cch_handle(cch); - return -EFAULT; - } - if (cch_locked) - ubufcch->delresp = 0; - bytes = sizeof(hdr) + GRU_CACHE_LINE_BYTES; - - if (cch_locked || !lock_cch) { - gts = gru->gs_gts[ctxnum]; - if (gts && gts->ts_vma) { - hdr.pid = gts->ts_tgid_owner; - hdr.vaddr = gts->ts_vma->vm_start; - } - if (cch->state != CCHSTATE_INACTIVE) { - cbrcnt = hweight64(cch->cbr_allocation_map) * - GRU_CBR_AU_SIZE; - dsrcnt = data_opt ? hweight32(cch->dsr_allocation_map) * - GRU_DSR_AU_CL : 0; - } - bytes += (3 * cbrcnt + dsrcnt) * GRU_CACHE_LINE_BYTES; - if (bytes > ubufend - ubuf) - ret = -EFBIG; - else - ret = gru_dump_context_data(grubase, cch, ubuf, ctxnum, - dsrcnt, flush_cbrs); - } - if (cch_locked) - unlock_cch_handle(cch); - if (ret) - return ret; - - hdr.magic = GRU_DUMP_MAGIC; - hdr.gid = gru->gs_gid; - hdr.ctxnum = ctxnum; - hdr.cbrcnt = cbrcnt; - hdr.dsrcnt = dsrcnt; - hdr.cch_locked = cch_locked; - if (copy_to_user(uhdr, &hdr, sizeof(hdr))) - return -EFAULT; - - return bytes; -} - -int gru_dump_chiplet_request(unsigned long arg) -{ - struct gru_state *gru; - struct gru_dump_chiplet_state_req req; - void __user *ubuf; - void __user *ubufend; - int ctxnum, ret, cnt = 0; - - if (copy_from_user(&req, (void __user *)arg, sizeof(req))) - return -EFAULT; - - /* Currently, only dump by gid is implemented */ - if (req.gid >= gru_max_gids) - return -EINVAL; - req.gid = array_index_nospec(req.gid, gru_max_gids); - - gru = GID_TO_GRU(req.gid); - ubuf = req.buf; - ubufend = req.buf + req.buflen; - - ret = gru_dump_tfm(gru, ubuf, ubufend); - if (ret < 0) - goto fail; - ubuf += ret; - - ret = gru_dump_tgh(gru, ubuf, ubufend); - if (ret < 0) - goto fail; - ubuf += ret; - - for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) { - if (req.ctxnum == ctxnum || req.ctxnum < 0) { - ret = gru_dump_context(gru, ctxnum, ubuf, ubufend, - req.data_opt, req.lock_cch, - req.flush_cbrs); - if (ret < 0) - goto fail; - ubuf += ret; - cnt++; - } - } - - if (copy_to_user((void __user *)arg, &req, sizeof(req))) - return -EFAULT; - return cnt; - -fail: - return ret; -} diff --git a/drivers/misc/sgi-gru/grukservices.c b/drivers/misc/sgi-gru/grukservices.c deleted file mode 100644 index c16012ee976f..000000000000 --- a/drivers/misc/sgi-gru/grukservices.c +++ /dev/null @@ -1,1159 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * KERNEL SERVICES THAT USE THE GRU - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "gru.h" -#include "grulib.h" -#include "grutables.h" -#include "grukservices.h" -#include "gru_instructions.h" -#include - -/* - * Kernel GRU Usage - * - * The following is an interim algorithm for management of kernel GRU - * resources. This will likely be replaced when we better understand the - * kernel/user requirements. - * - * Blade percpu resources reserved for kernel use. These resources are - * reserved whenever the kernel context for the blade is loaded. Note - * that the kernel context is not guaranteed to be always available. It is - * loaded on demand & can be stolen by a user if the user demand exceeds the - * kernel demand. The kernel can always reload the kernel context but - * a SLEEP may be required!!!. - * - * Async Overview: - * - * Each blade has one "kernel context" that owns GRU kernel resources - * located on the blade. Kernel drivers use GRU resources in this context - * for sending messages, zeroing memory, etc. - * - * The kernel context is dynamically loaded on demand. If it is not in - * use by the kernel, the kernel context can be unloaded & given to a user. - * The kernel context will be reloaded when needed. This may require that - * a context be stolen from a user. - * NOTE: frequent unloading/reloading of the kernel context is - * expensive. We are depending on batch schedulers, cpusets, sane - * drivers or some other mechanism to prevent the need for frequent - * stealing/reloading. - * - * The kernel context consists of two parts: - * - 1 CB & a few DSRs that are reserved for each cpu on the blade. - * Each cpu has it's own private resources & does not share them - * with other cpus. These resources are used serially, ie, - * locked, used & unlocked on each call to a function in - * grukservices. - * (Now that we have dynamic loading of kernel contexts, I - * may rethink this & allow sharing between cpus....) - * - * - Additional resources can be reserved long term & used directly - * by UV drivers located in the kernel. Drivers using these GRU - * resources can use asynchronous GRU instructions that send - * interrupts on completion. - * - these resources must be explicitly locked/unlocked - * - locked resources prevent (obviously) the kernel - * context from being unloaded. - * - drivers using these resource directly issue their own - * GRU instruction and must wait/check completion. - * - * When these resources are reserved, the caller can optionally - * associate a wait_queue with the resources and use asynchronous - * GRU instructions. When an async GRU instruction completes, the - * driver will do a wakeup on the event. - * - */ - - -#define ASYNC_HAN_TO_BID(h) ((h) - 1) -#define ASYNC_BID_TO_HAN(b) ((b) + 1) -#define ASYNC_HAN_TO_BS(h) gru_base[ASYNC_HAN_TO_BID(h)] - -#define GRU_NUM_KERNEL_CBR 1 -#define GRU_NUM_KERNEL_DSR_BYTES 256 -#define GRU_NUM_KERNEL_DSR_CL (GRU_NUM_KERNEL_DSR_BYTES / \ - GRU_CACHE_LINE_BYTES) - -/* GRU instruction attributes for all instructions */ -#define IMA IMA_CB_DELAY - -/* GRU cacheline size is always 64 bytes - even on arches with 128 byte lines */ -#define __gru_cacheline_aligned__ \ - __attribute__((__aligned__(GRU_CACHE_LINE_BYTES))) - -#define MAGIC 0x1234567887654321UL - -/* Default retry count for GRU errors on kernel instructions */ -#define EXCEPTION_RETRY_LIMIT 3 - -/* Status of message queue sections */ -#define MQS_EMPTY 0 -#define MQS_FULL 1 -#define MQS_NOOP 2 - -/*----------------- RESOURCE MANAGEMENT -------------------------------------*/ -/* optimized for x86_64 */ -struct message_queue { - union gru_mesqhead head __gru_cacheline_aligned__; /* CL 0 */ - int qlines; /* DW 1 */ - long hstatus[2]; - void *next __gru_cacheline_aligned__;/* CL 1 */ - void *limit; - void *start; - void *start2; - char data ____cacheline_aligned; /* CL 2 */ -}; - -/* First word in every message - used by mesq interface */ -struct message_header { - char present; - char present2; - char lines; - char fill; -}; - -#define HSTATUS(mq, h) ((mq) + offsetof(struct message_queue, hstatus[h])) - -/* - * Reload the blade's kernel context into a GRU chiplet. Called holding - * the bs_kgts_sema for READ. Will steal user contexts if necessary. - */ -static void gru_load_kernel_context(struct gru_blade_state *bs, int blade_id) -{ - struct gru_state *gru; - struct gru_thread_state *kgts; - void *vaddr; - int ctxnum, ncpus; - - up_read(&bs->bs_kgts_sema); - down_write(&bs->bs_kgts_sema); - - if (!bs->bs_kgts) { - do { - bs->bs_kgts = gru_alloc_gts(NULL, 0, 0, 0, 0, 0); - if (!IS_ERR(bs->bs_kgts)) - break; - msleep(1); - } while (true); - bs->bs_kgts->ts_user_blade_id = blade_id; - } - kgts = bs->bs_kgts; - - if (!kgts->ts_gru) { - STAT(load_kernel_context); - ncpus = uv_blade_nr_possible_cpus(blade_id); - kgts->ts_cbr_au_count = GRU_CB_COUNT_TO_AU( - GRU_NUM_KERNEL_CBR * ncpus + bs->bs_async_cbrs); - kgts->ts_dsr_au_count = GRU_DS_BYTES_TO_AU( - GRU_NUM_KERNEL_DSR_BYTES * ncpus + - bs->bs_async_dsr_bytes); - while (!gru_assign_gru_context(kgts)) { - msleep(1); - gru_steal_context(kgts); - } - gru_load_context(kgts); - gru = bs->bs_kgts->ts_gru; - vaddr = gru->gs_gru_base_vaddr; - ctxnum = kgts->ts_ctxnum; - bs->kernel_cb = get_gseg_base_address_cb(vaddr, ctxnum, 0); - bs->kernel_dsr = get_gseg_base_address_ds(vaddr, ctxnum, 0); - } - downgrade_write(&bs->bs_kgts_sema); -} - -/* - * Free all kernel contexts that are not currently in use. - * Returns 0 if all freed, else number of inuse context. - */ -static int gru_free_kernel_contexts(void) -{ - struct gru_blade_state *bs; - struct gru_thread_state *kgts; - int bid, ret = 0; - - for (bid = 0; bid < GRU_MAX_BLADES; bid++) { - bs = gru_base[bid]; - if (!bs) - continue; - - /* Ignore busy contexts. Don't want to block here. */ - if (down_write_trylock(&bs->bs_kgts_sema)) { - kgts = bs->bs_kgts; - if (kgts && kgts->ts_gru) - gru_unload_context(kgts, 0); - bs->bs_kgts = NULL; - up_write(&bs->bs_kgts_sema); - kfree(kgts); - } else { - ret++; - } - } - return ret; -} - -/* - * Lock & load the kernel context for the specified blade. - */ -static struct gru_blade_state *gru_lock_kernel_context(int blade_id) -{ - struct gru_blade_state *bs; - int bid; - - STAT(lock_kernel_context); -again: - bid = blade_id < 0 ? uv_numa_blade_id() : blade_id; - bs = gru_base[bid]; - - /* Handle the case where migration occurred while waiting for the sema */ - down_read(&bs->bs_kgts_sema); - if (blade_id < 0 && bid != uv_numa_blade_id()) { - up_read(&bs->bs_kgts_sema); - goto again; - } - if (!bs->bs_kgts || !bs->bs_kgts->ts_gru) - gru_load_kernel_context(bs, bid); - return bs; - -} - -/* - * Unlock the kernel context for the specified blade. Context is not - * unloaded but may be stolen before next use. - */ -static void gru_unlock_kernel_context(int blade_id) -{ - struct gru_blade_state *bs; - - bs = gru_base[blade_id]; - up_read(&bs->bs_kgts_sema); - STAT(unlock_kernel_context); -} - -/* - * Reserve & get pointers to the DSR/CBRs reserved for the current cpu. - * - returns with preemption disabled - */ -static int gru_get_cpu_resources(int dsr_bytes, void **cb, void **dsr) -{ - struct gru_blade_state *bs; - int lcpu; - - BUG_ON(dsr_bytes > GRU_NUM_KERNEL_DSR_BYTES); - bs = gru_lock_kernel_context(-1); - lcpu = uv_blade_processor_id(); - *cb = bs->kernel_cb + lcpu * GRU_HANDLE_STRIDE; - *dsr = bs->kernel_dsr + lcpu * GRU_NUM_KERNEL_DSR_BYTES; - return 0; -} - -/* - * Free the current cpus reserved DSR/CBR resources. - */ -static void gru_free_cpu_resources(void *cb, void *dsr) -{ - gru_unlock_kernel_context(uv_numa_blade_id()); -} - -/* - * Reserve GRU resources to be used asynchronously. - * Note: currently supports only 1 reservation per blade. - * - * input: - * blade_id - blade on which resources should be reserved - * cbrs - number of CBRs - * dsr_bytes - number of DSR bytes needed - * output: - * handle to identify resource - * (0 = async resources already reserved) - */ -unsigned long gru_reserve_async_resources(int blade_id, int cbrs, int dsr_bytes, - struct completion *cmp) -{ - struct gru_blade_state *bs; - struct gru_thread_state *kgts; - int ret = 0; - - bs = gru_base[blade_id]; - - down_write(&bs->bs_kgts_sema); - - /* Verify no resources already reserved */ - if (bs->bs_async_dsr_bytes + bs->bs_async_cbrs) - goto done; - bs->bs_async_dsr_bytes = dsr_bytes; - bs->bs_async_cbrs = cbrs; - bs->bs_async_wq = cmp; - kgts = bs->bs_kgts; - - /* Resources changed. Unload context if already loaded */ - if (kgts && kgts->ts_gru) - gru_unload_context(kgts, 0); - ret = ASYNC_BID_TO_HAN(blade_id); - -done: - up_write(&bs->bs_kgts_sema); - return ret; -} - -/* - * Release async resources previously reserved. - * - * input: - * han - handle to identify resources - */ -void gru_release_async_resources(unsigned long han) -{ - struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han); - - down_write(&bs->bs_kgts_sema); - bs->bs_async_dsr_bytes = 0; - bs->bs_async_cbrs = 0; - bs->bs_async_wq = NULL; - up_write(&bs->bs_kgts_sema); -} - -/* - * Wait for async GRU instructions to complete. - * - * input: - * han - handle to identify resources - */ -void gru_wait_async_cbr(unsigned long han) -{ - struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han); - - wait_for_completion(bs->bs_async_wq); - mb(); -} - -/* - * Lock previous reserved async GRU resources - * - * input: - * han - handle to identify resources - * output: - * cb - pointer to first CBR - * dsr - pointer to first DSR - */ -void gru_lock_async_resource(unsigned long han, void **cb, void **dsr) -{ - struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han); - int blade_id = ASYNC_HAN_TO_BID(han); - int ncpus; - - gru_lock_kernel_context(blade_id); - ncpus = uv_blade_nr_possible_cpus(blade_id); - if (cb) - *cb = bs->kernel_cb + ncpus * GRU_HANDLE_STRIDE; - if (dsr) - *dsr = bs->kernel_dsr + ncpus * GRU_NUM_KERNEL_DSR_BYTES; -} - -/* - * Unlock previous reserved async GRU resources - * - * input: - * han - handle to identify resources - */ -void gru_unlock_async_resource(unsigned long han) -{ - int blade_id = ASYNC_HAN_TO_BID(han); - - gru_unlock_kernel_context(blade_id); -} - -/*----------------------------------------------------------------------*/ -int gru_get_cb_exception_detail(void *cb, - struct control_block_extended_exc_detail *excdet) -{ - struct gru_control_block_extended *cbe; - struct gru_thread_state *kgts = NULL; - unsigned long off; - int cbrnum, bid; - - /* - * Locate kgts for cb. This algorithm is SLOW but - * this function is rarely called (ie., almost never). - * Performance does not matter. - */ - for_each_possible_blade(bid) { - if (!gru_base[bid]) - break; - kgts = gru_base[bid]->bs_kgts; - if (!kgts || !kgts->ts_gru) - continue; - off = cb - kgts->ts_gru->gs_gru_base_vaddr; - if (off < GRU_SIZE) - break; - kgts = NULL; - } - BUG_ON(!kgts); - cbrnum = thread_cbr_number(kgts, get_cb_number(cb)); - cbe = get_cbe(GRUBASE(cb), cbrnum); - gru_flush_cache(cbe); /* CBE not coherent */ - sync_core(); - excdet->cb = (unsigned long)cb; - excdet->opc = cbe->opccpy; - excdet->exopc = cbe->exopccpy; - excdet->ecause = cbe->ecause; - excdet->exceptdet0 = cbe->idef1upd; - excdet->exceptdet1 = cbe->idef3upd; - excdet->cbrstate = cbe->cbrstate; - excdet->cbrexecstatus = cbe->cbrexecstatus; - gru_flush_cache(cbe); - return 0; -} - -static char *gru_get_cb_exception_detail_str(int ret, void *cb, - char *buf, int size) -{ - struct gru_control_block_status *gen = cb; - struct control_block_extended_exc_detail excdet; - - if (ret > 0 && gen->istatus == CBS_EXCEPTION) { - gru_get_cb_exception_detail(cb, &excdet); - snprintf(buf, size, - "GRU:%d exception: cb %p, opc %d, exopc %d, ecause 0x%x," - "excdet0 0x%lx, excdet1 0x%x", smp_processor_id(), - gen, excdet.opc, excdet.exopc, excdet.ecause, - excdet.exceptdet0, excdet.exceptdet1); - } else { - snprintf(buf, size, "No exception"); - } - return buf; -} - -static int gru_wait_idle_or_exception(struct gru_control_block_status *gen) -{ - while (gen->istatus >= CBS_ACTIVE) { - cpu_relax(); - barrier(); - } - return gen->istatus; -} - -static int gru_retry_exception(void *cb) -{ - struct gru_control_block_status *gen = cb; - struct control_block_extended_exc_detail excdet; - int retry = EXCEPTION_RETRY_LIMIT; - - while (1) { - if (gru_wait_idle_or_exception(gen) == CBS_IDLE) - return CBS_IDLE; - if (gru_get_cb_message_queue_substatus(cb)) - return CBS_EXCEPTION; - gru_get_cb_exception_detail(cb, &excdet); - if ((excdet.ecause & ~EXCEPTION_RETRY_BITS) || - (excdet.cbrexecstatus & CBR_EXS_ABORT_OCC)) - break; - if (retry-- == 0) - break; - gen->icmd = 1; - gru_flush_cache(gen); - } - return CBS_EXCEPTION; -} - -int gru_check_status_proc(void *cb) -{ - struct gru_control_block_status *gen = cb; - int ret; - - ret = gen->istatus; - if (ret == CBS_EXCEPTION) - ret = gru_retry_exception(cb); - rmb(); - return ret; - -} - -int gru_wait_proc(void *cb) -{ - struct gru_control_block_status *gen = cb; - int ret; - - ret = gru_wait_idle_or_exception(gen); - if (ret == CBS_EXCEPTION) - ret = gru_retry_exception(cb); - rmb(); - return ret; -} - -static void gru_abort(int ret, void *cb, char *str) -{ - char buf[GRU_EXC_STR_SIZE]; - - panic("GRU FATAL ERROR: %s - %s\n", str, - gru_get_cb_exception_detail_str(ret, cb, buf, sizeof(buf))); -} - -void gru_wait_abort_proc(void *cb) -{ - int ret; - - ret = gru_wait_proc(cb); - if (ret) - gru_abort(ret, cb, "gru_wait_abort"); -} - - -/*------------------------------ MESSAGE QUEUES -----------------------------*/ - -/* Internal status . These are NOT returned to the user. */ -#define MQIE_AGAIN -1 /* try again */ - - -/* - * Save/restore the "present" flag that is in the second line of 2-line - * messages - */ -static inline int get_present2(void *p) -{ - struct message_header *mhdr = p + GRU_CACHE_LINE_BYTES; - return mhdr->present; -} - -static inline void restore_present2(void *p, int val) -{ - struct message_header *mhdr = p + GRU_CACHE_LINE_BYTES; - mhdr->present = val; -} - -/* - * Create a message queue. - * qlines - message queue size in cache lines. Includes 2-line header. - */ -int gru_create_message_queue(struct gru_message_queue_desc *mqd, - void *p, unsigned int bytes, int nasid, int vector, int apicid) -{ - struct message_queue *mq = p; - unsigned int qlines; - - qlines = bytes / GRU_CACHE_LINE_BYTES - 2; - memset(mq, 0, bytes); - mq->start = &mq->data; - mq->start2 = &mq->data + (qlines / 2 - 1) * GRU_CACHE_LINE_BYTES; - mq->next = &mq->data; - mq->limit = &mq->data + (qlines - 2) * GRU_CACHE_LINE_BYTES; - mq->qlines = qlines; - mq->hstatus[0] = 0; - mq->hstatus[1] = 1; - mq->head = gru_mesq_head(2, qlines / 2 + 1); - mqd->mq = mq; - mqd->mq_gpa = uv_gpa(mq); - mqd->qlines = qlines; - mqd->interrupt_pnode = nasid >> 1; - mqd->interrupt_vector = vector; - mqd->interrupt_apicid = apicid; - return 0; -} -EXPORT_SYMBOL_GPL(gru_create_message_queue); - -/* - * Send a NOOP message to a message queue - * Returns: - * 0 - if queue is full after the send. This is the normal case - * but various races can change this. - * -1 - if mesq sent successfully but queue not full - * >0 - unexpected error. MQE_xxx returned - */ -static int send_noop_message(void *cb, struct gru_message_queue_desc *mqd, - void *mesg) -{ - const struct message_header noop_header = { - .present = MQS_NOOP, .lines = 1}; - unsigned long m; - int substatus, ret; - struct message_header save_mhdr, *mhdr = mesg; - - STAT(mesq_noop); - save_mhdr = *mhdr; - *mhdr = noop_header; - gru_mesq(cb, mqd->mq_gpa, gru_get_tri(mhdr), 1, IMA); - ret = gru_wait(cb); - - if (ret) { - substatus = gru_get_cb_message_queue_substatus(cb); - switch (substatus) { - case CBSS_NO_ERROR: - STAT(mesq_noop_unexpected_error); - ret = MQE_UNEXPECTED_CB_ERR; - break; - case CBSS_LB_OVERFLOWED: - STAT(mesq_noop_lb_overflow); - ret = MQE_CONGESTION; - break; - case CBSS_QLIMIT_REACHED: - STAT(mesq_noop_qlimit_reached); - ret = 0; - break; - case CBSS_AMO_NACKED: - STAT(mesq_noop_amo_nacked); - ret = MQE_CONGESTION; - break; - case CBSS_PUT_NACKED: - STAT(mesq_noop_put_nacked); - m = mqd->mq_gpa + (gru_get_amo_value_head(cb) << 6); - gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, 1, 1, - IMA); - if (gru_wait(cb) == CBS_IDLE) - ret = MQIE_AGAIN; - else - ret = MQE_UNEXPECTED_CB_ERR; - break; - case CBSS_PAGE_OVERFLOW: - STAT(mesq_noop_page_overflow); - fallthrough; - default: - BUG(); - } - } - *mhdr = save_mhdr; - return ret; -} - -/* - * Handle a gru_mesq full. - */ -static int send_message_queue_full(void *cb, struct gru_message_queue_desc *mqd, - void *mesg, int lines) -{ - union gru_mesqhead mqh; - unsigned int limit, head; - unsigned long avalue; - int half, qlines; - - /* Determine if switching to first/second half of q */ - avalue = gru_get_amo_value(cb); - head = gru_get_amo_value_head(cb); - limit = gru_get_amo_value_limit(cb); - - qlines = mqd->qlines; - half = (limit != qlines); - - if (half) - mqh = gru_mesq_head(qlines / 2 + 1, qlines); - else - mqh = gru_mesq_head(2, qlines / 2 + 1); - - /* Try to get lock for switching head pointer */ - gru_gamir(cb, EOP_IR_CLR, HSTATUS(mqd->mq_gpa, half), XTYPE_DW, IMA); - if (gru_wait(cb) != CBS_IDLE) - goto cberr; - if (!gru_get_amo_value(cb)) { - STAT(mesq_qf_locked); - return MQE_QUEUE_FULL; - } - - /* Got the lock. Send optional NOP if queue not full, */ - if (head != limit) { - if (send_noop_message(cb, mqd, mesg)) { - gru_gamir(cb, EOP_IR_INC, HSTATUS(mqd->mq_gpa, half), - XTYPE_DW, IMA); - if (gru_wait(cb) != CBS_IDLE) - goto cberr; - STAT(mesq_qf_noop_not_full); - return MQIE_AGAIN; - } - avalue++; - } - - /* Then flip queuehead to other half of queue. */ - gru_gamer(cb, EOP_ERR_CSWAP, mqd->mq_gpa, XTYPE_DW, mqh.val, avalue, - IMA); - if (gru_wait(cb) != CBS_IDLE) - goto cberr; - - /* If not successfully in swapping queue head, clear the hstatus lock */ - if (gru_get_amo_value(cb) != avalue) { - STAT(mesq_qf_switch_head_failed); - gru_gamir(cb, EOP_IR_INC, HSTATUS(mqd->mq_gpa, half), XTYPE_DW, - IMA); - if (gru_wait(cb) != CBS_IDLE) - goto cberr; - } - return MQIE_AGAIN; -cberr: - STAT(mesq_qf_unexpected_error); - return MQE_UNEXPECTED_CB_ERR; -} - -/* - * Handle a PUT failure. Note: if message was a 2-line message, one of the - * lines might have successfully have been written. Before sending the - * message, "present" must be cleared in BOTH lines to prevent the receiver - * from prematurely seeing the full message. - */ -static int send_message_put_nacked(void *cb, struct gru_message_queue_desc *mqd, - void *mesg, int lines) -{ - unsigned long m; - int ret, loops = 200; /* experimentally determined */ - - m = mqd->mq_gpa + (gru_get_amo_value_head(cb) << 6); - if (lines == 2) { - gru_vset(cb, m, 0, XTYPE_CL, lines, 1, IMA); - if (gru_wait(cb) != CBS_IDLE) - return MQE_UNEXPECTED_CB_ERR; - } - gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, lines, 1, IMA); - if (gru_wait(cb) != CBS_IDLE) - return MQE_UNEXPECTED_CB_ERR; - - if (!mqd->interrupt_vector) - return MQE_OK; - - /* - * Send a noop message in order to deliver a cross-partition interrupt - * to the SSI that contains the target message queue. Normally, the - * interrupt is automatically delivered by hardware following mesq - * operations, but some error conditions require explicit delivery. - * The noop message will trigger delivery. Otherwise partition failures - * could cause unrecovered errors. - */ - do { - ret = send_noop_message(cb, mqd, mesg); - } while ((ret == MQIE_AGAIN || ret == MQE_CONGESTION) && (loops-- > 0)); - - if (ret == MQIE_AGAIN || ret == MQE_CONGESTION) { - /* - * Don't indicate to the app to resend the message, as it's - * already been successfully sent. We simply send an OK - * (rather than fail the send with MQE_UNEXPECTED_CB_ERR), - * assuming that the other side is receiving enough - * interrupts to get this message processed anyway. - */ - ret = MQE_OK; - } - return ret; -} - -/* - * Handle a gru_mesq failure. Some of these failures are software recoverable - * or retryable. - */ -static int send_message_failure(void *cb, struct gru_message_queue_desc *mqd, - void *mesg, int lines) -{ - int substatus, ret = 0; - - substatus = gru_get_cb_message_queue_substatus(cb); - switch (substatus) { - case CBSS_NO_ERROR: - STAT(mesq_send_unexpected_error); - ret = MQE_UNEXPECTED_CB_ERR; - break; - case CBSS_LB_OVERFLOWED: - STAT(mesq_send_lb_overflow); - ret = MQE_CONGESTION; - break; - case CBSS_QLIMIT_REACHED: - STAT(mesq_send_qlimit_reached); - ret = send_message_queue_full(cb, mqd, mesg, lines); - break; - case CBSS_AMO_NACKED: - STAT(mesq_send_amo_nacked); - ret = MQE_CONGESTION; - break; - case CBSS_PUT_NACKED: - STAT(mesq_send_put_nacked); - ret = send_message_put_nacked(cb, mqd, mesg, lines); - break; - case CBSS_PAGE_OVERFLOW: - STAT(mesq_page_overflow); - fallthrough; - default: - BUG(); - } - return ret; -} - -/* - * Send a message to a message queue - * mqd message queue descriptor - * mesg message. ust be vaddr within a GSEG - * bytes message size (<= 2 CL) - */ -int gru_send_message_gpa(struct gru_message_queue_desc *mqd, void *mesg, - unsigned int bytes) -{ - struct message_header *mhdr; - void *cb; - void *dsr; - int istatus, clines, ret; - - STAT(mesq_send); - BUG_ON(bytes < sizeof(int) || bytes > 2 * GRU_CACHE_LINE_BYTES); - - clines = DIV_ROUND_UP(bytes, GRU_CACHE_LINE_BYTES); - if (gru_get_cpu_resources(bytes, &cb, &dsr)) - return MQE_BUG_NO_RESOURCES; - memcpy(dsr, mesg, bytes); - mhdr = dsr; - mhdr->present = MQS_FULL; - mhdr->lines = clines; - if (clines == 2) { - mhdr->present2 = get_present2(mhdr); - restore_present2(mhdr, MQS_FULL); - } - - do { - ret = MQE_OK; - gru_mesq(cb, mqd->mq_gpa, gru_get_tri(mhdr), clines, IMA); - istatus = gru_wait(cb); - if (istatus != CBS_IDLE) - ret = send_message_failure(cb, mqd, dsr, clines); - } while (ret == MQIE_AGAIN); - gru_free_cpu_resources(cb, dsr); - - if (ret) - STAT(mesq_send_failed); - return ret; -} -EXPORT_SYMBOL_GPL(gru_send_message_gpa); - -/* - * Advance the receive pointer for the queue to the next message. - */ -void gru_free_message(struct gru_message_queue_desc *mqd, void *mesg) -{ - struct message_queue *mq = mqd->mq; - struct message_header *mhdr = mq->next; - void *next, *pnext; - int half = -1; - int lines = mhdr->lines; - - if (lines == 2) - restore_present2(mhdr, MQS_EMPTY); - mhdr->present = MQS_EMPTY; - - pnext = mq->next; - next = pnext + GRU_CACHE_LINE_BYTES * lines; - if (next == mq->limit) { - next = mq->start; - half = 1; - } else if (pnext < mq->start2 && next >= mq->start2) { - half = 0; - } - - if (half >= 0) - mq->hstatus[half] = 1; - mq->next = next; -} -EXPORT_SYMBOL_GPL(gru_free_message); - -/* - * Get next message from message queue. Return NULL if no message - * present. User must call next_message() to move to next message. - * rmq message queue - */ -void *gru_get_next_message(struct gru_message_queue_desc *mqd) -{ - struct message_queue *mq = mqd->mq; - struct message_header *mhdr = mq->next; - int present = mhdr->present; - - /* skip NOOP messages */ - while (present == MQS_NOOP) { - gru_free_message(mqd, mhdr); - mhdr = mq->next; - present = mhdr->present; - } - - /* Wait for both halves of 2 line messages */ - if (present == MQS_FULL && mhdr->lines == 2 && - get_present2(mhdr) == MQS_EMPTY) - present = MQS_EMPTY; - - if (!present) { - STAT(mesq_receive_none); - return NULL; - } - - if (mhdr->lines == 2) - restore_present2(mhdr, mhdr->present2); - - STAT(mesq_receive); - return mhdr; -} -EXPORT_SYMBOL_GPL(gru_get_next_message); - -/* ---------------------- GRU DATA COPY FUNCTIONS ---------------------------*/ - -/* - * Load a DW from a global GPA. The GPA can be a memory or MMR address. - */ -int gru_read_gpa(unsigned long *value, unsigned long gpa) -{ - void *cb; - void *dsr; - int ret, iaa; - - STAT(read_gpa); - if (gru_get_cpu_resources(GRU_NUM_KERNEL_DSR_BYTES, &cb, &dsr)) - return MQE_BUG_NO_RESOURCES; - iaa = gpa >> 62; - gru_vload_phys(cb, gpa, gru_get_tri(dsr), iaa, IMA); - ret = gru_wait(cb); - if (ret == CBS_IDLE) - *value = *(unsigned long *)dsr; - gru_free_cpu_resources(cb, dsr); - return ret; -} -EXPORT_SYMBOL_GPL(gru_read_gpa); - - -/* - * Copy a block of data using the GRU resources - */ -int gru_copy_gpa(unsigned long dest_gpa, unsigned long src_gpa, - unsigned int bytes) -{ - void *cb; - void *dsr; - int ret; - - STAT(copy_gpa); - if (gru_get_cpu_resources(GRU_NUM_KERNEL_DSR_BYTES, &cb, &dsr)) - return MQE_BUG_NO_RESOURCES; - gru_bcopy(cb, src_gpa, dest_gpa, gru_get_tri(dsr), - XTYPE_B, bytes, GRU_NUM_KERNEL_DSR_CL, IMA); - ret = gru_wait(cb); - gru_free_cpu_resources(cb, dsr); - return ret; -} -EXPORT_SYMBOL_GPL(gru_copy_gpa); - -/* ------------------- KERNEL QUICKTESTS RUN AT STARTUP ----------------*/ -/* Temp - will delete after we gain confidence in the GRU */ - -static int quicktest0(unsigned long arg) -{ - unsigned long word0; - unsigned long word1; - void *cb; - void *dsr; - unsigned long *p; - int ret = -EIO; - - if (gru_get_cpu_resources(GRU_CACHE_LINE_BYTES, &cb, &dsr)) - return MQE_BUG_NO_RESOURCES; - p = dsr; - word0 = MAGIC; - word1 = 0; - - gru_vload(cb, uv_gpa(&word0), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA); - if (gru_wait(cb) != CBS_IDLE) { - printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 1\n", smp_processor_id()); - goto done; - } - - if (*p != MAGIC) { - printk(KERN_DEBUG "GRU:%d quicktest0 bad magic 0x%lx\n", smp_processor_id(), *p); - goto done; - } - gru_vstore(cb, uv_gpa(&word1), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA); - if (gru_wait(cb) != CBS_IDLE) { - printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 2\n", smp_processor_id()); - goto done; - } - - if (word0 != word1 || word1 != MAGIC) { - printk(KERN_DEBUG - "GRU:%d quicktest0 err: found 0x%lx, expected 0x%lx\n", - smp_processor_id(), word1, MAGIC); - goto done; - } - ret = 0; - -done: - gru_free_cpu_resources(cb, dsr); - return ret; -} - -#define ALIGNUP(p, q) ((void *)(((unsigned long)(p) + (q) - 1) & ~(q - 1))) - -static int quicktest1(unsigned long arg) -{ - struct gru_message_queue_desc mqd; - void *p, *mq; - int i, ret = -EIO; - char mes[GRU_CACHE_LINE_BYTES], *m; - - /* Need 1K cacheline aligned that does not cross page boundary */ - p = kmalloc(4096, 0); - if (p == NULL) - return -ENOMEM; - mq = ALIGNUP(p, 1024); - memset(mes, 0xee, sizeof(mes)); - - gru_create_message_queue(&mqd, mq, 8 * GRU_CACHE_LINE_BYTES, 0, 0, 0); - for (i = 0; i < 6; i++) { - mes[8] = i; - do { - ret = gru_send_message_gpa(&mqd, mes, sizeof(mes)); - } while (ret == MQE_CONGESTION); - if (ret) - break; - } - if (ret != MQE_QUEUE_FULL || i != 4) { - printk(KERN_DEBUG "GRU:%d quicktest1: unexpected status %d, i %d\n", - smp_processor_id(), ret, i); - goto done; - } - - for (i = 0; i < 6; i++) { - m = gru_get_next_message(&mqd); - if (!m || m[8] != i) - break; - gru_free_message(&mqd, m); - } - if (i != 4) { - printk(KERN_DEBUG "GRU:%d quicktest2: bad message, i %d, m %p, m8 %d\n", - smp_processor_id(), i, m, m ? m[8] : -1); - goto done; - } - ret = 0; - -done: - kfree(p); - return ret; -} - -static int quicktest2(unsigned long arg) -{ - static DECLARE_COMPLETION(cmp); - unsigned long han; - int blade_id = 0; - int numcb = 4; - int ret = 0; - unsigned long *buf; - void *cb0, *cb; - struct gru_control_block_status *gen; - int i, k, istatus, bytes; - - bytes = numcb * 4 * 8; - buf = kmalloc(bytes, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = -EBUSY; - han = gru_reserve_async_resources(blade_id, numcb, 0, &cmp); - if (!han) - goto done; - - gru_lock_async_resource(han, &cb0, NULL); - memset(buf, 0xee, bytes); - for (i = 0; i < numcb; i++) - gru_vset(cb0 + i * GRU_HANDLE_STRIDE, uv_gpa(&buf[i * 4]), 0, - XTYPE_DW, 4, 1, IMA_INTERRUPT); - - ret = 0; - k = numcb; - do { - gru_wait_async_cbr(han); - for (i = 0; i < numcb; i++) { - cb = cb0 + i * GRU_HANDLE_STRIDE; - istatus = gru_check_status(cb); - if (istatus != CBS_ACTIVE && istatus != CBS_CALL_OS) - break; - } - if (i == numcb) - continue; - if (istatus != CBS_IDLE) { - printk(KERN_DEBUG "GRU:%d quicktest2: cb %d, exception\n", smp_processor_id(), i); - ret = -EFAULT; - } else if (buf[4 * i] || buf[4 * i + 1] || buf[4 * i + 2] || - buf[4 * i + 3]) { - printk(KERN_DEBUG "GRU:%d quicktest2:cb %d, buf 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", - smp_processor_id(), i, buf[4 * i], buf[4 * i + 1], buf[4 * i + 2], buf[4 * i + 3]); - ret = -EIO; - } - k--; - gen = cb; - gen->istatus = CBS_CALL_OS; /* don't handle this CBR again */ - } while (k); - BUG_ON(cmp.done); - - gru_unlock_async_resource(han); - gru_release_async_resources(han); -done: - kfree(buf); - return ret; -} - -#define BUFSIZE 200 -static int quicktest3(unsigned long arg) -{ - char buf1[BUFSIZE], buf2[BUFSIZE]; - int ret = 0; - - memset(buf2, 0, sizeof(buf2)); - memset(buf1, get_cycles() & 255, sizeof(buf1)); - gru_copy_gpa(uv_gpa(buf2), uv_gpa(buf1), BUFSIZE); - if (memcmp(buf1, buf2, BUFSIZE)) { - printk(KERN_DEBUG "GRU:%d quicktest3 error\n", smp_processor_id()); - ret = -EIO; - } - return ret; -} - -/* - * Debugging only. User hook for various kernel tests - * of driver & gru. - */ -int gru_ktest(unsigned long arg) -{ - int ret = -EINVAL; - - switch (arg & 0xff) { - case 0: - ret = quicktest0(arg); - break; - case 1: - ret = quicktest1(arg); - break; - case 2: - ret = quicktest2(arg); - break; - case 3: - ret = quicktest3(arg); - break; - case 99: - ret = gru_free_kernel_contexts(); - break; - } - return ret; - -} - -int gru_kservices_init(void) -{ - return 0; -} - -void gru_kservices_exit(void) -{ - if (gru_free_kernel_contexts()) - BUG(); -} diff --git a/drivers/misc/sgi-gru/grukservices.h b/drivers/misc/sgi-gru/grukservices.h deleted file mode 100644 index 510e45e9737e..000000000000 --- a/drivers/misc/sgi-gru/grukservices.h +++ /dev/null @@ -1,201 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/* - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ -#ifndef __GRU_KSERVICES_H_ -#define __GRU_KSERVICES_H_ - - -/* - * Message queues using the GRU to send/receive messages. - * - * These function allow the user to create a message queue for - * sending/receiving 1 or 2 cacheline messages using the GRU. - * - * Processes SENDING messages will use a kernel CBR/DSR to send - * the message. This is transparent to the caller. - * - * The receiver does not use any GRU resources. - * - * The functions support: - * - single receiver - * - multiple senders - * - cross partition message - * - * Missing features ZZZ: - * - user options for dealing with timeouts, queue full, etc. - * - gru_create_message_queue() needs interrupt vector info - */ - -struct gru_message_queue_desc { - void *mq; /* message queue vaddress */ - unsigned long mq_gpa; /* global address of mq */ - int qlines; /* queue size in CL */ - int interrupt_vector; /* interrupt vector */ - int interrupt_pnode; /* pnode for interrupt */ - int interrupt_apicid; /* lapicid for interrupt */ -}; - -/* - * Initialize a user allocated chunk of memory to be used as - * a message queue. The caller must ensure that the queue is - * in contiguous physical memory and is cacheline aligned. - * - * Message queue size is the total number of bytes allocated - * to the queue including a 2 cacheline header that is used - * to manage the queue. - * - * Input: - * mqd pointer to message queue descriptor - * p pointer to user allocated mesq memory. - * bytes size of message queue in bytes - * vector interrupt vector (zero if no interrupts) - * nasid nasid of blade where interrupt is delivered - * apicid apicid of cpu for interrupt - * - * Errors: - * 0 OK - * >0 error - */ -extern int gru_create_message_queue(struct gru_message_queue_desc *mqd, - void *p, unsigned int bytes, int nasid, int vector, int apicid); - -/* - * Send a message to a message queue. - * - * Note: The message queue transport mechanism uses the first 32 - * bits of the message. Users should avoid using these bits. - * - * - * Input: - * mqd pointer to message queue descriptor - * mesg pointer to message. Must be 64-bit aligned - * bytes size of message in bytes - * - * Output: - * 0 message sent - * >0 Send failure - see error codes below - * - */ -extern int gru_send_message_gpa(struct gru_message_queue_desc *mqd, - void *mesg, unsigned int bytes); - -/* Status values for gru_send_message() */ -#define MQE_OK 0 /* message sent successfully */ -#define MQE_CONGESTION 1 /* temporary congestion, try again */ -#define MQE_QUEUE_FULL 2 /* queue is full */ -#define MQE_UNEXPECTED_CB_ERR 3 /* unexpected CB error */ -#define MQE_PAGE_OVERFLOW 10 /* BUG - queue overflowed a page */ -#define MQE_BUG_NO_RESOURCES 11 /* BUG - could not alloc GRU cb/dsr */ - -/* - * Advance the receive pointer for the message queue to the next message. - * Note: current API requires messages to be gotten & freed in order. Future - * API extensions may allow for out-of-order freeing. - * - * Input - * mqd pointer to message queue descriptor - * mesq message being freed - */ -extern void gru_free_message(struct gru_message_queue_desc *mqd, - void *mesq); - -/* - * Get next message from message queue. Returns pointer to - * message OR NULL if no message present. - * User must call gru_free_message() after message is processed - * in order to move the queue pointers to next message. - * - * Input - * mqd pointer to message queue descriptor - * - * Output: - * p pointer to message - * NULL no message available - */ -extern void *gru_get_next_message(struct gru_message_queue_desc *mqd); - - -/* - * Read a GRU global GPA. Source can be located in a remote partition. - * - * Input: - * value memory address where MMR value is returned - * gpa source numalink physical address of GPA - * - * Output: - * 0 OK - * >0 error - */ -int gru_read_gpa(unsigned long *value, unsigned long gpa); - - -/* - * Copy data using the GRU. Source or destination can be located in a remote - * partition. - * - * Input: - * dest_gpa destination global physical address - * src_gpa source global physical address - * bytes number of bytes to copy - * - * Output: - * 0 OK - * >0 error - */ -extern int gru_copy_gpa(unsigned long dest_gpa, unsigned long src_gpa, - unsigned int bytes); - -/* - * Reserve GRU resources to be used asynchronously. - * - * input: - * blade_id - blade on which resources should be reserved - * cbrs - number of CBRs - * dsr_bytes - number of DSR bytes needed - * cmp - completion structure for waiting for - * async completions - * output: - * handle to identify resource - * (0 = no resources) - */ -extern unsigned long gru_reserve_async_resources(int blade_id, int cbrs, int dsr_bytes, - struct completion *cmp); - -/* - * Release async resources previously reserved. - * - * input: - * han - handle to identify resources - */ -extern void gru_release_async_resources(unsigned long han); - -/* - * Wait for async GRU instructions to complete. - * - * input: - * han - handle to identify resources - */ -extern void gru_wait_async_cbr(unsigned long han); - -/* - * Lock previous reserved async GRU resources - * - * input: - * han - handle to identify resources - * output: - * cb - pointer to first CBR - * dsr - pointer to first DSR - */ -extern void gru_lock_async_resource(unsigned long han, void **cb, void **dsr); - -/* - * Unlock previous reserved async GRU resources - * - * input: - * han - handle to identify resources - */ -extern void gru_unlock_async_resource(unsigned long han); - -#endif /* __GRU_KSERVICES_H_ */ diff --git a/drivers/misc/sgi-gru/grulib.h b/drivers/misc/sgi-gru/grulib.h deleted file mode 100644 index 85c103923632..000000000000 --- a/drivers/misc/sgi-gru/grulib.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __GRULIB_H__ -#define __GRULIB_H__ - -#define GRU_BASENAME "gru" -#define GRU_FULLNAME "/dev/gru" -#define GRU_IOCTL_NUM 'G' - -/* - * Maximum number of GRU segments that a user can have open - * ZZZ temp - set high for testing. Revisit. - */ -#define GRU_MAX_OPEN_CONTEXTS 32 - -/* Set Number of Request Blocks */ -#define GRU_CREATE_CONTEXT _IOWR(GRU_IOCTL_NUM, 1, void *) - -/* Set Context Options */ -#define GRU_SET_CONTEXT_OPTION _IOWR(GRU_IOCTL_NUM, 4, void *) - -/* Fetch exception detail */ -#define GRU_USER_GET_EXCEPTION_DETAIL _IOWR(GRU_IOCTL_NUM, 6, void *) - -/* For user call_os handling - normally a TLB fault */ -#define GRU_USER_CALL_OS _IOWR(GRU_IOCTL_NUM, 8, void *) - -/* For user unload context */ -#define GRU_USER_UNLOAD_CONTEXT _IOWR(GRU_IOCTL_NUM, 9, void *) - -/* For dumpping GRU chiplet state */ -#define GRU_DUMP_CHIPLET_STATE _IOWR(GRU_IOCTL_NUM, 11, void *) - -/* For getting gseg statistics */ -#define GRU_GET_GSEG_STATISTICS _IOWR(GRU_IOCTL_NUM, 12, void *) - -/* For user TLB flushing (primarily for tests) */ -#define GRU_USER_FLUSH_TLB _IOWR(GRU_IOCTL_NUM, 50, void *) - -/* Get some config options (primarily for tests & emulator) */ -#define GRU_GET_CONFIG_INFO _IOWR(GRU_IOCTL_NUM, 51, void *) - -/* Various kernel self-tests */ -#define GRU_KTEST _IOWR(GRU_IOCTL_NUM, 52, void *) - -#define CONTEXT_WINDOW_BYTES(th) (GRU_GSEG_PAGESIZE * (th)) -#define THREAD_POINTER(p, th) (p + GRU_GSEG_PAGESIZE * (th)) -#define GSEG_START(cb) ((void *)((unsigned long)(cb) & ~(GRU_GSEG_PAGESIZE - 1))) - -struct gru_get_gseg_statistics_req { - unsigned long gseg; - struct gru_gseg_statistics stats; -}; - -/* - * Structure used to pass TLB flush parameters to the driver - */ -struct gru_create_context_req { - unsigned long gseg; - unsigned int data_segment_bytes; - unsigned int control_blocks; - unsigned int maximum_thread_count; - unsigned int options; - unsigned char tlb_preload_count; -}; - -/* - * Structure used to pass unload context parameters to the driver - */ -struct gru_unload_context_req { - unsigned long gseg; -}; - -/* - * Structure used to set context options - */ -enum {sco_gseg_owner, sco_cch_req_slice, sco_blade_chiplet}; -struct gru_set_context_option_req { - unsigned long gseg; - int op; - int val0; - long val1; -}; - -/* - * Structure used to pass TLB flush parameters to the driver - */ -struct gru_flush_tlb_req { - unsigned long gseg; - unsigned long vaddr; - size_t len; -}; - -/* - * Structure used to pass TLB flush parameters to the driver - */ -enum {dcs_pid, dcs_gid}; -struct gru_dump_chiplet_state_req { - unsigned int op; - unsigned int gid; - int ctxnum; - char data_opt; - char lock_cch; - char flush_cbrs; - char fill[10]; - pid_t pid; - void *buf; - size_t buflen; - /* ---- output --- */ - unsigned int num_contexts; -}; - -#define GRU_DUMP_MAGIC 0x3474ab6c -struct gru_dump_context_header { - unsigned int magic; - unsigned int gid; - unsigned char ctxnum; - unsigned char cbrcnt; - unsigned char dsrcnt; - pid_t pid; - unsigned long vaddr; - int cch_locked; - unsigned long data[]; -}; - -/* - * GRU configuration info (temp - for testing) - */ -struct gru_config_info { - int cpus; - int blades; - int nodes; - int chiplets; - int fill[16]; -}; - -#endif /* __GRULIB_H__ */ diff --git a/drivers/misc/sgi-gru/grumain.c b/drivers/misc/sgi-gru/grumain.c deleted file mode 100644 index 278b76cbd281..000000000000 --- a/drivers/misc/sgi-gru/grumain.c +++ /dev/null @@ -1,969 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * DRIVER TABLE MANAGER + GRU CONTEXT LOAD/UNLOAD - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "gru.h" -#include "grutables.h" -#include "gruhandles.h" - -unsigned long gru_options __read_mostly; - -static struct device_driver gru_driver = { - .name = "gru" -}; - -static struct device gru_device = { - .init_name = "", - .driver = &gru_driver, -}; - -struct device *grudev = &gru_device; - -/* - * Select a gru fault map to be used by the current cpu. Note that - * multiple cpus may be using the same map. - * ZZZ should be inline but did not work on emulator - */ -int gru_cpu_fault_map_id(void) -{ - int cpu = smp_processor_id(); - int id, core; - - core = uv_cpu_core_number(cpu); - id = core + UV_MAX_INT_CORES * uv_cpu_socket_number(cpu); - return id; -} - -/*--------- ASID Management ------------------------------------------- - * - * Initially, assign asids sequentially from MIN_ASID .. MAX_ASID. - * Once MAX is reached, flush the TLB & start over. However, - * some asids may still be in use. There won't be many (percentage wise) still - * in use. Search active contexts & determine the value of the first - * asid in use ("x"s below). Set "limit" to this value. - * This defines a block of assignable asids. - * - * When "limit" is reached, search forward from limit+1 and determine the - * next block of assignable asids. - * - * Repeat until MAX_ASID is reached, then start over again. - * - * Each time MAX_ASID is reached, increment the asid generation. Since - * the search for in-use asids only checks contexts with GRUs currently - * assigned, asids in some contexts will be missed. Prior to loading - * a context, the asid generation of the GTS asid is rechecked. If it - * doesn't match the current generation, a new asid will be assigned. - * - * 0---------------x------------x---------------------x----| - * ^-next ^-limit ^-MAX_ASID - * - * All asid manipulation & context loading/unloading is protected by the - * gs_lock. - */ - -/* Hit the asid limit. Start over */ -static int gru_wrap_asid(struct gru_state *gru) -{ - gru_dbg(grudev, "gid %d\n", gru->gs_gid); - STAT(asid_wrap); - gru->gs_asid_gen++; - return MIN_ASID; -} - -/* Find the next chunk of unused asids */ -static int gru_reset_asid_limit(struct gru_state *gru, int asid) -{ - int i, gid, inuse_asid, limit; - - gru_dbg(grudev, "gid %d, asid 0x%x\n", gru->gs_gid, asid); - STAT(asid_next); - limit = MAX_ASID; - if (asid >= limit) - asid = gru_wrap_asid(gru); - gru_flush_all_tlb(gru); - gid = gru->gs_gid; -again: - for (i = 0; i < GRU_NUM_CCH; i++) { - if (!gru->gs_gts[i] || is_kernel_context(gru->gs_gts[i])) - continue; - inuse_asid = gru->gs_gts[i]->ts_gms->ms_asids[gid].mt_asid; - gru_dbg(grudev, "gid %d, gts %p, gms %p, inuse 0x%x, cxt %d\n", - gru->gs_gid, gru->gs_gts[i], gru->gs_gts[i]->ts_gms, - inuse_asid, i); - if (inuse_asid == asid) { - asid += ASID_INC; - if (asid >= limit) { - /* - * empty range: reset the range limit and - * start over - */ - limit = MAX_ASID; - if (asid >= MAX_ASID) - asid = gru_wrap_asid(gru); - goto again; - } - } - - if ((inuse_asid > asid) && (inuse_asid < limit)) - limit = inuse_asid; - } - gru->gs_asid_limit = limit; - gru->gs_asid = asid; - gru_dbg(grudev, "gid %d, new asid 0x%x, new_limit 0x%x\n", gru->gs_gid, - asid, limit); - return asid; -} - -/* Assign a new ASID to a thread context. */ -static int gru_assign_asid(struct gru_state *gru) -{ - int asid; - - gru->gs_asid += ASID_INC; - asid = gru->gs_asid; - if (asid >= gru->gs_asid_limit) - asid = gru_reset_asid_limit(gru, asid); - - gru_dbg(grudev, "gid %d, asid 0x%x\n", gru->gs_gid, asid); - return asid; -} - -/* - * Clear n bits in a word. Return a word indicating the bits that were cleared. - * Optionally, build an array of chars that contain the bit numbers allocated. - */ -static unsigned long reserve_resources(unsigned long *p, int n, int mmax, - signed char *idx) -{ - unsigned long bits = 0; - int i; - - while (n--) { - i = find_first_bit(p, mmax); - if (i == mmax) - BUG(); - __clear_bit(i, p); - __set_bit(i, &bits); - if (idx) - *idx++ = i; - } - return bits; -} - -unsigned long gru_reserve_cb_resources(struct gru_state *gru, int cbr_au_count, - signed char *cbmap) -{ - return reserve_resources(&gru->gs_cbr_map, cbr_au_count, GRU_CBR_AU, - cbmap); -} - -unsigned long gru_reserve_ds_resources(struct gru_state *gru, int dsr_au_count, - signed char *dsmap) -{ - return reserve_resources(&gru->gs_dsr_map, dsr_au_count, GRU_DSR_AU, - dsmap); -} - -static void reserve_gru_resources(struct gru_state *gru, - struct gru_thread_state *gts) -{ - gru->gs_active_contexts++; - gts->ts_cbr_map = - gru_reserve_cb_resources(gru, gts->ts_cbr_au_count, - gts->ts_cbr_idx); - gts->ts_dsr_map = - gru_reserve_ds_resources(gru, gts->ts_dsr_au_count, NULL); -} - -static void free_gru_resources(struct gru_state *gru, - struct gru_thread_state *gts) -{ - gru->gs_active_contexts--; - gru->gs_cbr_map |= gts->ts_cbr_map; - gru->gs_dsr_map |= gts->ts_dsr_map; -} - -/* - * Check if a GRU has sufficient free resources to satisfy an allocation - * request. Note: GRU locks may or may not be held when this is called. If - * not held, recheck after acquiring the appropriate locks. - * - * Returns 1 if sufficient resources, 0 if not - */ -static int check_gru_resources(struct gru_state *gru, int cbr_au_count, - int dsr_au_count, int max_active_contexts) -{ - return hweight64(gru->gs_cbr_map) >= cbr_au_count - && hweight64(gru->gs_dsr_map) >= dsr_au_count - && gru->gs_active_contexts < max_active_contexts; -} - -/* - * TLB manangment requires tracking all GRU chiplets that have loaded a GSEG - * context. - */ -static int gru_load_mm_tracker(struct gru_state *gru, - struct gru_thread_state *gts) -{ - struct gru_mm_struct *gms = gts->ts_gms; - struct gru_mm_tracker *asids = &gms->ms_asids[gru->gs_gid]; - unsigned short ctxbitmap = (1 << gts->ts_ctxnum); - int asid; - - spin_lock(&gms->ms_asid_lock); - asid = asids->mt_asid; - - spin_lock(&gru->gs_asid_lock); - if (asid == 0 || (asids->mt_ctxbitmap == 0 && asids->mt_asid_gen != - gru->gs_asid_gen)) { - asid = gru_assign_asid(gru); - asids->mt_asid = asid; - asids->mt_asid_gen = gru->gs_asid_gen; - STAT(asid_new); - } else { - STAT(asid_reuse); - } - spin_unlock(&gru->gs_asid_lock); - - BUG_ON(asids->mt_ctxbitmap & ctxbitmap); - asids->mt_ctxbitmap |= ctxbitmap; - if (!test_bit(gru->gs_gid, gms->ms_asidmap)) - __set_bit(gru->gs_gid, gms->ms_asidmap); - spin_unlock(&gms->ms_asid_lock); - - gru_dbg(grudev, - "gid %d, gts %p, gms %p, ctxnum %d, asid 0x%x, asidmap 0x%lx\n", - gru->gs_gid, gts, gms, gts->ts_ctxnum, asid, - gms->ms_asidmap[0]); - return asid; -} - -static void gru_unload_mm_tracker(struct gru_state *gru, - struct gru_thread_state *gts) -{ - struct gru_mm_struct *gms = gts->ts_gms; - struct gru_mm_tracker *asids; - unsigned short ctxbitmap; - - asids = &gms->ms_asids[gru->gs_gid]; - ctxbitmap = (1 << gts->ts_ctxnum); - spin_lock(&gms->ms_asid_lock); - spin_lock(&gru->gs_asid_lock); - BUG_ON((asids->mt_ctxbitmap & ctxbitmap) != ctxbitmap); - asids->mt_ctxbitmap ^= ctxbitmap; - gru_dbg(grudev, "gid %d, gts %p, gms %p, ctxnum %d, asidmap 0x%lx\n", - gru->gs_gid, gts, gms, gts->ts_ctxnum, gms->ms_asidmap[0]); - spin_unlock(&gru->gs_asid_lock); - spin_unlock(&gms->ms_asid_lock); -} - -/* - * Decrement the reference count on a GTS structure. Free the structure - * if the reference count goes to zero. - */ -void gts_drop(struct gru_thread_state *gts) -{ - if (gts && refcount_dec_and_test(>s->ts_refcnt)) { - if (gts->ts_gms) - gru_drop_mmu_notifier(gts->ts_gms); - kfree(gts); - STAT(gts_free); - } -} - -/* - * Locate the GTS structure for the current thread. - */ -static struct gru_thread_state *gru_find_current_gts_nolock(struct gru_vma_data - *vdata, int tsid) -{ - struct gru_thread_state *gts; - - list_for_each_entry(gts, &vdata->vd_head, ts_next) - if (gts->ts_tsid == tsid) - return gts; - return NULL; -} - -/* - * Allocate a thread state structure. - */ -struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma, - int cbr_au_count, int dsr_au_count, - unsigned char tlb_preload_count, int options, int tsid) -{ - struct gru_thread_state *gts; - struct gru_mm_struct *gms; - int bytes; - - bytes = DSR_BYTES(dsr_au_count) + CBR_BYTES(cbr_au_count); - bytes += sizeof(struct gru_thread_state); - gts = kmalloc(bytes, GFP_KERNEL); - if (!gts) - return ERR_PTR(-ENOMEM); - - STAT(gts_alloc); - memset(gts, 0, sizeof(struct gru_thread_state)); /* zero out header */ - refcount_set(>s->ts_refcnt, 1); - mutex_init(>s->ts_ctxlock); - gts->ts_cbr_au_count = cbr_au_count; - gts->ts_dsr_au_count = dsr_au_count; - gts->ts_tlb_preload_count = tlb_preload_count; - gts->ts_user_options = options; - gts->ts_user_blade_id = -1; - gts->ts_user_chiplet_id = -1; - gts->ts_tsid = tsid; - gts->ts_ctxnum = NULLCTX; - gts->ts_tlb_int_select = -1; - gts->ts_cch_req_slice = -1; - gts->ts_sizeavail = GRU_SIZEAVAIL(PAGE_SHIFT); - if (vma) { - gts->ts_mm = current->mm; - gts->ts_vma = vma; - gms = gru_register_mmu_notifier(); - if (IS_ERR(gms)) - goto err; - gts->ts_gms = gms; - } - - gru_dbg(grudev, "alloc gts %p\n", gts); - return gts; - -err: - gts_drop(gts); - return ERR_CAST(gms); -} - -/* - * Allocate a vma private data structure. - */ -struct gru_vma_data *gru_alloc_vma_data(struct vm_area_struct *vma, int tsid) -{ - struct gru_vma_data *vdata = NULL; - - vdata = kmalloc_obj(*vdata); - if (!vdata) - return NULL; - - STAT(vdata_alloc); - INIT_LIST_HEAD(&vdata->vd_head); - spin_lock_init(&vdata->vd_lock); - gru_dbg(grudev, "alloc vdata %p\n", vdata); - return vdata; -} - -/* - * Find the thread state structure for the current thread. - */ -struct gru_thread_state *gru_find_thread_state(struct vm_area_struct *vma, - int tsid) -{ - struct gru_vma_data *vdata = vma->vm_private_data; - struct gru_thread_state *gts; - - spin_lock(&vdata->vd_lock); - gts = gru_find_current_gts_nolock(vdata, tsid); - spin_unlock(&vdata->vd_lock); - gru_dbg(grudev, "vma %p, gts %p\n", vma, gts); - return gts; -} - -/* - * Allocate a new thread state for a GSEG. Note that races may allow - * another thread to race to create a gts. - */ -struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct *vma, - int tsid) -{ - struct gru_vma_data *vdata = vma->vm_private_data; - struct gru_thread_state *gts, *ngts; - - gts = gru_alloc_gts(vma, vdata->vd_cbr_au_count, - vdata->vd_dsr_au_count, - vdata->vd_tlb_preload_count, - vdata->vd_user_options, tsid); - if (IS_ERR(gts)) - return gts; - - spin_lock(&vdata->vd_lock); - ngts = gru_find_current_gts_nolock(vdata, tsid); - if (ngts) { - gts_drop(gts); - gts = ngts; - STAT(gts_double_allocate); - } else { - list_add(>s->ts_next, &vdata->vd_head); - } - spin_unlock(&vdata->vd_lock); - gru_dbg(grudev, "vma %p, gts %p\n", vma, gts); - return gts; -} - -/* - * Free the GRU context assigned to the thread state. - */ -static void gru_free_gru_context(struct gru_thread_state *gts) -{ - struct gru_state *gru; - - gru = gts->ts_gru; - gru_dbg(grudev, "gts %p, gid %d\n", gts, gru->gs_gid); - - spin_lock(&gru->gs_lock); - gru->gs_gts[gts->ts_ctxnum] = NULL; - free_gru_resources(gru, gts); - BUG_ON(test_bit(gts->ts_ctxnum, &gru->gs_context_map) == 0); - __clear_bit(gts->ts_ctxnum, &gru->gs_context_map); - gts->ts_ctxnum = NULLCTX; - gts->ts_gru = NULL; - gts->ts_blade = -1; - spin_unlock(&gru->gs_lock); - - gts_drop(gts); - STAT(free_context); -} - -/* - * Prefetching cachelines help hardware performance. - * (Strictly a performance enhancement. Not functionally required). - */ -static void prefetch_data(void *p, int num, int stride) -{ - while (num-- > 0) { - prefetchw(p); - p += stride; - } -} - -static inline long gru_copy_handle(void *d, void *s) -{ - memcpy(d, s, GRU_HANDLE_BYTES); - return GRU_HANDLE_BYTES; -} - -static void gru_prefetch_context(void *gseg, void *cb, void *cbe, - unsigned long cbrmap, unsigned long length) -{ - int i, scr; - - prefetch_data(gseg + GRU_DS_BASE, length / GRU_CACHE_LINE_BYTES, - GRU_CACHE_LINE_BYTES); - - for_each_cbr_in_allocation_map(i, &cbrmap, scr) { - prefetch_data(cb, 1, GRU_CACHE_LINE_BYTES); - prefetch_data(cbe + i * GRU_HANDLE_STRIDE, 1, - GRU_CACHE_LINE_BYTES); - cb += GRU_HANDLE_STRIDE; - } -} - -static void gru_load_context_data(void *save, void *grubase, int ctxnum, - unsigned long cbrmap, unsigned long dsrmap, - int data_valid) -{ - void *gseg, *cb, *cbe; - unsigned long length; - int i, scr; - - gseg = grubase + ctxnum * GRU_GSEG_STRIDE; - cb = gseg + GRU_CB_BASE; - cbe = grubase + GRU_CBE_BASE; - length = hweight64(dsrmap) * GRU_DSR_AU_BYTES; - gru_prefetch_context(gseg, cb, cbe, cbrmap, length); - - for_each_cbr_in_allocation_map(i, &cbrmap, scr) { - if (data_valid) { - save += gru_copy_handle(cb, save); - save += gru_copy_handle(cbe + i * GRU_HANDLE_STRIDE, - save); - } else { - memset(cb, 0, GRU_CACHE_LINE_BYTES); - memset(cbe + i * GRU_HANDLE_STRIDE, 0, - GRU_CACHE_LINE_BYTES); - } - /* Flush CBE to hide race in context restart */ - mb(); - gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE); - cb += GRU_HANDLE_STRIDE; - } - - if (data_valid) - memcpy(gseg + GRU_DS_BASE, save, length); - else - memset(gseg + GRU_DS_BASE, 0, length); -} - -static void gru_unload_context_data(void *save, void *grubase, int ctxnum, - unsigned long cbrmap, unsigned long dsrmap) -{ - void *gseg, *cb, *cbe; - unsigned long length; - int i, scr; - - gseg = grubase + ctxnum * GRU_GSEG_STRIDE; - cb = gseg + GRU_CB_BASE; - cbe = grubase + GRU_CBE_BASE; - length = hweight64(dsrmap) * GRU_DSR_AU_BYTES; - - /* CBEs may not be coherent. Flush them from cache */ - for_each_cbr_in_allocation_map(i, &cbrmap, scr) - gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE); - mb(); /* Let the CL flush complete */ - - gru_prefetch_context(gseg, cb, cbe, cbrmap, length); - - for_each_cbr_in_allocation_map(i, &cbrmap, scr) { - save += gru_copy_handle(save, cb); - save += gru_copy_handle(save, cbe + i * GRU_HANDLE_STRIDE); - cb += GRU_HANDLE_STRIDE; - } - memcpy(save, gseg + GRU_DS_BASE, length); -} - -void gru_unload_context(struct gru_thread_state *gts, int savestate) -{ - struct gru_state *gru = gts->ts_gru; - struct gru_context_configuration_handle *cch; - int ctxnum = gts->ts_ctxnum; - - if (!is_kernel_context(gts)) - zap_special_vma_range(gts->ts_vma, UGRUADDR(gts), GRU_GSEG_PAGESIZE); - cch = get_cch(gru->gs_gru_base_vaddr, ctxnum); - - gru_dbg(grudev, "gts %p, cbrmap 0x%lx, dsrmap 0x%lx\n", - gts, gts->ts_cbr_map, gts->ts_dsr_map); - lock_cch_handle(cch); - if (cch_interrupt_sync(cch)) - BUG(); - - if (!is_kernel_context(gts)) - gru_unload_mm_tracker(gru, gts); - if (savestate) { - gru_unload_context_data(gts->ts_gdata, gru->gs_gru_base_vaddr, - ctxnum, gts->ts_cbr_map, - gts->ts_dsr_map); - gts->ts_data_valid = 1; - } - - if (cch_deallocate(cch)) - BUG(); - unlock_cch_handle(cch); - - gru_free_gru_context(gts); -} - -/* - * Load a GRU context by copying it from the thread data structure in memory - * to the GRU. - */ -void gru_load_context(struct gru_thread_state *gts) -{ - struct gru_state *gru = gts->ts_gru; - struct gru_context_configuration_handle *cch; - int i, err, asid, ctxnum = gts->ts_ctxnum; - - cch = get_cch(gru->gs_gru_base_vaddr, ctxnum); - lock_cch_handle(cch); - cch->tfm_fault_bit_enable = - (gts->ts_user_options == GRU_OPT_MISS_FMM_POLL - || gts->ts_user_options == GRU_OPT_MISS_FMM_INTR); - cch->tlb_int_enable = (gts->ts_user_options == GRU_OPT_MISS_FMM_INTR); - if (cch->tlb_int_enable) { - gts->ts_tlb_int_select = gru_cpu_fault_map_id(); - cch->tlb_int_select = gts->ts_tlb_int_select; - } - if (gts->ts_cch_req_slice >= 0) { - cch->req_slice_set_enable = 1; - cch->req_slice = gts->ts_cch_req_slice; - } else { - cch->req_slice_set_enable =0; - } - cch->tfm_done_bit_enable = 0; - cch->dsr_allocation_map = gts->ts_dsr_map; - cch->cbr_allocation_map = gts->ts_cbr_map; - - if (is_kernel_context(gts)) { - cch->unmap_enable = 1; - cch->tfm_done_bit_enable = 1; - cch->cb_int_enable = 1; - cch->tlb_int_select = 0; /* For now, ints go to cpu 0 */ - } else { - cch->unmap_enable = 0; - cch->tfm_done_bit_enable = 0; - cch->cb_int_enable = 0; - asid = gru_load_mm_tracker(gru, gts); - for (i = 0; i < 8; i++) { - cch->asid[i] = asid + i; - cch->sizeavail[i] = gts->ts_sizeavail; - } - } - - err = cch_allocate(cch); - if (err) { - gru_dbg(grudev, - "err %d: cch %p, gts %p, cbr 0x%lx, dsr 0x%lx\n", - err, cch, gts, gts->ts_cbr_map, gts->ts_dsr_map); - BUG(); - } - - gru_load_context_data(gts->ts_gdata, gru->gs_gru_base_vaddr, ctxnum, - gts->ts_cbr_map, gts->ts_dsr_map, gts->ts_data_valid); - - if (cch_start(cch)) - BUG(); - unlock_cch_handle(cch); - - gru_dbg(grudev, "gid %d, gts %p, cbrmap 0x%lx, dsrmap 0x%lx, tie %d, tis %d\n", - gts->ts_gru->gs_gid, gts, gts->ts_cbr_map, gts->ts_dsr_map, - (gts->ts_user_options == GRU_OPT_MISS_FMM_INTR), gts->ts_tlb_int_select); -} - -/* - * Update fields in an active CCH: - * - retarget interrupts on local blade - * - update sizeavail mask - */ -int gru_update_cch(struct gru_thread_state *gts) -{ - struct gru_context_configuration_handle *cch; - struct gru_state *gru = gts->ts_gru; - int i, ctxnum = gts->ts_ctxnum, ret = 0; - - cch = get_cch(gru->gs_gru_base_vaddr, ctxnum); - - lock_cch_handle(cch); - if (cch->state == CCHSTATE_ACTIVE) { - if (gru->gs_gts[gts->ts_ctxnum] != gts) - goto exit; - if (cch_interrupt(cch)) - BUG(); - for (i = 0; i < 8; i++) - cch->sizeavail[i] = gts->ts_sizeavail; - gts->ts_tlb_int_select = gru_cpu_fault_map_id(); - cch->tlb_int_select = gru_cpu_fault_map_id(); - cch->tfm_fault_bit_enable = - (gts->ts_user_options == GRU_OPT_MISS_FMM_POLL - || gts->ts_user_options == GRU_OPT_MISS_FMM_INTR); - if (cch_start(cch)) - BUG(); - ret = 1; - } -exit: - unlock_cch_handle(cch); - return ret; -} - -/* - * Update CCH tlb interrupt select. Required when all the following is true: - * - task's GRU context is loaded into a GRU - * - task is using interrupt notification for TLB faults - * - task has migrated to a different cpu on the same blade where - * it was previously running. - */ -static int gru_retarget_intr(struct gru_thread_state *gts) -{ - if (gts->ts_tlb_int_select < 0 - || gts->ts_tlb_int_select == gru_cpu_fault_map_id()) - return 0; - - gru_dbg(grudev, "retarget from %d to %d\n", gts->ts_tlb_int_select, - gru_cpu_fault_map_id()); - return gru_update_cch(gts); -} - -/* - * Check if a GRU context is allowed to use a specific chiplet. By default - * a context is assigned to any blade-local chiplet. However, users can - * override this. - * Returns 1 if assignment allowed, 0 otherwise - */ -static int gru_check_chiplet_assignment(struct gru_state *gru, - struct gru_thread_state *gts) -{ - int blade_id; - int chiplet_id; - - blade_id = gts->ts_user_blade_id; - if (blade_id < 0) - blade_id = uv_numa_blade_id(); - - chiplet_id = gts->ts_user_chiplet_id; - return gru->gs_blade_id == blade_id && - (chiplet_id < 0 || chiplet_id == gru->gs_chiplet_id); -} - -/* - * Unload the gru context if it is not assigned to the correct blade or - * chiplet. Misassignment can occur if the process migrates to a different - * blade or if the user changes the selected blade/chiplet. - */ -int gru_check_context_placement(struct gru_thread_state *gts) -{ - struct gru_state *gru; - int ret = 0; - - /* - * If the current task is the context owner, verify that the - * context is correctly placed. This test is skipped for non-owner - * references. Pthread apps use non-owner references to the CBRs. - */ - gru = gts->ts_gru; - /* - * If gru or gts->ts_tgid_owner isn't initialized properly, return - * success to indicate that the caller does not need to unload the - * gru context.The caller is responsible for their inspection and - * reinitialization if needed. - */ - if (!gru || gts->ts_tgid_owner != current->tgid) - return ret; - - if (!gru_check_chiplet_assignment(gru, gts)) { - STAT(check_context_unload); - ret = -EINVAL; - } else if (gru_retarget_intr(gts)) { - STAT(check_context_retarget_intr); - } - - return ret; -} - - -/* - * Insufficient GRU resources available on the local blade. Steal a context from - * a process. This is a hack until a _real_ resource scheduler is written.... - */ -#define next_ctxnum(n) ((n) < GRU_NUM_CCH - 2 ? (n) + 1 : 0) -#define next_gru(b, g) (((g) < &(b)->bs_grus[GRU_CHIPLETS_PER_BLADE - 1]) ? \ - ((g)+1) : &(b)->bs_grus[0]) - -static int is_gts_stealable(struct gru_thread_state *gts, - struct gru_blade_state *bs) -{ - if (is_kernel_context(gts)) - return down_write_trylock(&bs->bs_kgts_sema); - else - return mutex_trylock(>s->ts_ctxlock); -} - -static void gts_stolen(struct gru_thread_state *gts, - struct gru_blade_state *bs) -{ - if (is_kernel_context(gts)) { - up_write(&bs->bs_kgts_sema); - STAT(steal_kernel_context); - } else { - mutex_unlock(>s->ts_ctxlock); - STAT(steal_user_context); - } -} - -void gru_steal_context(struct gru_thread_state *gts) -{ - struct gru_blade_state *blade; - struct gru_state *gru, *gru0; - struct gru_thread_state *ngts = NULL; - int ctxnum, ctxnum0, flag = 0, cbr, dsr; - int blade_id; - - blade_id = gts->ts_user_blade_id; - if (blade_id < 0) - blade_id = uv_numa_blade_id(); - cbr = gts->ts_cbr_au_count; - dsr = gts->ts_dsr_au_count; - - blade = gru_base[blade_id]; - spin_lock(&blade->bs_lock); - - ctxnum = next_ctxnum(blade->bs_lru_ctxnum); - gru = blade->bs_lru_gru; - if (ctxnum == 0) - gru = next_gru(blade, gru); - blade->bs_lru_gru = gru; - blade->bs_lru_ctxnum = ctxnum; - ctxnum0 = ctxnum; - gru0 = gru; - while (1) { - if (gru_check_chiplet_assignment(gru, gts)) { - if (check_gru_resources(gru, cbr, dsr, GRU_NUM_CCH)) - break; - spin_lock(&gru->gs_lock); - for (; ctxnum < GRU_NUM_CCH; ctxnum++) { - if (flag && gru == gru0 && ctxnum == ctxnum0) - break; - ngts = gru->gs_gts[ctxnum]; - /* - * We are grabbing locks out of order, so trylock is - * needed. GTSs are usually not locked, so the odds of - * success are high. If trylock fails, try to steal a - * different GSEG. - */ - if (ngts && is_gts_stealable(ngts, blade)) - break; - ngts = NULL; - } - spin_unlock(&gru->gs_lock); - if (ngts || (flag && gru == gru0 && ctxnum == ctxnum0)) - break; - } - if (flag && gru == gru0) - break; - flag = 1; - ctxnum = 0; - gru = next_gru(blade, gru); - } - spin_unlock(&blade->bs_lock); - - if (ngts) { - gts->ustats.context_stolen++; - ngts->ts_steal_jiffies = jiffies; - gru_unload_context(ngts, is_kernel_context(ngts) ? 0 : 1); - gts_stolen(ngts, blade); - } else { - STAT(steal_context_failed); - } - gru_dbg(grudev, - "stole gid %d, ctxnum %d from gts %p. Need cb %d, ds %d;" - " avail cb %ld, ds %ld\n", - gru->gs_gid, ctxnum, ngts, cbr, dsr, hweight64(gru->gs_cbr_map), - hweight64(gru->gs_dsr_map)); -} - -/* - * Assign a gru context. - */ -static int gru_assign_context_number(struct gru_state *gru) -{ - int ctxnum; - - ctxnum = find_first_zero_bit(&gru->gs_context_map, GRU_NUM_CCH); - __set_bit(ctxnum, &gru->gs_context_map); - return ctxnum; -} - -/* - * Scan the GRUs on the local blade & assign a GRU context. - */ -struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts) -{ - struct gru_state *gru, *grux; - int i, max_active_contexts; - int blade_id = gts->ts_user_blade_id; - - if (blade_id < 0) - blade_id = uv_numa_blade_id(); -again: - gru = NULL; - max_active_contexts = GRU_NUM_CCH; - for_each_gru_on_blade(grux, blade_id, i) { - if (!gru_check_chiplet_assignment(grux, gts)) - continue; - if (check_gru_resources(grux, gts->ts_cbr_au_count, - gts->ts_dsr_au_count, - max_active_contexts)) { - gru = grux; - max_active_contexts = grux->gs_active_contexts; - if (max_active_contexts == 0) - break; - } - } - - if (gru) { - spin_lock(&gru->gs_lock); - if (!check_gru_resources(gru, gts->ts_cbr_au_count, - gts->ts_dsr_au_count, GRU_NUM_CCH)) { - spin_unlock(&gru->gs_lock); - goto again; - } - reserve_gru_resources(gru, gts); - gts->ts_gru = gru; - gts->ts_blade = gru->gs_blade_id; - gts->ts_ctxnum = gru_assign_context_number(gru); - refcount_inc(>s->ts_refcnt); - gru->gs_gts[gts->ts_ctxnum] = gts; - spin_unlock(&gru->gs_lock); - - STAT(assign_context); - gru_dbg(grudev, - "gseg %p, gts %p, gid %d, ctx %d, cbr %d, dsr %d\n", - gseg_virtual_address(gts->ts_gru, gts->ts_ctxnum), gts, - gts->ts_gru->gs_gid, gts->ts_ctxnum, - gts->ts_cbr_au_count, gts->ts_dsr_au_count); - } else { - gru_dbg(grudev, "failed to allocate a GTS %s\n", ""); - STAT(assign_context_failed); - } - - return gru; -} - -/* - * gru_nopage - * - * Map the user's GRU segment - * - * Note: gru segments alway mmaped on GRU_GSEG_PAGESIZE boundaries. - */ -vm_fault_t gru_fault(struct vm_fault *vmf) -{ - struct vm_area_struct *vma = vmf->vma; - struct gru_thread_state *gts; - unsigned long paddr, vaddr; - unsigned long expires; - - vaddr = vmf->address; - gru_dbg(grudev, "vma %p, vaddr 0x%lx (0x%lx)\n", - vma, vaddr, GSEG_BASE(vaddr)); - STAT(nopfn); - - /* The following check ensures vaddr is a valid address in the VMA */ - gts = gru_find_thread_state(vma, TSID(vaddr, vma)); - if (!gts) - return VM_FAULT_SIGBUS; - -again: - mutex_lock(>s->ts_ctxlock); - - if (gru_check_context_placement(gts)) { - mutex_unlock(>s->ts_ctxlock); - gru_unload_context(gts, 1); - return VM_FAULT_NOPAGE; - } - - if (!gts->ts_gru) { - STAT(load_user_context); - if (!gru_assign_gru_context(gts)) { - mutex_unlock(>s->ts_ctxlock); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(GRU_ASSIGN_DELAY); /* true hack ZZZ */ - expires = gts->ts_steal_jiffies + GRU_STEAL_DELAY; - if (time_before(expires, jiffies)) - gru_steal_context(gts); - goto again; - } - gru_load_context(gts); - paddr = gseg_physical_address(gts->ts_gru, gts->ts_ctxnum); - remap_pfn_range(vma, vaddr & ~(GRU_GSEG_PAGESIZE - 1), - paddr >> PAGE_SHIFT, GRU_GSEG_PAGESIZE, - vma->vm_page_prot); - } - - mutex_unlock(>s->ts_ctxlock); - - return VM_FAULT_NOPAGE; -} - diff --git a/drivers/misc/sgi-gru/gruprocfs.c b/drivers/misc/sgi-gru/gruprocfs.c deleted file mode 100644 index b8139c27bc7f..000000000000 --- a/drivers/misc/sgi-gru/gruprocfs.c +++ /dev/null @@ -1,307 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * PROC INTERFACES - * - * This file supports the /proc interfaces for the GRU driver - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include -#include -#include -#include -#include "gru.h" -#include "grulib.h" -#include "grutables.h" - -#define printstat(s, f) printstat_val(s, &gru_stats.f, #f) - -static void printstat_val(struct seq_file *s, atomic_long_t *v, char *id) -{ - unsigned long val = atomic_long_read(v); - - seq_printf(s, "%16lu %s\n", val, id); -} - -static int statistics_show(struct seq_file *s, void *p) -{ - printstat(s, vdata_alloc); - printstat(s, vdata_free); - printstat(s, gts_alloc); - printstat(s, gts_free); - printstat(s, gms_alloc); - printstat(s, gms_free); - printstat(s, gts_double_allocate); - printstat(s, assign_context); - printstat(s, assign_context_failed); - printstat(s, free_context); - printstat(s, load_user_context); - printstat(s, load_kernel_context); - printstat(s, lock_kernel_context); - printstat(s, unlock_kernel_context); - printstat(s, steal_user_context); - printstat(s, steal_kernel_context); - printstat(s, steal_context_failed); - printstat(s, nopfn); - printstat(s, asid_new); - printstat(s, asid_next); - printstat(s, asid_wrap); - printstat(s, asid_reuse); - printstat(s, intr); - printstat(s, intr_cbr); - printstat(s, intr_tfh); - printstat(s, intr_spurious); - printstat(s, call_os); - printstat(s, call_os_wait_queue); - printstat(s, user_flush_tlb); - printstat(s, user_unload_context); - printstat(s, user_exception); - printstat(s, set_context_option); - printstat(s, check_context_retarget_intr); - printstat(s, check_context_unload); - printstat(s, tlb_dropin); - printstat(s, tlb_preload_page); - printstat(s, tlb_dropin_fail_no_asid); - printstat(s, tlb_dropin_fail_upm); - printstat(s, tlb_dropin_fail_invalid); - printstat(s, tlb_dropin_fail_range_active); - printstat(s, tlb_dropin_fail_idle); - printstat(s, tlb_dropin_fail_fmm); - printstat(s, tlb_dropin_fail_no_exception); - printstat(s, tfh_stale_on_fault); - printstat(s, mmu_invalidate_range); - printstat(s, mmu_invalidate_page); - printstat(s, flush_tlb); - printstat(s, flush_tlb_gru); - printstat(s, flush_tlb_gru_tgh); - printstat(s, flush_tlb_gru_zero_asid); - printstat(s, copy_gpa); - printstat(s, read_gpa); - printstat(s, mesq_receive); - printstat(s, mesq_receive_none); - printstat(s, mesq_send); - printstat(s, mesq_send_failed); - printstat(s, mesq_noop); - printstat(s, mesq_send_unexpected_error); - printstat(s, mesq_send_lb_overflow); - printstat(s, mesq_send_qlimit_reached); - printstat(s, mesq_send_amo_nacked); - printstat(s, mesq_send_put_nacked); - printstat(s, mesq_qf_locked); - printstat(s, mesq_qf_noop_not_full); - printstat(s, mesq_qf_switch_head_failed); - printstat(s, mesq_qf_unexpected_error); - printstat(s, mesq_noop_unexpected_error); - printstat(s, mesq_noop_lb_overflow); - printstat(s, mesq_noop_qlimit_reached); - printstat(s, mesq_noop_amo_nacked); - printstat(s, mesq_noop_put_nacked); - printstat(s, mesq_noop_page_overflow); - return 0; -} - -static ssize_t statistics_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *data) -{ - memset(&gru_stats, 0, sizeof(gru_stats)); - return count; -} - -static int mcs_statistics_show(struct seq_file *s, void *p) -{ - int op; - unsigned long total, count, max; - static char *id[] = {"cch_allocate", "cch_start", "cch_interrupt", - "cch_interrupt_sync", "cch_deallocate", "tfh_write_only", - "tfh_write_restart", "tgh_invalidate"}; - - seq_puts(s, "#id count aver-clks max-clks\n"); - for (op = 0; op < mcsop_last; op++) { - count = atomic_long_read(&mcs_op_statistics[op].count); - total = atomic_long_read(&mcs_op_statistics[op].total); - max = mcs_op_statistics[op].max; - seq_printf(s, "%-20s%12ld%12ld%12ld\n", id[op], count, - count ? total / count : 0, max); - } - return 0; -} - -static ssize_t mcs_statistics_write(struct file *file, - const char __user *userbuf, size_t count, loff_t *data) -{ - memset(mcs_op_statistics, 0, sizeof(mcs_op_statistics)); - return count; -} - -static int options_show(struct seq_file *s, void *p) -{ - seq_printf(s, "#bitmask: 1=trace, 2=statistics\n"); - seq_printf(s, "0x%lx\n", gru_options); - return 0; -} - -static ssize_t options_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *data) -{ - int ret; - - ret = kstrtoul_from_user(userbuf, count, 0, &gru_options); - if (ret) - return ret; - - return count; -} - -static int cch_seq_show(struct seq_file *file, void *data) -{ - long gid = *(long *)data; - int i; - struct gru_state *gru = GID_TO_GRU(gid); - struct gru_thread_state *ts; - const char *mode[] = { "??", "UPM", "INTR", "OS_POLL" }; - - if (gid == 0) - seq_puts(file, "# gid bid ctx# asid pid cbrs dsbytes mode\n"); - if (gru) - for (i = 0; i < GRU_NUM_CCH; i++) { - ts = gru->gs_gts[i]; - if (!ts) - continue; - seq_printf(file, " %5d%5d%6d%7d%9d%6d%8d%8s\n", - gru->gs_gid, gru->gs_blade_id, i, - is_kernel_context(ts) ? 0 : ts->ts_gms->ms_asids[gid].mt_asid, - is_kernel_context(ts) ? 0 : ts->ts_tgid_owner, - ts->ts_cbr_au_count * GRU_CBR_AU_SIZE, - ts->ts_cbr_au_count * GRU_DSR_AU_BYTES, - mode[ts->ts_user_options & - GRU_OPT_MISS_MASK]); - } - - return 0; -} - -static int gru_seq_show(struct seq_file *file, void *data) -{ - long gid = *(long *)data, ctxfree, cbrfree, dsrfree; - struct gru_state *gru = GID_TO_GRU(gid); - - if (gid == 0) { - seq_puts(file, "# gid nid ctx cbr dsr ctx cbr dsr\n"); - seq_puts(file, "# busy busy busy free free free\n"); - } - if (gru) { - ctxfree = GRU_NUM_CCH - gru->gs_active_contexts; - cbrfree = hweight64(gru->gs_cbr_map) * GRU_CBR_AU_SIZE; - dsrfree = hweight64(gru->gs_dsr_map) * GRU_DSR_AU_BYTES; - seq_printf(file, " %5d%5d%7ld%6ld%6ld%8ld%6ld%6ld\n", - gru->gs_gid, gru->gs_blade_id, GRU_NUM_CCH - ctxfree, - GRU_NUM_CBE - cbrfree, GRU_NUM_DSR_BYTES - dsrfree, - ctxfree, cbrfree, dsrfree); - } - - return 0; -} - -static void seq_stop(struct seq_file *file, void *data) -{ -} - -static void *seq_start(struct seq_file *file, loff_t *gid) -{ - if (*gid < gru_max_gids) - return gid; - return NULL; -} - -static void *seq_next(struct seq_file *file, void *data, loff_t *gid) -{ - (*gid)++; - if (*gid < gru_max_gids) - return gid; - return NULL; -} - -static const struct seq_operations cch_seq_ops = { - .start = seq_start, - .next = seq_next, - .stop = seq_stop, - .show = cch_seq_show -}; - -static const struct seq_operations gru_seq_ops = { - .start = seq_start, - .next = seq_next, - .stop = seq_stop, - .show = gru_seq_show -}; - -static int statistics_open(struct inode *inode, struct file *file) -{ - return single_open(file, statistics_show, NULL); -} - -static int mcs_statistics_open(struct inode *inode, struct file *file) -{ - return single_open(file, mcs_statistics_show, NULL); -} - -static int options_open(struct inode *inode, struct file *file) -{ - return single_open(file, options_show, NULL); -} - -/* *INDENT-OFF* */ -static const struct proc_ops statistics_proc_ops = { - .proc_open = statistics_open, - .proc_read = seq_read, - .proc_write = statistics_write, - .proc_lseek = seq_lseek, - .proc_release = single_release, -}; - -static const struct proc_ops mcs_statistics_proc_ops = { - .proc_open = mcs_statistics_open, - .proc_read = seq_read, - .proc_write = mcs_statistics_write, - .proc_lseek = seq_lseek, - .proc_release = single_release, -}; - -static const struct proc_ops options_proc_ops = { - .proc_open = options_open, - .proc_read = seq_read, - .proc_write = options_write, - .proc_lseek = seq_lseek, - .proc_release = single_release, -}; - -static struct proc_dir_entry *proc_gru __read_mostly; - -int gru_proc_init(void) -{ - proc_gru = proc_mkdir("sgi_uv/gru", NULL); - if (!proc_gru) - return -1; - if (!proc_create("statistics", 0644, proc_gru, &statistics_proc_ops)) - goto err; - if (!proc_create("mcs_statistics", 0644, proc_gru, &mcs_statistics_proc_ops)) - goto err; - if (!proc_create("debug_options", 0644, proc_gru, &options_proc_ops)) - goto err; - if (!proc_create_seq("cch_status", 0444, proc_gru, &cch_seq_ops)) - goto err; - if (!proc_create_seq("gru_status", 0444, proc_gru, &gru_seq_ops)) - goto err; - return 0; -err: - remove_proc_subtree("sgi_uv/gru", NULL); - return -1; -} - -void gru_proc_exit(void) -{ - remove_proc_subtree("sgi_uv/gru", NULL); -} diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h deleted file mode 100644 index 3348552925c6..000000000000 --- a/drivers/misc/sgi-gru/grutables.h +++ /dev/null @@ -1,658 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * SN Platform GRU Driver - * - * GRU DRIVER TABLES, MACROS, externs, etc - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#ifndef __GRUTABLES_H__ -#define __GRUTABLES_H__ - -/* - * GRU Chiplet: - * The GRU is a user addressible memory accelerator. It provides - * several forms of load, store, memset, bcopy instructions. In addition, it - * contains special instructions for AMOs, sending messages to message - * queues, etc. - * - * The GRU is an integral part of the node controller. It connects - * directly to the cpu socket. In its current implementation, there are 2 - * GRU chiplets in the node controller on each blade (~node). - * - * The entire GRU memory space is fully coherent and cacheable by the cpus. - * - * Each GRU chiplet has a physical memory map that looks like the following: - * - * +-----------------+ - * |/////////////////| - * |/////////////////| - * |/////////////////| - * |/////////////////| - * |/////////////////| - * |/////////////////| - * |/////////////////| - * |/////////////////| - * +-----------------+ - * | system control | - * +-----------------+ _______ +-------------+ - * |/////////////////| / | | - * |/////////////////| / | | - * |/////////////////| / | instructions| - * |/////////////////| / | | - * |/////////////////| / | | - * |/////////////////| / |-------------| - * |/////////////////| / | | - * +-----------------+ | | - * | context 15 | | data | - * +-----------------+ | | - * | ...... | \ | | - * +-----------------+ \____________ +-------------+ - * | context 1 | - * +-----------------+ - * | context 0 | - * +-----------------+ - * - * Each of the "contexts" is a chunk of memory that can be mmaped into user - * space. The context consists of 2 parts: - * - * - an instruction space that can be directly accessed by the user - * to issue GRU instructions and to check instruction status. - * - * - a data area that acts as normal RAM. - * - * User instructions contain virtual addresses of data to be accessed by the - * GRU. The GRU contains a TLB that is used to convert these user virtual - * addresses to physical addresses. - * - * The "system control" area of the GRU chiplet is used by the kernel driver - * to manage user contexts and to perform functions such as TLB dropin and - * purging. - * - * One context may be reserved for the kernel and used for cross-partition - * communication. The GRU will also be used to asynchronously zero out - * large blocks of memory (not currently implemented). - * - * - * Tables: - * - * VDATA-VMA Data - Holds a few parameters. Head of linked list of - * GTS tables for threads using the GSEG - * GTS - Gru Thread State - contains info for managing a GSEG context. A - * GTS is allocated for each thread accessing a - * GSEG. - * GTD - GRU Thread Data - contains shadow copy of GRU data when GSEG is - * not loaded into a GRU - * GMS - GRU Memory Struct - Used to manage TLB shootdowns. Tracks GRUs - * where a GSEG has been loaded. Similar to - * an mm_struct but for GRU. - * - * GS - GRU State - Used to manage the state of a GRU chiplet - * BS - Blade State - Used to manage state of all GRU chiplets - * on a blade - * - * - * Normal task tables for task using GRU. - * - 2 threads in process - * - 2 GSEGs open in process - * - GSEG1 is being used by both threads - * - GSEG2 is used only by thread 2 - * - * task -->| - * task ---+---> mm ->------ (notifier) -------+-> gms - * | | - * |--> vma -> vdata ---> gts--->| GSEG1 (thread1) - * | | | - * | +-> gts--->| GSEG1 (thread2) - * | | - * |--> vma -> vdata ---> gts--->| GSEG2 (thread2) - * . - * . - * - * GSEGs are marked DONTCOPY on fork - * - * At open - * file.private_data -> NULL - * - * At mmap, - * vma -> vdata - * - * After gseg reference - * vma -> vdata ->gts - * - * After fork - * parent - * vma -> vdata -> gts - * child - * (vma is not copied) - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include "gru.h" -#include "grulib.h" -#include "gruhandles.h" - -extern struct gru_stats_s gru_stats; -extern struct gru_blade_state *gru_base[]; -extern unsigned long gru_start_paddr, gru_end_paddr; -extern void *gru_start_vaddr; -extern unsigned int gru_max_gids; - -#define GRU_MAX_BLADES MAX_NUMNODES -#define GRU_MAX_GRUS (GRU_MAX_BLADES * GRU_CHIPLETS_PER_BLADE) - -#define GRU_DRIVER_ID_STR "SGI GRU Device Driver" -#define GRU_DRIVER_VERSION_STR "0.85" - -/* - * GRU statistics. - */ -struct gru_stats_s { - atomic_long_t vdata_alloc; - atomic_long_t vdata_free; - atomic_long_t gts_alloc; - atomic_long_t gts_free; - atomic_long_t gms_alloc; - atomic_long_t gms_free; - atomic_long_t gts_double_allocate; - atomic_long_t assign_context; - atomic_long_t assign_context_failed; - atomic_long_t free_context; - atomic_long_t load_user_context; - atomic_long_t load_kernel_context; - atomic_long_t lock_kernel_context; - atomic_long_t unlock_kernel_context; - atomic_long_t steal_user_context; - atomic_long_t steal_kernel_context; - atomic_long_t steal_context_failed; - atomic_long_t nopfn; - atomic_long_t asid_new; - atomic_long_t asid_next; - atomic_long_t asid_wrap; - atomic_long_t asid_reuse; - atomic_long_t intr; - atomic_long_t intr_cbr; - atomic_long_t intr_tfh; - atomic_long_t intr_spurious; - atomic_long_t call_os; - atomic_long_t call_os_wait_queue; - atomic_long_t user_flush_tlb; - atomic_long_t user_unload_context; - atomic_long_t user_exception; - atomic_long_t set_context_option; - atomic_long_t check_context_retarget_intr; - atomic_long_t check_context_unload; - atomic_long_t tlb_dropin; - atomic_long_t tlb_preload_page; - atomic_long_t tlb_dropin_fail_no_asid; - atomic_long_t tlb_dropin_fail_upm; - atomic_long_t tlb_dropin_fail_invalid; - atomic_long_t tlb_dropin_fail_range_active; - atomic_long_t tlb_dropin_fail_idle; - atomic_long_t tlb_dropin_fail_fmm; - atomic_long_t tlb_dropin_fail_no_exception; - atomic_long_t tfh_stale_on_fault; - atomic_long_t mmu_invalidate_range; - atomic_long_t mmu_invalidate_page; - atomic_long_t flush_tlb; - atomic_long_t flush_tlb_gru; - atomic_long_t flush_tlb_gru_tgh; - atomic_long_t flush_tlb_gru_zero_asid; - - atomic_long_t copy_gpa; - atomic_long_t read_gpa; - - atomic_long_t mesq_receive; - atomic_long_t mesq_receive_none; - atomic_long_t mesq_send; - atomic_long_t mesq_send_failed; - atomic_long_t mesq_noop; - atomic_long_t mesq_send_unexpected_error; - atomic_long_t mesq_send_lb_overflow; - atomic_long_t mesq_send_qlimit_reached; - atomic_long_t mesq_send_amo_nacked; - atomic_long_t mesq_send_put_nacked; - atomic_long_t mesq_page_overflow; - atomic_long_t mesq_qf_locked; - atomic_long_t mesq_qf_noop_not_full; - atomic_long_t mesq_qf_switch_head_failed; - atomic_long_t mesq_qf_unexpected_error; - atomic_long_t mesq_noop_unexpected_error; - atomic_long_t mesq_noop_lb_overflow; - atomic_long_t mesq_noop_qlimit_reached; - atomic_long_t mesq_noop_amo_nacked; - atomic_long_t mesq_noop_put_nacked; - atomic_long_t mesq_noop_page_overflow; - -}; - -enum mcs_op {cchop_allocate, cchop_start, cchop_interrupt, cchop_interrupt_sync, - cchop_deallocate, tfhop_write_only, tfhop_write_restart, - tghop_invalidate, mcsop_last}; - -struct mcs_op_statistic { - atomic_long_t count; - atomic_long_t total; - unsigned long max; -}; - -extern struct mcs_op_statistic mcs_op_statistics[mcsop_last]; - -#define OPT_DPRINT 1 -#define OPT_STATS 2 - - -#define IRQ_GRU 110 /* Starting IRQ number for interrupts */ - -/* Delay in jiffies between attempts to assign a GRU context */ -#define GRU_ASSIGN_DELAY ((HZ * 20) / 1000) - -/* - * If a process has it's context stolen, min delay in jiffies before trying to - * steal a context from another process. - */ -#define GRU_STEAL_DELAY ((HZ * 200) / 1000) - -#define STAT(id) do { \ - if (gru_options & OPT_STATS) \ - atomic_long_inc(&gru_stats.id); \ - } while (0) - -#ifdef CONFIG_SGI_GRU_DEBUG -#define gru_dbg(dev, fmt, x...) \ - do { \ - if (gru_options & OPT_DPRINT) \ - printk(KERN_DEBUG "GRU:%d %s: " fmt, smp_processor_id(), __func__, x);\ - } while (0) -#else -#define gru_dbg(x...) -#endif - -/*----------------------------------------------------------------------------- - * ASID management - */ -#define MAX_ASID 0xfffff0 -#define MIN_ASID 8 -#define ASID_INC 8 /* number of regions */ - -/* Generate a GRU asid value from a GRU base asid & a virtual address. */ -#define VADDR_HI_BIT 64 -#define GRUREGION(addr) ((addr) >> (VADDR_HI_BIT - 3) & 3) -#define GRUASID(asid, addr) ((asid) + GRUREGION(addr)) - -/*------------------------------------------------------------------------------ - * File & VMS Tables - */ - -struct gru_state; - -/* - * This structure is pointed to from the mmstruct via the notifier pointer. - * There is one of these per address space. - */ -struct gru_mm_tracker { /* pack to reduce size */ - unsigned int mt_asid_gen:24; /* ASID wrap count */ - unsigned int mt_asid:24; /* current base ASID for gru */ - unsigned short mt_ctxbitmap:16;/* bitmap of contexts using - asid */ -} __attribute__ ((packed)); - -struct gru_mm_struct { - struct mmu_notifier ms_notifier; - spinlock_t ms_asid_lock; /* protects ASID assignment */ - atomic_t ms_range_active;/* num range_invals active */ - wait_queue_head_t ms_wait_queue; - DECLARE_BITMAP(ms_asidmap, GRU_MAX_GRUS); - struct gru_mm_tracker ms_asids[GRU_MAX_GRUS]; -}; - -/* - * One of these structures is allocated when a GSEG is mmaped. The - * structure is pointed to by the vma->vm_private_data field in the vma struct. - */ -struct gru_vma_data { - spinlock_t vd_lock; /* Serialize access to vma */ - struct list_head vd_head; /* head of linked list of gts */ - long vd_user_options;/* misc user option flags */ - int vd_cbr_au_count; - int vd_dsr_au_count; - unsigned char vd_tlb_preload_count; -}; - -/* - * One of these is allocated for each thread accessing a mmaped GRU. A linked - * list of these structure is hung off the struct gru_vma_data in the mm_struct. - */ -struct gru_thread_state { - struct list_head ts_next; /* list - head at vma-private */ - struct mutex ts_ctxlock; /* load/unload CTX lock */ - struct mm_struct *ts_mm; /* mm currently mapped to - context */ - struct vm_area_struct *ts_vma; /* vma of GRU context */ - struct gru_state *ts_gru; /* GRU where the context is - loaded */ - struct gru_mm_struct *ts_gms; /* asid & ioproc struct */ - unsigned char ts_tlb_preload_count; /* TLB preload pages */ - unsigned long ts_cbr_map; /* map of allocated CBRs */ - unsigned long ts_dsr_map; /* map of allocated DATA - resources */ - unsigned long ts_steal_jiffies;/* jiffies when context last - stolen */ - long ts_user_options;/* misc user option flags */ - pid_t ts_tgid_owner; /* task that is using the - context - for migration */ - short ts_user_blade_id;/* user selected blade */ - signed char ts_user_chiplet_id;/* user selected chiplet */ - unsigned short ts_sizeavail; /* Pagesizes in use */ - int ts_tsid; /* thread that owns the - structure */ - int ts_tlb_int_select;/* target cpu if interrupts - enabled */ - int ts_ctxnum; /* context number where the - context is loaded */ - refcount_t ts_refcnt; /* reference count GTS */ - unsigned char ts_dsr_au_count;/* Number of DSR resources - required for contest */ - unsigned char ts_cbr_au_count;/* Number of CBR resources - required for contest */ - signed char ts_cch_req_slice;/* CCH packet slice */ - signed char ts_blade; /* If >= 0, migrate context if - ref from different blade */ - signed char ts_force_cch_reload; - signed char ts_cbr_idx[GRU_CBR_AU];/* CBR numbers of each - allocated CB */ - int ts_data_valid; /* Indicates if ts_gdata has - valid data */ - struct gru_gseg_statistics ustats; /* User statistics */ - unsigned long ts_gdata[]; /* save area for GRU data (CB, - DS, CBE) */ -}; - -/* - * Threaded programs actually allocate an array of GSEGs when a context is - * created. Each thread uses a separate GSEG. TSID is the index into the GSEG - * array. - */ -#define TSID(a, v) (((a) - (v)->vm_start) / GRU_GSEG_PAGESIZE) -#define UGRUADDR(gts) ((gts)->ts_vma->vm_start + \ - (gts)->ts_tsid * GRU_GSEG_PAGESIZE) - -#define NULLCTX (-1) /* if context not loaded into GRU */ - -/*----------------------------------------------------------------------------- - * GRU State Tables - */ - -/* - * One of these exists for each GRU chiplet. - */ -struct gru_state { - struct gru_blade_state *gs_blade; /* GRU state for entire - blade */ - unsigned long gs_gru_base_paddr; /* Physical address of - gru segments (64) */ - void *gs_gru_base_vaddr; /* Virtual address of - gru segments (64) */ - unsigned short gs_gid; /* unique GRU number */ - unsigned short gs_blade_id; /* blade of GRU */ - unsigned char gs_chiplet_id; /* blade chiplet of GRU */ - unsigned char gs_tgh_local_shift; /* used to pick TGH for - local flush */ - unsigned char gs_tgh_first_remote; /* starting TGH# for - remote flush */ - spinlock_t gs_asid_lock; /* lock used for - assigning asids */ - spinlock_t gs_lock; /* lock used for - assigning contexts */ - - /* -- the following are protected by the gs_asid_lock spinlock ---- */ - unsigned int gs_asid; /* Next availe ASID */ - unsigned int gs_asid_limit; /* Limit of available - ASIDs */ - unsigned int gs_asid_gen; /* asid generation. - Inc on wrap */ - - /* --- the following fields are protected by the gs_lock spinlock --- */ - unsigned long gs_context_map; /* bitmap to manage - contexts in use */ - unsigned long gs_cbr_map; /* bitmap to manage CB - resources */ - unsigned long gs_dsr_map; /* bitmap used to manage - DATA resources */ - unsigned int gs_reserved_cbrs; /* Number of kernel- - reserved cbrs */ - unsigned int gs_reserved_dsr_bytes; /* Bytes of kernel- - reserved dsrs */ - unsigned short gs_active_contexts; /* number of contexts - in use */ - struct gru_thread_state *gs_gts[GRU_NUM_CCH]; /* GTS currently using - the context */ - int gs_irq[GRU_NUM_TFM]; /* Interrupt irqs */ -}; - -/* - * This structure contains the GRU state for all the GRUs on a blade. - */ -struct gru_blade_state { - void *kernel_cb; /* First kernel - reserved cb */ - void *kernel_dsr; /* First kernel - reserved DSR */ - struct rw_semaphore bs_kgts_sema; /* lock for kgts */ - struct gru_thread_state *bs_kgts; /* GTS for kernel use */ - - /* ---- the following are used for managing kernel async GRU CBRs --- */ - int bs_async_dsr_bytes; /* DSRs for async */ - int bs_async_cbrs; /* CBRs AU for async */ - struct completion *bs_async_wq; - - /* ---- the following are protected by the bs_lock spinlock ---- */ - spinlock_t bs_lock; /* lock used for - stealing contexts */ - int bs_lru_ctxnum; /* STEAL - last context - stolen */ - struct gru_state *bs_lru_gru; /* STEAL - last gru - stolen */ - - struct gru_state bs_grus[GRU_CHIPLETS_PER_BLADE]; -}; - -/*----------------------------------------------------------------------------- - * Address Primitives - */ -#define get_tfm_for_cpu(g, c) \ - ((struct gru_tlb_fault_map *)get_tfm((g)->gs_gru_base_vaddr, (c))) -#define get_tfh_by_index(g, i) \ - ((struct gru_tlb_fault_handle *)get_tfh((g)->gs_gru_base_vaddr, (i))) -#define get_tgh_by_index(g, i) \ - ((struct gru_tlb_global_handle *)get_tgh((g)->gs_gru_base_vaddr, (i))) -#define get_cbe_by_index(g, i) \ - ((struct gru_control_block_extended *)get_cbe((g)->gs_gru_base_vaddr,\ - (i))) - -/*----------------------------------------------------------------------------- - * Useful Macros - */ - -/* Given a blade# & chiplet#, get a pointer to the GRU */ -#define get_gru(b, c) (&gru_base[b]->bs_grus[c]) - -/* Number of bytes to save/restore when unloading/loading GRU contexts */ -#define DSR_BYTES(dsr) ((dsr) * GRU_DSR_AU_BYTES) -#define CBR_BYTES(cbr) ((cbr) * GRU_HANDLE_BYTES * GRU_CBR_AU_SIZE * 2) - -/* Convert a user CB number to the actual CBRNUM */ -#define thread_cbr_number(gts, n) ((gts)->ts_cbr_idx[(n) / GRU_CBR_AU_SIZE] \ - * GRU_CBR_AU_SIZE + (n) % GRU_CBR_AU_SIZE) - -/* Convert a gid to a pointer to the GRU */ -#define GID_TO_GRU(gid) \ - (gru_base[(gid) / GRU_CHIPLETS_PER_BLADE] ? \ - (&gru_base[(gid) / GRU_CHIPLETS_PER_BLADE]-> \ - bs_grus[(gid) % GRU_CHIPLETS_PER_BLADE]) : \ - NULL) - -/* Scan all active GRUs in a GRU bitmap */ -#define for_each_gru_in_bitmap(gid, map) \ - for_each_set_bit((gid), (map), GRU_MAX_GRUS) - -/* Scan all active GRUs on a specific blade */ -#define for_each_gru_on_blade(gru, nid, i) \ - for ((gru) = gru_base[nid]->bs_grus, (i) = 0; \ - (i) < GRU_CHIPLETS_PER_BLADE; \ - (i)++, (gru)++) - -/* Scan all GRUs */ -#define foreach_gid(gid) \ - for ((gid) = 0; (gid) < gru_max_gids; (gid)++) - -/* Scan all active GTSs on a gru. Note: must hold ss_lock to use this macro. */ -#define for_each_gts_on_gru(gts, gru, ctxnum) \ - for ((ctxnum) = 0; (ctxnum) < GRU_NUM_CCH; (ctxnum)++) \ - if (((gts) = (gru)->gs_gts[ctxnum])) - -/* Scan each CBR whose bit is set in a TFM (or copy of) */ -#define for_each_cbr_in_tfm(i, map) \ - for_each_set_bit((i), (map), GRU_NUM_CBE) - -/* Scan each CBR in a CBR bitmap. Note: multiple CBRs in an allocation unit */ -#define for_each_cbr_in_allocation_map(i, map, k) \ - for_each_set_bit((k), (map), GRU_CBR_AU) \ - for ((i) = (k)*GRU_CBR_AU_SIZE; \ - (i) < ((k) + 1) * GRU_CBR_AU_SIZE; (i)++) - -#define gseg_physical_address(gru, ctxnum) \ - ((gru)->gs_gru_base_paddr + ctxnum * GRU_GSEG_STRIDE) -#define gseg_virtual_address(gru, ctxnum) \ - ((gru)->gs_gru_base_vaddr + ctxnum * GRU_GSEG_STRIDE) - -/*----------------------------------------------------------------------------- - * Lock / Unlock GRU handles - * Use the "delresp" bit in the handle as a "lock" bit. - */ - -/* Lock hierarchy checking enabled only in emulator */ - -/* 0 = lock failed, 1 = locked */ -static inline int __trylock_handle(void *h) -{ - return !test_and_set_bit(1, h); -} - -static inline void __lock_handle(void *h) -{ - while (test_and_set_bit(1, h)) - cpu_relax(); -} - -static inline void __unlock_handle(void *h) -{ - clear_bit(1, h); -} - -static inline int trylock_cch_handle(struct gru_context_configuration_handle *cch) -{ - return __trylock_handle(cch); -} - -static inline void lock_cch_handle(struct gru_context_configuration_handle *cch) -{ - __lock_handle(cch); -} - -static inline void unlock_cch_handle(struct gru_context_configuration_handle - *cch) -{ - __unlock_handle(cch); -} - -static inline void lock_tgh_handle(struct gru_tlb_global_handle *tgh) -{ - __lock_handle(tgh); -} - -static inline void unlock_tgh_handle(struct gru_tlb_global_handle *tgh) -{ - __unlock_handle(tgh); -} - -static inline int is_kernel_context(struct gru_thread_state *gts) -{ - return !gts->ts_mm; -} - -/* - * The following are for Nehelem-EX. A more general scheme is needed for - * future processors. - */ -#define UV_MAX_INT_CORES 8 -#define uv_cpu_socket_number(p) ((cpu_physical_id(p) >> 5) & 1) -#define uv_cpu_ht_number(p) (cpu_physical_id(p) & 1) -#define uv_cpu_core_number(p) (((cpu_physical_id(p) >> 2) & 4) | \ - ((cpu_physical_id(p) >> 1) & 3)) -/*----------------------------------------------------------------------------- - * Function prototypes & externs - */ -struct gru_unload_context_req; - -extern const struct vm_operations_struct gru_vm_ops; -extern struct device *grudev; - -extern struct gru_vma_data *gru_alloc_vma_data(struct vm_area_struct *vma, - int tsid); -extern struct gru_thread_state *gru_find_thread_state(struct vm_area_struct - *vma, int tsid); -extern struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct - *vma, int tsid); -extern struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts); -extern void gru_load_context(struct gru_thread_state *gts); -extern void gru_steal_context(struct gru_thread_state *gts); -extern void gru_unload_context(struct gru_thread_state *gts, int savestate); -extern int gru_update_cch(struct gru_thread_state *gts); -extern void gts_drop(struct gru_thread_state *gts); -extern void gru_tgh_flush_init(struct gru_state *gru); -extern int gru_kservices_init(void); -extern void gru_kservices_exit(void); -extern irqreturn_t gru0_intr(int irq, void *dev_id); -extern irqreturn_t gru1_intr(int irq, void *dev_id); -extern irqreturn_t gru_intr_mblade(int irq, void *dev_id); -extern int gru_dump_chiplet_request(unsigned long arg); -extern long gru_get_gseg_statistics(unsigned long arg); -extern int gru_handle_user_call_os(unsigned long address); -extern int gru_user_flush_tlb(unsigned long arg); -extern int gru_user_unload_context(unsigned long arg); -extern int gru_get_exception_detail(unsigned long arg); -extern int gru_set_context_option(unsigned long address); -extern int gru_check_context_placement(struct gru_thread_state *gts); -extern int gru_cpu_fault_map_id(void); -extern struct vm_area_struct *gru_find_vma(unsigned long vaddr); -extern void gru_flush_all_tlb(struct gru_state *gru); -extern int gru_proc_init(void); -extern void gru_proc_exit(void); - -extern struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma, - int cbr_au_count, int dsr_au_count, - unsigned char tlb_preload_count, int options, int tsid); -extern unsigned long gru_reserve_cb_resources(struct gru_state *gru, - int cbr_au_count, signed char *cbmap); -extern unsigned long gru_reserve_ds_resources(struct gru_state *gru, - int dsr_au_count, signed char *dsmap); -extern vm_fault_t gru_fault(struct vm_fault *vmf); -extern struct gru_mm_struct *gru_register_mmu_notifier(void); -extern void gru_drop_mmu_notifier(struct gru_mm_struct *gms); - -extern int gru_ktest(unsigned long arg); -extern void gru_flush_tlb_range(struct gru_mm_struct *gms, unsigned long start, - unsigned long len); - -extern unsigned long gru_options; - -#endif /* __GRUTABLES_H__ */ diff --git a/drivers/misc/sgi-gru/grutlbpurge.c b/drivers/misc/sgi-gru/grutlbpurge.c deleted file mode 100644 index a8121d40be68..000000000000 --- a/drivers/misc/sgi-gru/grutlbpurge.c +++ /dev/null @@ -1,316 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SN Platform GRU Driver - * - * MMUOPS callbacks + TLB flushing - * - * This file handles emu notifier callbacks from the core kernel. The callbacks - * are used to update the TLB in the GRU as a result of changes in the - * state of a process address space. This file also handles TLB invalidates - * from the GRU driver. - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "gru.h" -#include "grutables.h" -#include - -#define gru_random() get_cycles() - -/* ---------------------------------- TLB Invalidation functions -------- - * get_tgh_handle - * - * Find a TGH to use for issuing a TLB invalidate. For GRUs that are on the - * local blade, use a fixed TGH that is a function of the blade-local cpu - * number. Normally, this TGH is private to the cpu & no contention occurs for - * the TGH. For offblade GRUs, select a random TGH in the range above the - * private TGHs. A spinlock is required to access this TGH & the lock must be - * released when the invalidate is completes. This sucks, but it is the best we - * can do. - * - * Note that the spinlock is IN the TGH handle so locking does not involve - * additional cache lines. - * - */ -static inline int get_off_blade_tgh(struct gru_state *gru) -{ - int n; - - n = GRU_NUM_TGH - gru->gs_tgh_first_remote; - n = gru_random() % n; - n += gru->gs_tgh_first_remote; - return n; -} - -static inline int get_on_blade_tgh(struct gru_state *gru) -{ - return uv_blade_processor_id() >> gru->gs_tgh_local_shift; -} - -static struct gru_tlb_global_handle *get_lock_tgh_handle(struct gru_state - *gru) -{ - struct gru_tlb_global_handle *tgh; - int n; - - if (uv_numa_blade_id() == gru->gs_blade_id) - n = get_on_blade_tgh(gru); - else - n = get_off_blade_tgh(gru); - tgh = get_tgh_by_index(gru, n); - lock_tgh_handle(tgh); - - return tgh; -} - -static void get_unlock_tgh_handle(struct gru_tlb_global_handle *tgh) -{ - unlock_tgh_handle(tgh); -} - -/* - * gru_flush_tlb_range - * - * General purpose TLB invalidation function. This function scans every GRU in - * the ENTIRE system (partition) looking for GRUs where the specified MM has - * been accessed by the GRU. For each GRU found, the TLB must be invalidated OR - * the ASID invalidated. Invalidating an ASID causes a new ASID to be assigned - * on the next fault. This effectively flushes the ENTIRE TLB for the MM at the - * cost of (possibly) a large number of future TLBmisses. - * - * The current algorithm is optimized based on the following (somewhat true) - * assumptions: - * - GRU contexts are not loaded into a GRU unless a reference is made to - * the data segment or control block (this is true, not an assumption). - * If a DS/CB is referenced, the user will also issue instructions that - * cause TLBmisses. It is not necessary to optimize for the case where - * contexts are loaded but no instructions cause TLB misses. (I know - * this will happen but I'm not optimizing for it). - * - GRU instructions to invalidate TLB entries are SLOOOOWWW - normally - * a few usec but in unusual cases, it could be longer. Avoid if - * possible. - * - intrablade process migration between cpus is not frequent but is - * common. - * - a GRU context is not typically migrated to a different GRU on the - * blade because of intrablade migration - * - interblade migration is rare. Processes migrate their GRU context to - * the new blade. - * - if interblade migration occurs, migration back to the original blade - * is very very rare (ie., no optimization for this case) - * - most GRU instruction operate on a subset of the user REGIONS. Code - * & shared library regions are not likely targets of GRU instructions. - * - * To help improve the efficiency of TLB invalidation, the GMS data - * structure is maintained for EACH address space (MM struct). The GMS is - * also the structure that contains the pointer to the mmu callout - * functions. This structure is linked to the mm_struct for the address space - * using the mmu "register" function. The mmu interfaces are used to - * provide the callbacks for TLB invalidation. The GMS contains: - * - * - asid[maxgrus] array. ASIDs are assigned to a GRU when a context is - * loaded into the GRU. - * - asidmap[maxgrus]. bitmap to make it easier to find non-zero asids in - * the above array - * - ctxbitmap[maxgrus]. Indicates the contexts that are currently active - * in the GRU for the address space. This bitmap must be passed to the - * GRU to do an invalidate. - * - * The current algorithm for invalidating TLBs is: - * - scan the asidmap for GRUs where the context has been loaded, ie, - * asid is non-zero. - * - for each gru found: - * - if the ctxtmap is non-zero, there are active contexts in the - * GRU. TLB invalidate instructions must be issued to the GRU. - * - if the ctxtmap is zero, no context is active. Set the ASID to - * zero to force a full TLB invalidation. This is fast but will - * cause a lot of TLB misses if the context is reloaded onto the - * GRU - * - */ - -void gru_flush_tlb_range(struct gru_mm_struct *gms, unsigned long start, - unsigned long len) -{ - struct gru_state *gru; - struct gru_mm_tracker *asids; - struct gru_tlb_global_handle *tgh; - unsigned long num; - int grupagesize, pagesize, pageshift, gid, asid; - - /* ZZZ TODO - handle huge pages */ - pageshift = PAGE_SHIFT; - pagesize = (1UL << pageshift); - grupagesize = GRU_PAGESIZE(pageshift); - num = min(((len + pagesize - 1) >> pageshift), GRUMAXINVAL); - - STAT(flush_tlb); - gru_dbg(grudev, "gms %p, start 0x%lx, len 0x%lx, asidmap 0x%lx\n", gms, - start, len, gms->ms_asidmap[0]); - - spin_lock(&gms->ms_asid_lock); - for_each_gru_in_bitmap(gid, gms->ms_asidmap) { - STAT(flush_tlb_gru); - gru = GID_TO_GRU(gid); - asids = gms->ms_asids + gid; - asid = asids->mt_asid; - if (asids->mt_ctxbitmap && asid) { - STAT(flush_tlb_gru_tgh); - asid = GRUASID(asid, start); - gru_dbg(grudev, - " FLUSH gruid %d, asid 0x%x, vaddr 0x%lx, vamask 0x%x, num %ld, cbmap 0x%x\n", - gid, asid, start, grupagesize, num, asids->mt_ctxbitmap); - tgh = get_lock_tgh_handle(gru); - tgh_invalidate(tgh, start, ~0, asid, grupagesize, 0, - num - 1, asids->mt_ctxbitmap); - get_unlock_tgh_handle(tgh); - } else { - STAT(flush_tlb_gru_zero_asid); - asids->mt_asid = 0; - __clear_bit(gru->gs_gid, gms->ms_asidmap); - gru_dbg(grudev, - " CLEARASID gruid %d, asid 0x%x, cbtmap 0x%x, asidmap 0x%lx\n", - gid, asid, asids->mt_ctxbitmap, - gms->ms_asidmap[0]); - } - } - spin_unlock(&gms->ms_asid_lock); -} - -/* - * Flush the entire TLB on a chiplet. - */ -void gru_flush_all_tlb(struct gru_state *gru) -{ - struct gru_tlb_global_handle *tgh; - - gru_dbg(grudev, "gid %d\n", gru->gs_gid); - tgh = get_lock_tgh_handle(gru); - tgh_invalidate(tgh, 0, ~0, 0, 1, 1, GRUMAXINVAL - 1, 0xffff); - get_unlock_tgh_handle(tgh); -} - -/* - * MMUOPS notifier callout functions - */ -static int gru_invalidate_range_start(struct mmu_notifier *mn, - const struct mmu_notifier_range *range) -{ - struct gru_mm_struct *gms = container_of(mn, struct gru_mm_struct, - ms_notifier); - - STAT(mmu_invalidate_range); - atomic_inc(&gms->ms_range_active); - gru_dbg(grudev, "gms %p, start 0x%lx, end 0x%lx, act %d\n", gms, - range->start, range->end, atomic_read(&gms->ms_range_active)); - gru_flush_tlb_range(gms, range->start, range->end - range->start); - - return 0; -} - -static void gru_invalidate_range_end(struct mmu_notifier *mn, - const struct mmu_notifier_range *range) -{ - struct gru_mm_struct *gms = container_of(mn, struct gru_mm_struct, - ms_notifier); - - /* ..._and_test() provides needed barrier */ - (void)atomic_dec_and_test(&gms->ms_range_active); - - wake_up_all(&gms->ms_wait_queue); - gru_dbg(grudev, "gms %p, start 0x%lx, end 0x%lx\n", - gms, range->start, range->end); -} - -static struct mmu_notifier *gru_alloc_notifier(struct mm_struct *mm) -{ - struct gru_mm_struct *gms; - - gms = kzalloc_obj(*gms); - if (!gms) - return ERR_PTR(-ENOMEM); - STAT(gms_alloc); - spin_lock_init(&gms->ms_asid_lock); - init_waitqueue_head(&gms->ms_wait_queue); - - return &gms->ms_notifier; -} - -static void gru_free_notifier(struct mmu_notifier *mn) -{ - kfree(container_of(mn, struct gru_mm_struct, ms_notifier)); - STAT(gms_free); -} - -static const struct mmu_notifier_ops gru_mmuops = { - .invalidate_range_start = gru_invalidate_range_start, - .invalidate_range_end = gru_invalidate_range_end, - .alloc_notifier = gru_alloc_notifier, - .free_notifier = gru_free_notifier, -}; - -struct gru_mm_struct *gru_register_mmu_notifier(void) -{ - struct mmu_notifier *mn; - - mn = mmu_notifier_get_locked(&gru_mmuops, current->mm); - if (IS_ERR(mn)) - return ERR_CAST(mn); - - return container_of(mn, struct gru_mm_struct, ms_notifier); -} - -void gru_drop_mmu_notifier(struct gru_mm_struct *gms) -{ - mmu_notifier_put(&gms->ms_notifier); -} - -/* - * Setup TGH parameters. There are: - * - 24 TGH handles per GRU chiplet - * - a portion (MAX_LOCAL_TGH) of the handles are reserved for - * use by blade-local cpus - * - the rest are used by off-blade cpus. This usage is - * less frequent than blade-local usage. - * - * For now, use 16 handles for local flushes, 8 for remote flushes. If the blade - * has less tan or equal to 16 cpus, each cpu has a unique handle that it can - * use. - */ -#define MAX_LOCAL_TGH 16 - -void gru_tgh_flush_init(struct gru_state *gru) -{ - int cpus, shift = 0, n; - - cpus = uv_blade_nr_possible_cpus(gru->gs_blade_id); - - /* n = cpus rounded up to next power of 2 */ - if (cpus) { - n = 1 << fls(cpus - 1); - - /* - * shift count for converting local cpu# to TGH index - * 0 if cpus <= MAX_LOCAL_TGH, - * 1 if cpus <= 2*MAX_LOCAL_TGH, - * etc - */ - shift = max(0, fls(n - 1) - fls(MAX_LOCAL_TGH - 1)); - } - gru->gs_tgh_local_shift = shift; - - /* first starting TGH index to use for remote purges */ - gru->gs_tgh_first_remote = (cpus + (1 << shift) - 1) >> shift; - -} From 41fb4e925528aefa4b7a5f76c7f81db99c0d0f38 Mon Sep 17 00:00:00 2001 From: Kuan-Wei Chiu Date: Tue, 2 Dec 2025 08:26:13 +0000 Subject: [PATCH 479/513] coresight: etm3x: Fix cntr_val_show() to match cntr_val_store() behavior The cntr_val_show() function was intended to print the values of all counters using a loop. However, due to a buffer overwrite issue with sprintf(), it effectively only displayed the value of the last counter. The companion function, cntr_val_store(), allows users to modify a specific counter selected by 'cntr_idx'. To maintain consistency between read and write operations and to align with the ETM4x driver behavior, modify cntr_val_show() to report only the value of the currently selected counter. This change removes the loop and the "counter %d:" prefix, printing only the hexadecimal value. It also adopts sysfs_emit() for standard sysfs output formatting. Fixes: a939fc5a71ad ("coresight-etm: add CoreSight ETM/PTM driver") Cc: stable@vger.kernel.org Signed-off-by: Kuan-Wei Chiu Reviewed-by: James Clark Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20251202082613.3265761-1-visitorckw@gmail.com --- .../hwtracing/coresight/coresight-etm3x-sysfs.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 762109307b86..b3c67e96a82a 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -717,26 +717,19 @@ static DEVICE_ATTR_RW(cntr_rld_event); static ssize_t cntr_val_show(struct device *dev, struct device_attribute *attr, char *buf) { - int i, ret = 0; u32 val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etm_config *config = &drvdata->config; if (!coresight_get_mode(drvdata->csdev)) { spin_lock(&drvdata->spinlock); - for (i = 0; i < drvdata->nr_cntr; i++) - ret += sprintf(buf, "counter %d: %x\n", - i, config->cntr_val[i]); + val = config->cntr_val[config->cntr_idx]; spin_unlock(&drvdata->spinlock); - return ret; + } else { + val = etm_readl(drvdata, ETMCNTVRn(config->cntr_idx)); } - for (i = 0; i < drvdata->nr_cntr; i++) { - val = etm_readl(drvdata, ETMCNTVRn(i)); - ret += sprintf(buf, "counter %d: %x\n", i, val); - } - - return ret; + return sysfs_emit(buf, "%#x\n", val); } static ssize_t cntr_val_store(struct device *dev, From f67379bcf6c8440acdb00eb347b3e2cc9dd84a89 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Thu, 16 Apr 2026 10:42:19 +0100 Subject: [PATCH 480/513] MAINTAINERS: Update HiSilicon PCI Trace and Tune maintainer Yicong left Huawei a while back so drop both of our entries and add one for Sizhe Liu who will be looking after this code going forwards. Signed-off-by: Jonathan Cameron Acked-by: Sizhe Liu Acked-by: Jie Zhan Acked-by: Yicong Yang Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260416094219.25258-1-Jonathan.Cameron@huawei.com --- MAINTAINERS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 4a8b0fd665ce..de3e2b80e849 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11757,8 +11757,7 @@ F: drivers/perf/hisilicon F: tools/perf/pmu-events/arch/arm64/hisilicon/ HISILICON PTT DRIVER -M: Yicong Yang -M: Jonathan Cameron +M: Sizhe Liu L: linux-kernel@vger.kernel.org S: Maintained F: Documentation/ABI/testing/sysfs-bus-event_source-devices-hisi_ptt From 75d42d990335322852ed5f7ce324b701c0949d79 Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Tue, 14 Apr 2026 17:25:12 +0000 Subject: [PATCH 481/513] hwtracing: hisi_ptt: Propagate DMA reset timeout in trace_start() hisi_ptt_wait_dma_reset_done() discards the return value of readl_poll_timeout_atomic(). If the DMA engine does not complete its reset within the timeout, hisi_ptt_trace_start() proceeds to start tracing regardless. Return a bool from hisi_ptt_wait_dma_reset_done(), consistent with the other wait helpers in this driver. On timeout, log an error, de-assert the reset bit, and return -ETIMEDOUT. Move ctrl->started to the successful path so a failed start does not leave the trace marked as active. Fixes: ff0de066b463 ("hwtracing: hisi_ptt: Add trace function support for HiSilicon PCIe Tune and Trace device") Cc: stable@vger.kernel.org Signed-off-by: Sanman Pradhan Reviewed-by: Sizhe Liu Reviewed-by: Yicong Yang Tested-by: Sizhe Liu Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260414172451.14331-2-sanman.pradhan@hpe.com --- drivers/hwtracing/ptt/hisi_ptt.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c index 94c371c49135..b5d851281fbf 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.c +++ b/drivers/hwtracing/ptt/hisi_ptt.c @@ -171,13 +171,13 @@ static bool hisi_ptt_wait_trace_hw_idle(struct hisi_ptt *hisi_ptt) HISI_PTT_WAIT_TRACE_TIMEOUT_US); } -static void hisi_ptt_wait_dma_reset_done(struct hisi_ptt *hisi_ptt) +static bool hisi_ptt_wait_dma_reset_done(struct hisi_ptt *hisi_ptt) { u32 val; - readl_poll_timeout_atomic(hisi_ptt->iobase + HISI_PTT_TRACE_WR_STS, - val, !val, HISI_PTT_RESET_POLL_INTERVAL_US, - HISI_PTT_RESET_TIMEOUT_US); + return !readl_poll_timeout_atomic(hisi_ptt->iobase + HISI_PTT_TRACE_WR_STS, + val, !val, HISI_PTT_RESET_POLL_INTERVAL_US, + HISI_PTT_RESET_TIMEOUT_US); } static void hisi_ptt_trace_end(struct hisi_ptt *hisi_ptt) @@ -202,14 +202,18 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt) return -EBUSY; } - ctrl->started = true; - /* Reset the DMA before start tracing */ val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); val |= HISI_PTT_TRACE_CTRL_RST; writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); - hisi_ptt_wait_dma_reset_done(hisi_ptt); + if (!hisi_ptt_wait_dma_reset_done(hisi_ptt)) { + pci_err(hisi_ptt->pdev, "timed out waiting for DMA reset\n"); + val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); + val &= ~HISI_PTT_TRACE_CTRL_RST; + writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); + return -ETIMEDOUT; + } val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); val &= ~HISI_PTT_TRACE_CTRL_RST; @@ -234,6 +238,8 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt) if (!hisi_ptt->trace_ctrl.is_port) val |= HISI_PTT_TRACE_CTRL_FILTER_MODE; + ctrl->started = true; + /* Start the Trace */ val |= HISI_PTT_TRACE_CTRL_EN; writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); From 856119909d8de99d540edbdfb5431efab936f649 Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Tue, 14 Apr 2026 17:25:22 +0000 Subject: [PATCH 482/513] hwtracing: hisi_ptt: Remove unnecessary trace buffer zeroing in trace_start() hisi_ptt_trace_start() clears all four trace buffers before enabling tracing. This is unnecessary. On trace stop, hisi_ptt_update_aux() copies only the number of bytes reported in HISI_PTT_TRACE_WR_STS. On buffer-full interrupts, it copies a full completed buffer. In both cases the driver only consumes data written by hardware. Remove the buffer clearing from the trace start path. Signed-off-by: Sanman Pradhan Reviewed-by: Yicong Yang Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260414172451.14331-3-sanman.pradhan@hpe.com --- drivers/hwtracing/ptt/hisi_ptt.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c index b5d851281fbf..a8f6986c8e1f 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.c +++ b/drivers/hwtracing/ptt/hisi_ptt.c @@ -194,7 +194,6 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt) { struct hisi_ptt_trace_ctrl *ctrl = &hisi_ptt->trace_ctrl; u32 val; - int i; /* Check device idle before start trace */ if (!hisi_ptt_wait_trace_hw_idle(hisi_ptt)) { @@ -222,10 +221,6 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt) /* Reset the index of current buffer */ hisi_ptt->trace_ctrl.buf_index = 0; - /* Zero the trace buffers */ - for (i = 0; i < HISI_PTT_TRACE_BUF_CNT; i++) - memset(ctrl->trace_buf[i].addr, 0, HISI_PTT_TRACE_BUF_SIZE); - /* Clear the interrupt status */ writel(HISI_PTT_TRACE_INT_STAT_MASK, hisi_ptt->iobase + HISI_PTT_TRACE_INT_STAT); writel(0, hisi_ptt->iobase + HISI_PTT_TRACE_INT_MASK); From 0e1cd4270b42a257c139165622091e1e8c7104a7 Mon Sep 17 00:00:00 2001 From: Yeoreum Yun Date: Sat, 25 Jul 2026 12:36:33 +0100 Subject: [PATCH 483/513] coresight: etm4x: fix wrong check of etm4x_sspcicrn_present() According to Embedded Trace Macrocell Architecture Specification ETMv4.0 to ETM4.6 [0], TRCSSPCICR is present only if all of the following are true: - TRCIDR4.NUMSSCC > n. - TRCIDR4.NUMPC > 0b0000. - TRCSSCSR.PC == 0b1. Comment for etm4x_sspcicrn_present() is align with the specification. However, the check should use drvdata->nr_pe_cmp to check TRCIDR4.NUMPC not nr_pe. Link: https://developer.arm.com/documentation/ihi0064/latest/ [0] Fixes: f6a18f354c58 ("coresight: etm4x: Handle access to TRCSSPCICRn") Reviewed-by: Leo Yan Signed-off-by: Yeoreum Yun Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260725113645.57519-2-yeoreum.yun@arm.com --- drivers/hwtracing/coresight/coresight-etm4x-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 14bb31bd6a0b..1e3b0344dc00 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -93,7 +93,7 @@ static int etm4_probe_cpu(unsigned int cpu); static bool etm4x_sspcicrn_present(struct etmv4_drvdata *drvdata, int n) { return (n < drvdata->nr_ss_cmp) && - drvdata->nr_pe && + drvdata->nr_pe_cmp && (drvdata->config.ss_status[n] & TRCSSCSRn_PC); } From 1674d9bff8073bdee5dbc200f56fc3caa28d0566 Mon Sep 17 00:00:00 2001 From: Yeoreum Yun Date: Sat, 25 Jul 2026 12:36:34 +0100 Subject: [PATCH 484/513] coresight: etm4x: fix underflow for usage of (nrseqstate - 1) According to IHI006H Embedded Trace Macrocell Architecture Specification[0], TRCSEQEVR is implemented only when TRCIDR5.NUMSEQSTATE is 0b100, in which case n ranges from 0 to 2; otherwise, TRCIDR5.NUMSEQSTATE is 0b000. IOW, the number of usage in the initialisation or setting TRCSEQEVR with drvdata->nrseqstate - 1 in the loop could make underflow issue when TRCIDR5.NUMSEQSTATE is 0b000. Therefore, introduce nr_seq_ctrls field and untie it from nrseqstate. As part of this introduce ETM_MAX_SEQ_TRANSITIONS macro and apply nr_seq_ctrls and above macro to TRCSEQEVR relevant fields setup. Link: https://developer.arm.com/documentation/ihi0064/latest/ [0] Fixes: 2e1cdfe184b5 ("coresight-etm4x: Adding CoreSight ETM4x driver") Suggested-by: Leo Yan Suggested-by: Suzuki K Poulose Signed-off-by: Yeoreum Yun Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260725113645.57519-3-yeoreum.yun@arm.com --- drivers/hwtracing/coresight/coresight-etm4x-cfg.c | 2 +- drivers/hwtracing/coresight/coresight-etm4x-core.c | 9 ++++++--- drivers/hwtracing/coresight/coresight-etm4x-sysfs.c | 6 ++++-- drivers/hwtracing/coresight/coresight-etm4x.h | 7 +++++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c index c302072b293a..e1a59b434505 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -76,7 +76,7 @@ static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata, } else if ((offset & GENMASK(11, 4)) == TRCSEQEVRn(0)) { /* sequencer state control registers */ idx = (offset & GENMASK(3, 0)) / 4; - if (idx < ETM_MAX_SEQ_STATES) { + if (idx < ETM_MAX_SEQ_TRANSITIONS) { reg_csdev->driver_regval = &drvcfg->seq_ctrl[idx]; err = 0; } diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 1e3b0344dc00..1884960cfe6f 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -542,7 +542,8 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata) etm4x_relaxed_write32(csa, config->vissctlr, TRCVISSCTLR); if (drvdata->nr_pe_cmp) etm4x_relaxed_write32(csa, config->vipcssctlr, TRCVIPCSSCTLR); - for (i = 0; i < drvdata->nrseqstate - 1; i++) + + for (i = 0; i < drvdata->nr_seq_ctrls; i++) etm4x_relaxed_write32(csa, config->seq_ctrl[i], TRCSEQEVRn(i)); if (drvdata->nrseqstate) { etm4x_relaxed_write32(csa, config->seq_rst, TRCSEQRSTEVR); @@ -1508,6 +1509,8 @@ static void etm4_init_arch_data(void *info) drvdata->lpoverride = (etmidr5 & TRCIDR5_LPOVERRIDE) && (!drvdata->skip_power_up); /* NUMSEQSTATE, bits[27:25] number of sequencer states implemented */ drvdata->nrseqstate = FIELD_GET(TRCIDR5_NUMSEQSTATE_MASK, etmidr5); + if (drvdata->nrseqstate) + drvdata->nr_seq_ctrls = ETM_MAX_SEQ_TRANSITIONS; /* NUMCNTR, bits[30:28] number of counters available for tracing */ drvdata->nr_cntr = FIELD_GET(TRCIDR5_NUMCNTR_MASK, etmidr5); @@ -1896,7 +1899,7 @@ static int etm4_cpu_save(struct coresight_device *csdev) if (drvdata->nr_pe_cmp) state->trcvipcssctlr = etm4x_read32(csa, TRCVIPCSSCTLR); - for (i = 0; i < drvdata->nrseqstate - 1; i++) + for (i = 0; i < drvdata->nr_seq_ctrls; i++) state->trcseqevr[i] = etm4x_read32(csa, TRCSEQEVRn(i)); if (drvdata->nrseqstate) { @@ -2009,7 +2012,7 @@ static void etm4_cpu_restore(struct coresight_device *csdev) if (drvdata->nr_pe_cmp) etm4x_relaxed_write32(csa, state->trcvipcssctlr, TRCVIPCSSCTLR); - for (i = 0; i < drvdata->nrseqstate - 1; i++) + for (i = 0; i < drvdata->nr_seq_ctrls; i++) etm4x_relaxed_write32(csa, state->trcseqevr[i], TRCSEQEVRn(i)); if (drvdata->nrseqstate) { diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index e9eeea6240d5..cc6cdd3ae29d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -223,7 +223,7 @@ static ssize_t reset_store(struct device *dev, config->vipcssctlr = 0x0; /* Disable seq events */ - for (i = 0; i < drvdata->nrseqstate-1; i++) + for (i = 0; i < drvdata->nr_seq_ctrls; i++) config->seq_ctrl[i] = 0x0; config->seq_rst = 0x0; config->seq_state = 0x0; @@ -1395,9 +1395,11 @@ static ssize_t seq_idx_store(struct device *dev, struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etmv4_config *config = &drvdata->config; + if (!drvdata->nr_seq_ctrls) + return -ENOTSUPP; if (kstrtoul(buf, 16, &val)) return -EINVAL; - if (val >= drvdata->nrseqstate - 1) + if (val >= drvdata->nr_seq_ctrls) return -EINVAL; /* diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 89d81ce4e04e..84db8b97c98a 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -614,6 +614,7 @@ static inline u32 etm4_res_sel_pair(u8 res_sel_idx) #define ETM_MAX_NR_PE 8 #define ETMv4_MAX_CNTR 4 #define ETM_MAX_SEQ_STATES 4 +#define ETM_MAX_SEQ_TRANSITIONS 3 #define ETM_MAX_EXT_INP_SEL 4 #define ETM_MAX_EXT_INP 256 #define ETM_MAX_EXT_OUT 4 @@ -877,7 +878,7 @@ struct etmv4_config { u32 vipcssctlr; u8 seq_idx; u8 syncfreq; - u32 seq_ctrl[ETM_MAX_SEQ_STATES]; + u32 seq_ctrl[ETM_MAX_SEQ_TRANSITIONS]; u32 seq_rst; u32 seq_state; u8 cntr_idx; @@ -928,7 +929,7 @@ struct etmv4_save_state { u32 trcvissctlr; u32 trcvipcssctlr; - u32 trcseqevr[ETM_MAX_SEQ_STATES]; + u32 trcseqevr[ETM_MAX_SEQ_TRANSITIONS]; u32 trcseqrstevr; u32 trcseqstr; u32 trcextinselr; @@ -981,6 +982,7 @@ struct etmv4_save_state { * @numcidc: Number of contextID comparators. * @numvmidc: Number of VMID comparators. * @nrseqstate: The number of sequencer states that are implemented. + * @nr_seq_ctrls: The number of sequence state transition control registers. * @nr_event: Indicates how many events the trace unit support. * @nr_resource:The number of resource selection pairs available for tracing. * @nr_ss_cmp: Number of single-shot comparator controls that are available. @@ -1046,6 +1048,7 @@ struct etmv4_drvdata { u8 numextinsel; u8 numvmidc; u8 nrseqstate; + u8 nr_seq_ctrls; u8 nr_event; u8 nr_resource; u8 nr_ss_cmp; From 467e5862ccb0eed907002f4c6d3badfe34360940 Mon Sep 17 00:00:00 2001 From: Yeoreum Yun Date: Sat, 25 Jul 2026 12:36:35 +0100 Subject: [PATCH 485/513] coresight: etm4x: fix leaked trace id If etm4_enable_sysfs() fails in cscfg_csdev_enable_active_config(), the trace ID may be leaked because it is not released. To address this, call etm4_release_trace_id() when etm4_enable_sysfs() fails in cscfg_csdev_enable_active_config(). Fixes: 7ebd0ec6cf94 ("coresight: configfs: Allow configfs to activate configuration") Reviewed-by: Jie Gan Reviewed-by: Leo Yan Signed-off-by: Yeoreum Yun Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260725113645.57519-4-yeoreum.yun@arm.com --- drivers/hwtracing/coresight/coresight-etm4x-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 1884960cfe6f..8363c55b3952 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -920,8 +920,10 @@ static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_pa cscfg_config_sysfs_get_active_cfg(&cfg_hash, &preset); if (cfg_hash) { ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset); - if (ret) + if (ret) { + etm4_release_trace_id(drvdata); return ret; + } } raw_spin_lock(&drvdata->spinlock); From 0a47f0be6557b4a851addd430383a4dc7ee08752 Mon Sep 17 00:00:00 2001 From: Yeoreum Yun Date: Sat, 25 Jul 2026 12:36:37 +0100 Subject: [PATCH 486/513] coresight: etm4x: missing cscfg_csdev_disable_active_config() in perf enable In the perf enable path, there are missing cases where cscfg_csdev_disable_active_config() is not called: - Branch broadcast is selected but not supported by the hardware - etm4_enable_hw() fails This can lead to a leak of config_desc->active_cnt. Fix this by properly calling cscfg_csdev_disable_active_config() in these error paths. Fixes: 810ac401db1f ("coresight: etm4x: Add complex configuration handlers to etmv4") Suggested-by: Leo Yan Signed-off-by: Yeoreum Yun Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260725113645.57519-6-yeoreum.yun@arm.com --- .../coresight/coresight-etm4x-core.c | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 8363c55b3952..2247ad55d444 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -757,8 +757,7 @@ static int etm4_parse_event_config(struct coresight_device *csdev, .ATTR_CFG_FLD_timestamp_CFG = U64_MAX, }; struct perf_event_attr *attr = &event->attr; - unsigned long cfg_hash; - int preset, cc_threshold; + int cc_threshold; u8 ts_level; /* Clear configuration from previous run */ @@ -844,16 +843,6 @@ static int etm4_parse_event_config(struct coresight_device *csdev, /* bit[12], Return stack enable bit */ config->cfg |= TRCCONFIGR_RS; - /* - * Set any selected configuration and preset. A zero configid means no - * configuration active, preset = 0 means no preset selected. - */ - cfg_hash = ATTR_CFG_GET_FLD(attr, configid); - if (cfg_hash) { - preset = ATTR_CFG_GET_FLD(attr, preset); - ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset); - } - /* branch broadcast - enable if selected and supported */ if (ATTR_CFG_GET_FLD(attr, branch_broadcast)) { if (!drvdata->trcbb) { @@ -877,7 +866,9 @@ static int etm4_enable_perf(struct coresight_device *csdev, struct coresight_path *path) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - int ret; + struct perf_event_attr *attr = &event->attr; + unsigned long cfg_hash; + int ret, preset; if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) return -EINVAL; @@ -888,7 +879,19 @@ static int etm4_enable_perf(struct coresight_device *csdev, /* Configure the tracer based on the session's specifics */ ret = etm4_parse_event_config(csdev, event); if (ret) - goto out; + goto err; + + /* + * Set any selected configuration and preset. A zero configid means no + * configuration active, preset = 0 means no preset selected. + */ + cfg_hash = ATTR_CFG_GET_FLD(attr, configid); + if (cfg_hash) { + preset = ATTR_CFG_GET_FLD(attr, preset); + ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset); + if (ret) + goto err; + } drvdata->trcid = path->trace_id; @@ -897,16 +900,19 @@ static int etm4_enable_perf(struct coresight_device *csdev, /* And enable it */ ret = etm4_enable_hw(drvdata); - -out: - /* Failed to start tracer; roll back to DISABLED mode */ if (ret) { - coresight_set_mode(csdev, CS_MODE_DISABLED); - return ret; + if (cfg_hash) + cscfg_csdev_disable_active_config(csdev); + goto err; } csdev->path = path; return 0; + +err: + /* Failed to start tracer; roll back to DISABLED mode */ + coresight_set_mode(csdev, CS_MODE_DISABLED); + return ret; } static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_path *path) From 9e3604d7369cfc0110100eb1a0acab1865ee2d18 Mon Sep 17 00:00:00 2001 From: Yeoreum Yun Date: Sat, 25 Jul 2026 12:36:44 +0100 Subject: [PATCH 487/513] coresight: etm4x: remove redundant fields in etmv4_save_state Some of fields are redundant in etmv4_save_state and never used: ss_status => trcsscsr seq_state => trcseqstr cntr_val => trccntvr vinst_ctrl => trcvictlr Reviewed-by: Leo Yan Signed-off-by: Yeoreum Yun Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260725113645.57519-13-yeoreum.yun@arm.com --- drivers/hwtracing/coresight/coresight-etm4x.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 84db8b97c98a..df9e0748d71e 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -954,11 +954,6 @@ struct etmv4_save_state { u32 trcclaimset; - u32 cntr_val[ETMv4_MAX_CNTR]; - u32 seq_state; - u32 vinst_ctrl; - u32 ss_status[ETM_MAX_SS_CMP]; - u32 trcpdcr; }; From bcb721c1bcb02ab225b3937bf131a0bc6fc1fecd Mon Sep 17 00:00:00 2001 From: Paul Geurts Date: Mon, 6 Jul 2026 09:48:03 +0200 Subject: [PATCH 488/513] iio: ti-ads7138: Disable STATS_EN bit while reading conversion results There is a data race in reading the STATS registers, resulting in wrong data being read. When the data in the RECENT register switches between 0x24F0 and 0x2500, occasionally value 0x2400 or 0x25F0 is read. This happens when the value is updated in between reading MSB and LSB. The data sheet says: "Until a new conversion result is available, previous values can be read from the statistics registers. Before reading the statistics registers, set STATS_EN to 0 to prevent any updates to this register block." As the STATS_EN is currently not cleared, the values of the stats registers might change mid read, giving faulty values. Disable the STATS_EN bit before reading one of the statistics registers to make sure the device does not update the register mid read. This is applicable to registers MAX_CHn_xSB, MIN_CHn_xSB and RECENT_CHn_xSB. This means reading one of the statistics registers resets the MAX and MIN registers. This is unfortunate, but necessary to get correct data from the device. Signed-off-by: Paul Geurts Fixes: 024b08fee342 ("iio: adc: Add driver for ADS7128 / ADS7138") Reviewed-by: David Lechner Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7138.c | 42 ++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/ti-ads7138.c b/drivers/iio/adc/ti-ads7138.c index af87f5f19a0f..da82a6947b2b 100644 --- a/drivers/iio/adc/ti-ads7138.c +++ b/drivers/iio/adc/ti-ads7138.c @@ -227,6 +227,26 @@ static int ads7138_osr_to_bits(int osr) return -EINVAL; } +static int ads7138_read_statistics(const struct i2c_client *client, u8 reg, + u8 *out_values, u8 length) +{ + int ret; + + /* Disable statistics update so the value is not updated mid read */ + ret = ads7138_i2c_clear_bit(client, ADS7138_REG_GENERAL_CFG, + ADS7138_GENERAL_CFG_STATS_EN); + if (ret) + return ret; + + ret = ads7138_i2c_read_block(client, reg, out_values, length); + if (ret) + return ret; + + /* Enable statistics update after read */ + return ads7138_i2c_set_bit(client, ADS7138_REG_GENERAL_CFG, + ADS7138_GENERAL_CFG_STATS_EN); +} + static int ads7138_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -236,28 +256,32 @@ static int ads7138_read_raw(struct iio_dev *indio_dev, u8 values[2]; switch (mask) { + /* + * Reading the statistics registers reinitializes them. This is + * unfortunate but necessary to prevent data races. + */ case IIO_CHAN_INFO_RAW: - ret = ads7138_i2c_read_block(data->client, - ADS7138_REG_RECENT_LSB_CH(chan->channel), - values, ARRAY_SIZE(values)); + ret = ads7138_read_statistics(data->client, + ADS7138_REG_RECENT_LSB_CH(chan->channel), + values, ARRAY_SIZE(values)); if (ret) return ret; *val = get_unaligned_le16(values); return IIO_VAL_INT; case IIO_CHAN_INFO_PEAK: - ret = ads7138_i2c_read_block(data->client, - ADS7138_REG_MAX_LSB_CH(chan->channel), - values, ARRAY_SIZE(values)); + ret = ads7138_read_statistics(data->client, + ADS7138_REG_MAX_LSB_CH(chan->channel), + values, ARRAY_SIZE(values)); if (ret) return ret; *val = get_unaligned_le16(values); return IIO_VAL_INT; case IIO_CHAN_INFO_TROUGH: - ret = ads7138_i2c_read_block(data->client, - ADS7138_REG_MIN_LSB_CH(chan->channel), - values, ARRAY_SIZE(values)); + ret = ads7138_read_statistics(data->client, + ADS7138_REG_MIN_LSB_CH(chan->channel), + values, ARRAY_SIZE(values)); if (ret) return ret; From 739aac87638f06fcf851df41ecd52d30ab7b0570 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Wed, 8 Jul 2026 21:50:28 +0200 Subject: [PATCH 489/513] iio: dac: mcp47feb02: add missing 'select REGMAP_I2C' to Kconfig The Kconfig entry for the MCP47FEB02 is missing a 'select REGMAP_I2C', causing build failures. Fixes: bf394cc80369 ("iio: dac: adding support for Microchip MCP47FEB02") Cc: stable@vger.kernel.org Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 657c68e75542..14a246729d2b 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -552,6 +552,7 @@ config MCP4728 config MCP47FEB02 tristate "MCP47F(E/V)B01/02/04/08/11/12/14/18/21/22/24/28 DAC driver" depends on I2C + select REGMAP_I2C help Say yes here if you want to build the driver for the Microchip: - 8-bit DAC: From f2c5c76306fadb834dd5ea76cab0b7cd447e6035 Mon Sep 17 00:00:00 2001 From: Babanpreet Singh Date: Sat, 18 Jul 2026 18:22:36 +0000 Subject: [PATCH 490/513] iio: dac: ad3552r-hs: fix scnprintf() buffer bound in data source show ad3552r_hs_show_data_source_avail() formats the available data source names into a 128-byte stack buffer, but bounds each scnprintf() with PAGE_SIZE instead of the buffer size, so the bound does not protect the destination at all. This cannot overflow today - dbgfs_attr_source[] has two entries, "normal" and "ramp-16bit", 18 bytes formatted - but the bound stops protecting the stack the day the table grows. Use sizeof(buf) so the bound matches the destination. Found by smatch: drivers/iio/dac/ad3552r-hs.c:593 ad3552r_hs_show_data_source_avail() error: scnprintf() 'buf[len]' too small (128 vs 4096) Fixes: b1c5d68ea66e ("iio: dac: ad3552r-hs: add support for internal ramp") Assisted-by: Claude:claude-sonnet-5 Signed-off-by: Babanpreet Singh Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad3552r-hs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad3552r-hs.c b/drivers/iio/dac/ad3552r-hs.c index 6bc64f53bce9..5d6c2517a7a8 100644 --- a/drivers/iio/dac/ad3552r-hs.c +++ b/drivers/iio/dac/ad3552r-hs.c @@ -591,7 +591,7 @@ static ssize_t ad3552r_hs_show_data_source_avail(struct file *f, int i; for (i = 0; i < ARRAY_SIZE(dbgfs_attr_source); i++) { - len += scnprintf(buf + len, PAGE_SIZE - len, "%s ", + len += scnprintf(buf + len, sizeof(buf) - len, "%s ", dbgfs_attr_source[i]); } buf[len - 1] = '\n'; From 6288b593e76eb10329326f2cd51e32557203b9e5 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 15 Jul 2026 08:42:43 -0700 Subject: [PATCH 491/513] iio: buffer: Fix potential use-after-free in anonymous buffer release An anonymous buffer handle holds a reference to the underlying IIO device. The reference is dropped in the buffer handle's release function. If the device has been removed, either through unbind or hot-unplug, the buffer handle might hold the last reference. The release function takes the mutex for the buffer using a guard, which means the unlock happens after all the code in the function, including `iio_device_put()`. If the anonymous buffer holds the last reference this might free both the IIO device and the buffer, which contains the mutex, leading to use-after-free when the mutex is unlocked. Fix this by using a scoped guard just around the buffer dmabuf list access, making sure the mutex is unlocked before releasing the IIO device. Version 10 of the patch that introduced this issue used this exact scheme of first unlocking and then dropping the reference [1]. During review it was suggested to use a guard instead, and version 11 made that change [2]. Reported-by: codex:gpt-5.6 Fixes: 3e26d9f08fbe ("iio: core: Add new DMABUF interface infrastructure") Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/linux-iio/20240605110845.86740-4-paul@crapouillou.net #[1] Link: https://lore.kernel.org/linux-iio/20240618100302.72886-4-paul@crapouillou.net #[2] Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 531fc4ccc15d..04b3916f89e2 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1619,12 +1619,16 @@ static int iio_buffer_chrdev_release(struct inode *inode, struct file *filep) wake_up(&buffer->pollq); - guard(mutex)(&buffer->dmabufs_mutex); - - /* Close all attached DMABUFs */ - list_for_each_entry_safe(priv, tmp, &buffer->dmabufs, entry) { - list_del_init(&priv->entry); - iio_buffer_dmabuf_put(priv->attach); + /* + * The mutex must be unlocked before iio_device_put(), which might drop the + * last reference and free the buffer. + */ + scoped_guard(mutex, &buffer->dmabufs_mutex) { + /* Close all attached DMABUFs */ + list_for_each_entry_safe(priv, tmp, &buffer->dmabufs, entry) { + list_del_init(&priv->entry); + iio_buffer_dmabuf_put(priv->attach); + } } kfree(ib); From f25ec4627d935dedfb5fe83bd2c2678cdcc19611 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 15 Jul 2026 08:42:44 -0700 Subject: [PATCH 492/513] iio: buffer: Tie IIO dma fence lock lifetime to the fence The `iio_dma_fence` implementation currently uses a lock embedded in the `iio_dmabuf_priv`. But the `iio_dma_fence` can outlive the `iio_dmabuf_priv`, which can cause a use-after-free. Tie the lifetime of the lock to the lifetime of the fence by embedding them in the same struct. We can't just hold a reference to the `iio_dmabuf_priv` from the `iio_dma_fence` since `iio_buffer_dmabuf_release()` might sleep and the fence release callback is not allowed to sleep. Note that the `dma_fence` framework now has an internal lock that gets used when the passing `NULL` for `lock` in `dma_fence_init()`, but in order to allow this patch to be backportable use an external lock. Reported-by: codex:gpt-5.6 Fixes: 3e26d9f08fbe ("iio: core: Add new DMABUF interface infrastructure") Signed-off-by: Lars-Peter Clausen Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 04b3916f89e2..31c06bffdce8 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -47,9 +47,6 @@ struct iio_dmabuf_priv { u64 context; - /* Spinlock used for locking the dma_fence */ - spinlock_t lock; - struct dma_buf_attachment *attach; struct sg_table *sgt; enum dma_data_direction dir; @@ -58,6 +55,7 @@ struct iio_dmabuf_priv { struct iio_dma_fence { struct dma_fence base; + spinlock_t lock; /* protects base */ struct iio_dmabuf_priv *priv; struct work_struct work; }; @@ -1706,7 +1704,6 @@ static int iio_buffer_attach_dmabuf(struct iio_dev_buffer_pair *ib, if (!priv) return -ENOMEM; - spin_lock_init(&priv->lock); priv->context = dma_fence_context_alloc(1); dmabuf = dma_buf_get(fd); @@ -1896,6 +1893,8 @@ static int iio_buffer_enqueue_dmabuf(struct iio_dev_buffer_pair *ib, goto err_attachment_put; } + spin_lock_init(&fence->lock); + fence->priv = priv; seqno = atomic_add_return(1, &priv->seqno); @@ -1906,7 +1905,7 @@ static int iio_buffer_enqueue_dmabuf(struct iio_dev_buffer_pair *ib, * the dma_fence. */ dma_fence_init(&fence->base, &iio_buffer_dma_fence_ops, - &priv->lock, priv->context, seqno); + &fence->lock, priv->context, seqno); ret = iio_dma_resv_lock(dmabuf, nonblock); if (ret) From 8662e56c31cf23b61ca3d11b516efb94c35b8026 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 15 Jul 2026 08:42:45 -0700 Subject: [PATCH 493/513] iio: buffer: Make IIO DMA fence release RCU-safe The `dma_fence` documentation states that if a custom release implementation is provided, the `dma_fence` object must be freed in an RCU-safe way. The current `iio_dma_fence` implementation uses `kfree()`, which might result in a use-after-free. Remove the custom `release` implementation. This makes the DMA fence core fall back to `dma_fence_free()`, which calls `kfree_rcu()` on the fence. This requires that the fence be the first member of `struct iio_dma_fence`. Using the default release method for extended DMA fence structures is a common pattern. Reported-by: codex:gpt-5.6 Fixes: 3e26d9f08fbe ("iio: core: Add new DMABUF interface infrastructure") Signed-off-by: Lars-Peter Clausen Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 31c06bffdce8..2c9ec93dff47 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -54,6 +54,10 @@ struct iio_dmabuf_priv { }; struct iio_dma_fence { + /* + * Must remain the first member so the default release callback can pass + * the fence directly to dma_fence_free(). + */ struct dma_fence base; spinlock_t lock; /* protects base */ struct iio_dmabuf_priv *priv; @@ -1828,18 +1832,9 @@ iio_buffer_dma_fence_get_driver_name(struct dma_fence *fence) return "iio"; } -static void iio_buffer_dma_fence_release(struct dma_fence *fence) -{ - struct iio_dma_fence *iio_fence = - container_of(fence, struct iio_dma_fence, base); - - kfree(iio_fence); -} - static const struct dma_fence_ops iio_buffer_dma_fence_ops = { .get_driver_name = iio_buffer_dma_fence_get_driver_name, .get_timeline_name = iio_buffer_dma_fence_get_driver_name, - .release = iio_buffer_dma_fence_release, }; static int iio_buffer_enqueue_dmabuf(struct iio_dev_buffer_pair *ib, From 26e9213898fc949923188ef0aeea31fc87708836 Mon Sep 17 00:00:00 2001 From: Rupesh Majhi Date: Sun, 19 Jul 2026 03:07:52 +0300 Subject: [PATCH 494/513] iio: pressure: dps310: fix NULL pointer dereference on ACPI probe When the device is enumerated through its ACPI HID (IFX3100), i2c_client_get_device_id() returns NULL: the ACPI-derived client name does not match the driver's i2c_device_id table. dps310_probe() then dereferences that NULL pointer in "iio->name = id->name" and crashes the kernel during probe. The IIO device name is always "dps310", so set it directly and drop the now-unused device-id lookup. Fixes: 72ff282819d0 ("iio: pressure: dps310: Add ACPI HID table") Cc: stable@vger.kernel.org Signed-off-by: Rupesh Majhi Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/dps310.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c index f45af72a0554..45bdb8c7670f 100644 --- a/drivers/iio/pressure/dps310.c +++ b/drivers/iio/pressure/dps310.c @@ -845,7 +845,6 @@ static const struct iio_info dps310_info = { static int dps310_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); struct dps310_data *data; struct iio_dev *iio; int rc; @@ -858,7 +857,7 @@ static int dps310_probe(struct i2c_client *client) data->client = client; mutex_init(&data->lock); - iio->name = id->name; + iio->name = DPS310_DEV_NAME; iio->channels = dps310_channels; iio->num_channels = ARRAY_SIZE(dps310_channels); iio->info = &dps310_info; From c132aef0e757a39036b1d40faf0569f2e343b13e Mon Sep 17 00:00:00 2001 From: Vidhu Sarwal Date: Mon, 13 Jul 2026 07:58:29 +0530 Subject: [PATCH 495/513] iio: light: ltrf216a: fix runtime PM reference leak in error path ltrf216a_get_lux() acquires a runtime PM reference by calling ltrf216a_set_power_state(data, true). However, if ltrf216a_read_data() fails, the function returns immediately without dropping the reference. This leaves the runtime PM usage count unbalanced, preventing the device from autosuspending after a failed read. Fix this by releasing the runtime PM reference before returning from the error path. Fixes: 83f0bcd40d5c ("iio: light: Add support for ltrf216a sensor") Signed-off-by: Vidhu Sarwal Reviewed-by: Joshua Crofts Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/ltrf216a.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/light/ltrf216a.c b/drivers/iio/light/ltrf216a.c index aad96fc91565..dd8f3260b1d8 100644 --- a/drivers/iio/light/ltrf216a.c +++ b/drivers/iio/light/ltrf216a.c @@ -248,11 +248,10 @@ static int ltrf216a_get_lux(struct ltrf216a_data *data) return ret; greendata = ltrf216a_read_data(data, LTRF216A_ALS_DATA_0); + ltrf216a_set_power_state(data, false); if (greendata < 0) return greendata; - ltrf216a_set_power_state(data, false); - lux = greendata * data->info->lux_multiplier * LTRF216A_WIN_FAC; return lux; From b7e6e9af0d723afdec92364d5e7e064eeef44c8e Mon Sep 17 00:00:00 2001 From: Vidhu Sarwal Date: Wed, 15 Jul 2026 06:45:42 +0530 Subject: [PATCH 496/513] iio: light: opt4060: Reject integration times with a non-zero seconds part When setting the integration time, opt4060_write_raw() only uses val2 and ignores val. As a result, a write such as 1.000600 is accepted and programmed as 600 us, silently discarding the whole seconds part. Since all supported integration times are less than one second, any non-zero val represents an invalid input. Reject such values instead of silently accepting them. Fixes: 0c6db4506ad0 ("iio: light: Add support for TI OPT4060 color sensor") Signed-off-by: Vidhu Sarwal Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt4060.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/iio/light/opt4060.c b/drivers/iio/light/opt4060.c index c391ad3271c6..cf6f69e5be35 100644 --- a/drivers/iio/light/opt4060.c +++ b/drivers/iio/light/opt4060.c @@ -632,6 +632,9 @@ static int opt4060_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_INT_TIME: + if (val) + return -EINVAL; + int_time = opt4060_als_time_to_index(val2); if (int_time < 0) return int_time; From 0ba0ed0d42ebd3144f0050c48374817f5f3f07d6 Mon Sep 17 00:00:00 2001 From: Vidhu Sarwal Date: Wed, 15 Jul 2026 06:45:43 +0530 Subject: [PATCH 497/513] iio: light: opt4060: Fix pointer type passed to div_u64_rem() div_u64_rem() expects a u32 * for the remainder, but opt4060_read_ev_period() passes val2, which is declared as an int *. While this has no functional impact, it triggers a pointer type mismatch. There is no behavioural change because int and u32 have the same size and representation on all supported architectures, and the remainder is always less than MICRO, so it fits within the positive range of int. Use a local u32 to receive the remainder before assigning it to *val2. Fixes: 0c6db4506ad0 ("iio: light: Add support for TI OPT4060 color sensor") Signed-off-by: Vidhu Sarwal Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt4060.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/light/opt4060.c b/drivers/iio/light/opt4060.c index cf6f69e5be35..e3aabfb14d5d 100644 --- a/drivers/iio/light/opt4060.c +++ b/drivers/iio/light/opt4060.c @@ -713,6 +713,7 @@ static ssize_t opt4060_read_ev_period(struct opt4060_chip *chip, int *val, { int ret, pers, fault_count, int_time; u64 uval; + u32 rem; int_time = opt4060_int_time_reg[chip->int_time][0]; @@ -738,7 +739,8 @@ static ssize_t opt4060_read_ev_period(struct opt4060_chip *chip, int *val, } uval = mul_u32_u32(int_time, pers); - *val = div_u64_rem(uval, MICRO, val2); + *val = div_u64_rem(uval, MICRO, &rem); + *val2 = rem; return IIO_VAL_INT_PLUS_MICRO; } From ad367638212a9f2495ecfa59c886f0cfb7934e9c Mon Sep 17 00:00:00 2001 From: Vidhu Sarwal Date: Wed, 15 Jul 2026 06:45:44 +0530 Subject: [PATCH 498/513] iio: light: opt4060: Fix incorrect register name in threshold read error message opt4060_get_thresholds() correctly reads OPT4060_THRESHOLD_HIGH, but logs "Failed to read THRESHOLD_LOW." if the read fails. This is a copy-and-paste mistake, as the preceding low-threshold read already uses the correct error message. Update the error message to reference OPT4060_THRESHOLD_HIGH. Fixes: 0c6db4506ad0 ("iio: light: Add support for TI OPT4060 color sensor") Signed-off-by: Vidhu Sarwal Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt4060.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/opt4060.c b/drivers/iio/light/opt4060.c index e3aabfb14d5d..f79dd342937d 100644 --- a/drivers/iio/light/opt4060.c +++ b/drivers/iio/light/opt4060.c @@ -810,7 +810,7 @@ static int opt4060_get_thresholds(struct opt4060_chip *chip, u32 *th_lo, u32 *th ret = regmap_read(chip->regmap, OPT4060_THRESHOLD_HIGH, ®val); if (ret) { - dev_err(chip->dev, "Failed to read THRESHOLD_LOW.\n"); + dev_err(chip->dev, "Failed to read THRESHOLD_HIGH.\n"); return ret; } *th_hi = opt4060_calc_val_from_th_reg(regval); From 3b2cd82c524c75a2173f2e3f874652a75f81cd1d Mon Sep 17 00:00:00 2001 From: Nikhil Gautam Date: Tue, 14 Jul 2026 17:01:31 +0530 Subject: [PATCH 499/513] iio: light: opt4001: Fix power down clearing bits of the wrong register opt4001_power_down() intends to clear the operating mode bits in the CTRL register but reads OPT4001_DEVICE_ID instead of OPT4001_CTRL, so the value written back to CTRL contains device ID bits rather than the current configuration. Fix and simplify this by using regmap_clear_bits() on the CTRL register directly in the devm action, and drop opt4001_power_down() which has no other users. Suggested-by: Jonathan Cameron Fixes: 9a9608418292 ("iio: light: Add support for TI OPT4001 light sensor") Signed-off-by: Nikhil Gautam Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt4001.c | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/drivers/iio/light/opt4001.c b/drivers/iio/light/opt4001.c index dd152d921b48..288e699734af 100644 --- a/drivers/iio/light/opt4001.c +++ b/drivers/iio/light/opt4001.c @@ -222,33 +222,14 @@ static int opt4001_set_conf(struct opt4001_chip *chip) return ret; } -static int opt4001_power_down(struct opt4001_chip *chip) -{ - struct device *dev = &chip->client->dev; - int ret; - unsigned int reg; - - ret = regmap_read(chip->regmap, OPT4001_DEVICE_ID, ®); - if (ret) { - dev_err(dev, "Failed to read configuration\n"); - return ret; - } - - /* MODE_OFF is 0x0 so just set bits to 0 */ - reg &= ~OPT4001_CTRL_OPER_MODE_MASK; - - ret = regmap_write(chip->regmap, OPT4001_CTRL, reg); - if (ret) - dev_err(dev, "Failed to set configuration to power down\n"); - - return ret; -} - static void opt4001_chip_off_action(void *data) { struct opt4001_chip *chip = data; + int ret; - opt4001_power_down(chip); + ret = regmap_clear_bits(chip->regmap, OPT4001_CTRL, OPT4001_CTRL_OPER_MODE_MASK); + if (ret) + dev_err(&chip->client->dev, "Failed to power down\n"); } static const struct iio_chan_spec opt4001_channels[] = { From afa28741c9a2cf6edb2e41e25ff146a562160bb3 Mon Sep 17 00:00:00 2001 From: Nikhil Gautam Date: Tue, 14 Jul 2026 17:01:32 +0530 Subject: [PATCH 500/513] iio: light: opt4001: Fix incompatible pointer type passed to div_u64_rem() div_u64_rem() takes a u32 * for the remainder but is passed val2, which is an int *. There is no functional impact as int and u32 have the same size and representation on all supported architectures and the remainder is always smaller than the divisor, so it fits in the positive range of int. Fix the type mismatch by using a local u32 for the remainder and assigning the result to *val2. Fixes: 9a9608418292 ("iio: light: Add support for TI OPT4001 light sensor") Signed-off-by: Nikhil Gautam Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt4001.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/iio/light/opt4001.c b/drivers/iio/light/opt4001.c index 288e699734af..944923cb8f28 100644 --- a/drivers/iio/light/opt4001.c +++ b/drivers/iio/light/opt4001.c @@ -173,6 +173,7 @@ static int opt4001_read_lux_value(struct iio_dev *indio_dev, u8 crc; u8 calc_crc; u64 lux_raw; + u32 rem; int ret; ret = regmap_read(chip->regmap, OPT4001_LIGHT1_MSB, &light1); @@ -199,8 +200,8 @@ static int opt4001_read_lux_value(struct iio_dev *indio_dev, lux_raw = lux_raw << exp; lux_raw = lux_raw * chip->chip_info->mul; - *val = div_u64_rem(lux_raw, chip->chip_info->div, val2); - *val2 = *val2 * 100; + *val = div_u64_rem(lux_raw, chip->chip_info->div, &rem); + *val2 = rem * 100; return IIO_VAL_INT_PLUS_NANO; } From d0f21621f8b2b46661ea066d20705dbf7253db87 Mon Sep 17 00:00:00 2001 From: Nikhil Gautam Date: Tue, 14 Jul 2026 17:01:33 +0530 Subject: [PATCH 501/513] iio: light: opt4001: Reject integration times with a non-zero seconds part opt4001_write_raw() only looks at val2 when setting the integration time, so a write such as 1.000600 is silently accepted as 600 us. Return -EINVAL if val is non-zero. Fixes: 9a9608418292 ("iio: light: Add support for TI OPT4001 light sensor") Signed-off-by: Nikhil Gautam Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt4001.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/iio/light/opt4001.c b/drivers/iio/light/opt4001.c index 944923cb8f28..2448d6330489 100644 --- a/drivers/iio/light/opt4001.c +++ b/drivers/iio/light/opt4001.c @@ -269,6 +269,9 @@ static int opt4001_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_INT_TIME: + if (val) + return -EINVAL; + int_time = opt4001_als_time_to_index(val2); if (int_time < 0) return int_time; From d64bfd9f3352b9d9bdeca06de1a0a1c1bd47b896 Mon Sep 17 00:00:00 2001 From: Nikhil Gautam Date: Tue, 14 Jul 2026 17:01:34 +0530 Subject: [PATCH 502/513] iio: light: opt4001: Fix reversed GENMASK() arguments in fault count mask GENMASK(h, l) requires h >= l, but OPT4001_CTRL_FAULT_COUNT is defined as GENMASK(0, 1). The define is currently unused so there is no functional impact, but fix it before anyone builds on it, and add the _MASK suffix for consistency with the neighbouring definitions. Fixes: 9a9608418292 ("iio: light: Add support for TI OPT4001 light sensor") Signed-off-by: Nikhil Gautam Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt4001.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/opt4001.c b/drivers/iio/light/opt4001.c index 2448d6330489..aa3d87995b7c 100644 --- a/drivers/iio/light/opt4001.c +++ b/drivers/iio/light/opt4001.c @@ -39,7 +39,7 @@ #define OPT4001_CTRL_OPER_MODE_MASK GENMASK(5, 4) #define OPT4001_CTRL_LATCH_MASK GENMASK(3, 3) #define OPT4001_CTRL_INT_POL_MASK GENMASK(2, 2) -#define OPT4001_CTRL_FAULT_COUNT GENMASK(0, 1) +#define OPT4001_CTRL_FAULT_COUNT_MASK GENMASK(1, 0) /* OPT4001 constants */ #define OPT4001_DEVICE_ID_VAL 0x121 From a3c03cf36a083893dedad928915471dc8c10692e Mon Sep 17 00:00:00 2001 From: Can Peng Date: Tue, 14 Jul 2026 17:18:45 +0800 Subject: [PATCH 503/513] iio: dac: ad5446: fix OF module device table The ad5446 I2C driver exports its OF match table with MODULE_DEVICE_TABLE(OF, ...). The device table type is used by modpost when generating module aliases, and scripts/mod/file2alias.c matches the lowercase "of" type. Using "OF" prevents the OF table from being recognized, so no OF module alias is generated for the I2C driver. Use the lowercase "of" type so OF-based module autoloading works. Fixes: 876d94024087 ("iio: dac: ad5446: Separate I2C/SPI into different drivers") Signed-off-by: Can Peng Reviewed-by: Joshua Crofts Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5446-i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad5446-i2c.c b/drivers/iio/dac/ad5446-i2c.c index 2d4c8908d91e..cb3100fa32d9 100644 --- a/drivers/iio/dac/ad5446-i2c.c +++ b/drivers/iio/dac/ad5446-i2c.c @@ -84,7 +84,7 @@ static const struct of_device_id ad5446_i2c_of_ids[] = { { .compatible = "adi,ad5622", .data = &ad5622_chip_info }, { } }; -MODULE_DEVICE_TABLE(OF, ad5446_i2c_of_ids); +MODULE_DEVICE_TABLE(of, ad5446_i2c_of_ids); static struct i2c_driver ad5446_i2c_driver = { .driver = { From bcd3f72e26314edfce7eaf8d7160b3119c7b7fed Mon Sep 17 00:00:00 2001 From: Moksh Panicker Date: Mon, 13 Jul 2026 03:00:46 +0000 Subject: [PATCH 504/513] iio: chemical: atlas-sensor: fix PM reference leak in buffer postenable atlas_buffer_postenable() acquires a runtime PM reference with pm_runtime_resume_and_get() but returns the result of atlas_set_interrupt() directly. If atlas_set_interrupt() fails, the runtime PM reference is leaked and the device can never autosuspend. Add pm_runtime_put_autosuspend() on the error path to balance the reference. Fixes: 0e4f336f50de ("iio: chemical: atlas-sensor: Balance runtime pm + pm_runtime_resume_and_get()") Cc: stable@vger.kernel.org Signed-off-by: Moksh Panicker Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/atlas-sensor.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c index 0e2edcff63f9..b2c1a598b3a5 100644 --- a/drivers/iio/chemical/atlas-sensor.c +++ b/drivers/iio/chemical/atlas-sensor.c @@ -413,7 +413,11 @@ static int atlas_buffer_postenable(struct iio_dev *indio_dev) if (ret) return ret; - return atlas_set_interrupt(data, true); + ret = atlas_set_interrupt(data, true); + if (ret) + pm_runtime_put_autosuspend(&data->client->dev); + + return ret; } static int atlas_buffer_predisable(struct iio_dev *indio_dev) From 60f6f7fd5f89c7f55991c5dc59f3ccc74cd6efd7 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 13 Jul 2026 11:40:42 +0300 Subject: [PATCH 505/513] iio: adc: adi-axi-adc: add data size support for AD408X backend The AD408X AXI core can pack the sample data on the bus using different word widths. Expose this through the data_size_set backend operation so that frontends can program the packet format field (bits 3:2 of the CNTRL_3 register) according to the ADC resolution: 20-bit, 16-bit and 14-bit map to packet format values 0, 1 and 2 respectively. Signed-off-by: Antoniu Miclaus Reviewed-by: David Lechner Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/adi-axi-adc.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index ced0a2321ecf..aac6f4a0705e 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -54,6 +54,10 @@ #define AXI_AD485X_PACKET_FORMAT_24BIT 0x1 #define AXI_AD485X_PACKET_FORMAT_32BIT 0x2 #define AXI_AD408X_CNTRL_3_FILTER_EN_MSK BIT(0) +#define AXI_AD408X_CNTRL_3_PACKET_FORMAT_MSK GENMASK(3, 2) +#define AXI_AD408X_PACKET_FORMAT_20BIT 0x0 +#define AXI_AD408X_PACKET_FORMAT_16BIT 0x1 +#define AXI_AD408X_PACKET_FORMAT_14BIT 0x2 #define ADI_AXI_ADC_REG_SYNC_STATUS 0x0068 #define ADI_AXI_ADC_SYNC_STATUS_ADC_SYNC_MSK BIT(0) @@ -437,6 +441,31 @@ static int axi_adc_ad408x_filter_type_set(struct iio_backend *back, AXI_AD408X_CNTRL_3_FILTER_EN_MSK); } +static int axi_adc_ad408x_data_size_set(struct iio_backend *back, + unsigned int size) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + unsigned int val; + + switch (size) { + case 20: + val = AXI_AD408X_PACKET_FORMAT_20BIT; + break; + case 16: + val = AXI_AD408X_PACKET_FORMAT_16BIT; + break; + case 14: + val = AXI_AD408X_PACKET_FORMAT_14BIT; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3, + AXI_AD408X_CNTRL_3_PACKET_FORMAT_MSK, + FIELD_PREP(AXI_AD408X_CNTRL_3_PACKET_FORMAT_MSK, val)); +} + static int axi_adc_ad408x_interface_data_align(struct iio_backend *back, u32 timeout_us) { @@ -660,6 +689,7 @@ static const struct iio_backend_ops adi_ad408x_ops = { .free_buffer = axi_adc_free_buffer, .data_sample_trigger = axi_adc_data_sample_trigger, .filter_type_set = axi_adc_ad408x_filter_type_set, + .data_size_set = axi_adc_ad408x_data_size_set, .interface_data_align = axi_adc_ad408x_interface_data_align, .num_lanes_set = axi_adc_num_lanes_set, .debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access), From d39afd249d1ef5cb03b6f1dc22a68e6385de413d Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 13 Jul 2026 11:40:43 +0300 Subject: [PATCH 506/513] iio: adc: ad4080: configure backend data size The AXI backend needs to know the ADC word width in order to pack the sample data correctly on the bus. During channel setup, program the backend packet format via iio_backend_data_size_set() using the channel resolution, so the data is transferred according to the device's realbits. The backend packet format field defaults to 20-bit packing, so the 20-bit parts (AD4080/AD4081/AD4082, AD4880) were unaffected. The 16-bit (AD4083/AD4084/AD4085, AD4884) and 14-bit (AD4086/AD4087/AD4088) parts, however, were left packing data at the wrong width, producing corrupt buffered captures. Fixes: 6c3e7265734b ("iio: adc: ad4080: add support for AD4084") Signed-off-by: Antoniu Miclaus Reviewed-by: David Lechner Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4080.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c index 8d2953341b15..8feb0c7f7fcb 100644 --- a/drivers/iio/adc/ad4080.c +++ b/drivers/iio/adc/ad4080.c @@ -698,6 +698,11 @@ static int ad4080_setup_channel(struct ad4080_state *st, unsigned int ch) if (ret) return ret; + ret = iio_backend_data_size_set(st->back[ch], + st->info->channels[0].scan_type.realbits); + if (ret) + return ret; + if (!st->lvds_cnv_en) return 0; From a40b2e7a17f26e38ab054363c9c7cde149588357 Mon Sep 17 00:00:00 2001 From: Cong Nguyen Date: Mon, 27 Jul 2026 22:51:15 +0700 Subject: [PATCH 507/513] iio: srf04: fix pm_runtime handling on probe error path When pm_runtime_set_active() fails during probe, the driver logs the error and unregisters the IIO device, but then falls through and still calls pm_runtime_enable() before returning the error. Since probe returns an error, srf04_remove() is never called, so runtime PM is left enabled without a matching pm_runtime_disable(). This leaks the enable and triggers an "Unbalanced pm_runtime_enable!" warning on a subsequent bind of the device. Return the error right after unregistering the IIO device so that runtime PM is not enabled on the failure path. Fixes: 2251157b335b ("iio: srf04: add power management feature") Cc: stable@vger.kernel.org Signed-off-by: Cong Nguyen Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/srf04.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c index e97f9a20ac7a..337938657449 100644 --- a/drivers/iio/proximity/srf04.c +++ b/drivers/iio/proximity/srf04.c @@ -331,6 +331,7 @@ static int srf04_probe(struct platform_device *pdev) if (ret) { dev_err(data->dev, "pm_runtime_set_active: %d\n", ret); iio_device_unregister(indio_dev); + return ret; } pm_runtime_enable(data->dev); From 06fab97602fe400bea843176f485bbac07a668e2 Mon Sep 17 00:00:00 2001 From: Cong Nguyen Date: Sun, 2 Aug 2026 13:55:40 +0700 Subject: [PATCH 508/513] iio: gyro: mpu3050: fix sign of raw angular velocity readings The MPU-3050 gyroscope output registers hold 16-bit two's complement values; the angular velocity channels are declared with .sign = 's'. When mpu3050_read_raw() handles IIO_CHAN_INFO_RAW it reads the register via a big-endian regmap_bulk_read() and assigns it with: *val = be16_to_cpu(raw_val); be16_to_cpu() yields an unsigned 16-bit quantity, so negative rates (bit 15 set) are reported to userspace as large positive integers (e.g. -1 becomes 65535) instead of the correct negative value. Cast to s16 before the assignment, matching the temperature channel a few lines above which already handles the sign correctly. Fixes: 3904b28efb2c ("iio: gyro: Add driver for the MPU-3050 gyroscope") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4 Signed-off-by: Cong Nguyen Reviewed-by: Linus Walleij Reviewed-by: Joshua Crofts Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/mpu3050-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index d84e04e4b431..07c0f9011c63 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -356,7 +356,7 @@ static int mpu3050_read_raw(struct iio_dev *indio_dev, goto out_read_raw_unlock; } - *val = be16_to_cpu(raw_val); + *val = (s16)be16_to_cpu(raw_val); ret = IIO_VAL_INT; goto out_read_raw_unlock; From d378fceaafd79e0dc59d3546bda251a3058062c0 Mon Sep 17 00:00:00 2001 From: Moksh Panicker Date: Sun, 2 Aug 2026 19:07:01 +0000 Subject: [PATCH 509/513] iio: light: apds9306: fix PM reference leak in apds9306_read_data() apds9306_read_data() calls pm_runtime_resume_and_get() but several error paths return directly without calling pm_runtime_put_autosuspend(), leaking the runtime PM reference and preventing the device from autosuspending. Use PM_RUNTIME_ACQUIRE_AUTOSUSPEND() and PM_RUNTIME_ACQUIRE_ERR() to automatically handle runtime PM reference release on all return paths. Fixes: 620d1e6c7a3f ("iio: light: Add support for APDS9306 Light Sensor") Signed-off-by: Moksh Panicker Cc: stable@vger.kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/light/apds9306.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c index 5ca4c87524fe..d582bda4d847 100644 --- a/drivers/iio/light/apds9306.c +++ b/drivers/iio/light/apds9306.c @@ -469,9 +469,9 @@ static int apds9306_read_data(struct apds9306_data *data, int *val, int reg) int status = 0; u8 buff[3]; - ret = pm_runtime_resume_and_get(data->dev); - if (ret) - return ret; + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(data->dev, pm); + if (PM_RUNTIME_ACQUIRE_ERR(&pm)) + return PM_RUNTIME_ACQUIRE_ERR(&pm); ret = regmap_field_read(rf->intg_time, &intg_time_idx); if (ret) @@ -535,8 +535,6 @@ static int apds9306_read_data(struct apds9306_data *data, int *val, int reg) *val = get_unaligned_le24(&buff); - pm_runtime_put_autosuspend(data->dev); - return 0; } From 579c049b4cb6fc72ce2c505fc5334540be0efcd3 Mon Sep 17 00:00:00 2001 From: Nikhil Gautam Date: Wed, 22 Jul 2026 21:52:45 +0530 Subject: [PATCH 510/513] iio: light: gp2ap002: Fix unbalanced runtime PM on repeated event writes The IIO core does not filter duplicate writes to the event enable attribute, so writing the same value twice invokes write_event_config() twice. Enabling twice leaks a runtime PM reference, preventing the device from ever suspending again; disabling twice underflows the usage count and triggers a "Runtime PM usage count underflow" warning. Bail out early when the requested state matches the current state. While at it, switch to pm_runtime_resume_and_get() so a failed resume is propagated to userspace instead of silently marking the event enabled. Fixes: 97d642e23037c ("iio: light: Add a driver for Sharp GP2AP002x00F") Signed-off-by: Nikhil Gautam Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap002.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index 05773e24931b..708a77006529 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -342,6 +342,10 @@ static int gp2ap002_write_event_config(struct iio_dev *indio_dev, bool state) { struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); + int ret; + + if (state == gp2ap002->enabled) + return 0; if (state) { /* @@ -349,13 +353,16 @@ static int gp2ap002_write_event_config(struct iio_dev *indio_dev, * already) and reintialize the sensor by using runtime_pm * callbacks. */ - pm_runtime_get_sync(gp2ap002->dev); - gp2ap002->enabled = true; + ret = pm_runtime_resume_and_get(gp2ap002->dev); + if (ret) + return ret; + } else { pm_runtime_put_autosuspend(gp2ap002->dev); - gp2ap002->enabled = false; } + gp2ap002->enabled = state; + return 0; } From 5d89e7cbac40057f5241a0832a86ce0fe97e4818 Mon Sep 17 00:00:00 2001 From: Nikhil Gautam Date: Wed, 22 Jul 2026 21:52:46 +0530 Subject: [PATCH 511/513] iio: light: gp2ap002: re-enable irq if runtime suspend fails gp2ap002_runtime_suspend() disables the irq before writing OPMOD. If the write fails, the callback returns an error with the irq still disabled while the PM core marks the device active again. re-enable the irq before returning the error so the irq state matches the active state the PM core restores. Fixes: 97d642e23037c ("iio: light: Add a driver for Sharp GP2AP002x00F") Signed-off-by: Nikhil Gautam Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap002.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index 708a77006529..de29081fd3b2 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -649,6 +649,7 @@ static int gp2ap002_runtime_suspend(struct device *dev) /* Disable chip and IRQ, everything off */ ret = regmap_write(gp2ap002->map, GP2AP002_OPMOD, 0x00); if (ret) { + enable_irq(gp2ap002->irq); dev_err(gp2ap002->dev, "error setting up operation mode\n"); return ret; } From 3364c56b20c1c496bdb8c8df32f96a9947dbf98e Mon Sep 17 00:00:00 2001 From: Cong Nguyen Date: Sun, 2 Aug 2026 14:12:46 +0700 Subject: [PATCH 512/513] iio: adc: pac1921: fix wrong channel used in trigger handler read pac1921_trigger_handler() walks the enabled channels with iio_for_each_active_channel(), which yields the scan index (bit) of each active channel, while ch is a separate counter used to pack the samples contiguously into the scan buffer. The register to read was looked up with the packing counter instead of the scan index: ret = pac1921_read_res(priv, idev->channels[ch].address, &val); pac1921_channels[] is ordered by scan index, so channels[bit] is the channel that is actually enabled, whereas channels[ch] is merely the ch-th array entry. These coincide only when the enabled channels form a contiguous prefix (e.g. all channels enabled). With a sparse scan mask - for example when only the power channel (scan index 3) is enabled - the handler reads the wrong register (VBUS instead of VPOWER) and pushes it to userspace as the enabled channel's data. Index the channel array by the scan index (bit) to read the correct register, keeping ch only for contiguous packing into the scan buffer. Fixes: 371f778b83cd ("iio: adc: add support for pac1921") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4 Signed-off-by: Cong Nguyen Acked-by: Matteo Martelli Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/pac1921.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/pac1921.c b/drivers/iio/adc/pac1921.c index 68bdd2f30bad..f0bab94462f4 100644 --- a/drivers/iio/adc/pac1921.c +++ b/drivers/iio/adc/pac1921.c @@ -1037,7 +1037,7 @@ static irqreturn_t pac1921_trigger_handler(int irq, void *p) iio_for_each_active_channel(idev, bit) { u16 val; - ret = pac1921_read_res(priv, idev->channels[ch].address, &val); + ret = pac1921_read_res(priv, idev->channels[bit].address, &val); if (ret) goto done; From be61c8c6252671ecf1fee0ad90f87669e0be1e20 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Sun, 2 Aug 2026 07:18:58 +0000 Subject: [PATCH 513/513] iio: chemical: atlas-sensor: use iio_trigger_poll_nested() to fix remove UAF The atlas driver requests its hardware data-ready IRQ with devm_request_threaded_irq(); its threaded handler queues an irq_work, atlas_work_handler(), that calls iio_trigger_poll(data->trig). The IRQ is devm-managed, so free_irq() runs from the devres unwind after atlas_remove() returns without flushing that irq_work. Once a buffer is enabled, conversion-complete IRQs keep firing and queueing it; a pending irq_work can therefore run after the unwind has freed atlas_data/indio_dev and the trigger, when atlas_work_handler() derives the atlas_data pointer via container_of() and dereferences data->trig, a use-after-free. Call iio_trigger_poll_nested() directly from the threaded handler instead of bouncing through irq_work. free_irq() then drains the threaded handler, closing the window; other iio drivers with a threaded data-ready IRQ do the same (e.g. bmi270). This issue was found by an in-house static analysis tool. Fixes: 7103b99b031c ("iio: chemical: atlas-ph-sensor: reorg driver to allow multiple chips") Cc: stable@vger.kernel.org # v6.4+ Assisted-by: Codex:gpt-5.6 Signed-off-by: Fan Wu Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/atlas-sensor.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c index b2c1a598b3a5..aa625f39b85e 100644 --- a/drivers/iio/chemical/atlas-sensor.c +++ b/drivers/iio/chemical/atlas-sensor.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -88,7 +87,6 @@ struct atlas_data { struct iio_trigger *trig; const struct atlas_device *chip; struct regmap *regmap; - struct irq_work work; unsigned int interrupt_enabled; /* 96-bit data + 32-bit pad + 64-bit timestamp */ __be32 buffer[6] __aligned(8); @@ -441,13 +439,6 @@ static const struct iio_buffer_setup_ops atlas_buffer_setup_ops = { .predisable = atlas_buffer_predisable, }; -static void atlas_work_handler(struct irq_work *work) -{ - struct atlas_data *data = container_of(work, struct atlas_data, work); - - iio_trigger_poll(data->trig); -} - static irqreturn_t atlas_trigger_handler(int irq, void *private) { struct iio_poll_func *pf = private; @@ -474,7 +465,7 @@ static irqreturn_t atlas_interrupt_handler(int irq, void *private) struct iio_dev *indio_dev = private; struct atlas_data *data = iio_priv(indio_dev); - irq_work_queue(&data->work); + iio_trigger_poll_nested(data->trig); return IRQ_HANDLED; } @@ -670,8 +661,6 @@ static int atlas_probe(struct i2c_client *client) goto unregister_trigger; } - init_irq_work(&data->work, atlas_work_handler); - if (client->irq > 0) { /* interrupt pin toggles on new conversion */ ret = devm_request_threaded_irq(&client->dev, client->irq,