0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025 #include <linux/kernel.h>
0026 #include <linux/module.h>
0027 #include <linux/init.h>
0028 #include <linux/types.h>
0029 #include <linux/err.h>
0030 #include <linux/fs.h>
0031 #include <linux/miscdevice.h>
0032 #include <linux/mm.h>
0033 #include <linux/pagemap.h>
0034 #include <linux/slab.h>
0035 #include <linux/poll.h>
0036 #include <linux/of.h>
0037 #include <linux/of_irq.h>
0038 #include <linux/reboot.h>
0039 #include <linux/uaccess.h>
0040 #include <linux/notifier.h>
0041 #include <linux/interrupt.h>
0042
0043 #include <linux/io.h>
0044 #include <asm/fsl_hcalls.h>
0045
0046 #include <linux/fsl_hypervisor.h>
0047
0048 static BLOCKING_NOTIFIER_HEAD(failover_subscribers);
0049
0050
0051
0052
0053
0054
0055 static long ioctl_restart(struct fsl_hv_ioctl_restart __user *p)
0056 {
0057 struct fsl_hv_ioctl_restart param;
0058
0059
0060 if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_restart)))
0061 return -EFAULT;
0062
0063 param.ret = fh_partition_restart(param.partition);
0064
0065 if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
0066 return -EFAULT;
0067
0068 return 0;
0069 }
0070
0071
0072
0073
0074
0075
0076 static long ioctl_status(struct fsl_hv_ioctl_status __user *p)
0077 {
0078 struct fsl_hv_ioctl_status param;
0079 u32 status;
0080
0081
0082 if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_status)))
0083 return -EFAULT;
0084
0085 param.ret = fh_partition_get_status(param.partition, &status);
0086 if (!param.ret)
0087 param.status = status;
0088
0089 if (copy_to_user(p, ¶m, sizeof(struct fsl_hv_ioctl_status)))
0090 return -EFAULT;
0091
0092 return 0;
0093 }
0094
0095
0096
0097
0098
0099
0100 static long ioctl_start(struct fsl_hv_ioctl_start __user *p)
0101 {
0102 struct fsl_hv_ioctl_start param;
0103
0104
0105 if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_start)))
0106 return -EFAULT;
0107
0108 param.ret = fh_partition_start(param.partition, param.entry_point,
0109 param.load);
0110
0111 if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
0112 return -EFAULT;
0113
0114 return 0;
0115 }
0116
0117
0118
0119
0120
0121
0122 static long ioctl_stop(struct fsl_hv_ioctl_stop __user *p)
0123 {
0124 struct fsl_hv_ioctl_stop param;
0125
0126
0127 if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_stop)))
0128 return -EFAULT;
0129
0130 param.ret = fh_partition_stop(param.partition);
0131
0132 if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
0133 return -EFAULT;
0134
0135 return 0;
0136 }
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147 static long ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user *p)
0148 {
0149 struct fsl_hv_ioctl_memcpy param;
0150
0151 struct page **pages = NULL;
0152 void *sg_list_unaligned = NULL;
0153 struct fh_sg_list *sg_list = NULL;
0154
0155 unsigned int num_pages;
0156 unsigned long lb_offset;
0157
0158 unsigned int i;
0159 long ret = 0;
0160 int num_pinned = 0;
0161 phys_addr_t remote_paddr;
0162 uint32_t count;
0163
0164
0165 if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_memcpy)))
0166 return -EFAULT;
0167
0168
0169
0170
0171
0172
0173 if ((param.source == -1) == (param.target == -1))
0174 return -EINVAL;
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191
0192
0193
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217 lb_offset = param.local_vaddr & (PAGE_SIZE - 1);
0218 if (param.count == 0 ||
0219 param.count > U64_MAX - lb_offset - PAGE_SIZE + 1)
0220 return -EINVAL;
0221 num_pages = (param.count + lb_offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
0222
0223
0224
0225
0226
0227
0228
0229 pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL);
0230 if (!pages) {
0231 pr_debug("fsl-hv: could not allocate page list\n");
0232 return -ENOMEM;
0233 }
0234
0235
0236
0237
0238
0239 sg_list_unaligned = kmalloc(num_pages * sizeof(struct fh_sg_list) +
0240 sizeof(struct fh_sg_list) - 1, GFP_KERNEL);
0241 if (!sg_list_unaligned) {
0242 pr_debug("fsl-hv: could not allocate S/G list\n");
0243 ret = -ENOMEM;
0244 goto free_pages;
0245 }
0246 sg_list = PTR_ALIGN(sg_list_unaligned, sizeof(struct fh_sg_list));
0247
0248
0249 num_pinned = get_user_pages_fast(param.local_vaddr - lb_offset,
0250 num_pages, param.source != -1 ? FOLL_WRITE : 0, pages);
0251
0252 if (num_pinned != num_pages) {
0253 pr_debug("fsl-hv: could not lock source buffer\n");
0254 ret = (num_pinned < 0) ? num_pinned : -EFAULT;
0255 goto exit;
0256 }
0257
0258
0259
0260
0261
0262 if (param.source == -1) {
0263 sg_list[0].source = page_to_phys(pages[0]) + lb_offset;
0264 sg_list[0].target = param.remote_paddr;
0265 } else {
0266 sg_list[0].source = param.remote_paddr;
0267 sg_list[0].target = page_to_phys(pages[0]) + lb_offset;
0268 }
0269 sg_list[0].size = min_t(uint64_t, param.count, PAGE_SIZE - lb_offset);
0270
0271 remote_paddr = param.remote_paddr + sg_list[0].size;
0272 count = param.count - sg_list[0].size;
0273
0274 for (i = 1; i < num_pages; i++) {
0275 if (param.source == -1) {
0276
0277 sg_list[i].source = page_to_phys(pages[i]);
0278 sg_list[i].target = remote_paddr;
0279 } else {
0280
0281 sg_list[i].source = remote_paddr;
0282 sg_list[i].target = page_to_phys(pages[i]);
0283 }
0284 sg_list[i].size = min_t(uint64_t, count, PAGE_SIZE);
0285
0286 remote_paddr += sg_list[i].size;
0287 count -= sg_list[i].size;
0288 }
0289
0290 param.ret = fh_partition_memcpy(param.source, param.target,
0291 virt_to_phys(sg_list), num_pages);
0292
0293 exit:
0294 if (pages && (num_pinned > 0)) {
0295 for (i = 0; i < num_pinned; i++)
0296 put_page(pages[i]);
0297 }
0298
0299 kfree(sg_list_unaligned);
0300 free_pages:
0301 kfree(pages);
0302
0303 if (!ret)
0304 if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
0305 return -EFAULT;
0306
0307 return ret;
0308 }
0309
0310
0311
0312
0313
0314
0315 static long ioctl_doorbell(struct fsl_hv_ioctl_doorbell __user *p)
0316 {
0317 struct fsl_hv_ioctl_doorbell param;
0318
0319
0320 if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_doorbell)))
0321 return -EFAULT;
0322
0323 param.ret = ev_doorbell_send(param.doorbell);
0324
0325 if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
0326 return -EFAULT;
0327
0328 return 0;
0329 }
0330
0331 static long ioctl_dtprop(struct fsl_hv_ioctl_prop __user *p, int set)
0332 {
0333 struct fsl_hv_ioctl_prop param;
0334 char __user *upath, *upropname;
0335 void __user *upropval;
0336 char *path, *propname;
0337 void *propval;
0338 int ret = 0;
0339
0340
0341 if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_prop)))
0342 return -EFAULT;
0343
0344 upath = (char __user *)(uintptr_t)param.path;
0345 upropname = (char __user *)(uintptr_t)param.propname;
0346 upropval = (void __user *)(uintptr_t)param.propval;
0347
0348 path = strndup_user(upath, FH_DTPROP_MAX_PATHLEN);
0349 if (IS_ERR(path))
0350 return PTR_ERR(path);
0351
0352 propname = strndup_user(upropname, FH_DTPROP_MAX_PATHLEN);
0353 if (IS_ERR(propname)) {
0354 ret = PTR_ERR(propname);
0355 goto err_free_path;
0356 }
0357
0358 if (param.proplen > FH_DTPROP_MAX_PROPLEN) {
0359 ret = -EINVAL;
0360 goto err_free_propname;
0361 }
0362
0363 propval = kmalloc(param.proplen, GFP_KERNEL);
0364 if (!propval) {
0365 ret = -ENOMEM;
0366 goto err_free_propname;
0367 }
0368
0369 if (set) {
0370 if (copy_from_user(propval, upropval, param.proplen)) {
0371 ret = -EFAULT;
0372 goto err_free_propval;
0373 }
0374
0375 param.ret = fh_partition_set_dtprop(param.handle,
0376 virt_to_phys(path),
0377 virt_to_phys(propname),
0378 virt_to_phys(propval),
0379 param.proplen);
0380 } else {
0381 param.ret = fh_partition_get_dtprop(param.handle,
0382 virt_to_phys(path),
0383 virt_to_phys(propname),
0384 virt_to_phys(propval),
0385 ¶m.proplen);
0386
0387 if (param.ret == 0) {
0388 if (copy_to_user(upropval, propval, param.proplen) ||
0389 put_user(param.proplen, &p->proplen)) {
0390 ret = -EFAULT;
0391 goto err_free_propval;
0392 }
0393 }
0394 }
0395
0396 if (put_user(param.ret, &p->ret))
0397 ret = -EFAULT;
0398
0399 err_free_propval:
0400 kfree(propval);
0401 err_free_propname:
0402 kfree(propname);
0403 err_free_path:
0404 kfree(path);
0405
0406 return ret;
0407 }
0408
0409
0410
0411
0412 static long fsl_hv_ioctl(struct file *file, unsigned int cmd,
0413 unsigned long argaddr)
0414 {
0415 void __user *arg = (void __user *)argaddr;
0416 long ret;
0417
0418 switch (cmd) {
0419 case FSL_HV_IOCTL_PARTITION_RESTART:
0420 ret = ioctl_restart(arg);
0421 break;
0422 case FSL_HV_IOCTL_PARTITION_GET_STATUS:
0423 ret = ioctl_status(arg);
0424 break;
0425 case FSL_HV_IOCTL_PARTITION_START:
0426 ret = ioctl_start(arg);
0427 break;
0428 case FSL_HV_IOCTL_PARTITION_STOP:
0429 ret = ioctl_stop(arg);
0430 break;
0431 case FSL_HV_IOCTL_MEMCPY:
0432 ret = ioctl_memcpy(arg);
0433 break;
0434 case FSL_HV_IOCTL_DOORBELL:
0435 ret = ioctl_doorbell(arg);
0436 break;
0437 case FSL_HV_IOCTL_GETPROP:
0438 ret = ioctl_dtprop(arg, 0);
0439 break;
0440 case FSL_HV_IOCTL_SETPROP:
0441 ret = ioctl_dtprop(arg, 1);
0442 break;
0443 default:
0444 pr_debug("fsl-hv: bad ioctl dir=%u type=%u cmd=%u size=%u\n",
0445 _IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd),
0446 _IOC_SIZE(cmd));
0447 return -ENOTTY;
0448 }
0449
0450 return ret;
0451 }
0452
0453
0454 static struct list_head db_list;
0455
0456
0457 static DEFINE_SPINLOCK(db_list_lock);
0458
0459
0460 #define QSIZE 16
0461
0462
0463 #define nextp(x) (((x) + 1) & (QSIZE - 1))
0464
0465
0466 struct doorbell_queue {
0467 struct list_head list;
0468 spinlock_t lock;
0469 wait_queue_head_t wait;
0470 unsigned int head;
0471 unsigned int tail;
0472 uint32_t q[QSIZE];
0473 };
0474
0475
0476 struct list_head isr_list;
0477
0478
0479 struct doorbell_isr {
0480 struct list_head list;
0481 unsigned int irq;
0482 uint32_t doorbell;
0483 uint32_t partition;
0484 };
0485
0486
0487
0488
0489 static void fsl_hv_queue_doorbell(uint32_t doorbell)
0490 {
0491 struct doorbell_queue *dbq;
0492 unsigned long flags;
0493
0494
0495 spin_lock_irqsave(&db_list_lock, flags);
0496
0497 list_for_each_entry(dbq, &db_list, list) {
0498 if (dbq->head != nextp(dbq->tail)) {
0499 dbq->q[dbq->tail] = doorbell;
0500
0501
0502
0503
0504 smp_wmb();
0505 dbq->tail = nextp(dbq->tail);
0506 wake_up_interruptible(&dbq->wait);
0507 }
0508 }
0509
0510 spin_unlock_irqrestore(&db_list_lock, flags);
0511 }
0512
0513
0514
0515
0516
0517
0518
0519
0520 static irqreturn_t fsl_hv_isr(int irq, void *data)
0521 {
0522 fsl_hv_queue_doorbell((uintptr_t) data);
0523
0524 return IRQ_HANDLED;
0525 }
0526
0527
0528
0529
0530
0531
0532
0533
0534
0535
0536
0537 static irqreturn_t fsl_hv_state_change_thread(int irq, void *data)
0538 {
0539 struct doorbell_isr *dbisr = data;
0540
0541 blocking_notifier_call_chain(&failover_subscribers, dbisr->partition,
0542 NULL);
0543
0544 return IRQ_HANDLED;
0545 }
0546
0547
0548
0549
0550 static irqreturn_t fsl_hv_state_change_isr(int irq, void *data)
0551 {
0552 unsigned int status;
0553 struct doorbell_isr *dbisr = data;
0554 int ret;
0555
0556
0557 fsl_hv_queue_doorbell(dbisr->doorbell);
0558
0559
0560 ret = fh_partition_get_status(dbisr->partition, &status);
0561 if (!ret && (status == FH_PARTITION_STOPPED))
0562 return IRQ_WAKE_THREAD;
0563
0564 return IRQ_HANDLED;
0565 }
0566
0567
0568
0569
0570 static __poll_t fsl_hv_poll(struct file *filp, struct poll_table_struct *p)
0571 {
0572 struct doorbell_queue *dbq = filp->private_data;
0573 unsigned long flags;
0574 __poll_t mask;
0575
0576 spin_lock_irqsave(&dbq->lock, flags);
0577
0578 poll_wait(filp, &dbq->wait, p);
0579 mask = (dbq->head == dbq->tail) ? 0 : (EPOLLIN | EPOLLRDNORM);
0580
0581 spin_unlock_irqrestore(&dbq->lock, flags);
0582
0583 return mask;
0584 }
0585
0586
0587
0588
0589
0590
0591
0592
0593 static ssize_t fsl_hv_read(struct file *filp, char __user *buf, size_t len,
0594 loff_t *off)
0595 {
0596 struct doorbell_queue *dbq = filp->private_data;
0597 uint32_t __user *p = (uint32_t __user *) buf;
0598 unsigned long flags;
0599 ssize_t count = 0;
0600
0601
0602 while (len >= sizeof(uint32_t)) {
0603 uint32_t dbell;
0604
0605 spin_lock_irqsave(&dbq->lock, flags);
0606
0607
0608
0609
0610
0611
0612 if (dbq->head == dbq->tail) {
0613 spin_unlock_irqrestore(&dbq->lock, flags);
0614 if (count)
0615 break;
0616 if (filp->f_flags & O_NONBLOCK)
0617 return -EAGAIN;
0618 if (wait_event_interruptible(dbq->wait,
0619 dbq->head != dbq->tail))
0620 return -ERESTARTSYS;
0621 continue;
0622 }
0623
0624
0625
0626
0627
0628
0629
0630
0631
0632 smp_rmb();
0633
0634
0635
0636
0637 dbell = dbq->q[dbq->head];
0638 dbq->head = nextp(dbq->head);
0639
0640 spin_unlock_irqrestore(&dbq->lock, flags);
0641
0642 if (put_user(dbell, p))
0643 return -EFAULT;
0644 p++;
0645 count += sizeof(uint32_t);
0646 len -= sizeof(uint32_t);
0647 }
0648
0649 return count;
0650 }
0651
0652
0653
0654
0655
0656
0657
0658 static int fsl_hv_open(struct inode *inode, struct file *filp)
0659 {
0660 struct doorbell_queue *dbq;
0661 unsigned long flags;
0662
0663 dbq = kzalloc(sizeof(struct doorbell_queue), GFP_KERNEL);
0664 if (!dbq) {
0665 pr_err("fsl-hv: out of memory\n");
0666 return -ENOMEM;
0667 }
0668
0669 spin_lock_init(&dbq->lock);
0670 init_waitqueue_head(&dbq->wait);
0671
0672 spin_lock_irqsave(&db_list_lock, flags);
0673 list_add(&dbq->list, &db_list);
0674 spin_unlock_irqrestore(&db_list_lock, flags);
0675
0676 filp->private_data = dbq;
0677
0678 return 0;
0679 }
0680
0681
0682
0683
0684 static int fsl_hv_close(struct inode *inode, struct file *filp)
0685 {
0686 struct doorbell_queue *dbq = filp->private_data;
0687 unsigned long flags;
0688
0689 spin_lock_irqsave(&db_list_lock, flags);
0690 list_del(&dbq->list);
0691 spin_unlock_irqrestore(&db_list_lock, flags);
0692
0693 kfree(dbq);
0694
0695 return 0;
0696 }
0697
0698 static const struct file_operations fsl_hv_fops = {
0699 .owner = THIS_MODULE,
0700 .open = fsl_hv_open,
0701 .release = fsl_hv_close,
0702 .poll = fsl_hv_poll,
0703 .read = fsl_hv_read,
0704 .unlocked_ioctl = fsl_hv_ioctl,
0705 .compat_ioctl = compat_ptr_ioctl,
0706 };
0707
0708 static struct miscdevice fsl_hv_misc_dev = {
0709 MISC_DYNAMIC_MINOR,
0710 "fsl-hv",
0711 &fsl_hv_fops
0712 };
0713
0714 static irqreturn_t fsl_hv_shutdown_isr(int irq, void *data)
0715 {
0716 orderly_poweroff(false);
0717
0718 return IRQ_HANDLED;
0719 }
0720
0721
0722
0723
0724
0725
0726 static int get_parent_handle(struct device_node *np)
0727 {
0728 struct device_node *parent;
0729 const uint32_t *prop;
0730 uint32_t handle;
0731 int len;
0732
0733 parent = of_get_parent(np);
0734 if (!parent)
0735
0736 return -ENODEV;
0737
0738
0739
0740
0741
0742 prop = of_get_property(parent, "hv-handle", &len);
0743 if (!prop)
0744 prop = of_get_property(parent, "reg", &len);
0745
0746 if (!prop || (len != sizeof(uint32_t))) {
0747
0748 of_node_put(parent);
0749 return -ENODEV;
0750 }
0751
0752 handle = be32_to_cpup(prop);
0753 of_node_put(parent);
0754
0755 return handle;
0756 }
0757
0758
0759
0760
0761
0762
0763
0764 int fsl_hv_failover_register(struct notifier_block *nb)
0765 {
0766 return blocking_notifier_chain_register(&failover_subscribers, nb);
0767 }
0768 EXPORT_SYMBOL(fsl_hv_failover_register);
0769
0770
0771
0772
0773 int fsl_hv_failover_unregister(struct notifier_block *nb)
0774 {
0775 return blocking_notifier_chain_unregister(&failover_subscribers, nb);
0776 }
0777 EXPORT_SYMBOL(fsl_hv_failover_unregister);
0778
0779
0780
0781
0782
0783
0784
0785
0786
0787
0788
0789
0790 static int has_fsl_hypervisor(void)
0791 {
0792 struct device_node *node;
0793 int ret;
0794
0795 node = of_find_node_by_path("/hypervisor");
0796 if (!node)
0797 return 0;
0798
0799 ret = of_find_property(node, "fsl,hv-version", NULL) != NULL;
0800
0801 of_node_put(node);
0802
0803 return ret;
0804 }
0805
0806
0807
0808
0809
0810
0811
0812
0813
0814 static int __init fsl_hypervisor_init(void)
0815 {
0816 struct device_node *np;
0817 struct doorbell_isr *dbisr, *n;
0818 int ret;
0819
0820 pr_info("Freescale hypervisor management driver\n");
0821
0822 if (!has_fsl_hypervisor()) {
0823 pr_info("fsl-hv: no hypervisor found\n");
0824 return -ENODEV;
0825 }
0826
0827 ret = misc_register(&fsl_hv_misc_dev);
0828 if (ret) {
0829 pr_err("fsl-hv: cannot register device\n");
0830 return ret;
0831 }
0832
0833 INIT_LIST_HEAD(&db_list);
0834 INIT_LIST_HEAD(&isr_list);
0835
0836 for_each_compatible_node(np, NULL, "epapr,hv-receive-doorbell") {
0837 unsigned int irq;
0838 const uint32_t *handle;
0839
0840 handle = of_get_property(np, "interrupts", NULL);
0841 irq = irq_of_parse_and_map(np, 0);
0842 if (!handle || (irq == NO_IRQ)) {
0843 pr_err("fsl-hv: no 'interrupts' property in %pOF node\n",
0844 np);
0845 continue;
0846 }
0847
0848 dbisr = kzalloc(sizeof(*dbisr), GFP_KERNEL);
0849 if (!dbisr)
0850 goto out_of_memory;
0851
0852 dbisr->irq = irq;
0853 dbisr->doorbell = be32_to_cpup(handle);
0854
0855 if (of_device_is_compatible(np, "fsl,hv-shutdown-doorbell")) {
0856
0857 ret = request_irq(irq, fsl_hv_shutdown_isr, 0,
0858 np->name, NULL);
0859 } else if (of_device_is_compatible(np,
0860 "fsl,hv-state-change-doorbell")) {
0861
0862
0863
0864
0865
0866
0867
0868
0869 dbisr->partition = ret = get_parent_handle(np);
0870 if (ret < 0) {
0871 pr_err("fsl-hv: node %pOF has missing or "
0872 "malformed parent\n", np);
0873 kfree(dbisr);
0874 continue;
0875 }
0876 ret = request_threaded_irq(irq, fsl_hv_state_change_isr,
0877 fsl_hv_state_change_thread,
0878 0, np->name, dbisr);
0879 } else
0880 ret = request_irq(irq, fsl_hv_isr, 0, np->name, dbisr);
0881
0882 if (ret < 0) {
0883 pr_err("fsl-hv: could not request irq %u for node %pOF\n",
0884 irq, np);
0885 kfree(dbisr);
0886 continue;
0887 }
0888
0889 list_add(&dbisr->list, &isr_list);
0890
0891 pr_info("fsl-hv: registered handler for doorbell %u\n",
0892 dbisr->doorbell);
0893 }
0894
0895 return 0;
0896
0897 out_of_memory:
0898 list_for_each_entry_safe(dbisr, n, &isr_list, list) {
0899 free_irq(dbisr->irq, dbisr);
0900 list_del(&dbisr->list);
0901 kfree(dbisr);
0902 }
0903
0904 misc_deregister(&fsl_hv_misc_dev);
0905
0906 return -ENOMEM;
0907 }
0908
0909
0910
0911
0912
0913
0914 static void __exit fsl_hypervisor_exit(void)
0915 {
0916 struct doorbell_isr *dbisr, *n;
0917
0918 list_for_each_entry_safe(dbisr, n, &isr_list, list) {
0919 free_irq(dbisr->irq, dbisr);
0920 list_del(&dbisr->list);
0921 kfree(dbisr);
0922 }
0923
0924 misc_deregister(&fsl_hv_misc_dev);
0925 }
0926
0927 module_init(fsl_hypervisor_init);
0928 module_exit(fsl_hypervisor_exit);
0929
0930 MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
0931 MODULE_DESCRIPTION("Freescale hypervisor management driver");
0932 MODULE_LICENSE("GPL v2");