blob: 311f8a78021d29eda515f96c148d72e1eb859797 [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>
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070018#include <linux/err.h>
19#include <linux/of.h>
Matt Wagantalld41ce772012-05-10 23:16:41 -070020#include <linux/clk.h>
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070021
22#include "peripheral-loader.h"
23#include "pil-q6v5.h"
24
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070025#define QDSP6SS_RST_EVB 0x010
Matt Wagantall4d89c2e2012-05-25 19:28:34 -070026#define PROXY_TIMEOUT_MS 10000
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070027
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070028static int pil_lpass_shutdown(struct pil_desc *pil)
29{
30 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070031
Matt Wagantallb7747992012-05-11 19:37:51 -070032 pil_q6v5_halt_axi_port(pil, drv->axi_halt_base);
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070033
Matt Wagantalld41ce772012-05-10 23:16:41 -070034 /*
35 * If the shutdown function is called before the reset function, clocks
36 * will not be enabled yet. Enable them here so that register writes
37 * performed during the shutdown succeed.
38 */
39 if (drv->is_booted == false)
40 pil_q6v5_enable_clks(pil);
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070041
42 pil_q6v5_shutdown(pil);
Matt Wagantalld41ce772012-05-10 23:16:41 -070043 pil_q6v5_disable_clks(pil);
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070044
Matt Wagantalld41ce772012-05-10 23:16:41 -070045 drv->is_booted = false;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070046
47 return 0;
48}
49
50static int pil_lpass_reset(struct pil_desc *pil)
51{
52 struct q6v5_data *drv = dev_get_drvdata(pil->dev);
Matt Wagantalld41ce772012-05-10 23:16:41 -070053 int ret;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070054
Matt Wagantalld41ce772012-05-10 23:16:41 -070055 ret = pil_q6v5_enable_clks(pil);
56 if (ret)
57 return ret;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070058
59 /* Program Image Address */
60 writel_relaxed(((drv->start_addr >> 4) & 0x0FFFFFF0),
61 drv->reg_base + QDSP6SS_RST_EVB);
62
Matt Wagantalld41ce772012-05-10 23:16:41 -070063 ret = pil_q6v5_reset(pil);
64 if (ret) {
65 pil_q6v5_disable_clks(pil);
66 return ret;
67 }
68
69 drv->is_booted = true;
70
71 return 0;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070072}
73
74static struct pil_reset_ops pil_lpass_ops = {
75 .init_image = pil_q6v5_init_image,
76 .proxy_vote = pil_q6v5_make_proxy_votes,
77 .proxy_unvote = pil_q6v5_remove_proxy_votes,
78 .auth_and_reset = pil_lpass_reset,
79 .shutdown = pil_lpass_shutdown,
80};
81
82static int __devinit pil_lpass_driver_probe(struct platform_device *pdev)
83{
84 struct q6v5_data *drv;
85 struct pil_desc *desc;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070086
87 desc = pil_q6v5_init(pdev);
Matt Wagantall55252f12012-05-02 18:02:54 -070088 if (IS_ERR(desc))
89 return PTR_ERR(desc);
90
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070091 drv = platform_get_drvdata(pdev);
Matt Wagantall55252f12012-05-02 18:02:54 -070092 if (drv == NULL)
93 return -ENODEV;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070094
95 desc->ops = &pil_lpass_ops;
96 desc->owner = THIS_MODULE;
Matt Wagantall4d89c2e2012-05-25 19:28:34 -070097 desc->proxy_timeout = PROXY_TIMEOUT_MS;
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070098
Matt Wagantallc2bbdc32012-03-21 19:44:50 -070099 drv->pil = msm_pil_register(desc);
100 if (IS_ERR(drv->pil))
101 return PTR_ERR(drv->pil);
102
103 return 0;
104}
105
106static int __devexit pil_lpass_driver_exit(struct platform_device *pdev)
107{
108 struct q6v5_data *drv = platform_get_drvdata(pdev);
109 msm_pil_unregister(drv->pil);
110 return 0;
111}
112
113static struct of_device_id lpass_match_table[] = {
114 { .compatible = "qcom,pil-q6v5-lpass" },
115 {}
116};
117
118static struct platform_driver pil_lpass_driver = {
119 .probe = pil_lpass_driver_probe,
120 .remove = __devexit_p(pil_lpass_driver_exit),
121 .driver = {
122 .name = "pil-q6v5-lpass",
123 .of_match_table = lpass_match_table,
124 .owner = THIS_MODULE,
125 },
126};
127
128static int __init pil_lpass_init(void)
129{
130 return platform_driver_register(&pil_lpass_driver);
131}
132module_init(pil_lpass_init);
133
134static void __exit pil_lpass_exit(void)
135{
136 platform_driver_unregister(&pil_lpass_driver);
137}
138module_exit(pil_lpass_exit);
139
140MODULE_DESCRIPTION("Support for booting low-power audio subsystems with QDSP6v5 (Hexagon) processors");
141MODULE_LICENSE("GPL v2");