Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (C) 2016-2018 Linaro Ltd.
  4 * Copyright (C) 2014 Sony Mobile Communications AB
  5 * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
  6 */
  7#include <linux/iopoll.h>
  8#include <linux/kernel.h>
  9#include <linux/mfd/syscon.h>
 10#include <linux/module.h>
 11#include <linux/of_reserved_mem.h>
 12#include <linux/platform_device.h>
 13#include <linux/regmap.h>
 14#include <linux/reset.h>
 15#include <linux/soc/qcom/mdt_loader.h>
 16#include "qcom_common.h"
 17#include "qcom_q6v5.h"
 18
 19#define WCSS_CRASH_REASON		421
 20
 21/* Q6SS Register Offsets */
 22#define Q6SS_RESET_REG		0x014
 23#define Q6SS_GFMUX_CTL_REG		0x020
 24#define Q6SS_PWR_CTL_REG		0x030
 25#define Q6SS_MEM_PWR_CTL		0x0B0
 26
 27/* AXI Halt Register Offsets */
 28#define AXI_HALTREQ_REG			0x0
 29#define AXI_HALTACK_REG			0x4
 30#define AXI_IDLE_REG			0x8
 31
 32#define HALT_ACK_TIMEOUT_MS		100
 33
 34/* Q6SS_RESET */
 35#define Q6SS_STOP_CORE			BIT(0)
 36#define Q6SS_CORE_ARES			BIT(1)
 37#define Q6SS_BUS_ARES_ENABLE		BIT(2)
 38
 39/* Q6SS_GFMUX_CTL */
 40#define Q6SS_CLK_ENABLE			BIT(1)
 41
 42/* Q6SS_PWR_CTL */
 43#define Q6SS_L2DATA_STBY_N		BIT(18)
 44#define Q6SS_SLP_RET_N			BIT(19)
 45#define Q6SS_CLAMP_IO			BIT(20)
 46#define QDSS_BHS_ON			BIT(21)
 47
 48/* Q6SS parameters */
 49#define Q6SS_LDO_BYP		BIT(25)
 50#define Q6SS_BHS_ON		BIT(24)
 51#define Q6SS_CLAMP_WL		BIT(21)
 52#define Q6SS_CLAMP_QMC_MEM		BIT(22)
 53#define HALT_CHECK_MAX_LOOPS		200
 54#define Q6SS_XO_CBCR		GENMASK(5, 3)
 55
 56/* Q6SS config/status registers */
 57#define TCSR_GLOBAL_CFG0	0x0
 58#define TCSR_GLOBAL_CFG1	0x4
 59#define SSCAON_CONFIG		0x8
 60#define SSCAON_STATUS		0xc
 61#define Q6SS_BHS_STATUS		0x78
 62#define Q6SS_RST_EVB		0x10
 63
 64#define BHS_EN_REST_ACK		BIT(0)
 65#define SSCAON_ENABLE		BIT(13)
 66#define SSCAON_BUS_EN		BIT(15)
 67#define SSCAON_BUS_MUX_MASK	GENMASK(18, 16)
 68
 69#define MEM_BANKS		19
 70#define TCSR_WCSS_CLK_MASK	0x1F
 71#define TCSR_WCSS_CLK_ENABLE	0x14
 72
 73struct q6v5_wcss {
 74	struct device *dev;
 75
 76	void __iomem *reg_base;
 77	void __iomem *rmb_base;
 78
 79	struct regmap *halt_map;
 80	u32 halt_q6;
 81	u32 halt_wcss;
 82	u32 halt_nc;
 83
 84	struct reset_control *wcss_aon_reset;
 85	struct reset_control *wcss_reset;
 86	struct reset_control *wcss_q6_reset;
 87
 88	struct qcom_q6v5 q6v5;
 89
 90	phys_addr_t mem_phys;
 91	phys_addr_t mem_reloc;
 92	void *mem_region;
 93	size_t mem_size;
 94};
 95
 96static int q6v5_wcss_reset(struct q6v5_wcss *wcss)
 97{
 98	int ret;
 99	u32 val;
100	int i;
101
102	/* Assert resets, stop core */
103	val = readl(wcss->reg_base + Q6SS_RESET_REG);
104	val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE;
105	writel(val, wcss->reg_base + Q6SS_RESET_REG);
106
107	/* BHS require xo cbcr to be enabled */
108	val = readl(wcss->reg_base + Q6SS_XO_CBCR);
109	val |= 0x1;
110	writel(val, wcss->reg_base + Q6SS_XO_CBCR);
111
112	/* Read CLKOFF bit to go low indicating CLK is enabled */
113	ret = readl_poll_timeout(wcss->reg_base + Q6SS_XO_CBCR,
114				 val, !(val & BIT(31)), 1,
115				 HALT_CHECK_MAX_LOOPS);
116	if (ret) {
117		dev_err(wcss->dev,
118			"xo cbcr enabling timed out (rc:%d)\n", ret);
119		return ret;
120	}
121	/* Enable power block headswitch and wait for it to stabilize */
122	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
123	val |= Q6SS_BHS_ON;
124	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
125	udelay(1);
126
127	/* Put LDO in bypass mode */
128	val |= Q6SS_LDO_BYP;
129	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
130
131	/* Deassert Q6 compiler memory clamp */
132	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
133	val &= ~Q6SS_CLAMP_QMC_MEM;
134	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
135
136	/* Deassert memory peripheral sleep and L2 memory standby */
137	val |= Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N;
138	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
139
140	/* Turn on L1, L2, ETB and JU memories 1 at a time */
141	val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
142	for (i = MEM_BANKS; i >= 0; i--) {
143		val |= BIT(i);
144		writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL);
145		/*
146		 * Read back value to ensure the write is done then
147		 * wait for 1us for both memory peripheral and data
148		 * array to turn on.
149		 */
150		val |= readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
151		udelay(1);
152	}
153	/* Remove word line clamp */
154	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
155	val &= ~Q6SS_CLAMP_WL;
156	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
157
158	/* Remove IO clamp */
159	val &= ~Q6SS_CLAMP_IO;
160	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
161
162	/* Bring core out of reset */
163	val = readl(wcss->reg_base + Q6SS_RESET_REG);
164	val &= ~Q6SS_CORE_ARES;
165	writel(val, wcss->reg_base + Q6SS_RESET_REG);
166
167	/* Turn on core clock */
168	val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
169	val |= Q6SS_CLK_ENABLE;
170	writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
171
172	/* Start core execution */
173	val = readl(wcss->reg_base + Q6SS_RESET_REG);
174	val &= ~Q6SS_STOP_CORE;
175	writel(val, wcss->reg_base + Q6SS_RESET_REG);
176
177	return 0;
178}
179
180static int q6v5_wcss_start(struct rproc *rproc)
181{
182	struct q6v5_wcss *wcss = rproc->priv;
183	int ret;
184
185	qcom_q6v5_prepare(&wcss->q6v5);
186
187	/* Release Q6 and WCSS reset */
188	ret = reset_control_deassert(wcss->wcss_reset);
189	if (ret) {
190		dev_err(wcss->dev, "wcss_reset failed\n");
191		return ret;
192	}
193
194	ret = reset_control_deassert(wcss->wcss_q6_reset);
195	if (ret) {
196		dev_err(wcss->dev, "wcss_q6_reset failed\n");
197		goto wcss_reset;
198	}
199
200	/* Lithium configuration - clock gating and bus arbitration */
201	ret = regmap_update_bits(wcss->halt_map,
202				 wcss->halt_nc + TCSR_GLOBAL_CFG0,
203				 TCSR_WCSS_CLK_MASK,
204				 TCSR_WCSS_CLK_ENABLE);
205	if (ret)
206		goto wcss_q6_reset;
207
208	ret = regmap_update_bits(wcss->halt_map,
209				 wcss->halt_nc + TCSR_GLOBAL_CFG1,
210				 1, 0);
211	if (ret)
212		goto wcss_q6_reset;
213
214	/* Write bootaddr to EVB so that Q6WCSS will jump there after reset */
215	writel(rproc->bootaddr >> 4, wcss->reg_base + Q6SS_RST_EVB);
216
217	ret = q6v5_wcss_reset(wcss);
218	if (ret)
219		goto wcss_q6_reset;
220
221	ret = qcom_q6v5_wait_for_start(&wcss->q6v5, 5 * HZ);
222	if (ret == -ETIMEDOUT)
223		dev_err(wcss->dev, "start timed out\n");
224
225	return ret;
226
227wcss_q6_reset:
228	reset_control_assert(wcss->wcss_q6_reset);
229
230wcss_reset:
231	reset_control_assert(wcss->wcss_reset);
232
233	return ret;
234}
235
236static void q6v5_wcss_halt_axi_port(struct q6v5_wcss *wcss,
237				    struct regmap *halt_map,
238				    u32 offset)
239{
240	unsigned long timeout;
241	unsigned int val;
242	int ret;
243
244	/* Check if we're already idle */
245	ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);
246	if (!ret && val)
247		return;
248
249	/* Assert halt request */
250	regmap_write(halt_map, offset + AXI_HALTREQ_REG, 1);
251
252	/* Wait for halt */
253	timeout = jiffies + msecs_to_jiffies(HALT_ACK_TIMEOUT_MS);
254	for (;;) {
255		ret = regmap_read(halt_map, offset + AXI_HALTACK_REG, &val);
256		if (ret || val || time_after(jiffies, timeout))
257			break;
258
259		msleep(1);
260	}
261
262	ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);
263	if (ret || !val)
264		dev_err(wcss->dev, "port failed halt\n");
265
266	/* Clear halt request (port will remain halted until reset) */
267	regmap_write(halt_map, offset + AXI_HALTREQ_REG, 0);
268}
269
270static int q6v5_wcss_powerdown(struct q6v5_wcss *wcss)
271{
272	int ret;
273	u32 val;
274
275	/* 1 - Assert WCSS/Q6 HALTREQ */
276	q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_wcss);
277
278	/* 2 - Enable WCSSAON_CONFIG */
279	val = readl(wcss->rmb_base + SSCAON_CONFIG);
280	val |= SSCAON_ENABLE;
281	writel(val, wcss->rmb_base + SSCAON_CONFIG);
282
283	/* 3 - Set SSCAON_CONFIG */
284	val |= SSCAON_BUS_EN;
285	val &= ~SSCAON_BUS_MUX_MASK;
286	writel(val, wcss->rmb_base + SSCAON_CONFIG);
287
288	/* 4 - SSCAON_CONFIG 1 */
289	val |= BIT(1);
290	writel(val, wcss->rmb_base + SSCAON_CONFIG);
291
292	/* 5 - wait for SSCAON_STATUS */
293	ret = readl_poll_timeout(wcss->rmb_base + SSCAON_STATUS,
294				 val, (val & 0xffff) == 0x400, 1000,
295				 HALT_CHECK_MAX_LOOPS);
296	if (ret) {
297		dev_err(wcss->dev,
298			"can't get SSCAON_STATUS rc:%d)\n", ret);
299		return ret;
300	}
301
302	/* 6 - De-assert WCSS_AON reset */
303	reset_control_assert(wcss->wcss_aon_reset);
304
305	/* 7 - Disable WCSSAON_CONFIG 13 */
306	val = readl(wcss->rmb_base + SSCAON_CONFIG);
307	val &= ~SSCAON_ENABLE;
308	writel(val, wcss->rmb_base + SSCAON_CONFIG);
309
310	/* 8 - De-assert WCSS/Q6 HALTREQ */
311	reset_control_assert(wcss->wcss_reset);
312
313	return 0;
314}
315
316static int q6v5_q6_powerdown(struct q6v5_wcss *wcss)
317{
318	int ret;
319	u32 val;
320	int i;
321
322	/* 1 - Halt Q6 bus interface */
323	q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_q6);
324
325	/* 2 - Disable Q6 Core clock */
326	val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
327	val &= ~Q6SS_CLK_ENABLE;
328	writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
329
330	/* 3 - Clamp I/O */
331	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
332	val |= Q6SS_CLAMP_IO;
333	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
334
335	/* 4 - Clamp WL */
336	val |= QDSS_BHS_ON;
337	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
338
339	/* 5 - Clear Erase standby */
340	val &= ~Q6SS_L2DATA_STBY_N;
341	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
342
343	/* 6 - Clear Sleep RTN */
344	val &= ~Q6SS_SLP_RET_N;
345	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
346
347	/* 7 - turn off Q6 memory foot/head switch one bank at a time */
348	for (i = 0; i < 20; i++) {
349		val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
350		val &= ~BIT(i);
351		writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL);
352		mdelay(1);
353	}
354
355	/* 8 - Assert QMC memory RTN */
356	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
357	val |= Q6SS_CLAMP_QMC_MEM;
358	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
359
360	/* 9 - Turn off BHS */
361	val &= ~Q6SS_BHS_ON;
362	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
363	udelay(1);
364
365	/* 10 - Wait till BHS Reset is done */
366	ret = readl_poll_timeout(wcss->reg_base + Q6SS_BHS_STATUS,
367				 val, !(val & BHS_EN_REST_ACK), 1000,
368				 HALT_CHECK_MAX_LOOPS);
369	if (ret) {
370		dev_err(wcss->dev, "BHS_STATUS not OFF (rc:%d)\n", ret);
371		return ret;
372	}
373
374	/* 11 -  Assert WCSS reset */
375	reset_control_assert(wcss->wcss_reset);
376
377	/* 12 - Assert Q6 reset */
378	reset_control_assert(wcss->wcss_q6_reset);
379
380	return 0;
381}
382
383static int q6v5_wcss_stop(struct rproc *rproc)
384{
385	struct q6v5_wcss *wcss = rproc->priv;
386	int ret;
387
388	/* WCSS powerdown */
389	ret = qcom_q6v5_request_stop(&wcss->q6v5);
390	if (ret == -ETIMEDOUT) {
391		dev_err(wcss->dev, "timed out on wait\n");
392		return ret;
393	}
394
395	ret = q6v5_wcss_powerdown(wcss);
396	if (ret)
397		return ret;
398
399	/* Q6 Power down */
400	ret = q6v5_q6_powerdown(wcss);
401	if (ret)
402		return ret;
403
404	qcom_q6v5_unprepare(&wcss->q6v5);
405
406	return 0;
407}
408
409static void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, int len)
410{
411	struct q6v5_wcss *wcss = rproc->priv;
412	int offset;
413
414	offset = da - wcss->mem_reloc;
415	if (offset < 0 || offset + len > wcss->mem_size)
416		return NULL;
417
418	return wcss->mem_region + offset;
419}
420
421static int q6v5_wcss_load(struct rproc *rproc, const struct firmware *fw)
422{
423	struct q6v5_wcss *wcss = rproc->priv;
424
425	return qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware,
426				     0, wcss->mem_region, wcss->mem_phys,
427				     wcss->mem_size, &wcss->mem_reloc);
428}
429
430static const struct rproc_ops q6v5_wcss_ops = {
431	.start = q6v5_wcss_start,
432	.stop = q6v5_wcss_stop,
433	.da_to_va = q6v5_wcss_da_to_va,
434	.load = q6v5_wcss_load,
435	.get_boot_addr = rproc_elf_get_boot_addr,
436};
437
438static int q6v5_wcss_init_reset(struct q6v5_wcss *wcss)
439{
440	struct device *dev = wcss->dev;
441
442	wcss->wcss_aon_reset = devm_reset_control_get(dev, "wcss_aon_reset");
443	if (IS_ERR(wcss->wcss_aon_reset)) {
444		dev_err(wcss->dev, "unable to acquire wcss_aon_reset\n");
445		return PTR_ERR(wcss->wcss_aon_reset);
446	}
447
448	wcss->wcss_reset = devm_reset_control_get(dev, "wcss_reset");
449	if (IS_ERR(wcss->wcss_reset)) {
450		dev_err(wcss->dev, "unable to acquire wcss_reset\n");
451		return PTR_ERR(wcss->wcss_reset);
452	}
453
454	wcss->wcss_q6_reset = devm_reset_control_get(dev, "wcss_q6_reset");
455	if (IS_ERR(wcss->wcss_q6_reset)) {
456		dev_err(wcss->dev, "unable to acquire wcss_q6_reset\n");
457		return PTR_ERR(wcss->wcss_q6_reset);
458	}
459
460	return 0;
461}
462
463static int q6v5_wcss_init_mmio(struct q6v5_wcss *wcss,
464			       struct platform_device *pdev)
465{
466	struct of_phandle_args args;
467	struct resource *res;
468	int ret;
469
470	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6");
471	wcss->reg_base = devm_ioremap_resource(&pdev->dev, res);
472	if (IS_ERR(wcss->reg_base))
473		return PTR_ERR(wcss->reg_base);
474
475	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb");
476	wcss->rmb_base = devm_ioremap_resource(&pdev->dev, res);
477	if (IS_ERR(wcss->rmb_base))
478		return PTR_ERR(wcss->rmb_base);
479
480	ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
481					       "qcom,halt-regs", 3, 0, &args);
482	if (ret < 0) {
483		dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n");
484		return -EINVAL;
485	}
486
487	wcss->halt_map = syscon_node_to_regmap(args.np);
488	of_node_put(args.np);
489	if (IS_ERR(wcss->halt_map))
490		return PTR_ERR(wcss->halt_map);
491
492	wcss->halt_q6 = args.args[0];
493	wcss->halt_wcss = args.args[1];
494	wcss->halt_nc = args.args[2];
495
496	return 0;
497}
498
499static int q6v5_alloc_memory_region(struct q6v5_wcss *wcss)
500{
501	struct reserved_mem *rmem = NULL;
502	struct device_node *node;
503	struct device *dev = wcss->dev;
504
505	node = of_parse_phandle(dev->of_node, "memory-region", 0);
506	if (node)
507		rmem = of_reserved_mem_lookup(node);
508	of_node_put(node);
509
510	if (!rmem) {
511		dev_err(dev, "unable to acquire memory-region\n");
512		return -EINVAL;
513	}
514
515	wcss->mem_phys = rmem->base;
516	wcss->mem_reloc = rmem->base;
517	wcss->mem_size = rmem->size;
518	wcss->mem_region = devm_ioremap_wc(dev, wcss->mem_phys, wcss->mem_size);
519	if (!wcss->mem_region) {
520		dev_err(dev, "unable to map memory region: %pa+%pa\n",
521			&rmem->base, &rmem->size);
522		return -EBUSY;
523	}
524
525	return 0;
526}
527
528static int q6v5_wcss_probe(struct platform_device *pdev)
529{
530	struct q6v5_wcss *wcss;
531	struct rproc *rproc;
532	int ret;
533
534	rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_wcss_ops,
535			    "IPQ8074/q6_fw.mdt", sizeof(*wcss));
536	if (!rproc) {
537		dev_err(&pdev->dev, "failed to allocate rproc\n");
538		return -ENOMEM;
539	}
540
541	wcss = rproc->priv;
542	wcss->dev = &pdev->dev;
543
544	ret = q6v5_wcss_init_mmio(wcss, pdev);
545	if (ret)
546		goto free_rproc;
547
548	ret = q6v5_alloc_memory_region(wcss);
549	if (ret)
550		goto free_rproc;
551
552	ret = q6v5_wcss_init_reset(wcss);
553	if (ret)
554		goto free_rproc;
555
556	ret = qcom_q6v5_init(&wcss->q6v5, pdev, rproc, WCSS_CRASH_REASON, NULL);
557	if (ret)
558		goto free_rproc;
559
560	ret = rproc_add(rproc);
561	if (ret)
562		goto free_rproc;
563
564	platform_set_drvdata(pdev, rproc);
565
566	return 0;
567
568free_rproc:
569	rproc_free(rproc);
570
571	return ret;
572}
573
574static int q6v5_wcss_remove(struct platform_device *pdev)
575{
576	struct rproc *rproc = platform_get_drvdata(pdev);
577
578	rproc_del(rproc);
579	rproc_free(rproc);
580
581	return 0;
582}
583
584static const struct of_device_id q6v5_wcss_of_match[] = {
585	{ .compatible = "qcom,ipq8074-wcss-pil" },
586	{ },
587};
588MODULE_DEVICE_TABLE(of, q6v5_wcss_of_match);
589
590static struct platform_driver q6v5_wcss_driver = {
591	.probe = q6v5_wcss_probe,
592	.remove = q6v5_wcss_remove,
593	.driver = {
594		.name = "qcom-q6v5-wcss-pil",
595		.of_match_table = q6v5_wcss_of_match,
596	},
597};
598module_platform_driver(q6v5_wcss_driver);
599
600MODULE_DESCRIPTION("Hexagon WCSS Peripheral Image Loader");
601MODULE_LICENSE("GPL v2");