blob: c572291fd0154a6eea122b729ada260fd6a4fba6 [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>
Seemanta Duttad21a7972013-03-05 12:16:17 -080032#include <linux/interrupt.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033
34#include <asm/uaccess.h>
35#include <asm/setup.h>
Stephen Boydb455f322012-11-27 19:00:01 -080036#include <asm-generic/io-64-nonatomic-lo-hi.h>
37
38#include <mach/msm_iomap.h>
Seemanta Dutta4e2d49c2013-04-05 16:28:11 -070039#include <mach/ramdump.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070040
41#include "peripheral-loader.h"
42
Stephen Boyd163f1c32012-06-29 13:20:20 -070043#define pil_err(desc, fmt, ...) \
44 dev_err(desc->dev, "%s: " fmt, desc->name, ##__VA_ARGS__)
45#define pil_info(desc, fmt, ...) \
46 dev_info(desc->dev, "%s: " fmt, desc->name, ##__VA_ARGS__)
47
Stephen Boydb455f322012-11-27 19:00:01 -080048#define PIL_IMAGE_INFO_BASE (MSM_IMEM_BASE + 0x94c)
49
Matt Wagantall0aafa352012-05-14 16:40:41 -070050/**
51 * proxy_timeout - Override for proxy vote timeouts
52 * -1: Use driver-specified timeout
53 * 0: Hold proxy votes until shutdown
54 * >0: Specify a custom timeout in ms
55 */
56static int proxy_timeout_ms = -1;
57module_param(proxy_timeout_ms, int, S_IRUGO | S_IWUSR);
58
Stephen Boyd163f1c32012-06-29 13:20:20 -070059/**
Stephen Boydedff6cf2012-07-11 19:39:27 -070060 * struct pil_mdt - Representation of <name>.mdt file in memory
61 * @hdr: ELF32 header
62 * @phdr: ELF32 program headers
63 */
64struct pil_mdt {
65 struct elf32_hdr hdr;
66 struct elf32_phdr phdr[];
67};
68
69/**
70 * struct pil_seg - memory map representing one segment
71 * @next: points to next seg mentor NULL if last segment
72 * @paddr: start address of segment
73 * @sz: size of segment
74 * @filesz: size of segment on disk
75 * @num: segment number
Stephen Boyd2db158c2012-07-26 21:47:17 -070076 * @relocated: true if segment is relocated, false otherwise
Stephen Boydedff6cf2012-07-11 19:39:27 -070077 *
78 * Loosely based on an elf program header. Contains all necessary information
79 * to load and initialize a segment of the image in memory.
80 */
81struct pil_seg {
82 phys_addr_t paddr;
83 unsigned long sz;
84 unsigned long filesz;
85 int num;
86 struct list_head list;
Stephen Boyd2db158c2012-07-26 21:47:17 -070087 bool relocated;
Stephen Boydedff6cf2012-07-11 19:39:27 -070088};
89
90/**
Stephen Boydb455f322012-11-27 19:00:01 -080091 * struct pil_image_info - information in IMEM about image and where it is loaded
92 * @name: name of image (may or may not be NULL terminated)
93 * @start: indicates physical address where image starts (little endian)
94 * @size: size of image (little endian)
95 */
96struct pil_image_info {
97 char name[8];
98 __le64 start;
99 __le32 size;
100} __attribute__((__packed__));
101
102/**
Stephen Boyd163f1c32012-06-29 13:20:20 -0700103 * struct pil_priv - Private state for a pil_desc
104 * @proxy: work item used to run the proxy unvoting routine
105 * @wlock: wakelock to prevent suspend during pil_boot
106 * @wname: name of @wlock
107 * @desc: pointer to pil_desc this is private data for
Stephen Boydedff6cf2012-07-11 19:39:27 -0700108 * @seg: list of segments sorted by physical address
Stephen Boyd3030c252012-08-08 17:24:05 -0700109 * @entry_addr: physical address where processor starts booting at
Stephen Boyd2db158c2012-07-26 21:47:17 -0700110 * @base_addr: smallest start address among all segments that are relocatable
Stephen Boyd379e7332012-08-08 18:04:21 -0700111 * @region_start: address where relocatable region starts or lowest address
112 * for non-relocatable images
113 * @region_end: address where relocatable region ends or highest address for
114 * non-relocatable images
Stephen Boyd2db158c2012-07-26 21:47:17 -0700115 * @region: region allocated for relocatable images
Seemanta Dutta9907e2a2013-05-16 19:57:09 -0700116 * @unvoted_flag: flag to keep track if we have unvoted or not.
Stephen Boyd163f1c32012-06-29 13:20:20 -0700117 *
118 * This struct contains data for a pil_desc that should not be exposed outside
119 * of this file. This structure points to the descriptor and the descriptor
120 * points to this structure so that PIL drivers can't access the private
121 * data of a descriptor but this file can access both.
122 */
123struct pil_priv {
Stephen Boyd36974ec2012-03-22 01:30:59 -0700124 struct delayed_work proxy;
125 struct wake_lock wlock;
Stephen Boyd163f1c32012-06-29 13:20:20 -0700126 char wname[32];
127 struct pil_desc *desc;
Stephen Boydedff6cf2012-07-11 19:39:27 -0700128 struct list_head segs;
Stephen Boyd3030c252012-08-08 17:24:05 -0700129 phys_addr_t entry_addr;
Stephen Boyd2db158c2012-07-26 21:47:17 -0700130 phys_addr_t base_addr;
Stephen Boyd379e7332012-08-08 18:04:21 -0700131 phys_addr_t region_start;
132 phys_addr_t region_end;
Stephen Boyd2db158c2012-07-26 21:47:17 -0700133 struct ion_handle *region;
Stephen Boydb455f322012-11-27 19:00:01 -0800134 struct pil_image_info __iomem *info;
135 int id;
Seemanta Dutta9907e2a2013-05-16 19:57:09 -0700136 int unvoted_flag;
Stephen Boyd3f4da322011-08-30 01:03:23 -0700137};
138
Stephen Boyd10667772012-11-28 16:45:35 -0800139/**
140 * pil_do_ramdump() - Ramdump an image
141 * @desc: descriptor from pil_desc_init()
142 * @ramdump_dev: ramdump device returned from create_ramdump_device()
143 *
144 * Calls the ramdump API with a list of segments generated from the addresses
145 * that the descriptor corresponds to.
146 */
147int pil_do_ramdump(struct pil_desc *desc, void *ramdump_dev)
148{
149 struct pil_priv *priv = desc->priv;
150 struct pil_seg *seg;
151 int count = 0, ret;
152 struct ramdump_segment *ramdump_segs, *s;
153
154 list_for_each_entry(seg, &priv->segs, list)
155 count++;
156
157 ramdump_segs = kmalloc_array(count, sizeof(*ramdump_segs), GFP_KERNEL);
158 if (!ramdump_segs)
159 return -ENOMEM;
160
161 s = ramdump_segs;
162 list_for_each_entry(seg, &priv->segs, list) {
163 s->address = seg->paddr;
164 s->size = seg->sz;
165 s++;
166 }
167
168 ret = do_elf_ramdump(ramdump_dev, ramdump_segs, count);
169 kfree(ramdump_segs);
170
171 return ret;
172}
173EXPORT_SYMBOL(pil_do_ramdump);
174
Stephen Boyd2db158c2012-07-26 21:47:17 -0700175static struct ion_client *ion;
176
Stephen Boyd3030c252012-08-08 17:24:05 -0700177/**
178 * pil_get_entry_addr() - Retrieve the entry address of a peripheral image
179 * @desc: descriptor from pil_desc_init()
180 *
181 * Returns the physical address where the image boots at or 0 if unknown.
182 */
183phys_addr_t pil_get_entry_addr(struct pil_desc *desc)
184{
185 return desc->priv ? desc->priv->entry_addr : 0;
186}
187EXPORT_SYMBOL(pil_get_entry_addr);
188
Seemanta Dutta9907e2a2013-05-16 19:57:09 -0700189static void __pil_proxy_unvote(struct pil_priv *priv)
Stephen Boyd36974ec2012-03-22 01:30:59 -0700190{
Stephen Boyd163f1c32012-06-29 13:20:20 -0700191 struct pil_desc *desc = priv->desc;
Stephen Boyd36974ec2012-03-22 01:30:59 -0700192
Stephen Boyd163f1c32012-06-29 13:20:20 -0700193 desc->ops->proxy_unvote(desc);
194 wake_unlock(&priv->wlock);
195 module_put(desc->owner);
Seemanta Dutta9907e2a2013-05-16 19:57:09 -0700196
197}
198
199static void pil_proxy_unvote_work(struct work_struct *work)
200{
201 struct delayed_work *delayed = to_delayed_work(work);
202 struct pil_priv *priv = container_of(delayed, struct pil_priv, proxy);
203 __pil_proxy_unvote(priv);
Stephen Boyd36974ec2012-03-22 01:30:59 -0700204}
205
Stephen Boyd163f1c32012-06-29 13:20:20 -0700206static int pil_proxy_vote(struct pil_desc *desc)
Stephen Boyd36974ec2012-03-22 01:30:59 -0700207{
Stephen Boyd0a563142012-07-02 13:33:31 -0700208 int ret = 0;
Stephen Boyd163f1c32012-06-29 13:20:20 -0700209 struct pil_priv *priv = desc->priv;
Stephen Boyd0a563142012-07-02 13:33:31 -0700210
Stephen Boyd163f1c32012-06-29 13:20:20 -0700211 if (desc->ops->proxy_vote) {
212 wake_lock(&priv->wlock);
213 ret = desc->ops->proxy_vote(desc);
Stephen Boyd0a563142012-07-02 13:33:31 -0700214 if (ret)
Stephen Boyd163f1c32012-06-29 13:20:20 -0700215 wake_unlock(&priv->wlock);
Stephen Boyd36974ec2012-03-22 01:30:59 -0700216 }
Seemanta Dutta9907e2a2013-05-16 19:57:09 -0700217
218 if (desc->proxy_unvote_irq)
219 enable_irq(desc->proxy_unvote_irq);
220
Stephen Boyd0a563142012-07-02 13:33:31 -0700221 return ret;
Stephen Boyd36974ec2012-03-22 01:30:59 -0700222}
223
Seemanta Dutta78f43192013-03-04 18:29:43 -0800224static void pil_proxy_unvote(struct pil_desc *desc, int immediate)
Stephen Boyd36974ec2012-03-22 01:30:59 -0700225{
Stephen Boyd163f1c32012-06-29 13:20:20 -0700226 struct pil_priv *priv = desc->priv;
Seemanta Dutta78f43192013-03-04 18:29:43 -0800227 unsigned long timeout;
Stephen Boyd163f1c32012-06-29 13:20:20 -0700228
Seemanta Dutta78f43192013-03-04 18:29:43 -0800229 if (proxy_timeout_ms == 0 && !immediate)
230 return;
231 else if (proxy_timeout_ms > 0)
Matt Wagantall0aafa352012-05-14 16:40:41 -0700232 timeout = proxy_timeout_ms;
Seemanta Dutta78f43192013-03-04 18:29:43 -0800233 else
234 timeout = desc->proxy_timeout;
Matt Wagantall0aafa352012-05-14 16:40:41 -0700235
Seemanta Dutta78f43192013-03-04 18:29:43 -0800236 if (desc->ops->proxy_unvote) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700237 if (WARN_ON(!try_module_get(desc->owner)))
238 return;
Seemanta Dutta78f43192013-03-04 18:29:43 -0800239
240 if (immediate)
241 timeout = 0;
Seemanta Duttad21a7972013-03-05 12:16:17 -0800242
243 if (!desc->proxy_unvote_irq || immediate)
244 schedule_delayed_work(&priv->proxy,
245 msecs_to_jiffies(timeout));
Stephen Boyd163f1c32012-06-29 13:20:20 -0700246 }
Stephen Boyd36974ec2012-03-22 01:30:59 -0700247}
248
Seemanta Duttad21a7972013-03-05 12:16:17 -0800249static irqreturn_t proxy_unvote_intr_handler(int irq, void *dev_id)
250{
251 struct pil_desc *desc = dev_id;
Seemanta Dutta9907e2a2013-05-16 19:57:09 -0700252 struct pil_priv *priv = desc->priv;
Seemanta Duttad21a7972013-03-05 12:16:17 -0800253
Seemanta Dutta9907e2a2013-05-16 19:57:09 -0700254 if (!desc->priv->unvoted_flag) {
255 desc->priv->unvoted_flag = 1;
256 __pil_proxy_unvote(priv);
257 }
258
Seemanta Duttad21a7972013-03-05 12:16:17 -0800259 return IRQ_HANDLED;
260}
261
Stephen Boyd2db158c2012-07-26 21:47:17 -0700262static bool segment_is_relocatable(const struct elf32_phdr *p)
263{
264 return !!(p->p_flags & BIT(27));
265}
266
267static phys_addr_t pil_reloc(const struct pil_priv *priv, phys_addr_t addr)
268{
269 return addr - priv->base_addr + priv->region_start;
270}
271
Stephen Boydedff6cf2012-07-11 19:39:27 -0700272static struct pil_seg *pil_init_seg(const struct pil_desc *desc,
273 const struct elf32_phdr *phdr, int num)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700274{
Stephen Boyd2db158c2012-07-26 21:47:17 -0700275 bool reloc = segment_is_relocatable(phdr);
276 const struct pil_priv *priv = desc->priv;
Stephen Boydedff6cf2012-07-11 19:39:27 -0700277 struct pil_seg *seg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700278
Stephen Boyd2db158c2012-07-26 21:47:17 -0700279 if (!reloc && memblock_overlaps_memory(phdr->p_paddr, phdr->p_memsz)) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700280 pil_err(desc, "kernel memory would be overwritten [%#08lx, %#08lx)\n",
Stephen Boydf79af532012-05-14 18:57:26 -0700281 (unsigned long)phdr->p_paddr,
282 (unsigned long)(phdr->p_paddr + phdr->p_memsz));
Stephen Boydedff6cf2012-07-11 19:39:27 -0700283 return ERR_PTR(-EPERM);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700284 }
285
Stephen Boydedff6cf2012-07-11 19:39:27 -0700286 seg = kmalloc(sizeof(*seg), GFP_KERNEL);
287 if (!seg)
288 return ERR_PTR(-ENOMEM);
289 seg->num = num;
Stephen Boyd2db158c2012-07-26 21:47:17 -0700290 seg->paddr = reloc ? pil_reloc(priv, phdr->p_paddr) : phdr->p_paddr;
Stephen Boydedff6cf2012-07-11 19:39:27 -0700291 seg->filesz = phdr->p_filesz;
292 seg->sz = phdr->p_memsz;
Stephen Boyd2db158c2012-07-26 21:47:17 -0700293 seg->relocated = reloc;
Stephen Boydedff6cf2012-07-11 19:39:27 -0700294 INIT_LIST_HEAD(&seg->list);
295
296 return seg;
297}
298
299#define segment_is_hash(flag) (((flag) & (0x7 << 24)) == (0x2 << 24))
300
301static int segment_is_loadable(const struct elf32_phdr *p)
302{
Stephen Boyd2db158c2012-07-26 21:47:17 -0700303 return (p->p_type == PT_LOAD) && !segment_is_hash(p->p_flags) &&
304 p->p_memsz;
Stephen Boydedff6cf2012-07-11 19:39:27 -0700305}
306
Stephen Boyd3030c252012-08-08 17:24:05 -0700307static void pil_dump_segs(const struct pil_priv *priv)
308{
309 struct pil_seg *seg;
Tianyi Gou74a93992013-04-10 19:58:21 -0700310 phys_addr_t seg_h_paddr;
Stephen Boyd3030c252012-08-08 17:24:05 -0700311
312 list_for_each_entry(seg, &priv->segs, list) {
Tianyi Gou74a93992013-04-10 19:58:21 -0700313 seg_h_paddr = seg->paddr + seg->sz;
314 pil_info(priv->desc, "%d: %pa %pa\n", seg->num,
315 &seg->paddr, &seg_h_paddr);
Stephen Boyd3030c252012-08-08 17:24:05 -0700316 }
317}
318
319/*
Stephen Boyd2db158c2012-07-26 21:47:17 -0700320 * Ensure the entry address lies within the image limits and if the image is
321 * relocatable ensure it lies within a relocatable segment.
Stephen Boyd3030c252012-08-08 17:24:05 -0700322 */
323static int pil_init_entry_addr(struct pil_priv *priv, const struct pil_mdt *mdt)
324{
325 struct pil_seg *seg;
Stephen Boyd2db158c2012-07-26 21:47:17 -0700326 phys_addr_t entry = mdt->hdr.e_entry;
327 bool image_relocated = priv->region;
Stephen Boyd3030c252012-08-08 17:24:05 -0700328
Stephen Boyd2db158c2012-07-26 21:47:17 -0700329 if (image_relocated)
330 entry = pil_reloc(priv, entry);
331 priv->entry_addr = entry;
Stephen Boyd3030c252012-08-08 17:24:05 -0700332
333 if (priv->desc->flags & PIL_SKIP_ENTRY_CHECK)
334 return 0;
335
336 list_for_each_entry(seg, &priv->segs, list) {
Stephen Boyd2db158c2012-07-26 21:47:17 -0700337 if (entry >= seg->paddr && entry < seg->paddr + seg->sz) {
338 if (!image_relocated)
339 return 0;
340 else if (seg->relocated)
341 return 0;
342 }
Stephen Boyd3030c252012-08-08 17:24:05 -0700343 }
Tianyi Gou74a93992013-04-10 19:58:21 -0700344 pil_err(priv->desc, "entry address %pa not within range\n", &entry);
Stephen Boyd3030c252012-08-08 17:24:05 -0700345 pil_dump_segs(priv);
346 return -EADDRNOTAVAIL;
347}
348
Stephen Boyd2db158c2012-07-26 21:47:17 -0700349static int pil_alloc_region(struct pil_priv *priv, phys_addr_t min_addr,
350 phys_addr_t max_addr, size_t align)
351{
352 struct ion_handle *region;
353 int ret;
354 unsigned int mask;
Stephen Boyd6385e312012-12-12 11:17:04 -0800355 size_t size = max_addr - min_addr;
Stephen Boyd2db158c2012-07-26 21:47:17 -0700356
Stephen Boyd87c7bfa2013-03-20 18:19:13 -0700357 /* Don't reallocate due to fragmentation concerns, just sanity check */
358 if (priv->region) {
359 if (WARN(priv->region_end - priv->region_start < size,
360 "Can't reuse PIL memory, too small\n"))
361 return -ENOMEM;
362 return 0;
363 }
364
Stephen Boyd2db158c2012-07-26 21:47:17 -0700365 if (!ion) {
366 WARN_ON_ONCE("No ION client, can't support relocation\n");
367 return -ENOMEM;
368 }
369
370 /* Force alignment due to linker scripts not getting it right */
371 if (align > SZ_1M) {
372 mask = ION_HEAP(ION_PIL2_HEAP_ID);
373 align = SZ_4M;
374 } else {
375 mask = ION_HEAP(ION_PIL1_HEAP_ID);
376 align = SZ_1M;
377 }
378
379 region = ion_alloc(ion, size, align, mask, 0);
380 if (IS_ERR(region)) {
381 pil_err(priv->desc, "Failed to allocate relocatable region\n");
382 return PTR_ERR(region);
383 }
384
385 ret = ion_phys(ion, region, (ion_phys_addr_t *)&priv->region_start,
386 &size);
387 if (ret) {
388 ion_free(ion, region);
389 return ret;
390 }
391
392 priv->region = region;
393 priv->region_end = priv->region_start + size;
394 priv->base_addr = min_addr;
395
396 return 0;
397}
398
Stephen Boyd379e7332012-08-08 18:04:21 -0700399static int pil_setup_region(struct pil_priv *priv, const struct pil_mdt *mdt)
400{
401 const struct elf32_phdr *phdr;
Stephen Boyd2db158c2012-07-26 21:47:17 -0700402 phys_addr_t min_addr_r, min_addr_n, max_addr_r, max_addr_n, start, end;
403 size_t align = 0;
404 int i, ret = 0;
405 bool relocatable = false;
Stephen Boyd379e7332012-08-08 18:04:21 -0700406
Stephen Boyd2db158c2012-07-26 21:47:17 -0700407 min_addr_n = min_addr_r = (phys_addr_t)ULLONG_MAX;
408 max_addr_n = max_addr_r = 0;
Stephen Boyd379e7332012-08-08 18:04:21 -0700409
410 /* Find the image limits */
411 for (i = 0; i < mdt->hdr.e_phnum; i++) {
412 phdr = &mdt->phdr[i];
413 if (!segment_is_loadable(phdr))
414 continue;
415
Stephen Boyd2db158c2012-07-26 21:47:17 -0700416 start = phdr->p_paddr;
417 end = start + phdr->p_memsz;
418
419 if (segment_is_relocatable(phdr)) {
420 min_addr_r = min(min_addr_r, start);
421 max_addr_r = max(max_addr_r, end);
422 /*
423 * Lowest relocatable segment dictates alignment of
424 * relocatable region
425 */
426 if (min_addr_r == start)
427 align = phdr->p_align;
428 relocatable = true;
429 } else {
430 min_addr_n = min(min_addr_n, start);
431 max_addr_n = max(max_addr_n, end);
432 }
433
Stephen Boyd379e7332012-08-08 18:04:21 -0700434 }
435
Stephen Boyd6385e312012-12-12 11:17:04 -0800436 /*
437 * Align the max address to the next 4K boundary to satisfy iommus and
438 * XPUs that operate on 4K chunks.
439 */
440 max_addr_n = ALIGN(max_addr_n, SZ_4K);
441 max_addr_r = ALIGN(max_addr_r, SZ_4K);
442
Stephen Boyd2db158c2012-07-26 21:47:17 -0700443 if (relocatable) {
444 ret = pil_alloc_region(priv, min_addr_r, max_addr_r, align);
445 } else {
446 priv->region_start = min_addr_n;
447 priv->region_end = max_addr_n;
448 priv->base_addr = min_addr_n;
449 }
Stephen Boyd379e7332012-08-08 18:04:21 -0700450
Stephen Boydb455f322012-11-27 19:00:01 -0800451 writeq(priv->region_start, &priv->info->start);
452 writel_relaxed(priv->region_end - priv->region_start,
453 &priv->info->size);
454
Stephen Boyd2db158c2012-07-26 21:47:17 -0700455 return ret;
Stephen Boyd379e7332012-08-08 18:04:21 -0700456}
457
Stephen Boydedff6cf2012-07-11 19:39:27 -0700458static int pil_cmp_seg(void *priv, struct list_head *a, struct list_head *b)
459{
460 struct pil_seg *seg_a = list_entry(a, struct pil_seg, list);
461 struct pil_seg *seg_b = list_entry(b, struct pil_seg, list);
462
463 return seg_a->paddr - seg_b->paddr;
464}
465
466static int pil_init_mmap(struct pil_desc *desc, const struct pil_mdt *mdt)
467{
Stephen Boyd3030c252012-08-08 17:24:05 -0700468 struct pil_priv *priv = desc->priv;
Stephen Boydedff6cf2012-07-11 19:39:27 -0700469 const struct elf32_phdr *phdr;
470 struct pil_seg *seg;
Stephen Boyd379e7332012-08-08 18:04:21 -0700471 int i, ret;
472
473 ret = pil_setup_region(priv, mdt);
474 if (ret)
475 return ret;
Stephen Boydedff6cf2012-07-11 19:39:27 -0700476
477 for (i = 0; i < mdt->hdr.e_phnum; i++) {
478 phdr = &mdt->phdr[i];
479 if (!segment_is_loadable(phdr))
480 continue;
481
482 seg = pil_init_seg(desc, phdr, i);
483 if (IS_ERR(seg))
484 return PTR_ERR(seg);
485
486 list_add_tail(&seg->list, &priv->segs);
487 }
488 list_sort(NULL, &priv->segs, pil_cmp_seg);
489
Stephen Boyd3030c252012-08-08 17:24:05 -0700490 return pil_init_entry_addr(priv, mdt);
Stephen Boydedff6cf2012-07-11 19:39:27 -0700491}
492
493static void pil_release_mmap(struct pil_desc *desc)
494{
495 struct pil_priv *priv = desc->priv;
496 struct pil_seg *p, *tmp;
497
Stephen Boydb455f322012-11-27 19:00:01 -0800498 writeq(0, &priv->info->start);
499 writel_relaxed(0, &priv->info->size);
500
Stephen Boydedff6cf2012-07-11 19:39:27 -0700501 list_for_each_entry_safe(p, tmp, &priv->segs, list) {
502 list_del(&p->list);
503 kfree(p);
504 }
505}
506
507#define IOMAP_SIZE SZ_4M
508
509static int pil_load_seg(struct pil_desc *desc, struct pil_seg *seg)
510{
Tianyi Gou74a93992013-04-10 19:58:21 -0700511 int ret = 0, count;
512 phys_addr_t paddr;
Stephen Boydedff6cf2012-07-11 19:39:27 -0700513 char fw_name[30];
514 const struct firmware *fw = NULL;
515 const u8 *data;
516 int num = seg->num;
517
518 if (seg->filesz) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700519 snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d",
Stephen Boyd163f1c32012-06-29 13:20:20 -0700520 desc->name, num);
521 ret = request_firmware(&fw, fw_name, desc->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700522 if (ret) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700523 pil_err(desc, "Failed to locate blob %s\n", fw_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700524 return ret;
525 }
526
Stephen Boydedff6cf2012-07-11 19:39:27 -0700527 if (fw->size != seg->filesz) {
528 pil_err(desc, "Blob size %u doesn't match %lu\n",
529 fw->size, seg->filesz);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700530 ret = -EPERM;
531 goto release_fw;
532 }
533 }
534
535 /* Load the segment into memory */
Stephen Boydedff6cf2012-07-11 19:39:27 -0700536 count = seg->filesz;
537 paddr = seg->paddr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700538 data = fw ? fw->data : NULL;
539 while (count > 0) {
540 int size;
541 u8 __iomem *buf;
542
543 size = min_t(size_t, IOMAP_SIZE, count);
544 buf = ioremap(paddr, size);
545 if (!buf) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700546 pil_err(desc, "Failed to map memory\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700547 ret = -ENOMEM;
548 goto release_fw;
549 }
550 memcpy(buf, data, size);
551 iounmap(buf);
552
553 count -= size;
554 paddr += size;
555 data += size;
556 }
557
558 /* Zero out trailing memory */
Stephen Boydedff6cf2012-07-11 19:39:27 -0700559 count = seg->sz - seg->filesz;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700560 while (count > 0) {
561 int size;
562 u8 __iomem *buf;
563
564 size = min_t(size_t, IOMAP_SIZE, count);
565 buf = ioremap(paddr, size);
566 if (!buf) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700567 pil_err(desc, "Failed to map memory\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700568 ret = -ENOMEM;
569 goto release_fw;
570 }
571 memset(buf, 0, size);
572 iounmap(buf);
573
574 count -= size;
575 paddr += size;
576 }
577
Stephen Boyd163f1c32012-06-29 13:20:20 -0700578 if (desc->ops->verify_blob) {
Stephen Boydedff6cf2012-07-11 19:39:27 -0700579 ret = desc->ops->verify_blob(desc, seg->paddr, seg->sz);
Stephen Boydb0f1f802012-02-03 11:28:08 -0800580 if (ret)
Stephen Boyd163f1c32012-06-29 13:20:20 -0700581 pil_err(desc, "Blob%u failed verification\n", num);
Stephen Boydb0f1f802012-02-03 11:28:08 -0800582 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700583
584release_fw:
585 release_firmware(fw);
586 return ret;
587}
588
Stephen Boyd163f1c32012-06-29 13:20:20 -0700589/* Synchronize request_firmware() with suspend */
Stephen Boyd80bde032012-03-16 00:14:42 -0700590static DECLARE_RWSEM(pil_pm_rwsem);
591
Stephen Boyd163f1c32012-06-29 13:20:20 -0700592/**
593 * pil_boot() - Load a peripheral image into memory and boot it
594 * @desc: descriptor from pil_desc_init()
595 *
596 * Returns 0 on success or -ERROR on failure.
597 */
598int pil_boot(struct pil_desc *desc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700599{
Stephen Boydedff6cf2012-07-11 19:39:27 -0700600 int ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700601 char fw_name[30];
Stephen Boydedff6cf2012-07-11 19:39:27 -0700602 const struct pil_mdt *mdt;
603 const struct elf32_hdr *ehdr;
604 struct pil_seg *seg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700605 const struct firmware *fw;
Stephen Boyd379e7332012-08-08 18:04:21 -0700606 struct pil_priv *priv = desc->priv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700607
Stephen Boydedff6cf2012-07-11 19:39:27 -0700608 /* Reinitialize for new image */
609 pil_release_mmap(desc);
610
Stephen Boyd80bde032012-03-16 00:14:42 -0700611 down_read(&pil_pm_rwsem);
Stephen Boyd163f1c32012-06-29 13:20:20 -0700612 snprintf(fw_name, sizeof(fw_name), "%s.mdt", desc->name);
613 ret = request_firmware(&fw, fw_name, desc->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700614 if (ret) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700615 pil_err(desc, "Failed to locate %s\n", fw_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700616 goto out;
617 }
618
619 if (fw->size < sizeof(*ehdr)) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700620 pil_err(desc, "Not big enough to be an elf header\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700621 ret = -EIO;
622 goto release_fw;
623 }
624
Stephen Boydedff6cf2012-07-11 19:39:27 -0700625 mdt = (const struct pil_mdt *)fw->data;
626 ehdr = &mdt->hdr;
627
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700628 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700629 pil_err(desc, "Not an elf header\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700630 ret = -EIO;
631 goto release_fw;
632 }
633
634 if (ehdr->e_phnum == 0) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700635 pil_err(desc, "No loadable segments\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700636 ret = -EIO;
637 goto release_fw;
638 }
Stephen Boyd96a9f902011-07-18 18:43:00 -0700639 if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
640 sizeof(struct elf32_hdr) > fw->size) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700641 pil_err(desc, "Program headers not within mdt\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700642 ret = -EIO;
643 goto release_fw;
644 }
645
Stephen Boydedff6cf2012-07-11 19:39:27 -0700646 ret = pil_init_mmap(desc, mdt);
647 if (ret)
648 goto release_fw;
649
Stephen Boyd3030c252012-08-08 17:24:05 -0700650 if (desc->ops->init_image)
651 ret = desc->ops->init_image(desc, fw->data, fw->size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700652 if (ret) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700653 pil_err(desc, "Invalid firmware metadata\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700654 goto release_fw;
655 }
656
Stephen Boyd379e7332012-08-08 18:04:21 -0700657 if (desc->ops->mem_setup)
658 ret = desc->ops->mem_setup(desc, priv->region_start,
659 priv->region_end - priv->region_start);
660 if (ret) {
661 pil_err(desc, "Memory setup error\n");
662 goto release_fw;
663 }
664
Stephen Boydedff6cf2012-07-11 19:39:27 -0700665 list_for_each_entry(seg, &desc->priv->segs, list) {
666 ret = pil_load_seg(desc, seg);
667 if (ret)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700668 goto release_fw;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700669 }
670
Seemanta Dutta9907e2a2013-05-16 19:57:09 -0700671 desc->priv->unvoted_flag = 0;
Stephen Boyd163f1c32012-06-29 13:20:20 -0700672 ret = pil_proxy_vote(desc);
Stephen Boyd36974ec2012-03-22 01:30:59 -0700673 if (ret) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700674 pil_err(desc, "Failed to proxy vote\n");
Stephen Boyd36974ec2012-03-22 01:30:59 -0700675 goto release_fw;
676 }
677
Stephen Boyd163f1c32012-06-29 13:20:20 -0700678 ret = desc->ops->auth_and_reset(desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700679 if (ret) {
Stephen Boyd163f1c32012-06-29 13:20:20 -0700680 pil_err(desc, "Failed to bring out of reset\n");
Stephen Boyd36974ec2012-03-22 01:30:59 -0700681 goto err_boot;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700682 }
Stephen Boyd163f1c32012-06-29 13:20:20 -0700683 pil_info(desc, "Brought out of reset\n");
Stephen Boyd36974ec2012-03-22 01:30:59 -0700684err_boot:
Seemanta Dutta78f43192013-03-04 18:29:43 -0800685 pil_proxy_unvote(desc, ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700686release_fw:
687 release_firmware(fw);
688out:
Stephen Boyd80bde032012-03-16 00:14:42 -0700689 up_read(&pil_pm_rwsem);
Stephen Boyd87c7bfa2013-03-20 18:19:13 -0700690 if (ret) {
691 if (priv->region) {
692 ion_free(ion, priv->region);
693 priv->region = NULL;
694 }
Stephen Boydedff6cf2012-07-11 19:39:27 -0700695 pil_release_mmap(desc);
Stephen Boyd87c7bfa2013-03-20 18:19:13 -0700696 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700697 return ret;
698}
Stephen Boyd163f1c32012-06-29 13:20:20 -0700699EXPORT_SYMBOL(pil_boot);
700
Stephen Boyd163f1c32012-06-29 13:20:20 -0700701/**
702 * pil_shutdown() - Shutdown a peripheral
703 * @desc: descriptor from pil_desc_init()
704 */
705void pil_shutdown(struct pil_desc *desc)
Stephen Boyd36974ec2012-03-22 01:30:59 -0700706{
Stephen Boyd163f1c32012-06-29 13:20:20 -0700707 struct pil_priv *priv = desc->priv;
Seemanta Dutta9907e2a2013-05-16 19:57:09 -0700708
Matt Wagantall19b48592013-02-27 10:18:41 -0800709 if (desc->ops->shutdown)
710 desc->ops->shutdown(desc);
Seemanta Dutta9907e2a2013-05-16 19:57:09 -0700711
712 if (desc->proxy_unvote_irq) {
713 disable_irq(desc->proxy_unvote_irq);
714 if (!desc->priv->unvoted_flag)
715 pil_proxy_unvote(desc, 1);
716 return;
717 }
718
719 if (!proxy_timeout_ms)
720 pil_proxy_unvote(desc, 1);
Matt Wagantall0aafa352012-05-14 16:40:41 -0700721 else
Stephen Boyd163f1c32012-06-29 13:20:20 -0700722 flush_delayed_work(&priv->proxy);
723}
724EXPORT_SYMBOL(pil_shutdown);
Matt Wagantall0aafa352012-05-14 16:40:41 -0700725
Stephen Boydb455f322012-11-27 19:00:01 -0800726static DEFINE_IDA(pil_ida);
727
Stephen Boyd163f1c32012-06-29 13:20:20 -0700728/**
729 * pil_desc_init() - Initialize a pil descriptor
730 * @desc: descriptor to intialize
731 *
732 * Initialize a pil descriptor for use by other pil functions. This function
733 * must be called before calling pil_boot() or pil_shutdown().
734 *
735 * Returns 0 for success and -ERROR on failure.
736 */
737int pil_desc_init(struct pil_desc *desc)
738{
739 struct pil_priv *priv;
Seemanta Duttad21a7972013-03-05 12:16:17 -0800740 int ret;
Stephen Boydb455f322012-11-27 19:00:01 -0800741 void __iomem *addr;
Stephen Boyd06ce3962013-01-02 15:03:14 -0800742 char buf[sizeof(priv->info->name)];
Stephen Boyd163f1c32012-06-29 13:20:20 -0700743
744 /* Ignore users who don't make any sense */
Seemanta Duttad21a7972013-03-05 12:16:17 -0800745 WARN(desc->ops->proxy_unvote && desc->proxy_unvote_irq == 0
746 && !desc->proxy_timeout,
747 "Invalid proxy unvote callback or a proxy timeout of 0"
748 " was specified or no proxy unvote IRQ was specified.\n");
749
Stephen Boyd163f1c32012-06-29 13:20:20 -0700750 if (WARN(desc->ops->proxy_unvote && !desc->ops->proxy_vote,
751 "Invalid proxy voting. Ignoring\n"))
752 ((struct pil_reset_ops *)desc->ops)->proxy_unvote = NULL;
753
754 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
755 if (!priv)
756 return -ENOMEM;
757 desc->priv = priv;
758 priv->desc = desc;
759
Seemanta Duttad21a7972013-03-05 12:16:17 -0800760 priv->id = ret = ida_simple_get(&pil_ida, 0, 10, GFP_KERNEL);
761 if (priv->id < 0)
762 goto err;
763
764 addr = PIL_IMAGE_INFO_BASE + sizeof(struct pil_image_info) * priv->id;
Stephen Boydb455f322012-11-27 19:00:01 -0800765 priv->info = (struct pil_image_info __iomem *)addr;
766
Stephen Boyd06ce3962013-01-02 15:03:14 -0800767 strncpy(buf, desc->name, sizeof(buf));
768 __iowrite32_copy(priv->info->name, buf, sizeof(buf) / 4);
Stephen Boydb455f322012-11-27 19:00:01 -0800769
Seemanta Duttad21a7972013-03-05 12:16:17 -0800770 if (desc->proxy_unvote_irq > 0) {
Seemanta Dutta9907e2a2013-05-16 19:57:09 -0700771 ret = request_threaded_irq(desc->proxy_unvote_irq,
772 NULL,
Seemanta Duttad21a7972013-03-05 12:16:17 -0800773 proxy_unvote_intr_handler,
774 IRQF_TRIGGER_RISING|IRQF_SHARED,
775 desc->name, desc);
776 if (ret < 0) {
777 dev_err(desc->dev,
778 "Unable to request proxy unvote IRQ: %d\n",
779 ret);
780 goto err;
781 }
Seemanta Dutta9907e2a2013-05-16 19:57:09 -0700782 disable_irq(desc->proxy_unvote_irq);
Seemanta Duttad21a7972013-03-05 12:16:17 -0800783 }
784
Stephen Boyd163f1c32012-06-29 13:20:20 -0700785 snprintf(priv->wname, sizeof(priv->wname), "pil-%s", desc->name);
786 wake_lock_init(&priv->wlock, WAKE_LOCK_SUSPEND, priv->wname);
Seemanta Dutta9907e2a2013-05-16 19:57:09 -0700787 INIT_DELAYED_WORK(&priv->proxy, pil_proxy_unvote_work);
Stephen Boydedff6cf2012-07-11 19:39:27 -0700788 INIT_LIST_HEAD(&priv->segs);
Stephen Boyd163f1c32012-06-29 13:20:20 -0700789
790 return 0;
Seemanta Duttad21a7972013-03-05 12:16:17 -0800791err:
792 kfree(priv);
793 return ret;
Stephen Boyd163f1c32012-06-29 13:20:20 -0700794}
795EXPORT_SYMBOL(pil_desc_init);
796
797/**
798 * pil_desc_release() - Release a pil descriptor
799 * @desc: descriptor to free
800 */
801void pil_desc_release(struct pil_desc *desc)
802{
803 struct pil_priv *priv = desc->priv;
804
805 if (priv) {
Stephen Boydb455f322012-11-27 19:00:01 -0800806 ida_simple_remove(&pil_ida, priv->id);
Stephen Boyd163f1c32012-06-29 13:20:20 -0700807 flush_delayed_work(&priv->proxy);
808 wake_lock_destroy(&priv->wlock);
809 }
810 desc->priv = NULL;
811 kfree(priv);
812}
813EXPORT_SYMBOL(pil_desc_release);
814
Stephen Boyd80bde032012-03-16 00:14:42 -0700815static int pil_pm_notify(struct notifier_block *b, unsigned long event, void *p)
816{
817 switch (event) {
818 case PM_SUSPEND_PREPARE:
819 down_write(&pil_pm_rwsem);
820 break;
821 case PM_POST_SUSPEND:
822 up_write(&pil_pm_rwsem);
823 break;
824 }
825 return NOTIFY_DONE;
826}
827
828static struct notifier_block pil_pm_notifier = {
829 .notifier_call = pil_pm_notify,
830};
831
Stephen Boyd6d67d252011-09-27 11:50:05 -0700832static int __init msm_pil_init(void)
833{
Stephen Boyd2db158c2012-07-26 21:47:17 -0700834 ion = msm_ion_client_create(UINT_MAX, "pil");
835 if (IS_ERR(ion)) /* Can't support relocatable images */
836 ion = NULL;
Stephen Boyd04148122012-06-29 18:18:12 -0700837 return register_pm_notifier(&pil_pm_notifier);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700838}
Stephen Boyd2db158c2012-07-26 21:47:17 -0700839device_initcall(msm_pil_init);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700840
841static void __exit msm_pil_exit(void)
842{
Stephen Boyd80bde032012-03-16 00:14:42 -0700843 unregister_pm_notifier(&pil_pm_notifier);
Stephen Boyd2db158c2012-07-26 21:47:17 -0700844 if (ion)
845 ion_client_destroy(ion);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700846}
847module_exit(msm_pil_exit);
848
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700849MODULE_LICENSE("GPL v2");
850MODULE_DESCRIPTION("Load peripheral images and bring peripherals out of reset");