blob: cdbfc482475d3d5640a00a9a3ff28a59f6296df3 [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 Boydcc0f5342011-12-29 17:28:57 -080022#include <linux/workqueue.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>
Matt Wagantall39088932011-08-02 20:24:56 -070026#include <mach/msm_xo.h>
Stephen Boydeb819882011-08-29 14:46:30 -070027
28#include "peripheral-loader.h"
29#include "pil-q6v4.h"
30#include "scm-pas.h"
31
Matt Wagantall44175262011-12-16 15:36:14 -080032#define PROXY_VOTE_TIMEOUT 40000
Matt Wagantall39088932011-08-02 20:24:56 -070033
Stephen Boydeb819882011-08-29 14:46:30 -070034#define QDSP6SS_RST_EVB 0x0
35#define QDSP6SS_RESET 0x04
36#define QDSP6SS_CGC_OVERRIDE 0x18
37#define QDSP6SS_STRAP_TCM 0x1C
38#define QDSP6SS_STRAP_AHB 0x20
39#define QDSP6SS_GFMUX_CTL 0x30
40#define QDSP6SS_PWR_CTL 0x38
41
42#define MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C70)
43#define MSS_SLP_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C60)
44#define SFAB_MSS_M_ACLK_CTL (MSM_CLK_CTL_BASE + 0x2340)
45#define SFAB_MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C00)
46#define MSS_RESET (MSM_CLK_CTL_BASE + 0x2C64)
47
48#define Q6SS_SS_ARES BIT(0)
49#define Q6SS_CORE_ARES BIT(1)
50#define Q6SS_ISDB_ARES BIT(2)
51#define Q6SS_ETM_ARES BIT(3)
52#define Q6SS_STOP_CORE_ARES BIT(4)
53#define Q6SS_PRIV_ARES BIT(5)
54
55#define Q6SS_L2DATA_SLP_NRET_N BIT(0)
56#define Q6SS_SLP_RET_N BIT(1)
57#define Q6SS_L1TCM_SLP_NRET_N BIT(2)
58#define Q6SS_L2TAG_SLP_NRET_N BIT(3)
59#define Q6SS_ETB_SLEEP_NRET_N BIT(4)
60#define Q6SS_ARR_STBY_N BIT(5)
61#define Q6SS_CLAMP_IO BIT(6)
62
63#define Q6SS_CLK_ENA BIT(1)
64#define Q6SS_SRC_SWITCH_CLK_OVR BIT(8)
65#define Q6SS_AXIS_ACLK_EN BIT(9)
66
67struct q6v4_data {
68 void __iomem *base;
69 void __iomem *modem_base;
70 unsigned long start_addr;
71 struct regulator *vreg;
Stephen Boydcc0f5342011-12-29 17:28:57 -080072 struct regulator *pll_supply;
Stephen Boydeb819882011-08-29 14:46:30 -070073 bool vreg_enabled;
Matt Wagantall39088932011-08-02 20:24:56 -070074 struct msm_xo_voter *xo;
Stephen Boydcc0f5342011-12-29 17:28:57 -080075 struct delayed_work work;
Stephen Boydeb819882011-08-29 14:46:30 -070076};
77
78static int pil_q6v4_init_image(struct pil_desc *pil, const u8 *metadata,
79 size_t size)
80{
81 const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
82 struct q6v4_data *drv = dev_get_drvdata(pil->dev);
83 drv->start_addr = ehdr->e_entry;
84 return 0;
85}
86
87static int nop_verify_blob(struct pil_desc *pil, u32 phy_addr, size_t size)
88{
89 return 0;
90}
91
Stephen Boydcc0f5342011-12-29 17:28:57 -080092static void pil_q6v4_make_proxy_votes(struct device *dev)
Matt Wagantall39088932011-08-02 20:24:56 -070093{
94 struct q6v4_data *drv = dev_get_drvdata(dev);
Stephen Boydcc0f5342011-12-29 17:28:57 -080095 int ret;
Matt Wagantall39088932011-08-02 20:24:56 -070096
97 msm_xo_mode_vote(drv->xo, MSM_XO_MODE_ON);
Stephen Boydcc0f5342011-12-29 17:28:57 -080098 if (drv->pll_supply) {
99 ret = regulator_enable(drv->pll_supply);
100 if (ret)
101 dev_err(dev, "failed to enable pll supply\n");
102 }
103 schedule_delayed_work(&drv->work, msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
Matt Wagantall39088932011-08-02 20:24:56 -0700104}
105
Stephen Boydcc0f5342011-12-29 17:28:57 -0800106static void pil_q6v4_remove_proxy_votes(struct work_struct *work)
Matt Wagantall39088932011-08-02 20:24:56 -0700107{
Stephen Boydcc0f5342011-12-29 17:28:57 -0800108 struct q6v4_data *drv = container_of(work, struct q6v4_data, work.work);
109 if (drv->pll_supply)
110 regulator_disable(drv->pll_supply);
Matt Wagantall39088932011-08-02 20:24:56 -0700111 msm_xo_mode_vote(drv->xo, MSM_XO_MODE_OFF);
112}
113
Stephen Boydcc0f5342011-12-29 17:28:57 -0800114static void pil_q6v4_remove_proxy_votes_now(struct device *dev)
Matt Wagantall39088932011-08-02 20:24:56 -0700115{
116 struct q6v4_data *drv = dev_get_drvdata(dev);
Stephen Boydcc0f5342011-12-29 17:28:57 -0800117 flush_delayed_work(&drv->work);
Matt Wagantall39088932011-08-02 20:24:56 -0700118}
119
Stephen Boydeb819882011-08-29 14:46:30 -0700120static int pil_q6v4_power_up(struct device *dev)
121{
122 int err;
123 struct q6v4_data *drv = dev_get_drvdata(dev);
124
125 err = regulator_set_voltage(drv->vreg, 1050000, 1050000);
126 if (err) {
127 dev_err(dev, "Failed to set regulator's voltage.\n");
128 return err;
129 }
130 err = regulator_set_optimum_mode(drv->vreg, 100000);
131 if (err < 0) {
132 dev_err(dev, "Failed to set regulator's mode.\n");
133 return err;
134 }
135 err = regulator_enable(drv->vreg);
136 if (err) {
137 dev_err(dev, "Failed to enable regulator.\n");
138 return err;
139 }
140 drv->vreg_enabled = true;
141 return 0;
142}
143
144static DEFINE_MUTEX(pil_q6v4_modem_lock);
145static unsigned pil_q6v4_modem_count;
146
147/* Bring modem subsystem out of reset */
148static void pil_q6v4_init_modem(void __iomem *base, void __iomem *jtag_clk)
149{
150 mutex_lock(&pil_q6v4_modem_lock);
151 if (!pil_q6v4_modem_count) {
152 /* Enable MSS clocks */
153 writel_relaxed(0x10, SFAB_MSS_M_ACLK_CTL);
154 writel_relaxed(0x10, SFAB_MSS_S_HCLK_CTL);
155 writel_relaxed(0x10, MSS_S_HCLK_CTL);
156 writel_relaxed(0x10, MSS_SLP_CLK_CTL);
157 /* Wait for clocks to enable */
158 mb();
159 udelay(10);
160
161 /* De-assert MSS reset */
162 writel_relaxed(0x0, MSS_RESET);
163 mb();
164 udelay(10);
165 /* Enable MSS */
166 writel_relaxed(0x7, base);
167 }
168
169 /* Enable JTAG clocks */
170 /* TODO: Remove if/when Q6 software enables them? */
171 writel_relaxed(0x10, jtag_clk);
172
173 pil_q6v4_modem_count++;
174 mutex_unlock(&pil_q6v4_modem_lock);
175}
176
177/* Put modem subsystem back into reset */
178static void pil_q6v4_shutdown_modem(void)
179{
180 mutex_lock(&pil_q6v4_modem_lock);
181 if (pil_q6v4_modem_count)
182 pil_q6v4_modem_count--;
183 if (pil_q6v4_modem_count == 0)
184 writel_relaxed(0x1, MSS_RESET);
185 mutex_unlock(&pil_q6v4_modem_lock);
186}
187
188static int pil_q6v4_reset(struct pil_desc *pil)
189{
190 u32 reg, err = 0;
191 const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
192 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
193
Stephen Boydcc0f5342011-12-29 17:28:57 -0800194 pil_q6v4_make_proxy_votes(pil->dev);
Matt Wagantall39088932011-08-02 20:24:56 -0700195
Stephen Boydeb819882011-08-29 14:46:30 -0700196 err = pil_q6v4_power_up(pil->dev);
197 if (err)
198 return err;
199 /* Enable Q6 ACLK */
200 writel_relaxed(0x10, pdata->aclk_reg);
201
202 if (drv->modem_base)
203 pil_q6v4_init_modem(drv->modem_base, pdata->jtag_clk_reg);
204
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700205 /* Unhalt bus port */
206 err = msm_bus_axi_portunhalt(pdata->bus_port);
207 if (err)
208 dev_err(pil->dev, "Failed to unhalt bus port\n");
209
Stephen Boydeb819882011-08-29 14:46:30 -0700210 /*
211 * Assert AXIS_ACLK_EN override to allow for correct updating of the
212 * QDSP6_CORE_STATE status bit. This is mandatory only for the SW Q6
213 * in 8960v1 and optional elsewhere.
214 */
215 reg = readl_relaxed(drv->base + QDSP6SS_CGC_OVERRIDE);
216 reg |= Q6SS_AXIS_ACLK_EN;
217 writel_relaxed(reg, drv->base + QDSP6SS_CGC_OVERRIDE);
218
219 /* Deassert Q6SS_SS_ARES */
220 reg = readl_relaxed(drv->base + QDSP6SS_RESET);
221 reg &= ~(Q6SS_SS_ARES);
222 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
223
224 /* Program boot address */
225 writel_relaxed((drv->start_addr >> 8) & 0xFFFFFF,
226 drv->base + QDSP6SS_RST_EVB);
227
228 /* Program TCM and AHB address ranges */
229 writel_relaxed(pdata->strap_tcm_base, drv->base + QDSP6SS_STRAP_TCM);
230 writel_relaxed(pdata->strap_ahb_upper | pdata->strap_ahb_lower,
231 drv->base + QDSP6SS_STRAP_AHB);
232
233 /* Turn off Q6 core clock */
234 writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR,
235 drv->base + QDSP6SS_GFMUX_CTL);
236
237 /* Put memories to sleep */
238 writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL);
239
240 /* Assert resets */
241 reg = readl_relaxed(drv->base + QDSP6SS_RESET);
242 reg |= (Q6SS_CORE_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES
243 | Q6SS_STOP_CORE_ARES);
244 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
245
246 /* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
247 mb();
248 usleep_range(20, 30);
249
250 /* Turn on Q6 memories */
251 reg = Q6SS_L2DATA_SLP_NRET_N | Q6SS_SLP_RET_N | Q6SS_L1TCM_SLP_NRET_N
252 | Q6SS_L2TAG_SLP_NRET_N | Q6SS_ETB_SLEEP_NRET_N | Q6SS_ARR_STBY_N
253 | Q6SS_CLAMP_IO;
254 writel_relaxed(reg, drv->base + QDSP6SS_PWR_CTL);
255
256 /* Turn on Q6 core clock */
257 reg = Q6SS_CLK_ENA | Q6SS_SRC_SWITCH_CLK_OVR;
258 writel_relaxed(reg, drv->base + QDSP6SS_GFMUX_CTL);
259
260 /* Remove Q6SS_CLAMP_IO */
261 reg = readl_relaxed(drv->base + QDSP6SS_PWR_CTL);
262 reg &= ~Q6SS_CLAMP_IO;
263 writel_relaxed(reg, drv->base + QDSP6SS_PWR_CTL);
264
265 /* Bring Q6 core out of reset and start execution. */
266 writel_relaxed(0x0, drv->base + QDSP6SS_RESET);
267
268 /*
269 * Re-enable auto-gating of AXIS_ACLK at lease one AXI clock cycle
270 * after resets are de-asserted.
271 */
272 mb();
273 usleep_range(1, 10);
274 reg = readl_relaxed(drv->base + QDSP6SS_CGC_OVERRIDE);
275 reg &= ~Q6SS_AXIS_ACLK_EN;
276 writel_relaxed(reg, drv->base + QDSP6SS_CGC_OVERRIDE);
277
278 return 0;
279}
280
281static int pil_q6v4_shutdown(struct pil_desc *pil)
282{
283 u32 reg;
284 struct q6v4_data *drv = dev_get_drvdata(pil->dev);
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700285 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
286
287 /* Make sure bus port is halted */
288 msm_bus_axi_porthalt(pdata->bus_port);
Stephen Boydeb819882011-08-29 14:46:30 -0700289
290 /* Turn off Q6 core clock */
291 writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR,
292 drv->base + QDSP6SS_GFMUX_CTL);
293
294 /* Assert resets */
295 reg = (Q6SS_SS_ARES | Q6SS_CORE_ARES | Q6SS_ISDB_ARES
296 | Q6SS_ETM_ARES | Q6SS_STOP_CORE_ARES | Q6SS_PRIV_ARES);
297 writel_relaxed(reg, drv->base + QDSP6SS_RESET);
298
299 /* Turn off Q6 memories */
300 writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL);
301
302 if (drv->modem_base)
303 pil_q6v4_shutdown_modem();
304
305 if (drv->vreg_enabled) {
306 regulator_disable(drv->vreg);
307 drv->vreg_enabled = false;
308 }
309
Stephen Boydcc0f5342011-12-29 17:28:57 -0800310 pil_q6v4_remove_proxy_votes_now(pil->dev);
Matt Wagantall39088932011-08-02 20:24:56 -0700311
Stephen Boydeb819882011-08-29 14:46:30 -0700312 return 0;
313}
314
315static struct pil_reset_ops pil_q6v4_ops = {
316 .init_image = pil_q6v4_init_image,
317 .verify_blob = nop_verify_blob,
318 .auth_and_reset = pil_q6v4_reset,
319 .shutdown = pil_q6v4_shutdown,
320};
321
322static int pil_q6v4_init_image_trusted(struct pil_desc *pil,
323 const u8 *metadata, size_t size)
324{
325 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
326 return pas_init_image(pdata->pas_id, metadata, size);
327}
328
329static int pil_q6v4_reset_trusted(struct pil_desc *pil)
330{
331 const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
332 int err;
333
Stephen Boydcc0f5342011-12-29 17:28:57 -0800334 pil_q6v4_make_proxy_votes(pil->dev);
Matt Wagantall39088932011-08-02 20:24:56 -0700335
Stephen Boydeb819882011-08-29 14:46:30 -0700336 err = pil_q6v4_power_up(pil->dev);
337 if (err)
338 return err;
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700339
340 /* Unhalt bus port */
341 err = msm_bus_axi_portunhalt(pdata->bus_port);
342 if (err)
343 dev_err(pil->dev, "Failed to unhalt bus port\n");
Stephen Boydeb819882011-08-29 14:46:30 -0700344 return pas_auth_and_reset(pdata->pas_id);
345}
346
347static int pil_q6v4_shutdown_trusted(struct pil_desc *pil)
348{
349 int ret;
350 struct q6v4_data *drv = dev_get_drvdata(pil->dev);
351 struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
352
Matt Wagantall6e4aafb2011-09-09 17:53:54 -0700353 /* Make sure bus port is halted */
354 msm_bus_axi_porthalt(pdata->bus_port);
355
Stephen Boydeb819882011-08-29 14:46:30 -0700356 ret = pas_shutdown(pdata->pas_id);
357 if (ret)
358 return ret;
359
360 if (drv->vreg_enabled) {
361 regulator_disable(drv->vreg);
362 drv->vreg_enabled = false;
363 }
364
Stephen Boydcc0f5342011-12-29 17:28:57 -0800365 pil_q6v4_remove_proxy_votes_now(pil->dev);
Matt Wagantall39088932011-08-02 20:24:56 -0700366
Stephen Boydeb819882011-08-29 14:46:30 -0700367 return ret;
368}
369
370static struct pil_reset_ops pil_q6v4_ops_trusted = {
371 .init_image = pil_q6v4_init_image_trusted,
372 .verify_blob = nop_verify_blob,
373 .auth_and_reset = pil_q6v4_reset_trusted,
374 .shutdown = pil_q6v4_shutdown_trusted,
375};
376
377static int __devinit pil_q6v4_driver_probe(struct platform_device *pdev)
378{
379 const struct pil_q6v4_pdata *pdata = pdev->dev.platform_data;
380 struct q6v4_data *drv;
381 struct resource *res;
382 struct pil_desc *desc;
Stephen Boydcc0f5342011-12-29 17:28:57 -0800383 int ret;
Stephen Boydeb819882011-08-29 14:46:30 -0700384
385 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
386 if (!res)
387 return -EINVAL;
388
389 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
390 if (!drv)
391 return -ENOMEM;
392 platform_set_drvdata(pdev, drv);
393
394 drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
395 if (!drv->base)
396 return -ENOMEM;
397
398 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
399 if (res) {
400 drv->modem_base = devm_ioremap(&pdev->dev, res->start,
401 resource_size(res));
402 if (!drv->modem_base)
403 return -ENOMEM;
404 }
405
406 desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
Stephen Boydcc0f5342011-12-29 17:28:57 -0800407 if (!desc)
Stephen Boydeb819882011-08-29 14:46:30 -0700408 return -ENOMEM;
409
Stephen Boydcc0f5342011-12-29 17:28:57 -0800410 drv->pll_supply = regulator_get(&pdev->dev, "pll_vdd");
411 if (IS_ERR(drv->pll_supply)) {
412 drv->pll_supply = NULL;
413 } else {
414 ret = regulator_set_voltage(drv->pll_supply, 1800000, 1800000);
415 if (ret) {
416 dev_err(&pdev->dev, "failed to set pll voltage\n");
417 goto err;
418 }
419
420 ret = regulator_set_optimum_mode(drv->pll_supply, 100000);
421 if (ret < 0) {
422 dev_err(&pdev->dev, "failed to set pll optimum mode\n");
423 goto err;
424 }
425 }
426
Stephen Boydeb819882011-08-29 14:46:30 -0700427 desc->name = pdata->name;
428 desc->depends_on = pdata->depends;
429 desc->dev = &pdev->dev;
430
431 if (pas_supported(pdata->pas_id) > 0) {
432 desc->ops = &pil_q6v4_ops_trusted;
433 dev_info(&pdev->dev, "using secure boot\n");
434 } else {
435 desc->ops = &pil_q6v4_ops;
436 dev_info(&pdev->dev, "using non-secure boot\n");
437 }
438
439 drv->vreg = regulator_get(&pdev->dev, "core_vdd");
Stephen Boydcc0f5342011-12-29 17:28:57 -0800440 if (IS_ERR(drv->vreg)) {
441 ret = PTR_ERR(drv->vreg);
442 goto err;
Stephen Boydeb819882011-08-29 14:46:30 -0700443 }
Stephen Boydcc0f5342011-12-29 17:28:57 -0800444
445 drv->xo = msm_xo_get(pdata->xo_id, pdata->name);
446 if (IS_ERR(drv->xo)) {
447 ret = PTR_ERR(drv->xo);
448 goto err_xo;
449 }
450 INIT_DELAYED_WORK(&drv->work, pil_q6v4_remove_proxy_votes);
451
452 ret = msm_pil_register(desc);
453 if (ret)
454 goto err_pil;
Stephen Boydeb819882011-08-29 14:46:30 -0700455 return 0;
Stephen Boydcc0f5342011-12-29 17:28:57 -0800456err_pil:
457 cancel_delayed_work_sync(&drv->work);
458 msm_xo_put(drv->xo);
459err_xo:
460 regulator_put(drv->vreg);
461err:
462 regulator_put(drv->pll_supply);
463 return ret;
Stephen Boydeb819882011-08-29 14:46:30 -0700464}
465
466static int __devexit pil_q6v4_driver_exit(struct platform_device *pdev)
467{
468 struct q6v4_data *drv = platform_get_drvdata(pdev);
Stephen Boydcc0f5342011-12-29 17:28:57 -0800469 cancel_delayed_work_sync(&drv->work);
470 msm_xo_put(drv->xo);
Stephen Boydeb819882011-08-29 14:46:30 -0700471 regulator_put(drv->vreg);
Stephen Boydcc0f5342011-12-29 17:28:57 -0800472 regulator_put(drv->pll_supply);
Stephen Boydeb819882011-08-29 14:46:30 -0700473 return 0;
474}
475
476static struct platform_driver pil_q6v4_driver = {
477 .probe = pil_q6v4_driver_probe,
478 .remove = __devexit_p(pil_q6v4_driver_exit),
479 .driver = {
480 .name = "pil_qdsp6v4",
481 .owner = THIS_MODULE,
482 },
483};
484
485static int __init pil_q6v4_init(void)
486{
487 return platform_driver_register(&pil_q6v4_driver);
488}
489module_init(pil_q6v4_init);
490
491static void __exit pil_q6v4_exit(void)
492{
493 platform_driver_unregister(&pil_q6v4_driver);
494}
495module_exit(pil_q6v4_exit);
496
497MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
498MODULE_LICENSE("GPL v2");