xref: /linux/drivers/remoteproc/amd_mbv_bram_rproc.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * AMD MicroBlaze/V BRAM-based Remote Processor driver
4  *
5  * Copyright (C) 2026 Advanced Micro Devices, Inc.
6  *
7  * This driver supports soft-core processors (MicroBlaze, MicroBlaze-V, or
8  * similar) instantiated in AMD programmable logic, using dual-port BRAM
9  * for firmware storage and execution.
10  *
11  * The firmware memory (BRAM) is described in the processor-local address
12  * space and translated to the Linux-visible system physical address with
13  * standard devicetree address translation.
14  *
15  * Reset is controlled via GPIO connected to Processor System Reset IP.
16  */
17 
18 #include <linux/clk.h>
19 #include <linux/dma-mapping.h>
20 #include <linux/gpio/consumer.h>
21 #include <linux/module.h>
22 #include <linux/of.h>
23 #include <linux/of_address.h>
24 #include <linux/platform_device.h>
25 #include <linux/remoteproc.h>
26 
27 #include "remoteproc_internal.h"
28 
29 /**
30  * struct amd_bram_rproc - AMD MicroBlaze/V BRAM-based remoteproc private data
31  * @dev: device pointer
32  * @reset: GPIO descriptor for reset control (active-low)
33  * @clk: processor clock
34  */
35 struct amd_bram_rproc {
36 	struct device *dev;
37 	struct gpio_desc *reset;
38 	struct clk *clk;
39 };
40 
41 static int amd_bram_rproc_prepare(struct rproc *rproc)
42 {
43 	struct amd_bram_rproc *priv = rproc->priv;
44 	struct rproc_mem_entry *mem;
45 	struct resource res;
46 	u64 da, size;
47 	int ret;
48 
49 	ret = of_property_read_reg(priv->dev->of_node, 0, &da, &size);
50 	if (ret) {
51 		dev_err(priv->dev, "failed to parse executable memory reg\n");
52 		return ret;
53 	}
54 
55 	if (!size || size > U32_MAX) {
56 		dev_err(priv->dev, "invalid executable memory size\n");
57 		return -EINVAL;
58 	}
59 
60 	if (da > U32_MAX) {
61 		dev_err(priv->dev, "invalid executable memory address\n");
62 		return -EINVAL;
63 	}
64 
65 	ret = of_address_to_resource(priv->dev->of_node, 0, &res);
66 	if (ret) {
67 		dev_err(priv->dev, "failed to translate executable memory reg\n");
68 		return ret;
69 	}
70 
71 	mem = rproc_mem_entry_init(priv->dev, NULL, (dma_addr_t)res.start,
72 				   resource_size(&res), da,
73 				   rproc_mem_entry_ioremap_wc,
74 				   rproc_mem_entry_iounmap,
75 				   dev_name(priv->dev));
76 	if (!mem)
77 		return -ENOMEM;
78 
79 	rproc_add_carveout(rproc, mem);
80 	rproc_coredump_add_segment(rproc, da, resource_size(&res));
81 
82 	return 0;
83 }
84 
85 static int amd_bram_rproc_start(struct rproc *rproc)
86 {
87 	struct amd_bram_rproc *priv = rproc->priv;
88 	int ret;
89 
90 	/* Enable clock before releasing reset */
91 	ret = clk_prepare_enable(priv->clk);
92 	if (ret) {
93 		dev_err(priv->dev, "failed to enable clock: %d\n", ret);
94 		return ret;
95 	}
96 
97 	/* Deassert reset and let the processor run. */
98 	ret = gpiod_set_value_cansleep(priv->reset, 0);
99 	if (ret) {
100 		dev_err(priv->dev, "failed to deassert reset: %d\n", ret);
101 		clk_disable_unprepare(priv->clk);
102 		return ret;
103 	}
104 
105 	return 0;
106 }
107 
108 static int amd_bram_rproc_stop(struct rproc *rproc)
109 {
110 	struct amd_bram_rproc *priv = rproc->priv;
111 	int ret;
112 
113 	/* Assert reset before disabling the processor clock. */
114 	ret = gpiod_set_value_cansleep(priv->reset, 1);
115 	if (ret) {
116 		dev_err(priv->dev, "failed to assert reset: %d\n", ret);
117 		return ret;
118 	}
119 
120 	/* Disable clock after asserting reset */
121 	clk_disable_unprepare(priv->clk);
122 
123 	return 0;
124 }
125 
126 static int amd_bram_rproc_parse_fw(struct rproc *rproc,
127 				   const struct firmware *fw)
128 {
129 	rproc_elf_load_rsc_table_optional(rproc, fw, dev_dbg,
130 					  "no resource table found\n");
131 	return 0;
132 }
133 
134 static const struct rproc_ops amd_bram_rproc_ops = {
135 	.prepare	= amd_bram_rproc_prepare,
136 	.start		= amd_bram_rproc_start,
137 	.stop		= amd_bram_rproc_stop,
138 	.load		= rproc_elf_load_segments,
139 	.sanity_check	= rproc_elf_sanity_check,
140 	.get_boot_addr	= rproc_elf_get_boot_addr,
141 	.parse_fw	= amd_bram_rproc_parse_fw,
142 };
143 
144 static int amd_bram_rproc_probe(struct platform_device *pdev)
145 {
146 	struct device *dev = &pdev->dev;
147 	struct amd_bram_rproc *priv;
148 	const char *fw_name = NULL;
149 	struct rproc *rproc;
150 	int ret;
151 
152 	ret = rproc_of_parse_firmware(dev, 0, &fw_name);
153 	if (ret < 0 && ret != -EINVAL)
154 		return dev_err_probe(dev, ret,
155 				     "failed to parse firmware-name property\n");
156 
157 	rproc = devm_rproc_alloc(dev, dev_name(dev), &amd_bram_rproc_ops,
158 				 fw_name, sizeof(*priv));
159 	if (!rproc)
160 		return -ENOMEM;
161 
162 	priv = rproc->priv;
163 	priv->dev = dev;
164 
165 	/* Get the processor clock */
166 	priv->clk = devm_clk_get(dev, NULL);
167 	if (IS_ERR(priv->clk))
168 		return dev_err_probe(dev, PTR_ERR(priv->clk),
169 				     "failed to get clock\n");
170 
171 	/*
172 	 * Keep the processor in reset until remoteproc has finished loading
173 	 * firmware into the executable memory window described by reg and
174 	 * translated through the parent bus ranges property.
175 	 */
176 	priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
177 	if (IS_ERR(priv->reset))
178 		return dev_err_probe(dev, PTR_ERR(priv->reset),
179 				     "failed to get reset gpio\n");
180 
181 	rproc->auto_boot = false;
182 
183 	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
184 	if (ret)
185 		return dev_err_probe(dev, ret, "failed to set DMA mask\n");
186 
187 	platform_set_drvdata(pdev, rproc);
188 
189 	ret = devm_rproc_add(dev, rproc);
190 	if (ret)
191 		return dev_err_probe(dev, ret, "failed to register rproc\n");
192 
193 	return 0;
194 }
195 
196 static const struct of_device_id amd_bram_rproc_of_match[] = {
197 	{ .compatible = "xlnx,zynqmp-bram-rproc" },
198 	{ /* sentinel */ },
199 };
200 MODULE_DEVICE_TABLE(of, amd_bram_rproc_of_match);
201 
202 static struct platform_driver amd_bram_rproc_driver = {
203 	.probe = amd_bram_rproc_probe,
204 	.driver = {
205 		.name = "amd-bram-rproc",
206 		.of_match_table = amd_bram_rproc_of_match,
207 	},
208 };
209 module_platform_driver(amd_bram_rproc_driver);
210 
211 MODULE_DESCRIPTION("AMD MicroBlaze/V BRAM-based Remote Processor driver");
212 MODULE_AUTHOR("Ben Levinsky <ben.levinsky@amd.com>");
213 MODULE_LICENSE("GPL");
214