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