blob: 6e85fbc66b0105ff9f18e35374967419bde7273b [file] [log] [blame]
Stephen Boydeb819882011-08-29 14:46:30 -07001/* Copyright (c) 2011, Code Aurora Forum. 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#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/platform_device.h>
16#include <linux/io.h>
17#include <linux/ioport.h>
18#include <linux/regulator/consumer.h>
19#include <linux/elf.h>
20#include <linux/delay.h>
21#include <linux/err.h>
22
23#include <mach/msm_iomap.h>
Matt Wagantall39088932011-08-02 20:24:56 -070024#include <mach/msm_xo.h>
Stephen Boydeb819882011-08-29 14:46:30 -070025
26#include "peripheral-loader.h"
27#include "pil-q6v4.h"
28#include "scm-pas.h"
29
Matt Wagantall39088932011-08-02 20:24:56 -070030#define PROXY_VOTE_TIMEOUT 10000
31
Stephen Boydeb819882011-08-29 14:46:30 -070032#define QDSP6SS_RST_EVB 0x0
33#define QDSP6SS_RESET 0x04
34#define QDSP6SS_CGC_OVERRIDE 0x18
35#define QDSP6SS_STRAP_TCM 0x1C
36#define QDSP6SS_STRAP_AHB 0x20
37#define QDSP6SS_GFMUX_CTL 0x30
38#define QDSP6SS_PWR_CTL 0x38
39
40#define MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C70)
41#define MSS_SLP_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C60)
42#define SFAB_MSS_M_ACLK_CTL (MSM_CLK_CTL_BASE + 0x2340)
43#define SFAB_MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C00)
44#define MSS_RESET (MSM_CLK_CTL_BASE + 0x2C64)
45
46#define Q6SS_SS_ARES BIT(0)
47#define Q6SS_CORE_ARES BIT(1)
48#define Q6SS_ISDB_ARES BIT(2)
49#define Q6SS_ETM_ARES BIT(3)
50#define Q6SS_STOP_CORE_ARES BIT(4)
51#define Q6SS_PRIV_ARES BIT(5)
52
53#define Q6SS_L2DATA_SLP_NRET_N BIT(0)
54#define Q6SS_SLP_RET_N BIT(1)
55#define Q6SS_L1TCM_SLP_NRET_N BIT(2)
56#define Q6SS_L2TAG_SLP_NRET_N BIT(3)
57#define Q6SS_ETB_SLEEP_NRET_N BIT(4)
58#define Q6SS_ARR_STBY_N BIT(5)
59#define Q6SS_CLAMP_IO BIT(6)
60
61#define Q6SS_CLK_ENA BIT(1)
62#define Q6SS_SRC_SWITCH_CLK_OVR BIT(8)
63#define Q6SS_AXIS_ACLK_EN BIT(9)
64
65struct q6v4_data {
66 void __iomem *base;
67 void __iomem *modem_base;
68 unsigned long start_addr;
69 struct regulator *vreg;
70 bool vreg_enabled;
Matt Wagantall39088932011-08-02 20:24:56 -070071 struct msm_xo_voter *xo;
72 struct timer_list xo_timer;
Stephen Boydeb819882011-08-29 14:46:30 -070073};
74
75static int pil_q6v4_init_image(struct pil_desc *pil, const u8 *metadata,
76 size_t size)
77{
78 const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
79 struct q6v4_data *drv = dev_get_drvdata(pil->dev);
80 drv->start_addr = ehdr->e_entry;
81 return 0;
82}
83
84static int nop_verify_blob(struct pil_desc *pil, u32 phy_addr, size_t size)
85{
86 return 0;
87}
88
Matt Wagantall39088932011-08-02 20:24:56 -070089static void pil_q6v4_make_xo_proxy_votes(struct device *dev)
90{
91 struct q6v4_data *drv = dev_get_drvdata(dev);
92
93 msm_xo_mode_vote(drv->xo, MSM_XO_MODE_ON);
94 mod_timer(&drv->xo_timer, jiffies+msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
95}
96
97static void pil_q6v4_remove_xo_proxy_votes(unsigned long data)
98{
99 struct q6v4_data *drv = (struct q6v4_data *)data;
100
101 msm_xo_mode_vote(drv->xo, MSM_XO_MODE_OFF);
102}
103
104static void pil_q6v4_remove_xo_proxy_votes_now(struct device *dev)
105{
106 struct q6v4_data *drv = dev_get_drvdata(dev);
107
108 if (del_timer(&drv->xo_timer))
109 pil_q6v4_remove_xo_proxy_votes((unsigned long)drv);
110}
111
Stephen Boydeb819882011-08-29 14:46:30 -0700112static int pil_q6v4_power_up(struct device *dev)
113{
114 int err;
115 struct q6v4_data *drv = dev_get_drvdata(dev);
116
117 err = regulator_set_voltage(drv->vreg, 1050000, 1050000);
118 if (err) {
119 dev_err(dev, "Failed to set regulator's voltage.\n");
120 return err;
121 }
122 err = regulator_set_optimum_mode(drv->vreg, 100000);
123 if (err < 0) {
124 dev_err(dev, "Failed to set regulator's mode.\n");
125 return err;
126 }
127 err = regulator_enable(drv->vreg);
128 if (err) {
129 dev_err(dev, "Failed to enable regulator.\n");
130 return err;
131 }
132 drv->vreg_enabled = true;
133 return 0;
134}
135
136static DEFINE_MUTEX(pil_q6v4_modem_lock);
137static unsigned pil_q6v4_modem_count;
138
139/* Bring modem subsystem out of reset */
140static void pil_q6v4_init_modem(void __iomem *base, void __iomem *jtag_clk)
141{
142 mutex_lock(&pil_q6v4_modem_lock);
143 if (!pil_q6v4_modem_count) {
144 /* Enable MSS clocks */
145 writel_relaxed(0x10, SFAB_MSS_M_ACLK_CTL);
146 writel_relaxed(0x10, SFAB_MSS_S_HCLK_CTL);
147 writel_relaxed(0x10, MSS_S_HCLK_CTL);
148 writel_relaxed(0x10, MSS_SLP_CLK_CTL);
149 /* Wait for clocks to enable */
150 mb();
151 udelay(10);
152
153 /* De-assert MSS reset */
154 writel_relaxed(0x0, MSS_RESET);
155 mb();
156 udelay(10);
157 /* Enable MSS */
158 writel_relaxed(0x7, base);
159 }
160
161 /* Enable JTAG clocks */
162 /* TODO: Remove if/when Q6 software enables them? */
163 writel_relaxed(0x10, jtag_clk);
164
165 pil_q6v4_modem_count++;
166 mutex_unlock(&pil_q6v4_modem_lock);
167}
168
169/* Put modem subsystem back into reset */
170static void pil_q6v4_shutdown_modem(void)
171{
172 mutex_lock(&pil_q6v4_modem_lock);
173 if (pil_q6v4_modem_count)
174 pil_q6v4_modem_count--;
175 if (pil_q6v4_modem_count == 0)
176 writel_relaxed(0x1, MSS_RESET);
177 mutex_unlock(&pil_q6v4_modem_lock);
178}
179
180static int pil_q6v4_reset(struct pil_desc *pil)
181{
182 u32 reg, err = 0;
183 const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
184 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
185
Matt Wagantall39088932011-08-02 20:24:56 -0700186 pil_q6v4_make_xo_proxy_votes(pil->dev);
187
Stephen Boydeb819882011-08-29 14:46:30 -0700188 err = pil_q6v4_power_up(pil->dev);
189 if (err)
190 return err;
191 /* Enable Q6 ACLK */
192 writel_relaxed(0x10, pdata->aclk_reg);
193
194 if (drv->modem_base)
195 pil_q6v4_init_modem(drv->modem_base, pdata->jtag_clk_reg);
196
197 /*
198 * Assert AXIS_ACLK_EN override to allow for correct updating of the
199 * QDSP6_CORE_STATE status bit. This is mandatory only for the SW Q6
200 * in 8960v1 and optional elsewhere.
201 */
202 reg = readl_relaxed(drv->base + QDSP6SS_CGC_OVERRIDE);
203 reg |= Q6SS_AXIS_ACLK_EN;
204 writel_relaxed(reg, drv->base + QDSP6SS_CGC_OVERRIDE);
205
206 /* Deassert Q6SS_SS_ARES */
207 reg = readl_relaxed(drv->base + QDSP6SS_RESET);
208 reg &= ~(Q6SS_SS_ARES);
209 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
210
211 /* Program boot address */
212 writel_relaxed((drv->start_addr >> 8) & 0xFFFFFF,
213 drv->base + QDSP6SS_RST_EVB);
214
215 /* Program TCM and AHB address ranges */
216 writel_relaxed(pdata->strap_tcm_base, drv->base + QDSP6SS_STRAP_TCM);
217 writel_relaxed(pdata->strap_ahb_upper | pdata->strap_ahb_lower,
218 drv->base + QDSP6SS_STRAP_AHB);
219
220 /* Turn off Q6 core clock */
221 writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR,
222 drv->base + QDSP6SS_GFMUX_CTL);
223
224 /* Put memories to sleep */
225 writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL);
226
227 /* Assert resets */
228 reg = readl_relaxed(drv->base + QDSP6SS_RESET);
229 reg |= (Q6SS_CORE_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES
230 | Q6SS_STOP_CORE_ARES);
231 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
232
233 /* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
234 mb();
235 usleep_range(20, 30);
236
237 /* Turn on Q6 memories */
238 reg = Q6SS_L2DATA_SLP_NRET_N | Q6SS_SLP_RET_N | Q6SS_L1TCM_SLP_NRET_N
239 | Q6SS_L2TAG_SLP_NRET_N | Q6SS_ETB_SLEEP_NRET_N | Q6SS_ARR_STBY_N
240 | Q6SS_CLAMP_IO;
241 writel_relaxed(reg, drv->base + QDSP6SS_PWR_CTL);
242
243 /* Turn on Q6 core clock */
244 reg = Q6SS_CLK_ENA | Q6SS_SRC_SWITCH_CLK_OVR;
245 writel_relaxed(reg, drv->base + QDSP6SS_GFMUX_CTL);
246
247 /* Remove Q6SS_CLAMP_IO */
248 reg = readl_relaxed(drv->base + QDSP6SS_PWR_CTL);
249 reg &= ~Q6SS_CLAMP_IO;
250 writel_relaxed(reg, drv->base + QDSP6SS_PWR_CTL);
251
252 /* Bring Q6 core out of reset and start execution. */
253 writel_relaxed(0x0, drv->base + QDSP6SS_RESET);
254
255 /*
256 * Re-enable auto-gating of AXIS_ACLK at lease one AXI clock cycle
257 * after resets are de-asserted.
258 */
259 mb();
260 usleep_range(1, 10);
261 reg = readl_relaxed(drv->base + QDSP6SS_CGC_OVERRIDE);
262 reg &= ~Q6SS_AXIS_ACLK_EN;
263 writel_relaxed(reg, drv->base + QDSP6SS_CGC_OVERRIDE);
264
265 return 0;
266}
267
268static int pil_q6v4_shutdown(struct pil_desc *pil)
269{
270 u32 reg;
271 struct q6v4_data *drv = dev_get_drvdata(pil->dev);
272
273 /* Turn off Q6 core clock */
274 writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR,
275 drv->base + QDSP6SS_GFMUX_CTL);
276
277 /* Assert resets */
278 reg = (Q6SS_SS_ARES | Q6SS_CORE_ARES | Q6SS_ISDB_ARES
279 | Q6SS_ETM_ARES | Q6SS_STOP_CORE_ARES | Q6SS_PRIV_ARES);
280 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
281
282 /* Turn off Q6 memories */
283 writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL);
284
285 if (drv->modem_base)
286 pil_q6v4_shutdown_modem();
287
288 if (drv->vreg_enabled) {
289 regulator_disable(drv->vreg);
290 drv->vreg_enabled = false;
291 }
292
Matt Wagantall39088932011-08-02 20:24:56 -0700293 pil_q6v4_remove_xo_proxy_votes_now(pil->dev);
294
Stephen Boydeb819882011-08-29 14:46:30 -0700295 return 0;
296}
297
298static struct pil_reset_ops pil_q6v4_ops = {
299 .init_image = pil_q6v4_init_image,
300 .verify_blob = nop_verify_blob,
301 .auth_and_reset = pil_q6v4_reset,
302 .shutdown = pil_q6v4_shutdown,
303};
304
305static int pil_q6v4_init_image_trusted(struct pil_desc *pil,
306 const u8 *metadata, size_t size)
307{
308 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
309 return pas_init_image(pdata->pas_id, metadata, size);
310}
311
312static int pil_q6v4_reset_trusted(struct pil_desc *pil)
313{
314 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
315 int err;
316
Matt Wagantall39088932011-08-02 20:24:56 -0700317 pil_q6v4_make_xo_proxy_votes(pil->dev);
318
Stephen Boydeb819882011-08-29 14:46:30 -0700319 err = pil_q6v4_power_up(pil->dev);
320 if (err)
321 return err;
322 return pas_auth_and_reset(pdata->pas_id);
323}
324
325static int pil_q6v4_shutdown_trusted(struct pil_desc *pil)
326{
327 int ret;
328 struct q6v4_data *drv = dev_get_drvdata(pil->dev);
329 struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
330
331 ret = pas_shutdown(pdata->pas_id);
332 if (ret)
333 return ret;
334
335 if (drv->vreg_enabled) {
336 regulator_disable(drv->vreg);
337 drv->vreg_enabled = false;
338 }
339
Matt Wagantall39088932011-08-02 20:24:56 -0700340 pil_q6v4_remove_xo_proxy_votes_now(pil->dev);
341
Stephen Boydeb819882011-08-29 14:46:30 -0700342 return ret;
343}
344
345static struct pil_reset_ops pil_q6v4_ops_trusted = {
346 .init_image = pil_q6v4_init_image_trusted,
347 .verify_blob = nop_verify_blob,
348 .auth_and_reset = pil_q6v4_reset_trusted,
349 .shutdown = pil_q6v4_shutdown_trusted,
350};
351
352static int __devinit pil_q6v4_driver_probe(struct platform_device *pdev)
353{
354 const struct pil_q6v4_pdata *pdata = pdev->dev.platform_data;
355 struct q6v4_data *drv;
356 struct resource *res;
357 struct pil_desc *desc;
358
359 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
360 if (!res)
361 return -EINVAL;
362
363 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
364 if (!drv)
365 return -ENOMEM;
366 platform_set_drvdata(pdev, drv);
367
368 drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
369 if (!drv->base)
370 return -ENOMEM;
371
372 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
373 if (res) {
374 drv->modem_base = devm_ioremap(&pdev->dev, res->start,
375 resource_size(res));
376 if (!drv->modem_base)
377 return -ENOMEM;
378 }
379
380 desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
381 if (!drv)
382 return -ENOMEM;
383
384 desc->name = pdata->name;
385 desc->depends_on = pdata->depends;
386 desc->dev = &pdev->dev;
387
388 if (pas_supported(pdata->pas_id) > 0) {
389 desc->ops = &pil_q6v4_ops_trusted;
390 dev_info(&pdev->dev, "using secure boot\n");
391 } else {
392 desc->ops = &pil_q6v4_ops;
393 dev_info(&pdev->dev, "using non-secure boot\n");
394 }
395
396 drv->vreg = regulator_get(&pdev->dev, "core_vdd");
397 if (IS_ERR(drv->vreg))
398 return PTR_ERR(drv->vreg);
399
Matt Wagantall39088932011-08-02 20:24:56 -0700400 setup_timer(&drv->xo_timer, pil_q6v4_remove_xo_proxy_votes,
401 (unsigned long)drv);
402 drv->xo = msm_xo_get(pdata->xo_id, pdata->name);
403 if (IS_ERR(drv->xo))
404 return PTR_ERR(drv->xo);
405
Stephen Boydeb819882011-08-29 14:46:30 -0700406 if (msm_pil_register(desc)) {
407 regulator_put(drv->vreg);
408 return -EINVAL;
409 }
410 return 0;
411}
412
413static int __devexit pil_q6v4_driver_exit(struct platform_device *pdev)
414{
415 struct q6v4_data *drv = platform_get_drvdata(pdev);
416 regulator_put(drv->vreg);
417 return 0;
418}
419
420static struct platform_driver pil_q6v4_driver = {
421 .probe = pil_q6v4_driver_probe,
422 .remove = __devexit_p(pil_q6v4_driver_exit),
423 .driver = {
424 .name = "pil_qdsp6v4",
425 .owner = THIS_MODULE,
426 },
427};
428
429static int __init pil_q6v4_init(void)
430{
431 return platform_driver_register(&pil_q6v4_driver);
432}
433module_init(pil_q6v4_init);
434
435static void __exit pil_q6v4_exit(void)
436{
437 platform_driver_unregister(&pil_q6v4_driver);
438}
439module_exit(pil_q6v4_exit);
440
441MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
442MODULE_LICENSE("GPL v2");