xref: /freebsd-src/sys/compat/linuxkpi/common/src/linux_pci.c (revision 9183a497e74a12ab860730b88ef000ed9f343c88)
18d59ecb2SHans Petter Selasky /*-
2cb19abd2SHans Petter Selasky  * Copyright (c) 2015-2016 Mellanox Technologies, Ltd.
38d59ecb2SHans Petter Selasky  * All rights reserved.
48d59ecb2SHans Petter Selasky  *
58d59ecb2SHans Petter Selasky  * Redistribution and use in source and binary forms, with or without
68d59ecb2SHans Petter Selasky  * modification, are permitted provided that the following conditions
78d59ecb2SHans Petter Selasky  * are met:
88d59ecb2SHans Petter Selasky  * 1. Redistributions of source code must retain the above copyright
98d59ecb2SHans Petter Selasky  *    notice unmodified, this list of conditions, and the following
108d59ecb2SHans Petter Selasky  *    disclaimer.
118d59ecb2SHans Petter Selasky  * 2. Redistributions in binary form must reproduce the above copyright
128d59ecb2SHans Petter Selasky  *    notice, this list of conditions and the following disclaimer in the
138d59ecb2SHans Petter Selasky  *    documentation and/or other materials provided with the distribution.
148d59ecb2SHans Petter Selasky  *
158d59ecb2SHans Petter Selasky  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
168d59ecb2SHans Petter Selasky  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
178d59ecb2SHans Petter Selasky  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
188d59ecb2SHans Petter Selasky  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
198d59ecb2SHans Petter Selasky  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
208d59ecb2SHans Petter Selasky  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
218d59ecb2SHans Petter Selasky  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
228d59ecb2SHans Petter Selasky  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
238d59ecb2SHans Petter Selasky  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
248d59ecb2SHans Petter Selasky  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
258d59ecb2SHans Petter Selasky  */
268d59ecb2SHans Petter Selasky 
278d59ecb2SHans Petter Selasky #include <sys/cdefs.h>
288d59ecb2SHans Petter Selasky __FBSDID("$FreeBSD$");
298d59ecb2SHans Petter Selasky 
308d59ecb2SHans Petter Selasky #include <sys/param.h>
318d59ecb2SHans Petter Selasky #include <sys/systm.h>
328d59ecb2SHans Petter Selasky #include <sys/malloc.h>
338d59ecb2SHans Petter Selasky #include <sys/kernel.h>
348d59ecb2SHans Petter Selasky #include <sys/sysctl.h>
358d59ecb2SHans Petter Selasky #include <sys/lock.h>
368d59ecb2SHans Petter Selasky #include <sys/mutex.h>
378d59ecb2SHans Petter Selasky #include <sys/bus.h>
388d59ecb2SHans Petter Selasky #include <sys/fcntl.h>
398d59ecb2SHans Petter Selasky #include <sys/file.h>
408d59ecb2SHans Petter Selasky #include <sys/filio.h>
418d59ecb2SHans Petter Selasky #include <sys/rwlock.h>
428d59ecb2SHans Petter Selasky 
438d59ecb2SHans Petter Selasky #include <vm/vm.h>
448d59ecb2SHans Petter Selasky #include <vm/pmap.h>
458d59ecb2SHans Petter Selasky 
468d59ecb2SHans Petter Selasky #include <machine/stdarg.h>
478d59ecb2SHans Petter Selasky 
488d59ecb2SHans Petter Selasky #include <linux/kobject.h>
498d59ecb2SHans Petter Selasky #include <linux/device.h>
508d59ecb2SHans Petter Selasky #include <linux/slab.h>
518d59ecb2SHans Petter Selasky #include <linux/module.h>
528d59ecb2SHans Petter Selasky #include <linux/cdev.h>
538d59ecb2SHans Petter Selasky #include <linux/file.h>
548d59ecb2SHans Petter Selasky #include <linux/sysfs.h>
558d59ecb2SHans Petter Selasky #include <linux/mm.h>
568d59ecb2SHans Petter Selasky #include <linux/io.h>
578d59ecb2SHans Petter Selasky #include <linux/vmalloc.h>
588d59ecb2SHans Petter Selasky #include <linux/pci.h>
593ce12630SHans Petter Selasky #include <linux/compat.h>
608d59ecb2SHans Petter Selasky 
618d59ecb2SHans Petter Selasky static device_probe_t linux_pci_probe;
628d59ecb2SHans Petter Selasky static device_attach_t linux_pci_attach;
638d59ecb2SHans Petter Selasky static device_detach_t linux_pci_detach;
647d133393SHans Petter Selasky static device_suspend_t linux_pci_suspend;
657d133393SHans Petter Selasky static device_resume_t linux_pci_resume;
667d133393SHans Petter Selasky static device_shutdown_t linux_pci_shutdown;
678d59ecb2SHans Petter Selasky 
688d59ecb2SHans Petter Selasky static device_method_t pci_methods[] = {
698d59ecb2SHans Petter Selasky 	DEVMETHOD(device_probe, linux_pci_probe),
708d59ecb2SHans Petter Selasky 	DEVMETHOD(device_attach, linux_pci_attach),
718d59ecb2SHans Petter Selasky 	DEVMETHOD(device_detach, linux_pci_detach),
727d133393SHans Petter Selasky 	DEVMETHOD(device_suspend, linux_pci_suspend),
737d133393SHans Petter Selasky 	DEVMETHOD(device_resume, linux_pci_resume),
747d133393SHans Petter Selasky 	DEVMETHOD(device_shutdown, linux_pci_shutdown),
758d59ecb2SHans Petter Selasky 	DEVMETHOD_END
768d59ecb2SHans Petter Selasky };
778d59ecb2SHans Petter Selasky 
788d59ecb2SHans Petter Selasky static struct pci_driver *
798d59ecb2SHans Petter Selasky linux_pci_find(device_t dev, const struct pci_device_id **idp)
808d59ecb2SHans Petter Selasky {
818d59ecb2SHans Petter Selasky 	const struct pci_device_id *id;
828d59ecb2SHans Petter Selasky 	struct pci_driver *pdrv;
838d59ecb2SHans Petter Selasky 	uint16_t vendor;
848d59ecb2SHans Petter Selasky 	uint16_t device;
858d59ecb2SHans Petter Selasky 
868d59ecb2SHans Petter Selasky 	vendor = pci_get_vendor(dev);
878d59ecb2SHans Petter Selasky 	device = pci_get_device(dev);
888d59ecb2SHans Petter Selasky 
898d59ecb2SHans Petter Selasky 	spin_lock(&pci_lock);
908d59ecb2SHans Petter Selasky 	list_for_each_entry(pdrv, &pci_drivers, links) {
918d59ecb2SHans Petter Selasky 		for (id = pdrv->id_table; id->vendor != 0; id++) {
928d59ecb2SHans Petter Selasky 			if (vendor == id->vendor && device == id->device) {
938d59ecb2SHans Petter Selasky 				*idp = id;
948d59ecb2SHans Petter Selasky 				spin_unlock(&pci_lock);
958d59ecb2SHans Petter Selasky 				return (pdrv);
968d59ecb2SHans Petter Selasky 			}
978d59ecb2SHans Petter Selasky 		}
988d59ecb2SHans Petter Selasky 	}
998d59ecb2SHans Petter Selasky 	spin_unlock(&pci_lock);
1008d59ecb2SHans Petter Selasky 	return (NULL);
1018d59ecb2SHans Petter Selasky }
1028d59ecb2SHans Petter Selasky 
1038d59ecb2SHans Petter Selasky static int
1048d59ecb2SHans Petter Selasky linux_pci_probe(device_t dev)
1058d59ecb2SHans Petter Selasky {
1068d59ecb2SHans Petter Selasky 	const struct pci_device_id *id;
1078d59ecb2SHans Petter Selasky 	struct pci_driver *pdrv;
1088d59ecb2SHans Petter Selasky 
1098d59ecb2SHans Petter Selasky 	if ((pdrv = linux_pci_find(dev, &id)) == NULL)
1108d59ecb2SHans Petter Selasky 		return (ENXIO);
1118d59ecb2SHans Petter Selasky 	if (device_get_driver(dev) != &pdrv->driver)
1128d59ecb2SHans Petter Selasky 		return (ENXIO);
1138d59ecb2SHans Petter Selasky 	device_set_desc(dev, pdrv->name);
1148d59ecb2SHans Petter Selasky 	return (0);
1158d59ecb2SHans Petter Selasky }
1168d59ecb2SHans Petter Selasky 
1178d59ecb2SHans Petter Selasky static int
1188d59ecb2SHans Petter Selasky linux_pci_attach(device_t dev)
1198d59ecb2SHans Petter Selasky {
1208d59ecb2SHans Petter Selasky 	struct resource_list_entry *rle;
1218d59ecb2SHans Petter Selasky 	struct pci_dev *pdev;
1228d59ecb2SHans Petter Selasky 	struct pci_driver *pdrv;
1238d59ecb2SHans Petter Selasky 	const struct pci_device_id *id;
1243ce12630SHans Petter Selasky 	struct task_struct t;
1253ce12630SHans Petter Selasky 	struct thread *td;
1268d59ecb2SHans Petter Selasky 	int error;
1278d59ecb2SHans Petter Selasky 
1283ce12630SHans Petter Selasky 	td = curthread;
1293ce12630SHans Petter Selasky 	linux_set_current(td, &t);
1308d59ecb2SHans Petter Selasky 	pdrv = linux_pci_find(dev, &id);
1318d59ecb2SHans Petter Selasky 	pdev = device_get_softc(dev);
13206204f8eSHans Petter Selasky 	pdev->dev.parent = &linux_root_device;
1338d59ecb2SHans Petter Selasky 	pdev->dev.bsddev = dev;
1348d59ecb2SHans Petter Selasky 	INIT_LIST_HEAD(&pdev->dev.irqents);
1358d59ecb2SHans Petter Selasky 	pdev->device = id->device;
1368d59ecb2SHans Petter Selasky 	pdev->vendor = id->vendor;
1378d59ecb2SHans Petter Selasky 	pdev->dev.dma_mask = &pdev->dma_mask;
1388d59ecb2SHans Petter Selasky 	pdev->pdrv = pdrv;
13906204f8eSHans Petter Selasky 	kobject_init(&pdev->dev.kobj, &linux_dev_ktype);
1408d59ecb2SHans Petter Selasky 	kobject_set_name(&pdev->dev.kobj, device_get_nameunit(dev));
14106204f8eSHans Petter Selasky 	kobject_add(&pdev->dev.kobj, &linux_root_device.kobj,
1428d59ecb2SHans Petter Selasky 	    kobject_name(&pdev->dev.kobj));
1438d59ecb2SHans Petter Selasky 	rle = _pci_get_rle(pdev, SYS_RES_IRQ, 0);
1448d59ecb2SHans Petter Selasky 	if (rle)
1458d59ecb2SHans Petter Selasky 		pdev->dev.irq = rle->start;
1468d59ecb2SHans Petter Selasky 	else
1473092e0c5SHans Petter Selasky 		pdev->dev.irq = 255;
1488d59ecb2SHans Petter Selasky 	pdev->irq = pdev->dev.irq;
149*9183a497SHans Petter Selasky 	DROP_GIANT();
1508d59ecb2SHans Petter Selasky 	spin_lock(&pci_lock);
1518d59ecb2SHans Petter Selasky 	list_add(&pdev->links, &pci_devices);
1528d59ecb2SHans Petter Selasky 	spin_unlock(&pci_lock);
1538d59ecb2SHans Petter Selasky 	error = pdrv->probe(pdev, id);
154*9183a497SHans Petter Selasky 	PICKUP_GIANT();
1558d59ecb2SHans Petter Selasky 	if (error) {
1568d59ecb2SHans Petter Selasky 		spin_lock(&pci_lock);
1578d59ecb2SHans Petter Selasky 		list_del(&pdev->links);
1588d59ecb2SHans Petter Selasky 		spin_unlock(&pci_lock);
1598d59ecb2SHans Petter Selasky 		put_device(&pdev->dev);
1603ce12630SHans Petter Selasky 		error = -error;
1618d59ecb2SHans Petter Selasky 	}
1623ce12630SHans Petter Selasky 	linux_clear_current(td);
1633ce12630SHans Petter Selasky 	return (error);
1648d59ecb2SHans Petter Selasky }
1658d59ecb2SHans Petter Selasky 
1668d59ecb2SHans Petter Selasky static int
1678d59ecb2SHans Petter Selasky linux_pci_detach(device_t dev)
1688d59ecb2SHans Petter Selasky {
1698d59ecb2SHans Petter Selasky 	struct pci_dev *pdev;
1703ce12630SHans Petter Selasky 	struct task_struct t;
1713ce12630SHans Petter Selasky 	struct thread *td;
1728d59ecb2SHans Petter Selasky 
1733ce12630SHans Petter Selasky 	td = curthread;
1743ce12630SHans Petter Selasky 	linux_set_current(td, &t);
1758d59ecb2SHans Petter Selasky 	pdev = device_get_softc(dev);
176*9183a497SHans Petter Selasky 	DROP_GIANT();
1778d59ecb2SHans Petter Selasky 	pdev->pdrv->remove(pdev);
178*9183a497SHans Petter Selasky 	PICKUP_GIANT();
1798d59ecb2SHans Petter Selasky 	spin_lock(&pci_lock);
1808d59ecb2SHans Petter Selasky 	list_del(&pdev->links);
1818d59ecb2SHans Petter Selasky 	spin_unlock(&pci_lock);
1828d59ecb2SHans Petter Selasky 	put_device(&pdev->dev);
1833ce12630SHans Petter Selasky 	linux_clear_current(td);
1848d59ecb2SHans Petter Selasky 
1858d59ecb2SHans Petter Selasky 	return (0);
1868d59ecb2SHans Petter Selasky }
1878d59ecb2SHans Petter Selasky 
1887d133393SHans Petter Selasky static int
1897d133393SHans Petter Selasky linux_pci_suspend(device_t dev)
1907d133393SHans Petter Selasky {
1917d133393SHans Petter Selasky 	struct pm_message pm = { };
1927d133393SHans Petter Selasky 	struct pci_dev *pdev;
1933ce12630SHans Petter Selasky 	struct task_struct t;
1943ce12630SHans Petter Selasky 	struct thread *td;
1957d133393SHans Petter Selasky 	int err;
1967d133393SHans Petter Selasky 
1973ce12630SHans Petter Selasky 	td = curthread;
1983ce12630SHans Petter Selasky 	linux_set_current(td, &t);
1997d133393SHans Petter Selasky 	pdev = device_get_softc(dev);
2007d133393SHans Petter Selasky 	if (pdev->pdrv->suspend != NULL)
2017d133393SHans Petter Selasky 		err = -pdev->pdrv->suspend(pdev, pm);
2027d133393SHans Petter Selasky 	else
2037d133393SHans Petter Selasky 		err = 0;
2043ce12630SHans Petter Selasky 	linux_clear_current(td);
2057d133393SHans Petter Selasky 	return (err);
2067d133393SHans Petter Selasky }
2077d133393SHans Petter Selasky 
2087d133393SHans Petter Selasky static int
2097d133393SHans Petter Selasky linux_pci_resume(device_t dev)
2107d133393SHans Petter Selasky {
2117d133393SHans Petter Selasky 	struct pci_dev *pdev;
2123ce12630SHans Petter Selasky 	struct task_struct t;
2133ce12630SHans Petter Selasky 	struct thread *td;
2147d133393SHans Petter Selasky 	int err;
2157d133393SHans Petter Selasky 
2163ce12630SHans Petter Selasky 	td = curthread;
2173ce12630SHans Petter Selasky 	linux_set_current(td, &t);
2187d133393SHans Petter Selasky 	pdev = device_get_softc(dev);
2197d133393SHans Petter Selasky 	if (pdev->pdrv->resume != NULL)
2207d133393SHans Petter Selasky 		err = -pdev->pdrv->resume(pdev);
2217d133393SHans Petter Selasky 	else
2227d133393SHans Petter Selasky 		err = 0;
2233ce12630SHans Petter Selasky 	linux_clear_current(td);
2247d133393SHans Petter Selasky 	return (err);
2257d133393SHans Petter Selasky }
2267d133393SHans Petter Selasky 
2277d133393SHans Petter Selasky static int
2287d133393SHans Petter Selasky linux_pci_shutdown(device_t dev)
2297d133393SHans Petter Selasky {
2307d133393SHans Petter Selasky 	struct pci_dev *pdev;
2313ce12630SHans Petter Selasky 	struct task_struct t;
2323ce12630SHans Petter Selasky 	struct thread *td;
2337d133393SHans Petter Selasky 
2343ce12630SHans Petter Selasky 	td = curthread;
2353ce12630SHans Petter Selasky 	linux_set_current(td, &t);
2367d133393SHans Petter Selasky 	pdev = device_get_softc(dev);
237cb19abd2SHans Petter Selasky 	if (pdev->pdrv->shutdown != NULL) {
238cb19abd2SHans Petter Selasky 		DROP_GIANT();
2397d133393SHans Petter Selasky 		pdev->pdrv->shutdown(pdev);
240cb19abd2SHans Petter Selasky 		PICKUP_GIANT();
241cb19abd2SHans Petter Selasky 	}
2423ce12630SHans Petter Selasky 	linux_clear_current(td);
2437d133393SHans Petter Selasky 	return (0);
2447d133393SHans Petter Selasky }
2457d133393SHans Petter Selasky 
2468d59ecb2SHans Petter Selasky int
2478d59ecb2SHans Petter Selasky pci_register_driver(struct pci_driver *pdrv)
2488d59ecb2SHans Petter Selasky {
2498d59ecb2SHans Petter Selasky 	devclass_t bus;
2508d59ecb2SHans Petter Selasky 	int error = 0;
2518d59ecb2SHans Petter Selasky 
2528d59ecb2SHans Petter Selasky 	bus = devclass_find("pci");
2538d59ecb2SHans Petter Selasky 
2548d59ecb2SHans Petter Selasky 	spin_lock(&pci_lock);
2558d59ecb2SHans Petter Selasky 	list_add(&pdrv->links, &pci_drivers);
2568d59ecb2SHans Petter Selasky 	spin_unlock(&pci_lock);
2578d59ecb2SHans Petter Selasky 	pdrv->driver.name = pdrv->name;
2588d59ecb2SHans Petter Selasky 	pdrv->driver.methods = pci_methods;
2598d59ecb2SHans Petter Selasky 	pdrv->driver.size = sizeof(struct pci_dev);
2608d59ecb2SHans Petter Selasky 	mtx_lock(&Giant);
2618d59ecb2SHans Petter Selasky 	if (bus != NULL) {
2628d59ecb2SHans Petter Selasky 		error = devclass_add_driver(bus, &pdrv->driver, BUS_PASS_DEFAULT,
2638d59ecb2SHans Petter Selasky 		    &pdrv->bsdclass);
2648d59ecb2SHans Petter Selasky 	}
2658d59ecb2SHans Petter Selasky 	mtx_unlock(&Giant);
2668d59ecb2SHans Petter Selasky 	return (-error);
2678d59ecb2SHans Petter Selasky }
2688d59ecb2SHans Petter Selasky 
2698d59ecb2SHans Petter Selasky void
2708d59ecb2SHans Petter Selasky pci_unregister_driver(struct pci_driver *pdrv)
2718d59ecb2SHans Petter Selasky {
2728d59ecb2SHans Petter Selasky 	devclass_t bus;
2738d59ecb2SHans Petter Selasky 
2748d59ecb2SHans Petter Selasky 	bus = devclass_find("pci");
2758d59ecb2SHans Petter Selasky 
2768d59ecb2SHans Petter Selasky 	list_del(&pdrv->links);
2778d59ecb2SHans Petter Selasky 	mtx_lock(&Giant);
2788d59ecb2SHans Petter Selasky 	if (bus != NULL)
2798d59ecb2SHans Petter Selasky 		devclass_delete_driver(bus, &pdrv->driver);
2808d59ecb2SHans Petter Selasky 	mtx_unlock(&Giant);
2818d59ecb2SHans Petter Selasky }
2828d59ecb2SHans Petter Selasky 
283