blob: b95d5029b60d7d1134e6f189fc9cec6425abbe78 [file] [log] [blame]
Matt Wagantalle6e00d52012-03-08 17:39:07 -08001/*
Duy Truonge833aca2013-02-12 13:35:08 -08002 * Copyright (c) 2012, The Linux Foundation. All rights reserved.
Matt Wagantalle6e00d52012-03-08 17:39:07 -08003 *
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
Matt Wagantalle6e00d52012-03-08 17:39:07 -080040
Matt Wagantalle6e00d52012-03-08 17:39:07 -080041#define PROXY_TIMEOUT_MS 10000
42#define POLL_INTERVAL_US 50
43
Matt Wagantallf0141392012-08-16 18:53:53 -070044static int modem_auth_timeout_ms = 10000;
45module_param(modem_auth_timeout_ms, int, S_IRUGO | S_IWUSR);
46
Matt Wagantalle6e00d52012-03-08 17:39:07 -080047struct mba_data {
48 void __iomem *reg_base;
49 void __iomem *metadata_base;
50 unsigned long metadata_phys;
51 struct pil_device *pil;
52 struct clk *xo;
53 u32 img_length;
54};
55
56static int pil_mba_make_proxy_votes(struct pil_desc *pil)
57{
58 int ret;
59 struct mba_data *drv = dev_get_drvdata(pil->dev);
60
61 ret = clk_prepare_enable(drv->xo);
62 if (ret) {
63 dev_err(pil->dev, "Failed to enable XO\n");
64 return ret;
65 }
66 return 0;
67}
68
69static void pil_mba_remove_proxy_votes(struct pil_desc *pil)
70{
71 struct mba_data *drv = dev_get_drvdata(pil->dev);
72 clk_disable_unprepare(drv->xo);
73}
74
75static int pil_mba_init_image(struct pil_desc *pil,
76 const u8 *metadata, size_t size)
77{
78 struct mba_data *drv = dev_get_drvdata(pil->dev);
Matt Wagantall19887262012-08-03 16:37:34 -070079 s32 status;
Matt Wagantalle6e00d52012-03-08 17:39:07 -080080 int ret;
81
82 /* Copy metadata to assigned shared buffer location */
83 memcpy(drv->metadata_base, metadata, size);
84
85 /* Initialize length counter to 0 */
86 writel_relaxed(0, drv->reg_base + RMB_PMI_CODE_LENGTH);
87 drv->img_length = 0;
88
89 /* Pass address of meta-data to the MBA and perform authentication */
90 writel_relaxed(drv->metadata_phys, drv->reg_base + RMB_PMI_META_DATA);
91 writel_relaxed(CMD_META_DATA_READY, drv->reg_base + RMB_MBA_COMMAND);
92 ret = readl_poll_timeout(drv->reg_base + RMB_MBA_STATUS, status,
Matt Wagantall19887262012-08-03 16:37:34 -070093 status == STATUS_META_DATA_AUTH_SUCCESS || status < 0,
Matt Wagantallf0141392012-08-16 18:53:53 -070094 POLL_INTERVAL_US, modem_auth_timeout_ms * 1000);
Matt Wagantall19887262012-08-03 16:37:34 -070095 if (ret) {
Matt Wagantalle6e00d52012-03-08 17:39:07 -080096 dev_err(pil->dev, "MBA authentication timed out\n");
Matt Wagantall19887262012-08-03 16:37:34 -070097 } else if (status < 0) {
98 dev_err(pil->dev, "MBA returned error %d\n", status);
99 ret = -EINVAL;
100 }
Matt Wagantalle6e00d52012-03-08 17:39:07 -0800101
102 return ret;
103}
104
105static int pil_mba_verify_blob(struct pil_desc *pil, u32 phy_addr,
106 size_t size)
107{
108 struct mba_data *drv = dev_get_drvdata(pil->dev);
Matt Wagantall19887262012-08-03 16:37:34 -0700109 s32 status;
Matt Wagantalle6e00d52012-03-08 17:39:07 -0800110
111 /* Begin image authentication */
112 if (drv->img_length == 0) {
113 writel_relaxed(phy_addr, drv->reg_base + RMB_PMI_CODE_START);
114 writel_relaxed(CMD_LOAD_READY, drv->reg_base + RMB_MBA_COMMAND);
115 }
116 /* Increment length counter */
117 drv->img_length += size;
118 writel_relaxed(drv->img_length, drv->reg_base + RMB_PMI_CODE_LENGTH);
119
Matt Wagantall19887262012-08-03 16:37:34 -0700120 status = readl_relaxed(drv->reg_base + RMB_MBA_STATUS);
121 if (status < 0) {
122 dev_err(pil->dev, "MBA returned error %d\n", status);
123 return -EINVAL;
124 }
125
126 return 0;
Matt Wagantalle6e00d52012-03-08 17:39:07 -0800127}
128
129static int pil_mba_auth(struct pil_desc *pil)
130{
131 struct mba_data *drv = dev_get_drvdata(pil->dev);
132 int ret;
Matt Wagantall19887262012-08-03 16:37:34 -0700133 s32 status;
Matt Wagantalle6e00d52012-03-08 17:39:07 -0800134
135 /* Wait for all segments to be authenticated or an error to occur */
136 ret = readl_poll_timeout(drv->reg_base + RMB_MBA_STATUS, status,
Matt Wagantall19887262012-08-03 16:37:34 -0700137 status == STATUS_AUTH_COMPLETE || status < 0,
Matt Wagantallf0141392012-08-16 18:53:53 -0700138 50, modem_auth_timeout_ms * 1000);
Matt Wagantall19887262012-08-03 16:37:34 -0700139 if (ret) {
140 dev_err(pil->dev, "MBA authentication timed out\n");
141 } else if (status < 0) {
142 dev_err(pil->dev, "MBA returned error %d\n", status);
143 ret = -EINVAL;
144 }
Matt Wagantalle6e00d52012-03-08 17:39:07 -0800145
Matt Wagantall19887262012-08-03 16:37:34 -0700146 return ret;
Matt Wagantalle6e00d52012-03-08 17:39:07 -0800147}
148
149static int pil_mba_shutdown(struct pil_desc *pil)
150{
151 return 0;
152}
153
154static struct pil_reset_ops pil_mba_ops = {
155 .init_image = pil_mba_init_image,
156 .proxy_vote = pil_mba_make_proxy_votes,
157 .proxy_unvote = pil_mba_remove_proxy_votes,
158 .verify_blob = pil_mba_verify_blob,
159 .auth_and_reset = pil_mba_auth,
160 .shutdown = pil_mba_shutdown,
161};
162
163static int __devinit pil_mba_driver_probe(struct platform_device *pdev)
164{
165 struct mba_data *drv;
166 struct resource *res;
167 struct pil_desc *desc;
168 int ret;
169
170 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
171 if (!drv)
172 return -ENOMEM;
173 platform_set_drvdata(pdev, drv);
174
175 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
176 if (!res)
177 return -EINVAL;
178 drv->reg_base = devm_ioremap(&pdev->dev, res->start,
179 resource_size(res));
180 if (!drv->reg_base)
181 return -ENOMEM;
182
183 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
184 if (res) {
185 drv->metadata_base = devm_ioremap(&pdev->dev, res->start,
186 resource_size(res));
187 if (!drv->metadata_base)
188 return -ENOMEM;
189 drv->metadata_phys = res->start;
190 }
191
192 desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
193 if (!drv)
194 return -ENOMEM;
195
196 ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
197 &desc->name);
198 if (ret)
199 return ret;
200
201 of_property_read_string(pdev->dev.of_node, "qcom,depends-on",
202 &desc->depends_on);
203
204 drv->xo = devm_clk_get(&pdev->dev, "xo");
205 if (IS_ERR(drv->xo))
206 return PTR_ERR(drv->xo);
207
208 desc->dev = &pdev->dev;
209 desc->ops = &pil_mba_ops;
210 desc->owner = THIS_MODULE;
211 desc->proxy_timeout = PROXY_TIMEOUT_MS;
212
213 drv->pil = msm_pil_register(desc);
214 if (IS_ERR(drv->pil))
215 return PTR_ERR(drv->pil);
216
217 return 0;
218}
219
220static int __devexit pil_mba_driver_exit(struct platform_device *pdev)
221{
Stephen Boydfb71ba62012-07-02 15:24:42 -0700222 struct mba_data *drv = platform_get_drvdata(pdev);
223 msm_pil_unregister(drv->pil);
Matt Wagantalle6e00d52012-03-08 17:39:07 -0800224 return 0;
225}
226
227static struct of_device_id mba_match_table[] = {
228 { .compatible = "qcom,pil-mba" },
229 {}
230};
231
232struct platform_driver pil_mba_driver = {
233 .probe = pil_mba_driver_probe,
234 .remove = __devexit_p(pil_mba_driver_exit),
235 .driver = {
236 .name = "pil-mba",
237 .of_match_table = mba_match_table,
238 .owner = THIS_MODULE,
239 },
240};
241
242static int __init pil_mba_init(void)
243{
244 return platform_driver_register(&pil_mba_driver);
245}
246module_init(pil_mba_init);
247
248static void __exit pil_mba_exit(void)
249{
250 platform_driver_unregister(&pil_mba_driver);
251}
252module_exit(pil_mba_exit);
253
254MODULE_DESCRIPTION("Support for modem boot using the Modem Boot Authenticator");
255MODULE_LICENSE("GPL v2");