blob: ff0e792d25e6e96558219fc41e3a9ff454b77764 [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
41/* RMB Status Register Values */
42#define STATUS_PBL_SUCCESS 0x1
43#define STATUS_XPU_UNLOCKED 0x1
44#define STATUS_XPU_UNLOCKED_SCRIBBLED 0x2
45
46/* PBL/MBA interface registers */
47#define RMB_MBA_IMAGE 0x00
48#define RMB_PBL_STATUS 0x04
49#define RMB_MBA_STATUS 0x0C
50
51#define PBL_MBA_WAIT_TIMEOUT_US 100000
52#define PROXY_TIMEOUT_MS 10000
53#define POLL_INTERVAL_US 50
54
55static int pil_mss_power_up(struct device *dev)
56{
57 int ret;
58 struct q6v5_data *drv = dev_get_drvdata(dev);
59
60 ret = regulator_enable(drv->vreg);
61 if (ret)
62 dev_err(dev, "Failed to enable regulator.\n");
63
64 return ret;
65}
66
67static int pil_mss_power_down(struct device *dev)
68{
69 struct q6v5_data *drv = dev_get_drvdata(dev);
70
71 return regulator_disable(drv->vreg);
72}
73
74static int wait_for_mba_ready(struct device *dev)
75{
76 struct q6v5_data *drv = dev_get_drvdata(dev);
77 int ret;
78 u32 status;
79
80 /* Wait for PBL completion. */
81 ret = readl_poll_timeout(drv->rmb_base + RMB_PBL_STATUS, status,
82 status != 0, POLL_INTERVAL_US, PBL_MBA_WAIT_TIMEOUT_US);
83 if (ret) {
84 dev_err(dev, "PBL boot timed out\n");
85 return ret;
86 }
87 if (status != STATUS_PBL_SUCCESS) {
88 dev_err(dev, "PBL returned unexpected status %d\n", status);
89 return -EINVAL;
90 }
91
92 /* Wait for MBA completion. */
93 ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
94 status != 0, POLL_INTERVAL_US, PBL_MBA_WAIT_TIMEOUT_US);
95 if (ret) {
96 dev_err(dev, "MBA boot timed out\n");
97 return ret;
98 }
99 if (status != STATUS_XPU_UNLOCKED &&
100 status != STATUS_XPU_UNLOCKED_SCRIBBLED) {
101 dev_err(dev, "MBA returned unexpected status %d\n", status);
102 return -EINVAL;
103 }
104
105 return 0;
106}
107
108static int pil_mss_shutdown(struct pil_desc *pil)
109{
110 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
111
112 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_Q6_HALT_BASE);
113 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_MODEM_HALT_BASE);
114 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_NC_HALT_BASE);
115
116 /*
117 * If the shutdown function is called before the reset function, clocks
118 * and power will not be enabled yet. Enable them here so that register
119 * writes performed during the shutdown succeed.
120 */
121 if (drv->is_booted == false) {
122 pil_mss_power_up(pil->dev);
123 pil_q6v5_enable_clks(pil);
124 }
125 pil_q6v5_shutdown(pil);
126
127 pil_q6v5_disable_clks(pil);
128 pil_mss_power_down(pil->dev);
129
130 writel_relaxed(1, drv->restart_reg);
131
132 drv->is_booted = false;
133
134 return 0;
135}
136
137static int pil_mss_reset(struct pil_desc *pil)
138{
139 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
140 int ret;
141
Matt Wagantall33c2ec72012-07-26 20:26:57 -0700142 /* Deassert reset to subsystem and wait for propagation */
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700143 writel_relaxed(0, drv->restart_reg);
144 mb();
Matt Wagantall33c2ec72012-07-26 20:26:57 -0700145 udelay(2);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700146
147 /*
148 * Bring subsystem out of reset and enable required
149 * regulators and clocks.
150 */
151 ret = pil_mss_power_up(pil->dev);
152 if (ret)
153 goto err_power;
154
155 ret = pil_q6v5_enable_clks(pil);
156 if (ret)
157 goto err_clks;
158
159 /* Program Image Address */
Matt Wagantallf11928a2012-07-27 15:47:59 -0700160 if (drv->self_auth) {
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700161 writel_relaxed(drv->start_addr, drv->rmb_base + RMB_MBA_IMAGE);
Matt Wagantallf11928a2012-07-27 15:47:59 -0700162 /* Ensure write to RMB base occurs before reset is released. */
163 mb();
164 } else {
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700165 writel_relaxed((drv->start_addr >> 4) & 0x0FFFFFF0,
166 drv->reg_base + QDSP6SS_RST_EVB);
Matt Wagantallf11928a2012-07-27 15:47:59 -0700167 }
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700168
169 ret = pil_q6v5_reset(pil);
170 if (ret)
171 goto err_q6v5_reset;
172
173 /* Wait for MBA to start. Check for PBL and MBA errors while waiting. */
174 if (drv->self_auth) {
175 ret = wait_for_mba_ready(pil->dev);
176 if (ret)
177 goto err_auth;
178 }
179
180 drv->is_booted = true;
181
182 return 0;
183
184err_auth:
185 pil_q6v5_shutdown(pil);
186err_q6v5_reset:
187 pil_q6v5_disable_clks(pil);
188err_clks:
189 pil_mss_power_down(pil->dev);
190err_power:
191 return ret;
192}
193
194static struct pil_reset_ops pil_mss_ops = {
195 .init_image = pil_q6v5_init_image,
196 .proxy_vote = pil_q6v5_make_proxy_votes,
197 .proxy_unvote = pil_q6v5_remove_proxy_votes,
198 .auth_and_reset = pil_mss_reset,
199 .shutdown = pil_mss_shutdown,
200};
201
202static int __devinit pil_mss_driver_probe(struct platform_device *pdev)
203{
204 struct q6v5_data *drv;
205 struct pil_desc *desc;
206 struct resource *res;
207 int ret;
208
209 desc = pil_q6v5_init(pdev);
210 if (IS_ERR(desc))
211 return PTR_ERR(desc);
212 drv = platform_get_drvdata(pdev);
213 if (drv == NULL)
214 return -ENODEV;
215
216 desc->ops = &pil_mss_ops;
217 desc->owner = THIS_MODULE;
218 desc->proxy_timeout = PROXY_TIMEOUT_MS;
219
220 of_property_read_u32(pdev->dev.of_node, "qcom,pil-self-auth",
221 &drv->self_auth);
222 if (drv->self_auth) {
223 res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
224 drv->rmb_base = devm_ioremap(&pdev->dev, res->start,
225 resource_size(res));
226 if (!drv->rmb_base)
227 return -ENOMEM;
228 }
229
230 res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
231 drv->restart_reg = devm_ioremap(&pdev->dev, res->start,
232 resource_size(res));
233 if (!drv->restart_reg)
234 return -ENOMEM;
235
236 drv->vreg = devm_regulator_get(&pdev->dev, "vdd_mss");
237 if (IS_ERR(drv->vreg))
238 return PTR_ERR(drv->vreg);
239
240 ret = regulator_set_voltage(drv->vreg, 1150000, 1150000);
241 if (ret)
242 dev_err(&pdev->dev, "Failed to set regulator's voltage.\n");
243
244 ret = regulator_set_optimum_mode(drv->vreg, 100000);
245 if (ret < 0) {
246 dev_err(&pdev->dev, "Failed to set regulator's mode.\n");
247 return ret;
248 }
249
250 drv->mem_clk = devm_clk_get(&pdev->dev, "mem_clk");
251 if (IS_ERR(drv->mem_clk))
252 return PTR_ERR(drv->mem_clk);
253
254 drv->pil = msm_pil_register(desc);
255 if (IS_ERR(drv->pil))
256 return PTR_ERR(drv->pil);
257
258 return 0;
259}
260
261static int __devexit pil_mss_driver_exit(struct platform_device *pdev)
262{
263 struct q6v5_data *drv = platform_get_drvdata(pdev);
264 msm_pil_unregister(drv->pil);
265 return 0;
266}
267
268static struct of_device_id mss_match_table[] = {
269 { .compatible = "qcom,pil-q6v5-mss" },
270 {}
271};
272
273static struct platform_driver pil_mss_driver = {
274 .probe = pil_mss_driver_probe,
275 .remove = __devexit_p(pil_mss_driver_exit),
276 .driver = {
277 .name = "pil-q6v5-mss",
278 .of_match_table = mss_match_table,
279 .owner = THIS_MODULE,
280 },
281};
282
283static int __init pil_mss_init(void)
284{
285 return platform_driver_register(&pil_mss_driver);
286}
287module_init(pil_mss_init);
288
289static void __exit pil_mss_exit(void)
290{
291 platform_driver_unregister(&pil_mss_driver);
292}
293module_exit(pil_mss_exit);
294
295MODULE_DESCRIPTION("Support for booting modem subsystems with QDSP6v5 Hexagon processors");
296MODULE_LICENSE("GPL v2");