blob: cf29cf13da765db159f8609a405e3fa7d06834c9 [file] [log] [blame]
Sameer Thalappil4ca0d0f2013-02-16 07:12:36 -08001/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
Tianyi Gouc1e049f82011-11-23 14:20:16 -08002 *
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/kernel.h>
14#include <linux/err.h>
15#include <linux/io.h>
Tianyi Gouc1e049f82011-11-23 14:20:16 -080016#include <linux/delay.h>
17#include <linux/module.h>
18#include <linux/slab.h>
19#include <linux/platform_device.h>
20#include <linux/clk.h>
21#include <linux/iopoll.h>
22#include <linux/of.h>
23#include <linux/regulator/consumer.h>
Stephen Boyd581fe852012-06-13 12:05:35 -070024#include <linux/interrupt.h>
25#include <linux/jiffies.h>
26#include <linux/workqueue.h>
27#include <linux/wcnss_wlan.h>
Sameer Thalappilcbabf072013-03-28 13:51:41 -070028#include <linux/of_gpio.h>
Stephen Boyd581fe852012-06-13 12:05:35 -070029
30#include <mach/subsystem_restart.h>
Stephen Boyd581fe852012-06-13 12:05:35 -070031#include <mach/msm_smsm.h>
Seemanta Dutta4e2d49c2013-04-05 16:28:11 -070032#include <mach/ramdump.h>
Tianyi Gouc1e049f82011-11-23 14:20:16 -080033
34#include "peripheral-loader.h"
35#include "scm-pas.h"
36
37#define PRONTO_PMU_COMMON_GDSCR 0x24
38#define PRONTO_PMU_COMMON_GDSCR_SW_COLLAPSE BIT(0)
39#define CLK_DIS_WAIT 12
40#define EN_FEW_WAIT 16
41#define EN_REST_WAIT 20
42
43#define PRONTO_PMU_COMMON_CPU_CBCR 0x30
44#define PRONTO_PMU_COMMON_CPU_CBCR_CLK_EN BIT(0)
45#define PRONTO_PMU_COMMON_CPU_CLK_OFF BIT(31)
46
47#define PRONTO_PMU_COMMON_AHB_CBCR 0x34
48#define PRONTO_PMU_COMMON_AHB_CBCR_CLK_EN BIT(0)
49#define PRONTO_PMU_COMMON_AHB_CLK_OFF BIT(31)
50
51#define PRONTO_PMU_COMMON_CSR 0x1040
52#define PRONTO_PMU_COMMON_CSR_A2XB_CFG_EN BIT(0)
53
54#define PRONTO_PMU_SOFT_RESET 0x104C
55#define PRONTO_PMU_SOFT_RESET_CRCM_CCPU_SOFT_RESET BIT(10)
56
57#define PRONTO_PMU_CCPU_CTL 0x2000
58#define PRONTO_PMU_CCPU_CTL_REMAP_EN BIT(2)
59#define PRONTO_PMU_CCPU_CTL_HIGH_IVT BIT(0)
60
61#define PRONTO_PMU_CCPU_BOOT_REMAP_ADDR 0x2004
62
63#define CLK_CTL_WCNSS_RESTART_BIT BIT(0)
64
65#define AXI_HALTREQ 0x0
66#define AXI_HALTACK 0x4
67#define AXI_IDLE 0x8
68
69#define HALT_ACK_TIMEOUT_US 500000
70#define CLK_UPDATE_TIMEOUT_US 500000
71
72struct pronto_data {
73 void __iomem *base;
74 void __iomem *reset_base;
75 void __iomem *axi_halt_base;
Tianyi Gouc1e049f82011-11-23 14:20:16 -080076 struct pil_device *pil;
Stephen Boyd3e4e9752012-06-27 12:46:32 -070077 struct pil_desc desc;
Stephen Boyd581fe852012-06-13 12:05:35 -070078 struct subsys_device *subsys;
79 struct subsys_desc subsys_desc;
Tianyi Gouc1e049f82011-11-23 14:20:16 -080080 struct clk *cxo;
81 struct regulator *vreg;
Stephen Boyd581fe852012-06-13 12:05:35 -070082 bool restart_inprogress;
83 bool crash;
84 struct delayed_work cancel_vote_work;
85 int irq;
Sameer Thalappilcbabf072013-03-28 13:51:41 -070086 unsigned int err_fatal_irq;
87 int force_stop_gpio;
Sameer Thalappilb7488b22012-11-09 16:40:12 -080088 struct ramdump_device *ramdump_dev;
Tianyi Gouc1e049f82011-11-23 14:20:16 -080089};
90
91static int pil_pronto_make_proxy_vote(struct pil_desc *pil)
92{
93 struct pronto_data *drv = dev_get_drvdata(pil->dev);
94 int ret;
95
96 ret = regulator_enable(drv->vreg);
97 if (ret) {
98 dev_err(pil->dev, "failed to enable pll supply\n");
99 goto err;
100 }
101 ret = clk_prepare_enable(drv->cxo);
102 if (ret) {
103 dev_err(pil->dev, "failed to enable cxo\n");
104 goto err_clk;
105 }
106 return 0;
107err_clk:
108 regulator_disable(drv->vreg);
109err:
110 return ret;
111}
112
113static void pil_pronto_remove_proxy_vote(struct pil_desc *pil)
114{
115 struct pronto_data *drv = dev_get_drvdata(pil->dev);
116 regulator_disable(drv->vreg);
117 clk_disable_unprepare(drv->cxo);
118}
119
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800120static int pil_pronto_reset(struct pil_desc *pil)
121{
122 u32 reg;
123 int rc;
124 struct pronto_data *drv = dev_get_drvdata(pil->dev);
125 void __iomem *base = drv->base;
Tianyi Gou819851e2013-04-16 16:05:56 -0700126 phys_addr_t start_addr = pil_get_entry_addr(pil);
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800127
Matt Wagantall33c2ec72012-07-26 20:26:57 -0700128 /* Deassert reset to subsystem and wait for propagation */
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800129 reg = readl_relaxed(drv->reset_base);
130 reg &= ~CLK_CTL_WCNSS_RESTART_BIT;
131 writel_relaxed(reg, drv->reset_base);
132 mb();
Matt Wagantall33c2ec72012-07-26 20:26:57 -0700133 udelay(2);
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800134
135 /* Configure boot address */
136 writel_relaxed(start_addr >> 16, base +
137 PRONTO_PMU_CCPU_BOOT_REMAP_ADDR);
138
139 /* Use the high vector table */
140 reg = readl_relaxed(base + PRONTO_PMU_CCPU_CTL);
141 reg |= PRONTO_PMU_CCPU_CTL_REMAP_EN | PRONTO_PMU_CCPU_CTL_HIGH_IVT;
142 writel_relaxed(reg, base + PRONTO_PMU_CCPU_CTL);
143
144 /* Turn on AHB clock of common_ss */
145 reg = readl_relaxed(base + PRONTO_PMU_COMMON_AHB_CBCR);
146 reg |= PRONTO_PMU_COMMON_AHB_CBCR_CLK_EN;
147 writel_relaxed(reg, base + PRONTO_PMU_COMMON_AHB_CBCR);
148
149 /* Turn on CPU clock of common_ss */
150 reg = readl_relaxed(base + PRONTO_PMU_COMMON_CPU_CBCR);
151 reg |= PRONTO_PMU_COMMON_CPU_CBCR_CLK_EN;
152 writel_relaxed(reg, base + PRONTO_PMU_COMMON_CPU_CBCR);
153
154 /* Enable A2XB bridge */
155 reg = readl_relaxed(base + PRONTO_PMU_COMMON_CSR);
156 reg |= PRONTO_PMU_COMMON_CSR_A2XB_CFG_EN;
157 writel_relaxed(reg, base + PRONTO_PMU_COMMON_CSR);
158
159 /* Enable common_ss power */
160 reg = readl_relaxed(base + PRONTO_PMU_COMMON_GDSCR);
161 reg &= ~PRONTO_PMU_COMMON_GDSCR_SW_COLLAPSE;
162 writel_relaxed(reg, base + PRONTO_PMU_COMMON_GDSCR);
163
164 /* Wait for AHB clock to be on */
165 rc = readl_tight_poll_timeout(base + PRONTO_PMU_COMMON_AHB_CBCR,
166 reg,
167 !(reg & PRONTO_PMU_COMMON_AHB_CLK_OFF),
168 CLK_UPDATE_TIMEOUT_US);
169 if (rc) {
170 dev_err(pil->dev, "pronto common ahb clk enable timeout\n");
171 return rc;
172 }
173
174 /* Wait for CPU clock to be on */
175 rc = readl_tight_poll_timeout(base + PRONTO_PMU_COMMON_CPU_CBCR,
176 reg,
177 !(reg & PRONTO_PMU_COMMON_CPU_CLK_OFF),
178 CLK_UPDATE_TIMEOUT_US);
179 if (rc) {
180 dev_err(pil->dev, "pronto common cpu clk enable timeout\n");
181 return rc;
182 }
183
184 /* Deassert ARM9 software reset */
185 reg = readl_relaxed(base + PRONTO_PMU_SOFT_RESET);
186 reg &= ~PRONTO_PMU_SOFT_RESET_CRCM_CCPU_SOFT_RESET;
187 writel_relaxed(reg, base + PRONTO_PMU_SOFT_RESET);
188
189 return 0;
190}
191
192static int pil_pronto_shutdown(struct pil_desc *pil)
193{
194 struct pronto_data *drv = dev_get_drvdata(pil->dev);
195 int ret;
196 u32 reg, status;
197
198 /* Halt A2XB */
199 writel_relaxed(1, drv->axi_halt_base + AXI_HALTREQ);
200 ret = readl_poll_timeout(drv->axi_halt_base + AXI_HALTACK,
201 status, status, 50, HALT_ACK_TIMEOUT_US);
202 if (ret)
203 dev_err(pil->dev, "Port halt timeout\n");
204 else if (!readl_relaxed(drv->axi_halt_base + AXI_IDLE))
205 dev_err(pil->dev, "Port halt failed\n");
206
207 writel_relaxed(0, drv->axi_halt_base + AXI_HALTREQ);
208
209 /* Assert reset to Pronto */
210 reg = readl_relaxed(drv->reset_base);
211 reg |= CLK_CTL_WCNSS_RESTART_BIT;
212 writel_relaxed(reg, drv->reset_base);
213
214 /* Wait for reset to complete */
215 mb();
216 usleep_range(1000, 2000);
217
Matt Wagantall44131df2012-08-03 18:29:47 -0700218 /* Deassert reset to subsystem and wait for propagation */
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800219 reg = readl_relaxed(drv->reset_base);
220 reg &= ~CLK_CTL_WCNSS_RESTART_BIT;
221 writel_relaxed(reg, drv->reset_base);
222 mb();
Matt Wagantall44131df2012-08-03 18:29:47 -0700223 udelay(2);
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800224
225 return 0;
226}
227
228static struct pil_reset_ops pil_pronto_ops = {
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800229 .auth_and_reset = pil_pronto_reset,
230 .shutdown = pil_pronto_shutdown,
231 .proxy_vote = pil_pronto_make_proxy_vote,
232 .proxy_unvote = pil_pronto_remove_proxy_vote,
233};
234
Tianyi Gou13d85b92012-11-12 18:04:07 -0800235static int pil_pronto_init_image_trusted(struct pil_desc *pil,
236 const u8 *metadata, size_t size)
237{
238 return pas_init_image(PAS_WCNSS, metadata, size);
239}
240
Stephen Boydc8c5db92012-12-03 11:13:20 -0800241static int pil_pronto_mem_setup_trusted(struct pil_desc *pil, phys_addr_t addr,
242 size_t size)
243{
244 return pas_mem_setup(PAS_WCNSS, addr, size);
245}
246
Tianyi Gou13d85b92012-11-12 18:04:07 -0800247static int pil_pronto_reset_trusted(struct pil_desc *pil)
248{
249 return pas_auth_and_reset(PAS_WCNSS);
250}
251
252static int pil_pronto_shutdown_trusted(struct pil_desc *pil)
253{
254 return pas_shutdown(PAS_WCNSS);
255}
256
257static struct pil_reset_ops pil_pronto_ops_trusted = {
258 .init_image = pil_pronto_init_image_trusted,
Stephen Boydc8c5db92012-12-03 11:13:20 -0800259 .mem_setup = pil_pronto_mem_setup_trusted,
Tianyi Gou13d85b92012-11-12 18:04:07 -0800260 .auth_and_reset = pil_pronto_reset_trusted,
261 .shutdown = pil_pronto_shutdown_trusted,
262 .proxy_vote = pil_pronto_make_proxy_vote,
263 .proxy_unvote = pil_pronto_remove_proxy_vote,
264};
265
Stephen Boyd581fe852012-06-13 12:05:35 -0700266#define subsys_to_drv(d) container_of(d, struct pronto_data, subsys_desc)
267
Stephen Boyd3e4e9752012-06-27 12:46:32 -0700268static int pronto_start(const struct subsys_desc *desc)
269{
Stephen Boyd3e4e9752012-06-27 12:46:32 -0700270 struct pronto_data *drv = subsys_to_drv(desc);
Stephen Boyde83a0a22012-06-29 13:51:27 -0700271 return pil_boot(&drv->desc);
Stephen Boyd3e4e9752012-06-27 12:46:32 -0700272}
273
274static void pronto_stop(const struct subsys_desc *desc)
275{
276 struct pronto_data *drv = subsys_to_drv(desc);
Stephen Boyde83a0a22012-06-29 13:51:27 -0700277 pil_shutdown(&drv->desc);
Stephen Boyd3e4e9752012-06-27 12:46:32 -0700278}
279
Stephen Boyd581fe852012-06-13 12:05:35 -0700280static void log_wcnss_sfr(void)
281{
282 char *smem_reset_reason;
283 unsigned smem_reset_size;
284
285 smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_WCNSS0,
286 &smem_reset_size);
287
288 if (!smem_reset_reason || !smem_reset_size) {
289 pr_err("wcnss subsystem failure reason:\n"
290 "(unknown, smem_get_entry failed)");
291 } else if (!smem_reset_reason[0]) {
292 pr_err("wcnss subsystem failure reason:\n"
293 "(unknown, init string found)");
294 } else {
295 pr_err("wcnss subsystem failure reason: %.81s\n",
296 smem_reset_reason);
297 memset(smem_reset_reason, 0, smem_reset_size);
298 wmb();
299 }
300}
301
302static void restart_wcnss(struct pronto_data *drv)
303{
304 log_wcnss_sfr();
305 subsystem_restart_dev(drv->subsys);
306}
307
Sameer Thalappilcbabf072013-03-28 13:51:41 -0700308static irqreturn_t wcnss_err_fatal_intr_handler(int irq, void *dev_id)
Stephen Boyd581fe852012-06-13 12:05:35 -0700309{
Sameer Thalappilcbabf072013-03-28 13:51:41 -0700310 struct pronto_data *drv = dev_id;
311
312 pr_err("Fatal error on the wcnss.\n");
Stephen Boyd581fe852012-06-13 12:05:35 -0700313
314 drv->crash = true;
Stephen Boyd581fe852012-06-13 12:05:35 -0700315 if (drv->restart_inprogress) {
Sameer Thalappilcbabf072013-03-28 13:51:41 -0700316 pr_err("wcnss: Ignoring error fatal, restart in progress\n");
317 return IRQ_HANDLED;
Stephen Boyd581fe852012-06-13 12:05:35 -0700318 }
319
320 drv->restart_inprogress = true;
321 restart_wcnss(drv);
Sameer Thalappilcbabf072013-03-28 13:51:41 -0700322
323 return IRQ_HANDLED;
Stephen Boyd581fe852012-06-13 12:05:35 -0700324}
325
326static irqreturn_t wcnss_wdog_bite_irq_hdlr(int irq, void *dev_id)
327{
328 struct pronto_data *drv = dev_id;
329
330 drv->crash = true;
331
332 if (drv->restart_inprogress) {
333 pr_err("Ignoring wcnss bite irq, restart in progress\n");
334 return IRQ_HANDLED;
335 }
336
Sameer Thalappil480892c2012-11-14 13:56:13 -0800337 disable_irq_nosync(drv->irq);
Stephen Boyd581fe852012-06-13 12:05:35 -0700338 drv->restart_inprogress = true;
339 restart_wcnss(drv);
340
341 return IRQ_HANDLED;
342}
343
344static void wcnss_post_bootup(struct work_struct *work)
345{
346 struct platform_device *pdev = wcnss_get_platform_device();
347 struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
348
349 wcnss_wlan_power(&pdev->dev, pwlanconfig, WCNSS_WLAN_SWITCH_OFF);
350}
351
352static int wcnss_shutdown(const struct subsys_desc *subsys)
353{
354 struct pronto_data *drv = subsys_to_drv(subsys);
355
Stephen Boyde83a0a22012-06-29 13:51:27 -0700356 pil_shutdown(&drv->desc);
Stephen Boyd581fe852012-06-13 12:05:35 -0700357 flush_delayed_work(&drv->cancel_vote_work);
358 wcnss_flush_delayed_boot_votes();
Stephen Boyd581fe852012-06-13 12:05:35 -0700359
360 return 0;
361}
362
363static int wcnss_powerup(const struct subsys_desc *subsys)
364{
365 struct pronto_data *drv = subsys_to_drv(subsys);
366 struct platform_device *pdev = wcnss_get_platform_device();
367 struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
368 int ret = -1;
369
370 if (pdev && pwlanconfig)
371 ret = wcnss_wlan_power(&pdev->dev, pwlanconfig,
372 WCNSS_WLAN_SWITCH_ON);
373 if (!ret) {
374 msleep(1000);
Stephen Boyde83a0a22012-06-29 13:51:27 -0700375 ret = pil_boot(&drv->desc);
376 if (ret)
377 return ret;
Stephen Boyd581fe852012-06-13 12:05:35 -0700378 }
379 drv->restart_inprogress = false;
380 enable_irq(drv->irq);
381 schedule_delayed_work(&drv->cancel_vote_work, msecs_to_jiffies(5000));
382
383 return 0;
384}
385
386static void crash_shutdown(const struct subsys_desc *subsys)
387{
388 struct pronto_data *drv = subsys_to_drv(subsys);
389
390 pr_err("wcnss crash shutdown %d\n", drv->crash);
391 if (!drv->crash)
Sameer Thalappilcbabf072013-03-28 13:51:41 -0700392 gpio_set_value(drv->force_stop_gpio, 1);
Stephen Boyd581fe852012-06-13 12:05:35 -0700393}
394
Sameer Thalappilb7488b22012-11-09 16:40:12 -0800395static int wcnss_ramdump(int enable, const struct subsys_desc *subsys)
Stephen Boyd581fe852012-06-13 12:05:35 -0700396{
Sameer Thalappilb7488b22012-11-09 16:40:12 -0800397 struct pronto_data *drv = subsys_to_drv(subsys);
398
Stephen Boyd5eb17ce2012-11-29 15:34:21 -0800399 if (!enable)
Sameer Thalappilb7488b22012-11-09 16:40:12 -0800400 return 0;
Stephen Boyd5eb17ce2012-11-29 15:34:21 -0800401
402 return pil_do_ramdump(&drv->desc, drv->ramdump_dev);
Stephen Boyd581fe852012-06-13 12:05:35 -0700403}
404
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800405static int __devinit pil_pronto_probe(struct platform_device *pdev)
406{
407 struct pronto_data *drv;
408 struct resource *res;
409 struct pil_desc *desc;
Sameer Thalappilcbabf072013-03-28 13:51:41 -0700410 int ret, err_fatal_gpio, irq;
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800411 uint32_t regval;
412
Sameer Thalappil4ba86302013-04-05 17:36:54 -0700413 int clk_ready = of_get_named_gpio(pdev->dev.of_node,
414 "qcom,gpio-proxy-unvote", 0);
415 if (clk_ready < 0)
416 return clk_ready;
417
418 clk_ready = gpio_to_irq(clk_ready);
419 if (clk_ready < 0)
420 return clk_ready;
421
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800422 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
423 if (!drv)
424 return -ENOMEM;
425 platform_set_drvdata(pdev, drv);
426
Stephen Boyd581fe852012-06-13 12:05:35 -0700427 drv->irq = platform_get_irq(pdev, 0);
428 if (drv->irq < 0)
429 return drv->irq;
430
Stephen Boydf8f89282012-07-16 18:05:48 -0700431 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmu_base");
432 drv->base = devm_request_and_ioremap(&pdev->dev, res);
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800433 if (!drv->base)
434 return -ENOMEM;
435
Matt Wagantall1f168152012-09-25 13:26:47 -0700436 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "clk_base");
Stephen Boydf8f89282012-07-16 18:05:48 -0700437 drv->reset_base = devm_request_and_ioremap(&pdev->dev, res);
438 if (!drv->reset_base)
439 return -ENOMEM;
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800440
Matt Wagantall1f168152012-09-25 13:26:47 -0700441 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "halt_base");
Stephen Boydf8f89282012-07-16 18:05:48 -0700442 drv->axi_halt_base = devm_request_and_ioremap(&pdev->dev, res);
443 if (!drv->axi_halt_base)
444 return -ENOMEM;
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800445
Stephen Boyd3e4e9752012-06-27 12:46:32 -0700446 desc = &drv->desc;
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800447 ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
448 &desc->name);
449 if (ret)
450 return ret;
451
Sameer Thalappilcbabf072013-03-28 13:51:41 -0700452 err_fatal_gpio = of_get_named_gpio(pdev->dev.of_node,
453 "qcom,gpio-err-fatal", 0);
454 if (err_fatal_gpio < 0)
455 return err_fatal_gpio;
456
457 irq = gpio_to_irq(err_fatal_gpio);
458 if (irq < 0)
459 return irq;
460
461 drv->err_fatal_irq = irq;
462
463 drv->force_stop_gpio = of_get_named_gpio(pdev->dev.of_node,
464 "qcom,gpio-force-stop", 0);
465 if (drv->force_stop_gpio < 0)
466 return drv->force_stop_gpio;
467
468
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800469 desc->dev = &pdev->dev;
470 desc->owner = THIS_MODULE;
471 desc->proxy_timeout = 10000;
Sameer Thalappil4ba86302013-04-05 17:36:54 -0700472 desc->proxy_unvote_irq = clk_ready;
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800473
Tianyi Gou13d85b92012-11-12 18:04:07 -0800474 if (pas_supported(PAS_WCNSS) > 0) {
475 desc->ops = &pil_pronto_ops_trusted;
476 dev_info(&pdev->dev, "using secure boot\n");
477 } else {
478 desc->ops = &pil_pronto_ops;
479 dev_info(&pdev->dev, "using non-secure boot\n");
480 }
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800481
482 drv->vreg = devm_regulator_get(&pdev->dev, "vdd_pronto_pll");
483 if (IS_ERR(drv->vreg)) {
484 dev_err(&pdev->dev, "failed to get pronto pll supply");
485 return PTR_ERR(drv->vreg);
486 }
487
488 ret = regulator_set_voltage(drv->vreg, 1800000, 1800000);
489 if (ret) {
490 dev_err(&pdev->dev, "failed to set pll supply voltage\n");
491 return ret;
492 }
493
494 ret = regulator_set_optimum_mode(drv->vreg, 18000);
495 if (ret < 0) {
496 dev_err(&pdev->dev, "failed to set pll supply mode\n");
497 return ret;
498 }
499
500 drv->cxo = devm_clk_get(&pdev->dev, "xo");
501 if (IS_ERR(drv->cxo))
502 return PTR_ERR(drv->cxo);
503
Stephen Boyde83a0a22012-06-29 13:51:27 -0700504 ret = pil_desc_init(desc);
505 if (ret)
506 return ret;
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800507
Stephen Boyd581fe852012-06-13 12:05:35 -0700508 drv->subsys_desc.name = desc->name;
509 drv->subsys_desc.dev = &pdev->dev;
510 drv->subsys_desc.owner = THIS_MODULE;
511 drv->subsys_desc.shutdown = wcnss_shutdown;
512 drv->subsys_desc.powerup = wcnss_powerup;
513 drv->subsys_desc.ramdump = wcnss_ramdump;
514 drv->subsys_desc.crash_shutdown = crash_shutdown;
Stephen Boyd3e4e9752012-06-27 12:46:32 -0700515 drv->subsys_desc.start = pronto_start;
516 drv->subsys_desc.stop = pronto_stop;
Stephen Boyd581fe852012-06-13 12:05:35 -0700517
518 INIT_DELAYED_WORK(&drv->cancel_vote_work, wcnss_post_bootup);
519
520 drv->subsys = subsys_register(&drv->subsys_desc);
521 if (IS_ERR(drv->subsys)) {
522 ret = PTR_ERR(drv->subsys);
523 goto err_subsys;
524 }
525
526 ret = devm_request_irq(&pdev->dev, drv->irq, wcnss_wdog_bite_irq_hdlr,
527 IRQF_TRIGGER_HIGH, "wcnss_wdog", drv);
528 if (ret < 0)
529 goto err_irq;
530
Sameer Thalappilcbabf072013-03-28 13:51:41 -0700531 ret = devm_request_irq(&pdev->dev, drv->err_fatal_irq,
532 wcnss_err_fatal_intr_handler,
533 IRQF_TRIGGER_RISING, "pil-pronto", drv);
534 if (ret < 0) {
535 dev_err(&pdev->dev, "Unable to register SMP2P err fatal handler!\n");
536 goto err_irq;
537 }
538
Sameer Thalappilb7488b22012-11-09 16:40:12 -0800539 drv->ramdump_dev = create_ramdump_device("pronto", &pdev->dev);
540 if (!drv->ramdump_dev) {
541 ret = -ENOMEM;
542 goto err_irq;
543 }
544
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800545 /* Initialize common_ss GDSCR to wait 4 cycles between states */
546 regval = readl_relaxed(drv->base + PRONTO_PMU_COMMON_GDSCR)
547 & PRONTO_PMU_COMMON_GDSCR_SW_COLLAPSE;
548 regval |= (2 << EN_REST_WAIT) | (2 << EN_FEW_WAIT)
549 | (2 << CLK_DIS_WAIT);
550 writel_relaxed(regval, drv->base + PRONTO_PMU_COMMON_GDSCR);
551
552 return 0;
Sameer Thalappilcbabf072013-03-28 13:51:41 -0700553
Stephen Boyd581fe852012-06-13 12:05:35 -0700554err_irq:
555 subsys_unregister(drv->subsys);
556err_subsys:
Stephen Boyde83a0a22012-06-29 13:51:27 -0700557 pil_desc_release(desc);
Stephen Boyd581fe852012-06-13 12:05:35 -0700558 return ret;
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800559}
560
561static int __devexit pil_pronto_remove(struct platform_device *pdev)
562{
563 struct pronto_data *drv = platform_get_drvdata(pdev);
Stephen Boyd581fe852012-06-13 12:05:35 -0700564 subsys_unregister(drv->subsys);
Stephen Boyde83a0a22012-06-29 13:51:27 -0700565 pil_desc_release(&drv->desc);
Sameer Thalappilb7488b22012-11-09 16:40:12 -0800566 destroy_ramdump_device(drv->ramdump_dev);
Tianyi Gouc1e049f82011-11-23 14:20:16 -0800567 return 0;
568}
569
570static struct of_device_id msm_pil_pronto_match[] = {
571 {.compatible = "qcom,pil-pronto"},
572 {}
573};
574
575static struct platform_driver pil_pronto_driver = {
576 .probe = pil_pronto_probe,
577 .remove = __devexit_p(pil_pronto_remove),
578 .driver = {
579 .name = "pil_pronto",
580 .owner = THIS_MODULE,
581 .of_match_table = msm_pil_pronto_match,
582 },
583};
584
585static int __init pil_pronto_init(void)
586{
587 return platform_driver_register(&pil_pronto_driver);
588}
589module_init(pil_pronto_init);
590
591static void __exit pil_pronto_exit(void)
592{
593 platform_driver_unregister(&pil_pronto_driver);
594}
595module_exit(pil_pronto_exit);
596
597MODULE_DESCRIPTION("Support for booting PRONTO (WCNSS) processors");
598MODULE_LICENSE("GPL v2");