blob: de419df768e18e53c6cbc11e4c702a84afacf568 [file] [log] [blame]
Antonios Motakisde49fc02015-03-16 14:08:42 -06001/*
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/iommu.h>
17#include <linux/module.h>
18#include <linux/mutex.h>
19#include <linux/slab.h>
20#include <linux/types.h>
Antonios Motakis2e8567b2015-03-16 14:08:46 -060021#include <linux/uaccess.h>
Antonios Motakisde49fc02015-03-16 14:08:42 -060022#include <linux/vfio.h>
23
24#include "vfio_platform_private.h"
25
Eric Auger32a2d712015-11-03 18:12:12 +000026#define DRIVER_VERSION "0.10"
27#define DRIVER_AUTHOR "Antonios Motakis <a.motakis@virtualopensystems.com>"
28#define DRIVER_DESC "VFIO platform base module"
29
Eric Augere0864972015-11-03 18:12:13 +000030static LIST_HEAD(reset_list);
Antonios Motakise8909e62015-03-16 14:08:46 -060031static DEFINE_MUTEX(driver_lock);
32
Eric Augere9e05062015-11-03 18:12:17 +000033static vfio_platform_reset_fn_t vfio_platform_lookup_reset(const char *compat,
34 struct module **module)
Eric Auger3eeb0d52015-06-15 11:09:44 +020035{
Eric Augere9e05062015-11-03 18:12:17 +000036 struct vfio_platform_reset_node *iter;
37 vfio_platform_reset_fn_t reset_fn = NULL;
Eric Auger3eeb0d52015-06-15 11:09:44 +020038
Eric Augere9e05062015-11-03 18:12:17 +000039 mutex_lock(&driver_lock);
40 list_for_each_entry(iter, &reset_list, link) {
41 if (!strcmp(iter->compat, compat) &&
42 try_module_get(iter->owner)) {
43 *module = iter->owner;
Sinan Kaya7aef80c2016-07-19 09:01:41 -040044 reset_fn = iter->of_reset;
Eric Augere9e05062015-11-03 18:12:17 +000045 break;
Eric Auger3eeb0d52015-06-15 11:09:44 +020046 }
47 }
Eric Augere9e05062015-11-03 18:12:17 +000048 mutex_unlock(&driver_lock);
49 return reset_fn;
50}
51
52static void vfio_platform_get_reset(struct vfio_platform_device *vdev)
53{
Sinan Kaya7aef80c2016-07-19 09:01:41 -040054 vdev->of_reset = vfio_platform_lookup_reset(vdev->compat,
55 &vdev->reset_module);
56 if (!vdev->of_reset) {
Kees Cook7200be72015-11-19 16:17:24 -080057 request_module("vfio-reset:%s", vdev->compat);
Sinan Kaya7aef80c2016-07-19 09:01:41 -040058 vdev->of_reset = vfio_platform_lookup_reset(vdev->compat,
59 &vdev->reset_module);
Eric Augere9e05062015-11-03 18:12:17 +000060 }
Eric Auger3eeb0d52015-06-15 11:09:44 +020061}
62
63static void vfio_platform_put_reset(struct vfio_platform_device *vdev)
64{
Sinan Kaya7aef80c2016-07-19 09:01:41 -040065 if (vdev->of_reset)
Eric Augere9e05062015-11-03 18:12:17 +000066 module_put(vdev->reset_module);
Eric Auger3eeb0d52015-06-15 11:09:44 +020067}
68
Antonios Motakise8909e62015-03-16 14:08:46 -060069static int vfio_platform_regions_init(struct vfio_platform_device *vdev)
70{
71 int cnt = 0, i;
72
73 while (vdev->get_resource(vdev, cnt))
74 cnt++;
75
76 vdev->regions = kcalloc(cnt, sizeof(struct vfio_platform_region),
77 GFP_KERNEL);
78 if (!vdev->regions)
79 return -ENOMEM;
80
81 for (i = 0; i < cnt; i++) {
82 struct resource *res =
83 vdev->get_resource(vdev, i);
84
85 if (!res)
86 goto err;
87
88 vdev->regions[i].addr = res->start;
89 vdev->regions[i].size = resource_size(res);
90 vdev->regions[i].flags = 0;
91
92 switch (resource_type(res)) {
93 case IORESOURCE_MEM:
94 vdev->regions[i].type = VFIO_PLATFORM_REGION_TYPE_MMIO;
Antonios Motakis6e3f2642015-03-16 14:08:47 -060095 vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_READ;
96 if (!(res->flags & IORESOURCE_READONLY))
97 vdev->regions[i].flags |=
98 VFIO_REGION_INFO_FLAG_WRITE;
Antonios Motakisfad4d5b2015-03-16 14:08:48 -060099
100 /*
101 * Only regions addressed with PAGE granularity may be
102 * MMAPed securely.
103 */
104 if (!(vdev->regions[i].addr & ~PAGE_MASK) &&
105 !(vdev->regions[i].size & ~PAGE_MASK))
106 vdev->regions[i].flags |=
107 VFIO_REGION_INFO_FLAG_MMAP;
108
Antonios Motakise8909e62015-03-16 14:08:46 -0600109 break;
110 case IORESOURCE_IO:
111 vdev->regions[i].type = VFIO_PLATFORM_REGION_TYPE_PIO;
112 break;
113 default:
114 goto err;
115 }
116 }
117
118 vdev->num_regions = cnt;
119
120 return 0;
121err:
122 kfree(vdev->regions);
123 return -EINVAL;
124}
125
126static void vfio_platform_regions_cleanup(struct vfio_platform_device *vdev)
127{
Antonios Motakis6e3f2642015-03-16 14:08:47 -0600128 int i;
129
130 for (i = 0; i < vdev->num_regions; i++)
131 iounmap(vdev->regions[i].ioaddr);
132
Antonios Motakise8909e62015-03-16 14:08:46 -0600133 vdev->num_regions = 0;
134 kfree(vdev->regions);
135}
136
Sinan Kayaf084aa72016-07-19 09:01:42 -0400137static int vfio_platform_call_reset(struct vfio_platform_device *vdev)
138{
139 if (vdev->of_reset) {
140 dev_info(vdev->device, "reset\n");
141 return vdev->of_reset(vdev);
142 }
143
144 dev_warn(vdev->device, "no reset function found!\n");
145 return -EINVAL;
146}
147
Antonios Motakisde49fc02015-03-16 14:08:42 -0600148static void vfio_platform_release(void *device_data)
149{
Antonios Motakise8909e62015-03-16 14:08:46 -0600150 struct vfio_platform_device *vdev = device_data;
151
152 mutex_lock(&driver_lock);
153
154 if (!(--vdev->refcnt)) {
Sinan Kayaf084aa72016-07-19 09:01:42 -0400155 vfio_platform_call_reset(vdev);
Antonios Motakise8909e62015-03-16 14:08:46 -0600156 vfio_platform_regions_cleanup(vdev);
Antonios Motakis682704c2015-03-16 14:08:48 -0600157 vfio_platform_irq_cleanup(vdev);
Antonios Motakise8909e62015-03-16 14:08:46 -0600158 }
159
160 mutex_unlock(&driver_lock);
161
Eric Auger32a2d712015-11-03 18:12:12 +0000162 module_put(vdev->parent_module);
Antonios Motakisde49fc02015-03-16 14:08:42 -0600163}
164
165static int vfio_platform_open(void *device_data)
166{
Antonios Motakise8909e62015-03-16 14:08:46 -0600167 struct vfio_platform_device *vdev = device_data;
168 int ret;
169
Eric Auger32a2d712015-11-03 18:12:12 +0000170 if (!try_module_get(vdev->parent_module))
Antonios Motakisde49fc02015-03-16 14:08:42 -0600171 return -ENODEV;
172
Antonios Motakise8909e62015-03-16 14:08:46 -0600173 mutex_lock(&driver_lock);
174
175 if (!vdev->refcnt) {
176 ret = vfio_platform_regions_init(vdev);
177 if (ret)
178 goto err_reg;
Antonios Motakis682704c2015-03-16 14:08:48 -0600179
180 ret = vfio_platform_irq_init(vdev);
181 if (ret)
182 goto err_irq;
Eric Auger813ae6602015-06-15 11:09:43 +0200183
Sinan Kayaf084aa72016-07-19 09:01:42 -0400184 vfio_platform_call_reset(vdev);
Antonios Motakise8909e62015-03-16 14:08:46 -0600185 }
186
187 vdev->refcnt++;
188
189 mutex_unlock(&driver_lock);
Antonios Motakisde49fc02015-03-16 14:08:42 -0600190 return 0;
Antonios Motakise8909e62015-03-16 14:08:46 -0600191
Antonios Motakis682704c2015-03-16 14:08:48 -0600192err_irq:
193 vfio_platform_regions_cleanup(vdev);
Antonios Motakise8909e62015-03-16 14:08:46 -0600194err_reg:
195 mutex_unlock(&driver_lock);
196 module_put(THIS_MODULE);
197 return ret;
Antonios Motakisde49fc02015-03-16 14:08:42 -0600198}
199
200static long vfio_platform_ioctl(void *device_data,
201 unsigned int cmd, unsigned long arg)
202{
Antonios Motakis2e8567b2015-03-16 14:08:46 -0600203 struct vfio_platform_device *vdev = device_data;
204 unsigned long minsz;
Antonios Motakisde49fc02015-03-16 14:08:42 -0600205
Antonios Motakis2e8567b2015-03-16 14:08:46 -0600206 if (cmd == VFIO_DEVICE_GET_INFO) {
207 struct vfio_device_info info;
208
209 minsz = offsetofend(struct vfio_device_info, num_irqs);
210
211 if (copy_from_user(&info, (void __user *)arg, minsz))
212 return -EFAULT;
213
214 if (info.argsz < minsz)
215 return -EINVAL;
216
Sinan Kaya7aef80c2016-07-19 09:01:41 -0400217 if (vdev->of_reset)
Eric Auger813ae6602015-06-15 11:09:43 +0200218 vdev->flags |= VFIO_DEVICE_FLAGS_RESET;
Antonios Motakis2e8567b2015-03-16 14:08:46 -0600219 info.flags = vdev->flags;
Antonios Motakise8909e62015-03-16 14:08:46 -0600220 info.num_regions = vdev->num_regions;
Antonios Motakis682704c2015-03-16 14:08:48 -0600221 info.num_irqs = vdev->num_irqs;
Antonios Motakis2e8567b2015-03-16 14:08:46 -0600222
Michael S. Tsirkin8160c4e2016-02-28 16:31:39 +0200223 return copy_to_user((void __user *)arg, &info, minsz) ?
224 -EFAULT : 0;
Antonios Motakis2e8567b2015-03-16 14:08:46 -0600225
Antonios Motakise8909e62015-03-16 14:08:46 -0600226 } else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
227 struct vfio_region_info info;
Antonios Motakisde49fc02015-03-16 14:08:42 -0600228
Antonios Motakise8909e62015-03-16 14:08:46 -0600229 minsz = offsetofend(struct vfio_region_info, offset);
230
231 if (copy_from_user(&info, (void __user *)arg, minsz))
232 return -EFAULT;
233
234 if (info.argsz < minsz)
235 return -EINVAL;
236
237 if (info.index >= vdev->num_regions)
238 return -EINVAL;
239
240 /* map offset to the physical address */
241 info.offset = VFIO_PLATFORM_INDEX_TO_OFFSET(info.index);
242 info.size = vdev->regions[info.index].size;
243 info.flags = vdev->regions[info.index].flags;
244
Michael S. Tsirkin8160c4e2016-02-28 16:31:39 +0200245 return copy_to_user((void __user *)arg, &info, minsz) ?
246 -EFAULT : 0;
Antonios Motakise8909e62015-03-16 14:08:46 -0600247
Antonios Motakis682704c2015-03-16 14:08:48 -0600248 } else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) {
249 struct vfio_irq_info info;
Antonios Motakisde49fc02015-03-16 14:08:42 -0600250
Antonios Motakis682704c2015-03-16 14:08:48 -0600251 minsz = offsetofend(struct vfio_irq_info, count);
252
253 if (copy_from_user(&info, (void __user *)arg, minsz))
254 return -EFAULT;
255
256 if (info.argsz < minsz)
257 return -EINVAL;
258
259 if (info.index >= vdev->num_irqs)
260 return -EINVAL;
261
262 info.flags = vdev->irqs[info.index].flags;
263 info.count = vdev->irqs[info.index].count;
264
Michael S. Tsirkin8160c4e2016-02-28 16:31:39 +0200265 return copy_to_user((void __user *)arg, &info, minsz) ?
266 -EFAULT : 0;
Antonios Motakis682704c2015-03-16 14:08:48 -0600267
Antonios Motakis9a363212015-03-16 14:08:49 -0600268 } else if (cmd == VFIO_DEVICE_SET_IRQS) {
269 struct vfio_irq_set hdr;
270 u8 *data = NULL;
271 int ret = 0;
Antonios Motakisde49fc02015-03-16 14:08:42 -0600272
Antonios Motakis9a363212015-03-16 14:08:49 -0600273 minsz = offsetofend(struct vfio_irq_set, count);
274
275 if (copy_from_user(&hdr, (void __user *)arg, minsz))
276 return -EFAULT;
277
278 if (hdr.argsz < minsz)
279 return -EINVAL;
280
281 if (hdr.index >= vdev->num_irqs)
282 return -EINVAL;
283
284 if (hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK |
285 VFIO_IRQ_SET_ACTION_TYPE_MASK))
286 return -EINVAL;
287
288 if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) {
289 size_t size;
290
291 if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL)
292 size = sizeof(uint8_t);
293 else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD)
294 size = sizeof(int32_t);
295 else
296 return -EINVAL;
297
298 if (hdr.argsz - minsz < size)
299 return -EINVAL;
300
301 data = memdup_user((void __user *)(arg + minsz), size);
302 if (IS_ERR(data))
303 return PTR_ERR(data);
304 }
305
306 mutex_lock(&vdev->igate);
307
308 ret = vfio_platform_set_irqs_ioctl(vdev, hdr.flags, hdr.index,
309 hdr.start, hdr.count, data);
310 mutex_unlock(&vdev->igate);
311 kfree(data);
312
313 return ret;
314
Eric Auger813ae6602015-06-15 11:09:43 +0200315 } else if (cmd == VFIO_DEVICE_RESET) {
Sinan Kayaf084aa72016-07-19 09:01:42 -0400316 return vfio_platform_call_reset(vdev);
Eric Auger813ae6602015-06-15 11:09:43 +0200317 }
Antonios Motakisde49fc02015-03-16 14:08:42 -0600318
319 return -ENOTTY;
320}
321
James Morse1b4bb2e2015-10-29 16:50:43 +0000322static ssize_t vfio_platform_read_mmio(struct vfio_platform_region *reg,
Antonios Motakis6e3f2642015-03-16 14:08:47 -0600323 char __user *buf, size_t count,
324 loff_t off)
325{
326 unsigned int done = 0;
327
James Morse1b4bb2e2015-10-29 16:50:43 +0000328 if (!reg->ioaddr) {
329 reg->ioaddr =
330 ioremap_nocache(reg->addr, reg->size);
Antonios Motakis6e3f2642015-03-16 14:08:47 -0600331
James Morse1b4bb2e2015-10-29 16:50:43 +0000332 if (!reg->ioaddr)
Antonios Motakis6e3f2642015-03-16 14:08:47 -0600333 return -ENOMEM;
334 }
335
336 while (count) {
337 size_t filled;
338
339 if (count >= 4 && !(off % 4)) {
340 u32 val;
341
James Morse1b4bb2e2015-10-29 16:50:43 +0000342 val = ioread32(reg->ioaddr + off);
Antonios Motakis6e3f2642015-03-16 14:08:47 -0600343 if (copy_to_user(buf, &val, 4))
344 goto err;
345
346 filled = 4;
347 } else if (count >= 2 && !(off % 2)) {
348 u16 val;
349
James Morse1b4bb2e2015-10-29 16:50:43 +0000350 val = ioread16(reg->ioaddr + off);
Antonios Motakis6e3f2642015-03-16 14:08:47 -0600351 if (copy_to_user(buf, &val, 2))
352 goto err;
353
354 filled = 2;
355 } else {
356 u8 val;
357
James Morse1b4bb2e2015-10-29 16:50:43 +0000358 val = ioread8(reg->ioaddr + off);
Antonios Motakis6e3f2642015-03-16 14:08:47 -0600359 if (copy_to_user(buf, &val, 1))
360 goto err;
361
362 filled = 1;
363 }
364
365
366 count -= filled;
367 done += filled;
368 off += filled;
369 buf += filled;
370 }
371
372 return done;
373err:
374 return -EFAULT;
375}
376
Antonios Motakisde49fc02015-03-16 14:08:42 -0600377static ssize_t vfio_platform_read(void *device_data, char __user *buf,
378 size_t count, loff_t *ppos)
379{
Antonios Motakis6e3f2642015-03-16 14:08:47 -0600380 struct vfio_platform_device *vdev = device_data;
381 unsigned int index = VFIO_PLATFORM_OFFSET_TO_INDEX(*ppos);
382 loff_t off = *ppos & VFIO_PLATFORM_OFFSET_MASK;
383
384 if (index >= vdev->num_regions)
385 return -EINVAL;
386
387 if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ))
388 return -EINVAL;
389
390 if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO)
James Morse1b4bb2e2015-10-29 16:50:43 +0000391 return vfio_platform_read_mmio(&vdev->regions[index],
Antonios Motakis6e3f2642015-03-16 14:08:47 -0600392 buf, count, off);
393 else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO)
394 return -EINVAL; /* not implemented */
395
Antonios Motakisde49fc02015-03-16 14:08:42 -0600396 return -EINVAL;
397}
398
James Morse1b4bb2e2015-10-29 16:50:43 +0000399static ssize_t vfio_platform_write_mmio(struct vfio_platform_region *reg,
Antonios Motakis6e3f2642015-03-16 14:08:47 -0600400 const char __user *buf, size_t count,
401 loff_t off)
402{
403 unsigned int done = 0;
404
James Morse1b4bb2e2015-10-29 16:50:43 +0000405 if (!reg->ioaddr) {
406 reg->ioaddr =
407 ioremap_nocache(reg->addr, reg->size);
Antonios Motakis6e3f2642015-03-16 14:08:47 -0600408
James Morse1b4bb2e2015-10-29 16:50:43 +0000409 if (!reg->ioaddr)
Antonios Motakis6e3f2642015-03-16 14:08:47 -0600410 return -ENOMEM;
411 }
412
413 while (count) {
414 size_t filled;
415
416 if (count >= 4 && !(off % 4)) {
417 u32 val;
418
419 if (copy_from_user(&val, buf, 4))
420 goto err;
James Morse1b4bb2e2015-10-29 16:50:43 +0000421 iowrite32(val, reg->ioaddr + off);
Antonios Motakis6e3f2642015-03-16 14:08:47 -0600422
423 filled = 4;
424 } else if (count >= 2 && !(off % 2)) {
425 u16 val;
426
427 if (copy_from_user(&val, buf, 2))
428 goto err;
James Morse1b4bb2e2015-10-29 16:50:43 +0000429 iowrite16(val, reg->ioaddr + off);
Antonios Motakis6e3f2642015-03-16 14:08:47 -0600430
431 filled = 2;
432 } else {
433 u8 val;
434
435 if (copy_from_user(&val, buf, 1))
436 goto err;
James Morse1b4bb2e2015-10-29 16:50:43 +0000437 iowrite8(val, reg->ioaddr + off);
Antonios Motakis6e3f2642015-03-16 14:08:47 -0600438
439 filled = 1;
440 }
441
442 count -= filled;
443 done += filled;
444 off += filled;
445 buf += filled;
446 }
447
448 return done;
449err:
450 return -EFAULT;
451}
452
Antonios Motakisde49fc02015-03-16 14:08:42 -0600453static ssize_t vfio_platform_write(void *device_data, const char __user *buf,
454 size_t count, loff_t *ppos)
455{
Antonios Motakis6e3f2642015-03-16 14:08:47 -0600456 struct vfio_platform_device *vdev = device_data;
457 unsigned int index = VFIO_PLATFORM_OFFSET_TO_INDEX(*ppos);
458 loff_t off = *ppos & VFIO_PLATFORM_OFFSET_MASK;
459
460 if (index >= vdev->num_regions)
461 return -EINVAL;
462
463 if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE))
464 return -EINVAL;
465
466 if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO)
James Morse1b4bb2e2015-10-29 16:50:43 +0000467 return vfio_platform_write_mmio(&vdev->regions[index],
Antonios Motakis6e3f2642015-03-16 14:08:47 -0600468 buf, count, off);
469 else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO)
470 return -EINVAL; /* not implemented */
471
Antonios Motakisde49fc02015-03-16 14:08:42 -0600472 return -EINVAL;
473}
474
Antonios Motakisfad4d5b2015-03-16 14:08:48 -0600475static int vfio_platform_mmap_mmio(struct vfio_platform_region region,
476 struct vm_area_struct *vma)
477{
478 u64 req_len, pgoff, req_start;
479
480 req_len = vma->vm_end - vma->vm_start;
481 pgoff = vma->vm_pgoff &
482 ((1U << (VFIO_PLATFORM_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
483 req_start = pgoff << PAGE_SHIFT;
484
485 if (region.size < PAGE_SIZE || req_start + req_len > region.size)
486 return -EINVAL;
487
488 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
489 vma->vm_pgoff = (region.addr >> PAGE_SHIFT) + pgoff;
490
491 return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
492 req_len, vma->vm_page_prot);
493}
494
Antonios Motakisde49fc02015-03-16 14:08:42 -0600495static int vfio_platform_mmap(void *device_data, struct vm_area_struct *vma)
496{
Antonios Motakisfad4d5b2015-03-16 14:08:48 -0600497 struct vfio_platform_device *vdev = device_data;
498 unsigned int index;
499
500 index = vma->vm_pgoff >> (VFIO_PLATFORM_OFFSET_SHIFT - PAGE_SHIFT);
501
502 if (vma->vm_end < vma->vm_start)
503 return -EINVAL;
504 if (!(vma->vm_flags & VM_SHARED))
505 return -EINVAL;
506 if (index >= vdev->num_regions)
507 return -EINVAL;
508 if (vma->vm_start & ~PAGE_MASK)
509 return -EINVAL;
510 if (vma->vm_end & ~PAGE_MASK)
511 return -EINVAL;
512
513 if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_MMAP))
514 return -EINVAL;
515
516 if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ)
517 && (vma->vm_flags & VM_READ))
518 return -EINVAL;
519
520 if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE)
521 && (vma->vm_flags & VM_WRITE))
522 return -EINVAL;
523
524 vma->vm_private_data = vdev;
525
526 if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO)
527 return vfio_platform_mmap_mmio(vdev->regions[index], vma);
528
529 else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO)
530 return -EINVAL; /* not implemented */
531
Antonios Motakisde49fc02015-03-16 14:08:42 -0600532 return -EINVAL;
533}
534
535static const struct vfio_device_ops vfio_platform_ops = {
536 .name = "vfio-platform",
537 .open = vfio_platform_open,
538 .release = vfio_platform_release,
539 .ioctl = vfio_platform_ioctl,
540 .read = vfio_platform_read,
541 .write = vfio_platform_write,
542 .mmap = vfio_platform_mmap,
543};
544
545int vfio_platform_probe_common(struct vfio_platform_device *vdev,
546 struct device *dev)
547{
548 struct iommu_group *group;
549 int ret;
550
551 if (!vdev)
552 return -EINVAL;
553
Eric Auger0628c4d2015-11-03 18:12:16 +0000554 ret = device_property_read_string(dev, "compatible", &vdev->compat);
555 if (ret) {
556 pr_err("VFIO: cannot retrieve compat for %s\n", vdev->name);
557 return -EINVAL;
558 }
559
Eric Auger705e60b2015-11-03 18:12:18 +0000560 vdev->device = dev;
561
Peng Fan9698cbf2016-05-23 17:47:30 +0800562 group = vfio_iommu_group_get(dev);
Antonios Motakisde49fc02015-03-16 14:08:42 -0600563 if (!group) {
564 pr_err("VFIO: No IOMMU group for device %s\n", vdev->name);
565 return -EINVAL;
566 }
567
568 ret = vfio_add_group_dev(dev, &vfio_platform_ops, vdev);
569 if (ret) {
Peng Fan9698cbf2016-05-23 17:47:30 +0800570 vfio_iommu_group_put(group, dev);
Antonios Motakisde49fc02015-03-16 14:08:42 -0600571 return ret;
572 }
573
Eric Augere9e05062015-11-03 18:12:17 +0000574 vfio_platform_get_reset(vdev);
Eric Auger3eeb0d52015-06-15 11:09:44 +0200575
Antonios Motakis9a363212015-03-16 14:08:49 -0600576 mutex_init(&vdev->igate);
577
Antonios Motakisde49fc02015-03-16 14:08:42 -0600578 return 0;
579}
580EXPORT_SYMBOL_GPL(vfio_platform_probe_common);
581
582struct vfio_platform_device *vfio_platform_remove_common(struct device *dev)
583{
584 struct vfio_platform_device *vdev;
585
586 vdev = vfio_del_group_dev(dev);
Eric Auger3eeb0d52015-06-15 11:09:44 +0200587
588 if (vdev) {
589 vfio_platform_put_reset(vdev);
Peng Fan9698cbf2016-05-23 17:47:30 +0800590 vfio_iommu_group_put(dev->iommu_group, dev);
Eric Auger3eeb0d52015-06-15 11:09:44 +0200591 }
Antonios Motakisde49fc02015-03-16 14:08:42 -0600592
593 return vdev;
594}
595EXPORT_SYMBOL_GPL(vfio_platform_remove_common);
Eric Auger32a2d712015-11-03 18:12:12 +0000596
Eric Augere0864972015-11-03 18:12:13 +0000597void __vfio_platform_register_reset(struct vfio_platform_reset_node *node)
598{
599 mutex_lock(&driver_lock);
600 list_add(&node->link, &reset_list);
601 mutex_unlock(&driver_lock);
602}
603EXPORT_SYMBOL_GPL(__vfio_platform_register_reset);
604
605void vfio_platform_unregister_reset(const char *compat,
606 vfio_platform_reset_fn_t fn)
607{
608 struct vfio_platform_reset_node *iter, *temp;
609
610 mutex_lock(&driver_lock);
611 list_for_each_entry_safe(iter, temp, &reset_list, link) {
Sinan Kaya7aef80c2016-07-19 09:01:41 -0400612 if (!strcmp(iter->compat, compat) && (iter->of_reset == fn)) {
Eric Augere0864972015-11-03 18:12:13 +0000613 list_del(&iter->link);
614 break;
615 }
616 }
617
618 mutex_unlock(&driver_lock);
619
620}
621EXPORT_SYMBOL_GPL(vfio_platform_unregister_reset);
622
Eric Auger32a2d712015-11-03 18:12:12 +0000623MODULE_VERSION(DRIVER_VERSION);
624MODULE_LICENSE("GPL v2");
625MODULE_AUTHOR(DRIVER_AUTHOR);
626MODULE_DESCRIPTION(DRIVER_DESC);