1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com> |
4 | */ |
5 | |
6 | #include <linux/bitfield.h> |
7 | #include <linux/bitops.h> |
8 | #include <linux/clk.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/genalloc.h> |
11 | #include <linux/io.h> |
12 | #include <linux/mfd/syscon.h> |
13 | #include <linux/module.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/property.h> |
16 | #include <linux/regmap.h> |
17 | #include <linux/remoteproc.h> |
18 | #include <linux/reset.h> |
19 | #include <linux/sizes.h> |
20 | |
21 | #include "remoteproc_internal.h" |
22 | |
23 | #define AO_REMAP_REG0 0x0 |
24 | #define AO_REMAP_REG0_REMAP_AHB_SRAM_BITS_17_14_FOR_ARM_CPU GENMASK(3, 0) |
25 | |
26 | #define AO_REMAP_REG1 0x4 |
27 | #define AO_REMAP_REG1_MOVE_AHB_SRAM_TO_0X0_INSTEAD_OF_DDR BIT(4) |
28 | #define AO_REMAP_REG1_REMAP_AHB_SRAM_BITS_17_14_FOR_MEDIA_CPU GENMASK(3, 0) |
29 | |
30 | #define AO_CPU_CNTL 0x0 |
31 | #define AO_CPU_CNTL_AHB_SRAM_BITS_31_20 GENMASK(28, 16) |
32 | #define AO_CPU_CNTL_HALT BIT(9) |
33 | #define AO_CPU_CNTL_UNKNONWN BIT(8) |
34 | #define AO_CPU_CNTL_RUN BIT(0) |
35 | |
36 | #define AO_CPU_STAT 0x4 |
37 | |
38 | #define AO_SECURE_REG0 0x0 |
39 | #define AO_SECURE_REG0_AHB_SRAM_BITS_19_12 GENMASK(15, 8) |
40 | |
41 | /* Only bits [31:20] and [17:14] are usable, all other bits must be zero */ |
42 | #define MESON_AO_RPROC_SRAM_USABLE_BITS 0xfff3c000ULL |
43 | |
44 | #define MESON_AO_RPROC_MEMORY_OFFSET 0x10000000 |
45 | |
46 | struct meson_mx_ao_arc_rproc_priv { |
47 | void __iomem *remap_base; |
48 | void __iomem *cpu_base; |
49 | unsigned long sram_va; |
50 | phys_addr_t sram_pa; |
51 | size_t sram_size; |
52 | struct gen_pool *sram_pool; |
53 | struct reset_control *arc_reset; |
54 | struct clk *arc_pclk; |
55 | struct regmap *secbus2_regmap; |
56 | }; |
57 | |
58 | static int meson_mx_ao_arc_rproc_start(struct rproc *rproc) |
59 | { |
60 | struct meson_mx_ao_arc_rproc_priv *priv = rproc->priv; |
61 | phys_addr_t translated_sram_addr; |
62 | u32 tmp; |
63 | int ret; |
64 | |
65 | ret = clk_prepare_enable(clk: priv->arc_pclk); |
66 | if (ret) |
67 | return ret; |
68 | |
69 | tmp = FIELD_PREP(AO_REMAP_REG0_REMAP_AHB_SRAM_BITS_17_14_FOR_ARM_CPU, |
70 | priv->sram_pa >> 14); |
71 | writel(val: tmp, addr: priv->remap_base + AO_REMAP_REG0); |
72 | |
73 | /* |
74 | * The SRAM content as seen by the ARC core always starts at 0x0 |
75 | * regardless of the value given here (this was discovered by trial and |
76 | * error). For SoCs older than Meson6 we probably have to set |
77 | * AO_REMAP_REG1_MOVE_AHB_SRAM_TO_0X0_INSTEAD_OF_DDR to achieve the |
78 | * same. (At least) For Meson8 and newer that bit must not be set. |
79 | */ |
80 | writel(val: 0x0, addr: priv->remap_base + AO_REMAP_REG1); |
81 | |
82 | regmap_update_bits(map: priv->secbus2_regmap, AO_SECURE_REG0, |
83 | AO_SECURE_REG0_AHB_SRAM_BITS_19_12, |
84 | FIELD_PREP(AO_SECURE_REG0_AHB_SRAM_BITS_19_12, |
85 | priv->sram_pa >> 12)); |
86 | |
87 | ret = reset_control_reset(rstc: priv->arc_reset); |
88 | if (ret) { |
89 | clk_disable_unprepare(clk: priv->arc_pclk); |
90 | return ret; |
91 | } |
92 | |
93 | usleep_range(min: 10, max: 100); |
94 | |
95 | /* |
96 | * Convert from 0xd9000000 to 0xc9000000 as the vendor driver does. |
97 | * This only seems to be relevant for the AO_CPU_CNTL register. It is |
98 | * unknown why this is needed. |
99 | */ |
100 | translated_sram_addr = priv->sram_pa - MESON_AO_RPROC_MEMORY_OFFSET; |
101 | |
102 | tmp = FIELD_PREP(AO_CPU_CNTL_AHB_SRAM_BITS_31_20, |
103 | translated_sram_addr >> 20); |
104 | tmp |= AO_CPU_CNTL_UNKNONWN | AO_CPU_CNTL_RUN; |
105 | writel(val: tmp, addr: priv->cpu_base + AO_CPU_CNTL); |
106 | |
107 | usleep_range(min: 20, max: 200); |
108 | |
109 | return 0; |
110 | } |
111 | |
112 | static int meson_mx_ao_arc_rproc_stop(struct rproc *rproc) |
113 | { |
114 | struct meson_mx_ao_arc_rproc_priv *priv = rproc->priv; |
115 | |
116 | writel(AO_CPU_CNTL_HALT, addr: priv->cpu_base + AO_CPU_CNTL); |
117 | |
118 | clk_disable_unprepare(clk: priv->arc_pclk); |
119 | |
120 | return 0; |
121 | } |
122 | |
123 | static void *meson_mx_ao_arc_rproc_da_to_va(struct rproc *rproc, u64 da, |
124 | size_t len, bool *is_iomem) |
125 | { |
126 | struct meson_mx_ao_arc_rproc_priv *priv = rproc->priv; |
127 | |
128 | /* The memory from the ARC core's perspective always starts at 0x0. */ |
129 | if ((da + len) > priv->sram_size) |
130 | return NULL; |
131 | |
132 | return (void *)priv->sram_va + da; |
133 | } |
134 | |
135 | static struct rproc_ops meson_mx_ao_arc_rproc_ops = { |
136 | .start = meson_mx_ao_arc_rproc_start, |
137 | .stop = meson_mx_ao_arc_rproc_stop, |
138 | .da_to_va = meson_mx_ao_arc_rproc_da_to_va, |
139 | .get_boot_addr = rproc_elf_get_boot_addr, |
140 | .load = rproc_elf_load_segments, |
141 | .sanity_check = rproc_elf_sanity_check, |
142 | }; |
143 | |
144 | static int meson_mx_ao_arc_rproc_probe(struct platform_device *pdev) |
145 | { |
146 | struct meson_mx_ao_arc_rproc_priv *priv; |
147 | struct device *dev = &pdev->dev; |
148 | const char *fw_name = NULL; |
149 | struct rproc *rproc; |
150 | int ret; |
151 | |
152 | device_property_read_string(dev, propname: "firmware-name" , val: &fw_name); |
153 | |
154 | rproc = devm_rproc_alloc(dev, name: "meson-mx-ao-arc" , |
155 | ops: &meson_mx_ao_arc_rproc_ops, firmware: fw_name, |
156 | len: sizeof(*priv)); |
157 | if (!rproc) |
158 | return -ENOMEM; |
159 | |
160 | rproc->has_iommu = false; |
161 | priv = rproc->priv; |
162 | |
163 | priv->sram_pool = of_gen_pool_get(np: dev->of_node, propname: "sram" , index: 0); |
164 | if (!priv->sram_pool) { |
165 | dev_err(dev, "Could not get SRAM pool\n" ); |
166 | return -ENODEV; |
167 | } |
168 | |
169 | priv->sram_size = gen_pool_avail(priv->sram_pool); |
170 | |
171 | priv->sram_va = gen_pool_alloc(pool: priv->sram_pool, size: priv->sram_size); |
172 | if (!priv->sram_va) { |
173 | dev_err(dev, "Could not alloc memory in SRAM pool\n" ); |
174 | return -ENOMEM; |
175 | } |
176 | |
177 | priv->sram_pa = gen_pool_virt_to_phys(pool: priv->sram_pool, priv->sram_va); |
178 | if (priv->sram_pa & ~MESON_AO_RPROC_SRAM_USABLE_BITS) { |
179 | dev_err(dev, "SRAM address contains unusable bits\n" ); |
180 | ret = -EINVAL; |
181 | goto err_free_genpool; |
182 | } |
183 | |
184 | priv->secbus2_regmap = syscon_regmap_lookup_by_phandle(np: dev->of_node, |
185 | property: "amlogic,secbus2" ); |
186 | if (IS_ERR(ptr: priv->secbus2_regmap)) { |
187 | dev_err(dev, "Failed to find SECBUS2 regmap\n" ); |
188 | ret = PTR_ERR(ptr: priv->secbus2_regmap); |
189 | goto err_free_genpool; |
190 | } |
191 | |
192 | priv->remap_base = devm_platform_ioremap_resource_byname(pdev, name: "remap" ); |
193 | if (IS_ERR(ptr: priv->remap_base)) { |
194 | ret = PTR_ERR(ptr: priv->remap_base); |
195 | goto err_free_genpool; |
196 | } |
197 | |
198 | priv->cpu_base = devm_platform_ioremap_resource_byname(pdev, name: "cpu" ); |
199 | if (IS_ERR(ptr: priv->cpu_base)) { |
200 | ret = PTR_ERR(ptr: priv->cpu_base); |
201 | goto err_free_genpool; |
202 | } |
203 | |
204 | priv->arc_reset = devm_reset_control_get_exclusive(dev, NULL); |
205 | if (IS_ERR(ptr: priv->arc_reset)) { |
206 | dev_err(dev, "Failed to get ARC reset\n" ); |
207 | ret = PTR_ERR(ptr: priv->arc_reset); |
208 | goto err_free_genpool; |
209 | } |
210 | |
211 | priv->arc_pclk = devm_clk_get(dev, NULL); |
212 | if (IS_ERR(ptr: priv->arc_pclk)) { |
213 | dev_err(dev, "Failed to get the ARC PCLK\n" ); |
214 | ret = PTR_ERR(ptr: priv->arc_pclk); |
215 | goto err_free_genpool; |
216 | } |
217 | |
218 | platform_set_drvdata(pdev, data: rproc); |
219 | |
220 | ret = rproc_add(rproc); |
221 | if (ret) |
222 | goto err_free_genpool; |
223 | |
224 | return 0; |
225 | |
226 | err_free_genpool: |
227 | gen_pool_free(pool: priv->sram_pool, addr: priv->sram_va, size: priv->sram_size); |
228 | return ret; |
229 | } |
230 | |
231 | static void meson_mx_ao_arc_rproc_remove(struct platform_device *pdev) |
232 | { |
233 | struct rproc *rproc = platform_get_drvdata(pdev); |
234 | struct meson_mx_ao_arc_rproc_priv *priv = rproc->priv; |
235 | |
236 | rproc_del(rproc); |
237 | gen_pool_free(pool: priv->sram_pool, addr: priv->sram_va, size: priv->sram_size); |
238 | } |
239 | |
240 | static const struct of_device_id meson_mx_ao_arc_rproc_match[] = { |
241 | { .compatible = "amlogic,meson8-ao-arc" }, |
242 | { .compatible = "amlogic,meson8b-ao-arc" }, |
243 | { /* sentinel */ } |
244 | }; |
245 | MODULE_DEVICE_TABLE(of, meson_mx_ao_arc_rproc_match); |
246 | |
247 | static struct platform_driver meson_mx_ao_arc_rproc_driver = { |
248 | .probe = meson_mx_ao_arc_rproc_probe, |
249 | .remove_new = meson_mx_ao_arc_rproc_remove, |
250 | .driver = { |
251 | .name = "meson-mx-ao-arc-rproc" , |
252 | .of_match_table = meson_mx_ao_arc_rproc_match, |
253 | }, |
254 | }; |
255 | module_platform_driver(meson_mx_ao_arc_rproc_driver); |
256 | |
257 | MODULE_DESCRIPTION("Amlogic Meson6/8/8b/8m2 AO ARC remote processor driver" ); |
258 | MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>" ); |
259 | MODULE_LICENSE("GPL v2" ); |
260 | |