blob: 32cce1dd8e52fdb68a984828f90a4802bc0ab61e [file] [log] [blame]
Stephen Boydcc0f5342011-12-29 17:28:57 -08001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Stephen Boydeb819882011-08-29 14:46:30 -07002 *
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>
Stephen Boyded630b02012-01-26 15:26:47 -080022#include <linux/clk.h>
Stephen Boydeb819882011-08-29 14:46:30 -070023
Matt Wagantall6e4aafb2011-09-09 17:53:54 -070024#include <mach/msm_bus.h>
Stephen Boydeb819882011-08-29 14:46:30 -070025#include <mach/msm_iomap.h>
26
27#include "peripheral-loader.h"
28#include "pil-q6v4.h"
29#include "scm-pas.h"
30
31#define QDSP6SS_RST_EVB 0x0
32#define QDSP6SS_RESET 0x04
Stephen Boydeb819882011-08-29 14:46:30 -070033#define QDSP6SS_STRAP_TCM 0x1C
34#define QDSP6SS_STRAP_AHB 0x20
35#define QDSP6SS_GFMUX_CTL 0x30
36#define QDSP6SS_PWR_CTL 0x38
37
38#define MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C70)
39#define MSS_SLP_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C60)
40#define SFAB_MSS_M_ACLK_CTL (MSM_CLK_CTL_BASE + 0x2340)
41#define SFAB_MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C00)
42#define MSS_RESET (MSM_CLK_CTL_BASE + 0x2C64)
43
44#define Q6SS_SS_ARES BIT(0)
45#define Q6SS_CORE_ARES BIT(1)
46#define Q6SS_ISDB_ARES BIT(2)
47#define Q6SS_ETM_ARES BIT(3)
48#define Q6SS_STOP_CORE_ARES BIT(4)
49#define Q6SS_PRIV_ARES BIT(5)
50
51#define Q6SS_L2DATA_SLP_NRET_N BIT(0)
52#define Q6SS_SLP_RET_N BIT(1)
53#define Q6SS_L1TCM_SLP_NRET_N BIT(2)
54#define Q6SS_L2TAG_SLP_NRET_N BIT(3)
55#define Q6SS_ETB_SLEEP_NRET_N BIT(4)
56#define Q6SS_ARR_STBY_N BIT(5)
57#define Q6SS_CLAMP_IO BIT(6)
58
59#define Q6SS_CLK_ENA BIT(1)
60#define Q6SS_SRC_SWITCH_CLK_OVR BIT(8)
Stephen Boydeb819882011-08-29 14:46:30 -070061
62struct q6v4_data {
63 void __iomem *base;
64 void __iomem *modem_base;
65 unsigned long start_addr;
66 struct regulator *vreg;
Stephen Boydcc0f5342011-12-29 17:28:57 -080067 struct regulator *pll_supply;
Stephen Boydeb819882011-08-29 14:46:30 -070068 bool vreg_enabled;
Stephen Boyded630b02012-01-26 15:26:47 -080069 struct clk *xo;
Stephen Boyd6d67d252011-09-27 11:50:05 -070070 struct pil_device *pil;
Stephen Boydeb819882011-08-29 14:46:30 -070071};
72
73static int pil_q6v4_init_image(struct pil_desc *pil, const u8 *metadata,
74 size_t size)
75{
76 const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
77 struct q6v4_data *drv = dev_get_drvdata(pil->dev);
78 drv->start_addr = ehdr->e_entry;
79 return 0;
80}
81
Stephen Boyd0280ff22012-03-22 10:59:22 -070082static int pil_q6v4_make_proxy_votes(struct pil_desc *pil)
Matt Wagantall39088932011-08-02 20:24:56 -070083{
Stephen Boyd0280ff22012-03-22 10:59:22 -070084 const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
Stephen Boydcc0f5342011-12-29 17:28:57 -080085 int ret;
Matt Wagantall39088932011-08-02 20:24:56 -070086
Stephen Boyded630b02012-01-26 15:26:47 -080087 ret = clk_prepare_enable(drv->xo);
88 if (ret) {
Stephen Boyd0280ff22012-03-22 10:59:22 -070089 dev_err(pil->dev, "Failed to enable XO\n");
Stephen Boyded630b02012-01-26 15:26:47 -080090 goto err;
91 }
Stephen Boydcc0f5342011-12-29 17:28:57 -080092 if (drv->pll_supply) {
93 ret = regulator_enable(drv->pll_supply);
Stephen Boyded630b02012-01-26 15:26:47 -080094 if (ret) {
Stephen Boyd0280ff22012-03-22 10:59:22 -070095 dev_err(pil->dev, "Failed to enable pll supply\n");
Stephen Boyded630b02012-01-26 15:26:47 -080096 goto err_regulator;
97 }
Stephen Boydcc0f5342011-12-29 17:28:57 -080098 }
Stephen Boyded630b02012-01-26 15:26:47 -080099 return 0;
100err_regulator:
101 clk_disable_unprepare(drv->xo);
102err:
103 return ret;
Matt Wagantall39088932011-08-02 20:24:56 -0700104}
105
Stephen Boyd0280ff22012-03-22 10:59:22 -0700106static void pil_q6v4_remove_proxy_votes(struct pil_desc *pil)
Matt Wagantall39088932011-08-02 20:24:56 -0700107{
Stephen Boyd0280ff22012-03-22 10:59:22 -0700108 const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
Stephen Boydcc0f5342011-12-29 17:28:57 -0800109 if (drv->pll_supply)
110 regulator_disable(drv->pll_supply);
Stephen Boyded630b02012-01-26 15:26:47 -0800111 clk_disable_unprepare(drv->xo);
Matt Wagantall39088932011-08-02 20:24:56 -0700112}
113
Stephen Boydeb819882011-08-29 14:46:30 -0700114static int pil_q6v4_power_up(struct device *dev)
115{
116 int err;
117 struct q6v4_data *drv = dev_get_drvdata(dev);
118
Stephen Boydfdf9aa32012-07-25 15:35:21 -0700119 err = regulator_set_voltage(drv->vreg, 743750, 743750);
Stephen Boydeb819882011-08-29 14:46:30 -0700120 if (err) {
Matt Wagantalled36d822012-05-25 18:13:40 -0700121 dev_err(dev, "Failed to set regulator's voltage step.\n");
Stephen Boydeb819882011-08-29 14:46:30 -0700122 return err;
123 }
124 err = regulator_enable(drv->vreg);
125 if (err) {
126 dev_err(dev, "Failed to enable regulator.\n");
127 return err;
128 }
Matt Wagantalled36d822012-05-25 18:13:40 -0700129
130 /*
131 * Q6 hardware requires a two step voltage ramp-up.
132 * Delay between the steps.
133 */
134 udelay(100);
135
136 err = regulator_set_voltage(drv->vreg, 1050000, 1050000);
137 if (err) {
138 dev_err(dev, "Failed to set regulator's voltage.\n");
139 return err;
140 }
Stephen Boydeb819882011-08-29 14:46:30 -0700141 drv->vreg_enabled = true;
142 return 0;
143}
144
145static DEFINE_MUTEX(pil_q6v4_modem_lock);
146static unsigned pil_q6v4_modem_count;
147
148/* Bring modem subsystem out of reset */
149static void pil_q6v4_init_modem(void __iomem *base, void __iomem *jtag_clk)
150{
151 mutex_lock(&pil_q6v4_modem_lock);
152 if (!pil_q6v4_modem_count) {
153 /* Enable MSS clocks */
154 writel_relaxed(0x10, SFAB_MSS_M_ACLK_CTL);
155 writel_relaxed(0x10, SFAB_MSS_S_HCLK_CTL);
156 writel_relaxed(0x10, MSS_S_HCLK_CTL);
157 writel_relaxed(0x10, MSS_SLP_CLK_CTL);
158 /* Wait for clocks to enable */
159 mb();
160 udelay(10);
161
162 /* De-assert MSS reset */
163 writel_relaxed(0x0, MSS_RESET);
164 mb();
165 udelay(10);
166 /* Enable MSS */
167 writel_relaxed(0x7, base);
168 }
169
170 /* Enable JTAG clocks */
171 /* TODO: Remove if/when Q6 software enables them? */
172 writel_relaxed(0x10, jtag_clk);
173
174 pil_q6v4_modem_count++;
175 mutex_unlock(&pil_q6v4_modem_lock);
176}
177
178/* Put modem subsystem back into reset */
179static void pil_q6v4_shutdown_modem(void)
180{
181 mutex_lock(&pil_q6v4_modem_lock);
182 if (pil_q6v4_modem_count)
183 pil_q6v4_modem_count--;
184 if (pil_q6v4_modem_count == 0)
185 writel_relaxed(0x1, MSS_RESET);
186 mutex_unlock(&pil_q6v4_modem_lock);
187}
188
189static int pil_q6v4_reset(struct pil_desc *pil)
190{
Stephen Boyd0280ff22012-03-22 10:59:22 -0700191 u32 reg, err;
Stephen Boydeb819882011-08-29 14:46:30 -0700192 const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
193 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
194
195 err = pil_q6v4_power_up(pil->dev);
196 if (err)
197 return err;
198 /* Enable Q6 ACLK */
199 writel_relaxed(0x10, pdata->aclk_reg);
200
201 if (drv->modem_base)
202 pil_q6v4_init_modem(drv->modem_base, pdata->jtag_clk_reg);
203
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700204 /* Unhalt bus port */
205 err = msm_bus_axi_portunhalt(pdata->bus_port);
206 if (err)
207 dev_err(pil->dev, "Failed to unhalt bus port\n");
208
Stephen Boydeb819882011-08-29 14:46:30 -0700209 /* Deassert Q6SS_SS_ARES */
210 reg = readl_relaxed(drv->base + QDSP6SS_RESET);
211 reg &= ~(Q6SS_SS_ARES);
212 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
213
214 /* Program boot address */
215 writel_relaxed((drv->start_addr >> 8) & 0xFFFFFF,
216 drv->base + QDSP6SS_RST_EVB);
217
218 /* Program TCM and AHB address ranges */
219 writel_relaxed(pdata->strap_tcm_base, drv->base + QDSP6SS_STRAP_TCM);
220 writel_relaxed(pdata->strap_ahb_upper | pdata->strap_ahb_lower,
221 drv->base + QDSP6SS_STRAP_AHB);
222
223 /* Turn off Q6 core clock */
224 writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR,
225 drv->base + QDSP6SS_GFMUX_CTL);
226
227 /* Put memories to sleep */
228 writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL);
229
230 /* Assert resets */
231 reg = readl_relaxed(drv->base + QDSP6SS_RESET);
232 reg |= (Q6SS_CORE_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES
233 | Q6SS_STOP_CORE_ARES);
234 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
235
236 /* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
237 mb();
238 usleep_range(20, 30);
239
240 /* Turn on Q6 memories */
241 reg = Q6SS_L2DATA_SLP_NRET_N | Q6SS_SLP_RET_N | Q6SS_L1TCM_SLP_NRET_N
242 | Q6SS_L2TAG_SLP_NRET_N | Q6SS_ETB_SLEEP_NRET_N | Q6SS_ARR_STBY_N
243 | Q6SS_CLAMP_IO;
244 writel_relaxed(reg, drv->base + QDSP6SS_PWR_CTL);
245
246 /* Turn on Q6 core clock */
247 reg = Q6SS_CLK_ENA | Q6SS_SRC_SWITCH_CLK_OVR;
248 writel_relaxed(reg, drv->base + QDSP6SS_GFMUX_CTL);
249
250 /* Remove Q6SS_CLAMP_IO */
251 reg = readl_relaxed(drv->base + QDSP6SS_PWR_CTL);
252 reg &= ~Q6SS_CLAMP_IO;
253 writel_relaxed(reg, drv->base + QDSP6SS_PWR_CTL);
254
255 /* Bring Q6 core out of reset and start execution. */
256 writel_relaxed(0x0, drv->base + QDSP6SS_RESET);
257
Stephen Boydeb819882011-08-29 14:46:30 -0700258 return 0;
259}
260
261static int pil_q6v4_shutdown(struct pil_desc *pil)
262{
263 u32 reg;
264 struct q6v4_data *drv = dev_get_drvdata(pil->dev);
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700265 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
266
267 /* Make sure bus port is halted */
268 msm_bus_axi_porthalt(pdata->bus_port);
Stephen Boydeb819882011-08-29 14:46:30 -0700269
270 /* Turn off Q6 core clock */
271 writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR,
272 drv->base + QDSP6SS_GFMUX_CTL);
273
274 /* Assert resets */
275 reg = (Q6SS_SS_ARES | Q6SS_CORE_ARES | Q6SS_ISDB_ARES
276 | Q6SS_ETM_ARES | Q6SS_STOP_CORE_ARES | Q6SS_PRIV_ARES);
277 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
278
279 /* Turn off Q6 memories */
280 writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL);
281
282 if (drv->modem_base)
283 pil_q6v4_shutdown_modem();
284
285 if (drv->vreg_enabled) {
286 regulator_disable(drv->vreg);
287 drv->vreg_enabled = false;
288 }
289
290 return 0;
291}
292
293static struct pil_reset_ops pil_q6v4_ops = {
294 .init_image = pil_q6v4_init_image,
Stephen Boydeb819882011-08-29 14:46:30 -0700295 .auth_and_reset = pil_q6v4_reset,
296 .shutdown = pil_q6v4_shutdown,
Stephen Boyd0280ff22012-03-22 10:59:22 -0700297 .proxy_vote = pil_q6v4_make_proxy_votes,
298 .proxy_unvote = pil_q6v4_remove_proxy_votes,
Stephen Boydeb819882011-08-29 14:46:30 -0700299};
300
301static int pil_q6v4_init_image_trusted(struct pil_desc *pil,
302 const u8 *metadata, size_t size)
303{
304 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
305 return pas_init_image(pdata->pas_id, metadata, size);
306}
307
308static int pil_q6v4_reset_trusted(struct pil_desc *pil)
309{
310 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
311 int err;
312
313 err = pil_q6v4_power_up(pil->dev);
314 if (err)
315 return err;
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700316
317 /* Unhalt bus port */
318 err = msm_bus_axi_portunhalt(pdata->bus_port);
319 if (err)
320 dev_err(pil->dev, "Failed to unhalt bus port\n");
Stephen Boydeb819882011-08-29 14:46:30 -0700321 return pas_auth_and_reset(pdata->pas_id);
322}
323
324static int pil_q6v4_shutdown_trusted(struct pil_desc *pil)
325{
326 int ret;
327 struct q6v4_data *drv = dev_get_drvdata(pil->dev);
328 struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
329
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700330 /* Make sure bus port is halted */
331 msm_bus_axi_porthalt(pdata->bus_port);
332
Stephen Boydeb819882011-08-29 14:46:30 -0700333 ret = pas_shutdown(pdata->pas_id);
334 if (ret)
335 return ret;
336
337 if (drv->vreg_enabled) {
338 regulator_disable(drv->vreg);
339 drv->vreg_enabled = false;
340 }
341
342 return ret;
343}
344
345static struct pil_reset_ops pil_q6v4_ops_trusted = {
346 .init_image = pil_q6v4_init_image_trusted,
Stephen Boydeb819882011-08-29 14:46:30 -0700347 .auth_and_reset = pil_q6v4_reset_trusted,
348 .shutdown = pil_q6v4_shutdown_trusted,
Stephen Boyd0280ff22012-03-22 10:59:22 -0700349 .proxy_vote = pil_q6v4_make_proxy_votes,
350 .proxy_unvote = pil_q6v4_remove_proxy_votes,
Stephen Boydeb819882011-08-29 14:46:30 -0700351};
352
353static int __devinit pil_q6v4_driver_probe(struct platform_device *pdev)
354{
355 const struct pil_q6v4_pdata *pdata = pdev->dev.platform_data;
356 struct q6v4_data *drv;
357 struct resource *res;
358 struct pil_desc *desc;
Stephen Boydcc0f5342011-12-29 17:28:57 -0800359 int ret;
Stephen Boydeb819882011-08-29 14:46:30 -0700360
361 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
362 if (!res)
363 return -EINVAL;
364
365 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
366 if (!drv)
367 return -ENOMEM;
368 platform_set_drvdata(pdev, drv);
369
370 drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
371 if (!drv->base)
372 return -ENOMEM;
373
374 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
375 if (res) {
376 drv->modem_base = devm_ioremap(&pdev->dev, res->start,
377 resource_size(res));
378 if (!drv->modem_base)
379 return -ENOMEM;
380 }
381
382 desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
Stephen Boydcc0f5342011-12-29 17:28:57 -0800383 if (!desc)
Stephen Boydeb819882011-08-29 14:46:30 -0700384 return -ENOMEM;
385
Stephen Boyd4f524242012-03-23 15:03:58 -0700386 drv->pll_supply = devm_regulator_get(&pdev->dev, "pll_vdd");
Stephen Boydcc0f5342011-12-29 17:28:57 -0800387 if (IS_ERR(drv->pll_supply)) {
388 drv->pll_supply = NULL;
389 } else {
390 ret = regulator_set_voltage(drv->pll_supply, 1800000, 1800000);
391 if (ret) {
392 dev_err(&pdev->dev, "failed to set pll voltage\n");
Stephen Boyd4dde31b2012-03-23 15:23:10 -0700393 return ret;
Stephen Boydcc0f5342011-12-29 17:28:57 -0800394 }
395
396 ret = regulator_set_optimum_mode(drv->pll_supply, 100000);
397 if (ret < 0) {
398 dev_err(&pdev->dev, "failed to set pll optimum mode\n");
Stephen Boyd4dde31b2012-03-23 15:23:10 -0700399 return ret;
Stephen Boydcc0f5342011-12-29 17:28:57 -0800400 }
401 }
402
Stephen Boydeb819882011-08-29 14:46:30 -0700403 desc->name = pdata->name;
404 desc->depends_on = pdata->depends;
405 desc->dev = &pdev->dev;
Stephen Boyd6d67d252011-09-27 11:50:05 -0700406 desc->owner = THIS_MODULE;
Stephen Boyd0280ff22012-03-22 10:59:22 -0700407 desc->proxy_timeout = 10000;
Stephen Boydeb819882011-08-29 14:46:30 -0700408
409 if (pas_supported(pdata->pas_id) > 0) {
410 desc->ops = &pil_q6v4_ops_trusted;
411 dev_info(&pdev->dev, "using secure boot\n");
412 } else {
413 desc->ops = &pil_q6v4_ops;
414 dev_info(&pdev->dev, "using non-secure boot\n");
415 }
416
Stephen Boyd4f524242012-03-23 15:03:58 -0700417 drv->vreg = devm_regulator_get(&pdev->dev, "core_vdd");
Stephen Boyd4dde31b2012-03-23 15:23:10 -0700418 if (IS_ERR(drv->vreg))
419 return PTR_ERR(drv->vreg);
Stephen Boydcc0f5342011-12-29 17:28:57 -0800420
Matt Wagantalled36d822012-05-25 18:13:40 -0700421 ret = regulator_set_optimum_mode(drv->vreg, 100000);
422 if (ret < 0) {
423 dev_err(&pdev->dev, "Failed to set regulator's mode.\n");
424 return ret;
425 }
426
Stephen Boyd4dde31b2012-03-23 15:23:10 -0700427 drv->xo = devm_clk_get(&pdev->dev, "xo");
428 if (IS_ERR(drv->xo))
429 return PTR_ERR(drv->xo);
Stephen Boydcc0f5342011-12-29 17:28:57 -0800430
Stephen Boyd6d67d252011-09-27 11:50:05 -0700431 drv->pil = msm_pil_register(desc);
Stephen Boyd4dde31b2012-03-23 15:23:10 -0700432 if (IS_ERR(drv->pil))
433 return PTR_ERR(drv->pil);
Stephen Boydeb819882011-08-29 14:46:30 -0700434 return 0;
435}
436
437static int __devexit pil_q6v4_driver_exit(struct platform_device *pdev)
438{
439 struct q6v4_data *drv = platform_get_drvdata(pdev);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700440 msm_pil_unregister(drv->pil);
Stephen Boydeb819882011-08-29 14:46:30 -0700441 return 0;
442}
443
444static struct platform_driver pil_q6v4_driver = {
445 .probe = pil_q6v4_driver_probe,
446 .remove = __devexit_p(pil_q6v4_driver_exit),
447 .driver = {
448 .name = "pil_qdsp6v4",
449 .owner = THIS_MODULE,
450 },
451};
452
453static int __init pil_q6v4_init(void)
454{
455 return platform_driver_register(&pil_q6v4_driver);
456}
457module_init(pil_q6v4_init);
458
459static void __exit pil_q6v4_exit(void)
460{
461 platform_driver_unregister(&pil_q6v4_driver);
462}
463module_exit(pil_q6v4_exit);
464
465MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
466MODULE_LICENSE("GPL v2");