blob: 56be717ae2a8f6d0c554326fe2923895a7636d80 [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
77static int wait_for_mba_ready(struct device *dev)
78{
79 struct q6v5_data *drv = dev_get_drvdata(dev);
80 int ret;
81 u32 status;
82
83 /* Wait for PBL completion. */
84 ret = readl_poll_timeout(drv->rmb_base + RMB_PBL_STATUS, status,
85 status != 0, POLL_INTERVAL_US, PBL_MBA_WAIT_TIMEOUT_US);
86 if (ret) {
87 dev_err(dev, "PBL boot timed out\n");
88 return ret;
89 }
90 if (status != STATUS_PBL_SUCCESS) {
91 dev_err(dev, "PBL returned unexpected status %d\n", status);
92 return -EINVAL;
93 }
94
95 /* Wait for MBA completion. */
96 ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
97 status != 0, POLL_INTERVAL_US, PBL_MBA_WAIT_TIMEOUT_US);
98 if (ret) {
99 dev_err(dev, "MBA boot timed out\n");
100 return ret;
101 }
102 if (status != STATUS_XPU_UNLOCKED &&
103 status != STATUS_XPU_UNLOCKED_SCRIBBLED) {
104 dev_err(dev, "MBA returned unexpected status %d\n", status);
105 return -EINVAL;
106 }
107
108 return 0;
109}
110
111static int pil_mss_shutdown(struct pil_desc *pil)
112{
113 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
114
115 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_Q6_HALT_BASE);
116 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_MODEM_HALT_BASE);
117 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_NC_HALT_BASE);
118
119 /*
120 * If the shutdown function is called before the reset function, clocks
121 * and power will not be enabled yet. Enable them here so that register
122 * writes performed during the shutdown succeed.
123 */
124 if (drv->is_booted == false) {
125 pil_mss_power_up(pil->dev);
126 pil_q6v5_enable_clks(pil);
127 }
128 pil_q6v5_shutdown(pil);
129
130 pil_q6v5_disable_clks(pil);
131 pil_mss_power_down(pil->dev);
132
133 writel_relaxed(1, drv->restart_reg);
134
135 drv->is_booted = false;
136
137 return 0;
138}
139
140static int pil_mss_reset(struct pil_desc *pil)
141{
142 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
143 int ret;
144
Matt Wagantall33c2ec72012-07-26 20:26:57 -0700145 /* Deassert reset to subsystem and wait for propagation */
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700146 writel_relaxed(0, drv->restart_reg);
147 mb();
Matt Wagantall33c2ec72012-07-26 20:26:57 -0700148 udelay(2);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700149
150 /*
151 * Bring subsystem out of reset and enable required
152 * regulators and clocks.
153 */
154 ret = pil_mss_power_up(pil->dev);
155 if (ret)
156 goto err_power;
157
158 ret = pil_q6v5_enable_clks(pil);
159 if (ret)
160 goto err_clks;
161
162 /* Program Image Address */
Matt Wagantallf11928a2012-07-27 15:47:59 -0700163 if (drv->self_auth) {
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700164 writel_relaxed(drv->start_addr, drv->rmb_base + RMB_MBA_IMAGE);
Matt Wagantallf11928a2012-07-27 15:47:59 -0700165 /* Ensure write to RMB base occurs before reset is released. */
166 mb();
167 } else {
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700168 writel_relaxed((drv->start_addr >> 4) & 0x0FFFFFF0,
169 drv->reg_base + QDSP6SS_RST_EVB);
Matt Wagantallf11928a2012-07-27 15:47:59 -0700170 }
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700171
Matt Wagantall16bc5cc2012-08-09 21:33:23 -0700172 /* De-assert MSS IO clamps */
173 writel_relaxed(MSS_IO_UNCLAMP_ALL, drv->io_clamp_reg);
174
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700175 ret = pil_q6v5_reset(pil);
176 if (ret)
177 goto err_q6v5_reset;
178
179 /* Wait for MBA to start. Check for PBL and MBA errors while waiting. */
180 if (drv->self_auth) {
181 ret = wait_for_mba_ready(pil->dev);
182 if (ret)
183 goto err_auth;
184 }
185
186 drv->is_booted = true;
187
188 return 0;
189
190err_auth:
191 pil_q6v5_shutdown(pil);
192err_q6v5_reset:
193 pil_q6v5_disable_clks(pil);
194err_clks:
195 pil_mss_power_down(pil->dev);
196err_power:
197 return ret;
198}
199
200static struct pil_reset_ops pil_mss_ops = {
201 .init_image = pil_q6v5_init_image,
202 .proxy_vote = pil_q6v5_make_proxy_votes,
203 .proxy_unvote = pil_q6v5_remove_proxy_votes,
204 .auth_and_reset = pil_mss_reset,
205 .shutdown = pil_mss_shutdown,
206};
207
208static int __devinit pil_mss_driver_probe(struct platform_device *pdev)
209{
210 struct q6v5_data *drv;
211 struct pil_desc *desc;
212 struct resource *res;
213 int ret;
214
215 desc = pil_q6v5_init(pdev);
216 if (IS_ERR(desc))
217 return PTR_ERR(desc);
218 drv = platform_get_drvdata(pdev);
219 if (drv == NULL)
220 return -ENODEV;
221
222 desc->ops = &pil_mss_ops;
223 desc->owner = THIS_MODULE;
224 desc->proxy_timeout = PROXY_TIMEOUT_MS;
225
226 of_property_read_u32(pdev->dev.of_node, "qcom,pil-self-auth",
227 &drv->self_auth);
228 if (drv->self_auth) {
229 res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
230 drv->rmb_base = devm_ioremap(&pdev->dev, res->start,
231 resource_size(res));
232 if (!drv->rmb_base)
233 return -ENOMEM;
234 }
235
236 res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
237 drv->restart_reg = devm_ioremap(&pdev->dev, res->start,
238 resource_size(res));
239 if (!drv->restart_reg)
240 return -ENOMEM;
241
Matt Wagantall16bc5cc2012-08-09 21:33:23 -0700242 res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
243 drv->io_clamp_reg = devm_ioremap(&pdev->dev, res->start,
244 resource_size(res));
245 if (!drv->io_clamp_reg)
246 return -ENOMEM;
247
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700248 drv->vreg = devm_regulator_get(&pdev->dev, "vdd_mss");
249 if (IS_ERR(drv->vreg))
250 return PTR_ERR(drv->vreg);
251
Matt Wagantall5d929a82012-08-02 11:36:15 -0700252 ret = regulator_set_voltage(drv->vreg, 1050000, 1050000);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700253 if (ret)
254 dev_err(&pdev->dev, "Failed to set regulator's voltage.\n");
255
256 ret = regulator_set_optimum_mode(drv->vreg, 100000);
257 if (ret < 0) {
258 dev_err(&pdev->dev, "Failed to set regulator's mode.\n");
259 return ret;
260 }
261
Matt Wagantall56865f02012-08-09 15:03:36 -0700262 drv->ss_clk = devm_clk_get(&pdev->dev, "mem_clk");
263 if (IS_ERR(drv->ss_clk))
264 return PTR_ERR(drv->ss_clk);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700265
266 drv->pil = msm_pil_register(desc);
267 if (IS_ERR(drv->pil))
268 return PTR_ERR(drv->pil);
269
270 return 0;
271}
272
273static int __devexit pil_mss_driver_exit(struct platform_device *pdev)
274{
275 struct q6v5_data *drv = platform_get_drvdata(pdev);
276 msm_pil_unregister(drv->pil);
277 return 0;
278}
279
280static struct of_device_id mss_match_table[] = {
281 { .compatible = "qcom,pil-q6v5-mss" },
282 {}
283};
284
285static struct platform_driver pil_mss_driver = {
286 .probe = pil_mss_driver_probe,
287 .remove = __devexit_p(pil_mss_driver_exit),
288 .driver = {
289 .name = "pil-q6v5-mss",
290 .of_match_table = mss_match_table,
291 .owner = THIS_MODULE,
292 },
293};
294
295static int __init pil_mss_init(void)
296{
297 return platform_driver_register(&pil_mss_driver);
298}
299module_init(pil_mss_init);
300
301static void __exit pil_mss_exit(void)
302{
303 platform_driver_unregister(&pil_mss_driver);
304}
305module_exit(pil_mss_exit);
306
307MODULE_DESCRIPTION("Support for booting modem subsystems with QDSP6v5 Hexagon processors");
308MODULE_LICENSE("GPL v2");