xref: /freebsd-src/sys/compat/linuxkpi/common/src/linux_pci.c (revision d34188a0e18912e05f35e964b29e73f7e61c11f1)
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);
111b38dc0a1SMark Johnston 	if (device_get_driver(dev) != &pdrv->bsddriver)
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;
1248d59ecb2SHans Petter Selasky 	int error;
1258d59ecb2SHans Petter Selasky 
1261e3db1deSHans Petter Selasky 	linux_set_current(curthread);
1278d59ecb2SHans Petter Selasky 	pdrv = linux_pci_find(dev, &id);
1288d59ecb2SHans Petter Selasky 	pdev = device_get_softc(dev);
12906204f8eSHans Petter Selasky 	pdev->dev.parent = &linux_root_device;
1308d59ecb2SHans Petter Selasky 	pdev->dev.bsddev = dev;
1318d59ecb2SHans Petter Selasky 	INIT_LIST_HEAD(&pdev->dev.irqents);
1326373e95eSMark Johnston 	pdev->devfn = PCI_DEVFN(pci_get_slot(dev), pci_get_function(dev));
1338d59ecb2SHans Petter Selasky 	pdev->device = id->device;
1348d59ecb2SHans Petter Selasky 	pdev->vendor = id->vendor;
1356373e95eSMark Johnston 	pdev->class = pci_get_class(dev);
1366373e95eSMark Johnston 	pdev->revision = pci_get_revid(dev);
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));
143e996b07cSHans Petter Selasky 	rle = linux_pci_get_rle(pdev, SYS_RES_IRQ, 0);
144e996b07cSHans Petter Selasky 	if (rle != NULL)
1458d59ecb2SHans Petter Selasky 		pdev->dev.irq = rle->start;
1468d59ecb2SHans Petter Selasky 	else
1470a61267aSHans Petter Selasky 		pdev->dev.irq = LINUX_IRQ_INVALID;
1488d59ecb2SHans Petter Selasky 	pdev->irq = pdev->dev.irq;
1499183a497SHans 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);
1549183a497SHans 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 	return (error);
1638d59ecb2SHans Petter Selasky }
1648d59ecb2SHans Petter Selasky 
1658d59ecb2SHans Petter Selasky static int
1668d59ecb2SHans Petter Selasky linux_pci_detach(device_t dev)
1678d59ecb2SHans Petter Selasky {
1688d59ecb2SHans Petter Selasky 	struct pci_dev *pdev;
1698d59ecb2SHans Petter Selasky 
1701e3db1deSHans Petter Selasky 	linux_set_current(curthread);
1718d59ecb2SHans Petter Selasky 	pdev = device_get_softc(dev);
1729183a497SHans Petter Selasky 	DROP_GIANT();
1738d59ecb2SHans Petter Selasky 	pdev->pdrv->remove(pdev);
1749183a497SHans Petter Selasky 	PICKUP_GIANT();
1758d59ecb2SHans Petter Selasky 	spin_lock(&pci_lock);
1768d59ecb2SHans Petter Selasky 	list_del(&pdev->links);
1778d59ecb2SHans Petter Selasky 	spin_unlock(&pci_lock);
1788d59ecb2SHans Petter Selasky 	put_device(&pdev->dev);
1798d59ecb2SHans Petter Selasky 
1808d59ecb2SHans Petter Selasky 	return (0);
1818d59ecb2SHans Petter Selasky }
1828d59ecb2SHans Petter Selasky 
1837d133393SHans Petter Selasky static int
1847d133393SHans Petter Selasky linux_pci_suspend(device_t dev)
1857d133393SHans Petter Selasky {
186*d34188a0SMark Johnston 	const struct dev_pm_ops *pmops;
1877d133393SHans Petter Selasky 	struct pm_message pm = { };
1887d133393SHans Petter Selasky 	struct pci_dev *pdev;
189*d34188a0SMark Johnston 	int error;
1907d133393SHans Petter Selasky 
191*d34188a0SMark Johnston 	error = 0;
1921e3db1deSHans Petter Selasky 	linux_set_current(curthread);
1937d133393SHans Petter Selasky 	pdev = device_get_softc(dev);
194*d34188a0SMark Johnston 	pmops = pdev->pdrv->driver.pm;
195*d34188a0SMark Johnston 
1967d133393SHans Petter Selasky 	if (pdev->pdrv->suspend != NULL)
197*d34188a0SMark Johnston 		error = -pdev->pdrv->suspend(pdev, pm);
198*d34188a0SMark Johnston 	else if (pmops != NULL && pmops->suspend != NULL) {
199*d34188a0SMark Johnston 		error = -pmops->suspend(&pdev->dev);
200*d34188a0SMark Johnston 		if (error == 0 && pmops->suspend_late != NULL)
201*d34188a0SMark Johnston 			error = -pmops->suspend_late(&pdev->dev);
202*d34188a0SMark Johnston 	}
203*d34188a0SMark Johnston 	return (error);
2047d133393SHans Petter Selasky }
2057d133393SHans Petter Selasky 
2067d133393SHans Petter Selasky static int
2077d133393SHans Petter Selasky linux_pci_resume(device_t dev)
2087d133393SHans Petter Selasky {
209*d34188a0SMark Johnston 	const struct dev_pm_ops *pmops;
2107d133393SHans Petter Selasky 	struct pci_dev *pdev;
211*d34188a0SMark Johnston 	int error;
2127d133393SHans Petter Selasky 
213*d34188a0SMark Johnston 	error = 0;
2141e3db1deSHans Petter Selasky 	linux_set_current(curthread);
2157d133393SHans Petter Selasky 	pdev = device_get_softc(dev);
216*d34188a0SMark Johnston 	pmops = pdev->pdrv->driver.pm;
217*d34188a0SMark Johnston 
2187d133393SHans Petter Selasky 	if (pdev->pdrv->resume != NULL)
219*d34188a0SMark Johnston 		error = -pdev->pdrv->resume(pdev);
220*d34188a0SMark Johnston 	else if (pmops != NULL && pmops->resume != NULL) {
221*d34188a0SMark Johnston 		if (pmops->resume_early != NULL)
222*d34188a0SMark Johnston 			error = -pmops->resume_early(&pdev->dev);
223*d34188a0SMark Johnston 		if (error == 0 && pmops->resume != NULL)
224*d34188a0SMark Johnston 			error = -pmops->resume(&pdev->dev);
225*d34188a0SMark Johnston 	}
226*d34188a0SMark Johnston 	return (error);
2277d133393SHans Petter Selasky }
2287d133393SHans Petter Selasky 
2297d133393SHans Petter Selasky static int
2307d133393SHans Petter Selasky linux_pci_shutdown(device_t dev)
2317d133393SHans Petter Selasky {
2327d133393SHans Petter Selasky 	struct pci_dev *pdev;
2337d133393SHans Petter Selasky 
2341e3db1deSHans Petter Selasky 	linux_set_current(curthread);
2357d133393SHans Petter Selasky 	pdev = device_get_softc(dev);
236cb19abd2SHans Petter Selasky 	if (pdev->pdrv->shutdown != NULL) {
237cb19abd2SHans Petter Selasky 		DROP_GIANT();
2387d133393SHans Petter Selasky 		pdev->pdrv->shutdown(pdev);
239cb19abd2SHans Petter Selasky 		PICKUP_GIANT();
240cb19abd2SHans Petter Selasky 	}
2417d133393SHans Petter Selasky 	return (0);
2427d133393SHans Petter Selasky }
2437d133393SHans Petter Selasky 
2448d59ecb2SHans Petter Selasky int
2458d59ecb2SHans Petter Selasky pci_register_driver(struct pci_driver *pdrv)
2468d59ecb2SHans Petter Selasky {
2478d59ecb2SHans Petter Selasky 	devclass_t bus;
2488d59ecb2SHans Petter Selasky 	int error = 0;
2498d59ecb2SHans Petter Selasky 
2508d59ecb2SHans Petter Selasky 	bus = devclass_find("pci");
2518d59ecb2SHans Petter Selasky 
2521e3db1deSHans Petter Selasky 	linux_set_current(curthread);
2538d59ecb2SHans Petter Selasky 	spin_lock(&pci_lock);
2548d59ecb2SHans Petter Selasky 	list_add(&pdrv->links, &pci_drivers);
2558d59ecb2SHans Petter Selasky 	spin_unlock(&pci_lock);
256b38dc0a1SMark Johnston 	pdrv->bsddriver.name = pdrv->name;
257b38dc0a1SMark Johnston 	pdrv->bsddriver.methods = pci_methods;
258b38dc0a1SMark Johnston 	pdrv->bsddriver.size = sizeof(struct pci_dev);
259b38dc0a1SMark Johnston 
2608d59ecb2SHans Petter Selasky 	mtx_lock(&Giant);
2618d59ecb2SHans Petter Selasky 	if (bus != NULL) {
262b38dc0a1SMark Johnston 		error = devclass_add_driver(bus, &pdrv->bsddriver,
263b38dc0a1SMark Johnston 		    BUS_PASS_DEFAULT, &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 
2760a930cf0SMark Johnston 	spin_lock(&pci_lock);
2778d59ecb2SHans Petter Selasky 	list_del(&pdrv->links);
2780a930cf0SMark Johnston 	spin_unlock(&pci_lock);
2798d59ecb2SHans Petter Selasky 	mtx_lock(&Giant);
2808d59ecb2SHans Petter Selasky 	if (bus != NULL)
281b38dc0a1SMark Johnston 		devclass_delete_driver(bus, &pdrv->bsddriver);
2828d59ecb2SHans Petter Selasky 	mtx_unlock(&Giant);
2838d59ecb2SHans Petter Selasky }
284