blob: affb451b8bc089f2c0ef82915eaa8c232830ac3d [file] [log] [blame]
Stephen Boyd06ce3962013-01-02 15:03:14 -08001/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070015#include <linux/firmware.h>
16#include <linux/io.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070017#include <linux/elf.h>
18#include <linux/mutex.h>
19#include <linux/memblock.h>
Stephen Boyd3f4da322011-08-30 01:03:23 -070020#include <linux/slab.h>
Stephen Boyd80bde032012-03-16 00:14:42 -070021#include <linux/suspend.h>
22#include <linux/rwsem.h>
Stephen Boyd20ad8102011-10-09 21:28:01 -070023#include <linux/sysfs.h>
Stephen Boyd36974ec2012-03-22 01:30:59 -070024#include <linux/workqueue.h>
25#include <linux/jiffies.h>
26#include <linux/wakelock.h>
Stephen Boydedff6cf2012-07-11 19:39:27 -070027#include <linux/err.h>
Stephen Boyd2db158c2012-07-26 21:47:17 -070028#include <linux/msm_ion.h>
Stephen Boydedff6cf2012-07-11 19:39:27 -070029#include <linux/list.h>
30#include <linux/list_sort.h>
Stephen Boydb455f322012-11-27 19:00:01 -080031#include <linux/idr.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032
33#include <asm/uaccess.h>
34#include <asm/setup.h>
Stephen Boydb455f322012-11-27 19:00:01 -080035#include <asm-generic/io-64-nonatomic-lo-hi.h>
36
37#include <mach/msm_iomap.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038
39#include "peripheral-loader.h"
Stephen Boyd10667772012-11-28 16:45:35 -080040#include "ramdump.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070041
Stephen Boyd163f1c32012-06-29 13:20:20 -070042#define pil_err(desc, fmt, ...) \
43 dev_err(desc->dev, "%s: " fmt, desc->name, ##__VA_ARGS__)
44#define pil_info(desc, fmt, ...) \
45 dev_info(desc->dev, "%s: " fmt, desc->name, ##__VA_ARGS__)
46
Stephen Boydb455f322012-11-27 19:00:01 -080047#define PIL_IMAGE_INFO_BASE (MSM_IMEM_BASE + 0x94c)
48
Matt Wagantall0aafa352012-05-14 16:40:41 -070049/**
50 * proxy_timeout - Override for proxy vote timeouts
51 * -1: Use driver-specified timeout
52 * 0: Hold proxy votes until shutdown
53 * >0: Specify a custom timeout in ms
54 */
55static int proxy_timeout_ms = -1;
56module_param(proxy_timeout_ms, int, S_IRUGO | S_IWUSR);
57
Stephen Boyd163f1c32012-06-29 13:20:20 -070058/**
Stephen Boydedff6cf2012-07-11 19:39:27 -070059 * struct pil_mdt - Representation of <name>.mdt file in memory
60 * @hdr: ELF32 header
61 * @phdr: ELF32 program headers
62 */
63struct pil_mdt {
64 struct elf32_hdr hdr;
65 struct elf32_phdr phdr[];
66};
67
68/**
69 * struct pil_seg - memory map representing one segment
70 * @next: points to next seg mentor NULL if last segment
71 * @paddr: start address of segment
72 * @sz: size of segment
73 * @filesz: size of segment on disk
74 * @num: segment number
Stephen Boyd2db158c2012-07-26 21:47:17 -070075 * @relocated: true if segment is relocated, false otherwise
Stephen Boydedff6cf2012-07-11 19:39:27 -070076 *
77 * Loosely based on an elf program header. Contains all necessary information
78 * to load and initialize a segment of the image in memory.
79 */
80struct pil_seg {
81 phys_addr_t paddr;
82 unsigned long sz;
83 unsigned long filesz;
84 int num;
85 struct list_head list;
Stephen Boyd2db158c2012-07-26 21:47:17 -070086 bool relocated;
Stephen Boydedff6cf2012-07-11 19:39:27 -070087};
88
89/**
Stephen Boydb455f322012-11-27 19:00:01 -080090 * struct pil_image_info - information in IMEM about image and where it is loaded
91 * @name: name of image (may or may not be NULL terminated)
92 * @start: indicates physical address where image starts (little endian)
93 * @size: size of image (little endian)
94 */
95struct pil_image_info {
96 char name[8];
97 __le64 start;
98 __le32 size;
99} __attribute__((__packed__));
100
101/**
Stephen Boyd163f1c32012-06-29 13:20:20 -0700102 * struct pil_priv - Private state for a pil_desc
103 * @proxy: work item used to run the proxy unvoting routine
104 * @wlock: wakelock to prevent suspend during pil_boot
105 * @wname: name of @wlock
106 * @desc: pointer to pil_desc this is private data for
Stephen Boydedff6cf2012-07-11 19:39:27 -0700107 * @seg: list of segments sorted by physical address
Stephen Boyd3030c252012-08-08 17:24:05 -0700108 * @entry_addr: physical address where processor starts booting at
Stephen Boyd2db158c2012-07-26 21:47:17 -0700109 * @base_addr: smallest start address among all segments that are relocatable
Stephen Boyd379e7332012-08-08 18:04:21 -0700110 * @region_start: address where relocatable region starts or lowest address
111 * for non-relocatable images
112 * @region_end: address where relocatable region ends or highest address for
113 * non-relocatable images
Stephen Boyd2db158c2012-07-26 21:47:17 -0700114 * @region: region allocated for relocatable images
Stephen Boyd163f1c32012-06-29 13:20:20 -0700115 *
116 * This struct contains data for a pil_desc that should not be exposed outside
117 * of this file. This structure points to the descriptor and the descriptor
118 * points to this structure so that PIL drivers can't access the private
119 * data of a descriptor but this file can access both.
120 */
121struct pil_priv {
Stephen Boyd36974ec2012-03-22 01:30:59 -0700122 struct delayed_work proxy;
123 struct wake_lock wlock;
Stephen Boyd163f1c32012-06-29 13:20:20 -0700124 char wname[32];
125 struct pil_desc *desc;
Stephen Boydedff6cf2012-07-11 19:39:27 -0700126 struct list_head segs;
Stephen Boyd3030c252012-08-08 17:24:05 -0700127 phys_addr_t entry_addr;
Stephen Boyd2db158c2012-07-26 21:47:17 -0700128 phys_addr_t base_addr;
Stephen Boyd379e7332012-08-08 18:04:21 -0700129 phys_addr_t region_start;
130 phys_addr_t region_end;
Stephen Boyd2db158c2012-07-26 21:47:17 -0700131 struct ion_handle *region;
Stephen Boydb455f322012-11-27 19:00:01 -0800132 struct pil_image_info __iomem *info;
133 int id;
Stephen Boyd3f4da322011-08-30 01:03:23 -0700134};
135
Stephen Boyd10667772012-11-28 16:45:35 -0800136/**
137 * pil_do_ramdump() - Ramdump an image
138 * @desc: descriptor from pil_desc_init()
139 * @ramdump_dev: ramdump device returned from create_ramdump_device()
140 *
141 * Calls the ramdump API with a list of segments generated from the addresses
142 * that the descriptor corresponds to.
143 */
144int pil_do_ramdump(struct pil_desc *desc, void *ramdump_dev)
145{
146 struct pil_priv *priv = desc->priv;
147 struct pil_seg *seg;
148 int count = 0, ret;
149 struct ramdump_segment *ramdump_segs, *s;
150
151 list_for_each_entry(seg, &priv->segs, list)
152 count++;
153
154 ramdump_segs = kmalloc_array(count, sizeof(*ramdump_segs), GFP_KERNEL);
155 if (!ramdump_segs)
156 return -ENOMEM;
157
158 s = ramdump_segs;
159 list_for_each_entry(seg, &priv->segs, list) {
160 s->address = seg->paddr;
161 s->size = seg->sz;
162 s++;
163 }
164
165 ret = do_elf_ramdump(ramdump_dev, ramdump_segs, count);
166 kfree(ramdump_segs);
167
168 return ret;
169}
170EXPORT_SYMBOL(pil_do_ramdump);
171
Stephen Boyd2db158c2012-07-26 21:47:17 -0700172static struct ion_client *ion;
173
Stephen Boyd3030c252012-08-08 17:24:05 -0700174/**
175 * pil_get_entry_addr() - Retrieve the entry address of a peripheral image
176 * @desc: descriptor from pil_desc_init()
177 *
178 * Returns the physical address where the image boots at or 0 if unknown.
179 */
180phys_addr_t pil_get_entry_addr(struct pil_desc *desc)
181{
182 return desc->priv ? desc->priv->entry_addr : 0;
183}
184EXPORT_SYMBOL(pil_get_entry_addr);
185
Stephen Boyd36974ec2012-03-22 01:30:59 -0700186static void pil_proxy_work(struct work_struct *work)
187{
Stephen Boyd163f1c32012-06-29 13:20:20 -0700188 struct delayed_work *delayed = to_delayed_work(work);
189 struct pil_priv *priv = container_of(delayed, struct pil_priv, proxy);
190 struct pil_desc *desc = priv->desc;
Stephen Boyd36974ec2012-03-22 01:30:59 -0700191
Stephen Boyd163f1c32012-06-29 13:20:20 -0700192 desc->ops->proxy_unvote(desc);
193 wake_unlock(&priv->wlock);
194 module_put(desc->owner);
Stephen Boyd36974ec2012-03-22 01:30:59 -0700195}
196
Stephen Boyd163f1c32012-06-29 13:20:20 -0700197static int pil_proxy_vote(struct pil_desc *desc)
Stephen Boyd36974ec2012-03-22 01:30:59 -0700198{
Stephen Boyd0a563142012-07-02 13:33:31 -0700199 int ret = 0;
Stephen Boyd163f1c32012-06-29 13:20:20 -0700200 struct pil_priv *priv = desc->priv;
Stephen Boyd0a563142012-07-02 13:33:31 -0700201
Stephen Boyd163f1c32012-06-29 13:20:20 -0700202 if (desc->ops->proxy_vote) {
203 wake_lock(&priv->wlock);
204 ret = desc->ops->proxy_vote(desc);
Stephen Boyd0a563142012-07-02 13:33:31 -0700205 if (ret)
Stephen Boyd163f1c32012-06-29 13:20:20 -0700206 wake_unlock(&priv->wlock);
Stephen Boyd36974ec2012-03-22 01:30:59 -0700207 }
Stephen Boyd0a563142012-07-02 13:33:31 -0700208 return ret;
Stephen Boyd36974ec2012-03-22 01:30:59 -0700209}
210
Stephen Boyd163f1c32012-06-29 13:20:20 -0700211static void pil_proxy_unvote(struct pil_desc *desc, unsigned long timeout)
Stephen Boyd36974ec2012-03-22 01:30:59 -0700212{
Stephen Boyd163f1c32012-06-29 13:20:20 -0700213 struct pil_priv *priv = desc->priv;
214
Matt Wagantall0aafa352012-05-14 16:40:41 -0700215 if (proxy_timeout_ms >= 0)
216 timeout = proxy_timeout_ms;
217
Stephen Boyd163f1c32012-06-29 13:20:20 -0700218 if (timeout && desc->ops->proxy_unvote) {
219 if (WARN_ON(!try_module_get(desc->owner)))
220 return;
221 schedule_delayed_work(&priv->proxy, msecs_to_jiffies(timeout));
222 }
Stephen Boyd36974ec2012-03-22 01:30:59 -0700223}
224
Stephen Boyd2db158c2012-07-26 21:47:17 -0700225static bool segment_is_relocatable(const struct elf32_phdr *p)
226{
227 return !!(p->p_flags & BIT(27));
228}
229
230static phys_addr_t pil_reloc(const struct pil_priv *priv, phys_addr_t addr)
231{
232 return addr - priv->base_addr + priv->region_start;
233}
234
Stephen Boydedff6cf2012-07-11 19:39:27 -0700235static struct pil_seg *pil_init_seg(const struct pil_desc *desc,
236 const struct elf32_phdr *phdr, int num)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700237{
Stephen Boyd2db158c2012-07-26 21:47:17 -0700238 bool reloc = segment_is_relocatable(phdr);
239 const struct pil_priv *priv = desc->priv;
Stephen Boydedff6cf2012-07-11 19:39:27 -0700240 struct pil_seg *seg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700241
Stephen Boyd2db158c2012-07-26 21:47:17 -0700242 if (!reloc && memblock_overlaps_memory(phdr->p_paddr, phdr->p_memsz)) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700243 pil_err(desc, "kernel memory would be overwritten [%#08lx, %#08lx)\n",
Stephen Boydf79af532012-05-14 18:57:26 -0700244 (unsigned long)phdr->p_paddr,
245 (unsigned long)(phdr->p_paddr + phdr->p_memsz));
Stephen Boydedff6cf2012-07-11 19:39:27 -0700246 return ERR_PTR(-EPERM);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700247 }
248
Stephen Boydedff6cf2012-07-11 19:39:27 -0700249 seg = kmalloc(sizeof(*seg), GFP_KERNEL);
250 if (!seg)
251 return ERR_PTR(-ENOMEM);
252 seg->num = num;
Stephen Boyd2db158c2012-07-26 21:47:17 -0700253 seg->paddr = reloc ? pil_reloc(priv, phdr->p_paddr) : phdr->p_paddr;
Stephen Boydedff6cf2012-07-11 19:39:27 -0700254 seg->filesz = phdr->p_filesz;
255 seg->sz = phdr->p_memsz;
Stephen Boyd2db158c2012-07-26 21:47:17 -0700256 seg->relocated = reloc;
Stephen Boydedff6cf2012-07-11 19:39:27 -0700257 INIT_LIST_HEAD(&seg->list);
258
259 return seg;
260}
261
262#define segment_is_hash(flag) (((flag) & (0x7 << 24)) == (0x2 << 24))
263
264static int segment_is_loadable(const struct elf32_phdr *p)
265{
Stephen Boyd2db158c2012-07-26 21:47:17 -0700266 return (p->p_type == PT_LOAD) && !segment_is_hash(p->p_flags) &&
267 p->p_memsz;
Stephen Boydedff6cf2012-07-11 19:39:27 -0700268}
269
Stephen Boyd3030c252012-08-08 17:24:05 -0700270static void pil_dump_segs(const struct pil_priv *priv)
271{
272 struct pil_seg *seg;
273
274 list_for_each_entry(seg, &priv->segs, list) {
275 pil_info(priv->desc, "%d: %#08zx %#08lx\n", seg->num,
276 seg->paddr, seg->paddr + seg->sz);
277 }
278}
279
280/*
Stephen Boyd2db158c2012-07-26 21:47:17 -0700281 * Ensure the entry address lies within the image limits and if the image is
282 * relocatable ensure it lies within a relocatable segment.
Stephen Boyd3030c252012-08-08 17:24:05 -0700283 */
284static int pil_init_entry_addr(struct pil_priv *priv, const struct pil_mdt *mdt)
285{
286 struct pil_seg *seg;
Stephen Boyd2db158c2012-07-26 21:47:17 -0700287 phys_addr_t entry = mdt->hdr.e_entry;
288 bool image_relocated = priv->region;
Stephen Boyd3030c252012-08-08 17:24:05 -0700289
Stephen Boyd2db158c2012-07-26 21:47:17 -0700290 if (image_relocated)
291 entry = pil_reloc(priv, entry);
292 priv->entry_addr = entry;
Stephen Boyd3030c252012-08-08 17:24:05 -0700293
294 if (priv->desc->flags & PIL_SKIP_ENTRY_CHECK)
295 return 0;
296
297 list_for_each_entry(seg, &priv->segs, list) {
Stephen Boyd2db158c2012-07-26 21:47:17 -0700298 if (entry >= seg->paddr && entry < seg->paddr + seg->sz) {
299 if (!image_relocated)
300 return 0;
301 else if (seg->relocated)
302 return 0;
303 }
Stephen Boyd3030c252012-08-08 17:24:05 -0700304 }
Stephen Boyd2db158c2012-07-26 21:47:17 -0700305 pil_err(priv->desc, "entry address %08zx not within range\n", entry);
Stephen Boyd3030c252012-08-08 17:24:05 -0700306 pil_dump_segs(priv);
307 return -EADDRNOTAVAIL;
308}
309
Stephen Boyd2db158c2012-07-26 21:47:17 -0700310static int pil_alloc_region(struct pil_priv *priv, phys_addr_t min_addr,
311 phys_addr_t max_addr, size_t align)
312{
313 struct ion_handle *region;
314 int ret;
315 unsigned int mask;
Stephen Boyd6385e312012-12-12 11:17:04 -0800316 size_t size = max_addr - min_addr;
Stephen Boyd2db158c2012-07-26 21:47:17 -0700317
318 if (!ion) {
319 WARN_ON_ONCE("No ION client, can't support relocation\n");
320 return -ENOMEM;
321 }
322
323 /* Force alignment due to linker scripts not getting it right */
324 if (align > SZ_1M) {
325 mask = ION_HEAP(ION_PIL2_HEAP_ID);
326 align = SZ_4M;
327 } else {
328 mask = ION_HEAP(ION_PIL1_HEAP_ID);
329 align = SZ_1M;
330 }
331
332 region = ion_alloc(ion, size, align, mask, 0);
333 if (IS_ERR(region)) {
334 pil_err(priv->desc, "Failed to allocate relocatable region\n");
335 return PTR_ERR(region);
336 }
337
338 ret = ion_phys(ion, region, (ion_phys_addr_t *)&priv->region_start,
339 &size);
340 if (ret) {
341 ion_free(ion, region);
342 return ret;
343 }
344
345 priv->region = region;
346 priv->region_end = priv->region_start + size;
347 priv->base_addr = min_addr;
348
349 return 0;
350}
351
Stephen Boyd379e7332012-08-08 18:04:21 -0700352static int pil_setup_region(struct pil_priv *priv, const struct pil_mdt *mdt)
353{
354 const struct elf32_phdr *phdr;
Stephen Boyd2db158c2012-07-26 21:47:17 -0700355 phys_addr_t min_addr_r, min_addr_n, max_addr_r, max_addr_n, start, end;
356 size_t align = 0;
357 int i, ret = 0;
358 bool relocatable = false;
Stephen Boyd379e7332012-08-08 18:04:21 -0700359
Stephen Boyd2db158c2012-07-26 21:47:17 -0700360 min_addr_n = min_addr_r = (phys_addr_t)ULLONG_MAX;
361 max_addr_n = max_addr_r = 0;
Stephen Boyd379e7332012-08-08 18:04:21 -0700362
363 /* Find the image limits */
364 for (i = 0; i < mdt->hdr.e_phnum; i++) {
365 phdr = &mdt->phdr[i];
366 if (!segment_is_loadable(phdr))
367 continue;
368
Stephen Boyd2db158c2012-07-26 21:47:17 -0700369 start = phdr->p_paddr;
370 end = start + phdr->p_memsz;
371
372 if (segment_is_relocatable(phdr)) {
373 min_addr_r = min(min_addr_r, start);
374 max_addr_r = max(max_addr_r, end);
375 /*
376 * Lowest relocatable segment dictates alignment of
377 * relocatable region
378 */
379 if (min_addr_r == start)
380 align = phdr->p_align;
381 relocatable = true;
382 } else {
383 min_addr_n = min(min_addr_n, start);
384 max_addr_n = max(max_addr_n, end);
385 }
386
Stephen Boyd379e7332012-08-08 18:04:21 -0700387 }
388
Stephen Boyd6385e312012-12-12 11:17:04 -0800389 /*
390 * Align the max address to the next 4K boundary to satisfy iommus and
391 * XPUs that operate on 4K chunks.
392 */
393 max_addr_n = ALIGN(max_addr_n, SZ_4K);
394 max_addr_r = ALIGN(max_addr_r, SZ_4K);
395
Stephen Boyd2db158c2012-07-26 21:47:17 -0700396 if (relocatable) {
397 ret = pil_alloc_region(priv, min_addr_r, max_addr_r, align);
398 } else {
399 priv->region_start = min_addr_n;
400 priv->region_end = max_addr_n;
401 priv->base_addr = min_addr_n;
402 }
Stephen Boyd379e7332012-08-08 18:04:21 -0700403
Stephen Boydb455f322012-11-27 19:00:01 -0800404 writeq(priv->region_start, &priv->info->start);
405 writel_relaxed(priv->region_end - priv->region_start,
406 &priv->info->size);
407
Stephen Boyd2db158c2012-07-26 21:47:17 -0700408 return ret;
Stephen Boyd379e7332012-08-08 18:04:21 -0700409}
410
Stephen Boydedff6cf2012-07-11 19:39:27 -0700411static int pil_cmp_seg(void *priv, struct list_head *a, struct list_head *b)
412{
413 struct pil_seg *seg_a = list_entry(a, struct pil_seg, list);
414 struct pil_seg *seg_b = list_entry(b, struct pil_seg, list);
415
416 return seg_a->paddr - seg_b->paddr;
417}
418
419static int pil_init_mmap(struct pil_desc *desc, const struct pil_mdt *mdt)
420{
Stephen Boyd3030c252012-08-08 17:24:05 -0700421 struct pil_priv *priv = desc->priv;
Stephen Boydedff6cf2012-07-11 19:39:27 -0700422 const struct elf32_phdr *phdr;
423 struct pil_seg *seg;
Stephen Boyd379e7332012-08-08 18:04:21 -0700424 int i, ret;
425
426 ret = pil_setup_region(priv, mdt);
427 if (ret)
428 return ret;
Stephen Boydedff6cf2012-07-11 19:39:27 -0700429
430 for (i = 0; i < mdt->hdr.e_phnum; i++) {
431 phdr = &mdt->phdr[i];
432 if (!segment_is_loadable(phdr))
433 continue;
434
435 seg = pil_init_seg(desc, phdr, i);
436 if (IS_ERR(seg))
437 return PTR_ERR(seg);
438
439 list_add_tail(&seg->list, &priv->segs);
440 }
441 list_sort(NULL, &priv->segs, pil_cmp_seg);
442
Stephen Boyd3030c252012-08-08 17:24:05 -0700443 return pil_init_entry_addr(priv, mdt);
Stephen Boydedff6cf2012-07-11 19:39:27 -0700444}
445
446static void pil_release_mmap(struct pil_desc *desc)
447{
448 struct pil_priv *priv = desc->priv;
449 struct pil_seg *p, *tmp;
450
Stephen Boydb455f322012-11-27 19:00:01 -0800451 writeq(0, &priv->info->start);
452 writel_relaxed(0, &priv->info->size);
453
Stephen Boyd2db158c2012-07-26 21:47:17 -0700454 if (priv->region)
455 ion_free(ion, priv->region);
Stephen Boyd3e29f102012-11-28 18:55:25 -0800456 priv->region = NULL;
Stephen Boydedff6cf2012-07-11 19:39:27 -0700457 list_for_each_entry_safe(p, tmp, &priv->segs, list) {
458 list_del(&p->list);
459 kfree(p);
460 }
461}
462
463#define IOMAP_SIZE SZ_4M
464
465static int pil_load_seg(struct pil_desc *desc, struct pil_seg *seg)
466{
467 int ret = 0, count, paddr;
468 char fw_name[30];
469 const struct firmware *fw = NULL;
470 const u8 *data;
471 int num = seg->num;
472
473 if (seg->filesz) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700474 snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d",
Stephen Boyd163f1c32012-06-29 13:20:20 -0700475 desc->name, num);
476 ret = request_firmware(&fw, fw_name, desc->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700477 if (ret) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700478 pil_err(desc, "Failed to locate blob %s\n", fw_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700479 return ret;
480 }
481
Stephen Boydedff6cf2012-07-11 19:39:27 -0700482 if (fw->size != seg->filesz) {
483 pil_err(desc, "Blob size %u doesn't match %lu\n",
484 fw->size, seg->filesz);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700485 ret = -EPERM;
486 goto release_fw;
487 }
488 }
489
490 /* Load the segment into memory */
Stephen Boydedff6cf2012-07-11 19:39:27 -0700491 count = seg->filesz;
492 paddr = seg->paddr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700493 data = fw ? fw->data : NULL;
494 while (count > 0) {
495 int size;
496 u8 __iomem *buf;
497
498 size = min_t(size_t, IOMAP_SIZE, count);
499 buf = ioremap(paddr, size);
500 if (!buf) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700501 pil_err(desc, "Failed to map memory\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700502 ret = -ENOMEM;
503 goto release_fw;
504 }
505 memcpy(buf, data, size);
506 iounmap(buf);
507
508 count -= size;
509 paddr += size;
510 data += size;
511 }
512
513 /* Zero out trailing memory */
Stephen Boydedff6cf2012-07-11 19:39:27 -0700514 count = seg->sz - seg->filesz;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700515 while (count > 0) {
516 int size;
517 u8 __iomem *buf;
518
519 size = min_t(size_t, IOMAP_SIZE, count);
520 buf = ioremap(paddr, size);
521 if (!buf) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700522 pil_err(desc, "Failed to map memory\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700523 ret = -ENOMEM;
524 goto release_fw;
525 }
526 memset(buf, 0, size);
527 iounmap(buf);
528
529 count -= size;
530 paddr += size;
531 }
532
Stephen Boyd163f1c32012-06-29 13:20:20 -0700533 if (desc->ops->verify_blob) {
Stephen Boydedff6cf2012-07-11 19:39:27 -0700534 ret = desc->ops->verify_blob(desc, seg->paddr, seg->sz);
Stephen Boydb0f1f802012-02-03 11:28:08 -0800535 if (ret)
Stephen Boyd163f1c32012-06-29 13:20:20 -0700536 pil_err(desc, "Blob%u failed verification\n", num);
Stephen Boydb0f1f802012-02-03 11:28:08 -0800537 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700538
539release_fw:
540 release_firmware(fw);
541 return ret;
542}
543
Stephen Boyd163f1c32012-06-29 13:20:20 -0700544/* Synchronize request_firmware() with suspend */
Stephen Boyd80bde032012-03-16 00:14:42 -0700545static DECLARE_RWSEM(pil_pm_rwsem);
546
Stephen Boyd163f1c32012-06-29 13:20:20 -0700547/**
548 * pil_boot() - Load a peripheral image into memory and boot it
549 * @desc: descriptor from pil_desc_init()
550 *
551 * Returns 0 on success or -ERROR on failure.
552 */
553int pil_boot(struct pil_desc *desc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700554{
Stephen Boydedff6cf2012-07-11 19:39:27 -0700555 int ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700556 char fw_name[30];
Stephen Boydedff6cf2012-07-11 19:39:27 -0700557 const struct pil_mdt *mdt;
558 const struct elf32_hdr *ehdr;
559 struct pil_seg *seg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700560 const struct firmware *fw;
Stephen Boyd163f1c32012-06-29 13:20:20 -0700561 unsigned long proxy_timeout = desc->proxy_timeout;
Stephen Boyd379e7332012-08-08 18:04:21 -0700562 struct pil_priv *priv = desc->priv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700563
Stephen Boydedff6cf2012-07-11 19:39:27 -0700564 /* Reinitialize for new image */
565 pil_release_mmap(desc);
566
Stephen Boyd80bde032012-03-16 00:14:42 -0700567 down_read(&pil_pm_rwsem);
Stephen Boyd163f1c32012-06-29 13:20:20 -0700568 snprintf(fw_name, sizeof(fw_name), "%s.mdt", desc->name);
569 ret = request_firmware(&fw, fw_name, desc->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700570 if (ret) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700571 pil_err(desc, "Failed to locate %s\n", fw_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700572 goto out;
573 }
574
575 if (fw->size < sizeof(*ehdr)) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700576 pil_err(desc, "Not big enough to be an elf header\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700577 ret = -EIO;
578 goto release_fw;
579 }
580
Stephen Boydedff6cf2012-07-11 19:39:27 -0700581 mdt = (const struct pil_mdt *)fw->data;
582 ehdr = &mdt->hdr;
583
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700584 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700585 pil_err(desc, "Not an elf header\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700586 ret = -EIO;
587 goto release_fw;
588 }
589
590 if (ehdr->e_phnum == 0) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700591 pil_err(desc, "No loadable segments\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700592 ret = -EIO;
593 goto release_fw;
594 }
Stephen Boyd96a9f902011-07-18 18:43:00 -0700595 if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
596 sizeof(struct elf32_hdr) > fw->size) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700597 pil_err(desc, "Program headers not within mdt\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700598 ret = -EIO;
599 goto release_fw;
600 }
601
Stephen Boydedff6cf2012-07-11 19:39:27 -0700602 ret = pil_init_mmap(desc, mdt);
603 if (ret)
604 goto release_fw;
605
Stephen Boyd3030c252012-08-08 17:24:05 -0700606 if (desc->ops->init_image)
607 ret = desc->ops->init_image(desc, fw->data, fw->size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700608 if (ret) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700609 pil_err(desc, "Invalid firmware metadata\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700610 goto release_fw;
611 }
612
Stephen Boyd379e7332012-08-08 18:04:21 -0700613 if (desc->ops->mem_setup)
614 ret = desc->ops->mem_setup(desc, priv->region_start,
615 priv->region_end - priv->region_start);
616 if (ret) {
617 pil_err(desc, "Memory setup error\n");
618 goto release_fw;
619 }
620
Stephen Boydedff6cf2012-07-11 19:39:27 -0700621 list_for_each_entry(seg, &desc->priv->segs, list) {
622 ret = pil_load_seg(desc, seg);
623 if (ret)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700624 goto release_fw;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700625 }
626
Stephen Boyd163f1c32012-06-29 13:20:20 -0700627 ret = pil_proxy_vote(desc);
Stephen Boyd36974ec2012-03-22 01:30:59 -0700628 if (ret) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700629 pil_err(desc, "Failed to proxy vote\n");
Stephen Boyd36974ec2012-03-22 01:30:59 -0700630 goto release_fw;
631 }
632
Stephen Boyd163f1c32012-06-29 13:20:20 -0700633 ret = desc->ops->auth_and_reset(desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700634 if (ret) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700635 pil_err(desc, "Failed to bring out of reset\n");
Stephen Boyd36974ec2012-03-22 01:30:59 -0700636 proxy_timeout = 0; /* Remove proxy vote immediately on error */
637 goto err_boot;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700638 }
Stephen Boyd163f1c32012-06-29 13:20:20 -0700639 pil_info(desc, "Brought out of reset\n");
Stephen Boyd36974ec2012-03-22 01:30:59 -0700640err_boot:
Stephen Boyd163f1c32012-06-29 13:20:20 -0700641 pil_proxy_unvote(desc, proxy_timeout);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700642release_fw:
643 release_firmware(fw);
644out:
Stephen Boyd80bde032012-03-16 00:14:42 -0700645 up_read(&pil_pm_rwsem);
Stephen Boydedff6cf2012-07-11 19:39:27 -0700646 if (ret)
647 pil_release_mmap(desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700648 return ret;
649}
Stephen Boyd163f1c32012-06-29 13:20:20 -0700650EXPORT_SYMBOL(pil_boot);
651
Stephen Boyd163f1c32012-06-29 13:20:20 -0700652/**
653 * pil_shutdown() - Shutdown a peripheral
654 * @desc: descriptor from pil_desc_init()
655 */
656void pil_shutdown(struct pil_desc *desc)
Stephen Boyd36974ec2012-03-22 01:30:59 -0700657{
Stephen Boyd163f1c32012-06-29 13:20:20 -0700658 struct pil_priv *priv = desc->priv;
659 desc->ops->shutdown(desc);
660 if (proxy_timeout_ms == 0 && desc->ops->proxy_unvote)
661 desc->ops->proxy_unvote(desc);
Matt Wagantall0aafa352012-05-14 16:40:41 -0700662 else
Stephen Boyd163f1c32012-06-29 13:20:20 -0700663 flush_delayed_work(&priv->proxy);
664}
665EXPORT_SYMBOL(pil_shutdown);
Matt Wagantall0aafa352012-05-14 16:40:41 -0700666
Stephen Boydb455f322012-11-27 19:00:01 -0800667static DEFINE_IDA(pil_ida);
668
Stephen Boyd163f1c32012-06-29 13:20:20 -0700669/**
670 * pil_desc_init() - Initialize a pil descriptor
671 * @desc: descriptor to intialize
672 *
673 * Initialize a pil descriptor for use by other pil functions. This function
674 * must be called before calling pil_boot() or pil_shutdown().
675 *
676 * Returns 0 for success and -ERROR on failure.
677 */
678int pil_desc_init(struct pil_desc *desc)
679{
680 struct pil_priv *priv;
Stephen Boydb455f322012-11-27 19:00:01 -0800681 int id;
682 void __iomem *addr;
Stephen Boyd06ce3962013-01-02 15:03:14 -0800683 char buf[sizeof(priv->info->name)];
Stephen Boyd163f1c32012-06-29 13:20:20 -0700684
685 /* Ignore users who don't make any sense */
686 WARN(desc->ops->proxy_unvote && !desc->proxy_timeout,
687 "A proxy timeout of 0 was specified.\n");
688 if (WARN(desc->ops->proxy_unvote && !desc->ops->proxy_vote,
689 "Invalid proxy voting. Ignoring\n"))
690 ((struct pil_reset_ops *)desc->ops)->proxy_unvote = NULL;
691
692 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
693 if (!priv)
694 return -ENOMEM;
695 desc->priv = priv;
696 priv->desc = desc;
697
Stephen Boydb455f322012-11-27 19:00:01 -0800698 priv->id = id = ida_simple_get(&pil_ida, 0, 10, GFP_KERNEL);
699 if (id < 0) {
700 kfree(priv);
701 return id;
702 }
703 addr = PIL_IMAGE_INFO_BASE + sizeof(struct pil_image_info) * id;
704 priv->info = (struct pil_image_info __iomem *)addr;
705
Stephen Boyd06ce3962013-01-02 15:03:14 -0800706 strncpy(buf, desc->name, sizeof(buf));
707 __iowrite32_copy(priv->info->name, buf, sizeof(buf) / 4);
Stephen Boydb455f322012-11-27 19:00:01 -0800708
Stephen Boyd163f1c32012-06-29 13:20:20 -0700709 snprintf(priv->wname, sizeof(priv->wname), "pil-%s", desc->name);
710 wake_lock_init(&priv->wlock, WAKE_LOCK_SUSPEND, priv->wname);
711 INIT_DELAYED_WORK(&priv->proxy, pil_proxy_work);
Stephen Boydedff6cf2012-07-11 19:39:27 -0700712 INIT_LIST_HEAD(&priv->segs);
Stephen Boyd163f1c32012-06-29 13:20:20 -0700713
714 return 0;
715}
716EXPORT_SYMBOL(pil_desc_init);
717
718/**
719 * pil_desc_release() - Release a pil descriptor
720 * @desc: descriptor to free
721 */
722void pil_desc_release(struct pil_desc *desc)
723{
724 struct pil_priv *priv = desc->priv;
725
726 if (priv) {
Stephen Boydb455f322012-11-27 19:00:01 -0800727 ida_simple_remove(&pil_ida, priv->id);
Stephen Boyd163f1c32012-06-29 13:20:20 -0700728 flush_delayed_work(&priv->proxy);
729 wake_lock_destroy(&priv->wlock);
730 }
731 desc->priv = NULL;
732 kfree(priv);
733}
734EXPORT_SYMBOL(pil_desc_release);
735
Stephen Boyd80bde032012-03-16 00:14:42 -0700736static int pil_pm_notify(struct notifier_block *b, unsigned long event, void *p)
737{
738 switch (event) {
739 case PM_SUSPEND_PREPARE:
740 down_write(&pil_pm_rwsem);
741 break;
742 case PM_POST_SUSPEND:
743 up_write(&pil_pm_rwsem);
744 break;
745 }
746 return NOTIFY_DONE;
747}
748
749static struct notifier_block pil_pm_notifier = {
750 .notifier_call = pil_pm_notify,
751};
752
Stephen Boyd6d67d252011-09-27 11:50:05 -0700753static int __init msm_pil_init(void)
754{
Stephen Boyd2db158c2012-07-26 21:47:17 -0700755 ion = msm_ion_client_create(UINT_MAX, "pil");
756 if (IS_ERR(ion)) /* Can't support relocatable images */
757 ion = NULL;
Stephen Boyd04148122012-06-29 18:18:12 -0700758 return register_pm_notifier(&pil_pm_notifier);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700759}
Stephen Boyd2db158c2012-07-26 21:47:17 -0700760device_initcall(msm_pil_init);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700761
762static void __exit msm_pil_exit(void)
763{
Stephen Boyd80bde032012-03-16 00:14:42 -0700764 unregister_pm_notifier(&pil_pm_notifier);
Stephen Boyd2db158c2012-07-26 21:47:17 -0700765 if (ion)
766 ion_client_destroy(ion);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700767}
768module_exit(msm_pil_exit);
769
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700770MODULE_LICENSE("GPL v2");
771MODULE_DESCRIPTION("Load peripheral images and bring peripherals out of reset");