From f4aff53c90e48360c91b6d902b935ecbaccb0b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 2 Dec 2025 15:09:19 +0100 Subject: [PATCH 001/171] bus: fsl-mc: Drop error message in probe function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver core already emits an error message when probe fails, see call_driver_probe() in drivers/base/dd.c. So drop the duplicated error message. Signed-off-by: Uwe Kleine-König Tested-by: Ioana Ciornei Reviewed-by: Ioana Ciornei Link: https://lore.kernel.org/r/52c89497f78839446713a9c5456defce97a74c7d.1764684327.git.u.kleine-koenig@baylibre.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/bus/fsl-mc/fsl-mc-bus.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c index 25845c04e562..0f0a5067f109 100644 --- a/drivers/bus/fsl-mc/fsl-mc-bus.c +++ b/drivers/bus/fsl-mc/fsl-mc-bus.c @@ -442,14 +442,7 @@ static int fsl_mc_driver_probe(struct device *dev) mc_drv = to_fsl_mc_driver(dev->driver); - error = mc_drv->probe(mc_dev); - if (error < 0) { - if (error != -EPROBE_DEFER) - dev_err(dev, "%s failed: %d\n", __func__, error); - return error; - } - - return 0; + return mc_drv->probe(mc_dev); } static int fsl_mc_driver_remove(struct device *dev) From c8dff80a3108a2cb85ac7ac5b1a75c247ac55fd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 2 Dec 2025 15:09:20 +0100 Subject: [PATCH 002/171] bus: fsl-mc: Convert to bus callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the eventual goal to drop .probe(), .remove() and .shutdown() from struct device_driver, convert the fsl bus to use bus methods. Other than a driver's shutdown callback the bus shutdown callback is also called for unbound drivers. So check for the device being bound before following the pointer to its driver. Signed-off-by: Uwe Kleine-König Reviewed-by: Greg Kroah-Hartman Tested-by: Ioana Ciornei Reviewed-by: Ioana Ciornei Link: https://lore.kernel.org/r/848fffe5c479d899c04a4c99ccb5f0128ccc942d.1764684327.git.u.kleine-koenig@baylibre.com Link: https://lore.kernel.org/r/20251209115950.3382308-2-u.kleine-koenig@baylibre.com [chleroy: squashed the two patches] Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/bus/fsl-mc/fsl-mc-bus.c | 70 +++++++++++++++------------------ 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c index 0f0a5067f109..c08c04047ae2 100644 --- a/drivers/bus/fsl-mc/fsl-mc-bus.c +++ b/drivers/bus/fsl-mc/fsl-mc-bus.c @@ -137,6 +137,35 @@ static int fsl_mc_bus_uevent(const struct device *dev, struct kobj_uevent_env *e return 0; } +static int fsl_mc_probe(struct device *dev) +{ + struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver); + struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); + + if (mc_drv->probe) + return mc_drv->probe(mc_dev); + + return 0; +} + +static void fsl_mc_remove(struct device *dev) +{ + struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver); + struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); + + if (mc_drv->remove) + mc_drv->remove(mc_dev); +} + +static void fsl_mc_shutdown(struct device *dev) +{ + struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver); + struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); + + if (dev->driver && mc_drv->shutdown) + mc_drv->shutdown(mc_dev); +} + static int fsl_mc_dma_configure(struct device *dev) { const struct device_driver *drv = READ_ONCE(dev->driver); @@ -314,6 +343,9 @@ const struct bus_type fsl_mc_bus_type = { .name = "fsl-mc", .match = fsl_mc_bus_match, .uevent = fsl_mc_bus_uevent, + .probe = fsl_mc_probe, + .remove = fsl_mc_remove, + .shutdown = fsl_mc_shutdown, .dma_configure = fsl_mc_dma_configure, .dma_cleanup = fsl_mc_dma_cleanup, .dev_groups = fsl_mc_dev_groups, @@ -434,35 +466,6 @@ static const struct device_type *fsl_mc_get_device_type(const char *type) return NULL; } -static int fsl_mc_driver_probe(struct device *dev) -{ - struct fsl_mc_driver *mc_drv; - struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); - int error; - - mc_drv = to_fsl_mc_driver(dev->driver); - - return mc_drv->probe(mc_dev); -} - -static int fsl_mc_driver_remove(struct device *dev) -{ - struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver); - struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); - - mc_drv->remove(mc_dev); - - return 0; -} - -static void fsl_mc_driver_shutdown(struct device *dev) -{ - struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver); - struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); - - mc_drv->shutdown(mc_dev); -} - /* * __fsl_mc_driver_register - registers a child device driver with the * MC bus @@ -479,15 +482,6 @@ int __fsl_mc_driver_register(struct fsl_mc_driver *mc_driver, mc_driver->driver.owner = owner; mc_driver->driver.bus = &fsl_mc_bus_type; - if (mc_driver->probe) - mc_driver->driver.probe = fsl_mc_driver_probe; - - if (mc_driver->remove) - mc_driver->driver.remove = fsl_mc_driver_remove; - - if (mc_driver->shutdown) - mc_driver->driver.shutdown = fsl_mc_driver_shutdown; - error = driver_register(&mc_driver->driver); if (error < 0) { pr_err("driver_register() failed for %s: %d\n", From dc5aef63c037562500723d0041e863a092997124 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Tue, 4 Nov 2025 11:39:42 +0100 Subject: [PATCH 003/171] soc/xilinx: replace use of system_unbound_wq with system_dfl_wq Currently if a user enqueue a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistentcy cannot be addressed without refactoring the API. This patch continues the effort to refactor worqueue APIs, which has begun with the change introducing new workqueues and a new alloc_workqueue flag: commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag") system_dfl_wq should be the default workqueue so as not to enforce locality constraints for random work whenever it's not required. The old system_unbound_wq will be kept for a few release cycles. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/20251104103942.96647-1-marco.crivellari@suse.com --- drivers/soc/xilinx/zynqmp_power.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/soc/xilinx/zynqmp_power.c b/drivers/soc/xilinx/zynqmp_power.c index 9b7b2858b22a..9085db1b480a 100644 --- a/drivers/soc/xilinx/zynqmp_power.c +++ b/drivers/soc/xilinx/zynqmp_power.c @@ -82,7 +82,7 @@ static void subsystem_restart_event_callback(const u32 *payload, void *data) memcpy(zynqmp_pm_init_restart_work->args, &payload[0], sizeof(zynqmp_pm_init_restart_work->args)); - queue_work(system_unbound_wq, &zynqmp_pm_init_restart_work->callback_work); + queue_work(system_dfl_wq, &zynqmp_pm_init_restart_work->callback_work); } static void suspend_event_callback(const u32 *payload, void *data) @@ -95,7 +95,7 @@ static void suspend_event_callback(const u32 *payload, void *data) memcpy(zynqmp_pm_init_suspend_work->args, &payload[1], sizeof(zynqmp_pm_init_suspend_work->args)); - queue_work(system_unbound_wq, &zynqmp_pm_init_suspend_work->callback_work); + queue_work(system_dfl_wq, &zynqmp_pm_init_suspend_work->callback_work); } static irqreturn_t zynqmp_pm_isr(int irq, void *data) @@ -140,7 +140,7 @@ static void ipi_receive_callback(struct mbox_client *cl, void *data) memcpy(zynqmp_pm_init_suspend_work->args, &payload[1], sizeof(zynqmp_pm_init_suspend_work->args)); - queue_work(system_unbound_wq, + queue_work(system_dfl_wq, &zynqmp_pm_init_suspend_work->callback_work); /* Send NULL message to mbox controller to ack the message */ From 9fda364cb78c8b9e1abe4029f877300c94655742 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 10 Dec 2025 11:16:56 +0800 Subject: [PATCH 004/171] firmware: arm_ffa: Unmap Rx/Tx buffers on init failure ffa_init() maps the Rx/Tx buffers via ffa_rxtx_map() but on the partition setup failure path it never unmaps them. Add the missing ffa_rxtx_unmap() call in the error path so that the Rx/Tx buffers are properly released before freeing the backing pages. Signed-off-by: Haoxiang Li Message-Id: <20251210031656.56194-1-lihaoxiang@isrc.iscas.ac.cn> Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index c72ee4756585..7209a630f6d1 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -2068,6 +2068,7 @@ static int __init ffa_init(void) pr_err("failed to setup partitions\n"); ffa_notifications_cleanup(); + ffa_rxtx_unmap(drv_info->vm_id); free_pages: if (drv_info->tx_buffer) free_pages_exact(drv_info->tx_buffer, rxtx_bufsz); From f183b1dda4fc0348c4016f6289588d5853ab7936 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 16 Oct 2025 10:41:11 +0100 Subject: [PATCH 005/171] firmware: arm_ffa: Tie FF-A version checks to specific features The FF-A driver currently performs loose comparisons when checking the supported FF-A feature, which can inadvertently treat future or intermediate revisions as compatible. Replace generic `version {>,<} FFA_VERSION_1_*` pattern checks with feature-specific macros that clearly express which functionality depends on FF-A versioning. This improves readability and future maintainability by tying each feature (e.g. GET_COUNT_ONLY, size/UUID/exec state in responses) to explicit version requirements instead of relying on generic version comparisons. This improves robustness and clarity as the FF-A specification evolves. No functional change, only improves code readability. Message-Id: <20251016094111.946236-1-sudeep.holla@arm.com> Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 7209a630f6d1..351780ac8d72 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -246,6 +246,11 @@ static int ffa_features(u32 func_feat_id, u32 input_props, } #define PARTITION_INFO_GET_RETURN_COUNT_ONLY BIT(0) +#define FFA_SUPPORTS_GET_COUNT_ONLY(version) ((version) > FFA_VERSION_1_0) +#define FFA_PART_INFO_HAS_SIZE_IN_RESP(version) ((version) > FFA_VERSION_1_0) +#define FFA_PART_INFO_HAS_UUID_IN_RESP(version) ((version) > FFA_VERSION_1_0) +#define FFA_PART_INFO_HAS_EXEC_STATE_IN_RESP(version) \ + ((version) > FFA_VERSION_1_0) /* buffer must be sizeof(struct ffa_partition_info) * num_partitions */ static int @@ -255,7 +260,7 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, int idx, count, flags = 0, sz, buf_sz; ffa_value_t partition_info; - if (drv_info->version > FFA_VERSION_1_0 && + if (FFA_SUPPORTS_GET_COUNT_ONLY(drv_info->version) && (!buffer || !num_partitions)) /* Just get the count for now */ flags = PARTITION_INFO_GET_RETURN_COUNT_ONLY; @@ -273,12 +278,11 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, count = partition_info.a2; - if (drv_info->version > FFA_VERSION_1_0) { + if (FFA_PART_INFO_HAS_SIZE_IN_RESP(drv_info->version)) { buf_sz = sz = partition_info.a3; if (sz > sizeof(*buffer)) buf_sz = sizeof(*buffer); } else { - /* FFA_VERSION_1_0 lacks size in the response */ buf_sz = sz = 8; } @@ -1706,7 +1710,7 @@ static int ffa_setup_partitions(void) struct ffa_device *ffa_dev; struct ffa_partition_info *pbuf, *tpbuf; - if (drv_info->version == FFA_VERSION_1_0) { + if (!FFA_PART_INFO_HAS_UUID_IN_RESP(drv_info->version)) { ret = bus_register_notifier(&ffa_bus_type, &ffa_bus_nb); if (ret) pr_err("Failed to register FF-A bus notifiers\n"); @@ -1733,7 +1737,7 @@ static int ffa_setup_partitions(void) continue; } - if (drv_info->version > FFA_VERSION_1_0 && + if (FFA_PART_INFO_HAS_EXEC_STATE_IN_RESP(drv_info->version) && !(tpbuf->properties & FFA_PARTITION_AARCH64_EXEC)) ffa_mode_32bit_set(ffa_dev); From 78b74136affd7aabb7a578b86e26099872861841 Mon Sep 17 00:00:00 2001 From: Ally Heev Date: Wed, 5 Nov 2025 19:37:52 +0530 Subject: [PATCH 006/171] firmware: arm_scmi: Fix uninitialized pointers with __free attr Uninitialized pointers with `__free` attribute can cause undefined behaviour as the memory assigned(randomly) to the pointer is freed automatically when the pointer goes out of scope arm doesn't have any bugs related to this as of now, but it is better to initialize and assign pointers with `__free` attr in one statement to ensure proper scope-based cleanup Reported-by: Dan Carpenter Closes: https://lore.kernel.org/all/aPiG_F5EBQUjZqsl@stanley.mountain/ Signed-off-by: Ally Heev Reviewed-by: Cristian Marussi Message-Id: <20251105-aheev-uninitialized-free-attr-arm-v1-1-f7b6cb5d3361@gmail.com> Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/shmem.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/arm_scmi/shmem.c b/drivers/firmware/arm_scmi/shmem.c index 11c347bff766..dadb37557f8a 100644 --- a/drivers/firmware/arm_scmi/shmem.c +++ b/drivers/firmware/arm_scmi/shmem.c @@ -196,7 +196,6 @@ static void __iomem *shmem_setup_iomap(struct scmi_chan_info *cinfo, struct resource *res, struct scmi_shmem_io_ops **ops) { - struct device_node *shmem __free(device_node); const char *desc = tx ? "Tx" : "Rx"; int ret, idx = tx ? 0 : 1; struct device *cdev = cinfo->dev; @@ -205,7 +204,9 @@ static void __iomem *shmem_setup_iomap(struct scmi_chan_info *cinfo, void __iomem *addr; u32 reg_io_width; - shmem = of_parse_phandle(cdev->of_node, "shmem", idx); + struct device_node *shmem __free(device_node) = of_parse_phandle(cdev->of_node, + "shmem", idx); + if (!shmem) return IOMEM_ERR_PTR(-ENODEV); From fdce161afe29b40e84853ec821cbd7bbd727bf58 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 27 Nov 2025 21:57:31 -0800 Subject: [PATCH 007/171] firmware: ti_sci.h: fix all kernel-doc warnings Modify kernel-doc comments in ti_sci.h to eliminate all kernel-doc warnings: * use correct/matching struct names in kdoc comments * use correct struct member names in kdoc comments * add a ':' after struct member names where needed * change a blank kdoc line to " *" * convert 3 structs to kernel-doc comments: struct ti_sci_msg_rm_udmap_tx_ch_cfg_req struct ti_sci_msg_rm_udmap_rx_ch_cfg_req struct ti_sci_msg_rm_udmap_flow_cfg_req Fixes 13 kernel-doc warnings: Warning: drivers/firmware/ti_sci.h:609 expecting prototype for struct tisci_msg_req_prepare_sleep. Prototype was for struct ti_sci_msg_req_prepare_sleep instead Warning: drivers/firmware/ti_sci.h:609 struct member 'hdr' not described in 'ti_sci_msg_req_prepare_sleep' Warning: drivers/firmware/ti_sci.h:609 struct member 'mode' not described in 'ti_sci_msg_req_prepare_sleep' Warning: drivers/firmware/ti_sci.h:609 struct member 'ctx_lo' not described in 'ti_sci_msg_req_prepare_sleep' Warning: drivers/firmware/ti_sci.h:609 struct member 'ctx_hi' not described in 'ti_sci_msg_req_prepare_sleep' Warning: drivers/firmware/ti_sci.h:609 struct member 'debug_flags' not described in 'ti_sci_msg_req_prepare_sleep' Warning: drivers/firmware/ti_sci.h:623 expecting prototype for struct tisci_msg_set_io_isolation_req. Prototype was for struct ti_sci_msg_req_set_io_isolation instead Warning: drivers/firmware/ti_sci.h:696 struct member 'latency' not described in 'ti_sci_msg_req_lpm_set_latency_constraint' Warning: drivers/firmware/ti_sci.h:857 bad line: Warning: drivers/firmware/ti_sci.h:1002 This comment starts with '/**', but isn't a kernel-doc comment. Refer to Documentation/doc-guide/kernel-doc.rst * Configures a Navigator Subsystem UDMAP transmit channel Warning: drivers/firmware/ti_sci.h:1130 This comment starts with '/**', but isn't a kernel-doc comment. Refer to Documentation/doc-guide/kernel-doc.rst * Configures a Navigator Subsystem UDMAP receive channel Warning: drivers/firmware/ti_sci.h:1249 This comment starts with '/**', but isn't a kernel-doc comment. Refer to Documentation/doc-guide/kernel-doc.rst * Configures a Navigator Subsystem UDMAP receive flow Warning: drivers/firmware/ti_sci.h:1421 struct member 'valid_params' not described in 'ti_sci_msg_rm_udmap_flow_cfg_req' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20251128055731.812460-1-rdunlap@infradead.org Signed-off-by: Nishanth Menon --- drivers/firmware/ti_sci.h | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/ti_sci.h b/drivers/firmware/ti_sci.h index 91f234550c43..4616127e33ff 100644 --- a/drivers/firmware/ti_sci.h +++ b/drivers/firmware/ti_sci.h @@ -580,13 +580,13 @@ struct ti_sci_msg_resp_get_clock_freq { } __packed; /** - * struct tisci_msg_req_prepare_sleep - Request for TISCI_MSG_PREPARE_SLEEP. + * struct ti_sci_msg_req_prepare_sleep - Request for TISCI_MSG_PREPARE_SLEEP. * - * @hdr TISCI header to provide ACK/NAK flags to the host. - * @mode Low power mode to enter. - * @ctx_lo Low 32-bits of physical pointer to address to use for context save. - * @ctx_hi High 32-bits of physical pointer to address to use for context save. - * @debug_flags Flags that can be set to halt the sequence during suspend or + * @hdr: TISCI header to provide ACK/NAK flags to the host. + * @mode: Low power mode to enter. + * @ctx_lo: Low 32-bits of physical pointer to address to use for context save. + * @ctx_hi: High 32-bits of physical pointer to address to use for context save. + * @debug_flags: Flags that can be set to halt the sequence during suspend or * resume to allow JTAG connection and debug. * * This message is used as the first step of entering a low power mode. It @@ -610,7 +610,7 @@ struct ti_sci_msg_req_prepare_sleep { } __packed; /** - * struct tisci_msg_set_io_isolation_req - Request for TI_SCI_MSG_SET_IO_ISOLATION. + * struct ti_sci_msg_req_set_io_isolation - Request for TI_SCI_MSG_SET_IO_ISOLATION. * * @hdr: Generic header * @state: The deseared state of the IO isolation. @@ -676,7 +676,7 @@ struct ti_sci_msg_req_lpm_set_device_constraint { * TISCI_MSG_LPM_SET_LATENCY_CONSTRAINT. * * @hdr: TISCI header to provide ACK/NAK flags to the host. - * @wkup_latency: The maximum acceptable latency to wake up from low power mode + * @latency: The maximum acceptable latency to wake up from low power mode * in milliseconds. The deeper the state, the higher the latency. * @state: The desired state of wakeup latency constraint: set or clear. * @rsvd: Reserved for future use. @@ -855,7 +855,7 @@ struct ti_sci_msg_rm_ring_cfg_req { * UDMAP transmit channels mapped to source threads will have their * TCHAN_THRD_ID register programmed with the destination thread if the pairing * is successful. - + * * @dst_thread: PSI-L destination thread ID within the PSI-L System thread map. * PSI-L destination threads start at index 0x8000. The request is NACK'd if * the destination thread is not greater than or equal to 0x8000. @@ -1000,7 +1000,8 @@ struct rm_ti_sci_msg_udmap_rx_flow_opt_cfg { } __packed; /** - * Configures a Navigator Subsystem UDMAP transmit channel + * struct ti_sci_msg_rm_udmap_tx_ch_cfg_req - Configures a + * Navigator Subsystem UDMAP transmit channel * * Configures the non-real-time registers of a Navigator Subsystem UDMAP * transmit channel. The channel index must be assigned to the host defined @@ -1128,7 +1129,8 @@ struct ti_sci_msg_rm_udmap_tx_ch_cfg_req { } __packed; /** - * Configures a Navigator Subsystem UDMAP receive channel + * struct ti_sci_msg_rm_udmap_rx_ch_cfg_req - Configures a + * Navigator Subsystem UDMAP receive channel * * Configures the non-real-time registers of a Navigator Subsystem UDMAP * receive channel. The channel index must be assigned to the host defined @@ -1247,7 +1249,8 @@ struct ti_sci_msg_rm_udmap_rx_ch_cfg_req { } __packed; /** - * Configures a Navigator Subsystem UDMAP receive flow + * struct ti_sci_msg_rm_udmap_flow_cfg_req - Configures a + * Navigator Subsystem UDMAP receive flow * * Configures a Navigator Subsystem UDMAP receive flow's registers. * Configuration does not include the flow registers which handle size-based @@ -1258,7 +1261,7 @@ struct ti_sci_msg_rm_udmap_rx_ch_cfg_req { * * @hdr: Standard TISCI header * - * @valid_params + * @valid_params: * Bitfield defining validity of rx flow configuration parameters. The * rx flow configuration fields are not valid, and will not be used for flow * configuration, if their corresponding valid bit is zero. Valid bit usage: From ce39d255dea5ff7d618ef44f61776545481793a1 Mon Sep 17 00:00:00 2001 From: SungMin Park Date: Wed, 29 Oct 2025 18:37:31 +0530 Subject: [PATCH 008/171] dt-bindings: samsung: exynos-pmu: Add compatible for ARTPEC-9 SoC Add Axis ARTPEC-9 pmu compatible to the bindings documentation. It reuses the older samsung,exynos7-pmu design. Signed-off-by: SungMin Park Signed-off-by: Ravi Patel Acked-by: Conor Dooley Link: https://patch.msgid.link/20251029130731.51305-5-ravi.patel@samsung.com Signed-off-by: Krzysztof Kozlowski --- Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml index 6de47489ee42..0d52b0e9bc17 100644 --- a/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml +++ b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml @@ -52,6 +52,7 @@ properties: - const: syscon - items: - enum: + - axis,artpec9-pmu - samsung,exynos2200-pmu - samsung,exynos7870-pmu - samsung,exynos7885-pmu From 6cfa038bddd710f544076ea2ef7792fc82fbedd6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 21 Nov 2025 17:46:22 +0100 Subject: [PATCH 009/171] memory: mtk-smi: fix device leaks on common probe Make sure to drop the reference taken when looking up the SMI device during common probe on late probe failure (e.g. probe deferral) and on driver unbind. Fixes: 47404757702e ("memory: mtk-smi: Add device link for smi-sub-common") Fixes: 038ae37c510f ("memory: mtk-smi: add missing put_device() call in mtk_smi_device_link_common") Cc: stable@vger.kernel.org # 5.16: 038ae37c510f Cc: stable@vger.kernel.org # 5.16 Cc: Yong Wu Cc: Miaoqian Lin Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20251121164624.13685-2-johan@kernel.org Signed-off-by: Krzysztof Kozlowski --- drivers/memory/mtk-smi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c index 733e22f695ab..dd6150d200e8 100644 --- a/drivers/memory/mtk-smi.c +++ b/drivers/memory/mtk-smi.c @@ -674,6 +674,7 @@ static int mtk_smi_larb_probe(struct platform_device *pdev) err_pm_disable: pm_runtime_disable(dev); device_link_remove(dev, larb->smi_common_dev); + put_device(larb->smi_common_dev); return ret; } @@ -917,6 +918,7 @@ static void mtk_smi_common_remove(struct platform_device *pdev) if (common->plat->type == MTK_SMI_GEN2_SUB_COMM) device_link_remove(&pdev->dev, common->smi_common_dev); pm_runtime_disable(&pdev->dev); + put_device(common->smi_common_dev); } static int __maybe_unused mtk_smi_common_resume(struct device *dev) From 9dae65913b32d05dbc8ff4b8a6bf04a0e49a8eb6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 21 Nov 2025 17:46:23 +0100 Subject: [PATCH 010/171] memory: mtk-smi: fix device leak on larb probe Make sure to drop the reference taken when looking up the SMI device during larb probe on late probe failure (e.g. probe deferral) and on driver unbind. Fixes: cc8bbe1a8312 ("memory: mediatek: Add SMI driver") Fixes: 038ae37c510f ("memory: mtk-smi: add missing put_device() call in mtk_smi_device_link_common") Cc: stable@vger.kernel.org # 4.6: 038ae37c510f Cc: stable@vger.kernel.org # 4.6 Cc: Yong Wu Cc: Miaoqian Lin Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20251121164624.13685-3-johan@kernel.org Signed-off-by: Krzysztof Kozlowski --- drivers/memory/mtk-smi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c index dd6150d200e8..3609bfd3c64b 100644 --- a/drivers/memory/mtk-smi.c +++ b/drivers/memory/mtk-smi.c @@ -685,6 +685,7 @@ static void mtk_smi_larb_remove(struct platform_device *pdev) device_link_remove(&pdev->dev, larb->smi_common_dev); pm_runtime_disable(&pdev->dev); component_del(&pdev->dev, &mtk_smi_larb_component_ops); + put_device(larb->smi_common_dev); } static int __maybe_unused mtk_smi_larb_resume(struct device *dev) From 78da7027e2a99edea0d7e97b25d75be4698a8728 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 21 Nov 2025 17:46:24 +0100 Subject: [PATCH 011/171] memory: mtk-smi: clean up device link creation Clean up device link creation by bailing out early in case the SMI platform device lookup fails. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20251121164624.13685-4-johan@kernel.org Signed-off-by: Krzysztof Kozlowski --- drivers/memory/mtk-smi.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c index 3609bfd3c64b..aaeba8ab211e 100644 --- a/drivers/memory/mtk-smi.c +++ b/drivers/memory/mtk-smi.c @@ -595,25 +595,28 @@ static int mtk_smi_device_link_common(struct device *dev, struct device **com_de smi_com_pdev = of_find_device_by_node(smi_com_node); of_node_put(smi_com_node); - if (smi_com_pdev) { - /* smi common is the supplier, Make sure it is ready before */ - if (!platform_get_drvdata(smi_com_pdev)) { - put_device(&smi_com_pdev->dev); - return -EPROBE_DEFER; - } - smi_com_dev = &smi_com_pdev->dev; - link = device_link_add(dev, smi_com_dev, - DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS); - if (!link) { - dev_err(dev, "Unable to link smi-common dev\n"); - put_device(&smi_com_pdev->dev); - return -ENODEV; - } - *com_dev = smi_com_dev; - } else { + if (!smi_com_pdev) { dev_err(dev, "Failed to get the smi_common device\n"); return -EINVAL; } + + /* smi common is the supplier, Make sure it is ready before */ + if (!platform_get_drvdata(smi_com_pdev)) { + put_device(&smi_com_pdev->dev); + return -EPROBE_DEFER; + } + + smi_com_dev = &smi_com_pdev->dev; + link = device_link_add(dev, smi_com_dev, + DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS); + if (!link) { + dev_err(dev, "Unable to link smi-common dev\n"); + put_device(&smi_com_pdev->dev); + return -ENODEV; + } + + *com_dev = smi_com_dev; + return 0; } From f6753869a25ebf0022fe531a31e4fd2435d5e1a5 Mon Sep 17 00:00:00 2001 From: Artem Shimko Date: Sun, 23 Nov 2025 19:35:57 +0300 Subject: [PATCH 012/171] firmware: arm_scmi: Refactor reset domain handling Introduce scmi_reset_domain_lookup() to centralize domain ID validation and unify error reporting behaviour across the SCMI reset protocol. All reset domain operations are updated to use the new helper, removing duplicated validation logic and ensuring consistent handling of invalid domain IDs and lookup failures. This simplifies the internal flow and improves robustness of the reset protocol implementation. Suggested-by: Cristian Marussi Signed-off-by: Artem Shimko Message-Id: <20251123163557.230530-1-a.shimko.dev@gmail.com> Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/reset.c | 50 ++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c index 0aa82b96f41b..7c4550a3e258 100644 --- a/drivers/firmware/arm_scmi/reset.c +++ b/drivers/firmware/arm_scmi/reset.c @@ -98,6 +98,17 @@ static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph, return ret; } +static struct reset_dom_info * +scmi_reset_domain_lookup(const struct scmi_protocol_handle *ph, u32 domain) +{ + struct scmi_reset_info *pi = ph->get_priv(ph); + + if (domain >= pi->num_domains) + return ERR_PTR(-EINVAL); + + return pi->dom_info + domain; +} + static int scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph, struct scmi_reset_info *pinfo, @@ -156,20 +167,25 @@ static int scmi_reset_num_domains_get(const struct scmi_protocol_handle *ph) static const char * scmi_reset_name_get(const struct scmi_protocol_handle *ph, u32 domain) { - struct scmi_reset_info *pi = ph->get_priv(ph); + struct reset_dom_info *dom_info; - struct reset_dom_info *dom = pi->dom_info + domain; + dom_info = scmi_reset_domain_lookup(ph, domain); + if (IS_ERR(dom_info)) + return "unknown"; - return dom->name; + return dom_info->name; } static int scmi_reset_latency_get(const struct scmi_protocol_handle *ph, u32 domain) { - struct scmi_reset_info *pi = ph->get_priv(ph); - struct reset_dom_info *dom = pi->dom_info + domain; + struct reset_dom_info *dom_info; - return dom->latency_us; + dom_info = scmi_reset_domain_lookup(ph, domain); + if (IS_ERR(dom_info)) + return PTR_ERR(dom_info); + + return dom_info->latency_us; } static int scmi_domain_reset(const struct scmi_protocol_handle *ph, u32 domain, @@ -178,14 +194,13 @@ static int scmi_domain_reset(const struct scmi_protocol_handle *ph, u32 domain, int ret; struct scmi_xfer *t; struct scmi_msg_reset_domain_reset *dom; - struct scmi_reset_info *pi = ph->get_priv(ph); - struct reset_dom_info *rdom; + struct reset_dom_info *dom_info; - if (domain >= pi->num_domains) - return -EINVAL; + dom_info = scmi_reset_domain_lookup(ph, domain); + if (IS_ERR(dom_info)) + return PTR_ERR(dom_info); - rdom = pi->dom_info + domain; - if (rdom->async_reset && flags & AUTONOMOUS_RESET) + if (dom_info->async_reset && flags & AUTONOMOUS_RESET) flags |= ASYNCHRONOUS_RESET; ret = ph->xops->xfer_get_init(ph, RESET, sizeof(*dom), 0, &t); @@ -238,15 +253,16 @@ static const struct scmi_reset_proto_ops reset_proto_ops = { static bool scmi_reset_notify_supported(const struct scmi_protocol_handle *ph, u8 evt_id, u32 src_id) { - struct reset_dom_info *dom; - struct scmi_reset_info *pi = ph->get_priv(ph); + struct reset_dom_info *dom_info; - if (evt_id != SCMI_EVENT_RESET_ISSUED || src_id >= pi->num_domains) + if (evt_id != SCMI_EVENT_RESET_ISSUED) return false; - dom = pi->dom_info + src_id; + dom_info = scmi_reset_domain_lookup(ph, src_id); + if (IS_ERR(dom_info)) + return false; - return dom->reset_notify; + return dom_info->reset_notify; } static int scmi_reset_notify(const struct scmi_protocol_handle *ph, From a3c46c82d8a2075dfe2a2af1431486c8b128e93c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 27 Oct 2025 18:16:06 +0300 Subject: [PATCH 013/171] firmware: arm_scmi: Move boiler plate code into the get info functions This code to check whether the selector is valid and if the item has already been recorded in the array can be moved to the scmi_pinctrl_get_function_info() type functions. That way it's in one place instead of duplicated in each of the callers. Remove the check for if "pi->nr_groups == 0" because if that were the case then "selector >= pi->nr_groups" would already be true. It already was not checked for the pin case so this makes things a bit more uniform. Also remove the check for if (!pin) since pin is an offset into the middle of an array and can't be NULL. Signed-off-by: Dan Carpenter Reviewed-by: Cristian Marussi Message-Id: <287b5302f583e3535d50617ec3b0856e38253171.1761576798.git.dan.carpenter@linaro.org> Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/pinctrl.c | 108 +++++++++++++--------------- 1 file changed, 48 insertions(+), 60 deletions(-) diff --git a/drivers/firmware/arm_scmi/pinctrl.c b/drivers/firmware/arm_scmi/pinctrl.c index 3855c98caf06..d18c2d248f04 100644 --- a/drivers/firmware/arm_scmi/pinctrl.c +++ b/drivers/firmware/arm_scmi/pinctrl.c @@ -596,11 +596,19 @@ static int scmi_pinctrl_pin_free(const struct scmi_protocol_handle *ph, u32 pin) } static int scmi_pinctrl_get_group_info(const struct scmi_protocol_handle *ph, - u32 selector, - struct scmi_group_info *group) + u32 selector) { + struct scmi_pinctrl_info *pi = ph->get_priv(ph); + struct scmi_group_info *group; int ret; + if (selector >= pi->nr_groups) + return -EINVAL; + + group = &pi->groups[selector]; + if (group->present) + return 0; + ret = scmi_pinctrl_attributes(ph, GROUP_TYPE, selector, group->name, &group->nr_pins); if (ret) @@ -632,21 +640,14 @@ static int scmi_pinctrl_get_group_name(const struct scmi_protocol_handle *ph, u32 selector, const char **name) { struct scmi_pinctrl_info *pi = ph->get_priv(ph); + int ret; if (!name) return -EINVAL; - if (selector >= pi->nr_groups || pi->nr_groups == 0) - return -EINVAL; - - if (!pi->groups[selector].present) { - int ret; - - ret = scmi_pinctrl_get_group_info(ph, selector, - &pi->groups[selector]); - if (ret) - return ret; - } + ret = scmi_pinctrl_get_group_info(ph, selector); + if (ret) + return ret; *name = pi->groups[selector].name; @@ -658,21 +659,14 @@ static int scmi_pinctrl_group_pins_get(const struct scmi_protocol_handle *ph, u32 *nr_pins) { struct scmi_pinctrl_info *pi = ph->get_priv(ph); + int ret; if (!pins || !nr_pins) return -EINVAL; - if (selector >= pi->nr_groups || pi->nr_groups == 0) - return -EINVAL; - - if (!pi->groups[selector].present) { - int ret; - - ret = scmi_pinctrl_get_group_info(ph, selector, - &pi->groups[selector]); - if (ret) - return ret; - } + ret = scmi_pinctrl_get_group_info(ph, selector); + if (ret) + return ret; *pins = pi->groups[selector].group_pins; *nr_pins = pi->groups[selector].nr_pins; @@ -681,11 +675,19 @@ static int scmi_pinctrl_group_pins_get(const struct scmi_protocol_handle *ph, } static int scmi_pinctrl_get_function_info(const struct scmi_protocol_handle *ph, - u32 selector, - struct scmi_function_info *func) + u32 selector) { + struct scmi_pinctrl_info *pi = ph->get_priv(ph); + struct scmi_function_info *func; int ret; + if (selector >= pi->nr_functions) + return -EINVAL; + + func = &pi->functions[selector]; + if (func->present) + return 0; + ret = scmi_pinctrl_attributes(ph, FUNCTION_TYPE, selector, func->name, &func->nr_groups); if (ret) @@ -716,21 +718,14 @@ static int scmi_pinctrl_get_function_name(const struct scmi_protocol_handle *ph, u32 selector, const char **name) { struct scmi_pinctrl_info *pi = ph->get_priv(ph); + int ret; if (!name) return -EINVAL; - if (selector >= pi->nr_functions || pi->nr_functions == 0) - return -EINVAL; - - if (!pi->functions[selector].present) { - int ret; - - ret = scmi_pinctrl_get_function_info(ph, selector, - &pi->functions[selector]); - if (ret) - return ret; - } + ret = scmi_pinctrl_get_function_info(ph, selector); + if (ret) + return ret; *name = pi->functions[selector].name; return 0; @@ -742,21 +737,14 @@ scmi_pinctrl_function_groups_get(const struct scmi_protocol_handle *ph, const u32 **groups) { struct scmi_pinctrl_info *pi = ph->get_priv(ph); + int ret; if (!groups || !nr_groups) return -EINVAL; - if (selector >= pi->nr_functions || pi->nr_functions == 0) - return -EINVAL; - - if (!pi->functions[selector].present) { - int ret; - - ret = scmi_pinctrl_get_function_info(ph, selector, - &pi->functions[selector]); - if (ret) - return ret; - } + ret = scmi_pinctrl_get_function_info(ph, selector); + if (ret) + return ret; *groups = pi->functions[selector].groups; *nr_groups = pi->functions[selector].nr_groups; @@ -771,13 +759,19 @@ static int scmi_pinctrl_mux_set(const struct scmi_protocol_handle *ph, } static int scmi_pinctrl_get_pin_info(const struct scmi_protocol_handle *ph, - u32 selector, struct scmi_pin_info *pin) + u32 selector) { + struct scmi_pinctrl_info *pi = ph->get_priv(ph); + struct scmi_pin_info *pin; int ret; - if (!pin) + if (selector >= pi->nr_pins) return -EINVAL; + pin = &pi->pins[selector]; + if (pin->present) + return 0; + ret = scmi_pinctrl_attributes(ph, PIN_TYPE, selector, pin->name, NULL); if (ret) return ret; @@ -790,20 +784,14 @@ static int scmi_pinctrl_get_pin_name(const struct scmi_protocol_handle *ph, u32 selector, const char **name) { struct scmi_pinctrl_info *pi = ph->get_priv(ph); + int ret; if (!name) return -EINVAL; - if (selector >= pi->nr_pins) - return -EINVAL; - - if (!pi->pins[selector].present) { - int ret; - - ret = scmi_pinctrl_get_pin_info(ph, selector, &pi->pins[selector]); - if (ret) - return ret; - } + ret = scmi_pinctrl_get_pin_info(ph, selector); + if (ret) + return ret; *name = pi->pins[selector].name; From 6c2fd7a71e7ab490a0a9d591486d923712012f59 Mon Sep 17 00:00:00 2001 From: Vivek Aknurwar Date: Tue, 14 Oct 2025 00:34:54 -0700 Subject: [PATCH 014/171] firmware: arm_scmi: Increase performance MAX_OPPS limit to 64 Some platforms expose more than 32 operating performance points (OPPs) per performance domain via the SCMI performance protocol, but the driver currently limits the number of OPPs it can handle to 32 via MAX_OPPS. Bump MAX_OPPS to 64 so that these platforms can register all their performance levels. This is an internal limit in the driver only and does not affect the SCMI protocol ABI. 64 is chosen as the next power of two above the existing limit. Signed-off-by: Vivek Aknurwar Reviewed-by: Konrad Dybcio Reviewed-by: Alexey Klimov Message-Id: <20251014073454.461999-1-vivek.aknurwar@oss.qualcomm.com> (sudeep.holla: Updated commit log to reflect driver limitation rather than spec) Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/perf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 683fd9b85c5c..2249ef7fe790 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -27,7 +27,7 @@ /* Updated only after ALL the mandatory features for that version are merged */ #define SCMI_PROTOCOL_SUPPORTED_VERSION 0x40000 -#define MAX_OPPS 32 +#define MAX_OPPS 64 enum scmi_performance_protocol_cmd { PERF_DOMAIN_ATTRIBUTES = 0x3, From bd0b8028ce5fbc7d9f5c2751c20661b0d8114e60 Mon Sep 17 00:00:00 2001 From: Pankaj Patil Date: Thu, 11 Dec 2025 14:32:35 +0530 Subject: [PATCH 015/171] dt-bindings: cache: qcom,llcc: Document Glymur LLCC block Document the Last Level Cache Controller on Glymur SoC Glymur LLCC has 12 base register regions and an additional AND, OR broadcast region, total 14 register regions Increase maxItems for reg and reg-names to allow 14 entries for Glymur Reviewed-by: Krzysztof Kozlowski Signed-off-by: Pankaj Patil Link: https://lore.kernel.org/r/20251211-glymur_llcc_enablement-v3-1-43457b354b0d@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/cache/qcom,llcc.yaml | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/cache/qcom,llcc.yaml b/Documentation/devicetree/bindings/cache/qcom,llcc.yaml index a620a2ff5c56..4e99c405aea3 100644 --- a/Documentation/devicetree/bindings/cache/qcom,llcc.yaml +++ b/Documentation/devicetree/bindings/cache/qcom,llcc.yaml @@ -20,6 +20,7 @@ description: | properties: compatible: enum: + - qcom,glymur-llcc - qcom,ipq5424-llcc - qcom,kaanapali-llcc - qcom,qcs615-llcc @@ -46,11 +47,11 @@ properties: reg: minItems: 1 - maxItems: 10 + maxItems: 14 reg-names: minItems: 1 - maxItems: 10 + maxItems: 14 interrupts: maxItems: 1 @@ -84,6 +85,48 @@ allOf: items: - const: llcc0_base + - if: + properties: + compatible: + contains: + enum: + - qcom,glymur-llcc + then: + properties: + reg: + items: + - description: LLCC0 base register region + - description: LLCC1 base register region + - description: LLCC2 base register region + - description: LLCC3 base register region + - description: LLCC4 base register region + - description: LLCC5 base register region + - description: LLCC6 base register region + - description: LLCC7 base register region + - description: LLCC8 base register region + - description: LLCC9 base register region + - description: LLCC10 base register region + - description: LLCC11 base register region + - description: LLCC broadcast base register region + - description: LLCC broadcast AND register region + reg-names: + items: + - const: llcc0_base + - const: llcc1_base + - const: llcc2_base + - const: llcc3_base + - const: llcc4_base + - const: llcc5_base + - const: llcc6_base + - const: llcc7_base + - const: llcc7_base + - const: llcc8_base + - const: llcc9_base + - const: llcc10_base + - const: llcc11_base + - const: llcc_broadcast_base + - const: llcc_broadcast_and_base + - if: properties: compatible: From 0418592550c6a370b2b8a5cbebd53fb7dd63d837 Mon Sep 17 00:00:00 2001 From: Pankaj Patil Date: Thu, 11 Dec 2025 14:32:36 +0530 Subject: [PATCH 016/171] soc: qcom: llcc-qcom: Add support for Glymur Add system cache table(SCT) and configs for Glymur SoC Updated the list of usecase id's to enable additional clients for Glymur Reviewed-by: Konrad Dybcio Signed-off-by: Pankaj Patil Link: https://lore.kernel.org/r/20251211-glymur_llcc_enablement-v3-2-43457b354b0d@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/llcc-qcom.c | 207 +++++++++++++++++++++++++++++ include/linux/soc/qcom/llcc-qcom.h | 4 + 2 files changed, 211 insertions(+) diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 13e174267294..1abfda7a58f2 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -182,6 +182,197 @@ enum llcc_reg_offset { LLCC_TRP_WRS_CACHEABLE_EN, }; +static const struct llcc_slice_config glymur_data[] = { + { + .usecase_id = LLCC_CPUSS, + .slice_id = 1, + .max_cap = 7680, + .priority = 1, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .vict_prio = true, + .activate_on_init = true, + }, { + .usecase_id = LLCC_VIDSC0, + .slice_id = 2, + .max_cap = 512, + .priority = 3, + .fixed_size = true, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .vict_prio = true, + }, { + .usecase_id = LLCC_AUDIO, + .slice_id = 6, + .max_cap = 1024, + .priority = 1, + .fixed_size = true, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .vict_prio = true, + }, { + .usecase_id = LLCC_VIDSC1, + .slice_id = 4, + .max_cap = 512, + .priority = 3, + .fixed_size = true, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .vict_prio = true, + }, { + .usecase_id = LLCC_CMPT, + .slice_id = 10, + .max_cap = 7680, + .priority = 1, + .fixed_size = true, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .vict_prio = true, + }, { + .usecase_id = LLCC_GPUHTW, + .slice_id = 11, + .max_cap = 512, + .priority = 1, + .fixed_size = true, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .vict_prio = true, + }, { + .usecase_id = LLCC_GPU, + .slice_id = 9, + .max_cap = 7680, + .priority = 1, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .write_scid_en = true, + .write_scid_cacheable_en = true, + .stale_en = true, + .vict_prio = true, + }, { + .usecase_id = LLCC_MMUHWT, + .slice_id = 18, + .max_cap = 768, + .priority = 1, + .fixed_size = true, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .vict_prio = true, + .activate_on_init = true, + }, { + .usecase_id = LLCC_AUDHW, + .slice_id = 22, + .max_cap = 1024, + .priority = 1, + .fixed_size = true, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .vict_prio = true, + }, { + .usecase_id = LLCC_CVP, + .slice_id = 8, + .max_cap = 64, + .priority = 3, + .fixed_size = true, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .vict_prio = true, + }, { + .usecase_id = LLCC_WRCACHE, + .slice_id = 31, + .max_cap = 1536, + .priority = 1, + .fixed_size = true, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .vict_prio = true, + .activate_on_init = true, + }, { + .usecase_id = LLCC_CMPTHCP, + .slice_id = 17, + .max_cap = 256, + .priority = 3, + .fixed_size = true, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .vict_prio = true, + }, { + .usecase_id = LLCC_LCPDARE, + .slice_id = 30, + .max_cap = 768, + .priority = 3, + .fixed_size = true, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .alloc_oneway_en = true, + .vict_prio = true, + .activate_on_init = true, + }, { + .usecase_id = LLCC_AENPU, + .slice_id = 3, + .max_cap = 3072, + .priority = 1, + .fixed_size = true, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .cache_mode = 2, + .vict_prio = true, + }, { + .usecase_id = LLCC_ISLAND1, + .slice_id = 12, + .max_cap = 5632, + .priority = 7, + .fixed_size = true, + .bonus_ways = 0x0, + .res_ways = 0x7FF, + .vict_prio = true, + }, { + .usecase_id = LLCC_VIDVSP, + .slice_id = 28, + .max_cap = 256, + .priority = 3, + .fixed_size = true, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .vict_prio = true, + }, { + .usecase_id = LLCC_OOBM_NS, + .slice_id = 5, + .max_cap = 512, + .priority = 1, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .vict_prio = true, + }, { + .usecase_id = LLCC_CPUSS_OPP, + .slice_id = 32, + .max_cap = 0, + .fixed_size = true, + .bonus_ways = 0x0, + .res_ways = 0x0, + .vict_prio = true, + .activate_on_init = true, + }, { + .usecase_id = LLCC_PCIE_TCU, + .slice_id = 19, + .max_cap = 256, + .priority = 1, + .fixed_size = true, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .vict_prio = true, + .activate_on_init = true, + }, { + .usecase_id = LLCC_VIDSC_VSP1, + .slice_id = 29, + .max_cap = 256, + .priority = 3, + .fixed_size = true, + .bonus_ways = 0xFFF, + .res_ways = 0x0, + .vict_prio = true, + } +}; + static const struct llcc_slice_config ipq5424_data[] = { { .usecase_id = LLCC_CPUSS, @@ -3872,6 +4063,16 @@ static const struct qcom_llcc_config kaanapali_cfg[] = { }, }; +static const struct qcom_llcc_config glymur_cfg[] = { + { + .sct_data = glymur_data, + .size = ARRAY_SIZE(glymur_data), + .reg_offset = llcc_v6_reg_offset, + .edac_reg_offset = &llcc_v2_1_edac_reg_offset, + .no_edac = true, + }, +}; + static const struct qcom_llcc_config qcs615_cfg[] = { { .sct_data = qcs615_data, @@ -4103,6 +4304,11 @@ static const struct qcom_sct_config kaanapali_cfgs = { .num_config = ARRAY_SIZE(kaanapali_cfg), }; +static const struct qcom_sct_config glymur_cfgs = { + .llcc_config = glymur_cfg, + .num_config = ARRAY_SIZE(glymur_cfg), +}; + static const struct qcom_sct_config qcs615_cfgs = { .llcc_config = qcs615_cfg, .num_config = ARRAY_SIZE(qcs615_cfg), @@ -4941,6 +5147,7 @@ err: } static const struct of_device_id qcom_llcc_of_match[] = { + { .compatible = "qcom,glymur-llcc", .data = &glymur_cfgs }, { .compatible = "qcom,ipq5424-llcc", .data = &ipq5424_cfgs}, { .compatible = "qcom,kaanapali-llcc", .data = &kaanapali_cfgs}, { .compatible = "qcom,qcs615-llcc", .data = &qcs615_cfgs}, diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h index 0287f9182c4d..8243ab3a12a8 100644 --- a/include/linux/soc/qcom/llcc-qcom.h +++ b/include/linux/soc/qcom/llcc-qcom.h @@ -74,13 +74,17 @@ #define LLCC_CAMSRTIP 73 #define LLCC_CAMRTRF 74 #define LLCC_CAMSRTRF 75 +#define LLCC_OOBM_NS 81 +#define LLCC_OOBM_S 82 #define LLCC_VIDEO_APV 83 #define LLCC_COMPUTE1 87 #define LLCC_CPUSS_OPP 88 #define LLCC_CPUSSMPAM 89 +#define LLCC_VIDSC_VSP1 91 #define LLCC_CAM_IPE_STROV 92 #define LLCC_CAM_OFE_STROV 93 #define LLCC_CPUSS_HEU 94 +#define LLCC_PCIE_TCU 97 #define LLCC_MDM_PNG_FIXED 100 /** From 0fe01a7955f4fef97e7cc6d14bfc5931c660402b Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Tue, 9 Dec 2025 08:45:37 +0100 Subject: [PATCH 017/171] soc: qcom: smem: handle ENOMEM error during probe Fail the driver probe if the region can't be mapped Signed-off-by: Jorge Ramirez-Ortiz Fixes: 20bb6c9de1b7 ("soc: qcom: smem: map only partitions used by local HOST") Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20251209074610.3751781-1-jorge.ramirez@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/smem.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index fef840b54574..088b2bbee9e6 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -1219,7 +1219,9 @@ static int qcom_smem_probe(struct platform_device *pdev) smem->item_count = qcom_smem_get_item_count(smem); break; case SMEM_GLOBAL_HEAP_VERSION: - qcom_smem_map_global(smem, size); + ret = qcom_smem_map_global(smem, size); + if (ret < 0) + return ret; smem->item_count = SMEM_ITEM_COUNT; break; default: From a707eda330b932bcf698be9460e54e2f389e24b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 15 Dec 2025 15:16:31 +0100 Subject: [PATCH 018/171] tee: Add some helpers to reduce boilerplate for tee client drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to platform drivers (and others) create dedicated register and unregister functions and a macro to simplify modules that only need to handle driver registration in their init and exit handlers. Reviewed-by: Sumit Garg Signed-off-by: Uwe Kleine-König Signed-off-by: Jens Wiklander --- drivers/tee/tee_core.c | 16 ++++++++++++++++ include/linux/tee_drv.h | 9 +++++++++ 2 files changed, 25 insertions(+) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index d65d47cc154e..51379f7fc5d5 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -1405,6 +1405,22 @@ const struct bus_type tee_bus_type = { }; EXPORT_SYMBOL_GPL(tee_bus_type); +int __tee_client_driver_register(struct tee_client_driver *tee_driver, + struct module *owner) +{ + tee_driver->driver.owner = owner; + tee_driver->driver.bus = &tee_bus_type; + + return driver_register(&tee_driver->driver); +} +EXPORT_SYMBOL_GPL(__tee_client_driver_register); + +void tee_client_driver_unregister(struct tee_client_driver *tee_driver) +{ + driver_unregister(&tee_driver->driver); +} +EXPORT_SYMBOL_GPL(tee_client_driver_unregister); + static int __init tee_init(void) { int rc; diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 88a6f9697c89..850c03b2cdea 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -322,4 +322,13 @@ struct tee_client_driver { #define to_tee_client_driver(d) \ container_of_const(d, struct tee_client_driver, driver) +#define tee_client_driver_register(drv) \ + __tee_client_driver_register(drv, THIS_MODULE) +int __tee_client_driver_register(struct tee_client_driver *, struct module *); +void tee_client_driver_unregister(struct tee_client_driver *); + +#define module_tee_client_driver(__tee_client_driver) \ + module_driver(__tee_client_driver, tee_client_driver_register, \ + tee_client_driver_unregister) + #endif /*__TEE_DRV_H*/ From 71a33465726e4125f5ef33705ce5bc7ec1bf5b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 15 Dec 2025 15:16:32 +0100 Subject: [PATCH 019/171] tee: Add probe, remove and shutdown bus callbacks to tee_client_driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a bus specific probe, remove and shutdown function. For now this only allows to get rid of a cast of the generic device to a tee_client device in the drivers and changes the remove prototype to return void---a non-zero return value is ignored anyhow. The objective is to get rid of users of struct device_driver callbacks .probe(), .remove() and .shutdown() to eventually remove these. Until all tee_client drivers are converted this results in a runtime warning about the drivers needing an update because there is a bus probe function and a driver probe function. Signed-off-by: Uwe Kleine-König Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/tee_core.c | 68 +++++++++++++++++++++++++++++++++++++++++ include/linux/tee_drv.h | 3 ++ 2 files changed, 71 insertions(+) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 51379f7fc5d5..a015730664c7 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -1398,19 +1398,87 @@ static int tee_client_device_uevent(const struct device *dev, return add_uevent_var(env, "MODALIAS=tee:%pUb", dev_id); } +static int tee_client_device_probe(struct device *dev) +{ + struct tee_client_device *tcdev = to_tee_client_device(dev); + struct tee_client_driver *drv = to_tee_client_driver(dev->driver); + + if (drv->probe) + return drv->probe(tcdev); + else + return 0; +} + +static void tee_client_device_remove(struct device *dev) +{ + struct tee_client_device *tcdev = to_tee_client_device(dev); + struct tee_client_driver *drv = to_tee_client_driver(dev->driver); + + if (drv->remove) + drv->remove(tcdev); +} + +static void tee_client_device_shutdown(struct device *dev) +{ + struct tee_client_device *tcdev = to_tee_client_device(dev); + struct tee_client_driver *drv = to_tee_client_driver(dev->driver); + + if (dev->driver && drv->shutdown) + drv->shutdown(tcdev); +} + const struct bus_type tee_bus_type = { .name = "tee", .match = tee_client_device_match, .uevent = tee_client_device_uevent, + .probe = tee_client_device_probe, + .remove = tee_client_device_remove, + .shutdown = tee_client_device_shutdown, }; EXPORT_SYMBOL_GPL(tee_bus_type); +static int tee_client_device_probe_legacy(struct tee_client_device *tcdev) +{ + struct device *dev = &tcdev->dev; + struct device_driver *driver = dev->driver; + + return driver->probe(dev); +} + +static void tee_client_device_remove_legacy(struct tee_client_device *tcdev) +{ + struct device *dev = &tcdev->dev; + struct device_driver *driver = dev->driver; + + driver->remove(dev); +} + +static void tee_client_device_shutdown_legacy(struct tee_client_device *tcdev) +{ + struct device *dev = &tcdev->dev; + struct device_driver *driver = dev->driver; + + driver->shutdown(dev); +} + int __tee_client_driver_register(struct tee_client_driver *tee_driver, struct module *owner) { tee_driver->driver.owner = owner; tee_driver->driver.bus = &tee_bus_type; + /* + * Drivers that have callbacks set for tee_driver->driver need updating + * to use the callbacks in tee_driver instead. driver_register() warns + * about that, so no need to warn here, too. + */ + if (!tee_driver->probe && tee_driver->driver.probe) + tee_driver->probe = tee_client_device_probe_legacy; + if (!tee_driver->remove && tee_driver->driver.remove) + tee_driver->remove = tee_client_device_remove_legacy; + if (!tee_driver->shutdown && tee_driver->driver.probe) + tee_driver->shutdown = tee_client_device_shutdown_legacy; + return driver_register(&tee_driver->driver); } EXPORT_SYMBOL_GPL(__tee_client_driver_register); diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 850c03b2cdea..e561a26f537a 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -315,6 +315,9 @@ struct tee_client_device { * @driver: driver structure */ struct tee_client_driver { + int (*probe)(struct tee_client_device *); + void (*remove)(struct tee_client_device *); + void (*shutdown)(struct tee_client_device *); const struct tee_client_device_id *id_table; struct device_driver driver; }; From 71e47245f89502dafb5d944a571ccb5144a52645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 15 Dec 2025 15:16:33 +0100 Subject: [PATCH 020/171] tee: Adapt documentation to cover recent additions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commits introduced some helpers to reduce boilerplate and bus specific callbacks for probe and remove. Adapt the reference example to make use of these. Reviewed-by: Sumit Garg Signed-off-by: Uwe Kleine-König Signed-off-by: Jens Wiklander --- Documentation/driver-api/tee.rst | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/Documentation/driver-api/tee.rst b/Documentation/driver-api/tee.rst index 5eaeb8103988..4d58ac0712c1 100644 --- a/Documentation/driver-api/tee.rst +++ b/Documentation/driver-api/tee.rst @@ -43,24 +43,12 @@ snippet would look like:: MODULE_DEVICE_TABLE(tee, client_id_table); static struct tee_client_driver client_driver = { + .probe = client_probe, + .remove = client_remove, .id_table = client_id_table, .driver = { .name = DRIVER_NAME, - .bus = &tee_bus_type, - .probe = client_probe, - .remove = client_remove, }, }; - static int __init client_init(void) - { - return driver_register(&client_driver.driver); - } - - static void __exit client_exit(void) - { - driver_unregister(&client_driver.driver); - } - - module_init(client_init); - module_exit(client_exit); + module_tee_client_driver(client_driver); From 5422fad3e1cc2293cb3549f8ec02013ea50a9c80 Mon Sep 17 00:00:00 2001 From: Jingyi Wang Date: Tue, 21 Oct 2025 23:00:26 -0700 Subject: [PATCH 021/171] dt-bindings: interrupt-controller: qcom,pdc: Document Kaanapali Power Domain Controller Add a compatible for the Power Domain Controller on Kaanapali platforms. Signed-off-by: Jingyi Wang Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20251021-knp-pdc-v2-1-a38767f5bb8e@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/interrupt-controller/qcom,pdc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml b/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml index 38d0c2d57dd6..b26246de3186 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml @@ -27,6 +27,7 @@ properties: items: - enum: - qcom,glymur-pdc + - qcom,kaanapali-pdc - qcom,qcs615-pdc - qcom,qcs8300-pdc - qcom,qdu1000-pdc From 6c4bbcdad042b876c8e480ed75121756b1acfde7 Mon Sep 17 00:00:00 2001 From: Jingyi Wang Date: Sun, 23 Nov 2025 23:31:54 -0800 Subject: [PATCH 022/171] dt-bindings: sram: Document qcom,kaanapali-imem and its child node On Qualcomm Kaanapali platform, IMEM is a block of SRAM shared across multiple IP blocks which can falk back to "mmio-sram". Documnent it and its child node "qcom,pil-reloc-info" which is used for collecting remoteproc ramdumps. Signed-off-by: Jingyi Wang Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20251123-knp-soc-binding-v4-1-42b349a66c59@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/sram/sram.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/sram/sram.yaml b/Documentation/devicetree/bindings/sram/sram.yaml index 7c1337e159f2..c451140962c8 100644 --- a/Documentation/devicetree/bindings/sram/sram.yaml +++ b/Documentation/devicetree/bindings/sram/sram.yaml @@ -34,6 +34,7 @@ properties: - nvidia,tegra186-sysram - nvidia,tegra194-sysram - nvidia,tegra234-sysram + - qcom,kaanapali-imem - qcom,rpm-msg-ram - rockchip,rk3288-pmu-sram @@ -89,6 +90,7 @@ patternProperties: - arm,juno-scp-shmem - arm,scmi-shmem - arm,scp-shmem + - qcom,pil-reloc-info - renesas,smp-sram - rockchip,rk3066-smp-sram - samsung,exynos4210-sysram From 6d8a0031d92d56f0cf57947df408d5cafd57bb22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 15 Dec 2025 15:16:36 +0100 Subject: [PATCH 023/171] rtc: optee: Migrate to use tee specific driver registration function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tee subsystem recently got a set of dedicated functions to register (and unregister) a tee driver. Make use of them. These care for setting the driver's bus (so the explicit assignment can be dropped) and the driver owner (which is an improvement this driver benefits from). Reviewed-by: Sumit Garg Acked-by: Alexandre Belloni Signed-off-by: Uwe Kleine-König Signed-off-by: Jens Wiklander --- drivers/rtc/rtc-optee.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/rtc/rtc-optee.c b/drivers/rtc/rtc-optee.c index 184c6d142801..f924a729ead0 100644 --- a/drivers/rtc/rtc-optee.c +++ b/drivers/rtc/rtc-optee.c @@ -726,25 +726,13 @@ static struct tee_client_driver optee_rtc_driver = { .id_table = optee_rtc_id_table, .driver = { .name = "optee_rtc", - .bus = &tee_bus_type, .probe = optee_rtc_probe, .remove = optee_rtc_remove, .pm = pm_sleep_ptr(&optee_rtc_pm_ops), }, }; -static int __init optee_rtc_mod_init(void) -{ - return driver_register(&optee_rtc_driver.driver); -} - -static void __exit optee_rtc_mod_exit(void) -{ - driver_unregister(&optee_rtc_driver.driver); -} - -module_init(optee_rtc_mod_init); -module_exit(optee_rtc_mod_exit); +module_tee_client_driver(optee_rtc_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Clément Léger "); From 4f32b6341818be62db1ea41ced68ca2f0cce5700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 15 Dec 2025 15:16:37 +0100 Subject: [PATCH 024/171] rtc: optee: Make use of tee bus methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tee bus got dedicated callbacks for probe and remove. Make use of these. This fixes a runtime warning about the driver needing to be converted to the bus methods. Reviewed-by: Sumit Garg Acked-by: Alexandre Belloni Signed-off-by: Uwe Kleine-König Signed-off-by: Jens Wiklander --- drivers/rtc/rtc-optee.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/rtc/rtc-optee.c b/drivers/rtc/rtc-optee.c index f924a729ead0..eefde789d194 100644 --- a/drivers/rtc/rtc-optee.c +++ b/drivers/rtc/rtc-optee.c @@ -547,9 +547,9 @@ static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) return 0; } -static int optee_rtc_probe(struct device *dev) +static int optee_rtc_probe(struct tee_client_device *rtc_device) { - struct tee_client_device *rtc_device = to_tee_client_device(dev); + struct device *dev = &rtc_device->dev; struct tee_ioctl_open_session_arg sess2_arg = {0}; struct tee_ioctl_open_session_arg sess_arg = {0}; struct optee_rtc *priv; @@ -682,8 +682,9 @@ out_ctx: return err; } -static int optee_rtc_remove(struct device *dev) +static void optee_rtc_remove(struct tee_client_device *rtc_device) { + struct device *dev = &rtc_device->dev; struct optee_rtc *priv = dev_get_drvdata(dev); if (priv->features & TA_RTC_FEATURE_ALARM) { @@ -696,8 +697,6 @@ static int optee_rtc_remove(struct device *dev) tee_shm_free(priv->shm); tee_client_close_session(priv->ctx, priv->session_id); tee_client_close_context(priv->ctx); - - return 0; } static int optee_rtc_suspend(struct device *dev) @@ -724,10 +723,10 @@ MODULE_DEVICE_TABLE(tee, optee_rtc_id_table); static struct tee_client_driver optee_rtc_driver = { .id_table = optee_rtc_id_table, + .probe = optee_rtc_probe, + .remove = optee_rtc_remove, .driver = { .name = "optee_rtc", - .probe = optee_rtc_probe, - .remove = optee_rtc_remove, .pm = pm_sleep_ptr(&optee_rtc_pm_ops), }, }; From be4d4543f78074fbebd530ba5109d39a2a34e668 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 18 Dec 2025 14:20:01 +0000 Subject: [PATCH 025/171] firmware: arm_ffa: Correct 32-bit response handling in NOTIFICATION_INFO_GET The FF-A specification allows NOTIFICATION_INFO_GET to return either a 64-bit (FFA_FN64_SUCCESS) or a 32-bit (FFA_SUCCESS) response, depending on whether the firmware chooses the SMC64 or SMC32 calling convention. The driver previously detected the response format by checking ret.a0, but still interpreted the returned ID lists (x3..x17 or w3..w7) as if they always followed the 64-bit SMC64 layout. In the SMC32 case, the upper 32 bits of each argument register are undefined by the calling convention, meaning the driver could read stale or garbage values when parsing notification IDs. This resulted in incorrectly decoded partition/VCPU IDs whenever the FF-A firmware used an SMC32 return path. Fix the issue by: - Introducing logic to map list indices to the correct u16 offsets, depending on whether the response width matches the kernel word size or is a 32-bit response on a 64-bit kernel. - Ensuring that the packed ID list is parsed using the proper layout, avoiding reads from undefined upper halves in the SMC32 case. With this change, NOTIFICATION_INFO_GET now correctly interprets ID list entries regardless of the response width, aligning the driver with the FF-A specification. Fixes: 3522be48d82b ("firmware: arm_ffa: Implement the NOTIFICATION_INFO_GET interface") Reported-by: Sourav Mohapatra Message-Id: <20251218142001.2457111-1-sudeep.holla@arm.com> Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 33 +++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 351780ac8d72..8144f6a9f0e9 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -985,10 +985,27 @@ static void __do_sched_recv_cb(u16 part_id, u16 vcpu, bool is_per_vcpu) } } +/* + * Map logical ID index to the u16 index within the packed ID list. + * + * For native responses (FF-A width == kernel word size), IDs are + * tightly packed: idx -> idx. + * + * For 32-bit responses on a 64-bit kernel, each 64-bit register + * contributes 4 x u16 values but only the lower 2 are defined; the + * upper 2 are garbage. This mapping skips those upper halves: + * 0,1,2,3,4,5,... -> 0,1,4,5,8,9,... + */ +static int list_idx_to_u16_idx(int idx, bool is_native_resp) +{ + return is_native_resp ? idx : idx + 2 * (idx >> 1); +} + static void ffa_notification_info_get(void) { - int idx, list, max_ids, lists_cnt, ids_processed, ids_count[MAX_IDS_64]; - bool is_64b_resp; + int ids_processed, ids_count[MAX_IDS_64]; + int idx, list, max_ids, lists_cnt; + bool is_64b_resp, is_native_resp; ffa_value_t ret; u64 id_list; @@ -1005,6 +1022,7 @@ static void ffa_notification_info_get(void) } is_64b_resp = (ret.a0 == FFA_FN64_SUCCESS); + is_native_resp = (ret.a0 == FFA_FN_NATIVE(SUCCESS)); ids_processed = 0; lists_cnt = FIELD_GET(NOTIFICATION_INFO_GET_ID_COUNT, ret.a2); @@ -1021,12 +1039,16 @@ static void ffa_notification_info_get(void) /* Process IDs */ for (list = 0; list < lists_cnt; list++) { + int u16_idx; u16 vcpu_id, part_id, *packed_id_list = (u16 *)&ret.a3; if (ids_processed >= max_ids - 1) break; - part_id = packed_id_list[ids_processed++]; + u16_idx = list_idx_to_u16_idx(ids_processed, + is_native_resp); + part_id = packed_id_list[u16_idx]; + ids_processed++; if (ids_count[list] == 1) { /* Global Notification */ __do_sched_recv_cb(part_id, 0, false); @@ -1038,7 +1060,10 @@ static void ffa_notification_info_get(void) if (ids_processed >= max_ids - 1) break; - vcpu_id = packed_id_list[ids_processed++]; + u16_idx = list_idx_to_u16_idx(ids_processed, + is_native_resp); + vcpu_id = packed_id_list[u16_idx]; + ids_processed++; __do_sched_recv_cb(part_id, vcpu_id, true); } From dffaa1beea9e7a0d902fc4e25e137afcf1297267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Tue, 18 Nov 2025 16:07:57 +0100 Subject: [PATCH 026/171] dt-bindings: memory: factorise LPDDR props into SDRAM props MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LPDDR and DDR bindings are SDRAM types and are likely to share the same properties (at least for density, io-width and reg). To avoid bindings duplication, factorise the properties. The compatible description has been updated because the MR (Mode registers) used to get manufacturer ID and revision ID are not present in case of DDR. Those information should be in a SPD (Serial Presence Detect) EEPROM in case of DIMM module or are known in case of soldered memory chips as they are in the datasheet of the memory chips. Signed-off-by: Clément Le Goffic Reviewed-by: Krzysztof Kozlowski Signed-off-by: Clément Le Goffic Link: https://patch.msgid.link/20251118-b4-ddr-bindings-v9-1-a033ac5144da@gmail.com Signed-off-by: Krzysztof Kozlowski --- .../ddr/jedec,lpddr-props.yaml | 74 --------------- .../memory-controllers/ddr/jedec,lpddr2.yaml | 2 +- .../memory-controllers/ddr/jedec,lpddr3.yaml | 2 +- .../memory-controllers/ddr/jedec,lpddr4.yaml | 2 +- .../memory-controllers/ddr/jedec,lpddr5.yaml | 2 +- .../ddr/jedec,sdram-props.yaml | 94 +++++++++++++++++++ 6 files changed, 98 insertions(+), 78 deletions(-) delete mode 100644 Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr-props.yaml create mode 100644 Documentation/devicetree/bindings/memory-controllers/ddr/jedec,sdram-props.yaml diff --git a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr-props.yaml b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr-props.yaml deleted file mode 100644 index 30267ce70124..000000000000 --- a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr-props.yaml +++ /dev/null @@ -1,74 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/memory-controllers/ddr/jedec,lpddr-props.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Common properties for LPDDR types - -description: - Different LPDDR types generally use the same properties and only differ in the - range of legal values for each. This file defines the common parts that can be - reused for each type. Nodes using this schema should generally be nested under - an LPDDR channel node. - -maintainers: - - Krzysztof Kozlowski - -properties: - compatible: - description: - Compatible strings can be either explicit vendor names and part numbers - (e.g. elpida,ECB240ABACN), or generated strings of the form - lpddrX-YY,ZZZZ where X is the LPDDR version, YY is the manufacturer ID - (from MR5) and ZZZZ is the revision ID (from MR6 and MR7). Both IDs are - formatted in lower case hexadecimal representation with leading zeroes. - The latter form can be useful when LPDDR nodes are created at runtime by - boot firmware that doesn't have access to static part number information. - - reg: - description: - The rank number of this LPDDR rank when used as a subnode to an LPDDR - channel. - minimum: 0 - maximum: 3 - - revision-id: - $ref: /schemas/types.yaml#/definitions/uint32-array - description: - Revision IDs read from Mode Register 6 and 7. One byte per uint32 cell (i.e. ). - maxItems: 2 - items: - minimum: 0 - maximum: 255 - - density: - $ref: /schemas/types.yaml#/definitions/uint32 - description: - Density in megabits of SDRAM chip. Decoded from Mode Register 8. - enum: - - 64 - - 128 - - 256 - - 512 - - 1024 - - 2048 - - 3072 - - 4096 - - 6144 - - 8192 - - 12288 - - 16384 - - 24576 - - 32768 - - io-width: - $ref: /schemas/types.yaml#/definitions/uint32 - description: - IO bus width in bits of SDRAM chip. Decoded from Mode Register 8. - enum: - - 8 - - 16 - - 32 - -additionalProperties: true diff --git a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr2.yaml b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr2.yaml index a237bc259273..704bbc562528 100644 --- a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr2.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr2.yaml @@ -10,7 +10,7 @@ maintainers: - Krzysztof Kozlowski allOf: - - $ref: jedec,lpddr-props.yaml# + - $ref: jedec,sdram-props.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr3.yaml b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr3.yaml index e328a1195ba6..0d28df3d2bfa 100644 --- a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr3.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr3.yaml @@ -10,7 +10,7 @@ maintainers: - Krzysztof Kozlowski allOf: - - $ref: jedec,lpddr-props.yaml# + - $ref: jedec,sdram-props.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr4.yaml b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr4.yaml index a078892fecee..65aa07861453 100644 --- a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr4.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr4.yaml @@ -10,7 +10,7 @@ maintainers: - Krzysztof Kozlowski allOf: - - $ref: jedec,lpddr-props.yaml# + - $ref: jedec,sdram-props.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr5.yaml b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr5.yaml index e441dac5f154..cf5d5a8e94b3 100644 --- a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr5.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr5.yaml @@ -10,7 +10,7 @@ maintainers: - Krzysztof Kozlowski allOf: - - $ref: jedec,lpddr-props.yaml# + - $ref: jedec,sdram-props.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,sdram-props.yaml b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,sdram-props.yaml new file mode 100644 index 000000000000..fedd66eeb9d5 --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,sdram-props.yaml @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/memory-controllers/ddr/jedec,sdram-props.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Common properties for SDRAM types + +description: + Different SDRAM types generally use the same properties and only differ in the + range of legal values for each. This file defines the common parts that can be + reused for each type. Nodes using this schema should generally be nested under + a SDRAM channel node. + +maintainers: + - Krzysztof Kozlowski + +properties: + compatible: + description: | + Compatible strings can be either explicit vendor names and part numbers + (e.g. elpida,ECB240ABACN), or generated strings of the form + lpddrX-YY,ZZZZ or ddrX-YYYY,AAAA...-ZZ where X, Y, and Z are lowercase + hexadecimal with leading zeroes, and A is lowercase ASCII. + For LPDDR and DDR SDRAM, X is the SDRAM version (2, 3, 4, etc.). + For LPDDR SDRAM: + - YY is the manufacturer ID (from MR5), 1 byte + - ZZZZ is the revision ID (from MR6 and MR7), 2 bytes + For DDR4 SDRAM with SPD, according to JEDEC SPD4.1.2.L-6: + - YYYY is the manufacturer ID, 2 bytes, from bytes 320 and 321 + - AAAA... is the part number, 20 bytes (20 chars) from bytes 329 to 348 + without trailing spaces + - ZZ is the revision ID, 1 byte, from byte 349 + The former form is useful when the SDRAM vendor and part number are + known, for example, when memory is soldered on the board. The latter + form is useful when SDRAM nodes are created at runtime by boot firmware + that doesn't have access to static part number information. + + reg: + description: + The rank number of this memory rank when used as a subnode to an memory + channel. + minimum: 0 + maximum: 3 + + revision-id: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + SDRAM revision ID: + - LPDDR SDRAM, decoded from Mode Registers 6 and 7, always 2 bytes. + - DDR4 SDRAM, decoded from the SPD from byte 349 according to + JEDEC SPD4.1.2.L-6, always 1 byte. + One byte per uint32 cell (e.g., ). + maxItems: 2 + items: + minimum: 0 + maximum: 255 + + density: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Density of the SDRAM chip in megabits: + - LPDDR SDRAM, decoded from Mode Register 8. + - DDR4 SDRAM, decoded from the SPD from bits 3-0 of byte 4 according to + JEDEC SPD4.1.2.L-6. + enum: + - 64 + - 128 + - 256 + - 512 + - 1024 + - 2048 + - 3072 + - 4096 + - 6144 + - 8192 + - 12288 + - 16384 + - 24576 + - 32768 + + io-width: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + I/O bus width in bits of the SDRAM chip: + - LPDDR SDRAM, decoded from Mode Register 8. + - DDR4 SDRAM, decoded from the SPD from bits 2-0 of byte 12 according to + JEDEC SPD4.1.2.L-6. + enum: + - 8 + - 16 + - 32 + +additionalProperties: true From b5c1a217552c3513977a9f1138b05de0bada8a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Tue, 18 Nov 2025 16:07:58 +0100 Subject: [PATCH 027/171] dt-bindings: memory: introduce DDR4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce JEDEC compliant DDR bindings, that use new memory-props binding. The DDR4 compatible can be made of explicit vendor names and part numbers or be of the form "ddrX-YYYY,AAAA...-ZZ" when associated with an SPD, where (according to JEDEC SPD4.1.2.L-6): - YYYY is the manufacturer ID - AAAA... is the part number - ZZ is the revision ID The former form is useful when the SDRAM vendor and part number are known, for example, when memory is soldered on the board. The latter form is useful when SDRAM nodes are created at runtime by boot firmware that doesn't have access to static part number information. Signed-off-by: Clément Le Goffic Signed-off-by: Clément Le Goffic Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20251118-b4-ddr-bindings-v9-2-a033ac5144da@gmail.com Signed-off-by: Krzysztof Kozlowski --- .../memory-controllers/ddr/jedec,ddr4.yaml | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 Documentation/devicetree/bindings/memory-controllers/ddr/jedec,ddr4.yaml diff --git a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,ddr4.yaml b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,ddr4.yaml new file mode 100644 index 000000000000..928961c74026 --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,ddr4.yaml @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/memory-controllers/ddr/jedec,ddr4.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: DDR4 SDRAM compliant to JEDEC JESD79-4D + +maintainers: + - Krzysztof Kozlowski + +allOf: + - $ref: jedec,sdram-props.yaml# + +properties: + compatible: + items: + - pattern: "^ddr4-[0-9a-f]{4},[a-z]{1,20}-[0-9a-f]{2}$" + - const: jedec,ddr4 + +required: + - compatible + - density + - io-width + +unevaluatedProperties: false + +examples: + - | + ddr { + compatible = "ddr4-00ff,azaz-ff", "jedec,ddr4"; + density = <8192>; + io-width = <8>; + }; From 6ab3581ab19fa348b93c85a793e45cd8a80912a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Tue, 18 Nov 2025 16:07:59 +0100 Subject: [PATCH 028/171] dt-bindings: memory: factorise LPDDR channel binding into SDRAM channel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LPDDR, DDR and so SDRAM channels exist and share the same properties, they have a compatible, ranks, and an io-width. Signed-off-by: Clément Le Goffic Reviewed-by: Rob Herring (Arm) Signed-off-by: Clément Le Goffic Link: https://patch.msgid.link/20251118-b4-ddr-bindings-v9-3-a033ac5144da@gmail.com Signed-off-by: Krzysztof Kozlowski --- ...-channel.yaml => jedec,sdram-channel.yaml} | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) rename Documentation/devicetree/bindings/memory-controllers/ddr/{jedec,lpddr-channel.yaml => jedec,sdram-channel.yaml} (83%) diff --git a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr-channel.yaml b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,sdram-channel.yaml similarity index 83% rename from Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr-channel.yaml rename to Documentation/devicetree/bindings/memory-controllers/ddr/jedec,sdram-channel.yaml index 34b5bd153f63..9892da520fe4 100644 --- a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,lpddr-channel.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,sdram-channel.yaml @@ -1,16 +1,17 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/memory-controllers/ddr/jedec,lpddr-channel.yaml# +$id: http://devicetree.org/schemas/memory-controllers/ddr/jedec,sdram-channel.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: LPDDR channel with chip/rank topology description +title: SDRAM channel with chip/rank topology description description: - An LPDDR channel is a completely independent set of LPDDR pins (DQ, CA, CS, - CK, etc.) that connect one or more LPDDR chips to a host system. The main - purpose of this node is to overall LPDDR topology of the system, including the - amount of individual LPDDR chips and the ranks per chip. + A memory channel of SDRAM memory like DDR SDRAM or LPDDR SDRAM is a completely + independent set of pins (DQ, CA, CS, CK, etc.) that connect one or more memory + chips to a host system. The main purpose of this node is to overall memory + topology of the system, including the amount of individual memory chips and + the ranks per chip. maintainers: - Julius Werner @@ -26,14 +27,14 @@ properties: io-width: description: The number of DQ pins in the channel. If this number is different - from (a multiple of) the io-width of the LPDDR chip, that means that + from (a multiple of) the io-width of the SDRAM chip, that means that multiple instances of that type of chip are wired in parallel on this channel (with the channel's DQ pins split up between the different chips, and the CA, CS, etc. pins of the different chips all shorted together). This means that the total physical memory controlled by a channel is equal to the sum of the densities of each rank on the - connected LPDDR chip, times the io-width of the channel divided by - the io-width of the LPDDR chip. + connected SDRAM chip, times the io-width of the channel divided by + the io-width of the SDRAM chip. enum: - 8 - 16 @@ -51,8 +52,8 @@ patternProperties: "^rank@[0-9]+$": type: object description: - Each physical LPDDR chip may have one or more ranks. Ranks are - internal but fully independent sub-units of the chip. Each LPDDR bus + Each physical SDRAM chip may have one or more ranks. Ranks are + internal but fully independent sub-units of the chip. Each SDRAM bus transaction on the channel targets exactly one rank, based on the state of the CS pins. Different ranks may have different densities and timing requirements. From 36ecc8346747b600892e3040e1d0ecb1e939c6e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Tue, 18 Nov 2025 16:08:00 +0100 Subject: [PATCH 029/171] dt-bindings: memory: add DDR4 channel compatible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add in the memory channel binding the DDR4 compatible to support DDR4 memory channel. Signed-off-by: Clément Le Goffic Reviewed-by: Rob Herring (Arm) Signed-off-by: Clément Le Goffic Link: https://patch.msgid.link/20251118-b4-ddr-bindings-v9-4-a033ac5144da@gmail.com Signed-off-by: Krzysztof Kozlowski --- .../memory-controllers/ddr/jedec,sdram-channel.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,sdram-channel.yaml b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,sdram-channel.yaml index 9892da520fe4..866af40b654d 100644 --- a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,sdram-channel.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,sdram-channel.yaml @@ -19,6 +19,7 @@ maintainers: properties: compatible: enum: + - jedec,ddr4-channel - jedec,lpddr2-channel - jedec,lpddr3-channel - jedec,lpddr4-channel @@ -61,6 +62,15 @@ patternProperties: - reg allOf: + - if: + properties: + compatible: + contains: + const: jedec,ddr4-channel + then: + patternProperties: + "^rank@[0-9]+$": + $ref: /schemas/memory-controllers/ddr/jedec,ddr4.yaml# - if: properties: compatible: From 9805f2cfc883018f7bf84c84e3af3786c37dac7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Tue, 18 Nov 2025 16:08:01 +0100 Subject: [PATCH 030/171] dt-bindings: memory: SDRAM channel: standardise node name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a pattern for sdram channel node name. Signed-off-by: Clément Le Goffic Reviewed-by: Rob Herring (Arm) Signed-off-by: Clément Le Goffic Link: https://patch.msgid.link/20251118-b4-ddr-bindings-v9-5-a033ac5144da@gmail.com Signed-off-by: Krzysztof Kozlowski --- .../memory-controllers/ddr/jedec,sdram-channel.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,sdram-channel.yaml b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,sdram-channel.yaml index 866af40b654d..5cdd8ef45100 100644 --- a/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,sdram-channel.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/ddr/jedec,sdram-channel.yaml @@ -17,6 +17,9 @@ maintainers: - Julius Werner properties: + $nodename: + pattern: "sdram-channel-[0-9]+$" + compatible: enum: - jedec,ddr4-channel @@ -118,7 +121,7 @@ additionalProperties: false examples: - | - lpddr-channel0 { + sdram-channel-0 { #address-cells = <1>; #size-cells = <0>; compatible = "jedec,lpddr3-channel"; @@ -133,7 +136,7 @@ examples: }; }; - lpddr-channel1 { + sdram-channel-1 { #address-cells = <1>; #size-cells = <0>; compatible = "jedec,lpddr4-channel"; From 1b3376d2167766e9f5ac05a03ca4625777090ce7 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Thu, 20 Nov 2025 11:29:35 +0000 Subject: [PATCH 031/171] soc: samsung: exynos-chipid: use devm action to unregister soc device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify the unwinding of the soc device by using a devm action. Add the action before the exynos_asv_init() to avoid an explicit call to soc_device_unregister(). Signed-off-by: Tudor Ambarus Reviewed-by: André Draszik Link: https://patch.msgid.link/20251120-gs101-chipid-v3-1-1aeaa8b7fe35@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/soc/samsung/exynos-chipid.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c index d3b4b5508e0c..49cb113d99f3 100644 --- a/drivers/soc/samsung/exynos-chipid.c +++ b/drivers/soc/samsung/exynos-chipid.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -104,6 +105,11 @@ static int exynos_chipid_get_chipid_info(struct regmap *regmap, return 0; } +static void exynos_chipid_unregister_soc(void *data) +{ + soc_device_unregister(data); +} + static int exynos_chipid_probe(struct platform_device *pdev) { const struct exynos_chipid_variant *drv_data; @@ -152,28 +158,19 @@ static int exynos_chipid_probe(struct platform_device *pdev) if (IS_ERR(soc_dev)) return PTR_ERR(soc_dev); + ret = devm_add_action_or_reset(dev, exynos_chipid_unregister_soc, + soc_dev); + if (ret) + return dev_err_probe(dev, ret, "failed to add devm action\n"); + ret = exynos_asv_init(dev, regmap); if (ret) - goto err; - - platform_set_drvdata(pdev, soc_dev); + return ret; dev_info(dev, "Exynos: CPU[%s] PRO_ID[0x%x] REV[0x%x] Detected\n", soc_dev_attr->soc_id, soc_info.product_id, soc_info.revision); return 0; - -err: - soc_device_unregister(soc_dev); - - return ret; -} - -static void exynos_chipid_remove(struct platform_device *pdev) -{ - struct soc_device *soc_dev = platform_get_drvdata(pdev); - - soc_device_unregister(soc_dev); } static const struct exynos_chipid_variant exynos4210_chipid_drv_data = { @@ -206,7 +203,6 @@ static struct platform_driver exynos_chipid_driver = { .of_match_table = exynos_chipid_of_device_ids, }, .probe = exynos_chipid_probe, - .remove = exynos_chipid_remove, }; module_platform_driver(exynos_chipid_driver); From 8dfbb5fcb773a6481407ec890280cc3b1ff8e234 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Thu, 20 Nov 2025 11:29:36 +0000 Subject: [PATCH 032/171] soc: samsung: exynos-chipid: use dev_err_probe where appropiate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use dev_err_probe() to benefit of the standardized format of the error code (e.g. "ENODEV" instead of -19), to get meanigful error messages, and for more compact error paths. Signed-off-by: Tudor Ambarus Reviewed-by: André Draszik Link: https://patch.msgid.link/20251120-gs101-chipid-v3-2-1aeaa8b7fe35@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/soc/samsung/exynos-chipid.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c index 49cb113d99f3..b9a30452ad21 100644 --- a/drivers/soc/samsung/exynos-chipid.c +++ b/drivers/soc/samsung/exynos-chipid.c @@ -81,8 +81,8 @@ static const char *product_id_to_soc_id(unsigned int product_id) return NULL; } -static int exynos_chipid_get_chipid_info(struct regmap *regmap, - const struct exynos_chipid_variant *data, +static int exynos_chipid_get_chipid_info(struct device *dev, + struct regmap *regmap, const struct exynos_chipid_variant *data, struct exynos_chipid_info *soc_info) { int ret; @@ -90,13 +90,14 @@ static int exynos_chipid_get_chipid_info(struct regmap *regmap, ret = regmap_read(regmap, EXYNOS_CHIPID_REG_PRO_ID, &val); if (ret < 0) - return ret; + return dev_err_probe(dev, ret, "failed to read Product ID\n"); soc_info->product_id = val & EXYNOS_MASK; if (data->rev_reg != EXYNOS_CHIPID_REG_PRO_ID) { ret = regmap_read(regmap, data->rev_reg, &val); if (ret < 0) - return ret; + return dev_err_probe(dev, ret, + "failed to read revision\n"); } main_rev = (val >> data->main_rev_shift) & EXYNOS_REV_PART_MASK; sub_rev = (val >> data->sub_rev_shift) & EXYNOS_REV_PART_MASK; @@ -123,13 +124,15 @@ static int exynos_chipid_probe(struct platform_device *pdev) drv_data = of_device_get_match_data(dev); if (!drv_data) - return -EINVAL; + return dev_err_probe(dev, -EINVAL, + "failed to get match data\n"); regmap = device_node_to_regmap(dev->of_node); if (IS_ERR(regmap)) - return PTR_ERR(regmap); + return dev_err_probe(dev, PTR_ERR(regmap), + "failed to get regmap\n"); - ret = exynos_chipid_get_chipid_info(regmap, drv_data, &soc_info); + ret = exynos_chipid_get_chipid_info(dev, regmap, drv_data, &soc_info); if (ret < 0) return ret; @@ -148,15 +151,14 @@ static int exynos_chipid_probe(struct platform_device *pdev) if (!soc_dev_attr->revision) return -ENOMEM; soc_dev_attr->soc_id = product_id_to_soc_id(soc_info.product_id); - if (!soc_dev_attr->soc_id) { - pr_err("Unknown SoC\n"); - return -ENODEV; - } + if (!soc_dev_attr->soc_id) + return dev_err_probe(dev, -ENODEV, "Unknown SoC\n"); /* please note that the actual registration will be deferred */ soc_dev = soc_device_register(soc_dev_attr); if (IS_ERR(soc_dev)) - return PTR_ERR(soc_dev); + return dev_err_probe(dev, PTR_ERR(soc_dev), + "failed to register to the soc interface\n"); ret = devm_add_action_or_reset(dev, exynos_chipid_unregister_soc, soc_dev); From 1bea7e94bf09ee6d46051076866a9369f64d302a Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Tue, 16 Dec 2025 14:34:21 +0200 Subject: [PATCH 033/171] soc: renesas: Enable ICU support on RZ/N2H The Renesas RZ/N2H (R9A09G087) SoC has the same Interrupt Controller (ICU) as the Renesas RZ/T2H (R9A09G077) SoC. Enable support for it by selecting the RENESAS_RZT2H_ICU config. Signed-off-by: Cosmin Tanislav Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20251216123421.124401-1-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 340a1ff7e92b..4c5e4877a1f1 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -429,6 +429,7 @@ config ARCH_R9A09G077 config ARCH_R9A09G087 bool "ARM64 Platform support for R9A09G087 (RZ/N2H)" default y if ARCH_RENESAS + select RENESAS_RZT2H_ICU help This enables support for the Renesas RZ/N2H SoC variants. From 0fac05fdd9afff6de07a3766db802a3f2d028e2a Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Sat, 27 Dec 2025 16:41:31 +0000 Subject: [PATCH 034/171] firmware: arm_scmi: Rework protocol version negotiation logic Protocol version negotiation can be used by an agent to request the server to downgrade the version effectively utilized by a specific protocol during the current session, if the latest version used by the server is newer than the latest version known to the client. In order for the negotiation process to be fully effective at preventing any possible version incompatibility, it must happen early on, well before the specific protocol initialization phase takes place. Delegate protocol version querying to the core SCMI stack and rework the protocol negotiation logic in order to execute the needed negotiation exchanges upfront, right before the initialization phase takes place. Signed-off-by: Cristian Marussi Message-Id: <20251227164132.1311988-2-cristian.marussi@arm.com> Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 93 ++++++++++++++++++++++----- drivers/firmware/arm_scmi/protocols.h | 4 ++ 2 files changed, 81 insertions(+), 16 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 5caa9191a8d1..094cfcf51d28 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -2112,6 +2112,76 @@ static int scmi_protocol_version_negotiate(struct scmi_protocol_handle *ph) return ret; } +/** + * scmi_protocol_version_initialize - Initialize protocol version + * @dev: A device reference. + * @pi: A reference to the protocol instance being initialized + * + * At first retrieve the newest protocol version supported by the platform for + * this specific protoocol. + * + * Negotiation is attempted only when the platform advertised a protocol + * version newer than the most recent version known to this agent, since + * backward compatibility is NOT assured in general between versions. + * + * Failing to negotiate a fallback version or to query supported version at + * all will result in an attempt to use the newest version known to this agent + * even though compatibility is NOT assured. + * + * Versions are defined as: + * + * pi->version: the version supported by the platform as returned by the query. + * pi->proto->supported_version: the newest version supported by this agent + * for this protocol. + * pi->negotiated_version: The version successfully negotiated with the platform. + * ph->version: The final version effectively chosen for this session. + */ +static void scmi_protocol_version_initialize(struct device *dev, + struct scmi_protocol_instance *pi) +{ + struct scmi_protocol_handle *ph = &pi->ph; + int ret; + + /* + * Query and store platform supported protocol version: this is usually + * the newest version the platfom can support. + */ + ret = version_get(ph, &pi->version); + if (ret) { + dev_warn(dev, + "Failed to query supported version for protocol 0x%X.\n", + pi->proto->id); + goto best_effort; + } + + /* Need to negotiate at all ? */ + if (pi->version <= pi->proto->supported_version) { + ph->version = pi->version; + return; + } + + /* Attempt negotiation */ + ret = scmi_protocol_version_negotiate(ph); + if (!ret) { + ph->version = pi->negotiated_version; + dev_info(dev, + "Protocol 0x%X successfully negotiated version 0x%X\n", + pi->proto->id, ph->version); + return; + } + + dev_warn(dev, + "Detected UNSUPPORTED higher version 0x%X for protocol 0x%X.\n", + pi->version, pi->proto->id); + +best_effort: + /* Fallback to use newest version known to this agent */ + ph->version = pi->proto->supported_version; + dev_warn(dev, + "Trying version 0x%X. Backward compatibility is NOT assured.\n", + ph->version); +} + /** * scmi_alloc_init_protocol_instance - Allocate and initialize a protocol * instance descriptor. @@ -2157,6 +2227,13 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info, pi->ph.set_priv = scmi_set_protocol_priv; pi->ph.get_priv = scmi_get_protocol_priv; refcount_set(&pi->users, 1); + + /* + * Initialize effectively used protocol version performing any + * possibly needed negotiations. + */ + scmi_protocol_version_initialize(handle->dev, pi); + /* proto->init is assured NON NULL by scmi_protocol_register */ ret = pi->proto->instance_init(&pi->ph); if (ret) @@ -2184,22 +2261,6 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info, devres_close_group(handle->dev, pi->gid); dev_dbg(handle->dev, "Initialized protocol: 0x%X\n", pi->proto->id); - if (pi->version > proto->supported_version) { - ret = scmi_protocol_version_negotiate(&pi->ph); - if (!ret) { - dev_info(handle->dev, - "Protocol 0x%X successfully negotiated version 0x%X\n", - proto->id, pi->negotiated_version); - } else { - dev_warn(handle->dev, - "Detected UNSUPPORTED higher version 0x%X for protocol 0x%X.\n", - pi->version, pi->proto->id); - dev_warn(handle->dev, - "Trying version 0x%X. Backward compatibility is NOT assured.\n", - pi->proto->supported_version); - } - } - return pi; clean: diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h index d62c4469d1fd..2766c2b822b5 100644 --- a/drivers/firmware/arm_scmi/protocols.h +++ b/drivers/firmware/arm_scmi/protocols.h @@ -159,6 +159,9 @@ struct scmi_proto_helpers_ops; * struct scmi_protocol_handle - Reference to an initialized protocol instance * * @dev: A reference to the associated SCMI instance device (handle->dev). + * @version: The protocol version currently effectively in use by this + * initialized instance of the protocol as determined at the end of + * any possibly needed negotiations performed by the core. * @xops: A reference to a struct holding refs to the core xfer operations that * can be used by the protocol implementation to generate SCMI messages. * @set_priv: A method to set protocol private data for this instance. @@ -177,6 +180,7 @@ struct scmi_proto_helpers_ops; */ struct scmi_protocol_handle { struct device *dev; + unsigned int version; const struct scmi_xfer_ops *xops; const struct scmi_proto_helpers_ops *hops; int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv, From 24a0ffefe3f097aa8fe6997a731a71487dd0721f Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Sat, 27 Dec 2025 16:41:32 +0000 Subject: [PATCH 035/171] firmware: arm_scmi: Remove legacy protocol versioning logic Protocol version negotiation logic is centralized in the SCMI core stack so that most of the legacy per-protocol versioning logic is redundant and can be removed. Remove protocol-specific versioning code and refactor all the protocols to use the new simplified centralized logic. Signed-off-by: Cristian Marussi Message-Id: <20251227164132.1311988-3-cristian.marussi@arm.com> Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/base.c | 11 +--- drivers/firmware/arm_scmi/clock.c | 24 +++----- drivers/firmware/arm_scmi/driver.c | 5 +- drivers/firmware/arm_scmi/perf.c | 57 +++++++------------ drivers/firmware/arm_scmi/pinctrl.c | 12 +--- drivers/firmware/arm_scmi/power.c | 18 ++---- drivers/firmware/arm_scmi/powercap.c | 21 +++---- drivers/firmware/arm_scmi/protocols.h | 5 +- drivers/firmware/arm_scmi/reset.c | 18 ++---- drivers/firmware/arm_scmi/sensors.c | 22 +++---- drivers/firmware/arm_scmi/system.c | 14 +---- .../arm_scmi/vendors/imx/imx-sm-bbm.c | 10 +--- .../arm_scmi/vendors/imx/imx-sm-cpu.c | 9 +-- .../arm_scmi/vendors/imx/imx-sm-lmm.c | 9 +-- .../arm_scmi/vendors/imx/imx-sm-misc.c | 10 +--- drivers/firmware/arm_scmi/voltage.c | 13 +---- 16 files changed, 72 insertions(+), 186 deletions(-) diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c index 86b376c50a13..22267bbd0f4d 100644 --- a/drivers/firmware/arm_scmi/base.c +++ b/drivers/firmware/arm_scmi/base.c @@ -375,18 +375,13 @@ static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph) { int id, ret; u8 *prot_imp; - u32 version; char name[SCMI_SHORT_NAME_MAX_SIZE]; struct device *dev = ph->dev; struct scmi_revision_info *rev = scmi_revision_area_get(ph); - ret = ph->xops->version_get(ph, &version); - if (ret) - return ret; - - rev->major_ver = PROTOCOL_REV_MAJOR(version); - rev->minor_ver = PROTOCOL_REV_MINOR(version); - ph->set_priv(ph, rev, version); + rev->major_ver = PROTOCOL_REV_MAJOR(ph->version); + rev->minor_ver = PROTOCOL_REV_MINOR(ph->version); + ph->set_priv(ph, rev); ret = scmi_base_attributes_get(ph); if (ret) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index afa7981efe82..ab36871650a1 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -157,7 +157,6 @@ struct scmi_clock_rate_notify_payld { }; struct clock_info { - u32 version; int num_clocks; int max_async_req; bool notify_rate_changed_cmd; @@ -346,8 +345,7 @@ scmi_clock_get_permissions(const struct scmi_protocol_handle *ph, u32 clk_id, } static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, - u32 clk_id, struct clock_info *cinfo, - u32 version) + u32 clk_id, struct clock_info *cinfo) { int ret; u32 attributes; @@ -370,7 +368,7 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, attributes = le32_to_cpu(attr->attributes); strscpy(clk->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE); /* clock_enable_latency field is present only since SCMI v3.1 */ - if (PROTOCOL_REV_MAJOR(version) >= 0x2) + if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2) latency = le32_to_cpu(attr->clock_enable_latency); clk->enable_latency = latency ? : U32_MAX; } @@ -381,7 +379,7 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, * If supported overwrite short name with the extended one; * on error just carry on and use already provided short name. */ - if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x2) { + if (!ret && PROTOCOL_REV_MAJOR(ph->version) >= 0x2) { if (SUPPORTS_EXTENDED_NAMES(attributes)) ph->hops->extended_name_get(ph, CLOCK_NAME_GET, clk_id, NULL, clk->name, @@ -393,7 +391,7 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, if (cinfo->notify_rate_change_requested_cmd && SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes)) clk->rate_change_requested_notifications = true; - if (PROTOCOL_REV_MAJOR(version) >= 0x3) { + if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3) { if (SUPPORTS_PARENT_CLOCK(attributes)) scmi_clock_possible_parents(ph, clk_id, clk); if (SUPPORTS_GET_PERMISSIONS(attributes)) @@ -1068,16 +1066,11 @@ static const struct scmi_protocol_events clk_protocol_events = { static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph) { - u32 version; int clkid, ret; struct clock_info *cinfo; - ret = ph->xops->version_get(ph, &version); - if (ret) - return ret; - dev_dbg(ph->dev, "Clock Version %d.%d\n", - PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version)); cinfo = devm_kzalloc(ph->dev, sizeof(*cinfo), GFP_KERNEL); if (!cinfo) @@ -1095,12 +1088,12 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph) for (clkid = 0; clkid < cinfo->num_clocks; clkid++) { struct scmi_clock_info *clk = cinfo->clk + clkid; - ret = scmi_clock_attributes_get(ph, clkid, cinfo, version); + ret = scmi_clock_attributes_get(ph, clkid, cinfo); if (!ret) scmi_clock_describe_rates_get(ph, clkid, clk); } - if (PROTOCOL_REV_MAJOR(version) >= 0x3) { + if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3) { cinfo->clock_config_set = scmi_clock_config_set_v2; cinfo->clock_config_get = scmi_clock_config_get_v2; } else { @@ -1108,8 +1101,7 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph) cinfo->clock_config_get = scmi_clock_config_get; } - cinfo->version = version; - return ph->set_priv(ph, cinfo, version); + return ph->set_priv(ph, cinfo); } static const struct scmi_protocol scmi_clock = { diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 094cfcf51d28..3e76a3204ba4 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -1627,17 +1627,15 @@ static int version_get(const struct scmi_protocol_handle *ph, u32 *version) * * @ph: A reference to the protocol handle. * @priv: The private data to set. - * @version: The detected protocol version for the core to register. * * Return: 0 on Success */ static int scmi_set_protocol_priv(const struct scmi_protocol_handle *ph, - void *priv, u32 version) + void *priv) { struct scmi_protocol_instance *pi = ph_to_pi(ph); pi->priv = priv; - pi->version = version; return 0; } @@ -1657,7 +1655,6 @@ static void *scmi_get_protocol_priv(const struct scmi_protocol_handle *ph) } static const struct scmi_xfer_ops xfer_ops = { - .version_get = version_get, .xfer_get_init = xfer_get_init, .reset_rx_to_maxsz = reset_rx_to_maxsz, .do_xfer = do_xfer, diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 2249ef7fe790..4583d02bee1c 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -178,7 +178,6 @@ struct perf_dom_info { }) struct scmi_perf_info { - u32 version; u16 num_domains; enum scmi_power_scale power_scale; u64 stats_addr; @@ -215,7 +214,7 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph, if (POWER_SCALE_IN_MILLIWATT(flags)) pi->power_scale = SCMI_POWER_MILLIWATTS; - if (PROTOCOL_REV_MAJOR(pi->version) >= 0x3) + if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3) if (POWER_SCALE_IN_MICROWATT(flags)) pi->power_scale = SCMI_POWER_MICROWATTS; @@ -251,8 +250,7 @@ static void scmi_perf_xa_destroy(void *data) static int scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, struct perf_dom_info *dom_info, - bool notify_lim_cmd, bool notify_lvl_cmd, - u32 version) + bool notify_lim_cmd, bool notify_lvl_cmd) { int ret; u32 flags; @@ -280,7 +278,7 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, dom_info->perf_level_notify = SUPPORTS_PERF_LEVEL_NOTIFY(flags); dom_info->perf_fastchannels = SUPPORTS_PERF_FASTCHANNELS(flags); - if (PROTOCOL_REV_MAJOR(version) >= 0x4) + if (PROTOCOL_REV_MAJOR(ph->version) >= 0x4) dom_info->level_indexing_mode = SUPPORTS_LEVEL_INDEXING(flags); dom_info->rate_limit_us = le32_to_cpu(attr->rate_limit_us) & @@ -323,7 +321,7 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, * If supported overwrite short name with the extended one; * on error just carry on and use already provided short name. */ - if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 && + if (!ret && PROTOCOL_REV_MAJOR(ph->version) >= 0x3 && SUPPORTS_EXTENDED_NAMES(flags)) ph->hops->extended_name_get(ph, PERF_DOMAIN_NAME_GET, dom_info->id, NULL, dom_info->info.name, @@ -345,19 +343,14 @@ static int opp_cmp_func(const void *opp1, const void *opp2) return t1->perf - t2->perf; } -struct scmi_perf_ipriv { - u32 version; - struct perf_dom_info *perf_dom; -}; - static void iter_perf_levels_prepare_message(void *message, unsigned int desc_index, const void *priv) { struct scmi_msg_perf_describe_levels *msg = message; - const struct scmi_perf_ipriv *p = priv; + const struct perf_dom_info *perf_dom = priv; - msg->domain = cpu_to_le32(p->perf_dom->id); + msg->domain = cpu_to_le32(perf_dom->id); /* Set the number of OPPs to be skipped/already read */ msg->level_index = cpu_to_le32(desc_index); } @@ -445,21 +438,21 @@ iter_perf_levels_process_response(const struct scmi_protocol_handle *ph, { int ret; struct scmi_opp *opp; - struct scmi_perf_ipriv *p = priv; + struct perf_dom_info *perf_dom = priv; - opp = &p->perf_dom->opp[p->perf_dom->opp_count]; - if (PROTOCOL_REV_MAJOR(p->version) <= 0x3) - ret = process_response_opp(ph->dev, p->perf_dom, opp, + opp = &perf_dom->opp[perf_dom->opp_count]; + if (PROTOCOL_REV_MAJOR(ph->version) <= 0x3) + ret = process_response_opp(ph->dev, perf_dom, opp, st->loop_idx, response); else - ret = process_response_opp_v4(ph->dev, p->perf_dom, opp, + ret = process_response_opp_v4(ph->dev, perf_dom, opp, st->loop_idx, response); /* Skip BAD duplicates received from firmware */ if (ret) return ret == -EBUSY ? 0 : ret; - p->perf_dom->opp_count++; + perf_dom->opp_count++; dev_dbg(ph->dev, "Level %d Power %d Latency %dus Ifreq %d Index %d\n", opp->perf, opp->power, opp->trans_latency_us, @@ -470,7 +463,7 @@ iter_perf_levels_process_response(const struct scmi_protocol_handle *ph, static int scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph, - struct perf_dom_info *perf_dom, u32 version) + struct perf_dom_info *perf_dom) { int ret; void *iter; @@ -479,15 +472,11 @@ scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph, .update_state = iter_perf_levels_update_state, .process_response = iter_perf_levels_process_response, }; - struct scmi_perf_ipriv ppriv = { - .version = version, - .perf_dom = perf_dom, - }; iter = ph->hops->iter_response_init(ph, &ops, MAX_OPPS, PERF_DESCRIBE_LEVELS, sizeof(struct scmi_msg_perf_describe_levels), - &ppriv); + perf_dom); if (IS_ERR(iter)) return PTR_ERR(iter); @@ -576,7 +565,6 @@ static int __scmi_perf_limits_set(const struct scmi_protocol_handle *ph, static int scmi_perf_limits_set(const struct scmi_protocol_handle *ph, u32 domain, u32 max_perf, u32 min_perf) { - struct scmi_perf_info *pi = ph->get_priv(ph); struct perf_dom_info *dom; dom = scmi_perf_domain_lookup(ph, domain); @@ -586,7 +574,7 @@ static int scmi_perf_limits_set(const struct scmi_protocol_handle *ph, if (!dom->set_limits) return -EOPNOTSUPP; - if (PROTOCOL_REV_MAJOR(pi->version) >= 0x3 && !max_perf && !min_perf) + if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3 && !max_perf && !min_perf) return -EINVAL; if (dom->level_indexing_mode) { @@ -1281,22 +1269,15 @@ static const struct scmi_protocol_events perf_protocol_events = { static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph) { int domain, ret; - u32 version; struct scmi_perf_info *pinfo; - ret = ph->xops->version_get(ph, &version); - if (ret) - return ret; - dev_dbg(ph->dev, "Performance Version %d.%d\n", - PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version)); pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL); if (!pinfo) return -ENOMEM; - pinfo->version = version; - ret = scmi_perf_attributes_get(ph, pinfo); if (ret) return ret; @@ -1311,8 +1292,8 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph) dom->id = domain; scmi_perf_domain_attributes_get(ph, dom, pinfo->notify_lim_cmd, - pinfo->notify_lvl_cmd, version); - scmi_perf_describe_levels_get(ph, dom, version); + pinfo->notify_lvl_cmd); + scmi_perf_describe_levels_get(ph, dom); if (dom->perf_fastchannels) scmi_perf_domain_init_fc(ph, dom); @@ -1322,7 +1303,7 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph) if (ret) return ret; - return ph->set_priv(ph, pinfo, version); + return ph->set_priv(ph, pinfo); } static const struct scmi_protocol scmi_perf = { diff --git a/drivers/firmware/arm_scmi/pinctrl.c b/drivers/firmware/arm_scmi/pinctrl.c index d18c2d248f04..a020e23d7c49 100644 --- a/drivers/firmware/arm_scmi/pinctrl.c +++ b/drivers/firmware/arm_scmi/pinctrl.c @@ -117,7 +117,6 @@ struct scmi_pin_info { }; struct scmi_pinctrl_info { - u32 version; int nr_groups; int nr_functions; int nr_pins; @@ -831,15 +830,10 @@ static const struct scmi_pinctrl_proto_ops pinctrl_proto_ops = { static int scmi_pinctrl_protocol_init(const struct scmi_protocol_handle *ph) { int ret; - u32 version; struct scmi_pinctrl_info *pinfo; - ret = ph->xops->version_get(ph, &version); - if (ret) - return ret; - dev_dbg(ph->dev, "Pinctrl Version %d.%d\n", - PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version)); pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL); if (!pinfo) @@ -864,9 +858,7 @@ static int scmi_pinctrl_protocol_init(const struct scmi_protocol_handle *ph) if (!pinfo->functions) return -ENOMEM; - pinfo->version = version; - - return ph->set_priv(ph, pinfo, version); + return ph->set_priv(ph, pinfo); } static int scmi_pinctrl_protocol_deinit(const struct scmi_protocol_handle *ph) diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c index 59aa16444c64..bb5062ab8280 100644 --- a/drivers/firmware/arm_scmi/power.c +++ b/drivers/firmware/arm_scmi/power.c @@ -67,7 +67,6 @@ struct power_dom_info { }; struct scmi_power_info { - u32 version; bool notify_state_change_cmd; int num_domains; u64 stats_addr; @@ -109,7 +108,7 @@ static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph, static int scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph, u32 domain, struct power_dom_info *dom_info, - u32 version, bool notify_state_change_cmd) + bool notify_state_change_cmd) { int ret; u32 flags; @@ -141,7 +140,7 @@ scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph, * If supported overwrite short name with the extended one; * on error just carry on and use already provided short name. */ - if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 && + if (!ret && PROTOCOL_REV_MAJOR(ph->version) >= 0x3 && SUPPORTS_EXTENDED_NAMES(flags)) { ph->hops->extended_name_get(ph, POWER_DOMAIN_NAME_GET, domain, NULL, dom_info->name, @@ -323,15 +322,10 @@ static const struct scmi_protocol_events power_protocol_events = { static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph) { int domain, ret; - u32 version; struct scmi_power_info *pinfo; - ret = ph->xops->version_get(ph, &version); - if (ret) - return ret; - dev_dbg(ph->dev, "Power Version %d.%d\n", - PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version)); pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL); if (!pinfo) @@ -349,13 +343,11 @@ static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph) for (domain = 0; domain < pinfo->num_domains; domain++) { struct power_dom_info *dom = pinfo->dom_info + domain; - scmi_power_domain_attributes_get(ph, domain, dom, version, + scmi_power_domain_attributes_get(ph, domain, dom, pinfo->notify_state_change_cmd); } - pinfo->version = version; - - return ph->set_priv(ph, pinfo, version); + return ph->set_priv(ph, pinfo); } static const struct scmi_protocol scmi_power = { diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c index 1fa79bba492e..ab9733f4458b 100644 --- a/drivers/firmware/arm_scmi/powercap.c +++ b/drivers/firmware/arm_scmi/powercap.c @@ -122,7 +122,6 @@ struct scmi_powercap_state { }; struct powercap_info { - u32 version; int num_domains; bool notify_cap_cmd; bool notify_measurements_cmd; @@ -434,7 +433,7 @@ static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph, } /* Save the last explicitly set non-zero powercap value */ - if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 && !ret && power_cap) + if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2 && !ret && power_cap) pi->states[domain_id].last_pcap = power_cap; return ret; @@ -454,7 +453,7 @@ static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph, return -EINVAL; /* Just log the last set request if acting on a disabled domain */ - if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 && + if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2 && !pi->states[domain_id].enabled) { pi->states[domain_id].last_pcap = power_cap; return 0; @@ -635,7 +634,7 @@ static int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph, u32 power_cap; struct powercap_info *pi = ph->get_priv(ph); - if (PROTOCOL_REV_MAJOR(pi->version) < 0x2) + if (PROTOCOL_REV_MAJOR(ph->version) < 0x2) return -EINVAL; if (enable == pi->states[domain_id].enabled) @@ -676,7 +675,7 @@ static int scmi_powercap_cap_enable_get(const struct scmi_protocol_handle *ph, struct powercap_info *pi = ph->get_priv(ph); *enable = true; - if (PROTOCOL_REV_MAJOR(pi->version) < 0x2) + if (PROTOCOL_REV_MAJOR(ph->version) < 0x2) return 0; /* @@ -961,15 +960,10 @@ static int scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph) { int domain, ret; - u32 version; struct powercap_info *pinfo; - ret = ph->xops->version_get(ph, &version); - if (ret) - return ret; - dev_dbg(ph->dev, "Powercap Version %d.%d\n", - PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version)); pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL); if (!pinfo) @@ -1006,7 +1000,7 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph) &pinfo->powercaps[domain].fc_info); /* Grab initial state when disable is supported. */ - if (PROTOCOL_REV_MAJOR(version) >= 0x2) { + if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2) { ret = __scmi_powercap_cap_get(ph, &pinfo->powercaps[domain], &pinfo->states[domain].last_pcap); @@ -1018,8 +1012,7 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph) } } - pinfo->version = version; - return ph->set_priv(ph, pinfo, version); + return ph->set_priv(ph, pinfo); } static const struct scmi_protocol scmi_powercap = { diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h index 2766c2b822b5..4c75970326e6 100644 --- a/drivers/firmware/arm_scmi/protocols.h +++ b/drivers/firmware/arm_scmi/protocols.h @@ -183,8 +183,7 @@ struct scmi_protocol_handle { unsigned int version; const struct scmi_xfer_ops *xops; const struct scmi_proto_helpers_ops *hops; - int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv, - u32 version); + int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv); void *(*get_priv)(const struct scmi_protocol_handle *ph); }; @@ -291,7 +290,6 @@ struct scmi_proto_helpers_ops { /** * struct scmi_xfer_ops - References to the core SCMI xfer operations. - * @version_get: Get this version protocol. * @xfer_get_init: Initialize one struct xfer if any xfer slot is free. * @reset_rx_to_maxsz: Reset rx size to max transport size. * @do_xfer: Do the SCMI transfer. @@ -304,7 +302,6 @@ struct scmi_proto_helpers_ops { * another protocol. */ struct scmi_xfer_ops { - int (*version_get)(const struct scmi_protocol_handle *ph, u32 *version); int (*xfer_get_init)(const struct scmi_protocol_handle *ph, u8 msg_id, size_t tx_size, size_t rx_size, struct scmi_xfer **p); diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c index 7c4550a3e258..4bc5c24c2d72 100644 --- a/drivers/firmware/arm_scmi/reset.c +++ b/drivers/firmware/arm_scmi/reset.c @@ -65,7 +65,6 @@ struct reset_dom_info { }; struct scmi_reset_info { - u32 version; int num_domains; bool notify_reset_cmd; struct reset_dom_info *dom_info; @@ -111,8 +110,7 @@ scmi_reset_domain_lookup(const struct scmi_protocol_handle *ph, u32 domain) static int scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph, - struct scmi_reset_info *pinfo, - u32 domain, u32 version) + struct scmi_reset_info *pinfo, u32 domain) { int ret; u32 attributes; @@ -148,7 +146,7 @@ scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph, * If supported overwrite short name with the extended one; * on error just carry on and use already provided short name. */ - if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 && + if (!ret && PROTOCOL_REV_MAJOR(ph->version) >= 0x3 && SUPPORTS_EXTENDED_NAMES(attributes)) ph->hops->extended_name_get(ph, RESET_DOMAIN_NAME_GET, domain, NULL, dom_info->name, @@ -356,15 +354,10 @@ static const struct scmi_protocol_events reset_protocol_events = { static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph) { int domain, ret; - u32 version; struct scmi_reset_info *pinfo; - ret = ph->xops->version_get(ph, &version); - if (ret) - return ret; - dev_dbg(ph->dev, "Reset Version %d.%d\n", - PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version)); pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL); if (!pinfo) @@ -380,10 +373,9 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph) return -ENOMEM; for (domain = 0; domain < pinfo->num_domains; domain++) - scmi_reset_domain_attributes_get(ph, pinfo, domain, version); + scmi_reset_domain_attributes_get(ph, pinfo, domain); - pinfo->version = version; - return ph->set_priv(ph, pinfo, version); + return ph->set_priv(ph, pinfo); } static const struct scmi_protocol scmi_reset = { diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index 791efd0f82d7..882d55f987d2 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -214,7 +214,6 @@ struct scmi_sensor_update_notify_payld { }; struct sensors_info { - u32 version; bool notify_trip_point_cmd; bool notify_continuos_update_cmd; int num_sensors; @@ -524,8 +523,7 @@ scmi_sensor_axis_extended_names_get(const struct scmi_protocol_handle *ph, } static int scmi_sensor_axis_description(const struct scmi_protocol_handle *ph, - struct scmi_sensor_info *s, - u32 version) + struct scmi_sensor_info *s) { int ret; void *iter; @@ -555,7 +553,7 @@ static int scmi_sensor_axis_description(const struct scmi_protocol_handle *ph, if (ret) return ret; - if (PROTOCOL_REV_MAJOR(version) >= 0x3 && + if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3 && apriv.any_axes_support_extended_names) ret = scmi_sensor_axis_extended_names_get(ph, s); @@ -621,7 +619,7 @@ iter_sens_descr_process_response(const struct scmi_protocol_handle *ph, s->type = SENSOR_TYPE(attrh); /* Use pre-allocated pool wherever possible */ s->intervals.desc = s->intervals.prealloc_pool; - if (si->version == SCMIv2_SENSOR_PROTOCOL) { + if (ph->version == SCMIv2_SENSOR_PROTOCOL) { s->intervals.segmented = false; s->intervals.count = 1; /* @@ -659,7 +657,7 @@ iter_sens_descr_process_response(const struct scmi_protocol_handle *ph, * one; on error just carry on and use already provided * short name. */ - if (PROTOCOL_REV_MAJOR(si->version) >= 0x3 && + if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3 && SUPPORTS_EXTENDED_NAMES(attrl)) ph->hops->extended_name_get(ph, SENSOR_NAME_GET, s->id, NULL, s->name, SCMI_MAX_STR_SIZE); @@ -683,7 +681,7 @@ iter_sens_descr_process_response(const struct scmi_protocol_handle *ph, } if (s->num_axis > 0) - ret = scmi_sensor_axis_description(ph, s, si->version); + ret = scmi_sensor_axis_description(ph, s); st->priv = ((u8 *)sdesc + dsize); @@ -1148,21 +1146,15 @@ static const struct scmi_protocol_events sensor_protocol_events = { static int scmi_sensors_protocol_init(const struct scmi_protocol_handle *ph) { - u32 version; int ret; struct sensors_info *sinfo; - ret = ph->xops->version_get(ph, &version); - if (ret) - return ret; - dev_dbg(ph->dev, "Sensor Version %d.%d\n", - PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version)); sinfo = devm_kzalloc(ph->dev, sizeof(*sinfo), GFP_KERNEL); if (!sinfo) return -ENOMEM; - sinfo->version = version; ret = scmi_sensor_attributes_get(ph, sinfo); if (ret) @@ -1176,7 +1168,7 @@ static int scmi_sensors_protocol_init(const struct scmi_protocol_handle *ph) if (ret) return ret; - return ph->set_priv(ph, sinfo, version); + return ph->set_priv(ph, sinfo); } static const struct scmi_protocol scmi_sensors = { diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c index ec3d355d1772..0f51c36f6a9d 100644 --- a/drivers/firmware/arm_scmi/system.c +++ b/drivers/firmware/arm_scmi/system.c @@ -34,7 +34,6 @@ struct scmi_system_power_state_notifier_payld { }; struct scmi_system_info { - u32 version; bool graceful_timeout_supported; bool power_state_notify_cmd; }; @@ -141,29 +140,22 @@ static const struct scmi_protocol_events system_protocol_events = { static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph) { - int ret; - u32 version; struct scmi_system_info *pinfo; - ret = ph->xops->version_get(ph, &version); - if (ret) - return ret; - dev_dbg(ph->dev, "System Power Version %d.%d\n", - PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version)); pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL); if (!pinfo) return -ENOMEM; - pinfo->version = version; - if (PROTOCOL_REV_MAJOR(pinfo->version) >= 0x2) + if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2) pinfo->graceful_timeout_supported = true; if (!ph->hops->protocol_msg_check(ph, SYSTEM_POWER_STATE_NOTIFY, NULL)) pinfo->power_state_notify_cmd = true; - return ph->set_priv(ph, pinfo, version); + return ph->set_priv(ph, pinfo); } static const struct scmi_protocol scmi_system = { diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-bbm.c b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-bbm.c index aa176c1a5eef..33f9ebf6092b 100644 --- a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-bbm.c +++ b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-bbm.c @@ -48,7 +48,6 @@ enum scmi_imx_bbm_protocol_cmd { #define SCMI_IMX_BBM_EVENT_RTC_MASK GENMASK(31, 24) struct scmi_imx_bbm_info { - u32 version; int nr_rtc; int nr_gpr; }; @@ -345,16 +344,11 @@ static const struct scmi_imx_bbm_proto_ops scmi_imx_bbm_proto_ops = { static int scmi_imx_bbm_protocol_init(const struct scmi_protocol_handle *ph) { - u32 version; int ret; struct scmi_imx_bbm_info *binfo; - ret = ph->xops->version_get(ph, &version); - if (ret) - return ret; - dev_info(ph->dev, "NXP SM BBM Version %d.%d\n", - PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version)); binfo = devm_kzalloc(ph->dev, sizeof(*binfo), GFP_KERNEL); if (!binfo) @@ -364,7 +358,7 @@ static int scmi_imx_bbm_protocol_init(const struct scmi_protocol_handle *ph) if (ret) return ret; - return ph->set_priv(ph, binfo, version); + return ph->set_priv(ph, binfo); } static const struct scmi_protocol scmi_imx_bbm = { diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c index 66f47f5371e5..753274af11d2 100644 --- a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c +++ b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c @@ -233,15 +233,10 @@ static int scmi_imx_cpu_attributes_get(const struct scmi_protocol_handle *ph, static int scmi_imx_cpu_protocol_init(const struct scmi_protocol_handle *ph) { struct scmi_imx_cpu_info *info; - u32 version; int ret, i; - ret = ph->xops->version_get(ph, &version); - if (ret) - return ret; - dev_info(ph->dev, "NXP SM CPU Protocol Version %d.%d\n", - PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version)); info = devm_kzalloc(ph->dev, sizeof(*info), GFP_KERNEL); if (!info) @@ -257,7 +252,7 @@ static int scmi_imx_cpu_protocol_init(const struct scmi_protocol_handle *ph) return ret; } - return ph->set_priv(ph, info, version); + return ph->set_priv(ph, info); } static const struct scmi_protocol scmi_imx_cpu = { diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-lmm.c b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-lmm.c index b519c67fe920..c56ae247774d 100644 --- a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-lmm.c +++ b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-lmm.c @@ -226,15 +226,10 @@ static int scmi_imx_lmm_protocol_attributes_get(const struct scmi_protocol_handl static int scmi_imx_lmm_protocol_init(const struct scmi_protocol_handle *ph) { struct scmi_imx_lmm_priv *info; - u32 version; int ret; - ret = ph->xops->version_get(ph, &version); - if (ret) - return ret; - dev_info(ph->dev, "NXP SM LMM Version %d.%d\n", - PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version)); info = devm_kzalloc(ph->dev, sizeof(*info), GFP_KERNEL); if (!info) @@ -244,7 +239,7 @@ static int scmi_imx_lmm_protocol_init(const struct scmi_protocol_handle *ph) if (ret) return ret; - return ph->set_priv(ph, info, version); + return ph->set_priv(ph, info); } static const struct scmi_protocol scmi_imx_lmm = { diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c index 700a3f24f4ef..73d80f221b9d 100644 --- a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c +++ b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c @@ -32,7 +32,6 @@ enum scmi_imx_misc_protocol_cmd { }; struct scmi_imx_misc_info { - u32 version; u32 nr_dev_ctrl; u32 nr_brd_ctrl; u32 nr_reason; @@ -380,15 +379,10 @@ static const struct scmi_imx_misc_proto_ops scmi_imx_misc_proto_ops = { static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph) { struct scmi_imx_misc_info *minfo; - u32 version; int ret; - ret = ph->xops->version_get(ph, &version); - if (ret) - return ret; - dev_info(ph->dev, "NXP SM MISC Version %d.%d\n", - PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version)); minfo = devm_kzalloc(ph->dev, sizeof(*minfo), GFP_KERNEL); if (!minfo) @@ -410,7 +404,7 @@ static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph) if (ret && ret != -EOPNOTSUPP) return ret; - return ph->set_priv(ph, minfo, version); + return ph->set_priv(ph, minfo); } static const struct scmi_protocol scmi_imx_misc = { diff --git a/drivers/firmware/arm_scmi/voltage.c b/drivers/firmware/arm_scmi/voltage.c index 17127880e10a..b9391c1ee8a0 100644 --- a/drivers/firmware/arm_scmi/voltage.c +++ b/drivers/firmware/arm_scmi/voltage.c @@ -66,7 +66,6 @@ struct scmi_resp_voltage_level_set_complete { }; struct voltage_info { - unsigned int version; unsigned int num_domains; struct scmi_voltage_info *domains; }; @@ -243,7 +242,7 @@ static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph, * If supported overwrite short name with the extended one; * on error just carry on and use already provided short name. */ - if (PROTOCOL_REV_MAJOR(vinfo->version) >= 0x2) { + if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2) { if (SUPPORTS_EXTENDED_NAMES(attributes)) ph->hops->extended_name_get(ph, VOLTAGE_DOMAIN_NAME_GET, @@ -405,20 +404,14 @@ static const struct scmi_voltage_proto_ops voltage_proto_ops = { static int scmi_voltage_protocol_init(const struct scmi_protocol_handle *ph) { int ret; - u32 version; struct voltage_info *vinfo; - ret = ph->xops->version_get(ph, &version); - if (ret) - return ret; - dev_dbg(ph->dev, "Voltage Version %d.%d\n", - PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version)); vinfo = devm_kzalloc(ph->dev, sizeof(*vinfo), GFP_KERNEL); if (!vinfo) return -ENOMEM; - vinfo->version = version; ret = scmi_protocol_attributes_get(ph, vinfo); if (ret) @@ -437,7 +430,7 @@ static int scmi_voltage_protocol_init(const struct scmi_protocol_handle *ph) dev_warn(ph->dev, "No Voltage domains found.\n"); } - return ph->set_priv(ph, vinfo, version); + return ph->set_priv(ph, vinfo); } static const struct scmi_protocol scmi_voltage = { From 12da6f08a07ddaddd336af878350d30449d23a54 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 22 Dec 2025 16:30:05 +0000 Subject: [PATCH 036/171] dt-bindings: nvmem: add google,gs101-otp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add binding for the OTP controller found on Google GS101. Reviewed-by: André Draszik Reviewed-by: Krzysztof Kozlowski Signed-off-by: Tudor Ambarus Link: https://patch.msgid.link/20251222-gs101-chipid-v4-1-aa8e20ce7bb3@linaro.org Signed-off-by: Krzysztof Kozlowski --- .../bindings/nvmem/google,gs101-otp.yaml | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 Documentation/devicetree/bindings/nvmem/google,gs101-otp.yaml diff --git a/Documentation/devicetree/bindings/nvmem/google,gs101-otp.yaml b/Documentation/devicetree/bindings/nvmem/google,gs101-otp.yaml new file mode 100644 index 000000000000..99e322c72f9e --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/google,gs101-otp.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/google,gs101-otp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Google GS101 OTP Controller + +maintainers: + - Tudor Ambarus + +description: | + OTP controller drives a NVMEM memory where system or user specific data + can be stored. The OTP controller register space is of interest as well + because it contains dedicated registers where it stores the Product ID + and the Chip ID (apart other things like TMU or ASV info). + +allOf: + - $ref: nvmem.yaml# + +properties: + compatible: + items: + - const: google,gs101-otp + + clocks: + maxItems: 1 + + clock-names: + const: pclk + + interrupts: + maxItems: 1 + + reg: + maxItems: 1 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + +unevaluatedProperties: false + +examples: + - | + #include + #include + + efuse@10000000 { + compatible = "google,gs101-otp"; + reg = <0x10000000 0xf084>; + clocks = <&cmu_misc CLK_GOUT_MISC_OTP_CON_TOP_PCLK>; + clock-names = "pclk"; + interrupts = ; + }; From 9133ae2119cb3c948675dc566eebf11cc4bb1681 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 22 Dec 2025 16:30:06 +0000 Subject: [PATCH 037/171] soc: samsung: exynos-chipid: rename method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit s/product_id_to_soc_id/exynos_product_id_to_name. Prepend exynos_ to avoid name space pollution. The method translates the product id to a name, rename the method to make that clear. While touching the code where it is called, add a blank line for readability purposes. Signed-off-by: Tudor Ambarus Reviewed-by: André Draszik Link: https://patch.msgid.link/20251222-gs101-chipid-v4-2-aa8e20ce7bb3@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/soc/samsung/exynos-chipid.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c index b9a30452ad21..88d264ef1b88 100644 --- a/drivers/soc/samsung/exynos-chipid.c +++ b/drivers/soc/samsung/exynos-chipid.c @@ -71,7 +71,7 @@ static const struct exynos_soc_id { { "EXYNOSAUTOV920", 0x0A920000 }, }; -static const char *product_id_to_soc_id(unsigned int product_id) +static const char *exynos_product_id_to_name(unsigned int product_id) { int i; @@ -150,7 +150,8 @@ static int exynos_chipid_probe(struct platform_device *pdev) soc_info.revision); if (!soc_dev_attr->revision) return -ENOMEM; - soc_dev_attr->soc_id = product_id_to_soc_id(soc_info.product_id); + + soc_dev_attr->soc_id = exynos_product_id_to_name(soc_info.product_id); if (!soc_dev_attr->soc_id) return dev_err_probe(dev, -ENODEV, "Unknown SoC\n"); From c38cfc303db9ab4d5f482ae8e36e5a677db8eee6 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 22 Dec 2025 16:30:07 +0000 Subject: [PATCH 038/171] soc: samsung: exynos-chipid: downgrade dev_info to dev_dbg for soc info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SoC information is exposed to userspace using the standard soc interface. Downgrade to dev_dbg to stop polluting the console log. Signed-off-by: Tudor Ambarus Reviewed-by: André Draszik Link: https://patch.msgid.link/20251222-gs101-chipid-v4-3-aa8e20ce7bb3@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/soc/samsung/exynos-chipid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c index 88d264ef1b88..5c8660374269 100644 --- a/drivers/soc/samsung/exynos-chipid.c +++ b/drivers/soc/samsung/exynos-chipid.c @@ -170,8 +170,8 @@ static int exynos_chipid_probe(struct platform_device *pdev) if (ret) return ret; - dev_info(dev, "Exynos: CPU[%s] PRO_ID[0x%x] REV[0x%x] Detected\n", - soc_dev_attr->soc_id, soc_info.product_id, soc_info.revision); + dev_dbg(dev, "Exynos: CPU[%s] PRO_ID[0x%x] REV[0x%x] Detected\n", + soc_dev_attr->soc_id, soc_info.product_id, soc_info.revision); return 0; } From 732af51910960535382db3f6e0b33e2e2b0ff7b6 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 22 Dec 2025 16:30:08 +0000 Subject: [PATCH 039/171] soc: samsung: exynos-chipid: add google,gs101-otp support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GS101 is different (but also e850 and autov9 I assume) from the SoCs that are currently handled by the exynos-chipid driver because the chip ID info is part of the OTP registers. GS101 OTP has a clock, an interrupt line, a register space (that contains product and chip ID, TMU data, ASV, etc) and a 32Kbit memory space that can be read/program/locked with specific commands. On GS101 the "ChipID block" is just an abstraction, it's not a physical device. When the power-on sequence progresses, the OTP chipid values are loaded to the OTP registers. Add the GS101 chip ID support. The support is intentionally added in the exynos-chipid driver, and not in a dedicated Exynos OTP driver, because we estimate that there will not be any OTP consumers in the kernel other than the chip ID/SoC interface. The downstream GS101 drivers confirm this supposition. Signed-off-by: Tudor Ambarus Reviewed-by: André Draszik Link: https://patch.msgid.link/20251222-gs101-chipid-v4-4-aa8e20ce7bb3@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/soc/samsung/exynos-chipid.c | 70 +++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 9 deletions(-) diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c index 5c8660374269..6ef9751e2509 100644 --- a/drivers/soc/samsung/exynos-chipid.c +++ b/drivers/soc/samsung/exynos-chipid.c @@ -15,7 +15,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -28,9 +29,11 @@ #include "exynos-asv.h" struct exynos_chipid_variant { - unsigned int rev_reg; /* revision register offset */ + unsigned int main_rev_reg; /* main revision register offset */ + unsigned int sub_rev_reg; /* sub revision register offset */ unsigned int main_rev_shift; /* main revision offset in rev_reg */ unsigned int sub_rev_shift; /* sub revision offset in rev_reg */ + bool efuse; }; struct exynos_chipid_info { @@ -69,6 +72,8 @@ static const struct exynos_soc_id { { "EXYNOS990", 0xE9830000 }, { "EXYNOSAUTOV9", 0xAAA80000 }, { "EXYNOSAUTOV920", 0x0A920000 }, + /* Compatible with: google,gs101-otp */ + { "GS101", 0x9845000 }, }; static const char *exynos_product_id_to_name(unsigned int product_id) @@ -93,19 +98,53 @@ static int exynos_chipid_get_chipid_info(struct device *dev, return dev_err_probe(dev, ret, "failed to read Product ID\n"); soc_info->product_id = val & EXYNOS_MASK; - if (data->rev_reg != EXYNOS_CHIPID_REG_PRO_ID) { - ret = regmap_read(regmap, data->rev_reg, &val); + if (data->sub_rev_reg == EXYNOS_CHIPID_REG_PRO_ID) { + /* exynos4210 case */ + main_rev = (val >> data->main_rev_shift) & EXYNOS_REV_PART_MASK; + sub_rev = (val >> data->sub_rev_shift) & EXYNOS_REV_PART_MASK; + } else { + unsigned int val2; + + ret = regmap_read(regmap, data->sub_rev_reg, &val2); if (ret < 0) return dev_err_probe(dev, ret, "failed to read revision\n"); + + if (data->main_rev_reg == EXYNOS_CHIPID_REG_PRO_ID) + /* gs101 case */ + main_rev = (val >> data->main_rev_shift) & EXYNOS_REV_PART_MASK; + else + /* exynos850 case */ + main_rev = (val2 >> data->main_rev_shift) & EXYNOS_REV_PART_MASK; + + sub_rev = (val2 >> data->sub_rev_shift) & EXYNOS_REV_PART_MASK; } - main_rev = (val >> data->main_rev_shift) & EXYNOS_REV_PART_MASK; - sub_rev = (val >> data->sub_rev_shift) & EXYNOS_REV_PART_MASK; + soc_info->revision = (main_rev << EXYNOS_REV_PART_SHIFT) | sub_rev; return 0; } +static struct regmap *exynos_chipid_get_efuse_regmap(struct platform_device *pdev) +{ + struct resource *res; + void __iomem *base; + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) + return ERR_CAST(base); + + const struct regmap_config reg_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .use_relaxed_mmio = true, + .max_register = (resource_size(res) - reg_config.reg_stride), + }; + + return devm_regmap_init_mmio_clk(&pdev->dev, "pclk", base, ®_config); +} + static void exynos_chipid_unregister_soc(void *data) { soc_device_unregister(data); @@ -127,7 +166,11 @@ static int exynos_chipid_probe(struct platform_device *pdev) return dev_err_probe(dev, -EINVAL, "failed to get match data\n"); - regmap = device_node_to_regmap(dev->of_node); + if (drv_data->efuse) + regmap = exynos_chipid_get_efuse_regmap(pdev); + else + regmap = device_node_to_regmap(dev->of_node); + if (IS_ERR(regmap)) return dev_err_probe(dev, PTR_ERR(regmap), "failed to get regmap\n"); @@ -177,19 +220,28 @@ static int exynos_chipid_probe(struct platform_device *pdev) } static const struct exynos_chipid_variant exynos4210_chipid_drv_data = { - .rev_reg = 0x0, .main_rev_shift = 4, .sub_rev_shift = 0, }; static const struct exynos_chipid_variant exynos850_chipid_drv_data = { - .rev_reg = 0x10, + .main_rev_reg = 0x10, + .sub_rev_reg = 0x10, .main_rev_shift = 20, .sub_rev_shift = 16, }; +static const struct exynos_chipid_variant gs101_chipid_drv_data = { + .sub_rev_reg = 0x10, + .sub_rev_shift = 16, + .efuse = true, +}; + static const struct of_device_id exynos_chipid_of_device_ids[] = { { + .compatible = "google,gs101-otp", + .data = &gs101_chipid_drv_data, + }, { .compatible = "samsung,exynos4210-chipid", .data = &exynos4210_chipid_drv_data, }, { From 9c252f3c8f390fae4ca09de36c9262a35ae88ace Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 2 Jan 2026 13:50:31 +0100 Subject: [PATCH 040/171] bus: qcom-ebi2: Simplify with scoped for each OF child loop Use scoped for-each loop when iterating over device nodes to make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260102125030.65186-3-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/bus/qcom-ebi2.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/bus/qcom-ebi2.c b/drivers/bus/qcom-ebi2.c index c1fef1b4bd89..be8166565e7c 100644 --- a/drivers/bus/qcom-ebi2.c +++ b/drivers/bus/qcom-ebi2.c @@ -292,7 +292,6 @@ static void qcom_ebi2_setup_chipselect(struct device_node *np, static int qcom_ebi2_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - struct device_node *child; struct device *dev = &pdev->dev; struct resource *res; void __iomem *ebi2_base; @@ -348,15 +347,13 @@ static int qcom_ebi2_probe(struct platform_device *pdev) writel(val, ebi2_base); /* Walk over the child nodes and see what chipselects we use */ - for_each_available_child_of_node(np, child) { + for_each_available_child_of_node_scoped(np, child) { u32 csindex; /* Figure out the chipselect */ ret = of_property_read_u32(child, "reg", &csindex); - if (ret) { - of_node_put(child); + if (ret) return ret; - } if (csindex > 5) { dev_err(dev, From 380f8a4c734b029100dea5bc9e2f8982f72da6b9 Mon Sep 17 00:00:00 2001 From: Dale Whinham Date: Sat, 20 Dec 2025 16:26:30 +0100 Subject: [PATCH 041/171] firmware: qcom: scm: allow QSEECOM on Surface Pro 11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enables access to EFI variables on this machine. Signed-off-by: Dale Whinham Signed-off-by: Jérôme de Bretagne Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20251220-surface-sp11-for-next-v6-2-81f7451edb77@gmail.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 1a6f85e463e0..3dabb04094f9 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -2007,6 +2007,7 @@ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = { { .compatible = "lenovo,yoga-slim7x" }, { .compatible = "microsoft,arcata", }, { .compatible = "microsoft,blackrock" }, + { .compatible = "microsoft,denali", }, { .compatible = "microsoft,romulus13", }, { .compatible = "microsoft,romulus15", }, { .compatible = "qcom,hamoa-iot-evk" }, From da9e6b1a96b1eef47542ec46b67e3f4f883fed3b Mon Sep 17 00:00:00 2001 From: Unnathi Chalicheemala Date: Wed, 17 Dec 2025 20:04:19 +0530 Subject: [PATCH 042/171] firmware: qcom_scm: Add API to get waitqueue IRQ info Bootloader and firmware for SM8650 and older chipsets expect node name as "qcom_scm", in order to patch the wait queue IRQ information. However, DeviceTree uses node name "scm" and this mismatch prevents firmware from correctly identifying waitqueue IRQ information. Waitqueue IRQ is used for signaling between secure and non-secure worlds. To resolve this, introduce qcom_scm_get_waitq_irq() that'll get the hardware IRQ number to be used from firmware instead of relying on data provided by devicetree, thereby bypassing the DeviceTree node name mismatch. This hardware IRQ number is converted to a Linux IRQ number using newly qcom_scm_fill_irq_fwspec_params(). This Linux IRQ number is then supplied to the threaded_irq call. Reviewed-by: Bartosz Golaszewski Signed-off-by: Unnathi Chalicheemala Signed-off-by: Shivendra Pratap Reviewed-by: Mukesh Ojha Link: https://lore.kernel.org/r/20251217-multi_waitq_scm-v11-1-f21e50e792b8@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 62 +++++++++++++++++++++++++++++++- drivers/firmware/qcom/qcom_scm.h | 1 + 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 3dabb04094f9..78ee8e22a6a8 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -30,11 +30,18 @@ #include #include +#include + #include "qcom_scm.h" #include "qcom_tzmem.h" static u32 download_mode; +#define GIC_SPI_BASE 32 +#define GIC_MAX_SPI 1019 // SPIs in GICv3 spec range from 32..1019 +#define GIC_ESPI_BASE 4096 +#define GIC_MAX_ESPI 5119 // ESPIs in GICv3 spec range from 4096..5119 + struct qcom_scm { struct device *dev; struct clk *core_clk; @@ -2209,6 +2216,56 @@ bool qcom_scm_is_available(void) } EXPORT_SYMBOL_GPL(qcom_scm_is_available); +static int qcom_scm_fill_irq_fwspec_params(struct irq_fwspec *fwspec, u32 hwirq) +{ + if (hwirq >= GIC_SPI_BASE && hwirq <= GIC_MAX_SPI) { + fwspec->param[0] = GIC_SPI; + fwspec->param[1] = hwirq - GIC_SPI_BASE; + } else if (hwirq >= GIC_ESPI_BASE && hwirq <= GIC_MAX_ESPI) { + fwspec->param[0] = GIC_ESPI; + fwspec->param[1] = hwirq - GIC_ESPI_BASE; + } else { + WARN(1, "Unexpected hwirq: %d\n", hwirq); + return -ENXIO; + } + + fwspec->param[2] = IRQ_TYPE_EDGE_RISING; + fwspec->param_count = 3; + + return 0; +} + +static int qcom_scm_get_waitq_irq(struct qcom_scm *scm) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_WAITQ, + .cmd = QCOM_SCM_WAITQ_GET_INFO, + .owner = ARM_SMCCC_OWNER_SIP + }; + struct device_node *parent_irq_node; + struct irq_fwspec fwspec; + struct qcom_scm_res res; + u32 hwirq; + int ret; + + ret = qcom_scm_call_atomic(scm->dev, &desc, &res); + if (ret) + return ret; + + hwirq = res.result[1] & GENMASK(15, 0); + ret = qcom_scm_fill_irq_fwspec_params(&fwspec, hwirq); + if (ret) + return ret; + + parent_irq_node = of_irq_find_parent(scm->dev->of_node); + if (!parent_irq_node) + return -ENODEV; + + fwspec.fwnode = of_fwnode_handle(parent_irq_node); + + return irq_create_fwspec_mapping(&fwspec); +} + static int qcom_scm_assert_valid_wq_ctx(u32 wq_ctx) { /* FW currently only supports a single wq_ctx (zero). @@ -2382,7 +2439,10 @@ static int qcom_scm_probe(struct platform_device *pdev) return dev_err_probe(scm->dev, PTR_ERR(scm->mempool), "Failed to create the SCM memory pool\n"); - irq = platform_get_irq_optional(pdev, 0); + irq = qcom_scm_get_waitq_irq(scm); + if (irq < 0) + irq = platform_get_irq_optional(pdev, 0); + if (irq < 0) { if (irq != -ENXIO) return irq; diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h index a56c8212cc0c..8b1e2ea18a59 100644 --- a/drivers/firmware/qcom/qcom_scm.h +++ b/drivers/firmware/qcom/qcom_scm.h @@ -152,6 +152,7 @@ int qcom_scm_shm_bridge_enable(struct device *scm_dev); #define QCOM_SCM_SVC_WAITQ 0x24 #define QCOM_SCM_WAITQ_RESUME 0x02 #define QCOM_SCM_WAITQ_GET_WQ_CTX 0x03 +#define QCOM_SCM_WAITQ_GET_INFO 0x04 #define QCOM_SCM_SVC_GPU 0x28 #define QCOM_SCM_SVC_GPU_INIT_REGS 0x01 From ccd207ec848e768da41465352a0f52081eec6bb1 Mon Sep 17 00:00:00 2001 From: Unnathi Chalicheemala Date: Wed, 17 Dec 2025 20:04:20 +0530 Subject: [PATCH 043/171] firmware: qcom_scm: Support multiple waitq contexts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, only a single waitqueue context exists in the driver. Multi-waitqueue mechanism is added in firmware to support the case, when multiple VMs make SMC calls or single VM making multiple calls on same CPU. Enhance the driver to support multiple waitqueue when support is present in the firmware. When VMs make a SMC call, firmware allocates a waitqueue context, assuming the SMC call to be a blocking call. The SMC calls that cannot acquire resources, while execution in firmware, are returned to sleep in the calling VM. When the resource becomes available in the firmware, the VM gets notified to wake the sleeping thread and resume SMC call. The current qcom_scm driver supports single waitqueue as the old firmwares support only single waitqueue with waitqueue id zero. Multi-waitqueue mechanism is added in firmware starting SM8650 to support the case when multiple VMs make SMC calls or single VM making multiple calls on same CPU. To enable this support in qcom_scm driver, add support for handling multiple waitqueues. For instance, SM8650 firmware can allocate two such waitq contexts, so the driver needs to implement two waitqueue contexts. For a generalized approach, the number of supported waitqueues can be queried from the firmware using a SMC call. Introduce qcom_scm_query_waitq_count to get the number of waitqueue contexts supported by the firmware and allocate “N” unique waitqueue contexts with a dynamic sized array where each unique wq_ctx is associated with a struct completion variable for easy lookup. Older targets which support only a single waitqueue, may return an error for qcom_scm_query_waitq_count, set the wq_cnt to one for such failures. Reviewed-by: Bartosz Golaszewski Signed-off-by: Unnathi Chalicheemala Signed-off-by: Shivendra Pratap Reviewed-by: Mukesh Ojha Link: https://lore.kernel.org/r/20251217-multi_waitq_scm-v11-2-f21e50e792b8@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 72 ++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 22 deletions(-) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 78ee8e22a6a8..c10062430e29 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -48,7 +48,7 @@ struct qcom_scm { struct clk *iface_clk; struct clk *bus_clk; struct icc_path *path; - struct completion waitq_comp; + struct completion *waitq_comps; struct reset_controller_dev reset; /* control access to the interconnect path */ @@ -58,6 +58,7 @@ struct qcom_scm { u64 dload_mode_addr; struct qcom_tzmem_pool *mempool; + unsigned int wq_cnt; }; struct qcom_scm_current_perm_info { @@ -137,6 +138,8 @@ static const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = { #define QCOM_DLOAD_MINIDUMP 2 #define QCOM_DLOAD_BOTHDUMP 3 +#define QCOM_SCM_DEFAULT_WAITQ_COUNT 1 + static const char * const qcom_scm_convention_names[] = { [SMC_CONVENTION_UNKNOWN] = "unknown", [SMC_CONVENTION_ARM_32] = "smc arm 32", @@ -2235,6 +2238,23 @@ static int qcom_scm_fill_irq_fwspec_params(struct irq_fwspec *fwspec, u32 hwirq) return 0; } +static int qcom_scm_query_waitq_count(struct qcom_scm *scm) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_WAITQ, + .cmd = QCOM_SCM_WAITQ_GET_INFO, + .owner = ARM_SMCCC_OWNER_SIP + }; + struct qcom_scm_res res; + int ret; + + ret = qcom_scm_call_atomic(scm->dev, &desc, &res); + if (ret) + return ret; + + return res.result[0] & GENMASK(7, 0); +} + static int qcom_scm_get_waitq_irq(struct qcom_scm *scm) { struct qcom_scm_desc desc = { @@ -2266,42 +2286,40 @@ static int qcom_scm_get_waitq_irq(struct qcom_scm *scm) return irq_create_fwspec_mapping(&fwspec); } -static int qcom_scm_assert_valid_wq_ctx(u32 wq_ctx) +static struct completion *qcom_scm_get_completion(u32 wq_ctx) { - /* FW currently only supports a single wq_ctx (zero). - * TODO: Update this logic to include dynamic allocation and lookup of - * completion structs when FW supports more wq_ctx values. - */ - if (wq_ctx != 0) { - dev_err(__scm->dev, "Firmware unexpectedly passed non-zero wq_ctx\n"); - return -EINVAL; - } + struct completion *wq; - return 0; + if (WARN_ON_ONCE(wq_ctx >= __scm->wq_cnt)) + return ERR_PTR(-EINVAL); + + wq = &__scm->waitq_comps[wq_ctx]; + + return wq; } int qcom_scm_wait_for_wq_completion(u32 wq_ctx) { - int ret; + struct completion *wq; - ret = qcom_scm_assert_valid_wq_ctx(wq_ctx); - if (ret) - return ret; + wq = qcom_scm_get_completion(wq_ctx); + if (IS_ERR(wq)) + return PTR_ERR(wq); - wait_for_completion(&__scm->waitq_comp); + wait_for_completion(wq); return 0; } static int qcom_scm_waitq_wakeup(unsigned int wq_ctx) { - int ret; + struct completion *wq; - ret = qcom_scm_assert_valid_wq_ctx(wq_ctx); - if (ret) - return ret; + wq = qcom_scm_get_completion(wq_ctx); + if (IS_ERR(wq)) + return PTR_ERR(wq); - complete(&__scm->waitq_comp); + complete(wq); return 0; } @@ -2377,6 +2395,7 @@ static int qcom_scm_probe(struct platform_device *pdev) struct qcom_tzmem_pool_config pool_config; struct qcom_scm *scm; int irq, ret; + int i; scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL); if (!scm) @@ -2387,7 +2406,6 @@ static int qcom_scm_probe(struct platform_device *pdev) if (ret < 0) return ret; - init_completion(&scm->waitq_comp); mutex_init(&scm->scm_bw_lock); scm->path = devm_of_icc_get(&pdev->dev, NULL); @@ -2439,6 +2457,16 @@ static int qcom_scm_probe(struct platform_device *pdev) return dev_err_probe(scm->dev, PTR_ERR(scm->mempool), "Failed to create the SCM memory pool\n"); + ret = qcom_scm_query_waitq_count(scm); + scm->wq_cnt = ret < 0 ? QCOM_SCM_DEFAULT_WAITQ_COUNT : ret; + scm->waitq_comps = devm_kcalloc(&pdev->dev, scm->wq_cnt, sizeof(*scm->waitq_comps), + GFP_KERNEL); + if (!scm->waitq_comps) + return -ENOMEM; + + for (i = 0; i < scm->wq_cnt; i++) + init_completion(&scm->waitq_comps[i]); + irq = qcom_scm_get_waitq_irq(scm); if (irq < 0) irq = platform_get_irq_optional(pdev, 0); From 366f05e348b2ba454869ba7148ace6f25f229540 Mon Sep 17 00:00:00 2001 From: Unnathi Chalicheemala Date: Wed, 17 Dec 2025 20:04:21 +0530 Subject: [PATCH 044/171] firmware: qcom_scm: Use TASK_IDLE state in wait_for_wq_completion() When the kernel issues an SMC (Secure Monitor Call) and the firmware requests the kernel to wait, the waiting thread enters an uninterruptible (D) state. In case of an extended wait request by the firmware, any device suspend request, cannot proceed because of the thread stuck in D state. This blocks the device suspend. Replace wait_for_completion() with wait_for_completion_state(..., TASK_IDLE), so that the waiting thread, show up in TASK_IDLE state, instead of TASK_UNINTERRUPTIBLE (D state). This allows the thread to block until completion, without blocking the device suspend. Reviewed-by: Mukesh Ojha Reviewed-by: Bartosz Golaszewski Signed-off-by: Unnathi Chalicheemala Signed-off-by: Shivendra Pratap Reviewed-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20251217-multi_waitq_scm-v11-3-f21e50e792b8@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index c10062430e29..3a78092b97f9 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -2306,7 +2306,7 @@ int qcom_scm_wait_for_wq_completion(u32 wq_ctx) if (IS_ERR(wq)) return PTR_ERR(wq); - wait_for_completion(wq); + wait_for_completion_state(wq, TASK_IDLE); return 0; } From 1e07ebe744fb522983bd52a4a6148601675330c7 Mon Sep 17 00:00:00 2001 From: Haotian Zhang Date: Wed, 17 Dec 2025 12:13:38 +0800 Subject: [PATCH 045/171] clk: qcom: Return correct error code in qcom_cc_probe_by_index() When devm_platform_ioremap_resource() fails, it returns various error codes. Returning a hardcoded -ENOMEM masks the actual failure reason. Use PTR_ERR() to propagate the actual error code returned by devm_platform_ioremap_resource() instead of -ENOMEM. Fixes: 75e0a1e30191 ("clk: qcom: define probe by index API as common API") Signed-off-by: Haotian Zhang Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20251217041338.2432-1-vulab@iscas.ac.cn Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index 121591886774..eec369d2173b 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -454,7 +454,7 @@ int qcom_cc_probe_by_index(struct platform_device *pdev, int index, base = devm_platform_ioremap_resource(pdev, index); if (IS_ERR(base)) - return -ENOMEM; + return PTR_ERR(base); regmap = devm_regmap_init_mmio(&pdev->dev, base, desc->config); if (IS_ERR(regmap)) From 4cc19518da40573fc1057c618a5b1acf8a941472 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 8 Dec 2025 03:08:45 +0100 Subject: [PATCH 046/171] tee: qcomtee: call: Fix confusing cleanup.h syntax Initializing automatic __free variables to NULL without need (e.g. branches with different allocations), followed by actual allocation is in contrary to explicit coding rules guiding cleanup.h: "Given that the "__free(...) = NULL" pattern for variables defined at the top of the function poses this potential interdependency problem the recommendation is to always define and assign variables in one statement and not group variable definitions at the top of the function when __free() is used." Code does not have a bug, but is less readable and uses discouraged coding practice, so fix that by moving declaration to the place of assignment. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Amirreza Zarrabi Signed-off-by: Jens Wiklander --- drivers/tee/qcomtee/call.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/tee/qcomtee/call.c b/drivers/tee/qcomtee/call.c index 65f9140d4e1f..8f8830f0df26 100644 --- a/drivers/tee/qcomtee/call.c +++ b/drivers/tee/qcomtee/call.c @@ -395,9 +395,7 @@ static int qcomtee_object_invoke(struct tee_context *ctx, struct tee_ioctl_object_invoke_arg *arg, struct tee_param *params) { - struct qcomtee_object_invoke_ctx *oic __free(kfree) = NULL; struct qcomtee_context_data *ctxdata = ctx->data; - struct qcomtee_arg *u __free(kfree) = NULL; struct qcomtee_object *object; int i, ret, result; @@ -412,12 +410,14 @@ static int qcomtee_object_invoke(struct tee_context *ctx, } /* Otherwise, invoke a QTEE object: */ - oic = qcomtee_object_invoke_ctx_alloc(ctx); + struct qcomtee_object_invoke_ctx *oic __free(kfree) = + qcomtee_object_invoke_ctx_alloc(ctx); if (!oic) return -ENOMEM; /* +1 for ending QCOMTEE_ARG_TYPE_INV. */ - u = kcalloc(arg->num_params + 1, sizeof(*u), GFP_KERNEL); + struct qcomtee_arg *u __free(kfree) = kcalloc(arg->num_params + 1, sizeof(*u), + GFP_KERNEL); if (!u) return -ENOMEM; @@ -562,9 +562,8 @@ static int qcomtee_supp_send(struct tee_context *ctx, u32 errno, u32 num_params, static int qcomtee_open(struct tee_context *ctx) { - struct qcomtee_context_data *ctxdata __free(kfree) = NULL; - - ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL); + struct qcomtee_context_data *ctxdata __free(kfree) = kzalloc(sizeof(*ctxdata), + GFP_KERNEL); if (!ctxdata) return -ENOMEM; @@ -645,12 +644,12 @@ static void qcomtee_get_version(struct tee_device *teedev, static void qcomtee_get_qtee_feature_list(struct tee_context *ctx, u32 id, u32 *version) { - struct qcomtee_object_invoke_ctx *oic __free(kfree) = NULL; struct qcomtee_object *client_env, *service; struct qcomtee_arg u[3] = { 0 }; int result; - oic = qcomtee_object_invoke_ctx_alloc(ctx); + struct qcomtee_object_invoke_ctx *oic __free(kfree) = + qcomtee_object_invoke_ctx_alloc(ctx); if (!oic) return; From 7c4c14ad3d8cbc3507231e3f3bc090dfbfcabf9d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 8 Dec 2025 03:08:46 +0100 Subject: [PATCH 047/171] tee: qcomtee: mem: Fix confusing cleanup.h syntax Initializing automatic __free variables to NULL without need (e.g. branches with different allocations), followed by actual allocation is in contrary to explicit coding rules guiding cleanup.h: "Given that the "__free(...) = NULL" pattern for variables defined at the top of the function poses this potential interdependency problem the recommendation is to always define and assign variables in one statement and not group variable definitions at the top of the function when __free() is used." Code does not have a bug, but is less readable and uses discouraged coding practice, so fix that by moving declaration to the place of assignment. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Amirreza Zarrabi Signed-off-by: Jens Wiklander --- drivers/tee/qcomtee/mem_obj.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tee/qcomtee/mem_obj.c b/drivers/tee/qcomtee/mem_obj.c index 228a3e30a31b..a16f8fc39b8d 100644 --- a/drivers/tee/qcomtee/mem_obj.c +++ b/drivers/tee/qcomtee/mem_obj.c @@ -88,11 +88,11 @@ int qcomtee_memobj_param_to_object(struct qcomtee_object **object, struct tee_param *param, struct tee_context *ctx) { - struct qcomtee_mem_object *mem_object __free(kfree) = NULL; struct tee_shm *shm; int err; - mem_object = kzalloc(sizeof(*mem_object), GFP_KERNEL); + struct qcomtee_mem_object *mem_object __free(kfree) = kzalloc(sizeof(*mem_object), + GFP_KERNEL); if (!mem_object) return -ENOMEM; From 1c05d9a4cab2abfb93ebce5edaa17752126b6d35 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 8 Dec 2025 03:08:47 +0100 Subject: [PATCH 048/171] tee: qcomtee: user: Fix confusing cleanup.h syntax Initializing automatic __free variables to NULL without need (e.g. branches with different allocations), followed by actual allocation is in contrary to explicit coding rules guiding cleanup.h: "Given that the "__free(...) = NULL" pattern for variables defined at the top of the function poses this potential interdependency problem the recommendation is to always define and assign variables in one statement and not group variable definitions at the top of the function when __free() is used." Code does not have a bug, but is less readable and uses discouraged coding practice, so fix that by moving declaration to the place of assignment. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Amirreza Zarrabi Signed-off-by: Jens Wiklander --- drivers/tee/qcomtee/user_obj.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/tee/qcomtee/user_obj.c b/drivers/tee/qcomtee/user_obj.c index 0139905f2684..6aa3aefd67f0 100644 --- a/drivers/tee/qcomtee/user_obj.c +++ b/drivers/tee/qcomtee/user_obj.c @@ -228,10 +228,10 @@ static int qcomtee_user_object_dispatch(struct qcomtee_object_invoke_ctx *oic, { struct qcomtee_user_object *uo = to_qcomtee_user_object(object); struct qcomtee_context_data *ctxdata = uo->ctx->data; - struct qcomtee_ureq *ureq __free(kfree) = NULL; int errno; - ureq = kzalloc(sizeof(*ureq), GFP_KERNEL); + struct qcomtee_ureq *ureq __free(kfree) = kzalloc(sizeof(*ureq), + GFP_KERNEL); if (!ureq) return -ENOMEM; @@ -367,10 +367,10 @@ int qcomtee_user_param_to_object(struct qcomtee_object **object, struct tee_param *param, struct tee_context *ctx) { - struct qcomtee_user_object *user_object __free(kfree) = NULL; int err; - user_object = kzalloc(sizeof(*user_object), GFP_KERNEL); + struct qcomtee_user_object *user_object __free(kfree) = + kzalloc(sizeof(*user_object), GFP_KERNEL); if (!user_object) return -ENOMEM; From 499ea377edde1c085f872d35ffe370f54175bef0 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 4 Jan 2026 18:01:19 +0100 Subject: [PATCH 049/171] optee: update outdated comment The function cmd_alloc_suppl() was renamed as optee_rpc_cmd_alloc_suppl() in commit c51a564a5b48 ("optee: isolate smc abi"). Update the comment accordingly. Signed-off-by: Julia Lawall Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/optee/rpc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index ebbbd42b0e3e..97fc5b14db0c 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -247,8 +247,8 @@ void optee_rpc_cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm) param.u.value.c = 0; /* - * Match the tee_shm_get_from_id() in cmd_alloc_suppl() as secure - * world has released its reference. + * Match the tee_shm_get_from_id() in optee_rpc_cmd_alloc_suppl() + * as secure world has released its reference. * * It's better to do this before sending the request to supplicant * as we'd like to let the process doing the initial allocation to From a1a359e12a4226f3ade63ee1c08cf21ce896a07a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 2 Jan 2026 13:48:02 +0100 Subject: [PATCH 050/171] soc: dove: pmu: Simplify with scoped for each OF child loop Use scoped for-each loop when iterating over device nodes to make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Andrew Lunn Signed-off-by: Gregory CLEMENT --- drivers/soc/dove/pmu.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/soc/dove/pmu.c b/drivers/soc/dove/pmu.c index 7bbd3f940e4d..dd8ade8e9ee8 100644 --- a/drivers/soc/dove/pmu.c +++ b/drivers/soc/dove/pmu.c @@ -371,7 +371,7 @@ int __init dove_init_pmu_legacy(const struct dove_pmu_initdata *initdata) */ int __init dove_init_pmu(void) { - struct device_node *np_pmu, *domains_node, *np; + struct device_node *np_pmu, *domains_node; struct pmu_data *pmu; int ret, parent_irq; @@ -404,21 +404,18 @@ int __init dove_init_pmu(void) pmu_reset_init(pmu); - for_each_available_child_of_node(domains_node, np) { + for_each_available_child_of_node_scoped(domains_node, np) { struct of_phandle_args args; struct pmu_domain *domain; domain = kzalloc(sizeof(*domain), GFP_KERNEL); - if (!domain) { - of_node_put(np); + if (!domain) break; - } domain->pmu = pmu; domain->base.name = kasprintf(GFP_KERNEL, "%pOFn", np); if (!domain->base.name) { kfree(domain); - of_node_put(np); break; } From 5cd2a743ce384a5a4a1b5b09d4983df2592fbe1c Mon Sep 17 00:00:00 2001 From: Pankaj Patil Date: Mon, 5 Jan 2026 18:30:50 +0530 Subject: [PATCH 051/171] dt-bindings: cache: qcom,llcc: Remove duplicate llcc7_base for Glymur Drop redundant llcc7_base entry from Glymur LLCC reg-items Fixes: bd0b8028ce5f ("dt-bindings: cache: qcom,llcc: Document Glymur LLCC block") Signed-off-by: Pankaj Patil Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260105130050.1062903-1-pankaj.patil@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/cache/qcom,llcc.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/cache/qcom,llcc.yaml b/Documentation/devicetree/bindings/cache/qcom,llcc.yaml index 4e99c405aea3..6671e461e34a 100644 --- a/Documentation/devicetree/bindings/cache/qcom,llcc.yaml +++ b/Documentation/devicetree/bindings/cache/qcom,llcc.yaml @@ -119,7 +119,6 @@ allOf: - const: llcc5_base - const: llcc6_base - const: llcc7_base - - const: llcc7_base - const: llcc8_base - const: llcc9_base - const: llcc10_base From 168d2fb7805510aba3b0871414ec93c2364f9e23 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 2 Jan 2026 13:47:30 +0100 Subject: [PATCH 052/171] soc: ti: knav_qmss: Remove ENOMEM printks Printing messages on ENOMEM errors is redundant and discouraged, because core already prints detailed report. Simplify the code by dropping such dev_err(). Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260102124729.63964-6-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Nishanth Menon --- drivers/soc/ti/knav_qmss_queue.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c index 6e56e7609ccd..ff097a1531a9 100644 --- a/drivers/soc/ti/knav_qmss_queue.c +++ b/drivers/soc/ti/knav_qmss_queue.c @@ -1091,7 +1091,6 @@ static int knav_queue_setup_regions(struct knav_device *kdev, region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL); if (!region) { of_node_put(child); - dev_err(dev, "out of memory allocating region\n"); return -ENOMEM; } @@ -1409,7 +1408,6 @@ static int knav_queue_init_qmgrs(struct knav_device *kdev, qmgr = devm_kzalloc(dev, sizeof(*qmgr), GFP_KERNEL); if (!qmgr) { of_node_put(child); - dev_err(dev, "out of memory allocating qmgr\n"); return -ENOMEM; } @@ -1509,7 +1507,6 @@ static int knav_queue_init_pdsps(struct knav_device *kdev, pdsp = devm_kzalloc(dev, sizeof(*pdsp), GFP_KERNEL); if (!pdsp) { of_node_put(child); - dev_err(dev, "out of memory allocating pdsp\n"); return -ENOMEM; } pdsp->name = knav_queue_find_name(child); From c076d74383f1ce2c8db4c83c40c714c8751d5d01 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 2 Jan 2026 13:47:31 +0100 Subject: [PATCH 053/171] soc: ti: knav_dma: Remove ENOMEM printks Printing messages on ENOMEM errors is redundant and discouraged, because core already prints detailed report. Simplify the code by dropping such dev_err(). Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260102124729.63964-7-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Nishanth Menon --- drivers/soc/ti/knav_dma.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/soc/ti/knav_dma.c b/drivers/soc/ti/knav_dma.c index 553ae7ee20f1..93365bac2631 100644 --- a/drivers/soc/ti/knav_dma.c +++ b/drivers/soc/ti/knav_dma.c @@ -716,10 +716,8 @@ static int knav_dma_probe(struct platform_device *pdev) kdev = devm_kzalloc(dev, sizeof(struct knav_dma_pool_device), GFP_KERNEL); - if (!kdev) { - dev_err(dev, "could not allocate driver mem\n"); + if (!kdev) return -ENOMEM; - } kdev->dev = dev; INIT_LIST_HEAD(&kdev->list); From 9a97b2154ef51dd079bbaf247289e6d82210c03b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 2 Jan 2026 13:47:32 +0100 Subject: [PATCH 054/171] soc: ti: knav_dma: Simplify error messages in probe Simplify the code by using dev_err_probe() and local 'dev' variable. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260102124729.63964-8-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Nishanth Menon --- drivers/soc/ti/knav_dma.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/soc/ti/knav_dma.c b/drivers/soc/ti/knav_dma.c index 93365bac2631..7adf1664da60 100644 --- a/drivers/soc/ti/knav_dma.c +++ b/drivers/soc/ti/knav_dma.c @@ -709,10 +709,8 @@ static int knav_dma_probe(struct platform_device *pdev) struct device_node *child; int ret = 0; - if (!node) { - dev_err(&pdev->dev, "could not find device info\n"); - return -EINVAL; - } + if (!node) + return dev_err_probe(dev, -EINVAL, "could not find device info\n"); kdev = devm_kzalloc(dev, sizeof(struct knav_dma_pool_device), GFP_KERNEL); @@ -725,7 +723,7 @@ static int knav_dma_probe(struct platform_device *pdev) pm_runtime_enable(kdev->dev); ret = pm_runtime_resume_and_get(kdev->dev); if (ret < 0) { - dev_err(kdev->dev, "unable to enable pktdma, err %d\n", ret); + dev_err(dev, "unable to enable pktdma, err %d\n", ret); goto err_pm_disable; } @@ -734,14 +732,13 @@ static int knav_dma_probe(struct platform_device *pdev) ret = dma_init(node, child); if (ret) { of_node_put(child); - dev_err(&pdev->dev, "init failed with %d\n", ret); + dev_err(dev, "init failed with %d\n", ret); break; } } if (list_empty(&kdev->list)) { - dev_err(dev, "no valid dma instance\n"); - ret = -ENODEV; + ret = dev_err_probe(dev, -ENODEV, "no valid dma instance\n"); goto err_put_sync; } From 6b222f28999c481a6531c7b5fbbf95f877875d23 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 2 Jan 2026 13:47:33 +0100 Subject: [PATCH 055/171] soc: ti: knav: Simplify with scoped for each OF child loop Use scoped for-each loop when iterating over device nodes to make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260102124729.63964-9-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Nishanth Menon --- drivers/soc/ti/knav_qmss_queue.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c index ff097a1531a9..86d7a9c9ae01 100644 --- a/drivers/soc/ti/knav_qmss_queue.c +++ b/drivers/soc/ti/knav_qmss_queue.c @@ -1079,7 +1079,6 @@ static int knav_queue_setup_regions(struct knav_device *kdev, struct device_node *regions __free(device_node) = of_get_child_by_name(node, "descriptor-regions"); struct knav_region *region; - struct device_node *child; u32 temp[2]; int ret; @@ -1087,12 +1086,10 @@ static int knav_queue_setup_regions(struct knav_device *kdev, return dev_err_probe(dev, -ENODEV, "descriptor-regions not specified\n"); - for_each_child_of_node(regions, child) { + for_each_child_of_node_scoped(regions, child) { region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL); - if (!region) { - of_node_put(child); + if (!region) return -ENOMEM; - } region->name = knav_queue_find_name(child); of_property_read_u32(child, "id", ®ion->id); @@ -1396,7 +1393,6 @@ static int knav_queue_init_qmgrs(struct knav_device *kdev, struct device_node *qmgrs __free(device_node) = of_get_child_by_name(node, "qmgrs"); struct knav_qmgr_info *qmgr; - struct device_node *child; u32 temp[2]; int ret; @@ -1404,12 +1400,10 @@ static int knav_queue_init_qmgrs(struct knav_device *kdev, return dev_err_probe(dev, -ENODEV, "queue manager info not specified\n"); - for_each_child_of_node(qmgrs, child) { + for_each_child_of_node_scoped(qmgrs, child) { qmgr = devm_kzalloc(dev, sizeof(*qmgr), GFP_KERNEL); - if (!qmgr) { - of_node_put(child); + if (!qmgr) return -ENOMEM; - } ret = of_property_read_u32_array(child, "managed-queues", temp, 2); @@ -1501,14 +1495,12 @@ static int knav_queue_init_pdsps(struct knav_device *kdev, { struct device *dev = kdev->dev; struct knav_pdsp_info *pdsp; - struct device_node *child; - for_each_child_of_node(pdsps, child) { + for_each_child_of_node_scoped(pdsps, child) { pdsp = devm_kzalloc(dev, sizeof(*pdsp), GFP_KERNEL); - if (!pdsp) { - of_node_put(child); + if (!pdsp) return -ENOMEM; - } + pdsp->name = knav_queue_find_name(child); pdsp->iram = knav_queue_map_reg(kdev, child, From 126f61528d34658d80ec827f981c540c1230fdc4 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 2 Jan 2026 13:47:34 +0100 Subject: [PATCH 056/171] soc: ti: knav_dma: Simplify with scoped for each OF child loop Use scoped for-each loop when iterating over device nodes to make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260102124729.63964-10-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Nishanth Menon --- drivers/soc/ti/knav_dma.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/soc/ti/knav_dma.c b/drivers/soc/ti/knav_dma.c index 7adf1664da60..e5f5e3142fc4 100644 --- a/drivers/soc/ti/knav_dma.c +++ b/drivers/soc/ti/knav_dma.c @@ -706,7 +706,6 @@ static int knav_dma_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *node = pdev->dev.of_node; - struct device_node *child; int ret = 0; if (!node) @@ -728,10 +727,9 @@ static int knav_dma_probe(struct platform_device *pdev) } /* Initialise all packet dmas */ - for_each_child_of_node(node, child) { + for_each_child_of_node_scoped(node, child) { ret = dma_init(node, child); if (ret) { - of_node_put(child); dev_err(dev, "init failed with %d\n", ret); break; } From f50da52e5b2ed73913cc6d0db7c81cd33ced3ae7 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Wed, 10 Dec 2025 10:43:26 +0900 Subject: [PATCH 057/171] dt-bindings: crypto: qcom,prng: document Milos Document Milos SoC compatible for the True Random Number Generator. Acked-by: Rob Herring (Arm) Reviewed-by: Bjorn Andersson Signed-off-by: Luca Weiss Link: https://lore.kernel.org/r/20251210-sm7635-fp6-initial-v4-2-b05fddd8b45c@fairphone.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/crypto/qcom,prng.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/crypto/qcom,prng.yaml b/Documentation/devicetree/bindings/crypto/qcom,prng.yaml index 597441d94cf1..a9674e29144e 100644 --- a/Documentation/devicetree/bindings/crypto/qcom,prng.yaml +++ b/Documentation/devicetree/bindings/crypto/qcom,prng.yaml @@ -21,6 +21,7 @@ properties: - qcom,ipq5424-trng - qcom,ipq9574-trng - qcom,kaanapali-trng + - qcom,milos-trng - qcom,qcs615-trng - qcom,qcs8300-trng - qcom,sa8255p-trng From 42f2799124a4d0081b0c8c50980e37769e8d6880 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Wed, 10 Dec 2025 10:43:27 +0900 Subject: [PATCH 058/171] dt-bindings: qcom,pdc: document the Milos Power Domain Controller Document the Power Domain Controller on the Milos SoC. Acked-by: Rob Herring (Arm) Reviewed-by: Bjorn Andersson Signed-off-by: Luca Weiss Link: https://lore.kernel.org/r/20251210-sm7635-fp6-initial-v4-3-b05fddd8b45c@fairphone.com Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/interrupt-controller/qcom,pdc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml b/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml index b26246de3186..f9321366cae4 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml @@ -28,6 +28,7 @@ properties: - enum: - qcom,glymur-pdc - qcom,kaanapali-pdc + - qcom,milos-pdc - qcom,qcs615-pdc - qcom,qcs8300-pdc - qcom,qdu1000-pdc From 4acd805157102eef1b98794450d2e599c7497542 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 5 Jan 2026 15:28:57 -0600 Subject: [PATCH 059/171] dt-bindings: soc: samsung: exynos-pmu: Drop unnecessary select schema The "select" schema is not necessary because "syscon" compatible is already excluded from the default select logic. Signed-off-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260105212858.3454174-1-robh@kernel.org Signed-off-by: Krzysztof Kozlowski --- .../bindings/soc/samsung/exynos-pmu.yaml | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml index 0d52b0e9bc17..a6bb3093b10a 100644 --- a/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml +++ b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml @@ -9,28 +9,6 @@ title: Samsung Exynos SoC series Power Management Unit (PMU) maintainers: - Krzysztof Kozlowski -# Custom select to avoid matching all nodes with 'syscon' -select: - properties: - compatible: - contains: - enum: - - google,gs101-pmu - - samsung,exynos3250-pmu - - samsung,exynos4210-pmu - - samsung,exynos4212-pmu - - samsung,exynos4412-pmu - - samsung,exynos5250-pmu - - samsung,exynos5260-pmu - - samsung,exynos5410-pmu - - samsung,exynos5420-pmu - - samsung,exynos5433-pmu - - samsung,exynos7-pmu - - samsung,exynos850-pmu - - samsung-s5pv210-pmu - required: - - compatible - properties: compatible: oneOf: From 148891e95014b5dc5878acefa57f1940c281c431 Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Wed, 3 Dec 2025 01:44:38 +0800 Subject: [PATCH 060/171] bus: fsl-mc: fix use-after-free in driver_override_show() The driver_override_show() function reads the driver_override string without holding the device_lock. However, driver_override_store() uses driver_set_override(), which modifies and frees the string while holding the device_lock. This can result in a concurrent use-after-free if the string is freed by the store function while being read by the show function. Fix this by holding the device_lock around the read operation. Fixes: 1f86a00c1159 ("bus/fsl-mc: add support for 'driver_override' in the mc-bus") Cc: stable@vger.kernel.org Signed-off-by: Gui-Dong Han Reviewed-by: Ioana Ciornei Link: https://lore.kernel.org/r/20251202174438.12658-1-hanguidong02@gmail.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/bus/fsl-mc/fsl-mc-bus.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c index c08c04047ae2..08b99b0b342f 100644 --- a/drivers/bus/fsl-mc/fsl-mc-bus.c +++ b/drivers/bus/fsl-mc/fsl-mc-bus.c @@ -231,8 +231,12 @@ static ssize_t driver_override_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); + ssize_t len; - return sysfs_emit(buf, "%s\n", mc_dev->driver_override); + device_lock(dev); + len = sysfs_emit(buf, "%s\n", mc_dev->driver_override); + device_unlock(dev); + return len; } static DEVICE_ATTR_RW(driver_override); From 66a4ff38d7b213a1278840a754c6d357e7745b24 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 2 Jan 2026 13:47:55 +0100 Subject: [PATCH 061/171] soc: fsl: qe: Simplify with scoped for each OF child loop Use scoped for-each loop when iterating over device nodes to make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Acked-by: Herve Codina Link: https://lore.kernel.org/r/20260102124754.64122-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/qe/qmc.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/soc/fsl/qe/qmc.c b/drivers/soc/fsl/qe/qmc.c index da5ea6d35618..c4587b32a59b 100644 --- a/drivers/soc/fsl/qe/qmc.c +++ b/drivers/soc/fsl/qe/qmc.c @@ -1284,31 +1284,26 @@ static unsigned int qmc_nb_chans(struct qmc *qmc) static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np) { - struct device_node *chan_np; struct qmc_chan *chan; const char *mode; u32 chan_id; u64 ts_mask; int ret; - for_each_available_child_of_node(np, chan_np) { + for_each_available_child_of_node_scoped(np, chan_np) { ret = of_property_read_u32(chan_np, "reg", &chan_id); if (ret) { dev_err(qmc->dev, "%pOF: failed to read reg\n", chan_np); - of_node_put(chan_np); return ret; } if (chan_id > 63) { dev_err(qmc->dev, "%pOF: Invalid chan_id\n", chan_np); - of_node_put(chan_np); return -EINVAL; } chan = devm_kzalloc(qmc->dev, sizeof(*chan), GFP_KERNEL); - if (!chan) { - of_node_put(chan_np); + if (!chan) return -ENOMEM; - } chan->id = chan_id; spin_lock_init(&chan->ts_lock); @@ -1319,7 +1314,6 @@ static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np) if (ret) { dev_err(qmc->dev, "%pOF: failed to read fsl,tx-ts-mask\n", chan_np); - of_node_put(chan_np); return ret; } chan->tx_ts_mask_avail = ts_mask; @@ -1329,7 +1323,6 @@ static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np) if (ret) { dev_err(qmc->dev, "%pOF: failed to read fsl,rx-ts-mask\n", chan_np); - of_node_put(chan_np); return ret; } chan->rx_ts_mask_avail = ts_mask; @@ -1340,7 +1333,6 @@ static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np) if (ret && ret != -EINVAL) { dev_err(qmc->dev, "%pOF: failed to read fsl,operational-mode\n", chan_np); - of_node_put(chan_np); return ret; } if (!strcmp(mode, "transparent")) { @@ -1350,7 +1342,6 @@ static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np) } else { dev_err(qmc->dev, "%pOF: Invalid fsl,operational-mode (%s)\n", chan_np, mode); - of_node_put(chan_np); return -EINVAL; } From 21ecfe424bf332f02d7cba830466a6e29d6fb847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 15 Dec 2025 15:16:34 +0100 Subject: [PATCH 062/171] hwrng: optee - Make use of module_tee_client_driver() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce boilerplate by using the newly introduced module_tee_client_driver(). That takes care of assigning the driver's bus, so the explicit assigning in this driver can be dropped. Reviewed-by: Sumit Garg Signed-off-by: Uwe Kleine-König Acked-by: Herbert Xu Signed-off-by: Jens Wiklander --- drivers/char/hw_random/optee-rng.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/char/hw_random/optee-rng.c b/drivers/char/hw_random/optee-rng.c index 96b5d546d136..6ee748c0cf57 100644 --- a/drivers/char/hw_random/optee-rng.c +++ b/drivers/char/hw_random/optee-rng.c @@ -281,24 +281,12 @@ static struct tee_client_driver optee_rng_driver = { .id_table = optee_rng_id_table, .driver = { .name = DRIVER_NAME, - .bus = &tee_bus_type, .probe = optee_rng_probe, .remove = optee_rng_remove, }, }; -static int __init optee_rng_mod_init(void) -{ - return driver_register(&optee_rng_driver.driver); -} - -static void __exit optee_rng_mod_exit(void) -{ - driver_unregister(&optee_rng_driver.driver); -} - -module_init(optee_rng_mod_init); -module_exit(optee_rng_mod_exit); +module_tee_client_driver(optee_rng_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Sumit Garg "); From 5e9151cecbbefe60fbe459d9f7c65a32d2bca548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 15 Dec 2025 15:16:35 +0100 Subject: [PATCH 063/171] hwrng: optee - Make use of tee bus methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tee bus got dedicated callbacks for probe and remove. Make use of these. This fixes a runtime warning about the driver needing to be converted to the bus methods. Reviewed-by: Sumit Garg Signed-off-by: Uwe Kleine-König Acked-by: Herbert Xu Signed-off-by: Jens Wiklander --- drivers/char/hw_random/optee-rng.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/char/hw_random/optee-rng.c b/drivers/char/hw_random/optee-rng.c index 6ee748c0cf57..5a3fa0b38497 100644 --- a/drivers/char/hw_random/optee-rng.c +++ b/drivers/char/hw_random/optee-rng.c @@ -211,9 +211,9 @@ static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) return 0; } -static int optee_rng_probe(struct device *dev) +static int optee_rng_probe(struct tee_client_device *rng_device) { - struct tee_client_device *rng_device = to_tee_client_device(dev); + struct device *dev = &rng_device->dev; int ret = 0, err = -ENODEV; struct tee_ioctl_open_session_arg sess_arg; @@ -261,12 +261,10 @@ out_ctx: return err; } -static int optee_rng_remove(struct device *dev) +static void optee_rng_remove(struct tee_client_device *tee_dev) { tee_client_close_session(pvt_data.ctx, pvt_data.session_id); tee_client_close_context(pvt_data.ctx); - - return 0; } static const struct tee_client_device_id optee_rng_id_table[] = { @@ -278,11 +276,11 @@ static const struct tee_client_device_id optee_rng_id_table[] = { MODULE_DEVICE_TABLE(tee, optee_rng_id_table); static struct tee_client_driver optee_rng_driver = { + .probe = optee_rng_probe, + .remove = optee_rng_remove, .id_table = optee_rng_id_table, .driver = { .name = DRIVER_NAME, - .probe = optee_rng_probe, - .remove = optee_rng_remove, }, }; From 0a6441a30b839b7e0afd80b7ffe91f1baeb44415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 15 Dec 2025 15:16:38 +0100 Subject: [PATCH 064/171] efi: stmm: Make use of module_tee_client_driver() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce boilerplate by using the newly introduced module_tee_client_driver(). That takes care of assigning the driver's bus, so the explicit assigning in this driver can be dropped. Reviewed-by: Sumit Garg Signed-off-by: Uwe Kleine-König Acked-by: Ilias Apalodimas Signed-off-by: Jens Wiklander --- drivers/firmware/efi/stmm/tee_stmm_efi.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/firmware/efi/stmm/tee_stmm_efi.c b/drivers/firmware/efi/stmm/tee_stmm_efi.c index 65c0fe1ba275..5903811858b6 100644 --- a/drivers/firmware/efi/stmm/tee_stmm_efi.c +++ b/drivers/firmware/efi/stmm/tee_stmm_efi.c @@ -584,24 +584,12 @@ static struct tee_client_driver tee_stmm_efi_driver = { .id_table = tee_stmm_efi_id_table, .driver = { .name = "tee-stmm-efi", - .bus = &tee_bus_type, .probe = tee_stmm_efi_probe, .remove = tee_stmm_efi_remove, }, }; -static int __init tee_stmm_efi_mod_init(void) -{ - return driver_register(&tee_stmm_efi_driver.driver); -} - -static void __exit tee_stmm_efi_mod_exit(void) -{ - driver_unregister(&tee_stmm_efi_driver.driver); -} - -module_init(tee_stmm_efi_mod_init); -module_exit(tee_stmm_efi_mod_exit); +module_tee_client_driver(tee_stmm_efi_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ilias Apalodimas "); From 7a5f567ab437acd5b298149c6c963650a9e7cfe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 15 Dec 2025 15:16:39 +0100 Subject: [PATCH 065/171] efi: stmm: Make use of tee bus methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tee bus got dedicated callbacks for probe and remove. Make use of these. This fixes a runtime warning about the driver needing to be converted to the bus methods. Reviewed-by: Sumit Garg Signed-off-by: Uwe Kleine-König Reviewed-by: Ilias Apalodimas Signed-off-by: Jens Wiklander --- drivers/firmware/efi/stmm/tee_stmm_efi.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/efi/stmm/tee_stmm_efi.c b/drivers/firmware/efi/stmm/tee_stmm_efi.c index 5903811858b6..7b04dd649629 100644 --- a/drivers/firmware/efi/stmm/tee_stmm_efi.c +++ b/drivers/firmware/efi/stmm/tee_stmm_efi.c @@ -520,8 +520,9 @@ static void tee_stmm_restore_efivars_generic_ops(void) efivars_generic_ops_register(); } -static int tee_stmm_efi_probe(struct device *dev) +static int tee_stmm_efi_probe(struct tee_client_device *tee_dev) { + struct device *dev = &tee_dev->dev; struct tee_ioctl_open_session_arg sess_arg; efi_status_t ret; int rc; @@ -571,21 +572,19 @@ static int tee_stmm_efi_probe(struct device *dev) return 0; } -static int tee_stmm_efi_remove(struct device *dev) +static void tee_stmm_efi_remove(struct tee_client_device *dev) { tee_stmm_restore_efivars_generic_ops(); - - return 0; } MODULE_DEVICE_TABLE(tee, tee_stmm_efi_id_table); static struct tee_client_driver tee_stmm_efi_driver = { .id_table = tee_stmm_efi_id_table, + .probe = tee_stmm_efi_probe, + .remove = tee_stmm_efi_remove, .driver = { .name = "tee-stmm-efi", - .probe = tee_stmm_efi_probe, - .remove = tee_stmm_efi_remove, }, }; From fe700bc50a9c28ddac2121ddf35e1447d9cd72bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 15 Dec 2025 15:16:40 +0100 Subject: [PATCH 066/171] firmware: arm_scmi: optee: Make use of module_tee_client_driver() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce boilerplate by using the newly introduced module_tee_client_driver(). That takes care of assigning the driver's bus, so the explicit assigning in this driver can be dropped. Reviewed-by: Sumit Garg Reviewed-by: Sudeep Holla Signed-off-by: Uwe Kleine-König Signed-off-by: Jens Wiklander --- drivers/firmware/arm_scmi/transports/optee.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/firmware/arm_scmi/transports/optee.c b/drivers/firmware/arm_scmi/transports/optee.c index dc0f46340153..8fdb80d3fabd 100644 --- a/drivers/firmware/arm_scmi/transports/optee.c +++ b/drivers/firmware/arm_scmi/transports/optee.c @@ -612,23 +612,12 @@ static struct tee_client_driver scmi_optee_service_driver = { .id_table = scmi_optee_service_id, .driver = { .name = "scmi-optee", - .bus = &tee_bus_type, .probe = scmi_optee_service_probe, .remove = scmi_optee_service_remove, }, }; -static int __init scmi_transport_optee_init(void) -{ - return driver_register(&scmi_optee_service_driver.driver); -} -module_init(scmi_transport_optee_init); - -static void __exit scmi_transport_optee_exit(void) -{ - driver_unregister(&scmi_optee_service_driver.driver); -} -module_exit(scmi_transport_optee_exit); +module_tee_client_driver(scmi_optee_service_driver); MODULE_AUTHOR("Etienne Carriere "); MODULE_DESCRIPTION("SCMI OPTEE Transport driver"); From 191ef0c5b3e3b18b976ee6d1215cdcc258f48690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 15 Dec 2025 15:16:41 +0100 Subject: [PATCH 067/171] firmware: arm_scmi: Make use of tee bus methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tee bus got dedicated callbacks for probe and remove. Make use of these. This fixes a runtime warning about the driver needing to be converted to the bus methods. Note that the return value of .remove() was already ignored before, so there is no problem introduced by dropping the error returns. Reviewed-by: Sumit Garg Reviewed-by: Sudeep Holla Signed-off-by: Uwe Kleine-König Signed-off-by: Jens Wiklander --- drivers/firmware/arm_scmi/transports/optee.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/arm_scmi/transports/optee.c b/drivers/firmware/arm_scmi/transports/optee.c index 8fdb80d3fabd..07ae18d5279d 100644 --- a/drivers/firmware/arm_scmi/transports/optee.c +++ b/drivers/firmware/arm_scmi/transports/optee.c @@ -529,8 +529,9 @@ static const struct of_device_id scmi_of_match[] = { DEFINE_SCMI_TRANSPORT_DRIVER(scmi_optee, scmi_optee_driver, scmi_optee_desc, scmi_of_match, core); -static int scmi_optee_service_probe(struct device *dev) +static int scmi_optee_service_probe(struct tee_client_device *scmi_pta) { + struct device *dev = &scmi_pta->dev; struct scmi_optee_agent *agent; struct tee_context *tee_ctx; int ret; @@ -578,24 +579,22 @@ err: return ret; } -static int scmi_optee_service_remove(struct device *dev) +static void scmi_optee_service_remove(struct tee_client_device *scmi_pta) { struct scmi_optee_agent *agent = scmi_optee_private; if (!scmi_optee_private) - return -EINVAL; + return; platform_driver_unregister(&scmi_optee_driver); if (!list_empty(&scmi_optee_private->channel_list)) - return -EBUSY; + return; /* Ensure cleared reference is visible before resources are released */ smp_store_mb(scmi_optee_private, NULL); tee_client_close_context(agent->tee_ctx); - - return 0; } static const struct tee_client_device_id scmi_optee_service_id[] = { @@ -609,11 +608,11 @@ static const struct tee_client_device_id scmi_optee_service_id[] = { MODULE_DEVICE_TABLE(tee, scmi_optee_service_id); static struct tee_client_driver scmi_optee_service_driver = { - .id_table = scmi_optee_service_id, - .driver = { + .probe = scmi_optee_service_probe, + .remove = scmi_optee_service_remove, + .id_table = scmi_optee_service_id, + .driver = { .name = "scmi-optee", - .probe = scmi_optee_service_probe, - .remove = scmi_optee_service_remove, }, }; From ae9d338ba7cf2bcdc94d0dead4db36b568b36a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 15 Dec 2025 15:16:42 +0100 Subject: [PATCH 068/171] firmware: tee_bnxt: Make use of module_tee_client_driver() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce boilerplate by using the newly introduced module_tee_client_driver(). That takes care of assigning the driver's bus, so the explicit assigning in this driver can be dropped. Reviewed-by: Sumit Garg Signed-off-by: Uwe Kleine-König Signed-off-by: Jens Wiklander --- drivers/firmware/broadcom/tee_bnxt_fw.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/firmware/broadcom/tee_bnxt_fw.c b/drivers/firmware/broadcom/tee_bnxt_fw.c index 40e3183a3d11..fbdf1aa97c82 100644 --- a/drivers/firmware/broadcom/tee_bnxt_fw.c +++ b/drivers/firmware/broadcom/tee_bnxt_fw.c @@ -261,25 +261,13 @@ static struct tee_client_driver tee_bnxt_fw_driver = { .id_table = tee_bnxt_fw_id_table, .driver = { .name = KBUILD_MODNAME, - .bus = &tee_bus_type, .probe = tee_bnxt_fw_probe, .remove = tee_bnxt_fw_remove, .shutdown = tee_bnxt_fw_shutdown, }, }; -static int __init tee_bnxt_fw_mod_init(void) -{ - return driver_register(&tee_bnxt_fw_driver.driver); -} - -static void __exit tee_bnxt_fw_mod_exit(void) -{ - driver_unregister(&tee_bnxt_fw_driver.driver); -} - -module_init(tee_bnxt_fw_mod_init); -module_exit(tee_bnxt_fw_mod_exit); +module_tee_client_driver(tee_bnxt_fw_driver); MODULE_AUTHOR("Vikas Gupta "); MODULE_DESCRIPTION("Broadcom bnxt firmware manager"); From 2966fa040b4657547bf0cac4e6f02f14b1790e0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 15 Dec 2025 15:16:43 +0100 Subject: [PATCH 069/171] firmware: tee_bnxt: Make use of tee bus methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tee bus got dedicated callbacks for probe and remove. Make use of these. This fixes a runtime warning about the driver needing to be converted to the bus methods. Reviewed-by: Sumit Garg Signed-off-by: Uwe Kleine-König Signed-off-by: Jens Wiklander --- drivers/firmware/broadcom/tee_bnxt_fw.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/firmware/broadcom/tee_bnxt_fw.c b/drivers/firmware/broadcom/tee_bnxt_fw.c index fbdf1aa97c82..a706c84eb2b6 100644 --- a/drivers/firmware/broadcom/tee_bnxt_fw.c +++ b/drivers/firmware/broadcom/tee_bnxt_fw.c @@ -181,9 +181,9 @@ static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) return (ver->impl_id == TEE_IMPL_ID_OPTEE); } -static int tee_bnxt_fw_probe(struct device *dev) +static int tee_bnxt_fw_probe(struct tee_client_device *bnxt_device) { - struct tee_client_device *bnxt_device = to_tee_client_device(dev); + struct device *dev = &bnxt_device->dev; int ret, err = -ENODEV; struct tee_ioctl_open_session_arg sess_arg; struct tee_shm *fw_shm_pool; @@ -231,17 +231,15 @@ out_ctx: return err; } -static int tee_bnxt_fw_remove(struct device *dev) +static void tee_bnxt_fw_remove(struct tee_client_device *bnxt_device) { tee_shm_free(pvt_data.fw_shm_pool); tee_client_close_session(pvt_data.ctx, pvt_data.session_id); tee_client_close_context(pvt_data.ctx); pvt_data.ctx = NULL; - - return 0; } -static void tee_bnxt_fw_shutdown(struct device *dev) +static void tee_bnxt_fw_shutdown(struct tee_client_device *bnxt_device) { tee_shm_free(pvt_data.fw_shm_pool); tee_client_close_session(pvt_data.ctx, pvt_data.session_id); @@ -258,12 +256,12 @@ static const struct tee_client_device_id tee_bnxt_fw_id_table[] = { MODULE_DEVICE_TABLE(tee, tee_bnxt_fw_id_table); static struct tee_client_driver tee_bnxt_fw_driver = { + .probe = tee_bnxt_fw_probe, + .remove = tee_bnxt_fw_remove, + .shutdown = tee_bnxt_fw_shutdown, .id_table = tee_bnxt_fw_id_table, .driver = { .name = KBUILD_MODNAME, - .probe = tee_bnxt_fw_probe, - .remove = tee_bnxt_fw_remove, - .shutdown = tee_bnxt_fw_shutdown, }, }; From 7b7e532b58e89b3318242bfcb57f3f2b3b384855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 15 Dec 2025 15:16:44 +0100 Subject: [PATCH 070/171] KEYS: trusted: Migrate to use tee specific driver registration function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tee subsystem recently got a set of dedicated functions to register (and unregister) a tee driver. Make use of them. These care for setting the driver's bus (so the explicit assignment can be dropped) and the driver owner (which is an improvement this driver benefits from). Reviewed-by: Sumit Garg Signed-off-by: Uwe Kleine-König Reviewed-by: Jarkko Sakkinen Signed-off-by: Jens Wiklander --- security/keys/trusted-keys/trusted_tee.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/security/keys/trusted-keys/trusted_tee.c b/security/keys/trusted-keys/trusted_tee.c index aa3d477de6db..3cea9a377955 100644 --- a/security/keys/trusted-keys/trusted_tee.c +++ b/security/keys/trusted-keys/trusted_tee.c @@ -264,7 +264,6 @@ static struct tee_client_driver trusted_key_driver = { .id_table = trusted_key_id_table, .driver = { .name = DRIVER_NAME, - .bus = &tee_bus_type, .probe = trusted_key_probe, .remove = trusted_key_remove, }, @@ -272,12 +271,12 @@ static struct tee_client_driver trusted_key_driver = { static int trusted_tee_init(void) { - return driver_register(&trusted_key_driver.driver); + return tee_client_driver_register(&trusted_key_driver); } static void trusted_tee_exit(void) { - driver_unregister(&trusted_key_driver.driver); + tee_client_driver_unregister(&trusted_key_driver); } struct trusted_key_ops trusted_key_tee_ops = { From c6ef3e90575b727bf711c93a3e3168d88a6c9479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 15 Dec 2025 15:16:45 +0100 Subject: [PATCH 071/171] KEYS: trusted: Make use of tee bus methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tee bus got dedicated callbacks for probe and remove. Make use of these. This fixes a runtime warning about the driver needing to be converted to the bus methods. Reviewed-by: Sumit Garg Signed-off-by: Uwe Kleine-König Reviewed-by: Jarkko Sakkinen Signed-off-by: Jens Wiklander --- security/keys/trusted-keys/trusted_tee.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/security/keys/trusted-keys/trusted_tee.c b/security/keys/trusted-keys/trusted_tee.c index 3cea9a377955..6e465c8bef5e 100644 --- a/security/keys/trusted-keys/trusted_tee.c +++ b/security/keys/trusted-keys/trusted_tee.c @@ -202,9 +202,9 @@ static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) return 0; } -static int trusted_key_probe(struct device *dev) +static int trusted_key_probe(struct tee_client_device *rng_device) { - struct tee_client_device *rng_device = to_tee_client_device(dev); + struct device *dev = &rng_device->dev; int ret; struct tee_ioctl_open_session_arg sess_arg; @@ -244,13 +244,11 @@ out_ctx: return ret; } -static int trusted_key_remove(struct device *dev) +static void trusted_key_remove(struct tee_client_device *dev) { unregister_key_type(&key_type_trusted); tee_client_close_session(pvt_data.ctx, pvt_data.session_id); tee_client_close_context(pvt_data.ctx); - - return 0; } static const struct tee_client_device_id trusted_key_id_table[] = { @@ -261,11 +259,11 @@ static const struct tee_client_device_id trusted_key_id_table[] = { MODULE_DEVICE_TABLE(tee, trusted_key_id_table); static struct tee_client_driver trusted_key_driver = { + .probe = trusted_key_probe, + .remove = trusted_key_remove, .id_table = trusted_key_id_table, .driver = { .name = DRIVER_NAME, - .probe = trusted_key_probe, - .remove = trusted_key_remove, }, }; From 9e4c7808b728daa2f3bd7ee3fb1dec07ac522585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 15 Dec 2025 15:16:46 +0100 Subject: [PATCH 072/171] tpm/tpm_ftpm_tee: Make use of tee specific driver registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tee_client_driver_register() is typed more strongly and cares about assigning the driver's bus. Similar for tee_client_driver_unregister(). Make use of these functions. Reviewed-by: Sumit Garg Signed-off-by: Uwe Kleine-König Reviewed-by: Jarkko Sakkinen Signed-off-by: Jens Wiklander --- drivers/char/tpm/tpm_ftpm_tee.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c index 4e63c30aeaf1..e5fbc70b0eca 100644 --- a/drivers/char/tpm/tpm_ftpm_tee.c +++ b/drivers/char/tpm/tpm_ftpm_tee.c @@ -338,7 +338,6 @@ static struct tee_client_driver ftpm_tee_driver = { .id_table = optee_ftpm_id_table, .driver = { .name = "optee-ftpm", - .bus = &tee_bus_type, .probe = ftpm_tee_probe, .remove = ftpm_tee_remove, }, @@ -352,7 +351,7 @@ static int __init ftpm_mod_init(void) if (rc) return rc; - rc = driver_register(&ftpm_tee_driver.driver); + rc = tee_client_driver_register(&ftpm_tee_driver); if (rc) { platform_driver_unregister(&ftpm_tee_plat_driver); return rc; @@ -364,7 +363,7 @@ static int __init ftpm_mod_init(void) static void __exit ftpm_mod_exit(void) { platform_driver_unregister(&ftpm_tee_plat_driver); - driver_unregister(&ftpm_tee_driver.driver); + tee_client_driver_unregister(&ftpm_tee_driver); } module_init(ftpm_mod_init); From 92fad96aea24fc19abe1eae2249402b61de3a3e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 15 Dec 2025 15:16:47 +0100 Subject: [PATCH 073/171] tpm/tpm_ftpm_tee: Make use of tee bus methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tee bus got dedicated callbacks for probe and remove. Make use of these. This fixes a runtime warning about the driver needing to be converted to the bus methods. Reviewed-by: Sumit Garg Reviewed-by: Jarkko Sakkinen Signed-off-by: Uwe Kleine-König Signed-off-by: Jens Wiklander --- drivers/char/tpm/tpm_ftpm_tee.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c index e5fbc70b0eca..20294d1953a3 100644 --- a/drivers/char/tpm/tpm_ftpm_tee.c +++ b/drivers/char/tpm/tpm_ftpm_tee.c @@ -169,7 +169,7 @@ static int ftpm_tee_match(struct tee_ioctl_version_data *ver, const void *data) * Return: * On success, 0. On failure, -errno. */ -static int ftpm_tee_probe(struct device *dev) +static int ftpm_tee_probe_generic(struct device *dev) { int rc; struct tpm_chip *chip; @@ -251,11 +251,18 @@ out_tee_session: return rc; } +static int ftpm_tee_probe(struct tee_client_device *tcdev) +{ + struct device *dev = &tcdev->dev; + + return ftpm_tee_probe_generic(dev); +} + static int ftpm_plat_tee_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - return ftpm_tee_probe(dev); + return ftpm_tee_probe_generic(dev); } /** @@ -265,7 +272,7 @@ static int ftpm_plat_tee_probe(struct platform_device *pdev) * Return: * 0 always. */ -static int ftpm_tee_remove(struct device *dev) +static void ftpm_tee_remove_generic(struct device *dev) { struct ftpm_tee_private *pvt_data = dev_get_drvdata(dev); @@ -285,15 +292,20 @@ static int ftpm_tee_remove(struct device *dev) tee_client_close_context(pvt_data->ctx); /* memory allocated with devm_kzalloc() is freed automatically */ +} - return 0; +static void ftpm_tee_remove(struct tee_client_device *tcdev) +{ + struct device *dev = &tcdev->dev; + + ftpm_tee_remove_generic(dev); } static void ftpm_plat_tee_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - ftpm_tee_remove(dev); + ftpm_tee_remove_generic(dev); } /** @@ -335,11 +347,11 @@ static const struct tee_client_device_id optee_ftpm_id_table[] = { MODULE_DEVICE_TABLE(tee, optee_ftpm_id_table); static struct tee_client_driver ftpm_tee_driver = { + .probe = ftpm_tee_probe, + .remove = ftpm_tee_remove, .id_table = optee_ftpm_id_table, .driver = { .name = "optee-ftpm", - .probe = ftpm_tee_probe, - .remove = ftpm_tee_remove, }, }; From 0539c5a6fdef1b274112638aa5aa722b1df5e711 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 27 Oct 2025 14:02:14 +0100 Subject: [PATCH 074/171] soc: qcom: pmic_glink_altmode: Consume TBT3/USB4 mode notifications Some compute SoCs support additional operation modes, extending the existing set of USB3/safe/DP-alt-mode. The firmware performs all the necessary handshakes for us and there is no room for error on that level (i.e. the sequences will match what happens on Windows). The trade-off with that approach is that the received notifications trim some of the PDO/EUDO data (particularly the cable/plug parts), offering a set of similar-in-nature-but-not-the-same indicators. In an attempt to remedy this, I reconstructed some of it, so that the connected mux/retimer drivers can continue to behave as expected. Add support for parsing the aforementioned data coming from PMIC_GLINK and passing it on to the various Type-C components. Reviewed-by: Jack Pham Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20251027-topic-pg_altmode_usb4-v1-1-2931a3ecc146@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/pmic_glink_altmode.c | 188 ++++++++++++++++++++++---- 1 file changed, 165 insertions(+), 23 deletions(-) diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c index 7f11acd33323..d0afdcb96ee1 100644 --- a/drivers/soc/qcom/pmic_glink_altmode.c +++ b/drivers/soc/qcom/pmic_glink_altmode.c @@ -14,10 +14,12 @@ #include #include +#include #include #include #include #include +#include #include @@ -37,11 +39,38 @@ struct usbc_write_req { __le32 reserved; }; -#define NOTIFY_PAYLOAD_SIZE 16 +struct usbc_sc8280x_dp_data { + u8 pin_assignment : 6; + u8 hpd_state : 1; + u8 hpd_irq : 1; + u8 res[7]; +}; + +/* Used for both TBT and USB4 notifications */ +struct usbc_sc8280x_tbt_data { + u8 usb_speed : 3; + u8 cable_type : 3; + /* This field is NOP on USB4, all cables support rounded rates by spec */ + u8 rounded_cable : 1; + u8 power_limited : 1; + u8 res[11]; +}; + struct usbc_notify { struct pmic_glink_hdr hdr; - char payload[NOTIFY_PAYLOAD_SIZE]; - u32 reserved; + u8 port_idx; + u8 orientation; + u8 mux_ctrl; +#define MUX_CTRL_STATE_NO_CONN 0 +#define MUX_CTRL_STATE_TUNNELING 4 + + u8 res; + __le16 vid; + __le16 svid; + union usbc_sc8280x_extended_data { + struct usbc_sc8280x_dp_data dp; + struct usbc_sc8280x_tbt_data tbt; + } extended_data; }; struct usbc_sc8180x_notify { @@ -74,6 +103,7 @@ struct pmic_glink_altmode_port { struct typec_retimer *typec_retimer; struct typec_retimer_state retimer_state; struct typec_altmode dp_alt; + struct typec_altmode tbt_alt; struct work_struct work; @@ -81,10 +111,12 @@ struct pmic_glink_altmode_port { enum typec_orientation orientation; u16 svid; + struct usbc_sc8280x_tbt_data tbt_data; u8 dp_data; u8 mode; u8 hpd_state; u8 hpd_irq; + u8 mux_ctrl; }; #define work_to_altmode(w) container_of((w), struct pmic_glink_altmode, enable_work) @@ -170,6 +202,102 @@ static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode, dev_err(altmode->dev, "failed to setup retimer to DP: %d\n", ret); } +static void pmic_glink_altmode_enable_tbt(struct pmic_glink_altmode *altmode, + struct pmic_glink_altmode_port *port) +{ + struct usbc_sc8280x_tbt_data *tbt = &port->tbt_data; + struct typec_thunderbolt_data tbt_data = {}; + u32 cable_speed; + int ret; + + /* Device Discover Mode VDO */ + tbt_data.device_mode = TBT_MODE; + tbt_data.device_mode |= TBT_SET_ADAPTER(TBT_ADAPTER_TBT3); + + /* Cable Discover Mode VDO */ + tbt_data.cable_mode = TBT_MODE; + + if (tbt->usb_speed == 0) { + cable_speed = TBT_CABLE_USB3_PASSIVE; + } else if (tbt->usb_speed == 1) { + cable_speed = TBT_CABLE_10_AND_20GBPS; + } else { + dev_err(altmode->dev, + "Got illegal TBT3 cable speed value (%u), falling back to passive\n", + tbt->usb_speed); + cable_speed = TBT_CABLE_USB3_PASSIVE; + } + tbt_data.cable_mode |= TBT_SET_CABLE_SPEED(cable_speed); + + if (tbt->cable_type) { + tbt_data.cable_mode |= TBT_CABLE_ACTIVE_PASSIVE; + tbt_data.cable_mode |= TBT_SET_CABLE_ROUNDED(tbt->rounded_cable); + } + + /* Enter Mode VDO */ + tbt_data.enter_vdo |= TBT_MODE; + tbt_data.enter_vdo |= TBT_ENTER_MODE_CABLE_SPEED(cable_speed); + + if (tbt->cable_type) { + tbt_data.enter_vdo |= TBT_CABLE_ACTIVE_PASSIVE; + tbt_data.enter_vdo |= TBT_SET_CABLE_ROUNDED(tbt->rounded_cable); + } + + port->state.alt = &port->tbt_alt; + port->state.data = &tbt_data; + port->state.mode = TYPEC_MODAL_STATE(port->mode); + + ret = typec_mux_set(port->typec_mux, &port->state); + if (ret) + dev_err(altmode->dev, "failed to switch mux to USB: %d\n", ret); + + port->retimer_state.alt = &port->tbt_alt; + port->retimer_state.data = &tbt_data; + port->retimer_state.mode = TYPEC_MODAL_STATE(port->mode); + + ret = typec_retimer_set(port->typec_retimer, &port->retimer_state); + if (ret) + dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret); +} + +static void pmic_glink_altmode_enable_usb4(struct pmic_glink_altmode *altmode, + struct pmic_glink_altmode_port *port) +{ + struct usbc_sc8280x_tbt_data *tbt = &port->tbt_data; + struct enter_usb_data data = {}; + int ret; + + data.eudo = FIELD_PREP(EUDO_USB_MODE_MASK, EUDO_USB_MODE_USB4); + + if (tbt->usb_speed == 0) { + data.eudo |= FIELD_PREP(EUDO_CABLE_SPEED_MASK, EUDO_CABLE_SPEED_USB4_GEN2); + } else if (tbt->usb_speed == 1) { + data.eudo |= FIELD_PREP(EUDO_CABLE_SPEED_MASK, EUDO_CABLE_SPEED_USB4_GEN3); + } else { + pr_err("Got illegal USB4 cable speed value (%u), falling back to G2\n", + tbt->usb_speed); + data.eudo |= FIELD_PREP(EUDO_CABLE_SPEED_MASK, EUDO_CABLE_SPEED_USB4_GEN2); + } + + data.eudo |= FIELD_PREP(EUDO_CABLE_TYPE_MASK, tbt->cable_type); + + port->state.alt = NULL; + port->state.data = &data; + port->state.mode = TYPEC_MODE_USB4; + + ret = typec_mux_set(port->typec_mux, &port->state); + if (ret) + dev_err(altmode->dev, "failed to switch mux to USB: %d\n", ret); + + port->retimer_state.alt = NULL; + port->retimer_state.data = &data; + port->retimer_state.mode = TYPEC_MODE_USB4; + + ret = typec_retimer_set(port->typec_retimer, &port->retimer_state); + if (ret) + dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret); +} + static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode, struct pmic_glink_altmode_port *port) { @@ -222,15 +350,15 @@ static void pmic_glink_altmode_worker(struct work_struct *work) typec_switch_set(alt_port->typec_switch, alt_port->orientation); - if (alt_port->svid == USB_TYPEC_DP_SID) { - if (alt_port->mode == 0xff) { - pmic_glink_altmode_safe(altmode, alt_port); - } else { - pmic_glink_altmode_enable_dp(altmode, alt_port, - alt_port->mode, - alt_port->hpd_state, - alt_port->hpd_irq); - } + if (alt_port->mux_ctrl == MUX_CTRL_STATE_NO_CONN) { + pmic_glink_altmode_safe(altmode, alt_port); + } else if (alt_port->svid == USB_TYPEC_TBT_SID) { + pmic_glink_altmode_enable_tbt(altmode, alt_port); + } else if (alt_port->svid == USB_TYPEC_DP_SID) { + pmic_glink_altmode_enable_dp(altmode, alt_port, + alt_port->mode, + alt_port->hpd_state, + alt_port->hpd_irq); if (alt_port->hpd_state) conn_status = connector_status_connected; @@ -238,6 +366,8 @@ static void pmic_glink_altmode_worker(struct work_struct *work) conn_status = connector_status_disconnected; drm_aux_hpd_bridge_notify(&alt_port->bridge->dev, conn_status); + } else if (alt_port->mux_ctrl == MUX_CTRL_STATE_TUNNELING) { + pmic_glink_altmode_enable_usb4(altmode, alt_port); } else { pmic_glink_altmode_enable_usb(altmode, alt_port); } @@ -314,11 +444,10 @@ static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmod u16 svid, const void *data, size_t len) { struct pmic_glink_altmode_port *alt_port; + const struct usbc_sc8280x_tbt_data *tbt; + const struct usbc_sc8280x_dp_data *dp; const struct usbc_notify *notify; u8 orientation; - u8 hpd_state; - u8 hpd_irq; - u8 mode; u8 port; if (len != sizeof(*notify)) { @@ -329,11 +458,8 @@ static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmod notify = data; - port = notify->payload[0]; - orientation = notify->payload[1]; - mode = FIELD_GET(SC8280XP_DPAM_MASK, notify->payload[8]) - DPAM_HPD_A; - hpd_state = FIELD_GET(SC8280XP_HPD_STATE_MASK, notify->payload[8]); - hpd_irq = FIELD_GET(SC8280XP_HPD_IRQ_MASK, notify->payload[8]); + port = notify->port_idx; + orientation = notify->orientation; if (port >= ARRAY_SIZE(altmode->ports) || !altmode->ports[port].altmode) { dev_dbg(altmode->dev, "notification on undefined port %d\n", port); @@ -343,9 +469,21 @@ static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmod alt_port = &altmode->ports[port]; alt_port->orientation = pmic_glink_altmode_orientation(orientation); alt_port->svid = svid; - alt_port->mode = mode; - alt_port->hpd_state = hpd_state; - alt_port->hpd_irq = hpd_irq; + alt_port->mux_ctrl = notify->mux_ctrl; + + if (svid == USB_TYPEC_DP_SID) { + dp = ¬ify->extended_data.dp; + + alt_port->mode = dp->pin_assignment - DPAM_HPD_A; + alt_port->hpd_state = dp->hpd_state; + alt_port->hpd_irq = dp->hpd_irq; + } else if (alt_port->mux_ctrl == MUX_CTRL_STATE_TUNNELING) { + /* Valid for both USB4 and TBT3 */ + tbt = ¬ify->extended_data.tbt; + + alt_port->tbt_data = *tbt; + } + schedule_work(&alt_port->work); } @@ -471,6 +609,10 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev, alt_port->dp_alt.mode = USB_TYPEC_DP_MODE; alt_port->dp_alt.active = 1; + alt_port->tbt_alt.svid = USB_TYPEC_TBT_SID; + alt_port->tbt_alt.mode = TYPEC_TBT_MODE; + alt_port->tbt_alt.active = 1; + alt_port->typec_mux = fwnode_typec_mux_get(fwnode); if (IS_ERR(alt_port->typec_mux)) { fwnode_handle_put(fwnode); From 0da7824734d8d83e6a844dd0207f071cb0c50cf4 Mon Sep 17 00:00:00 2001 From: Haotian Zhang Date: Tue, 16 Dec 2025 09:39:32 +0800 Subject: [PATCH 075/171] soc: qcom: cmd-db: Use devm_memremap() to fix memory leak in cmd_db_dev_probe If cmd_db_magic_matches() fails after memremap() succeeds, the function returns -EINVAL without unmapping the memory region, causing a potential resource leak. Switch to devm_memremap to automatically manage the map resource. Fixes: 312416d9171a ("drivers: qcom: add command DB driver") Suggested-by: Dmitry Baryshkov Signed-off-by: Haotian Zhang Link: https://lore.kernel.org/r/20251216013933.773-1-vulab@iscas.ac.cn Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/cmd-db.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index ae66c2623d25..84a75d8c4b70 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -349,15 +349,16 @@ static int cmd_db_dev_probe(struct platform_device *pdev) return -EINVAL; } - cmd_db_header = memremap(rmem->base, rmem->size, MEMREMAP_WC); - if (!cmd_db_header) { - ret = -ENOMEM; + cmd_db_header = devm_memremap(&pdev->dev, rmem->base, rmem->size, MEMREMAP_WC); + if (IS_ERR(cmd_db_header)) { + ret = PTR_ERR(cmd_db_header); cmd_db_header = NULL; return ret; } if (!cmd_db_magic_matches(cmd_db_header)) { dev_err(&pdev->dev, "Invalid Command DB Magic\n"); + cmd_db_header = NULL; return -EINVAL; } From 6259094ee806fb813ca95894c65fb80e2ec98bf1 Mon Sep 17 00:00:00 2001 From: Zilin Guan Date: Sun, 28 Dec 2025 16:26:36 +0000 Subject: [PATCH 076/171] soc: mediatek: svs: Fix memory leak in svs_enable_debug_write() In svs_enable_debug_write(), the buf allocated by memdup_user_nul() is leaked if kstrtoint() fails. Fix this by using __free(kfree) to automatically free buf, eliminating the need for explicit kfree() calls and preventing leaks. Fixes: 13f1bbcfb582 ("soc: mediatek: SVS: add debug commands") Co-developed-by: Jianhao Xu Signed-off-by: Jianhao Xu Signed-off-by: Zilin Guan [Angelo: Added missing cleanup.h inclusion] Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-svs.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/soc/mediatek/mtk-svs.c b/drivers/soc/mediatek/mtk-svs.c index f45537546553..99edecb204f2 100644 --- a/drivers/soc/mediatek/mtk-svs.c +++ b/drivers/soc/mediatek/mtk-svs.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -789,7 +790,7 @@ static ssize_t svs_enable_debug_write(struct file *filp, struct svs_bank *svsb = file_inode(filp)->i_private; struct svs_platform *svsp = dev_get_drvdata(svsb->dev); int enabled, ret; - char *buf = NULL; + char *buf __free(kfree) = NULL; if (count >= PAGE_SIZE) return -EINVAL; @@ -807,8 +808,6 @@ static ssize_t svs_enable_debug_write(struct file *filp, svsb->mode_support = SVSB_MODE_ALL_DISABLE; } - kfree(buf); - return count; } From db6dcaeeb60f97ce9765d50035be23c0901d7348 Mon Sep 17 00:00:00 2001 From: Louis-Alexis Eyraud Date: Tue, 9 Dec 2025 10:33:20 +0100 Subject: [PATCH 077/171] soc: mediatek: mtk-socinfo: Add entry for MT8371AV/AZA Genio 520 Add an entry for the MT8371 SoC with commercial name Genio 520. Signed-off-by: Louis-Alexis Eyraud Reviewed-by: Macpaul Lin Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-socinfo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/mediatek/mtk-socinfo.c b/drivers/soc/mediatek/mtk-socinfo.c index 978c43e9115a..424a1eb82c20 100644 --- a/drivers/soc/mediatek/mtk-socinfo.c +++ b/drivers/soc/mediatek/mtk-socinfo.c @@ -59,6 +59,7 @@ static struct socinfo_data socinfo_data_table[] = { MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EZA", "Kompanio 1380", 0x81950400, CELL_NOT_USED), MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EHZA", "Kompanio 1380", 0x81950404, CELL_NOT_USED), MTK_SOCINFO_ENTRY("MT8370", "MT8370AV/AZA", "Genio 510", 0x83700000, 0x00000081), + MTK_SOCINFO_ENTRY("MT8371", "MT8371AV/AZA", "Genio 520", 0x83710000, 0x00000081), MTK_SOCINFO_ENTRY("MT8390", "MT8390AV/AZA", "Genio 700", 0x83900000, 0x00000080), MTK_SOCINFO_ENTRY("MT8391", "MT8391AV/AZA", "Genio 720", 0x83910000, 0x00000080), MTK_SOCINFO_ENTRY("MT8395", "MT8395AV/ZA", "Genio 1200", 0x83950100, CELL_NOT_USED), From 831ee17036e259da23a6313e28a3cbdda221a88c Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Mon, 24 Nov 2025 12:06:51 +0100 Subject: [PATCH 078/171] dt-bindings: soc: mediatek: dvfsrc: Document clock The DVFSRC hardware has a clock on all platforms. Instead or proliferating the culture of omitting clock descriptions in the clock controller drivers or marking them critical instead of declaring these types of relationships, add this one to the binding. Any device that wishes to use this binding should figure out their incomplete or incorrect clock situation first before piling more features on top. Acked-by: Rob Herring (Arm) Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Nicolas Frattaroli Signed-off-by: AngeloGioacchino Del Regno --- .../bindings/soc/mediatek/mediatek,mt8183-dvfsrc.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/soc/mediatek/mediatek,mt8183-dvfsrc.yaml b/Documentation/devicetree/bindings/soc/mediatek/mediatek,mt8183-dvfsrc.yaml index 4c96d4917967..27cce748e0ca 100644 --- a/Documentation/devicetree/bindings/soc/mediatek/mediatek,mt8183-dvfsrc.yaml +++ b/Documentation/devicetree/bindings/soc/mediatek/mediatek,mt8183-dvfsrc.yaml @@ -34,6 +34,10 @@ properties: maxItems: 1 description: DVFSRC common register address and length. + clocks: + items: + - description: Clock that drives the DVFSRC MCU + regulators: type: object $ref: /schemas/regulator/mediatek,mt6873-dvfsrc-regulator.yaml# @@ -50,6 +54,7 @@ additionalProperties: false examples: - | + #include soc { #address-cells = <2>; #size-cells = <2>; @@ -57,6 +62,7 @@ examples: system-controller@10012000 { compatible = "mediatek,mt8195-dvfsrc"; reg = <0 0x10012000 0 0x1000>; + clocks = <&topckgen CLK_TOP_DVFSRC>; regulators { compatible = "mediatek,mt8195-dvfsrc-regulator"; From 23f1b4922a9135515e37d3bbad766e311845071f Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 24 Nov 2025 12:06:53 +0100 Subject: [PATCH 079/171] soc: mediatek: mtk-dvfsrc: Change error check for DVFSRCv4 START cmd In preparation for adding support for DVFSRC Version 4, change the error check for the MTK_SIP_DVFSRC_START command in the probe function to error out only if BIT(0) is set: this is still valid for the previous DVFSRC versions, as those always set this bit in a fail reply anyway. Signed-off-by: Nicolas Frattaroli Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-dvfsrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/mediatek/mtk-dvfsrc.c b/drivers/soc/mediatek/mtk-dvfsrc.c index 41add5636b03..7708b07ab2d6 100644 --- a/drivers/soc/mediatek/mtk-dvfsrc.c +++ b/drivers/soc/mediatek/mtk-dvfsrc.c @@ -440,7 +440,7 @@ static int mtk_dvfsrc_probe(struct platform_device *pdev) /* Everything is set up - make it run! */ arm_smccc_smc(MTK_SIP_DVFSRC_VCOREFS_CONTROL, MTK_SIP_DVFSRC_START, 0, 0, 0, 0, 0, 0, &ares); - if (ares.a0) + if (ares.a0 & BIT(0)) return dev_err_probe(&pdev->dev, -EINVAL, "Cannot start DVFSRC: %lu\n", ares.a0); return 0; From c2488fecba681d632a3dbb6b2f33c39df2cb7be9 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 24 Nov 2025 12:06:54 +0100 Subject: [PATCH 080/171] soc: mediatek: mtk-dvfsrc: Add and propagate DVFSRC bandwidth type In preparation for adding support for DVFSRC Version 4, add a new mtk_dvfsrc_bw_type enumeration, and propagate it from specific bw setting callbacks to __dvfsrc_set_dram_bw_v1(), which will use it to choose calculation multipliers and dividers in v4 callbacks. Signed-off-by: Nicolas Frattaroli Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-dvfsrc.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/soc/mediatek/mtk-dvfsrc.c b/drivers/soc/mediatek/mtk-dvfsrc.c index 7708b07ab2d6..a684e405daf7 100644 --- a/drivers/soc/mediatek/mtk-dvfsrc.c +++ b/drivers/soc/mediatek/mtk-dvfsrc.c @@ -36,6 +36,13 @@ #define MTK_SIP_DVFSRC_INIT 0x0 #define MTK_SIP_DVFSRC_START 0x1 +enum mtk_dvfsrc_bw_type { + DVFSRC_BW_AVG, + DVFSRC_BW_PEAK, + DVFSRC_BW_HRT, + DVFSRC_BW_MAX, +}; + struct dvfsrc_bw_constraints { u16 max_dram_nom_bw; u16 max_dram_peak_bw; @@ -268,7 +275,7 @@ static void dvfsrc_set_vscp_level_v2(struct mtk_dvfsrc *dvfsrc, u32 level) } static void __dvfsrc_set_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, u32 reg, - u16 max_bw, u16 min_bw, u64 bw) + int type, u16 max_bw, u16 min_bw, u64 bw) { u32 new_bw = (u32)div_u64(bw, 100 * 1000); @@ -285,21 +292,21 @@ static void dvfsrc_set_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, u64 bw) { u64 max_bw = dvfsrc->dvd->bw_constraints->max_dram_nom_bw; - __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_BW, max_bw, 0, bw); + __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_BW, DVFSRC_BW_AVG, max_bw, 0, bw); }; static void dvfsrc_set_dram_peak_bw_v1(struct mtk_dvfsrc *dvfsrc, u64 bw) { u64 max_bw = dvfsrc->dvd->bw_constraints->max_dram_peak_bw; - __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_PEAK_BW, max_bw, 0, bw); + __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_PEAK_BW, DVFSRC_BW_PEAK, max_bw, 0, bw); } static void dvfsrc_set_dram_hrt_bw_v1(struct mtk_dvfsrc *dvfsrc, u64 bw) { u64 max_bw = dvfsrc->dvd->bw_constraints->max_dram_hrt_bw; - __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_HRT_BW, max_bw, 0, bw); + __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_HRT_BW, DVFSRC_BW_HRT, max_bw, 0, bw); } static void dvfsrc_set_opp_level_v1(struct mtk_dvfsrc *dvfsrc, u32 level) From ddb5862a43b1f40ca0a5cc16882277d8d07b966a Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 24 Nov 2025 12:06:55 +0100 Subject: [PATCH 081/171] soc: mediatek: mtk-dvfsrc: Add a new callback for calc_dram_bw In preparation for adding support for DVFSRC Version 4, add a new callback for calculating the dram bandwidth, assign the current calculation algo to all of the currently supported SoCs, and use this in __dvfsrc_set_dram_bw_v1(). Signed-off-by: Nicolas Frattaroli Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-dvfsrc.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/soc/mediatek/mtk-dvfsrc.c b/drivers/soc/mediatek/mtk-dvfsrc.c index a684e405daf7..3cbccbb7469a 100644 --- a/drivers/soc/mediatek/mtk-dvfsrc.c +++ b/drivers/soc/mediatek/mtk-dvfsrc.c @@ -73,6 +73,7 @@ struct mtk_dvfsrc { struct dvfsrc_soc_data { const int *regs; const struct dvfsrc_opp_desc *opps_desc; + u32 (*calc_dram_bw)(struct mtk_dvfsrc *dvfsrc, int type, u64 bw); u32 (*get_target_level)(struct mtk_dvfsrc *dvfsrc); u32 (*get_current_level)(struct mtk_dvfsrc *dvfsrc); u32 (*get_vcore_level)(struct mtk_dvfsrc *dvfsrc); @@ -274,10 +275,15 @@ static void dvfsrc_set_vscp_level_v2(struct mtk_dvfsrc *dvfsrc, u32 level) dvfsrc_writel(dvfsrc, DVFSRC_VCORE, val); } +static u32 dvfsrc_calc_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, int type, u64 bw) +{ + return (u32)div_u64(bw, 100 * 1000); +} + static void __dvfsrc_set_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, u32 reg, int type, u16 max_bw, u16 min_bw, u64 bw) { - u32 new_bw = (u32)div_u64(bw, 100 * 1000); + u32 new_bw = dvfsrc->dvd->calc_dram_bw(dvfsrc, type, bw); /* If bw constraints (in mbps) are defined make sure to respect them */ if (max_bw) @@ -519,6 +525,7 @@ static const struct dvfsrc_opp_desc dvfsrc_opp_mt8183_desc[] = { static const struct dvfsrc_soc_data mt8183_data = { .opps_desc = dvfsrc_opp_mt8183_desc, .regs = dvfsrc_mt8183_regs, + .calc_dram_bw = dvfsrc_calc_dram_bw_v1, .get_target_level = dvfsrc_get_target_level_v1, .get_current_level = dvfsrc_get_current_level_v1, .get_vcore_level = dvfsrc_get_vcore_level_v1, @@ -549,6 +556,7 @@ static const struct dvfsrc_opp_desc dvfsrc_opp_mt8195_desc[] = { static const struct dvfsrc_soc_data mt8195_data = { .opps_desc = dvfsrc_opp_mt8195_desc, .regs = dvfsrc_mt8195_regs, + .calc_dram_bw = dvfsrc_calc_dram_bw_v1, .get_target_level = dvfsrc_get_target_level_v2, .get_current_level = dvfsrc_get_current_level_v2, .get_vcore_level = dvfsrc_get_vcore_level_v2, From 7cf9db2aca552f5f537d46f1e52e0ab08ddc2d64 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 24 Nov 2025 12:06:56 +0100 Subject: [PATCH 082/171] soc: mediatek: mtk-dvfsrc: Write bandwidth to EMI DDR if present In preparation for adding support for DVFSRC Version 4, add a new `has_emi_ddr` member to struct dvfsrc_soc_data: if true, write the DRAM bandwidth both to the BW_AVG and to the newly defined EMI_BW register, present only on DVFSRC v4. Currently supported SoCs will not use this, as has_emi_ddr is left out from their platform data, hence reading false. Signed-off-by: Nicolas Frattaroli Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-dvfsrc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/soc/mediatek/mtk-dvfsrc.c b/drivers/soc/mediatek/mtk-dvfsrc.c index 3cbccbb7469a..bf0e7b01d255 100644 --- a/drivers/soc/mediatek/mtk-dvfsrc.c +++ b/drivers/soc/mediatek/mtk-dvfsrc.c @@ -72,6 +72,7 @@ struct mtk_dvfsrc { struct dvfsrc_soc_data { const int *regs; + const bool has_emi_ddr; const struct dvfsrc_opp_desc *opps_desc; u32 (*calc_dram_bw)(struct mtk_dvfsrc *dvfsrc, int type, u64 bw); u32 (*get_target_level)(struct mtk_dvfsrc *dvfsrc); @@ -107,6 +108,7 @@ enum dvfsrc_regs { DVFSRC_SW_BW, DVFSRC_SW_PEAK_BW, DVFSRC_SW_HRT_BW, + DVFSRC_SW_EMI_BW, DVFSRC_VCORE, DVFSRC_REGS_MAX, }; @@ -292,6 +294,9 @@ static void __dvfsrc_set_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, u32 reg, new_bw = max(new_bw, min_bw); dvfsrc_writel(dvfsrc, reg, new_bw); + + if (type == DVFSRC_BW_AVG && dvfsrc->dvd->has_emi_ddr) + dvfsrc_writel(dvfsrc, DVFSRC_SW_EMI_BW, bw); } static void dvfsrc_set_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, u64 bw) From 75cf308fee7e4b3038741f96fd90afc3bd871e64 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 24 Nov 2025 12:06:57 +0100 Subject: [PATCH 083/171] soc: mediatek: mtk-dvfsrc: Add support for DVFSRCv4 and MT8196 Add support for the DVFSRC Version 4 by adding new functions for vcore/dram levels (in v4, called gears instead), and for readout of pre-programmed dvfsrc_opp entries, corresponding to each gear. In the probe function, for v4, the curr_opps is initialized from the get_hw_opps() function instead of platform data. In order to make use of the new DVFSRCv4 code, also add support for the MediaTek MT8196 SoC. Co-developed-by: Nicolas Frattaroli Signed-off-by: Nicolas Frattaroli Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-dvfsrc.c | 248 +++++++++++++++++++++++++++++- 1 file changed, 247 insertions(+), 1 deletion(-) diff --git a/drivers/soc/mediatek/mtk-dvfsrc.c b/drivers/soc/mediatek/mtk-dvfsrc.c index bf0e7b01d255..3a83fd4baf54 100644 --- a/drivers/soc/mediatek/mtk-dvfsrc.c +++ b/drivers/soc/mediatek/mtk-dvfsrc.c @@ -15,11 +15,17 @@ #include #include +/* DVFSRC_BASIC_CONTROL */ +#define DVFSRC_V4_BASIC_CTRL_OPP_COUNT GENMASK(26, 20) + /* DVFSRC_LEVEL */ #define DVFSRC_V1_LEVEL_TARGET_LEVEL GENMASK(15, 0) #define DVFSRC_TGT_LEVEL_IDLE 0x00 #define DVFSRC_V1_LEVEL_CURRENT_LEVEL GENMASK(31, 16) +#define DVFSRC_V4_LEVEL_TARGET_LEVEL GENMASK(15, 8) +#define DVFSRC_V4_LEVEL_TARGET_PRESENT BIT(16) + /* DVFSRC_SW_REQ, DVFSRC_SW_REQ2 */ #define DVFSRC_V1_SW_REQ2_DRAM_LEVEL GENMASK(1, 0) #define DVFSRC_V1_SW_REQ2_VCORE_LEVEL GENMASK(3, 2) @@ -27,9 +33,23 @@ #define DVFSRC_V2_SW_REQ_DRAM_LEVEL GENMASK(3, 0) #define DVFSRC_V2_SW_REQ_VCORE_LEVEL GENMASK(6, 4) +#define DVFSRC_V4_SW_REQ_EMI_LEVEL GENMASK(3, 0) +#define DVFSRC_V4_SW_REQ_DRAM_LEVEL GENMASK(15, 12) + /* DVFSRC_VCORE */ #define DVFSRC_V2_VCORE_REQ_VSCP_LEVEL GENMASK(14, 12) +/* DVFSRC_TARGET_GEAR */ +#define DVFSRC_V4_GEAR_TARGET_DRAM GENMASK(7, 0) +#define DVFSRC_V4_GEAR_TARGET_VCORE GENMASK(15, 8) + +/* DVFSRC_GEAR_INFO */ +#define DVFSRC_V4_GEAR_INFO_REG_WIDTH 0x4 +#define DVFSRC_V4_GEAR_INFO_REG_LEVELS 64 +#define DVFSRC_V4_GEAR_INFO_VCORE GENMASK(3, 0) +#define DVFSRC_V4_GEAR_INFO_EMI GENMASK(7, 4) +#define DVFSRC_V4_GEAR_INFO_DRAM GENMASK(15, 12) + #define DVFSRC_POLL_TIMEOUT_US 1000 #define STARTUP_TIME_US 1 @@ -52,6 +72,7 @@ struct dvfsrc_bw_constraints { struct dvfsrc_opp { u32 vcore_opp; u32 dram_opp; + u32 emi_opp; }; struct dvfsrc_opp_desc { @@ -72,6 +93,7 @@ struct mtk_dvfsrc { struct dvfsrc_soc_data { const int *regs; + const u8 *bw_units; const bool has_emi_ddr; const struct dvfsrc_opp_desc *opps_desc; u32 (*calc_dram_bw)(struct mtk_dvfsrc *dvfsrc, int type, u64 bw); @@ -79,6 +101,8 @@ struct dvfsrc_soc_data { u32 (*get_current_level)(struct mtk_dvfsrc *dvfsrc); u32 (*get_vcore_level)(struct mtk_dvfsrc *dvfsrc); u32 (*get_vscp_level)(struct mtk_dvfsrc *dvfsrc); + u32 (*get_opp_count)(struct mtk_dvfsrc *dvfsrc); + int (*get_hw_opps)(struct mtk_dvfsrc *dvfsrc); void (*set_dram_bw)(struct mtk_dvfsrc *dvfsrc, u64 bw); void (*set_dram_peak_bw)(struct mtk_dvfsrc *dvfsrc, u64 bw); void (*set_dram_hrt_bw)(struct mtk_dvfsrc *dvfsrc, u64 bw); @@ -101,6 +125,7 @@ static void dvfsrc_writel(struct mtk_dvfsrc *dvfs, u32 offset, u32 val) } enum dvfsrc_regs { + DVFSRC_BASIC_CONTROL, DVFSRC_SW_REQ, DVFSRC_SW_REQ2, DVFSRC_LEVEL, @@ -110,6 +135,9 @@ enum dvfsrc_regs { DVFSRC_SW_HRT_BW, DVFSRC_SW_EMI_BW, DVFSRC_VCORE, + DVFSRC_TARGET_GEAR, + DVFSRC_GEAR_INFO_L, + DVFSRC_GEAR_INFO_H, DVFSRC_REGS_MAX, }; @@ -130,6 +158,22 @@ static const int dvfsrc_mt8195_regs[] = { [DVFSRC_TARGET_LEVEL] = 0xd48, }; +static const int dvfsrc_mt8196_regs[] = { + [DVFSRC_BASIC_CONTROL] = 0x0, + [DVFSRC_SW_REQ] = 0x18, + [DVFSRC_VCORE] = 0x80, + [DVFSRC_GEAR_INFO_L] = 0xfc, + [DVFSRC_SW_BW] = 0x1e8, + [DVFSRC_SW_PEAK_BW] = 0x1f4, + [DVFSRC_SW_HRT_BW] = 0x20c, + [DVFSRC_LEVEL] = 0x5f0, + [DVFSRC_TARGET_LEVEL] = 0x5f0, + [DVFSRC_SW_REQ2] = 0x604, + [DVFSRC_SW_EMI_BW] = 0x60c, + [DVFSRC_TARGET_GEAR] = 0x6ac, + [DVFSRC_GEAR_INFO_H] = 0x6b0, +}; + static const struct dvfsrc_opp *dvfsrc_get_current_opp(struct mtk_dvfsrc *dvfsrc) { u32 level = dvfsrc->dvd->get_current_level(dvfsrc); @@ -137,6 +181,20 @@ static const struct dvfsrc_opp *dvfsrc_get_current_opp(struct mtk_dvfsrc *dvfsrc return &dvfsrc->curr_opps->opps[level]; } +static u32 dvfsrc_get_current_target_vcore_gear(struct mtk_dvfsrc *dvfsrc) +{ + u32 val = dvfsrc_readl(dvfsrc, DVFSRC_TARGET_GEAR); + + return FIELD_GET(DVFSRC_V4_GEAR_TARGET_VCORE, val); +} + +static u32 dvfsrc_get_current_target_dram_gear(struct mtk_dvfsrc *dvfsrc) +{ + u32 val = dvfsrc_readl(dvfsrc, DVFSRC_TARGET_GEAR); + + return FIELD_GET(DVFSRC_V4_GEAR_TARGET_DRAM, val); +} + static bool dvfsrc_is_idle(struct mtk_dvfsrc *dvfsrc) { if (!dvfsrc->dvd->get_target_level) @@ -193,6 +251,24 @@ static int dvfsrc_wait_for_opp_level_v2(struct mtk_dvfsrc *dvfsrc, u32 level) return 0; } +static int dvfsrc_wait_for_vcore_level_v4(struct mtk_dvfsrc *dvfsrc, u32 level) +{ + u32 val; + + return readx_poll_timeout_atomic(dvfsrc_get_current_target_vcore_gear, + dvfsrc, val, val >= level, + STARTUP_TIME_US, DVFSRC_POLL_TIMEOUT_US); +} + +static int dvfsrc_wait_for_opp_level_v4(struct mtk_dvfsrc *dvfsrc, u32 level) +{ + u32 val; + + return readx_poll_timeout_atomic(dvfsrc_get_current_target_dram_gear, + dvfsrc, val, val >= level, + STARTUP_TIME_US, DVFSRC_POLL_TIMEOUT_US); +} + static u32 dvfsrc_get_target_level_v1(struct mtk_dvfsrc *dvfsrc) { u32 val = dvfsrc_readl(dvfsrc, DVFSRC_LEVEL); @@ -226,6 +302,27 @@ static u32 dvfsrc_get_current_level_v2(struct mtk_dvfsrc *dvfsrc) return 0; } +static u32 dvfsrc_get_target_level_v4(struct mtk_dvfsrc *dvfsrc) +{ + u32 val = dvfsrc_readl(dvfsrc, DVFSRC_TARGET_LEVEL); + + if (val & DVFSRC_V4_LEVEL_TARGET_PRESENT) + return FIELD_GET(DVFSRC_V4_LEVEL_TARGET_LEVEL, val) + 1; + return 0; +} + +static u32 dvfsrc_get_current_level_v4(struct mtk_dvfsrc *dvfsrc) +{ + u32 level = dvfsrc_readl(dvfsrc, DVFSRC_LEVEL) + 1; + + /* Valid levels */ + if (level < dvfsrc->curr_opps->num_opp) + return dvfsrc->curr_opps->num_opp - level; + + /* Zero for level 0 or invalid level */ + return 0; +} + static u32 dvfsrc_get_vcore_level_v1(struct mtk_dvfsrc *dvfsrc) { u32 val = dvfsrc_readl(dvfsrc, DVFSRC_SW_REQ2); @@ -277,11 +374,30 @@ static void dvfsrc_set_vscp_level_v2(struct mtk_dvfsrc *dvfsrc, u32 level) dvfsrc_writel(dvfsrc, DVFSRC_VCORE, val); } +static u32 dvfsrc_get_opp_count_v4(struct mtk_dvfsrc *dvfsrc) +{ + u32 val = dvfsrc_readl(dvfsrc, DVFSRC_BASIC_CONTROL); + + return FIELD_GET(DVFSRC_V4_BASIC_CTRL_OPP_COUNT, val) + 1; +} + static u32 dvfsrc_calc_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, int type, u64 bw) { return (u32)div_u64(bw, 100 * 1000); } +static u32 dvfsrc_calc_dram_bw_v4(struct mtk_dvfsrc *dvfsrc, int type, u64 bw) +{ + u8 bw_unit = dvfsrc->dvd->bw_units[type]; + u64 bw_mbps; + + if (type < DVFSRC_BW_AVG || type >= DVFSRC_BW_MAX) + return 0; + + bw_mbps = div_u64(bw, 1000); + return (u32)div_u64((bw_mbps + bw_unit - 1), bw_unit); +} + static void __dvfsrc_set_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, u32 reg, int type, u16 max_bw, u16 min_bw, u64 bw) { @@ -333,6 +449,100 @@ static void dvfsrc_set_opp_level_v1(struct mtk_dvfsrc *dvfsrc, u32 level) dvfsrc_writel(dvfsrc, DVFSRC_SW_REQ, val); } +static u32 dvfsrc_get_opp_gear(struct mtk_dvfsrc *dvfsrc, u8 level) +{ + u32 reg_ofst, val; + u8 idx; + + /* Calculate register offset and index for requested gear */ + if (level < DVFSRC_V4_GEAR_INFO_REG_LEVELS) { + reg_ofst = dvfsrc->dvd->regs[DVFSRC_GEAR_INFO_L]; + idx = level; + } else { + reg_ofst = dvfsrc->dvd->regs[DVFSRC_GEAR_INFO_H]; + idx = level - DVFSRC_V4_GEAR_INFO_REG_LEVELS; + } + reg_ofst += DVFSRC_V4_GEAR_INFO_REG_WIDTH * (level / 2); + + /* Read the corresponding gear register */ + val = readl(dvfsrc->regs + reg_ofst); + + /* Each register contains two sets of data, 16 bits per gear */ + val >>= 16 * (idx % 2); + + return val; +} + +static int dvfsrc_get_hw_opps_v4(struct mtk_dvfsrc *dvfsrc) +{ + struct dvfsrc_opp *dvfsrc_opps; + struct dvfsrc_opp_desc *desc; + u32 num_opps, gear_info; + u8 num_vcore, num_dram; + u8 num_emi; + int i; + + num_opps = dvfsrc_get_opp_count_v4(dvfsrc); + if (num_opps == 0) { + dev_err(dvfsrc->dev, "No OPPs programmed in DVFSRC MCU.\n"); + return -EINVAL; + } + + /* + * The first 16 bits set in the gear info table says how many OPPs + * and how many vcore, dram and emi table entries are available. + */ + gear_info = dvfsrc_readl(dvfsrc, DVFSRC_GEAR_INFO_L); + if (gear_info == 0) { + dev_err(dvfsrc->dev, "No gear info in DVFSRC MCU.\n"); + return -EINVAL; + } + + num_vcore = FIELD_GET(DVFSRC_V4_GEAR_INFO_VCORE, gear_info) + 1; + num_dram = FIELD_GET(DVFSRC_V4_GEAR_INFO_DRAM, gear_info) + 1; + num_emi = FIELD_GET(DVFSRC_V4_GEAR_INFO_EMI, gear_info) + 1; + dev_info(dvfsrc->dev, + "Discovered %u gears and %u vcore, %u dram, %u emi table entries.\n", + num_opps, num_vcore, num_dram, num_emi); + + /* Allocate everything now as anything else after that cannot fail */ + desc = devm_kzalloc(dvfsrc->dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + dvfsrc_opps = devm_kcalloc(dvfsrc->dev, num_opps + 1, + sizeof(*dvfsrc_opps), GFP_KERNEL); + if (!dvfsrc_opps) + return -ENOMEM; + + /* Read the OPP table gear indices */ + for (i = 0; i <= num_opps; i++) { + gear_info = dvfsrc_get_opp_gear(dvfsrc, num_opps - i); + dvfsrc_opps[i].vcore_opp = FIELD_GET(DVFSRC_V4_GEAR_INFO_VCORE, gear_info); + dvfsrc_opps[i].dram_opp = FIELD_GET(DVFSRC_V4_GEAR_INFO_DRAM, gear_info); + dvfsrc_opps[i].emi_opp = FIELD_GET(DVFSRC_V4_GEAR_INFO_EMI, gear_info); + }; + desc->num_opp = num_opps + 1; + desc->opps = dvfsrc_opps; + + /* Assign to main structure now that everything is done! */ + dvfsrc->curr_opps = desc; + + return 0; +} + +static void dvfsrc_set_dram_level_v4(struct mtk_dvfsrc *dvfsrc, u32 level) +{ + u32 val = dvfsrc_readl(dvfsrc, DVFSRC_SW_REQ); + + val &= ~DVFSRC_V4_SW_REQ_DRAM_LEVEL; + val |= FIELD_PREP(DVFSRC_V4_SW_REQ_DRAM_LEVEL, level); + + dev_dbg(dvfsrc->dev, "%s level=%u\n", __func__, level); + + dvfsrc_writel(dvfsrc, DVFSRC_SW_REQ, val); +} + int mtk_dvfsrc_send_request(const struct device *dev, u32 cmd, u64 data) { struct mtk_dvfsrc *dvfsrc = dev_get_drvdata(dev); @@ -448,7 +658,14 @@ static int mtk_dvfsrc_probe(struct platform_device *pdev) dvfsrc->dram_type = ares.a1; dev_dbg(&pdev->dev, "DRAM Type: %d\n", dvfsrc->dram_type); - dvfsrc->curr_opps = &dvfsrc->dvd->opps_desc[dvfsrc->dram_type]; + /* Newer versions of the DVFSRC MCU have pre-programmed gear tables */ + if (dvfsrc->dvd->get_hw_opps) { + ret = dvfsrc->dvd->get_hw_opps(dvfsrc); + if (ret) + return ret; + } else { + dvfsrc->curr_opps = &dvfsrc->dvd->opps_desc[dvfsrc->dram_type]; + } platform_set_drvdata(pdev, dvfsrc); ret = devm_of_platform_populate(&pdev->dev); @@ -576,10 +793,39 @@ static const struct dvfsrc_soc_data mt8195_data = { .bw_constraints = &dvfsrc_bw_constr_v2, }; +static const u8 mt8196_bw_units[] = { + [DVFSRC_BW_AVG] = 64, + [DVFSRC_BW_PEAK] = 64, + [DVFSRC_BW_HRT] = 30, +}; + +static const struct dvfsrc_soc_data mt8196_data = { + .regs = dvfsrc_mt8196_regs, + .bw_units = mt8196_bw_units, + .has_emi_ddr = true, + .get_target_level = dvfsrc_get_target_level_v4, + .get_current_level = dvfsrc_get_current_level_v4, + .get_vcore_level = dvfsrc_get_vcore_level_v2, + .get_vscp_level = dvfsrc_get_vscp_level_v2, + .get_opp_count = dvfsrc_get_opp_count_v4, + .get_hw_opps = dvfsrc_get_hw_opps_v4, + .calc_dram_bw = dvfsrc_calc_dram_bw_v4, + .set_dram_bw = dvfsrc_set_dram_bw_v1, + .set_dram_peak_bw = dvfsrc_set_dram_peak_bw_v1, + .set_dram_hrt_bw = dvfsrc_set_dram_hrt_bw_v1, + .set_opp_level = dvfsrc_set_dram_level_v4, + .set_vcore_level = dvfsrc_set_vcore_level_v2, + .set_vscp_level = dvfsrc_set_vscp_level_v2, + .wait_for_opp_level = dvfsrc_wait_for_opp_level_v4, + .wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v4, + .bw_constraints = &dvfsrc_bw_constr_v1, +}; + static const struct of_device_id mtk_dvfsrc_of_match[] = { { .compatible = "mediatek,mt6893-dvfsrc", .data = &mt6893_data }, { .compatible = "mediatek,mt8183-dvfsrc", .data = &mt8183_data }, { .compatible = "mediatek,mt8195-dvfsrc", .data = &mt8195_data }, + { .compatible = "mediatek,mt8196-dvfsrc", .data = &mt8196_data }, { /* sentinel */ } }; From 39aa8c4e762ea9b00d66cc55957527167ed89435 Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Mon, 24 Nov 2025 12:06:58 +0100 Subject: [PATCH 084/171] soc: mediatek: mtk-dvfsrc: Get and Enable DVFSRC clock The DVFSRC has a clock on all platforms. Get and enable it in the probe function, so that Linux's common clock framework knows we're a user of it. Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Nicolas Frattaroli Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-dvfsrc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/soc/mediatek/mtk-dvfsrc.c b/drivers/soc/mediatek/mtk-dvfsrc.c index 3a83fd4baf54..a43d6f913914 100644 --- a/drivers/soc/mediatek/mtk-dvfsrc.c +++ b/drivers/soc/mediatek/mtk-dvfsrc.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -83,6 +84,7 @@ struct dvfsrc_opp_desc { struct dvfsrc_soc_data; struct mtk_dvfsrc { struct device *dev; + struct clk *clk; struct platform_device *icc; struct platform_device *regulator; const struct dvfsrc_soc_data *dvd; @@ -650,6 +652,11 @@ static int mtk_dvfsrc_probe(struct platform_device *pdev) if (IS_ERR(dvfsrc->regs)) return PTR_ERR(dvfsrc->regs); + dvfsrc->clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(dvfsrc->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(dvfsrc->clk), + "Couldn't get and enable DVFSRC clock\n"); + arm_smccc_smc(MTK_SIP_DVFSRC_VCOREFS_CONTROL, MTK_SIP_DVFSRC_INIT, 0, 0, 0, 0, 0, 0, &ares); if (ares.a0) From 3da293d70005496317d1ff3a49b89c29dd7c21e8 Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Mon, 24 Nov 2025 12:06:59 +0100 Subject: [PATCH 085/171] soc: mediatek: mtk-dvfsrc: Rework bandwidth calculations The code, as it is, plays fast and loose with bandwidth units. It also doesn't specify its constraints in the actual maximum hardware value, but as some roundabout thing that then ends up multiplied into the actual hardware value constraint after some indirections. In part, this is due to the use of individual members for storing each limit, instead of making it possible to index them by type. Rework all of this by adding const array members indexed by the bandwidth type enum to the soc_data struct. This array expresses the actual hardware value limitations, not a factor thereof. Use the clamp function macro to clamp the values between the minimum and maximum constraints after all the calculations, which also means the code doesn't write nonsense to a hardware register when the math is wrong, as it'll constrain after all the calculations. Pass the type as the actual enum type as well, and not as an int. If there's some type checking that can be extracted from the function signature, then we may as well use it. Don't needlessly explicitly cast return values to the return type either; this is both unnecessary and makes it harder to spot type safety issues. Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Nicolas Frattaroli Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-dvfsrc.c | 107 +++++++++++++++++++----------- 1 file changed, 67 insertions(+), 40 deletions(-) diff --git a/drivers/soc/mediatek/mtk-dvfsrc.c b/drivers/soc/mediatek/mtk-dvfsrc.c index a43d6f913914..548a28f50242 100644 --- a/drivers/soc/mediatek/mtk-dvfsrc.c +++ b/drivers/soc/mediatek/mtk-dvfsrc.c @@ -64,12 +64,6 @@ enum mtk_dvfsrc_bw_type { DVFSRC_BW_MAX, }; -struct dvfsrc_bw_constraints { - u16 max_dram_nom_bw; - u16 max_dram_peak_bw; - u16 max_dram_hrt_bw; -}; - struct dvfsrc_opp { u32 vcore_opp; u32 dram_opp; @@ -98,7 +92,7 @@ struct dvfsrc_soc_data { const u8 *bw_units; const bool has_emi_ddr; const struct dvfsrc_opp_desc *opps_desc; - u32 (*calc_dram_bw)(struct mtk_dvfsrc *dvfsrc, int type, u64 bw); + u32 (*calc_dram_bw)(struct mtk_dvfsrc *dvfsrc, enum mtk_dvfsrc_bw_type type, u64 bw); u32 (*get_target_level)(struct mtk_dvfsrc *dvfsrc); u32 (*get_current_level)(struct mtk_dvfsrc *dvfsrc); u32 (*get_vcore_level)(struct mtk_dvfsrc *dvfsrc); @@ -113,7 +107,22 @@ struct dvfsrc_soc_data { void (*set_vscp_level)(struct mtk_dvfsrc *dvfsrc, u32 level); int (*wait_for_opp_level)(struct mtk_dvfsrc *dvfsrc, u32 level); int (*wait_for_vcore_level)(struct mtk_dvfsrc *dvfsrc, u32 level); - const struct dvfsrc_bw_constraints *bw_constraints; + + /** + * @bw_max_constraints - array of maximum bandwidth for this hardware + * + * indexed by &enum mtk_dvfsrc_bw_type, storing the maximum permissible + * hardware value for each bandwidth type. + */ + const u32 *const bw_max_constraints; + + /** + * @bw_min_constraints - array of minimum bandwidth for this hardware + * + * indexed by &enum mtk_dvfsrc_bw_type, storing the minimum permissible + * hardware value for each bandwidth type. + */ + const u32 *const bw_min_constraints; }; static u32 dvfsrc_readl(struct mtk_dvfsrc *dvfs, u32 offset) @@ -383,59 +392,62 @@ static u32 dvfsrc_get_opp_count_v4(struct mtk_dvfsrc *dvfsrc) return FIELD_GET(DVFSRC_V4_BASIC_CTRL_OPP_COUNT, val) + 1; } -static u32 dvfsrc_calc_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, int type, u64 bw) +static u32 +dvfsrc_calc_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, enum mtk_dvfsrc_bw_type type, u64 bw) { - return (u32)div_u64(bw, 100 * 1000); + return clamp_val(div_u64(bw, 100 * 1000), dvfsrc->dvd->bw_min_constraints[type], + dvfsrc->dvd->bw_max_constraints[type]); } -static u32 dvfsrc_calc_dram_bw_v4(struct mtk_dvfsrc *dvfsrc, int type, u64 bw) +/** + * dvfsrc_calc_dram_bw_v4 - convert kbps to hardware register bandwidth value + * @dvfsrc: pointer to the &struct mtk_dvfsrc of this driver instance + * @type: one of %DVFSRC_BW_AVG, %DVFSRC_BW_PEAK, or %DVFSRC_BW_HRT + * @bw: the bandwidth in kilobits per second + * + * Returns the hardware register value appropriate for expressing @bw, clamped + * to hardware limits. + */ +static u32 +dvfsrc_calc_dram_bw_v4(struct mtk_dvfsrc *dvfsrc, enum mtk_dvfsrc_bw_type type, u64 bw) { u8 bw_unit = dvfsrc->dvd->bw_units[type]; u64 bw_mbps; + u32 bw_hw; if (type < DVFSRC_BW_AVG || type >= DVFSRC_BW_MAX) return 0; bw_mbps = div_u64(bw, 1000); - return (u32)div_u64((bw_mbps + bw_unit - 1), bw_unit); + bw_hw = div_u64((bw_mbps + bw_unit - 1), bw_unit); + return clamp_val(bw_hw, dvfsrc->dvd->bw_min_constraints[type], + dvfsrc->dvd->bw_max_constraints[type]); } static void __dvfsrc_set_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, u32 reg, - int type, u16 max_bw, u16 min_bw, u64 bw) + enum mtk_dvfsrc_bw_type type, u64 bw) { - u32 new_bw = dvfsrc->dvd->calc_dram_bw(dvfsrc, type, bw); + u32 bw_hw = dvfsrc->dvd->calc_dram_bw(dvfsrc, type, bw); - /* If bw constraints (in mbps) are defined make sure to respect them */ - if (max_bw) - new_bw = min(new_bw, max_bw); - if (min_bw && new_bw > 0) - new_bw = max(new_bw, min_bw); - - dvfsrc_writel(dvfsrc, reg, new_bw); + dvfsrc_writel(dvfsrc, reg, bw_hw); if (type == DVFSRC_BW_AVG && dvfsrc->dvd->has_emi_ddr) - dvfsrc_writel(dvfsrc, DVFSRC_SW_EMI_BW, bw); + dvfsrc_writel(dvfsrc, DVFSRC_SW_EMI_BW, bw_hw); } static void dvfsrc_set_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, u64 bw) { - u64 max_bw = dvfsrc->dvd->bw_constraints->max_dram_nom_bw; - - __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_BW, DVFSRC_BW_AVG, max_bw, 0, bw); + __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_BW, DVFSRC_BW_AVG, bw); }; static void dvfsrc_set_dram_peak_bw_v1(struct mtk_dvfsrc *dvfsrc, u64 bw) { - u64 max_bw = dvfsrc->dvd->bw_constraints->max_dram_peak_bw; - - __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_PEAK_BW, DVFSRC_BW_PEAK, max_bw, 0, bw); + __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_PEAK_BW, DVFSRC_BW_PEAK, bw); } static void dvfsrc_set_dram_hrt_bw_v1(struct mtk_dvfsrc *dvfsrc, u64 bw) { - u64 max_bw = dvfsrc->dvd->bw_constraints->max_dram_hrt_bw; - - __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_HRT_BW, DVFSRC_BW_HRT, max_bw, 0, bw); + __dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_HRT_BW, DVFSRC_BW_HRT, bw); } static void dvfsrc_set_opp_level_v1(struct mtk_dvfsrc *dvfsrc, u32 level) @@ -688,11 +700,22 @@ static int mtk_dvfsrc_probe(struct platform_device *pdev) return 0; } -static const struct dvfsrc_bw_constraints dvfsrc_bw_constr_v1 = { 0, 0, 0 }; -static const struct dvfsrc_bw_constraints dvfsrc_bw_constr_v2 = { - .max_dram_nom_bw = 255, - .max_dram_peak_bw = 255, - .max_dram_hrt_bw = 1023, +static const u32 dvfsrc_bw_min_constr_none[DVFSRC_BW_MAX] = { + [DVFSRC_BW_AVG] = 0, + [DVFSRC_BW_PEAK] = 0, + [DVFSRC_BW_HRT] = 0, +}; + +static const u32 dvfsrc_bw_max_constr_v1[DVFSRC_BW_MAX] = { + [DVFSRC_BW_AVG] = U32_MAX, + [DVFSRC_BW_PEAK] = U32_MAX, + [DVFSRC_BW_HRT] = U32_MAX, +}; + +static const u32 dvfsrc_bw_max_constr_v2[DVFSRC_BW_MAX] = { + [DVFSRC_BW_AVG] = 65535, + [DVFSRC_BW_PEAK] = 65535, + [DVFSRC_BW_HRT] = 1023, }; static const struct dvfsrc_opp dvfsrc_opp_mt6893_lp4[] = { @@ -725,7 +748,8 @@ static const struct dvfsrc_soc_data mt6893_data = { .set_vscp_level = dvfsrc_set_vscp_level_v2, .wait_for_opp_level = dvfsrc_wait_for_opp_level_v2, .wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v1, - .bw_constraints = &dvfsrc_bw_constr_v2, + .bw_max_constraints = dvfsrc_bw_max_constr_v2, + .bw_min_constraints = dvfsrc_bw_min_constr_none, }; static const struct dvfsrc_opp dvfsrc_opp_mt8183_lp4[] = { @@ -763,7 +787,8 @@ static const struct dvfsrc_soc_data mt8183_data = { .set_vcore_level = dvfsrc_set_vcore_level_v1, .wait_for_opp_level = dvfsrc_wait_for_opp_level_v1, .wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v1, - .bw_constraints = &dvfsrc_bw_constr_v1, + .bw_max_constraints = dvfsrc_bw_max_constr_v1, + .bw_min_constraints = dvfsrc_bw_min_constr_none, }; static const struct dvfsrc_opp dvfsrc_opp_mt8195_lp4[] = { @@ -797,7 +822,8 @@ static const struct dvfsrc_soc_data mt8195_data = { .set_vscp_level = dvfsrc_set_vscp_level_v2, .wait_for_opp_level = dvfsrc_wait_for_opp_level_v2, .wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v1, - .bw_constraints = &dvfsrc_bw_constr_v2, + .bw_max_constraints = dvfsrc_bw_max_constr_v2, + .bw_min_constraints = dvfsrc_bw_min_constr_none, }; static const u8 mt8196_bw_units[] = { @@ -825,7 +851,8 @@ static const struct dvfsrc_soc_data mt8196_data = { .set_vscp_level = dvfsrc_set_vscp_level_v2, .wait_for_opp_level = dvfsrc_wait_for_opp_level_v4, .wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v4, - .bw_constraints = &dvfsrc_bw_constr_v1, + .bw_max_constraints = dvfsrc_bw_max_constr_v2, + .bw_min_constraints = dvfsrc_bw_min_constr_none, }; static const struct of_device_id mtk_dvfsrc_of_match[] = { From b3d8508351af7f6366a0a18068c194b399ead2c3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Nov 2025 14:57:39 +0100 Subject: [PATCH 086/171] reset: gpio: add the "compatible" property In order to correctly handle the interaction between the reset-gpio devices and shared GPIOs managed by GPIOLIB, we need to be able to identify the former. Add the "compatible" property to allow us to use the device_is_compatible() helper. Signed-off-by: Bartosz Golaszewski Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 0135dd0ae204..0666dfc41ca9 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -868,11 +868,11 @@ static int reset_add_gpio_aux_device(struct device *parent, */ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args) { - struct property_entry properties[2] = { }; + struct property_entry properties[3] = { }; unsigned int offset, of_flags, lflags; struct reset_gpio_lookup *rgpio_dev; struct device *parent; - int id, ret; + int id, ret, prop = 0; /* * Currently only #gpio-cells=2 is supported with the meaning of: @@ -923,7 +923,8 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args) lflags = GPIO_PERSISTENT | (of_flags & GPIO_ACTIVE_LOW); parent = gpio_device_to_device(gdev); - properties[0] = PROPERTY_ENTRY_GPIO("reset-gpios", parent->fwnode, offset, lflags); + properties[prop++] = PROPERTY_ENTRY_STRING("compatible", "reset-gpio"); + properties[prop++] = PROPERTY_ENTRY_GPIO("reset-gpios", parent->fwnode, offset, lflags); id = ida_alloc(&reset_gpio_ida, GFP_KERNEL); if (id < 0) From 2b7a02c322922a37cc5fc15d055b794cc2193062 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Fri, 19 Dec 2025 21:52:08 +0800 Subject: [PATCH 087/171] clk: spacemit: prepare common ccu header In order to prepare adding clock driver for new K3 SoC, extract generic code to a separate common ccu header file, so they are not defined in K1 SoC-specific file, and then can be shared by all clock drivers. Link: https://lore.kernel.org/r/20260108-06-k1-clk-common-v4-1-badf635993d3@gentoo.org Reviewed-by: Alex Elder Signed-off-by: Yixun Lan --- include/soc/spacemit/ccu.h | 21 +++++++++++++++++++++ include/soc/spacemit/k1-syscon.h | 12 +----------- 2 files changed, 22 insertions(+), 11 deletions(-) create mode 100644 include/soc/spacemit/ccu.h diff --git a/include/soc/spacemit/ccu.h b/include/soc/spacemit/ccu.h new file mode 100644 index 000000000000..84dcdecccc05 --- /dev/null +++ b/include/soc/spacemit/ccu.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_SPACEMIT_CCU_H__ +#define __SOC_SPACEMIT_CCU_H__ + +#include +#include + +/* Auxiliary device used to represent a CCU reset controller */ +struct spacemit_ccu_adev { + struct auxiliary_device adev; + struct regmap *regmap; +}; + +static inline struct spacemit_ccu_adev * +to_spacemit_ccu_adev(struct auxiliary_device *adev) +{ + return container_of(adev, struct spacemit_ccu_adev, adev); +} + +#endif /* __SOC_SPACEMIT_CCU_H__ */ diff --git a/include/soc/spacemit/k1-syscon.h b/include/soc/spacemit/k1-syscon.h index 354751562c55..0be7a2e8d445 100644 --- a/include/soc/spacemit/k1-syscon.h +++ b/include/soc/spacemit/k1-syscon.h @@ -5,17 +5,7 @@ #ifndef __SOC_K1_SYSCON_H__ #define __SOC_K1_SYSCON_H__ -/* Auxiliary device used to represent a CCU reset controller */ -struct spacemit_ccu_adev { - struct auxiliary_device adev; - struct regmap *regmap; -}; - -static inline struct spacemit_ccu_adev * -to_spacemit_ccu_adev(struct auxiliary_device *adev) -{ - return container_of(adev, struct spacemit_ccu_adev, adev); -} +#include "ccu.h" /* APBS register offset */ #define APBS_PLL1_SWCR1 0x100 From ecff77f7c04141cc18ee2482936c96117060c0f2 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Fri, 19 Dec 2025 05:34:39 +0800 Subject: [PATCH 088/171] reset: spacemit: fix auxiliary device id Due to the auxiliary register procedure moved to ccu common module where the module name changed to spacemit_ccu, then the reset auxiliary device register id also need to be adjusted in order to prepare for adding new K3 reset driver, otherwise two reset drivers will claim to support same "compatible" auxiliary device. In order to prevent the reset driver breakage, this commit is necessary as a post-fix for changes introduced by two patches below, and should be merged with them to make the patch series runtime bisectable. ("clk: spacemit: add platform SoC prefix to reset name") ("clk: spacemit: extract common ccu functions") Link: https://lore.kernel.org/r/20260108-06-k1-clk-common-v4-4-badf635993d3@gentoo.org Acked-by: Philipp Zabel Reviewed-by: Alex Elder Signed-off-by: Yixun Lan --- drivers/reset/reset-spacemit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/reset/reset-spacemit.c b/drivers/reset/reset-spacemit.c index e1272aff28f7..cc7fd1f8750d 100644 --- a/drivers/reset/reset-spacemit.c +++ b/drivers/reset/reset-spacemit.c @@ -278,7 +278,7 @@ static int spacemit_reset_probe(struct auxiliary_device *adev, #define K1_AUX_DEV_ID(_unit) \ { \ - .name = "spacemit_ccu_k1." #_unit "-reset", \ + .name = "spacemit_ccu.k1-" #_unit "-reset", \ .driver_data = (kernel_ulong_t)&k1_ ## _unit ## _reset_data, \ } From 091d19cc24018f2bd783e932fb2403cb7a2bdb3c Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Sat, 20 Dec 2025 21:28:15 +0800 Subject: [PATCH 089/171] clk: spacemit: k3: extract common header Extracting common header file, which will be shared by clock and reset drivers. So will make it easy to add reset driver for K3 SoC later. Link: https://lore.kernel.org/r/20260108-k3-clk-v5-4-42a11b74ad58@gentoo.org Signed-off-by: Yixun Lan --- include/soc/spacemit/k3-syscon.h | 273 +++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 include/soc/spacemit/k3-syscon.h diff --git a/include/soc/spacemit/k3-syscon.h b/include/soc/spacemit/k3-syscon.h new file mode 100644 index 000000000000..0299bea065a0 --- /dev/null +++ b/include/soc/spacemit/k3-syscon.h @@ -0,0 +1,273 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* SpacemiT clock and reset driver definitions for the K3 SoC */ + +#ifndef __SOC_K3_SYSCON_H__ +#define __SOC_K3_SYSCON_H__ + +#include "ccu.h" + +/* APBS register offset */ +#define APBS_PLL1_SWCR1 0x100 +#define APBS_PLL1_SWCR2 0x104 +#define APBS_PLL1_SWCR3 0x108 +#define APBS_PLL2_SWCR1 0x118 +#define APBS_PLL2_SWCR2 0x11c +#define APBS_PLL2_SWCR3 0x120 +#define APBS_PLL3_SWCR1 0x124 +#define APBS_PLL3_SWCR2 0x128 +#define APBS_PLL3_SWCR3 0x12c +#define APBS_PLL4_SWCR1 0x130 +#define APBS_PLL4_SWCR2 0x134 +#define APBS_PLL4_SWCR3 0x138 +#define APBS_PLL5_SWCR1 0x13c +#define APBS_PLL5_SWCR2 0x140 +#define APBS_PLL5_SWCR3 0x144 +#define APBS_PLL6_SWCR1 0x148 +#define APBS_PLL6_SWCR2 0x14c +#define APBS_PLL6_SWCR3 0x150 +#define APBS_PLL7_SWCR1 0x158 +#define APBS_PLL7_SWCR2 0x15c +#define APBS_PLL7_SWCR3 0x160 +#define APBS_PLL8_SWCR1 0x180 +#define APBS_PLL8_SWCR2 0x184 +#define APBS_PLL8_SWCR3 0x188 + +/* MPMU register offset */ +#define MPMU_FCCR 0x0008 +#define MPMU_POSR 0x0010 +#define POSR_PLL1_LOCK BIT(24) +#define POSR_PLL2_LOCK BIT(25) +#define POSR_PLL3_LOCK BIT(26) +#define POSR_PLL4_LOCK BIT(27) +#define POSR_PLL5_LOCK BIT(28) +#define POSR_PLL6_LOCK BIT(29) +#define POSR_PLL7_LOCK BIT(30) +#define POSR_PLL8_LOCK BIT(31) +#define MPMU_SUCCR 0x0014 +#define MPMU_ISCCR 0x0044 +#define MPMU_WDTPCR 0x0200 +#define MPMU_RIPCCR 0x0210 +#define MPMU_ACGR 0x1024 +#define MPMU_APBCSCR 0x1050 +#define MPMU_SUCCR_1 0x10b0 + +#define MPMU_I2S0_SYSCLK 0x1100 +#define MPMU_I2S2_SYSCLK 0x1104 +#define MPMU_I2S3_SYSCLK 0x1108 +#define MPMU_I2S4_SYSCLK 0x110c +#define MPMU_I2S5_SYSCLK 0x1110 +#define MPMU_I2S_SYSCLK_CTRL 0x1114 + +/* APBC register offset */ +#define APBC_UART0_CLK_RST 0x00 +#define APBC_UART2_CLK_RST 0x04 +#define APBC_GPIO_CLK_RST 0x08 +#define APBC_PWM0_CLK_RST 0x0c +#define APBC_PWM1_CLK_RST 0x10 +#define APBC_PWM2_CLK_RST 0x14 +#define APBC_PWM3_CLK_RST 0x18 +#define APBC_TWSI8_CLK_RST 0x20 +#define APBC_UART3_CLK_RST 0x24 +#define APBC_RTC_CLK_RST 0x28 +#define APBC_TWSI0_CLK_RST 0x2c +#define APBC_TWSI1_CLK_RST 0x30 +#define APBC_TIMERS0_CLK_RST 0x34 +#define APBC_TWSI2_CLK_RST 0x38 +#define APBC_AIB_CLK_RST 0x3c +#define APBC_TWSI4_CLK_RST 0x40 +#define APBC_TIMERS1_CLK_RST 0x44 +#define APBC_ONEWIRE_CLK_RST 0x48 +#define APBC_TWSI5_CLK_RST 0x4c +#define APBC_DRO_CLK_RST 0x58 +#define APBC_IR0_CLK_RST 0x5c +#define APBC_IR1_CLK_RST 0x1c +#define APBC_TWSI6_CLK_RST 0x60 +#define APBC_COUNTER_CLK_SEL 0x64 +#define APBC_TSEN_CLK_RST 0x6c +#define APBC_UART4_CLK_RST 0x70 +#define APBC_UART5_CLK_RST 0x74 +#define APBC_UART6_CLK_RST 0x78 +#define APBC_SSP3_CLK_RST 0x7c +#define APBC_SSPA0_CLK_RST 0x80 +#define APBC_SSPA1_CLK_RST 0x84 +#define APBC_SSPA2_CLK_RST 0x88 +#define APBC_SSPA3_CLK_RST 0x8c +#define APBC_IPC_AP2AUD_CLK_RST 0x90 +#define APBC_UART7_CLK_RST 0x94 +#define APBC_UART8_CLK_RST 0x98 +#define APBC_UART9_CLK_RST 0x9c +#define APBC_CAN0_CLK_RST 0xa0 +#define APBC_CAN1_CLK_RST 0xa4 +#define APBC_PWM4_CLK_RST 0xa8 +#define APBC_PWM5_CLK_RST 0xac +#define APBC_PWM6_CLK_RST 0xb0 +#define APBC_PWM7_CLK_RST 0xb4 +#define APBC_PWM8_CLK_RST 0xb8 +#define APBC_PWM9_CLK_RST 0xbc +#define APBC_PWM10_CLK_RST 0xc0 +#define APBC_PWM11_CLK_RST 0xc4 +#define APBC_PWM12_CLK_RST 0xc8 +#define APBC_PWM13_CLK_RST 0xcc +#define APBC_PWM14_CLK_RST 0xd0 +#define APBC_PWM15_CLK_RST 0xd4 +#define APBC_PWM16_CLK_RST 0xd8 +#define APBC_PWM17_CLK_RST 0xdc +#define APBC_PWM18_CLK_RST 0xe0 +#define APBC_PWM19_CLK_RST 0xe4 +#define APBC_TIMERS2_CLK_RST 0x11c +#define APBC_TIMERS3_CLK_RST 0x120 +#define APBC_TIMERS4_CLK_RST 0x124 +#define APBC_TIMERS5_CLK_RST 0x128 +#define APBC_TIMERS6_CLK_RST 0x12c +#define APBC_TIMERS7_CLK_RST 0x130 + +#define APBC_CAN2_CLK_RST 0x148 +#define APBC_CAN3_CLK_RST 0x14c +#define APBC_CAN4_CLK_RST 0x150 +#define APBC_UART10_CLK_RST 0x154 +#define APBC_SSP0_CLK_RST 0x158 +#define APBC_SSP1_CLK_RST 0x15c +#define APBC_SSPA4_CLK_RST 0x160 +#define APBC_SSPA5_CLK_RST 0x164 + +/* APMU register offset */ +#define APMU_CSI_CCIC2_CLK_RES_CTRL 0x024 +#define APMU_ISP_CLK_RES_CTRL 0x038 +#define APMU_PMU_CLK_GATE_CTRL 0x040 +#define APMU_LCD_CLK_RES_CTRL1 0x044 +#define APMU_LCD_SPI_CLK_RES_CTRL 0x048 +#define APMU_LCD_CLK_RES_CTRL2 0x04c +#define APMU_CCIC_CLK_RES_CTRL 0x050 +#define APMU_SDH0_CLK_RES_CTRL 0x054 +#define APMU_SDH1_CLK_RES_CTRL 0x058 +#define APMU_USB_CLK_RES_CTRL 0x05c +#define APMU_QSPI_CLK_RES_CTRL 0x060 +#define APMU_DMA_CLK_RES_CTRL 0x064 +#define APMU_AES_CLK_RES_CTRL 0x068 +#define APMU_MCB_CLK_RES_CTRL 0x06c +#define APMU_VPU_CLK_RES_CTRL 0x0a4 +#define APMU_DTC_CLK_RES_CTRL 0x0ac +#define APMU_GPU_CLK_RES_CTRL 0x0cc +#define APMU_SDH2_CLK_RES_CTRL 0x0e0 +#define APMU_PMUA_MC_CTRL 0x0e8 +#define APMU_PMU_CC2_AP 0x100 +#define APMU_PMUA_EM_CLK_RES_CTRL 0x104 +#define APMU_UCIE_CTRL 0x11c +#define APMU_RCPU_CLK_RES_CTRL 0x14c +#define APMU_TOP_DCLK_CTRL 0x158 +#define APMU_LCD_EDP_CTRL 0x23c +#define APMU_UFS_CLK_RES_CTRL 0x268 +#define APMU_LCD_CLK_RES_CTRL3 0x26c +#define APMU_LCD_CLK_RES_CTRL4 0x270 +#define APMU_LCD_CLK_RES_CTRL5 0x274 +#define APMU_CCI550_CLK_CTRL 0x300 +#define APMU_ACLK_CLK_CTRL 0x388 +#define APMU_CPU_C0_CLK_CTRL 0x38C +#define APMU_CPU_C1_CLK_CTRL 0x390 +#define APMU_CPU_C2_CLK_CTRL 0x394 +#define APMU_CPU_C3_CLK_CTRL 0x208 +#define APMU_PCIE_CLK_RES_CTRL_A 0x1f0 +#define APMU_PCIE_CLK_RES_CTRL_B 0x1c8 +#define APMU_PCIE_CLK_RES_CTRL_C 0x1d0 +#define APMU_PCIE_CLK_RES_CTRL_D 0x1e0 +#define APMU_PCIE_CLK_RES_CTRL_E 0x1e8 +#define APMU_EMAC0_CLK_RES_CTRL 0x3e4 +#define APMU_EMAC1_CLK_RES_CTRL 0x3ec +#define APMU_EMAC2_CLK_RES_CTRL 0x248 +#define APMU_ESPI_CLK_RES_CTRL 0x240 +#define APMU_SNR_ISIM_VCLK_CTRL 0x3f8 + +/* DCIU register offsets */ +#define DCIU_DMASYS_CLK_EN 0x234 +#define DCIU_DMASYS_SDMA_CLK_EN 0x238 +#define DCIU_C2_TCM_PIPE_CLK 0x244 +#define DCIU_C3_TCM_PIPE_CLK 0x248 + +#define DCIU_DMASYS_S0_RSTN 0x204 +#define DCIU_DMASYS_S1_RSTN 0x208 +#define DCIU_DMASYS_A0_RSTN 0x20C +#define DCIU_DMASYS_A1_RSTN 0x210 +#define DCIU_DMASYS_A2_RSTN 0x214 +#define DCIU_DMASYS_A3_RSTN 0x218 +#define DCIU_DMASYS_A4_RSTN 0x21C +#define DCIU_DMASYS_A5_RSTN 0x220 +#define DCIU_DMASYS_A6_RSTN 0x224 +#define DCIU_DMASYS_A7_RSTN 0x228 +#define DCIU_DMASYS_RSTN 0x22C +#define DCIU_DMASYS_SDMA_RSTN 0x230 + +/* RCPU SYSCTRL register offsets */ +#define RCPU_CAN_CLK_RST 0x4c +#define RCPU_CAN1_CLK_RST 0xF0 +#define RCPU_CAN2_CLK_RST 0xF4 +#define RCPU_CAN3_CLK_RST 0xF8 +#define RCPU_CAN4_CLK_RST 0xFC +#define RCPU_IRC_CLK_RST 0x48 +#define RCPU_IRC1_CLK_RST 0xEC +#define RCPU_GMAC_CLK_RST 0xE4 +#define RCPU_ESPI_CLK_RST 0xDC +#define RCPU_AUDIO_I2S0_SYS_CLK_CTRL 0x70 +#define RCPU_AUDIO_I2S1_SYS_CLK_CTRL 0x44 + +/* RCPU UARTCTRL register offsets */ +#define RCPU1_UART0_CLK_RST 0x00 +#define RCPU1_UART1_CLK_RST 0x04 +#define RCPU1_UART2_CLK_RST 0x08 +#define RCPU1_UART3_CLK_RST 0x0c +#define RCPU1_UART4_CLK_RST 0x10 +#define RCPU1_UART5_CLK_RST 0x14 + +/* RCPU I2SCTRL register offsets */ +#define RCPU2_AUDIO_I2S0_TX_RX_CLK_CTRL 0x60 +#define RCPU2_AUDIO_I2S1_TX_RX_CLK_CTRL 0x64 +#define RCPU2_AUDIO_I2S2_TX_RX_CLK_CTRL 0x68 +#define RCPU2_AUDIO_I2S3_TX_RX_CLK_CTRL 0x6C + +#define RCPU2_AUDIO_I2S2_SYS_CLK_CTRL 0x44 +#define RCPU2_AUDIO_I2S3_SYS_CLK_CTRL 0x54 + +/* RCPU SPICTRL register offsets */ +#define RCPU3_SSP0_CLK_RST 0x00 +#define RCPU3_SSP1_CLK_RST 0x04 +#define RCPU3_PWR_SSP_CLK_RST 0x08 + +/* RCPU I2CCTRL register offsets */ +#define RCPU4_I2C0_CLK_RST 0x00 +#define RCPU4_I2C1_CLK_RST 0x04 +#define RCPU4_PWR_I2C_CLK_RST 0x08 + +/* RPMU register offsets */ +#define RCPU5_AON_PER_CLK_RST_CTRL 0x2C +#define RCPU5_TIMER1_CLK_RST 0x4C +#define RCPU5_TIMER2_CLK_RST 0x70 +#define RCPU5_TIMER3_CLK_RST 0x78 +#define RCPU5_TIMER4_CLK_RST 0x7C +#define RCPU5_GPIO_AND_EDGE_CLK_RST 0x74 +#define RCPU5_RCPU_BUS_CLK_CTRL 0xC0 +#define RCPU5_RT24_CORE0_CLK_CTRL 0xC4 +#define RCPU5_RT24_CORE1_CLK_CTRL 0xC8 +#define RCPU5_RT24_CORE0_SW_RESET 0xCC +#define RCPU5_RT24_CORE1_SW_RESET 0xD0 + +/* RCPU PWMCTRL register offsets */ +#define RCPU6_PWM0_CLK_RST 0x00 +#define RCPU6_PWM1_CLK_RST 0x04 +#define RCPU6_PWM2_CLK_RST 0x08 +#define RCPU6_PWM3_CLK_RST 0x0c +#define RCPU6_PWM4_CLK_RST 0x10 +#define RCPU6_PWM5_CLK_RST 0x14 +#define RCPU6_PWM6_CLK_RST 0x18 +#define RCPU6_PWM7_CLK_RST 0x1c +#define RCPU6_PWM8_CLK_RST 0x20 +#define RCPU6_PWM9_CLK_RST 0x24 + +/* APBC2 SEC register offsets */ +#define APBC2_UART1_CLK_RST 0x00 +#define APBC2_SSP2_CLK_RST 0x04 +#define APBC2_TWSI3_CLK_RST 0x08 +#define APBC2_RTC_CLK_RST 0x0c +#define APBC2_TIMERS_CLK_RST 0x10 +#define APBC2_GPIO_CLK_RST 0x1c + +#endif /* __SOC_K3_SYSCON_H__ */ From f0bcd784e1b76bc3918433f2bd7e52f56f0dcf22 Mon Sep 17 00:00:00 2001 From: "Christophe Leroy (CS GROUP)" Date: Wed, 7 Jan 2026 17:59:09 +0100 Subject: [PATCH 090/171] soc: fsl: qe: Add an interrupt controller for QUICC Engine Ports The QUICC Engine provides interrupts for a few I/O ports. This is handled via a separate interrupt ID and managed via a triplet of dedicated registers hosted by the SoC. Implement an interrupt driver for it so that those IRQs can then be linked to the related GPIOs. Link: https://lore.kernel.org/r/63f19db21a91729d91b3df336a56a7eb4206e561.1767804922.git.chleroy@kernel.org Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/qe/Makefile | 2 +- drivers/soc/fsl/qe/qe_ports_ic.c | 142 +++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 drivers/soc/fsl/qe/qe_ports_ic.c diff --git a/drivers/soc/fsl/qe/Makefile b/drivers/soc/fsl/qe/Makefile index ec8506e13113..901a9c40d5eb 100644 --- a/drivers/soc/fsl/qe/Makefile +++ b/drivers/soc/fsl/qe/Makefile @@ -11,4 +11,4 @@ obj-$(CONFIG_UCC_SLOW) += ucc_slow.o obj-$(CONFIG_UCC_FAST) += ucc_fast.o obj-$(CONFIG_QE_TDM) += qe_tdm.o obj-$(CONFIG_QE_USB) += usb.o -obj-$(CONFIG_QE_GPIO) += gpio.o +obj-$(CONFIG_QE_GPIO) += gpio.o qe_ports_ic.o diff --git a/drivers/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c new file mode 100644 index 000000000000..61dd09fec6f6 --- /dev/null +++ b/drivers/soc/fsl/qe/qe_ports_ic.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * QUICC ENGINE I/O Ports Interrupt Controller + * + * Copyright (c) 2025 Christophe Leroy CS GROUP France (christophe.leroy@csgroup.eu) + */ + +#include +#include +#include + +/* QE IC registers offset */ +#define CEPIER 0x0c +#define CEPIMR 0x10 +#define CEPICR 0x14 + +struct qepic_data { + void __iomem *reg; + struct irq_domain *host; +}; + +static void qepic_mask(struct irq_data *d) +{ + struct qepic_data *data = irq_data_get_irq_chip_data(d); + + clrbits32(data->reg + CEPIMR, 1 << (31 - irqd_to_hwirq(d))); +} + +static void qepic_unmask(struct irq_data *d) +{ + struct qepic_data *data = irq_data_get_irq_chip_data(d); + + setbits32(data->reg + CEPIMR, 1 << (31 - irqd_to_hwirq(d))); +} + +static void qepic_end(struct irq_data *d) +{ + struct qepic_data *data = irq_data_get_irq_chip_data(d); + + out_be32(data->reg + CEPIER, 1 << (31 - irqd_to_hwirq(d))); +} + +static int qepic_set_type(struct irq_data *d, unsigned int flow_type) +{ + struct qepic_data *data = irq_data_get_irq_chip_data(d); + unsigned int vec = (unsigned int)irqd_to_hwirq(d); + + switch (flow_type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_FALLING: + setbits32(data->reg + CEPICR, 1 << (31 - vec)); + return 0; + case IRQ_TYPE_EDGE_BOTH: + case IRQ_TYPE_NONE: + clrbits32(data->reg + CEPICR, 1 << (31 - vec)); + return 0; + } + return -EINVAL; +} + +static struct irq_chip qepic = { + .name = "QEPIC", + .irq_mask = qepic_mask, + .irq_unmask = qepic_unmask, + .irq_eoi = qepic_end, + .irq_set_type = qepic_set_type, +}; + +static int qepic_get_irq(struct irq_desc *desc) +{ + struct qepic_data *data = irq_desc_get_handler_data(desc); + u32 event = in_be32(data->reg + CEPIER); + + if (!event) + return -1; + + return irq_find_mapping(data->host, 32 - ffs(event)); +} + +static void qepic_cascade(struct irq_desc *desc) +{ + generic_handle_irq(qepic_get_irq(desc)); +} + +static int qepic_host_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) +{ + irq_set_chip_data(virq, h->host_data); + irq_set_chip_and_handler(virq, &qepic, handle_fasteoi_irq); + return 0; +} + +static const struct irq_domain_ops qepic_host_ops = { + .map = qepic_host_map, +}; + +static int qepic_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct qepic_data *data; + int irq; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->reg)) + return PTR_ERR(data->reg); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + data->host = irq_domain_add_linear(dev->of_node, 32, &qepic_host_ops, data); + if (!data->host) + return -ENODEV; + + irq_set_handler_data(irq, data); + irq_set_chained_handler(irq, qepic_cascade); + + return 0; +} + +static const struct of_device_id qepic_match[] = { + { + .compatible = "fsl,mpc8323-qe-ports-ic", + }, + {}, +}; + +static struct platform_driver qepic_driver = { + .driver = { + .name = "qe_ports_ic", + .of_match_table = qepic_match, + }, + .probe = qepic_probe, +}; + +static int __init qepic_init(void) +{ + return platform_driver_register(&qepic_driver); +} +arch_initcall(qepic_init); From 0d069bb381839ba252ecca4031f7eb6f2fc72ab4 Mon Sep 17 00:00:00 2001 From: "Christophe Leroy (CS GROUP)" Date: Wed, 7 Jan 2026 17:59:10 +0100 Subject: [PATCH 091/171] dt-bindings: soc: fsl: qe: Add an interrupt controller for QUICC Engine Ports The QUICC Engine provides interrupts for a few I/O ports. This is handled via a separate interrupt ID and managed via a triplet of dedicated registers hosted by the SoC. Implement an interrupt driver for it so that those IRQs can then be linked to the related GPIOs. Acked-by: Conor Dooley Link: https://lore.kernel.org/r/7708243d6cca21004de8b3da87369c06dbee3848.1767804922.git.chleroy@kernel.org Signed-off-by: Christophe Leroy (CS GROUP) [moved from bindings/soc/fsl/cpm_qe/ to bindings/interrupt-controller/ while applying] --- .../interrupt-controller/fsl,qe-ports-ic.yaml | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/fsl,qe-ports-ic.yaml diff --git a/Documentation/devicetree/bindings/interrupt-controller/fsl,qe-ports-ic.yaml b/Documentation/devicetree/bindings/interrupt-controller/fsl,qe-ports-ic.yaml new file mode 100644 index 000000000000..2b8e7b9c6d7a --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/fsl,qe-ports-ic.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/fsl,qe-ports-ic.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale QUICC Engine I/O Ports Interrupt Controller + +maintainers: + - Christophe Leroy (CS GROUP) + +properties: + compatible: + enum: + - fsl,mpc8323-qe-ports-ic + + reg: + maxItems: 1 + + interrupt-controller: true + + '#address-cells': + const: 0 + + '#interrupt-cells': + const: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - interrupt-controller + - '#address-cells' + - '#interrupt-cells' + - interrupts + +additionalProperties: false + +examples: + - | + interrupt-controller@c00 { + compatible = "fsl,mpc8323-qe-ports-ic"; + reg = <0xc00 0x18>; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + interrupts = <74 0x8>; + interrupt-parent = <&ipic>; + }; From 1f58ad77a8b49638ad2b95b364cddc12f20cd011 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 15 Dec 2025 09:28:00 +0100 Subject: [PATCH 092/171] cpuidle: zynq: Switch Michal Simek's email to new one @xilinx.com is still working but better to switch to new amd.com after AMD/Xilinx acquisition. Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/ebfbf945d90b0efff3ce0dc17fb7f1f0db5b6628.1765787278.git.michal.simek@amd.com --- drivers/cpuidle/cpuidle-zynq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpuidle/cpuidle-zynq.c b/drivers/cpuidle/cpuidle-zynq.c index a79610e723b3..89448ae4845c 100644 --- a/drivers/cpuidle/cpuidle-zynq.c +++ b/drivers/cpuidle/cpuidle-zynq.c @@ -11,7 +11,7 @@ * #1 wait-for-interrupt * #2 wait-for-interrupt and RAM self refresh * - * Maintainer: Michal Simek + * Maintainer: Michal Simek */ #include From 31fce0d2b13e6a4a12c9ba016e961418f8c82e34 Mon Sep 17 00:00:00 2001 From: Nick Xie Date: Tue, 13 Jan 2026 09:25:27 +0800 Subject: [PATCH 093/171] soc: amlogic: meson-gx-socinfo: add new SoC id for S905Y4 Add new definition for Amlogic S4 S905Y4. Signed-off-by: Nick Xie Reviewed-by: Neil Armstrong Link: https://patch.msgid.link/20260113012527.40725-1-nick@khadas.com Signed-off-by: Neil Armstrong --- drivers/soc/amlogic/meson-gx-socinfo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c index 2a54ca43cd13..dcb75baaff24 100644 --- a/drivers/soc/amlogic/meson-gx-socinfo.c +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -85,6 +85,7 @@ static const struct meson_gx_package_id { { "S905D3", 0x2b, 0x30, 0x3f }, { "A113L", 0x2c, 0x0, 0xf8 }, { "S805X2", 0x37, 0x2, 0xf }, + { "S905Y4", 0x37, 0x3, 0xf }, { "C308L", 0x3d, 0x1, 0xf }, { "A311D2", 0x36, 0x1, 0xf }, { "A113X2", 0x3c, 0x1, 0xf }, From ebb0bbef88b3276c01e2d42b71d67c26b4eb938a Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:49 +0530 Subject: [PATCH 094/171] dt-bindings: remoteproc: qcom,pas: Add iommus property On most Qualcomm platforms, the Qualcomm Hypervisor Execution Environment (QHEE) is either used as a standalone hypervisor or co-exists as a module with the Gunyah hypervisor. When QHEE is present, it configures firmware streams for remote processors. If QHEE is not available, the operating system must perform these configurations instead and for that remote processor firmware stream should be presented to the operating system. To support this, add the iommus property as an optional property for PAS-supported devices. Acked-by: Rob Herring (Arm) Reviewed-by: Bryan O'Donoghue Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-1-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/remoteproc/qcom,pas-common.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml index 63a82e7a8bf8..68c17bf18987 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml @@ -44,6 +44,9 @@ properties: - const: stop-ack - const: shutdown-ack + iommus: + maxItems: 1 + power-domains: minItems: 1 maxItems: 3 From 25906ae9b28af23de509cb290a49ddd314f6c432 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:50 +0530 Subject: [PATCH 095/171] firmware: qcom_scm: Remove redundant piece of code The qcom_scm_pas_metadata_release() function already frees the allocated memory and sets ctx->ptr to NULL. Resetting ctx->phys and ctx->size to zero is unnecessary because the context is expected to be discarded after release. Lets removes redundant assignments. Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-2-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 1a6f85e463e0..6461408c58a3 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -650,8 +650,6 @@ void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx) dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys); ctx->ptr = NULL; - ctx->phys = 0; - ctx->size = 0; } EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release); From 69054348cc1c2d87acad90aec5e6e0d191012aff Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:51 +0530 Subject: [PATCH 096/171] firmware: qcom_scm: Rename peripheral as pas_id Peripheral and pas_id refers to unique id for a subsystem and used only when peripheral authentication service from secure world is utilized. Lets rename peripheral to pas_id to reflect closer to its meaning. Reviewed-by: Bryan O'Donoghue Reviewed-by: Konrad Dybcio Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-3-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 30 +++++++++++++------------- include/linux/firmware/qcom/qcom_scm.h | 10 ++++----- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 6461408c58a3..1e1057638e98 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -562,7 +562,7 @@ static void qcom_scm_set_download_mode(u32 dload_mode) * qcom_scm_pas_init_image() - Initialize peripheral authentication service * state machine for a given peripheral, using the * metadata - * @peripheral: peripheral id + * @pas_id: peripheral authentication service id * @metadata: pointer to memory containing ELF header, program header table * and optional blob of data used for authenticating the metadata * and the rest of the firmware @@ -575,7 +575,7 @@ static void qcom_scm_set_download_mode(u32 dload_mode) * track the metadata allocation, this needs to be released by invoking * qcom_scm_pas_metadata_release() by the caller. */ -int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size, +int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, struct qcom_scm_pas_metadata *ctx) { dma_addr_t mdata_phys; @@ -585,7 +585,7 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size, .svc = QCOM_SCM_SVC_PIL, .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE, .arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW), - .args[0] = peripheral, + .args[0] = pas_id, .owner = ARM_SMCCC_OWNER_SIP, }; struct qcom_scm_res res; @@ -656,20 +656,20 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release); /** * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral * for firmware loading - * @peripheral: peripheral id + * @pas_id: peripheral authentication service id * @addr: start address of memory area to prepare * @size: size of the memory area to prepare * * Returns 0 on success. */ -int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size) +int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size) { int ret; struct qcom_scm_desc desc = { .svc = QCOM_SCM_SVC_PIL, .cmd = QCOM_SCM_PIL_PAS_MEM_SETUP, .arginfo = QCOM_SCM_ARGS(3), - .args[0] = peripheral, + .args[0] = pas_id, .args[1] = addr, .args[2] = size, .owner = ARM_SMCCC_OWNER_SIP, @@ -697,18 +697,18 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup); /** * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware * and reset the remote processor - * @peripheral: peripheral id + * @pas_id: peripheral authentication service id * * Return 0 on success. */ -int qcom_scm_pas_auth_and_reset(u32 peripheral) +int qcom_scm_pas_auth_and_reset(u32 pas_id) { int ret; struct qcom_scm_desc desc = { .svc = QCOM_SCM_SVC_PIL, .cmd = QCOM_SCM_PIL_PAS_AUTH_AND_RESET, .arginfo = QCOM_SCM_ARGS(1), - .args[0] = peripheral, + .args[0] = pas_id, .owner = ARM_SMCCC_OWNER_SIP, }; struct qcom_scm_res res; @@ -733,18 +733,18 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset); /** * qcom_scm_pas_shutdown() - Shut down the remote processor - * @peripheral: peripheral id + * @pas_id: peripheral authentication service id * * Returns 0 on success. */ -int qcom_scm_pas_shutdown(u32 peripheral) +int qcom_scm_pas_shutdown(u32 pas_id) { int ret; struct qcom_scm_desc desc = { .svc = QCOM_SCM_SVC_PIL, .cmd = QCOM_SCM_PIL_PAS_SHUTDOWN, .arginfo = QCOM_SCM_ARGS(1), - .args[0] = peripheral, + .args[0] = pas_id, .owner = ARM_SMCCC_OWNER_SIP, }; struct qcom_scm_res res; @@ -770,18 +770,18 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown); /** * qcom_scm_pas_supported() - Check if the peripheral authentication service is * available for the given peripherial - * @peripheral: peripheral id + * @pas_id: peripheral authentication service id * * Returns true if PAS is supported for this peripheral, otherwise false. */ -bool qcom_scm_pas_supported(u32 peripheral) +bool qcom_scm_pas_supported(u32 pas_id) { int ret; struct qcom_scm_desc desc = { .svc = QCOM_SCM_SVC_PIL, .cmd = QCOM_SCM_PIL_PAS_IS_SUPPORTED, .arginfo = QCOM_SCM_ARGS(1), - .args[0] = peripheral, + .args[0] = pas_id, .owner = ARM_SMCCC_OWNER_SIP, }; struct qcom_scm_res res; diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h index a55ca771286b..a13f703b16cd 100644 --- a/include/linux/firmware/qcom/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h @@ -72,13 +72,13 @@ struct qcom_scm_pas_metadata { ssize_t size; }; -int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size, +int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, struct qcom_scm_pas_metadata *ctx); void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx); -int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size); -int qcom_scm_pas_auth_and_reset(u32 peripheral); -int qcom_scm_pas_shutdown(u32 peripheral); -bool qcom_scm_pas_supported(u32 peripheral); +int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size); +int qcom_scm_pas_auth_and_reset(u32 pas_id); +int qcom_scm_pas_shutdown(u32 pas_id); +bool qcom_scm_pas_supported(u32 pas_id); int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val); int qcom_scm_io_writel(phys_addr_t addr, unsigned int val); From ccb7bde5f7cc794dee0cd66fd451cb0e0715712d Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:52 +0530 Subject: [PATCH 097/171] firmware: qcom_scm: Introduce PAS context allocator helper function When the Peripheral Authentication Service (PAS) method runs on a SoC where Linux operates at EL2 (i.e., without the Gunyah hypervisor), the reset sequences are handled by TrustZone. In such cases, Linux must perform additional steps before invoking PAS SMC calls, such as creating a SHM bridge. Therefore, PAS SMC calls require awareness and handling of these additional steps when Linux runs at EL2. To support this, there is a need for a data structure that can be initialized prior to invoking any SMC or MDT functions. This structure allows those functions to determine whether they are operating in the presence or absence of the Gunyah hypervisor and behave accordingly. Currently, remoteproc and non-remoteproc subsystems use different variants of the MDT loader helper API, primarily due to differences in metadata context handling. Remoteproc subsystems retain the metadata context until authentication and reset are completed, while non-remoteproc subsystems (e.g., video, graphics, IPA, etc.) do not retain the metadata context and can free it within the qcom_scm_pas_init() call by passing a NULL context parameter and due to these differences, it is not possible to extend metadata context handling to support remoteproc and non remoteproc subsystem use PAS operations, when Linux operates at EL2. Add PAS context data structure allocator helper function. Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-4-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 34 ++++++++++++++++++++++++++ include/linux/firmware/qcom/qcom_scm.h | 14 +++++++++++ 2 files changed, 48 insertions(+) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 1e1057638e98..5162c02f5f88 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -558,6 +558,40 @@ static void qcom_scm_set_download_mode(u32 dload_mode) dev_err(__scm->dev, "failed to set download mode: %d\n", ret); } +/** + * devm_qcom_scm_pas_context_alloc() - Allocate peripheral authentication service + * context for a given peripheral + * + * PAS context is device-resource managed, so the caller does not need + * to worry about freeing the context memory. + * + * @dev: PAS firmware device + * @pas_id: peripheral authentication service id + * @mem_phys: Subsystem reserve memory start address + * @mem_size: Subsystem reserve memory size + * + * Returns: The new PAS context, or ERR_PTR() on failure. + */ +struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev, + u32 pas_id, + phys_addr_t mem_phys, + size_t mem_size) +{ + struct qcom_scm_pas_context *ctx; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->dev = dev; + ctx->pas_id = pas_id; + ctx->mem_phys = mem_phys; + ctx->mem_size = mem_size; + + return ctx; +} +EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc); + /** * qcom_scm_pas_init_image() - Initialize peripheral authentication service * state machine for a given peripheral, using the diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h index a13f703b16cd..5045f8fe876d 100644 --- a/include/linux/firmware/qcom/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h @@ -72,6 +72,20 @@ struct qcom_scm_pas_metadata { ssize_t size; }; +struct qcom_scm_pas_context { + struct device *dev; + u32 pas_id; + phys_addr_t mem_phys; + size_t mem_size; + void *ptr; + dma_addr_t phys; + ssize_t size; +}; + +struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev, + u32 pas_id, + phys_addr_t mem_phys, + size_t mem_size); int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, struct qcom_scm_pas_metadata *ctx); void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx); From b13d8baf56016e7eec29395b52d18b91df081d48 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:53 +0530 Subject: [PATCH 098/171] remoteproc: pas: Replace metadata context with PAS context structure As a superset of the existing metadata context, the PAS context structure enables both remoteproc and non-remoteproc subsystems to better support scenarios where the SoC runs with or without the Gunyah hypervisor. To reflect this, relevant SCM and metadata functions are updated to incorporate PAS context awareness and remove metadata context data structure completely. Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-5-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 8 +++--- drivers/remoteproc/qcom_q6v5_pas.c | 38 ++++++++++++++++++-------- drivers/soc/qcom/mdt_loader.c | 4 +-- include/linux/firmware/qcom/qcom_scm.h | 10 ++----- include/linux/soc/qcom/mdt_loader.h | 6 ++-- 5 files changed, 38 insertions(+), 28 deletions(-) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 5162c02f5f88..4edd475ef848 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -601,7 +601,7 @@ EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc); * and optional blob of data used for authenticating the metadata * and the rest of the firmware * @size: size of the metadata - * @ctx: optional metadata context + * @ctx: optional pas context * * Return: 0 on success. * @@ -610,7 +610,7 @@ EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc); * qcom_scm_pas_metadata_release() by the caller. */ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, - struct qcom_scm_pas_metadata *ctx) + struct qcom_scm_pas_context *ctx) { dma_addr_t mdata_phys; void *mdata_buf; @@ -674,9 +674,9 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image); /** * qcom_scm_pas_metadata_release() - release metadata context - * @ctx: metadata context + * @ctx: pas context */ -void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx) +void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx) { if (!ctx->ptr) return; diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 52680ac99589..bfcb65aed008 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -117,8 +117,8 @@ struct qcom_pas { struct qcom_rproc_ssr ssr_subdev; struct qcom_sysmon *sysmon; - struct qcom_scm_pas_metadata pas_metadata; - struct qcom_scm_pas_metadata dtb_pas_metadata; + struct qcom_scm_pas_context *pas_ctx; + struct qcom_scm_pas_context *dtb_pas_ctx; }; static void qcom_pas_segment_dump(struct rproc *rproc, @@ -211,9 +211,9 @@ static int qcom_pas_unprepare(struct rproc *rproc) * auth_and_reset() was successful, but in other cases clean it up * here. */ - qcom_scm_pas_metadata_release(&pas->pas_metadata); + qcom_scm_pas_metadata_release(pas->pas_ctx); if (pas->dtb_pas_id) - qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata); + qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); return 0; } @@ -241,7 +241,7 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) ret = qcom_mdt_pas_init(pas->dev, pas->dtb_firmware, pas->dtb_firmware_name, pas->dtb_pas_id, pas->dtb_mem_phys, - &pas->dtb_pas_metadata); + pas->dtb_pas_ctx); if (ret) goto release_dtb_firmware; @@ -255,7 +255,7 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) return 0; release_dtb_metadata: - qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata); + qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); release_dtb_firmware: release_firmware(pas->dtb_firmware); @@ -306,7 +306,7 @@ static int qcom_pas_start(struct rproc *rproc) } ret = qcom_mdt_pas_init(pas->dev, pas->firmware, rproc->firmware, pas->pas_id, - pas->mem_phys, &pas->pas_metadata); + pas->mem_phys, pas->pas_ctx); if (ret) goto disable_px_supply; @@ -332,9 +332,9 @@ static int qcom_pas_start(struct rproc *rproc) goto release_pas_metadata; } - qcom_scm_pas_metadata_release(&pas->pas_metadata); + qcom_scm_pas_metadata_release(pas->pas_ctx); if (pas->dtb_pas_id) - qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata); + qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); /* firmware is used to pass reference from qcom_pas_start(), drop it now */ pas->firmware = NULL; @@ -342,9 +342,9 @@ static int qcom_pas_start(struct rproc *rproc) return 0; release_pas_metadata: - qcom_scm_pas_metadata_release(&pas->pas_metadata); + qcom_scm_pas_metadata_release(pas->pas_ctx); if (pas->dtb_pas_id) - qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata); + qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); disable_px_supply: if (pas->px_supply) regulator_disable(pas->px_supply); @@ -760,6 +760,22 @@ static int qcom_pas_probe(struct platform_device *pdev) } qcom_add_ssr_subdev(rproc, &pas->ssr_subdev, desc->ssr_name); + + pas->pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->pas_id, + pas->mem_phys, pas->mem_size); + if (IS_ERR(pas->pas_ctx)) { + ret = PTR_ERR(pas->pas_ctx); + goto remove_ssr_sysmon; + } + + pas->dtb_pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->dtb_pas_id, + pas->dtb_mem_phys, + pas->dtb_mem_size); + if (IS_ERR(pas->dtb_pas_ctx)) { + ret = PTR_ERR(pas->dtb_pas_ctx); + goto remove_ssr_sysmon; + } + ret = rproc_add(rproc); if (ret) goto remove_ssr_sysmon; diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index c239107cb930..b125140100db 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -234,13 +234,13 @@ EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata); * @fw_name: name of the firmware, for construction of segment file names * @pas_id: PAS identifier * @mem_phys: physical address of allocated memory region - * @ctx: PAS metadata context, to be released by caller + * @ctx: PAS context, ctx->metadata to be released by caller * * Returns 0 on success, negative errno otherwise. */ int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_metadata *ctx) + struct qcom_scm_pas_context *ctx) { const struct elf32_phdr *phdrs; const struct elf32_phdr *phdr; diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h index 5045f8fe876d..ad69b51fe6fc 100644 --- a/include/linux/firmware/qcom/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h @@ -66,12 +66,6 @@ int qcom_scm_set_warm_boot_addr(void *entry); void qcom_scm_cpu_power_down(u32 flags); int qcom_scm_set_remote_state(u32 state, u32 id); -struct qcom_scm_pas_metadata { - void *ptr; - dma_addr_t phys; - ssize_t size; -}; - struct qcom_scm_pas_context { struct device *dev; u32 pas_id; @@ -87,8 +81,8 @@ struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev, phys_addr_t mem_phys, size_t mem_size); int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, - struct qcom_scm_pas_metadata *ctx); -void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx); + struct qcom_scm_pas_context *ctx); +void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx); int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size); int qcom_scm_pas_auth_and_reset(u32 pas_id); int qcom_scm_pas_shutdown(u32 pas_id); diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h index 8ea8230579a2..07c278841816 100644 --- a/include/linux/soc/qcom/mdt_loader.h +++ b/include/linux/soc/qcom/mdt_loader.h @@ -10,14 +10,14 @@ struct device; struct firmware; -struct qcom_scm_pas_metadata; +struct qcom_scm_pas_context; #if IS_ENABLED(CONFIG_QCOM_MDT_LOADER) ssize_t qcom_mdt_get_size(const struct firmware *fw); int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_metadata *pas_metadata_ctx); + struct qcom_scm_pas_context *pas_ctx); int qcom_mdt_load(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, void *mem_region, phys_addr_t mem_phys, size_t mem_size, @@ -39,7 +39,7 @@ static inline ssize_t qcom_mdt_get_size(const struct firmware *fw) static inline int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_metadata *pas_metadata_ctx) + struct qcom_scm_pas_context *pas_ctx) { return -ENODEV; } From 8a4fcffde6c860c4e9164cf3530c9d97972781dc Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:54 +0530 Subject: [PATCH 099/171] soc: qcom: mdtloader: Add PAS context aware qcom_mdt_pas_load() function Introduce a new PAS context-aware function, qcom_mdt_pas_load(), for remote processor drivers. This function utilizes the PAS context pointer returned from qcom_scm_pas_ctx_init() to perform firmware metadata verification and memory setup via SMC calls. The qcom_mdt_pas_load() and qcom_mdt_load() functions are largely similar, but the former is designed for clients using the PAS context-based data structure. Over time, all users of qcom_mdt_load() can be migrated to use qcom_mdt_pas_load() for consistency and improved abstraction. As the remoteproc PAS driver (qcom_q6v5_pas) has already adopted the PAS context-based approach, update it to use qcom_mdt_pas_load(). Reviewed-by: Konrad Dybcio Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-6-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_pas.c | 24 +++++----------------- drivers/soc/qcom/mdt_loader.c | 31 +++++++++++++++++++++++++++++ include/linux/soc/qcom/mdt_loader.h | 10 ++++++++++ 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index bfcb65aed008..f746d2f39a1d 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -239,15 +239,9 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) return ret; } - ret = qcom_mdt_pas_init(pas->dev, pas->dtb_firmware, pas->dtb_firmware_name, - pas->dtb_pas_id, pas->dtb_mem_phys, - pas->dtb_pas_ctx); - if (ret) - goto release_dtb_firmware; - - ret = qcom_mdt_load_no_init(pas->dev, pas->dtb_firmware, pas->dtb_firmware_name, - pas->dtb_mem_region, pas->dtb_mem_phys, - pas->dtb_mem_size, &pas->dtb_mem_reloc); + ret = qcom_mdt_pas_load(pas->dtb_pas_ctx, pas->dtb_firmware, + pas->dtb_firmware_name, pas->dtb_mem_region, + &pas->dtb_mem_reloc); if (ret) goto release_dtb_metadata; } @@ -256,8 +250,6 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) release_dtb_metadata: qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); - -release_dtb_firmware: release_firmware(pas->dtb_firmware); return ret; @@ -305,14 +297,8 @@ static int qcom_pas_start(struct rproc *rproc) } } - ret = qcom_mdt_pas_init(pas->dev, pas->firmware, rproc->firmware, pas->pas_id, - pas->mem_phys, pas->pas_ctx); - if (ret) - goto disable_px_supply; - - ret = qcom_mdt_load_no_init(pas->dev, pas->firmware, rproc->firmware, - pas->mem_region, pas->mem_phys, pas->mem_size, - &pas->mem_reloc); + ret = qcom_mdt_pas_load(pas->pas_ctx, pas->firmware, rproc->firmware, + pas->mem_region, &pas->mem_reloc); if (ret) goto release_pas_metadata; diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index b125140100db..50c6a3c6b2a3 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -478,5 +478,36 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, } EXPORT_SYMBOL_GPL(qcom_mdt_load); +/** + * qcom_mdt_pas_load - Loads and authenticates the metadata of the firmware + * (typically contained in the .mdt file), followed by loading the actual + * firmware segments (e.g., .bXX files). Authentication of the segments done + * by a separate call. + * + * The PAS context must be initialized using qcom_scm_pas_context_init() + * prior to invoking this function. + * + * @ctx: Pointer to the PAS (Peripheral Authentication Service) context + * @fw: Firmware object representing the .mdt file + * @firmware: Name of the firmware used to construct segment file names + * @mem_region: Memory region allocated for loading the firmware + * @reloc_base: Physical address adjusted after relocation + * + * Return: 0 on success or a negative error code on failure. + */ +int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *fw, + const char *firmware, void *mem_region, phys_addr_t *reloc_base) +{ + int ret; + + ret = qcom_mdt_pas_init(ctx->dev, fw, firmware, ctx->pas_id, ctx->mem_phys, ctx); + if (ret) + return ret; + + return qcom_mdt_load_no_init(ctx->dev, fw, firmware, mem_region, ctx->mem_phys, + ctx->mem_size, reloc_base); +} +EXPORT_SYMBOL_GPL(qcom_mdt_pas_load); + MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format"); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h index 07c278841816..7d57746fbbfa 100644 --- a/include/linux/soc/qcom/mdt_loader.h +++ b/include/linux/soc/qcom/mdt_loader.h @@ -23,6 +23,9 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, phys_addr_t mem_phys, size_t mem_size, phys_addr_t *reloc_base); +int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *fw, + const char *firmware, void *mem_region, phys_addr_t *reloc_base); + int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, const char *fw_name, void *mem_region, phys_addr_t mem_phys, size_t mem_size, @@ -52,6 +55,13 @@ static inline int qcom_mdt_load(struct device *dev, const struct firmware *fw, return -ENODEV; } +static inline int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, + const struct firmware *fw, const char *firmware, + void *mem_region, phys_addr_t *reloc_base) +{ + return -ENODEV; +} + static inline int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, const char *fw_name, void *mem_region, From 928dbaaa9d89363d79e309ec00c5527ddfbe47c8 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:55 +0530 Subject: [PATCH 100/171] soc: qcom: mdtloader: Remove qcom_mdt_pas_init() from exported symbols qcom_mdt_pas_init() was previously used only by the remoteproc driver (drivers/remoteproc/qcom_q6v5_pas.c). Since that driver has now transitioned to using PAS context-based qcom_mdt_pas_load() function, making qcom_mdt_pas_init() obsolete for external use. Removes qcom_mdt_pas_init() from the list of exported symbols and make it static to limit its scope to internal use within mdtloader. Reviewed-by: Konrad Dybcio Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-7-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/mdt_loader.c | 22 +++++----------------- include/linux/soc/qcom/mdt_loader.h | 10 ---------- 2 files changed, 5 insertions(+), 27 deletions(-) diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index 50c6a3c6b2a3..c004d444d698 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -227,20 +227,9 @@ void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len, } EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata); -/** - * qcom_mdt_pas_init() - initialize PAS region for firmware loading - * @dev: device handle to associate resources with - * @fw: firmware object for the mdt file - * @fw_name: name of the firmware, for construction of segment file names - * @pas_id: PAS identifier - * @mem_phys: physical address of allocated memory region - * @ctx: PAS context, ctx->metadata to be released by caller - * - * Returns 0 on success, negative errno otherwise. - */ -int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, - const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_context *ctx) +static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, + const char *fw_name, int pas_id, phys_addr_t mem_phys, + struct qcom_scm_pas_context *ctx) { const struct elf32_phdr *phdrs; const struct elf32_phdr *phdr; @@ -302,7 +291,6 @@ int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, out: return ret; } -EXPORT_SYMBOL_GPL(qcom_mdt_pas_init); static bool qcom_mdt_bins_are_split(const struct firmware *fw) { @@ -469,7 +457,7 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, { int ret; - ret = qcom_mdt_pas_init(dev, fw, fw_name, pas_id, mem_phys, NULL); + ret = __qcom_mdt_pas_init(dev, fw, fw_name, pas_id, mem_phys, NULL); if (ret) return ret; @@ -500,7 +488,7 @@ int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *f { int ret; - ret = qcom_mdt_pas_init(ctx->dev, fw, firmware, ctx->pas_id, ctx->mem_phys, ctx); + ret = __qcom_mdt_pas_init(ctx->dev, fw, firmware, ctx->pas_id, ctx->mem_phys, ctx); if (ret) return ret; diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h index 7d57746fbbfa..82372e0db0a1 100644 --- a/include/linux/soc/qcom/mdt_loader.h +++ b/include/linux/soc/qcom/mdt_loader.h @@ -15,9 +15,6 @@ struct qcom_scm_pas_context; #if IS_ENABLED(CONFIG_QCOM_MDT_LOADER) ssize_t qcom_mdt_get_size(const struct firmware *fw); -int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, - const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_context *pas_ctx); int qcom_mdt_load(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, void *mem_region, phys_addr_t mem_phys, size_t mem_size, @@ -40,13 +37,6 @@ static inline ssize_t qcom_mdt_get_size(const struct firmware *fw) return -ENODEV; } -static inline int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, - const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_context *pas_ctx) -{ - return -ENODEV; -} - static inline int qcom_mdt_load(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, void *mem_region, phys_addr_t mem_phys, From 4a7d6a78fbc6527fb1b61944aab00d9cdd1d4f01 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:56 +0530 Subject: [PATCH 101/171] firmware: qcom_scm: Add a prep version of auth_and_reset function For memory passed to TrustZone (TZ), it must either be part of a pool registered with TZ or explicitly registered via SHMbridge SMC calls. When Gunyah hypervisor is present, PAS SMC calls from Linux running at EL1 are trapped by Gunyah running @ EL2, which handles SHMbridge creation for both metadata and remoteproc carveout memory before invoking the calls to TZ. On SoCs running with a non-Gunyah-based hypervisor, Linux must take responsibility for creating the SHM bridge before invoking PAS SMC calls. For the auth_and_reset() call, the remoteproc carveout memory must first be registered with TZ via a SHMbridge SMC call and once authentication and reset are complete, the SHMbridge memory can be deregistered. Introduce qcom_scm_pas_prepare_and_auth_reset(), which sets up the SHM bridge over the remoteproc carveout memory when Linux operates at EL2. This behavior is indicated by a new field added to the PAS context data structure. The function then invokes the auth_and_reset SMC call. Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-8-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 47 ++++++++++++++++++++++++++ include/linux/firmware/qcom/qcom_scm.h | 2 ++ 2 files changed, 49 insertions(+) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 4edd475ef848..d3783166fea1 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -765,6 +765,53 @@ disable_clk: } EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset); +/** + * qcom_scm_pas_prepare_and_auth_reset() - Prepare, authenticate, and reset the + * remote processor + * + * @ctx: Context saved during call to qcom_scm_pas_context_init() + * + * This function performs the necessary steps to prepare a PAS subsystem, + * authenticate it using the provided metadata, and initiate a reset sequence. + * + * It should be used when Linux is in control setting up the IOMMU hardware + * for remote subsystem during secure firmware loading processes. The preparation + * step sets up a shmbridge over the firmware memory before TrustZone accesses the + * firmware memory region for authentication. The authentication step verifies + * the integrity and authenticity of the firmware or configuration using secure + * metadata. Finally, the reset step ensures the subsystem starts in a clean and + * sane state. + * + * Return: 0 on success, negative errno on failure. + */ +int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx) +{ + u64 handle; + int ret; + + /* + * When Linux running @ EL1, Gunyah hypervisor running @ EL2 traps the + * auth_and_reset call and create an shmbridge on the remote subsystem + * memory region and then invokes a call to TrustZone to authenticate. + */ + if (!ctx->use_tzmem) + return qcom_scm_pas_auth_and_reset(ctx->pas_id); + + /* + * When Linux runs @ EL2 Linux must create the shmbridge itself and then + * subsequently call TrustZone for authenticate and reset. + */ + ret = qcom_tzmem_shm_bridge_create(ctx->mem_phys, ctx->mem_size, &handle); + if (ret) + return ret; + + ret = qcom_scm_pas_auth_and_reset(ctx->pas_id); + qcom_tzmem_shm_bridge_delete(handle); + + return ret; +} +EXPORT_SYMBOL_GPL(qcom_scm_pas_prepare_and_auth_reset); + /** * qcom_scm_pas_shutdown() - Shut down the remote processor * @pas_id: peripheral authentication service id diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h index ad69b51fe6fc..d6d83888bb75 100644 --- a/include/linux/firmware/qcom/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h @@ -74,6 +74,7 @@ struct qcom_scm_pas_context { void *ptr; dma_addr_t phys; ssize_t size; + bool use_tzmem; }; struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev, @@ -87,6 +88,7 @@ int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size); int qcom_scm_pas_auth_and_reset(u32 pas_id); int qcom_scm_pas_shutdown(u32 pas_id); bool qcom_scm_pas_supported(u32 pas_id); +int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx); int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val); int qcom_scm_io_writel(phys_addr_t addr, unsigned int val); From 223a87168030b422dda44c21319122f6328b5867 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:57 +0530 Subject: [PATCH 102/171] firmware: qcom_scm: Refactor qcom_scm_pas_init_image() Refactor qcom_scm_pas_init_image() by moving the memory allocation, copy, and free operations to a higher-level function, and isolate the actual SMC call in a separate function. The main intention is to allow flexibility for different allocators and to respect any constraints that the allocator API may impose before invoking the actual SCM function. Reviewed-by: Bryan O'Donoghue Reviewed-by: Konrad Dybcio Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-9-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 58 ++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index d3783166fea1..bc3b8dc7d3e4 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -592,6 +592,37 @@ struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev, } EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc); +static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys, + struct qcom_scm_res *res) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_PIL, + .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE, + .arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW), + .args[0] = pas_id, + .owner = ARM_SMCCC_OWNER_SIP, + }; + int ret; + + ret = qcom_scm_clk_enable(); + if (ret) + return ret; + + ret = qcom_scm_bw_enable(); + if (ret) + goto disable_clk; + + desc.args[1] = mdata_phys; + + ret = qcom_scm_call(__scm->dev, &desc, res); + qcom_scm_bw_disable(); + +disable_clk: + qcom_scm_clk_disable(); + + return ret; +} + /** * qcom_scm_pas_init_image() - Initialize peripheral authentication service * state machine for a given peripheral, using the @@ -612,17 +643,10 @@ EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc); int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, struct qcom_scm_pas_context *ctx) { + struct qcom_scm_res res; dma_addr_t mdata_phys; void *mdata_buf; int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, - .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE, - .arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW), - .args[0] = pas_id, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; /* * During the scm call memory protection will be enabled for the meta @@ -643,23 +667,7 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, memcpy(mdata_buf, metadata, size); - ret = qcom_scm_clk_enable(); - if (ret) - goto out; - - ret = qcom_scm_bw_enable(); - if (ret) - goto disable_clk; - - desc.args[1] = mdata_phys; - - ret = qcom_scm_call(__scm->dev, &desc, &res); - qcom_scm_bw_disable(); - -disable_clk: - qcom_scm_clk_disable(); - -out: + ret = __qcom_scm_pas_init_image(pas_id, mdata_phys, &res); if (ret < 0 || !ctx) { dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys); } else if (ctx) { From b019925838bca1599fa443b34c8ed5876f14f144 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:58 +0530 Subject: [PATCH 103/171] firmware: qcom_scm: Add SHM bridge handling for PAS when running without QHEE On SoCs running with a non-Gunyah-based hypervisor, Linux must take responsibility for creating the SHM bridge both for metadata (before calling qcom_scm_pas_init_image()) and for remoteproc memory (before calling qcom_scm_pas_auth_and_reset()). We have taken care the things required for qcom_scm_pas_auth_and_reset(). Lets put these awareness of above conditions into qcom_scm_pas_init_image() and qcom_scm_pas_metadata_release(). Reviewed-by: Konrad Dybcio Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-10-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index bc3b8dc7d3e4..4ce892d8fb25 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -623,6 +623,30 @@ disable_clk: return ret; } +static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx, + const void *metadata, size_t size) +{ + struct qcom_scm_res res; + phys_addr_t mdata_phys; + void *mdata_buf; + int ret; + + mdata_buf = qcom_tzmem_alloc(__scm->mempool, size, GFP_KERNEL); + if (!mdata_buf) + return -ENOMEM; + + memcpy(mdata_buf, metadata, size); + mdata_phys = qcom_tzmem_to_phys(mdata_buf); + + ret = __qcom_scm_pas_init_image(ctx->pas_id, mdata_phys, &res); + if (ret < 0) + qcom_tzmem_free(mdata_buf); + else + ctx->ptr = mdata_buf; + + return ret ? : res.result[0]; +} + /** * qcom_scm_pas_init_image() - Initialize peripheral authentication service * state machine for a given peripheral, using the @@ -648,6 +672,9 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, void *mdata_buf; int ret; + if (ctx && ctx->use_tzmem) + return qcom_scm_pas_prep_and_init_image(ctx, metadata, size); + /* * During the scm call memory protection will be enabled for the meta * data blob, so make sure it's physically contiguous, 4K aligned and @@ -689,7 +716,10 @@ void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx) if (!ctx->ptr) return; - dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys); + if (ctx->use_tzmem) + qcom_tzmem_free(ctx->ptr); + else + dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys); ctx->ptr = NULL; } From 8b9d2050cfa0c22c05622df103e366933fc045ed Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:59 +0530 Subject: [PATCH 104/171] firmware: qcom_scm: Add qcom_scm_pas_get_rsc_table() to get resource table Qualcomm remote processor may rely on Static and Dynamic resources for it to be functional. Static resources are fixed like for example, memory-mapped addresses required by the subsystem and dynamic resources, such as shared memory in DDR etc., are determined at runtime during the boot process. For most of the Qualcomm SoCs, when run with Gunyah or older QHEE hypervisor, all the resources whether it is static or dynamic, is managed by the hypervisor. Dynamic resources if it is present for a remote processor will always be coming from secure world via SMC call while static resources may be present in remote processor firmware binary or it may be coming qcom_scm_pas_get_rsc_table() SMC call along with dynamic resources. Some of the remote processor drivers, such as video, GPU, IPA, etc., do not check whether resources are present in their remote processor firmware binary. In such cases, the caller of this function should set input_rt and input_rt_size as NULL and zero respectively. Remoteproc framework has method to check whether firmware binary contain resources or not and they should be pass resource table pointer to input_rt and resource table size to input_rt_size and this will be forwarded to TrustZone for authentication. TrustZone will then append the dynamic resources and return the complete resource table in the passed output buffer. More about documentation on resource table format can be found in include/linux/remoteproc.h Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-11-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 171 +++++++++++++++++++++++++ drivers/firmware/qcom/qcom_scm.h | 1 + include/linux/firmware/qcom/qcom_scm.h | 4 + 3 files changed, 176 insertions(+) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 4ce892d8fb25..918613d5a151 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -111,6 +112,8 @@ enum qcom_scm_qseecom_tz_cmd_info { QSEECOM_TZ_CMD_INFO_VERSION = 3, }; +#define RSCTABLE_BUFFER_NOT_SUFFICIENT 20 + #define QSEECOM_MAX_APP_NAME_SIZE 64 #define SHMBRIDGE_RESULT_NOTSUPP 4 @@ -766,6 +769,174 @@ disable_clk: } EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup); +static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm, + size_t input_rt_size, + size_t *output_rt_size) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_PIL, + .cmd = QCOM_SCM_PIL_PAS_GET_RSCTABLE, + .arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_VAL, QCOM_SCM_RO, QCOM_SCM_VAL, + QCOM_SCM_RW, QCOM_SCM_VAL), + .args[0] = pas_id, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + void *output_rt_tzm; + int ret; + + output_rt_tzm = qcom_tzmem_alloc(__scm->mempool, *output_rt_size, GFP_KERNEL); + if (!output_rt_tzm) + return ERR_PTR(-ENOMEM); + + desc.args[1] = qcom_tzmem_to_phys(input_rt_tzm); + desc.args[2] = input_rt_size; + desc.args[3] = qcom_tzmem_to_phys(output_rt_tzm); + desc.args[4] = *output_rt_size; + + /* + * Whether SMC fail or pass, res.result[2] will hold actual resource table + * size. + * + * If passed 'output_rt_size' buffer size is not sufficient to hold the + * resource table TrustZone sends, response code in res.result[1] as + * RSCTABLE_BUFFER_NOT_SUFFICIENT so that caller can retry this SMC call + * with output_rt_tzm buffer with res.result[2] size however, It should not + * be of unresonable size. + */ + ret = qcom_scm_call(__scm->dev, &desc, &res); + if (!ret && res.result[2] > SZ_1G) { + ret = -E2BIG; + goto free_output_rt; + } + + *output_rt_size = res.result[2]; + if (ret && res.result[1] == RSCTABLE_BUFFER_NOT_SUFFICIENT) + ret = -EOVERFLOW; + +free_output_rt: + if (ret) + qcom_tzmem_free(output_rt_tzm); + + return ret ? ERR_PTR(ret) : output_rt_tzm; +} + +/** + * qcom_scm_pas_get_rsc_table() - Retrieve the resource table in passed output buffer + * for a given peripheral. + * + * Qualcomm remote processor may rely on both static and dynamic resources for + * its functionality. Static resources typically refer to memory-mapped addresses + * required by the subsystem and are often embedded within the firmware binary + * and dynamic resources, such as shared memory in DDR etc., are determined at + * runtime during the boot process. + * + * On Qualcomm Technologies devices, it's possible that static resources are not + * embedded in the firmware binary and instead are provided by TrustZone However, + * dynamic resources are always expected to come from TrustZone. This indicates + * that for Qualcomm devices, all resources (static and dynamic) will be provided + * by TrustZone via the SMC call. + * + * If the remote processor firmware binary does contain static resources, they + * should be passed in input_rt. These will be forwarded to TrustZone for + * authentication. TrustZone will then append the dynamic resources and return + * the complete resource table in output_rt_tzm. + * + * If the remote processor firmware binary does not include a resource table, + * the caller of this function should set input_rt as NULL and input_rt_size + * as zero respectively. + * + * More about documentation on resource table data structures can be found in + * include/linux/remoteproc.h + * + * @ctx: PAS context + * @pas_id: peripheral authentication service id + * @input_rt: resource table buffer which is present in firmware binary + * @input_rt_size: size of the resource table present in firmware binary + * @output_rt_size: TrustZone expects caller should pass worst case size for + * the output_rt_tzm. + * + * Return: + * On success, returns a pointer to the allocated buffer containing the final + * resource table and output_rt_size will have actual resource table size from + * TrustZone. The caller is responsible for freeing the buffer. On failure, + * returns ERR_PTR(-errno). + */ +struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx, + void *input_rt, + size_t input_rt_size, + size_t *output_rt_size) +{ + struct resource_table empty_rsc = {}; + size_t size = SZ_16K; + void *output_rt_tzm; + void *input_rt_tzm; + void *tbl_ptr; + int ret; + + ret = qcom_scm_clk_enable(); + if (ret) + return ERR_PTR(ret); + + ret = qcom_scm_bw_enable(); + if (ret) + goto disable_clk; + + /* + * TrustZone can not accept buffer as NULL value as argument hence, + * we need to pass a input buffer indicating that subsystem firmware + * does not have resource table by filling resource table structure. + */ + if (!input_rt) { + input_rt = &empty_rsc; + input_rt_size = sizeof(empty_rsc); + } + + input_rt_tzm = qcom_tzmem_alloc(__scm->mempool, input_rt_size, GFP_KERNEL); + if (!input_rt_tzm) { + ret = -ENOMEM; + goto disable_scm_bw; + } + + memcpy(input_rt_tzm, input_rt, input_rt_size); + + output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id, input_rt_tzm, + input_rt_size, &size); + if (PTR_ERR(output_rt_tzm) == -EOVERFLOW) + /* Try again with the size requested by the TZ */ + output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id, + input_rt_tzm, + input_rt_size, + &size); + if (IS_ERR(output_rt_tzm)) { + ret = PTR_ERR(output_rt_tzm); + goto free_input_rt; + } + + tbl_ptr = kzalloc(size, GFP_KERNEL); + if (!tbl_ptr) { + qcom_tzmem_free(output_rt_tzm); + ret = -ENOMEM; + goto free_input_rt; + } + + memcpy(tbl_ptr, output_rt_tzm, size); + *output_rt_size = size; + qcom_tzmem_free(output_rt_tzm); + +free_input_rt: + qcom_tzmem_free(input_rt_tzm); + +disable_scm_bw: + qcom_scm_bw_disable(); + +disable_clk: + qcom_scm_clk_disable(); + + return ret ? ERR_PTR(ret) : tbl_ptr; +} +EXPORT_SYMBOL_GPL(qcom_scm_pas_get_rsc_table); + /** * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware * and reset the remote processor diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h index a56c8212cc0c..50d87c628d78 100644 --- a/drivers/firmware/qcom/qcom_scm.h +++ b/drivers/firmware/qcom/qcom_scm.h @@ -105,6 +105,7 @@ int qcom_scm_shm_bridge_enable(struct device *scm_dev); #define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06 #define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07 #define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a +#define QCOM_SCM_PIL_PAS_GET_RSCTABLE 0x21 #define QCOM_SCM_SVC_IO 0x05 #define QCOM_SCM_IO_READ 0x01 diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h index d6d83888bb75..5747bd191bf1 100644 --- a/include/linux/firmware/qcom/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h @@ -88,6 +88,10 @@ int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size); int qcom_scm_pas_auth_and_reset(u32 pas_id); int qcom_scm_pas_shutdown(u32 pas_id); bool qcom_scm_pas_supported(u32 pas_id); +struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx, + void *input_rt, size_t input_rt_size, + size_t *output_rt_size); + int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx); int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val); From a4584bff63c8aba994c8cbccc36748226f8f4b21 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:53:00 +0530 Subject: [PATCH 105/171] remoteproc: pas: Extend parse_fw callback to fetch resources via SMC call Qualcomm remote processor may rely on static and dynamic resources for it to be functional. For most of the Qualcomm SoCs, when run with Gunyah or older QHEE hypervisor, all the resources whether it is static or dynamic, is managed by the hypervisor. Dynamic resources if it is present for a remote processor will always be coming from secure world via SMC call while static resources may be present in remote processor firmware binary or it may be coming from SMC call along with dynamic resources. Remoteproc already has method like rproc_elf_load_rsc_table() to check firmware binary has resources or not and if it is not having then we pass NULL and zero as input resource table and its size argument respectively to qcom_scm_pas_get_rsc_table() and while it has resource present then it should pass the present resources to Trustzone(TZ) so that it could authenticate the present resources and append dynamic resource to return in output_rt argument along with authenticated resources. Extend parse_fw callback to include SMC call to get resources from Trustzone and to leverage resource table parsing and mapping and unmapping code from the remoteproc framework. Reviewed-by: Konrad Dybcio Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-12-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_pas.c | 59 +++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index f746d2f39a1d..e0c7bf94bfc6 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -413,6 +413,61 @@ static void *qcom_pas_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is return pas->mem_region + offset; } +static int qcom_pas_parse_firmware(struct rproc *rproc, const struct firmware *fw) +{ + struct qcom_pas *pas = rproc->priv; + struct resource_table *table = NULL; + size_t output_rt_size; + void *output_rt; + size_t table_sz; + int ret; + + ret = qcom_register_dump_segments(rproc, fw); + if (ret) { + dev_err(pas->dev, "Error in registering dump segments\n"); + return ret; + } + + if (!rproc->has_iommu) + return 0; + + ret = rproc_elf_load_rsc_table(rproc, fw); + if (ret) + dev_dbg(&rproc->dev, "Failed to load resource table from firmware\n"); + + table = rproc->table_ptr; + table_sz = rproc->table_sz; + + /* + * The resources consumed by Qualcomm remote processors fall into two categories: + * static (such as the memory carveouts for the rproc firmware) and dynamic (like + * shared memory pools). Both are managed by a Qualcomm hypervisor (such as QHEE + * or Gunyah), if one is present. Otherwise, a resource table must be retrieved + * via an SCM call. That table will list all dynamic resources (if any) and possibly + * the static ones. The static resources may also come from a resource table embedded + * in the rproc firmware instead. + * + * Here, we call rproc_elf_load_rsc_table() to check firmware binary has resources + * or not and if it is not having then we pass NULL and zero as input resource + * table pointer and size respectively to the argument of qcom_scm_pas_get_rsc_table() + * and this is even true for Qualcomm remote processor who does follow remoteproc + * framework. + */ + output_rt = qcom_scm_pas_get_rsc_table(pas->pas_ctx, table, table_sz, &output_rt_size); + ret = IS_ERR(output_rt) ? PTR_ERR(output_rt) : 0; + if (ret) { + dev_err(pas->dev, "Error in getting resource table: %d\n", ret); + return ret; + } + + kfree(rproc->cached_table); + rproc->cached_table = output_rt; + rproc->table_ptr = rproc->cached_table; + rproc->table_sz = output_rt_size; + + return ret; +} + static unsigned long qcom_pas_panic(struct rproc *rproc) { struct qcom_pas *pas = rproc->priv; @@ -425,7 +480,7 @@ static const struct rproc_ops qcom_pas_ops = { .start = qcom_pas_start, .stop = qcom_pas_stop, .da_to_va = qcom_pas_da_to_va, - .parse_fw = qcom_register_dump_segments, + .parse_fw = qcom_pas_parse_firmware, .load = qcom_pas_load, .panic = qcom_pas_panic, }; @@ -435,7 +490,7 @@ static const struct rproc_ops qcom_pas_minidump_ops = { .start = qcom_pas_start, .stop = qcom_pas_stop, .da_to_va = qcom_pas_da_to_va, - .parse_fw = qcom_register_dump_segments, + .parse_fw = qcom_pas_parse_firmware, .load = qcom_pas_load, .panic = qcom_pas_panic, .coredump = qcom_pas_minidump, From 5c720260e840b508053dd5338577e0175ef31739 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:53:01 +0530 Subject: [PATCH 106/171] remoteproc: qcom: pas: Enable Secure PAS support with IOMMU managed by Linux Most Qualcomm platforms feature Gunyah hypervisor, which typically handles IOMMU configuration. This includes mapping memory regions and device memory resources for remote processors by intercepting qcom_scm_pas_auth_and_reset() calls. These mappings are later removed during teardown. Additionally, SHM bridge setup is required to enable memory protection for both remoteproc metadata and its memory regions. When the aforementioned hypervisor is absent, the operating system must perform these configurations instead. When Linux runs as the hypervisor (@ EL2) on a SoC, it will have its own device tree overlay file that specifies the firmware stream ID now managed by Linux for a particular remote processor. If the iommus property is specified in the remoteproc device tree node, it indicates that IOMMU configuration must be handled by Linux. In this case, the has_iommu flag is set for the remote processor, which ensures that the resource table, carveouts, and SHM bridge are properly configured before memory is passed to TrustZone for authentication. Otherwise, the has_iommu flag remains unset, which indicates default behavior. Enables Secure PAS support for remote processors when IOMMU configuration is managed by Linux. Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-13-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_pas.c | 48 ++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index e0c7bf94bfc6..46204da046fa 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -255,6 +256,22 @@ release_dtb_metadata: return ret; } +static void qcom_pas_unmap_carveout(struct rproc *rproc, phys_addr_t mem_phys, size_t size) +{ + if (rproc->has_iommu) + iommu_unmap(rproc->domain, mem_phys, size); +} + +static int qcom_pas_map_carveout(struct rproc *rproc, phys_addr_t mem_phys, size_t size) +{ + int ret = 0; + + if (rproc->has_iommu) + ret = iommu_map(rproc->domain, mem_phys, mem_phys, size, + IOMMU_READ | IOMMU_WRITE, GFP_KERNEL); + return ret; +} + static int qcom_pas_start(struct rproc *rproc) { struct qcom_pas *pas = rproc->priv; @@ -289,11 +306,15 @@ static int qcom_pas_start(struct rproc *rproc) } if (pas->dtb_pas_id) { - ret = qcom_scm_pas_auth_and_reset(pas->dtb_pas_id); + ret = qcom_pas_map_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size); + if (ret) + goto disable_px_supply; + + ret = qcom_scm_pas_prepare_and_auth_reset(pas->dtb_pas_ctx); if (ret) { dev_err(pas->dev, "failed to authenticate dtb image and release reset\n"); - goto disable_px_supply; + goto unmap_dtb_carveout; } } @@ -304,18 +325,22 @@ static int qcom_pas_start(struct rproc *rproc) qcom_pil_info_store(pas->info_name, pas->mem_phys, pas->mem_size); - ret = qcom_scm_pas_auth_and_reset(pas->pas_id); + ret = qcom_pas_map_carveout(rproc, pas->mem_phys, pas->mem_size); + if (ret) + goto release_pas_metadata; + + ret = qcom_scm_pas_prepare_and_auth_reset(pas->pas_ctx); if (ret) { dev_err(pas->dev, "failed to authenticate image and release reset\n"); - goto release_pas_metadata; + goto unmap_carveout; } ret = qcom_q6v5_wait_for_start(&pas->q6v5, msecs_to_jiffies(5000)); if (ret == -ETIMEDOUT) { dev_err(pas->dev, "start timed out\n"); qcom_scm_pas_shutdown(pas->pas_id); - goto release_pas_metadata; + goto unmap_carveout; } qcom_scm_pas_metadata_release(pas->pas_ctx); @@ -327,10 +352,16 @@ static int qcom_pas_start(struct rproc *rproc) return 0; +unmap_carveout: + qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size); release_pas_metadata: qcom_scm_pas_metadata_release(pas->pas_ctx); if (pas->dtb_pas_id) qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); + +unmap_dtb_carveout: + if (pas->dtb_pas_id) + qcom_pas_unmap_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size); disable_px_supply: if (pas->px_supply) regulator_disable(pas->px_supply); @@ -386,8 +417,12 @@ static int qcom_pas_stop(struct rproc *rproc) ret = qcom_scm_pas_shutdown(pas->dtb_pas_id); if (ret) dev_err(pas->dev, "failed to shutdown dtb: %d\n", ret); + + qcom_pas_unmap_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size); } + qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size); + handover = qcom_q6v5_unprepare(&pas->q6v5); if (handover) qcom_pas_handover(&pas->q6v5); @@ -738,6 +773,7 @@ static int qcom_pas_probe(struct platform_device *pdev) return -ENOMEM; } + rproc->has_iommu = of_property_present(pdev->dev.of_node, "iommus"); rproc->auto_boot = desc->auto_boot; rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); @@ -817,6 +853,8 @@ static int qcom_pas_probe(struct platform_device *pdev) goto remove_ssr_sysmon; } + pas->pas_ctx->use_tzmem = rproc->has_iommu; + pas->dtb_pas_ctx->use_tzmem = rproc->has_iommu; ret = rproc_add(rproc); if (ret) goto remove_ssr_sysmon; From a0db08f47c836251fbaccf711e12fe8428235465 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 5 Jan 2026 13:49:41 +0100 Subject: [PATCH 107/171] tee: amdtee: Remove unused return variables In tee_params_to_amd_params() and amd_params_to_tee_params(), return 0 directly and remove the unused return variables. Signed-off-by: Thorsten Blum Signed-off-by: Jens Wiklander --- drivers/tee/amdtee/call.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/tee/amdtee/call.c b/drivers/tee/amdtee/call.c index 4c21b02be4af..460b0c9e511f 100644 --- a/drivers/tee/amdtee/call.c +++ b/drivers/tee/amdtee/call.c @@ -15,7 +15,7 @@ static int tee_params_to_amd_params(struct tee_param *tee, u32 count, struct tee_operation *amd) { - int i, ret = 0; + int i; u32 type; if (!count) @@ -66,13 +66,13 @@ static int tee_params_to_amd_params(struct tee_param *tee, u32 count, i, amd->params[i].val.b); } } - return ret; + return 0; } static int amd_params_to_tee_params(struct tee_param *tee, u32 count, struct tee_operation *amd) { - int i, ret = 0; + int i; u32 type; if (!count) @@ -118,7 +118,7 @@ static int amd_params_to_tee_params(struct tee_param *tee, u32 count, i, amd->params[i].val.b); } } - return ret; + return 0; } static DEFINE_MUTEX(ta_refcount_mutex); From 900131320bc9a9ec1d84702b2694b813c11c91b7 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Mon, 12 Jan 2026 23:15:54 +0200 Subject: [PATCH 108/171] ARM: s3c: remove a leftover hwmon-s3c.h header file The last user of defined structures s3c_hwmon_pdata and s3c_hwmon_chcfg was removed in commit 0d297df03890 ("ARM: s3c: simplify platform code"), thus the platform data header file itself can be removed also. Signed-off-by: Vladimir Zapolskiy Link: https://patch.msgid.link/20260112211554.3755188-1-vz@mleia.com Signed-off-by: Krzysztof Kozlowski --- include/linux/platform_data/hwmon-s3c.h | 36 ------------------------- 1 file changed, 36 deletions(-) delete mode 100644 include/linux/platform_data/hwmon-s3c.h diff --git a/include/linux/platform_data/hwmon-s3c.h b/include/linux/platform_data/hwmon-s3c.h deleted file mode 100644 index 7d21e0c41037..000000000000 --- a/include/linux/platform_data/hwmon-s3c.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright 2005 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C - HWMon interface for ADC -*/ - -#ifndef __HWMON_S3C_H__ -#define __HWMON_S3C_H__ - -/** - * s3c_hwmon_chcfg - channel configuration - * @name: The name to give this channel. - * @mult: Multiply the ADC value read by this. - * @div: Divide the value from the ADC by this. - * - * The value read from the ADC is converted to a value that - * hwmon expects (mV) by result = (value_read * @mult) / @div. - */ -struct s3c_hwmon_chcfg { - const char *name; - unsigned int mult; - unsigned int div; -}; - -/** - * s3c_hwmon_pdata - HWMON platform data - * @in: One configuration for each possible channel used. - */ -struct s3c_hwmon_pdata { - struct s3c_hwmon_chcfg *in[8]; -}; - -#endif /* __HWMON_S3C_H__ */ From 241bdf7253502c56251ef8b25ab9cad5b6547422 Mon Sep 17 00:00:00 2001 From: Aristo Chen Date: Mon, 12 Jan 2026 23:48:29 +0800 Subject: [PATCH 109/171] tee: add revision sysfs attribute Add a generic TEE revision sysfs attribute backed by a new optional get_tee_revision() callback. The revision string is diagnostic-only and must not be used to infer feature support. Signed-off-by: Aristo Chen Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- Documentation/ABI/testing/sysfs-class-tee | 10 +++++ drivers/tee/tee_core.c | 51 ++++++++++++++++++++++- include/linux/tee_core.h | 9 ++++ 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee index c9144d16003e..1a0a3050aaa9 100644 --- a/Documentation/ABI/testing/sysfs-class-tee +++ b/Documentation/ABI/testing/sysfs-class-tee @@ -13,3 +13,13 @@ Description: space if the variable is absent. The primary purpose of this variable is to let systemd know whether tee-supplicant is needed in the early boot with initramfs. + +What: /sys/class/tee/tee{,priv}X/revision +Date: Jan 2026 +KernelVersion: 6.19 +Contact: op-tee@lists.trustedfirmware.org +Description: + Read-only revision string reported by the TEE driver. This is + for diagnostics only and must not be used to infer feature + support. Use TEE_IOC_VERSION for capability and compatibility + checks. diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index d65d47cc154e..0a00499811c1 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = { NULL }; -ATTRIBUTE_GROUPS(tee_dev); +static const struct attribute_group tee_dev_group = { + .attrs = tee_dev_attrs, +}; + +static ssize_t revision_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tee_device *teedev = container_of(dev, struct tee_device, dev); + char version[TEE_REVISION_STR_SIZE]; + int ret; + + if (!teedev->desc->ops->get_tee_revision) + return -ENODEV; + + ret = teedev->desc->ops->get_tee_revision(teedev, version, + sizeof(version)); + if (ret) + return ret; + + return sysfs_emit(buf, "%s\n", version); +} +static DEVICE_ATTR_RO(revision); + +static struct attribute *tee_revision_attrs[] = { + &dev_attr_revision.attr, + NULL +}; + +static umode_t tee_revision_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct tee_device *teedev = container_of(dev, struct tee_device, dev); + + if (teedev->desc->ops->get_tee_revision) + return attr->mode; + + return 0; +} + +static const struct attribute_group tee_revision_group = { + .attrs = tee_revision_attrs, + .is_visible = tee_revision_attr_is_visible, +}; + +static const struct attribute_group *tee_dev_groups[] = { + &tee_dev_group, + &tee_revision_group, + NULL +}; static const struct class tee_class = { .name = "tee", diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h index 1f3e5dad6d0d..ee5f0bd41f43 100644 --- a/include/linux/tee_core.h +++ b/include/linux/tee_core.h @@ -76,6 +76,9 @@ struct tee_device { /** * struct tee_driver_ops - driver operations vtable * @get_version: returns version of driver + * @get_tee_revision: returns revision string (diagnostic only); + * do not infer feature support from this, use + * TEE_IOC_VERSION instead * @open: called for a context when the device file is opened * @close_context: called when the device file is closed * @release: called to release the context @@ -95,9 +98,12 @@ struct tee_device { * client closes the device file, even if there are existing references to the * context. The TEE driver can use @close_context to start cleaning up. */ + struct tee_driver_ops { void (*get_version)(struct tee_device *teedev, struct tee_ioctl_version_data *vers); + int (*get_tee_revision)(struct tee_device *teedev, + char *buf, size_t len); int (*open)(struct tee_context *ctx); void (*close_context)(struct tee_context *ctx); void (*release)(struct tee_context *ctx); @@ -123,6 +129,9 @@ struct tee_driver_ops { int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm); }; +/* Size for TEE revision string buffer used by get_tee_revision(). */ +#define TEE_REVISION_STR_SIZE 128 + /** * struct tee_desc - Describes the TEE driver to the subsystem * @name: name of driver From 3fec51b89d603f2d8ebf50f815b5e159efc381da Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 27 Nov 2025 14:54:54 +0100 Subject: [PATCH 110/171] soc: ti: k3-socinfo: Fix compile testing There seems to be nothing preventing this driver from being compile tested so enable that by adding the missing input prompt. Fixes: 907a2b7e2fc7 ("soc: ti: add k3 platforms chipid module driver") Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20251127135455.2497-1-johan@kernel.org Signed-off-by: Nishanth Menon --- drivers/soc/ti/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig index 1a93001c9e36..163aadd589d3 100644 --- a/drivers/soc/ti/Kconfig +++ b/drivers/soc/ti/Kconfig @@ -62,7 +62,7 @@ config TI_K3_RINGACC If unsure, say N. config TI_K3_SOCINFO - bool + bool "K3 SoC Information driver" if COMPILE_TEST depends on ARCH_K3 || COMPILE_TEST select SOC_BUS select MFD_SYSCON From c933138d45176780fabbbe7da263e04d5b3e525d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 27 Nov 2025 14:49:42 +0100 Subject: [PATCH 111/171] soc: ti: k3-socinfo: Fix regmap leak on probe failure The mmio regmap allocated during probe is never freed. Switch to using the device managed allocator so that the regmap is released on probe failures (e.g. probe deferral) and on driver unbind. Fixes: a5caf03188e4 ("soc: ti: k3-socinfo: Do not use syscon helper to build regmap") Cc: stable@vger.kernel.org # 6.15 Cc: Andrew Davis Signed-off-by: Johan Hovold Acked-by: Andrew Davis Link: https://patch.msgid.link/20251127134942.2121-1-johan@kernel.org Signed-off-by: Nishanth Menon --- drivers/soc/ti/k3-socinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/ti/k3-socinfo.c b/drivers/soc/ti/k3-socinfo.c index 50c170a995f9..42275cb5ba1c 100644 --- a/drivers/soc/ti/k3-socinfo.c +++ b/drivers/soc/ti/k3-socinfo.c @@ -141,7 +141,7 @@ static int k3_chipinfo_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - regmap = regmap_init_mmio(dev, base, &k3_chipinfo_regmap_cfg); + regmap = devm_regmap_init_mmio(dev, base, &k3_chipinfo_regmap_cfg); if (IS_ERR(regmap)) return PTR_ERR(regmap); From 80db65d4acfb9ff12d00172aed39ea8b98261aad Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 13 Jan 2026 01:47:16 +0000 Subject: [PATCH 112/171] soc: ti: pruss: Fix double free in pruss_clk_mux_setup() In the pruss_clk_mux_setup(), the devm_add_action_or_reset() indirectly calls pruss_of_free_clk_provider(), which calls of_node_put(clk_mux_np) on the error path. However, after the devm_add_action_or_reset() returns, the of_node_put(clk_mux_np) is called again, causing a double free. Fix by returning directly, to avoid the duplicate of_node_put(). Fixes: ba59c9b43c86 ("soc: ti: pruss: support CORECLK_MUX and IEPCLK_MUX") Cc: stable@vger.kernel.org Signed-off-by: Wentao Liang Link: https://patch.msgid.link/20260113014716.2464741-1-vulab@iscas.ac.cn Signed-off-by: Nishanth Menon --- drivers/soc/ti/pruss.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/ti/pruss.c b/drivers/soc/ti/pruss.c index 038576805bfa..0fd59c73f585 100644 --- a/drivers/soc/ti/pruss.c +++ b/drivers/soc/ti/pruss.c @@ -366,12 +366,10 @@ static int pruss_clk_mux_setup(struct pruss *pruss, struct clk *clk_mux, ret = devm_add_action_or_reset(dev, pruss_of_free_clk_provider, clk_mux_np); - if (ret) { + if (ret) dev_err(dev, "failed to add clkmux free action %d", ret); - goto put_clk_mux_np; - } - return 0; + return ret; put_clk_mux_np: of_node_put(clk_mux_np); From ccef4b2703ff5b0de0b1bda30a0de3026d52eb19 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 10 Jan 2026 21:37:53 +0200 Subject: [PATCH 113/171] soc: qcom: ubwc: add missing include The header has a function which calls pr_err(). Don't require users of the header to include and include it here. Fixes: 87cfc79dcd60 ("drm/msm/a6xx: Resolve the meaning of UBWC_MODE") Signed-off-by: Dmitry Baryshkov Reviewed-by: Bryan O'Donoghue Link: https://lore.kernel.org/r/20260110-iris-ubwc-v1-1-dd70494dcd7b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- include/linux/soc/qcom/ubwc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index 0a4edfe3d96d..f052e241736c 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -8,6 +8,7 @@ #define __QCOM_UBWC_H__ #include +#include #include struct qcom_ubwc_cfg_data { From 5eb63e9bb65d88abde647ced50fe6ad40c11de1a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 19 Dec 2025 12:01:19 +0100 Subject: [PATCH 114/171] bus: omap-ocp2scp: fix OF populate on driver rebind Since commit c6e126de43e7 ("of: Keep track of populated platform devices") child devices will not be created by of_platform_populate() if the devices had previously been deregistered individually so that the OF_POPULATED flag is still set in the corresponding OF nodes. Switch to using of_platform_depopulate() instead of open coding so that the child devices are created if the driver is rebound. Fixes: c6e126de43e7 ("of: Keep track of populated platform devices") Cc: stable@vger.kernel.org # 3.16 Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20251219110119.23507-1-johan@kernel.org Signed-off-by: Kevin Hilman --- drivers/bus/omap-ocp2scp.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/bus/omap-ocp2scp.c b/drivers/bus/omap-ocp2scp.c index e4dfda7b3b10..eee5ad191ea9 100644 --- a/drivers/bus/omap-ocp2scp.c +++ b/drivers/bus/omap-ocp2scp.c @@ -17,15 +17,6 @@ #define OCP2SCP_TIMING 0x18 #define SYNC2_MASK 0xf -static int ocp2scp_remove_devices(struct device *dev, void *c) -{ - struct platform_device *pdev = to_platform_device(dev); - - platform_device_unregister(pdev); - - return 0; -} - static int omap_ocp2scp_probe(struct platform_device *pdev) { int ret; @@ -79,7 +70,7 @@ err1: pm_runtime_disable(&pdev->dev); err0: - device_for_each_child(&pdev->dev, NULL, ocp2scp_remove_devices); + of_platform_depopulate(&pdev->dev); return ret; } @@ -87,7 +78,7 @@ err0: static void omap_ocp2scp_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); - device_for_each_child(&pdev->dev, NULL, ocp2scp_remove_devices); + of_platform_depopulate(&pdev->dev); } #ifdef CONFIG_OF From 5e2d6fa48a841e419848a811a015f61128a149ce Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 19 Dec 2025 12:02:59 +0100 Subject: [PATCH 115/171] bus: omap-ocp2scp: enable compile testing There seems to be nothing preventing the driver from being compile tested so enable that for wider build coverage. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20251219110259.23630-1-johan@kernel.org Signed-off-by: Kevin Hilman --- drivers/bus/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index fe7600283e70..2a1b46f07080 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -141,7 +141,7 @@ config OMAP_INTERCONNECT config OMAP_OCP2SCP tristate "OMAP OCP2SCP DRIVER" - depends on ARCH_OMAP2PLUS + depends on ARCH_OMAP2PLUS || COMPILE_TEST help Driver to enable ocp2scp module which transforms ocp interface protocol to scp protocol. In OMAP4, USB PHY is connected via From 3bd4edd67b034f8e1f61c86e0eb098de6179e3f2 Mon Sep 17 00:00:00 2001 From: Haotian Zhang Date: Mon, 24 Nov 2025 18:48:05 +0800 Subject: [PATCH 116/171] hwspinlock: omap: Handle devm_pm_runtime_enable() errors Although unlikely, devm_pm_runtime_enable() can fail due to memory allocations. Without proper error handling, the subsequent pm_runtime_resume_and_get() call may operate on incorrectly initialized runtime PM state. Add error handling to check the return value of devm_pm_runtime_enable() and return on failure. Fixes: 25f7d74d4514 ("hwspinlock: omap: Use devm_pm_runtime_enable() helper") Signed-off-by: Haotian Zhang Link: https://patch.msgid.link/20251124104805.135-1-vulab@iscas.ac.cn Signed-off-by: Kevin Hilman --- drivers/hwspinlock/omap_hwspinlock.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hwspinlock/omap_hwspinlock.c b/drivers/hwspinlock/omap_hwspinlock.c index 27b47b8623c0..2d8de835bc24 100644 --- a/drivers/hwspinlock/omap_hwspinlock.c +++ b/drivers/hwspinlock/omap_hwspinlock.c @@ -88,7 +88,9 @@ static int omap_hwspinlock_probe(struct platform_device *pdev) * make sure the module is enabled and clocked before reading * the module SYSSTATUS register */ - devm_pm_runtime_enable(&pdev->dev); + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) return ret; From e82d0477fd80707221c3d110f56d05506de2698c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 13 Jan 2026 15:38:15 +0100 Subject: [PATCH 117/171] tpm/tpm_ftpm_tee: Fix kdoc after function renames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recently ftpm_tee_probe() and ftpm_tee_remove() grew a suffix in their function name but I failed to adapt the kernel doc when doing so. This change aligns the kernel doc to the actual function name (again). Fixes: 92fad96aea24 ("tpm/tpm_ftpm_tee: Make use of tee bus methods") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202601132105.9lgSsC4U-lkp@intel.com/ Signed-off-by: Uwe Kleine-König Reviewed-by: Jarkko Sakkinen Signed-off-by: Jens Wiklander --- drivers/char/tpm/tpm_ftpm_tee.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c index 20294d1953a3..b82490439633 100644 --- a/drivers/char/tpm/tpm_ftpm_tee.c +++ b/drivers/char/tpm/tpm_ftpm_tee.c @@ -163,7 +163,7 @@ static int ftpm_tee_match(struct tee_ioctl_version_data *ver, const void *data) } /** - * ftpm_tee_probe() - initialize the fTPM + * ftpm_tee_probe_generic() - initialize the fTPM * @dev: the device description. * * Return: @@ -266,7 +266,7 @@ static int ftpm_plat_tee_probe(struct platform_device *pdev) } /** - * ftpm_tee_remove() - remove the TPM device + * ftpm_tee_remove_generic() - remove the TPM device * @dev: the device description. * * Return: From c19faf5a62315d5e0e65dde49b7b59e30330b9c2 Mon Sep 17 00:00:00 2001 From: Aristo Chen Date: Mon, 12 Jan 2026 23:48:30 +0800 Subject: [PATCH 118/171] tee: optee: store OS revision for TEE core Collect OP-TEE OS revision from secure world for both SMC and FF-A ABIs, store it in the OP-TEE driver, and expose it through the generic get_tee_revision() callback. Signed-off-by: Aristo Chen Signed-off-by: Jens Wiklander Reviewed-by: Sumit Garg --- drivers/tee/optee/core.c | 23 +++++++++++++ drivers/tee/optee/ffa_abi.c | 54 +++++++++++++++++++++++-------- drivers/tee/optee/optee_private.h | 19 +++++++++++ drivers/tee/optee/smc_abi.c | 15 +++++++-- 4 files changed, 94 insertions(+), 17 deletions(-) diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 5b62139714ce..2d807bc748bc 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width) return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask); } +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len) +{ + struct optee *optee = tee_get_drvdata(teedev); + u64 build_id; + + if (!optee) + return -ENODEV; + if (!buf || !len) + return -EINVAL; + + build_id = optee->revision.os_build_id; + if (build_id) + scnprintf(buf, len, "%u.%u (%016llx)", + optee->revision.os_major, + optee->revision.os_minor, + (unsigned long long)build_id); + else + scnprintf(buf, len, "%u.%u", optee->revision.os_major, + optee->revision.os_minor); + + return 0; +} + static void optee_bus_scan(struct work_struct *work) { WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP)); diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c index bf8390789ecf..8fc72aa95722 100644 --- a/drivers/tee/optee/ffa_abi.c +++ b/drivers/tee/optee/ffa_abi.c @@ -775,6 +775,39 @@ static int optee_ffa_reclaim_protmem(struct optee *optee, * with a matching configuration. */ +static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev, + const struct ffa_ops *ops, + struct optee_revision *revision) +{ + const struct ffa_msg_ops *msg_ops = ops->msg_ops; + struct ffa_send_direct_data data = { + .data0 = OPTEE_FFA_GET_OS_VERSION, + }; + int rc; + + msg_ops->mode_32bit_set(ffa_dev); + + rc = msg_ops->sync_send_receive(ffa_dev, &data); + if (rc) { + pr_err("Unexpected error %d\n", rc); + return false; + } + + if (revision) { + revision->os_major = data.data0; + revision->os_minor = data.data1; + revision->os_build_id = data.data2; + } + + if (data.data2) + pr_info("revision %lu.%lu (%08lx)", + data.data0, data.data1, data.data2); + else + pr_info("revision %lu.%lu", data.data0, data.data1); + + return true; +} + static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev, const struct ffa_ops *ops) { @@ -798,20 +831,6 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev, return false; } - data = (struct ffa_send_direct_data){ - .data0 = OPTEE_FFA_GET_OS_VERSION, - }; - rc = msg_ops->sync_send_receive(ffa_dev, &data); - if (rc) { - pr_err("Unexpected error %d\n", rc); - return false; - } - if (data.data2) - pr_info("revision %lu.%lu (%08lx)", - data.data0, data.data1, data.data2); - else - pr_info("revision %lu.%lu", data.data0, data.data1); - return true; } @@ -900,6 +919,7 @@ static int optee_ffa_open(struct tee_context *ctx) static const struct tee_driver_ops optee_ffa_clnt_ops = { .get_version = optee_ffa_get_version, + .get_tee_revision = optee_get_revision, .open = optee_ffa_open, .release = optee_release, .open_session = optee_open_session, @@ -918,6 +938,7 @@ static const struct tee_desc optee_ffa_clnt_desc = { static const struct tee_driver_ops optee_ffa_supp_ops = { .get_version = optee_ffa_get_version, + .get_tee_revision = optee_get_revision, .open = optee_ffa_open, .release = optee_release_supp, .supp_recv = optee_supp_recv, @@ -1060,6 +1081,11 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) if (!optee) return -ENOMEM; + if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision)) { + rc = -EINVAL; + goto err_free_optee; + } + pool = optee_ffa_shm_pool_alloc_pages(); if (IS_ERR(pool)) { rc = PTR_ERR(pool); diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index db9ea673fbca..acd3051c4879 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -171,6 +171,24 @@ struct optee_ffa { struct optee; +/** + * struct optee_revision - OP-TEE OS revision reported by secure world + * @os_major: OP-TEE OS major version + * @os_minor: OP-TEE OS minor version + * @os_build_id: OP-TEE OS build identifier (0 if unspecified) + * + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an + * FF-A ABI version. + */ +struct optee_revision { + u32 os_major; + u32 os_minor; + u64 os_build_id; +}; + +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len); + /** * struct optee_ops - OP-TEE driver internal operations * @do_call_with_arg: enters OP-TEE in secure world @@ -249,6 +267,7 @@ struct optee { bool in_kernel_rpmb_routing; struct work_struct scan_bus_work; struct work_struct rpmb_scan_bus_work; + struct optee_revision revision; }; struct optee_session { diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c index 0be663fcd52b..51fae1ab8ef8 100644 --- a/drivers/tee/optee/smc_abi.c +++ b/drivers/tee/optee/smc_abi.c @@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx) static const struct tee_driver_ops optee_clnt_ops = { .get_version = optee_get_version, + .get_tee_revision = optee_get_revision, .open = optee_smc_open, .release = optee_release, .open_session = optee_open_session, @@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = { static const struct tee_driver_ops optee_supp_ops = { .get_version = optee_get_version, + .get_tee_revision = optee_get_revision, .open = optee_smc_open, .release = optee_release_supp, .supp_recv = optee_supp_recv, @@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn) } #endif -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn) +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn, + struct optee_revision *revision) { union { struct arm_smccc_res smccc; @@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn) invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc); + if (revision) { + revision->os_major = res.result.major; + revision->os_minor = res.result.minor; + revision->os_build_id = res.result.build_id; + } + if (res.result.build_id) pr_info("revision %lu.%lu (%0*lx)", res.result.major, res.result.minor, (int)sizeof(res.result.build_id) * 2, @@ -1745,8 +1754,6 @@ static int optee_probe(struct platform_device *pdev) return -EINVAL; } - optee_msg_get_os_revision(invoke_fn); - if (!optee_msg_api_revision_is_compatible(invoke_fn)) { pr_warn("api revision mismatch\n"); return -EINVAL; @@ -1815,6 +1822,8 @@ static int optee_probe(struct platform_device *pdev) goto err_free_shm_pool; } + optee_msg_get_os_revision(invoke_fn, &optee->revision); + optee->ops = &optee_ops; optee->smc.invoke_fn = invoke_fn; optee->smc.sec_caps = sec_caps; From 49d2cda7ca2e8e287617e7a5b7fae523eaece955 Mon Sep 17 00:00:00 2001 From: "Herve Codina (Schneider Electric)" Date: Wed, 14 Jan 2026 10:39:30 +0100 Subject: [PATCH 119/171] of/irq: Introduce for_each_of_imap_item for_each_of_imap_item is an iterator designed to help a driver to parse an interrupt-map property. Indeed some drivers need to know details about the interrupt mapping described in the device-tree in order to set internal registers accordingly. Signed-off-by: Herve Codina (Schneider Electric) Tested-by: Wolfram Sang Reviewed-by: Rob Herring (Arm) Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260114093938.1089936-2-herve.codina@bootlin.com Signed-off-by: Geert Uytterhoeven --- drivers/of/irq.c | 70 ++++++++++++++++++++++++++++++++++++++++++ include/linux/of_irq.h | 41 ++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/drivers/of/irq.c b/drivers/of/irq.c index e3816819dbfe..f374d8b212b8 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -157,6 +157,76 @@ const __be32 *of_irq_parse_imap_parent(const __be32 *imap, int len, struct of_ph return imap; } +int of_imap_parser_init(struct of_imap_parser *parser, struct device_node *node, + struct of_imap_item *item) +{ + int imaplen; + u32 tmp; + int ret; + + /* + * parent_offset is the offset where the parent part is starting. + * In other words, the offset where the parent interrupt controller + * phandle is present. + * + * Compute this offset (child #interrupt-cells + child #address-cells) + */ + parser->parent_offset = of_bus_n_addr_cells(node); + + ret = of_property_read_u32(node, "#interrupt-cells", &tmp); + if (ret) + return ret; + + parser->parent_offset += tmp; + + if (WARN(parser->parent_offset > ARRAY_SIZE(item->child_imap), + "child part size = %u, cannot fit in array of %zu items", + parser->parent_offset, ARRAY_SIZE(item->child_imap))) + return -EINVAL; + + parser->imap = of_get_property(node, "interrupt-map", &imaplen); + if (!parser->imap) + return -ENOENT; + + imaplen /= sizeof(*parser->imap); + parser->imap_end = parser->imap + imaplen; + + memset(item, 0, sizeof(*item)); + item->child_imap_count = parser->parent_offset; + + return 0; +} +EXPORT_SYMBOL_GPL(of_imap_parser_init); + +struct of_imap_item *of_imap_parser_one(struct of_imap_parser *parser, + struct of_imap_item *item) +{ + const __be32 *imap_parent, *imap_next; + int i; + + /* Release previously get parent node */ + of_node_put(item->parent_args.np); + + if (parser->imap + parser->parent_offset + 1 >= parser->imap_end) + return NULL; + + imap_parent = parser->imap + parser->parent_offset; + + imap_next = of_irq_parse_imap_parent(imap_parent, + parser->imap_end - imap_parent, + &item->parent_args); + if (!imap_next) + return NULL; + + for (i = 0; i < parser->parent_offset; i++) + item->child_imap[i] = be32_to_cpu(*(parser->imap + i)); + + parser->imap = imap_next; + + return item; +} +EXPORT_SYMBOL_GPL(of_imap_parser_one); + /** * of_irq_parse_raw - Low level interrupt tree parsing * @addr: address specifier (start of "reg" property of the device) in be32 format diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h index 1c2bc0281807..2a64d8cecaae 100644 --- a/include/linux/of_irq.h +++ b/include/linux/of_irq.h @@ -11,6 +11,30 @@ typedef int (*of_irq_init_cb_t)(struct device_node *, struct device_node *); +struct of_imap_parser { + struct device_node *node; + const __be32 *imap; + const __be32 *imap_end; + u32 parent_offset; +}; + +struct of_imap_item { + struct of_phandle_args parent_args; + u32 child_imap_count; + u32 child_imap[16]; /* Arbitrary size. + * Should be #address-cells + #interrupt-cells but + * avoid using allocation and so, expect that 16 + * should be enough + */ +}; + +/* + * If the iterator is exited prematurely (break, goto, return) of_node_put() has + * to be called on item.parent_args.np + */ +#define for_each_of_imap_item(parser, item) \ + for (; of_imap_parser_one(parser, item);) + /* * Workarounds only applied to 32bit powermac machines */ @@ -49,6 +73,11 @@ extern int of_irq_get_byname(struct device_node *dev, const char *name); extern int of_irq_to_resource_table(struct device_node *dev, struct resource *res, int nr_irqs); extern struct device_node *of_irq_find_parent(struct device_node *child); +extern int of_imap_parser_init(struct of_imap_parser *parser, + struct device_node *node, + struct of_imap_item *item); +extern struct of_imap_item *of_imap_parser_one(struct of_imap_parser *parser, + struct of_imap_item *item); extern struct irq_domain *of_msi_get_domain(struct device *dev, const struct device_node *np, enum irq_domain_bus_token token); @@ -92,7 +121,17 @@ static inline void *of_irq_find_parent(struct device_node *child) { return NULL; } - +static inline int of_imap_parser_init(struct of_imap_parser *parser, + struct device_node *node, + struct of_imap_item *item) +{ + return -ENOSYS; +} +static inline struct of_imap_item *of_imap_parser_one(struct of_imap_parser *parser, + struct of_imap_item *item) +{ + return NULL; +} static inline struct irq_domain *of_msi_get_domain(struct device *dev, struct device_node *np, enum irq_domain_bus_token token) From a9811aeb42f85bd423725996ef7dce5cd51f8ff7 Mon Sep 17 00:00:00 2001 From: "Herve Codina (Schneider Electric)" Date: Wed, 14 Jan 2026 10:39:31 +0100 Subject: [PATCH 120/171] of: unittest: Add a test case for for_each_of_imap_item iterator Recently for_each_of_imap_item iterator has been introduce to help drivers in parsing the interrupt-map property. Add a test case for this iterator. Signed-off-by: Herve Codina (Schneider Electric) Reviewed-by: Rob Herring (Arm) Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260114093938.1089936-3-herve.codina@bootlin.com Signed-off-by: Geert Uytterhoeven --- .../of/unittest-data/tests-interrupts.dtsi | 9 ++ drivers/of/unittest.c | 116 ++++++++++++++++++ 2 files changed, 125 insertions(+) diff --git a/drivers/of/unittest-data/tests-interrupts.dtsi b/drivers/of/unittest-data/tests-interrupts.dtsi index 4ccb54f91c30..974f888c9b15 100644 --- a/drivers/of/unittest-data/tests-interrupts.dtsi +++ b/drivers/of/unittest-data/tests-interrupts.dtsi @@ -50,6 +50,15 @@ interrupt-map = <0x5000 1 2 &test_intc0 15>; }; + intmap2 { + #interrupt-cells = <2>; + #address-cells = <0>; + interrupt-map = <1 11 &test_intc0 100>, + <2 22 &test_intc1 200 201 202>, + <3 33 &test_intc2 300 301>, + <4 44 &test_intc2 400 401>; + }; + test_intc_intmap0: intc-intmap0 { #interrupt-cells = <1>; #address-cells = <1>; diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 388e9ec2cccf..d88a12f23ad5 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -1654,6 +1654,121 @@ static void __init of_unittest_parse_interrupts_extended(void) of_node_put(np); } +struct of_unittest_expected_imap_item { + u32 child_imap_count; + u32 child_imap[2]; + const char *parent_path; + int parent_args_count; + u32 parent_args[3]; +}; + +static const struct of_unittest_expected_imap_item of_unittest_expected_imap_items[] = { + { + .child_imap_count = 2, + .child_imap = {1, 11}, + .parent_path = "/testcase-data/interrupts/intc0", + .parent_args_count = 1, + .parent_args = {100}, + }, { + .child_imap_count = 2, + .child_imap = {2, 22}, + .parent_path = "/testcase-data/interrupts/intc1", + .parent_args_count = 3, + .parent_args = {200, 201, 202}, + }, { + .child_imap_count = 2, + .child_imap = {3, 33}, + .parent_path = "/testcase-data/interrupts/intc2", + .parent_args_count = 2, + .parent_args = {300, 301}, + }, { + .child_imap_count = 2, + .child_imap = {4, 44}, + .parent_path = "/testcase-data/interrupts/intc2", + .parent_args_count = 2, + .parent_args = {400, 401}, + } +}; + +static void __init of_unittest_parse_interrupt_map(void) +{ + const struct of_unittest_expected_imap_item *expected_item; + struct device_node *imap_np, *expected_parent_np; + struct of_imap_parser imap_parser; + struct of_imap_item imap_item; + int count, ret, i; + + if (of_irq_workarounds & (OF_IMAP_NO_PHANDLE | OF_IMAP_OLDWORLD_MAC)) + return; + + imap_np = of_find_node_by_path("/testcase-data/interrupts/intmap2"); + if (!imap_np) { + pr_err("missing testcase data\n"); + return; + } + + ret = of_imap_parser_init(&imap_parser, imap_np, &imap_item); + if (unittest(!ret, "of_imap_parser_init(%pOF) returned error %d\n", + imap_np, ret)) + goto end; + + expected_item = of_unittest_expected_imap_items; + count = 0; + + for_each_of_imap_item(&imap_parser, &imap_item) { + if (unittest(count < ARRAY_SIZE(of_unittest_expected_imap_items), + "imap item number %d not expected. Max number %zu\n", + count, ARRAY_SIZE(of_unittest_expected_imap_items) - 1)) { + of_node_put(imap_item.parent_args.np); + goto end; + } + + expected_parent_np = of_find_node_by_path(expected_item->parent_path); + if (unittest(expected_parent_np, + "missing dependent testcase data (%s)\n", + expected_item->parent_path)) { + of_node_put(imap_item.parent_args.np); + goto end; + } + + unittest(imap_item.child_imap_count == expected_item->child_imap_count, + "imap[%d] child_imap_count = %u, expected %u\n", + count, imap_item.child_imap_count, + expected_item->child_imap_count); + + for (i = 0; i < expected_item->child_imap_count; i++) + unittest(imap_item.child_imap[i] == expected_item->child_imap[i], + "imap[%d] child_imap[%d] = %u, expected %u\n", + count, i, imap_item.child_imap[i], + expected_item->child_imap[i]); + + unittest(imap_item.parent_args.np == expected_parent_np, + "imap[%d] parent np = %pOF, expected %pOF\n", + count, imap_item.parent_args.np, expected_parent_np); + + unittest(imap_item.parent_args.args_count == expected_item->parent_args_count, + "imap[%d] parent param_count = %d, expected %d\n", + count, imap_item.parent_args.args_count, + expected_item->parent_args_count); + + for (i = 0; i < expected_item->parent_args_count; i++) + unittest(imap_item.parent_args.args[i] == expected_item->parent_args[i], + "imap[%d] parent param[%d] = %u, expected %u\n", + count, i, imap_item.parent_args.args[i], + expected_item->parent_args[i]); + + of_node_put(expected_parent_np); + count++; + expected_item++; + } + + unittest(count == ARRAY_SIZE(of_unittest_expected_imap_items), + "Missing items. %d parsed, expected %zu\n", + count, ARRAY_SIZE(of_unittest_expected_imap_items)); +end: + of_node_put(imap_np); +} + #if IS_ENABLED(CONFIG_OF_DYNAMIC) static void __init of_unittest_irq_refcount(void) { @@ -4395,6 +4510,7 @@ static int __init of_unittest(void) of_unittest_changeset_prop(); of_unittest_parse_interrupts(); of_unittest_parse_interrupts_extended(); + of_unittest_parse_interrupt_map(); of_unittest_irq_refcount(); of_unittest_dma_get_max_cpu_address(); of_unittest_parse_dma_ranges(); From 3ac6dfe3d7a2396602b67667249b146504dfbd2a Mon Sep 17 00:00:00 2001 From: "Herve Codina (Schneider Electric)" Date: Wed, 14 Jan 2026 10:39:32 +0100 Subject: [PATCH 121/171] irqchip/ls-extirq: Use for_each_of_imap_item iterator The ls-extirq driver parses the interrupt-map property. It does it using open code. Recently for_each_of_imap_item iterator has been introduce to help drivers in this parsing. Convert the ls-extirq driver to use the for_each_of_imap_item iterator instead of open code. Signed-off-by: Herve Codina (Schneider Electric) Reviewed-by: Linus Walleij Reviewed-by: Thomas Gleixner Link: https://patch.msgid.link/20260114093938.1089936-4-herve.codina@bootlin.com Signed-off-by: Geert Uytterhoeven --- drivers/irqchip/irq-ls-extirq.c | 47 ++++++++++++--------------------- 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/drivers/irqchip/irq-ls-extirq.c b/drivers/irqchip/irq-ls-extirq.c index 50a7b38381b9..ed8755777349 100644 --- a/drivers/irqchip/irq-ls-extirq.c +++ b/drivers/irqchip/irq-ls-extirq.c @@ -125,45 +125,32 @@ static const struct irq_domain_ops extirq_domain_ops = { static int ls_extirq_parse_map(struct ls_extirq_data *priv, struct device_node *node) { - const __be32 *map; - u32 mapsize; + struct of_imap_parser imap_parser; + struct of_imap_item imap_item; int ret; - map = of_get_property(node, "interrupt-map", &mapsize); - if (!map) - return -ENOENT; - if (mapsize % sizeof(*map)) - return -EINVAL; - mapsize /= sizeof(*map); + ret = of_imap_parser_init(&imap_parser, node, &imap_item); + if (ret) + return ret; - while (mapsize) { + for_each_of_imap_item(&imap_parser, &imap_item) { struct device_node *ipar; - u32 hwirq, intsize, j; + u32 hwirq; + int i; - if (mapsize < 3) - return -EINVAL; - hwirq = be32_to_cpup(map); - if (hwirq >= MAXIRQ) + hwirq = imap_item.child_imap[0]; + if (hwirq >= MAXIRQ) { + of_node_put(imap_item.parent_args.np); return -EINVAL; + } priv->nirq = max(priv->nirq, hwirq + 1); - ipar = of_find_node_by_phandle(be32_to_cpup(map + 2)); - map += 3; - mapsize -= 3; - if (!ipar) - return -EINVAL; - priv->map[hwirq].fwnode = &ipar->fwnode; - ret = of_property_read_u32(ipar, "#interrupt-cells", &intsize); - if (ret) - return ret; + ipar = of_node_get(imap_item.parent_args.np); + priv->map[hwirq].fwnode = of_fwnode_handle(ipar); - if (intsize > mapsize) - return -EINVAL; - - priv->map[hwirq].param_count = intsize; - for (j = 0; j < intsize; ++j) - priv->map[hwirq].param[j] = be32_to_cpup(map++); - mapsize -= intsize; + priv->map[hwirq].param_count = imap_item.parent_args.args_count; + for (i = 0; i < priv->map[hwirq].param_count; i++) + priv->map[hwirq].param[i] = imap_item.parent_args.args[i]; } return 0; } From 94ed03a00571cd7e648b1dfa0136103d8a14fd10 Mon Sep 17 00:00:00 2001 From: "Herve Codina (Schneider Electric)" Date: Wed, 14 Jan 2026 10:39:33 +0100 Subject: [PATCH 122/171] irqchip/renesas-rza1: Use for_each_of_imap_item iterator The renesas-rza1 driver parses the interrupt-map property. It does it using open code. Recently for_each_of_imap_item iterator has been introduce to help drivers in this parsing. Convert the renesas-rza1 driver to use the for_each_of_imap_item iterator instead of open code. Signed-off-by: Herve Codina (Schneider Electric) Tested-by: Wolfram Sang Reviewed-by: Linus Walleij Reviewed-by: Thomas Gleixner Link: https://patch.msgid.link/20260114093938.1089936-5-herve.codina@bootlin.com Signed-off-by: Geert Uytterhoeven --- drivers/irqchip/irq-renesas-rza1.c | 43 +++++++++++------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/drivers/irqchip/irq-renesas-rza1.c b/drivers/irqchip/irq-renesas-rza1.c index 6047a524ac77..370d968b2398 100644 --- a/drivers/irqchip/irq-renesas-rza1.c +++ b/drivers/irqchip/irq-renesas-rza1.c @@ -142,47 +142,36 @@ static const struct irq_domain_ops rza1_irqc_domain_ops = { static int rza1_irqc_parse_map(struct rza1_irqc_priv *priv, struct device_node *gic_node) { + struct of_imap_parser imap_parser; struct device *dev = priv->dev; - unsigned int imaplen, i, j; + struct of_imap_item imap_item; struct device_node *ipar; - const __be32 *imap; - u32 intsize; + unsigned int j; + u32 i = 0; int ret; - imap = of_get_property(dev->of_node, "interrupt-map", &imaplen); - if (!imap) - return -EINVAL; - - for (i = 0; i < IRQC_NUM_IRQ; i++) { - if (imaplen < 3) - return -EINVAL; + ret = of_imap_parser_init(&imap_parser, dev->of_node, &imap_item); + if (ret) + return ret; + for_each_of_imap_item(&imap_parser, &imap_item) { /* Check interrupt number, ignore sense */ - if (be32_to_cpup(imap) != i) + if (imap_item.child_imap[0] != i) { + of_node_put(imap_item.parent_args.np); return -EINVAL; + } - ipar = of_find_node_by_phandle(be32_to_cpup(imap + 2)); + ipar = imap_item.parent_args.np; if (ipar != gic_node) { of_node_put(ipar); return -EINVAL; } - imap += 3; - imaplen -= 3; + priv->map[i].args_count = imap_item.parent_args.args_count; + for (j = 0; j < priv->map[i].args_count; j++) + priv->map[i].args[j] = imap_item.parent_args.args[j]; - ret = of_property_read_u32(ipar, "#interrupt-cells", &intsize); - of_node_put(ipar); - if (ret) - return ret; - - if (imaplen < intsize) - return -EINVAL; - - priv->map[i].args_count = intsize; - for (j = 0; j < intsize; j++) - priv->map[i].args[j] = be32_to_cpup(imap++); - - imaplen -= intsize; + i++; } return 0; From 49261f479d45714692587ff0abe0c5279c6068d6 Mon Sep 17 00:00:00 2001 From: "Herve Codina (Schneider Electric)" Date: Wed, 14 Jan 2026 10:39:36 +0100 Subject: [PATCH 123/171] soc: renesas: Add support for RZ/N1 GPIO Interrupt Multiplexer On the Renesas RZ/N1 SoC, GPIOs can generate interruptions. Those interruption lines are multiplexed by the GPIO Interrupt Multiplexer in order to map 32 * 3 GPIO interrupt lines to 8 GIC interrupt lines. The GPIO interrupt multiplexer IP does nothing but select 8 GPIO IRQ lines out of the 96 available to wire them to the GIC input lines. Signed-off-by: Herve Codina (Schneider Electric) Reviewed-by: Linus Walleij Tested-by: Wolfram Sang Reviewed-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260114093938.1089936-8-herve.codina@bootlin.com Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/Kconfig | 4 + drivers/soc/renesas/Makefile | 1 + drivers/soc/renesas/rzn1_irqmux.c | 127 ++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 drivers/soc/renesas/rzn1_irqmux.c diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 4c5e4877a1f1..d84f20175ea3 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -62,6 +62,7 @@ config ARCH_RZN1 select PM select PM_GENERIC_DOMAINS select ARM_AMBA + select RZN1_IRQMUX if GPIO_DWAPB if ARM && ARCH_RENESAS @@ -460,6 +461,9 @@ config PWC_RZV2M config RST_RCAR bool "Reset Controller support for R-Car" if COMPILE_TEST +config RZN1_IRQMUX + bool "Renesas RZ/N1 GPIO IRQ multiplexer support" if COMPILE_TEST + config SYSC_RZ bool "System controller for RZ SoCs" if COMPILE_TEST select MFD_SYSCON diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index 3bdcc6a395d5..33d44d964d61 100644 --- a/drivers/soc/renesas/Makefile +++ b/drivers/soc/renesas/Makefile @@ -14,4 +14,5 @@ obj-$(CONFIG_SYS_R9A09G057) += r9a09g057-sys.o # Family obj-$(CONFIG_PWC_RZV2M) += pwc-rzv2m.o obj-$(CONFIG_RST_RCAR) += rcar-rst.o +obj-$(CONFIG_RZN1_IRQMUX) += rzn1_irqmux.o obj-$(CONFIG_SYSC_RZ) += rz-sysc.o diff --git a/drivers/soc/renesas/rzn1_irqmux.c b/drivers/soc/renesas/rzn1_irqmux.c new file mode 100644 index 000000000000..b50b295f83d7 --- /dev/null +++ b/drivers/soc/renesas/rzn1_irqmux.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RZ/N1 GPIO Interrupt Multiplexer + * + * Copyright 2025 Schneider Electric + * Author: Herve Codina + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Up to 8 output lines are connected to GIC SPI interrupt controller + * starting at IRQ 103. + */ +#define RZN1_IRQMUX_GIC_SPI_BASE 103 +#define RZN1_IRQMUX_NUM_OUTPUTS 8 + +static int rzn1_irqmux_parent_args_to_line_index(struct device *dev, + const struct of_phandle_args *parent_args) +{ + /* + * The parent interrupt should be one of the GIC controller. + * Three arguments must be provided. + * - args[0]: GIC_SPI + * - args[1]: The GIC interrupt number + * - args[2]: The interrupt flags + * + * We retrieve the line index based on the GIC interrupt number + * provided. + */ + + if (parent_args->args_count != 3 || parent_args->args[0] != GIC_SPI) { + dev_err(dev, "Invalid interrupt-map item\n"); + return -EINVAL; + } + + if (parent_args->args[1] < RZN1_IRQMUX_GIC_SPI_BASE || + parent_args->args[1] >= RZN1_IRQMUX_GIC_SPI_BASE + RZN1_IRQMUX_NUM_OUTPUTS) { + dev_err(dev, "Invalid GIC interrupt %u\n", parent_args->args[1]); + return -EINVAL; + } + + return parent_args->args[1] - RZN1_IRQMUX_GIC_SPI_BASE; +} + +static int rzn1_irqmux_probe(struct platform_device *pdev) +{ + DECLARE_BITMAP(index_done, RZN1_IRQMUX_NUM_OUTPUTS) = {}; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct of_imap_parser imap_parser; + struct of_imap_item imap_item; + u32 __iomem *regs; + int index; + int ret; + u32 tmp; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + /* We support only #interrupt-cells = <1> and #address-cells = <0> */ + ret = of_property_read_u32(np, "#interrupt-cells", &tmp); + if (ret) + return ret; + if (tmp != 1) + return -EINVAL; + + ret = of_property_read_u32(np, "#address-cells", &tmp); + if (ret) + return ret; + if (tmp != 0) + return -EINVAL; + + ret = of_imap_parser_init(&imap_parser, np, &imap_item); + if (ret) + return ret; + + for_each_of_imap_item(&imap_parser, &imap_item) { + index = rzn1_irqmux_parent_args_to_line_index(dev, &imap_item.parent_args); + if (index < 0) { + of_node_put(imap_item.parent_args.np); + return index; + } + + if (test_and_set_bit(index, index_done)) { + of_node_put(imap_item.parent_args.np); + dev_err(dev, "Mux output line %d already defined in interrupt-map\n", + index); + return -EINVAL; + } + + /* + * The child #address-cells is 0 (already checked). The first + * value in imap item is the src hwirq. + */ + writel(imap_item.child_imap[0], regs + index); + } + + return 0; +} + +static const struct of_device_id rzn1_irqmux_of_match[] = { + { .compatible = "renesas,rzn1-gpioirqmux", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rzn1_irqmux_of_match); + +static struct platform_driver rzn1_irqmux_driver = { + .probe = rzn1_irqmux_probe, + .driver = { + .name = "rzn1_irqmux", + .of_match_table = rzn1_irqmux_of_match, + }, +}; +module_platform_driver(rzn1_irqmux_driver); + +MODULE_AUTHOR("Herve Codina "); +MODULE_DESCRIPTION("Renesas RZ/N1 GPIO IRQ Multiplexer Driver"); +MODULE_LICENSE("GPL"); From 73503de518bdb3adaaeee5d4551d4675a5e054bc Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 15 Jan 2026 15:22:42 +0100 Subject: [PATCH 124/171] MAINTAINERS: Mark the OP-TEE mailing list moderated After sending a patch to op-tee@lists.trustedfirmware.org, I got the typical response for a moderated list: Your mail to 'op-tee@lists.trustedfirmware.org' with the subject [...] Is being held until the list moderator can review it for approval. The message is being held because: The message is not from a list member Either the message will get posted to the list, or you will receive notification of the moderator's decision. Mark this mailing list moderated in MAINTAINERS. Signed-off-by: Geert Uytterhoeven Reviewed-by: Jens Wiklander Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- MAINTAINERS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 5b11839cba9d..ad3665999d75 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19511,14 +19511,14 @@ F: drivers/net/phy/ncn* OP-TEE DRIVER M: Jens Wiklander -L: op-tee@lists.trustedfirmware.org +L: op-tee@lists.trustedfirmware.org (moderated for non-subscribers) S: Maintained F: Documentation/ABI/testing/sysfs-bus-optee-devices F: drivers/tee/optee/ OP-TEE RANDOM NUMBER GENERATOR (RNG) DRIVER M: Sumit Garg -L: op-tee@lists.trustedfirmware.org +L: op-tee@lists.trustedfirmware.org (moderated for non-subscribers) S: Maintained F: drivers/char/hw_random/optee-rng.c @@ -25617,7 +25617,7 @@ F: include/media/i2c/tw9910.h TEE SUBSYSTEM M: Jens Wiklander R: Sumit Garg -L: op-tee@lists.trustedfirmware.org +L: op-tee@lists.trustedfirmware.org (moderated for non-subscribers) S: Maintained F: Documentation/ABI/testing/sysfs-class-tee F: Documentation/driver-api/tee.rst From 94ea7063fae835e800768d3b0507f0994ef03878 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 9 Jan 2026 15:44:42 +0000 Subject: [PATCH 125/171] optee: make read-only array attr static const Don't populate the read-only array attr on the stack at run time, instead make it static const. Signed-off-by: Colin Ian King Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/optee/rpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index 97fc5b14db0c..1758eb7e6e8b 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -43,7 +43,7 @@ static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx, struct i2c_msg msg = { }; size_t i; int ret = -EOPNOTSUPP; - u8 attr[] = { + static const u8 attr[] = { TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT, From 3cdc30c42d4a87444f6c7afbefd6a9381c4caa27 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 16 Jan 2026 08:55:28 +0800 Subject: [PATCH 126/171] soc: rockchip: grf: Fix wrong RK3576_IOCGRF_MISC_CON definition RK3576_IOCGRF_MISC_CON is IOC_GRF + 0x40F0, fix it. Fixes: e1aaecacfa13 ("soc: rockchip: grf: Add rk3576 default GRF values") Cc: stable@vger.kernel.org Cc: Detlev Casanova Signed-off-by: Shawn Lin Reviewed-by: Chaoyi Chen Tested-by: Marco Schirrmeister Link: https://patch.msgid.link/1768524932-163929-2-git-send-email-shawn.lin@rock-chips.com Signed-off-by: Heiko Stuebner --- drivers/soc/rockchip/grf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/rockchip/grf.c b/drivers/soc/rockchip/grf.c index 27bfa09ff251..8974d1c6b35d 100644 --- a/drivers/soc/rockchip/grf.c +++ b/drivers/soc/rockchip/grf.c @@ -146,7 +146,7 @@ static const struct rockchip_grf_info rk3576_sysgrf __initconst = { .num_values = ARRAY_SIZE(rk3576_defaults_sys_grf), }; -#define RK3576_IOCGRF_MISC_CON 0x04F0 +#define RK3576_IOCGRF_MISC_CON 0x40F0 static const struct rockchip_grf_value rk3576_defaults_ioc_grf[] __initconst = { { "jtag switching", RK3576_IOCGRF_MISC_CON, FIELD_PREP_WM16_CONST(BIT(1), 0) }, From 75fb63ae031211e9264ac888fabc2ca9cd3fcccf Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 16 Jan 2026 08:55:29 +0800 Subject: [PATCH 127/171] soc: rockchip: grf: Support multiple grf to be handled Currently, only the first matched node will be handled. This leads to jtag switching broken for RK3576, as rk3576-sys-grf is found before rk3576-ioc-grf. Change the code to scan all the possible node to fix the problem. Fixes: e1aaecacfa13 ("soc: rockchip: grf: Add rk3576 default GRF values") Cc: stable@vger.kernel.org Cc: Detlev Casanova Signed-off-by: Shawn Lin Tested-by: Marco Schirrmeister Link: https://patch.msgid.link/1768524932-163929-3-git-send-email-shawn.lin@rock-chips.com Signed-off-by: Heiko Stuebner --- drivers/soc/rockchip/grf.c | 47 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/drivers/soc/rockchip/grf.c b/drivers/soc/rockchip/grf.c index 8974d1c6b35d..04937c40da47 100644 --- a/drivers/soc/rockchip/grf.c +++ b/drivers/soc/rockchip/grf.c @@ -217,34 +217,33 @@ static int __init rockchip_grf_init(void) struct regmap *grf; int ret, i; - np = of_find_matching_node_and_match(NULL, rockchip_grf_dt_match, - &match); - if (!np) - return -ENODEV; - if (!match || !match->data) { - pr_err("%s: missing grf data\n", __func__); - of_node_put(np); - return -EINVAL; - } + for_each_matching_node_and_match(np, rockchip_grf_dt_match, &match) { + if (!of_device_is_available(np)) + continue; + if (!match || !match->data) { + pr_err("%s: missing grf data\n", __func__); + of_node_put(np); + return -EINVAL; + } - grf_info = match->data; + grf_info = match->data; - grf = syscon_node_to_regmap(np); - of_node_put(np); - if (IS_ERR(grf)) { - pr_err("%s: could not get grf syscon\n", __func__); - return PTR_ERR(grf); - } + grf = syscon_node_to_regmap(np); + if (IS_ERR(grf)) { + pr_err("%s: could not get grf syscon\n", __func__); + return PTR_ERR(grf); + } - for (i = 0; i < grf_info->num_values; i++) { - const struct rockchip_grf_value *val = &grf_info->values[i]; + for (i = 0; i < grf_info->num_values; i++) { + const struct rockchip_grf_value *val = &grf_info->values[i]; - pr_debug("%s: adjusting %s in %#6x to %#10x\n", __func__, - val->desc, val->reg, val->val); - ret = regmap_write(grf, val->reg, val->val); - if (ret < 0) - pr_err("%s: write to %#6x failed with %d\n", - __func__, val->reg, ret); + pr_debug("%s: adjusting %s in %#6x to %#10x\n", __func__, + val->desc, val->reg, val->val); + ret = regmap_write(grf, val->reg, val->val); + if (ret < 0) + pr_err("%s: write to %#6x failed with %d\n", + __func__, val->reg, ret); + } } return 0; From 5a6d033c4905d78c9c05b1cab36c7e03951fab9e Mon Sep 17 00:00:00 2001 From: Alexander Wilhelm Date: Wed, 19 Nov 2025 11:40:05 +0100 Subject: [PATCH 128/171] soc: qcom: check QMI basic element error codes Extend handling of QMI basic element types to also capture error codes. This is required for big-endian platforms where a simple memcpy is not sufficient and exact data type knowledge is necessary. Signed-off-by: Alexander Wilhelm Link: https://lore.kernel.org/r/20251119104008.3505152-2-alexander.wilhelm@westermo.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qmi_encdec.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/soc/qcom/qmi_encdec.c b/drivers/soc/qcom/qmi_encdec.c index 7660a960fb45..1d2d9e515870 100644 --- a/drivers/soc/qcom/qmi_encdec.c +++ b/drivers/soc/qcom/qmi_encdec.c @@ -267,11 +267,15 @@ static int qmi_encode_string_elem(const struct qmi_elem_info *ei_array, } rc = qmi_encode_basic_elem(buf_dst, &string_len, 1, string_len_sz); + if (rc < 0) + return rc; encoded_bytes += rc; } rc = qmi_encode_basic_elem(buf_dst + encoded_bytes, buf_src, string_len, temp_ei->elem_size); + if (rc < 0) + return rc; encoded_bytes += rc; return encoded_bytes; @@ -333,6 +337,8 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf, case QMI_OPT_FLAG: rc = qmi_encode_basic_elem(&opt_flag_value, buf_src, 1, sizeof(u8)); + if (rc < 0) + return rc; if (opt_flag_value) temp_ei = temp_ei + 1; else @@ -354,11 +360,15 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf, data_len_value = (u32)val8; rc = qmi_encode_basic_elem(buf_dst, &val8, 1, data_len_sz); + if (rc < 0) + return rc; } else { val16 = *(u16 *)buf_src; data_len_value = (u32)le16_to_cpu(val16); rc = qmi_encode_basic_elem(buf_dst, &val16, 1, data_len_sz); + if (rc < 0) + return rc; } UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst, encoded_bytes, tlv_len, @@ -386,6 +396,8 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf, rc = qmi_encode_basic_elem(buf_dst, buf_src, data_len_value, temp_ei->elem_size); + if (rc < 0) + return rc; UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst, encoded_bytes, tlv_len, encode_tlv, rc); @@ -544,10 +556,14 @@ static int qmi_decode_string_elem(const struct qmi_elem_info *ei_array, if (string_len_sz == sizeof(u8)) { rc = qmi_decode_basic_elem(&val8, buf_src, 1, string_len_sz); + if (rc < 0) + return rc; string_len = (u32)val8; } else { rc = qmi_decode_basic_elem(&val16, buf_src, 1, string_len_sz); + if (rc < 0) + return rc; string_len = (u32)val16; } decoded_bytes += rc; @@ -565,6 +581,8 @@ static int qmi_decode_string_elem(const struct qmi_elem_info *ei_array, rc = qmi_decode_basic_elem(buf_dst, buf_src + decoded_bytes, string_len, temp_ei->elem_size); + if (rc < 0) + return rc; *((char *)buf_dst + string_len) = '\0'; decoded_bytes += rc; @@ -667,10 +685,14 @@ static int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct, if (data_len_sz == sizeof(u8)) { rc = qmi_decode_basic_elem(&val8, buf_src, 1, data_len_sz); + if (rc < 0) + return rc; data_len_value = (u32)val8; } else { rc = qmi_decode_basic_elem(&val16, buf_src, 1, data_len_sz); + if (rc < 0) + return rc; data_len_value = (u32)val16; } val32 = cpu_to_le32(data_len_value); @@ -701,6 +723,8 @@ static int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct, rc = qmi_decode_basic_elem(buf_dst, buf_src, data_len_value, temp_ei->elem_size); + if (rc < 0) + return rc; UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc); break; From d9c83903be080a6bc25ccabaafe5487836a7e1a7 Mon Sep 17 00:00:00 2001 From: Alexander Wilhelm Date: Wed, 19 Nov 2025 11:40:06 +0100 Subject: [PATCH 129/171] soc: qcom: fix QMI encoding/decoding for basic elements Extend the QMI byte encoding and decoding logic to support multiple basic data type sizes (u8, u16, u32, u64) using differnet macros for each type. Ensure correct handling of data sizes and proper byte order conversion on big-endian platforms by consistently applying these macros during encoding and decoding of basic elements. Signed-off-by: Alexander Wilhelm Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20251119104008.3505152-3-alexander.wilhelm@westermo.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qmi_encdec.c | 102 ++++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 12 deletions(-) diff --git a/drivers/soc/qcom/qmi_encdec.c b/drivers/soc/qcom/qmi_encdec.c index 1d2d9e515870..030b18fa086a 100644 --- a/drivers/soc/qcom/qmi_encdec.c +++ b/drivers/soc/qcom/qmi_encdec.c @@ -23,18 +23,60 @@ *p_length |= ((u8)*p_src) << 8; \ } while (0) -#define QMI_ENCDEC_ENCODE_N_BYTES(p_dst, p_src, size) \ +#define QMI_ENCDEC_ENCODE_U8(p_dst, p_src) \ do { \ - memcpy(p_dst, p_src, size); \ - p_dst = (u8 *)p_dst + size; \ - p_src = (u8 *)p_src + size; \ + memcpy(p_dst, p_src, sizeof(u8)); \ + p_dst = (u8 *)p_dst + sizeof(u8); \ + p_src = (u8 *)p_src + sizeof(u8); \ } while (0) -#define QMI_ENCDEC_DECODE_N_BYTES(p_dst, p_src, size) \ +#define QMI_ENCDEC_ENCODE_U16(p_dst, p_src) \ do { \ - memcpy(p_dst, p_src, size); \ - p_dst = (u8 *)p_dst + size; \ - p_src = (u8 *)p_src + size; \ + *(__le16 *)p_dst = __cpu_to_le16(*(u16 *)p_src); \ + p_dst = (u8 *)p_dst + sizeof(u16); \ + p_src = (u8 *)p_src + sizeof(u16); \ +} while (0) + +#define QMI_ENCDEC_ENCODE_U32(p_dst, p_src) \ +do { \ + *(__le32 *)p_dst = __cpu_to_le32(*(u32 *)p_src); \ + p_dst = (u8 *)p_dst + sizeof(u32); \ + p_src = (u8 *)p_src + sizeof(u32); \ +} while (0) + +#define QMI_ENCDEC_ENCODE_U64(p_dst, p_src) \ +do { \ + *(__le64 *)p_dst = __cpu_to_le64(*(u64 *)p_src); \ + p_dst = (u8 *)p_dst + sizeof(u64); \ + p_src = (u8 *)p_src + sizeof(u64); \ +} while (0) + +#define QMI_ENCDEC_DECODE_U8(p_dst, p_src) \ +do { \ + memcpy(p_dst, p_src, sizeof(u8)); \ + p_dst = (u8 *)p_dst + sizeof(u8); \ + p_src = (u8 *)p_src + sizeof(u8); \ +} while (0) + +#define QMI_ENCDEC_DECODE_U16(p_dst, p_src) \ +do { \ + *(u16 *)p_dst = __le16_to_cpu(*(__le16 *)p_src); \ + p_dst = (u8 *)p_dst + sizeof(u16); \ + p_src = (u8 *)p_src + sizeof(u16); \ +} while (0) + +#define QMI_ENCDEC_DECODE_U32(p_dst, p_src) \ +do { \ + *(u32 *)p_dst = __le32_to_cpu(*(__le32 *)p_src); \ + p_dst = (u8 *)p_dst + sizeof(u32); \ + p_src = (u8 *)p_src + sizeof(u32); \ +} while (0) + +#define QMI_ENCDEC_DECODE_U64(p_dst, p_src) \ +do { \ + *(u64 *)p_dst = __le64_to_cpu(*(__le64 *)p_src); \ + p_dst = (u8 *)p_dst + sizeof(u64); \ + p_src = (u8 *)p_src + sizeof(u64); \ } while (0) #define UPDATE_ENCODE_VARIABLES(temp_si, buf_dst, \ @@ -161,7 +203,8 @@ static int qmi_calc_min_msg_len(const struct qmi_elem_info *ei_array, * of primary data type which include u8 - u64 or similar. This * function returns the number of bytes of encoded information. * - * Return: The number of bytes of encoded information. + * Return: The number of bytes of encoded information on success or negative + * errno on error. */ static int qmi_encode_basic_elem(void *buf_dst, const void *buf_src, u32 elem_len, u32 elem_size) @@ -169,7 +212,24 @@ static int qmi_encode_basic_elem(void *buf_dst, const void *buf_src, u32 i, rc = 0; for (i = 0; i < elem_len; i++) { - QMI_ENCDEC_ENCODE_N_BYTES(buf_dst, buf_src, elem_size); + switch (elem_size) { + case sizeof(u8): + QMI_ENCDEC_ENCODE_U8(buf_dst, buf_src); + break; + case sizeof(u16): + QMI_ENCDEC_ENCODE_U16(buf_dst, buf_src); + break; + case sizeof(u32): + QMI_ENCDEC_ENCODE_U32(buf_dst, buf_src); + break; + case sizeof(u64): + QMI_ENCDEC_ENCODE_U64(buf_dst, buf_src); + break; + default: + pr_err("%s: Unrecognized element size\n", __func__); + return -EINVAL; + } + rc += elem_size; } @@ -456,7 +516,8 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf, * of primary data type which include u8 - u64 or similar. This * function returns the number of bytes of decoded information. * - * Return: The total size of the decoded data elements, in bytes. + * Return: The total size of the decoded data elements, in bytes, on success or + * negative errno on error. */ static int qmi_decode_basic_elem(void *buf_dst, const void *buf_src, u32 elem_len, u32 elem_size) @@ -464,7 +525,24 @@ static int qmi_decode_basic_elem(void *buf_dst, const void *buf_src, u32 i, rc = 0; for (i = 0; i < elem_len; i++) { - QMI_ENCDEC_DECODE_N_BYTES(buf_dst, buf_src, elem_size); + switch (elem_size) { + case sizeof(u8): + QMI_ENCDEC_DECODE_U8(buf_dst, buf_src); + break; + case sizeof(u16): + QMI_ENCDEC_DECODE_U16(buf_dst, buf_src); + break; + case sizeof(u32): + QMI_ENCDEC_DECODE_U32(buf_dst, buf_src); + break; + case sizeof(u64): + QMI_ENCDEC_DECODE_U64(buf_dst, buf_src); + break; + default: + pr_err("%s: Unrecognized element size\n", __func__); + return -EINVAL; + } + rc += elem_size; } From fe099c387e06b566840449ac21008db1b25ad1f4 Mon Sep 17 00:00:00 2001 From: Alexander Wilhelm Date: Wed, 19 Nov 2025 11:40:07 +0100 Subject: [PATCH 130/171] soc: qcom: preserve CPU endianness for QMI_DATA_LEN To ensure correct handling of endianness in the QMI subsystem, the QMI_DATA_LEN field used in host-side drivers remains in CPU-native byte order. Remove unnecessary endianness conversions, considering that QMI_DATA_LEN is always of type `u32` on the host. On the QMI wire interface, however, its representation is variable and may use either 1 or 2 bytes. Signed-off-by: Alexander Wilhelm Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20251119104008.3505152-4-alexander.wilhelm@westermo.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qmi_encdec.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/soc/qcom/qmi_encdec.c b/drivers/soc/qcom/qmi_encdec.c index 030b18fa086a..28ce6f130b6a 100644 --- a/drivers/soc/qcom/qmi_encdec.c +++ b/drivers/soc/qcom/qmi_encdec.c @@ -406,6 +406,7 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf, break; case QMI_DATA_LEN: + memcpy(&data_len_value, buf_src, sizeof(u32)); data_len_sz = temp_ei->elem_size == sizeof(u8) ? sizeof(u8) : sizeof(u16); /* Check to avoid out of range buffer access */ @@ -416,15 +417,13 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf, return -ETOOSMALL; } if (data_len_sz == sizeof(u8)) { - val8 = *(u8 *)buf_src; - data_len_value = (u32)val8; + val8 = data_len_value; rc = qmi_encode_basic_elem(buf_dst, &val8, 1, data_len_sz); if (rc < 0) return rc; } else { - val16 = *(u16 *)buf_src; - data_len_value = (u32)le16_to_cpu(val16); + val16 = data_len_value; rc = qmi_encode_basic_elem(buf_dst, &val16, 1, data_len_sz); if (rc < 0) @@ -721,7 +720,6 @@ static int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct, int rc; u8 val8; u16 val16; - u32 val32; while (decoded_bytes < in_buf_len) { if (dec_level >= 2 && temp_ei->data_type == QMI_EOTI) @@ -773,8 +771,7 @@ static int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct, return rc; data_len_value = (u32)val16; } - val32 = cpu_to_le32(data_len_value); - memcpy(buf_dst, &val32, sizeof(u32)); + memcpy(buf_dst, &data_len_value, sizeof(u32)); temp_ei = temp_ei + 1; buf_dst = out_c_struct + temp_ei->offset; tlv_len -= data_len_sz; From 4fa62e80c7dba7ff419dea23f10bcb125e38bde3 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 29 Oct 2025 20:07:01 +0800 Subject: [PATCH 131/171] firmware: arm_scmi: imx: Support getting syslog of MISC protocol MISC protocol supports getting system log regarding system sleep latency, wakeup interrupt and etc. Add the API for user to retrieve the information from SM. Signed-off-by: Peng Fan Acked-by: Sudeep Holla Signed-off-by: Shawn Guo --- .../arm_scmi/vendors/imx/imx-sm-misc.c | 83 +++++++++++++++++++ include/linux/scmi_imx_protocol.h | 2 + 2 files changed, 85 insertions(+) diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c index 700a3f24f4ef..eae0b0562f6c 100644 --- a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c +++ b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c @@ -28,6 +28,7 @@ enum scmi_imx_misc_protocol_cmd { SCMI_IMX_MISC_DISCOVER_BUILD_INFO = 0x6, SCMI_IMX_MISC_CTRL_NOTIFY = 0x8, SCMI_IMX_MISC_CFG_INFO_GET = 0xC, + SCMI_IMX_MISC_SYSLOG_GET = 0xD, SCMI_IMX_MISC_BOARD_INFO = 0xE, }; @@ -89,6 +90,19 @@ struct scmi_imx_misc_cfg_info_out { u8 cfgname[MISC_MAX_CFGNAME]; }; +struct scmi_imx_misc_syslog_in { + __le32 flags; + __le32 index; +}; + +#define REMAINING(x) le32_get_bits((x), GENMASK(31, 20)) +#define RETURNED(x) le32_get_bits((x), GENMASK(11, 0)) + +struct scmi_imx_misc_syslog_out { + __le32 numlogflags; + __le32 syslog[]; +}; + static int scmi_imx_misc_attributes_get(const struct scmi_protocol_handle *ph, struct scmi_imx_misc_info *mi) { @@ -371,10 +385,79 @@ static int scmi_imx_misc_cfg_info_get(const struct scmi_protocol_handle *ph) return ret; } +struct scmi_imx_misc_syslog_ipriv { + u32 *array; + u16 *size; +}; + +static void iter_misc_syslog_prepare_message(void *message, u32 desc_index, + const void *priv) +{ + struct scmi_imx_misc_syslog_in *msg = message; + + msg->flags = cpu_to_le32(0); + msg->index = cpu_to_le32(desc_index); +} + +static int iter_misc_syslog_update_state(struct scmi_iterator_state *st, + const void *response, void *priv) +{ + const struct scmi_imx_misc_syslog_out *r = response; + struct scmi_imx_misc_syslog_ipriv *p = priv; + + st->num_returned = RETURNED(r->numlogflags); + st->num_remaining = REMAINING(r->numlogflags); + *p->size = st->num_returned + st->num_remaining; + + return 0; +} + +static int +iter_misc_syslog_process_response(const struct scmi_protocol_handle *ph, + const void *response, + struct scmi_iterator_state *st, void *priv) +{ + const struct scmi_imx_misc_syslog_out *r = response; + struct scmi_imx_misc_syslog_ipriv *p = priv; + + p->array[st->desc_index + st->loop_idx] = + le32_to_cpu(r->syslog[st->loop_idx]); + + return 0; +} + +static int scmi_imx_misc_syslog_get(const struct scmi_protocol_handle *ph, u16 *size, + void *array) +{ + struct scmi_iterator_ops ops = { + .prepare_message = iter_misc_syslog_prepare_message, + .update_state = iter_misc_syslog_update_state, + .process_response = iter_misc_syslog_process_response, + }; + struct scmi_imx_misc_syslog_ipriv ipriv = { + .array = array, + .size = size, + }; + void *iter; + + if (!array || !size || !*size) + return -EINVAL; + + iter = ph->hops->iter_response_init(ph, &ops, *size, SCMI_IMX_MISC_SYSLOG_GET, + sizeof(struct scmi_imx_misc_syslog_in), + &ipriv); + if (IS_ERR(iter)) + return PTR_ERR(iter); + + /* If firmware return NOT SUPPORTED, propagate value to caller */ + return ph->hops->iter_response_run(iter); +} + static const struct scmi_imx_misc_proto_ops scmi_imx_misc_proto_ops = { .misc_ctrl_set = scmi_imx_misc_ctrl_set, .misc_ctrl_get = scmi_imx_misc_ctrl_get, .misc_ctrl_req_notify = scmi_imx_misc_ctrl_notify, + .misc_syslog = scmi_imx_misc_syslog_get, }; static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph) diff --git a/include/linux/scmi_imx_protocol.h b/include/linux/scmi_imx_protocol.h index 27bd372cbfb1..2407d7693b6b 100644 --- a/include/linux/scmi_imx_protocol.h +++ b/include/linux/scmi_imx_protocol.h @@ -59,6 +59,8 @@ struct scmi_imx_misc_proto_ops { u32 *num, u32 *val); int (*misc_ctrl_req_notify)(const struct scmi_protocol_handle *ph, u32 ctrl_id, u32 evt_id, u32 flags); + int (*misc_syslog)(const struct scmi_protocol_handle *ph, u16 *size, + void *array); }; /* See LMM_ATTRIBUTES in imx95.rst */ From 80a4062e8821861c3ce7407ea5b23afa959917e2 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 29 Oct 2025 20:07:02 +0800 Subject: [PATCH 132/171] firmware: imx: sm-misc: Dump syslog info Add debugfs interface to read System Manager syslog info Signed-off-by: Peng Fan Signed-off-by: Shawn Guo --- drivers/firmware/imx/sm-misc.c | 37 +++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/imx/sm-misc.c b/drivers/firmware/imx/sm-misc.c index fc3ee12c2be8..0a8ada329c9d 100644 --- a/drivers/firmware/imx/sm-misc.c +++ b/drivers/firmware/imx/sm-misc.c @@ -3,12 +3,16 @@ * Copyright 2024 NXP */ +#include +#include #include #include #include #include #include #include +#include +#include static const struct scmi_imx_misc_proto_ops *imx_misc_ctrl_ops; static struct scmi_protocol_handle *ph; @@ -44,10 +48,38 @@ static int scmi_imx_misc_ctrl_notifier(struct notifier_block *nb, return 0; } +static int syslog_show(struct seq_file *file, void *priv) +{ + /* 4KB is large enough for syslog */ + void *syslog __free(kfree) = kmalloc(SZ_4K, GFP_KERNEL); + /* syslog API use num words, not num bytes */ + u16 size = SZ_4K / 4; + int ret; + + if (!ph) + return -ENODEV; + + ret = imx_misc_ctrl_ops->misc_syslog(ph, &size, syslog); + if (ret) + return ret; + + seq_hex_dump(file, " ", DUMP_PREFIX_NONE, 16, sizeof(u32), syslog, size * 4, false); + seq_putc(file, '\n'); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(syslog); + +static void scmi_imx_misc_put(void *p) +{ + debugfs_remove((struct dentry *)p); +} + static int scmi_imx_misc_ctrl_probe(struct scmi_device *sdev) { const struct scmi_handle *handle = sdev->handle; struct device_node *np = sdev->dev.of_node; + struct dentry *scmi_imx_dentry; u32 src_id, flags; int ret, i, num; @@ -98,7 +130,10 @@ static int scmi_imx_misc_ctrl_probe(struct scmi_device *sdev) } } - return 0; + scmi_imx_dentry = debugfs_create_dir("scmi_imx", NULL); + debugfs_create_file("syslog", 0444, scmi_imx_dentry, &sdev->dev, &syslog_fops); + + return devm_add_action_or_reset(&sdev->dev, scmi_imx_misc_put, scmi_imx_dentry); } static const struct scmi_device_id scmi_id_table[] = { From ab382a6ee25f0b571cffad66b2e6aaf0d2d245ec Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 17 Dec 2025 20:42:07 +0800 Subject: [PATCH 133/171] soc: imx: Use device-managed APIs for i.MX9 Use device-managed APi to simplify code. Signed-off-by: Peng Fan Reviewed-by: Frank Li Signed-off-by: Shawn Guo --- drivers/soc/imx/soc-imx9.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/drivers/soc/imx/soc-imx9.c b/drivers/soc/imx/soc-imx9.c index b46d22cf0212..0b1c59c7ddb2 100644 --- a/drivers/soc/imx/soc-imx9.c +++ b/drivers/soc/imx/soc-imx9.c @@ -18,6 +18,7 @@ static int imx9_soc_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct soc_device_attribute *attr; struct arm_smccc_res res; struct soc_device *sdev; @@ -25,17 +26,17 @@ static int imx9_soc_probe(struct platform_device *pdev) u64 uid127_64, uid63_0; int err; - attr = kzalloc(sizeof(*attr), GFP_KERNEL); + attr = devm_kzalloc(dev, sizeof(*attr), GFP_KERNEL); if (!attr) return -ENOMEM; err = of_property_read_string(of_root, "model", &attr->machine); if (err) { pr_err("%s: missing model property: %d\n", __func__, err); - goto attr; + return err; } - attr->family = kasprintf(GFP_KERNEL, "Freescale i.MX"); + attr->family = devm_kasprintf(dev, GFP_KERNEL, "Freescale i.MX"); /* * Retrieve the soc id, rev & uid info: @@ -47,39 +48,28 @@ static int imx9_soc_probe(struct platform_device *pdev) arm_smccc_smc(IMX_SIP_GET_SOC_INFO, 0, 0, 0, 0, 0, 0, 0, &res); if (res.a0 != SMCCC_RET_SUCCESS) { pr_err("%s: SMC failed: 0x%lx\n", __func__, res.a0); - err = -EINVAL; - goto family; + return -EINVAL; } soc_id = SOC_ID(res.a1); rev_major = SOC_REV_MAJOR(res.a1); rev_minor = SOC_REV_MINOR(res.a1); - attr->soc_id = kasprintf(GFP_KERNEL, "i.MX%2x", soc_id); - attr->revision = kasprintf(GFP_KERNEL, "%d.%d", rev_major, rev_minor); + attr->soc_id = devm_kasprintf(dev, GFP_KERNEL, "i.MX%2x", soc_id); + attr->revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d", rev_major, rev_minor); uid127_64 = res.a2; uid63_0 = res.a3; - attr->serial_number = kasprintf(GFP_KERNEL, "%016llx%016llx", uid127_64, uid63_0); + attr->serial_number = devm_kasprintf(dev, GFP_KERNEL, "%016llx%016llx", uid127_64, uid63_0); sdev = soc_device_register(attr); if (IS_ERR(sdev)) { err = PTR_ERR(sdev); pr_err("%s failed to register SoC as a device: %d\n", __func__, err); - goto serial_number; + return err; } return 0; - -serial_number: - kfree(attr->serial_number); - kfree(attr->revision); - kfree(attr->soc_id); -family: - kfree(attr->family); -attr: - kfree(attr); - return err; } static __maybe_unused const struct of_device_id imx9_soc_match[] = { From 36ca5298fc426f7c69111c7d4ef9310d2dc2d296 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 17 Dec 2025 20:42:08 +0800 Subject: [PATCH 134/171] soc: imx: Use dev_err_probe() for i.MX9 Use dev_err_probe() to simplify code. No functional changes. Signed-off-by: Peng Fan Reviewed-by: Frank Li Signed-off-by: Shawn Guo --- drivers/soc/imx/soc-imx9.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/soc/imx/soc-imx9.c b/drivers/soc/imx/soc-imx9.c index 0b1c59c7ddb2..d9a686299c5f 100644 --- a/drivers/soc/imx/soc-imx9.c +++ b/drivers/soc/imx/soc-imx9.c @@ -31,10 +31,8 @@ static int imx9_soc_probe(struct platform_device *pdev) return -ENOMEM; err = of_property_read_string(of_root, "model", &attr->machine); - if (err) { - pr_err("%s: missing model property: %d\n", __func__, err); - return err; - } + if (err) + return dev_err_probe(dev, err, "%s: missing model property\n", __func__); attr->family = devm_kasprintf(dev, GFP_KERNEL, "Freescale i.MX"); @@ -46,10 +44,8 @@ static int imx9_soc_probe(struct platform_device *pdev) * res.a3: uid[63:0]; */ arm_smccc_smc(IMX_SIP_GET_SOC_INFO, 0, 0, 0, 0, 0, 0, 0, &res); - if (res.a0 != SMCCC_RET_SUCCESS) { - pr_err("%s: SMC failed: 0x%lx\n", __func__, res.a0); - return -EINVAL; - } + if (res.a0 != SMCCC_RET_SUCCESS) + return dev_err_probe(dev, -EINVAL, "%s: SMC failed: 0x%lx\n", __func__, res.a0); soc_id = SOC_ID(res.a1); rev_major = SOC_REV_MAJOR(res.a1); @@ -63,11 +59,9 @@ static int imx9_soc_probe(struct platform_device *pdev) attr->serial_number = devm_kasprintf(dev, GFP_KERNEL, "%016llx%016llx", uid127_64, uid63_0); sdev = soc_device_register(attr); - if (IS_ERR(sdev)) { - err = PTR_ERR(sdev); - pr_err("%s failed to register SoC as a device: %d\n", __func__, err); - return err; - } + if (IS_ERR(sdev)) + return dev_err_probe(dev, PTR_ERR(sdev), + "%s failed to register SoC as a device\n", __func__); return 0; } From 4acaf8b293c8b03301508764dc3e586658186730 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 17 Dec 2025 20:42:09 +0800 Subject: [PATCH 135/171] soc: imx: Spport i.MX9[4,52] Add i.MX9[4,52] machine compatible to allow soc device could be created. SOC_ID is 16bit format data: - i.MX943: 0x9430 - i.MX952: 0x9520 Update SOC_ID macro to get the accurate data. Co-developed-by: Jacky Bai Signed-off-by: Jacky Bai Signed-off-by: Peng Fan Signed-off-by: Shawn Guo --- drivers/soc/imx/soc-imx9.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/soc/imx/soc-imx9.c b/drivers/soc/imx/soc-imx9.c index d9a686299c5f..d67bc7402b10 100644 --- a/drivers/soc/imx/soc-imx9.c +++ b/drivers/soc/imx/soc-imx9.c @@ -12,7 +12,7 @@ #include #define IMX_SIP_GET_SOC_INFO 0xc2000006 -#define SOC_ID(x) (((x) & 0xFFFF) >> 8) +#define SOC_ID(x) (((x) & 0xFF) ? ((x) & 0xFFFF) >> 4 : ((x) & 0xFFFF) >> 8) #define SOC_REV_MAJOR(x) ((((x) >> 28) & 0xF) - 0x9) #define SOC_REV_MINOR(x) (((x) >> 24) & 0xF) @@ -68,7 +68,9 @@ static int imx9_soc_probe(struct platform_device *pdev) static __maybe_unused const struct of_device_id imx9_soc_match[] = { { .compatible = "fsl,imx93", }, + { .compatible = "fsl,imx94", }, { .compatible = "fsl,imx95", }, + { .compatible = "fsl,imx952", }, { } }; From f6ef3d9ff81240e9bcc030f2da132eb0f8a761d7 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 14 Jan 2026 06:12:41 +0800 Subject: [PATCH 136/171] soc: imx8m: Fix error handling for clk_prepare_enable() imx8m_soc_prepare() directly returns the result of clk_prepare_enable(), which skips proper cleanup if the clock enable fails. Check the return value of clk_prepare_enable() and release resources if failure. Reported-by: kernel test robot Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/202601111406.ZVV3YaiU-lkp@intel.com/ Signed-off-by: Peng Fan Reviewed-by: Marco Felsch Reviewed-by: Daniel Baluta Signed-off-by: Shawn Guo --- drivers/soc/imx/soc-imx8m.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/soc/imx/soc-imx8m.c b/drivers/soc/imx/soc-imx8m.c index 04a1b60f2f2b..8e2322999f09 100644 --- a/drivers/soc/imx/soc-imx8m.c +++ b/drivers/soc/imx/soc-imx8m.c @@ -148,7 +148,11 @@ static int imx8m_soc_prepare(struct platform_device *pdev, const char *ocotp_com goto err_clk; } - return clk_prepare_enable(drvdata->clk); + ret = clk_prepare_enable(drvdata->clk); + if (ret) + goto err_clk; + + return 0; err_clk: iounmap(drvdata->ocotp_base); From e6d96073af681780820c94079b978474a8a44413 Mon Sep 17 00:00:00 2001 From: Prathamesh Shete Date: Thu, 8 Jan 2026 05:01:03 +0000 Subject: [PATCH 137/171] soc/tegra: pmc: Fix unsafe generic_handle_irq() call Currently, when resuming from system suspend on Tegra platforms, the following warning is observed: WARNING: CPU: 0 PID: 14459 at kernel/irq/irqdesc.c:666 Call trace: handle_irq_desc+0x20/0x58 (P) tegra186_pmc_wake_syscore_resume+0xe4/0x15c syscore_resume+0x3c/0xb8 suspend_devices_and_enter+0x510/0x540 pm_suspend+0x16c/0x1d8 The warning occurs because generic_handle_irq() is being called from a non-interrupt context which is considered as unsafe. Fix this warning by deferring generic_handle_irq() call to an IRQ work which gets executed in hard IRQ context where generic_handle_irq() can be called safely. When PREEMPT_RT kernels are used, regular IRQ work (initialized with init_irq_work) is deferred to run in per-CPU kthreads in preemptible context rather than hard IRQ context. Hence, use the IRQ_WORK_INIT_HARD variant so that with PREEMPT_RT kernels, the IRQ work is processed in hardirq context instead of being deferred to a thread which is required for calling generic_handle_irq(). On non-PREEMPT_RT kernels, both init_irq_work() and IRQ_WORK_INIT_HARD() execute in IRQ context, so this change has no functional impact for standard kernel configurations. Signed-off-by: Petlozu Pravareshwar Signed-off-by: Prathamesh Shete Reviewed-by: Jon Hunter Tested-by: Jon Hunter [treding@nvidia.com: miscellaneous cleanups] Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 104 ++++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 30 deletions(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index f3760a3b3026..407fa840814c 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -468,6 +469,10 @@ struct tegra_pmc { unsigned long *wake_sw_status_map; unsigned long *wake_cntrl_level_map; struct syscore syscore; + + /* Pending wake IRQ processing */ + struct irq_work wake_work; + u32 *wake_status; }; static struct tegra_pmc *pmc = &(struct tegra_pmc) { @@ -1905,6 +1910,50 @@ static int tegra_pmc_parse_dt(struct tegra_pmc *pmc, struct device_node *np) return 0; } +/* translate sc7 wake sources back into IRQs to catch edge triggered wakeups */ +static void tegra186_pmc_wake_handler(struct irq_work *work) +{ + struct tegra_pmc *pmc = container_of(work, struct tegra_pmc, wake_work); + unsigned int i, wake; + + for (i = 0; i < pmc->soc->max_wake_vectors; i++) { + unsigned long status = pmc->wake_status[i]; + + for_each_set_bit(wake, &status, 32) { + irq_hw_number_t hwirq = wake + (i * 32); + struct irq_desc *desc; + unsigned int irq; + + irq = irq_find_mapping(pmc->domain, hwirq); + if (!irq) { + dev_warn(pmc->dev, + "No IRQ found for WAKE#%lu!\n", + hwirq); + continue; + } + + dev_dbg(pmc->dev, + "Resume caused by WAKE#%lu mapped to IRQ#%u\n", + hwirq, irq); + + desc = irq_to_desc(irq); + if (!desc) { + dev_warn(pmc->dev, + "No descriptor found for IRQ#%u\n", + irq); + continue; + } + + if (!desc->action || !desc->action->name) + continue; + + generic_handle_irq(irq); + } + + pmc->wake_status[i] = 0; + } +} + static int tegra_pmc_init(struct tegra_pmc *pmc) { if (pmc->soc->max_wake_events > 0) { @@ -1923,6 +1972,18 @@ static int tegra_pmc_init(struct tegra_pmc *pmc) pmc->wake_cntrl_level_map = bitmap_zalloc(pmc->soc->max_wake_events, GFP_KERNEL); if (!pmc->wake_cntrl_level_map) return -ENOMEM; + + pmc->wake_status = kcalloc(pmc->soc->max_wake_vectors, sizeof(u32), GFP_KERNEL); + if (!pmc->wake_status) + return -ENOMEM; + + /* + * Initialize IRQ work for processing wake IRQs. Must use + * HARD_IRQ variant to run in hard IRQ context on PREEMPT_RT + * because we call generic_handle_irq() which requires hard + * IRQ context. + */ + pmc->wake_work = IRQ_WORK_INIT_HARD(tegra186_pmc_wake_handler); } if (pmc->soc->init) @@ -3129,47 +3190,30 @@ static void wke_clear_wake_status(struct tegra_pmc *pmc) } } -/* translate sc7 wake sources back into IRQs to catch edge triggered wakeups */ -static void tegra186_pmc_process_wake_events(struct tegra_pmc *pmc, unsigned int index, - unsigned long status) -{ - unsigned int wake; - - dev_dbg(pmc->dev, "Wake[%d:%d] status=%#lx\n", (index * 32) + 31, index * 32, status); - - for_each_set_bit(wake, &status, 32) { - irq_hw_number_t hwirq = wake + 32 * index; - struct irq_desc *desc; - unsigned int irq; - - irq = irq_find_mapping(pmc->domain, hwirq); - - desc = irq_to_desc(irq); - if (!desc || !desc->action || !desc->action->name) { - dev_dbg(pmc->dev, "Resume caused by WAKE%ld, IRQ %d\n", hwirq, irq); - continue; - } - - dev_dbg(pmc->dev, "Resume caused by WAKE%ld, %s\n", hwirq, desc->action->name); - generic_handle_irq(irq); - } -} - static void tegra186_pmc_wake_syscore_resume(void *data) { - u32 status, mask; unsigned int i; + u32 mask; for (i = 0; i < pmc->soc->max_wake_vectors; i++) { mask = readl(pmc->wake + WAKE_AOWAKE_TIER2_ROUTING(i)); - status = readl(pmc->wake + WAKE_AOWAKE_STATUS_R(i)) & mask; - - tegra186_pmc_process_wake_events(pmc, i, status); + pmc->wake_status[i] = readl(pmc->wake + WAKE_AOWAKE_STATUS_R(i)) & mask; } + + /* Schedule IRQ work to process wake IRQs (if any) */ + irq_work_queue(&pmc->wake_work); } static int tegra186_pmc_wake_syscore_suspend(void *data) { + unsigned int i; + + /* Check if there are unhandled wake IRQs */ + for (i = 0; i < pmc->soc->max_wake_vectors; i++) + if (pmc->wake_status[i]) + dev_warn(pmc->dev, + "Unhandled wake IRQs pending vector[%u]: 0x%x\n", + i, pmc->wake_status[i]); wke_read_sw_wake_status(pmc); /* flip the wakeup trigger for dual-edge triggered pads From f59dbd0c9388321336b0f5c57644fd8b0ee94aa9 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 3 Feb 2025 18:17:37 +0100 Subject: [PATCH 138/171] soc/tegra: pmc: Use contextual data instead of global variable Pass the driver-specific data via the syscore struct and use it in the syscore ops. Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 407fa840814c..2f283fc1c6c5 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -3192,6 +3192,7 @@ static void wke_clear_wake_status(struct tegra_pmc *pmc) static void tegra186_pmc_wake_syscore_resume(void *data) { + struct tegra_pmc *pmc = data; unsigned int i; u32 mask; @@ -3206,6 +3207,7 @@ static void tegra186_pmc_wake_syscore_resume(void *data) static int tegra186_pmc_wake_syscore_suspend(void *data) { + struct tegra_pmc *pmc = data; unsigned int i; /* Check if there are unhandled wake IRQs */ @@ -3214,6 +3216,7 @@ static int tegra186_pmc_wake_syscore_suspend(void *data) dev_warn(pmc->dev, "Unhandled wake IRQs pending vector[%u]: 0x%x\n", i, pmc->wake_status[i]); + wke_read_sw_wake_status(pmc); /* flip the wakeup trigger for dual-edge triggered pads @@ -3887,6 +3890,7 @@ static const struct tegra_pmc_regs tegra186_pmc_regs = { static void tegra186_pmc_init(struct tegra_pmc *pmc) { pmc->syscore.ops = &tegra186_pmc_wake_syscore_ops; + pmc->syscore.data = pmc; register_syscore(&pmc->syscore); } From 1c672945ceb4ba55feb2ba9d6816be395a6e32d7 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 3 Feb 2025 14:27:21 +0100 Subject: [PATCH 139/171] soc/tegra: pmc: Pass struct tegra_pmc to tegra_powergate_state() By using the generic read_poll_timeout() instead of readx_poll_timeout() we can pass additional parameters, which allows us to pass an additional PMC context structure and avoid relying on a global variable for this. Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 2f283fc1c6c5..8f29a35a8e01 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -546,12 +546,7 @@ static void tegra_pmc_scratch_writel(struct tegra_pmc *pmc, u32 value, writel(value, pmc->scratch + offset); } -/* - * TODO Figure out a way to call this with the struct tegra_pmc * passed in. - * This currently doesn't work because readx_poll_timeout() can only operate - * on functions that take a single argument. - */ -static inline bool tegra_powergate_state(int id) +static inline bool tegra_powergate_state(struct tegra_pmc *pmc, int id) { if (id == TEGRA_POWERGATE_3D && pmc->soc->has_gpu_clamps) return (tegra_pmc_readl(pmc, GPU_RG_CNTRL) & 0x1) == 0; @@ -603,8 +598,9 @@ static int tegra20_powergate_set(struct tegra_pmc *pmc, unsigned int id, tegra_pmc_writel(pmc, PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE); /* wait for PMC to execute the command */ - ret = readx_poll_timeout(tegra_powergate_state, id, status, - status == new_state, 1, 10); + ret = read_poll_timeout(tegra_powergate_state, status, + status == new_state, 1, 10, false, + pmc, id); } while (ret == -ETIMEDOUT && retries--); return ret; @@ -636,8 +632,9 @@ static int tegra114_powergate_set(struct tegra_pmc *pmc, unsigned int id, return err; /* wait for PMC to execute the command */ - err = readx_poll_timeout(tegra_powergate_state, id, status, - status == new_state, 10, 100000); + err = read_poll_timeout(tegra_powergate_state, status, + status == new_state, 10, 100000, false, + pmc, id); if (err) return err; @@ -660,7 +657,7 @@ static int tegra_powergate_set(struct tegra_pmc *pmc, unsigned int id, mutex_lock(&pmc->powergates_lock); - if (tegra_powergate_state(id) == new_state) { + if (tegra_powergate_state(pmc, id) == new_state) { mutex_unlock(&pmc->powergates_lock); return 0; } @@ -981,7 +978,7 @@ static int tegra_powergate_is_powered(struct tegra_pmc *pmc, unsigned int id) if (!tegra_powergate_is_valid(pmc, id)) return -EINVAL; - return tegra_powergate_state(id); + return tegra_powergate_state(pmc, id); } /** From 0732dffeb093064b7954a48362ec83a25262878b Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 3 Feb 2025 14:33:09 +0100 Subject: [PATCH 140/171] soc/tegra: pmc: Store PMC context in clocks Clocks exposed by the PMC need to reference the PMC context for register programming. Store a reference to the context in the data structure for each clock to avoid the need for a global variable. Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 46 +++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 8f29a35a8e01..ec364dda9f75 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -202,18 +202,20 @@ #define TEGRA_SMC_PMC_WRITE 0xbb struct pmc_clk { - struct clk_hw hw; - unsigned long offs; - u32 mux_shift; - u32 force_en_shift; + struct clk_hw hw; + struct tegra_pmc *pmc; + unsigned long offs; + u32 mux_shift; + u32 force_en_shift; }; #define to_pmc_clk(_hw) container_of(_hw, struct pmc_clk, hw) struct pmc_clk_gate { - struct clk_hw hw; - unsigned long offs; - u32 shift; + struct clk_hw hw; + struct tegra_pmc *pmc; + unsigned long offs; + u32 shift; }; #define to_pmc_clk_gate(_hw) container_of(_hw, struct pmc_clk_gate, hw) @@ -2601,7 +2603,7 @@ static int tegra_pmc_clk_notify_cb(struct notifier_block *nb, return NOTIFY_OK; } -static void pmc_clk_fence_udelay(u32 offset) +static void pmc_clk_fence_udelay(struct tegra_pmc *pmc, u32 offset) { tegra_pmc_readl(pmc, offset); /* pmc clk propagation delay 2 us */ @@ -2613,7 +2615,7 @@ static u8 pmc_clk_mux_get_parent(struct clk_hw *hw) struct pmc_clk *clk = to_pmc_clk(hw); u32 val; - val = tegra_pmc_readl(pmc, clk->offs) >> clk->mux_shift; + val = tegra_pmc_readl(clk->pmc, clk->offs) >> clk->mux_shift; val &= PMC_CLK_OUT_MUX_MASK; return val; @@ -2624,11 +2626,11 @@ static int pmc_clk_mux_set_parent(struct clk_hw *hw, u8 index) struct pmc_clk *clk = to_pmc_clk(hw); u32 val; - val = tegra_pmc_readl(pmc, clk->offs); + val = tegra_pmc_readl(clk->pmc, clk->offs); val &= ~(PMC_CLK_OUT_MUX_MASK << clk->mux_shift); val |= index << clk->mux_shift; - tegra_pmc_writel(pmc, val, clk->offs); - pmc_clk_fence_udelay(clk->offs); + tegra_pmc_writel(clk->pmc, val, clk->offs); + pmc_clk_fence_udelay(clk->pmc, clk->offs); return 0; } @@ -2638,26 +2640,27 @@ static int pmc_clk_is_enabled(struct clk_hw *hw) struct pmc_clk *clk = to_pmc_clk(hw); u32 val; - val = tegra_pmc_readl(pmc, clk->offs) & BIT(clk->force_en_shift); + val = tegra_pmc_readl(clk->pmc, clk->offs) & BIT(clk->force_en_shift); return val ? 1 : 0; } -static void pmc_clk_set_state(unsigned long offs, u32 shift, int state) +static void pmc_clk_set_state(struct tegra_pmc *pmc, unsigned long offs, + u32 shift, int state) { u32 val; val = tegra_pmc_readl(pmc, offs); val = state ? (val | BIT(shift)) : (val & ~BIT(shift)); tegra_pmc_writel(pmc, val, offs); - pmc_clk_fence_udelay(offs); + pmc_clk_fence_udelay(pmc, offs); } static int pmc_clk_enable(struct clk_hw *hw) { struct pmc_clk *clk = to_pmc_clk(hw); - pmc_clk_set_state(clk->offs, clk->force_en_shift, 1); + pmc_clk_set_state(clk->pmc, clk->offs, clk->force_en_shift, 1); return 0; } @@ -2666,7 +2669,7 @@ static void pmc_clk_disable(struct clk_hw *hw) { struct pmc_clk *clk = to_pmc_clk(hw); - pmc_clk_set_state(clk->offs, clk->force_en_shift, 0); + pmc_clk_set_state(clk->pmc, clk->offs, clk->force_en_shift, 0); } static const struct clk_ops pmc_clk_ops = { @@ -2698,6 +2701,7 @@ tegra_pmc_clk_out_register(struct tegra_pmc *pmc, CLK_SET_PARENT_GATE; pmc_clk->hw.init = &init; + pmc_clk->pmc = pmc; pmc_clk->offs = offset; pmc_clk->mux_shift = data->mux_shift; pmc_clk->force_en_shift = data->force_en_shift; @@ -2708,15 +2712,16 @@ tegra_pmc_clk_out_register(struct tegra_pmc *pmc, static int pmc_clk_gate_is_enabled(struct clk_hw *hw) { struct pmc_clk_gate *gate = to_pmc_clk_gate(hw); + u32 value = tegra_pmc_readl(gate->pmc, gate->offs); - return tegra_pmc_readl(pmc, gate->offs) & BIT(gate->shift) ? 1 : 0; + return value & BIT(gate->shift) ? 1 : 0; } static int pmc_clk_gate_enable(struct clk_hw *hw) { struct pmc_clk_gate *gate = to_pmc_clk_gate(hw); - pmc_clk_set_state(gate->offs, gate->shift, 1); + pmc_clk_set_state(gate->pmc, gate->offs, gate->shift, 1); return 0; } @@ -2725,7 +2730,7 @@ static void pmc_clk_gate_disable(struct clk_hw *hw) { struct pmc_clk_gate *gate = to_pmc_clk_gate(hw); - pmc_clk_set_state(gate->offs, gate->shift, 0); + pmc_clk_set_state(gate->pmc, gate->offs, gate->shift, 0); } static const struct clk_ops pmc_clk_gate_ops = { @@ -2753,6 +2758,7 @@ tegra_pmc_clk_gate_register(struct tegra_pmc *pmc, const char *name, init.flags = 0; gate->hw.init = &init; + gate->pmc = pmc; gate->offs = offset; gate->shift = shift; From 48b7f802fb78f6f13c771c03fd6329c8a88e6991 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 3 Feb 2025 14:38:24 +0100 Subject: [PATCH 141/171] soc/tegra: pmc: Embed reboot notifier in PMC context Instead of relying on a global variable to track the PMC context, embed the reboot notifier into the PMC context so that the latter can be resolved using container_of(). Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index ec364dda9f75..9ec033d539e9 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -470,6 +470,8 @@ struct tegra_pmc { unsigned long *wake_type_dual_edge_map; unsigned long *wake_sw_status_map; unsigned long *wake_cntrl_level_map; + + struct notifier_block reboot_notifier; struct syscore syscore; /* Pending wake IRQ processing */ @@ -1103,7 +1105,8 @@ int tegra_pmc_cpu_remove_clamping(unsigned int cpuid) return tegra_powergate_remove_clamping(id); } -static void tegra_pmc_program_reboot_reason(const char *cmd) +static void tegra_pmc_program_reboot_reason(struct tegra_pmc *pmc, + const char *cmd) { u32 value; @@ -1127,16 +1130,14 @@ static void tegra_pmc_program_reboot_reason(const char *cmd) static int tegra_pmc_reboot_notify(struct notifier_block *this, unsigned long action, void *data) { + struct tegra_pmc *pmc = container_of(this, struct tegra_pmc, + reboot_notifier); if (action == SYS_RESTART) - tegra_pmc_program_reboot_reason(data); + tegra_pmc_program_reboot_reason(pmc, data); return NOTIFY_DONE; } -static struct notifier_block tegra_pmc_reboot_notifier = { - .notifier_call = tegra_pmc_reboot_notify, -}; - static void tegra_pmc_restart(void) { u32 value; @@ -2995,8 +2996,10 @@ static int tegra_pmc_probe(struct platform_device *pdev) * CPU without resetting everything else. */ if (pmc->scratch) { + pmc->reboot_notifier.notifier_call = tegra_pmc_reboot_notify; + err = devm_register_reboot_notifier(&pdev->dev, - &tegra_pmc_reboot_notifier); + &pmc->reboot_notifier); if (err) { dev_err(&pdev->dev, "unable to register reboot notifier, %d\n", From a9f822b3ff7b47a5b78f1258b998cbeb29089a62 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 3 Feb 2025 14:41:45 +0100 Subject: [PATCH 142/171] soc/tegra: pmc: Pass PMC context via sys-off callback data To avoid relying on global variables, use the sys-off callback data to store a reference to the PMC context structure. Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 9ec033d539e9..cd7c557c886b 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -1138,7 +1138,7 @@ static int tegra_pmc_reboot_notify(struct notifier_block *this, return NOTIFY_DONE; } -static void tegra_pmc_restart(void) +static void tegra_pmc_restart(struct tegra_pmc *pmc) { u32 value; @@ -1150,13 +1150,17 @@ static void tegra_pmc_restart(void) static int tegra_pmc_restart_handler(struct sys_off_data *data) { - tegra_pmc_restart(); + struct tegra_pmc *pmc = data->cb_data; + + tegra_pmc_restart(pmc); return NOTIFY_DONE; } static int tegra_pmc_power_off_handler(struct sys_off_data *data) { + struct tegra_pmc *pmc = data->cb_data; + /* * Reboot Nexus 7 into special bootloader mode if USB cable is * connected in order to display battery status and power off. @@ -1166,7 +1170,7 @@ static int tegra_pmc_power_off_handler(struct sys_off_data *data) const u32 go_to_charger_mode = 0xa5a55a5a; tegra_pmc_writel(pmc, go_to_charger_mode, PMC_SCRATCH37); - tegra_pmc_restart(); + tegra_pmc_restart(pmc); } return NOTIFY_DONE; @@ -3011,7 +3015,8 @@ static int tegra_pmc_probe(struct platform_device *pdev) err = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART, SYS_OFF_PRIO_LOW, - tegra_pmc_restart_handler, NULL); + tegra_pmc_restart_handler, + pmc); if (err) { dev_err(&pdev->dev, "failed to register sys-off handler: %d\n", err); @@ -3025,7 +3030,8 @@ static int tegra_pmc_probe(struct platform_device *pdev) err = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE, - tegra_pmc_power_off_handler, NULL); + tegra_pmc_power_off_handler, + pmc); if (err) { dev_err(&pdev->dev, "failed to register sys-off handler: %d\n", err); From b2a3e8200056480219d4bc44c3403cacf0125495 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 3 Feb 2025 14:47:15 +0100 Subject: [PATCH 143/171] soc/tegra: pmc: Pass PMC context as debugfs data Each debugfs file can have private data associated with it. Use this to pass the PMC context instead of relying on a global variable. Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index cd7c557c886b..f215396206f1 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -1178,6 +1178,7 @@ static int tegra_pmc_power_off_handler(struct sys_off_data *data) static int powergate_show(struct seq_file *s, void *data) { + struct tegra_pmc *pmc = data; unsigned int i; int status; @@ -3097,7 +3098,7 @@ static int tegra_pmc_probe(struct platform_device *pdev) if (pmc->soc->set_wake_filters) pmc->soc->set_wake_filters(pmc); - debugfs_create_file("powergate", 0444, NULL, NULL, &powergate_fops); + debugfs_create_file("powergate", 0444, NULL, pmc, &powergate_fops); return 0; From bb946b0e118dc95160d2313474b2ca4de771f82b Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 3 Feb 2025 14:51:18 +0100 Subject: [PATCH 144/171] soc/tegra: pmc: Use PMC context embedded in powergates The powergates exposed by the PMC have a pointer to the PMC context embedded. Use that embedded reference instead of relying on a global variable. For the core power domain a new structure needs to be introduced to wrap the generic PM domain and store the PMC context. Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index f215396206f1..da8890520fc8 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -268,6 +268,17 @@ static const struct pmc_clk_init_data tegra_pmc_clks_data[] = { }, }; +struct tegra_pmc_core_pd { + struct generic_pm_domain genpd; + struct tegra_pmc *pmc; +}; + +static inline struct tegra_pmc_core_pd * +to_core_pd(struct generic_pm_domain *genpd) +{ + return container_of(genpd, struct tegra_pmc_core_pd, genpd); +} + struct tegra_powergate { struct generic_pm_domain genpd; struct tegra_pmc *pmc; @@ -1387,6 +1398,8 @@ static int tegra_pmc_core_pd_set_performance_state(struct generic_pm_domain *genpd, unsigned int level) { + struct tegra_pmc_core_pd *pd = to_core_pd(genpd); + struct tegra_pmc *pmc = pd->pmc; struct dev_pm_opp *opp; int err; @@ -1414,30 +1427,31 @@ tegra_pmc_core_pd_set_performance_state(struct generic_pm_domain *genpd, static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np) { - struct generic_pm_domain *genpd; const char *rname[] = { "core", NULL}; + struct tegra_pmc_core_pd *pd; int err; - genpd = devm_kzalloc(pmc->dev, sizeof(*genpd), GFP_KERNEL); - if (!genpd) + pd = devm_kzalloc(pmc->dev, sizeof(*pd), GFP_KERNEL); + if (!pd) return -ENOMEM; - genpd->name = "core"; - genpd->flags = GENPD_FLAG_NO_SYNC_STATE; - genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state; + pd->genpd.name = "core"; + pd->genpd.flags = GENPD_FLAG_NO_SYNC_STATE; + pd->genpd.set_performance_state = tegra_pmc_core_pd_set_performance_state; + pd->pmc = pmc; err = devm_pm_opp_set_regulators(pmc->dev, rname); if (err) return dev_err_probe(pmc->dev, err, "failed to set core OPP regulator\n"); - err = pm_genpd_init(genpd, NULL, false); + err = pm_genpd_init(&pd->genpd, NULL, false); if (err) { dev_err(pmc->dev, "failed to init core genpd: %d\n", err); return err; } - err = of_genpd_add_provider_simple(np, genpd); + err = of_genpd_add_provider_simple(np, &pd->genpd); if (err) { dev_err(pmc->dev, "failed to add core genpd: %d\n", err); goto remove_genpd; @@ -1446,7 +1460,7 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np) return 0; remove_genpd: - pm_genpd_remove(genpd); + pm_genpd_remove(&pd->genpd); return err; } @@ -1509,7 +1523,7 @@ static void tegra_powergate_remove(struct generic_pm_domain *genpd) kfree(pg->clks); - set_bit(pg->id, pmc->powergates_available); + set_bit(pg->id, pg->pmc->powergates_available); kfree(pg); } From 2e944c51d6617d3ded7734d21202036c126344a1 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 3 Feb 2025 14:52:46 +0100 Subject: [PATCH 145/171] soc/tegra: pmc: Use driver-private data Instead of relying on a global variable for the PMC context, use the driver-private data for sysfs attributes. Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index da8890520fc8..9cfa7c3d5cae 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -2243,6 +2243,7 @@ static int tegra_pmc_pinctrl_init(struct tegra_pmc *pmc) static ssize_t reset_reason_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct tegra_pmc *pmc = dev_get_drvdata(dev); u32 value; value = tegra_pmc_readl(pmc, pmc->soc->regs->rst_status); @@ -2260,6 +2261,7 @@ static DEVICE_ATTR_RO(reset_reason); static ssize_t reset_level_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct tegra_pmc *pmc = dev_get_drvdata(dev); u32 value; value = tegra_pmc_readl(pmc, pmc->soc->regs->rst_status); From e1fd5ad68acd3c3a76651c811e1fdb4846237370 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 3 Feb 2025 14:55:03 +0100 Subject: [PATCH 146/171] soc/tegra: pmc: Do not rely on global variable The reset action for changing the suspend mode back on failure can take a context-specific data argument that can be set to the PMC context in order to avoid relying on a global variable. Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 9cfa7c3d5cae..750fe29f4cf5 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -2944,6 +2944,8 @@ static int tegra_pmc_regmap_init(struct tegra_pmc *pmc) static void tegra_pmc_reset_suspend_mode(void *data) { + struct tegra_pmc *pmc = data; + pmc->suspend_mode = TEGRA_SUSPEND_NOT_READY; } @@ -2966,7 +2968,7 @@ static int tegra_pmc_probe(struct platform_device *pdev) return err; err = devm_add_action_or_reset(&pdev->dev, tegra_pmc_reset_suspend_mode, - NULL); + pmc); if (err) return err; From 70f752ebb08c85a5ea19471a5aaf26263e53dcb0 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 3 Feb 2025 16:22:58 +0100 Subject: [PATCH 147/171] soc/tegra: pmc: Add PMC contextual functions Add implementations that take as argument a struct tegra_pmc * for most public APIs, as well as a function to obtain the PMC for any given device. This will allow transitioning away users from relying on a global variable storing the PMC context. Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 178 ++++++++++++++++++++++++++++++++++++---- include/soc/tegra/pmc.h | 60 +++++++++++++- 2 files changed, 221 insertions(+), 17 deletions(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 750fe29f4cf5..9cdbd8ba94be 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -957,29 +957,121 @@ static int tegra_genpd_power_off(struct generic_pm_domain *domain) return err; } +static void tegra_pmc_put_device(void *data) +{ + struct tegra_pmc *pmc = data; + + put_device(pmc->dev); +} + +static const struct of_device_id tegra_pmc_match[]; + +static struct tegra_pmc *tegra_pmc_get(struct device *dev) +{ + struct platform_device *pdev; + struct device_node *np; + struct tegra_pmc *pmc; + + np = of_parse_phandle(dev->of_node, "nvidia,pmc", 0); + if (!np) { + struct device_node *parent = of_node_get(dev->of_node); + + while ((parent = of_get_next_parent(parent)) != NULL) { + np = of_find_matching_node(parent, tegra_pmc_match); + if (np) + break; + } + + of_node_put(parent); + + if (!np) + return ERR_PTR(-ENODEV); + } + + pdev = of_find_device_by_node(np); + of_node_put(np); + + if (!pdev) + return ERR_PTR(-ENODEV); + + pmc = platform_get_drvdata(pdev); + if (!pmc) { + put_device(&pdev->dev); + return ERR_PTR(-EPROBE_DEFER); + } + + return pmc; +} + /** - * tegra_powergate_power_on() - power on partition + * tegra_pmc_get() - find the PMC for a given device + * @dev: device for which to find the PMC + * + * Returns a pointer to the PMC on success or an ERR_PTR()-encoded error code + * otherwise. + */ +struct tegra_pmc *devm_tegra_pmc_get(struct device *dev) +{ + struct tegra_pmc *pmc; + int err; + + pmc = tegra_pmc_get(dev); + if (IS_ERR(pmc)) + return pmc; + + err = devm_add_action_or_reset(dev, tegra_pmc_put_device, pmc); + if (err < 0) + return ERR_PTR(err); + + return pmc; +} +EXPORT_SYMBOL(devm_tegra_pmc_get); + +/** + * tegra_pmc_powergate_power_on() - power on partition + * @pmc: power management controller * @id: partition ID */ -int tegra_powergate_power_on(unsigned int id) +int tegra_pmc_powergate_power_on(struct tegra_pmc *pmc, unsigned int id) { if (!tegra_powergate_is_available(pmc, id)) return -EINVAL; return tegra_powergate_set(pmc, id, true); } +EXPORT_SYMBOL(tegra_pmc_powergate_power_on); + +/** + * tegra_powergate_power_on() - power on partition + * @id: partition ID + */ +int tegra_powergate_power_on(unsigned int id) +{ + return tegra_pmc_powergate_power_on(pmc, id); +} EXPORT_SYMBOL(tegra_powergate_power_on); +/** + * tegra_pmc_powergate_power_off() - power off partition + * @pmc: power management controller + * @id: partition ID + */ +int tegra_pmc_powergate_power_off(struct tegra_pmc *pmc, unsigned int id) +{ + if (!tegra_powergate_is_available(pmc, id)) + return -EINVAL; + + return tegra_powergate_set(pmc, id, false); +} +EXPORT_SYMBOL(tegra_pmc_powergate_power_off); + /** * tegra_powergate_power_off() - power off partition * @id: partition ID */ int tegra_powergate_power_off(unsigned int id) { - if (!tegra_powergate_is_available(pmc, id)) - return -EINVAL; - - return tegra_powergate_set(pmc, id, false); + return tegra_pmc_powergate_power_off(pmc, id); } EXPORT_SYMBOL(tegra_powergate_power_off); @@ -997,28 +1089,41 @@ static int tegra_powergate_is_powered(struct tegra_pmc *pmc, unsigned int id) } /** - * tegra_powergate_remove_clamping() - remove power clamps for partition + * tegra_pmc_powergate_remove_clamping() - remove power clamps for partition + * @pmc: power management controller * @id: partition ID */ -int tegra_powergate_remove_clamping(unsigned int id) +int tegra_pmc_powergate_remove_clamping(struct tegra_pmc *pmc, unsigned int id) { if (!tegra_powergate_is_available(pmc, id)) return -EINVAL; return __tegra_powergate_remove_clamping(pmc, id); } +EXPORT_SYMBOL(tegra_pmc_powergate_remove_clamping); + +/** + * tegra_powergate_remove_clamping() - remove power clamps for partition + * @id: partition ID + */ +int tegra_powergate_remove_clamping(unsigned int id) +{ + return tegra_pmc_powergate_remove_clamping(pmc, id); +} EXPORT_SYMBOL(tegra_powergate_remove_clamping); /** - * tegra_powergate_sequence_power_up() - power up partition + * tegra_pmc_powergate_sequence_power_up() - power up partition + * @pmc: power management controller * @id: partition ID * @clk: clock for partition * @rst: reset for partition * * Must be called with clk disabled, and returns with clk enabled. */ -int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk, - struct reset_control *rst) +int tegra_pmc_powergate_sequence_power_up(struct tegra_pmc *pmc, + unsigned int id, struct clk *clk, + struct reset_control *rst) { struct tegra_powergate *pg; int err; @@ -1052,6 +1157,21 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk, return err; } +EXPORT_SYMBOL(tegra_pmc_powergate_sequence_power_up); + +/** + * tegra_powergate_sequence_power_up() - power up partition + * @id: partition ID + * @clk: clock for partition + * @rst: reset for partition + * + * Must be called with clk disabled, and returns with clk enabled. + */ +int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk, + struct reset_control *rst) +{ + return tegra_pmc_powergate_sequence_power_up(pmc, id, clk, rst); +} EXPORT_SYMBOL(tegra_powergate_sequence_power_up); /** @@ -1627,11 +1747,12 @@ static void tegra_io_pad_unprepare(struct tegra_pmc *pmc) /** * tegra_io_pad_power_enable() - enable power to I/O pad + * @pmc: power management controller * @id: Tegra I/O pad ID for which to enable power * * Returns: 0 on success or a negative error code on failure. */ -int tegra_io_pad_power_enable(enum tegra_io_pad id) +int tegra_pmc_io_pad_power_enable(struct tegra_pmc *pmc, enum tegra_io_pad id) { const struct tegra_io_pad_soc *pad; unsigned long request, status; @@ -1666,15 +1787,28 @@ unlock: mutex_unlock(&pmc->powergates_lock); return err; } +EXPORT_SYMBOL(tegra_pmc_io_pad_power_enable); + +/** + * tegra_io_pad_power_enable() - enable power to I/O pad + * @id: Tegra I/O pad ID for which to enable power + * + * Returns: 0 on success or a negative error code on failure. + */ +int tegra_io_pad_power_enable(enum tegra_io_pad id) +{ + return tegra_pmc_io_pad_power_enable(pmc, id); +} EXPORT_SYMBOL(tegra_io_pad_power_enable); /** - * tegra_io_pad_power_disable() - disable power to I/O pad + * tegra_pmc_io_pad_power_disable() - disable power to I/O pad + * @pmc: power management controller * @id: Tegra I/O pad ID for which to disable power * * Returns: 0 on success or a negative error code on failure. */ -int tegra_io_pad_power_disable(enum tegra_io_pad id) +int tegra_pmc_io_pad_power_disable(struct tegra_pmc *pmc, enum tegra_io_pad id) { const struct tegra_io_pad_soc *pad; unsigned long request, status; @@ -1709,6 +1843,18 @@ unlock: mutex_unlock(&pmc->powergates_lock); return err; } +EXPORT_SYMBOL(tegra_pmc_io_pad_power_disable); + +/** + * tegra_io_pad_power_disable() - disable power to I/O pad + * @id: Tegra I/O pad ID for which to disable power + * + * Returns: 0 on success or a negative error code on failure. + */ +int tegra_io_pad_power_disable(enum tegra_io_pad id) +{ + return tegra_pmc_io_pad_power_disable(pmc, id); +} EXPORT_SYMBOL(tegra_io_pad_power_disable); static int tegra_io_pad_is_powered(struct tegra_pmc *pmc, enum tegra_io_pad id) @@ -2184,9 +2330,9 @@ static int tegra_io_pad_pinconf_set(struct pinctrl_dev *pctl_dev, switch (param) { case PIN_CONFIG_MODE_LOW_POWER: if (arg) - err = tegra_io_pad_power_disable(pad->id); + err = tegra_pmc_io_pad_power_disable(pmc, pad->id); else - err = tegra_io_pad_power_enable(pad->id); + err = tegra_pmc_io_pad_power_enable(pmc, pad->id); if (err) return err; break; diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h index c545875d0ff1..1fd21be02577 100644 --- a/include/soc/tegra/pmc.h +++ b/include/soc/tegra/pmc.h @@ -16,6 +16,7 @@ struct clk; struct reset_control; +struct tegra_pmc; bool tegra_pmc_cpu_is_powered(unsigned int cpuid); int tegra_pmc_cpu_power_on(unsigned int cpuid); @@ -149,11 +150,24 @@ enum tegra_io_pad { }; #ifdef CONFIG_SOC_TEGRA_PMC +struct tegra_pmc *devm_tegra_pmc_get(struct device *dev); + +int tegra_pmc_powergate_power_on(struct tegra_pmc *pmc, unsigned int id); +int tegra_pmc_powergate_power_off(struct tegra_pmc *pmc, unsigned int id); +int tegra_pmc_powergate_remove_clamping(struct tegra_pmc *pmc, unsigned int id); + +/* Must be called with clk disabled, and returns with clk enabled */ +int tegra_pmc_powergate_sequence_power_up(struct tegra_pmc *pmc, + unsigned int id, struct clk *clk, + struct reset_control *rst); +int tegra_pmc_io_pad_power_enable(struct tegra_pmc *pmc, enum tegra_io_pad id); +int tegra_pmc_io_pad_power_disable(struct tegra_pmc *pmc, enum tegra_io_pad id); + +/* legacy */ int tegra_powergate_power_on(unsigned int id); int tegra_powergate_power_off(unsigned int id); int tegra_powergate_remove_clamping(unsigned int id); -/* Must be called with clk disabled, and returns with clk enabled */ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk, struct reset_control *rst); @@ -166,6 +180,50 @@ void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode); bool tegra_pmc_core_domain_state_synced(void); #else +static inline struct tegra_pmc *devm_tegra_pmc_get(struct device *dev) +{ + return ERR_PTR(-ENOSYS); +} + +static inline int +tegra_pmc_powergate_power_on(struct tegra_pmc *pmc, unsigned int id) +{ + return -ENOSYS; +} + +static inline int +tegra_pmc_powergate_power_off(struct tegra_pmc *pmc, unsigned int id) +{ + return -ENOSYS; +} + +static inline int +tegra_pmc_powergate_remove_clamping(struct tegra_pmc *pmc, unsigned int id) +{ + return -ENOSYS; +} + +/* Must be called with clk disabled, and returns with clk enabled */ +static inline int +tegra_pmc_powergate_sequence_power_up(struct tegra_pmc *pmc, unsigned int id, + struct clk *clk, + struct reset_control *rst) +{ + return -ENOSYS; +} + +static inline int +tegra_pmc_io_pad_power_enable(struct tegra_pmc *pmc, enum tegra_io_pad id) +{ + return -ENOSYS; +} + +static inline int +tegra_pmc_io_pad_power_disable(struct tegra_pmc *pmc, enum tegra_io_pad id) +{ + return -ENOSYS; +} + static inline int tegra_powergate_power_on(unsigned int id) { return -ENOSYS; From ba99035bf16ef0d4a7f6acd56fc9292c0bd0d42e Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Sat, 17 Jan 2026 18:59:10 +0100 Subject: [PATCH 148/171] soc: apple: rtkit: Add function to poweroff Add a function to put a co-processor into the lowest possible power state from which recovery usually isn't possible without a full SoC reset. This is required for the USB4/Thunderbolt co-processors which can be restarted since the entire USB4 root complex can be completely reset independently of the rest of the SoC. Reviewed-by: Janne Grunau Link: https://patch.msgid.link/20260117-apple-rtkit-poweroff-v2-1-b882a180e44d@kernel.org Signed-off-by: Sven Peter --- drivers/soc/apple/rtkit.c | 16 ++++++++++++++++ include/linux/soc/apple/rtkit.h | 7 +++++++ 2 files changed, 23 insertions(+) diff --git a/drivers/soc/apple/rtkit.c b/drivers/soc/apple/rtkit.c index b8d4da147d23..4ad4f964fde7 100644 --- a/drivers/soc/apple/rtkit.c +++ b/drivers/soc/apple/rtkit.c @@ -851,6 +851,22 @@ int apple_rtkit_shutdown(struct apple_rtkit *rtk) } EXPORT_SYMBOL_GPL(apple_rtkit_shutdown); +int apple_rtkit_poweroff(struct apple_rtkit *rtk) +{ + int ret; + + ret = apple_rtkit_set_ap_power_state(rtk, APPLE_RTKIT_PWR_STATE_OFF); + if (ret) + return ret; + + ret = apple_rtkit_set_iop_power_state(rtk, APPLE_RTKIT_PWR_STATE_OFF); + if (ret) + return ret; + + return apple_rtkit_reinit(rtk); +} +EXPORT_SYMBOL_GPL(apple_rtkit_poweroff); + int apple_rtkit_idle(struct apple_rtkit *rtk) { int ret; diff --git a/include/linux/soc/apple/rtkit.h b/include/linux/soc/apple/rtkit.h index 736f53018017..bda3c528b515 100644 --- a/include/linux/soc/apple/rtkit.h +++ b/include/linux/soc/apple/rtkit.h @@ -125,6 +125,13 @@ int apple_rtkit_wake(struct apple_rtkit *rtk); */ int apple_rtkit_shutdown(struct apple_rtkit *rtk); +/* + * Put the co-processor into the lowest power state. Note that it usually + * is not possible to recover from this state without a full SoC reset. + */ + +int apple_rtkit_poweroff(struct apple_rtkit *rtk); + /* * Put the co-processor into idle mode */ From 16de4c6a8fe9ff497ca1aba33ef0dbee09f11952 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 4 Dec 2025 10:44:12 +0100 Subject: [PATCH 149/171] reset: gpio: suppress bind attributes in sysfs This is a special device that's created dynamically and is supposed to stay in memory forever. We also currently don't have a devlink between it and the actual reset consumer. Suppress sysfs bind attributes so that user-space can't unbind the device because - as of now - it will cause a use-after-free splat from any user that puts the reset control handle. Fixes: cee544a40e44 ("reset: gpio: Add GPIO-based reset controller") Cc: stable@vger.kernel.org Signed-off-by: Bartosz Golaszewski Reviewed-by: Krzysztof Kozlowski Signed-off-by: Philipp Zabel --- drivers/reset/reset-gpio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/reset/reset-gpio.c b/drivers/reset/reset-gpio.c index e5512b3b596b..626c4c639c15 100644 --- a/drivers/reset/reset-gpio.c +++ b/drivers/reset/reset-gpio.c @@ -111,6 +111,7 @@ static struct auxiliary_driver reset_gpio_driver = { .id_table = reset_gpio_ids, .driver = { .name = "reset-gpio", + .suppress_bind_attrs = true, }, }; module_auxiliary_driver(reset_gpio_driver); From 266f35701b6f7ddd9521310eb5add01001d4a614 Mon Sep 17 00:00:00 2001 From: Jason-JH Lin Date: Fri, 31 Oct 2025 23:56:30 +0800 Subject: [PATCH 150/171] mailbox: mtk-cmdq: Add cmdq private data to cmdq_pkt for generating instruction Add the cmdq_mbox_priv structure to store the private data of GCE, such as the shift bits of the physical address. Then, include the cmdq_mbox_priv structure within the cmdq_pkt structure. This allows CMDQ users to utilize the private data in cmdq_pkt to generate GCE instructions when needed. Additionally, having cmdq_mbox_priv makes it easier to expand and reference other GCE private data in the future. Add cmdq_get_mbox_priv() for CMDQ users to get all the private data into the cmdq_mbox_priv of the cmdq_pkt. Signed-off-by: Jason-JH Lin Reviewed-by: AngeloGioacchino Del Regno Acked-by: Jassi Brar Acked-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno --- drivers/mailbox/mtk-cmdq-mailbox.c | 8 ++++++++ include/linux/mailbox/mtk-cmdq-mailbox.h | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index 5791f80f995a..95e8a5331b7c 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -104,6 +104,14 @@ static inline dma_addr_t cmdq_revert_gce_addr(u32 addr, const struct gce_plat *p return (dma_addr_t)addr << pdata->shift; } +void cmdq_get_mbox_priv(struct mbox_chan *chan, struct cmdq_mbox_priv *priv) +{ + struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox); + + priv->shift_pa = cmdq->pdata->shift; +} +EXPORT_SYMBOL(cmdq_get_mbox_priv); + u8 cmdq_get_shift_pa(struct mbox_chan *chan) { struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox); diff --git a/include/linux/mailbox/mtk-cmdq-mailbox.h b/include/linux/mailbox/mtk-cmdq-mailbox.h index e1555e06e7e5..73b70be4a8a7 100644 --- a/include/linux/mailbox/mtk-cmdq-mailbox.h +++ b/include/linux/mailbox/mtk-cmdq-mailbox.h @@ -70,13 +70,31 @@ struct cmdq_cb_data { struct cmdq_pkt *pkt; }; +struct cmdq_mbox_priv { + u8 shift_pa; +}; + struct cmdq_pkt { void *va_base; dma_addr_t pa_base; size_t cmd_buf_size; /* command occupied size */ size_t buf_size; /* real buffer size */ + struct cmdq_mbox_priv priv; /* for generating instruction */ }; +/** + * cmdq_get_mbox_priv() - get the private data of mailbox channel + * @chan: mailbox channel + * @priv: pointer to store the private data of mailbox channel + * + * While generating the GCE instruction to command buffer, the private data + * of GCE hardware may need to be referenced, such as the shift bits of + * physical address. + * + * This function should be called before generating the GCE instruction. + */ +void cmdq_get_mbox_priv(struct mbox_chan *chan, struct cmdq_mbox_priv *priv); + /** * cmdq_get_shift_pa() - get the shift bits of physical address * @chan: mailbox channel From 7005b7cb2fff9081a6b1738b84a8ea12a6781fb3 Mon Sep 17 00:00:00 2001 From: Jason-JH Lin Date: Fri, 31 Oct 2025 23:56:31 +0800 Subject: [PATCH 151/171] mailbox: mtk-cmdq: Add GCE hardware virtualization configuration The GCE hardware virtualization configuration supports the isolation of GCE hardware resources across different OS environments. Each OS is treated as a virtual machine (VM) for GCE purposes. There are 6 VMs and 1 host VM. The host VM has main control over the GCE virtualization settings for all VMs. To properly access the GCE thread registers, it is necessary to configure access permissions for specific GCE threads assigned to different VMs. Currently, since only the host VM is being used, it is required to enable access permissions for all GCE threads for the host VM. There are 2 VM configurations: 1. VM_ID_MAP There are 4 registers to allocate 32 GCE threads across different VMs: VM_ID_MAP0 for threads 0-9, VM_ID_MAP1 for threads 10-19, VM_ID_MAP2 for threads 20-29, and VM_ID_MAP3 for threads 30-31. Each thread has a 3-bit configuration, where setting all bits to 1 configures the thread for the host VM. 2. VM_CPR_GSIZE It is used to allocate the CPR SRAM size to each VM. Each VM has 4-bit configuration, where setting bit 0-3 to configures the size of host VM. This setting must be configured before the VM configuration to prevent resource leakage. Signed-off-by: Jason-JH Lin Reviewed-by: AngeloGioacchino Del Regno Acked-by: Jassi Brar Acked-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno --- drivers/mailbox/mtk-cmdq-mailbox.c | 48 ++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index 95e8a5331b7c..a544108ddae7 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -43,6 +43,13 @@ #define GCE_CTRL_BY_SW GENMASK(2, 0) #define GCE_DDR_EN GENMASK(18, 16) +#define GCE_VM_ID_MAP(n) (0x5018 + (n) / 10 * 4) +#define GCE_VM_ID_MAP_THR_FLD_SHIFT(n) ((n) % 10 * 3) +#define GCE_VM_ID_MAP_HOST_VM GENMASK(2, 0) +#define GCE_VM_CPR_GSIZE 0x50c4 +#define GCE_VM_CPR_GSIZE_FLD_SHIFT(vm_id) ((vm_id) * 4) +#define GCE_VM_CPR_GSIZE_MAX GENMASK(3, 0) + #define CMDQ_THR_ACTIVE_SLOT_CYCLES 0x3200 #define CMDQ_THR_ENABLED 0x1 #define CMDQ_THR_DISABLED 0x0 @@ -89,6 +96,7 @@ struct gce_plat { u8 shift; bool control_by_sw; bool sw_ddr_en; + bool gce_vm; u32 gce_num; }; @@ -120,6 +128,45 @@ u8 cmdq_get_shift_pa(struct mbox_chan *chan) } EXPORT_SYMBOL(cmdq_get_shift_pa); +static void cmdq_vm_init(struct cmdq *cmdq) +{ + int i; + u32 vm_cpr_gsize = 0, vm_id_map = 0; + u32 *vm_map = NULL; + + if (!cmdq->pdata->gce_vm) + return; + + vm_map = kcalloc(cmdq->pdata->thread_nr, sizeof(*vm_map), GFP_KERNEL); + if (!vm_map) + return; + + /* only configure the max CPR SRAM size to host vm (vm_id = 0) currently */ + vm_cpr_gsize = GCE_VM_CPR_GSIZE_MAX << GCE_VM_CPR_GSIZE_FLD_SHIFT(0); + + /* set all thread mapping to host vm currently */ + for (i = 0; i < cmdq->pdata->thread_nr; i++) + vm_map[i] = GCE_VM_ID_MAP_HOST_VM << GCE_VM_ID_MAP_THR_FLD_SHIFT(i); + + /* set the amount of CPR SRAM to allocate to each VM */ + writel(vm_cpr_gsize, cmdq->base + GCE_VM_CPR_GSIZE); + + /* config CPR_GSIZE before setting VM_ID_MAP to avoid data leakage */ + for (i = 0; i < cmdq->pdata->thread_nr; i++) { + vm_id_map |= vm_map[i]; + /* config every 10 threads, e.g., thread id=0~9, 10~19, ..., into one register */ + if ((i + 1) % 10 == 0) { + writel(vm_id_map, cmdq->base + GCE_VM_ID_MAP(i)); + vm_id_map = 0; + } + } + /* config remaining threads settings */ + if (cmdq->pdata->thread_nr % 10 != 0) + writel(vm_id_map, cmdq->base + GCE_VM_ID_MAP(cmdq->pdata->thread_nr - 1)); + + kfree(vm_map); +} + static void cmdq_gctl_value_toggle(struct cmdq *cmdq, bool ddr_enable) { u32 val = cmdq->pdata->control_by_sw ? GCE_CTRL_BY_SW : 0; @@ -164,6 +211,7 @@ static void cmdq_init(struct cmdq *cmdq) WARN_ON(clk_bulk_enable(cmdq->pdata->gce_num, cmdq->clocks)); + cmdq_vm_init(cmdq); cmdq_gctl_value_toggle(cmdq, true); writel(CMDQ_THR_ACTIVE_SLOT_CYCLES, cmdq->base + CMDQ_THR_SLOT_CYCLES); From 1c1874843bc43d9f333d441af00f61ece2373e5d Mon Sep 17 00:00:00 2001 From: Jason-JH Lin Date: Fri, 31 Oct 2025 23:56:32 +0800 Subject: [PATCH 152/171] mailbox: mtk-cmdq: Add mminfra_offset configuration for DRAM transaction The GCE in MT8196 is placed in MMINFRA and requires all addresses in GCE instructions for DRAM transactions to be IOVA. Due to MMIO, if the GCE needs to access a hardware register at 0x1000_0000, but the SMMU is also mapping a DRAM block at 0x1000_0000, the MMINFRA will not know whether to write to the hardware register or the DRAM. To solve this, MMINFRA treats addresses greater than 2G as data paths and those less than 2G as config paths because the DRAM start address is currently at 2G (0x8000_0000). On the data path, MMINFRA remaps DRAM addresses by subtracting 2G, allowing SMMU to map DRAM addresses less than 2G. For example, if the DRAM start address 0x8000_0000 is mapped to IOVA=0x0, when GCE accesses IOVA=0x0, it must add a 2G offset to the address in the GCE instruction. MMINFRA will then see it as a data path (IOVA >= 2G) and subtract 2G, allowing GCE to access IOVA=0x0. Since the MMINFRA remap subtracting 2G is done in hardware and cannot be configured by software, the address of DRAM in GCE instruction must always add 2G to ensure proper access. After that, the shift functions do more than just shift addresses, so the APIs were renamed to cmdq_convert_gce_addr() and cmdq_revert_gce_addr(). This 2G adjustment is referred to as mminfra_offset in the CMDQ driver. CMDQ helper can get the mminfra_offset from the cmdq_mbox_priv of cmdq_pkt and add the mminfra_offset to the DRAM address in GCE instructions. Signed-off-by: Jason-JH Lin Reviewed-by: AngeloGioacchino Del Regno Acked-by: Jassi Brar Acked-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno --- drivers/mailbox/mtk-cmdq-mailbox.c | 6 ++++-- include/linux/mailbox/mtk-cmdq-mailbox.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index a544108ddae7..a9c06e4bbad4 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -94,6 +94,7 @@ struct cmdq { struct gce_plat { u32 thread_nr; u8 shift; + dma_addr_t mminfra_offset; bool control_by_sw; bool sw_ddr_en; bool gce_vm; @@ -103,13 +104,13 @@ struct gce_plat { static inline u32 cmdq_convert_gce_addr(dma_addr_t addr, const struct gce_plat *pdata) { /* Convert DMA addr (PA or IOVA) to GCE readable addr */ - return addr >> pdata->shift; + return (addr + pdata->mminfra_offset) >> pdata->shift; } static inline dma_addr_t cmdq_revert_gce_addr(u32 addr, const struct gce_plat *pdata) { /* Revert GCE readable addr to DMA addr (PA or IOVA) */ - return (dma_addr_t)addr << pdata->shift; + return ((dma_addr_t)addr << pdata->shift) - pdata->mminfra_offset; } void cmdq_get_mbox_priv(struct mbox_chan *chan, struct cmdq_mbox_priv *priv) @@ -117,6 +118,7 @@ void cmdq_get_mbox_priv(struct mbox_chan *chan, struct cmdq_mbox_priv *priv) struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox); priv->shift_pa = cmdq->pdata->shift; + priv->mminfra_offset = cmdq->pdata->mminfra_offset; } EXPORT_SYMBOL(cmdq_get_mbox_priv); diff --git a/include/linux/mailbox/mtk-cmdq-mailbox.h b/include/linux/mailbox/mtk-cmdq-mailbox.h index 73b70be4a8a7..07c1bfbdb8c4 100644 --- a/include/linux/mailbox/mtk-cmdq-mailbox.h +++ b/include/linux/mailbox/mtk-cmdq-mailbox.h @@ -72,6 +72,7 @@ struct cmdq_cb_data { struct cmdq_mbox_priv { u8 shift_pa; + dma_addr_t mminfra_offset; }; struct cmdq_pkt { From 5ea617e818333a2078dadc11e5734886e39901d0 Mon Sep 17 00:00:00 2001 From: Jason-JH Lin Date: Fri, 31 Oct 2025 23:56:33 +0800 Subject: [PATCH 153/171] mailbox: mtk-cmdq: Add driver data to support for MT8196 MT8196 has 2 new hardware configuration compared with the previous SoC, which correspond to the 2 new driver data: 1. mminfra_offset: For GCE data path control Since GCE has been moved into mminfra, GCE needs to append the mminfra offset to the DRAM address when accessing the DRAM. 2. gce_vm: For GCE hardware virtualization control Currently, the first version of the mt8196 mailbox controller only requires setting the VM-related registers to enable the permissions of a host VM. Signed-off-by: Jason-JH Lin Reviewed-by: CK Hu Reviewed-by: AngeloGioacchino Del Regno Acked-by: Jassi Brar Acked-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno --- drivers/mailbox/mtk-cmdq-mailbox.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index a9c06e4bbad4..1bf6984948ef 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -840,6 +841,16 @@ static const struct gce_plat gce_plat_mt8195 = { .gce_num = 2 }; +static const struct gce_plat gce_plat_mt8196 = { + .thread_nr = 32, + .shift = 3, + .mminfra_offset = SZ_2G, + .control_by_sw = true, + .sw_ddr_en = true, + .gce_vm = true, + .gce_num = 2 +}; + static const struct of_device_id cmdq_of_ids[] = { {.compatible = "mediatek,mt6779-gce", .data = (void *)&gce_plat_mt6779}, {.compatible = "mediatek,mt8173-gce", .data = (void *)&gce_plat_mt8173}, @@ -848,6 +859,7 @@ static const struct of_device_id cmdq_of_ids[] = { {.compatible = "mediatek,mt8188-gce", .data = (void *)&gce_plat_mt8188}, {.compatible = "mediatek,mt8192-gce", .data = (void *)&gce_plat_mt8192}, {.compatible = "mediatek,mt8195-gce", .data = (void *)&gce_plat_mt8195}, + {.compatible = "mediatek,mt8196-gce", .data = (void *)&gce_plat_mt8196}, {} }; MODULE_DEVICE_TABLE(of, cmdq_of_ids); From c775b23b1f78626daca804bd26f1460368f20406 Mon Sep 17 00:00:00 2001 From: Jason-JH Lin Date: Fri, 31 Oct 2025 23:56:34 +0800 Subject: [PATCH 154/171] soc: mediatek: mtk-cmdq: Add cmdq_get_mbox_priv() in cmdq_pkt_create() Add cmdq_get_mbox_priv() in cmdq_pkt_create() to ensure getting private data before generating GCE instructions. Signed-off-by: Jason-JH Lin Acked-by: Jassi Brar Acked-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-cmdq-helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c index 455221e8de24..8feeaa320359 100644 --- a/drivers/soc/mediatek/mtk-cmdq-helper.c +++ b/drivers/soc/mediatek/mtk-cmdq-helper.c @@ -140,6 +140,7 @@ int cmdq_pkt_create(struct cmdq_client *client, struct cmdq_pkt *pkt, size_t siz } pkt->pa_base = dma_addr; + cmdq_get_mbox_priv(client->chan, &pkt->priv); return 0; } From 4bf783d8415cc397334b375a05f0b2321fc6c319 Mon Sep 17 00:00:00 2001 From: Jason-JH Lin Date: Fri, 31 Oct 2025 23:56:35 +0800 Subject: [PATCH 155/171] soc: mediatek: mtk-cmdq: Add pa_base parsing for hardware without subsys ID support When GCE executes instructions, it typically locates the corresponding hardware register using the subsys ID. For hardware that does not support subsys ID, the subsys ID is set to an invalid value, and the physical address must be used to generate GCE instructions. The main advantage of using subsys ID is to reduce the number of instructions. Without subsys ID, an additional `ASSIGN` instruction is needed to assign the high bytes of the physical address, which can impact performance if too many instructions are required. However, if the hardware does not support subsys ID, using the physical address is the only option to achieve the same functionality. This commit adds a pa_base parsing flow to the cmdq_client_reg structure to handle hardware without subsys ID support. Signed-off-by: Jason-JH Lin Reviewed-by: AngeloGioacchino Del Regno Acked-by: Jassi Brar Acked-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-cmdq-helper.c | 16 ++++++++++++++-- include/linux/soc/mediatek/mtk-cmdq.h | 3 +++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c index 8feeaa320359..80806fbeba91 100644 --- a/drivers/soc/mediatek/mtk-cmdq-helper.c +++ b/drivers/soc/mediatek/mtk-cmdq-helper.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #define CMDQ_WRITE_ENABLE_MASK BIT(0) @@ -60,20 +61,31 @@ int cmdq_dev_get_client_reg(struct device *dev, struct cmdq_client_reg *client_reg, int idx) { struct of_phandle_args spec; + struct resource res; int err; if (!client_reg) return -ENOENT; + err = of_address_to_resource(dev->of_node, 0, &res); + if (err) { + dev_err(dev, "Missing reg in %s node\n", dev->of_node->full_name); + return -EINVAL; + } + client_reg->pa_base = res.start; + err = of_parse_phandle_with_fixed_args(dev->of_node, "mediatek,gce-client-reg", 3, idx, &spec); if (err < 0) { - dev_warn(dev, + dev_dbg(dev, "error %d can't parse gce-client-reg property (%d)", err, idx); - return err; + /* make subsys invalid */ + client_reg->subsys = CMDQ_SUBSYS_INVALID; + + return 0; } client_reg->subsys = (u8)spec.args[0]; diff --git a/include/linux/soc/mediatek/mtk-cmdq.h b/include/linux/soc/mediatek/mtk-cmdq.h index 0c3906e8ad19..3578cc9200e9 100644 --- a/include/linux/soc/mediatek/mtk-cmdq.h +++ b/include/linux/soc/mediatek/mtk-cmdq.h @@ -23,6 +23,8 @@ #define CMDQ_THR_SPR_IDX2 (2) #define CMDQ_THR_SPR_IDX3 (3) +#define CMDQ_SUBSYS_INVALID (U8_MAX) + struct cmdq_pkt; enum cmdq_logic_op { @@ -52,6 +54,7 @@ struct cmdq_operand { struct cmdq_client_reg { u8 subsys; + phys_addr_t pa_base; u16 offset; u16 size; }; From 40dc5bbad63b5f60dd2e69a32def1a2673cba09e Mon Sep 17 00:00:00 2001 From: Jason-JH Lin Date: Fri, 31 Oct 2025 23:56:36 +0800 Subject: [PATCH 156/171] soc: mediatek: mtk-cmdq: Extend cmdq_pkt_write API for SoCs without subsys ID This patch extends the cmdq_pkt_write API to support SoCs that do not have subsys ID mapping by introducing new register write APIs: - cmdq_pkt_write_pa() and cmdq_pkt_write_subsys() replace cmdq_pkt_write() - cmdq_pkt_write_mask_pa() and cmdq_pkt_write_mask_subsys() replace cmdq_pkt_write_mask() To ensure consistent function pointer interfaces, both cmdq_pkt_write_pa() and cmdq_pkt_write_subsys() provide subsys and pa_base parameters. This unifies how register writes are invoked, regardless of whether subsys ID is supported by the device. All GCEs support writing registers by PA (with mask) without subsys, but this requires extra GCE instructions to convert the PA into a GCE readable format, reducing performance compared to using subsys directly. Therefore, subsys is preferred for register writes when available. API documentation and function pointer declarations in cmdq_client_reg have been updated. The original write APIs will be removed after all CMDQ users transition to the new interfaces. Signed-off-by: Jason-JH Lin Reviewed-by: AngeloGioacchino Del Regno Acked-by: Jassi Brar Acked-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-cmdq-helper.c | 54 ++++++++++++++++ include/linux/soc/mediatek/mtk-cmdq.h | 90 ++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c index 80806fbeba91..b30715ef0d6f 100644 --- a/drivers/soc/mediatek/mtk-cmdq-helper.c +++ b/drivers/soc/mediatek/mtk-cmdq-helper.c @@ -85,6 +85,16 @@ int cmdq_dev_get_client_reg(struct device *dev, /* make subsys invalid */ client_reg->subsys = CMDQ_SUBSYS_INVALID; + /* + * All GCEs support writing register PA with mask without subsys, + * but this requires extra GCE instructions to convert the PA into + * a format that GCE can handle, which is less performance than + * directly using subsys. Therefore, when subsys is available, + * we prefer to use subsys for writing register PA. + */ + client_reg->pkt_write = cmdq_pkt_write_pa; + client_reg->pkt_write_mask = cmdq_pkt_write_mask_pa; + return 0; } @@ -93,6 +103,9 @@ int cmdq_dev_get_client_reg(struct device *dev, client_reg->size = (u16)spec.args[2]; of_node_put(spec.np); + client_reg->pkt_write = cmdq_pkt_write_subsys; + client_reg->pkt_write_mask = cmdq_pkt_write_mask_subsys; + return 0; } EXPORT_SYMBOL(cmdq_dev_get_client_reg); @@ -214,6 +227,26 @@ int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value) } EXPORT_SYMBOL(cmdq_pkt_write); +int cmdq_pkt_write_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, u32 pa_base, + u16 offset, u32 value) +{ + int err; + + err = cmdq_pkt_assign(pkt, CMDQ_THR_SPR_IDX0, CMDQ_ADDR_HIGH(pa_base)); + if (err < 0) + return err; + + return cmdq_pkt_write_s_value(pkt, CMDQ_THR_SPR_IDX0, CMDQ_ADDR_LOW(offset), value); +} +EXPORT_SYMBOL(cmdq_pkt_write_pa); + +int cmdq_pkt_write_subsys(struct cmdq_pkt *pkt, u8 subsys, u32 pa_base /*unused*/, + u16 offset, u32 value) +{ + return cmdq_pkt_write(pkt, subsys, offset, value); +} +EXPORT_SYMBOL(cmdq_pkt_write_subsys); + int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value, u32 mask) { @@ -231,6 +264,27 @@ int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys, } EXPORT_SYMBOL(cmdq_pkt_write_mask); +int cmdq_pkt_write_mask_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, u32 pa_base, + u16 offset, u32 value, u32 mask) +{ + int err; + + err = cmdq_pkt_assign(pkt, CMDQ_THR_SPR_IDX0, CMDQ_ADDR_HIGH(pa_base)); + if (err < 0) + return err; + + return cmdq_pkt_write_s_mask_value(pkt, CMDQ_THR_SPR_IDX0, + CMDQ_ADDR_LOW(offset), value, mask); +} +EXPORT_SYMBOL(cmdq_pkt_write_mask_pa); + +int cmdq_pkt_write_mask_subsys(struct cmdq_pkt *pkt, u8 subsys, u32 pa_base /*unused*/, + u16 offset, u32 value, u32 mask) +{ + return cmdq_pkt_write_mask(pkt, subsys, offset, value, mask); +} +EXPORT_SYMBOL(cmdq_pkt_write_mask_subsys); + int cmdq_pkt_read_s(struct cmdq_pkt *pkt, u16 high_addr_reg_idx, u16 addr_low, u16 reg_idx) { diff --git a/include/linux/soc/mediatek/mtk-cmdq.h b/include/linux/soc/mediatek/mtk-cmdq.h index 3578cc9200e9..a06b5a61f337 100644 --- a/include/linux/soc/mediatek/mtk-cmdq.h +++ b/include/linux/soc/mediatek/mtk-cmdq.h @@ -57,6 +57,17 @@ struct cmdq_client_reg { phys_addr_t pa_base; u16 offset; u16 size; + + /* + * Client only uses these functions for MMIO access, + * so doesn't need to handle the mminfra_offset. + * The mminfra_offset is used for DRAM access and + * is handled internally by CMDQ APIs. + */ + int (*pkt_write)(struct cmdq_pkt *pkt, u8 subsys, u32 pa_base, + u16 offset, u32 value); + int (*pkt_write_mask)(struct cmdq_pkt *pkt, u8 subsys, u32 pa_base, + u16 offset, u32 value, u32 mask); }; struct cmdq_client { @@ -124,6 +135,32 @@ void cmdq_pkt_destroy(struct cmdq_client *client, struct cmdq_pkt *pkt); */ int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value); +/** + * cmdq_pkt_write_pa() - append write command to the CMDQ packet with pa_base + * @pkt: the CMDQ packet + * @subsys: unused parameter + * @pa_base: the physical address base of the hardware register + * @offset: register offset from CMDQ sub system + * @value: the specified target register value + * + * Return: 0 for success; else the error code is returned + */ +int cmdq_pkt_write_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, + u32 pa_base, u16 offset, u32 value); + +/** + * cmdq_pkt_write_subsys() - append write command to the CMDQ packet with subsys + * @pkt: the CMDQ packet + * @subsys: the CMDQ sub system code + * @pa_base: unused parameter + * @offset: register offset from CMDQ sub system + * @value: the specified target register value + * + * Return: 0 for success; else the error code is returned + */ +int cmdq_pkt_write_subsys(struct cmdq_pkt *pkt, u8 subsys, + u32 pa_base /*unused*/, u16 offset, u32 value); + /** * cmdq_pkt_write_mask() - append write command with mask to the CMDQ packet * @pkt: the CMDQ packet @@ -137,6 +174,34 @@ int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value); int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value, u32 mask); +/** + * cmdq_pkt_write_mask_pa() - append write command with mask to the CMDQ packet with pa + * @pkt: the CMDQ packet + * @subsys: unused parameter + * @pa_base: the physical address base of the hardware register + * @offset: register offset from CMDQ sub system + * @value: the specified target register value + * @mask: the specified target register mask + * + * Return: 0 for success; else the error code is returned + */ +int cmdq_pkt_write_mask_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, + u32 pa_base, u16 offset, u32 value, u32 mask); + +/** + * cmdq_pkt_write_mask_subsys() - append write command with mask to the CMDQ packet with subsys + * @pkt: the CMDQ packet + * @subsys: the CMDQ sub system code + * @pa_base: unused parameter + * @offset: register offset from CMDQ sub system + * @value: the specified target register value + * @mask: the specified target register mask + * + * Return: 0 for success; else the error code is returned + */ +int cmdq_pkt_write_mask_subsys(struct cmdq_pkt *pkt, u8 subsys, + u32 pa_base /*unused*/, u16 offset, u32 value, u32 mask); + /* * cmdq_pkt_read_s() - append read_s command to the CMDQ packet * @pkt: the CMDQ packet @@ -421,12 +486,37 @@ static inline int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u3 return -ENOENT; } +static inline int cmdq_pkt_write_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, + u32 pa_base, u16 offset, u32 value) +{ + return -ENOENT; +} + +static inline int cmdq_pkt_write_subsys(struct cmdq_pkt *pkt, u8 subsys, + u32 pa_base /*unused*/, u16 offset, u32 value) +{ + return -ENOENT; +} + static inline int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value, u32 mask) { return -ENOENT; } +static inline int cmdq_pkt_write_mask_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, + u32 pa_base, u16 offset, u32 value, u32 mask) +{ + return -ENOENT; +} + +static inline int cmdq_pkt_write_mask_subsys(struct cmdq_pkt *pkt, u8 subsys, + u32 pa_base /*unused*/, u16 offset, + u32 value, u32 mask) +{ + return -ENOENT; +} + static inline int cmdq_pkt_read_s(struct cmdq_pkt *pkt, u16 high_addr_reg_idx, u16 addr_low, u16 reg_idx) { From 22ce09ce1af574747fce072c3f62c29c440538d7 Mon Sep 17 00:00:00 2001 From: Jason-JH Lin Date: Fri, 31 Oct 2025 23:56:37 +0800 Subject: [PATCH 157/171] soc: mediatek: mtk-cmdq: Add mminfra_offset adjustment for DRAM addresses Since GCE has been moved to MMINFRA in MT8196, all transactions from MMINFRA to DRAM will have their addresses adjusted by subtracting a mminfra_offset. Therefore, the CMDQ helper driver needs to get the mminfra_offset value of the SoC from cmdq_mbox_priv of cmdq_pkt and then add it to the DRAM address when generating instructions to ensure GCE accesses the correct DRAM address. CMDQ users can then call CMDQ helper APIs as usual. Signed-off-by: Jason-JH Lin Reviewed-by: AngeloGioacchino Del Regno Acked-by: Jassi Brar Acked-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-cmdq-helper.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c index b30715ef0d6f..67e5879374ac 100644 --- a/drivers/soc/mediatek/mtk-cmdq-helper.c +++ b/drivers/soc/mediatek/mtk-cmdq-helper.c @@ -372,6 +372,7 @@ int cmdq_pkt_mem_move(struct cmdq_pkt *pkt, dma_addr_t src_addr, dma_addr_t dst_ int ret; /* read the value of src_addr into high_addr_reg_idx */ + src_addr += pkt->priv.mminfra_offset; ret = cmdq_pkt_assign(pkt, high_addr_reg_idx, CMDQ_ADDR_HIGH(src_addr)); if (ret < 0) return ret; @@ -380,6 +381,7 @@ int cmdq_pkt_mem_move(struct cmdq_pkt *pkt, dma_addr_t src_addr, dma_addr_t dst_ return ret; /* write the value of value_reg_idx into dst_addr */ + dst_addr += pkt->priv.mminfra_offset; ret = cmdq_pkt_assign(pkt, high_addr_reg_idx, CMDQ_ADDR_HIGH(dst_addr)); if (ret < 0) return ret; @@ -505,7 +507,7 @@ int cmdq_pkt_poll_addr(struct cmdq_pkt *pkt, dma_addr_t addr, u32 value, u32 mas inst.op = CMDQ_CODE_MASK; inst.dst_t = CMDQ_REG_TYPE; inst.sop = CMDQ_POLL_ADDR_GPR; - inst.value = addr; + inst.value = addr + pkt->priv.mminfra_offset; ret = cmdq_pkt_append_command(pkt, inst); if (ret < 0) return ret; @@ -565,7 +567,7 @@ int cmdq_pkt_jump_abs(struct cmdq_pkt *pkt, dma_addr_t addr, u8 shift_pa) struct cmdq_instruction inst = { .op = CMDQ_CODE_JUMP, .offset = CMDQ_JUMP_ABSOLUTE, - .value = addr >> shift_pa + .value = (addr + pkt->priv.mminfra_offset) >> pkt->priv.shift_pa }; return cmdq_pkt_append_command(pkt, inst); } From 65d5727645acbc019fd17d47f47b743eb116ff14 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Mon, 19 Jan 2026 13:57:15 +0800 Subject: [PATCH 158/171] soc: fsl: qe: qe_ports_ic: Consolidate chained IRQ handler install/remove The driver currently sets the handler data and the chained handler in two separate steps. This creates a theoretical race window where an interrupt could fire after the handler is set but before the data is assigned, leading to a NULL pointer dereference. Replace the two calls with irq_set_chained_handler_and_data() to set both the handler and its data atomically under the irq_desc->lock. Signed-off-by: Chen Ni Link: https://lore.kernel.org/r/20260119055715.889001-1-nichen@iscas.ac.cn Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/qe/qe_ports_ic.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c index 61dd09fec6f6..8e2107e2cde5 100644 --- a/drivers/soc/fsl/qe/qe_ports_ic.c +++ b/drivers/soc/fsl/qe/qe_ports_ic.c @@ -114,8 +114,7 @@ static int qepic_probe(struct platform_device *pdev) if (!data->host) return -ENODEV; - irq_set_handler_data(irq, data); - irq_set_chained_handler(irq, qepic_cascade); + irq_set_chained_handler_and_data(irq, qepic_cascade, data); return 0; } From 212212062f981fe2c98d235ee008201a6dd36c28 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Wed, 26 Nov 2025 04:42:14 -0800 Subject: [PATCH 159/171] reset: imx8mp-audiomix: Drop unneeded macros The macros defining the mask values for the EARC, EARC PHY resets, and the DSP RUN_STALL signal can be dropped as they are not and will not be used anywhere else except to set the value of the "mask" field from "struct imx8mp_reset_map". In this particular case, based on the name of the "mask" field, you can already deduce what these values are for, which is why defining macros for them doesn't offer any new information, nor does it help with the code readability. Reviewed-by: Daniel Baluta Reviewed-by: Frank Li Reviewed-by: Philipp Zabel Signed-off-by: Laurentiu Mihalcea Signed-off-by: Philipp Zabel --- drivers/reset/reset-imx8mp-audiomix.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c index eceb37ff5dc5..acfa92b15329 100644 --- a/drivers/reset/reset-imx8mp-audiomix.c +++ b/drivers/reset/reset-imx8mp-audiomix.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -14,11 +15,7 @@ #include #define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET 0x200 -#define IMX8MP_AUDIOMIX_EARC_RESET_MASK BIT(0) -#define IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK BIT(1) - #define IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET 0x108 -#define IMX8MP_AUDIOMIX_DSP_RUNSTALL_MASK BIT(5) struct imx8mp_reset_map { unsigned int offset; @@ -29,17 +26,17 @@ struct imx8mp_reset_map { static const struct imx8mp_reset_map reset_map[] = { [IMX8MP_AUDIOMIX_EARC_RESET] = { .offset = IMX8MP_AUDIOMIX_EARC_RESET_OFFSET, - .mask = IMX8MP_AUDIOMIX_EARC_RESET_MASK, + .mask = BIT(0), .active_low = true, }, [IMX8MP_AUDIOMIX_EARC_PHY_RESET] = { .offset = IMX8MP_AUDIOMIX_EARC_RESET_OFFSET, - .mask = IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK, + .mask = BIT(1), .active_low = true, }, [IMX8MP_AUDIOMIX_DSP_RUNSTALL] = { .offset = IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET, - .mask = IMX8MP_AUDIOMIX_DSP_RUNSTALL_MASK, + .mask = BIT(5), .active_low = false, }, }; From 6d6818abec260e506ea5978df920430f54deb70a Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Wed, 26 Nov 2025 04:42:15 -0800 Subject: [PATCH 160/171] reset: imx8mp-audiomix: Switch to using regmap API Switch to using the regmap API to allow performing register operations under the same lock. This is needed for cases such as i.MX8ULP's SIM LPAV where clock gating, reset control and MUX-ing is performed via the same register (i.e. SYSCTRL0) and different subsystem APIs. Reviewed-by: Philipp Zabel Reviewed-by: Frank Li Signed-off-by: Laurentiu Mihalcea Signed-off-by: Philipp Zabel --- drivers/reset/reset-imx8mp-audiomix.c | 92 +++++++++++++++++---------- 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c index acfa92b15329..f6152c0cc5ff 100644 --- a/drivers/reset/reset-imx8mp-audiomix.c +++ b/drivers/reset/reset-imx8mp-audiomix.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET 0x200 @@ -43,8 +44,7 @@ static const struct imx8mp_reset_map reset_map[] = { struct imx8mp_audiomix_reset { struct reset_controller_dev rcdev; - spinlock_t lock; /* protect register read-modify-write cycle */ - void __iomem *base; + struct regmap *regmap; }; static struct imx8mp_audiomix_reset *to_imx8mp_audiomix_reset(struct reset_controller_dev *rcdev) @@ -56,26 +56,14 @@ static int imx8mp_audiomix_update(struct reset_controller_dev *rcdev, unsigned long id, bool assert) { struct imx8mp_audiomix_reset *priv = to_imx8mp_audiomix_reset(rcdev); - void __iomem *reg_addr = priv->base; - unsigned int mask, offset, active_low; - unsigned long reg, flags; + unsigned int mask, offset, active_low, val; mask = reset_map[id].mask; offset = reset_map[id].offset; active_low = reset_map[id].active_low; + val = (active_low ^ assert) ? mask : ~mask; - spin_lock_irqsave(&priv->lock, flags); - - reg = readl(reg_addr + offset); - if (active_low ^ assert) - reg |= mask; - else - reg &= ~mask; - writel(reg, reg_addr + offset); - - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; + return regmap_update_bits(priv->regmap, offset, mask, val); } static int imx8mp_audiomix_reset_assert(struct reset_controller_dev *rcdev, @@ -95,6 +83,52 @@ static const struct reset_control_ops imx8mp_audiomix_reset_ops = { .deassert = imx8mp_audiomix_reset_deassert, }; +static const struct regmap_config regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +/* assumption: registered only if not using parent regmap */ +static void imx8mp_audiomix_reset_iounmap(void *data) +{ + void __iomem *base = (void __iomem *)data; + + iounmap(base); +} + +static int imx8mp_audiomix_reset_get_regmap(struct imx8mp_audiomix_reset *priv) +{ + void __iomem *base; + struct device *dev; + int ret; + + dev = priv->rcdev.dev; + + /* try to use the parent's regmap */ + priv->regmap = dev_get_regmap(dev->parent, NULL); + if (priv->regmap) + return 0; + + /* ... if that's not possible then initialize the regmap right now */ + base = of_iomap(dev->parent->of_node, 0); + if (!base) + return dev_err_probe(dev, -ENOMEM, "failed to iomap address space\n"); + + ret = devm_add_action_or_reset(dev, + imx8mp_audiomix_reset_iounmap, + (void __force *)base); + if (ret) + return dev_err_probe(dev, ret, "failed to register action\n"); + + priv->regmap = devm_regmap_init_mmio(dev, base, ®map_config); + if (IS_ERR(priv->regmap)) + return dev_err_probe(dev, PTR_ERR(priv->regmap), + "failed to initialize regmap\n"); + + return 0; +} + static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) { @@ -106,36 +140,25 @@ static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev, if (!priv) return -ENOMEM; - spin_lock_init(&priv->lock); - priv->rcdev.owner = THIS_MODULE; priv->rcdev.nr_resets = ARRAY_SIZE(reset_map); priv->rcdev.ops = &imx8mp_audiomix_reset_ops; priv->rcdev.of_node = dev->parent->of_node; priv->rcdev.dev = dev; priv->rcdev.of_reset_n_cells = 1; - priv->base = of_iomap(dev->parent->of_node, 0); - if (!priv->base) - return -ENOMEM; dev_set_drvdata(dev, priv); + ret = imx8mp_audiomix_reset_get_regmap(priv); + if (ret) + return dev_err_probe(dev, ret, "failed to get regmap\n"); + ret = devm_reset_controller_register(dev, &priv->rcdev); if (ret) - goto out_unmap; + return dev_err_probe(dev, ret, + "failed to register reset controller\n"); return 0; - -out_unmap: - iounmap(priv->base); - return ret; -} - -static void imx8mp_audiomix_reset_remove(struct auxiliary_device *adev) -{ - struct imx8mp_audiomix_reset *priv = dev_get_drvdata(&adev->dev); - - iounmap(priv->base); } static const struct auxiliary_device_id imx8mp_audiomix_reset_ids[] = { @@ -148,7 +171,6 @@ MODULE_DEVICE_TABLE(auxiliary, imx8mp_audiomix_reset_ids); static struct auxiliary_driver imx8mp_audiomix_reset_driver = { .probe = imx8mp_audiomix_reset_probe, - .remove = imx8mp_audiomix_reset_remove, .id_table = imx8mp_audiomix_reset_ids, }; From cc3b1245cd64b1bb73204e9d697e812ea76e266d Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Wed, 26 Nov 2025 04:42:16 -0800 Subject: [PATCH 161/171] reset: imx8mp-audiomix: Extend the driver usage Switch to per-device reset map to allow reusing the driver for other NXP block control IPs. Reviewed-by: Daniel Baluta Reviewed-by: Frank Li Signed-off-by: Laurentiu Mihalcea Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/reset-imx8mp-audiomix.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c index f6152c0cc5ff..00eee528a2d2 100644 --- a/drivers/reset/reset-imx8mp-audiomix.c +++ b/drivers/reset/reset-imx8mp-audiomix.c @@ -24,7 +24,12 @@ struct imx8mp_reset_map { bool active_low; }; -static const struct imx8mp_reset_map reset_map[] = { +struct imx8mp_reset_info { + const struct imx8mp_reset_map *map; + int num_lines; +}; + +static const struct imx8mp_reset_map imx8mp_reset_map[] = { [IMX8MP_AUDIOMIX_EARC_RESET] = { .offset = IMX8MP_AUDIOMIX_EARC_RESET_OFFSET, .mask = BIT(0), @@ -42,9 +47,15 @@ static const struct imx8mp_reset_map reset_map[] = { }, }; +static const struct imx8mp_reset_info imx8mp_reset_info = { + .map = imx8mp_reset_map, + .num_lines = ARRAY_SIZE(imx8mp_reset_map), +}; + struct imx8mp_audiomix_reset { struct reset_controller_dev rcdev; struct regmap *regmap; + const struct imx8mp_reset_map *map; }; static struct imx8mp_audiomix_reset *to_imx8mp_audiomix_reset(struct reset_controller_dev *rcdev) @@ -56,6 +67,7 @@ static int imx8mp_audiomix_update(struct reset_controller_dev *rcdev, unsigned long id, bool assert) { struct imx8mp_audiomix_reset *priv = to_imx8mp_audiomix_reset(rcdev); + const struct imx8mp_reset_map *reset_map = priv->map; unsigned int mask, offset, active_low, val; mask = reset_map[id].mask; @@ -132,16 +144,20 @@ static int imx8mp_audiomix_reset_get_regmap(struct imx8mp_audiomix_reset *priv) static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) { + const struct imx8mp_reset_info *rinfo; struct imx8mp_audiomix_reset *priv; struct device *dev = &adev->dev; int ret; + rinfo = (void *)id->driver_data; + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->rcdev.owner = THIS_MODULE; - priv->rcdev.nr_resets = ARRAY_SIZE(reset_map); + priv->map = rinfo->map; + priv->rcdev.nr_resets = rinfo->num_lines; priv->rcdev.ops = &imx8mp_audiomix_reset_ops; priv->rcdev.of_node = dev->parent->of_node; priv->rcdev.dev = dev; @@ -164,6 +180,7 @@ static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev, static const struct auxiliary_device_id imx8mp_audiomix_reset_ids[] = { { .name = "clk_imx8mp_audiomix.reset", + .driver_data = (kernel_ulong_t)&imx8mp_reset_info, }, { } }; From 5aac7afc752be03bad2ed4d5074c62b3e4d9179f Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Wed, 26 Nov 2025 04:42:17 -0800 Subject: [PATCH 162/171] reset: imx8mp-audiomix: Support i.MX8ULP SIM LPAV Support i.MX8ULP's SIM LPAV by adding its reset map definition. Reviewed-by: Daniel Baluta Reviewed-by: Frank Li Reviewed-by: Philipp Zabel Signed-off-by: Laurentiu Mihalcea Signed-off-by: Philipp Zabel --- drivers/reset/reset-imx8mp-audiomix.c | 45 +++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c index 00eee528a2d2..b7fa3110f282 100644 --- a/drivers/reset/reset-imx8mp-audiomix.c +++ b/drivers/reset/reset-imx8mp-audiomix.c @@ -3,6 +3,7 @@ * Copyright 2024 NXP */ +#include #include #include @@ -18,6 +19,8 @@ #define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET 0x200 #define IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET 0x108 +#define IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET 0x8 + struct imx8mp_reset_map { unsigned int offset; unsigned int mask; @@ -52,6 +55,44 @@ static const struct imx8mp_reset_info imx8mp_reset_info = { .num_lines = ARRAY_SIZE(imx8mp_reset_map), }; +static const struct imx8mp_reset_map imx8ulp_reset_map[] = { + [IMX8ULP_SIM_LPAV_HIFI4_DSP_DBG_RST] = { + .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET, + .mask = BIT(25), + .active_low = false, + }, + [IMX8ULP_SIM_LPAV_HIFI4_DSP_RST] = { + .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET, + .mask = BIT(16), + .active_low = false, + }, + [IMX8ULP_SIM_LPAV_HIFI4_DSP_STALL] = { + .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET, + .mask = BIT(13), + .active_low = false, + }, + [IMX8ULP_SIM_LPAV_DSI_RST_BYTE_N] = { + .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET, + .mask = BIT(5), + .active_low = true, + }, + [IMX8ULP_SIM_LPAV_DSI_RST_ESC_N] = { + .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET, + .mask = BIT(4), + .active_low = true, + }, + [IMX8ULP_SIM_LPAV_DSI_RST_DPI_N] = { + .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET, + .mask = BIT(3), + .active_low = true, + }, +}; + +static const struct imx8mp_reset_info imx8ulp_reset_info = { + .map = imx8ulp_reset_map, + .num_lines = ARRAY_SIZE(imx8ulp_reset_map), +}; + struct imx8mp_audiomix_reset { struct reset_controller_dev rcdev; struct regmap *regmap; @@ -182,6 +223,10 @@ static const struct auxiliary_device_id imx8mp_audiomix_reset_ids[] = { .name = "clk_imx8mp_audiomix.reset", .driver_data = (kernel_ulong_t)&imx8mp_reset_info, }, + { + .name = "clk_imx8ulp_sim_lpav.reset", + .driver_data = (kernel_ulong_t)&imx8ulp_reset_info, + }, { } }; MODULE_DEVICE_TABLE(auxiliary, imx8mp_audiomix_reset_ids); From ae089de7adc4bd15c607b0045a304653cd6652e8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 2 Dec 2025 17:39:37 +0100 Subject: [PATCH 163/171] reset: gpio: check the return value of gpiod_set_value_cansleep() gpiod_set_value_cansleep() now returns an integer and can indicate failures. Propagate the return value to the caller of the reset API. Signed-off-by: Bartosz Golaszewski Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/reset-gpio.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/reset/reset-gpio.c b/drivers/reset/reset-gpio.c index 626c4c639c15..0a1610d9e78a 100644 --- a/drivers/reset/reset-gpio.c +++ b/drivers/reset/reset-gpio.c @@ -22,9 +22,7 @@ static int reset_gpio_assert(struct reset_controller_dev *rc, unsigned long id) { struct reset_gpio_priv *priv = rc_to_reset_gpio(rc); - gpiod_set_value_cansleep(priv->reset, 1); - - return 0; + return gpiod_set_value_cansleep(priv->reset, 1); } static int reset_gpio_deassert(struct reset_controller_dev *rc, @@ -32,9 +30,7 @@ static int reset_gpio_deassert(struct reset_controller_dev *rc, { struct reset_gpio_priv *priv = rc_to_reset_gpio(rc); - gpiod_set_value_cansleep(priv->reset, 0); - - return 0; + return gpiod_set_value_cansleep(priv->reset, 0); } static int reset_gpio_status(struct reset_controller_dev *rc, unsigned long id) From 2289ccd753deeb2cfe300d97e8697680c1ce556c Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 9 Jan 2026 16:36:58 +0200 Subject: [PATCH 164/171] reset: rzg2l-usbphy-ctrl: Propagate the return value of regmap_field_update_bits() Propagate the return value of regmap_field_update_bits() to avoid losing any possible error. With this, the return type of rzg2l_usbphy_ctrl_set_pwrrdy() was updated accordingly. Reviewed-by: Philipp Zabel Reviewed-by: Biju Das Signed-off-by: Claudiu Beznea Signed-off-by: Philipp Zabel --- drivers/reset/reset-rzg2l-usbphy-ctrl.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c index 4ecb9acb2641..9ce0c1f5d465 100644 --- a/drivers/reset/reset-rzg2l-usbphy-ctrl.c +++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c @@ -117,13 +117,13 @@ static const struct regmap_config rzg2l_usb_regconf = { .max_register = 1, }; -static void rzg2l_usbphy_ctrl_set_pwrrdy(struct regmap_field *pwrrdy, - bool power_on) +static int rzg2l_usbphy_ctrl_set_pwrrdy(struct regmap_field *pwrrdy, + bool power_on) { u32 val = power_on ? 0 : 1; /* The initialization path guarantees that the mask is 1 bit long. */ - regmap_field_update_bits(pwrrdy, 1, val); + return regmap_field_update_bits(pwrrdy, 1, val); } static void rzg2l_usbphy_ctrl_pwrrdy_off(void *data) @@ -138,6 +138,7 @@ static int rzg2l_usbphy_ctrl_pwrrdy_init(struct device *dev) struct regmap *regmap; const int *data; u32 args[2]; + int ret; data = device_get_match_data(dev); if ((uintptr_t)data != RZG2L_USBPHY_CTRL_PWRRDY) @@ -161,7 +162,9 @@ static int rzg2l_usbphy_ctrl_pwrrdy_init(struct device *dev) if (IS_ERR(pwrrdy)) return PTR_ERR(pwrrdy); - rzg2l_usbphy_ctrl_set_pwrrdy(pwrrdy, true); + ret = rzg2l_usbphy_ctrl_set_pwrrdy(priv->pwrrdy, true); + if (ret) + return ret; return devm_add_action_or_reset(dev, rzg2l_usbphy_ctrl_pwrrdy_off, pwrrdy); } From c5b7cd9adefc5c060facaff6f54d3187480e4e1b Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 9 Jan 2026 16:36:59 +0200 Subject: [PATCH 165/171] reset: rzg2l-usbphy-ctrl: Add suspend/resume support The RZ/G2L USBPHY control driver is also used on the RZ/G3S SoC. The RZ/G3S SoC supports a power-saving mode in which power to most USB components (including the USBPHY control block) is turned off. Because of this, the USBPHY control block needs to be reconfigured when returning from power-saving mode. Add suspend/resume support to handle runtime suspend/resume of the device, assert/deassert the reset signal, and reinitialize the USBPHY control block. Reviewed-by: Biju Das Reviewed-by: Philipp Zabel Signed-off-by: Claudiu Beznea Signed-off-by: Philipp Zabel --- drivers/reset/reset-rzg2l-usbphy-ctrl.c | 99 +++++++++++++++++++++---- 1 file changed, 84 insertions(+), 15 deletions(-) diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c index 9ce0c1f5d465..32bc268c9149 100644 --- a/drivers/reset/reset-rzg2l-usbphy-ctrl.c +++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c @@ -36,6 +36,7 @@ struct rzg2l_usbphy_ctrl_priv { struct reset_control *rstc; void __iomem *base; struct platform_device *vdev; + struct regmap_field *pwrrdy; spinlock_t lock; }; @@ -92,6 +93,19 @@ static int rzg2l_usbphy_ctrl_status(struct reset_controller_dev *rcdev, return !!(readl(priv->base + RESET) & port_mask); } +/* put pll and phy into reset state */ +static void rzg2l_usbphy_ctrl_init(struct rzg2l_usbphy_ctrl_priv *priv) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&priv->lock, flags); + val = readl(priv->base + RESET); + val |= RESET_SEL_PLLRESET | RESET_PLLRESET | PHY_RESET_PORT2 | PHY_RESET_PORT1; + writel(val, priv->base + RESET); + spin_unlock_irqrestore(&priv->lock, flags); +} + #define RZG2L_USBPHY_CTRL_PWRRDY 1 static const struct of_device_id rzg2l_usbphy_ctrl_match_table[] = { @@ -131,9 +145,9 @@ static void rzg2l_usbphy_ctrl_pwrrdy_off(void *data) rzg2l_usbphy_ctrl_set_pwrrdy(data, false); } -static int rzg2l_usbphy_ctrl_pwrrdy_init(struct device *dev) +static int rzg2l_usbphy_ctrl_pwrrdy_init(struct device *dev, + struct rzg2l_usbphy_ctrl_priv *priv) { - struct regmap_field *pwrrdy; struct reg_field field; struct regmap *regmap; const int *data; @@ -158,15 +172,15 @@ static int rzg2l_usbphy_ctrl_pwrrdy_init(struct device *dev) field.lsb = __ffs(args[1]); field.msb = __fls(args[1]); - pwrrdy = devm_regmap_field_alloc(dev, regmap, field); - if (IS_ERR(pwrrdy)) - return PTR_ERR(pwrrdy); + priv->pwrrdy = devm_regmap_field_alloc(dev, regmap, field); + if (IS_ERR(priv->pwrrdy)) + return PTR_ERR(priv->pwrrdy); ret = rzg2l_usbphy_ctrl_set_pwrrdy(priv->pwrrdy, true); if (ret) return ret; - return devm_add_action_or_reset(dev, rzg2l_usbphy_ctrl_pwrrdy_off, pwrrdy); + return devm_add_action_or_reset(dev, rzg2l_usbphy_ctrl_pwrrdy_off, priv->pwrrdy); } static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev) @@ -175,9 +189,7 @@ static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev) struct rzg2l_usbphy_ctrl_priv *priv; struct platform_device *vdev; struct regmap *regmap; - unsigned long flags; int error; - u32 val; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -191,7 +203,7 @@ static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev) if (IS_ERR(regmap)) return PTR_ERR(regmap); - error = rzg2l_usbphy_ctrl_pwrrdy_init(dev); + error = rzg2l_usbphy_ctrl_pwrrdy_init(dev, priv); if (error) return error; @@ -214,12 +226,7 @@ static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev) goto err_pm_disable_reset_deassert; } - /* put pll and phy into reset state */ - spin_lock_irqsave(&priv->lock, flags); - val = readl(priv->base + RESET); - val |= RESET_SEL_PLLRESET | RESET_PLLRESET | PHY_RESET_PORT2 | PHY_RESET_PORT1; - writel(val, priv->base + RESET); - spin_unlock_irqrestore(&priv->lock, flags); + rzg2l_usbphy_ctrl_init(priv); priv->rcdev.ops = &rzg2l_usbphy_ctrl_reset_ops; priv->rcdev.of_reset_n_cells = 1; @@ -266,10 +273,72 @@ static void rzg2l_usbphy_ctrl_remove(struct platform_device *pdev) reset_control_assert(priv->rstc); } +static int rzg2l_usbphy_ctrl_suspend(struct device *dev) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_drvdata(dev); + u32 val; + int ret; + + val = readl(priv->base + RESET); + if (!(val & PHY_RESET_PORT2) || !(val & PHY_RESET_PORT1)) + WARN(1, "Suspend with resets de-asserted\n"); + + pm_runtime_put_sync(dev); + + ret = reset_control_assert(priv->rstc); + if (ret) + goto rpm_resume; + + ret = rzg2l_usbphy_ctrl_set_pwrrdy(priv->pwrrdy, false); + if (ret) + goto reset_deassert; + + return 0; + +reset_deassert: + reset_control_deassert(priv->rstc); +rpm_resume: + pm_runtime_resume_and_get(dev); + return ret; +} + +static int rzg2l_usbphy_ctrl_resume(struct device *dev) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = rzg2l_usbphy_ctrl_set_pwrrdy(priv->pwrrdy, true); + if (ret) + return ret; + + ret = reset_control_deassert(priv->rstc); + if (ret) + goto pwrrdy_off; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + goto reset_assert; + + rzg2l_usbphy_ctrl_init(priv); + + return 0; + +reset_assert: + reset_control_assert(priv->rstc); +pwrrdy_off: + rzg2l_usbphy_ctrl_set_pwrrdy(priv->pwrrdy, false); + return ret; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(rzg2l_usbphy_ctrl_pm_ops, + rzg2l_usbphy_ctrl_suspend, + rzg2l_usbphy_ctrl_resume); + static struct platform_driver rzg2l_usbphy_ctrl_driver = { .driver = { .name = "rzg2l_usbphy_ctrl", .of_match_table = rzg2l_usbphy_ctrl_match_table, + .pm = pm_ptr(&rzg2l_usbphy_ctrl_pm_ops), }, .probe = rzg2l_usbphy_ctrl_probe, .remove = rzg2l_usbphy_ctrl_remove, From c7a5e01e229d21e0560d78bd645b4f7398667ce4 Mon Sep 17 00:00:00 2001 From: Junhui Liu Date: Sat, 17 Jan 2026 18:06:22 +0800 Subject: [PATCH 166/171] reset: canaan: k230: drop OF dependency and enable by default The driver doesn't use any symbols depending on CONFIG_OF, so drop the dependency. Also, enable it by default when ARCH_CANAAN is selected. Fixes: 360a7a647759 ("reset: canaan: add reset driver for Kendryte K230") Signed-off-by: Junhui Liu Signed-off-by: Philipp Zabel --- drivers/reset/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 6e5d6deffa7d..52ee102621ee 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -161,7 +161,7 @@ config RESET_K210 config RESET_K230 tristate "Reset controller driver for Canaan Kendryte K230 SoC" depends on ARCH_CANAAN || COMPILE_TEST - depends on OF + default ARCH_CANAAN help Support for the Canaan Kendryte K230 RISC-V SoC reset controller. Say Y if you want to control reset signals provided by this From 216e0a5e98e5f0f02a818884e8acf340892cecae Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Tue, 20 Jan 2026 19:10:49 +0800 Subject: [PATCH 167/171] dt-bindings: soc: spacemit: Add K3 reset support and IDs Update the spacemit,k1-syscon.yaml binding to document K3 SoC reset support. K3 reset devices are registered at runtime as auxiliary devices by the K3 CCU driver. Since K3 reuses the K1 syscon binding, there is no separate YAML binding file for K3 resets. Update #reset-cells description to document where reset IDs are defined. Acked-by: Alex Elder Acked-by: Krzysztof Kozlowski Signed-off-by: Guodong Xu Reviewed-by: Yixun Lan Link: https://lore.kernel.org/spacemit/20260114092742-GYC7933267@gentoo.org/ [1] Signed-off-by: Philipp Zabel --- .../soc/spacemit/spacemit,k1-syscon.yaml | 8 +- .../dt-bindings/reset/spacemit,k3-resets.h | 171 ++++++++++++++++++ 2 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 include/dt-bindings/reset/spacemit,k3-resets.h diff --git a/Documentation/devicetree/bindings/soc/spacemit/spacemit,k1-syscon.yaml b/Documentation/devicetree/bindings/soc/spacemit/spacemit,k1-syscon.yaml index 133a391ee68c..f0c6feb99be3 100644 --- a/Documentation/devicetree/bindings/soc/spacemit/spacemit,k1-syscon.yaml +++ b/Documentation/devicetree/bindings/soc/spacemit/spacemit,k1-syscon.yaml @@ -10,7 +10,7 @@ maintainers: - Haylen Chu description: - System controllers found on SpacemiT K1 SoC, which are capable of + System controllers found on SpacemiT K1/K3 SoC, which are capable of clock, reset and power-management functions. properties: @@ -46,6 +46,12 @@ properties: "#reset-cells": const: 1 + description: | + ID of the reset controller line. Valid IDs are defined in corresponding + files: + + For SpacemiT K1, see include/dt-bindings/clock/spacemit,k1-syscon.h + For SpacemiT K3, see include/dt-bindings/reset/spacemit,k3-resets.h required: - compatible diff --git a/include/dt-bindings/reset/spacemit,k3-resets.h b/include/dt-bindings/reset/spacemit,k3-resets.h new file mode 100644 index 000000000000..79ac1c22b7b5 --- /dev/null +++ b/include/dt-bindings/reset/spacemit,k3-resets.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2025 SpacemiT Technology Co. Ltd + */ + +#ifndef _DT_BINDINGS_RESET_SPACEMIT_K3_RESETS_H_ +#define _DT_BINDINGS_RESET_SPACEMIT_K3_RESETS_H_ + +/* MPMU resets */ +#define RESET_MPMU_WDT 0 +#define RESET_MPMU_RIPC 1 + +/* APBC resets */ +#define RESET_APBC_UART0 0 +#define RESET_APBC_UART2 1 +#define RESET_APBC_UART3 2 +#define RESET_APBC_UART4 3 +#define RESET_APBC_UART5 4 +#define RESET_APBC_UART6 5 +#define RESET_APBC_UART7 6 +#define RESET_APBC_UART8 7 +#define RESET_APBC_UART9 8 +#define RESET_APBC_UART10 9 +#define RESET_APBC_GPIO 10 +#define RESET_APBC_PWM0 11 +#define RESET_APBC_PWM1 12 +#define RESET_APBC_PWM2 13 +#define RESET_APBC_PWM3 14 +#define RESET_APBC_PWM4 15 +#define RESET_APBC_PWM5 16 +#define RESET_APBC_PWM6 17 +#define RESET_APBC_PWM7 18 +#define RESET_APBC_PWM8 19 +#define RESET_APBC_PWM9 20 +#define RESET_APBC_PWM10 21 +#define RESET_APBC_PWM11 22 +#define RESET_APBC_PWM12 23 +#define RESET_APBC_PWM13 24 +#define RESET_APBC_PWM14 25 +#define RESET_APBC_PWM15 26 +#define RESET_APBC_PWM16 27 +#define RESET_APBC_PWM17 28 +#define RESET_APBC_PWM18 29 +#define RESET_APBC_PWM19 30 +#define RESET_APBC_SPI0 31 +#define RESET_APBC_SPI1 32 +#define RESET_APBC_SPI3 33 +#define RESET_APBC_RTC 34 +#define RESET_APBC_TWSI0 35 +#define RESET_APBC_TWSI1 36 +#define RESET_APBC_TWSI2 37 +#define RESET_APBC_TWSI4 38 +#define RESET_APBC_TWSI5 39 +#define RESET_APBC_TWSI6 40 +#define RESET_APBC_TWSI8 41 +#define RESET_APBC_TIMERS0 42 +#define RESET_APBC_TIMERS1 43 +#define RESET_APBC_TIMERS2 44 +#define RESET_APBC_TIMERS3 45 +#define RESET_APBC_TIMERS4 46 +#define RESET_APBC_TIMERS5 47 +#define RESET_APBC_TIMERS6 48 +#define RESET_APBC_TIMERS7 49 +#define RESET_APBC_AIB 50 +#define RESET_APBC_ONEWIRE 51 +#define RESET_APBC_I2S0 52 +#define RESET_APBC_I2S1 53 +#define RESET_APBC_I2S2 54 +#define RESET_APBC_I2S3 55 +#define RESET_APBC_I2S4 56 +#define RESET_APBC_I2S5 57 +#define RESET_APBC_DRO 58 +#define RESET_APBC_IR0 59 +#define RESET_APBC_IR1 60 +#define RESET_APBC_TSEN 61 +#define RESET_IPC_AP2AUD 62 +#define RESET_APBC_CAN0 63 +#define RESET_APBC_CAN1 64 +#define RESET_APBC_CAN2 65 +#define RESET_APBC_CAN3 66 +#define RESET_APBC_CAN4 67 + +/* APMU resets */ +#define RESET_APMU_CSI 0 +#define RESET_APMU_CCIC2PHY 1 +#define RESET_APMU_CCIC3PHY 2 +#define RESET_APMU_ISP_CIBUS 3 +#define RESET_APMU_DSI_ESC 4 +#define RESET_APMU_LCD 5 +#define RESET_APMU_V2D 6 +#define RESET_APMU_LCD_MCLK 7 +#define RESET_APMU_LCD_DSCCLK 8 +#define RESET_APMU_SC2_HCLK 9 +#define RESET_APMU_CCIC_4X 10 +#define RESET_APMU_CCIC1_PHY 11 +#define RESET_APMU_SDH_AXI 12 +#define RESET_APMU_SDH0 13 +#define RESET_APMU_SDH1 14 +#define RESET_APMU_SDH2 15 +#define RESET_APMU_USB2 16 +#define RESET_APMU_USB3_PORTA 17 +#define RESET_APMU_USB3_PORTB 18 +#define RESET_APMU_USB3_PORTC 19 +#define RESET_APMU_USB3_PORTD 20 +#define RESET_APMU_QSPI 21 +#define RESET_APMU_QSPI_BUS 22 +#define RESET_APMU_DMA 23 +#define RESET_APMU_AES_WTM 24 +#define RESET_APMU_MCB_DCLK 25 +#define RESET_APMU_MCB_ACLK 26 +#define RESET_APMU_VPU 27 +#define RESET_APMU_DTC 28 +#define RESET_APMU_GPU 29 +#define RESET_APMU_ALZO 30 +#define RESET_APMU_MC 31 +#define RESET_APMU_CPU0_POP 32 +#define RESET_APMU_CPU0_SW 33 +#define RESET_APMU_CPU1_POP 34 +#define RESET_APMU_CPU1_SW 35 +#define RESET_APMU_CPU2_POP 36 +#define RESET_APMU_CPU2_SW 37 +#define RESET_APMU_CPU3_POP 38 +#define RESET_APMU_CPU3_SW 39 +#define RESET_APMU_C0_MPSUB_SW 40 +#define RESET_APMU_CPU4_POP 41 +#define RESET_APMU_CPU4_SW 42 +#define RESET_APMU_CPU5_POP 43 +#define RESET_APMU_CPU5_SW 44 +#define RESET_APMU_CPU6_POP 45 +#define RESET_APMU_CPU6_SW 46 +#define RESET_APMU_CPU7_POP 47 +#define RESET_APMU_CPU7_SW 48 +#define RESET_APMU_C1_MPSUB_SW 49 +#define RESET_APMU_MPSUB_DBG 50 +#define RESET_APMU_UCIE 51 +#define RESET_APMU_RCPU 52 +#define RESET_APMU_DSI4LN2_ESCCLK 53 +#define RESET_APMU_DSI4LN2_LCD_SW 54 +#define RESET_APMU_DSI4LN2_LCD_MCLK 55 +#define RESET_APMU_DSI4LN2_LCD_DSCCLK 56 +#define RESET_APMU_DSI4LN2_DPU_ACLK 57 +#define RESET_APMU_DPU_ACLK 58 +#define RESET_APMU_UFS_ACLK 59 +#define RESET_APMU_EDP0 60 +#define RESET_APMU_EDP1 61 +#define RESET_APMU_PCIE_PORTA 62 +#define RESET_APMU_PCIE_PORTB 63 +#define RESET_APMU_PCIE_PORTC 64 +#define RESET_APMU_PCIE_PORTD 65 +#define RESET_APMU_PCIE_PORTE 66 +#define RESET_APMU_EMAC0 67 +#define RESET_APMU_EMAC1 68 +#define RESET_APMU_EMAC2 69 +#define RESET_APMU_ESPI_MCLK 70 +#define RESET_APMU_ESPI_SCLK 71 + +/* DCIU resets*/ +#define RESET_DCIU_HDMA 0 +#define RESET_DCIU_DMA350 1 +#define RESET_DCIU_DMA350_0 2 +#define RESET_DCIU_DMA350_1 3 +#define RESET_DCIU_AXIDMA0 4 +#define RESET_DCIU_AXIDMA1 5 +#define RESET_DCIU_AXIDMA2 6 +#define RESET_DCIU_AXIDMA3 7 +#define RESET_DCIU_AXIDMA4 8 +#define RESET_DCIU_AXIDMA5 9 +#define RESET_DCIU_AXIDMA6 10 +#define RESET_DCIU_AXIDMA7 11 + +#endif /* _DT_BINDINGS_RESET_SPACEMIT_K3_H_ */ From 2875b4b5d2657ff2fd979103d88e9afcae51481c Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Tue, 20 Jan 2026 19:10:50 +0800 Subject: [PATCH 168/171] reset: Create subdirectory for SpacemiT drivers Create a dedicated subdirectory for SpacemiT reset drivers to allow for better organization as support for more SoCs is added. Move the existing K1 reset driver into this new directory and rename it to reset-spacemit-k1.c. Rename the Kconfig symbol to RESET_SPACEMIT_K1 and update its default from ARCH_SPACEMIT to SPACEMIT_K1_CCU. The reset driver depends on the clock driver to register reset devices as an auxiliary device, so the default should reflect this dependency. Also sort the drivers/reset/Kconfig entries alphabetically. Reviewed-by: Alex Elder Signed-off-by: Guodong Xu Reviewed-by: Yixun Lan Link: https://lore.kernel.org/spacemit/20260114092742-GYC7933267@gentoo.org/ [1] Signed-off-by: Philipp Zabel --- drivers/reset/Kconfig | 12 ++---------- drivers/reset/Makefile | 2 +- drivers/reset/spacemit/Kconfig | 14 ++++++++++++++ drivers/reset/spacemit/Makefile | 2 ++ .../reset-spacemit-k1.c} | 0 5 files changed, 19 insertions(+), 11 deletions(-) create mode 100644 drivers/reset/spacemit/Kconfig create mode 100644 drivers/reset/spacemit/Makefile rename drivers/reset/{reset-spacemit.c => spacemit/reset-spacemit-k1.c} (100%) diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 52ee102621ee..7ce151f6a7e4 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -299,15 +299,6 @@ config RESET_SOCFPGA This enables the reset driver for the SoCFPGA ARMv7 platforms. This driver gets initialized early during platform init calls. -config RESET_SPACEMIT - tristate "SpacemiT reset driver" - depends on ARCH_SPACEMIT || COMPILE_TEST - select AUXILIARY_BUS - default ARCH_SPACEMIT - help - This enables the reset controller driver for SpacemiT SoCs, - including the K1. - config RESET_SUNPLUS bool "Sunplus SoCs Reset Driver" if COMPILE_TEST default ARCH_SUNPLUS @@ -406,9 +397,10 @@ config RESET_ZYNQMP This enables the reset controller driver for Xilinx ZynqMP SoCs. source "drivers/reset/amlogic/Kconfig" +source "drivers/reset/hisilicon/Kconfig" +source "drivers/reset/spacemit/Kconfig" source "drivers/reset/starfive/Kconfig" source "drivers/reset/sti/Kconfig" -source "drivers/reset/hisilicon/Kconfig" source "drivers/reset/tegra/Kconfig" endif diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 9c3e484dfd81..fc0cc99f8514 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -2,6 +2,7 @@ obj-y += core.o obj-y += amlogic/ obj-y += hisilicon/ +obj-y += spacemit/ obj-y += starfive/ obj-y += sti/ obj-y += tegra/ @@ -38,7 +39,6 @@ obj-$(CONFIG_RESET_RZV2H_USB2PHY) += reset-rzv2h-usb2phy.o obj-$(CONFIG_RESET_SCMI) += reset-scmi.o obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o -obj-$(CONFIG_RESET_SPACEMIT) += reset-spacemit.o obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o obj-$(CONFIG_RESET_TH1520) += reset-th1520.o diff --git a/drivers/reset/spacemit/Kconfig b/drivers/reset/spacemit/Kconfig new file mode 100644 index 000000000000..552884e8b72a --- /dev/null +++ b/drivers/reset/spacemit/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config RESET_SPACEMIT_K1 + tristate "SpacemiT K1 reset driver" + depends on ARCH_SPACEMIT || COMPILE_TEST + depends on SPACEMIT_K1_CCU + select AUXILIARY_BUS + default SPACEMIT_K1_CCU + help + Support for reset controller in SpacemiT K1 SoC. + This driver works with the SpacemiT K1 clock controller + unit (CCU) driver to provide reset control functionality + for various peripherals and subsystems in the SoC. + diff --git a/drivers/reset/spacemit/Makefile b/drivers/reset/spacemit/Makefile new file mode 100644 index 000000000000..34e3350136bb --- /dev/null +++ b/drivers/reset/spacemit/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_RESET_SPACEMIT_K1) += reset-spacemit-k1.o diff --git a/drivers/reset/reset-spacemit.c b/drivers/reset/spacemit/reset-spacemit-k1.c similarity index 100% rename from drivers/reset/reset-spacemit.c rename to drivers/reset/spacemit/reset-spacemit-k1.c From aba86f7bff0bfd6956aff9bbbfb0c6ea6d56809e Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Tue, 20 Jan 2026 19:10:51 +0800 Subject: [PATCH 169/171] reset: spacemit: Extract common K1 reset code Extract the common reset controller code from the K1 driver into separate reset-spacemit-common.{c,h} files to prepare for additional SpacemiT SoCs that share the same reset controller architecture. The common code includes handlers for reset assert and deassert operations and probing for auxiliary bus devices. Changes during extraction: - Module ownership: Use dev->driver->owner instead of THIS_MODULE in spacemit_reset_controller_register() to correctly reference the calling driver's module. - Rename spacemit_reset_ids to spacemit_k1_reset_ids. - Define new namespace "RESET_SPACEMIT" for the exported common functions (spacemit_reset_probe) and update K1 driver to import it. This prepares for additional SpacemiT SoCs (K3) that share the same reset controller architecture. Reviewed-by: Alex Elder Signed-off-by: Guodong Xu Reviewed-by: Yixun Lan Link: https://lore.kernel.org/spacemit/20260114092742-GYC7933267@gentoo.org/ [1] Signed-off-by: Philipp Zabel --- drivers/reset/spacemit/Kconfig | 17 ++- drivers/reset/spacemit/Makefile | 2 + .../reset/spacemit/reset-spacemit-common.c | 77 +++++++++++++ .../reset/spacemit/reset-spacemit-common.h | 42 +++++++ drivers/reset/spacemit/reset-spacemit-k1.c | 107 ++---------------- 5 files changed, 144 insertions(+), 101 deletions(-) create mode 100644 drivers/reset/spacemit/reset-spacemit-common.c create mode 100644 drivers/reset/spacemit/reset-spacemit-common.h diff --git a/drivers/reset/spacemit/Kconfig b/drivers/reset/spacemit/Kconfig index 552884e8b72a..56a4858b30e1 100644 --- a/drivers/reset/spacemit/Kconfig +++ b/drivers/reset/spacemit/Kconfig @@ -1,10 +1,20 @@ # SPDX-License-Identifier: GPL-2.0-only -config RESET_SPACEMIT_K1 - tristate "SpacemiT K1 reset driver" +menu "Reset support for SpacemiT platforms" depends on ARCH_SPACEMIT || COMPILE_TEST - depends on SPACEMIT_K1_CCU + +config RESET_SPACEMIT_COMMON + tristate select AUXILIARY_BUS + help + Common reset controller infrastructure for SpacemiT SoCs. + This provides shared code and helper functions used by + reset drivers for various SpacemiT SoC families. + +config RESET_SPACEMIT_K1 + tristate "Support for SpacemiT K1 SoC" + depends on SPACEMIT_K1_CCU + select RESET_SPACEMIT_COMMON default SPACEMIT_K1_CCU help Support for reset controller in SpacemiT K1 SoC. @@ -12,3 +22,4 @@ config RESET_SPACEMIT_K1 unit (CCU) driver to provide reset control functionality for various peripherals and subsystems in the SoC. +endmenu diff --git a/drivers/reset/spacemit/Makefile b/drivers/reset/spacemit/Makefile index 34e3350136bb..0b056e8661ec 100644 --- a/drivers/reset/spacemit/Makefile +++ b/drivers/reset/spacemit/Makefile @@ -1,2 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_RESET_SPACEMIT_COMMON) += reset-spacemit-common.o + obj-$(CONFIG_RESET_SPACEMIT_K1) += reset-spacemit-k1.o diff --git a/drivers/reset/spacemit/reset-spacemit-common.c b/drivers/reset/spacemit/reset-spacemit-common.c new file mode 100644 index 000000000000..0626633a5e7d --- /dev/null +++ b/drivers/reset/spacemit/reset-spacemit-common.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* SpacemiT reset controller driver - common implementation */ + +#include +#include +#include + +#include + +#include "reset-spacemit-common.h" + +static int spacemit_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct ccu_reset_controller *controller; + const struct ccu_reset_data *data; + u32 mask; + u32 val; + + controller = container_of(rcdev, struct ccu_reset_controller, rcdev); + data = &controller->data->reset_data[id]; + mask = data->assert_mask | data->deassert_mask; + val = assert ? data->assert_mask : data->deassert_mask; + + return regmap_update_bits(controller->regmap, data->offset, mask, val); +} + +static int spacemit_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return spacemit_reset_update(rcdev, id, true); +} + +static int spacemit_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return spacemit_reset_update(rcdev, id, false); +} + +static const struct reset_control_ops spacemit_reset_control_ops = { + .assert = spacemit_reset_assert, + .deassert = spacemit_reset_deassert, +}; + +static int spacemit_reset_controller_register(struct device *dev, + struct ccu_reset_controller *controller) +{ + struct reset_controller_dev *rcdev = &controller->rcdev; + + rcdev->ops = &spacemit_reset_control_ops; + rcdev->owner = dev->driver->owner; + rcdev->of_node = dev->of_node; + rcdev->nr_resets = controller->data->count; + + return devm_reset_controller_register(dev, &controller->rcdev); +} + +int spacemit_reset_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct spacemit_ccu_adev *rdev = to_spacemit_ccu_adev(adev); + struct ccu_reset_controller *controller; + struct device *dev = &adev->dev; + + controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL); + if (!controller) + return -ENOMEM; + controller->data = (const struct ccu_reset_controller_data *)id->driver_data; + controller->regmap = rdev->regmap; + + return spacemit_reset_controller_register(dev, controller); +} +EXPORT_SYMBOL_NS_GPL(spacemit_reset_probe, "RESET_SPACEMIT"); + +MODULE_DESCRIPTION("SpacemiT reset controller driver - common code"); +MODULE_LICENSE("GPL"); diff --git a/drivers/reset/spacemit/reset-spacemit-common.h b/drivers/reset/spacemit/reset-spacemit-common.h new file mode 100644 index 000000000000..ffaf2f86eb39 --- /dev/null +++ b/drivers/reset/spacemit/reset-spacemit-common.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * SpacemiT reset controller driver - common definitions + */ + +#ifndef _RESET_SPACEMIT_COMMON_H_ +#define _RESET_SPACEMIT_COMMON_H_ + +#include +#include +#include +#include + +struct ccu_reset_data { + u32 offset; + u32 assert_mask; + u32 deassert_mask; +}; + +struct ccu_reset_controller_data { + const struct ccu_reset_data *reset_data; /* array */ + size_t count; +}; + +struct ccu_reset_controller { + struct reset_controller_dev rcdev; + const struct ccu_reset_controller_data *data; + struct regmap *regmap; +}; + +#define RESET_DATA(_offset, _assert_mask, _deassert_mask) \ + { \ + .offset = (_offset), \ + .assert_mask = (_assert_mask), \ + .deassert_mask = (_deassert_mask), \ + } + +/* Common probe function */ +int spacemit_reset_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id); + +#endif /* _RESET_SPACEMIT_COMMON_H_ */ diff --git a/drivers/reset/spacemit/reset-spacemit-k1.c b/drivers/reset/spacemit/reset-spacemit-k1.c index cc7fd1f8750d..8f3b5329ea5f 100644 --- a/drivers/reset/spacemit/reset-spacemit-k1.c +++ b/drivers/reset/spacemit/reset-spacemit-k1.c @@ -1,41 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-only -/* SpacemiT reset controller driver */ +/* SpacemiT K1 reset controller driver */ -#include -#include -#include #include -#include -#include -#include -#include #include +#include -struct ccu_reset_data { - u32 offset; - u32 assert_mask; - u32 deassert_mask; -}; - -struct ccu_reset_controller_data { - const struct ccu_reset_data *reset_data; /* array */ - size_t count; -}; - -struct ccu_reset_controller { - struct reset_controller_dev rcdev; - const struct ccu_reset_controller_data *data; - struct regmap *regmap; -}; - -#define RESET_DATA(_offset, _assert_mask, _deassert_mask) \ - { \ - .offset = (_offset), \ - .assert_mask = (_assert_mask), \ - .deassert_mask = (_deassert_mask), \ - } +#include "reset-spacemit-common.h" static const struct ccu_reset_data k1_mpmu_resets[] = { [RESET_WDT] = RESET_DATA(MPMU_WDTPCR, BIT(2), 0), @@ -214,91 +186,30 @@ static const struct ccu_reset_controller_data k1_apbc2_reset_data = { .count = ARRAY_SIZE(k1_apbc2_resets), }; -static int spacemit_reset_update(struct reset_controller_dev *rcdev, - unsigned long id, bool assert) -{ - struct ccu_reset_controller *controller; - const struct ccu_reset_data *data; - u32 mask; - u32 val; - - controller = container_of(rcdev, struct ccu_reset_controller, rcdev); - data = &controller->data->reset_data[id]; - mask = data->assert_mask | data->deassert_mask; - val = assert ? data->assert_mask : data->deassert_mask; - - return regmap_update_bits(controller->regmap, data->offset, mask, val); -} - -static int spacemit_reset_assert(struct reset_controller_dev *rcdev, - unsigned long id) -{ - return spacemit_reset_update(rcdev, id, true); -} - -static int spacemit_reset_deassert(struct reset_controller_dev *rcdev, - unsigned long id) -{ - return spacemit_reset_update(rcdev, id, false); -} - -static const struct reset_control_ops spacemit_reset_control_ops = { - .assert = spacemit_reset_assert, - .deassert = spacemit_reset_deassert, -}; - -static int spacemit_reset_controller_register(struct device *dev, - struct ccu_reset_controller *controller) -{ - struct reset_controller_dev *rcdev = &controller->rcdev; - - rcdev->ops = &spacemit_reset_control_ops; - rcdev->owner = THIS_MODULE; - rcdev->of_node = dev->of_node; - rcdev->nr_resets = controller->data->count; - - return devm_reset_controller_register(dev, &controller->rcdev); -} - -static int spacemit_reset_probe(struct auxiliary_device *adev, - const struct auxiliary_device_id *id) -{ - struct spacemit_ccu_adev *rdev = to_spacemit_ccu_adev(adev); - struct ccu_reset_controller *controller; - struct device *dev = &adev->dev; - - controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL); - if (!controller) - return -ENOMEM; - controller->data = (const struct ccu_reset_controller_data *)id->driver_data; - controller->regmap = rdev->regmap; - - return spacemit_reset_controller_register(dev, controller); -} - #define K1_AUX_DEV_ID(_unit) \ { \ .name = "spacemit_ccu.k1-" #_unit "-reset", \ .driver_data = (kernel_ulong_t)&k1_ ## _unit ## _reset_data, \ } -static const struct auxiliary_device_id spacemit_reset_ids[] = { +static const struct auxiliary_device_id spacemit_k1_reset_ids[] = { K1_AUX_DEV_ID(mpmu), K1_AUX_DEV_ID(apbc), K1_AUX_DEV_ID(apmu), K1_AUX_DEV_ID(rcpu), K1_AUX_DEV_ID(rcpu2), K1_AUX_DEV_ID(apbc2), - { }, + { /* sentinel */ } }; -MODULE_DEVICE_TABLE(auxiliary, spacemit_reset_ids); +MODULE_DEVICE_TABLE(auxiliary, spacemit_k1_reset_ids); static struct auxiliary_driver spacemit_k1_reset_driver = { .probe = spacemit_reset_probe, - .id_table = spacemit_reset_ids, + .id_table = spacemit_k1_reset_ids, }; module_auxiliary_driver(spacemit_k1_reset_driver); +MODULE_IMPORT_NS("RESET_SPACEMIT"); MODULE_AUTHOR("Alex Elder "); -MODULE_DESCRIPTION("SpacemiT reset controller driver"); +MODULE_DESCRIPTION("SpacemiT K1 reset controller driver"); MODULE_LICENSE("GPL"); From 938ce3b16582657e67f3bd8a7efa59089c467c90 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Tue, 20 Jan 2026 19:10:52 +0800 Subject: [PATCH 170/171] reset: spacemit: Add SpacemiT K3 reset driver Add support for the SpacemiT K3 SoC reset controller. The K3 reset driver reuses the common reset controller code and provides K3-specific reset data for devices managed by the following units: - MPMU (Main Power Management Unit) - APBC (APB clock unit) - APMU (Application Subsystem Power Management Unit) - DCIU (DMA Control and Interface Unit) Acked-by: Alex Elder Signed-off-by: Guodong Xu Reviewed-by: Yixun Lan Link: https://lore.kernel.org/spacemit/20260114092742-GYC7933267@gentoo.org/ [1] Signed-off-by: Philipp Zabel --- drivers/reset/spacemit/Kconfig | 11 + drivers/reset/spacemit/Makefile | 1 + drivers/reset/spacemit/reset-spacemit-k3.c | 233 +++++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 drivers/reset/spacemit/reset-spacemit-k3.c diff --git a/drivers/reset/spacemit/Kconfig b/drivers/reset/spacemit/Kconfig index 56a4858b30e1..545d6b41c6ca 100644 --- a/drivers/reset/spacemit/Kconfig +++ b/drivers/reset/spacemit/Kconfig @@ -22,4 +22,15 @@ config RESET_SPACEMIT_K1 unit (CCU) driver to provide reset control functionality for various peripherals and subsystems in the SoC. +config RESET_SPACEMIT_K3 + tristate "Support for SpacemiT K3 SoC" + depends on SPACEMIT_K3_CCU + select RESET_SPACEMIT_COMMON + default SPACEMIT_K3_CCU + help + Support for reset controller in SpacemiT K3 SoC. + This driver works with the SpacemiT K3 clock controller + unit (CCU) driver to provide reset control functionality + for various peripherals and subsystems in the SoC. + endmenu diff --git a/drivers/reset/spacemit/Makefile b/drivers/reset/spacemit/Makefile index 0b056e8661ec..00669132c6ac 100644 --- a/drivers/reset/spacemit/Makefile +++ b/drivers/reset/spacemit/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_RESET_SPACEMIT_COMMON) += reset-spacemit-common.o obj-$(CONFIG_RESET_SPACEMIT_K1) += reset-spacemit-k1.o +obj-$(CONFIG_RESET_SPACEMIT_K3) += reset-spacemit-k3.o diff --git a/drivers/reset/spacemit/reset-spacemit-k3.c b/drivers/reset/spacemit/reset-spacemit-k3.c new file mode 100644 index 000000000000..e9e32e4c1ba5 --- /dev/null +++ b/drivers/reset/spacemit/reset-spacemit-k3.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* SpacemiT K3 reset controller driver */ + +#include + +#include +#include + +#include "reset-spacemit-common.h" + +static const struct ccu_reset_data k3_mpmu_resets[] = { + [RESET_MPMU_WDT] = RESET_DATA(MPMU_WDTPCR, BIT(2), 0), + [RESET_MPMU_RIPC] = RESET_DATA(MPMU_RIPCCR, BIT(2), 0), +}; + +static const struct ccu_reset_controller_data k3_mpmu_reset_data = { + .reset_data = k3_mpmu_resets, + .count = ARRAY_SIZE(k3_mpmu_resets), +}; + +static const struct ccu_reset_data k3_apbc_resets[] = { + [RESET_APBC_UART0] = RESET_DATA(APBC_UART0_CLK_RST, BIT(2), 0), + [RESET_APBC_UART2] = RESET_DATA(APBC_UART2_CLK_RST, BIT(2), 0), + [RESET_APBC_UART3] = RESET_DATA(APBC_UART3_CLK_RST, BIT(2), 0), + [RESET_APBC_UART4] = RESET_DATA(APBC_UART4_CLK_RST, BIT(2), 0), + [RESET_APBC_UART5] = RESET_DATA(APBC_UART5_CLK_RST, BIT(2), 0), + [RESET_APBC_UART6] = RESET_DATA(APBC_UART6_CLK_RST, BIT(2), 0), + [RESET_APBC_UART7] = RESET_DATA(APBC_UART7_CLK_RST, BIT(2), 0), + [RESET_APBC_UART8] = RESET_DATA(APBC_UART8_CLK_RST, BIT(2), 0), + [RESET_APBC_UART9] = RESET_DATA(APBC_UART9_CLK_RST, BIT(2), 0), + [RESET_APBC_UART10] = RESET_DATA(APBC_UART10_CLK_RST, BIT(2), 0), + [RESET_APBC_GPIO] = RESET_DATA(APBC_GPIO_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM0] = RESET_DATA(APBC_PWM0_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM1] = RESET_DATA(APBC_PWM1_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM2] = RESET_DATA(APBC_PWM2_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM3] = RESET_DATA(APBC_PWM3_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM4] = RESET_DATA(APBC_PWM4_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM5] = RESET_DATA(APBC_PWM5_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM6] = RESET_DATA(APBC_PWM6_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM7] = RESET_DATA(APBC_PWM7_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM8] = RESET_DATA(APBC_PWM8_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM9] = RESET_DATA(APBC_PWM9_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM10] = RESET_DATA(APBC_PWM10_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM11] = RESET_DATA(APBC_PWM11_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM12] = RESET_DATA(APBC_PWM12_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM13] = RESET_DATA(APBC_PWM13_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM14] = RESET_DATA(APBC_PWM14_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM15] = RESET_DATA(APBC_PWM15_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM16] = RESET_DATA(APBC_PWM16_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM17] = RESET_DATA(APBC_PWM17_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM18] = RESET_DATA(APBC_PWM18_CLK_RST, BIT(2), 0), + [RESET_APBC_PWM19] = RESET_DATA(APBC_PWM19_CLK_RST, BIT(2), 0), + [RESET_APBC_SPI0] = RESET_DATA(APBC_SSP0_CLK_RST, BIT(2), 0), + [RESET_APBC_SPI1] = RESET_DATA(APBC_SSP1_CLK_RST, BIT(2), 0), + [RESET_APBC_SPI3] = RESET_DATA(APBC_SSP3_CLK_RST, BIT(2), 0), + [RESET_APBC_RTC] = RESET_DATA(APBC_RTC_CLK_RST, BIT(2), 0), + [RESET_APBC_TWSI0] = RESET_DATA(APBC_TWSI0_CLK_RST, BIT(2), 0), + [RESET_APBC_TWSI1] = RESET_DATA(APBC_TWSI1_CLK_RST, BIT(2), 0), + [RESET_APBC_TWSI2] = RESET_DATA(APBC_TWSI2_CLK_RST, BIT(2), 0), + [RESET_APBC_TWSI4] = RESET_DATA(APBC_TWSI4_CLK_RST, BIT(2), 0), + [RESET_APBC_TWSI5] = RESET_DATA(APBC_TWSI5_CLK_RST, BIT(2), 0), + [RESET_APBC_TWSI6] = RESET_DATA(APBC_TWSI6_CLK_RST, BIT(2), 0), + [RESET_APBC_TWSI8] = RESET_DATA(APBC_TWSI8_CLK_RST, BIT(2), 0), + [RESET_APBC_TIMERS0] = RESET_DATA(APBC_TIMERS0_CLK_RST, BIT(2), 0), + [RESET_APBC_TIMERS1] = RESET_DATA(APBC_TIMERS1_CLK_RST, BIT(2), 0), + [RESET_APBC_TIMERS2] = RESET_DATA(APBC_TIMERS2_CLK_RST, BIT(2), 0), + [RESET_APBC_TIMERS3] = RESET_DATA(APBC_TIMERS3_CLK_RST, BIT(2), 0), + [RESET_APBC_TIMERS4] = RESET_DATA(APBC_TIMERS4_CLK_RST, BIT(2), 0), + [RESET_APBC_TIMERS5] = RESET_DATA(APBC_TIMERS5_CLK_RST, BIT(2), 0), + [RESET_APBC_TIMERS6] = RESET_DATA(APBC_TIMERS6_CLK_RST, BIT(2), 0), + [RESET_APBC_TIMERS7] = RESET_DATA(APBC_TIMERS7_CLK_RST, BIT(2), 0), + [RESET_APBC_AIB] = RESET_DATA(APBC_AIB_CLK_RST, BIT(2), 0), + [RESET_APBC_ONEWIRE] = RESET_DATA(APBC_ONEWIRE_CLK_RST, BIT(2), 0), + [RESET_APBC_I2S0] = RESET_DATA(APBC_SSPA0_CLK_RST, BIT(2), 0), + [RESET_APBC_I2S1] = RESET_DATA(APBC_SSPA1_CLK_RST, BIT(2), 0), + [RESET_APBC_I2S2] = RESET_DATA(APBC_SSPA2_CLK_RST, BIT(2), 0), + [RESET_APBC_I2S3] = RESET_DATA(APBC_SSPA3_CLK_RST, BIT(2), 0), + [RESET_APBC_I2S4] = RESET_DATA(APBC_SSPA4_CLK_RST, BIT(2), 0), + [RESET_APBC_I2S5] = RESET_DATA(APBC_SSPA5_CLK_RST, BIT(2), 0), + [RESET_APBC_DRO] = RESET_DATA(APBC_DRO_CLK_RST, BIT(2), 0), + [RESET_APBC_IR0] = RESET_DATA(APBC_IR0_CLK_RST, BIT(2), 0), + [RESET_APBC_IR1] = RESET_DATA(APBC_IR1_CLK_RST, BIT(2), 0), + [RESET_APBC_TSEN] = RESET_DATA(APBC_TSEN_CLK_RST, BIT(2), 0), + [RESET_IPC_AP2AUD] = RESET_DATA(APBC_IPC_AP2AUD_CLK_RST, BIT(2), 0), + [RESET_APBC_CAN0] = RESET_DATA(APBC_CAN0_CLK_RST, BIT(2), 0), + [RESET_APBC_CAN1] = RESET_DATA(APBC_CAN1_CLK_RST, BIT(2), 0), + [RESET_APBC_CAN2] = RESET_DATA(APBC_CAN2_CLK_RST, BIT(2), 0), + [RESET_APBC_CAN3] = RESET_DATA(APBC_CAN3_CLK_RST, BIT(2), 0), + [RESET_APBC_CAN4] = RESET_DATA(APBC_CAN4_CLK_RST, BIT(2), 0), +}; + +static const struct ccu_reset_controller_data k3_apbc_reset_data = { + .reset_data = k3_apbc_resets, + .count = ARRAY_SIZE(k3_apbc_resets), +}; + +static const struct ccu_reset_data k3_apmu_resets[] = { + [RESET_APMU_CSI] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(1)), + [RESET_APMU_CCIC2PHY] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(2)), + [RESET_APMU_CCIC3PHY] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(29)), + [RESET_APMU_ISP_CIBUS] = RESET_DATA(APMU_ISP_CLK_RES_CTRL, 0, BIT(16)), + [RESET_APMU_DSI_ESC] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(3)), + [RESET_APMU_LCD] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(4)), + [RESET_APMU_V2D] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(27)), + [RESET_APMU_LCD_MCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL2, 0, BIT(9)), + [RESET_APMU_LCD_DSCCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL2, 0, BIT(15)), + [RESET_APMU_SC2_HCLK] = RESET_DATA(APMU_CCIC_CLK_RES_CTRL, 0, BIT(0)), + [RESET_APMU_CCIC_4X] = RESET_DATA(APMU_CCIC_CLK_RES_CTRL, 0, BIT(1)), + [RESET_APMU_CCIC1_PHY] = RESET_DATA(APMU_CCIC_CLK_RES_CTRL, 0, BIT(2)), + [RESET_APMU_SDH_AXI] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(0)), + [RESET_APMU_SDH0] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(1)), + [RESET_APMU_SDH1] = RESET_DATA(APMU_SDH1_CLK_RES_CTRL, 0, BIT(1)), + [RESET_APMU_SDH2] = RESET_DATA(APMU_SDH2_CLK_RES_CTRL, 0, BIT(1)), + [RESET_APMU_USB2] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, + BIT(1)|BIT(2)|BIT(3)), + [RESET_APMU_USB3_PORTA] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, + BIT(5)|BIT(6)|BIT(7)), + [RESET_APMU_USB3_PORTB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, + BIT(9)|BIT(10)|BIT(11)), + [RESET_APMU_USB3_PORTC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, + BIT(13)|BIT(14)|BIT(15)), + [RESET_APMU_USB3_PORTD] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, + BIT(17)|BIT(18)|BIT(19)), + [RESET_APMU_QSPI] = RESET_DATA(APMU_QSPI_CLK_RES_CTRL, 0, BIT(1)), + [RESET_APMU_QSPI_BUS] = RESET_DATA(APMU_QSPI_CLK_RES_CTRL, 0, BIT(0)), + [RESET_APMU_DMA] = RESET_DATA(APMU_DMA_CLK_RES_CTRL, 0, BIT(0)), + [RESET_APMU_AES_WTM] = RESET_DATA(APMU_AES_CLK_RES_CTRL, 0, BIT(4)), + [RESET_APMU_MCB_DCLK] = RESET_DATA(APMU_MCB_CLK_RES_CTRL, 0, BIT(0)), + [RESET_APMU_MCB_ACLK] = RESET_DATA(APMU_MCB_CLK_RES_CTRL, 0, BIT(1)), + [RESET_APMU_VPU] = RESET_DATA(APMU_VPU_CLK_RES_CTRL, 0, BIT(0)), + [RESET_APMU_DTC] = RESET_DATA(APMU_DTC_CLK_RES_CTRL, 0, BIT(0)), + [RESET_APMU_GPU] = RESET_DATA(APMU_GPU_CLK_RES_CTRL, 0, BIT(1)), + [RESET_APMU_MC] = RESET_DATA(APMU_PMUA_MC_CTRL, 0, BIT(0)), + [RESET_APMU_CPU0_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(0), 0), + [RESET_APMU_CPU0_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(1), 0), + [RESET_APMU_CPU1_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(3), 0), + [RESET_APMU_CPU1_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(4), 0), + [RESET_APMU_CPU2_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(6), 0), + [RESET_APMU_CPU2_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(7), 0), + [RESET_APMU_CPU3_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(9), 0), + [RESET_APMU_CPU3_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(10), 0), + [RESET_APMU_C0_MPSUB_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(12), 0), + [RESET_APMU_CPU4_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(16), 0), + [RESET_APMU_CPU4_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(17), 0), + [RESET_APMU_CPU5_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(19), 0), + [RESET_APMU_CPU5_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(20), 0), + [RESET_APMU_CPU6_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(22), 0), + [RESET_APMU_CPU6_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(23), 0), + [RESET_APMU_CPU7_POP] = RESET_DATA(APMU_PMU_CC2_AP, BIT(25), 0), + [RESET_APMU_CPU7_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(26), 0), + [RESET_APMU_C1_MPSUB_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(28), 0), + [RESET_APMU_MPSUB_DBG] = RESET_DATA(APMU_PMU_CC2_AP, BIT(29), 0), + [RESET_APMU_UCIE] = RESET_DATA(APMU_UCIE_CTRL, + BIT(1) | BIT(2) | BIT(3), 0), + [RESET_APMU_RCPU] = RESET_DATA(APMU_RCPU_CLK_RES_CTRL, 0, + BIT(3) | BIT(2) | BIT(0)), + [RESET_APMU_DSI4LN2_ESCCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL3, 0, BIT(3)), + [RESET_APMU_DSI4LN2_LCD_SW] = RESET_DATA(APMU_LCD_CLK_RES_CTRL3, 0, BIT(4)), + [RESET_APMU_DSI4LN2_LCD_MCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL4, 0, BIT(9)), + [RESET_APMU_DSI4LN2_LCD_DSCCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL4, 0, BIT(15)), + [RESET_APMU_DSI4LN2_DPU_ACLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL5, 0, BIT(0)), + [RESET_APMU_DPU_ACLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL5, 0, BIT(15)), + [RESET_APMU_UFS_ACLK] = RESET_DATA(APMU_UFS_CLK_RES_CTRL, 0, BIT(0)), + [RESET_APMU_EDP0] = RESET_DATA(APMU_LCD_EDP_CTRL, 0, BIT(0)), + [RESET_APMU_EDP1] = RESET_DATA(APMU_LCD_EDP_CTRL, 0, BIT(16)), + [RESET_APMU_PCIE_PORTA] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_A, 0, + BIT(5) | BIT(4) | BIT(3)), + [RESET_APMU_PCIE_PORTB] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_B, 0, + BIT(5) | BIT(4) | BIT(3)), + [RESET_APMU_PCIE_PORTC] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_C, 0, + BIT(5) | BIT(4) | BIT(3)), + [RESET_APMU_PCIE_PORTD] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_D, 0, + BIT(5) | BIT(4) | BIT(3)), + [RESET_APMU_PCIE_PORTE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_E, 0, + BIT(5) | BIT(4) | BIT(3)), + [RESET_APMU_EMAC0] = RESET_DATA(APMU_EMAC0_CLK_RES_CTRL, 0, BIT(1)), + [RESET_APMU_EMAC1] = RESET_DATA(APMU_EMAC1_CLK_RES_CTRL, 0, BIT(1)), + [RESET_APMU_EMAC2] = RESET_DATA(APMU_EMAC2_CLK_RES_CTRL, 0, BIT(1)), + [RESET_APMU_ESPI_MCLK] = RESET_DATA(APMU_ESPI_CLK_RES_CTRL, 0, BIT(0)), + [RESET_APMU_ESPI_SCLK] = RESET_DATA(APMU_ESPI_CLK_RES_CTRL, 0, BIT(2)), +}; + +static const struct ccu_reset_controller_data k3_apmu_reset_data = { + .reset_data = k3_apmu_resets, + .count = ARRAY_SIZE(k3_apmu_resets), +}; + +static const struct ccu_reset_data k3_dciu_resets[] = { + [RESET_DCIU_HDMA] = RESET_DATA(DCIU_DMASYS_RSTN, 0, BIT(0)), + [RESET_DCIU_DMA350] = RESET_DATA(DCIU_DMASYS_SDMA_RSTN, 0, BIT(0)), + [RESET_DCIU_DMA350_0] = RESET_DATA(DCIU_DMASYS_S0_RSTN, 0, BIT(0)), + [RESET_DCIU_DMA350_1] = RESET_DATA(DCIU_DMASYS_S1_RSTN, 0, BIT(0)), + [RESET_DCIU_AXIDMA0] = RESET_DATA(DCIU_DMASYS_A0_RSTN, 0, BIT(0)), + [RESET_DCIU_AXIDMA1] = RESET_DATA(DCIU_DMASYS_A1_RSTN, 0, BIT(0)), + [RESET_DCIU_AXIDMA2] = RESET_DATA(DCIU_DMASYS_A2_RSTN, 0, BIT(0)), + [RESET_DCIU_AXIDMA3] = RESET_DATA(DCIU_DMASYS_A3_RSTN, 0, BIT(0)), + [RESET_DCIU_AXIDMA4] = RESET_DATA(DCIU_DMASYS_A4_RSTN, 0, BIT(0)), + [RESET_DCIU_AXIDMA5] = RESET_DATA(DCIU_DMASYS_A5_RSTN, 0, BIT(0)), + [RESET_DCIU_AXIDMA6] = RESET_DATA(DCIU_DMASYS_A6_RSTN, 0, BIT(0)), + [RESET_DCIU_AXIDMA7] = RESET_DATA(DCIU_DMASYS_A7_RSTN, 0, BIT(0)), +}; + +static const struct ccu_reset_controller_data k3_dciu_reset_data = { + .reset_data = k3_dciu_resets, + .count = ARRAY_SIZE(k3_dciu_resets), +}; + +#define K3_AUX_DEV_ID(_unit) \ + { \ + .name = "spacemit_ccu.k3-" #_unit "-reset", \ + .driver_data = (kernel_ulong_t)&k3_ ## _unit ## _reset_data, \ + } + +static const struct auxiliary_device_id spacemit_k3_reset_ids[] = { + K3_AUX_DEV_ID(mpmu), + K3_AUX_DEV_ID(apbc), + K3_AUX_DEV_ID(apmu), + K3_AUX_DEV_ID(dciu), + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(auxiliary, spacemit_k3_reset_ids); + +static struct auxiliary_driver spacemit_k3_reset_driver = { + .probe = spacemit_reset_probe, + .id_table = spacemit_k3_reset_ids, +}; +module_auxiliary_driver(spacemit_k3_reset_driver); + +MODULE_IMPORT_NS("RESET_SPACEMIT"); +MODULE_AUTHOR("Guodong Xu "); +MODULE_DESCRIPTION("SpacemiT K3 reset controller driver"); +MODULE_LICENSE("GPL"); From 52f527d0916bcdd7621a0c9e7e599b133294d495 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Sat, 24 Jan 2026 18:20:54 +0800 Subject: [PATCH 171/171] bus: fsl-mc: fix an error handling in fsl_mc_device_add() In fsl_mc_device_add(), device_initialize() is called first. put_device() should be called to drop the reference if error occurs. And other resources would be released via put_device -> fsl_mc_device_release. So remove redundant kfree() in error handling path. Fixes: bbf9d17d9875 ("staging: fsl-mc: Freescale Management Complex (fsl-mc) bus driver") Cc: stable@vger.kernel.org Reported-by: Dan Carpenter Closes: https://lore.kernel.org/all/b767348e-d89c-416e-acea-1ebbff3bea20@stanley.mountain/ Signed-off-by: Su Hui Suggested-by: Christophe Leroy (CS GROUP) Signed-off-by: Haoxiang Li Reviewed-by: Ioana Ciornei Link: https://lore.kernel.org/r/20260124102054.1613093-1-lihaoxiang@isrc.iscas.ac.cn Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/bus/fsl-mc/fsl-mc-bus.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c index 08b99b0b342f..007223549887 100644 --- a/drivers/bus/fsl-mc/fsl-mc-bus.c +++ b/drivers/bus/fsl-mc/fsl-mc-bus.c @@ -896,11 +896,7 @@ int fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc, return 0; error_cleanup_dev: - kfree(mc_dev->regions); - if (mc_bus) - kfree(mc_bus); - else - kfree(mc_dev); + put_device(&mc_dev->dev); return error; }