18d59ecb2SHans Petter Selasky /*- 2cb19abd2SHans Petter Selasky * Copyright (c) 2015-2016 Mellanox Technologies, Ltd. 38d59ecb2SHans Petter Selasky * All rights reserved. 495edb10bSBjoern A. Zeeb * Copyright (c) 2020-2022 The FreeBSD Foundation 5d4a4960cSBjoern A. Zeeb * 6d4a4960cSBjoern A. Zeeb * Portions of this software were developed by Björn Zeeb 7d4a4960cSBjoern A. Zeeb * under sponsorship from the FreeBSD Foundation. 88d59ecb2SHans Petter Selasky * 98d59ecb2SHans Petter Selasky * Redistribution and use in source and binary forms, with or without 108d59ecb2SHans Petter Selasky * modification, are permitted provided that the following conditions 118d59ecb2SHans Petter Selasky * are met: 128d59ecb2SHans Petter Selasky * 1. Redistributions of source code must retain the above copyright 138d59ecb2SHans Petter Selasky * notice unmodified, this list of conditions, and the following 148d59ecb2SHans Petter Selasky * disclaimer. 158d59ecb2SHans Petter Selasky * 2. Redistributions in binary form must reproduce the above copyright 168d59ecb2SHans Petter Selasky * notice, this list of conditions and the following disclaimer in the 178d59ecb2SHans Petter Selasky * documentation and/or other materials provided with the distribution. 188d59ecb2SHans Petter Selasky * 198d59ecb2SHans Petter Selasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 208d59ecb2SHans Petter Selasky * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 218d59ecb2SHans Petter Selasky * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 228d59ecb2SHans Petter Selasky * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 238d59ecb2SHans Petter Selasky * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 248d59ecb2SHans Petter Selasky * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 258d59ecb2SHans Petter Selasky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 268d59ecb2SHans Petter Selasky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 278d59ecb2SHans Petter Selasky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 288d59ecb2SHans Petter Selasky * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 298d59ecb2SHans Petter Selasky */ 308d59ecb2SHans Petter Selasky 318d59ecb2SHans Petter Selasky #include <sys/cdefs.h> 328d59ecb2SHans Petter Selasky __FBSDID("$FreeBSD$"); 338d59ecb2SHans Petter Selasky 348d59ecb2SHans Petter Selasky #include <sys/param.h> 358d59ecb2SHans Petter Selasky #include <sys/systm.h> 36937a05baSJustin Hibbits #include <sys/bus.h> 378d59ecb2SHans Petter Selasky #include <sys/malloc.h> 388d59ecb2SHans Petter Selasky #include <sys/kernel.h> 398d59ecb2SHans Petter Selasky #include <sys/sysctl.h> 408d59ecb2SHans Petter Selasky #include <sys/lock.h> 418d59ecb2SHans Petter Selasky #include <sys/mutex.h> 428d59ecb2SHans Petter Selasky #include <sys/fcntl.h> 438d59ecb2SHans Petter Selasky #include <sys/file.h> 448d59ecb2SHans Petter Selasky #include <sys/filio.h> 459275cd0dSWarner Losh #include <sys/pciio.h> 46f211d536STycho Nightingale #include <sys/pctrie.h> 478d59ecb2SHans Petter Selasky #include <sys/rwlock.h> 488d59ecb2SHans Petter Selasky 498d59ecb2SHans Petter Selasky #include <vm/vm.h> 508d59ecb2SHans Petter Selasky #include <vm/pmap.h> 518d59ecb2SHans Petter Selasky 528d59ecb2SHans Petter Selasky #include <machine/stdarg.h> 538d59ecb2SHans Petter Selasky 549275cd0dSWarner Losh #include <dev/pci/pcivar.h> 559275cd0dSWarner Losh #include <dev/pci/pci_private.h> 569275cd0dSWarner Losh #include <dev/pci/pci_iov.h> 572b68c973SEmmanuel Vadot #include <dev/backlight/backlight.h> 589275cd0dSWarner Losh 59e8670741SBjoern A. Zeeb #include <linux/kernel.h> 608d59ecb2SHans Petter Selasky #include <linux/kobject.h> 618d59ecb2SHans Petter Selasky #include <linux/device.h> 628d59ecb2SHans Petter Selasky #include <linux/slab.h> 638d59ecb2SHans Petter Selasky #include <linux/module.h> 648d59ecb2SHans Petter Selasky #include <linux/cdev.h> 658d59ecb2SHans Petter Selasky #include <linux/file.h> 668d59ecb2SHans Petter Selasky #include <linux/sysfs.h> 678d59ecb2SHans Petter Selasky #include <linux/mm.h> 688d59ecb2SHans Petter Selasky #include <linux/io.h> 698d59ecb2SHans Petter Selasky #include <linux/vmalloc.h> 708d59ecb2SHans Petter Selasky #include <linux/pci.h> 713ce12630SHans Petter Selasky #include <linux/compat.h> 728d59ecb2SHans Petter Selasky 732b68c973SEmmanuel Vadot #include <linux/backlight.h> 742b68c973SEmmanuel Vadot 752b68c973SEmmanuel Vadot #include "backlight_if.h" 76de27805fSKonstantin Belousov #include "pcib_if.h" 772b68c973SEmmanuel Vadot 78105a37caSEmmanuel Vadot /* Undef the linux function macro defined in linux/pci.h */ 79105a37caSEmmanuel Vadot #undef pci_get_class 80105a37caSEmmanuel Vadot 81e8670741SBjoern A. Zeeb extern int linuxkpi_debug; 82e8670741SBjoern A. Zeeb 83e8670741SBjoern A. Zeeb SYSCTL_DECL(_compat_linuxkpi); 84e8670741SBjoern A. Zeeb 85e8670741SBjoern A. Zeeb static counter_u64_t lkpi_pci_nseg1_fail; 86e8670741SBjoern A. Zeeb SYSCTL_COUNTER_U64(_compat_linuxkpi, OID_AUTO, lkpi_pci_nseg1_fail, CTLFLAG_RD, 87e8670741SBjoern A. Zeeb &lkpi_pci_nseg1_fail, "Count of busdma mapping failures of single-segment"); 88e8670741SBjoern A. Zeeb 898d59ecb2SHans Petter Selasky static device_probe_t linux_pci_probe; 908d59ecb2SHans Petter Selasky static device_attach_t linux_pci_attach; 918d59ecb2SHans Petter Selasky static device_detach_t linux_pci_detach; 927d133393SHans Petter Selasky static device_suspend_t linux_pci_suspend; 937d133393SHans Petter Selasky static device_resume_t linux_pci_resume; 947d133393SHans Petter Selasky static device_shutdown_t linux_pci_shutdown; 952928e60eSKonstantin Belousov static pci_iov_init_t linux_pci_iov_init; 962928e60eSKonstantin Belousov static pci_iov_uninit_t linux_pci_iov_uninit; 972928e60eSKonstantin Belousov static pci_iov_add_vf_t linux_pci_iov_add_vf; 982b68c973SEmmanuel Vadot static int linux_backlight_get_status(device_t dev, struct backlight_props *props); 992b68c973SEmmanuel Vadot static int linux_backlight_update_status(device_t dev, struct backlight_props *props); 1002b68c973SEmmanuel Vadot static int linux_backlight_get_info(device_t dev, struct backlight_info *info); 1018d59ecb2SHans Petter Selasky 1028d59ecb2SHans Petter Selasky static device_method_t pci_methods[] = { 1038d59ecb2SHans Petter Selasky DEVMETHOD(device_probe, linux_pci_probe), 1048d59ecb2SHans Petter Selasky DEVMETHOD(device_attach, linux_pci_attach), 1058d59ecb2SHans Petter Selasky DEVMETHOD(device_detach, linux_pci_detach), 1067d133393SHans Petter Selasky DEVMETHOD(device_suspend, linux_pci_suspend), 1077d133393SHans Petter Selasky DEVMETHOD(device_resume, linux_pci_resume), 1087d133393SHans Petter Selasky DEVMETHOD(device_shutdown, linux_pci_shutdown), 1092928e60eSKonstantin Belousov DEVMETHOD(pci_iov_init, linux_pci_iov_init), 1102928e60eSKonstantin Belousov DEVMETHOD(pci_iov_uninit, linux_pci_iov_uninit), 1112928e60eSKonstantin Belousov DEVMETHOD(pci_iov_add_vf, linux_pci_iov_add_vf), 1122b68c973SEmmanuel Vadot 1132b68c973SEmmanuel Vadot /* backlight interface */ 1142b68c973SEmmanuel Vadot DEVMETHOD(backlight_update_status, linux_backlight_update_status), 1152b68c973SEmmanuel Vadot DEVMETHOD(backlight_get_status, linux_backlight_get_status), 1162b68c973SEmmanuel Vadot DEVMETHOD(backlight_get_info, linux_backlight_get_info), 1178d59ecb2SHans Petter Selasky DEVMETHOD_END 1188d59ecb2SHans Petter Selasky }; 1198d59ecb2SHans Petter Selasky 120*4cb3cb2dSJake Freeland const char *pci_power_names[] = { 121*4cb3cb2dSJake Freeland "UNKNOWN", "D0", "D1", "D2", "D3hot", "D3cold" 122*4cb3cb2dSJake Freeland }; 123*4cb3cb2dSJake Freeland 124f211d536STycho Nightingale struct linux_dma_priv { 125f211d536STycho Nightingale uint64_t dma_mask; 126f211d536STycho Nightingale bus_dma_tag_t dmat; 127c39eefe7SBjoern A. Zeeb uint64_t dma_coherent_mask; 128c39eefe7SBjoern A. Zeeb bus_dma_tag_t dmat_coherent; 129c39eefe7SBjoern A. Zeeb struct mtx lock; 130f211d536STycho Nightingale struct pctrie ptree; 131f211d536STycho Nightingale }; 132a6619e8dSHans Petter Selasky #define DMA_PRIV_LOCK(priv) mtx_lock(&(priv)->lock) 133a6619e8dSHans Petter Selasky #define DMA_PRIV_UNLOCK(priv) mtx_unlock(&(priv)->lock) 134f211d536STycho Nightingale 135f211d536STycho Nightingale static int 136f211d536STycho Nightingale linux_pdev_dma_uninit(struct pci_dev *pdev) 137f211d536STycho Nightingale { 138f211d536STycho Nightingale struct linux_dma_priv *priv; 139f211d536STycho Nightingale 140f211d536STycho Nightingale priv = pdev->dev.dma_priv; 141f211d536STycho Nightingale if (priv->dmat) 142f211d536STycho Nightingale bus_dma_tag_destroy(priv->dmat); 143c39eefe7SBjoern A. Zeeb if (priv->dmat_coherent) 144c39eefe7SBjoern A. Zeeb bus_dma_tag_destroy(priv->dmat_coherent); 145a6619e8dSHans Petter Selasky mtx_destroy(&priv->lock); 146f211d536STycho Nightingale pdev->dev.dma_priv = NULL; 147c39eefe7SBjoern A. Zeeb free(priv, M_DEVBUF); 148f211d536STycho Nightingale return (0); 149f211d536STycho Nightingale } 150f211d536STycho Nightingale 151c39eefe7SBjoern A. Zeeb static int 152c39eefe7SBjoern A. Zeeb linux_pdev_dma_init(struct pci_dev *pdev) 153c39eefe7SBjoern A. Zeeb { 154c39eefe7SBjoern A. Zeeb struct linux_dma_priv *priv; 155c39eefe7SBjoern A. Zeeb int error; 156c39eefe7SBjoern A. Zeeb 157c39eefe7SBjoern A. Zeeb priv = malloc(sizeof(*priv), M_DEVBUF, M_WAITOK | M_ZERO); 158c39eefe7SBjoern A. Zeeb 159c39eefe7SBjoern A. Zeeb mtx_init(&priv->lock, "lkpi-priv-dma", NULL, MTX_DEF); 160c39eefe7SBjoern A. Zeeb pctrie_init(&priv->ptree); 161c39eefe7SBjoern A. Zeeb 162c39eefe7SBjoern A. Zeeb pdev->dev.dma_priv = priv; 163c39eefe7SBjoern A. Zeeb 164c39eefe7SBjoern A. Zeeb /* Create a default DMA tags. */ 165c39eefe7SBjoern A. Zeeb error = linux_dma_tag_init(&pdev->dev, DMA_BIT_MASK(64)); 166c39eefe7SBjoern A. Zeeb if (error != 0) 167c39eefe7SBjoern A. Zeeb goto err; 168c39eefe7SBjoern A. Zeeb /* Coherent is lower 32bit only by default in Linux. */ 169c39eefe7SBjoern A. Zeeb error = linux_dma_tag_init_coherent(&pdev->dev, DMA_BIT_MASK(32)); 170c39eefe7SBjoern A. Zeeb if (error != 0) 171c39eefe7SBjoern A. Zeeb goto err; 172c39eefe7SBjoern A. Zeeb 173c39eefe7SBjoern A. Zeeb return (error); 174c39eefe7SBjoern A. Zeeb 175c39eefe7SBjoern A. Zeeb err: 176c39eefe7SBjoern A. Zeeb linux_pdev_dma_uninit(pdev); 177c39eefe7SBjoern A. Zeeb return (error); 178c39eefe7SBjoern A. Zeeb } 179c39eefe7SBjoern A. Zeeb 180f211d536STycho Nightingale int 181f211d536STycho Nightingale linux_dma_tag_init(struct device *dev, u64 dma_mask) 182f211d536STycho Nightingale { 183f211d536STycho Nightingale struct linux_dma_priv *priv; 184f211d536STycho Nightingale int error; 185f211d536STycho Nightingale 186f211d536STycho Nightingale priv = dev->dma_priv; 187f211d536STycho Nightingale 188f211d536STycho Nightingale if (priv->dmat) { 189f211d536STycho Nightingale if (priv->dma_mask == dma_mask) 190f211d536STycho Nightingale return (0); 191f211d536STycho Nightingale 192f211d536STycho Nightingale bus_dma_tag_destroy(priv->dmat); 193f211d536STycho Nightingale } 194f211d536STycho Nightingale 195f211d536STycho Nightingale priv->dma_mask = dma_mask; 196f211d536STycho Nightingale 197f211d536STycho Nightingale error = bus_dma_tag_create(bus_get_dma_tag(dev->bsddev), 198f211d536STycho Nightingale 1, 0, /* alignment, boundary */ 199f211d536STycho Nightingale dma_mask, /* lowaddr */ 200f211d536STycho Nightingale BUS_SPACE_MAXADDR, /* highaddr */ 201f211d536STycho Nightingale NULL, NULL, /* filtfunc, filtfuncarg */ 202b09626b3STycho Nightingale BUS_SPACE_MAXSIZE, /* maxsize */ 203f211d536STycho Nightingale 1, /* nsegments */ 204b09626b3STycho Nightingale BUS_SPACE_MAXSIZE, /* maxsegsz */ 205f211d536STycho Nightingale 0, /* flags */ 206f211d536STycho Nightingale NULL, NULL, /* lockfunc, lockfuncarg */ 207f211d536STycho Nightingale &priv->dmat); 208f211d536STycho Nightingale return (-error); 209f211d536STycho Nightingale } 210f211d536STycho Nightingale 211c39eefe7SBjoern A. Zeeb int 212c39eefe7SBjoern A. Zeeb linux_dma_tag_init_coherent(struct device *dev, u64 dma_mask) 213c39eefe7SBjoern A. Zeeb { 214c39eefe7SBjoern A. Zeeb struct linux_dma_priv *priv; 215c39eefe7SBjoern A. Zeeb int error; 216c39eefe7SBjoern A. Zeeb 217c39eefe7SBjoern A. Zeeb priv = dev->dma_priv; 218c39eefe7SBjoern A. Zeeb 219c39eefe7SBjoern A. Zeeb if (priv->dmat_coherent) { 220c39eefe7SBjoern A. Zeeb if (priv->dma_coherent_mask == dma_mask) 221c39eefe7SBjoern A. Zeeb return (0); 222c39eefe7SBjoern A. Zeeb 223c39eefe7SBjoern A. Zeeb bus_dma_tag_destroy(priv->dmat_coherent); 224c39eefe7SBjoern A. Zeeb } 225c39eefe7SBjoern A. Zeeb 226c39eefe7SBjoern A. Zeeb priv->dma_coherent_mask = dma_mask; 227c39eefe7SBjoern A. Zeeb 228c39eefe7SBjoern A. Zeeb error = bus_dma_tag_create(bus_get_dma_tag(dev->bsddev), 229c39eefe7SBjoern A. Zeeb 1, 0, /* alignment, boundary */ 230c39eefe7SBjoern A. Zeeb dma_mask, /* lowaddr */ 231c39eefe7SBjoern A. Zeeb BUS_SPACE_MAXADDR, /* highaddr */ 232c39eefe7SBjoern A. Zeeb NULL, NULL, /* filtfunc, filtfuncarg */ 233c39eefe7SBjoern A. Zeeb BUS_SPACE_MAXSIZE, /* maxsize */ 234c39eefe7SBjoern A. Zeeb 1, /* nsegments */ 235c39eefe7SBjoern A. Zeeb BUS_SPACE_MAXSIZE, /* maxsegsz */ 236c39eefe7SBjoern A. Zeeb 0, /* flags */ 237c39eefe7SBjoern A. Zeeb NULL, NULL, /* lockfunc, lockfuncarg */ 238c39eefe7SBjoern A. Zeeb &priv->dmat_coherent); 239c39eefe7SBjoern A. Zeeb return (-error); 240c39eefe7SBjoern A. Zeeb } 241c39eefe7SBjoern A. Zeeb 2428d59ecb2SHans Petter Selasky static struct pci_driver * 2438d59ecb2SHans Petter Selasky linux_pci_find(device_t dev, const struct pci_device_id **idp) 2448d59ecb2SHans Petter Selasky { 2458d59ecb2SHans Petter Selasky const struct pci_device_id *id; 2468d59ecb2SHans Petter Selasky struct pci_driver *pdrv; 2478d59ecb2SHans Petter Selasky uint16_t vendor; 2488d59ecb2SHans Petter Selasky uint16_t device; 249232028b3SHans Petter Selasky uint16_t subvendor; 250232028b3SHans Petter Selasky uint16_t subdevice; 2518d59ecb2SHans Petter Selasky 2528d59ecb2SHans Petter Selasky vendor = pci_get_vendor(dev); 2538d59ecb2SHans Petter Selasky device = pci_get_device(dev); 254232028b3SHans Petter Selasky subvendor = pci_get_subvendor(dev); 255232028b3SHans Petter Selasky subdevice = pci_get_subdevice(dev); 2568d59ecb2SHans Petter Selasky 2578d59ecb2SHans Petter Selasky spin_lock(&pci_lock); 258cf899348SBjoern A. Zeeb list_for_each_entry(pdrv, &pci_drivers, node) { 2598d59ecb2SHans Petter Selasky for (id = pdrv->id_table; id->vendor != 0; id++) { 260232028b3SHans Petter Selasky if (vendor == id->vendor && 261232028b3SHans Petter Selasky (PCI_ANY_ID == id->device || device == id->device) && 262232028b3SHans Petter Selasky (PCI_ANY_ID == id->subvendor || subvendor == id->subvendor) && 263232028b3SHans Petter Selasky (PCI_ANY_ID == id->subdevice || subdevice == id->subdevice)) { 2648d59ecb2SHans Petter Selasky *idp = id; 2658d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 2668d59ecb2SHans Petter Selasky return (pdrv); 2678d59ecb2SHans Petter Selasky } 2688d59ecb2SHans Petter Selasky } 2698d59ecb2SHans Petter Selasky } 2708d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 2718d59ecb2SHans Petter Selasky return (NULL); 2728d59ecb2SHans Petter Selasky } 2738d59ecb2SHans Petter Selasky 274105a37caSEmmanuel Vadot static void 2755f88df77SBjoern A. Zeeb lkpi_pci_dev_release(struct device *dev) 2765f88df77SBjoern A. Zeeb { 2775f88df77SBjoern A. Zeeb 2785f88df77SBjoern A. Zeeb lkpi_devres_release_free_list(dev); 2795f88df77SBjoern A. Zeeb spin_lock_destroy(&dev->devres_lock); 2805f88df77SBjoern A. Zeeb } 2815f88df77SBjoern A. Zeeb 2825f88df77SBjoern A. Zeeb static void 283105a37caSEmmanuel Vadot lkpifill_pci_dev(device_t dev, struct pci_dev *pdev) 284105a37caSEmmanuel Vadot { 285105a37caSEmmanuel Vadot 286105a37caSEmmanuel Vadot pdev->devfn = PCI_DEVFN(pci_get_slot(dev), pci_get_function(dev)); 287105a37caSEmmanuel Vadot pdev->vendor = pci_get_vendor(dev); 288105a37caSEmmanuel Vadot pdev->device = pci_get_device(dev); 2891fac2cb4SBjoern A. Zeeb pdev->subsystem_vendor = pci_get_subvendor(dev); 2901fac2cb4SBjoern A. Zeeb pdev->subsystem_device = pci_get_subdevice(dev); 291105a37caSEmmanuel Vadot pdev->class = pci_get_class(dev); 292105a37caSEmmanuel Vadot pdev->revision = pci_get_revid(dev); 2931fac2cb4SBjoern A. Zeeb pdev->bus = malloc(sizeof(*pdev->bus), M_DEVBUF, M_WAITOK | M_ZERO); 294b3b83625SBjoern A. Zeeb /* 295b3b83625SBjoern A. Zeeb * This should be the upstream bridge; pci_upstream_bridge() 296b3b83625SBjoern A. Zeeb * handles that case on demand as otherwise we'll shadow the 297b3b83625SBjoern A. Zeeb * entire PCI hierarchy. 298b3b83625SBjoern A. Zeeb */ 299105a37caSEmmanuel Vadot pdev->bus->self = pdev; 300105a37caSEmmanuel Vadot pdev->bus->number = pci_get_bus(dev); 301105a37caSEmmanuel Vadot pdev->bus->domain = pci_get_domain(dev); 3021fac2cb4SBjoern A. Zeeb pdev->dev.bsddev = dev; 3031fac2cb4SBjoern A. Zeeb pdev->dev.parent = &linux_root_device; 304d4a4960cSBjoern A. Zeeb pdev->dev.release = lkpi_pci_dev_release; 3051fac2cb4SBjoern A. Zeeb INIT_LIST_HEAD(&pdev->dev.irqents); 3061fac2cb4SBjoern A. Zeeb kobject_init(&pdev->dev.kobj, &linux_dev_ktype); 3071fac2cb4SBjoern A. Zeeb kobject_set_name(&pdev->dev.kobj, device_get_nameunit(dev)); 3081fac2cb4SBjoern A. Zeeb kobject_add(&pdev->dev.kobj, &linux_root_device.kobj, 3091fac2cb4SBjoern A. Zeeb kobject_name(&pdev->dev.kobj)); 310c3518147SBjoern A. Zeeb spin_lock_init(&pdev->dev.devres_lock); 311c3518147SBjoern A. Zeeb INIT_LIST_HEAD(&pdev->dev.devres_head); 3121fac2cb4SBjoern A. Zeeb } 3131fac2cb4SBjoern A. Zeeb 3141fac2cb4SBjoern A. Zeeb static void 3151fac2cb4SBjoern A. Zeeb lkpinew_pci_dev_release(struct device *dev) 3161fac2cb4SBjoern A. Zeeb { 3171fac2cb4SBjoern A. Zeeb struct pci_dev *pdev; 3181fac2cb4SBjoern A. Zeeb 3191fac2cb4SBjoern A. Zeeb pdev = to_pci_dev(dev); 3208e106c52SBjoern A. Zeeb if (pdev->root != NULL) 3218e106c52SBjoern A. Zeeb pci_dev_put(pdev->root); 322b3b83625SBjoern A. Zeeb if (pdev->bus->self != pdev) 323b3b83625SBjoern A. Zeeb pci_dev_put(pdev->bus->self); 3241fac2cb4SBjoern A. Zeeb free(pdev->bus, M_DEVBUF); 3251fac2cb4SBjoern A. Zeeb free(pdev, M_DEVBUF); 326105a37caSEmmanuel Vadot } 327105a37caSEmmanuel Vadot 3288e106c52SBjoern A. Zeeb struct pci_dev * 329105a37caSEmmanuel Vadot lkpinew_pci_dev(device_t dev) 330105a37caSEmmanuel Vadot { 331105a37caSEmmanuel Vadot struct pci_dev *pdev; 332105a37caSEmmanuel Vadot 333105a37caSEmmanuel Vadot pdev = malloc(sizeof(*pdev), M_DEVBUF, M_WAITOK|M_ZERO); 334105a37caSEmmanuel Vadot lkpifill_pci_dev(dev, pdev); 3351fac2cb4SBjoern A. Zeeb pdev->dev.release = lkpinew_pci_dev_release; 3361fac2cb4SBjoern A. Zeeb 337105a37caSEmmanuel Vadot return (pdev); 338105a37caSEmmanuel Vadot } 339105a37caSEmmanuel Vadot 340105a37caSEmmanuel Vadot struct pci_dev * 341105a37caSEmmanuel Vadot lkpi_pci_get_class(unsigned int class, struct pci_dev *from) 342105a37caSEmmanuel Vadot { 343105a37caSEmmanuel Vadot device_t dev; 344105a37caSEmmanuel Vadot device_t devfrom = NULL; 345105a37caSEmmanuel Vadot struct pci_dev *pdev; 346105a37caSEmmanuel Vadot 347105a37caSEmmanuel Vadot if (from != NULL) 348105a37caSEmmanuel Vadot devfrom = from->dev.bsddev; 349105a37caSEmmanuel Vadot 350105a37caSEmmanuel Vadot dev = pci_find_class_from(class >> 16, (class >> 8) & 0xFF, devfrom); 351105a37caSEmmanuel Vadot if (dev == NULL) 352105a37caSEmmanuel Vadot return (NULL); 353105a37caSEmmanuel Vadot 354105a37caSEmmanuel Vadot pdev = lkpinew_pci_dev(dev); 355105a37caSEmmanuel Vadot return (pdev); 356105a37caSEmmanuel Vadot } 357105a37caSEmmanuel Vadot 358105a37caSEmmanuel Vadot struct pci_dev * 359105a37caSEmmanuel Vadot lkpi_pci_get_domain_bus_and_slot(int domain, unsigned int bus, 360105a37caSEmmanuel Vadot unsigned int devfn) 361105a37caSEmmanuel Vadot { 362105a37caSEmmanuel Vadot device_t dev; 363105a37caSEmmanuel Vadot struct pci_dev *pdev; 364105a37caSEmmanuel Vadot 365105a37caSEmmanuel Vadot dev = pci_find_dbsf(domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); 366105a37caSEmmanuel Vadot if (dev == NULL) 367105a37caSEmmanuel Vadot return (NULL); 368105a37caSEmmanuel Vadot 369105a37caSEmmanuel Vadot pdev = lkpinew_pci_dev(dev); 370105a37caSEmmanuel Vadot return (pdev); 371105a37caSEmmanuel Vadot } 372105a37caSEmmanuel Vadot 3738d59ecb2SHans Petter Selasky static int 3748d59ecb2SHans Petter Selasky linux_pci_probe(device_t dev) 3758d59ecb2SHans Petter Selasky { 3768d59ecb2SHans Petter Selasky const struct pci_device_id *id; 3778d59ecb2SHans Petter Selasky struct pci_driver *pdrv; 3788d59ecb2SHans Petter Selasky 3798d59ecb2SHans Petter Selasky if ((pdrv = linux_pci_find(dev, &id)) == NULL) 3808d59ecb2SHans Petter Selasky return (ENXIO); 381b38dc0a1SMark Johnston if (device_get_driver(dev) != &pdrv->bsddriver) 3828d59ecb2SHans Petter Selasky return (ENXIO); 3838d59ecb2SHans Petter Selasky device_set_desc(dev, pdrv->name); 384b91dd79bSBjoern A. Zeeb 385b91dd79bSBjoern A. Zeeb /* Assume BSS initialized (should never return BUS_PROBE_SPECIFIC). */ 386b91dd79bSBjoern A. Zeeb if (pdrv->bsd_probe_return == 0) 38739d8c387SBjoern A. Zeeb return (BUS_PROBE_DEFAULT); 388b91dd79bSBjoern A. Zeeb else 389b91dd79bSBjoern A. Zeeb return (pdrv->bsd_probe_return); 3908d59ecb2SHans Petter Selasky } 3918d59ecb2SHans Petter Selasky 3928d59ecb2SHans Petter Selasky static int 3938d59ecb2SHans Petter Selasky linux_pci_attach(device_t dev) 3948d59ecb2SHans Petter Selasky { 395253dbe74SHans Petter Selasky const struct pci_device_id *id; 396253dbe74SHans Petter Selasky struct pci_driver *pdrv; 397253dbe74SHans Petter Selasky struct pci_dev *pdev; 398253dbe74SHans Petter Selasky 399253dbe74SHans Petter Selasky pdrv = linux_pci_find(dev, &id); 400253dbe74SHans Petter Selasky pdev = device_get_softc(dev); 401253dbe74SHans Petter Selasky 402253dbe74SHans Petter Selasky MPASS(pdrv != NULL); 403253dbe74SHans Petter Selasky MPASS(pdev != NULL); 404253dbe74SHans Petter Selasky 405253dbe74SHans Petter Selasky return (linux_pci_attach_device(dev, pdrv, id, pdev)); 406253dbe74SHans Petter Selasky } 407253dbe74SHans Petter Selasky 408253dbe74SHans Petter Selasky int 409253dbe74SHans Petter Selasky linux_pci_attach_device(device_t dev, struct pci_driver *pdrv, 410253dbe74SHans Petter Selasky const struct pci_device_id *id, struct pci_dev *pdev) 411253dbe74SHans Petter Selasky { 4128d59ecb2SHans Petter Selasky struct resource_list_entry *rle; 4130b7bd01aSMark Johnston device_t parent; 414de27805fSKonstantin Belousov uintptr_t rid; 4158d59ecb2SHans Petter Selasky int error; 416de27805fSKonstantin Belousov bool isdrm; 4178d59ecb2SHans Petter Selasky 4181e3db1deSHans Petter Selasky linux_set_current(curthread); 4190b7bd01aSMark Johnston 4200b7bd01aSMark Johnston parent = device_get_parent(dev); 421de27805fSKonstantin Belousov isdrm = pdrv != NULL && pdrv->isdrm; 422de27805fSKonstantin Belousov 423de27805fSKonstantin Belousov if (isdrm) { 4241fac2cb4SBjoern A. Zeeb struct pci_devinfo *dinfo; 4251fac2cb4SBjoern A. Zeeb 4260b7bd01aSMark Johnston dinfo = device_get_ivars(parent); 4270b7bd01aSMark Johnston device_set_ivars(dev, dinfo); 4280b7bd01aSMark Johnston } 4290b7bd01aSMark Johnston 430105a37caSEmmanuel Vadot lkpifill_pci_dev(dev, pdev); 431de27805fSKonstantin Belousov if (isdrm) 432de27805fSKonstantin Belousov PCI_GET_ID(device_get_parent(parent), parent, PCI_ID_RID, &rid); 433de27805fSKonstantin Belousov else 434de27805fSKonstantin Belousov PCI_GET_ID(parent, dev, PCI_ID_RID, &rid); 435de27805fSKonstantin Belousov pdev->devfn = rid; 4368d59ecb2SHans Petter Selasky pdev->pdrv = pdrv; 43782098c8bSJessica Clarke rle = linux_pci_get_rle(pdev, SYS_RES_IRQ, 0, false); 438e996b07cSHans Petter Selasky if (rle != NULL) 4398d59ecb2SHans Petter Selasky pdev->dev.irq = rle->start; 4408d59ecb2SHans Petter Selasky else 4410a61267aSHans Petter Selasky pdev->dev.irq = LINUX_IRQ_INVALID; 4428d59ecb2SHans Petter Selasky pdev->irq = pdev->dev.irq; 443f211d536STycho Nightingale error = linux_pdev_dma_init(pdev); 444f211d536STycho Nightingale if (error) 445eb6f5342SHans Petter Selasky goto out_dma_init; 4460b7bd01aSMark Johnston 4474c274849SEmmanuel Vadot TAILQ_INIT(&pdev->mmio); 4480b7bd01aSMark Johnston 4498d59ecb2SHans Petter Selasky spin_lock(&pci_lock); 4508d59ecb2SHans Petter Selasky list_add(&pdev->links, &pci_devices); 4518d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 4524b706099SHans Petter Selasky 453253dbe74SHans Petter Selasky if (pdrv != NULL) { 4548d59ecb2SHans Petter Selasky error = pdrv->probe(pdev, id); 455eb6f5342SHans Petter Selasky if (error) 456eb6f5342SHans Petter Selasky goto out_probe; 457253dbe74SHans Petter Selasky } 458eb6f5342SHans Petter Selasky return (0); 459eb6f5342SHans Petter Selasky 460eb6f5342SHans Petter Selasky out_probe: 461e2eb11e5SHans Petter Selasky free(pdev->bus, M_DEVBUF); 462eb6f5342SHans Petter Selasky linux_pdev_dma_uninit(pdev); 463eb6f5342SHans Petter Selasky out_dma_init: 4648d59ecb2SHans Petter Selasky spin_lock(&pci_lock); 4658d59ecb2SHans Petter Selasky list_del(&pdev->links); 4668d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 4678d59ecb2SHans Petter Selasky put_device(&pdev->dev); 468eb6f5342SHans Petter Selasky return (-error); 4698d59ecb2SHans Petter Selasky } 4708d59ecb2SHans Petter Selasky 4718d59ecb2SHans Petter Selasky static int 4728d59ecb2SHans Petter Selasky linux_pci_detach(device_t dev) 4738d59ecb2SHans Petter Selasky { 4748d59ecb2SHans Petter Selasky struct pci_dev *pdev; 4758d59ecb2SHans Petter Selasky 4768d59ecb2SHans Petter Selasky pdev = device_get_softc(dev); 4774b706099SHans Petter Selasky 478253dbe74SHans Petter Selasky MPASS(pdev != NULL); 479253dbe74SHans Petter Selasky 480253dbe74SHans Petter Selasky device_set_desc(dev, NULL); 481253dbe74SHans Petter Selasky 482253dbe74SHans Petter Selasky return (linux_pci_detach_device(pdev)); 483253dbe74SHans Petter Selasky } 484253dbe74SHans Petter Selasky 485253dbe74SHans Petter Selasky int 486253dbe74SHans Petter Selasky linux_pci_detach_device(struct pci_dev *pdev) 487253dbe74SHans Petter Selasky { 488253dbe74SHans Petter Selasky 489253dbe74SHans Petter Selasky linux_set_current(curthread); 490253dbe74SHans Petter Selasky 491253dbe74SHans Petter Selasky if (pdev->pdrv != NULL) 4928d59ecb2SHans Petter Selasky pdev->pdrv->remove(pdev); 493e2eb11e5SHans Petter Selasky 4948e106c52SBjoern A. Zeeb if (pdev->root != NULL) 4958e106c52SBjoern A. Zeeb pci_dev_put(pdev->root); 496e2eb11e5SHans Petter Selasky free(pdev->bus, M_DEVBUF); 497f211d536STycho Nightingale linux_pdev_dma_uninit(pdev); 4984b706099SHans Petter Selasky 4998d59ecb2SHans Petter Selasky spin_lock(&pci_lock); 5008d59ecb2SHans Petter Selasky list_del(&pdev->links); 5018d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 5028d59ecb2SHans Petter Selasky put_device(&pdev->dev); 5038d59ecb2SHans Petter Selasky 5048d59ecb2SHans Petter Selasky return (0); 5058d59ecb2SHans Petter Selasky } 5068d59ecb2SHans Petter Selasky 5077d133393SHans Petter Selasky static int 508d4a4960cSBjoern A. Zeeb lkpi_pci_disable_dev(struct device *dev) 509d4a4960cSBjoern A. Zeeb { 510d4a4960cSBjoern A. Zeeb 511d4a4960cSBjoern A. Zeeb (void) pci_disable_io(dev->bsddev, SYS_RES_MEMORY); 512d4a4960cSBjoern A. Zeeb (void) pci_disable_io(dev->bsddev, SYS_RES_IOPORT); 513d4a4960cSBjoern A. Zeeb return (0); 514d4a4960cSBjoern A. Zeeb } 515d4a4960cSBjoern A. Zeeb 5163ea682e2SWarner Losh struct pci_devres * 5173ea682e2SWarner Losh lkpi_pci_devres_get_alloc(struct pci_dev *pdev) 5183ea682e2SWarner Losh { 5193ea682e2SWarner Losh struct pci_devres *dr; 5203ea682e2SWarner Losh 5213ea682e2SWarner Losh dr = lkpi_devres_find(&pdev->dev, lkpi_pci_devres_release, NULL, NULL); 5223ea682e2SWarner Losh if (dr == NULL) { 5233ea682e2SWarner Losh dr = lkpi_devres_alloc(lkpi_pci_devres_release, sizeof(*dr), 5243ea682e2SWarner Losh GFP_KERNEL | __GFP_ZERO); 5253ea682e2SWarner Losh if (dr != NULL) 5263ea682e2SWarner Losh lkpi_devres_add(&pdev->dev, dr); 5273ea682e2SWarner Losh } 5283ea682e2SWarner Losh 5293ea682e2SWarner Losh return (dr); 5303ea682e2SWarner Losh } 5313ea682e2SWarner Losh 532d4a4960cSBjoern A. Zeeb void 533d4a4960cSBjoern A. Zeeb lkpi_pci_devres_release(struct device *dev, void *p) 534d4a4960cSBjoern A. Zeeb { 535d4a4960cSBjoern A. Zeeb struct pci_devres *dr; 536d4a4960cSBjoern A. Zeeb struct pci_dev *pdev; 537d4a4960cSBjoern A. Zeeb int bar; 538d4a4960cSBjoern A. Zeeb 539d4a4960cSBjoern A. Zeeb pdev = to_pci_dev(dev); 540d4a4960cSBjoern A. Zeeb dr = p; 541d4a4960cSBjoern A. Zeeb 542d4a4960cSBjoern A. Zeeb if (pdev->msix_enabled) 543d4a4960cSBjoern A. Zeeb lkpi_pci_disable_msix(pdev); 544d4a4960cSBjoern A. Zeeb if (pdev->msi_enabled) 545d4a4960cSBjoern A. Zeeb lkpi_pci_disable_msi(pdev); 546d4a4960cSBjoern A. Zeeb 547d4a4960cSBjoern A. Zeeb if (dr->enable_io && lkpi_pci_disable_dev(dev) == 0) 548d4a4960cSBjoern A. Zeeb dr->enable_io = false; 549d4a4960cSBjoern A. Zeeb 550d4a4960cSBjoern A. Zeeb if (dr->region_mask == 0) 551d4a4960cSBjoern A. Zeeb return; 552d4a4960cSBjoern A. Zeeb for (bar = PCIR_MAX_BAR_0; bar >= 0; bar--) { 553d4a4960cSBjoern A. Zeeb 554d4a4960cSBjoern A. Zeeb if ((dr->region_mask & (1 << bar)) == 0) 555d4a4960cSBjoern A. Zeeb continue; 556d4a4960cSBjoern A. Zeeb pci_release_region(pdev, bar); 557d4a4960cSBjoern A. Zeeb } 558d4a4960cSBjoern A. Zeeb } 559d4a4960cSBjoern A. Zeeb 5602bf3361dSWarner Losh struct pcim_iomap_devres * 5612bf3361dSWarner Losh lkpi_pcim_iomap_devres_find(struct pci_dev *pdev) 5622bf3361dSWarner Losh { 5632bf3361dSWarner Losh struct pcim_iomap_devres *dr; 5642bf3361dSWarner Losh 5652bf3361dSWarner Losh dr = lkpi_devres_find(&pdev->dev, lkpi_pcim_iomap_table_release, 5662bf3361dSWarner Losh NULL, NULL); 5672bf3361dSWarner Losh if (dr == NULL) { 5682bf3361dSWarner Losh dr = lkpi_devres_alloc(lkpi_pcim_iomap_table_release, 5692bf3361dSWarner Losh sizeof(*dr), GFP_KERNEL | __GFP_ZERO); 5702bf3361dSWarner Losh if (dr != NULL) 5712bf3361dSWarner Losh lkpi_devres_add(&pdev->dev, dr); 5722bf3361dSWarner Losh } 5732bf3361dSWarner Losh 5742bf3361dSWarner Losh if (dr == NULL) 5752bf3361dSWarner Losh device_printf(pdev->dev.bsddev, "%s: NULL\n", __func__); 5762bf3361dSWarner Losh 5772bf3361dSWarner Losh return (dr); 5782bf3361dSWarner Losh } 5792bf3361dSWarner Losh 580d4a4960cSBjoern A. Zeeb void 581d4a4960cSBjoern A. Zeeb lkpi_pcim_iomap_table_release(struct device *dev, void *p) 582d4a4960cSBjoern A. Zeeb { 583d4a4960cSBjoern A. Zeeb struct pcim_iomap_devres *dr; 584d4a4960cSBjoern A. Zeeb struct pci_dev *pdev; 585d4a4960cSBjoern A. Zeeb int bar; 586d4a4960cSBjoern A. Zeeb 587d4a4960cSBjoern A. Zeeb dr = p; 588d4a4960cSBjoern A. Zeeb pdev = to_pci_dev(dev); 589d4a4960cSBjoern A. Zeeb for (bar = PCIR_MAX_BAR_0; bar >= 0; bar--) { 590d4a4960cSBjoern A. Zeeb 591d4a4960cSBjoern A. Zeeb if (dr->mmio_table[bar] == NULL) 592d4a4960cSBjoern A. Zeeb continue; 593d4a4960cSBjoern A. Zeeb 594d4a4960cSBjoern A. Zeeb pci_iounmap(pdev, dr->mmio_table[bar]); 595d4a4960cSBjoern A. Zeeb } 596d4a4960cSBjoern A. Zeeb } 597d4a4960cSBjoern A. Zeeb 598d4a4960cSBjoern A. Zeeb static int 5997d133393SHans Petter Selasky linux_pci_suspend(device_t dev) 6007d133393SHans Petter Selasky { 601d34188a0SMark Johnston const struct dev_pm_ops *pmops; 6027d133393SHans Petter Selasky struct pm_message pm = { }; 6037d133393SHans Petter Selasky struct pci_dev *pdev; 604d34188a0SMark Johnston int error; 6057d133393SHans Petter Selasky 606d34188a0SMark Johnston error = 0; 6071e3db1deSHans Petter Selasky linux_set_current(curthread); 6087d133393SHans Petter Selasky pdev = device_get_softc(dev); 609d34188a0SMark Johnston pmops = pdev->pdrv->driver.pm; 610d34188a0SMark Johnston 6117d133393SHans Petter Selasky if (pdev->pdrv->suspend != NULL) 612d34188a0SMark Johnston error = -pdev->pdrv->suspend(pdev, pm); 613d34188a0SMark Johnston else if (pmops != NULL && pmops->suspend != NULL) { 614d34188a0SMark Johnston error = -pmops->suspend(&pdev->dev); 615d34188a0SMark Johnston if (error == 0 && pmops->suspend_late != NULL) 616d34188a0SMark Johnston error = -pmops->suspend_late(&pdev->dev); 617d34188a0SMark Johnston } 618d34188a0SMark Johnston return (error); 6197d133393SHans Petter Selasky } 6207d133393SHans Petter Selasky 6217d133393SHans Petter Selasky static int 6227d133393SHans Petter Selasky linux_pci_resume(device_t dev) 6237d133393SHans Petter Selasky { 624d34188a0SMark Johnston const struct dev_pm_ops *pmops; 6257d133393SHans Petter Selasky struct pci_dev *pdev; 626d34188a0SMark Johnston int error; 6277d133393SHans Petter Selasky 628d34188a0SMark Johnston error = 0; 6291e3db1deSHans Petter Selasky linux_set_current(curthread); 6307d133393SHans Petter Selasky pdev = device_get_softc(dev); 631d34188a0SMark Johnston pmops = pdev->pdrv->driver.pm; 632d34188a0SMark Johnston 6337d133393SHans Petter Selasky if (pdev->pdrv->resume != NULL) 634d34188a0SMark Johnston error = -pdev->pdrv->resume(pdev); 635d34188a0SMark Johnston else if (pmops != NULL && pmops->resume != NULL) { 636d34188a0SMark Johnston if (pmops->resume_early != NULL) 637d34188a0SMark Johnston error = -pmops->resume_early(&pdev->dev); 638d34188a0SMark Johnston if (error == 0 && pmops->resume != NULL) 639d34188a0SMark Johnston error = -pmops->resume(&pdev->dev); 640d34188a0SMark Johnston } 641d34188a0SMark Johnston return (error); 6427d133393SHans Petter Selasky } 6437d133393SHans Petter Selasky 6447d133393SHans Petter Selasky static int 6457d133393SHans Petter Selasky linux_pci_shutdown(device_t dev) 6467d133393SHans Petter Selasky { 6477d133393SHans Petter Selasky struct pci_dev *pdev; 6487d133393SHans Petter Selasky 6491e3db1deSHans Petter Selasky linux_set_current(curthread); 6507d133393SHans Petter Selasky pdev = device_get_softc(dev); 6514b706099SHans Petter Selasky if (pdev->pdrv->shutdown != NULL) 6527d133393SHans Petter Selasky pdev->pdrv->shutdown(pdev); 6537d133393SHans Petter Selasky return (0); 6547d133393SHans Petter Selasky } 6557d133393SHans Petter Selasky 6560b7bd01aSMark Johnston static int 6572928e60eSKonstantin Belousov linux_pci_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t *pf_config) 6582928e60eSKonstantin Belousov { 6592928e60eSKonstantin Belousov struct pci_dev *pdev; 6602928e60eSKonstantin Belousov int error; 6612928e60eSKonstantin Belousov 6622928e60eSKonstantin Belousov linux_set_current(curthread); 6632928e60eSKonstantin Belousov pdev = device_get_softc(dev); 6642928e60eSKonstantin Belousov if (pdev->pdrv->bsd_iov_init != NULL) 6652928e60eSKonstantin Belousov error = pdev->pdrv->bsd_iov_init(dev, num_vfs, pf_config); 6662928e60eSKonstantin Belousov else 6672928e60eSKonstantin Belousov error = EINVAL; 6682928e60eSKonstantin Belousov return (error); 6692928e60eSKonstantin Belousov } 6702928e60eSKonstantin Belousov 6712928e60eSKonstantin Belousov static void 6722928e60eSKonstantin Belousov linux_pci_iov_uninit(device_t dev) 6732928e60eSKonstantin Belousov { 6742928e60eSKonstantin Belousov struct pci_dev *pdev; 6752928e60eSKonstantin Belousov 6762928e60eSKonstantin Belousov linux_set_current(curthread); 6772928e60eSKonstantin Belousov pdev = device_get_softc(dev); 6782928e60eSKonstantin Belousov if (pdev->pdrv->bsd_iov_uninit != NULL) 6792928e60eSKonstantin Belousov pdev->pdrv->bsd_iov_uninit(dev); 6802928e60eSKonstantin Belousov } 6812928e60eSKonstantin Belousov 6822928e60eSKonstantin Belousov static int 6832928e60eSKonstantin Belousov linux_pci_iov_add_vf(device_t dev, uint16_t vfnum, const nvlist_t *vf_config) 6842928e60eSKonstantin Belousov { 6852928e60eSKonstantin Belousov struct pci_dev *pdev; 6862928e60eSKonstantin Belousov int error; 6872928e60eSKonstantin Belousov 6882928e60eSKonstantin Belousov linux_set_current(curthread); 6892928e60eSKonstantin Belousov pdev = device_get_softc(dev); 6902928e60eSKonstantin Belousov if (pdev->pdrv->bsd_iov_add_vf != NULL) 6912928e60eSKonstantin Belousov error = pdev->pdrv->bsd_iov_add_vf(dev, vfnum, vf_config); 6922928e60eSKonstantin Belousov else 6932928e60eSKonstantin Belousov error = EINVAL; 6942928e60eSKonstantin Belousov return (error); 6952928e60eSKonstantin Belousov } 6962928e60eSKonstantin Belousov 6972928e60eSKonstantin Belousov static int 6980b7bd01aSMark Johnston _linux_pci_register_driver(struct pci_driver *pdrv, devclass_t dc) 6998d59ecb2SHans Petter Selasky { 7000b7bd01aSMark Johnston int error; 7018d59ecb2SHans Petter Selasky 7021e3db1deSHans Petter Selasky linux_set_current(curthread); 7038d59ecb2SHans Petter Selasky spin_lock(&pci_lock); 704cf899348SBjoern A. Zeeb list_add(&pdrv->node, &pci_drivers); 7058d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 70681d058dfSBjoern A. Zeeb if (pdrv->bsddriver.name == NULL) 707b38dc0a1SMark Johnston pdrv->bsddriver.name = pdrv->name; 708b38dc0a1SMark Johnston pdrv->bsddriver.methods = pci_methods; 709b38dc0a1SMark Johnston pdrv->bsddriver.size = sizeof(struct pci_dev); 710b38dc0a1SMark Johnston 711c6df6f53SWarner Losh bus_topo_lock(); 7120b7bd01aSMark Johnston error = devclass_add_driver(dc, &pdrv->bsddriver, 713b38dc0a1SMark Johnston BUS_PASS_DEFAULT, &pdrv->bsdclass); 714c6df6f53SWarner Losh bus_topo_unlock(); 7158d59ecb2SHans Petter Selasky return (-error); 7168d59ecb2SHans Petter Selasky } 7178d59ecb2SHans Petter Selasky 7180b7bd01aSMark Johnston int 7190b7bd01aSMark Johnston linux_pci_register_driver(struct pci_driver *pdrv) 7200b7bd01aSMark Johnston { 7210b7bd01aSMark Johnston devclass_t dc; 7220b7bd01aSMark Johnston 7230b7bd01aSMark Johnston dc = devclass_find("pci"); 7240b7bd01aSMark Johnston if (dc == NULL) 7250b7bd01aSMark Johnston return (-ENXIO); 7260b7bd01aSMark Johnston pdrv->isdrm = false; 7270b7bd01aSMark Johnston return (_linux_pci_register_driver(pdrv, dc)); 7280b7bd01aSMark Johnston } 7290b7bd01aSMark Johnston 73082098c8bSJessica Clarke struct resource_list_entry * 73182098c8bSJessica Clarke linux_pci_reserve_bar(struct pci_dev *pdev, struct resource_list *rl, 73282098c8bSJessica Clarke int type, int rid) 73382098c8bSJessica Clarke { 73482098c8bSJessica Clarke device_t dev; 73582098c8bSJessica Clarke struct resource *res; 73682098c8bSJessica Clarke 73782098c8bSJessica Clarke KASSERT(type == SYS_RES_IOPORT || type == SYS_RES_MEMORY, 73882098c8bSJessica Clarke ("trying to reserve non-BAR type %d", type)); 73982098c8bSJessica Clarke 74082098c8bSJessica Clarke dev = pdev->pdrv != NULL && pdev->pdrv->isdrm ? 74182098c8bSJessica Clarke device_get_parent(pdev->dev.bsddev) : pdev->dev.bsddev; 74282098c8bSJessica Clarke res = pci_reserve_map(device_get_parent(dev), dev, type, &rid, 0, ~0, 74382098c8bSJessica Clarke 1, 1, 0); 74482098c8bSJessica Clarke if (res == NULL) 74582098c8bSJessica Clarke return (NULL); 74682098c8bSJessica Clarke return (resource_list_find(rl, type, rid)); 74782098c8bSJessica Clarke } 74882098c8bSJessica Clarke 749937a05baSJustin Hibbits unsigned long 750937a05baSJustin Hibbits pci_resource_start(struct pci_dev *pdev, int bar) 751937a05baSJustin Hibbits { 752937a05baSJustin Hibbits struct resource_list_entry *rle; 7534eaa2fdeSJustin Hibbits rman_res_t newstart; 754937a05baSJustin Hibbits device_t dev; 7551fb99e97SMark Johnston int error; 756937a05baSJustin Hibbits 75782098c8bSJessica Clarke if ((rle = linux_pci_get_bar(pdev, bar, true)) == NULL) 758937a05baSJustin Hibbits return (0); 759de27805fSKonstantin Belousov dev = pdev->pdrv != NULL && pdev->pdrv->isdrm ? 760de27805fSKonstantin Belousov device_get_parent(pdev->dev.bsddev) : pdev->dev.bsddev; 7611fb99e97SMark Johnston error = bus_translate_resource(dev, rle->type, rle->start, &newstart); 7621fb99e97SMark Johnston if (error != 0) { 7631fb99e97SMark Johnston device_printf(pdev->dev.bsddev, 7641fb99e97SMark Johnston "translate of %#jx failed: %d\n", 7651fb99e97SMark Johnston (uintmax_t)rle->start, error); 766937a05baSJustin Hibbits return (0); 767937a05baSJustin Hibbits } 768937a05baSJustin Hibbits return (newstart); 769937a05baSJustin Hibbits } 770937a05baSJustin Hibbits 771937a05baSJustin Hibbits unsigned long 772937a05baSJustin Hibbits pci_resource_len(struct pci_dev *pdev, int bar) 773937a05baSJustin Hibbits { 774937a05baSJustin Hibbits struct resource_list_entry *rle; 775937a05baSJustin Hibbits 77682098c8bSJessica Clarke if ((rle = linux_pci_get_bar(pdev, bar, true)) == NULL) 777937a05baSJustin Hibbits return (0); 778937a05baSJustin Hibbits return (rle->count); 779937a05baSJustin Hibbits } 780937a05baSJustin Hibbits 7810b7bd01aSMark Johnston int 7821cdb2534SWarner Losh pci_request_region(struct pci_dev *pdev, int bar, const char *res_name) 7831cdb2534SWarner Losh { 7841cdb2534SWarner Losh struct resource *res; 7851cdb2534SWarner Losh struct pci_devres *dr; 7861cdb2534SWarner Losh struct pci_mmio_region *mmio; 7871cdb2534SWarner Losh int rid; 7881cdb2534SWarner Losh int type; 7891cdb2534SWarner Losh 7901cdb2534SWarner Losh type = pci_resource_type(pdev, bar); 7911cdb2534SWarner Losh if (type < 0) 7921cdb2534SWarner Losh return (-ENODEV); 7931cdb2534SWarner Losh rid = PCIR_BAR(bar); 7941cdb2534SWarner Losh res = bus_alloc_resource_any(pdev->dev.bsddev, type, &rid, 7951cdb2534SWarner Losh RF_ACTIVE|RF_SHAREABLE); 7961cdb2534SWarner Losh if (res == NULL) { 7971cdb2534SWarner Losh device_printf(pdev->dev.bsddev, "%s: failed to alloc " 7981cdb2534SWarner Losh "bar %d type %d rid %d\n", 7991cdb2534SWarner Losh __func__, bar, type, PCIR_BAR(bar)); 8001cdb2534SWarner Losh return (-ENODEV); 8011cdb2534SWarner Losh } 8021cdb2534SWarner Losh 8031cdb2534SWarner Losh /* 8041cdb2534SWarner Losh * It seems there is an implicit devres tracking on these if the device 8051cdb2534SWarner Losh * is managed; otherwise the resources are not automatiaclly freed on 8061cdb2534SWarner Losh * FreeBSD/LinuxKPI tough they should be/are expected to be by Linux 8071cdb2534SWarner Losh * drivers. 8081cdb2534SWarner Losh */ 8091cdb2534SWarner Losh dr = lkpi_pci_devres_find(pdev); 8101cdb2534SWarner Losh if (dr != NULL) { 8111cdb2534SWarner Losh dr->region_mask |= (1 << bar); 8121cdb2534SWarner Losh dr->region_table[bar] = res; 8131cdb2534SWarner Losh } 8141cdb2534SWarner Losh 8151cdb2534SWarner Losh /* Even if the device is not managed we need to track it for iomap. */ 8161cdb2534SWarner Losh mmio = malloc(sizeof(*mmio), M_DEVBUF, M_WAITOK | M_ZERO); 8171cdb2534SWarner Losh mmio->rid = PCIR_BAR(bar); 8181cdb2534SWarner Losh mmio->type = type; 8191cdb2534SWarner Losh mmio->res = res; 8201cdb2534SWarner Losh TAILQ_INSERT_TAIL(&pdev->mmio, mmio, next); 8211cdb2534SWarner Losh 8221cdb2534SWarner Losh return (0); 8231cdb2534SWarner Losh } 8241cdb2534SWarner Losh 8251cdb2534SWarner Losh struct resource * 8261cdb2534SWarner Losh _lkpi_pci_iomap(struct pci_dev *pdev, int bar, int mmio_size __unused) 8271cdb2534SWarner Losh { 8281cdb2534SWarner Losh struct pci_mmio_region *mmio, *p; 8291cdb2534SWarner Losh int type; 8301cdb2534SWarner Losh 8311cdb2534SWarner Losh type = pci_resource_type(pdev, bar); 8321cdb2534SWarner Losh if (type < 0) { 8331cdb2534SWarner Losh device_printf(pdev->dev.bsddev, "%s: bar %d type %d\n", 8341cdb2534SWarner Losh __func__, bar, type); 8351cdb2534SWarner Losh return (NULL); 8361cdb2534SWarner Losh } 8371cdb2534SWarner Losh 8381cdb2534SWarner Losh /* 8391cdb2534SWarner Losh * Check for duplicate mappings. 8401cdb2534SWarner Losh * This can happen if a driver calls pci_request_region() first. 8411cdb2534SWarner Losh */ 8421cdb2534SWarner Losh TAILQ_FOREACH_SAFE(mmio, &pdev->mmio, next, p) { 8431cdb2534SWarner Losh if (mmio->type == type && mmio->rid == PCIR_BAR(bar)) { 8441cdb2534SWarner Losh return (mmio->res); 8451cdb2534SWarner Losh } 8461cdb2534SWarner Losh } 8471cdb2534SWarner Losh 8481cdb2534SWarner Losh mmio = malloc(sizeof(*mmio), M_DEVBUF, M_WAITOK | M_ZERO); 8491cdb2534SWarner Losh mmio->rid = PCIR_BAR(bar); 8501cdb2534SWarner Losh mmio->type = type; 8511cdb2534SWarner Losh mmio->res = bus_alloc_resource_any(pdev->dev.bsddev, mmio->type, 8521cdb2534SWarner Losh &mmio->rid, RF_ACTIVE|RF_SHAREABLE); 8531cdb2534SWarner Losh if (mmio->res == NULL) { 8541cdb2534SWarner Losh device_printf(pdev->dev.bsddev, "%s: failed to alloc " 8551cdb2534SWarner Losh "bar %d type %d rid %d\n", 8561cdb2534SWarner Losh __func__, bar, type, PCIR_BAR(bar)); 8571cdb2534SWarner Losh free(mmio, M_DEVBUF); 8581cdb2534SWarner Losh return (NULL); 8591cdb2534SWarner Losh } 8601cdb2534SWarner Losh TAILQ_INSERT_TAIL(&pdev->mmio, mmio, next); 8611cdb2534SWarner Losh 8621cdb2534SWarner Losh return (mmio->res); 8631cdb2534SWarner Losh } 8641cdb2534SWarner Losh 8651cdb2534SWarner Losh int 8660b7bd01aSMark Johnston linux_pci_register_drm_driver(struct pci_driver *pdrv) 8670b7bd01aSMark Johnston { 8680b7bd01aSMark Johnston devclass_t dc; 8690b7bd01aSMark Johnston 8700b7bd01aSMark Johnston dc = devclass_create("vgapci"); 8710b7bd01aSMark Johnston if (dc == NULL) 8720b7bd01aSMark Johnston return (-ENXIO); 8730b7bd01aSMark Johnston pdrv->isdrm = true; 8740b7bd01aSMark Johnston pdrv->name = "drmn"; 8750b7bd01aSMark Johnston return (_linux_pci_register_driver(pdrv, dc)); 8760b7bd01aSMark Johnston } 8770b7bd01aSMark Johnston 8788d59ecb2SHans Petter Selasky void 8790b7bd01aSMark Johnston linux_pci_unregister_driver(struct pci_driver *pdrv) 8808d59ecb2SHans Petter Selasky { 8818d59ecb2SHans Petter Selasky devclass_t bus; 8828d59ecb2SHans Petter Selasky 8838d59ecb2SHans Petter Selasky bus = devclass_find("pci"); 8848d59ecb2SHans Petter Selasky 8850a930cf0SMark Johnston spin_lock(&pci_lock); 886cf899348SBjoern A. Zeeb list_del(&pdrv->node); 8870a930cf0SMark Johnston spin_unlock(&pci_lock); 888c6df6f53SWarner Losh bus_topo_lock(); 8898d59ecb2SHans Petter Selasky if (bus != NULL) 890b38dc0a1SMark Johnston devclass_delete_driver(bus, &pdrv->bsddriver); 891c6df6f53SWarner Losh bus_topo_unlock(); 8928d59ecb2SHans Petter Selasky } 893f211d536STycho Nightingale 8945098ed5fSJohannes Lundberg void 8955098ed5fSJohannes Lundberg linux_pci_unregister_drm_driver(struct pci_driver *pdrv) 8965098ed5fSJohannes Lundberg { 8975098ed5fSJohannes Lundberg devclass_t bus; 8985098ed5fSJohannes Lundberg 8995098ed5fSJohannes Lundberg bus = devclass_find("vgapci"); 9005098ed5fSJohannes Lundberg 9015098ed5fSJohannes Lundberg spin_lock(&pci_lock); 902cf899348SBjoern A. Zeeb list_del(&pdrv->node); 9035098ed5fSJohannes Lundberg spin_unlock(&pci_lock); 904c6df6f53SWarner Losh bus_topo_lock(); 9055098ed5fSJohannes Lundberg if (bus != NULL) 9065098ed5fSJohannes Lundberg devclass_delete_driver(bus, &pdrv->bsddriver); 907c6df6f53SWarner Losh bus_topo_unlock(); 9085098ed5fSJohannes Lundberg } 9095098ed5fSJohannes Lundberg 91036b5c440SWarner Losh int 91136b5c440SWarner Losh pci_alloc_irq_vectors(struct pci_dev *pdev, int minv, int maxv, 91236b5c440SWarner Losh unsigned int flags) 91336b5c440SWarner Losh { 91436b5c440SWarner Losh int error; 91536b5c440SWarner Losh 91636b5c440SWarner Losh if (flags & PCI_IRQ_MSIX) { 91736b5c440SWarner Losh struct msix_entry *entries; 91836b5c440SWarner Losh int i; 91936b5c440SWarner Losh 92036b5c440SWarner Losh entries = kcalloc(maxv, sizeof(*entries), GFP_KERNEL); 92136b5c440SWarner Losh if (entries == NULL) { 92236b5c440SWarner Losh error = -ENOMEM; 92336b5c440SWarner Losh goto out; 92436b5c440SWarner Losh } 92536b5c440SWarner Losh for (i = 0; i < maxv; ++i) 92636b5c440SWarner Losh entries[i].entry = i; 92736b5c440SWarner Losh error = pci_enable_msix(pdev, entries, maxv); 92836b5c440SWarner Losh out: 92936b5c440SWarner Losh kfree(entries); 93036b5c440SWarner Losh if (error == 0 && pdev->msix_enabled) 93136b5c440SWarner Losh return (pdev->dev.irq_end - pdev->dev.irq_start); 93236b5c440SWarner Losh } 93336b5c440SWarner Losh if (flags & PCI_IRQ_MSI) { 93436b5c440SWarner Losh error = pci_enable_msi(pdev); 93536b5c440SWarner Losh if (error == 0 && pdev->msi_enabled) 93636b5c440SWarner Losh return (pdev->dev.irq_end - pdev->dev.irq_start); 93736b5c440SWarner Losh } 93836b5c440SWarner Losh if (flags & PCI_IRQ_LEGACY) { 93936b5c440SWarner Losh if (pdev->irq) 94036b5c440SWarner Losh return (1); 94136b5c440SWarner Losh } 94236b5c440SWarner Losh 94336b5c440SWarner Losh return (-EINVAL); 94436b5c440SWarner Losh } 94536b5c440SWarner Losh 946d4fedb75SHans Petter Selasky CTASSERT(sizeof(dma_addr_t) <= sizeof(uint64_t)); 947d4fedb75SHans Petter Selasky 948f211d536STycho Nightingale struct linux_dma_obj { 949f211d536STycho Nightingale void *vaddr; 950d4fedb75SHans Petter Selasky uint64_t dma_addr; 951f211d536STycho Nightingale bus_dmamap_t dmamap; 952c39eefe7SBjoern A. Zeeb bus_dma_tag_t dmat; 953f211d536STycho Nightingale }; 954f211d536STycho Nightingale 955f211d536STycho Nightingale static uma_zone_t linux_dma_trie_zone; 956f211d536STycho Nightingale static uma_zone_t linux_dma_obj_zone; 957f211d536STycho Nightingale 958f211d536STycho Nightingale static void 959f211d536STycho Nightingale linux_dma_init(void *arg) 960f211d536STycho Nightingale { 961f211d536STycho Nightingale 962f211d536STycho Nightingale linux_dma_trie_zone = uma_zcreate("linux_dma_pctrie", 963f211d536STycho Nightingale pctrie_node_size(), NULL, NULL, pctrie_zone_init, NULL, 964f211d536STycho Nightingale UMA_ALIGN_PTR, 0); 965f211d536STycho Nightingale linux_dma_obj_zone = uma_zcreate("linux_dma_object", 966f211d536STycho Nightingale sizeof(struct linux_dma_obj), NULL, NULL, NULL, NULL, 967f211d536STycho Nightingale UMA_ALIGN_PTR, 0); 968e8670741SBjoern A. Zeeb lkpi_pci_nseg1_fail = counter_u64_alloc(M_WAITOK); 969f211d536STycho Nightingale } 970f211d536STycho Nightingale SYSINIT(linux_dma, SI_SUB_DRIVERS, SI_ORDER_THIRD, linux_dma_init, NULL); 971f211d536STycho Nightingale 972f211d536STycho Nightingale static void 973f211d536STycho Nightingale linux_dma_uninit(void *arg) 974f211d536STycho Nightingale { 975f211d536STycho Nightingale 976e8670741SBjoern A. Zeeb counter_u64_free(lkpi_pci_nseg1_fail); 977f211d536STycho Nightingale uma_zdestroy(linux_dma_obj_zone); 978f211d536STycho Nightingale uma_zdestroy(linux_dma_trie_zone); 979f211d536STycho Nightingale } 980f211d536STycho Nightingale SYSUNINIT(linux_dma, SI_SUB_DRIVERS, SI_ORDER_THIRD, linux_dma_uninit, NULL); 981f211d536STycho Nightingale 982f211d536STycho Nightingale static void * 983f211d536STycho Nightingale linux_dma_trie_alloc(struct pctrie *ptree) 984f211d536STycho Nightingale { 985f211d536STycho Nightingale 98692a15f94SRyan Stone return (uma_zalloc(linux_dma_trie_zone, M_NOWAIT)); 987f211d536STycho Nightingale } 988f211d536STycho Nightingale 989f211d536STycho Nightingale static void 990f211d536STycho Nightingale linux_dma_trie_free(struct pctrie *ptree, void *node) 991f211d536STycho Nightingale { 992f211d536STycho Nightingale 993f211d536STycho Nightingale uma_zfree(linux_dma_trie_zone, node); 994f211d536STycho Nightingale } 995f211d536STycho Nightingale 996f211d536STycho Nightingale PCTRIE_DEFINE(LINUX_DMA, linux_dma_obj, dma_addr, linux_dma_trie_alloc, 997f211d536STycho Nightingale linux_dma_trie_free); 998f211d536STycho Nightingale 999b961c0f2STycho Nightingale #if defined(__i386__) || defined(__amd64__) || defined(__aarch64__) 1000c39eefe7SBjoern A. Zeeb static dma_addr_t 1001c39eefe7SBjoern A. Zeeb linux_dma_map_phys_common(struct device *dev, vm_paddr_t phys, size_t len, 1002c39eefe7SBjoern A. Zeeb bus_dma_tag_t dmat) 1003f211d536STycho Nightingale { 1004f211d536STycho Nightingale struct linux_dma_priv *priv; 1005f211d536STycho Nightingale struct linux_dma_obj *obj; 1006f211d536STycho Nightingale int error, nseg; 1007f211d536STycho Nightingale bus_dma_segment_t seg; 1008f211d536STycho Nightingale 1009f211d536STycho Nightingale priv = dev->dma_priv; 1010f211d536STycho Nightingale 1011b961c0f2STycho Nightingale /* 1012b961c0f2STycho Nightingale * If the resultant mapping will be entirely 1:1 with the 1013b961c0f2STycho Nightingale * physical address, short-circuit the remainder of the 1014b961c0f2STycho Nightingale * bus_dma API. This avoids tracking collisions in the pctrie 1015b961c0f2STycho Nightingale * with the additional benefit of reducing overhead. 1016b961c0f2STycho Nightingale */ 1017c39eefe7SBjoern A. Zeeb if (bus_dma_id_mapped(dmat, phys, len)) 1018b961c0f2STycho Nightingale return (phys); 1019b961c0f2STycho Nightingale 102092a15f94SRyan Stone obj = uma_zalloc(linux_dma_obj_zone, M_NOWAIT); 102192a15f94SRyan Stone if (obj == NULL) { 102292a15f94SRyan Stone return (0); 102392a15f94SRyan Stone } 1024c39eefe7SBjoern A. Zeeb obj->dmat = dmat; 1025f211d536STycho Nightingale 1026a6619e8dSHans Petter Selasky DMA_PRIV_LOCK(priv); 1027c39eefe7SBjoern A. Zeeb if (bus_dmamap_create(obj->dmat, 0, &obj->dmamap) != 0) { 1028a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1029f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1030f211d536STycho Nightingale return (0); 1031f211d536STycho Nightingale } 1032f211d536STycho Nightingale 1033f211d536STycho Nightingale nseg = -1; 1034c39eefe7SBjoern A. Zeeb if (_bus_dmamap_load_phys(obj->dmat, obj->dmamap, phys, len, 1035f211d536STycho Nightingale BUS_DMA_NOWAIT, &seg, &nseg) != 0) { 1036c39eefe7SBjoern A. Zeeb bus_dmamap_destroy(obj->dmat, obj->dmamap); 1037a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1038f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1039e8670741SBjoern A. Zeeb counter_u64_add(lkpi_pci_nseg1_fail, 1); 1040e8670741SBjoern A. Zeeb if (linuxkpi_debug) 1041e8670741SBjoern A. Zeeb dump_stack(); 1042f211d536STycho Nightingale return (0); 1043f211d536STycho Nightingale } 1044f211d536STycho Nightingale 1045f211d536STycho Nightingale KASSERT(++nseg == 1, ("More than one segment (nseg=%d)", nseg)); 1046f211d536STycho Nightingale obj->dma_addr = seg.ds_addr; 1047f211d536STycho Nightingale 1048f211d536STycho Nightingale error = LINUX_DMA_PCTRIE_INSERT(&priv->ptree, obj); 1049f211d536STycho Nightingale if (error != 0) { 1050c39eefe7SBjoern A. Zeeb bus_dmamap_unload(obj->dmat, obj->dmamap); 1051c39eefe7SBjoern A. Zeeb bus_dmamap_destroy(obj->dmat, obj->dmamap); 1052a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1053f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1054f211d536STycho Nightingale return (0); 1055f211d536STycho Nightingale } 1056a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1057f211d536STycho Nightingale return (obj->dma_addr); 1058f211d536STycho Nightingale } 1059b961c0f2STycho Nightingale #else 106012698731SBjoern A. Zeeb static dma_addr_t 1061c39eefe7SBjoern A. Zeeb linux_dma_map_phys_common(struct device *dev __unused, vm_paddr_t phys, 1062c39eefe7SBjoern A. Zeeb size_t len __unused, bus_dma_tag_t dmat __unused) 1063b961c0f2STycho Nightingale { 1064b961c0f2STycho Nightingale return (phys); 1065b961c0f2STycho Nightingale } 1066b961c0f2STycho Nightingale #endif 1067f211d536STycho Nightingale 1068c39eefe7SBjoern A. Zeeb dma_addr_t 1069c39eefe7SBjoern A. Zeeb linux_dma_map_phys(struct device *dev, vm_paddr_t phys, size_t len) 1070c39eefe7SBjoern A. Zeeb { 1071c39eefe7SBjoern A. Zeeb struct linux_dma_priv *priv; 1072c39eefe7SBjoern A. Zeeb 1073c39eefe7SBjoern A. Zeeb priv = dev->dma_priv; 1074c39eefe7SBjoern A. Zeeb return (linux_dma_map_phys_common(dev, phys, len, priv->dmat)); 1075c39eefe7SBjoern A. Zeeb } 1076c39eefe7SBjoern A. Zeeb 1077b961c0f2STycho Nightingale #if defined(__i386__) || defined(__amd64__) || defined(__aarch64__) 1078f211d536STycho Nightingale void 1079f211d536STycho Nightingale linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len) 1080f211d536STycho Nightingale { 1081f211d536STycho Nightingale struct linux_dma_priv *priv; 1082f211d536STycho Nightingale struct linux_dma_obj *obj; 1083f211d536STycho Nightingale 1084f211d536STycho Nightingale priv = dev->dma_priv; 1085f211d536STycho Nightingale 1086b961c0f2STycho Nightingale if (pctrie_is_empty(&priv->ptree)) 1087b961c0f2STycho Nightingale return; 1088b961c0f2STycho Nightingale 1089a6619e8dSHans Petter Selasky DMA_PRIV_LOCK(priv); 1090f211d536STycho Nightingale obj = LINUX_DMA_PCTRIE_LOOKUP(&priv->ptree, dma_addr); 1091f211d536STycho Nightingale if (obj == NULL) { 1092a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1093f211d536STycho Nightingale return; 1094f211d536STycho Nightingale } 1095f211d536STycho Nightingale LINUX_DMA_PCTRIE_REMOVE(&priv->ptree, dma_addr); 1096c39eefe7SBjoern A. Zeeb bus_dmamap_unload(obj->dmat, obj->dmamap); 1097c39eefe7SBjoern A. Zeeb bus_dmamap_destroy(obj->dmat, obj->dmamap); 1098a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1099f211d536STycho Nightingale 1100f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1101f211d536STycho Nightingale } 1102b961c0f2STycho Nightingale #else 1103b961c0f2STycho Nightingale void 1104b961c0f2STycho Nightingale linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len) 1105b961c0f2STycho Nightingale { 1106b961c0f2STycho Nightingale } 1107b961c0f2STycho Nightingale #endif 1108f211d536STycho Nightingale 1109c39eefe7SBjoern A. Zeeb void * 1110c39eefe7SBjoern A. Zeeb linux_dma_alloc_coherent(struct device *dev, size_t size, 1111c39eefe7SBjoern A. Zeeb dma_addr_t *dma_handle, gfp_t flag) 1112c39eefe7SBjoern A. Zeeb { 1113c39eefe7SBjoern A. Zeeb struct linux_dma_priv *priv; 1114c39eefe7SBjoern A. Zeeb vm_paddr_t high; 1115c39eefe7SBjoern A. Zeeb size_t align; 1116c39eefe7SBjoern A. Zeeb void *mem; 1117c39eefe7SBjoern A. Zeeb 1118c39eefe7SBjoern A. Zeeb if (dev == NULL || dev->dma_priv == NULL) { 1119c39eefe7SBjoern A. Zeeb *dma_handle = 0; 1120c39eefe7SBjoern A. Zeeb return (NULL); 1121c39eefe7SBjoern A. Zeeb } 1122c39eefe7SBjoern A. Zeeb priv = dev->dma_priv; 1123c39eefe7SBjoern A. Zeeb if (priv->dma_coherent_mask) 1124c39eefe7SBjoern A. Zeeb high = priv->dma_coherent_mask; 1125c39eefe7SBjoern A. Zeeb else 1126c39eefe7SBjoern A. Zeeb /* Coherent is lower 32bit only by default in Linux. */ 1127c39eefe7SBjoern A. Zeeb high = BUS_SPACE_MAXADDR_32BIT; 1128c39eefe7SBjoern A. Zeeb align = PAGE_SIZE << get_order(size); 1129c39eefe7SBjoern A. Zeeb /* Always zero the allocation. */ 1130c39eefe7SBjoern A. Zeeb flag |= M_ZERO; 1131f49fd63aSJohn Baldwin mem = kmem_alloc_contig(size, flag & GFP_NATIVE_MASK, 0, high, 1132c39eefe7SBjoern A. Zeeb align, 0, VM_MEMATTR_DEFAULT); 1133c39eefe7SBjoern A. Zeeb if (mem != NULL) { 1134c39eefe7SBjoern A. Zeeb *dma_handle = linux_dma_map_phys_common(dev, vtophys(mem), size, 1135c39eefe7SBjoern A. Zeeb priv->dmat_coherent); 1136c39eefe7SBjoern A. Zeeb if (*dma_handle == 0) { 1137f49fd63aSJohn Baldwin kmem_free(mem, size); 1138c39eefe7SBjoern A. Zeeb mem = NULL; 1139c39eefe7SBjoern A. Zeeb } 1140c39eefe7SBjoern A. Zeeb } else { 1141c39eefe7SBjoern A. Zeeb *dma_handle = 0; 1142c39eefe7SBjoern A. Zeeb } 1143c39eefe7SBjoern A. Zeeb return (mem); 1144c39eefe7SBjoern A. Zeeb } 1145c39eefe7SBjoern A. Zeeb 11467105f0d9SBjoern A. Zeeb struct lkpi_devres_dmam_coherent { 11477105f0d9SBjoern A. Zeeb size_t size; 11487105f0d9SBjoern A. Zeeb dma_addr_t *handle; 11497105f0d9SBjoern A. Zeeb void *mem; 11507105f0d9SBjoern A. Zeeb }; 11517105f0d9SBjoern A. Zeeb 11527105f0d9SBjoern A. Zeeb static void 11537105f0d9SBjoern A. Zeeb lkpi_dmam_free_coherent(struct device *dev, void *p) 11547105f0d9SBjoern A. Zeeb { 11557105f0d9SBjoern A. Zeeb struct lkpi_devres_dmam_coherent *dr; 11567105f0d9SBjoern A. Zeeb 11577105f0d9SBjoern A. Zeeb dr = p; 11587105f0d9SBjoern A. Zeeb dma_free_coherent(dev, dr->size, dr->mem, *dr->handle); 11597105f0d9SBjoern A. Zeeb } 11607105f0d9SBjoern A. Zeeb 11617105f0d9SBjoern A. Zeeb void * 11627105f0d9SBjoern A. Zeeb linuxkpi_dmam_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, 11637105f0d9SBjoern A. Zeeb gfp_t flag) 11647105f0d9SBjoern A. Zeeb { 11657105f0d9SBjoern A. Zeeb struct lkpi_devres_dmam_coherent *dr; 11667105f0d9SBjoern A. Zeeb 11677105f0d9SBjoern A. Zeeb dr = lkpi_devres_alloc(lkpi_dmam_free_coherent, 11687105f0d9SBjoern A. Zeeb sizeof(*dr), GFP_KERNEL | __GFP_ZERO); 11697105f0d9SBjoern A. Zeeb 11707105f0d9SBjoern A. Zeeb if (dr == NULL) 11717105f0d9SBjoern A. Zeeb return (NULL); 11727105f0d9SBjoern A. Zeeb 11737105f0d9SBjoern A. Zeeb dr->size = size; 11747105f0d9SBjoern A. Zeeb dr->mem = linux_dma_alloc_coherent(dev, size, dma_handle, flag); 11757105f0d9SBjoern A. Zeeb dr->handle = dma_handle; 11767105f0d9SBjoern A. Zeeb if (dr->mem == NULL) { 11777105f0d9SBjoern A. Zeeb lkpi_devres_free(dr); 11787105f0d9SBjoern A. Zeeb return (NULL); 11797105f0d9SBjoern A. Zeeb } 11807105f0d9SBjoern A. Zeeb 11817105f0d9SBjoern A. Zeeb lkpi_devres_add(dev, dr); 11827105f0d9SBjoern A. Zeeb return (dr->mem); 11837105f0d9SBjoern A. Zeeb } 11847105f0d9SBjoern A. Zeeb 118595edb10bSBjoern A. Zeeb void 118695edb10bSBjoern A. Zeeb linuxkpi_dma_sync(struct device *dev, dma_addr_t dma_addr, size_t size, 118795edb10bSBjoern A. Zeeb bus_dmasync_op_t op) 118895edb10bSBjoern A. Zeeb { 118995edb10bSBjoern A. Zeeb struct linux_dma_priv *priv; 119095edb10bSBjoern A. Zeeb struct linux_dma_obj *obj; 119195edb10bSBjoern A. Zeeb 119295edb10bSBjoern A. Zeeb priv = dev->dma_priv; 119395edb10bSBjoern A. Zeeb 119495edb10bSBjoern A. Zeeb if (pctrie_is_empty(&priv->ptree)) 119595edb10bSBjoern A. Zeeb return; 119695edb10bSBjoern A. Zeeb 119795edb10bSBjoern A. Zeeb DMA_PRIV_LOCK(priv); 119895edb10bSBjoern A. Zeeb obj = LINUX_DMA_PCTRIE_LOOKUP(&priv->ptree, dma_addr); 119995edb10bSBjoern A. Zeeb if (obj == NULL) { 120095edb10bSBjoern A. Zeeb DMA_PRIV_UNLOCK(priv); 120195edb10bSBjoern A. Zeeb return; 120295edb10bSBjoern A. Zeeb } 120395edb10bSBjoern A. Zeeb 120495edb10bSBjoern A. Zeeb bus_dmamap_sync(obj->dmat, obj->dmamap, op); 120595edb10bSBjoern A. Zeeb DMA_PRIV_UNLOCK(priv); 120695edb10bSBjoern A. Zeeb } 120795edb10bSBjoern A. Zeeb 1208f211d536STycho Nightingale int 1209f211d536STycho Nightingale linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents, 121095edb10bSBjoern A. Zeeb enum dma_data_direction direction, unsigned long attrs __unused) 1211f211d536STycho Nightingale { 1212f211d536STycho Nightingale struct linux_dma_priv *priv; 1213442d12d8SHans Petter Selasky struct scatterlist *sg; 1214442d12d8SHans Petter Selasky int i, nseg; 1215f211d536STycho Nightingale bus_dma_segment_t seg; 1216f211d536STycho Nightingale 1217f211d536STycho Nightingale priv = dev->dma_priv; 1218f211d536STycho Nightingale 1219a6619e8dSHans Petter Selasky DMA_PRIV_LOCK(priv); 1220442d12d8SHans Petter Selasky 1221442d12d8SHans Petter Selasky /* create common DMA map in the first S/G entry */ 1222442d12d8SHans Petter Selasky if (bus_dmamap_create(priv->dmat, 0, &sgl->dma_map) != 0) { 1223a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1224f211d536STycho Nightingale return (0); 1225f211d536STycho Nightingale } 1226f211d536STycho Nightingale 1227442d12d8SHans Petter Selasky /* load all S/G list entries */ 1228442d12d8SHans Petter Selasky for_each_sg(sgl, sg, nents, i) { 1229f211d536STycho Nightingale nseg = -1; 1230442d12d8SHans Petter Selasky if (_bus_dmamap_load_phys(priv->dmat, sgl->dma_map, 1231442d12d8SHans Petter Selasky sg_phys(sg), sg->length, BUS_DMA_NOWAIT, 1232f211d536STycho Nightingale &seg, &nseg) != 0) { 1233442d12d8SHans Petter Selasky bus_dmamap_unload(priv->dmat, sgl->dma_map); 1234442d12d8SHans Petter Selasky bus_dmamap_destroy(priv->dmat, sgl->dma_map); 1235a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1236f211d536STycho Nightingale return (0); 1237f211d536STycho Nightingale } 1238442d12d8SHans Petter Selasky KASSERT(nseg == 0, 1239442d12d8SHans Petter Selasky ("More than one segment (nseg=%d)", nseg + 1)); 1240f211d536STycho Nightingale 1241442d12d8SHans Petter Selasky sg_dma_address(sg) = seg.ds_addr; 1242f211d536STycho Nightingale } 124395edb10bSBjoern A. Zeeb 124495edb10bSBjoern A. Zeeb switch (direction) { 124595edb10bSBjoern A. Zeeb case DMA_BIDIRECTIONAL: 124695edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREWRITE); 124795edb10bSBjoern A. Zeeb break; 124895edb10bSBjoern A. Zeeb case DMA_TO_DEVICE: 124995edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREREAD); 125095edb10bSBjoern A. Zeeb break; 125195edb10bSBjoern A. Zeeb case DMA_FROM_DEVICE: 125295edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREWRITE); 125395edb10bSBjoern A. Zeeb break; 125495edb10bSBjoern A. Zeeb default: 125595edb10bSBjoern A. Zeeb break; 125695edb10bSBjoern A. Zeeb } 125795edb10bSBjoern A. Zeeb 1258a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1259442d12d8SHans Petter Selasky 1260442d12d8SHans Petter Selasky return (nents); 1261f211d536STycho Nightingale } 1262f211d536STycho Nightingale 1263f211d536STycho Nightingale void 1264f211d536STycho Nightingale linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sgl, 126595edb10bSBjoern A. Zeeb int nents __unused, enum dma_data_direction direction, 126698a6984aSVladimir Kondratyev unsigned long attrs __unused) 1267f211d536STycho Nightingale { 1268f211d536STycho Nightingale struct linux_dma_priv *priv; 1269f211d536STycho Nightingale 1270f211d536STycho Nightingale priv = dev->dma_priv; 1271f211d536STycho Nightingale 1272a6619e8dSHans Petter Selasky DMA_PRIV_LOCK(priv); 127395edb10bSBjoern A. Zeeb 127495edb10bSBjoern A. Zeeb switch (direction) { 127595edb10bSBjoern A. Zeeb case DMA_BIDIRECTIONAL: 127695edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_POSTREAD); 127795edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREREAD); 127895edb10bSBjoern A. Zeeb break; 127995edb10bSBjoern A. Zeeb case DMA_TO_DEVICE: 128095edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_POSTWRITE); 128195edb10bSBjoern A. Zeeb break; 128295edb10bSBjoern A. Zeeb case DMA_FROM_DEVICE: 128395edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_POSTREAD); 128495edb10bSBjoern A. Zeeb break; 128595edb10bSBjoern A. Zeeb default: 128695edb10bSBjoern A. Zeeb break; 128795edb10bSBjoern A. Zeeb } 128895edb10bSBjoern A. Zeeb 1289442d12d8SHans Petter Selasky bus_dmamap_unload(priv->dmat, sgl->dma_map); 1290442d12d8SHans Petter Selasky bus_dmamap_destroy(priv->dmat, sgl->dma_map); 1291a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1292f211d536STycho Nightingale } 1293f211d536STycho Nightingale 129493a203eaSHans Petter Selasky struct dma_pool { 129593a203eaSHans Petter Selasky struct device *pool_device; 129693a203eaSHans Petter Selasky uma_zone_t pool_zone; 1297a6619e8dSHans Petter Selasky struct mtx pool_lock; 129893a203eaSHans Petter Selasky bus_dma_tag_t pool_dmat; 129993a203eaSHans Petter Selasky size_t pool_entry_size; 130093a203eaSHans Petter Selasky struct pctrie pool_ptree; 130193a203eaSHans Petter Selasky }; 130293a203eaSHans Petter Selasky 1303a6619e8dSHans Petter Selasky #define DMA_POOL_LOCK(pool) mtx_lock(&(pool)->pool_lock) 1304a6619e8dSHans Petter Selasky #define DMA_POOL_UNLOCK(pool) mtx_unlock(&(pool)->pool_lock) 1305a6619e8dSHans Petter Selasky 1306f211d536STycho Nightingale static inline int 1307f211d536STycho Nightingale dma_pool_obj_ctor(void *mem, int size, void *arg, int flags) 1308f211d536STycho Nightingale { 1309f211d536STycho Nightingale struct linux_dma_obj *obj = mem; 1310f211d536STycho Nightingale struct dma_pool *pool = arg; 1311f211d536STycho Nightingale int error, nseg; 1312f211d536STycho Nightingale bus_dma_segment_t seg; 1313f211d536STycho Nightingale 1314f211d536STycho Nightingale nseg = -1; 1315a6619e8dSHans Petter Selasky DMA_POOL_LOCK(pool); 1316f211d536STycho Nightingale error = _bus_dmamap_load_phys(pool->pool_dmat, obj->dmamap, 1317f211d536STycho Nightingale vtophys(obj->vaddr), pool->pool_entry_size, BUS_DMA_NOWAIT, 1318f211d536STycho Nightingale &seg, &nseg); 1319a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1320f211d536STycho Nightingale if (error != 0) { 1321f211d536STycho Nightingale return (error); 1322f211d536STycho Nightingale } 1323f211d536STycho Nightingale KASSERT(++nseg == 1, ("More than one segment (nseg=%d)", nseg)); 1324f211d536STycho Nightingale obj->dma_addr = seg.ds_addr; 1325f211d536STycho Nightingale 1326f211d536STycho Nightingale return (0); 1327f211d536STycho Nightingale } 1328f211d536STycho Nightingale 1329f211d536STycho Nightingale static void 1330f211d536STycho Nightingale dma_pool_obj_dtor(void *mem, int size, void *arg) 1331f211d536STycho Nightingale { 1332f211d536STycho Nightingale struct linux_dma_obj *obj = mem; 1333f211d536STycho Nightingale struct dma_pool *pool = arg; 1334f211d536STycho Nightingale 1335a6619e8dSHans Petter Selasky DMA_POOL_LOCK(pool); 1336f211d536STycho Nightingale bus_dmamap_unload(pool->pool_dmat, obj->dmamap); 1337a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1338f211d536STycho Nightingale } 1339f211d536STycho Nightingale 1340f211d536STycho Nightingale static int 1341f211d536STycho Nightingale dma_pool_obj_import(void *arg, void **store, int count, int domain __unused, 1342f211d536STycho Nightingale int flags) 1343f211d536STycho Nightingale { 1344f211d536STycho Nightingale struct dma_pool *pool = arg; 1345f211d536STycho Nightingale struct linux_dma_obj *obj; 1346f211d536STycho Nightingale int error, i; 1347f211d536STycho Nightingale 1348f211d536STycho Nightingale for (i = 0; i < count; i++) { 1349f211d536STycho Nightingale obj = uma_zalloc(linux_dma_obj_zone, flags); 1350f211d536STycho Nightingale if (obj == NULL) 1351f211d536STycho Nightingale break; 1352f211d536STycho Nightingale 1353f211d536STycho Nightingale error = bus_dmamem_alloc(pool->pool_dmat, &obj->vaddr, 1354f211d536STycho Nightingale BUS_DMA_NOWAIT, &obj->dmamap); 1355f211d536STycho Nightingale if (error!= 0) { 1356f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1357f211d536STycho Nightingale break; 1358f211d536STycho Nightingale } 1359f211d536STycho Nightingale 1360f211d536STycho Nightingale store[i] = obj; 1361f211d536STycho Nightingale } 1362f211d536STycho Nightingale 1363f211d536STycho Nightingale return (i); 1364f211d536STycho Nightingale } 1365f211d536STycho Nightingale 1366f211d536STycho Nightingale static void 1367f211d536STycho Nightingale dma_pool_obj_release(void *arg, void **store, int count) 1368f211d536STycho Nightingale { 1369f211d536STycho Nightingale struct dma_pool *pool = arg; 1370f211d536STycho Nightingale struct linux_dma_obj *obj; 1371f211d536STycho Nightingale int i; 1372f211d536STycho Nightingale 1373f211d536STycho Nightingale for (i = 0; i < count; i++) { 1374f211d536STycho Nightingale obj = store[i]; 1375f211d536STycho Nightingale bus_dmamem_free(pool->pool_dmat, obj->vaddr, obj->dmamap); 1376f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1377f211d536STycho Nightingale } 1378f211d536STycho Nightingale } 1379f211d536STycho Nightingale 1380f211d536STycho Nightingale struct dma_pool * 1381f211d536STycho Nightingale linux_dma_pool_create(char *name, struct device *dev, size_t size, 1382f211d536STycho Nightingale size_t align, size_t boundary) 1383f211d536STycho Nightingale { 1384f211d536STycho Nightingale struct linux_dma_priv *priv; 1385f211d536STycho Nightingale struct dma_pool *pool; 1386f211d536STycho Nightingale 1387f211d536STycho Nightingale priv = dev->dma_priv; 1388f211d536STycho Nightingale 1389f211d536STycho Nightingale pool = kzalloc(sizeof(*pool), GFP_KERNEL); 13905a637529SHans Petter Selasky pool->pool_device = dev; 1391f211d536STycho Nightingale pool->pool_entry_size = size; 1392f211d536STycho Nightingale 1393f211d536STycho Nightingale if (bus_dma_tag_create(bus_get_dma_tag(dev->bsddev), 1394f211d536STycho Nightingale align, boundary, /* alignment, boundary */ 1395f211d536STycho Nightingale priv->dma_mask, /* lowaddr */ 1396f211d536STycho Nightingale BUS_SPACE_MAXADDR, /* highaddr */ 1397f211d536STycho Nightingale NULL, NULL, /* filtfunc, filtfuncarg */ 1398f211d536STycho Nightingale size, /* maxsize */ 1399f211d536STycho Nightingale 1, /* nsegments */ 1400f211d536STycho Nightingale size, /* maxsegsz */ 1401f211d536STycho Nightingale 0, /* flags */ 1402f211d536STycho Nightingale NULL, NULL, /* lockfunc, lockfuncarg */ 1403f211d536STycho Nightingale &pool->pool_dmat)) { 1404f211d536STycho Nightingale kfree(pool); 1405f211d536STycho Nightingale return (NULL); 1406f211d536STycho Nightingale } 1407f211d536STycho Nightingale 1408f211d536STycho Nightingale pool->pool_zone = uma_zcache_create(name, -1, dma_pool_obj_ctor, 1409f211d536STycho Nightingale dma_pool_obj_dtor, NULL, NULL, dma_pool_obj_import, 1410f211d536STycho Nightingale dma_pool_obj_release, pool, 0); 1411f211d536STycho Nightingale 1412a6619e8dSHans Petter Selasky mtx_init(&pool->pool_lock, "lkpi-dma-pool", NULL, MTX_DEF); 1413f211d536STycho Nightingale pctrie_init(&pool->pool_ptree); 1414f211d536STycho Nightingale 1415f211d536STycho Nightingale return (pool); 1416f211d536STycho Nightingale } 1417f211d536STycho Nightingale 1418f211d536STycho Nightingale void 1419f211d536STycho Nightingale linux_dma_pool_destroy(struct dma_pool *pool) 1420f211d536STycho Nightingale { 1421f211d536STycho Nightingale 1422f211d536STycho Nightingale uma_zdestroy(pool->pool_zone); 1423f211d536STycho Nightingale bus_dma_tag_destroy(pool->pool_dmat); 1424a6619e8dSHans Petter Selasky mtx_destroy(&pool->pool_lock); 1425f211d536STycho Nightingale kfree(pool); 1426f211d536STycho Nightingale } 1427f211d536STycho Nightingale 14282afeed13SBjoern A. Zeeb void 14292afeed13SBjoern A. Zeeb lkpi_dmam_pool_destroy(struct device *dev, void *p) 14302afeed13SBjoern A. Zeeb { 14312afeed13SBjoern A. Zeeb struct dma_pool *pool; 14322afeed13SBjoern A. Zeeb 14332afeed13SBjoern A. Zeeb pool = *(struct dma_pool **)p; 14342afeed13SBjoern A. Zeeb LINUX_DMA_PCTRIE_RECLAIM(&pool->pool_ptree); 14352afeed13SBjoern A. Zeeb linux_dma_pool_destroy(pool); 14362afeed13SBjoern A. Zeeb } 14372afeed13SBjoern A. Zeeb 1438f211d536STycho Nightingale void * 1439f211d536STycho Nightingale linux_dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags, 1440f211d536STycho Nightingale dma_addr_t *handle) 1441f211d536STycho Nightingale { 1442f211d536STycho Nightingale struct linux_dma_obj *obj; 1443f211d536STycho Nightingale 1444f6e7d67aSBryan Drewery obj = uma_zalloc_arg(pool->pool_zone, pool, mem_flags & GFP_NATIVE_MASK); 1445f211d536STycho Nightingale if (obj == NULL) 1446f211d536STycho Nightingale return (NULL); 1447f211d536STycho Nightingale 1448a6619e8dSHans Petter Selasky DMA_POOL_LOCK(pool); 1449f211d536STycho Nightingale if (LINUX_DMA_PCTRIE_INSERT(&pool->pool_ptree, obj) != 0) { 1450a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1451f211d536STycho Nightingale uma_zfree_arg(pool->pool_zone, obj, pool); 1452f211d536STycho Nightingale return (NULL); 1453f211d536STycho Nightingale } 1454a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1455f211d536STycho Nightingale 1456f211d536STycho Nightingale *handle = obj->dma_addr; 1457f211d536STycho Nightingale return (obj->vaddr); 1458f211d536STycho Nightingale } 1459f211d536STycho Nightingale 1460f211d536STycho Nightingale void 1461f211d536STycho Nightingale linux_dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t dma_addr) 1462f211d536STycho Nightingale { 1463f211d536STycho Nightingale struct linux_dma_obj *obj; 1464f211d536STycho Nightingale 1465a6619e8dSHans Petter Selasky DMA_POOL_LOCK(pool); 1466f211d536STycho Nightingale obj = LINUX_DMA_PCTRIE_LOOKUP(&pool->pool_ptree, dma_addr); 1467f211d536STycho Nightingale if (obj == NULL) { 1468a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1469f211d536STycho Nightingale return; 1470f211d536STycho Nightingale } 1471f211d536STycho Nightingale LINUX_DMA_PCTRIE_REMOVE(&pool->pool_ptree, dma_addr); 1472a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1473f211d536STycho Nightingale 1474f211d536STycho Nightingale uma_zfree_arg(pool->pool_zone, obj, pool); 1475f211d536STycho Nightingale } 14762b68c973SEmmanuel Vadot 14772b68c973SEmmanuel Vadot static int 14782b68c973SEmmanuel Vadot linux_backlight_get_status(device_t dev, struct backlight_props *props) 14792b68c973SEmmanuel Vadot { 14802b68c973SEmmanuel Vadot struct pci_dev *pdev; 14812b68c973SEmmanuel Vadot 14822b68c973SEmmanuel Vadot linux_set_current(curthread); 14832b68c973SEmmanuel Vadot pdev = device_get_softc(dev); 14842b68c973SEmmanuel Vadot 14852b68c973SEmmanuel Vadot props->brightness = pdev->dev.bd->props.brightness; 14862b68c973SEmmanuel Vadot props->brightness = props->brightness * 100 / pdev->dev.bd->props.max_brightness; 14872b68c973SEmmanuel Vadot props->nlevels = 0; 14882b68c973SEmmanuel Vadot 14892b68c973SEmmanuel Vadot return (0); 14902b68c973SEmmanuel Vadot } 14912b68c973SEmmanuel Vadot 14922b68c973SEmmanuel Vadot static int 14932b68c973SEmmanuel Vadot linux_backlight_get_info(device_t dev, struct backlight_info *info) 14942b68c973SEmmanuel Vadot { 14952b68c973SEmmanuel Vadot struct pci_dev *pdev; 14962b68c973SEmmanuel Vadot 14972b68c973SEmmanuel Vadot linux_set_current(curthread); 14982b68c973SEmmanuel Vadot pdev = device_get_softc(dev); 14992b68c973SEmmanuel Vadot 15002b68c973SEmmanuel Vadot info->type = BACKLIGHT_TYPE_PANEL; 15012b68c973SEmmanuel Vadot strlcpy(info->name, pdev->dev.bd->name, BACKLIGHTMAXNAMELENGTH); 15022b68c973SEmmanuel Vadot return (0); 15032b68c973SEmmanuel Vadot } 15042b68c973SEmmanuel Vadot 15052b68c973SEmmanuel Vadot static int 15062b68c973SEmmanuel Vadot linux_backlight_update_status(device_t dev, struct backlight_props *props) 15072b68c973SEmmanuel Vadot { 15082b68c973SEmmanuel Vadot struct pci_dev *pdev; 15092b68c973SEmmanuel Vadot 15102b68c973SEmmanuel Vadot linux_set_current(curthread); 15112b68c973SEmmanuel Vadot pdev = device_get_softc(dev); 15122b68c973SEmmanuel Vadot 15132b68c973SEmmanuel Vadot pdev->dev.bd->props.brightness = pdev->dev.bd->props.max_brightness * 15142b68c973SEmmanuel Vadot props->brightness / 100; 1515b52e3638SVladimir Kondratyev pdev->dev.bd->props.power = props->brightness == 0 ? 1516b52e3638SVladimir Kondratyev 4/* FB_BLANK_POWERDOWN */ : 0/* FB_BLANK_UNBLANK */; 15172b68c973SEmmanuel Vadot return (pdev->dev.bd->ops->update_status(pdev->dev.bd)); 15182b68c973SEmmanuel Vadot } 15192b68c973SEmmanuel Vadot 15202b68c973SEmmanuel Vadot struct backlight_device * 15212b68c973SEmmanuel Vadot linux_backlight_device_register(const char *name, struct device *dev, 15222b68c973SEmmanuel Vadot void *data, const struct backlight_ops *ops, struct backlight_properties *props) 15232b68c973SEmmanuel Vadot { 15242b68c973SEmmanuel Vadot 15252b68c973SEmmanuel Vadot dev->bd = malloc(sizeof(*dev->bd), M_DEVBUF, M_WAITOK | M_ZERO); 15262b68c973SEmmanuel Vadot dev->bd->ops = ops; 15272b68c973SEmmanuel Vadot dev->bd->props.type = props->type; 15282b68c973SEmmanuel Vadot dev->bd->props.max_brightness = props->max_brightness; 15292b68c973SEmmanuel Vadot dev->bd->props.brightness = props->brightness; 15302b68c973SEmmanuel Vadot dev->bd->props.power = props->power; 15312b68c973SEmmanuel Vadot dev->bd->data = data; 15322b68c973SEmmanuel Vadot dev->bd->dev = dev; 15332b68c973SEmmanuel Vadot dev->bd->name = strdup(name, M_DEVBUF); 15342b68c973SEmmanuel Vadot 15352b68c973SEmmanuel Vadot dev->backlight_dev = backlight_register(name, dev->bsddev); 15362b68c973SEmmanuel Vadot 15372b68c973SEmmanuel Vadot return (dev->bd); 15382b68c973SEmmanuel Vadot } 15392b68c973SEmmanuel Vadot 15402b68c973SEmmanuel Vadot void 15412b68c973SEmmanuel Vadot linux_backlight_device_unregister(struct backlight_device *bd) 15422b68c973SEmmanuel Vadot { 15432b68c973SEmmanuel Vadot 15442b68c973SEmmanuel Vadot backlight_destroy(bd->dev->backlight_dev); 15452b68c973SEmmanuel Vadot free(bd->name, M_DEVBUF); 15462b68c973SEmmanuel Vadot free(bd, M_DEVBUF); 15472b68c973SEmmanuel Vadot } 1548