diff options
| author | Arnd Bergmann <arnd@arndb.de> | 2026-07-24 18:30:38 +0200 |
|---|---|---|
| committer | Arnd Bergmann <arnd@arndb.de> | 2026-07-24 18:30:38 +0200 |
| commit | e09d030c92a47582006c5147f9874a902f4e32cd (patch) | |
| tree | 3885e9e86dd2b5ba73bda5ef33c2b23c4e6ee682 | |
| parent | e58aaa10f8d1fc5fdcb77f5eae7043e200b4fcf0 (diff) | |
| parent | 6bbf8038f8cae72f08007604b8c863492970c2f6 (diff) | |
| download | linux-next-e09d030c92a47582006c5147f9874a902f4e32cd.tar.gz linux-next-e09d030c92a47582006c5147f9874a902f4e32cd.zip | |
Merge branch 'soc/drivers' into for-next
* soc/drivers: (26 commits)
soc: ixp4xx: Remove redundant dev_err()
soc: ixp4xx: npe: add missing MODULE_DEVICE_TABLE()
soc: ixp4xx: qmgr: add missing MODULE_DEVICE_TABLE()
firmware: arm_scmi: Unrequest devices if driver registration fails
firmware: arm_scmi: Roll back partial protocol table registration
firmware: arm_scmi: Fix requested device removal race
soc: renesas: r8a78000: Drop duplicate "default ARCH_RENESAS"
firmware: arm_scmi: Fix transport device teardown lookup
firmware: arm_scmi: Fix SCMI device destroy lifetimes
firmware: arm_scmi: Unwind P2A receiver mailbox setup failure
firmware: arm_scmi: Unwind TX receiver mailbox setup failure
firmware: arm_scmi: Fix OF node reference handling
firmware: arm_scmi: Clear SystemPower flag on create failure
firmware: arm_scmi: Drop handle on protocol bind failures
firmware: arm_scmi: Protect device request lookup with RCU
firmware: arm_scmi: Use channel ID for transport teardown
firmware: arm_scmi: Reject out of range DT protocol IDs
firmware: arm_scmi: Avoid IDR updates while cleaning channels
firmware: arm_scmi: Free transport channel on IDR failure
firmware: arm_scmi: Clean up channels on setup failure
...
| -rw-r--r-- | Documentation/devicetree/bindings/arm/rockchip/pmu.yaml | 8 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/soc/rockchip/grf.yaml | 7 | ||||
| -rw-r--r-- | drivers/firmware/arm_scmi/bus.c | 182 | ||||
| -rw-r--r-- | drivers/firmware/arm_scmi/common.h | 2 | ||||
| -rw-r--r-- | drivers/firmware/arm_scmi/driver.c | 81 | ||||
| -rw-r--r-- | drivers/firmware/arm_scmi/notify.c | 64 | ||||
| -rw-r--r-- | drivers/firmware/arm_scmi/notify.h | 1 | ||||
| -rw-r--r-- | drivers/firmware/arm_scmi/transports/mailbox.c | 26 | ||||
| -rw-r--r-- | drivers/firmware/arm_scmi/transports/smc.c | 15 | ||||
| -rw-r--r-- | drivers/soc/ixp4xx/ixp4xx-npe.c | 1 | ||||
| -rw-r--r-- | drivers/soc/ixp4xx/ixp4xx-qmgr.c | 11 | ||||
| -rw-r--r-- | drivers/soc/renesas/Kconfig | 1 |
12 files changed, 246 insertions, 153 deletions
diff --git a/Documentation/devicetree/bindings/arm/rockchip/pmu.yaml b/Documentation/devicetree/bindings/arm/rockchip/pmu.yaml index 55b2200d6e75..1cff2328cd01 100644 --- a/Documentation/devicetree/bindings/arm/rockchip/pmu.yaml +++ b/Documentation/devicetree/bindings/arm/rockchip/pmu.yaml @@ -61,7 +61,13 @@ properties: type: object reboot-mode: - type: object + $ref: /schemas/power/reset/syscon-reboot-mode.yaml + unevaluatedProperties: false + + patternProperties: + # Negative look-ahead to disallow unsupported modes. The '$' has to be + # part of lookahead group to work, instead of trailing outside of (). + "^mode-(?!(bootloader$|loader$|normal$|recovery$))": false required: - compatible diff --git a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml index 2cc43742b8e3..7bcb4e2f47ec 100644 --- a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml +++ b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml @@ -239,11 +239,14 @@ allOf: properties: reboot-mode: type: object - $ref: /schemas/power/reset/syscon-reboot-mode.yaml# - unevaluatedProperties: false + patternProperties: + # Negative look-ahead to disallow unsupported modes. The '$' has to be + # part of lookahead group to work, instead of trailing outside of (). + "^mode-(?!(bootloader$|fastboot$|loader$|normal$|recovery$))": false + - if: properties: compatible: diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index 793be9eabaed..e060edbe7e83 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -7,7 +7,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/atomic.h> #include <linux/types.h> #include <linux/module.h> #include <linux/of.h> @@ -33,8 +32,8 @@ struct scmi_requested_dev { struct list_head node; }; -/* Track globally the creation of SCMI SystemPower related devices */ -static atomic_t scmi_syspower_registered = ATOMIC_INIT(0); +/* Track globally the SCMI SystemPower protocol device. */ +static struct scmi_device *scmi_syspower_registered; /** * scmi_protocol_device_request - Helper to request a device @@ -136,17 +135,6 @@ out: return ret; } -static int scmi_protocol_table_register(const struct scmi_device_id *id_table) -{ - int ret = 0; - const struct scmi_device_id *entry; - - for (entry = id_table; entry->name && ret == 0; entry++) - ret = scmi_protocol_device_request(entry); - - return ret; -} - /** * scmi_protocol_device_unrequest - Helper to unrequest a device * @@ -159,6 +147,7 @@ static int scmi_protocol_table_register(const struct scmi_device_id *id_table) */ static void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table) { + struct scmi_requested_dev *rdev, *victim = NULL; struct list_head *phead; pr_debug("Unrequesting SCMI device (%s) for protocol %x\n", @@ -167,29 +156,48 @@ static void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table mutex_lock(&scmi_requested_devices_mtx); phead = idr_find(&scmi_requested_devices, id_table->protocol_id); if (phead) { - struct scmi_requested_dev *victim, *tmp; - - list_for_each_entry_safe(victim, tmp, phead, node) { - if (!strcmp(victim->id_table->name, id_table->name)) { - list_del(&victim->node); - - mutex_unlock(&scmi_requested_devices_mtx); - blocking_notifier_call_chain(&scmi_requested_devices_nh, - SCMI_BUS_NOTIFY_DEVICE_UNREQUEST, - (void *)victim->id_table); - kfree(victim); - mutex_lock(&scmi_requested_devices_mtx); + list_for_each_entry(rdev, phead, node) { + if (!strcmp(rdev->id_table->name, id_table->name)) { + victim = rdev; + list_del(&rdev->node); break; } } - if (list_empty(phead)) { + if (victim && list_empty(phead)) { idr_remove(&scmi_requested_devices, id_table->protocol_id); kfree(phead); } } mutex_unlock(&scmi_requested_devices_mtx); + + if (victim) { + blocking_notifier_call_chain(&scmi_requested_devices_nh, + SCMI_BUS_NOTIFY_DEVICE_UNREQUEST, + (void *)victim->id_table); + kfree(victim); + } +} + +static int scmi_protocol_table_register(const struct scmi_device_id *id_table) +{ + const struct scmi_device_id *entry; + int ret; + + for (entry = id_table; entry->name; entry++) { + ret = scmi_protocol_device_request(entry); + if (ret) + goto err_unrequest; + } + + return 0; + +err_unrequest: + while (entry != id_table) + scmi_protocol_device_unrequest(--entry); + + return ret; } static void @@ -201,21 +209,33 @@ scmi_protocol_table_unregister(const struct scmi_device_id *id_table) scmi_protocol_device_unrequest(entry); } -static int scmi_dev_match_by_id_table(struct scmi_device *scmi_dev, - const struct scmi_device_id *id_table) +static bool scmi_device_is_transport(const struct scmi_device *scmi_dev) +{ + return !strncmp(scmi_dev->name, SCMI_TRANSPORT_DEVNAME_PREFIX, + strlen(SCMI_TRANSPORT_DEVNAME_PREFIX)); +} + +static int __scmi_dev_match_by_id_table(struct scmi_device *scmi_dev, + const struct scmi_device_id *id_table, + bool skip_transport) { if (!id_table || !id_table->name) return 0; - /* Always skip transport devices from matching */ for (; id_table->protocol_id && id_table->name; id_table++) if (id_table->protocol_id == scmi_dev->protocol_id && - strncmp(scmi_dev->name, "__scmi_transport_device", 23) && + !(skip_transport && scmi_device_is_transport(scmi_dev)) && !strcmp(id_table->name, scmi_dev->name)) return 1; return 0; } +static int scmi_dev_match_by_id_table(struct scmi_device *scmi_dev, + const struct scmi_device_id *id_table) +{ + return __scmi_dev_match_by_id_table(scmi_dev, id_table, true); +} + static int scmi_dev_match_id(struct scmi_device *scmi_dev, const struct scmi_driver *scmi_drv) { @@ -235,11 +255,12 @@ static int scmi_match_by_id_table(struct device *dev, const void *data) struct scmi_device *scmi_dev = to_scmi_dev(dev); const struct scmi_device_id *id_table = data; - return scmi_dev_match_by_id_table(scmi_dev, id_table); + return __scmi_dev_match_by_id_table(scmi_dev, id_table, false); } -static struct scmi_device *scmi_child_dev_find(struct device *parent, - int prot_id, const char *name) +/* Returns a device_find_child() reference which must be dropped by caller. */ +static struct scmi_device * +scmi_child_dev_find_get(struct device *parent, int prot_id, const char *name) { struct scmi_device_id id_table[2] = { 0 }; struct device *dev; @@ -251,9 +272,6 @@ static struct scmi_device *scmi_child_dev_find(struct device *parent, if (!dev) return NULL; - /* Drop the refcnt bumped implicitly by device_find_child */ - put_device(dev); - return to_scmi_dev(dev); } @@ -377,10 +395,14 @@ int scmi_driver_register(struct scmi_driver *driver, struct module *owner, driver->driver.mod_name = mod_name; retval = driver_register(&driver->driver); - if (!retval) - pr_debug("Registered new scmi driver %s\n", driver->name); + if (retval) { + scmi_protocol_table_unregister(driver->id_table); + return retval; + } - return retval; + pr_debug("Registered new scmi driver %s\n", driver->name); + + return 0; } EXPORT_SYMBOL_GPL(scmi_driver_register); @@ -391,10 +413,23 @@ void scmi_driver_unregister(struct scmi_driver *driver) } EXPORT_SYMBOL_GPL(scmi_driver_unregister); +static void scmi_device_release_resources(struct scmi_device *scmi_dev) +{ + if (scmi_dev->protocol_id == SCMI_PROTOCOL_SYSTEM) + cmpxchg(&scmi_syspower_registered, scmi_dev, NULL); + + if (scmi_dev->id) { + ida_free(&scmi_bus_id, scmi_dev->id); + scmi_dev->id = 0; + } +} + static void scmi_device_release(struct device *dev) { struct scmi_device *scmi_dev = to_scmi_dev(dev); + scmi_device_release_resources(scmi_dev); + of_node_put(dev->of_node); kfree_const(scmi_dev->name); kfree(scmi_dev); } @@ -406,11 +441,9 @@ static void __scmi_device_destroy(struct scmi_device *scmi_dev) dev_name(&scmi_dev->dev), scmi_dev->protocol_id, scmi_dev->name); - if (scmi_dev->protocol_id == SCMI_PROTOCOL_SYSTEM) - atomic_set(&scmi_syspower_registered, 0); - - ida_free(&scmi_bus_id, scmi_dev->id); - device_unregister(&scmi_dev->dev); + device_del(&scmi_dev->dev); + scmi_device_release_resources(scmi_dev); + put_device(&scmi_dev->dev); } static struct scmi_device * @@ -419,6 +452,7 @@ __scmi_device_create(struct device_node *np, struct device *parent, { int id, retval; struct scmi_device *scmi_dev; + bool syspower = (protocol == SCMI_PROTOCOL_SYSTEM); /* * If the same protocol/name device already exist under the same parent @@ -427,45 +461,41 @@ __scmi_device_create(struct device_node *np, struct device *parent, * each DT defined protocol at probe time, and the concurrent * registration of SCMI drivers. */ - scmi_dev = scmi_child_dev_find(parent, protocol, name); - if (scmi_dev) + scmi_dev = scmi_child_dev_find_get(parent, protocol, name); + if (scmi_dev) { + put_device(&scmi_dev->dev); return scmi_dev; - - /* - * Ignore any possible subsequent failures while creating the device - * since we are doomed anyway at that point; not using a mutex which - * spans across this whole function to keep things simple and to avoid - * to serialize all the __scmi_device_create calls across possibly - * different SCMI server instances (parent) - */ - if (protocol == SCMI_PROTOCOL_SYSTEM && - atomic_cmpxchg(&scmi_syspower_registered, 0, 1)) { - dev_warn(parent, - "SCMI SystemPower protocol device must be unique !\n"); - return NULL; } scmi_dev = kzalloc_obj(*scmi_dev); if (!scmi_dev) return NULL; - scmi_dev->name = kstrdup_const(name ?: "unknown", GFP_KERNEL); - if (!scmi_dev->name) { + scmi_dev->protocol_id = protocol; + + /* + * Reserve the singleton SystemPower protocol device using the device + * pointer itself, so delayed release of an older device cannot clear + * a reservation owned by a newer device. + */ + if (syspower && cmpxchg(&scmi_syspower_registered, NULL, scmi_dev)) { + dev_warn(parent, + "SCMI SystemPower protocol device must be unique !\n"); kfree(scmi_dev); return NULL; } + scmi_dev->name = kstrdup_const(name ?: "unknown", GFP_KERNEL); + if (!scmi_dev->name) + goto free_dev; + id = ida_alloc_min(&scmi_bus_id, 1, GFP_KERNEL); - if (id < 0) { - kfree_const(scmi_dev->name); - kfree(scmi_dev); - return NULL; - } + if (id < 0) + goto free_name; scmi_dev->id = id; - scmi_dev->protocol_id = protocol; scmi_dev->dev.parent = parent; - device_set_node(&scmi_dev->dev, of_fwnode_handle(np)); + device_set_node(&scmi_dev->dev, of_fwnode_handle(of_node_get(np))); scmi_dev->dev.bus = &scmi_bus_type; scmi_dev->dev.release = scmi_device_release; dev_set_name(&scmi_dev->dev, "scmi_dev.%d", id); @@ -479,8 +509,14 @@ __scmi_device_create(struct device_node *np, struct device *parent, return scmi_dev; put_dev: + scmi_device_release_resources(scmi_dev); put_device(&scmi_dev->dev); - ida_free(&scmi_bus_id, id); + return NULL; +free_name: + kfree_const(scmi_dev->name); +free_dev: + scmi_device_release_resources(scmi_dev); + kfree(scmi_dev); return NULL; } @@ -561,9 +597,11 @@ void scmi_device_destroy(struct device *parent, int protocol, const char *name) { struct scmi_device *scmi_dev; - scmi_dev = scmi_child_dev_find(parent, protocol, name); - if (scmi_dev) + scmi_dev = scmi_child_dev_find_get(parent, protocol, name); + if (scmi_dev) { __scmi_device_destroy(scmi_dev); + put_device(&scmi_dev->dev); + } } EXPORT_SYMBOL_GPL(scmi_device_destroy); diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index b9723c105fc1..fe8c22cfb9f7 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -34,6 +34,8 @@ #define SCMI_SHMEM_MAX_PAYLOAD_SIZE 104 +#define SCMI_TRANSPORT_DEVNAME_PREFIX "__scmi_transport_device" + enum scmi_error_codes { SCMI_SUCCESS = 0, /* Success */ SCMI_ERR_SUPPORT = -1, /* Not supported */ diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 3e0d975ec94c..ef29fd223287 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -33,6 +33,7 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/processor.h> +#include <linux/rcupdate.h> #include <linux/refcount.h> #include <linux/slab.h> #include <linux/xarray.h> @@ -2628,21 +2629,31 @@ static int scmi_handle_put(const struct scmi_handle *handle) return 0; } -static void scmi_device_link_add(struct device *consumer, +static bool scmi_device_link_add(struct device *consumer, struct device *supplier) { struct device_link *link; link = device_link_add(consumer, supplier, DL_FLAG_AUTOREMOVE_CONSUMER); - WARN_ON(!link); + return !WARN_ON(!link); +} + +static void scmi_clear_handle(struct scmi_device *scmi_dev) +{ + if (!scmi_dev->handle) + return; + + scmi_handle_put(scmi_dev->handle); + scmi_dev->handle = NULL; } static void scmi_set_handle(struct scmi_device *scmi_dev) { scmi_dev->handle = scmi_handle_get(&scmi_dev->dev); - if (scmi_dev->handle) - scmi_device_link_add(&scmi_dev->dev, scmi_dev->handle->dev); + if (scmi_dev->handle && + !scmi_device_link_add(&scmi_dev->dev, scmi_dev->handle->dev)) + scmi_clear_handle(scmi_dev); } static int __scmi_xfer_info_init(struct scmi_info *sinfo, @@ -2751,6 +2762,9 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node, idx = tx ? 0 : 1; idr = tx ? &info->tx_idr : &info->rx_idr; + if (idr_find(idr, prot_id)) + return -EEXIST; + if (!info->desc->ops->chan_available(of_node, idx)) { cinfo = idr_find(idr, SCMI_PROTOCOL_BASE); if (unlikely(!cinfo)) /* Possible only if platform has no Rx */ @@ -2768,7 +2782,7 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node, cinfo->no_completion_irq = info->desc->no_completion_irq; /* Create a unique name for this transport device */ - snprintf(name, 32, "__scmi_transport_device_%s_%02X", + snprintf(name, sizeof(name), SCMI_TRANSPORT_DEVNAME_PREFIX "_%s_%02X", idx ? "rx" : "tx", prot_id); /* Create a uniquely named, dedicated transport device for this chan */ tdev = scmi_device_create(of_node, info->dev, prot_id, name); @@ -2778,13 +2792,12 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node, devm_kfree(info->dev, cinfo); return -EINVAL; } - of_node_get(of_node); cinfo->id = prot_id; cinfo->dev = &tdev->dev; + cinfo->handle = &info->handle; ret = info->desc->ops->chan_setup(cinfo, info->dev, tx); if (ret) { - of_node_put(of_node); scmi_device_destroy(info->dev, prot_id, name); devm_kfree(info->dev, cinfo); return ret; @@ -2807,14 +2820,13 @@ idr_alloc: "unable to allocate SCMI idr slot err %d\n", ret); /* Destroy channel and device only if created by this call. */ if (tdev) { - of_node_put(of_node); + info->desc->ops->chan_free(prot_id, cinfo, idr); scmi_device_destroy(info->dev, prot_id, name); devm_kfree(info->dev, cinfo); } return ret; } - cinfo->handle = &info->handle; return 0; } @@ -2872,9 +2884,11 @@ static int scmi_channels_setup(struct scmi_info *info) if (of_property_read_u32(child, "reg", &prot_id)) continue; - if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id)) + if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id)) { dev_err(info->dev, "Out of range protocol %d\n", prot_id); + continue; + } ret = scmi_txrx_setup(info, child, prot_id); if (ret) @@ -2884,7 +2898,7 @@ static int scmi_channels_setup(struct scmi_info *info) return 0; } -static int scmi_chan_destroy(int id, void *p, void *idr) +static int scmi_chan_destroy(int id, void *p, void *data) { struct scmi_chan_info *cinfo = p; @@ -2892,13 +2906,10 @@ static int scmi_chan_destroy(int id, void *p, void *idr) struct scmi_info *info = handle_to_scmi_info(cinfo->handle); struct scmi_device *sdev = to_scmi_dev(cinfo->dev); - of_node_put(cinfo->dev->of_node); - scmi_device_destroy(info->dev, id, sdev->name); + scmi_device_destroy(info->dev, cinfo->id, sdev->name); cinfo->dev = NULL; } - idr_remove(idr, id); - return 0; } @@ -2925,6 +2936,7 @@ static int scmi_bus_notifier(struct notifier_block *nb, { struct scmi_info *info = bus_nb_to_scmi_info(nb); struct scmi_device *sdev = to_scmi_dev(data); + const char *status; /* Skip devices of different SCMI instances */ if (sdev->dev.parent != info->dev) @@ -2934,18 +2946,22 @@ static int scmi_bus_notifier(struct notifier_block *nb, case BUS_NOTIFY_BIND_DRIVER: /* setup handle now as the transport is ready */ scmi_set_handle(sdev); + status = "about to be BOUND."; + break; + case BUS_NOTIFY_DRIVER_NOT_BOUND: + scmi_clear_handle(sdev); + status = "NOT BOUND."; break; case BUS_NOTIFY_UNBOUND_DRIVER: - scmi_handle_put(sdev->handle); - sdev->handle = NULL; + scmi_clear_handle(sdev); + status = "UNBOUND."; break; default: return NOTIFY_DONE; } dev_dbg(info->dev, "Device %s (%s) is now %s\n", dev_name(&sdev->dev), - sdev->name, action == BUS_NOTIFY_BIND_DRIVER ? - "about to be BOUND." : "UNBOUND."); + sdev->name, status); return NOTIFY_OK; } @@ -2957,7 +2973,9 @@ static int scmi_device_request_notifier(struct notifier_block *nb, struct scmi_device_id *id_table = data; struct scmi_info *info = req_nb_to_scmi_info(nb); + rcu_read_lock(); np = idr_find(&info->active_protocols, id_table->protocol_id); + rcu_read_unlock(); if (!np) return NOTIFY_DONE; @@ -3263,7 +3281,7 @@ static int scmi_probe(struct platform_device *pdev) ret = scmi_channels_setup(info); if (ret) { err_str = "failed to setup channels\n"; - goto clear_ida; + goto clear_txrx_setup; } ret = bus_register_notifier(&scmi_bus_type, &info->bus_nb); @@ -3325,7 +3343,7 @@ static int scmi_probe(struct platform_device *pdev) dev_err(dev, "%s", err_str); return 0; } - goto notification_exit; + goto raw_mode_cleanup; } mutex_lock(&scmi_list_mutex); @@ -3340,8 +3358,10 @@ static int scmi_probe(struct platform_device *pdev) if (of_property_read_u32(child, "reg", &prot_id)) continue; - if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id)) + if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id)) { dev_err(dev, "Out of range protocol %d\n", prot_id); + continue; + } if (!scmi_is_protocol_implemented(handle, prot_id)) { dev_err(dev, "SCMI protocol %d not implemented\n", @@ -3367,18 +3387,18 @@ static int scmi_probe(struct platform_device *pdev) return 0; -notification_exit: +raw_mode_cleanup: if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT)) scmi_raw_mode_cleanup(info->raw); - scmi_notification_exit(&info->handle); clear_dev_req_notifier: blocking_notifier_chain_unregister(&scmi_requested_devices_nh, &info->dev_req_nb); clear_bus_notifier: bus_unregister_notifier(&scmi_bus_type, &info->bus_nb); clear_txrx_setup: + scmi_notification_quiesce(&info->handle); scmi_cleanup_txrx_channels(info); -clear_ida: + scmi_notification_exit(&info->handle); ida_free(&scmi_id, info->id); out_err: @@ -3401,6 +3421,12 @@ static void scmi_remove(struct platform_device *pdev) list_del(&info->node); mutex_unlock(&scmi_list_mutex); + blocking_notifier_chain_unregister(&scmi_requested_devices_nh, + &info->dev_req_nb); + + /* Stop transport callbacks before tearing down notifications. */ + scmi_notification_quiesce(&info->handle); + scmi_cleanup_txrx_channels(info); scmi_notification_exit(&info->handle); mutex_lock(&info->protocols_mtx); @@ -3411,13 +3437,8 @@ static void scmi_remove(struct platform_device *pdev) of_node_put(child); idr_destroy(&info->active_protocols); - blocking_notifier_chain_unregister(&scmi_requested_devices_nh, - &info->dev_req_nb); bus_unregister_notifier(&scmi_bus_type, &info->bus_nb); - /* Safe to free channels since no more users */ - scmi_cleanup_txrx_channels(info); - ida_free(&scmi_id, info->id); } diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c index 0a192cf2deab..fb60fb07c0ed 100644 --- a/drivers/firmware/arm_scmi/notify.c +++ b/drivers/firmware/arm_scmi/notify.c @@ -209,11 +209,11 @@ struct scmi_registered_events_desc; * @init_work: A work item to perform final initializations of pending handlers * @notify_wq: A reference to the allocated Kernel cmwq * @pending_mtx: A mutex to protect @pending_events_handlers + * @pending_events_handlers: An hashtable containing all pending events' + * handlers descriptors * @registered_protocols: A statically allocated array containing pointers to * all the registered protocol-level specific information * related to events' handling - * @pending_events_handlers: An hashtable containing all pending events' - * handlers descriptors * * Each platform instance, represented by a handle, has its own instance of * the notification subsystem represented by this structure. @@ -225,8 +225,8 @@ struct scmi_notify_instance { struct workqueue_struct *notify_wq; /* lock to protect pending_events_handlers */ struct mutex pending_mtx; - struct scmi_registered_events_desc **registered_protocols; DECLARE_HASHTABLE(pending_events_handlers, SCMI_PENDING_HASH_SZ); + struct scmi_registered_events_desc *registered_protocols[SCMI_MAX_PROTO]; }; /** @@ -276,13 +276,13 @@ struct scmi_registered_event; * @eh_sz: Size of the pre-allocated buffer @eh * @in_flight: A reference to an in flight &struct scmi_registered_event * @num_events: Number of events in @registered_events - * @registered_events: A dynamically allocated array holding all the registered - * events' descriptors, whose fixed-size is determined at - * compile time. * @registered_mtx: A mutex to protect @registered_events_handlers * @ph: SCMI protocol handle reference * @registered_events_handlers: An hashtable containing all events' handlers * descriptors registered for this protocol + * @registered_events: A dynamically allocated array holding all the registered + * events' descriptors, whose fixed-size is determined at + * compile time. * * All protocols that register at least one event have their protocol-specific * information stored here, together with the embedded allocated events_queue. @@ -302,11 +302,11 @@ struct scmi_registered_events_desc { size_t eh_sz; void *in_flight; int num_events; - struct scmi_registered_event **registered_events; /* mutex to protect registered_events_handlers */ struct mutex registered_mtx; const struct scmi_protocol_handle *ph; DECLARE_HASHTABLE(registered_events_handlers, SCMI_REGISTERED_HASH_SZ); + struct scmi_registered_event *registered_events[] __counted_by(num_events); }; /** @@ -338,9 +338,9 @@ struct scmi_registered_event { void *report; u32 num_sources; bool not_supported_by_platform; - refcount_t *sources; /* locking to serialize the access to sources */ struct mutex sources_mtx; + refcount_t sources[] __counted_by(num_sources); }; /** @@ -706,9 +706,13 @@ scmi_allocate_registered_events_desc(struct scmi_notify_instance *ni, if (WARN_ON(ni->registered_protocols[proto_id])) return ERR_PTR(-EINVAL); - pd = devm_kzalloc(ni->handle->dev, sizeof(*pd), GFP_KERNEL); + pd = devm_kzalloc(ni->handle->dev, + struct_size(pd, registered_events, num_events), + GFP_KERNEL); if (!pd) return ERR_PTR(-ENOMEM); + + pd->num_events = num_events; pd->id = proto_id; pd->ops = ops; pd->ni = ni; @@ -722,12 +726,6 @@ scmi_allocate_registered_events_desc(struct scmi_notify_instance *ni, return ERR_PTR(-ENOMEM); pd->eh_sz = eh_sz; - pd->registered_events = devm_kcalloc(ni->handle->dev, num_events, - sizeof(char *), GFP_KERNEL); - if (!pd->registered_events) - return ERR_PTR(-ENOMEM); - pd->num_events = num_events; - /* Initialize per protocol handlers table */ mutex_init(&pd->registered_mtx); hash_init(pd->registered_events_handlers); @@ -796,18 +794,16 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id, int id; struct scmi_registered_event *r_evt; - r_evt = devm_kzalloc(ni->handle->dev, sizeof(*r_evt), + r_evt = devm_kzalloc(ni->handle->dev, + struct_size(r_evt, sources, num_sources), GFP_KERNEL); if (!r_evt) return -ENOMEM; + + r_evt->num_sources = num_sources; r_evt->proto = pd; r_evt->evt = evt; - r_evt->sources = devm_kcalloc(ni->handle->dev, num_sources, - sizeof(refcount_t), GFP_KERNEL); - if (!r_evt->sources) - return -ENOMEM; - r_evt->num_sources = num_sources; mutex_init(&r_evt->sources_mtx); r_evt->report = devm_kzalloc(ni->handle->dev, @@ -1673,11 +1669,6 @@ int scmi_notification_init(struct scmi_handle *handle) ni->gid = gid; ni->handle = handle; - ni->registered_protocols = devm_kcalloc(handle->dev, SCMI_MAX_PROTO, - sizeof(char *), GFP_KERNEL); - if (!ni->registered_protocols) - goto err; - ni->notify_wq = alloc_workqueue(dev_name(handle->dev), WQ_UNBOUND | WQ_FREEZABLE | WQ_SYSFS, 0); @@ -1707,6 +1698,25 @@ err: } /** + * scmi_notification_quiesce() - Stop notification late initialization + * @handle: The handle identifying the platform instance to quiesce + * + * Prevent new late-init work from being queued and wait for any already queued + * or running late-init work to complete before transport channels are torn + * down. + */ +void scmi_notification_quiesce(struct scmi_handle *handle) +{ + struct scmi_notify_instance *ni; + + ni = scmi_notification_instance_data_get(handle); + if (!ni) + return; + + disable_work_sync(&ni->init_work); +} + +/** * scmi_notification_exit() - Shutdown and clean Notification core * @handle: The handle identifying the platform instance to shutdown */ @@ -1717,6 +1727,8 @@ void scmi_notification_exit(struct scmi_handle *handle) ni = scmi_notification_instance_data_get(handle); if (!ni) return; + + scmi_notification_quiesce(handle); scmi_notification_instance_data_set(handle, NULL); /* Destroy while letting pending work complete */ diff --git a/drivers/firmware/arm_scmi/notify.h b/drivers/firmware/arm_scmi/notify.h index 76758a736cf4..f18f98c5ab3b 100644 --- a/drivers/firmware/arm_scmi/notify.h +++ b/drivers/firmware/arm_scmi/notify.h @@ -82,6 +82,7 @@ struct scmi_protocol_events { }; int scmi_notification_init(struct scmi_handle *handle); +void scmi_notification_quiesce(struct scmi_handle *handle); void scmi_notification_exit(struct scmi_handle *handle); int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id, const struct scmi_protocol_handle *ph, diff --git a/drivers/firmware/arm_scmi/transports/mailbox.c b/drivers/firmware/arm_scmi/transports/mailbox.c index ae0f67e6cc45..308736c3ead9 100644 --- a/drivers/firmware/arm_scmi/transports/mailbox.c +++ b/drivers/firmware/arm_scmi/transports/mailbox.c @@ -211,13 +211,18 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, cl->tx_block = false; cl->knows_txdone = tx; + cinfo->transport_info = smbox; + smbox->cinfo = cinfo; + mutex_init(&smbox->chan_lock); + smbox->chan = mbox_request_channel(cl, tx ? 0 : p2a_chan); if (IS_ERR(smbox->chan)) { ret = PTR_ERR(smbox->chan); + smbox->chan = NULL; if (ret != -EPROBE_DEFER) dev_err(cdev, "failed to request SCMI %s mailbox\n", desc); - return ret; + goto err_clear_cinfo; } /* Additional unidirectional channel for TX if needed */ @@ -225,9 +230,10 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, smbox->chan_receiver = mbox_request_channel(cl, a2p_rx_chan); if (IS_ERR(smbox->chan_receiver)) { ret = PTR_ERR(smbox->chan_receiver); + smbox->chan_receiver = NULL; if (ret != -EPROBE_DEFER) dev_err(cdev, "failed to request SCMI Tx Receiver mailbox\n"); - return ret; + goto err_free_chan; } } @@ -235,17 +241,23 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, smbox->chan_platform_receiver = mbox_request_channel(cl, p2a_rx_chan); if (IS_ERR(smbox->chan_platform_receiver)) { ret = PTR_ERR(smbox->chan_platform_receiver); + smbox->chan_platform_receiver = NULL; if (ret != -EPROBE_DEFER) dev_err(cdev, "failed to request SCMI P2A Receiver mailbox\n"); - return ret; + goto err_free_chan; } } - cinfo->transport_info = smbox; - smbox->cinfo = cinfo; - mutex_init(&smbox->chan_lock); - return 0; + +err_free_chan: + mbox_free_channel(smbox->chan); +err_clear_cinfo: + cinfo->transport_info = NULL; + smbox->cinfo = NULL; + devm_iounmap(dev, smbox->shmem); + devm_kfree(dev, smbox); + return ret; } static int mailbox_chan_free(int id, void *p, void *data) diff --git a/drivers/firmware/arm_scmi/transports/smc.c b/drivers/firmware/arm_scmi/transports/smc.c index 21abb571e4f2..1fce3ccdeb7f 100644 --- a/drivers/firmware/arm_scmi/transports/smc.c +++ b/drivers/firmware/arm_scmi/transports/smc.c @@ -172,6 +172,13 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, scmi_info->param_page = SHMEM_PAGE(res.start); scmi_info->param_offset = SHMEM_OFFSET(res.start); } + + scmi_info->func_id = func_id; + scmi_info->cap_id = cap_id; + scmi_info->cinfo = cinfo; + smc_channel_lock_init(scmi_info); + cinfo->transport_info = scmi_info; + /* * If there is an interrupt named "a2p", then the service and * completion of a message is signaled by an interrupt rather than by @@ -183,18 +190,14 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, IRQF_NO_SUSPEND, dev_name(dev), scmi_info); if (ret) { dev_err(dev, "failed to setup SCMI smc irq\n"); + cinfo->transport_info = NULL; + scmi_info->cinfo = NULL; return ret; } } else { cinfo->no_completion_irq = true; } - scmi_info->func_id = func_id; - scmi_info->cap_id = cap_id; - scmi_info->cinfo = cinfo; - smc_channel_lock_init(scmi_info); - cinfo->transport_info = scmi_info; - return 0; } diff --git a/drivers/soc/ixp4xx/ixp4xx-npe.c b/drivers/soc/ixp4xx/ixp4xx-npe.c index 33e2e0366f19..0b3cc471ed62 100644 --- a/drivers/soc/ixp4xx/ixp4xx-npe.c +++ b/drivers/soc/ixp4xx/ixp4xx-npe.c @@ -752,6 +752,7 @@ static const struct of_device_id ixp4xx_npe_of_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, ixp4xx_npe_of_match); static struct platform_driver ixp4xx_npe_driver = { .driver = { diff --git a/drivers/soc/ixp4xx/ixp4xx-qmgr.c b/drivers/soc/ixp4xx/ixp4xx-qmgr.c index 475e229039e3..492a31660535 100644 --- a/drivers/soc/ixp4xx/ixp4xx-qmgr.c +++ b/drivers/soc/ixp4xx/ixp4xx-qmgr.c @@ -421,19 +421,13 @@ static int ixp4xx_qmgr_probe(struct platform_device *pdev) err = devm_request_irq(dev, irq1, handler1, 0, "IXP4xx Queue Manager", NULL); - if (err) { - dev_err(dev, "failed to request IRQ%i (%i)\n", - irq1, err); + if (err) return err; - } err = devm_request_irq(dev, irq2, handler2, 0, "IXP4xx Queue Manager", NULL); - if (err) { - dev_err(dev, "failed to request IRQ%i (%i)\n", - irq2, err); + if (err) return err; - } used_sram_bitmap[0] = 0xF; /* 4 first pages reserved for config */ spin_lock_init(&qmgr_lock); @@ -454,6 +448,7 @@ static const struct of_device_id ixp4xx_qmgr_of_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, ixp4xx_qmgr_of_match); static struct platform_driver ixp4xx_qmgr_driver = { .driver = { diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 2ab150d04bb1..fdf18ed2dfc2 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -356,7 +356,6 @@ config ARCH_R8A779H0 config ARCH_R8A78000 bool "ARM64 Platform support for R8A78000 (R-Car X5H)" default y if ARCH_RENESAS - default ARCH_RENESAS select ARCH_RCAR_GEN5 help This enables support for the Renesas R-Car X5H SoC. |
