blob: 6a30940b7cbc1da943f9ad1f3a5059e43639c423 [file] [log] [blame]
Matt Wagantall4e2599e2012-03-21 22:31:35 -07001/*
2 * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
3 *
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
54#define PBL_MBA_WAIT_TIMEOUT_US 100000
55#define PROXY_TIMEOUT_MS 10000
56#define POLL_INTERVAL_US 50
57
58static int pil_mss_power_up(struct device *dev)
59{
60 int ret;
61 struct q6v5_data *drv = dev_get_drvdata(dev);
62
63 ret = regulator_enable(drv->vreg);
64 if (ret)
65 dev_err(dev, "Failed to enable regulator.\n");
66
67 return ret;
68}
69
70static int pil_mss_power_down(struct device *dev)
71{
72 struct q6v5_data *drv = dev_get_drvdata(dev);
73
74 return regulator_disable(drv->vreg);
75}
76
Matt Wagantall8c2246d2012-08-12 17:08:04 -070077static int pil_mss_enable_clks(struct q6v5_data *drv)
78{
79 int ret;
80
81 ret = clk_prepare_enable(drv->ahb_clk);
82 if (ret)
83 goto err_ahb_clk;
84 ret = clk_reset(drv->core_clk, CLK_RESET_DEASSERT);
85 if (ret)
86 goto err_reset;
87 ret = clk_prepare_enable(drv->core_clk);
88 if (ret)
89 goto err_core_clk;
90 ret = clk_prepare_enable(drv->axi_clk);
91 if (ret)
92 goto err_axi_clk;
93 ret = clk_prepare_enable(drv->reg_clk);
94 if (ret)
95 goto err_reg_clk;
96 ret = clk_prepare_enable(drv->rom_clk);
97 if (ret)
98 goto err_rom_clk;
99
100 return 0;
101
102err_rom_clk:
103 clk_disable_unprepare(drv->reg_clk);
104err_reg_clk:
105 clk_disable_unprepare(drv->axi_clk);
106err_axi_clk:
107 clk_disable_unprepare(drv->core_clk);
108err_core_clk:
109 clk_reset(drv->core_clk, CLK_RESET_ASSERT);
110err_reset:
111 clk_disable_unprepare(drv->ahb_clk);
112err_ahb_clk:
113 return ret;
114}
115
116static void pil_mss_disable_clks(struct q6v5_data *drv)
117{
118 clk_disable_unprepare(drv->rom_clk);
119 clk_disable_unprepare(drv->reg_clk);
120 clk_disable_unprepare(drv->axi_clk);
121 clk_disable_unprepare(drv->core_clk);
122 clk_reset(drv->core_clk, CLK_RESET_ASSERT);
123 clk_disable_unprepare(drv->ahb_clk);
124}
125
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700126static int wait_for_mba_ready(struct device *dev)
127{
128 struct q6v5_data *drv = dev_get_drvdata(dev);
129 int ret;
130 u32 status;
131
132 /* Wait for PBL completion. */
133 ret = readl_poll_timeout(drv->rmb_base + RMB_PBL_STATUS, status,
134 status != 0, POLL_INTERVAL_US, PBL_MBA_WAIT_TIMEOUT_US);
135 if (ret) {
136 dev_err(dev, "PBL boot timed out\n");
137 return ret;
138 }
139 if (status != STATUS_PBL_SUCCESS) {
140 dev_err(dev, "PBL returned unexpected status %d\n", status);
141 return -EINVAL;
142 }
143
144 /* Wait for MBA completion. */
145 ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
146 status != 0, POLL_INTERVAL_US, PBL_MBA_WAIT_TIMEOUT_US);
147 if (ret) {
148 dev_err(dev, "MBA boot timed out\n");
149 return ret;
150 }
151 if (status != STATUS_XPU_UNLOCKED &&
152 status != STATUS_XPU_UNLOCKED_SCRIBBLED) {
153 dev_err(dev, "MBA returned unexpected status %d\n", status);
154 return -EINVAL;
155 }
156
157 return 0;
158}
159
160static int pil_mss_shutdown(struct pil_desc *pil)
161{
162 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
163
164 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_Q6_HALT_BASE);
165 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_MODEM_HALT_BASE);
166 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_NC_HALT_BASE);
167
168 /*
169 * If the shutdown function is called before the reset function, clocks
170 * and power will not be enabled yet. Enable them here so that register
171 * writes performed during the shutdown succeed.
172 */
173 if (drv->is_booted == false) {
174 pil_mss_power_up(pil->dev);
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700175 pil_mss_enable_clks(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700176 }
177 pil_q6v5_shutdown(pil);
178
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700179 pil_mss_disable_clks(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700180 pil_mss_power_down(pil->dev);
181
182 writel_relaxed(1, drv->restart_reg);
183
184 drv->is_booted = false;
185
186 return 0;
187}
188
189static int pil_mss_reset(struct pil_desc *pil)
190{
191 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
192 int ret;
193
Matt Wagantall33c2ec72012-07-26 20:26:57 -0700194 /* Deassert reset to subsystem and wait for propagation */
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700195 writel_relaxed(0, drv->restart_reg);
196 mb();
Matt Wagantall33c2ec72012-07-26 20:26:57 -0700197 udelay(2);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700198
199 /*
200 * Bring subsystem out of reset and enable required
201 * regulators and clocks.
202 */
203 ret = pil_mss_power_up(pil->dev);
204 if (ret)
205 goto err_power;
206
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700207 ret = pil_mss_enable_clks(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700208 if (ret)
209 goto err_clks;
210
211 /* Program Image Address */
Matt Wagantallf11928a2012-07-27 15:47:59 -0700212 if (drv->self_auth) {
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700213 writel_relaxed(drv->start_addr, drv->rmb_base + RMB_MBA_IMAGE);
Matt Wagantallf11928a2012-07-27 15:47:59 -0700214 /* Ensure write to RMB base occurs before reset is released. */
215 mb();
216 } else {
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700217 writel_relaxed((drv->start_addr >> 4) & 0x0FFFFFF0,
218 drv->reg_base + QDSP6SS_RST_EVB);
Matt Wagantallf11928a2012-07-27 15:47:59 -0700219 }
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700220
Matt Wagantall16bc5cc2012-08-09 21:33:23 -0700221 /* De-assert MSS IO clamps */
222 writel_relaxed(MSS_IO_UNCLAMP_ALL, drv->io_clamp_reg);
223
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700224 ret = pil_q6v5_reset(pil);
225 if (ret)
226 goto err_q6v5_reset;
227
228 /* Wait for MBA to start. Check for PBL and MBA errors while waiting. */
229 if (drv->self_auth) {
230 ret = wait_for_mba_ready(pil->dev);
231 if (ret)
232 goto err_auth;
233 }
234
235 drv->is_booted = true;
236
237 return 0;
238
239err_auth:
240 pil_q6v5_shutdown(pil);
241err_q6v5_reset:
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700242 pil_mss_disable_clks(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700243err_clks:
244 pil_mss_power_down(pil->dev);
245err_power:
246 return ret;
247}
248
249static struct pil_reset_ops pil_mss_ops = {
250 .init_image = pil_q6v5_init_image,
251 .proxy_vote = pil_q6v5_make_proxy_votes,
252 .proxy_unvote = pil_q6v5_remove_proxy_votes,
253 .auth_and_reset = pil_mss_reset,
254 .shutdown = pil_mss_shutdown,
255};
256
257static int __devinit pil_mss_driver_probe(struct platform_device *pdev)
258{
259 struct q6v5_data *drv;
260 struct pil_desc *desc;
261 struct resource *res;
262 int ret;
263
264 desc = pil_q6v5_init(pdev);
265 if (IS_ERR(desc))
266 return PTR_ERR(desc);
267 drv = platform_get_drvdata(pdev);
268 if (drv == NULL)
269 return -ENODEV;
270
271 desc->ops = &pil_mss_ops;
272 desc->owner = THIS_MODULE;
273 desc->proxy_timeout = PROXY_TIMEOUT_MS;
274
275 of_property_read_u32(pdev->dev.of_node, "qcom,pil-self-auth",
276 &drv->self_auth);
277 if (drv->self_auth) {
278 res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
279 drv->rmb_base = devm_ioremap(&pdev->dev, res->start,
280 resource_size(res));
281 if (!drv->rmb_base)
282 return -ENOMEM;
283 }
284
285 res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
286 drv->restart_reg = devm_ioremap(&pdev->dev, res->start,
287 resource_size(res));
288 if (!drv->restart_reg)
289 return -ENOMEM;
290
Matt Wagantall16bc5cc2012-08-09 21:33:23 -0700291 res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
292 drv->io_clamp_reg = devm_ioremap(&pdev->dev, res->start,
293 resource_size(res));
294 if (!drv->io_clamp_reg)
295 return -ENOMEM;
296
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700297 drv->vreg = devm_regulator_get(&pdev->dev, "vdd_mss");
298 if (IS_ERR(drv->vreg))
299 return PTR_ERR(drv->vreg);
300
Matt Wagantall5d929a82012-08-02 11:36:15 -0700301 ret = regulator_set_voltage(drv->vreg, 1050000, 1050000);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700302 if (ret)
303 dev_err(&pdev->dev, "Failed to set regulator's voltage.\n");
304
305 ret = regulator_set_optimum_mode(drv->vreg, 100000);
306 if (ret < 0) {
307 dev_err(&pdev->dev, "Failed to set regulator's mode.\n");
308 return ret;
309 }
310
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700311 drv->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
312 if (IS_ERR(drv->ahb_clk))
313 return PTR_ERR(drv->ahb_clk);
314
315 drv->core_clk = devm_clk_get(&pdev->dev, "core_clk");
316 if (IS_ERR(drv->core_clk))
317 return PTR_ERR(drv->core_clk);
318
319 drv->axi_clk = devm_clk_get(&pdev->dev, "bus_clk");
320 if (IS_ERR(drv->axi_clk))
321 return PTR_ERR(drv->axi_clk);
322
323 drv->reg_clk = devm_clk_get(&pdev->dev, "reg_clk");
324 if (IS_ERR(drv->reg_clk))
325 return PTR_ERR(drv->reg_clk);
326
327 drv->rom_clk = devm_clk_get(&pdev->dev, "mem_clk");
328 if (IS_ERR(drv->rom_clk))
329 return PTR_ERR(drv->rom_clk);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700330
331 drv->pil = msm_pil_register(desc);
332 if (IS_ERR(drv->pil))
333 return PTR_ERR(drv->pil);
334
335 return 0;
336}
337
338static int __devexit pil_mss_driver_exit(struct platform_device *pdev)
339{
340 struct q6v5_data *drv = platform_get_drvdata(pdev);
341 msm_pil_unregister(drv->pil);
342 return 0;
343}
344
345static struct of_device_id mss_match_table[] = {
346 { .compatible = "qcom,pil-q6v5-mss" },
347 {}
348};
349
350static struct platform_driver pil_mss_driver = {
351 .probe = pil_mss_driver_probe,
352 .remove = __devexit_p(pil_mss_driver_exit),
353 .driver = {
354 .name = "pil-q6v5-mss",
355 .of_match_table = mss_match_table,
356 .owner = THIS_MODULE,
357 },
358};
359
360static int __init pil_mss_init(void)
361{
362 return platform_driver_register(&pil_mss_driver);
363}
364module_init(pil_mss_init);
365
366static void __exit pil_mss_exit(void)
367{
368 platform_driver_unregister(&pil_mss_driver);
369}
370module_exit(pil_mss_exit);
371
372MODULE_DESCRIPTION("Support for booting modem subsystems with QDSP6v5 Hexagon processors");
373MODULE_LICENSE("GPL v2");