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 120f211d536STycho Nightingale struct linux_dma_priv { 121f211d536STycho Nightingale uint64_t dma_mask; 122f211d536STycho Nightingale bus_dma_tag_t dmat; 123c39eefe7SBjoern A. Zeeb uint64_t dma_coherent_mask; 124c39eefe7SBjoern A. Zeeb bus_dma_tag_t dmat_coherent; 125c39eefe7SBjoern A. Zeeb struct mtx lock; 126f211d536STycho Nightingale struct pctrie ptree; 127f211d536STycho Nightingale }; 128a6619e8dSHans Petter Selasky #define DMA_PRIV_LOCK(priv) mtx_lock(&(priv)->lock) 129a6619e8dSHans Petter Selasky #define DMA_PRIV_UNLOCK(priv) mtx_unlock(&(priv)->lock) 130f211d536STycho Nightingale 131f211d536STycho Nightingale static int 132f211d536STycho Nightingale linux_pdev_dma_uninit(struct pci_dev *pdev) 133f211d536STycho Nightingale { 134f211d536STycho Nightingale struct linux_dma_priv *priv; 135f211d536STycho Nightingale 136f211d536STycho Nightingale priv = pdev->dev.dma_priv; 137f211d536STycho Nightingale if (priv->dmat) 138f211d536STycho Nightingale bus_dma_tag_destroy(priv->dmat); 139c39eefe7SBjoern A. Zeeb if (priv->dmat_coherent) 140c39eefe7SBjoern A. Zeeb bus_dma_tag_destroy(priv->dmat_coherent); 141a6619e8dSHans Petter Selasky mtx_destroy(&priv->lock); 142f211d536STycho Nightingale pdev->dev.dma_priv = NULL; 143c39eefe7SBjoern A. Zeeb free(priv, M_DEVBUF); 144f211d536STycho Nightingale return (0); 145f211d536STycho Nightingale } 146f211d536STycho Nightingale 147c39eefe7SBjoern A. Zeeb static int 148c39eefe7SBjoern A. Zeeb linux_pdev_dma_init(struct pci_dev *pdev) 149c39eefe7SBjoern A. Zeeb { 150c39eefe7SBjoern A. Zeeb struct linux_dma_priv *priv; 151c39eefe7SBjoern A. Zeeb int error; 152c39eefe7SBjoern A. Zeeb 153c39eefe7SBjoern A. Zeeb priv = malloc(sizeof(*priv), M_DEVBUF, M_WAITOK | M_ZERO); 154c39eefe7SBjoern A. Zeeb 155c39eefe7SBjoern A. Zeeb mtx_init(&priv->lock, "lkpi-priv-dma", NULL, MTX_DEF); 156c39eefe7SBjoern A. Zeeb pctrie_init(&priv->ptree); 157c39eefe7SBjoern A. Zeeb 158c39eefe7SBjoern A. Zeeb pdev->dev.dma_priv = priv; 159c39eefe7SBjoern A. Zeeb 160c39eefe7SBjoern A. Zeeb /* Create a default DMA tags. */ 161c39eefe7SBjoern A. Zeeb error = linux_dma_tag_init(&pdev->dev, DMA_BIT_MASK(64)); 162c39eefe7SBjoern A. Zeeb if (error != 0) 163c39eefe7SBjoern A. Zeeb goto err; 164c39eefe7SBjoern A. Zeeb /* Coherent is lower 32bit only by default in Linux. */ 165c39eefe7SBjoern A. Zeeb error = linux_dma_tag_init_coherent(&pdev->dev, DMA_BIT_MASK(32)); 166c39eefe7SBjoern A. Zeeb if (error != 0) 167c39eefe7SBjoern A. Zeeb goto err; 168c39eefe7SBjoern A. Zeeb 169c39eefe7SBjoern A. Zeeb return (error); 170c39eefe7SBjoern A. Zeeb 171c39eefe7SBjoern A. Zeeb err: 172c39eefe7SBjoern A. Zeeb linux_pdev_dma_uninit(pdev); 173c39eefe7SBjoern A. Zeeb return (error); 174c39eefe7SBjoern A. Zeeb } 175c39eefe7SBjoern A. Zeeb 176f211d536STycho Nightingale int 177f211d536STycho Nightingale linux_dma_tag_init(struct device *dev, u64 dma_mask) 178f211d536STycho Nightingale { 179f211d536STycho Nightingale struct linux_dma_priv *priv; 180f211d536STycho Nightingale int error; 181f211d536STycho Nightingale 182f211d536STycho Nightingale priv = dev->dma_priv; 183f211d536STycho Nightingale 184f211d536STycho Nightingale if (priv->dmat) { 185f211d536STycho Nightingale if (priv->dma_mask == dma_mask) 186f211d536STycho Nightingale return (0); 187f211d536STycho Nightingale 188f211d536STycho Nightingale bus_dma_tag_destroy(priv->dmat); 189f211d536STycho Nightingale } 190f211d536STycho Nightingale 191f211d536STycho Nightingale priv->dma_mask = dma_mask; 192f211d536STycho Nightingale 193f211d536STycho Nightingale error = bus_dma_tag_create(bus_get_dma_tag(dev->bsddev), 194f211d536STycho Nightingale 1, 0, /* alignment, boundary */ 195f211d536STycho Nightingale dma_mask, /* lowaddr */ 196f211d536STycho Nightingale BUS_SPACE_MAXADDR, /* highaddr */ 197f211d536STycho Nightingale NULL, NULL, /* filtfunc, filtfuncarg */ 198b09626b3STycho Nightingale BUS_SPACE_MAXSIZE, /* maxsize */ 199f211d536STycho Nightingale 1, /* nsegments */ 200b09626b3STycho Nightingale BUS_SPACE_MAXSIZE, /* maxsegsz */ 201f211d536STycho Nightingale 0, /* flags */ 202f211d536STycho Nightingale NULL, NULL, /* lockfunc, lockfuncarg */ 203f211d536STycho Nightingale &priv->dmat); 204f211d536STycho Nightingale return (-error); 205f211d536STycho Nightingale } 206f211d536STycho Nightingale 207c39eefe7SBjoern A. Zeeb int 208c39eefe7SBjoern A. Zeeb linux_dma_tag_init_coherent(struct device *dev, u64 dma_mask) 209c39eefe7SBjoern A. Zeeb { 210c39eefe7SBjoern A. Zeeb struct linux_dma_priv *priv; 211c39eefe7SBjoern A. Zeeb int error; 212c39eefe7SBjoern A. Zeeb 213c39eefe7SBjoern A. Zeeb priv = dev->dma_priv; 214c39eefe7SBjoern A. Zeeb 215c39eefe7SBjoern A. Zeeb if (priv->dmat_coherent) { 216c39eefe7SBjoern A. Zeeb if (priv->dma_coherent_mask == dma_mask) 217c39eefe7SBjoern A. Zeeb return (0); 218c39eefe7SBjoern A. Zeeb 219c39eefe7SBjoern A. Zeeb bus_dma_tag_destroy(priv->dmat_coherent); 220c39eefe7SBjoern A. Zeeb } 221c39eefe7SBjoern A. Zeeb 222c39eefe7SBjoern A. Zeeb priv->dma_coherent_mask = dma_mask; 223c39eefe7SBjoern A. Zeeb 224c39eefe7SBjoern A. Zeeb error = bus_dma_tag_create(bus_get_dma_tag(dev->bsddev), 225c39eefe7SBjoern A. Zeeb 1, 0, /* alignment, boundary */ 226c39eefe7SBjoern A. Zeeb dma_mask, /* lowaddr */ 227c39eefe7SBjoern A. Zeeb BUS_SPACE_MAXADDR, /* highaddr */ 228c39eefe7SBjoern A. Zeeb NULL, NULL, /* filtfunc, filtfuncarg */ 229c39eefe7SBjoern A. Zeeb BUS_SPACE_MAXSIZE, /* maxsize */ 230c39eefe7SBjoern A. Zeeb 1, /* nsegments */ 231c39eefe7SBjoern A. Zeeb BUS_SPACE_MAXSIZE, /* maxsegsz */ 232c39eefe7SBjoern A. Zeeb 0, /* flags */ 233c39eefe7SBjoern A. Zeeb NULL, NULL, /* lockfunc, lockfuncarg */ 234c39eefe7SBjoern A. Zeeb &priv->dmat_coherent); 235c39eefe7SBjoern A. Zeeb return (-error); 236c39eefe7SBjoern A. Zeeb } 237c39eefe7SBjoern A. Zeeb 2388d59ecb2SHans Petter Selasky static struct pci_driver * 2398d59ecb2SHans Petter Selasky linux_pci_find(device_t dev, const struct pci_device_id **idp) 2408d59ecb2SHans Petter Selasky { 2418d59ecb2SHans Petter Selasky const struct pci_device_id *id; 2428d59ecb2SHans Petter Selasky struct pci_driver *pdrv; 2438d59ecb2SHans Petter Selasky uint16_t vendor; 2448d59ecb2SHans Petter Selasky uint16_t device; 245232028b3SHans Petter Selasky uint16_t subvendor; 246232028b3SHans Petter Selasky uint16_t subdevice; 2478d59ecb2SHans Petter Selasky 2488d59ecb2SHans Petter Selasky vendor = pci_get_vendor(dev); 2498d59ecb2SHans Petter Selasky device = pci_get_device(dev); 250232028b3SHans Petter Selasky subvendor = pci_get_subvendor(dev); 251232028b3SHans Petter Selasky subdevice = pci_get_subdevice(dev); 2528d59ecb2SHans Petter Selasky 2538d59ecb2SHans Petter Selasky spin_lock(&pci_lock); 254cf899348SBjoern A. Zeeb list_for_each_entry(pdrv, &pci_drivers, node) { 2558d59ecb2SHans Petter Selasky for (id = pdrv->id_table; id->vendor != 0; id++) { 256232028b3SHans Petter Selasky if (vendor == id->vendor && 257232028b3SHans Petter Selasky (PCI_ANY_ID == id->device || device == id->device) && 258232028b3SHans Petter Selasky (PCI_ANY_ID == id->subvendor || subvendor == id->subvendor) && 259232028b3SHans Petter Selasky (PCI_ANY_ID == id->subdevice || subdevice == id->subdevice)) { 2608d59ecb2SHans Petter Selasky *idp = id; 2618d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 2628d59ecb2SHans Petter Selasky return (pdrv); 2638d59ecb2SHans Petter Selasky } 2648d59ecb2SHans Petter Selasky } 2658d59ecb2SHans Petter Selasky } 2668d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 2678d59ecb2SHans Petter Selasky return (NULL); 2688d59ecb2SHans Petter Selasky } 2698d59ecb2SHans Petter Selasky 270105a37caSEmmanuel Vadot static void 2715f88df77SBjoern A. Zeeb lkpi_pci_dev_release(struct device *dev) 2725f88df77SBjoern A. Zeeb { 2735f88df77SBjoern A. Zeeb 2745f88df77SBjoern A. Zeeb lkpi_devres_release_free_list(dev); 2755f88df77SBjoern A. Zeeb spin_lock_destroy(&dev->devres_lock); 2765f88df77SBjoern A. Zeeb } 2775f88df77SBjoern A. Zeeb 2785f88df77SBjoern A. Zeeb static void 279105a37caSEmmanuel Vadot lkpifill_pci_dev(device_t dev, struct pci_dev *pdev) 280105a37caSEmmanuel Vadot { 281105a37caSEmmanuel Vadot 282105a37caSEmmanuel Vadot pdev->devfn = PCI_DEVFN(pci_get_slot(dev), pci_get_function(dev)); 283105a37caSEmmanuel Vadot pdev->vendor = pci_get_vendor(dev); 284105a37caSEmmanuel Vadot pdev->device = pci_get_device(dev); 2851fac2cb4SBjoern A. Zeeb pdev->subsystem_vendor = pci_get_subvendor(dev); 2861fac2cb4SBjoern A. Zeeb pdev->subsystem_device = pci_get_subdevice(dev); 287105a37caSEmmanuel Vadot pdev->class = pci_get_class(dev); 288105a37caSEmmanuel Vadot pdev->revision = pci_get_revid(dev); 2891fac2cb4SBjoern A. Zeeb pdev->bus = malloc(sizeof(*pdev->bus), M_DEVBUF, M_WAITOK | M_ZERO); 290b3b83625SBjoern A. Zeeb /* 291b3b83625SBjoern A. Zeeb * This should be the upstream bridge; pci_upstream_bridge() 292b3b83625SBjoern A. Zeeb * handles that case on demand as otherwise we'll shadow the 293b3b83625SBjoern A. Zeeb * entire PCI hierarchy. 294b3b83625SBjoern A. Zeeb */ 295105a37caSEmmanuel Vadot pdev->bus->self = pdev; 296105a37caSEmmanuel Vadot pdev->bus->number = pci_get_bus(dev); 297105a37caSEmmanuel Vadot pdev->bus->domain = pci_get_domain(dev); 2981fac2cb4SBjoern A. Zeeb pdev->dev.bsddev = dev; 2991fac2cb4SBjoern A. Zeeb pdev->dev.parent = &linux_root_device; 300d4a4960cSBjoern A. Zeeb pdev->dev.release = lkpi_pci_dev_release; 3011fac2cb4SBjoern A. Zeeb INIT_LIST_HEAD(&pdev->dev.irqents); 3021fac2cb4SBjoern A. Zeeb kobject_init(&pdev->dev.kobj, &linux_dev_ktype); 3031fac2cb4SBjoern A. Zeeb kobject_set_name(&pdev->dev.kobj, device_get_nameunit(dev)); 3041fac2cb4SBjoern A. Zeeb kobject_add(&pdev->dev.kobj, &linux_root_device.kobj, 3051fac2cb4SBjoern A. Zeeb kobject_name(&pdev->dev.kobj)); 306c3518147SBjoern A. Zeeb spin_lock_init(&pdev->dev.devres_lock); 307c3518147SBjoern A. Zeeb INIT_LIST_HEAD(&pdev->dev.devres_head); 3081fac2cb4SBjoern A. Zeeb } 3091fac2cb4SBjoern A. Zeeb 3101fac2cb4SBjoern A. Zeeb static void 3111fac2cb4SBjoern A. Zeeb lkpinew_pci_dev_release(struct device *dev) 3121fac2cb4SBjoern A. Zeeb { 3131fac2cb4SBjoern A. Zeeb struct pci_dev *pdev; 3141fac2cb4SBjoern A. Zeeb 3151fac2cb4SBjoern A. Zeeb pdev = to_pci_dev(dev); 3168e106c52SBjoern A. Zeeb if (pdev->root != NULL) 3178e106c52SBjoern A. Zeeb pci_dev_put(pdev->root); 318b3b83625SBjoern A. Zeeb if (pdev->bus->self != pdev) 319b3b83625SBjoern A. Zeeb pci_dev_put(pdev->bus->self); 3201fac2cb4SBjoern A. Zeeb free(pdev->bus, M_DEVBUF); 3211fac2cb4SBjoern A. Zeeb free(pdev, M_DEVBUF); 322105a37caSEmmanuel Vadot } 323105a37caSEmmanuel Vadot 3248e106c52SBjoern A. Zeeb struct pci_dev * 325105a37caSEmmanuel Vadot lkpinew_pci_dev(device_t dev) 326105a37caSEmmanuel Vadot { 327105a37caSEmmanuel Vadot struct pci_dev *pdev; 328105a37caSEmmanuel Vadot 329105a37caSEmmanuel Vadot pdev = malloc(sizeof(*pdev), M_DEVBUF, M_WAITOK|M_ZERO); 330105a37caSEmmanuel Vadot lkpifill_pci_dev(dev, pdev); 3311fac2cb4SBjoern A. Zeeb pdev->dev.release = lkpinew_pci_dev_release; 3321fac2cb4SBjoern A. Zeeb 333105a37caSEmmanuel Vadot return (pdev); 334105a37caSEmmanuel Vadot } 335105a37caSEmmanuel Vadot 336105a37caSEmmanuel Vadot struct pci_dev * 337105a37caSEmmanuel Vadot lkpi_pci_get_class(unsigned int class, struct pci_dev *from) 338105a37caSEmmanuel Vadot { 339105a37caSEmmanuel Vadot device_t dev; 340105a37caSEmmanuel Vadot device_t devfrom = NULL; 341105a37caSEmmanuel Vadot struct pci_dev *pdev; 342105a37caSEmmanuel Vadot 343105a37caSEmmanuel Vadot if (from != NULL) 344105a37caSEmmanuel Vadot devfrom = from->dev.bsddev; 345105a37caSEmmanuel Vadot 346105a37caSEmmanuel Vadot dev = pci_find_class_from(class >> 16, (class >> 8) & 0xFF, devfrom); 347105a37caSEmmanuel Vadot if (dev == NULL) 348105a37caSEmmanuel Vadot return (NULL); 349105a37caSEmmanuel Vadot 350105a37caSEmmanuel Vadot pdev = lkpinew_pci_dev(dev); 351105a37caSEmmanuel Vadot return (pdev); 352105a37caSEmmanuel Vadot } 353105a37caSEmmanuel Vadot 354105a37caSEmmanuel Vadot struct pci_dev * 355105a37caSEmmanuel Vadot lkpi_pci_get_domain_bus_and_slot(int domain, unsigned int bus, 356105a37caSEmmanuel Vadot unsigned int devfn) 357105a37caSEmmanuel Vadot { 358105a37caSEmmanuel Vadot device_t dev; 359105a37caSEmmanuel Vadot struct pci_dev *pdev; 360105a37caSEmmanuel Vadot 361105a37caSEmmanuel Vadot dev = pci_find_dbsf(domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); 362105a37caSEmmanuel Vadot if (dev == NULL) 363105a37caSEmmanuel Vadot return (NULL); 364105a37caSEmmanuel Vadot 365105a37caSEmmanuel Vadot pdev = lkpinew_pci_dev(dev); 366105a37caSEmmanuel Vadot return (pdev); 367105a37caSEmmanuel Vadot } 368105a37caSEmmanuel Vadot 3698d59ecb2SHans Petter Selasky static int 3708d59ecb2SHans Petter Selasky linux_pci_probe(device_t dev) 3718d59ecb2SHans Petter Selasky { 3728d59ecb2SHans Petter Selasky const struct pci_device_id *id; 3738d59ecb2SHans Petter Selasky struct pci_driver *pdrv; 3748d59ecb2SHans Petter Selasky 3758d59ecb2SHans Petter Selasky if ((pdrv = linux_pci_find(dev, &id)) == NULL) 3768d59ecb2SHans Petter Selasky return (ENXIO); 377b38dc0a1SMark Johnston if (device_get_driver(dev) != &pdrv->bsddriver) 3788d59ecb2SHans Petter Selasky return (ENXIO); 3798d59ecb2SHans Petter Selasky device_set_desc(dev, pdrv->name); 380b91dd79bSBjoern A. Zeeb 381b91dd79bSBjoern A. Zeeb /* Assume BSS initialized (should never return BUS_PROBE_SPECIFIC). */ 382b91dd79bSBjoern A. Zeeb if (pdrv->bsd_probe_return == 0) 38339d8c387SBjoern A. Zeeb return (BUS_PROBE_DEFAULT); 384b91dd79bSBjoern A. Zeeb else 385b91dd79bSBjoern A. Zeeb return (pdrv->bsd_probe_return); 3868d59ecb2SHans Petter Selasky } 3878d59ecb2SHans Petter Selasky 3888d59ecb2SHans Petter Selasky static int 3898d59ecb2SHans Petter Selasky linux_pci_attach(device_t dev) 3908d59ecb2SHans Petter Selasky { 391253dbe74SHans Petter Selasky const struct pci_device_id *id; 392253dbe74SHans Petter Selasky struct pci_driver *pdrv; 393253dbe74SHans Petter Selasky struct pci_dev *pdev; 394253dbe74SHans Petter Selasky 395253dbe74SHans Petter Selasky pdrv = linux_pci_find(dev, &id); 396253dbe74SHans Petter Selasky pdev = device_get_softc(dev); 397253dbe74SHans Petter Selasky 398253dbe74SHans Petter Selasky MPASS(pdrv != NULL); 399253dbe74SHans Petter Selasky MPASS(pdev != NULL); 400253dbe74SHans Petter Selasky 401253dbe74SHans Petter Selasky return (linux_pci_attach_device(dev, pdrv, id, pdev)); 402253dbe74SHans Petter Selasky } 403253dbe74SHans Petter Selasky 404253dbe74SHans Petter Selasky int 405253dbe74SHans Petter Selasky linux_pci_attach_device(device_t dev, struct pci_driver *pdrv, 406253dbe74SHans Petter Selasky const struct pci_device_id *id, struct pci_dev *pdev) 407253dbe74SHans Petter Selasky { 4088d59ecb2SHans Petter Selasky struct resource_list_entry *rle; 4090b7bd01aSMark Johnston device_t parent; 410de27805fSKonstantin Belousov uintptr_t rid; 4118d59ecb2SHans Petter Selasky int error; 412de27805fSKonstantin Belousov bool isdrm; 4138d59ecb2SHans Petter Selasky 4141e3db1deSHans Petter Selasky linux_set_current(curthread); 4150b7bd01aSMark Johnston 4160b7bd01aSMark Johnston parent = device_get_parent(dev); 417de27805fSKonstantin Belousov isdrm = pdrv != NULL && pdrv->isdrm; 418de27805fSKonstantin Belousov 419de27805fSKonstantin Belousov if (isdrm) { 4201fac2cb4SBjoern A. Zeeb struct pci_devinfo *dinfo; 4211fac2cb4SBjoern A. Zeeb 4220b7bd01aSMark Johnston dinfo = device_get_ivars(parent); 4230b7bd01aSMark Johnston device_set_ivars(dev, dinfo); 4240b7bd01aSMark Johnston } 4250b7bd01aSMark Johnston 426105a37caSEmmanuel Vadot lkpifill_pci_dev(dev, pdev); 427de27805fSKonstantin Belousov if (isdrm) 428de27805fSKonstantin Belousov PCI_GET_ID(device_get_parent(parent), parent, PCI_ID_RID, &rid); 429de27805fSKonstantin Belousov else 430de27805fSKonstantin Belousov PCI_GET_ID(parent, dev, PCI_ID_RID, &rid); 431de27805fSKonstantin Belousov pdev->devfn = rid; 4328d59ecb2SHans Petter Selasky pdev->pdrv = pdrv; 43382098c8bSJessica Clarke rle = linux_pci_get_rle(pdev, SYS_RES_IRQ, 0, false); 434e996b07cSHans Petter Selasky if (rle != NULL) 4358d59ecb2SHans Petter Selasky pdev->dev.irq = rle->start; 4368d59ecb2SHans Petter Selasky else 4370a61267aSHans Petter Selasky pdev->dev.irq = LINUX_IRQ_INVALID; 4388d59ecb2SHans Petter Selasky pdev->irq = pdev->dev.irq; 439f211d536STycho Nightingale error = linux_pdev_dma_init(pdev); 440f211d536STycho Nightingale if (error) 441eb6f5342SHans Petter Selasky goto out_dma_init; 4420b7bd01aSMark Johnston 4434c274849SEmmanuel Vadot TAILQ_INIT(&pdev->mmio); 4440b7bd01aSMark Johnston 4458d59ecb2SHans Petter Selasky spin_lock(&pci_lock); 4468d59ecb2SHans Petter Selasky list_add(&pdev->links, &pci_devices); 4478d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 4484b706099SHans Petter Selasky 449253dbe74SHans Petter Selasky if (pdrv != NULL) { 4508d59ecb2SHans Petter Selasky error = pdrv->probe(pdev, id); 451eb6f5342SHans Petter Selasky if (error) 452eb6f5342SHans Petter Selasky goto out_probe; 453253dbe74SHans Petter Selasky } 454eb6f5342SHans Petter Selasky return (0); 455eb6f5342SHans Petter Selasky 456eb6f5342SHans Petter Selasky out_probe: 457e2eb11e5SHans Petter Selasky free(pdev->bus, M_DEVBUF); 458eb6f5342SHans Petter Selasky linux_pdev_dma_uninit(pdev); 459eb6f5342SHans Petter Selasky out_dma_init: 4608d59ecb2SHans Petter Selasky spin_lock(&pci_lock); 4618d59ecb2SHans Petter Selasky list_del(&pdev->links); 4628d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 4638d59ecb2SHans Petter Selasky put_device(&pdev->dev); 464eb6f5342SHans Petter Selasky return (-error); 4658d59ecb2SHans Petter Selasky } 4668d59ecb2SHans Petter Selasky 4678d59ecb2SHans Petter Selasky static int 4688d59ecb2SHans Petter Selasky linux_pci_detach(device_t dev) 4698d59ecb2SHans Petter Selasky { 4708d59ecb2SHans Petter Selasky struct pci_dev *pdev; 4718d59ecb2SHans Petter Selasky 4728d59ecb2SHans Petter Selasky pdev = device_get_softc(dev); 4734b706099SHans Petter Selasky 474253dbe74SHans Petter Selasky MPASS(pdev != NULL); 475253dbe74SHans Petter Selasky 476253dbe74SHans Petter Selasky device_set_desc(dev, NULL); 477253dbe74SHans Petter Selasky 478253dbe74SHans Petter Selasky return (linux_pci_detach_device(pdev)); 479253dbe74SHans Petter Selasky } 480253dbe74SHans Petter Selasky 481253dbe74SHans Petter Selasky int 482253dbe74SHans Petter Selasky linux_pci_detach_device(struct pci_dev *pdev) 483253dbe74SHans Petter Selasky { 484253dbe74SHans Petter Selasky 485253dbe74SHans Petter Selasky linux_set_current(curthread); 486253dbe74SHans Petter Selasky 487253dbe74SHans Petter Selasky if (pdev->pdrv != NULL) 4888d59ecb2SHans Petter Selasky pdev->pdrv->remove(pdev); 489e2eb11e5SHans Petter Selasky 4908e106c52SBjoern A. Zeeb if (pdev->root != NULL) 4918e106c52SBjoern A. Zeeb pci_dev_put(pdev->root); 492e2eb11e5SHans Petter Selasky free(pdev->bus, M_DEVBUF); 493f211d536STycho Nightingale linux_pdev_dma_uninit(pdev); 4944b706099SHans Petter Selasky 4958d59ecb2SHans Petter Selasky spin_lock(&pci_lock); 4968d59ecb2SHans Petter Selasky list_del(&pdev->links); 4978d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 4988d59ecb2SHans Petter Selasky put_device(&pdev->dev); 4998d59ecb2SHans Petter Selasky 5008d59ecb2SHans Petter Selasky return (0); 5018d59ecb2SHans Petter Selasky } 5028d59ecb2SHans Petter Selasky 5037d133393SHans Petter Selasky static int 504d4a4960cSBjoern A. Zeeb lkpi_pci_disable_dev(struct device *dev) 505d4a4960cSBjoern A. Zeeb { 506d4a4960cSBjoern A. Zeeb 507d4a4960cSBjoern A. Zeeb (void) pci_disable_io(dev->bsddev, SYS_RES_MEMORY); 508d4a4960cSBjoern A. Zeeb (void) pci_disable_io(dev->bsddev, SYS_RES_IOPORT); 509d4a4960cSBjoern A. Zeeb return (0); 510d4a4960cSBjoern A. Zeeb } 511d4a4960cSBjoern A. Zeeb 5123ea682e2SWarner Losh struct pci_devres * 5133ea682e2SWarner Losh lkpi_pci_devres_get_alloc(struct pci_dev *pdev) 5143ea682e2SWarner Losh { 5153ea682e2SWarner Losh struct pci_devres *dr; 5163ea682e2SWarner Losh 5173ea682e2SWarner Losh dr = lkpi_devres_find(&pdev->dev, lkpi_pci_devres_release, NULL, NULL); 5183ea682e2SWarner Losh if (dr == NULL) { 5193ea682e2SWarner Losh dr = lkpi_devres_alloc(lkpi_pci_devres_release, sizeof(*dr), 5203ea682e2SWarner Losh GFP_KERNEL | __GFP_ZERO); 5213ea682e2SWarner Losh if (dr != NULL) 5223ea682e2SWarner Losh lkpi_devres_add(&pdev->dev, dr); 5233ea682e2SWarner Losh } 5243ea682e2SWarner Losh 5253ea682e2SWarner Losh return (dr); 5263ea682e2SWarner Losh } 5273ea682e2SWarner Losh 528d4a4960cSBjoern A. Zeeb void 529d4a4960cSBjoern A. Zeeb lkpi_pci_devres_release(struct device *dev, void *p) 530d4a4960cSBjoern A. Zeeb { 531d4a4960cSBjoern A. Zeeb struct pci_devres *dr; 532d4a4960cSBjoern A. Zeeb struct pci_dev *pdev; 533d4a4960cSBjoern A. Zeeb int bar; 534d4a4960cSBjoern A. Zeeb 535d4a4960cSBjoern A. Zeeb pdev = to_pci_dev(dev); 536d4a4960cSBjoern A. Zeeb dr = p; 537d4a4960cSBjoern A. Zeeb 538d4a4960cSBjoern A. Zeeb if (pdev->msix_enabled) 539d4a4960cSBjoern A. Zeeb lkpi_pci_disable_msix(pdev); 540d4a4960cSBjoern A. Zeeb if (pdev->msi_enabled) 541d4a4960cSBjoern A. Zeeb lkpi_pci_disable_msi(pdev); 542d4a4960cSBjoern A. Zeeb 543d4a4960cSBjoern A. Zeeb if (dr->enable_io && lkpi_pci_disable_dev(dev) == 0) 544d4a4960cSBjoern A. Zeeb dr->enable_io = false; 545d4a4960cSBjoern A. Zeeb 546d4a4960cSBjoern A. Zeeb if (dr->region_mask == 0) 547d4a4960cSBjoern A. Zeeb return; 548d4a4960cSBjoern A. Zeeb for (bar = PCIR_MAX_BAR_0; bar >= 0; bar--) { 549d4a4960cSBjoern A. Zeeb 550d4a4960cSBjoern A. Zeeb if ((dr->region_mask & (1 << bar)) == 0) 551d4a4960cSBjoern A. Zeeb continue; 552d4a4960cSBjoern A. Zeeb pci_release_region(pdev, bar); 553d4a4960cSBjoern A. Zeeb } 554d4a4960cSBjoern A. Zeeb } 555d4a4960cSBjoern A. Zeeb 556*2bf3361dSWarner Losh struct pcim_iomap_devres * 557*2bf3361dSWarner Losh lkpi_pcim_iomap_devres_find(struct pci_dev *pdev) 558*2bf3361dSWarner Losh { 559*2bf3361dSWarner Losh struct pcim_iomap_devres *dr; 560*2bf3361dSWarner Losh 561*2bf3361dSWarner Losh dr = lkpi_devres_find(&pdev->dev, lkpi_pcim_iomap_table_release, 562*2bf3361dSWarner Losh NULL, NULL); 563*2bf3361dSWarner Losh if (dr == NULL) { 564*2bf3361dSWarner Losh dr = lkpi_devres_alloc(lkpi_pcim_iomap_table_release, 565*2bf3361dSWarner Losh sizeof(*dr), GFP_KERNEL | __GFP_ZERO); 566*2bf3361dSWarner Losh if (dr != NULL) 567*2bf3361dSWarner Losh lkpi_devres_add(&pdev->dev, dr); 568*2bf3361dSWarner Losh } 569*2bf3361dSWarner Losh 570*2bf3361dSWarner Losh if (dr == NULL) 571*2bf3361dSWarner Losh device_printf(pdev->dev.bsddev, "%s: NULL\n", __func__); 572*2bf3361dSWarner Losh 573*2bf3361dSWarner Losh return (dr); 574*2bf3361dSWarner Losh } 575*2bf3361dSWarner Losh 576d4a4960cSBjoern A. Zeeb void 577d4a4960cSBjoern A. Zeeb lkpi_pcim_iomap_table_release(struct device *dev, void *p) 578d4a4960cSBjoern A. Zeeb { 579d4a4960cSBjoern A. Zeeb struct pcim_iomap_devres *dr; 580d4a4960cSBjoern A. Zeeb struct pci_dev *pdev; 581d4a4960cSBjoern A. Zeeb int bar; 582d4a4960cSBjoern A. Zeeb 583d4a4960cSBjoern A. Zeeb dr = p; 584d4a4960cSBjoern A. Zeeb pdev = to_pci_dev(dev); 585d4a4960cSBjoern A. Zeeb for (bar = PCIR_MAX_BAR_0; bar >= 0; bar--) { 586d4a4960cSBjoern A. Zeeb 587d4a4960cSBjoern A. Zeeb if (dr->mmio_table[bar] == NULL) 588d4a4960cSBjoern A. Zeeb continue; 589d4a4960cSBjoern A. Zeeb 590d4a4960cSBjoern A. Zeeb pci_iounmap(pdev, dr->mmio_table[bar]); 591d4a4960cSBjoern A. Zeeb } 592d4a4960cSBjoern A. Zeeb } 593d4a4960cSBjoern A. Zeeb 594d4a4960cSBjoern A. Zeeb static int 5957d133393SHans Petter Selasky linux_pci_suspend(device_t dev) 5967d133393SHans Petter Selasky { 597d34188a0SMark Johnston const struct dev_pm_ops *pmops; 5987d133393SHans Petter Selasky struct pm_message pm = { }; 5997d133393SHans Petter Selasky struct pci_dev *pdev; 600d34188a0SMark Johnston int error; 6017d133393SHans Petter Selasky 602d34188a0SMark Johnston error = 0; 6031e3db1deSHans Petter Selasky linux_set_current(curthread); 6047d133393SHans Petter Selasky pdev = device_get_softc(dev); 605d34188a0SMark Johnston pmops = pdev->pdrv->driver.pm; 606d34188a0SMark Johnston 6077d133393SHans Petter Selasky if (pdev->pdrv->suspend != NULL) 608d34188a0SMark Johnston error = -pdev->pdrv->suspend(pdev, pm); 609d34188a0SMark Johnston else if (pmops != NULL && pmops->suspend != NULL) { 610d34188a0SMark Johnston error = -pmops->suspend(&pdev->dev); 611d34188a0SMark Johnston if (error == 0 && pmops->suspend_late != NULL) 612d34188a0SMark Johnston error = -pmops->suspend_late(&pdev->dev); 613d34188a0SMark Johnston } 614d34188a0SMark Johnston return (error); 6157d133393SHans Petter Selasky } 6167d133393SHans Petter Selasky 6177d133393SHans Petter Selasky static int 6187d133393SHans Petter Selasky linux_pci_resume(device_t dev) 6197d133393SHans Petter Selasky { 620d34188a0SMark Johnston const struct dev_pm_ops *pmops; 6217d133393SHans Petter Selasky struct pci_dev *pdev; 622d34188a0SMark Johnston int error; 6237d133393SHans Petter Selasky 624d34188a0SMark Johnston error = 0; 6251e3db1deSHans Petter Selasky linux_set_current(curthread); 6267d133393SHans Petter Selasky pdev = device_get_softc(dev); 627d34188a0SMark Johnston pmops = pdev->pdrv->driver.pm; 628d34188a0SMark Johnston 6297d133393SHans Petter Selasky if (pdev->pdrv->resume != NULL) 630d34188a0SMark Johnston error = -pdev->pdrv->resume(pdev); 631d34188a0SMark Johnston else if (pmops != NULL && pmops->resume != NULL) { 632d34188a0SMark Johnston if (pmops->resume_early != NULL) 633d34188a0SMark Johnston error = -pmops->resume_early(&pdev->dev); 634d34188a0SMark Johnston if (error == 0 && pmops->resume != NULL) 635d34188a0SMark Johnston error = -pmops->resume(&pdev->dev); 636d34188a0SMark Johnston } 637d34188a0SMark Johnston return (error); 6387d133393SHans Petter Selasky } 6397d133393SHans Petter Selasky 6407d133393SHans Petter Selasky static int 6417d133393SHans Petter Selasky linux_pci_shutdown(device_t dev) 6427d133393SHans Petter Selasky { 6437d133393SHans Petter Selasky struct pci_dev *pdev; 6447d133393SHans Petter Selasky 6451e3db1deSHans Petter Selasky linux_set_current(curthread); 6467d133393SHans Petter Selasky pdev = device_get_softc(dev); 6474b706099SHans Petter Selasky if (pdev->pdrv->shutdown != NULL) 6487d133393SHans Petter Selasky pdev->pdrv->shutdown(pdev); 6497d133393SHans Petter Selasky return (0); 6507d133393SHans Petter Selasky } 6517d133393SHans Petter Selasky 6520b7bd01aSMark Johnston static int 6532928e60eSKonstantin Belousov linux_pci_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t *pf_config) 6542928e60eSKonstantin Belousov { 6552928e60eSKonstantin Belousov struct pci_dev *pdev; 6562928e60eSKonstantin Belousov int error; 6572928e60eSKonstantin Belousov 6582928e60eSKonstantin Belousov linux_set_current(curthread); 6592928e60eSKonstantin Belousov pdev = device_get_softc(dev); 6602928e60eSKonstantin Belousov if (pdev->pdrv->bsd_iov_init != NULL) 6612928e60eSKonstantin Belousov error = pdev->pdrv->bsd_iov_init(dev, num_vfs, pf_config); 6622928e60eSKonstantin Belousov else 6632928e60eSKonstantin Belousov error = EINVAL; 6642928e60eSKonstantin Belousov return (error); 6652928e60eSKonstantin Belousov } 6662928e60eSKonstantin Belousov 6672928e60eSKonstantin Belousov static void 6682928e60eSKonstantin Belousov linux_pci_iov_uninit(device_t dev) 6692928e60eSKonstantin Belousov { 6702928e60eSKonstantin Belousov struct pci_dev *pdev; 6712928e60eSKonstantin Belousov 6722928e60eSKonstantin Belousov linux_set_current(curthread); 6732928e60eSKonstantin Belousov pdev = device_get_softc(dev); 6742928e60eSKonstantin Belousov if (pdev->pdrv->bsd_iov_uninit != NULL) 6752928e60eSKonstantin Belousov pdev->pdrv->bsd_iov_uninit(dev); 6762928e60eSKonstantin Belousov } 6772928e60eSKonstantin Belousov 6782928e60eSKonstantin Belousov static int 6792928e60eSKonstantin Belousov linux_pci_iov_add_vf(device_t dev, uint16_t vfnum, const nvlist_t *vf_config) 6802928e60eSKonstantin Belousov { 6812928e60eSKonstantin Belousov struct pci_dev *pdev; 6822928e60eSKonstantin Belousov int error; 6832928e60eSKonstantin Belousov 6842928e60eSKonstantin Belousov linux_set_current(curthread); 6852928e60eSKonstantin Belousov pdev = device_get_softc(dev); 6862928e60eSKonstantin Belousov if (pdev->pdrv->bsd_iov_add_vf != NULL) 6872928e60eSKonstantin Belousov error = pdev->pdrv->bsd_iov_add_vf(dev, vfnum, vf_config); 6882928e60eSKonstantin Belousov else 6892928e60eSKonstantin Belousov error = EINVAL; 6902928e60eSKonstantin Belousov return (error); 6912928e60eSKonstantin Belousov } 6922928e60eSKonstantin Belousov 6932928e60eSKonstantin Belousov static int 6940b7bd01aSMark Johnston _linux_pci_register_driver(struct pci_driver *pdrv, devclass_t dc) 6958d59ecb2SHans Petter Selasky { 6960b7bd01aSMark Johnston int error; 6978d59ecb2SHans Petter Selasky 6981e3db1deSHans Petter Selasky linux_set_current(curthread); 6998d59ecb2SHans Petter Selasky spin_lock(&pci_lock); 700cf899348SBjoern A. Zeeb list_add(&pdrv->node, &pci_drivers); 7018d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 70281d058dfSBjoern A. Zeeb if (pdrv->bsddriver.name == NULL) 703b38dc0a1SMark Johnston pdrv->bsddriver.name = pdrv->name; 704b38dc0a1SMark Johnston pdrv->bsddriver.methods = pci_methods; 705b38dc0a1SMark Johnston pdrv->bsddriver.size = sizeof(struct pci_dev); 706b38dc0a1SMark Johnston 707c6df6f53SWarner Losh bus_topo_lock(); 7080b7bd01aSMark Johnston error = devclass_add_driver(dc, &pdrv->bsddriver, 709b38dc0a1SMark Johnston BUS_PASS_DEFAULT, &pdrv->bsdclass); 710c6df6f53SWarner Losh bus_topo_unlock(); 7118d59ecb2SHans Petter Selasky return (-error); 7128d59ecb2SHans Petter Selasky } 7138d59ecb2SHans Petter Selasky 7140b7bd01aSMark Johnston int 7150b7bd01aSMark Johnston linux_pci_register_driver(struct pci_driver *pdrv) 7160b7bd01aSMark Johnston { 7170b7bd01aSMark Johnston devclass_t dc; 7180b7bd01aSMark Johnston 7190b7bd01aSMark Johnston dc = devclass_find("pci"); 7200b7bd01aSMark Johnston if (dc == NULL) 7210b7bd01aSMark Johnston return (-ENXIO); 7220b7bd01aSMark Johnston pdrv->isdrm = false; 7230b7bd01aSMark Johnston return (_linux_pci_register_driver(pdrv, dc)); 7240b7bd01aSMark Johnston } 7250b7bd01aSMark Johnston 72682098c8bSJessica Clarke struct resource_list_entry * 72782098c8bSJessica Clarke linux_pci_reserve_bar(struct pci_dev *pdev, struct resource_list *rl, 72882098c8bSJessica Clarke int type, int rid) 72982098c8bSJessica Clarke { 73082098c8bSJessica Clarke device_t dev; 73182098c8bSJessica Clarke struct resource *res; 73282098c8bSJessica Clarke 73382098c8bSJessica Clarke KASSERT(type == SYS_RES_IOPORT || type == SYS_RES_MEMORY, 73482098c8bSJessica Clarke ("trying to reserve non-BAR type %d", type)); 73582098c8bSJessica Clarke 73682098c8bSJessica Clarke dev = pdev->pdrv != NULL && pdev->pdrv->isdrm ? 73782098c8bSJessica Clarke device_get_parent(pdev->dev.bsddev) : pdev->dev.bsddev; 73882098c8bSJessica Clarke res = pci_reserve_map(device_get_parent(dev), dev, type, &rid, 0, ~0, 73982098c8bSJessica Clarke 1, 1, 0); 74082098c8bSJessica Clarke if (res == NULL) 74182098c8bSJessica Clarke return (NULL); 74282098c8bSJessica Clarke return (resource_list_find(rl, type, rid)); 74382098c8bSJessica Clarke } 74482098c8bSJessica Clarke 745937a05baSJustin Hibbits unsigned long 746937a05baSJustin Hibbits pci_resource_start(struct pci_dev *pdev, int bar) 747937a05baSJustin Hibbits { 748937a05baSJustin Hibbits struct resource_list_entry *rle; 7494eaa2fdeSJustin Hibbits rman_res_t newstart; 750937a05baSJustin Hibbits device_t dev; 7511fb99e97SMark Johnston int error; 752937a05baSJustin Hibbits 75382098c8bSJessica Clarke if ((rle = linux_pci_get_bar(pdev, bar, true)) == NULL) 754937a05baSJustin Hibbits return (0); 755de27805fSKonstantin Belousov dev = pdev->pdrv != NULL && pdev->pdrv->isdrm ? 756de27805fSKonstantin Belousov device_get_parent(pdev->dev.bsddev) : pdev->dev.bsddev; 7571fb99e97SMark Johnston error = bus_translate_resource(dev, rle->type, rle->start, &newstart); 7581fb99e97SMark Johnston if (error != 0) { 7591fb99e97SMark Johnston device_printf(pdev->dev.bsddev, 7601fb99e97SMark Johnston "translate of %#jx failed: %d\n", 7611fb99e97SMark Johnston (uintmax_t)rle->start, error); 762937a05baSJustin Hibbits return (0); 763937a05baSJustin Hibbits } 764937a05baSJustin Hibbits return (newstart); 765937a05baSJustin Hibbits } 766937a05baSJustin Hibbits 767937a05baSJustin Hibbits unsigned long 768937a05baSJustin Hibbits pci_resource_len(struct pci_dev *pdev, int bar) 769937a05baSJustin Hibbits { 770937a05baSJustin Hibbits struct resource_list_entry *rle; 771937a05baSJustin Hibbits 77282098c8bSJessica Clarke if ((rle = linux_pci_get_bar(pdev, bar, true)) == NULL) 773937a05baSJustin Hibbits return (0); 774937a05baSJustin Hibbits return (rle->count); 775937a05baSJustin Hibbits } 776937a05baSJustin Hibbits 7770b7bd01aSMark Johnston int 7781cdb2534SWarner Losh pci_request_region(struct pci_dev *pdev, int bar, const char *res_name) 7791cdb2534SWarner Losh { 7801cdb2534SWarner Losh struct resource *res; 7811cdb2534SWarner Losh struct pci_devres *dr; 7821cdb2534SWarner Losh struct pci_mmio_region *mmio; 7831cdb2534SWarner Losh int rid; 7841cdb2534SWarner Losh int type; 7851cdb2534SWarner Losh 7861cdb2534SWarner Losh type = pci_resource_type(pdev, bar); 7871cdb2534SWarner Losh if (type < 0) 7881cdb2534SWarner Losh return (-ENODEV); 7891cdb2534SWarner Losh rid = PCIR_BAR(bar); 7901cdb2534SWarner Losh res = bus_alloc_resource_any(pdev->dev.bsddev, type, &rid, 7911cdb2534SWarner Losh RF_ACTIVE|RF_SHAREABLE); 7921cdb2534SWarner Losh if (res == NULL) { 7931cdb2534SWarner Losh device_printf(pdev->dev.bsddev, "%s: failed to alloc " 7941cdb2534SWarner Losh "bar %d type %d rid %d\n", 7951cdb2534SWarner Losh __func__, bar, type, PCIR_BAR(bar)); 7961cdb2534SWarner Losh return (-ENODEV); 7971cdb2534SWarner Losh } 7981cdb2534SWarner Losh 7991cdb2534SWarner Losh /* 8001cdb2534SWarner Losh * It seems there is an implicit devres tracking on these if the device 8011cdb2534SWarner Losh * is managed; otherwise the resources are not automatiaclly freed on 8021cdb2534SWarner Losh * FreeBSD/LinuxKPI tough they should be/are expected to be by Linux 8031cdb2534SWarner Losh * drivers. 8041cdb2534SWarner Losh */ 8051cdb2534SWarner Losh dr = lkpi_pci_devres_find(pdev); 8061cdb2534SWarner Losh if (dr != NULL) { 8071cdb2534SWarner Losh dr->region_mask |= (1 << bar); 8081cdb2534SWarner Losh dr->region_table[bar] = res; 8091cdb2534SWarner Losh } 8101cdb2534SWarner Losh 8111cdb2534SWarner Losh /* Even if the device is not managed we need to track it for iomap. */ 8121cdb2534SWarner Losh mmio = malloc(sizeof(*mmio), M_DEVBUF, M_WAITOK | M_ZERO); 8131cdb2534SWarner Losh mmio->rid = PCIR_BAR(bar); 8141cdb2534SWarner Losh mmio->type = type; 8151cdb2534SWarner Losh mmio->res = res; 8161cdb2534SWarner Losh TAILQ_INSERT_TAIL(&pdev->mmio, mmio, next); 8171cdb2534SWarner Losh 8181cdb2534SWarner Losh return (0); 8191cdb2534SWarner Losh } 8201cdb2534SWarner Losh 8211cdb2534SWarner Losh struct resource * 8221cdb2534SWarner Losh _lkpi_pci_iomap(struct pci_dev *pdev, int bar, int mmio_size __unused) 8231cdb2534SWarner Losh { 8241cdb2534SWarner Losh struct pci_mmio_region *mmio, *p; 8251cdb2534SWarner Losh int type; 8261cdb2534SWarner Losh 8271cdb2534SWarner Losh type = pci_resource_type(pdev, bar); 8281cdb2534SWarner Losh if (type < 0) { 8291cdb2534SWarner Losh device_printf(pdev->dev.bsddev, "%s: bar %d type %d\n", 8301cdb2534SWarner Losh __func__, bar, type); 8311cdb2534SWarner Losh return (NULL); 8321cdb2534SWarner Losh } 8331cdb2534SWarner Losh 8341cdb2534SWarner Losh /* 8351cdb2534SWarner Losh * Check for duplicate mappings. 8361cdb2534SWarner Losh * This can happen if a driver calls pci_request_region() first. 8371cdb2534SWarner Losh */ 8381cdb2534SWarner Losh TAILQ_FOREACH_SAFE(mmio, &pdev->mmio, next, p) { 8391cdb2534SWarner Losh if (mmio->type == type && mmio->rid == PCIR_BAR(bar)) { 8401cdb2534SWarner Losh return (mmio->res); 8411cdb2534SWarner Losh } 8421cdb2534SWarner Losh } 8431cdb2534SWarner Losh 8441cdb2534SWarner Losh mmio = malloc(sizeof(*mmio), M_DEVBUF, M_WAITOK | M_ZERO); 8451cdb2534SWarner Losh mmio->rid = PCIR_BAR(bar); 8461cdb2534SWarner Losh mmio->type = type; 8471cdb2534SWarner Losh mmio->res = bus_alloc_resource_any(pdev->dev.bsddev, mmio->type, 8481cdb2534SWarner Losh &mmio->rid, RF_ACTIVE|RF_SHAREABLE); 8491cdb2534SWarner Losh if (mmio->res == NULL) { 8501cdb2534SWarner Losh device_printf(pdev->dev.bsddev, "%s: failed to alloc " 8511cdb2534SWarner Losh "bar %d type %d rid %d\n", 8521cdb2534SWarner Losh __func__, bar, type, PCIR_BAR(bar)); 8531cdb2534SWarner Losh free(mmio, M_DEVBUF); 8541cdb2534SWarner Losh return (NULL); 8551cdb2534SWarner Losh } 8561cdb2534SWarner Losh TAILQ_INSERT_TAIL(&pdev->mmio, mmio, next); 8571cdb2534SWarner Losh 8581cdb2534SWarner Losh return (mmio->res); 8591cdb2534SWarner Losh } 8601cdb2534SWarner Losh 8611cdb2534SWarner Losh int 8620b7bd01aSMark Johnston linux_pci_register_drm_driver(struct pci_driver *pdrv) 8630b7bd01aSMark Johnston { 8640b7bd01aSMark Johnston devclass_t dc; 8650b7bd01aSMark Johnston 8660b7bd01aSMark Johnston dc = devclass_create("vgapci"); 8670b7bd01aSMark Johnston if (dc == NULL) 8680b7bd01aSMark Johnston return (-ENXIO); 8690b7bd01aSMark Johnston pdrv->isdrm = true; 8700b7bd01aSMark Johnston pdrv->name = "drmn"; 8710b7bd01aSMark Johnston return (_linux_pci_register_driver(pdrv, dc)); 8720b7bd01aSMark Johnston } 8730b7bd01aSMark Johnston 8748d59ecb2SHans Petter Selasky void 8750b7bd01aSMark Johnston linux_pci_unregister_driver(struct pci_driver *pdrv) 8768d59ecb2SHans Petter Selasky { 8778d59ecb2SHans Petter Selasky devclass_t bus; 8788d59ecb2SHans Petter Selasky 8798d59ecb2SHans Petter Selasky bus = devclass_find("pci"); 8808d59ecb2SHans Petter Selasky 8810a930cf0SMark Johnston spin_lock(&pci_lock); 882cf899348SBjoern A. Zeeb list_del(&pdrv->node); 8830a930cf0SMark Johnston spin_unlock(&pci_lock); 884c6df6f53SWarner Losh bus_topo_lock(); 8858d59ecb2SHans Petter Selasky if (bus != NULL) 886b38dc0a1SMark Johnston devclass_delete_driver(bus, &pdrv->bsddriver); 887c6df6f53SWarner Losh bus_topo_unlock(); 8888d59ecb2SHans Petter Selasky } 889f211d536STycho Nightingale 8905098ed5fSJohannes Lundberg void 8915098ed5fSJohannes Lundberg linux_pci_unregister_drm_driver(struct pci_driver *pdrv) 8925098ed5fSJohannes Lundberg { 8935098ed5fSJohannes Lundberg devclass_t bus; 8945098ed5fSJohannes Lundberg 8955098ed5fSJohannes Lundberg bus = devclass_find("vgapci"); 8965098ed5fSJohannes Lundberg 8975098ed5fSJohannes Lundberg spin_lock(&pci_lock); 898cf899348SBjoern A. Zeeb list_del(&pdrv->node); 8995098ed5fSJohannes Lundberg spin_unlock(&pci_lock); 900c6df6f53SWarner Losh bus_topo_lock(); 9015098ed5fSJohannes Lundberg if (bus != NULL) 9025098ed5fSJohannes Lundberg devclass_delete_driver(bus, &pdrv->bsddriver); 903c6df6f53SWarner Losh bus_topo_unlock(); 9045098ed5fSJohannes Lundberg } 9055098ed5fSJohannes Lundberg 90636b5c440SWarner Losh int 90736b5c440SWarner Losh pci_alloc_irq_vectors(struct pci_dev *pdev, int minv, int maxv, 90836b5c440SWarner Losh unsigned int flags) 90936b5c440SWarner Losh { 91036b5c440SWarner Losh int error; 91136b5c440SWarner Losh 91236b5c440SWarner Losh if (flags & PCI_IRQ_MSIX) { 91336b5c440SWarner Losh struct msix_entry *entries; 91436b5c440SWarner Losh int i; 91536b5c440SWarner Losh 91636b5c440SWarner Losh entries = kcalloc(maxv, sizeof(*entries), GFP_KERNEL); 91736b5c440SWarner Losh if (entries == NULL) { 91836b5c440SWarner Losh error = -ENOMEM; 91936b5c440SWarner Losh goto out; 92036b5c440SWarner Losh } 92136b5c440SWarner Losh for (i = 0; i < maxv; ++i) 92236b5c440SWarner Losh entries[i].entry = i; 92336b5c440SWarner Losh error = pci_enable_msix(pdev, entries, maxv); 92436b5c440SWarner Losh out: 92536b5c440SWarner Losh kfree(entries); 92636b5c440SWarner Losh if (error == 0 && pdev->msix_enabled) 92736b5c440SWarner Losh return (pdev->dev.irq_end - pdev->dev.irq_start); 92836b5c440SWarner Losh } 92936b5c440SWarner Losh if (flags & PCI_IRQ_MSI) { 93036b5c440SWarner Losh error = pci_enable_msi(pdev); 93136b5c440SWarner Losh if (error == 0 && pdev->msi_enabled) 93236b5c440SWarner Losh return (pdev->dev.irq_end - pdev->dev.irq_start); 93336b5c440SWarner Losh } 93436b5c440SWarner Losh if (flags & PCI_IRQ_LEGACY) { 93536b5c440SWarner Losh if (pdev->irq) 93636b5c440SWarner Losh return (1); 93736b5c440SWarner Losh } 93836b5c440SWarner Losh 93936b5c440SWarner Losh return (-EINVAL); 94036b5c440SWarner Losh } 94136b5c440SWarner Losh 942d4fedb75SHans Petter Selasky CTASSERT(sizeof(dma_addr_t) <= sizeof(uint64_t)); 943d4fedb75SHans Petter Selasky 944f211d536STycho Nightingale struct linux_dma_obj { 945f211d536STycho Nightingale void *vaddr; 946d4fedb75SHans Petter Selasky uint64_t dma_addr; 947f211d536STycho Nightingale bus_dmamap_t dmamap; 948c39eefe7SBjoern A. Zeeb bus_dma_tag_t dmat; 949f211d536STycho Nightingale }; 950f211d536STycho Nightingale 951f211d536STycho Nightingale static uma_zone_t linux_dma_trie_zone; 952f211d536STycho Nightingale static uma_zone_t linux_dma_obj_zone; 953f211d536STycho Nightingale 954f211d536STycho Nightingale static void 955f211d536STycho Nightingale linux_dma_init(void *arg) 956f211d536STycho Nightingale { 957f211d536STycho Nightingale 958f211d536STycho Nightingale linux_dma_trie_zone = uma_zcreate("linux_dma_pctrie", 959f211d536STycho Nightingale pctrie_node_size(), NULL, NULL, pctrie_zone_init, NULL, 960f211d536STycho Nightingale UMA_ALIGN_PTR, 0); 961f211d536STycho Nightingale linux_dma_obj_zone = uma_zcreate("linux_dma_object", 962f211d536STycho Nightingale sizeof(struct linux_dma_obj), NULL, NULL, NULL, NULL, 963f211d536STycho Nightingale UMA_ALIGN_PTR, 0); 964e8670741SBjoern A. Zeeb lkpi_pci_nseg1_fail = counter_u64_alloc(M_WAITOK); 965f211d536STycho Nightingale } 966f211d536STycho Nightingale SYSINIT(linux_dma, SI_SUB_DRIVERS, SI_ORDER_THIRD, linux_dma_init, NULL); 967f211d536STycho Nightingale 968f211d536STycho Nightingale static void 969f211d536STycho Nightingale linux_dma_uninit(void *arg) 970f211d536STycho Nightingale { 971f211d536STycho Nightingale 972e8670741SBjoern A. Zeeb counter_u64_free(lkpi_pci_nseg1_fail); 973f211d536STycho Nightingale uma_zdestroy(linux_dma_obj_zone); 974f211d536STycho Nightingale uma_zdestroy(linux_dma_trie_zone); 975f211d536STycho Nightingale } 976f211d536STycho Nightingale SYSUNINIT(linux_dma, SI_SUB_DRIVERS, SI_ORDER_THIRD, linux_dma_uninit, NULL); 977f211d536STycho Nightingale 978f211d536STycho Nightingale static void * 979f211d536STycho Nightingale linux_dma_trie_alloc(struct pctrie *ptree) 980f211d536STycho Nightingale { 981f211d536STycho Nightingale 98292a15f94SRyan Stone return (uma_zalloc(linux_dma_trie_zone, M_NOWAIT)); 983f211d536STycho Nightingale } 984f211d536STycho Nightingale 985f211d536STycho Nightingale static void 986f211d536STycho Nightingale linux_dma_trie_free(struct pctrie *ptree, void *node) 987f211d536STycho Nightingale { 988f211d536STycho Nightingale 989f211d536STycho Nightingale uma_zfree(linux_dma_trie_zone, node); 990f211d536STycho Nightingale } 991f211d536STycho Nightingale 992f211d536STycho Nightingale PCTRIE_DEFINE(LINUX_DMA, linux_dma_obj, dma_addr, linux_dma_trie_alloc, 993f211d536STycho Nightingale linux_dma_trie_free); 994f211d536STycho Nightingale 995b961c0f2STycho Nightingale #if defined(__i386__) || defined(__amd64__) || defined(__aarch64__) 996c39eefe7SBjoern A. Zeeb static dma_addr_t 997c39eefe7SBjoern A. Zeeb linux_dma_map_phys_common(struct device *dev, vm_paddr_t phys, size_t len, 998c39eefe7SBjoern A. Zeeb bus_dma_tag_t dmat) 999f211d536STycho Nightingale { 1000f211d536STycho Nightingale struct linux_dma_priv *priv; 1001f211d536STycho Nightingale struct linux_dma_obj *obj; 1002f211d536STycho Nightingale int error, nseg; 1003f211d536STycho Nightingale bus_dma_segment_t seg; 1004f211d536STycho Nightingale 1005f211d536STycho Nightingale priv = dev->dma_priv; 1006f211d536STycho Nightingale 1007b961c0f2STycho Nightingale /* 1008b961c0f2STycho Nightingale * If the resultant mapping will be entirely 1:1 with the 1009b961c0f2STycho Nightingale * physical address, short-circuit the remainder of the 1010b961c0f2STycho Nightingale * bus_dma API. This avoids tracking collisions in the pctrie 1011b961c0f2STycho Nightingale * with the additional benefit of reducing overhead. 1012b961c0f2STycho Nightingale */ 1013c39eefe7SBjoern A. Zeeb if (bus_dma_id_mapped(dmat, phys, len)) 1014b961c0f2STycho Nightingale return (phys); 1015b961c0f2STycho Nightingale 101692a15f94SRyan Stone obj = uma_zalloc(linux_dma_obj_zone, M_NOWAIT); 101792a15f94SRyan Stone if (obj == NULL) { 101892a15f94SRyan Stone return (0); 101992a15f94SRyan Stone } 1020c39eefe7SBjoern A. Zeeb obj->dmat = dmat; 1021f211d536STycho Nightingale 1022a6619e8dSHans Petter Selasky DMA_PRIV_LOCK(priv); 1023c39eefe7SBjoern A. Zeeb if (bus_dmamap_create(obj->dmat, 0, &obj->dmamap) != 0) { 1024a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1025f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1026f211d536STycho Nightingale return (0); 1027f211d536STycho Nightingale } 1028f211d536STycho Nightingale 1029f211d536STycho Nightingale nseg = -1; 1030c39eefe7SBjoern A. Zeeb if (_bus_dmamap_load_phys(obj->dmat, obj->dmamap, phys, len, 1031f211d536STycho Nightingale BUS_DMA_NOWAIT, &seg, &nseg) != 0) { 1032c39eefe7SBjoern A. Zeeb bus_dmamap_destroy(obj->dmat, obj->dmamap); 1033a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1034f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1035e8670741SBjoern A. Zeeb counter_u64_add(lkpi_pci_nseg1_fail, 1); 1036e8670741SBjoern A. Zeeb if (linuxkpi_debug) 1037e8670741SBjoern A. Zeeb dump_stack(); 1038f211d536STycho Nightingale return (0); 1039f211d536STycho Nightingale } 1040f211d536STycho Nightingale 1041f211d536STycho Nightingale KASSERT(++nseg == 1, ("More than one segment (nseg=%d)", nseg)); 1042f211d536STycho Nightingale obj->dma_addr = seg.ds_addr; 1043f211d536STycho Nightingale 1044f211d536STycho Nightingale error = LINUX_DMA_PCTRIE_INSERT(&priv->ptree, obj); 1045f211d536STycho Nightingale if (error != 0) { 1046c39eefe7SBjoern A. Zeeb bus_dmamap_unload(obj->dmat, obj->dmamap); 1047c39eefe7SBjoern A. Zeeb bus_dmamap_destroy(obj->dmat, obj->dmamap); 1048a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1049f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1050f211d536STycho Nightingale return (0); 1051f211d536STycho Nightingale } 1052a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1053f211d536STycho Nightingale return (obj->dma_addr); 1054f211d536STycho Nightingale } 1055b961c0f2STycho Nightingale #else 105612698731SBjoern A. Zeeb static dma_addr_t 1057c39eefe7SBjoern A. Zeeb linux_dma_map_phys_common(struct device *dev __unused, vm_paddr_t phys, 1058c39eefe7SBjoern A. Zeeb size_t len __unused, bus_dma_tag_t dmat __unused) 1059b961c0f2STycho Nightingale { 1060b961c0f2STycho Nightingale return (phys); 1061b961c0f2STycho Nightingale } 1062b961c0f2STycho Nightingale #endif 1063f211d536STycho Nightingale 1064c39eefe7SBjoern A. Zeeb dma_addr_t 1065c39eefe7SBjoern A. Zeeb linux_dma_map_phys(struct device *dev, vm_paddr_t phys, size_t len) 1066c39eefe7SBjoern A. Zeeb { 1067c39eefe7SBjoern A. Zeeb struct linux_dma_priv *priv; 1068c39eefe7SBjoern A. Zeeb 1069c39eefe7SBjoern A. Zeeb priv = dev->dma_priv; 1070c39eefe7SBjoern A. Zeeb return (linux_dma_map_phys_common(dev, phys, len, priv->dmat)); 1071c39eefe7SBjoern A. Zeeb } 1072c39eefe7SBjoern A. Zeeb 1073b961c0f2STycho Nightingale #if defined(__i386__) || defined(__amd64__) || defined(__aarch64__) 1074f211d536STycho Nightingale void 1075f211d536STycho Nightingale linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len) 1076f211d536STycho Nightingale { 1077f211d536STycho Nightingale struct linux_dma_priv *priv; 1078f211d536STycho Nightingale struct linux_dma_obj *obj; 1079f211d536STycho Nightingale 1080f211d536STycho Nightingale priv = dev->dma_priv; 1081f211d536STycho Nightingale 1082b961c0f2STycho Nightingale if (pctrie_is_empty(&priv->ptree)) 1083b961c0f2STycho Nightingale return; 1084b961c0f2STycho Nightingale 1085a6619e8dSHans Petter Selasky DMA_PRIV_LOCK(priv); 1086f211d536STycho Nightingale obj = LINUX_DMA_PCTRIE_LOOKUP(&priv->ptree, dma_addr); 1087f211d536STycho Nightingale if (obj == NULL) { 1088a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1089f211d536STycho Nightingale return; 1090f211d536STycho Nightingale } 1091f211d536STycho Nightingale LINUX_DMA_PCTRIE_REMOVE(&priv->ptree, dma_addr); 1092c39eefe7SBjoern A. Zeeb bus_dmamap_unload(obj->dmat, obj->dmamap); 1093c39eefe7SBjoern A. Zeeb bus_dmamap_destroy(obj->dmat, obj->dmamap); 1094a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1095f211d536STycho Nightingale 1096f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1097f211d536STycho Nightingale } 1098b961c0f2STycho Nightingale #else 1099b961c0f2STycho Nightingale void 1100b961c0f2STycho Nightingale linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len) 1101b961c0f2STycho Nightingale { 1102b961c0f2STycho Nightingale } 1103b961c0f2STycho Nightingale #endif 1104f211d536STycho Nightingale 1105c39eefe7SBjoern A. Zeeb void * 1106c39eefe7SBjoern A. Zeeb linux_dma_alloc_coherent(struct device *dev, size_t size, 1107c39eefe7SBjoern A. Zeeb dma_addr_t *dma_handle, gfp_t flag) 1108c39eefe7SBjoern A. Zeeb { 1109c39eefe7SBjoern A. Zeeb struct linux_dma_priv *priv; 1110c39eefe7SBjoern A. Zeeb vm_paddr_t high; 1111c39eefe7SBjoern A. Zeeb size_t align; 1112c39eefe7SBjoern A. Zeeb void *mem; 1113c39eefe7SBjoern A. Zeeb 1114c39eefe7SBjoern A. Zeeb if (dev == NULL || dev->dma_priv == NULL) { 1115c39eefe7SBjoern A. Zeeb *dma_handle = 0; 1116c39eefe7SBjoern A. Zeeb return (NULL); 1117c39eefe7SBjoern A. Zeeb } 1118c39eefe7SBjoern A. Zeeb priv = dev->dma_priv; 1119c39eefe7SBjoern A. Zeeb if (priv->dma_coherent_mask) 1120c39eefe7SBjoern A. Zeeb high = priv->dma_coherent_mask; 1121c39eefe7SBjoern A. Zeeb else 1122c39eefe7SBjoern A. Zeeb /* Coherent is lower 32bit only by default in Linux. */ 1123c39eefe7SBjoern A. Zeeb high = BUS_SPACE_MAXADDR_32BIT; 1124c39eefe7SBjoern A. Zeeb align = PAGE_SIZE << get_order(size); 1125c39eefe7SBjoern A. Zeeb /* Always zero the allocation. */ 1126c39eefe7SBjoern A. Zeeb flag |= M_ZERO; 1127c39eefe7SBjoern A. Zeeb mem = (void *)kmem_alloc_contig(size, flag & GFP_NATIVE_MASK, 0, high, 1128c39eefe7SBjoern A. Zeeb align, 0, VM_MEMATTR_DEFAULT); 1129c39eefe7SBjoern A. Zeeb if (mem != NULL) { 1130c39eefe7SBjoern A. Zeeb *dma_handle = linux_dma_map_phys_common(dev, vtophys(mem), size, 1131c39eefe7SBjoern A. Zeeb priv->dmat_coherent); 1132c39eefe7SBjoern A. Zeeb if (*dma_handle == 0) { 1133c39eefe7SBjoern A. Zeeb kmem_free((vm_offset_t)mem, size); 1134c39eefe7SBjoern A. Zeeb mem = NULL; 1135c39eefe7SBjoern A. Zeeb } 1136c39eefe7SBjoern A. Zeeb } else { 1137c39eefe7SBjoern A. Zeeb *dma_handle = 0; 1138c39eefe7SBjoern A. Zeeb } 1139c39eefe7SBjoern A. Zeeb return (mem); 1140c39eefe7SBjoern A. Zeeb } 1141c39eefe7SBjoern A. Zeeb 114295edb10bSBjoern A. Zeeb void 114395edb10bSBjoern A. Zeeb linuxkpi_dma_sync(struct device *dev, dma_addr_t dma_addr, size_t size, 114495edb10bSBjoern A. Zeeb bus_dmasync_op_t op) 114595edb10bSBjoern A. Zeeb { 114695edb10bSBjoern A. Zeeb struct linux_dma_priv *priv; 114795edb10bSBjoern A. Zeeb struct linux_dma_obj *obj; 114895edb10bSBjoern A. Zeeb 114995edb10bSBjoern A. Zeeb priv = dev->dma_priv; 115095edb10bSBjoern A. Zeeb 115195edb10bSBjoern A. Zeeb if (pctrie_is_empty(&priv->ptree)) 115295edb10bSBjoern A. Zeeb return; 115395edb10bSBjoern A. Zeeb 115495edb10bSBjoern A. Zeeb DMA_PRIV_LOCK(priv); 115595edb10bSBjoern A. Zeeb obj = LINUX_DMA_PCTRIE_LOOKUP(&priv->ptree, dma_addr); 115695edb10bSBjoern A. Zeeb if (obj == NULL) { 115795edb10bSBjoern A. Zeeb DMA_PRIV_UNLOCK(priv); 115895edb10bSBjoern A. Zeeb return; 115995edb10bSBjoern A. Zeeb } 116095edb10bSBjoern A. Zeeb 116195edb10bSBjoern A. Zeeb bus_dmamap_sync(obj->dmat, obj->dmamap, op); 116295edb10bSBjoern A. Zeeb DMA_PRIV_UNLOCK(priv); 116395edb10bSBjoern A. Zeeb } 116495edb10bSBjoern A. Zeeb 1165f211d536STycho Nightingale int 1166f211d536STycho Nightingale linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents, 116795edb10bSBjoern A. Zeeb enum dma_data_direction direction, unsigned long attrs __unused) 1168f211d536STycho Nightingale { 1169f211d536STycho Nightingale struct linux_dma_priv *priv; 1170442d12d8SHans Petter Selasky struct scatterlist *sg; 1171442d12d8SHans Petter Selasky int i, nseg; 1172f211d536STycho Nightingale bus_dma_segment_t seg; 1173f211d536STycho Nightingale 1174f211d536STycho Nightingale priv = dev->dma_priv; 1175f211d536STycho Nightingale 1176a6619e8dSHans Petter Selasky DMA_PRIV_LOCK(priv); 1177442d12d8SHans Petter Selasky 1178442d12d8SHans Petter Selasky /* create common DMA map in the first S/G entry */ 1179442d12d8SHans Petter Selasky if (bus_dmamap_create(priv->dmat, 0, &sgl->dma_map) != 0) { 1180a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1181f211d536STycho Nightingale return (0); 1182f211d536STycho Nightingale } 1183f211d536STycho Nightingale 1184442d12d8SHans Petter Selasky /* load all S/G list entries */ 1185442d12d8SHans Petter Selasky for_each_sg(sgl, sg, nents, i) { 1186f211d536STycho Nightingale nseg = -1; 1187442d12d8SHans Petter Selasky if (_bus_dmamap_load_phys(priv->dmat, sgl->dma_map, 1188442d12d8SHans Petter Selasky sg_phys(sg), sg->length, BUS_DMA_NOWAIT, 1189f211d536STycho Nightingale &seg, &nseg) != 0) { 1190442d12d8SHans Petter Selasky bus_dmamap_unload(priv->dmat, sgl->dma_map); 1191442d12d8SHans Petter Selasky bus_dmamap_destroy(priv->dmat, sgl->dma_map); 1192a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1193f211d536STycho Nightingale return (0); 1194f211d536STycho Nightingale } 1195442d12d8SHans Petter Selasky KASSERT(nseg == 0, 1196442d12d8SHans Petter Selasky ("More than one segment (nseg=%d)", nseg + 1)); 1197f211d536STycho Nightingale 1198442d12d8SHans Petter Selasky sg_dma_address(sg) = seg.ds_addr; 1199f211d536STycho Nightingale } 120095edb10bSBjoern A. Zeeb 120195edb10bSBjoern A. Zeeb switch (direction) { 120295edb10bSBjoern A. Zeeb case DMA_BIDIRECTIONAL: 120395edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREWRITE); 120495edb10bSBjoern A. Zeeb break; 120595edb10bSBjoern A. Zeeb case DMA_TO_DEVICE: 120695edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREREAD); 120795edb10bSBjoern A. Zeeb break; 120895edb10bSBjoern A. Zeeb case DMA_FROM_DEVICE: 120995edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREWRITE); 121095edb10bSBjoern A. Zeeb break; 121195edb10bSBjoern A. Zeeb default: 121295edb10bSBjoern A. Zeeb break; 121395edb10bSBjoern A. Zeeb } 121495edb10bSBjoern A. Zeeb 1215a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1216442d12d8SHans Petter Selasky 1217442d12d8SHans Petter Selasky return (nents); 1218f211d536STycho Nightingale } 1219f211d536STycho Nightingale 1220f211d536STycho Nightingale void 1221f211d536STycho Nightingale linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sgl, 122295edb10bSBjoern A. Zeeb int nents __unused, enum dma_data_direction direction, 122398a6984aSVladimir Kondratyev unsigned long attrs __unused) 1224f211d536STycho Nightingale { 1225f211d536STycho Nightingale struct linux_dma_priv *priv; 1226f211d536STycho Nightingale 1227f211d536STycho Nightingale priv = dev->dma_priv; 1228f211d536STycho Nightingale 1229a6619e8dSHans Petter Selasky DMA_PRIV_LOCK(priv); 123095edb10bSBjoern A. Zeeb 123195edb10bSBjoern A. Zeeb switch (direction) { 123295edb10bSBjoern A. Zeeb case DMA_BIDIRECTIONAL: 123395edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_POSTREAD); 123495edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREREAD); 123595edb10bSBjoern A. Zeeb break; 123695edb10bSBjoern A. Zeeb case DMA_TO_DEVICE: 123795edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_POSTWRITE); 123895edb10bSBjoern A. Zeeb break; 123995edb10bSBjoern A. Zeeb case DMA_FROM_DEVICE: 124095edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_POSTREAD); 124195edb10bSBjoern A. Zeeb break; 124295edb10bSBjoern A. Zeeb default: 124395edb10bSBjoern A. Zeeb break; 124495edb10bSBjoern A. Zeeb } 124595edb10bSBjoern A. Zeeb 1246442d12d8SHans Petter Selasky bus_dmamap_unload(priv->dmat, sgl->dma_map); 1247442d12d8SHans Petter Selasky bus_dmamap_destroy(priv->dmat, sgl->dma_map); 1248a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1249f211d536STycho Nightingale } 1250f211d536STycho Nightingale 125193a203eaSHans Petter Selasky struct dma_pool { 125293a203eaSHans Petter Selasky struct device *pool_device; 125393a203eaSHans Petter Selasky uma_zone_t pool_zone; 1254a6619e8dSHans Petter Selasky struct mtx pool_lock; 125593a203eaSHans Petter Selasky bus_dma_tag_t pool_dmat; 125693a203eaSHans Petter Selasky size_t pool_entry_size; 125793a203eaSHans Petter Selasky struct pctrie pool_ptree; 125893a203eaSHans Petter Selasky }; 125993a203eaSHans Petter Selasky 1260a6619e8dSHans Petter Selasky #define DMA_POOL_LOCK(pool) mtx_lock(&(pool)->pool_lock) 1261a6619e8dSHans Petter Selasky #define DMA_POOL_UNLOCK(pool) mtx_unlock(&(pool)->pool_lock) 1262a6619e8dSHans Petter Selasky 1263f211d536STycho Nightingale static inline int 1264f211d536STycho Nightingale dma_pool_obj_ctor(void *mem, int size, void *arg, int flags) 1265f211d536STycho Nightingale { 1266f211d536STycho Nightingale struct linux_dma_obj *obj = mem; 1267f211d536STycho Nightingale struct dma_pool *pool = arg; 1268f211d536STycho Nightingale int error, nseg; 1269f211d536STycho Nightingale bus_dma_segment_t seg; 1270f211d536STycho Nightingale 1271f211d536STycho Nightingale nseg = -1; 1272a6619e8dSHans Petter Selasky DMA_POOL_LOCK(pool); 1273f211d536STycho Nightingale error = _bus_dmamap_load_phys(pool->pool_dmat, obj->dmamap, 1274f211d536STycho Nightingale vtophys(obj->vaddr), pool->pool_entry_size, BUS_DMA_NOWAIT, 1275f211d536STycho Nightingale &seg, &nseg); 1276a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1277f211d536STycho Nightingale if (error != 0) { 1278f211d536STycho Nightingale return (error); 1279f211d536STycho Nightingale } 1280f211d536STycho Nightingale KASSERT(++nseg == 1, ("More than one segment (nseg=%d)", nseg)); 1281f211d536STycho Nightingale obj->dma_addr = seg.ds_addr; 1282f211d536STycho Nightingale 1283f211d536STycho Nightingale return (0); 1284f211d536STycho Nightingale } 1285f211d536STycho Nightingale 1286f211d536STycho Nightingale static void 1287f211d536STycho Nightingale dma_pool_obj_dtor(void *mem, int size, void *arg) 1288f211d536STycho Nightingale { 1289f211d536STycho Nightingale struct linux_dma_obj *obj = mem; 1290f211d536STycho Nightingale struct dma_pool *pool = arg; 1291f211d536STycho Nightingale 1292a6619e8dSHans Petter Selasky DMA_POOL_LOCK(pool); 1293f211d536STycho Nightingale bus_dmamap_unload(pool->pool_dmat, obj->dmamap); 1294a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1295f211d536STycho Nightingale } 1296f211d536STycho Nightingale 1297f211d536STycho Nightingale static int 1298f211d536STycho Nightingale dma_pool_obj_import(void *arg, void **store, int count, int domain __unused, 1299f211d536STycho Nightingale int flags) 1300f211d536STycho Nightingale { 1301f211d536STycho Nightingale struct dma_pool *pool = arg; 1302f211d536STycho Nightingale struct linux_dma_obj *obj; 1303f211d536STycho Nightingale int error, i; 1304f211d536STycho Nightingale 1305f211d536STycho Nightingale for (i = 0; i < count; i++) { 1306f211d536STycho Nightingale obj = uma_zalloc(linux_dma_obj_zone, flags); 1307f211d536STycho Nightingale if (obj == NULL) 1308f211d536STycho Nightingale break; 1309f211d536STycho Nightingale 1310f211d536STycho Nightingale error = bus_dmamem_alloc(pool->pool_dmat, &obj->vaddr, 1311f211d536STycho Nightingale BUS_DMA_NOWAIT, &obj->dmamap); 1312f211d536STycho Nightingale if (error!= 0) { 1313f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1314f211d536STycho Nightingale break; 1315f211d536STycho Nightingale } 1316f211d536STycho Nightingale 1317f211d536STycho Nightingale store[i] = obj; 1318f211d536STycho Nightingale } 1319f211d536STycho Nightingale 1320f211d536STycho Nightingale return (i); 1321f211d536STycho Nightingale } 1322f211d536STycho Nightingale 1323f211d536STycho Nightingale static void 1324f211d536STycho Nightingale dma_pool_obj_release(void *arg, void **store, int count) 1325f211d536STycho Nightingale { 1326f211d536STycho Nightingale struct dma_pool *pool = arg; 1327f211d536STycho Nightingale struct linux_dma_obj *obj; 1328f211d536STycho Nightingale int i; 1329f211d536STycho Nightingale 1330f211d536STycho Nightingale for (i = 0; i < count; i++) { 1331f211d536STycho Nightingale obj = store[i]; 1332f211d536STycho Nightingale bus_dmamem_free(pool->pool_dmat, obj->vaddr, obj->dmamap); 1333f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1334f211d536STycho Nightingale } 1335f211d536STycho Nightingale } 1336f211d536STycho Nightingale 1337f211d536STycho Nightingale struct dma_pool * 1338f211d536STycho Nightingale linux_dma_pool_create(char *name, struct device *dev, size_t size, 1339f211d536STycho Nightingale size_t align, size_t boundary) 1340f211d536STycho Nightingale { 1341f211d536STycho Nightingale struct linux_dma_priv *priv; 1342f211d536STycho Nightingale struct dma_pool *pool; 1343f211d536STycho Nightingale 1344f211d536STycho Nightingale priv = dev->dma_priv; 1345f211d536STycho Nightingale 1346f211d536STycho Nightingale pool = kzalloc(sizeof(*pool), GFP_KERNEL); 13475a637529SHans Petter Selasky pool->pool_device = dev; 1348f211d536STycho Nightingale pool->pool_entry_size = size; 1349f211d536STycho Nightingale 1350f211d536STycho Nightingale if (bus_dma_tag_create(bus_get_dma_tag(dev->bsddev), 1351f211d536STycho Nightingale align, boundary, /* alignment, boundary */ 1352f211d536STycho Nightingale priv->dma_mask, /* lowaddr */ 1353f211d536STycho Nightingale BUS_SPACE_MAXADDR, /* highaddr */ 1354f211d536STycho Nightingale NULL, NULL, /* filtfunc, filtfuncarg */ 1355f211d536STycho Nightingale size, /* maxsize */ 1356f211d536STycho Nightingale 1, /* nsegments */ 1357f211d536STycho Nightingale size, /* maxsegsz */ 1358f211d536STycho Nightingale 0, /* flags */ 1359f211d536STycho Nightingale NULL, NULL, /* lockfunc, lockfuncarg */ 1360f211d536STycho Nightingale &pool->pool_dmat)) { 1361f211d536STycho Nightingale kfree(pool); 1362f211d536STycho Nightingale return (NULL); 1363f211d536STycho Nightingale } 1364f211d536STycho Nightingale 1365f211d536STycho Nightingale pool->pool_zone = uma_zcache_create(name, -1, dma_pool_obj_ctor, 1366f211d536STycho Nightingale dma_pool_obj_dtor, NULL, NULL, dma_pool_obj_import, 1367f211d536STycho Nightingale dma_pool_obj_release, pool, 0); 1368f211d536STycho Nightingale 1369a6619e8dSHans Petter Selasky mtx_init(&pool->pool_lock, "lkpi-dma-pool", NULL, MTX_DEF); 1370f211d536STycho Nightingale pctrie_init(&pool->pool_ptree); 1371f211d536STycho Nightingale 1372f211d536STycho Nightingale return (pool); 1373f211d536STycho Nightingale } 1374f211d536STycho Nightingale 1375f211d536STycho Nightingale void 1376f211d536STycho Nightingale linux_dma_pool_destroy(struct dma_pool *pool) 1377f211d536STycho Nightingale { 1378f211d536STycho Nightingale 1379f211d536STycho Nightingale uma_zdestroy(pool->pool_zone); 1380f211d536STycho Nightingale bus_dma_tag_destroy(pool->pool_dmat); 1381a6619e8dSHans Petter Selasky mtx_destroy(&pool->pool_lock); 1382f211d536STycho Nightingale kfree(pool); 1383f211d536STycho Nightingale } 1384f211d536STycho Nightingale 13852afeed13SBjoern A. Zeeb void 13862afeed13SBjoern A. Zeeb lkpi_dmam_pool_destroy(struct device *dev, void *p) 13872afeed13SBjoern A. Zeeb { 13882afeed13SBjoern A. Zeeb struct dma_pool *pool; 13892afeed13SBjoern A. Zeeb 13902afeed13SBjoern A. Zeeb pool = *(struct dma_pool **)p; 13912afeed13SBjoern A. Zeeb LINUX_DMA_PCTRIE_RECLAIM(&pool->pool_ptree); 13922afeed13SBjoern A. Zeeb linux_dma_pool_destroy(pool); 13932afeed13SBjoern A. Zeeb } 13942afeed13SBjoern A. Zeeb 1395f211d536STycho Nightingale void * 1396f211d536STycho Nightingale linux_dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags, 1397f211d536STycho Nightingale dma_addr_t *handle) 1398f211d536STycho Nightingale { 1399f211d536STycho Nightingale struct linux_dma_obj *obj; 1400f211d536STycho Nightingale 1401f6e7d67aSBryan Drewery obj = uma_zalloc_arg(pool->pool_zone, pool, mem_flags & GFP_NATIVE_MASK); 1402f211d536STycho Nightingale if (obj == NULL) 1403f211d536STycho Nightingale return (NULL); 1404f211d536STycho Nightingale 1405a6619e8dSHans Petter Selasky DMA_POOL_LOCK(pool); 1406f211d536STycho Nightingale if (LINUX_DMA_PCTRIE_INSERT(&pool->pool_ptree, obj) != 0) { 1407a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1408f211d536STycho Nightingale uma_zfree_arg(pool->pool_zone, obj, pool); 1409f211d536STycho Nightingale return (NULL); 1410f211d536STycho Nightingale } 1411a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1412f211d536STycho Nightingale 1413f211d536STycho Nightingale *handle = obj->dma_addr; 1414f211d536STycho Nightingale return (obj->vaddr); 1415f211d536STycho Nightingale } 1416f211d536STycho Nightingale 1417f211d536STycho Nightingale void 1418f211d536STycho Nightingale linux_dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t dma_addr) 1419f211d536STycho Nightingale { 1420f211d536STycho Nightingale struct linux_dma_obj *obj; 1421f211d536STycho Nightingale 1422a6619e8dSHans Petter Selasky DMA_POOL_LOCK(pool); 1423f211d536STycho Nightingale obj = LINUX_DMA_PCTRIE_LOOKUP(&pool->pool_ptree, dma_addr); 1424f211d536STycho Nightingale if (obj == NULL) { 1425a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1426f211d536STycho Nightingale return; 1427f211d536STycho Nightingale } 1428f211d536STycho Nightingale LINUX_DMA_PCTRIE_REMOVE(&pool->pool_ptree, dma_addr); 1429a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1430f211d536STycho Nightingale 1431f211d536STycho Nightingale uma_zfree_arg(pool->pool_zone, obj, pool); 1432f211d536STycho Nightingale } 14332b68c973SEmmanuel Vadot 14342b68c973SEmmanuel Vadot static int 14352b68c973SEmmanuel Vadot linux_backlight_get_status(device_t dev, struct backlight_props *props) 14362b68c973SEmmanuel Vadot { 14372b68c973SEmmanuel Vadot struct pci_dev *pdev; 14382b68c973SEmmanuel Vadot 14392b68c973SEmmanuel Vadot linux_set_current(curthread); 14402b68c973SEmmanuel Vadot pdev = device_get_softc(dev); 14412b68c973SEmmanuel Vadot 14422b68c973SEmmanuel Vadot props->brightness = pdev->dev.bd->props.brightness; 14432b68c973SEmmanuel Vadot props->brightness = props->brightness * 100 / pdev->dev.bd->props.max_brightness; 14442b68c973SEmmanuel Vadot props->nlevels = 0; 14452b68c973SEmmanuel Vadot 14462b68c973SEmmanuel Vadot return (0); 14472b68c973SEmmanuel Vadot } 14482b68c973SEmmanuel Vadot 14492b68c973SEmmanuel Vadot static int 14502b68c973SEmmanuel Vadot linux_backlight_get_info(device_t dev, struct backlight_info *info) 14512b68c973SEmmanuel Vadot { 14522b68c973SEmmanuel Vadot struct pci_dev *pdev; 14532b68c973SEmmanuel Vadot 14542b68c973SEmmanuel Vadot linux_set_current(curthread); 14552b68c973SEmmanuel Vadot pdev = device_get_softc(dev); 14562b68c973SEmmanuel Vadot 14572b68c973SEmmanuel Vadot info->type = BACKLIGHT_TYPE_PANEL; 14582b68c973SEmmanuel Vadot strlcpy(info->name, pdev->dev.bd->name, BACKLIGHTMAXNAMELENGTH); 14592b68c973SEmmanuel Vadot return (0); 14602b68c973SEmmanuel Vadot } 14612b68c973SEmmanuel Vadot 14622b68c973SEmmanuel Vadot static int 14632b68c973SEmmanuel Vadot linux_backlight_update_status(device_t dev, struct backlight_props *props) 14642b68c973SEmmanuel Vadot { 14652b68c973SEmmanuel Vadot struct pci_dev *pdev; 14662b68c973SEmmanuel Vadot 14672b68c973SEmmanuel Vadot linux_set_current(curthread); 14682b68c973SEmmanuel Vadot pdev = device_get_softc(dev); 14692b68c973SEmmanuel Vadot 14702b68c973SEmmanuel Vadot pdev->dev.bd->props.brightness = pdev->dev.bd->props.max_brightness * 14712b68c973SEmmanuel Vadot props->brightness / 100; 1472b52e3638SVladimir Kondratyev pdev->dev.bd->props.power = props->brightness == 0 ? 1473b52e3638SVladimir Kondratyev 4/* FB_BLANK_POWERDOWN */ : 0/* FB_BLANK_UNBLANK */; 14742b68c973SEmmanuel Vadot return (pdev->dev.bd->ops->update_status(pdev->dev.bd)); 14752b68c973SEmmanuel Vadot } 14762b68c973SEmmanuel Vadot 14772b68c973SEmmanuel Vadot struct backlight_device * 14782b68c973SEmmanuel Vadot linux_backlight_device_register(const char *name, struct device *dev, 14792b68c973SEmmanuel Vadot void *data, const struct backlight_ops *ops, struct backlight_properties *props) 14802b68c973SEmmanuel Vadot { 14812b68c973SEmmanuel Vadot 14822b68c973SEmmanuel Vadot dev->bd = malloc(sizeof(*dev->bd), M_DEVBUF, M_WAITOK | M_ZERO); 14832b68c973SEmmanuel Vadot dev->bd->ops = ops; 14842b68c973SEmmanuel Vadot dev->bd->props.type = props->type; 14852b68c973SEmmanuel Vadot dev->bd->props.max_brightness = props->max_brightness; 14862b68c973SEmmanuel Vadot dev->bd->props.brightness = props->brightness; 14872b68c973SEmmanuel Vadot dev->bd->props.power = props->power; 14882b68c973SEmmanuel Vadot dev->bd->data = data; 14892b68c973SEmmanuel Vadot dev->bd->dev = dev; 14902b68c973SEmmanuel Vadot dev->bd->name = strdup(name, M_DEVBUF); 14912b68c973SEmmanuel Vadot 14922b68c973SEmmanuel Vadot dev->backlight_dev = backlight_register(name, dev->bsddev); 14932b68c973SEmmanuel Vadot 14942b68c973SEmmanuel Vadot return (dev->bd); 14952b68c973SEmmanuel Vadot } 14962b68c973SEmmanuel Vadot 14972b68c973SEmmanuel Vadot void 14982b68c973SEmmanuel Vadot linux_backlight_device_unregister(struct backlight_device *bd) 14992b68c973SEmmanuel Vadot { 15002b68c973SEmmanuel Vadot 15012b68c973SEmmanuel Vadot backlight_destroy(bd->dev->backlight_dev); 15022b68c973SEmmanuel Vadot free(bd->name, M_DEVBUF); 15032b68c973SEmmanuel Vadot free(bd, M_DEVBUF); 15042b68c973SEmmanuel Vadot } 1505