blob: 83444965285da541e03870d36618d6625b77c74f [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/clk.h>
Stephen Boyd4eb885b2011-09-29 01:16:03 -070022
23#include <mach/msm_iomap.h>
Stephen Boyd4eb885b2011-09-29 01:16:03 -070024
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
Stephen Boyd4eb885b2011-09-29 01:16:03 -070050struct modem_data {
51 void __iomem *base;
52 unsigned long start_addr;
Stephen Boyd6d67d252011-09-27 11:50:05 -070053 struct pil_device *pil;
Stephen Boyd67036532012-01-26 15:43:51 -080054 struct clk *xo;
Stephen Boyd4eb885b2011-09-29 01:16:03 -070055};
56
Stephen Boyd06aba622012-03-22 10:59:22 -070057static int make_modem_proxy_votes(struct pil_desc *pil)
Stephen Boyd4eb885b2011-09-29 01:16:03 -070058{
59 int ret;
Stephen Boyd06aba622012-03-22 10:59:22 -070060 struct modem_data *drv = dev_get_drvdata(pil->dev);
Stephen Boyd4eb885b2011-09-29 01:16:03 -070061
Stephen Boyd67036532012-01-26 15:43:51 -080062 ret = clk_prepare_enable(drv->xo);
63 if (ret) {
Stephen Boyd06aba622012-03-22 10:59:22 -070064 dev_err(pil->dev, "Failed to enable XO\n");
Stephen Boyd67036532012-01-26 15:43:51 -080065 return ret;
66 }
Stephen Boyd67036532012-01-26 15:43:51 -080067 return 0;
68}
69
Stephen Boyd06aba622012-03-22 10:59:22 -070070static void remove_modem_proxy_votes(struct pil_desc *pil)
Stephen Boyd67036532012-01-26 15:43:51 -080071{
Stephen Boyd06aba622012-03-22 10:59:22 -070072 struct modem_data *drv = dev_get_drvdata(pil->dev);
Stephen Boyd67036532012-01-26 15:43:51 -080073 clk_disable_unprepare(drv->xo);
Stephen Boyd4eb885b2011-09-29 01:16:03 -070074}
75
Stephen Boyd4eb885b2011-09-29 01:16:03 -070076static int modem_init_image(struct pil_desc *pil, const u8 *metadata,
77 size_t size)
78{
79 const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
80 struct modem_data *drv = dev_get_drvdata(pil->dev);
81 drv->start_addr = ehdr->e_entry;
82 return 0;
83}
84
85static int modem_reset(struct pil_desc *pil)
86{
87 u32 reg;
88 const struct modem_data *drv = dev_get_drvdata(pil->dev);
89
Stephen Boyd4eb885b2011-09-29 01:16:03 -070090 /* Put modem AHB0,1,2 clocks into reset */
91 writel_relaxed(BIT(0) | BIT(1), MAHB0_SFAB_PORT_RESET);
92 writel_relaxed(BIT(7), MAHB1_CLK_CTL);
93 writel_relaxed(BIT(7), MAHB2_CLK_CTL);
94
95 /* Vote for pll8 on behalf of the modem */
96 reg = readl_relaxed(PLL_ENA_MARM);
97 reg |= BIT(8);
98 writel_relaxed(reg, PLL_ENA_MARM);
99
100 /* Wait for PLL8 to enable */
101 while (!(readl_relaxed(PLL8_STATUS) & BIT(16)))
102 cpu_relax();
103
104 /* Set MAHB1 divider to Div-5 to run MAHB1,2 and sfab at 79.8 Mhz*/
105 writel_relaxed(0x4, MAHB1_NS);
106
107 /* Vote for modem AHB1 and 2 clocks to be on on behalf of the modem */
108 reg = readl_relaxed(MARM_CLK_BRANCH_ENA_VOTE);
109 reg |= BIT(0) | BIT(1);
110 writel_relaxed(reg, MARM_CLK_BRANCH_ENA_VOTE);
111
112 /* Source marm_clk off of PLL8 */
113 reg = readl_relaxed(MARM_CLK_SRC_CTL);
114 if ((reg & 0x1) == 0) {
115 writel_relaxed(0x3, MARM_CLK_SRC1_NS);
116 reg |= 0x1;
117 } else {
118 writel_relaxed(0x3, MARM_CLK_SRC0_NS);
119 reg &= ~0x1;
120 }
121 writel_relaxed(reg | 0x2, MARM_CLK_SRC_CTL);
122
123 /*
124 * Force core on and periph on signals to remain active during halt
125 * for marm_clk and mahb2_clk
126 */
127 writel_relaxed(0x6F, MARM_CLK_FS);
128 writel_relaxed(0x6F, MAHB2_CLK_FS);
129
130 /*
131 * Enable all of the marm_clk branches, cxo sourced marm branches,
132 * and sleep clock branches
133 */
134 writel_relaxed(0x10, MARM_CLK_CTL);
135 writel_relaxed(0x10, MAHB0_CLK_CTL);
136 writel_relaxed(0x10, SFAB_MSS_S_HCLK_CTL);
137 writel_relaxed(0x10, MSS_MODEM_CXO_CLK_CTL);
138 writel_relaxed(0x10, MSS_SLP_CLK_CTL);
139 writel_relaxed(0x10, MSS_MARM_SYS_REF_CLK_CTL);
140
141 /* Wait for above clocks to be turned on */
142 while (readl_relaxed(CLK_HALT_MSS_SMPSS_MISC_STATE) & (BIT(7) | BIT(8) |
143 BIT(9) | BIT(10) | BIT(4) | BIT(6)))
144 cpu_relax();
145
146 /* Take MAHB0,1,2 clocks out of reset */
147 writel_relaxed(0x0, MAHB2_CLK_CTL);
148 writel_relaxed(0x0, MAHB1_CLK_CTL);
149 writel_relaxed(0x0, MAHB0_SFAB_PORT_RESET);
150 mb();
151
152 /* Setup exception vector table base address */
153 writel_relaxed(drv->start_addr | 0x1, drv->base + MARM_BOOT_CONTROL);
154
155 /* Wait for vector table to be setup */
156 mb();
157
158 /* Bring modem out of reset */
159 writel_relaxed(0x0, MARM_RESET);
160
161 return 0;
162}
163
164static int modem_shutdown(struct pil_desc *pil)
165{
166 u32 reg;
Stephen Boyd4eb885b2011-09-29 01:16:03 -0700167
168 /* Put modem into reset */
169 writel_relaxed(0x1, MARM_RESET);
170 mb();
171
172 /* Put modem AHB0,1,2 clocks into reset */
173 writel_relaxed(BIT(0) | BIT(1), MAHB0_SFAB_PORT_RESET);
174 writel_relaxed(BIT(7), MAHB1_CLK_CTL);
175 writel_relaxed(BIT(7), MAHB2_CLK_CTL);
176 mb();
177
178 /*
179 * Disable all of the marm_clk branches, cxo sourced marm branches,
180 * and sleep clock branches
181 */
182 writel_relaxed(0x0, MARM_CLK_CTL);
183 writel_relaxed(0x0, MAHB0_CLK_CTL);
184 writel_relaxed(0x0, SFAB_MSS_S_HCLK_CTL);
185 writel_relaxed(0x0, MSS_MODEM_CXO_CLK_CTL);
186 writel_relaxed(0x0, MSS_SLP_CLK_CTL);
187 writel_relaxed(0x0, MSS_MARM_SYS_REF_CLK_CTL);
188
189 /* Disable marm_clk */
190 reg = readl_relaxed(MARM_CLK_SRC_CTL);
191 reg &= ~0x2;
192 writel_relaxed(reg, MARM_CLK_SRC_CTL);
193
194 /* Clear modem's votes for ahb clocks */
195 writel_relaxed(0x0, MARM_CLK_BRANCH_ENA_VOTE);
196
197 /* Clear modem's votes for PLLs */
198 writel_relaxed(0x0, PLL_ENA_MARM);
199
Stephen Boyd4eb885b2011-09-29 01:16:03 -0700200 return 0;
201}
202
203static struct pil_reset_ops pil_modem_ops = {
204 .init_image = modem_init_image,
Stephen Boyd4eb885b2011-09-29 01:16:03 -0700205 .auth_and_reset = modem_reset,
206 .shutdown = modem_shutdown,
Stephen Boyd06aba622012-03-22 10:59:22 -0700207 .proxy_vote = make_modem_proxy_votes,
208 .proxy_unvote = remove_modem_proxy_votes,
Stephen Boyd4eb885b2011-09-29 01:16:03 -0700209};
210
211static int modem_init_image_trusted(struct pil_desc *pil, const u8 *metadata,
212 size_t size)
213{
214 return pas_init_image(PAS_MODEM, metadata, size);
215}
216
217static int modem_reset_trusted(struct pil_desc *pil)
218{
Stephen Boyd06aba622012-03-22 10:59:22 -0700219 return pas_auth_and_reset(PAS_MODEM);
Stephen Boyd4eb885b2011-09-29 01:16:03 -0700220}
221
222static int modem_shutdown_trusted(struct pil_desc *pil)
223{
Stephen Boyd06aba622012-03-22 10:59:22 -0700224 return pas_shutdown(PAS_MODEM);
Stephen Boyd4eb885b2011-09-29 01:16:03 -0700225}
226
227static struct pil_reset_ops pil_modem_ops_trusted = {
228 .init_image = modem_init_image_trusted,
Stephen Boyd4eb885b2011-09-29 01:16:03 -0700229 .auth_and_reset = modem_reset_trusted,
230 .shutdown = modem_shutdown_trusted,
Stephen Boyd06aba622012-03-22 10:59:22 -0700231 .proxy_vote = make_modem_proxy_votes,
232 .proxy_unvote = remove_modem_proxy_votes,
Stephen Boyd4eb885b2011-09-29 01:16:03 -0700233};
234
235static int __devinit pil_modem_driver_probe(struct platform_device *pdev)
236{
237 struct modem_data *drv;
238 struct resource *res;
239 struct pil_desc *desc;
240
241 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
242 if (!res)
243 return -EINVAL;
244
245 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
246 if (!drv)
247 return -ENOMEM;
248 platform_set_drvdata(pdev, drv);
249
250 drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
251 if (!drv->base)
252 return -ENOMEM;
253
Stephen Boydb0895692012-03-23 15:23:10 -0700254 drv->xo = devm_clk_get(&pdev->dev, "xo");
Stephen Boyd67036532012-01-26 15:43:51 -0800255 if (IS_ERR(drv->xo))
256 return PTR_ERR(drv->xo);
Stephen Boyd4eb885b2011-09-29 01:16:03 -0700257
258 desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
259 if (!desc)
260 return -ENOMEM;
261
Stephen Boyd4eb885b2011-09-29 01:16:03 -0700262 desc->name = "modem";
263 desc->depends_on = "q6";
264 desc->dev = &pdev->dev;
Stephen Boyd6d67d252011-09-27 11:50:05 -0700265 desc->owner = THIS_MODULE;
Stephen Boyd06aba622012-03-22 10:59:22 -0700266 desc->proxy_timeout = 10000;
Stephen Boyd4eb885b2011-09-29 01:16:03 -0700267
268 if (pas_supported(PAS_MODEM) > 0) {
269 desc->ops = &pil_modem_ops_trusted;
270 dev_info(&pdev->dev, "using secure boot\n");
271 } else {
272 desc->ops = &pil_modem_ops;
273 dev_info(&pdev->dev, "using non-secure boot\n");
274 }
Stephen Boyd6d67d252011-09-27 11:50:05 -0700275 drv->pil = msm_pil_register(desc);
276 if (IS_ERR(drv->pil)) {
Stephen Boyd6d67d252011-09-27 11:50:05 -0700277 return PTR_ERR(drv->pil);
Stephen Boyd4eb885b2011-09-29 01:16:03 -0700278 }
Stephen Boyd6d67d252011-09-27 11:50:05 -0700279 return 0;
Stephen Boyd4eb885b2011-09-29 01:16:03 -0700280}
281
282static int __devexit pil_modem_driver_exit(struct platform_device *pdev)
283{
284 struct modem_data *drv = platform_get_drvdata(pdev);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700285 msm_pil_unregister(drv->pil);
Stephen Boyd4eb885b2011-09-29 01:16:03 -0700286 return 0;
287}
288
289static struct platform_driver pil_modem_driver = {
290 .probe = pil_modem_driver_probe,
291 .remove = __devexit_p(pil_modem_driver_exit),
292 .driver = {
293 .name = "pil_modem",
294 .owner = THIS_MODULE,
295 },
296};
297
298static int __init pil_modem_init(void)
299{
300 return platform_driver_register(&pil_modem_driver);
301}
302module_init(pil_modem_init);
303
304static void __exit pil_modem_exit(void)
305{
306 platform_driver_unregister(&pil_modem_driver);
307}
308module_exit(pil_modem_exit);
309
310MODULE_DESCRIPTION("Support for booting modem processors");
311MODULE_LICENSE("GPL v2");