Commit | Line | Data |
---|---|---|
bb6869b2 PG |
1 | /* |
2 | * SLIM core rproc driver | |
3 | * | |
4 | * Copyright (C) 2016 STMicroelectronics | |
5 | * | |
6 | * Author: Peter Griffin <peter.griffin@linaro.org> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | */ | |
13 | ||
14 | #include <linux/clk.h> | |
15 | #include <linux/err.h> | |
16 | #include <linux/kernel.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/of.h> | |
19 | #include <linux/of_device.h> | |
20 | #include <linux/platform_device.h> | |
21 | #include <linux/remoteproc.h> | |
22 | #include <linux/remoteproc/st_slim_rproc.h> | |
23 | #include "remoteproc_internal.h" | |
24 | ||
25 | /* SLIM core registers */ | |
26 | #define SLIM_ID_OFST 0x0 | |
27 | #define SLIM_VER_OFST 0x4 | |
28 | ||
29 | #define SLIM_EN_OFST 0x8 | |
30 | #define SLIM_EN_RUN BIT(0) | |
31 | ||
32 | #define SLIM_CLK_GATE_OFST 0xC | |
33 | #define SLIM_CLK_GATE_DIS BIT(0) | |
34 | #define SLIM_CLK_GATE_RESET BIT(2) | |
35 | ||
36 | #define SLIM_SLIM_PC_OFST 0x20 | |
37 | ||
38 | /* DMEM registers */ | |
39 | #define SLIM_REV_ID_OFST 0x0 | |
40 | #define SLIM_REV_ID_MIN_MASK GENMASK(15, 8) | |
41 | #define SLIM_REV_ID_MIN(id) ((id & SLIM_REV_ID_MIN_MASK) >> 8) | |
42 | #define SLIM_REV_ID_MAJ_MASK GENMASK(23, 16) | |
43 | #define SLIM_REV_ID_MAJ(id) ((id & SLIM_REV_ID_MAJ_MASK) >> 16) | |
44 | ||
45 | ||
46 | /* peripherals registers */ | |
47 | #define SLIM_STBUS_SYNC_OFST 0xF88 | |
48 | #define SLIM_STBUS_SYNC_DIS BIT(0) | |
49 | ||
50 | #define SLIM_INT_SET_OFST 0xFD4 | |
51 | #define SLIM_INT_CLR_OFST 0xFD8 | |
52 | #define SLIM_INT_MASK_OFST 0xFDC | |
53 | ||
54 | #define SLIM_CMD_CLR_OFST 0xFC8 | |
55 | #define SLIM_CMD_MASK_OFST 0xFCC | |
56 | ||
57 | static const char *mem_names[ST_SLIM_MEM_MAX] = { | |
58 | [ST_SLIM_DMEM] = "dmem", | |
59 | [ST_SLIM_IMEM] = "imem", | |
60 | }; | |
61 | ||
62 | static int slim_clk_get(struct st_slim_rproc *slim_rproc, struct device *dev) | |
63 | { | |
64 | int clk, err; | |
65 | ||
66 | for (clk = 0; clk < ST_SLIM_MAX_CLK; clk++) { | |
67 | slim_rproc->clks[clk] = of_clk_get(dev->of_node, clk); | |
68 | if (IS_ERR(slim_rproc->clks[clk])) { | |
69 | err = PTR_ERR(slim_rproc->clks[clk]); | |
70 | if (err == -EPROBE_DEFER) | |
71 | goto err_put_clks; | |
72 | slim_rproc->clks[clk] = NULL; | |
73 | break; | |
74 | } | |
75 | } | |
76 | ||
77 | return 0; | |
78 | ||
79 | err_put_clks: | |
80 | while (--clk >= 0) | |
81 | clk_put(slim_rproc->clks[clk]); | |
82 | ||
83 | return err; | |
84 | } | |
85 | ||
86 | static void slim_clk_disable(struct st_slim_rproc *slim_rproc) | |
87 | { | |
88 | int clk; | |
89 | ||
90 | for (clk = 0; clk < ST_SLIM_MAX_CLK && slim_rproc->clks[clk]; clk++) | |
91 | clk_disable_unprepare(slim_rproc->clks[clk]); | |
92 | } | |
93 | ||
94 | static int slim_clk_enable(struct st_slim_rproc *slim_rproc) | |
95 | { | |
96 | int clk, ret; | |
97 | ||
98 | for (clk = 0; clk < ST_SLIM_MAX_CLK && slim_rproc->clks[clk]; clk++) { | |
99 | ret = clk_prepare_enable(slim_rproc->clks[clk]); | |
100 | if (ret) | |
101 | goto err_disable_clks; | |
102 | } | |
103 | ||
104 | return 0; | |
105 | ||
106 | err_disable_clks: | |
107 | while (--clk >= 0) | |
108 | clk_disable_unprepare(slim_rproc->clks[clk]); | |
109 | ||
110 | return ret; | |
111 | } | |
112 | ||
113 | /* | |
114 | * Remoteproc slim specific device handlers | |
115 | */ | |
116 | static int slim_rproc_start(struct rproc *rproc) | |
117 | { | |
118 | struct device *dev = &rproc->dev; | |
119 | struct st_slim_rproc *slim_rproc = rproc->priv; | |
120 | unsigned long hw_id, hw_ver, fw_rev; | |
121 | u32 val; | |
122 | ||
123 | /* disable CPU pipeline clock & reset CPU pipeline */ | |
124 | val = SLIM_CLK_GATE_DIS | SLIM_CLK_GATE_RESET; | |
125 | writel(val, slim_rproc->slimcore + SLIM_CLK_GATE_OFST); | |
126 | ||
127 | /* disable SLIM core STBus sync */ | |
128 | writel(SLIM_STBUS_SYNC_DIS, slim_rproc->peri + SLIM_STBUS_SYNC_OFST); | |
129 | ||
130 | /* enable cpu pipeline clock */ | |
131 | writel(!SLIM_CLK_GATE_DIS, | |
132 | slim_rproc->slimcore + SLIM_CLK_GATE_OFST); | |
133 | ||
134 | /* clear int & cmd mailbox */ | |
135 | writel(~0U, slim_rproc->peri + SLIM_INT_CLR_OFST); | |
136 | writel(~0U, slim_rproc->peri + SLIM_CMD_CLR_OFST); | |
137 | ||
138 | /* enable all channels cmd & int */ | |
139 | writel(~0U, slim_rproc->peri + SLIM_INT_MASK_OFST); | |
140 | writel(~0U, slim_rproc->peri + SLIM_CMD_MASK_OFST); | |
141 | ||
142 | /* enable cpu */ | |
143 | writel(SLIM_EN_RUN, slim_rproc->slimcore + SLIM_EN_OFST); | |
144 | ||
145 | hw_id = readl_relaxed(slim_rproc->slimcore + SLIM_ID_OFST); | |
146 | hw_ver = readl_relaxed(slim_rproc->slimcore + SLIM_VER_OFST); | |
147 | ||
148 | fw_rev = readl(slim_rproc->mem[ST_SLIM_DMEM].cpu_addr + | |
149 | SLIM_REV_ID_OFST); | |
150 | ||
151 | dev_info(dev, "fw rev:%ld.%ld on SLIM %ld.%ld\n", | |
152 | SLIM_REV_ID_MAJ(fw_rev), SLIM_REV_ID_MIN(fw_rev), | |
153 | hw_id, hw_ver); | |
154 | ||
155 | return 0; | |
156 | } | |
157 | ||
158 | static int slim_rproc_stop(struct rproc *rproc) | |
159 | { | |
160 | struct st_slim_rproc *slim_rproc = rproc->priv; | |
161 | u32 val; | |
162 | ||
163 | /* mask all (cmd & int) channels */ | |
164 | writel(0UL, slim_rproc->peri + SLIM_INT_MASK_OFST); | |
165 | writel(0UL, slim_rproc->peri + SLIM_CMD_MASK_OFST); | |
166 | ||
167 | /* disable cpu pipeline clock */ | |
168 | writel(SLIM_CLK_GATE_DIS, slim_rproc->slimcore + SLIM_CLK_GATE_OFST); | |
169 | ||
170 | writel(!SLIM_EN_RUN, slim_rproc->slimcore + SLIM_EN_OFST); | |
171 | ||
172 | val = readl(slim_rproc->slimcore + SLIM_EN_OFST); | |
173 | if (val & SLIM_EN_RUN) | |
174 | dev_warn(&rproc->dev, "Failed to disable SLIM"); | |
175 | ||
176 | dev_dbg(&rproc->dev, "slim stopped\n"); | |
177 | ||
178 | return 0; | |
179 | } | |
180 | ||
181 | static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, int len) | |
182 | { | |
183 | struct st_slim_rproc *slim_rproc = rproc->priv; | |
184 | void *va = NULL; | |
185 | int i; | |
186 | ||
187 | for (i = 0; i < ST_SLIM_MEM_MAX; i++) { | |
188 | if (da != slim_rproc->mem[i].bus_addr) | |
189 | continue; | |
190 | ||
191 | if (len <= slim_rproc->mem[i].size) { | |
192 | /* __force to make sparse happy with type conversion */ | |
193 | va = (__force void *)slim_rproc->mem[i].cpu_addr; | |
194 | break; | |
195 | } | |
196 | } | |
197 | ||
198 | dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%x va = 0x%p\n", da, len, va); | |
199 | ||
200 | return va; | |
201 | } | |
202 | ||
203 | static struct rproc_ops slim_rproc_ops = { | |
204 | .start = slim_rproc_start, | |
205 | .stop = slim_rproc_stop, | |
206 | .da_to_va = slim_rproc_da_to_va, | |
207 | }; | |
208 | ||
209 | /* | |
210 | * Firmware handler operations: sanity, boot address, load ... | |
211 | */ | |
212 | ||
213 | static struct resource_table empty_rsc_tbl = { | |
214 | .ver = 1, | |
215 | .num = 0, | |
216 | }; | |
217 | ||
218 | static struct resource_table *slim_rproc_find_rsc_table(struct rproc *rproc, | |
219 | const struct firmware *fw, | |
220 | int *tablesz) | |
221 | { | |
222 | *tablesz = sizeof(empty_rsc_tbl); | |
223 | return &empty_rsc_tbl; | |
224 | } | |
225 | ||
226 | static struct rproc_fw_ops slim_rproc_fw_ops = { | |
227 | .find_rsc_table = slim_rproc_find_rsc_table, | |
228 | }; | |
229 | ||
230 | /** | |
231 | * st_slim_rproc_alloc() - allocate and initialise slim rproc | |
232 | * @pdev: Pointer to the platform_device struct | |
233 | * @fw_name: Name of firmware for rproc to use | |
234 | * | |
235 | * Function for allocating and initialising a slim rproc for use by | |
236 | * device drivers whose IP is based around the SLIM core. It | |
237 | * obtains and enables any clocks required by the SLIM core and also | |
238 | * ioremaps the various IO. | |
239 | * | |
240 | * Returns st_slim_rproc pointer or PTR_ERR() on error. | |
241 | */ | |
242 | ||
243 | struct st_slim_rproc *st_slim_rproc_alloc(struct platform_device *pdev, | |
244 | char *fw_name) | |
245 | { | |
246 | struct device *dev = &pdev->dev; | |
247 | struct st_slim_rproc *slim_rproc; | |
248 | struct device_node *np = dev->of_node; | |
249 | struct rproc *rproc; | |
250 | struct resource *res; | |
251 | int err, i; | |
252 | const struct rproc_fw_ops *elf_ops; | |
253 | ||
254 | if (!fw_name) | |
255 | return ERR_PTR(-EINVAL); | |
256 | ||
257 | if (!of_device_is_compatible(np, "st,slim-rproc")) | |
258 | return ERR_PTR(-EINVAL); | |
259 | ||
260 | rproc = rproc_alloc(dev, np->name, &slim_rproc_ops, | |
261 | fw_name, sizeof(*slim_rproc)); | |
262 | if (!rproc) | |
263 | return ERR_PTR(-ENOMEM); | |
264 | ||
265 | rproc->has_iommu = false; | |
266 | ||
267 | slim_rproc = rproc->priv; | |
268 | slim_rproc->rproc = rproc; | |
269 | ||
270 | elf_ops = rproc->fw_ops; | |
271 | /* Use some generic elf ops */ | |
272 | slim_rproc_fw_ops.load = elf_ops->load; | |
273 | slim_rproc_fw_ops.sanity_check = elf_ops->sanity_check; | |
274 | ||
275 | rproc->fw_ops = &slim_rproc_fw_ops; | |
276 | ||
277 | /* get imem and dmem */ | |
278 | for (i = 0; i < ARRAY_SIZE(mem_names); i++) { | |
279 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
280 | mem_names[i]); | |
281 | ||
282 | slim_rproc->mem[i].cpu_addr = devm_ioremap_resource(dev, res); | |
283 | if (IS_ERR(slim_rproc->mem[i].cpu_addr)) { | |
284 | dev_err(&pdev->dev, "devm_ioremap_resource failed\n"); | |
285 | err = PTR_ERR(slim_rproc->mem[i].cpu_addr); | |
286 | goto err; | |
287 | } | |
288 | slim_rproc->mem[i].bus_addr = res->start; | |
289 | slim_rproc->mem[i].size = resource_size(res); | |
290 | } | |
291 | ||
292 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "slimcore"); | |
293 | slim_rproc->slimcore = devm_ioremap_resource(dev, res); | |
294 | if (IS_ERR(slim_rproc->slimcore)) { | |
295 | dev_err(&pdev->dev, "failed to ioremap slimcore IO\n"); | |
296 | err = PTR_ERR(slim_rproc->slimcore); | |
297 | goto err; | |
298 | } | |
299 | ||
300 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "peripherals"); | |
301 | slim_rproc->peri = devm_ioremap_resource(dev, res); | |
302 | if (IS_ERR(slim_rproc->peri)) { | |
303 | dev_err(&pdev->dev, "failed to ioremap peripherals IO\n"); | |
304 | err = PTR_ERR(slim_rproc->peri); | |
305 | goto err; | |
306 | } | |
307 | ||
308 | err = slim_clk_get(slim_rproc, dev); | |
309 | if (err) | |
310 | goto err; | |
311 | ||
312 | err = slim_clk_enable(slim_rproc); | |
313 | if (err) { | |
314 | dev_err(dev, "Failed to enable clocks\n"); | |
315 | goto err_clk_put; | |
316 | } | |
317 | ||
318 | /* Register as a remoteproc device */ | |
319 | err = rproc_add(rproc); | |
320 | if (err) { | |
321 | dev_err(dev, "registration of slim remoteproc failed\n"); | |
322 | goto err_clk_dis; | |
323 | } | |
324 | ||
325 | return slim_rproc; | |
326 | ||
327 | err_clk_dis: | |
328 | slim_clk_disable(slim_rproc); | |
329 | err_clk_put: | |
330 | for (i = 0; i < ST_SLIM_MAX_CLK && slim_rproc->clks[i]; i++) | |
331 | clk_put(slim_rproc->clks[i]); | |
332 | err: | |
333 | rproc_put(rproc); | |
334 | return ERR_PTR(err); | |
335 | } | |
336 | EXPORT_SYMBOL(st_slim_rproc_alloc); | |
337 | ||
338 | /** | |
339 | * st_slim_rproc_put() - put slim rproc resources | |
340 | * @slim_rproc: Pointer to the st_slim_rproc struct | |
341 | * | |
342 | * Function for calling respective _put() functions on slim_rproc resources. | |
343 | * | |
344 | */ | |
345 | void st_slim_rproc_put(struct st_slim_rproc *slim_rproc) | |
346 | { | |
347 | int clk; | |
348 | ||
349 | if (!slim_rproc) | |
350 | return; | |
351 | ||
352 | slim_clk_disable(slim_rproc); | |
353 | ||
354 | for (clk = 0; clk < ST_SLIM_MAX_CLK && slim_rproc->clks[clk]; clk++) | |
355 | clk_put(slim_rproc->clks[clk]); | |
356 | ||
357 | rproc_del(slim_rproc->rproc); | |
358 | rproc_put(slim_rproc->rproc); | |
359 | } | |
360 | EXPORT_SYMBOL(st_slim_rproc_put); | |
361 | ||
362 | MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>"); | |
363 | MODULE_DESCRIPTION("STMicroelectronics SLIM core rproc driver"); | |
364 | MODULE_LICENSE("GPL v2"); |