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