blob: 715492b081a16bbc932f9eeb0cc55ca4fae40e0d [file] [log] [blame]
Srinu Gorlecf8c6752018-01-19 18:36:13 +05301/* Copyright (c) 2014-2016, 2018 The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#define VIDC_DBG_LABEL "venus_boot"
14
15#include <asm/dma-iommu.h>
16#include <asm/page.h>
17#include <linux/clk.h>
18#include <linux/delay.h>
19#include <linux/dma-mapping.h>
20#include <linux/err.h>
21#include <linux/io.h>
22#include <linux/iommu.h>
23#include <linux/iopoll.h>
24#include <linux/kernel.h>
25#include <linux/module.h>
26#include <linux/of.h>
27#include <linux/platform_device.h>
28#include <linux/regulator/consumer.h>
29#include <linux/sizes.h>
30#include <linux/slab.h>
31#include <soc/qcom/subsystem_notif.h>
32#include <soc/qcom/subsystem_restart.h>
33#include "msm_vidc_debug.h"
34#include "vidc_hfi_io.h"
35#include "venus_boot.h"
36
37/* VENUS WRAPPER registers */
38#define VENUS_WRAPPER_VBIF_SS_SEC_CPA_START_ADDR_v1 \
39 (VIDC_WRAPPER_BASE_OFFS + 0x1018)
40#define VENUS_WRAPPER_VBIF_SS_SEC_CPA_END_ADDR_v1 \
41 (VIDC_WRAPPER_BASE_OFFS + 0x101C)
42#define VENUS_WRAPPER_VBIF_SS_SEC_FW_START_ADDR_v1 \
43 (VIDC_WRAPPER_BASE_OFFS + 0x1020)
44#define VENUS_WRAPPER_VBIF_SS_SEC_FW_END_ADDR_v1 \
45 (VIDC_WRAPPER_BASE_OFFS + 0x1024)
46
47#define VENUS_WRAPPER_VBIF_SS_SEC_CPA_START_ADDR_v2 \
48 (VIDC_WRAPPER_BASE_OFFS + 0x1020)
49#define VENUS_WRAPPER_VBIF_SS_SEC_CPA_END_ADDR_v2 \
50 (VIDC_WRAPPER_BASE_OFFS + 0x1024)
51#define VENUS_WRAPPER_VBIF_SS_SEC_FW_START_ADDR_v2 \
52 (VIDC_WRAPPER_BASE_OFFS + 0x1028)
53#define VENUS_WRAPPER_VBIF_SS_SEC_FW_END_ADDR_v2 \
54 (VIDC_WRAPPER_BASE_OFFS + 0x102C)
55
56#define VENUS_WRAPPER_SW_RESET (VIDC_WRAPPER_BASE_OFFS + 0x3000)
57
58/* VENUS VBIF registers */
59#define VENUS_VBIF_CLKON_FORCE_ON BIT(0)
60
61#define VENUS_VBIF_ADDR_TRANS_EN (VIDC_VBIF_BASE_OFFS + 0x1000)
62#define VENUS_VBIF_AT_OLD_BASE (VIDC_VBIF_BASE_OFFS + 0x1004)
63#define VENUS_VBIF_AT_OLD_HIGH (VIDC_VBIF_BASE_OFFS + 0x1008)
64#define VENUS_VBIF_AT_NEW_BASE (VIDC_VBIF_BASE_OFFS + 0x1010)
65#define VENUS_VBIF_AT_NEW_HIGH (VIDC_VBIF_BASE_OFFS + 0x1018)
66
67
68/* Poll interval in uS */
69#define POLL_INTERVAL_US 50
70
71#define VENUS_REGION_SIZE 0x00500000
72
73static struct {
74 struct msm_vidc_platform_resources *resources;
75 struct regulator *gdsc;
76 const char *reg_name;
77 void __iomem *reg_base;
78 struct device *iommu_ctx_bank_dev;
79 struct dma_iommu_mapping *mapping;
80 dma_addr_t fw_iova;
81 bool is_booted;
82 bool hw_ver_checked;
83 u32 fw_sz;
84 u32 hw_ver_major;
85 u32 hw_ver_minor;
86 void *venus_notif_hdle;
87} *venus_data = NULL;
88
89/* Get venus clocks and set rates for rate-settable clocks */
90static int venus_clock_setup(void)
91{
92 int i, rc = 0;
93 unsigned long rate;
94 struct msm_vidc_platform_resources *res = venus_data->resources;
95 struct clock_info *cl;
96
97 for (i = 0; i < res->clock_set.count; i++) {
98 cl = &res->clock_set.clock_tbl[i];
99 /* Make sure rate-settable clocks' rates are set */
100 if (!clk_get_rate(cl->clk) && cl->count) {
101 rate = clk_round_rate(cl->clk, 0);
102 rc = clk_set_rate(cl->clk, rate);
103 if (rc) {
104 dprintk(VIDC_ERR,
105 "Failed to set clock rate %lu %s: %d\n",
106 rate, cl->name, rc);
107 break;
108 }
109 }
110 }
111
112 return rc;
113}
114
115static int venus_clock_prepare_enable(void)
116{
117 int i, rc = 0;
118 struct msm_vidc_platform_resources *res = venus_data->resources;
119 struct clock_info *cl;
120
121 for (i = 0; i < res->clock_set.count; i++) {
122 cl = &res->clock_set.clock_tbl[i];
123 rc = clk_prepare_enable(cl->clk);
124 if (rc) {
125 dprintk(VIDC_ERR, "failed to enable %s\n", cl->name);
126 for (i--; i >= 0; i--) {
127 cl = &res->clock_set.clock_tbl[i];
128 clk_disable_unprepare(cl->clk);
129 }
130 return rc;
131 }
132 }
133
134 return rc;
135}
136
137static void venus_clock_disable_unprepare(void)
138{
139 int i;
140 struct msm_vidc_platform_resources *res = venus_data->resources;
141 struct clock_info *cl;
142
143 for (i = 0; i < res->clock_set.count; i++) {
144 cl = &res->clock_set.clock_tbl[i];
145 clk_disable_unprepare(cl->clk);
146 }
147}
148
149static int venus_setup_cb(struct device *dev,
150 u32 size)
151{
152 dma_addr_t va_start = 0x0;
153 size_t va_size = size;
154
155 venus_data->mapping = arm_iommu_create_mapping(
156 dev->bus, va_start, va_size);
157 if (IS_ERR_OR_NULL(venus_data->mapping)) {
158 dprintk(VIDC_ERR, "%s: failed to create mapping for %s\n",
159 __func__, dev_name(dev));
160 return -ENODEV;
161 }
162 dprintk(VIDC_DBG,
163 "%s Attached device %pK and created mapping %pK for %s\n",
164 __func__, dev, venus_data->mapping, dev_name(dev));
165 return 0;
166}
167
168static int pil_venus_mem_setup(size_t size)
169{
170 int rc = 0;
171
172 if (!venus_data->mapping) {
173 size = round_up(size, SZ_4K);
174 rc = venus_setup_cb(venus_data->iommu_ctx_bank_dev, size);
175 if (rc) {
176 dprintk(VIDC_ERR,
177 "%s: Failed to setup context bank for venus : %s\n",
178 __func__,
179 dev_name(venus_data->iommu_ctx_bank_dev));
180 return rc;
181 }
182 venus_data->fw_sz = size;
183 }
184
185 return rc;
186}
187
188static int pil_venus_auth_and_reset(void)
189{
190 int rc;
191
192 phys_addr_t fw_bias = venus_data->resources->firmware_base;
193 void __iomem *reg_base = venus_data->reg_base;
194 u32 ver;
195 bool iommu_present = is_iommu_present(venus_data->resources);
196 struct device *dev = venus_data->iommu_ctx_bank_dev;
197
198 if (!fw_bias) {
199 dprintk(VIDC_ERR, "FW bias is not valid\n");
200 return -EINVAL;
201 }
202 venus_data->fw_iova = (dma_addr_t)NULL;
203 /* Get Venus version number */
204 if (!venus_data->hw_ver_checked) {
205 ver = readl_relaxed(reg_base + VIDC_WRAPPER_HW_VERSION);
206 venus_data->hw_ver_minor = (ver & 0x0FFF0000) >> 16;
207 venus_data->hw_ver_major = (ver & 0xF0000000) >> 28;
208 venus_data->hw_ver_checked = 1;
209 }
210
211 if (iommu_present) {
212 u32 cpa_start_addr, cpa_end_addr, fw_start_addr, fw_end_addr;
213 /* Get the cpa and fw start/end addr based on Venus version */
214 if (venus_data->hw_ver_major == 0x1 &&
215 venus_data->hw_ver_minor <= 1) {
216 cpa_start_addr =
217 VENUS_WRAPPER_VBIF_SS_SEC_CPA_START_ADDR_v1;
218 cpa_end_addr =
219 VENUS_WRAPPER_VBIF_SS_SEC_CPA_END_ADDR_v1;
220 fw_start_addr =
221 VENUS_WRAPPER_VBIF_SS_SEC_FW_START_ADDR_v1;
222 fw_end_addr =
223 VENUS_WRAPPER_VBIF_SS_SEC_FW_END_ADDR_v1;
224 } else {
225 cpa_start_addr =
226 VENUS_WRAPPER_VBIF_SS_SEC_CPA_START_ADDR_v2;
227 cpa_end_addr =
228 VENUS_WRAPPER_VBIF_SS_SEC_CPA_END_ADDR_v2;
229 fw_start_addr =
230 VENUS_WRAPPER_VBIF_SS_SEC_FW_START_ADDR_v2;
231 fw_end_addr =
232 VENUS_WRAPPER_VBIF_SS_SEC_FW_END_ADDR_v2;
233 }
234
235 /* Program CPA start and end address */
236 writel_relaxed(0, reg_base + cpa_start_addr);
237 writel_relaxed(venus_data->fw_sz, reg_base + cpa_end_addr);
238
239 /* Program FW start and end address */
240 writel_relaxed(0, reg_base + fw_start_addr);
241 writel_relaxed(venus_data->fw_sz, reg_base + fw_end_addr);
242 } else {
243 rc = regulator_enable(venus_data->gdsc);
244 if (rc) {
245 dprintk(VIDC_ERR, "GDSC enable failed\n");
246 goto err;
247 }
248
249 rc = venus_clock_prepare_enable();
250 if (rc) {
251 dprintk(VIDC_ERR, "Clock prepare and enable failed\n");
252 regulator_disable(venus_data->gdsc);
253 goto err;
254 }
255
256 writel_relaxed(0, reg_base + VENUS_VBIF_AT_OLD_BASE);
257 writel_relaxed(VENUS_REGION_SIZE,
258 reg_base + VENUS_VBIF_AT_OLD_HIGH);
259 writel_relaxed(fw_bias, reg_base + VENUS_VBIF_AT_NEW_BASE);
260 writel_relaxed(fw_bias + VENUS_REGION_SIZE,
261 reg_base + VENUS_VBIF_AT_NEW_HIGH);
262 writel_relaxed(0x7F007F, reg_base + VENUS_VBIF_ADDR_TRANS_EN);
263 venus_clock_disable_unprepare();
264 regulator_disable(venus_data->gdsc);
265 }
266 /* Make sure all register writes are committed. */
267 mb();
268
269 /*
270 * Need to wait 10 cycles of internal clocks before bringing ARM9
271 * out of reset.
272 */
273 udelay(1);
274
275 if (iommu_present) {
276 phys_addr_t pa = fw_bias;
277
278 rc = arm_iommu_attach_device(dev, venus_data->mapping);
279 if (rc) {
280 dprintk(VIDC_ERR,
281 "Failed to attach iommu for %s : %d\n",
282 dev_name(dev), rc);
283 goto release_mapping;
284 }
285
286 dprintk(VIDC_DBG, "Attached and created mapping for %s\n",
287 dev_name(dev));
288
289 /* Map virtual addr space 0 - fw_sz to fw phys addr space */
290 rc = iommu_map(venus_data->mapping->domain,
291 venus_data->fw_iova, pa, venus_data->fw_sz,
292 IOMMU_READ|IOMMU_WRITE|IOMMU_PRIV);
293 if (!rc) {
294 dprintk(VIDC_DBG,
295 "%s - Successfully mapped and performed test translation!\n",
296 dev_name(dev));
297 }
298
299 if (rc || (venus_data->fw_iova != 0)) {
300 dprintk(VIDC_ERR, "%s - Failed to setup IOMMU\n",
301 dev_name(dev));
302 goto err_iommu_map;
303 }
304 }
305 /* Bring Arm9 out of reset */
306 writel_relaxed(0, reg_base + VENUS_WRAPPER_SW_RESET);
307
308 venus_data->is_booted = 1;
309 return 0;
310
311err_iommu_map:
312 if (iommu_present)
313 arm_iommu_detach_device(dev);
314release_mapping:
315 if (iommu_present)
316 arm_iommu_release_mapping(venus_data->mapping);
317err:
318 return rc;
319}
320
321static int pil_venus_shutdown(void)
322{
323 void __iomem *reg_base = venus_data->reg_base;
324 u32 reg;
325 int rc;
326
327 if (!venus_data->is_booted)
328 return 0;
329
330 /* Assert the reset to ARM9 */
331 reg = readl_relaxed(reg_base + VENUS_WRAPPER_SW_RESET);
332 reg |= BIT(4);
333 writel_relaxed(reg, reg_base + VENUS_WRAPPER_SW_RESET);
334
335 /* Make sure reset is asserted before the mapping is removed */
336 mb();
337
338 if (is_iommu_present(venus_data->resources)) {
339 iommu_unmap(venus_data->mapping->domain, venus_data->fw_iova,
340 venus_data->fw_sz);
341 arm_iommu_detach_device(venus_data->iommu_ctx_bank_dev);
342 }
343 /*
344 * Force the VBIF clk to be on to avoid AXI bridge halt ack failure
345 * for certain Venus version.
346 */
347 if (venus_data->hw_ver_major == 0x1 &&
348 (venus_data->hw_ver_minor == 0x2 ||
349 venus_data->hw_ver_minor == 0x3)) {
350 reg = readl_relaxed(reg_base + VIDC_VENUS_VBIF_CLK_ON);
351 reg |= VENUS_VBIF_CLKON_FORCE_ON;
352 writel_relaxed(reg, reg_base + VIDC_VENUS_VBIF_CLK_ON);
353 }
354
355 /* Halt AXI and AXI OCMEM VBIF Access */
356 reg = readl_relaxed(reg_base + VENUS_VBIF_AXI_HALT_CTRL0);
357 reg |= VENUS_VBIF_AXI_HALT_CTRL0_HALT_REQ;
358 writel_relaxed(reg, reg_base + VENUS_VBIF_AXI_HALT_CTRL0);
359
360 /* Request for AXI bus port halt */
361 rc = readl_poll_timeout(reg_base + VENUS_VBIF_AXI_HALT_CTRL1,
362 reg, reg & VENUS_VBIF_AXI_HALT_CTRL1_HALT_ACK,
363 POLL_INTERVAL_US,
364 VENUS_VBIF_AXI_HALT_ACK_TIMEOUT_US);
365 if (rc)
366 dprintk(VIDC_ERR, "Port halt timeout\n");
367
368 venus_data->is_booted = 0;
369
370 return 0;
371}
372
373static int venus_notifier_cb(struct notifier_block *this, unsigned long code,
374 void *ss_handle)
375{
376 struct notif_data *data = (struct notif_data *)ss_handle;
377 static bool venus_data_set;
378 int ret;
379
380 if (!data->no_auth)
381 return NOTIFY_DONE;
382
383 if (!venus_data_set) {
384 ret = venus_clock_setup();
385 if (ret)
386 return ret;
387
388 ret = of_property_read_string(data->pdev->dev.of_node,
389 "qcom,proxy-reg-names", &venus_data->reg_name);
390 if (ret)
391 return ret;
392
393 venus_data->gdsc = devm_regulator_get(
394 &data->pdev->dev, venus_data->reg_name);
395 if (IS_ERR(venus_data->gdsc)) {
396 dprintk(VIDC_ERR, "Failed to get Venus GDSC\n");
397 return -ENODEV;
398 }
399
400 venus_data_set = true;
401 }
402
403 if (code != SUBSYS_AFTER_POWERUP && code != SUBSYS_AFTER_SHUTDOWN)
404 return NOTIFY_DONE;
405
406 ret = regulator_enable(venus_data->gdsc);
407 if (ret) {
408 dprintk(VIDC_ERR, "GDSC enable failed\n");
409 return ret;
410 }
411
412 ret = venus_clock_prepare_enable();
413 if (ret) {
414 dprintk(VIDC_ERR, "Clock prepare and enable failed\n");
415 goto err_clks;
416 }
417
418 if (code == SUBSYS_AFTER_POWERUP) {
419 if (is_iommu_present(venus_data->resources))
420 pil_venus_mem_setup(VENUS_REGION_SIZE);
421 pil_venus_auth_and_reset();
422 } else if (code == SUBSYS_AFTER_SHUTDOWN)
423 pil_venus_shutdown();
424
425 venus_clock_disable_unprepare();
426 regulator_disable(venus_data->gdsc);
427
428 return NOTIFY_DONE;
429err_clks:
430 regulator_disable(venus_data->gdsc);
431 return ret;
432}
433
434static struct notifier_block venus_notifier = {
435 .notifier_call = venus_notifier_cb,
436};
437
438int venus_boot_init(struct msm_vidc_platform_resources *res,
439 struct context_bank_info *cb)
440{
441 int rc = 0;
442
443 if (!res || !cb) {
444 dprintk(VIDC_ERR, "Invalid platform resource handle\n");
445 return -EINVAL;
446 }
447 venus_data = kzalloc(sizeof(*venus_data), GFP_KERNEL);
448 if (!venus_data)
449 return -ENOMEM;
450
451 venus_data->resources = res;
452 venus_data->iommu_ctx_bank_dev = cb->dev;
453 if (!venus_data->iommu_ctx_bank_dev) {
454 dprintk(VIDC_ERR, "Invalid venus context bank device\n");
455 return -ENODEV;
456 }
457 venus_data->reg_base = ioremap_nocache(res->register_base,
458 (unsigned long)res->register_size);
459 if (!venus_data->reg_base) {
460 dprintk(VIDC_ERR,
461 "could not map reg addr %pa of size %d\n",
462 &res->register_base, res->register_size);
463 rc = -ENOMEM;
464 goto err_ioremap_fail;
465 }
466 venus_data->venus_notif_hdle = subsys_notif_register_notifier("venus",
467 &venus_notifier);
468 if (IS_ERR(venus_data->venus_notif_hdle)) {
469 dprintk(VIDC_ERR, "register event notification failed\n");
470 rc = PTR_ERR(venus_data->venus_notif_hdle);
471 goto err_subsys_notif;
472 }
473
474 return rc;
475
476err_subsys_notif:
477err_ioremap_fail:
478 kfree(venus_data);
479 return rc;
480}
481
482void venus_boot_deinit(void)
483{
484 venus_data->resources = NULL;
485 subsys_notif_unregister_notifier(venus_data->venus_notif_hdle,
486 &venus_notifier);
487 kfree(venus_data);
488}