blob: c4b60380c0755e1ce33073e29b6a44517a68ade9 [file] [log] [blame]
Seemanta Dutta4e2d49c2013-04-05 16:28:11 -07001/* Copyright (c) 2012,2013 The Linux Foundation. All rights reserved.
Stephen Boydbdb53f32012-06-05 18:39:47 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/platform_device.h>
16#include <linux/regulator/consumer.h>
17#include <linux/err.h>
18#include <linux/io.h>
19#include <linux/delay.h>
20#include <linux/clk.h>
Stephen Boyd2efa9962012-06-12 14:20:12 -070021#include <linux/interrupt.h>
Stephen Boydbdb53f32012-06-05 18:39:47 -070022
Stephen Boyd2efa9962012-06-12 14:20:12 -070023#include <mach/subsystem_restart.h>
24#include <mach/msm_smsm.h>
Seemanta Dutta4e2d49c2013-04-05 16:28:11 -070025#include <mach/ramdump.h>
Jeff Hugo5ba15fe2013-05-06 14:24:24 -060026#include <mach/msm_smem.h>
Stephen Boydbdb53f32012-06-05 18:39:47 -070027
Stephen Boyd2efa9962012-06-12 14:20:12 -070028#include "smd_private.h"
Stephen Boydbdb53f32012-06-05 18:39:47 -070029#include "peripheral-loader.h"
30#include "pil-q6v4.h"
31#include "scm-pas.h"
32
Stephen Boyde24edf52012-07-12 17:46:19 -070033#define MSS_S_HCLK_CTL 0x2C70
34#define MSS_SLP_CLK_CTL 0x2C60
35#define SFAB_MSS_M_ACLK_CTL 0x2340
36#define SFAB_MSS_S_HCLK_CTL 0x2C00
37#define MSS_RESET 0x2C64
Stephen Boydbdb53f32012-06-05 18:39:47 -070038
39struct q6v4_modem {
40 struct q6v4_data q6_fw;
41 struct q6v4_data q6_sw;
42 void __iomem *modem_base;
Stephen Boyde24edf52012-07-12 17:46:19 -070043 void __iomem *cbase;
Stephen Boyd2efa9962012-06-12 14:20:12 -070044 void *fw_ramdump_dev;
45 void *sw_ramdump_dev;
46 void *smem_ramdump_dev;
47 struct subsys_device *subsys;
48 struct subsys_desc subsys_desc;
49 int crash_shutdown;
50 int loadable;
Stephen Boyd3e4e9752012-06-27 12:46:32 -070051 void *pil;
Stephen Boydbdb53f32012-06-05 18:39:47 -070052};
53
54static DEFINE_MUTEX(pil_q6v4_modem_lock);
55static unsigned pil_q6v4_modem_count;
56
57/* Bring modem subsystem out of reset */
Stephen Boyde24edf52012-07-12 17:46:19 -070058static void pil_q6v4_init_modem(void __iomem *base, void __iomem *cbase,
59 void __iomem *jtag_clk)
Stephen Boydbdb53f32012-06-05 18:39:47 -070060{
61 mutex_lock(&pil_q6v4_modem_lock);
62 if (!pil_q6v4_modem_count) {
63 /* Enable MSS clocks */
Stephen Boyde24edf52012-07-12 17:46:19 -070064 writel_relaxed(0x10, cbase + SFAB_MSS_M_ACLK_CTL);
65 writel_relaxed(0x10, cbase + SFAB_MSS_S_HCLK_CTL);
66 writel_relaxed(0x10, cbase + MSS_S_HCLK_CTL);
67 writel_relaxed(0x10, cbase + MSS_SLP_CLK_CTL);
Stephen Boydbdb53f32012-06-05 18:39:47 -070068 /* Wait for clocks to enable */
69 mb();
70 udelay(10);
71
72 /* De-assert MSS reset */
Stephen Boyde24edf52012-07-12 17:46:19 -070073 writel_relaxed(0x0, cbase + MSS_RESET);
Stephen Boydbdb53f32012-06-05 18:39:47 -070074 mb();
75 udelay(10);
76 /* Enable MSS */
77 writel_relaxed(0x7, base);
78 }
79
80 /* Enable JTAG clocks */
81 /* TODO: Remove if/when Q6 software enables them? */
82 writel_relaxed(0x10, jtag_clk);
83
84 pil_q6v4_modem_count++;
85 mutex_unlock(&pil_q6v4_modem_lock);
86}
87
88/* Put modem subsystem back into reset */
Stephen Boyde24edf52012-07-12 17:46:19 -070089static void pil_q6v4_shutdown_modem(struct q6v4_modem *mdm)
Stephen Boydbdb53f32012-06-05 18:39:47 -070090{
91 mutex_lock(&pil_q6v4_modem_lock);
92 if (pil_q6v4_modem_count)
93 pil_q6v4_modem_count--;
94 if (pil_q6v4_modem_count == 0)
Stephen Boyde24edf52012-07-12 17:46:19 -070095 writel_relaxed(0x1, mdm->cbase + MSS_RESET);
Stephen Boydbdb53f32012-06-05 18:39:47 -070096 mutex_unlock(&pil_q6v4_modem_lock);
97}
98
99static int pil_q6v4_modem_boot(struct pil_desc *pil)
100{
101 struct q6v4_data *drv = pil_to_q6v4_data(pil);
102 struct q6v4_modem *mdm = dev_get_drvdata(pil->dev);
103 int err;
104
105 err = pil_q6v4_power_up(drv);
106 if (err)
107 return err;
108
Stephen Boyde24edf52012-07-12 17:46:19 -0700109 pil_q6v4_init_modem(mdm->modem_base, mdm->cbase, drv->jtag_clk_reg);
Stephen Boydbdb53f32012-06-05 18:39:47 -0700110 return pil_q6v4_boot(pil);
111}
112
113static int pil_q6v4_modem_shutdown(struct pil_desc *pil)
114{
115 struct q6v4_data *drv = pil_to_q6v4_data(pil);
Stephen Boyde24edf52012-07-12 17:46:19 -0700116 struct q6v4_modem *mdm = dev_get_drvdata(pil->dev);
Stephen Boydbdb53f32012-06-05 18:39:47 -0700117 int ret;
118
119 ret = pil_q6v4_shutdown(pil);
120 if (ret)
121 return ret;
Stephen Boyde24edf52012-07-12 17:46:19 -0700122 pil_q6v4_shutdown_modem(mdm);
Stephen Boydbdb53f32012-06-05 18:39:47 -0700123 pil_q6v4_power_down(drv);
124 return 0;
125}
126
127static struct pil_reset_ops pil_q6v4_modem_ops = {
Stephen Boydbdb53f32012-06-05 18:39:47 -0700128 .auth_and_reset = pil_q6v4_modem_boot,
129 .shutdown = pil_q6v4_modem_shutdown,
130 .proxy_vote = pil_q6v4_make_proxy_votes,
131 .proxy_unvote = pil_q6v4_remove_proxy_votes,
132};
133
134static struct pil_reset_ops pil_q6v4_modem_ops_trusted = {
135 .init_image = pil_q6v4_init_image_trusted,
136 .auth_and_reset = pil_q6v4_boot_trusted,
137 .shutdown = pil_q6v4_shutdown_trusted,
138 .proxy_vote = pil_q6v4_make_proxy_votes,
139 .proxy_unvote = pil_q6v4_remove_proxy_votes,
140};
141
Stephen Boyd2efa9962012-06-12 14:20:12 -0700142static void log_modem_sfr(void)
143{
144 u32 size;
145 char *smem_reason, reason[81];
146
147 smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
148 if (!smem_reason || !size) {
149 pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
150 return;
151 }
152 if (!smem_reason[0]) {
153 pr_err("modem subsystem failure reason: (unknown, init string found).\n");
154 return;
155 }
156
157 size = min(size, sizeof(reason)-1);
158 memcpy(reason, smem_reason, size);
159 reason[size] = '\0';
160 pr_err("modem subsystem failure reason: %s.\n", reason);
161
162 smem_reason[0] = '\0';
163 wmb();
164}
165
166static void restart_modem(struct q6v4_modem *drv)
167{
168 log_modem_sfr();
169 subsystem_restart_dev(drv->subsys);
170}
171
172#define desc_to_modem(d) container_of(d, struct q6v4_modem, subsys_desc)
173
Stephen Boyd3e4e9752012-06-27 12:46:32 -0700174static int modem_start(const struct subsys_desc *desc)
175{
176 struct q6v4_modem *drv = desc_to_modem(desc);
Stephen Boyde83a0a22012-06-29 13:51:27 -0700177 int ret = 0;
Stephen Boyd3e4e9752012-06-27 12:46:32 -0700178
179 if (drv->loadable) {
Stephen Boyde83a0a22012-06-29 13:51:27 -0700180 ret = pil_boot(&drv->q6_fw.desc);
181 if (ret)
182 return ret;
183 ret = pil_boot(&drv->q6_sw.desc);
184 if (ret)
185 pil_shutdown(&drv->q6_fw.desc);
Stephen Boyd3e4e9752012-06-27 12:46:32 -0700186 }
Stephen Boyde83a0a22012-06-29 13:51:27 -0700187 return ret;
Stephen Boyd3e4e9752012-06-27 12:46:32 -0700188}
189
190static void modem_stop(const struct subsys_desc *desc)
191{
192 struct q6v4_modem *drv = desc_to_modem(desc);
Stephen Boyde83a0a22012-06-29 13:51:27 -0700193 if (drv->loadable) {
194 pil_shutdown(&drv->q6_sw.desc);
195 pil_shutdown(&drv->q6_fw.desc);
196 }
Stephen Boyd3e4e9752012-06-27 12:46:32 -0700197}
198
Stephen Boyd2efa9962012-06-12 14:20:12 -0700199static int modem_shutdown(const struct subsys_desc *subsys)
200{
201 struct q6v4_modem *drv = desc_to_modem(subsys);
202
203 /* The watchdogs keep running even after the modem is shutdown */
204 writel_relaxed(0x0, drv->q6_fw.wdog_base + 0x24);
205 writel_relaxed(0x0, drv->q6_sw.wdog_base + 0x24);
206 mb();
207
208 if (drv->loadable) {
Stephen Boyde83a0a22012-06-29 13:51:27 -0700209 pil_shutdown(&drv->q6_sw.desc);
210 pil_shutdown(&drv->q6_fw.desc);
Stephen Boyd2efa9962012-06-12 14:20:12 -0700211 }
212
213 disable_irq_nosync(drv->q6_fw.wdog_irq);
214 disable_irq_nosync(drv->q6_sw.wdog_irq);
215
216 return 0;
217}
218
219static int modem_powerup(const struct subsys_desc *subsys)
220{
221 struct q6v4_modem *drv = desc_to_modem(subsys);
Stephen Boyde83a0a22012-06-29 13:51:27 -0700222 int ret;
Stephen Boyd2efa9962012-06-12 14:20:12 -0700223
224 if (drv->loadable) {
Stephen Boyde83a0a22012-06-29 13:51:27 -0700225 ret = pil_boot(&drv->q6_fw.desc);
226 if (ret)
227 return ret;
228 ret = pil_boot(&drv->q6_sw.desc);
229 if (ret) {
230 pil_shutdown(&drv->q6_fw.desc);
231 return ret;
232 }
Stephen Boyd2efa9962012-06-12 14:20:12 -0700233 }
234 enable_irq(drv->q6_fw.wdog_irq);
235 enable_irq(drv->q6_sw.wdog_irq);
236 return 0;
237}
238
239void modem_crash_shutdown(const struct subsys_desc *subsys)
240{
241 struct q6v4_modem *drv = desc_to_modem(subsys);
242
243 drv->crash_shutdown = 1;
244 smsm_reset_modem(SMSM_RESET);
245}
246
Stephen Boyd2efa9962012-06-12 14:20:12 -0700247static struct ramdump_segment smem_segments[] = {
248 {0x80000000, 0x00200000},
249};
250
251static int modem_ramdump(int enable, const struct subsys_desc *subsys)
252{
253 struct q6v4_modem *drv = desc_to_modem(subsys);
254 int ret;
255
256 if (!enable)
257 return 0;
258
Stephen Boyd5eb17ce2012-11-29 15:34:21 -0800259 ret = pil_do_ramdump(&drv->q6_sw.desc, drv->sw_ramdump_dev);
Stephen Boyd2efa9962012-06-12 14:20:12 -0700260 if (ret < 0)
261 return ret;
262
Stephen Boyd5eb17ce2012-11-29 15:34:21 -0800263 ret = pil_do_ramdump(&drv->q6_fw.desc, drv->fw_ramdump_dev);
Stephen Boyd2efa9962012-06-12 14:20:12 -0700264 if (ret < 0)
265 return ret;
266
Stephen Boyd5eb17ce2012-11-29 15:34:21 -0800267 ret = do_elf_ramdump(drv->smem_ramdump_dev, smem_segments,
Stephen Boyd2efa9962012-06-12 14:20:12 -0700268 ARRAY_SIZE(smem_segments));
269 if (ret < 0)
270 return ret;
271
272 return 0;
273}
274
275static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
276{
277 struct q6v4_modem *drv = data;
278
279 /* Ignore if we're the one that set SMSM_RESET */
280 if (drv->crash_shutdown)
281 return;
282
283 if (new_state & SMSM_RESET) {
284 pr_err("Probable fatal error on the modem.\n");
285 restart_modem(drv);
286 }
287}
288
289static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
290{
291 struct q6v4_modem *drv = dev_id;
292 restart_modem(drv);
293 return IRQ_HANDLED;
294}
295
Stephen Boydbdb53f32012-06-05 18:39:47 -0700296static int __devinit
297pil_q6v4_proc_init(struct q6v4_data *drv, struct platform_device *pdev, int i)
298{
299 static const char *name[2] = { "fw", "sw" };
300 const struct pil_q6v4_pdata *pdata_p = pdev->dev.platform_data;
301 const struct pil_q6v4_pdata *pdata = pdata_p + i;
302 char reg_name[12];
303 struct pil_desc *desc;
304 struct resource *res;
305
Stephen Boyde24edf52012-07-12 17:46:19 -0700306 res = platform_get_resource(pdev, IORESOURCE_MEM, 2 + (i * 2));
Stephen Boydf8f89282012-07-16 18:05:48 -0700307 drv->base = devm_request_and_ioremap(&pdev->dev, res);
Stephen Boydbdb53f32012-06-05 18:39:47 -0700308 if (!drv->base)
309 return -ENOMEM;
310
Stephen Boyde24edf52012-07-12 17:46:19 -0700311 res = platform_get_resource(pdev, IORESOURCE_MEM, 3 + (i * 2));
Stephen Boydf8f89282012-07-16 18:05:48 -0700312 drv->wdog_base = devm_request_and_ioremap(&pdev->dev, res);
Stephen Boyd2efa9962012-06-12 14:20:12 -0700313 if (!drv->wdog_base)
314 return -ENOMEM;
315
Stephen Boydbdb53f32012-06-05 18:39:47 -0700316 snprintf(reg_name, sizeof(reg_name), "%s_core_vdd", name[i]);
317 drv->vreg = devm_regulator_get(&pdev->dev, reg_name);
318 if (IS_ERR(drv->vreg))
319 return PTR_ERR(drv->vreg);
320
321 drv->xo = devm_clk_get(&pdev->dev, "xo");
322 if (IS_ERR(drv->xo))
323 return PTR_ERR(drv->xo);
324
325 desc = &drv->desc;
326 desc->name = pdata->name;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700327 desc->dev = &pdev->dev;
328 desc->owner = THIS_MODULE;
329 desc->proxy_timeout = 10000;
330 pil_q6v4_init(drv, pdata);
331
332 if (pas_supported(pdata->pas_id) > 0) {
333 desc->ops = &pil_q6v4_modem_ops_trusted;
334 dev_info(&pdev->dev, "using secure boot for %s\n", name[i]);
335 } else {
336 desc->ops = &pil_q6v4_modem_ops;
337 dev_info(&pdev->dev, "using non-secure boot for %s\n", name[i]);
338 }
339 return 0;
340}
341
342static int __devinit pil_q6v4_modem_driver_probe(struct platform_device *pdev)
343{
344 struct q6v4_data *drv_fw, *drv_sw;
345 struct q6v4_modem *drv;
346 struct resource *res;
347 struct regulator *pll_supply;
348 int ret;
Stephen Boyd2efa9962012-06-12 14:20:12 -0700349 const struct pil_q6v4_pdata *pdata = pdev->dev.platform_data;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700350
351 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
352 if (!drv)
353 return -ENOMEM;
354 platform_set_drvdata(pdev, drv);
355
356 drv_fw = &drv->q6_fw;
357 drv_sw = &drv->q6_sw;
358
Stephen Boyd2efa9962012-06-12 14:20:12 -0700359 drv_fw->wdog_irq = platform_get_irq(pdev, 0);
360 if (drv_fw->wdog_irq < 0)
361 return drv_fw->wdog_irq;
362
363 drv_sw->wdog_irq = platform_get_irq(pdev, 1);
364 if (drv_sw->wdog_irq < 0)
365 return drv_sw->wdog_irq;
366
367 drv->loadable = !!pdata; /* No pdata = don't use PIL */
368 if (drv->loadable) {
369 ret = pil_q6v4_proc_init(drv_fw, pdev, 0);
370 if (ret)
371 return ret;
372
373 ret = pil_q6v4_proc_init(drv_sw, pdev, 1);
374 if (ret)
375 return ret;
376
377 pll_supply = devm_regulator_get(&pdev->dev, "pll_vdd");
378 drv_fw->pll_supply = drv_sw->pll_supply = pll_supply;
379 if (IS_ERR(pll_supply))
380 return PTR_ERR(pll_supply);
381
382 ret = regulator_set_voltage(pll_supply, 1800000, 1800000);
383 if (ret) {
384 dev_err(&pdev->dev, "failed to set pll voltage\n");
385 return ret;
386 }
387
388 ret = regulator_set_optimum_mode(pll_supply, 100000);
389 if (ret < 0) {
390 dev_err(&pdev->dev, "failed to set pll optimum mode\n");
391 return ret;
392 }
393
394 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Stephen Boydf8f89282012-07-16 18:05:48 -0700395 drv->modem_base = devm_request_and_ioremap(&pdev->dev, res);
Stephen Boyd2efa9962012-06-12 14:20:12 -0700396 if (!drv->modem_base)
397 return -ENOMEM;
398
Stephen Boyde24edf52012-07-12 17:46:19 -0700399 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
400 if (!res)
401 return -EINVAL;
402 drv->cbase = devm_ioremap(&pdev->dev, res->start,
403 resource_size(res));
404 if (!drv->cbase)
405 return -ENOMEM;
406
Stephen Boyde83a0a22012-06-29 13:51:27 -0700407 ret = pil_desc_init(&drv_fw->desc);
408 if (ret)
409 return ret;
Stephen Boyd2efa9962012-06-12 14:20:12 -0700410
Stephen Boyde83a0a22012-06-29 13:51:27 -0700411 ret = pil_desc_init(&drv_sw->desc);
412 if (ret)
Stephen Boyd2efa9962012-06-12 14:20:12 -0700413 goto err_pil_sw;
Stephen Boyd2efa9962012-06-12 14:20:12 -0700414 }
415
416 drv->subsys_desc.name = "modem";
Stephen Boyd77db8bb2012-06-27 15:15:16 -0700417 drv->subsys_desc.depends_on = "adsp";
Stephen Boyd3e4e9752012-06-27 12:46:32 -0700418 drv->subsys_desc.dev = &pdev->dev;
419 drv->subsys_desc.owner = THIS_MODULE;
420 drv->subsys_desc.start = modem_start;
421 drv->subsys_desc.stop = modem_stop;
Stephen Boyd2efa9962012-06-12 14:20:12 -0700422 drv->subsys_desc.shutdown = modem_shutdown;
423 drv->subsys_desc.powerup = modem_powerup;
424 drv->subsys_desc.ramdump = modem_ramdump;
425 drv->subsys_desc.crash_shutdown = modem_crash_shutdown;
426
Stephen Boydc1a72612012-07-05 14:07:35 -0700427 drv->fw_ramdump_dev = create_ramdump_device("modem_fw", &pdev->dev);
Stephen Boyd2efa9962012-06-12 14:20:12 -0700428 if (!drv->fw_ramdump_dev) {
429 ret = -ENOMEM;
430 goto err_fw_ramdump;
431 }
432
Stephen Boydc1a72612012-07-05 14:07:35 -0700433 drv->sw_ramdump_dev = create_ramdump_device("modem_sw", &pdev->dev);
Stephen Boyd2efa9962012-06-12 14:20:12 -0700434 if (!drv->sw_ramdump_dev) {
435 ret = -ENOMEM;
436 goto err_sw_ramdump;
437 }
438
Stephen Boydc1a72612012-07-05 14:07:35 -0700439 drv->smem_ramdump_dev = create_ramdump_device("smem-modem", &pdev->dev);
Stephen Boyd2efa9962012-06-12 14:20:12 -0700440 if (!drv->smem_ramdump_dev) {
441 ret = -ENOMEM;
442 goto err_smem_ramdump;
443 }
444
445 drv->subsys = subsys_register(&drv->subsys_desc);
446 if (IS_ERR(drv->subsys)) {
447 ret = PTR_ERR(drv->subsys);
448 goto err_subsys;
449 }
Stephen Boyd43b380a2012-09-21 17:34:24 -0700450 if (!drv->loadable)
451 subsys_default_online(drv->subsys);
Stephen Boyd2efa9962012-06-12 14:20:12 -0700452
453 ret = devm_request_irq(&pdev->dev, drv_fw->wdog_irq,
454 modem_wdog_bite_irq, IRQF_TRIGGER_RISING,
455 dev_name(&pdev->dev), drv);
Stephen Boydbdb53f32012-06-05 18:39:47 -0700456 if (ret)
Stephen Boyd2efa9962012-06-12 14:20:12 -0700457 goto err_irq;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700458
Stephen Boyd2efa9962012-06-12 14:20:12 -0700459 ret = devm_request_irq(&pdev->dev, drv_sw->wdog_irq,
460 modem_wdog_bite_irq, IRQF_TRIGGER_RISING,
461 dev_name(&pdev->dev), drv);
Stephen Boydbdb53f32012-06-05 18:39:47 -0700462 if (ret)
Stephen Boyd2efa9962012-06-12 14:20:12 -0700463 goto err_irq;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700464
Stephen Boyd2efa9962012-06-12 14:20:12 -0700465 ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
466 smsm_state_cb, drv);
467 if (ret)
468 goto err_irq;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700469 return 0;
Stephen Boyd2efa9962012-06-12 14:20:12 -0700470
471err_irq:
472 subsys_unregister(drv->subsys);
473err_subsys:
474 destroy_ramdump_device(drv->smem_ramdump_dev);
475err_smem_ramdump:
476 destroy_ramdump_device(drv->sw_ramdump_dev);
477err_sw_ramdump:
478 destroy_ramdump_device(drv->fw_ramdump_dev);
479err_fw_ramdump:
480 if (drv->loadable)
Stephen Boyde83a0a22012-06-29 13:51:27 -0700481 pil_desc_release(&drv_sw->desc);
Stephen Boyd2efa9962012-06-12 14:20:12 -0700482err_pil_sw:
Stephen Boyde83a0a22012-06-29 13:51:27 -0700483 pil_desc_release(&drv_fw->desc);
Stephen Boyd2efa9962012-06-12 14:20:12 -0700484 return ret;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700485}
486
487static int __devexit pil_q6v4_modem_driver_exit(struct platform_device *pdev)
488{
489 struct q6v4_modem *drv = platform_get_drvdata(pdev);
Stephen Boyd2efa9962012-06-12 14:20:12 -0700490
491 smsm_state_cb_deregister(SMSM_MODEM_STATE, SMSM_RESET,
492 smsm_state_cb, drv);
493 subsys_unregister(drv->subsys);
494 destroy_ramdump_device(drv->smem_ramdump_dev);
495 destroy_ramdump_device(drv->sw_ramdump_dev);
496 destroy_ramdump_device(drv->fw_ramdump_dev);
497 if (drv->loadable) {
Stephen Boyde83a0a22012-06-29 13:51:27 -0700498 pil_desc_release(&drv->q6_sw.desc);
499 pil_desc_release(&drv->q6_fw.desc);
Stephen Boyd2efa9962012-06-12 14:20:12 -0700500 }
Stephen Boydbdb53f32012-06-05 18:39:47 -0700501 return 0;
502}
503
504static struct platform_driver pil_q6v4_modem_driver = {
505 .probe = pil_q6v4_modem_driver_probe,
506 .remove = __devexit_p(pil_q6v4_modem_driver_exit),
507 .driver = {
508 .name = "pil-q6v4-modem",
509 .owner = THIS_MODULE,
510 },
511};
512
513static int __init pil_q6v4_modem_init(void)
514{
515 return platform_driver_register(&pil_q6v4_modem_driver);
516}
517module_init(pil_q6v4_modem_init);
518
519static void __exit pil_q6v4_modem_exit(void)
520{
521 platform_driver_unregister(&pil_q6v4_modem_driver);
522}
523module_exit(pil_q6v4_modem_exit);
524
525MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
526MODULE_LICENSE("GPL v2");