blob: 10279187725761bddc7864d332e284244724a541 [file] [log] [blame]
Matt Wagantall4e2599e2012-03-21 22:31:35 -07001/*
Duy Truonge833aca2013-02-12 13:35:08 -08002 * Copyright (c) 2012, The Linux Foundation. All rights reserved.
Matt Wagantall4e2599e2012-03-21 22:31:35 -07003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/init.h>
15#include <linux/module.h>
16#include <linux/platform_device.h>
17#include <linux/io.h>
18#include <linux/iopoll.h>
19#include <linux/ioport.h>
20#include <linux/elf.h>
21#include <linux/delay.h>
22#include <linux/sched.h>
23#include <linux/clk.h>
24#include <linux/err.h>
25#include <linux/of.h>
26#include <linux/regulator/consumer.h>
27
28#include <mach/clk.h>
29
30#include "peripheral-loader.h"
31#include "pil-q6v5.h"
32
33/* Q6 Register Offsets */
34#define QDSP6SS_RST_EVB 0x010
35
36/* AXI Halting Registers */
37#define MSS_Q6_HALT_BASE 0x180
38#define MSS_MODEM_HALT_BASE 0x200
39#define MSS_NC_HALT_BASE 0x280
40
Matt Wagantall16bc5cc2012-08-09 21:33:23 -070041/* MSS_CLAMP_IO Register Value */
42#define MSS_IO_UNCLAMP_ALL 0x40
43
Matt Wagantall4e2599e2012-03-21 22:31:35 -070044/* RMB Status Register Values */
45#define STATUS_PBL_SUCCESS 0x1
46#define STATUS_XPU_UNLOCKED 0x1
47#define STATUS_XPU_UNLOCKED_SCRIBBLED 0x2
48
49/* PBL/MBA interface registers */
50#define RMB_MBA_IMAGE 0x00
51#define RMB_PBL_STATUS 0x04
52#define RMB_MBA_STATUS 0x0C
53
Matt Wagantall4e2599e2012-03-21 22:31:35 -070054#define PROXY_TIMEOUT_MS 10000
55#define POLL_INTERVAL_US 50
56
Matt Wagantallf0141392012-08-16 18:53:53 -070057static int pbl_mba_boot_timeout_ms = 100;
58module_param(pbl_mba_boot_timeout_ms, int, S_IRUGO | S_IWUSR);
59
Matt Wagantall4e2599e2012-03-21 22:31:35 -070060static int pil_mss_power_up(struct device *dev)
61{
62 int ret;
63 struct q6v5_data *drv = dev_get_drvdata(dev);
64
65 ret = regulator_enable(drv->vreg);
66 if (ret)
67 dev_err(dev, "Failed to enable regulator.\n");
68
69 return ret;
70}
71
72static int pil_mss_power_down(struct device *dev)
73{
74 struct q6v5_data *drv = dev_get_drvdata(dev);
75
76 return regulator_disable(drv->vreg);
77}
78
Matt Wagantall8c2246d2012-08-12 17:08:04 -070079static int pil_mss_enable_clks(struct q6v5_data *drv)
80{
81 int ret;
Matt Wagantallbbbf5fb2012-08-20 18:00:05 -070082 void __iomem *mpll1_config_ctl;
Matt Wagantall8c2246d2012-08-12 17:08:04 -070083
84 ret = clk_prepare_enable(drv->ahb_clk);
85 if (ret)
86 goto err_ahb_clk;
87 ret = clk_reset(drv->core_clk, CLK_RESET_DEASSERT);
88 if (ret)
89 goto err_reset;
90 ret = clk_prepare_enable(drv->core_clk);
91 if (ret)
92 goto err_core_clk;
93 ret = clk_prepare_enable(drv->axi_clk);
94 if (ret)
95 goto err_axi_clk;
96 ret = clk_prepare_enable(drv->reg_clk);
97 if (ret)
98 goto err_reg_clk;
99 ret = clk_prepare_enable(drv->rom_clk);
100 if (ret)
101 goto err_rom_clk;
102
Matt Wagantallbbbf5fb2012-08-20 18:00:05 -0700103 /* TODO: Remove when support for 8974v1.0 HW is dropped. */
104 mpll1_config_ctl = ioremap(0xFC981034, 0x4);
105 writel_relaxed(0x0300403D, mpll1_config_ctl);
106 mb();
107 iounmap(mpll1_config_ctl);
108
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700109 return 0;
110
111err_rom_clk:
112 clk_disable_unprepare(drv->reg_clk);
113err_reg_clk:
114 clk_disable_unprepare(drv->axi_clk);
115err_axi_clk:
116 clk_disable_unprepare(drv->core_clk);
117err_core_clk:
118 clk_reset(drv->core_clk, CLK_RESET_ASSERT);
119err_reset:
120 clk_disable_unprepare(drv->ahb_clk);
121err_ahb_clk:
122 return ret;
123}
124
125static void pil_mss_disable_clks(struct q6v5_data *drv)
126{
127 clk_disable_unprepare(drv->rom_clk);
128 clk_disable_unprepare(drv->reg_clk);
129 clk_disable_unprepare(drv->axi_clk);
130 clk_disable_unprepare(drv->core_clk);
131 clk_reset(drv->core_clk, CLK_RESET_ASSERT);
132 clk_disable_unprepare(drv->ahb_clk);
133}
134
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700135static int wait_for_mba_ready(struct device *dev)
136{
137 struct q6v5_data *drv = dev_get_drvdata(dev);
138 int ret;
139 u32 status;
140
141 /* Wait for PBL completion. */
142 ret = readl_poll_timeout(drv->rmb_base + RMB_PBL_STATUS, status,
Matt Wagantallf0141392012-08-16 18:53:53 -0700143 status != 0, POLL_INTERVAL_US, pbl_mba_boot_timeout_ms * 1000);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700144 if (ret) {
145 dev_err(dev, "PBL boot timed out\n");
146 return ret;
147 }
148 if (status != STATUS_PBL_SUCCESS) {
149 dev_err(dev, "PBL returned unexpected status %d\n", status);
150 return -EINVAL;
151 }
152
153 /* Wait for MBA completion. */
154 ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
Matt Wagantallf0141392012-08-16 18:53:53 -0700155 status != 0, POLL_INTERVAL_US, pbl_mba_boot_timeout_ms * 1000);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700156 if (ret) {
157 dev_err(dev, "MBA boot timed out\n");
158 return ret;
159 }
160 if (status != STATUS_XPU_UNLOCKED &&
161 status != STATUS_XPU_UNLOCKED_SCRIBBLED) {
162 dev_err(dev, "MBA returned unexpected status %d\n", status);
163 return -EINVAL;
164 }
165
166 return 0;
167}
168
169static int pil_mss_shutdown(struct pil_desc *pil)
170{
171 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
172
173 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_Q6_HALT_BASE);
174 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_MODEM_HALT_BASE);
175 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_NC_HALT_BASE);
176
177 /*
178 * If the shutdown function is called before the reset function, clocks
179 * and power will not be enabled yet. Enable them here so that register
180 * writes performed during the shutdown succeed.
181 */
182 if (drv->is_booted == false) {
183 pil_mss_power_up(pil->dev);
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700184 pil_mss_enable_clks(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700185 }
186 pil_q6v5_shutdown(pil);
187
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700188 pil_mss_disable_clks(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700189 pil_mss_power_down(pil->dev);
190
191 writel_relaxed(1, drv->restart_reg);
192
193 drv->is_booted = false;
194
195 return 0;
196}
197
198static int pil_mss_reset(struct pil_desc *pil)
199{
200 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
201 int ret;
202
Matt Wagantall33c2ec72012-07-26 20:26:57 -0700203 /* Deassert reset to subsystem and wait for propagation */
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700204 writel_relaxed(0, drv->restart_reg);
205 mb();
Matt Wagantall33c2ec72012-07-26 20:26:57 -0700206 udelay(2);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700207
208 /*
209 * Bring subsystem out of reset and enable required
210 * regulators and clocks.
211 */
212 ret = pil_mss_power_up(pil->dev);
213 if (ret)
214 goto err_power;
215
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700216 ret = pil_mss_enable_clks(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700217 if (ret)
218 goto err_clks;
219
220 /* Program Image Address */
Matt Wagantallf11928a2012-07-27 15:47:59 -0700221 if (drv->self_auth) {
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700222 writel_relaxed(drv->start_addr, drv->rmb_base + RMB_MBA_IMAGE);
Matt Wagantallf11928a2012-07-27 15:47:59 -0700223 /* Ensure write to RMB base occurs before reset is released. */
224 mb();
225 } else {
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700226 writel_relaxed((drv->start_addr >> 4) & 0x0FFFFFF0,
227 drv->reg_base + QDSP6SS_RST_EVB);
Matt Wagantallf11928a2012-07-27 15:47:59 -0700228 }
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700229
Matt Wagantall16bc5cc2012-08-09 21:33:23 -0700230 /* De-assert MSS IO clamps */
231 writel_relaxed(MSS_IO_UNCLAMP_ALL, drv->io_clamp_reg);
232
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700233 ret = pil_q6v5_reset(pil);
234 if (ret)
235 goto err_q6v5_reset;
236
237 /* Wait for MBA to start. Check for PBL and MBA errors while waiting. */
238 if (drv->self_auth) {
239 ret = wait_for_mba_ready(pil->dev);
240 if (ret)
241 goto err_auth;
242 }
243
244 drv->is_booted = true;
245
246 return 0;
247
248err_auth:
249 pil_q6v5_shutdown(pil);
250err_q6v5_reset:
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700251 pil_mss_disable_clks(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700252err_clks:
253 pil_mss_power_down(pil->dev);
254err_power:
255 return ret;
256}
257
258static struct pil_reset_ops pil_mss_ops = {
259 .init_image = pil_q6v5_init_image,
260 .proxy_vote = pil_q6v5_make_proxy_votes,
261 .proxy_unvote = pil_q6v5_remove_proxy_votes,
262 .auth_and_reset = pil_mss_reset,
263 .shutdown = pil_mss_shutdown,
264};
265
266static int __devinit pil_mss_driver_probe(struct platform_device *pdev)
267{
268 struct q6v5_data *drv;
269 struct pil_desc *desc;
270 struct resource *res;
271 int ret;
272
273 desc = pil_q6v5_init(pdev);
274 if (IS_ERR(desc))
275 return PTR_ERR(desc);
276 drv = platform_get_drvdata(pdev);
277 if (drv == NULL)
278 return -ENODEV;
279
280 desc->ops = &pil_mss_ops;
281 desc->owner = THIS_MODULE;
282 desc->proxy_timeout = PROXY_TIMEOUT_MS;
283
284 of_property_read_u32(pdev->dev.of_node, "qcom,pil-self-auth",
285 &drv->self_auth);
286 if (drv->self_auth) {
287 res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
288 drv->rmb_base = devm_ioremap(&pdev->dev, res->start,
289 resource_size(res));
290 if (!drv->rmb_base)
291 return -ENOMEM;
292 }
293
294 res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
295 drv->restart_reg = devm_ioremap(&pdev->dev, res->start,
296 resource_size(res));
297 if (!drv->restart_reg)
298 return -ENOMEM;
299
Matt Wagantall16bc5cc2012-08-09 21:33:23 -0700300 res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
301 drv->io_clamp_reg = devm_ioremap(&pdev->dev, res->start,
302 resource_size(res));
303 if (!drv->io_clamp_reg)
304 return -ENOMEM;
305
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700306 drv->vreg = devm_regulator_get(&pdev->dev, "vdd_mss");
307 if (IS_ERR(drv->vreg))
308 return PTR_ERR(drv->vreg);
309
Matt Wagantall5d929a82012-08-02 11:36:15 -0700310 ret = regulator_set_voltage(drv->vreg, 1050000, 1050000);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700311 if (ret)
312 dev_err(&pdev->dev, "Failed to set regulator's voltage.\n");
313
314 ret = regulator_set_optimum_mode(drv->vreg, 100000);
315 if (ret < 0) {
316 dev_err(&pdev->dev, "Failed to set regulator's mode.\n");
317 return ret;
318 }
319
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700320 drv->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
321 if (IS_ERR(drv->ahb_clk))
322 return PTR_ERR(drv->ahb_clk);
323
324 drv->core_clk = devm_clk_get(&pdev->dev, "core_clk");
325 if (IS_ERR(drv->core_clk))
326 return PTR_ERR(drv->core_clk);
327
328 drv->axi_clk = devm_clk_get(&pdev->dev, "bus_clk");
329 if (IS_ERR(drv->axi_clk))
330 return PTR_ERR(drv->axi_clk);
331
332 drv->reg_clk = devm_clk_get(&pdev->dev, "reg_clk");
333 if (IS_ERR(drv->reg_clk))
334 return PTR_ERR(drv->reg_clk);
335
336 drv->rom_clk = devm_clk_get(&pdev->dev, "mem_clk");
337 if (IS_ERR(drv->rom_clk))
338 return PTR_ERR(drv->rom_clk);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700339
340 drv->pil = msm_pil_register(desc);
341 if (IS_ERR(drv->pil))
342 return PTR_ERR(drv->pil);
343
344 return 0;
345}
346
347static int __devexit pil_mss_driver_exit(struct platform_device *pdev)
348{
349 struct q6v5_data *drv = platform_get_drvdata(pdev);
350 msm_pil_unregister(drv->pil);
351 return 0;
352}
353
354static struct of_device_id mss_match_table[] = {
355 { .compatible = "qcom,pil-q6v5-mss" },
356 {}
357};
358
359static struct platform_driver pil_mss_driver = {
360 .probe = pil_mss_driver_probe,
361 .remove = __devexit_p(pil_mss_driver_exit),
362 .driver = {
363 .name = "pil-q6v5-mss",
364 .of_match_table = mss_match_table,
365 .owner = THIS_MODULE,
366 },
367};
368
369static int __init pil_mss_init(void)
370{
371 return platform_driver_register(&pil_mss_driver);
372}
373module_init(pil_mss_init);
374
375static void __exit pil_mss_exit(void)
376{
377 platform_driver_unregister(&pil_mss_driver);
378}
379module_exit(pil_mss_exit);
380
381MODULE_DESCRIPTION("Support for booting modem subsystems with QDSP6v5 Hexagon processors");
382MODULE_LICENSE("GPL v2");