blob: 672f3327c51d5ac9a64fb4d382f0e2fa7fc5287b [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>
Stephen Boyd3f4da322011-08-30 01:03:23 -070015#include <linux/device.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070016#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>
Stephen Boyd3f4da322011-08-30 01:03:23 -070022#include <linux/slab.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070023
Matt Wagantallf3ba25c2011-10-14 19:49:33 -070024#include <mach/socinfo.h>
25
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070026#include <asm/uaccess.h>
27#include <asm/setup.h>
28
29#include "peripheral-loader.h"
30
Stephen Boyd3f4da322011-08-30 01:03:23 -070031struct pil_device {
32 struct pil_desc *desc;
33 int count;
34 struct mutex lock;
35 struct list_head list;
36};
37
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038static DEFINE_MUTEX(pil_list_lock);
39static LIST_HEAD(pil_list);
40
41static struct pil_device *__find_peripheral(const char *str)
42{
43 struct pil_device *dev;
44
45 list_for_each_entry(dev, &pil_list, list)
Stephen Boyd3f4da322011-08-30 01:03:23 -070046 if (!strcmp(dev->desc->name, str))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070047 return dev;
48 return NULL;
49}
50
51static struct pil_device *find_peripheral(const char *str)
52{
53 struct pil_device *dev;
54
55 if (!str)
56 return NULL;
57
58 mutex_lock(&pil_list_lock);
59 dev = __find_peripheral(str);
60 mutex_unlock(&pil_list_lock);
61
62 return dev;
63}
64
65#define IOMAP_SIZE SZ_4M
66
67static int load_segment(const struct elf32_phdr *phdr, unsigned num,
68 struct pil_device *pil)
69{
70 int ret, count, paddr;
71 char fw_name[30];
72 const struct firmware *fw = NULL;
73 const u8 *data;
74
75 if (memblock_is_region_memory(phdr->p_paddr, phdr->p_memsz)) {
Stephen Boyd3f4da322011-08-30 01:03:23 -070076 dev_err(pil->desc->dev, "Kernel memory would be overwritten");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077 return -EPERM;
78 }
79
80 if (phdr->p_filesz) {
Stephen Boyd3f4da322011-08-30 01:03:23 -070081 snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d",
82 pil->desc->name, num);
83 ret = request_firmware(&fw, fw_name, pil->desc->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070084 if (ret) {
Stephen Boyd3f4da322011-08-30 01:03:23 -070085 dev_err(pil->desc->dev, "Failed to locate blob %s\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070086 fw_name);
87 return ret;
88 }
89
90 if (fw->size != phdr->p_filesz) {
Stephen Boyd3f4da322011-08-30 01:03:23 -070091 dev_err(pil->desc->dev,
92 "Blob size %u doesn't match %u\n", fw->size,
93 phdr->p_filesz);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070094 ret = -EPERM;
95 goto release_fw;
96 }
97 }
98
99 /* Load the segment into memory */
100 count = phdr->p_filesz;
101 paddr = phdr->p_paddr;
102 data = fw ? fw->data : NULL;
103 while (count > 0) {
104 int size;
105 u8 __iomem *buf;
106
107 size = min_t(size_t, IOMAP_SIZE, count);
108 buf = ioremap(paddr, size);
109 if (!buf) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700110 dev_err(pil->desc->dev, "Failed to map memory\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111 ret = -ENOMEM;
112 goto release_fw;
113 }
114 memcpy(buf, data, size);
115 iounmap(buf);
116
117 count -= size;
118 paddr += size;
119 data += size;
120 }
121
122 /* Zero out trailing memory */
123 count = phdr->p_memsz - phdr->p_filesz;
124 while (count > 0) {
125 int size;
126 u8 __iomem *buf;
127
128 size = min_t(size_t, IOMAP_SIZE, count);
129 buf = ioremap(paddr, size);
130 if (!buf) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700131 dev_err(pil->desc->dev, "Failed to map memory\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700132 ret = -ENOMEM;
133 goto release_fw;
134 }
135 memset(buf, 0, size);
136 iounmap(buf);
137
138 count -= size;
139 paddr += size;
140 }
141
Stephen Boyd3f4da322011-08-30 01:03:23 -0700142 ret = pil->desc->ops->verify_blob(pil->desc, phdr->p_paddr,
143 phdr->p_memsz);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700144 if (ret)
Stephen Boyd3f4da322011-08-30 01:03:23 -0700145 dev_err(pil->desc->dev, "Blob %u failed verification\n", num);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146
147release_fw:
148 release_firmware(fw);
149 return ret;
150}
151
152#define segment_is_hash(flag) (((flag) & (0x7 << 24)) == (0x2 << 24))
153
154static int segment_is_loadable(const struct elf32_phdr *p)
155{
156 return (p->p_type & PT_LOAD) && !segment_is_hash(p->p_flags);
157}
158
159static int load_image(struct pil_device *pil)
160{
161 int i, ret;
162 char fw_name[30];
163 struct elf32_hdr *ehdr;
164 const struct elf32_phdr *phdr;
165 const struct firmware *fw;
166
Stephen Boyd3f4da322011-08-30 01:03:23 -0700167 snprintf(fw_name, sizeof(fw_name), "%s.mdt", pil->desc->name);
168 ret = request_firmware(&fw, fw_name, pil->desc->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700169 if (ret) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700170 dev_err(pil->desc->dev, "Failed to locate %s\n", fw_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171 goto out;
172 }
173
174 if (fw->size < sizeof(*ehdr)) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700175 dev_err(pil->desc->dev, "Not big enough to be an elf header\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700176 ret = -EIO;
177 goto release_fw;
178 }
179
180 ehdr = (struct elf32_hdr *)fw->data;
181 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700182 dev_err(pil->desc->dev, "Not an elf header\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700183 ret = -EIO;
184 goto release_fw;
185 }
186
187 if (ehdr->e_phnum == 0) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700188 dev_err(pil->desc->dev, "No loadable segments\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189 ret = -EIO;
190 goto release_fw;
191 }
Stephen Boyd96a9f902011-07-18 18:43:00 -0700192 if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
193 sizeof(struct elf32_hdr) > fw->size) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700194 dev_err(pil->desc->dev, "Program headers not within mdt\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700195 ret = -EIO;
196 goto release_fw;
197 }
198
Stephen Boyd3f4da322011-08-30 01:03:23 -0700199 ret = pil->desc->ops->init_image(pil->desc, fw->data, fw->size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200 if (ret) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700201 dev_err(pil->desc->dev, "Invalid firmware metadata\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700202 goto release_fw;
203 }
204
Stephen Boydc9753e12011-07-13 17:58:48 -0700205 phdr = (const struct elf32_phdr *)(fw->data + sizeof(struct elf32_hdr));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206 for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
207 if (!segment_is_loadable(phdr))
208 continue;
209
210 ret = load_segment(phdr, i, pil);
211 if (ret) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700212 dev_err(pil->desc->dev, "Failed to load segment %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700213 i);
214 goto release_fw;
215 }
216 }
217
Stephen Boyd3f4da322011-08-30 01:03:23 -0700218 ret = pil->desc->ops->auth_and_reset(pil->desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700219 if (ret) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700220 dev_err(pil->desc->dev, "Failed to bring out of reset\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700221 goto release_fw;
222 }
223
224release_fw:
225 release_firmware(fw);
226out:
227 return ret;
228}
229
230/**
231 * pil_get() - Load a peripheral into memory and take it out of reset
232 * @name: pointer to a string containing the name of the peripheral to load
233 *
234 * This function returns a pointer if it succeeds. If an error occurs an
235 * ERR_PTR is returned.
236 *
237 * If PIL is not enabled in the kernel, the value %NULL will be returned.
238 */
239void *pil_get(const char *name)
240{
241 int ret;
242 struct pil_device *pil;
243 struct pil_device *pil_d;
244 void *retval;
245
Matt Wagantallf3ba25c2011-10-14 19:49:33 -0700246 /* PIL is not yet supported on 8064. */
247 if (cpu_is_apq8064())
248 return NULL;
249
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700250 pil = retval = find_peripheral(name);
251 if (!pil)
252 return ERR_PTR(-ENODEV);
253
Stephen Boyd3f4da322011-08-30 01:03:23 -0700254 pil_d = find_peripheral(pil->desc->depends_on);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700255 if (pil_d) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700256 void *p = pil_get(pil_d->desc->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700257 if (IS_ERR(p))
258 return p;
259 }
260
261 mutex_lock(&pil->lock);
262 if (pil->count) {
263 pil->count++;
264 goto unlock;
265 }
266
267 ret = load_image(pil);
268 if (ret) {
269 retval = ERR_PTR(ret);
270 goto unlock;
271 }
272
273 pil->count++;
274unlock:
275 mutex_unlock(&pil->lock);
276 return retval;
277}
278EXPORT_SYMBOL(pil_get);
279
280/**
281 * pil_put() - Inform PIL the peripheral no longer needs to be active
282 * @peripheral_handle: pointer from a previous call to pil_get()
283 *
284 * This doesn't imply that a peripheral is shutdown or in reset since another
285 * driver could be using the peripheral.
286 */
287void pil_put(void *peripheral_handle)
288{
289 struct pil_device *pil_d;
290 struct pil_device *pil = peripheral_handle;
Stephen Boyd545e8362011-09-13 15:04:38 -0700291 if (!pil || IS_ERR(pil))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700292 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293
294 mutex_lock(&pil->lock);
295 WARN(!pil->count, "%s: Reference count mismatch\n", __func__);
296 /* TODO: Peripheral shutdown support */
297 if (pil->count == 1)
298 goto unlock;
299 if (pil->count)
300 pil->count--;
301 if (pil->count == 0)
Stephen Boyd3f4da322011-08-30 01:03:23 -0700302 pil->desc->ops->shutdown(pil->desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700303unlock:
304 mutex_unlock(&pil->lock);
305
Stephen Boyd3f4da322011-08-30 01:03:23 -0700306 pil_d = find_peripheral(pil->desc->depends_on);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700307 if (pil_d)
308 pil_put(pil_d);
309}
310EXPORT_SYMBOL(pil_put);
311
312void pil_force_shutdown(const char *name)
313{
314 struct pil_device *pil;
315
316 pil = find_peripheral(name);
317 if (!pil)
318 return;
319
320 mutex_lock(&pil->lock);
321 if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
Stephen Boyd3f4da322011-08-30 01:03:23 -0700322 pil->desc->ops->shutdown(pil->desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700323 mutex_unlock(&pil->lock);
324}
325EXPORT_SYMBOL(pil_force_shutdown);
326
327int pil_force_boot(const char *name)
328{
329 int ret = -EINVAL;
330 struct pil_device *pil;
331
332 pil = find_peripheral(name);
333 if (!pil)
334 return -EINVAL;
335
336 mutex_lock(&pil->lock);
337 if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
338 ret = load_image(pil);
339 mutex_unlock(&pil->lock);
340
341 return ret;
342}
343EXPORT_SYMBOL(pil_force_boot);
344
345#ifdef CONFIG_DEBUG_FS
346int msm_pil_debugfs_open(struct inode *inode, struct file *filp)
347{
348 filp->private_data = inode->i_private;
349 return 0;
350}
351
352static ssize_t msm_pil_debugfs_read(struct file *filp, char __user *ubuf,
353 size_t cnt, loff_t *ppos)
354{
355 int r;
356 char buf[40];
357 struct pil_device *pil = filp->private_data;
358
359 mutex_lock(&pil->lock);
360 r = snprintf(buf, sizeof(buf), "%d\n", pil->count);
361 mutex_unlock(&pil->lock);
362 return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
363}
364
365static ssize_t msm_pil_debugfs_write(struct file *filp,
366 const char __user *ubuf, size_t cnt, loff_t *ppos)
367{
368 struct pil_device *pil = filp->private_data;
369 char buf[4];
370
371 if (cnt > sizeof(buf))
372 return -EINVAL;
373
374 if (copy_from_user(&buf, ubuf, cnt))
375 return -EFAULT;
376
377 if (!strncmp(buf, "get", 3)) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700378 if (IS_ERR(pil_get(pil->desc->name)))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700379 return -EIO;
380 } else if (!strncmp(buf, "put", 3))
381 pil_put(pil);
382 else
383 return -EINVAL;
384
385 return cnt;
386}
387
388static const struct file_operations msm_pil_debugfs_fops = {
389 .open = msm_pil_debugfs_open,
390 .read = msm_pil_debugfs_read,
391 .write = msm_pil_debugfs_write,
392};
393
394static struct dentry *pil_base_dir;
395
396static int msm_pil_debugfs_init(void)
397{
398 pil_base_dir = debugfs_create_dir("pil", NULL);
399 if (!pil_base_dir) {
400 pil_base_dir = NULL;
401 return -ENOMEM;
402 }
403
404 return 0;
405}
406arch_initcall(msm_pil_debugfs_init);
407
408static int msm_pil_debugfs_add(struct pil_device *pil)
409{
410 if (!pil_base_dir)
411 return -ENOMEM;
412
Stephen Boyd3f4da322011-08-30 01:03:23 -0700413 if (!debugfs_create_file(pil->desc->name, S_IRUGO | S_IWUSR,
414 pil_base_dir, pil, &msm_pil_debugfs_fops))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700415 return -ENOMEM;
416 return 0;
417}
418#else
419static int msm_pil_debugfs_add(struct pil_device *pil) { return 0; }
420#endif
421
422static int msm_pil_shutdown_at_boot(void)
423{
424 struct pil_device *pil;
425
426 mutex_lock(&pil_list_lock);
427 list_for_each_entry(pil, &pil_list, list)
Stephen Boyd3f4da322011-08-30 01:03:23 -0700428 pil->desc->ops->shutdown(pil->desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700429 mutex_unlock(&pil_list_lock);
430
431 return 0;
432}
433late_initcall(msm_pil_shutdown_at_boot);
434
Stephen Boyd3f4da322011-08-30 01:03:23 -0700435int msm_pil_register(struct pil_desc *desc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700436{
Stephen Boyd3f4da322011-08-30 01:03:23 -0700437 struct pil_device *pil = kzalloc(sizeof(*pil), GFP_KERNEL);
438 if (!pil)
439 return -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700440
441 mutex_init(&pil->lock);
Stephen Boyd3f4da322011-08-30 01:03:23 -0700442 INIT_LIST_HEAD(&pil->list);
443 pil->desc = desc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700444
445 mutex_lock(&pil_list_lock);
446 list_add(&pil->list, &pil_list);
447 mutex_unlock(&pil_list_lock);
448
Stephen Boyd3f4da322011-08-30 01:03:23 -0700449 return msm_pil_debugfs_add(pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700450}
Stephen Boyd3f4da322011-08-30 01:03:23 -0700451EXPORT_SYMBOL(msm_pil_register);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700452
453MODULE_LICENSE("GPL v2");
454MODULE_DESCRIPTION("Load peripheral images and bring peripherals out of reset");