blob: 1954ec3d280caecbc09b85e92382e4bb128ce31d [file] [log] [blame]
Matt Wagantall4e2599e2012-03-21 22:31:35 -07001/*
Matt Wagantalla72d03d2013-02-26 21:13:14 -08002 * Copyright (c) 2012-2013, 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
Patrick Daly068ea8e2013-03-04 19:52:40 -080060#define MAX_VDD_MX_UV 1150000
Matt Wagantall70315fb2012-12-03 16:33:28 -080061
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
Patrick Daly11ca6af2013-03-03 17:07:28 -080073/* External BHS */
74#define EXTERNAL_BHS_ON BIT(0)
75#define EXTERNAL_BHS_STATUS BIT(4)
76#define BHS_TIMEOUT_US 50
77
Stephen Boyd3da4fd02012-07-06 10:00:12 -070078struct mba_data {
79 void __iomem *metadata_base;
80 void __iomem *rmb_base;
81 void __iomem *io_clamp_reg;
82 unsigned long metadata_phys;
Stephen Boyd3da4fd02012-07-06 10:00:12 -070083 struct pil_desc desc;
84 struct subsys_device *subsys;
85 struct subsys_desc subsys_desc;
Vikram Mulukutla896d0582012-10-17 16:57:46 -070086 void *adsp_state_notifier;
Stephen Boyd3da4fd02012-07-06 10:00:12 -070087 u32 img_length;
88 struct q6v5_data *q6;
Patrick Dalyb830a3f2013-03-11 14:21:34 -070089 bool self_auth;
Stephen Boyd3da4fd02012-07-06 10:00:12 -070090 void *ramdump_dev;
91 void *smem_ramdump_dev;
92 bool crash_shutdown;
93 bool ignore_errors;
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -080094 int is_loadable;
Stephen Boyd3da4fd02012-07-06 10:00:12 -070095};
96
Matt Wagantalld251d8e2012-08-16 18:53:53 -070097static int pbl_mba_boot_timeout_ms = 100;
98module_param(pbl_mba_boot_timeout_ms, int, S_IRUGO | S_IWUSR);
99
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700100static int modem_auth_timeout_ms = 10000;
101module_param(modem_auth_timeout_ms, int, S_IRUGO | S_IWUSR);
102
Stephen Boyd3826cd42012-07-05 17:37:53 -0700103static int pil_mss_power_up(struct q6v5_data *drv)
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700104{
105 int ret;
Stephen Boyd3826cd42012-07-05 17:37:53 -0700106 struct device *dev = drv->desc.dev;
Patrick Daly11ca6af2013-03-03 17:07:28 -0800107 u32 regval;
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700108
109 ret = regulator_enable(drv->vreg);
110 if (ret)
Matt Wagantall70315fb2012-12-03 16:33:28 -0800111 dev_err(dev, "Failed to enable modem regulator.\n");
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700112
Patrick Daly11ca6af2013-03-03 17:07:28 -0800113 if (drv->cxrail_bhs) {
114 regval = readl_relaxed(drv->cxrail_bhs);
115 regval |= EXTERNAL_BHS_ON;
116 writel_relaxed(regval, drv->cxrail_bhs);
117
118 ret = readl_poll_timeout(drv->cxrail_bhs, regval,
119 regval & EXTERNAL_BHS_STATUS, 1, BHS_TIMEOUT_US);
120 }
121
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700122 return ret;
123}
124
Stephen Boyd3826cd42012-07-05 17:37:53 -0700125static int pil_mss_power_down(struct q6v5_data *drv)
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700126{
Patrick Daly11ca6af2013-03-03 17:07:28 -0800127 u32 regval;
128
129 if (drv->cxrail_bhs) {
130 regval = readl_relaxed(drv->cxrail_bhs);
131 regval &= ~EXTERNAL_BHS_ON;
132 writel_relaxed(regval, drv->cxrail_bhs);
133 }
134
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700135 return regulator_disable(drv->vreg);
136}
137
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700138static int pil_mss_enable_clks(struct q6v5_data *drv)
139{
140 int ret;
141
142 ret = clk_prepare_enable(drv->ahb_clk);
143 if (ret)
144 goto err_ahb_clk;
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700145 ret = clk_prepare_enable(drv->axi_clk);
146 if (ret)
147 goto err_axi_clk;
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700148 ret = clk_prepare_enable(drv->rom_clk);
149 if (ret)
150 goto err_rom_clk;
151
152 return 0;
153
154err_rom_clk:
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700155 clk_disable_unprepare(drv->axi_clk);
156err_axi_clk:
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700157 clk_disable_unprepare(drv->ahb_clk);
158err_ahb_clk:
159 return ret;
160}
161
162static void pil_mss_disable_clks(struct q6v5_data *drv)
163{
164 clk_disable_unprepare(drv->rom_clk);
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700165 clk_disable_unprepare(drv->axi_clk);
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700166 clk_disable_unprepare(drv->ahb_clk);
167}
168
Stephen Boyd3826cd42012-07-05 17:37:53 -0700169static int wait_for_mba_ready(struct q6v5_data *drv)
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700170{
Stephen Boyd3826cd42012-07-05 17:37:53 -0700171 struct device *dev = drv->desc.dev;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700172 struct mba_data *mba = platform_get_drvdata(to_platform_device(dev));
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700173 int ret;
174 u32 status;
175
176 /* Wait for PBL completion. */
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700177 ret = readl_poll_timeout(mba->rmb_base + RMB_PBL_STATUS, status,
Matt Wagantalld251d8e2012-08-16 18:53:53 -0700178 status != 0, POLL_INTERVAL_US, pbl_mba_boot_timeout_ms * 1000);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700179 if (ret) {
180 dev_err(dev, "PBL boot timed out\n");
181 return ret;
182 }
183 if (status != STATUS_PBL_SUCCESS) {
184 dev_err(dev, "PBL returned unexpected status %d\n", status);
185 return -EINVAL;
186 }
187
188 /* Wait for MBA completion. */
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700189 ret = readl_poll_timeout(mba->rmb_base + RMB_MBA_STATUS, status,
Matt Wagantalld251d8e2012-08-16 18:53:53 -0700190 status != 0, POLL_INTERVAL_US, pbl_mba_boot_timeout_ms * 1000);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700191 if (ret) {
192 dev_err(dev, "MBA boot timed out\n");
193 return ret;
194 }
195 if (status != STATUS_XPU_UNLOCKED &&
196 status != STATUS_XPU_UNLOCKED_SCRIBBLED) {
197 dev_err(dev, "MBA returned unexpected status %d\n", status);
198 return -EINVAL;
199 }
200
201 return 0;
202}
203
204static int pil_mss_shutdown(struct pil_desc *pil)
205{
Stephen Boyd3826cd42012-07-05 17:37:53 -0700206 struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700207
208 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_Q6_HALT_BASE);
209 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_MODEM_HALT_BASE);
210 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_NC_HALT_BASE);
211
212 /*
213 * If the shutdown function is called before the reset function, clocks
214 * and power will not be enabled yet. Enable them here so that register
215 * writes performed during the shutdown succeed.
216 */
217 if (drv->is_booted == false) {
Stephen Boyd3826cd42012-07-05 17:37:53 -0700218 pil_mss_power_up(drv);
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700219 pil_mss_enable_clks(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700220 }
221 pil_q6v5_shutdown(pil);
222
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700223 pil_mss_disable_clks(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700224
225 writel_relaxed(1, drv->restart_reg);
226
Patrick Daly11ca6af2013-03-03 17:07:28 -0800227 /*
228 * access to the cx_rail_bhs is restricted until after the gcc_mss
229 * reset is asserted once the PBL starts executing.
230 */
231 pil_mss_power_down(drv);
232
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700233 drv->is_booted = false;
234
235 return 0;
236}
237
238static int pil_mss_reset(struct pil_desc *pil)
239{
Stephen Boyd3826cd42012-07-05 17:37:53 -0700240 struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700241 struct platform_device *pdev = to_platform_device(pil->dev);
242 struct mba_data *mba = platform_get_drvdata(pdev);
Stephen Boyd3030c252012-08-08 17:24:05 -0700243 unsigned long start_addr = pil_get_entry_addr(pil);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700244 int ret;
245
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700246 /*
247 * Bring subsystem out of reset and enable required
248 * regulators and clocks.
249 */
Stephen Boyd3826cd42012-07-05 17:37:53 -0700250 ret = pil_mss_power_up(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700251 if (ret)
252 goto err_power;
253
Patrick Daly11ca6af2013-03-03 17:07:28 -0800254 /* Deassert reset to subsystem and wait for propagation */
255 writel_relaxed(0, drv->restart_reg);
256 mb();
257 udelay(2);
258
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700259 ret = pil_mss_enable_clks(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700260 if (ret)
261 goto err_clks;
262
263 /* Program Image Address */
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700264 if (mba->self_auth) {
Stephen Boyd3030c252012-08-08 17:24:05 -0700265 writel_relaxed(start_addr, mba->rmb_base + RMB_MBA_IMAGE);
Matt Wagantallf11928a2012-07-27 15:47:59 -0700266 /* Ensure write to RMB base occurs before reset is released. */
267 mb();
268 } else {
Stephen Boyd3030c252012-08-08 17:24:05 -0700269 writel_relaxed((start_addr >> 4) & 0x0FFFFFF0,
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700270 drv->reg_base + QDSP6SS_RST_EVB);
Matt Wagantallf11928a2012-07-27 15:47:59 -0700271 }
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700272
273 ret = pil_q6v5_reset(pil);
274 if (ret)
275 goto err_q6v5_reset;
276
277 /* Wait for MBA to start. Check for PBL and MBA errors while waiting. */
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700278 if (mba->self_auth) {
Stephen Boyd3826cd42012-07-05 17:37:53 -0700279 ret = wait_for_mba_ready(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700280 if (ret)
281 goto err_auth;
282 }
283
284 drv->is_booted = true;
285
286 return 0;
287
288err_auth:
289 pil_q6v5_shutdown(pil);
290err_q6v5_reset:
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700291 pil_mss_disable_clks(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700292err_clks:
Stephen Boyd3826cd42012-07-05 17:37:53 -0700293 pil_mss_power_down(drv);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700294err_power:
295 return ret;
296}
297
Matt Wagantall70315fb2012-12-03 16:33:28 -0800298static int pil_q6v5_mss_make_proxy_votes(struct pil_desc *pil)
299{
300 int ret;
301 struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
302
303 ret = regulator_set_voltage(drv->vreg_mx, VDD_MSS_UV, MAX_VDD_MX_UV);
304 if (ret) {
305 dev_err(pil->dev, "Failed to request vreg_mx voltage\n");
306 return ret;
307 }
308
309 ret = regulator_enable(drv->vreg_mx);
310 if (ret) {
311 dev_err(pil->dev, "Failed to enable vreg_mx\n");
312 regulator_set_voltage(drv->vreg_mx, 0, MAX_VDD_MX_UV);
313 return ret;
314 }
315
316 ret = pil_q6v5_make_proxy_votes(pil);
317 if (ret) {
318 regulator_disable(drv->vreg_mx);
319 regulator_set_voltage(drv->vreg_mx, 0, MAX_VDD_MX_UV);
320 }
321
322 return ret;
323}
324
325static void pil_q6v5_mss_remove_proxy_votes(struct pil_desc *pil)
326{
327 struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
328 pil_q6v5_remove_proxy_votes(pil);
329 regulator_disable(drv->vreg_mx);
330 regulator_set_voltage(drv->vreg_mx, 0, MAX_VDD_MX_UV);
331}
332
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700333static struct pil_reset_ops pil_mss_ops = {
Matt Wagantall70315fb2012-12-03 16:33:28 -0800334 .proxy_vote = pil_q6v5_mss_make_proxy_votes,
335 .proxy_unvote = pil_q6v5_mss_remove_proxy_votes,
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700336 .auth_and_reset = pil_mss_reset,
337 .shutdown = pil_mss_shutdown,
338};
339
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700340static int pil_mba_make_proxy_votes(struct pil_desc *pil)
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700341{
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700342 int ret;
343 struct mba_data *drv = dev_get_drvdata(pil->dev);
344
345 ret = clk_prepare_enable(drv->q6->xo);
346 if (ret) {
347 dev_err(pil->dev, "Failed to enable XO\n");
348 return ret;
349 }
350 return 0;
351}
352
353static void pil_mba_remove_proxy_votes(struct pil_desc *pil)
354{
355 struct mba_data *drv = dev_get_drvdata(pil->dev);
356 clk_disable_unprepare(drv->q6->xo);
357}
358
359static int pil_mba_init_image(struct pil_desc *pil,
360 const u8 *metadata, size_t size)
361{
362 struct mba_data *drv = dev_get_drvdata(pil->dev);
363 s32 status;
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700364 int ret;
365
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700366 /* Copy metadata to assigned shared buffer location */
367 memcpy(drv->metadata_base, metadata, size);
368
369 /* Initialize length counter to 0 */
370 writel_relaxed(0, drv->rmb_base + RMB_PMI_CODE_LENGTH);
371 drv->img_length = 0;
372
373 /* Pass address of meta-data to the MBA and perform authentication */
374 writel_relaxed(drv->metadata_phys, drv->rmb_base + RMB_PMI_META_DATA);
375 writel_relaxed(CMD_META_DATA_READY, drv->rmb_base + RMB_MBA_COMMAND);
376 ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
377 status == STATUS_META_DATA_AUTH_SUCCESS || status < 0,
378 POLL_INTERVAL_US, modem_auth_timeout_ms * 1000);
379 if (ret) {
380 dev_err(pil->dev, "MBA authentication of headers timed out\n");
381 } else if (status < 0) {
382 dev_err(pil->dev, "MBA returned error %d for headers\n",
383 status);
384 ret = -EINVAL;
385 }
386
387 return ret;
388}
389
390static int pil_mba_verify_blob(struct pil_desc *pil, u32 phy_addr,
391 size_t size)
392{
393 struct mba_data *drv = dev_get_drvdata(pil->dev);
394 s32 status;
395
396 /* Begin image authentication */
397 if (drv->img_length == 0) {
398 writel_relaxed(phy_addr, drv->rmb_base + RMB_PMI_CODE_START);
399 writel_relaxed(CMD_LOAD_READY, drv->rmb_base + RMB_MBA_COMMAND);
400 }
401 /* Increment length counter */
402 drv->img_length += size;
403 writel_relaxed(drv->img_length, drv->rmb_base + RMB_PMI_CODE_LENGTH);
404
405 status = readl_relaxed(drv->rmb_base + RMB_MBA_STATUS);
406 if (status < 0) {
407 dev_err(pil->dev, "MBA returned error %d\n", status);
408 return -EINVAL;
409 }
410
411 return 0;
412}
413
414static int pil_mba_auth(struct pil_desc *pil)
415{
416 struct mba_data *drv = dev_get_drvdata(pil->dev);
417 int ret;
418 s32 status;
419
420 /* Wait for all segments to be authenticated or an error to occur */
421 ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
422 status == STATUS_AUTH_COMPLETE || status < 0,
423 50, modem_auth_timeout_ms * 1000);
424 if (ret) {
425 dev_err(pil->dev, "MBA authentication of image timed out\n");
426 } else if (status < 0) {
427 dev_err(pil->dev, "MBA returned error %d for image\n", status);
428 ret = -EINVAL;
429 }
430
431 return ret;
432}
433
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700434static struct pil_reset_ops pil_mba_ops = {
435 .init_image = pil_mba_init_image,
436 .proxy_vote = pil_mba_make_proxy_votes,
437 .proxy_unvote = pil_mba_remove_proxy_votes,
438 .verify_blob = pil_mba_verify_blob,
439 .auth_and_reset = pil_mba_auth,
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700440};
441
442#define subsys_to_drv(d) container_of(d, struct mba_data, subsys_desc)
443
444static void log_modem_sfr(void)
445{
446 u32 size;
447 char *smem_reason, reason[MAX_SSR_REASON_LEN];
448
449 smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
450 if (!smem_reason || !size) {
451 pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
452 return;
453 }
454 if (!smem_reason[0]) {
455 pr_err("modem subsystem failure reason: (unknown, empty string found).\n");
456 return;
457 }
458
459 strlcpy(reason, smem_reason, min(size, sizeof(reason)));
460 pr_err("modem subsystem failure reason: %s.\n", reason);
461
462 smem_reason[0] = '\0';
463 wmb();
464}
465
466static void restart_modem(struct mba_data *drv)
467{
468 log_modem_sfr();
469 drv->ignore_errors = true;
470 subsystem_restart_dev(drv->subsys);
471}
472
Seemanta Duttaa48a11d2013-02-19 16:01:22 -0800473static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700474{
Seemanta Duttaa48a11d2013-02-19 16:01:22 -0800475 struct mba_data *drv = data;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700476
Seemanta Duttaa48a11d2013-02-19 16:01:22 -0800477 /* Ignore if we're the one that set SMSM_RESET */
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700478 if (drv->crash_shutdown)
Seemanta Duttaa48a11d2013-02-19 16:01:22 -0800479 return;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700480
Seemanta Duttaa48a11d2013-02-19 16:01:22 -0800481 if (new_state & SMSM_RESET) {
482 pr_err("Probable fatal error on the modem.\n");
483 restart_modem(drv);
484 }
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700485}
486
487static int modem_shutdown(const struct subsys_desc *subsys)
488{
Stephen Boyde83a0a22012-06-29 13:51:27 -0700489 struct mba_data *drv = subsys_to_drv(subsys);
490
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800491 if (!drv->is_loadable)
Vikram Mulukutla1d958af2012-11-20 14:06:12 -0800492 return 0;
Matt Wagantalla72d03d2013-02-26 21:13:14 -0800493 pil_shutdown(&drv->desc);
Stephen Boyde83a0a22012-06-29 13:51:27 -0700494 pil_shutdown(&drv->q6->desc);
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700495 return 0;
496}
497
498static int modem_powerup(const struct subsys_desc *subsys)
499{
500 struct mba_data *drv = subsys_to_drv(subsys);
Stephen Boyde83a0a22012-06-29 13:51:27 -0700501 int ret;
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800502
503 if (!drv->is_loadable)
Vikram Mulukutla1d958af2012-11-20 14:06:12 -0800504 return 0;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700505 /*
506 * At this time, the modem is shutdown. Therefore this function cannot
507 * run concurrently with either the watchdog bite error handler or the
508 * SMSM callback, making it safe to unset the flag below.
509 */
510 drv->ignore_errors = false;
Stephen Boyde83a0a22012-06-29 13:51:27 -0700511 ret = pil_boot(&drv->q6->desc);
512 if (ret)
513 return ret;
514 ret = pil_boot(&drv->desc);
515 if (ret)
516 pil_shutdown(&drv->q6->desc);
517 return ret;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700518}
519
520static void modem_crash_shutdown(const struct subsys_desc *subsys)
521{
522 struct mba_data *drv = subsys_to_drv(subsys);
523 drv->crash_shutdown = true;
Seemanta Duttaa48a11d2013-02-19 16:01:22 -0800524 smsm_reset_modem(SMSM_RESET);
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700525}
526
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700527static struct ramdump_segment smem_segments[] = {
528 {0x0FA00000, 0x0FC00000 - 0x0FA00000},
529};
530
531static int modem_ramdump(int enable, const struct subsys_desc *subsys)
532{
533 struct mba_data *drv = subsys_to_drv(subsys);
534 int ret;
535
536 if (!enable)
537 return 0;
538
Stephen Boyde83a0a22012-06-29 13:51:27 -0700539 ret = pil_boot(&drv->q6->desc);
540 if (ret)
541 return ret;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700542
Stephen Boyd05c45f22013-01-24 12:02:28 -0800543 ret = pil_do_ramdump(&drv->desc, drv->ramdump_dev);
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700544 if (ret < 0) {
545 pr_err("Unable to dump modem fw memory (rc = %d).\n", ret);
546 goto out;
547 }
548
Stephen Boyd5eb17ce2012-11-29 15:34:21 -0800549 ret = do_elf_ramdump(drv->smem_ramdump_dev, smem_segments,
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700550 ARRAY_SIZE(smem_segments));
551 if (ret < 0) {
552 pr_err("Unable to dump smem memory (rc = %d).\n", ret);
553 goto out;
554 }
555
556out:
Stephen Boyde83a0a22012-06-29 13:51:27 -0700557 pil_shutdown(&drv->q6->desc);
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700558 return ret;
559}
560
Vikram Mulukutla896d0582012-10-17 16:57:46 -0700561static int adsp_state_notifier_fn(struct notifier_block *this,
562 unsigned long code, void *ss_handle)
563{
564 int ret;
565 ret = sysmon_send_event(SYSMON_SS_MODEM, "adsp", code);
566 if (ret < 0)
567 pr_err("%s: sysmon_send_event failed (%d).", __func__, ret);
568 return NOTIFY_DONE;
569}
570
571static struct notifier_block adsp_state_notifier_block = {
572 .notifier_call = adsp_state_notifier_fn,
573};
574
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700575static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
576{
577 struct mba_data *drv = dev_id;
578 if (drv->ignore_errors)
579 return IRQ_HANDLED;
580 pr_err("Watchdog bite received from modem software!\n");
581 restart_modem(drv);
582 return IRQ_HANDLED;
583}
584
585static int mss_start(const struct subsys_desc *desc)
586{
Stephen Boyde83a0a22012-06-29 13:51:27 -0700587 int ret;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700588 struct mba_data *drv = subsys_to_drv(desc);
589
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800590 if (!drv->is_loadable)
Vikram Mulukutla1d958af2012-11-20 14:06:12 -0800591 return 0;
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800592
Stephen Boyde83a0a22012-06-29 13:51:27 -0700593 ret = pil_boot(&drv->q6->desc);
594 if (ret)
595 return ret;
596 ret = pil_boot(&drv->desc);
597 if (ret)
598 pil_shutdown(&drv->q6->desc);
599 return ret;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700600}
601
602static void mss_stop(const struct subsys_desc *desc)
603{
604 struct mba_data *drv = subsys_to_drv(desc);
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800605
606 if (!drv->is_loadable)
607 return;
608
Matt Wagantalla72d03d2013-02-26 21:13:14 -0800609 pil_shutdown(&drv->desc);
Stephen Boyde83a0a22012-06-29 13:51:27 -0700610 pil_shutdown(&drv->q6->desc);
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700611}
612
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800613static int __devinit pil_subsys_init(struct mba_data *drv,
614 struct platform_device *pdev)
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700615{
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800616 int irq, ret;
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700617
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700618 irq = platform_get_irq(pdev, 0);
619 if (irq < 0)
620 return irq;
621
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800622 drv->subsys_desc.name = "modem";
623 drv->subsys_desc.dev = &pdev->dev;
624 drv->subsys_desc.owner = THIS_MODULE;
625 drv->subsys_desc.shutdown = modem_shutdown;
626 drv->subsys_desc.powerup = modem_powerup;
627 drv->subsys_desc.ramdump = modem_ramdump;
628 drv->subsys_desc.crash_shutdown = modem_crash_shutdown;
629 drv->subsys_desc.start = mss_start;
630 drv->subsys_desc.stop = mss_stop;
631
632 drv->subsys = subsys_register(&drv->subsys_desc);
633 if (IS_ERR(drv->subsys)) {
634 ret = PTR_ERR(drv->subsys);
635 goto err_subsys;
636 }
637
638 drv->ramdump_dev = create_ramdump_device("modem", &pdev->dev);
639 if (!drv->ramdump_dev) {
640 pr_err("%s: Unable to create a modem ramdump device.\n",
641 __func__);
642 ret = -ENOMEM;
643 goto err_ramdump;
644 }
645
646 drv->smem_ramdump_dev = create_ramdump_device("smem-modem", &pdev->dev);
647 if (!drv->smem_ramdump_dev) {
648 pr_err("%s: Unable to create an smem ramdump device.\n",
649 __func__);
650 ret = -ENOMEM;
651 goto err_ramdump_smem;
652 }
653
654 ret = devm_request_irq(&pdev->dev, irq, modem_wdog_bite_irq,
655 IRQF_TRIGGER_RISING, "modem_wdog", drv);
656 if (ret < 0) {
657 dev_err(&pdev->dev, "Unable to request watchdog IRQ.\n");
658 goto err_irq;
659 }
660
Seemanta Duttaa48a11d2013-02-19 16:01:22 -0800661 ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
662 smsm_state_cb, drv);
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800663 if (ret < 0) {
Seemanta Duttaa48a11d2013-02-19 16:01:22 -0800664 dev_err(&pdev->dev, "Unable to register SMSM callback!\n");
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800665 goto err_irq;
666 }
667
668 drv->adsp_state_notifier = subsys_notif_register_notifier("adsp",
669 &adsp_state_notifier_block);
670 if (IS_ERR(drv->adsp_state_notifier)) {
671 ret = PTR_ERR(drv->adsp_state_notifier);
672 dev_err(&pdev->dev, "%s: Registration with the SSR notification driver failed (%d)",
673 __func__, ret);
Seemanta Duttaa48a11d2013-02-19 16:01:22 -0800674 goto err_smsm;
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800675 }
676
677 return 0;
678
Seemanta Duttaa48a11d2013-02-19 16:01:22 -0800679err_smsm:
680 smsm_state_cb_deregister(SMSM_MODEM_STATE, SMSM_RESET, smsm_state_cb,
681 drv);
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800682err_irq:
683 destroy_ramdump_device(drv->smem_ramdump_dev);
684err_ramdump_smem:
685 destroy_ramdump_device(drv->ramdump_dev);
686err_ramdump:
687 subsys_unregister(drv->subsys);
688err_subsys:
689 return ret;
690}
691
692static int __devinit pil_mss_loadable_init(struct mba_data *drv,
693 struct platform_device *pdev)
694{
695 struct q6v5_data *q6;
696 struct pil_desc *q6_desc, *mba_desc;
697 struct resource *res;
698 int ret;
699
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700700 q6 = pil_q6v5_init(pdev);
701 if (IS_ERR(q6))
702 return PTR_ERR(q6);
703 drv->q6 = q6;
704
705 q6_desc = &q6->desc;
706 q6_desc->ops = &pil_mss_ops;
707 q6_desc->owner = THIS_MODULE;
708 q6_desc->proxy_timeout = PROXY_TIMEOUT_MS;
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700709
Patrick Dalyb830a3f2013-03-11 14:21:34 -0700710 drv->self_auth = of_property_read_bool(pdev->dev.of_node,
711 "qcom,pil-self-auth");
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700712 if (drv->self_auth) {
Matt Wagantall1f168152012-09-25 13:26:47 -0700713 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
714 "rmb_base");
Stephen Boydf8f89282012-07-16 18:05:48 -0700715 drv->rmb_base = devm_request_and_ioremap(&pdev->dev, res);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700716 if (!drv->rmb_base)
717 return -ENOMEM;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700718 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
719 "metadata_base");
720 if (res) {
721 drv->metadata_base = devm_ioremap(&pdev->dev,
722 res->start, resource_size(res));
723 if (!drv->metadata_base)
724 return -ENOMEM;
725 drv->metadata_phys = res->start;
726 }
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700727 }
728
Matt Wagantall1f168152012-09-25 13:26:47 -0700729 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "restart_reg");
Stephen Boydf8f89282012-07-16 18:05:48 -0700730 q6->restart_reg = devm_request_and_ioremap(&pdev->dev, res);
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700731 if (!q6->restart_reg)
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700732 return -ENOMEM;
733
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700734 q6->vreg = devm_regulator_get(&pdev->dev, "vdd_mss");
735 if (IS_ERR(q6->vreg))
736 return PTR_ERR(q6->vreg);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700737
Matt Wagantall70315fb2012-12-03 16:33:28 -0800738 q6->vreg_mx = devm_regulator_get(&pdev->dev, "vdd_mx");
739 if (IS_ERR(q6->vreg_mx))
740 return PTR_ERR(q6->vreg_mx);
741
742 ret = regulator_set_voltage(q6->vreg, VDD_MSS_UV, VDD_MSS_UV);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700743 if (ret)
744 dev_err(&pdev->dev, "Failed to set regulator's voltage.\n");
745
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700746 ret = regulator_set_optimum_mode(q6->vreg, 100000);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700747 if (ret < 0) {
748 dev_err(&pdev->dev, "Failed to set regulator's mode.\n");
749 return ret;
750 }
751
Patrick Daly11ca6af2013-03-03 17:07:28 -0800752 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
753 "cxrail_bhs_reg");
754 if (res)
755 q6->cxrail_bhs = devm_ioremap(&pdev->dev, res->start,
756 resource_size(res));
757
758
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700759 q6->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
760 if (IS_ERR(q6->ahb_clk))
761 return PTR_ERR(q6->ahb_clk);
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700762
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700763 q6->axi_clk = devm_clk_get(&pdev->dev, "bus_clk");
764 if (IS_ERR(q6->axi_clk))
765 return PTR_ERR(q6->axi_clk);
Matt Wagantall8c2246d2012-08-12 17:08:04 -0700766
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700767 q6->rom_clk = devm_clk_get(&pdev->dev, "mem_clk");
768 if (IS_ERR(q6->rom_clk))
769 return PTR_ERR(q6->rom_clk);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700770
Stephen Boyde83a0a22012-06-29 13:51:27 -0700771 ret = pil_desc_init(q6_desc);
772 if (ret)
773 return ret;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700774
775 mba_desc = &drv->desc;
776 mba_desc->name = "modem";
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700777 mba_desc->dev = &pdev->dev;
778 mba_desc->ops = &pil_mba_ops;
779 mba_desc->owner = THIS_MODULE;
780 mba_desc->proxy_timeout = PROXY_TIMEOUT_MS;
781
Stephen Boyde83a0a22012-06-29 13:51:27 -0700782 ret = pil_desc_init(mba_desc);
783 if (ret)
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700784 goto err_mba_desc;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700785
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700786 return 0;
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700787
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700788err_mba_desc:
Stephen Boyde83a0a22012-06-29 13:51:27 -0700789 pil_desc_release(q6_desc);
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700790 return ret;
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800791
792}
793
794static int __devinit pil_mss_driver_probe(struct platform_device *pdev)
795{
796 struct mba_data *drv;
Seemanta Duttaa48a11d2013-02-19 16:01:22 -0800797 int ret;
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800798
799 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
800 if (!drv)
801 return -ENOMEM;
802 platform_set_drvdata(pdev, drv);
803
Vikram Mulukutla2d4f0862012-11-16 11:57:34 -0800804 drv->is_loadable = of_property_read_bool(pdev->dev.of_node,
805 "qcom,is-loadable");
Vikram Mulukutla7dc2d4e2012-11-12 13:04:50 -0800806 if (drv->is_loadable) {
807 ret = pil_mss_loadable_init(drv, pdev);
808 if (ret)
809 return ret;
810 }
811
812 return pil_subsys_init(drv, pdev);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700813}
814
815static int __devexit pil_mss_driver_exit(struct platform_device *pdev)
816{
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700817 struct mba_data *drv = platform_get_drvdata(pdev);
Vikram Mulukutla896d0582012-10-17 16:57:46 -0700818
819 subsys_notif_unregister_notifier(drv->adsp_state_notifier,
820 &adsp_state_notifier_block);
Seemanta Duttaa48a11d2013-02-19 16:01:22 -0800821 smsm_state_cb_deregister(SMSM_MODEM_STATE, SMSM_RESET,
822 smsm_state_cb, drv);
Stephen Boyd3da4fd02012-07-06 10:00:12 -0700823 subsys_unregister(drv->subsys);
824 destroy_ramdump_device(drv->smem_ramdump_dev);
825 destroy_ramdump_device(drv->ramdump_dev);
Stephen Boyde83a0a22012-06-29 13:51:27 -0700826 pil_desc_release(&drv->desc);
827 pil_desc_release(&drv->q6->desc);
Matt Wagantall4e2599e2012-03-21 22:31:35 -0700828 return 0;
829}
830
831static struct of_device_id mss_match_table[] = {
832 { .compatible = "qcom,pil-q6v5-mss" },
833 {}
834};
835
836static struct platform_driver pil_mss_driver = {
837 .probe = pil_mss_driver_probe,
838 .remove = __devexit_p(pil_mss_driver_exit),
839 .driver = {
840 .name = "pil-q6v5-mss",
841 .of_match_table = mss_match_table,
842 .owner = THIS_MODULE,
843 },
844};
845
846static int __init pil_mss_init(void)
847{
848 return platform_driver_register(&pil_mss_driver);
849}
850module_init(pil_mss_init);
851
852static void __exit pil_mss_exit(void)
853{
854 platform_driver_unregister(&pil_mss_driver);
855}
856module_exit(pil_mss_exit);
857
858MODULE_DESCRIPTION("Support for booting modem subsystems with QDSP6v5 Hexagon processors");
859MODULE_LICENSE("GPL v2");