blob: d0e45f794accae6dd1bf3e9fa1917375ecc4dbab [file] [log] [blame]
Matt Wagantall39f90cb2012-02-08 14:09:11 -08001/* Copyright (c) 2010-2012, Code Aurora Forum. 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>
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>
Stephen Boyd6d67d252011-09-27 11:50:05 -070023#include <linux/atomic.h>
Stephen Boyd80bde032012-03-16 00:14:42 -070024#include <linux/suspend.h>
25#include <linux/rwsem.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070026
27#include <asm/uaccess.h>
28#include <asm/setup.h>
Stephen Boyd6d67d252011-09-27 11:50:05 -070029#include <mach/peripheral-loader.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030
31#include "peripheral-loader.h"
32
Stephen Boyd3f4da322011-08-30 01:03:23 -070033struct pil_device {
34 struct pil_desc *desc;
35 int count;
36 struct mutex lock;
Stephen Boyd6d67d252011-09-27 11:50:05 -070037 struct device dev;
38 struct module *owner;
39#ifdef CONFIG_DEBUG_FS
40 struct dentry *dentry;
41#endif
Stephen Boyd3f4da322011-08-30 01:03:23 -070042};
43
Stephen Boyd6d67d252011-09-27 11:50:05 -070044#define to_pil_device(d) container_of(d, struct pil_device, dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045
Stephen Boyd6d67d252011-09-27 11:50:05 -070046struct bus_type pil_bus_type = {
47 .name = "pil",
48};
49
50static int __find_peripheral(struct device *dev, void *data)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051{
Stephen Boyd6d67d252011-09-27 11:50:05 -070052 struct pil_device *pdev = to_pil_device(dev);
53 return !strncmp(pdev->desc->name, data, INT_MAX);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070054}
55
56static struct pil_device *find_peripheral(const char *str)
57{
Stephen Boyd6d67d252011-09-27 11:50:05 -070058 struct device *dev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070059
60 if (!str)
61 return NULL;
62
Stephen Boyd6d67d252011-09-27 11:50:05 -070063 dev = bus_find_device(&pil_bus_type, NULL, (void *)str,
64 __find_peripheral);
65 return dev ? to_pil_device(dev) : NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070066}
67
68#define IOMAP_SIZE SZ_4M
69
70static int load_segment(const struct elf32_phdr *phdr, unsigned num,
71 struct pil_device *pil)
72{
73 int ret, count, paddr;
74 char fw_name[30];
75 const struct firmware *fw = NULL;
76 const u8 *data;
77
78 if (memblock_is_region_memory(phdr->p_paddr, phdr->p_memsz)) {
Stephen Boyd6d67d252011-09-27 11:50:05 -070079 dev_err(&pil->dev, "Kernel memory would be overwritten");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080 return -EPERM;
81 }
82
83 if (phdr->p_filesz) {
Stephen Boyd3f4da322011-08-30 01:03:23 -070084 snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d",
85 pil->desc->name, num);
Stephen Boyd6d67d252011-09-27 11:50:05 -070086 ret = request_firmware(&fw, fw_name, &pil->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070087 if (ret) {
Stephen Boyd6d67d252011-09-27 11:50:05 -070088 dev_err(&pil->dev, "Failed to locate blob %s\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070089 fw_name);
90 return ret;
91 }
92
93 if (fw->size != phdr->p_filesz) {
Stephen Boyd6d67d252011-09-27 11:50:05 -070094 dev_err(&pil->dev, "Blob size %u doesn't match %u\n",
95 fw->size, phdr->p_filesz);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070096 ret = -EPERM;
97 goto release_fw;
98 }
99 }
100
101 /* Load the segment into memory */
102 count = phdr->p_filesz;
103 paddr = phdr->p_paddr;
104 data = fw ? fw->data : NULL;
105 while (count > 0) {
106 int size;
107 u8 __iomem *buf;
108
109 size = min_t(size_t, IOMAP_SIZE, count);
110 buf = ioremap(paddr, size);
111 if (!buf) {
Stephen Boyd6d67d252011-09-27 11:50:05 -0700112 dev_err(&pil->dev, "Failed to map memory\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700113 ret = -ENOMEM;
114 goto release_fw;
115 }
116 memcpy(buf, data, size);
117 iounmap(buf);
118
119 count -= size;
120 paddr += size;
121 data += size;
122 }
123
124 /* Zero out trailing memory */
125 count = phdr->p_memsz - phdr->p_filesz;
126 while (count > 0) {
127 int size;
128 u8 __iomem *buf;
129
130 size = min_t(size_t, IOMAP_SIZE, count);
131 buf = ioremap(paddr, size);
132 if (!buf) {
Stephen Boyd6d67d252011-09-27 11:50:05 -0700133 dev_err(&pil->dev, "Failed to map memory\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700134 ret = -ENOMEM;
135 goto release_fw;
136 }
137 memset(buf, 0, size);
138 iounmap(buf);
139
140 count -= size;
141 paddr += size;
142 }
143
Stephen Boyd3f4da322011-08-30 01:03:23 -0700144 ret = pil->desc->ops->verify_blob(pil->desc, phdr->p_paddr,
145 phdr->p_memsz);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146 if (ret)
Stephen Boyd6d67d252011-09-27 11:50:05 -0700147 dev_err(&pil->dev, "Blob %u failed verification\n", num);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700148
149release_fw:
150 release_firmware(fw);
151 return ret;
152}
153
154#define segment_is_hash(flag) (((flag) & (0x7 << 24)) == (0x2 << 24))
155
156static int segment_is_loadable(const struct elf32_phdr *p)
157{
158 return (p->p_type & PT_LOAD) && !segment_is_hash(p->p_flags);
159}
160
Stephen Boyd80bde032012-03-16 00:14:42 -0700161/* Sychronize request_firmware() with suspend */
162static DECLARE_RWSEM(pil_pm_rwsem);
163
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700164static int load_image(struct pil_device *pil)
165{
166 int i, ret;
167 char fw_name[30];
168 struct elf32_hdr *ehdr;
169 const struct elf32_phdr *phdr;
170 const struct firmware *fw;
171
Stephen Boyd80bde032012-03-16 00:14:42 -0700172 down_read(&pil_pm_rwsem);
Stephen Boyd3f4da322011-08-30 01:03:23 -0700173 snprintf(fw_name, sizeof(fw_name), "%s.mdt", pil->desc->name);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700174 ret = request_firmware(&fw, fw_name, &pil->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700175 if (ret) {
Stephen Boyd6d67d252011-09-27 11:50:05 -0700176 dev_err(&pil->dev, "Failed to locate %s\n", fw_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700177 goto out;
178 }
179
180 if (fw->size < sizeof(*ehdr)) {
Stephen Boyd6d67d252011-09-27 11:50:05 -0700181 dev_err(&pil->dev, "Not big enough to be an elf header\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700182 ret = -EIO;
183 goto release_fw;
184 }
185
186 ehdr = (struct elf32_hdr *)fw->data;
187 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
Stephen Boyd6d67d252011-09-27 11:50:05 -0700188 dev_err(&pil->dev, "Not an elf header\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189 ret = -EIO;
190 goto release_fw;
191 }
192
193 if (ehdr->e_phnum == 0) {
Stephen Boyd6d67d252011-09-27 11:50:05 -0700194 dev_err(&pil->dev, "No loadable segments\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700195 ret = -EIO;
196 goto release_fw;
197 }
Stephen Boyd96a9f902011-07-18 18:43:00 -0700198 if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
199 sizeof(struct elf32_hdr) > fw->size) {
Stephen Boyd6d67d252011-09-27 11:50:05 -0700200 dev_err(&pil->dev, "Program headers not within mdt\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700201 ret = -EIO;
202 goto release_fw;
203 }
204
Stephen Boyd3f4da322011-08-30 01:03:23 -0700205 ret = pil->desc->ops->init_image(pil->desc, fw->data, fw->size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206 if (ret) {
Stephen Boyd6d67d252011-09-27 11:50:05 -0700207 dev_err(&pil->dev, "Invalid firmware metadata\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700208 goto release_fw;
209 }
210
Stephen Boydc9753e12011-07-13 17:58:48 -0700211 phdr = (const struct elf32_phdr *)(fw->data + sizeof(struct elf32_hdr));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700212 for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
213 if (!segment_is_loadable(phdr))
214 continue;
215
216 ret = load_segment(phdr, i, pil);
217 if (ret) {
Stephen Boyd6d67d252011-09-27 11:50:05 -0700218 dev_err(&pil->dev, "Failed to load segment %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700219 i);
220 goto release_fw;
221 }
222 }
223
Stephen Boyd3f4da322011-08-30 01:03:23 -0700224 ret = pil->desc->ops->auth_and_reset(pil->desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700225 if (ret) {
Stephen Boyd6d67d252011-09-27 11:50:05 -0700226 dev_err(&pil->dev, "Failed to bring out of reset\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700227 goto release_fw;
228 }
Stephen Boyd6d67d252011-09-27 11:50:05 -0700229 dev_info(&pil->dev, "brought %s out of reset\n", pil->desc->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700230
231release_fw:
232 release_firmware(fw);
233out:
Stephen Boyd80bde032012-03-16 00:14:42 -0700234 up_read(&pil_pm_rwsem);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700235 return ret;
236}
237
238/**
239 * pil_get() - Load a peripheral into memory and take it out of reset
240 * @name: pointer to a string containing the name of the peripheral to load
241 *
242 * This function returns a pointer if it succeeds. If an error occurs an
243 * ERR_PTR is returned.
244 *
245 * If PIL is not enabled in the kernel, the value %NULL will be returned.
246 */
247void *pil_get(const char *name)
248{
249 int ret;
250 struct pil_device *pil;
251 struct pil_device *pil_d;
252 void *retval;
253
Stephen Boyd6d67d252011-09-27 11:50:05 -0700254 if (!name)
255 return NULL;
256
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700257 pil = retval = find_peripheral(name);
258 if (!pil)
259 return ERR_PTR(-ENODEV);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700260 if (!try_module_get(pil->owner)) {
261 put_device(&pil->dev);
262 return ERR_PTR(-ENODEV);
263 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700264
Stephen Boyd6d67d252011-09-27 11:50:05 -0700265 pil_d = pil_get(pil->desc->depends_on);
266 if (IS_ERR(pil_d)) {
267 retval = pil_d;
268 goto err_depends;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700269 }
270
271 mutex_lock(&pil->lock);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700272 if (!pil->count++) {
273 ret = load_image(pil);
274 if (ret) {
275 retval = ERR_PTR(ret);
276 goto err_load;
277 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700278 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700279 mutex_unlock(&pil->lock);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700280out:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700281 return retval;
Stephen Boyd6d67d252011-09-27 11:50:05 -0700282err_load:
283 mutex_unlock(&pil->lock);
284 pil_put(pil_d);
285err_depends:
286 put_device(&pil->dev);
287 module_put(pil->owner);
288 goto out;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700289}
290EXPORT_SYMBOL(pil_get);
291
292/**
293 * pil_put() - Inform PIL the peripheral no longer needs to be active
294 * @peripheral_handle: pointer from a previous call to pil_get()
295 *
296 * This doesn't imply that a peripheral is shutdown or in reset since another
297 * driver could be using the peripheral.
298 */
299void pil_put(void *peripheral_handle)
300{
Stephen Boyd6d67d252011-09-27 11:50:05 -0700301 struct pil_device *pil_d, *pil = peripheral_handle;
302
303 if (IS_ERR_OR_NULL(pil))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700304 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700305
306 mutex_lock(&pil->lock);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700307 if (WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
308 goto err_out;
309 if (!--pil->count)
Stephen Boyd3f4da322011-08-30 01:03:23 -0700310 pil->desc->ops->shutdown(pil->desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700311 mutex_unlock(&pil->lock);
312
Stephen Boyd3f4da322011-08-30 01:03:23 -0700313 pil_d = find_peripheral(pil->desc->depends_on);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700314 module_put(pil->owner);
315 if (pil_d) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700316 pil_put(pil_d);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700317 put_device(&pil_d->dev);
318 }
319 put_device(&pil->dev);
320 return;
321err_out:
322 mutex_unlock(&pil->lock);
323 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700324}
325EXPORT_SYMBOL(pil_put);
326
327void pil_force_shutdown(const char *name)
328{
329 struct pil_device *pil;
330
331 pil = find_peripheral(name);
332 if (!pil)
333 return;
334
335 mutex_lock(&pil->lock);
336 if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
Stephen Boyd3f4da322011-08-30 01:03:23 -0700337 pil->desc->ops->shutdown(pil->desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700338 mutex_unlock(&pil->lock);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700339 put_device(&pil->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700340}
341EXPORT_SYMBOL(pil_force_shutdown);
342
343int pil_force_boot(const char *name)
344{
345 int ret = -EINVAL;
346 struct pil_device *pil;
347
348 pil = find_peripheral(name);
349 if (!pil)
350 return -EINVAL;
351
352 mutex_lock(&pil->lock);
353 if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
354 ret = load_image(pil);
355 mutex_unlock(&pil->lock);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700356 put_device(&pil->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700357
358 return ret;
359}
360EXPORT_SYMBOL(pil_force_boot);
361
362#ifdef CONFIG_DEBUG_FS
Stephen Boyd6d67d252011-09-27 11:50:05 -0700363static int msm_pil_debugfs_open(struct inode *inode, struct file *filp)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700364{
365 filp->private_data = inode->i_private;
366 return 0;
367}
368
369static ssize_t msm_pil_debugfs_read(struct file *filp, char __user *ubuf,
370 size_t cnt, loff_t *ppos)
371{
372 int r;
373 char buf[40];
374 struct pil_device *pil = filp->private_data;
375
376 mutex_lock(&pil->lock);
377 r = snprintf(buf, sizeof(buf), "%d\n", pil->count);
378 mutex_unlock(&pil->lock);
379 return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
380}
381
382static ssize_t msm_pil_debugfs_write(struct file *filp,
383 const char __user *ubuf, size_t cnt, loff_t *ppos)
384{
385 struct pil_device *pil = filp->private_data;
386 char buf[4];
387
388 if (cnt > sizeof(buf))
389 return -EINVAL;
390
391 if (copy_from_user(&buf, ubuf, cnt))
392 return -EFAULT;
393
394 if (!strncmp(buf, "get", 3)) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700395 if (IS_ERR(pil_get(pil->desc->name)))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700396 return -EIO;
397 } else if (!strncmp(buf, "put", 3))
398 pil_put(pil);
399 else
400 return -EINVAL;
401
402 return cnt;
403}
404
405static const struct file_operations msm_pil_debugfs_fops = {
406 .open = msm_pil_debugfs_open,
407 .read = msm_pil_debugfs_read,
408 .write = msm_pil_debugfs_write,
409};
410
411static struct dentry *pil_base_dir;
412
Stephen Boyd6d67d252011-09-27 11:50:05 -0700413static int __init msm_pil_debugfs_init(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700414{
415 pil_base_dir = debugfs_create_dir("pil", NULL);
416 if (!pil_base_dir) {
417 pil_base_dir = NULL;
418 return -ENOMEM;
419 }
420
421 return 0;
422}
Stephen Boyd6d67d252011-09-27 11:50:05 -0700423
424static void __exit msm_pil_debugfs_exit(void)
425{
426 debugfs_remove_recursive(pil_base_dir);
427}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700428
429static int msm_pil_debugfs_add(struct pil_device *pil)
430{
431 if (!pil_base_dir)
432 return -ENOMEM;
433
Stephen Boyd6d67d252011-09-27 11:50:05 -0700434 pil->dentry = debugfs_create_file(pil->desc->name, S_IRUGO | S_IWUSR,
435 pil_base_dir, pil, &msm_pil_debugfs_fops);
436 return !pil->dentry ? -ENOMEM : 0;
437}
438
439static void msm_pil_debugfs_remove(struct pil_device *pil)
440{
441 debugfs_remove(pil->dentry);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700442}
443#else
Stephen Boyd6d67d252011-09-27 11:50:05 -0700444static int __init msm_pil_debugfs_init(void) { return 0; };
445static void __exit msm_pil_debugfs_exit(void) { return 0; };
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700446static int msm_pil_debugfs_add(struct pil_device *pil) { return 0; }
Stephen Boyd6d67d252011-09-27 11:50:05 -0700447static void msm_pil_debugfs_remove(struct pil_device *pil) { }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700448#endif
449
Stephen Boyd6d67d252011-09-27 11:50:05 -0700450static int __msm_pil_shutdown(struct device *dev, void *data)
451{
452 struct pil_device *pil = to_pil_device(dev);
453 pil->desc->ops->shutdown(pil->desc);
454 return 0;
455}
456
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700457static int msm_pil_shutdown_at_boot(void)
458{
Stephen Boyd6d67d252011-09-27 11:50:05 -0700459 return bus_for_each_dev(&pil_bus_type, NULL, NULL, __msm_pil_shutdown);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700460}
461late_initcall(msm_pil_shutdown_at_boot);
462
Stephen Boyd6d67d252011-09-27 11:50:05 -0700463static void pil_device_release(struct device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700464{
Stephen Boyd6d67d252011-09-27 11:50:05 -0700465 struct pil_device *pil = to_pil_device(dev);
466 mutex_destroy(&pil->lock);
467 kfree(pil);
468}
469
470struct pil_device *msm_pil_register(struct pil_desc *desc)
471{
472 int err;
473 static atomic_t pil_count = ATOMIC_INIT(-1);
Stephen Boyd3f4da322011-08-30 01:03:23 -0700474 struct pil_device *pil = kzalloc(sizeof(*pil), GFP_KERNEL);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700475
Stephen Boyd3f4da322011-08-30 01:03:23 -0700476 if (!pil)
Stephen Boyd6d67d252011-09-27 11:50:05 -0700477 return ERR_PTR(-ENOMEM);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700478
479 mutex_init(&pil->lock);
Stephen Boyd3f4da322011-08-30 01:03:23 -0700480 pil->desc = desc;
Stephen Boyd6d67d252011-09-27 11:50:05 -0700481 pil->owner = desc->owner;
482 pil->dev.parent = desc->dev;
483 pil->dev.bus = &pil_bus_type;
484 pil->dev.release = pil_device_release;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700485
Stephen Boyd6d67d252011-09-27 11:50:05 -0700486 dev_set_name(&pil->dev, "pil%d", atomic_inc_return(&pil_count));
487 err = device_register(&pil->dev);
488 if (err) {
489 put_device(&pil->dev);
490 mutex_destroy(&pil->lock);
491 kfree(pil);
492 return ERR_PTR(err);
493 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700494
Stephen Boyd6d67d252011-09-27 11:50:05 -0700495 err = msm_pil_debugfs_add(pil);
496 if (err) {
497 device_unregister(&pil->dev);
498 return ERR_PTR(err);
499 }
500
501 return pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700502}
Stephen Boyd3f4da322011-08-30 01:03:23 -0700503EXPORT_SYMBOL(msm_pil_register);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700504
Stephen Boyd6d67d252011-09-27 11:50:05 -0700505void msm_pil_unregister(struct pil_device *pil)
506{
507 if (IS_ERR_OR_NULL(pil))
508 return;
509
510 if (get_device(&pil->dev)) {
511 mutex_lock(&pil->lock);
512 WARN_ON(pil->count);
513 msm_pil_debugfs_remove(pil);
514 device_unregister(&pil->dev);
515 mutex_unlock(&pil->lock);
516 put_device(&pil->dev);
517 }
518}
519EXPORT_SYMBOL(msm_pil_unregister);
520
Stephen Boyd80bde032012-03-16 00:14:42 -0700521static int pil_pm_notify(struct notifier_block *b, unsigned long event, void *p)
522{
523 switch (event) {
524 case PM_SUSPEND_PREPARE:
525 down_write(&pil_pm_rwsem);
526 break;
527 case PM_POST_SUSPEND:
528 up_write(&pil_pm_rwsem);
529 break;
530 }
531 return NOTIFY_DONE;
532}
533
534static struct notifier_block pil_pm_notifier = {
535 .notifier_call = pil_pm_notify,
536};
537
Stephen Boyd6d67d252011-09-27 11:50:05 -0700538static int __init msm_pil_init(void)
539{
540 int ret = msm_pil_debugfs_init();
541 if (ret)
542 return ret;
Stephen Boyd80bde032012-03-16 00:14:42 -0700543 register_pm_notifier(&pil_pm_notifier);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700544 return bus_register(&pil_bus_type);
545}
546subsys_initcall(msm_pil_init);
547
548static void __exit msm_pil_exit(void)
549{
550 bus_unregister(&pil_bus_type);
Stephen Boyd80bde032012-03-16 00:14:42 -0700551 unregister_pm_notifier(&pil_pm_notifier);
Stephen Boyd6d67d252011-09-27 11:50:05 -0700552 msm_pil_debugfs_exit();
553}
554module_exit(msm_pil_exit);
555
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700556MODULE_LICENSE("GPL v2");
557MODULE_DESCRIPTION("Load peripheral images and bring peripherals out of reset");