blob: ed85c953604f1f37d5322fa04590946bd2bb4004 [file] [log] [blame]
Matt Wagantall4e2599e2012-03-21 22:31:35 -07001/*
Matt Wagantallf5c44bd2012-10-29 12:20:07 -07002 * 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>
Matt Wagantall4e2599e2012-03-21 22:31:35 -070020#include <linux/delay.h>
21#include <linux/sched.h>
22#include <linux/clk.h>
23#include <linux/err.h>
24#include <linux/of.h>
25#include <linux/regulator/consumer.h>
Stephen Boyd3da4fd02012-07-06 10:00:12 -070026#include <linux/interrupt.h>
Matt Wagantall4e2599e2012-03-21 22:31:35 -070027
Stephen Boyd3da4fd02012-07-06 10:00:12 -070028#include <mach/subsystem_restart.h>
Matt Wagantall4e2599e2012-03-21 22:31:35 -070029#include <mach/clk.h>
Stephen Boyd3da4fd02012-07-06 10:00:12 -070030#include <mach/msm_smsm.h>
Matt Wagantall4e2599e2012-03-21 22:31:35 -070031
32#include "peripheral-loader.h"
33#include "pil-q6v5.h"
Stephen Boyd3da4fd02012-07-06 10:00:12 -070034#include "ramdump.h"
Vikram Mulukutla896d0582012-10-17 16:57:46 -070035#include "sysmon.h"
Matt Wagantall4e2599e2012-03-21 22:31:35 -070036
37/* Q6 Register Offsets */
38#define QDSP6SS_RST_EVB 0x010
39
40/* AXI Halting Registers */
41#define MSS_Q6_HALT_BASE 0x180
42#define MSS_MODEM_HALT_BASE 0x200
43#define MSS_NC_HALT_BASE 0x280
44
45/* RMB Status Register Values */
46#define STATUS_PBL_SUCCESS 0x1
47#define STATUS_XPU_UNLOCKED 0x1
48#define STATUS_XPU_UNLOCKED_SCRIBBLED 0x2
49
50/* PBL/MBA interface registers */
51#define RMB_MBA_IMAGE 0x00
52#define RMB_PBL_STATUS 0x04
Stephen Boyd3da4fd02012-07-06 10:00:12 -070053#define RMB_MBA_COMMAND 0x08
Matt Wagantall4e2599e2012-03-21 22:31:35 -070054#define RMB_MBA_STATUS 0x0C
Stephen Boyd3da4fd02012-07-06 10:00:12 -070055#define RMB_PMI_META_DATA 0x10
56#define RMB_PMI_CODE_START 0x14
57#define RMB_PMI_CODE_LENGTH 0x18
Matt Wagantall4e2599e2012-03-21 22:31:35 -070058
Matt Wagantall70315fb2012-12-03 16:33:28 -080059#define VDD_MSS_UV 1050000
60#define MAX_VDD_MX_UV 1050000
61
Matt Wagantall4e2599e2012-03-21 22:31:35 -070062#define PROXY_TIMEOUT_MS 10000
63#define POLL_INTERVAL_US 50
64
Stephen Boyd3da4fd02012-07-06 10:00:12 -070065#define CMD_META_DATA_READY 0x1
66#define CMD_LOAD_READY 0x2
67
68#define STATUS_META_DATA_AUTH_SUCCESS 0x3
69#define STATUS_AUTH_COMPLETE 0x4
70
71#define MAX_SSR_REASON_LEN 81U
72
73struct mba_data {
74 void __iomem *metadata_base;
75 void __iomem *rmb_base;
76 void __iomem *io_clamp_reg;
77 unsigned long metadata_phys;
Stephen Boyd3da4fd02012-07-06 10:00:12 -070078 struct pil_desc desc;
79 struct subsys_device *subsys;
80 struct subsys_desc subsys_desc;
Vikram Mulukutla896d0582012-10-17 16:57:46 -070081 void *adsp_state_notifier;
Stephen Boyd3da4fd02012-07-06 10:00:12 -070082 u32 img_length;
83 struct q6v5_data *q6;
84 int self_auth;
85 void *ramdump_dev;
86 void *smem_ramdump_dev;
87 bool crash_shutdown;
88 bool ignore_errors;
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -080089 int is_loadable;
Stephen Boyd3da4fd02012-07-06 10:00:12 -070090};
91
Matt Wagantalld251d8e2012-08-16 18:53:53 -070092static int pbl_mba_boot_timeout_ms = 100;
93module_param(pbl_mba_boot_timeout_ms, int, S_IRUGO | S_IWUSR);
94
Stephen Boyd3da4fd02012-07-06 10:00:12 -070095static int modem_auth_timeout_ms = 10000;
96module_param(modem_auth_timeout_ms, int, S_IRUGO | S_IWUSR);
97
Stephen Boyd3826cd42012-07-05 17:37:53 -070098static int pil_mss_power_up(struct q6v5_data *drv)
Matt Wagantall4e2599e2012-03-21 22:31:35 -070099{
100 int ret;
Stephen Boyd3826cd42012-07-05 17:37:53 -0700101 struct device *dev = drv->desc.dev;
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700102
103 ret = regulator_enable(drv->vreg);
104 if (ret)
Matt Wagantall70315fb2012-12-03 16:33:28 -0800105 dev_err(dev, "Failed to enable modem regulator.\n");
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700106
107 return ret;
108}
109
Stephen Boyd3826cd42012-07-05 17:37:53 -0700110static int pil_mss_power_down(struct q6v5_data *drv)
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700111{
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700112 return regulator_disable(drv->vreg);
113}
114
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700115static int pil_mss_enable_clks(struct q6v5_data *drv)
116{
117 int ret;
118
119 ret = clk_prepare_enable(drv->ahb_clk);
120 if (ret)
121 goto err_ahb_clk;
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700122 ret = clk_prepare_enable(drv->axi_clk);
123 if (ret)
124 goto err_axi_clk;
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700125 ret = clk_prepare_enable(drv->rom_clk);
126 if (ret)
127 goto err_rom_clk;
128
129 return 0;
130
131err_rom_clk:
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700132 clk_disable_unprepare(drv->axi_clk);
133err_axi_clk:
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700134 clk_disable_unprepare(drv->ahb_clk);
135err_ahb_clk:
136 return ret;
137}
138
139static void pil_mss_disable_clks(struct q6v5_data *drv)
140{
141 clk_disable_unprepare(drv->rom_clk);
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700142 clk_disable_unprepare(drv->axi_clk);
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700143 clk_disable_unprepare(drv->ahb_clk);
144}
145
Stephen Boyd3826cd42012-07-05 17:37:53 -0700146static int wait_for_mba_ready(struct q6v5_data *drv)
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700147{
Stephen Boyd3826cd42012-07-05 17:37:53 -0700148 struct device *dev = drv->desc.dev;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700149 struct mba_data *mba = platform_get_drvdata(to_platform_device(dev));
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700150 int ret;
151 u32 status;
152
153 /* Wait for PBL completion. */
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700154 ret = readl_poll_timeout(mba->rmb_base + RMB_PBL_STATUS, status,
Matt Wagantalld251d8e2012-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, "PBL boot timed out\n");
158 return ret;
159 }
160 if (status != STATUS_PBL_SUCCESS) {
161 dev_err(dev, "PBL returned unexpected status %d\n", status);
162 return -EINVAL;
163 }
164
165 /* Wait for MBA completion. */
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700166 ret = readl_poll_timeout(mba->rmb_base + RMB_MBA_STATUS, status,
Matt Wagantalld251d8e2012-08-16 18:53:53 -0700167 status != 0, POLL_INTERVAL_US, pbl_mba_boot_timeout_ms * 1000);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700168 if (ret) {
169 dev_err(dev, "MBA boot timed out\n");
170 return ret;
171 }
172 if (status != STATUS_XPU_UNLOCKED &&
173 status != STATUS_XPU_UNLOCKED_SCRIBBLED) {
174 dev_err(dev, "MBA returned unexpected status %d\n", status);
175 return -EINVAL;
176 }
177
178 return 0;
179}
180
181static int pil_mss_shutdown(struct pil_desc *pil)
182{
Stephen Boyd3826cd42012-07-05 17:37:53 -0700183 struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700184
185 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_Q6_HALT_BASE);
186 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_MODEM_HALT_BASE);
187 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_NC_HALT_BASE);
188
189 /*
190 * If the shutdown function is called before the reset function, clocks
191 * and power will not be enabled yet. Enable them here so that register
192 * writes performed during the shutdown succeed.
193 */
194 if (drv->is_booted == false) {
Stephen Boyd3826cd42012-07-05 17:37:53 -0700195 pil_mss_power_up(drv);
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700196 pil_mss_enable_clks(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700197 }
198 pil_q6v5_shutdown(pil);
199
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700200 pil_mss_disable_clks(drv);
Stephen Boyd3826cd42012-07-05 17:37:53 -0700201 pil_mss_power_down(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700202
203 writel_relaxed(1, drv->restart_reg);
204
205 drv->is_booted = false;
206
207 return 0;
208}
209
210static int pil_mss_reset(struct pil_desc *pil)
211{
Stephen Boyd3826cd42012-07-05 17:37:53 -0700212 struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700213 struct platform_device *pdev = to_platform_device(pil->dev);
214 struct mba_data *mba = platform_get_drvdata(pdev);
Stephen Boyd3030c252012-08-08 17:24:05 -0700215 unsigned long start_addr = pil_get_entry_addr(pil);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700216 int ret;
217
Matt Wagantall33c2ec72012-07-26 20:26:57 -0700218 /* Deassert reset to subsystem and wait for propagation */
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700219 writel_relaxed(0, drv->restart_reg);
220 mb();
Matt Wagantall33c2ec72012-07-26 20:26:57 -0700221 udelay(2);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700222
223 /*
224 * Bring subsystem out of reset and enable required
225 * regulators and clocks.
226 */
Stephen Boyd3826cd42012-07-05 17:37:53 -0700227 ret = pil_mss_power_up(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700228 if (ret)
229 goto err_power;
230
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700231 ret = pil_mss_enable_clks(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700232 if (ret)
233 goto err_clks;
234
235 /* Program Image Address */
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700236 if (mba->self_auth) {
Stephen Boyd3030c252012-08-08 17:24:05 -0700237 writel_relaxed(start_addr, mba->rmb_base + RMB_MBA_IMAGE);
Matt Wagantallf11928a2012-07-27 15:47:59 -0700238 /* Ensure write to RMB base occurs before reset is released. */
239 mb();
240 } else {
Stephen Boyd3030c252012-08-08 17:24:05 -0700241 writel_relaxed((start_addr >> 4) & 0x0FFFFFF0,
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700242 drv->reg_base + QDSP6SS_RST_EVB);
Matt Wagantallf11928a2012-07-27 15:47:59 -0700243 }
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700244
245 ret = pil_q6v5_reset(pil);
246 if (ret)
247 goto err_q6v5_reset;
248
249 /* Wait for MBA to start. Check for PBL and MBA errors while waiting. */
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700250 if (mba->self_auth) {
Stephen Boyd3826cd42012-07-05 17:37:53 -0700251 ret = wait_for_mba_ready(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700252 if (ret)
253 goto err_auth;
254 }
255
256 drv->is_booted = true;
257
258 return 0;
259
260err_auth:
261 pil_q6v5_shutdown(pil);
262err_q6v5_reset:
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700263 pil_mss_disable_clks(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700264err_clks:
Stephen Boyd3826cd42012-07-05 17:37:53 -0700265 pil_mss_power_down(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700266err_power:
267 return ret;
268}
269
Matt Wagantall70315fb2012-12-03 16:33:28 -0800270static int pil_q6v5_mss_make_proxy_votes(struct pil_desc *pil)
271{
272 int ret;
273 struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
274
275 ret = regulator_set_voltage(drv->vreg_mx, VDD_MSS_UV, MAX_VDD_MX_UV);
276 if (ret) {
277 dev_err(pil->dev, "Failed to request vreg_mx voltage\n");
278 return ret;
279 }
280
281 ret = regulator_enable(drv->vreg_mx);
282 if (ret) {
283 dev_err(pil->dev, "Failed to enable vreg_mx\n");
284 regulator_set_voltage(drv->vreg_mx, 0, MAX_VDD_MX_UV);
285 return ret;
286 }
287
288 ret = pil_q6v5_make_proxy_votes(pil);
289 if (ret) {
290 regulator_disable(drv->vreg_mx);
291 regulator_set_voltage(drv->vreg_mx, 0, MAX_VDD_MX_UV);
292 }
293
294 return ret;
295}
296
297static void pil_q6v5_mss_remove_proxy_votes(struct pil_desc *pil)
298{
299 struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
300 pil_q6v5_remove_proxy_votes(pil);
301 regulator_disable(drv->vreg_mx);
302 regulator_set_voltage(drv->vreg_mx, 0, MAX_VDD_MX_UV);
303}
304
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700305static struct pil_reset_ops pil_mss_ops = {
Matt Wagantall70315fb2012-12-03 16:33:28 -0800306 .proxy_vote = pil_q6v5_mss_make_proxy_votes,
307 .proxy_unvote = pil_q6v5_mss_remove_proxy_votes,
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700308 .auth_and_reset = pil_mss_reset,
309 .shutdown = pil_mss_shutdown,
310};
311
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700312static int pil_mba_make_proxy_votes(struct pil_desc *pil)
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700313{
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700314 int ret;
315 struct mba_data *drv = dev_get_drvdata(pil->dev);
316
317 ret = clk_prepare_enable(drv->q6->xo);
318 if (ret) {
319 dev_err(pil->dev, "Failed to enable XO\n");
320 return ret;
321 }
322 return 0;
323}
324
325static void pil_mba_remove_proxy_votes(struct pil_desc *pil)
326{
327 struct mba_data *drv = dev_get_drvdata(pil->dev);
328 clk_disable_unprepare(drv->q6->xo);
329}
330
331static int pil_mba_init_image(struct pil_desc *pil,
332 const u8 *metadata, size_t size)
333{
334 struct mba_data *drv = dev_get_drvdata(pil->dev);
335 s32 status;
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700336 int ret;
337
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700338 /* Copy metadata to assigned shared buffer location */
339 memcpy(drv->metadata_base, metadata, size);
340
341 /* Initialize length counter to 0 */
342 writel_relaxed(0, drv->rmb_base + RMB_PMI_CODE_LENGTH);
343 drv->img_length = 0;
344
345 /* Pass address of meta-data to the MBA and perform authentication */
346 writel_relaxed(drv->metadata_phys, drv->rmb_base + RMB_PMI_META_DATA);
347 writel_relaxed(CMD_META_DATA_READY, drv->rmb_base + RMB_MBA_COMMAND);
348 ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
349 status == STATUS_META_DATA_AUTH_SUCCESS || status < 0,
350 POLL_INTERVAL_US, modem_auth_timeout_ms * 1000);
351 if (ret) {
352 dev_err(pil->dev, "MBA authentication of headers timed out\n");
353 } else if (status < 0) {
354 dev_err(pil->dev, "MBA returned error %d for headers\n",
355 status);
356 ret = -EINVAL;
357 }
358
359 return ret;
360}
361
362static int pil_mba_verify_blob(struct pil_desc *pil, u32 phy_addr,
363 size_t size)
364{
365 struct mba_data *drv = dev_get_drvdata(pil->dev);
366 s32 status;
367
368 /* Begin image authentication */
369 if (drv->img_length == 0) {
370 writel_relaxed(phy_addr, drv->rmb_base + RMB_PMI_CODE_START);
371 writel_relaxed(CMD_LOAD_READY, drv->rmb_base + RMB_MBA_COMMAND);
372 }
373 /* Increment length counter */
374 drv->img_length += size;
375 writel_relaxed(drv->img_length, drv->rmb_base + RMB_PMI_CODE_LENGTH);
376
377 status = readl_relaxed(drv->rmb_base + RMB_MBA_STATUS);
378 if (status < 0) {
379 dev_err(pil->dev, "MBA returned error %d\n", status);
380 return -EINVAL;
381 }
382
383 return 0;
384}
385
386static int pil_mba_auth(struct pil_desc *pil)
387{
388 struct mba_data *drv = dev_get_drvdata(pil->dev);
389 int ret;
390 s32 status;
391
392 /* Wait for all segments to be authenticated or an error to occur */
393 ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
394 status == STATUS_AUTH_COMPLETE || status < 0,
395 50, modem_auth_timeout_ms * 1000);
396 if (ret) {
397 dev_err(pil->dev, "MBA authentication of image timed out\n");
398 } else if (status < 0) {
399 dev_err(pil->dev, "MBA returned error %d for image\n", status);
400 ret = -EINVAL;
401 }
402
403 return ret;
404}
405
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700406static struct pil_reset_ops pil_mba_ops = {
407 .init_image = pil_mba_init_image,
408 .proxy_vote = pil_mba_make_proxy_votes,
409 .proxy_unvote = pil_mba_remove_proxy_votes,
410 .verify_blob = pil_mba_verify_blob,
411 .auth_and_reset = pil_mba_auth,
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700412};
413
414#define subsys_to_drv(d) container_of(d, struct mba_data, subsys_desc)
415
416static void log_modem_sfr(void)
417{
418 u32 size;
419 char *smem_reason, reason[MAX_SSR_REASON_LEN];
420
421 smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
422 if (!smem_reason || !size) {
423 pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
424 return;
425 }
426 if (!smem_reason[0]) {
427 pr_err("modem subsystem failure reason: (unknown, empty string found).\n");
428 return;
429 }
430
431 strlcpy(reason, smem_reason, min(size, sizeof(reason)));
432 pr_err("modem subsystem failure reason: %s.\n", reason);
433
434 smem_reason[0] = '\0';
435 wmb();
436}
437
438static void restart_modem(struct mba_data *drv)
439{
440 log_modem_sfr();
441 drv->ignore_errors = true;
442 subsystem_restart_dev(drv->subsys);
443}
444
445static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
446{
447 struct mba_data *drv = data;
448
449 /* Ignore if we're the one that set SMSM_RESET */
450 if (drv->crash_shutdown)
451 return;
452
453 if (new_state & SMSM_RESET) {
454 pr_err("Probable fatal error on the modem.\n");
455 restart_modem(drv);
456 }
457}
458
459static int modem_shutdown(const struct subsys_desc *subsys)
460{
Stephen Boyde83a0a22012-06-29 13:51:27 -0700461 struct mba_data *drv = subsys_to_drv(subsys);
462
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800463 if (!drv->is_loadable)
Vikram Mulukutla1d958af2012-11-20 14:06:12 -0800464 return 0;
Stephen Boyde83a0a22012-06-29 13:51:27 -0700465 /* MBA doesn't support shutdown */
466 pil_shutdown(&drv->q6->desc);
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700467 return 0;
468}
469
470static int modem_powerup(const struct subsys_desc *subsys)
471{
472 struct mba_data *drv = subsys_to_drv(subsys);
Stephen Boyde83a0a22012-06-29 13:51:27 -0700473 int ret;
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800474
475 if (!drv->is_loadable)
Vikram Mulukutla1d958af2012-11-20 14:06:12 -0800476 return 0;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700477 /*
478 * At this time, the modem is shutdown. Therefore this function cannot
479 * run concurrently with either the watchdog bite error handler or the
480 * SMSM callback, making it safe to unset the flag below.
481 */
482 drv->ignore_errors = false;
Stephen Boyde83a0a22012-06-29 13:51:27 -0700483 ret = pil_boot(&drv->q6->desc);
484 if (ret)
485 return ret;
486 ret = pil_boot(&drv->desc);
487 if (ret)
488 pil_shutdown(&drv->q6->desc);
489 return ret;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700490}
491
492static void modem_crash_shutdown(const struct subsys_desc *subsys)
493{
494 struct mba_data *drv = subsys_to_drv(subsys);
495 drv->crash_shutdown = true;
496 smsm_reset_modem(SMSM_RESET);
497}
498
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700499static struct ramdump_segment smem_segments[] = {
500 {0x0FA00000, 0x0FC00000 - 0x0FA00000},
501};
502
503static int modem_ramdump(int enable, const struct subsys_desc *subsys)
504{
505 struct mba_data *drv = subsys_to_drv(subsys);
506 int ret;
507
508 if (!enable)
509 return 0;
510
Stephen Boyde83a0a22012-06-29 13:51:27 -0700511 ret = pil_boot(&drv->q6->desc);
512 if (ret)
513 return ret;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700514
Stephen Boyd5eb17ce2012-11-29 15:34:21 -0800515 ret = pil_do_ramdump(&drv->q6->desc, drv->ramdump_dev);
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700516 if (ret < 0) {
517 pr_err("Unable to dump modem fw memory (rc = %d).\n", ret);
518 goto out;
519 }
520
Stephen Boyd5eb17ce2012-11-29 15:34:21 -0800521 ret = do_elf_ramdump(drv->smem_ramdump_dev, smem_segments,
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700522 ARRAY_SIZE(smem_segments));
523 if (ret < 0) {
524 pr_err("Unable to dump smem memory (rc = %d).\n", ret);
525 goto out;
526 }
527
528out:
Stephen Boyde83a0a22012-06-29 13:51:27 -0700529 pil_shutdown(&drv->q6->desc);
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700530 return ret;
531}
532
Vikram Mulukutla896d0582012-10-17 16:57:46 -0700533static int adsp_state_notifier_fn(struct notifier_block *this,
534 unsigned long code, void *ss_handle)
535{
536 int ret;
537 ret = sysmon_send_event(SYSMON_SS_MODEM, "adsp", code);
538 if (ret < 0)
539 pr_err("%s: sysmon_send_event failed (%d).", __func__, ret);
540 return NOTIFY_DONE;
541}
542
543static struct notifier_block adsp_state_notifier_block = {
544 .notifier_call = adsp_state_notifier_fn,
545};
546
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700547static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
548{
549 struct mba_data *drv = dev_id;
550 if (drv->ignore_errors)
551 return IRQ_HANDLED;
552 pr_err("Watchdog bite received from modem software!\n");
553 restart_modem(drv);
554 return IRQ_HANDLED;
555}
556
557static int mss_start(const struct subsys_desc *desc)
558{
Stephen Boyde83a0a22012-06-29 13:51:27 -0700559 int ret;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700560 struct mba_data *drv = subsys_to_drv(desc);
561
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800562 if (!drv->is_loadable)
Vikram Mulukutla1d958af2012-11-20 14:06:12 -0800563 return 0;
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800564
Stephen Boyde83a0a22012-06-29 13:51:27 -0700565 ret = pil_boot(&drv->q6->desc);
566 if (ret)
567 return ret;
568 ret = pil_boot(&drv->desc);
569 if (ret)
570 pil_shutdown(&drv->q6->desc);
571 return ret;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700572}
573
574static void mss_stop(const struct subsys_desc *desc)
575{
576 struct mba_data *drv = subsys_to_drv(desc);
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800577
578 if (!drv->is_loadable)
579 return;
580
Stephen Boyde83a0a22012-06-29 13:51:27 -0700581 /* MBA doesn't support shutdown */
582 pil_shutdown(&drv->q6->desc);
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700583}
584
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800585static int __devinit pil_subsys_init(struct mba_data *drv,
586 struct platform_device *pdev)
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700587{
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800588 int irq, ret;
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700589
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700590 irq = platform_get_irq(pdev, 0);
591 if (irq < 0)
592 return irq;
593
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800594 drv->subsys_desc.name = "modem";
595 drv->subsys_desc.dev = &pdev->dev;
596 drv->subsys_desc.owner = THIS_MODULE;
597 drv->subsys_desc.shutdown = modem_shutdown;
598 drv->subsys_desc.powerup = modem_powerup;
599 drv->subsys_desc.ramdump = modem_ramdump;
600 drv->subsys_desc.crash_shutdown = modem_crash_shutdown;
601 drv->subsys_desc.start = mss_start;
602 drv->subsys_desc.stop = mss_stop;
603
604 drv->subsys = subsys_register(&drv->subsys_desc);
605 if (IS_ERR(drv->subsys)) {
606 ret = PTR_ERR(drv->subsys);
607 goto err_subsys;
608 }
609
610 drv->ramdump_dev = create_ramdump_device("modem", &pdev->dev);
611 if (!drv->ramdump_dev) {
612 pr_err("%s: Unable to create a modem ramdump device.\n",
613 __func__);
614 ret = -ENOMEM;
615 goto err_ramdump;
616 }
617
618 drv->smem_ramdump_dev = create_ramdump_device("smem-modem", &pdev->dev);
619 if (!drv->smem_ramdump_dev) {
620 pr_err("%s: Unable to create an smem ramdump device.\n",
621 __func__);
622 ret = -ENOMEM;
623 goto err_ramdump_smem;
624 }
625
626 ret = devm_request_irq(&pdev->dev, irq, modem_wdog_bite_irq,
627 IRQF_TRIGGER_RISING, "modem_wdog", drv);
628 if (ret < 0) {
629 dev_err(&pdev->dev, "Unable to request watchdog IRQ.\n");
630 goto err_irq;
631 }
632
633 ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
634 smsm_state_cb, drv);
635 if (ret < 0) {
636 dev_err(&pdev->dev, "Unable to register SMSM callback!\n");
637 goto err_irq;
638 }
639
640 drv->adsp_state_notifier = subsys_notif_register_notifier("adsp",
641 &adsp_state_notifier_block);
642 if (IS_ERR(drv->adsp_state_notifier)) {
643 ret = PTR_ERR(drv->adsp_state_notifier);
644 dev_err(&pdev->dev, "%s: Registration with the SSR notification driver failed (%d)",
645 __func__, ret);
646 goto err_smsm;
647 }
648
649 return 0;
650
651err_smsm:
652 smsm_state_cb_deregister(SMSM_MODEM_STATE, SMSM_RESET, smsm_state_cb,
653 drv);
654err_irq:
655 destroy_ramdump_device(drv->smem_ramdump_dev);
656err_ramdump_smem:
657 destroy_ramdump_device(drv->ramdump_dev);
658err_ramdump:
659 subsys_unregister(drv->subsys);
660err_subsys:
661 return ret;
662}
663
664static int __devinit pil_mss_loadable_init(struct mba_data *drv,
665 struct platform_device *pdev)
666{
667 struct q6v5_data *q6;
668 struct pil_desc *q6_desc, *mba_desc;
669 struct resource *res;
670 int ret;
671
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700672 q6 = pil_q6v5_init(pdev);
673 if (IS_ERR(q6))
674 return PTR_ERR(q6);
675 drv->q6 = q6;
676
677 q6_desc = &q6->desc;
678 q6_desc->ops = &pil_mss_ops;
679 q6_desc->owner = THIS_MODULE;
680 q6_desc->proxy_timeout = PROXY_TIMEOUT_MS;
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700681
682 of_property_read_u32(pdev->dev.of_node, "qcom,pil-self-auth",
683 &drv->self_auth);
684 if (drv->self_auth) {
Matt Wagantall1f168152012-09-25 13:26:47 -0700685 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
686 "rmb_base");
Stephen Boydf8f89282012-07-16 18:05:48 -0700687 drv->rmb_base = devm_request_and_ioremap(&pdev->dev, res);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700688 if (!drv->rmb_base)
689 return -ENOMEM;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700690 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
691 "metadata_base");
692 if (res) {
693 drv->metadata_base = devm_ioremap(&pdev->dev,
694 res->start, resource_size(res));
695 if (!drv->metadata_base)
696 return -ENOMEM;
697 drv->metadata_phys = res->start;
698 }
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700699 }
700
Matt Wagantall1f168152012-09-25 13:26:47 -0700701 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "restart_reg");
Stephen Boydf8f89282012-07-16 18:05:48 -0700702 q6->restart_reg = devm_request_and_ioremap(&pdev->dev, res);
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700703 if (!q6->restart_reg)
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700704 return -ENOMEM;
705
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700706 q6->vreg = devm_regulator_get(&pdev->dev, "vdd_mss");
707 if (IS_ERR(q6->vreg))
708 return PTR_ERR(q6->vreg);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700709
Matt Wagantall70315fb2012-12-03 16:33:28 -0800710 q6->vreg_mx = devm_regulator_get(&pdev->dev, "vdd_mx");
711 if (IS_ERR(q6->vreg_mx))
712 return PTR_ERR(q6->vreg_mx);
713
714 ret = regulator_set_voltage(q6->vreg, VDD_MSS_UV, VDD_MSS_UV);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700715 if (ret)
716 dev_err(&pdev->dev, "Failed to set regulator's voltage.\n");
717
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700718 ret = regulator_set_optimum_mode(q6->vreg, 100000);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700719 if (ret < 0) {
720 dev_err(&pdev->dev, "Failed to set regulator's mode.\n");
721 return ret;
722 }
723
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700724 q6->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
725 if (IS_ERR(q6->ahb_clk))
726 return PTR_ERR(q6->ahb_clk);
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700727
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700728 q6->axi_clk = devm_clk_get(&pdev->dev, "bus_clk");
729 if (IS_ERR(q6->axi_clk))
730 return PTR_ERR(q6->axi_clk);
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700731
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700732 q6->rom_clk = devm_clk_get(&pdev->dev, "mem_clk");
733 if (IS_ERR(q6->rom_clk))
734 return PTR_ERR(q6->rom_clk);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700735
Stephen Boyde83a0a22012-06-29 13:51:27 -0700736 ret = pil_desc_init(q6_desc);
737 if (ret)
738 return ret;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700739
740 mba_desc = &drv->desc;
741 mba_desc->name = "modem";
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700742 mba_desc->dev = &pdev->dev;
743 mba_desc->ops = &pil_mba_ops;
744 mba_desc->owner = THIS_MODULE;
745 mba_desc->proxy_timeout = PROXY_TIMEOUT_MS;
746
Stephen Boyde83a0a22012-06-29 13:51:27 -0700747 ret = pil_desc_init(mba_desc);
748 if (ret)
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700749 goto err_mba_desc;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700750
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700751 return 0;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700752
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700753err_mba_desc:
Stephen Boyde83a0a22012-06-29 13:51:27 -0700754 pil_desc_release(q6_desc);
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700755 return ret;
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800756
757}
758
759static int __devinit pil_mss_driver_probe(struct platform_device *pdev)
760{
761 struct mba_data *drv;
762 int ret;
763
764 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
765 if (!drv)
766 return -ENOMEM;
767 platform_set_drvdata(pdev, drv);
768
Vikram Mulukutla2d4f0862012-11-16 11:57:34 -0800769 drv->is_loadable = of_property_read_bool(pdev->dev.of_node,
770 "qcom,is-loadable");
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800771 if (drv->is_loadable) {
772 ret = pil_mss_loadable_init(drv, pdev);
773 if (ret)
774 return ret;
775 }
776
777 return pil_subsys_init(drv, pdev);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700778}
779
780static int __devexit pil_mss_driver_exit(struct platform_device *pdev)
781{
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700782 struct mba_data *drv = platform_get_drvdata(pdev);
Vikram Mulukutla896d0582012-10-17 16:57:46 -0700783
784 subsys_notif_unregister_notifier(drv->adsp_state_notifier,
785 &adsp_state_notifier_block);
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700786 smsm_state_cb_deregister(SMSM_MODEM_STATE, SMSM_RESET,
787 smsm_state_cb, drv);
788 subsys_unregister(drv->subsys);
789 destroy_ramdump_device(drv->smem_ramdump_dev);
790 destroy_ramdump_device(drv->ramdump_dev);
Stephen Boyde83a0a22012-06-29 13:51:27 -0700791 pil_desc_release(&drv->desc);
792 pil_desc_release(&drv->q6->desc);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700793 return 0;
794}
795
796static struct of_device_id mss_match_table[] = {
797 { .compatible = "qcom,pil-q6v5-mss" },
798 {}
799};
800
801static struct platform_driver pil_mss_driver = {
802 .probe = pil_mss_driver_probe,
803 .remove = __devexit_p(pil_mss_driver_exit),
804 .driver = {
805 .name = "pil-q6v5-mss",
806 .of_match_table = mss_match_table,
807 .owner = THIS_MODULE,
808 },
809};
810
811static int __init pil_mss_init(void)
812{
813 return platform_driver_register(&pil_mss_driver);
814}
815module_init(pil_mss_init);
816
817static void __exit pil_mss_exit(void)
818{
819 platform_driver_unregister(&pil_mss_driver);
820}
821module_exit(pil_mss_exit);
822
823MODULE_DESCRIPTION("Support for booting modem subsystems with QDSP6v5 Hexagon processors");
824MODULE_LICENSE("GPL v2");