blob: dc3b26f88f22d76094d2350cf7ff36bdce45560d [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -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/module.h>
14#include <linux/string.h>
15#include <linux/platform_device.h>
16#include <linux/firmware.h>
17#include <linux/io.h>
18#include <linux/debugfs.h>
19#include <linux/elf.h>
20#include <linux/mutex.h>
21#include <linux/memblock.h>
22
Matt Wagantallf3ba25c2011-10-14 19:49:33 -070023#include <mach/socinfo.h>
24
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070025#include <asm/uaccess.h>
26#include <asm/setup.h>
27
28#include "peripheral-loader.h"
29
30static DEFINE_MUTEX(pil_list_lock);
31static LIST_HEAD(pil_list);
32
33static struct pil_device *__find_peripheral(const char *str)
34{
35 struct pil_device *dev;
36
37 list_for_each_entry(dev, &pil_list, list)
38 if (!strcmp(dev->name, str))
39 return dev;
40 return NULL;
41}
42
43static struct pil_device *find_peripheral(const char *str)
44{
45 struct pil_device *dev;
46
47 if (!str)
48 return NULL;
49
50 mutex_lock(&pil_list_lock);
51 dev = __find_peripheral(str);
52 mutex_unlock(&pil_list_lock);
53
54 return dev;
55}
56
57#define IOMAP_SIZE SZ_4M
58
59static int load_segment(const struct elf32_phdr *phdr, unsigned num,
60 struct pil_device *pil)
61{
62 int ret, count, paddr;
63 char fw_name[30];
64 const struct firmware *fw = NULL;
65 const u8 *data;
66
67 if (memblock_is_region_memory(phdr->p_paddr, phdr->p_memsz)) {
68 dev_err(&pil->pdev.dev, "Kernel memory would be overwritten");
69 return -EPERM;
70 }
71
72 if (phdr->p_filesz) {
73 snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d", pil->name,
74 num);
75 ret = request_firmware(&fw, fw_name, &pil->pdev.dev);
76 if (ret) {
77 dev_err(&pil->pdev.dev, "Failed to locate blob %s\n",
78 fw_name);
79 return ret;
80 }
81
82 if (fw->size != phdr->p_filesz) {
83 dev_err(&pil->pdev.dev,
84 "Blob size %u doesn't match %u\n",
85 fw->size, phdr->p_filesz);
86 ret = -EPERM;
87 goto release_fw;
88 }
89 }
90
91 /* Load the segment into memory */
92 count = phdr->p_filesz;
93 paddr = phdr->p_paddr;
94 data = fw ? fw->data : NULL;
95 while (count > 0) {
96 int size;
97 u8 __iomem *buf;
98
99 size = min_t(size_t, IOMAP_SIZE, count);
100 buf = ioremap(paddr, size);
101 if (!buf) {
102 dev_err(&pil->pdev.dev, "Failed to map memory\n");
103 ret = -ENOMEM;
104 goto release_fw;
105 }
106 memcpy(buf, data, size);
107 iounmap(buf);
108
109 count -= size;
110 paddr += size;
111 data += size;
112 }
113
114 /* Zero out trailing memory */
115 count = phdr->p_memsz - phdr->p_filesz;
116 while (count > 0) {
117 int size;
118 u8 __iomem *buf;
119
120 size = min_t(size_t, IOMAP_SIZE, count);
121 buf = ioremap(paddr, size);
122 if (!buf) {
123 dev_err(&pil->pdev.dev, "Failed to map memory\n");
124 ret = -ENOMEM;
125 goto release_fw;
126 }
127 memset(buf, 0, size);
128 iounmap(buf);
129
130 count -= size;
131 paddr += size;
132 }
133
Stephen Boyd5bd999a2011-08-02 18:50:57 -0700134 ret = pil->ops->verify_blob(pil, phdr->p_paddr, phdr->p_memsz);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700135 if (ret)
136 dev_err(&pil->pdev.dev, "Blob %u failed verification\n", num);
137
138release_fw:
139 release_firmware(fw);
140 return ret;
141}
142
143#define segment_is_hash(flag) (((flag) & (0x7 << 24)) == (0x2 << 24))
144
145static int segment_is_loadable(const struct elf32_phdr *p)
146{
147 return (p->p_type & PT_LOAD) && !segment_is_hash(p->p_flags);
148}
149
150static int load_image(struct pil_device *pil)
151{
152 int i, ret;
153 char fw_name[30];
154 struct elf32_hdr *ehdr;
155 const struct elf32_phdr *phdr;
156 const struct firmware *fw;
157
158 snprintf(fw_name, sizeof(fw_name), "%s.mdt", pil->name);
159 ret = request_firmware(&fw, fw_name, &pil->pdev.dev);
160 if (ret) {
161 dev_err(&pil->pdev.dev, "Failed to locate %s\n", fw_name);
162 goto out;
163 }
164
165 if (fw->size < sizeof(*ehdr)) {
166 dev_err(&pil->pdev.dev, "Not big enough to be an elf header\n");
167 ret = -EIO;
168 goto release_fw;
169 }
170
171 ehdr = (struct elf32_hdr *)fw->data;
172 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
173 dev_err(&pil->pdev.dev, "Not an elf header\n");
174 ret = -EIO;
175 goto release_fw;
176 }
177
178 if (ehdr->e_phnum == 0) {
179 dev_err(&pil->pdev.dev, "No loadable segments\n");
180 ret = -EIO;
181 goto release_fw;
182 }
Stephen Boyd96a9f902011-07-18 18:43:00 -0700183 if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
184 sizeof(struct elf32_hdr) > fw->size) {
185 dev_err(&pil->pdev.dev, "Program headers not within mdt\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700186 ret = -EIO;
187 goto release_fw;
188 }
189
Stephen Boyd5bd999a2011-08-02 18:50:57 -0700190 ret = pil->ops->init_image(pil, fw->data, fw->size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700191 if (ret) {
192 dev_err(&pil->pdev.dev, "Invalid firmware metadata\n");
193 goto release_fw;
194 }
195
Stephen Boydc9753e12011-07-13 17:58:48 -0700196 phdr = (const struct elf32_phdr *)(fw->data + sizeof(struct elf32_hdr));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197 for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
198 if (!segment_is_loadable(phdr))
199 continue;
200
201 ret = load_segment(phdr, i, pil);
202 if (ret) {
203 dev_err(&pil->pdev.dev, "Failed to load segment %d\n",
204 i);
205 goto release_fw;
206 }
207 }
208
Stephen Boyd5bd999a2011-08-02 18:50:57 -0700209 ret = pil->ops->auth_and_reset(pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700210 if (ret) {
211 dev_err(&pil->pdev.dev, "Failed to bring out of reset\n");
212 goto release_fw;
213 }
214
215release_fw:
216 release_firmware(fw);
217out:
218 return ret;
219}
220
221/**
222 * pil_get() - Load a peripheral into memory and take it out of reset
223 * @name: pointer to a string containing the name of the peripheral to load
224 *
225 * This function returns a pointer if it succeeds. If an error occurs an
226 * ERR_PTR is returned.
227 *
228 * If PIL is not enabled in the kernel, the value %NULL will be returned.
229 */
230void *pil_get(const char *name)
231{
232 int ret;
233 struct pil_device *pil;
234 struct pil_device *pil_d;
235 void *retval;
236
Matt Wagantallf3ba25c2011-10-14 19:49:33 -0700237 /* PIL is not yet supported on 8064. */
238 if (cpu_is_apq8064())
239 return NULL;
240
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700241 pil = retval = find_peripheral(name);
242 if (!pil)
243 return ERR_PTR(-ENODEV);
244
245 pil_d = find_peripheral(pil->depends_on);
246 if (pil_d) {
247 void *p = pil_get(pil_d->name);
248 if (IS_ERR(p))
249 return p;
250 }
251
252 mutex_lock(&pil->lock);
253 if (pil->count) {
254 pil->count++;
255 goto unlock;
256 }
257
258 ret = load_image(pil);
259 if (ret) {
260 retval = ERR_PTR(ret);
261 goto unlock;
262 }
263
264 pil->count++;
265unlock:
266 mutex_unlock(&pil->lock);
267 return retval;
268}
269EXPORT_SYMBOL(pil_get);
270
271/**
272 * pil_put() - Inform PIL the peripheral no longer needs to be active
273 * @peripheral_handle: pointer from a previous call to pil_get()
274 *
275 * This doesn't imply that a peripheral is shutdown or in reset since another
276 * driver could be using the peripheral.
277 */
278void pil_put(void *peripheral_handle)
279{
280 struct pil_device *pil_d;
281 struct pil_device *pil = peripheral_handle;
Stephen Boyd545e8362011-09-13 15:04:38 -0700282 if (!pil || IS_ERR(pil))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700283 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700284
285 mutex_lock(&pil->lock);
286 WARN(!pil->count, "%s: Reference count mismatch\n", __func__);
287 /* TODO: Peripheral shutdown support */
288 if (pil->count == 1)
289 goto unlock;
290 if (pil->count)
291 pil->count--;
292 if (pil->count == 0)
Stephen Boyd5bd999a2011-08-02 18:50:57 -0700293 pil->ops->shutdown(pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700294unlock:
295 mutex_unlock(&pil->lock);
296
297 pil_d = find_peripheral(pil->depends_on);
298 if (pil_d)
299 pil_put(pil_d);
300}
301EXPORT_SYMBOL(pil_put);
302
303void pil_force_shutdown(const char *name)
304{
305 struct pil_device *pil;
306
307 pil = find_peripheral(name);
308 if (!pil)
309 return;
310
311 mutex_lock(&pil->lock);
312 if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
Stephen Boyd5bd999a2011-08-02 18:50:57 -0700313 pil->ops->shutdown(pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700314 mutex_unlock(&pil->lock);
315}
316EXPORT_SYMBOL(pil_force_shutdown);
317
318int pil_force_boot(const char *name)
319{
320 int ret = -EINVAL;
321 struct pil_device *pil;
322
323 pil = find_peripheral(name);
324 if (!pil)
325 return -EINVAL;
326
327 mutex_lock(&pil->lock);
328 if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
329 ret = load_image(pil);
330 mutex_unlock(&pil->lock);
331
332 return ret;
333}
334EXPORT_SYMBOL(pil_force_boot);
335
336#ifdef CONFIG_DEBUG_FS
337int msm_pil_debugfs_open(struct inode *inode, struct file *filp)
338{
339 filp->private_data = inode->i_private;
340 return 0;
341}
342
343static ssize_t msm_pil_debugfs_read(struct file *filp, char __user *ubuf,
344 size_t cnt, loff_t *ppos)
345{
346 int r;
347 char buf[40];
348 struct pil_device *pil = filp->private_data;
349
350 mutex_lock(&pil->lock);
351 r = snprintf(buf, sizeof(buf), "%d\n", pil->count);
352 mutex_unlock(&pil->lock);
353 return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
354}
355
356static ssize_t msm_pil_debugfs_write(struct file *filp,
357 const char __user *ubuf, size_t cnt, loff_t *ppos)
358{
359 struct pil_device *pil = filp->private_data;
360 char buf[4];
361
362 if (cnt > sizeof(buf))
363 return -EINVAL;
364
365 if (copy_from_user(&buf, ubuf, cnt))
366 return -EFAULT;
367
368 if (!strncmp(buf, "get", 3)) {
369 if (IS_ERR(pil_get(pil->name)))
370 return -EIO;
371 } else if (!strncmp(buf, "put", 3))
372 pil_put(pil);
373 else
374 return -EINVAL;
375
376 return cnt;
377}
378
379static const struct file_operations msm_pil_debugfs_fops = {
380 .open = msm_pil_debugfs_open,
381 .read = msm_pil_debugfs_read,
382 .write = msm_pil_debugfs_write,
383};
384
385static struct dentry *pil_base_dir;
386
387static int msm_pil_debugfs_init(void)
388{
389 pil_base_dir = debugfs_create_dir("pil", NULL);
390 if (!pil_base_dir) {
391 pil_base_dir = NULL;
392 return -ENOMEM;
393 }
394
395 return 0;
396}
397arch_initcall(msm_pil_debugfs_init);
398
399static int msm_pil_debugfs_add(struct pil_device *pil)
400{
401 if (!pil_base_dir)
402 return -ENOMEM;
403
404 if (!debugfs_create_file(pil->name, S_IRUGO | S_IWUSR, pil_base_dir,
405 pil, &msm_pil_debugfs_fops))
406 return -ENOMEM;
407 return 0;
408}
409#else
410static int msm_pil_debugfs_add(struct pil_device *pil) { return 0; }
411#endif
412
413static int msm_pil_shutdown_at_boot(void)
414{
415 struct pil_device *pil;
416
417 mutex_lock(&pil_list_lock);
418 list_for_each_entry(pil, &pil_list, list)
Stephen Boyd5bd999a2011-08-02 18:50:57 -0700419 pil->ops->shutdown(pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700420 mutex_unlock(&pil_list_lock);
421
422 return 0;
423}
424late_initcall(msm_pil_shutdown_at_boot);
425
426int msm_pil_add_device(struct pil_device *pil)
427{
428 int ret;
429 ret = platform_device_register(&pil->pdev);
430 if (ret)
431 return ret;
432
433 mutex_init(&pil->lock);
434
435 mutex_lock(&pil_list_lock);
436 list_add(&pil->list, &pil_list);
437 mutex_unlock(&pil_list_lock);
438
439 msm_pil_debugfs_add(pil);
440 return 0;
441}
442
443MODULE_LICENSE("GPL v2");
444MODULE_DESCRIPTION("Load peripheral images and bring peripherals out of reset");