blob: 5eac539b37ba6b41ef753f95994ace7d59d13e1f [file] [log] [blame]
Matt Wagantallc2bbdc32012-03-21 19:44:50 -07001/*
2 * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/init.h>
15#include <linux/module.h>
16#include <linux/platform_device.h>
17#include <linux/io.h>
18#include <linux/iopoll.h>
19#include <linux/err.h>
20#include <linux/of.h>
21
22#include "peripheral-loader.h"
23#include "pil-q6v5.h"
24
25/* Register Offsets */
26#define QDSP6SS_RST_EVB 0x010
27#define LPASS_Q6SS_BCR 0x06000
28#define LPASS_Q6SS_AHB_LFABIF_CBCR 0x22000
29#define LPASS_Q6SS_XO_CBCR 0x26000
30#define AXI_HALTREQ 0x0
31#define AXI_HALTACK 0x4
32#define AXI_IDLE 0x8
33
34#define HALT_ACK_TIMEOUT_US 100000
35
36static void clk_reg_enable(void __iomem *reg)
37{
38 u32 val;
39 val = readl_relaxed(reg);
40 val |= BIT(0);
41 writel_relaxed(val, reg);
42}
43
44static void clk_reg_disable(void __iomem *reg)
45{
46 u32 val;
47 val = readl_relaxed(reg);
48 val &= ~BIT(0);
49 writel_relaxed(val, reg);
50}
51
52static int pil_lpass_shutdown(struct pil_desc *pil)
53{
54 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
55 int ret;
56 u32 status;
57
58 writel_relaxed(1, drv->axi_halt_base + AXI_HALTREQ);
59 ret = readl_poll_timeout(drv->axi_halt_base + AXI_HALTACK,
60 status, status, 50, HALT_ACK_TIMEOUT_US);
61 if (ret)
62 dev_err(pil->dev, "Port halt timeout\n");
63 else if (!readl_relaxed(drv->axi_halt_base + AXI_IDLE))
64 dev_err(pil->dev, "Port halt failed\n");
65 writel_relaxed(0, drv->axi_halt_base + AXI_HALTREQ);
66
67 /* Make sure Q6 registers are accessible */
68 writel_relaxed(0, drv->clk_base + LPASS_Q6SS_BCR);
69 clk_reg_enable(drv->clk_base + LPASS_Q6SS_AHB_LFABIF_CBCR);
70 mb();
71
72 pil_q6v5_shutdown(pil);
73
74 /* Disable clocks and assert subsystem resets. */
75 clk_reg_disable(drv->clk_base + LPASS_Q6SS_AHB_LFABIF_CBCR);
76 clk_reg_disable(drv->clk_base + LPASS_Q6SS_XO_CBCR);
77 writel_relaxed(1, drv->clk_base + LPASS_Q6SS_BCR);
78
79 return 0;
80}
81
82static int pil_lpass_reset(struct pil_desc *pil)
83{
84 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
85
86 /*
87 * Bring subsystem out of reset and enable required
88 * regulators and clocks.
89 */
90 writel_relaxed(0, drv->clk_base + LPASS_Q6SS_BCR);
91 clk_reg_enable(drv->clk_base + LPASS_Q6SS_XO_CBCR);
92 clk_reg_enable(drv->clk_base + LPASS_Q6SS_AHB_LFABIF_CBCR);
93 mb();
94
95 /* Program Image Address */
96 writel_relaxed(((drv->start_addr >> 4) & 0x0FFFFFF0),
97 drv->reg_base + QDSP6SS_RST_EVB);
98
99 return pil_q6v5_reset(pil);
100}
101
102static struct pil_reset_ops pil_lpass_ops = {
103 .init_image = pil_q6v5_init_image,
104 .proxy_vote = pil_q6v5_make_proxy_votes,
105 .proxy_unvote = pil_q6v5_remove_proxy_votes,
106 .auth_and_reset = pil_lpass_reset,
107 .shutdown = pil_lpass_shutdown,
108};
109
110static int __devinit pil_lpass_driver_probe(struct platform_device *pdev)
111{
112 struct q6v5_data *drv;
113 struct pil_desc *desc;
114 struct resource *res;
115
116 desc = pil_q6v5_init(pdev);
Matt Wagantall55252f12012-05-02 18:02:54 -0700117 if (IS_ERR(desc))
118 return PTR_ERR(desc);
119
Matt Wagantallc2bbdc32012-03-21 19:44:50 -0700120 drv = platform_get_drvdata(pdev);
Matt Wagantall55252f12012-05-02 18:02:54 -0700121 if (drv == NULL)
122 return -ENODEV;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -0700123
124 desc->ops = &pil_lpass_ops;
125 desc->owner = THIS_MODULE;
126
127 res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
128 drv->axi_halt_base = devm_ioremap(&pdev->dev, res->start,
129 resource_size(res));
130 if (!drv->axi_halt_base)
131 return -ENOMEM;
132
133 drv->pil = msm_pil_register(desc);
134 if (IS_ERR(drv->pil))
135 return PTR_ERR(drv->pil);
136
137 return 0;
138}
139
140static int __devexit pil_lpass_driver_exit(struct platform_device *pdev)
141{
142 struct q6v5_data *drv = platform_get_drvdata(pdev);
143 msm_pil_unregister(drv->pil);
144 return 0;
145}
146
147static struct of_device_id lpass_match_table[] = {
148 { .compatible = "qcom,pil-q6v5-lpass" },
149 {}
150};
151
152static struct platform_driver pil_lpass_driver = {
153 .probe = pil_lpass_driver_probe,
154 .remove = __devexit_p(pil_lpass_driver_exit),
155 .driver = {
156 .name = "pil-q6v5-lpass",
157 .of_match_table = lpass_match_table,
158 .owner = THIS_MODULE,
159 },
160};
161
162static int __init pil_lpass_init(void)
163{
164 return platform_driver_register(&pil_lpass_driver);
165}
166module_init(pil_lpass_init);
167
168static void __exit pil_lpass_exit(void)
169{
170 platform_driver_unregister(&pil_lpass_driver);
171}
172module_exit(pil_lpass_exit);
173
174MODULE_DESCRIPTION("Support for booting low-power audio subsystems with QDSP6v5 (Hexagon) processors");
175MODULE_LICENSE("GPL v2");