16db71994STimur Tabi /*
26db71994STimur Tabi * Freescale Hypervisor Management Driver
36db71994STimur Tabi
46db71994STimur Tabi * Copyright (C) 2008-2011 Freescale Semiconductor, Inc.
56db71994STimur Tabi * Author: Timur Tabi <timur@freescale.com>
66db71994STimur Tabi *
76db71994STimur Tabi * This file is licensed under the terms of the GNU General Public License
86db71994STimur Tabi * version 2. This program is licensed "as is" without any warranty of any
96db71994STimur Tabi * kind, whether express or implied.
106db71994STimur Tabi *
116db71994STimur Tabi * The Freescale hypervisor management driver provides several services to
126db71994STimur Tabi * drivers and applications related to the Freescale hypervisor:
136db71994STimur Tabi *
146db71994STimur Tabi * 1. An ioctl interface for querying and managing partitions.
156db71994STimur Tabi *
166db71994STimur Tabi * 2. A file interface to reading incoming doorbells.
176db71994STimur Tabi *
186db71994STimur Tabi * 3. An interrupt handler for shutting down the partition upon receiving the
196db71994STimur Tabi * shutdown doorbell from a manager partition.
206db71994STimur Tabi *
216db71994STimur Tabi * 4. A kernel interface for receiving callbacks when a managed partition
226db71994STimur Tabi * shuts down.
236db71994STimur Tabi */
246db71994STimur Tabi
256db71994STimur Tabi #include <linux/kernel.h>
266db71994STimur Tabi #include <linux/module.h>
276db71994STimur Tabi #include <linux/init.h>
286db71994STimur Tabi #include <linux/types.h>
296db71994STimur Tabi #include <linux/err.h>
306db71994STimur Tabi #include <linux/fs.h>
316db71994STimur Tabi #include <linux/miscdevice.h>
326db71994STimur Tabi #include <linux/mm.h>
336db71994STimur Tabi #include <linux/pagemap.h>
346db71994STimur Tabi #include <linux/slab.h>
356db71994STimur Tabi #include <linux/poll.h>
366db71994STimur Tabi #include <linux/of.h>
375af50730SRob Herring #include <linux/of_irq.h>
386db71994STimur Tabi #include <linux/reboot.h>
396db71994STimur Tabi #include <linux/uaccess.h>
406db71994STimur Tabi #include <linux/notifier.h>
41f1f4ee01STimur Tabi #include <linux/interrupt.h>
426db71994STimur Tabi
436db71994STimur Tabi #include <linux/io.h>
446db71994STimur Tabi #include <asm/fsl_hcalls.h>
456db71994STimur Tabi
466db71994STimur Tabi #include <linux/fsl_hypervisor.h>
476db71994STimur Tabi
486db71994STimur Tabi static BLOCKING_NOTIFIER_HEAD(failover_subscribers);
496db71994STimur Tabi
506db71994STimur Tabi /*
516db71994STimur Tabi * Ioctl interface for FSL_HV_IOCTL_PARTITION_RESTART
526db71994STimur Tabi *
536db71994STimur Tabi * Restart a running partition
546db71994STimur Tabi */
ioctl_restart(struct fsl_hv_ioctl_restart __user * p)556db71994STimur Tabi static long ioctl_restart(struct fsl_hv_ioctl_restart __user *p)
566db71994STimur Tabi {
576db71994STimur Tabi struct fsl_hv_ioctl_restart param;
586db71994STimur Tabi
596db71994STimur Tabi /* Get the parameters from the user */
606db71994STimur Tabi if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_restart)))
616db71994STimur Tabi return -EFAULT;
626db71994STimur Tabi
636db71994STimur Tabi param.ret = fh_partition_restart(param.partition);
646db71994STimur Tabi
656db71994STimur Tabi if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
666db71994STimur Tabi return -EFAULT;
676db71994STimur Tabi
686db71994STimur Tabi return 0;
696db71994STimur Tabi }
706db71994STimur Tabi
716db71994STimur Tabi /*
726db71994STimur Tabi * Ioctl interface for FSL_HV_IOCTL_PARTITION_STATUS
736db71994STimur Tabi *
746db71994STimur Tabi * Query the status of a partition
756db71994STimur Tabi */
ioctl_status(struct fsl_hv_ioctl_status __user * p)766db71994STimur Tabi static long ioctl_status(struct fsl_hv_ioctl_status __user *p)
776db71994STimur Tabi {
786db71994STimur Tabi struct fsl_hv_ioctl_status param;
796db71994STimur Tabi u32 status;
806db71994STimur Tabi
816db71994STimur Tabi /* Get the parameters from the user */
826db71994STimur Tabi if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_status)))
836db71994STimur Tabi return -EFAULT;
846db71994STimur Tabi
856db71994STimur Tabi param.ret = fh_partition_get_status(param.partition, &status);
866db71994STimur Tabi if (!param.ret)
876db71994STimur Tabi param.status = status;
886db71994STimur Tabi
896db71994STimur Tabi if (copy_to_user(p, ¶m, sizeof(struct fsl_hv_ioctl_status)))
906db71994STimur Tabi return -EFAULT;
916db71994STimur Tabi
926db71994STimur Tabi return 0;
936db71994STimur Tabi }
946db71994STimur Tabi
956db71994STimur Tabi /*
966db71994STimur Tabi * Ioctl interface for FSL_HV_IOCTL_PARTITION_START
976db71994STimur Tabi *
986db71994STimur Tabi * Start a stopped partition.
996db71994STimur Tabi */
ioctl_start(struct fsl_hv_ioctl_start __user * p)1006db71994STimur Tabi static long ioctl_start(struct fsl_hv_ioctl_start __user *p)
1016db71994STimur Tabi {
1026db71994STimur Tabi struct fsl_hv_ioctl_start param;
1036db71994STimur Tabi
1046db71994STimur Tabi /* Get the parameters from the user */
1056db71994STimur Tabi if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_start)))
1066db71994STimur Tabi return -EFAULT;
1076db71994STimur Tabi
1086db71994STimur Tabi param.ret = fh_partition_start(param.partition, param.entry_point,
1096db71994STimur Tabi param.load);
1106db71994STimur Tabi
1116db71994STimur Tabi if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
1126db71994STimur Tabi return -EFAULT;
1136db71994STimur Tabi
1146db71994STimur Tabi return 0;
1156db71994STimur Tabi }
1166db71994STimur Tabi
1176db71994STimur Tabi /*
1186db71994STimur Tabi * Ioctl interface for FSL_HV_IOCTL_PARTITION_STOP
1196db71994STimur Tabi *
1206db71994STimur Tabi * Stop a running partition
1216db71994STimur Tabi */
ioctl_stop(struct fsl_hv_ioctl_stop __user * p)1226db71994STimur Tabi static long ioctl_stop(struct fsl_hv_ioctl_stop __user *p)
1236db71994STimur Tabi {
1246db71994STimur Tabi struct fsl_hv_ioctl_stop param;
1256db71994STimur Tabi
1266db71994STimur Tabi /* Get the parameters from the user */
1276db71994STimur Tabi if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_stop)))
1286db71994STimur Tabi return -EFAULT;
1296db71994STimur Tabi
1306db71994STimur Tabi param.ret = fh_partition_stop(param.partition);
1316db71994STimur Tabi
1326db71994STimur Tabi if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
1336db71994STimur Tabi return -EFAULT;
1346db71994STimur Tabi
1356db71994STimur Tabi return 0;
1366db71994STimur Tabi }
1376db71994STimur Tabi
1386db71994STimur Tabi /*
1396db71994STimur Tabi * Ioctl interface for FSL_HV_IOCTL_MEMCPY
1406db71994STimur Tabi *
1416db71994STimur Tabi * The FH_MEMCPY hypercall takes an array of address/address/size structures
1426db71994STimur Tabi * to represent the data being copied. As a convenience to the user, this
1436db71994STimur Tabi * ioctl takes a user-create buffer and a pointer to a guest physically
1446db71994STimur Tabi * contiguous buffer in the remote partition, and creates the
1456db71994STimur Tabi * address/address/size array for the hypercall.
1466db71994STimur Tabi */
ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user * p)1476db71994STimur Tabi static long ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user *p)
1486db71994STimur Tabi {
1496db71994STimur Tabi struct fsl_hv_ioctl_memcpy param;
1506db71994STimur Tabi
1516db71994STimur Tabi struct page **pages = NULL;
1526db71994STimur Tabi void *sg_list_unaligned = NULL;
1536db71994STimur Tabi struct fh_sg_list *sg_list = NULL;
1546db71994STimur Tabi
1556db71994STimur Tabi unsigned int num_pages;
1566db71994STimur Tabi unsigned long lb_offset; /* Offset within a page of the local buffer */
1576db71994STimur Tabi
1586db71994STimur Tabi unsigned int i;
1596db71994STimur Tabi long ret = 0;
1607f360becSSouptick Joarder int num_pinned = 0; /* return value from get_user_pages_fast() */
1616db71994STimur Tabi phys_addr_t remote_paddr; /* The next address in the remote buffer */
1626db71994STimur Tabi uint32_t count; /* The number of bytes left to copy */
1636db71994STimur Tabi
1646db71994STimur Tabi /* Get the parameters from the user */
1656db71994STimur Tabi if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_memcpy)))
1666db71994STimur Tabi return -EFAULT;
1676db71994STimur Tabi
1686db71994STimur Tabi /*
1696db71994STimur Tabi * One partition must be local, the other must be remote. In other
1706db71994STimur Tabi * words, if source and target are both -1, or are both not -1, then
1716db71994STimur Tabi * return an error.
1726db71994STimur Tabi */
1736db71994STimur Tabi if ((param.source == -1) == (param.target == -1))
1746db71994STimur Tabi return -EINVAL;
1756db71994STimur Tabi
1766db71994STimur Tabi /*
1777f360becSSouptick Joarder * The array of pages returned by get_user_pages_fast() covers only
1786db71994STimur Tabi * page-aligned memory. Since the user buffer is probably not
1796db71994STimur Tabi * page-aligned, we need to handle the discrepancy.
1806db71994STimur Tabi *
1816db71994STimur Tabi * We calculate the offset within a page of the S/G list, and make
1826db71994STimur Tabi * adjustments accordingly. This will result in a page list that looks
1836db71994STimur Tabi * like this:
1846db71994STimur Tabi *
1856db71994STimur Tabi * ---- <-- first page starts before the buffer
1866db71994STimur Tabi * | |
1876db71994STimur Tabi * |////|-> ----
1886db71994STimur Tabi * |////| | |
1896db71994STimur Tabi * ---- | |
1906db71994STimur Tabi * | |
1916db71994STimur Tabi * ---- | |
1926db71994STimur Tabi * |////| | |
1936db71994STimur Tabi * |////| | |
1946db71994STimur Tabi * |////| | |
1956db71994STimur Tabi * ---- | |
1966db71994STimur Tabi * | |
1976db71994STimur Tabi * ---- | |
1986db71994STimur Tabi * |////| | |
1996db71994STimur Tabi * |////| | |
2006db71994STimur Tabi * |////| | |
2016db71994STimur Tabi * ---- | |
2026db71994STimur Tabi * | |
2036db71994STimur Tabi * ---- | |
2046db71994STimur Tabi * |////| | |
2056db71994STimur Tabi * |////|-> ----
2066db71994STimur Tabi * | | <-- last page ends after the buffer
2076db71994STimur Tabi * ----
2086db71994STimur Tabi *
2096db71994STimur Tabi * The distance between the start of the first page and the start of the
2106db71994STimur Tabi * buffer is lb_offset. The hashed (///) areas are the parts of the
2116db71994STimur Tabi * page list that contain the actual buffer.
2126db71994STimur Tabi *
2136db71994STimur Tabi * The advantage of this approach is that the number of pages is
2146db71994STimur Tabi * equal to the number of entries in the S/G list that we give to the
2156db71994STimur Tabi * hypervisor.
2166db71994STimur Tabi */
2176db71994STimur Tabi lb_offset = param.local_vaddr & (PAGE_SIZE - 1);
2186a024330SDan Carpenter if (param.count == 0 ||
2196a024330SDan Carpenter param.count > U64_MAX - lb_offset - PAGE_SIZE + 1)
2206a024330SDan Carpenter return -EINVAL;
2216db71994STimur Tabi num_pages = (param.count + lb_offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
2226db71994STimur Tabi
2236db71994STimur Tabi /* Allocate the buffers we need */
2246db71994STimur Tabi
2256db71994STimur Tabi /*
2266db71994STimur Tabi * 'pages' is an array of struct page pointers that's initialized by
2277f360becSSouptick Joarder * get_user_pages_fast().
2286db71994STimur Tabi */
2296396bb22SKees Cook pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL);
2306db71994STimur Tabi if (!pages) {
2316db71994STimur Tabi pr_debug("fsl-hv: could not allocate page list\n");
2326db71994STimur Tabi return -ENOMEM;
2336db71994STimur Tabi }
2346db71994STimur Tabi
2356db71994STimur Tabi /*
2366db71994STimur Tabi * sg_list is the list of fh_sg_list objects that we pass to the
2376db71994STimur Tabi * hypervisor.
2386db71994STimur Tabi */
2396db71994STimur Tabi sg_list_unaligned = kmalloc(num_pages * sizeof(struct fh_sg_list) +
2406db71994STimur Tabi sizeof(struct fh_sg_list) - 1, GFP_KERNEL);
2416db71994STimur Tabi if (!sg_list_unaligned) {
2426db71994STimur Tabi pr_debug("fsl-hv: could not allocate S/G list\n");
2436db71994STimur Tabi ret = -ENOMEM;
2447f360becSSouptick Joarder goto free_pages;
2456db71994STimur Tabi }
2466db71994STimur Tabi sg_list = PTR_ALIGN(sg_list_unaligned, sizeof(struct fh_sg_list));
2476db71994STimur Tabi
2486db71994STimur Tabi /* Get the physical addresses of the source buffer */
2491aaa09caSAl Viro num_pinned = get_user_pages_fast(param.local_vaddr - lb_offset,
25073b0140bSIra Weiny num_pages, param.source != -1 ? FOLL_WRITE : 0, pages);
2516db71994STimur Tabi
2526db71994STimur Tabi if (num_pinned != num_pages) {
2536db71994STimur Tabi pr_debug("fsl-hv: could not lock source buffer\n");
2546db71994STimur Tabi ret = (num_pinned < 0) ? num_pinned : -EFAULT;
2556db71994STimur Tabi goto exit;
2566db71994STimur Tabi }
2576db71994STimur Tabi
2586db71994STimur Tabi /*
2596db71994STimur Tabi * Build the fh_sg_list[] array. The first page is special
2606db71994STimur Tabi * because it's misaligned.
2616db71994STimur Tabi */
2626db71994STimur Tabi if (param.source == -1) {
2636db71994STimur Tabi sg_list[0].source = page_to_phys(pages[0]) + lb_offset;
2646db71994STimur Tabi sg_list[0].target = param.remote_paddr;
2656db71994STimur Tabi } else {
2666db71994STimur Tabi sg_list[0].source = param.remote_paddr;
2676db71994STimur Tabi sg_list[0].target = page_to_phys(pages[0]) + lb_offset;
2686db71994STimur Tabi }
2696db71994STimur Tabi sg_list[0].size = min_t(uint64_t, param.count, PAGE_SIZE - lb_offset);
2706db71994STimur Tabi
2716db71994STimur Tabi remote_paddr = param.remote_paddr + sg_list[0].size;
2726db71994STimur Tabi count = param.count - sg_list[0].size;
2736db71994STimur Tabi
2746db71994STimur Tabi for (i = 1; i < num_pages; i++) {
2756db71994STimur Tabi if (param.source == -1) {
2766db71994STimur Tabi /* local to remote */
2776db71994STimur Tabi sg_list[i].source = page_to_phys(pages[i]);
2786db71994STimur Tabi sg_list[i].target = remote_paddr;
2796db71994STimur Tabi } else {
2806db71994STimur Tabi /* remote to local */
2816db71994STimur Tabi sg_list[i].source = remote_paddr;
2826db71994STimur Tabi sg_list[i].target = page_to_phys(pages[i]);
2836db71994STimur Tabi }
2846db71994STimur Tabi sg_list[i].size = min_t(uint64_t, count, PAGE_SIZE);
2856db71994STimur Tabi
2866db71994STimur Tabi remote_paddr += sg_list[i].size;
2876db71994STimur Tabi count -= sg_list[i].size;
2886db71994STimur Tabi }
2896db71994STimur Tabi
2906db71994STimur Tabi param.ret = fh_partition_memcpy(param.source, param.target,
2916db71994STimur Tabi virt_to_phys(sg_list), num_pages);
2926db71994STimur Tabi
2936db71994STimur Tabi exit:
2947f360becSSouptick Joarder if (pages && (num_pinned > 0)) {
2957f360becSSouptick Joarder for (i = 0; i < num_pinned; i++)
2966db71994STimur Tabi put_page(pages[i]);
2976db71994STimur Tabi }
2986db71994STimur Tabi
2996db71994STimur Tabi kfree(sg_list_unaligned);
3007f360becSSouptick Joarder free_pages:
3016db71994STimur Tabi kfree(pages);
3026db71994STimur Tabi
3036db71994STimur Tabi if (!ret)
3046db71994STimur Tabi if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
3056db71994STimur Tabi return -EFAULT;
3066db71994STimur Tabi
3076db71994STimur Tabi return ret;
3086db71994STimur Tabi }
3096db71994STimur Tabi
3106db71994STimur Tabi /*
3116db71994STimur Tabi * Ioctl interface for FSL_HV_IOCTL_DOORBELL
3126db71994STimur Tabi *
3136db71994STimur Tabi * Ring a doorbell
3146db71994STimur Tabi */
ioctl_doorbell(struct fsl_hv_ioctl_doorbell __user * p)3156db71994STimur Tabi static long ioctl_doorbell(struct fsl_hv_ioctl_doorbell __user *p)
3166db71994STimur Tabi {
3176db71994STimur Tabi struct fsl_hv_ioctl_doorbell param;
3186db71994STimur Tabi
3196db71994STimur Tabi /* Get the parameters from the user. */
3206db71994STimur Tabi if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_doorbell)))
3216db71994STimur Tabi return -EFAULT;
3226db71994STimur Tabi
3236db71994STimur Tabi param.ret = ev_doorbell_send(param.doorbell);
3246db71994STimur Tabi
3256db71994STimur Tabi if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
3266db71994STimur Tabi return -EFAULT;
3276db71994STimur Tabi
3286db71994STimur Tabi return 0;
3296db71994STimur Tabi }
3306db71994STimur Tabi
ioctl_dtprop(struct fsl_hv_ioctl_prop __user * p,int set)3316db71994STimur Tabi static long ioctl_dtprop(struct fsl_hv_ioctl_prop __user *p, int set)
3326db71994STimur Tabi {
3336db71994STimur Tabi struct fsl_hv_ioctl_prop param;
3346db71994STimur Tabi char __user *upath, *upropname;
3356db71994STimur Tabi void __user *upropval;
336c8ea3663SDan Carpenter char *path, *propname;
337c8ea3663SDan Carpenter void *propval;
3386db71994STimur Tabi int ret = 0;
3396db71994STimur Tabi
3406db71994STimur Tabi /* Get the parameters from the user. */
3416db71994STimur Tabi if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_prop)))
3426db71994STimur Tabi return -EFAULT;
3436db71994STimur Tabi
3446db71994STimur Tabi upath = (char __user *)(uintptr_t)param.path;
3456db71994STimur Tabi upropname = (char __user *)(uintptr_t)param.propname;
3466db71994STimur Tabi upropval = (void __user *)(uintptr_t)param.propval;
3476db71994STimur Tabi
3486db71994STimur Tabi path = strndup_user(upath, FH_DTPROP_MAX_PATHLEN);
349c8ea3663SDan Carpenter if (IS_ERR(path))
350c8ea3663SDan Carpenter return PTR_ERR(path);
3516db71994STimur Tabi
3526db71994STimur Tabi propname = strndup_user(upropname, FH_DTPROP_MAX_PATHLEN);
3536db71994STimur Tabi if (IS_ERR(propname)) {
3546db71994STimur Tabi ret = PTR_ERR(propname);
355c8ea3663SDan Carpenter goto err_free_path;
3566db71994STimur Tabi }
3576db71994STimur Tabi
3586db71994STimur Tabi if (param.proplen > FH_DTPROP_MAX_PROPLEN) {
3596db71994STimur Tabi ret = -EINVAL;
360c8ea3663SDan Carpenter goto err_free_propname;
3616db71994STimur Tabi }
3626db71994STimur Tabi
3636db71994STimur Tabi propval = kmalloc(param.proplen, GFP_KERNEL);
3646db71994STimur Tabi if (!propval) {
3656db71994STimur Tabi ret = -ENOMEM;
366c8ea3663SDan Carpenter goto err_free_propname;
3676db71994STimur Tabi }
3686db71994STimur Tabi
3696db71994STimur Tabi if (set) {
3706db71994STimur Tabi if (copy_from_user(propval, upropval, param.proplen)) {
3716db71994STimur Tabi ret = -EFAULT;
372c8ea3663SDan Carpenter goto err_free_propval;
3736db71994STimur Tabi }
3746db71994STimur Tabi
3756db71994STimur Tabi param.ret = fh_partition_set_dtprop(param.handle,
3766db71994STimur Tabi virt_to_phys(path),
3776db71994STimur Tabi virt_to_phys(propname),
3786db71994STimur Tabi virt_to_phys(propval),
3796db71994STimur Tabi param.proplen);
3806db71994STimur Tabi } else {
3816db71994STimur Tabi param.ret = fh_partition_get_dtprop(param.handle,
3826db71994STimur Tabi virt_to_phys(path),
3836db71994STimur Tabi virt_to_phys(propname),
3846db71994STimur Tabi virt_to_phys(propval),
3856db71994STimur Tabi ¶m.proplen);
3866db71994STimur Tabi
3876db71994STimur Tabi if (param.ret == 0) {
3886db71994STimur Tabi if (copy_to_user(upropval, propval, param.proplen) ||
3896db71994STimur Tabi put_user(param.proplen, &p->proplen)) {
3906db71994STimur Tabi ret = -EFAULT;
391c8ea3663SDan Carpenter goto err_free_propval;
3926db71994STimur Tabi }
3936db71994STimur Tabi }
3946db71994STimur Tabi }
3956db71994STimur Tabi
3966db71994STimur Tabi if (put_user(param.ret, &p->ret))
3976db71994STimur Tabi ret = -EFAULT;
3986db71994STimur Tabi
399c8ea3663SDan Carpenter err_free_propval:
4006db71994STimur Tabi kfree(propval);
401c8ea3663SDan Carpenter err_free_propname:
4026db71994STimur Tabi kfree(propname);
403c8ea3663SDan Carpenter err_free_path:
404c8ea3663SDan Carpenter kfree(path);
4056db71994STimur Tabi
4066db71994STimur Tabi return ret;
4076db71994STimur Tabi }
4086db71994STimur Tabi
4096db71994STimur Tabi /*
4106db71994STimur Tabi * Ioctl main entry point
4116db71994STimur Tabi */
fsl_hv_ioctl(struct file * file,unsigned int cmd,unsigned long argaddr)4126db71994STimur Tabi static long fsl_hv_ioctl(struct file *file, unsigned int cmd,
4136db71994STimur Tabi unsigned long argaddr)
4146db71994STimur Tabi {
4156db71994STimur Tabi void __user *arg = (void __user *)argaddr;
4166db71994STimur Tabi long ret;
4176db71994STimur Tabi
4186db71994STimur Tabi switch (cmd) {
4196db71994STimur Tabi case FSL_HV_IOCTL_PARTITION_RESTART:
4206db71994STimur Tabi ret = ioctl_restart(arg);
4216db71994STimur Tabi break;
4226db71994STimur Tabi case FSL_HV_IOCTL_PARTITION_GET_STATUS:
4236db71994STimur Tabi ret = ioctl_status(arg);
4246db71994STimur Tabi break;
4256db71994STimur Tabi case FSL_HV_IOCTL_PARTITION_START:
4266db71994STimur Tabi ret = ioctl_start(arg);
4276db71994STimur Tabi break;
4286db71994STimur Tabi case FSL_HV_IOCTL_PARTITION_STOP:
4296db71994STimur Tabi ret = ioctl_stop(arg);
4306db71994STimur Tabi break;
4316db71994STimur Tabi case FSL_HV_IOCTL_MEMCPY:
4326db71994STimur Tabi ret = ioctl_memcpy(arg);
4336db71994STimur Tabi break;
4346db71994STimur Tabi case FSL_HV_IOCTL_DOORBELL:
4356db71994STimur Tabi ret = ioctl_doorbell(arg);
4366db71994STimur Tabi break;
4376db71994STimur Tabi case FSL_HV_IOCTL_GETPROP:
4386db71994STimur Tabi ret = ioctl_dtprop(arg, 0);
4396db71994STimur Tabi break;
4406db71994STimur Tabi case FSL_HV_IOCTL_SETPROP:
4416db71994STimur Tabi ret = ioctl_dtprop(arg, 1);
4426db71994STimur Tabi break;
4436db71994STimur Tabi default:
4446db71994STimur Tabi pr_debug("fsl-hv: bad ioctl dir=%u type=%u cmd=%u size=%u\n",
4456db71994STimur Tabi _IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd),
4466db71994STimur Tabi _IOC_SIZE(cmd));
4476db71994STimur Tabi return -ENOTTY;
4486db71994STimur Tabi }
4496db71994STimur Tabi
4506db71994STimur Tabi return ret;
4516db71994STimur Tabi }
4526db71994STimur Tabi
4536db71994STimur Tabi /* Linked list of processes that have us open */
4546db71994STimur Tabi static struct list_head db_list;
4556db71994STimur Tabi
4566db71994STimur Tabi /* spinlock for db_list */
4576db71994STimur Tabi static DEFINE_SPINLOCK(db_list_lock);
4586db71994STimur Tabi
4596db71994STimur Tabi /* The size of the doorbell event queue. This must be a power of two. */
4606db71994STimur Tabi #define QSIZE 16
4616db71994STimur Tabi
4626db71994STimur Tabi /* Returns the next head/tail pointer, wrapping around the queue if necessary */
4636db71994STimur Tabi #define nextp(x) (((x) + 1) & (QSIZE - 1))
4646db71994STimur Tabi
4656db71994STimur Tabi /* Per-open data structure */
4666db71994STimur Tabi struct doorbell_queue {
4676db71994STimur Tabi struct list_head list;
4686db71994STimur Tabi spinlock_t lock;
4696db71994STimur Tabi wait_queue_head_t wait;
4706db71994STimur Tabi unsigned int head;
4716db71994STimur Tabi unsigned int tail;
4726db71994STimur Tabi uint32_t q[QSIZE];
4736db71994STimur Tabi };
4746db71994STimur Tabi
4756db71994STimur Tabi /* Linked list of ISRs that we registered */
4766db71994STimur Tabi struct list_head isr_list;
4776db71994STimur Tabi
4786db71994STimur Tabi /* Per-ISR data structure */
4796db71994STimur Tabi struct doorbell_isr {
4806db71994STimur Tabi struct list_head list;
4816db71994STimur Tabi unsigned int irq;
4826db71994STimur Tabi uint32_t doorbell; /* The doorbell handle */
4836db71994STimur Tabi uint32_t partition; /* The partition handle, if used */
4846db71994STimur Tabi };
4856db71994STimur Tabi
4866db71994STimur Tabi /*
4876db71994STimur Tabi * Add a doorbell to all of the doorbell queues
4886db71994STimur Tabi */
fsl_hv_queue_doorbell(uint32_t doorbell)4896db71994STimur Tabi static void fsl_hv_queue_doorbell(uint32_t doorbell)
4906db71994STimur Tabi {
4916db71994STimur Tabi struct doorbell_queue *dbq;
4926db71994STimur Tabi unsigned long flags;
4936db71994STimur Tabi
4946db71994STimur Tabi /* Prevent another core from modifying db_list */
4956db71994STimur Tabi spin_lock_irqsave(&db_list_lock, flags);
4966db71994STimur Tabi
4976db71994STimur Tabi list_for_each_entry(dbq, &db_list, list) {
4986db71994STimur Tabi if (dbq->head != nextp(dbq->tail)) {
4996db71994STimur Tabi dbq->q[dbq->tail] = doorbell;
5006db71994STimur Tabi /*
5016db71994STimur Tabi * This memory barrier eliminates the need to grab
5026db71994STimur Tabi * the spinlock for dbq.
5036db71994STimur Tabi */
5046db71994STimur Tabi smp_wmb();
5056db71994STimur Tabi dbq->tail = nextp(dbq->tail);
5066db71994STimur Tabi wake_up_interruptible(&dbq->wait);
5076db71994STimur Tabi }
5086db71994STimur Tabi }
5096db71994STimur Tabi
5106db71994STimur Tabi spin_unlock_irqrestore(&db_list_lock, flags);
5116db71994STimur Tabi }
5126db71994STimur Tabi
5136db71994STimur Tabi /*
5146db71994STimur Tabi * Interrupt handler for all doorbells
5156db71994STimur Tabi *
5166db71994STimur Tabi * We use the same interrupt handler for all doorbells. Whenever a doorbell
5176db71994STimur Tabi * is rung, and we receive an interrupt, we just put the handle for that
5186db71994STimur Tabi * doorbell (passed to us as *data) into all of the queues.
5196db71994STimur Tabi */
fsl_hv_isr(int irq,void * data)5206db71994STimur Tabi static irqreturn_t fsl_hv_isr(int irq, void *data)
5216db71994STimur Tabi {
5226db71994STimur Tabi fsl_hv_queue_doorbell((uintptr_t) data);
5236db71994STimur Tabi
5246db71994STimur Tabi return IRQ_HANDLED;
5256db71994STimur Tabi }
5266db71994STimur Tabi
5276db71994STimur Tabi /*
5286db71994STimur Tabi * State change thread function
5296db71994STimur Tabi *
5306db71994STimur Tabi * The state change notification arrives in an interrupt, but we can't call
5316db71994STimur Tabi * blocking_notifier_call_chain() in an interrupt handler. We could call
5326db71994STimur Tabi * atomic_notifier_call_chain(), but that would require the clients' call-back
5336db71994STimur Tabi * function to run in interrupt context. Since we don't want to impose that
5346db71994STimur Tabi * restriction on the clients, we use a threaded IRQ to process the
5356db71994STimur Tabi * notification in kernel context.
5366db71994STimur Tabi */
fsl_hv_state_change_thread(int irq,void * data)5376db71994STimur Tabi static irqreturn_t fsl_hv_state_change_thread(int irq, void *data)
5386db71994STimur Tabi {
5396db71994STimur Tabi struct doorbell_isr *dbisr = data;
5406db71994STimur Tabi
5416db71994STimur Tabi blocking_notifier_call_chain(&failover_subscribers, dbisr->partition,
5426db71994STimur Tabi NULL);
5436db71994STimur Tabi
5446db71994STimur Tabi return IRQ_HANDLED;
5456db71994STimur Tabi }
5466db71994STimur Tabi
5476db71994STimur Tabi /*
5486db71994STimur Tabi * Interrupt handler for state-change doorbells
5496db71994STimur Tabi */
fsl_hv_state_change_isr(int irq,void * data)5506db71994STimur Tabi static irqreturn_t fsl_hv_state_change_isr(int irq, void *data)
5516db71994STimur Tabi {
5526db71994STimur Tabi unsigned int status;
5536db71994STimur Tabi struct doorbell_isr *dbisr = data;
5546db71994STimur Tabi int ret;
5556db71994STimur Tabi
5566db71994STimur Tabi /* It's still a doorbell, so add it to all the queues. */
5576db71994STimur Tabi fsl_hv_queue_doorbell(dbisr->doorbell);
5586db71994STimur Tabi
5596db71994STimur Tabi /* Determine the new state, and if it's stopped, notify the clients. */
5606db71994STimur Tabi ret = fh_partition_get_status(dbisr->partition, &status);
5616db71994STimur Tabi if (!ret && (status == FH_PARTITION_STOPPED))
5626db71994STimur Tabi return IRQ_WAKE_THREAD;
5636db71994STimur Tabi
5646db71994STimur Tabi return IRQ_HANDLED;
5656db71994STimur Tabi }
5666db71994STimur Tabi
5676db71994STimur Tabi /*
5686db71994STimur Tabi * Returns a bitmask indicating whether a read will block
5696db71994STimur Tabi */
fsl_hv_poll(struct file * filp,struct poll_table_struct * p)570afc9a42bSAl Viro static __poll_t fsl_hv_poll(struct file *filp, struct poll_table_struct *p)
5716db71994STimur Tabi {
5726db71994STimur Tabi struct doorbell_queue *dbq = filp->private_data;
5736db71994STimur Tabi unsigned long flags;
574afc9a42bSAl Viro __poll_t mask;
5756db71994STimur Tabi
5766db71994STimur Tabi spin_lock_irqsave(&dbq->lock, flags);
5776db71994STimur Tabi
5786db71994STimur Tabi poll_wait(filp, &dbq->wait, p);
579a9a08845SLinus Torvalds mask = (dbq->head == dbq->tail) ? 0 : (EPOLLIN | EPOLLRDNORM);
5806db71994STimur Tabi
5816db71994STimur Tabi spin_unlock_irqrestore(&dbq->lock, flags);
5826db71994STimur Tabi
5836db71994STimur Tabi return mask;
5846db71994STimur Tabi }
5856db71994STimur Tabi
5866db71994STimur Tabi /*
5876db71994STimur Tabi * Return the handles for any incoming doorbells
5886db71994STimur Tabi *
5896db71994STimur Tabi * If there are doorbell handles in the queue for this open instance, then
5906db71994STimur Tabi * return them to the caller as an array of 32-bit integers. Otherwise,
5916db71994STimur Tabi * block until there is at least one handle to return.
5926db71994STimur Tabi */
fsl_hv_read(struct file * filp,char __user * buf,size_t len,loff_t * off)5936db71994STimur Tabi static ssize_t fsl_hv_read(struct file *filp, char __user *buf, size_t len,
5946db71994STimur Tabi loff_t *off)
5956db71994STimur Tabi {
5966db71994STimur Tabi struct doorbell_queue *dbq = filp->private_data;
5976db71994STimur Tabi uint32_t __user *p = (uint32_t __user *) buf; /* for put_user() */
5986db71994STimur Tabi unsigned long flags;
5996db71994STimur Tabi ssize_t count = 0;
6006db71994STimur Tabi
6016db71994STimur Tabi /* Make sure we stop when the user buffer is full. */
6026db71994STimur Tabi while (len >= sizeof(uint32_t)) {
6036db71994STimur Tabi uint32_t dbell; /* Local copy of doorbell queue data */
6046db71994STimur Tabi
6056db71994STimur Tabi spin_lock_irqsave(&dbq->lock, flags);
6066db71994STimur Tabi
6076db71994STimur Tabi /*
6086db71994STimur Tabi * If the queue is empty, then either we're done or we need
6096db71994STimur Tabi * to block. If the application specified O_NONBLOCK, then
6106db71994STimur Tabi * we return the appropriate error code.
6116db71994STimur Tabi */
6126db71994STimur Tabi if (dbq->head == dbq->tail) {
6136db71994STimur Tabi spin_unlock_irqrestore(&dbq->lock, flags);
6146db71994STimur Tabi if (count)
6156db71994STimur Tabi break;
6166db71994STimur Tabi if (filp->f_flags & O_NONBLOCK)
6176db71994STimur Tabi return -EAGAIN;
6186db71994STimur Tabi if (wait_event_interruptible(dbq->wait,
6196db71994STimur Tabi dbq->head != dbq->tail))
6206db71994STimur Tabi return -ERESTARTSYS;
6216db71994STimur Tabi continue;
6226db71994STimur Tabi }
6236db71994STimur Tabi
6246db71994STimur Tabi /*
6256db71994STimur Tabi * Even though we have an smp_wmb() in the ISR, the core
6266db71994STimur Tabi * might speculatively execute the "dbell = ..." below while
6276db71994STimur Tabi * it's evaluating the if-statement above. In that case, the
6286db71994STimur Tabi * value put into dbell could be stale if the core accepts the
6296db71994STimur Tabi * speculation. To prevent that, we need a read memory barrier
6306db71994STimur Tabi * here as well.
6316db71994STimur Tabi */
6326db71994STimur Tabi smp_rmb();
6336db71994STimur Tabi
6346db71994STimur Tabi /* Copy the data to a temporary local buffer, because
6356db71994STimur Tabi * we can't call copy_to_user() from inside a spinlock
6366db71994STimur Tabi */
6376db71994STimur Tabi dbell = dbq->q[dbq->head];
6386db71994STimur Tabi dbq->head = nextp(dbq->head);
6396db71994STimur Tabi
6406db71994STimur Tabi spin_unlock_irqrestore(&dbq->lock, flags);
6416db71994STimur Tabi
6426db71994STimur Tabi if (put_user(dbell, p))
6436db71994STimur Tabi return -EFAULT;
6446db71994STimur Tabi p++;
6456db71994STimur Tabi count += sizeof(uint32_t);
6466db71994STimur Tabi len -= sizeof(uint32_t);
6476db71994STimur Tabi }
6486db71994STimur Tabi
6496db71994STimur Tabi return count;
6506db71994STimur Tabi }
6516db71994STimur Tabi
6526db71994STimur Tabi /*
6536db71994STimur Tabi * Open the driver and prepare for reading doorbells.
6546db71994STimur Tabi *
6556db71994STimur Tabi * Every time an application opens the driver, we create a doorbell queue
6566db71994STimur Tabi * for that file handle. This queue is used for any incoming doorbells.
6576db71994STimur Tabi */
fsl_hv_open(struct inode * inode,struct file * filp)6586db71994STimur Tabi static int fsl_hv_open(struct inode *inode, struct file *filp)
6596db71994STimur Tabi {
6606db71994STimur Tabi struct doorbell_queue *dbq;
6616db71994STimur Tabi unsigned long flags;
6626db71994STimur Tabi
6636db71994STimur Tabi dbq = kzalloc(sizeof(struct doorbell_queue), GFP_KERNEL);
6646db71994STimur Tabi if (!dbq) {
6656db71994STimur Tabi pr_err("fsl-hv: out of memory\n");
6666db71994STimur Tabi return -ENOMEM;
6676db71994STimur Tabi }
6686db71994STimur Tabi
6696db71994STimur Tabi spin_lock_init(&dbq->lock);
6706db71994STimur Tabi init_waitqueue_head(&dbq->wait);
6716db71994STimur Tabi
6726db71994STimur Tabi spin_lock_irqsave(&db_list_lock, flags);
6736db71994STimur Tabi list_add(&dbq->list, &db_list);
6746db71994STimur Tabi spin_unlock_irqrestore(&db_list_lock, flags);
6756db71994STimur Tabi
6766db71994STimur Tabi filp->private_data = dbq;
6776db71994STimur Tabi
6784647769bSGuo Zhengkui return 0;
6796db71994STimur Tabi }
6806db71994STimur Tabi
6816db71994STimur Tabi /*
6826db71994STimur Tabi * Close the driver
6836db71994STimur Tabi */
fsl_hv_close(struct inode * inode,struct file * filp)6846db71994STimur Tabi static int fsl_hv_close(struct inode *inode, struct file *filp)
6856db71994STimur Tabi {
6866db71994STimur Tabi struct doorbell_queue *dbq = filp->private_data;
6876db71994STimur Tabi unsigned long flags;
6886db71994STimur Tabi
6896db71994STimur Tabi spin_lock_irqsave(&db_list_lock, flags);
6906db71994STimur Tabi list_del(&dbq->list);
6916db71994STimur Tabi spin_unlock_irqrestore(&db_list_lock, flags);
6926db71994STimur Tabi
6936db71994STimur Tabi kfree(dbq);
6946db71994STimur Tabi
695b734fed0SHaowen Bai return 0;
6966db71994STimur Tabi }
6976db71994STimur Tabi
6986db71994STimur Tabi static const struct file_operations fsl_hv_fops = {
6996db71994STimur Tabi .owner = THIS_MODULE,
7006db71994STimur Tabi .open = fsl_hv_open,
7016db71994STimur Tabi .release = fsl_hv_close,
7026db71994STimur Tabi .poll = fsl_hv_poll,
7036db71994STimur Tabi .read = fsl_hv_read,
7046db71994STimur Tabi .unlocked_ioctl = fsl_hv_ioctl,
7051832f2d8SArnd Bergmann .compat_ioctl = compat_ptr_ioctl,
7066db71994STimur Tabi };
7076db71994STimur Tabi
7086db71994STimur Tabi static struct miscdevice fsl_hv_misc_dev = {
7096db71994STimur Tabi MISC_DYNAMIC_MINOR,
7106db71994STimur Tabi "fsl-hv",
7116db71994STimur Tabi &fsl_hv_fops
7126db71994STimur Tabi };
7136db71994STimur Tabi
fsl_hv_shutdown_isr(int irq,void * data)7146db71994STimur Tabi static irqreturn_t fsl_hv_shutdown_isr(int irq, void *data)
7156db71994STimur Tabi {
7166db71994STimur Tabi orderly_poweroff(false);
7176db71994STimur Tabi
7186db71994STimur Tabi return IRQ_HANDLED;
7196db71994STimur Tabi }
7206db71994STimur Tabi
7216db71994STimur Tabi /*
7226db71994STimur Tabi * Returns the handle of the parent of the given node
7236db71994STimur Tabi *
7246db71994STimur Tabi * The handle is the value of the 'hv-handle' property
7256db71994STimur Tabi */
get_parent_handle(struct device_node * np)7266db71994STimur Tabi static int get_parent_handle(struct device_node *np)
7276db71994STimur Tabi {
7286db71994STimur Tabi struct device_node *parent;
7296db71994STimur Tabi const uint32_t *prop;
7306db71994STimur Tabi uint32_t handle;
7316db71994STimur Tabi int len;
7326db71994STimur Tabi
7336db71994STimur Tabi parent = of_get_parent(np);
7346db71994STimur Tabi if (!parent)
7356db71994STimur Tabi /* It's not really possible for this to fail */
7366db71994STimur Tabi return -ENODEV;
7376db71994STimur Tabi
7386db71994STimur Tabi /*
7396db71994STimur Tabi * The proper name for the handle property is "hv-handle", but some
7406db71994STimur Tabi * older versions of the hypervisor used "reg".
7416db71994STimur Tabi */
7426db71994STimur Tabi prop = of_get_property(parent, "hv-handle", &len);
7436db71994STimur Tabi if (!prop)
7446db71994STimur Tabi prop = of_get_property(parent, "reg", &len);
7456db71994STimur Tabi
7466db71994STimur Tabi if (!prop || (len != sizeof(uint32_t))) {
7476db71994STimur Tabi /* This can happen only if the node is malformed */
7486db71994STimur Tabi of_node_put(parent);
7496db71994STimur Tabi return -ENODEV;
7506db71994STimur Tabi }
7516db71994STimur Tabi
7526db71994STimur Tabi handle = be32_to_cpup(prop);
7536db71994STimur Tabi of_node_put(parent);
7546db71994STimur Tabi
7556db71994STimur Tabi return handle;
7566db71994STimur Tabi }
7576db71994STimur Tabi
7586db71994STimur Tabi /*
7596db71994STimur Tabi * Register a callback for failover events
7606db71994STimur Tabi *
7616db71994STimur Tabi * This function is called by device drivers to register their callback
7626db71994STimur Tabi * functions for fail-over events.
7636db71994STimur Tabi */
fsl_hv_failover_register(struct notifier_block * nb)7646db71994STimur Tabi int fsl_hv_failover_register(struct notifier_block *nb)
7656db71994STimur Tabi {
7666db71994STimur Tabi return blocking_notifier_chain_register(&failover_subscribers, nb);
7676db71994STimur Tabi }
7686db71994STimur Tabi EXPORT_SYMBOL(fsl_hv_failover_register);
7696db71994STimur Tabi
7706db71994STimur Tabi /*
7716db71994STimur Tabi * Unregister a callback for failover events
7726db71994STimur Tabi */
fsl_hv_failover_unregister(struct notifier_block * nb)7736db71994STimur Tabi int fsl_hv_failover_unregister(struct notifier_block *nb)
7746db71994STimur Tabi {
7756db71994STimur Tabi return blocking_notifier_chain_unregister(&failover_subscribers, nb);
7766db71994STimur Tabi }
7776db71994STimur Tabi EXPORT_SYMBOL(fsl_hv_failover_unregister);
7786db71994STimur Tabi
7796db71994STimur Tabi /*
7806db71994STimur Tabi * Return TRUE if we're running under FSL hypervisor
7816db71994STimur Tabi *
7826db71994STimur Tabi * This function checks to see if we're running under the Freescale
7836db71994STimur Tabi * hypervisor, and returns zero if we're not, or non-zero if we are.
7846db71994STimur Tabi *
7856db71994STimur Tabi * First, it checks if MSR[GS]==1, which means we're running under some
7866db71994STimur Tabi * hypervisor. Then it checks if there is a hypervisor node in the device
7876db71994STimur Tabi * tree. Currently, that means there needs to be a node in the root called
7886db71994STimur Tabi * "hypervisor" and which has a property named "fsl,hv-version".
7896db71994STimur Tabi */
has_fsl_hypervisor(void)7906db71994STimur Tabi static int has_fsl_hypervisor(void)
7916db71994STimur Tabi {
7926db71994STimur Tabi struct device_node *node;
7936db71994STimur Tabi int ret;
7946db71994STimur Tabi
7956db71994STimur Tabi node = of_find_node_by_path("/hypervisor");
7966db71994STimur Tabi if (!node)
7976db71994STimur Tabi return 0;
7986db71994STimur Tabi
799*10ce6b70SRob Herring ret = of_property_present(node, "fsl,hv-version");
8006db71994STimur Tabi
8016db71994STimur Tabi of_node_put(node);
8026db71994STimur Tabi
8036db71994STimur Tabi return ret;
8046db71994STimur Tabi }
8056db71994STimur Tabi
8066db71994STimur Tabi /*
8076db71994STimur Tabi * Freescale hypervisor management driver init
8086db71994STimur Tabi *
8096db71994STimur Tabi * This function is called when this module is loaded.
8106db71994STimur Tabi *
8116db71994STimur Tabi * Register ourselves as a miscellaneous driver. This will register the
8126db71994STimur Tabi * fops structure and create the right sysfs entries for udev.
8136db71994STimur Tabi */
fsl_hypervisor_init(void)8146db71994STimur Tabi static int __init fsl_hypervisor_init(void)
8156db71994STimur Tabi {
8166db71994STimur Tabi struct device_node *np;
8176db71994STimur Tabi struct doorbell_isr *dbisr, *n;
8186db71994STimur Tabi int ret;
8196db71994STimur Tabi
8206db71994STimur Tabi pr_info("Freescale hypervisor management driver\n");
8216db71994STimur Tabi
8226db71994STimur Tabi if (!has_fsl_hypervisor()) {
8236db71994STimur Tabi pr_info("fsl-hv: no hypervisor found\n");
8246db71994STimur Tabi return -ENODEV;
8256db71994STimur Tabi }
8266db71994STimur Tabi
8276db71994STimur Tabi ret = misc_register(&fsl_hv_misc_dev);
8286db71994STimur Tabi if (ret) {
8296db71994STimur Tabi pr_err("fsl-hv: cannot register device\n");
8306db71994STimur Tabi return ret;
8316db71994STimur Tabi }
8326db71994STimur Tabi
8336db71994STimur Tabi INIT_LIST_HEAD(&db_list);
8346db71994STimur Tabi INIT_LIST_HEAD(&isr_list);
8356db71994STimur Tabi
8366db71994STimur Tabi for_each_compatible_node(np, NULL, "epapr,hv-receive-doorbell") {
8376db71994STimur Tabi unsigned int irq;
8386db71994STimur Tabi const uint32_t *handle;
8396db71994STimur Tabi
8406db71994STimur Tabi handle = of_get_property(np, "interrupts", NULL);
8416db71994STimur Tabi irq = irq_of_parse_and_map(np, 0);
84202c39bbbSChristophe Leroy if (!handle || !irq) {
84355e18b18SRob Herring pr_err("fsl-hv: no 'interrupts' property in %pOF node\n",
84455e18b18SRob Herring np);
8456db71994STimur Tabi continue;
8466db71994STimur Tabi }
8476db71994STimur Tabi
8486db71994STimur Tabi dbisr = kzalloc(sizeof(*dbisr), GFP_KERNEL);
8496db71994STimur Tabi if (!dbisr)
8506db71994STimur Tabi goto out_of_memory;
8516db71994STimur Tabi
8526db71994STimur Tabi dbisr->irq = irq;
8536db71994STimur Tabi dbisr->doorbell = be32_to_cpup(handle);
8546db71994STimur Tabi
8556db71994STimur Tabi if (of_device_is_compatible(np, "fsl,hv-shutdown-doorbell")) {
8566db71994STimur Tabi /* The shutdown doorbell gets its own ISR */
8576db71994STimur Tabi ret = request_irq(irq, fsl_hv_shutdown_isr, 0,
8586db71994STimur Tabi np->name, NULL);
8596db71994STimur Tabi } else if (of_device_is_compatible(np,
8606db71994STimur Tabi "fsl,hv-state-change-doorbell")) {
8616db71994STimur Tabi /*
8626db71994STimur Tabi * The state change doorbell triggers a notification if
8636db71994STimur Tabi * the state of the managed partition changes to
8646db71994STimur Tabi * "stopped". We need a separate interrupt handler for
8656db71994STimur Tabi * that, and we also need to know the handle of the
8666db71994STimur Tabi * target partition, not just the handle of the
8676db71994STimur Tabi * doorbell.
8686db71994STimur Tabi */
8696db71994STimur Tabi dbisr->partition = ret = get_parent_handle(np);
8706db71994STimur Tabi if (ret < 0) {
87155e18b18SRob Herring pr_err("fsl-hv: node %pOF has missing or "
87255e18b18SRob Herring "malformed parent\n", np);
8736db71994STimur Tabi kfree(dbisr);
8746db71994STimur Tabi continue;
8756db71994STimur Tabi }
8766db71994STimur Tabi ret = request_threaded_irq(irq, fsl_hv_state_change_isr,
8776db71994STimur Tabi fsl_hv_state_change_thread,
8786db71994STimur Tabi 0, np->name, dbisr);
8796db71994STimur Tabi } else
8806db71994STimur Tabi ret = request_irq(irq, fsl_hv_isr, 0, np->name, dbisr);
8816db71994STimur Tabi
8826db71994STimur Tabi if (ret < 0) {
88355e18b18SRob Herring pr_err("fsl-hv: could not request irq %u for node %pOF\n",
88455e18b18SRob Herring irq, np);
8856db71994STimur Tabi kfree(dbisr);
8866db71994STimur Tabi continue;
8876db71994STimur Tabi }
8886db71994STimur Tabi
8896db71994STimur Tabi list_add(&dbisr->list, &isr_list);
8906db71994STimur Tabi
8916db71994STimur Tabi pr_info("fsl-hv: registered handler for doorbell %u\n",
8926db71994STimur Tabi dbisr->doorbell);
8936db71994STimur Tabi }
8946db71994STimur Tabi
8956db71994STimur Tabi return 0;
8966db71994STimur Tabi
8976db71994STimur Tabi out_of_memory:
8986db71994STimur Tabi list_for_each_entry_safe(dbisr, n, &isr_list, list) {
8996db71994STimur Tabi free_irq(dbisr->irq, dbisr);
9006db71994STimur Tabi list_del(&dbisr->list);
9016db71994STimur Tabi kfree(dbisr);
9026db71994STimur Tabi }
9036db71994STimur Tabi
9046db71994STimur Tabi misc_deregister(&fsl_hv_misc_dev);
9056db71994STimur Tabi
9066db71994STimur Tabi return -ENOMEM;
9076db71994STimur Tabi }
9086db71994STimur Tabi
9096db71994STimur Tabi /*
9106db71994STimur Tabi * Freescale hypervisor management driver termination
9116db71994STimur Tabi *
9126db71994STimur Tabi * This function is called when this driver is unloaded.
9136db71994STimur Tabi */
fsl_hypervisor_exit(void)9146db71994STimur Tabi static void __exit fsl_hypervisor_exit(void)
9156db71994STimur Tabi {
9166db71994STimur Tabi struct doorbell_isr *dbisr, *n;
9176db71994STimur Tabi
9186db71994STimur Tabi list_for_each_entry_safe(dbisr, n, &isr_list, list) {
9196db71994STimur Tabi free_irq(dbisr->irq, dbisr);
9206db71994STimur Tabi list_del(&dbisr->list);
9216db71994STimur Tabi kfree(dbisr);
9226db71994STimur Tabi }
9236db71994STimur Tabi
9246db71994STimur Tabi misc_deregister(&fsl_hv_misc_dev);
9256db71994STimur Tabi }
9266db71994STimur Tabi
9276db71994STimur Tabi module_init(fsl_hypervisor_init);
9286db71994STimur Tabi module_exit(fsl_hypervisor_exit);
9296db71994STimur Tabi
9306db71994STimur Tabi MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
9316db71994STimur Tabi MODULE_DESCRIPTION("Freescale hypervisor management driver");
9326db71994STimur Tabi MODULE_LICENSE("GPL v2");
933