1From f9881d01669cd98e6f897214f407dce8a245bdfe Mon Sep 17 00:00:00 2001
2From: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
3Date: Mon, 19 Feb 2024 16:01:28 +0000
4Subject: [PATCH 1/6] remoteproc: Add Arm remoteproc driver
5
6introduce remoteproc support for Arm remote processors
7
8The supported remote processors are those that come with a reset
9control register and a reset status register. The driver allows to
10switch on or off the remote processor.
11
12The current use case is Corstone-1000 External System (Cortex-M3).
13
14The driver can be extended to support other remote processors
15controlled with a reset control and a reset status registers.
16
17The driver also supports control of multiple remote processors at the
18same time.
19
20Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
21Upstream-Status: Denied [Agreement reached: https://lore.kernel.org/all/20241009094635.GA14639@e130802.arm.com/]
22---
23 MAINTAINERS                    |   6 +
24 drivers/remoteproc/Kconfig     |  18 ++
25 drivers/remoteproc/Makefile    |   1 +
26 drivers/remoteproc/arm_rproc.c | 395 +++++++++++++++++++++++++++++++++
27 4 files changed, 420 insertions(+)
28 create mode 100644 drivers/remoteproc/arm_rproc.c
29
30diff --git a/MAINTAINERS b/MAINTAINERS
31index 8d1052fa6a69..54d6a40feea5 100644
32--- a/MAINTAINERS
33+++ b/MAINTAINERS
34@@ -1764,6 +1764,12 @@ S:	Maintained
35 F:	Documentation/devicetree/bindings/interrupt-controller/arm,vic.yaml
36 F:	drivers/irqchip/irq-vic.c
37
38+ARM REMOTEPROC DRIVER
39+M:	Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
40+L:	linux-remoteproc@vger.kernel.org
41+S:	Maintained
42+F:	drivers/remoteproc/arm_rproc.c
43+
44 ARM SMC WATCHDOG DRIVER
45 M:	Julius Werner <jwerner@chromium.org>
46 R:	Evan Benn <evanbenn@chromium.org>
47diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
48index 48845dc8fa85..57fbac454a5d 100644
49--- a/drivers/remoteproc/Kconfig
50+++ b/drivers/remoteproc/Kconfig
51@@ -365,6 +365,24 @@ config XLNX_R5_REMOTEPROC
52
53 	  It's safe to say N if not interested in using RPU r5f cores.
54
55+config ARM_REMOTEPROC
56+	tristate "Arm remoteproc support"
57+	depends on HAS_IOMEM && ARM64
58+	default n
59+	help
60+	  Say y here to support Arm remote processors via the remote
61+	  processor framework.
62+
63+	  The supported processors are those that come with a reset control register
64+	  and a reset status register. The design can be extended to support different
65+	  processors meeting these requirements.
66+	  The driver also supports control of multiple remote cores at the same time.
67+
68+	  Supported remote cores:
69+	      Corstone-1000 External System (Cortex-M3)
70+
71+	  It's safe to say N here.
72+
73 endif # REMOTEPROC
74
75 endmenu
76diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
77index 91314a9b43ce..73126310835b 100644
78--- a/drivers/remoteproc/Makefile
79+++ b/drivers/remoteproc/Makefile
80@@ -39,3 +39,4 @@ obj-$(CONFIG_STM32_RPROC)		+= stm32_rproc.o
81 obj-$(CONFIG_TI_K3_DSP_REMOTEPROC)	+= ti_k3_dsp_remoteproc.o
82 obj-$(CONFIG_TI_K3_R5_REMOTEPROC)	+= ti_k3_r5_remoteproc.o
83 obj-$(CONFIG_XLNX_R5_REMOTEPROC)	+= xlnx_r5_remoteproc.o
84+obj-$(CONFIG_ARM_REMOTEPROC)		+= arm_rproc.o
85diff --git a/drivers/remoteproc/arm_rproc.c b/drivers/remoteproc/arm_rproc.c
86new file mode 100644
87index 000000000000..6afa78ae7ad3
88--- /dev/null
89+++ b/drivers/remoteproc/arm_rproc.c
90@@ -0,0 +1,395 @@
91+// SPDX-License-Identifier: GPL-2.0-only
92+/*
93+ * Copyright 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
94+ *
95+ * Authors:
96+ *   Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
97+ */
98+
99+#include <linux/delay.h>
100+#include <linux/err.h>
101+#include <linux/firmware.h>
102+#include <linux/kernel.h>
103+#include <linux/module.h>
104+#include <linux/of.h>
105+#include <linux/platform_device.h>
106+#include <linux/remoteproc.h>
107+
108+#include "remoteproc_internal.h"
109+
110+/**
111+ * struct arm_rproc_reset_cfg - remote processor reset configuration
112+ * @ctrl_reg: address of the control register
113+ * @state_reg: address of the reset status register
114+ */
115+struct arm_rproc_reset_cfg {
116+	void __iomem *ctrl_reg;
117+	void __iomem *state_reg;
118+};
119+
120+struct arm_rproc;
121+
122+/**
123+ * struct arm_rproc_dcfg - Arm remote processor configuration
124+ * @stop: stop callback function
125+ * @start: start callback function
126+ */
127+struct arm_rproc_dcfg {
128+	int (*stop)(struct rproc *rproc);
129+	int (*start)(struct rproc *rproc);
130+};
131+
132+/**
133+ * struct arm_rproc - Arm remote processor instance
134+ * @rproc: rproc handler
135+ * @core_dcfg: device configuration pointer
136+ * @reset_cfg: reset configuration registers
137+ */
138+struct arm_rproc {
139+	struct rproc				*rproc;
140+	const struct arm_rproc_dcfg		*core_dcfg;
141+	struct arm_rproc_reset_cfg		reset_cfg;
142+};
143+
144+/* Definitions for Arm Corstone-1000 External System */
145+
146+#define EXTSYS_RST_CTRL_CPUWAIT			BIT(0)
147+#define EXTSYS_RST_CTRL_RST_REQ			BIT(1)
148+
149+#define EXTSYS_RST_ACK_MASK				GENMASK(2, 1)
150+#define EXTSYS_RST_ST_RST_ACK(x)			\
151+				((u8)(FIELD_GET(EXTSYS_RST_ACK_MASK, (x))))
152+
153+#define EXTSYS_RST_ACK_NO_RESET_REQ			(0x0)
154+#define EXTSYS_RST_ACK_NOT_COMPLETE			(0x1)
155+#define EXTSYS_RST_ACK_COMPLETE			(0x2)
156+#define EXTSYS_RST_ACK_RESERVED			(0x3)
157+
158+#define EXTSYS_RST_ACK_POLL_TRIES			(3)
159+#define EXTSYS_RST_ACK_POLL_TIMEOUT			(1000)
160+
161+/**
162+ * arm_rproc_start_cs1000_extsys() - custom start function
163+ * @rproc: pointer to the remote processor object
164+ *
165+ * Start function for Corstone-1000 External System.
166+ * Allow the External System core start execute instructions.
167+ *
168+ * Return:
169+ *
170+ * 0 on success. Otherwise, failure
171+ */
172+static int arm_rproc_start_cs1000_extsys(struct rproc *rproc)
173+{
174+	struct arm_rproc *priv = rproc->priv;
175+	u32 ctrl_reg;
176+
177+	/* CPUWAIT signal of the External System is de-asserted */
178+	ctrl_reg = readl(priv->reset_cfg.ctrl_reg);
179+	ctrl_reg &= ~EXTSYS_RST_CTRL_CPUWAIT;
180+	writel(ctrl_reg, priv->reset_cfg.ctrl_reg);
181+
182+	return 0;
183+}
184+
185+/**
186+ * arm_rproc_cs1000_extsys_poll_rst_ack() - poll RST_ACK bits
187+ * @rproc: pointer to the remote processor object
188+ * @exp_ack: expected bits value
189+ * @rst_ack: bits value read
190+ *
191+ * Tries to read RST_ACK bits until the timeout expires.
192+ * EXTSYS_RST_ACK_POLL_TRIES tries are made,
193+ * every EXTSYS_RST_ACK_POLL_TIMEOUT milliseconds.
194+ *
195+ * Return:
196+ *
197+ * 0 on success. Otherwise, failure
198+ */
199+static int arm_rproc_cs1000_extsys_poll_rst_ack(struct rproc *rproc,
200+						u8 exp_ack, u8 *rst_ack)
201+{
202+	struct arm_rproc *priv = rproc->priv;
203+	struct device *dev = rproc->dev.parent;
204+	u32 state_reg;
205+	int tries = EXTSYS_RST_ACK_POLL_TRIES;
206+	unsigned long timeout;
207+
208+	do {
209+		state_reg = readl(priv->reset_cfg.state_reg);
210+		*rst_ack = EXTSYS_RST_ST_RST_ACK(state_reg);
211+
212+		if (*rst_ack == EXTSYS_RST_ACK_RESERVED) {
213+			dev_err(dev, "unexpected RST_ACK value: 0x%x\n",
214+				*rst_ack);
215+			return -EINVAL;
216+		}
217+
218+		/* expected ACK value read */
219+		if ((*rst_ack & exp_ack) || (*rst_ack == exp_ack))
220+			return 0;
221+
222+		timeout = msleep_interruptible(EXTSYS_RST_ACK_POLL_TIMEOUT);
223+
224+		if (timeout) {
225+			dev_err(dev, "polling RST_ACK  aborted\n");
226+			return -ECONNABORTED;
227+		}
228+	} while (--tries);
229+
230+	dev_err(dev, "polling RST_ACK timed out\n");
231+
232+	return -ETIMEDOUT;
233+}
234+
235+/**
236+ * arm_rproc_stop_cs1000_extsys() - custom stop function
237+ * @rproc: pointer to the remote processor object
238+ *
239+ * Reset all logic within the External System, the core will be in a halt state.
240+ *
241+ * Return:
242+ *
243+ * 0 on success. Otherwise, failure
244+ */
245+static int arm_rproc_stop_cs1000_extsys(struct rproc *rproc)
246+{
247+	struct arm_rproc *priv = rproc->priv;
248+	struct device *dev = rproc->dev.parent;
249+	u32 ctrl_reg;
250+	u8 rst_ack, req_status;
251+	int ret;
252+
253+	ctrl_reg = readl(priv->reset_cfg.ctrl_reg);
254+	ctrl_reg |= EXTSYS_RST_CTRL_RST_REQ;
255+	writel(ctrl_reg, priv->reset_cfg.ctrl_reg);
256+
257+	ret = arm_rproc_cs1000_extsys_poll_rst_ack(rproc,
258+						   EXTSYS_RST_ACK_COMPLETE |
259+						   EXTSYS_RST_ACK_NOT_COMPLETE,
260+						   &rst_ack);
261+	if (ret)
262+		return ret;
263+
264+	req_status = rst_ack;
265+
266+	ctrl_reg = readl(priv->reset_cfg.ctrl_reg);
267+	ctrl_reg &= ~EXTSYS_RST_CTRL_RST_REQ;
268+	writel(ctrl_reg, priv->reset_cfg.ctrl_reg);
269+
270+	ret = arm_rproc_cs1000_extsys_poll_rst_ack(rproc, 0, &rst_ack);
271+	if (ret)
272+		return ret;
273+
274+	if (req_status == EXTSYS_RST_ACK_COMPLETE) {
275+		dev_dbg(dev, "the requested reset has been accepted\n");
276+		return 0;
277+	}
278+
279+	dev_err(dev, "the requested reset has been denied\n");
280+	return -EACCES;
281+}
282+
283+static const struct arm_rproc_dcfg arm_rproc_cfg_corstone1000_extsys = {
284+	.stop          = arm_rproc_stop_cs1000_extsys,
285+	.start         = arm_rproc_start_cs1000_extsys,
286+};
287+
288+/**
289+ * arm_rproc_stop() - Stop function for rproc_ops
290+ * @rproc: pointer to the remote processor object
291+ *
292+ * Calls the stop() callback of the remote core
293+ *
294+ * Return:
295+ *
296+ * 0 on success. Otherwise, failure
297+ */
298+static int arm_rproc_stop(struct rproc *rproc)
299+{
300+	struct arm_rproc *priv = rproc->priv;
301+
302+	return priv->core_dcfg->stop(rproc);
303+}
304+
305+/**
306+ * arm_rproc_start() - Start function for rproc_ops
307+ * @rproc: pointer to the remote processor object
308+ *
309+ * Calls the start() callback of the remote core
310+ *
311+ * Return:
312+ *
313+ * 0 on success. Otherwise, failure
314+ */
315+static int arm_rproc_start(struct rproc *rproc)
316+{
317+	struct arm_rproc *priv = rproc->priv;
318+
319+	return priv->core_dcfg->start(rproc);
320+}
321+
322+/**
323+ * arm_rproc_parse_fw() - Parse firmware function for rproc_ops
324+ * @rproc: pointer to the remote processor object
325+ * @fw: pointer to the firmware
326+ *
327+ * Does nothing currently.
328+ *
329+ * Return:
330+ *
331+ * 0 for success.
332+ */
333+static int arm_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
334+{
335+	return 0;
336+}
337+
338+/**
339+ * arm_rproc_load() - Load firmware to memory function for rproc_ops
340+ * @rproc: pointer to the remote processor object
341+ * @fw: pointer to the firmware
342+ *
343+ * Does nothing currently.
344+ *
345+ * Return:
346+ *
347+ * 0 for success.
348+ */
349+static int arm_rproc_load(struct rproc *rproc, const struct firmware *fw)
350+{
351+	return 0;
352+}
353+
354+static const struct rproc_ops arm_rproc_ops = {
355+	.start		= arm_rproc_start,
356+	.stop		= arm_rproc_stop,
357+	.load		= arm_rproc_load,
358+	.parse_fw	= arm_rproc_parse_fw,
359+};
360+
361+/**
362+ * arm_rproc_probe() - the platform device probe
363+ * @pdev: the platform device
364+ *
365+ * Read from the device tree the properties needed to setup
366+ * the reset and comms for the remote processor.
367+ * Also, allocate a rproc device and register it with the remoteproc subsystem.
368+ *
369+ * Return:
370+ *
371+ * 0 on success. Otherwise, failure
372+ */
373+static int arm_rproc_probe(struct platform_device *pdev)
374+{
375+	const struct arm_rproc_dcfg *core_dcfg;
376+	struct device *dev = &pdev->dev;
377+	struct device_node *np = dev->of_node;
378+	struct arm_rproc *priv;
379+	struct rproc *rproc;
380+	const char *fw_name;
381+	int ret;
382+	struct resource *res;
383+
384+	core_dcfg = of_device_get_match_data(dev);
385+	if (!core_dcfg)
386+		return -ENODEV;
387+
388+	ret = rproc_of_parse_firmware(dev, 0, &fw_name);
389+	if (ret) {
390+		dev_err(dev,
391+			"can't parse firmware-name from device tree (%pe)\n",
392+			ERR_PTR(ret));
393+		return ret;
394+	}
395+
396+	dev_dbg(dev, "firmware-name: %s\n", fw_name);
397+
398+	rproc = rproc_alloc(dev, np->name, &arm_rproc_ops, fw_name,
399+			    sizeof(*priv));
400+	if (!rproc)
401+		return -ENOMEM;
402+
403+	priv = rproc->priv;
404+	priv->rproc = rproc;
405+	priv->core_dcfg = core_dcfg;
406+
407+	res = platform_get_resource_byname(pdev,
408+					   IORESOURCE_MEM, "reset-control");
409+	priv->reset_cfg.ctrl_reg = devm_ioremap_resource(&pdev->dev, res);
410+	if (IS_ERR(priv->reset_cfg.ctrl_reg)) {
411+		ret = PTR_ERR(priv->reset_cfg.ctrl_reg);
412+		dev_err(dev,
413+			"can't map the reset-control register (%pe)\n",
414+			ERR_PTR((unsigned long)priv->reset_cfg.ctrl_reg));
415+		goto err_free_rproc;
416+	} else {
417+		dev_dbg(dev, "reset-control: %p\n", priv->reset_cfg.ctrl_reg);
418+	}
419+
420+	res = platform_get_resource_byname(pdev,
421+					   IORESOURCE_MEM, "reset-status");
422+	priv->reset_cfg.state_reg = devm_ioremap_resource(&pdev->dev, res);
423+	if (IS_ERR(priv->reset_cfg.state_reg)) {
424+		ret = PTR_ERR(priv->reset_cfg.state_reg);
425+		dev_err(dev,
426+			"can't map the reset-status register (%pe)\n",
427+			ERR_PTR((unsigned long)priv->reset_cfg.state_reg));
428+		goto err_free_rproc;
429+	} else {
430+		dev_dbg(dev, "reset-status: %p\n",
431+			priv->reset_cfg.state_reg);
432+	}
433+
434+	platform_set_drvdata(pdev, rproc);
435+
436+	ret = rproc_add(rproc);
437+	if (ret) {
438+		dev_err(dev, "can't add remote processor (%pe)\n",
439+			ERR_PTR(ret));
440+		goto err_free_rproc;
441+	} else {
442+		dev_dbg(dev, "remote processor added\n");
443+	}
444+
445+	return 0;
446+
447+err_free_rproc:
448+	rproc_free(rproc);
449+
450+	return ret;
451+}
452+
453+/**
454+ * arm_rproc_remove() - the platform device remove
455+ * @pdev: the platform device
456+ *
457+ * Delete and free the resources used.
458+ */
459+static void arm_rproc_remove(struct platform_device *pdev)
460+{
461+	struct rproc *rproc = platform_get_drvdata(pdev);
462+
463+	rproc_del(rproc);
464+	rproc_free(rproc);
465+}
466+
467+static const struct of_device_id arm_rproc_of_match[] = {
468+	{ .compatible = "arm,corstone1000-extsys", .data = &arm_rproc_cfg_corstone1000_extsys },
469+	{},
470+};
471+MODULE_DEVICE_TABLE(of, arm_rproc_of_match);
472+
473+static struct platform_driver arm_rproc_driver = {
474+	.probe = arm_rproc_probe,
475+	.remove_new = arm_rproc_remove,
476+	.driver = {
477+		.name = "arm-rproc",
478+		.of_match_table = arm_rproc_of_match,
479+	},
480+};
481+module_platform_driver(arm_rproc_driver);
482+
483+MODULE_LICENSE("GPL");
484+MODULE_DESCRIPTION("Arm Remote Processor Control Driver");
485+MODULE_AUTHOR("Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>");
486--
4872.25.1
488
489