blob: 5aa383456c1c468dbea30d1ba37ad3c0332e7d44 [file] [log] [blame]
Stephen Boyd4eb885b2011-09-29 01:16:03 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
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/io.h>
17#include <linux/ioport.h>
18#include <linux/elf.h>
19#include <linux/delay.h>
20#include <linux/err.h>
21
22#include <mach/msm_iomap.h>
23#include <mach/msm_xo.h>
24
25#include "peripheral-loader.h"
26#include "scm-pas.h"
27
28#define MARM_BOOT_CONTROL 0x0010
29#define MARM_RESET (MSM_CLK_CTL_BASE + 0x2BD4)
30#define MAHB0_SFAB_PORT_RESET (MSM_CLK_CTL_BASE + 0x2304)
31#define MARM_CLK_BRANCH_ENA_VOTE (MSM_CLK_CTL_BASE + 0x3000)
32#define MARM_CLK_SRC0_NS (MSM_CLK_CTL_BASE + 0x2BC0)
33#define MARM_CLK_SRC1_NS (MSM_CLK_CTL_BASE + 0x2BC4)
34#define MARM_CLK_SRC_CTL (MSM_CLK_CTL_BASE + 0x2BC8)
35#define MARM_CLK_CTL (MSM_CLK_CTL_BASE + 0x2BCC)
36#define SFAB_MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C00)
37#define MSS_MODEM_CXO_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C44)
38#define MSS_SLP_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C60)
39#define MSS_MARM_SYS_REF_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C64)
40#define MAHB0_CLK_CTL (MSM_CLK_CTL_BASE + 0x2300)
41#define MAHB1_CLK_CTL (MSM_CLK_CTL_BASE + 0x2BE4)
42#define MAHB2_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C20)
43#define MAHB1_NS (MSM_CLK_CTL_BASE + 0x2BE0)
44#define MARM_CLK_FS (MSM_CLK_CTL_BASE + 0x2BD0)
45#define MAHB2_CLK_FS (MSM_CLK_CTL_BASE + 0x2C24)
46#define PLL_ENA_MARM (MSM_CLK_CTL_BASE + 0x3500)
47#define PLL8_STATUS (MSM_CLK_CTL_BASE + 0x3158)
48#define CLK_HALT_MSS_SMPSS_MISC_STATE (MSM_CLK_CTL_BASE + 0x2FDC)
49
50#define PROXY_VOTE_TIMEOUT 10000
51
52struct modem_data {
53 void __iomem *base;
54 unsigned long start_addr;
55 struct msm_xo_voter *pxo;
56 struct timer_list timer;
57};
58
59static int nop_verify_blob(struct pil_desc *pil, u32 phy_addr, size_t size)
60{
61 return 0;
62}
63
64static void remove_proxy_votes(unsigned long data)
65{
66 struct modem_data *drv = (struct modem_data *)data;
67 msm_xo_mode_vote(drv->pxo, MSM_XO_MODE_OFF);
68}
69
70static void make_modem_proxy_votes(struct device *dev)
71{
72 int ret;
73 struct modem_data *drv = dev_get_drvdata(dev);
74
75 ret = msm_xo_mode_vote(drv->pxo, MSM_XO_MODE_ON);
76 if (ret)
77 dev_err(dev, "Failed to enable PXO\n");
78 mod_timer(&drv->timer, jiffies + msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
79}
80
81static void remove_modem_proxy_votes_now(struct modem_data *drv)
82{
83 /* If the proxy vote hasn't been removed yet, remove it immediately. */
84 if (del_timer(&drv->timer))
85 remove_proxy_votes((unsigned long)drv);
86}
87
88static int modem_init_image(struct pil_desc *pil, const u8 *metadata,
89 size_t size)
90{
91 const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
92 struct modem_data *drv = dev_get_drvdata(pil->dev);
93 drv->start_addr = ehdr->e_entry;
94 return 0;
95}
96
97static int modem_reset(struct pil_desc *pil)
98{
99 u32 reg;
100 const struct modem_data *drv = dev_get_drvdata(pil->dev);
101
102 make_modem_proxy_votes(pil->dev);
103
104 /* Put modem AHB0,1,2 clocks into reset */
105 writel_relaxed(BIT(0) | BIT(1), MAHB0_SFAB_PORT_RESET);
106 writel_relaxed(BIT(7), MAHB1_CLK_CTL);
107 writel_relaxed(BIT(7), MAHB2_CLK_CTL);
108
109 /* Vote for pll8 on behalf of the modem */
110 reg = readl_relaxed(PLL_ENA_MARM);
111 reg |= BIT(8);
112 writel_relaxed(reg, PLL_ENA_MARM);
113
114 /* Wait for PLL8 to enable */
115 while (!(readl_relaxed(PLL8_STATUS) & BIT(16)))
116 cpu_relax();
117
118 /* Set MAHB1 divider to Div-5 to run MAHB1,2 and sfab at 79.8 Mhz*/
119 writel_relaxed(0x4, MAHB1_NS);
120
121 /* Vote for modem AHB1 and 2 clocks to be on on behalf of the modem */
122 reg = readl_relaxed(MARM_CLK_BRANCH_ENA_VOTE);
123 reg |= BIT(0) | BIT(1);
124 writel_relaxed(reg, MARM_CLK_BRANCH_ENA_VOTE);
125
126 /* Source marm_clk off of PLL8 */
127 reg = readl_relaxed(MARM_CLK_SRC_CTL);
128 if ((reg & 0x1) == 0) {
129 writel_relaxed(0x3, MARM_CLK_SRC1_NS);
130 reg |= 0x1;
131 } else {
132 writel_relaxed(0x3, MARM_CLK_SRC0_NS);
133 reg &= ~0x1;
134 }
135 writel_relaxed(reg | 0x2, MARM_CLK_SRC_CTL);
136
137 /*
138 * Force core on and periph on signals to remain active during halt
139 * for marm_clk and mahb2_clk
140 */
141 writel_relaxed(0x6F, MARM_CLK_FS);
142 writel_relaxed(0x6F, MAHB2_CLK_FS);
143
144 /*
145 * Enable all of the marm_clk branches, cxo sourced marm branches,
146 * and sleep clock branches
147 */
148 writel_relaxed(0x10, MARM_CLK_CTL);
149 writel_relaxed(0x10, MAHB0_CLK_CTL);
150 writel_relaxed(0x10, SFAB_MSS_S_HCLK_CTL);
151 writel_relaxed(0x10, MSS_MODEM_CXO_CLK_CTL);
152 writel_relaxed(0x10, MSS_SLP_CLK_CTL);
153 writel_relaxed(0x10, MSS_MARM_SYS_REF_CLK_CTL);
154
155 /* Wait for above clocks to be turned on */
156 while (readl_relaxed(CLK_HALT_MSS_SMPSS_MISC_STATE) & (BIT(7) | BIT(8) |
157 BIT(9) | BIT(10) | BIT(4) | BIT(6)))
158 cpu_relax();
159
160 /* Take MAHB0,1,2 clocks out of reset */
161 writel_relaxed(0x0, MAHB2_CLK_CTL);
162 writel_relaxed(0x0, MAHB1_CLK_CTL);
163 writel_relaxed(0x0, MAHB0_SFAB_PORT_RESET);
164 mb();
165
166 /* Setup exception vector table base address */
167 writel_relaxed(drv->start_addr | 0x1, drv->base + MARM_BOOT_CONTROL);
168
169 /* Wait for vector table to be setup */
170 mb();
171
172 /* Bring modem out of reset */
173 writel_relaxed(0x0, MARM_RESET);
174
175 return 0;
176}
177
178static int modem_shutdown(struct pil_desc *pil)
179{
180 u32 reg;
181 struct modem_data *drv = dev_get_drvdata(pil->dev);
182
183 /* Put modem into reset */
184 writel_relaxed(0x1, MARM_RESET);
185 mb();
186
187 /* Put modem AHB0,1,2 clocks into reset */
188 writel_relaxed(BIT(0) | BIT(1), MAHB0_SFAB_PORT_RESET);
189 writel_relaxed(BIT(7), MAHB1_CLK_CTL);
190 writel_relaxed(BIT(7), MAHB2_CLK_CTL);
191 mb();
192
193 /*
194 * Disable all of the marm_clk branches, cxo sourced marm branches,
195 * and sleep clock branches
196 */
197 writel_relaxed(0x0, MARM_CLK_CTL);
198 writel_relaxed(0x0, MAHB0_CLK_CTL);
199 writel_relaxed(0x0, SFAB_MSS_S_HCLK_CTL);
200 writel_relaxed(0x0, MSS_MODEM_CXO_CLK_CTL);
201 writel_relaxed(0x0, MSS_SLP_CLK_CTL);
202 writel_relaxed(0x0, MSS_MARM_SYS_REF_CLK_CTL);
203
204 /* Disable marm_clk */
205 reg = readl_relaxed(MARM_CLK_SRC_CTL);
206 reg &= ~0x2;
207 writel_relaxed(reg, MARM_CLK_SRC_CTL);
208
209 /* Clear modem's votes for ahb clocks */
210 writel_relaxed(0x0, MARM_CLK_BRANCH_ENA_VOTE);
211
212 /* Clear modem's votes for PLLs */
213 writel_relaxed(0x0, PLL_ENA_MARM);
214
215 remove_modem_proxy_votes_now(drv);
216
217 return 0;
218}
219
220static struct pil_reset_ops pil_modem_ops = {
221 .init_image = modem_init_image,
222 .verify_blob = nop_verify_blob,
223 .auth_and_reset = modem_reset,
224 .shutdown = modem_shutdown,
225};
226
227static int modem_init_image_trusted(struct pil_desc *pil, const u8 *metadata,
228 size_t size)
229{
230 return pas_init_image(PAS_MODEM, metadata, size);
231}
232
233static int modem_reset_trusted(struct pil_desc *pil)
234{
235 int ret;
236 struct modem_data *drv = dev_get_drvdata(pil->dev);
237
238 make_modem_proxy_votes(pil->dev);
239
240 ret = pas_auth_and_reset(PAS_MODEM);
241 if (ret)
242 remove_modem_proxy_votes_now(drv);
243
244 return ret;
245}
246
247static int modem_shutdown_trusted(struct pil_desc *pil)
248{
249 int ret;
250 struct modem_data *drv = dev_get_drvdata(pil->dev);
251
252 ret = pas_shutdown(PAS_MODEM);
253 if (ret)
254 return ret;
255
256 remove_modem_proxy_votes_now(drv);
257 return 0;
258}
259
260static struct pil_reset_ops pil_modem_ops_trusted = {
261 .init_image = modem_init_image_trusted,
262 .verify_blob = nop_verify_blob,
263 .auth_and_reset = modem_reset_trusted,
264 .shutdown = modem_shutdown_trusted,
265};
266
267static int __devinit pil_modem_driver_probe(struct platform_device *pdev)
268{
269 struct modem_data *drv;
270 struct resource *res;
271 struct pil_desc *desc;
272
273 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
274 if (!res)
275 return -EINVAL;
276
277 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
278 if (!drv)
279 return -ENOMEM;
280 platform_set_drvdata(pdev, drv);
281
282 drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
283 if (!drv->base)
284 return -ENOMEM;
285
286 drv->pxo = msm_xo_get(MSM_XO_PXO, dev_name(&pdev->dev));
287 if (IS_ERR(drv->pxo))
288 return PTR_ERR(drv->pxo);
289
290 desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
291 if (!desc)
292 return -ENOMEM;
293
294 setup_timer(&drv->timer, remove_proxy_votes, (unsigned long)drv);
295 desc->name = "modem";
296 desc->depends_on = "q6";
297 desc->dev = &pdev->dev;
298
299 if (pas_supported(PAS_MODEM) > 0) {
300 desc->ops = &pil_modem_ops_trusted;
301 dev_info(&pdev->dev, "using secure boot\n");
302 } else {
303 desc->ops = &pil_modem_ops;
304 dev_info(&pdev->dev, "using non-secure boot\n");
305 }
306
307 if (msm_pil_register(desc)) {
308 msm_xo_put(drv->pxo);
309 return -EINVAL;
310 }
311 return 0;
312}
313
314static int __devexit pil_modem_driver_exit(struct platform_device *pdev)
315{
316 struct modem_data *drv = platform_get_drvdata(pdev);
317 del_timer_sync(&drv->timer);
318 msm_xo_put(drv->pxo);
319 return 0;
320}
321
322static struct platform_driver pil_modem_driver = {
323 .probe = pil_modem_driver_probe,
324 .remove = __devexit_p(pil_modem_driver_exit),
325 .driver = {
326 .name = "pil_modem",
327 .owner = THIS_MODULE,
328 },
329};
330
331static int __init pil_modem_init(void)
332{
333 return platform_driver_register(&pil_modem_driver);
334}
335module_init(pil_modem_init);
336
337static void __exit pil_modem_exit(void)
338{
339 platform_driver_unregister(&pil_modem_driver);
340}
341module_exit(pil_modem_exit);
342
343MODULE_DESCRIPTION("Support for booting modem processors");
344MODULE_LICENSE("GPL v2");