blob: 8446e428cdfebc5ab80c7c0211c2634b9610b158 [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
119 err = regulator_set_voltage(drv->vreg, 1050000, 1050000);
120 if (err) {
121 dev_err(dev, "Failed to set regulator's voltage.\n");
122 return err;
123 }
124 err = regulator_set_optimum_mode(drv->vreg, 100000);
125 if (err < 0) {
126 dev_err(dev, "Failed to set regulator's mode.\n");
127 return err;
128 }
129 err = regulator_enable(drv->vreg);
130 if (err) {
131 dev_err(dev, "Failed to enable regulator.\n");
132 return err;
133 }
134 drv->vreg_enabled = true;
135 return 0;
136}
137
138static DEFINE_MUTEX(pil_q6v4_modem_lock);
139static unsigned pil_q6v4_modem_count;
140
141/* Bring modem subsystem out of reset */
142static void pil_q6v4_init_modem(void __iomem *base, void __iomem *jtag_clk)
143{
144 mutex_lock(&pil_q6v4_modem_lock);
145 if (!pil_q6v4_modem_count) {
146 /* Enable MSS clocks */
147 writel_relaxed(0x10, SFAB_MSS_M_ACLK_CTL);
148 writel_relaxed(0x10, SFAB_MSS_S_HCLK_CTL);
149 writel_relaxed(0x10, MSS_S_HCLK_CTL);
150 writel_relaxed(0x10, MSS_SLP_CLK_CTL);
151 /* Wait for clocks to enable */
152 mb();
153 udelay(10);
154
155 /* De-assert MSS reset */
156 writel_relaxed(0x0, MSS_RESET);
157 mb();
158 udelay(10);
159 /* Enable MSS */
160 writel_relaxed(0x7, base);
161 }
162
163 /* Enable JTAG clocks */
164 /* TODO: Remove if/when Q6 software enables them? */
165 writel_relaxed(0x10, jtag_clk);
166
167 pil_q6v4_modem_count++;
168 mutex_unlock(&pil_q6v4_modem_lock);
169}
170
171/* Put modem subsystem back into reset */
172static void pil_q6v4_shutdown_modem(void)
173{
174 mutex_lock(&pil_q6v4_modem_lock);
175 if (pil_q6v4_modem_count)
176 pil_q6v4_modem_count--;
177 if (pil_q6v4_modem_count == 0)
178 writel_relaxed(0x1, MSS_RESET);
179 mutex_unlock(&pil_q6v4_modem_lock);
180}
181
182static int pil_q6v4_reset(struct pil_desc *pil)
183{
Stephen Boyd0280ff22012-03-22 10:59:22 -0700184 u32 reg, err;
Stephen Boydeb819882011-08-29 14:46:30 -0700185 const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
186 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
187
188 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
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700197 /* Unhalt bus port */
198 err = msm_bus_axi_portunhalt(pdata->bus_port);
199 if (err)
200 dev_err(pil->dev, "Failed to unhalt bus port\n");
201
Stephen Boydeb819882011-08-29 14:46:30 -0700202 /* Deassert Q6SS_SS_ARES */
203 reg = readl_relaxed(drv->base + QDSP6SS_RESET);
204 reg &= ~(Q6SS_SS_ARES);
205 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
206
207 /* Program boot address */
208 writel_relaxed((drv->start_addr >> 8) & 0xFFFFFF,
209 drv->base + QDSP6SS_RST_EVB);
210
211 /* Program TCM and AHB address ranges */
212 writel_relaxed(pdata->strap_tcm_base, drv->base + QDSP6SS_STRAP_TCM);
213 writel_relaxed(pdata->strap_ahb_upper | pdata->strap_ahb_lower,
214 drv->base + QDSP6SS_STRAP_AHB);
215
216 /* Turn off Q6 core clock */
217 writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR,
218 drv->base + QDSP6SS_GFMUX_CTL);
219
220 /* Put memories to sleep */
221 writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL);
222
223 /* Assert resets */
224 reg = readl_relaxed(drv->base + QDSP6SS_RESET);
225 reg |= (Q6SS_CORE_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES
226 | Q6SS_STOP_CORE_ARES);
227 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
228
229 /* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
230 mb();
231 usleep_range(20, 30);
232
233 /* Turn on Q6 memories */
234 reg = Q6SS_L2DATA_SLP_NRET_N | Q6SS_SLP_RET_N | Q6SS_L1TCM_SLP_NRET_N
235 | Q6SS_L2TAG_SLP_NRET_N | Q6SS_ETB_SLEEP_NRET_N | Q6SS_ARR_STBY_N
236 | Q6SS_CLAMP_IO;
237 writel_relaxed(reg, drv->base + QDSP6SS_PWR_CTL);
238
239 /* Turn on Q6 core clock */
240 reg = Q6SS_CLK_ENA | Q6SS_SRC_SWITCH_CLK_OVR;
241 writel_relaxed(reg, drv->base + QDSP6SS_GFMUX_CTL);
242
243 /* Remove Q6SS_CLAMP_IO */
244 reg = readl_relaxed(drv->base + QDSP6SS_PWR_CTL);
245 reg &= ~Q6SS_CLAMP_IO;
246 writel_relaxed(reg, drv->base + QDSP6SS_PWR_CTL);
247
248 /* Bring Q6 core out of reset and start execution. */
249 writel_relaxed(0x0, drv->base + QDSP6SS_RESET);
250
Stephen Boydeb819882011-08-29 14:46:30 -0700251 return 0;
252}
253
254static int pil_q6v4_shutdown(struct pil_desc *pil)
255{
256 u32 reg;
257 struct q6v4_data *drv = dev_get_drvdata(pil->dev);
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700258 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
259
260 /* Make sure bus port is halted */
261 msm_bus_axi_porthalt(pdata->bus_port);
Stephen Boydeb819882011-08-29 14:46:30 -0700262
263 /* Turn off Q6 core clock */
264 writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR,
265 drv->base + QDSP6SS_GFMUX_CTL);
266
267 /* Assert resets */
268 reg = (Q6SS_SS_ARES | Q6SS_CORE_ARES | Q6SS_ISDB_ARES
269 | Q6SS_ETM_ARES | Q6SS_STOP_CORE_ARES | Q6SS_PRIV_ARES);
270 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
271
272 /* Turn off Q6 memories */
273 writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL);
274
275 if (drv->modem_base)
276 pil_q6v4_shutdown_modem();
277
278 if (drv->vreg_enabled) {
279 regulator_disable(drv->vreg);
280 drv->vreg_enabled = false;
281 }
282
283 return 0;
284}
285
286static struct pil_reset_ops pil_q6v4_ops = {
287 .init_image = pil_q6v4_init_image,
Stephen Boydeb819882011-08-29 14:46:30 -0700288 .auth_and_reset = pil_q6v4_reset,
289 .shutdown = pil_q6v4_shutdown,
Stephen Boyd0280ff22012-03-22 10:59:22 -0700290 .proxy_vote = pil_q6v4_make_proxy_votes,
291 .proxy_unvote = pil_q6v4_remove_proxy_votes,
Stephen Boydeb819882011-08-29 14:46:30 -0700292};
293
294static int pil_q6v4_init_image_trusted(struct pil_desc *pil,
295 const u8 *metadata, size_t size)
296{
297 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
298 return pas_init_image(pdata->pas_id, metadata, size);
299}
300
301static int pil_q6v4_reset_trusted(struct pil_desc *pil)
302{
303 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
304 int err;
305
306 err = pil_q6v4_power_up(pil->dev);
307 if (err)
308 return err;
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700309
310 /* Unhalt bus port */
311 err = msm_bus_axi_portunhalt(pdata->bus_port);
312 if (err)
313 dev_err(pil->dev, "Failed to unhalt bus port\n");
Stephen Boydeb819882011-08-29 14:46:30 -0700314 return pas_auth_and_reset(pdata->pas_id);
315}
316
317static int pil_q6v4_shutdown_trusted(struct pil_desc *pil)
318{
319 int ret;
320 struct q6v4_data *drv = dev_get_drvdata(pil->dev);
321 struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
322
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700323 /* Make sure bus port is halted */
324 msm_bus_axi_porthalt(pdata->bus_port);
325
Stephen Boydeb819882011-08-29 14:46:30 -0700326 ret = pas_shutdown(pdata->pas_id);
327 if (ret)
328 return ret;
329
330 if (drv->vreg_enabled) {
331 regulator_disable(drv->vreg);
332 drv->vreg_enabled = false;
333 }
334
335 return ret;
336}
337
338static struct pil_reset_ops pil_q6v4_ops_trusted = {
339 .init_image = pil_q6v4_init_image_trusted,
Stephen Boydeb819882011-08-29 14:46:30 -0700340 .auth_and_reset = pil_q6v4_reset_trusted,
341 .shutdown = pil_q6v4_shutdown_trusted,
Stephen Boyd0280ff22012-03-22 10:59:22 -0700342 .proxy_vote = pil_q6v4_make_proxy_votes,
343 .proxy_unvote = pil_q6v4_remove_proxy_votes,
Stephen Boydeb819882011-08-29 14:46:30 -0700344};
345
346static int __devinit pil_q6v4_driver_probe(struct platform_device *pdev)
347{
348 const struct pil_q6v4_pdata *pdata = pdev->dev.platform_data;
349 struct q6v4_data *drv;
350 struct resource *res;
351 struct pil_desc *desc;
Stephen Boydcc0f5342011-12-29 17:28:57 -0800352 int ret;
Stephen Boydeb819882011-08-29 14:46:30 -0700353
354 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
355 if (!res)
356 return -EINVAL;
357
358 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
359 if (!drv)
360 return -ENOMEM;
361 platform_set_drvdata(pdev, drv);
362
363 drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
364 if (!drv->base)
365 return -ENOMEM;
366
367 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
368 if (res) {
369 drv->modem_base = devm_ioremap(&pdev->dev, res->start,
370 resource_size(res));
371 if (!drv->modem_base)
372 return -ENOMEM;
373 }
374
375 desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
Stephen Boydcc0f5342011-12-29 17:28:57 -0800376 if (!desc)
Stephen Boydeb819882011-08-29 14:46:30 -0700377 return -ENOMEM;
378
Stephen Boyd4f524242012-03-23 15:03:58 -0700379 drv->pll_supply = devm_regulator_get(&pdev->dev, "pll_vdd");
Stephen Boydcc0f5342011-12-29 17:28:57 -0800380 if (IS_ERR(drv->pll_supply)) {
381 drv->pll_supply = NULL;
382 } else {
383 ret = regulator_set_voltage(drv->pll_supply, 1800000, 1800000);
384 if (ret) {
385 dev_err(&pdev->dev, "failed to set pll voltage\n");
Stephen Boyd4dde31b2012-03-23 15:23:10 -0700386 return ret;
Stephen Boydcc0f5342011-12-29 17:28:57 -0800387 }
388
389 ret = regulator_set_optimum_mode(drv->pll_supply, 100000);
390 if (ret < 0) {
391 dev_err(&pdev->dev, "failed to set pll optimum mode\n");
Stephen Boyd4dde31b2012-03-23 15:23:10 -0700392 return ret;
Stephen Boydcc0f5342011-12-29 17:28:57 -0800393 }
394 }
395
Stephen Boydeb819882011-08-29 14:46:30 -0700396 desc->name = pdata->name;
397 desc->depends_on = pdata->depends;
398 desc->dev = &pdev->dev;
Stephen Boyd6d67d252011-09-27 11:50:05 -0700399 desc->owner = THIS_MODULE;
Stephen Boyd0280ff22012-03-22 10:59:22 -0700400 desc->proxy_timeout = 10000;
Stephen Boydeb819882011-08-29 14:46:30 -0700401
402 if (pas_supported(pdata->pas_id) > 0) {
403 desc->ops = &pil_q6v4_ops_trusted;
404 dev_info(&pdev->dev, "using secure boot\n");
405 } else {
406 desc->ops = &pil_q6v4_ops;
407 dev_info(&pdev->dev, "using non-secure boot\n");
408 }
409
Stephen Boyd4f524242012-03-23 15:03:58 -0700410 drv->vreg = devm_regulator_get(&pdev->dev, "core_vdd");
Stephen Boyd4dde31b2012-03-23 15:23:10 -0700411 if (IS_ERR(drv->vreg))
412 return PTR_ERR(drv->vreg);
Stephen Boydcc0f5342011-12-29 17:28:57 -0800413
Stephen Boyd4dde31b2012-03-23 15:23:10 -0700414 drv->xo = devm_clk_get(&pdev->dev, "xo");
415 if (IS_ERR(drv->xo))
416 return PTR_ERR(drv->xo);
Stephen Boydcc0f5342011-12-29 17:28:57 -0800417
Stephen Boyd6d67d252011-09-27 11:50:05 -0700418 drv->pil = msm_pil_register(desc);
Stephen Boyd4dde31b2012-03-23 15:23:10 -0700419 if (IS_ERR(drv->pil))
420 return PTR_ERR(drv->pil);
Stephen Boydeb819882011-08-29 14:46:30 -0700421 return 0;
422}
423
424static int __devexit pil_q6v4_driver_exit(struct platform_device *pdev)
425{
426 struct q6v4_data *drv = platform_get_drvdata(pdev);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700427 msm_pil_unregister(drv->pil);
Stephen Boydeb819882011-08-29 14:46:30 -0700428 return 0;
429}
430
431static struct platform_driver pil_q6v4_driver = {
432 .probe = pil_q6v4_driver_probe,
433 .remove = __devexit_p(pil_q6v4_driver_exit),
434 .driver = {
435 .name = "pil_qdsp6v4",
436 .owner = THIS_MODULE,
437 },
438};
439
440static int __init pil_q6v4_init(void)
441{
442 return platform_driver_register(&pil_q6v4_driver);
443}
444module_init(pil_q6v4_init);
445
446static void __exit pil_q6v4_exit(void)
447{
448 platform_driver_unregister(&pil_q6v4_driver);
449}
450module_exit(pil_q6v4_exit);
451
452MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
453MODULE_LICENSE("GPL v2");