blob: 7405ab96aba49d27aa6b1bc63b6c683f0bf6cf93 [file] [log] [blame]
Matt Wagantalle6e00d52012-03-08 17:39:07 -08001/*
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/ioport.h>
20#include <linux/elf.h>
21#include <linux/delay.h>
22#include <linux/sched.h>
23#include <linux/clk.h>
24#include <linux/err.h>
25#include <linux/of.h>
26
27#include "peripheral-loader.h"
28
29#define RMB_MBA_COMMAND 0x08
30#define RMB_MBA_STATUS 0x0C
31#define RMB_PMI_META_DATA 0x10
32#define RMB_PMI_CODE_START 0x14
33#define RMB_PMI_CODE_LENGTH 0x18
34
35#define CMD_META_DATA_READY 0x1
36#define CMD_LOAD_READY 0x2
37
38#define STATUS_META_DATA_AUTH_SUCCESS 0x3
39#define STATUS_AUTH_COMPLETE 0x4
40#define STATUS_ERROR_MASK BIT(31)
41
42#define AUTH_TIMEOUT_US 10000000
43#define PROXY_TIMEOUT_MS 10000
44#define POLL_INTERVAL_US 50
45
46struct mba_data {
47 void __iomem *reg_base;
48 void __iomem *metadata_base;
49 unsigned long metadata_phys;
50 struct pil_device *pil;
51 struct clk *xo;
52 u32 img_length;
53};
54
55static int pil_mba_make_proxy_votes(struct pil_desc *pil)
56{
57 int ret;
58 struct mba_data *drv = dev_get_drvdata(pil->dev);
59
60 ret = clk_prepare_enable(drv->xo);
61 if (ret) {
62 dev_err(pil->dev, "Failed to enable XO\n");
63 return ret;
64 }
65 return 0;
66}
67
68static void pil_mba_remove_proxy_votes(struct pil_desc *pil)
69{
70 struct mba_data *drv = dev_get_drvdata(pil->dev);
71 clk_disable_unprepare(drv->xo);
72}
73
74static int pil_mba_init_image(struct pil_desc *pil,
75 const u8 *metadata, size_t size)
76{
77 struct mba_data *drv = dev_get_drvdata(pil->dev);
78 u32 status;
79 int ret;
80
81 /* Copy metadata to assigned shared buffer location */
82 memcpy(drv->metadata_base, metadata, size);
83
84 /* Initialize length counter to 0 */
85 writel_relaxed(0, drv->reg_base + RMB_PMI_CODE_LENGTH);
86 drv->img_length = 0;
87
88 /* Pass address of meta-data to the MBA and perform authentication */
89 writel_relaxed(drv->metadata_phys, drv->reg_base + RMB_PMI_META_DATA);
90 writel_relaxed(CMD_META_DATA_READY, drv->reg_base + RMB_MBA_COMMAND);
91 ret = readl_poll_timeout(drv->reg_base + RMB_MBA_STATUS, status,
92 status == STATUS_META_DATA_AUTH_SUCCESS,
93 POLL_INTERVAL_US, AUTH_TIMEOUT_US);
94 if (ret)
95 dev_err(pil->dev, "MBA authentication timed out\n");
96
97 return ret;
98}
99
100static int pil_mba_verify_blob(struct pil_desc *pil, u32 phy_addr,
101 size_t size)
102{
103 struct mba_data *drv = dev_get_drvdata(pil->dev);
104
105 /* Begin image authentication */
106 if (drv->img_length == 0) {
107 writel_relaxed(phy_addr, drv->reg_base + RMB_PMI_CODE_START);
108 writel_relaxed(CMD_LOAD_READY, drv->reg_base + RMB_MBA_COMMAND);
109 }
110 /* Increment length counter */
111 drv->img_length += size;
112 writel_relaxed(drv->img_length, drv->reg_base + RMB_PMI_CODE_LENGTH);
113
114 return readl_relaxed(drv->reg_base + RMB_MBA_STATUS)
115 & STATUS_ERROR_MASK;
116}
117
118static int pil_mba_auth(struct pil_desc *pil)
119{
120 struct mba_data *drv = dev_get_drvdata(pil->dev);
121 int ret;
122 u32 status;
123
124 /* Wait for all segments to be authenticated or an error to occur */
125 ret = readl_poll_timeout(drv->reg_base + RMB_MBA_STATUS, status,
126 status == STATUS_AUTH_COMPLETE ||
127 status & STATUS_ERROR_MASK,
128 50, AUTH_TIMEOUT_US);
129 if (ret)
130 return ret;
131
132 if (status & STATUS_ERROR_MASK)
133 return -EINVAL;
134
135 return 0;
136}
137
138static int pil_mba_shutdown(struct pil_desc *pil)
139{
140 return 0;
141}
142
143static struct pil_reset_ops pil_mba_ops = {
144 .init_image = pil_mba_init_image,
145 .proxy_vote = pil_mba_make_proxy_votes,
146 .proxy_unvote = pil_mba_remove_proxy_votes,
147 .verify_blob = pil_mba_verify_blob,
148 .auth_and_reset = pil_mba_auth,
149 .shutdown = pil_mba_shutdown,
150};
151
152static int __devinit pil_mba_driver_probe(struct platform_device *pdev)
153{
154 struct mba_data *drv;
155 struct resource *res;
156 struct pil_desc *desc;
157 int ret;
158
159 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
160 if (!drv)
161 return -ENOMEM;
162 platform_set_drvdata(pdev, drv);
163
164 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
165 if (!res)
166 return -EINVAL;
167 drv->reg_base = devm_ioremap(&pdev->dev, res->start,
168 resource_size(res));
169 if (!drv->reg_base)
170 return -ENOMEM;
171
172 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
173 if (res) {
174 drv->metadata_base = devm_ioremap(&pdev->dev, res->start,
175 resource_size(res));
176 if (!drv->metadata_base)
177 return -ENOMEM;
178 drv->metadata_phys = res->start;
179 }
180
181 desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
182 if (!drv)
183 return -ENOMEM;
184
185 ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
186 &desc->name);
187 if (ret)
188 return ret;
189
190 of_property_read_string(pdev->dev.of_node, "qcom,depends-on",
191 &desc->depends_on);
192
193 drv->xo = devm_clk_get(&pdev->dev, "xo");
194 if (IS_ERR(drv->xo))
195 return PTR_ERR(drv->xo);
196
197 desc->dev = &pdev->dev;
198 desc->ops = &pil_mba_ops;
199 desc->owner = THIS_MODULE;
200 desc->proxy_timeout = PROXY_TIMEOUT_MS;
201
202 drv->pil = msm_pil_register(desc);
203 if (IS_ERR(drv->pil))
204 return PTR_ERR(drv->pil);
205
206 return 0;
207}
208
209static int __devexit pil_mba_driver_exit(struct platform_device *pdev)
210{
211 return 0;
212}
213
214static struct of_device_id mba_match_table[] = {
215 { .compatible = "qcom,pil-mba" },
216 {}
217};
218
219struct platform_driver pil_mba_driver = {
220 .probe = pil_mba_driver_probe,
221 .remove = __devexit_p(pil_mba_driver_exit),
222 .driver = {
223 .name = "pil-mba",
224 .of_match_table = mba_match_table,
225 .owner = THIS_MODULE,
226 },
227};
228
229static int __init pil_mba_init(void)
230{
231 return platform_driver_register(&pil_mba_driver);
232}
233module_init(pil_mba_init);
234
235static void __exit pil_mba_exit(void)
236{
237 platform_driver_unregister(&pil_mba_driver);
238}
239module_exit(pil_mba_exit);
240
241MODULE_DESCRIPTION("Support for modem boot using the Modem Boot Authenticator");
242MODULE_LICENSE("GPL v2");