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