xref: /openbmc/linux/drivers/vfio/fsl-mc/vfio_fsl_mc.c (revision 704f5082d8457a2166eb4cb4e460510117c70928)
1fb1ff4c1SBharat Bhushan // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2fb1ff4c1SBharat Bhushan /*
3fb1ff4c1SBharat Bhushan  * Copyright 2013-2016 Freescale Semiconductor Inc.
4fb1ff4c1SBharat Bhushan  * Copyright 2016-2017,2019-2020 NXP
5fb1ff4c1SBharat Bhushan  */
6fb1ff4c1SBharat Bhushan 
7fb1ff4c1SBharat Bhushan #include <linux/device.h>
8fb1ff4c1SBharat Bhushan #include <linux/iommu.h>
9fb1ff4c1SBharat Bhushan #include <linux/module.h>
10fb1ff4c1SBharat Bhushan #include <linux/mutex.h>
11fb1ff4c1SBharat Bhushan #include <linux/slab.h>
12fb1ff4c1SBharat Bhushan #include <linux/types.h>
13fb1ff4c1SBharat Bhushan #include <linux/vfio.h>
14fb1ff4c1SBharat Bhushan #include <linux/fsl/mc.h>
15fb1ff4c1SBharat Bhushan 
16fb1ff4c1SBharat Bhushan #include "vfio_fsl_mc_private.h"
17fb1ff4c1SBharat Bhushan 
18*704f5082SDiana Craciun static struct fsl_mc_driver vfio_fsl_mc_driver;
19*704f5082SDiana Craciun 
20fb1ff4c1SBharat Bhushan static int vfio_fsl_mc_open(void *device_data)
21fb1ff4c1SBharat Bhushan {
22fb1ff4c1SBharat Bhushan 	if (!try_module_get(THIS_MODULE))
23fb1ff4c1SBharat Bhushan 		return -ENODEV;
24fb1ff4c1SBharat Bhushan 
25fb1ff4c1SBharat Bhushan 	return 0;
26fb1ff4c1SBharat Bhushan }
27fb1ff4c1SBharat Bhushan 
28fb1ff4c1SBharat Bhushan static void vfio_fsl_mc_release(void *device_data)
29fb1ff4c1SBharat Bhushan {
30fb1ff4c1SBharat Bhushan 	module_put(THIS_MODULE);
31fb1ff4c1SBharat Bhushan }
32fb1ff4c1SBharat Bhushan 
33fb1ff4c1SBharat Bhushan static long vfio_fsl_mc_ioctl(void *device_data, unsigned int cmd,
34fb1ff4c1SBharat Bhushan 			      unsigned long arg)
35fb1ff4c1SBharat Bhushan {
36fb1ff4c1SBharat Bhushan 	switch (cmd) {
37fb1ff4c1SBharat Bhushan 	case VFIO_DEVICE_GET_INFO:
38fb1ff4c1SBharat Bhushan 	{
39fb1ff4c1SBharat Bhushan 		return -ENOTTY;
40fb1ff4c1SBharat Bhushan 	}
41fb1ff4c1SBharat Bhushan 	case VFIO_DEVICE_GET_REGION_INFO:
42fb1ff4c1SBharat Bhushan 	{
43fb1ff4c1SBharat Bhushan 		return -ENOTTY;
44fb1ff4c1SBharat Bhushan 	}
45fb1ff4c1SBharat Bhushan 	case VFIO_DEVICE_GET_IRQ_INFO:
46fb1ff4c1SBharat Bhushan 	{
47fb1ff4c1SBharat Bhushan 		return -ENOTTY;
48fb1ff4c1SBharat Bhushan 	}
49fb1ff4c1SBharat Bhushan 	case VFIO_DEVICE_SET_IRQS:
50fb1ff4c1SBharat Bhushan 	{
51fb1ff4c1SBharat Bhushan 		return -ENOTTY;
52fb1ff4c1SBharat Bhushan 	}
53fb1ff4c1SBharat Bhushan 	case VFIO_DEVICE_RESET:
54fb1ff4c1SBharat Bhushan 	{
55fb1ff4c1SBharat Bhushan 		return -ENOTTY;
56fb1ff4c1SBharat Bhushan 	}
57fb1ff4c1SBharat Bhushan 	default:
58fb1ff4c1SBharat Bhushan 		return -ENOTTY;
59fb1ff4c1SBharat Bhushan 	}
60fb1ff4c1SBharat Bhushan }
61fb1ff4c1SBharat Bhushan 
62fb1ff4c1SBharat Bhushan static ssize_t vfio_fsl_mc_read(void *device_data, char __user *buf,
63fb1ff4c1SBharat Bhushan 				size_t count, loff_t *ppos)
64fb1ff4c1SBharat Bhushan {
65fb1ff4c1SBharat Bhushan 	return -EINVAL;
66fb1ff4c1SBharat Bhushan }
67fb1ff4c1SBharat Bhushan 
68fb1ff4c1SBharat Bhushan static ssize_t vfio_fsl_mc_write(void *device_data, const char __user *buf,
69fb1ff4c1SBharat Bhushan 				 size_t count, loff_t *ppos)
70fb1ff4c1SBharat Bhushan {
71fb1ff4c1SBharat Bhushan 	return -EINVAL;
72fb1ff4c1SBharat Bhushan }
73fb1ff4c1SBharat Bhushan 
74fb1ff4c1SBharat Bhushan static int vfio_fsl_mc_mmap(void *device_data, struct vm_area_struct *vma)
75fb1ff4c1SBharat Bhushan {
76fb1ff4c1SBharat Bhushan 	return -EINVAL;
77fb1ff4c1SBharat Bhushan }
78fb1ff4c1SBharat Bhushan 
79fb1ff4c1SBharat Bhushan static const struct vfio_device_ops vfio_fsl_mc_ops = {
80fb1ff4c1SBharat Bhushan 	.name		= "vfio-fsl-mc",
81fb1ff4c1SBharat Bhushan 	.open		= vfio_fsl_mc_open,
82fb1ff4c1SBharat Bhushan 	.release	= vfio_fsl_mc_release,
83fb1ff4c1SBharat Bhushan 	.ioctl		= vfio_fsl_mc_ioctl,
84fb1ff4c1SBharat Bhushan 	.read		= vfio_fsl_mc_read,
85fb1ff4c1SBharat Bhushan 	.write		= vfio_fsl_mc_write,
86fb1ff4c1SBharat Bhushan 	.mmap		= vfio_fsl_mc_mmap,
87fb1ff4c1SBharat Bhushan };
88fb1ff4c1SBharat Bhushan 
89*704f5082SDiana Craciun static int vfio_fsl_mc_bus_notifier(struct notifier_block *nb,
90*704f5082SDiana Craciun 				    unsigned long action, void *data)
91*704f5082SDiana Craciun {
92*704f5082SDiana Craciun 	struct vfio_fsl_mc_device *vdev = container_of(nb,
93*704f5082SDiana Craciun 					struct vfio_fsl_mc_device, nb);
94*704f5082SDiana Craciun 	struct device *dev = data;
95*704f5082SDiana Craciun 	struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
96*704f5082SDiana Craciun 	struct fsl_mc_device *mc_cont = to_fsl_mc_device(mc_dev->dev.parent);
97*704f5082SDiana Craciun 
98*704f5082SDiana Craciun 	if (action == BUS_NOTIFY_ADD_DEVICE &&
99*704f5082SDiana Craciun 	    vdev->mc_dev == mc_cont) {
100*704f5082SDiana Craciun 		mc_dev->driver_override = kasprintf(GFP_KERNEL, "%s",
101*704f5082SDiana Craciun 						    vfio_fsl_mc_ops.name);
102*704f5082SDiana Craciun 		if (!mc_dev->driver_override)
103*704f5082SDiana Craciun 			dev_warn(dev, "VFIO_FSL_MC: Setting driver override for device in dprc %s failed\n",
104*704f5082SDiana Craciun 				 dev_name(&mc_cont->dev));
105*704f5082SDiana Craciun 		else
106*704f5082SDiana Craciun 			dev_info(dev, "VFIO_FSL_MC: Setting driver override for device in dprc %s\n",
107*704f5082SDiana Craciun 				 dev_name(&mc_cont->dev));
108*704f5082SDiana Craciun 	} else if (action == BUS_NOTIFY_BOUND_DRIVER &&
109*704f5082SDiana Craciun 		vdev->mc_dev == mc_cont) {
110*704f5082SDiana Craciun 		struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
111*704f5082SDiana Craciun 
112*704f5082SDiana Craciun 		if (mc_drv && mc_drv != &vfio_fsl_mc_driver)
113*704f5082SDiana Craciun 			dev_warn(dev, "VFIO_FSL_MC: Object %s bound to driver %s while DPRC bound to vfio-fsl-mc\n",
114*704f5082SDiana Craciun 				 dev_name(dev), mc_drv->driver.name);
115*704f5082SDiana Craciun 	}
116*704f5082SDiana Craciun 
117*704f5082SDiana Craciun 	return 0;
118*704f5082SDiana Craciun }
119*704f5082SDiana Craciun 
120*704f5082SDiana Craciun static int vfio_fsl_mc_init_device(struct vfio_fsl_mc_device *vdev)
121*704f5082SDiana Craciun {
122*704f5082SDiana Craciun 	struct fsl_mc_device *mc_dev = vdev->mc_dev;
123*704f5082SDiana Craciun 	int ret;
124*704f5082SDiana Craciun 
125*704f5082SDiana Craciun 	/* Non-dprc devices share mc_io from parent */
126*704f5082SDiana Craciun 	if (!is_fsl_mc_bus_dprc(mc_dev)) {
127*704f5082SDiana Craciun 		struct fsl_mc_device *mc_cont = to_fsl_mc_device(mc_dev->dev.parent);
128*704f5082SDiana Craciun 
129*704f5082SDiana Craciun 		mc_dev->mc_io = mc_cont->mc_io;
130*704f5082SDiana Craciun 		return 0;
131*704f5082SDiana Craciun 	}
132*704f5082SDiana Craciun 
133*704f5082SDiana Craciun 	vdev->nb.notifier_call = vfio_fsl_mc_bus_notifier;
134*704f5082SDiana Craciun 	ret = bus_register_notifier(&fsl_mc_bus_type, &vdev->nb);
135*704f5082SDiana Craciun 	if (ret)
136*704f5082SDiana Craciun 		return ret;
137*704f5082SDiana Craciun 
138*704f5082SDiana Craciun 	/* open DPRC, allocate a MC portal */
139*704f5082SDiana Craciun 	ret = dprc_setup(mc_dev);
140*704f5082SDiana Craciun 	if (ret) {
141*704f5082SDiana Craciun 		dev_err(&mc_dev->dev, "VFIO_FSL_MC: Failed to setup DPRC (%d)\n", ret);
142*704f5082SDiana Craciun 		goto out_nc_unreg;
143*704f5082SDiana Craciun 	}
144*704f5082SDiana Craciun 
145*704f5082SDiana Craciun 	ret = dprc_scan_container(mc_dev, false);
146*704f5082SDiana Craciun 	if (ret) {
147*704f5082SDiana Craciun 		dev_err(&mc_dev->dev, "VFIO_FSL_MC: Container scanning failed (%d)\n", ret);
148*704f5082SDiana Craciun 		goto out_dprc_cleanup;
149*704f5082SDiana Craciun 	}
150*704f5082SDiana Craciun 
151*704f5082SDiana Craciun 	return 0;
152*704f5082SDiana Craciun 
153*704f5082SDiana Craciun out_dprc_cleanup:
154*704f5082SDiana Craciun 	dprc_remove_devices(mc_dev, NULL, 0);
155*704f5082SDiana Craciun 	dprc_cleanup(mc_dev);
156*704f5082SDiana Craciun out_nc_unreg:
157*704f5082SDiana Craciun 	bus_unregister_notifier(&fsl_mc_bus_type, &vdev->nb);
158*704f5082SDiana Craciun 	vdev->nb.notifier_call = NULL;
159*704f5082SDiana Craciun 
160*704f5082SDiana Craciun 	return ret;
161*704f5082SDiana Craciun }
162*704f5082SDiana Craciun 
163fb1ff4c1SBharat Bhushan static int vfio_fsl_mc_probe(struct fsl_mc_device *mc_dev)
164fb1ff4c1SBharat Bhushan {
165fb1ff4c1SBharat Bhushan 	struct iommu_group *group;
166fb1ff4c1SBharat Bhushan 	struct vfio_fsl_mc_device *vdev;
167fb1ff4c1SBharat Bhushan 	struct device *dev = &mc_dev->dev;
168fb1ff4c1SBharat Bhushan 	int ret;
169fb1ff4c1SBharat Bhushan 
170fb1ff4c1SBharat Bhushan 	group = vfio_iommu_group_get(dev);
171fb1ff4c1SBharat Bhushan 	if (!group) {
172fb1ff4c1SBharat Bhushan 		dev_err(dev, "VFIO_FSL_MC: No IOMMU group\n");
173fb1ff4c1SBharat Bhushan 		return -EINVAL;
174fb1ff4c1SBharat Bhushan 	}
175fb1ff4c1SBharat Bhushan 
176fb1ff4c1SBharat Bhushan 	vdev = devm_kzalloc(dev, sizeof(*vdev), GFP_KERNEL);
177fb1ff4c1SBharat Bhushan 	if (!vdev) {
178fb1ff4c1SBharat Bhushan 		ret = -ENOMEM;
179fb1ff4c1SBharat Bhushan 		goto out_group_put;
180fb1ff4c1SBharat Bhushan 	}
181fb1ff4c1SBharat Bhushan 
182fb1ff4c1SBharat Bhushan 	vdev->mc_dev = mc_dev;
183fb1ff4c1SBharat Bhushan 
184fb1ff4c1SBharat Bhushan 	ret = vfio_add_group_dev(dev, &vfio_fsl_mc_ops, vdev);
185fb1ff4c1SBharat Bhushan 	if (ret) {
186fb1ff4c1SBharat Bhushan 		dev_err(dev, "VFIO_FSL_MC: Failed to add to vfio group\n");
187fb1ff4c1SBharat Bhushan 		goto out_group_put;
188fb1ff4c1SBharat Bhushan 	}
189*704f5082SDiana Craciun 
190*704f5082SDiana Craciun 	ret = vfio_fsl_mc_init_device(vdev);
191*704f5082SDiana Craciun 	if (ret)
192*704f5082SDiana Craciun 		goto out_group_dev;
193*704f5082SDiana Craciun 
194fb1ff4c1SBharat Bhushan 	return 0;
195fb1ff4c1SBharat Bhushan 
196*704f5082SDiana Craciun out_group_dev:
197*704f5082SDiana Craciun 	vfio_del_group_dev(dev);
198fb1ff4c1SBharat Bhushan out_group_put:
199fb1ff4c1SBharat Bhushan 	vfio_iommu_group_put(group, dev);
200fb1ff4c1SBharat Bhushan 	return ret;
201fb1ff4c1SBharat Bhushan }
202fb1ff4c1SBharat Bhushan 
203fb1ff4c1SBharat Bhushan static int vfio_fsl_mc_remove(struct fsl_mc_device *mc_dev)
204fb1ff4c1SBharat Bhushan {
205fb1ff4c1SBharat Bhushan 	struct vfio_fsl_mc_device *vdev;
206fb1ff4c1SBharat Bhushan 	struct device *dev = &mc_dev->dev;
207fb1ff4c1SBharat Bhushan 
208fb1ff4c1SBharat Bhushan 	vdev = vfio_del_group_dev(dev);
209fb1ff4c1SBharat Bhushan 	if (!vdev)
210fb1ff4c1SBharat Bhushan 		return -EINVAL;
211fb1ff4c1SBharat Bhushan 
212*704f5082SDiana Craciun 	if (is_fsl_mc_bus_dprc(mc_dev)) {
213*704f5082SDiana Craciun 		dprc_remove_devices(mc_dev, NULL, 0);
214*704f5082SDiana Craciun 		dprc_cleanup(mc_dev);
215*704f5082SDiana Craciun 	}
216*704f5082SDiana Craciun 
217*704f5082SDiana Craciun 	if (vdev->nb.notifier_call)
218*704f5082SDiana Craciun 		bus_unregister_notifier(&fsl_mc_bus_type, &vdev->nb);
219*704f5082SDiana Craciun 
220fb1ff4c1SBharat Bhushan 	vfio_iommu_group_put(mc_dev->dev.iommu_group, dev);
221fb1ff4c1SBharat Bhushan 
222fb1ff4c1SBharat Bhushan 	return 0;
223fb1ff4c1SBharat Bhushan }
224fb1ff4c1SBharat Bhushan 
225fb1ff4c1SBharat Bhushan static struct fsl_mc_driver vfio_fsl_mc_driver = {
226fb1ff4c1SBharat Bhushan 	.probe		= vfio_fsl_mc_probe,
227fb1ff4c1SBharat Bhushan 	.remove		= vfio_fsl_mc_remove,
228fb1ff4c1SBharat Bhushan 	.driver	= {
229fb1ff4c1SBharat Bhushan 		.name	= "vfio-fsl-mc",
230fb1ff4c1SBharat Bhushan 		.owner	= THIS_MODULE,
231fb1ff4c1SBharat Bhushan 	},
232fb1ff4c1SBharat Bhushan };
233fb1ff4c1SBharat Bhushan 
234fb1ff4c1SBharat Bhushan static int __init vfio_fsl_mc_driver_init(void)
235fb1ff4c1SBharat Bhushan {
236fb1ff4c1SBharat Bhushan 	return fsl_mc_driver_register(&vfio_fsl_mc_driver);
237fb1ff4c1SBharat Bhushan }
238fb1ff4c1SBharat Bhushan 
239fb1ff4c1SBharat Bhushan static void __exit vfio_fsl_mc_driver_exit(void)
240fb1ff4c1SBharat Bhushan {
241fb1ff4c1SBharat Bhushan 	fsl_mc_driver_unregister(&vfio_fsl_mc_driver);
242fb1ff4c1SBharat Bhushan }
243fb1ff4c1SBharat Bhushan 
244fb1ff4c1SBharat Bhushan module_init(vfio_fsl_mc_driver_init);
245fb1ff4c1SBharat Bhushan module_exit(vfio_fsl_mc_driver_exit);
246fb1ff4c1SBharat Bhushan 
247fb1ff4c1SBharat Bhushan MODULE_LICENSE("Dual BSD/GPL");
248fb1ff4c1SBharat Bhushan MODULE_DESCRIPTION("VFIO for FSL-MC devices - User Level meta-driver");
249