blob: 8691ac7af8a29dafaafe4ff5657a2da6bca6dfc4 [file] [log] [blame]
Matt Wagantallc2bbdc32012-03-21 19:44:50 -07001/*
2 * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/init.h>
15#include <linux/module.h>
16#include <linux/platform_device.h>
17#include <linux/io.h>
18#include <linux/iopoll.h>
19#include <linux/err.h>
20#include <linux/of.h>
Matt Wagantalld41ce772012-05-10 23:16:41 -070021#include <linux/clk.h>
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070022
23#include "peripheral-loader.h"
24#include "pil-q6v5.h"
25
26/* Register Offsets */
27#define QDSP6SS_RST_EVB 0x010
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070028#define AXI_HALTREQ 0x0
29#define AXI_HALTACK 0x4
30#define AXI_IDLE 0x8
31
32#define HALT_ACK_TIMEOUT_US 100000
33
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070034static int pil_lpass_shutdown(struct pil_desc *pil)
35{
36 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
37 int ret;
38 u32 status;
39
40 writel_relaxed(1, drv->axi_halt_base + AXI_HALTREQ);
41 ret = readl_poll_timeout(drv->axi_halt_base + AXI_HALTACK,
42 status, status, 50, HALT_ACK_TIMEOUT_US);
43 if (ret)
44 dev_err(pil->dev, "Port halt timeout\n");
45 else if (!readl_relaxed(drv->axi_halt_base + AXI_IDLE))
46 dev_err(pil->dev, "Port halt failed\n");
47 writel_relaxed(0, drv->axi_halt_base + AXI_HALTREQ);
48
Matt Wagantalld41ce772012-05-10 23:16:41 -070049 /*
50 * If the shutdown function is called before the reset function, clocks
51 * will not be enabled yet. Enable them here so that register writes
52 * performed during the shutdown succeed.
53 */
54 if (drv->is_booted == false)
55 pil_q6v5_enable_clks(pil);
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070056
57 pil_q6v5_shutdown(pil);
Matt Wagantalld41ce772012-05-10 23:16:41 -070058 pil_q6v5_disable_clks(pil);
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070059
Matt Wagantalld41ce772012-05-10 23:16:41 -070060 drv->is_booted = false;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070061
62 return 0;
63}
64
65static int pil_lpass_reset(struct pil_desc *pil)
66{
67 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
Matt Wagantalld41ce772012-05-10 23:16:41 -070068 int ret;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070069
Matt Wagantalld41ce772012-05-10 23:16:41 -070070 ret = pil_q6v5_enable_clks(pil);
71 if (ret)
72 return ret;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070073
74 /* Program Image Address */
75 writel_relaxed(((drv->start_addr >> 4) & 0x0FFFFFF0),
76 drv->reg_base + QDSP6SS_RST_EVB);
77
Matt Wagantalld41ce772012-05-10 23:16:41 -070078 ret = pil_q6v5_reset(pil);
79 if (ret) {
80 pil_q6v5_disable_clks(pil);
81 return ret;
82 }
83
84 drv->is_booted = true;
85
86 return 0;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070087}
88
89static struct pil_reset_ops pil_lpass_ops = {
90 .init_image = pil_q6v5_init_image,
91 .proxy_vote = pil_q6v5_make_proxy_votes,
92 .proxy_unvote = pil_q6v5_remove_proxy_votes,
93 .auth_and_reset = pil_lpass_reset,
94 .shutdown = pil_lpass_shutdown,
95};
96
97static int __devinit pil_lpass_driver_probe(struct platform_device *pdev)
98{
99 struct q6v5_data *drv;
100 struct pil_desc *desc;
101 struct resource *res;
102
103 desc = pil_q6v5_init(pdev);
Matt Wagantall55252f12012-05-02 18:02:54 -0700104 if (IS_ERR(desc))
105 return PTR_ERR(desc);
106
Matt Wagantallc2bbdc32012-03-21 19:44:50 -0700107 drv = platform_get_drvdata(pdev);
Matt Wagantall55252f12012-05-02 18:02:54 -0700108 if (drv == NULL)
109 return -ENODEV;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -0700110
111 desc->ops = &pil_lpass_ops;
112 desc->owner = THIS_MODULE;
113
Matt Wagantalld41ce772012-05-10 23:16:41 -0700114 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
Matt Wagantallc2bbdc32012-03-21 19:44:50 -0700115 drv->axi_halt_base = devm_ioremap(&pdev->dev, res->start,
116 resource_size(res));
117 if (!drv->axi_halt_base)
118 return -ENOMEM;
119
120 drv->pil = msm_pil_register(desc);
121 if (IS_ERR(drv->pil))
122 return PTR_ERR(drv->pil);
123
124 return 0;
125}
126
127static int __devexit pil_lpass_driver_exit(struct platform_device *pdev)
128{
129 struct q6v5_data *drv = platform_get_drvdata(pdev);
130 msm_pil_unregister(drv->pil);
131 return 0;
132}
133
134static struct of_device_id lpass_match_table[] = {
135 { .compatible = "qcom,pil-q6v5-lpass" },
136 {}
137};
138
139static struct platform_driver pil_lpass_driver = {
140 .probe = pil_lpass_driver_probe,
141 .remove = __devexit_p(pil_lpass_driver_exit),
142 .driver = {
143 .name = "pil-q6v5-lpass",
144 .of_match_table = lpass_match_table,
145 .owner = THIS_MODULE,
146 },
147};
148
149static int __init pil_lpass_init(void)
150{
151 return platform_driver_register(&pil_lpass_driver);
152}
153module_init(pil_lpass_init);
154
155static void __exit pil_lpass_exit(void)
156{
157 platform_driver_unregister(&pil_lpass_driver);
158}
159module_exit(pil_lpass_exit);
160
161MODULE_DESCRIPTION("Support for booting low-power audio subsystems with QDSP6v5 (Hexagon) processors");
162MODULE_LICENSE("GPL v2");