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