summaryrefslogtreecommitdiffstats
path: root/drivers/soc/microchip/mpfs-sys-controller.c
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2026-09-02 09:55:01 -0400
committerSteven Rostedt <rostedt@goodmis.org>2026-09-02 11:08:21 -0400
commit4617721c502b2ddaa4e324e86da4997edf738fa5 (patch)
tree350aa83b47da0b574a814795392b20b0bed83cbc /drivers/soc/microchip/mpfs-sys-controller.c
downloadlinux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.tar.gz
linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.zip
ftrace: Synchronize the initialization of ftrace_opsgrafted
There's some internal state that ftrace_ops needs to have set, but since it can be declared outside of the ftrace.c code, it calls ftrace_ops_init() on the ops in every global function. The issue is that if two tasks call it on the same ops at the same time it is possible to have the initialization of one corrupt the initialization of the other call. Create a ops_mutex to use to synchronize every initialization of the ftrace_ops. The mutex is taken within checking the ftrace_ops flag that states it was initializied but the flag is checked again after the mutex has been taken. Checking first outside the mutex allows it to shortcut having to take the mutex. But then the check needs to be done again after the mute is taken in case of races. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260902095501.6b59af20@gandalf.local.home Fixes: f04f24fb7e48d ("ftrace, kprobes: Fix a deadlock on ftrace_regex_lock") Reported-by: sashiko-bot@kernel.org Close: https://lore.kernel.org/all/20260829025528.49A831F000E9@smtp.kernel.org/ Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'drivers/soc/microchip/mpfs-sys-controller.c')
-rw-r--r--drivers/soc/microchip/mpfs-sys-controller.c282
1 files changed, 282 insertions, 0 deletions
diff --git a/drivers/soc/microchip/mpfs-sys-controller.c b/drivers/soc/microchip/mpfs-sys-controller.c
new file mode 100644
index 000000000..0400a01b2
--- /dev/null
+++ b/drivers/soc/microchip/mpfs-sys-controller.c
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microchip PolarFire SoC (MPFS) system controller driver
+ *
+ * Copyright (c) 2020-2021 Microchip Corporation. All rights reserved.
+ *
+ * Author: Conor Dooley <conor.dooley@microchip.com>
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/kref.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/mtd/mtd.h>
+#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/mailbox_client.h>
+#include <linux/platform_device.h>
+#include <soc/microchip/mpfs.h>
+
+/*
+ * This timeout must be long, as some services (example: image authentication)
+ * take significant time to complete
+ */
+#define MPFS_SYS_CTRL_TIMEOUT_MS 30000
+
+static DEFINE_MUTEX(transaction_lock);
+
+struct mpfs_sys_controller {
+ struct mbox_client client;
+ struct mbox_chan *chan;
+ struct completion c;
+ struct mtd_info *flash;
+ struct kref consumers;
+};
+
+struct mpfs_syscon_config {
+ unsigned int nb_subdevs;
+ struct platform_device *subdevs;
+};
+
+int mpfs_blocking_transaction(struct mpfs_sys_controller *sys_controller, struct mpfs_mss_msg *msg)
+{
+ unsigned long timeout = msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS);
+ int ret;
+
+ ret = mutex_lock_interruptible(&transaction_lock);
+ if (ret)
+ return ret;
+
+ reinit_completion(&sys_controller->c);
+
+ ret = mbox_send_message(sys_controller->chan, msg);
+ if (ret < 0) {
+ dev_warn(sys_controller->client.dev, "MPFS sys controller service timeout\n");
+ goto out;
+ }
+
+ /*
+ * Unfortunately, the system controller will only deliver an interrupt
+ * if a service succeeds. mbox_send_message() will block until the busy
+ * flag is gone. If the busy flag is gone but no interrupt has arrived
+ * to trigger the rx callback then the service can be deemed to have
+ * failed.
+ * The caller can then interrogate msg::response::resp_status to
+ * determine the cause of the failure.
+ * mbox_send_message() returns positive integers in the success path, so
+ * ret needs to be cleared if we do get an interrupt.
+ */
+ if (!wait_for_completion_timeout(&sys_controller->c, timeout)) {
+ ret = -EBADMSG;
+ dev_warn(sys_controller->client.dev,
+ "MPFS sys controller service failed with status: %d\n",
+ msg->response->resp_status);
+ } else {
+ ret = 0;
+ }
+
+out:
+ mutex_unlock(&transaction_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(mpfs_blocking_transaction);
+
+static void mpfs_sys_controller_rx_callback(struct mbox_client *client, void *msg)
+{
+ struct mpfs_sys_controller *sys_controller =
+ container_of(client, struct mpfs_sys_controller, client);
+
+ complete(&sys_controller->c);
+}
+
+static void mpfs_sys_controller_delete(struct kref *kref)
+{
+ struct mpfs_sys_controller *sys_controller =
+ container_of(kref, struct mpfs_sys_controller, consumers);
+
+ mbox_free_channel(sys_controller->chan);
+ kfree(sys_controller);
+}
+
+static void mpfs_sys_controller_put(void *data)
+{
+ struct mpfs_sys_controller *sys_controller = data;
+
+ kref_put(&sys_controller->consumers, mpfs_sys_controller_delete);
+}
+
+struct mtd_info *mpfs_sys_controller_get_flash(struct mpfs_sys_controller *mpfs_client)
+{
+ return mpfs_client->flash;
+}
+EXPORT_SYMBOL(mpfs_sys_controller_get_flash);
+
+static int mpfs_sys_controller_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mpfs_sys_controller *sys_controller;
+ struct mpfs_syscon_config *of_data;
+ struct device_node *np;
+ int i, ret;
+
+ sys_controller = kzalloc_obj(*sys_controller);
+ if (!sys_controller)
+ return -ENOMEM;
+
+ np = of_parse_phandle(dev->of_node, "microchip,bitstream-flash", 0);
+ if (!np)
+ goto no_flash;
+
+ sys_controller->flash = of_get_mtd_device_by_node(np);
+ of_node_put(np);
+ if (IS_ERR(sys_controller->flash)) {
+ ret = dev_err_probe(dev, PTR_ERR(sys_controller->flash), "Failed to get flash\n");
+ goto out_free;
+ }
+
+no_flash:
+ sys_controller->client.dev = dev;
+ sys_controller->client.rx_callback = mpfs_sys_controller_rx_callback;
+ sys_controller->client.tx_block = 1U;
+ sys_controller->client.tx_tout = msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS);
+
+ sys_controller->chan = mbox_request_channel(&sys_controller->client, 0);
+ if (IS_ERR(sys_controller->chan)) {
+ ret = dev_err_probe(dev, PTR_ERR(sys_controller->chan),
+ "Failed to get mbox channel\n");
+ goto out_free;
+ }
+
+ init_completion(&sys_controller->c);
+ kref_init(&sys_controller->consumers);
+
+ platform_set_drvdata(pdev, sys_controller);
+
+ of_data = (struct mpfs_syscon_config *) device_get_match_data(dev);
+ if (!of_data) {
+ ret = dev_err_probe(dev, -EINVAL, "Error getting match data\n");
+ goto out_free_channel;
+ }
+
+ for (i = 0; i < of_data->nb_subdevs; i++) {
+ of_data->subdevs[i].dev.parent = dev;
+ if (platform_device_register(&of_data->subdevs[i]))
+ dev_warn(dev, "Error registering sub device %s\n",
+ of_data->subdevs[i].name);
+ }
+
+ dev_info(&pdev->dev, "Registered MPFS system controller\n");
+
+ return 0;
+
+out_free_channel:
+ mbox_free_channel(sys_controller->chan);
+out_free:
+ kfree(sys_controller);
+ return ret;
+}
+
+static void mpfs_sys_controller_remove(struct platform_device *pdev)
+{
+ struct mpfs_sys_controller *sys_controller = platform_get_drvdata(pdev);
+
+ mpfs_sys_controller_put(sys_controller);
+}
+
+static struct platform_device mpfs_subdevs[] = {
+ {
+ .name = "mpfs-rng",
+ .id = -1,
+ },
+ {
+ .name = "mpfs-generic-service",
+ .id = -1,
+ },
+ {
+ .name = "mpfs-auto-update",
+ .id = -1,
+ },
+};
+
+static struct platform_device pic64gx_subdevs[] = {
+ {
+ .name = "mpfs-rng",
+ .id = -1,
+ },
+ {
+ .name = "mpfs-generic-service",
+ .id = -1,
+ },
+};
+
+static const struct mpfs_syscon_config mpfs_config = {
+ .nb_subdevs = ARRAY_SIZE(mpfs_subdevs),
+ .subdevs = mpfs_subdevs,
+};
+
+static const struct mpfs_syscon_config pic64gx_config = {
+ .nb_subdevs = ARRAY_SIZE(pic64gx_subdevs),
+ .subdevs = pic64gx_subdevs,
+};
+
+static const struct of_device_id mpfs_sys_controller_of_match[] = {
+ {.compatible = "microchip,mpfs-sys-controller", .data = &mpfs_config},
+ {.compatible = "microchip,pic64gx-sys-controller", .data = &pic64gx_config},
+ {},
+};
+MODULE_DEVICE_TABLE(of, mpfs_sys_controller_of_match);
+
+struct mpfs_sys_controller *mpfs_sys_controller_get(struct device *dev)
+{
+ const struct of_device_id *match;
+ struct mpfs_sys_controller *sys_controller;
+ int ret;
+
+ if (!dev->parent)
+ goto err_no_device;
+
+ match = of_match_node(mpfs_sys_controller_of_match, dev->parent->of_node);
+ of_node_put(dev->parent->of_node);
+ if (!match)
+ goto err_no_device;
+
+ sys_controller = dev_get_drvdata(dev->parent);
+ if (!sys_controller)
+ goto err_bad_device;
+
+ if (!kref_get_unless_zero(&sys_controller->consumers))
+ goto err_bad_device;
+
+ ret = devm_add_action_or_reset(dev, mpfs_sys_controller_put, sys_controller);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return sys_controller;
+
+err_no_device:
+ dev_dbg(dev, "Parent device was not an MPFS system controller\n");
+ return ERR_PTR(-ENODEV);
+
+err_bad_device:
+ dev_dbg(dev, "MPFS system controller found but could not register as a sub device\n");
+ return ERR_PTR(-EPROBE_DEFER);
+}
+EXPORT_SYMBOL(mpfs_sys_controller_get);
+
+static struct platform_driver mpfs_sys_controller_driver = {
+ .driver = {
+ .name = "mpfs-sys-controller",
+ .of_match_table = mpfs_sys_controller_of_match,
+ },
+ .probe = mpfs_sys_controller_probe,
+ .remove = mpfs_sys_controller_remove,
+};
+module_platform_driver(mpfs_sys_controller_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
+MODULE_DESCRIPTION("MPFS system controller driver");