blob: b16b6872ae26cc5e93c8ad70c8b0113fdc018f57 [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 }
Stephen Boyde4389ad2011-12-08 10:20:27 -0800223 dev_info(pil->desc->dev, "brought out of reset\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700224
225release_fw:
226 release_firmware(fw);
227out:
228 return ret;
229}
230
231/**
232 * pil_get() - Load a peripheral into memory and take it out of reset
233 * @name: pointer to a string containing the name of the peripheral to load
234 *
235 * This function returns a pointer if it succeeds. If an error occurs an
236 * ERR_PTR is returned.
237 *
238 * If PIL is not enabled in the kernel, the value %NULL will be returned.
239 */
240void *pil_get(const char *name)
241{
242 int ret;
243 struct pil_device *pil;
244 struct pil_device *pil_d;
245 void *retval;
246
Matt Wagantallf3ba25c2011-10-14 19:49:33 -0700247 /* PIL is not yet supported on 8064. */
248 if (cpu_is_apq8064())
249 return NULL;
250
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700251 pil = retval = find_peripheral(name);
252 if (!pil)
253 return ERR_PTR(-ENODEV);
254
Stephen Boyd3f4da322011-08-30 01:03:23 -0700255 pil_d = find_peripheral(pil->desc->depends_on);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700256 if (pil_d) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700257 void *p = pil_get(pil_d->desc->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700258 if (IS_ERR(p))
259 return p;
260 }
261
262 mutex_lock(&pil->lock);
263 if (pil->count) {
264 pil->count++;
265 goto unlock;
266 }
267
268 ret = load_image(pil);
269 if (ret) {
270 retval = ERR_PTR(ret);
271 goto unlock;
272 }
273
274 pil->count++;
275unlock:
276 mutex_unlock(&pil->lock);
277 return retval;
278}
279EXPORT_SYMBOL(pil_get);
280
281/**
282 * pil_put() - Inform PIL the peripheral no longer needs to be active
283 * @peripheral_handle: pointer from a previous call to pil_get()
284 *
285 * This doesn't imply that a peripheral is shutdown or in reset since another
286 * driver could be using the peripheral.
287 */
288void pil_put(void *peripheral_handle)
289{
290 struct pil_device *pil_d;
291 struct pil_device *pil = peripheral_handle;
Stephen Boyd545e8362011-09-13 15:04:38 -0700292 if (!pil || IS_ERR(pil))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700294
295 mutex_lock(&pil->lock);
296 WARN(!pil->count, "%s: Reference count mismatch\n", __func__);
297 /* TODO: Peripheral shutdown support */
298 if (pil->count == 1)
299 goto unlock;
300 if (pil->count)
301 pil->count--;
302 if (pil->count == 0)
Stephen Boyd3f4da322011-08-30 01:03:23 -0700303 pil->desc->ops->shutdown(pil->desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700304unlock:
305 mutex_unlock(&pil->lock);
306
Stephen Boyd3f4da322011-08-30 01:03:23 -0700307 pil_d = find_peripheral(pil->desc->depends_on);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700308 if (pil_d)
309 pil_put(pil_d);
310}
311EXPORT_SYMBOL(pil_put);
312
313void pil_force_shutdown(const char *name)
314{
315 struct pil_device *pil;
316
317 pil = find_peripheral(name);
318 if (!pil)
319 return;
320
321 mutex_lock(&pil->lock);
322 if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
Stephen Boyd3f4da322011-08-30 01:03:23 -0700323 pil->desc->ops->shutdown(pil->desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700324 mutex_unlock(&pil->lock);
325}
326EXPORT_SYMBOL(pil_force_shutdown);
327
328int pil_force_boot(const char *name)
329{
330 int ret = -EINVAL;
331 struct pil_device *pil;
332
333 pil = find_peripheral(name);
334 if (!pil)
335 return -EINVAL;
336
337 mutex_lock(&pil->lock);
338 if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
339 ret = load_image(pil);
340 mutex_unlock(&pil->lock);
341
342 return ret;
343}
344EXPORT_SYMBOL(pil_force_boot);
345
346#ifdef CONFIG_DEBUG_FS
347int msm_pil_debugfs_open(struct inode *inode, struct file *filp)
348{
349 filp->private_data = inode->i_private;
350 return 0;
351}
352
353static ssize_t msm_pil_debugfs_read(struct file *filp, char __user *ubuf,
354 size_t cnt, loff_t *ppos)
355{
356 int r;
357 char buf[40];
358 struct pil_device *pil = filp->private_data;
359
360 mutex_lock(&pil->lock);
361 r = snprintf(buf, sizeof(buf), "%d\n", pil->count);
362 mutex_unlock(&pil->lock);
363 return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
364}
365
366static ssize_t msm_pil_debugfs_write(struct file *filp,
367 const char __user *ubuf, size_t cnt, loff_t *ppos)
368{
369 struct pil_device *pil = filp->private_data;
370 char buf[4];
371
372 if (cnt > sizeof(buf))
373 return -EINVAL;
374
375 if (copy_from_user(&buf, ubuf, cnt))
376 return -EFAULT;
377
378 if (!strncmp(buf, "get", 3)) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700379 if (IS_ERR(pil_get(pil->desc->name)))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700380 return -EIO;
381 } else if (!strncmp(buf, "put", 3))
382 pil_put(pil);
383 else
384 return -EINVAL;
385
386 return cnt;
387}
388
389static const struct file_operations msm_pil_debugfs_fops = {
390 .open = msm_pil_debugfs_open,
391 .read = msm_pil_debugfs_read,
392 .write = msm_pil_debugfs_write,
393};
394
395static struct dentry *pil_base_dir;
396
397static int msm_pil_debugfs_init(void)
398{
399 pil_base_dir = debugfs_create_dir("pil", NULL);
400 if (!pil_base_dir) {
401 pil_base_dir = NULL;
402 return -ENOMEM;
403 }
404
405 return 0;
406}
407arch_initcall(msm_pil_debugfs_init);
408
409static int msm_pil_debugfs_add(struct pil_device *pil)
410{
411 if (!pil_base_dir)
412 return -ENOMEM;
413
Stephen Boyd3f4da322011-08-30 01:03:23 -0700414 if (!debugfs_create_file(pil->desc->name, S_IRUGO | S_IWUSR,
415 pil_base_dir, pil, &msm_pil_debugfs_fops))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700416 return -ENOMEM;
417 return 0;
418}
419#else
420static int msm_pil_debugfs_add(struct pil_device *pil) { return 0; }
421#endif
422
423static int msm_pil_shutdown_at_boot(void)
424{
425 struct pil_device *pil;
426
427 mutex_lock(&pil_list_lock);
428 list_for_each_entry(pil, &pil_list, list)
Stephen Boyd3f4da322011-08-30 01:03:23 -0700429 pil->desc->ops->shutdown(pil->desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700430 mutex_unlock(&pil_list_lock);
431
432 return 0;
433}
434late_initcall(msm_pil_shutdown_at_boot);
435
Stephen Boyd3f4da322011-08-30 01:03:23 -0700436int msm_pil_register(struct pil_desc *desc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700437{
Stephen Boyd3f4da322011-08-30 01:03:23 -0700438 struct pil_device *pil = kzalloc(sizeof(*pil), GFP_KERNEL);
439 if (!pil)
440 return -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700441
442 mutex_init(&pil->lock);
Stephen Boyd3f4da322011-08-30 01:03:23 -0700443 INIT_LIST_HEAD(&pil->list);
444 pil->desc = desc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700445
446 mutex_lock(&pil_list_lock);
447 list_add(&pil->list, &pil_list);
448 mutex_unlock(&pil_list_lock);
449
Stephen Boyd3f4da322011-08-30 01:03:23 -0700450 return msm_pil_debugfs_add(pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700451}
Stephen Boyd3f4da322011-08-30 01:03:23 -0700452EXPORT_SYMBOL(msm_pil_register);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700453
454MODULE_LICENSE("GPL v2");
455MODULE_DESCRIPTION("Load peripheral images and bring peripherals out of reset");