linux/drivers/vfio/platform/vfio_platform_common.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2013 - Virtual Open Systems
   3 * Author: Antonios Motakis <a.motakis@virtualopensystems.com>
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License, version 2, as
   7 * published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 */
  14
  15#include <linux/device.h>
  16#include <linux/acpi.h>
  17#include <linux/iommu.h>
  18#include <linux/module.h>
  19#include <linux/mutex.h>
  20#include <linux/slab.h>
  21#include <linux/types.h>
  22#include <linux/uaccess.h>
  23#include <linux/vfio.h>
  24
  25#include "vfio_platform_private.h"
  26
  27#define DRIVER_VERSION  "0.10"
  28#define DRIVER_AUTHOR   "Antonios Motakis <a.motakis@virtualopensystems.com>"
  29#define DRIVER_DESC     "VFIO platform base module"
  30
  31#define VFIO_PLATFORM_IS_ACPI(vdev) ((vdev)->acpihid != NULL)
  32
  33static LIST_HEAD(reset_list);
  34static DEFINE_MUTEX(driver_lock);
  35
  36static vfio_platform_reset_fn_t vfio_platform_lookup_reset(const char *compat,
  37                                        struct module **module)
  38{
  39        struct vfio_platform_reset_node *iter;
  40        vfio_platform_reset_fn_t reset_fn = NULL;
  41
  42        mutex_lock(&driver_lock);
  43        list_for_each_entry(iter, &reset_list, link) {
  44                if (!strcmp(iter->compat, compat) &&
  45                        try_module_get(iter->owner)) {
  46                        *module = iter->owner;
  47                        reset_fn = iter->of_reset;
  48                        break;
  49                }
  50        }
  51        mutex_unlock(&driver_lock);
  52        return reset_fn;
  53}
  54
  55static int vfio_platform_acpi_probe(struct vfio_platform_device *vdev,
  56                                    struct device *dev)
  57{
  58        struct acpi_device *adev;
  59
  60        if (acpi_disabled)
  61                return -ENOENT;
  62
  63        adev = ACPI_COMPANION(dev);
  64        if (!adev) {
  65                pr_err("VFIO: ACPI companion device not found for %s\n",
  66                        vdev->name);
  67                return -ENODEV;
  68        }
  69
  70#ifdef CONFIG_ACPI
  71        vdev->acpihid = acpi_device_hid(adev);
  72#endif
  73        return WARN_ON(!vdev->acpihid) ? -EINVAL : 0;
  74}
  75
  76static int vfio_platform_acpi_call_reset(struct vfio_platform_device *vdev,
  77                                  const char **extra_dbg)
  78{
  79#ifdef CONFIG_ACPI
  80        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
  81        struct device *dev = vdev->device;
  82        acpi_handle handle = ACPI_HANDLE(dev);
  83        acpi_status acpi_ret;
  84
  85        acpi_ret = acpi_evaluate_object(handle, "_RST", NULL, &buffer);
  86        if (ACPI_FAILURE(acpi_ret)) {
  87                if (extra_dbg)
  88                        *extra_dbg = acpi_format_exception(acpi_ret);
  89                return -EINVAL;
  90        }
  91
  92        return 0;
  93#else
  94        return -ENOENT;
  95#endif
  96}
  97
  98static bool vfio_platform_acpi_has_reset(struct vfio_platform_device *vdev)
  99{
 100#ifdef CONFIG_ACPI
 101        struct device *dev = vdev->device;
 102        acpi_handle handle = ACPI_HANDLE(dev);
 103
 104        return acpi_has_method(handle, "_RST");
 105#else
 106        return false;
 107#endif
 108}
 109
 110static bool vfio_platform_has_reset(struct vfio_platform_device *vdev)
 111{
 112        if (VFIO_PLATFORM_IS_ACPI(vdev))
 113                return vfio_platform_acpi_has_reset(vdev);
 114
 115        return vdev->of_reset ? true : false;
 116}
 117
 118static int vfio_platform_get_reset(struct vfio_platform_device *vdev)
 119{
 120        if (VFIO_PLATFORM_IS_ACPI(vdev))
 121                return vfio_platform_acpi_has_reset(vdev) ? 0 : -ENOENT;
 122
 123        vdev->of_reset = vfio_platform_lookup_reset(vdev->compat,
 124                                                    &vdev->reset_module);
 125        if (!vdev->of_reset) {
 126                request_module("vfio-reset:%s", vdev->compat);
 127                vdev->of_reset = vfio_platform_lookup_reset(vdev->compat,
 128                                                        &vdev->reset_module);
 129        }
 130
 131        return vdev->of_reset ? 0 : -ENOENT;
 132}
 133
 134static void vfio_platform_put_reset(struct vfio_platform_device *vdev)
 135{
 136        if (VFIO_PLATFORM_IS_ACPI(vdev))
 137                return;
 138
 139        if (vdev->of_reset)
 140                module_put(vdev->reset_module);
 141}
 142
 143static int vfio_platform_regions_init(struct vfio_platform_device *vdev)
 144{
 145        int cnt = 0, i;
 146
 147        while (vdev->get_resource(vdev, cnt))
 148                cnt++;
 149
 150        vdev->regions = kcalloc(cnt, sizeof(struct vfio_platform_region),
 151                                GFP_KERNEL);
 152        if (!vdev->regions)
 153                return -ENOMEM;
 154
 155        for (i = 0; i < cnt;  i++) {
 156                struct resource *res =
 157                        vdev->get_resource(vdev, i);
 158
 159                if (!res)
 160                        goto err;
 161
 162                vdev->regions[i].addr = res->start;
 163                vdev->regions[i].size = resource_size(res);
 164                vdev->regions[i].flags = 0;
 165
 166                switch (resource_type(res)) {
 167                case IORESOURCE_MEM:
 168                        vdev->regions[i].type = VFIO_PLATFORM_REGION_TYPE_MMIO;
 169                        vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_READ;
 170                        if (!(res->flags & IORESOURCE_READONLY))
 171                                vdev->regions[i].flags |=
 172                                        VFIO_REGION_INFO_FLAG_WRITE;
 173
 174                        /*
 175                         * Only regions addressed with PAGE granularity may be
 176                         * MMAPed securely.
 177                         */
 178                        if (!(vdev->regions[i].addr & ~PAGE_MASK) &&
 179                                        !(vdev->regions[i].size & ~PAGE_MASK))
 180                                vdev->regions[i].flags |=
 181                                        VFIO_REGION_INFO_FLAG_MMAP;
 182
 183                        break;
 184                case IORESOURCE_IO:
 185                        vdev->regions[i].type = VFIO_PLATFORM_REGION_TYPE_PIO;
 186                        break;
 187                default:
 188                        goto err;
 189                }
 190        }
 191
 192        vdev->num_regions = cnt;
 193
 194        return 0;
 195err:
 196        kfree(vdev->regions);
 197        return -EINVAL;
 198}
 199
 200static void vfio_platform_regions_cleanup(struct vfio_platform_device *vdev)
 201{
 202        int i;
 203
 204        for (i = 0; i < vdev->num_regions; i++)
 205                iounmap(vdev->regions[i].ioaddr);
 206
 207        vdev->num_regions = 0;
 208        kfree(vdev->regions);
 209}
 210
 211static int vfio_platform_call_reset(struct vfio_platform_device *vdev,
 212                                    const char **extra_dbg)
 213{
 214        if (VFIO_PLATFORM_IS_ACPI(vdev)) {
 215                dev_info(vdev->device, "reset\n");
 216                return vfio_platform_acpi_call_reset(vdev, extra_dbg);
 217        } else if (vdev->of_reset) {
 218                dev_info(vdev->device, "reset\n");
 219                return vdev->of_reset(vdev);
 220        }
 221
 222        dev_warn(vdev->device, "no reset function found!\n");
 223        return -EINVAL;
 224}
 225
 226static void vfio_platform_release(void *device_data)
 227{
 228        struct vfio_platform_device *vdev = device_data;
 229
 230        mutex_lock(&driver_lock);
 231
 232        if (!(--vdev->refcnt)) {
 233                const char *extra_dbg = NULL;
 234                int ret;
 235
 236                ret = vfio_platform_call_reset(vdev, &extra_dbg);
 237                if (ret && vdev->reset_required) {
 238                        dev_warn(vdev->device, "reset driver is required and reset call failed in release (%d) %s\n",
 239                                 ret, extra_dbg ? extra_dbg : "");
 240                        WARN_ON(1);
 241                }
 242                vfio_platform_regions_cleanup(vdev);
 243                vfio_platform_irq_cleanup(vdev);
 244        }
 245
 246        mutex_unlock(&driver_lock);
 247
 248        module_put(vdev->parent_module);
 249}
 250
 251static int vfio_platform_open(void *device_data)
 252{
 253        struct vfio_platform_device *vdev = device_data;
 254        int ret;
 255
 256        if (!try_module_get(vdev->parent_module))
 257                return -ENODEV;
 258
 259        mutex_lock(&driver_lock);
 260
 261        if (!vdev->refcnt) {
 262                const char *extra_dbg = NULL;
 263
 264                ret = vfio_platform_regions_init(vdev);
 265                if (ret)
 266                        goto err_reg;
 267
 268                ret = vfio_platform_irq_init(vdev);
 269                if (ret)
 270                        goto err_irq;
 271
 272                ret = vfio_platform_call_reset(vdev, &extra_dbg);
 273                if (ret && vdev->reset_required) {
 274                        dev_warn(vdev->device, "reset driver is required and reset call failed in open (%d) %s\n",
 275                                 ret, extra_dbg ? extra_dbg : "");
 276                        goto err_rst;
 277                }
 278        }
 279
 280        vdev->refcnt++;
 281
 282        mutex_unlock(&driver_lock);
 283        return 0;
 284
 285err_rst:
 286        vfio_platform_irq_cleanup(vdev);
 287err_irq:
 288        vfio_platform_regions_cleanup(vdev);
 289err_reg:
 290        mutex_unlock(&driver_lock);
 291        module_put(THIS_MODULE);
 292        return ret;
 293}
 294
 295static long vfio_platform_ioctl(void *device_data,
 296                                unsigned int cmd, unsigned long arg)
 297{
 298        struct vfio_platform_device *vdev = device_data;
 299        unsigned long minsz;
 300
 301        if (cmd == VFIO_DEVICE_GET_INFO) {
 302                struct vfio_device_info info;
 303
 304                minsz = offsetofend(struct vfio_device_info, num_irqs);
 305
 306                if (copy_from_user(&info, (void __user *)arg, minsz))
 307                        return -EFAULT;
 308
 309                if (info.argsz < minsz)
 310                        return -EINVAL;
 311
 312                if (vfio_platform_has_reset(vdev))
 313                        vdev->flags |= VFIO_DEVICE_FLAGS_RESET;
 314                info.flags = vdev->flags;
 315                info.num_regions = vdev->num_regions;
 316                info.num_irqs = vdev->num_irqs;
 317
 318                return copy_to_user((void __user *)arg, &info, minsz) ?
 319                        -EFAULT : 0;
 320
 321        } else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
 322                struct vfio_region_info info;
 323
 324                minsz = offsetofend(struct vfio_region_info, offset);
 325
 326                if (copy_from_user(&info, (void __user *)arg, minsz))
 327                        return -EFAULT;
 328
 329                if (info.argsz < minsz)
 330                        return -EINVAL;
 331
 332                if (info.index >= vdev->num_regions)
 333                        return -EINVAL;
 334
 335                /* map offset to the physical address  */
 336                info.offset = VFIO_PLATFORM_INDEX_TO_OFFSET(info.index);
 337                info.size = vdev->regions[info.index].size;
 338                info.flags = vdev->regions[info.index].flags;
 339
 340                return copy_to_user((void __user *)arg, &info, minsz) ?
 341                        -EFAULT : 0;
 342
 343        } else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) {
 344                struct vfio_irq_info info;
 345
 346                minsz = offsetofend(struct vfio_irq_info, count);
 347
 348                if (copy_from_user(&info, (void __user *)arg, minsz))
 349                        return -EFAULT;
 350
 351                if (info.argsz < minsz)
 352                        return -EINVAL;
 353
 354                if (info.index >= vdev->num_irqs)
 355                        return -EINVAL;
 356
 357                info.flags = vdev->irqs[info.index].flags;
 358                info.count = vdev->irqs[info.index].count;
 359
 360                return copy_to_user((void __user *)arg, &info, minsz) ?
 361                        -EFAULT : 0;
 362
 363        } else if (cmd == VFIO_DEVICE_SET_IRQS) {
 364                struct vfio_irq_set hdr;
 365                u8 *data = NULL;
 366                int ret = 0;
 367                size_t data_size = 0;
 368
 369                minsz = offsetofend(struct vfio_irq_set, count);
 370
 371                if (copy_from_user(&hdr, (void __user *)arg, minsz))
 372                        return -EFAULT;
 373
 374                ret = vfio_set_irqs_validate_and_prepare(&hdr, vdev->num_irqs,
 375                                                 vdev->num_irqs, &data_size);
 376                if (ret)
 377                        return ret;
 378
 379                if (data_size) {
 380                        data = memdup_user((void __user *)(arg + minsz),
 381                                            data_size);
 382                        if (IS_ERR(data))
 383                                return PTR_ERR(data);
 384                }
 385
 386                mutex_lock(&vdev->igate);
 387
 388                ret = vfio_platform_set_irqs_ioctl(vdev, hdr.flags, hdr.index,
 389                                                   hdr.start, hdr.count, data);
 390                mutex_unlock(&vdev->igate);
 391                kfree(data);
 392
 393                return ret;
 394
 395        } else if (cmd == VFIO_DEVICE_RESET) {
 396                return vfio_platform_call_reset(vdev, NULL);
 397        }
 398
 399        return -ENOTTY;
 400}
 401
 402static ssize_t vfio_platform_read_mmio(struct vfio_platform_region *reg,
 403                                       char __user *buf, size_t count,
 404                                       loff_t off)
 405{
 406        unsigned int done = 0;
 407
 408        if (!reg->ioaddr) {
 409                reg->ioaddr =
 410                        ioremap_nocache(reg->addr, reg->size);
 411
 412                if (!reg->ioaddr)
 413                        return -ENOMEM;
 414        }
 415
 416        while (count) {
 417                size_t filled;
 418
 419                if (count >= 4 && !(off % 4)) {
 420                        u32 val;
 421
 422                        val = ioread32(reg->ioaddr + off);
 423                        if (copy_to_user(buf, &val, 4))
 424                                goto err;
 425
 426                        filled = 4;
 427                } else if (count >= 2 && !(off % 2)) {
 428                        u16 val;
 429
 430                        val = ioread16(reg->ioaddr + off);
 431                        if (copy_to_user(buf, &val, 2))
 432                                goto err;
 433
 434                        filled = 2;
 435                } else {
 436                        u8 val;
 437
 438                        val = ioread8(reg->ioaddr + off);
 439                        if (copy_to_user(buf, &val, 1))
 440                                goto err;
 441
 442                        filled = 1;
 443                }
 444
 445
 446                count -= filled;
 447                done += filled;
 448                off += filled;
 449                buf += filled;
 450        }
 451
 452        return done;
 453err:
 454        return -EFAULT;
 455}
 456
 457static ssize_t vfio_platform_read(void *device_data, char __user *buf,
 458                                  size_t count, loff_t *ppos)
 459{
 460        struct vfio_platform_device *vdev = device_data;
 461        unsigned int index = VFIO_PLATFORM_OFFSET_TO_INDEX(*ppos);
 462        loff_t off = *ppos & VFIO_PLATFORM_OFFSET_MASK;
 463
 464        if (index >= vdev->num_regions)
 465                return -EINVAL;
 466
 467        if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ))
 468                return -EINVAL;
 469
 470        if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO)
 471                return vfio_platform_read_mmio(&vdev->regions[index],
 472                                                        buf, count, off);
 473        else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO)
 474                return -EINVAL; /* not implemented */
 475
 476        return -EINVAL;
 477}
 478
 479static ssize_t vfio_platform_write_mmio(struct vfio_platform_region *reg,
 480                                        const char __user *buf, size_t count,
 481                                        loff_t off)
 482{
 483        unsigned int done = 0;
 484
 485        if (!reg->ioaddr) {
 486                reg->ioaddr =
 487                        ioremap_nocache(reg->addr, reg->size);
 488
 489                if (!reg->ioaddr)
 490                        return -ENOMEM;
 491        }
 492
 493        while (count) {
 494                size_t filled;
 495
 496                if (count >= 4 && !(off % 4)) {
 497                        u32 val;
 498
 499                        if (copy_from_user(&val, buf, 4))
 500                                goto err;
 501                        iowrite32(val, reg->ioaddr + off);
 502
 503                        filled = 4;
 504                } else if (count >= 2 && !(off % 2)) {
 505                        u16 val;
 506
 507                        if (copy_from_user(&val, buf, 2))
 508                                goto err;
 509                        iowrite16(val, reg->ioaddr + off);
 510
 511                        filled = 2;
 512                } else {
 513                        u8 val;
 514
 515                        if (copy_from_user(&val, buf, 1))
 516                                goto err;
 517                        iowrite8(val, reg->ioaddr + off);
 518
 519                        filled = 1;
 520                }
 521
 522                count -= filled;
 523                done += filled;
 524                off += filled;
 525                buf += filled;
 526        }
 527
 528        return done;
 529err:
 530        return -EFAULT;
 531}
 532
 533static ssize_t vfio_platform_write(void *device_data, const char __user *buf,
 534                                   size_t count, loff_t *ppos)
 535{
 536        struct vfio_platform_device *vdev = device_data;
 537        unsigned int index = VFIO_PLATFORM_OFFSET_TO_INDEX(*ppos);
 538        loff_t off = *ppos & VFIO_PLATFORM_OFFSET_MASK;
 539
 540        if (index >= vdev->num_regions)
 541                return -EINVAL;
 542
 543        if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE))
 544                return -EINVAL;
 545
 546        if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO)
 547                return vfio_platform_write_mmio(&vdev->regions[index],
 548                                                        buf, count, off);
 549        else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO)
 550                return -EINVAL; /* not implemented */
 551
 552        return -EINVAL;
 553}
 554
 555static int vfio_platform_mmap_mmio(struct vfio_platform_region region,
 556                                   struct vm_area_struct *vma)
 557{
 558        u64 req_len, pgoff, req_start;
 559
 560        req_len = vma->vm_end - vma->vm_start;
 561        pgoff = vma->vm_pgoff &
 562                ((1U << (VFIO_PLATFORM_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
 563        req_start = pgoff << PAGE_SHIFT;
 564
 565        if (region.size < PAGE_SIZE || req_start + req_len > region.size)
 566                return -EINVAL;
 567
 568        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 569        vma->vm_pgoff = (region.addr >> PAGE_SHIFT) + pgoff;
 570
 571        return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
 572                               req_len, vma->vm_page_prot);
 573}
 574
 575static int vfio_platform_mmap(void *device_data, struct vm_area_struct *vma)
 576{
 577        struct vfio_platform_device *vdev = device_data;
 578        unsigned int index;
 579
 580        index = vma->vm_pgoff >> (VFIO_PLATFORM_OFFSET_SHIFT - PAGE_SHIFT);
 581
 582        if (vma->vm_end < vma->vm_start)
 583                return -EINVAL;
 584        if (!(vma->vm_flags & VM_SHARED))
 585                return -EINVAL;
 586        if (index >= vdev->num_regions)
 587                return -EINVAL;
 588        if (vma->vm_start & ~PAGE_MASK)
 589                return -EINVAL;
 590        if (vma->vm_end & ~PAGE_MASK)
 591                return -EINVAL;
 592
 593        if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_MMAP))
 594                return -EINVAL;
 595
 596        if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ)
 597                        && (vma->vm_flags & VM_READ))
 598                return -EINVAL;
 599
 600        if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE)
 601                        && (vma->vm_flags & VM_WRITE))
 602                return -EINVAL;
 603
 604        vma->vm_private_data = vdev;
 605
 606        if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO)
 607                return vfio_platform_mmap_mmio(vdev->regions[index], vma);
 608
 609        else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO)
 610                return -EINVAL; /* not implemented */
 611
 612        return -EINVAL;
 613}
 614
 615static const struct vfio_device_ops vfio_platform_ops = {
 616        .name           = "vfio-platform",
 617        .open           = vfio_platform_open,
 618        .release        = vfio_platform_release,
 619        .ioctl          = vfio_platform_ioctl,
 620        .read           = vfio_platform_read,
 621        .write          = vfio_platform_write,
 622        .mmap           = vfio_platform_mmap,
 623};
 624
 625static int vfio_platform_of_probe(struct vfio_platform_device *vdev,
 626                           struct device *dev)
 627{
 628        int ret;
 629
 630        ret = device_property_read_string(dev, "compatible",
 631                                          &vdev->compat);
 632        if (ret)
 633                pr_err("VFIO: cannot retrieve compat for %s\n",
 634                        vdev->name);
 635
 636        return ret;
 637}
 638
 639/*
 640 * There can be two kernel build combinations. One build where
 641 * ACPI is not selected in Kconfig and another one with the ACPI Kconfig.
 642 *
 643 * In the first case, vfio_platform_acpi_probe will return since
 644 * acpi_disabled is 1. DT user will not see any kind of messages from
 645 * ACPI.
 646 *
 647 * In the second case, both DT and ACPI is compiled in but the system is
 648 * booting with any of these combinations.
 649 *
 650 * If the firmware is DT type, then acpi_disabled is 1. The ACPI probe routine
 651 * terminates immediately without any messages.
 652 *
 653 * If the firmware is ACPI type, then acpi_disabled is 0. All other checks are
 654 * valid checks. We cannot claim that this system is DT.
 655 */
 656int vfio_platform_probe_common(struct vfio_platform_device *vdev,
 657                               struct device *dev)
 658{
 659        struct iommu_group *group;
 660        int ret;
 661
 662        if (!vdev)
 663                return -EINVAL;
 664
 665        ret = vfio_platform_acpi_probe(vdev, dev);
 666        if (ret)
 667                ret = vfio_platform_of_probe(vdev, dev);
 668
 669        if (ret)
 670                return ret;
 671
 672        vdev->device = dev;
 673
 674        ret = vfio_platform_get_reset(vdev);
 675        if (ret && vdev->reset_required) {
 676                pr_err("vfio: no reset function found for device %s\n",
 677                       vdev->name);
 678                return ret;
 679        }
 680
 681        group = vfio_iommu_group_get(dev);
 682        if (!group) {
 683                pr_err("VFIO: No IOMMU group for device %s\n", vdev->name);
 684                return -EINVAL;
 685        }
 686
 687        ret = vfio_add_group_dev(dev, &vfio_platform_ops, vdev);
 688        if (ret) {
 689                vfio_iommu_group_put(group, dev);
 690                return ret;
 691        }
 692
 693        mutex_init(&vdev->igate);
 694
 695        return 0;
 696}
 697EXPORT_SYMBOL_GPL(vfio_platform_probe_common);
 698
 699struct vfio_platform_device *vfio_platform_remove_common(struct device *dev)
 700{
 701        struct vfio_platform_device *vdev;
 702
 703        vdev = vfio_del_group_dev(dev);
 704
 705        if (vdev) {
 706                vfio_platform_put_reset(vdev);
 707                vfio_iommu_group_put(dev->iommu_group, dev);
 708        }
 709
 710        return vdev;
 711}
 712EXPORT_SYMBOL_GPL(vfio_platform_remove_common);
 713
 714void __vfio_platform_register_reset(struct vfio_platform_reset_node *node)
 715{
 716        mutex_lock(&driver_lock);
 717        list_add(&node->link, &reset_list);
 718        mutex_unlock(&driver_lock);
 719}
 720EXPORT_SYMBOL_GPL(__vfio_platform_register_reset);
 721
 722void vfio_platform_unregister_reset(const char *compat,
 723                                    vfio_platform_reset_fn_t fn)
 724{
 725        struct vfio_platform_reset_node *iter, *temp;
 726
 727        mutex_lock(&driver_lock);
 728        list_for_each_entry_safe(iter, temp, &reset_list, link) {
 729                if (!strcmp(iter->compat, compat) && (iter->of_reset == fn)) {
 730                        list_del(&iter->link);
 731                        break;
 732                }
 733        }
 734
 735        mutex_unlock(&driver_lock);
 736
 737}
 738EXPORT_SYMBOL_GPL(vfio_platform_unregister_reset);
 739
 740MODULE_VERSION(DRIVER_VERSION);
 741MODULE_LICENSE("GPL v2");
 742MODULE_AUTHOR(DRIVER_AUTHOR);
 743MODULE_DESCRIPTION(DRIVER_DESC);
 744