diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-09-17 10:48:09 +0200 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-09-17 10:48:09 +0200 |
| commit | b8979c6b4d0d1b36e94f5bc483fd86e38107e554 (patch) | |
| tree | 4b2081e6fae1a40a57cf36c5b130b02c55c666f3 /drivers/firmware/arm_scmi/driver.c | |
| parent | 7b17f5ebd5fc5e9275eaa5af3d0771f2a7b01bbf (diff) | |
| parent | b62800736f61521547d50fd8cc332cf9b74cbaff (diff) | |
Merge tag 'soc-drivers-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
Pull SoC driver updates from Arnd Bergmann:
"The driver updates seem larger this time around, with changes is many
of the SoC specific drivers, both the custom drivers/soc ones and the
closely related subsystems (memory, bus, firmware, reset, ...).
The at91 platform gains support for sam9x7 chips in the soc and power
management code. This is the latest variant of one of the oldest still
supported SoC families, using the ARM9 (ARMv5) core.
As usual, the qualcomm snapdragon platform gets a ton of updates in
many of their drivers to add more features and additional SoC support.
Most of these are somewhat firmware related as the platform has a
number of firmware based interfaces to the kernel. A notable addition
here is the inclusion of trace events to two of these drivers.
Herve Codina and Christophe Leroy are now sending updates for
drivers/soc/fsl/ code through the SoC tree, this contains both PowerPC
and Arm specific platforms and has previously been problematic to
maintain. The first update here contains support for newer PowerPC
variants and some cleanups.
The turris mox firmware driver has a number of updates, mostly
cleanups.
The Arm SCMI firmware driver gets a major rework to modularize the
existing code into separately loadable drivers for the various
transports, the addition of custom NXP i.MX9 interfaces and a number
of smaller updates.
The Arm FF-A firmware driver gets a feature update to support the v1.2
version of the specification.
The reset controller drivers have some smaller cleanups and a newly
added driver for the Intel/Mobileye EyeQ5/EyeQ6 MIPS SoCs.
The memory controller drivers get some cleanups and refactoring for
Tegra, TI, Freescale/NXP and a couple more platforms.
Finally there are lots of minor updates to firmware (raspberry pi,
tegra, imx), bus (sunxi, omap, tegra) and soc (rockchips, tegra,
amlogic, mediatek) drivers and their DT bindings"
* tag 'soc-drivers-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (212 commits)
firmware: imx: remove duplicate scmi_imx_misc_ctrl_get()
platform: cznic: turris-omnia-mcu: Fix error check in omnia_mcu_register_trng()
bus: sunxi-rsb: Simplify code with dev_err_probe()
soc: fsl: qe: ucc: Export ucc_mux_set_grant_tsa_bkpt
soc: fsl: cpm1: qmc: Fix dependency on fsl_soc.h
dt-bindings: arm: rockchip: Add rk3576 compatible string to pmu.yaml
soc: fsl: qbman: Remove redundant warnings
soc: fsl: qbman: Use iommu_paging_domain_alloc()
MAINTAINERS: Add QE files related to the Freescale QMC controller
soc: fsl: cpm1: qmc: Handle QUICC Engine (QE) soft-qmc firmware
soc: fsl: cpm1: qmc: Add support for QUICC Engine (QE) implementation
soc: fsl: qe: Add missing PUSHSCHED command
soc: fsl: qe: Add resource-managed muram allocators
soc: fsl: cpm1: qmc: Introduce qmc_version
soc: fsl: cpm1: qmc: Rename SCC_GSMRL_MODE_QMC
soc: fsl: cpm1: qmc: Handle RPACK initialization
soc: fsl: cpm1: qmc: Rename qmc_chan_command()
soc: fsl: cpm1: qmc: Introduce qmc_{init,exit}_xcc() and their CPM1 version
soc: fsl: cpm1: qmc: Introduce qmc_init_resource() and its CPM1 version
soc: fsl: cpm1: qmc: Re-order probe() operations
...
Diffstat (limited to 'drivers/firmware/arm_scmi/driver.c')
| -rw-r--r-- | drivers/firmware/arm_scmi/driver.c | 241 |
1 files changed, 135 insertions, 106 deletions
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 6b6957f4743f..69c15135371c 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -11,7 +11,7 @@ * various power domain DVFS including the core/cluster, certain system * clocks configuration, thermal sensors and many others. * - * Copyright (C) 2018-2021 ARM Ltd. + * Copyright (C) 2018-2024 ARM Ltd. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -117,12 +117,14 @@ struct scmi_protocol_instance { * @name: Name of this SCMI instance * @type: Type of this SCMI instance * @is_atomic: Flag to state if the transport of this instance is atomic + * @counters: An array of atomic_c's used for tracking statistics (if enabled) */ struct scmi_debug_info { struct dentry *top_dentry; const char *name; const char *type; bool is_atomic; + atomic_t counters[SCMI_DEBUG_COUNTERS_LAST]; }; /** @@ -194,6 +196,16 @@ struct scmi_info { #define bus_nb_to_scmi_info(nb) container_of(nb, struct scmi_info, bus_nb) #define req_nb_to_scmi_info(nb) container_of(nb, struct scmi_info, dev_req_nb) +static void scmi_rx_callback(struct scmi_chan_info *cinfo, + u32 msg_hdr, void *priv); +static void scmi_bad_message_trace(struct scmi_chan_info *cinfo, + u32 msg_hdr, enum scmi_bad_msg err); + +static struct scmi_transport_core_operations scmi_trans_core_ops = { + .bad_message_trace = scmi_bad_message_trace, + .rx_callback = scmi_rx_callback, +}; + static unsigned long scmi_vendor_protocol_signature(unsigned int protocol_id, char *vendor_id, char *sub_vendor_id, u32 impl_ver) @@ -833,8 +845,8 @@ scmi_xfer_lookup_unlocked(struct scmi_xfers_info *minfo, u16 xfer_id) * timed-out message that arrives and as such, can be traced only referring to * the header content, since the payload is missing/unreliable. */ -void scmi_bad_message_trace(struct scmi_chan_info *cinfo, u32 msg_hdr, - enum scmi_bad_msg err) +static void scmi_bad_message_trace(struct scmi_chan_info *cinfo, u32 msg_hdr, + enum scmi_bad_msg err) { char *tag; struct scmi_info *info = handle_to_scmi_info(cinfo->handle); @@ -988,6 +1000,7 @@ scmi_xfer_command_acquire(struct scmi_chan_info *cinfo, u32 msg_hdr) spin_unlock_irqrestore(&minfo->xfer_lock, flags); scmi_bad_message_trace(cinfo, msg_hdr, MSG_UNEXPECTED); + scmi_inc_count(info->dbg->counters, ERR_MSG_UNEXPECTED); return xfer; } @@ -1015,6 +1028,7 @@ scmi_xfer_command_acquire(struct scmi_chan_info *cinfo, u32 msg_hdr) msg_type, xfer_id, msg_hdr, xfer->state); scmi_bad_message_trace(cinfo, msg_hdr, MSG_INVALID); + scmi_inc_count(info->dbg->counters, ERR_MSG_INVALID); /* On error the refcount incremented above has to be dropped */ __scmi_xfer_put(minfo, xfer); @@ -1054,6 +1068,7 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo, PTR_ERR(xfer)); scmi_bad_message_trace(cinfo, msg_hdr, MSG_NOMEM); + scmi_inc_count(info->dbg->counters, ERR_MSG_NOMEM); scmi_clear_channel(info, cinfo); return; @@ -1069,6 +1084,7 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo, trace_scmi_msg_dump(info->id, cinfo->id, xfer->hdr.protocol_id, xfer->hdr.id, "NOTI", xfer->hdr.seq, xfer->hdr.status, xfer->rx.buf, xfer->rx.len); + scmi_inc_count(info->dbg->counters, NOTIFICATION_OK); scmi_notify(cinfo->handle, xfer->hdr.protocol_id, xfer->hdr.id, xfer->rx.buf, xfer->rx.len, ts); @@ -1128,8 +1144,10 @@ static void scmi_handle_response(struct scmi_chan_info *cinfo, if (xfer->hdr.type == MSG_TYPE_DELAYED_RESP) { scmi_clear_channel(info, cinfo); complete(xfer->async_done); + scmi_inc_count(info->dbg->counters, DELAYED_RESPONSE_OK); } else { complete(&xfer->done); + scmi_inc_count(info->dbg->counters, RESPONSE_OK); } if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT)) { @@ -1160,7 +1178,8 @@ static void scmi_handle_response(struct scmi_chan_info *cinfo, * NOTE: This function will be invoked in IRQ context, hence should be * as optimal as possible. */ -void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr, void *priv) +static void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr, + void *priv) { u8 msg_type = MSG_XTRACT_TYPE(msg_hdr); @@ -1213,6 +1232,7 @@ static int scmi_wait_for_reply(struct device *dev, const struct scmi_desc *desc, struct scmi_xfer *xfer, unsigned int timeout_ms) { int ret = 0; + struct scmi_info *info = handle_to_scmi_info(cinfo->handle); if (xfer->hdr.poll_completion) { /* @@ -1233,13 +1253,12 @@ static int scmi_wait_for_reply(struct device *dev, const struct scmi_desc *desc, "timed out in resp(caller: %pS) - polling\n", (void *)_RET_IP_); ret = -ETIMEDOUT; + scmi_inc_count(info->dbg->counters, XFERS_RESPONSE_POLLED_TIMEOUT); } } if (!ret) { unsigned long flags; - struct scmi_info *info = - handle_to_scmi_info(cinfo->handle); /* * Do not fetch_response if an out-of-order delayed @@ -1259,11 +1278,9 @@ static int scmi_wait_for_reply(struct device *dev, const struct scmi_desc *desc, "RESP" : "resp", xfer->hdr.seq, xfer->hdr.status, xfer->rx.buf, xfer->rx.len); + scmi_inc_count(info->dbg->counters, RESPONSE_POLLED_OK); if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT)) { - struct scmi_info *info = - handle_to_scmi_info(cinfo->handle); - scmi_raw_message_report(info->raw, xfer, SCMI_RAW_REPLY_QUEUE, cinfo->id); @@ -1276,6 +1293,7 @@ static int scmi_wait_for_reply(struct device *dev, const struct scmi_desc *desc, dev_err(dev, "timed out in resp(caller: %pS)\n", (void *)_RET_IP_); ret = -ETIMEDOUT; + scmi_inc_count(info->dbg->counters, XFERS_RESPONSE_TIMEOUT); } } @@ -1359,13 +1377,15 @@ static int do_xfer(const struct scmi_protocol_handle *ph, !is_transport_polling_capable(info->desc)) { dev_warn_once(dev, "Polling mode is not supported by transport.\n"); + scmi_inc_count(info->dbg->counters, SENT_FAIL_POLLING_UNSUPPORTED); return -EINVAL; } cinfo = idr_find(&info->tx_idr, pi->proto->id); - if (unlikely(!cinfo)) + if (unlikely(!cinfo)) { + scmi_inc_count(info->dbg->counters, SENT_FAIL_CHANNEL_NOT_FOUND); return -EINVAL; - + } /* True ONLY if also supported by transport. */ if (is_polling_enabled(cinfo, info->desc)) xfer->hdr.poll_completion = true; @@ -1397,16 +1417,20 @@ static int do_xfer(const struct scmi_protocol_handle *ph, ret = info->desc->ops->send_message(cinfo, xfer); if (ret < 0) { dev_dbg(dev, "Failed to send message %d\n", ret); + scmi_inc_count(info->dbg->counters, SENT_FAIL); return ret; } trace_scmi_msg_dump(info->id, cinfo->id, xfer->hdr.protocol_id, xfer->hdr.id, "CMND", xfer->hdr.seq, xfer->hdr.status, xfer->tx.buf, xfer->tx.len); + scmi_inc_count(info->dbg->counters, SENT_OK); ret = scmi_wait_for_message_response(cinfo, xfer); - if (!ret && xfer->hdr.status) + if (!ret && xfer->hdr.status) { ret = scmi_to_linux_errno(xfer->hdr.status); + scmi_inc_count(info->dbg->counters, ERR_PROTOCOL); + } if (info->desc->ops->mark_txdone) info->desc->ops->mark_txdone(cinfo, ret, xfer); @@ -2708,14 +2732,14 @@ scmi_txrx_setup(struct scmi_info *info, struct device_node *of_node, static int scmi_channels_setup(struct scmi_info *info) { int ret; - struct device_node *child, *top_np = info->dev->of_node; + struct device_node *top_np = info->dev->of_node; /* Initialize a common generic channel at first */ ret = scmi_txrx_setup(info, top_np, SCMI_PROTOCOL_BASE); if (ret) return ret; - for_each_available_child_of_node(top_np, child) { + for_each_available_child_of_node_scoped(top_np, child) { u32 prot_id; if (of_property_read_u32(child, "reg", &prot_id)) @@ -2726,10 +2750,8 @@ static int scmi_channels_setup(struct scmi_info *info) "Out of range protocol %d\n", prot_id); ret = scmi_txrx_setup(info, child, prot_id); - if (ret) { - of_node_put(child); + if (ret) return ret; - } } return 0; @@ -2833,6 +2855,56 @@ static int scmi_device_request_notifier(struct notifier_block *nb, return NOTIFY_OK; } +static const char * const dbg_counter_strs[] = { + "sent_ok", + "sent_fail", + "sent_fail_polling_unsupported", + "sent_fail_channel_not_found", + "response_ok", + "notification_ok", + "delayed_response_ok", + "xfers_response_timeout", + "xfers_response_polled_timeout", + "response_polled_ok", + "err_msg_unexpected", + "err_msg_invalid", + "err_msg_nomem", + "err_protocol", +}; + +static ssize_t reset_all_on_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct scmi_debug_info *dbg = filp->private_data; + + for (int i = 0; i < SCMI_DEBUG_COUNTERS_LAST; i++) + atomic_set(&dbg->counters[i], 0); + + return count; +} + +static const struct file_operations fops_reset_counts = { + .owner = THIS_MODULE, + .open = simple_open, + .llseek = no_llseek, + .write = reset_all_on_write, +}; + +static void scmi_debugfs_counters_setup(struct scmi_debug_info *dbg, + struct dentry *trans) +{ + struct dentry *counters; + int idx; + + counters = debugfs_create_dir("counters", trans); + + for (idx = 0; idx < SCMI_DEBUG_COUNTERS_LAST; idx++) + debugfs_create_atomic_t(dbg_counter_strs[idx], 0600, counters, + &dbg->counters[idx]); + + debugfs_create_file("reset", 0200, counters, dbg, &fops_reset_counts); +} + static void scmi_debugfs_common_cleanup(void *d) { struct scmi_debug_info *dbg = d; @@ -2899,6 +2971,9 @@ static struct scmi_debug_info *scmi_debugfs_common_setup(struct scmi_info *info) debugfs_create_u32("rx_max_msg", 0400, trans, (u32 *)&info->rx_minfo.max_msg); + if (IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_COUNTERS)) + scmi_debugfs_counters_setup(dbg, trans); + dbg->top_dentry = top_dentry; if (devm_add_action_or_reset(info->dev, @@ -2950,6 +3025,37 @@ static int scmi_debugfs_raw_mode_setup(struct scmi_info *info) return ret; } +static const struct scmi_desc *scmi_transport_setup(struct device *dev) +{ + struct scmi_transport *trans; + int ret; + + trans = dev_get_platdata(dev); + if (!trans || !trans->desc || !trans->supplier || !trans->core_ops) + return NULL; + + if (!device_link_add(dev, trans->supplier, DL_FLAG_AUTOREMOVE_CONSUMER)) { + dev_err(dev, + "Adding link to supplier transport device failed\n"); + return NULL; + } + + /* Provide core transport ops */ + *trans->core_ops = &scmi_trans_core_ops; + + dev_info(dev, "Using %s\n", dev_driver_string(trans->supplier)); + + ret = of_property_read_u32(dev->of_node, "max-rx-timeout-ms", + &trans->desc->max_rx_timeout_ms); + if (ret && ret != -EINVAL) + dev_err(dev, "Malformed max-rx-timeout-ms DT property.\n"); + + dev_info(dev, "SCMI max-rx-timeout: %dms\n", + trans->desc->max_rx_timeout_ms); + + return trans->desc; +} + static int scmi_probe(struct platform_device *pdev) { int ret; @@ -2961,9 +3067,12 @@ static int scmi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *child, *np = dev->of_node; - desc = of_device_get_match_data(dev); - if (!desc) - return -EINVAL; + desc = scmi_transport_setup(dev); + if (!desc) { + err_str = "transport invalid\n"; + ret = -EINVAL; + goto out_err; + } info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); if (!info) @@ -3002,14 +3111,6 @@ static int scmi_probe(struct platform_device *pdev) info->atomic_threshold); handle->is_transport_atomic = scmi_is_transport_atomic; - if (desc->ops->link_supplier) { - ret = desc->ops->link_supplier(dev); - if (ret) { - err_str = "transport not ready\n"; - goto clear_ida; - } - } - /* Setup all channels described in the DT at first */ ret = scmi_channels_setup(info); if (ret) { @@ -3130,6 +3231,7 @@ clear_txrx_setup: clear_ida: ida_free(&scmi_id, info->id); +out_err: return dev_err_probe(dev, ret, "%s", err_str); } @@ -3215,86 +3317,16 @@ static struct attribute *versions_attrs[] = { }; ATTRIBUTE_GROUPS(versions); -/* Each compatible listed below must have descriptor associated with it */ -static const struct of_device_id scmi_of_match[] = { -#ifdef CONFIG_ARM_SCMI_TRANSPORT_MAILBOX - { .compatible = "arm,scmi", .data = &scmi_mailbox_desc }, -#endif -#ifdef CONFIG_ARM_SCMI_TRANSPORT_OPTEE - { .compatible = "linaro,scmi-optee", .data = &scmi_optee_desc }, -#endif -#ifdef CONFIG_ARM_SCMI_TRANSPORT_SMC - { .compatible = "arm,scmi-smc", .data = &scmi_smc_desc}, - { .compatible = "arm,scmi-smc-param", .data = &scmi_smc_desc}, - { .compatible = "qcom,scmi-smc", .data = &scmi_smc_desc}, -#endif -#ifdef CONFIG_ARM_SCMI_TRANSPORT_VIRTIO - { .compatible = "arm,scmi-virtio", .data = &scmi_virtio_desc}, -#endif - { /* Sentinel */ }, -}; - -MODULE_DEVICE_TABLE(of, scmi_of_match); - static struct platform_driver scmi_driver = { .driver = { .name = "arm-scmi", .suppress_bind_attrs = true, - .of_match_table = scmi_of_match, .dev_groups = versions_groups, }, .probe = scmi_probe, .remove_new = scmi_remove, }; -/** - * __scmi_transports_setup - Common helper to call transport-specific - * .init/.exit code if provided. - * - * @init: A flag to distinguish between init and exit. - * - * Note that, if provided, we invoke .init/.exit functions for all the - * transports currently compiled in. - * - * Return: 0 on Success. - */ -static inline int __scmi_transports_setup(bool init) -{ - int ret = 0; - const struct of_device_id *trans; - - for (trans = scmi_of_match; trans->data; trans++) { - const struct scmi_desc *tdesc = trans->data; - - if ((init && !tdesc->transport_init) || - (!init && !tdesc->transport_exit)) - continue; - - if (init) - ret = tdesc->transport_init(); - else - tdesc->transport_exit(); - - if (ret) { - pr_err("SCMI transport %s FAILED initialization!\n", - trans->compatible); - break; - } - } - - return ret; -} - -static int __init scmi_transports_init(void) -{ - return __scmi_transports_setup(true); -} - -static void __exit scmi_transports_exit(void) -{ - __scmi_transports_setup(false); -} - static struct dentry *scmi_debugfs_init(void) { struct dentry *d; @@ -3310,16 +3342,15 @@ static struct dentry *scmi_debugfs_init(void) static int __init scmi_driver_init(void) { - int ret; - /* Bail out if no SCMI transport was configured */ if (WARN_ON(!IS_ENABLED(CONFIG_ARM_SCMI_HAVE_TRANSPORT))) return -EINVAL; - /* Initialize any compiled-in transport which provided an init/exit */ - ret = scmi_transports_init(); - if (ret) - return ret; + if (IS_ENABLED(CONFIG_ARM_SCMI_HAVE_SHMEM)) + scmi_trans_core_ops.shmem = scmi_shared_mem_operations_get(); + + if (IS_ENABLED(CONFIG_ARM_SCMI_HAVE_MSG)) + scmi_trans_core_ops.msg = scmi_message_operations_get(); if (IS_ENABLED(CONFIG_ARM_SCMI_NEED_DEBUGFS)) scmi_top_dentry = scmi_debugfs_init(); @@ -3354,8 +3385,6 @@ static void __exit scmi_driver_exit(void) scmi_powercap_unregister(); scmi_pinctrl_unregister(); - scmi_transports_exit(); - platform_driver_unregister(&scmi_driver); debugfs_remove_recursive(scmi_top_dentry); |