blob: d5a552b4ed076fbe6b105d1e963eca25178110c3 [file] [log] [blame]
Matt Wagantallc2bbdc32012-03-21 19:44:50 -07001/*
Duy Truonge833aca2013-02-12 13:35:08 -08002 * Copyright (c) 2012, The Linux Foundation. All rights reserved.
Matt Wagantallc2bbdc32012-03-21 19:44:50 -07003 *
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>
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070018#include <linux/err.h>
19#include <linux/of.h>
Matt Wagantalld41ce772012-05-10 23:16:41 -070020#include <linux/clk.h>
Matt Wagantall8c2246d2012-08-12 17:08:04 -070021#include <mach/clk.h>
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070022#include "peripheral-loader.h"
23#include "pil-q6v5.h"
Matt Wagantallc40e28f2012-07-26 11:52:02 -070024#include "scm-pas.h"
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070025
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070026#define QDSP6SS_RST_EVB 0x010
Matt Wagantall4d89c2e2012-05-25 19:28:34 -070027#define PROXY_TIMEOUT_MS 10000
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070028
Matt Wagantall8c2246d2012-08-12 17:08:04 -070029static int pil_lpass_enable_clks(struct q6v5_data *drv)
30{
31 int ret;
32
33 ret = clk_reset(drv->core_clk, CLK_RESET_DEASSERT);
34 if (ret)
35 goto err_reset;
36 ret = clk_prepare_enable(drv->core_clk);
37 if (ret)
38 goto err_core_clk;
39 ret = clk_prepare_enable(drv->ahb_clk);
40 if (ret)
41 goto err_ahb_clk;
42 ret = clk_prepare_enable(drv->axi_clk);
43 if (ret)
44 goto err_axi_clk;
45 ret = clk_prepare_enable(drv->reg_clk);
46 if (ret)
47 goto err_reg_clk;
48
49 return 0;
50
51err_reg_clk:
52 clk_disable_unprepare(drv->axi_clk);
53err_axi_clk:
54 clk_disable_unprepare(drv->ahb_clk);
55err_ahb_clk:
56 clk_disable_unprepare(drv->core_clk);
57err_core_clk:
58 clk_reset(drv->core_clk, CLK_RESET_ASSERT);
59err_reset:
60 return ret;
61}
62
63static void pil_lpass_disable_clks(struct q6v5_data *drv)
64{
65 clk_disable_unprepare(drv->reg_clk);
66 clk_disable_unprepare(drv->axi_clk);
67 clk_disable_unprepare(drv->ahb_clk);
68 clk_disable_unprepare(drv->core_clk);
69 clk_reset(drv->core_clk, CLK_RESET_ASSERT);
70}
71
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070072static int pil_lpass_shutdown(struct pil_desc *pil)
73{
74 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070075
Matt Wagantallb7747992012-05-11 19:37:51 -070076 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base);
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070077
Matt Wagantalld41ce772012-05-10 23:16:41 -070078 /*
79 * If the shutdown function is called before the reset function, clocks
80 * will not be enabled yet. Enable them here so that register writes
81 * performed during the shutdown succeed.
82 */
83 if (drv->is_booted == false)
Matt Wagantall8c2246d2012-08-12 17:08:04 -070084 pil_lpass_enable_clks(drv);
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070085
86 pil_q6v5_shutdown(pil);
Matt Wagantall8c2246d2012-08-12 17:08:04 -070087 pil_lpass_disable_clks(drv);
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070088
Matt Wagantalld41ce772012-05-10 23:16:41 -070089 drv->is_booted = false;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070090
91 return 0;
92}
93
94static int pil_lpass_reset(struct pil_desc *pil)
95{
96 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
Matt Wagantalld41ce772012-05-10 23:16:41 -070097 int ret;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070098
Matt Wagantall8c2246d2012-08-12 17:08:04 -070099 ret = pil_lpass_enable_clks(drv);
Matt Wagantalld41ce772012-05-10 23:16:41 -0700100 if (ret)
101 return ret;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -0700102
103 /* Program Image Address */
104 writel_relaxed(((drv->start_addr >> 4) & 0x0FFFFFF0),
105 drv->reg_base + QDSP6SS_RST_EVB);
106
Matt Wagantalld41ce772012-05-10 23:16:41 -0700107 ret = pil_q6v5_reset(pil);
108 if (ret) {
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700109 pil_lpass_disable_clks(drv);
Matt Wagantalld41ce772012-05-10 23:16:41 -0700110 return ret;
111 }
112
113 drv->is_booted = true;
114
115 return 0;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -0700116}
117
118static struct pil_reset_ops pil_lpass_ops = {
119 .init_image = pil_q6v5_init_image,
120 .proxy_vote = pil_q6v5_make_proxy_votes,
121 .proxy_unvote = pil_q6v5_remove_proxy_votes,
122 .auth_and_reset = pil_lpass_reset,
123 .shutdown = pil_lpass_shutdown,
124};
125
Matt Wagantallc40e28f2012-07-26 11:52:02 -0700126static int pil_lpass_init_image_trusted(struct pil_desc *pil,
127 const u8 *metadata, size_t size)
128{
129 return pas_init_image(PAS_Q6, metadata, size);
130}
131
132static int pil_lpass_reset_trusted(struct pil_desc *pil)
133{
134 return pas_auth_and_reset(PAS_Q6);
135}
136
137static int pil_lpass_shutdown_trusted(struct pil_desc *pil)
138{
139 return pas_shutdown(PAS_Q6);
140}
141
142static struct pil_reset_ops pil_lpass_ops_trusted = {
143 .init_image = pil_lpass_init_image_trusted,
144 .proxy_vote = pil_q6v5_make_proxy_votes,
145 .proxy_unvote = pil_q6v5_remove_proxy_votes,
146 .auth_and_reset = pil_lpass_reset_trusted,
147 .shutdown = pil_lpass_shutdown_trusted,
148};
149
Matt Wagantallc2bbdc32012-03-21 19:44:50 -0700150static int __devinit pil_lpass_driver_probe(struct platform_device *pdev)
151{
152 struct q6v5_data *drv;
153 struct pil_desc *desc;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -0700154
155 desc = pil_q6v5_init(pdev);
Matt Wagantall55252f12012-05-02 18:02:54 -0700156 if (IS_ERR(desc))
157 return PTR_ERR(desc);
158
Matt Wagantallc2bbdc32012-03-21 19:44:50 -0700159 drv = platform_get_drvdata(pdev);
Matt Wagantall55252f12012-05-02 18:02:54 -0700160 if (drv == NULL)
161 return -ENODEV;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -0700162
163 desc->ops = &pil_lpass_ops;
164 desc->owner = THIS_MODULE;
Matt Wagantall4d89c2e2012-05-25 19:28:34 -0700165 desc->proxy_timeout = PROXY_TIMEOUT_MS;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -0700166
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700167 drv->core_clk = devm_clk_get(&pdev->dev, "core_clk");
168 if (IS_ERR(drv->core_clk))
169 return PTR_ERR(drv->core_clk);
170
171 drv->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
172 if (IS_ERR(drv->ahb_clk))
173 return PTR_ERR(drv->ahb_clk);
174
175 drv->axi_clk = devm_clk_get(&pdev->dev, "bus_clk");
176 if (IS_ERR(drv->axi_clk))
177 return PTR_ERR(drv->axi_clk);
178
179 drv->reg_clk = devm_clk_get(&pdev->dev, "reg_clk");
180 if (IS_ERR(drv->reg_clk))
181 return PTR_ERR(drv->reg_clk);
Matt Wagantall56865f02012-08-09 15:03:36 -0700182
Matt Wagantallc40e28f2012-07-26 11:52:02 -0700183 if (pas_supported(PAS_Q6) > 0) {
184 desc->ops = &pil_lpass_ops_trusted;
185 dev_info(&pdev->dev, "using secure boot\n");
186 } else {
187 desc->ops = &pil_lpass_ops;
188 dev_info(&pdev->dev, "using non-secure boot\n");
189 }
190
Matt Wagantallc2bbdc32012-03-21 19:44:50 -0700191 drv->pil = msm_pil_register(desc);
192 if (IS_ERR(drv->pil))
193 return PTR_ERR(drv->pil);
194
195 return 0;
196}
197
198static int __devexit pil_lpass_driver_exit(struct platform_device *pdev)
199{
200 struct q6v5_data *drv = platform_get_drvdata(pdev);
201 msm_pil_unregister(drv->pil);
202 return 0;
203}
204
205static struct of_device_id lpass_match_table[] = {
206 { .compatible = "qcom,pil-q6v5-lpass" },
207 {}
208};
209
210static struct platform_driver pil_lpass_driver = {
211 .probe = pil_lpass_driver_probe,
212 .remove = __devexit_p(pil_lpass_driver_exit),
213 .driver = {
214 .name = "pil-q6v5-lpass",
215 .of_match_table = lpass_match_table,
216 .owner = THIS_MODULE,
217 },
218};
219
220static int __init pil_lpass_init(void)
221{
222 return platform_driver_register(&pil_lpass_driver);
223}
224module_init(pil_lpass_init);
225
226static void __exit pil_lpass_exit(void)
227{
228 platform_driver_unregister(&pil_lpass_driver);
229}
230module_exit(pil_lpass_exit);
231
232MODULE_DESCRIPTION("Support for booting low-power audio subsystems with QDSP6v5 (Hexagon) processors");
233MODULE_LICENSE("GPL v2");