1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2017 Linaro Ltd.
4 */
5
6 #include <linux/device.h>
7 #include <linux/firmware.h>
8 #include <linux/kernel.h>
9 #include <linux/iommu.h>
10 #include <linux/io.h>
11 #include <linux/of.h>
12 #include <linux/of_reserved_mem.h>
13 #include <linux/platform_device.h>
14 #include <linux/of_device.h>
15 #include <linux/firmware/qcom/qcom_scm.h>
16 #include <linux/sizes.h>
17 #include <linux/soc/qcom/mdt_loader.h>
18
19 #include "core.h"
20 #include "firmware.h"
21 #include "hfi_venus_io.h"
22
23 #define VENUS_PAS_ID 9
24 #define VENUS_FW_MEM_SIZE (6 * SZ_1M)
25 #define VENUS_FW_START_ADDR 0x0
26
venus_reset_cpu(struct venus_core * core)27 static void venus_reset_cpu(struct venus_core *core)
28 {
29 u32 fw_size = core->fw.mapped_mem_size;
30 void __iomem *wrapper_base;
31
32 if (IS_IRIS2(core) || IS_IRIS2_1(core))
33 wrapper_base = core->wrapper_tz_base;
34 else
35 wrapper_base = core->wrapper_base;
36
37 writel(0, wrapper_base + WRAPPER_FW_START_ADDR);
38 writel(fw_size, wrapper_base + WRAPPER_FW_END_ADDR);
39 writel(0, wrapper_base + WRAPPER_CPA_START_ADDR);
40 writel(fw_size, wrapper_base + WRAPPER_CPA_END_ADDR);
41 writel(fw_size, wrapper_base + WRAPPER_NONPIX_START_ADDR);
42 writel(fw_size, wrapper_base + WRAPPER_NONPIX_END_ADDR);
43
44 if (IS_IRIS2(core) || IS_IRIS2_1(core)) {
45 /* Bring XTSS out of reset */
46 writel(0, wrapper_base + WRAPPER_TZ_XTSS_SW_RESET);
47 } else {
48 writel(0x0, wrapper_base + WRAPPER_CPU_CGC_DIS);
49 writel(0x0, wrapper_base + WRAPPER_CPU_CLOCK_CONFIG);
50
51 /* Bring ARM9 out of reset */
52 writel(0, wrapper_base + WRAPPER_A9SS_SW_RESET);
53 }
54 }
55
venus_set_hw_state(struct venus_core * core,bool resume)56 int venus_set_hw_state(struct venus_core *core, bool resume)
57 {
58 int ret;
59
60 if (core->use_tz) {
61 ret = qcom_scm_set_remote_state(resume, 0);
62 if (resume && ret == -EINVAL)
63 ret = 0;
64 return ret;
65 }
66
67 if (resume) {
68 venus_reset_cpu(core);
69 } else {
70 if (IS_IRIS2(core) || IS_IRIS2_1(core))
71 writel(WRAPPER_XTSS_SW_RESET_BIT,
72 core->wrapper_tz_base + WRAPPER_TZ_XTSS_SW_RESET);
73 else
74 writel(WRAPPER_A9SS_SW_RESET_BIT,
75 core->wrapper_base + WRAPPER_A9SS_SW_RESET);
76 }
77
78 return 0;
79 }
80
venus_load_fw(struct venus_core * core,const char * fwname,phys_addr_t * mem_phys,size_t * mem_size)81 static int venus_load_fw(struct venus_core *core, const char *fwname,
82 phys_addr_t *mem_phys, size_t *mem_size)
83 {
84 const struct firmware *mdt;
85 struct resource res;
86 struct device *dev;
87 ssize_t fw_size;
88 void *mem_va;
89 int ret;
90
91 *mem_phys = 0;
92 *mem_size = 0;
93
94 dev = core->dev;
95 ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res);
96 if (ret) {
97 dev_err(dev, "failed to lookup reserved memory-region\n");
98 return -EINVAL;
99 }
100
101 ret = request_firmware(&mdt, fwname, dev);
102 if (ret < 0)
103 return ret;
104
105 fw_size = qcom_mdt_get_size(mdt);
106 if (fw_size < 0) {
107 ret = fw_size;
108 goto err_release_fw;
109 }
110
111 *mem_phys = res.start;
112 *mem_size = resource_size(&res);
113
114 if (*mem_size < fw_size || fw_size > VENUS_FW_MEM_SIZE) {
115 ret = -EINVAL;
116 goto err_release_fw;
117 }
118
119 mem_va = memremap(*mem_phys, *mem_size, MEMREMAP_WC);
120 if (!mem_va) {
121 dev_err(dev, "unable to map memory region %pa size %#zx\n", mem_phys, *mem_size);
122 ret = -ENOMEM;
123 goto err_release_fw;
124 }
125
126 if (core->use_tz)
127 ret = qcom_mdt_load(dev, mdt, fwname, VENUS_PAS_ID,
128 mem_va, *mem_phys, *mem_size, NULL);
129 else
130 ret = qcom_mdt_load_no_init(dev, mdt, fwname, mem_va,
131 *mem_phys, *mem_size, NULL);
132
133 memunmap(mem_va);
134 err_release_fw:
135 release_firmware(mdt);
136 return ret;
137 }
138
venus_boot_no_tz(struct venus_core * core,phys_addr_t mem_phys,size_t mem_size)139 static int venus_boot_no_tz(struct venus_core *core, phys_addr_t mem_phys,
140 size_t mem_size)
141 {
142 struct iommu_domain *iommu;
143 struct device *dev;
144 int ret;
145
146 dev = core->fw.dev;
147 if (!dev)
148 return -EPROBE_DEFER;
149
150 iommu = core->fw.iommu_domain;
151 core->fw.mapped_mem_size = mem_size;
152
153 ret = iommu_map(iommu, VENUS_FW_START_ADDR, mem_phys, mem_size,
154 IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV, GFP_KERNEL);
155 if (ret) {
156 dev_err(dev, "could not map video firmware region\n");
157 return ret;
158 }
159
160 venus_reset_cpu(core);
161
162 return 0;
163 }
164
venus_shutdown_no_tz(struct venus_core * core)165 static int venus_shutdown_no_tz(struct venus_core *core)
166 {
167 const size_t mapped = core->fw.mapped_mem_size;
168 struct iommu_domain *iommu;
169 size_t unmapped;
170 u32 reg;
171 struct device *dev = core->fw.dev;
172 void __iomem *wrapper_base = core->wrapper_base;
173 void __iomem *wrapper_tz_base = core->wrapper_tz_base;
174
175 if (IS_IRIS2(core) || IS_IRIS2_1(core)) {
176 /* Assert the reset to XTSS */
177 reg = readl(wrapper_tz_base + WRAPPER_TZ_XTSS_SW_RESET);
178 reg |= WRAPPER_XTSS_SW_RESET_BIT;
179 writel(reg, wrapper_tz_base + WRAPPER_TZ_XTSS_SW_RESET);
180 } else {
181 /* Assert the reset to ARM9 */
182 reg = readl(wrapper_base + WRAPPER_A9SS_SW_RESET);
183 reg |= WRAPPER_A9SS_SW_RESET_BIT;
184 writel(reg, wrapper_base + WRAPPER_A9SS_SW_RESET);
185 }
186
187 iommu = core->fw.iommu_domain;
188
189 if (core->fw.mapped_mem_size && iommu) {
190 unmapped = iommu_unmap(iommu, VENUS_FW_START_ADDR, mapped);
191
192 if (unmapped != mapped)
193 dev_err(dev, "failed to unmap firmware\n");
194 else
195 core->fw.mapped_mem_size = 0;
196 }
197
198 return 0;
199 }
200
venus_firmware_cfg(struct venus_core * core)201 int venus_firmware_cfg(struct venus_core *core)
202 {
203 void __iomem *cpu_cs_base = core->cpu_cs_base;
204
205 if (IS_AR50_LITE(core))
206 writel(CPU_CS_VCICMD_ARP_OFF, cpu_cs_base + CPU_CS_VCICMD);
207
208 return 0;
209 }
210
venus_boot(struct venus_core * core)211 int venus_boot(struct venus_core *core)
212 {
213 struct device *dev = core->dev;
214 const struct venus_resources *res = core->res;
215 const char *fwpath = NULL;
216 phys_addr_t mem_phys;
217 size_t mem_size;
218 int ret;
219
220 if (!IS_ENABLED(CONFIG_QCOM_MDT_LOADER) ||
221 (core->use_tz && !qcom_scm_is_available()))
222 return -EPROBE_DEFER;
223
224 ret = of_property_read_string_index(dev->of_node, "firmware-name", 0,
225 &fwpath);
226 if (ret)
227 fwpath = core->res->fwname;
228
229 ret = venus_load_fw(core, fwpath, &mem_phys, &mem_size);
230 if (ret) {
231 dev_err(dev, "fail to load video firmware\n");
232 return -EINVAL;
233 }
234
235 core->fw.mem_size = mem_size;
236 core->fw.mem_phys = mem_phys;
237
238 if (core->use_tz)
239 ret = qcom_scm_pas_auth_and_reset(VENUS_PAS_ID);
240 else
241 ret = venus_boot_no_tz(core, mem_phys, mem_size);
242
243 if (ret)
244 return ret;
245
246 if (core->use_tz && res->cp_size) {
247 /*
248 * Clues for porting using downstream data:
249 * cp_start = 0
250 * cp_size = venus_ns/virtual-addr-pool[0] - yes, address and not size!
251 * This works, as the non-secure context bank is placed
252 * contiguously right after the Content Protection region.
253 *
254 * cp_nonpixel_start = venus_sec_non_pixel/virtual-addr-pool[0]
255 * cp_nonpixel_size = venus_sec_non_pixel/virtual-addr-pool[1]
256 */
257 ret = qcom_scm_mem_protect_video_var(res->cp_start,
258 res->cp_size,
259 res->cp_nonpixel_start,
260 res->cp_nonpixel_size);
261 if (ret) {
262 qcom_scm_pas_shutdown(VENUS_PAS_ID);
263 dev_err(dev, "set virtual address ranges fail (%d)\n",
264 ret);
265 return ret;
266 }
267 }
268
269 return 0;
270 }
271
venus_shutdown(struct venus_core * core)272 int venus_shutdown(struct venus_core *core)
273 {
274 int ret;
275
276 if (core->use_tz)
277 ret = qcom_scm_pas_shutdown(VENUS_PAS_ID);
278 else
279 ret = venus_shutdown_no_tz(core);
280
281 return ret;
282 }
283
venus_firmware_check(struct venus_core * core)284 int venus_firmware_check(struct venus_core *core)
285 {
286 const struct firmware_version *req = core->res->min_fw;
287 const struct firmware_version *run = &core->venus_ver;
288
289 if (!req)
290 return 0;
291
292 if (!is_fw_rev_or_newer(core, req->major, req->minor, req->rev))
293 goto error;
294
295 return 0;
296 error:
297 dev_err(core->dev, "Firmware v%d.%d.%d < v%d.%d.%d\n",
298 run->major, run->minor, run->rev,
299 req->major, req->minor, req->rev);
300
301 return -EINVAL;
302 }
303
venus_firmware_init(struct venus_core * core)304 int venus_firmware_init(struct venus_core *core)
305 {
306 struct platform_device_info info;
307 struct iommu_domain *iommu_dom;
308 struct platform_device *pdev;
309 struct device_node *np;
310 int ret;
311
312 np = of_get_child_by_name(core->dev->of_node, "video-firmware");
313 if (!np) {
314 core->use_tz = true;
315 return 0;
316 }
317
318 memset(&info, 0, sizeof(info));
319 info.fwnode = &np->fwnode;
320 info.parent = core->dev;
321 info.name = np->name;
322 info.dma_mask = DMA_BIT_MASK(32);
323
324 pdev = platform_device_register_full(&info);
325 if (IS_ERR(pdev)) {
326 of_node_put(np);
327 return PTR_ERR(pdev);
328 }
329
330 pdev->dev.of_node = np;
331
332 ret = of_dma_configure(&pdev->dev, np, true);
333 if (ret) {
334 dev_err(core->dev, "dma configure fail\n");
335 goto err_unregister;
336 }
337
338 core->fw.dev = &pdev->dev;
339
340 iommu_dom = iommu_paging_domain_alloc(core->fw.dev);
341 if (IS_ERR(iommu_dom)) {
342 dev_err(core->fw.dev, "Failed to allocate iommu domain\n");
343 ret = PTR_ERR(iommu_dom);
344 goto err_unregister;
345 }
346
347 ret = iommu_attach_device(iommu_dom, core->fw.dev);
348 if (ret) {
349 dev_err(core->fw.dev, "could not attach device\n");
350 goto err_iommu_free;
351 }
352
353 core->fw.iommu_domain = iommu_dom;
354
355 of_node_put(np);
356
357 return 0;
358
359 err_iommu_free:
360 iommu_domain_free(iommu_dom);
361 err_unregister:
362 platform_device_unregister(pdev);
363 of_node_put(np);
364 return ret;
365 }
366
venus_firmware_deinit(struct venus_core * core)367 void venus_firmware_deinit(struct venus_core *core)
368 {
369 struct iommu_domain *iommu;
370
371 if (!core->fw.dev)
372 return;
373
374 iommu = core->fw.iommu_domain;
375
376 iommu_detach_device(iommu, core->fw.dev);
377
378 if (core->fw.iommu_domain) {
379 iommu_domain_free(iommu);
380 core->fw.iommu_domain = NULL;
381 }
382
383 platform_device_unregister(to_platform_device(core->fw.dev));
384 }
385